@pattern-stack/codegen 0.11.0 → 0.12.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 (37) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/dist/runtime/subsystems/index.d.ts +7 -3
  3. package/dist/runtime/subsystems/index.js +993 -19
  4. package/dist/runtime/subsystems/index.js.map +1 -1
  5. package/dist/runtime/subsystems/integration/entity-change-source-registry.memory.d.ts +25 -0
  6. package/dist/runtime/subsystems/integration/entity-change-source-registry.memory.js +34 -0
  7. package/dist/runtime/subsystems/integration/entity-change-source-registry.memory.js.map +1 -0
  8. package/dist/runtime/subsystems/integration/entity-change-source-registry.protocol.d.ts +53 -0
  9. package/dist/runtime/subsystems/integration/entity-change-source-registry.protocol.js +13 -0
  10. package/dist/runtime/subsystems/integration/entity-change-source-registry.protocol.js.map +1 -0
  11. package/dist/runtime/subsystems/integration/execute-integration.use-case.js.map +1 -1
  12. package/dist/runtime/subsystems/integration/index.d.ts +3 -1
  13. package/dist/runtime/subsystems/integration/index.js +35 -0
  14. package/dist/runtime/subsystems/integration/index.js.map +1 -1
  15. package/dist/runtime/subsystems/integration/integration-cursor-store.drizzle-backend.js.map +1 -1
  16. package/dist/runtime/subsystems/integration/integration-run-recorder.drizzle-backend.js.map +1 -1
  17. package/dist/runtime/subsystems/integration/integration.module.js.map +1 -1
  18. package/dist/runtime/subsystems/integration/integration.tokens.d.ts +14 -1
  19. package/dist/runtime/subsystems/integration/integration.tokens.js +2 -0
  20. package/dist/runtime/subsystems/integration/integration.tokens.js.map +1 -1
  21. package/dist/runtime/subsystems/observability/index.js.map +1 -1
  22. package/dist/runtime/subsystems/observability/observability.module.js.map +1 -1
  23. package/dist/runtime/subsystems/observability/observability.service.js.map +1 -1
  24. package/dist/src/cli/index.js +1074 -107
  25. package/dist/src/cli/index.js.map +1 -1
  26. package/dist/src/index.d.ts +48 -0
  27. package/dist/src/index.js +99 -3
  28. package/dist/src/index.js.map +1 -1
  29. package/package.json +9 -1
  30. package/runtime/subsystems/index.ts +15 -0
  31. package/runtime/subsystems/integration/entity-change-source-registry.memory.ts +40 -0
  32. package/runtime/subsystems/integration/entity-change-source-registry.protocol.ts +59 -0
  33. package/runtime/subsystems/integration/index.ts +9 -0
  34. package/runtime/subsystems/integration/integration.tokens.ts +14 -0
  35. package/templates/entity/new/clean-lite-ps/entity.ejs.t +12 -3
  36. package/templates/entity/new/clean-lite-ps/prompt-extension.js +212 -29
  37. package/templates/entity/new/backend/modules/core/integration-source.providers.ejs.t +0 -18
@@ -9,7 +9,7 @@ var __decorateClass = (decorators, target, key, kind) => {
9
9
  if (kind && result) __defProp(target, key, result);
10
10
  return result;
11
11
  };
12
- var __decorateParam = (index3, decorator) => (target, key) => decorator(target, key, index3);
12
+ var __decorateParam = (index4, decorator) => (target, key) => decorator(target, key, index4);
13
13
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
14
14
 
15
15
  // runtime/subsystems/events/events.tokens.ts
@@ -4017,16 +4017,22 @@ import { Module as Module6 } from "@nestjs/common";
4017
4017
  import { Inject as Inject13, Injectable as Injectable14, Optional as Optional7 } from "@nestjs/common";
4018
4018
 
4019
4019
  // runtime/subsystems/integration/integration.tokens.ts
4020
+ var INTEGRATION_CHANGE_SOURCE = "INTEGRATION_CHANGE_SOURCE";
4020
4021
  var INTEGRATION_CURSOR_STORE = "INTEGRATION_CURSOR_STORE";
4022
+ var INTEGRATION_FIELD_DIFFER = "INTEGRATION_FIELD_DIFFER";
4023
+ var INTEGRATION_SINK = "INTEGRATION_SINK";
4021
4024
  var INTEGRATION_RUN_RECORDER = "INTEGRATION_RUN_RECORDER";
4025
+ var INTEGRATION_MODULE_OPTIONS = "INTEGRATION_MODULE_OPTIONS";
4026
+ var INTEGRATION_MULTI_TENANT = "INTEGRATION_MULTI_TENANT";
4027
+ var ENTITY_CHANGE_SOURCE_REGISTRY = "ENTITY_CHANGE_SOURCE_REGISTRY";
4022
4028
 
4023
4029
  // runtime/subsystems/observability/observability.service.ts
4024
4030
  var MAX_TIMELINE_PAGES = 50;
