@geekmidas/constructs 10.0.0-alpha.107 → 10.0.0-alpha.109

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 (157) hide show
  1. package/dist/Construct-BQALFCh2.d.cts.map +1 -1
  2. package/dist/{Construct-B1vGBPme.mjs → Construct-CKUA-CTA.mjs} +3 -2
  3. package/dist/Construct-CKUA-CTA.mjs.map +1 -0
  4. package/dist/{Construct-DUSXuTHP.cjs → Construct-DEOVauyA.cjs} +3 -2
  5. package/dist/Construct-DEOVauyA.cjs.map +1 -0
  6. package/dist/Construct-QXnl3VqT.d.mts.map +1 -1
  7. package/dist/{Cron-uYAHPnp7.d.mts → Cron-CQJqqkhY.d.mts} +2 -2
  8. package/dist/{Cron-uYAHPnp7.d.mts.map → Cron-CQJqqkhY.d.mts.map} +1 -1
  9. package/dist/{CronBuilder-C0Ek1-RA.cjs → CronBuilder-BXTG10u5.cjs} +3 -3
  10. package/dist/{CronBuilder-C0Ek1-RA.cjs.map → CronBuilder-BXTG10u5.cjs.map} +1 -1
  11. package/dist/{CronBuilder-Dmix_Pen.mjs → CronBuilder-D3sF9tS_.mjs} +3 -3
  12. package/dist/{CronBuilder-Dmix_Pen.mjs.map → CronBuilder-D3sF9tS_.mjs.map} +1 -1
  13. package/dist/{CronBuilder-DaSUm2Na.d.mts → CronBuilder-Xx_zakuc.d.mts} +4 -4
  14. package/dist/{CronBuilder-DaSUm2Na.d.mts.map → CronBuilder-Xx_zakuc.d.mts.map} +1 -1
  15. package/dist/Endpoint-BWyKCa2x.d.cts.map +1 -1
  16. package/dist/{Endpoint-C3r2OzOD.d.mts → Endpoint-CrE9Ppdk.d.mts} +6 -6
  17. package/dist/{Endpoint-C3r2OzOD.d.mts.map → Endpoint-CrE9Ppdk.d.mts.map} +1 -1
  18. package/dist/{Endpoint-BW-nFdL_.mjs → Endpoint-gPXhHKhJ.mjs} +11 -4
  19. package/dist/Endpoint-gPXhHKhJ.mjs.map +1 -0
  20. package/dist/{Endpoint-B_UHlLxv.cjs → Endpoint-yhR2T2Wt.cjs} +11 -4
  21. package/dist/Endpoint-yhR2T2Wt.cjs.map +1 -0
  22. package/dist/{EndpointFactory-C9eVhDKF.d.mts → EndpointFactory-BV-nfnaP.d.mts} +5 -5
  23. package/dist/{EndpointFactory-C9eVhDKF.d.mts.map → EndpointFactory-BV-nfnaP.d.mts.map} +1 -1
  24. package/dist/{EndpointFactory-DeZTvDjU.cjs → EndpointFactory-BVv6EIDu.cjs} +4 -4
  25. package/dist/{EndpointFactory-DeZTvDjU.cjs.map → EndpointFactory-BVv6EIDu.cjs.map} +1 -1
  26. package/dist/{EndpointFactory-Bbmx4vXc.mjs → EndpointFactory-Cs8IHvwF.mjs} +4 -4
  27. package/dist/{EndpointFactory-Bbmx4vXc.mjs.map → EndpointFactory-Cs8IHvwF.mjs.map} +1 -1
  28. package/dist/{HonoEndpointAdaptor-eCKyEOl_.cjs → HonoEndpointAdaptor-CN1jjUBJ.cjs} +11 -13
  29. package/dist/HonoEndpointAdaptor-CN1jjUBJ.cjs.map +1 -0
  30. package/dist/{HonoEndpointAdaptor-RSvRnXSl.mjs → HonoEndpointAdaptor-euH1oSdY.mjs} +7 -9
  31. package/dist/HonoEndpointAdaptor-euH1oSdY.mjs.map +1 -0
  32. package/dist/{Queue-A3wfD0it.cjs → Queue-BhuofM3k.cjs} +2 -2
  33. package/dist/{Queue-A3wfD0it.cjs.map → Queue-BhuofM3k.cjs.map} +1 -1
  34. package/dist/{Queue-D6USGS4q.mjs → Queue-cmhqEBOB.mjs} +2 -2
  35. package/dist/{Queue-D6USGS4q.mjs.map → Queue-cmhqEBOB.mjs.map} +1 -1
  36. package/dist/{QueueBuilder-QHsnVt1P.d.mts → QueueBuilder-Cx9lKC5d.d.mts} +2 -2
  37. package/dist/{QueueBuilder-QHsnVt1P.d.mts.map → QueueBuilder-Cx9lKC5d.d.mts.map} +1 -1
  38. package/dist/{QueueBuilder-Cue73M4t.mjs → QueueBuilder-DXu1qxT1.mjs} +2 -2
  39. package/dist/{QueueBuilder-Cue73M4t.mjs.map → QueueBuilder-DXu1qxT1.mjs.map} +1 -1
  40. package/dist/{QueueBuilder-BjuF0UBl.cjs → QueueBuilder-DdU2klPu.cjs} +2 -2
  41. package/dist/{QueueBuilder-BjuF0UBl.cjs.map → QueueBuilder-DdU2klPu.cjs.map} +1 -1
  42. package/dist/{SubscriberBuilder-CuCMFpYW.d.mts → SubscriberBuilder-4DWe9feX.d.mts} +2 -2
  43. package/dist/{SubscriberBuilder-CuCMFpYW.d.mts.map → SubscriberBuilder-4DWe9feX.d.mts.map} +1 -1
  44. package/dist/{SubscriberBuilder-CDy4nHCz.mjs → SubscriberBuilder-DRXfxWn_.mjs} +2 -2
  45. package/dist/{SubscriberBuilder-CDy4nHCz.mjs.map → SubscriberBuilder-DRXfxWn_.mjs.map} +1 -1
  46. package/dist/{SubscriberBuilder-D_owVoHo.cjs → SubscriberBuilder-M2EAgQhC.cjs} +2 -2
  47. package/dist/{SubscriberBuilder-D_owVoHo.cjs.map → SubscriberBuilder-M2EAgQhC.cjs.map} +1 -1
  48. package/dist/{Topic-D74b5cgs.mjs → Topic-D2R0r24C.mjs} +2 -2
  49. package/dist/{Topic-D74b5cgs.mjs.map → Topic-D2R0r24C.mjs.map} +1 -1
  50. package/dist/{Topic-GHxcU8AG.cjs → Topic-DwS3QO0m.cjs} +2 -2
  51. package/dist/{Topic-GHxcU8AG.cjs.map → Topic-DwS3QO0m.cjs.map} +1 -1
  52. package/dist/adaptors/aws.cjs +19 -15
  53. package/dist/adaptors/aws.cjs.map +1 -1
  54. package/dist/adaptors/aws.d.cts +2 -0
  55. package/dist/adaptors/aws.d.cts.map +1 -1
  56. package/dist/adaptors/aws.d.mts +5 -3
  57. package/dist/adaptors/aws.d.mts.map +1 -1
  58. package/dist/adaptors/aws.mjs +17 -13
  59. package/dist/adaptors/aws.mjs.map +1 -1
  60. package/dist/adaptors/hono.cjs +1 -1
  61. package/dist/adaptors/hono.d.cts.map +1 -1
  62. package/dist/adaptors/hono.d.mts +2 -2
  63. package/dist/adaptors/hono.d.mts.map +1 -1
  64. package/dist/adaptors/hono.mjs +1 -1
  65. package/dist/adaptors/testing.cjs +13 -14
  66. package/dist/adaptors/testing.cjs.map +1 -1
  67. package/dist/adaptors/testing.d.cts.map +1 -1
  68. package/dist/adaptors/testing.d.mts +4 -4
  69. package/dist/adaptors/testing.d.mts.map +1 -1
  70. package/dist/adaptors/testing.mjs +11 -12
  71. package/dist/adaptors/testing.mjs.map +1 -1
  72. package/dist/{auth-CcuceLYo.d.mts → auth-D_0788uW.d.mts} +4 -4
  73. package/dist/{auth-CcuceLYo.d.mts.map → auth-D_0788uW.d.mts.map} +1 -1
  74. package/dist/auth.cjs +1 -1
  75. package/dist/auth.d.mts +1 -1
  76. package/dist/auth.mjs +1 -1
  77. package/dist/{cache-CXh6GIgb.d.mts → cache-5QZzh5Er.d.mts} +2 -2
  78. package/dist/{cache-CXh6GIgb.d.mts.map → cache-5QZzh5Er.d.mts.map} +1 -1
  79. package/dist/cache.d.mts +1 -1
  80. package/dist/{construct-interface-DluK3_da.d.mts → construct-interface-ZvYQbivO.d.mts} +2 -2
  81. package/dist/{construct-interface-DluK3_da.d.mts.map → construct-interface-ZvYQbivO.d.mts.map} +1 -1
  82. package/dist/construct-interface.d.mts +1 -1
  83. package/dist/credential.d.mts +1 -1
  84. package/dist/crons/index.cjs +3 -3
  85. package/dist/crons/index.d.mts +2 -2
  86. package/dist/crons/index.mjs +3 -3
  87. package/dist/database/kysely.cjs +1 -1
  88. package/dist/database/kysely.d.mts +1 -1
  89. package/dist/database/kysely.mjs +1 -1
  90. package/dist/email.d.mts +2 -2
  91. package/dist/encryption.d.mts +1 -1
  92. package/dist/endpoints/index.cjs +3 -3
  93. package/dist/endpoints/index.d.mts +2 -2
  94. package/dist/endpoints/index.mjs +3 -3
  95. package/dist/external-api.cjs +1 -1
  96. package/dist/external-api.d.mts +1 -1
  97. package/dist/external-api.mjs +1 -1
  98. package/dist/{fake-BcHTPz7N.d.mts → fake-DqKo8INJ.d.mts} +2 -2
  99. package/dist/{fake-BcHTPz7N.d.mts.map → fake-DqKo8INJ.d.mts.map} +1 -1
  100. package/dist/file-server.d.mts +1 -1
  101. package/dist/functions/index.cjs +1 -1
  102. package/dist/functions/index.d.mts +1 -1
  103. package/dist/functions/index.mjs +1 -1
  104. package/dist/{functions-BFizikJU.mjs → functions-CTI-hDem.mjs} +2 -2
  105. package/dist/{functions-BFizikJU.mjs.map → functions-CTI-hDem.mjs.map} +1 -1
  106. package/dist/{functions-CfzJG9hK.cjs → functions-D7O5SGVc.cjs} +2 -2
  107. package/dist/{functions-CfzJG9hK.cjs.map → functions-D7O5SGVc.cjs.map} +1 -1
  108. package/dist/{index-CTkKOer4.d.mts → index-B13kQB_U.d.mts} +2 -2
  109. package/dist/{index-CTkKOer4.d.mts.map → index-B13kQB_U.d.mts.map} +1 -1
  110. package/dist/index.cjs +1 -1
  111. package/dist/index.mjs +1 -1
  112. package/dist/{kysely-CO_Dk43W.cjs → kysely-B3zfg9ti.cjs} +2 -2
  113. package/dist/{kysely-CO_Dk43W.cjs.map → kysely-B3zfg9ti.cjs.map} +1 -1
  114. package/dist/{kysely-Bii4l6eP.mjs → kysely-B_0BAr0T.mjs} +2 -2
  115. package/dist/{kysely-Bii4l6eP.mjs.map → kysely-B_0BAr0T.mjs.map} +1 -1
  116. package/dist/{kysely-DUqC3Ie0.d.mts → kysely-DKwnl0nf.d.mts} +4 -4
  117. package/dist/{kysely-DUqC3Ie0.d.mts.map → kysely-DKwnl0nf.d.mts.map} +1 -1
  118. package/dist/mobile-app.d.mts +1 -1
  119. package/dist/object-storage.d.mts +1 -1
  120. package/dist/queue/index.cjs +2 -2
  121. package/dist/queue/index.d.mts +1 -1
  122. package/dist/queue/index.mjs +2 -2
  123. package/dist/random-password.d.mts +1 -1
  124. package/dist/{rateLimit-BwHcMECR.cjs → requestLogger-BZcXwdBs.cjs} +33 -1
  125. package/dist/requestLogger-BZcXwdBs.cjs.map +1 -0
  126. package/dist/{rateLimit-DbOTsP01.mjs → requestLogger-pA4gi3E1.mjs} +28 -2
  127. package/dist/requestLogger-pA4gi3E1.mjs.map +1 -0
  128. package/dist/rest-api.cjs +1 -1
  129. package/dist/rest-api.d.mts +4 -4
  130. package/dist/rest-api.mjs +1 -1
  131. package/dist/site.d.mts +2 -2
  132. package/dist/subscribers/index.cjs +2 -2
  133. package/dist/subscribers/index.d.mts +1 -1
  134. package/dist/subscribers/index.mjs +2 -2
  135. package/dist/{telemetry-DRtT1_0A.d.mts → telemetry-BEoBn6GO.d.mts} +2 -2
  136. package/dist/{telemetry-DRtT1_0A.d.mts.map → telemetry-BEoBn6GO.d.mts.map} +1 -1
  137. package/dist/telemetry.d.mts +1 -1
  138. package/dist/topic/index.cjs +1 -1
  139. package/dist/topic/index.mjs +1 -1
  140. package/dist/{tracing-CxaZ98IB.mjs → tracing-Bbj1uoFJ.mjs} +19 -2
  141. package/dist/tracing-Bbj1uoFJ.mjs.map +1 -0
  142. package/dist/{tracing-DPMidF_r.cjs → tracing-C0ooU0V2.cjs} +24 -1
  143. package/dist/tracing-C0ooU0V2.cjs.map +1 -0
  144. package/dist/worker.cjs +4 -4
  145. package/dist/worker.d.mts +8 -8
  146. package/dist/worker.mjs +4 -4
  147. package/package.json +31 -31
  148. package/dist/Construct-B1vGBPme.mjs.map +0 -1
  149. package/dist/Construct-DUSXuTHP.cjs.map +0 -1
  150. package/dist/Endpoint-BW-nFdL_.mjs.map +0 -1
  151. package/dist/Endpoint-B_UHlLxv.cjs.map +0 -1
  152. package/dist/HonoEndpointAdaptor-RSvRnXSl.mjs.map +0 -1
  153. package/dist/HonoEndpointAdaptor-eCKyEOl_.cjs.map +0 -1
  154. package/dist/rateLimit-BwHcMECR.cjs.map +0 -1
  155. package/dist/rateLimit-DbOTsP01.mjs.map +0 -1
  156. package/dist/tracing-CxaZ98IB.mjs.map +0 -1
  157. package/dist/tracing-DPMidF_r.cjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"requestLogger-pA4gi3E1.mjs","names":[],"sources":["../src/endpoints/optionalPeers.ts","../src/endpoints/processAudits.ts","../src/endpoints/rateLimit.ts","../src/endpoints/requestLogger.ts"],"sourcesContent":["/**\n * The optional peers an endpoint needs only for a feature it opted into.\n *\n * `@geekmidas/audit`, `@geekmidas/rate-limit` and `@geekmidas/db` are optional\n * peers of this package: an app that audits nothing, limits nothing and sets\n * no row-level security need not install them. The adaptor every endpoint is\n * served by used to import them at the top, so serving any endpoint needed\n * them all — and a production bundle of an app without them failed to build.\n * They are loaded here, when an endpoint that uses one is served, which is\n * also when the app declaring that endpoint has installed it: an auditor's\n * storage, a rate limit's store and an RLS policy's database come from these\n * packages.\n *\n * The `try` is load-bearing beyond the error it names: a bundler leaves an\n * import it cannot resolve to run time when the import is guarded, and fails\n * the build when it is not.\n */\n\n/** An endpoint uses a feature whose optional peer is not installed. */\nexport class OptionalPeerMissing extends Error {\n\tconstructor(\n\t\treadonly packageName: string,\n\t\treadonly feature: string,\n\t\tcause: unknown,\n\t) {\n\t\tsuper(\n\t\t\t`${feature} needs '${packageName}', which could not be loaded. ` +\n\t\t\t\t`Add it to the dependencies of the app that serves this endpoint.`,\n\t\t\t{ cause },\n\t\t);\n\t\tthis.name = 'OptionalPeerMissing';\n\t}\n}\n\n/** `@geekmidas/audit`, for an endpoint with an auditor. */\nexport async function loadAudit(): Promise<typeof import('@geekmidas/audit')> {\n\ttry {\n\t\treturn await import('@geekmidas/audit');\n\t} catch (cause) {\n\t\tthrow new OptionalPeerMissing(\n\t\t\t'@geekmidas/audit',\n\t\t\t'An endpoint with an auditor',\n\t\t\tcause,\n\t\t);\n\t}\n}\n\n/** `@geekmidas/rate-limit`, for an endpoint with a rate limit. */\nexport async function loadRateLimit(): Promise<\n\ttypeof import('@geekmidas/rate-limit')\n> {\n\ttry {\n\t\treturn await import('@geekmidas/rate-limit');\n\t} catch (cause) {\n\t\tthrow new OptionalPeerMissing(\n\t\t\t'@geekmidas/rate-limit',\n\t\t\t'An endpoint with a rate limit',\n\t\t\tcause,\n\t\t);\n\t}\n}\n\n/** `@geekmidas/db/rls`, for an endpoint with row-level security. */\nexport async function loadRls(): Promise<typeof import('@geekmidas/db/rls')> {\n\ttry {\n\t\treturn await import('@geekmidas/db/rls');\n\t} catch (cause) {\n\t\tthrow new OptionalPeerMissing(\n\t\t\t'@geekmidas/db',\n\t\t\t'An endpoint with row-level security',\n\t\t\tcause,\n\t\t);\n\t}\n}\n","import type {\n\tAuditActor,\n\tAuditableAction,\n\tAuditor,\n\tAuditStorage,\n} from '@geekmidas/audit';\nimport type { Logger } from '@geekmidas/logger';\nimport type { InferStandardSchema } from '@geekmidas/schema';\nimport type { Service, ServiceDiscovery } from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport type { ActorExtractor, MappedAudit } from './audit';\nimport type { CookieFn, Endpoint, HeaderFn } from './Endpoint';\nimport { loadAudit } from './optionalPeers';\n\n/**\n * Process declarative audit definitions after successful endpoint execution.\n * Similar to publishConstructEvents for events.\n *\n * @param endpoint - The endpoint with audit configuration\n * @param response - The handler response to generate audit payloads from\n * @param serviceDiscovery - Service discovery for registering audit storage\n * @param logger - Logger for debug/error messages\n * @param ctx - Request context (session, headers, cookies, services)\n * @param existingAuditor - Optional existing auditor instance (e.g., from handler context).\n * If provided, uses this auditor (with its stored transaction).\n * If not provided, creates a new auditor.\n */\nexport async function processEndpointAudits<\n\tTServices extends Service[] = [],\n\tTSession = unknown,\n\tTLogger extends Logger = Logger,\n\tOutSchema extends StandardSchemaV1 | undefined = undefined,\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>(\n\tendpoint: Endpoint<\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tOutSchema,\n\t\tTServices,\n\t\tTLogger,\n\t\tTSession,\n\t\tTAuditStorage,\n\t\tTAuditStorageServiceName,\n\t\tTAuditAction\n\t>,\n\tresponse: InferStandardSchema<OutSchema>,\n\tserviceDiscovery: ServiceDiscovery<any>,\n\tlogger: TLogger,\n\tctx: {\n\t\tsession: TSession;\n\t\theader: HeaderFn;\n\t\tcookie: CookieFn;\n\t\tservices: Record<string, unknown>;\n\t},\n\texistingAuditor?: Auditor<TAuditAction>,\n): Promise<void> {\n\ttry {\n\t\tconst audits = endpoint.audits as MappedAudit<TAuditAction, OutSchema>[];\n\n\t\t// If we have an existing auditor (from handler context), we need to flush\n\t\t// any manual audits it collected, even if there are no declarative audits\n\t\tconst hasExistingRecords =\n\t\t\texistingAuditor && existingAuditor.getRecords().length > 0;\n\n\t\t// Skip if no declarative audits and no existing records to flush\n\t\tif (!audits?.length && !hasExistingRecords) {\n\t\t\tlogger.debug('No audits to process');\n\t\t\treturn;\n\t\t}\n\n\t\t// If no auditor storage service and we have things to process, warn\n\t\tif (!endpoint.auditorStorageService) {\n\t\t\tif (hasExistingRecords || audits?.length) {\n\t\t\t\tlogger.warn('No auditor storage service available');\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Get or create auditor\n\t\tlet auditor: Auditor<TAuditAction>;\n\n\t\tif (existingAuditor) {\n\t\t\t// Use existing auditor (preserves stored transaction and manual audits)\n\t\t\tauditor = existingAuditor;\n\t\t\tlogger.debug('Using existing auditor from handler context');\n\t\t} else {\n\t\t\t// Create new auditor (backward compatibility)\n\t\t\tconst services = await serviceDiscovery.register([\n\t\t\t\tendpoint.auditorStorageService,\n\t\t\t]);\n\t\t\tconst storage = services[\n\t\t\t\tendpoint.auditorStorageService.serviceName\n\t\t\t] as AuditStorage;\n\n\t\t\t// Extract actor if configured\n\t\t\tlet actor: AuditActor = { id: 'system', type: 'system' };\n\t\t\tif (endpoint.actorExtractor) {\n\t\t\t\ttry {\n\t\t\t\t\tactor = await (\n\t\t\t\t\t\tendpoint.actorExtractor as ActorExtractor<\n\t\t\t\t\t\t\tTServices,\n\t\t\t\t\t\t\tTSession,\n\t\t\t\t\t\t\tTLogger\n\t\t\t\t\t\t>\n\t\t\t\t\t)({\n\t\t\t\t\t\tservices: ctx.services as any,\n\t\t\t\t\t\tsession: ctx.session,\n\t\t\t\t\t\theader: ctx.header,\n\t\t\t\t\t\tcookie: ctx.cookie,\n\t\t\t\t\t\tlogger,\n\t\t\t\t\t});\n\t\t\t\t} catch (error) {\n\t\t\t\t\tlogger.error(error as Error, 'Failed to extract actor for audits');\n\t\t\t\t\t// Continue with system actor\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst { DefaultAuditor } = await loadAudit();\n\t\t\tauditor = new DefaultAuditor<TAuditAction>({\n\t\t\t\tactor,\n\t\t\t\tstorage,\n\t\t\t\tmetadata: {\n\t\t\t\t\tendpoint: endpoint.route,\n\t\t\t\t\tmethod: endpoint.method,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\n\t\t// Process each declarative audit\n\t\tif (audits?.length) {\n\t\t\tfor (const audit of audits) {\n\t\t\t\tlogger.debug({ audit: audit.type }, 'Processing declarative audit');\n\n\t\t\t\t// Check when condition\n\t\t\t\tif (audit.when && !audit.when(response as any)) {\n\t\t\t\t\tlogger.debug(\n\t\t\t\t\t\t{ audit: audit.type },\n\t\t\t\t\t\t'Audit skipped due to when condition',\n\t\t\t\t\t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Extract payload\n\t\t\t\tconst payload = audit.payload(response as any);\n\n\t\t\t\t// Extract entityId if configured\n\t\t\t\tconst entityId = audit.entityId?.(response as any);\n\n\t\t\t\t// Record the audit\n\t\t\t\tauditor.audit(audit.type as any, payload as any, {\n\t\t\t\t\ttable: audit.table,\n\t\t\t\t\tentityId,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\t// Flush audits to storage\n\t\t// Note: If existingAuditor has a stored transaction (via setTransaction),\n\t\t// flush() will use it automatically\n\t\tconst recordCount = auditor.getRecords().length;\n\t\tif (recordCount > 0) {\n\t\t\t// Check if auditor has a stored transaction (for logging purposes)\n\t\t\tconst trx =\n\t\t\t\t'getTransaction' in auditor\n\t\t\t\t\t? (auditor as { getTransaction(): unknown }).getTransaction()\n\t\t\t\t\t: undefined;\n\t\t\tlogger.debug(\n\t\t\t\t{ auditCount: recordCount, hasTransaction: !!trx },\n\t\t\t\t'Flushing audits',\n\t\t\t);\n\t\t\tawait auditor.flush();\n\t\t}\n\t} catch (error) {\n\t\tlogger.error(error as Error, 'Failed to process audits');\n\t\t// Don't rethrow - audit failures shouldn't fail the request\n\t}\n}\n\n/**\n * Context for audit-aware handler execution.\n */\nexport interface AuditExecutionContext<\n\tTAuditAction extends AuditableAction<string, unknown> = AuditableAction<\n\t\tstring,\n\t\tunknown\n\t>,\n> {\n\t/** The auditor instance for recording audits */\n\tauditor: Auditor<TAuditAction>;\n\t/** The audit storage instance */\n\tstorage: AuditStorage;\n}\n\n/**\n * Create audit context for handler execution.\n * Returns the auditor and storage for use in the handler.\n *\n * @param endpoint - The endpoint with audit configuration\n * @param serviceDiscovery - Service discovery for getting audit storage\n * @param logger - Logger for debug/error messages\n * @param ctx - Request context for actor extraction\n * @returns Audit context with auditor and storage, or undefined if not configured\n */\nexport async function createAuditContext<\n\tTServices extends Service[] = [],\n\tTSession = unknown,\n\tTLogger extends Logger = Logger,\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\tendpoint: Endpoint<\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tTServices,\n\t\tTLogger,\n\t\tTSession,\n\t\tTAuditStorage,\n\t\tTAuditStorageServiceName,\n\t\tTAuditAction,\n\t\tTDatabase,\n\t\tTDatabaseServiceName\n\t>,\n\tserviceDiscovery: ServiceDiscovery<any>,\n\tlogger: TLogger,\n\tctx: {\n\t\tsession: TSession;\n\t\theader: HeaderFn;\n\t\tcookie: CookieFn;\n\t\tservices: Record<string, unknown>;\n\t},\n): Promise<AuditExecutionContext<TAuditAction> | undefined> {\n\tif (!endpoint.auditorStorageService) {\n\t\treturn undefined;\n\t}\n\n\tconst services = await serviceDiscovery.register([\n\t\tendpoint.auditorStorageService,\n\t]);\n\tconst storage = services[\n\t\tendpoint.auditorStorageService.serviceName\n\t] as AuditStorage;\n\n\t// Extract actor if configured\n\tlet actor: AuditActor = { id: 'system', type: 'system' };\n\tif (endpoint.actorExtractor) {\n\t\ttry {\n\t\t\tactor = await (\n\t\t\t\tendpoint.actorExtractor as ActorExtractor<TServices, TSession, TLogger>\n\t\t\t)({\n\t\t\t\tservices: ctx.services as any,\n\t\t\t\tsession: ctx.session,\n\t\t\t\theader: ctx.header,\n\t\t\t\tcookie: ctx.cookie,\n\t\t\t\tlogger,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tlogger.error(error as Error, 'Failed to extract actor for audits');\n\t\t}\n\t}\n\n\tconst { DefaultAuditor } = await loadAudit();\n\tconst auditor = new DefaultAuditor<TAuditAction>({\n\t\tactor,\n\t\tstorage,\n\t\tmetadata: {\n\t\t\tendpoint: endpoint.route,\n\t\t\tmethod: endpoint.method,\n\t\t},\n\t});\n\n\treturn { auditor, storage };\n}\n\n/**\n * Options for executeWithAuditTransaction.\n */\nexport interface ExecuteWithAuditTransactionOptions {\n\t/**\n\t * Database connection to use for the transaction.\n\t * If this is already a transaction, it will be reused instead of creating a nested one.\n\t * If not provided, the storage's internal database is used.\n\t */\n\tdb?: unknown;\n}\n\n/**\n * Execute a handler with automatic audit transaction support.\n * If the audit storage provides a withTransaction method, wraps execution\n * in a transaction so audits are atomic with handler's database operations.\n *\n * This is database-agnostic - each storage implementation provides its own\n * transaction handling based on the underlying database (Kysely, Drizzle, etc.).\n *\n * If the db parameter is provided and is already a transaction, the storage\n * will reuse it instead of creating a nested transaction (similar to\n * packages/db/src/kysely.ts#withTransaction).\n *\n * @param auditContext - The audit context from createAuditContext\n * @param handler - The handler function to execute (receives auditor)\n * @param onComplete - Called after handler with response, to process declarative audits\n * @param options - Optional configuration including database connection\n * @returns The handler result\n */\nexport async function executeWithAuditTransaction<\n\tT,\n\tTAuditAction extends AuditableAction<string, unknown> = AuditableAction<\n\t\tstring,\n\t\tunknown\n\t>,\n>(\n\tauditContext: AuditExecutionContext<TAuditAction> | undefined,\n\thandler: (auditor?: Auditor<TAuditAction>) => Promise<T>,\n\tonComplete?: (response: T, auditor: Auditor<TAuditAction>) => Promise<void>,\n\toptions?: ExecuteWithAuditTransactionOptions,\n): Promise<T> {\n\t// No audit context - just run handler\n\tif (!auditContext) {\n\t\treturn handler(undefined);\n\t}\n\n\tconst { auditor, storage } = auditContext;\n\n\t// Check if storage provides a transaction wrapper\n\tif (storage.withTransaction) {\n\t\t// Wrap in transaction - audits are atomic with handler operations\n\t\t// The storage's withTransaction handles setTransaction and flush\n\t\t// Pass db so existing transactions are reused\n\t\treturn storage.withTransaction(\n\t\t\tauditor,\n\t\t\tasync () => {\n\t\t\t\tconst response = await handler(auditor);\n\n\t\t\t\t// Process declarative audits within the transaction\n\t\t\t\tif (onComplete) {\n\t\t\t\t\tawait onComplete(response, auditor);\n\t\t\t\t}\n\n\t\t\t\treturn response;\n\t\t\t},\n\t\t\toptions?.db,\n\t\t);\n\t}\n\n\t// No transaction support - run handler and flush audits after\n\tconst response = await handler(auditor);\n\n\tif (onComplete) {\n\t\tawait onComplete(response, auditor);\n\t}\n\n\t// Flush audits (no transaction)\n\tawait auditor.flush();\n\n\treturn response;\n}\n","import type { RateLimitConfig, RateLimitContext } from '@geekmidas/rate-limit';\nimport { loadRateLimit } from './optionalPeers';\n\n/**\n * An endpoint's `.rateLimit()`, checked the one way every adaptor checks it —\n * Hono, Lambda and the test adaptor — after the request is authorized.\n *\n * Under the limit, it answers the headers the response carries:\n * `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` (and\n * `Retry-After` on the last request the window allows).\n *\n * @throws {TooManyRequestsError} over the limit — `@geekmidas/errors`' 429,\n * whose `headers` hold `Retry-After` (seconds until the window resets) and the\n * same `X-RateLimit-*`, which every adaptor sets on the error response.\n */\nexport async function enforceRateLimit(\n\tconfig: RateLimitConfig,\n\tctx: RateLimitContext<any, any, any>,\n): Promise<Record<string, string>> {\n\tconst { checkRateLimit, getRateLimitHeaders } = await loadRateLimit();\n\tconst info = await checkRateLimit(config, ctx);\n\n\tconst headers: Record<string, string> = {};\n\tfor (const [key, value] of Object.entries(\n\t\tgetRateLimitHeaders(info, config),\n\t)) {\n\t\tif (value !== undefined) headers[key] = value;\n\t}\n\treturn headers;\n}\n","import type { Logger } from '@geekmidas/logger';\n\n/** What an endpoint contributes to every line logged for one of its requests. */\ninterface LoggingEndpoint {\n\tlogger: Logger;\n\tfullPath: string;\n\troute: string;\n\tmethod: string;\n}\n\n/**\n * The logger for one request to `endpoint`: the surface's, bound with which\n * request it is and what it was for — the request id, the endpoint, its route\n * and method — plus whatever the adaptor knows of the request itself (host,\n * path, the Lambda and API Gateway context).\n *\n * One per request, made once, and the one the request context carries: the\n * handler logs through it, a service reaches it through the context, and\n * identifying the request (`identifyRequest`) derives the signed-in user's\n * logger from it — so a line says whose request it was without losing which\n * route it came from. A field the request does not have is left out rather\n * than logged as `undefined`.\n */\nexport function requestLogger<TLogger extends Logger>(\n\tendpoint: LoggingEndpoint,\n\tfields: { requestId: string } & Record<string, unknown>,\n): TLogger {\n\tconst { requestId, ...request } = fields;\n\tconst bindings: Record<string, unknown> = {\n\t\trequestId,\n\t\tendpoint: endpoint.fullPath,\n\t\troute: endpoint.route,\n\t\tmethod: endpoint.method,\n\t};\n\tfor (const [key, value] of Object.entries(request)) {\n\t\tif (value !== undefined) bindings[key] = value;\n\t}\n\treturn endpoint.logger.child(bindings) as TLogger;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAmBA,IAAa,sBAAb,cAAyC,MAAM;CAEpC;CACA;CAFV,YACC,aACA,SACA,OACC;EACD,MACC,GAAG,QAAQ,UAAU,YAAY,iGAEjC,EAAE,MAAM,CACT;EARS,KAAA,cAAA;EACA,KAAA,UAAA;EAQT,KAAK,OAAO;CACb;AACD;;AAGA,eAAsB,YAAwD;CAC7E,IAAI;EACH,OAAO,MAAM,OAAO;CACrB,SAAS,OAAO;EACf,MAAM,IAAI,oBACT,oBACA,+BACA,KACD;CACD;AACD;;AAGA,eAAsB,gBAEpB;CACD,IAAI;EACH,OAAO,MAAM,OAAO;CACrB,SAAS,OAAO;EACf,MAAM,IAAI,oBACT,yBACA,iCACA,KACD;CACD;AACD;;AAGA,eAAsB,UAAuD;CAC5E,IAAI;EACH,OAAO,MAAM,OAAO;CACrB,SAAS,OAAO;EACf,MAAM,IAAI,oBACT,iBACA,uCACA,KACD;CACD;AACD;;;;;;;;;;;;;ACwIA,eAAsB,mBAarB,UAcA,kBACA,QACA,KAM2D;CAC3D,IAAI,CAAC,SAAS,uBACb;CAMD,MAAM,WAAU,MAHO,iBAAiB,SAAS,CAChD,SAAS,qBACV,CAAC,EAAA,CAEA,SAAS,sBAAsB;CAIhC,IAAI,QAAoB;EAAE,IAAI;EAAU,MAAM;CAAS;CACvD,IAAI,SAAS,gBACZ,IAAI;EACH,QAAQ,MACP,SAAS,eACR;GACD,UAAU,IAAI;GACd,SAAS,IAAI;GACb,QAAQ,IAAI;GACZ,QAAQ,IAAI;GACZ;EACD,CAAC;CACF,SAAS,OAAO;EACf,OAAO,MAAM,OAAgB,oCAAoC;CAClE;CAGD,MAAM,EAAE,mBAAmB,MAAM,UAAU;CAU3C,OAAO;EAAE,SAAA,IATW,eAA6B;GAChD;GACA;GACA,UAAU;IACT,UAAU,SAAS;IACnB,QAAQ,SAAS;GAClB;EACD,CAEe;EAAG;CAAQ;AAC3B;;;;;;;;;;;;;;;;;;;AAgCA,eAAsB,4BAOrB,cACA,SACA,YACA,SACa;CAEb,IAAI,CAAC,cACJ,OAAO,QAAQ,KAAA,CAAS;CAGzB,MAAM,EAAE,SAAS,YAAY;CAG7B,IAAI,QAAQ,iBAIX,OAAO,QAAQ,gBACd,SACA,YAAY;EACX,MAAM,WAAW,MAAM,QAAQ,OAAO;EAGtC,IAAI,YACH,MAAM,WAAW,UAAU,OAAO;EAGnC,OAAO;CACR,GACA,SAAS,EACV;CAID,MAAM,WAAW,MAAM,QAAQ,OAAO;CAEtC,IAAI,YACH,MAAM,WAAW,UAAU,OAAO;CAInC,MAAM,QAAQ,MAAM;CAEpB,OAAO;AACR;;;;;;;;;;;;;;;ACjWA,eAAsB,iBACrB,QACA,KACkC;CAClC,MAAM,EAAE,gBAAgB,wBAAwB,MAAM,cAAc;CACpE,MAAM,OAAO,MAAM,eAAe,QAAQ,GAAG;CAE7C,MAAM,UAAkC,CAAC;CACzC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QACjC,oBAAoB,MAAM,MAAM,CACjC,GACC,IAAI,UAAU,KAAA,GAAW,QAAQ,OAAO;CAEzC,OAAO;AACR;;;;;;;;;;;;;;;;ACNA,SAAgB,cACf,UACA,QACU;CACV,MAAM,EAAE,WAAW,GAAG,YAAY;CAClC,MAAM,WAAoC;EACzC;EACA,UAAU,SAAS;EACnB,OAAO,SAAS;EAChB,QAAQ,SAAS;CAClB;CACA,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,GAChD,IAAI,UAAU,KAAA,GAAW,SAAS,OAAO;CAE1C,OAAO,SAAS,OAAO,MAAM,QAAQ;AACtC"}
