@pattern-stack/codegen 0.16.0 → 0.17.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 (112) hide show
  1. package/CHANGELOG.md +93 -0
  2. package/consumer-skills/entities/families-and-queries.md +5 -3
  3. package/consumer-skills/integration/change-sources-and-sinks.md +1 -1
  4. package/dist/{chunk-3RWMQC3K.js → chunk-3MAZ4TQH.js} +12 -12
  5. package/dist/{chunk-VNBC3VXM.js → chunk-3VEVGL74.js} +4 -4
  6. package/dist/{chunk-WWGYCIJX.js → chunk-43SBT72G.js} +2 -2
  7. package/dist/{chunk-Y7GDG744.js → chunk-4GLNY5V6.js} +5 -5
  8. package/dist/{chunk-BK5ICA2F.js → chunk-4MVGAMUA.js} +4 -4
  9. package/dist/{chunk-3NMCDN7L.js → chunk-5TK7MEN4.js} +2 -2
  10. package/dist/chunk-5TK7MEN4.js.map +1 -0
  11. package/dist/{chunk-T6SCOJF4.js → chunk-7LKAMLV4.js} +4 -4
  12. package/dist/{chunk-BHZP6LOV.js → chunk-CDLWYZVQ.js} +7 -7
  13. package/dist/{chunk-DKKFTHHI.js → chunk-CZQUOIDY.js} +4 -4
  14. package/dist/{chunk-XWBK3XJK.js → chunk-DCCZB4UC.js} +4 -4
  15. package/dist/{chunk-EBKVKN75.js → chunk-DTXH24LR.js} +2 -2
  16. package/dist/{chunk-RUYLXR5F.js → chunk-GJDEPTPY.js} +10 -10
  17. package/dist/{chunk-32DOFN3T.js → chunk-IOQMMH6C.js} +17 -7
  18. package/dist/{chunk-32DOFN3T.js.map → chunk-IOQMMH6C.js.map} +1 -1
  19. package/dist/{chunk-BOPZWRJK.js → chunk-JYBFPNBJ.js} +8 -8
  20. package/dist/chunk-JYBFPNBJ.js.map +1 -0
  21. package/dist/{chunk-KSTZIULO.js → chunk-K2I6XIK5.js} +4 -4
  22. package/dist/{chunk-CEWLVVAH.js → chunk-L3VJ47BU.js} +5 -5
  23. package/dist/chunk-MKWQKKK7.js +72 -0
  24. package/dist/chunk-MKWQKKK7.js.map +1 -0
  25. package/dist/{chunk-DRCLNYH7.js → chunk-NXNVTXKG.js} +4 -4
  26. package/dist/{chunk-TDEHU73T.js → chunk-OGIZXGPY.js} +4 -4
  27. package/dist/{chunk-XDIIVIIK.js → chunk-OITTYGJS.js} +4 -4
  28. package/dist/{chunk-24WXSC3C.js → chunk-P3AYBRP6.js} +7 -7
  29. package/dist/{chunk-EJBK7I4F.js → chunk-RHYNACZS.js} +3 -3
  30. package/dist/{chunk-YK5JEVLX.js → chunk-SR7F3TJY.js} +4 -4
  31. package/dist/{chunk-YLPAPPLW.js → chunk-TIZXQU26.js} +36 -9
  32. package/dist/chunk-TIZXQU26.js.map +1 -0
  33. package/dist/{chunk-4PFF3ED4.js → chunk-UTNWFHJF.js} +4 -4
  34. package/dist/{chunk-LQ6PYFU6.js → chunk-Z7PQCAVK.js} +4 -4
  35. package/dist/runtime/base-classes/activity-entity-repository.d.ts +39 -7
  36. package/dist/runtime/base-classes/activity-entity-repository.js +1 -1
  37. package/dist/runtime/base-classes/activity-entity-service.d.ts +12 -10
  38. package/dist/runtime/base-classes/activity-entity-service.js +1 -1
  39. package/dist/runtime/base-classes/index.js +23 -23
  40. package/dist/runtime/shared/openapi/index.js +7 -7
  41. package/dist/runtime/shared/openapi/registry.js +2 -2
  42. package/dist/runtime/subsystems/analytics/analytics.module.js +2 -2
  43. package/dist/runtime/subsystems/analytics/index.js +4 -4
  44. package/dist/runtime/subsystems/auth/auth.module.js +3 -3
  45. package/dist/runtime/subsystems/auth/index.js +7 -7
  46. package/dist/runtime/subsystems/bridge/bridge-delivery-handler.js +2 -2
  47. package/dist/runtime/subsystems/bridge/bridge-delivery.drizzle-backend.js +2 -2
  48. package/dist/runtime/subsystems/bridge/bridge-outbox-drain-hook.js +4 -4
  49. package/dist/runtime/subsystems/bridge/bridge.module.js +15 -15
  50. package/dist/runtime/subsystems/bridge/index.js +17 -17
  51. package/dist/runtime/subsystems/cache/cache.module.js +1 -1
  52. package/dist/runtime/subsystems/cache/index.js +3 -3
  53. package/dist/runtime/subsystems/events/event-bus.drizzle-backend.js +2 -2
  54. package/dist/runtime/subsystems/events/event-bus.memory-backend.js +2 -2
  55. package/dist/runtime/subsystems/events/events.module.js +4 -4
  56. package/dist/runtime/subsystems/events/index.js +4 -4
  57. package/dist/runtime/subsystems/index.js +107 -107
  58. package/dist/runtime/subsystems/integration/build-change-source.js +2 -2
  59. package/dist/runtime/subsystems/integration/detection-config.schema.d.ts +23 -15
  60. package/dist/runtime/subsystems/integration/detection-config.schema.js +1 -1
  61. package/dist/runtime/subsystems/integration/execute-integration.use-case.js +2 -2
  62. package/dist/runtime/subsystems/integration/index.js +43 -43
  63. package/dist/runtime/subsystems/integration/integration-cursor-store.drizzle-backend.js +2 -2
  64. package/dist/runtime/subsystems/integration/integration-run-recorder.drizzle-backend.js +2 -2
  65. package/dist/runtime/subsystems/integration/integration.module.js +5 -5
  66. package/dist/runtime/subsystems/integration/webhook-change-source.d.ts +36 -6
  67. package/dist/runtime/subsystems/integration/webhook-change-source.js +1 -1
  68. package/dist/runtime/subsystems/jobs/index.js +30 -30
  69. package/dist/runtime/subsystems/jobs/job-orchestrator.bullmq-backend.js +4 -4
  70. package/dist/runtime/subsystems/jobs/job-orchestrator.memory-backend.js +1 -1
  71. package/dist/runtime/subsystems/jobs/job-run-service.drizzle-backend.js +2 -2
  72. package/dist/runtime/subsystems/jobs/job-run-service.memory-backend.js +2 -2
  73. package/dist/runtime/subsystems/jobs/job-worker.js +2 -2
  74. package/dist/runtime/subsystems/jobs/job-worker.module.js +10 -10
  75. package/dist/runtime/subsystems/jobs/jobs-domain.module.js +8 -8
  76. package/dist/runtime/subsystems/storage/index.js +4 -4
  77. package/dist/runtime/subsystems/storage/storage.module.js +2 -2
  78. package/dist/src/cli/index.js +15 -15
  79. package/dist/src/index.d.ts +34 -19
  80. package/dist/src/index.js +14 -14
  81. package/package.json +2 -1
  82. package/runtime/base-classes/activity-entity-repository.ts +72 -13
  83. package/runtime/base-classes/activity-entity-service.ts +14 -12
  84. package/runtime/subsystems/integration/detection-config.schema.ts +64 -54
  85. package/runtime/subsystems/integration/webhook-change-source.ts +187 -133
  86. package/src/patterns/library/activity.pattern.ts +40 -10
  87. package/dist/chunk-3NMCDN7L.js.map +0 -1
  88. package/dist/chunk-BOPZWRJK.js.map +0 -1
  89. package/dist/chunk-XCEI7NUH.js +0 -41
  90. package/dist/chunk-XCEI7NUH.js.map +0 -1
  91. package/dist/chunk-YLPAPPLW.js.map +0 -1
  92. /package/dist/{chunk-3RWMQC3K.js.map → chunk-3MAZ4TQH.js.map} +0 -0
  93. /package/dist/{chunk-VNBC3VXM.js.map → chunk-3VEVGL74.js.map} +0 -0
  94. /package/dist/{chunk-WWGYCIJX.js.map → chunk-43SBT72G.js.map} +0 -0
  95. /package/dist/{chunk-Y7GDG744.js.map → chunk-4GLNY5V6.js.map} +0 -0
  96. /package/dist/{chunk-BK5ICA2F.js.map → chunk-4MVGAMUA.js.map} +0 -0
  97. /package/dist/{chunk-T6SCOJF4.js.map → chunk-7LKAMLV4.js.map} +0 -0
  98. /package/dist/{chunk-BHZP6LOV.js.map → chunk-CDLWYZVQ.js.map} +0 -0
  99. /package/dist/{chunk-DKKFTHHI.js.map → chunk-CZQUOIDY.js.map} +0 -0
  100. /package/dist/{chunk-XWBK3XJK.js.map → chunk-DCCZB4UC.js.map} +0 -0
  101. /package/dist/{chunk-EBKVKN75.js.map → chunk-DTXH24LR.js.map} +0 -0
  102. /package/dist/{chunk-RUYLXR5F.js.map → chunk-GJDEPTPY.js.map} +0 -0
  103. /package/dist/{chunk-KSTZIULO.js.map → chunk-K2I6XIK5.js.map} +0 -0
  104. /package/dist/{chunk-CEWLVVAH.js.map → chunk-L3VJ47BU.js.map} +0 -0
  105. /package/dist/{chunk-DRCLNYH7.js.map → chunk-NXNVTXKG.js.map} +0 -0
  106. /package/dist/{chunk-TDEHU73T.js.map → chunk-OGIZXGPY.js.map} +0 -0
  107. /package/dist/{chunk-XDIIVIIK.js.map → chunk-OITTYGJS.js.map} +0 -0
  108. /package/dist/{chunk-24WXSC3C.js.map → chunk-P3AYBRP6.js.map} +0 -0
  109. /package/dist/{chunk-EJBK7I4F.js.map → chunk-RHYNACZS.js.map} +0 -0
  110. /package/dist/{chunk-YK5JEVLX.js.map → chunk-SR7F3TJY.js.map} +0 -0
  111. /package/dist/{chunk-4PFF3ED4.js.map → chunk-UTNWFHJF.js.map} +0 -0
  112. /package/dist/{chunk-LQ6PYFU6.js.map → chunk-Z7PQCAVK.js.map} +0 -0
