@geekmidas/constructs 10.0.0-alpha.39 → 10.0.0-alpha.40

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.
@@ -1 +1 @@
1
- {"version":3,"file":"Construct-BQALFCh2.d.cts","names":[],"sources":["../src/topic/Topic.ts","../src/publisher.ts","../src/Construct.ts"],"mappings":";;;;;;;;;;KAoBY,cAAc,eAAe;;;;;KAM7B,aAAa,gBAAgB,kBACvC,WAAW,mBAAmB,mBAC9B,GACA,oBAAoB,QAAQ,cAEtB;;;;;;;;;;;;;;;;;;;cAoBK,MACZ,+BACA,gBAAgB,cAAc,qBACrB;EACT;SAEO,QAAQ,eAAe,OAAO,cAAc;;;;;;;WAc1C;;WAGA,MAAM;;;;;;WAON,cAAc;;;;;;;;;;;;;WAcd,SAAS,QACjB,aAAa,QACb,eAAe,aAAa;;mBAIZ;EAEjB,YAAY,MAAM,cAAc,QAAQ,SAAS,aAAa;;;;;;;;EA8B9D,WAAW;;MAmBP,qBAAqB;;UAKT,aAAa,gBAAgB;;EAE7C,QAAQ;;;;;;;;;;;UChJQ,WAAW;;EAE3B,OAAO,gBAAgB;EACvB;;EAEA,UAAU,QAAQ;;EAElB,QAAQ,QAAQ;;;;;;KAOL,SACX,eAAe,iBACf,WACG,eAAe,oBAAoB,cAEnC,WAAW;EACX,MAAM;EACN,UAAU,QAAQ,YAAY,oBAAoB,QAAQ;EAC1D,QAAQ,QAAQ;WAEV;;iBAwFY,uBACrB,kBAAkB,0CAElB,WAAW,KAAK,iCAChB,UAAU,oBAAoB,YAC9B,kBAAkB,uBAClB,SAAQ,QACR,WAAU,SAAS,2BAA6B;;;;;;;;;cC5G3C,gBAAgB;uBAsBA,UACrB,gBAAgB,SAAS,QACzB,kBAAkB,0CAClB,kBAAkB,gBAClB,kDACA,sBAAsB,sCACtB,8CACA;WAoBiB,MAAM;WACN,QAAQ;WACR,UAAU;;;;;WAKV,QAAQ;EACjB,eAAe;WACN;WACA;WACA,wBAAwB,QACvC,0BACA;;;;;;;;;;;WAYe;;EA1CV,kBAAkB,QAAQ,sBAAsB;;;;;;;;;;;;;EAchD;EAEP,YACiB,MAAM,eACN,QAAQ,SACR,UAAU,WAKV;;;;;EAAA,SAAQ,cACjB,eAAe,uBACN,8BACA,iCACA,wBAAwB,QACvC,0BACA,gBAYe;;;;;;;;;;;EAAA;;;;;;;;;;;;;;;;;;;;;;;;EA0BX,eACL;IAAQ;MACN;;aAiEQ;EACX;EACA;EACA;EACA;EACA;EACA;;;;;;;;;;;;;;;;iBAiBqB,aAAa,SAAS,UAAU,QAAQ"}
1
+ {"version":3,"file":"Construct-BQALFCh2.d.cts","names":[],"sources":["../src/topic/Topic.ts","../src/publisher.ts","../src/Construct.ts"],"mappings":";;;;;;;;;;KAoBY,cAAc,eAAe;;;;;KAM7B,aAAa,gBAAgB,kBACvC,WAAW,mBAAmB,mBAC9B,GACA,oBAAoB,QAAQ,cAEtB;;;;;;;;;;;;;;;;;;;cAoBK,MACZ,+BACA,gBAAgB,cAAc,qBACrB;EACT;SAEO,QAAQ,eAAe,OAAO,cAAc;;;;;;;WAc1C;;WAGA,MAAM;;;;;;WAON,cAAc;;;;;;;;;;;;;WAcd,SAAS,QACjB,aAAa,QACb,eAAe,aAAa;;mBAIZ;EAEjB,YAAY,MAAM,cAAc,QAAQ,SAAS,aAAa;;;;;;;;EAkC9D,WAAW;;MAmBP,qBAAqB;;UAKT,aAAa,gBAAgB;;EAE7C,QAAQ;;;;;;;;;;;UCpJQ,WAAW;;EAE3B,OAAO,gBAAgB;EACvB;;EAEA,UAAU,QAAQ;;EAElB,QAAQ,QAAQ;;;;;;KAOL,SACX,eAAe,iBACf,WACG,eAAe,oBAAoB,cAEnC,WAAW;EACX,MAAM;EACN,UAAU,QAAQ,YAAY,oBAAoB,QAAQ;EAC1D,QAAQ,QAAQ;WAEV;;iBAwFY,uBACrB,kBAAkB,0CAElB,WAAW,KAAK,iCAChB,UAAU,oBAAoB,YAC9B,kBAAkB,uBAClB,SAAQ,QACR,WAAU,SAAS,2BAA6B;;;;;;;;;cC5G3C,gBAAgB;uBAsBA,UACrB,gBAAgB,SAAS,QACzB,kBAAkB,0CAClB,kBAAkB,gBAClB,kDACA,sBAAsB,sCACtB,8CACA;WAoBiB,MAAM;WACN,QAAQ;WACR,UAAU;;;;;WAKV,QAAQ;EACjB,eAAe;WACN;WACA;WACA,wBAAwB,QACvC,0BACA;;;;;;;;;;;WAYe;;EA1CV,kBAAkB,QAAQ,sBAAsB;;;;;;;;;;;;;EAchD;EAEP,YACiB,MAAM,eACN,QAAQ,SACR,UAAU,WAKV;;;;;EAAA,SAAQ,cACjB,eAAe,uBACN,8BACA,iCACA,wBAAwB,QACvC,0BACA,gBAYe;;;;;;;;;;;EAAA;;;;;;;;;;;;;;;;;;;;;;;;EA0BX,eACL;IAAQ;MACN;;aAiEQ;EACX;EACA;EACA;EACA;EACA;EACA;;;;;;;;;;;;;;;;iBAiBqB,aAAa,SAAS,UAAU,QAAQ"}
@@ -1 +1 @@
1
- {"version":3,"file":"Construct-QXnl3VqT.d.mts","names":[],"sources":["../src/topic/Topic.ts","../src/publisher.ts","../src/Construct.ts"],"mappings":";;;;;;;;;;KAoBY,cAAc,eAAe;;;;;KAM7B,aAAa,gBAAgB,kBACvC,WAAW,mBAAmB,mBAC9B,GACA,oBAAoB,QAAQ,cAEtB;;;;;;;;;;;;;;;;;;;cAoBK,MACZ,+BACA,gBAAgB,cAAc,qBACrB;EACT;SAEO,QAAQ,eAAe,OAAO,cAAc;;;;;;;WAc1C;;WAGA,MAAM;;;;;;WAON,cAAc;;;;;;;;;;;;;WAcd,SAAS,QACjB,aAAa,QACb,eAAe,aAAa;;mBAIZ;EAEjB,YAAY,MAAM,cAAc,QAAQ,SAAS,aAAa;;;;;;;;EA8B9D,WAAW;;MAmBP,qBAAqB;;UAKT,aAAa,gBAAgB;;EAE7C,QAAQ;;;;;;;;;;;UChJQ,WAAW;;EAE3B,OAAO,gBAAgB;EACvB;;EAEA,UAAU,QAAQ;;EAElB,QAAQ,QAAQ;;;;;;KAOL,SACX,eAAe,iBACf,WACG,eAAe,oBAAoB,cAEnC,WAAW;EACX,MAAM;EACN,UAAU,QAAQ,YAAY,oBAAoB,QAAQ;EAC1D,QAAQ,QAAQ;WAEV;;iBAwFY,uBACrB,kBAAkB,0CAElB,WAAW,KAAK,iCAChB,UAAU,oBAAoB,YAC9B,kBAAkB,uBAClB,SAAQ,QACR,WAAU,SAAS,2BAA6B;;;;;;;;;cC5G3C,gBAAgB;uBAsBA,UACrB,gBAAgB,SAAS,QACzB,kBAAkB,0CAClB,kBAAkB,gBAClB,kDACA,sBAAsB,sCACtB,8CACA;WAoBiB,MAAM;WACN,QAAQ;WACR,UAAU;;;;;WAKV,QAAQ;EACjB,eAAe;WACN;WACA;WACA,wBAAwB,QACvC,0BACA;;;;;;;;;;;WAYe;;EA1CV,kBAAkB,QAAQ,sBAAsB;;;;;;;;;;;;;EAchD;EAEP,YACiB,MAAM,eACN,QAAQ,SACR,UAAU,WAKV;;;;;EAAA,SAAQ,cACjB,eAAe,uBACN,8BACA,iCACA,wBAAwB,QACvC,0BACA,gBAYe;;;;;;;;;;;EAAA;;;;;;;;;;;;;;;;;;;;;;;;EA0BX,eACL;IAAQ;MACN;;aAiEQ;EACX;EACA;EACA;EACA;EACA;EACA;;;;;;;;;;;;;;;;iBAiBqB,aAAa,SAAS,UAAU,QAAQ"}
1
+ {"version":3,"file":"Construct-QXnl3VqT.d.mts","names":[],"sources":["../src/topic/Topic.ts","../src/publisher.ts","../src/Construct.ts"],"mappings":";;;;;;;;;;KAoBY,cAAc,eAAe;;;;;KAM7B,aAAa,gBAAgB,kBACvC,WAAW,mBAAmB,mBAC9B,GACA,oBAAoB,QAAQ,cAEtB;;;;;;;;;;;;;;;;;;;cAoBK,MACZ,+BACA,gBAAgB,cAAc,qBACrB;EACT;SAEO,QAAQ,eAAe,OAAO,cAAc;;;;;;;WAc1C;;WAGA,MAAM;;;;;;WAON,cAAc;;;;;;;;;;;;;WAcd,SAAS,QACjB,aAAa,QACb,eAAe,aAAa;;mBAIZ;EAEjB,YAAY,MAAM,cAAc,QAAQ,SAAS,aAAa;;;;;;;;EAkC9D,WAAW;;MAmBP,qBAAqB;;UAKT,aAAa,gBAAgB;;EAE7C,QAAQ;;;;;;;;;;;UCpJQ,WAAW;;EAE3B,OAAO,gBAAgB;EACvB;;EAEA,UAAU,QAAQ;;EAElB,QAAQ,QAAQ;;;;;;KAOL,SACX,eAAe,iBACf,WACG,eAAe,oBAAoB,cAEnC,WAAW;EACX,MAAM;EACN,UAAU,QAAQ,YAAY,oBAAoB,QAAQ;EAC1D,QAAQ,QAAQ;WAEV;;iBAwFY,uBACrB,kBAAkB,0CAElB,WAAW,KAAK,iCAChB,UAAU,oBAAoB,YAC9B,kBAAkB,uBAClB,SAAQ,QACR,WAAU,SAAS,2BAA6B;;;;;;;;;cC5G3C,gBAAgB;uBAsBA,UACrB,gBAAgB,SAAS,QACzB,kBAAkB,0CAClB,kBAAkB,gBAClB,kDACA,sBAAsB,sCACtB,8CACA;WAoBiB,MAAM;WACN,QAAQ;WACR,UAAU;;;;;WAKV,QAAQ;EACjB,eAAe;WACN;WACA;WACA,wBAAwB,QACvC,0BACA;;;;;;;;;;;WAYe;;EA1CV,kBAAkB,QAAQ,sBAAsB;;;;;;;;;;;;;EAchD;EAEP,YACiB,MAAM,eACN,QAAQ,SACR,UAAU,WAKV;;;;;EAAA,SAAQ,cACjB,eAAe,uBACN,8BACA,iCACA,wBAAwB,QACvC,0BACA,gBAYe;;;;;;;;;;;EAAA;;;;;;;;;;;;;;;;;;;;;;;;EA0BX,eACL;IAAQ;MACN;;aAiEQ;EACX;EACA;EACA;EACA;EACA;EACA;;;;;;;;;;;;;;;;iBAiBqB,aAAa,SAAS,UAAU,QAAQ"}
@@ -63,11 +63,12 @@ var Topic = class extends require_Construct.Construct {
63
63
  this.eventSchemas = options.events;
64
64
  this.connectionKey = (0, _geekmidas_manifest.provideKey)(this.id, "publisherConnectionString");
65
65
  const envVar = this.connectionKey;
66
+ const topic = this.name;
66
67
  this.service = {
67
68
  serviceName: (0, _geekmidas_manifest.serviceKey)(this.id),
68
69
  async register({ envParser }) {
69
70
  const { connectionString } = envParser.create((get) => ({ connectionString: get(envVar).string() })).parse();
70
- return _geekmidas_events.Publisher.fromConnectionString(connectionString);
71
+ return _geekmidas_events.Publisher.fromConnectionString(connectionString, { topic });
71
72
  }
72
73
  };
73
74
  }
