@geekmidas/cli 10.0.0-alpha.81 → 10.0.0-alpha.83

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 (148) hide show
  1. package/dist/{EndpointGenerator-DftalLYg.mjs → EndpointGenerator-DtnIGafh.mjs} +71 -8
  2. package/dist/EndpointGenerator-DtnIGafh.mjs.map +1 -0
  3. package/dist/{EndpointGenerator-CP6sBvQV.cjs → EndpointGenerator-cKlnllIu.cjs} +88 -7
  4. package/dist/EndpointGenerator-cKlnllIu.cjs.map +1 -0
  5. package/dist/{LegacyStateStore-Ceq7A_75.mjs → LegacyStateStore-DFO3aJgs.mjs} +2 -2
  6. package/dist/{LegacyStateStore-Ceq7A_75.mjs.map → LegacyStateStore-DFO3aJgs.mjs.map} +1 -1
  7. package/dist/{LegacyStateStore-Z-EKUGSX.cjs → LegacyStateStore-iUWVxrXU.cjs} +2 -2
  8. package/dist/{LegacyStateStore-Z-EKUGSX.cjs.map → LegacyStateStore-iUWVxrXU.cjs.map} +1 -1
  9. package/dist/{LocalStateStore-Bocb8dmZ.cjs → LocalStateStore-BsFHdSeA.cjs} +2 -2
  10. package/dist/{LocalStateStore-Bocb8dmZ.cjs.map → LocalStateStore-BsFHdSeA.cjs.map} +1 -1
  11. package/dist/{LocalStateStore-C72bwpG2.mjs → LocalStateStore-DIfuh0Z1.mjs} +2 -2
  12. package/dist/{LocalStateStore-C72bwpG2.mjs.map → LocalStateStore-DIfuh0Z1.mjs.map} +1 -1
  13. package/dist/{OpenApiTsGenerator-nu1r1NgB.mjs → OpenApiTsGenerator-ByRPMbBN.mjs} +34 -7
  14. package/dist/OpenApiTsGenerator-ByRPMbBN.mjs.map +1 -0
  15. package/dist/{OpenApiTsGenerator-CKBCckKz.cjs → OpenApiTsGenerator-CdUM6Hvv.cjs} +34 -7
  16. package/dist/OpenApiTsGenerator-CdUM6Hvv.cjs.map +1 -0
  17. package/dist/{S3StateStore-C9upSW5o.mjs → S3StateStore-DR2f78Dj.mjs} +2 -2
  18. package/dist/{S3StateStore-C9upSW5o.mjs.map → S3StateStore-DR2f78Dj.mjs.map} +1 -1
  19. package/dist/{S3StateStore-35YfcdL_.cjs → S3StateStore-ytvpIiv8.cjs} +2 -2
  20. package/dist/{S3StateStore-35YfcdL_.cjs.map → S3StateStore-ytvpIiv8.cjs.map} +1 -1
  21. package/dist/{SSMStateStore-dOmFO6g1.mjs → SSMStateStore-BZ3XL0Mr.mjs} +2 -2
  22. package/dist/{SSMStateStore-dOmFO6g1.mjs.map → SSMStateStore-BZ3XL0Mr.mjs.map} +1 -1
  23. package/dist/{SSMStateStore-W-Ps22AQ.cjs → SSMStateStore-C1JruCD9.cjs} +2 -2
  24. package/dist/{SSMStateStore-W-Ps22AQ.cjs.map → SSMStateStore-C1JruCD9.cjs.map} +1 -1
  25. package/dist/{SubscriberGenerator-CajwOa5W.cjs → SubscriberGenerator-CYriX5BV.cjs} +2 -2
  26. package/dist/{SubscriberGenerator-CajwOa5W.cjs.map → SubscriberGenerator-CYriX5BV.cjs.map} +1 -1
  27. package/dist/{SubscriberGenerator-5looQ3gB.mjs → SubscriberGenerator-DQZeGkDW.mjs} +2 -2
  28. package/dist/{SubscriberGenerator-5looQ3gB.mjs.map → SubscriberGenerator-DQZeGkDW.mjs.map} +1 -1
  29. package/dist/{add-DF9IHQov.cjs → add-Bwddb4KD.cjs} +3 -3
  30. package/dist/{add-DF9IHQov.cjs.map → add-Bwddb4KD.cjs.map} +1 -1
  31. package/dist/{add-Bn1bqt0c.mjs → add-DkBEZ5_s.mjs} +3 -3
  32. package/dist/{add-Bn1bqt0c.mjs.map → add-DkBEZ5_s.mjs.map} +1 -1
  33. package/dist/{bundler-Ba_LoFXI.mjs → bundler-B8TGsunu.mjs} +5 -5
  34. package/dist/{bundler-Ba_LoFXI.mjs.map → bundler-B8TGsunu.mjs.map} +1 -1
  35. package/dist/{bundler-DeIVbloM.cjs → bundler-BkH1kPEX.cjs} +5 -5
  36. package/dist/{bundler-DeIVbloM.cjs.map → bundler-BkH1kPEX.cjs.map} +1 -1
  37. package/dist/{cli-CznHL-4-.mjs → cli-BgDd4cZB.mjs} +3 -3
  38. package/dist/{cli-CznHL-4-.mjs.map → cli-BgDd4cZB.mjs.map} +1 -1
  39. package/dist/{cli-BB1KOkfo.cjs → cli-DO7DQqCq.cjs} +3 -3
  40. package/dist/{cli-BB1KOkfo.cjs.map → cli-DO7DQqCq.cjs.map} +1 -1
  41. package/dist/{compose-BxIsChn5.mjs → compose-BPJ9bUKp.mjs} +5 -5
  42. package/dist/{compose-BxIsChn5.mjs.map → compose-BPJ9bUKp.mjs.map} +1 -1
  43. package/dist/{compose-76ho0pUg.cjs → compose-CGyxYe-h.cjs} +5 -5
  44. package/dist/{compose-76ho0pUg.cjs.map → compose-CGyxYe-h.cjs.map} +1 -1
  45. package/dist/{config-L_6NivCp.d.mts → config-BBFiH4qk.d.mts} +2 -2
  46. package/dist/{config-L_6NivCp.d.mts.map → config-BBFiH4qk.d.mts.map} +1 -1
  47. package/dist/{config-Mr4rcXFC.d.cts → config-DMdzdO5u.d.cts} +2 -2
  48. package/dist/{config-Mr4rcXFC.d.cts.map → config-DMdzdO5u.d.cts.map} +1 -1
  49. package/dist/config.cjs +1 -1
  50. package/dist/config.d.cts +2 -2
  51. package/dist/config.d.mts +2 -2
  52. package/dist/config.mjs +1 -1
  53. package/dist/deploy/public.cjs +6 -6
  54. package/dist/deploy/public.d.cts +1 -1
  55. package/dist/deploy/public.d.cts.map +1 -1
  56. package/dist/deploy/public.d.mts +1 -1
  57. package/dist/deploy/public.d.mts.map +1 -1
  58. package/dist/deploy/public.mjs +6 -6
  59. package/dist/{deploy-BRUx9hiA.mjs → deploy-0XpZDYAr.mjs} +5 -5
  60. package/dist/{deploy-BRUx9hiA.mjs.map → deploy-0XpZDYAr.mjs.map} +1 -1
  61. package/dist/{deploy-DuHRWNeJ.cjs → deploy-BlZx6J5x.cjs} +5 -5
  62. package/dist/{deploy-DuHRWNeJ.cjs.map → deploy-BlZx6J5x.cjs.map} +1 -1
  63. package/dist/{dokploy-BTO37Co8.mjs → dokploy-Crh4uDmP.mjs} +2 -2
  64. package/dist/{dokploy-BTO37Co8.mjs.map → dokploy-Crh4uDmP.mjs.map} +1 -1
  65. package/dist/{dokploy-Q4iYDmls.cjs → dokploy-rjXmkuwH.cjs} +2 -2
  66. package/dist/{dokploy-Q4iYDmls.cjs.map → dokploy-rjXmkuwH.cjs.map} +1 -1
  67. package/dist/{drivers-Ch1rJitn.mjs → drivers-B4RP2B2O.mjs} +2 -2
  68. package/dist/{drivers-Ch1rJitn.mjs.map → drivers-B4RP2B2O.mjs.map} +1 -1
  69. package/dist/{drivers-BdvtCKc7.cjs → drivers-CJx76mcb.cjs} +2 -2
  70. package/dist/{drivers-BdvtCKc7.cjs.map → drivers-CJx76mcb.cjs.map} +1 -1
  71. package/dist/{encryption-BESGvjHD.mjs → encryption-0eo9aY_H.mjs} +3 -3
  72. package/dist/{encryption-BESGvjHD.mjs.map → encryption-0eo9aY_H.mjs.map} +1 -1
  73. package/dist/{encryption-9YA5ThNl.cjs → encryption-CxTfRx4h.cjs} +3 -3
  74. package/dist/{encryption-9YA5ThNl.cjs.map → encryption-CxTfRx4h.cjs.map} +1 -1
  75. package/dist/{engine-U0xzB5i4.cjs → engine-CMzSebbs.cjs} +4 -4
  76. package/dist/{engine-U0xzB5i4.cjs.map → engine-CMzSebbs.cjs.map} +1 -1
  77. package/dist/{engine-CHij99aX.mjs → engine-DxWswfXe.mjs} +4 -4
  78. package/dist/{engine-CHij99aX.mjs.map → engine-DxWswfXe.mjs.map} +1 -1
  79. package/dist/{github-BM-Y9M2G.cjs → github-Dukt5cJe.cjs} +3 -3
  80. package/dist/{github-BM-Y9M2G.cjs.map → github-Dukt5cJe.cjs.map} +1 -1
  81. package/dist/{github-2OSRBf9G.mjs → github-HOfOpFI_.mjs} +3 -3
  82. package/dist/{github-2OSRBf9G.mjs.map → github-HOfOpFI_.mjs.map} +1 -1
  83. package/dist/{index-DTEPwLml.d.cts → index-DFrbhRwg.d.cts} +6 -2
  84. package/dist/{index-C0UmP_lg.d.mts.map → index-DFrbhRwg.d.cts.map} +1 -1
  85. package/dist/{index-C0UmP_lg.d.mts → index-Wuqpf316.d.mts} +6 -2
  86. package/dist/{index-DTEPwLml.d.cts.map → index-Wuqpf316.d.mts.map} +1 -1
  87. package/dist/index.cjs +437 -117
  88. package/dist/index.cjs.map +1 -1
  89. package/dist/index.mjs +437 -117
  90. package/dist/index.mjs.map +1 -1
  91. package/dist/{migrate-KwZsJFce.mjs → migrate-1dGEzuJ-.mjs} +3 -3
  92. package/dist/{migrate-KwZsJFce.mjs.map → migrate-1dGEzuJ-.mjs.map} +1 -1
  93. package/dist/{migrate-C440QQvZ.cjs → migrate-Bemd4tkz.cjs} +3 -3
  94. package/dist/{migrate-C440QQvZ.cjs.map → migrate-Bemd4tkz.cjs.map} +1 -1
  95. package/dist/{migrate-BlYiMA23.cjs → migrate-BgPWVnAZ.cjs} +3 -3
  96. package/dist/{migrate-BlYiMA23.cjs.map → migrate-BgPWVnAZ.cjs.map} +1 -1
  97. package/dist/{migrate-3oLefufw.mjs → migrate-C14NNbFx.mjs} +3 -3
  98. package/dist/{migrate-3oLefufw.mjs.map → migrate-C14NNbFx.mjs.map} +1 -1
  99. package/dist/openapi.cjs +17 -12
  100. package/dist/openapi.cjs.map +1 -1
  101. package/dist/openapi.d.cts +28 -0
  102. package/dist/openapi.d.cts.map +1 -1
  103. package/dist/openapi.d.mts +28 -0
  104. package/dist/openapi.d.mts.map +1 -1
  105. package/dist/openapi.mjs +17 -13
  106. package/dist/openapi.mjs.map +1 -1
  107. package/dist/{ready-BB9xS4S9.cjs → ready-4kx2pAhm.cjs} +5 -5
  108. package/dist/{ready-BB9xS4S9.cjs.map → ready-4kx2pAhm.cjs.map} +1 -1
  109. package/dist/{ready-ZZoNskrr.mjs → ready-DhZbZtFw.mjs} +5 -5
  110. package/dist/{ready-ZZoNskrr.mjs.map → ready-DhZbZtFw.mjs.map} +1 -1
  111. package/dist/{resolve-CBrJTyDO.cjs → resolve-DCTcD823.cjs} +3 -3
  112. package/dist/{resolve-CBrJTyDO.cjs.map → resolve-DCTcD823.cjs.map} +1 -1
  113. package/dist/{resolve-BPIDcuQ3.mjs → resolve-y1PkSZrF.mjs} +3 -3
  114. package/dist/{resolve-BPIDcuQ3.mjs.map → resolve-y1PkSZrF.mjs.map} +1 -1
  115. package/dist/{rollback-P09wRaW4.cjs → rollback-BVQFDjxr.cjs} +5 -5
  116. package/dist/{rollback-P09wRaW4.cjs.map → rollback-BVQFDjxr.cjs.map} +1 -1
  117. package/dist/{rollback-B7uQKHEk.mjs → rollback-CwVhG_xa.mjs} +5 -5
  118. package/dist/{rollback-B7uQKHEk.mjs.map → rollback-CwVhG_xa.mjs.map} +1 -1
  119. package/dist/{seeds-B4PiO_48.mjs → seeds-BoKQ74Od.mjs} +15 -6
  120. package/dist/seeds-BoKQ74Od.mjs.map +1 -0
  121. package/dist/{seeds-BjHiy9tW.cjs → seeds-Ccju3zGl.cjs} +15 -6
  122. package/dist/{seeds-BjHiy9tW.cjs.map → seeds-Ccju3zGl.cjs.map} +1 -1
  123. package/dist/{sniffer-ByWtH28i.mjs → sniffer-6oYI-MpT.mjs} +6 -6
  124. package/dist/{sniffer-ByWtH28i.mjs.map → sniffer-6oYI-MpT.mjs.map} +1 -1
  125. package/dist/{sniffer-BjG-1mlD.cjs → sniffer-DeyYI9Iv.cjs} +6 -6
  126. package/dist/{sniffer-BjG-1mlD.cjs.map → sniffer-DeyYI9Iv.cjs.map} +1 -1
  127. package/dist/stages-CHF9RFog.mjs +174 -0
  128. package/dist/stages-CHF9RFog.mjs.map +1 -0
  129. package/dist/stages-CwrYVX9u.cjs +174 -0
  130. package/dist/stages-CwrYVX9u.cjs.map +1 -0
  131. package/dist/target/public.cjs +1 -1
  132. package/dist/target/public.mjs +1 -1
  133. package/dist/vitest.cjs +1 -1
  134. package/dist/vitest.mjs +1 -1
  135. package/dist/workspace/index.cjs +2 -1
  136. package/dist/workspace/index.d.cts +2 -2
  137. package/dist/workspace/index.d.mts +2 -2
  138. package/dist/workspace/index.mjs +2 -2
  139. package/dist/{workspace-Bp8FEkF-.cjs → workspace-4i6Jwsoa.cjs} +22 -2
  140. package/dist/{workspace-Bp8FEkF-.cjs.map → workspace-4i6Jwsoa.cjs.map} +1 -1
  141. package/dist/{workspace-BBdqmkch.mjs → workspace-DD9mtPy8.mjs} +11 -3
  142. package/dist/{workspace-BBdqmkch.mjs.map → workspace-DD9mtPy8.mjs.map} +1 -1
  143. package/package.json +15 -15
  144. package/dist/EndpointGenerator-CP6sBvQV.cjs.map +0 -1
  145. package/dist/EndpointGenerator-DftalLYg.mjs.map +0 -1
  146. package/dist/OpenApiTsGenerator-CKBCckKz.cjs.map +0 -1
  147. package/dist/OpenApiTsGenerator-nu1r1NgB.mjs.map +0 -1
  148. package/dist/seeds-B4PiO_48.mjs.map +0 -1
@@ -1,3 +1,4 @@
1
+ const require_EndpointGenerator = require("./EndpointGenerator-cKlnllIu.cjs");
1
2
  let _geekmidas_schema_conversion = require("@geekmidas/schema/conversion");
2
3
  //#region src/generators/OpenApiTsGenerator.ts
3
4
  /**
@@ -10,7 +11,8 @@ let _geekmidas_schema_conversion = require("@geekmidas/schema/conversion");
10
11
  */
