@mj-biz-apps/common-activity-sync 0.0.0 → 5.37.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 (90) hide show
  1. package/dist/ActivitySyncEngine.d.ts +118 -0
  2. package/dist/ActivitySyncEngine.d.ts.map +1 -0
  3. package/dist/ActivitySyncEngine.js +620 -0
  4. package/dist/ActivitySyncEngine.js.map +1 -0
  5. package/dist/BaseActivitySyncExtension.d.ts +99 -0
  6. package/dist/BaseActivitySyncExtension.d.ts.map +1 -0
  7. package/dist/BaseActivitySyncExtension.js +3 -0
  8. package/dist/BaseActivitySyncExtension.js.map +1 -0
  9. package/dist/BaseActivitySyncProvider.d.ts +40 -0
  10. package/dist/BaseActivitySyncProvider.d.ts.map +1 -0
  11. package/dist/BaseActivitySyncProvider.js +76 -0
  12. package/dist/BaseActivitySyncProvider.js.map +1 -0
  13. package/dist/action-result.d.ts +38 -0
  14. package/dist/action-result.d.ts.map +1 -0
  15. package/dist/action-result.js +43 -0
  16. package/dist/action-result.js.map +1 -0
  17. package/dist/entity-names.d.ts +19 -0
  18. package/dist/entity-names.d.ts.map +1 -0
  19. package/dist/entity-names.js +19 -0
  20. package/dist/entity-names.js.map +1 -0
  21. package/dist/extensions.d.ts +37 -0
  22. package/dist/extensions.d.ts.map +1 -0
  23. package/dist/extensions.js +80 -0
  24. package/dist/extensions.js.map +1 -0
  25. package/dist/identity.d.ts +30 -0
  26. package/dist/identity.d.ts.map +1 -0
  27. package/dist/identity.js +90 -0
  28. package/dist/identity.js.map +1 -0
  29. package/dist/index.d.ts +29 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +29 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/load.d.ts +17 -0
  34. package/dist/load.d.ts.map +1 -0
  35. package/dist/load.js +29 -0
  36. package/dist/load.js.map +1 -0
  37. package/dist/manual-log.d.ts +33 -0
  38. package/dist/manual-log.d.ts.map +1 -0
  39. package/dist/manual-log.js +230 -0
  40. package/dist/manual-log.js.map +1 -0
  41. package/dist/participants.d.ts +47 -0
  42. package/dist/participants.d.ts.map +1 -0
  43. package/dist/participants.js +59 -0
  44. package/dist/participants.js.map +1 -0
  45. package/dist/providers/FixtureActivitySyncProvider.d.ts +21 -0
  46. package/dist/providers/FixtureActivitySyncProvider.d.ts.map +1 -0
  47. package/dist/providers/FixtureActivitySyncProvider.js +58 -0
  48. package/dist/providers/FixtureActivitySyncProvider.js.map +1 -0
  49. package/dist/providers/GraphMessageMapper.d.ts +37 -0
  50. package/dist/providers/GraphMessageMapper.d.ts.map +1 -0
  51. package/dist/providers/GraphMessageMapper.js +91 -0
  52. package/dist/providers/GraphMessageMapper.js.map +1 -0
  53. package/dist/providers/MSGraphActivitySyncProvider.d.ts +22 -0
  54. package/dist/providers/MSGraphActivitySyncProvider.d.ts.map +1 -0
  55. package/dist/providers/MSGraphActivitySyncProvider.js +62 -0
  56. package/dist/providers/MSGraphActivitySyncProvider.js.map +1 -0
  57. package/dist/providers/MSGraphCalendarSyncProvider.d.ts +60 -0
  58. package/dist/providers/MSGraphCalendarSyncProvider.d.ts.map +1 -0
  59. package/dist/providers/MSGraphCalendarSyncProvider.js +145 -0
  60. package/dist/providers/MSGraphCalendarSyncProvider.js.map +1 -0
  61. package/dist/qualification.d.ts +91 -0
  62. package/dist/qualification.d.ts.map +1 -0
  63. package/dist/qualification.js +62 -0
  64. package/dist/qualification.js.map +1 -0
  65. package/dist/run.d.ts +67 -0
  66. package/dist/run.d.ts.map +1 -0
  67. package/dist/run.js +84 -0
  68. package/dist/run.js.map +1 -0
  69. package/dist/sql.d.ts +17 -0
  70. package/dist/sql.d.ts.map +1 -0
  71. package/dist/sql.js +50 -0
  72. package/dist/sql.js.map +1 -0
  73. package/dist/stages.d.ts +55 -0
  74. package/dist/stages.d.ts.map +1 -0
  75. package/dist/stages.js +109 -0
  76. package/dist/stages.js.map +1 -0
  77. package/dist/types.d.ts +130 -0
  78. package/dist/types.d.ts.map +1 -0
  79. package/dist/types.js +38 -0
  80. package/dist/types.js.map +1 -0
  81. package/dist/watermark.d.ts +63 -0
  82. package/dist/watermark.d.ts.map +1 -0
  83. package/dist/watermark.js +103 -0
  84. package/dist/watermark.js.map +1 -0
  85. package/dist/writer.d.ts +132 -0
  86. package/dist/writer.d.ts.map +1 -0
  87. package/dist/writer.js +326 -0
  88. package/dist/writer.js.map +1 -0
  89. package/package.json +32 -5
  90. package/README.md +0 -12