package/dist/rest-api.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_construct_interface = require("./construct-interface.cjs");
3
- const require_EndpointFactory = require("./EndpointFactory-DeZTvDjU.cjs");
3
+ const require_EndpointFactory = require("./EndpointFactory-BVv6EIDu.cjs");
4
4
  const require_surfaceEnv = require("./surfaceEnv-CNimK76p.cjs");
5
5
  let _geekmidas_manifest = require("@geekmidas/manifest");
6
6
  let _geekmidas_logger_console = require("@geekmidas/logger/console");
@@ -1,7 +1,7 @@
1
- import { i as Declarable, t as Authenticator } from "./construct-interface-DluK3_da.mjs";
2
- import { n as Telemetry } from "./telemetry-DRtT1_0A.mjs";
3
- import { D as BuiltInSecuritySchemeId } from "./Endpoint-C3r2OzOD.mjs";
4
- import { t as EndpointFactory } from "./EndpointFactory-C9eVhDKF.mjs";
1
+ import { i as Declarable, t as Authenticator } from "./construct-interface-ZvYQbivO.mjs";
2
+ import { n as Telemetry } from "./telemetry-BEoBn6GO.mjs";
3
+ import { D as BuiltInSecuritySchemeId } from "./Endpoint-CrE9Ppdk.mjs";
4
+ import { t as EndpointFactory } from "./EndpointFactory-BV-nfnaP.mjs";
5
5
  import { ConstructName, Declaration, Dependency } from "@geekmidas/manifest";
