@classytic/party 0.1.0 → 0.3.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/CHANGELOG.md +83 -0
- package/LICENSE +1 -1
- package/README.md +29 -4
- package/dist/events.d.ts +46 -9
- package/dist/events.js +60 -34
- package/dist/index.d.ts +410 -90
- package/dist/index.js +1000 -382
- package/dist/maintenance.d.ts +216 -0
- package/dist/maintenance.js +353 -0
- package/package.json +21 -10
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
import { OperationOptions, PaginationConfig, PluginType, Repository } from "@classytic/mongokit";
|
|
1
|
+
import { ModelBlueprint, OperationOptions, PaginationConfig, PluginType, Repository } from "@classytic/mongokit";
|
|
2
|
+
import { ResolvedTenantConfig, TenantConfig } from "@classytic/repo-core/tenant";
|
|
3
|
+
import { InProcessEventBus } from "@classytic/primitives/event-infra";
|
|
4
|
+
import { LegalForm } from "@classytic/primitives/legal-form";
|
|
2
5
|
import { Connection, Model, Schema, SchemaDefinitionProperty, Types } from "mongoose";
|
|
3
6
|
import { DomainEvent, EventHandler, EventMeta, EventTransport } from "@classytic/primitives/events";
|
|
7
|
+
import { ExternalRef, ExternalRef as ExternalRef$1, IdLike } from "@classytic/primitives/reference";
|
|
4
8
|
import { z } from "zod";
|
|
5
9
|
import { OutboxStore } from "@classytic/primitives/outbox";
|
|
6
10
|
import { OperationContext } from "@classytic/primitives/context";
|
|
7
|
-
import { ExternalRef, ExternalRef as ExternalRef$1 } from "@classytic/primitives/reference";
|
|
8
11
|
import { PersonName, PersonName as PersonName$1 } from "@classytic/primitives/person";
|
|
9
12
|
import { ContactAddress } from "@classytic/primitives/address";
|
|
13
|
+
import { SubjectRef } from "@classytic/primitives/subject";
|
|
10
14
|
import { HttpError } from "@classytic/repo-core/errors";
|
|
11
15
|
//#region src/domain/enums.d.ts
|
|
12
16
|
/**
|
|
@@ -38,12 +42,31 @@ type PartyStatus = (typeof PARTY_STATUSES)[number];
|
|
|
38
42
|
/** Channel kind for a contact point. Drives normalization + dedup keys. */
|
|
39
43
|
declare const CONTACT_POINT_TYPES: readonly ['email', 'phone'];
|
|
40
44
|
type ContactPointType = (typeof CONTACT_POINT_TYPES)[number];
|
|
45
|
+
/**
|
|
46
|
+
* Whether this channel may be used for MARKETING. Transactional messages (your
|
|
47
|
+
* booking is confirmed, your invoice is due) need no consent and are not what
|
|
48
|
+
* this records.
|
|
49
|
+
*
|
|
50
|
+
* Two states only, and `withdrawn` is deliberately not "absent": a channel that
|
|
51
|
+
* was never asked and a channel someone opted OUT of are the same silence to a
|
|
52
|
+
* sender but not to a regulator, and only the second one must survive a merge,
|
|
53
|
+
* a re-import, or the person booking again.
|
|
54
|
+
*/
|
|
55
|
+
declare const CONSENT_STATUSES: readonly ['granted', 'withdrawn'];
|
|
56
|
+
type ConsentStatus = (typeof CONSENT_STATUSES)[number];
|
|
41
57
|
/**
|
|
42
58
|
* Address role. Mirrors Odoo's `type` (contact/invoice/delivery/other) but
|
|
43
59
|
* as a first-class enum. A party carries many; `isDefault` picks the fallback.
|
|
44
60
|
*/
|
|
45
61
|
declare const ADDRESS_TYPES: readonly ['billing', 'shipping', 'contact', 'other'];
|
|
46
62
|
type AddressType = (typeof ADDRESS_TYPES)[number];
|
|
63
|
+
/**
|
|
64
|
+
* How a party is related to the reporting company (or to another party) — the related-party facts tax and
|
|
65
|
+
* disclosure rules read (IAS 24 in spirit). One party can hold several at once (a director who is also a
|
|
66
|
+
* shareholder); each is dated. A pack derives its own terms from these (a "sponsor shareholder").
|
|
67
|
+
*/
|
|
68
|
+
declare const RELATIONSHIP_KINDS: readonly ['shareholder', 'director', 'key_management', 'partner_in_firm', 'member', 'employee', 'lessor', 'associate'];
|
|
69
|
+
type RelationshipKind = (typeof RELATIONSHIP_KINDS)[number];
|
|
47
70
|
//#endregion
|
|
48
71
|
//#region src/domain/value-objects.d.ts
|
|
49
72
|
/**
|
|
@@ -65,11 +88,63 @@ interface PartyAddress extends ContactAddress {
|
|
|
65
88
|
*/
|
|
66
89
|
interface ContactPoint {
|
|
67
90
|
type: ContactPointType;
|
|
91
|
+
/**
|
|
92
|
+
* The CANONICAL form — a lowercased email, or an E.164 phone. Identity is the
|
|
93
|
+
* normalized value only; this is also what a notification transport should
|
|
94
|
+
* dial or mail, so a stored local spelling is a second bug waiting behind the
|
|
95
|
+
* dedup one.
|
|
96
|
+
*/
|
|
68
97
|
value: string;
|
|
69
98
|
/** Normalized dedup key — the `identityKeys` index entry. */
|
|
70
99
|
key: string;
|
|
100
|
+
/**
|
|
101
|
+
* What the operator actually TYPED, kept only when it differs from `value`.
|
|
102
|
+
* Support-facing provenance ("the till shows 01712345678, the invoice shows
|
|
103
|
+
* +8801712345678 — same number"). Never an identity signal: it is not keyed,
|
|
104
|
+
* not indexed and not matched on.
|
|
105
|
+
*/
|
|
106
|
+
input?: string;
|
|
71
107
|
isPrimary: boolean;
|
|
72
108
|
verified: boolean;
|
|
109
|
+
/**
|
|
110
|
+
* Permission to MARKET to this channel, and the proof of it. Absent means
|
|
111
|
+
* never asked — which is a refusal to a sender, so treat it as one.
|
|
112
|
+
*
|
|
113
|
+
* Here rather than in a CRM package because consent is a property of the
|
|
114
|
+
* CHANNEL, exactly like `verified`: it is the answer to "may I mail this
|
|
115
|
+
* address", not a domain opinion about the person. Campaign subscriptions,
|
|
116
|
+
* sequences, suppression lists and unsubscribe tokens stay in CRM keyed by
|
|
117
|
+
* `partyId` — this is the one fact they all have to agree with, and a sender
|
|
118
|
+
* that disagrees with it is the bug this field exists to make visible.
|
|
119
|
+
*
|
|
120
|
+
* In a multi-tenant deployment a party row is already per-organization, so
|
|
121
|
+
* this is one business's permission and never a platform-wide one — which is
|
|
122
|
+
* what "specific and informed" requires.
|
|
123
|
+
*/
|
|
124
|
+
consent?: ChannelConsent;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* A consent decision, with what a regulator asks for when they ask you to
|
|
128
|
+
* prove it: what was decided, WHEN, and where the person was standing.
|
|
129
|
+
*
|
|
130
|
+
* Immutable by convention — a change of mind is a new record replacing this
|
|
131
|
+
* one, never an edit, so `at` always dates the CURRENT status.
|
|
132
|
+
*/
|
|
133
|
+
interface ChannelConsent {
|
|
134
|
+
status: ConsentStatus;
|
|
135
|
+
/** The moment the person decided. */
|
|
136
|
+
at: Date;
|
|
137
|
+
/**
|
|
138
|
+
* The surface that asked — `'booking-form'`, `'dashboard'`, `'import:2019-mailchimp'`.
|
|
139
|
+
* Free text on purpose: the kernel cannot enumerate a host's screens, and an
|
|
140
|
+
* imported consent whose origin is unrecorded is one you cannot defend.
|
|
141
|
+
*/
|
|
142
|
+
source?: string;
|
|
143
|
+
/**
|
|
144
|
+
* Caller IP at the moment of GRANT, when the host has one. Never backfilled:
|
|
145
|
+
* an IP attached later proves nothing and is then just retained PII.
|
|
146
|
+
*/
|
|
147
|
+
ip?: string;
|
|
73
148
|
}
|
|
74
149
|
/**
|
|
75
150
|
* Statutory tax identity. Country-agnostic slots — a BD deployment fills
|
|
@@ -177,13 +252,7 @@ interface PartyLogger {
|
|
|
177
252
|
//#region src/models/tenant-field.d.ts
|
|
178
253
|
type TenantFieldType = 'objectId' | 'string';
|
|
179
254
|
interface SchemaBuildConfig {
|
|
180
|
-
|
|
181
|
-
tenantFieldType: TenantFieldType;
|
|
182
|
-
/**
|
|
183
|
-
* When `false`, models carry NO tenant column (company-wide identity). The
|
|
184
|
-
* engine wires no `multiTenantPlugin` either. Default `true`.
|
|
185
|
-
*/
|
|
186
|
-
multiTenant: boolean;
|
|
255
|
+
tenant: ResolvedTenantConfig;
|
|
187
256
|
}
|
|
188
257
|
/**
|
|
189
258
|
* Tenant-column schema definition, or `undefined` when the engine is
|
|
@@ -192,6 +261,19 @@ interface SchemaBuildConfig {
|
|
|
192
261
|
declare function tenantFieldDef(config: SchemaBuildConfig): SchemaDefinitionProperty | undefined;
|
|
193
262
|
//#endregion
|
|
194
263
|
//#region src/models/party.model.d.ts
|
|
264
|
+
interface PartyRelationship {
|
|
265
|
+
_id: Types.ObjectId;
|
|
266
|
+
kind: RelationshipKind;
|
|
267
|
+
/** The other party; absent means the reporting company itself. */
|
|
268
|
+
relatedTo?: Types.ObjectId | string | null;
|
|
269
|
+
/** Shareholding detail when `kind: 'shareholder'`. */
|
|
270
|
+
shareClass?: string | null;
|
|
271
|
+
sharePercent?: number | null;
|
|
272
|
+
from: Date;
|
|
273
|
+
/** Exclusive end; absent while it holds. */
|
|
274
|
+
to?: Date | null;
|
|
275
|
+
note?: string | null;
|
|
276
|
+
}
|
|
195
277
|
interface IPartyDocument {
|
|
196
278
|
_id: Types.ObjectId;
|
|
197
279
|
/** Present only when the engine runs multi-tenant; absent for company-wide identity. */
|
|
@@ -214,6 +296,10 @@ interface IPartyDocument {
|
|
|
214
296
|
/** Set when `status: 'merged'` — the survivor this party redirects to. */
|
|
215
297
|
mergedInto?: Types.ObjectId | string;
|
|
216
298
|
tags: string[];
|
|
299
|
+
/** What the law treats the party as (a sole trader is an `individual`). Absent: not stated. */
|
|
300
|
+
legalForm?: LegalForm;
|
|
301
|
+
/** Dated relationships to the reporting company or another party. */
|
|
302
|
+
relationships: PartyRelationship[];
|
|
217
303
|
metadata?: Record<string, unknown>;
|
|
218
304
|
/** Optimistic-concurrency counter (drives `claimVersion`). */
|
|
219
305
|
version: number;
|
|
@@ -226,15 +312,17 @@ declare function buildPartySchema(config: SchemaBuildConfig): Schema<IPartyDocum
|
|
|
226
312
|
interface PartyModels {
|
|
227
313
|
Party: Model<IPartyDocument>;
|
|
228
314
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
interface CreateModelsOptions extends SchemaBuildConfig {
|
|
232
|
-
/** On a name collision: `false` (default) throws; `true` deletes + re-registers (tests). */
|
|
315
|
+
interface DefinePartyModelsConfig extends SchemaBuildConfig {
|
|
316
|
+
/** On a name collision: `false` (default) throws; `true` re-registers (test/dev fixtures). */
|
|
233
317
|
forceRecreate?: boolean;
|
|
234
318
|
/** Mongoose auto-index builds. Default `true` (dev); `false` in prod (§35). */
|
|
235
319
|
autoIndex?: boolean;
|
|
236
320
|
}
|
|
237
|
-
|
|
321
|
+
/**
|
|
322
|
+
* Describe the Party model set as a connection-free blueprint. Pure — builds no schema and
|
|
323
|
+
* registers nothing until `.bind(connection)`.
|
|
324
|
+
*/
|
|
325
|
+
declare function definePartyModels(config: DefinePartyModelsConfig): ModelBlueprint<PartyModels>;
|
|
238
326
|
//#endregion
|
|
239
327
|
//#region src/events/dispatch.d.ts
|
|
240
328
|
/** Context key carrying a post-commit publish queue (attached by `withTransaction`). */
|
|
@@ -268,12 +356,45 @@ declare class PartyEventDispatcher {
|
|
|
268
356
|
declare function flushPending(dispatcher: PartyEventDispatcher, queue: DomainEvent[], logger?: PartyLogger): Promise<void>;
|
|
269
357
|
//#endregion
|
|
270
358
|
//#region src/repositories/party.repository.d.ts
|
|
359
|
+
/** What a host asks `recordConsent` to write. */
|
|
360
|
+
interface RecordConsentInput {
|
|
361
|
+
channel: ContactPointType;
|
|
362
|
+
/** The channel as the person gave it; normalized here before it is matched. */
|
|
363
|
+
value: string;
|
|
364
|
+
status: ConsentStatus;
|
|
365
|
+
/** The surface that asked — see `ChannelConsent.source`. */
|
|
366
|
+
source?: string;
|
|
367
|
+
/** Caller IP. Kept only on a grant. */
|
|
368
|
+
ip?: string;
|
|
369
|
+
/**
|
|
370
|
+
* When they said it. Defaults to now; supply it only when replaying a real
|
|
371
|
+
* historical decision (a migration), never to backdate a fresh one.
|
|
372
|
+
*/
|
|
373
|
+
at?: Date;
|
|
374
|
+
}
|
|
271
375
|
interface PartyRepositoryDeps {
|
|
272
376
|
events: PartyEventDispatcher;
|
|
273
377
|
logger?: PartyLogger | undefined;
|
|
378
|
+
/**
|
|
379
|
+
* ISO 3166-1 alpha-2 region used to interpret phone input that carries no
|
|
380
|
+
* country code. Comes from `PartyShape.defaultPhoneRegion`, i.e. the
|
|
381
|
+
* deployment — this kernel names no country.
|
|
382
|
+
*
|
|
383
|
+
* Held on the repository rather than passed per call so `resolveOrCreate` and
|
|
384
|
+
* `syncIdentity` cannot disagree: a caller-supplied region would let the
|
|
385
|
+
* create path and the sync path key the same human differently, which is the
|
|
386
|
+
* split this whole change removes.
|
|
387
|
+
*/
|
|
388
|
+
defaultPhoneRegion?: string | undefined;
|
|
389
|
+
/** Deployment-wide contact channels used as authoritative identity keys. */
|
|
390
|
+
identityContactTypes?: readonly ContactPointType[] | undefined;
|
|
274
391
|
}
|
|
275
392
|
declare class PartyRepository extends Repository<IPartyDocument> implements PartyPort<IPartyDocument> {
|
|
276
393
|
private readonly events;
|
|
394
|
+
/** The ONE interpretation of free-form contact input in this deployment. */
|
|
395
|
+
private readonly normalize;
|
|
396
|
+
/** Contact data can exist without being strong enough to collapse two parties. */
|
|
397
|
+
private readonly identityContactTypes;
|
|
277
398
|
constructor(model: Model<IPartyDocument>, deps: PartyRepositoryDeps, plugins?: PluginType[]);
|
|
278
399
|
/** `update` that never returns null — the party was loaded first; a null means a concurrent delete. */
|
|
279
400
|
private mustUpdate;
|
|
@@ -291,15 +412,46 @@ declare class PartyRepository extends Repository<IPartyDocument> implements Part
|
|
|
291
412
|
syncIdentity(id: string, input: ResolvePartyInput, ctx: PartyContext): Promise<IPartyDocument>;
|
|
292
413
|
addRole(id: string, role: PartyRole, ctx: PartyContext): Promise<IPartyDocument>;
|
|
293
414
|
removeRole(id: string, role: PartyRole, ctx: PartyContext): Promise<IPartyDocument>;
|
|
415
|
+
/**
|
|
416
|
+
* Record a dated relationship (shareholder, director, lessor, …) to the reporting company or another
|
|
417
|
+
* party. Refused when one of the same kind with the same counterpart is already in force over the dates:
|
|
418
|
+
* two overlapping rows would double a shareholding, and only one of them could be ended.
|
|
419
|
+
*/
|
|
420
|
+
addRelationship(id: string, input: AddRelationshipInput, ctx: PartyContext): Promise<IPartyDocument>;
|
|
421
|
+
/** End a relationship on a date (exclusive). Ending one already ended earlier is refused, never moved. */
|
|
422
|
+
endRelationship(id: string, relationshipId: string, to: Date, ctx: PartyContext): Promise<IPartyDocument>;
|
|
423
|
+
/** The relationships in force on a date — following a merged party to its survivor. */
|
|
424
|
+
relationshipsOn(id: string, on: Date, ctx: PartyContext): Promise<PartyRelationship[]>;
|
|
294
425
|
linkUser(id: string, userId: string, ctx: PartyContext): Promise<IPartyDocument>;
|
|
426
|
+
/**
|
|
427
|
+
* Record what this person said about being MARKETED to on one channel.
|
|
428
|
+
*
|
|
429
|
+
* Addressed by the channel's raw value (what they typed) and normalized here,
|
|
430
|
+
* so a host passing `07700 900000` and a host passing `+447700900000` reach
|
|
431
|
+
* the same contact point — consent landing on a second, differently-spelled
|
|
432
|
+
* row is an opt-out that no sender would ever find.
|
|
433
|
+
*
|
|
434
|
+
* A channel the party does not hold is a typed error rather than a silent
|
|
435
|
+
* no-op: a grant that went nowhere looks identical to one that was stored,
|
|
436
|
+
* and the caller is usually a booking form that will never check again.
|
|
437
|
+
*/
|
|
438
|
+
recordConsent(id: string, input: RecordConsentInput, ctx: PartyContext): Promise<IPartyDocument>;
|
|
295
439
|
attachExternalRef(id: string, ref: ExternalRef, ctx: PartyContext): Promise<IPartyDocument>;
|
|
296
440
|
archive(id: string, ctx: PartyContext): Promise<IPartyDocument>;
|
|
297
441
|
restore(id: string, ctx: PartyContext): Promise<IPartyDocument>;
|
|
298
442
|
/**
|
|
299
443
|
* Merge `sourceId` into `targetId`: the survivor absorbs the loser's identity
|
|
300
|
-
* keys, contact points, addresses, roles, external refs,
|
|
301
|
-
*
|
|
302
|
-
*
|
|
444
|
+
* keys, contact points, addresses, roles, external refs, user links, tags, and
|
|
445
|
+
* metadata, and FILLS its empty scalar slots (`taxIdentity`, `personName`,
|
|
446
|
+
* `legalName`, `parentId`) from the loser — never overwriting a populated
|
|
447
|
+
* survivor field. The loser becomes a terminal `merged` redirect
|
|
448
|
+
* (`mergedInto`). Two-document write → runs in a transaction (falls back to
|
|
449
|
+
* sequential on standalone Mongo).
|
|
450
|
+
*
|
|
451
|
+
* Concurrency: the loser is claimed `active|archived → merged` via CAS
|
|
452
|
+
* (`repo.claim`), so a second concurrent merge of the same source loses the
|
|
453
|
+
* race and surfaces a typed `PartyMergeError` — never a raw `E11000`. A
|
|
454
|
+
* duplicate-key collision while adopting the loser's keys maps to the same.
|
|
303
455
|
*/
|
|
304
456
|
merge(sourceId: string, targetId: string, ctx: PartyContext): Promise<IPartyDocument>;
|
|
305
457
|
/** Load a party by ObjectId hex, or throw the typed not-found (§P9-style). */
|
|
@@ -309,46 +461,27 @@ declare class PartyRepository extends Repository<IPartyDocument> implements Part
|
|
|
309
461
|
/** Route a domain event with meta stamped from ctx + the party's org. */
|
|
310
462
|
private dispatch;
|
|
311
463
|
}
|
|
464
|
+
interface AddRelationshipInput {
|
|
465
|
+
kind: RelationshipKind;
|
|
466
|
+
/** The other party; omit for the reporting company. */
|
|
467
|
+
relatedTo?: string | null;
|
|
468
|
+
from: Date;
|
|
469
|
+
to?: Date | null;
|
|
470
|
+
shareClass?: string;
|
|
471
|
+
sharePercent?: number;
|
|
472
|
+
note?: string;
|
|
473
|
+
}
|
|
312
474
|
//#endregion
|
|
313
475
|
//#region src/engine/engine-types.d.ts
|
|
314
|
-
interface PartyConfig {
|
|
315
|
-
/** Mongoose connection — the package creates its model on this connection. */
|
|
316
|
-
connection: Connection;
|
|
317
|
-
/**
|
|
318
|
-
* Multi-tenant scoping. Identity is FREQUENTLY company-wide (one customer is
|
|
319
|
-
* the same person in every branch) — such hosts pass `false` and the model
|
|
320
|
-
* carries no tenant column. SaaS / true-isolation hosts pass `true` (default).
|
|
321
|
-
*/
|
|
322
|
-
multiTenant?: boolean;
|
|
323
|
-
/**
|
|
324
|
-
* Tenant column shape when `multiTenant` is on. `'objectId'` (default)
|
|
325
|
-
* matches Better Auth orgs (unlocks `$lookup`/`.populate()`); `'string'`
|
|
326
|
-
* matches UUID/slug hosts. Threaded into schema + plugin (§9.2).
|
|
327
|
-
*/
|
|
328
|
-
tenantFieldType?: TenantFieldType;
|
|
329
|
-
/** Arc-compatible event transport. Default: in-process bus. */
|
|
330
|
-
eventTransport?: EventTransport;
|
|
331
|
-
/** Transactional outbox (§P8). When wired, events persist in the write's session. */
|
|
332
|
-
outbox?: OutboxStore;
|
|
333
|
-
/** Logger surface for the in-process bus + repository. */
|
|
334
|
-
logger?: PartyLogger;
|
|
335
|
-
/** Pagination defaults for the repository (mongokit). */
|
|
336
|
-
pagination?: PaginationConfig;
|
|
337
|
-
/** Extra mongokit plugins applied after the defaults. */
|
|
338
|
-
plugins?: PluginType[];
|
|
339
|
-
/** On a model-name collision: `false` (default) throws; `true` re-registers (tests, §21). */
|
|
340
|
-
forceRecreate?: boolean;
|
|
341
|
-
/** Mongoose auto-index builds. Default `true` (dev); `false` in prod (§35). */
|
|
342
|
-
autoIndex?: boolean;
|
|
343
|
-
/** Downgrade the boot `transactions` capability requirement to a warning (standalone Mongo). */
|
|
344
|
-
allowNonTransactional?: boolean;
|
|
345
|
-
}
|
|
346
476
|
interface ResolvedPartyConfig {
|
|
347
477
|
connection: Connection;
|
|
348
|
-
|
|
349
|
-
tenantFieldType: TenantFieldType;
|
|
478
|
+
tenant: Readonly<ResolvedTenantConfig>;
|
|
350
479
|
forceRecreate: boolean;
|
|
351
480
|
autoIndex: boolean;
|
|
481
|
+
/** ISO 3166-1 alpha-2 region for national-format phone input; unset ⇒ E.164 only. */
|
|
482
|
+
defaultPhoneRegion?: string;
|
|
483
|
+
/** Contact channels used as authoritative unique identity keys. */
|
|
484
|
+
identityContactTypes: readonly ContactPointType[];
|
|
352
485
|
allowNonTransactional: boolean;
|
|
353
486
|
logger?: PartyLogger;
|
|
354
487
|
pagination?: PaginationConfig;
|
|
@@ -380,16 +513,141 @@ interface PartyEngine {
|
|
|
380
513
|
withTransaction<T>(ctx: PartyContext, body: (txCtx: PartyContext) => Promise<T>): Promise<T>;
|
|
381
514
|
/** Build/align indexes. Call once per deploy when booted `autoIndex: false` (§35). */
|
|
382
515
|
syncIndexes(): Promise<void>;
|
|
383
|
-
/** Release resources. Idempotent. */
|
|
384
|
-
|
|
516
|
+
/** Release resources owned by this engine. Idempotent. The kernel-standard lifecycle name. */
|
|
517
|
+
close(): Promise<void>;
|
|
385
518
|
}
|
|
386
519
|
//#endregion
|
|
387
|
-
//#region src/engine/
|
|
388
|
-
|
|
520
|
+
//#region src/engine/define-party.d.ts
|
|
521
|
+
/**
|
|
522
|
+
* DESCRIBE-time shape — everything that determines the persistence SHAPE and needs no
|
|
523
|
+
* connection or I/O to inspect. Nothing here opens a socket or reads a secret.
|
|
524
|
+
*/
|
|
525
|
+
interface PartyShape {
|
|
526
|
+
/** Company-wide identity (`false`, no tenant column) vs isolated (`true`, default). */
|
|
527
|
+
tenant?: boolean | TenantConfig;
|
|
528
|
+
/** Mongoose auto-index builds. Default `true` (dev); pass `false` in prod (§35). */
|
|
529
|
+
autoIndex?: boolean;
|
|
530
|
+
/**
|
|
531
|
+
* On a model-name collision: default throws; `true` re-registers (test/dev fixtures).
|
|
532
|
+
* Maps to mongokit's `replace` policy, which itself throws under `NODE_ENV=production`.
|
|
533
|
+
*/
|
|
534
|
+
forceRecreate?: boolean;
|
|
535
|
+
/**
|
|
536
|
+
* ISO 3166-1 alpha-2 region used to read phone input that carries no country
|
|
537
|
+
* code — `'BD'` for a Bangladeshi deployment, `'GB'` for a British one. THIS
|
|
538
|
+
* KERNEL NAMES NO COUNTRY: the value is the deployment's, and belongs in
|
|
539
|
+
* `deployment.config.ts`.
|
|
540
|
+
*
|
|
541
|
+
* It is describe-time because it determines the persisted `identityKeys`, not
|
|
542
|
+
* just a request's interpretation — two boots with different regions would key
|
|
543
|
+
* the same human differently and fork the identity.
|
|
544
|
+
*
|
|
545
|
+
* Unset ⇒ only explicit E.164 phones are accepted; a national-format number is
|
|
546
|
+
* REFUSED rather than guessed at (FAIL LOUD rule 2). That is the safe default:
|
|
547
|
+
* before this option existed, a rejected parse silently degraded to
|
|
548
|
+
* `+${digits}`, and `01309000993` / `+8801309000993` became two parties for one
|
|
549
|
+
* member with nothing reporting it.
|
|
550
|
+
*/
|
|
551
|
+
defaultPhoneRegion?: string;
|
|
552
|
+
/**
|
|
553
|
+
* Contact channels authoritative enough to resolve two records as the same
|
|
554
|
+
* party. Defaults to both `email` and `phone` for backward compatibility.
|
|
555
|
+
*
|
|
556
|
+
* Set `[]` when shared household/company contact details are candidate-match
|
|
557
|
+
* signals rather than proof of identity. Contacts are still normalized and
|
|
558
|
+
* stored; tax identifiers and external references remain identity keys.
|
|
559
|
+
*
|
|
560
|
+
* Persistence-shaping configuration: changing it does not rewrite existing
|
|
561
|
+
* `identityKeys`; migrate existing rows before changing a live deployment.
|
|
562
|
+
*/
|
|
563
|
+
identityContactTypes?: readonly ContactPointType[];
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* BIND-time runtime collaborators — the live connection is the first `bind` arg; these are the
|
|
567
|
+
* rest. A provider client, transport, logger or store belongs here, not in the shape.
|
|
568
|
+
*/
|
|
569
|
+
interface PartyRuntime {
|
|
570
|
+
eventTransport?: EventTransport;
|
|
571
|
+
outbox?: OutboxStore;
|
|
572
|
+
logger?: PartyLogger;
|
|
573
|
+
pagination?: PaginationConfig;
|
|
574
|
+
/** Downgrade the boot `transactions` capability requirement to a warning (standalone Mongo). */
|
|
575
|
+
allowNonTransactional?: boolean;
|
|
576
|
+
/** Extra mongokit plugins applied after the defaults. */
|
|
577
|
+
plugins?: PluginType[];
|
|
578
|
+
}
|
|
579
|
+
interface PartyBlueprint {
|
|
580
|
+
readonly id: 'party';
|
|
581
|
+
/** The model names this blueprint registers — known without a connection. */
|
|
582
|
+
readonly modelNames: readonly string[];
|
|
583
|
+
/**
|
|
584
|
+
* Compile + register the model on `connection`, wire the engine, verify capabilities.
|
|
585
|
+
* Synchronous: party's construction performs no async work (model registration and repo
|
|
586
|
+
* wiring are synchronous). The `KernelBlueprint` contract still permits async binds.
|
|
587
|
+
*/
|
|
588
|
+
bind(connection: Connection, runtime?: PartyRuntime): PartyEngine;
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Describe the party engine. Pure — registers no model, builds no schema, performs no I/O.
|
|
592
|
+
*/
|
|
593
|
+
declare function defineParty(shape?: PartyShape): PartyBlueprint;
|
|
389
594
|
//#endregion
|
|
390
595
|
//#region src/repositories/repo-options.d.ts
|
|
391
596
|
declare function repoOptionsFromCtx(ctx: PartyContext): OperationOptions;
|
|
392
597
|
//#endregion
|
|
598
|
+
//#region src/domain/subject.d.ts
|
|
599
|
+
/**
|
|
600
|
+
* The canonical `subjectModel` value for a Party.
|
|
601
|
+
*
|
|
602
|
+
* Import this — never write `'Party'` in a host, a module, or a test.
|
|
603
|
+
*/
|
|
604
|
+
declare const PARTY_SUBJECT_MODEL = "Party";
|
|
605
|
+
/**
|
|
606
|
+
* A Party as other kernels reference it.
|
|
607
|
+
*
|
|
608
|
+
* `subjectModel` is narrowed to the literal type so assigning a hand-typed
|
|
609
|
+
* string is a compile error at the call site, not a runtime mismatch discovered
|
|
610
|
+
* when a card fails to deactivate.
|
|
611
|
+
*/
|
|
612
|
+
interface PartySubject extends SubjectRef {
|
|
613
|
+
/**
|
|
614
|
+
* Narrowed to the literal so assigning a hand-typed string is a COMPILE error
|
|
615
|
+
* at the call site, not a runtime mismatch discovered when a card fails to
|
|
616
|
+
* deactivate.
|
|
617
|
+
*
|
|
618
|
+
* Extends `SubjectRef` from `@classytic/primitives/subject` rather than
|
|
619
|
+
* re-declaring the pair: that primitive is the canonical shape for "who a record
|
|
620
|
+
* is about", shared by attendance, access, credentials and loyalty — and by the
|
|
621
|
+
* SDK, so a UI cannot bind a card to a different model than entitlements are
|
|
622
|
+
* granted against. (Deliberately NOT `ExternalRef`, which is the same shape for a
|
|
623
|
+
* different question — provenance, not identity. A grant carries both at once.)
|
|
624
|
+
*/
|
|
625
|
+
subjectModel: typeof PARTY_SUBJECT_MODEL;
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Build the subject pair for a Party.
|
|
629
|
+
*
|
|
630
|
+
* Accepts anything id-shaped (`ObjectId`, hex string, a document's `_id`) and
|
|
631
|
+
* stringifies through primitives' `idToString`, because the receiving kernels
|
|
632
|
+
* store `subjectRef` as a String — a raw `ObjectId` would stringify implicitly
|
|
633
|
+
* somewhere downstream and the two forms would not compare equal.
|
|
634
|
+
*
|
|
635
|
+
* Returns `null` for a missing id rather than a pair containing `"undefined"`.
|
|
636
|
+
* That string is a real hazard here: it is a *valid* `subjectRef` as far as the
|
|
637
|
+
* schema is concerned, so it persists, matches nothing, and looks like data.
|
|
638
|
+
*/
|
|
639
|
+
declare function partySubject(id: IdLike | null | undefined): PartySubject | null;
|
|
640
|
+
/**
|
|
641
|
+
* Does this subject pair refer to a Party?
|
|
642
|
+
*
|
|
643
|
+
* For a resolver that fans a mixed subject stream out to the right kernel — an
|
|
644
|
+
* attendance list can hold Party members and, in an HR deployment, employees
|
|
645
|
+
* from another identity source.
|
|
646
|
+
*/
|
|
647
|
+
declare function isPartySubject(subject: {
|
|
648
|
+
subjectModel?: string | undefined;
|
|
649
|
+
}): boolean;
|
|
650
|
+
//#endregion
|
|
393
651
|
//#region src/domain/state-machine.d.ts
|
|
394
652
|
/**
|
|
395
653
|
* Party identity lifecycle FSM — the compile-time twin of the `repo.claim()`
|
|
@@ -404,20 +662,62 @@ declare function repoOptionsFromCtx(ctx: PartyContext): OperationOptions;
|
|
|
404
662
|
*/
|
|
405
663
|
declare const PARTY_STATE_MACHINE: import("@classytic/primitives/state-machine").StateMachine<"active" | "archived" | "merged">;
|
|
406
664
|
//#endregion
|
|
665
|
+
//#region src/domain/normalize.d.ts
|
|
666
|
+
/**
|
|
667
|
+
* How free-form contact input is interpreted. Threaded from the engine's
|
|
668
|
+
* resolved config so every path — `resolveOrCreate`, `syncIdentity`, merge —
|
|
669
|
+
* reads the same answer.
|
|
670
|
+
*/
|
|
671
|
+
interface NormalizeOptions {
|
|
672
|
+
/**
|
|
673
|
+
* ISO 3166-1 alpha-2 region applied to phone input that carries no country
|
|
674
|
+
* code (`01712345678` on a Bangladeshi till). Omitted ⇒ E.164 only.
|
|
675
|
+
*/
|
|
676
|
+
defaultRegion?: string | undefined;
|
|
677
|
+
}
|
|
678
|
+
/**
|
|
679
|
+
* Canonical email for the dedup key. Trim + lowercase, and deliberately NOT
|
|
680
|
+
* gmail dot / `+tag` folding — `a+shop@x.com` is a real, distinct inbox, and
|
|
681
|
+
* folding it MERGES two people, which is the irreversible direction.
|
|
682
|
+
*
|
|
683
|
+
* The rule moved to `@classytic/contact/email` unchanged for the canonical case,
|
|
684
|
+
* with one addition: an address that cannot be canonicalised now THROWS instead of
|
|
685
|
+
* becoming an identity key. Same reasoning as the phone side — a malformed address
|
|
686
|
+
* still makes a perfectly stable key, so admitting it does not fail, it forks.
|
|
687
|
+
*/
|
|
688
|
+
declare function normalizeEmail(raw: string): string;
|
|
689
|
+
/** The stable dedup key for a contact point (`email:ada@x.com`, `phone:+8801...`). */
|
|
690
|
+
declare function contactPointKey(type: ContactPointType, value: string, options?: NormalizeOptions): string;
|
|
691
|
+
/** A tax-identity dedup key (`vat:...` / `bin:...`) — cross-scope-stable statutory id. */
|
|
692
|
+
declare function taxIdentityKey(kind: 'vat' | 'bin', value: string): string;
|
|
693
|
+
//#endregion
|
|
407
694
|
//#region src/domain/identity.d.ts
|
|
695
|
+
interface IdentityKeyOptions {
|
|
696
|
+
/** Contact channels authoritative enough to resolve two records as one party. Default: both. */
|
|
697
|
+
contactTypes?: readonly ContactPointType[];
|
|
698
|
+
}
|
|
408
699
|
/** De-duplicate an array preserving first-seen order. */
|
|
409
700
|
declare function dedupe<T>(arr: readonly T[]): T[];
|
|
410
701
|
/**
|
|
411
702
|
* Turn the input's `emails[]` / `phones[]` into normalized `ContactPoint`s. The
|
|
412
703
|
* first email and first phone are marked primary; blanks are skipped.
|
|
704
|
+
*
|
|
705
|
+
* THE SEAM. Both `resolveOrCreate` and `syncIdentity` funnel here, so this is
|
|
706
|
+
* the one place normalization can be enforced — a fix applied at N call sites
|
|
707
|
+
* is the same bug waiting at N+1.
|
|
708
|
+
*
|
|
709
|
+
* Two spellings of one number in a single input collapse to ONE point (they
|
|
710
|
+
* share a key). An unparseable phone THROWS rather than being skipped: skipping
|
|
711
|
+
* would create a party with no phone identity at all, which the next
|
|
712
|
+
* well-formed visit would not resolve onto — a silent fork by a different route.
|
|
413
713
|
*/
|
|
414
|
-
declare function buildContactPoints(input: ResolvePartyInput): ContactPoint[];
|
|
714
|
+
declare function buildContactPoints(input: ResolvePartyInput, options?: NormalizeOptions): ContactPoint[];
|
|
415
715
|
/**
|
|
416
716
|
* The de-dup keys a party is resolved on: normalized emails/phones, VAT/BIN
|
|
417
717
|
* statutory ids, and external-store refs. Blank/degenerate keys are dropped so
|
|
418
718
|
* they never poison the unique index.
|
|
419
719
|
*/
|
|
420
|
-
declare function deriveIdentityKeys(contactPoints: readonly ContactPoint[], input: ResolvePartyInput): string[];
|
|
720
|
+
declare function deriveIdentityKeys(contactPoints: readonly ContactPoint[], input: ResolvePartyInput, options?: IdentityKeyOptions): string[];
|
|
421
721
|
/** Best display name from the available signals — never empty. */
|
|
422
722
|
declare function resolveDisplayName(input: ResolvePartyInput, contactPoints: readonly ContactPoint[]): string;
|
|
423
723
|
/** The primary (or first) contact-point value of a channel type. */
|
|
@@ -428,29 +728,30 @@ declare function pickPrimary(contactPoints: readonly ContactPoint[], type: 'emai
|
|
|
428
728
|
declare function unionStrings(a: readonly string[], b: readonly string[]): string[];
|
|
429
729
|
/** Union external refs by `(sourceModel, sourceId)` identity. */
|
|
430
730
|
declare function unionRefs(a: readonly ExternalRef[], b: readonly ExternalRef[]): ExternalRef[];
|
|
731
|
+
/**
|
|
732
|
+
* Reconcile two consent records for the SAME channel.
|
|
733
|
+
*
|
|
734
|
+
* A withdrawal always beats a grant, regardless of which is newer. Recency is
|
|
735
|
+
* the intuitive rule and it is the wrong one here: the two records being
|
|
736
|
+
* reconciled came from duplicate rows whose clocks record when each row was
|
|
737
|
+
* TOUCHED, not the order the person actually spoke, so letting the later
|
|
738
|
+
* timestamp win means a merge can quietly re-subscribe someone who opted out.
|
|
739
|
+
* Being over-cautious costs a marketing email; being wrong costs a complaint
|
|
740
|
+
* you cannot defend. Between two records of the same status the later one wins,
|
|
741
|
+
* since only its timestamp is then load-bearing.
|
|
742
|
+
*/
|
|
743
|
+
declare function reconcileConsent(a: ChannelConsent | undefined, b: ChannelConsent | undefined): ChannelConsent | undefined;
|
|
431
744
|
/**
|
|
432
745
|
* Union contact points by their normalized `key`. The survivor keeps its own
|
|
433
746
|
* primaries; absorbed points come in as non-primary (a merge must never create
|
|
434
747
|
* two primaries of the same channel).
|
|
748
|
+
*
|
|
749
|
+
* Where both sides hold the same channel the survivor's row is kept but its
|
|
750
|
+
* CONSENT is reconciled — dropping the loser's row wholesale would discard an
|
|
751
|
+
* opt-out that the duplicate was the only carrier of.
|
|
435
752
|
*/
|
|
436
753
|
declare function unionContacts(a: readonly ContactPoint[], b: readonly ContactPoint[]): ContactPoint[];
|
|
437
754
|
//#endregion
|
|
438
|
-
//#region src/domain/normalize.d.ts
|
|
439
|
-
/** Lowercase + trim an email. Conservative — never strips dots/plus tags. */
|
|
440
|
-
declare function normalizeEmail(raw: string): string;
|
|
441
|
-
/**
|
|
442
|
-
* Canonical phone form for dedup. Uses primitives' `parsePhone`: a valid E.164
|
|
443
|
-
* input collapses to its canonical `+<cc><national>`; anything the strict parser
|
|
444
|
-
* rejects (local formats without a country code) falls back to a digit-only
|
|
445
|
-
* key so at least same-format duplicates still collapse. Hosts wanting global
|
|
446
|
-
* dedup pass already-E.164 numbers.
|
|
447
|
-
*/
|
|
448
|
-
declare function normalizePhone(raw: string): string;
|
|
449
|
-
/** The stable dedup key for a contact point (`email:ada@x.com`, `phone:+8801...`). */
|
|
450
|
-
declare function contactPointKey(type: ContactPointType, value: string): string;
|
|
451
|
-
/** A tax-identity dedup key (`vat:...` / `bin:...`) — cross-scope-stable statutory id. */
|
|
452
|
-
declare function taxIdentityKey(kind: 'vat' | 'bin', value: string): string;
|
|
453
|
-
//#endregion
|
|
454
755
|
//#region src/domain/errors.d.ts
|
|
455
756
|
/** Base class so hosts can `instanceof PartyError` across every party throwable. */
|
|
456
757
|
declare class PartyError extends Error implements HttpError {
|
|
@@ -470,6 +771,10 @@ declare class PartyNotFoundError extends PartyError {
|
|
|
470
771
|
declare class PartyMergeError extends PartyError {
|
|
471
772
|
constructor(message: string, sourceId: string, targetId: string);
|
|
472
773
|
}
|
|
774
|
+
/** A relationship that cannot be recorded or ended as asked (overlap, bad dates, unknown row). */
|
|
775
|
+
declare class PartyRelationshipError extends PartyError {
|
|
776
|
+
constructor(message: string, partyId: string);
|
|
777
|
+
}
|
|
473
778
|
/**
|
|
474
779
|
* `resolveOrCreate` lost the create race and the winning row could not be
|
|
475
780
|
* re-read (should be transient — surfaced rather than swallowed).
|
|
@@ -484,18 +789,28 @@ declare class PartyResolveConflictError extends PartyError {
|
|
|
484
789
|
declare class PartyVersionConflictError extends PartyError {
|
|
485
790
|
constructor(id: string, expected: number);
|
|
486
791
|
}
|
|
487
|
-
/**
|
|
488
|
-
* A Mongoose model name this package registers is already on the connection.
|
|
489
|
-
* Default init throws this; `forceRecreate: true` opts into delete+re-register
|
|
490
|
-
* (test fixtures / hot reload only). PACKAGE_RULES §21.
|
|
491
|
-
*/
|
|
492
|
-
declare class PartyModelCollisionError extends PartyError {
|
|
493
|
-
constructor(modelName: string);
|
|
494
|
-
}
|
|
495
792
|
/** Input failed a domain-verb precondition (before any DB write). */
|
|
496
793
|
declare class PartyValidationError extends PartyError {
|
|
497
794
|
constructor(message: string, meta?: Record<string, unknown>);
|
|
498
795
|
}
|
|
796
|
+
/**
|
|
797
|
+
* A phone contact point could not be normalized to E.164 — so it cannot become
|
|
798
|
+
* an identity key.
|
|
799
|
+
*
|
|
800
|
+
* This REFUSES rather than degrades, and that is the whole point. The previous
|
|
801
|
+
* behaviour fell back to `+${digits}`, which produced keys like
|
|
802
|
+
* `phone:+01309000993` — neither a national number nor E.164. Two spellings of
|
|
803
|
+
* one member therefore keyed differently and `resolveOrCreate` forked the
|
|
804
|
+
* identity, silently: every screen looked correct and the membership history
|
|
805
|
+
* simply split in two (FAIL LOUD rule 2 — an input we do not understand must
|
|
806
|
+
* fail, not widen).
|
|
807
|
+
*
|
|
808
|
+
* `meta` names the offending value and the region it was interpreted in, so an
|
|
809
|
+
* operator can fix the data instead of guessing which field a 400 refers to.
|
|
810
|
+
*/
|
|
811
|
+
declare class PartyPhoneFormatError extends PartyError {
|
|
812
|
+
constructor(value: string, defaultRegion: string | undefined, detail: string);
|
|
813
|
+
}
|
|
499
814
|
//#endregion
|
|
500
815
|
//#region src/events/event-constants.d.ts
|
|
501
816
|
/** PACKAGE_RULES §12: `package:resource.verb`. Emit ONLY these catalog names. */
|
|
@@ -505,10 +820,12 @@ declare const PARTY_EVENTS: {
|
|
|
505
820
|
readonly ROLE_ADDED: 'party:party.role_added';
|
|
506
821
|
readonly ROLE_REMOVED: 'party:party.role_removed';
|
|
507
822
|
readonly USER_LINKED: 'party:party.user_linked';
|
|
823
|
+
readonly CONSENT_RECORDED: 'party:party.consent_recorded';
|
|
508
824
|
readonly EXTERNAL_REF_ATTACHED: 'party:party.external_ref_attached';
|
|
509
825
|
readonly ARCHIVED: 'party:party.archived';
|
|
510
826
|
readonly RESTORED: 'party:party.restored';
|
|
511
827
|
readonly MERGED: 'party:party.merged';
|
|
828
|
+
readonly RELATIONSHIP_CHANGED: 'party:party.relationship_changed';
|
|
512
829
|
};
|
|
513
830
|
type PartyEventName = (typeof PARTY_EVENTS)[keyof typeof PARTY_EVENTS];
|
|
514
831
|
//#endregion
|
|
@@ -533,6 +850,14 @@ interface PartyUserLinkedPayload {
|
|
|
533
850
|
partyId: string;
|
|
534
851
|
userId: string;
|
|
535
852
|
}
|
|
853
|
+
interface PartyConsentRecordedPayload {
|
|
854
|
+
partyId: string;
|
|
855
|
+
channel: 'email' | 'phone';
|
|
856
|
+
/** The NORMALIZED channel — what a sender would actually dial or mail. */
|
|
857
|
+
value: string;
|
|
858
|
+
status: 'granted' | 'withdrawn';
|
|
859
|
+
source?: string;
|
|
860
|
+
}
|
|
536
861
|
interface PartyExternalRefAttachedPayload {
|
|
537
862
|
partyId: string;
|
|
538
863
|
sourceModel: string;
|
|
@@ -556,6 +881,7 @@ interface PartyEventPayloads {
|
|
|
556
881
|
[PARTY_EVENTS.ROLE_ADDED]: PartyRoleChangedPayload;
|
|
557
882
|
[PARTY_EVENTS.ROLE_REMOVED]: PartyRoleChangedPayload;
|
|
558
883
|
[PARTY_EVENTS.USER_LINKED]: PartyUserLinkedPayload;
|
|
884
|
+
[PARTY_EVENTS.CONSENT_RECORDED]: PartyConsentRecordedPayload;
|
|
559
885
|
[PARTY_EVENTS.EXTERNAL_REF_ATTACHED]: PartyExternalRefAttachedPayload;
|
|
560
886
|
[PARTY_EVENTS.ARCHIVED]: PartyLifecyclePayload;
|
|
561
887
|
[PARTY_EVENTS.RESTORED]: PartyLifecyclePayload;
|
|
@@ -577,16 +903,10 @@ declare const partyEventDefinitions: ReadonlyArray<PartyEventDefinition>;
|
|
|
577
903
|
declare function createEvent<T>(type: string, payload: T, ctx?: PartyContext, meta?: Partial<EventMeta>): DomainEvent<T>;
|
|
578
904
|
//#endregion
|
|
579
905
|
//#region src/events/in-process-bus.d.ts
|
|
580
|
-
declare class InProcessPartyBus
|
|
581
|
-
readonly name = "in-process-party";
|
|
582
|
-
private subs;
|
|
583
|
-
private readonly logger;
|
|
906
|
+
declare class InProcessPartyBus extends InProcessEventBus {
|
|
584
907
|
constructor(options?: {
|
|
585
|
-
logger?: PartyLogger
|
|
908
|
+
logger?: PartyLogger;
|
|
586
909
|
});
|
|
587
|
-
publish(event: DomainEvent): Promise<void>;
|
|
588
|
-
subscribe(pattern: string, handler: EventHandler): Promise<() => void>;
|
|
589
|
-
close(): Promise<void>;
|
|
590
910
|
}
|
|
591
911
|
//#endregion
|
|
592
|
-
export { ADDRESS_TYPES, type AddressType, CONTACT_POINT_TYPES, type ContactPoint, type ContactPointType, type ExternalRef, type IPartyDocument, InProcessPartyBus, PARTY_EVENTS, PARTY_KINDS,
|
|
912
|
+
export { ADDRESS_TYPES, type AddRelationshipInput, type AddressType, CONSENT_STATUSES, CONTACT_POINT_TYPES, type ChannelConsent, type ConsentStatus, type ContactPoint, type ContactPointType, type ExternalRef, type IPartyDocument, type IdentityKeyOptions, InProcessPartyBus, type NormalizeOptions, PARTY_EVENTS, PARTY_KINDS, PARTY_ROLES, PARTY_STATE_MACHINE, PARTY_STATUSES, PARTY_SUBJECT_MODEL, PENDING_EVENTS, type PartyAddress, type PartyBlueprint, type PartyContext, type PartyEngine, PartyError, PartyEventDispatcher, type PartyEventName, type PartyEventPayloads, type PartyKind, type PartyLogger, PartyMergeError, type PartyModels, PartyNotFoundError, PartyPhoneFormatError, type PartyPort, type PartyRelationship, PartyRelationshipError, type PartyRepositories, PartyRepository, type PartyRepositoryDeps, PartyResolveConflictError, type PartyRole, type PartyRuntime, type PartyShape, type PartySnapshot, type PartyStatus, type PartySubject, PartyValidationError, PartyVersionConflictError, type PersonName, RELATIONSHIP_KINDS, type RecordConsentInput, type RelationshipKind, type ResolvePartyInput, type ResolveResult, type ResolvedPartyConfig, type SchemaBuildConfig, type TaxIdentity, type TenantFieldType, buildContactPoints, buildPartySchema, contactPointKey, createEvent, dedupe, defineParty, definePartyModels, deriveIdentityKeys, flushPending, isPartySubject, normalizeEmail, partyEventDefinitions, partySubject, pickPrimary, reconcileConsent, repoOptionsFromCtx, resolveDisplayName, taxIdentityKey, tenantFieldDef, unionContacts, unionRefs, unionStrings };
|