@pattern-stack/codegen 0.8.0 → 0.9.0

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.
Files changed (118) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/dist/runtime/subsystems/auth/controllers/auth.controller.d.ts +1 -0
  3. package/dist/runtime/subsystems/auth/index.d.ts +2 -0
  4. package/dist/runtime/subsystems/auth/index.js +55 -0
  5. package/dist/runtime/subsystems/auth/index.js.map +1 -1
  6. package/dist/runtime/subsystems/auth/middleware/requester-context.d.ts +81 -0
  7. package/dist/runtime/subsystems/auth/middleware/requester-context.js +60 -0
  8. package/dist/runtime/subsystems/auth/middleware/requester-context.js.map +1 -0
  9. package/dist/runtime/subsystems/auth/protocols/user-context.d.ts +18 -0
  10. package/dist/runtime/subsystems/bridge/bridge-delivery-handler.js.map +1 -1
  11. package/dist/runtime/subsystems/bridge/bridge-outbox-drain-hook.js.map +1 -1
  12. package/dist/runtime/subsystems/bridge/bridge.module.d.ts +3 -0
  13. package/dist/runtime/subsystems/bridge/bridge.module.js +930 -275
  14. package/dist/runtime/subsystems/bridge/bridge.module.js.map +1 -1
  15. package/dist/runtime/subsystems/bridge/event-flow.service.js.map +1 -1
  16. package/dist/runtime/subsystems/bridge/index.d.ts +3 -0
  17. package/dist/runtime/subsystems/bridge/index.js +837 -182
  18. package/dist/runtime/subsystems/bridge/index.js.map +1 -1
  19. package/dist/runtime/subsystems/events/event-bus.drizzle-backend.d.ts +3 -1
  20. package/dist/runtime/subsystems/events/event-bus.drizzle-backend.js +92 -1
  21. package/dist/runtime/subsystems/events/event-bus.drizzle-backend.js.map +1 -1
  22. package/dist/runtime/subsystems/events/event-bus.memory-backend.d.ts +3 -1
  23. package/dist/runtime/subsystems/events/event-bus.memory-backend.js +99 -0
  24. package/dist/runtime/subsystems/events/event-bus.memory-backend.js.map +1 -1
  25. package/dist/runtime/subsystems/events/event-bus.redis-backend.js.map +1 -1
  26. package/dist/runtime/subsystems/events/event-keyset-cursor.d.ts +32 -0
  27. package/dist/runtime/subsystems/events/event-keyset-cursor.js +38 -0
  28. package/dist/runtime/subsystems/events/event-keyset-cursor.js.map +1 -0
  29. package/dist/runtime/subsystems/events/event-read.protocol.d.ts +94 -0
  30. package/dist/runtime/subsystems/events/event-read.protocol.js +9 -0
  31. package/dist/runtime/subsystems/events/event-read.protocol.js.map +1 -0
  32. package/dist/runtime/subsystems/events/events.module.js +177 -3
  33. package/dist/runtime/subsystems/events/events.module.js.map +1 -1
  34. package/dist/runtime/subsystems/events/events.tokens.d.ts +16 -1
  35. package/dist/runtime/subsystems/events/events.tokens.js +2 -0
  36. package/dist/runtime/subsystems/events/events.tokens.js.map +1 -1
  37. package/dist/runtime/subsystems/events/generated/bus.js.map +1 -1
  38. package/dist/runtime/subsystems/events/generated/index.js.map +1 -1
  39. package/dist/runtime/subsystems/events/index.d.ts +2 -1
  40. package/dist/runtime/subsystems/events/index.js +178 -3
  41. package/dist/runtime/subsystems/events/index.js.map +1 -1
  42. package/dist/runtime/subsystems/index.d.ts +2 -0
  43. package/dist/runtime/subsystems/index.js +1198 -264
  44. package/dist/runtime/subsystems/index.js.map +1 -1
  45. package/dist/runtime/subsystems/jobs/bullmq.config.d.ts +98 -0
  46. package/dist/runtime/subsystems/jobs/bullmq.config.js +143 -0
  47. package/dist/runtime/subsystems/jobs/bullmq.config.js.map +1 -0
  48. package/dist/runtime/subsystems/jobs/index.d.ts +6 -2
  49. package/dist/runtime/subsystems/jobs/index.js +861 -201
  50. package/dist/runtime/subsystems/jobs/index.js.map +1 -1
  51. package/dist/runtime/subsystems/jobs/job-orchestrator.bullmq-backend.d.ts +107 -0
  52. package/dist/runtime/subsystems/jobs/job-orchestrator.bullmq-backend.js +922 -0
  53. package/dist/runtime/subsystems/jobs/job-orchestrator.bullmq-backend.js.map +1 -0
  54. package/dist/runtime/subsystems/jobs/job-run-keyset-cursor.d.ts +52 -0
  55. package/dist/runtime/subsystems/jobs/job-run-keyset-cursor.js +57 -0
  56. package/dist/runtime/subsystems/jobs/job-run-keyset-cursor.js.map +1 -0
  57. package/dist/runtime/subsystems/jobs/job-run-service.drizzle-backend.d.ts +2 -1
  58. package/dist/runtime/subsystems/jobs/job-run-service.drizzle-backend.js +81 -1
  59. package/dist/runtime/subsystems/jobs/job-run-service.drizzle-backend.js.map +1 -1
  60. package/dist/runtime/subsystems/jobs/job-run-service.memory-backend.d.ts +2 -1
  61. package/dist/runtime/subsystems/jobs/job-run-service.memory-backend.js +81 -0
  62. package/dist/runtime/subsystems/jobs/job-run-service.memory-backend.js.map +1 -1
  63. package/dist/runtime/subsystems/jobs/job-run-service.protocol.d.ts +74 -1
  64. package/dist/runtime/subsystems/jobs/job-worker.bullmq-backend.d.ts +48 -0
  65. package/dist/runtime/subsystems/jobs/job-worker.bullmq-backend.js +374 -0
  66. package/dist/runtime/subsystems/jobs/job-worker.bullmq-backend.js.map +1 -0
  67. package/dist/runtime/subsystems/jobs/job-worker.module.d.ts +42 -4
  68. package/dist/runtime/subsystems/jobs/job-worker.module.js +832 -178
  69. package/dist/runtime/subsystems/jobs/job-worker.module.js.map +1 -1
  70. package/dist/runtime/subsystems/jobs/jobs-domain.module.d.ts +10 -1
  71. package/dist/runtime/subsystems/jobs/jobs-domain.module.js +519 -20
  72. package/dist/runtime/subsystems/jobs/jobs-domain.module.js.map +1 -1
  73. package/dist/runtime/subsystems/jobs/pool-config.loader.d.ts +9 -1
  74. package/dist/runtime/subsystems/jobs/pool-config.loader.js +4 -0
  75. package/dist/runtime/subsystems/jobs/pool-config.loader.js.map +1 -1
  76. package/dist/runtime/subsystems/observability/index.d.ts +4 -3
  77. package/dist/runtime/subsystems/observability/index.js +109 -2
  78. package/dist/runtime/subsystems/observability/index.js.map +1 -1
  79. package/dist/runtime/subsystems/observability/observability.module.js +109 -2
  80. package/dist/runtime/subsystems/observability/observability.module.js.map +1 -1
  81. package/dist/runtime/subsystems/observability/observability.protocol.d.ts +63 -2
  82. package/dist/runtime/subsystems/observability/observability.service.d.ts +21 -3
  83. package/dist/runtime/subsystems/observability/observability.service.js +109 -2
  84. package/dist/runtime/subsystems/observability/observability.service.js.map +1 -1
  85. package/dist/runtime/subsystems/observability/reporters/bridge-metrics.reporter.d.ts +1 -0
  86. package/dist/runtime/subsystems/observability/reporters/index.d.ts +1 -0
  87. package/dist/src/cli/index.js +43 -7
  88. package/dist/src/cli/index.js.map +1 -1
  89. package/package.json +1 -1
  90. package/runtime/subsystems/auth/index.ts +8 -0
  91. package/runtime/subsystems/auth/middleware/requester-context.ts +141 -0
  92. package/runtime/subsystems/auth/protocols/user-context.ts +17 -0
  93. package/runtime/subsystems/bridge/bridge.module.ts +5 -0
  94. package/runtime/subsystems/events/event-bus.drizzle-backend.ts +109 -3
  95. package/runtime/subsystems/events/event-bus.memory-backend.ts +103 -1
  96. package/runtime/subsystems/events/event-keyset-cursor.ts +59 -0
  97. package/runtime/subsystems/events/event-read.protocol.ts +97 -0
  98. package/runtime/subsystems/events/events.module.ts +18 -2
  99. package/runtime/subsystems/events/events.tokens.ts +16 -0
  100. package/runtime/subsystems/events/index.ts +7 -0
  101. package/runtime/subsystems/jobs/bullmq.config.ts +125 -0
  102. package/runtime/subsystems/jobs/index.ts +22 -0
  103. package/runtime/subsystems/jobs/job-orchestrator.bullmq-backend.ts +381 -0
  104. package/runtime/subsystems/jobs/job-run-keyset-cursor.ts +88 -0
  105. package/runtime/subsystems/jobs/job-run-service.drizzle-backend.ts +59 -1
  106. package/runtime/subsystems/jobs/job-run-service.memory-backend.ts +53 -0
  107. package/runtime/subsystems/jobs/job-run-service.protocol.ts +77 -0
  108. package/runtime/subsystems/jobs/job-worker.bullmq-backend.ts +311 -0
  109. package/runtime/subsystems/jobs/job-worker.module.ts +124 -10
  110. package/runtime/subsystems/jobs/jobs-domain.module.ts +40 -21
  111. package/runtime/subsystems/jobs/pool-config.loader.ts +11 -0
  112. package/runtime/subsystems/observability/index.ts +8 -0
  113. package/runtime/subsystems/observability/observability.protocol.ts +76 -0
  114. package/runtime/subsystems/observability/observability.service.ts +148 -1
  115. package/templates/entity/new/clean-lite-ps/prompt-extension.js +14 -12
  116. package/templates/relationship/new/prompt.js +8 -5
  117. package/templates/subsystem/jobs/worker.ejs.t +30 -7
  118. package/templates/subsystem/sync/sync-audit.schema.ejs.t +12 -16
