@pattern-stack/codegen 0.4.4 → 0.4.5

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 (34) hide show
  1. package/dist/runtime/subsystems/index.d.ts +7 -0
  2. package/dist/runtime/subsystems/index.js +905 -208
  3. package/dist/runtime/subsystems/index.js.map +1 -1
  4. package/dist/runtime/subsystems/observability/index.d.ts +10 -0
  5. package/dist/runtime/subsystems/observability/index.js +895 -0
  6. package/dist/runtime/subsystems/observability/index.js.map +1 -0
  7. package/dist/runtime/subsystems/observability/observability.drizzle-backend.d.ts +15 -0
  8. package/dist/runtime/subsystems/observability/observability.drizzle-backend.js +465 -0
  9. package/dist/runtime/subsystems/observability/observability.drizzle-backend.js.map +1 -0
  10. package/dist/runtime/subsystems/observability/observability.memory-backend.d.ts +28 -0
  11. package/dist/runtime/subsystems/observability/observability.memory-backend.js +75 -0
  12. package/dist/runtime/subsystems/observability/observability.memory-backend.js.map +1 -0
  13. package/dist/runtime/subsystems/observability/observability.module.d.ts +56 -0
  14. package/dist/runtime/subsystems/observability/observability.module.js +887 -0
  15. package/dist/runtime/subsystems/observability/observability.module.js.map +1 -0
  16. package/dist/runtime/subsystems/observability/observability.protocol.d.ts +155 -0
  17. package/dist/runtime/subsystems/observability/observability.protocol.js +1 -0
  18. package/dist/runtime/subsystems/observability/observability.protocol.js.map +1 -0
  19. package/dist/runtime/subsystems/observability/observability.tokens.d.ts +19 -0
  20. package/dist/runtime/subsystems/observability/observability.tokens.js +8 -0
  21. package/dist/runtime/subsystems/observability/observability.tokens.js.map +1 -0
  22. package/dist/runtime/subsystems/observability/reporters/bridge-metrics.reporter.d.ts +79 -0
  23. package/dist/runtime/subsystems/observability/reporters/bridge-metrics.reporter.js +425 -0
  24. package/dist/runtime/subsystems/observability/reporters/bridge-metrics.reporter.js.map +1 -0
  25. package/dist/runtime/subsystems/sync/sync-audit.schema.d.ts +4 -4
  26. package/package.json +6 -1
  27. package/runtime/subsystems/index.ts +23 -0
  28. package/runtime/subsystems/observability/index.ts +35 -0
  29. package/runtime/subsystems/observability/observability.drizzle-backend.ts +223 -0
  30. package/runtime/subsystems/observability/observability.memory-backend.ts +111 -0
  31. package/runtime/subsystems/observability/observability.module.ts +115 -0
  32. package/runtime/subsystems/observability/observability.protocol.ts +167 -0
  33. package/runtime/subsystems/observability/observability.tokens.ts +18 -0
  34. package/runtime/subsystems/observability/reporters/bridge-metrics.reporter.ts +222 -0
