@classytic/party 0.1.0 → 0.2.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/index.d.ts CHANGED
@@ -1,12 +1,15 @@
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";
2
4
  import { Connection, Model, Schema, SchemaDefinitionProperty, Types } from "mongoose";
3
5
  import { DomainEvent, EventHandler, EventMeta, EventTransport } from "@classytic/primitives/events";
6
+ import { ExternalRef, ExternalRef as ExternalRef$1, IdLike } from "@classytic/primitives/reference";
4
7
  import { z } from "zod";
5
8
  import { OutboxStore } from "@classytic/primitives/outbox";
6
9
  import { OperationContext } from "@classytic/primitives/context";
7
- import { ExternalRef, ExternalRef as ExternalRef$1 } from "@classytic/primitives/reference";
8
10
  import { PersonName, PersonName as PersonName$1 } from "@classytic/primitives/person";
9
11
  import { ContactAddress } from "@classytic/primitives/address";
12
+ import { SubjectRef } from "@classytic/primitives/subject";
10
13
  import { HttpError } from "@classytic/repo-core/errors";
11
14
  //#region src/domain/enums.d.ts
12
15
  /**
@@ -65,9 +68,22 @@ interface PartyAddress extends ContactAddress {
65
68
  */
66
69
  interface ContactPoint {
67
70
  type: ContactPointType;
71
+ /**
72
+ * The CANONICAL form — a lowercased email, or an E.164 phone. Identity is the
73
+ * normalized value only; this is also what a notification transport should
74
+ * dial or mail, so a stored local spelling is a second bug waiting behind the
75
+ * dedup one.
76
+ */
68
77
  value: string;
69
78
  /** Normalized dedup key — the `identityKeys` index entry. */
70
79
  key: string;
80
+ /**
81
+ * What the operator actually TYPED, kept only when it differs from `value`.
82
+ * Support-facing provenance ("the till shows 01712345678, the invoice shows
83
+ * +8801712345678 — same number"). Never an identity signal: it is not keyed,
84
+ * not indexed and not matched on.
85
+ */
86
+ input?: string;
71
87
  isPrimary: boolean;
72
88
  verified: boolean;
73
89
  }