@@ -203,10 +203,13 @@ function processFields(fields) {
203
203
  function zodChainForCreate(field) {
204
204
  const { type, nullable, required, hasDefault, hasChoices, choices } = field;
205
205
 
206
+ // Nullability and optionality are independent — see the clean-lite-ps copy of
207
+ // this function for the rationale (nullable-and-optional fields must get both
208
+ // `.nullable()` and `.optional()`, not just `.nullable()`).
206
209
  if (hasChoices) {
207
- const base = `z.enum([${choices.map((c) => `'${c}'`).join(", ")}])`;
208
- if (!required && !nullable) return base + ".optional()";
209
- if (nullable) return base + ".nullable()";
210
+ let base = `z.enum([${choices.map((c) => `'${c}'`).join(", ")}])`;
211
+ if (nullable) base += ".nullable()";
212
+ if (!required) base += ".optional()";
210
213
  return base;
211
214
  }
212
215
 
@@ -215,8 +218,8 @@ function zodChainForCreate(field) {
215
218
  base += `.default(${field.default ?? false})`;
216
219
  return base;
217
220
  }
218
- if (nullable) return base + ".nullable()";
219
- if (!required) return base + ".optional()";
221
+ if (nullable) base += ".nullable()";
222
+ if (!required) base += ".optional()";
220
223
  return base;
221
224
  }
222
225
 
@@ -5,13 +5,30 @@ unless_exists: true
5
5
  /**
6
6
  * Standalone job worker entrypoint — emitted by `codegen subsystem install jobs`.
7
7
  *
8
- * Boots a Nest application context (NO HTTP listener) wiring the jobs domain
9
- * module plus JobWorkerModule in `standalone` mode. Run with:
8
+ * Boots a Nest application context (NO HTTP listener) wiring the full
9
+ * subsystem barrel (`SUBSYSTEM_MODULES` events + jobs + bridge + sync, in
10
+ * dependency order) plus `JobWorkerModule.forRoot({ mode: 'standalone',
11
+ * allPools: true })`. Run with:
10
12
  *
11
13
  * bun worker.ts
12
14
  *
15
+ * Why the barrel + `allPools`:
16
+ * - The events subsystem's outbox drain and the bridge's fanout wrappers
17
+ * run as `job_run` rows in the RESERVED `events_*` pools. A worker that
18
+ * only polls the non-reserved pools (`interactive`, `batch`, …) leaves
19
+ * those lanes stranded — `BridgeDeliveryHandler` never fires and durable
20
+ * event→job fanout silently stops.
21
+ * - `allPools: true` activates every pool in the resolved config, reserved
22
+ * lanes included, so this single standalone process drains both user work
23
+ * and the framework's reserved lanes.
24
+ * - Importing `SUBSYSTEM_MODULES` (rather than `JobsDomainModule` alone)
25
+ * registers `EVENT_BUS` / `JOB_ORCHESTRATOR` / `BRIDGE_*` so the
26
+ * framework `@framework/bridge_delivery` handler resolves its DI deps.
27
+ * `BridgeModule`'s reserved-pool guard short-circuits to pass because
28
+ * `allPools` is set.
29
+ *
13
30
  * Embedded mode (single-process) is configured by importing
14
- * JobWorkerModule.forRoot({ mode: 'embedded' }) inside AppModule instead —
31
+ * `JobWorkerModule.forRoot({ mode: 'embedded' })` inside AppModule instead —
15
32
  * see the commented guidance injected into `src/main.ts`.
16
33
  *
17
34
  * SIGTERM triggers graceful shutdown bounded by SHUTDOWN_TIMEOUT_MS; after the
@@ -23,16 +40,22 @@ import { Logger, Module } from '@nestjs/common';
23
40
  import { NestFactory } from '@nestjs/core';
24
41
 
25
42
  import { DatabaseModule } from '@shared/database/database.module';
26
- import { JobsDomainModule } from '@shared/subsystems/jobs/jobs-domain.module';
27
43
  import { JobWorkerModule } from '@shared/subsystems/jobs/job-worker.module';
44
+ import { SUBSYSTEM_MODULES } from '@generated/subsystems';
28
45
 
29
46
  const SHUTDOWN_TIMEOUT_MS = 30_000;
30
47
 
31
48
  @Module({
32
49
  imports: [
33
50
  DatabaseModule,
34
- JobsDomainModule.forRoot({ backend: 'drizzle' }),
35
- JobWorkerModule.forRoot({ mode: 'standalone' }),
51
+ // Events + Jobs + Bridge + Sync (dependency-ordered) from the generated
52
+ // barrel. This is the same composition AppModule imports — keeping the
53
+ // worker's DI graph identical to the HTTP app's so handlers resolve the
54
+ // same way in both processes.
55
+ ...SUBSYSTEM_MODULES,
56
+ // `allPools: true` drains the reserved `events_*` lanes (events outbox +
57
+ // bridge wrappers) alongside the user pools.
58
+ JobWorkerModule.forRoot({ mode: 'standalone', allPools: true }),
36
59
  ],
37
60
  })
38
61
  class WorkerAppModule {}
@@ -72,7 +95,7 @@ async function bootstrap(): Promise<void> {
72
95
  void shutdown('SIGINT');
73
96
  });
74
97
 
75
- logger.log('job worker started (standalone mode)');
98
+ logger.log('job worker started (standalone mode, all pools)');
76
99
  }
77
100
 
78
101
  bootstrap().catch((err) => {
@@ -19,14 +19,16 @@ force: true
19
19
  * is owned by the sync subsystem's runtime protocol
20
20
  * (`sync-field-diff.protocol.ts` from SYNC-2).
21
21
  *
22
- * ## `tenant_id` columns — scaffold-time conditional
22
+ * ## `tenant_id` columns — always emitted
23
23
  *
24
- * When `sync.multi_tenant: true` in `codegen.config.yaml`, this schema
25
- * emits `tenant_id` as a nullable text column on all three tables. The
26
- * `SYNC_MULTI_TENANT` DI flag (SYNC-6) enforces non-null at runtime
27
- * across the orchestrator + Drizzle backends. Enabling post-install
28
- * requires reinstalling this subsystem (`subsystem install sync --force
29
- * --force-config`) plus an Atlas migration.
24
+ * `tenant_id` is emitted as a nullable text column on all three tables
25
+ * REGARDLESS of `sync.multi_tenant` the runtime sync code (cursor store +
26
+ * run recorder) references `tenant_id` unconditionally, so a `multi_tenant:
27
+ * false` consumer that omitted the column failed to type-check (the column
28
+ * was referenced but absent). The `SYNC_MULTI_TENANT` DI flag (SYNC-6) gates
29
+ * non-null *enforcement* at runtime; it does not gate the column's existence
30
+ * (mirrors the jobs subsystem). Under `multi_tenant: false` the column simply
31
+ * stays null.
30
32
  *
31
33
  * See SYNC-1 / SYNC-6 in epic #60 for the decision rationale.
32
34
  */
@@ -98,9 +100,7 @@ export const syncSubscriptions = pgTable(
98
100
  config: jsonb('config').notNull().default({}).$type<Record<string, unknown>>(),
99
101
  cursor: jsonb('cursor').$type<unknown>(),
100
102
  lastSyncAt: timestamp('last_sync_at', { withTimezone: true }),
101
- <% if (multiTenant) { -%>
102
- tenantId: text('tenant_id'), // scaffold-time conditional — see sync.multi_tenant
103
- <% } -%>
103
+ tenantId: text('tenant_id'), // always emitted — the runtime sync code (cursor store + run recorder) references tenant_id unconditionally; SYNC_MULTI_TENANT gates enforcement, not the column's existence (mirrors jobs)
104
104
  createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
105
105
  updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
106
106
  },
@@ -141,9 +141,7 @@ export const syncRuns = pgTable(
141
141
  .notNull()
142
142
  .defaultNow(),
143
143
  completedAt: timestamp('completed_at', { withTimezone: true }),
144
- <% if (multiTenant) { -%>
145
- tenantId: text('tenant_id'), // scaffold-time conditional — see sync.multi_tenant
146
- <% } -%>
144
+ tenantId: text('tenant_id'), // always emitted — the runtime sync code (cursor store + run recorder) references tenant_id unconditionally; SYNC_MULTI_TENANT gates enforcement, not the column's existence (mirrors jobs)
147
145
  },
148
146
  (t) => ({
149
147
  idxSyncRunsSubscriptionStartedAt: index(
@@ -178,9 +176,7 @@ export const syncRunItems = pgTable(
178
176
  createdAt: timestamp('created_at', { withTimezone: true })
179
177
  .notNull()
180
178
  .defaultNow(),
181
- <% if (multiTenant) { -%>
182
- tenantId: text('tenant_id'), // scaffold-time conditional — see sync.multi_tenant
183
- <% } -%>
179
+ tenantId: text('tenant_id'), // always emitted — the runtime sync code (cursor store + run recorder) references tenant_id unconditionally; SYNC_MULTI_TENANT gates enforcement, not the column's existence (mirrors jobs)
184
180
  },
185
181
  (t) => ({
186
182
  idxSyncRunItemsRunCreatedAt: index('idx_sync_run_items_run_created_at').on(