@geekmidas/constructs 10.0.0-alpha.123 → 10.0.0-alpha.125

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.
Files changed (183) hide show
  1. package/dist/{Construct-CFW0zjdD.d.cts → Construct-127M7yrl.d.cts} +3 -2
  2. package/dist/{Construct-B7AXn0xH.d.mts.map → Construct-127M7yrl.d.cts.map} +1 -1
  3. package/dist/{Construct-CKUA-CTA.mjs → Construct-CGuc_Ijk.mjs} +2 -1
  4. package/dist/{Construct-CKUA-CTA.mjs.map → Construct-CGuc_Ijk.mjs.map} +1 -1
  5. package/dist/{Construct-DEOVauyA.cjs → Construct-DorKS0Ua.cjs} +2 -1
  6. package/dist/{Construct-DEOVauyA.cjs.map → Construct-DorKS0Ua.cjs.map} +1 -1
  7. package/dist/{Construct-B7AXn0xH.d.mts → Construct-DySlwh8o.d.mts} +3 -2
  8. package/dist/{Construct-CFW0zjdD.d.cts.map → Construct-DySlwh8o.d.mts.map} +1 -1
  9. package/dist/{Cron-bdGRSROY.d.cts → Cron-CFkpEys6.d.cts} +4 -4
  10. package/dist/{Cron-bdGRSROY.d.cts.map → Cron-CFkpEys6.d.cts.map} +1 -1
  11. package/dist/{Cron-i4Uetezh.d.mts → Cron-CQoDSu-n.d.mts} +4 -4
  12. package/dist/{Cron-i4Uetezh.d.mts.map → Cron-CQoDSu-n.d.mts.map} +1 -1
  13. package/dist/{CronBuilder-9H4BnD6W.d.mts → CronBuilder-1W-G_i15.d.mts} +5 -5
  14. package/dist/{CronBuilder-9H4BnD6W.d.mts.map → CronBuilder-1W-G_i15.d.mts.map} +1 -1
  15. package/dist/{CronBuilder-D3sF9tS_.mjs → CronBuilder-BJ5LoadF.mjs} +3 -3
  16. package/dist/{CronBuilder-D3sF9tS_.mjs.map → CronBuilder-BJ5LoadF.mjs.map} +1 -1
  17. package/dist/{CronBuilder-BXTG10u5.cjs → CronBuilder-B_NSP-Y5.cjs} +3 -3
  18. package/dist/{CronBuilder-BXTG10u5.cjs.map → CronBuilder-B_NSP-Y5.cjs.map} +1 -1
  19. package/dist/{CronBuilder-B_24LZpn.d.cts → CronBuilder-OaCRRFVk.d.cts} +5 -5
  20. package/dist/{CronBuilder-B_24LZpn.d.cts.map → CronBuilder-OaCRRFVk.d.cts.map} +1 -1
  21. package/dist/{Endpoint-DgrpG15P.d.cts → Endpoint-BEh-jW-f.d.cts} +4 -4
  22. package/dist/{Endpoint-CeFkSwh5.d.mts.map → Endpoint-BEh-jW-f.d.cts.map} +1 -1
  23. package/dist/{Endpoint-yhR2T2Wt.cjs → Endpoint-BuGFNFSB.cjs} +3 -3
  24. package/dist/{Endpoint-yhR2T2Wt.cjs.map → Endpoint-BuGFNFSB.cjs.map} +1 -1
  25. package/dist/{Endpoint-CeFkSwh5.d.mts → Endpoint-DTXDggNl.d.mts} +4 -4
  26. package/dist/{Endpoint-DgrpG15P.d.cts.map → Endpoint-DTXDggNl.d.mts.map} +1 -1
  27. package/dist/{Endpoint-gPXhHKhJ.mjs → Endpoint-VdaYKc2g.mjs} +3 -3
  28. package/dist/{Endpoint-gPXhHKhJ.mjs.map → Endpoint-VdaYKc2g.mjs.map} +1 -1
  29. package/dist/{EndpointFactory-Cc863XJS.d.cts → EndpointFactory-B2dCS9dy.d.cts} +4 -4
  30. package/dist/{EndpointFactory-Dbw9ugkX.d.mts.map → EndpointFactory-B2dCS9dy.d.cts.map} +1 -1
  31. package/dist/{EndpointFactory-CSrAyazj.cjs → EndpointFactory-C6pVda4y.cjs} +4 -4
  32. package/dist/{EndpointFactory-CSrAyazj.cjs.map → EndpointFactory-C6pVda4y.cjs.map} +1 -1
  33. package/dist/{EndpointFactory-c2ZugGig.mjs → EndpointFactory-CUpqgtaH.mjs} +4 -4
  34. package/dist/{EndpointFactory-c2ZugGig.mjs.map → EndpointFactory-CUpqgtaH.mjs.map} +1 -1
  35. package/dist/{EndpointFactory-Dbw9ugkX.d.mts → EndpointFactory-D2HXcUrJ.d.mts} +4 -4
  36. package/dist/{EndpointFactory-Cc863XJS.d.cts.map → EndpointFactory-D2HXcUrJ.d.mts.map} +1 -1
  37. package/dist/{Function-B0uZhZnU.d.cts → Function-BIEsY0Hm.d.cts} +2 -2
  38. package/dist/{Function-B0uZhZnU.d.cts.map → Function-BIEsY0Hm.d.cts.map} +1 -1
  39. package/dist/{Function-B_9R7Zy1.d.mts → Function-DfMzTGS4.d.mts} +2 -2
  40. package/dist/{Function-B_9R7Zy1.d.mts.map → Function-DfMzTGS4.d.mts.map} +1 -1
  41. package/dist/{HonoEndpointAdaptor-CN1jjUBJ.cjs → HonoEndpointAdaptor-BBTzbP38.cjs} +3 -3
  42. package/dist/{HonoEndpointAdaptor-CN1jjUBJ.cjs.map → HonoEndpointAdaptor-BBTzbP38.cjs.map} +1 -1
  43. package/dist/{HonoEndpointAdaptor-euH1oSdY.mjs → HonoEndpointAdaptor-BR_LyaFE.mjs} +3 -3
  44. package/dist/{HonoEndpointAdaptor-euH1oSdY.mjs.map → HonoEndpointAdaptor-BR_LyaFE.mjs.map} +1 -1
  45. package/dist/ObjectSubscriberBuilder-HmBwFn2o.d.mts +129 -0
  46. package/dist/ObjectSubscriberBuilder-HmBwFn2o.d.mts.map +1 -0
  47. package/dist/ObjectSubscriberBuilder-l-64yUiF.d.cts +129 -0
  48. package/dist/ObjectSubscriberBuilder-l-64yUiF.d.cts.map +1 -0
  49. package/dist/ProcessorBuilder-B2_stA8f.d.mts +216 -0
  50. package/dist/ProcessorBuilder-B2_stA8f.d.mts.map +1 -0
  51. package/dist/ProcessorBuilder-C62bIOwj.d.cts +216 -0
  52. package/dist/ProcessorBuilder-C62bIOwj.d.cts.map +1 -0
  53. package/dist/{Queue-B-dDXyjF.d.mts → Queue-BnK3UH8r.d.mts} +3 -3
  54. package/dist/{Queue-B-dDXyjF.d.mts.map → Queue-BnK3UH8r.d.mts.map} +1 -1
  55. package/dist/{Queue-JbIjVtTf.d.cts → Queue-C4rijiM7.d.cts} +3 -3
  56. package/dist/{Queue-JbIjVtTf.d.cts.map → Queue-C4rijiM7.d.cts.map} +1 -1
  57. package/dist/{Queue-BhuofM3k.cjs → Queue-D4Iaidrs.cjs} +3 -3
  58. package/dist/{Queue-BhuofM3k.cjs.map → Queue-D4Iaidrs.cjs.map} +1 -1
  59. package/dist/{Queue-cmhqEBOB.mjs → Queue-u8w8_HpE.mjs} +3 -3
  60. package/dist/{Queue-cmhqEBOB.mjs.map → Queue-u8w8_HpE.mjs.map} +1 -1
  61. package/dist/{QueueBuilder-DXu1qxT1.mjs → QueueBuilder-2wP5lEC8.mjs} +2 -2
  62. package/dist/{QueueBuilder-DXu1qxT1.mjs.map → QueueBuilder-2wP5lEC8.mjs.map} +1 -1
  63. package/dist/{QueueBuilder-48W-q8In.d.mts → QueueBuilder-B3vL1ED2.d.mts} +2 -2
  64. package/dist/{QueueBuilder-48W-q8In.d.mts.map → QueueBuilder-B3vL1ED2.d.mts.map} +1 -1
  65. package/dist/{QueueBuilder-MfpkJ3R9.d.cts → QueueBuilder-BcPBQSSX.d.cts} +2 -2
  66. package/dist/{QueueBuilder-MfpkJ3R9.d.cts.map → QueueBuilder-BcPBQSSX.d.cts.map} +1 -1
  67. package/dist/{QueueBuilder-DdU2klPu.cjs → QueueBuilder-BxsZ9o5z.cjs} +2 -2
  68. package/dist/{QueueBuilder-DdU2klPu.cjs.map → QueueBuilder-BxsZ9o5z.cjs.map} +1 -1
  69. package/dist/{Subscriber-CEt7kHkc.d.cts → Subscriber-BdCtfFXL.d.cts} +15 -3
  70. package/dist/{Subscriber-CEt7kHkc.d.cts.map → Subscriber-BdCtfFXL.d.cts.map} +1 -1
  71. package/dist/{Subscriber-dsxc5LKa.d.mts → Subscriber-DDo4HFNn.d.mts} +15 -3
  72. package/dist/{Subscriber-dsxc5LKa.d.mts.map → Subscriber-DDo4HFNn.d.mts.map} +1 -1
  73. package/dist/{SubscriberBuilder-DRXfxWn_.mjs → SubscriberBuilder-3msj1U2w.mjs} +99 -3
  74. package/dist/SubscriberBuilder-3msj1U2w.mjs.map +1 -0
  75. package/dist/{SubscriberBuilder-M2EAgQhC.cjs → SubscriberBuilder-CQst6Iz3.cjs} +110 -2
  76. package/dist/SubscriberBuilder-CQst6Iz3.cjs.map +1 -0
  77. package/dist/{Topic-D2R0r24C.mjs → Topic-DN2gYJ-p.mjs} +3 -3
  78. package/dist/{Topic-D2R0r24C.mjs.map → Topic-DN2gYJ-p.mjs.map} +1 -1
  79. package/dist/{Topic-DwS3QO0m.cjs → Topic-Dp9Q7Lj2.cjs} +3 -3
  80. package/dist/{Topic-DwS3QO0m.cjs.map → Topic-Dp9Q7Lj2.cjs.map} +1 -1
  81. package/dist/adaptors/aws.cjs +4 -4
  82. package/dist/adaptors/aws.cjs.map +1 -1
  83. package/dist/adaptors/aws.d.cts +5 -5
  84. package/dist/adaptors/aws.d.mts +5 -5
  85. package/dist/adaptors/aws.mjs +5 -5
  86. package/dist/adaptors/aws.mjs.map +1 -1
  87. package/dist/adaptors/hono.cjs +1 -1
  88. package/dist/adaptors/hono.d.cts +1 -1
  89. package/dist/adaptors/hono.d.mts +1 -1
  90. package/dist/adaptors/hono.mjs +1 -1
  91. package/dist/adaptors/testing.cjs +114 -7
  92. package/dist/adaptors/testing.cjs.map +1 -1
  93. package/dist/adaptors/testing.d.cts +25 -4
  94. package/dist/adaptors/testing.d.cts.map +1 -1
  95. package/dist/adaptors/testing.d.mts +25 -4
  96. package/dist/adaptors/testing.d.mts.map +1 -1
  97. package/dist/adaptors/testing.mjs +114 -7
  98. package/dist/adaptors/testing.mjs.map +1 -1
  99. package/dist/crons/index.cjs +2 -2
  100. package/dist/crons/index.d.cts +2 -2
  101. package/dist/crons/index.d.mts +2 -2
  102. package/dist/crons/index.mjs +2 -2
  103. package/dist/endpoints/index.cjs +3 -3
  104. package/dist/endpoints/index.d.cts +3 -3
  105. package/dist/endpoints/index.d.mts +3 -3
  106. package/dist/endpoints/index.mjs +3 -3
  107. package/dist/file-server.cjs +101 -31
  108. package/dist/file-server.cjs.map +1 -1
  109. package/dist/file-server.d.cts +95 -0
  110. package/dist/file-server.d.cts.map +1 -1
  111. package/dist/file-server.d.mts +95 -0
  112. package/dist/file-server.d.mts.map +1 -1
  113. package/dist/file-server.mjs +101 -33
  114. package/dist/file-server.mjs.map +1 -1
  115. package/dist/functions/index.cjs +1 -1
  116. package/dist/functions/index.d.cts +2 -2
  117. package/dist/functions/index.d.mts +2 -2
  118. package/dist/functions/index.mjs +1 -1
  119. package/dist/{functions-CTI-hDem.mjs → functions-BvIDQKAA.mjs} +3 -3
  120. package/dist/{functions-CTI-hDem.mjs.map → functions-BvIDQKAA.mjs.map} +1 -1
  121. package/dist/{functions-D7O5SGVc.cjs → functions-DLF6-IK8.cjs} +3 -3
  122. package/dist/{functions-D7O5SGVc.cjs.map → functions-DLF6-IK8.cjs.map} +1 -1
  123. package/dist/{index-Cd2w-DRL.d.cts → index-BtzMzhMA.d.cts} +3 -3
  124. package/dist/{index-Cd2w-DRL.d.cts.map → index-BtzMzhMA.d.cts.map} +1 -1
  125. package/dist/{index-aBIcAiH5.d.mts → index-DuriFjYD.d.mts} +3 -3
  126. package/dist/{index-aBIcAiH5.d.mts.map → index-DuriFjYD.d.mts.map} +1 -1
  127. package/dist/index.cjs +1 -1
  128. package/dist/index.d.cts +1 -1
  129. package/dist/index.d.mts +1 -1
  130. package/dist/index.mjs +1 -1
  131. package/dist/pattern-C1CEcglD.mjs +241 -0
  132. package/dist/pattern-C1CEcglD.mjs.map +1 -0
  133. package/dist/pattern-QFaUIr77.cjs +288 -0
  134. package/dist/pattern-QFaUIr77.cjs.map +1 -0
  135. package/dist/processors/index.cjs +214 -0
  136. package/dist/processors/index.cjs.map +1 -0
  137. package/dist/processors/index.d.cts +78 -0
  138. package/dist/processors/index.d.cts.map +1 -0
  139. package/dist/processors/index.d.mts +78 -0
  140. package/dist/processors/index.d.mts.map +1 -0
  141. package/dist/processors/index.mjs +193 -0
  142. package/dist/processors/index.mjs.map +1 -0
  143. package/dist/queue/index.cjs +2 -2
  144. package/dist/queue/index.d.cts +2 -2
  145. package/dist/queue/index.d.mts +2 -2
  146. package/dist/queue/index.mjs +2 -2
  147. package/dist/rest-api.cjs +1 -1
  148. package/dist/rest-api.d.cts +2 -2
  149. package/dist/rest-api.d.mts +2 -2
  150. package/dist/rest-api.mjs +1 -1
  151. package/dist/{runSubscriber-D8nLXIv1.cjs → runSubscriber-B1Yg4dID.cjs} +5 -3
  152. package/dist/runSubscriber-B1Yg4dID.cjs.map +1 -0
  153. package/dist/{runSubscriber-BHvJ8POr.mjs → runSubscriber-JCgkeYbE.mjs} +5 -3
  154. package/dist/runSubscriber-JCgkeYbE.mjs.map +1 -0
  155. package/dist/subscribers/index.cjs +31 -13
  156. package/dist/subscribers/index.cjs.map +1 -1
  157. package/dist/subscribers/index.d.cts +27 -4
  158. package/dist/subscribers/index.d.cts.map +1 -1
  159. package/dist/subscribers/index.d.mts +27 -4
  160. package/dist/subscribers/index.d.mts.map +1 -1
  161. package/dist/subscribers/index.mjs +30 -14
  162. package/dist/subscribers/index.mjs.map +1 -1
  163. package/dist/topic/index.cjs +1 -1
  164. package/dist/topic/index.d.cts +1 -1
  165. package/dist/topic/index.d.mts +1 -1
  166. package/dist/topic/index.mjs +1 -1
  167. package/dist/worker.cjs +25 -4
  168. package/dist/worker.cjs.map +1 -1
  169. package/dist/worker.d.cts +26 -7
  170. package/dist/worker.d.cts.map +1 -1
  171. package/dist/worker.d.mts +26 -7
  172. package/dist/worker.d.mts.map +1 -1
  173. package/dist/worker.mjs +25 -4
  174. package/dist/worker.mjs.map +1 -1
  175. package/package.json +41 -31
  176. package/dist/SubscriberBuilder-BRa_nrmH.d.cts +0 -67
  177. package/dist/SubscriberBuilder-BRa_nrmH.d.cts.map +0 -1
  178. package/dist/SubscriberBuilder-D9bkrYzH.d.mts +0 -67
  179. package/dist/SubscriberBuilder-D9bkrYzH.d.mts.map +0 -1
  180. package/dist/SubscriberBuilder-DRXfxWn_.mjs.map +0 -1
  181. package/dist/SubscriberBuilder-M2EAgQhC.cjs.map +0 -1
  182. package/dist/runSubscriber-BHvJ8POr.mjs.map +0 -1
  183. package/dist/runSubscriber-D8nLXIv1.cjs.map +0 -1
package/dist/worker.cjs CHANGED
@@ -1,10 +1,10 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_construct_interface = require("./construct-interface.cjs");
3
- const require_functions = require("./functions-D7O5SGVc.cjs");
3
+ const require_functions = require("./functions-DLF6-IK8.cjs");
4
4
  const require_surfaceEnv = require("./surfaceEnv-CNimK76p.cjs");