@@ -0,0 +1,99 @@
1
+ /**
2
+ * @fileoverview The in-process enrichment contract — how a downstream app adds to an Activity
3
+ * without Common knowing it exists.
4
+ *
5
+ * ── WHY IN-STREAM AND NOT AN ENTITY ACTION ─────────────────────────────────────────────────────
6
+ *
7
+ * MJ's `Entity Actions` bind declaratively to `AfterCreate` and are the right tool for most
8
+ * after-the-fact work. They are the wrong tool here for one reason: they fire AFTER the commit, in
9
+ * a separate transaction. An activity's links are what make it reachable — every read starts from a
10
+ * link — so an activity that commits without its deal link is briefly, and observably, unattributed.
11
+ * Running extensions inside the write transaction keeps "the activity and everything that explains
12
+ * it" a single atomic fact.
13
+ *
14
+ * ── EXTENSIONS ENRICH; THEY NEVER VETO ─────────────────────────────────────────────────────────
15
+ *
16
+ * Qualification is the engine's job and has already run by the time an extension sees anything. If
17
+ * an extension could reject an activity, whether a message got captured would depend on which apps
18
+ * happened to be installed on the host — precisely the coupling this design exists to prevent. An
19
+ * extension that wants an activity gone should record why, not suppress it.
20
+ *
21
+ * Registered by `DriverClass`, enabled and ordered by an `ActivitySyncExtension` row the consumer
22
+ * app ships in its OWN metadata:
23
+ *
24
+ * ```ts
25
+ * @RegisterClass(BaseActivitySyncExtension, 'Sales.DealLinker')
26
+ * export class DealLinkerExtension extends BaseActivitySyncExtension { ... }
27
+ * ```
28
+ *
29
+ * @module @mj-biz-apps/common-activity-sync
30
+ */
31
+ import type { IMetadataProvider, UserInfo } from '@memberjunction/core';
32
+ import type { mjBizAppsCommonActivityEntity, mjBizAppsCommonActivityLinkEntity } from '@mj-biz-apps/common-entities';
33
+ import type { ActivityIdentityKind, ActivityLinkRole, NormalizedItem } from './types.js';
34
+ /** A party the engine matched to a Common record. */
35
+ export interface ResolvedParty {
36
+ Kind: 'Person' | 'Organization';
37
+ RecordID: string;
38
+ Role: ActivityLinkRole;
39
+ }
40
+ /** A party the engine could not match, recorded so a later sweep can resolve it. */
41
+ export interface UnresolvedParty {
42
+ Kind: ActivityIdentityKind;
43
+ Value: string;
44
+ Role: ActivityLinkRole;
45
+ }
46
+ /**
47
+ * What an extension is handed.
48
+ *
49
+ * The activity is already saved and the transaction is still open, so an extension adds rows that
50
+ * commit or roll back with it.
51
+ */
52
+ export interface ActivityWriteContext {
53
+ /** The saved activity. Mutating and re-saving it is allowed; deleting it is not. */
54
+ Activity: mjBizAppsCommonActivityEntity;
55
+ /** Links written by the engine so far, in write order. */
56
+ Links: mjBizAppsCommonActivityLinkEntity[];
57
+ /** The source item, for anything the entity does not carry. */
58
+ Item: NormalizedItem;
59
+ ResolvedParties: ResolvedParty[];
60
+ UnresolvedParties: UnresolvedParty[];
61
+ ConnectionID: string;
62
+ ProviderTypeCode: string;
63
+ ContextUser: UserInfo;
64
+ /**
65
+ * The provider the engine is writing through.
66
+ *
67
+ * Carried rather than left to the extension to obtain, because an extension MUST write through
68
+ * the same provider as the engine or its rows land outside the write transaction — which is the
69
+ * one guarantee an in-stream extension exists to give. It is also how an extension resolves an
70
+ * `EntityID` for a link (`Provider.Entities`).
71
+ */
72
+ Provider: IMetadataProvider;
73
+ /**
74
+ * Cooperative cancellation for the registration's TimeoutMS.
75
+ *
76
+ * WithTimeout races; it does not kill the work. A timed-out Enrich keeps running inside
77
+ * the open write transaction unless it watches this signal. Abort the remaining work
78
+ * when `Signal.aborted` is set — otherwise a Skip loop can still be mid-Save when
79
+ * Commit or Rollback runs.
80
+ */
81
+ Signal?: AbortSignal;
82
+ }
83
+ export declare abstract class BaseActivitySyncExtension {
84
+ /**
85
+ * Add whatever this app knows to the activity, inside its transaction.
86
+ *
87
+ * Throwing is a legitimate way to report failure. What happens next is the REGISTRATION's
88
+ * choice, not this class's: `FailurePolicy = 'Skip'` (the default) records the error and lets
89
+ * the activity commit without the enrichment; `'Abort'` rolls the whole write back. Skip is the
90
+ * default because the activity is worth more than the enrichment, and one buggy consumer app
91
+ * must not be able to halt ingestion for every other app on the host.
92
+ *
93
+ * Keep it inside the registration's `TimeoutMS`: this runs with the write transaction open.
94
+ * Timeout is cooperative — watch `context.Signal` and return or throw when it aborts.
95
+ * `WithTimeout` rejects the waiter; it does not cancel the promise.
96
+ */
97
+ abstract Enrich(context: ActivityWriteContext): Promise<void>;
98
+ }
99
+ //# sourceMappingURL=BaseActivitySyncExtension.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BaseActivitySyncExtension.d.ts","sourceRoot":"","sources":["../src/BaseActivitySyncExtension.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACxE,OAAO,KAAK,EACR,6BAA6B,EAC7B,iCAAiC,EACpC,MAAM,8BAA8B,CAAC;AAEtC,OAAO,KAAK,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEzF,qDAAqD;AACrD,MAAM,WAAW,aAAa;IAC1B,IAAI,EAAE,QAAQ,GAAG,cAAc,CAAC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,gBAAgB,CAAC;CAC1B;AAED,oFAAoF;AACpF,MAAM,WAAW,eAAe;IAC5B,IAAI,EAAE,oBAAoB,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,gBAAgB,CAAC;CAC1B;AAED;;;;;GAKG;AACH,MAAM,WAAW,oBAAoB;IACjC,oFAAoF;IACpF,QAAQ,EAAE,6BAA6B,CAAC;IACxC,0DAA0D;IAC1D,KAAK,EAAE,iCAAiC,EAAE,CAAC;IAC3C,+DAA+D;IAC/D,IAAI,EAAE,cAAc,CAAC;IACrB,eAAe,EAAE,aAAa,EAAE,CAAC;IACjC,iBAAiB,EAAE,eAAe,EAAE,CAAC;IACrC,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,QAAQ,CAAC;IACtB;;;;;;;OAOG;IACH,QAAQ,EAAE,iBAAiB,CAAC;IAC5B;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;CACxB;AAED,8BAAsB,yBAAyB;IAC3C;;;;;;;;;;;;OAYG;aACa,MAAM,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;CACvE"}
@@ -0,0 +1,3 @@
1
+ export class BaseActivitySyncExtension {
2
+ }
3
+ //# sourceMappingURL=BaseActivitySyncExtension.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BaseActivitySyncExtension.js","sourceRoot":"","sources":["../src/BaseActivitySyncExtension.ts"],"names":[],"mappings":"AA0FA,MAAM,OAAgB,yBAAyB;CAe9C"}
@@ -0,0 +1,40 @@
1
+ import type { ActivitySourceBatch, ActivitySourceKind, ActivitySourceQuery, NormalizedItem, RawBatch } from './types.js';
2
+ export declare abstract class BaseActivitySyncProvider {
3
+ /** Which surface this reads. Selects the watermark basis — see `WatermarkBasisForKind`. */
4
+ abstract readonly Kind: ActivitySourceKind;
5
+ /** Must match an `ActivitySyncProviderType.Code`. */
6
+ abstract readonly ProviderTypeCode: string;
7
+ /**
8
+ * False for fixtures.
9
+ *
10
+ * Not decoration: the engine refuses to write `Source: 'Integration'` rows from a non-live
11
+ * provider without being told to, so a fixture run cannot be mistaken for a real one in the
12
+ * database.
13
+ */
14
+ abstract readonly IsLive: boolean;
15
+ /** Ask the transport for whatever it has. Raw payloads — no interpretation. */
16
+ protected abstract FetchRaw(query: ActivitySourceQuery): Promise<RawBatch>;
17
+ /** Map raw payloads into the engine's vocabulary. The one place a transport's shape is known. */
18
+ protected abstract Normalize(raw: RawBatch, query: ActivitySourceQuery): NormalizedItem[];
19
+ /**
20
+ * One sync pass. Template method — subclasses extend it through the hooks below, never by
21
+ * overriding this.
22
+ */
23
+ Fetch(query: ActivitySourceQuery): Promise<ActivitySourceBatch>;
24
+ /**
25
+ * The newest point this pass can honestly claim.
26
+ *
27
+ * Overridable for a transport that exposes a better signal than either item time or observation
28
+ * time (a provider `ModifiedAt`, a server-side cursor). Overriding it means taking on the rules
29
+ * in `watermark.ts` — read them first.
30
+ */
31
+ protected ComputeHighWatermark(items: readonly NormalizedItem[], observedAt: Date): Date | null;
32
+ /** Seam for tests. Everything stored is UTC; a JS Date is a UTC instant. */
33
+ protected Now(): Date;
34
+ protected OnBeforeFetch(_query: ActivitySourceQuery): Promise<void>;
35
+ protected OnAfterFetch(_raw: RawBatch, _query: ActivitySourceQuery): Promise<void>;
36
+ protected OnBeforeNormalize(_raw: RawBatch, _query: ActivitySourceQuery): Promise<void>;
37
+ protected OnAfterNormalize(_items: NormalizedItem[], _query: ActivitySourceQuery): Promise<void>;
38
+ protected OnError(_error: unknown, _query: ActivitySourceQuery): Promise<void>;
39
+ }
40
+ //# sourceMappingURL=BaseActivitySyncProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BaseActivitySyncProvider.d.ts","sourceRoot":"","sources":["../src/BaseActivitySyncProvider.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EACR,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,QAAQ,EACX,MAAM,YAAY,CAAC;AAEpB,8BAAsB,wBAAwB;IAC1C,2FAA2F;IAC3F,kBAAyB,IAAI,EAAE,kBAAkB,CAAC;IAElD,qDAAqD;IACrD,kBAAyB,gBAAgB,EAAE,MAAM,CAAC;IAElD;;;;;;OAMG;IACH,kBAAyB,MAAM,EAAE,OAAO,CAAC;IAEzC,+EAA+E;IAC/E,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,QAAQ,CAAC;IAE1E,iGAAiG;IACjG,SAAS,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,mBAAmB,GAAG,cAAc,EAAE;IAEzF;;;OAGG;IACU,KAAK,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IA6B5E;;;;;;OAMG;IACH,SAAS,CAAC,oBAAoB,CAAC,KAAK,EAAE,SAAS,cAAc,EAAE,EAAE,UAAU,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI;IAI/F,4EAA4E;IAC5E,SAAS,CAAC,GAAG,IAAI,IAAI;cAML,aAAa,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;cACzD,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;cACxE,iBAAiB,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;cAC7E,gBAAgB,CAAC,MAAM,EAAE,cAAc,EAAE,EAAE,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;cACtF,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;CACvF"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * @fileoverview The provider plugin base class.
3
+ *
4
+ * A BASE CLASS rather than an interface, deliberately. The parts of a sync pass that every provider
5
+ * gets wrong in the same way — the watermark, issue collection, hook ordering — live here once and
6
+ * are not a subclass's to reimplement. A subclass supplies only what is genuinely specific to its
7
+ * transport: how to fetch, how to map, and (where it disagrees with the default) how to compute its
8
+ * own high-water mark.
9
+ *
10
+ * Providers WRAP existing plumbing rather than reinventing transport: MJ Communication providers
11
+ * for Gmail and Microsoft Graph, Twilio for SMS and WhatsApp, MJ Actions for social feeds. A
12
+ * provider's job is to speak its transport in this engine's vocabulary.
13
+ *
14
+ * Registered by `DriverClass`, matching every other plugin surface in MJ:
15
+ *
16
+ * ```ts
17
+ * @RegisterClass(BaseActivitySyncProvider, 'Microsoft365')
18
+ * export class MSGraphActivitySyncProvider extends BaseActivitySyncProvider { ... }
19
+ * ```
20
+ *
21
+ * @module @mj-biz-apps/common-activity-sync
22
+ */
23
+ import { LogError } from '@memberjunction/core';
24
+ import { ResolveHighWatermark } from './watermark.js';
25
+ export class BaseActivitySyncProvider {
26
+ /**
27
+ * One sync pass. Template method — subclasses extend it through the hooks below, never by
28
+ * overriding this.
29
+ */
30
+ async Fetch(query) {
31
+ const observedAt = this.Now();
32
+ try {
33
+ await this.OnBeforeFetch(query);
34
+ const raw = await this.FetchRaw(query);
35
+ await this.OnAfterFetch(raw, query);
36
+ await this.OnBeforeNormalize(raw, query);
37
+ const items = this.Normalize(raw, query);
38
+ await this.OnAfterNormalize(items, query);
39
+ return {
40
+ Items: items,
41
+ HighWatermark: this.ComputeHighWatermark(items, observedAt),
42
+ Issues: raw.Issues,
43
+ };
44
+ }
45
+ catch (error) {
46
+ const message = error instanceof Error ? error.message : String(error);
47
+ LogError(`BaseActivitySyncProvider.Fetch failed for provider "${this.ProviderTypeCode}" ` +
48
+ `on mailbox "${query.Mailbox}": ${message}`);
49
+ await this.OnError(error, query);
50
+ // Failed, not empty. An empty mailbox is a successful look that found nothing; a
51
+ // throw means we saw nothing and must not report SUCCESS or clear LastError.
52
+ return { Items: [], HighWatermark: null, Issues: [message], Failed: true };
53
+ }
54
+ }
55
+ /**
56
+ * The newest point this pass can honestly claim.
57
+ *
58
+ * Overridable for a transport that exposes a better signal than either item time or observation
59
+ * time (a provider `ModifiedAt`, a server-side cursor). Overriding it means taking on the rules
60
+ * in `watermark.ts` — read them first.
61
+ */
62
+ ComputeHighWatermark(items, observedAt) {
63
+ return ResolveHighWatermark(this.Kind, items, observedAt);
64
+ }
65
+ /** Seam for tests. Everything stored is UTC; a JS Date is a UTC instant. */
66
+ Now() {
67
+ return new Date();
68
+ }
69
+ // ── Hooks. All no-ops; override any subset. ────────────────────────────────────────────────
70
+ async OnBeforeFetch(_query) { }
71
+ async OnAfterFetch(_raw, _query) { }
72
+ async OnBeforeNormalize(_raw, _query) { }
73
+ async OnAfterNormalize(_items, _query) { }
74
+ async OnError(_error, _query) { }
75
+ }
76
+ //# sourceMappingURL=BaseActivitySyncProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BaseActivitySyncProvider.js","sourceRoot":"","sources":["../src/BaseActivitySyncProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEhD,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAStD,MAAM,OAAgB,wBAAwB;IAsB1C;;;OAGG;IACI,KAAK,CAAC,KAAK,CAAC,KAA0B;QACzC,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC9B,IAAI,CAAC;YACD,MAAM,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;YAChC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;YACvC,MAAM,IAAI,CAAC,YAAY,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YAEpC,MAAM,IAAI,CAAC,iBAAiB,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YACzC,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YACzC,MAAM,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;YAE1C,OAAO;gBACH,KAAK,EAAE,KAAK;gBACZ,aAAa,EAAE,IAAI,CAAC,oBAAoB,CAAC,KAAK,EAAE,UAAU,CAAC;gBAC3D,MAAM,EAAE,GAAG,CAAC,MAAM;aACrB,CAAC;QACN,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,QAAQ,CACJ,uDAAuD,IAAI,CAAC,gBAAgB,IAAI;gBAC5E,eAAe,KAAK,CAAC,OAAO,MAAM,OAAO,EAAE,CAClD,CAAC;YACF,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;YACjC,iFAAiF;YACjF,6EAA6E;YAC7E,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;QAC/E,CAAC;IACL,CAAC;IAED;;;;;;OAMG;IACO,oBAAoB,CAAC,KAAgC,EAAE,UAAgB;QAC7E,OAAO,oBAAoB,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC;IAC9D,CAAC;IAED,4EAA4E;IAClE,GAAG;QACT,OAAO,IAAI,IAAI,EAAE,CAAC;IACtB,CAAC;IAED,8FAA8F;IAEpF,KAAK,CAAC,aAAa,CAAC,MAA2B,IAAkB,CAAC;IAClE,KAAK,CAAC,YAAY,CAAC,IAAc,EAAE,MAA2B,IAAkB,CAAC;IACjF,KAAK,CAAC,iBAAiB,CAAC,IAAc,EAAE,MAA2B,IAAkB,CAAC;IACtF,KAAK,CAAC,gBAAgB,CAAC,MAAwB,EAAE,MAA2B,IAAkB,CAAC;IAC/F,KAAK,CAAC,OAAO,CAAC,MAAe,EAAE,MAA2B,IAAkB,CAAC;CAC1F"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Map a fleet run onto the Common.SyncActivities Action result codes.
3
+ *
4
+ * Lives next to FleetRunResult so the Action cannot re-invent a success path the engine
5
+ * already marked failed. The load-failed case (Success=false, ConnectionsAttempted=0) is
6
+ * not "no connections" — that is the same class of bug as treating a failed ViewLoad as [].
7
+ */
8
+ export interface FleetSurfaceResult {
9
+ Fetched: number;
10
+ Included: number;
11
+ Duplicates: number;
12
+ Excluded: number;
13
+ Failed: number;
14
+ }
15
+ export interface FleetLike {
16
+ Success: boolean;
17
+ ConnectionsAttempted: number;
18
+ Issues: readonly string[];
19
+ Results: ReadonlyArray<{
20
+ Result: FleetSurfaceResult;
21
+ }>;
22
+ }
23
+ export interface FleetTotals {
24
+ Fetched: number;
25
+ Written: number;
26
+ Duplicates: number;
27
+ Excluded: number;
28
+ Failed: number;
29
+ }
30
+ export type SyncActionResultCode = 'SUCCESS' | 'PARTIAL' | 'NO_CONNECTIONS' | 'ERROR';
31
+ export interface SyncActionResult {
32
+ Success: boolean;
33
+ ResultCode: SyncActionResultCode;
34
+ Message: string;
35
+ }
36
+ export declare function TotalsFromFleet(fleet: FleetLike): FleetTotals;
37
+ export declare function ActionResultFromFleet(fleet: FleetLike, totals: FleetTotals): SyncActionResult;
38
+ //# sourceMappingURL=action-result.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action-result.d.ts","sourceRoot":"","sources":["../src/action-result.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,WAAW,kBAAkB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,SAAS;IACtB,OAAO,EAAE,OAAO,CAAC;IACjB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1B,OAAO,EAAE,aAAa,CAAC;QAAE,MAAM,EAAE,kBAAkB,CAAA;KAAE,CAAC,CAAC;CAC1D;AAED,MAAM,WAAW,WAAW;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,oBAAoB,GAAG,SAAS,GAAG,SAAS,GAAG,gBAAgB,GAAG,OAAO,CAAC;AAEtF,MAAM,WAAW,gBAAgB;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,oBAAoB,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,GAAG,WAAW,CAW7D;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,GAAG,gBAAgB,CA0B7F"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Map a fleet run onto the Common.SyncActivities Action result codes.
3
+ *
4
+ * Lives next to FleetRunResult so the Action cannot re-invent a success path the engine
5
+ * already marked failed. The load-failed case (Success=false, ConnectionsAttempted=0) is
6
+ * not "no connections" — that is the same class of bug as treating a failed ViewLoad as [].
7
+ */
8
+ export function TotalsFromFleet(fleet) {
9
+ return fleet.Results.reduce((sum, run) => ({
10
+ Fetched: sum.Fetched + run.Result.Fetched,
11
+ Written: sum.Written + run.Result.Included,
12
+ Duplicates: sum.Duplicates + run.Result.Duplicates,
13
+ Excluded: sum.Excluded + run.Result.Excluded,
14
+ Failed: sum.Failed + run.Result.Failed,
15
+ }), { Fetched: 0, Written: 0, Duplicates: 0, Excluded: 0, Failed: 0 });
16
+ }
17
+ export function ActionResultFromFleet(fleet, totals) {
18
+ if (!fleet.Success && fleet.ConnectionsAttempted === 0) {
19
+ return {
20
+ Success: false,
21
+ ResultCode: 'ERROR',
22
+ Message: fleet.Issues.length
23
+ ? fleet.Issues.join(' | ')
24
+ : 'Activity sync failed before any connection was attempted.',
25
+ };
26
+ }
27
+ if (fleet.ConnectionsAttempted === 0) {
28
+ return {
29
+ Success: true,
30
+ ResultCode: 'NO_CONNECTIONS',
31
+ Message: 'No Active activity-sync connection exists, so nothing was read.',
32
+ };
33
+ }
34
+ return {
35
+ Success: fleet.Success,
36
+ ResultCode: fleet.Success ? 'SUCCESS' : 'PARTIAL',
37
+ Message: `${fleet.ConnectionsAttempted} connection(s): fetched ${totals.Fetched}, ` +
38
+ `written ${totals.Written}, duplicates ${totals.Duplicates}, ` +
39
+ `excluded ${totals.Excluded}, failed ${totals.Failed}.` +
40
+ (fleet.Issues.length ? ` Issues: ${fleet.Issues.join(' | ')}` : ''),
41
+ };
42
+ }
43
+ //# sourceMappingURL=action-result.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action-result.js","sourceRoot":"","sources":["../src/action-result.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAiCH,MAAM,UAAU,eAAe,CAAC,KAAgB;IAC5C,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,CACvB,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;QACX,OAAO,EAAE,GAAG,CAAC,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,OAAO;QACzC,OAAO,EAAE,GAAG,CAAC,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ;QAC1C,UAAU,EAAE,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,UAAU;QAClD,QAAQ,EAAE,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ;QAC5C,MAAM,EAAE,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM;KACzC,CAAC,EACF,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CACpE,CAAC;AACN,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,KAAgB,EAAE,MAAmB;IACvE,IAAI,CAAC,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,oBAAoB,KAAK,CAAC,EAAE,CAAC;QACrD,OAAO;YACH,OAAO,EAAE,KAAK;YACd,UAAU,EAAE,OAAO;YACnB,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM;gBACxB,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC;gBAC1B,CAAC,CAAC,2DAA2D;SACpE,CAAC;IACN,CAAC;IACD,IAAI,KAAK,CAAC,oBAAoB,KAAK,CAAC,EAAE,CAAC;QACnC,OAAO;YACH,OAAO,EAAE,IAAI;YACb,UAAU,EAAE,gBAAgB;YAC5B,OAAO,EAAE,iEAAiE;SAC7E,CAAC;IACN,CAAC;IACD,OAAO;QACH,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,UAAU,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;QACjD,OAAO,EACH,GAAG,KAAK,CAAC,oBAAoB,2BAA2B,MAAM,CAAC,OAAO,IAAI;YAC1E,WAAW,MAAM,CAAC,OAAO,gBAAgB,MAAM,CAAC,UAAU,IAAI;YAC9D,YAAY,MAAM,CAAC,QAAQ,YAAY,MAAM,CAAC,MAAM,GAAG;YACvD,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,YAAY,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC1E,CAAC;AACN,CAAC"}
@@ -0,0 +1,19 @@
1
+ /** Entity names as CodeGen registered them. One place so the engine and writer cannot drift. */
2
+ export declare const ACTIVITY_SYNC_ENTITIES: {
3
+ readonly Activities: "MJ_BizApps_Common: Activities";
4
+ readonly ActivityLinks: "MJ_BizApps_Common: Activity Links";
5
+ readonly ActivityTypes: "MJ_BizApps_Common: Activity Types";
6
+ readonly Connections: "MJ_BizApps_Common: Activity Sync Connections";
7
+ readonly ProviderTypes: "MJ_BizApps_Common: Activity Sync Provider Types";
8
+ readonly RuleSets: "MJ_BizApps_Common: Activity Sync Rule Sets";
9
+ readonly Rules: "MJ_BizApps_Common: Activity Sync Rules";
10
+ readonly ConnectionRuleSets: "MJ_BizApps_Common: Activity Sync Connection Rule Sets";
11
+ readonly Exclusions: "MJ_BizApps_Common: Activity Sync Exclusions";
12
+ readonly Runs: "MJ_BizApps_Common: Activity Sync Runs";
13
+ readonly RunDetails: "MJ_BizApps_Common: Activity Sync Run Details";
14
+ readonly Extensions: "MJ_BizApps_Common: Activity Sync Extensions";
15
+ readonly ContactMethods: "MJ_BizApps_Common: Contact Methods";
16
+ readonly People: "MJ_BizApps_Common: People";
17
+ readonly Organizations: "MJ_BizApps_Common: Organizations";
18
+ };
19
+ //# sourceMappingURL=entity-names.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entity-names.d.ts","sourceRoot":"","sources":["../src/entity-names.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAChG,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;CAgBzB,CAAC"}
@@ -0,0 +1,19 @@
1
+ /** Entity names as CodeGen registered them. One place so the engine and writer cannot drift. */
2
+ export const ACTIVITY_SYNC_ENTITIES = {
3
+ Activities: 'MJ_BizApps_Common: Activities',
4
+ ActivityLinks: 'MJ_BizApps_Common: Activity Links',
5
+ ActivityTypes: 'MJ_BizApps_Common: Activity Types',
6
+ Connections: 'MJ_BizApps_Common: Activity Sync Connections',
7
+ ProviderTypes: 'MJ_BizApps_Common: Activity Sync Provider Types',
8
+ RuleSets: 'MJ_BizApps_Common: Activity Sync Rule Sets',
9
+ Rules: 'MJ_BizApps_Common: Activity Sync Rules',
10
+ ConnectionRuleSets: 'MJ_BizApps_Common: Activity Sync Connection Rule Sets',
11
+ Exclusions: 'MJ_BizApps_Common: Activity Sync Exclusions',
12
+ Runs: 'MJ_BizApps_Common: Activity Sync Runs',
13
+ RunDetails: 'MJ_BizApps_Common: Activity Sync Run Details',
14
+ Extensions: 'MJ_BizApps_Common: Activity Sync Extensions',
15
+ ContactMethods: 'MJ_BizApps_Common: Contact Methods',
16
+ People: 'MJ_BizApps_Common: People',
17
+ Organizations: 'MJ_BizApps_Common: Organizations',
18
+ };
19
+ //# sourceMappingURL=entity-names.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entity-names.js","sourceRoot":"","sources":["../src/entity-names.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAChG,MAAM,CAAC,MAAM,sBAAsB,GAAG;IAClC,UAAU,EAAE,+BAA+B;IAC3C,aAAa,EAAE,mCAAmC;IAClD,aAAa,EAAE,mCAAmC;IAClD,WAAW,EAAE,8CAA8C;IAC3D,aAAa,EAAE,iDAAiD;IAChE,QAAQ,EAAE,4CAA4C;IACtD,KAAK,EAAE,wCAAwC;IAC/C,kBAAkB,EAAE,uDAAuD;IAC3E,UAAU,EAAE,6CAA6C;IACzD,IAAI,EAAE,uCAAuC;IAC7C,UAAU,EAAE,8CAA8C;IAC1D,UAAU,EAAE,6CAA6C;IACzD,cAAc,EAAE,oCAAoC;IACpD,MAAM,EAAE,2BAA2B;IACnC,aAAa,EAAE,kCAAkC;CAC3C,CAAC"}
@@ -0,0 +1,37 @@
1
+ import { type ActivityWriteContext, type BaseActivitySyncExtension } from './BaseActivitySyncExtension.js';
2
+ export interface ExtensionRegistration {
3
+ ID: string;
4
+ DriverClass: string;
5
+ Sequence: number;
6
+ FailurePolicy: 'Skip' | 'Abort';
7
+ TimeoutMS: number;
8
+ ActivitySyncConnectionID: string | null;
9
+ ActivitySyncProviderTypeID: string | null;
10
+ }
11
+ export interface ExtensionStamp {
12
+ ID: string;
13
+ LastError: string | null;
14
+ }
15
+ export interface ExtensionRunResult {
16
+ Aborted: boolean;
17
+ Errors: number;
18
+ Stamps: ExtensionStamp[];
19
+ }
20
+ /** Enabled rows that apply to this connection / provider type (null = global). */
21
+ export declare function ExtensionsExtraFilter(connectionID: string, providerTypeID: string | null): string;
22
+ /**
23
+ * Race `work` against TimeoutMS. This REJECTS THE WAITER; it does not cancel `work`.
24
+ *
25
+ * Pass an AbortController so Enrich can see `context.Signal.aborted` and stop. Work that
26
+ * ignores the signal keeps running inside the open write transaction.
27
+ */
28
+ export declare function WithTimeout<T>(work: Promise<T>, timeoutMS: number, label: string, controller?: AbortController): Promise<T>;
29
+ /**
30
+ * Run registered extensions in Sequence order.
31
+ *
32
+ * A missing DriverClass is a failure of that row, not a skip of the rest.
33
+ * Stamps are returned so LastError / LastRunAt persist OUTSIDE the activity
34
+ * transaction — an Abort rollback must not also erase the error that caused it.
35
+ */
36
+ export declare function RunRegisteredExtensions(context: ActivityWriteContext, rows: readonly ExtensionRegistration[], create: (driverClass: string) => BaseActivitySyncExtension | null): Promise<ExtensionRunResult>;
37
+ //# sourceMappingURL=extensions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extensions.d.ts","sourceRoot":"","sources":["../src/extensions.ts"],"names":[],"mappings":"AAQA,OAAO,EACH,KAAK,oBAAoB,EACzB,KAAK,yBAAyB,EACjC,MAAM,gCAAgC,CAAC;AAExC,MAAM,WAAW,qBAAqB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,wBAAwB,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,0BAA0B,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7C;AAED,MAAM,WAAW,cAAc;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,kBAAkB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,cAAc,EAAE,CAAC;CAC5B;AAED,kFAAkF;AAClF,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAOjG;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,CAAC,EAC/B,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,EAChB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,UAAU,CAAC,EAAE,eAAe,GAC7B,OAAO,CAAC,CAAC,CAAC,CAaZ;AAED;;;;;;GAMG;AACH,wBAAsB,uBAAuB,CACzC,OAAO,EAAE,oBAAoB,EAC7B,IAAI,EAAE,SAAS,qBAAqB,EAAE,EACtC,MAAM,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,yBAAyB,GAAG,IAAI,GAClE,OAAO,CAAC,kBAAkB,CAAC,CAkC7B"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * In-stream extension dispatch — Sequence order, timeout, Skip vs Abort.
3
+ *
4
+ * Runs inside the Activity write transaction. Skip records the error and lets
5
+ * the activity commit; Abort sets `Aborted` so the writer can roll the write
6
+ * back. Stamps are always returned so LastError survives an Abort rollback.
7
+ */
8
+ import { RequireUUID } from './sql.js';
9
+ /** Enabled rows that apply to this connection / provider type (null = global). */
10
+ export function ExtensionsExtraFilter(connectionID, providerTypeID) {
11
+ const conn = RequireUUID(connectionID, 'ActivitySyncConnectionID');
12
+ const connClause = `(ActivitySyncConnectionID IS NULL OR ActivitySyncConnectionID = '${conn}')`;
13
+ const typeClause = providerTypeID
14
+ ? `(ActivitySyncProviderTypeID IS NULL OR ActivitySyncProviderTypeID = '${RequireUUID(providerTypeID, 'ActivitySyncProviderTypeID')}')`
15
+ : 'ActivitySyncProviderTypeID IS NULL';
16
+ return `IsEnabled = 1 AND ${connClause} AND ${typeClause}`;
17
+ }
18
+ /**
19
+ * Race `work` against TimeoutMS. This REJECTS THE WAITER; it does not cancel `work`.
20
+ *
21
+ * Pass an AbortController so Enrich can see `context.Signal.aborted` and stop. Work that
22
+ * ignores the signal keeps running inside the open write transaction.
23
+ */
24
+ export async function WithTimeout(work, timeoutMS, label, controller) {
25
+ let timer;
26
+ const timeout = new Promise((_, reject) => {
27
+ timer = setTimeout(() => {
28
+ controller?.abort();
29
+ reject(new Error(`${label} timed out after ${timeoutMS}ms`));
30
+ }, timeoutMS);
31
+ });
32
+ try {
33
+ return await Promise.race([work, timeout]);
34
+ }
35
+ finally {
36
+ if (timer)
37
+ clearTimeout(timer);
38
+ }
39
+ }
40
+ /**
41
+ * Run registered extensions in Sequence order.
42
+ *
43
+ * A missing DriverClass is a failure of that row, not a skip of the rest.
44
+ * Stamps are returned so LastError / LastRunAt persist OUTSIDE the activity
45
+ * transaction — an Abort rollback must not also erase the error that caused it.
46
+ */
47
+ export async function RunRegisteredExtensions(context, rows, create) {
48
+ const ordered = [...rows].sort((a, b) => a.Sequence - b.Sequence);
49
+ const result = { Aborted: false, Errors: 0, Stamps: [] };
50
+ for (const row of ordered) {
51
+ const instance = create(row.DriverClass);
52
+ if (!instance) {
53
+ const message = `No BaseActivitySyncExtension registered for DriverClass '${row.DriverClass}'.`;
54
+ result.Errors++;
55
+ result.Stamps.push({ ID: row.ID, LastError: message });
56
+ if (row.FailurePolicy === 'Abort') {
57
+ result.Aborted = true;
58
+ return result;
59
+ }
60
+ continue;
61
+ }
62
+ const controller = new AbortController();
63
+ const timed = { ...context, Signal: controller.signal };
64
+ try {
65
+ await WithTimeout(instance.Enrich(timed), row.TimeoutMS, row.DriverClass, controller);
66
+ result.Stamps.push({ ID: row.ID, LastError: null });
67
+ }
68
+ catch (err) {
69
+ const message = err instanceof Error ? err.message : String(err);
70
+ result.Errors++;
71
+ result.Stamps.push({ ID: row.ID, LastError: message });
72
+ if (row.FailurePolicy === 'Abort') {
73
+ result.Aborted = true;
74
+ return result;
75
+ }
76
+ }
77
+ }
78
+ return result;
79
+ }
80
+ //# sourceMappingURL=extensions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extensions.js","sourceRoot":"","sources":["../src/extensions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Participant address → Person / Organization.
3
+ *
4
+ * An unmatched address becomes an unresolved ActivityLink (IdentityKind + IdentityValue).
5
+ * This file never creates a Person.
6
+ */
7
+ import { type UserInfo } from '@memberjunction/core';
8
+ import type { ResolvedParty, UnresolvedParty } from './BaseActivitySyncExtension.js';
9
+ import type { ItemParticipant } from './types.js';
10
+ export interface KnownAddress {
11
+ Address: string;
12
+ PersonID: string | null;
13
+ OrganizationID: string | null;
14
+ }
15
+ export interface IdentityResolution {
16
+ Resolved: ResolvedParty[];
17
+ Unresolved: UnresolvedParty[];
18
+ Known: Map<string, KnownAddress>;
19
+ LookupFailed: boolean;
20
+ }
21
+ export declare class IdentityResolver {
22
+ /**
23
+ * ONE query for the whole batch. A failed read is LookupFailed — not "nobody matched" —
24
+ * because those two look the same and only one may advance the watermark.
25
+ */
26
+ Resolve(participants: readonly ItemParticipant[], contextUser: UserInfo): Promise<IdentityResolution>;
27
+ private lookup;
28
+ }
29
+ export declare function Quote(value: string): string;
30
+ //# sourceMappingURL=identity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAW,KAAK,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAE9D,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAGrF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,MAAM,WAAW,YAAY;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,MAAM,WAAW,kBAAkB;IAC/B,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,UAAU,EAAE,eAAe,EAAE,CAAC;IAC9B,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACjC,YAAY,EAAE,OAAO,CAAC;CACzB;AASD,qBAAa,gBAAgB;IACzB;;;OAGG;IACU,OAAO,CAChB,YAAY,EAAE,SAAS,eAAe,EAAE,EACxC,WAAW,EAAE,QAAQ,GACtB,OAAO,CAAC,kBAAkB,CAAC;YA0ChB,MAAM;CAgCvB;AAED,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE3C"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Participant address → Person / Organization.
3
+ *
4
+ * An unmatched address becomes an unresolved ActivityLink (IdentityKind + IdentityValue).
5
+ * This file never creates a Person.
6
+ */
7
+ import { RunView } from '@memberjunction/core';
8
+ import { ACTIVITY_SYNC_ENTITIES } from './entity-names.js';
9
+ import { EscapeText, InList } from './sql.js';
10
+ export class IdentityResolver {
11
+ /**
12
+ * ONE query for the whole batch. A failed read is LookupFailed — not "nobody matched" —
13
+ * because those two look the same and only one may advance the watermark.
14
+ */
15
+ async Resolve(participants, contextUser) {
16
+ const addresses = [
17
+ ...new Set(participants.map((p) => p.Address.trim().toLowerCase()).filter(Boolean)),
18
+ ];
19
+ if (addresses.length === 0) {
20
+ return { Resolved: [], Unresolved: [], Known: new Map(), LookupFailed: false };
21
+ }
22
+ const { known, failed } = await this.lookup(addresses, contextUser);
23
+ const resolved = [];
24
+ const unresolved = [];
25
+ const seen = new Set();
26
+ for (const participant of participants) {
27
+ const address = participant.Address.trim().toLowerCase();
28
+ if (!address)
29
+ continue;
30
+ const hit = known.get(address);
31
+ if (hit?.PersonID) {
32
+ const key = `p:${hit.PersonID}:${participant.Role}`;
33
+ if (seen.has(key))
34
+ continue;
35
+ seen.add(key);
36
+ resolved.push({ Kind: 'Person', RecordID: hit.PersonID, Role: participant.Role });
37
+ }
38
+ else if (hit?.OrganizationID) {
39
+ const key = `o:${hit.OrganizationID}:${participant.Role}`;
40
+ if (seen.has(key))
41
+ continue;
42
+ seen.add(key);
43
+ resolved.push({ Kind: 'Organization', RecordID: hit.OrganizationID, Role: participant.Role });
44
+ }
45
+ else {
46
+ const key = `i:${participant.IdentityKind}:${address}`;
47
+ if (seen.has(key))
48
+ continue;
49
+ seen.add(key);
50
+ unresolved.push({
51
+ Kind: participant.IdentityKind,
52
+ Value: address,
53
+ Role: participant.Role,
54
+ });
55
+ }
56
+ }
57
+ return { Resolved: resolved, Unresolved: unresolved, Known: known, LookupFailed: failed };
58
+ }
59
+ async lookup(addresses, contextUser) {
60
+ const rv = new RunView();
61
+ const res = await rv.RunView({
62
+ EntityName: ACTIVITY_SYNC_ENTITIES.ContactMethods,
63
+ // Sargable equality. Literals are already lowercased. SQL Server CI collations
64
+ // match mixed-case stored values; wrapping the column in LOWER() would disable
65
+ // the index on the highest-cardinality lookup in a run. PostgreSQL is CS by
66
+ // default and wants citext or a functional index, not LOWER(Value) as a scan.
67
+ ExtraFilter: `Value IN (${InList(addresses)})`,
68
+ ResultType: 'simple',
69
+ }, contextUser);
70
+ if (!res.Success) {
71
+ return { known: new Map(), failed: true };
72
+ }
73
+ const known = new Map();
74
+ for (const row of res.Results ?? []) {
75
+ const address = String(row.Value ?? '').trim().toLowerCase();
76
+ if (!address)
77
+ continue;
78
+ known.set(address, {
79
+ Address: address,
80
+ PersonID: row.PersonID ?? null,
81
+ OrganizationID: row.OrganizationID ?? null,
82
+ });
83
+ }
84
+ return { known, failed: false };
85
+ }
86
+ }
87
+ export function Quote(value) {
88
+ return EscapeText(value);
89
+ }
90
+ //# sourceMappingURL=identity.js.map