@geekmidas/constructs 10.0.0-alpha.70 → 10.0.0-alpha.71

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 (123) hide show
  1. package/dist/{Construct-BO1X-kt9.mjs → Construct-B1vGBPme.mjs} +2 -2
  2. package/dist/{Construct-BO1X-kt9.mjs.map → Construct-B1vGBPme.mjs.map} +1 -1
  3. package/dist/{Construct-DmvXceVY.cjs → Construct-DUSXuTHP.cjs} +2 -2
  4. package/dist/{Construct-DmvXceVY.cjs.map → Construct-DUSXuTHP.cjs.map} +1 -1
  5. package/dist/{CronBuilder-BoHzlWU_.cjs → CronBuilder-C0Ek1-RA.cjs} +3 -3
  6. package/dist/{CronBuilder-BoHzlWU_.cjs.map → CronBuilder-C0Ek1-RA.cjs.map} +1 -1
  7. package/dist/{CronBuilder-CCgt0wCE.mjs → CronBuilder-Dmix_Pen.mjs} +3 -3
  8. package/dist/{CronBuilder-CCgt0wCE.mjs.map → CronBuilder-Dmix_Pen.mjs.map} +1 -1
  9. package/dist/{Endpoint-DfiXTm_v.cjs → Endpoint-Ba1PiyuJ.cjs} +3 -3
  10. package/dist/{Endpoint-DfiXTm_v.cjs.map → Endpoint-Ba1PiyuJ.cjs.map} +1 -1
  11. package/dist/{Endpoint-DV3jK5sP.d.cts → Endpoint-DJZkADYh.d.cts} +2 -2
  12. package/dist/{Endpoint-BJDBZBZu.d.mts.map → Endpoint-DJZkADYh.d.cts.map} +1 -1
  13. package/dist/{Endpoint-CMWWrQ7u.mjs → Endpoint-ZJhv68qq.mjs} +3 -3
  14. package/dist/{Endpoint-CMWWrQ7u.mjs.map → Endpoint-ZJhv68qq.mjs.map} +1 -1
  15. package/dist/{Endpoint-BJDBZBZu.d.mts → Endpoint-hcHSFhda.d.mts} +3 -3
  16. package/dist/{Endpoint-DV3jK5sP.d.cts.map → Endpoint-hcHSFhda.d.mts.map} +1 -1
  17. package/dist/{EndpointFactory-gaF4yRpP.d.mts → EndpointFactory-B5siFRcO.d.mts} +3 -3
  18. package/dist/{EndpointFactory-gaF4yRpP.d.mts.map → EndpointFactory-B5siFRcO.d.mts.map} +1 -1
  19. package/dist/{EndpointFactory-CLxrcS0O.cjs → EndpointFactory-B_x2fXWQ.cjs} +4 -4
  20. package/dist/{EndpointFactory-CLxrcS0O.cjs.map → EndpointFactory-B_x2fXWQ.cjs.map} +1 -1
  21. package/dist/{EndpointFactory-CLzCLkLt.d.cts → EndpointFactory-BkYuVg8W.d.cts} +2 -2
  22. package/dist/{EndpointFactory-CLzCLkLt.d.cts.map → EndpointFactory-BkYuVg8W.d.cts.map} +1 -1
  23. package/dist/{EndpointFactory-7g4Dg6cW.mjs → EndpointFactory-C3QvXMk6.mjs} +4 -4
  24. package/dist/{EndpointFactory-7g4Dg6cW.mjs.map → EndpointFactory-C3QvXMk6.mjs.map} +1 -1
  25. package/dist/{HonoEndpointAdaptor-Df5-cIJU.cjs → HonoEndpointAdaptor-0yLvYunI.cjs} +39 -16
  26. package/dist/HonoEndpointAdaptor-0yLvYunI.cjs.map +1 -0
  27. package/dist/{HonoEndpointAdaptor-9Byxn0TX.mjs → HonoEndpointAdaptor-chgcjPmg.mjs} +39 -16
  28. package/dist/HonoEndpointAdaptor-chgcjPmg.mjs.map +1 -0
  29. package/dist/{Queue-hwPN-t31.mjs → Queue-CLRT_j5a.mjs} +2 -2
  30. package/dist/{Queue-hwPN-t31.mjs.map → Queue-CLRT_j5a.mjs.map} +1 -1
  31. package/dist/{Queue-D5jcw0mu.cjs → Queue-umpX3683.cjs} +2 -2
  32. package/dist/{Queue-D5jcw0mu.cjs.map → Queue-umpX3683.cjs.map} +1 -1
  33. package/dist/{QueueBuilder-DCuYdjMw.mjs → QueueBuilder-B0mCqq74.mjs} +2 -2
  34. package/dist/{QueueBuilder-DCuYdjMw.mjs.map → QueueBuilder-B0mCqq74.mjs.map} +1 -1
  35. package/dist/{QueueBuilder-BFQn3OzZ.cjs → QueueBuilder-B9ClmXjX.cjs} +2 -2
  36. package/dist/{QueueBuilder-BFQn3OzZ.cjs.map → QueueBuilder-B9ClmXjX.cjs.map} +1 -1
  37. package/dist/{SubscriberBuilder-DMFk_Ap1.mjs → SubscriberBuilder-CDy4nHCz.mjs} +2 -2
  38. package/dist/{SubscriberBuilder-DMFk_Ap1.mjs.map → SubscriberBuilder-CDy4nHCz.mjs.map} +1 -1
  39. package/dist/{SubscriberBuilder-C2PwqwIl.cjs → SubscriberBuilder-D_owVoHo.cjs} +2 -2
  40. package/dist/{SubscriberBuilder-C2PwqwIl.cjs.map → SubscriberBuilder-D_owVoHo.cjs.map} +1 -1
  41. package/dist/{Topic-CkxOTrMv.cjs → Topic-BliAKi5r.cjs} +2 -2
  42. package/dist/{Topic-CkxOTrMv.cjs.map → Topic-BliAKi5r.cjs.map} +1 -1
  43. package/dist/{Topic-hPffOsot.mjs → Topic-DCxIstf9.mjs} +2 -2
  44. package/dist/{Topic-hPffOsot.mjs.map → Topic-DCxIstf9.mjs.map} +1 -1
  45. package/dist/adaptors/aws.cjs +8 -8
  46. package/dist/adaptors/aws.cjs.map +1 -1
  47. package/dist/adaptors/aws.d.cts +1 -1
  48. package/dist/adaptors/aws.d.mts +2 -2
  49. package/dist/adaptors/aws.mjs +9 -9
  50. package/dist/adaptors/aws.mjs.map +1 -1
  51. package/dist/adaptors/hono.cjs +1 -1
  52. package/dist/adaptors/hono.d.cts +1 -1
  53. package/dist/adaptors/hono.d.cts.map +1 -1
  54. package/dist/adaptors/hono.d.mts +1 -1
  55. package/dist/adaptors/hono.d.mts.map +1 -1
  56. package/dist/adaptors/hono.mjs +1 -1
  57. package/dist/adaptors/testing.cjs +7 -7
  58. package/dist/adaptors/testing.d.cts +1 -1
  59. package/dist/adaptors/testing.d.mts +1 -1
  60. package/dist/adaptors/testing.mjs +7 -7
  61. package/dist/{auth-CIFXNuDk.d.cts → auth-CuCozjsh.d.cts} +62 -7
  62. package/dist/auth-CuCozjsh.d.cts.map +1 -0
  63. package/dist/{auth-CJ9Ff6Lu.d.mts → auth-XtDcvQxV.d.mts} +63 -8
  64. package/dist/auth-XtDcvQxV.d.mts.map +1 -0
  65. package/dist/auth.cjs +78 -11
  66. package/dist/auth.cjs.map +1 -1
  67. package/dist/auth.d.cts +2 -2
  68. package/dist/auth.d.mts +2 -2
  69. package/dist/auth.mjs +77 -11
  70. package/dist/auth.mjs.map +1 -1
  71. package/dist/crons/index.cjs +2 -2
  72. package/dist/crons/index.mjs +2 -2
  73. package/dist/database/kysely.cjs +1 -1
  74. package/dist/database/kysely.mjs +1 -1
  75. package/dist/endpoints/index.cjs +3 -3
  76. package/dist/endpoints/index.d.cts +2 -2
  77. package/dist/endpoints/index.d.mts +2 -2
  78. package/dist/endpoints/index.mjs +3 -3
  79. package/dist/functions/index.cjs +1 -1
  80. package/dist/functions/index.mjs +1 -1
  81. package/dist/{functions-Di6n5ltG.mjs → functions-BFizikJU.mjs} +2 -2
  82. package/dist/{functions-Di6n5ltG.mjs.map → functions-BFizikJU.mjs.map} +1 -1
  83. package/dist/{functions-CbP5ZKma.cjs → functions-CfzJG9hK.cjs} +2 -2
  84. package/dist/{functions-CbP5ZKma.cjs.map → functions-CfzJG9hK.cjs.map} +1 -1
  85. package/dist/index.cjs +1 -1
  86. package/dist/index.mjs +1 -1
  87. package/dist/{kysely-BRzzawr2.cjs → kysely-B-xWOGmn.cjs} +2 -2
  88. package/dist/kysely-B-xWOGmn.cjs.map +1 -0
  89. package/dist/{kysely-BYfM6Zby.mjs → kysely-BhJ3lLdE.mjs} +2 -2
  90. package/dist/kysely-BhJ3lLdE.mjs.map +1 -0
  91. package/dist/{processAudits-BpRlPQMW.mjs → processAudits-BRRVVHUB.mjs} +57 -3
  92. package/dist/processAudits-BRRVVHUB.mjs.map +1 -0
  93. package/dist/{processAudits-D_2W5x8k.cjs → processAudits-DasOZjPl.cjs} +69 -3
  94. package/dist/processAudits-DasOZjPl.cjs.map +1 -0
  95. package/dist/queue/index.cjs +2 -2
  96. package/dist/queue/index.mjs +2 -2
  97. package/dist/rest-api.cjs +1 -1
  98. package/dist/rest-api.d.cts +2 -2
  99. package/dist/rest-api.d.mts +2 -2
  100. package/dist/rest-api.mjs +1 -1
  101. package/dist/{runSubscriber-BYEY_bfA.mjs → runSubscriber-BHvJ8POr.mjs} +2 -2
  102. package/dist/runSubscriber-BHvJ8POr.mjs.map +1 -0
  103. package/dist/{runSubscriber-BRp9a_qi.cjs → runSubscriber-D8nLXIv1.cjs} +2 -2
  104. package/dist/runSubscriber-D8nLXIv1.cjs.map +1 -0
  105. package/dist/subscribers/index.cjs +3 -3
  106. package/dist/subscribers/index.cjs.map +1 -1
  107. package/dist/subscribers/index.mjs +3 -3
  108. package/dist/subscribers/index.mjs.map +1 -1
  109. package/dist/topic/index.cjs +1 -1
  110. package/dist/topic/index.mjs +1 -1
  111. package/dist/worker.cjs +4 -4
  112. package/dist/worker.mjs +4 -4
  113. package/package.json +31 -31
  114. package/dist/HonoEndpointAdaptor-9Byxn0TX.mjs.map +0 -1
  115. package/dist/HonoEndpointAdaptor-Df5-cIJU.cjs.map +0 -1
  116. package/dist/auth-CIFXNuDk.d.cts.map +0 -1
  117. package/dist/auth-CJ9Ff6Lu.d.mts.map +0 -1
  118. package/dist/kysely-BRzzawr2.cjs.map +0 -1
  119. package/dist/kysely-BYfM6Zby.mjs.map +0 -1
  120. package/dist/processAudits-BpRlPQMW.mjs.map +0 -1
  121. package/dist/processAudits-D_2W5x8k.cjs.map +0 -1
  122. package/dist/runSubscriber-BRp9a_qi.cjs.map +0 -1
  123. package/dist/runSubscriber-BYEY_bfA.mjs.map +0 -1
@@ -1,12 +1,10 @@
1
- import { i as publishConstructEvents } from "./functions-Di6n5ltG.mjs";
2
- import { n as ResponseBuilder, t as Endpoint } from "./Endpoint-CMWWrQ7u.mjs";
1
+ import { i as publishConstructEvents } from "./functions-BFizikJU.mjs";
2
+ import { n as ResponseBuilder, t as Endpoint } from "./Endpoint-ZJhv68qq.mjs";
3
3
  import { a as createHonoHeaders, i as createHonoCookies } from "./lazyAccessors-CFanua0O.mjs";
4
- import { n as executeWithAuditTransaction, t as createAuditContext } from "./processAudits-BpRlPQMW.mjs";
4
+ import { i as loadRls, n as executeWithAuditTransaction, r as loadRateLimit, t as createAuditContext } from "./processAudits-BRRVVHUB.mjs";
5
5
  import { wrapError } from "@geekmidas/errors";
6
6
  import { ServiceDiscovery, runWithRequestContext } from "@geekmidas/services";
7
7
  import { setCookie } from "hono/cookie";
8
- import { withRlsContext } from "@geekmidas/db/rls";
9
- import { checkRateLimit, getRateLimitHeaders } from "@geekmidas/rate-limit";
10
8
  import { Hono } from "hono";
11
9
  import { logger } from "hono/logger";
12
10
  import { timing } from "hono/timing";
@@ -197,15 +195,15 @@ var HonoEndpoint = class HonoEndpoint {
197
195
  return c.json({ error: "Forbidden" }, 403);
198
196
  }
199
197
  if (features.hasRateLimit) {
200
- const rateLimitInfo = await checkRateLimit(endpoint.rateLimit, {
198
+ const { checkRateLimit, getRateLimitHeaders } = await loadRateLimit();
199
+ const rateLimitHeaders = getRateLimitHeaders(await checkRateLimit(endpoint.rateLimit, {
201
200
  header,
202
201
  services,
203
202
  logger,
204
203
  session,
205
204
  path: c.req.path,
206
205
  method: endpoint.method
207
- });
208
- const rateLimitHeaders = getRateLimitHeaders(rateLimitInfo, endpoint.rateLimit);
206
+ }), endpoint.rateLimit);
209
207
  for (const [key, value] of Object.entries(rateLimitHeaders)) if (value) c.header(key, value);
210
208
  }
211
209
  const auditContext = features.hasAudits ? await createAuditContext(endpoint, serviceDiscovery, logger, {
@@ -251,7 +249,10 @@ var HonoEndpoint = class HonoEndpoint {
251
249
  responseBuilder
252
250
  };
253
251
  };
254
- if (features.hasRls && rlsContext && baseDb) return withRlsContext(baseDb, rlsContext, async (trx) => executeHandler(trx), { prefix: endpoint.rlsConfig?.prefix });
252
+ if (features.hasRls && rlsContext && baseDb) {
253
+ const { withRlsContext } = await loadRls();
254
+ return withRlsContext(baseDb, rlsContext, async (trx) => executeHandler(trx), { prefix: endpoint.rlsConfig?.prefix });
255
+ }
255
256
  return executeHandler(baseDb);
256
257
  }, async (result, auditor) => {
257
258
  if (!audits?.length) return;
@@ -279,23 +280,25 @@ var HonoEndpoint = class HonoEndpoint {
279
280
  if (!(metadata.headers && hasContentTypeHeader(metadata.headers))) c.header("content-type", endpoint.responseType);
280
281
  return c.body(output, status);
281
282
  } catch (validationError) {
282
- logger.error(validationError, "Output validation failed");
283
+ logger.error({ err: validationError }, "Output validation failed");
283
284
  const error = wrapError(validationError, 422, "Response validation failed");
285
+ const body = errorBody(error);
284
286
  if (HonoEndpoint.isDev) logger.info({
285
287
  status: error.statusCode,
286
- body: error
288
+ body
287
289
  }, "Outgoing response");
288
- return c.json(error, error.statusCode);
290
+ return c.json(body, error.statusCode);
289
291
  }
290
292
  } catch (e) {
291
- logger.error(e, "Error processing endpoint request");
293
+ logger.error({ err: e }, "Error processing endpoint request");
292
294
  const error = wrapError(e, 500, "Internal Server Error");
293
295
  if (error.statusCode >= 500 && e instanceof Error) c.error = e;
296
+ const body = errorBody(error);
294
297
  if (HonoEndpoint.isDev) logger.info({
295
298
  status: error.statusCode,
296
- body: error
299
+ body
297
300
  }, "Outgoing response");
298
- return c.json(error, error.statusCode);
301
+ return c.json(body, error.statusCode);
299
302
  }
300
303
  });
301
304
  };
@@ -312,7 +315,27 @@ var HonoEndpoint = class HonoEndpoint {
312
315
  });
313
316
  }
314
317
  };
318
+ /**
319
+ * What a client is told about a failed request: what went wrong, and the stack
320
+ * only under `gkm dev`. A server's stack is its file layout and its
321
+ * dependencies' versions — for the person debugging it, not for whoever sent
322
+ * the request. A server failure's details are the error it wrapped (an
323
+ * environment variable it could not read, a query that failed), so they go to
324
+ * the log and not the response either; a 4xx's details are the client's.
325
+ */
326
+ function errorBody(error) {
327
+ const internal = error.statusCode >= 500 && !HonoEndpoint.isDev;
328
+ return {
329
+ name: error.name,
330
+ message: error.message,
331
+ statusCode: error.statusCode,
332
+ statusMessage: error.statusMessage,
333
+ code: error.code,
334
+ ...internal ? {} : { details: error.details },
335
+ ...HonoEndpoint.isDev ? { stack: error.stack } : {}
336
+ };
337
+ }
315
338
  //#endregion
316
339
  export { HonoEndpoint as t };
317
340
 