@@ -1,5 +1,29 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
6
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
7
+ }) : x)(function(x) {
8
+ if (typeof require !== "undefined") return require.apply(this, arguments);
9
+ throw Error('Dynamic require of "' + x + '" is not supported');
10
+ });
11
+ var __esm = (fn, res) => function __init() {
12
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
13
+ };
14
+ var __export = (target, all) => {
15
+ for (var name in all)
16
+ __defProp(target, name, { get: all[name], enumerable: true });
17
+ };
18
+ var __copyProps = (to, from, except, desc5) => {
19
+ if (from && typeof from === "object" || typeof from === "function") {
20
+ for (let key of __getOwnPropNames(from))
21
+ if (!__hasOwnProp.call(to, key) && key !== except)
22
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc5 = __getOwnPropDesc(from, key)) || desc5.enumerable });
23
+ }
24
+ return to;
25
+ };
26
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
3
27
  var __decorateClass = (decorators, target, key, kind) => {
4
28
  var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
5
29
  for (var i = decorators.length - 1, decorator; i >= 0; i--)
@@ -8,7 +32,452 @@ var __decorateClass = (decorators, target, key, kind) => {
8
32
  if (kind && result) __defProp(target, key, result);
9
33
  return result;
10
34
  };
11
- var __decorateParam = (index3, decorator) => (target, key) => decorator(target, key, index3);
35
+ var __decorateParam = (index5, decorator) => (target, key) => decorator(target, key, index5);
36
+
37
+ // runtime/constants/tokens.ts
38
+ var DRIZZLE;
39
+ var init_tokens = __esm({
40
+ "runtime/constants/tokens.ts"() {
41
+ "use strict";
42
+ DRIZZLE = "DRIZZLE";
43
+ }
44
+ });
45
+
46
+ // runtime/subsystems/events/domain-events.schema.ts
47
+ import {
48
+ index,
49
+ jsonb,
50
+ pgTable,
51
+ text,
52
+ timestamp,
53
+ uuid
54
+ } from "drizzle-orm/pg-core";
55
+ var domainEvents;
56
+ var init_domain_events_schema = __esm({
57
+ "runtime/subsystems/events/domain-events.schema.ts"() {
58
+ "use strict";
59
+ domainEvents = pgTable(
60
+ "domain_events",
61
+ {
62
+ id: uuid("id").primaryKey(),
63
+ type: text("type").notNull(),
64
+ aggregateId: text("aggregate_id").notNull(),
65
+ aggregateType: text("aggregate_type").notNull(),
66
+ payload: jsonb("payload").notNull().$type(),
67
+ occurredAt: timestamp("occurred_at", { withTimezone: true }).notNull(),
68
+ processedAt: timestamp("processed_at", { withTimezone: true }),
69
+ /** Lifecycle status: pending | processed | failed */
70
+ status: text("status").notNull().default("pending"),
71
+ /** Error message from the last failed dispatch attempt. */
72
+ error: text("error"),
73
+ metadata: jsonb("metadata").$type(),
74
+ /** Routing pool (e.g. `events_inbound`, `events_change`, `events_outbound`). Populated by DrizzleEventBus.publish() in EVT-4. */
75
+ pool: text("pool"),
76
+ /** Routing direction: `inbound` | `change` | `outbound`. Populated by DrizzleEventBus.publish() in EVT-4. */
77
+ direction: text("direction"),
78
+ // conditional: emitted only when events.multi_tenant: true
79
+ tenantId: text("tenant_id")
80
+ },
81
+ (t) => ({
82
+ /** Polling drain filter (existing — promoted from comment to declaration in EVT-1). */
83
+ idxDomainEventsStatusOccurredAt: index("idx_domain_events_status_occurred_at").on(
84
+ t.status,
85
+ t.occurredAt
86
+ ),
87
+ /** Event replay per aggregate (existing — promoted from comment to declaration in EVT-1). */
88
+ idxDomainEventsAggregate: index("idx_domain_events_aggregate").on(
89
+ t.aggregateId,
90
+ t.aggregateType
91
+ ),
92
+ /** Per-pool drain filter (EVT-1). Enables DrizzleEventBus to drain a single pool without scanning all events. */
93
+ idxDomainEventsPoolStatusOccurredAt: index(
94
+ "idx_domain_events_pool_status_occurred_at"
95
+ ).on(t.pool, t.status, t.occurredAt)
96
+ })
97
+ );
98
+ }
99
+ });
100
+
101
+ // runtime/subsystems/jobs/job-orchestration.schema.ts
102
+ import {
103
+ pgEnum,
104
+ pgTable as pgTable2,
105
+ uuid as uuid2,
106
+ text as text2,
107
+ jsonb as jsonb2,
108
+ integer,
109
+ timestamp as timestamp2,
110
+ index as index2,
111
+ uniqueIndex
112
+ } from "drizzle-orm/pg-core";
113
+ import { sql } from "drizzle-orm";
114
+ var jobRunStatusEnum, jobStepKindEnum, jobStepStatusEnum, collisionModeEnum, replayFromEnum, parentClosePolicyEnum, waitKindEnum, triggerSourceEnum, jobs, jobRuns, jobSteps;
115
+ var init_job_orchestration_schema = __esm({
116
+ "runtime/subsystems/jobs/job-orchestration.schema.ts"() {
117
+ "use strict";
118
+ jobRunStatusEnum = pgEnum("job_run_status", [
119
+ "pending",
120
+ "running",
121
+ "waiting",
122
+ "completed",
123
+ "failed",
124
+ "timed_out",
125
+ "canceled"
126
+ ]);
127
+ jobStepKindEnum = pgEnum("job_step_kind", ["task"]);
128
+ jobStepStatusEnum = pgEnum("job_step_status", [
129
+ "pending",
130
+ "running",
131
+ "completed",
132
+ "failed",
133
+ "skipped"
134
+ ]);
135
+ collisionModeEnum = pgEnum("job_collision_mode", [
136
+ "queue",
137
+ "reject",
138
+ "replace"
139
+ ]);
140
+ replayFromEnum = pgEnum("job_replay_from", [
141
+ "scratch",
142
+ "last_step",
143
+ "last_checkpoint"
144
+ ]);
145
+ parentClosePolicyEnum = pgEnum("job_parent_close_policy", [
146
+ "terminate",
147
+ "cancel",
148
+ "abandon"
149
+ ]);
150
+ waitKindEnum = pgEnum("job_wait_kind", ["signal"]);
151
+ triggerSourceEnum = pgEnum("job_trigger_source", [
152
+ "manual",
153
+ "schedule",
154
+ "event",
155
+ "parent"
156
+ ]);
157
+ jobs = pgTable2("job", {
158
+ type: text2("type").primaryKey(),
159
+ version: integer("version").notNull().default(1),
160
+ pool: text2("pool").notNull(),
161
+ scopeEntityType: text2("scope_entity_type"),
162
+ retryPolicy: jsonb2("retry_policy").notNull().$type(),
163
+ timeoutMs: integer("timeout_ms"),
164
+ concurrencyKeyTemplate: text2("concurrency_key_template"),
165
+ collisionMode: collisionModeEnum("collision_mode").notNull().default("queue"),
166
+ dedupeKeyTemplate: text2("dedupe_key_template"),
167
+ dedupeWindowMs: integer("dedupe_window_ms"),
168
+ priorityDefault: integer("priority_default").notNull().default(0),
169
+ replayFrom: replayFromEnum("replay_from").notNull().default("last_checkpoint"),
170
+ createdAt: timestamp2("created_at", { withTimezone: true }).notNull().defaultNow(),
171
+ updatedAt: timestamp2("updated_at", { withTimezone: true }).notNull().defaultNow()
172
+ });
173
+ jobRuns = pgTable2(
174
+ "job_run",
175
+ {
176
+ id: uuid2("id").primaryKey().defaultRandom(),
177
+ jobType: text2("job_type").notNull().references(() => jobs.type),
178
+ jobVersion: integer("job_version").notNull(),
179
+ parentRunId: uuid2("parent_run_id").references(() => jobRuns.id),
180
+ /**
181
+ * Service generates `id` client-side via randomUUID() and sets
182
+ * root_run_id = id for root runs (single INSERT, no self-FK race).
183
+ */
184
+ rootRunId: uuid2("root_run_id").notNull(),
185
+ parentClosePolicy: parentClosePolicyEnum("parent_close_policy").notNull().default("terminate"),
186
+ scopeEntityType: text2("scope_entity_type"),
187
+ scopeEntityId: text2("scope_entity_id"),
188
+ tenantId: text2("tenant_id"),
189
+ tags: jsonb2("tags").notNull().default({}).$type(),
190
+ pool: text2("pool").notNull(),
191
+ priority: integer("priority").notNull().default(0),
192
+ concurrencyKey: text2("concurrency_key"),
193
+ dedupeKey: text2("dedupe_key"),
194
+ status: jobRunStatusEnum("status").notNull().default("pending"),
195
+ input: jsonb2("input").notNull().$type(),
196
+ output: jsonb2("output").$type(),
197
+ error: jsonb2("error").$type(),
198
+ triggerSource: triggerSourceEnum("trigger_source").notNull(),
199
+ triggerRef: text2("trigger_ref"),
200
+ runAt: timestamp2("run_at", { withTimezone: true }).notNull().defaultNow(),
201
+ startedAt: timestamp2("started_at", { withTimezone: true }),
202
+ finishedAt: timestamp2("finished_at", { withTimezone: true }),
203
+ claimedAt: timestamp2("claimed_at", { withTimezone: true }),
204
+ attempts: integer("attempts").notNull().default(0),
205
+ // Phase 3 placeholder — see ADR-025
206
+ waitKind: waitKindEnum("wait_kind"),
207
+ // Phase 3 placeholder — see ADR-025
208
+ resumeToken: text2("resume_token"),
209
+ // Phase 3 placeholder — see ADR-025
210
+ waitDeadline: timestamp2("wait_deadline", { withTimezone: true }),
211
+ createdAt: timestamp2("created_at", { withTimezone: true }).notNull().defaultNow(),
212
+ updatedAt: timestamp2("updated_at", { withTimezone: true }).notNull().defaultNow()
213
+ },
214
+ (t) => ({
215
+ /** Claim query: ORDER BY priority DESC, run_at ASC. */
216
+ idxJobRunClaim: index2("idx_job_run_claim").on(t.status, t.pool, t.runAt),
217
+ /** Tree traversal / cascade cancel. */
218
+ idxJobRunRoot: index2("idx_job_run_root").on(t.rootRunId),
219
+ /** listForScope query. */
220
+ idxJobRunScope: index2("idx_job_run_scope").on(t.scopeEntityType, t.scopeEntityId),
221
+ /** Idempotency collapse — partial index. */
222
+ idxJobRunDedupe: index2("idx_job_run_dedupe").on(t.jobType, t.dedupeKey).where(sql`${t.dedupeKey} IS NOT NULL`),
223
+ /** Collision check — partial index. */
224
+ idxJobRunConcurrency: index2("idx_job_run_concurrency").on(t.concurrencyKey).where(
225
+ sql`${t.concurrencyKey} IS NOT NULL AND ${t.status} IN ('pending','running')`
226
+ )
227
+ })
228
+ );
229
+ jobSteps = pgTable2(
230
+ "job_step",
231
+ {
232
+ id: uuid2("id").primaryKey().defaultRandom(),
233
+ jobRunId: uuid2("job_run_id").notNull().references(() => jobRuns.id),
234
+ stepId: text2("step_id").notNull(),
235
+ kind: jobStepKindEnum("kind").notNull().default("task"),
236
+ /**
237
+ * Monotonic within run. integer (max ~2B per run) is sufficient —
238
+ * downgraded from ADR-022's bigint; revisit only if a single run
239
+ * ever exceeds 2 billion steps.
240
+ */
241
+ seq: integer("seq").notNull(),
242
+ status: jobStepStatusEnum("status").notNull().default("pending"),
243
+ input: jsonb2("input").$type(),
244
+ /** Memoised on success for replay. */
245
+ output: jsonb2("output").$type(),
246
+ error: jsonb2("error").$type(),
247
+ attempts: integer("attempts").notNull().default(0),
248
+ startedAt: timestamp2("started_at", { withTimezone: true }),
249
+ finishedAt: timestamp2("finished_at", { withTimezone: true })
250
+ },
251
+ (t) => ({
252
+ /** No duplicate step IDs per run. */
253
+ idxJobStepRunStep: uniqueIndex("idx_job_step_run_step").on(t.jobRunId, t.stepId),
254
+ /** Ordered timeline reads. */
255
+ idxJobStepTimeline: index2("idx_job_step_timeline").on(t.jobRunId, t.seq)
256
+ })
257
+ );
258
+ }
259
+ });
260
+
261
+ // runtime/subsystems/bridge/bridge-delivery.schema.ts
262
+ import {
263
+ index as index4,
264
+ jsonb as jsonb5,
265
+ pgEnum as pgEnum3,
266
+ pgTable as pgTable5,
267
+ text as text5,
268
+ timestamp as timestamp5,
269
+ unique,
270
+ uuid as uuid4
271
+ } from "drizzle-orm/pg-core";
272
+ import { sql as sql6 } from "drizzle-orm";
273
+ var bridgeDeliveryStatusEnum, bridgeDelivery;
274
+ var init_bridge_delivery_schema = __esm({
275
+ "runtime/subsystems/bridge/bridge-delivery.schema.ts"() {
276
+ "use strict";
277
+ init_domain_events_schema();
278
+ init_job_orchestration_schema();
279
+ bridgeDeliveryStatusEnum = pgEnum3("bridge_delivery_status", [
280
+ "pending",
281
+ "delivered",
282
+ "skipped",
283
+ "failed"
284
+ ]);
285
+ bridgeDelivery = pgTable5(
286
+ "bridge_delivery",
287
+ {
288
+ id: uuid4("id").primaryKey().defaultRandom(),
289
+ /** FK to the source event in the outbox. */
290
+ eventId: uuid4("event_id").notNull().references(() => domainEvents.id),
291
+ /**
292
+ * Stable codegen-emitted identifier for the (job, trigger) pair, of the
293
+ * form `<job_type>#<triggerIndex>` (BRIDGE-6). Forms the second half of
294
+ * the UNIQUE idempotency key.
295
+ */
296
+ triggerId: text5("trigger_id").notNull(),
297
+ /**
298
+ * Wrapper `job_run.id` (the framework `@framework/bridge_delivery` run
299
+ * that drove this delivery). Nullable: the facade-eager path
300
+ * (`publishAndStart` Case B) pre-writes a delivered row with no wrapper.
301
+ */
302
+ wrapperRunId: uuid4("wrapper_run_id").references(() => jobRuns.id),
303
+ /**
304
+ * Spawned user `job_run.id`. Null until status is `delivered`; remains
305
+ * null for `skipped` and `failed` deliveries.
306
+ */
307
+ userRunId: uuid4("user_run_id").references(() => jobRuns.id),
308
+ status: bridgeDeliveryStatusEnum("status").notNull().default("pending"),
309
+ /** Populated when status=`skipped` (e.g. `'when_returned_false'`, `'trigger_unregistered'`). */
310
+ skipReason: text5("skip_reason"),
311
+ /** Populated when status=`failed`. Mirrors `job_run.error` shape. */
312
+ error: jsonb5("error").$type(),
313
+ /**
314
+ * Emitted unconditionally and nullable (JOB-8 / SYNC-6 precedent).
315
+ * Enforcement gated on `BRIDGE_MULTI_TENANT` at the service layer
316
+ * (BRIDGE-8); no DB constraint.
317
+ */
318
+ tenantId: text5("tenant_id"),
319
+ attemptedAt: timestamp5("attempted_at", { withTimezone: true }).notNull().defaultNow(),
320
+ deliveredAt: timestamp5("delivered_at", { withTimezone: true })
321
+ },
322
+ (t) => ({
323
+ /**
324
+ * Idempotency ledger. Outbox replays and facade-vs-drain collisions both
325
+ * dedup through this constraint.
326
+ */
327
+ uqBridgeDeliveryEventTrigger: unique("uq_bridge_delivery_event_trigger").on(
328
+ t.eventId,
329
+ t.triggerId
330
+ ),
331
+ /** Lookup all deliveries for an event (fanout report, debugging). */
332
+ idxBridgeDeliveryEvent: index4("idx_bridge_delivery_event").on(t.eventId),
333
+ /**
334
+ * Ops dashboard filter — only the actionable states. Partial index keeps
335
+ * it small at scale (the bulk of rows will be `delivered`).
336
+ */
337
+ idxBridgeDeliveryStatus: index4("idx_bridge_delivery_status").on(t.status).where(sql6`${t.status} IN ('pending','failed')`),
338
+ /**
339
+ * Reverse lookup from a spawned user run back to its delivery row.
340
+ * Partial — most rows in the bridge ledger but only successful
341
+ * deliveries have a `user_run_id`.
342
+ */
343
+ idxBridgeDeliveryUserRun: index4("idx_bridge_delivery_user_run").on(t.userRunId).where(sql6`${t.userRunId} IS NOT NULL`)
344
+ })
345
+ );
346
+ }
347
+ });
348
+
349
+ // runtime/subsystems/observability/reporters/bridge-metrics.reporter.ts
350
+ var bridge_metrics_reporter_exports = {};
351
+ __export(bridge_metrics_reporter_exports, {
352
+ BridgeMetricsReporter: () => BridgeMetricsReporter
353
+ });
354
+ import {
355
+ Inject as Inject15,
356
+ Injectable as Injectable17,
357
+ Logger as Logger8
358
+ } from "@nestjs/common";
359
+ import { and as and6, eq as eq8, gt as gt3, sql as sql7 } from "drizzle-orm";
360
+ var INTERVAL_NAME, DEFAULT_INTERVAL_MS, MIN_INTERVAL_MS, BridgeMetricsReporter;
361
+ var init_bridge_metrics_reporter = __esm({
362
+ "runtime/subsystems/observability/reporters/bridge-metrics.reporter.ts"() {
363
+ "use strict";
364
+ init_tokens();
365
+ init_bridge_delivery_schema();
366
+ init_domain_events_schema();
367
+ INTERVAL_NAME = "bridge-metrics-tick";
368
+ DEFAULT_INTERVAL_MS = 6e4;
369
+ MIN_INTERVAL_MS = 1e3;
370
+ BridgeMetricsReporter = class {
371
+ constructor(db, scheduler) {
372
+ this.db = db;
373
+ this.scheduler = scheduler;
374
+ this.intervalMs = this.resolveIntervalMs();
375
+ this.lastTickAt = /* @__PURE__ */ new Date();
376
+ }
377
+ db;
378
+ scheduler;
379
+ logger = new Logger8(BridgeMetricsReporter.name);
380
+ intervalMs;
381
+ lastTickAt;
382
+ onModuleInit() {
383
+ this.logger.log(
384
+ `BridgeMetricsReporter starting (intervalMs=${this.intervalMs}).`
385
+ );
386
+ const timer = setInterval(() => {
387
+ void this.tick();
388
+ }, this.intervalMs);
389
+ timer.unref?.();
390
+ this.scheduler.addInterval(INTERVAL_NAME, timer);
391
+ }
392
+ onModuleDestroy() {
393
+ if (this.scheduler.getIntervals().includes(INTERVAL_NAME)) {
394
+ this.scheduler.deleteInterval(INTERVAL_NAME);
395
+ }
396
+ }
397
+ /**
398
+ * Run one sampling tick. Public so tests can drive it deterministically
399
+ * without waiting on the timer.
400
+ */
401
+ async tick() {
402
+ const windowStart = this.lastTickAt;
403
+ const windowEnd = /* @__PURE__ */ new Date();
404
+ this.lastTickAt = windowEnd;
405
+ let rows = [];
406
+ try {
407
+ rows = await this.sample(windowStart, windowEnd);
408
+ } catch (err) {
409
+ this.logger.error(
410
+ `bridge metrics sample failed: ${err.message}`
411
+ );
412
+ return { windowStart, windowEnd, rows: [] };
413
+ }
414
+ this.emit({ windowStart, windowEnd, rows });
415
+ return { windowStart, windowEnd, rows };
416
+ }
417
+ async sample(windowStart, windowEnd) {
418
+ const lastTransition = sql7`COALESCE(${bridgeDelivery.deliveredAt}, ${bridgeDelivery.attemptedAt})`;
419
+ const result = await this.db.select({
420
+ status: bridgeDelivery.status,
421
+ eventType: domainEvents.type,
422
+ skipReason: bridgeDelivery.skipReason,
423
+ count: sql7`COUNT(*)::int`
424
+ }).from(bridgeDelivery).innerJoin(domainEvents, eq8(bridgeDelivery.eventId, domainEvents.id)).where(
425
+ and6(
426
+ gt3(lastTransition, windowStart),
427
+ sql7`${lastTransition} <= ${windowEnd}`
428
+ )
429
+ ).groupBy(
430
+ bridgeDelivery.status,
431
+ domainEvents.type,
432
+ bridgeDelivery.skipReason
433
+ );
434
+ return result.map((r) => ({
435
+ status: r.status,
436
+ eventType: r.eventType,
437
+ skipReason: r.skipReason,
438
+ count: r.count
439
+ }));
440
+ }
441
+ emit(tick) {
442
+ if (tick.rows.length === 0) {
443
+ this.logger.log(
444
+ `bridge_metrics tick=empty window=[${tick.windowStart.toISOString()}..${tick.windowEnd.toISOString()}]`
445
+ );
446
+ return;
447
+ }
448
+ const totals = tick.rows.reduce(
449
+ (acc, r) => {
450
+ acc[r.status] = (acc[r.status] ?? 0) + r.count;
451
+ return acc;
452
+ },
453
+ {}
454
+ );
455
+ const detail = tick.rows.map(
456
+ (r) => `${r.eventType}|${r.status}${r.skipReason ? `:${r.skipReason}` : ""}=${r.count}`
457
+ ).join(" ");
458
+ this.logger.log(
459
+ `bridge_metrics tick window=[${tick.windowStart.toISOString()}..${tick.windowEnd.toISOString()}] totals=${JSON.stringify(totals)} detail=[${detail}]`
460
+ );
461
+ }
462
+ resolveIntervalMs() {
463
+ const raw = process.env["BRIDGE_METRICS_INTERVAL_MS"];
464
+ if (!raw) return DEFAULT_INTERVAL_MS;
465
+ const parsed = Number.parseInt(raw, 10);
466
+ if (!Number.isFinite(parsed) || parsed < MIN_INTERVAL_MS) {
467
+ new Logger8(BridgeMetricsReporter.name).warn(
468
+ `Ignoring BRIDGE_METRICS_INTERVAL_MS='${raw}' (invalid or < ${MIN_INTERVAL_MS}ms); using default ${DEFAULT_INTERVAL_MS}ms.`
469
+ );
470
+ return DEFAULT_INTERVAL_MS;
471
+ }
472
+ return parsed;
473
+ }
474
+ };
475
+ BridgeMetricsReporter = __decorateClass([
476
+ Injectable17(),
477
+ __decorateParam(0, Inject15(DRIZZLE))
478
+ ], BridgeMetricsReporter);
479
+ }
480
+ });
12
481
 
13
482
  // runtime/subsystems/events/events.tokens.ts
14
483
  var EVENT_BUS = "EVENT_BUS";
@@ -208,63 +677,14 @@ TypedEventBus = __decorateClass([
208
677
 
209
678
  // runtime/subsystems/events/events.module.ts
210
679
  import { Module } from "@nestjs/common";
211
-
212
- // runtime/constants/tokens.ts
213
- var DRIZZLE = "DRIZZLE";
680
+ init_tokens();
214
681
 
215
682
  // runtime/subsystems/events/event-bus.drizzle-backend.ts
683
+ init_domain_events_schema();
684
+ init_tokens();
216
685
  import { Injectable as Injectable2, Inject as Inject2, Logger, Optional } from "@nestjs/common";
217
686
  import { eq, and, inArray, asc } from "drizzle-orm";
218
687
 
219
- // runtime/subsystems/events/domain-events.schema.ts
220
- import {
221
- index,
222
- jsonb,
223
- pgTable,
224
- text,
225
- timestamp,
226
- uuid
227
- } from "drizzle-orm/pg-core";
228
- var domainEvents = pgTable(
229
- "domain_events",
230
- {
231
- id: uuid("id").primaryKey(),
232
- type: text("type").notNull(),
233
- aggregateId: text("aggregate_id").notNull(),
234
- aggregateType: text("aggregate_type").notNull(),
235
- payload: jsonb("payload").notNull().$type(),
236
- occurredAt: timestamp("occurred_at", { withTimezone: true }).notNull(),
237
- processedAt: timestamp("processed_at", { withTimezone: true }),
238
- /** Lifecycle status: pending | processed | failed */
239
- status: text("status").notNull().default("pending"),
240
- /** Error message from the last failed dispatch attempt. */
241
- error: text("error"),
242
- metadata: jsonb("metadata").$type(),
243
- /** Routing pool (e.g. `events_inbound`, `events_change`, `events_outbound`). Populated by DrizzleEventBus.publish() in EVT-4. */
244
- pool: text("pool"),
245
- /** Routing direction: `inbound` | `change` | `outbound`. Populated by DrizzleEventBus.publish() in EVT-4. */
246
- direction: text("direction"),
247
- // conditional: emitted only when events.multi_tenant: true
248
- tenantId: text("tenant_id")
249
- },
250
- (t) => ({
251
- /** Polling drain filter (existing — promoted from comment to declaration in EVT-1). */
252
- idxDomainEventsStatusOccurredAt: index("idx_domain_events_status_occurred_at").on(
253
- t.status,
254
- t.occurredAt
255
- ),
256
- /** Event replay per aggregate (existing — promoted from comment to declaration in EVT-1). */
257
- idxDomainEventsAggregate: index("idx_domain_events_aggregate").on(
258
- t.aggregateId,
259
- t.aggregateType
260
- ),
261
- /** Per-pool drain filter (EVT-1). Enables DrizzleEventBus to drain a single pool without scanning all events. */
262
- idxDomainEventsPoolStatusOccurredAt: index(
263
- "idx_domain_events_pool_status_occurred_at"
264
- ).on(t.pool, t.status, t.occurredAt)
265
- })
266
- );
267
-
268
688
  // runtime/subsystems/bridge/bridge.tokens.ts
269
689
  var BRIDGE_OUTBOX_DRAIN_HOOK = "BRIDGE_OUTBOX_DRAIN_HOOK";
270
690
 
@@ -893,159 +1313,12 @@ EventsModule = __decorateClass([
893
1313
  Module({})
894
1314
  ], EventsModule);
895
1315
 
896
- // runtime/subsystems/jobs/job-orchestration.schema.ts
897
- import {
898
- pgEnum,
899
- pgTable as pgTable2,
900
- uuid as uuid2,
901
- text as text2,
902
- jsonb as jsonb2,
903
- integer,
904
- timestamp as timestamp2,
905
- index as index2,
906
- uniqueIndex
907
- } from "drizzle-orm/pg-core";
908
- import { sql } from "drizzle-orm";
909
- var jobRunStatusEnum = pgEnum("job_run_status", [
910
- "pending",
911
- "running",
912
- "waiting",
913
- "completed",
914
- "failed",
915
- "timed_out",
916
- "canceled"
917
- ]);
918
- var jobStepKindEnum = pgEnum("job_step_kind", ["task"]);
919
- var jobStepStatusEnum = pgEnum("job_step_status", [
920
- "pending",
921
- "running",
922
- "completed",
923
- "failed",
924
- "skipped"
925
- ]);
926
- var collisionModeEnum = pgEnum("job_collision_mode", [
927
- "queue",
928
- "reject",
929
- "replace"
930
- ]);
931
- var replayFromEnum = pgEnum("job_replay_from", [
932
- "scratch",
933
- "last_step",
934
- "last_checkpoint"
935
- ]);
936
- var parentClosePolicyEnum = pgEnum("job_parent_close_policy", [
937
- "terminate",
938
- "cancel",
939
- "abandon"
940
- ]);
941
- var waitKindEnum = pgEnum("job_wait_kind", ["signal"]);
942
- var triggerSourceEnum = pgEnum("job_trigger_source", [
943
- "manual",
944
- "schedule",
945
- "event",
946
- "parent"
947
- ]);
948
- var jobs = pgTable2("job", {
949
- type: text2("type").primaryKey(),
950
- version: integer("version").notNull().default(1),
951
- pool: text2("pool").notNull(),
952
- scopeEntityType: text2("scope_entity_type"),
953
- retryPolicy: jsonb2("retry_policy").notNull().$type(),
954
- timeoutMs: integer("timeout_ms"),
955
- concurrencyKeyTemplate: text2("concurrency_key_template"),
956
- collisionMode: collisionModeEnum("collision_mode").notNull().default("queue"),
957
- dedupeKeyTemplate: text2("dedupe_key_template"),
958
- dedupeWindowMs: integer("dedupe_window_ms"),
959
- priorityDefault: integer("priority_default").notNull().default(0),
960
- replayFrom: replayFromEnum("replay_from").notNull().default("last_checkpoint"),
961
- createdAt: timestamp2("created_at", { withTimezone: true }).notNull().defaultNow(),
962
- updatedAt: timestamp2("updated_at", { withTimezone: true }).notNull().defaultNow()
963
- });
964
- var jobRuns = pgTable2(
965
- "job_run",
966
- {
967
- id: uuid2("id").primaryKey().defaultRandom(),
968
- jobType: text2("job_type").notNull().references(() => jobs.type),
969
- jobVersion: integer("job_version").notNull(),
970
- parentRunId: uuid2("parent_run_id").references(() => jobRuns.id),
971
- /**
972
- * Service generates `id` client-side via randomUUID() and sets
973
- * root_run_id = id for root runs (single INSERT, no self-FK race).
974
- */
975
- rootRunId: uuid2("root_run_id").notNull(),
976
- parentClosePolicy: parentClosePolicyEnum("parent_close_policy").notNull().default("terminate"),
977
- scopeEntityType: text2("scope_entity_type"),
978
- scopeEntityId: text2("scope_entity_id"),
979
- tenantId: text2("tenant_id"),
980
- tags: jsonb2("tags").notNull().default({}).$type(),
981
- pool: text2("pool").notNull(),
982
- priority: integer("priority").notNull().default(0),
983
- concurrencyKey: text2("concurrency_key"),
984
- dedupeKey: text2("dedupe_key"),
985
- status: jobRunStatusEnum("status").notNull().default("pending"),
986
- input: jsonb2("input").notNull().$type(),
987
- output: jsonb2("output").$type(),
988
- error: jsonb2("error").$type(),
989
- triggerSource: triggerSourceEnum("trigger_source").notNull(),
990
- triggerRef: text2("trigger_ref"),
991
- runAt: timestamp2("run_at", { withTimezone: true }).notNull().defaultNow(),
992
- startedAt: timestamp2("started_at", { withTimezone: true }),
993
- finishedAt: timestamp2("finished_at", { withTimezone: true }),
994
- claimedAt: timestamp2("claimed_at", { withTimezone: true }),
995
- attempts: integer("attempts").notNull().default(0),
996
- // Phase 3 placeholder — see ADR-025
997
- waitKind: waitKindEnum("wait_kind"),
998
- // Phase 3 placeholder — see ADR-025
999
- resumeToken: text2("resume_token"),
1000
- // Phase 3 placeholder — see ADR-025
1001
- waitDeadline: timestamp2("wait_deadline", { withTimezone: true }),
1002
- createdAt: timestamp2("created_at", { withTimezone: true }).notNull().defaultNow(),
1003
- updatedAt: timestamp2("updated_at", { withTimezone: true }).notNull().defaultNow()
1004
- },
1005
- (t) => ({
1006
- /** Claim query: ORDER BY priority DESC, run_at ASC. */
1007
- idxJobRunClaim: index2("idx_job_run_claim").on(t.status, t.pool, t.runAt),
1008
- /** Tree traversal / cascade cancel. */
1009
- idxJobRunRoot: index2("idx_job_run_root").on(t.rootRunId),
1010
- /** listForScope query. */
1011
- idxJobRunScope: index2("idx_job_run_scope").on(t.scopeEntityType, t.scopeEntityId),
1012
- /** Idempotency collapse — partial index. */
1013
- idxJobRunDedupe: index2("idx_job_run_dedupe").on(t.jobType, t.dedupeKey).where(sql`${t.dedupeKey} IS NOT NULL`),
1014
- /** Collision check — partial index. */
1015
- idxJobRunConcurrency: index2("idx_job_run_concurrency").on(t.concurrencyKey).where(
1016
- sql`${t.concurrencyKey} IS NOT NULL AND ${t.status} IN ('pending','running')`
1017
- )
1018
- })
1019
- );
1020
- var jobSteps = pgTable2(
1021
- "job_step",
1022
- {
1023
- id: uuid2("id").primaryKey().defaultRandom(),
1024
- jobRunId: uuid2("job_run_id").notNull().references(() => jobRuns.id),
1025
- stepId: text2("step_id").notNull(),
1026
- kind: jobStepKindEnum("kind").notNull().default("task"),
1027
- /**
1028
- * Monotonic within run. integer (max ~2B per run) is sufficient —
1029
- * downgraded from ADR-022's bigint; revisit only if a single run
1030
- * ever exceeds 2 billion steps.
1031
- */
1032
- seq: integer("seq").notNull(),
1033
- status: jobStepStatusEnum("status").notNull().default("pending"),
1034
- input: jsonb2("input").$type(),
1035
- /** Memoised on success for replay. */
1036
- output: jsonb2("output").$type(),
1037
- error: jsonb2("error").$type(),
1038
- attempts: integer("attempts").notNull().default(0),
1039
- startedAt: timestamp2("started_at", { withTimezone: true }),
1040
- finishedAt: timestamp2("finished_at", { withTimezone: true })
1041
- },
1042
- (t) => ({
1043
- /** No duplicate step IDs per run. */
1044
- idxJobStepRunStep: uniqueIndex("idx_job_step_run_step").on(t.jobRunId, t.stepId),
1045
- /** Ordered timeline reads. */
1046
- idxJobStepTimeline: index2("idx_job_step_timeline").on(t.jobRunId, t.seq)
1047
- })
1048
- );
1316
+ // runtime/subsystems/events/index.ts
1317
+ init_domain_events_schema();
1318
+
1319
+ // runtime/subsystems/jobs/index.ts
1320
+ init_job_orchestration_schema();
1321
+ init_job_orchestration_schema();
1049
1322
 
1050
1323
  // runtime/subsystems/jobs/jobs-domain.tokens.ts
1051
1324
  var JOB_ORCHESTRATOR = /* @__PURE__ */ Symbol("JOB_ORCHESTRATOR");
@@ -1068,6 +1341,8 @@ var HandlerRegistry;
1068
1341
  })(HandlerRegistry || (HandlerRegistry = {}));
1069
1342
 
1070
1343
  // runtime/subsystems/jobs/job-orchestrator.drizzle-backend.ts
1344
+ init_tokens();
1345
+ init_job_orchestration_schema();
1071
1346
  import { randomUUID as randomUUID2 } from "crypto";
1072
1347
  import { Inject as Inject5, Injectable as Injectable5, Logger as Logger4 } from "@nestjs/common";
1073
1348
  import { and as and2, desc, eq as eq2, gt, inArray as inArray2, isNotNull, ne, notInArray, sql as sql2 } from "drizzle-orm";
@@ -1152,6 +1427,7 @@ var ReservedPoolViolationError = class extends Error {
1152
1427
  };
1153
1428
 
1154
1429
  // runtime/subsystems/jobs/job-orchestrator.drizzle-backend.ts
1430
+ init_job_orchestration_schema();
1155
1431
  var TERMINAL_STATUSES = [
1156
1432
  "completed",
1157
1433
  "failed",
@@ -1476,6 +1752,8 @@ function notInStatus(statuses) {
1476
1752
  }
1477
1753
 
1478
1754
  // runtime/subsystems/jobs/job-run-service.drizzle-backend.ts
1755
+ init_tokens();
1756
+ init_job_orchestration_schema();
1479
1757
  import { Inject as Inject6, Injectable as Injectable6 } from "@nestjs/common";
1480
1758
  import { and as and3, asc as asc2, desc as desc2, eq as eq3, inArray as inArray3, isNull } from "drizzle-orm";
1481
1759
  var NON_TERMINAL_STATUSES = [
@@ -1587,6 +1865,8 @@ DrizzleJobRunService = __decorateClass([
1587
1865
  ], DrizzleJobRunService);
1588
1866
 
1589
1867
  // runtime/subsystems/jobs/job-step-service.drizzle-backend.ts
1868
+ init_tokens();
1869
+ init_job_orchestration_schema();
1590
1870
  import { Inject as Inject7, Injectable as Injectable7 } from "@nestjs/common";
1591
1871
  import { and as and4, eq as eq4 } from "drizzle-orm";
1592
1872
  var DrizzleJobStepService = class {
@@ -1631,6 +1911,8 @@ DrizzleJobStepService = __decorateClass([
1631
1911
  ], DrizzleJobStepService);
1632
1912
 
1633
1913
  // runtime/subsystems/jobs/job-worker.ts
1914
+ init_tokens();
1915
+ init_job_orchestration_schema();
1634
1916
  import { Inject as Inject8, Injectable as Injectable8, Logger as Logger5 } from "@nestjs/common";
1635
1917
  import { and as and5, asc as asc3, desc as desc3, eq as eq5, inArray as inArray4, lt, lte, sql as sql3 } from "drizzle-orm";
1636
1918
  var JOB_WORKER_OPTIONS = /* @__PURE__ */ Symbol("JOB_WORKER_OPTIONS");
@@ -2904,6 +3186,7 @@ JobsDomainModule = __decorateClass([
2904
3186
  ], JobsDomainModule);
2905
3187
 
2906
3188
  // runtime/subsystems/jobs/job-worker.module.ts
3189
+ init_tokens();
2907
3190
  import {
2908
3191
  Inject as Inject11,
2909
3192
  Injectable as Injectable12,
@@ -3200,6 +3483,7 @@ var CACHE_DEFAULT_TTL = /* @__PURE__ */ Symbol("CACHE_DEFAULT_TTL");
3200
3483
 
3201
3484
  // runtime/subsystems/cache/cache.module.ts
3202
3485
  import { Module as Module4 } from "@nestjs/common";
3486
+ init_tokens();
3203
3487
 
3204
3488
  // runtime/subsystems/cache/cache.drizzle-backend.ts
3205
3489
  import { Injectable as Injectable13, Inject as Inject12, Optional as Optional5 } from "@nestjs/common";
@@ -3221,6 +3505,7 @@ var cacheEntries = pgTable3(
3221
3505
  );
3222
3506
 
3223
3507
  // runtime/subsystems/cache/cache.drizzle-backend.ts
3508
+ init_tokens();
3224
3509
  var CLEANUP_INTERVAL_MS = 5 * 60 * 1e3;
3225
3510
  var DrizzleCacheService = class {
3226
3511
  constructor(db, defaultTtl = null) {
@@ -3641,6 +3926,412 @@ StorageModule = __decorateClass([
3641
3926
  Module5({})
3642
3927
  ], StorageModule);
3643
3928
 
3929
+ // runtime/subsystems/observability/observability.tokens.ts
3930
+ var OBSERVABILITY = /* @__PURE__ */ Symbol("OBSERVABILITY");
3931
+ var OBSERVABILITY_REPORTERS = /* @__PURE__ */ Symbol("OBSERVABILITY_REPORTERS");
3932
+
3933
+ // runtime/subsystems/observability/observability.module.ts
3934
+ import { Module as Module6 } from "@nestjs/common";
3935
+
3936
+ // runtime/subsystems/observability/observability.drizzle-backend.ts
3937
+ init_tokens();
3938
+ init_job_orchestration_schema();
3939
+ import { Inject as Inject14, Injectable as Injectable15 } from "@nestjs/common";
3940
+ import { desc as desc4, eq as eq7, sql as sql5 } from "drizzle-orm";
3941
+
3942
+ // runtime/subsystems/sync/sync-audit.schema.ts
3943
+ import {
3944
+ pgEnum as pgEnum2,
3945
+ pgTable as pgTable4,
3946
+ uuid as uuid3,
3947
+ text as text4,
3948
+ jsonb as jsonb4,
3949
+ integer as integer2,
3950
+ boolean,
3951
+ timestamp as timestamp4,
3952
+ index as index3,
3953
+ uniqueIndex as uniqueIndex2
3954
+ } from "drizzle-orm/pg-core";
3955
+ var syncRunDirectionEnum = pgEnum2("sync_run_direction", [
3956
+ "inbound",
3957
+ "outbound"
3958
+ ]);
3959
+ var syncRunActionEnum = pgEnum2("sync_run_action", [
3960
+ "poll",
3961
+ "cdc",
3962
+ "webhook",
3963
+ "manual",
3964
+ "writeback"
3965
+ ]);
3966
+ var syncRunStatusEnum = pgEnum2("sync_run_status", [
3967
+ "running",
3968
+ "success",
3969
+ "no_changes",
3970
+ "failed"
3971
+ ]);
3972
+ var syncRunItemOperationEnum = pgEnum2("sync_run_item_operation", [
3973
+ "created",
3974
+ "updated",
3975
+ "deleted",
3976
+ "noop"
3977
+ ]);
3978
+ var syncRunItemStatusEnum = pgEnum2("sync_run_item_status", [
3979
+ "success",
3980
+ "failed",
3981
+ "skipped"
3982
+ ]);
3983
+ var syncSubscriptions = pgTable4(
3984
+ "sync_subscriptions",
3985
+ {
3986
+ id: uuid3("id").primaryKey().defaultRandom(),
3987
+ integrationId: text4("integration_id").notNull(),
3988
+ adapter: text4("adapter").notNull(),
3989
+ domain: text4("domain").notNull(),
3990
+ externalRef: text4("external_ref"),
3991
+ enabled: boolean("enabled").notNull().default(true),
3992
+ /**
3993
+ * Per-subscription configuration bag. Strategies type it internally;
3994
+ * e.g. polling strategies stash `{ batchSize, highWatermark }` here.
3995
+ */
3996
+ config: jsonb4("config").notNull().default({}).$type(),
3997
+ /**
3998
+ * Opaque cursor persisted by `ICursorStore.put()`. NULL until the first
3999
+ * successful run advances it.
4000
+ */
4001
+ cursor: jsonb4("cursor").$type(),
4002
+ lastSyncAt: timestamp4("last_sync_at", { withTimezone: true }),
4003
+ /** Runtime-enforced when `SYNC_MULTI_TENANT` is true; see SYNC-6. */
4004
+ tenantId: text4("tenant_id"),
4005
+ createdAt: timestamp4("created_at", { withTimezone: true }).notNull().defaultNow(),
4006
+ updatedAt: timestamp4("updated_at", { withTimezone: true }).notNull().defaultNow()
4007
+ },
4008
+ (t) => ({
4009
+ /**
4010
+ * Composite uniqueness per the epic shape. `external_ref` is nullable;
4011
+ * Postgres treats NULLs as distinct in a UNIQUE constraint, which means
4012
+ * two rows with the same `(integration_id, adapter, domain)` and NULL
4013
+ * external_ref are allowed. That's intentional — a subscription with
4014
+ * NULL external_ref covers the full domain, and duplicates there would
4015
+ * be a consumer-layer modeling issue, not a schema concern.
4016
+ */
4017
+ uqSyncSubscriptionTuple: uniqueIndex2("uq_sync_subscriptions_tuple").on(
4018
+ t.integrationId,
4019
+ t.adapter,
4020
+ t.domain,
4021
+ t.externalRef
4022
+ ),
4023
+ /** Scheduling query: list enabled subscriptions ordered by staleness. */
4024
+ idxSyncSubscriptionsEnabledLastSync: index3(
4025
+ "idx_sync_subscriptions_enabled_last_sync"
4026
+ ).on(t.enabled, t.lastSyncAt)
4027
+ })
4028
+ );
4029
+ var syncRuns = pgTable4(
4030
+ "sync_runs",
4031
+ {
4032
+ id: uuid3("id").primaryKey().defaultRandom(),
4033
+ subscriptionId: uuid3("subscription_id").notNull().references(() => syncSubscriptions.id, { onDelete: "cascade" }),
4034
+ direction: syncRunDirectionEnum("direction").notNull(),
4035
+ action: syncRunActionEnum("action").notNull(),
4036
+ status: syncRunStatusEnum("status").notNull().default("running"),
4037
+ recordsFound: integer2("records_found").notNull().default(0),
4038
+ recordsProcessed: integer2("records_processed").notNull().default(0),
4039
+ cursorBefore: jsonb4("cursor_before").$type(),
4040
+ cursorAfter: jsonb4("cursor_after").$type(),
4041
+ durationMs: integer2("duration_ms"),
4042
+ error: text4("error"),
4043
+ startedAt: timestamp4("started_at", { withTimezone: true }).notNull().defaultNow(),
4044
+ completedAt: timestamp4("completed_at", { withTimezone: true }),
4045
+ /** Runtime-enforced when `SYNC_MULTI_TENANT` is true; see SYNC-6. */
4046
+ tenantId: text4("tenant_id")
4047
+ },
4048
+ (t) => ({
4049
+ /** Timeline read: "most recent runs for this subscription". */
4050
+ idxSyncRunsSubscriptionStartedAt: index3(
4051
+ "idx_sync_runs_subscription_started_at"
4052
+ ).on(t.subscriptionId, t.startedAt),
4053
+ /** Stale-run sweeper: "runs that started > N minutes ago and are still running". */
4054
+ idxSyncRunsStatusStartedAt: index3("idx_sync_runs_status_started_at").on(
4055
+ t.status,
4056
+ t.startedAt
4057
+ )
4058
+ })
4059
+ );
4060
+ var syncRunItems = pgTable4(
4061
+ "sync_run_items",
4062
+ {
4063
+ id: uuid3("id").primaryKey().defaultRandom(),
4064
+ syncRunId: uuid3("sync_run_id").notNull().references(() => syncRuns.id, { onDelete: "cascade" }),
4065
+ entityType: text4("entity_type").notNull(),
4066
+ externalId: text4("external_id").notNull(),
4067
+ localId: text4("local_id"),
4068
+ operation: syncRunItemOperationEnum("operation").notNull(),
4069
+ status: syncRunItemStatusEnum("status").notNull(),
4070
+ /**
4071
+ * Structured per-field diff — ADR-0003 shape enforced by
4072
+ * `FieldDiffSchema.parse` at the recorder service layer.
4073
+ *
4074
+ * Shape: `{ [fieldName]: { from: unknown, to: unknown } }`.
4075
+ * Empty `{}` for `noop` items; `{ [field]: { from: null, to: <value> } }`
4076
+ * for created items; `{ [field]: { from: <value>, to: null } }` for
4077
+ * deleted items.
4078
+ */
4079
+ changedFields: jsonb4("changed_fields").notNull().default({}).$type(),
4080
+ title: text4("title"),
4081
+ error: text4("error"),
4082
+ createdAt: timestamp4("created_at", { withTimezone: true }).notNull().defaultNow(),
4083
+ /** Runtime-enforced when `SYNC_MULTI_TENANT` is true; see SYNC-6. */
4084
+ tenantId: text4("tenant_id")
4085
+ },
4086
+ (t) => ({
4087
+ /** Ordered timeline within a run. */
4088
+ idxSyncRunItemsRunCreatedAt: index3("idx_sync_run_items_run_created_at").on(
4089
+ t.syncRunId,
4090
+ t.createdAt
4091
+ ),
4092
+ /** Per-record history: "every sync that touched opportunity/$extId". */
4093
+ idxSyncRunItemsEntityExternal: index3(
4094
+ "idx_sync_run_items_entity_external"
4095
+ ).on(t.entityType, t.externalId)
4096
+ })
4097
+ );
4098
+
4099
+ // runtime/subsystems/observability/observability.drizzle-backend.ts
4100
+ var DrizzleObservabilityService = class {
4101
+ constructor(db) {
4102
+ this.db = db;
4103
+ }
4104
+ db;
4105
+ async getPoolDepths() {
4106
+ const result = await this.db.execute(sql5`
4107
+ SELECT
4108
+ pool AS name,
4109
+ COUNT(*) FILTER (WHERE status = 'pending')::int AS pending,
4110
+ COUNT(*) FILTER (WHERE status = 'running')::int AS running,
4111
+ (percentile_cont(0.95) WITHIN GROUP (
4112
+ ORDER BY EXTRACT(EPOCH FROM (now() - claimed_at)) * 1000
4113
+ ) FILTER (WHERE status = 'running' AND claimed_at IS NOT NULL))::int
4114
+ AS claimed_age_p95_ms
4115
+ FROM job_run
4116
+ WHERE status IN ('pending','running')
4117
+ GROUP BY pool
4118
+ ORDER BY pool
4119
+ `);
4120
+ const rows = extractRows(result);
4121
+ return rows.map((r) => ({
4122
+ name: r.name,
4123
+ pending: r.pending,
4124
+ running: r.running,
4125
+ claimedAgeP95Ms: r.claimed_age_p95_ms
4126
+ }));
4127
+ }
4128
+ async getRecentSyncRuns(limit, integrationId) {
4129
+ const base = this.db.select({
4130
+ id: syncRuns.id,
4131
+ subscriptionId: syncRuns.subscriptionId,
4132
+ integrationId: syncSubscriptions.integrationId,
4133
+ adapter: syncSubscriptions.adapter,
4134
+ domain: syncSubscriptions.domain,
4135
+ direction: syncRuns.direction,
4136
+ action: syncRuns.action,
4137
+ status: syncRuns.status,
4138
+ recordsFound: syncRuns.recordsFound,
4139
+ recordsProcessed: syncRuns.recordsProcessed,
4140
+ durationMs: syncRuns.durationMs,
4141
+ error: syncRuns.error,
4142
+ startedAt: syncRuns.startedAt,
4143
+ completedAt: syncRuns.completedAt
4144
+ }).from(syncRuns).innerJoin(
4145
+ syncSubscriptions,
4146
+ eq7(syncRuns.subscriptionId, syncSubscriptions.id)
4147
+ );
4148
+ const filtered = integrationId !== void 0 ? base.where(eq7(syncSubscriptions.integrationId, integrationId)) : base;
4149
+ const rows = await filtered.orderBy(desc4(syncRuns.startedAt)).limit(limit);
4150
+ return rows.map((r) => ({
4151
+ id: r.id,
4152
+ subscriptionId: r.subscriptionId,
4153
+ integrationId: r.integrationId,
4154
+ adapter: r.adapter,
4155
+ domain: r.domain,
4156
+ direction: r.direction,
4157
+ action: r.action,
4158
+ status: r.status,
4159
+ recordsFound: r.recordsFound,
4160
+ recordsProcessed: r.recordsProcessed,
4161
+ durationMs: r.durationMs,
4162
+ error: r.error,
4163
+ startedAt: r.startedAt,
4164
+ completedAt: r.completedAt
4165
+ }));
4166
+ }
4167
+ async getBridgeDeliveryHistogram(windowHours) {
4168
+ const result = await this.db.execute(sql5`
4169
+ SELECT status, COUNT(*)::int AS count
4170
+ FROM bridge_delivery
4171
+ WHERE COALESCE(delivered_at, attempted_at) > now() - make_interval(hours => ${windowHours})
4172
+ GROUP BY status
4173
+ `);
4174
+ const rows = extractRows(result);
4175
+ const hist = {};
4176
+ for (const r of rows) hist[r.status] = r.count;
4177
+ return hist;
4178
+ }
4179
+ async getRecentFailedJobs(limit) {
4180
+ const rows = await this.db.select({
4181
+ id: jobRuns.id,
4182
+ jobType: jobRuns.jobType,
4183
+ pool: jobRuns.pool,
4184
+ status: jobRuns.status,
4185
+ error: jobRuns.error,
4186
+ startedAt: jobRuns.startedAt,
4187
+ finishedAt: jobRuns.finishedAt,
4188
+ attempts: jobRuns.attempts
4189
+ }).from(jobRuns).where(eq7(jobRuns.status, "failed")).orderBy(desc4(jobRuns.finishedAt)).limit(limit);
4190
+ return rows.map((r) => ({
4191
+ id: r.id,
4192
+ jobType: r.jobType,
4193
+ pool: r.pool,
4194
+ status: r.status,
4195
+ error: r.error,
4196
+ startedAt: r.startedAt,
4197
+ finishedAt: r.finishedAt,
4198
+ attempts: r.attempts
4199
+ }));
4200
+ }
4201
+ async getCursors() {
4202
+ const rows = await this.db.select({
4203
+ id: syncSubscriptions.id,
4204
+ integrationId: syncSubscriptions.integrationId,
4205
+ adapter: syncSubscriptions.adapter,
4206
+ domain: syncSubscriptions.domain,
4207
+ cursor: syncSubscriptions.cursor,
4208
+ lastSyncAt: syncSubscriptions.lastSyncAt
4209
+ }).from(syncSubscriptions).where(eq7(syncSubscriptions.enabled, true)).orderBy(syncSubscriptions.integrationId, syncSubscriptions.domain);
4210
+ return rows.map((r) => ({
4211
+ subscriptionId: r.id,
4212
+ integrationId: r.integrationId,
4213
+ adapter: r.adapter,
4214
+ domain: r.domain,
4215
+ lastCursor: r.cursor,
4216
+ lastSyncAt: r.lastSyncAt
4217
+ }));
4218
+ }
4219
+ };
4220
+ DrizzleObservabilityService = __decorateClass([
4221
+ Injectable15(),
4222
+ __decorateParam(0, Inject14(DRIZZLE))
4223
+ ], DrizzleObservabilityService);
4224
+ function extractRows(result) {
4225
+ const maybe = result;
4226
+ if (Array.isArray(maybe.rows)) return maybe.rows;
4227
+ if (Array.isArray(result)) return result;
4228
+ return [];
4229
+ }
4230
+
4231
+ // runtime/subsystems/observability/observability.memory-backend.ts
4232
+ import { Injectable as Injectable16 } from "@nestjs/common";
4233
+ var MemoryObservabilityService = class {
4234
+ pools = [];
4235
+ syncRuns = [];
4236
+ bridgeHistogram = {};
4237
+ failedJobs = [];
4238
+ cursors = [];
4239
+ // ─── Core contract ─────────────────────────────────────────────────────
4240
+ async getPoolDepths() {
4241
+ return [...this.pools];
4242
+ }
4243
+ async getRecentSyncRuns(limit, integrationId) {
4244
+ const filtered = integrationId !== void 0 ? this.syncRuns.filter((r) => r.integrationId === integrationId) : this.syncRuns;
4245
+ return filtered.slice().sort((a, b) => b.startedAt.getTime() - a.startedAt.getTime()).slice(0, limit);
4246
+ }
4247
+ async getBridgeDeliveryHistogram(_windowHours) {
4248
+ return { ...this.bridgeHistogram };
4249
+ }
4250
+ async getRecentFailedJobs(limit) {
4251
+ return this.failedJobs.slice().sort(
4252
+ (a, b) => (b.finishedAt?.getTime() ?? 0) - (a.finishedAt?.getTime() ?? 0)
4253
+ ).slice(0, limit);
4254
+ }
4255
+ async getCursors() {
4256
+ return [...this.cursors];
4257
+ }
4258
+ // ─── Test seams ────────────────────────────────────────────────────────
4259
+ /** Replace the pool-depth slice. */
4260
+ seedPools(pools) {
4261
+ this.pools = [...pools];
4262
+ }
4263
+ /** Replace the sync-run slice. */
4264
+ seedSyncRuns(runs) {
4265
+ this.syncRuns = [...runs];
4266
+ }
4267
+ /** Replace the bridge-delivery histogram. */
4268
+ seedBridgeHistogram(hist) {
4269
+ this.bridgeHistogram = { ...hist };
4270
+ }
4271
+ /** Replace the failed-jobs slice. */
4272
+ seedFailedJobs(jobs2) {
4273
+ this.failedJobs = [...jobs2];
4274
+ }
4275
+ /** Replace the cursor slice. */
4276
+ seedCursors(cursors) {
4277
+ this.cursors = [...cursors];
4278
+ }
4279
+ /** Reset every slice — for afterEach hooks. */
4280
+ reset() {
4281
+ this.pools = [];
4282
+ this.syncRuns = [];
4283
+ this.bridgeHistogram = {};
4284
+ this.failedJobs = [];
4285
+ this.cursors = [];
4286
+ }
4287
+ };
4288
+ MemoryObservabilityService = __decorateClass([
4289
+ Injectable16()
4290
+ ], MemoryObservabilityService);
4291
+
4292
+ // runtime/subsystems/observability/observability.module.ts
4293
+ var ObservabilityModule = class {
4294
+ static forRoot(options = { backend: "drizzle" }) {
4295
+ const ConcreteClass = options.backend === "drizzle" ? DrizzleObservabilityService : MemoryObservabilityService;
4296
+ const wantsBridgeMetrics = options.reporters?.bridgeMetrics === true;
4297
+ const providers = [
4298
+ // Register the concrete class as the canonical instance.
4299
+ ConcreteClass,
4300
+ // OBSERVABILITY token points at the same instance — no duplicate.
4301
+ { provide: OBSERVABILITY, useExisting: ConcreteClass },
4302
+ // Expose the resolved reporter config for introspection / tests.
4303
+ {
4304
+ provide: OBSERVABILITY_REPORTERS,
4305
+ useValue: options.reporters ?? {}
4306
+ }
4307
+ ];
4308
+ const exports = [OBSERVABILITY];
4309
+ if (wantsBridgeMetrics) {
4310
+ const { BridgeMetricsReporter: BridgeMetricsReporter2 } = (init_bridge_metrics_reporter(), __toCommonJS(bridge_metrics_reporter_exports));
4311
+ providers.push(BridgeMetricsReporter2);
4312
+ exports.push(BridgeMetricsReporter2);
4313
+ }
4314
+ const imports = [];
4315
+ if (wantsBridgeMetrics) {
4316
+ const { ScheduleModule } = __require("@nestjs/schedule");
4317
+ imports.push(ScheduleModule.forRoot());
4318
+ }
4319
+ return {
4320
+ module: ObservabilityModule,
4321
+ global: true,
4322
+ imports,
4323
+ providers,
4324
+ exports
4325
+ };
4326
+ }
4327
+ };
4328
+ ObservabilityModule = __decorateClass([
4329
+ Module6({})
4330
+ ], ObservabilityModule);
4331
+
4332
+ // runtime/subsystems/observability/index.ts
4333
+ init_bridge_metrics_reporter();
4334
+
3644
4335
  // runtime/subsystems/auth/auth.tokens.ts
3645
4336
  var ENCRYPTION_KEY = /* @__PURE__ */ Symbol("ENCRYPTION_KEY");
3646
4337
  var OAUTH_STATE_STORE = /* @__PURE__ */ Symbol("OAUTH_STATE_STORE");
@@ -3854,7 +4545,7 @@ var InMemoryOAuthStateStore = class {
3854
4545
  };
3855
4546
 
3856
4547
  // runtime/subsystems/auth/auth.module.ts
3857
- import { Module as Module6 } from "@nestjs/common";
4548
+ import { Module as Module7 } from "@nestjs/common";
3858
4549
  function resolveEncryptionKeyProvider(choice) {
3859
4550
  if (choice === "env") {
3860
4551
  return { provide: ENCRYPTION_KEY, useClass: EnvEncryptionKey };
@@ -3884,16 +4575,18 @@ var AuthModule = class {
3884
4575
  }
3885
4576
  };
3886
4577
  AuthModule = __decorateClass([
3887
- Module6({})
4578
+ Module7({})
3888
4579
  ], AuthModule);
3889
4580
  export {
3890
4581
  AUTH_INTEGRATION_READER,
3891
4582
  AUTH_INTEGRATION_TOKEN_WRITER,
3892
4583
  AuthModule,
4584
+ BridgeMetricsReporter,
3893
4585
  CACHE,
3894
4586
  CacheModule,
3895
4587
  DrizzleCacheService,
3896
4588
  DrizzleEventBus,
4589
+ DrizzleObservabilityService,
3897
4590
  ENCRYPTION_KEY,
3898
4591
  EVENT_BUS,
3899
4592
  EnvEncryptionKey,
@@ -3903,9 +4596,13 @@ export {
3903
4596
  LocalStorageBackend,
3904
4597
  MemoryCacheService,
3905
4598
  MemoryEventBus,
4599
+ MemoryObservabilityService,
3906
4600
  MemoryStorageBackend,
3907
4601
  OAUTH_STATE_STORE,
3908
4602
  OAuth2RefreshStrategy,
4603
+ OBSERVABILITY,
4604
+ OBSERVABILITY_REPORTERS,
4605
+ ObservabilityModule,
3909
4606
  STORAGE,
3910
4607
  SessionExpiredError,
3911
4608
  StorageModule,