@lunora/cli 1.0.0-alpha.330 → 1.0.0-alpha.331

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/cli",
3
- "version": "1.0.0-alpha.330",
3
+ "version": "1.0.0-alpha.331",
4
4
  "description": "The Lunora CLI: init, dev, deploy, codegen, migrate, seed, doctor, insights, logs, registry, and the rest of the project commands",
5
5
  "keywords": [
6
6
  "agent-skills",
@@ -52,16 +52,16 @@
52
52
  },
53
53
  "dependencies": {
54
54
  "@bomb.sh/tab": "0.0.22",
55
- "@lunora/advisor": "1.0.0-alpha.175",
55
+ "@lunora/advisor": "1.0.0-alpha.176",
56
56
  "@lunora/bindings": "1.0.0-alpha.88",
57
- "@lunora/codegen": "1.0.0-alpha.243",
58
- "@lunora/config": "1.0.0-alpha.288",
57
+ "@lunora/codegen": "1.0.0-alpha.244",
58
+ "@lunora/config": "1.0.0-alpha.289",
59
59
  "@lunora/container": "1.0.0-alpha.64",
60
60
  "@lunora/d1": "1.0.0-alpha.155",
61
61
  "@lunora/errors": "1.0.0-alpha.48",
62
- "@lunora/mcp": "1.0.0-alpha.201",
63
- "@lunora/runtime": "1.0.0-alpha.167",
64
- "@lunora/seed": "1.0.0-alpha.173",
62
+ "@lunora/mcp": "1.0.0-alpha.202",
63
+ "@lunora/runtime": "1.0.0-alpha.168",
64
+ "@lunora/seed": "1.0.0-alpha.174",
65
65
  "@visulima/cerebro": "3.1.1",
66
66
  "@visulima/error": "6.1.0",
67
67
  "@visulima/fs": "6.0.7",
@@ -220,6 +220,7 @@ So write the call first, then run `lunora codegen` to surface the typed context:
220
220
  | `ctx.ai` | `@lunora/ai` (Workers AI) |
221
221
  | `ctx.flags` | `@lunora/flags` (OpenFeature) |
222
222
  | `ctx.queues.<name>` | `@lunora/queue` |
223
+ | `ctx.topics.<name>` (pub/sub, see below) | `@lunora/queue` |
223
224
  | `ctx.workflows` / `ctx.runStep` | `@lunora/workflow` |
224
225
  | `ctx.containers` | `@lunora/container` |
225
226
  | `ctx.browser` (action-only) | `@lunora/browser` |
@@ -242,6 +243,69 @@ non-deterministic I/O: a query is re-run on every subscription re-evaluation, so
242
243
  a non-deterministic read makes reactivity wrong, and a mutation's writes are
243
244
  transactional — a rollback cannot un-send a network call.
244
245
 
246
+ ### Topics: one event, many consumers
247
+
248
+ A queue has one consumer. To fan one event out to several independent
249
+ consumers, declare a topic and subscribe to it — in `lunora/queues.ts`, next to
250
+ your queues. Each subscription is its own queue with its own retries and
251
+ dead-letter queue.
252
+
253
+ ```ts
254
+ // lunora/queues.ts
255
+ import { defineSubscription, defineTopic } from "@lunora/queue";
256
+
257
+ import { internal } from "./_generated/api";
258
+
259
+ export const signups = defineTopic<{ userId: string }>();
260
+
261
+ export const welcomeEmail = defineSubscription(signups, {
262
+ handler: async (_ctx, batch) => {
263
+ for (const message of batch.messages) {
264
+ await message.run(internal.email.welcome, { userId: message.body.userId });
265
+ message.ack();
266
+ }
267
+ },
268
+ maxRetries: 5,
269
+ deadLetterQueue: "welcome-email-dlq",
270
+ });
271
+
272
+ // in a mutation or action
273
+ await ctx.topics.signups.publish({ userId });
274
+ ```
275
+
276
+ - Delivery is **at-least-once per subscription** and unordered — make handlers
277
+ idempotent. A failed send rejects `publish`, and a retry re-delivers to the
278
+ subscriptions that already got it.
279
+ - A subscription is published to only through its topic, never `ctx.queues`.
280
+ The topic passed to `defineSubscription` must be a `defineTopic` export of the
281
+ same file.
282
+ - Rate-limit public procedures that publish: one publish is one send per
283
+ subscription (`privileged_fanout_from_public_procedure` flags it).
284
+
285
+ ## Modules: grouping `lunora/` folders
286
+
287
+ A folder whose `module.ts` default-exports `defineModule(...)` is a module.
288
+ Modules are metadata — no `api.*` path or runtime change, still one Worker —
289
+ that drive the Studio **Architecture** view, OpenAPI tags, and table ownership:
290
+
291
+ ```ts
292
+ // lunora/billing/module.ts
293
+ import { defineModule } from "@lunora/server";
294
+
295
+ export default defineModule({ description: "Invoices and payments", tables: ["invoices"] });
296
+ ```
297
+
298
+ - Write `description` and `tables` inline; codegen reads them without running
299
+ the file. Modules do not nest.
300
+ - `cross_module_table_write` warns when a function outside the owning module
301
+ writes an owned table (insert, `patch`/`replace`/`delete`, batch or facade).
302
+ Route the write through a function in the owning module instead.
303
+ - Installed components (`.extend(...)` schema extensions) count as modules that
304
+ own their prefixed tables: write them through the component's functions, not
305
+ `ctx.db` directly.
306
+ - Keep ids typed `Id<"table">` — a write through an untyped `string` id cannot
307
+ be attributed to a table.
308
+
245
309
  ## HTTP endpoints
246
310
 
247
311
  For webhooks or non-RPC HTTP, use `httpRouter` / `httpRoute` + `httpAction`:
@@ -284,4 +348,6 @@ export default app;
284
348
  - [ ] `ctx.db` writes only inside mutations; ids typed with `Id<"table">`.
285
349
  - [ ] Any `ctx.db.related` walk is narrowed with `edges` / `direction`, and its
286
350
  foreign keys are indexed.
351
+ - [ ] Writes to a module- or component-owned table go through the owner's
352
+ functions (`cross_module_table_write` is clean).
287
353
  - [ ] Ran `lunora codegen`; typecheck is clean.