6
6
  import { EnvironmentParser } from "@geekmidas/envkit";
7
7
  import { Logger } from "@geekmidas/logger";
package/dist/rest-api.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { edgeTo } from "./construct-interface.mjs";
2
- import { o as getSecurityScheme, t as EndpointFactory } from "./EndpointFactory-Bbmx4vXc.mjs";
2
+ import { o as getSecurityScheme, t as EndpointFactory } from "./EndpointFactory-Cs8IHvwF.mjs";
3
3
  import { n as envParserFor } from "./surfaceEnv-wMT5QzgZ.mjs";
4
4
  import { canonicalId, provideKey } from "@geekmidas/manifest";
5
5
  import { DEFAULT_LOGGER } from "@geekmidas/logger/console";
package/dist/site.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { i as Declarable } from "./construct-interface-DluK3_da.mjs";
2
- import { n as Telemetry } from "./telemetry-DRtT1_0A.mjs";
1
+ import { i as Declarable } from "./construct-interface-ZvYQbivO.mjs";
2
+ import { n as Telemetry } from "./telemetry-BEoBn6GO.mjs";
3
3
  import { ConstructName, Declaration, Dependency, SiteDeclaration } from "@geekmidas/manifest";
4
4
  //#region src/site.d.ts
5
5
  /**
@@ -1,9 +1,9 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_SubscriberBuilder = require("../SubscriberBuilder-D_owVoHo.cjs");
2
+ const require_SubscriberBuilder = require("../SubscriberBuilder-M2EAgQhC.cjs");
3
3
  const require_runSubscriber = require("../runSubscriber-D8nLXIv1.cjs");
4
4
  let _geekmidas_events = require("@geekmidas/events");
5
- let _geekmidas_errors = require("@geekmidas/errors");
6
5
  let _geekmidas_services = require("@geekmidas/services");
6
+ let _geekmidas_errors = require("@geekmidas/errors");
7
7
  let _geekmidas_events_sns = require("@geekmidas/events/sns");
8
8
  //#region src/subscribers/SnsPushSubscriberAdaptor.ts
9
9
  /**
@@ -1,5 +1,5 @@
1
1
  import { t as Subscriber } from "../Subscriber-CtN9R48-.mjs";
2
- import { t as SubscriberBuilder } from "../SubscriberBuilder-CuCMFpYW.mjs";
2
+ import { t as SubscriberBuilder } from "../SubscriberBuilder-4DWe9feX.mjs";
3
3
  import { EnvironmentParser } from "@geekmidas/envkit";
4
4
  //#region src/subscribers/SnsPushSubscriberAdaptor.d.ts
5
5
  interface SnsPushOptions {
@@ -1,8 +1,8 @@
1
- import { n as Subscriber, t as SubscriberBuilder } from "../SubscriberBuilder-CDy4nHCz.mjs";
1
+ import { n as Subscriber, t as SubscriberBuilder } from "../SubscriberBuilder-DRXfxWn_.mjs";
2
2
  import { n as subscribedEvents, r as subscriberContext, t as runSubscriber } from "../runSubscriber-BHvJ8POr.mjs";
3
3
  import { carrierFromAttributes, consumeTraced } from "@geekmidas/events";
4
- import { wrapError } from "@geekmidas/errors";
5
4
  import { runWithRequestContext } from "@geekmidas/services";
5
+ import { wrapError } from "@geekmidas/errors";
6
6
  import { confirmSnsSubscription, toSnsEvent, verifySnsMessage } from "@geekmidas/events/sns";
7
7
  //#region src/subscribers/SnsPushSubscriberAdaptor.ts
8
8
  /**
@@ -1,4 +1,4 @@
1
- import { i as Declarable } from "./construct-interface-DluK3_da.mjs";
1
+ import { i as Declarable } from "./construct-interface-ZvYQbivO.mjs";
2
2
  import { ConstructName, Declaration } from "@geekmidas/manifest";
3
3
  //#region src/telemetry.d.ts
4
4
  interface TelemetryConfig {
@@ -37,4 +37,4 @@ interface EndpointTelemetry {
37
37
  }
38
38
  //#endregion
39
39
  export { Telemetry as n, TelemetryConfig as r, EndpointTelemetry as t };
40
- //# sourceMappingURL=telemetry-DRtT1_0A.d.mts.map
40
+ //# sourceMappingURL=telemetry-BEoBn6GO.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"telemetry-DRtT1_0A.d.mts","names":[],"sources":["../src/telemetry.ts"],"mappings":";;;UAkCiB;;;;;;;EAOhB;;;;;;EAMA,aAAa,SAAS;;cAGV,UAAU,0CACX,WAAW;WAEb,IAAI;WACJ;WACA,YAAY,SAAS;EAE9B,YAAY,IAAI,cAAc,QAAQ,SAAQ;;;;;;EAW9C,WAAW;;;UAgBK;;EAEhB;;EAEA,aAAa,SAAS"}
1
+ {"version":3,"file":"telemetry-BEoBn6GO.d.mts","names":[],"sources":["../src/telemetry.ts"],"mappings":";;;UAkCiB;;;;;;;EAOhB;;;;;;EAMA,aAAa,SAAS;;cAGV,UAAU,0CACX,WAAW;WAEb,IAAI;WACJ;WACA,YAAY,SAAS;EAE9B,YAAY,IAAI,cAAc,QAAQ,SAAQ;;;;;;EAW9C,WAAW;;;UAgBK;;EAEhB;;EAEA,aAAa,SAAS"}
@@ -1,2 +1,2 @@
1
- import { n as Telemetry, r as TelemetryConfig, t as EndpointTelemetry } from "./telemetry-DRtT1_0A.mjs";
1
+ import { n as Telemetry, r as TelemetryConfig, t as EndpointTelemetry } from "./telemetry-BEoBn6GO.mjs";
2
2
  export { EndpointTelemetry, Telemetry, TelemetryConfig };
@@ -1,3 +1,3 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_Topic = require("../Topic-GHxcU8AG.cjs");
2
+ const require_Topic = require("../Topic-DwS3QO0m.cjs");
3
3
  exports.Topic = require_Topic.Topic;
@@ -1,2 +1,2 @@
1
- import { t as Topic } from "../Topic-D74b5cgs.mjs";
1
+ import { t as Topic } from "../Topic-D2R0r24C.mjs";
2
2
  export { Topic };
@@ -1,5 +1,6 @@
1
1
  import { channel } from "node:diagnostics_channel";
2
2
  import { carrierFromAttributes } from "@geekmidas/events";
3
+ import { identifyRequest } from "@geekmidas/services";
3
4
  import { SpanKind, SpanStatusCode, context, propagation, trace } from "@opentelemetry/api";
4
5
  //#region src/tracing.ts
5
6
  /**
@@ -104,7 +105,23 @@ function injectTraceContext(headers) {
104
105
  if (FETCH_REQUESTS.hasSubscribers) return;
105
106
  propagation.inject(context.active(), headers, { set: (carrier, key, value) => carrier.set(key, value) });
106
107
  }
108
+ /**
109
+ * Say who the current request is for: on its context — so its logger and
110
+ * every service read it — and on its span, as OpenTelemetry's `enduser.id`
111
+ * and `session.id`, so the request's trace can be found by who made it.
112
+ *
113
+ * The span is the active one: the request's own, since a session is verified
114
+ * before the handler opens any of its own. With no provider registered there
115
+ * is none, and only the context is told.
116
+ */
117
+ function identifyUser(user) {
118
+ identifyRequest(user);
119
+ trace.getActiveSpan()?.setAttributes({
120
+ "enduser.id": user.id,
121
+ ...user.sessionId ? { "session.id": user.sessionId } : {}
122
+ });
123
+ }
107
124
  //#endregion