5
- const require_CronBuilder = require("./CronBuilder-BXTG10u5.cjs");
6
- const require_QueueBuilder = require("./QueueBuilder-DdU2klPu.cjs");
7
- const require_SubscriberBuilder = require("./SubscriberBuilder-M2EAgQhC.cjs");
5
+ const require_CronBuilder = require("./CronBuilder-B_NSP-Y5.cjs");
6
+ const require_QueueBuilder = require("./QueueBuilder-BxsZ9o5z.cjs");
7
+ const require_SubscriberBuilder = require("./SubscriberBuilder-CQst6Iz3.cjs");
8
8
  let _geekmidas_manifest = require("@geekmidas/manifest");
9
9
  let _geekmidas_logger_console = require("@geekmidas/logger/console");
10
10
  //#region src/worker.ts
@@ -92,6 +92,27 @@ var Worker = class Worker {
92
92
  return this.own(new require_QueueBuilder.QueueBuilder().logger(this.logger)).queue(name);
93
93
  }
94
94
  /**
95
+ * A runnable that reacts to a bucket's objects changing.
96
+ *
97
+ * A bucket is a topic whose publisher is the storage: the binding is the
98
+ * same as {@link Worker.topic}'s, and so is everything after it — the
99
+ * services, the database, how it is delivered. The handler is handed the
100
+ * objects; reading one still means depending on the bucket,
101
+ * `.dependsOn([uploads])`.
102
+ *
103
+ * @example
104
+ * ```ts
105
+ * export const onAvatar = worker
106
+ * .objects(uploads)
107
+ * .on(['object.created'], { prefix: 'avatars/', suffix: '.png' })
108
+ * .handle(async ({ objects }) => { … });
109
+ * ```
110
+ */
111
+ objects(bucket) {
112
+ const inner = this.own(new require_SubscriberBuilder.SubscriberBuilder().logger(this.logger));
113
+ return new require_SubscriberBuilder.ObjectSubscriberBuilder(inner, bucket.id);
114
+ }
115
+ /**
95
116
  * A runnable that consumes a topic.
96
117
  *
97
118
  * Binding is not publishing: a consumer is handed the topic's event types
@@ -1 +1 @@
1
- {"version":3,"file":"worker.cjs","names":["canonicalId","DEFAULT_LOGGER","envParserFor","edgeTo","CronBuilder","QueueBuilder","SubscriberBuilder","FunctionBuilder"],"sources":["../src/worker.ts"],"sourcesContent":["/**\n * A process with no port.\n *\n * `RestApi` is the process that answers HTTP. This is the one that does not —\n * the thing that runs a schedule, drains a queue, consumes a topic. Until it\n * existed, those had nowhere to say they belonged: a cron declared a schedule\n * and nothing else, and which container ran it was decided by the directory the\n * file sat in. A glob owned it.\n *\n * The cost of that shows up in the scaffolds. `gkm init --template worker` —\n * a project that is background jobs and nothing else — declared\n * `new RestApi('Api', { defaultAuthorizer: 'none', logger })`: an HTTP surface\n * with no HTTP on it, written because it was the only construct that made an\n * app exist and the only way to get a logger into the generated runtime.\n *\n * It takes no authorizer, and that is the tell that these are two constructs\n * rather than one with a flag: a `RestApi` needs one because an HTTP surface\n * can ship open by omission, and nothing reaches a worker from outside.\n *\n * ## Crons on a server target need a database\n *\n * A cron becomes an EventBridge rule on AWS and nothing here is consulted. A\n * server has nothing firing its crons but itself, and a process that schedules\n * in memory fires every job once per replica while reporting success — so the\n * schedule is kept in Postgres, and the worker says which one with\n * `.database(db)`.\n *\n * A worker with crons and no `.database(…)` schedules nothing on a server\n * target and says so at startup. That is a limitation of workers today rather\n * than a design: the store could be a cache, or something the target\n * provisions, and Postgres is what exists.\n *\n * ## The database is everything's default\n *\n * The same `.database(db)` is the database every cron, queue, subscriber and\n * function built from the worker receives as `db`. The work a worker groups is\n * usually work against one database, and naming it per handler was a line\n * repeated in every file that could drift in any one of them. A runnable that\n * works against another names its own with `.database(other)`, which replaces\n * the worker's — client and manifest edge both — rather than adding to it.\n *\n * It is not an app: it declares no path, and its code lives in an app's\n * directory. But it is a process of its own. On a server target each worker\n * with work to do is built — from the app that holds that work — into an image\n * that runs its crons, consumers and subscribers and answers only a health\n * check, so a slow queue never shares a process with the requests an API\n * serves. `gkm dev` still runs everything in one process. Declare as many as\n * the shape of the work wants: each is one container.\n *\n * @example\n * ```ts\n * // constructs/worker.ts\n * import { Worker } from '@geekmidas/constructs/worker';\n * import { logger } from './logger.js';\n *\n * export const worker = new Worker('Worker', { logger }).database(database);\n *\n * // crons/cleanup.ts\n * import { worker } from '@acme/constructs/worker.js';\n *\n * export const cleanup = worker\n * .cron('rate(1 day)')\n * .handle(async ({ db, logger }) => { … });\n *\n * // queues/reports.ts — against another database than the worker's\n * export const reports = worker\n * .queue('Reports')\n * .database(analytics)\n * .message(z.object({ id: z.uuid() }))\n * .handle(async ({ messages, db }) => { … });\n * ```\n */\n\nimport type { EnvironmentParser } from '@geekmidas/envkit';\nimport type { EventPublisher } from '@geekmidas/events';\nimport type { Logger } from '@geekmidas/logger';\nimport { DEFAULT_LOGGER } from '@geekmidas/logger/console';\nimport {\n\ttype ConstructName,\n\tcanonicalId,\n\ttype Declaration,\n\ttype Dependency,\n} from '@geekmidas/manifest';\nimport type { ComposableStandardSchema } from '@geekmidas/schema';\nimport type { Service } from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport {\n\ttype Consumable,\n\ttype Declarable,\n\tedgeTo,\n} from './construct-interface';\nimport type { ScheduleExpression } from './crons/Cron';\nimport { CronBuilder } from './crons/CronBuilder';\nimport { envParserFor } from './endpoints/surfaceEnv';\nimport { FunctionBuilder } from './functions/FunctionBuilder';\nimport { QueueBuilder } from './queue/QueueBuilder';\nimport { SubscriberBuilder } from './subscribers/SubscriberBuilder';\nimport type { Telemetry } from './telemetry';\nimport type { Topic, TopicEvents } from './topic/Topic';\n\nexport interface WorkerConfig {\n\t/**\n\t * The logger everything built from this worker runs with.\n\t *\n\t * The actual logger, not a path to one — the same reason `RestApi` takes\n\t * one. Every cron and subscriber in a scaffolded project used to open with\n\t * `import { logger } from '…/constructs/logger.js'`, which is the line this\n\t * removes.\n\t */\n\tlogger?: Logger;\n\t/**\n\t * The environment parser everything built from this worker runs with.\n\t *\n\t * Defaults to `process.env` merged with the credentials `gkm dev` injected,\n\t * which is what an application's own `config/env.ts` always was.\n\t */\n\tenvParser?: EnvironmentParser<{}>;\n\t/**\n\t * What the worker's process emits — the `Telemetry` construct, passed like\n\t * the logger. An edge to the node: the deploy hands the process the\n\t * `OTEL_*` keys its stage resolves.\n\t */\n\ttelemetry?: Telemetry;\n}\n\nexport class Worker<\n\tTName extends string = string,\n\tTDatabase = undefined,\n\tTDatabaseServiceName extends string = string,\n> implements Declarable<TName>\n{\n\treadonly id: TName;\n\n\t/** Always defined — the worker's own, or the console logger. */\n\treadonly logger: Logger;\n\n\t/** Always defined, and normally the default. */\n\treadonly envParser: EnvironmentParser<{}>;\n\n\t/**\n\t * Where this worker's schedules live, when it has crons and a server runs\n\t * them — and the database everything built from it receives as `db`.\n\t * `undefined` until `.database(…)` says.\n\t */\n\treadonly scheduleStore?: Consumable<string, unknown>;\n\n\tconstructor(\n\t\tid: ConstructName<TName>,\n\t\tprivate readonly config: WorkerConfig = {},\n\t\t/** Internal: how `.calls()` carries edges into the copy it returns. */\n\t\tprivate readonly dependencies: readonly Dependency[] = [],\n\t\tscheduleStore?: Consumable<string, unknown>,\n\t) {\n\t\tconst canonical = canonicalId(id as string);\n\n\t\tthis.id = canonical as TName;\n\t\tthis.logger = config.logger ?? DEFAULT_LOGGER;\n\t\tthis.envParser = envParserFor(\n\t\t\tconfig.envParser\n\t\t\t\t? { id: canonical, envParser: config.envParser }\n\t\t\t\t: undefined,\n\t\t);\n\t\tthis.scheduleStore = scheduleStore;\n\t}\n\n\t/**\n\t * The database this worker keeps its schedules in, and the one everything\n\t * built from it — crons, queues, subscribers, functions — receives as `db`.\n\t * One of them that works against another database names it with its own\n\t * `.database(other)`, which replaces this one for that runnable alone.\n\t *\n\t * Only a server target uses it. On AWS a cron is an EventBridge rule\n\t * invoking a Lambda and nothing here is consulted — but a server has nothing\n\t * firing its crons except itself, and a process that schedules in memory\n\t * fires every job once per replica while reporting that all is well.\n\t *\n\t * So the store is declared rather than discovered. It could have been\n\t * inferred — from a database the app happens to declare, or from the events\n\t * backend when that backend is pg-boss — and both would work until an app\n\t * had two databases, or moved its events to SNS, at which point the\n\t * schedules would quietly move or stop. Naming it is one line and cannot\n\t * drift.\n\t *\n\t * @example\n\t * ```ts\n\t * export const jobs = new Worker('Jobs', { logger }).database(database);\n\t *\n\t * export const sweep = jobs\n\t * .cron('rate(1 hour)')\n\t * .handle(async ({ db }) => db.deleteFrom('sessions').execute());\n\t * ```\n\t */\n\tdatabase<T, TDbName extends string>(\n\t\tsource: Consumable<TDbName, T>,\n\t): Worker<TName, T, TDbName> {\n\t\treturn new Worker<TName, T, TDbName>(\n\t\t\tthis.id as ConstructName<TName>,\n\t\t\tthis.config,\n\t\t\t[...this.dependencies, edgeTo(source)],\n\t\t\tsource as unknown as Consumable<string, unknown>,\n\t\t);\n\t}\n\n\t/**\n\t * A scheduled runnable.\n\t *\n\t * @example `worker.cron('rate(1 day)').handle(async ({ logger }) => { … })`\n\t */\n\tcron(schedule: ScheduleExpression) {\n\t\treturn this.own(\n\t\t\tnew CronBuilder<\n\t\t\t\tComposableStandardSchema,\n\t\t\t\tService[],\n\t\t\t\tLogger,\n\t\t\t\tundefined,\n\t\t\t\tTDatabase,\n\t\t\t\tTDatabaseServiceName\n\t\t\t>().logger(this.logger),\n\t\t).schedule(schedule);\n\t}\n\n\t/**\n\t * A queue and its one consumer, run by this worker.\n\t *\n\t * One construct, because a queue has exactly one consumer: nothing else can\n\t * attach a second, or forget to attach the first. Producers send to it by\n\t * depending on it — `.dependsOn([emails])` makes `services.emails` the\n\t * publisher.\n\t *\n\t * @example\n\t * ```ts\n\t * export const emails = worker\n\t * .queue('Emails')\n\t * .message(z.object({ userId: z.uuid(), template: z.string() }))\n\t * .handle(async ({ messages, services }) => { … });\n\t * ```\n\t */\n\tqueue<TQueueName extends string>(name: TQueueName) {\n\t\treturn this.own(\n\t\t\tnew QueueBuilder<\n\t\t\t\tstring,\n\t\t\t\tundefined,\n\t\t\t\t[],\n\t\t\t\tLogger,\n\t\t\t\tTDatabase,\n\t\t\t\tTDatabaseServiceName\n\t\t\t>().logger(this.logger),\n\t\t).queue(name);\n\t}\n\n\t/**\n\t * A runnable that consumes a topic.\n\t *\n\t * Binding is not publishing: a consumer is handed the topic's event types\n\t * and no connection string it could publish with. A handler that emits\n\t * follow-up events depends on the topic it emits to —\n\t * `.dependsOn([orders])` — which is the only reason it would need one.\n\t *\n\t * @example `worker.topic(users).subscribe(['user.created']).handle(…)`\n\t */\n\ttopic<TTopicName extends string, TEvents extends TopicEvents>(\n\t\ttopic: Topic<TTopicName, TEvents>,\n\t) {\n\t\treturn this.own(\n\t\t\tnew SubscriberBuilder<\n\t\t\t\t[],\n\t\t\t\tLogger,\n\t\t\t\tundefined,\n\t\t\t\tundefined,\n\t\t\t\t[],\n\t\t\t\tTDatabase,\n\t\t\t\tTDatabaseServiceName\n\t\t\t>().logger(this.logger),\n\t\t).topic(topic);\n\t}\n\n\t/**\n\t * Everything below builds a function — the runnable with no schedule and no\n\t * topic, invoked by something else.\n\t *\n\t * These sit on the worker rather than behind a `functions` namespace because\n\t * the namespace was a hop that said nothing: `worker.functions.input(…)`\n\t * names a collection to reach one member of it. What kind of runnable is\n\t * being built is decided by which of these is called first — a schedule\n\t * makes a cron, a topic makes a subscriber, and anything else makes a\n\t * function.\n\t */\n\tinput<T extends ComposableStandardSchema>(schema: T) {\n\t\treturn this.functions.input(schema);\n\t}\n\n\toutput<T extends StandardSchemaV1>(schema: T) {\n\t\treturn this.functions.output(schema);\n\t}\n\n\ttimeout(ms: number) {\n\t\treturn this.functions.timeout(ms);\n\t}\n\n\tmemorySize(mb: number) {\n\t\treturn this.functions.memorySize(mb);\n\t}\n\n\tdependsOn<const T extends readonly Consumable[]>(constructs: T) {\n\t\treturn this.functions.dependsOn(constructs);\n\t}\n\n\tservices<T extends Service[]>(services: T) {\n\t\treturn this.functions.services(services);\n\t}\n\n\t/**\n\t * A function with no input schema — the shortest runnable there is.\n\t *\n\t * @example `worker.handle(async ({ logger }) => { … })`\n\t */\n\thandle(\n\t\t...args: Parameters<\n\t\t\tReturnType<\n\t\t\t\tWorker<TName, TDatabase, TDatabaseServiceName>['schemalessFunction']\n\t\t\t>['handle']\n\t\t>\n\t) {\n\t\treturn this.schemalessFunction().handle(...args);\n\t}\n\n\t/** The builder `handle` delegates to, named so its type can be referred to. */\n\tprivate schemalessFunction() {\n\t\treturn this.functions;\n\t}\n\n\t/** The function builder the delegating methods above are built on. */\n\tprivate get functions() {\n\t\treturn this.own(\n\t\t\tnew FunctionBuilder<\n\t\t\t\tany,\n\t\t\t\tundefined,\n\t\t\t\t[],\n\t\t\t\tLogger,\n\t\t\t\tundefined,\n\t\t\t\tstring,\n\t\t\t\tTDatabase,\n\t\t\t\tTDatabaseServiceName\n\t\t\t>().logger(this.logger),\n\t\t);\n\t}\n\n\t/**\n\t * Resources and surfaces this worker calls.\n\t *\n\t * Not `.dependsOn()`: this records the edge that derives the worker's\n\t * environment and its access, the same distinction `RestApi.calls()` draws.\n\t */\n\tcalls(\n\t\tconstructs: readonly Declarable[],\n\t): Worker<TName, TDatabase, TDatabaseServiceName> {\n\t\treturn new Worker<TName, TDatabase, TDatabaseServiceName>(\n\t\t\tthis.id as ConstructName<TName>,\n\t\t\tthis.config,\n\t\t\t[...this.dependencies, ...constructs.map(edgeTo)],\n\t\t\tthis.scheduleStore,\n\t\t);\n\t}\n\n\t/**\n\t * Stamps this worker's id onto a builder, so everything built from it says\n\t * which process runs it — and hands it the worker's database.\n\t *\n\t * The database goes through the builder's own `.database()` rather than\n\t * being written into its fields, so the default is recorded exactly as an\n\t * explicit one would be: the client the handler gets, and the edge the\n\t * manifest reads. That is also what lets a later `.database(other)` replace\n\t * it outright. The builder arrives already typed with this worker's\n\t * database; this is the runtime half of that.\n\t */\n\tprivate own<\n\t\tT extends {\n\t\t\t_owner?: string;\n\t\t\t_scheduleStore?: unknown;\n\t\t\tdatabase(source: Consumable<string, unknown>): unknown;\n\t\t},\n\t>(builder: T): T {\n\t\tconst owned = this.scheduleStore\n\t\t\t? (builder.database(this.scheduleStore) as T)\n\t\t\t: builder;\n\n\t\towned._owner = this.id;\n\t\towned._scheduleStore = this.scheduleStore;\n\n\t\treturn owned;\n\t}\n\n\tdeclare(): Declaration[] {\n\t\treturn [\n\t\t\t{\n\t\t\t\tkind: 'worker',\n\t\t\t\tid: this.id,\n\t\t\t\t...(this.dependencies.length\n\t\t\t\t\t? { dependencies: this.dependencies }\n\t\t\t\t\t: {}),\n\t\t\t\t...(this.config.telemetry\n\t\t\t\t\t? { telemetry: this.config.telemetry.id }\n\t\t\t\t\t: {}),\n\t\t\t\t// Nothing calls a worker, so it publishes no address.\n\t\t\t\tprovides: [],\n\t\t\t},\n\t\t];\n\t}\n}\n"],"mappings":";;;;;;;;;;AA6HA,IAAa,SAAb,MAAa,OAKb;CAkBmB;CAEA;CAnBlB;;CAGA;;CAGA;;;;;;CAOA;CAEA,YACC,IACA,SAAwC,CAAC,GAEzC,eAAuD,CAAC,GACxD,eACC;EAJgB,KAAA,SAAA;EAEA,KAAA,eAAA;EAGjB,MAAM,aAAA,GAAYA,oBAAAA,YAAAA,CAAY,EAAY;EAE1C,KAAK,KAAK;EACV,KAAK,SAAS,OAAO,UAAUC,0BAAAA;EAC/B,KAAK,YAAYC,mBAAAA,aAChB,OAAO,YACJ;GAAE,IAAI;GAAW,WAAW,OAAO;EAAU,IAC7C,KAAA,CACJ;EACA,KAAK,gBAAgB;CACtB;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6BA,SACC,QAC4B;EAC5B,OAAO,IAAI,OACV,KAAK,IACL,KAAK,QACL,CAAC,GAAG,KAAK,cAAcC,4BAAAA,OAAO,MAAM,CAAC,GACrC,MACD;CACD;;;;;;CAOA,KAAK,UAA8B;EAClC,OAAO,KAAK,IACX,IAAIC,oBAAAA,YAOF,CAAC,CAAC,OAAO,KAAK,MAAM,CACvB,CAAC,CAAC,SAAS,QAAQ;CACpB;;;;;;;;;;;;;;;;;CAkBA,MAAiC,MAAkB;EAClD,OAAO,KAAK,IACX,IAAIC,qBAAAA,aAOF,CAAC,CAAC,OAAO,KAAK,MAAM,CACvB,CAAC,CAAC,MAAM,IAAI;CACb;;;;;;;;;;;CAYA,MACC,OACC;EACD,OAAO,KAAK,IACX,IAAIC,0BAAAA,kBAQF,CAAC,CAAC,OAAO,KAAK,MAAM,CACvB,CAAC,CAAC,MAAM,KAAK;CACd;;;;;;;;;;;;CAaA,MAA0C,QAAW;EACpD,OAAO,KAAK,UAAU,MAAM,MAAM;CACnC;CAEA,OAAmC,QAAW;EAC7C,OAAO,KAAK,UAAU,OAAO,MAAM;CACpC;CAEA,QAAQ,IAAY;EACnB,OAAO,KAAK,UAAU,QAAQ,EAAE;CACjC;CAEA,WAAW,IAAY;EACtB,OAAO,KAAK,UAAU,WAAW,EAAE;CACpC;CAEA,UAAiD,YAAe;EAC/D,OAAO,KAAK,UAAU,UAAU,UAAU;CAC3C;CAEA,SAA8B,UAAa;EAC1C,OAAO,KAAK,UAAU,SAAS,QAAQ;CACxC;;;;;;CAOA,OACC,GAAG,MAKF;EACD,OAAO,KAAK,mBAAmB,CAAC,CAAC,OAAO,GAAG,IAAI;CAChD;;CAGA,qBAA6B;EAC5B,OAAO,KAAK;CACb;;CAGA,IAAY,YAAY;EACvB,OAAO,KAAK,IACX,IAAIC,kBAAAA,gBASF,CAAC,CAAC,OAAO,KAAK,MAAM,CACvB;CACD;;;;;;;CAQA,MACC,YACiD;EACjD,OAAO,IAAI,OACV,KAAK,IACL,KAAK,QACL,CAAC,GAAG,KAAK,cAAc,GAAG,WAAW,IAAIJ,4BAAAA,MAAM,CAAC,GAChD,KAAK,aACN;CACD;;;;;;;;;;;;CAaA,IAME,SAAe;EAChB,MAAM,QAAQ,KAAK,gBACf,QAAQ,SAAS,KAAK,aAAa,IACpC;EAEH,MAAM,SAAS,KAAK;EACpB,MAAM,iBAAiB,KAAK;EAE5B,OAAO;CACR;CAEA,UAAyB;EACxB,OAAO,CACN;GACC,MAAM;GACN,IAAI,KAAK;GACT,GAAI,KAAK,aAAa,SACnB,EAAE,cAAc,KAAK,aAAa,IAClC,CAAC;GACJ,GAAI,KAAK,OAAO,YACb,EAAE,WAAW,KAAK,OAAO,UAAU,GAAG,IACtC,CAAC;GAEJ,UAAU,CAAC;EACZ,CACD;CACD;AACD"}
1
+ {"version":3,"file":"worker.cjs","names":["canonicalId","DEFAULT_LOGGER","envParserFor","edgeTo","CronBuilder","QueueBuilder","SubscriberBuilder","ObjectSubscriberBuilder","FunctionBuilder"],"sources":["../src/worker.ts"],"sourcesContent":["/**\n * A process with no port.\n *\n * `RestApi` is the process that answers HTTP. This is the one that does not —\n * the thing that runs a schedule, drains a queue, consumes a topic. Until it\n * existed, those had nowhere to say they belonged: a cron declared a schedule\n * and nothing else, and which container ran it was decided by the directory the\n * file sat in. A glob owned it.\n *\n * The cost of that shows up in the scaffolds. `gkm init --template worker` —\n * a project that is background jobs and nothing else — declared\n * `new RestApi('Api', { defaultAuthorizer: 'none', logger })`: an HTTP surface\n * with no HTTP on it, written because it was the only construct that made an\n * app exist and the only way to get a logger into the generated runtime.\n *\n * It takes no authorizer, and that is the tell that these are two constructs\n * rather than one with a flag: a `RestApi` needs one because an HTTP surface\n * can ship open by omission, and nothing reaches a worker from outside.\n *\n * ## Crons on a server target need a database\n *\n * A cron becomes an EventBridge rule on AWS and nothing here is consulted. A\n * server has nothing firing its crons but itself, and a process that schedules\n * in memory fires every job once per replica while reporting success — so the\n * schedule is kept in Postgres, and the worker says which one with\n * `.database(db)`.\n *\n * A worker with crons and no `.database(…)` schedules nothing on a server\n * target and says so at startup. That is a limitation of workers today rather\n * than a design: the store could be a cache, or something the target\n * provisions, and Postgres is what exists.\n *\n * ## The database is everything's default\n *\n * The same `.database(db)` is the database every cron, queue, subscriber and\n * function built from the worker receives as `db`. The work a worker groups is\n * usually work against one database, and naming it per handler was a line\n * repeated in every file that could drift in any one of them. A runnable that\n * works against another names its own with `.database(other)`, which replaces\n * the worker's — client and manifest edge both — rather than adding to it.\n *\n * It is not an app: it declares no path, and its code lives in an app's\n * directory. But it is a process of its own. On a server target each worker\n * with work to do is built — from the app that holds that work — into an image\n * that runs its crons, consumers and subscribers and answers only a health\n * check, so a slow queue never shares a process with the requests an API\n * serves. `gkm dev` still runs everything in one process. Declare as many as\n * the shape of the work wants: each is one container.\n *\n * @example\n * ```ts\n * // constructs/worker.ts\n * import { Worker } from '@geekmidas/constructs/worker';\n * import { logger } from './logger.js';\n *\n * export const worker = new Worker('Worker', { logger }).database(database);\n *\n * // crons/cleanup.ts\n * import { worker } from '@acme/constructs/worker.js';\n *\n * export const cleanup = worker\n * .cron('rate(1 day)')\n * .handle(async ({ db, logger }) => { … });\n *\n * // queues/reports.ts — against another database than the worker's\n * export const reports = worker\n * .queue('Reports')\n * .database(analytics)\n * .message(z.object({ id: z.uuid() }))\n * .handle(async ({ messages, db }) => { … });\n * ```\n */\n\nimport type { EnvironmentParser } from '@geekmidas/envkit';\nimport type { EventPublisher } from '@geekmidas/events';\nimport type { Logger } from '@geekmidas/logger';\nimport { DEFAULT_LOGGER } from '@geekmidas/logger/console';\nimport {\n\ttype ConstructName,\n\tcanonicalId,\n\ttype Declaration,\n\ttype Dependency,\n} from '@geekmidas/manifest';\nimport type { ComposableStandardSchema } from '@geekmidas/schema';\nimport type { Service } from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport {\n\ttype Consumable,\n\ttype Declarable,\n\tedgeTo,\n} from './construct-interface';\nimport type { ScheduleExpression } from './crons/Cron';\nimport { CronBuilder } from './crons/CronBuilder';\nimport { envParserFor } from './endpoints/surfaceEnv';\nimport { FunctionBuilder } from './functions/FunctionBuilder';\nimport type { ObjectStorage } from './object-storage';\nimport { QueueBuilder } from './queue/QueueBuilder';\nimport { ObjectSubscriberBuilder } from './subscribers/ObjectSubscriberBuilder';\nimport { SubscriberBuilder } from './subscribers/SubscriberBuilder';\nimport type { Telemetry } from './telemetry';\nimport type { Topic, TopicEvents } from './topic/Topic';\n\nexport interface WorkerConfig {\n\t/**\n\t * The logger everything built from this worker runs with.\n\t *\n\t * The actual logger, not a path to one — the same reason `RestApi` takes\n\t * one. Every cron and subscriber in a scaffolded project used to open with\n\t * `import { logger } from '…/constructs/logger.js'`, which is the line this\n\t * removes.\n\t */\n\tlogger?: Logger;\n\t/**\n\t * The environment parser everything built from this worker runs with.\n\t *\n\t * Defaults to `process.env` merged with the credentials `gkm dev` injected,\n\t * which is what an application's own `config/env.ts` always was.\n\t */\n\tenvParser?: EnvironmentParser<{}>;\n\t/**\n\t * What the worker's process emits — the `Telemetry` construct, passed like\n\t * the logger. An edge to the node: the deploy hands the process the\n\t * `OTEL_*` keys its stage resolves.\n\t */\n\ttelemetry?: Telemetry;\n}\n\nexport class Worker<\n\tTName extends string = string,\n\tTDatabase = undefined,\n\tTDatabaseServiceName extends string = string,\n> implements Declarable<TName>\n{\n\treadonly id: TName;\n\n\t/** Always defined — the worker's own, or the console logger. */\n\treadonly logger: Logger;\n\n\t/** Always defined, and normally the default. */\n\treadonly envParser: EnvironmentParser<{}>;\n\n\t/**\n\t * Where this worker's schedules live, when it has crons and a server runs\n\t * them — and the database everything built from it receives as `db`.\n\t * `undefined` until `.database(…)` says.\n\t */\n\treadonly scheduleStore?: Consumable<string, unknown>;\n\n\tconstructor(\n\t\tid: ConstructName<TName>,\n\t\tprivate readonly config: WorkerConfig = {},\n\t\t/** Internal: how `.calls()` carries edges into the copy it returns. */\n\t\tprivate readonly dependencies: readonly Dependency[] = [],\n\t\tscheduleStore?: Consumable<string, unknown>,\n\t) {\n\t\tconst canonical = canonicalId(id as string);\n\n\t\tthis.id = canonical as TName;\n\t\tthis.logger = config.logger ?? DEFAULT_LOGGER;\n\t\tthis.envParser = envParserFor(\n\t\t\tconfig.envParser\n\t\t\t\t? { id: canonical, envParser: config.envParser }\n\t\t\t\t: undefined,\n\t\t);\n\t\tthis.scheduleStore = scheduleStore;\n\t}\n\n\t/**\n\t * The database this worker keeps its schedules in, and the one everything\n\t * built from it — crons, queues, subscribers, functions — receives as `db`.\n\t * One of them that works against another database names it with its own\n\t * `.database(other)`, which replaces this one for that runnable alone.\n\t *\n\t * Only a server target uses it. On AWS a cron is an EventBridge rule\n\t * invoking a Lambda and nothing here is consulted — but a server has nothing\n\t * firing its crons except itself, and a process that schedules in memory\n\t * fires every job once per replica while reporting that all is well.\n\t *\n\t * So the store is declared rather than discovered. It could have been\n\t * inferred — from a database the app happens to declare, or from the events\n\t * backend when that backend is pg-boss — and both would work until an app\n\t * had two databases, or moved its events to SNS, at which point the\n\t * schedules would quietly move or stop. Naming it is one line and cannot\n\t * drift.\n\t *\n\t * @example\n\t * ```ts\n\t * export const jobs = new Worker('Jobs', { logger }).database(database);\n\t *\n\t * export const sweep = jobs\n\t * .cron('rate(1 hour)')\n\t * .handle(async ({ db }) => db.deleteFrom('sessions').execute());\n\t * ```\n\t */\n\tdatabase<T, TDbName extends string>(\n\t\tsource: Consumable<TDbName, T>,\n\t): Worker<TName, T, TDbName> {\n\t\treturn new Worker<TName, T, TDbName>(\n\t\t\tthis.id as ConstructName<TName>,\n\t\t\tthis.config,\n\t\t\t[...this.dependencies, edgeTo(source)],\n\t\t\tsource as unknown as Consumable<string, unknown>,\n\t\t);\n\t}\n\n\t/**\n\t * A scheduled runnable.\n\t *\n\t * @example `worker.cron('rate(1 day)').handle(async ({ logger }) => { … })`\n\t */\n\tcron(schedule: ScheduleExpression) {\n\t\treturn this.own(\n\t\t\tnew CronBuilder<\n\t\t\t\tComposableStandardSchema,\n\t\t\t\tService[],\n\t\t\t\tLogger,\n\t\t\t\tundefined,\n\t\t\t\tTDatabase,\n\t\t\t\tTDatabaseServiceName\n\t\t\t>().logger(this.logger),\n\t\t).schedule(schedule);\n\t}\n\n\t/**\n\t * A queue and its one consumer, run by this worker.\n\t *\n\t * One construct, because a queue has exactly one consumer: nothing else can\n\t * attach a second, or forget to attach the first. Producers send to it by\n\t * depending on it — `.dependsOn([emails])` makes `services.emails` the\n\t * publisher.\n\t *\n\t * @example\n\t * ```ts\n\t * export const emails = worker\n\t * .queue('Emails')\n\t * .message(z.object({ userId: z.uuid(), template: z.string() }))\n\t * .handle(async ({ messages, services }) => { … });\n\t * ```\n\t */\n\tqueue<TQueueName extends string>(name: TQueueName) {\n\t\treturn this.own(\n\t\t\tnew QueueBuilder<\n\t\t\t\tstring,\n\t\t\t\tundefined,\n\t\t\t\t[],\n\t\t\t\tLogger,\n\t\t\t\tTDatabase,\n\t\t\t\tTDatabaseServiceName\n\t\t\t>().logger(this.logger),\n\t\t).queue(name);\n\t}\n\n\t/**\n\t * A runnable that reacts to a bucket's objects changing.\n\t *\n\t * A bucket is a topic whose publisher is the storage: the binding is the\n\t * same as {@link Worker.topic}'s, and so is everything after it — the\n\t * services, the database, how it is delivered. The handler is handed the\n\t * objects; reading one still means depending on the bucket,\n\t * `.dependsOn([uploads])`.\n\t *\n\t * @example\n\t * ```ts\n\t * export const onAvatar = worker\n\t * .objects(uploads)\n\t * .on(['object.created'], { prefix: 'avatars/', suffix: '.png' })\n\t * .handle(async ({ objects }) => { … });\n\t * ```\n\t */\n\tobjects<TBucketName extends string>(bucket: ObjectStorage<TBucketName>) {\n\t\tconst inner = this.own(\n\t\t\tnew SubscriberBuilder<\n\t\t\t\t[],\n\t\t\t\tLogger,\n\t\t\t\tundefined,\n\t\t\t\tundefined,\n\t\t\t\t[],\n\t\t\t\tTDatabase,\n\t\t\t\tTDatabaseServiceName\n\t\t\t>().logger(this.logger),\n\t\t);\n\t\treturn new ObjectSubscriberBuilder<\n\t\t\t[],\n\t\t\tLogger,\n\t\t\tundefined,\n\t\t\t[],\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>(inner, bucket.id);\n\t}\n\n\t/**\n\t * A runnable that consumes a topic.\n\t *\n\t * Binding is not publishing: a consumer is handed the topic's event types\n\t * and no connection string it could publish with. A handler that emits\n\t * follow-up events depends on the topic it emits to —\n\t * `.dependsOn([orders])` — which is the only reason it would need one.\n\t *\n\t * @example `worker.topic(users).subscribe(['user.created']).handle(…)`\n\t */\n\ttopic<TTopicName extends string, TEvents extends TopicEvents>(\n\t\ttopic: Topic<TTopicName, TEvents>,\n\t) {\n\t\treturn this.own(\n\t\t\tnew SubscriberBuilder<\n\t\t\t\t[],\n\t\t\t\tLogger,\n\t\t\t\tundefined,\n\t\t\t\tundefined,\n\t\t\t\t[],\n\t\t\t\tTDatabase,\n\t\t\t\tTDatabaseServiceName\n\t\t\t>().logger(this.logger),\n\t\t).topic(topic);\n\t}\n\n\t/**\n\t * Everything below builds a function — the runnable with no schedule and no\n\t * topic, invoked by something else.\n\t *\n\t * These sit on the worker rather than behind a `functions` namespace because\n\t * the namespace was a hop that said nothing: `worker.functions.input(…)`\n\t * names a collection to reach one member of it. What kind of runnable is\n\t * being built is decided by which of these is called first — a schedule\n\t * makes a cron, a topic makes a subscriber, and anything else makes a\n\t * function.\n\t */\n\tinput<T extends ComposableStandardSchema>(schema: T) {\n\t\treturn this.functions.input(schema);\n\t}\n\n\toutput<T extends StandardSchemaV1>(schema: T) {\n\t\treturn this.functions.output(schema);\n\t}\n\n\ttimeout(ms: number) {\n\t\treturn this.functions.timeout(ms);\n\t}\n\n\tmemorySize(mb: number) {\n\t\treturn this.functions.memorySize(mb);\n\t}\n\n\tdependsOn<const T extends readonly Consumable[]>(constructs: T) {\n\t\treturn this.functions.dependsOn(constructs);\n\t}\n\n\tservices<T extends Service[]>(services: T) {\n\t\treturn this.functions.services(services);\n\t}\n\n\t/**\n\t * A function with no input schema — the shortest runnable there is.\n\t *\n\t * @example `worker.handle(async ({ logger }) => { … })`\n\t */\n\thandle(\n\t\t...args: Parameters<\n\t\t\tReturnType<\n\t\t\t\tWorker<TName, TDatabase, TDatabaseServiceName>['schemalessFunction']\n\t\t\t>['handle']\n\t\t>\n\t) {\n\t\treturn this.schemalessFunction().handle(...args);\n\t}\n\n\t/** The builder `handle` delegates to, named so its type can be referred to. */\n\tprivate schemalessFunction() {\n\t\treturn this.functions;\n\t}\n\n\t/** The function builder the delegating methods above are built on. */\n\tprivate get functions() {\n\t\treturn this.own(\n\t\t\tnew FunctionBuilder<\n\t\t\t\tany,\n\t\t\t\tundefined,\n\t\t\t\t[],\n\t\t\t\tLogger,\n\t\t\t\tundefined,\n\t\t\t\tstring,\n\t\t\t\tTDatabase,\n\t\t\t\tTDatabaseServiceName\n\t\t\t>().logger(this.logger),\n\t\t);\n\t}\n\n\t/**\n\t * Resources and surfaces this worker calls.\n\t *\n\t * Not `.dependsOn()`: this records the edge that derives the worker's\n\t * environment and its access, the same distinction `RestApi.calls()` draws.\n\t */\n\tcalls(\n\t\tconstructs: readonly Declarable[],\n\t): Worker<TName, TDatabase, TDatabaseServiceName> {\n\t\treturn new Worker<TName, TDatabase, TDatabaseServiceName>(\n\t\t\tthis.id as ConstructName<TName>,\n\t\t\tthis.config,\n\t\t\t[...this.dependencies, ...constructs.map(edgeTo)],\n\t\t\tthis.scheduleStore,\n\t\t);\n\t}\n\n\t/**\n\t * Stamps this worker's id onto a builder, so everything built from it says\n\t * which process runs it — and hands it the worker's database.\n\t *\n\t * The database goes through the builder's own `.database()` rather than\n\t * being written into its fields, so the default is recorded exactly as an\n\t * explicit one would be: the client the handler gets, and the edge the\n\t * manifest reads. That is also what lets a later `.database(other)` replace\n\t * it outright. The builder arrives already typed with this worker's\n\t * database; this is the runtime half of that.\n\t */\n\tprivate own<\n\t\tT extends {\n\t\t\t_owner?: string;\n\t\t\t_scheduleStore?: unknown;\n\t\t\tdatabase(source: Consumable<string, unknown>): unknown;\n\t\t},\n\t>(builder: T): T {\n\t\tconst owned = this.scheduleStore\n\t\t\t? (builder.database(this.scheduleStore) as T)\n\t\t\t: builder;\n\n\t\towned._owner = this.id;\n\t\towned._scheduleStore = this.scheduleStore;\n\n\t\treturn owned;\n\t}\n\n\tdeclare(): Declaration[] {\n\t\treturn [\n\t\t\t{\n\t\t\t\tkind: 'worker',\n\t\t\t\tid: this.id,\n\t\t\t\t...(this.dependencies.length\n\t\t\t\t\t? { dependencies: this.dependencies }\n\t\t\t\t\t: {}),\n\t\t\t\t...(this.config.telemetry\n\t\t\t\t\t? { telemetry: this.config.telemetry.id }\n\t\t\t\t\t: {}),\n\t\t\t\t// Nothing calls a worker, so it publishes no address.\n\t\t\t\tprovides: [],\n\t\t\t},\n\t\t];\n\t}\n}\n"],"mappings":";;;;;;;;;;AA+HA,IAAa,SAAb,MAAa,OAKb;CAkBmB;CAEA;CAnBlB;;CAGA;;CAGA;;;;;;CAOA;CAEA,YACC,IACA,SAAwC,CAAC,GAEzC,eAAuD,CAAC,GACxD,eACC;EAJgB,KAAA,SAAA;EAEA,KAAA,eAAA;EAGjB,MAAM,aAAA,GAAYA,oBAAAA,YAAAA,CAAY,EAAY;EAE1C,KAAK,KAAK;EACV,KAAK,SAAS,OAAO,UAAUC,0BAAAA;EAC/B,KAAK,YAAYC,mBAAAA,aAChB,OAAO,YACJ;GAAE,IAAI;GAAW,WAAW,OAAO;EAAU,IAC7C,KAAA,CACJ;EACA,KAAK,gBAAgB;CACtB;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6BA,SACC,QAC4B;EAC5B,OAAO,IAAI,OACV,KAAK,IACL,KAAK,QACL,CAAC,GAAG,KAAK,cAAcC,4BAAAA,OAAO,MAAM,CAAC,GACrC,MACD;CACD;;;;;;CAOA,KAAK,UAA8B;EAClC,OAAO,KAAK,IACX,IAAIC,oBAAAA,YAOF,CAAC,CAAC,OAAO,KAAK,MAAM,CACvB,CAAC,CAAC,SAAS,QAAQ;CACpB;;;;;;;;;;;;;;;;;CAkBA,MAAiC,MAAkB;EAClD,OAAO,KAAK,IACX,IAAIC,qBAAAA,aAOF,CAAC,CAAC,OAAO,KAAK,MAAM,CACvB,CAAC,CAAC,MAAM,IAAI;CACb;;;;;;;;;;;;;;;;;;CAmBA,QAAoC,QAAoC;EACvE,MAAM,QAAQ,KAAK,IAClB,IAAIC,0BAAAA,kBAQF,CAAC,CAAC,OAAO,KAAK,MAAM,CACvB;EACA,OAAO,IAAIC,0BAAAA,wBAOT,OAAO,OAAO,EAAE;CACnB;;;;;;;;;;;CAYA,MACC,OACC;EACD,OAAO,KAAK,IACX,IAAID,0BAAAA,kBAQF,CAAC,CAAC,OAAO,KAAK,MAAM,CACvB,CAAC,CAAC,MAAM,KAAK;CACd;;;;;;;;;;;;CAaA,MAA0C,QAAW;EACpD,OAAO,KAAK,UAAU,MAAM,MAAM;CACnC;CAEA,OAAmC,QAAW;EAC7C,OAAO,KAAK,UAAU,OAAO,MAAM;CACpC;CAEA,QAAQ,IAAY;EACnB,OAAO,KAAK,UAAU,QAAQ,EAAE;CACjC;CAEA,WAAW,IAAY;EACtB,OAAO,KAAK,UAAU,WAAW,EAAE;CACpC;CAEA,UAAiD,YAAe;EAC/D,OAAO,KAAK,UAAU,UAAU,UAAU;CAC3C;CAEA,SAA8B,UAAa;EAC1C,OAAO,KAAK,UAAU,SAAS,QAAQ;CACxC;;;;;;CAOA,OACC,GAAG,MAKF;EACD,OAAO,KAAK,mBAAmB,CAAC,CAAC,OAAO,GAAG,IAAI;CAChD;;CAGA,qBAA6B;EAC5B,OAAO,KAAK;CACb;;CAGA,IAAY,YAAY;EACvB,OAAO,KAAK,IACX,IAAIE,kBAAAA,gBASF,CAAC,CAAC,OAAO,KAAK,MAAM,CACvB;CACD;;;;;;;CAQA,MACC,YACiD;EACjD,OAAO,IAAI,OACV,KAAK,IACL,KAAK,QACL,CAAC,GAAG,KAAK,cAAc,GAAG,WAAW,IAAIL,4BAAAA,MAAM,CAAC,GAChD,KAAK,aACN;CACD;;;;;;;;;;;;CAaA,IAME,SAAe;EAChB,MAAM,QAAQ,KAAK,gBACf,QAAQ,SAAS,KAAK,aAAa,IACpC;EAEH,MAAM,SAAS,KAAK;EACpB,MAAM,iBAAiB,KAAK;EAE5B,OAAO;CACR;CAEA,UAAyB;EACxB,OAAO,CACN;GACC,MAAM;GACN,IAAI,KAAK;GACT,GAAI,KAAK,aAAa,SACnB,EAAE,cAAc,KAAK,aAAa,IAClC,CAAC;GACJ,GAAI,KAAK,OAAO,YACb,EAAE,WAAW,KAAK,OAAO,UAAU,GAAG,IACtC,CAAC;GAEJ,UAAU,CAAC;EACZ,CACD;CACD;AACD"}
package/dist/worker.d.cts CHANGED
@@ -1,13 +1,14 @@
1
1
  import { n as StandardSchemaV1 } from "./index-DdRjYhiJ.cjs";
2
- import { c as Topic, l as TopicEvents, u as TopicMessage } from "./Construct-CFW0zjdD.cjs";
3
- import { i as FunctionHandler, n as Function } from "./Function-B0uZhZnU.cjs";
2
+ import { c as Topic, l as TopicEvents, u as TopicMessage } from "./Construct-127M7yrl.cjs";
3
+ import { i as FunctionHandler, n as Function } from "./Function-BIEsY0Hm.cjs";
4
4
  import { c as ServicesOf, i as Declarable, r as Consumable } from "./construct-interface-B1AfqRl4.cjs";
5
- import { t as FunctionBuilder } from "./index-Cd2w-DRL.cjs";
6
- import { i as ScheduleExpression } from "./Cron-bdGRSROY.cjs";
5
+ import { t as FunctionBuilder } from "./index-BtzMzhMA.cjs";
6
+ import { i as ScheduleExpression } from "./Cron-CFkpEys6.cjs";
7
7
  import { n as Telemetry } from "./telemetry-B48RLjyk.cjs";
8
- import { t as CronBuilder } from "./CronBuilder-B_24LZpn.cjs";
9
- import { t as QueueBuilder } from "./QueueBuilder-MfpkJ3R9.cjs";
10
- import { t as SubscriberBuilder } from "./SubscriberBuilder-BRa_nrmH.cjs";
8
+ import { t as CronBuilder } from "./CronBuilder-OaCRRFVk.cjs";
9
+ import { ObjectStorage } from "./object-storage.cjs";
10
+ import { t as QueueBuilder } from "./QueueBuilder-BcPBQSSX.cjs";
11
+ import { n as ObjectSubscriberBuilder, o as SubscriberBuilder } from "./ObjectSubscriberBuilder-l-64yUiF.cjs";
11
12
  import "./topic/index.cjs";
12
13
  import { EnvironmentParser } from "@geekmidas/envkit";
13
14
  import { Logger } from "@geekmidas/logger";
@@ -108,6 +109,24 @@ export declare class Worker<TName extends string = string, TDatabase = undefined
108
109
  * ```
109
110
  */
110
111
  queue<TQueueName extends string>(name: TQueueName): QueueBuilder<TQueueName, undefined, [], Logger, TDatabase, TDatabaseServiceName>;
112
+ /**
113
+ * A runnable that reacts to a bucket's objects changing.
114
+ *
115
+ * A bucket is a topic whose publisher is the storage: the binding is the
116
+ * same as {@link Worker.topic}'s, and so is everything after it — the
117
+ * services, the database, how it is delivered. The handler is handed the
118
+ * objects; reading one still means depending on the bucket,
119
+ * `.dependsOn([uploads])`.
120
+ *
121
+ * @example
122
+ * ```ts
123
+ * export const onAvatar = worker
124
+ * .objects(uploads)
125
+ * .on(['object.created'], { prefix: 'avatars/', suffix: '.png' })
126
+ * .handle(async ({ objects }) => { … });
127
+ * ```
128
+ */
129
+ objects<TBucketName extends string>(bucket: ObjectStorage<TBucketName>): ObjectSubscriberBuilder<[], Logger, undefined, [], TDatabase, TDatabaseServiceName>;
111
130
  /**
112
131
  * A runnable that consumes a topic.
113
132
  *
@@ -1 +1 @@
1
- {"version":3,"file":"worker.d.cts","names":["_$__topic0.TopicMessage","_$__construct_interface0.ServicesOf","_$__functions0.FunctionHandler","_$__functions0.Function"],"sources":["../src/worker.ts"],"mappings":";;;;;;;;;;;;;;;;;iBAoGiB;;;;;;;;;EAShB,SAAS;;;;;;;EAOT,YAAY;;;;;;EAMZ,YAAY;;qBAGA,OACZ,+BACA,uBACA,yDACY,WAAW;mBAmBL;;mBAEA;WAnBT,IAAI;;WAGJ,QAAQ;;WAGR,WAAW;;;;;;WAOX,gBAAgB;EAEzB,YACC,IAAI,cAAc,QACD,SAAQ,cAER;;EAAA,wBAAuB,cACxC,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyCjB,SAAS,GAAG,wBACX,QAAQ,WAAW,SAAS,KAC1B,OAAO,OAAO,GAAG;;;;;;EAcpB,KAAK,UAAU,qBAAkB,YAAA,0BAAA,WAAA,mBAAA,WAAA;;;;;;;;;;;;;;;;;EA6BjC,MAAM,2BAA2B,MAAM,aAAU,aAAA,2BAAA,QAAA,WAAA;;;;;;;;;;;EAuBjD,MAAM,2BAA2B,gBAAgB,aAChD,OAAO,MAAM,YAAY,WAAQ,sBAAA,mBAAA,eAAAA,aAAA,iBAAA,WAAA;;;;;;;;;;;;EA0BlC,MAAM,UAAU,0BAA0B,QAAQ,IAAC,gBAAA,kBAAA,2BAAA,WAAA,sBAAA;EAInD,OAAO,UAAU,kBAAkB,QAAQ,IAAC,qBAAA,OAAA,2BAAA,WAAA,sBAAA;EAI5C,QAAQ,aAAU,oCAAA,2BAAA,WAAA,sBAAA;EAIlB,WAAW,aAAU,oCAAA,2BAAA,WAAA,sBAAA;EAIrB,gBAAgB,mBAAmB,cAAc,YAAY,IAAC,oCAAAC,WAAA,KAAA,2BAAA,WAAA,sBAAA;EAI9D,SAAS,UAAU,WAAW,UAAU,IAAC,oCAAA,IAAA,2BAAA,WAAA,sBAAA;;;;;;EASzC,UACI,MAAM,WACR,WACC,OAAO,OAAO,WAAW,0DAE1BE,kBAAA,sCAAA,WAAA,sBAAA,kCAAAD,yBAAA,mBAAA,sBAAA;;UAMM;;cAKI;;;;;;;EAqBZ,MACC,qBAAqB,eACnB,OAAO,OAAO,WAAW;;;;;;;;;;;;UAoBpB;EAiBR,WAAW"}
1
+ {"version":3,"file":"worker.d.cts","names":["_$__topic0.TopicMessage","_$__construct_interface0.ServicesOf","_$__functions0.FunctionHandler","_$__functions0.Function"],"sources":["../src/worker.ts"],"mappings":";;;;;;;;;;;;;;;;;;iBAsGiB;;;;;;;;;EAShB,SAAS;;;;;;;EAOT,YAAY;;;;;;EAMZ,YAAY;;qBAGA,OACZ,+BACA,uBACA,yDACY,WAAW;mBAmBL;;mBAEA;WAnBT,IAAI;;WAGJ,QAAQ;;WAGR,WAAW;;;;;;WAOX,gBAAgB;EAEzB,YACC,IAAI,cAAc,QACD,SAAQ,cAER;;EAAA,wBAAuB,cACxC,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyCjB,SAAS,GAAG,wBACX,QAAQ,WAAW,SAAS,KAC1B,OAAO,OAAO,GAAG;;;;;;EAcpB,KAAK,UAAU,qBAAkB,YAAA,0BAAA,WAAA,mBAAA,WAAA;;;;;;;;;;;;;;;;;EA6BjC,MAAM,2BAA2B,MAAM,aAAU,aAAA,2BAAA,QAAA,WAAA;;;;;;;;;;;;;;;;;;EA8BjD,QAAQ,4BAA4B,QAAQ,cAAc,eAAY,4BAAA,uBAAA,WAAA;;;;;;;;;;;EAgCtE,MAAM,2BAA2B,gBAAgB,aAChD,OAAO,MAAM,YAAY,WAAQ,sBAAA,mBAAA,eAAAA,aAAA,iBAAA,WAAA;;;;;;;;;;;;EA0BlC,MAAM,UAAU,0BAA0B,QAAQ,IAAC,gBAAA,kBAAA,2BAAA,WAAA,sBAAA;EAInD,OAAO,UAAU,kBAAkB,QAAQ,IAAC,qBAAA,OAAA,2BAAA,WAAA,sBAAA;EAI5C,QAAQ,aAAU,oCAAA,2BAAA,WAAA,sBAAA;EAIlB,WAAW,aAAU,oCAAA,2BAAA,WAAA,sBAAA;EAIrB,gBAAgB,mBAAmB,cAAc,YAAY,IAAC,oCAAAC,WAAA,KAAA,2BAAA,WAAA,sBAAA;EAI9D,SAAS,UAAU,WAAW,UAAU,IAAC,oCAAA,IAAA,2BAAA,WAAA,sBAAA;;;;;;EASzC,UACI,MAAM,WACR,WACC,OAAO,OAAO,WAAW,0DAE1BE,kBAAA,sCAAA,WAAA,sBAAA,kCAAAD,yBAAA,mBAAA,sBAAA;;UAMM;;cAKI;;;;;;;EAqBZ,MACC,qBAAqB,eACnB,OAAO,OAAO,WAAW;;;;;;;;;;;;UAoBpB;EAiBR,WAAW"}
package/dist/worker.d.mts CHANGED
@@ -1,13 +1,14 @@
1
1
  import { n as StandardSchemaV1 } from "./index-DdRjYhiJ.mjs";
2
- import { c as Topic, l as TopicEvents, u as TopicMessage } from "./Construct-B7AXn0xH.mjs";
3
- import { i as FunctionHandler, n as Function } from "./Function-B_9R7Zy1.mjs";
2
+ import { c as Topic, l as TopicEvents, u as TopicMessage } from "./Construct-DySlwh8o.mjs";
3
+ import { i as FunctionHandler, n as Function } from "./Function-DfMzTGS4.mjs";
4
4
  import { c as ServicesOf, i as Declarable, r as Consumable } from "./construct-interface-ZvYQbivO.mjs";
5
- import { t as FunctionBuilder } from "./index-aBIcAiH5.mjs";
6
- import { i as ScheduleExpression } from "./Cron-i4Uetezh.mjs";
5
+ import { t as FunctionBuilder } from "./index-DuriFjYD.mjs";
6
+ import { i as ScheduleExpression } from "./Cron-CQoDSu-n.mjs";
7
7
  import { n as Telemetry } from "./telemetry-BEoBn6GO.mjs";
8
- import { t as CronBuilder } from "./CronBuilder-9H4BnD6W.mjs";
9
- import { t as QueueBuilder } from "./QueueBuilder-48W-q8In.mjs";
10
- import { t as SubscriberBuilder } from "./SubscriberBuilder-D9bkrYzH.mjs";
8
+ import { t as CronBuilder } from "./CronBuilder-1W-G_i15.mjs";
9
+ import { ObjectStorage } from "./object-storage.mjs";
10
+ import { t as QueueBuilder } from "./QueueBuilder-B3vL1ED2.mjs";
11
+ import { n as ObjectSubscriberBuilder, o as SubscriberBuilder } from "./ObjectSubscriberBuilder-HmBwFn2o.mjs";
11
12
  import "./topic/index.mjs";
12
13
  import { ConstructName, Declaration, Dependency } from "@geekmidas/manifest";
13
14
  import { Service } from "@geekmidas/services";
@@ -108,6 +109,24 @@ export declare class Worker<TName extends string = string, TDatabase = undefined
108
109
  * ```
109
110
  */
110
111
  queue<TQueueName extends string>(name: TQueueName): QueueBuilder<TQueueName, undefined, [], Logger, TDatabase, TDatabaseServiceName>;
112
+ /**
113
+ * A runnable that reacts to a bucket's objects changing.
114
+ *
115
+ * A bucket is a topic whose publisher is the storage: the binding is the
116
+ * same as {@link Worker.topic}'s, and so is everything after it — the
117
+ * services, the database, how it is delivered. The handler is handed the
118
+ * objects; reading one still means depending on the bucket,
119
+ * `.dependsOn([uploads])`.
120
+ *
121
+ * @example
122
+ * ```ts
123
+ * export const onAvatar = worker
124
+ * .objects(uploads)
125
+ * .on(['object.created'], { prefix: 'avatars/', suffix: '.png' })
126
+ * .handle(async ({ objects }) => { … });
127
+ * ```
128
+ */
129
+ objects<TBucketName extends string>(bucket: ObjectStorage<TBucketName>): ObjectSubscriberBuilder<[], Logger, undefined, [], TDatabase, TDatabaseServiceName>;
111
130
  /**
112
131
  * A runnable that consumes a topic.
113
132
  *
@@ -1 +1 @@
1
- {"version":3,"file":"worker.d.mts","names":["_$__topic0.TopicMessage","_$__construct_interface0.ServicesOf","_$__functions0.FunctionHandler","_$__functions0.Function"],"sources":["../src/worker.ts"],"mappings":";;;;;;;;;;;;;;;;;iBAoGiB;;;;;;;;;EAShB,SAAS;;;;;;;EAOT,YAAY;;;;;;EAMZ,YAAY;;qBAGA,OACZ,+BACA,uBACA,yDACY,WAAW;mBAmBL;;mBAEA;WAnBT,IAAI;;WAGJ,QAAQ;;WAGR,WAAW;;;;;;WAOX,gBAAgB;EAEzB,YACC,IAAI,cAAc,QACD,SAAQ,cAER;;EAAA,wBAAuB,cACxC,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyCjB,SAAS,GAAG,wBACX,QAAQ,WAAW,SAAS,KAC1B,OAAO,OAAO,GAAG;;;;;;EAcpB,KAAK,UAAU,qBAAkB,YAAA,0BAAA,WAAA,mBAAA,WAAA;;;;;;;;;;;;;;;;;EA6BjC,MAAM,2BAA2B,MAAM,aAAU,aAAA,2BAAA,QAAA,WAAA;;;;;;;;;;;EAuBjD,MAAM,2BAA2B,gBAAgB,aAChD,OAAO,MAAM,YAAY,WAAQ,sBAAA,mBAAA,eAAAA,aAAA,iBAAA,WAAA;;;;;;;;;;;;EA0BlC,MAAM,UAAU,0BAA0B,QAAQ,IAAC,gBAAA,kBAAA,2BAAA,WAAA,sBAAA;EAInD,OAAO,UAAU,kBAAkB,QAAQ,IAAC,qBAAA,OAAA,2BAAA,WAAA,sBAAA;EAI5C,QAAQ,aAAU,oCAAA,2BAAA,WAAA,sBAAA;EAIlB,WAAW,aAAU,oCAAA,2BAAA,WAAA,sBAAA;EAIrB,gBAAgB,mBAAmB,cAAc,YAAY,IAAC,oCAAAC,WAAA,KAAA,2BAAA,WAAA,sBAAA;EAI9D,SAAS,UAAU,WAAW,UAAU,IAAC,oCAAA,IAAA,2BAAA,WAAA,sBAAA;;;;;;EASzC,UACI,MAAM,WACR,WACC,OAAO,OAAO,WAAW,0DAE1BE,kBAAA,sCAAA,WAAA,sBAAA,kCAAAD,yBAAA,mBAAA,sBAAA;;UAMM;;cAKI;;;;;;;EAqBZ,MACC,qBAAqB,eACnB,OAAO,OAAO,WAAW;;;;;;;;;;;;UAoBpB;EAiBR,WAAW"}
1
+ {"version":3,"file":"worker.d.mts","names":["_$__topic0.TopicMessage","_$__construct_interface0.ServicesOf","_$__functions0.FunctionHandler","_$__functions0.Function"],"sources":["../src/worker.ts"],"mappings":";;;;;;;;;;;;;;;;;;iBAsGiB;;;;;;;;;EAShB,SAAS;;;;;;;EAOT,YAAY;;;;;;EAMZ,YAAY;;qBAGA,OACZ,+BACA,uBACA,yDACY,WAAW;mBAmBL;;mBAEA;WAnBT,IAAI;;WAGJ,QAAQ;;WAGR,WAAW;;;;;;WAOX,gBAAgB;EAEzB,YACC,IAAI,cAAc,QACD,SAAQ,cAER;;EAAA,wBAAuB,cACxC,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyCjB,SAAS,GAAG,wBACX,QAAQ,WAAW,SAAS,KAC1B,OAAO,OAAO,GAAG;;;;;;EAcpB,KAAK,UAAU,qBAAkB,YAAA,0BAAA,WAAA,mBAAA,WAAA;;;;;;;;;;;;;;;;;EA6BjC,MAAM,2BAA2B,MAAM,aAAU,aAAA,2BAAA,QAAA,WAAA;;;;;;;;;;;;;;;;;;EA8BjD,QAAQ,4BAA4B,QAAQ,cAAc,eAAY,4BAAA,uBAAA,WAAA;;;;;;;;;;;EAgCtE,MAAM,2BAA2B,gBAAgB,aAChD,OAAO,MAAM,YAAY,WAAQ,sBAAA,mBAAA,eAAAA,aAAA,iBAAA,WAAA;;;;;;;;;;;;EA0BlC,MAAM,UAAU,0BAA0B,QAAQ,IAAC,gBAAA,kBAAA,2BAAA,WAAA,sBAAA;EAInD,OAAO,UAAU,kBAAkB,QAAQ,IAAC,qBAAA,OAAA,2BAAA,WAAA,sBAAA;EAI5C,QAAQ,aAAU,oCAAA,2BAAA,WAAA,sBAAA;EAIlB,WAAW,aAAU,oCAAA,2BAAA,WAAA,sBAAA;EAIrB,gBAAgB,mBAAmB,cAAc,YAAY,IAAC,oCAAAC,WAAA,KAAA,2BAAA,WAAA,sBAAA;EAI9D,SAAS,UAAU,WAAW,UAAU,IAAC,oCAAA,IAAA,2BAAA,WAAA,sBAAA;;;;;;EASzC,UACI,MAAM,WACR,WACC,OAAO,OAAO,WAAW,0DAE1BE,kBAAA,sCAAA,WAAA,sBAAA,kCAAAD,yBAAA,mBAAA,sBAAA;;UAMM;;cAKI;;;;;;;EAqBZ,MACC,qBAAqB,eACnB,OAAO,OAAO,WAAW;;;;;;;;;;;;UAoBpB;EAiBR,WAAW"}
package/dist/worker.mjs CHANGED
@@ -1,9 +1,9 @@
1
1
  import { edgeTo } from "./construct-interface.mjs";
2
- import { t as FunctionBuilder } from "./functions-CTI-hDem.mjs";
2
+ import { t as FunctionBuilder } from "./functions-BvIDQKAA.mjs";
3
3
  import { n as envParserFor } from "./surfaceEnv-wMT5QzgZ.mjs";
4
- import { t as CronBuilder } from "./CronBuilder-D3sF9tS_.mjs";
5
- import { t as QueueBuilder } from "./QueueBuilder-DXu1qxT1.mjs";
6
- import { t as SubscriberBuilder } from "./SubscriberBuilder-DRXfxWn_.mjs";
4
+ import { t as CronBuilder } from "./CronBuilder-BJ5LoadF.mjs";
5
+ import { t as QueueBuilder } from "./QueueBuilder-2wP5lEC8.mjs";
6
+ import { r as ObjectSubscriberBuilder, t as SubscriberBuilder } from "./SubscriberBuilder-3msj1U2w.mjs";
7
7
  import { canonicalId } from "@geekmidas/manifest";
8
8
  import { DEFAULT_LOGGER } from "@geekmidas/logger/console";
9
9
  //#region src/worker.ts
@@ -91,6 +91,27 @@ var Worker = class Worker {
91
91
  return this.own(new QueueBuilder().logger(this.logger)).queue(name);
92
92
  }
93
93
  /**
94
+ * A runnable that reacts to a bucket's objects changing.
95
+ *
96
+ * A bucket is a topic whose publisher is the storage: the binding is the
97
+ * same as {@link Worker.topic}'s, and so is everything after it — the
98
+ * services, the database, how it is delivered. The handler is handed the
99
+ * objects; reading one still means depending on the bucket,
100
+ * `.dependsOn([uploads])`.
101
+ *
102
+ * @example
103
+ * ```ts
104
+ * export const onAvatar = worker
105
+ * .objects(uploads)
106
+ * .on(['object.created'], { prefix: 'avatars/', suffix: '.png' })
107
+ * .handle(async ({ objects }) => { … });
108
+ * ```
109
+ */
110
+ objects(bucket) {
111
+ const inner = this.own(new SubscriberBuilder().logger(this.logger));
112
+ return new ObjectSubscriberBuilder(inner, bucket.id);
113
+ }
114
+ /**
94
115
  * A runnable that consumes a topic.
95
116
  *
96
117
  * Binding is not publishing: a consumer is handed the topic's event types
@@ -1 +1 @@
1
- {"version":3,"file":"worker.mjs","names":[],"sources":["../src/worker.ts"],"sourcesContent":["/**\n * A process with no port.\n *\n * `RestApi` is the process that answers HTTP. This is the one that does not —\n * the thing that runs a schedule, drains a queue, consumes a topic. Until it\n * existed, those had nowhere to say they belonged: a cron declared a schedule\n * and nothing else, and which container ran it was decided by the directory the\n * file sat in. A glob owned it.\n *\n * The cost of that shows up in the scaffolds. `gkm init --template worker` —\n * a project that is background jobs and nothing else — declared\n * `new RestApi('Api', { defaultAuthorizer: 'none', logger })`: an HTTP surface\n * with no HTTP on it, written because it was the only construct that made an\n * app exist and the only way to get a logger into the generated runtime.\n *\n * It takes no authorizer, and that is the tell that these are two constructs\n * rather than one with a flag: a `RestApi` needs one because an HTTP surface\n * can ship open by omission, and nothing reaches a worker from outside.\n *\n * ## Crons on a server target need a database\n *\n * A cron becomes an EventBridge rule on AWS and nothing here is consulted. A\n * server has nothing firing its crons but itself, and a process that schedules\n * in memory fires every job once per replica while reporting success — so the\n * schedule is kept in Postgres, and the worker says which one with\n * `.database(db)`.\n *\n * A worker with crons and no `.database(…)` schedules nothing on a server\n * target and says so at startup. That is a limitation of workers today rather\n * than a design: the store could be a cache, or something the target\n * provisions, and Postgres is what exists.\n *\n * ## The database is everything's default\n *\n * The same `.database(db)` is the database every cron, queue, subscriber and\n * function built from the worker receives as `db`. The work a worker groups is\n * usually work against one database, and naming it per handler was a line\n * repeated in every file that could drift in any one of them. A runnable that\n * works against another names its own with `.database(other)`, which replaces\n * the worker's — client and manifest edge both — rather than adding to it.\n *\n * It is not an app: it declares no path, and its code lives in an app's\n * directory. But it is a process of its own. On a server target each worker\n * with work to do is built — from the app that holds that work — into an image\n * that runs its crons, consumers and subscribers and answers only a health\n * check, so a slow queue never shares a process with the requests an API\n * serves. `gkm dev` still runs everything in one process. Declare as many as\n * the shape of the work wants: each is one container.\n *\n * @example\n * ```ts\n * // constructs/worker.ts\n * import { Worker } from '@geekmidas/constructs/worker';\n * import { logger } from './logger.js';\n *\n * export const worker = new Worker('Worker', { logger }).database(database);\n *\n * // crons/cleanup.ts\n * import { worker } from '@acme/constructs/worker.js';\n *\n * export const cleanup = worker\n * .cron('rate(1 day)')\n * .handle(async ({ db, logger }) => { … });\n *\n * // queues/reports.ts — against another database than the worker's\n * export const reports = worker\n * .queue('Reports')\n * .database(analytics)\n * .message(z.object({ id: z.uuid() }))\n * .handle(async ({ messages, db }) => { … });\n * ```\n */\n\nimport type { EnvironmentParser } from '@geekmidas/envkit';\nimport type { EventPublisher } from '@geekmidas/events';\nimport type { Logger } from '@geekmidas/logger';\nimport { DEFAULT_LOGGER } from '@geekmidas/logger/console';\nimport {\n\ttype ConstructName,\n\tcanonicalId,\n\ttype Declaration,\n\ttype Dependency,\n} from '@geekmidas/manifest';\nimport type { ComposableStandardSchema } from '@geekmidas/schema';\nimport type { Service } from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport {\n\ttype Consumable,\n\ttype Declarable,\n\tedgeTo,\n} from './construct-interface';\nimport type { ScheduleExpression } from './crons/Cron';\nimport { CronBuilder } from './crons/CronBuilder';\nimport { envParserFor } from './endpoints/surfaceEnv';\nimport { FunctionBuilder } from './functions/FunctionBuilder';\nimport { QueueBuilder } from './queue/QueueBuilder';\nimport { SubscriberBuilder } from './subscribers/SubscriberBuilder';\nimport type { Telemetry } from './telemetry';\nimport type { Topic, TopicEvents } from './topic/Topic';\n\nexport interface WorkerConfig {\n\t/**\n\t * The logger everything built from this worker runs with.\n\t *\n\t * The actual logger, not a path to one — the same reason `RestApi` takes\n\t * one. Every cron and subscriber in a scaffolded project used to open with\n\t * `import { logger } from '…/constructs/logger.js'`, which is the line this\n\t * removes.\n\t */\n\tlogger?: Logger;\n\t/**\n\t * The environment parser everything built from this worker runs with.\n\t *\n\t * Defaults to `process.env` merged with the credentials `gkm dev` injected,\n\t * which is what an application's own `config/env.ts` always was.\n\t */\n\tenvParser?: EnvironmentParser<{}>;\n\t/**\n\t * What the worker's process emits — the `Telemetry` construct, passed like\n\t * the logger. An edge to the node: the deploy hands the process the\n\t * `OTEL_*` keys its stage resolves.\n\t */\n\ttelemetry?: Telemetry;\n}\n\nexport class Worker<\n\tTName extends string = string,\n\tTDatabase = undefined,\n\tTDatabaseServiceName extends string = string,\n> implements Declarable<TName>\n{\n\treadonly id: TName;\n\n\t/** Always defined — the worker's own, or the console logger. */\n\treadonly logger: Logger;\n\n\t/** Always defined, and normally the default. */\n\treadonly envParser: EnvironmentParser<{}>;\n\n\t/**\n\t * Where this worker's schedules live, when it has crons and a server runs\n\t * them — and the database everything built from it receives as `db`.\n\t * `undefined` until `.database(…)` says.\n\t */\n\treadonly scheduleStore?: Consumable<string, unknown>;\n\n\tconstructor(\n\t\tid: ConstructName<TName>,\n\t\tprivate readonly config: WorkerConfig = {},\n\t\t/** Internal: how `.calls()` carries edges into the copy it returns. */\n\t\tprivate readonly dependencies: readonly Dependency[] = [],\n\t\tscheduleStore?: Consumable<string, unknown>,\n\t) {\n\t\tconst canonical = canonicalId(id as string);\n\n\t\tthis.id = canonical as TName;\n\t\tthis.logger = config.logger ?? DEFAULT_LOGGER;\n\t\tthis.envParser = envParserFor(\n\t\t\tconfig.envParser\n\t\t\t\t? { id: canonical, envParser: config.envParser }\n\t\t\t\t: undefined,\n\t\t);\n\t\tthis.scheduleStore = scheduleStore;\n\t}\n\n\t/**\n\t * The database this worker keeps its schedules in, and the one everything\n\t * built from it — crons, queues, subscribers, functions — receives as `db`.\n\t * One of them that works against another database names it with its own\n\t * `.database(other)`, which replaces this one for that runnable alone.\n\t *\n\t * Only a server target uses it. On AWS a cron is an EventBridge rule\n\t * invoking a Lambda and nothing here is consulted — but a server has nothing\n\t * firing its crons except itself, and a process that schedules in memory\n\t * fires every job once per replica while reporting that all is well.\n\t *\n\t * So the store is declared rather than discovered. It could have been\n\t * inferred — from a database the app happens to declare, or from the events\n\t * backend when that backend is pg-boss — and both would work until an app\n\t * had two databases, or moved its events to SNS, at which point the\n\t * schedules would quietly move or stop. Naming it is one line and cannot\n\t * drift.\n\t *\n\t * @example\n\t * ```ts\n\t * export const jobs = new Worker('Jobs', { logger }).database(database);\n\t *\n\t * export const sweep = jobs\n\t * .cron('rate(1 hour)')\n\t * .handle(async ({ db }) => db.deleteFrom('sessions').execute());\n\t * ```\n\t */\n\tdatabase<T, TDbName extends string>(\n\t\tsource: Consumable<TDbName, T>,\n\t): Worker<TName, T, TDbName> {\n\t\treturn new Worker<TName, T, TDbName>(\n\t\t\tthis.id as ConstructName<TName>,\n\t\t\tthis.config,\n\t\t\t[...this.dependencies, edgeTo(source)],\n\t\t\tsource as unknown as Consumable<string, unknown>,\n\t\t);\n\t}\n\n\t/**\n\t * A scheduled runnable.\n\t *\n\t * @example `worker.cron('rate(1 day)').handle(async ({ logger }) => { … })`\n\t */\n\tcron(schedule: ScheduleExpression) {\n\t\treturn this.own(\n\t\t\tnew CronBuilder<\n\t\t\t\tComposableStandardSchema,\n\t\t\t\tService[],\n\t\t\t\tLogger,\n\t\t\t\tundefined,\n\t\t\t\tTDatabase,\n\t\t\t\tTDatabaseServiceName\n\t\t\t>().logger(this.logger),\n\t\t).schedule(schedule);\n\t}\n\n\t/**\n\t * A queue and its one consumer, run by this worker.\n\t *\n\t * One construct, because a queue has exactly one consumer: nothing else can\n\t * attach a second, or forget to attach the first. Producers send to it by\n\t * depending on it — `.dependsOn([emails])` makes `services.emails` the\n\t * publisher.\n\t *\n\t * @example\n\t * ```ts\n\t * export const emails = worker\n\t * .queue('Emails')\n\t * .message(z.object({ userId: z.uuid(), template: z.string() }))\n\t * .handle(async ({ messages, services }) => { … });\n\t * ```\n\t */\n\tqueue<TQueueName extends string>(name: TQueueName) {\n\t\treturn this.own(\n\t\t\tnew QueueBuilder<\n\t\t\t\tstring,\n\t\t\t\tundefined,\n\t\t\t\t[],\n\t\t\t\tLogger,\n\t\t\t\tTDatabase,\n\t\t\t\tTDatabaseServiceName\n\t\t\t>().logger(this.logger),\n\t\t).queue(name);\n\t}\n\n\t/**\n\t * A runnable that consumes a topic.\n\t *\n\t * Binding is not publishing: a consumer is handed the topic's event types\n\t * and no connection string it could publish with. A handler that emits\n\t * follow-up events depends on the topic it emits to —\n\t * `.dependsOn([orders])` — which is the only reason it would need one.\n\t *\n\t * @example `worker.topic(users).subscribe(['user.created']).handle(…)`\n\t */\n\ttopic<TTopicName extends string, TEvents extends TopicEvents>(\n\t\ttopic: Topic<TTopicName, TEvents>,\n\t) {\n\t\treturn this.own(\n\t\t\tnew SubscriberBuilder<\n\t\t\t\t[],\n\t\t\t\tLogger,\n\t\t\t\tundefined,\n\t\t\t\tundefined,\n\t\t\t\t[],\n\t\t\t\tTDatabase,\n\t\t\t\tTDatabaseServiceName\n\t\t\t>().logger(this.logger),\n\t\t).topic(topic);\n\t}\n\n\t/**\n\t * Everything below builds a function — the runnable with no schedule and no\n\t * topic, invoked by something else.\n\t *\n\t * These sit on the worker rather than behind a `functions` namespace because\n\t * the namespace was a hop that said nothing: `worker.functions.input(…)`\n\t * names a collection to reach one member of it. What kind of runnable is\n\t * being built is decided by which of these is called first — a schedule\n\t * makes a cron, a topic makes a subscriber, and anything else makes a\n\t * function.\n\t */\n\tinput<T extends ComposableStandardSchema>(schema: T) {\n\t\treturn this.functions.input(schema);\n\t}\n\n\toutput<T extends StandardSchemaV1>(schema: T) {\n\t\treturn this.functions.output(schema);\n\t}\n\n\ttimeout(ms: number) {\n\t\treturn this.functions.timeout(ms);\n\t}\n\n\tmemorySize(mb: number) {\n\t\treturn this.functions.memorySize(mb);\n\t}\n\n\tdependsOn<const T extends readonly Consumable[]>(constructs: T) {\n\t\treturn this.functions.dependsOn(constructs);\n\t}\n\n\tservices<T extends Service[]>(services: T) {\n\t\treturn this.functions.services(services);\n\t}\n\n\t/**\n\t * A function with no input schema — the shortest runnable there is.\n\t *\n\t * @example `worker.handle(async ({ logger }) => { … })`\n\t */\n\thandle(\n\t\t...args: Parameters<\n\t\t\tReturnType<\n\t\t\t\tWorker<TName, TDatabase, TDatabaseServiceName>['schemalessFunction']\n\t\t\t>['handle']\n\t\t>\n\t) {\n\t\treturn this.schemalessFunction().handle(...args);\n\t}\n\n\t/** The builder `handle` delegates to, named so its type can be referred to. */\n\tprivate schemalessFunction() {\n\t\treturn this.functions;\n\t}\n\n\t/** The function builder the delegating methods above are built on. */\n\tprivate get functions() {\n\t\treturn this.own(\n\t\t\tnew FunctionBuilder<\n\t\t\t\tany,\n\t\t\t\tundefined,\n\t\t\t\t[],\n\t\t\t\tLogger,\n\t\t\t\tundefined,\n\t\t\t\tstring,\n\t\t\t\tTDatabase,\n\t\t\t\tTDatabaseServiceName\n\t\t\t>().logger(this.logger),\n\t\t);\n\t}\n\n\t/**\n\t * Resources and surfaces this worker calls.\n\t *\n\t * Not `.dependsOn()`: this records the edge that derives the worker's\n\t * environment and its access, the same distinction `RestApi.calls()` draws.\n\t */\n\tcalls(\n\t\tconstructs: readonly Declarable[],\n\t): Worker<TName, TDatabase, TDatabaseServiceName> {\n\t\treturn new Worker<TName, TDatabase, TDatabaseServiceName>(\n\t\t\tthis.id as ConstructName<TName>,\n\t\t\tthis.config,\n\t\t\t[...this.dependencies, ...constructs.map(edgeTo)],\n\t\t\tthis.scheduleStore,\n\t\t);\n\t}\n\n\t/**\n\t * Stamps this worker's id onto a builder, so everything built from it says\n\t * which process runs it — and hands it the worker's database.\n\t *\n\t * The database goes through the builder's own `.database()` rather than\n\t * being written into its fields, so the default is recorded exactly as an\n\t * explicit one would be: the client the handler gets, and the edge the\n\t * manifest reads. That is also what lets a later `.database(other)` replace\n\t * it outright. The builder arrives already typed with this worker's\n\t * database; this is the runtime half of that.\n\t */\n\tprivate own<\n\t\tT extends {\n\t\t\t_owner?: string;\n\t\t\t_scheduleStore?: unknown;\n\t\t\tdatabase(source: Consumable<string, unknown>): unknown;\n\t\t},\n\t>(builder: T): T {\n\t\tconst owned = this.scheduleStore\n\t\t\t? (builder.database(this.scheduleStore) as T)\n\t\t\t: builder;\n\n\t\towned._owner = this.id;\n\t\towned._scheduleStore = this.scheduleStore;\n\n\t\treturn owned;\n\t}\n\n\tdeclare(): Declaration[] {\n\t\treturn [\n\t\t\t{\n\t\t\t\tkind: 'worker',\n\t\t\t\tid: this.id,\n\t\t\t\t...(this.dependencies.length\n\t\t\t\t\t? { dependencies: this.dependencies }\n\t\t\t\t\t: {}),\n\t\t\t\t...(this.config.telemetry\n\t\t\t\t\t? { telemetry: this.config.telemetry.id }\n\t\t\t\t\t: {}),\n\t\t\t\t// Nothing calls a worker, so it publishes no address.\n\t\t\t\tprovides: [],\n\t\t\t},\n\t\t];\n\t}\n}\n"],"mappings":";;;;;;;;;AA6HA,IAAa,SAAb,MAAa,OAKb;CAkBmB;CAEA;CAnBlB;;CAGA;;CAGA;;;;;;CAOA;CAEA,YACC,IACA,SAAwC,CAAC,GAEzC,eAAuD,CAAC,GACxD,eACC;EAJgB,KAAA,SAAA;EAEA,KAAA,eAAA;EAGjB,MAAM,YAAY,YAAY,EAAY;EAE1C,KAAK,KAAK;EACV,KAAK,SAAS,OAAO,UAAU;EAC/B,KAAK,YAAY,aAChB,OAAO,YACJ;GAAE,IAAI;GAAW,WAAW,OAAO;EAAU,IAC7C,KAAA,CACJ;EACA,KAAK,gBAAgB;CACtB;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6BA,SACC,QAC4B;EAC5B,OAAO,IAAI,OACV,KAAK,IACL,KAAK,QACL,CAAC,GAAG,KAAK,cAAc,OAAO,MAAM,CAAC,GACrC,MACD;CACD;;;;;;CAOA,KAAK,UAA8B;EAClC,OAAO,KAAK,IACX,IAAI,YAOF,CAAC,CAAC,OAAO,KAAK,MAAM,CACvB,CAAC,CAAC,SAAS,QAAQ;CACpB;;;;;;;;;;;;;;;;;CAkBA,MAAiC,MAAkB;EAClD,OAAO,KAAK,IACX,IAAI,aAOF,CAAC,CAAC,OAAO,KAAK,MAAM,CACvB,CAAC,CAAC,MAAM,IAAI;CACb;;;;;;;;;;;CAYA,MACC,OACC;EACD,OAAO,KAAK,IACX,IAAI,kBAQF,CAAC,CAAC,OAAO,KAAK,MAAM,CACvB,CAAC,CAAC,MAAM,KAAK;CACd;;;;;;;;;;;;CAaA,MAA0C,QAAW;EACpD,OAAO,KAAK,UAAU,MAAM,MAAM;CACnC;CAEA,OAAmC,QAAW;EAC7C,OAAO,KAAK,UAAU,OAAO,MAAM;CACpC;CAEA,QAAQ,IAAY;EACnB,OAAO,KAAK,UAAU,QAAQ,EAAE;CACjC;CAEA,WAAW,IAAY;EACtB,OAAO,KAAK,UAAU,WAAW,EAAE;CACpC;CAEA,UAAiD,YAAe;EAC/D,OAAO,KAAK,UAAU,UAAU,UAAU;CAC3C;CAEA,SAA8B,UAAa;EAC1C,OAAO,KAAK,UAAU,SAAS,QAAQ;CACxC;;;;;;CAOA,OACC,GAAG,MAKF;EACD,OAAO,KAAK,mBAAmB,CAAC,CAAC,OAAO,GAAG,IAAI;CAChD;;CAGA,qBAA6B;EAC5B,OAAO,KAAK;CACb;;CAGA,IAAY,YAAY;EACvB,OAAO,KAAK,IACX,IAAI,gBASF,CAAC,CAAC,OAAO,KAAK,MAAM,CACvB;CACD;;;;;;;CAQA,MACC,YACiD;EACjD,OAAO,IAAI,OACV,KAAK,IACL,KAAK,QACL,CAAC,GAAG,KAAK,cAAc,GAAG,WAAW,IAAI,MAAM,CAAC,GAChD,KAAK,aACN;CACD;;;;;;;;;;;;CAaA,IAME,SAAe;EAChB,MAAM,QAAQ,KAAK,gBACf,QAAQ,SAAS,KAAK,aAAa,IACpC;EAEH,MAAM,SAAS,KAAK;EACpB,MAAM,iBAAiB,KAAK;EAE5B,OAAO;CACR;CAEA,UAAyB;EACxB,OAAO,CACN;GACC,MAAM;GACN,IAAI,KAAK;GACT,GAAI,KAAK,aAAa,SACnB,EAAE,cAAc,KAAK,aAAa,IAClC,CAAC;GACJ,GAAI,KAAK,OAAO,YACb,EAAE,WAAW,KAAK,OAAO,UAAU,GAAG,IACtC,CAAC;GAEJ,UAAU,CAAC;EACZ,CACD;CACD;AACD"}
1
+ {"version":3,"file":"worker.mjs","names":[],"sources":["../src/worker.ts"],"sourcesContent":["/**\n * A process with no port.\n *\n * `RestApi` is the process that answers HTTP. This is the one that does not —\n * the thing that runs a schedule, drains a queue, consumes a topic. Until it\n * existed, those had nowhere to say they belonged: a cron declared a schedule\n * and nothing else, and which container ran it was decided by the directory the\n * file sat in. A glob owned it.\n *\n * The cost of that shows up in the scaffolds. `gkm init --template worker` —\n * a project that is background jobs and nothing else — declared\n * `new RestApi('Api', { defaultAuthorizer: 'none', logger })`: an HTTP surface\n * with no HTTP on it, written because it was the only construct that made an\n * app exist and the only way to get a logger into the generated runtime.\n *\n * It takes no authorizer, and that is the tell that these are two constructs\n * rather than one with a flag: a `RestApi` needs one because an HTTP surface\n * can ship open by omission, and nothing reaches a worker from outside.\n *\n * ## Crons on a server target need a database\n *\n * A cron becomes an EventBridge rule on AWS and nothing here is consulted. A\n * server has nothing firing its crons but itself, and a process that schedules\n * in memory fires every job once per replica while reporting success — so the\n * schedule is kept in Postgres, and the worker says which one with\n * `.database(db)`.\n *\n * A worker with crons and no `.database(…)` schedules nothing on a server\n * target and says so at startup. That is a limitation of workers today rather\n * than a design: the store could be a cache, or something the target\n * provisions, and Postgres is what exists.\n *\n * ## The database is everything's default\n *\n * The same `.database(db)` is the database every cron, queue, subscriber and\n * function built from the worker receives as `db`. The work a worker groups is\n * usually work against one database, and naming it per handler was a line\n * repeated in every file that could drift in any one of them. A runnable that\n * works against another names its own with `.database(other)`, which replaces\n * the worker's — client and manifest edge both — rather than adding to it.\n *\n * It is not an app: it declares no path, and its code lives in an app's\n * directory. But it is a process of its own. On a server target each worker\n * with work to do is built — from the app that holds that work — into an image\n * that runs its crons, consumers and subscribers and answers only a health\n * check, so a slow queue never shares a process with the requests an API\n * serves. `gkm dev` still runs everything in one process. Declare as many as\n * the shape of the work wants: each is one container.\n *\n * @example\n * ```ts\n * // constructs/worker.ts\n * import { Worker } from '@geekmidas/constructs/worker';\n * import { logger } from './logger.js';\n *\n * export const worker = new Worker('Worker', { logger }).database(database);\n *\n * // crons/cleanup.ts\n * import { worker } from '@acme/constructs/worker.js';\n *\n * export const cleanup = worker\n * .cron('rate(1 day)')\n * .handle(async ({ db, logger }) => { … });\n *\n * // queues/reports.ts — against another database than the worker's\n * export const reports = worker\n * .queue('Reports')\n * .database(analytics)\n * .message(z.object({ id: z.uuid() }))\n * .handle(async ({ messages, db }) => { … });\n * ```\n */\n\nimport type { EnvironmentParser } from '@geekmidas/envkit';\nimport type { EventPublisher } from '@geekmidas/events';\nimport type { Logger } from '@geekmidas/logger';\nimport { DEFAULT_LOGGER } from '@geekmidas/logger/console';\nimport {\n\ttype ConstructName,\n\tcanonicalId,\n\ttype Declaration,\n\ttype Dependency,\n} from '@geekmidas/manifest';\nimport type { ComposableStandardSchema } from '@geekmidas/schema';\nimport type { Service } from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport {\n\ttype Consumable,\n\ttype Declarable,\n\tedgeTo,\n} from './construct-interface';\nimport type { ScheduleExpression } from './crons/Cron';\nimport { CronBuilder } from './crons/CronBuilder';\nimport { envParserFor } from './endpoints/surfaceEnv';\nimport { FunctionBuilder } from './functions/FunctionBuilder';\nimport type { ObjectStorage } from './object-storage';\nimport { QueueBuilder } from './queue/QueueBuilder';\nimport { ObjectSubscriberBuilder } from './subscribers/ObjectSubscriberBuilder';\nimport { SubscriberBuilder } from './subscribers/SubscriberBuilder';\nimport type { Telemetry } from './telemetry';\nimport type { Topic, TopicEvents } from './topic/Topic';\n\nexport interface WorkerConfig {\n\t/**\n\t * The logger everything built from this worker runs with.\n\t *\n\t * The actual logger, not a path to one — the same reason `RestApi` takes\n\t * one. Every cron and subscriber in a scaffolded project used to open with\n\t * `import { logger } from '…/constructs/logger.js'`, which is the line this\n\t * removes.\n\t */\n\tlogger?: Logger;\n\t/**\n\t * The environment parser everything built from this worker runs with.\n\t *\n\t * Defaults to `process.env` merged with the credentials `gkm dev` injected,\n\t * which is what an application's own `config/env.ts` always was.\n\t */\n\tenvParser?: EnvironmentParser<{}>;\n\t/**\n\t * What the worker's process emits — the `Telemetry` construct, passed like\n\t * the logger. An edge to the node: the deploy hands the process the\n\t * `OTEL_*` keys its stage resolves.\n\t */\n\ttelemetry?: Telemetry;\n}\n\nexport class Worker<\n\tTName extends string = string,\n\tTDatabase = undefined,\n\tTDatabaseServiceName extends string = string,\n> implements Declarable<TName>\n{\n\treadonly id: TName;\n\n\t/** Always defined — the worker's own, or the console logger. */\n\treadonly logger: Logger;\n\n\t/** Always defined, and normally the default. */\n\treadonly envParser: EnvironmentParser<{}>;\n\n\t/**\n\t * Where this worker's schedules live, when it has crons and a server runs\n\t * them — and the database everything built from it receives as `db`.\n\t * `undefined` until `.database(…)` says.\n\t */\n\treadonly scheduleStore?: Consumable<string, unknown>;\n\n\tconstructor(\n\t\tid: ConstructName<TName>,\n\t\tprivate readonly config: WorkerConfig = {},\n\t\t/** Internal: how `.calls()` carries edges into the copy it returns. */\n\t\tprivate readonly dependencies: readonly Dependency[] = [],\n\t\tscheduleStore?: Consumable<string, unknown>,\n\t) {\n\t\tconst canonical = canonicalId(id as string);\n\n\t\tthis.id = canonical as TName;\n\t\tthis.logger = config.logger ?? DEFAULT_LOGGER;\n\t\tthis.envParser = envParserFor(\n\t\t\tconfig.envParser\n\t\t\t\t? { id: canonical, envParser: config.envParser }\n\t\t\t\t: undefined,\n\t\t);\n\t\tthis.scheduleStore = scheduleStore;\n\t}\n\n\t/**\n\t * The database this worker keeps its schedules in, and the one everything\n\t * built from it — crons, queues, subscribers, functions — receives as `db`.\n\t * One of them that works against another database names it with its own\n\t * `.database(other)`, which replaces this one for that runnable alone.\n\t *\n\t * Only a server target uses it. On AWS a cron is an EventBridge rule\n\t * invoking a Lambda and nothing here is consulted — but a server has nothing\n\t * firing its crons except itself, and a process that schedules in memory\n\t * fires every job once per replica while reporting that all is well.\n\t *\n\t * So the store is declared rather than discovered. It could have been\n\t * inferred — from a database the app happens to declare, or from the events\n\t * backend when that backend is pg-boss — and both would work until an app\n\t * had two databases, or moved its events to SNS, at which point the\n\t * schedules would quietly move or stop. Naming it is one line and cannot\n\t * drift.\n\t *\n\t * @example\n\t * ```ts\n\t * export const jobs = new Worker('Jobs', { logger }).database(database);\n\t *\n\t * export const sweep = jobs\n\t * .cron('rate(1 hour)')\n\t * .handle(async ({ db }) => db.deleteFrom('sessions').execute());\n\t * ```\n\t */\n\tdatabase<T, TDbName extends string>(\n\t\tsource: Consumable<TDbName, T>,\n\t): Worker<TName, T, TDbName> {\n\t\treturn new Worker<TName, T, TDbName>(\n\t\t\tthis.id as ConstructName<TName>,\n\t\t\tthis.config,\n\t\t\t[...this.dependencies, edgeTo(source)],\n\t\t\tsource as unknown as Consumable<string, unknown>,\n\t\t);\n\t}\n\n\t/**\n\t * A scheduled runnable.\n\t *\n\t * @example `worker.cron('rate(1 day)').handle(async ({ logger }) => { … })`\n\t */\n\tcron(schedule: ScheduleExpression) {\n\t\treturn this.own(\n\t\t\tnew CronBuilder<\n\t\t\t\tComposableStandardSchema,\n\t\t\t\tService[],\n\t\t\t\tLogger,\n\t\t\t\tundefined,\n\t\t\t\tTDatabase,\n\t\t\t\tTDatabaseServiceName\n\t\t\t>().logger(this.logger),\n\t\t).schedule(schedule);\n\t}\n\n\t/**\n\t * A queue and its one consumer, run by this worker.\n\t *\n\t * One construct, because a queue has exactly one consumer: nothing else can\n\t * attach a second, or forget to attach the first. Producers send to it by\n\t * depending on it — `.dependsOn([emails])` makes `services.emails` the\n\t * publisher.\n\t *\n\t * @example\n\t * ```ts\n\t * export const emails = worker\n\t * .queue('Emails')\n\t * .message(z.object({ userId: z.uuid(), template: z.string() }))\n\t * .handle(async ({ messages, services }) => { … });\n\t * ```\n\t */\n\tqueue<TQueueName extends string>(name: TQueueName) {\n\t\treturn this.own(\n\t\t\tnew QueueBuilder<\n\t\t\t\tstring,\n\t\t\t\tundefined,\n\t\t\t\t[],\n\t\t\t\tLogger,\n\t\t\t\tTDatabase,\n\t\t\t\tTDatabaseServiceName\n\t\t\t>().logger(this.logger),\n\t\t).queue(name);\n\t}\n\n\t/**\n\t * A runnable that reacts to a bucket's objects changing.\n\t *\n\t * A bucket is a topic whose publisher is the storage: the binding is the\n\t * same as {@link Worker.topic}'s, and so is everything after it — the\n\t * services, the database, how it is delivered. The handler is handed the\n\t * objects; reading one still means depending on the bucket,\n\t * `.dependsOn([uploads])`.\n\t *\n\t * @example\n\t * ```ts\n\t * export const onAvatar = worker\n\t * .objects(uploads)\n\t * .on(['object.created'], { prefix: 'avatars/', suffix: '.png' })\n\t * .handle(async ({ objects }) => { … });\n\t * ```\n\t */\n\tobjects<TBucketName extends string>(bucket: ObjectStorage<TBucketName>) {\n\t\tconst inner = this.own(\n\t\t\tnew SubscriberBuilder<\n\t\t\t\t[],\n\t\t\t\tLogger,\n\t\t\t\tundefined,\n\t\t\t\tundefined,\n\t\t\t\t[],\n\t\t\t\tTDatabase,\n\t\t\t\tTDatabaseServiceName\n\t\t\t>().logger(this.logger),\n\t\t);\n\t\treturn new ObjectSubscriberBuilder<\n\t\t\t[],\n\t\t\tLogger,\n\t\t\tundefined,\n\t\t\t[],\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>(inner, bucket.id);\n\t}\n\n\t/**\n\t * A runnable that consumes a topic.\n\t *\n\t * Binding is not publishing: a consumer is handed the topic's event types\n\t * and no connection string it could publish with. A handler that emits\n\t * follow-up events depends on the topic it emits to —\n\t * `.dependsOn([orders])` — which is the only reason it would need one.\n\t *\n\t * @example `worker.topic(users).subscribe(['user.created']).handle(…)`\n\t */\n\ttopic<TTopicName extends string, TEvents extends TopicEvents>(\n\t\ttopic: Topic<TTopicName, TEvents>,\n\t) {\n\t\treturn this.own(\n\t\t\tnew SubscriberBuilder<\n\t\t\t\t[],\n\t\t\t\tLogger,\n\t\t\t\tundefined,\n\t\t\t\tundefined,\n\t\t\t\t[],\n\t\t\t\tTDatabase,\n\t\t\t\tTDatabaseServiceName\n\t\t\t>().logger(this.logger),\n\t\t).topic(topic);\n\t}\n\n\t/**\n\t * Everything below builds a function — the runnable with no schedule and no\n\t * topic, invoked by something else.\n\t *\n\t * These sit on the worker rather than behind a `functions` namespace because\n\t * the namespace was a hop that said nothing: `worker.functions.input(…)`\n\t * names a collection to reach one member of it. What kind of runnable is\n\t * being built is decided by which of these is called first — a schedule\n\t * makes a cron, a topic makes a subscriber, and anything else makes a\n\t * function.\n\t */\n\tinput<T extends ComposableStandardSchema>(schema: T) {\n\t\treturn this.functions.input(schema);\n\t}\n\n\toutput<T extends StandardSchemaV1>(schema: T) {\n\t\treturn this.functions.output(schema);\n\t}\n\n\ttimeout(ms: number) {\n\t\treturn this.functions.timeout(ms);\n\t}\n\n\tmemorySize(mb: number) {\n\t\treturn this.functions.memorySize(mb);\n\t}\n\n\tdependsOn<const T extends readonly Consumable[]>(constructs: T) {\n\t\treturn this.functions.dependsOn(constructs);\n\t}\n\n\tservices<T extends Service[]>(services: T) {\n\t\treturn this.functions.services(services);\n\t}\n\n\t/**\n\t * A function with no input schema — the shortest runnable there is.\n\t *\n\t * @example `worker.handle(async ({ logger }) => { … })`\n\t */\n\thandle(\n\t\t...args: Parameters<\n\t\t\tReturnType<\n\t\t\t\tWorker<TName, TDatabase, TDatabaseServiceName>['schemalessFunction']\n\t\t\t>['handle']\n\t\t>\n\t) {\n\t\treturn this.schemalessFunction().handle(...args);\n\t}\n\n\t/** The builder `handle` delegates to, named so its type can be referred to. */\n\tprivate schemalessFunction() {\n\t\treturn this.functions;\n\t}\n\n\t/** The function builder the delegating methods above are built on. */\n\tprivate get functions() {\n\t\treturn this.own(\n\t\t\tnew FunctionBuilder<\n\t\t\t\tany,\n\t\t\t\tundefined,\n\t\t\t\t[],\n\t\t\t\tLogger,\n\t\t\t\tundefined,\n\t\t\t\tstring,\n\t\t\t\tTDatabase,\n\t\t\t\tTDatabaseServiceName\n\t\t\t>().logger(this.logger),\n\t\t);\n\t}\n\n\t/**\n\t * Resources and surfaces this worker calls.\n\t *\n\t * Not `.dependsOn()`: this records the edge that derives the worker's\n\t * environment and its access, the same distinction `RestApi.calls()` draws.\n\t */\n\tcalls(\n\t\tconstructs: readonly Declarable[],\n\t): Worker<TName, TDatabase, TDatabaseServiceName> {\n\t\treturn new Worker<TName, TDatabase, TDatabaseServiceName>(\n\t\t\tthis.id as ConstructName<TName>,\n\t\t\tthis.config,\n\t\t\t[...this.dependencies, ...constructs.map(edgeTo)],\n\t\t\tthis.scheduleStore,\n\t\t);\n\t}\n\n\t/**\n\t * Stamps this worker's id onto a builder, so everything built from it says\n\t * which process runs it — and hands it the worker's database.\n\t *\n\t * The database goes through the builder's own `.database()` rather than\n\t * being written into its fields, so the default is recorded exactly as an\n\t * explicit one would be: the client the handler gets, and the edge the\n\t * manifest reads. That is also what lets a later `.database(other)` replace\n\t * it outright. The builder arrives already typed with this worker's\n\t * database; this is the runtime half of that.\n\t */\n\tprivate own<\n\t\tT extends {\n\t\t\t_owner?: string;\n\t\t\t_scheduleStore?: unknown;\n\t\t\tdatabase(source: Consumable<string, unknown>): unknown;\n\t\t},\n\t>(builder: T): T {\n\t\tconst owned = this.scheduleStore\n\t\t\t? (builder.database(this.scheduleStore) as T)\n\t\t\t: builder;\n\n\t\towned._owner = this.id;\n\t\towned._scheduleStore = this.scheduleStore;\n\n\t\treturn owned;\n\t}\n\n\tdeclare(): Declaration[] {\n\t\treturn [\n\t\t\t{\n\t\t\t\tkind: 'worker',\n\t\t\t\tid: this.id,\n\t\t\t\t...(this.dependencies.length\n\t\t\t\t\t? { dependencies: this.dependencies }\n\t\t\t\t\t: {}),\n\t\t\t\t...(this.config.telemetry\n\t\t\t\t\t? { telemetry: this.config.telemetry.id }\n\t\t\t\t\t: {}),\n\t\t\t\t// Nothing calls a worker, so it publishes no address.\n\t\t\t\tprovides: [],\n\t\t\t},\n\t\t];\n\t}\n}\n"],"mappings":";;;;;;;;;AA+HA,IAAa,SAAb,MAAa,OAKb;CAkBmB;CAEA;CAnBlB;;CAGA;;CAGA;;;;;;CAOA;CAEA,YACC,IACA,SAAwC,CAAC,GAEzC,eAAuD,CAAC,GACxD,eACC;EAJgB,KAAA,SAAA;EAEA,KAAA,eAAA;EAGjB,MAAM,YAAY,YAAY,EAAY;EAE1C,KAAK,KAAK;EACV,KAAK,SAAS,OAAO,UAAU;EAC/B,KAAK,YAAY,aAChB,OAAO,YACJ;GAAE,IAAI;GAAW,WAAW,OAAO;EAAU,IAC7C,KAAA,CACJ;EACA,KAAK,gBAAgB;CACtB;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6BA,SACC,QAC4B;EAC5B,OAAO,IAAI,OACV,KAAK,IACL,KAAK,QACL,CAAC,GAAG,KAAK,cAAc,OAAO,MAAM,CAAC,GACrC,MACD;CACD;;;;;;CAOA,KAAK,UAA8B;EAClC,OAAO,KAAK,IACX,IAAI,YAOF,CAAC,CAAC,OAAO,KAAK,MAAM,CACvB,CAAC,CAAC,SAAS,QAAQ;CACpB;;;;;;;;;;;;;;;;;CAkBA,MAAiC,MAAkB;EAClD,OAAO,KAAK,IACX,IAAI,aAOF,CAAC,CAAC,OAAO,KAAK,MAAM,CACvB,CAAC,CAAC,MAAM,IAAI;CACb;;;;;;;;;;;;;;;;;;CAmBA,QAAoC,QAAoC;EACvE,MAAM,QAAQ,KAAK,IAClB,IAAI,kBAQF,CAAC,CAAC,OAAO,KAAK,MAAM,CACvB;EACA,OAAO,IAAI,wBAOT,OAAO,OAAO,EAAE;CACnB;;;;;;;;;;;CAYA,MACC,OACC;EACD,OAAO,KAAK,IACX,IAAI,kBAQF,CAAC,CAAC,OAAO,KAAK,MAAM,CACvB,CAAC,CAAC,MAAM,KAAK;CACd;;;;;;;;;;;;CAaA,MAA0C,QAAW;EACpD,OAAO,KAAK,UAAU,MAAM,MAAM;CACnC;CAEA,OAAmC,QAAW;EAC7C,OAAO,KAAK,UAAU,OAAO,MAAM;CACpC;CAEA,QAAQ,IAAY;EACnB,OAAO,KAAK,UAAU,QAAQ,EAAE;CACjC;CAEA,WAAW,IAAY;EACtB,OAAO,KAAK,UAAU,WAAW,EAAE;CACpC;CAEA,UAAiD,YAAe;EAC/D,OAAO,KAAK,UAAU,UAAU,UAAU;CAC3C;CAEA,SAA8B,UAAa;EAC1C,OAAO,KAAK,UAAU,SAAS,QAAQ;CACxC;;;;;;CAOA,OACC,GAAG,MAKF;EACD,OAAO,KAAK,mBAAmB,CAAC,CAAC,OAAO,GAAG,IAAI;CAChD;;CAGA,qBAA6B;EAC5B,OAAO,KAAK;CACb;;CAGA,IAAY,YAAY;EACvB,OAAO,KAAK,IACX,IAAI,gBASF,CAAC,CAAC,OAAO,KAAK,MAAM,CACvB;CACD;;;;;;;CAQA,MACC,YACiD;EACjD,OAAO,IAAI,OACV,KAAK,IACL,KAAK,QACL,CAAC,GAAG,KAAK,cAAc,GAAG,WAAW,IAAI,MAAM,CAAC,GAChD,KAAK,aACN;CACD;;;;;;;;;;;;CAaA,IAME,SAAe;EAChB,MAAM,QAAQ,KAAK,gBACf,QAAQ,SAAS,KAAK,aAAa,IACpC;EAEH,MAAM,SAAS,KAAK;EACpB,MAAM,iBAAiB,KAAK;EAE5B,OAAO;CACR;CAEA,UAAyB;EACxB,OAAO,CACN;GACC,MAAM;GACN,IAAI,KAAK;GACT,GAAI,KAAK,aAAa,SACnB,EAAE,cAAc,KAAK,aAAa,IAClC,CAAC;GACJ,GAAI,KAAK,OAAO,YACb,EAAE,WAAW,KAAK,OAAO,UAAU,GAAG,IACtC,CAAC;GAEJ,UAAU,CAAC;EACZ,CACD;CACD;AACD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geekmidas/constructs",
3
- "version": "10.0.0-alpha.123",
3
+ "version": "10.0.0-alpha.125",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "exports": {
@@ -144,6 +144,16 @@
144
144
  "default": "./dist/file-server.cjs"
145
145
  }
146
146
  },
147
+ "./processors": {
148
+ "import": {
149
+ "types": "./dist/processors/index.d.mts",
150
+ "default": "./dist/processors/index.mjs"
151
+ },
152
+ "require": {
153
+ "types": "./dist/processors/index.d.cts",
154
+ "default": "./dist/processors/index.cjs"
155
+ }
156
+ },
147
157
  "./email": {
148
158
  "import": {
149
159
  "types": "./dist/email.d.mts",
@@ -307,13 +317,13 @@
307
317
  "lodash.uniqby": "~4.7.0",
308
318
  "openapi-types": "~12.1.3",
309
319
  "qs": "~6.16.0",
310
- "@geekmidas/envkit": "^10.0.0-alpha.123",
311
- "@geekmidas/errors": "^10.0.0-alpha.123",
312
- "@geekmidas/events": "^10.0.0-alpha.123",
313
- "@geekmidas/logger": "^10.0.0-alpha.123",
314
- "@geekmidas/manifest": "^10.0.0-alpha.123",
315
- "@geekmidas/schema": "^10.0.0-alpha.123",
316
- "@geekmidas/services": "^10.0.0-alpha.123"
320
+ "@geekmidas/envkit": "^10.0.0-alpha.125",
321
+ "@geekmidas/errors": "^10.0.0-alpha.125",
322
+ "@geekmidas/events": "^10.0.0-alpha.125",
323
+ "@geekmidas/logger": "^10.0.0-alpha.125",
324
+ "@geekmidas/manifest": "^10.0.0-alpha.125",
325
+ "@geekmidas/schema": "^10.0.0-alpha.125",
326
+ "@geekmidas/services": "^10.0.0-alpha.125"
317
327
  },
318
328
  "devDependencies": {
319
329
  "@aws-sdk/client-kms": "~3.1136.0",
@@ -331,20 +341,20 @@
331
341
  "msw": "~2.15.0",
332
342
  "pg": "~8.23.0",
333
343
  "zod": "~4.6.5",
334
- "@geekmidas/audit": "^10.0.0-alpha.123",
335
- "@geekmidas/auth": "10.0.0-alpha.123",
336
- "@geekmidas/cache": "^10.0.0-alpha.123",
337
- "@geekmidas/db": "^10.0.0-alpha.123",
338
- "@geekmidas/emailkit": "^10.0.0-alpha.123",
339
- "@geekmidas/envkit": "^10.0.0-alpha.123",
340
- "@geekmidas/errors": "^10.0.0-alpha.123",
341
- "@geekmidas/events": "^10.0.0-alpha.123",
342
- "@geekmidas/logger": "^10.0.0-alpha.123",
343
- "@geekmidas/rate-limit": "^10.0.0-alpha.123",
344
- "@geekmidas/schema": "^10.0.0-alpha.123",
345
- "@geekmidas/services": "^10.0.0-alpha.123",
346
- "@geekmidas/telescope": "10.0.0-alpha.123",
347
- "@geekmidas/testkit": "^10.0.0-alpha.123"
344
+ "@geekmidas/audit": "^10.0.0-alpha.125",
345
+ "@geekmidas/auth": "10.0.0-alpha.125",
346
+ "@geekmidas/cache": "^10.0.0-alpha.125",
347
+ "@geekmidas/db": "^10.0.0-alpha.125",
348
+ "@geekmidas/emailkit": "^10.0.0-alpha.125",
349
+ "@geekmidas/envkit": "^10.0.0-alpha.125",
350
+ "@geekmidas/errors": "^10.0.0-alpha.125",
351
+ "@geekmidas/events": "^10.0.0-alpha.125",
352
+ "@geekmidas/logger": "^10.0.0-alpha.125",
353
+ "@geekmidas/rate-limit": "^10.0.0-alpha.125",
354
+ "@geekmidas/schema": "^10.0.0-alpha.125",
355
+ "@geekmidas/services": "^10.0.0-alpha.125",
356
+ "@geekmidas/telescope": "10.0.0-alpha.125",
357
+ "@geekmidas/testkit": "^10.0.0-alpha.125"
348
358
  },
349
359
  "peerDependencies": {
350
360
  "@aws-sdk/client-kms": ">=3.1136.0",
@@ -355,15 +365,15 @@
355
365
  "kysely": "~0.29.6",
356
366
  "msw": ">=2.15.0",
357
367
  "pg": ">=8.23.0",
358
- "@geekmidas/audit": "^10.0.0-alpha.123",
359
- "@geekmidas/auth": "10.0.0-alpha.123",
360
- "@geekmidas/cache": "^10.0.0-alpha.123",
361
- "@geekmidas/db": "^10.0.0-alpha.123",
362
- "@geekmidas/emailkit": "^10.0.0-alpha.123",
363
- "@geekmidas/rate-limit": "^10.0.0-alpha.123",
364
- "@geekmidas/storage": "^10.0.0-alpha.123",
365
- "@geekmidas/telescope": "10.0.0-alpha.123",
366
- "@geekmidas/testkit": "^10.0.0-alpha.123"
368
+ "@geekmidas/audit": "^10.0.0-alpha.125",
369
+ "@geekmidas/auth": "10.0.0-alpha.125",
370
+ "@geekmidas/cache": "^10.0.0-alpha.125",
371
+ "@geekmidas/db": "^10.0.0-alpha.125",
372
+ "@geekmidas/emailkit": "^10.0.0-alpha.125",
373
+ "@geekmidas/rate-limit": "^10.0.0-alpha.125",
374
+ "@geekmidas/storage": "^10.0.0-alpha.125",
375
+ "@geekmidas/telescope": "10.0.0-alpha.125",
376
+ "@geekmidas/testkit": "^10.0.0-alpha.125"
367
377
  },
368
378
  "peerDependenciesMeta": {
369
379
  "@aws-sdk/client-kms": {
@@ -1,67 +0,0 @@
1
- import { n as StandardSchemaV1 } from "./index-DdRjYhiJ.cjs";
2
- import { c as Topic, l as TopicEvents, u as TopicMessage } from "./Construct-CFW0zjdD.cjs";
3
- import { c as ServicesOf, r as Consumable } from "./construct-interface-B1AfqRl4.cjs";
4
- import { n as SubscriberHandler, t as Subscriber } from "./Subscriber-CEt7kHkc.cjs";
5
- import { Logger } from "@geekmidas/logger";
6
- import { Service } from "@geekmidas/services";
7
- import { EventPublisher, ExtractPublisherMessage } from "@geekmidas/events";
8
- //#region src/subscribers/SubscriberBuilder.d.ts
9
- declare class SubscriberBuilder<TServices extends Service[] = [], TLogger extends Logger = Logger, OutSchema extends StandardSchemaV1 | undefined = undefined, TEventPublisher extends EventPublisher<any> | undefined = undefined, TSubscribedEvents extends any[] = [], TDatabase = undefined, TDatabaseServiceName extends string = string> {
10
- private _subscribedEvents;
11
- private _timeout?;
12
- private outputSchema?;
13
- private _services;
14
- /** The construct ids `.dependsOn()` named — what the manifest records. */
15
- _constructs: string[];
16
- private _logger;
17
- /**
18
- * The deploy unit that runs this, by construct id. Seeded by the factory a
19
- * `Worker` hands out, so a subscriber is not owned by its directory.
20
- */
21
- _owner?: string;
22
- private _topicName?;
23
- /** What the handler's `db` comes from — the worker's, unless overridden. */
24
- private _databaseService?;
25
- /** The edge `.database()` added, so the next one can replace it. */
26
- private _databaseEdge?;
27
- constructor();
28
- /**
29
- * Bind this subscriber to a {@link Topic}: the subscribable event types (and
30
- * their payloads) come from the topic's contract, and the binding is recorded
31
- * for the manifest, so infra wires the SNS subscription.
32
- *
33
- * A consumer does not publish, so binding a topic does *not* hand it the
34
- * topic's publisher connection string (least privilege). One that publishes
35
- * follow-up events depends on the topic it publishes to: `.dependsOn([orders])`.
36
- */
37
- topic<TName extends string, TEvents extends TopicEvents>(topic: Topic<TName, TEvents>): SubscriberBuilder<TServices, TLogger, OutSchema, EventPublisher<TopicMessage<TEvents>>, TSubscribedEvents, TDatabase, TDatabaseServiceName>;
38
- timeout(timeout: number): this;
39
- output<T extends StandardSchemaV1>(schema: T): SubscriberBuilder<TServices, TLogger, T, TEventPublisher, TSubscribedEvents, TDatabase, TDatabaseServiceName>;
40
- /**
41
- * Depend on constructs — a database, a bucket, a mail sender, a queue, a
42
- * topic.
43
- *
44
- * It records the edge and dissolves each construct's client into the
45
- * handler's service record under the construct's own id, so
46
- * `.dependsOn([uploads])` is what makes `services.uploads` exist and type.
47
- *
48
- * Constructs only. A `Service` does not match the shape, which is what keeps
49
- * env sniffing confined to `.services()` and the explicit lift.
50
- */
51
- dependsOn<const T extends readonly Consumable[]>(constructs: T): SubscriberBuilder<[...TServices, ...ServicesOf<T>], TLogger, OutSchema, TEventPublisher, TSubscribedEvents, TDatabase, TDatabaseServiceName>;
52
- services<T extends Service[]>(services: T): SubscriberBuilder<[...TServices, ...T], TLogger, OutSchema, TEventPublisher, TSubscribedEvents, TDatabase, TDatabaseServiceName>;
53
- logger<T extends Logger>(logger: T): SubscriberBuilder<TServices, T, OutSchema, TEventPublisher, TSubscribedEvents, TDatabase, TDatabaseServiceName>;
54
- /**
55
- * The database the handler receives as `db`.
56
- *
57
- * A subscriber built from a worker already has the worker's, when the
58
- * worker named one with `.database(db)`; this replaces it — the edge as well
59
- * as the client — for a consumer that works against a different database.
60
- */
61
- database<T, TDbName extends string>(source: Consumable<TDbName, T> | Service<TDbName, T>): SubscriberBuilder<TServices, TLogger, OutSchema, TEventPublisher, TSubscribedEvents, T, TDbName>;
62
- subscribe<TEvent extends TEventPublisher extends EventPublisher<any> ? ExtractPublisherMessage<TEventPublisher>['type'] | ExtractPublisherMessage<TEventPublisher>['type'][] : never>(event: TEvent): SubscriberBuilder<TServices, TLogger, OutSchema, TEventPublisher, TEvent extends any[] ? [...TSubscribedEvents, ...TEvent] : [...TSubscribedEvents, TEvent], TDatabase, TDatabaseServiceName>;
63
- handle(fn: SubscriberHandler<TEventPublisher, TSubscribedEvents, TServices, TLogger, OutSchema, TDatabase>): Subscriber<TServices, TLogger, OutSchema, TEventPublisher, TSubscribedEvents, TDatabase, TDatabaseServiceName>;
64
- }
65
- //#endregion
66
- export { SubscriberBuilder as t };
67
- //# sourceMappingURL=SubscriberBuilder-BRa_nrmH.d.cts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"SubscriberBuilder-BRa_nrmH.d.cts","names":[],"sources":["../src/subscribers/SubscriberBuilder.ts"],"mappings":";;;;;;;;cAoBa,kBACZ,kBAAkB,gBAClB,gBAAgB,SAAS,QACzB,kBAAkB,0CAClB,wBAAwB,6CACxB,sCACA,uBACA;UAEQ;UACA;UACA;UACA;;EAED;UACC;;;;;EAKD;UACC;;UAEA;;UAEA;EAER;;;;;;;;;;EAaA,MAAM,sBAAsB,gBAAgB,aAC3C,OAAO,MAAM,OAAO,WAClB,kBACF,WACA,SACA,WACA,eAAe,aAAa,WAC5B,mBACA,WACA;EAeD,QAAQ;EAIR,OAAO,UAAU,kBAChB,QAAQ,IACN,kBACF,WACA,SACA,GACA,iBACA,mBACA,WACA;;;;;;;;;;;;EAkBD,gBAAgB,mBAAmB,cAClC,YAAY,IACV,sBACE,cAAc,WAAW,KAC7B,SACA,WACA,iBACA,mBACA,WACA;EAyBD,SAAS,UAAU,WAClB,UAAU,IACR,sBACE,cAAc,IAClB,SACA,WACA,iBACA,mBACA,WACA;EAOD,OAAO,UAAU,QAChB,QAAQ,IACN,kBACF,WACA,GACA,WACA,iBACA,mBACA,WACA;;;;;;;;EAYD,SAAS,GAAG,wBACX,QAAQ,WAAW,SAAS,KAAK,QAAQ,SAAS,KAChD,kBACF,WACA,SACA,WACA,iBACA,mBACA,GACA;EAwBD,UACC,eAAe,wBAAwB,sBAElC,wBAAwB,2BACxB,wBAAwB,oCAG7B,OAAO,SACL,kBACF,WACA,SACA,WACA,iBACA,2BACO,sBAAsB,cACtB,mBAAmB,SAC1B,WACA;EAQD,OACC,IAAI,kBACH,iBACA,mBACA,WACA,SACA,WACA,aAEC,WACF,WACA,SACA,WACA,iBACA,mBACA,WACA"}
@@ -1,67 +0,0 @@
1
- import { n as StandardSchemaV1 } from "./index-DdRjYhiJ.mjs";
2
- import { c as Topic, l as TopicEvents, u as TopicMessage } from "./Construct-B7AXn0xH.mjs";
3
- import { c as ServicesOf, r as Consumable } from "./construct-interface-ZvYQbivO.mjs";
4
- import { n as SubscriberHandler, t as Subscriber } from "./Subscriber-dsxc5LKa.mjs";
5
- import { EventPublisher, ExtractPublisherMessage } from "@geekmidas/events";
6
- import { Service } from "@geekmidas/services";
7
- import { Logger } from "@geekmidas/logger";
8
- //#region src/subscribers/SubscriberBuilder.d.ts
9
- declare class SubscriberBuilder<TServices extends Service[] = [], TLogger extends Logger = Logger, OutSchema extends StandardSchemaV1 | undefined = undefined, TEventPublisher extends EventPublisher<any> | undefined = undefined, TSubscribedEvents extends any[] = [], TDatabase = undefined, TDatabaseServiceName extends string = string> {
10
- private _subscribedEvents;
11
- private _timeout?;
12
- private outputSchema?;
13
- private _services;
14
- /** The construct ids `.dependsOn()` named — what the manifest records. */
15
- _constructs: string[];
16
- private _logger;
17
- /**
18
- * The deploy unit that runs this, by construct id. Seeded by the factory a
19
- * `Worker` hands out, so a subscriber is not owned by its directory.
20
- */
21
- _owner?: string;
22
- private _topicName?;
23
- /** What the handler's `db` comes from — the worker's, unless overridden. */
24
- private _databaseService?;
25
- /** The edge `.database()` added, so the next one can replace it. */
26
- private _databaseEdge?;
27
- constructor();
28
- /**
29
- * Bind this subscriber to a {@link Topic}: the subscribable event types (and
30
- * their payloads) come from the topic's contract, and the binding is recorded
31
- * for the manifest, so infra wires the SNS subscription.
32
- *
33
- * A consumer does not publish, so binding a topic does *not* hand it the
34
- * topic's publisher connection string (least privilege). One that publishes
35
- * follow-up events depends on the topic it publishes to: `.dependsOn([orders])`.
36
- */
37
- topic<TName extends string, TEvents extends TopicEvents>(topic: Topic<TName, TEvents>): SubscriberBuilder<TServices, TLogger, OutSchema, EventPublisher<TopicMessage<TEvents>>, TSubscribedEvents, TDatabase, TDatabaseServiceName>;
38
- timeout(timeout: number): this;
39
- output<T extends StandardSchemaV1>(schema: T): SubscriberBuilder<TServices, TLogger, T, TEventPublisher, TSubscribedEvents, TDatabase, TDatabaseServiceName>;
40
- /**
41
- * Depend on constructs — a database, a bucket, a mail sender, a queue, a
42
- * topic.
43
- *
44
- * It records the edge and dissolves each construct's client into the
45
- * handler's service record under the construct's own id, so
46
- * `.dependsOn([uploads])` is what makes `services.uploads` exist and type.
47
- *
48
- * Constructs only. A `Service` does not match the shape, which is what keeps
49
- * env sniffing confined to `.services()` and the explicit lift.
50
- */
51
- dependsOn<const T extends readonly Consumable[]>(constructs: T): SubscriberBuilder<[...TServices, ...ServicesOf<T>], TLogger, OutSchema, TEventPublisher, TSubscribedEvents, TDatabase, TDatabaseServiceName>;
52
- services<T extends Service[]>(services: T): SubscriberBuilder<[...TServices, ...T], TLogger, OutSchema, TEventPublisher, TSubscribedEvents, TDatabase, TDatabaseServiceName>;
53
- logger<T extends Logger>(logger: T): SubscriberBuilder<TServices, T, OutSchema, TEventPublisher, TSubscribedEvents, TDatabase, TDatabaseServiceName>;
54
- /**
55
- * The database the handler receives as `db`.
56
- *
57
- * A subscriber built from a worker already has the worker's, when the
58
- * worker named one with `.database(db)`; this replaces it — the edge as well
59
- * as the client — for a consumer that works against a different database.
60
- */
61
- database<T, TDbName extends string>(source: Consumable<TDbName, T> | Service<TDbName, T>): SubscriberBuilder<TServices, TLogger, OutSchema, TEventPublisher, TSubscribedEvents, T, TDbName>;
62
- subscribe<TEvent extends TEventPublisher extends EventPublisher<any> ? ExtractPublisherMessage<TEventPublisher>['type'] | ExtractPublisherMessage<TEventPublisher>['type'][] : never>(event: TEvent): SubscriberBuilder<TServices, TLogger, OutSchema, TEventPublisher, TEvent extends any[] ? [...TSubscribedEvents, ...TEvent] : [...TSubscribedEvents, TEvent], TDatabase, TDatabaseServiceName>;
63
- handle(fn: SubscriberHandler<TEventPublisher, TSubscribedEvents, TServices, TLogger, OutSchema, TDatabase>): Subscriber<TServices, TLogger, OutSchema, TEventPublisher, TSubscribedEvents, TDatabase, TDatabaseServiceName>;
64
- }
65
- //#endregion
66
- export { SubscriberBuilder as t };
67
- //# sourceMappingURL=SubscriberBuilder-D9bkrYzH.d.mts.map