@@ -1,17 +1,46 @@
1
1
  /**
2
- * ActivityPattern — replaces `family: activity`.
2
+ * ActivityPattern — config-driven subject-scoped interaction base.
3
3
  *
4
- * Activity entities represent time-bounded interactions (calls, meetings,
5
- * emails). The base repository/service expose date-range + opportunity +
6
- * user-scoped lookups on top of the standard CRUD methods.
4
+ * Activity entities represent interactions (calls, meetings, emails, messages,
5
+ * transcripts) that reference a *subject* the thing the interaction is about.
6
+ * Which subject is a per-entity fact, not a library constant: a CRM activity is
7
+ * scoped to an `opportunity`, a swe-brain interaction to a `person` (later
8
+ * `repo`/`team`, ADR-0006's Salesforce Activities-vs-Records shape). The base
9
+ * repository/service therefore expose **generic** subject-scoped finders
10
+ * (`findBySubjectId` / `findRecentBySubjectId`) that read the subject FK column
11
+ * from the entity's `config:` block, on top of the standard CRUD methods plus
12
+ * date-range and actor (`user_id`) scoping.
7
13
  *
8
- * Class names, import paths, and inherited-method strings match the
9
- * legacy `FAMILY_MAP` entry verbatim so PATTERN-5's template swap produces
10
- * byte-identical output.
14
+ * The subject FK column resolves from `config: { Activity: { ... } }`:
15
+ * - `subjectColumn` explicit snake_case column, OR
16
+ * - `<subject>_id` — derived from the `subject` entity name.
17
+ * The recency-ordering column is `occurredAt` (snake_case in config), default
18
+ * `occurred_at`. The base reads these via `this.patternConfig` — the same
19
+ * ADR-031 §4 hand-off `IntegratedEntityRepository` uses for `integrationConfig`.
20
+ *
21
+ * See `docs/specs/ACTIVITY-SUBJECT-1.md`.
11
22
  */
