@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.
- package/dist/ActivitySyncEngine.d.ts +118 -0
- package/dist/ActivitySyncEngine.d.ts.map +1 -0
- package/dist/ActivitySyncEngine.js +620 -0
- package/dist/ActivitySyncEngine.js.map +1 -0
- package/dist/BaseActivitySyncExtension.d.ts +99 -0
- package/dist/BaseActivitySyncExtension.d.ts.map +1 -0
- package/dist/BaseActivitySyncExtension.js +3 -0
- package/dist/BaseActivitySyncExtension.js.map +1 -0
- package/dist/BaseActivitySyncProvider.d.ts +40 -0
- package/dist/BaseActivitySyncProvider.d.ts.map +1 -0
- package/dist/BaseActivitySyncProvider.js +76 -0
- package/dist/BaseActivitySyncProvider.js.map +1 -0
- package/dist/action-result.d.ts +38 -0
- package/dist/action-result.d.ts.map +1 -0
- package/dist/action-result.js +43 -0
- package/dist/action-result.js.map +1 -0
- package/dist/entity-names.d.ts +19 -0
- package/dist/entity-names.d.ts.map +1 -0
- package/dist/entity-names.js +19 -0
- package/dist/entity-names.js.map +1 -0
- package/dist/extensions.d.ts +37 -0
- package/dist/extensions.d.ts.map +1 -0
- package/dist/extensions.js +80 -0
- package/dist/extensions.js.map +1 -0
- package/dist/identity.d.ts +30 -0
- package/dist/identity.d.ts.map +1 -0
- package/dist/identity.js +90 -0
- package/dist/identity.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/load.d.ts +17 -0
- package/dist/load.d.ts.map +1 -0
- package/dist/load.js +29 -0
- package/dist/load.js.map +1 -0
- package/dist/manual-log.d.ts +33 -0
- package/dist/manual-log.d.ts.map +1 -0
- package/dist/manual-log.js +230 -0
- package/dist/manual-log.js.map +1 -0
- package/dist/participants.d.ts +47 -0
- package/dist/participants.d.ts.map +1 -0
- package/dist/participants.js +59 -0
- package/dist/participants.js.map +1 -0
- package/dist/providers/FixtureActivitySyncProvider.d.ts +21 -0
- package/dist/providers/FixtureActivitySyncProvider.d.ts.map +1 -0
- package/dist/providers/FixtureActivitySyncProvider.js +58 -0
- package/dist/providers/FixtureActivitySyncProvider.js.map +1 -0
- package/dist/providers/GraphMessageMapper.d.ts +37 -0
- package/dist/providers/GraphMessageMapper.d.ts.map +1 -0
- package/dist/providers/GraphMessageMapper.js +91 -0
- package/dist/providers/GraphMessageMapper.js.map +1 -0
- package/dist/providers/MSGraphActivitySyncProvider.d.ts +22 -0
- package/dist/providers/MSGraphActivitySyncProvider.d.ts.map +1 -0
- package/dist/providers/MSGraphActivitySyncProvider.js +62 -0
- package/dist/providers/MSGraphActivitySyncProvider.js.map +1 -0
- package/dist/providers/MSGraphCalendarSyncProvider.d.ts +60 -0
- package/dist/providers/MSGraphCalendarSyncProvider.d.ts.map +1 -0
- package/dist/providers/MSGraphCalendarSyncProvider.js +145 -0
- package/dist/providers/MSGraphCalendarSyncProvider.js.map +1 -0
- package/dist/qualification.d.ts +91 -0
- package/dist/qualification.d.ts.map +1 -0
- package/dist/qualification.js +62 -0
- package/dist/qualification.js.map +1 -0
- package/dist/run.d.ts +67 -0
- package/dist/run.d.ts.map +1 -0
- package/dist/run.js +84 -0
- package/dist/run.js.map +1 -0
- package/dist/sql.d.ts +17 -0
- package/dist/sql.d.ts.map +1 -0
- package/dist/sql.js +50 -0
- package/dist/sql.js.map +1 -0
- package/dist/stages.d.ts +55 -0
- package/dist/stages.d.ts.map +1 -0
- package/dist/stages.js +109 -0
- package/dist/stages.js.map +1 -0
- package/dist/types.d.ts +130 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +38 -0
- package/dist/types.js.map +1 -0
- package/dist/watermark.d.ts +63 -0
- package/dist/watermark.d.ts.map +1 -0
- package/dist/watermark.js +103 -0
- package/dist/watermark.js.map +1 -0
- package/dist/writer.d.ts +132 -0
- package/dist/writer.d.ts.map +1 -0
- package/dist/writer.js +326 -0
- package/dist/writer.js.map +1 -0
- package/package.json +32 -5
- package/README.md +0 -12
package/dist/run.d.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The run vocabulary: activation, dry run, per-message decisions, and the
|
|
3
|
+
* provider→connection→rule policy chain.
|
|
4
|
+
*
|
|
5
|
+
* Everything here is pure. That is deliberate — these are the decisions a reviewer most needs to be
|
|
6
|
+
* able to reason about without a database, and the ones a dry run exists to preview.
|
|
7
|
+
*
|
|
8
|
+
* @module @mj-biz-apps/common-activity-sync
|
|
9
|
+
*/
|
|
10
|
+
/** Mirrors `CK_ActivitySyncConnection_Status`. */
|
|
11
|
+
export type ConnectionStatus = 'Active' | 'Paused' | 'Error' | 'Disabled';
|
|
12
|
+
/** Mirrors `CK_ActivitySyncRun_TriggerType`. */
|
|
13
|
+
export type SyncTriggerType = 'Scheduled' | 'Manual' | 'Webhook' | 'Backfill';
|
|
14
|
+
/** Mirrors `CK_ActivitySyncRunDetail_Decision`. */
|
|
15
|
+
export type SyncDecision = 'Included' | 'Excluded' | 'Duplicate' | 'Failed'
|
|
16
|
+
/** Dry-run outcomes. Nothing was written; this is what WOULD have happened. */
|
|
17
|
+
| 'WouldInclude' | 'WouldExclude';
|
|
18
|
+
/** Mirrors the `SkippedContentPolicy` CHECKs on both the provider type and the connection. */
|
|
19
|
+
export type SkippedContentPolicy = 'None' | 'SubjectEncrypted' | 'FullEncrypted';
|
|
20
|
+
export interface SyncRunOptions {
|
|
21
|
+
/**
|
|
22
|
+
* Evaluate and report without writing.
|
|
23
|
+
*
|
|
24
|
+
* A dry run fetches, qualifies and resolves exactly as a real run does, then writes only the
|
|
25
|
+
* `ActivitySyncRun` and its details — never an `Activity`, never an `ActivityLink`, never an
|
|
26
|
+
* attachment, and it never advances the watermark. It is the only safe way to see what a rule
|
|
27
|
+
* set would do to a real mailbox before pointing it at one, and `CK_ActivitySyncRun_DryRunNoWatermark`
|
|
28
|
+
* enforces the watermark half at the database rather than trusting the engine.
|
|
29
|
+
*/
|
|
30
|
+
DryRun: boolean;
|
|
31
|
+
TriggerType: SyncTriggerType;
|
|
32
|
+
/** Hard cap on items fetched this pass. */
|
|
33
|
+
Limit: number;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Whether a connection should sync right now.
|
|
37
|
+
*
|
|
38
|
+
* `Status` is the master switch; `StartAt`/`EndAt` are an activation WINDOW, so a mailbox can be
|
|
39
|
+
* provisioned in advance or retired on a date without anyone remembering to flip a switch. Both
|
|
40
|
+
* bounds are open when null, and the window only ever narrows what `Status` already allows.
|
|
41
|
+
*/
|
|
42
|
+
export declare function IsConnectionActive(status: ConnectionStatus, startAt: Date | null, endAt: Date | null, now: Date): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Resolve a policy down the chain, most specific wins, `null` meaning "inherit".
|
|
45
|
+
*
|
|
46
|
+
* The chain is always provider type → connection → (where it applies) rule. An operator configures
|
|
47
|
+
* storage, encryption and caps ONCE per provider; a connection overrides only when that mailbox is
|
|
48
|
+
* genuinely different. That is the difference between configuring a fleet and configuring a mailbox
|
|
49
|
+
* at a time.
|
|
50
|
+
*/
|
|
51
|
+
export declare function ResolvePolicy<T>(providerDefault: T, ...overrides: (T | null | undefined)[]): T;
|
|
52
|
+
/**
|
|
53
|
+
* Whether captured content may be retained for a message the engine declined to ingest.
|
|
54
|
+
*
|
|
55
|
+
* Retention is only ever permissible ENCRYPTED, so a policy above `None` without a key is not a
|
|
56
|
+
* configuration to honour — it is a misconfiguration to refuse. The database enforces the same pair
|
|
57
|
+
* on both ends (`CK_ActivitySyncProviderType_KeyRequired`, `CK_ActivitySyncRunDetail_ContentKey`);
|
|
58
|
+
* this is the runtime half, so the engine fails loudly rather than silently writing plaintext or
|
|
59
|
+
* silently writing nothing.
|
|
60
|
+
*/
|
|
61
|
+
export declare function ResolveCapturePlan(policy: SkippedContentPolicy, encryptionKeyID: string | null): {
|
|
62
|
+
Capture: 'None' | 'Subject' | 'Full';
|
|
63
|
+
EncryptionKeyID: string | null;
|
|
64
|
+
};
|
|
65
|
+
/** The dry-run counterpart of a decision, so a preview never claims to have written anything. */
|
|
66
|
+
export declare function AsDryRunDecision(decision: SyncDecision): SyncDecision;
|
|
67
|
+
//# sourceMappingURL=run.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,kDAAkD;AAClD,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,UAAU,CAAC;AAE1E,gDAAgD;AAChD,MAAM,MAAM,eAAe,GAAG,WAAW,GAAG,QAAQ,GAAG,SAAS,GAAG,UAAU,CAAC;AAE9E,mDAAmD;AACnD,MAAM,MAAM,YAAY,GAClB,UAAU,GACV,UAAU,GACV,WAAW,GACX,QAAQ;AACV,+EAA+E;GAC7E,cAAc,GACd,cAAc,CAAC;AAErB,8FAA8F;AAC9F,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,kBAAkB,GAAG,eAAe,CAAC;AAEjF,MAAM,WAAW,cAAc;IAC3B;;;;;;;;OAQG;IACH,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,eAAe,CAAC;IAC7B,2CAA2C;IAC3C,KAAK,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAC9B,MAAM,EAAE,gBAAgB,EACxB,OAAO,EAAE,IAAI,GAAG,IAAI,EACpB,KAAK,EAAE,IAAI,GAAG,IAAI,EAClB,GAAG,EAAE,IAAI,GACV,OAAO,CAiBT;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,eAAe,EAAE,CAAC,EAAE,GAAG,SAAS,EAAE,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,GAAG,CAAC,CAQ9F;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAC9B,MAAM,EAAE,oBAAoB,EAC5B,eAAe,EAAE,MAAM,GAAG,IAAI,GAC/B;IAAE,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAa1E;AAED,iGAAiG;AACjG,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,YAAY,GAAG,YAAY,CASrE"}
|
package/dist/run.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The run vocabulary: activation, dry run, per-message decisions, and the
|
|
3
|
+
* provider→connection→rule policy chain.
|
|
4
|
+
*
|
|
5
|
+
* Everything here is pure. That is deliberate — these are the decisions a reviewer most needs to be
|
|
6
|
+
* able to reason about without a database, and the ones a dry run exists to preview.
|
|
7
|
+
*
|
|
8
|
+
* @module @mj-biz-apps/common-activity-sync
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Whether a connection should sync right now.
|
|
12
|
+
*
|
|
13
|
+
* `Status` is the master switch; `StartAt`/`EndAt` are an activation WINDOW, so a mailbox can be
|
|
14
|
+
* provisioned in advance or retired on a date without anyone remembering to flip a switch. Both
|
|
15
|
+
* bounds are open when null, and the window only ever narrows what `Status` already allows.
|
|
16
|
+
*/
|
|
17
|
+
export function IsConnectionActive(status, startAt, endAt, now) {
|
|
18
|
+
/**
|
|
19
|
+
* `Error` retries. `Paused` and `Disabled` are a person deciding the mailbox
|
|
20
|
+
* is off and must be honoured. `Error` is a record of the last run and says
|
|
21
|
+
* nothing about whether the next one will work — refusing it latches a
|
|
22
|
+
* throttle into a permanent stop.
|
|
23
|
+
*/
|
|
24
|
+
if (status !== 'Active' && status !== 'Error') {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
if (startAt !== null && now.getTime() < startAt.getTime()) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
if (endAt !== null && now.getTime() > endAt.getTime()) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Resolve a policy down the chain, most specific wins, `null` meaning "inherit".
|
|
37
|
+
*
|
|
38
|
+
* The chain is always provider type → connection → (where it applies) rule. An operator configures
|
|
39
|
+
* storage, encryption and caps ONCE per provider; a connection overrides only when that mailbox is
|
|
40
|
+
* genuinely different. That is the difference between configuring a fleet and configuring a mailbox
|
|
41
|
+
* at a time.
|
|
42
|
+
*/
|
|
43
|
+
export function ResolvePolicy(providerDefault, ...overrides) {
|
|
44
|
+
for (let i = overrides.length - 1; i >= 0; i--) {
|
|
45
|
+
const candidate = overrides[i];
|
|
46
|
+
if (candidate !== null && candidate !== undefined) {
|
|
47
|
+
return candidate;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return providerDefault;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Whether captured content may be retained for a message the engine declined to ingest.
|
|
54
|
+
*
|
|
55
|
+
* Retention is only ever permissible ENCRYPTED, so a policy above `None` without a key is not a
|
|
56
|
+
* configuration to honour — it is a misconfiguration to refuse. The database enforces the same pair
|
|
57
|
+
* on both ends (`CK_ActivitySyncProviderType_KeyRequired`, `CK_ActivitySyncRunDetail_ContentKey`);
|
|
58
|
+
* this is the runtime half, so the engine fails loudly rather than silently writing plaintext or
|
|
59
|
+
* silently writing nothing.
|
|
60
|
+
*/
|
|
61
|
+
export function ResolveCapturePlan(policy, encryptionKeyID) {
|
|
62
|
+
if (policy === 'None') {
|
|
63
|
+
return { Capture: 'None', EncryptionKeyID: null };
|
|
64
|
+
}
|
|
65
|
+
if (encryptionKeyID === null) {
|
|
66
|
+
throw new Error(`SkippedContentPolicy is "${policy}" but no encryption key is configured. Retaining ` +
|
|
67
|
+
`content from a message that was deliberately not ingested is only permissible ` +
|
|
68
|
+
`encrypted — set an encryption key on the provider type or the connection, or set ` +
|
|
69
|
+
`the policy to "None".`);
|
|
70
|
+
}
|
|
71
|
+
return { Capture: policy === 'SubjectEncrypted' ? 'Subject' : 'Full', EncryptionKeyID: encryptionKeyID };
|
|
72
|
+
}
|
|
73
|
+
/** The dry-run counterpart of a decision, so a preview never claims to have written anything. */
|
|
74
|
+
export function AsDryRunDecision(decision) {
|
|
75
|
+
switch (decision) {
|
|
76
|
+
case 'Included':
|
|
77
|
+
return 'WouldInclude';
|
|
78
|
+
case 'Excluded':
|
|
79
|
+
return 'WouldExclude';
|
|
80
|
+
default:
|
|
81
|
+
return decision;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=run.js.map
|
package/dist/run.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAqCH;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAC9B,MAAwB,EACxB,OAAoB,EACpB,KAAkB,EAClB,GAAS;IAET;;;;;OAKG;IACH,IAAI,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,OAAO,EAAE,CAAC;QAC5C,OAAO,KAAK,CAAC;IACjB,CAAC;IACD,IAAI,OAAO,KAAK,IAAI,IAAI,GAAG,CAAC,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;QACxD,OAAO,KAAK,CAAC;IACjB,CAAC;IACD,IAAI,KAAK,KAAK,IAAI,IAAI,GAAG,CAAC,OAAO,EAAE,GAAG,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;QACpD,OAAO,KAAK,CAAC;IACjB,CAAC;IACD,OAAO,IAAI,CAAC;AAChB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAAI,eAAkB,EAAE,GAAG,SAAmC;IACvF,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7C,MAAM,SAAS,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;QAC/B,IAAI,SAAS,KAAK,IAAI,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAChD,OAAO,SAAS,CAAC;QACrB,CAAC;IACL,CAAC;IACD,OAAO,eAAe,CAAC;AAC3B,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB,CAC9B,MAA4B,EAC5B,eAA8B;IAE9B,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACpB,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC;IACtD,CAAC;IACD,IAAI,eAAe,KAAK,IAAI,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CACX,4BAA4B,MAAM,mDAAmD;YACjF,gFAAgF;YAChF,mFAAmF;YACnF,uBAAuB,CAC9B,CAAC;IACN,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,kBAAkB,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,EAAE,eAAe,EAAE,eAAe,EAAE,CAAC;AAC7G,CAAC;AAED,iGAAiG;AACjG,MAAM,UAAU,gBAAgB,CAAC,QAAsB;IACnD,QAAQ,QAAQ,EAAE,CAAC;QACf,KAAK,UAAU;YACX,OAAO,cAAc,CAAC;QAC1B,KAAK,UAAU;YACX,OAAO,cAAc,CAAC;QAC1B;YACI,OAAO,QAAQ,CAAC;IACxB,CAAC;AACL,CAAC"}
|
package/dist/sql.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Values that reach ExtraFilter. ExtraFilter is a string, not parameters.
|
|
3
|
+
*
|
|
4
|
+
* IDs: reject anything that is not a UUID. Addresses and other free text:
|
|
5
|
+
* coerce, drop NULs, double single quotes. A malformed id must not become a
|
|
6
|
+
* widened result set.
|
|
7
|
+
*/
|
|
8
|
+
export declare class InvalidFilterInputError extends Error {
|
|
9
|
+
constructor(message: string);
|
|
10
|
+
}
|
|
11
|
+
export declare function RequireUUID(value: string, field: string): string;
|
|
12
|
+
export declare function RequireUUIDs(values: readonly unknown[] | null | undefined, field: string): string[];
|
|
13
|
+
/** Free text for a SQL string literal (addresses, type codes, external ids). */
|
|
14
|
+
export declare function EscapeText(value: unknown): string;
|
|
15
|
+
export declare function InList(values: readonly unknown[]): string;
|
|
16
|
+
export declare function UuidInList(values: readonly unknown[], field: string): string;
|
|
17
|
+
//# sourceMappingURL=sql.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql.d.ts","sourceRoot":"","sources":["../src/sql.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,qBAAa,uBAAwB,SAAQ,KAAK;gBAClC,OAAO,EAAE,MAAM;CAI9B;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAKhE;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,GAAG,IAAI,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAQnG;AAED,gFAAgF;AAChF,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAIjD;AAED,wBAAgB,MAAM,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,GAAG,MAAM,CAKzD;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAM5E"}
|
package/dist/sql.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Values that reach ExtraFilter. ExtraFilter is a string, not parameters.
|
|
3
|
+
*
|
|
4
|
+
* IDs: reject anything that is not a UUID. Addresses and other free text:
|
|
5
|
+
* coerce, drop NULs, double single quotes. A malformed id must not become a
|
|
6
|
+
* widened result set.
|
|
7
|
+
*/
|
|
8
|
+
const UUID = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
9
|
+
export class InvalidFilterInputError extends Error {
|
|
10
|
+
constructor(message) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = 'InvalidFilterInputError';
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export function RequireUUID(value, field) {
|
|
16
|
+
if (typeof value !== 'string' || !UUID.test(value)) {
|
|
17
|
+
throw new InvalidFilterInputError(`${field} must be a UUID.`);
|
|
18
|
+
}
|
|
19
|
+
return value;
|
|
20
|
+
}
|
|
21
|
+
export function RequireUUIDs(values, field) {
|
|
22
|
+
if (!values?.length)
|
|
23
|
+
return [];
|
|
24
|
+
return values.map((v) => {
|
|
25
|
+
if (typeof v !== 'string') {
|
|
26
|
+
throw new InvalidFilterInputError(`${field} must be a UUID.`);
|
|
27
|
+
}
|
|
28
|
+
return RequireUUID(v, field);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
/** Free text for a SQL string literal (addresses, type codes, external ids). */
|
|
32
|
+
export function EscapeText(value) {
|
|
33
|
+
return String(value ?? '')
|
|
34
|
+
.replace(/\0/g, '')
|
|
35
|
+
.replace(/'/g, "''");
|
|
36
|
+
}
|
|
37
|
+
export function InList(values) {
|
|
38
|
+
if (values.length === 0) {
|
|
39
|
+
return `'00000000-0000-0000-0000-000000000000'`;
|
|
40
|
+
}
|
|
41
|
+
return values.map((v) => `'${EscapeText(v)}'`).join(',');
|
|
42
|
+
}
|
|
43
|
+
export function UuidInList(values, field) {
|
|
44
|
+
const ids = RequireUUIDs(values, field);
|
|
45
|
+
if (ids.length === 0) {
|
|
46
|
+
return `'00000000-0000-0000-0000-000000000000'`;
|
|
47
|
+
}
|
|
48
|
+
return ids.map((id) => `'${id}'`).join(',');
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=sql.js.map
|
package/dist/sql.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql.js","sourceRoot":"","sources":["../src/sql.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,IAAI,GACN,+EAA+E,CAAC;AAEpF,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IAC9C,YAAY,OAAe;QACvB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;IAC1C,CAAC;CACJ;AAED,MAAM,UAAU,WAAW,CAAC,KAAa,EAAE,KAAa;IACpD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACjD,MAAM,IAAI,uBAAuB,CAAC,GAAG,KAAK,kBAAkB,CAAC,CAAC;IAClE,CAAC;IACD,OAAO,KAAK,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAA6C,EAAE,KAAa;IACrF,IAAI,CAAC,MAAM,EAAE,MAAM;QAAE,OAAO,EAAE,CAAC;IAC/B,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACpB,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;YACxB,MAAM,IAAI,uBAAuB,CAAC,GAAG,KAAK,kBAAkB,CAAC,CAAC;QAClE,CAAC;QACD,OAAO,WAAW,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;AACP,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,UAAU,CAAC,KAAc;IACrC,OAAO,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC;SACrB,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;SAClB,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,MAA0B;IAC7C,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,wCAAwC,CAAC;IACpD,CAAC;IACD,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAA0B,EAAE,KAAa;IAChE,MAAM,GAAG,GAAG,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACxC,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnB,OAAO,wCAAwC,CAAC;IACpD,CAAC;IACD,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAChD,CAAC"}
|
package/dist/stages.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic qualification stages. Exclusions run first and are absolute.
|
|
3
|
+
*/
|
|
4
|
+
import { type ParticipantScope } from './participants.js';
|
|
5
|
+
import type { IQualificationStage, QualificationContext, QualificationVerdict } from './qualification.js';
|
|
6
|
+
import type { NormalizedItem } from './types.js';
|
|
7
|
+
export interface ExclusionRow {
|
|
8
|
+
ID: string;
|
|
9
|
+
IdentityKind: string | null;
|
|
10
|
+
IdentityValue: string;
|
|
11
|
+
ActivitySyncRuleSetID: string | null;
|
|
12
|
+
}
|
|
13
|
+
export interface RuleRow {
|
|
14
|
+
ID: string;
|
|
15
|
+
Sequence: number;
|
|
16
|
+
IsEnabled: boolean;
|
|
17
|
+
Action: 'Include' | 'Exclude';
|
|
18
|
+
Direction: 'Inbound' | 'Outbound' | 'Internal' | null;
|
|
19
|
+
DateFrom: Date | string | null;
|
|
20
|
+
DateTo: Date | string | null;
|
|
21
|
+
ParticipantScope: ParticipantScope | null;
|
|
22
|
+
ActivitySyncRuleSetID: string | null;
|
|
23
|
+
}
|
|
24
|
+
export interface KnownAddressHit {
|
|
25
|
+
Address: string;
|
|
26
|
+
}
|
|
27
|
+
export interface EngineQualificationContext extends QualificationContext {
|
|
28
|
+
Exclusions: ExclusionRow[];
|
|
29
|
+
Rules: RuleRow[];
|
|
30
|
+
InternalDomains: string[];
|
|
31
|
+
KnownAddresses: Map<string, KnownAddressHit>;
|
|
32
|
+
}
|
|
33
|
+
/** Stage 0. An exclusion is not a rule a later Include can outrank. */
|
|
34
|
+
export declare class ExclusionStage implements IQualificationStage {
|
|
35
|
+
readonly Name = "Exclusions";
|
|
36
|
+
readonly RequiresInference = false;
|
|
37
|
+
Evaluate(item: NormalizedItem, context: QualificationContext): Promise<QualificationVerdict>;
|
|
38
|
+
}
|
|
39
|
+
/** Stage 1. Bound rule sets, in sequence. */
|
|
40
|
+
export declare class RulesStage implements IQualificationStage {
|
|
41
|
+
readonly Name = "Rules";
|
|
42
|
+
readonly RequiresInference = false;
|
|
43
|
+
Evaluate(item: NormalizedItem, context: QualificationContext): Promise<QualificationVerdict>;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Stage 2. Exact ContactMethod address match — never a domain match.
|
|
47
|
+
* A hit Includes; no hit abstains so the provider-type default (Exclude for mailboxes) decides.
|
|
48
|
+
*/
|
|
49
|
+
export declare class KnownParticipantStage implements IQualificationStage {
|
|
50
|
+
readonly Name = "KnownParticipant";
|
|
51
|
+
readonly RequiresInference = false;
|
|
52
|
+
Evaluate(item: NormalizedItem, context: QualificationContext): Promise<QualificationVerdict>;
|
|
53
|
+
}
|
|
54
|
+
export declare function DefaultDeterministicStages(): IQualificationStage[];
|
|
55
|
+
//# sourceMappingURL=stages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stages.d.ts","sourceRoot":"","sources":["../src/stages.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAA2D,KAAK,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACnH,OAAO,KAAK,EACR,mBAAmB,EACnB,oBAAoB,EACpB,oBAAoB,EACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,WAAW,YAAY;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;CACxC;AAED,MAAM,WAAW,OAAO;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,SAAS,GAAG,SAAS,CAAC;IAC9B,SAAS,EAAE,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG,IAAI,CAAC;IACtD,QAAQ,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAAC;IAC/B,MAAM,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAAC;IAC7B,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC1C,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;CACxC;AAED,MAAM,WAAW,eAAe;IAC5B,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,0BAA2B,SAAQ,oBAAoB;IACpE,UAAU,EAAE,YAAY,EAAE,CAAC;IAC3B,KAAK,EAAE,OAAO,EAAE,CAAC;IACjB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;CAChD;AAQD,uEAAuE;AACvE,qBAAa,cAAe,YAAW,mBAAmB;IACtD,SAAgB,IAAI,gBAAgB;IACpC,SAAgB,iBAAiB,SAAS;IAE7B,QAAQ,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,oBAAoB,CAAC;CAsB5G;AAED,6CAA6C;AAC7C,qBAAa,UAAW,YAAW,mBAAmB;IAClD,SAAgB,IAAI,WAAW;IAC/B,SAAgB,iBAAiB,SAAS;IAE7B,QAAQ,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,oBAAoB,CAAC;CA0B5G;AAED;;;GAGG;AACH,qBAAa,qBAAsB,YAAW,mBAAmB;IAC7D,SAAgB,IAAI,sBAAsB;IAC1C,SAAgB,iBAAiB,SAAS;IAE7B,QAAQ,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,oBAAoB,CAAC;CAmB5G;AAED,wBAAgB,0BAA0B,IAAI,mBAAmB,EAAE,CAElE"}
|
package/dist/stages.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic qualification stages. Exclusions run first and are absolute.
|
|
3
|
+
*/
|
|
4
|
+
import { ClassifyParticipants, DomainOf, MatchesParticipantScope } from './participants.js';
|
|
5
|
+
function asDate(value) {
|
|
6
|
+
if (value == null)
|
|
7
|
+
return null;
|
|
8
|
+
const d = value instanceof Date ? value : new Date(value);
|
|
9
|
+
return Number.isNaN(d.getTime()) ? null : d;
|
|
10
|
+
}
|
|
11
|
+
/** Stage 0. An exclusion is not a rule a later Include can outrank. */
|
|
12
|
+
export class ExclusionStage {
|
|
13
|
+
constructor() {
|
|
14
|
+
this.Name = 'Exclusions';
|
|
15
|
+
this.RequiresInference = false;
|
|
16
|
+
}
|
|
17
|
+
async Evaluate(item, context) {
|
|
18
|
+
const ctx = context;
|
|
19
|
+
const addresses = item.Participants.map((p) => p.Address.trim().toLowerCase()).filter(Boolean);
|
|
20
|
+
for (const exclusion of ctx.Exclusions ?? []) {
|
|
21
|
+
const needle = exclusion.IdentityValue.trim().toLowerCase().replace(/^@/, '');
|
|
22
|
+
if (!needle)
|
|
23
|
+
continue;
|
|
24
|
+
for (const address of addresses) {
|
|
25
|
+
const domain = DomainOf(address);
|
|
26
|
+
const identityHit = address === needle || address.endsWith('@' + needle);
|
|
27
|
+
const domainHit = domain === needle;
|
|
28
|
+
if (identityHit || domainHit) {
|
|
29
|
+
return {
|
|
30
|
+
Decision: 'Exclude',
|
|
31
|
+
Reason: `Exclusion ${exclusion.ID} matched ${address}`,
|
|
32
|
+
StageName: this.Name,
|
|
33
|
+
ActivitySyncExclusionID: exclusion.ID,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return { Decision: 'Undecided', Reason: 'No exclusion matched', StageName: this.Name };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/** Stage 1. Bound rule sets, in sequence. */
|
|
42
|
+
export class RulesStage {
|
|
43
|
+
constructor() {
|
|
44
|
+
this.Name = 'Rules';
|
|
45
|
+
this.RequiresInference = false;
|
|
46
|
+
}
|
|
47
|
+
async Evaluate(item, context) {
|
|
48
|
+
const ctx = context;
|
|
49
|
+
const rules = [...(ctx.Rules ?? [])]
|
|
50
|
+
.filter((r) => r.IsEnabled)
|
|
51
|
+
.sort((a, b) => a.Sequence - b.Sequence);
|
|
52
|
+
for (const rule of rules) {
|
|
53
|
+
if (rule.Direction && rule.Direction !== item.Direction)
|
|
54
|
+
continue;
|
|
55
|
+
const from = asDate(rule.DateFrom);
|
|
56
|
+
const to = asDate(rule.DateTo);
|
|
57
|
+
if (from && item.StartedAt.getTime() < from.getTime())
|
|
58
|
+
continue;
|
|
59
|
+
if (to && item.StartedAt.getTime() > to.getTime())
|
|
60
|
+
continue;
|
|
61
|
+
if (rule.ParticipantScope && rule.ParticipantScope !== 'Any') {
|
|
62
|
+
const composition = ClassifyParticipants(item.Participants, ctx.InternalDomains ?? []);
|
|
63
|
+
if (!MatchesParticipantScope(rule.ParticipantScope, composition)) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
Decision: rule.Action,
|
|
69
|
+
Reason: `Rule ${rule.ID} ${rule.Action}`,
|
|
70
|
+
StageName: this.Name,
|
|
71
|
+
ActivitySyncRuleID: rule.ID,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
return { Decision: 'Undecided', Reason: 'No rule matched', StageName: this.Name };
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Stage 2. Exact ContactMethod address match — never a domain match.
|
|
79
|
+
* A hit Includes; no hit abstains so the provider-type default (Exclude for mailboxes) decides.
|
|
80
|
+
*/
|
|
81
|
+
export class KnownParticipantStage {
|
|
82
|
+
constructor() {
|
|
83
|
+
this.Name = 'KnownParticipant';
|
|
84
|
+
this.RequiresInference = false;
|
|
85
|
+
}
|
|
86
|
+
async Evaluate(item, context) {
|
|
87
|
+
const ctx = context;
|
|
88
|
+
const known = ctx.KnownAddresses ?? new Map();
|
|
89
|
+
for (const participant of item.Participants) {
|
|
90
|
+
const address = participant.Address.trim().toLowerCase();
|
|
91
|
+
if (address && known.has(address)) {
|
|
92
|
+
return {
|
|
93
|
+
Decision: 'Include',
|
|
94
|
+
Reason: `Known contact method ${address}`,
|
|
95
|
+
StageName: this.Name,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
Decision: 'Undecided',
|
|
101
|
+
Reason: 'No participant matched a stored ContactMethod',
|
|
102
|
+
StageName: this.Name,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
export function DefaultDeterministicStages() {
|
|
107
|
+
return [new ExclusionStage(), new RulesStage(), new KnownParticipantStage()];
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=stages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stages.js","sourceRoot":"","sources":["../src/stages.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The provider-neutral shapes every activity source speaks.
|
|
3
|
+
*
|
|
4
|
+
* Nothing downstream of a provider knows what produced an item, which is what makes a fixture
|
|
5
|
+
* provider a genuine substitute rather than a mock: the fixture and a live mailbox hand over the
|
|
6
|
+
* same type, so a test exercises the real code path instead of a parallel one.
|
|
7
|
+
*
|
|
8
|
+
* @module @mj-biz-apps/common-activity-sync
|
|
9
|
+
*/
|
|
10
|
+
/** Mirrors `CK_Activity_Direction`. */
|
|
11
|
+
export type ActivityDirection = 'Inbound' | 'Outbound' | 'Internal';
|
|
12
|
+
/** Mirrors `CK_ActivityLink_Role`. */
|
|
13
|
+
export type ActivityLinkRole = 'Regarding' | 'Participant' | 'From' | 'To' | 'Cc' | 'Bcc' | 'Organizer' | 'Attendee' | 'LoggedFor';
|
|
14
|
+
/** Mirrors `CK_ActivityLink_IdentityKind`. */
|
|
15
|
+
export type ActivityIdentityKind = 'Email' | 'Phone' | 'ExternalUser';
|
|
16
|
+
/**
|
|
17
|
+
* Runtime value lists for the two unions above, for validating link specs that arrive as data
|
|
18
|
+
* (JSON params on `Common.LogActivity`) rather than as typed code. The compiler checks every
|
|
19
|
+
* member is a legal union value; keep them in lockstep with the CHECK constraints.
|
|
20
|
+
*/
|
|
21
|
+
export declare const ACTIVITY_LINK_ROLES: readonly ActivityLinkRole[];
|
|
22
|
+
export declare const ACTIVITY_IDENTITY_KINDS: readonly ActivityIdentityKind[];
|
|
23
|
+
/**
|
|
24
|
+
* The surface a provider reads.
|
|
25
|
+
*
|
|
26
|
+
* This is not decoration — it selects the watermark basis. See {@link WatermarkBasisForKind}.
|
|
27
|
+
*/
|
|
28
|
+
export type ActivitySourceKind = 'Message' | 'Calendar' | 'Social' | 'Chat';
|
|
29
|
+
/**
|
|
30
|
+
* How a surface's watermark advances.
|
|
31
|
+
*
|
|
32
|
+
* `ItemTime` — the newest item's own timestamp. Valid only where a timestamp is always in the past.
|
|
33
|
+
* `ObservationTime` — when the source looked. Required wherever an item can be dated in the FUTURE.
|
|
34
|
+
*/
|
|
35
|
+
export type WatermarkBasis = 'ItemTime' | 'ObservationTime';
|
|
36
|
+
/** One party on an item, as the source reports them — an address, not yet a person. */
|
|
37
|
+
export interface ItemParticipant {
|
|
38
|
+
/** Lower-cased by the provider. Matching is case-insensitive and normalization belongs at the edge. */
|
|
39
|
+
Address: string;
|
|
40
|
+
Name: string | null;
|
|
41
|
+
Role: ActivityLinkRole;
|
|
42
|
+
/** Which identity column an unresolved link would use. */
|
|
43
|
+
IdentityKind: ActivityIdentityKind;
|
|
44
|
+
}
|
|
45
|
+
/** A provider-neutral item, ready for qualification. */
|
|
46
|
+
export interface NormalizedItem {
|
|
47
|
+
/**
|
|
48
|
+
* The provider's own stable id — half of the dedupe key, paired with `SourceSystem`.
|
|
49
|
+
*
|
|
50
|
+
* For Graph this is the message id, NOT the RFC-822 `Message-ID` header. The distinction
|
|
51
|
+
* matters: the RFC header is stable across mailboxes and the Graph id is not, so keying on the
|
|
52
|
+
* Graph id means a message ingested from two mailboxes yields two rows.
|
|
53
|
+
*/
|
|
54
|
+
ExternalID: string;
|
|
55
|
+
/** Groups replies. Becomes `Activity.ExternalThreadID`. */
|
|
56
|
+
ExternalThreadID: string | null;
|
|
57
|
+
/** `ActivityType.Code` — resolved by CODE so renaming a type never breaks ingestion. */
|
|
58
|
+
TypeCode: string;
|
|
59
|
+
/** Becomes `Activity.Title`. */
|
|
60
|
+
Subject: string;
|
|
61
|
+
/** Body or preview. Becomes `Activity.Description`. */
|
|
62
|
+
Body: string | null;
|
|
63
|
+
StartedAt: Date;
|
|
64
|
+
/** Set for calendar items; null for messages. */
|
|
65
|
+
EndedAt: Date | null;
|
|
66
|
+
/** Set for calendar items. */
|
|
67
|
+
Location: string | null;
|
|
68
|
+
Direction: ActivityDirection;
|
|
69
|
+
Participants: ItemParticipant[];
|
|
70
|
+
/**
|
|
71
|
+
* The item was called off and did not happen.
|
|
72
|
+
*
|
|
73
|
+
* First-class rather than something to dig out of `Raw`, because the write branches on it:
|
|
74
|
+
* `CK_Activity_Status` allows `'Cancelled'`, so a cancelled meeting is recorded as cancelled
|
|
75
|
+
* rather than as completed. Always false for messages — a sent mail cannot be un-sent.
|
|
76
|
+
*/
|
|
77
|
+
Cancelled: boolean;
|
|
78
|
+
/** The provider payload, verbatim. Stringified into `Activity.Details`. */
|
|
79
|
+
Raw: Record<string, unknown>;
|
|
80
|
+
}
|
|
81
|
+
/** What the engine asks a provider for. */
|
|
82
|
+
export interface ActivitySourceQuery {
|
|
83
|
+
/** The account/mailbox/handle to read, from `ActivitySyncConnection.Mailbox`. */
|
|
84
|
+
Mailbox: string;
|
|
85
|
+
/**
|
|
86
|
+
* The watermark — `ActivitySyncConnection.LastSyncAt`. Null on a first run.
|
|
87
|
+
*
|
|
88
|
+
* ADVISORY TO THE PROVIDER, NOT A GUARANTEE. Several transports expose no date filter at all
|
|
89
|
+
* (MJ's `GetMessagesParams` is `{ Identifier, NumMessages, UnreadOnly, IncludeHeaders }`), so a
|
|
90
|
+
* provider may have to fetch the most recent `Limit` and let the engine discard what it has
|
|
91
|
+
* already seen.
|
|
92
|
+
*/
|
|
93
|
+
Since: Date | null;
|
|
94
|
+
/** Hard cap on items returned. */
|
|
95
|
+
Limit: number;
|
|
96
|
+
}
|
|
97
|
+
/** Whatever a provider's transport handed back, before normalization. */
|
|
98
|
+
export interface RawBatch {
|
|
99
|
+
Payloads: Record<string, unknown>[];
|
|
100
|
+
/** Reported, never thrown — a partial batch is still worth filing. */
|
|
101
|
+
Issues: string[];
|
|
102
|
+
}
|
|
103
|
+
/** One provider pass. */
|
|
104
|
+
export interface ActivitySourceBatch {
|
|
105
|
+
Items: NormalizedItem[];
|
|
106
|
+
/**
|
|
107
|
+
* Advanced from the ITEMS (or the observation), never from a bare clock read at the end of the
|
|
108
|
+
* run: a wall-clock watermark skips anything that arrives while the run is in flight, whereas
|
|
109
|
+
* an item-derived one cannot, because it never claims to have seen past the newest thing it
|
|
110
|
+
* actually saw.
|
|
111
|
+
*/
|
|
112
|
+
HighWatermark: Date | null;
|
|
113
|
+
Issues: string[];
|
|
114
|
+
/**
|
|
115
|
+
* True when the provider did not complete a look — a throw from FetchRaw / transport / auth —
|
|
116
|
+
* not an empty mailbox and not a deliberate live-fetch refusal. The engine must not treat
|
|
117
|
+
* this as Success or clear LastError.
|
|
118
|
+
*/
|
|
119
|
+
Failed?: boolean;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* The watermark basis a surface requires.
|
|
123
|
+
*
|
|
124
|
+
* A CALENDAR source must never advance on `max(StartedAt)`. A meeting's start is routinely in the
|
|
125
|
+
* FUTURE, so one December event sets the watermark to December and every meeting created afterwards
|
|
126
|
+
* for an earlier date is filtered out as already-seen — permanently, and with no error. The
|
|
127
|
+
* calendar simply stops ingesting.
|
|
128
|
+
*/
|
|
129
|
+
export declare function WatermarkBasisForKind(kind: ActivitySourceKind): WatermarkBasis;
|
|
130
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,uCAAuC;AACvC,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,CAAC;AAEpE,sCAAsC;AACtC,MAAM,MAAM,gBAAgB,GACtB,WAAW,GACX,aAAa,GACb,MAAM,GACN,IAAI,GACJ,IAAI,GACJ,KAAK,GACL,WAAW,GACX,UAAU,GACV,WAAW,CAAC;AAElB,8CAA8C;AAC9C,MAAM,MAAM,oBAAoB,GAAG,OAAO,GAAG,OAAO,GAAG,cAAc,CAAC;AAEtE;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,SAAS,gBAAgB,EAU1D,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,SAAS,oBAAoB,EAAuC,CAAC;AAE3G;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,UAAU,GAAG,QAAQ,GAAG,MAAM,CAAC;AAE5E;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,iBAAiB,CAAC;AAE5D,uFAAuF;AACvF,MAAM,WAAW,eAAe;IAC5B,uGAAuG;IACvG,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,gBAAgB,CAAC;IACvB,0DAA0D;IAC1D,YAAY,EAAE,oBAAoB,CAAC;CACtC;AAED,wDAAwD;AACxD,MAAM,WAAW,cAAc;IAC3B;;;;;;OAMG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,wFAAwF;IACxF,QAAQ,EAAE,MAAM,CAAC;IACjB,gCAAgC;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,uDAAuD;IACvD,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,SAAS,EAAE,IAAI,CAAC;IAChB,iDAAiD;IACjD,OAAO,EAAE,IAAI,GAAG,IAAI,CAAC;IACrB,8BAA8B;IAC9B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,iBAAiB,CAAC;IAC7B,YAAY,EAAE,eAAe,EAAE,CAAC;IAChC;;;;;;OAMG;IACH,SAAS,EAAE,OAAO,CAAC;IACnB,2EAA2E;IAC3E,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,2CAA2C;AAC3C,MAAM,WAAW,mBAAmB;IAChC,iFAAiF;IACjF,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;;;OAOG;IACH,KAAK,EAAE,IAAI,GAAG,IAAI,CAAC;IACnB,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC;CACjB;AAED,yEAAyE;AACzE,MAAM,WAAW,QAAQ;IACrB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACpC,sEAAsE;IACtE,MAAM,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,yBAAyB;AACzB,MAAM,WAAW,mBAAmB;IAChC,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB;;;;;OAKG;IACH,aAAa,EAAE,IAAI,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,kBAAkB,GAAG,cAAc,CAE9E"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The provider-neutral shapes every activity source speaks.
|
|
3
|
+
*
|
|
4
|
+
* Nothing downstream of a provider knows what produced an item, which is what makes a fixture
|
|
5
|
+
* provider a genuine substitute rather than a mock: the fixture and a live mailbox hand over the
|
|
6
|
+
* same type, so a test exercises the real code path instead of a parallel one.
|
|
7
|
+
*
|
|
8
|
+
* @module @mj-biz-apps/common-activity-sync
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Runtime value lists for the two unions above, for validating link specs that arrive as data
|
|
12
|
+
* (JSON params on `Common.LogActivity`) rather than as typed code. The compiler checks every
|
|
13
|
+
* member is a legal union value; keep them in lockstep with the CHECK constraints.
|
|
14
|
+
*/
|
|
15
|
+
export const ACTIVITY_LINK_ROLES = [
|
|
16
|
+
'Regarding',
|
|
17
|
+
'Participant',
|
|
18
|
+
'From',
|
|
19
|
+
'To',
|
|
20
|
+
'Cc',
|
|
21
|
+
'Bcc',
|
|
22
|
+
'Organizer',
|
|
23
|
+
'Attendee',
|
|
24
|
+
'LoggedFor',
|
|
25
|
+
];
|
|
26
|
+
export const ACTIVITY_IDENTITY_KINDS = ['Email', 'Phone', 'ExternalUser'];
|
|
27
|
+
/**
|
|
28
|
+
* The watermark basis a surface requires.
|
|
29
|
+
*
|
|
30
|
+
* A CALENDAR source must never advance on `max(StartedAt)`. A meeting's start is routinely in the
|
|
31
|
+
* FUTURE, so one December event sets the watermark to December and every meeting created afterwards
|
|
32
|
+
* for an earlier date is filtered out as already-seen — permanently, and with no error. The
|
|
33
|
+
* calendar simply stops ingesting.
|
|
34
|
+
*/
|
|
35
|
+
export function WatermarkBasisForKind(kind) {
|
|
36
|
+
return kind === 'Calendar' ? 'ObservationTime' : 'ItemTime';
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAoBH;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAgC;IAC5D,WAAW;IACX,aAAa;IACb,MAAM;IACN,IAAI;IACJ,IAAI;IACJ,KAAK;IACL,WAAW;IACX,UAAU;IACV,WAAW;CACd,CAAC;AAEF,MAAM,CAAC,MAAM,uBAAuB,GAAoC,CAAC,OAAO,EAAE,OAAO,EAAE,cAAc,CAAC,CAAC;AA2G3G;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAwB;IAC1D,OAAO,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,UAAU,CAAC;AAChE,CAAC"}
|