108
- export { recordCarrier as n, traceClient as r, injectTraceContext as t };
125
+ export { traceClient as i, injectTraceContext as n, recordCarrier as r, identifyUser as t };
109
126
 
110
- //# sourceMappingURL=tracing-CxaZ98IB.mjs.map
127
+ //# sourceMappingURL=tracing-Bbj1uoFJ.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tracing-Bbj1uoFJ.mjs","names":[],"sources":["../src/tracing.ts"],"sourcesContent":["/**\n * Where the constructs meet OpenTelemetry, beyond the spans `@geekmidas/events`\n * starts for the brokers: the trace context in a Lambda's records, the spans\n * around an `ExternalApi`'s client, and the context a call to a sibling\n * service carries.\n *\n * Everything goes through the global API, so without a registered provider\n * none of it records anything.\n *\n * @module\n */\nimport { channel } from 'node:diagnostics_channel';\nimport { carrierFromAttributes, type TraceCarrier } from '@geekmidas/events';\nimport { identifyRequest, type RequestUser } from '@geekmidas/services';\nimport {\n\tcontext,\n\tpropagation,\n\tSpanKind,\n\tSpanStatusCode,\n\ttrace,\n} from '@opentelemetry/api';\n\n/**\n * The trace context a Lambda record carries: an SQS record's message\n * attributes, an SNS record's, or those of the SNS envelope an SQS\n * subscription delivers in its body.\n */\nexport function recordCarrier(record: unknown): TraceCarrier | undefined {\n\tconst r = record as {\n\t\tmessageAttributes?: Record<string, unknown>;\n\t\tbody?: string;\n\t\tSns?: { MessageAttributes?: Record<string, unknown> };\n\t};\n\tif (r?.Sns) return carrierFromAttributes(r.Sns.MessageAttributes);\n\n\tconst own = carrierFromAttributes(r?.messageAttributes);\n\tif (own || typeof r?.body !== 'string') return own;\n\ttry {\n\t\tconst body = JSON.parse(r.body);\n\t\treturn body?.Type === 'Notification'\n\t\t\t? carrierFromAttributes(body.MessageAttributes)\n\t\t\t: undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/**\n * `client`, with a span around each of its methods: `<api>.<method>`, carrying\n * the API's name. An outbound `fetch` the method makes is traced already, and\n * becomes this span's child, so the name is on the trace without anything\n * knowing which URL belongs to which API.\n *\n * Only the client's own methods: what they return is the client's business.\n * Methods are called on the client itself, never on the proxy, so private\n * fields keep working.\n */\nexport function traceClient<T>(\n\tapi: string,\n\tclient: T,\n\tattributes: Record<string, string> = {},\n): T {\n\tif (!client || (typeof client !== 'object' && typeof client !== 'function')) {\n\t\treturn client;\n\t}\n\tconst wrapped = new Map<PropertyKey, unknown>();\n\treturn new Proxy(client as object, {\n\t\tget(target, property, receiver) {\n\t\t\tconst value = Reflect.get(target, property, receiver);\n\t\t\tif (typeof value !== 'function' || typeof property === 'symbol') {\n\t\t\t\treturn value;\n\t\t\t}\n\t\t\tif (property === 'constructor' || property === 'then') return value;\n\n\t\t\tconst cached = wrapped.get(property);\n\t\t\tif (cached && (cached as { original?: unknown }).original === value) {\n\t\t\t\treturn cached;\n\t\t\t}\n\t\t\tconst traced = Object.assign(\n\t\t\t\t(...args: unknown[]) =>\n\t\t\t\t\tcallTraced(api, property, attributes, () =>\n\t\t\t\t\t\t(value as (...a: unknown[]) => unknown).apply(target, args),\n\t\t\t\t\t),\n\t\t\t\t{ original: value },\n\t\t\t);\n\t\t\twrapped.set(property, traced);\n\t\t\treturn traced;\n\t\t},\n\t}) as T;\n}\n\nfunction callTraced(\n\tapi: string,\n\tmethod: string,\n\tattributes: Record<string, string>,\n\tcall: () => unknown,\n): unknown {\n\treturn trace.getTracer('@geekmidas/constructs').startActiveSpan(\n\t\t`${api}.${method}`,\n\t\t{\n\t\t\tkind: SpanKind.INTERNAL,\n\t\t\tattributes: {\n\t\t\t\t'gkm.external_api.name': api,\n\t\t\t\t'gkm.external_api.method': method,\n\t\t\t\t...attributes,\n\t\t\t},\n\t\t},\n\t\t(span) => {\n\t\t\tconst fail = (error: unknown) => {\n\t\t\t\tif (error instanceof Error) span.recordException(error);\n\t\t\t\tspan.setStatus({ code: SpanStatusCode.ERROR });\n\t\t\t\tspan.end();\n\t\t\t};\n\t\t\tlet result: unknown;\n\t\t\ttry {\n\t\t\t\tresult = call();\n\t\t\t} catch (error) {\n\t\t\t\tfail(error);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\t// Only a native promise is waited on, and the caller gets it back as\n\t\t\t// it was: a lazy thenable runs on `then`, and a promise subclass has\n\t\t\t// methods a chained promise would not.\n\t\t\tif (result instanceof Promise) {\n\t\t\t\tresult.then(\n\t\t\t\t\t() => span.end(),\n\t\t\t\t\t(error) => fail(error),\n\t\t\t\t);\n\t\t\t\treturn result;\n\t\t\t}\n\t\t\tspan.end();\n\t\t\treturn result;\n\t\t},\n\t);\n}\n\n/**\n * Where an instrumentation of `fetch` hears of each request — undici's own\n * diagnostics channel, which OpenTelemetry's undici instrumentation (and every\n * APM built on it) subscribes to.\n */\nconst FETCH_REQUESTS = channel('undici:request:create');\n\n/**\n * The active trace context, written into `headers` for a call this process\n * makes to one of its own services — the auth server's session check — so the\n * callee's span joins the caller's trace.\n *\n * Through the global propagator, so it is a no-op without one. And not at all\n * when a `fetch` instrumentation is listening: that opens the request's own\n * CLIENT span and writes its `traceparent` by appending a header, so a second\n * one here would reach the callee as two values joined — `a, b` — which no\n * propagator reads, and the trace would break instead of joining.\n */\nexport function injectTraceContext(headers: Headers): void {\n\tif (FETCH_REQUESTS.hasSubscribers) return;\n\tpropagation.inject(context.active(), headers, {\n\t\tset: (carrier, key, value) => carrier.set(key, value),\n\t});\n}\n\n/**\n * Say who the current request is for: on its context — so its logger and\n * every service read it — and on its span, as OpenTelemetry's `enduser.id`\n * and `session.id`, so the request's trace can be found by who made it.\n *\n * The span is the active one: the request's own, since a session is verified\n * before the handler opens any of its own. With no provider registered there\n * is none, and only the context is told.\n */\nexport function identifyUser(user: RequestUser): void {\n\tidentifyRequest(user);\n\ttrace.getActiveSpan()?.setAttributes({\n\t\t'enduser.id': user.id,\n\t\t...(user.sessionId ? { 'session.id': user.sessionId } : {}),\n\t});\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AA2BA,SAAgB,cAAc,QAA2C;CACxE,MAAM,IAAI;CAKV,IAAI,GAAG,KAAK,OAAO,sBAAsB,EAAE,IAAI,iBAAiB;CAEhE,MAAM,MAAM,sBAAsB,GAAG,iBAAiB;CACtD,IAAI,OAAO,OAAO,GAAG,SAAS,UAAU,OAAO;CAC/C,IAAI;EACH,MAAM,OAAO,KAAK,MAAM,EAAE,IAAI;EAC9B,OAAO,MAAM,SAAS,iBACnB,sBAAsB,KAAK,iBAAiB,IAC5C,KAAA;CACJ,QAAQ;EACP;CACD;AACD;;;;;;;;;;;AAYA,SAAgB,YACf,KACA,QACA,aAAqC,CAAC,GAClC;CACJ,IAAI,CAAC,UAAW,OAAO,WAAW,YAAY,OAAO,WAAW,YAC/D,OAAO;CAER,MAAM,0BAAU,IAAI,IAA0B;CAC9C,OAAO,IAAI,MAAM,QAAkB,EAClC,IAAI,QAAQ,UAAU,UAAU;EAC/B,MAAM,QAAQ,QAAQ,IAAI,QAAQ,UAAU,QAAQ;EACpD,IAAI,OAAO,UAAU,cAAc,OAAO,aAAa,UACtD,OAAO;EAER,IAAI,aAAa,iBAAiB,aAAa,QAAQ,OAAO;EAE9D,MAAM,SAAS,QAAQ,IAAI,QAAQ;EACnC,IAAI,UAAW,OAAkC,aAAa,OAC7D,OAAO;EAER,MAAM,SAAS,OAAO,QACpB,GAAG,SACH,WAAW,KAAK,UAAU,kBACxB,MAAuC,MAAM,QAAQ,IAAI,CAC3D,GACD,EAAE,UAAU,MAAM,CACnB;EACA,QAAQ,IAAI,UAAU,MAAM;EAC5B,OAAO;CACR,EACD,CAAC;AACF;AAEA,SAAS,WACR,KACA,QACA,YACA,MACU;CACV,OAAO,MAAM,UAAU,uBAAuB,CAAC,CAAC,gBAC/C,GAAG,IAAI,GAAG,UACV;EACC,MAAM,SAAS;EACf,YAAY;GACX,yBAAyB;GACzB,2BAA2B;GAC3B,GAAG;EACJ;CACD,IACC,SAAS;EACT,MAAM,QAAQ,UAAmB;GAChC,IAAI,iBAAiB,OAAO,KAAK,gBAAgB,KAAK;GACtD,KAAK,UAAU,EAAE,MAAM,eAAe,MAAM,CAAC;GAC7C,KAAK,IAAI;EACV;EACA,IAAI;EACJ,IAAI;GACH,SAAS,KAAK;EACf,SAAS,OAAO;GACf,KAAK,KAAK;GACV,MAAM;EACP;EAIA,IAAI,kBAAkB,SAAS;GAC9B,OAAO,WACA,KAAK,IAAI,IACd,UAAU,KAAK,KAAK,CACtB;GACA,OAAO;EACR;EACA,KAAK,IAAI;EACT,OAAO;CACR,CACD;AACD;;;;;;AAOA,MAAM,iBAAiB,QAAQ,uBAAuB;;;;;;;;;;;;AAatD,SAAgB,mBAAmB,SAAwB;CAC1D,IAAI,eAAe,gBAAgB;CACnC,YAAY,OAAO,QAAQ,OAAO,GAAG,SAAS,EAC7C,MAAM,SAAS,KAAK,UAAU,QAAQ,IAAI,KAAK,KAAK,EACrD,CAAC;AACF;;;;;;;;;;AAWA,SAAgB,aAAa,MAAyB;CACrD,gBAAgB,IAAI;CACpB,MAAM,cAAc,CAAC,EAAE,cAAc;EACpC,cAAc,KAAK;EACnB,GAAI,KAAK,YAAY,EAAE,cAAc,KAAK,UAAU,IAAI,CAAC;CAC1D,CAAC;AACF"}
@@ -1,5 +1,6 @@
1
1
  let node_diagnostics_channel = require("node:diagnostics_channel");
