Skip to content

Commit 80fd94f

Browse files
committed
feat: initial commit for the monorepo structuring
I've decided to stick with `just` for command management, as they provide a module structure that's very favorable for monorepos. each package is going to be stored at `src/{package}`, with its own `pyproject.toml` and `mod.just` files, for defining relevant commands, tests, lints, and so on. the main `justfile` in the workspace root then serves as the coordinator, calling all the subfiles in order to create commands to run in the CI and pre-commit.
1 parent 054f605 commit 80fd94f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+4331
-1665
lines changed

cli_scripts.py

Lines changed: 0 additions & 45 deletions
This file was deleted.

justfile

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# commands related to realtime
2+
mod realtime "src/realtime/mod.just"
3+
# commands related to supabase
4+
mod supabase "src/supabase/mod.just"
5+
6+
[doc("Lists all available commands")]
7+
default:
8+
@just --list
9+
10+
[doc("Run all available tests")]
11+
test: realtime::pytest && supabase::pytest
12+
13+
[doc("Run pre-commit on all files")]
14+
pre-commit:
15+
uv run pre-commit run --all-files
16+
17+
[doc("Run CI tests")]
18+
ci: pre-commit && test

poetry.lock

Lines changed: 0 additions & 1599 deletions
This file was deleted.

pyproject.toml

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ classifiers = [
1919
]
2020
requires-python = ">=3.9"
2121
dependencies = [
22+
"realtime",
2223
"postgrest == 1.1.1",
23-
"realtime == 2.6.0",
2424
"gotrue == 2.12.3",
2525
"storage3 == 0.12.0",
2626
"supafunc == 0.10.1",
@@ -32,10 +32,6 @@ homepage = "https://github.com/supabase/supabase-py"
3232
repository = "https://github.com/supabase/supabase-py"
3333
documentation = "https://github.com/supabase/supabase-py"
3434

35-
[project.scripts]
36-
tests = "cli_scripts:run_tests"
37-
build_sync = "cli_scripts:build_sync"
38-
3935
[dependency-groups]
4036
dev = [
4137
{ include-group = "pre-commit" },
@@ -60,17 +56,25 @@ lints = [
6056
[tool.uv]
6157
default-groups = [ "dev" ]
6258

59+
[tool.uv.workspace]
60+
members = [ "src/realtime" ]
6361

6462
[tool.uv.sources]
63+
realtime = { workspace = true }
6564
unasync-cli = { git = "https://github.com/supabase-community/unasync-cli.git", branch = "main" }
6665

67-
[build-system]
68-
requires = ["uv_build>=0.8.3,<0.9.0"]
69-
build-backend = "uv_build"
66+
[project.scripts]
67+
tests = "cli_scripts:run_tests"
68+
build_sync = "cli_scripts:build_sync"
69+
7070

7171
[tool.uv.build-backend]
7272
module-name = "supabase"
73-
module-root = ""
73+
module-root = "src/supabase/src"
74+
75+
[build-system]
76+
requires = ["uv_build>=0.8.3,<0.9.0"]
77+
build-backend = "uv_build"
7478

7579
[tool.pytest.ini_options]
7680
asyncio_mode = "auto"

src/realtime/README.md

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
<br />
2+
<p align="center">
3+
<a href="https://supabase.io">
4+
<picture>
5+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/supabase/supabase/main/packages/common/assets/images/supabase-logo-wordmark--dark.svg">
6+
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/supabase/supabase/main/packages/common/assets/images/supabase-logo-wordmark--light.svg">
7+
<img alt="Supabase Logo" width="300" src="https://raw.githubusercontent.com/supabase/supabase/main/packages/common/assets/images/logo-preview.jpg">
8+
</picture>
9+
</a>
10+
11+
<h1 align="center">Supabase Realtime Client</h1>
12+
13+
<h3 align="center">Send ephemeral messages with <b>Broadcast</b>, track and synchronize state with <b>Presence</b>, and listen to database changes with <b>Postgres Change Data Capture (CDC)</b>.</h3>
14+
15+
<p align="center">
16+
<a href="https://supabase.com/docs/guides/realtime">Guides</a>
17+
·
18+
<a href="https://supabase.com/docs/reference/python">Reference Docs</a>
19+
·
20+
<a href="https://multiplayer.dev">Multiplayer Demo</a>
21+
</p>
22+
</p>
23+
24+
# Overview
25+
26+
This client enables you to use the following Supabase Realtime's features:
27+
28+
- **Broadcast**: send ephemeral messages from client to clients with minimal latency. Use cases include sharing cursor positions between users.
29+
- **Presence**: track and synchronize shared state across clients with the help of CRDTs. Use cases include tracking which users are currently viewing a specific webpage.
30+
- **Postgres Change Data Capture (CDC)**: listen for changes in your PostgreSQL database and send them to clients.
31+
32+
# Usage
33+
34+
## Installing the Package
35+
36+
```bash
37+
pip3 install realtime
38+
```
39+
40+
## Creating a Channel
41+
42+
```python
43+
import asyncio
44+
from typing import Optional
45+
46+
from realtime import AsyncRealtimeClient, RealtimeSubscribeStates
47+
48+
49+
async def main():
50+
REALTIME_URL = "ws://localhost:4000/websocket"
51+
API_KEY = "1234567890"
52+
53+
socket = AsyncRealtimeClient(REALTIME_URL, API_KEY)
54+
channel = socket.channel("test-channel")
55+
56+
def _on_subscribe(status: RealtimeSubscribeStates, err: Optional[Exception]):
57+
if status == RealtimeSubscribeStates.SUBSCRIBED:
58+
print("Connected!")
59+
elif status == RealtimeSubscribeStates.CHANNEL_ERROR:
60+
print(f"There was an error subscribing to channel: {err.args}")
61+
elif status == RealtimeSubscribeStates.TIMED_OUT:
62+
print("Realtime server did not respond in time.")
63+
elif status == RealtimeSubscribeStates.CLOSED:
64+
print("Realtime channel was unexpectedly closed.")
65+
66+
await channel.subscribe(_on_subscribe)
67+
```
68+
69+
### Notes:
70+
71+
- `REALTIME_URL` is `ws://localhost:4000/socket` when developing locally and `wss://<project_ref>.supabase.co/realtime/v1` when connecting to your Supabase project.
72+
- `API_KEY` is a JWT whose claims must contain `exp` and `role` (existing database role).
73+
- Channel name can be any `string`.
74+
75+
## Broadcast
76+
77+
Your client can send and receive messages based on the `event`.
78+
79+
```python
80+
# Setup...
81+
82+
channel = client.channel(
83+
"broadcast-test", {"config": {"broadcast": {"ack": False, "self": False}}}
84+
)
85+
86+
await channel.on_broadcast("some-event", lambda payload: print(payload)).subscribe()
87+
await channel.send_broadcast("some-event", {"hello": "world"})
88+
```
89+
90+
### Notes:
91+
92+
- Setting `ack` to `true` means that the `channel.send` promise will resolve once server replies with acknowledgement that it received the broadcast message request.
93+
- Setting `self` to `true` means that the client will receive the broadcast message it sent out.
94+
- Setting `private` to `true` means that the client will use RLS to determine if the user can connect or not to a given channel.
95+
96+
## Presence
97+
98+
Your client can track and sync state that's stored in the channel.
99+
100+
```python
101+
# Setup...
102+
103+
channel = client.channel(
104+
"presence-test",
105+
{
106+
"config": {
107+
"presence": {
108+
"key": ""
109+
}
110+
}
111+
}
112+
)
113+
114+
channel.on_presence_sync(lambda: print("Online users: ", channel.presence_state()))
115+
channel.on_presence_join(lambda new_presences: print("New users have joined: ", new_presences))
116+
channel.on_presence_leave(lambda left_presences: print("Users have left: ", left_presences))
117+
118+
await channel.track({ 'user_id': 1 })
119+
```
120+
121+
## Postgres CDC
122+
123+
Receive database changes on the client.
124+
125+
```python
126+
# Setup...
127+
128+
channel = client.channel("db-changes")
129+
130+
channel.on_postgres_changes(
131+
"*",
132+
schema="public",
133+
callback=lambda payload: print("All changes in public schema: ", payload),
134+
)
135+
136+
channel.on_postgres_changes(
137+
"INSERT",
138+
schema="public",
139+
table="messages",
140+
callback=lambda payload: print("All inserts in messages table: ", payload),
141+
)
142+
143+
channel.on_postgres_changes(
144+
"UPDATE",
145+
schema="public",
146+
table="users",
147+
filter="username=eq.Realtime",
148+
callback=lambda payload: print(
149+
"All updates on users table when username is Realtime: ", payload
150+
),
151+
)
152+
153+
channel.subscribe(
154+
lambda status, err: status == RealtimeSubscribeStates.SUBSCRIBED
155+
and print("Ready to receive database changes!")
156+
)
157+
```
158+
159+
## Get All Channels
160+
161+
You can see all the channels that your client has instantiated.
162+
163+
```python
164+
# Setup...
165+
166+
client.get_channels()
167+
```
168+
169+
## Cleanup
170+
171+
It is highly recommended that you clean up your channels after you're done with them.
172+
173+
- Remove a single channel
174+
175+
```python
176+
# Setup...
177+
178+
channel = client.channel('some-channel-to-remove')
179+
180+
channel.subscribe()
181+
182+
await client.remove_channel(channel)
183+
```
184+
185+
- Remove all channels
186+
187+
```python
188+
# Setup...
189+
190+
channel1 = client.channel('a-channel-to-remove')
191+
channel2 = client.channel('another-channel-to-remove')
192+
193+
await channel1.subscribe()
194+
await channel2.subscribe()
195+
196+
await client.remove_all_channels()
197+
```
198+
199+
## Credits
200+
201+
This repo draws heavily from [phoenix-js](https://github.com/phoenixframework/phoenix/tree/master/assets/js/phoenix).
202+
203+
## License
204+
205+
MIT.

src/realtime/flake.lock

Lines changed: 48 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)