@@ -177,13 +193,7 @@ interface PartyLogger {
177
193
  //#region src/models/tenant-field.d.ts
178
194
  type TenantFieldType = 'objectId' | 'string';
179
195
  interface SchemaBuildConfig {
180
- /** Schema type for the tenant column. Engine plumbs this everywhere. */
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;
196
+ tenant: ResolvedTenantConfig;
187
197
  }
188
198
  /**
189
199
  * Tenant-column schema definition, or `undefined` when the engine is
@@ -226,15 +236,17 @@ declare function buildPartySchema(config: SchemaBuildConfig): Schema<IPartyDocum
226
236
  interface PartyModels {
227
237
  Party: Model<IPartyDocument>;
228
238
  }
229
- /** Mongoose model names this package registers — drives the §21 collision check. */
230
- declare const PARTY_MODEL_NAMES: readonly ['Party'];
231
- interface CreateModelsOptions extends SchemaBuildConfig {
232
- /** On a name collision: `false` (default) throws; `true` deletes + re-registers (tests). */
239
+ interface DefinePartyModelsConfig extends SchemaBuildConfig {
240
+ /** On a name collision: `false` (default) throws; `true` re-registers (test/dev fixtures). */
233
241
  forceRecreate?: boolean;
234
242
  /** Mongoose auto-index builds. Default `true` (dev); `false` in prod (§35). */
235
243
  autoIndex?: boolean;
236
244
  }
237
- declare function createPartyModels(connection: Connection, config: CreateModelsOptions): PartyModels;
245
+ /**
246
+ * Describe the Party model set as a connection-free blueprint. Pure — builds no schema and
247
+ * registers nothing until `.bind(connection)`.
248
+ */
249
+ declare function definePartyModels(config: DefinePartyModelsConfig): ModelBlueprint<PartyModels>;
238
250
  //#endregion
239
251
  //#region src/events/dispatch.d.ts
240
252
  /** Context key carrying a post-commit publish queue (attached by `withTransaction`). */
@@ -271,9 +283,26 @@ declare function flushPending(dispatcher: PartyEventDispatcher, queue: DomainEve
271
283
  interface PartyRepositoryDeps {
272
284
  events: PartyEventDispatcher;
273
285
  logger?: PartyLogger | undefined;
286
+ /**
287
+ * ISO 3166-1 alpha-2 region used to interpret phone input that carries no
288
+ * country code. Comes from `PartyShape.defaultPhoneRegion`, i.e. the
289
+ * deployment — this kernel names no country.
290
+ *
291
+ * Held on the repository rather than passed per call so `resolveOrCreate` and
292
+ * `syncIdentity` cannot disagree: a caller-supplied region would let the
293
+ * create path and the sync path key the same human differently, which is the
294
+ * split this whole change removes.
295
+ */
296
+ defaultPhoneRegion?: string | undefined;
297
+ /** Deployment-wide contact channels used as authoritative identity keys. */
298
+ identityContactTypes?: readonly ContactPointType[] | undefined;
274
299
  }
275
300
  declare class PartyRepository extends Repository<IPartyDocument> implements PartyPort<IPartyDocument> {
276
301
  private readonly events;
302
+ /** The ONE interpretation of free-form contact input in this deployment. */
303
+ private readonly normalize;
304
+ /** Contact data can exist without being strong enough to collapse two parties. */
305
+ private readonly identityContactTypes;
277
306
  constructor(model: Model<IPartyDocument>, deps: PartyRepositoryDeps, plugins?: PluginType[]);
278
307
  /** `update` that never returns null — the party was loaded first; a null means a concurrent delete. */
279
308
  private mustUpdate;
@@ -297,9 +326,17 @@ declare class PartyRepository extends Repository<IPartyDocument> implements Part
297
326
  restore(id: string, ctx: PartyContext): Promise<IPartyDocument>;
298
327
  /**
299
328
  * Merge `sourceId` into `targetId`: the survivor absorbs the loser's identity
300
- * keys, contact points, addresses, roles, external refs, and user links; the
301
- * loser becomes a terminal `merged` redirect (`mergedInto`). Two-document
302
- * write → runs in a transaction (falls back to sequential on standalone Mongo).
329
+ * keys, contact points, addresses, roles, external refs, user links, tags, and
330
+ * metadata, and FILLS its empty scalar slots (`taxIdentity`, `personName`,
331
+ * `legalName`, `parentId`) from the loser — never overwriting a populated
332
+ * survivor field. The loser becomes a terminal `merged` redirect
333
+ * (`mergedInto`). Two-document write → runs in a transaction (falls back to
334
+ * sequential on standalone Mongo).
335
+ *
336
+ * Concurrency: the loser is claimed `active|archived → merged` via CAS
337
+ * (`repo.claim`), so a second concurrent merge of the same source loses the
338
+ * race and surfaces a typed `PartyMergeError` — never a raw `E11000`. A
339
+ * duplicate-key collision while adopting the loser's keys maps to the same.
303
340
  */
304
341
  merge(sourceId: string, targetId: string, ctx: PartyContext): Promise<IPartyDocument>;
305
342
  /** Load a party by ObjectId hex, or throw the typed not-found (§P9-style). */
@@ -311,44 +348,15 @@ declare class PartyRepository extends Repository<IPartyDocument> implements Part
311
348
  }
312
349
  //#endregion
313
350
  //#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
351
  interface ResolvedPartyConfig {
347
352
  connection: Connection;
348
- multiTenant: boolean;
349
- tenantFieldType: TenantFieldType;
353
+ tenant: Readonly<ResolvedTenantConfig>;
350
354
  forceRecreate: boolean;
351
355
  autoIndex: boolean;
356
+ /** ISO 3166-1 alpha-2 region for national-format phone input; unset ⇒ E.164 only. */
357
+ defaultPhoneRegion?: string;
358
+ /** Contact channels used as authoritative unique identity keys. */
359
+ identityContactTypes: readonly ContactPointType[];
352
360
  allowNonTransactional: boolean;
353
361
  logger?: PartyLogger;
354
362
  pagination?: PaginationConfig;
@@ -380,16 +388,141 @@ interface PartyEngine {
380
388
  withTransaction<T>(ctx: PartyContext, body: (txCtx: PartyContext) => Promise<T>): Promise<T>;
381
389
  /** Build/align indexes. Call once per deploy when booted `autoIndex: false` (§35). */
382
390
  syncIndexes(): Promise<void>;
383
- /** Release resources. Idempotent. */
384
- dispose(): Promise<void>;
391
+ /** Release resources owned by this engine. Idempotent. The kernel-standard lifecycle name. */
392
+ close(): Promise<void>;
385
393
  }
386
394
  //#endregion
387
- //#region src/engine/create-party.d.ts
388
- declare function createParty(config: PartyConfig): Promise<PartyEngine>;
395
+ //#region src/engine/define-party.d.ts
396
+ /**
397
+ * DESCRIBE-time shape — everything that determines the persistence SHAPE and needs no
398
+ * connection or I/O to inspect. Nothing here opens a socket or reads a secret.
399
+ */
400
+ interface PartyShape {
401
+ /** Company-wide identity (`false`, no tenant column) vs isolated (`true`, default). */
402
+ tenant?: boolean | TenantConfig;
403
+ /** Mongoose auto-index builds. Default `true` (dev); pass `false` in prod (§35). */
404
+ autoIndex?: boolean;
405
+ /**
406
+ * On a model-name collision: default throws; `true` re-registers (test/dev fixtures).
407
+ * Maps to mongokit's `replace` policy, which itself throws under `NODE_ENV=production`.
408
+ */
409
+ forceRecreate?: boolean;
410
+ /**
411
+ * ISO 3166-1 alpha-2 region used to read phone input that carries no country
412
+ * code — `'BD'` for a Bangladeshi deployment, `'GB'` for a British one. THIS
413
+ * KERNEL NAMES NO COUNTRY: the value is the deployment's, and belongs in
414
+ * `deployment.config.ts`.
415
+ *
416
+ * It is describe-time because it determines the persisted `identityKeys`, not
417
+ * just a request's interpretation — two boots with different regions would key
418
+ * the same human differently and fork the identity.
419
+ *
420
+ * Unset ⇒ only explicit E.164 phones are accepted; a national-format number is
421
+ * REFUSED rather than guessed at (FAIL LOUD rule 2). That is the safe default:
422
+ * before this option existed, a rejected parse silently degraded to
423
+ * `+${digits}`, and `01309000993` / `+8801309000993` became two parties for one
424
+ * member with nothing reporting it.
425
+ */
426
+ defaultPhoneRegion?: string;
427
+ /**
428
+ * Contact channels authoritative enough to resolve two records as the same
429
+ * party. Defaults to both `email` and `phone` for backward compatibility.
430
+ *
431
+ * Set `[]` when shared household/company contact details are candidate-match
432
+ * signals rather than proof of identity. Contacts are still normalized and
433
+ * stored; tax identifiers and external references remain identity keys.
434
+ *
435
+ * Persistence-shaping configuration: changing it does not rewrite existing
436
+ * `identityKeys`; migrate existing rows before changing a live deployment.
437
+ */
438
+ identityContactTypes?: readonly ContactPointType[];
439
+ }
440
+ /**
441
+ * BIND-time runtime collaborators — the live connection is the first `bind` arg; these are the
442
+ * rest. A provider client, transport, logger or store belongs here, not in the shape.
443
+ */
444
+ interface PartyRuntime {
445
+ eventTransport?: EventTransport;
446
+ outbox?: OutboxStore;
447
+ logger?: PartyLogger;
448
+ pagination?: PaginationConfig;
449
+ /** Downgrade the boot `transactions` capability requirement to a warning (standalone Mongo). */
450
+ allowNonTransactional?: boolean;
451
+ /** Extra mongokit plugins applied after the defaults. */
452
+ plugins?: PluginType[];
453
+ }
454
+ interface PartyBlueprint {
455
+ readonly id: 'party';
456
+ /** The model names this blueprint registers — known without a connection. */
457
+ readonly modelNames: readonly string[];
458
+ /**
459
+ * Compile + register the model on `connection`, wire the engine, verify capabilities.
460
+ * Synchronous: party's construction performs no async work (model registration and repo
461
+ * wiring are synchronous). The `KernelBlueprint` contract still permits async binds.
462
+ */
463
+ bind(connection: Connection, runtime?: PartyRuntime): PartyEngine;
464
+ }
465
+ /**
466
+ * Describe the party engine. Pure — registers no model, builds no schema, performs no I/O.
467
+ */
468
+ declare function defineParty(shape?: PartyShape): PartyBlueprint;
389
469
  //#endregion
390
470
  //#region src/repositories/repo-options.d.ts
391
471
  declare function repoOptionsFromCtx(ctx: PartyContext): OperationOptions;
392
472
  //#endregion
473
+ //#region src/domain/subject.d.ts
474
+ /**
475
+ * The canonical `subjectModel` value for a Party.
476
+ *
477
+ * Import this — never write `'Party'` in a host, a module, or a test.
478
+ */
479
+ declare const PARTY_SUBJECT_MODEL = "Party";
480
+ /**
481
+ * A Party as other kernels reference it.
482
+ *
483
+ * `subjectModel` is narrowed to the literal type so assigning a hand-typed
484
+ * string is a compile error at the call site, not a runtime mismatch discovered
485
+ * when a card fails to deactivate.
486
+ */
487
+ interface PartySubject extends SubjectRef {
488
+ /**
489
+ * Narrowed to the literal so assigning a hand-typed string is a COMPILE error
490
+ * at the call site, not a runtime mismatch discovered when a card fails to
491
+ * deactivate.
492
+ *
493
+ * Extends `SubjectRef` from `@classytic/primitives/subject` rather than
494
+ * re-declaring the pair: that primitive is the canonical shape for "who a record
495
+ * is about", shared by attendance, access, credentials and loyalty — and by the
496
+ * SDK, so a UI cannot bind a card to a different model than entitlements are
497
+ * granted against. (Deliberately NOT `ExternalRef`, which is the same shape for a
498
+ * different question — provenance, not identity. A grant carries both at once.)
499
+ */
500
+ subjectModel: typeof PARTY_SUBJECT_MODEL;
501
+ }
502
+ /**
503
+ * Build the subject pair for a Party.
504
+ *
505
+ * Accepts anything id-shaped (`ObjectId`, hex string, a document's `_id`) and
506
+ * stringifies through primitives' `idToString`, because the receiving kernels
507
+ * store `subjectRef` as a String — a raw `ObjectId` would stringify implicitly
508
+ * somewhere downstream and the two forms would not compare equal.
509
+ *
510
+ * Returns `null` for a missing id rather than a pair containing `"undefined"`.
511
+ * That string is a real hazard here: it is a *valid* `subjectRef` as far as the
512
+ * schema is concerned, so it persists, matches nothing, and looks like data.
513
+ */
514
+ declare function partySubject(id: IdLike | null | undefined): PartySubject | null;
515
+ /**
516
+ * Does this subject pair refer to a Party?
517
+ *
518
+ * For a resolver that fans a mixed subject stream out to the right kernel — an
519
+ * attendance list can hold Party members and, in an HR deployment, employees
520
+ * from another identity source.
521
+ */
522
+ declare function isPartySubject(subject: {
523
+ subjectModel?: string | undefined;
524
+ }): boolean;
525
+ //#endregion
393
526
  //#region src/domain/state-machine.d.ts
394
527
  /**
395
528
  * Party identity lifecycle FSM — the compile-time twin of the `repo.claim()`
@@ -404,20 +537,62 @@ declare function repoOptionsFromCtx(ctx: PartyContext): OperationOptions;
404
537
  */
405
538
  declare const PARTY_STATE_MACHINE: import("@classytic/primitives/state-machine").StateMachine<"active" | "archived" | "merged">;
406
539
  //#endregion
540
+ //#region src/domain/normalize.d.ts
541
+ /**
542
+ * How free-form contact input is interpreted. Threaded from the engine's
543
+ * resolved config so every path — `resolveOrCreate`, `syncIdentity`, merge —
544
+ * reads the same answer.
545
+ */
546
+ interface NormalizeOptions {
547
+ /**
548
+ * ISO 3166-1 alpha-2 region applied to phone input that carries no country
549
+ * code (`01712345678` on a Bangladeshi till). Omitted ⇒ E.164 only.
550
+ */
551
+ defaultRegion?: string | undefined;
552
+ }
553
+ /**
554
+ * Canonical email for the dedup key. Trim + lowercase, and deliberately NOT
555
+ * gmail dot / `+tag` folding — `a+shop@x.com` is a real, distinct inbox, and
556
+ * folding it MERGES two people, which is the irreversible direction.
557
+ *
558
+ * The rule moved to `@classytic/contact/email` unchanged for the canonical case,
559
+ * with one addition: an address that cannot be canonicalised now THROWS instead of
560
+ * becoming an identity key. Same reasoning as the phone side — a malformed address
561
+ * still makes a perfectly stable key, so admitting it does not fail, it forks.
562
+ */
563
+ declare function normalizeEmail(raw: string): string;
564
+ /** The stable dedup key for a contact point (`email:ada@x.com`, `phone:+8801...`). */
565
+ declare function contactPointKey(type: ContactPointType, value: string, options?: NormalizeOptions): string;
566
+ /** A tax-identity dedup key (`vat:...` / `bin:...`) — cross-scope-stable statutory id. */
567
+ declare function taxIdentityKey(kind: 'vat' | 'bin', value: string): string;
568
+ //#endregion
407
569
  //#region src/domain/identity.d.ts
570
+ interface IdentityKeyOptions {
571
+ /** Contact channels authoritative enough to resolve two records as one party. Default: both. */
572
+ contactTypes?: readonly ContactPointType[];
573
+ }
408
574
  /** De-duplicate an array preserving first-seen order. */
409
575
  declare function dedupe<T>(arr: readonly T[]): T[];
410
576
  /**
411
577
  * Turn the input's `emails[]` / `phones[]` into normalized `ContactPoint`s. The
412
578
  * first email and first phone are marked primary; blanks are skipped.
579
+ *
580
+ * THE SEAM. Both `resolveOrCreate` and `syncIdentity` funnel here, so this is
581
+ * the one place normalization can be enforced — a fix applied at N call sites
582
+ * is the same bug waiting at N+1.
583
+ *
584
+ * Two spellings of one number in a single input collapse to ONE point (they
585
+ * share a key). An unparseable phone THROWS rather than being skipped: skipping
586
+ * would create a party with no phone identity at all, which the next
587
+ * well-formed visit would not resolve onto — a silent fork by a different route.
413
588
  */
414
- declare function buildContactPoints(input: ResolvePartyInput): ContactPoint[];
589
+ declare function buildContactPoints(input: ResolvePartyInput, options?: NormalizeOptions): ContactPoint[];
415
590
  /**
416
591
  * The de-dup keys a party is resolved on: normalized emails/phones, VAT/BIN
417
592
  * statutory ids, and external-store refs. Blank/degenerate keys are dropped so
418
593
  * they never poison the unique index.
419
594
  */
420
- declare function deriveIdentityKeys(contactPoints: readonly ContactPoint[], input: ResolvePartyInput): string[];
595
+ declare function deriveIdentityKeys(contactPoints: readonly ContactPoint[], input: ResolvePartyInput, options?: IdentityKeyOptions): string[];
421
596
  /** Best display name from the available signals — never empty. */
422
597
  declare function resolveDisplayName(input: ResolvePartyInput, contactPoints: readonly ContactPoint[]): string;
423
598
  /** The primary (or first) contact-point value of a channel type. */
@@ -435,22 +610,6 @@ declare function unionRefs(a: readonly ExternalRef[], b: readonly ExternalRef[])
435
610
  */
436
611
  declare function unionContacts(a: readonly ContactPoint[], b: readonly ContactPoint[]): ContactPoint[];
437
612
  //#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
613
  //#region src/domain/errors.d.ts
455
614
  /** Base class so hosts can `instanceof PartyError` across every party throwable. */
456
615
  declare class PartyError extends Error implements HttpError {
@@ -484,18 +643,28 @@ declare class PartyResolveConflictError extends PartyError {
484
643
  declare class PartyVersionConflictError extends PartyError {
485
644
  constructor(id: string, expected: number);
486
645
  }
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
646
  /** Input failed a domain-verb precondition (before any DB write). */
496
647
  declare class PartyValidationError extends PartyError {
497
648
  constructor(message: string, meta?: Record<string, unknown>);
498
649
  }
650
+ /**
651
+ * A phone contact point could not be normalized to E.164 — so it cannot become
652
+ * an identity key.
653
+ *
654
+ * This REFUSES rather than degrades, and that is the whole point. The previous
655
+ * behaviour fell back to `+${digits}`, which produced keys like
656
+ * `phone:+01309000993` — neither a national number nor E.164. Two spellings of
657
+ * one member therefore keyed differently and `resolveOrCreate` forked the
658
+ * identity, silently: every screen looked correct and the membership history
659
+ * simply split in two (FAIL LOUD rule 2 — an input we do not understand must
660
+ * fail, not widen).
661
+ *
662
+ * `meta` names the offending value and the region it was interpreted in, so an
663
+ * operator can fix the data instead of guessing which field a 400 refers to.
664
+ */
665
+ declare class PartyPhoneFormatError extends PartyError {
666
+ constructor(value: string, defaultRegion: string | undefined, detail: string);
667
+ }
499
668
  //#endregion
500
669
  //#region src/events/event-constants.d.ts
501
670
  /** PACKAGE_RULES §12: `package:resource.verb`. Emit ONLY these catalog names. */
@@ -577,16 +746,10 @@ declare const partyEventDefinitions: ReadonlyArray<PartyEventDefinition>;
577
746
  declare function createEvent<T>(type: string, payload: T, ctx?: PartyContext, meta?: Partial<EventMeta>): DomainEvent<T>;
578
747
  //#endregion
579
748
  //#region src/events/in-process-bus.d.ts
580
- declare class InProcessPartyBus implements EventTransport {
581
- readonly name = "in-process-party";
582
- private subs;
583
- private readonly logger;
749
+ declare class InProcessPartyBus extends InProcessEventBus {
584
750
  constructor(options?: {
585
- logger?: PartyLogger | undefined;
751
+ logger?: PartyLogger;
586
752
  });
587
- publish(event: DomainEvent): Promise<void>;
588
- subscribe(pattern: string, handler: EventHandler): Promise<() => void>;
589
- close(): Promise<void>;
590
753
  }
591
754
  //#endregion
592
- export { ADDRESS_TYPES, type AddressType, CONTACT_POINT_TYPES, type ContactPoint, type ContactPointType, type ExternalRef, type IPartyDocument, InProcessPartyBus, PARTY_EVENTS, PARTY_KINDS, PARTY_MODEL_NAMES, PARTY_ROLES, PARTY_STATE_MACHINE, PARTY_STATUSES, PENDING_EVENTS, type PartyAddress, type PartyConfig, type PartyContext, type PartyEngine, PartyError, PartyEventDispatcher, type PartyEventName, type PartyEventPayloads, type PartyKind, type PartyLogger, PartyMergeError, PartyModelCollisionError, type PartyModels, PartyNotFoundError, type PartyPort, type PartyRepositories, PartyRepository, type PartyRepositoryDeps, PartyResolveConflictError, type PartyRole, type PartySnapshot, type PartyStatus, PartyValidationError, PartyVersionConflictError, type PersonName, type ResolvePartyInput, type ResolveResult, type ResolvedPartyConfig, type SchemaBuildConfig, type TaxIdentity, type TenantFieldType, buildContactPoints, buildPartySchema, contactPointKey, createEvent, createParty, createParty as default, createPartyModels, dedupe, deriveIdentityKeys, flushPending, normalizeEmail, normalizePhone, partyEventDefinitions, pickPrimary, repoOptionsFromCtx, resolveDisplayName, taxIdentityKey, tenantFieldDef, unionContacts, unionRefs, unionStrings };
755
+ export { ADDRESS_TYPES, type AddressType, CONTACT_POINT_TYPES, 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 PartyRepositories, PartyRepository, type PartyRepositoryDeps, PartyResolveConflictError, type PartyRole, type PartyRuntime, type PartyShape, type PartySnapshot, type PartyStatus, type PartySubject, PartyValidationError, PartyVersionConflictError, type PersonName, 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, repoOptionsFromCtx, resolveDisplayName, taxIdentityKey, tenantFieldDef, unionContacts, unionRefs, unionStrings };