2
2
  let _geekmidas_events = require("@geekmidas/events");
3
+ let _geekmidas_services = require("@geekmidas/services");
3
4
  let _opentelemetry_api = require("@opentelemetry/api");
4
5
  //#region src/tracing.ts
5
6
  /**
@@ -104,7 +105,29 @@ function injectTraceContext(headers) {
104
105
  if (FETCH_REQUESTS.hasSubscribers) return;
105
106
  _opentelemetry_api.propagation.inject(_opentelemetry_api.context.active(), headers, { set: (carrier, key, value) => carrier.set(key, value) });
106
107
  }
108
+ /**
109
+ * Say who the current request is for: on its context — so its logger and
110
+ * every service read it — and on its span, as OpenTelemetry's `enduser.id`
111
+ * and `session.id`, so the request's trace can be found by who made it.
112
+ *
113
+ * The span is the active one: the request's own, since a session is verified
114
+ * before the handler opens any of its own. With no provider registered there
115
+ * is none, and only the context is told.
116
+ */
117
+ function identifyUser(user) {
118
+ (0, _geekmidas_services.identifyRequest)(user);
119
+ _opentelemetry_api.trace.getActiveSpan()?.setAttributes({
120
+ "enduser.id": user.id,
121
+ ...user.sessionId ? { "session.id": user.sessionId } : {}
122
+ });
123
+ }
107
124
  //#endregion