@@ -100,4 +101,4 @@ Object.defineProperty(exports, "Topic", {
100
101
  }
101
102
  });
102
103
 
103
- //# sourceMappingURL=Topic-BBVXC3H4.cjs.map
104
+ //# sourceMappingURL=Topic-CkxOTrMv.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Topic-BBVXC3H4.cjs","names":["Construct","DEFAULT_LOGGER","canonicalId","provideKey","serviceKey","Publisher"],"sources":["../src/topic/Topic.ts"],"sourcesContent":["import {\n\ttype EventPublisher,\n\ttype EventPublisherConnectionString,\n\ttype PublishableMessage,\n\tPublisher,\n} from '@geekmidas/events';\nimport { DEFAULT_LOGGER } from '@geekmidas/logger/console';\nimport {\n\ttype ConstructName,\n\tcanonicalId,\n\ttype Declaration,\n\tprovideKey,\n\tserviceKey,\n} from '@geekmidas/manifest';\nimport type { InferStandardSchema } from '@geekmidas/schema';\nimport type { Service } from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport { Construct, ConstructType } from '../Construct';\n\n/** A topic's event contract — a map of event type → payload schema. */\nexport type TopicEvents = Record<string, StandardSchemaV1>;\n\n/**\n * The union of wire messages a topic carries, derived from its event map:\n * `{ type: 'user.created'; payload: … } | { type: 'user.updated'; payload: … }`.\n */\nexport type TopicMessage<TEvents extends TopicEvents> = {\n\t[K in keyof TEvents & string]: PublishableMessage<\n\t\tK,\n\t\tInferStandardSchema<TEvents[K]>\n\t>;\n}[keyof TEvents & string];\n\n/**\n * A topic — pub/sub fan-out. Unlike a `Queue` (point-to-point, one consumer), a\n * topic is a *resource* owned by no single handler: it declares the event\n * contract, and any number of subscribers (`worker.topic(topic)`) bind to it.\n * `gkm build` discovers it into the manifest's `topics` field; infra provisions\n * an SNS topic.\n *\n * A construct publishes with `.event(topic, { type, payload })`, repeatable\n * across topics, each event going through its own topic's publisher. That also\n * puts `services.<topic>` in the handler, as `.dependsOn([topic])` does, for an\n * event the handler decides on itself.\n *\n * ```ts\n * export const users = new Topic('Users', {\n * events: { 'user.created': z.object({ userId: z.string() }) },\n * });\n * ```\n */\nexport class Topic<\n\tTName extends string = string,\n\tTEvents extends TopicEvents = TopicEvents,\n> extends Construct {\n\t__IS_TOPIC__ = true;\n\n\tstatic isTopic(obj: unknown): obj is Topic<string, TopicEvents> {\n\t\treturn Boolean(\n\t\t\tobj &&\n\t\t\t\t(obj as { __IS_TOPIC__?: boolean }).__IS_TOPIC__ === true &&\n\t\t\t\t(obj as Construct).type === ConstructType.Topic,\n\t\t);\n\t}\n\n\t/**\n\t * The canonical id this topic is declared under.\n\t *\n\t * Derived from the name, so `users` and `Users` are one topic. The name is\n\t * left alone: subscribers bind to it and it is what the broker routes on.\n\t */\n\treadonly id: string;\n\n\t/** The name it was declared with — what the broker routes on. */\n\treadonly name: TName;\n\n\t/**\n\t * The event contract — a map of event type → payload schema. Named\n\t * `eventSchemas` (not `events`) to avoid clashing with `Construct.events`,\n\t * which is the array of `MappedEvent`s a construct *publishes*.\n\t */\n\treadonly eventSchemas: TEvents;\n\n\t/**\n\t * The topic as a dependency — what `.dependsOn([users])` and\n\t * `.event(users, …)` dissolve into, reachable as `services.users`.\n\t *\n\t * The producer, because publishing is the only thing depending on a topic\n\t * can mean: a subscriber binds to the topic instead, and is never handed\n\t * this. It reads `<NAME>_PUBLISHER_CONNECTION_STRING` and picks the\n\t * transport from its protocol — `pgboss://` locally, `sns://` deployed.\n\t *\n\t * A field assigned once, not a getter: services are cached by object\n\t * identity, and a fresh literal on every access never hit that cache.\n\t */\n\treadonly service: Service<\n\t\tUncapitalize<TName>,\n\t\tEventPublisher<TopicMessage<TEvents>>\n\t>;\n\n\t/** The producer's env key, read by both {@link declare} and the service. */\n\tprivate readonly connectionKey: string;\n\n\tconstructor(name: ConstructName<TName>, options: TopicOptions<TEvents>) {\n\t\tsuper(ConstructType.Topic, DEFAULT_LOGGER, [], []);\n\n\t\tthis.name = name as TName;\n\t\tthis.id = canonicalId(name as string);\n\t\tthis.eventSchemas = options.events;\n\t\tthis.connectionKey = provideKey(this.id, 'publisherConnectionString');\n\n\t\tconst envVar = this.connectionKey;\n\t\tthis.service = {\n\t\t\tserviceName: serviceKey(this.id) as Uncapitalize<TName>,\n\t\t\tasync register({ envParser }) {\n\t\t\t\tconst { connectionString } = envParser\n\t\t\t\t\t.create((get) => ({ connectionString: get(envVar).string() }))\n\t\t\t\t\t.parse();\n\n\t\t\t\treturn Publisher.fromConnectionString<TopicMessage<TEvents>>(\n\t\t\t\t\tconnectionString as EventPublisherConnectionString,\n\t\t\t\t);\n\t\t\t},\n\t\t};\n\t}\n\n\t/**\n\t * What this topic is in the manifest.\n\t *\n\t * Only the producer's key: a subscriber is *bound* to a topic rather than\n\t * depending on it, so the binding is an edge the deploy target reads and not\n\t * an env key anyone can hold. Locally both sides meet on the same broker.\n\t */\n\tdeclare(): Declaration[] {\n\t\treturn [\n\t\t\t{\n\t\t\t\tkind: 'topic',\n\t\t\t\tid: this.id,\n\t\t\t\tprovides: [this.connectionKey],\n\t\t\t\t// The contract this topic carries. Structural: which events exist\n\t\t\t\t// is a fact about the application, and a subscriber binding to one\n\t\t\t\t// that does not is a mistake worth catching at build.\n\t\t\t\tevents: this.eventTypes,\n\t\t\t\t// Bound rather than declared here — a subscriber names the topic,\n\t\t\t\t// not the other way round — so the build fills these from what it\n\t\t\t\t// found. Nested when it does, because position is the trigger.\n\t\t\t\tsubscribers: [],\n\t\t\t},\n\t\t];\n\t}\n\n\t/** The event type names this topic carries. */\n\tget eventTypes(): (keyof TEvents & string)[] {\n\t\treturn Object.keys(this.eventSchemas) as (keyof TEvents & string)[];\n\t}\n}\n\nexport interface TopicOptions<TEvents extends TopicEvents> {\n\t/** Each event this topic carries — its type, and its payload's schema. */\n\tevents: TEvents;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAmDA,IAAa,QAAb,cAGUA,kBAAAA,UAAU;CACnB,eAAe;CAEf,OAAO,QAAQ,KAAiD;EAC/D,OAAO,QACN,OACE,IAAmC,iBAAiB,QACpD,IAAkB,SAAA,8BACrB;CACD;;;;;;;CAQA;;CAGA;;;;;;CAOA;;;;;;;;;;;;;CAcA;;CAMA;CAEA,YAAY,MAA4B,SAAgC;EACvE,MAAA,gCAA2BC,0BAAAA,gBAAgB,CAAC,GAAG,CAAC,CAAC;EAEjD,KAAK,OAAO;EACZ,KAAK,MAAA,GAAKC,oBAAAA,YAAAA,CAAY,IAAc;EACpC,KAAK,eAAe,QAAQ;EAC5B,KAAK,iBAAA,GAAgBC,oBAAAA,WAAAA,CAAW,KAAK,IAAI,2BAA2B;EAEpE,MAAM,SAAS,KAAK;EACpB,KAAK,UAAU;GACd,cAAA,GAAaC,oBAAAA,WAAAA,CAAW,KAAK,EAAE;GAC/B,MAAM,SAAS,EAAE,aAAa;IAC7B,MAAM,EAAE,qBAAqB,UAC3B,QAAQ,SAAS,EAAE,kBAAkB,IAAI,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAC7D,MAAM;IAER,OAAOC,kBAAAA,UAAU,qBAChB,gBACD;GACD;EACD;CACD;;;;;;;;CASA,UAAyB;EACxB,OAAO,CACN;GACC,MAAM;GACN,IAAI,KAAK;GACT,UAAU,CAAC,KAAK,aAAa;GAI7B,QAAQ,KAAK;GAIb,aAAa,CAAC;EACf,CACD;CACD;;CAGA,IAAI,aAAyC;EAC5C,OAAO,OAAO,KAAK,KAAK,YAAY;CACrC;AACD"}
1
+ {"version":3,"file":"Topic-CkxOTrMv.cjs","names":["Construct","DEFAULT_LOGGER","canonicalId","provideKey","serviceKey","Publisher"],"sources":["../src/topic/Topic.ts"],"sourcesContent":["import {\n\ttype EventPublisher,\n\ttype EventPublisherConnectionString,\n\ttype PublishableMessage,\n\tPublisher,\n} from '@geekmidas/events';\nimport { DEFAULT_LOGGER } from '@geekmidas/logger/console';\nimport {\n\ttype ConstructName,\n\tcanonicalId,\n\ttype Declaration,\n\tprovideKey,\n\tserviceKey,\n} from '@geekmidas/manifest';\nimport type { InferStandardSchema } from '@geekmidas/schema';\nimport type { Service } from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport { Construct, ConstructType } from '../Construct';\n\n/** A topic's event contract — a map of event type → payload schema. */\nexport type TopicEvents = Record<string, StandardSchemaV1>;\n\n/**\n * The union of wire messages a topic carries, derived from its event map:\n * `{ type: 'user.created'; payload: … } | { type: 'user.updated'; payload: … }`.\n */\nexport type TopicMessage<TEvents extends TopicEvents> = {\n\t[K in keyof TEvents & string]: PublishableMessage<\n\t\tK,\n\t\tInferStandardSchema<TEvents[K]>\n\t>;\n}[keyof TEvents & string];\n\n/**\n * A topic — pub/sub fan-out. Unlike a `Queue` (point-to-point, one consumer), a\n * topic is a *resource* owned by no single handler: it declares the event\n * contract, and any number of subscribers (`worker.topic(topic)`) bind to it.\n * `gkm build` discovers it into the manifest's `topics` field; infra provisions\n * an SNS topic.\n *\n * A construct publishes with `.event(topic, { type, payload })`, repeatable\n * across topics, each event going through its own topic's publisher. That also\n * puts `services.<topic>` in the handler, as `.dependsOn([topic])` does, for an\n * event the handler decides on itself.\n *\n * ```ts\n * export const users = new Topic('Users', {\n * events: { 'user.created': z.object({ userId: z.string() }) },\n * });\n * ```\n */\nexport class Topic<\n\tTName extends string = string,\n\tTEvents extends TopicEvents = TopicEvents,\n> extends Construct {\n\t__IS_TOPIC__ = true;\n\n\tstatic isTopic(obj: unknown): obj is Topic<string, TopicEvents> {\n\t\treturn Boolean(\n\t\t\tobj &&\n\t\t\t\t(obj as { __IS_TOPIC__?: boolean }).__IS_TOPIC__ === true &&\n\t\t\t\t(obj as Construct).type === ConstructType.Topic,\n\t\t);\n\t}\n\n\t/**\n\t * The canonical id this topic is declared under.\n\t *\n\t * Derived from the name, so `users` and `Users` are one topic. The name is\n\t * left alone: subscribers bind to it and it is what the broker routes on.\n\t */\n\treadonly id: string;\n\n\t/** The name it was declared with — what the broker routes on. */\n\treadonly name: TName;\n\n\t/**\n\t * The event contract — a map of event type → payload schema. Named\n\t * `eventSchemas` (not `events`) to avoid clashing with `Construct.events`,\n\t * which is the array of `MappedEvent`s a construct *publishes*.\n\t */\n\treadonly eventSchemas: TEvents;\n\n\t/**\n\t * The topic as a dependency — what `.dependsOn([users])` and\n\t * `.event(users, …)` dissolve into, reachable as `services.users`.\n\t *\n\t * The producer, because publishing is the only thing depending on a topic\n\t * can mean: a subscriber binds to the topic instead, and is never handed\n\t * this. It reads `<NAME>_PUBLISHER_CONNECTION_STRING` and picks the\n\t * transport from its protocol — `pgboss://` locally, `sns://` deployed.\n\t *\n\t * A field assigned once, not a getter: services are cached by object\n\t * identity, and a fresh literal on every access never hit that cache.\n\t */\n\treadonly service: Service<\n\t\tUncapitalize<TName>,\n\t\tEventPublisher<TopicMessage<TEvents>>\n\t>;\n\n\t/** The producer's env key, read by both {@link declare} and the service. */\n\tprivate readonly connectionKey: string;\n\n\tconstructor(name: ConstructName<TName>, options: TopicOptions<TEvents>) {\n\t\tsuper(ConstructType.Topic, DEFAULT_LOGGER, [], []);\n\n\t\tthis.name = name as TName;\n\t\tthis.id = canonicalId(name as string);\n\t\tthis.eventSchemas = options.events;\n\t\tthis.connectionKey = provideKey(this.id, 'publisherConnectionString');\n\n\t\tconst envVar = this.connectionKey;\n\t\tconst topic = this.name as string;\n\t\tthis.service = {\n\t\t\tserviceName: serviceKey(this.id) as Uncapitalize<TName>,\n\t\t\tasync register({ envParser }) {\n\t\t\t\tconst { connectionString } = envParser\n\t\t\t\t\t.create((get) => ({ connectionString: get(envVar).string() }))\n\t\t\t\t\t.parse();\n\n\t\t\t\t// Named as a topic, so a broker with one address for everything\n\t\t\t\t// (pg-boss) fans each message out to every subscriber.\n\t\t\t\treturn Publisher.fromConnectionString<TopicMessage<TEvents>>(\n\t\t\t\t\tconnectionString as EventPublisherConnectionString,\n\t\t\t\t\t{ topic },\n\t\t\t\t);\n\t\t\t},\n\t\t};\n\t}\n\n\t/**\n\t * What this topic is in the manifest.\n\t *\n\t * Only the producer's key: a subscriber is *bound* to a topic rather than\n\t * depending on it, so the binding is an edge the deploy target reads and not\n\t * an env key anyone can hold. Locally both sides meet on the same broker.\n\t */\n\tdeclare(): Declaration[] {\n\t\treturn [\n\t\t\t{\n\t\t\t\tkind: 'topic',\n\t\t\t\tid: this.id,\n\t\t\t\tprovides: [this.connectionKey],\n\t\t\t\t// The contract this topic carries. Structural: which events exist\n\t\t\t\t// is a fact about the application, and a subscriber binding to one\n\t\t\t\t// that does not is a mistake worth catching at build.\n\t\t\t\tevents: this.eventTypes,\n\t\t\t\t// Bound rather than declared here — a subscriber names the topic,\n\t\t\t\t// not the other way round — so the build fills these from what it\n\t\t\t\t// found. Nested when it does, because position is the trigger.\n\t\t\t\tsubscribers: [],\n\t\t\t},\n\t\t];\n\t}\n\n\t/** The event type names this topic carries. */\n\tget eventTypes(): (keyof TEvents & string)[] {\n\t\treturn Object.keys(this.eventSchemas) as (keyof TEvents & string)[];\n\t}\n}\n\nexport interface TopicOptions<TEvents extends TopicEvents> {\n\t/** Each event this topic carries — its type, and its payload's schema. */\n\tevents: TEvents;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAmDA,IAAa,QAAb,cAGUA,kBAAAA,UAAU;CACnB,eAAe;CAEf,OAAO,QAAQ,KAAiD;EAC/D,OAAO,QACN,OACE,IAAmC,iBAAiB,QACpD,IAAkB,SAAA,8BACrB;CACD;;;;;;;CAQA;;CAGA;;;;;;CAOA;;;;;;;;;;;;;CAcA;;CAMA;CAEA,YAAY,MAA4B,SAAgC;EACvE,MAAA,gCAA2BC,0BAAAA,gBAAgB,CAAC,GAAG,CAAC,CAAC;EAEjD,KAAK,OAAO;EACZ,KAAK,MAAA,GAAKC,oBAAAA,YAAAA,CAAY,IAAc;EACpC,KAAK,eAAe,QAAQ;EAC5B,KAAK,iBAAA,GAAgBC,oBAAAA,WAAAA,CAAW,KAAK,IAAI,2BAA2B;EAEpE,MAAM,SAAS,KAAK;EACpB,MAAM,QAAQ,KAAK;EACnB,KAAK,UAAU;GACd,cAAA,GAAaC,oBAAAA,WAAAA,CAAW,KAAK,EAAE;GAC/B,MAAM,SAAS,EAAE,aAAa;IAC7B,MAAM,EAAE,qBAAqB,UAC3B,QAAQ,SAAS,EAAE,kBAAkB,IAAI,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAC7D,MAAM;IAIR,OAAOC,kBAAAA,UAAU,qBAChB,kBACA,EAAE,MAAM,CACT;GACD;EACD;CACD;;;;;;;;CASA,UAAyB;EACxB,OAAO,CACN;GACC,MAAM;GACN,IAAI,KAAK;GACT,UAAU,CAAC,KAAK,aAAa;GAI7B,QAAQ,KAAK;GAIb,aAAa,CAAC;EACf,CACD;CACD;;CAGA,IAAI,aAAyC;EAC5C,OAAO,OAAO,KAAK,KAAK,YAAY;CACrC;AACD"}
@@ -63,11 +63,12 @@ var Topic = class extends Construct {
63
63
  this.eventSchemas = options.events;
64
64
  this.connectionKey = provideKey(this.id, "publisherConnectionString");
65
65
  const envVar = this.connectionKey;
66
+ const topic = this.name;
66
67
  this.service = {
67
68
  serviceName: serviceKey(this.id),
68
69
  async register({ envParser }) {
69
70
  const { connectionString } = envParser.create((get) => ({ connectionString: get(envVar).string() })).parse();
70
- return Publisher.fromConnectionString(connectionString);
71
+ return Publisher.fromConnectionString(connectionString, { topic });
71
72
  }
72
73
  };
73
74
  }
@@ -95,4 +96,4 @@ var Topic = class extends Construct {
95
96
  //#endregion
96
97
  export { Topic as t };
97
98
 
98
- //# sourceMappingURL=Topic-DOw051ch.mjs.map
99
+ //# sourceMappingURL=Topic-hPffOsot.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Topic-DOw051ch.mjs","names":[],"sources":["../src/topic/Topic.ts"],"sourcesContent":["import {\n\ttype EventPublisher,\n\ttype EventPublisherConnectionString,\n\ttype PublishableMessage,\n\tPublisher,\n} from '@geekmidas/events';\nimport { DEFAULT_LOGGER } from '@geekmidas/logger/console';\nimport {\n\ttype ConstructName,\n\tcanonicalId,\n\ttype Declaration,\n\tprovideKey,\n\tserviceKey,\n} from '@geekmidas/manifest';\nimport type { InferStandardSchema } from '@geekmidas/schema';\nimport type { Service } from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport { Construct, ConstructType } from '../Construct';\n\n/** A topic's event contract — a map of event type → payload schema. */\nexport type TopicEvents = Record<string, StandardSchemaV1>;\n\n/**\n * The union of wire messages a topic carries, derived from its event map:\n * `{ type: 'user.created'; payload: … } | { type: 'user.updated'; payload: … }`.\n */\nexport type TopicMessage<TEvents extends TopicEvents> = {\n\t[K in keyof TEvents & string]: PublishableMessage<\n\t\tK,\n\t\tInferStandardSchema<TEvents[K]>\n\t>;\n}[keyof TEvents & string];\n\n/**\n * A topic — pub/sub fan-out. Unlike a `Queue` (point-to-point, one consumer), a\n * topic is a *resource* owned by no single handler: it declares the event\n * contract, and any number of subscribers (`worker.topic(topic)`) bind to it.\n * `gkm build` discovers it into the manifest's `topics` field; infra provisions\n * an SNS topic.\n *\n * A construct publishes with `.event(topic, { type, payload })`, repeatable\n * across topics, each event going through its own topic's publisher. That also\n * puts `services.<topic>` in the handler, as `.dependsOn([topic])` does, for an\n * event the handler decides on itself.\n *\n * ```ts\n * export const users = new Topic('Users', {\n * events: { 'user.created': z.object({ userId: z.string() }) },\n * });\n * ```\n */\nexport class Topic<\n\tTName extends string = string,\n\tTEvents extends TopicEvents = TopicEvents,\n> extends Construct {\n\t__IS_TOPIC__ = true;\n\n\tstatic isTopic(obj: unknown): obj is Topic<string, TopicEvents> {\n\t\treturn Boolean(\n\t\t\tobj &&\n\t\t\t\t(obj as { __IS_TOPIC__?: boolean }).__IS_TOPIC__ === true &&\n\t\t\t\t(obj as Construct).type === ConstructType.Topic,\n\t\t);\n\t}\n\n\t/**\n\t * The canonical id this topic is declared under.\n\t *\n\t * Derived from the name, so `users` and `Users` are one topic. The name is\n\t * left alone: subscribers bind to it and it is what the broker routes on.\n\t */\n\treadonly id: string;\n\n\t/** The name it was declared with — what the broker routes on. */\n\treadonly name: TName;\n\n\t/**\n\t * The event contract — a map of event type → payload schema. Named\n\t * `eventSchemas` (not `events`) to avoid clashing with `Construct.events`,\n\t * which is the array of `MappedEvent`s a construct *publishes*.\n\t */\n\treadonly eventSchemas: TEvents;\n\n\t/**\n\t * The topic as a dependency — what `.dependsOn([users])` and\n\t * `.event(users, …)` dissolve into, reachable as `services.users`.\n\t *\n\t * The producer, because publishing is the only thing depending on a topic\n\t * can mean: a subscriber binds to the topic instead, and is never handed\n\t * this. It reads `<NAME>_PUBLISHER_CONNECTION_STRING` and picks the\n\t * transport from its protocol — `pgboss://` locally, `sns://` deployed.\n\t *\n\t * A field assigned once, not a getter: services are cached by object\n\t * identity, and a fresh literal on every access never hit that cache.\n\t */\n\treadonly service: Service<\n\t\tUncapitalize<TName>,\n\t\tEventPublisher<TopicMessage<TEvents>>\n\t>;\n\n\t/** The producer's env key, read by both {@link declare} and the service. */\n\tprivate readonly connectionKey: string;\n\n\tconstructor(name: ConstructName<TName>, options: TopicOptions<TEvents>) {\n\t\tsuper(ConstructType.Topic, DEFAULT_LOGGER, [], []);\n\n\t\tthis.name = name as TName;\n\t\tthis.id = canonicalId(name as string);\n\t\tthis.eventSchemas = options.events;\n\t\tthis.connectionKey = provideKey(this.id, 'publisherConnectionString');\n\n\t\tconst envVar = this.connectionKey;\n\t\tthis.service = {\n\t\t\tserviceName: serviceKey(this.id) as Uncapitalize<TName>,\n\t\t\tasync register({ envParser }) {\n\t\t\t\tconst { connectionString } = envParser\n\t\t\t\t\t.create((get) => ({ connectionString: get(envVar).string() }))\n\t\t\t\t\t.parse();\n\n\t\t\t\treturn Publisher.fromConnectionString<TopicMessage<TEvents>>(\n\t\t\t\t\tconnectionString as EventPublisherConnectionString,\n\t\t\t\t);\n\t\t\t},\n\t\t};\n\t}\n\n\t/**\n\t * What this topic is in the manifest.\n\t *\n\t * Only the producer's key: a subscriber is *bound* to a topic rather than\n\t * depending on it, so the binding is an edge the deploy target reads and not\n\t * an env key anyone can hold. Locally both sides meet on the same broker.\n\t */\n\tdeclare(): Declaration[] {\n\t\treturn [\n\t\t\t{\n\t\t\t\tkind: 'topic',\n\t\t\t\tid: this.id,\n\t\t\t\tprovides: [this.connectionKey],\n\t\t\t\t// The contract this topic carries. Structural: which events exist\n\t\t\t\t// is a fact about the application, and a subscriber binding to one\n\t\t\t\t// that does not is a mistake worth catching at build.\n\t\t\t\tevents: this.eventTypes,\n\t\t\t\t// Bound rather than declared here — a subscriber names the topic,\n\t\t\t\t// not the other way round — so the build fills these from what it\n\t\t\t\t// found. Nested when it does, because position is the trigger.\n\t\t\t\tsubscribers: [],\n\t\t\t},\n\t\t];\n\t}\n\n\t/** The event type names this topic carries. */\n\tget eventTypes(): (keyof TEvents & string)[] {\n\t\treturn Object.keys(this.eventSchemas) as (keyof TEvents & string)[];\n\t}\n}\n\nexport interface TopicOptions<TEvents extends TopicEvents> {\n\t/** Each event this topic carries — its type, and its payload's schema. */\n\tevents: TEvents;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAmDA,IAAa,QAAb,cAGU,UAAU;CACnB,eAAe;CAEf,OAAO,QAAQ,KAAiD;EAC/D,OAAO,QACN,OACE,IAAmC,iBAAiB,QACpD,IAAkB,SAAA,8BACrB;CACD;;;;;;;CAQA;;CAGA;;;;;;CAOA;;;;;;;;;;;;;CAcA;;CAMA;CAEA,YAAY,MAA4B,SAAgC;EACvE,MAAA,gCAA2B,gBAAgB,CAAC,GAAG,CAAC,CAAC;EAEjD,KAAK,OAAO;EACZ,KAAK,KAAK,YAAY,IAAc;EACpC,KAAK,eAAe,QAAQ;EAC5B,KAAK,gBAAgB,WAAW,KAAK,IAAI,2BAA2B;EAEpE,MAAM,SAAS,KAAK;EACpB,KAAK,UAAU;GACd,aAAa,WAAW,KAAK,EAAE;GAC/B,MAAM,SAAS,EAAE,aAAa;IAC7B,MAAM,EAAE,qBAAqB,UAC3B,QAAQ,SAAS,EAAE,kBAAkB,IAAI,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAC7D,MAAM;IAER,OAAO,UAAU,qBAChB,gBACD;GACD;EACD;CACD;;;;;;;;CASA,UAAyB;EACxB,OAAO,CACN;GACC,MAAM;GACN,IAAI,KAAK;GACT,UAAU,CAAC,KAAK,aAAa;GAI7B,QAAQ,KAAK;GAIb,aAAa,CAAC;EACf,CACD;CACD;;CAGA,IAAI,aAAyC;EAC5C,OAAO,OAAO,KAAK,KAAK,YAAY;CACrC;AACD"}
1
+ {"version":3,"file":"Topic-hPffOsot.mjs","names":[],"sources":["../src/topic/Topic.ts"],"sourcesContent":["import {\n\ttype EventPublisher,\n\ttype EventPublisherConnectionString,\n\ttype PublishableMessage,\n\tPublisher,\n} from '@geekmidas/events';\nimport { DEFAULT_LOGGER } from '@geekmidas/logger/console';\nimport {\n\ttype ConstructName,\n\tcanonicalId,\n\ttype Declaration,\n\tprovideKey,\n\tserviceKey,\n} from '@geekmidas/manifest';\nimport type { InferStandardSchema } from '@geekmidas/schema';\nimport type { Service } from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport { Construct, ConstructType } from '../Construct';\n\n/** A topic's event contract — a map of event type → payload schema. */\nexport type TopicEvents = Record<string, StandardSchemaV1>;\n\n/**\n * The union of wire messages a topic carries, derived from its event map:\n * `{ type: 'user.created'; payload: … } | { type: 'user.updated'; payload: … }`.\n */\nexport type TopicMessage<TEvents extends TopicEvents> = {\n\t[K in keyof TEvents & string]: PublishableMessage<\n\t\tK,\n\t\tInferStandardSchema<TEvents[K]>\n\t>;\n}[keyof TEvents & string];\n\n/**\n * A topic — pub/sub fan-out. Unlike a `Queue` (point-to-point, one consumer), a\n * topic is a *resource* owned by no single handler: it declares the event\n * contract, and any number of subscribers (`worker.topic(topic)`) bind to it.\n * `gkm build` discovers it into the manifest's `topics` field; infra provisions\n * an SNS topic.\n *\n * A construct publishes with `.event(topic, { type, payload })`, repeatable\n * across topics, each event going through its own topic's publisher. That also\n * puts `services.<topic>` in the handler, as `.dependsOn([topic])` does, for an\n * event the handler decides on itself.\n *\n * ```ts\n * export const users = new Topic('Users', {\n * events: { 'user.created': z.object({ userId: z.string() }) },\n * });\n * ```\n */\nexport class Topic<\n\tTName extends string = string,\n\tTEvents extends TopicEvents = TopicEvents,\n> extends Construct {\n\t__IS_TOPIC__ = true;\n\n\tstatic isTopic(obj: unknown): obj is Topic<string, TopicEvents> {\n\t\treturn Boolean(\n\t\t\tobj &&\n\t\t\t\t(obj as { __IS_TOPIC__?: boolean }).__IS_TOPIC__ === true &&\n\t\t\t\t(obj as Construct).type === ConstructType.Topic,\n\t\t);\n\t}\n\n\t/**\n\t * The canonical id this topic is declared under.\n\t *\n\t * Derived from the name, so `users` and `Users` are one topic. The name is\n\t * left alone: subscribers bind to it and it is what the broker routes on.\n\t */\n\treadonly id: string;\n\n\t/** The name it was declared with — what the broker routes on. */\n\treadonly name: TName;\n\n\t/**\n\t * The event contract — a map of event type → payload schema. Named\n\t * `eventSchemas` (not `events`) to avoid clashing with `Construct.events`,\n\t * which is the array of `MappedEvent`s a construct *publishes*.\n\t */\n\treadonly eventSchemas: TEvents;\n\n\t/**\n\t * The topic as a dependency — what `.dependsOn([users])` and\n\t * `.event(users, …)` dissolve into, reachable as `services.users`.\n\t *\n\t * The producer, because publishing is the only thing depending on a topic\n\t * can mean: a subscriber binds to the topic instead, and is never handed\n\t * this. It reads `<NAME>_PUBLISHER_CONNECTION_STRING` and picks the\n\t * transport from its protocol — `pgboss://` locally, `sns://` deployed.\n\t *\n\t * A field assigned once, not a getter: services are cached by object\n\t * identity, and a fresh literal on every access never hit that cache.\n\t */\n\treadonly service: Service<\n\t\tUncapitalize<TName>,\n\t\tEventPublisher<TopicMessage<TEvents>>\n\t>;\n\n\t/** The producer's env key, read by both {@link declare} and the service. */\n\tprivate readonly connectionKey: string;\n\n\tconstructor(name: ConstructName<TName>, options: TopicOptions<TEvents>) {\n\t\tsuper(ConstructType.Topic, DEFAULT_LOGGER, [], []);\n\n\t\tthis.name = name as TName;\n\t\tthis.id = canonicalId(name as string);\n\t\tthis.eventSchemas = options.events;\n\t\tthis.connectionKey = provideKey(this.id, 'publisherConnectionString');\n\n\t\tconst envVar = this.connectionKey;\n\t\tconst topic = this.name as string;\n\t\tthis.service = {\n\t\t\tserviceName: serviceKey(this.id) as Uncapitalize<TName>,\n\t\t\tasync register({ envParser }) {\n\t\t\t\tconst { connectionString } = envParser\n\t\t\t\t\t.create((get) => ({ connectionString: get(envVar).string() }))\n\t\t\t\t\t.parse();\n\n\t\t\t\t// Named as a topic, so a broker with one address for everything\n\t\t\t\t// (pg-boss) fans each message out to every subscriber.\n\t\t\t\treturn Publisher.fromConnectionString<TopicMessage<TEvents>>(\n\t\t\t\t\tconnectionString as EventPublisherConnectionString,\n\t\t\t\t\t{ topic },\n\t\t\t\t);\n\t\t\t},\n\t\t};\n\t}\n\n\t/**\n\t * What this topic is in the manifest.\n\t *\n\t * Only the producer's key: a subscriber is *bound* to a topic rather than\n\t * depending on it, so the binding is an edge the deploy target reads and not\n\t * an env key anyone can hold. Locally both sides meet on the same broker.\n\t */\n\tdeclare(): Declaration[] {\n\t\treturn [\n\t\t\t{\n\t\t\t\tkind: 'topic',\n\t\t\t\tid: this.id,\n\t\t\t\tprovides: [this.connectionKey],\n\t\t\t\t// The contract this topic carries. Structural: which events exist\n\t\t\t\t// is a fact about the application, and a subscriber binding to one\n\t\t\t\t// that does not is a mistake worth catching at build.\n\t\t\t\tevents: this.eventTypes,\n\t\t\t\t// Bound rather than declared here — a subscriber names the topic,\n\t\t\t\t// not the other way round — so the build fills these from what it\n\t\t\t\t// found. Nested when it does, because position is the trigger.\n\t\t\t\tsubscribers: [],\n\t\t\t},\n\t\t];\n\t}\n\n\t/** The event type names this topic carries. */\n\tget eventTypes(): (keyof TEvents & string)[] {\n\t\treturn Object.keys(this.eventSchemas) as (keyof TEvents & string)[];\n\t}\n}\n\nexport interface TopicOptions<TEvents extends TopicEvents> {\n\t/** Each event this topic carries — its type, and its payload's schema. */\n\tevents: TEvents;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAmDA,IAAa,QAAb,cAGU,UAAU;CACnB,eAAe;CAEf,OAAO,QAAQ,KAAiD;EAC/D,OAAO,QACN,OACE,IAAmC,iBAAiB,QACpD,IAAkB,SAAA,8BACrB;CACD;;;;;;;CAQA;;CAGA;;;;;;CAOA;;;;;;;;;;;;;CAcA;;CAMA;CAEA,YAAY,MAA4B,SAAgC;EACvE,MAAA,gCAA2B,gBAAgB,CAAC,GAAG,CAAC,CAAC;EAEjD,KAAK,OAAO;EACZ,KAAK,KAAK,YAAY,IAAc;EACpC,KAAK,eAAe,QAAQ;EAC5B,KAAK,gBAAgB,WAAW,KAAK,IAAI,2BAA2B;EAEpE,MAAM,SAAS,KAAK;EACpB,MAAM,QAAQ,KAAK;EACnB,KAAK,UAAU;GACd,aAAa,WAAW,KAAK,EAAE;GAC/B,MAAM,SAAS,EAAE,aAAa;IAC7B,MAAM,EAAE,qBAAqB,UAC3B,QAAQ,SAAS,EAAE,kBAAkB,IAAI,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAC7D,MAAM;IAIR,OAAO,UAAU,qBAChB,kBACA,EAAE,MAAM,CACT;GACD;EACD;CACD;;;;;;;;CASA,UAAyB;EACxB,OAAO,CACN;GACC,MAAM;GACN,IAAI,KAAK;GACT,UAAU,CAAC,KAAK,aAAa;GAI7B,QAAQ,KAAK;GAIb,aAAa,CAAC;EACf,CACD;CACD;;CAGA,IAAI,aAAyC;EAC5C,OAAO,OAAO,KAAK,KAAK,YAAY;CACrC;AACD"}
@@ -14,6 +14,7 @@ let _geekmidas_audit = require("@geekmidas/audit");
14
14
  let _middy_core = require("@middy/core");
15
15
  _middy_core = require_rolldown_runtime.__toESM(_middy_core, 1);
16
16
  let _geekmidas_audit_kysely = require("@geekmidas/audit/kysely");
17
+ let _geekmidas_events_sns = require("@geekmidas/events/sns");
17
18
  //#region src/functions/FunctionExecutionWrapper.ts
18
19
  var FunctionExecutionWrapper = class {
19
20
  envParser;
@@ -865,11 +866,92 @@ var AWSLambdaSubscriber = class {
865
866
  }
866
867
  };
867
868
  //#endregion
869
+ //#region src/subscribers/SnsPushSubscriberAdaptor.ts
870
+ /**
871
+ * A topic subscriber run by SNS pushing to an HTTP endpoint, rather than by a
872
+ * poller.
873
+ *
874
+ * The notification is handed to {@link AWSLambdaSubscriber} as the Lambda
875
+ * event SNS would have invoked a function with, so the parsing, the services
876
+ * and the error handling are the ones that run deployed on Lambda — the
877
+ * endpoint is only a different way for the event to arrive.
878
+ */
879
+ var SnsPushSubscriberAdaptor = class {
880
+ subscriber;
881
+ options;
882
+ lambda;
883
+ constructor(envParser, subscriber, options) {
884
+ this.subscriber = subscriber;
885
+ this.options = options;
886
+ this.lambda = new AWSLambdaSubscriber(envParser, subscriber);
887
+ }
888
+ async handle(body) {
889
+ const message = body;
890
+ if (typeof message?.Type !== "string" || !message.TopicArn) return refuse(400, "not an SNS message");
891
+ if (message.TopicArn !== this.options.topicArn) return refuse(403, `not subscribed to ${message.TopicArn}`);
892
+ if (this.options.verify) try {
893
+ await (0, _geekmidas_events_sns.verifySnsMessage)(message);
894
+ } catch (error) {
895
+ this.subscriber.logger.warn({
896
+ error,
897
+ topicArn: message.TopicArn
898
+ }, "Refused an SNS message that did not verify");
899
+ return refuse(403, error.name);
900
+ }
901
+ switch (message.Type) {
902
+ case "SubscriptionConfirmation":
903
+ await (0, _geekmidas_events_sns.confirmSnsSubscription)(this.options.emulatorEndpoint && message.SubscribeURL ? {
904
+ ...message,
905
+ SubscribeURL: throughEndpoint(message.SubscribeURL, this.options.emulatorEndpoint)
906
+ } : message);
907
+ this.subscriber.logger.info({ topicArn: message.TopicArn }, "Confirmed SNS subscription");
908
+ return {
909
+ status: 200,
910
+ body: { status: "confirmed" }
911
+ };
912
+ case "Notification": try {
913
+ await this.lambda.handler((0, _geekmidas_events_sns.toSnsEvent)(message), {
914
+ awsRequestId: message.MessageId,
915
+ functionName: this.subscriber.topicName ?? "subscriber"
916
+ }, () => {});
917
+ return {
918
+ status: 200,
919
+ body: { status: "ok" }
920
+ };
921
+ } catch (error) {
922
+ return refuse(500, error.name);
923
+ }
924
+ default: return {
925
+ status: 200,
926
+ body: { status: "ignored" }
927
+ };
928
+ }
929
+ }
930
+ };
931
+ /** `url`, sent to `endpoint`'s origin instead of its own. */
932
+ function throughEndpoint(url, endpoint) {
933
+ const target = new URL(url);
934
+ const origin = new URL(endpoint);
935
+ target.protocol = origin.protocol;
936
+ target.host = origin.host;
937
+ return target.toString();
938
+ }
939
+ function refuse(status, error) {
940
+ return {
941
+ status,
942
+ body: {
943
+ status: "refused",
944
+ error
945
+ }
946
+ };
947
+ }
948
+ //#endregion
868
949
  exports.AWSLambdaFunction = AWSLambdaFunction;
869
950
  exports.AWSLambdaQueue = AWSLambdaQueue;
870
951
  exports.AWSLambdaSubscriber = AWSLambdaSubscriber;
871
952
  exports.AWSScheduledFunction = AWSScheduledFunction;
872
953
  exports.AmazonApiGatewayV1Endpoint = AmazonApiGatewayV1Endpoint;
873
954
  exports.AmazonApiGatewayV2Endpoint = AmazonApiGatewayV2Endpoint;
955
+ exports.SnsPushSubscriberAdaptor = SnsPushSubscriberAdaptor;
874
956
 
875
957
  //# sourceMappingURL=aws.cjs.map