@contractspec/lib.bus 1.56.0 → 1.57.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.
@@ -4,7 +4,6 @@ import { EventEnvelope, EventKey, EventSpec } from "@contractspec/lib.contracts"
4
4
  import { AnySchemaModel } from "@contractspec/lib.schema";
5
5
 
6
6
  //#region src/auditableBus.d.ts
7
-
8
7
  /**
9
8
  * Extended event envelope with metadata for auditing.
10
9
  */
@@ -1 +1 @@
1
- {"version":3,"file":"auditableBus.d.ts","names":[],"sources":["../src/auditableBus.ts"],"sourcesContent":[],"mappings":";;;;;;;;;AAcA;AAA2E,UAA1D,sBAA0D,CAAA,IAAA,OAAA,CAAA,SAAd,aAAc,CAAA,CAAA,CAAA,CAAA;EAC9D,QAAA,CAAA,EAAA,aAAA;;;AAMb;AAcA;AAEgB,UAhBC,WAAA,CAgBD;EAAc,EAAA,EAAA,MAAA;EAEZ,QAAA,EAAA,MAAA;EAA4B,YAAA,EAAA,MAAA;EAAR,OAAA,EAAA,OAAA;EAAO,QAAA,CAAA,EAbhC,aAagC;EAM5B,UAAA,EAAA,MAAA;EAeA,OAAA,CAAA,EAAA,MAAA;EAEV,UAAA,EAjCO,IAiCP;;;;;AAQ2C,UAnCjC,YAAA,CAmCiC;EAAW;EAMhD,KAAA,CAAA,MAAA,EAvCG,WAuCe,CAAA,EAvCD,OAuCC,CAAA,IAAA,CAAA;EAUR;EAWA,KAAA,EAAA,OAAA,EA1DL,iBA0DK,CAAA,EA1De,OA0Df,CA1DuB,WA0DvB,EAAA,CAAA;;;;;AA0Ca,UA9FnB,iBAAA,CA8FmB;EACjB,QAAA,CAAA,EAAA,MAAA;EAAd,OAAA,CAAA,EAAA,MAAA;EAOuB,QAAA,CAAA,EAAA,MAAA;EAA4B,KAAA,CAAA,EAAA,MAAA;EAAR,OAAA,CAAA,EAAA,MAAA;EAvEN,IAAA,CAAA,EAzBjC,IAyBiC;EAAQ,EAAA,CAAA,EAxB3C,IAwB2C;EAkFlC,KAAA,CAAA,EAAA,MAAA;EAAiC,MAAA,CAAA,EAAA,MAAA;;;;;AAG7B,UArGH,wBAAA,CAqGG;EAGP;EACgC,GAAA,EAvGtC,QAuGsC;EAAe;EAAA,OAAA,CAAA,EArGhD,YAqGgD;EAwB/C;EAGS,eAAA,CAAA,EA9HF,aA8HE;EAAc;EAIb,WAAA,CAAA,EAAA,CAAA,QAAA,EAAA,MAAA,EAAA,QAAA,EAhIsB,sBAgItB,EAAA,GAAA,OAAA;EAA4B;EAAR,oBAAA,CAAA,EAAA,CAAA,MAAA,EA9HT,WA8HS,EAAA,GA9HO,WA8HP;;;;AAiE3C;AACO,cA1LM,iBAAA,YAA6B,QA0LnC,CAAA;EACU,iBAAA,GAAA;EAAL,iBAAA,OAAA;EACT,iBAAA,eAAA;EAAiB,iBAAA,WAAA;;uBAlLG;;;;iBAWA,iBAAiB,aAAa;;;;mBAyC1C,oCACU,eAAe,gBAC/B,cAAc;;;;sBAOS,oBAAoB,QAAQ;;;;;iBAWxC,iCAAiC,qBAC1C,oBAAoB,gBACnB,UAAU,sBACE,0BAGP;;aACgC;MAAe;;;;cAwB/C,oBAAA,YAAgC;;gBAGvB,cAAc;iBAIb,oBAAoB,QAAQ;;;;YAkDvC;;;;;;;;;iBAeI,uBAAA,MACT,oBACK,KAAK,mCACd"}
1
+ {"version":3,"file":"auditableBus.d.ts","names":[],"sources":["../src/auditableBus.ts"],"mappings":";;;;;;;;AAcA;UAAiB,sBAAA,sBAA4C,aAAA,CAAc,CAAA;EACzE,QAAA,GAAW,aAAA;AAAA;;;;UAMI,WAAA;EACf,EAAA;EACA,QAAA;EACA,YAAA;EACA,OAAA;EACA,QAAA,GAAW,aAAA;EACX,UAAA;EACA,OAAA;EACA,UAAA,EAAY,IAAA;AAAA;;;;UAMG,YAAA;EAXf;EAaA,KAAA,CAAM,MAAA,EAAQ,WAAA,GAAc,OAAA;EAX5B;EAaA,KAAA,EAAO,OAAA,EAAS,iBAAA,GAAoB,OAAA,CAAQ,WAAA;AAAA;;;;UAM7B,iBAAA;EACf,QAAA;EACA,OAAA;EACA,QAAA;EACA,KAAA;EACA,OAAA;EACA,IAAA,GAAO,IAAA;EACP,EAAA,GAAK,IAAA;EACL,KAAA;EACA,MAAA;AAAA;;;;UAMe,wBAAA;EAvBa;EAyB5B,GAAA,EAAK,QAAA;EAvBW;EAyBhB,OAAA,GAAU,YAAA;EAzB0B;EA2BpC,eAAA,GAAkB,aAAA;EA3BqC;EA6BvD,WAAA,IAAe,QAAA,UAAkB,QAAA,EAAU,sBAAA;EAvB5B;EAyBf,oBAAA,IAAwB,MAAA,EAAQ,WAAA,KAAgB,WAAA;AAAA;;;;cAMrC,iBAAA,YAA6B,QAAA;EAAA,iBACvB,GAAA;EAAA,iBACA,OAAA;EAAA,iBACA,eAAA;EAAA,iBACA,WAAA;EAAA,iBAIA,oBAAA;cAEL,OAAA,EAAS,wBAAA;EAjCrB;;;EA4CM,OAAA,CAAQ,KAAA,EAAO,QAAA,EAAU,KAAA,EAAO,UAAA,GAAa,OAAA;EArCpC;;;EA6ET,SAAA,CACJ,KAAA,EAAO,QAAA,WACP,OAAA,GAAU,KAAA,EAAO,UAAA,KAAe,OAAA,SAC/B,OAAA,OAAc,OAAA;EA5EP;;;EAmFJ,UAAA,CAAW,OAAA,EAAS,iBAAA,GAAoB,OAAA,CAAQ,WAAA;AAAA;;;;iBAWxC,sBAAA,WAAiC,cAAA,CAAA,CAC/C,GAAA,EAAK,iBAAA,GAAoB,QAAA,EACzB,IAAA,EAAM,SAAA,CAAU,CAAA,GAChB,eAAA,GAAkB,aAAA,IAGhB,OAAA,EAAS,CAAA,EACT,OAAA;EAAY,OAAA;EAAkB,QAAA,GAAW,aAAA;AAAA,MAAe,OAAA;;;;cAwB/C,oBAAA,YAAgC,YAAA;EAAA,QACnC,OAAA;EAEF,KAAA,CAAM,MAAA,EAAQ,WAAA,GAAc,OAAA;EAI5B,KAAA,CAAM,OAAA,EAAS,iBAAA,GAAoB,OAAA,CAAQ,WAAA;EA9HzB;;;EAgLxB,MAAA,CAAA,GAAU,WAAA;EA1KC;;;EAiLX,KAAA,CAAA;AAAA;;;;iBAQc,uBAAA,CACd,GAAA,EAAK,QAAA,EACL,OAAA,GAAU,IAAA,CAAK,wBAAA,WACd,iBAAA"}
@@ -4,8 +4,7 @@ import { AnySchemaModel } from "@contractspec/lib.schema";
4
4
  //#region src/eventBus.d.ts