125
+ Object.defineProperty(exports, "identifyUser", {
126
+ enumerable: true,
127
+ get: function() {
128
+ return identifyUser;
129
+ }
130
+ });
108
131
  Object.defineProperty(exports, "injectTraceContext", {
109
132
  enumerable: true,
110
133
  get: function() {
@@ -124,4 +147,4 @@ Object.defineProperty(exports, "traceClient", {
124
147
  }
125
148
  });
126
149
 
127
- //# sourceMappingURL=tracing-DPMidF_r.cjs.map
150
+ //# sourceMappingURL=tracing-C0ooU0V2.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tracing-C0ooU0V2.cjs","names":["carrierFromAttributes","trace","SpanKind","SpanStatusCode","channel","context"],"sources":["../src/tracing.ts"],"sourcesContent":["/**\n * Where the constructs meet OpenTelemetry, beyond the spans `@geekmidas/events`\n * starts for the brokers: the trace context in a Lambda's records, the spans\n * around an `ExternalApi`'s client, and the context a call to a sibling\n * service carries.\n *\n * Everything goes through the global API, so without a registered provider\n * none of it records anything.\n *\n * @module\n */\nimport { channel } from 'node:diagnostics_channel';\nimport { carrierFromAttributes, type TraceCarrier } from '@geekmidas/events';\nimport { identifyRequest, type RequestUser } from '@geekmidas/services';\nimport {\n\tcontext,\n\tpropagation,\n\tSpanKind,\n\tSpanStatusCode,\n\ttrace,\n} from '@opentelemetry/api';\n\n/**\n * The trace context a Lambda record carries: an SQS record's message\n * attributes, an SNS record's, or those of the SNS envelope an SQS\n * subscription delivers in its body.\n */\nexport function recordCarrier(record: unknown): TraceCarrier | undefined {\n\tconst r = record as {\n\t\tmessageAttributes?: Record<string, unknown>;\n\t\tbody?: string;\n\t\tSns?: { MessageAttributes?: Record<string, unknown> };\n\t};\n\tif (r?.Sns) return carrierFromAttributes(r.Sns.MessageAttributes);\n\n\tconst own = carrierFromAttributes(r?.messageAttributes);\n\tif (own || typeof r?.body !== 'string') return own;\n\ttry {\n\t\tconst body = JSON.parse(r.body);\n\t\treturn body?.Type === 'Notification'\n\t\t\t? carrierFromAttributes(body.MessageAttributes)\n\t\t\t: undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/**\n * `client`, with a span around each of its methods: `<api>.<method>`, carrying\n * the API's name. An outbound `fetch` the method makes is traced already, and\n * becomes this span's child, so the name is on the trace without anything\n * knowing which URL belongs to which API.\n *\n * Only the client's own methods: what they return is the client's business.\n * Methods are called on the client itself, never on the proxy, so private\n * fields keep working.\n */\nexport function traceClient<T>(\n\tapi: string,\n\tclient: T,\n\tattributes: Record<string, string> = {},\n): T {\n\tif (!client || (typeof client !== 'object' && typeof client !== 'function')) {\n\t\treturn client;\n\t}\n\tconst wrapped = new Map<PropertyKey, unknown>();\n\treturn new Proxy(client as object, {\n\t\tget(target, property, receiver) {\n\t\t\tconst value = Reflect.get(target, property, receiver);\n\t\t\tif (typeof value !== 'function' || typeof property === 'symbol') {\n\t\t\t\treturn value;\n\t\t\t}\n\t\t\tif (property === 'constructor' || property === 'then') return value;\n\n\t\t\tconst cached = wrapped.get(property);\n\t\t\tif (cached && (cached as { original?: unknown }).original === value) {\n\t\t\t\treturn cached;\n\t\t\t}\n\t\t\tconst traced = Object.assign(\n\t\t\t\t(...args: unknown[]) =>\n\t\t\t\t\tcallTraced(api, property, attributes, () =>\n\t\t\t\t\t\t(value as (...a: unknown[]) => unknown).apply(target, args),\n\t\t\t\t\t),\n\t\t\t\t{ original: value },\n\t\t\t);\n\t\t\twrapped.set(property, traced);\n\t\t\treturn traced;\n\t\t},\n\t}) as T;\n}\n\nfunction callTraced(\n\tapi: string,\n\tmethod: string,\n\tattributes: Record<string, string>,\n\tcall: () => unknown,\n): unknown {\n\treturn trace.getTracer('@geekmidas/constructs').startActiveSpan(\n\t\t`${api}.${method}`,\n\t\t{\n\t\t\tkind: SpanKind.INTERNAL,\n\t\t\tattributes: {\n\t\t\t\t'gkm.external_api.name': api,\n\t\t\t\t'gkm.external_api.method': method,\n\t\t\t\t...attributes,\n\t\t\t},\n\t\t},\n\t\t(span) => {\n\t\t\tconst fail = (error: unknown) => {\n\t\t\t\tif (error instanceof Error) span.recordException(error);\n\t\t\t\tspan.setStatus({ code: SpanStatusCode.ERROR });\n\t\t\t\tspan.end();\n\t\t\t};\n\t\t\tlet result: unknown;\n\t\t\ttry {\n\t\t\t\tresult = call();\n\t\t\t} catch (error) {\n\t\t\t\tfail(error);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\t// Only a native promise is waited on, and the caller gets it back as\n\t\t\t// it was: a lazy thenable runs on `then`, and a promise subclass has\n\t\t\t// methods a chained promise would not.\n\t\t\tif (result instanceof Promise) {\n\t\t\t\tresult.then(\n\t\t\t\t\t() => span.end(),\n\t\t\t\t\t(error) => fail(error),\n\t\t\t\t);\n\t\t\t\treturn result;\n\t\t\t}\n\t\t\tspan.end();\n\t\t\treturn result;\n\t\t},\n\t);\n}\n\n/**\n * Where an instrumentation of `fetch` hears of each request — undici's own\n * diagnostics channel, which OpenTelemetry's undici instrumentation (and every\n * APM built on it) subscribes to.\n */\nconst FETCH_REQUESTS = channel('undici:request:create');\n\n/**\n * The active trace context, written into `headers` for a call this process\n * makes to one of its own services — the auth server's session check — so the\n * callee's span joins the caller's trace.\n *\n * Through the global propagator, so it is a no-op without one. And not at all\n * when a `fetch` instrumentation is listening: that opens the request's own\n * CLIENT span and writes its `traceparent` by appending a header, so a second\n * one here would reach the callee as two values joined — `a, b` — which no\n * propagator reads, and the trace would break instead of joining.\n */\nexport function injectTraceContext(headers: Headers): void {\n\tif (FETCH_REQUESTS.hasSubscribers) return;\n\tpropagation.inject(context.active(), headers, {\n\t\tset: (carrier, key, value) => carrier.set(key, value),\n\t});\n}\n\n/**\n * Say who the current request is for: on its context — so its logger and\n * every service read it — and on its span, as OpenTelemetry's `enduser.id`\n * and `session.id`, so the request's trace can be found by who made it.\n *\n * The span is the active one: the request's own, since a session is verified\n * before the handler opens any of its own. With no provider registered there\n * is none, and only the context is told.\n */\nexport function identifyUser(user: RequestUser): void {\n\tidentifyRequest(user);\n\ttrace.getActiveSpan()?.setAttributes({\n\t\t'enduser.id': user.id,\n\t\t...(user.sessionId ? { 'session.id': user.sessionId } : {}),\n\t});\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AA2BA,SAAgB,cAAc,QAA2C;CACxE,MAAM,IAAI;CAKV,IAAI,GAAG,KAAK,QAAA,GAAOA,kBAAAA,sBAAAA,CAAsB,EAAE,IAAI,iBAAiB;CAEhE,MAAM,OAAA,GAAMA,kBAAAA,sBAAAA,CAAsB,GAAG,iBAAiB;CACtD,IAAI,OAAO,OAAO,GAAG,SAAS,UAAU,OAAO;CAC/C,IAAI;EACH,MAAM,OAAO,KAAK,MAAM,EAAE,IAAI;EAC9B,OAAO,MAAM,SAAS,kBAAA,GACnBA,kBAAAA,sBAAAA,CAAsB,KAAK,iBAAiB,IAC5C,KAAA;CACJ,QAAQ;EACP;CACD;AACD;;;;;;;;;;;AAYA,SAAgB,YACf,KACA,QACA,aAAqC,CAAC,GAClC;CACJ,IAAI,CAAC,UAAW,OAAO,WAAW,YAAY,OAAO,WAAW,YAC/D,OAAO;CAER,MAAM,0BAAU,IAAI,IAA0B;CAC9C,OAAO,IAAI,MAAM,QAAkB,EAClC,IAAI,QAAQ,UAAU,UAAU;EAC/B,MAAM,QAAQ,QAAQ,IAAI,QAAQ,UAAU,QAAQ;EACpD,IAAI,OAAO,UAAU,cAAc,OAAO,aAAa,UACtD,OAAO;EAER,IAAI,aAAa,iBAAiB,aAAa,QAAQ,OAAO;EAE9D,MAAM,SAAS,QAAQ,IAAI,QAAQ;EACnC,IAAI,UAAW,OAAkC,aAAa,OAC7D,OAAO;EAER,MAAM,SAAS,OAAO,QACpB,GAAG,SACH,WAAW,KAAK,UAAU,kBACxB,MAAuC,MAAM,QAAQ,IAAI,CAC3D,GACD,EAAE,UAAU,MAAM,CACnB;EACA,QAAQ,IAAI,UAAU,MAAM;EAC5B,OAAO;CACR,EACD,CAAC;AACF;AAEA,SAAS,WACR,KACA,QACA,YACA,MACU;CACV,OAAOC,mBAAAA,MAAM,UAAU,uBAAuB,CAAC,CAAC,gBAC/C,GAAG,IAAI,GAAG,UACV;EACC,MAAMC,mBAAAA,SAAS;EACf,YAAY;GACX,yBAAyB;GACzB,2BAA2B;GAC3B,GAAG;EACJ;CACD,IACC,SAAS;EACT,MAAM,QAAQ,UAAmB;GAChC,IAAI,iBAAiB,OAAO,KAAK,gBAAgB,KAAK;GACtD,KAAK,UAAU,EAAE,MAAMC,mBAAAA,eAAe,MAAM,CAAC;GAC7C,KAAK,IAAI;EACV;EACA,IAAI;EACJ,IAAI;GACH,SAAS,KAAK;EACf,SAAS,OAAO;GACf,KAAK,KAAK;GACV,MAAM;EACP;EAIA,IAAI,kBAAkB,SAAS;GAC9B,OAAO,WACA,KAAK,IAAI,IACd,UAAU,KAAK,KAAK,CACtB;GACA,OAAO;EACR;EACA,KAAK,IAAI;EACT,OAAO;CACR,CACD;AACD;;;;;;AAOA,MAAM,kBAAA,GAAiBC,yBAAAA,QAAAA,CAAQ,uBAAuB;;;;;;;;;;;;AAatD,SAAgB,mBAAmB,SAAwB;CAC1D,IAAI,eAAe,gBAAgB;CACnC,mBAAA,YAAY,OAAOC,mBAAAA,QAAQ,OAAO,GAAG,SAAS,EAC7C,MAAM,SAAS,KAAK,UAAU,QAAQ,IAAI,KAAK,KAAK,EACrD,CAAC;AACF;;;;;;;;;;AAWA,SAAgB,aAAa,MAAyB;CACrD,CAAA,GAAA,oBAAA,gBAAA,CAAgB,IAAI;CACpB,mBAAA,MAAM,cAAc,CAAC,EAAE,cAAc;EACpC,cAAc,KAAK;EACnB,GAAI,KAAK,YAAY,EAAE,cAAc,KAAK,UAAU,IAAI,CAAC;CAC1D,CAAC;AACF"}
package/dist/worker.cjs CHANGED
@@ -1,10 +1,10 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_construct_interface = require("./construct-interface.cjs");
3
- const require_functions = require("./functions-CfzJG9hK.cjs");
3
+ const require_functions = require("./functions-D7O5SGVc.cjs");
4
4
  const require_surfaceEnv = require("./surfaceEnv-CNimK76p.cjs");
5
- const require_CronBuilder = require("./CronBuilder-C0Ek1-RA.cjs");
6
- const require_QueueBuilder = require("./QueueBuilder-BjuF0UBl.cjs");
7
- const require_SubscriberBuilder = require("./SubscriberBuilder-D_owVoHo.cjs");
5
+ const require_CronBuilder = require("./CronBuilder-BXTG10u5.cjs");
6
+ const require_QueueBuilder = require("./QueueBuilder-DdU2klPu.cjs");
7
+ const require_SubscriberBuilder = require("./SubscriberBuilder-M2EAgQhC.cjs");
8
8
  let _geekmidas_manifest = require("@geekmidas/manifest");
9
9
  let _geekmidas_logger_console = require("@geekmidas/logger/console");
10
10
  //#region src/worker.ts
package/dist/worker.d.mts CHANGED
@@ -1,17 +1,17 @@
1
1
  import { t as StandardSchemaV1 } from "./index-CeEtxDmK.mjs";
2
2
  import { c as Topic, l as TopicEvents, u as TopicMessage } from "./Construct-QXnl3VqT.mjs";
3
3
  import { i as FunctionHandler, n as Function } from "./Function-DuqGcbOI.mjs";
4
- import { c as ServicesOf, i as Declarable, r as Consumable } from "./construct-interface-DluK3_da.mjs";
5
- import { t as FunctionBuilder } from "./index-CTkKOer4.mjs";
6
- import { i as ScheduleExpression } from "./Cron-uYAHPnp7.mjs";
7
- import { n as Telemetry } from "./telemetry-DRtT1_0A.mjs";
8
- import { t as CronBuilder } from "./CronBuilder-DaSUm2Na.mjs";
9
- import { t as QueueBuilder } from "./QueueBuilder-QHsnVt1P.mjs";
10
- import { t as SubscriberBuilder } from "./SubscriberBuilder-CuCMFpYW.mjs";
4
+ import { c as ServicesOf, i as Declarable, r as Consumable } from "./construct-interface-ZvYQbivO.mjs";
5
+ import { t as FunctionBuilder } from "./index-B13kQB_U.mjs";
6
+ import { i as ScheduleExpression } from "./Cron-CQJqqkhY.mjs";
7
+ import { n as Telemetry } from "./telemetry-BEoBn6GO.mjs";
8
+ import { t as CronBuilder } from "./CronBuilder-Xx_zakuc.mjs";
9
+ import { t as QueueBuilder } from "./QueueBuilder-Cx9lKC5d.mjs";
10
+ import { t as SubscriberBuilder } from "./SubscriberBuilder-4DWe9feX.mjs";
11
11
  import "./topic/index.mjs";
12
12
  import { ConstructName, Declaration, Dependency } from "@geekmidas/manifest";
13
- import { EnvironmentParser } from "@geekmidas/envkit";
14
13
  import { Service } from "@geekmidas/services";
14
+ import { EnvironmentParser } from "@geekmidas/envkit";
15
15
  import { Logger } from "@geekmidas/logger";
16
16
  import { ComposableStandardSchema } from "@geekmidas/schema";
17
17
  //#region src/worker.d.ts
package/dist/worker.mjs CHANGED
@@ -1,9 +1,9 @@
1
1
  import { edgeTo } from "./construct-interface.mjs";
2
- import { t as FunctionBuilder } from "./functions-BFizikJU.mjs";
2
+ import { t as FunctionBuilder } from "./functions-CTI-hDem.mjs";
3
3
  import { n as envParserFor } from "./surfaceEnv-wMT5QzgZ.mjs";
4
- import { t as CronBuilder } from "./CronBuilder-Dmix_Pen.mjs";
5
- import { t as QueueBuilder } from "./QueueBuilder-Cue73M4t.mjs";
6
- import { t as SubscriberBuilder } from "./SubscriberBuilder-CDy4nHCz.mjs";
4
+ import { t as CronBuilder } from "./CronBuilder-D3sF9tS_.mjs";
5
+ import { t as QueueBuilder } from "./QueueBuilder-DXu1qxT1.mjs";
6
+ import { t as SubscriberBuilder } from "./SubscriberBuilder-DRXfxWn_.mjs";
7
7
  import { canonicalId } from "@geekmidas/manifest";
8
8
  import { DEFAULT_LOGGER } from "@geekmidas/logger/console";
9
9
  //#region src/worker.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geekmidas/constructs",
3
- "version": "10.0.0-alpha.107",
3
+ "version": "10.0.0-alpha.109",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "exports": {
@@ -297,13 +297,13 @@
297
297
  "lodash.uniqby": "~4.7.0",
298
298
  "openapi-types": "~12.1.3",
299
299
  "qs": "~6.16.0",
300
- "@geekmidas/envkit": "^10.0.0-alpha.107",
301
- "@geekmidas/errors": "^10.0.0-alpha.107",
302
- "@geekmidas/events": "^10.0.0-alpha.107",
303
- "@geekmidas/logger": "^10.0.0-alpha.107",
304
- "@geekmidas/manifest": "^10.0.0-alpha.107",
305
- "@geekmidas/schema": "^10.0.0-alpha.107",
306
- "@geekmidas/services": "^10.0.0-alpha.107"
300
+ "@geekmidas/envkit": "^10.0.0-alpha.109",
301
+ "@geekmidas/errors": "^10.0.0-alpha.109",
302
+ "@geekmidas/events": "^10.0.0-alpha.109",
303
+ "@geekmidas/logger": "^10.0.0-alpha.109",
304
+ "@geekmidas/manifest": "^10.0.0-alpha.109",
305
+ "@geekmidas/schema": "^10.0.0-alpha.109",
306
+ "@geekmidas/services": "^10.0.0-alpha.109"
307
307
  },
308
308
  "devDependencies": {
309
309
  "@aws-sdk/client-kms": "~3.1136.0",
@@ -321,20 +321,20 @@
321
321
  "msw": "~2.15.0",
322
322
  "pg": "~8.23.0",
323
323
  "zod": "~4.6.5",
324
- "@geekmidas/audit": "^10.0.0-alpha.107",
325
- "@geekmidas/auth": "10.0.0-alpha.107",
326
- "@geekmidas/cache": "^10.0.0-alpha.107",
327
- "@geekmidas/db": "^10.0.0-alpha.107",
328
- "@geekmidas/emailkit": "^10.0.0-alpha.107",
329
- "@geekmidas/envkit": "^10.0.0-alpha.107",
330
- "@geekmidas/errors": "^10.0.0-alpha.107",
331
- "@geekmidas/events": "^10.0.0-alpha.107",
332
- "@geekmidas/logger": "^10.0.0-alpha.107",
333
- "@geekmidas/rate-limit": "^10.0.0-alpha.107",
334
- "@geekmidas/schema": "^10.0.0-alpha.107",
335
- "@geekmidas/services": "^10.0.0-alpha.107",
336
- "@geekmidas/telescope": "10.0.0-alpha.107",
337
- "@geekmidas/testkit": "^10.0.0-alpha.107"
324
+ "@geekmidas/audit": "^10.0.0-alpha.109",
325
+ "@geekmidas/auth": "10.0.0-alpha.109",
326
+ "@geekmidas/cache": "^10.0.0-alpha.109",
327
+ "@geekmidas/db": "^10.0.0-alpha.109",
328
+ "@geekmidas/emailkit": "^10.0.0-alpha.109",
329
+ "@geekmidas/envkit": "^10.0.0-alpha.109",
330
+ "@geekmidas/errors": "^10.0.0-alpha.109",
331
+ "@geekmidas/events": "^10.0.0-alpha.109",
332
+ "@geekmidas/logger": "^10.0.0-alpha.109",
333
+ "@geekmidas/rate-limit": "^10.0.0-alpha.109",
334
+ "@geekmidas/schema": "^10.0.0-alpha.109",
335
+ "@geekmidas/services": "^10.0.0-alpha.109",
336
+ "@geekmidas/telescope": "10.0.0-alpha.109",
337
+ "@geekmidas/testkit": "^10.0.0-alpha.109"
338
338
  },
339
339
  "peerDependencies": {
340
340
  "@aws-sdk/client-kms": ">=3.1136.0",
@@ -345,15 +345,15 @@
345
345
  "kysely": "~0.29.6",
346
346
  "msw": ">=2.15.0",
347
347
  "pg": ">=8.23.0",
348
- "@geekmidas/audit": "^10.0.0-alpha.107",
349
- "@geekmidas/auth": "10.0.0-alpha.107",
350
- "@geekmidas/cache": "^10.0.0-alpha.107",
351
- "@geekmidas/db": "^10.0.0-alpha.107",
352
- "@geekmidas/emailkit": "^10.0.0-alpha.107",
353
- "@geekmidas/rate-limit": "^10.0.0-alpha.107",
354
- "@geekmidas/storage": "^10.0.0-alpha.107",
355
- "@geekmidas/telescope": "10.0.0-alpha.107",
356
- "@geekmidas/testkit": "^10.0.0-alpha.107"
348
+ "@geekmidas/audit": "^10.0.0-alpha.109",
349
+ "@geekmidas/auth": "10.0.0-alpha.109",
350
+ "@geekmidas/cache": "^10.0.0-alpha.109",
351
+ "@geekmidas/db": "^10.0.0-alpha.109",
352
+ "@geekmidas/emailkit": "^10.0.0-alpha.109",
353
+ "@geekmidas/rate-limit": "^10.0.0-alpha.109",
354
+ "@geekmidas/storage": "^10.0.0-alpha.109",
355
+ "@geekmidas/telescope": "10.0.0-alpha.109",
356
+ "@geekmidas/testkit": "^10.0.0-alpha.109"
357
357
  },
358
358
  "peerDependenciesMeta": {
359
359
  "@aws-sdk/client-kms": {
@@ -1 +0,0 @@
1
- {"version":3,"file":"Construct-B1vGBPme.mjs","names":["compact"],"sources":["../../../node_modules/.pnpm/lodash.compact@3.0.1/node_modules/lodash.compact/index.js","../src/Construct.ts"],"sourcesContent":["/**\n * lodash 3.0.1 (Custom Build) <https://lodash.com/>\n * Build: `lodash modularize exports=\"npm\" -o ./`\n * Copyright 2012-2016 The Dojo Foundation <http://dojofoundation.org/>\n * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>\n * Copyright 2009-2016 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors\n * Available under MIT license <https://lodash.com/license>\n */\n\n/**\n * Creates an array with all falsey values removed. The values `false`, `null`,\n * `0`, `\"\"`, `undefined`, and `NaN` are falsey.\n *\n * @static\n * @memberOf _\n * @category Array\n * @param {Array} array The array to compact.\n * @returns {Array} Returns the new array of filtered values.\n * @example\n *\n * _.compact([0, 1, false, 2, '', 3]);\n * // => [1, 2, 3]\n */\nfunction compact(array) {\n var index = -1,\n length = array ? array.length : 0,\n resIndex = 0,\n result = [];\n\n while (++index < length) {\n var value = array[index];\n if (value) {\n result[resIndex++] = value;\n }\n }\n return result;\n}\n\nmodule.exports = compact;\n","import type { AuditStorage } from '@geekmidas/audit';\nimport {\n\tSnifferEnvironmentParser,\n\ttype SniffResult,\n\tsniffWithFireAndForget,\n} from '@geekmidas/envkit/sniffer';\nimport type { Logger } from '@geekmidas/logger';\nimport type { Service, ServiceContext } from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport compact from 'lodash.compact';\nimport type { TopicEvent } from './publisher';\n\n// Cache for service environment variables to handle singleton services\n// Stores both the full list and the optional subset so the same sniff result\n// can satisfy both getEnvironment() and getEnvironment({ markOptional: true }).\nconst serviceEnvCache = new Map<\n\tService,\n\t{ envVars: string[]; optionalEnvVars: string[] }\n>();\n\n/**\n * Noop context for environment sniffing.\n * Used when calling service.register() to detect env vars without a real request.\n * Returns dummy values since services should only use context in instance methods,\n * not during registration.\n */\nconst snifferContext: ServiceContext = {\n\tgetLogger() {\n\t\t// Return a noop logger for sniffing - services shouldn't log during registration\n\t\treturn {\n\t\t\tdebug: () => {},\n\t\t\tinfo: () => {},\n\t\t\twarn: () => {},\n\t\t\terror: () => {},\n\t\t\tchild: () => snifferContext.getLogger(),\n\t\t} as unknown as Logger;\n\t},\n\tgetRequestId() {\n\t\treturn 'sniffer-context';\n\t},\n\tgetRequestStartTime() {\n\t\treturn Date.now();\n\t},\n\thasContext() {\n\t\treturn false;\n\t},\n};\n\nexport abstract class Construct<\n\tTLogger extends Logger = Logger,\n\tOutSchema extends StandardSchemaV1 | undefined = undefined,\n\tTServices extends Service[] = [],\n\tTAuditStorageServiceName extends string = string,\n\tTAuditStorage extends AuditStorage | undefined = undefined,\n\tTDatabaseServiceName extends string = string,\n\tTDatabase = unknown,\n> {\n\t/** Database service for this construct (used by Endpoint for RLS/audit context) */\n\tpublic databaseService?: Service<TDatabaseServiceName, TDatabase>;\n\n\t/**\n\t * The deploy unit that runs this, by construct id.\n\t *\n\t * What an endpoint's `surface` is to it, and for the same reason: something\n\t * has to say which process a runnable belongs to, and until this existed the\n\t * only thing that said so was the directory its file sat in. Set by the\n\t * factory it was built from — `worker.crons`, `api.get(…)` — so it is\n\t * carried rather than configured.\n\t *\n\t * Undefined for a construct built from a free-standing builder, which is\n\t * still how `c`, `s` and `f` work.\n\t */\n\tpublic owner?: string;\n\n\tconstructor(\n\t\tpublic readonly type: ConstructType,\n\t\tpublic readonly logger: TLogger,\n\t\tpublic readonly services: TServices,\n\t\t/**\n\t\t * What this construct publishes once its handler succeeds — each event\n\t\t * bound to its own topic, so a construct publishes to as many as it names.\n\t\t */\n\t\tpublic readonly events: TopicEvent[] = [],\n\t\tpublic outputSchema?: OutSchema,\n\t\tpublic readonly timeout?: number,\n\t\tpublic readonly memorySize?: number,\n\t\tpublic readonly auditorStorageService?: Service<\n\t\t\tTAuditStorageServiceName,\n\t\t\tTAuditStorage\n\t\t>,\n\t\t/**\n\t\t * The constructs this one declared an edge to, by id.\n\t\t *\n\t\t * What the manifest records. `services` is the same information in the\n\t\t * form a handler runs with — clients, keyed by name — and an id cannot be\n\t\t * recovered from one of those reliably, so both are kept.\n\t\t *\n\t\t * Last in the list because every other parameter is positional and already\n\t\t * spoken for; a new one anywhere else silently rebinds existing callers.\n\t\t */\n\t\tpublic readonly constructs: string[] = [],\n\t) {}\n\n\t/**\n\t * Returns an array of environment variable names required by this construct's services.\n\t * This is determined by running a \"sniffer\" EnvironmentParser through each service's\n\t * register method to track which environment variables are accessed.\n\t *\n\t * Results are cached per service class to handle singleton patterns where\n\t * subsequent register() calls may short-circuit and not access env vars.\n\t *\n\t * @param markOptional - When true, optional vars (those accessed via `.optional()` or\n\t * `.default()`) are suffixed with `?` in the returned array (e.g. `'PORT?'`).\n\t * @returns Promise that resolves to array of environment variable names, sorted alphabetically\n\t *\n\t * @example\n\t * ```typescript\n\t * const endpoint = e\n\t * .services([databaseService, authService])\n\t * .get('/users')\n\t * .handle(async () => []);\n\t *\n\t * await endpoint.getEnvironment(); // ['AUTH_SECRET', 'DATABASE_URL']\n\t * await endpoint.getEnvironment({ markOptional: true }); // ['AUTH_SECRET', 'DATABASE_URL', 'PORT?']\n\t * ```\n\t */\n\tasync getEnvironment(\n\t\topts: { markOptional?: boolean } = {},\n\t): Promise<string[]> {\n\t\tconst { markOptional = false } = opts;\n\t\tconst envVars = new Set<string>();\n\t\tconst optionalVars = new Set<string>();\n\t\tconst services: Service[] = compact([\n\t\t\t...this.services,\n\t\t\t// Each topic an event names is a dependency like any other.\n\t\t\t...this.events.map(({ topic }) => topic),\n\t\t\tthis.auditorStorageService,\n\t\t\tthis.databaseService,\n\t\t]);\n\n\t\ttry {\n\t\t\tfor (const service of services) {\n\t\t\t\t// Check cache first - handles singleton services that short-circuit\n\t\t\t\tif (serviceEnvCache.has(service)) {\n\t\t\t\t\tconst cached = serviceEnvCache.get(service)!;\n\t\t\t\t\tcached.envVars.forEach((v) => envVars.add(v));\n\t\t\t\t\tcached.optionalEnvVars.forEach((v) => optionalVars.add(v));\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Use sniffService to properly handle all error scenarios\n\t\t\t\tconst result = await sniffService(service);\n\n\t\t\t\t// Log any issues for debugging\n\t\t\t\tif (result.error) {\n\t\t\t\t\tthis.logger.warn(\n\t\t\t\t\t\t{ error: result.error.message, service: service.serviceName },\n\t\t\t\t\t\t'Service threw error during env sniffing (env vars still captured)',\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (result.unhandledRejections.length > 0) {\n\t\t\t\t\tthis.logger.warn(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\terrors: result.unhandledRejections.map((e) => e.message),\n\t\t\t\t\t\t\tservice: service.serviceName,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t'Fire-and-forget rejections during env sniffing (suppressed)',\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\t// Cache and collect env vars (both required and optional)\n\t\t\t\tserviceEnvCache.set(service, {\n\t\t\t\t\tenvVars: result.envVars,\n\t\t\t\t\toptionalEnvVars: result.optionalEnvVars,\n\t\t\t\t});\n\t\t\t\tresult.envVars.forEach((v) => envVars.add(v));\n\t\t\t\tresult.optionalEnvVars.forEach((v) => optionalVars.add(v));\n\t\t\t}\n\n\t\t\tconst sorted = Array.from(envVars).sort();\n\t\t\tif (!markOptional || optionalVars.size === 0) return sorted;\n\t\t\treturn sorted.map((v) => (optionalVars.has(v) ? `${v}?` : v));\n\t\t} catch (error) {\n\t\t\tthis.logger.error(\n\t\t\t\t{ err: error },\n\t\t\t\t'Error determining environment variables for construct',\n\t\t\t);\n\n\t\t\treturn [];\n\t\t}\n\t}\n}\n\nexport enum ConstructType {\n\tCron = 'dev.geekmidas.function.cron',\n\tEndpoint = 'dev.geekmidas.function.endpoint',\n\tFunction = 'dev.geekmidas.function.function',\n\tSubscriber = 'dev.geekmidas.function.subscriber',\n\tQueue = 'dev.geekmidas.function.queue',\n\tTopic = 'dev.geekmidas.resource.topic',\n}\n\n/**\n * Utility to test sniffing a service for environment variables.\n * Useful for debugging services that throw errors during sniffing.\n *\n * @example\n * ```typescript\n * import { sniffService } from '@geekmidas/constructs';\n * import { authService } from './services/AuthService';\n *\n * const result = await sniffService(authService);\n * console.log('Env vars:', result.envVars);\n * console.log('Error:', result.error);\n * ```\n */\nexport async function sniffService(service: Service): Promise<SniffResult> {\n\tconst sniffer = new SnifferEnvironmentParser();\n\n\treturn sniffWithFireAndForget(sniffer, () =>\n\t\tservice.register({\n\t\t\tenvParser: sniffer as any,\n\t\t\tcontext: snifferContext,\n\t\t}),\n\t);\n}\n\n// Export for testing\nexport { snifferContext };\n"],"x_google_ignoreList":[0],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"Construct-DUSXuTHP.cjs","names":["compact","SnifferEnvironmentParser","sniffWithFireAndForget"],"sources":["../../../node_modules/.pnpm/lodash.compact@3.0.1/node_modules/lodash.compact/index.js","../src/Construct.ts"],"sourcesContent":["/**\n * lodash 3.0.1 (Custom Build) <https://lodash.com/>\n * Build: `lodash modularize exports=\"npm\" -o ./`\n * Copyright 2012-2016 The Dojo Foundation <http://dojofoundation.org/>\n * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>\n * Copyright 2009-2016 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors\n * Available under MIT license <https://lodash.com/license>\n */\n\n/**\n * Creates an array with all falsey values removed. The values `false`, `null`,\n * `0`, `\"\"`, `undefined`, and `NaN` are falsey.\n *\n * @static\n * @memberOf _\n * @category Array\n * @param {Array} array The array to compact.\n * @returns {Array} Returns the new array of filtered values.\n * @example\n *\n * _.compact([0, 1, false, 2, '', 3]);\n * // => [1, 2, 3]\n */\nfunction compact(array) {\n var index = -1,\n length = array ? array.length : 0,\n resIndex = 0,\n result = [];\n\n while (++index < length) {\n var value = array[index];\n if (value) {\n result[resIndex++] = value;\n }\n }\n return result;\n}\n\nmodule.exports = compact;\n","import type { AuditStorage } from '@geekmidas/audit';\nimport {\n\tSnifferEnvironmentParser,\n\ttype SniffResult,\n\tsniffWithFireAndForget,\n} from '@geekmidas/envkit/sniffer';\nimport type { Logger } from '@geekmidas/logger';\nimport type { Service, ServiceContext } from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport compact from 'lodash.compact';\nimport type { TopicEvent } from './publisher';\n\n// Cache for service environment variables to handle singleton services\n// Stores both the full list and the optional subset so the same sniff result\n// can satisfy both getEnvironment() and getEnvironment({ markOptional: true }).\nconst serviceEnvCache = new Map<\n\tService,\n\t{ envVars: string[]; optionalEnvVars: string[] }\n>();\n\n/**\n * Noop context for environment sniffing.\n * Used when calling service.register() to detect env vars without a real request.\n * Returns dummy values since services should only use context in instance methods,\n * not during registration.\n */\nconst snifferContext: ServiceContext = {\n\tgetLogger() {\n\t\t// Return a noop logger for sniffing - services shouldn't log during registration\n\t\treturn {\n\t\t\tdebug: () => {},\n\t\t\tinfo: () => {},\n\t\t\twarn: () => {},\n\t\t\terror: () => {},\n\t\t\tchild: () => snifferContext.getLogger(),\n\t\t} as unknown as Logger;\n\t},\n\tgetRequestId() {\n\t\treturn 'sniffer-context';\n\t},\n\tgetRequestStartTime() {\n\t\treturn Date.now();\n\t},\n\thasContext() {\n\t\treturn false;\n\t},\n};\n\nexport abstract class Construct<\n\tTLogger extends Logger = Logger,\n\tOutSchema extends StandardSchemaV1 | undefined = undefined,\n\tTServices extends Service[] = [],\n\tTAuditStorageServiceName extends string = string,\n\tTAuditStorage extends AuditStorage | undefined = undefined,\n\tTDatabaseServiceName extends string = string,\n\tTDatabase = unknown,\n> {\n\t/** Database service for this construct (used by Endpoint for RLS/audit context) */\n\tpublic databaseService?: Service<TDatabaseServiceName, TDatabase>;\n\n\t/**\n\t * The deploy unit that runs this, by construct id.\n\t *\n\t * What an endpoint's `surface` is to it, and for the same reason: something\n\t * has to say which process a runnable belongs to, and until this existed the\n\t * only thing that said so was the directory its file sat in. Set by the\n\t * factory it was built from — `worker.crons`, `api.get(…)` — so it is\n\t * carried rather than configured.\n\t *\n\t * Undefined for a construct built from a free-standing builder, which is\n\t * still how `c`, `s` and `f` work.\n\t */\n\tpublic owner?: string;\n\n\tconstructor(\n\t\tpublic readonly type: ConstructType,\n\t\tpublic readonly logger: TLogger,\n\t\tpublic readonly services: TServices,\n\t\t/**\n\t\t * What this construct publishes once its handler succeeds — each event\n\t\t * bound to its own topic, so a construct publishes to as many as it names.\n\t\t */\n\t\tpublic readonly events: TopicEvent[] = [],\n\t\tpublic outputSchema?: OutSchema,\n\t\tpublic readonly timeout?: number,\n\t\tpublic readonly memorySize?: number,\n\t\tpublic readonly auditorStorageService?: Service<\n\t\t\tTAuditStorageServiceName,\n\t\t\tTAuditStorage\n\t\t>,\n\t\t/**\n\t\t * The constructs this one declared an edge to, by id.\n\t\t *\n\t\t * What the manifest records. `services` is the same information in the\n\t\t * form a handler runs with — clients, keyed by name — and an id cannot be\n\t\t * recovered from one of those reliably, so both are kept.\n\t\t *\n\t\t * Last in the list because every other parameter is positional and already\n\t\t * spoken for; a new one anywhere else silently rebinds existing callers.\n\t\t */\n\t\tpublic readonly constructs: string[] = [],\n\t) {}\n\n\t/**\n\t * Returns an array of environment variable names required by this construct's services.\n\t * This is determined by running a \"sniffer\" EnvironmentParser through each service's\n\t * register method to track which environment variables are accessed.\n\t *\n\t * Results are cached per service class to handle singleton patterns where\n\t * subsequent register() calls may short-circuit and not access env vars.\n\t *\n\t * @param markOptional - When true, optional vars (those accessed via `.optional()` or\n\t * `.default()`) are suffixed with `?` in the returned array (e.g. `'PORT?'`).\n\t * @returns Promise that resolves to array of environment variable names, sorted alphabetically\n\t *\n\t * @example\n\t * ```typescript\n\t * const endpoint = e\n\t * .services([databaseService, authService])\n\t * .get('/users')\n\t * .handle(async () => []);\n\t *\n\t * await endpoint.getEnvironment(); // ['AUTH_SECRET', 'DATABASE_URL']\n\t * await endpoint.getEnvironment({ markOptional: true }); // ['AUTH_SECRET', 'DATABASE_URL', 'PORT?']\n\t * ```\n\t */\n\tasync getEnvironment(\n\t\topts: { markOptional?: boolean } = {},\n\t): Promise<string[]> {\n\t\tconst { markOptional = false } = opts;\n\t\tconst envVars = new Set<string>();\n\t\tconst optionalVars = new Set<string>();\n\t\tconst services: Service[] = compact([\n\t\t\t...this.services,\n\t\t\t// Each topic an event names is a dependency like any other.\n\t\t\t...this.events.map(({ topic }) => topic),\n\t\t\tthis.auditorStorageService,\n\t\t\tthis.databaseService,\n\t\t]);\n\n\t\ttry {\n\t\t\tfor (const service of services) {\n\t\t\t\t// Check cache first - handles singleton services that short-circuit\n\t\t\t\tif (serviceEnvCache.has(service)) {\n\t\t\t\t\tconst cached = serviceEnvCache.get(service)!;\n\t\t\t\t\tcached.envVars.forEach((v) => envVars.add(v));\n\t\t\t\t\tcached.optionalEnvVars.forEach((v) => optionalVars.add(v));\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Use sniffService to properly handle all error scenarios\n\t\t\t\tconst result = await sniffService(service);\n\n\t\t\t\t// Log any issues for debugging\n\t\t\t\tif (result.error) {\n\t\t\t\t\tthis.logger.warn(\n\t\t\t\t\t\t{ error: result.error.message, service: service.serviceName },\n\t\t\t\t\t\t'Service threw error during env sniffing (env vars still captured)',\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (result.unhandledRejections.length > 0) {\n\t\t\t\t\tthis.logger.warn(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\terrors: result.unhandledRejections.map((e) => e.message),\n\t\t\t\t\t\t\tservice: service.serviceName,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t'Fire-and-forget rejections during env sniffing (suppressed)',\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\t// Cache and collect env vars (both required and optional)\n\t\t\t\tserviceEnvCache.set(service, {\n\t\t\t\t\tenvVars: result.envVars,\n\t\t\t\t\toptionalEnvVars: result.optionalEnvVars,\n\t\t\t\t});\n\t\t\t\tresult.envVars.forEach((v) => envVars.add(v));\n\t\t\t\tresult.optionalEnvVars.forEach((v) => optionalVars.add(v));\n\t\t\t}\n\n\t\t\tconst sorted = Array.from(envVars).sort();\n\t\t\tif (!markOptional || optionalVars.size === 0) return sorted;\n\t\t\treturn sorted.map((v) => (optionalVars.has(v) ? `${v}?` : v));\n\t\t} catch (error) {\n\t\t\tthis.logger.error(\n\t\t\t\t{ err: error },\n\t\t\t\t'Error determining environment variables for construct',\n\t\t\t);\n\n\t\t\treturn [];\n\t\t}\n\t}\n}\n\nexport enum ConstructType {\n\tCron = 'dev.geekmidas.function.cron',\n\tEndpoint = 'dev.geekmidas.function.endpoint',\n\tFunction = 'dev.geekmidas.function.function',\n\tSubscriber = 'dev.geekmidas.function.subscriber',\n\tQueue = 'dev.geekmidas.function.queue',\n\tTopic = 'dev.geekmidas.resource.topic',\n}\n\n/**\n * Utility to test sniffing a service for environment variables.\n * Useful for debugging services that throw errors during sniffing.\n *\n * @example\n * ```typescript\n * import { sniffService } from '@geekmidas/constructs';\n * import { authService } from './services/AuthService';\n *\n * const result = await sniffService(authService);\n * console.log('Env vars:', result.envVars);\n * console.log('Error:', result.error);\n * ```\n */\nexport async function sniffService(service: Service): Promise<SniffResult> {\n\tconst sniffer = new SnifferEnvironmentParser();\n\n\treturn sniffWithFireAndForget(sniffer, () =>\n\t\tservice.register({\n\t\t\tenvParser: sniffer as any,\n\t\t\tcontext: snifferContext,\n\t\t}),\n\t);\n}\n\n// Export for testing\nexport { snifferContext };\n"],"x_google_ignoreList":[0],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;CAuBA,SAAS,QAAQ,OAAO;EACtB,IAAI,QAAQ,IACR,SAAS,QAAQ,MAAM,SAAS,GAChC,WAAW,GACX,SAAS,CAAC;EAEd,OAAO,EAAE,QAAQ,QAAQ;GACvB,IAAI,QAAQ,MAAM;GAClB,IAAI,OACF,OAAO,cAAc;EAEzB;EACA,OAAO;CACT;CAEA,OAAO,UAAU;;;;;ACvBjB,MAAM,kCAAkB,IAAI,IAG1B;;;;;;;AAQF,MAAM,iBAAiC;CACtC,YAAY;EAEX,OAAO;GACN,aAAa,CAAC;GACd,YAAY,CAAC;GACb,YAAY,CAAC;GACb,aAAa,CAAC;GACd,aAAa,eAAe,UAAU;EACvC;CACD;CACA,eAAe;EACd,OAAO;CACR;CACA,sBAAsB;EACrB,OAAO,KAAK,IAAI;CACjB;CACA,aAAa;EACZ,OAAO;CACR;AACD;AAEA,IAAsB,YAAtB,MAQE;CAmBgB;CACA;CACA;CAKA;CACT;CACS;CACA;CACA;CAcA;;CA1CjB;;;;;;;;;;;;;CAcA;CAEA,YACC,MACA,QACA,UAKA,SAAuC,CAAC,GACxC,cACA,SACA,YACA,uBAcA,aAAuC,CAAC,GACvC;EA1Be,KAAA,OAAA;EACA,KAAA,SAAA;EACA,KAAA,WAAA;EAKA,KAAA,SAAA;EACT,KAAA,eAAA;EACS,KAAA,UAAA;EACA,KAAA,aAAA;EACA,KAAA,wBAAA;EAcA,KAAA,aAAA;CACd;;;;;;;;;;;;;;;;;;;;;;;;CAyBH,MAAM,eACL,OAAmC,CAAC,GAChB;EACpB,MAAM,EAAE,eAAe,UAAU;EACjC,MAAM,0BAAU,IAAI,IAAY;EAChC,MAAM,+BAAe,IAAI,IAAY;EACrC,MAAM,YAAA,GAAsBA,sBAAAA,QAAAA,CAAQ;GACnC,GAAG,KAAK;GAER,GAAG,KAAK,OAAO,KAAK,EAAE,YAAY,KAAK;GACvC,KAAK;GACL,KAAK;EACN,CAAC;EAED,IAAI;GACH,KAAK,MAAM,WAAW,UAAU;IAE/B,IAAI,gBAAgB,IAAI,OAAO,GAAG;KACjC,MAAM,SAAS,gBAAgB,IAAI,OAAO;KAC1C,OAAO,QAAQ,SAAS,MAAM,QAAQ,IAAI,CAAC,CAAC;KAC5C,OAAO,gBAAgB,SAAS,MAAM,aAAa,IAAI,CAAC,CAAC;KACzD;IACD;IAGA,MAAM,SAAS,MAAM,aAAa,OAAO;IAGzC,IAAI,OAAO,OACV,KAAK,OAAO,KACX;KAAE,OAAO,OAAO,MAAM;KAAS,SAAS,QAAQ;IAAY,GAC5D,mEACD;IAED,IAAI,OAAO,oBAAoB,SAAS,GACvC,KAAK,OAAO,KACX;KACC,QAAQ,OAAO,oBAAoB,KAAK,MAAM,EAAE,OAAO;KACvD,SAAS,QAAQ;IAClB,GACA,6DACD;IAID,gBAAgB,IAAI,SAAS;KAC5B,SAAS,OAAO;KAChB,iBAAiB,OAAO;IACzB,CAAC;IACD,OAAO,QAAQ,SAAS,MAAM,QAAQ,IAAI,CAAC,CAAC;IAC5C,OAAO,gBAAgB,SAAS,MAAM,aAAa,IAAI,CAAC,CAAC;GAC1D;GAEA,MAAM,SAAS,MAAM,KAAK,OAAO,CAAC,CAAC,KAAK;GACxC,IAAI,CAAC,gBAAgB,aAAa,SAAS,GAAG,OAAO;GACrD,OAAO,OAAO,KAAK,MAAO,aAAa,IAAI,CAAC,IAAI,GAAG,EAAE,KAAK,CAAE;EAC7D,SAAS,OAAO;GACf,KAAK,OAAO,MACX,EAAE,KAAK,MAAM,GACb,uDACD;GAEA,OAAO,CAAC;EACT;CACD;AACD;AAEA,IAAY,gBAAL,yBAAA,eAAA;CACN,cAAA,UAAA;CACA,cAAA,cAAA;CACA,cAAA,cAAA;CACA,cAAA,gBAAA;CACA,cAAA,WAAA;CACA,cAAA,WAAA;;AACD,EAAA,CAAA,CAAA;;;;;;;;;;;;;;;AAgBA,eAAsB,aAAa,SAAwC;CAC1E,MAAM,UAAU,IAAIC,0BAAAA,yBAAyB;CAE7C,QAAA,GAAOC,0BAAAA,uBAAAA,CAAuB,eAC7B,QAAQ,SAAS;EAChB,WAAW;EACX,SAAS;CACV,CAAC,CACF;AACD"}