11
12
  var OpenApiTsGenerator = class {
12
13
  async generate(endpoints, options = {}) {
13
- const { title = "API", version = "1.0.0", description } = options;
14
+ const { title = "API", version = "1.0.0", description, telemetry = false } = options;
15
+ const telemetryDefault = require_EndpointGenerator.printTelemetryDefault(telemetry);
14
16
  const endpointInfos = await this.extractEndpointInfos(endpoints);
15
17
  const securitySchemes = this.collectSecuritySchemes(endpointInfos);
16
18
  const endpointAuth = this.buildEndpointAuthMap(endpointInfos);
@@ -23,7 +25,8 @@ var OpenApiTsGenerator = class {
23
25
  securitySchemes,
24
26
  endpointAuth,
25
27
  schemaInterfaces,
26
- pathsInterface
28
+ pathsInterface,
29
+ telemetryDefault
27
30
  });
28
31
  }
29
32
  async extractEndpointInfos(endpoints) {
@@ -309,7 +312,19 @@ var OpenApiTsGenerator = class {
309
312
  return `{\n ${parts.join(";\n ")};\n }`;
310
313
  }
311
314
  buildModule(params) {
312
- const { title, version, description, securitySchemes, endpointAuth, schemaInterfaces, pathsInterface } = params;
315
+ const { title, version, description, securitySchemes, endpointAuth, schemaInterfaces, pathsInterface, telemetryDefault } = params;
316
+ const telemetrySection = `
317
+ // ============================================================
318
+ // Trace Propagation
319
+ // ============================================================
320
+
321
+ /**
322
+ * Whether \`createApi\` sends W3C trace context (\`traceparent\`,
323
+ * \`tracestate\`) to this API when its caller does not say. Set by gkm from
324
+ * the site's edge to a \`Telemetry\` construct; \`false\` sends none.
325
+ */
326
+ export const telemetryDefault: boolean | ClientTelemetryOptions = ${telemetryDefault};
327
+ `;
313
328
  const securitySchemesObj = securitySchemes.reduce((acc, s) => {
314
329
  acc[s.name] = s.scheme;
315
330
  return acc;
@@ -325,8 +340,9 @@ import {
325
340
  type AuthStrategy,
326
341
  } from '@geekmidas/client/auth-fetcher';
327
342
  import { createEndpointHooks } from '@geekmidas/client/endpoint-hooks';
343
+ import type { ClientTelemetryOptions } from '@geekmidas/client/telemetry';
328
344
  import type { QueryClient } from '@tanstack/react-query';
329
-
345
+ ${telemetrySection}
330
346
  /**
331
347
  * Options for creating the API client.
332
348
  */
@@ -341,6 +357,12 @@ export interface CreateApiOptions {
341
357
  onRequest?: (config: RequestInit) => RequestInit | Promise<RequestInit>;
342
358
  /** The \`fetch\` requests go out on — a test browser's, for one */
343
359
  fetch?: typeof fetch;
360
+ /**
361
+ * Trace context on every request to \`baseURL\` (never another origin):
362
+ * the active OpenTelemetry span's when there is one, otherwise a trace per
363
+ * page view sampled at \`sampleRate\`. Defaults to \`telemetryDefault\`.
364
+ */
365
+ telemetry?: boolean | ClientTelemetryOptions;
344
366
  }
345
367
 
346
368
  /**
@@ -371,6 +393,7 @@ export function createApi(options: CreateApiOptions) {
371
393
  authStrategies: options.authStrategies,
372
394
  onRequest: options.onRequest,
373
395
  ...(options.fetch ? { fetch: options.fetch } : {}),
396
+ telemetry: options.telemetry ?? telemetryDefault,
374
397
  });
375
398
 
376
399
  const hooks = createEndpointHooks<paths>(fetcher, { queryClient: options.queryClient });
@@ -384,8 +407,9 @@ export function createApi(options: CreateApiOptions) {
384
407
 
385
408
  import { createTypedFetcher, type FetcherOptions } from '@geekmidas/client/fetcher';
386
409
  import { createEndpointHooks } from '@geekmidas/client/endpoint-hooks';
410
+ import type { ClientTelemetryOptions } from '@geekmidas/client/telemetry';
387
411
  import type { QueryClient } from '@tanstack/react-query';
388
-
412
+ ${telemetrySection}
389
413
  /**
390
414
  * Options for creating the API client.
391
415
  */
@@ -414,7 +438,10 @@ export interface CreateApiOptions extends Omit<FetcherOptions, 'baseURL'> {
414
438
  */
415
439
  export function createApi(options: CreateApiOptions) {
416
440
  const { queryClient, ...fetcherOptions } = options;
417
- const request = createTypedFetcher<paths>(fetcherOptions);
441
+ const request = createTypedFetcher<paths>({
442
+ ...fetcherOptions,
443
+ telemetry: options.telemetry ?? telemetryDefault,
444
+ });
418
445
 
419
446
  const hooks = createEndpointHooks<paths>(request, { queryClient });
420
447
 
@@ -511,4 +538,4 @@ Object.defineProperty(exports, "OpenApiTsGenerator", {
511
538
  }
512
539
  });
513
540
 
514
- //# sourceMappingURL=OpenApiTsGenerator-CKBCckKz.cjs.map
541
+ //# sourceMappingURL=OpenApiTsGenerator-CdUM6Hvv.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OpenApiTsGenerator-CdUM6Hvv.cjs","names":["printTelemetryDefault","getRegisteredZodJsonSchemas","getSchemaMetadata","StandardSchemaJsonSchema"],"sources":["../src/generators/OpenApiTsGenerator.ts"],"sourcesContent":["import type { Endpoint } from '@geekmidas/constructs/endpoints';\nimport {\n\tgetRegisteredZodJsonSchemas,\n\tgetSchemaMetadata,\n\tStandardSchemaJsonSchema,\n} from '@geekmidas/schema/conversion';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport {\n\ttype ClientTelemetryDefault,\n\tprintTelemetryDefault,\n} from './clientTelemetry.js';\n\nexport type { ClientTelemetryDefault } from './clientTelemetry.js';\n\nexport interface OpenApiTsOptions {\n\ttitle?: string;\n\tversion?: string;\n\tdescription?: string;\n\t/**\n\t * Trace propagation in the generated `createApi`, off by default. The\n\t * site's edge to a `Telemetry` construct is what turns it on.\n\t */\n\ttelemetry?: ClientTelemetryDefault;\n}\n\n// JSON Schema type definition\ninterface JsonSchema {\n\t// An array since Zod 4.6, which writes a union of primitives — a nullable\n\t// string among them — as `type: ['string', 'null']` rather than `anyOf`.\n\ttype?: string | string[];\n\t/** A literal — `z.literal('open')` is `{ type: 'string', const: 'open' }`. */\n\tconst?: unknown;\n\tproperties?: Record<string, JsonSchema>;\n\titems?: JsonSchema;\n\trequired?: string[];\n\tenum?: string[];\n\t$ref?: string;\n\tanyOf?: JsonSchema[];\n\toneOf?: JsonSchema[];\n\tallOf?: JsonSchema[];\n\tadditionalProperties?: boolean | JsonSchema;\n\t[key: string]: unknown;\n}\n\n// Security scheme type (OpenAPI 3.1)\ninterface SecuritySchemeObject {\n\ttype: 'apiKey' | 'http' | 'mutualTLS' | 'oauth2' | 'openIdConnect';\n\tdescription?: string;\n\tname?: string;\n\tin?: 'query' | 'header' | 'cookie';\n\tscheme?: string;\n\tbearerFormat?: string;\n\tflows?: Record<string, unknown>;\n\topenIdConnectUrl?: string;\n\t[key: string]: unknown;\n}\n\ninterface EndpointInfo {\n\tendpoint: string;\n\troute: string;\n\tmethod: string;\n\tauthorizerName: string | null;\n\t/** @deprecated Use securityScheme instead */\n\tauthorizerType: string | null;\n\t/** The OpenAPI security scheme definition for this endpoint's authorizer */\n\tsecurityScheme: SecuritySchemeObject | null;\n\tinput?: {\n\t\tbody?: StandardSchemaV1;\n\t\tquery?: StandardSchemaV1;\n\t\tparams?: StandardSchemaV1;\n\t};\n\toutput?: StandardSchemaV1;\n\tdescription?: string;\n\ttags?: string[];\n\toperationId?: string;\n\tresponseType: string;\n}\n\ninterface SecuritySchemeInfo {\n\tname: string;\n\ttype: string;\n\tscheme: SecuritySchemeObject;\n}\n\n/**\n * Generates TypeScript OpenAPI module from endpoints.\n * Outputs:\n * - securitySchemes: typed security scheme definitions\n * - endpointAuth: runtime map of endpoints to auth requirements\n * - paths: TypeScript interface for type-safe fetcher\n * - schema interfaces: reusable TypeScript types from Zod/Valibot schemas\n */\nexport class OpenApiTsGenerator {\n\tasync generate(\n\t\tendpoints: Endpoint<any, any, any, any, any, any>[],\n\t\toptions: OpenApiTsOptions = {},\n\t): Promise<string> {\n\t\tconst {\n\t\t\ttitle = 'API',\n\t\t\tversion = '1.0.0',\n\t\t\tdescription,\n\t\t\ttelemetry = false,\n\t\t} = options;\n\t\tconst telemetryDefault = printTelemetryDefault(telemetry);\n\n\t\t// Extract endpoint info\n\t\tconst endpointInfos = await this.extractEndpointInfos(endpoints);\n\n\t\t// Collect unique security schemes\n\t\tconst securitySchemes = this.collectSecuritySchemes(endpointInfos);\n\n\t\t// Build endpoint auth map\n\t\tconst endpointAuth = this.buildEndpointAuthMap(endpointInfos);\n\n\t\t// Generate schema interfaces\n\t\tconst schemaInterfaces = await this.generateSchemaInterfaces(endpointInfos);\n\n\t\t// Generate paths interface\n\t\tconst pathsInterface = await this.generatePathsInterface(endpointInfos);\n\n\t\t// Build the final TypeScript module\n\t\treturn this.buildModule({\n\t\t\ttitle,\n\t\t\tversion,\n\t\t\tdescription,\n\t\t\tsecuritySchemes,\n\t\t\tendpointAuth,\n\t\t\tschemaInterfaces,\n\t\t\tpathsInterface,\n\t\t\ttelemetryDefault,\n\t\t});\n\t}\n\n\tprivate async extractEndpointInfos(\n\t\tendpoints: Endpoint<any, any, any, any, any, any>[],\n\t): Promise<EndpointInfo[]> {\n\t\treturn endpoints.map((ep) => {\n\t\t\tconst route = ep.route.replace(/:(\\w+)/g, '{$1}');\n\t\t\tconst method = ep.method.toUpperCase();\n\n\t\t\t// Get security scheme from authorizer (if available)\n\t\t\t// This is the preferred way - the scheme is stored directly on the authorizer\n\t\t\tconst securityScheme = ep.authorizer?.securityScheme as\n\t\t\t\t| SecuritySchemeObject\n\t\t\t\t| undefined;\n\n\t\t\treturn {\n\t\t\t\tendpoint: `${method} ${route}`,\n\t\t\t\troute,\n\t\t\t\tmethod,\n\t\t\t\tauthorizerName: ep.authorizer?.name ?? null,\n\t\t\t\tauthorizerType: ep.authorizer?.type ?? null,\n\t\t\t\tsecurityScheme: securityScheme ?? null,\n\t\t\t\tinput: ep.input,\n\t\t\t\toutput: ep.outputSchema,\n\t\t\t\tdescription: ep.description,\n\t\t\t\ttags: ep.tags,\n\t\t\t\toperationId: ep.operationId,\n\t\t\t\tresponseType: ep.responseType ?? 'application/json',\n\t\t\t};\n\t\t});\n\t}\n\n\tprivate collectSecuritySchemes(\n\t\tendpointInfos: EndpointInfo[],\n\t): SecuritySchemeInfo[] {\n\t\tconst schemes = new Map<string, SecuritySchemeInfo>();\n\n\t\tfor (const info of endpointInfos) {\n\t\t\tif (info.authorizerName && !schemes.has(info.authorizerName)) {\n\t\t\t\t// Prefer the stored security scheme (from .securitySchemes() or built-ins)\n\t\t\t\t// Fall back to inference from authorizerType for backward compatibility\n\t\t\t\tconst scheme =\n\t\t\t\t\tinfo.securityScheme ??\n\t\t\t\t\t(info.authorizerType\n\t\t\t\t\t\t? this.mapAuthorizerToSecurityScheme(\n\t\t\t\t\t\t\t\tinfo.authorizerType,\n\t\t\t\t\t\t\t\tinfo.authorizerName,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t: null);\n\n\t\t\t\tif (scheme) {\n\t\t\t\t\tschemes.set(info.authorizerName, {\n\t\t\t\t\t\tname: info.authorizerName,\n\t\t\t\t\t\ttype: scheme.type,\n\t\t\t\t\t\tscheme,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn Array.from(schemes.values());\n\t}\n\n\tprivate mapAuthorizerToSecurityScheme(\n\t\ttype: string,\n\t\t_name: string,\n\t): SecuritySchemeObject {\n\t\tswitch (type.toLowerCase()) {\n\t\t\tcase 'jwt':\n\t\t\tcase 'bearer':\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'http',\n\t\t\t\t\tscheme: 'bearer',\n\t\t\t\t\tbearerFormat: 'JWT',\n\t\t\t\t};\n\t\t\tcase 'iam':\n\t\t\tcase 'aws-sigv4':\n\t\t\tcase 'sigv4':\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'apiKey',\n\t\t\t\t\tin: 'header',\n\t\t\t\t\tname: 'Authorization',\n\t\t\t\t\t'x-amazon-apigateway-authtype': 'awsSigv4',\n\t\t\t\t};\n\t\t\tcase 'apikey':\n\t\t\tcase 'api-key':\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'apiKey',\n\t\t\t\t\tin: 'header',\n\t\t\t\t\tname: 'X-API-Key',\n\t\t\t\t};\n\t\t\tcase 'oauth2':\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'oauth2',\n\t\t\t\t\tflows: {},\n\t\t\t\t};\n\t\t\tcase 'oidc':\n\t\t\tcase 'openidconnect':\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'openIdConnect',\n\t\t\t\t\topenIdConnectUrl: '',\n\t\t\t\t};\n\t\t\tdefault:\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'http',\n\t\t\t\t\tscheme: 'bearer',\n\t\t\t\t};\n\t\t}\n\t}\n\n\tprivate buildEndpointAuthMap(\n\t\tendpointInfos: EndpointInfo[],\n\t): Record<string, string | null> {\n\t\tconst authMap: Record<string, string | null> = {};\n\n\t\tfor (const info of endpointInfos) {\n\t\t\tauthMap[info.endpoint] = info.authorizerName;\n\t\t}\n\n\t\treturn authMap;\n\t}\n\n\tprivate async generateSchemaInterfaces(\n\t\tendpointInfos: EndpointInfo[],\n\t): Promise<string> {\n\t\tconst interfaces: string[] = [];\n\t\tconst generatedNames = new Set<string>();\n\t\t// Collect nested schemas with $defs (from .meta({ id: 'X' }))\n\t\tconst collectedDefs = new Map<string, JsonSchema>();\n\n\t\tfor (const info of endpointInfos) {\n\t\t\tconst baseName = this.getSchemaBaseName(info);\n\n\t\t\t// Input body schema\n\t\t\tif (info.input?.body) {\n\t\t\t\tconst name = await this.getSchemaName(\n\t\t\t\t\tinfo.input.body,\n\t\t\t\t\t`${baseName}Input`,\n\t\t\t\t);\n\t\t\t\tif (!generatedNames.has(name)) {\n\t\t\t\t\tconst schema = await this.schemaToInterfaceWithDefs(\n\t\t\t\t\t\tinfo.input.body,\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tcollectedDefs,\n\t\t\t\t\t);\n\t\t\t\t\tif (schema) {\n\t\t\t\t\t\tinterfaces.push(schema);\n\t\t\t\t\t\tgeneratedNames.add(name);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Input params schema\n\t\t\tif (info.input?.params) {\n\t\t\t\tconst name = await this.getSchemaName(\n\t\t\t\t\tinfo.input.params,\n\t\t\t\t\t`${baseName}Params`,\n\t\t\t\t);\n\t\t\t\tif (!generatedNames.has(name)) {\n\t\t\t\t\tconst schema = await this.schemaToInterfaceWithDefs(\n\t\t\t\t\t\tinfo.input.params,\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tcollectedDefs,\n\t\t\t\t\t);\n\t\t\t\t\tif (schema) {\n\t\t\t\t\t\tinterfaces.push(schema);\n\t\t\t\t\t\tgeneratedNames.add(name);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Input query schema\n\t\t\tif (info.input?.query) {\n\t\t\t\tconst name = await this.getSchemaName(\n\t\t\t\t\tinfo.input.query,\n\t\t\t\t\t`${baseName}Query`,\n\t\t\t\t);\n\t\t\t\tif (!generatedNames.has(name)) {\n\t\t\t\t\tconst schema = await this.schemaToInterfaceWithDefs(\n\t\t\t\t\t\tinfo.input.query,\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tcollectedDefs,\n\t\t\t\t\t);\n\t\t\t\t\tif (schema) {\n\t\t\t\t\t\tinterfaces.push(schema);\n\t\t\t\t\t\tgeneratedNames.add(name);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Output schema\n\t\t\tif (info.output) {\n\t\t\t\tconst name = await this.getSchemaName(info.output, `${baseName}Output`);\n\t\t\t\tif (!generatedNames.has(name)) {\n\t\t\t\t\tconst schema = await this.schemaToInterfaceWithDefs(\n\t\t\t\t\t\tinfo.output,\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tcollectedDefs,\n\t\t\t\t\t);\n\t\t\t\t\tif (schema) {\n\t\t\t\t\t\tinterfaces.push(schema);\n\t\t\t\t\t\tgeneratedNames.add(name);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Pull in every schema registered via `.meta({ id })` in zod's global\n\t\t// registry, even when no endpoint references it directly. This makes\n\t\t// derived schemas like `UserSchema.pick(...)` discoverable through the\n\t\t// named parent type without requiring the parent to be wired into an\n\t\t// endpoint.\n\t\tconst registrySchemas = await getRegisteredZodJsonSchemas();\n\t\tfor (const [rawName, rawSchema] of Object.entries(registrySchemas)) {\n\t\t\tconst defName = this.pascalCase(rawName);\n\t\t\tif (collectedDefs.has(defName) || generatedNames.has(defName)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst {\n\t\t\t\tid: _id,\n\t\t\t\t$defs: _defs,\n\t\t\t\t...rest\n\t\t\t} = rawSchema as JsonSchema & {\n\t\t\t\tid?: string;\n\t\t\t};\n\t\t\tcollectedDefs.set(defName, rest as JsonSchema);\n\t\t}\n\n\t\t// Generate interfaces for collected $defs (nested schemas with .meta({ id: 'X' }))\n\t\tfor (const [defName, defSchema] of collectedDefs) {\n\t\t\tif (!generatedNames.has(defName)) {\n\t\t\t\tconst interfaceStr = this.jsonSchemaToInterface(defSchema, defName);\n\t\t\t\tinterfaces.push(interfaceStr);\n\t\t\t\tgeneratedNames.add(defName);\n\t\t\t}\n\t\t}\n\n\t\treturn interfaces.join('\\n\\n');\n\t}\n\n\t/**\n\t * Get the name for a schema, using metadata `id` if available,\n\t * otherwise falling back to the provided default name.\n\t */\n\tprivate async getSchemaName(\n\t\tschema: StandardSchemaV1,\n\t\tdefaultName: string,\n\t): Promise<string> {\n\t\ttry {\n\t\t\tconst metadata = await getSchemaMetadata(schema);\n\t\t\tif (metadata?.id) {\n\t\t\t\treturn this.pascalCase(metadata.id);\n\t\t\t}\n\t\t} catch {\n\t\t\t// Ignore metadata extraction errors\n\t\t}\n\t\treturn defaultName;\n\t}\n\n\tprivate getSchemaBaseName(info: EndpointInfo): string {\n\t\tif (info.operationId) {\n\t\t\treturn this.pascalCase(info.operationId);\n\t\t}\n\n\t\t// Generate name from method + route\n\t\tconst routeParts = info.route\n\t\t\t.replace(/[{}]/g, '')\n\t\t\t.split('/')\n\t\t\t.filter(Boolean)\n\t\t\t.map((part) => this.pascalCase(part));\n\n\t\treturn `${this.pascalCase(info.method.toLowerCase())}${routeParts.join('')}`;\n\t}\n\n\tprivate pascalCase(str: string): string {\n\t\treturn str\n\t\t\t.replace(/[-_](.)/g, (_, c) => c.toUpperCase())\n\t\t\t.replace(/^./, (c) => c.toUpperCase());\n\t}\n\n\t/**\n\t * Convert schema to interface while collecting $defs for nested schemas\n\t * with .meta({ id: 'X' }).\n\t */\n\tprivate async schemaToInterfaceWithDefs(\n\t\tschema: StandardSchemaV1,\n\t\tname: string,\n\t\tcollectedDefs: Map<string, JsonSchema>,\n\t): Promise<string | null> {\n\t\ttry {\n\t\t\t// Get raw JSON schema with $defs intact (don't use convertStandardSchemaToJsonSchema\n\t\t\t// which strips $defs)\n\t\t\tconst vendor = schema['~standard']?.vendor;\n\t\t\tif (!vendor || !(vendor in StandardSchemaJsonSchema)) {\n\t\t\t\treturn unknownType(name);\n\t\t\t}\n\n\t\t\tconst toJsonSchema =\n\t\t\t\tStandardSchemaJsonSchema[\n\t\t\t\t\tvendor as keyof typeof StandardSchemaJsonSchema\n\t\t\t\t];\n\t\t\tconst jsonSchema = await toJsonSchema(schema);\n\t\t\tif (!jsonSchema) return unknownType(name);\n\n\t\t\t// Extract $defs from the JSON schema (these come from .meta({ id: 'X' }))\n\t\t\tif (jsonSchema.$defs && typeof jsonSchema.$defs === 'object') {\n\t\t\t\tfor (const [defName, defSchema] of Object.entries(jsonSchema.$defs)) {\n\t\t\t\t\tif (!collectedDefs.has(defName)) {\n\t\t\t\t\t\t// Remove the 'id' field from the schema as it's just metadata\n\t\t\t\t\t\tconst { id, ...schemaWithoutId } = defSchema as JsonSchema & {\n\t\t\t\t\t\t\tid?: string;\n\t\t\t\t\t\t};\n\t\t\t\t\t\tcollectedDefs.set(defName, schemaWithoutId as JsonSchema);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Remove $defs from the schema before converting to interface\n\t\t\tconst { $defs, ...schemaWithoutDefs } = jsonSchema;\n\n\t\t\t// A schema that *is* a registered schema converts to nothing but a\n\t\t\t// `$ref` at it, and the def it points at has already been collected\n\t\t\t// above — so emitting a declaration here as well produces\n\t\t\t// `export type User = User`, a circular alias that is not a type.\n\t\t\t//\n\t\t\t// Zod used to inline this case and only started emitting the `$ref` in\n\t\t\t// 4.6, which is how the bug arrived without the generator changing.\n\t\t\tconst ref = (schemaWithoutDefs as { $ref?: string }).$ref;\n\t\t\tif (\n\t\t\t\tref &&\n\t\t\t\tObject.keys(schemaWithoutDefs).every(\n\t\t\t\t\t(k) => k === '$ref' || k === '$schema',\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tconst target = ref.split('/').pop();\n\t\t\t\t// The def's own interface is the declaration. Only alias it when the\n\t\t\t\t// names actually differ, which is when the alias says something.\n\t\t\t\treturn target === name ? null : `export type ${name} = ${target};`;\n\t\t\t}\n\n\t\t\treturn this.jsonSchemaToInterface(schemaWithoutDefs, name);\n\t\t} catch {\n\t\t\treturn unknownType(name);\n\t\t}\n\t}\n\n\tprivate jsonSchemaToInterface(schema: JsonSchema, name: string): string {\n\t\tif (schema.type !== 'object' || !schema.properties) {\n\t\t\t// For non-object types, create a type alias\n\t\t\tconst typeStr = this.jsonSchemaTypeToTs(schema);\n\t\t\treturn `export type ${name} = ${typeStr};`;\n\t\t}\n\n\t\tconst props: string[] = [];\n\t\tconst required = new Set(schema.required || []);\n\n\t\tfor (const [propName, propSchema] of Object.entries(schema.properties)) {\n\t\t\tconst isRequired = required.has(propName);\n\t\t\tconst typeStr = this.jsonSchemaTypeToTs(propSchema as JsonSchema);\n\t\t\tconst optionalMark = isRequired ? '' : '?';\n\t\t\tprops.push(` ${propName}${optionalMark}: ${typeStr};`);\n\t\t}\n\n\t\treturn `export interface ${name} {\\n${props.join('\\n')}\\n}`;\n\t}\n\n\tprivate jsonSchemaTypeToTs(schema: JsonSchema): string {\n\t\tif (!schema) return 'unknown';\n\n\t\tif (schema.$ref) {\n\t\t\t// Extract name from $ref\n\t\t\tconst refName = schema.$ref.split('/').pop() || 'unknown';\n\t\t\treturn refName;\n\t\t}\n\n\t\t// Before the type: a literal is narrower than the type it belongs to, and\n\t\t// reading only the type turned `'open' | 'closed'` into `string | string`.\n\t\tif (schema.const !== undefined) {\n\t\t\treturn typeof schema.const === 'string'\n\t\t\t\t? `'${schema.const}'`\n\t\t\t\t: String(schema.const);\n\t\t}\n\n\t\tif (schema.anyOf) {\n\t\t\treturn schema.anyOf\n\t\t\t\t.map((s: JsonSchema) => this.jsonSchemaTypeToTs(s))\n\t\t\t\t.join(' | ');\n\t\t}\n\n\t\tif (schema.oneOf) {\n\t\t\treturn schema.oneOf\n\t\t\t\t.map((s: JsonSchema) => this.jsonSchemaTypeToTs(s))\n\t\t\t\t.join(' | ');\n\t\t}\n\n\t\tif (schema.allOf) {\n\t\t\treturn schema.allOf\n\t\t\t\t.map((s: JsonSchema) => this.jsonSchemaTypeToTs(s))\n\t\t\t\t.join(' & ');\n\t\t}\n\n\t\tif (Array.isArray(schema.type)) {\n\t\t\treturn schema.type\n\t\t\t\t.map((type) => this.jsonSchemaTypeToTs({ ...schema, type }))\n\t\t\t\t.join(' | ');\n\t\t}\n\n\t\tswitch (schema.type) {\n\t\t\tcase 'string':\n\t\t\t\tif (schema.enum) {\n\t\t\t\t\treturn schema.enum.map((e: string) => `'${e}'`).join(' | ');\n\t\t\t\t}\n\t\t\t\treturn 'string';\n\t\t\tcase 'number':\n\t\t\tcase 'integer':\n\t\t\t\treturn 'number';\n\t\t\tcase 'boolean':\n\t\t\t\treturn 'boolean';\n\t\t\tcase 'null':\n\t\t\t\treturn 'null';\n\t\t\tcase 'array':\n\t\t\t\tif (schema.items) {\n\t\t\t\t\treturn `Array<${this.jsonSchemaTypeToTs(schema.items as JsonSchema)}>`;\n\t\t\t\t}\n\t\t\t\treturn 'Array<unknown>';\n\t\t\tcase 'object':\n\t\t\t\tif (schema.properties) {\n\t\t\t\t\tconst props: string[] = [];\n\t\t\t\t\tconst required = new Set(schema.required || []);\n\t\t\t\t\tfor (const [propName, propSchema] of Object.entries(\n\t\t\t\t\t\tschema.properties,\n\t\t\t\t\t)) {\n\t\t\t\t\t\tconst isRequired = required.has(propName);\n\t\t\t\t\t\tconst typeStr = this.jsonSchemaTypeToTs(propSchema as JsonSchema);\n\t\t\t\t\t\tconst optionalMark = isRequired ? '' : '?';\n\t\t\t\t\t\tprops.push(`${propName}${optionalMark}: ${typeStr}`);\n\t\t\t\t\t}\n\t\t\t\t\treturn `{ ${props.join('; ')} }`;\n\t\t\t\t}\n\t\t\t\tif (schema.additionalProperties) {\n\t\t\t\t\tconst valueType = this.jsonSchemaTypeToTs(\n\t\t\t\t\t\tschema.additionalProperties as JsonSchema,\n\t\t\t\t\t);\n\t\t\t\t\treturn `Record<string, ${valueType}>`;\n\t\t\t\t}\n\t\t\t\treturn 'Record<string, unknown>';\n\t\t\tdefault:\n\t\t\t\treturn 'unknown';\n\t\t}\n\t}\n\n\tprivate async generatePathsInterface(\n\t\tendpointInfos: EndpointInfo[],\n\t): Promise<string> {\n\t\tconst pathGroups = new Map<string, EndpointInfo[]>();\n\n\t\t// Group endpoints by route\n\t\tfor (const info of endpointInfos) {\n\t\t\tconst existing = pathGroups.get(info.route) || [];\n\t\t\texisting.push(info);\n\t\t\tpathGroups.set(info.route, existing);\n\t\t}\n\n\t\tconst pathEntries: string[] = [];\n\n\t\tfor (const [route, infos] of pathGroups) {\n\t\t\tconst methodEntries: string[] = [];\n\n\t\t\tfor (const info of infos) {\n\t\t\t\tconst methodDef = await this.generateMethodDefinition(info);\n\t\t\t\tmethodEntries.push(` ${info.method.toLowerCase()}: ${methodDef};`);\n\t\t\t}\n\n\t\t\t// Add path parameters if present\n\t\t\tconst firstWithParams = infos.find((i) => i.input?.params);\n\t\t\tlet paramsEntry = '';\n\t\t\tif (firstWithParams?.input?.params) {\n\t\t\t\tconst paramsName = await this.getSchemaName(\n\t\t\t\t\tfirstWithParams.input.params,\n\t\t\t\t\t`${this.getSchemaBaseName(firstWithParams)}Params`,\n\t\t\t\t);\n\t\t\t\tparamsEntry = `\\n parameters: {\\n path: ${paramsName};\\n };`;\n\t\t\t}\n\n\t\t\tpathEntries.push(\n\t\t\t\t` '${route}': {${paramsEntry}\\n${methodEntries.join('\\n')}\\n };`,\n\t\t\t);\n\t\t}\n\n\t\treturn `export interface paths {\\n${pathEntries.join('\\n')}\\n}`;\n\t}\n\n\tprivate async generateMethodDefinition(info: EndpointInfo): Promise<string> {\n\t\tconst parts: string[] = [];\n\t\tconst baseName = this.getSchemaBaseName(info);\n\n\t\t// Request body\n\t\tif (info.input?.body) {\n\t\t\tconst bodyName = await this.getSchemaName(\n\t\t\t\tinfo.input.body,\n\t\t\t\t`${baseName}Input`,\n\t\t\t);\n\t\t\tparts.push(`requestBody: {\n content: {\n 'application/json': ${bodyName};\n };\n }`);\n\t\t}\n\n\t\t// Query parameters\n\t\tif (info.input?.query) {\n\t\t\tconst queryName = await this.getSchemaName(\n\t\t\t\tinfo.input.query,\n\t\t\t\t`${baseName}Query`,\n\t\t\t);\n\t\t\tparts.push(`parameters: {\n query: ${queryName};\n }`);\n\t\t}\n\n\t\t// Responses\n\t\tconst outputName = info.output\n\t\t\t? await this.getSchemaName(info.output, `${baseName}Output`)\n\t\t\t: 'unknown';\n\t\tparts.push(`responses: {\n 200: {\n content: {\n '${info.responseType}': ${outputName};\n };\n };\n }`);\n\n\t\treturn `{\\n ${parts.join(';\\n ')};\\n }`;\n\t}\n\n\tprivate buildModule(params: {\n\t\ttitle: string;\n\t\tversion: string;\n\t\tdescription?: string;\n\t\tsecuritySchemes: SecuritySchemeInfo[];\n\t\tendpointAuth: Record<string, string | null>;\n\t\tschemaInterfaces: string;\n\t\tpathsInterface: string;\n\t\ttelemetryDefault: string;\n\t}): string {\n\t\tconst {\n\t\t\ttitle,\n\t\t\tversion,\n\t\t\tdescription,\n\t\t\tsecuritySchemes,\n\t\t\tendpointAuth,\n\t\t\tschemaInterfaces,\n\t\t\tpathsInterface,\n\t\t\ttelemetryDefault,\n\t\t} = params;\n\n\t\tconst telemetrySection = `\n// ============================================================\n// Trace Propagation\n// ============================================================\n\n/**\n * Whether \\`createApi\\` sends W3C trace context (\\`traceparent\\`,\n * \\`tracestate\\`) to this API when its caller does not say. Set by gkm from\n * the site's edge to a \\`Telemetry\\` construct; \\`false\\` sends none.\n */\nexport const telemetryDefault: boolean | ClientTelemetryOptions = ${telemetryDefault};\n`;\n\n\t\tconst securitySchemesObj = securitySchemes.reduce(\n\t\t\t(acc, s) => {\n\t\t\t\tacc[s.name] = s.scheme;\n\t\t\t\treturn acc;\n\t\t\t},\n\t\t\t{} as Record<string, SecuritySchemeObject>,\n\t\t);\n\n\t\tconst schemeNames = securitySchemes.map((s) => `'${s.name}'`).join(' | ');\n\n\t\t// Generate createApi only if there are security schemes\n\t\tconst hasSecuritySchemes = schemeNames.length > 0;\n\n\t\tconst createApiSection = hasSecuritySchemes\n\t\t\t? `\n// ============================================================\n// API Client Factory\n// ============================================================\n\nimport {\n createAuthAwareFetcher,\n type AuthStrategy,\n} from '@geekmidas/client/auth-fetcher';\nimport { createEndpointHooks } from '@geekmidas/client/endpoint-hooks';\nimport type { ClientTelemetryOptions } from '@geekmidas/client/telemetry';\nimport type { QueryClient } from '@tanstack/react-query';\n${telemetrySection}\n/**\n * Options for creating the API client.\n */\nexport interface CreateApiOptions {\n /** Base URL for all API requests (required) */\n baseURL: string;\n /** Auth strategies for each security scheme used in this API */\n authStrategies: Record<SecuritySchemeId, AuthStrategy>;\n /** Optional React Query client instance */\n queryClient?: QueryClient;\n /** Optional request interceptor */\n onRequest?: (config: RequestInit) => RequestInit | Promise<RequestInit>;\n /** The \\`fetch\\` requests go out on — a test browser's, for one */\n fetch?: typeof fetch;\n /**\n * Trace context on every request to \\`baseURL\\` (never another origin):\n * the active OpenTelemetry span's when there is one, otherwise a trace per\n * page view sampled at \\`sampleRate\\`. Defaults to \\`telemetryDefault\\`.\n */\n telemetry?: boolean | ClientTelemetryOptions;\n}\n\n/**\n * Create a type-safe API client with authentication and React Query hooks.\n *\n * @example\n * \\`\\`\\`typescript\n * const api = createApi({\n * baseURL: 'https://api.example.com',\n * authStrategies: {\n * jwt: { type: 'bearer', tokenProvider },\n * },\n * });\n *\n * // Imperative fetch\n * const user = await api('GET /users/{id}', { params: { id: '123' } });\n *\n * // React Query hooks\n * const { data } = api.useQuery('GET /users/{id}', { params: { id: '123' } });\n * const mutation = api.useMutation('POST /users');\n * \\`\\`\\`\n */\nexport function createApi(options: CreateApiOptions) {\n const fetcher = createAuthAwareFetcher<paths, typeof endpointAuth, typeof securitySchemes>({\n baseURL: options.baseURL,\n endpointAuth,\n securitySchemes,\n authStrategies: options.authStrategies,\n onRequest: options.onRequest,\n ...(options.fetch ? { fetch: options.fetch } : {}),\n telemetry: options.telemetry ?? telemetryDefault,\n });\n\n const hooks = createEndpointHooks<paths>(fetcher, { queryClient: options.queryClient });\n\n return Object.assign(fetcher, hooks);\n}\n`\n\t\t\t: `\n// ============================================================\n// API Client Factory\n// ============================================================\n\nimport { createTypedFetcher, type FetcherOptions } from '@geekmidas/client/fetcher';\nimport { createEndpointHooks } from '@geekmidas/client/endpoint-hooks';\nimport type { ClientTelemetryOptions } from '@geekmidas/client/telemetry';\nimport type { QueryClient } from '@tanstack/react-query';\n${telemetrySection}\n/**\n * Options for creating the API client.\n */\nexport interface CreateApiOptions extends Omit<FetcherOptions, 'baseURL'> {\n /** Base URL for all API requests (required) */\n baseURL: string;\n /** Optional React Query client instance */\n queryClient?: QueryClient;\n}\n\n/**\n * Create a type-safe API client with React Query hooks.\n *\n * @example\n * \\`\\`\\`typescript\n * const api = createApi({\n * baseURL: 'https://api.example.com',\n * });\n *\n * // Imperative fetch\n * const data = await api('GET /health');\n *\n * // React Query hooks\n * const { data } = api.useQuery('GET /health');\n * \\`\\`\\`\n */\nexport function createApi(options: CreateApiOptions) {\n const { queryClient, ...fetcherOptions } = options;\n const request = createTypedFetcher<paths>({\n ...fetcherOptions,\n telemetry: options.telemetry ?? telemetryDefault,\n });\n\n const hooks = createEndpointHooks<paths>(request, { queryClient });\n\n return Object.assign(request, hooks);\n}\n`;\n\n\t\treturn `// Auto-generated by @geekmidas/cli - DO NOT EDIT\n\n// ============================================================\n// Security Scheme Type\n// ============================================================\n\ninterface SecuritySchemeObject {\n type: 'apiKey' | 'http' | 'mutualTLS' | 'oauth2' | 'openIdConnect';\n description?: string;\n name?: string;\n in?: 'query' | 'header' | 'cookie';\n scheme?: string;\n bearerFormat?: string;\n flows?: Record<string, unknown>;\n openIdConnectUrl?: string;\n [key: string]: unknown;\n}\n\n// ============================================================\n// API Info\n// ============================================================\n\nexport const apiInfo = {\n title: '${title}',\n version: '${version}',${description ? `\\n description: '${description.replace(/'/g, \"\\\\'\")}',` : ''}\n} as const;\n\n// ============================================================\n// Security Schemes\n// ============================================================\n\n/**\n * Available security schemes for this API.\n * Maps authorizer names to OpenAPI security scheme definitions.\n */\nexport const securitySchemes = ${JSON.stringify(securitySchemesObj, null, 2).replace(/\"([a-zA-Z_$][a-zA-Z0-9_$]*)\":/g, '$1:')} as const satisfies Record<string, SecuritySchemeObject>;\n\nexport type SecuritySchemeId = ${schemeNames || 'never'};\n\n// ============================================================\n// Endpoint Authentication Map\n// ============================================================\n\n/**\n * Runtime map of endpoints to their required authentication scheme.\n * \\`null\\` indicates a public endpoint (no auth required).\n */\nexport const endpointAuth = ${JSON.stringify(endpointAuth, null, 2).replace(/\"([^\"]+)\":/g, \"'$1':\")} as const satisfies Record<string, SecuritySchemeId | null>;\n\nexport type EndpointString = keyof typeof endpointAuth;\n\nexport type AuthenticatedEndpoint = {\n [K in EndpointString]: typeof endpointAuth[K] extends null ? never : K;\n}[EndpointString];\n\nexport type PublicEndpoint = {\n [K in EndpointString]: typeof endpointAuth[K] extends null ? K : never;\n}[EndpointString];\n\n// ============================================================\n// Schema Definitions\n// ============================================================\n\n${schemaInterfaces}\n\n// ============================================================\n// OpenAPI Paths\n// ============================================================\n\n${pathsInterface}\n${createApiSection}\n`;\n\t}\n}\n\n/**\n * The declaration for a schema that cannot be converted — a vendor with no\n * converter, or one that fails. The paths interface names it either way, so\n * leaving it undeclared made the generated client fail to compile.\n */\nfunction unknownType(name: string): string {\n\treturn `export type ${name} = unknown;`;\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { n as __require } from "./rolldown-runtime-BhDjJH2R.mjs";
2
- import { d as StateLocked, l as DocumentStateStore, m as StateVersionConflict } from "./sniffer-ByWtH28i.mjs";
2
+ import { d as StateLocked, l as DocumentStateStore, m as StateVersionConflict } from "./sniffer-6oYI-MpT.mjs";
3
3
  import { DeleteObjectCommand, GetObjectCommand, PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
4
4
  //#region src/deploy/S3StateStore.ts
5
5
  /**
@@ -158,4 +158,4 @@ var S3StateStore = class S3StateStore extends DocumentStateStore {
158
158
  //#endregion
159
159
  export { S3StateStore };
160
160
 
161
- //# sourceMappingURL=S3StateStore-C9upSW5o.mjs.map
161
+ //# sourceMappingURL=S3StateStore-DR2f78Dj.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"S3StateStore-C9upSW5o.mjs","names":[],"sources":["../src/deploy/S3StateStore.ts"],"sourcesContent":["/**\n * Amazon S3 state store.\n *\n * - `<prefix>/<workspace>/<stage>/state.json`: the state.\n * - `<prefix>/<workspace>/<stage>/lock.json`: the lock.\n * - `<prefix>/<workspace>/<stage>/state.v1.json`: the v1 state, kept on\n * migration.\n *\n * Every write is conditional on the server: `If-None-Match: *` to create,\n * `If-Match: <etag>` to replace. S3 answers a lost race with 412 (or 409 when\n * two conditional writes are in flight at once), so there is no window\n * between a check and a write for another run to slip into.\n */\n\nimport {\n\tDeleteObjectCommand,\n\tGetObjectCommand,\n\tPutObjectCommand,\n\tS3Client,\n\ttype S3ClientConfig,\n} from '@aws-sdk/client-s3';\nimport type { AwsRegion } from './StateProvider';\nimport {\n\tDocumentStateStore,\n\ttype LockHolder,\n\ttype RawState,\n\tStateLocked,\n\ttype StateVersion,\n\tStateVersionConflict,\n} from './StateStore';\n\nexport interface S3StateStoreOptions {\n\tworkspaceName: string;\n\tbucket: string;\n\t/** Key prefix inside the bucket; `gkm` by default. */\n\tprefix?: string;\n\tregion?: AwsRegion;\n\t/** Uses the default credential chain when omitted. */\n\tprofile?: string;\n\tcredentials?: S3ClientConfig['credentials'];\n\t/** For the local AWS emulator. */\n\tendpoint?: string;\n}\n\ninterface S3Failure {\n\tname?: string;\n\t$metadata?: { httpStatusCode?: number };\n}\n\n/** A conditional write that lost: the object is not what the write assumed. */\nfunction lostRace(error: unknown): boolean {\n\tconst { name, $metadata } = (error ?? {}) as S3Failure;\n\treturn (\n\t\tname === 'PreconditionFailed' ||\n\t\tname === 'ConditionalRequestConflict' ||\n\t\t$metadata?.httpStatusCode === 412 ||\n\t\t$metadata?.httpStatusCode === 409\n\t);\n}\n\nfunction notFound(error: unknown): boolean {\n\tconst { name, $metadata } = (error ?? {}) as S3Failure;\n\treturn (\n\t\tname === 'NoSuchKey' ||\n\t\tname === 'NotFound' ||\n\t\t$metadata?.httpStatusCode === 404\n\t);\n}\n\nexport class S3StateStore extends DocumentStateStore {\n\tprivate readonly prefix: string;\n\n\tconstructor(\n\t\treadonly workspaceName: string,\n\t\treadonly bucket: string,\n\t\tprivate readonly client: S3Client,\n\t\tprefix = 'gkm',\n\t) {\n\t\tsuper();\n\t\tthis.prefix = prefix.replace(/\\/+$/, '');\n\t}\n\n\tstatic create(options: S3StateStoreOptions): S3StateStore {\n\t\tconst config: S3ClientConfig = {\n\t\t\tregion: options.region,\n\t\t\tendpoint: options.endpoint,\n\t\t\t// The emulator serves buckets by path, not by subdomain.\n\t\t\tforcePathStyle: options.endpoint ? true : undefined,\n\t\t};\n\t\tif (options.profile) {\n\t\t\t// Required lazily: only a profile needs the credential providers.\n\t\t\tconst { fromIni } = require('@aws-sdk/credential-providers');\n\t\t\tconfig.credentials = fromIni({ profile: options.profile });\n\t\t} else if (options.credentials) {\n\t\t\tconfig.credentials = options.credentials;\n\t\t}\n\t\treturn new S3StateStore(\n\t\t\toptions.workspaceName,\n\t\t\toptions.bucket,\n\t\t\tnew S3Client(config),\n\t\t\toptions.prefix,\n\t\t);\n\t}\n\n\tprivate key(stage: string, leaf: string): string {\n\t\tconst path = `${this.workspaceName}/${stage}/${leaf}`;\n\t\treturn this.prefix ? `${this.prefix}/${path}` : path;\n\t}\n\n\tprotected location(stage: string): string {\n\t\treturn `s3://${this.bucket}/${this.key(stage, 'state.json')}`;\n\t}\n\n\tprotected async readRaw(stage: string): Promise<RawState | null> {\n\t\tconst object = await this.get(this.key(stage, 'state.json'));\n\t\tif (!object) return null;\n\t\treturn { body: object.body, version: object.etag };\n\t}\n\n\tprotected async writeRaw(\n\t\tstage: string,\n\t\tbody: string,\n\t\texpectedVersion: StateVersion | null,\n\t): Promise<StateVersion> {\n\t\tconst Key = this.key(stage, 'state.json');\n\t\ttry {\n\t\t\tconst { ETag } = await this.client.send(\n\t\t\t\tnew PutObjectCommand({\n\t\t\t\t\tBucket: this.bucket,\n\t\t\t\t\tKey,\n\t\t\t\t\tBody: body,\n\t\t\t\t\tContentType: 'application/json',\n\t\t\t\t\t...(expectedVersion === null\n\t\t\t\t\t\t? { IfNoneMatch: '*' }\n\t\t\t\t\t\t: { IfMatch: expectedVersion }),\n\t\t\t\t}),\n\t\t\t);\n\t\t\tif (ETag) return ETag;\n\t\t\t// Some S3-compatible servers omit the ETag on a conditional put.\n\t\t\treturn (await this.get(Key))?.etag ?? '';\n\t\t} catch (error) {\n\t\t\tif (lostRace(error)) {\n\t\t\t\tconst current = await this.get(Key);\n\t\t\t\tthrow new StateVersionConflict(\n\t\t\t\t\tstage,\n\t\t\t\t\texpectedVersion,\n\t\t\t\t\tcurrent?.etag ?? null,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprotected async writeV1Backup(stage: string, body: string): Promise<void> {\n\t\ttry {\n\t\t\tawait this.client.send(\n\t\t\t\tnew PutObjectCommand({\n\t\t\t\t\tBucket: this.bucket,\n\t\t\t\t\tKey: this.key(stage, 'state.v1.json'),\n\t\t\t\t\tBody: body,\n\t\t\t\t\tContentType: 'application/json',\n\t\t\t\t\tIfNoneMatch: '*',\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\t// The first migration's backup is the one worth keeping.\n\t\t\tif (!lostRace(error)) throw error;\n\t\t}\n\t}\n\n\tprotected async createLock(stage: string, holder: LockHolder): Promise<void> {\n\t\tconst Key = this.key(stage, 'lock.json');\n\t\ttry {\n\t\t\tawait this.client.send(\n\t\t\t\tnew PutObjectCommand({\n\t\t\t\t\tBucket: this.bucket,\n\t\t\t\t\tKey,\n\t\t\t\t\tBody: JSON.stringify(holder),\n\t\t\t\t\tContentType: 'application/json',\n\t\t\t\t\tIfNoneMatch: '*',\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tif (lostRace(error)) {\n\t\t\t\tthrow new StateLocked(\n\t\t\t\t\tstage,\n\t\t\t\t\tawait this.readLock(stage),\n\t\t\t\t\t`s3://${this.bucket}/${Key}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprotected async readLock(stage: string): Promise<LockHolder | null> {\n\t\tconst object = await this.get(this.key(stage, 'lock.json'));\n\t\tif (!object) return null;\n\t\ttry {\n\t\t\treturn JSON.parse(object.body) as LockHolder;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprotected async removeLock(\n\t\tstage: string,\n\t\tholder: LockHolder | null,\n\t): Promise<void> {\n\t\tconst Key = this.key(stage, 'lock.json');\n\t\tconst object = await this.get(Key);\n\t\tif (!object) return;\n\t\tif (holder) {\n\t\t\ttry {\n\t\t\t\tif ((JSON.parse(object.body) as LockHolder).id !== holder.id) return;\n\t\t\t} catch {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\ttry {\n\t\t\t// Conditional on the lock read above, so a lock taken by another\n\t\t\t// run in the meantime survives.\n\t\t\tawait this.client.send(\n\t\t\t\tnew DeleteObjectCommand({\n\t\t\t\t\tBucket: this.bucket,\n\t\t\t\t\tKey,\n\t\t\t\t\tIfMatch: object.etag,\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tif (!lostRace(error) && !notFound(error)) throw error;\n\t\t}\n\t}\n\n\tprivate async get(\n\t\tKey: string,\n\t): Promise<{ body: string; etag: string } | null> {\n\t\ttry {\n\t\t\tconst response = await this.client.send(\n\t\t\t\tnew GetObjectCommand({ Bucket: this.bucket, Key }),\n\t\t\t);\n\t\t\tconst body = (await response.Body?.transformToString('utf-8')) ?? '';\n\t\t\treturn { body, etag: response.ETag ?? '' };\n\t\t} catch (error) {\n\t\t\tif (notFound(error)) return null;\n\t\t\tthrow error;\n\t\t}\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"S3StateStore-DR2f78Dj.mjs","names":[],"sources":["../src/deploy/S3StateStore.ts"],"sourcesContent":["/**\n * Amazon S3 state store.\n *\n * - `<prefix>/<workspace>/<stage>/state.json`: the state.\n * - `<prefix>/<workspace>/<stage>/lock.json`: the lock.\n * - `<prefix>/<workspace>/<stage>/state.v1.json`: the v1 state, kept on\n * migration.\n *\n * Every write is conditional on the server: `If-None-Match: *` to create,\n * `If-Match: <etag>` to replace. S3 answers a lost race with 412 (or 409 when\n * two conditional writes are in flight at once), so there is no window\n * between a check and a write for another run to slip into.\n */\n\nimport {\n\tDeleteObjectCommand,\n\tGetObjectCommand,\n\tPutObjectCommand,\n\tS3Client,\n\ttype S3ClientConfig,\n} from '@aws-sdk/client-s3';\nimport type { AwsRegion } from './StateProvider';\nimport {\n\tDocumentStateStore,\n\ttype LockHolder,\n\ttype RawState,\n\tStateLocked,\n\ttype StateVersion,\n\tStateVersionConflict,\n} from './StateStore';\n\nexport interface S3StateStoreOptions {\n\tworkspaceName: string;\n\tbucket: string;\n\t/** Key prefix inside the bucket; `gkm` by default. */\n\tprefix?: string;\n\tregion?: AwsRegion;\n\t/** Uses the default credential chain when omitted. */\n\tprofile?: string;\n\tcredentials?: S3ClientConfig['credentials'];\n\t/** For the local AWS emulator. */\n\tendpoint?: string;\n}\n\ninterface S3Failure {\n\tname?: string;\n\t$metadata?: { httpStatusCode?: number };\n}\n\n/** A conditional write that lost: the object is not what the write assumed. */\nfunction lostRace(error: unknown): boolean {\n\tconst { name, $metadata } = (error ?? {}) as S3Failure;\n\treturn (\n\t\tname === 'PreconditionFailed' ||\n\t\tname === 'ConditionalRequestConflict' ||\n\t\t$metadata?.httpStatusCode === 412 ||\n\t\t$metadata?.httpStatusCode === 409\n\t);\n}\n\nfunction notFound(error: unknown): boolean {\n\tconst { name, $metadata } = (error ?? {}) as S3Failure;\n\treturn (\n\t\tname === 'NoSuchKey' ||\n\t\tname === 'NotFound' ||\n\t\t$metadata?.httpStatusCode === 404\n\t);\n}\n\nexport class S3StateStore extends DocumentStateStore {\n\tprivate readonly prefix: string;\n\n\tconstructor(\n\t\treadonly workspaceName: string,\n\t\treadonly bucket: string,\n\t\tprivate readonly client: S3Client,\n\t\tprefix = 'gkm',\n\t) {\n\t\tsuper();\n\t\tthis.prefix = prefix.replace(/\\/+$/, '');\n\t}\n\n\tstatic create(options: S3StateStoreOptions): S3StateStore {\n\t\tconst config: S3ClientConfig = {\n\t\t\tregion: options.region,\n\t\t\tendpoint: options.endpoint,\n\t\t\t// The emulator serves buckets by path, not by subdomain.\n\t\t\tforcePathStyle: options.endpoint ? true : undefined,\n\t\t};\n\t\tif (options.profile) {\n\t\t\t// Required lazily: only a profile needs the credential providers.\n\t\t\tconst { fromIni } = require('@aws-sdk/credential-providers');\n\t\t\tconfig.credentials = fromIni({ profile: options.profile });\n\t\t} else if (options.credentials) {\n\t\t\tconfig.credentials = options.credentials;\n\t\t}\n\t\treturn new S3StateStore(\n\t\t\toptions.workspaceName,\n\t\t\toptions.bucket,\n\t\t\tnew S3Client(config),\n\t\t\toptions.prefix,\n\t\t);\n\t}\n\n\tprivate key(stage: string, leaf: string): string {\n\t\tconst path = `${this.workspaceName}/${stage}/${leaf}`;\n\t\treturn this.prefix ? `${this.prefix}/${path}` : path;\n\t}\n\n\tprotected location(stage: string): string {\n\t\treturn `s3://${this.bucket}/${this.key(stage, 'state.json')}`;\n\t}\n\n\tprotected async readRaw(stage: string): Promise<RawState | null> {\n\t\tconst object = await this.get(this.key(stage, 'state.json'));\n\t\tif (!object) return null;\n\t\treturn { body: object.body, version: object.etag };\n\t}\n\n\tprotected async writeRaw(\n\t\tstage: string,\n\t\tbody: string,\n\t\texpectedVersion: StateVersion | null,\n\t): Promise<StateVersion> {\n\t\tconst Key = this.key(stage, 'state.json');\n\t\ttry {\n\t\t\tconst { ETag } = await this.client.send(\n\t\t\t\tnew PutObjectCommand({\n\t\t\t\t\tBucket: this.bucket,\n\t\t\t\t\tKey,\n\t\t\t\t\tBody: body,\n\t\t\t\t\tContentType: 'application/json',\n\t\t\t\t\t...(expectedVersion === null\n\t\t\t\t\t\t? { IfNoneMatch: '*' }\n\t\t\t\t\t\t: { IfMatch: expectedVersion }),\n\t\t\t\t}),\n\t\t\t);\n\t\t\tif (ETag) return ETag;\n\t\t\t// Some S3-compatible servers omit the ETag on a conditional put.\n\t\t\treturn (await this.get(Key))?.etag ?? '';\n\t\t} catch (error) {\n\t\t\tif (lostRace(error)) {\n\t\t\t\tconst current = await this.get(Key);\n\t\t\t\tthrow new StateVersionConflict(\n\t\t\t\t\tstage,\n\t\t\t\t\texpectedVersion,\n\t\t\t\t\tcurrent?.etag ?? null,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprotected async writeV1Backup(stage: string, body: string): Promise<void> {\n\t\ttry {\n\t\t\tawait this.client.send(\n\t\t\t\tnew PutObjectCommand({\n\t\t\t\t\tBucket: this.bucket,\n\t\t\t\t\tKey: this.key(stage, 'state.v1.json'),\n\t\t\t\t\tBody: body,\n\t\t\t\t\tContentType: 'application/json',\n\t\t\t\t\tIfNoneMatch: '*',\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\t// The first migration's backup is the one worth keeping.\n\t\t\tif (!lostRace(error)) throw error;\n\t\t}\n\t}\n\n\tprotected async createLock(stage: string, holder: LockHolder): Promise<void> {\n\t\tconst Key = this.key(stage, 'lock.json');\n\t\ttry {\n\t\t\tawait this.client.send(\n\t\t\t\tnew PutObjectCommand({\n\t\t\t\t\tBucket: this.bucket,\n\t\t\t\t\tKey,\n\t\t\t\t\tBody: JSON.stringify(holder),\n\t\t\t\t\tContentType: 'application/json',\n\t\t\t\t\tIfNoneMatch: '*',\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tif (lostRace(error)) {\n\t\t\t\tthrow new StateLocked(\n\t\t\t\t\tstage,\n\t\t\t\t\tawait this.readLock(stage),\n\t\t\t\t\t`s3://${this.bucket}/${Key}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprotected async readLock(stage: string): Promise<LockHolder | null> {\n\t\tconst object = await this.get(this.key(stage, 'lock.json'));\n\t\tif (!object) return null;\n\t\ttry {\n\t\t\treturn JSON.parse(object.body) as LockHolder;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprotected async removeLock(\n\t\tstage: string,\n\t\tholder: LockHolder | null,\n\t): Promise<void> {\n\t\tconst Key = this.key(stage, 'lock.json');\n\t\tconst object = await this.get(Key);\n\t\tif (!object) return;\n\t\tif (holder) {\n\t\t\ttry {\n\t\t\t\tif ((JSON.parse(object.body) as LockHolder).id !== holder.id) return;\n\t\t\t} catch {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\ttry {\n\t\t\t// Conditional on the lock read above, so a lock taken by another\n\t\t\t// run in the meantime survives.\n\t\t\tawait this.client.send(\n\t\t\t\tnew DeleteObjectCommand({\n\t\t\t\t\tBucket: this.bucket,\n\t\t\t\t\tKey,\n\t\t\t\t\tIfMatch: object.etag,\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tif (!lostRace(error) && !notFound(error)) throw error;\n\t\t}\n\t}\n\n\tprivate async get(\n\t\tKey: string,\n\t): Promise<{ body: string; etag: string } | null> {\n\t\ttry {\n\t\t\tconst response = await this.client.send(\n\t\t\t\tnew GetObjectCommand({ Bucket: this.bucket, Key }),\n\t\t\t);\n\t\t\tconst body = (await response.Body?.transformToString('utf-8')) ?? '';\n\t\t\treturn { body, etag: response.ETag ?? '' };\n\t\t} catch (error) {\n\t\t\tif (notFound(error)) return null;\n\t\t\tthrow error;\n\t\t}\n\t}\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- const require_sniffer = require("./sniffer-BjG-1mlD.cjs");
1
+ const require_sniffer = require("./sniffer-DeyYI9Iv.cjs");
2
2
  let _aws_sdk_client_s3 = require("@aws-sdk/client-s3");
3
3
  //#region src/deploy/S3StateStore.ts
4
4
  /**
@@ -157,4 +157,4 @@ var S3StateStore = class S3StateStore extends require_sniffer.DocumentStateStore
157
157
  //#endregion
158
158
  exports.S3StateStore = S3StateStore;
159
159
 
160
- //# sourceMappingURL=S3StateStore-35YfcdL_.cjs.map
160
+ //# sourceMappingURL=S3StateStore-ytvpIiv8.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"S3StateStore-35YfcdL_.cjs","names":["DocumentStateStore","S3Client","PutObjectCommand","StateVersionConflict","StateLocked","DeleteObjectCommand","GetObjectCommand"],"sources":["../src/deploy/S3StateStore.ts"],"sourcesContent":["/**\n * Amazon S3 state store.\n *\n * - `<prefix>/<workspace>/<stage>/state.json`: the state.\n * - `<prefix>/<workspace>/<stage>/lock.json`: the lock.\n * - `<prefix>/<workspace>/<stage>/state.v1.json`: the v1 state, kept on\n * migration.\n *\n * Every write is conditional on the server: `If-None-Match: *` to create,\n * `If-Match: <etag>` to replace. S3 answers a lost race with 412 (or 409 when\n * two conditional writes are in flight at once), so there is no window\n * between a check and a write for another run to slip into.\n */\n\nimport {\n\tDeleteObjectCommand,\n\tGetObjectCommand,\n\tPutObjectCommand,\n\tS3Client,\n\ttype S3ClientConfig,\n} from '@aws-sdk/client-s3';\nimport type { AwsRegion } from './StateProvider';\nimport {\n\tDocumentStateStore,\n\ttype LockHolder,\n\ttype RawState,\n\tStateLocked,\n\ttype StateVersion,\n\tStateVersionConflict,\n} from './StateStore';\n\nexport interface S3StateStoreOptions {\n\tworkspaceName: string;\n\tbucket: string;\n\t/** Key prefix inside the bucket; `gkm` by default. */\n\tprefix?: string;\n\tregion?: AwsRegion;\n\t/** Uses the default credential chain when omitted. */\n\tprofile?: string;\n\tcredentials?: S3ClientConfig['credentials'];\n\t/** For the local AWS emulator. */\n\tendpoint?: string;\n}\n\ninterface S3Failure {\n\tname?: string;\n\t$metadata?: { httpStatusCode?: number };\n}\n\n/** A conditional write that lost: the object is not what the write assumed. */\nfunction lostRace(error: unknown): boolean {\n\tconst { name, $metadata } = (error ?? {}) as S3Failure;\n\treturn (\n\t\tname === 'PreconditionFailed' ||\n\t\tname === 'ConditionalRequestConflict' ||\n\t\t$metadata?.httpStatusCode === 412 ||\n\t\t$metadata?.httpStatusCode === 409\n\t);\n}\n\nfunction notFound(error: unknown): boolean {\n\tconst { name, $metadata } = (error ?? {}) as S3Failure;\n\treturn (\n\t\tname === 'NoSuchKey' ||\n\t\tname === 'NotFound' ||\n\t\t$metadata?.httpStatusCode === 404\n\t);\n}\n\nexport class S3StateStore extends DocumentStateStore {\n\tprivate readonly prefix: string;\n\n\tconstructor(\n\t\treadonly workspaceName: string,\n\t\treadonly bucket: string,\n\t\tprivate readonly client: S3Client,\n\t\tprefix = 'gkm',\n\t) {\n\t\tsuper();\n\t\tthis.prefix = prefix.replace(/\\/+$/, '');\n\t}\n\n\tstatic create(options: S3StateStoreOptions): S3StateStore {\n\t\tconst config: S3ClientConfig = {\n\t\t\tregion: options.region,\n\t\t\tendpoint: options.endpoint,\n\t\t\t// The emulator serves buckets by path, not by subdomain.\n\t\t\tforcePathStyle: options.endpoint ? true : undefined,\n\t\t};\n\t\tif (options.profile) {\n\t\t\t// Required lazily: only a profile needs the credential providers.\n\t\t\tconst { fromIni } = require('@aws-sdk/credential-providers');\n\t\t\tconfig.credentials = fromIni({ profile: options.profile });\n\t\t} else if (options.credentials) {\n\t\t\tconfig.credentials = options.credentials;\n\t\t}\n\t\treturn new S3StateStore(\n\t\t\toptions.workspaceName,\n\t\t\toptions.bucket,\n\t\t\tnew S3Client(config),\n\t\t\toptions.prefix,\n\t\t);\n\t}\n\n\tprivate key(stage: string, leaf: string): string {\n\t\tconst path = `${this.workspaceName}/${stage}/${leaf}`;\n\t\treturn this.prefix ? `${this.prefix}/${path}` : path;\n\t}\n\n\tprotected location(stage: string): string {\n\t\treturn `s3://${this.bucket}/${this.key(stage, 'state.json')}`;\n\t}\n\n\tprotected async readRaw(stage: string): Promise<RawState | null> {\n\t\tconst object = await this.get(this.key(stage, 'state.json'));\n\t\tif (!object) return null;\n\t\treturn { body: object.body, version: object.etag };\n\t}\n\n\tprotected async writeRaw(\n\t\tstage: string,\n\t\tbody: string,\n\t\texpectedVersion: StateVersion | null,\n\t): Promise<StateVersion> {\n\t\tconst Key = this.key(stage, 'state.json');\n\t\ttry {\n\t\t\tconst { ETag } = await this.client.send(\n\t\t\t\tnew PutObjectCommand({\n\t\t\t\t\tBucket: this.bucket,\n\t\t\t\t\tKey,\n\t\t\t\t\tBody: body,\n\t\t\t\t\tContentType: 'application/json',\n\t\t\t\t\t...(expectedVersion === null\n\t\t\t\t\t\t? { IfNoneMatch: '*' }\n\t\t\t\t\t\t: { IfMatch: expectedVersion }),\n\t\t\t\t}),\n\t\t\t);\n\t\t\tif (ETag) return ETag;\n\t\t\t// Some S3-compatible servers omit the ETag on a conditional put.\n\t\t\treturn (await this.get(Key))?.etag ?? '';\n\t\t} catch (error) {\n\t\t\tif (lostRace(error)) {\n\t\t\t\tconst current = await this.get(Key);\n\t\t\t\tthrow new StateVersionConflict(\n\t\t\t\t\tstage,\n\t\t\t\t\texpectedVersion,\n\t\t\t\t\tcurrent?.etag ?? null,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprotected async writeV1Backup(stage: string, body: string): Promise<void> {\n\t\ttry {\n\t\t\tawait this.client.send(\n\t\t\t\tnew PutObjectCommand({\n\t\t\t\t\tBucket: this.bucket,\n\t\t\t\t\tKey: this.key(stage, 'state.v1.json'),\n\t\t\t\t\tBody: body,\n\t\t\t\t\tContentType: 'application/json',\n\t\t\t\t\tIfNoneMatch: '*',\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\t// The first migration's backup is the one worth keeping.\n\t\t\tif (!lostRace(error)) throw error;\n\t\t}\n\t}\n\n\tprotected async createLock(stage: string, holder: LockHolder): Promise<void> {\n\t\tconst Key = this.key(stage, 'lock.json');\n\t\ttry {\n\t\t\tawait this.client.send(\n\t\t\t\tnew PutObjectCommand({\n\t\t\t\t\tBucket: this.bucket,\n\t\t\t\t\tKey,\n\t\t\t\t\tBody: JSON.stringify(holder),\n\t\t\t\t\tContentType: 'application/json',\n\t\t\t\t\tIfNoneMatch: '*',\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tif (lostRace(error)) {\n\t\t\t\tthrow new StateLocked(\n\t\t\t\t\tstage,\n\t\t\t\t\tawait this.readLock(stage),\n\t\t\t\t\t`s3://${this.bucket}/${Key}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprotected async readLock(stage: string): Promise<LockHolder | null> {\n\t\tconst object = await this.get(this.key(stage, 'lock.json'));\n\t\tif (!object) return null;\n\t\ttry {\n\t\t\treturn JSON.parse(object.body) as LockHolder;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprotected async removeLock(\n\t\tstage: string,\n\t\tholder: LockHolder | null,\n\t): Promise<void> {\n\t\tconst Key = this.key(stage, 'lock.json');\n\t\tconst object = await this.get(Key);\n\t\tif (!object) return;\n\t\tif (holder) {\n\t\t\ttry {\n\t\t\t\tif ((JSON.parse(object.body) as LockHolder).id !== holder.id) return;\n\t\t\t} catch {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\ttry {\n\t\t\t// Conditional on the lock read above, so a lock taken by another\n\t\t\t// run in the meantime survives.\n\t\t\tawait this.client.send(\n\t\t\t\tnew DeleteObjectCommand({\n\t\t\t\t\tBucket: this.bucket,\n\t\t\t\t\tKey,\n\t\t\t\t\tIfMatch: object.etag,\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tif (!lostRace(error) && !notFound(error)) throw error;\n\t\t}\n\t}\n\n\tprivate async get(\n\t\tKey: string,\n\t): Promise<{ body: string; etag: string } | null> {\n\t\ttry {\n\t\t\tconst response = await this.client.send(\n\t\t\t\tnew GetObjectCommand({ Bucket: this.bucket, Key }),\n\t\t\t);\n\t\t\tconst body = (await response.Body?.transformToString('utf-8')) ?? '';\n\t\t\treturn { body, etag: response.ETag ?? '' };\n\t\t} catch (error) {\n\t\t\tif (notFound(error)) return null;\n\t\t\tthrow error;\n\t\t}\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"S3StateStore-ytvpIiv8.cjs","names":["DocumentStateStore","S3Client","PutObjectCommand","StateVersionConflict","StateLocked","DeleteObjectCommand","GetObjectCommand"],"sources":["../src/deploy/S3StateStore.ts"],"sourcesContent":["/**\n * Amazon S3 state store.\n *\n * - `<prefix>/<workspace>/<stage>/state.json`: the state.\n * - `<prefix>/<workspace>/<stage>/lock.json`: the lock.\n * - `<prefix>/<workspace>/<stage>/state.v1.json`: the v1 state, kept on\n * migration.\n *\n * Every write is conditional on the server: `If-None-Match: *` to create,\n * `If-Match: <etag>` to replace. S3 answers a lost race with 412 (or 409 when\n * two conditional writes are in flight at once), so there is no window\n * between a check and a write for another run to slip into.\n */\n\nimport {\n\tDeleteObjectCommand,\n\tGetObjectCommand,\n\tPutObjectCommand,\n\tS3Client,\n\ttype S3ClientConfig,\n} from '@aws-sdk/client-s3';\nimport type { AwsRegion } from './StateProvider';\nimport {\n\tDocumentStateStore,\n\ttype LockHolder,\n\ttype RawState,\n\tStateLocked,\n\ttype StateVersion,\n\tStateVersionConflict,\n} from './StateStore';\n\nexport interface S3StateStoreOptions {\n\tworkspaceName: string;\n\tbucket: string;\n\t/** Key prefix inside the bucket; `gkm` by default. */\n\tprefix?: string;\n\tregion?: AwsRegion;\n\t/** Uses the default credential chain when omitted. */\n\tprofile?: string;\n\tcredentials?: S3ClientConfig['credentials'];\n\t/** For the local AWS emulator. */\n\tendpoint?: string;\n}\n\ninterface S3Failure {\n\tname?: string;\n\t$metadata?: { httpStatusCode?: number };\n}\n\n/** A conditional write that lost: the object is not what the write assumed. */\nfunction lostRace(error: unknown): boolean {\n\tconst { name, $metadata } = (error ?? {}) as S3Failure;\n\treturn (\n\t\tname === 'PreconditionFailed' ||\n\t\tname === 'ConditionalRequestConflict' ||\n\t\t$metadata?.httpStatusCode === 412 ||\n\t\t$metadata?.httpStatusCode === 409\n\t);\n}\n\nfunction notFound(error: unknown): boolean {\n\tconst { name, $metadata } = (error ?? {}) as S3Failure;\n\treturn (\n\t\tname === 'NoSuchKey' ||\n\t\tname === 'NotFound' ||\n\t\t$metadata?.httpStatusCode === 404\n\t);\n}\n\nexport class S3StateStore extends DocumentStateStore {\n\tprivate readonly prefix: string;\n\n\tconstructor(\n\t\treadonly workspaceName: string,\n\t\treadonly bucket: string,\n\t\tprivate readonly client: S3Client,\n\t\tprefix = 'gkm',\n\t) {\n\t\tsuper();\n\t\tthis.prefix = prefix.replace(/\\/+$/, '');\n\t}\n\n\tstatic create(options: S3StateStoreOptions): S3StateStore {\n\t\tconst config: S3ClientConfig = {\n\t\t\tregion: options.region,\n\t\t\tendpoint: options.endpoint,\n\t\t\t// The emulator serves buckets by path, not by subdomain.\n\t\t\tforcePathStyle: options.endpoint ? true : undefined,\n\t\t};\n\t\tif (options.profile) {\n\t\t\t// Required lazily: only a profile needs the credential providers.\n\t\t\tconst { fromIni } = require('@aws-sdk/credential-providers');\n\t\t\tconfig.credentials = fromIni({ profile: options.profile });\n\t\t} else if (options.credentials) {\n\t\t\tconfig.credentials = options.credentials;\n\t\t}\n\t\treturn new S3StateStore(\n\t\t\toptions.workspaceName,\n\t\t\toptions.bucket,\n\t\t\tnew S3Client(config),\n\t\t\toptions.prefix,\n\t\t);\n\t}\n\n\tprivate key(stage: string, leaf: string): string {\n\t\tconst path = `${this.workspaceName}/${stage}/${leaf}`;\n\t\treturn this.prefix ? `${this.prefix}/${path}` : path;\n\t}\n\n\tprotected location(stage: string): string {\n\t\treturn `s3://${this.bucket}/${this.key(stage, 'state.json')}`;\n\t}\n\n\tprotected async readRaw(stage: string): Promise<RawState | null> {\n\t\tconst object = await this.get(this.key(stage, 'state.json'));\n\t\tif (!object) return null;\n\t\treturn { body: object.body, version: object.etag };\n\t}\n\n\tprotected async writeRaw(\n\t\tstage: string,\n\t\tbody: string,\n\t\texpectedVersion: StateVersion | null,\n\t): Promise<StateVersion> {\n\t\tconst Key = this.key(stage, 'state.json');\n\t\ttry {\n\t\t\tconst { ETag } = await this.client.send(\n\t\t\t\tnew PutObjectCommand({\n\t\t\t\t\tBucket: this.bucket,\n\t\t\t\t\tKey,\n\t\t\t\t\tBody: body,\n\t\t\t\t\tContentType: 'application/json',\n\t\t\t\t\t...(expectedVersion === null\n\t\t\t\t\t\t? { IfNoneMatch: '*' }\n\t\t\t\t\t\t: { IfMatch: expectedVersion }),\n\t\t\t\t}),\n\t\t\t);\n\t\t\tif (ETag) return ETag;\n\t\t\t// Some S3-compatible servers omit the ETag on a conditional put.\n\t\t\treturn (await this.get(Key))?.etag ?? '';\n\t\t} catch (error) {\n\t\t\tif (lostRace(error)) {\n\t\t\t\tconst current = await this.get(Key);\n\t\t\t\tthrow new StateVersionConflict(\n\t\t\t\t\tstage,\n\t\t\t\t\texpectedVersion,\n\t\t\t\t\tcurrent?.etag ?? null,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprotected async writeV1Backup(stage: string, body: string): Promise<void> {\n\t\ttry {\n\t\t\tawait this.client.send(\n\t\t\t\tnew PutObjectCommand({\n\t\t\t\t\tBucket: this.bucket,\n\t\t\t\t\tKey: this.key(stage, 'state.v1.json'),\n\t\t\t\t\tBody: body,\n\t\t\t\t\tContentType: 'application/json',\n\t\t\t\t\tIfNoneMatch: '*',\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\t// The first migration's backup is the one worth keeping.\n\t\t\tif (!lostRace(error)) throw error;\n\t\t}\n\t}\n\n\tprotected async createLock(stage: string, holder: LockHolder): Promise<void> {\n\t\tconst Key = this.key(stage, 'lock.json');\n\t\ttry {\n\t\t\tawait this.client.send(\n\t\t\t\tnew PutObjectCommand({\n\t\t\t\t\tBucket: this.bucket,\n\t\t\t\t\tKey,\n\t\t\t\t\tBody: JSON.stringify(holder),\n\t\t\t\t\tContentType: 'application/json',\n\t\t\t\t\tIfNoneMatch: '*',\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tif (lostRace(error)) {\n\t\t\t\tthrow new StateLocked(\n\t\t\t\t\tstage,\n\t\t\t\t\tawait this.readLock(stage),\n\t\t\t\t\t`s3://${this.bucket}/${Key}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprotected async readLock(stage: string): Promise<LockHolder | null> {\n\t\tconst object = await this.get(this.key(stage, 'lock.json'));\n\t\tif (!object) return null;\n\t\ttry {\n\t\t\treturn JSON.parse(object.body) as LockHolder;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprotected async removeLock(\n\t\tstage: string,\n\t\tholder: LockHolder | null,\n\t): Promise<void> {\n\t\tconst Key = this.key(stage, 'lock.json');\n\t\tconst object = await this.get(Key);\n\t\tif (!object) return;\n\t\tif (holder) {\n\t\t\ttry {\n\t\t\t\tif ((JSON.parse(object.body) as LockHolder).id !== holder.id) return;\n\t\t\t} catch {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\ttry {\n\t\t\t// Conditional on the lock read above, so a lock taken by another\n\t\t\t// run in the meantime survives.\n\t\t\tawait this.client.send(\n\t\t\t\tnew DeleteObjectCommand({\n\t\t\t\t\tBucket: this.bucket,\n\t\t\t\t\tKey,\n\t\t\t\t\tIfMatch: object.etag,\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tif (!lostRace(error) && !notFound(error)) throw error;\n\t\t}\n\t}\n\n\tprivate async get(\n\t\tKey: string,\n\t): Promise<{ body: string; etag: string } | null> {\n\t\ttry {\n\t\t\tconst response = await this.client.send(\n\t\t\t\tnew GetObjectCommand({ Bucket: this.bucket, Key }),\n\t\t\t);\n\t\t\tconst body = (await response.Body?.transformToString('utf-8')) ?? '';\n\t\t\treturn { body, etag: response.ETag ?? '' };\n\t\t} catch (error) {\n\t\t\tif (notFound(error)) return null;\n\t\t\tthrow error;\n\t\t}\n\t}\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { n as __require } from "./rolldown-runtime-BhDjJH2R.mjs";
2
- import { d as StateLocked, l as DocumentStateStore, m as StateVersionConflict } from "./sniffer-ByWtH28i.mjs";
2
+ import { d as StateLocked, l as DocumentStateStore, m as StateVersionConflict } from "./sniffer-6oYI-MpT.mjs";
3
3
  import { DeleteParameterCommand, GetParameterCommand, ParameterAlreadyExists, ParameterNotFound, PutParameterCommand, SSMClient } from "@aws-sdk/client-ssm";
4
4
  //#region src/deploy/SSMStateStore.ts
5
5
  /**
@@ -151,4 +151,4 @@ function parseHolder(value) {
151
151
  //#endregion
152
152
  export { SSMStateStore };
153
153
 
154
- //# sourceMappingURL=SSMStateStore-dOmFO6g1.mjs.map
154
+ //# sourceMappingURL=SSMStateStore-BZ3XL0Mr.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"SSMStateStore-dOmFO6g1.mjs","names":[],"sources":["../src/deploy/SSMStateStore.ts"],"sourcesContent":["/**\n * AWS SSM Parameter Store state store.\n *\n * - `/gkm/<workspace>/<stage>/state`: the state, a SecureString.\n * - `/gkm/<workspace>/<stage>/lock`: the lock, created only if absent.\n * - `/gkm/<workspace>/<stage>/state.v1`: the v1 state, kept on migration.\n *\n * SSM has no conditional put, so a write checks the parameter's version\n * before it puts, and checks the version the put returns after: a put that\n * did not land exactly one version above the one it was based on raced\n * another writer, and raises `StateVersionConflict` rather than reporting\n * success. Both writes stay in the parameter's history.\n */\n\nimport {\n\tDeleteParameterCommand,\n\tGetParameterCommand,\n\tParameterAlreadyExists,\n\tParameterNotFound,\n\tPutParameterCommand,\n\tSSMClient,\n\ttype SSMClientConfig,\n} from '@aws-sdk/client-ssm';\nimport type { AwsRegion } from './StateProvider';\nimport {\n\tDocumentStateStore,\n\ttype LockHolder,\n\ttype RawState,\n\tStateLocked,\n\ttype StateVersion,\n\tStateVersionConflict,\n} from './StateStore';\n\nexport interface SSMStateStoreOptions {\n\tworkspaceName: string;\n\tregion?: AwsRegion;\n\t/** Uses the default credential chain when omitted. */\n\tprofile?: string;\n\tcredentials?: SSMClientConfig['credentials'];\n\t/** For the local AWS emulator. */\n\tendpoint?: string;\n}\n\nexport class SSMStateStore extends DocumentStateStore {\n\tconstructor(\n\t\treadonly workspaceName: string,\n\t\tprivate readonly client: SSMClient,\n\t) {\n\t\tsuper();\n\t}\n\n\tstatic create(options: SSMStateStoreOptions): SSMStateStore {\n\t\tconst config: SSMClientConfig = {\n\t\t\tregion: options.region,\n\t\t\tendpoint: options.endpoint,\n\t\t};\n\t\tif (options.profile) {\n\t\t\t// Required lazily: only a profile needs the credential providers.\n\t\t\tconst { fromIni } = require('@aws-sdk/credential-providers');\n\t\t\tconfig.credentials = fromIni({ profile: options.profile });\n\t\t} else if (options.credentials) {\n\t\t\tconfig.credentials = options.credentials;\n\t\t}\n\t\treturn new SSMStateStore(options.workspaceName, new SSMClient(config));\n\t}\n\n\tprivate name(stage: string, leaf: string): string {\n\t\treturn `/gkm/${this.workspaceName}/${stage}/${leaf}`;\n\t}\n\n\tprotected location(stage: string): string {\n\t\treturn `ssm:${this.name(stage, 'state')}`;\n\t}\n\n\tprotected async readRaw(stage: string): Promise<RawState | null> {\n\t\tconst parameter = await this.get(this.name(stage, 'state'));\n\t\tif (!parameter) return null;\n\t\treturn { body: parameter.value, version: String(parameter.version) };\n\t}\n\n\tprotected async writeRaw(\n\t\tstage: string,\n\t\tbody: string,\n\t\texpectedVersion: StateVersion | null,\n\t): Promise<StateVersion> {\n\t\tconst Name = this.name(stage, 'state');\n\t\tconst Description = `GKM deployment state for ${this.workspaceName}/${stage}`;\n\n\t\tif (expectedVersion === null) {\n\t\t\ttry {\n\t\t\t\tconst { Version } = await this.client.send(\n\t\t\t\t\tnew PutParameterCommand({\n\t\t\t\t\t\tName,\n\t\t\t\t\t\tValue: body,\n\t\t\t\t\t\tType: 'SecureString',\n\t\t\t\t\t\tDescription,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\treturn String(Version);\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof ParameterAlreadyExists) {\n\t\t\t\t\tconst current = await this.get(Name);\n\t\t\t\t\tthrow new StateVersionConflict(\n\t\t\t\t\t\tstage,\n\t\t\t\t\t\tnull,\n\t\t\t\t\t\tcurrent ? String(current.version) : null,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\n\t\tconst current = await this.get(Name);\n\t\tconst actual = current ? String(current.version) : null;\n\t\tif (actual !== expectedVersion) {\n\t\t\tthrow new StateVersionConflict(stage, expectedVersion, actual);\n\t\t}\n\n\t\tconst { Version } = await this.client.send(\n\t\t\tnew PutParameterCommand({\n\t\t\t\tName,\n\t\t\t\tValue: body,\n\t\t\t\tType: 'SecureString',\n\t\t\t\tOverwrite: true,\n\t\t\t\tDescription,\n\t\t\t}),\n\t\t);\n\t\tif (Version !== Number(expectedVersion) + 1) {\n\t\t\t// Another put landed between the check and this one. Ours is now\n\t\t\t// on top, but the caller that wrote the other believes its write\n\t\t\t// is current — its next write will conflict too, so neither is\n\t\t\t// lost silently.\n\t\t\tthrow new StateVersionConflict(\n\t\t\t\tstage,\n\t\t\t\texpectedVersion,\n\t\t\t\tString((Version ?? 1) - 1),\n\t\t\t);\n\t\t}\n\n\t\t// The version alone is not enough under an emulator, and need not be\n\t\t// on SSM either: two overwrites that raced can both be answered with\n\t\t// the same version, and both would report success. If the parameter is\n\t\t// still at the version this put returned, it has to hold this body —\n\t\t// otherwise the other put is the one that stuck.\n\t\tconst landed = await this.get(Name);\n\t\tif (landed && landed.version === Version && landed.value !== body) {\n\t\t\tthrow new StateVersionConflict(stage, expectedVersion, String(Version));\n\t\t}\n\t\treturn String(Version);\n\t}\n\n\tprotected async writeV1Backup(stage: string, body: string): Promise<void> {\n\t\ttry {\n\t\t\tawait this.client.send(\n\t\t\t\tnew PutParameterCommand({\n\t\t\t\t\tName: this.name(stage, 'state.v1'),\n\t\t\t\t\tValue: body,\n\t\t\t\t\tType: 'SecureString',\n\t\t\t\t\tDescription: `GKM v1 deployment state for ${this.workspaceName}/${stage}, kept on migration`,\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\t// The first migration's backup is the one worth keeping.\n\t\t\tif (!(error instanceof ParameterAlreadyExists)) throw error;\n\t\t}\n\t}\n\n\tprotected async createLock(stage: string, holder: LockHolder): Promise<void> {\n\t\tconst Name = this.name(stage, 'lock');\n\t\ttry {\n\t\t\tawait this.client.send(\n\t\t\t\tnew PutParameterCommand({\n\t\t\t\t\tName,\n\t\t\t\t\tValue: JSON.stringify(holder),\n\t\t\t\t\tType: 'String',\n\t\t\t\t\tDescription: `GKM deploy lock for ${this.workspaceName}/${stage}`,\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tif (error instanceof ParameterAlreadyExists) {\n\t\t\t\tthrow new StateLocked(stage, await this.readLock(stage), `ssm:${Name}`);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\n\t\t// A create-only put is atomic on SSM, so the lock is ours. Read it back\n\t\t// anyway: on a store where two racing creates can both succeed (the\n\t\t// local emulator does, a few times in a hundred) the later one has\n\t\t// overwritten the earlier — version 2, someone else's holder — and\n\t\t// taking the lock regardless would give the stage two deploys.\n\t\tconst created = await this.get(Name);\n\t\tconst recorded = created ? parseHolder(created.value) : null;\n\t\tif (created?.version !== 1 || recorded?.id !== holder.id) {\n\t\t\tthrow new StateLocked(stage, recorded, `ssm:${Name}`);\n\t\t}\n\t}\n\n\tprotected async readLock(stage: string): Promise<LockHolder | null> {\n\t\tconst parameter = await this.get(this.name(stage, 'lock'));\n\t\treturn parameter ? parseHolder(parameter.value) : null;\n\t}\n\n\tprotected async removeLock(\n\t\tstage: string,\n\t\tholder: LockHolder | null,\n\t): Promise<void> {\n\t\tif (holder) {\n\t\t\tconst current = await this.readLock(stage);\n\t\t\tif (current?.id !== holder.id) return;\n\t\t}\n\t\ttry {\n\t\t\tawait this.client.send(\n\t\t\t\tnew DeleteParameterCommand({ Name: this.name(stage, 'lock') }),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tif (!(error instanceof ParameterNotFound)) throw error;\n\t\t}\n\t}\n\n\tprivate async get(\n\t\tName: string,\n\t): Promise<{ value: string; version: number } | null> {\n\t\ttry {\n\t\t\tconst { Parameter } = await this.client.send(\n\t\t\t\tnew GetParameterCommand({ Name, WithDecryption: true }),\n\t\t\t);\n\t\t\tif (Parameter?.Value === undefined) return null;\n\t\t\treturn { value: Parameter.Value, version: Parameter.Version ?? 1 };\n\t\t} catch (error) {\n\t\t\tif (error instanceof ParameterNotFound) return null;\n\t\t\tthrow error;\n\t\t}\n\t}\n}\n\nfunction parseHolder(value: string): LockHolder | null {\n\ttry {\n\t\treturn JSON.parse(value) as LockHolder;\n\t} catch {\n\t\treturn null;\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"SSMStateStore-BZ3XL0Mr.mjs","names":[],"sources":["../src/deploy/SSMStateStore.ts"],"sourcesContent":["/**\n * AWS SSM Parameter Store state store.\n *\n * - `/gkm/<workspace>/<stage>/state`: the state, a SecureString.\n * - `/gkm/<workspace>/<stage>/lock`: the lock, created only if absent.\n * - `/gkm/<workspace>/<stage>/state.v1`: the v1 state, kept on migration.\n *\n * SSM has no conditional put, so a write checks the parameter's version\n * before it puts, and checks the version the put returns after: a put that\n * did not land exactly one version above the one it was based on raced\n * another writer, and raises `StateVersionConflict` rather than reporting\n * success. Both writes stay in the parameter's history.\n */\n\nimport {\n\tDeleteParameterCommand,\n\tGetParameterCommand,\n\tParameterAlreadyExists,\n\tParameterNotFound,\n\tPutParameterCommand,\n\tSSMClient,\n\ttype SSMClientConfig,\n} from '@aws-sdk/client-ssm';\nimport type { AwsRegion } from './StateProvider';\nimport {\n\tDocumentStateStore,\n\ttype LockHolder,\n\ttype RawState,\n\tStateLocked,\n\ttype StateVersion,\n\tStateVersionConflict,\n} from './StateStore';\n\nexport interface SSMStateStoreOptions {\n\tworkspaceName: string;\n\tregion?: AwsRegion;\n\t/** Uses the default credential chain when omitted. */\n\tprofile?: string;\n\tcredentials?: SSMClientConfig['credentials'];\n\t/** For the local AWS emulator. */\n\tendpoint?: string;\n}\n\nexport class SSMStateStore extends DocumentStateStore {\n\tconstructor(\n\t\treadonly workspaceName: string,\n\t\tprivate readonly client: SSMClient,\n\t) {\n\t\tsuper();\n\t}\n\n\tstatic create(options: SSMStateStoreOptions): SSMStateStore {\n\t\tconst config: SSMClientConfig = {\n\t\t\tregion: options.region,\n\t\t\tendpoint: options.endpoint,\n\t\t};\n\t\tif (options.profile) {\n\t\t\t// Required lazily: only a profile needs the credential providers.\n\t\t\tconst { fromIni } = require('@aws-sdk/credential-providers');\n\t\t\tconfig.credentials = fromIni({ profile: options.profile });\n\t\t} else if (options.credentials) {\n\t\t\tconfig.credentials = options.credentials;\n\t\t}\n\t\treturn new SSMStateStore(options.workspaceName, new SSMClient(config));\n\t}\n\n\tprivate name(stage: string, leaf: string): string {\n\t\treturn `/gkm/${this.workspaceName}/${stage}/${leaf}`;\n\t}\n\n\tprotected location(stage: string): string {\n\t\treturn `ssm:${this.name(stage, 'state')}`;\n\t}\n\n\tprotected async readRaw(stage: string): Promise<RawState | null> {\n\t\tconst parameter = await this.get(this.name(stage, 'state'));\n\t\tif (!parameter) return null;\n\t\treturn { body: parameter.value, version: String(parameter.version) };\n\t}\n\n\tprotected async writeRaw(\n\t\tstage: string,\n\t\tbody: string,\n\t\texpectedVersion: StateVersion | null,\n\t): Promise<StateVersion> {\n\t\tconst Name = this.name(stage, 'state');\n\t\tconst Description = `GKM deployment state for ${this.workspaceName}/${stage}`;\n\n\t\tif (expectedVersion === null) {\n\t\t\ttry {\n\t\t\t\tconst { Version } = await this.client.send(\n\t\t\t\t\tnew PutParameterCommand({\n\t\t\t\t\t\tName,\n\t\t\t\t\t\tValue: body,\n\t\t\t\t\t\tType: 'SecureString',\n\t\t\t\t\t\tDescription,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\treturn String(Version);\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof ParameterAlreadyExists) {\n\t\t\t\t\tconst current = await this.get(Name);\n\t\t\t\t\tthrow new StateVersionConflict(\n\t\t\t\t\t\tstage,\n\t\t\t\t\t\tnull,\n\t\t\t\t\t\tcurrent ? String(current.version) : null,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\n\t\tconst current = await this.get(Name);\n\t\tconst actual = current ? String(current.version) : null;\n\t\tif (actual !== expectedVersion) {\n\t\t\tthrow new StateVersionConflict(stage, expectedVersion, actual);\n\t\t}\n\n\t\tconst { Version } = await this.client.send(\n\t\t\tnew PutParameterCommand({\n\t\t\t\tName,\n\t\t\t\tValue: body,\n\t\t\t\tType: 'SecureString',\n\t\t\t\tOverwrite: true,\n\t\t\t\tDescription,\n\t\t\t}),\n\t\t);\n\t\tif (Version !== Number(expectedVersion) + 1) {\n\t\t\t// Another put landed between the check and this one. Ours is now\n\t\t\t// on top, but the caller that wrote the other believes its write\n\t\t\t// is current — its next write will conflict too, so neither is\n\t\t\t// lost silently.\n\t\t\tthrow new StateVersionConflict(\n\t\t\t\tstage,\n\t\t\t\texpectedVersion,\n\t\t\t\tString((Version ?? 1) - 1),\n\t\t\t);\n\t\t}\n\n\t\t// The version alone is not enough under an emulator, and need not be\n\t\t// on SSM either: two overwrites that raced can both be answered with\n\t\t// the same version, and both would report success. If the parameter is\n\t\t// still at the version this put returned, it has to hold this body —\n\t\t// otherwise the other put is the one that stuck.\n\t\tconst landed = await this.get(Name);\n\t\tif (landed && landed.version === Version && landed.value !== body) {\n\t\t\tthrow new StateVersionConflict(stage, expectedVersion, String(Version));\n\t\t}\n\t\treturn String(Version);\n\t}\n\n\tprotected async writeV1Backup(stage: string, body: string): Promise<void> {\n\t\ttry {\n\t\t\tawait this.client.send(\n\t\t\t\tnew PutParameterCommand({\n\t\t\t\t\tName: this.name(stage, 'state.v1'),\n\t\t\t\t\tValue: body,\n\t\t\t\t\tType: 'SecureString',\n\t\t\t\t\tDescription: `GKM v1 deployment state for ${this.workspaceName}/${stage}, kept on migration`,\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\t// The first migration's backup is the one worth keeping.\n\t\t\tif (!(error instanceof ParameterAlreadyExists)) throw error;\n\t\t}\n\t}\n\n\tprotected async createLock(stage: string, holder: LockHolder): Promise<void> {\n\t\tconst Name = this.name(stage, 'lock');\n\t\ttry {\n\t\t\tawait this.client.send(\n\t\t\t\tnew PutParameterCommand({\n\t\t\t\t\tName,\n\t\t\t\t\tValue: JSON.stringify(holder),\n\t\t\t\t\tType: 'String',\n\t\t\t\t\tDescription: `GKM deploy lock for ${this.workspaceName}/${stage}`,\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tif (error instanceof ParameterAlreadyExists) {\n\t\t\t\tthrow new StateLocked(stage, await this.readLock(stage), `ssm:${Name}`);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\n\t\t// A create-only put is atomic on SSM, so the lock is ours. Read it back\n\t\t// anyway: on a store where two racing creates can both succeed (the\n\t\t// local emulator does, a few times in a hundred) the later one has\n\t\t// overwritten the earlier — version 2, someone else's holder — and\n\t\t// taking the lock regardless would give the stage two deploys.\n\t\tconst created = await this.get(Name);\n\t\tconst recorded = created ? parseHolder(created.value) : null;\n\t\tif (created?.version !== 1 || recorded?.id !== holder.id) {\n\t\t\tthrow new StateLocked(stage, recorded, `ssm:${Name}`);\n\t\t}\n\t}\n\n\tprotected async readLock(stage: string): Promise<LockHolder | null> {\n\t\tconst parameter = await this.get(this.name(stage, 'lock'));\n\t\treturn parameter ? parseHolder(parameter.value) : null;\n\t}\n\n\tprotected async removeLock(\n\t\tstage: string,\n\t\tholder: LockHolder | null,\n\t): Promise<void> {\n\t\tif (holder) {\n\t\t\tconst current = await this.readLock(stage);\n\t\t\tif (current?.id !== holder.id) return;\n\t\t}\n\t\ttry {\n\t\t\tawait this.client.send(\n\t\t\t\tnew DeleteParameterCommand({ Name: this.name(stage, 'lock') }),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tif (!(error instanceof ParameterNotFound)) throw error;\n\t\t}\n\t}\n\n\tprivate async get(\n\t\tName: string,\n\t): Promise<{ value: string; version: number } | null> {\n\t\ttry {\n\t\t\tconst { Parameter } = await this.client.send(\n\t\t\t\tnew GetParameterCommand({ Name, WithDecryption: true }),\n\t\t\t);\n\t\t\tif (Parameter?.Value === undefined) return null;\n\t\t\treturn { value: Parameter.Value, version: Parameter.Version ?? 1 };\n\t\t} catch (error) {\n\t\t\tif (error instanceof ParameterNotFound) return null;\n\t\t\tthrow error;\n\t\t}\n\t}\n}\n\nfunction parseHolder(value: string): LockHolder | null {\n\ttry {\n\t\treturn JSON.parse(value) as LockHolder;\n\t} catch {\n\t\treturn null;\n\t}\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- const require_sniffer = require("./sniffer-BjG-1mlD.cjs");
1
+ const require_sniffer = require("./sniffer-DeyYI9Iv.cjs");
2
2
  let _aws_sdk_client_ssm = require("@aws-sdk/client-ssm");
3
3
  //#region src/deploy/SSMStateStore.ts
4
4
  /**
@@ -150,4 +150,4 @@ function parseHolder(value) {
150
150
  //#endregion
151
151
  exports.SSMStateStore = SSMStateStore;
152
152
 
153
- //# sourceMappingURL=SSMStateStore-W-Ps22AQ.cjs.map
153
+ //# sourceMappingURL=SSMStateStore-C1JruCD9.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"SSMStateStore-W-Ps22AQ.cjs","names":["DocumentStateStore","SSMClient","PutParameterCommand","ParameterAlreadyExists","StateVersionConflict","StateLocked","DeleteParameterCommand","ParameterNotFound","GetParameterCommand"],"sources":["../src/deploy/SSMStateStore.ts"],"sourcesContent":["/**\n * AWS SSM Parameter Store state store.\n *\n * - `/gkm/<workspace>/<stage>/state`: the state, a SecureString.\n * - `/gkm/<workspace>/<stage>/lock`: the lock, created only if absent.\n * - `/gkm/<workspace>/<stage>/state.v1`: the v1 state, kept on migration.\n *\n * SSM has no conditional put, so a write checks the parameter's version\n * before it puts, and checks the version the put returns after: a put that\n * did not land exactly one version above the one it was based on raced\n * another writer, and raises `StateVersionConflict` rather than reporting\n * success. Both writes stay in the parameter's history.\n */\n\nimport {\n\tDeleteParameterCommand,\n\tGetParameterCommand,\n\tParameterAlreadyExists,\n\tParameterNotFound,\n\tPutParameterCommand,\n\tSSMClient,\n\ttype SSMClientConfig,\n} from '@aws-sdk/client-ssm';\nimport type { AwsRegion } from './StateProvider';\nimport {\n\tDocumentStateStore,\n\ttype LockHolder,\n\ttype RawState,\n\tStateLocked,\n\ttype StateVersion,\n\tStateVersionConflict,\n} from './StateStore';\n\nexport interface SSMStateStoreOptions {\n\tworkspaceName: string;\n\tregion?: AwsRegion;\n\t/** Uses the default credential chain when omitted. */\n\tprofile?: string;\n\tcredentials?: SSMClientConfig['credentials'];\n\t/** For the local AWS emulator. */\n\tendpoint?: string;\n}\n\nexport class SSMStateStore extends DocumentStateStore {\n\tconstructor(\n\t\treadonly workspaceName: string,\n\t\tprivate readonly client: SSMClient,\n\t) {\n\t\tsuper();\n\t}\n\n\tstatic create(options: SSMStateStoreOptions): SSMStateStore {\n\t\tconst config: SSMClientConfig = {\n\t\t\tregion: options.region,\n\t\t\tendpoint: options.endpoint,\n\t\t};\n\t\tif (options.profile) {\n\t\t\t// Required lazily: only a profile needs the credential providers.\n\t\t\tconst { fromIni } = require('@aws-sdk/credential-providers');\n\t\t\tconfig.credentials = fromIni({ profile: options.profile });\n\t\t} else if (options.credentials) {\n\t\t\tconfig.credentials = options.credentials;\n\t\t}\n\t\treturn new SSMStateStore(options.workspaceName, new SSMClient(config));\n\t}\n\n\tprivate name(stage: string, leaf: string): string {\n\t\treturn `/gkm/${this.workspaceName}/${stage}/${leaf}`;\n\t}\n\n\tprotected location(stage: string): string {\n\t\treturn `ssm:${this.name(stage, 'state')}`;\n\t}\n\n\tprotected async readRaw(stage: string): Promise<RawState | null> {\n\t\tconst parameter = await this.get(this.name(stage, 'state'));\n\t\tif (!parameter) return null;\n\t\treturn { body: parameter.value, version: String(parameter.version) };\n\t}\n\n\tprotected async writeRaw(\n\t\tstage: string,\n\t\tbody: string,\n\t\texpectedVersion: StateVersion | null,\n\t): Promise<StateVersion> {\n\t\tconst Name = this.name(stage, 'state');\n\t\tconst Description = `GKM deployment state for ${this.workspaceName}/${stage}`;\n\n\t\tif (expectedVersion === null) {\n\t\t\ttry {\n\t\t\t\tconst { Version } = await this.client.send(\n\t\t\t\t\tnew PutParameterCommand({\n\t\t\t\t\t\tName,\n\t\t\t\t\t\tValue: body,\n\t\t\t\t\t\tType: 'SecureString',\n\t\t\t\t\t\tDescription,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\treturn String(Version);\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof ParameterAlreadyExists) {\n\t\t\t\t\tconst current = await this.get(Name);\n\t\t\t\t\tthrow new StateVersionConflict(\n\t\t\t\t\t\tstage,\n\t\t\t\t\t\tnull,\n\t\t\t\t\t\tcurrent ? String(current.version) : null,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\n\t\tconst current = await this.get(Name);\n\t\tconst actual = current ? String(current.version) : null;\n\t\tif (actual !== expectedVersion) {\n\t\t\tthrow new StateVersionConflict(stage, expectedVersion, actual);\n\t\t}\n\n\t\tconst { Version } = await this.client.send(\n\t\t\tnew PutParameterCommand({\n\t\t\t\tName,\n\t\t\t\tValue: body,\n\t\t\t\tType: 'SecureString',\n\t\t\t\tOverwrite: true,\n\t\t\t\tDescription,\n\t\t\t}),\n\t\t);\n\t\tif (Version !== Number(expectedVersion) + 1) {\n\t\t\t// Another put landed between the check and this one. Ours is now\n\t\t\t// on top, but the caller that wrote the other believes its write\n\t\t\t// is current — its next write will conflict too, so neither is\n\t\t\t// lost silently.\n\t\t\tthrow new StateVersionConflict(\n\t\t\t\tstage,\n\t\t\t\texpectedVersion,\n\t\t\t\tString((Version ?? 1) - 1),\n\t\t\t);\n\t\t}\n\n\t\t// The version alone is not enough under an emulator, and need not be\n\t\t// on SSM either: two overwrites that raced can both be answered with\n\t\t// the same version, and both would report success. If the parameter is\n\t\t// still at the version this put returned, it has to hold this body —\n\t\t// otherwise the other put is the one that stuck.\n\t\tconst landed = await this.get(Name);\n\t\tif (landed && landed.version === Version && landed.value !== body) {\n\t\t\tthrow new StateVersionConflict(stage, expectedVersion, String(Version));\n\t\t}\n\t\treturn String(Version);\n\t}\n\n\tprotected async writeV1Backup(stage: string, body: string): Promise<void> {\n\t\ttry {\n\t\t\tawait this.client.send(\n\t\t\t\tnew PutParameterCommand({\n\t\t\t\t\tName: this.name(stage, 'state.v1'),\n\t\t\t\t\tValue: body,\n\t\t\t\t\tType: 'SecureString',\n\t\t\t\t\tDescription: `GKM v1 deployment state for ${this.workspaceName}/${stage}, kept on migration`,\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\t// The first migration's backup is the one worth keeping.\n\t\t\tif (!(error instanceof ParameterAlreadyExists)) throw error;\n\t\t}\n\t}\n\n\tprotected async createLock(stage: string, holder: LockHolder): Promise<void> {\n\t\tconst Name = this.name(stage, 'lock');\n\t\ttry {\n\t\t\tawait this.client.send(\n\t\t\t\tnew PutParameterCommand({\n\t\t\t\t\tName,\n\t\t\t\t\tValue: JSON.stringify(holder),\n\t\t\t\t\tType: 'String',\n\t\t\t\t\tDescription: `GKM deploy lock for ${this.workspaceName}/${stage}`,\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tif (error instanceof ParameterAlreadyExists) {\n\t\t\t\tthrow new StateLocked(stage, await this.readLock(stage), `ssm:${Name}`);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\n\t\t// A create-only put is atomic on SSM, so the lock is ours. Read it back\n\t\t// anyway: on a store where two racing creates can both succeed (the\n\t\t// local emulator does, a few times in a hundred) the later one has\n\t\t// overwritten the earlier — version 2, someone else's holder — and\n\t\t// taking the lock regardless would give the stage two deploys.\n\t\tconst created = await this.get(Name);\n\t\tconst recorded = created ? parseHolder(created.value) : null;\n\t\tif (created?.version !== 1 || recorded?.id !== holder.id) {\n\t\t\tthrow new StateLocked(stage, recorded, `ssm:${Name}`);\n\t\t}\n\t}\n\n\tprotected async readLock(stage: string): Promise<LockHolder | null> {\n\t\tconst parameter = await this.get(this.name(stage, 'lock'));\n\t\treturn parameter ? parseHolder(parameter.value) : null;\n\t}\n\n\tprotected async removeLock(\n\t\tstage: string,\n\t\tholder: LockHolder | null,\n\t): Promise<void> {\n\t\tif (holder) {\n\t\t\tconst current = await this.readLock(stage);\n\t\t\tif (current?.id !== holder.id) return;\n\t\t}\n\t\ttry {\n\t\t\tawait this.client.send(\n\t\t\t\tnew DeleteParameterCommand({ Name: this.name(stage, 'lock') }),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tif (!(error instanceof ParameterNotFound)) throw error;\n\t\t}\n\t}\n\n\tprivate async get(\n\t\tName: string,\n\t): Promise<{ value: string; version: number } | null> {\n\t\ttry {\n\t\t\tconst { Parameter } = await this.client.send(\n\t\t\t\tnew GetParameterCommand({ Name, WithDecryption: true }),\n\t\t\t);\n\t\t\tif (Parameter?.Value === undefined) return null;\n\t\t\treturn { value: Parameter.Value, version: Parameter.Version ?? 1 };\n\t\t} catch (error) {\n\t\t\tif (error instanceof ParameterNotFound) return null;\n\t\t\tthrow error;\n\t\t}\n\t}\n}\n\nfunction parseHolder(value: string): LockHolder | null {\n\ttry {\n\t\treturn JSON.parse(value) as LockHolder;\n\t} catch {\n\t\treturn null;\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"SSMStateStore-C1JruCD9.cjs","names":["DocumentStateStore","SSMClient","PutParameterCommand","ParameterAlreadyExists","StateVersionConflict","StateLocked","DeleteParameterCommand","ParameterNotFound","GetParameterCommand"],"sources":["../src/deploy/SSMStateStore.ts"],"sourcesContent":["/**\n * AWS SSM Parameter Store state store.\n *\n * - `/gkm/<workspace>/<stage>/state`: the state, a SecureString.\n * - `/gkm/<workspace>/<stage>/lock`: the lock, created only if absent.\n * - `/gkm/<workspace>/<stage>/state.v1`: the v1 state, kept on migration.\n *\n * SSM has no conditional put, so a write checks the parameter's version\n * before it puts, and checks the version the put returns after: a put that\n * did not land exactly one version above the one it was based on raced\n * another writer, and raises `StateVersionConflict` rather than reporting\n * success. Both writes stay in the parameter's history.\n */\n\nimport {\n\tDeleteParameterCommand,\n\tGetParameterCommand,\n\tParameterAlreadyExists,\n\tParameterNotFound,\n\tPutParameterCommand,\n\tSSMClient,\n\ttype SSMClientConfig,\n} from '@aws-sdk/client-ssm';\nimport type { AwsRegion } from './StateProvider';\nimport {\n\tDocumentStateStore,\n\ttype LockHolder,\n\ttype RawState,\n\tStateLocked,\n\ttype StateVersion,\n\tStateVersionConflict,\n} from './StateStore';\n\nexport interface SSMStateStoreOptions {\n\tworkspaceName: string;\n\tregion?: AwsRegion;\n\t/** Uses the default credential chain when omitted. */\n\tprofile?: string;\n\tcredentials?: SSMClientConfig['credentials'];\n\t/** For the local AWS emulator. */\n\tendpoint?: string;\n}\n\nexport class SSMStateStore extends DocumentStateStore {\n\tconstructor(\n\t\treadonly workspaceName: string,\n\t\tprivate readonly client: SSMClient,\n\t) {\n\t\tsuper();\n\t}\n\n\tstatic create(options: SSMStateStoreOptions): SSMStateStore {\n\t\tconst config: SSMClientConfig = {\n\t\t\tregion: options.region,\n\t\t\tendpoint: options.endpoint,\n\t\t};\n\t\tif (options.profile) {\n\t\t\t// Required lazily: only a profile needs the credential providers.\n\t\t\tconst { fromIni } = require('@aws-sdk/credential-providers');\n\t\t\tconfig.credentials = fromIni({ profile: options.profile });\n\t\t} else if (options.credentials) {\n\t\t\tconfig.credentials = options.credentials;\n\t\t}\n\t\treturn new SSMStateStore(options.workspaceName, new SSMClient(config));\n\t}\n\n\tprivate name(stage: string, leaf: string): string {\n\t\treturn `/gkm/${this.workspaceName}/${stage}/${leaf}`;\n\t}\n\n\tprotected location(stage: string): string {\n\t\treturn `ssm:${this.name(stage, 'state')}`;\n\t}\n\n\tprotected async readRaw(stage: string): Promise<RawState | null> {\n\t\tconst parameter = await this.get(this.name(stage, 'state'));\n\t\tif (!parameter) return null;\n\t\treturn { body: parameter.value, version: String(parameter.version) };\n\t}\n\n\tprotected async writeRaw(\n\t\tstage: string,\n\t\tbody: string,\n\t\texpectedVersion: StateVersion | null,\n\t): Promise<StateVersion> {\n\t\tconst Name = this.name(stage, 'state');\n\t\tconst Description = `GKM deployment state for ${this.workspaceName}/${stage}`;\n\n\t\tif (expectedVersion === null) {\n\t\t\ttry {\n\t\t\t\tconst { Version } = await this.client.send(\n\t\t\t\t\tnew PutParameterCommand({\n\t\t\t\t\t\tName,\n\t\t\t\t\t\tValue: body,\n\t\t\t\t\t\tType: 'SecureString',\n\t\t\t\t\t\tDescription,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\treturn String(Version);\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof ParameterAlreadyExists) {\n\t\t\t\t\tconst current = await this.get(Name);\n\t\t\t\t\tthrow new StateVersionConflict(\n\t\t\t\t\t\tstage,\n\t\t\t\t\t\tnull,\n\t\t\t\t\t\tcurrent ? String(current.version) : null,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\n\t\tconst current = await this.get(Name);\n\t\tconst actual = current ? String(current.version) : null;\n\t\tif (actual !== expectedVersion) {\n\t\t\tthrow new StateVersionConflict(stage, expectedVersion, actual);\n\t\t}\n\n\t\tconst { Version } = await this.client.send(\n\t\t\tnew PutParameterCommand({\n\t\t\t\tName,\n\t\t\t\tValue: body,\n\t\t\t\tType: 'SecureString',\n\t\t\t\tOverwrite: true,\n\t\t\t\tDescription,\n\t\t\t}),\n\t\t);\n\t\tif (Version !== Number(expectedVersion) + 1) {\n\t\t\t// Another put landed between the check and this one. Ours is now\n\t\t\t// on top, but the caller that wrote the other believes its write\n\t\t\t// is current — its next write will conflict too, so neither is\n\t\t\t// lost silently.\n\t\t\tthrow new StateVersionConflict(\n\t\t\t\tstage,\n\t\t\t\texpectedVersion,\n\t\t\t\tString((Version ?? 1) - 1),\n\t\t\t);\n\t\t}\n\n\t\t// The version alone is not enough under an emulator, and need not be\n\t\t// on SSM either: two overwrites that raced can both be answered with\n\t\t// the same version, and both would report success. If the parameter is\n\t\t// still at the version this put returned, it has to hold this body —\n\t\t// otherwise the other put is the one that stuck.\n\t\tconst landed = await this.get(Name);\n\t\tif (landed && landed.version === Version && landed.value !== body) {\n\t\t\tthrow new StateVersionConflict(stage, expectedVersion, String(Version));\n\t\t}\n\t\treturn String(Version);\n\t}\n\n\tprotected async writeV1Backup(stage: string, body: string): Promise<void> {\n\t\ttry {\n\t\t\tawait this.client.send(\n\t\t\t\tnew PutParameterCommand({\n\t\t\t\t\tName: this.name(stage, 'state.v1'),\n\t\t\t\t\tValue: body,\n\t\t\t\t\tType: 'SecureString',\n\t\t\t\t\tDescription: `GKM v1 deployment state for ${this.workspaceName}/${stage}, kept on migration`,\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\t// The first migration's backup is the one worth keeping.\n\t\t\tif (!(error instanceof ParameterAlreadyExists)) throw error;\n\t\t}\n\t}\n\n\tprotected async createLock(stage: string, holder: LockHolder): Promise<void> {\n\t\tconst Name = this.name(stage, 'lock');\n\t\ttry {\n\t\t\tawait this.client.send(\n\t\t\t\tnew PutParameterCommand({\n\t\t\t\t\tName,\n\t\t\t\t\tValue: JSON.stringify(holder),\n\t\t\t\t\tType: 'String',\n\t\t\t\t\tDescription: `GKM deploy lock for ${this.workspaceName}/${stage}`,\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tif (error instanceof ParameterAlreadyExists) {\n\t\t\t\tthrow new StateLocked(stage, await this.readLock(stage), `ssm:${Name}`);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\n\t\t// A create-only put is atomic on SSM, so the lock is ours. Read it back\n\t\t// anyway: on a store where two racing creates can both succeed (the\n\t\t// local emulator does, a few times in a hundred) the later one has\n\t\t// overwritten the earlier — version 2, someone else's holder — and\n\t\t// taking the lock regardless would give the stage two deploys.\n\t\tconst created = await this.get(Name);\n\t\tconst recorded = created ? parseHolder(created.value) : null;\n\t\tif (created?.version !== 1 || recorded?.id !== holder.id) {\n\t\t\tthrow new StateLocked(stage, recorded, `ssm:${Name}`);\n\t\t}\n\t}\n\n\tprotected async readLock(stage: string): Promise<LockHolder | null> {\n\t\tconst parameter = await this.get(this.name(stage, 'lock'));\n\t\treturn parameter ? parseHolder(parameter.value) : null;\n\t}\n\n\tprotected async removeLock(\n\t\tstage: string,\n\t\tholder: LockHolder | null,\n\t): Promise<void> {\n\t\tif (holder) {\n\t\t\tconst current = await this.readLock(stage);\n\t\t\tif (current?.id !== holder.id) return;\n\t\t}\n\t\ttry {\n\t\t\tawait this.client.send(\n\t\t\t\tnew DeleteParameterCommand({ Name: this.name(stage, 'lock') }),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tif (!(error instanceof ParameterNotFound)) throw error;\n\t\t}\n\t}\n\n\tprivate async get(\n\t\tName: string,\n\t): Promise<{ value: string; version: number } | null> {\n\t\ttry {\n\t\t\tconst { Parameter } = await this.client.send(\n\t\t\t\tnew GetParameterCommand({ Name, WithDecryption: true }),\n\t\t\t);\n\t\t\tif (Parameter?.Value === undefined) return null;\n\t\t\treturn { value: Parameter.Value, version: Parameter.Version ?? 1 };\n\t\t} catch (error) {\n\t\t\tif (error instanceof ParameterNotFound) return null;\n\t\t\tthrow error;\n\t\t}\n\t}\n}\n\nfunction parseHolder(value: string): LockHolder | null {\n\ttry {\n\t\treturn JSON.parse(value) as LockHolder;\n\t} catch {\n\t\treturn null;\n\t}\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  const require_discover = require("./discover-D1It5r_h.cjs");
2
- const require_EndpointGenerator = require("./EndpointGenerator-CP6sBvQV.cjs");
2
+ const require_EndpointGenerator = require("./EndpointGenerator-cKlnllIu.cjs");
3
3
  let node_path = require("node:path");
4
4
  let node_fs_promises = require("node:fs/promises");
5
5
  let _geekmidas_constructs_subscribers = require("@geekmidas/constructs/subscribers");
@@ -486,4 +486,4 @@ Object.defineProperty(exports, "readFakes", {
486
486
  }
487
487
  });
488
488
 
489
- //# sourceMappingURL=SubscriberGenerator-CajwOa5W.cjs.map
489
+ //# sourceMappingURL=SubscriberGenerator-CYriX5BV.cjs.map