318
- //# sourceMappingURL=HonoEndpointAdaptor-9Byxn0TX.mjs.map
341
+ //# sourceMappingURL=HonoEndpointAdaptor-chgcjPmg.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HonoEndpointAdaptor-chgcjPmg.mjs","names":["honoLogger"],"sources":["../src/endpoints/helpers.ts","../src/endpoints/parseHonoQuery.ts","../src/endpoints/HonoEndpointAdaptor.ts"],"sourcesContent":["import path from 'node:path';\nimport type { Service } from '@geekmidas/services';\nimport fg from 'fast-glob';\nimport type { HttpMethod } from '../types';\nimport { Endpoint } from './Endpoint';\n\n// Re-export utility functions\n\n/**\n * Recursively finds the project root directory by looking for lock files.\n * Traverses up the directory tree until it finds a package manager lock file.\n *\n * @param cwd - The current working directory to start searching from\n * @returns Promise resolving to the absolute path of the project root\n *\n * @example\n * ```typescript\n * const projectRoot = await getProjectRoot(process.cwd());\n * console.log(`Project root: ${projectRoot}`);\n * // Output: Project root: /Users/user/my-project\n * ```\n */\nexport async function getProjectRoot(cwd: string): Promise<string> {\n\tif (cwd === '/') {\n\t\treturn cwd;\n\t}\n\n\tconst stream = fg.stream(\n\t\t['yarn.lock', 'pnpm-lock.yaml', 'package-lock.json', 'deno.lock'],\n\t\t{ dot: true, cwd },\n\t);\n\n\tlet isRoot = false;\n\n\tfor await (const _ of stream) {\n\t\tisRoot = true;\n\t\tbreak;\n\t}\n\n\tif (isRoot) {\n\t\treturn cwd;\n\t}\n\n\treturn getProjectRoot(path.resolve(cwd, '..'));\n}\n\n/**\n * Discovers and imports all Endpoint instances from the specified route patterns.\n * Uses fast-glob to find files matching the patterns and extracts exported Endpoints.\n *\n * @template TServices - Array of service types used by the endpoints\n * @param routes - Array of glob patterns to match route files (e.g., ['src/routes/*.ts'])\n * @param cwd - The current working directory to resolve paths from\n * @returns Promise resolving to an array of Endpoint instances found in the matched files\n *\n * @example\n * ```typescript\n * // Find all endpoints in the routes directory\n * const endpoints = await getEndpointsFromRoutes(\n * ['src/routes/**\\/*.ts'],\n * process.cwd()\n * );\n *\n * // Register endpoints with your server\n * for (const endpoint of endpoints) {\n * server.register(endpoint);\n * }\n * ```\n *\n * @remarks\n * - Only exports that are valid Endpoint instances are included\n * - Files are imported dynamically, so they must be valid ES modules\n * - The function filters out non-Endpoint exports automatically\n */\nexport async function getEndpointsFromRoutes<TServices extends Service[]>(\n\troutes: string[],\n\tcwd: string,\n): Promise<Endpoint<string, HttpMethod, any, any, TServices>[]> {\n\tconst stream = fg.stream(routes, { cwd });\n\n\tconst endpoints: Endpoint<string, HttpMethod, any, any, TServices>[] = [];\n\n\tfor await (const f of stream) {\n\t\t// Resolve the absolute path for the route file\n\t\tconst routePath = path.resolve(cwd, f.toString());\n\t\t// Dynamically import the route module\n\t\tconst route = await import(routePath);\n\n\t\t// Filter exported values to find only Endpoint instances\n\t\tconst handlers = Object.values(route).filter((value) => {\n\t\t\treturn Endpoint.isEndpoint(value);\n\t\t}) as unknown as Endpoint<string, HttpMethod, any, any, TServices>[];\n\n\t\tendpoints.push(...handlers);\n\t}\n\n\treturn endpoints;\n}\n","import type { Context } from 'hono';\nimport qs from 'qs';\n\n/**\n * Parse Hono query parameters using qs bracket notation\n * Supports nested objects, arrays, and deep nesting via standard bracket syntax\n */\nexport function parseHonoQuery(c: Context): Record<string, any> {\n\tconst url = c.req.url;\n\tconst queryIndex = url.indexOf('?');\n\tif (queryIndex === -1) {\n\t\treturn {};\n\t}\n\tconst queryString = url.slice(queryIndex + 1);\n\treturn qs.parse(queryString) as Record<string, any>;\n}\n","import type { AuditableAction, AuditStorage } from '@geekmidas/audit';\nimport type { EnvironmentParser } from '@geekmidas/envkit';\nimport { type HttpError, wrapError } from '@geekmidas/errors';\nimport type { EventPublisher } from '@geekmidas/events';\nimport type { Logger } from '@geekmidas/logger';\nimport {\n\trunWithRequestContext,\n\ttype Service,\n\tServiceDiscovery,\n\ttype ServiceRecord,\n} from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport { type Context, Hono } from 'hono';\nimport { setCookie } from 'hono/cookie';\nimport { logger as honoLogger } from 'hono/logger';\nimport { timing } from 'hono/timing';\nimport type { ContentfulStatusCode } from 'hono/utils/http-status';\nimport { validator } from 'hono/validator';\nimport { publishConstructEvents } from '../publisher';\nimport type { HttpMethod, LowerHttpMethod } from '../types';\nimport type { MappedAudit } from './audit';\nimport {\n\tEndpoint,\n\ttype EndpointContext,\n\ttype EndpointSchemas,\n\tResponseBuilder,\n} from './Endpoint';\nimport { getEndpointsFromRoutes } from './helpers';\nimport { createHonoCookies, createHonoHeaders } from './lazyAccessors';\nimport { loadRateLimit, loadRls } from './optionalPeers';\nimport { parseHonoQuery } from './parseHonoQuery';\nimport {\n\tcreateAuditContext,\n\texecuteWithAuditTransaction,\n} from './processAudits';\n\nexport interface HonoEndpointOptions {\n\t/**\n\t * Path where OpenAPI documentation will be served.\n\t * Set to false to disable docs route.\n\t * @default '/docs'\n\t */\n\tdocsPath?: string | false;\n\t/**\n\t * OpenAPI schema options\n\t */\n\topenApiOptions?: {\n\t\ttitle?: string;\n\t\tversion?: string;\n\t\tdescription?: string;\n\t};\n}\n\n/**\n * Feature flags for an endpoint, analyzed once at route registration time.\n * This avoids per-request feature detection overhead.\n */\ninterface EndpointFeatures {\n\thasServices: boolean;\n\thasDatabase: boolean;\n\thasBodyValidation: boolean;\n\thasQueryValidation: boolean;\n\thasParamValidation: boolean;\n\thasAudits: boolean;\n\thasEvents: boolean;\n\thasRateLimit: boolean;\n\thasRls: boolean;\n}\n\n/**\n * Case-insensitive check for a `content-type` header in a metadata headers map.\n */\nfunction hasContentTypeHeader(headers: Record<string, string>): boolean {\n\tfor (const key of Object.keys(headers)) {\n\t\tif (key.toLowerCase() === 'content-type') return true;\n\t}\n\treturn false;\n}\n\n/**\n * Analyze endpoint features at registration time (not per-request)\n */\nfunction analyzeEndpointFeatures(\n\tendpoint: Endpoint<\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany\n\t>,\n): EndpointFeatures {\n\treturn {\n\t\thasServices: endpoint.services.length > 0,\n\t\thasDatabase: !!endpoint.databaseService,\n\t\thasBodyValidation: !!endpoint.input?.body,\n\t\thasQueryValidation: !!endpoint.input?.query,\n\t\thasParamValidation: !!endpoint.input?.params,\n\t\t// Audit context needed if declarative audits OR auditor storage service configured (for manual audits)\n\t\thasAudits:\n\t\t\t(endpoint.audits?.length ?? 0) > 0 || !!endpoint.auditorStorageService,\n\t\thasEvents: (endpoint.events?.length ?? 0) > 0,\n\t\thasRateLimit: !!endpoint.rateLimit,\n\t\thasRls: !!endpoint.rlsConfig && !endpoint.rlsBypass,\n\t};\n}\n\nexport class HonoEndpoint<\n\tTRoute extends string,\n\tTMethod extends HttpMethod,\n\tTInput extends EndpointSchemas = {},\n\tTOutSchema extends StandardSchemaV1 | undefined = undefined,\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTSession = unknown,\n\tTAuditStorage extends AuditStorage | undefined = undefined,\n\tTAuditStorageServiceName extends string = string,\n\tTAuditAction extends AuditableAction<string, unknown> = AuditableAction<\n\t\tstring,\n\t\tunknown\n\t>,\n\tTDatabase = undefined,\n\tTDatabaseServiceName extends string = string,\n> {\n\tconstructor(\n\t\tprivate readonly endpoint: Endpoint<\n\t\t\tTRoute,\n\t\t\tTMethod,\n\t\t\tTInput,\n\t\t\tTOutSchema,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTSession,\n\t\t\tTAuditStorage,\n\t\t\tTAuditStorageServiceName,\n\t\t\tTAuditAction,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>,\n\t) {}\n\n\tstatic isDev = process.env.NODE_ENV === 'development';\n\n\tstatic async validate<T extends StandardSchemaV1>(\n\t\tc: Context<any, string, {}>,\n\t\tdata: unknown,\n\t\tschema?: T,\n\t) {\n\t\tif (!schema) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst parsed = await Endpoint.validate(schema, data);\n\n\t\tif (parsed.issues) {\n\t\t\treturn c.json(parsed.issues, 422);\n\t\t}\n\n\t\treturn parsed.value;\n\t}\n\taddRoute(\n\t\tserviceDiscovery: ServiceDiscovery<ServiceRecord<TServices>>,\n\t\tapp: Hono,\n\t): void {\n\t\tHonoEndpoint.addRoute(this.endpoint, serviceDiscovery, app);\n\t}\n\n\t/**\n\t * @deprecated Global event middleware is no longer used.\n\t * Events are now published per-route only for endpoints that have events configured.\n\t * This method is kept for backward compatibility but does nothing.\n\t */\n\tstatic applyEventMiddleware(\n\t\t_app: Hono,\n\t\t_serviceDiscovery: ServiceDiscovery<any>,\n\t) {\n\t\t// No-op: Event publishing is now handled per-route in addRoute\n\t\t// This avoids running middleware on every request including 404s\n\t}\n\n\tstatic async fromRoutes<TLogger extends Logger, TServices extends Service[]>(\n\t\troutes: string[],\n\t\tenvParser: EnvironmentParser<{}>,\n\t\tapp = new Hono(),\n\t\t_logger: TLogger,\n\t\tcwd = process.cwd(),\n\t\toptions?: HonoEndpointOptions,\n\t): Promise<Hono> {\n\t\tconst endpoints = await getEndpointsFromRoutes<TServices>(routes, cwd);\n\t\tconst serviceDiscovery =\n\t\t\tServiceDiscovery.getInstance<ServiceRecord<TServices>>(envParser);\n\n\t\tHonoEndpoint.addRoutes(endpoints, serviceDiscovery, app, options);\n\n\t\treturn app;\n\t}\n\n\tstatic addRoutes<\n\t\tTServices extends Service[] = [],\n\t\tTLogger extends Logger = Logger,\n\t>(\n\t\tendpoints: Endpoint<string, HttpMethod, any, any, TServices, TLogger>[],\n\t\tserviceDiscovery: ServiceDiscovery<ServiceRecord<TServices>>,\n\t\tapp: Hono,\n\t\toptions?: HonoEndpointOptions,\n\t): void {\n\t\t// Add timing middleware (always enabled)\n\t\tapp.use('*', timing());\n\n\t\t// Add logger middleware in development mode\n\n\t\tif (HonoEndpoint.isDev) {\n\t\t\tapp.use('*', honoLogger());\n\t\t}\n\n\t\t// Add docs route if not disabled\n\t\tconst docsPath =\n\t\t\toptions?.docsPath !== false ? options?.docsPath || '/docs' : null;\n\t\tif (docsPath) {\n\t\t\tHonoEndpoint.addDocsRoute(\n\t\t\t\tendpoints,\n\t\t\t\tapp,\n\t\t\t\tdocsPath,\n\t\t\t\toptions?.openApiOptions,\n\t\t\t);\n\t\t}\n\n\t\t// Sort endpoints to ensure static routes come before dynamic ones\n\t\tconst sortedEndpoints = endpoints.sort((a, b) => {\n\t\t\tconst aSegments = a.route.split('/');\n\t\t\tconst bSegments = b.route.split('/');\n\n\t\t\t// Compare each segment\n\t\t\tfor (let i = 0; i < Math.max(aSegments.length, bSegments.length); i++) {\n\t\t\t\tconst aSegment = aSegments[i] || '';\n\t\t\t\tconst bSegment = bSegments[i] || '';\n\n\t\t\t\t// If one is dynamic and the other is not, static comes first\n\t\t\t\tconst aIsDynamic = aSegment.startsWith(':');\n\t\t\t\tconst bIsDynamic = bSegment.startsWith(':');\n\n\t\t\t\tif (!aIsDynamic && bIsDynamic) return -1;\n\t\t\t\tif (aIsDynamic && !bIsDynamic) return 1;\n\n\t\t\t\t// If both are the same type, compare alphabetically\n\t\t\t\tif (aSegment !== bSegment) {\n\t\t\t\t\treturn aSegment.localeCompare(bSegment);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn 0;\n\t\t});\n\n\t\t// Note: Global event middleware removed for performance\n\t\t// Events are now published per-route only for endpoints with events configured\n\t\tfor (const endpoint of sortedEndpoints) {\n\t\t\tHonoEndpoint.addRoute(endpoint, serviceDiscovery, app);\n\t\t}\n\t}\n\n\t// biome-ignore lint/suspicious/useAdjacentOverloadSignatures: not an overload — the instance `addRoute` above registers this adaptor's own endpoint, this one registers any endpoint on a given app. They share a name because they do the same thing to different subjects.\n\tstatic addRoute<\n\t\tTRoute extends string,\n\t\tTMethod extends HttpMethod,\n\t\tTInput extends EndpointSchemas = {},\n\t\tTOutSchema extends StandardSchemaV1 | undefined = undefined,\n\t\tTServices extends Service[] = [],\n\t\tTLogger extends Logger = Logger,\n\t\tTSession = unknown,\n\t\tTAuditStorage extends AuditStorage | undefined = undefined,\n\t\tTAuditStorageServiceName extends string = string,\n\t\tTAuditAction extends AuditableAction<string, unknown> = AuditableAction<\n\t\t\tstring,\n\t\t\tunknown\n\t\t>,\n\t\tTDatabase = undefined,\n\t\tTDatabaseServiceName extends string = string,\n\t>(\n\t\tendpoint: Endpoint<\n\t\t\tTRoute,\n\t\t\tTMethod,\n\t\t\tTInput,\n\t\t\tTOutSchema,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTSession,\n\t\t\tTAuditStorage,\n\t\t\tTAuditStorageServiceName,\n\t\t\tTAuditAction,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>,\n\t\tserviceDiscovery: ServiceDiscovery<ServiceRecord<TServices>>,\n\t\tapp: Hono,\n\t): void {\n\t\tconst { route } = endpoint;\n\t\tconst method = endpoint.method.toLowerCase() as LowerHttpMethod<TMethod>;\n\n\t\t// Analyze endpoint features once at registration time (not per-request)\n\t\tconst features = analyzeEndpointFeatures(endpoint);\n\n\t\t// Build validators array - only add validators for schemas that exist\n\t\tconst validators: any[] = [];\n\n\t\tif (features.hasBodyValidation) {\n\t\t\tvalidators.push(\n\t\t\t\tvalidator('json', (value, c) =>\n\t\t\t\t\tHonoEndpoint.validate(c, value, endpoint.input?.body),\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\n\t\tif (features.hasQueryValidation) {\n\t\t\tvalidators.push(\n\t\t\t\tvalidator('query', (_, c) => {\n\t\t\t\t\tconst parsedQuery = parseHonoQuery(c);\n\t\t\t\t\treturn HonoEndpoint.validate(c, parsedQuery, endpoint.input?.query);\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tif (features.hasParamValidation) {\n\t\t\tvalidators.push(\n\t\t\t\tvalidator('param', (params, c) =>\n\t\t\t\t\tHonoEndpoint.validate(c, params, endpoint.input?.params),\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\n\t\t// Main handler\n\t\tconst handler = async (c: Context) => {\n\t\t\t// Request context setup\n\t\t\tconst startTime = Date.now();\n\t\t\tconst requestId = c.req.header('X-Request-ID') ?? crypto.randomUUID();\n\n\t\t\tconst logger = endpoint.logger.child({\n\t\t\t\trequestId,\n\t\t\t\tendpoint: endpoint.fullPath,\n\t\t\t\troute: endpoint.route,\n\t\t\t\thost: c.req.header('host'),\n\t\t\t\tmethod: endpoint.method,\n\t\t\t\tpath: c.req.path,\n\t\t\t}) as TLogger;\n\n\t\t\t// Set response header\n\t\t\tc.header('X-Request-ID', requestId);\n\n\t\t\t// Wrap entire handler in request context for services to access\n\t\t\treturn runWithRequestContext(\n\t\t\t\t{\n\t\t\t\t\tlogger,\n\t\t\t\t\trequestId,\n\t\t\t\t\tstartTime,\n\t\t\t\t\toperation: `${endpoint.method} ${endpoint.route}`,\n\t\t\t\t},\n\t\t\t\tasync () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// Lazy accessors - no upfront parsing, use native Hono methods\n\t\t\t\t\t\tconst header = createHonoHeaders(c);\n\t\t\t\t\t\tconst cookie = createHonoCookies(c);\n\n\t\t\t\t\t\t// Only register services if endpoint has any\n\t\t\t\t\t\tconst services = features.hasServices\n\t\t\t\t\t\t\t? await serviceDiscovery.register(endpoint.services)\n\t\t\t\t\t\t\t: ({} as ServiceRecord<TServices>);\n\n\t\t\t\t\t\t// Resolve database service only if configured\n\t\t\t\t\t\tconst rawDb = features.hasDatabase\n\t\t\t\t\t\t\t? await serviceDiscovery\n\t\t\t\t\t\t\t\t\t.register([endpoint.databaseService!])\n\t\t\t\t\t\t\t\t\t.then(\n\t\t\t\t\t\t\t\t\t\t(s) =>\n\t\t\t\t\t\t\t\t\t\t\ts[\n\t\t\t\t\t\t\t\t\t\t\t\tendpoint.databaseService?.serviceName as keyof typeof s\n\t\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t: undefined;\n\n\t\t\t\t\t\t// Extract session (defaults to empty object)\n\t\t\t\t\t\tconst session = await endpoint.getSession({\n\t\t\t\t\t\t\tservices,\n\t\t\t\t\t\t\tlogger,\n\t\t\t\t\t\t\theader,\n\t\t\t\t\t\t\tcookie,\n\t\t\t\t\t\t\t...(rawDb !== undefined && { db: rawDb }),\n\t\t\t\t\t\t} as any);\n\n\t\t\t\t\t\t// Check authorization (defaults to true)\n\t\t\t\t\t\tconst isAuthorized = await endpoint.authorize({\n\t\t\t\t\t\t\theader,\n\t\t\t\t\t\t\tcookie,\n\t\t\t\t\t\t\tservices,\n\t\t\t\t\t\t\tlogger,\n\t\t\t\t\t\t\tsession,\n\t\t\t\t\t\t\t...(rawDb !== undefined && { db: rawDb }),\n\t\t\t\t\t\t\tbody: features.hasBodyValidation\n\t\t\t\t\t\t\t\t? (c.req.valid as any)('json')\n\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t\tquery: features.hasQueryValidation\n\t\t\t\t\t\t\t\t? (c.req.valid as any)('query')\n\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t\tparams: features.hasParamValidation\n\t\t\t\t\t\t\t\t? (c.req.valid as any)('param')\n\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t} as any);\n\n\t\t\t\t\t\tif (!isAuthorized) {\n\t\t\t\t\t\t\tlogger.warn('Forbidden access attempt');\n\t\t\t\t\t\t\treturn c.json({ error: 'Forbidden' }, 403);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Check rate limit only if configured\n\t\t\t\t\t\tif (features.hasRateLimit) {\n\t\t\t\t\t\t\tconst { checkRateLimit, getRateLimitHeaders } =\n\t\t\t\t\t\t\t\tawait loadRateLimit();\n\t\t\t\t\t\t\tconst rateLimitInfo = await checkRateLimit(endpoint.rateLimit!, {\n\t\t\t\t\t\t\t\theader,\n\t\t\t\t\t\t\t\tservices,\n\t\t\t\t\t\t\t\tlogger,\n\t\t\t\t\t\t\t\tsession,\n\t\t\t\t\t\t\t\tpath: c.req.path,\n\t\t\t\t\t\t\t\tmethod: endpoint.method,\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\tconst rateLimitHeaders = getRateLimitHeaders(\n\t\t\t\t\t\t\t\trateLimitInfo,\n\t\t\t\t\t\t\t\tendpoint.rateLimit!,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tfor (const [key, value] of Object.entries(rateLimitHeaders)) {\n\t\t\t\t\t\t\t\tif (value) {\n\t\t\t\t\t\t\t\t\tc.header(key, value);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Create audit context only if audits are configured\n\t\t\t\t\t\tconst auditContext = features.hasAudits\n\t\t\t\t\t\t\t? await createAuditContext(\n\t\t\t\t\t\t\t\t\tendpoint as any,\n\t\t\t\t\t\t\t\t\tserviceDiscovery,\n\t\t\t\t\t\t\t\t\tlogger,\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tsession,\n\t\t\t\t\t\t\t\t\t\theader,\n\t\t\t\t\t\t\t\t\t\tcookie,\n\t\t\t\t\t\t\t\t\t\tservices: services as Record<string, unknown>,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t: undefined;\n\n\t\t\t\t\t\tconst audits = features.hasAudits\n\t\t\t\t\t\t\t? (endpoint.audits as MappedAudit<TAuditAction, TOutSchema>[])\n\t\t\t\t\t\t\t: [];\n\n\t\t\t\t\t\t// Warn if declarative audits are configured but no audit storage\n\t\t\t\t\t\tif (features.hasAudits && !auditContext) {\n\t\t\t\t\t\t\tlogger.warn('No auditor storage service available');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Extract RLS context only if configured and not bypassed\n\t\t\t\t\t\tconst rlsContext =\n\t\t\t\t\t\t\tfeatures.hasRls && rawDb !== undefined\n\t\t\t\t\t\t\t\t? await endpoint.rlsConfig?.extractor({\n\t\t\t\t\t\t\t\t\t\tservices,\n\t\t\t\t\t\t\t\t\t\tsession: session as TSession,\n\t\t\t\t\t\t\t\t\t\theader,\n\t\t\t\t\t\t\t\t\t\tcookie,\n\t\t\t\t\t\t\t\t\t\tlogger,\n\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t: undefined;\n\n\t\t\t\t\t\t// Execute handler with automatic audit transaction support\n\t\t\t\t\t\tconst result = await executeWithAuditTransaction(\n\t\t\t\t\t\t\tauditContext,\n\t\t\t\t\t\t\tasync (auditor) => {\n\t\t\t\t\t\t\t\tconst sameDatabase =\n\t\t\t\t\t\t\t\t\tauditContext?.storage?.databaseServiceName &&\n\t\t\t\t\t\t\t\t\tauditContext.storage.databaseServiceName ===\n\t\t\t\t\t\t\t\t\t\tendpoint.databaseService?.serviceName;\n\t\t\t\t\t\t\t\tconst baseDb = sameDatabase\n\t\t\t\t\t\t\t\t\t? (auditor?.getTransaction?.() ?? rawDb)\n\t\t\t\t\t\t\t\t\t: rawDb;\n\n\t\t\t\t\t\t\t\tconst executeHandler = async (db: TDatabase | undefined) => {\n\t\t\t\t\t\t\t\t\tconst responseBuilder = new ResponseBuilder();\n\t\t\t\t\t\t\t\t\tconst response = await endpoint.handler(\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tservices,\n\t\t\t\t\t\t\t\t\t\t\tlogger,\n\t\t\t\t\t\t\t\t\t\t\tbody: features.hasBodyValidation\n\t\t\t\t\t\t\t\t\t\t\t\t? (c.req.valid as any)('json')\n\t\t\t\t\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t\t\t\t\t\tquery: features.hasQueryValidation\n\t\t\t\t\t\t\t\t\t\t\t\t? (c.req.valid as any)('query')\n\t\t\t\t\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t\t\t\t\t\tparams: features.hasParamValidation\n\t\t\t\t\t\t\t\t\t\t\t\t? (c.req.valid as any)('param')\n\t\t\t\t\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t\t\t\t\t\tsession,\n\t\t\t\t\t\t\t\t\t\t\theader,\n\t\t\t\t\t\t\t\t\t\t\tcookie,\n\t\t\t\t\t\t\t\t\t\t\tauditor,\n\t\t\t\t\t\t\t\t\t\t\tdb,\n\t\t\t\t\t\t\t\t\t\t} as unknown as EndpointContext<\n\t\t\t\t\t\t\t\t\t\t\tTInput,\n\t\t\t\t\t\t\t\t\t\t\tTServices,\n\t\t\t\t\t\t\t\t\t\t\tTLogger,\n\t\t\t\t\t\t\t\t\t\t\tTSession,\n\t\t\t\t\t\t\t\t\t\t\tTAuditAction,\n\t\t\t\t\t\t\t\t\t\t\tTDatabase,\n\t\t\t\t\t\t\t\t\t\t\tTAuditStorage\n\t\t\t\t\t\t\t\t\t\t>,\n\t\t\t\t\t\t\t\t\t\tresponseBuilder,\n\t\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\t\tlet data = response;\n\t\t\t\t\t\t\t\t\tlet metadata = responseBuilder.getMetadata();\n\n\t\t\t\t\t\t\t\t\tif (Endpoint.hasMetadata(response)) {\n\t\t\t\t\t\t\t\t\t\tdata = response.data;\n\t\t\t\t\t\t\t\t\t\tmetadata = response.metadata;\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tconst output = endpoint.outputSchema\n\t\t\t\t\t\t\t\t\t\t? await endpoint.parseOutput(data)\n\t\t\t\t\t\t\t\t\t\t: undefined;\n\n\t\t\t\t\t\t\t\t\treturn { output, metadata, responseBuilder };\n\t\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\t\tif (features.hasRls && rlsContext && baseDb) {\n\t\t\t\t\t\t\t\t\tconst { withRlsContext } = await loadRls();\n\t\t\t\t\t\t\t\t\treturn withRlsContext(\n\t\t\t\t\t\t\t\t\t\tbaseDb as any,\n\t\t\t\t\t\t\t\t\t\trlsContext,\n\t\t\t\t\t\t\t\t\t\tasync (trx) => executeHandler(trx as TDatabase),\n\t\t\t\t\t\t\t\t\t\t{ prefix: endpoint.rlsConfig?.prefix },\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\treturn executeHandler(baseDb as TDatabase | undefined);\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tasync (result, auditor) => {\n\t\t\t\t\t\t\t\tif (!audits?.length) return;\n\n\t\t\t\t\t\t\t\tfor (const audit of audits) {\n\t\t\t\t\t\t\t\t\tif (audit.when && !audit.when(result.output as any)) {\n\t\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tconst payload = audit.payload(result.output as any);\n\t\t\t\t\t\t\t\t\tconst entityId = audit.entityId?.(result.output as any);\n\t\t\t\t\t\t\t\t\tauditor.audit(audit.type as any, payload as any, {\n\t\t\t\t\t\t\t\t\t\ttable: audit.table,\n\t\t\t\t\t\t\t\t\t\tentityId,\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{ db: rawDb },\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\tconst { output, metadata } = result;\n\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tlet status = endpoint.status as ContentfulStatusCode;\n\n\t\t\t\t\t\t\tif (metadata.status) {\n\t\t\t\t\t\t\t\tstatus = metadata.status as ContentfulStatusCode;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (metadata.headers) {\n\t\t\t\t\t\t\t\tfor (const [key, value] of Object.entries(metadata.headers)) {\n\t\t\t\t\t\t\t\t\tc.header(key, value);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (metadata.cookies) {\n\t\t\t\t\t\t\t\tfor (const [name, { value, options }] of metadata.cookies) {\n\t\t\t\t\t\t\t\t\tsetCookie(c, name, value, options);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Only publish events if configured (no global middleware overhead)\n\t\t\t\t\t\t\tif (features.hasEvents && Endpoint.isSuccessStatus(status)) {\n\t\t\t\t\t\t\t\tawait publishConstructEvents<any>(\n\t\t\t\t\t\t\t\t\tendpoint as any,\n\t\t\t\t\t\t\t\t\toutput,\n\t\t\t\t\t\t\t\t\tserviceDiscovery,\n\t\t\t\t\t\t\t\t\tlogger,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (HonoEndpoint.isDev) {\n\t\t\t\t\t\t\t\tlogger.info({ status, body: output }, 'Outgoing response');\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Respect the endpoint's declared responseType. For JSON (default)\n\t\t\t\t\t\t\t// we use c.json() which sets Content-Type and serializes. For any\n\t\t\t\t\t\t\t// other type, emit the body as-is and set the header explicitly —\n\t\t\t\t\t\t\t// metadata.headers (from r.header()) applied above still wins.\n\t\t\t\t\t\t\tif (endpoint.responseType === 'application/json') {\n\t\t\t\t\t\t\t\t// Hono 4.13 types `c.json`'s status well enough that the\n\t\t\t\t\t\t\t\t// suppression here is now itself an error.\n\t\t\t\t\t\t\t\treturn c.json(output, status);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t!(metadata.headers && hasContentTypeHeader(metadata.headers))\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tc.header('content-type', endpoint.responseType);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn c.body(output as any, status);\n\t\t\t\t\t\t} catch (validationError: any) {\n\t\t\t\t\t\t\tlogger.error(\n\t\t\t\t\t\t\t\t{ err: validationError },\n\t\t\t\t\t\t\t\t'Output validation failed',\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tconst error = wrapError(\n\t\t\t\t\t\t\t\tvalidationError,\n\t\t\t\t\t\t\t\t422,\n\t\t\t\t\t\t\t\t'Response validation failed',\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tconst body = errorBody(error);\n\t\t\t\t\t\t\tif (HonoEndpoint.isDev) {\n\t\t\t\t\t\t\t\tlogger.info(\n\t\t\t\t\t\t\t\t\t{ status: error.statusCode, body },\n\t\t\t\t\t\t\t\t\t'Outgoing response',\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn c.json(body, error.statusCode as ContentfulStatusCode);\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (e: any) {\n\t\t\t\t\t\t// Under `err`, which every pino serializes as an Error — passed bare,\n\t\t\t\t\t\t// one pino does not recognise (a ZodError) is spread into the line\n\t\t\t\t\t\t// with no stack.\n\t\t\t\t\t\tlogger.error({ err: e }, 'Error processing endpoint request');\n\t\t\t\t\t\tconst error = wrapError(e, 500, 'Internal Server Error');\n\t\t\t\t\t\t// Answered here rather than rethrown, so Hono never sets it:\n\t\t\t\t\t\t// a server failure is left on the context the way Hono\n\t\t\t\t\t\t// leaves an uncaught one, for middleware — a request span\n\t\t\t\t\t\t// records it as the exception. A 4xx is the client's.\n\t\t\t\t\t\tif (error.statusCode >= 500 && e instanceof Error) c.error = e;\n\t\t\t\t\t\tconst body = errorBody(error);\n\t\t\t\t\t\tif (HonoEndpoint.isDev) {\n\t\t\t\t\t\t\tlogger.info(\n\t\t\t\t\t\t\t\t{ status: error.statusCode, body },\n\t\t\t\t\t\t\t\t'Outgoing response',\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn c.json(body, error.statusCode as ContentfulStatusCode);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t);\n\t\t};\n\n\t\t// Register route with conditional validators.\n\t\t//\n\t\t// `route` is a generic `TRoute`, and Hono 4.13 overloads `app.get` on\n\t\t// whether the first argument is a path or a handler. A generic that is\n\t\t// only *constrained* to string does not pick the path overload, so it\n\t\t// gets matched against the handler one and reported there. Saying it is a\n\t\t// string chooses the overload that was always intended.\n\t\t//\n\t\t// The spread is why a cast is needed rather than a better type: with\n\t\t// `...validators` of unknown length, the arity is not decidable, so the\n\t\t// first overload is chosen whatever the path argument says. Naming the\n\t\t// shape we are calling states the intent the overloads cannot infer.\n\t\t(\n\t\t\tapp[method] as unknown as (\n\t\t\t\tpath: string,\n\t\t\t\t...handlers: unknown[]\n\t\t\t) => unknown\n\t\t)(route, ...validators, handler);\n\t}\n\n\tstatic addDocsRoute<\n\t\tTServices extends Service[] = [],\n\t\tTLogger extends Logger = Logger,\n\t>(\n\t\tendpoints: Endpoint<string, HttpMethod, any, any, TServices, TLogger>[],\n\t\tapp: Hono,\n\t\tdocsPath: string,\n\t\topenApiOptions?: HonoEndpointOptions['openApiOptions'],\n\t): void {\n\t\tapp.get(docsPath, async (c) => {\n\t\t\ttry {\n\t\t\t\tconst openApiSchema = await Endpoint.buildOpenApiSchema(\n\t\t\t\t\tendpoints,\n\t\t\t\t\topenApiOptions,\n\t\t\t\t);\n\n\t\t\t\treturn c.json(openApiSchema);\n\t\t\t} catch {\n\t\t\t\treturn c.json(\n\t\t\t\t\t{ error: 'Failed to generate OpenAPI documentation' },\n\t\t\t\t\t500,\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\t}\n}\n\n/**\n * What a client is told about a failed request: what went wrong, and the stack\n * only under `gkm dev`. A server's stack is its file layout and its\n * dependencies' versions — for the person debugging it, not for whoever sent\n * the request. A server failure's details are the error it wrapped (an\n * environment variable it could not read, a query that failed), so they go to\n * the log and not the response either; a 4xx's details are the client's.\n */\nfunction errorBody(error: HttpError) {\n\tconst internal = error.statusCode >= 500 && !HonoEndpoint.isDev;\n\treturn {\n\t\tname: error.name,\n\t\tmessage: error.message,\n\t\tstatusCode: error.statusCode,\n\t\tstatusMessage: error.statusMessage,\n\t\tcode: error.code,\n\t\t...(internal ? {} : { details: error.details }),\n\t\t...(HonoEndpoint.isDev ? { stack: error.stack } : {}),\n\t};\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0EA,eAAsB,uBACrB,QACA,KAC+D;CAC/D,MAAM,SAAS,GAAG,OAAO,QAAQ,EAAE,IAAI,CAAC;CAExC,MAAM,YAAiE,CAAC;CAExE,WAAW,MAAM,KAAK,QAAQ;EAI7B,MAAM,QAAQ,MAAM,OAFF,KAAK,QAAQ,KAAK,EAAE,SAAS,CAEZ;EAGnC,MAAM,WAAW,OAAO,OAAO,KAAK,CAAC,CAAC,QAAQ,UAAU;GACvD,OAAO,SAAS,WAAW,KAAK;EACjC,CAAC;EAED,UAAU,KAAK,GAAG,QAAQ;CAC3B;CAEA,OAAO;AACR;;;;;;;AC1FA,SAAgB,eAAe,GAAiC;CAC/D,MAAM,MAAM,EAAE,IAAI;CAClB,MAAM,aAAa,IAAI,QAAQ,GAAG;CAClC,IAAI,eAAe,IAClB,OAAO,CAAC;CAET,MAAM,cAAc,IAAI,MAAM,aAAa,CAAC;CAC5C,OAAO,GAAG,MAAM,WAAW;AAC5B;;;;;;ACyDA,SAAS,qBAAqB,SAA0C;CACvE,KAAK,MAAM,OAAO,OAAO,KAAK,OAAO,GACpC,IAAI,IAAI,YAAY,MAAM,gBAAgB,OAAO;CAElD,OAAO;AACR;;;;AAKA,SAAS,wBACR,UAcmB;CACnB,OAAO;EACN,aAAa,SAAS,SAAS,SAAS;EACxC,aAAa,CAAC,CAAC,SAAS;EACxB,mBAAmB,CAAC,CAAC,SAAS,OAAO;EACrC,oBAAoB,CAAC,CAAC,SAAS,OAAO;EACtC,oBAAoB,CAAC,CAAC,SAAS,OAAO;EAEtC,YACE,SAAS,QAAQ,UAAU,KAAK,KAAK,CAAC,CAAC,SAAS;EAClD,YAAY,SAAS,QAAQ,UAAU,KAAK;EAC5C,cAAc,CAAC,CAAC,SAAS;EACzB,QAAQ,CAAC,CAAC,SAAS,aAAa,CAAC,SAAS;CAC3C;AACD;AAEA,IAAa,eAAb,MAAa,aAgBX;CAEiB;CADlB,YACC,UAcC;EAdgB,KAAA,WAAA;CAcf;CAEH,OAAO,QAAQ,QAAQ,IAAI,aAAa;CAExC,aAAa,SACZ,GACA,MACA,QACC;EACD,IAAI,CAAC,QACJ;EAGD,MAAM,SAAS,MAAM,SAAS,SAAS,QAAQ,IAAI;EAEnD,IAAI,OAAO,QACV,OAAO,EAAE,KAAK,OAAO,QAAQ,GAAG;EAGjC,OAAO,OAAO;CACf;CACA,SACC,kBACA,KACO;EACP,aAAa,SAAS,KAAK,UAAU,kBAAkB,GAAG;CAC3D;;;;;;CAOA,OAAO,qBACN,MACA,mBACC,CAGF;CAEA,aAAa,WACZ,QACA,WACA,MAAM,IAAI,KAAK,GACf,SACA,MAAM,QAAQ,IAAI,GAClB,SACgB;EAChB,MAAM,YAAY,MAAM,uBAAkC,QAAQ,GAAG;EACrE,MAAM,mBACL,iBAAiB,YAAsC,SAAS;EAEjE,aAAa,UAAU,WAAW,kBAAkB,KAAK,OAAO;EAEhE,OAAO;CACR;CAEA,OAAO,UAIN,WACA,kBACA,KACA,SACO;EAEP,IAAI,IAAI,KAAK,OAAO,CAAC;EAIrB,IAAI,aAAa,OAChB,IAAI,IAAI,KAAKA,OAAW,CAAC;EAI1B,MAAM,WACL,SAAS,aAAa,QAAQ,SAAS,YAAY,UAAU;EAC9D,IAAI,UACH,aAAa,aACZ,WACA,KACA,UACA,SAAS,cACV;EAID,MAAM,kBAAkB,UAAU,MAAM,GAAG,MAAM;GAChD,MAAM,YAAY,EAAE,MAAM,MAAM,GAAG;GACnC,MAAM,YAAY,EAAE,MAAM,MAAM,GAAG;GAGnC,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,IAAI,UAAU,QAAQ,UAAU,MAAM,GAAG,KAAK;IACtE,MAAM,WAAW,UAAU,MAAM;IACjC,MAAM,WAAW,UAAU,MAAM;IAGjC,MAAM,aAAa,SAAS,WAAW,GAAG;IAC1C,MAAM,aAAa,SAAS,WAAW,GAAG;IAE1C,IAAI,CAAC,cAAc,YAAY,OAAO;IACtC,IAAI,cAAc,CAAC,YAAY,OAAO;IAGtC,IAAI,aAAa,UAChB,OAAO,SAAS,cAAc,QAAQ;GAExC;GAEA,OAAO;EACR,CAAC;EAID,KAAK,MAAM,YAAY,iBACtB,aAAa,SAAS,UAAU,kBAAkB,GAAG;CAEvD;CAGA,OAAO,SAiBN,UAcA,kBACA,KACO;EACP,MAAM,EAAE,UAAU;EAClB,MAAM,SAAS,SAAS,OAAO,YAAY;EAG3C,MAAM,WAAW,wBAAwB,QAAQ;EAGjD,MAAM,aAAoB,CAAC;EAE3B,IAAI,SAAS,mBACZ,WAAW,KACV,UAAU,SAAS,OAAO,MACzB,aAAa,SAAS,GAAG,OAAO,SAAS,OAAO,IAAI,CACrD,CACD;EAGD,IAAI,SAAS,oBACZ,WAAW,KACV,UAAU,UAAU,GAAG,MAAM;GAC5B,MAAM,cAAc,eAAe,CAAC;GACpC,OAAO,aAAa,SAAS,GAAG,aAAa,SAAS,OAAO,KAAK;EACnE,CAAC,CACF;EAGD,IAAI,SAAS,oBACZ,WAAW,KACV,UAAU,UAAU,QAAQ,MAC3B,aAAa,SAAS,GAAG,QAAQ,SAAS,OAAO,MAAM,CACxD,CACD;EAID,MAAM,UAAU,OAAO,MAAe;GAErC,MAAM,YAAY,KAAK,IAAI;GAC3B,MAAM,YAAY,EAAE,IAAI,OAAO,cAAc,KAAK,OAAO,WAAW;GAEpE,MAAM,SAAS,SAAS,OAAO,MAAM;IACpC;IACA,UAAU,SAAS;IACnB,OAAO,SAAS;IAChB,MAAM,EAAE,IAAI,OAAO,MAAM;IACzB,QAAQ,SAAS;IACjB,MAAM,EAAE,IAAI;GACb,CAAC;GAGD,EAAE,OAAO,gBAAgB,SAAS;GAGlC,OAAO,sBACN;IACC;IACA;IACA;IACA,WAAW,GAAG,SAAS,OAAO,GAAG,SAAS;GAC3C,GACA,YAAY;IACX,IAAI;KAEH,MAAM,SAAS,kBAAkB,CAAC;KAClC,MAAM,SAAS,kBAAkB,CAAC;KAGlC,MAAM,WAAW,SAAS,cACvB,MAAM,iBAAiB,SAAS,SAAS,QAAQ,IAChD,CAAC;KAGL,MAAM,QAAQ,SAAS,cACpB,MAAM,iBACL,SAAS,CAAC,SAAS,eAAgB,CAAC,CAAC,CACrC,MACC,MACA,EACC,SAAS,iBAAiB,YAE7B,IACA,KAAA;KAGH,MAAM,UAAU,MAAM,SAAS,WAAW;MACzC;MACA;MACA;MACA;MACA,GAAI,UAAU,KAAA,KAAa,EAAE,IAAI,MAAM;KACxC,CAAQ;KAqBR,IAAI,CAAC,MAlBsB,SAAS,UAAU;MAC7C;MACA;MACA;MACA;MACA;MACA,GAAI,UAAU,KAAA,KAAa,EAAE,IAAI,MAAM;MACvC,MAAM,SAAS,oBACX,EAAE,IAAI,MAAc,MAAM,IAC3B,KAAA;MACH,OAAO,SAAS,qBACZ,EAAE,IAAI,MAAc,OAAO,IAC5B,KAAA;MACH,QAAQ,SAAS,qBACb,EAAE,IAAI,MAAc,OAAO,IAC5B,KAAA;KACJ,CAAQ,GAEW;MAClB,OAAO,KAAK,0BAA0B;MACtC,OAAO,EAAE,KAAK,EAAE,OAAO,YAAY,GAAG,GAAG;KAC1C;KAGA,IAAI,SAAS,cAAc;MAC1B,MAAM,EAAE,gBAAgB,wBACvB,MAAM,cAAc;MAUrB,MAAM,mBAAmB,oBACxB,MAV2B,eAAe,SAAS,WAAY;OAC/D;OACA;OACA;OACA;OACA,MAAM,EAAE,IAAI;OACZ,QAAQ,SAAS;MAClB,CAAC,GAIA,SAAS,SACV;MACA,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,gBAAgB,GACzD,IAAI,OACH,EAAE,OAAO,KAAK,KAAK;KAGtB;KAGA,MAAM,eAAe,SAAS,YAC3B,MAAM,mBACN,UACA,kBACA,QACA;MACC;MACA;MACA;MACU;KACX,CACD,IACC,KAAA;KAEH,MAAM,SAAS,SAAS,YACpB,SAAS,SACV,CAAC;KAGJ,IAAI,SAAS,aAAa,CAAC,cAC1B,OAAO,KAAK,sCAAsC;KAInD,MAAM,aACL,SAAS,UAAU,UAAU,KAAA,IAC1B,MAAM,SAAS,WAAW,UAAU;MACpC;MACS;MACT;MACA;MACA;KACD,CAAC,IACA,KAAA;KA2FJ,MAAM,EAAE,QAAQ,aAAa,MAxFR,4BACpB,cACA,OAAO,YAAY;MAKlB,MAAM,SAHL,cAAc,SAAS,uBACvB,aAAa,QAAQ,wBACpB,SAAS,iBAAiB,cAExB,SAAS,iBAAiB,KAAK,QAChC;MAEH,MAAM,iBAAiB,OAAO,OAA8B;OAC3D,MAAM,kBAAkB,IAAI,gBAAgB;OAC5C,MAAM,WAAW,MAAM,SAAS,QAC/B;QACC;QACA;QACA,MAAM,SAAS,oBACX,EAAE,IAAI,MAAc,MAAM,IAC3B,KAAA;QACH,OAAO,SAAS,qBACZ,EAAE,IAAI,MAAc,OAAO,IAC5B,KAAA;QACH,QAAQ,SAAS,qBACb,EAAE,IAAI,MAAc,OAAO,IAC5B,KAAA;QACH;QACA;QACA;QACA;QACA;OACD,GASA,eACD;OAEA,IAAI,OAAO;OACX,IAAI,WAAW,gBAAgB,YAAY;OAE3C,IAAI,SAAS,YAAY,QAAQ,GAAG;QACnC,OAAO,SAAS;QAChB,WAAW,SAAS;OACrB;OAMA,OAAO;QAAE,QAJM,SAAS,eACrB,MAAM,SAAS,YAAY,IAAI,IAC/B,KAAA;QAEc;QAAU;OAAgB;MAC5C;MAEA,IAAI,SAAS,UAAU,cAAc,QAAQ;OAC5C,MAAM,EAAE,mBAAmB,MAAM,QAAQ;OACzC,OAAO,eACN,QACA,YACA,OAAO,QAAQ,eAAe,GAAgB,GAC9C,EAAE,QAAQ,SAAS,WAAW,OAAO,CACtC;MACD;MAEA,OAAO,eAAe,MAA+B;KACtD,GACA,OAAO,QAAQ,YAAY;MAC1B,IAAI,CAAC,QAAQ,QAAQ;MAErB,KAAK,MAAM,SAAS,QAAQ;OAC3B,IAAI,MAAM,QAAQ,CAAC,MAAM,KAAK,OAAO,MAAa,GACjD;OAED,MAAM,UAAU,MAAM,QAAQ,OAAO,MAAa;OAClD,MAAM,WAAW,MAAM,WAAW,OAAO,MAAa;OACtD,QAAQ,MAAM,MAAM,MAAa,SAAgB;QAChD,OAAO,MAAM;QACb;OACD,CAAC;MACF;KACD,GACA,EAAE,IAAI,MAAM,CACb;KAIA,IAAI;MACH,IAAI,SAAS,SAAS;MAEtB,IAAI,SAAS,QACZ,SAAS,SAAS;MAGnB,IAAI,SAAS,SACZ,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,SAAS,OAAO,GACzD,EAAE,OAAO,KAAK,KAAK;MAIrB,IAAI,SAAS,SACZ,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,cAAc,SAAS,SACjD,UAAU,GAAG,MAAM,OAAO,OAAO;MAKnC,IAAI,SAAS,aAAa,SAAS,gBAAgB,MAAM,GACxD,MAAM,uBACL,UACA,QACA,kBACA,MACD;MAGD,IAAI,aAAa,OAChB,OAAO,KAAK;OAAE;OAAQ,MAAM;MAAO,GAAG,mBAAmB;MAO1D,IAAI,SAAS,iBAAiB,oBAG7B,OAAO,EAAE,KAAK,QAAQ,MAAM;MAE7B,IACC,EAAE,SAAS,WAAW,qBAAqB,SAAS,OAAO,IAE3D,EAAE,OAAO,gBAAgB,SAAS,YAAY;MAE/C,OAAO,EAAE,KAAK,QAAe,MAAM;KACpC,SAAS,iBAAsB;MAC9B,OAAO,MACN,EAAE,KAAK,gBAAgB,GACvB,0BACD;MACA,MAAM,QAAQ,UACb,iBACA,KACA,4BACD;MACA,MAAM,OAAO,UAAU,KAAK;MAC5B,IAAI,aAAa,OAChB,OAAO,KACN;OAAE,QAAQ,MAAM;OAAY;MAAK,GACjC,mBACD;MAED,OAAO,EAAE,KAAK,MAAM,MAAM,UAAkC;KAC7D;IACD,SAAS,GAAQ;KAIhB,OAAO,MAAM,EAAE,KAAK,EAAE,GAAG,mCAAmC;KAC5D,MAAM,QAAQ,UAAU,GAAG,KAAK,uBAAuB;KAKvD,IAAI,MAAM,cAAc,OAAO,aAAa,OAAO,EAAE,QAAQ;KAC7D,MAAM,OAAO,UAAU,KAAK;KAC5B,IAAI,aAAa,OAChB,OAAO,KACN;MAAE,QAAQ,MAAM;MAAY;KAAK,GACjC,mBACD;KAED,OAAO,EAAE,KAAK,MAAM,MAAM,UAAkC;IAC7D;GACD,CACD;EACD;EAcA,IACK,OAAO,CAIV,OAAO,GAAG,YAAY,OAAO;CAChC;CAEA,OAAO,aAIN,WACA,KACA,UACA,gBACO;EACP,IAAI,IAAI,UAAU,OAAO,MAAM;GAC9B,IAAI;IACH,MAAM,gBAAgB,MAAM,SAAS,mBACpC,WACA,cACD;IAEA,OAAO,EAAE,KAAK,aAAa;GAC5B,QAAQ;IACP,OAAO,EAAE,KACR,EAAE,OAAO,2CAA2C,GACpD,GACD;GACD;EACD,CAAC;CACF;AACD;;;;;;;;;AAUA,SAAS,UAAU,OAAkB;CACpC,MAAM,WAAW,MAAM,cAAc,OAAO,CAAC,aAAa;CAC1D,OAAO;EACN,MAAM,MAAM;EACZ,SAAS,MAAM;EACf,YAAY,MAAM;EAClB,eAAe,MAAM;EACrB,MAAM,MAAM;EACZ,GAAI,WAAW,CAAC,IAAI,EAAE,SAAS,MAAM,QAAQ;EAC7C,GAAI,aAAa,QAAQ,EAAE,OAAO,MAAM,MAAM,IAAI,CAAC;CACpD;AACD"}
@@ -1,4 +1,4 @@
1
- import { t as Construct } from "./Construct-BO1X-kt9.mjs";
1
+ import { t as Construct } from "./Construct-B1vGBPme.mjs";
2
2
  import { canonicalId, provideKey, serviceKey } from "@geekmidas/manifest";
