@pattern-stack/codegen 0.27.1 → 0.27.3

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 (31) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/dist/{chunk-E5FJWOMP.js → chunk-3AM722ZH.js} +15 -6
  3. package/dist/chunk-3AM722ZH.js.map +1 -0
  4. package/dist/{chunk-DKKFTHHI.js → chunk-CZQUOIDY.js} +4 -4
  5. package/dist/{chunk-Z7YFYK6H.js → chunk-EGXFEZ2N.js} +4 -4
  6. package/dist/{chunk-KS4BZHIA.js → chunk-XW4XKN3F.js} +7 -7
  7. package/dist/{chunk-36NRG2EP.js → chunk-YQA5PMOD.js} +3 -3
  8. package/dist/runtime/base-classes/index.js +17 -17
  9. package/dist/runtime/subsystems/analytics/analytics.module.js +2 -2
  10. package/dist/runtime/subsystems/analytics/index.js +4 -4
  11. package/dist/runtime/subsystems/auth/auth.module.js +1 -1
  12. package/dist/runtime/subsystems/auth/index.js +3 -3
  13. package/dist/runtime/subsystems/bridge/bridge-outbox-drain-hook.js +1 -1
  14. package/dist/runtime/subsystems/bridge/bridge.module.js +7 -7
  15. package/dist/runtime/subsystems/bridge/bridge.protocol.d.ts +20 -14
  16. package/dist/runtime/subsystems/bridge/index.js +7 -7
  17. package/dist/runtime/subsystems/index.js +24 -24
  18. package/dist/runtime/subsystems/jobs/index.js +13 -13
  19. package/dist/runtime/subsystems/jobs/job-worker.module.js +5 -5
  20. package/dist/runtime/subsystems/jobs/jobs-domain.module.js +3 -3
  21. package/dist/src/cli/index.js +63 -19
  22. package/dist/src/cli/index.js.map +1 -1
  23. package/package.json +1 -1
  24. package/runtime/subsystems/bridge/bridge-outbox-drain-hook.ts +34 -11
  25. package/runtime/subsystems/bridge/bridge.protocol.ts +20 -14
  26. package/templates/entity/new/clean-lite-ps/dto/output.ejs.t +6 -0
  27. package/dist/chunk-E5FJWOMP.js.map +0 -1
  28. /package/dist/{chunk-DKKFTHHI.js.map → chunk-CZQUOIDY.js.map} +0 -0
  29. /package/dist/{chunk-Z7YFYK6H.js.map → chunk-EGXFEZ2N.js.map} +0 -0
  30. /package/dist/{chunk-KS4BZHIA.js.map → chunk-XW4XKN3F.js.map} +0 -0
  31. /package/dist/{chunk-36NRG2EP.js.map → chunk-YQA5PMOD.js.map} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pattern-stack/codegen",