4025
4031
  var ObservabilityService = class {
4026
- constructor(jobRuns2, bridge, integrationRuns, cursors, events) {
4032
+ constructor(jobRuns2, bridge, integrationRuns2, cursors, events) {
4027
4033
  this.jobRuns = jobRuns2;
4028
4034
  this.bridge = bridge;
4029
- this.integrationRuns = integrationRuns;
4035
+ this.integrationRuns = integrationRuns2;
4030
4036
  this.cursors = cursors;
4031
4037
  this.events = events;
4032
4038
  }
@@ -4287,6 +4293,971 @@ var ObservabilityError = class extends Error {
4287
4293
  cause;
4288
4294
  };
4289
4295
 
4296
+ // runtime/subsystems/integration/integration-field-diff.protocol.ts
4297
+ import { z as z2 } from "zod";
4298
+ var FieldDiffValueSchema = z2.object({
4299
+ from: z2.unknown(),
4300
+ to: z2.unknown()
4301
+ });
4302
+ var FieldDiffSchema = z2.record(z2.string(), FieldDiffValueSchema);
4303
+
4304
+ // runtime/subsystems/integration/entity-change-source-registry.protocol.ts
4305
+ var UnknownEntityError = class extends Error {
4306
+ constructor(entity, available) {
4307
+ super(
4308
+ `No change source registered for entity '${entity}'. Available: ${available.join(", ")}`
4309
+ );
4310
+ this.name = "UnknownEntityError";
4311
+ }
4312
+ };
4313
+
4314
+ // runtime/subsystems/integration/entity-change-source-registry.memory.ts
4315
+ var MemoryEntityChangeSourceRegistry = class {
4316
+ constructor(sources) {
4317
+ this.sources = sources;
4318
+ }
4319
+ sources;
4320
+ get(name) {
4321
+ const source = this.sources.get(name);
4322
+ if (!source) {
4323
+ throw new UnknownEntityError(name, [...this.sources.keys()]);
4324
+ }
4325
+ return source;
4326
+ }
4327
+ has(name) {
4328
+ return this.sources.has(name);
4329
+ }
4330
+ entities() {
4331
+ return [...this.sources.keys()];
4332
+ }
4333
+ };
4334
+
4335
+ // runtime/subsystems/integration/detection-config.schema.ts
4336
+ import { z as z3 } from "zod";
4337
+ var FieldMappingSchema = z3.object({
4338
+ source: z3.string().min(1),
4339
+ target: z3.string().min(1),
4340
+ transform: z3.string().min(1).optional()
4341
+ });
4342
+ var ResolvedFilterSchema = z3.object({
4343
+ field: z3.string().min(1),
4344
+ op: z3.enum(["eq", "neq", "in", "nin", "gt", "gte", "lt", "lte"]),
4345
+ value: z3.unknown()
4346
+ });
4347
+ var SystemModstampCursorSchema = z3.object({
4348
+ kind: z3.literal("systemModstamp"),
4349
+ field: z3.string().min(1)
4350
+ });
4351
+ var ReplayIdCursorSchema = z3.object({
4352
+ kind: z3.literal("replayId"),
4353
+ field: z3.string().min(1)
4354
+ });
4355
+ var TimestampCursorSchema = z3.object({
4356
+ kind: z3.literal("timestamp"),
4357
+ field: z3.string().min(1)
4358
+ });
4359
+ var EventIdCursorSchema = z3.object({
4360
+ kind: z3.literal("eventId"),
4361
+ field: z3.string().min(1)
4362
+ });
4363
+ var CursorStrategySchema = z3.discriminatedUnion("kind", [
4364
+ SystemModstampCursorSchema,
4365
+ ReplayIdCursorSchema,
4366
+ TimestampCursorSchema,
4367
+ EventIdCursorSchema
4368
+ ]);
4369
+ var PollDetectionSchema = z3.object({
4370
+ cursor: CursorStrategySchema,
4371
+ provenance: z3.enum(["poll", "cdc"]).optional()
4372
+ });
4373
+ var WebhookDetectionSchema = z3.object({
4374
+ eventIdField: z3.string().min(1)
4375
+ });
4376
+ var PollModeSchema = z3.object({
4377
+ mode: z3.literal("poll"),
4378
+ poll: PollDetectionSchema,
4379
+ mapping: z3.array(FieldMappingSchema).min(1),
4380
+ filters: z3.array(ResolvedFilterSchema).default([])
4381
+ });
4382
+ var WebhookModeSchema = z3.object({
4383
+ mode: z3.literal("webhook"),
4384
+ webhook: WebhookDetectionSchema,
4385
+ mapping: z3.array(FieldMappingSchema).min(1),
4386
+ filters: z3.array(ResolvedFilterSchema).default([])
4387
+ });
4388
+ var DetectionConfigSchema = z3.discriminatedUnion("mode", [
4389
+ PollModeSchema,
4390
+ WebhookModeSchema
4391
+ ]);
4392
+
4393
+ // runtime/subsystems/integration/integration-errors.ts
4394
+ var MissingTenantIdError3 = class extends Error {
4395
+ name = "MissingTenantIdError";
4396
+ constructor(operation) {
4397
+ super(
4398
+ `Missing tenantId for integration operation '${operation}'. IntegrationModule is configured with multiTenant: true \u2014 every call must include a non-null tenantId. Either pass the tenantId or disable multi-tenancy on the module.`
4399
+ );
4400
+ }
4401
+ };
4402
+ function assertTenantId(tenantId, options) {
4403
+ if (!options.multiTenant) return;
4404
+ if (tenantId === void 0 || tenantId === null) {
4405
+ throw new MissingTenantIdError3(options.operation);
4406
+ }
4407
+ }
4408
+
4409
+ // runtime/subsystems/integration/integration-audit.schema.ts
4410
+ import {
4411
+ pgEnum as pgEnum2,
4412
+ pgTable as pgTable4,
4413
+ uuid as uuid3,
4414
+ text as text4,
4415
+ jsonb as jsonb4,
4416
+ integer as integer2,
4417
+ boolean,
4418
+ timestamp as timestamp4,
4419
+ index as index3,
4420
+ uniqueIndex as uniqueIndex2
4421
+ } from "drizzle-orm/pg-core";
4422
+ var integrationRunDirectionEnum = pgEnum2("integration_run_direction", [
4423
+ "inbound",
4424
+ "outbound"
4425
+ ]);
4426
+ var integrationRunActionEnum = pgEnum2("integration_run_action", [
4427
+ "poll",
4428
+ "cdc",
4429
+ "webhook",
4430
+ "manual",
4431
+ "writeback"
4432
+ ]);
4433
+ var integrationRunStatusEnum = pgEnum2("integration_run_status", [
4434
+ "running",
4435
+ "success",
4436
+ "no_changes",
4437
+ "failed"
4438
+ ]);
4439
+ var integrationRunItemOperationEnum = pgEnum2("integration_run_item_operation", [
4440
+ "created",
4441
+ "updated",
4442
+ "deleted",
4443
+ "noop"
4444
+ ]);
4445
+ var integrationRunItemStatusEnum = pgEnum2("integration_run_item_status", [
4446
+ "success",
4447
+ "failed",
4448
+ "skipped"
4449
+ ]);
4450
+ var integrationSubscriptions = pgTable4(
4451
+ "integration_subscriptions",
4452
+ {
4453
+ id: uuid3("id").primaryKey().defaultRandom(),
4454
+ connectionId: text4("connection_id").notNull(),
4455
+ adapter: text4("adapter").notNull(),
4456
+ domain: text4("domain").notNull(),
4457
+ externalRef: text4("external_ref"),
4458
+ enabled: boolean("enabled").notNull().default(true),
4459
+ /**
4460
+ * Per-subscription configuration bag. Strategies type it internally;
4461
+ * e.g. polling strategies stash `{ batchSize, highWatermark }` here.
4462
+ */
4463
+ config: jsonb4("config").notNull().default({}).$type(),
4464
+ /**
4465
+ * Opaque cursor persisted by `ICursorStore.put()`. NULL until the first
4466
+ * successful run advances it.
4467
+ */
4468
+ cursor: jsonb4("cursor").$type(),
4469
+ lastIntegrationAt: timestamp4("last_integration_at", { withTimezone: true }),
4470
+ /** Runtime-enforced when `INTEGRATION_MULTI_TENANT` is true; see SYNC-6. */
4471
+ tenantId: text4("tenant_id"),
4472
+ createdAt: timestamp4("created_at", { withTimezone: true }).notNull().defaultNow(),
4473
+ updatedAt: timestamp4("updated_at", { withTimezone: true }).notNull().defaultNow()
4474
+ },
4475
+ (t) => ({
4476
+ /**
4477
+ * Composite uniqueness per the epic shape. `external_ref` is nullable;
4478
+ * Postgres treats NULLs as distinct in a UNIQUE constraint, which means
4479
+ * two rows with the same `(connection_id, adapter, domain)` and NULL
4480
+ * external_ref are allowed. That's intentional — a subscription with
4481
+ * NULL external_ref covers the full domain, and duplicates there would
4482
+ * be a consumer-layer modeling issue, not a schema concern.
4483
+ */
4484
+ uqIntegrationSubscriptionTuple: uniqueIndex2("uq_integration_subscriptions_tuple").on(
4485
+ t.connectionId,
4486
+ t.adapter,
4487
+ t.domain,
4488
+ t.externalRef
4489
+ ),
4490
+ /** Scheduling query: list enabled subscriptions ordered by staleness. */
4491
+ idxIntegrationSubscriptionsEnabledLastIntegration: index3(
4492
+ "idx_integration_subscriptions_enabled_last_integration"
4493
+ ).on(t.enabled, t.lastIntegrationAt)
4494
+ })
4495
+ );
4496
+ var integrationRuns = pgTable4(
4497
+ "integration_runs",
4498
+ {
4499
+ id: uuid3("id").primaryKey().defaultRandom(),
4500
+ subscriptionId: uuid3("subscription_id").notNull().references(() => integrationSubscriptions.id, { onDelete: "cascade" }),
4501
+ direction: integrationRunDirectionEnum("direction").notNull(),
4502
+ action: integrationRunActionEnum("action").notNull(),
4503
+ status: integrationRunStatusEnum("status").notNull().default("running"),
4504
+ recordsFound: integer2("records_found").notNull().default(0),
4505
+ recordsProcessed: integer2("records_processed").notNull().default(0),
4506
+ cursorBefore: jsonb4("cursor_before").$type(),
4507
+ cursorAfter: jsonb4("cursor_after").$type(),
4508
+ durationMs: integer2("duration_ms"),
4509
+ error: text4("error"),
4510
+ startedAt: timestamp4("started_at", { withTimezone: true }).notNull().defaultNow(),
4511
+ completedAt: timestamp4("completed_at", { withTimezone: true }),
4512
+ /** Runtime-enforced when `INTEGRATION_MULTI_TENANT` is true; see SYNC-6. */
4513
+ tenantId: text4("tenant_id")
4514
+ },
4515
+ (t) => ({
4516
+ /** Timeline read: "most recent runs for this subscription". */
4517
+ idxIntegrationRunsSubscriptionStartedAt: index3(
4518
+ "idx_integration_runs_subscription_started_at"
4519
+ ).on(t.subscriptionId, t.startedAt),
4520
+ /** Stale-run sweeper: "runs that started > N minutes ago and are still running". */
4521
+ idxIntegrationRunsStatusStartedAt: index3("idx_integration_runs_status_started_at").on(
4522
+ t.status,
4523
+ t.startedAt
4524
+ )
4525
+ })
4526
+ );
4527
+ var integrationRunItems = pgTable4(
4528
+ "integration_run_items",
4529
+ {
4530
+ id: uuid3("id").primaryKey().defaultRandom(),
4531
+ integrationRunId: uuid3("integration_run_id").notNull().references(() => integrationRuns.id, { onDelete: "cascade" }),
4532
+ entityType: text4("entity_type").notNull(),
4533
+ externalId: text4("external_id").notNull(),
4534
+ localId: text4("local_id"),
4535
+ operation: integrationRunItemOperationEnum("operation").notNull(),
4536
+ status: integrationRunItemStatusEnum("status").notNull(),
4537
+ /**
4538
+ * Structured per-field diff — ADR-0003 shape enforced by
4539
+ * `FieldDiffSchema.parse` at the recorder service layer.
4540
+ *
4541
+ * Shape: `{ [fieldName]: { from: unknown, to: unknown } }`.
4542
+ * Empty `{}` for `noop` items; `{ [field]: { from: null, to: <value> } }`
4543
+ * for created items; `{ [field]: { from: <value>, to: null } }` for
4544
+ * deleted items.
4545
+ */
4546
+ changedFields: jsonb4("changed_fields").notNull().default({}).$type(),
4547
+ title: text4("title"),
4548
+ error: text4("error"),
4549
+ createdAt: timestamp4("created_at", { withTimezone: true }).notNull().defaultNow(),
4550
+ /** Runtime-enforced when `INTEGRATION_MULTI_TENANT` is true; see SYNC-6. */
4551
+ tenantId: text4("tenant_id")
4552
+ },
4553
+ (t) => ({
4554
+ /** Ordered timeline within a run. */
4555
+ idxIntegrationRunItemsRunCreatedAt: index3("idx_integration_run_items_run_created_at").on(
4556
+ t.integrationRunId,
4557
+ t.createdAt
4558
+ ),
4559
+ /** Per-record history: "every integration that touched opportunity/$extId". */
4560
+ idxIntegrationRunItemsEntityExternal: index3(
4561
+ "idx_integration_run_items_entity_external"
4562
+ ).on(t.entityType, t.externalId)
4563
+ })
4564
+ );
4565
+
4566
+ // runtime/subsystems/integration/integration-cursor-store.memory-backend.ts
4567
+ import { Injectable as Injectable16 } from "@nestjs/common";
4568
+ var MemoryCursorStore = class {
4569
+ /**
4570
+ * Subscription-id → last persisted cursor. Public so tests can inspect
4571
+ * or pre-seed state; production callers MUST go through `get`/`put`.
4572
+ */
4573
+ cursors = /* @__PURE__ */ new Map();
4574
+ /**
4575
+ * Seedable subscription metadata for `listAll` — the memory backend
4576
+ * stores only `subscriptionId → cursor` in its write path, so the
4577
+ * snapshot shape (`connectionId`, `adapter`, `domain`, `externalRef`,
4578
+ * timestamps) has no natural source without test seeding. Tests populate
4579
+ * this map; unseeded entries get empty-string metadata and `new Date(0)`
4580
+ * timestamps so the shape stays stable. Production paths go through the
4581
+ * Drizzle backend.
4582
+ */
4583
+ subscriptions = /* @__PURE__ */ new Map();
4584
+ async get(subscriptionId, _tenantId) {
4585
+ const value = this.cursors.get(subscriptionId);
4586
+ return value === void 0 ? null : value;
4587
+ }
4588
+ async put(subscriptionId, cursor, _tenantId) {
4589
+ this.cursors.set(subscriptionId, cursor);
4590
+ }
4591
+ async listAll(_tenantId) {
4592
+ const snapshots = [];
4593
+ for (const [subscriptionId, cursor] of this.cursors.entries()) {
4594
+ const meta = this.subscriptions.get(subscriptionId);
4595
+ snapshots.push({
4596
+ subscriptionId,
4597
+ connectionId: meta?.connectionId ?? "",
4598
+ adapter: meta?.adapter ?? "",
4599
+ domain: meta?.domain ?? "",
4600
+ externalRef: meta?.externalRef ?? null,
4601
+ cursor: cursor ?? null,
4602
+ lastIntegrationAt: meta?.lastIntegrationAt ?? null,
4603
+ updatedAt: meta?.updatedAt ?? /* @__PURE__ */ new Date(0),
4604
+ tenantId: null
4605
+ });
4606
+ }
4607
+ return snapshots.sort(
4608
+ (a, b) => b.updatedAt.getTime() - a.updatedAt.getTime()
4609
+ );
4610
+ }
4611
+ /** Reset state. Tests call this in `beforeEach`. */
4612
+ clear() {
4613
+ this.cursors.clear();
4614
+ this.subscriptions.clear();
4615
+ }
4616
+ };
4617
+ MemoryCursorStore = __decorateClass([
4618
+ Injectable16()
4619
+ ], MemoryCursorStore);
4620
+
4621
+ // runtime/subsystems/integration/integration-run-recorder.memory-backend.ts
4622
+ import { Injectable as Injectable17 } from "@nestjs/common";
4623
+ var MemoryRunRecorder = class {
4624
+ /**
4625
+ * All started runs keyed by id. Public so tests can inspect lifecycle
4626
+ * transitions without poking through recording methods.
4627
+ */
4628
+ runs = /* @__PURE__ */ new Map();
4629
+ /**
4630
+ * Items keyed by `integration_run_id`, array order matches insertion order —
4631
+ * mirrors the timeline the `(integration_run_id, created_at)` index produces
4632
+ * in Postgres.
4633
+ */
4634
+ items = /* @__PURE__ */ new Map();
4635
+ /**
4636
+ * Seedable subscription metadata — tests populate this to make
4637
+ * `listRecent` return meaningful `connectionId` values. The memory
4638
+ * backend doesn't track subscriptions on its own (only runs + items), so
4639
+ * this map is the intentional extension point: tests write entries for
4640
+ * the subscription ids they use, production code never touches it.
4641
+ */
4642
+ subscriptions = /* @__PURE__ */ new Map();
4643
+ async startRun(input) {
4644
+ const id = crypto.randomUUID();
4645
+ this.runs.set(id, {
4646
+ id,
4647
+ subscriptionId: input.subscriptionId,
4648
+ direction: input.direction,
4649
+ action: input.action,
4650
+ status: "running",
4651
+ cursorBefore: input.cursorBefore ?? null,
4652
+ cursorAfter: null,
4653
+ recordsFound: 0,
4654
+ recordsProcessed: 0,
4655
+ durationMs: null,
4656
+ error: null,
4657
+ tenantId: input.tenantId ?? null,
4658
+ startedAt: /* @__PURE__ */ new Date(),
4659
+ completedAt: null
4660
+ });
4661
+ this.items.set(id, []);
4662
+ return { id };
4663
+ }
4664
+ async recordItem(input) {
4665
+ FieldDiffSchema.parse(input.changedFields);
4666
+ const bucket = this.items.get(input.integrationRunId);
4667
+ if (!bucket) {
4668
+ throw new Error(
4669
+ `MemoryRunRecorder.recordItem: no run started for id '${input.integrationRunId}'. Call startRun(...) first.`
4670
+ );
4671
+ }
4672
+ bucket.push(input);
4673
+ }
4674
+ async completeRun(runId, input) {
4675
+ const run = this.runs.get(runId);
4676
+ if (!run) {
4677
+ throw new Error(
4678
+ `MemoryRunRecorder.completeRun: no run started for id '${runId}'.`
4679
+ );
4680
+ }
4681
+ run.status = input.status;
4682
+ run.recordsFound = input.recordsFound;
4683
+ run.recordsProcessed = input.recordsProcessed;
4684
+ run.cursorAfter = input.cursorAfter ?? null;
4685
+ run.durationMs = input.durationMs;
4686
+ run.error = input.error ?? null;
4687
+ run.completedAt = /* @__PURE__ */ new Date();
4688
+ }
4689
+ async listRecent(limit, subscriptionId, _tenantId) {
4690
+ const all = Array.from(this.runs.values());
4691
+ const filtered = subscriptionId === void 0 ? all : all.filter((r) => r.subscriptionId === subscriptionId);
4692
+ return filtered.sort((a, b) => b.startedAt.getTime() - a.startedAt.getTime()).slice(0, limit).map((r) => ({
4693
+ id: r.id,
4694
+ subscriptionId: r.subscriptionId,
4695
+ // connectionId is only knowable if the test seeded subscriptions
4696
+ // metadata; empty string otherwise. The Drizzle backend resolves
4697
+ // it via JOIN, which is the production path.
4698
+ connectionId: this.subscriptions.get(r.subscriptionId)?.connectionId ?? "",
4699
+ status: r.status,
4700
+ startedAt: r.startedAt,
4701
+ completedAt: r.completedAt,
4702
+ recordsProcessed: r.recordsProcessed,
4703
+ tenantId: r.tenantId
4704
+ }));
4705
+ }
4706
+ /** Reset state. Tests call this in `beforeEach`. */
4707
+ clear() {
4708
+ this.runs.clear();
4709
+ this.items.clear();
4710
+ this.subscriptions.clear();
4711
+ }
4712
+ // ─── test ergonomics ─────────────────────────────────────────────────
4713
+ /** All runs for a subscription, newest first. Timeline reads. */
4714
+ getRunsForSubscription(subscriptionId) {
4715
+ return Array.from(this.runs.values()).filter((r) => r.subscriptionId === subscriptionId).sort((a, b) => b.startedAt.getTime() - a.startedAt.getTime());
4716
+ }
4717
+ /** All item rows for a run, insertion-ordered. */
4718
+ getItemsForRun(runId) {
4719
+ return this.items.get(runId) ?? [];
4720
+ }
4721
+ };
4722
+ MemoryRunRecorder = __decorateClass([
4723
+ Injectable17()
4724
+ ], MemoryRunRecorder);
4725
+
4726
+ // runtime/subsystems/integration/deep-equal.differ.ts
4727
+ import { Injectable as Injectable18 } from "@nestjs/common";
4728
+ var DEFAULT_IGNORE_FIELDS = /* @__PURE__ */ new Set([
4729
+ "id",
4730
+ "createdAt",
4731
+ "updatedAt",
4732
+ "deletedAt",
4733
+ "type",
4734
+ "lastModifiedAt",
4735
+ "fields",
4736
+ "external_id",
4737
+ "externalId",
4738
+ "provider",
4739
+ "provider_metadata",
4740
+ "providerMetadata"
4741
+ ]);
4742
+ var DeepEqualDiffer = class {
4743
+ ignore;
4744
+ constructor(opts = {}) {
4745
+ if (opts.ignore && opts.ignore.length > 0) {
4746
+ this.ignore = /* @__PURE__ */ new Set([...DEFAULT_IGNORE_FIELDS, ...opts.ignore]);
4747
+ } else {
4748
+ this.ignore = DEFAULT_IGNORE_FIELDS;
4749
+ }
4750
+ }
4751
+ diff(existing, incoming, providerChangedFields) {
4752
+ if (existing === null) {
4753
+ const out2 = {};
4754
+ for (const key of Object.keys(incoming)) {
4755
+ if (this.ignore.has(key)) continue;
4756
+ const value = incoming[key];
4757
+ if (value === null || value === void 0) continue;
4758
+ out2[key] = { from: null, to: value };
4759
+ }
4760
+ return Object.keys(out2).length === 0 ? "noop" : out2;
4761
+ }
4762
+ const candidates = /* @__PURE__ */ new Set();
4763
+ if (providerChangedFields && providerChangedFields.length > 0) {
4764
+ for (const key of providerChangedFields) {
4765
+ if (!this.ignore.has(key)) candidates.add(key);
4766
+ }
4767
+ } else {
4768
+ for (const key of Object.keys(incoming)) {
4769
+ if (!this.ignore.has(key)) candidates.add(key);
4770
+ }
4771
+ for (const key of Object.keys(existing)) {
4772
+ if (this.ignore.has(key)) continue;
4773
+ if (!(key in incoming)) continue;
4774
+ candidates.add(key);
4775
+ }
4776
+ }
4777
+ const out = {};
4778
+ for (const key of candidates) {
4779
+ const before = existing[key];
4780
+ const after = incoming[key];
4781
+ if (!isEqual(before, after)) {
4782
+ out[key] = { from: before ?? null, to: after ?? null };
4783
+ }
4784
+ }
4785
+ return Object.keys(out).length === 0 ? "noop" : out;
4786
+ }
4787
+ };
4788
+ DeepEqualDiffer = __decorateClass([
4789
+ Injectable18()
4790
+ ], DeepEqualDiffer);
4791
+ function isEqual(a, b) {
4792
+ if (a === b) return true;
4793
+ const na = normalize(a);
4794
+ const nb = normalize(b);
4795
+ if (na === nb) return true;
4796
+ if (typeof na === "object" && typeof nb === "object" && na !== null && nb !== null) {
4797
+ return deepEqualObject(na, nb);
4798
+ }
4799
+ const numericEqual = maybeNumericEqual(na, nb) || maybeNumericEqual(nb, na);
4800
+ return numericEqual;
4801
+ }
4802
+ function normalize(value) {
4803
+ if (value instanceof Date) return value.toISOString();
4804
+ return value;
4805
+ }
4806
+ function maybeNumericEqual(a, b) {
4807
+ if (typeof a !== "string" || typeof b !== "number") return false;
4808
+ if (a.trim() === "") return false;
4809
+ const parsed = Number(a);
4810
+ if (!Number.isFinite(parsed)) return false;
4811
+ return parsed === b;
4812
+ }
4813
+ function deepEqualObject(a, b) {
4814
+ if (Array.isArray(a) !== Array.isArray(b)) return false;
4815
+ const aKeys = Object.keys(a);
4816
+ const bKeys = Object.keys(b);
4817
+ if (aKeys.length !== bKeys.length) return false;
4818
+ for (const key of aKeys) {
4819
+ if (!(key in b)) return false;
4820
+ if (!isEqual(a[key], b[key])) return false;
4821
+ }
4822
+ return true;
4823
+ }
4824
+
4825
+ // runtime/subsystems/integration/execute-integration.use-case.ts
4826
+ import { Inject as Inject15, Injectable as Injectable19, Logger as Logger8, Optional as Optional8 } from "@nestjs/common";
4827
+ var ExecuteIntegrationUseCase = class {
4828
+ constructor(source, cursors, differ, sink, recorder, multiTenant = false) {
4829
+ this.source = source;
4830
+ this.cursors = cursors;
4831
+ this.differ = differ;
4832
+ this.sink = sink;
4833
+ this.recorder = recorder;
4834
+ this.multiTenant = multiTenant;
4835
+ }
4836
+ source;
4837
+ cursors;
4838
+ differ;
4839
+ sink;
4840
+ recorder;
4841
+ multiTenant;
4842
+ logger = new Logger8(ExecuteIntegrationUseCase.name);
4843
+ async execute(input) {
4844
+ assertTenantId(input.tenantId, {
4845
+ multiTenant: this.multiTenant,
4846
+ operation: "execute"
4847
+ });
4848
+ const source = input.sourceOverride ?? this.source;
4849
+ const startedAt = Date.now();
4850
+ const cursorBefore = await this.cursors.get(input.subscription.id, input.tenantId);
4851
+ const { id: runId } = await this.recorder.startRun({
4852
+ subscriptionId: input.subscription.id,
4853
+ direction: input.direction,
4854
+ action: input.action,
4855
+ cursorBefore,
4856
+ tenantId: input.tenantId
4857
+ });
4858
+ let recordsFound = 0;
4859
+ let recordsProcessed = 0;
4860
+ let recordsFailed = 0;
4861
+ let latestCursor = cursorBefore;
4862
+ let cursorAdvanced = false;
4863
+ let runError = null;
4864
+ let status = "no_changes";
4865
+ try {
4866
+ for await (const change of source.listChanges(input.subscription, cursorBefore)) {
4867
+ recordsFound++;
4868
+ latestCursor = change.cursor;
4869
+ cursorAdvanced = true;
4870
+ try {
4871
+ await this.processChange(runId, input, change);
4872
+ recordsProcessed++;
4873
+ } catch (err) {
4874
+ recordsFailed++;
4875
+ const message = err instanceof Error ? err.message : String(err);
4876
+ this.logger.warn(
4877
+ `integration item failed: subscription=${input.subscription.id} externalId=${change.externalId}: ${message}`
4878
+ );
4879
+ await this.recorder.recordItem({
4880
+ integrationRunId: runId,
4881
+ entityType: input.subscription.domain,
4882
+ externalId: change.externalId,
4883
+ operation: change.operation === "deleted" ? "deleted" : "updated",
4884
+ status: "failed",
4885
+ changedFields: {},
4886
+ error: message,
4887
+ tenantId: input.tenantId
4888
+ });
4889
+ }
4890
+ }
4891
+ if (recordsFailed > 0 && recordsProcessed === 0 && recordsFound > 0) {
4892
+ status = "failed";
4893
+ runError = `all ${recordsFailed} records failed`;
4894
+ } else if (recordsFound === 0) {
4895
+ status = "no_changes";
4896
+ } else {
4897
+ status = "success";
4898
+ }
4899
+ } catch (err) {
4900
+ status = "failed";
4901
+ runError = err instanceof Error ? err.message : String(err);
4902
+ this.logger.error(
4903
+ `integration source failed: subscription=${input.subscription.id}: ${runError}`
4904
+ );
4905
+ }
4906
+ if (cursorAdvanced && latestCursor !== null && latestCursor !== void 0) {
4907
+ try {
4908
+ await this.cursors.put(input.subscription.id, latestCursor, input.tenantId);
4909
+ } catch (err) {
4910
+ const message = err instanceof Error ? err.message : String(err);
4911
+ this.logger.error(
4912
+ `cursor put failed: subscription=${input.subscription.id}: ${message}`
4913
+ );
4914
+ if (status !== "failed") {
4915
+ status = "failed";
4916
+ runError = `cursor put failed: ${message}`;
4917
+ }
4918
+ }
4919
+ }
4920
+ const durationMs = Date.now() - startedAt;
4921
+ await this.recorder.completeRun(runId, {
4922
+ status,
4923
+ recordsFound,
4924
+ recordsProcessed,
4925
+ cursorAfter: cursorAdvanced ? latestCursor : cursorBefore,
4926
+ durationMs,
4927
+ error: runError
4928
+ });
4929
+ return {
4930
+ runId,
4931
+ status,
4932
+ recordsFound,
4933
+ recordsProcessed,
4934
+ recordsFailed,
4935
+ cursorBefore,
4936
+ cursorAfter: cursorAdvanced ? latestCursor : cursorBefore,
4937
+ durationMs,
4938
+ error: runError
4939
+ };
4940
+ }
4941
+ async processChange(runId, input, change) {
4942
+ if (change.operation === "deleted") {
4943
+ const result = await this.sink.softDeleteByExternalId(
4944
+ input.userId,
4945
+ change.externalId
4946
+ );
4947
+ await this.recorder.recordItem({
4948
+ integrationRunId: runId,
4949
+ entityType: input.subscription.domain,
4950
+ externalId: change.externalId,
4951
+ localId: result?.id ?? null,
4952
+ operation: result ? "deleted" : "noop",
4953
+ status: "success",
4954
+ changedFields: {},
4955
+ tenantId: input.tenantId
4956
+ });
4957
+ return;
4958
+ }
4959
+ const existing = await this.sink.findByExternalId(
4960
+ input.userId,
4961
+ change.externalId
4962
+ );
4963
+ const diff = this.differ.diff(
4964
+ existing,
4965
+ change.record,
4966
+ change.providerChangedFields
4967
+ );
4968
+ if (diff === "noop") {
4969
+ if (!this.sink.reprojectsOnNoop) {
4970
+ await this.recorder.recordItem({
4971
+ integrationRunId: runId,
4972
+ entityType: input.subscription.domain,
4973
+ externalId: change.externalId,
4974
+ localId: null,
4975
+ operation: "noop",
4976
+ status: "success",
4977
+ changedFields: {},
4978
+ tenantId: input.tenantId
4979
+ });
4980
+ return;
4981
+ }
4982
+ const { id: noopLocalId } = await this.sink.upsertByExternalId(
4983
+ input.userId,
4984
+ change.record,
4985
+ input.provider
4986
+ );
4987
+ await this.recorder.recordItem({
4988
+ integrationRunId: runId,
4989
+ entityType: input.subscription.domain,
4990
+ externalId: change.externalId,
4991
+ localId: noopLocalId,
4992
+ operation: "noop",
4993
+ status: "success",
4994
+ changedFields: {},
4995
+ tenantId: input.tenantId
4996
+ });
4997
+ return;
4998
+ }
4999
+ const { id: localId } = await this.sink.upsertByExternalId(
5000
+ input.userId,
5001
+ change.record,
5002
+ input.provider
5003
+ );
5004
+ await this.recorder.recordItem({
5005
+ integrationRunId: runId,
5006
+ entityType: input.subscription.domain,
5007
+ externalId: change.externalId,
5008
+ localId,
5009
+ operation: existing === null ? "created" : "updated",
5010
+ status: "success",
5011
+ changedFields: diff,
5012
+ tenantId: input.tenantId
5013
+ });
5014
+ }
5015
+ };
5016
+ ExecuteIntegrationUseCase = __decorateClass([
5017
+ Injectable19(),
5018
+ __decorateParam(0, Inject15(INTEGRATION_CHANGE_SOURCE)),
5019
+ __decorateParam(1, Inject15(INTEGRATION_CURSOR_STORE)),
5020
+ __decorateParam(2, Inject15(INTEGRATION_FIELD_DIFFER)),
5021
+ __decorateParam(3, Inject15(INTEGRATION_SINK)),
5022
+ __decorateParam(4, Inject15(INTEGRATION_RUN_RECORDER)),
5023
+ __decorateParam(5, Optional8()),
5024
+ __decorateParam(5, Inject15(INTEGRATION_MULTI_TENANT))
5025
+ ], ExecuteIntegrationUseCase);
5026
+
5027
+ // runtime/subsystems/integration/integration-cursor-store.drizzle-backend.ts
5028
+ import { Inject as Inject16, Injectable as Injectable20, Optional as Optional9 } from "@nestjs/common";
5029
+ import { and as and6, desc as desc5, eq as eq7 } from "drizzle-orm";
5030
+ var PostgresCursorStore = class {
5031
+ constructor(db, multiTenant) {
5032
+ this.db = db;
5033
+ this.multiTenant = multiTenant ?? false;
5034
+ }
5035
+ db;
5036
+ multiTenant;
5037
+ async get(subscriptionId, tenantId) {
5038
+ const where = this.buildWhere(subscriptionId, tenantId, "cursor.get");
5039
+ const rows = await this.db.select({ cursor: integrationSubscriptions.cursor }).from(integrationSubscriptions).where(where).limit(1);
5040
+ if (rows.length === 0) return null;
5041
+ return rows[0]?.cursor ?? null;
5042
+ }
5043
+ async put(subscriptionId, cursor, tenantId) {
5044
+ const where = this.buildWhere(subscriptionId, tenantId, "cursor.put");
5045
+ await this.db.update(integrationSubscriptions).set({
5046
+ cursor,
5047
+ lastIntegrationAt: /* @__PURE__ */ new Date(),
5048
+ updatedAt: /* @__PURE__ */ new Date()
5049
+ }).where(where);
5050
+ }
5051
+ async listAll(tenantId) {
5052
+ assertTenantId(tenantId, {
5053
+ multiTenant: this.multiTenant,
5054
+ operation: "cursor.listAll"
5055
+ });
5056
+ const where = this.multiTenant ? eq7(integrationSubscriptions.tenantId, tenantId) : void 0;
5057
+ const rows = await this.db.select({
5058
+ id: integrationSubscriptions.id,
5059
+ connectionId: integrationSubscriptions.connectionId,
5060
+ adapter: integrationSubscriptions.adapter,
5061
+ domain: integrationSubscriptions.domain,
5062
+ externalRef: integrationSubscriptions.externalRef,
5063
+ cursor: integrationSubscriptions.cursor,
5064
+ lastIntegrationAt: integrationSubscriptions.lastIntegrationAt,
5065
+ updatedAt: integrationSubscriptions.updatedAt,
5066
+ tenantId: integrationSubscriptions.tenantId
5067
+ }).from(integrationSubscriptions).where(where).orderBy(desc5(integrationSubscriptions.updatedAt));
5068
+ return rows.map((row) => ({
5069
+ subscriptionId: row.id,
5070
+ connectionId: row.connectionId,
5071
+ adapter: row.adapter,
5072
+ domain: row.domain,
5073
+ externalRef: row.externalRef,
5074
+ cursor: row.cursor ?? null,
5075
+ lastIntegrationAt: row.lastIntegrationAt,
5076
+ updatedAt: row.updatedAt,
5077
+ tenantId: row.tenantId
5078
+ }));
5079
+ }
5080
+ /**
5081
+ * Centralized WHERE clause — `get` and `put` share identical semantics.
5082
+ * Drift here would let a caller read under one tenancy rule and write
5083
+ * under another.
5084
+ */
5085
+ buildWhere(subscriptionId, tenantId, operation) {
5086
+ assertTenantId(tenantId, {
5087
+ multiTenant: this.multiTenant,
5088
+ operation
5089
+ });
5090
+ if (this.multiTenant) {
5091
+ return and6(
5092
+ eq7(integrationSubscriptions.id, subscriptionId),
5093
+ eq7(integrationSubscriptions.tenantId, tenantId)
5094
+ );
5095
+ }
5096
+ return eq7(integrationSubscriptions.id, subscriptionId);
5097
+ }
5098
+ };
5099
+ PostgresCursorStore = __decorateClass([
5100
+ Injectable20(),
5101
+ __decorateParam(0, Inject16(DRIZZLE)),
5102
+ __decorateParam(1, Optional9()),
5103
+ __decorateParam(1, Inject16(INTEGRATION_MULTI_TENANT))
5104
+ ], PostgresCursorStore);
5105
+
5106
+ // runtime/subsystems/integration/integration-run-recorder.drizzle-backend.ts
5107
+ import { Inject as Inject17, Injectable as Injectable21, Optional as Optional10 } from "@nestjs/common";
5108
+ import { and as and7, desc as desc6, eq as eq8 } from "drizzle-orm";
5109
+ var DrizzleIntegrationRunRecorder = class {
5110
+ constructor(db, multiTenant) {
5111
+ this.db = db;
5112
+ this.multiTenant = multiTenant ?? false;
5113
+ }
5114
+ db;
5115
+ multiTenant;
5116
+ async startRun(input) {
5117
+ assertTenantId(input.tenantId, {
5118
+ multiTenant: this.multiTenant,
5119
+ operation: "startRun"
5120
+ });
5121
+ const rows = await this.db.insert(integrationRuns).values({
5122
+ subscriptionId: input.subscriptionId,
5123
+ direction: input.direction,
5124
+ action: input.action,
5125
+ status: "running",
5126
+ cursorBefore: input.cursorBefore ?? null,
5127
+ tenantId: input.tenantId ?? null
5128
+ }).returning({ id: integrationRuns.id });
5129
+ const id = rows[0]?.id;
5130
+ if (!id) {
5131
+ throw new Error("DrizzleIntegrationRunRecorder: INSERT RETURNING produced no id");
5132
+ }
5133
+ return { id };
5134
+ }
5135
+ async recordItem(input) {
5136
+ assertTenantId(input.tenantId, {
5137
+ multiTenant: this.multiTenant,
5138
+ operation: "recordItem"
5139
+ });
5140
+ FieldDiffSchema.parse(input.changedFields);
5141
+ await this.db.insert(integrationRunItems).values({
5142
+ integrationRunId: input.integrationRunId,
5143
+ entityType: input.entityType,
5144
+ externalId: input.externalId,
5145
+ localId: input.localId ?? null,
5146
+ operation: input.operation,
5147
+ status: input.status,
5148
+ changedFields: input.changedFields,
5149
+ title: input.title ?? null,
5150
+ error: input.error ?? null,
5151
+ tenantId: input.tenantId ?? null
5152
+ });
5153
+ }
5154
+ async listRecent(limit, subscriptionId, tenantId) {
5155
+ assertTenantId(tenantId, {
5156
+ multiTenant: this.multiTenant,
5157
+ operation: "listRecent"
5158
+ });
5159
+ const conditions = [];
5160
+ if (subscriptionId !== void 0) {
5161
+ conditions.push(eq8(integrationRuns.subscriptionId, subscriptionId));
5162
+ }
5163
+ if (this.multiTenant) {
5164
+ conditions.push(eq8(integrationRuns.tenantId, tenantId));
5165
+ }
5166
+ const where = conditions.length === 0 ? void 0 : conditions.length === 1 ? conditions[0] : and7(...conditions);
5167
+ const rows = await this.db.select({
5168
+ id: integrationRuns.id,
5169
+ subscriptionId: integrationRuns.subscriptionId,
5170
+ connectionId: integrationSubscriptions.connectionId,
5171
+ status: integrationRuns.status,
5172
+ startedAt: integrationRuns.startedAt,
5173
+ completedAt: integrationRuns.completedAt,
5174
+ recordsProcessed: integrationRuns.recordsProcessed,
5175
+ tenantId: integrationRuns.tenantId
5176
+ }).from(integrationRuns).innerJoin(
5177
+ integrationSubscriptions,
5178
+ eq8(integrationRuns.subscriptionId, integrationSubscriptions.id)
5179
+ ).where(where).orderBy(desc6(integrationRuns.startedAt)).limit(limit);
5180
+ return rows.map((row) => ({
5181
+ id: row.id,
5182
+ subscriptionId: row.subscriptionId,
5183
+ connectionId: row.connectionId,
5184
+ status: row.status,
5185
+ startedAt: row.startedAt,
5186
+ completedAt: row.completedAt,
5187
+ recordsProcessed: row.recordsProcessed,
5188
+ tenantId: row.tenantId
5189
+ }));
5190
+ }
5191
+ async completeRun(runId, input) {
5192
+ await this.db.update(integrationRuns).set({
5193
+ status: input.status,
5194
+ recordsFound: input.recordsFound,
5195
+ recordsProcessed: input.recordsProcessed,
5196
+ cursorAfter: input.cursorAfter ?? null,
5197
+ durationMs: input.durationMs,
5198
+ error: input.error ?? null,
5199
+ completedAt: /* @__PURE__ */ new Date()
5200
+ }).where(eq8(integrationRuns.id, runId));
5201
+ }
5202
+ };
5203
+ DrizzleIntegrationRunRecorder = __decorateClass([
5204
+ Injectable21(),
5205
+ __decorateParam(0, Inject17(DRIZZLE)),
5206
+ __decorateParam(1, Optional10()),
5207
+ __decorateParam(1, Inject17(INTEGRATION_MULTI_TENANT))
5208
+ ], DrizzleIntegrationRunRecorder);
5209
+
5210
+ // runtime/subsystems/integration/integration.module.ts
5211
+ import { Module as Module7 } from "@nestjs/common";
5212
+ var IntegrationModule = class {
5213
+ static forRoot(options) {
5214
+ const multiTenant = options.multiTenant ?? false;
5215
+ const sharedProviders = [
5216
+ { provide: INTEGRATION_MODULE_OPTIONS, useValue: options },
5217
+ { provide: INTEGRATION_MULTI_TENANT, useValue: multiTenant },
5218
+ // Default differ — consumers can override by binding a different
5219
+ // `IFieldDiffer<T>` to `INTEGRATION_FIELD_DIFFER` in their feature module.
5220
+ { provide: INTEGRATION_FIELD_DIFFER, useValue: new DeepEqualDiffer() }
5221
+ ];
5222
+ const backendProviders = options.backend === "memory" ? [
5223
+ // Wired as singletons via `useValue` so tests can pull
5224
+ // them out via `moduleRef.get(MemoryCursorStore)` for
5225
+ // direct assertions. Matches JOB-4 / MemoryJobStore shape.
5226
+ { provide: MemoryCursorStore, useValue: new MemoryCursorStore() },
5227
+ {
5228
+ provide: INTEGRATION_CURSOR_STORE,
5229
+ useExisting: MemoryCursorStore
5230
+ },
5231
+ { provide: MemoryRunRecorder, useValue: new MemoryRunRecorder() },
5232
+ {
5233
+ provide: INTEGRATION_RUN_RECORDER,
5234
+ useExisting: MemoryRunRecorder
5235
+ }
5236
+ ] : [
5237
+ // Drizzle backends — injected with DRIZZLE (provided by the
5238
+ // consumer's DrizzleModule) + the INTEGRATION_MULTI_TENANT flag
5239
+ // we bound above.
5240
+ { provide: INTEGRATION_CURSOR_STORE, useClass: PostgresCursorStore },
5241
+ { provide: INTEGRATION_RUN_RECORDER, useClass: DrizzleIntegrationRunRecorder }
5242
+ ];
5243
+ return {
5244
+ module: IntegrationModule,
5245
+ global: true,
5246
+ providers: [...sharedProviders, ...backendProviders],
5247
+ exports: [
5248
+ INTEGRATION_MODULE_OPTIONS,
5249
+ INTEGRATION_MULTI_TENANT,
5250
+ INTEGRATION_FIELD_DIFFER,
5251
+ INTEGRATION_CURSOR_STORE,
5252
+ INTEGRATION_RUN_RECORDER
5253
+ ]
5254
+ };
5255
+ }
5256
+ };
5257
+ IntegrationModule = __decorateClass([
5258
+ Module7({})
5259
+ ], IntegrationModule);
5260
+
4290
5261
  // runtime/subsystems/auth/protocols/oauth-state-store.ts
4291
5262
  var OAuthStateError = class extends Error {
4292
5263
  constructor(message, reason) {
@@ -4443,12 +5414,12 @@ async function withAuthRetry(authStrategy, connectionId, op, options = {}) {
4443
5414
  }
4444
5415
 
4445
5416
  // runtime/subsystems/auth/auth-oauth-state.schema.ts
4446
- import { pgTable as pgTable4, text as text4, timestamp as timestamp4 } from "drizzle-orm/pg-core";
4447
- var authOAuthState = pgTable4("auth_oauth_state", {
4448
- state: text4("state").primaryKey(),
4449
- userId: text4("user_id").notNull(),
4450
- redirect: text4("redirect"),
4451
- expiresAt: timestamp4("expires_at", { withTimezone: true }).notNull()
5417
+ import { pgTable as pgTable5, text as text5, timestamp as timestamp5 } from "drizzle-orm/pg-core";
5418
+ var authOAuthState = pgTable5("auth_oauth_state", {
5419
+ state: text5("state").primaryKey(),
5420
+ userId: text5("user_id").notNull(),
5421
+ redirect: text5("redirect"),
5422
+ expiresAt: timestamp5("expires_at", { withTimezone: true }).notNull()
4452
5423
  });
4453
5424
 
4454
5425
  // runtime/subsystems/auth/backends/encryption-key/env.ts
@@ -4539,7 +5510,7 @@ var MemoryOAuthStateStore = class {
4539
5510
 
4540
5511
  // runtime/subsystems/auth/backends/state-store.drizzle-backend.ts
4541
5512
  import { randomBytes as randomBytes3 } from "crypto";
4542
- import { eq as eq7 } from "drizzle-orm";
5513
+ import { eq as eq9 } from "drizzle-orm";
4543
5514
  var DrizzleOAuthStateStore = class {
4544
5515
  constructor(db, opts = {}) {
4545
5516
  this.db = db;
@@ -4563,7 +5534,7 @@ var DrizzleOAuthStateStore = class {
4563
5534
  return state;
4564
5535
  }
4565
5536
  async consume(state) {
4566
- const rows = await this.db.delete(authOAuthState).where(eq7(authOAuthState.state, state)).returning();
5537
+ const rows = await this.db.delete(authOAuthState).where(eq9(authOAuthState.state, state)).returning();
4567
5538
  const row = rows[0];
4568
5539
  if (!row) {
4569
5540
  throw new OAuthStateError(
@@ -4585,7 +5556,7 @@ var DrizzleOAuthStateStore = class {
4585
5556
  import {
4586
5557
  Controller,
4587
5558
  Get,
4588
- Inject as Inject15,
5559
+ Inject as Inject18,
4589
5560
  Param,
4590
5561
  Query,
4591
5562
  Req,
@@ -4687,11 +5658,11 @@ __decorateClass([
4687
5658
  ], AuthController.prototype, "callback", 1);
4688
5659
  AuthController = __decorateClass([
4689
5660
  Controller("auth"),
4690
- __decorateParam(0, Inject15(STRATEGY_REGISTRY)),
4691
- __decorateParam(1, Inject15(AUTH_USER_CONTEXT)),
4692
- __decorateParam(2, Inject15(OAUTH_STATE_STORE)),
4693
- __decorateParam(3, Inject15(AUTH_CONNECTION_GRANT_SINK)),
4694
- __decorateParam(4, Inject15(AUTH_OPTIONS))
5661
+ __decorateParam(0, Inject18(STRATEGY_REGISTRY)),
5662
+ __decorateParam(1, Inject18(AUTH_USER_CONTEXT)),
5663
+ __decorateParam(2, Inject18(OAUTH_STATE_STORE)),
5664
+ __decorateParam(3, Inject18(AUTH_CONNECTION_GRANT_SINK)),
5665
+ __decorateParam(4, Inject18(AUTH_OPTIONS))
4695
5666
  ], AuthController);
4696
5667
 
4697
5668
  // runtime/base-classes/tenant-context.ts
@@ -4699,7 +5670,7 @@ import { AsyncLocalStorage } from "async_hooks";
4699
5670
  var als = new AsyncLocalStorage();
4700
5671
 
4701
5672
  // runtime/subsystems/auth/auth.module.ts
4702
- import { Module as Module7 } from "@nestjs/common";
5673
+ import { Module as Module8 } from "@nestjs/common";
4703
5674
  function resolveEncryptionKeyProvider(choice) {
4704
5675
  if (choice === "env") {
4705
5676
  return { provide: ENCRYPTION_KEY, useClass: EnvEncryptionKey };
@@ -4759,7 +5730,7 @@ var AuthModule = class {
4759
5730
  }
4760
5731
  };
4761
5732
  AuthModule = __decorateClass([
4762
- Module7({})
5733
+ Module8({})
4763
5734
  ], AuthModule);
4764
5735
  export {
4765
5736
  AUTH_CONNECTION_GRANT_SINK,
@@ -4776,11 +5747,13 @@ export {
4776
5747
  DrizzleEventBus,
4777
5748
  DrizzleOAuthStateStore,
4778
5749
  ENCRYPTION_KEY,
5750
+ ENTITY_CHANGE_SOURCE_REGISTRY,
4779
5751
  EVENT_BUS,
4780
5752
  EnvEncryptionKey,
4781
5753
  EventsModule,
4782
5754
  LocalStorageBackend,
4783
5755
  MemoryCacheService,
5756
+ MemoryEntityChangeSourceRegistry,
4784
5757
  MemoryEventBus,
4785
5758
  MemoryOAuthStateStore,
4786
5759
  MemoryStorageBackend,
@@ -4795,6 +5768,7 @@ export {
4795
5768
  STRATEGY_REGISTRY,
4796
5769
  SessionExpiredError,
4797
5770
  StorageModule,
5771
+ UnknownEntityError,
4798
5772
  authOAuthState,
4799
5773
  collisionModeEnum,
4800
5774
  isSessionExpiredError,