Skip to content

Commit 56f0b96

Browse files
authored
Merge branch 'master' into tyler/update-nativeaot-llvm-infrastructure
2 parents 5893f67 + 7726fe8 commit 56f0b96

27 files changed

Lines changed: 1120 additions & 2 deletions

File tree

crates/core/src/subscription/module_subscription_actor.rs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4357,4 +4357,65 @@ mod tests {
43574357

43584358
Ok(())
43594359
}
4360+
4361+
#[tokio::test]
4362+
async fn test_subscriptions_for_the_same_client_identity() -> anyhow::Result<()> {
4363+
let db = relational_db()?;
4364+
4365+
let identity = identity_from_u8(7);
4366+
let client_id_for_a = ClientActorId {
4367+
identity,
4368+
connection_id: connection_id_from_u8(1),
4369+
name: ClientName(1),
4370+
};
4371+
let client_id_for_b = ClientActorId {
4372+
identity,
4373+
connection_id: connection_id_from_u8(2),
4374+
name: ClientName(2),
4375+
};
4376+
4377+
let (tx_for_a, mut rx_for_a) = client_connection(client_id_for_a, &db);
4378+
let (tx_for_b, mut rx_for_b) = client_connection(client_id_for_b, &db);
4379+
4380+
let auth_for_a = AuthCtx::new(db.owner_identity(), client_id_for_a.identity);
4381+
let auth_for_b = AuthCtx::new(db.owner_identity(), client_id_for_b.identity);
4382+
4383+
let subs = ModuleSubscriptions::for_test_enclosing_runtime(db.clone());
4384+
let table_id = db.create_table_for_test("t", &[("a", AlgebraicType::U8)], &[])?;
4385+
let schema = ProductType::from([AlgebraicType::U8]);
4386+
4387+
let mut query_ids = 0;
4388+
subscribe_multi(
4389+
&subs,
4390+
auth_for_a,
4391+
&["select * from t where a = 1"],
4392+
tx_for_a,
4393+
&mut query_ids,
4394+
)
4395+
.await?;
4396+
subscribe_multi(
4397+
&subs,
4398+
auth_for_b,
4399+
&["select * from t where a = 2"],
4400+
tx_for_b,
4401+
&mut query_ids,
4402+
)
4403+
.await?;
4404+
4405+
assert!(matches!(
4406+
rx_for_a.recv().await,
4407+
Some(OutboundMessage::V1(SerializableMessage::Subscription(_)))
4408+
));
4409+
assert!(matches!(
4410+
rx_for_b.recv().await,
4411+
Some(OutboundMessage::V1(SerializableMessage::Subscription(_)))
4412+
));
4413+
4414+
commit_tx(&db, &subs, [], [(table_id, product![1_u8]), (table_id, product![2_u8])])?;
4415+
4416+
assert_tx_update_for_table(rx_for_a.recv(), table_id, &schema, [product![1_u8]], []).await;
4417+
assert_tx_update_for_table(rx_for_b.recv(), table_id, &schema, [product![2_u8]], []).await;
4418+
4419+
Ok(())
4420+
}
43604421
}
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
---
2+
title: Astro Quickstart
3+
sidebar_label: Astro
4+
slug: /quickstarts/astro
5+
hide_table_of_contents: true
6+
---
7+
8+
import { InstallCardLink } from "@site/src/components/InstallCardLink";
9+
import { StepByStep, Step, StepText, StepCode } from "@site/src/components/Steps";
10+
11+
12+
Get a SpacetimeDB Astro app running in under 5 minutes.
13+
14+
## Prerequisites
15+
16+
- [Node.js](https://nodejs.org/) 18+ installed
17+
- [SpacetimeDB CLI](https://spacetimedb.com/install) installed
18+
19+
<InstallCardLink />
20+
21+
---
22+
23+
<StepByStep>
24+
<Step title="Create your project">
25+
<StepText>
26+
Run the `spacetime dev` command to create a new project with a SpacetimeDB module and Astro client.
27+
28+
This will start the local SpacetimeDB server, publish your module, generate TypeScript bindings, and start the Astro development server.
29+
</StepText>
30+
<StepCode>
31+
```bash
32+
spacetime dev --template astro-ts
33+
```
34+
</StepCode>
35+
</Step>
36+
37+
<Step title="Open your app">
38+
<StepText>
39+
Navigate to [http://localhost:4321](http://localhost:4321) to see your app running.
40+
41+
The Astro app reads `SPACETIMEDB_*` variables on the server and `PUBLIC_SPACETIMEDB_*` variables in the client, so `.env.local` can configure both sides of the app.
42+
</StepText>
43+
</Step>
44+
45+
<Step title="Explore the project structure">
46+
<StepText>
47+
Your project contains both server and client code using Astro SSR and a live React island for real-time updates.
48+
49+
Edit `spacetimedb/src/index.ts` to add tables and reducers. Edit `src/pages/index.astro` and `src/components/PersonList.tsx` to build your UI.
50+
</StepText>
51+
<StepCode>
52+
```text
53+
my-astro-app/
54+
├── spacetimedb/ # Your SpacetimeDB module
55+
│ └── src/
56+
│ └── index.ts # SpacetimeDB module logic
57+
├── src/
58+
│ ├── components/
59+
│ │ ├── PersonList.tsx
60+
│ │ ├── SpacetimeApp.tsx
61+
│ │ └── DeferredPeopleSnapshot.astro
62+
│ ├── lib/
63+
│ │ └── spacetimedb-server.ts
64+
│ ├── module_bindings/ # Auto-generated types
65+
│ ├── layouts/
66+
│ │ └── Layout.astro
67+
│ ├── pages/
68+
│ │ └── index.astro
69+
│ └── styles/
70+
│ └── global.css
71+
└── package.json
72+
```
73+
</StepCode>
74+
</Step>
75+
76+
<Step title="Understand tables and reducers">
77+
<StepText>
78+
Open `spacetimedb/src/index.ts` to see the module code. The template includes a `person` table and two reducers: `add` to insert a person, and `sayHello` to greet everyone.
79+
80+
Tables store your data. Reducers are functions that modify data and are the only way to write to the database.
81+
</StepText>
82+
<StepCode>
83+
```typescript
84+
import { schema, table, t } from 'spacetimedb/server';
85+
86+
const spacetimedb = schema({
87+
person: table(
88+
{ public: true },
89+
{
90+
name: t.string(),
91+
}
92+
),
93+
});
94+
export default spacetimedb;
95+
96+
export const add = spacetimedb.reducer(
97+
{ name: t.string() },
98+
(ctx, { name }) => {
99+
ctx.db.person.insert({ name });
100+
}
101+
);
102+
103+
export const sayHello = spacetimedb.reducer(ctx => {
104+
for (const person of ctx.db.person.iter()) {
105+
console.info(`Hello, ${person.name}!`);
106+
}
107+
console.info('Hello, World!');
108+
});
109+
```
110+
</StepCode>
111+
</Step>
112+
113+
<Step title="Test with the CLI">
114+
<StepText>
115+
Open a new terminal and navigate to your project directory. Then use the SpacetimeDB CLI to call reducers and query your data directly.
116+
</StepText>
117+
<StepCode>
118+
```bash
119+
cd my-spacetime-app
120+
121+
# Call the add reducer to insert a person
122+
spacetime call add Alice
123+
124+
# Query the person table
125+
spacetime sql "SELECT * FROM person"
126+
name
127+
---------
128+
"Alice"
129+
130+
# Call sayHello to greet everyone
131+
spacetime call say_hello
132+
133+
# View the module logs
134+
spacetime logs
135+
2025-01-13T12:00:00.000000Z INFO: Hello, Alice!
136+
2025-01-13T12:00:00.000000Z INFO: Hello, World!
137+
```
138+
</StepCode>
139+
</Step>
140+
141+
<Step title="Understand Astro SSR and real-time hydration">
142+
<StepText>
143+
The SpacetimeDB SDK works both server-side and client-side. The template uses a hybrid approach:
144+
145+
- **Astro page** (`src/pages/index.astro`): Fetches initial data on the server for a fast first render
146+
- **React island** (`src/components/SpacetimeApp.tsx`): Hydrates on the client and provides the SpacetimeDB connection
147+
- **Live table UI** (`src/components/PersonList.tsx`): Uses `useTable()` and `useReducer()` for real-time updates
148+
</StepText>
149+
<StepCode>
150+
```astro
151+
---
152+
import Layout from '../layouts/Layout.astro';
153+
import SpacetimeApp from '../components/SpacetimeApp';
154+
import { fetchPeople } from '../lib/spacetimedb-server';
155+
156+
const initialPeople = await fetchPeople();
157+
---
158+
159+
<Layout title="astro-ts">
160+
<h1>astro-ts</h1>
161+
<SpacetimeApp client:load initialPeople={initialPeople} />
162+
</Layout>
163+
```
164+
</StepCode>
165+
</Step>
166+
167+
<Step title="Understand Astro server islands">
168+
<StepText>
169+
The template also includes a deferred Astro-only section to demonstrate `server:defer`.
170+
171+
`src/components/DeferredPeopleSnapshot.astro` fetches its own server-rendered snapshot, and `src/pages/index.astro` renders it as a server island without changing the main real-time client flow.
172+
</StepText>
173+
<StepCode>
174+
```astro
175+
<DeferredPeopleSnapshot server:defer>
176+
<div slot="fallback">Loading deferred people snapshot…</div>
177+
</DeferredPeopleSnapshot>
178+
```
179+
</StepCode>
180+
</Step>
181+
</StepByStep>

docs/docs/00200-core-concepts/00200-functions/00400-procedures.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ export default spacetimedb;
145145
146146
export const insert_a_value = spacetimedb.procedure({ a: t.u32(), b: t.u32() }, t.unit(), (ctx, { a, b }) => {
147147
ctx.withTx(ctx => {
148-
ctx.myTable.insert({ a, b });
148+
ctx.db.myTable.insert({ a, b });
149149
});
150150
return {};
151151
})
@@ -332,7 +332,7 @@ export const maybe_insert_a_value = spacetimedb.procedure({ a: t.u32(), b: t.str
332332
if (a < 10) {
333333
throw new SenderError("a is less than 10!");
334334
}
335-
ctx.myTable.insert({ a, b });
335+
ctx.db.myTable.insert({ a, b });
336336
});
337337
})
338338
```

templates/astro-ts/.gitignore

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# build output
2+
dist/
3+
spacetimedb/dist/
4+
5+
# generated types
6+
.astro/
7+
8+
# dependencies
9+
node_modules/
10+
11+
# logs
12+
npm-debug.log*
13+
yarn-debug.log*
14+
yarn-error.log*
15+
pnpm-debug.log*
16+
17+
# environment variables
18+
.env
19+
.env.local
20+
.env.*.local
21+
.env.production
22+
23+
# macOS-specific files
24+
.DS_Store
25+
26+
# jetbrains setting folder
27+
.idea/
28+

templates/astro-ts/.template.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"description": "Astro app with a TypeScript server module",
3+
"client_framework": "Astro",
4+
"client_lang": "typescript",
5+
"server_lang": "typescript",
6+
"builtWith": [
7+
"astro",
8+
"react",
9+
"react-dom",
10+
"typescript",
11+
"spacetimedb"
12+
]
13+
}

templates/astro-ts/LICENSE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../licenses/apache2.txt

0 commit comments

Comments
 (0)