3
3
  import { DEFAULT_LOGGER } from "@geekmidas/logger/console";
4
4
  import { Publisher } from "@geekmidas/events";
@@ -101,4 +101,4 @@ var Queue = class extends Construct {
101
101
  //#endregion
102
102
  export { Queue as t };
103
103
 
104
- //# sourceMappingURL=Queue-hwPN-t31.mjs.map
104
+ //# sourceMappingURL=Queue-CLRT_j5a.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Queue-hwPN-t31.mjs","names":[],"sources":["../src/queue/Queue.ts"],"sourcesContent":["import {\n\ttype EventPublisher,\n\ttype EventPublisherConnectionString,\n\ttype PublishableMessage,\n\tPublisher,\n} from '@geekmidas/events';\nimport type { Logger } from '@geekmidas/logger';\nimport { DEFAULT_LOGGER } from '@geekmidas/logger/console';\nimport {\n\tcanonicalId,\n\ttype Declaration,\n\tprovideKey,\n\tserviceKey,\n} from '@geekmidas/manifest';\nimport type { InferStandardSchema } from '@geekmidas/schema';\nimport type { Service, ServiceRecord } from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport { Construct, ConstructType } from '../Construct';\nimport type { DatabaseContext } from '../functions/Function';\n\n/**\n * The wire message a queue carries: `{ type: <queue name>, payload: <message> }`.\n * The producer publishes this shape and the worker receives the `payload`s as\n * its `messages`. Typing `type` to the queue name keeps the publisher fully\n * typed and lets pg-boss (local) / SQS (deployed) route by name.\n */\nexport type QueueMessage<\n\tTName extends string,\n\tTMessage extends StandardSchemaV1,\n> = PublishableMessage<TName, InferStandardSchema<TMessage>>;\n\n/**\n * A queue worker — a point-to-point SQS-style queue and its single consumer.\n * Unlike a `Subscriber` (topic fan-out, filtered by `subscribedEvents`), a queue\n * drains every message of its one `message` type. Build one from the worker\n * that runs it — `worker.queue('Emails').message(schema).handle(…)` — and\n * `gkm build` discovers it into the manifest's `queues` field.\n *\n * Producers send to it by depending on it: `.dependsOn([emails])` makes\n * `services.emails` the publisher.\n *\n * Its handler gets `db` when it has a database: the worker's, declared once\n * with `worker.database(db)`, or its own with `.database(other)`.\n */\nexport class Queue<\n\tTName extends string = string,\n\tTMessage extends StandardSchemaV1 = StandardSchemaV1,\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTDatabase = undefined,\n\tTDatabaseServiceName extends string = string,\n> extends Construct<\n\tTLogger,\n\tundefined,\n\tTServices,\n\tstring,\n\tundefined,\n\tTDatabaseServiceName,\n\tTDatabase\n> {\n\t__IS_QUEUE__ = true;\n\n\tstatic isQueue(\n\t\tobj: unknown,\n\t): obj is Queue<string, StandardSchemaV1, Service[], Logger, any> {\n\t\treturn Boolean(\n\t\t\tobj &&\n\t\t\t\t(obj as { __IS_QUEUE__?: boolean }).__IS_QUEUE__ === true &&\n\t\t\t\t(obj as Construct).type === ConstructType.Queue,\n\t\t);\n\t}\n\n\t/**\n\t * The canonical id this queue is declared under.\n\t *\n\t * Derived from the name rather than given separately, so `emails` and\n\t * `Emails` are one queue. The name stays what it was: it is the wire `type`\n\t * a producer sends and the worker subscribes to, and changing that would\n\t * silently orphan in-flight messages.\n\t */\n\treadonly id: string;\n\n\t/**\n\t * The queue as a dependency — what `.dependsOn([emails])` dissolves into,\n\t * reachable as `services.emails`.\n\t *\n\t * The producer: the queue's one consumer is the handler it was built with,\n\t * so the only thing another construct can want from a queue is to send to\n\t * it. It reads `<NAME>_PUBLISHER_CONNECTION_STRING` and picks the transport\n\t * from its protocol — `pgboss://` locally, `sqs://` deployed. A field\n\t * assigned once, not a getter: services are cached by object identity.\n\t */\n\treadonly service: Service<\n\t\tUncapitalize<TName>,\n\t\tEventPublisher<QueueMessage<TName, TMessage>>\n\t>;\n\n\t/**\n\t * The producer's env key. Read by both {@link declare} and the service, so\n\t * what the target publishes and what the producer looks up cannot drift.\n\t */\n\tprivate readonly connectionKey: string;\n\n\tconstructor(\n\t\tpublic readonly name: TName,\n\t\tpublic readonly handler: QueueHandler<\n\t\t\tTMessage,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTDatabase\n\t\t>,\n\t\tpublic readonly messageSchema: TMessage,\n\t\tpublic override readonly timeout: number = 30000,\n\t\tpublic override readonly services: TServices = [] as unknown as TServices,\n\t\tpublic override readonly logger: TLogger = DEFAULT_LOGGER as TLogger,\n\t\t/** SQS event-source batch size (deployed). */\n\t\tpublic readonly batchSize?: number,\n\t\t/** Whether the queue is FIFO (deployed). */\n\t\tpublic readonly fifo?: boolean,\n\t\t/**\n\t\t * The construct ids the *worker* `.dependsOn()` named — last, so no\n\t\t * existing positional argument moves.\n\t\t */\n\t\tconstructs: string[] = [],\n\t\t/**\n\t\t * What the handler's `db` is registered from — after `constructs`, for\n\t\t * the same reason.\n\t\t */\n\t\tdatabaseService?: Service<TDatabaseServiceName, TDatabase>,\n\t) {\n\t\tsuper(\n\t\t\tConstructType.Queue,\n\t\t\tlogger,\n\t\t\tservices,\n\t\t\t[],\n\t\t\tundefined,\n\t\t\ttimeout,\n\t\t\tundefined, // memorySize\n\t\t\tundefined, // auditorStorageService\n\t\t\tconstructs,\n\t\t);\n\n\t\tthis.databaseService = databaseService;\n\t\tthis.id = canonicalId(name);\n\t\tthis.connectionKey = provideKey(this.id, 'publisherConnectionString');\n\n\t\tconst envVar = this.connectionKey;\n\t\tthis.service = {\n\t\t\tserviceName: serviceKey(this.id) as Uncapitalize<TName>,\n\t\t\tasync register({ envParser }) {\n\t\t\t\tconst { connectionString } = envParser\n\t\t\t\t\t.create((get) => ({ connectionString: get(envVar).string() }))\n\t\t\t\t\t.parse();\n\n\t\t\t\treturn Publisher.fromConnectionString<QueueMessage<TName, TMessage>>(\n\t\t\t\t\tconnectionString as EventPublisherConnectionString,\n\t\t\t\t);\n\t\t\t},\n\t\t};\n\t}\n\n\t/**\n\t * What this queue is in the manifest.\n\t *\n\t * A queue is a resource, not a handler: the worker is reached *through* it,\n\t * so the declaration carries the producer's key and nothing about the code\n\t * that drains it. The local target reads this to know which broker has to be\n\t * running and what connection string to inject.\n\t */\n\tdeclare(): Declaration[] {\n\t\treturn [\n\t\t\t{\n\t\t\t\tkind: 'queue',\n\t\t\t\tid: this.id,\n\t\t\t\tprovides: [this.connectionKey],\n\t\t\t\t...(this.fifo ? { fifo: true } : {}),\n\t\t\t\t// Nested rather than a sibling node, because position carries the\n\t\t\t\t// trigger: this handler is reached by messages on this queue and\n\t\t\t\t// by nothing else, so there is no `trigger` field to keep in step.\n\t\t\t\t//\n\t\t\t\t// `handler` is the export name; which directory the build wrote it\n\t\t\t\t// to is the target's business, and a path with a cloud in it does\n\t\t\t\t// not belong in a neutral declaration.\n\t\t\t\tworker: {\n\t\t\t\t\tid: `${this.id}Worker`,\n\t\t\t\t\thandler: `${this.id}.handler`,\n\t\t\t\t\t// Filled by the build from what the worker declared. Empty is a\n\t\t\t\t\t// stated gap, not a claim that it reaches nothing.\n\t\t\t\t\tdependencies: [],\n\t\t\t\t},\n\t\t\t},\n\t\t];\n\t}\n}\n\n/**\n * The context a queue handler receives — a batch of typed messages, and `db`\n * when the queue has a database.\n */\nexport type QueueContext<\n\tTMessage extends StandardSchemaV1,\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTDatabase = undefined,\n> = {\n\tmessages: InferStandardSchema<TMessage>[];\n\tservices: ServiceRecord<TServices>;\n\tlogger: TLogger;\n} & DatabaseContext<TDatabase>;\n\nexport type QueueHandler<\n\tTMessage extends StandardSchemaV1,\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTDatabase = undefined,\n> = (\n\tctx: QueueContext<TMessage, TServices, TLogger, TDatabase>,\n) => unknown | Promise<unknown>;\n"],"mappings":";;;;;;;;;;;;;;;;;;AA4CA,IAAa,QAAb,cAOU,UAQR;CA6CgB;CACA;CAMA;CACS;CACA;CACA;CAET;CAEA;CA1DjB,eAAe;CAEf,OAAO,QACN,KACiE;EACjE,OAAO,QACN,OACE,IAAmC,iBAAiB,QACpD,IAAkB,SAAA,8BACrB;CACD;;;;;;;;;CAUA;;;;;;;;;;;CAYA;;;;;CASA;CAEA,YACC,MACA,SAMA,eACA,UAA2C,KAC3C,WAA+C,CAAC,GAChD,SAA2C,gBAE3C,WAEA,MAKA,aAAuB,CAAC,GAKxB,iBACC;EACD,MAAA,gCAEC,QACA,UACA,CAAC,GACD,KAAA,GACA,SACA,KAAA,GACA,KAAA,GACA,UACD;EApCgB,KAAA,OAAA;EACA,KAAA,UAAA;EAMA,KAAA,gBAAA;EACS,KAAA,UAAA;EACA,KAAA,WAAA;EACA,KAAA,SAAA;EAET,KAAA,YAAA;EAEA,KAAA,OAAA;EAwBhB,KAAK,kBAAkB;EACvB,KAAK,KAAK,YAAY,IAAI;EAC1B,KAAK,gBAAgB,WAAW,KAAK,IAAI,2BAA2B;EAEpE,MAAM,SAAS,KAAK;EACpB,KAAK,UAAU;GACd,aAAa,WAAW,KAAK,EAAE;GAC/B,MAAM,SAAS,EAAE,aAAa;IAC7B,MAAM,EAAE,qBAAqB,UAC3B,QAAQ,SAAS,EAAE,kBAAkB,IAAI,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAC7D,MAAM;IAER,OAAO,UAAU,qBAChB,gBACD;GACD;EACD;CACD;;;;;;;;;CAUA,UAAyB;EACxB,OAAO,CACN;GACC,MAAM;GACN,IAAI,KAAK;GACT,UAAU,CAAC,KAAK,aAAa;GAC7B,GAAI,KAAK,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;GAQlC,QAAQ;IACP,IAAI,GAAG,KAAK,GAAG;IACf,SAAS,GAAG,KAAK,GAAG;IAGpB,cAAc,CAAC;GAChB;EACD,CACD;CACD;AACD"}
1
+ {"version":3,"file":"Queue-CLRT_j5a.mjs","names":[],"sources":["../src/queue/Queue.ts"],"sourcesContent":["import {\n\ttype EventPublisher,\n\ttype EventPublisherConnectionString,\n\ttype PublishableMessage,\n\tPublisher,\n} from '@geekmidas/events';\nimport type { Logger } from '@geekmidas/logger';\nimport { DEFAULT_LOGGER } from '@geekmidas/logger/console';\nimport {\n\tcanonicalId,\n\ttype Declaration,\n\tprovideKey,\n\tserviceKey,\n} from '@geekmidas/manifest';\nimport type { InferStandardSchema } from '@geekmidas/schema';\nimport type { Service, ServiceRecord } from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport { Construct, ConstructType } from '../Construct';\nimport type { DatabaseContext } from '../functions/Function';\n\n/**\n * The wire message a queue carries: `{ type: <queue name>, payload: <message> }`.\n * The producer publishes this shape and the worker receives the `payload`s as\n * its `messages`. Typing `type` to the queue name keeps the publisher fully\n * typed and lets pg-boss (local) / SQS (deployed) route by name.\n */\nexport type QueueMessage<\n\tTName extends string,\n\tTMessage extends StandardSchemaV1,\n> = PublishableMessage<TName, InferStandardSchema<TMessage>>;\n\n/**\n * A queue worker — a point-to-point SQS-style queue and its single consumer.\n * Unlike a `Subscriber` (topic fan-out, filtered by `subscribedEvents`), a queue\n * drains every message of its one `message` type. Build one from the worker\n * that runs it — `worker.queue('Emails').message(schema).handle(…)` — and\n * `gkm build` discovers it into the manifest's `queues` field.\n *\n * Producers send to it by depending on it: `.dependsOn([emails])` makes\n * `services.emails` the publisher.\n *\n * Its handler gets `db` when it has a database: the worker's, declared once\n * with `worker.database(db)`, or its own with `.database(other)`.\n */\nexport class Queue<\n\tTName extends string = string,\n\tTMessage extends StandardSchemaV1 = StandardSchemaV1,\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTDatabase = undefined,\n\tTDatabaseServiceName extends string = string,\n> extends Construct<\n\tTLogger,\n\tundefined,\n\tTServices,\n\tstring,\n\tundefined,\n\tTDatabaseServiceName,\n\tTDatabase\n> {\n\t__IS_QUEUE__ = true;\n\n\tstatic isQueue(\n\t\tobj: unknown,\n\t): obj is Queue<string, StandardSchemaV1, Service[], Logger, any> {\n\t\treturn Boolean(\n\t\t\tobj &&\n\t\t\t\t(obj as { __IS_QUEUE__?: boolean }).__IS_QUEUE__ === true &&\n\t\t\t\t(obj as Construct).type === ConstructType.Queue,\n\t\t);\n\t}\n\n\t/**\n\t * The canonical id this queue is declared under.\n\t *\n\t * Derived from the name rather than given separately, so `emails` and\n\t * `Emails` are one queue. The name stays what it was: it is the wire `type`\n\t * a producer sends and the worker subscribes to, and changing that would\n\t * silently orphan in-flight messages.\n\t */\n\treadonly id: string;\n\n\t/**\n\t * The queue as a dependency — what `.dependsOn([emails])` dissolves into,\n\t * reachable as `services.emails`.\n\t *\n\t * The producer: the queue's one consumer is the handler it was built with,\n\t * so the only thing another construct can want from a queue is to send to\n\t * it. It reads `<NAME>_PUBLISHER_CONNECTION_STRING` and picks the transport\n\t * from its protocol — `pgboss://` locally, `sqs://` deployed. A field\n\t * assigned once, not a getter: services are cached by object identity.\n\t */\n\treadonly service: Service<\n\t\tUncapitalize<TName>,\n\t\tEventPublisher<QueueMessage<TName, TMessage>>\n\t>;\n\n\t/**\n\t * The producer's env key. Read by both {@link declare} and the service, so\n\t * what the target publishes and what the producer looks up cannot drift.\n\t */\n\tprivate readonly connectionKey: string;\n\n\tconstructor(\n\t\tpublic readonly name: TName,\n\t\tpublic readonly handler: QueueHandler<\n\t\t\tTMessage,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTDatabase\n\t\t>,\n\t\tpublic readonly messageSchema: TMessage,\n\t\tpublic override readonly timeout: number = 30000,\n\t\tpublic override readonly services: TServices = [] as unknown as TServices,\n\t\tpublic override readonly logger: TLogger = DEFAULT_LOGGER as TLogger,\n\t\t/** SQS event-source batch size (deployed). */\n\t\tpublic readonly batchSize?: number,\n\t\t/** Whether the queue is FIFO (deployed). */\n\t\tpublic readonly fifo?: boolean,\n\t\t/**\n\t\t * The construct ids the *worker* `.dependsOn()` named — last, so no\n\t\t * existing positional argument moves.\n\t\t */\n\t\tconstructs: string[] = [],\n\t\t/**\n\t\t * What the handler's `db` is registered from — after `constructs`, for\n\t\t * the same reason.\n\t\t */\n\t\tdatabaseService?: Service<TDatabaseServiceName, TDatabase>,\n\t) {\n\t\tsuper(\n\t\t\tConstructType.Queue,\n\t\t\tlogger,\n\t\t\tservices,\n\t\t\t[],\n\t\t\tundefined,\n\t\t\ttimeout,\n\t\t\tundefined, // memorySize\n\t\t\tundefined, // auditorStorageService\n\t\t\tconstructs,\n\t\t);\n\n\t\tthis.databaseService = databaseService;\n\t\tthis.id = canonicalId(name);\n\t\tthis.connectionKey = provideKey(this.id, 'publisherConnectionString');\n\n\t\tconst envVar = this.connectionKey;\n\t\tthis.service = {\n\t\t\tserviceName: serviceKey(this.id) as Uncapitalize<TName>,\n\t\t\tasync register({ envParser }) {\n\t\t\t\tconst { connectionString } = envParser\n\t\t\t\t\t.create((get) => ({ connectionString: get(envVar).string() }))\n\t\t\t\t\t.parse();\n\n\t\t\t\treturn Publisher.fromConnectionString<QueueMessage<TName, TMessage>>(\n\t\t\t\t\tconnectionString as EventPublisherConnectionString,\n\t\t\t\t);\n\t\t\t},\n\t\t};\n\t}\n\n\t/**\n\t * What this queue is in the manifest.\n\t *\n\t * A queue is a resource, not a handler: the worker is reached *through* it,\n\t * so the declaration carries the producer's key and nothing about the code\n\t * that drains it. The local target reads this to know which broker has to be\n\t * running and what connection string to inject.\n\t */\n\tdeclare(): Declaration[] {\n\t\treturn [\n\t\t\t{\n\t\t\t\tkind: 'queue',\n\t\t\t\tid: this.id,\n\t\t\t\tprovides: [this.connectionKey],\n\t\t\t\t...(this.fifo ? { fifo: true } : {}),\n\t\t\t\t// Nested rather than a sibling node, because position carries the\n\t\t\t\t// trigger: this handler is reached by messages on this queue and\n\t\t\t\t// by nothing else, so there is no `trigger` field to keep in step.\n\t\t\t\t//\n\t\t\t\t// `handler` is the export name; which directory the build wrote it\n\t\t\t\t// to is the target's business, and a path with a cloud in it does\n\t\t\t\t// not belong in a neutral declaration.\n\t\t\t\tworker: {\n\t\t\t\t\tid: `${this.id}Worker`,\n\t\t\t\t\thandler: `${this.id}.handler`,\n\t\t\t\t\t// Filled by the build from what the worker declared. Empty is a\n\t\t\t\t\t// stated gap, not a claim that it reaches nothing.\n\t\t\t\t\tdependencies: [],\n\t\t\t\t},\n\t\t\t},\n\t\t];\n\t}\n}\n\n/**\n * The context a queue handler receives — a batch of typed messages, and `db`\n * when the queue has a database.\n */\nexport type QueueContext<\n\tTMessage extends StandardSchemaV1,\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTDatabase = undefined,\n> = {\n\tmessages: InferStandardSchema<TMessage>[];\n\tservices: ServiceRecord<TServices>;\n\tlogger: TLogger;\n} & DatabaseContext<TDatabase>;\n\nexport type QueueHandler<\n\tTMessage extends StandardSchemaV1,\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTDatabase = undefined,\n> = (\n\tctx: QueueContext<TMessage, TServices, TLogger, TDatabase>,\n) => unknown | Promise<unknown>;\n"],"mappings":";;;;;;;;;;;;;;;;;;AA4CA,IAAa,QAAb,cAOU,UAQR;CA6CgB;CACA;CAMA;CACS;CACA;CACA;CAET;CAEA;CA1DjB,eAAe;CAEf,OAAO,QACN,KACiE;EACjE,OAAO,QACN,OACE,IAAmC,iBAAiB,QACpD,IAAkB,SAAA,8BACrB;CACD;;;;;;;;;CAUA;;;;;;;;;;;CAYA;;;;;CASA;CAEA,YACC,MACA,SAMA,eACA,UAA2C,KAC3C,WAA+C,CAAC,GAChD,SAA2C,gBAE3C,WAEA,MAKA,aAAuB,CAAC,GAKxB,iBACC;EACD,MAAA,gCAEC,QACA,UACA,CAAC,GACD,KAAA,GACA,SACA,KAAA,GACA,KAAA,GACA,UACD;EApCgB,KAAA,OAAA;EACA,KAAA,UAAA;EAMA,KAAA,gBAAA;EACS,KAAA,UAAA;EACA,KAAA,WAAA;EACA,KAAA,SAAA;EAET,KAAA,YAAA;EAEA,KAAA,OAAA;EAwBhB,KAAK,kBAAkB;EACvB,KAAK,KAAK,YAAY,IAAI;EAC1B,KAAK,gBAAgB,WAAW,KAAK,IAAI,2BAA2B;EAEpE,MAAM,SAAS,KAAK;EACpB,KAAK,UAAU;GACd,aAAa,WAAW,KAAK,EAAE;GAC/B,MAAM,SAAS,EAAE,aAAa;IAC7B,MAAM,EAAE,qBAAqB,UAC3B,QAAQ,SAAS,EAAE,kBAAkB,IAAI,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAC7D,MAAM;IAER,OAAO,UAAU,qBAChB,gBACD;GACD;EACD;CACD;;;;;;;;;CAUA,UAAyB;EACxB,OAAO,CACN;GACC,MAAM;GACN,IAAI,KAAK;GACT,UAAU,CAAC,KAAK,aAAa;GAC7B,GAAI,KAAK,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;GAQlC,QAAQ;IACP,IAAI,GAAG,KAAK,GAAG;IACf,SAAS,GAAG,KAAK,GAAG;IAGpB,cAAc,CAAC;GAChB;EACD,CACD;CACD;AACD"}
@@ -1,4 +1,4 @@
1
- const require_Construct = require("./Construct-DmvXceVY.cjs");
1
+ const require_Construct = require("./Construct-DUSXuTHP.cjs");
2
2
  let _geekmidas_manifest = require("@geekmidas/manifest");
3
3
  let _geekmidas_logger_console = require("@geekmidas/logger/console");
4
4
  let _geekmidas_events = require("@geekmidas/events");
@@ -106,4 +106,4 @@ Object.defineProperty(exports, "Queue", {
106
106
  }
107
107
  });
