create-seiro 0.1.6 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -7,9 +7,9 @@ CQRS over WebSocket with Bun + Preact Signals + Web Components.
|
|
|
7
7
|
```
|
|
8
8
|
← { profile } sent on connect (User or null)
|
|
9
9
|
|
|
10
|
-
→ { cmd, cid, data }
|
|
11
|
-
← { cid, result } command success
|
|
12
|
-
← { cid, err } command error
|
|
10
|
+
→ { cmd, cid, data, ack? } command request (ack=true requests response)
|
|
11
|
+
← { cid, result } command success (only if ack was true)
|
|
12
|
+
← { cid, err } command error (always sent on failure)
|
|
13
13
|
|
|
14
14
|
→ { q, id, params } query request
|
|
15
15
|
← { id, row } query row (repeated)
|
|
@@ -22,6 +22,8 @@ CQRS over WebSocket with Bun + Preact Signals + Web Components.
|
|
|
22
22
|
→ { unsub: "pattern" } unsubscribe
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
+
The `ack` flag controls whether the server sends a success response. When `ack: true`, the server sends `{ cid, result }` on completion. When `ack` is absent or false, no success response is sent (fire-and-forget). Error responses are always sent regardless of `ack`.
|
|
26
|
+
|
|
25
27
|
## Type Definitions
|
|
26
28
|
|
|
27
29
|
Use `Command<D, R>` and `Query<P, R>` helpers:
|
|
@@ -93,31 +95,44 @@ export async function register<
|
|
|
93
95
|
E extends EntityEvents,
|
|
94
96
|
>(server: Server<C, Q, E>, sql: Sql, listener?: Sql) {
|
|
95
97
|
// Listen to postgres notifications (if listener provided)
|
|
98
|
+
// The 3rd callback is called on connect AND reconnect - postgres.js handles reconnection internally
|
|
96
99
|
if (listener) {
|
|
97
|
-
await listener.listen(
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
100
|
+
await listener.listen(
|
|
101
|
+
"entity_created",
|
|
102
|
+
(payload: string) => {
|
|
103
|
+
try {
|
|
104
|
+
server.emit("entity_created", JSON.parse(payload) as Entity);
|
|
105
|
+
} catch (e) {
|
|
106
|
+
console.error("Failed to parse entity_created payload:", payload, e);
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
() => console.log("Listening on entity_created"),
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
await listener.listen(
|
|
113
|
+
"entity_updated",
|
|
114
|
+
(payload: string) => {
|
|
115
|
+
try {
|
|
116
|
+
server.emit("entity_updated", JSON.parse(payload) as Entity);
|
|
117
|
+
} catch (e) {
|
|
118
|
+
console.error("Failed to parse entity_updated payload:", payload, e);
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
() => console.log("Listening on entity_updated"),
|
|
122
|
+
);
|
|
112
123
|
|
|
113
124
|
// Different payload type for delete - just the id
|
|
114
|
-
await listener.listen(
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
125
|
+
await listener.listen(
|
|
126
|
+
"entity_deleted",
|
|
127
|
+
(payload: string) => {
|
|
128
|
+
try {
|
|
129
|
+
server.emit("entity_deleted", JSON.parse(payload) as { id: number });
|
|
130
|
+
} catch (e) {
|
|
131
|
+
console.error("Failed to parse entity_deleted payload:", payload, e);
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
() => console.log("Listening on entity_deleted"),
|
|
135
|
+
);
|
|
121
136
|
}
|
|
122
137
|
|
|
123
138
|
// Command with typed result
|
|
@@ -163,12 +178,15 @@ client.subscribe();
|
|
|
163
178
|
## Client API
|
|
164
179
|
|
|
165
180
|
```typescript
|
|
166
|
-
// Command with callbacks
|
|
181
|
+
// Command with callbacks (sends ack: true, server responds on success)
|
|
167
182
|
client.cmd("entity.save", { id: 1, name: "Updated" }, {
|
|
168
183
|
onSuccess: (result) => navigate(`#/entities/${result.id}`),
|
|
169
184
|
onError: (err) => showError(err),
|
|
170
185
|
});
|
|
171
186
|
|
|
187
|
+
// Fire-and-forget command (no callbacks = no ack, no success response)
|
|
188
|
+
client.cmd("analytics.track", { event: "page_view" });
|
|
189
|
+
|
|
172
190
|
// Query (async iterator)
|
|
173
191
|
for await (const row of client.query("entities.all")) {
|
|
174
192
|
items.push(row);
|
|
@@ -357,6 +375,29 @@ server.query("logs.stream", async function* (params, ctx) {
|
|
|
357
375
|
});
|
|
358
376
|
```
|
|
359
377
|
|
|
378
|
+
## PostgreSQL Listener Reconnection
|
|
379
|
+
|
|
380
|
+
LISTEN blocks the connection - if it drops, you need to re-establish subscriptions. The postgres.js library handles this automatically via the `onlisten` callback (third parameter to `sql.listen()`):
|
|
381
|
+
|
|
382
|
+
```typescript
|
|
383
|
+
await listener.listen(
|
|
384
|
+
"entity_created",
|
|
385
|
+
(payload: string) => {
|
|
386
|
+
// Handle notification
|
|
387
|
+
server.emit("entity_created", JSON.parse(payload) as Entity);
|
|
388
|
+
},
|
|
389
|
+
() => {
|
|
390
|
+
// Called on initial connect AND on reconnect
|
|
391
|
+
console.log("Listening on entity_created");
|
|
392
|
+
},
|
|
393
|
+
);
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
The `onlisten` callback is useful for:
|
|
397
|
+
- Logging when listeners are active (helpful for debugging)
|
|
398
|
+
- Fetching any missed data after reconnection
|
|
399
|
+
- Re-syncing state with the database
|
|
400
|
+
|
|
360
401
|
## Conventions
|
|
361
402
|
|
|
362
403
|
- Commands return `{ id }` for create/save operations
|