3
- "version": "0.27.1",
3
+ "version": "0.27.3",
4
4
  "description": "Entity-driven code generation for full-stack TypeScript applications",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -82,22 +82,40 @@ export class BridgeOutboxDrainHook implements IBridgeOutboxDrainHook {
82
82
  event: DomainEvent,
83
83
  tx: DrizzleTransaction,
84
84
  ): Promise<BridgeOutboxDrainResult> {
85
- // Audit-tier guard (defense-in-depth — AUDIT-4). Audit events are not
86
- // bridge-eligible: the codegen-side validator (AUDIT-2) blocks the
87
- // registry from listing them as triggers. Reaching this branch means
88
- // registry/runtime drift an out-of-band `bridge_trigger` insert, or
89
- // version skew during deploy. Refuse fanout, surface drift via WARN.
85
+ const triggers = this.lookupTriggers(event.type);
86
+
87
+ // Audit-tier guard (defense-in-depth AUDIT-4). Audit events are
88
+ // bridge-inert by design: the codegen-side validator (AUDIT-2) blocks the
89
+ // registry from listing them as triggers, so an audit event should never
90
+ // have a matched trigger. The guard runs AFTER the lookup so it can tell
91
+ // apart the two cases that share `tier === 'audit'`:
92
+ // - triggers present → genuine registry/runtime drift (an out-of-band
93
+ // `bridge_trigger` insert, or version skew during deploy). Refuse
94
+ // fanout and surface the drift via WARN — now a verified claim, not a
95
+ // guess. (Revises the original top-of-method guard, which warned for
96
+ // every audit event: audit-tier lifecycle events like
97
+ // `connection.created` routinely share the outbox with domain events,
98
+ // so the unconditional warning was a false positive. See
99
+ // ai-docs/specs/issue-242/plan.md §AUDIT-4 revision note.)
100
+ // - no triggers → the common, benign case. Stay silent; auditBlocked: 0.
90
101
  if (event.metadata?.['tier'] === 'audit') {
91
- this.warnAuditBlockedOnce(event);
102
+ if (triggers.length > 0) {
103
+ this.warnAuditBlockedOnce(event, triggers);
104
+ return {
105
+ delivered: 0,
106
+ dedupSkips: 0,
107
+ triggerCount: 0,
108
+ auditBlocked: 1,
109
+ };
110
+ }
92
111
  return {
93
112
  delivered: 0,
94
113
  dedupSkips: 0,
95
114
  triggerCount: 0,
96
- auditBlocked: 1,
115
+ auditBlocked: 0,
97
116
  };
98
117
  }
99
118
 
100
- const triggers = this.lookupTriggers(event.type);
101
119
  if (triggers.length === 0) {
102
120
  return {
103
121
  delivered: 0,
@@ -247,13 +265,18 @@ export class BridgeOutboxDrainHook implements IBridgeOutboxDrainHook {
247
265
  };
248
266
  }
249
267
 
250
- private warnAuditBlockedOnce(event: DomainEvent): void {
268
+ private warnAuditBlockedOnce(
269
+ event: DomainEvent,
270
+ triggers: BridgeTriggerEntry[],
271
+ ): void {
251
272
  if (this.warnedAuditTypes.has(event.type)) return;
252
273
  this.warnedAuditTypes.add(event.type);
274
+ const triggerIds = triggers.map((t) => t.triggerId).join(', ');
253
275
  this.logger.warn(
254
276
  `Bridge guard blocked audit-tier event '${event.type}' (event.id=${event.id}). ` +
255
- `Registry says this event is not bridge-eligible; a bridge_trigger row exists ` +
256
- `out-of-band. Investigate registry/runtime drift.`,
277
+ `Audit events are not bridge-eligible, yet the registry has trigger(s) ` +
278
+ `registered against this type [${triggerIds}] — an out-of-band bridge_trigger ` +
279
+ `row or version skew. Investigate registry/runtime drift.`,
257
280
  );
258
281
  }
259
282
 
@@ -334,14 +334,16 @@ export type BridgeRegistry = {
334
334
  * `triggerCount`: total triggers matched in the registry for this event;
335
335
  * `triggerCount === delivered + dedupSkips`.
336
336
  *
337
- * `auditBlocked`: number of events the dispatcher refused to fan out
338
- * because their `metadata.tier === 'audit'`. Audit-tier events are not
339
- * bridge-eligible; codegen errors block the registry from listing them
340
- * as triggers, so a non-zero value here indicates registry/runtime drift
341
- * (an out-of-band `bridge_trigger` insert, version skew during deploy).
342
- * Per-event: `0` when the guard does not fire, `1` when it does. Add it
343
- * to per-batch logging if your drain caller aggregates results. See
344
- * ai-docs/specs/issue-242/plan.md §AUDIT-4.
337
+ * `auditBlocked`: number of audit-tier events the dispatcher refused to fan
338
+ * out *because they had a trigger registered against them*. Audit-tier events
339
+ * are not bridge-eligible; codegen errors block the registry from listing them
340
+ * as triggers, so a non-zero value here indicates genuine registry/runtime
341
+ * drift (an out-of-band `bridge_trigger` insert, version skew during deploy).
342
+ * A benign audit-tier event one with no matched trigger, the common case for
343
+ * lifecycle events sharing the outbox returns `0` and produces no log.
344
+ * Per-event: `0` when the guard does not fire (including benign audit events),
345
+ * `1` when it fires on drift. Add it to per-batch logging if your drain caller
346
+ * aggregates results. See ai-docs/specs/issue-242/plan.md §AUDIT-4.
345
347
  */
346
348
  export interface BridgeOutboxDrainResult {
347
349
  delivered: number;
@@ -376,13 +378,17 @@ export interface IBridgeOutboxDrainHook {
376
378
  * job_run` row pairs for every matched trigger via the supplied `tx`.
377
379
  *
378
380
  * Behaviour:
379
- * 0. **Audit-tier guard (defense-in-depth).** If
380
- * `event.metadata.tier === 'audit'`, returns
381
+ * 0. **Audit-tier guard (defense-in-depth).** Runs *after* the registry
382
+ * lookup (step 1). If `event.metadata.tier === 'audit'` AND a trigger
383
+ * is registered against the type (genuine drift — AUDIT-2 should have
384
+ * prevented it), returns
381
385
  * `{ delivered: 0, dedupSkips: 0, triggerCount: 0, auditBlocked: 1 }`
382
- * immediately and logs a per-`(event_type, process)` WARN. The
383
- * codegen-side validator (AUDIT-2) is the primary enforcement;
384
- * this guard catches out-of-band `bridge_trigger` inserts and
385
- * version skew during deploy. See
386
+ * and logs a per-`(event_type, process)` WARN naming the offending
387
+ * trigger id(s). If the audit event has *no* matched trigger (the
388
+ * common, benign case lifecycle events sharing the outbox), returns
389
+ * all zeros (`auditBlocked: 0`) silently. The codegen-side validator
390
+ * (AUDIT-2) is the primary enforcement; this guard catches out-of-band
391
+ * `bridge_trigger` inserts and version skew. See
386
392
  * ai-docs/specs/issue-242/plan.md §AUDIT-4.
387
393
  * 1. Looks up `bridgeRegistry[event.type]`. No matches → returns
388
394
  * `{ delivered: 0, dedupSkips: 0, triggerCount: 0, auditBlocked: 0 }`;
@@ -14,6 +14,12 @@ export const <%= classNames.outputSchema %> = z.object({
14
14
  <%_ clpOutputDtoFields.forEach(field => { _%>
15
15
  <%= field.camelName %>: <%- field.zodChainOutput %>,
16
16
  <%_ }) _%>
17
+ <%_ if (hasExternalIdTracking) { _%>
18
+ // external_id_tracking behavior — external_id is the public cross-entity join
19
+ // key (a referenced entity exposes it so consumers can join), so it rides the
20
+ // output DTO read-only. provider / provider_metadata stay internal.
21
+ externalId: z.string().nullable(),
22
+ <%_ } _%>
17
23
  <%_ if (hasTimestamps) { _%>
18
24
  createdAt: z.coerce.date(),
19
25
  updatedAt: z.coerce.date(),
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../runtime/subsystems/bridge/bridge-outbox-drain-hook.ts"],"sourcesContent":["/**\n * BridgeOutboxDrainHook — drains-time bridge fanout writer (BRIDGE-4,\n * ADR-023 Phase 2).\n *\n * Implements `IBridgeOutboxDrainHook`. Called by `DrizzleEventBus`'s\n * modified `processBatch` once per drained event, INSIDE the per-event\n * transaction. For every trigger registered against the event's type in\n * the codegen-emitted `bridgeRegistry`, writes:\n *\n * 1. `bridge_delivery` ledger row — `INSERT … ON CONFLICT (event_id,\n * trigger_id) DO NOTHING RETURNING id`. Empty result ⇒ Case B\n * facade-eager pre-write OR drain-replay collision; skip wrapper\n * insert for that trigger; sibling triggers still fire.\n * 2. `job_run` wrapper row — `type='@framework/bridge_delivery'`,\n * `pool='events_<direction>'`, `input={ deliveryId }`,\n * `trigger_source='event'`, `trigger_ref=event.id`. The wrapper is\n * what the framework `BridgeDeliveryHandler` (BRIDGE-5) eventually\n * claims via the worker that polls the corresponding reserved pool.\n *\n * Null `event.metadata.direction` is tolerated: the hook logs a one-line\n * warning per event and returns zeros without writing rows. The drain's\n * `processed_at` stamp + subscriber dispatch still fire normally.\n * Direction is null only for events published via the legacy\n * `IEventBus.publish(...)` path (`TypedEventBus.publish` always sets it);\n * such events are out of scope for bridge fanout.\n *\n * The wrapper insert generates its own `id` via Drizzle's `defaultRandom`\n * — we don't `RETURNING id` because nobody needs it at drain time;\n * `BridgeDeliveryHandler` later looks up the wrapper via the\n * `bridge_delivery.wrapper_run_id` link if needed. This keeps the drain\n * one-round-trip-per-trigger.\n */\nimport { Inject, Injectable, Logger, Optional } from '@nestjs/common';\nimport { randomUUID } from 'node:crypto';\nimport { eq } from 'drizzle-orm';\n\nimport type { DomainEvent, DrizzleTransaction } from '../events/event-bus.protocol';\nimport { bridgeDelivery } from './bridge-delivery.schema';\nimport { jobRuns } from '../jobs/job-orchestration.schema';\n\nimport { BRIDGE_REGISTRY } from './bridge.tokens';\nimport type {\n BridgeOutboxDrainResult,\n BridgeRegistry,\n BridgeTriggerEntry,\n IBridgeOutboxDrainHook,\n} from './bridge.protocol';\nimport { BRIDGE_DELIVERY_JOB_TYPE } from './bridge-delivery-handler';\nimport type { EventTypeName } from '../events/event-registry';\nimport { JOBS_LISTEN_NOTIFY } from '../jobs/jobs-domain.tokens';\nimport { JOBS_WAKE_CHANNEL, pgNotify } from '../jobs/pg-notify';\n\n/** Reserved pools the wrapper rows route into; ADR-022 / ADR-024. */\nconst POOL_BY_DIRECTION: Record<string, string> = {\n inbound: 'events_inbound',\n change: 'events_change',\n outbound: 'events_outbound',\n};\n\n@Injectable()\nexport class BridgeOutboxDrainHook implements IBridgeOutboxDrainHook {\n private readonly logger = new Logger(BridgeOutboxDrainHook.name);\n private warnedNullDirection = false;\n private readonly warnedAuditTypes = new Set<string>();\n\n constructor(\n @Optional()\n @Inject(BRIDGE_REGISTRY)\n private readonly registry: BridgeRegistry = {},\n // LISTEN-NOTIFY-1 — when true, the wrapper `job_run` insert below emits an\n // in-tx `pg_notify(codegen_jobs_wake, <wrapperPool>)` so the reserved-pool\n // worker wakes the instant the per-event drain tx commits — otherwise the\n // bridge hop alone would still cost a full poll interval. `@Optional()`\n // defaulting false so the hook keeps working when jobs isn't installed\n // (bridge can drive non-jobs consumers) or in vendored/test wiring.\n @Optional()\n @Inject(JOBS_LISTEN_NOTIFY)\n private readonly listenNotify: boolean = false,\n ) {}\n\n async processEvent(\n event: DomainEvent,\n tx: DrizzleTransaction,\n ): Promise<BridgeOutboxDrainResult> {\n // Audit-tier guard (defense-in-depth — AUDIT-4). Audit events are not\n // bridge-eligible: the codegen-side validator (AUDIT-2) blocks the\n // registry from listing them as triggers. Reaching this branch means\n // registry/runtime drift — an out-of-band `bridge_trigger` insert, or\n // version skew during deploy. Refuse fanout, surface drift via WARN.\n if (event.metadata?.['tier'] === 'audit') {\n this.warnAuditBlockedOnce(event);\n return {\n delivered: 0,\n dedupSkips: 0,\n triggerCount: 0,\n auditBlocked: 1,\n };\n }\n\n const triggers = this.lookupTriggers(event.type);\n if (triggers.length === 0) {\n return {\n delivered: 0,\n dedupSkips: 0,\n triggerCount: 0,\n auditBlocked: 0,\n };\n }\n\n const direction =\n (event.metadata?.['direction'] as string | undefined) ?? null;\n const tenantId =\n (event.metadata?.['tenantId'] as string | null | undefined) ?? null;\n const wrapperPool = direction ? POOL_BY_DIRECTION[direction] : undefined;\n\n if (!wrapperPool) {\n // Null direction (or an unrecognised one — defensive). Bridge\n // fanout requires a routed wrapper pool; without one we can't\n // spawn. Log once per process so misconfiguration surfaces.\n if (!this.warnedNullDirection) {\n this.warnedNullDirection = true;\n this.logger.warn(\n `Skipping bridge fanout for events with null/unknown direction. ` +\n `event.id=${event.id} event.type=${event.type} ` +\n `direction=${String(direction)}. The wrapper pool is derived ` +\n `from direction (events_<direction>); publishers must use ` +\n `TypedEventBus.publish() so direction is stamped on the ` +\n `outbox row.`,\n );\n }\n return {\n delivered: 0,\n dedupSkips: 0,\n triggerCount: triggers.length,\n auditBlocked: 0,\n };\n }\n\n let delivered = 0;\n let dedupSkips = 0;\n const client = tx as unknown as {\n insert: (table: unknown) => {\n values: (v: unknown) => {\n onConflictDoNothing: (opts: unknown) => {\n returning: (cols: unknown) => Promise<{ id: string }[]>;\n };\n } & {\n // wrapper insert path — no ON CONFLICT\n // (typed loosely via the same helper return shape)\n };\n };\n };\n\n for (const trigger of triggers) {\n const deliveryId = randomUUID();\n const wrapperRunId = randomUUID();\n\n // FK ORDER (BRIDGE / 0.15.2): `bridge_delivery.wrapper_run_id` REFERENCES\n // `job_run(id)` is a plain (non-deferrable) FK, so the referenced\n // wrapper `job_run` MUST exist before the delivery row that points at it\n // is inserted — otherwise Postgres rejects the delivery insert\n // immediately. (The codegen unit tests mock `tx`, so they never\n // exercised this ordering against a real FK; package-mode bridge\n // deliveries are the first to do so.) We therefore insert the wrapper\n // run FIRST, then the delivery. Idempotency is unchanged: the delivery\n // keeps its `ON CONFLICT (event_id, trigger_id) DO NOTHING RETURNING`,\n // and when the delivery conflicts (outbox replay or facade-eager Case B)\n // we DELETE the just-inserted orphan wrapper run in the same tx, so a\n // skipped delivery leaves no stray `job_run` for a worker to claim.\n\n // 1. Wrapper job_run insert. We carry the deliveryId into the wrapper\n // input so BridgeDeliveryHandler.run(ctx) can locate the row via\n // repo.findDeliveryById(ctx.input.deliveryId).\n await (tx as unknown as { insert: typeof client.insert })\n .insert(jobRuns)\n .values({\n id: wrapperRunId,\n jobType: BRIDGE_DELIVERY_JOB_TYPE,\n jobVersion: 1,\n rootRunId: wrapperRunId,\n pool: wrapperPool,\n status: 'pending',\n input: { deliveryId },\n triggerSource: 'event',\n triggerRef: event.id,\n tenantId,\n });\n\n // 2. bridge_delivery insert with ON CONFLICT DO NOTHING + RETURNING.\n const inserted = await (tx as unknown as {\n insert: typeof client.insert;\n })\n .insert(bridgeDelivery)\n .values({\n id: deliveryId,\n eventId: event.id,\n triggerId: trigger.triggerId,\n wrapperRunId,\n status: 'pending',\n tenantId,\n })\n .onConflictDoNothing({\n target: [bridgeDelivery.eventId, bridgeDelivery.triggerId],\n })\n .returning({ id: bridgeDelivery.id });\n\n if (inserted.length === 0) {\n // Case B (facade pre-wrote `delivered`) or drain replay — the delivery\n // already exists, so this trigger is a no-op. Remove the orphan wrapper\n // run we speculatively inserted above so no worker claims it. Sibling\n // triggers still fire.\n await (tx as unknown as {\n delete: (table: unknown) => {\n where: (cond: unknown) => Promise<unknown>;\n };\n })\n .delete(jobRuns)\n .where(eq(jobRuns.id, wrapperRunId));\n dedupSkips++;\n continue;\n }\n\n // LISTEN-NOTIFY-1 — the wrapper run is real and claimable; wake its\n // reserved-pool worker on commit (D7). Same `tx` as the inserts above, so\n // delivery is gated on the per-event drain tx committing. Best-effort: a\n // notify failure is non-fatal (the reserved-pool worker still polls).\n if (this.listenNotify) {\n try {\n await pgNotify(tx, JOBS_WAKE_CHANNEL, wrapperPool);\n } catch (err) {\n this.logger.warn(\n `pg_notify(${JOBS_WAKE_CHANNEL}, ${wrapperPool}) failed for ` +\n `wrapper run ${wrapperRunId}: ${(err as Error).message} ` +\n `(non-fatal — the reserved-pool worker still polls).`,\n );\n }\n }\n\n delivered++;\n }\n\n return {\n delivered,\n dedupSkips,\n triggerCount: triggers.length,\n auditBlocked: 0,\n };\n }\n\n private warnAuditBlockedOnce(event: DomainEvent): void {\n if (this.warnedAuditTypes.has(event.type)) return;\n this.warnedAuditTypes.add(event.type);\n this.logger.warn(\n `Bridge guard blocked audit-tier event '${event.type}' (event.id=${event.id}). ` +\n `Registry says this event is not bridge-eligible; a bridge_trigger row exists ` +\n `out-of-band. Investigate registry/runtime drift.`,\n );\n }\n\n private lookupTriggers(\n eventType: string,\n ): BridgeTriggerEntry[] {\n const candidates = this.registry[eventType as EventTypeName];\n return (candidates ?? []) as BridgeTriggerEntry[];\n }\n}\n"],"mappings":"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","names":[]}