108
108
 
109
- //# sourceMappingURL=Queue-D5jcw0mu.cjs.map
109
+ //# sourceMappingURL=Queue-umpX3683.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Queue-D5jcw0mu.cjs","names":["Construct","DEFAULT_LOGGER","canonicalId","provideKey","serviceKey","Publisher"],"sources":["../src/queue/Queue.ts"],"sourcesContent":["import {\n\ttype EventPublisher,\n\ttype EventPublisherConnectionString,\n\ttype PublishableMessage,\n\tPublisher,\n} from '@geekmidas/events';\nimport type { Logger } from '@geekmidas/logger';\nimport { DEFAULT_LOGGER } from '@geekmidas/logger/console';\nimport {\n\tcanonicalId,\n\ttype Declaration,\n\tprovideKey,\n\tserviceKey,\n} from '@geekmidas/manifest';\nimport type { InferStandardSchema } from '@geekmidas/schema';\nimport type { Service, ServiceRecord } from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport { Construct, ConstructType } from '../Construct';\nimport type { DatabaseContext } from '../functions/Function';\n\n/**\n * The wire message a queue carries: `{ type: <queue name>, payload: <message> }`.\n * The producer publishes this shape and the worker receives the `payload`s as\n * its `messages`. Typing `type` to the queue name keeps the publisher fully\n * typed and lets pg-boss (local) / SQS (deployed) route by name.\n */\nexport type QueueMessage<\n\tTName extends string,\n\tTMessage extends StandardSchemaV1,\n> = PublishableMessage<TName, InferStandardSchema<TMessage>>;\n\n/**\n * A queue worker — a point-to-point SQS-style queue and its single consumer.\n * Unlike a `Subscriber` (topic fan-out, filtered by `subscribedEvents`), a queue\n * drains every message of its one `message` type. Build one from the worker\n * that runs it — `worker.queue('Emails').message(schema).handle(…)` — and\n * `gkm build` discovers it into the manifest's `queues` field.\n *\n * Producers send to it by depending on it: `.dependsOn([emails])` makes\n * `services.emails` the publisher.\n *\n * Its handler gets `db` when it has a database: the worker's, declared once\n * with `worker.database(db)`, or its own with `.database(other)`.\n */\nexport class Queue<\n\tTName extends string = string,\n\tTMessage extends StandardSchemaV1 = StandardSchemaV1,\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTDatabase = undefined,\n\tTDatabaseServiceName extends string = string,\n> extends Construct<\n\tTLogger,\n\tundefined,\n\tTServices,\n\tstring,\n\tundefined,\n\tTDatabaseServiceName,\n\tTDatabase\n> {\n\t__IS_QUEUE__ = true;\n\n\tstatic isQueue(\n\t\tobj: unknown,\n\t): obj is Queue<string, StandardSchemaV1, Service[], Logger, any> {\n\t\treturn Boolean(\n\t\t\tobj &&\n\t\t\t\t(obj as { __IS_QUEUE__?: boolean }).__IS_QUEUE__ === true &&\n\t\t\t\t(obj as Construct).type === ConstructType.Queue,\n\t\t);\n\t}\n\n\t/**\n\t * The canonical id this queue is declared under.\n\t *\n\t * Derived from the name rather than given separately, so `emails` and\n\t * `Emails` are one queue. The name stays what it was: it is the wire `type`\n\t * a producer sends and the worker subscribes to, and changing that would\n\t * silently orphan in-flight messages.\n\t */\n\treadonly id: string;\n\n\t/**\n\t * The queue as a dependency — what `.dependsOn([emails])` dissolves into,\n\t * reachable as `services.emails`.\n\t *\n\t * The producer: the queue's one consumer is the handler it was built with,\n\t * so the only thing another construct can want from a queue is to send to\n\t * it. It reads `<NAME>_PUBLISHER_CONNECTION_STRING` and picks the transport\n\t * from its protocol — `pgboss://` locally, `sqs://` deployed. A field\n\t * assigned once, not a getter: services are cached by object identity.\n\t */\n\treadonly service: Service<\n\t\tUncapitalize<TName>,\n\t\tEventPublisher<QueueMessage<TName, TMessage>>\n\t>;\n\n\t/**\n\t * The producer's env key. Read by both {@link declare} and the service, so\n\t * what the target publishes and what the producer looks up cannot drift.\n\t */\n\tprivate readonly connectionKey: string;\n\n\tconstructor(\n\t\tpublic readonly name: TName,\n\t\tpublic readonly handler: QueueHandler<\n\t\t\tTMessage,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTDatabase\n\t\t>,\n\t\tpublic readonly messageSchema: TMessage,\n\t\tpublic override readonly timeout: number = 30000,\n\t\tpublic override readonly services: TServices = [] as unknown as TServices,\n\t\tpublic override readonly logger: TLogger = DEFAULT_LOGGER as TLogger,\n\t\t/** SQS event-source batch size (deployed). */\n\t\tpublic readonly batchSize?: number,\n\t\t/** Whether the queue is FIFO (deployed). */\n\t\tpublic readonly fifo?: boolean,\n\t\t/**\n\t\t * The construct ids the *worker* `.dependsOn()` named — last, so no\n\t\t * existing positional argument moves.\n\t\t */\n\t\tconstructs: string[] = [],\n\t\t/**\n\t\t * What the handler's `db` is registered from — after `constructs`, for\n\t\t * the same reason.\n\t\t */\n\t\tdatabaseService?: Service<TDatabaseServiceName, TDatabase>,\n\t) {\n\t\tsuper(\n\t\t\tConstructType.Queue,\n\t\t\tlogger,\n\t\t\tservices,\n\t\t\t[],\n\t\t\tundefined,\n\t\t\ttimeout,\n\t\t\tundefined, // memorySize\n\t\t\tundefined, // auditorStorageService\n\t\t\tconstructs,\n\t\t);\n\n\t\tthis.databaseService = databaseService;\n\t\tthis.id = canonicalId(name);\n\t\tthis.connectionKey = provideKey(this.id, 'publisherConnectionString');\n\n\t\tconst envVar = this.connectionKey;\n\t\tthis.service = {\n\t\t\tserviceName: serviceKey(this.id) as Uncapitalize<TName>,\n\t\t\tasync register({ envParser }) {\n\t\t\t\tconst { connectionString } = envParser\n\t\t\t\t\t.create((get) => ({ connectionString: get(envVar).string() }))\n\t\t\t\t\t.parse();\n\n\t\t\t\treturn Publisher.fromConnectionString<QueueMessage<TName, TMessage>>(\n\t\t\t\t\tconnectionString as EventPublisherConnectionString,\n\t\t\t\t);\n\t\t\t},\n\t\t};\n\t}\n\n\t/**\n\t * What this queue is in the manifest.\n\t *\n\t * A queue is a resource, not a handler: the worker is reached *through* it,\n\t * so the declaration carries the producer's key and nothing about the code\n\t * that drains it. The local target reads this to know which broker has to be\n\t * running and what connection string to inject.\n\t */\n\tdeclare(): Declaration[] {\n\t\treturn [\n\t\t\t{\n\t\t\t\tkind: 'queue',\n\t\t\t\tid: this.id,\n\t\t\t\tprovides: [this.connectionKey],\n\t\t\t\t...(this.fifo ? { fifo: true } : {}),\n\t\t\t\t// Nested rather than a sibling node, because position carries the\n\t\t\t\t// trigger: this handler is reached by messages on this queue and\n\t\t\t\t// by nothing else, so there is no `trigger` field to keep in step.\n\t\t\t\t//\n\t\t\t\t// `handler` is the export name; which directory the build wrote it\n\t\t\t\t// to is the target's business, and a path with a cloud in it does\n\t\t\t\t// not belong in a neutral declaration.\n\t\t\t\tworker: {\n\t\t\t\t\tid: `${this.id}Worker`,\n\t\t\t\t\thandler: `${this.id}.handler`,\n\t\t\t\t\t// Filled by the build from what the worker declared. Empty is a\n\t\t\t\t\t// stated gap, not a claim that it reaches nothing.\n\t\t\t\t\tdependencies: [],\n\t\t\t\t},\n\t\t\t},\n\t\t];\n\t}\n}\n\n/**\n * The context a queue handler receives — a batch of typed messages, and `db`\n * when the queue has a database.\n */\nexport type QueueContext<\n\tTMessage extends StandardSchemaV1,\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTDatabase = undefined,\n> = {\n\tmessages: InferStandardSchema<TMessage>[];\n\tservices: ServiceRecord<TServices>;\n\tlogger: TLogger;\n} & DatabaseContext<TDatabase>;\n\nexport type QueueHandler<\n\tTMessage extends StandardSchemaV1,\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTDatabase = undefined,\n> = (\n\tctx: QueueContext<TMessage, TServices, TLogger, TDatabase>,\n) => unknown | Promise<unknown>;\n"],"mappings":";;;;;;;;;;;;;;;;;;AA4CA,IAAa,QAAb,cAOUA,kBAAAA,UAQR;CA6CgB;CACA;CAMA;CACS;CACA;CACA;CAET;CAEA;CA1DjB,eAAe;CAEf,OAAO,QACN,KACiE;EACjE,OAAO,QACN,OACE,IAAmC,iBAAiB,QACpD,IAAkB,SAAA,8BACrB;CACD;;;;;;;;;CAUA;;;;;;;;;;;CAYA;;;;;CASA;CAEA,YACC,MACA,SAMA,eACA,UAA2C,KAC3C,WAA+C,CAAC,GAChD,SAA2CC,0BAAAA,gBAE3C,WAEA,MAKA,aAAuB,CAAC,GAKxB,iBACC;EACD,MAAA,gCAEC,QACA,UACA,CAAC,GACD,KAAA,GACA,SACA,KAAA,GACA,KAAA,GACA,UACD;EApCgB,KAAA,OAAA;EACA,KAAA,UAAA;EAMA,KAAA,gBAAA;EACS,KAAA,UAAA;EACA,KAAA,WAAA;EACA,KAAA,SAAA;EAET,KAAA,YAAA;EAEA,KAAA,OAAA;EAwBhB,KAAK,kBAAkB;EACvB,KAAK,MAAA,GAAKC,oBAAAA,YAAAA,CAAY,IAAI;EAC1B,KAAK,iBAAA,GAAgBC,oBAAAA,WAAAA,CAAW,KAAK,IAAI,2BAA2B;EAEpE,MAAM,SAAS,KAAK;EACpB,KAAK,UAAU;GACd,cAAA,GAAaC,oBAAAA,WAAAA,CAAW,KAAK,EAAE;GAC/B,MAAM,SAAS,EAAE,aAAa;IAC7B,MAAM,EAAE,qBAAqB,UAC3B,QAAQ,SAAS,EAAE,kBAAkB,IAAI,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAC7D,MAAM;IAER,OAAOC,kBAAAA,UAAU,qBAChB,gBACD;GACD;EACD;CACD;;;;;;;;;CAUA,UAAyB;EACxB,OAAO,CACN;GACC,MAAM;GACN,IAAI,KAAK;GACT,UAAU,CAAC,KAAK,aAAa;GAC7B,GAAI,KAAK,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;GAQlC,QAAQ;IACP,IAAI,GAAG,KAAK,GAAG;IACf,SAAS,GAAG,KAAK,GAAG;IAGpB,cAAc,CAAC;GAChB;EACD,CACD;CACD;AACD"}
1
+ {"version":3,"file":"Queue-umpX3683.cjs","names":["Construct","DEFAULT_LOGGER","canonicalId","provideKey","serviceKey","Publisher"],"sources":["../src/queue/Queue.ts"],"sourcesContent":["import {\n\ttype EventPublisher,\n\ttype EventPublisherConnectionString,\n\ttype PublishableMessage,\n\tPublisher,\n} from '@geekmidas/events';\nimport type { Logger } from '@geekmidas/logger';\nimport { DEFAULT_LOGGER } from '@geekmidas/logger/console';\nimport {\n\tcanonicalId,\n\ttype Declaration,\n\tprovideKey,\n\tserviceKey,\n} from '@geekmidas/manifest';\nimport type { InferStandardSchema } from '@geekmidas/schema';\nimport type { Service, ServiceRecord } from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport { Construct, ConstructType } from '../Construct';\nimport type { DatabaseContext } from '../functions/Function';\n\n/**\n * The wire message a queue carries: `{ type: <queue name>, payload: <message> }`.\n * The producer publishes this shape and the worker receives the `payload`s as\n * its `messages`. Typing `type` to the queue name keeps the publisher fully\n * typed and lets pg-boss (local) / SQS (deployed) route by name.\n */\nexport type QueueMessage<\n\tTName extends string,\n\tTMessage extends StandardSchemaV1,\n> = PublishableMessage<TName, InferStandardSchema<TMessage>>;\n\n/**\n * A queue worker — a point-to-point SQS-style queue and its single consumer.\n * Unlike a `Subscriber` (topic fan-out, filtered by `subscribedEvents`), a queue\n * drains every message of its one `message` type. Build one from the worker\n * that runs it — `worker.queue('Emails').message(schema).handle(…)` — and\n * `gkm build` discovers it into the manifest's `queues` field.\n *\n * Producers send to it by depending on it: `.dependsOn([emails])` makes\n * `services.emails` the publisher.\n *\n * Its handler gets `db` when it has a database: the worker's, declared once\n * with `worker.database(db)`, or its own with `.database(other)`.\n */\nexport class Queue<\n\tTName extends string = string,\n\tTMessage extends StandardSchemaV1 = StandardSchemaV1,\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTDatabase = undefined,\n\tTDatabaseServiceName extends string = string,\n> extends Construct<\n\tTLogger,\n\tundefined,\n\tTServices,\n\tstring,\n\tundefined,\n\tTDatabaseServiceName,\n\tTDatabase\n> {\n\t__IS_QUEUE__ = true;\n\n\tstatic isQueue(\n\t\tobj: unknown,\n\t): obj is Queue<string, StandardSchemaV1, Service[], Logger, any> {\n\t\treturn Boolean(\n\t\t\tobj &&\n\t\t\t\t(obj as { __IS_QUEUE__?: boolean }).__IS_QUEUE__ === true &&\n\t\t\t\t(obj as Construct).type === ConstructType.Queue,\n\t\t);\n\t}\n\n\t/**\n\t * The canonical id this queue is declared under.\n\t *\n\t * Derived from the name rather than given separately, so `emails` and\n\t * `Emails` are one queue. The name stays what it was: it is the wire `type`\n\t * a producer sends and the worker subscribes to, and changing that would\n\t * silently orphan in-flight messages.\n\t */\n\treadonly id: string;\n\n\t/**\n\t * The queue as a dependency — what `.dependsOn([emails])` dissolves into,\n\t * reachable as `services.emails`.\n\t *\n\t * The producer: the queue's one consumer is the handler it was built with,\n\t * so the only thing another construct can want from a queue is to send to\n\t * it. It reads `<NAME>_PUBLISHER_CONNECTION_STRING` and picks the transport\n\t * from its protocol — `pgboss://` locally, `sqs://` deployed. A field\n\t * assigned once, not a getter: services are cached by object identity.\n\t */\n\treadonly service: Service<\n\t\tUncapitalize<TName>,\n\t\tEventPublisher<QueueMessage<TName, TMessage>>\n\t>;\n\n\t/**\n\t * The producer's env key. Read by both {@link declare} and the service, so\n\t * what the target publishes and what the producer looks up cannot drift.\n\t */\n\tprivate readonly connectionKey: string;\n\n\tconstructor(\n\t\tpublic readonly name: TName,\n\t\tpublic readonly handler: QueueHandler<\n\t\t\tTMessage,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTDatabase\n\t\t>,\n\t\tpublic readonly messageSchema: TMessage,\n\t\tpublic override readonly timeout: number = 30000,\n\t\tpublic override readonly services: TServices = [] as unknown as TServices,\n\t\tpublic override readonly logger: TLogger = DEFAULT_LOGGER as TLogger,\n\t\t/** SQS event-source batch size (deployed). */\n\t\tpublic readonly batchSize?: number,\n\t\t/** Whether the queue is FIFO (deployed). */\n\t\tpublic readonly fifo?: boolean,\n\t\t/**\n\t\t * The construct ids the *worker* `.dependsOn()` named — last, so no\n\t\t * existing positional argument moves.\n\t\t */\n\t\tconstructs: string[] = [],\n\t\t/**\n\t\t * What the handler's `db` is registered from — after `constructs`, for\n\t\t * the same reason.\n\t\t */\n\t\tdatabaseService?: Service<TDatabaseServiceName, TDatabase>,\n\t) {\n\t\tsuper(\n\t\t\tConstructType.Queue,\n\t\t\tlogger,\n\t\t\tservices,\n\t\t\t[],\n\t\t\tundefined,\n\t\t\ttimeout,\n\t\t\tundefined, // memorySize\n\t\t\tundefined, // auditorStorageService\n\t\t\tconstructs,\n\t\t);\n\n\t\tthis.databaseService = databaseService;\n\t\tthis.id = canonicalId(name);\n\t\tthis.connectionKey = provideKey(this.id, 'publisherConnectionString');\n\n\t\tconst envVar = this.connectionKey;\n\t\tthis.service = {\n\t\t\tserviceName: serviceKey(this.id) as Uncapitalize<TName>,\n\t\t\tasync register({ envParser }) {\n\t\t\t\tconst { connectionString } = envParser\n\t\t\t\t\t.create((get) => ({ connectionString: get(envVar).string() }))\n\t\t\t\t\t.parse();\n\n\t\t\t\treturn Publisher.fromConnectionString<QueueMessage<TName, TMessage>>(\n\t\t\t\t\tconnectionString as EventPublisherConnectionString,\n\t\t\t\t);\n\t\t\t},\n\t\t};\n\t}\n\n\t/**\n\t * What this queue is in the manifest.\n\t *\n\t * A queue is a resource, not a handler: the worker is reached *through* it,\n\t * so the declaration carries the producer's key and nothing about the code\n\t * that drains it. The local target reads this to know which broker has to be\n\t * running and what connection string to inject.\n\t */\n\tdeclare(): Declaration[] {\n\t\treturn [\n\t\t\t{\n\t\t\t\tkind: 'queue',\n\t\t\t\tid: this.id,\n\t\t\t\tprovides: [this.connectionKey],\n\t\t\t\t...(this.fifo ? { fifo: true } : {}),\n\t\t\t\t// Nested rather than a sibling node, because position carries the\n\t\t\t\t// trigger: this handler is reached by messages on this queue and\n\t\t\t\t// by nothing else, so there is no `trigger` field to keep in step.\n\t\t\t\t//\n\t\t\t\t// `handler` is the export name; which directory the build wrote it\n\t\t\t\t// to is the target's business, and a path with a cloud in it does\n\t\t\t\t// not belong in a neutral declaration.\n\t\t\t\tworker: {\n\t\t\t\t\tid: `${this.id}Worker`,\n\t\t\t\t\thandler: `${this.id}.handler`,\n\t\t\t\t\t// Filled by the build from what the worker declared. Empty is a\n\t\t\t\t\t// stated gap, not a claim that it reaches nothing.\n\t\t\t\t\tdependencies: [],\n\t\t\t\t},\n\t\t\t},\n\t\t];\n\t}\n}\n\n/**\n * The context a queue handler receives — a batch of typed messages, and `db`\n * when the queue has a database.\n */\nexport type QueueContext<\n\tTMessage extends StandardSchemaV1,\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTDatabase = undefined,\n> = {\n\tmessages: InferStandardSchema<TMessage>[];\n\tservices: ServiceRecord<TServices>;\n\tlogger: TLogger;\n} & DatabaseContext<TDatabase>;\n\nexport type QueueHandler<\n\tTMessage extends StandardSchemaV1,\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTDatabase = undefined,\n> = (\n\tctx: QueueContext<TMessage, TServices, TLogger, TDatabase>,\n) => unknown | Promise<unknown>;\n"],"mappings":";;;;;;;;;;;;;;;;;;AA4CA,IAAa,QAAb,cAOUA,kBAAAA,UAQR;CA6CgB;CACA;CAMA;CACS;CACA;CACA;CAET;CAEA;CA1DjB,eAAe;CAEf,OAAO,QACN,KACiE;EACjE,OAAO,QACN,OACE,IAAmC,iBAAiB,QACpD,IAAkB,SAAA,8BACrB;CACD;;;;;;;;;CAUA;;;;;;;;;;;CAYA;;;;;CASA;CAEA,YACC,MACA,SAMA,eACA,UAA2C,KAC3C,WAA+C,CAAC,GAChD,SAA2CC,0BAAAA,gBAE3C,WAEA,MAKA,aAAuB,CAAC,GAKxB,iBACC;EACD,MAAA,gCAEC,QACA,UACA,CAAC,GACD,KAAA,GACA,SACA,KAAA,GACA,KAAA,GACA,UACD;EApCgB,KAAA,OAAA;EACA,KAAA,UAAA;EAMA,KAAA,gBAAA;EACS,KAAA,UAAA;EACA,KAAA,WAAA;EACA,KAAA,SAAA;EAET,KAAA,YAAA;EAEA,KAAA,OAAA;EAwBhB,KAAK,kBAAkB;EACvB,KAAK,MAAA,GAAKC,oBAAAA,YAAAA,CAAY,IAAI;EAC1B,KAAK,iBAAA,GAAgBC,oBAAAA,WAAAA,CAAW,KAAK,IAAI,2BAA2B;EAEpE,MAAM,SAAS,KAAK;EACpB,KAAK,UAAU;GACd,cAAA,GAAaC,oBAAAA,WAAAA,CAAW,KAAK,EAAE;GAC/B,MAAM,SAAS,EAAE,aAAa;IAC7B,MAAM,EAAE,qBAAqB,UAC3B,QAAQ,SAAS,EAAE,kBAAkB,IAAI,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAC7D,MAAM;IAER,OAAOC,kBAAAA,UAAU,qBAChB,gBACD;GACD;EACD;CACD;;;;;;;;;CAUA,UAAyB;EACxB,OAAO,CACN;GACC,MAAM;GACN,IAAI,KAAK;GACT,UAAU,CAAC,KAAK,aAAa;GAC7B,GAAI,KAAK,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;GAQlC,QAAQ;IACP,IAAI,GAAG,KAAK,GAAG;IACf,SAAS,GAAG,KAAK,GAAG;IAGpB,cAAc,CAAC;GAChB;EACD,CACD;CACD;AACD"}
@@ -1,6 +1,6 @@
1
1
  import { databaseEdges, idsOf, serviceOf, servicesOf } from "./construct-interface.mjs";