12
23
 
24
+ import { z } from 'zod';
13
25
  import { definePattern } from '../pattern-definition.js';
14
26
 
27
+ /**
28
+ * Per-entity `config: { Activity: {...} }` block, validated at parse time.
29
+ * All fields optional — a date/user-only Activity entity supplies no config
30
+ * (and the subject finders throw if called). `.strict()` rejects misspelled
31
+ * keys loudly, matching JunctionPattern.
32
+ */
33
+ const ActivityPatternConfigSchema = z
34
+ .object({
35
+ /** Subject entity name → derives the FK column `<subject>_id`. */
36
+ subject: z.string().optional(),
37
+ /** Explicit snake_case FK column, when it does not follow `<subject>_id`. */
38
+ subjectColumn: z.string().optional(),
39
+ /** snake_case recency-ordering column; defaults to `occurred_at`. */
40
+ occurredAt: z.string().optional(),
41
+ })
42
+ .strict();
43
+
15
44
  export const ActivityPattern = definePattern({
16
45
  name: 'Activity',
17
46
  extends: ['Base'],
@@ -19,14 +48,15 @@ export const ActivityPattern = definePattern({
19
48
  serviceClass: 'ActivityEntityService',
20
49
  repositoryImport: '@shared/base-classes/activity-entity-repository',
21
50
  serviceImport: '@shared/base-classes/activity-entity-service',
51
+ configSchema: ActivityPatternConfigSchema,
22
52
  repositoryInheritedMethods: [
23
53
  'findById, findByIds, list, count, exists, create, update, delete, upsertMany',
24
- 'findByDateRange, findByUserId, findByOpportunityId, findRecentByOpportunityId',
54
+ 'findByDateRange, findByUserId, findBySubjectId, findRecentBySubjectId',
25
55
  ],
26
56
  serviceInheritedMethods: [
27
57
  'findById, findByIds, list, count, exists, create, update, delete',
28
- 'findByDateRange, findByUserId, findByOpportunityId, findRecentByOpportunityId',
58
+ 'findByDateRange, findByUser, findBySubject, findRecent',
29
59
  ],
30
60
  description:
31
- 'Time-bounded interaction entities — date-range + opportunity scoped lookups',
61
+ 'Subject-scoped interaction entities — date-range + actor + config-driven subject lookups',
32
62
  });
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../runtime/subsystems/integration/detection-config.schema.ts"],"sourcesContent":["/**\n * Integration subsystem — DetectionConfig schema (#226-1)\n *\n * Canonical Zod schema for per-entity integration detection config. The schema is\n * the single source of truth for filter/mapping shape and is consumed by:\n *\n * 1. Runtime primitives — `PollChangeSource<T>`, `WebhookChangeSource<T>`\n * (#226-3, #226-4) accept a parsed `DetectionConfig` at construction.\n * 2. Codegen — `src/schema/entity-definition.schema.ts` (#226-6) imports\n * this schema so per-entity YAML `detection:` blocks validate against\n * the same shape the runtime enforces.\n *\n * Locked decisions (see ADR-033 + decision memo Q1–Q6):\n * - Filter vocabulary is flat AND of `{ field, op, value }` triples; richer\n * boolean expressions (OR / NOT / nested) are deferred per epic open Q3.\n * - Cursor strategy is a tagged union over the four shapes the three modes\n * need (`systemModstamp`, `replayId`, `timestamp`, `eventId`). Each\n * strategy types its cursor internally; the orchestrator persists what\n * the iterator last yielded (integration skill rule 2).\n * - `mode: 'poll'` may opt into `provenance: 'cdc'` so Stripe-style event\n * endpoints (mechanically a poll, semantically CDC) reuse the poll\n * primitive while emitting `Change<T>.source = 'cdc'`. Long-lived\n * streaming CDC (SFDC Pub-Sub, Debezium) is a separate primitive\n * deferred to #226-8.\n * - `webhook` mode requires `eventIdField` so `WebhookChangeSource<T>`\n * can populate `Change<T>.dedupKey` from the inbound staging row.\n */\nimport { z } from 'zod';\n\n// ============================================================================\n// Field mapping — provider field → canonical target\n// ============================================================================\n\n/**\n * Maps a single provider field onto the canonical record. `transform` is an\n * opt-in tag the adapter callback may inspect (`date-iso`, `decimal-string`,\n * etc.); the schema does not enumerate transforms — adapters interpret them.\n */\nexport const FieldMappingSchema = z.object({\n source: z.string().min(1),\n target: z.string().min(1),\n transform: z.string().min(1).optional(),\n});\n\nexport type FieldMapping = z.infer<typeof FieldMappingSchema>;\n\n// ============================================================================\n// Resolved filter — flat-AND triple\n// ============================================================================\n\n/**\n * A single resolved filter clause applied at fetch time. `value` is `unknown`\n * to admit primitives, arrays (for `in` / `nin`), and dates as ISO strings —\n * adapters interpret per provider.\n */\nexport const ResolvedFilterSchema = z.object({\n field: z.string().min(1),\n op: z.enum(['eq', 'neq', 'in', 'nin', 'gt', 'gte', 'lt', 'lte']),\n value: z.unknown(),\n});\n\nexport type ResolvedFilter = z.infer<typeof ResolvedFilterSchema>;\n\n// ============================================================================\n// Cursor strategy — tagged union over the four shapes the modes need\n// ============================================================================\n\nconst SystemModstampCursorSchema = z.object({\n kind: z.literal('systemModstamp'),\n field: z.string().min(1),\n});\n\nconst ReplayIdCursorSchema = z.object({\n kind: z.literal('replayId'),\n field: z.string().min(1),\n});\n\nconst TimestampCursorSchema = z.object({\n kind: z.literal('timestamp'),\n field: z.string().min(1),\n});\n\nconst EventIdCursorSchema = z.object({\n kind: z.literal('eventId'),\n field: z.string().min(1),\n});\n\n/**\n * Gmail `historyId` (RFC-0003 §3) — an opaque, atomic vendor token. The next\n * watermark only exists at end-of-walk; there is no resumable mid-walk value.\n * `field` is metadata for codegen/adapters (the response key the token lives on).\n */\nconst HistoryIdCursorSchema = z.object({\n kind: z.literal('historyId'),\n field: z.string().min(1),\n});\n\n/**\n * Google Calendar `syncToken` (RFC-0003 §3) — an opaque, atomic sync token,\n * same divisibility profile as `historyId`.\n */\nconst SyncTokenCursorSchema = z.object({\n kind: z.literal('syncToken'),\n field: z.string().min(1),\n});\n\nexport const CursorStrategySchema = z.discriminatedUnion('kind', [\n SystemModstampCursorSchema,\n ReplayIdCursorSchema,\n TimestampCursorSchema,\n EventIdCursorSchema,\n HistoryIdCursorSchema,\n SyncTokenCursorSchema,\n]);\n\nexport type CursorStrategy = z.infer<typeof CursorStrategySchema>;\n\n// ============================================================================\n// Cursor divisibility (RFC-0003 §3)\n// ============================================================================\n\n/**\n * Whether a cursor strategy is *divisible* — a property of the strategy, not\n * the read primitive. Divisible cursors are sortable/monotonic watermarks whose\n * value is meaningful AS OF any single record (HubSpot `systemModstamp`, a\n * `timestamp` field, a Salesforce CDC `replayId`); the read primitive may\n * checkpoint per-ref mid-walk, so a crash resumes from the last delivered ref.\n *\n * Atomic cursors are opaque vendor tokens (Gmail `historyId`, Calendar\n * `syncToken`, a generic `eventId`) whose next value only exists at end-of-walk.\n * The primitive must withhold per-ref cursors and emit the token only at a safe\n * boundary, so an interrupted run never persists an unresumable mid-walk token\n * (it resumes all-or-nothing from the prior token — see `IncrementalReadBase`).\n *\n * `eventId` is classified atomic conservatively: a generic opaque id is treated\n * all-or-nothing unless a concrete strategy proves it monotonically resumable.\n */\nexport const CURSOR_DIVISIBILITY: Readonly<Record<CursorStrategy['kind'], boolean>> = {\n systemModstamp: true,\n timestamp: true,\n replayId: true,\n eventId: false,\n historyId: false,\n syncToken: false,\n};\n\n/** Predicate form of {@link CURSOR_DIVISIBILITY}. */\nexport function isDivisibleCursor(kind: CursorStrategy['kind']): boolean {\n return CURSOR_DIVISIBILITY[kind];\n}\n\n// ============================================================================\n// Mode-specific blocks\n// ============================================================================\n\n/**\n * Poll-mode block. `provenance: 'cdc'` opts the poll primitive into stamping\n * `Change<T>.source = 'cdc'` and populating `dedupKey` from the cursor's\n * `field` — used for Stripe-style event endpoints. Defaults to `'poll'`.\n */\nexport const PollDetectionSchema = z.object({\n cursor: CursorStrategySchema,\n provenance: z.enum(['poll', 'cdc']).optional(),\n});\n\nexport type PollDetection = z.infer<typeof PollDetectionSchema>;\n\n/**\n * Webhook-mode block. `eventIdField` names the column in the consumer-owned\n * inbound staging row that `WebhookChangeSource<T>` reads to set\n * `Change<T>.dedupKey`.\n */\nexport const WebhookDetectionSchema = z.object({\n eventIdField: z.string().min(1),\n});\n\nexport type WebhookDetection = z.infer<typeof WebhookDetectionSchema>;\n\n// ============================================================================\n// DetectionConfig — top-level discriminated union over `mode`\n// ============================================================================\n\nconst PollModeSchema = z.object({\n mode: z.literal('poll'),\n poll: PollDetectionSchema,\n mapping: z.array(FieldMappingSchema).min(1),\n filters: z.array(ResolvedFilterSchema).default([]),\n});\n\nconst WebhookModeSchema = z.object({\n mode: z.literal('webhook'),\n webhook: WebhookDetectionSchema,\n mapping: z.array(FieldMappingSchema).min(1),\n filters: z.array(ResolvedFilterSchema).default([]),\n});\n\n/**\n * Top-level detection config. Discriminated on `mode` so the relevant\n * mode-block (poll/webhook) is structurally required for that mode. CDC as a\n * long-lived streaming primitive is deferred (#226-8); CDC-as-provenance\n * (Stripe-style event endpoints) is expressed via `mode: 'poll'` with\n * `poll.provenance: 'cdc'`.\n */\nexport const DetectionConfigSchema = z.discriminatedUnion('mode', [\n PollModeSchema,\n WebhookModeSchema,\n]);\n\nexport type DetectionConfig = z.infer<typeof DetectionConfigSchema>;\n"],"mappings":";AA2BA,SAAS,SAAS;AAWX,IAAM,qBAAqB,EAAE,OAAO;AAAA,EACzC,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACxB,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACxB,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AACxC,CAAC;AAaM,IAAM,uBAAuB,EAAE,OAAO;AAAA,EAC3C,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACvB,IAAI,EAAE,KAAK,CAAC,MAAM,OAAO,MAAM,OAAO,MAAM,OAAO,MAAM,KAAK,CAAC;AAAA,EAC/D,OAAO,EAAE,QAAQ;AACnB,CAAC;AAQD,IAAM,6BAA6B,EAAE,OAAO;AAAA,EAC1C,MAAM,EAAE,QAAQ,gBAAgB;AAAA,EAChC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC;AACzB,CAAC;AAED,IAAM,uBAAuB,EAAE,OAAO;AAAA,EACpC,MAAM,EAAE,QAAQ,UAAU;AAAA,EAC1B,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC;AACzB,CAAC;AAED,IAAM,wBAAwB,EAAE,OAAO;AAAA,EACrC,MAAM,EAAE,QAAQ,WAAW;AAAA,EAC3B,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC;AACzB,CAAC;AAED,IAAM,sBAAsB,EAAE,OAAO;AAAA,EACnC,MAAM,EAAE,QAAQ,SAAS;AAAA,EACzB,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC;AACzB,CAAC;AAOD,IAAM,wBAAwB,EAAE,OAAO;AAAA,EACrC,MAAM,EAAE,QAAQ,WAAW;AAAA,EAC3B,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC;AACzB,CAAC;AAMD,IAAM,wBAAwB,EAAE,OAAO;AAAA,EACrC,MAAM,EAAE,QAAQ,WAAW;AAAA,EAC3B,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC;AACzB,CAAC;AAEM,IAAM,uBAAuB,EAAE,mBAAmB,QAAQ;AAAA,EAC/D;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAwBM,IAAM,sBAAyE;AAAA,EACpF,gBAAgB;AAAA,EAChB,WAAW;AAAA,EACX,UAAU;AAAA,EACV,SAAS;AAAA,EACT,WAAW;AAAA,EACX,WAAW;AACb;AAGO,SAAS,kBAAkB,MAAuC;AACvE,SAAO,oBAAoB,IAAI;AACjC;AAWO,IAAM,sBAAsB,EAAE,OAAO;AAAA,EAC1C,QAAQ;AAAA,EACR,YAAY,EAAE,KAAK,CAAC,QAAQ,KAAK,CAAC,EAAE,SAAS;AAC/C,CAAC;AASM,IAAM,yBAAyB,EAAE,OAAO;AAAA,EAC7C,cAAc,EAAE,OAAO,EAAE,IAAI,CAAC;AAChC,CAAC;AAQD,IAAM,iBAAiB,EAAE,OAAO;AAAA,EAC9B,MAAM,EAAE,QAAQ,MAAM;AAAA,EACtB,MAAM;AAAA,EACN,SAAS,EAAE,MAAM,kBAAkB,EAAE,IAAI,CAAC;AAAA,EAC1C,SAAS,EAAE,MAAM,oBAAoB,EAAE,QAAQ,CAAC,CAAC;AACnD,CAAC;AAED,IAAM,oBAAoB,EAAE,OAAO;AAAA,EACjC,MAAM,EAAE,QAAQ,SAAS;AAAA,EACzB,SAAS;AAAA,EACT,SAAS,EAAE,MAAM,kBAAkB,EAAE,IAAI,CAAC;AAAA,EAC1C,SAAS,EAAE,MAAM,oBAAoB,EAAE,QAAQ,CAAC,CAAC;AACnD,CAAC;AASM,IAAM,wBAAwB,EAAE,mBAAmB,QAAQ;AAAA,EAChE;AAAA,EACA;AACF,CAAC;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../runtime/base-classes/activity-entity-service.ts"],"sourcesContent":["/**\n * ActivityEntityService<TRepo, TEntity>\n *\n * Family-specific base service for activity entities.\n * Delegates to an activity repository that provides date-range,\n * user, and opportunity queries.\n */\nimport { BaseService, type IBaseRepository } from './base-service';\n\nexport interface IActivityEntityRepository<TEntity> extends IBaseRepository<TEntity> {\n findByDateRange(start: Date, end: Date): Promise<TEntity[]>;\n findByUserId(userId: string): Promise<TEntity[]>;\n findByOpportunityId(opportunityId: string): Promise<TEntity[]>;\n findRecentByOpportunityId(opportunityId: string, limit?: number): Promise<TEntity[]>;\n}\n\nexport abstract class ActivityEntityService<\n TRepo extends IActivityEntityRepository<TEntity>,\n TEntity,\n> extends BaseService<TRepo, TEntity> {\n /**\n * Find activities within a date range (inclusive).\n */\n findByDateRange(start: Date, end: Date): Promise<TEntity[]> {\n return this.repository.findByDateRange(start, end);\n }\n\n /**\n * Find all activities for a specific user.\n */\n findByUser(userId: string): Promise<TEntity[]> {\n return this.repository.findByUserId(userId);\n }\n\n /**\n * Find all activities for a specific opportunity.\n */\n findByOpportunity(opportunityId: string): Promise<TEntity[]> {\n return this.repository.findByOpportunityId(opportunityId);\n }\n\n /**\n * Find the most recent activities for an opportunity.\n */\n findRecent(opportunityId: string, limit?: number): Promise<TEntity[]> {\n return this.repository.findRecentByOpportunityId(opportunityId, limit);\n }\n}\n"],"mappings":";;;;;AAgBO,IAAe,wBAAf,cAGG,YAA4B;AAAA;AAAA;AAAA;AAAA,EAIpC,gBAAgB,OAAa,KAA+B;AAC1D,WAAO,KAAK,WAAW,gBAAgB,OAAO,GAAG;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA,EAKA,WAAW,QAAoC;AAC7C,WAAO,KAAK,WAAW,aAAa,MAAM;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA,EAKA,kBAAkB,eAA2C;AAC3D,WAAO,KAAK,WAAW,oBAAoB,aAAa;AAAA,EAC1D;AAAA;AAAA;AAAA;AAAA,EAKA,WAAW,eAAuB,OAAoC;AACpE,WAAO,KAAK,WAAW,0BAA0B,eAAe,KAAK;AAAA,EACvE;AACF;","names":[]}
@@ -1,41 +0,0 @@
1
- import {
2
- BaseRepository
3
- } from "./chunk-J6KZS54B.js";
4
-
5
- // runtime/base-classes/activity-entity-repository.ts
6
- import { eq, between, desc } from "drizzle-orm";
7
- var ActivityEntityRepository = class extends BaseRepository {
8
- /**
9
- * Find activities within a date range (inclusive).
10
- */
11
- async findByDateRange(start, end) {
12
- const rows = await this.baseQuery().where(between(this.table["occurredAt"], start, end));
13
- return rows;
14
- }
15
- /**
16
- * Find all activities for a specific user.
17
- */
18
- async findByUserId(userId) {
19
- const rows = await this.baseQuery().where(eq(this.table["userId"], userId));
20
- return rows;
21
- }
22
- /**
23
- * Find all activities for a specific opportunity.
24
- */
25
- async findByOpportunityId(opportunityId) {
26
- const rows = await this.baseQuery().where(eq(this.table["opportunityId"], opportunityId));
27
- return rows;
28
- }
29
- /**
30
- * Find the most recent activities for an opportunity, ordered by occurredAt desc.
31
- */
32
- async findRecentByOpportunityId(opportunityId, limit = 10) {
33
- const rows = await this.baseQuery().where(eq(this.table["opportunityId"], opportunityId)).orderBy(desc(this.table["occurredAt"])).limit(limit);
34
- return rows;
35
- }
36
- };
37
-
38
- export {
39
- ActivityEntityRepository
40
- };
41
- //# sourceMappingURL=chunk-XCEI7NUH.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../runtime/base-classes/activity-entity-repository.ts"],"sourcesContent":["/**\n * ActivityEntityRepository<TEntity>\n *\n * Family-specific base for activity entities (emails, calls, meetings, notes).\n * Adds date-range queries, user/opportunity scoping, and recency ordering.\n *\n * Concrete repos extend this and declare their table + behaviors.\n */\nimport { eq, between, desc } from 'drizzle-orm';\nimport { BaseRepository } from './base-repository';\n\nexport abstract class ActivityEntityRepository<TEntity> extends BaseRepository<TEntity> {\n /**\n * Find activities within a date range (inclusive).\n */\n async findByDateRange(start: Date, end: Date): Promise<TEntity[]> {\n const rows = await this.baseQuery()\n .where(between(this.table['occurredAt'], start, end));\n return rows as TEntity[];\n }\n\n /**\n * Find all activities for a specific user.\n */\n async findByUserId(userId: string): Promise<TEntity[]> {\n const rows = await this.baseQuery()\n .where(eq(this.table['userId'], userId));\n return rows as TEntity[];\n }\n\n /**\n * Find all activities for a specific opportunity.\n */\n async findByOpportunityId(opportunityId: string): Promise<TEntity[]> {\n const rows = await this.baseQuery()\n .where(eq(this.table['opportunityId'], opportunityId));\n return rows as TEntity[];\n }\n\n /**\n * Find the most recent activities for an opportunity, ordered by occurredAt desc.\n */\n async findRecentByOpportunityId(opportunityId: string, limit = 10): Promise<TEntity[]> {\n const rows = await this.baseQuery()\n .where(eq(this.table['opportunityId'], opportunityId))\n .orderBy(desc(this.table['occurredAt']))\n .limit(limit);\n return rows as TEntity[];\n }\n}\n"],"mappings":";;;;;AAQA,SAAS,IAAI,SAAS,YAAY;AAG3B,IAAe,2BAAf,cAAyD,eAAwB;AAAA;AAAA;AAAA;AAAA,EAItF,MAAM,gBAAgB,OAAa,KAA+B;AAChE,UAAM,OAAO,MAAM,KAAK,UAAU,EAC/B,MAAM,QAAQ,KAAK,MAAM,YAAY,GAAG,OAAO,GAAG,CAAC;AACtD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAAa,QAAoC;AACrD,UAAM,OAAO,MAAM,KAAK,UAAU,EAC/B,MAAM,GAAG,KAAK,MAAM,QAAQ,GAAG,MAAM,CAAC;AACzC,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,oBAAoB,eAA2C;AACnE,UAAM,OAAO,MAAM,KAAK,UAAU,EAC/B,MAAM,GAAG,KAAK,MAAM,eAAe,GAAG,aAAa,CAAC;AACvD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,0BAA0B,eAAuB,QAAQ,IAAwB;AACrF,UAAM,OAAO,MAAM,KAAK,UAAU,EAC/B,MAAM,GAAG,KAAK,MAAM,eAAe,GAAG,aAAa,CAAC,EACpD,QAAQ,KAAK,KAAK,MAAM,YAAY,CAAC,CAAC,EACtC,MAAM,KAAK;AACd,WAAO;AAAA,EACT;AACF;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../runtime/subsystems/integration/webhook-change-source.ts"],"sourcesContent":["/**\n * Integration subsystem — `WebhookChangeSource<T>` primitive (#226-4, ADR-033).\n *\n * Generic webhook-mode `IChangeSource<T>` implementation parameterized by a\n * parsed `DetectionConfig` (webhook mode) and a consumer-supplied\n * `WebhookFetchCallback<T>` that iterates a consumer-owned inbound staging\n * queue. The primitive owns:\n *\n * - canonical `Change<T>.source = 'webhook'` stamping;\n * - `dedupKey` derivation from the configured `webhook.eventIdField` on\n * the emitted record;\n * - `externalId` derivation: the mapping entry whose `target === 'external_id'`\n * names — via its `source` — the field on the emitted record that carries\n * the canonical external id (mirrors `PollChangeSource`);\n * - middleware-chain composition via the locked `ChangeMiddleware<T>` shape\n * (#226-1) — same composition seam as the poll primitive.\n *\n * The primitive is **passive**: it iterates whatever the consumer-owned\n * queue yields. It does NOT synchronously drive the orchestrator, does NOT\n * own a transport, and does NOT manage acks. The inbound staging table\n * schema is consumer-owned and deferred per ADR-0002 §Phase 4 — the\n * `WebhookFetchCallback<T>` is the queue contract the consumer injects.\n *\n * Shape locks (decision memo Q5, mirrored from poll primitive):\n * - `WebhookFetchContext = { subscription, cursor }` — explicitly NO\n * `userId` / `tenantId`. Run-scope identity is closed over by the\n * consumer at queue construction or resolved inside the callback via\n * consumer services. There are no `filters` on the webhook context —\n * filtering is done at registration / on the staging row, not at the\n * port seam (the queue is already filtered by the time the primitive\n * iterates).\n *\n * Long-lived streaming CDC primitives (SFDC Pub-Sub gRPC, Debezium/Kafka,\n * Postgres logical replication) are deferred to `#226-8` — they need a\n * fundamentally different lifecycle (`subscribe(onChange, onError)`,\n * server-paced backpressure, ack-on-yield) and shouldn't be retrofitted\n * into either this primitive or the poll primitive.\n */\n\nimport type { DetectionConfig } from './detection-config.schema';\nimport type {\n Change,\n IChangeSource,\n IntegrationSubscriptionView,\n} from './integration-change-source.protocol';\nimport type {\n ChangeIterator,\n ChangeMiddleware,\n} from './integration-middleware.protocol';\n\n// ============================================================================\n// Cursor + queue callback shapes\n// ============================================================================\n\n/**\n * Opaque webhook cursor shape. Webhook mode typically has a cursor of\n * `{ ts: ISO-string }` (last drained staging-row timestamp) but the\n * primitive treats it as opaque. Consumer-owned queue iterators interpret\n * it however the staging schema needs.\n */\nexport type WebhookCursor = unknown;\n\n/**\n * Context the primitive forwards to the queue iterator. Locked to exactly\n * two fields per the same Q5 reasoning that locks `PollFetchContext` — no\n * `userId` / `tenantId`.\n */\nexport interface WebhookFetchContext {\n readonly subscription: IntegrationSubscriptionView;\n readonly cursor: WebhookCursor | null;\n}\n\n/**\n * Consumer-supplied queue iterator. Returns an async iterable of\n * `{ record }` pairs — the consumer drains the inbound staging queue and\n * emits already-mapped canonical records `T`. The primitive stamps\n * `source: 'webhook'` and `dedupKey` from the record's configured\n * `webhook.eventIdField`; the consumer is the one who decided when a\n * staging row is \"ready\" to drain.\n *\n * Webhook mode has no per-record cursor advance — the staging-row drain\n * order is consumer policy (FIFO by ingestion timestamp, by event id, etc.)\n * and is opaque to the primitive. The orchestrator's last-yielded cursor\n * is whatever the consumer chooses to surface, if anything.\n */\nexport type WebhookFetchCallback<T> = (\n ctx: WebhookFetchContext,\n) => AsyncIterable<{ record: T; cursor?: WebhookCursor }>;\n\n// ============================================================================\n// Constructor options\n// ============================================================================\n\nexport interface WebhookChangeSourceOptions<T> {\n /** Consumer-supplied inbound queue iterator. */\n readonly queue: WebhookFetchCallback<T>;\n /**\n * Parsed detection config. MUST be `mode: 'webhook'`; the constructor\n * throws if a poll config is supplied. Codegen-emitted factories call\n * `DetectionConfigSchema.parse(...)` upstream so this is a safety net,\n * not the primary validation point.\n */\n readonly config: DetectionConfig;\n /**\n * Optional middleware chain. Same shape and composition rules as\n * `PollChangeSource` — first element is the outermost layer.\n */\n readonly middlewares?: ReadonlyArray<ChangeMiddleware<T>>;\n /**\n * Optional human label for run logs (e.g. `'stripe-webhook-charge'`).\n * Defaults to a derived label based on the mapping at construction.\n */\n readonly label?: string;\n}\n\n// ============================================================================\n// WebhookChangeSource<T>\n// ============================================================================\n\nexport class WebhookChangeSource<T> implements IChangeSource<T> {\n public readonly label: string;\n\n private readonly queue: WebhookFetchCallback<T>;\n private readonly externalIdSourceField: string;\n private readonly eventIdSourceField: string;\n private readonly composed: ChangeIterator<T>;\n\n constructor(opts: WebhookChangeSourceOptions<T>) {\n if (opts.config.mode !== 'webhook') {\n throw new Error(\n `WebhookChangeSource requires DetectionConfig.mode === 'webhook'; got '${(opts.config as { mode: string }).mode}'`,\n );\n }\n const config = opts.config;\n\n // Field mapping: locate the entry whose canonical `target` is `external_id`\n // — mirrors the poll primitive's contract. Adapters emit records\n // already-mapped; the primitive needs to know which key on T carries the\n // external id so it can stamp `Change.externalId`. That key is the\n // mapping's `source` (the field on the emitted record), NOT its `target`\n // (the canonical column) — they differ whenever the canonical record is\n // vendor-neutral camelCase (e.g. `source: 'externalId'` → `target: 'external_id'`).\n const externalIdMapping = config.mapping.find(\n (m) => m.target === 'external_id',\n );\n if (!externalIdMapping) {\n throw new Error(\n \"WebhookChangeSource: DetectionConfig.mapping must include an entry with target 'external_id' so emitted Change<T>.externalId can be populated\",\n );\n }\n this.externalIdSourceField = externalIdMapping.source;\n this.eventIdSourceField = config.webhook.eventIdField;\n\n this.queue = opts.queue;\n\n this.label =\n opts.label ?? `webhook-change-source:${externalIdMapping.source}`;\n\n // Compose middleware chain — same shape as PollChangeSource.\n const inner: ChangeIterator<T> = (sub, cur) => this.fetch(sub, cur);\n const middlewares = opts.middlewares ?? [];\n this.composed = middlewares.reduceRight<ChangeIterator<T>>(\n (next, mw) => mw(next),\n inner,\n );\n }\n\n listChanges(\n subscription: IntegrationSubscriptionView,\n cursor: unknown | null,\n ): AsyncIterable<Change<T>> {\n return this.composed(subscription, cursor);\n }\n\n private async *fetch(\n subscription: IntegrationSubscriptionView,\n cursor: unknown | null,\n ): AsyncIterable<Change<T>> {\n const ctx: WebhookFetchContext = {\n subscription,\n cursor: cursor as WebhookCursor | null,\n };\n\n for await (const { record, cursor: nextCursor } of this.queue(ctx)) {\n const externalIdRaw = (record as Record<string, unknown>)[\n this.externalIdSourceField\n ];\n if (typeof externalIdRaw !== 'string' || externalIdRaw.length === 0) {\n throw new Error(\n `WebhookChangeSource: record missing string '${this.externalIdSourceField}' — emitted records MUST carry the canonical external id keyed by the mapping source`,\n );\n }\n const eventIdRaw = (record as Record<string, unknown>)[\n this.eventIdSourceField\n ];\n if (typeof eventIdRaw !== 'string' || eventIdRaw.length === 0) {\n throw new Error(\n `WebhookChangeSource: record missing string '${this.eventIdSourceField}' — webhook records MUST carry the event id (DetectionConfig.webhook.eventIdField) so Change<T>.dedupKey can be populated`,\n );\n }\n\n const change: Change<T> = {\n externalId: externalIdRaw,\n // Webhook mode cannot distinguish create vs. update vs. delete on\n // its own — the orchestrator's diff stage handles classification.\n // Tombstone / soft-delete detection is consumer-driven (same as\n // poll mode — see ADR-033).\n operation: 'updated',\n record,\n cursor: nextCursor ?? null,\n source: 'webhook',\n dedupKey: eventIdRaw,\n };\n yield change;\n }\n }\n}\n"],"mappings":";AAuHO,IAAM,sBAAN,MAAyD;AAAA,EAC9C;AAAA,EAEC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,YAAY,MAAqC;AAC/C,QAAI,KAAK,OAAO,SAAS,WAAW;AAClC,YAAM,IAAI;AAAA,QACR,yEAA0E,KAAK,OAA4B,IAAI;AAAA,MACjH;AAAA,IACF;AACA,UAAM,SAAS,KAAK;AASpB,UAAM,oBAAoB,OAAO,QAAQ;AAAA,MACvC,CAAC,MAAM,EAAE,WAAW;AAAA,IACtB;AACA,QAAI,CAAC,mBAAmB;AACtB,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,SAAK,wBAAwB,kBAAkB;AAC/C,SAAK,qBAAqB,OAAO,QAAQ;AAEzC,SAAK,QAAQ,KAAK;AAElB,SAAK,QACH,KAAK,SAAS,yBAAyB,kBAAkB,MAAM;AAGjE,UAAM,QAA2B,CAAC,KAAK,QAAQ,KAAK,MAAM,KAAK,GAAG;AAClE,UAAM,cAAc,KAAK,eAAe,CAAC;AACzC,SAAK,WAAW,YAAY;AAAA,MAC1B,CAAC,MAAM,OAAO,GAAG,IAAI;AAAA,MACrB;AAAA,IACF;AAAA,EACF;AAAA,EAEA,YACE,cACA,QAC0B;AAC1B,WAAO,KAAK,SAAS,cAAc,MAAM;AAAA,EAC3C;AAAA,EAEA,OAAe,MACb,cACA,QAC0B;AAC1B,UAAM,MAA2B;AAAA,MAC/B;AAAA,MACA;AAAA,IACF;AAEA,qBAAiB,EAAE,QAAQ,QAAQ,WAAW,KAAK,KAAK,MAAM,GAAG,GAAG;AAClE,YAAM,gBAAiB,OACrB,KAAK,qBACP;AACA,UAAI,OAAO,kBAAkB,YAAY,cAAc,WAAW,GAAG;AACnE,cAAM,IAAI;AAAA,UACR,+CAA+C,KAAK,qBAAqB;AAAA,QAC3E;AAAA,MACF;AACA,YAAM,aAAc,OAClB,KAAK,kBACP;AACA,UAAI,OAAO,eAAe,YAAY,WAAW,WAAW,GAAG;AAC7D,cAAM,IAAI;AAAA,UACR,+CAA+C,KAAK,kBAAkB;AAAA,QACxE;AAAA,MACF;AAEA,YAAM,SAAoB;AAAA,QACxB,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA,QAKZ,WAAW;AAAA,QACX;AAAA,QACA,QAAQ,cAAc;AAAA,QACtB,QAAQ;AAAA,QACR,UAAU;AAAA,MACZ;AACA,YAAM;AAAA,IACR;AAAA,EACF;AACF;","names":[]}