5
5
  interface EventBus {
6
6
  publish: (topic: EventKey, bytes: Uint8Array) => Promise<void>;
7
- subscribe: (topic: EventKey | string,
8
- // allow wildcard if your broker supports it
7
+ subscribe: (topic: EventKey | string, // allow wildcard if your broker supports it
9
8
  handler: (bytes: Uint8Array) => Promise<void>) => Promise<() => Promise<void>>;
10
9
  }
11
10
  /** Helper to encode a typed event envelope into JSON string */
@@ -1 +1 @@
1
- {"version":3,"file":"eventBus.d.ts","names":[],"sources":["../src/eventBus.ts"],"sourcesContent":[],"mappings":";;;;UAQiB,QAAA;mBACE,iBAAiB,eAAe;EADlC,SAAA,EAAQ,CAAA,KAAA,EAGd,QAHc,GAAA,MAAA;EAAA;EACN,OAAA,EAAA,CAAA,KAAA,EAGE,UAHF,EAAA,GAGiB,OAHjB,CAAA,IAAA,CAAA,EAAA,GAIZ,OAJY,CAAA,GAAA,GAIE,OAJF,CAAA,IAAA,CAAA,CAAA;;;AAER,iBAMK,WANL,CAAA,CAAA,CAAA,CAAA,QAAA,EAM8B,aAN9B,CAM4C,CAN5C,CAAA,CAAA,EAMiD,UANjD;;AACyB,iBAUpB,WAVoB,CAAA,CAAA,CAAA,CAAA,IAAA,EAUC,UAVD,CAAA,EAUc,aAVd,CAU4B,CAV5B,CAAA;;;;AAKpC;AAAuD,iBAavC,aAbuC,CAAA,UAaf,cAbe,CAAA,CAAA,GAAA,EAchD,QAdgD,EAAA,IAAA,EAe/C,SAf+C,CAerC,CAfqC,CAAA,CAAA,EAAA,CAAA,OAAA,EAiB9B,CAjB8B,EAAA,OAAA,CAAA,EAAA,MAAA,EAAA,GAiBX,OAjBW,CAAA,IAAA,CAAA"}
1
+ {"version":3,"file":"eventBus.d.ts","names":[],"sources":["../src/eventBus.ts"],"mappings":";;;;UAQiB,QAAA;EACf,OAAA,GAAU,KAAA,EAAO,QAAA,EAAU,KAAA,EAAO,UAAA,KAAe,OAAA;EACjD,SAAA,GACE,KAAA,EAAO,QAAA;EACP,OAAA,GAAU,KAAA,EAAO,UAAA,KAAe,OAAA,WAC7B,OAAA,OAAc,OAAA;AAAA;;iBAIL,WAAA,GAAA,CAAe,QAAA,EAAU,aAAA,CAAc,CAAA,IAAK,UAAA;;iBAK5C,WAAA,GAAA,CAAe,IAAA,EAAM,UAAA,GAAa,aAAA,CAAc,CAAA;;;;;iBAQhD,aAAA,WAAwB,cAAA,CAAA,CACtC,GAAA,EAAK,QAAA,EACL,IAAA,EAAM,SAAA,CAAU,CAAA,KAEF,OAAA,EAAS,CAAA,EAAG,OAAA,cAAgB,OAAA"}
@@ -4,7 +4,6 @@ import { EventKey, EventSpec } from "@contractspec/lib.contracts";
4
4
  import { AnySchemaModel } from "@contractspec/lib.schema";
5
5
 
6
6
  //#region src/filtering.d.ts
7
-
8
7
  /**
9
8
  * Event filter configuration.
10
9
  */
@@ -1 +1 @@
1
- {"version":3,"file":"filtering.d.ts","names":[],"sources":["../src/filtering.ts"],"sourcesContent":[],"mappings":";;;;;;;;;AAUA;AAoBgB,UApBC,WAAA,CAoBY;EAyDb;EACT,SAAA,CAAA,EAAA,MAAA;EACG;EACY,MAAA,CAAA,EAAA,MAAA;EAA2B;EAE1B,OAAA,CAAA,EAAA,MAAA;EAAiB;EAAA,OAAA,CAAA,EAAA,MAAA;EAAA;EAc3B,KAAA,CAAA,EAAA,MAAA;EAEa;EAOA,IAAA,CAAA,EA7FjB,MA6FiB,CAAA,MAAA,EAAA,MAAA,CAAA;EACN;EAAV,SAAA,CAAA,EAAA,CAAA,QAAA,EA5Fe,sBA4Ff,EAAA,GAAA,OAAA;;;;;AA0ByC,iBAhHnC,aAAA,CAgHmC,QAAA,EA/GvC,sBA+GuC,EAAA,MAAA,EA9GzC,WA8GyC,CAAA,EAAA,OAAA;;;;AAavC,iBApEI,wBAAA,CAoEJ,GAAA,EAnEL,QAmEK,EAAA,MAAA,EAlEF,WAkEE,EAAA,OAAA,EAAA,CAAA,QAAA,EAjEU,sBAiEV,EAAA,GAjEqC,OAiErC,CAAA,IAAA,CAAA,CAAA,EAAA,CAAA,KAAA,EA/DW,QA+DX,GAAA,MAAA,EAAA,GA/D4B,OA+D5B,CAAA,GAAA,GA/D4B,OA+D5B,CAAA,IAAA,CAAA,CAAA;;;;AAEP,cAnDQ,cAAA,CAmDR;EAAO,iBAAA,GAAA;EAmBI,iBAAA,MAAe;EAOlB,WAAA,CAAA,GAAW,EA3EE,QA2EF,EAAA,MAAA,EAAA,MAAA;EAUZ;;;EAUe,OAAA,CAAA,UAxFD,cAwFC,CAAA,CAAA,IAAA,EAvFjB,SAuFiB,CAvFP,CAuFO,CAAA,EAAA,OAAA,EAtFd,CAsFc,EAAA,QAAA,CAAA,EArFZ,sBAqFY,CAAA,UAAA,CAAA,CAAA,EApFtB,OAoFsB,CAAA,IAAA,CAAA;EAAyB;;;EAaH,YAAA,CAAA,OAAA,EAAA,CAAA,QAAA,EA1EzB,sBA0EyB,EAAA,GA1EE,OA0EF,CAAA,IAAA,CAAA,CAAA,EAzE5C,OAyE4C,CAAA,GAAA,GAzE9B,OAyE8B,CAAA,IAAA,CAAA,CAAA;EAAd;;AAanC;4BA1EY,KAAK,4CACO,2BAA2B,gBAC9C,cAAc;;;;;iBAmBH,eAAA,MAAqB,2BAA2B;;;;cAOnD,WAAA;;;;;gBAUD,iCACY,2BAA2B;;;;qBASxB,yBAAyB;;;;wBAY3C,mBACI,sBAAsB,cAAc;;;;;iBAajC,iBAAA,CAAA,GAAqB"}
1
+ {"version":3,"file":"filtering.d.ts","names":[],"sources":["../src/filtering.ts"],"mappings":";;;;;;;;AAUA;UAAiB,WAAA;;EAEf,SAAA;EAAA;EAEA,MAAA;EAEA;EAAA,OAAA;EAIA;EAFA,OAAA;EAIO;EAFP,KAAA;EAIuB;EAFvB,IAAA,GAAO,MAAA;EAEsC;EAA7C,SAAA,IAAa,QAAA,EAAU,sBAAA;AAAA;;;;iBAMT,aAAA,CACd,QAAA,EAAU,sBAAA,EACV,MAAA,EAAQ,WAAA;;;;iBAuDM,wBAAA,CACd,GAAA,EAAK,QAAA,EACL,MAAA,EAAQ,WAAA,EACR,OAAA,GAAU,QAAA,EAAU,sBAAA,KAA2B,OAAA,UAEjC,KAAA,EAAO,QAAA,cAAiB,OAAA,OAAA,OAAA;;AALxC;;cAmBa,cAAA;EAAA,iBAEQ,GAAA;EAAA,iBACA,MAAA;cADA,GAAA,EAAK,QAAA,EACL,MAAA;EAnB4B;;;EAyBzC,OAAA,WAAkB,cAAA,CAAA,CACtB,IAAA,EAAM,SAAA,CAAU,CAAA,GAChB,OAAA,EAAS,CAAA,EACT,QAAA,GAAW,sBAAA,eACV,OAAA;EA3BmC;;;EAiDhC,YAAA,CACJ,OAAA,GAAU,QAAA,EAAU,sBAAA,KAA2B,OAAA,SAC9C,OAAA,OAAc,OAAA;EAtDT;;;EAiEF,iBAAA,CACJ,MAAA,EAAQ,IAAA,CAAK,WAAA,aACb,OAAA,GAAU,QAAA,EAAU,sBAAA,KAA2B,OAAA,SAC9C,OAAA,OAAc,OAAA;AAAA;;;;iBAmBH,eAAA,CAAgB,GAAA,EAAK,QAAA,EAAU,MAAA,WAAiB,cAAA;;;;cAOnD,WAAA;EAAA,QACH,MAAA;EA9EiB;;;EAsFzB,KAAA,CACE,MAAA,EAAQ,WAAA,EACR,OAAA,GAAU,QAAA,EAAU,sBAAA,KAA2B,OAAA;EA9EzC;;;EAuFF,QAAA,CAAS,QAAA,EAAU,sBAAA,GAAyB,OAAA;EA7D5B;;;EAwEtB,gBAAA,CACE,GAAA,EAAK,QAAA,IACH,KAAA,EAAO,QAAA,cAAsB,OAAA,OAAc,OAAA;AAAA;;;;iBAajC,iBAAA,CAAA,GAAqB,WAAA"}
package/dist/filtering.js CHANGED
@@ -8,7 +8,7 @@ import { satisfies } from "compare-versions";
8
8
  function matchesFilter(envelope, filter) {
9
9
  if (filter.eventName) {
10
10
  const pattern = filter.eventName.replace(/\*/g, ".*");
11
- if (!(/* @__PURE__ */ new RegExp(`^${pattern}$`)).test(envelope.key)) return false;
11
+ if (!new RegExp(`^${pattern}$`).test(envelope.key)) return false;
12
12
  }
13
13
  if (filter.domain) {
14
14
  if (!envelope.key.startsWith(filter.domain + ".")) return false;
@@ -1 +1 @@
1
- {"version":3,"file":"filtering.js","names":[],"sources":["../src/filtering.ts"],"sourcesContent":["import type { EventKey, EventSpec } from '@contractspec/lib.contracts';\nimport type { AnySchemaModel } from '@contractspec/lib.schema';\nimport type { EventBus } from './eventBus';\nimport { decodeEvent } from './eventBus';\nimport type { AuditableEventEnvelope } from './auditableBus';\nimport { satisfies } from 'compare-versions';\n\n/**\n * Event filter configuration.\n */\nexport interface EventFilter {\n /** Filter by event name pattern (supports * wildcard) */\n eventName?: string;\n /** Filter by domain prefix */\n domain?: string;\n /** Filter by version */\n version?: string;\n /** Filter by actor ID */\n actorId?: string;\n /** Filter by organization ID */\n orgId?: string;\n /** Filter by custom tags */\n tags?: Record<string, string>;\n /** Custom predicate function */\n predicate?: (envelope: AuditableEventEnvelope) => boolean;\n}\n\n/**\n * Check if an event matches a filter.\n */\nexport function matchesFilter(\n envelope: AuditableEventEnvelope,\n filter: EventFilter\n): boolean {\n // Check event name pattern\n if (filter.eventName) {\n const pattern = filter.eventName.replace(/\\*/g, '.*');\n const regex = new RegExp(`^${pattern}$`);\n if (!regex.test(envelope.key)) {\n return false;\n }\n }\n\n // Check domain prefix\n if (filter.domain) {\n if (!envelope.key.startsWith(filter.domain + '.')) {\n return false;\n }\n }\n\n // Check version\n if (filter.version) {\n if (!envelope.version || !satisfies(envelope.version, filter.version)) {\n return false;\n }\n }\n\n // Check metadata fields\n if (filter.actorId && envelope.metadata?.actorId !== filter.actorId) {\n return false;\n }\n\n if (filter.orgId && envelope.metadata?.orgId !== filter.orgId) {\n return false;\n }\n\n // Check tags\n if (filter.tags) {\n const eventTags = envelope.metadata?.tags ?? {};\n for (const [key, value] of Object.entries(filter.tags)) {\n if (eventTags[key] !== value) {\n return false;\n }\n }\n }\n\n // Custom predicate\n if (filter.predicate && !filter.predicate(envelope)) {\n return false;\n }\n\n return true;\n}\n\n/**\n * Create a filtered subscriber that only receives matching events.\n */\nexport function createFilteredSubscriber(\n bus: EventBus,\n filter: EventFilter,\n handler: (envelope: AuditableEventEnvelope) => Promise<void>\n) {\n return async (topic: EventKey | string) => {\n return bus.subscribe(topic, async (bytes) => {\n const envelope = decodeEvent<unknown>(bytes) as AuditableEventEnvelope;\n\n if (matchesFilter(envelope, filter)) {\n await handler(envelope);\n }\n });\n };\n}\n\n/**\n * Domain-specific event bus that filters by domain prefix.\n */\nexport class DomainEventBus {\n constructor(\n private readonly bus: EventBus,\n private readonly domain: string\n ) {}\n\n /**\n * Publish a domain event.\n */\n async publish<T extends AnySchemaModel>(\n spec: EventSpec<T>,\n payload: T,\n metadata?: AuditableEventEnvelope['metadata']\n ): Promise<void> {\n // Ensure event name starts with domain\n const eventName = spec.meta.key.startsWith(this.domain + '.')\n ? spec.meta.key\n : `${this.domain}.${spec.meta.key}`;\n\n const envelope: AuditableEventEnvelope<T> = {\n id: crypto.randomUUID(),\n occurredAt: new Date().toISOString(),\n key: eventName,\n version: spec.meta.version,\n payload,\n metadata,\n };\n\n const bytes = new TextEncoder().encode(JSON.stringify(envelope));\n await this.bus.publish(`${eventName}.v${spec.meta.version}`, bytes);\n }\n\n /**\n * Subscribe to all domain events.\n */\n async subscribeAll(\n handler: (envelope: AuditableEventEnvelope) => Promise<void>\n ): Promise<() => Promise<void>> {\n // Subscribe to wildcard pattern if supported\n return this.bus.subscribe(`${this.domain}.*`, async (bytes) => {\n const envelope = decodeEvent<unknown>(bytes) as AuditableEventEnvelope;\n await handler(envelope);\n });\n }\n\n /**\n * Subscribe with filter.\n */\n async subscribeFiltered(\n filter: Omit<EventFilter, 'domain'>,\n handler: (envelope: AuditableEventEnvelope) => Promise<void>\n ): Promise<() => Promise<void>> {\n const fullFilter: EventFilter = {\n ...filter,\n domain: this.domain,\n };\n\n return this.bus.subscribe(`${this.domain}.*`, async (bytes) => {\n const envelope = decodeEvent<unknown>(bytes) as AuditableEventEnvelope;\n\n if (matchesFilter(envelope, fullFilter)) {\n await handler(envelope);\n }\n });\n }\n}\n\n/**\n * Create a domain-scoped event bus.\n */\nexport function createDomainBus(bus: EventBus, domain: string): DomainEventBus {\n return new DomainEventBus(bus, domain);\n}\n\n/**\n * Event router that routes events to different handlers based on filters.\n */\nexport class EventRouter {\n private routes: {\n filter: EventFilter;\n handler: (envelope: AuditableEventEnvelope) => Promise<void>;\n }[] = [];\n\n /**\n * Add a route.\n */\n route(\n filter: EventFilter,\n handler: (envelope: AuditableEventEnvelope) => Promise<void>\n ): this {\n this.routes.push({ filter, handler });\n return this;\n }\n\n /**\n * Route an event to matching handlers.\n */\n async dispatch(envelope: AuditableEventEnvelope): Promise<void> {\n const matchingRoutes = this.routes.filter((r) =>\n matchesFilter(envelope, r.filter)\n );\n\n await Promise.all(matchingRoutes.map((r) => r.handler(envelope)));\n }\n\n /**\n * Create a subscriber that routes events.\n */\n createSubscriber(\n bus: EventBus\n ): (topic: EventKey | string) => Promise<() => Promise<void>> {\n return async (topic: EventKey | string) => {\n return bus.subscribe(topic, async (bytes) => {\n const envelope = decodeEvent<unknown>(bytes) as AuditableEventEnvelope;\n await this.dispatch(envelope);\n });\n };\n }\n}\n\n/**\n * Create an event router.\n */\nexport function createEventRouter(): EventRouter {\n return new EventRouter();\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"filtering.js","names":[],"sources":["../src/filtering.ts"],"sourcesContent":["import type { EventKey, EventSpec } from '@contractspec/lib.contracts';\nimport type { AnySchemaModel } from '@contractspec/lib.schema';\nimport type { EventBus } from './eventBus';\nimport { decodeEvent } from './eventBus';\nimport type { AuditableEventEnvelope } from './auditableBus';\nimport { satisfies } from 'compare-versions';\n\n/**\n * Event filter configuration.\n */\nexport interface EventFilter {\n /** Filter by event name pattern (supports * wildcard) */\n eventName?: string;\n /** Filter by domain prefix */\n domain?: string;\n /** Filter by version */\n version?: string;\n /** Filter by actor ID */\n actorId?: string;\n /** Filter by organization ID */\n orgId?: string;\n /** Filter by custom tags */\n tags?: Record<string, string>;\n /** Custom predicate function */\n predicate?: (envelope: AuditableEventEnvelope) => boolean;\n}\n\n/**\n * Check if an event matches a filter.\n */\nexport function matchesFilter(\n envelope: AuditableEventEnvelope,\n filter: EventFilter\n): boolean {\n // Check event name pattern\n if (filter.eventName) {\n const pattern = filter.eventName.replace(/\\*/g, '.*');\n const regex = new RegExp(`^${pattern}$`);\n if (!regex.test(envelope.key)) {\n return false;\n }\n }\n\n // Check domain prefix\n if (filter.domain) {\n if (!envelope.key.startsWith(filter.domain + '.')) {\n return false;\n }\n }\n\n // Check version\n if (filter.version) {\n if (!envelope.version || !satisfies(envelope.version, filter.version)) {\n return false;\n }\n }\n\n // Check metadata fields\n if (filter.actorId && envelope.metadata?.actorId !== filter.actorId) {\n return false;\n }\n\n if (filter.orgId && envelope.metadata?.orgId !== filter.orgId) {\n return false;\n }\n\n // Check tags\n if (filter.tags) {\n const eventTags = envelope.metadata?.tags ?? {};\n for (const [key, value] of Object.entries(filter.tags)) {\n if (eventTags[key] !== value) {\n return false;\n }\n }\n }\n\n // Custom predicate\n if (filter.predicate && !filter.predicate(envelope)) {\n return false;\n }\n\n return true;\n}\n\n/**\n * Create a filtered subscriber that only receives matching events.\n */\nexport function createFilteredSubscriber(\n bus: EventBus,\n filter: EventFilter,\n handler: (envelope: AuditableEventEnvelope) => Promise<void>\n) {\n return async (topic: EventKey | string) => {\n return bus.subscribe(topic, async (bytes) => {\n const envelope = decodeEvent<unknown>(bytes) as AuditableEventEnvelope;\n\n if (matchesFilter(envelope, filter)) {\n await handler(envelope);\n }\n });\n };\n}\n\n/**\n * Domain-specific event bus that filters by domain prefix.\n */\nexport class DomainEventBus {\n constructor(\n private readonly bus: EventBus,\n private readonly domain: string\n ) {}\n\n /**\n * Publish a domain event.\n */\n async publish<T extends AnySchemaModel>(\n spec: EventSpec<T>,\n payload: T,\n metadata?: AuditableEventEnvelope['metadata']\n ): Promise<void> {\n // Ensure event name starts with domain\n const eventName = spec.meta.key.startsWith(this.domain + '.')\n ? spec.meta.key\n : `${this.domain}.${spec.meta.key}`;\n\n const envelope: AuditableEventEnvelope<T> = {\n id: crypto.randomUUID(),\n occurredAt: new Date().toISOString(),\n key: eventName,\n version: spec.meta.version,\n payload,\n metadata,\n };\n\n const bytes = new TextEncoder().encode(JSON.stringify(envelope));\n await this.bus.publish(`${eventName}.v${spec.meta.version}`, bytes);\n }\n\n /**\n * Subscribe to all domain events.\n */\n async subscribeAll(\n handler: (envelope: AuditableEventEnvelope) => Promise<void>\n ): Promise<() => Promise<void>> {\n // Subscribe to wildcard pattern if supported\n return this.bus.subscribe(`${this.domain}.*`, async (bytes) => {\n const envelope = decodeEvent<unknown>(bytes) as AuditableEventEnvelope;\n await handler(envelope);\n });\n }\n\n /**\n * Subscribe with filter.\n */\n async subscribeFiltered(\n filter: Omit<EventFilter, 'domain'>,\n handler: (envelope: AuditableEventEnvelope) => Promise<void>\n ): Promise<() => Promise<void>> {\n const fullFilter: EventFilter = {\n ...filter,\n domain: this.domain,\n };\n\n return this.bus.subscribe(`${this.domain}.*`, async (bytes) => {\n const envelope = decodeEvent<unknown>(bytes) as AuditableEventEnvelope;\n\n if (matchesFilter(envelope, fullFilter)) {\n await handler(envelope);\n }\n });\n }\n}\n\n/**\n * Create a domain-scoped event bus.\n */\nexport function createDomainBus(bus: EventBus, domain: string): DomainEventBus {\n return new DomainEventBus(bus, domain);\n}\n\n/**\n * Event router that routes events to different handlers based on filters.\n */\nexport class EventRouter {\n private routes: {\n filter: EventFilter;\n handler: (envelope: AuditableEventEnvelope) => Promise<void>;\n }[] = [];\n\n /**\n * Add a route.\n */\n route(\n filter: EventFilter,\n handler: (envelope: AuditableEventEnvelope) => Promise<void>\n ): this {\n this.routes.push({ filter, handler });\n return this;\n }\n\n /**\n * Route an event to matching handlers.\n */\n async dispatch(envelope: AuditableEventEnvelope): Promise<void> {\n const matchingRoutes = this.routes.filter((r) =>\n matchesFilter(envelope, r.filter)\n );\n\n await Promise.all(matchingRoutes.map((r) => r.handler(envelope)));\n }\n\n /**\n * Create a subscriber that routes events.\n */\n createSubscriber(\n bus: EventBus\n ): (topic: EventKey | string) => Promise<() => Promise<void>> {\n return async (topic: EventKey | string) => {\n return bus.subscribe(topic, async (bytes) => {\n const envelope = decodeEvent<unknown>(bytes) as AuditableEventEnvelope;\n await this.dispatch(envelope);\n });\n };\n }\n}\n\n/**\n * Create an event router.\n */\nexport function createEventRouter(): EventRouter {\n return new EventRouter();\n}\n"],"mappings":"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"}
@@ -2,7 +2,6 @@ import { EventBus } from "./eventBus.js";
2
2
  import { EventKey } from "@contractspec/lib.contracts";
3
3
 
4
4
  //#region src/inMemoryBus.d.ts
5
-
6
5
  /**
7
6
  * In-memory bus for dev/test. Not for production scale.
8
7
  * Subscribers receive events synchronously in the order they were published.
@@ -1 +1 @@
1
- {"version":3,"file":"inMemoryBus.d.ts","names":[],"sources":["../src/inMemoryBus.ts"],"sourcesContent":[],"mappings":";;;;;;;AAOA;;AAM0C,cAN7B,WAAA,YAAuB,QAMM,CAAA;EAAa,QAAA,SAAA;EAOnC,OAAA,CAAA,KAAA,EAPG,QAOH,EAAA,OAAA,EAPsB,UAOtB,CAAA,EAPmC,OAOnC,CAAA,IAAA,CAAA;EACG,SAAA,CAAA,KAAA,EAAA,MAAA,GADH,MACG,EAAA,OAAA,EAAA,CAAA,OAAA,EAAA,UAAA,EAAA,GAAe,OAAf,CAAA,IAAA,CAAA,CAAA,EAClB,OADkB,CAAA,GAAA,GACJ,OADI,CAAA,IAAA,CAAA,CAAA"}
1
+ {"version":3,"file":"inMemoryBus.d.ts","names":[],"sources":["../src/inMemoryBus.ts"],"mappings":";;;;;;AAOA;;cAAa,WAAA,YAAuB,QAAA;EAAA,QAC1B,SAAA;EAKF,OAAA,CAAQ,KAAA,EAAO,QAAA,EAAU,OAAA,EAAS,UAAA,GAAa,OAAA;EAM/C,SAAA,CACJ,KAAA,WAAgB,MAAA,EAChB,OAAA,GAAU,OAAA,EAAS,UAAA,KAAe,OAAA,SACjC,OAAA,OAAc,OAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"metadata.d.ts","names":[],"sources":["../src/metadata.ts"],"sourcesContent":[],"mappings":";;AAGA;AAoCA;AAyBgB,UA7DC,aAAA,CA6DY;EACrB;EACa,OAAA,CAAA,EAAA,MAAA;EAAR;EACV,SAAA,CAAA,EAAA,MAAA,GAAA,QAAA,GAAA,SAAA;EAAa;EAcH,QAAA,CAAA,EAAA,MAAA;EAGU;EAUd,UAAA,CAAA,EAAA,MAAA;EAOa;EAAoB,KAAA,CAAA,EAAA,MAAA;EAAU;EAAc,QAAA,CAAA,EAAA,MAAA;EAgBvC;EAAR,OAAA,CAAA,EAAA,MAAA;EAA8B;EAAe,MAAA,CAAA,EAAA,MAAA;EAahD;;;;;;;;;;;;;SAjGP;;;;;iBAMO,yBAAA;;;;;;;;IAQZ;;;;iBAiBY,aAAA,OACR,0BACK,QAAQ,iBAClB;;;;cAcU,eAAA;;wBAGU;;;;SAUd;;;;sBAOa,oBAAoB,UAAU,cAAc;;;;;;;;oBAgB/C,QAAQ,iBAAsB;;;;;iBAajC,qBAAA,WACJ,gBACT"}
1
+ {"version":3,"file":"metadata.d.ts","names":[],"sources":["../src/metadata.ts"],"mappings":";;AAGA;;UAAiB,aAAA;EA8BF;EA5Bb,OAAA;EAEA;EAAA,SAAA;EAIA;EAFA,QAAA;EAMA;EAJA,UAAA;EAQA;EANA,KAAA;EAUA;EARA,QAAA;EAYA;EAVA,OAAA;EAcA;EAZA,MAAA;EAcO;EAZP,QAAA;EAYa;EAVb,SAAA;EAgBuC;EAdvC,SAAA;EAsBe;EApBf,SAAA;EAcA;EAZA,MAAA;EAcA;EAZA,aAAA;EAcA;EAZA,IAAA,GAAO,MAAA;AAAA;;;;iBAMO,yBAAA,CAA0B,OAAA;EACxC,MAAA;EACA,KAAA;EACA,SAAA;EACA,SAAA;EACA,OAAA;EACA,QAAA;EACA,SAAA;AAAA,IACE,aAAA;;;;iBAiBY,aAAA,CACd,IAAA,EAAM,aAAA,EACN,SAAA,EAAW,OAAA,CAAQ,aAAA,IAClB,aAAA;;;;cAcU,eAAA;EAAA,QACH,QAAA;cAEI,OAAA,GAAS,aAAA;EAHK;;;EAa1B,GAAA,CAAA,GAAO,aAAA;EAOiC;;;EAAxC,GAAA,iBAAoB,aAAA,CAAA,CAAe,GAAA,EAAK,CAAA,EAAG,KAAA,EAAO,aAAA,CAAc,CAAA;EAgB/C;;;EARjB,GAAA,CAAI,GAAA,UAAa,KAAA;EA3BT;;;EAmCR,KAAA,CAAM,SAAA,GAAW,OAAA,CAAQ,aAAA,IAAsB,eAAA;AAAA;;;;iBAajC,qBAAA,CACd,OAAA,GAAU,aAAA,GACT,eAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"subscriber.d.ts","names":[],"sources":["../src/subscriber.ts"],"sourcesContent":[],"mappings":";;;;;;iBAKsB,yBAAyB,qBACxC,gBACC,UAAU,uBAEL;EAJS,OAAA,CAAA,EAAA,MAAA;EAAyB,UAAA,CAAA,EAAA,MAAA;CACxC,EAAA,GAKA,OALA,CAAA,IAAA,CAAA,CAAA,EAKa,OALb,CAAA,GAAA,GAKa,OALb,CAAA,IAAA,CAAA,CAAA"}
1
+ {"version":3,"file":"subscriber.d.ts","names":[],"sources":["../src/subscriber.ts"],"mappings":";;;;;;iBAKsB,cAAA,WAAyB,cAAA,CAAA,CAC7C,GAAA,EAAK,QAAA,EACL,IAAA,EAAM,SAAA,CAAU,CAAA,GAChB,OAAA,GACE,OAAA,EAAS,CAAA,EACT,GAAA;EAAO,OAAA;EAAkB,UAAA;AAAA,MACtB,OAAA,SAAa,OAAA,OAAA,OAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contractspec/lib.bus",
3
- "version": "1.56.0",
3
+ "version": "1.57.0",
4
4
  "description": "Event bus and messaging primitives",
5
5
  "keywords": [
6
6
  "contractspec",
@@ -22,15 +22,15 @@
22
22
  "lint:check": "eslint src"
23
23
  },
24
24
  "devDependencies": {
25
- "@contractspec/tool.tsdown": "1.56.0",
26
- "@contractspec/tool.typescript": "1.56.0",
25
+ "@contractspec/tool.tsdown": "1.57.0",
26
+ "@contractspec/tool.typescript": "1.57.0",
27
27
  "@types/express": "^5.0.3",
28
- "tsdown": "^0.19.0",
28
+ "tsdown": "^0.20.3",
29
29
  "typescript": "^5.9.3"
30
30
  },
31
31
  "dependencies": {
32
- "@contractspec/lib.schema": "1.56.0",
33
- "@contractspec/lib.contracts": "1.56.0",
32
+ "@contractspec/lib.schema": "1.57.0",
33
+ "@contractspec/lib.contracts": "1.57.0",
34
34
  "compare-versions": "^6.1.1"
35
35
  },
36
36
  "peerDependencies": {