2
2
  import { t as cloneWith } from "./clone-EKNovYHu.mjs";
3
- import { t as Queue } from "./Queue-hwPN-t31.mjs";
3
+ import { t as Queue } from "./Queue-CLRT_j5a.mjs";
4
4
  import { DEFAULT_LOGGER } from "@geekmidas/logger/console";
5
5
  //#region src/queue/QueueBuilder.ts
6
6
  /**
@@ -115,4 +115,4 @@ var QueueNeedsMessage = class extends Error {
115
115
  //#endregion
116
116
  export { QueueNeedsMessage as n, QueueNeedsName as r, QueueBuilder as t };
117
117
 
118
- //# sourceMappingURL=QueueBuilder-DCuYdjMw.mjs.map
118
+ //# sourceMappingURL=QueueBuilder-B0mCqq74.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"QueueBuilder-DCuYdjMw.mjs","names":[],"sources":["../src/queue/QueueBuilder.ts"],"sourcesContent":["import type { Logger } from '@geekmidas/logger';\nimport { DEFAULT_LOGGER } from '@geekmidas/logger/console';\nimport type { Service } from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport { cloneWith } from '../clone';\nimport {\n\ttype Consumable,\n\tdatabaseEdges,\n\tidsOf,\n\ttype ServicesOf,\n\tserviceOf,\n\tservicesOf,\n} from '../construct-interface';\nimport { Queue, type QueueHandler } from './Queue';\n\n/**\n * Builds a {@link Queue} and its one consumer. Reached through\n * `worker.queue('orders')`, never constructed by hand: the worker stamps itself\n * as the owner. `message` is the typed job payload and `handle` the consumer.\n * The queue name is captured as a literal so `queue.service` publishes\n * `{ type: '<name>', payload }`. The handler's `db` is the worker's database\n * unless `.database(other)` names another.\n */\nexport class QueueBuilder<\n\tTName extends string = string,\n\tTMessage extends StandardSchemaV1 | undefined = undefined,\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTDatabase = undefined,\n\tTDatabaseServiceName extends string = string,\n> {\n\tprivate _name?: string;\n\tprivate _messageSchema?: TMessage;\n\tprivate _timeout = 30000;\n\tprivate _batchSize?: number;\n\tprivate _fifo?: boolean;\n\tprivate _services: TServices = [] as Service[] as TServices;\n\t/** The construct ids `.dependsOn()` named — what the manifest records. */\n\tpublic _constructs: string[] = [];\n\tprivate _logger: TLogger = DEFAULT_LOGGER as TLogger;\n\t/** The worker that runs this queue's consumer — stamped by `worker.queue()`. */\n\tpublic _owner?: string;\n\t/** What the handler's `db` comes from — the worker's, unless overridden. */\n\tprivate _databaseService?: Service<TDatabaseServiceName, TDatabase>;\n\t/** The edge `.database()` added, so the next one can replace it. */\n\tprivate _databaseEdge?: string;\n\n\t/** The queue name — drives the infra queue and its `<NAME>_*` env vars. */\n\tqueue<T extends string>(\n\t\tname: T,\n\t): QueueBuilder<\n\t\tT,\n\t\tTMessage,\n\t\tTServices,\n\t\tTLogger,\n\t\tTDatabase,\n\t\tTDatabaseServiceName\n\t> {\n\t\treturn cloneWith(this, { _name: name }) as unknown as QueueBuilder<\n\t\t\tT,\n\t\t\tTMessage,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>;\n\t}\n\n\ttimeout(timeout: number): this {\n\t\treturn cloneWith(this, { _timeout: timeout });\n\t}\n\n\t/** SQS event-source batch size (deployed). */\n\tbatchSize(batchSize: number): this {\n\t\treturn cloneWith(this, { _batchSize: batchSize });\n\t}\n\n\t/** Mark the queue as FIFO (deployed). */\n\tfifo(fifo = true): this {\n\t\treturn cloneWith(this, { _fifo: fifo });\n\t}\n\n\t/**\n\t * Depend on constructs — a database, a bucket, a mail sender, a queue, a\n\t * topic.\n\t *\n\t * It records the edge and dissolves each construct's client into the\n\t * handler's service record under the construct's own id, so\n\t * `.dependsOn([uploads])` is what makes `services.uploads` exist and type.\n\t *\n\t * Constructs only. A `Service` does not match the shape, which is what keeps\n\t * env sniffing confined to `.services()` and the explicit lift.\n\t */\n\tdependsOn<const T extends readonly Consumable[]>(\n\t\tconstructs: T,\n\t): QueueBuilder<\n\t\tTName,\n\t\tTMessage,\n\t\t[...TServices, ...ServicesOf<T>],\n\t\tTLogger,\n\t\tTDatabase,\n\t\tTDatabaseServiceName\n\t> {\n\t\t// Both halves of the edge, from one call and one clone: the services the\n\t\t// handler runs with, and the ids the manifest records. Recording them\n\t\t// separately is what let them drift apart.\n\t\t//\n\t\t// `servicesOf` is the half that validates, so it runs first — recording\n\t\t// ids ahead of it left a caught `NotAConstruct` with `undefined` already\n\t\t// on a `string[]`.\n\t\tconst services = servicesOf(constructs) as unknown as Service[];\n\n\t\treturn cloneWith(this, {\n\t\t\t_services: [...this._services, ...services],\n\t\t\t_constructs: idsOf(constructs, this._constructs),\n\t\t}) as unknown as QueueBuilder<\n\t\t\tTName,\n\t\t\tTMessage,\n\t\t\t[...TServices, ...ServicesOf<T>],\n\t\t\tTLogger,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>;\n\t}\n\n\tservices<T extends Service[]>(\n\t\tservices: T,\n\t): QueueBuilder<\n\t\tTName,\n\t\tTMessage,\n\t\t[...TServices, ...T],\n\t\tTLogger,\n\t\tTDatabase,\n\t\tTDatabaseServiceName\n\t> {\n\t\treturn cloneWith(this, {\n\t\t\t_services: [...this._services, ...services] as unknown as TServices,\n\t\t}) as unknown as QueueBuilder<\n\t\t\tTName,\n\t\t\tTMessage,\n\t\t\t[...TServices, ...T],\n\t\t\tTLogger,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>;\n\t}\n\n\tlogger<T extends Logger>(\n\t\tlogger: T,\n\t): QueueBuilder<\n\t\tTName,\n\t\tTMessage,\n\t\tTServices,\n\t\tT,\n\t\tTDatabase,\n\t\tTDatabaseServiceName\n\t> {\n\t\treturn cloneWith(this, {\n\t\t\t_logger: logger as unknown as TLogger,\n\t\t}) as unknown as QueueBuilder<\n\t\t\tTName,\n\t\t\tTMessage,\n\t\t\tTServices,\n\t\t\tT,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>;\n\t}\n\n\t/**\n\t * The database the handler receives as `db`.\n\t *\n\t * A queue built from a worker already has the worker's, when the worker\n\t * named one with `.database(db)`; this replaces it — the edge as well as\n\t * the client — for a consumer that works against a different database.\n\t */\n\tdatabase<T, TDbName extends string>(\n\t\tsource: Consumable<TDbName, T> | Service<TDbName, T>,\n\t): QueueBuilder<TName, TMessage, TServices, TLogger, T, TDbName> {\n\t\treturn cloneWith(this, {\n\t\t\t_databaseService: serviceOf(source),\n\t\t\t...databaseEdges(\n\t\t\t\t{\n\t\t\t\t\tconstructs: this._constructs,\n\t\t\t\t\tedge: this._databaseEdge,\n\t\t\t\t\tservice: this._databaseService as Service | undefined,\n\t\t\t\t\tservices: this._services,\n\t\t\t\t},\n\t\t\t\tsource,\n\t\t\t),\n\t\t}) as unknown as QueueBuilder<\n\t\t\tTName,\n\t\t\tTMessage,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tT,\n\t\t\tTDbName\n\t\t>;\n\t}\n\n\t/** The typed message (job) payload the queue carries. */\n\tmessage<T extends StandardSchemaV1>(\n\t\tschema: T,\n\t): QueueBuilder<\n\t\tTName,\n\t\tT,\n\t\tTServices,\n\t\tTLogger,\n\t\tTDatabase,\n\t\tTDatabaseServiceName\n\t> {\n\t\treturn cloneWith(this, {\n\t\t\t_messageSchema: schema as unknown as TMessage,\n\t\t}) as unknown as QueueBuilder<\n\t\t\tTName,\n\t\t\tT,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>;\n\t}\n\n\thandle(\n\t\tfn: QueueHandler<NonNullable<TMessage>, TServices, TLogger, TDatabase>,\n\t): Queue<\n\t\tTName,\n\t\tNonNullable<TMessage>,\n\t\tTServices,\n\t\tTLogger,\n\t\tTDatabase,\n\t\tTDatabaseServiceName\n\t> {\n\t\tif (!this._name) throw new QueueNeedsName();\n\t\tif (!this._messageSchema) throw new QueueNeedsMessage(this._name);\n\n\t\tconst queue = new Queue<\n\t\t\tTName,\n\t\t\tNonNullable<TMessage>,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>(\n\t\t\tthis._name as TName,\n\t\t\tfn,\n\t\t\tthis._messageSchema as NonNullable<TMessage>,\n\t\t\tthis._timeout,\n\t\t\tthis._services,\n\t\t\tthis._logger,\n\t\t\tthis._batchSize,\n\t\t\tthis._fifo,\n\t\t\tthis._constructs,\n\t\t\tthis._databaseService,\n\t\t);\n\t\t// Which process runs the consumer: the worker it was built from.\n\t\tqueue.owner = this._owner;\n\n\t\t// No reset. `.handle()` reads this builder and leaves it alone, so a\n\t\t// configured base — `const fn = f.logger(log).timeout(60_000)` — keeps\n\t\t// its configuration for every construct built from it. The reset that\n\t\t// used to be here wiped the base on first use, which made a base usable\n\t\t// exactly once, and quietly reverted the logger to the default.\n\n\t\treturn queue;\n\t}\n}\n\n/** A queue built without a name — only reachable by building one by hand. */\nexport class QueueNeedsName extends Error {\n\tconstructor() {\n\t\tsuper(\n\t\t\t\"A queue needs a name: build it from a worker, worker.queue('Emails').\",\n\t\t);\n\t\tthis.name = 'QueueNeedsName';\n\t}\n}\n\n/** A queue whose message was never described. */\nexport class QueueNeedsMessage extends Error {\n\tconstructor(readonly queue: string) {\n\t\tsuper(\n\t\t\t`Queue '${queue}' has no message: call .message(schema) before .handle(), ` +\n\t\t\t\t'so its producers and its consumer agree on what it carries.',\n\t\t);\n\t\tthis.name = 'QueueNeedsMessage';\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;AAuBA,IAAa,eAAb,MAOE;CACD;CACA;CACA,WAAmB;CACnB;CACA;CACA,YAA+B,CAAC;;CAEhC,cAA+B,CAAC;CAChC,UAA2B;;CAE3B;;CAEA;;CAEA;;CAGA,MACC,MAQC;EACD,OAAO,UAAU,MAAM,EAAE,OAAO,KAAK,CAAC;CAQvC;CAEA,QAAQ,SAAuB;EAC9B,OAAO,UAAU,MAAM,EAAE,UAAU,QAAQ,CAAC;CAC7C;;CAGA,UAAU,WAAyB;EAClC,OAAO,UAAU,MAAM,EAAE,YAAY,UAAU,CAAC;CACjD;;CAGA,KAAK,OAAO,MAAY;EACvB,OAAO,UAAU,MAAM,EAAE,OAAO,KAAK,CAAC;CACvC;;;;;;;;;;;;CAaA,UACC,YAQC;EAQD,MAAM,WAAW,WAAW,UAAU;EAEtC,OAAO,UAAU,MAAM;GACtB,WAAW,CAAC,GAAG,KAAK,WAAW,GAAG,QAAQ;GAC1C,aAAa,MAAM,YAAY,KAAK,WAAW;EAChD,CAAC;CAQF;CAEA,SACC,UAQC;EACD,OAAO,UAAU,MAAM,EACtB,WAAW,CAAC,GAAG,KAAK,WAAW,GAAG,QAAQ,EAC3C,CAAC;CAQF;CAEA,OACC,QAQC;EACD,OAAO,UAAU,MAAM,EACtB,SAAS,OACV,CAAC;CAQF;;;;;;;;CASA,SACC,QACgE;EAChE,OAAO,UAAU,MAAM;GACtB,kBAAkB,UAAU,MAAM;GAClC,GAAG,cACF;IACC,YAAY,KAAK;IACjB,MAAM,KAAK;IACX,SAAS,KAAK;IACd,UAAU,KAAK;GAChB,GACA,MACD;EACD,CAAC;CAQF;;CAGA,QACC,QAQC;EACD,OAAO,UAAU,MAAM,EACtB,gBAAgB,OACjB,CAAC;CAQF;CAEA,OACC,IAQC;EACD,IAAI,CAAC,KAAK,OAAO,MAAM,IAAI,eAAe;EAC1C,IAAI,CAAC,KAAK,gBAAgB,MAAM,IAAI,kBAAkB,KAAK,KAAK;EAEhE,MAAM,QAAQ,IAAI,MAQjB,KAAK,OACL,IACA,KAAK,gBACL,KAAK,UACL,KAAK,WACL,KAAK,SACL,KAAK,YACL,KAAK,OACL,KAAK,aACL,KAAK,gBACN;EAEA,MAAM,QAAQ,KAAK;EAQnB,OAAO;CACR;AACD;;AAGA,IAAa,iBAAb,cAAoC,MAAM;CACzC,cAAc;EACb,MACC,uEACD;EACA,KAAK,OAAO;CACb;AACD;;AAGA,IAAa,oBAAb,cAAuC,MAAM;CACvB;CAArB,YAAY,OAAwB;EACnC,MACC,UAAU,MAAM,sHAEjB;EAJoB,KAAA,QAAA;EAKpB,KAAK,OAAO;CACb;AACD"}
1
+ {"version":3,"file":"QueueBuilder-B0mCqq74.mjs","names":[],"sources":["../src/queue/QueueBuilder.ts"],"sourcesContent":["import type { Logger } from '@geekmidas/logger';\nimport { DEFAULT_LOGGER } from '@geekmidas/logger/console';\nimport type { Service } from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport { cloneWith } from '../clone';\nimport {\n\ttype Consumable,\n\tdatabaseEdges,\n\tidsOf,\n\ttype ServicesOf,\n\tserviceOf,\n\tservicesOf,\n} from '../construct-interface';\nimport { Queue, type QueueHandler } from './Queue';\n\n/**\n * Builds a {@link Queue} and its one consumer. Reached through\n * `worker.queue('orders')`, never constructed by hand: the worker stamps itself\n * as the owner. `message` is the typed job payload and `handle` the consumer.\n * The queue name is captured as a literal so `queue.service` publishes\n * `{ type: '<name>', payload }`. The handler's `db` is the worker's database\n * unless `.database(other)` names another.\n */\nexport class QueueBuilder<\n\tTName extends string = string,\n\tTMessage extends StandardSchemaV1 | undefined = undefined,\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTDatabase = undefined,\n\tTDatabaseServiceName extends string = string,\n> {\n\tprivate _name?: string;\n\tprivate _messageSchema?: TMessage;\n\tprivate _timeout = 30000;\n\tprivate _batchSize?: number;\n\tprivate _fifo?: boolean;\n\tprivate _services: TServices = [] as Service[] as TServices;\n\t/** The construct ids `.dependsOn()` named — what the manifest records. */\n\tpublic _constructs: string[] = [];\n\tprivate _logger: TLogger = DEFAULT_LOGGER as TLogger;\n\t/** The worker that runs this queue's consumer — stamped by `worker.queue()`. */\n\tpublic _owner?: string;\n\t/** What the handler's `db` comes from — the worker's, unless overridden. */\n\tprivate _databaseService?: Service<TDatabaseServiceName, TDatabase>;\n\t/** The edge `.database()` added, so the next one can replace it. */\n\tprivate _databaseEdge?: string;\n\n\t/** The queue name — drives the infra queue and its `<NAME>_*` env vars. */\n\tqueue<T extends string>(\n\t\tname: T,\n\t): QueueBuilder<\n\t\tT,\n\t\tTMessage,\n\t\tTServices,\n\t\tTLogger,\n\t\tTDatabase,\n\t\tTDatabaseServiceName\n\t> {\n\t\treturn cloneWith(this, { _name: name }) as unknown as QueueBuilder<\n\t\t\tT,\n\t\t\tTMessage,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>;\n\t}\n\n\ttimeout(timeout: number): this {\n\t\treturn cloneWith(this, { _timeout: timeout });\n\t}\n\n\t/** SQS event-source batch size (deployed). */\n\tbatchSize(batchSize: number): this {\n\t\treturn cloneWith(this, { _batchSize: batchSize });\n\t}\n\n\t/** Mark the queue as FIFO (deployed). */\n\tfifo(fifo = true): this {\n\t\treturn cloneWith(this, { _fifo: fifo });\n\t}\n\n\t/**\n\t * Depend on constructs — a database, a bucket, a mail sender, a queue, a\n\t * topic.\n\t *\n\t * It records the edge and dissolves each construct's client into the\n\t * handler's service record under the construct's own id, so\n\t * `.dependsOn([uploads])` is what makes `services.uploads` exist and type.\n\t *\n\t * Constructs only. A `Service` does not match the shape, which is what keeps\n\t * env sniffing confined to `.services()` and the explicit lift.\n\t */\n\tdependsOn<const T extends readonly Consumable[]>(\n\t\tconstructs: T,\n\t): QueueBuilder<\n\t\tTName,\n\t\tTMessage,\n\t\t[...TServices, ...ServicesOf<T>],\n\t\tTLogger,\n\t\tTDatabase,\n\t\tTDatabaseServiceName\n\t> {\n\t\t// Both halves of the edge, from one call and one clone: the services the\n\t\t// handler runs with, and the ids the manifest records. Recording them\n\t\t// separately is what let them drift apart.\n\t\t//\n\t\t// `servicesOf` is the half that validates, so it runs first — recording\n\t\t// ids ahead of it left a caught `NotAConstruct` with `undefined` already\n\t\t// on a `string[]`.\n\t\tconst services = servicesOf(constructs) as unknown as Service[];\n\n\t\treturn cloneWith(this, {\n\t\t\t_services: [...this._services, ...services],\n\t\t\t_constructs: idsOf(constructs, this._constructs),\n\t\t}) as unknown as QueueBuilder<\n\t\t\tTName,\n\t\t\tTMessage,\n\t\t\t[...TServices, ...ServicesOf<T>],\n\t\t\tTLogger,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>;\n\t}\n\n\tservices<T extends Service[]>(\n\t\tservices: T,\n\t): QueueBuilder<\n\t\tTName,\n\t\tTMessage,\n\t\t[...TServices, ...T],\n\t\tTLogger,\n\t\tTDatabase,\n\t\tTDatabaseServiceName\n\t> {\n\t\treturn cloneWith(this, {\n\t\t\t_services: [...this._services, ...services] as unknown as TServices,\n\t\t}) as unknown as QueueBuilder<\n\t\t\tTName,\n\t\t\tTMessage,\n\t\t\t[...TServices, ...T],\n\t\t\tTLogger,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>;\n\t}\n\n\tlogger<T extends Logger>(\n\t\tlogger: T,\n\t): QueueBuilder<\n\t\tTName,\n\t\tTMessage,\n\t\tTServices,\n\t\tT,\n\t\tTDatabase,\n\t\tTDatabaseServiceName\n\t> {\n\t\treturn cloneWith(this, {\n\t\t\t_logger: logger as unknown as TLogger,\n\t\t}) as unknown as QueueBuilder<\n\t\t\tTName,\n\t\t\tTMessage,\n\t\t\tTServices,\n\t\t\tT,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>;\n\t}\n\n\t/**\n\t * The database the handler receives as `db`.\n\t *\n\t * A queue built from a worker already has the worker's, when the worker\n\t * named one with `.database(db)`; this replaces it — the edge as well as\n\t * the client — for a consumer that works against a different database.\n\t */\n\tdatabase<T, TDbName extends string>(\n\t\tsource: Consumable<TDbName, T> | Service<TDbName, T>,\n\t): QueueBuilder<TName, TMessage, TServices, TLogger, T, TDbName> {\n\t\treturn cloneWith(this, {\n\t\t\t_databaseService: serviceOf(source),\n\t\t\t...databaseEdges(\n\t\t\t\t{\n\t\t\t\t\tconstructs: this._constructs,\n\t\t\t\t\tedge: this._databaseEdge,\n\t\t\t\t\tservice: this._databaseService as Service | undefined,\n\t\t\t\t\tservices: this._services,\n\t\t\t\t},\n\t\t\t\tsource,\n\t\t\t),\n\t\t}) as unknown as QueueBuilder<\n\t\t\tTName,\n\t\t\tTMessage,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tT,\n\t\t\tTDbName\n\t\t>;\n\t}\n\n\t/** The typed message (job) payload the queue carries. */\n\tmessage<T extends StandardSchemaV1>(\n\t\tschema: T,\n\t): QueueBuilder<\n\t\tTName,\n\t\tT,\n\t\tTServices,\n\t\tTLogger,\n\t\tTDatabase,\n\t\tTDatabaseServiceName\n\t> {\n\t\treturn cloneWith(this, {\n\t\t\t_messageSchema: schema as unknown as TMessage,\n\t\t}) as unknown as QueueBuilder<\n\t\t\tTName,\n\t\t\tT,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>;\n\t}\n\n\thandle(\n\t\tfn: QueueHandler<NonNullable<TMessage>, TServices, TLogger, TDatabase>,\n\t): Queue<\n\t\tTName,\n\t\tNonNullable<TMessage>,\n\t\tTServices,\n\t\tTLogger,\n\t\tTDatabase,\n\t\tTDatabaseServiceName\n\t> {\n\t\tif (!this._name) throw new QueueNeedsName();\n\t\tif (!this._messageSchema) throw new QueueNeedsMessage(this._name);\n\n\t\tconst queue = new Queue<\n\t\t\tTName,\n\t\t\tNonNullable<TMessage>,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>(\n\t\t\tthis._name as TName,\n\t\t\tfn,\n\t\t\tthis._messageSchema as NonNullable<TMessage>,\n\t\t\tthis._timeout,\n\t\t\tthis._services,\n\t\t\tthis._logger,\n\t\t\tthis._batchSize,\n\t\t\tthis._fifo,\n\t\t\tthis._constructs,\n\t\t\tthis._databaseService,\n\t\t);\n\t\t// Which process runs the consumer: the worker it was built from.\n\t\tqueue.owner = this._owner;\n\n\t\t// No reset. `.handle()` reads this builder and leaves it alone, so a\n\t\t// configured base — `const fn = f.logger(log).timeout(60_000)` — keeps\n\t\t// its configuration for every construct built from it. The reset that\n\t\t// used to be here wiped the base on first use, which made a base usable\n\t\t// exactly once, and quietly reverted the logger to the default.\n\n\t\treturn queue;\n\t}\n}\n\n/** A queue built without a name — only reachable by building one by hand. */\nexport class QueueNeedsName extends Error {\n\tconstructor() {\n\t\tsuper(\n\t\t\t\"A queue needs a name: build it from a worker, worker.queue('Emails').\",\n\t\t);\n\t\tthis.name = 'QueueNeedsName';\n\t}\n}\n\n/** A queue whose message was never described. */\nexport class QueueNeedsMessage extends Error {\n\tconstructor(readonly queue: string) {\n\t\tsuper(\n\t\t\t`Queue '${queue}' has no message: call .message(schema) before .handle(), ` +\n\t\t\t\t'so its producers and its consumer agree on what it carries.',\n\t\t);\n\t\tthis.name = 'QueueNeedsMessage';\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;AAuBA,IAAa,eAAb,MAOE;CACD;CACA;CACA,WAAmB;CACnB;CACA;CACA,YAA+B,CAAC;;CAEhC,cAA+B,CAAC;CAChC,UAA2B;;CAE3B;;CAEA;;CAEA;;CAGA,MACC,MAQC;EACD,OAAO,UAAU,MAAM,EAAE,OAAO,KAAK,CAAC;CAQvC;CAEA,QAAQ,SAAuB;EAC9B,OAAO,UAAU,MAAM,EAAE,UAAU,QAAQ,CAAC;CAC7C;;CAGA,UAAU,WAAyB;EAClC,OAAO,UAAU,MAAM,EAAE,YAAY,UAAU,CAAC;CACjD;;CAGA,KAAK,OAAO,MAAY;EACvB,OAAO,UAAU,MAAM,EAAE,OAAO,KAAK,CAAC;CACvC;;;;;;;;;;;;CAaA,UACC,YAQC;EAQD,MAAM,WAAW,WAAW,UAAU;EAEtC,OAAO,UAAU,MAAM;GACtB,WAAW,CAAC,GAAG,KAAK,WAAW,GAAG,QAAQ;GAC1C,aAAa,MAAM,YAAY,KAAK,WAAW;EAChD,CAAC;CAQF;CAEA,SACC,UAQC;EACD,OAAO,UAAU,MAAM,EACtB,WAAW,CAAC,GAAG,KAAK,WAAW,GAAG,QAAQ,EAC3C,CAAC;CAQF;CAEA,OACC,QAQC;EACD,OAAO,UAAU,MAAM,EACtB,SAAS,OACV,CAAC;CAQF;;;;;;;;CASA,SACC,QACgE;EAChE,OAAO,UAAU,MAAM;GACtB,kBAAkB,UAAU,MAAM;GAClC,GAAG,cACF;IACC,YAAY,KAAK;IACjB,MAAM,KAAK;IACX,SAAS,KAAK;IACd,UAAU,KAAK;GAChB,GACA,MACD;EACD,CAAC;CAQF;;CAGA,QACC,QAQC;EACD,OAAO,UAAU,MAAM,EACtB,gBAAgB,OACjB,CAAC;CAQF;CAEA,OACC,IAQC;EACD,IAAI,CAAC,KAAK,OAAO,MAAM,IAAI,eAAe;EAC1C,IAAI,CAAC,KAAK,gBAAgB,MAAM,IAAI,kBAAkB,KAAK,KAAK;EAEhE,MAAM,QAAQ,IAAI,MAQjB,KAAK,OACL,IACA,KAAK,gBACL,KAAK,UACL,KAAK,WACL,KAAK,SACL,KAAK,YACL,KAAK,OACL,KAAK,aACL,KAAK,gBACN;EAEA,MAAM,QAAQ,KAAK;EAQnB,OAAO;CACR;AACD;;AAGA,IAAa,iBAAb,cAAoC,MAAM;CACzC,cAAc;EACb,MACC,uEACD;EACA,KAAK,OAAO;CACb;AACD;;AAGA,IAAa,oBAAb,cAAuC,MAAM;CACvB;CAArB,YAAY,OAAwB;EACnC,MACC,UAAU,MAAM,sHAEjB;EAJoB,KAAA,QAAA;EAKpB,KAAK,OAAO;CACb;AACD"}
@@ -1,6 +1,6 @@
1
1
  const require_construct_interface = require("./construct-interface.cjs");
2
2
  const require_clone = require("./clone-UGJpvQnL.cjs");
3
- const require_Queue = require("./Queue-D5jcw0mu.cjs");
3
+ const require_Queue = require("./Queue-umpX3683.cjs");
4
4
  let _geekmidas_logger_console = require("@geekmidas/logger/console");
5
5
  //#region src/queue/QueueBuilder.ts
6
6
  /**
@@ -132,4 +132,4 @@ Object.defineProperty(exports, "QueueNeedsName", {
132
132
  }
133
133
  });
134
134
 
135
- //# sourceMappingURL=QueueBuilder-BFQn3OzZ.cjs.map
135
+ //# sourceMappingURL=QueueBuilder-B9ClmXjX.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"QueueBuilder-BFQn3OzZ.cjs","names":["DEFAULT_LOGGER","cloneWith","servicesOf","idsOf","serviceOf","databaseEdges","Queue"],"sources":["../src/queue/QueueBuilder.ts"],"sourcesContent":["import type { Logger } from '@geekmidas/logger';\nimport { DEFAULT_LOGGER } from '@geekmidas/logger/console';\nimport type { Service } from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport { cloneWith } from '../clone';\nimport {\n\ttype Consumable,\n\tdatabaseEdges,\n\tidsOf,\n\ttype ServicesOf,\n\tserviceOf,\n\tservicesOf,\n} from '../construct-interface';\nimport { Queue, type QueueHandler } from './Queue';\n\n/**\n * Builds a {@link Queue} and its one consumer. Reached through\n * `worker.queue('orders')`, never constructed by hand: the worker stamps itself\n * as the owner. `message` is the typed job payload and `handle` the consumer.\n * The queue name is captured as a literal so `queue.service` publishes\n * `{ type: '<name>', payload }`. The handler's `db` is the worker's database\n * unless `.database(other)` names another.\n */\nexport class QueueBuilder<\n\tTName extends string = string,\n\tTMessage extends StandardSchemaV1 | undefined = undefined,\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTDatabase = undefined,\n\tTDatabaseServiceName extends string = string,\n> {\n\tprivate _name?: string;\n\tprivate _messageSchema?: TMessage;\n\tprivate _timeout = 30000;\n\tprivate _batchSize?: number;\n\tprivate _fifo?: boolean;\n\tprivate _services: TServices = [] as Service[] as TServices;\n\t/** The construct ids `.dependsOn()` named — what the manifest records. */\n\tpublic _constructs: string[] = [];\n\tprivate _logger: TLogger = DEFAULT_LOGGER as TLogger;\n\t/** The worker that runs this queue's consumer — stamped by `worker.queue()`. */\n\tpublic _owner?: string;\n\t/** What the handler's `db` comes from — the worker's, unless overridden. */\n\tprivate _databaseService?: Service<TDatabaseServiceName, TDatabase>;\n\t/** The edge `.database()` added, so the next one can replace it. */\n\tprivate _databaseEdge?: string;\n\n\t/** The queue name — drives the infra queue and its `<NAME>_*` env vars. */\n\tqueue<T extends string>(\n\t\tname: T,\n\t): QueueBuilder<\n\t\tT,\n\t\tTMessage,\n\t\tTServices,\n\t\tTLogger,\n\t\tTDatabase,\n\t\tTDatabaseServiceName\n\t> {\n\t\treturn cloneWith(this, { _name: name }) as unknown as QueueBuilder<\n\t\t\tT,\n\t\t\tTMessage,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>;\n\t}\n\n\ttimeout(timeout: number): this {\n\t\treturn cloneWith(this, { _timeout: timeout });\n\t}\n\n\t/** SQS event-source batch size (deployed). */\n\tbatchSize(batchSize: number): this {\n\t\treturn cloneWith(this, { _batchSize: batchSize });\n\t}\n\n\t/** Mark the queue as FIFO (deployed). */\n\tfifo(fifo = true): this {\n\t\treturn cloneWith(this, { _fifo: fifo });\n\t}\n\n\t/**\n\t * Depend on constructs — a database, a bucket, a mail sender, a queue, a\n\t * topic.\n\t *\n\t * It records the edge and dissolves each construct's client into the\n\t * handler's service record under the construct's own id, so\n\t * `.dependsOn([uploads])` is what makes `services.uploads` exist and type.\n\t *\n\t * Constructs only. A `Service` does not match the shape, which is what keeps\n\t * env sniffing confined to `.services()` and the explicit lift.\n\t */\n\tdependsOn<const T extends readonly Consumable[]>(\n\t\tconstructs: T,\n\t): QueueBuilder<\n\t\tTName,\n\t\tTMessage,\n\t\t[...TServices, ...ServicesOf<T>],\n\t\tTLogger,\n\t\tTDatabase,\n\t\tTDatabaseServiceName\n\t> {\n\t\t// Both halves of the edge, from one call and one clone: the services the\n\t\t// handler runs with, and the ids the manifest records. Recording them\n\t\t// separately is what let them drift apart.\n\t\t//\n\t\t// `servicesOf` is the half that validates, so it runs first — recording\n\t\t// ids ahead of it left a caught `NotAConstruct` with `undefined` already\n\t\t// on a `string[]`.\n\t\tconst services = servicesOf(constructs) as unknown as Service[];\n\n\t\treturn cloneWith(this, {\n\t\t\t_services: [...this._services, ...services],\n\t\t\t_constructs: idsOf(constructs, this._constructs),\n\t\t}) as unknown as QueueBuilder<\n\t\t\tTName,\n\t\t\tTMessage,\n\t\t\t[...TServices, ...ServicesOf<T>],\n\t\t\tTLogger,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>;\n\t}\n\n\tservices<T extends Service[]>(\n\t\tservices: T,\n\t): QueueBuilder<\n\t\tTName,\n\t\tTMessage,\n\t\t[...TServices, ...T],\n\t\tTLogger,\n\t\tTDatabase,\n\t\tTDatabaseServiceName\n\t> {\n\t\treturn cloneWith(this, {\n\t\t\t_services: [...this._services, ...services] as unknown as TServices,\n\t\t}) as unknown as QueueBuilder<\n\t\t\tTName,\n\t\t\tTMessage,\n\t\t\t[...TServices, ...T],\n\t\t\tTLogger,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>;\n\t}\n\n\tlogger<T extends Logger>(\n\t\tlogger: T,\n\t): QueueBuilder<\n\t\tTName,\n\t\tTMessage,\n\t\tTServices,\n\t\tT,\n\t\tTDatabase,\n\t\tTDatabaseServiceName\n\t> {\n\t\treturn cloneWith(this, {\n\t\t\t_logger: logger as unknown as TLogger,\n\t\t}) as unknown as QueueBuilder<\n\t\t\tTName,\n\t\t\tTMessage,\n\t\t\tTServices,\n\t\t\tT,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>;\n\t}\n\n\t/**\n\t * The database the handler receives as `db`.\n\t *\n\t * A queue built from a worker already has the worker's, when the worker\n\t * named one with `.database(db)`; this replaces it — the edge as well as\n\t * the client — for a consumer that works against a different database.\n\t */\n\tdatabase<T, TDbName extends string>(\n\t\tsource: Consumable<TDbName, T> | Service<TDbName, T>,\n\t): QueueBuilder<TName, TMessage, TServices, TLogger, T, TDbName> {\n\t\treturn cloneWith(this, {\n\t\t\t_databaseService: serviceOf(source),\n\t\t\t...databaseEdges(\n\t\t\t\t{\n\t\t\t\t\tconstructs: this._constructs,\n\t\t\t\t\tedge: this._databaseEdge,\n\t\t\t\t\tservice: this._databaseService as Service | undefined,\n\t\t\t\t\tservices: this._services,\n\t\t\t\t},\n\t\t\t\tsource,\n\t\t\t),\n\t\t}) as unknown as QueueBuilder<\n\t\t\tTName,\n\t\t\tTMessage,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tT,\n\t\t\tTDbName\n\t\t>;\n\t}\n\n\t/** The typed message (job) payload the queue carries. */\n\tmessage<T extends StandardSchemaV1>(\n\t\tschema: T,\n\t): QueueBuilder<\n\t\tTName,\n\t\tT,\n\t\tTServices,\n\t\tTLogger,\n\t\tTDatabase,\n\t\tTDatabaseServiceName\n\t> {\n\t\treturn cloneWith(this, {\n\t\t\t_messageSchema: schema as unknown as TMessage,\n\t\t}) as unknown as QueueBuilder<\n\t\t\tTName,\n\t\t\tT,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>;\n\t}\n\n\thandle(\n\t\tfn: QueueHandler<NonNullable<TMessage>, TServices, TLogger, TDatabase>,\n\t): Queue<\n\t\tTName,\n\t\tNonNullable<TMessage>,\n\t\tTServices,\n\t\tTLogger,\n\t\tTDatabase,\n\t\tTDatabaseServiceName\n\t> {\n\t\tif (!this._name) throw new QueueNeedsName();\n\t\tif (!this._messageSchema) throw new QueueNeedsMessage(this._name);\n\n\t\tconst queue = new Queue<\n\t\t\tTName,\n\t\t\tNonNullable<TMessage>,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>(\n\t\t\tthis._name as TName,\n\t\t\tfn,\n\t\t\tthis._messageSchema as NonNullable<TMessage>,\n\t\t\tthis._timeout,\n\t\t\tthis._services,\n\t\t\tthis._logger,\n\t\t\tthis._batchSize,\n\t\t\tthis._fifo,\n\t\t\tthis._constructs,\n\t\t\tthis._databaseService,\n\t\t);\n\t\t// Which process runs the consumer: the worker it was built from.\n\t\tqueue.owner = this._owner;\n\n\t\t// No reset. `.handle()` reads this builder and leaves it alone, so a\n\t\t// configured base — `const fn = f.logger(log).timeout(60_000)` — keeps\n\t\t// its configuration for every construct built from it. The reset that\n\t\t// used to be here wiped the base on first use, which made a base usable\n\t\t// exactly once, and quietly reverted the logger to the default.\n\n\t\treturn queue;\n\t}\n}\n\n/** A queue built without a name — only reachable by building one by hand. */\nexport class QueueNeedsName extends Error {\n\tconstructor() {\n\t\tsuper(\n\t\t\t\"A queue needs a name: build it from a worker, worker.queue('Emails').\",\n\t\t);\n\t\tthis.name = 'QueueNeedsName';\n\t}\n}\n\n/** A queue whose message was never described. */\nexport class QueueNeedsMessage extends Error {\n\tconstructor(readonly queue: string) {\n\t\tsuper(\n\t\t\t`Queue '${queue}' has no message: call .message(schema) before .handle(), ` +\n\t\t\t\t'so its producers and its consumer agree on what it carries.',\n\t\t);\n\t\tthis.name = 'QueueNeedsMessage';\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;AAuBA,IAAa,eAAb,MAOE;CACD;CACA;CACA,WAAmB;CACnB;CACA;CACA,YAA+B,CAAC;;CAEhC,cAA+B,CAAC;CAChC,UAA2BA,0BAAAA;;CAE3B;;CAEA;;CAEA;;CAGA,MACC,MAQC;EACD,OAAOC,cAAAA,UAAU,MAAM,EAAE,OAAO,KAAK,CAAC;CAQvC;CAEA,QAAQ,SAAuB;EAC9B,OAAOA,cAAAA,UAAU,MAAM,EAAE,UAAU,QAAQ,CAAC;CAC7C;;CAGA,UAAU,WAAyB;EAClC,OAAOA,cAAAA,UAAU,MAAM,EAAE,YAAY,UAAU,CAAC;CACjD;;CAGA,KAAK,OAAO,MAAY;EACvB,OAAOA,cAAAA,UAAU,MAAM,EAAE,OAAO,KAAK,CAAC;CACvC;;;;;;;;;;;;CAaA,UACC,YAQC;EAQD,MAAM,WAAWC,4BAAAA,WAAW,UAAU;EAEtC,OAAOD,cAAAA,UAAU,MAAM;GACtB,WAAW,CAAC,GAAG,KAAK,WAAW,GAAG,QAAQ;GAC1C,aAAaE,4BAAAA,MAAM,YAAY,KAAK,WAAW;EAChD,CAAC;CAQF;CAEA,SACC,UAQC;EACD,OAAOF,cAAAA,UAAU,MAAM,EACtB,WAAW,CAAC,GAAG,KAAK,WAAW,GAAG,QAAQ,EAC3C,CAAC;CAQF;CAEA,OACC,QAQC;EACD,OAAOA,cAAAA,UAAU,MAAM,EACtB,SAAS,OACV,CAAC;CAQF;;;;;;;;CASA,SACC,QACgE;EAChE,OAAOA,cAAAA,UAAU,MAAM;GACtB,kBAAkBG,4BAAAA,UAAU,MAAM;GAClC,GAAGC,4BAAAA,cACF;IACC,YAAY,KAAK;IACjB,MAAM,KAAK;IACX,SAAS,KAAK;IACd,UAAU,KAAK;GAChB,GACA,MACD;EACD,CAAC;CAQF;;CAGA,QACC,QAQC;EACD,OAAOJ,cAAAA,UAAU,MAAM,EACtB,gBAAgB,OACjB,CAAC;CAQF;CAEA,OACC,IAQC;EACD,IAAI,CAAC,KAAK,OAAO,MAAM,IAAI,eAAe;EAC1C,IAAI,CAAC,KAAK,gBAAgB,MAAM,IAAI,kBAAkB,KAAK,KAAK;EAEhE,MAAM,QAAQ,IAAIK,cAAAA,MAQjB,KAAK,OACL,IACA,KAAK,gBACL,KAAK,UACL,KAAK,WACL,KAAK,SACL,KAAK,YACL,KAAK,OACL,KAAK,aACL,KAAK,gBACN;EAEA,MAAM,QAAQ,KAAK;EAQnB,OAAO;CACR;AACD;;AAGA,IAAa,iBAAb,cAAoC,MAAM;CACzC,cAAc;EACb,MACC,uEACD;EACA,KAAK,OAAO;CACb;AACD;;AAGA,IAAa,oBAAb,cAAuC,MAAM;CACvB;CAArB,YAAY,OAAwB;EACnC,MACC,UAAU,MAAM,sHAEjB;EAJoB,KAAA,QAAA;EAKpB,KAAK,OAAO;CACb;AACD"}
1
+ {"version":3,"file":"QueueBuilder-B9ClmXjX.cjs","names":["DEFAULT_LOGGER","cloneWith","servicesOf","idsOf","serviceOf","databaseEdges","Queue"],"sources":["../src/queue/QueueBuilder.ts"],"sourcesContent":["import type { Logger } from '@geekmidas/logger';\nimport { DEFAULT_LOGGER } from '@geekmidas/logger/console';\nimport type { Service } from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport { cloneWith } from '../clone';\nimport {\n\ttype Consumable,\n\tdatabaseEdges,\n\tidsOf,\n\ttype ServicesOf,\n\tserviceOf,\n\tservicesOf,\n} from '../construct-interface';\nimport { Queue, type QueueHandler } from './Queue';\n\n/**\n * Builds a {@link Queue} and its one consumer. Reached through\n * `worker.queue('orders')`, never constructed by hand: the worker stamps itself\n * as the owner. `message` is the typed job payload and `handle` the consumer.\n * The queue name is captured as a literal so `queue.service` publishes\n * `{ type: '<name>', payload }`. The handler's `db` is the worker's database\n * unless `.database(other)` names another.\n */\nexport class QueueBuilder<\n\tTName extends string = string,\n\tTMessage extends StandardSchemaV1 | undefined = undefined,\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTDatabase = undefined,\n\tTDatabaseServiceName extends string = string,\n> {\n\tprivate _name?: string;\n\tprivate _messageSchema?: TMessage;\n\tprivate _timeout = 30000;\n\tprivate _batchSize?: number;\n\tprivate _fifo?: boolean;\n\tprivate _services: TServices = [] as Service[] as TServices;\n\t/** The construct ids `.dependsOn()` named — what the manifest records. */\n\tpublic _constructs: string[] = [];\n\tprivate _logger: TLogger = DEFAULT_LOGGER as TLogger;\n\t/** The worker that runs this queue's consumer — stamped by `worker.queue()`. */\n\tpublic _owner?: string;\n\t/** What the handler's `db` comes from — the worker's, unless overridden. */\n\tprivate _databaseService?: Service<TDatabaseServiceName, TDatabase>;\n\t/** The edge `.database()` added, so the next one can replace it. */\n\tprivate _databaseEdge?: string;\n\n\t/** The queue name — drives the infra queue and its `<NAME>_*` env vars. */\n\tqueue<T extends string>(\n\t\tname: T,\n\t): QueueBuilder<\n\t\tT,\n\t\tTMessage,\n\t\tTServices,\n\t\tTLogger,\n\t\tTDatabase,\n\t\tTDatabaseServiceName\n\t> {\n\t\treturn cloneWith(this, { _name: name }) as unknown as QueueBuilder<\n\t\t\tT,\n\t\t\tTMessage,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>;\n\t}\n\n\ttimeout(timeout: number): this {\n\t\treturn cloneWith(this, { _timeout: timeout });\n\t}\n\n\t/** SQS event-source batch size (deployed). */\n\tbatchSize(batchSize: number): this {\n\t\treturn cloneWith(this, { _batchSize: batchSize });\n\t}\n\n\t/** Mark the queue as FIFO (deployed). */\n\tfifo(fifo = true): this {\n\t\treturn cloneWith(this, { _fifo: fifo });\n\t}\n\n\t/**\n\t * Depend on constructs — a database, a bucket, a mail sender, a queue, a\n\t * topic.\n\t *\n\t * It records the edge and dissolves each construct's client into the\n\t * handler's service record under the construct's own id, so\n\t * `.dependsOn([uploads])` is what makes `services.uploads` exist and type.\n\t *\n\t * Constructs only. A `Service` does not match the shape, which is what keeps\n\t * env sniffing confined to `.services()` and the explicit lift.\n\t */\n\tdependsOn<const T extends readonly Consumable[]>(\n\t\tconstructs: T,\n\t): QueueBuilder<\n\t\tTName,\n\t\tTMessage,\n\t\t[...TServices, ...ServicesOf<T>],\n\t\tTLogger,\n\t\tTDatabase,\n\t\tTDatabaseServiceName\n\t> {\n\t\t// Both halves of the edge, from one call and one clone: the services the\n\t\t// handler runs with, and the ids the manifest records. Recording them\n\t\t// separately is what let them drift apart.\n\t\t//\n\t\t// `servicesOf` is the half that validates, so it runs first — recording\n\t\t// ids ahead of it left a caught `NotAConstruct` with `undefined` already\n\t\t// on a `string[]`.\n\t\tconst services = servicesOf(constructs) as unknown as Service[];\n\n\t\treturn cloneWith(this, {\n\t\t\t_services: [...this._services, ...services],\n\t\t\t_constructs: idsOf(constructs, this._constructs),\n\t\t}) as unknown as QueueBuilder<\n\t\t\tTName,\n\t\t\tTMessage,\n\t\t\t[...TServices, ...ServicesOf<T>],\n\t\t\tTLogger,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>;\n\t}\n\n\tservices<T extends Service[]>(\n\t\tservices: T,\n\t): QueueBuilder<\n\t\tTName,\n\t\tTMessage,\n\t\t[...TServices, ...T],\n\t\tTLogger,\n\t\tTDatabase,\n\t\tTDatabaseServiceName\n\t> {\n\t\treturn cloneWith(this, {\n\t\t\t_services: [...this._services, ...services] as unknown as TServices,\n\t\t}) as unknown as QueueBuilder<\n\t\t\tTName,\n\t\t\tTMessage,\n\t\t\t[...TServices, ...T],\n\t\t\tTLogger,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>;\n\t}\n\n\tlogger<T extends Logger>(\n\t\tlogger: T,\n\t): QueueBuilder<\n\t\tTName,\n\t\tTMessage,\n\t\tTServices,\n\t\tT,\n\t\tTDatabase,\n\t\tTDatabaseServiceName\n\t> {\n\t\treturn cloneWith(this, {\n\t\t\t_logger: logger as unknown as TLogger,\n\t\t}) as unknown as QueueBuilder<\n\t\t\tTName,\n\t\t\tTMessage,\n\t\t\tTServices,\n\t\t\tT,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>;\n\t}\n\n\t/**\n\t * The database the handler receives as `db`.\n\t *\n\t * A queue built from a worker already has the worker's, when the worker\n\t * named one with `.database(db)`; this replaces it — the edge as well as\n\t * the client — for a consumer that works against a different database.\n\t */\n\tdatabase<T, TDbName extends string>(\n\t\tsource: Consumable<TDbName, T> | Service<TDbName, T>,\n\t): QueueBuilder<TName, TMessage, TServices, TLogger, T, TDbName> {\n\t\treturn cloneWith(this, {\n\t\t\t_databaseService: serviceOf(source),\n\t\t\t...databaseEdges(\n\t\t\t\t{\n\t\t\t\t\tconstructs: this._constructs,\n\t\t\t\t\tedge: this._databaseEdge,\n\t\t\t\t\tservice: this._databaseService as Service | undefined,\n\t\t\t\t\tservices: this._services,\n\t\t\t\t},\n\t\t\t\tsource,\n\t\t\t),\n\t\t}) as unknown as QueueBuilder<\n\t\t\tTName,\n\t\t\tTMessage,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tT,\n\t\t\tTDbName\n\t\t>;\n\t}\n\n\t/** The typed message (job) payload the queue carries. */\n\tmessage<T extends StandardSchemaV1>(\n\t\tschema: T,\n\t): QueueBuilder<\n\t\tTName,\n\t\tT,\n\t\tTServices,\n\t\tTLogger,\n\t\tTDatabase,\n\t\tTDatabaseServiceName\n\t> {\n\t\treturn cloneWith(this, {\n\t\t\t_messageSchema: schema as unknown as TMessage,\n\t\t}) as unknown as QueueBuilder<\n\t\t\tTName,\n\t\t\tT,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>;\n\t}\n\n\thandle(\n\t\tfn: QueueHandler<NonNullable<TMessage>, TServices, TLogger, TDatabase>,\n\t): Queue<\n\t\tTName,\n\t\tNonNullable<TMessage>,\n\t\tTServices,\n\t\tTLogger,\n\t\tTDatabase,\n\t\tTDatabaseServiceName\n\t> {\n\t\tif (!this._name) throw new QueueNeedsName();\n\t\tif (!this._messageSchema) throw new QueueNeedsMessage(this._name);\n\n\t\tconst queue = new Queue<\n\t\t\tTName,\n\t\t\tNonNullable<TMessage>,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>(\n\t\t\tthis._name as TName,\n\t\t\tfn,\n\t\t\tthis._messageSchema as NonNullable<TMessage>,\n\t\t\tthis._timeout,\n\t\t\tthis._services,\n\t\t\tthis._logger,\n\t\t\tthis._batchSize,\n\t\t\tthis._fifo,\n\t\t\tthis._constructs,\n\t\t\tthis._databaseService,\n\t\t);\n\t\t// Which process runs the consumer: the worker it was built from.\n\t\tqueue.owner = this._owner;\n\n\t\t// No reset. `.handle()` reads this builder and leaves it alone, so a\n\t\t// configured base — `const fn = f.logger(log).timeout(60_000)` — keeps\n\t\t// its configuration for every construct built from it. The reset that\n\t\t// used to be here wiped the base on first use, which made a base usable\n\t\t// exactly once, and quietly reverted the logger to the default.\n\n\t\treturn queue;\n\t}\n}\n\n/** A queue built without a name — only reachable by building one by hand. */\nexport class QueueNeedsName extends Error {\n\tconstructor() {\n\t\tsuper(\n\t\t\t\"A queue needs a name: build it from a worker, worker.queue('Emails').\",\n\t\t);\n\t\tthis.name = 'QueueNeedsName';\n\t}\n}\n\n/** A queue whose message was never described. */\nexport class QueueNeedsMessage extends Error {\n\tconstructor(readonly queue: string) {\n\t\tsuper(\n\t\t\t`Queue '${queue}' has no message: call .message(schema) before .handle(), ` +\n\t\t\t\t'so its producers and its consumer agree on what it carries.',\n\t\t);\n\t\tthis.name = 'QueueNeedsMessage';\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;AAuBA,IAAa,eAAb,MAOE;CACD;CACA;CACA,WAAmB;CACnB;CACA;CACA,YAA+B,CAAC;;CAEhC,cAA+B,CAAC;CAChC,UAA2BA,0BAAAA;;CAE3B;;CAEA;;CAEA;;CAGA,MACC,MAQC;EACD,OAAOC,cAAAA,UAAU,MAAM,EAAE,OAAO,KAAK,CAAC;CAQvC;CAEA,QAAQ,SAAuB;EAC9B,OAAOA,cAAAA,UAAU,MAAM,EAAE,UAAU,QAAQ,CAAC;CAC7C;;CAGA,UAAU,WAAyB;EAClC,OAAOA,cAAAA,UAAU,MAAM,EAAE,YAAY,UAAU,CAAC;CACjD;;CAGA,KAAK,OAAO,MAAY;EACvB,OAAOA,cAAAA,UAAU,MAAM,EAAE,OAAO,KAAK,CAAC;CACvC;;;;;;;;;;;;CAaA,UACC,YAQC;EAQD,MAAM,WAAWC,4BAAAA,WAAW,UAAU;EAEtC,OAAOD,cAAAA,UAAU,MAAM;GACtB,WAAW,CAAC,GAAG,KAAK,WAAW,GAAG,QAAQ;GAC1C,aAAaE,4BAAAA,MAAM,YAAY,KAAK,WAAW;EAChD,CAAC;CAQF;CAEA,SACC,UAQC;EACD,OAAOF,cAAAA,UAAU,MAAM,EACtB,WAAW,CAAC,GAAG,KAAK,WAAW,GAAG,QAAQ,EAC3C,CAAC;CAQF;CAEA,OACC,QAQC;EACD,OAAOA,cAAAA,UAAU,MAAM,EACtB,SAAS,OACV,CAAC;CAQF;;;;;;;;CASA,SACC,QACgE;EAChE,OAAOA,cAAAA,UAAU,MAAM;GACtB,kBAAkBG,4BAAAA,UAAU,MAAM;GAClC,GAAGC,4BAAAA,cACF;IACC,YAAY,KAAK;IACjB,MAAM,KAAK;IACX,SAAS,KAAK;IACd,UAAU,KAAK;GAChB,GACA,MACD;EACD,CAAC;CAQF;;CAGA,QACC,QAQC;EACD,OAAOJ,cAAAA,UAAU,MAAM,EACtB,gBAAgB,OACjB,CAAC;CAQF;CAEA,OACC,IAQC;EACD,IAAI,CAAC,KAAK,OAAO,MAAM,IAAI,eAAe;EAC1C,IAAI,CAAC,KAAK,gBAAgB,MAAM,IAAI,kBAAkB,KAAK,KAAK;EAEhE,MAAM,QAAQ,IAAIK,cAAAA,MAQjB,KAAK,OACL,IACA,KAAK,gBACL,KAAK,UACL,KAAK,WACL,KAAK,SACL,KAAK,YACL,KAAK,OACL,KAAK,aACL,KAAK,gBACN;EAEA,MAAM,QAAQ,KAAK;EAQnB,OAAO;CACR;AACD;;AAGA,IAAa,iBAAb,cAAoC,MAAM;CACzC,cAAc;EACb,MACC,uEACD;EACA,KAAK,OAAO;CACb;AACD;;AAGA,IAAa,oBAAb,cAAuC,MAAM;CACvB;CAArB,YAAY,OAAwB;EACnC,MACC,UAAU,MAAM,sHAEjB;EAJoB,KAAA,QAAA;EAKpB,KAAK,OAAO;CACb;AACD"}
@@ -1,4 +1,4 @@
1
- import { t as Construct } from "./Construct-BO1X-kt9.mjs";
1
+ import { t as Construct } from "./Construct-B1vGBPme.mjs";
2
2
  import { databaseEdges, idsOf, serviceOf, servicesOf } from "./construct-interface.mjs";
3
3
  import { t as cloneWith } from "./clone-EKNovYHu.mjs";
4
4
  import { DEFAULT_LOGGER } from "@geekmidas/logger/console";
@@ -131,4 +131,4 @@ var SubscriberBuilder = class {
131
131
  //#endregion
132
132
  export { Subscriber as n, SubscriberBuilder as t };
133
133
 
134
- //# sourceMappingURL=SubscriberBuilder-DMFk_Ap1.mjs.map
134
+ //# sourceMappingURL=SubscriberBuilder-CDy4nHCz.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"SubscriberBuilder-DMFk_Ap1.mjs","names":[],"sources":["../src/subscribers/Subscriber.ts","../src/subscribers/SubscriberBuilder.ts"],"sourcesContent":["import type {\n\tEventPublisher,\n\tExtractPublisherMessage,\n} from '@geekmidas/events';\nimport type { Logger } from '@geekmidas/logger';\nimport { DEFAULT_LOGGER } from '@geekmidas/logger/console';\nimport type { InferStandardSchema } from '@geekmidas/schema';\nimport type { Service, ServiceRecord } from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport { Construct, ConstructType } from '../Construct';\nimport type { DatabaseContext } from '../functions/Function';\n\n// Helper type to extract payload types for subscribed events\ntype ExtractEventPayloads<\n\tTPublisher extends EventPublisher<any> | undefined,\n\tTEventTypes extends any[],\n> = TPublisher extends EventPublisher<any>\n\t? Extract<ExtractPublisherMessage<TPublisher>, { type: TEventTypes[number] }>\n\t: never;\n\n/**\n * A runnable that consumes a topic. Its handler gets `db` when it has a\n * database: the worker's, declared once with `worker.database(db)`, or its own\n * with `.database(other)`.\n */\nexport class Subscriber<\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tOutSchema extends StandardSchemaV1 | undefined = undefined,\n\t/** The topic's message union — what types the events this subscriber receives. */\n\tTEventPublisher extends EventPublisher<any> | undefined = undefined,\n\tTSubscribedEvents extends\n\t\tExtractPublisherMessage<TEventPublisher>['type'][] = ExtractPublisherMessage<TEventPublisher>['type'][],\n\tTDatabase = undefined,\n\tTDatabaseServiceName extends string = string,\n> extends Construct<\n\tTLogger,\n\tOutSchema,\n\tTServices,\n\tstring,\n\tundefined,\n\tTDatabaseServiceName,\n\tTDatabase\n> {\n\t__IS_SUBSCRIBER__ = true;\n\n\tstatic isSubscriber(\n\t\tobj: any,\n\t): obj is Subscriber<any, any, any, any, any, any> {\n\t\treturn Boolean(\n\t\t\tobj &&\n\t\t\t\tobj.__IS_SUBSCRIBER__ === true &&\n\t\t\t\tobj.type === ConstructType.Subscriber,\n\t\t);\n\t}\n\n\tconstructor(\n\t\tpublic readonly handler: SubscriberHandler<\n\t\t\tTEventPublisher,\n\t\t\tTSubscribedEvents,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tOutSchema,\n\t\t\tTDatabase\n\t\t>,\n\t\tpublic override readonly timeout: number = 30000,\n\t\tprotected _subscribedEvents?: TSubscribedEvents,\n\t\tpublic override readonly outputSchema?: OutSchema,\n\t\tpublic override readonly services: TServices = [] as unknown as TServices,\n\t\tpublic override readonly logger: TLogger = DEFAULT_LOGGER as TLogger,\n\t\t/**\n\t\t * The name of the {@link Topic} this subscriber binds to (via\n\t\t * `worker.topic(topic)`), recorded for the manifest so infra wires the\n\t\t * SNS subscription.\n\t\t */\n\t\tpublic readonly topicName?: string,\n\t\t/**\n\t\t * The construct ids `.dependsOn()` named — last, so no existing positional\n\t\t * argument moves.\n\t\t */\n\t\tconstructs: string[] = [],\n\t\t/**\n\t\t * What the handler's `db` is registered from — after `constructs`, for\n\t\t * the same reason.\n\t\t */\n\t\tdatabaseService?: Service<TDatabaseServiceName, TDatabase>,\n\t) {\n\t\tsuper(\n\t\t\tConstructType.Subscriber,\n\t\t\tlogger,\n\t\t\tservices,\n\t\t\t[],\n\t\t\toutputSchema,\n\t\t\ttimeout,\n\t\t\tundefined, // memorySize\n\t\t\tundefined, // auditorStorageService\n\t\t\tconstructs,\n\t\t);\n\n\t\tthis.databaseService = databaseService;\n\t}\n\n\tget subscribedEvents(): TSubscribedEvents | undefined {\n\t\treturn this._subscribedEvents;\n\t}\n}\n\n// Handler type for subscribers that receives an array of events\nexport type SubscriberHandler<\n\tTEventPublisher extends EventPublisher<any> | undefined,\n\tTSubscribedEvents extends ExtractPublisherMessage<TEventPublisher>['type'][],\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tOutSchema extends StandardSchemaV1 | undefined = undefined,\n\tTDatabase = undefined,\n> = (\n\tctx: SubscriberContext<\n\t\tTEventPublisher,\n\t\tTSubscribedEvents,\n\t\tTServices,\n\t\tTLogger,\n\t\tTDatabase\n\t>,\n) => OutSchema extends StandardSchemaV1\n\t? InferStandardSchema<OutSchema> | Promise<InferStandardSchema<OutSchema>>\n\t: any | Promise<any>;\n\n// Context type for subscriber handlers — `db` only when a database is set.\nexport type SubscriberContext<\n\tTEventPublisher extends EventPublisher<any> | undefined,\n\tTSubscribedEvents extends ExtractPublisherMessage<TEventPublisher>['type'][],\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTDatabase = undefined,\n> = {\n\tevents: ExtractEventPayloads<TEventPublisher, TSubscribedEvents>[];\n\tservices: ServiceRecord<TServices>;\n\tlogger: TLogger;\n} & DatabaseContext<TDatabase>;\n","import type {\n\tEventPublisher,\n\tExtractPublisherMessage,\n} from '@geekmidas/events';\nimport type { Logger } from '@geekmidas/logger';\nimport { DEFAULT_LOGGER } from '@geekmidas/logger/console';\nimport type { Service } from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport { cloneWith } from '../clone';\nimport {\n\ttype Consumable,\n\tdatabaseEdges,\n\tidsOf,\n\ttype ServicesOf,\n\tserviceOf,\n\tservicesOf,\n} from '../construct-interface';\nimport type { Topic, TopicEvents, TopicMessage } from '../topic/Topic';\nimport { Subscriber, type SubscriberHandler } from './Subscriber';\n\nexport class SubscriberBuilder<\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tOutSchema extends StandardSchemaV1 | undefined = undefined,\n\tTEventPublisher extends EventPublisher<any> | undefined = undefined,\n\tTSubscribedEvents extends any[] = [],\n\tTDatabase = undefined,\n\tTDatabaseServiceName extends string = string,\n> {\n\tprivate _subscribedEvents: TSubscribedEvents = [] as any;\n\tprivate _timeout?: number;\n\tprivate outputSchema?: OutSchema;\n\tprivate _services: TServices = [] as Service[] as TServices;\n\t/** The construct ids `.dependsOn()` named — what the manifest records. */\n\tpublic _constructs: string[] = [];\n\tprivate _logger: TLogger = DEFAULT_LOGGER;\n\t/**\n\t * The deploy unit that runs this, by construct id. Seeded by the factory a\n\t * `Worker` hands out, so a subscriber is not owned by its directory.\n\t */\n\tpublic _owner?: string;\n\tprivate _topicName?: string;\n\t/** What the handler's `db` comes from — the worker's, unless overridden. */\n\tprivate _databaseService?: Service<TDatabaseServiceName, TDatabase>;\n\t/** The edge `.database()` added, so the next one can replace it. */\n\tprivate _databaseEdge?: string;\n\n\tconstructor() {\n\t\tthis._timeout = 30000; // Default timeout\n\t}\n\n\t/**\n\t * Bind this subscriber to a {@link Topic}: the subscribable event types (and\n\t * their payloads) come from the topic's contract, and the binding is recorded\n\t * for the manifest, so infra wires the SNS subscription.\n\t *\n\t * A consumer does not publish, so binding a topic does *not* hand it the\n\t * topic's publisher connection string (least privilege). One that publishes\n\t * follow-up events depends on the topic it publishes to: `.dependsOn([orders])`.\n\t */\n\ttopic<TName extends string, TEvents extends TopicEvents>(\n\t\ttopic: Topic<TName, TEvents>,\n\t): SubscriberBuilder<\n\t\tTServices,\n\t\tTLogger,\n\t\tOutSchema,\n\t\tEventPublisher<TopicMessage<TEvents>>,\n\t\tTSubscribedEvents,\n\t\tTDatabase,\n\t\tTDatabaseServiceName\n\t> {\n\t\treturn cloneWith(this, {\n\t\t\t_topicName: topic.name,\n\t\t}) as unknown as SubscriberBuilder<\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tOutSchema,\n\t\t\tEventPublisher<TopicMessage<TEvents>>,\n\t\t\tTSubscribedEvents,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>;\n\t}\n\n\ttimeout(timeout: number): this {\n\t\treturn cloneWith(this, { _timeout: timeout });\n\t}\n\n\toutput<T extends StandardSchemaV1>(\n\t\tschema: T,\n\t): SubscriberBuilder<\n\t\tTServices,\n\t\tTLogger,\n\t\tT,\n\t\tTEventPublisher,\n\t\tTSubscribedEvents,\n\t\tTDatabase,\n\t\tTDatabaseServiceName\n\t> {\n\t\treturn cloneWith(this, {\n\t\t\toutputSchema: schema as unknown as OutSchema,\n\t\t}) as any;\n\t}\n\n\t/**\n\t * Depend on constructs — a database, a bucket, a mail sender, a queue, a\n\t * topic.\n\t *\n\t * It records the edge and dissolves each construct's client into the\n\t * handler's service record under the construct's own id, so\n\t * `.dependsOn([uploads])` is what makes `services.uploads` exist and type.\n\t *\n\t * Constructs only. A `Service` does not match the shape, which is what keeps\n\t * env sniffing confined to `.services()` and the explicit lift.\n\t */\n\tdependsOn<const T extends readonly Consumable[]>(\n\t\tconstructs: T,\n\t): SubscriberBuilder<\n\t\t[...TServices, ...ServicesOf<T>],\n\t\tTLogger,\n\t\tOutSchema,\n\t\tTEventPublisher,\n\t\tTSubscribedEvents,\n\t\tTDatabase,\n\t\tTDatabaseServiceName\n\t> {\n\t\t// Both halves of the edge, from one call and one clone: the services the\n\t\t// handler runs with, and the ids the manifest records. Recording them\n\t\t// separately is what let them drift apart.\n\t\t//\n\t\t// `servicesOf` is the half that validates, so it runs first — recording\n\t\t// ids ahead of it left a caught `NotAConstruct` with `undefined` already\n\t\t// on a `string[]`.\n\t\tconst services = servicesOf(constructs) as unknown as Service[];\n\n\t\treturn cloneWith(this, {\n\t\t\t_services: [...this._services, ...services],\n\t\t\t_constructs: idsOf(constructs, this._constructs),\n\t\t}) as unknown as SubscriberBuilder<\n\t\t\t[...TServices, ...ServicesOf<T>],\n\t\t\tTLogger,\n\t\t\tOutSchema,\n\t\t\tTEventPublisher,\n\t\t\tTSubscribedEvents,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>;\n\t}\n\n\tservices<T extends Service[]>(\n\t\tservices: T,\n\t): SubscriberBuilder<\n\t\t[...TServices, ...T],\n\t\tTLogger,\n\t\tOutSchema,\n\t\tTEventPublisher,\n\t\tTSubscribedEvents,\n\t\tTDatabase,\n\t\tTDatabaseServiceName\n\t> {\n\t\treturn cloneWith(this, {\n\t\t\t_services: [...this._services, ...services] as any,\n\t\t}) as any;\n\t}\n\n\tlogger<T extends Logger>(\n\t\tlogger: T,\n\t): SubscriberBuilder<\n\t\tTServices,\n\t\tT,\n\t\tOutSchema,\n\t\tTEventPublisher,\n\t\tTSubscribedEvents,\n\t\tTDatabase,\n\t\tTDatabaseServiceName\n\t> {\n\t\treturn cloneWith(this, { _logger: logger as unknown as TLogger }) as any;\n\t}\n\n\t/**\n\t * The database the handler receives as `db`.\n\t *\n\t * A subscriber built from a worker already has the worker's, when the\n\t * worker named one with `.database(db)`; this replaces it — the edge as well\n\t * as the client — for a consumer that works against a different database.\n\t */\n\tdatabase<T, TDbName extends string>(\n\t\tsource: Consumable<TDbName, T> | Service<TDbName, T>,\n\t): SubscriberBuilder<\n\t\tTServices,\n\t\tTLogger,\n\t\tOutSchema,\n\t\tTEventPublisher,\n\t\tTSubscribedEvents,\n\t\tT,\n\t\tTDbName\n\t> {\n\t\treturn cloneWith(this, {\n\t\t\t_databaseService: serviceOf(source),\n\t\t\t...databaseEdges(\n\t\t\t\t{\n\t\t\t\t\tconstructs: this._constructs,\n\t\t\t\t\tedge: this._databaseEdge,\n\t\t\t\t\tservice: this._databaseService as Service | undefined,\n\t\t\t\t\tservices: this._services,\n\t\t\t\t},\n\t\t\t\tsource,\n\t\t\t),\n\t\t}) as unknown as SubscriberBuilder<\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tOutSchema,\n\t\t\tTEventPublisher,\n\t\t\tTSubscribedEvents,\n\t\t\tT,\n\t\t\tTDbName\n\t\t>;\n\t}\n\n\tsubscribe<\n\t\tTEvent extends TEventPublisher extends EventPublisher<any>\n\t\t\t?\n\t\t\t\t\t| ExtractPublisherMessage<TEventPublisher>['type']\n\t\t\t\t\t| ExtractPublisherMessage<TEventPublisher>['type'][]\n\t\t\t: never,\n\t>(\n\t\tevent: TEvent,\n\t): SubscriberBuilder<\n\t\tTServices,\n\t\tTLogger,\n\t\tOutSchema,\n\t\tTEventPublisher,\n\t\tTEvent extends any[]\n\t\t\t? [...TSubscribedEvents, ...TEvent]\n\t\t\t: [...TSubscribedEvents, TEvent],\n\t\tTDatabase,\n\t\tTDatabaseServiceName\n\t> {\n\t\tconst eventsToAdd = Array.isArray(event) ? event : [event];\n\t\treturn cloneWith(this, {\n\t\t\t_subscribedEvents: [...this._subscribedEvents, ...eventsToAdd] as any,\n\t\t}) as any;\n\t}\n\n\thandle(\n\t\tfn: SubscriberHandler<\n\t\t\tTEventPublisher,\n\t\t\tTSubscribedEvents,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tOutSchema,\n\t\t\tTDatabase\n\t\t>,\n\t): Subscriber<\n\t\tTServices,\n\t\tTLogger,\n\t\tOutSchema,\n\t\tTEventPublisher,\n\t\tTSubscribedEvents,\n\t\tTDatabase,\n\t\tTDatabaseServiceName\n\t> {\n\t\tconst subscriber = new Subscriber(\n\t\t\tfn,\n\t\t\tthis._timeout,\n\t\t\tthis._subscribedEvents,\n\t\t\tthis.outputSchema,\n\t\t\tthis._services,\n\t\t\tthis._logger,\n\t\t\tthis._topicName,\n\t\t\tthis._constructs,\n\t\t\tthis._databaseService,\n\t\t);\n\n\t\t// Which process runs it. Carried from the factory rather than inferred\n\t\t// from the directory the file happens to sit in.\n\t\tsubscriber.owner = this._owner;\n\n\t\t// No reset: `.handle()` reads this builder and leaves it alone, so a\n\t\t// configured base stays usable for the next subscriber.\n\t\treturn subscriber;\n\t}\n}\n"],"mappings":";;;;;;;;;;AAyBA,IAAa,aAAb,cAUU,UAQR;CAcgB;CAQS;CACf;CACe;CACA;CACA;CAMT;CA/BjB,oBAAoB;CAEpB,OAAO,aACN,KACkD;EAClD,OAAO,QACN,OACC,IAAI,sBAAsB,QAC1B,IAAI,SAAA,mCACN;CACD;CAEA,YACC,SAQA,UAA2C,KAC3C,mBACA,cACA,WAA+C,CAAC,GAChD,SAA2C,gBAM3C,WAKA,aAAuB,CAAC,GAKxB,iBACC;EACD,MAAA,qCAEC,QACA,UACA,CAAC,GACD,cACA,SACA,KAAA,GACA,KAAA,GACA,UACD;EAxCgB,KAAA,UAAA;EAQS,KAAA,UAAA;EACf,KAAA,oBAAA;EACe,KAAA,eAAA;EACA,KAAA,WAAA;EACA,KAAA,SAAA;EAMT,KAAA,YAAA;EAwBhB,KAAK,kBAAkB;CACxB;CAEA,IAAI,mBAAkD;EACrD,OAAO,KAAK;CACb;AACD;;;ACrFA,IAAa,oBAAb,MAQE;CACD,oBAA+C,CAAC;CAChD;CACA;CACA,YAA+B,CAAC;;CAEhC,cAA+B,CAAC;CAChC,UAA2B;;;;;CAK3B;CACA;;CAEA;;CAEA;CAEA,cAAc;EACb,KAAK,WAAW;CACjB;;;;;;;;;;CAWA,MACC,OASC;EACD,OAAO,UAAU,MAAM,EACtB,YAAY,MAAM,KACnB,CAAC;CASF;CAEA,QAAQ,SAAuB;EAC9B,OAAO,UAAU,MAAM,EAAE,UAAU,QAAQ,CAAC;CAC7C;CAEA,OACC,QASC;EACD,OAAO,UAAU,MAAM,EACtB,cAAc,OACf,CAAC;CACF;;;;;;;;;;;;CAaA,UACC,YASC;EAQD,MAAM,WAAW,WAAW,UAAU;EAEtC,OAAO,UAAU,MAAM;GACtB,WAAW,CAAC,GAAG,KAAK,WAAW,GAAG,QAAQ;GAC1C,aAAa,MAAM,YAAY,KAAK,WAAW;EAChD,CAAC;CASF;CAEA,SACC,UASC;EACD,OAAO,UAAU,MAAM,EACtB,WAAW,CAAC,GAAG,KAAK,WAAW,GAAG,QAAQ,EAC3C,CAAC;CACF;CAEA,OACC,QASC;EACD,OAAO,UAAU,MAAM,EAAE,SAAS,OAA6B,CAAC;CACjE;;;;;;;;CASA,SACC,QASC;EACD,OAAO,UAAU,MAAM;GACtB,kBAAkB,UAAU,MAAM;GAClC,GAAG,cACF;IACC,YAAY,KAAK;IACjB,MAAM,KAAK;IACX,SAAS,KAAK;IACd,UAAU,KAAK;GAChB,GACA,MACD;EACD,CAAC;CASF;CAEA,UAOC,OAWC;EACD,MAAM,cAAc,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;EACzD,OAAO,UAAU,MAAM,EACtB,mBAAmB,CAAC,GAAG,KAAK,mBAAmB,GAAG,WAAW,EAC9D,CAAC;CACF;CAEA,OACC,IAgBC;EACD,MAAM,aAAa,IAAI,WACtB,IACA,KAAK,UACL,KAAK,mBACL,KAAK,cACL,KAAK,WACL,KAAK,SACL,KAAK,YACL,KAAK,aACL,KAAK,gBACN;EAIA,WAAW,QAAQ,KAAK;EAIxB,OAAO;CACR;AACD"}
1
+ {"version":3,"file":"SubscriberBuilder-CDy4nHCz.mjs","names":[],"sources":["../src/subscribers/Subscriber.ts","../src/subscribers/SubscriberBuilder.ts"],"sourcesContent":["import type {\n\tEventPublisher,\n\tExtractPublisherMessage,\n} from '@geekmidas/events';\nimport type { Logger } from '@geekmidas/logger';\nimport { DEFAULT_LOGGER } from '@geekmidas/logger/console';\nimport type { InferStandardSchema } from '@geekmidas/schema';\nimport type { Service, ServiceRecord } from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport { Construct, ConstructType } from '../Construct';\nimport type { DatabaseContext } from '../functions/Function';\n\n// Helper type to extract payload types for subscribed events\ntype ExtractEventPayloads<\n\tTPublisher extends EventPublisher<any> | undefined,\n\tTEventTypes extends any[],\n> = TPublisher extends EventPublisher<any>\n\t? Extract<ExtractPublisherMessage<TPublisher>, { type: TEventTypes[number] }>\n\t: never;\n\n/**\n * A runnable that consumes a topic. Its handler gets `db` when it has a\n * database: the worker's, declared once with `worker.database(db)`, or its own\n * with `.database(other)`.\n */\nexport class Subscriber<\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tOutSchema extends StandardSchemaV1 | undefined = undefined,\n\t/** The topic's message union — what types the events this subscriber receives. */\n\tTEventPublisher extends EventPublisher<any> | undefined = undefined,\n\tTSubscribedEvents extends\n\t\tExtractPublisherMessage<TEventPublisher>['type'][] = ExtractPublisherMessage<TEventPublisher>['type'][],\n\tTDatabase = undefined,\n\tTDatabaseServiceName extends string = string,\n> extends Construct<\n\tTLogger,\n\tOutSchema,\n\tTServices,\n\tstring,\n\tundefined,\n\tTDatabaseServiceName,\n\tTDatabase\n> {\n\t__IS_SUBSCRIBER__ = true;\n\n\tstatic isSubscriber(\n\t\tobj: any,\n\t): obj is Subscriber<any, any, any, any, any, any> {\n\t\treturn Boolean(\n\t\t\tobj &&\n\t\t\t\tobj.__IS_SUBSCRIBER__ === true &&\n\t\t\t\tobj.type === ConstructType.Subscriber,\n\t\t);\n\t}\n\n\tconstructor(\n\t\tpublic readonly handler: SubscriberHandler<\n\t\t\tTEventPublisher,\n\t\t\tTSubscribedEvents,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tOutSchema,\n\t\t\tTDatabase\n\t\t>,\n\t\tpublic override readonly timeout: number = 30000,\n\t\tprotected _subscribedEvents?: TSubscribedEvents,\n\t\tpublic override readonly outputSchema?: OutSchema,\n\t\tpublic override readonly services: TServices = [] as unknown as TServices,\n\t\tpublic override readonly logger: TLogger = DEFAULT_LOGGER as TLogger,\n\t\t/**\n\t\t * The name of the {@link Topic} this subscriber binds to (via\n\t\t * `worker.topic(topic)`), recorded for the manifest so infra wires the\n\t\t * SNS subscription.\n\t\t */\n\t\tpublic readonly topicName?: string,\n\t\t/**\n\t\t * The construct ids `.dependsOn()` named — last, so no existing positional\n\t\t * argument moves.\n\t\t */\n\t\tconstructs: string[] = [],\n\t\t/**\n\t\t * What the handler's `db` is registered from — after `constructs`, for\n\t\t * the same reason.\n\t\t */\n\t\tdatabaseService?: Service<TDatabaseServiceName, TDatabase>,\n\t) {\n\t\tsuper(\n\t\t\tConstructType.Subscriber,\n\t\t\tlogger,\n\t\t\tservices,\n\t\t\t[],\n\t\t\toutputSchema,\n\t\t\ttimeout,\n\t\t\tundefined, // memorySize\n\t\t\tundefined, // auditorStorageService\n\t\t\tconstructs,\n\t\t);\n\n\t\tthis.databaseService = databaseService;\n\t}\n\n\tget subscribedEvents(): TSubscribedEvents | undefined {\n\t\treturn this._subscribedEvents;\n\t}\n}\n\n// Handler type for subscribers that receives an array of events\nexport type SubscriberHandler<\n\tTEventPublisher extends EventPublisher<any> | undefined,\n\tTSubscribedEvents extends ExtractPublisherMessage<TEventPublisher>['type'][],\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tOutSchema extends StandardSchemaV1 | undefined = undefined,\n\tTDatabase = undefined,\n> = (\n\tctx: SubscriberContext<\n\t\tTEventPublisher,\n\t\tTSubscribedEvents,\n\t\tTServices,\n\t\tTLogger,\n\t\tTDatabase\n\t>,\n) => OutSchema extends StandardSchemaV1\n\t? InferStandardSchema<OutSchema> | Promise<InferStandardSchema<OutSchema>>\n\t: any | Promise<any>;\n\n// Context type for subscriber handlers — `db` only when a database is set.\nexport type SubscriberContext<\n\tTEventPublisher extends EventPublisher<any> | undefined,\n\tTSubscribedEvents extends ExtractPublisherMessage<TEventPublisher>['type'][],\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTDatabase = undefined,\n> = {\n\tevents: ExtractEventPayloads<TEventPublisher, TSubscribedEvents>[];\n\tservices: ServiceRecord<TServices>;\n\tlogger: TLogger;\n} & DatabaseContext<TDatabase>;\n","import type {\n\tEventPublisher,\n\tExtractPublisherMessage,\n} from '@geekmidas/events';\nimport type { Logger } from '@geekmidas/logger';\nimport { DEFAULT_LOGGER } from '@geekmidas/logger/console';\nimport type { Service } from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport { cloneWith } from '../clone';\nimport {\n\ttype Consumable,\n\tdatabaseEdges,\n\tidsOf,\n\ttype ServicesOf,\n\tserviceOf,\n\tservicesOf,\n} from '../construct-interface';\nimport type { Topic, TopicEvents, TopicMessage } from '../topic/Topic';\nimport { Subscriber, type SubscriberHandler } from './Subscriber';\n\nexport class SubscriberBuilder<\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tOutSchema extends StandardSchemaV1 | undefined = undefined,\n\tTEventPublisher extends EventPublisher<any> | undefined = undefined,\n\tTSubscribedEvents extends any[] = [],\n\tTDatabase = undefined,\n\tTDatabaseServiceName extends string = string,\n> {\n\tprivate _subscribedEvents: TSubscribedEvents = [] as any;\n\tprivate _timeout?: number;\n\tprivate outputSchema?: OutSchema;\n\tprivate _services: TServices = [] as Service[] as TServices;\n\t/** The construct ids `.dependsOn()` named — what the manifest records. */\n\tpublic _constructs: string[] = [];\n\tprivate _logger: TLogger = DEFAULT_LOGGER;\n\t/**\n\t * The deploy unit that runs this, by construct id. Seeded by the factory a\n\t * `Worker` hands out, so a subscriber is not owned by its directory.\n\t */\n\tpublic _owner?: string;\n\tprivate _topicName?: string;\n\t/** What the handler's `db` comes from — the worker's, unless overridden. */\n\tprivate _databaseService?: Service<TDatabaseServiceName, TDatabase>;\n\t/** The edge `.database()` added, so the next one can replace it. */\n\tprivate _databaseEdge?: string;\n\n\tconstructor() {\n\t\tthis._timeout = 30000; // Default timeout\n\t}\n\n\t/**\n\t * Bind this subscriber to a {@link Topic}: the subscribable event types (and\n\t * their payloads) come from the topic's contract, and the binding is recorded\n\t * for the manifest, so infra wires the SNS subscription.\n\t *\n\t * A consumer does not publish, so binding a topic does *not* hand it the\n\t * topic's publisher connection string (least privilege). One that publishes\n\t * follow-up events depends on the topic it publishes to: `.dependsOn([orders])`.\n\t */\n\ttopic<TName extends string, TEvents extends TopicEvents>(\n\t\ttopic: Topic<TName, TEvents>,\n\t): SubscriberBuilder<\n\t\tTServices,\n\t\tTLogger,\n\t\tOutSchema,\n\t\tEventPublisher<TopicMessage<TEvents>>,\n\t\tTSubscribedEvents,\n\t\tTDatabase,\n\t\tTDatabaseServiceName\n\t> {\n\t\treturn cloneWith(this, {\n\t\t\t_topicName: topic.name,\n\t\t}) as unknown as SubscriberBuilder<\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tOutSchema,\n\t\t\tEventPublisher<TopicMessage<TEvents>>,\n\t\t\tTSubscribedEvents,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>;\n\t}\n\n\ttimeout(timeout: number): this {\n\t\treturn cloneWith(this, { _timeout: timeout });\n\t}\n\n\toutput<T extends StandardSchemaV1>(\n\t\tschema: T,\n\t): SubscriberBuilder<\n\t\tTServices,\n\t\tTLogger,\n\t\tT,\n\t\tTEventPublisher,\n\t\tTSubscribedEvents,\n\t\tTDatabase,\n\t\tTDatabaseServiceName\n\t> {\n\t\treturn cloneWith(this, {\n\t\t\toutputSchema: schema as unknown as OutSchema,\n\t\t}) as any;\n\t}\n\n\t/**\n\t * Depend on constructs — a database, a bucket, a mail sender, a queue, a\n\t * topic.\n\t *\n\t * It records the edge and dissolves each construct's client into the\n\t * handler's service record under the construct's own id, so\n\t * `.dependsOn([uploads])` is what makes `services.uploads` exist and type.\n\t *\n\t * Constructs only. A `Service` does not match the shape, which is what keeps\n\t * env sniffing confined to `.services()` and the explicit lift.\n\t */\n\tdependsOn<const T extends readonly Consumable[]>(\n\t\tconstructs: T,\n\t): SubscriberBuilder<\n\t\t[...TServices, ...ServicesOf<T>],\n\t\tTLogger,\n\t\tOutSchema,\n\t\tTEventPublisher,\n\t\tTSubscribedEvents,\n\t\tTDatabase,\n\t\tTDatabaseServiceName\n\t> {\n\t\t// Both halves of the edge, from one call and one clone: the services the\n\t\t// handler runs with, and the ids the manifest records. Recording them\n\t\t// separately is what let them drift apart.\n\t\t//\n\t\t// `servicesOf` is the half that validates, so it runs first — recording\n\t\t// ids ahead of it left a caught `NotAConstruct` with `undefined` already\n\t\t// on a `string[]`.\n\t\tconst services = servicesOf(constructs) as unknown as Service[];\n\n\t\treturn cloneWith(this, {\n\t\t\t_services: [...this._services, ...services],\n\t\t\t_constructs: idsOf(constructs, this._constructs),\n\t\t}) as unknown as SubscriberBuilder<\n\t\t\t[...TServices, ...ServicesOf<T>],\n\t\t\tTLogger,\n\t\t\tOutSchema,\n\t\t\tTEventPublisher,\n\t\t\tTSubscribedEvents,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>;\n\t}\n\n\tservices<T extends Service[]>(\n\t\tservices: T,\n\t): SubscriberBuilder<\n\t\t[...TServices, ...T],\n\t\tTLogger,\n\t\tOutSchema,\n\t\tTEventPublisher,\n\t\tTSubscribedEvents,\n\t\tTDatabase,\n\t\tTDatabaseServiceName\n\t> {\n\t\treturn cloneWith(this, {\n\t\t\t_services: [...this._services, ...services] as any,\n\t\t}) as any;\n\t}\n\n\tlogger<T extends Logger>(\n\t\tlogger: T,\n\t): SubscriberBuilder<\n\t\tTServices,\n\t\tT,\n\t\tOutSchema,\n\t\tTEventPublisher,\n\t\tTSubscribedEvents,\n\t\tTDatabase,\n\t\tTDatabaseServiceName\n\t> {\n\t\treturn cloneWith(this, { _logger: logger as unknown as TLogger }) as any;\n\t}\n\n\t/**\n\t * The database the handler receives as `db`.\n\t *\n\t * A subscriber built from a worker already has the worker's, when the\n\t * worker named one with `.database(db)`; this replaces it — the edge as well\n\t * as the client — for a consumer that works against a different database.\n\t */\n\tdatabase<T, TDbName extends string>(\n\t\tsource: Consumable<TDbName, T> | Service<TDbName, T>,\n\t): SubscriberBuilder<\n\t\tTServices,\n\t\tTLogger,\n\t\tOutSchema,\n\t\tTEventPublisher,\n\t\tTSubscribedEvents,\n\t\tT,\n\t\tTDbName\n\t> {\n\t\treturn cloneWith(this, {\n\t\t\t_databaseService: serviceOf(source),\n\t\t\t...databaseEdges(\n\t\t\t\t{\n\t\t\t\t\tconstructs: this._constructs,\n\t\t\t\t\tedge: this._databaseEdge,\n\t\t\t\t\tservice: this._databaseService as Service | undefined,\n\t\t\t\t\tservices: this._services,\n\t\t\t\t},\n\t\t\t\tsource,\n\t\t\t),\n\t\t}) as unknown as SubscriberBuilder<\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tOutSchema,\n\t\t\tTEventPublisher,\n\t\t\tTSubscribedEvents,\n\t\t\tT,\n\t\t\tTDbName\n\t\t>;\n\t}\n\n\tsubscribe<\n\t\tTEvent extends TEventPublisher extends EventPublisher<any>\n\t\t\t?\n\t\t\t\t\t| ExtractPublisherMessage<TEventPublisher>['type']\n\t\t\t\t\t| ExtractPublisherMessage<TEventPublisher>['type'][]\n\t\t\t: never,\n\t>(\n\t\tevent: TEvent,\n\t): SubscriberBuilder<\n\t\tTServices,\n\t\tTLogger,\n\t\tOutSchema,\n\t\tTEventPublisher,\n\t\tTEvent extends any[]\n\t\t\t? [...TSubscribedEvents, ...TEvent]\n\t\t\t: [...TSubscribedEvents, TEvent],\n\t\tTDatabase,\n\t\tTDatabaseServiceName\n\t> {\n\t\tconst eventsToAdd = Array.isArray(event) ? event : [event];\n\t\treturn cloneWith(this, {\n\t\t\t_subscribedEvents: [...this._subscribedEvents, ...eventsToAdd] as any,\n\t\t}) as any;\n\t}\n\n\thandle(\n\t\tfn: SubscriberHandler<\n\t\t\tTEventPublisher,\n\t\t\tTSubscribedEvents,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tOutSchema,\n\t\t\tTDatabase\n\t\t>,\n\t): Subscriber<\n\t\tTServices,\n\t\tTLogger,\n\t\tOutSchema,\n\t\tTEventPublisher,\n\t\tTSubscribedEvents,\n\t\tTDatabase,\n\t\tTDatabaseServiceName\n\t> {\n\t\tconst subscriber = new Subscriber(\n\t\t\tfn,\n\t\t\tthis._timeout,\n\t\t\tthis._subscribedEvents,\n\t\t\tthis.outputSchema,\n\t\t\tthis._services,\n\t\t\tthis._logger,\n\t\t\tthis._topicName,\n\t\t\tthis._constructs,\n\t\t\tthis._databaseService,\n\t\t);\n\n\t\t// Which process runs it. Carried from the factory rather than inferred\n\t\t// from the directory the file happens to sit in.\n\t\tsubscriber.owner = this._owner;\n\n\t\t// No reset: `.handle()` reads this builder and leaves it alone, so a\n\t\t// configured base stays usable for the next subscriber.\n\t\treturn subscriber;\n\t}\n}\n"],"mappings":";;;;;;;;;;AAyBA,IAAa,aAAb,cAUU,UAQR;CAcgB;CAQS;CACf;CACe;CACA;CACA;CAMT;CA/BjB,oBAAoB;CAEpB,OAAO,aACN,KACkD;EAClD,OAAO,QACN,OACC,IAAI,sBAAsB,QAC1B,IAAI,SAAA,mCACN;CACD;CAEA,YACC,SAQA,UAA2C,KAC3C,mBACA,cACA,WAA+C,CAAC,GAChD,SAA2C,gBAM3C,WAKA,aAAuB,CAAC,GAKxB,iBACC;EACD,MAAA,qCAEC,QACA,UACA,CAAC,GACD,cACA,SACA,KAAA,GACA,KAAA,GACA,UACD;EAxCgB,KAAA,UAAA;EAQS,KAAA,UAAA;EACf,KAAA,oBAAA;EACe,KAAA,eAAA;EACA,KAAA,WAAA;EACA,KAAA,SAAA;EAMT,KAAA,YAAA;EAwBhB,KAAK,kBAAkB;CACxB;CAEA,IAAI,mBAAkD;EACrD,OAAO,KAAK;CACb;AACD;;;ACrFA,IAAa,oBAAb,MAQE;CACD,oBAA+C,CAAC;CAChD;CACA;CACA,YAA+B,CAAC;;CAEhC,cAA+B,CAAC;CAChC,UAA2B;;;;;CAK3B;CACA;;CAEA;;CAEA;CAEA,cAAc;EACb,KAAK,WAAW;CACjB;;;;;;;;;;CAWA,MACC,OASC;EACD,OAAO,UAAU,MAAM,EACtB,YAAY,MAAM,KACnB,CAAC;CASF;CAEA,QAAQ,SAAuB;EAC9B,OAAO,UAAU,MAAM,EAAE,UAAU,QAAQ,CAAC;CAC7C;CAEA,OACC,QASC;EACD,OAAO,UAAU,MAAM,EACtB,cAAc,OACf,CAAC;CACF;;;;;;;;;;;;CAaA,UACC,YASC;EAQD,MAAM,WAAW,WAAW,UAAU;EAEtC,OAAO,UAAU,MAAM;GACtB,WAAW,CAAC,GAAG,KAAK,WAAW,GAAG,QAAQ;GAC1C,aAAa,MAAM,YAAY,KAAK,WAAW;EAChD,CAAC;CASF;CAEA,SACC,UASC;EACD,OAAO,UAAU,MAAM,EACtB,WAAW,CAAC,GAAG,KAAK,WAAW,GAAG,QAAQ,EAC3C,CAAC;CACF;CAEA,OACC,QASC;EACD,OAAO,UAAU,MAAM,EAAE,SAAS,OAA6B,CAAC;CACjE;;;;;;;;CASA,SACC,QASC;EACD,OAAO,UAAU,MAAM;GACtB,kBAAkB,UAAU,MAAM;GAClC,GAAG,cACF;IACC,YAAY,KAAK;IACjB,MAAM,KAAK;IACX,SAAS,KAAK;IACd,UAAU,KAAK;GAChB,GACA,MACD;EACD,CAAC;CASF;CAEA,UAOC,OAWC;EACD,MAAM,cAAc,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;EACzD,OAAO,UAAU,MAAM,EACtB,mBAAmB,CAAC,GAAG,KAAK,mBAAmB,GAAG,WAAW,EAC9D,CAAC;CACF;CAEA,OACC,IAgBC;EACD,MAAM,aAAa,IAAI,WACtB,IACA,KAAK,UACL,KAAK,mBACL,KAAK,cACL,KAAK,WACL,KAAK,SACL,KAAK,YACL,KAAK,aACL,KAAK,gBACN;EAIA,WAAW,QAAQ,KAAK;EAIxB,OAAO;CACR;AACD"}
@@ -1,4 +1,4 @@
1
- const require_Construct = require("./Construct-DmvXceVY.cjs");
1
+ const require_Construct = require("./Construct-DUSXuTHP.cjs");
2
2
  const require_construct_interface = require("./construct-interface.cjs");
3
3
  const require_clone = require("./clone-UGJpvQnL.cjs");
4
4
  let _geekmidas_logger_console = require("@geekmidas/logger/console");
@@ -142,4 +142,4 @@ Object.defineProperty(exports, "SubscriberBuilder", {
142
142
  }
143
143
  });
144
144
 
145
- //# sourceMappingURL=SubscriberBuilder-C2PwqwIl.cjs.map
145
+ //# sourceMappingURL=SubscriberBuilder-D_owVoHo.cjs.map