@shipfox/api-runners 12.0.0 → 12.2.0

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 (116) hide show
  1. package/.turbo/turbo-build.log +4 -4
  2. package/CHANGELOG.md +30 -0
  3. package/dist/config.d.ts +3 -0
  4. package/dist/config.d.ts.map +1 -1
  5. package/dist/config.js +24 -2
  6. package/dist/config.js.map +1 -1
  7. package/dist/core/errors.d.ts +4 -0
  8. package/dist/core/errors.d.ts.map +1 -1
  9. package/dist/core/errors.js +6 -0
  10. package/dist/core/errors.js.map +1 -1
  11. package/dist/core/index.d.ts +1 -1
  12. package/dist/core/index.d.ts.map +1 -1
  13. package/dist/core/index.js +1 -1
  14. package/dist/core/index.js.map +1 -1
  15. package/dist/core/rate-limit.d.ts.map +1 -1
  16. package/dist/core/runner-control-sessions.d.ts.map +1 -1
  17. package/dist/core/runner-control-sessions.js +23 -9
  18. package/dist/core/runner-control-sessions.js.map +1 -1
  19. package/dist/core/runner-instances.d.ts +1 -0
  20. package/dist/core/runner-instances.d.ts.map +1 -1
  21. package/dist/core/runner-instances.js.map +1 -1
  22. package/dist/core/runner-labels.d.ts +11 -0
  23. package/dist/core/runner-labels.d.ts.map +1 -0
  24. package/dist/core/runner-labels.js +41 -0
  25. package/dist/core/runner-labels.js.map +1 -0
  26. package/dist/core/runner-sessions.d.ts.map +1 -1
  27. package/dist/core/runner-sessions.js +5 -1
  28. package/dist/core/runner-sessions.js.map +1 -1
  29. package/dist/db/db.d.ts +64 -64
  30. package/dist/db/ephemeral-registration-tokens.d.ts.map +1 -1
  31. package/dist/db/ephemeral-registration-tokens.js +12 -2
  32. package/dist/db/ephemeral-registration-tokens.js.map +1 -1
  33. package/dist/db/reservations.d.ts.map +1 -1
  34. package/dist/db/reservations.js +15 -5
  35. package/dist/db/reservations.js.map +1 -1
  36. package/dist/db/runner-instances.d.ts +1 -0
  37. package/dist/db/runner-instances.d.ts.map +1 -1
  38. package/dist/db/runner-instances.js +6 -4
  39. package/dist/db/runner-instances.js.map +1 -1
  40. package/dist/db/runner-sessions.d.ts.map +1 -1
  41. package/dist/db/runner-sessions.js +12 -1
  42. package/dist/db/runner-sessions.js.map +1 -1
  43. package/dist/db/schema/admin-command-results.d.ts +1 -1
  44. package/dist/db/schema/ephemeral-registration-tokens.d.ts +1 -1
  45. package/dist/db/schema/manual-registration-tokens.d.ts +1 -1
  46. package/dist/db/schema/outbox.d.ts +1 -1
  47. package/dist/db/schema/pending-job-executions.d.ts +3 -3
  48. package/dist/db/schema/provisioner-capability-snapshots.d.ts +3 -3
  49. package/dist/db/schema/provisioner-tokens.d.ts +2 -2
  50. package/dist/db/schema/rate-limits.d.ts +1 -1
  51. package/dist/db/schema/reservations.d.ts +3 -3
  52. package/dist/db/schema/runner-activation-tokens.d.ts +1 -1
  53. package/dist/db/schema/runner-control-sessions.d.ts +2 -2
  54. package/dist/db/schema/runner-instances.d.ts +4 -4
  55. package/dist/db/schema/runner-instances.d.ts.map +1 -1
  56. package/dist/db/schema/runner-instances.js +1 -0
  57. package/dist/db/schema/runner-instances.js.map +1 -1
  58. package/dist/db/schema/runner-sessions.d.ts +4 -4
  59. package/dist/db/schema/running-job-executions.d.ts +5 -5
  60. package/dist/metrics/index.d.ts +2 -1
  61. package/dist/metrics/index.d.ts.map +1 -1
  62. package/dist/metrics/index.js +1 -1
  63. package/dist/metrics/index.js.map +1 -1
  64. package/dist/metrics/instance.d.ts +13 -8
  65. package/dist/metrics/instance.d.ts.map +1 -1
  66. package/dist/metrics/instance.js +8 -0
  67. package/dist/metrics/instance.js.map +1 -1
  68. package/dist/presentation/dto/admin-provisioner-token.d.ts +1 -1
  69. package/dist/presentation/dto/admin-provisioner-token.d.ts.map +1 -1
  70. package/dist/presentation/dto/admin-runner-instances.d.ts +2 -2
  71. package/dist/presentation/dto/admin-runner-instances.d.ts.map +1 -1
  72. package/dist/presentation/routes/poll-demand.d.ts.map +1 -1
  73. package/dist/presentation/routes/poll-demand.js +12 -6
  74. package/dist/presentation/routes/poll-demand.js.map +1 -1
  75. package/dist/presentation/routes/rate-limit.d.ts.map +1 -1
  76. package/dist/presentation/routes/register.d.ts.map +1 -1
  77. package/dist/presentation/routes/register.js +9 -1
  78. package/dist/presentation/routes/register.js.map +1 -1
  79. package/dist/presentation/routes/report-runner-instances.d.ts.map +1 -1
  80. package/dist/presentation/routes/report-runner-instances.js +1 -0
  81. package/dist/presentation/routes/report-runner-instances.js.map +1 -1
  82. package/dist/tsconfig.test.tsbuildinfo +1 -1
  83. package/drizzle/0004_huge_skreet.sql +1 -0
  84. package/drizzle/meta/0004_snapshot.json +2194 -0
  85. package/drizzle/meta/_journal.json +7 -0
  86. package/package.json +9 -9
  87. package/src/config.test.ts +81 -0
  88. package/src/config.ts +39 -2
  89. package/src/core/errors.ts +9 -0
  90. package/src/core/index.ts +1 -0
  91. package/src/core/runner-control-sessions.test.ts +239 -0
  92. package/src/core/runner-control-sessions.ts +29 -10
  93. package/src/core/runner-instances.ts +1 -0
  94. package/src/core/runner-labels.test.ts +53 -0
  95. package/src/core/runner-labels.ts +51 -0
  96. package/src/core/runner-sessions.test.ts +34 -1
  97. package/src/core/runner-sessions.ts +8 -1
  98. package/src/db/ephemeral-registration-tokens.ts +15 -1
  99. package/src/db/reservations.test.ts +169 -0
  100. package/src/db/reservations.ts +42 -8
  101. package/src/db/runner-instances.test.ts +45 -1
  102. package/src/db/runner-instances.ts +7 -2
  103. package/src/db/runner-sessions.ts +14 -1
  104. package/src/db/schema/runner-instances.ts +3 -0
  105. package/src/metrics/index.ts +2 -0
  106. package/src/metrics/instance.ts +18 -0
  107. package/src/presentation/routes/poll-demand.test.ts +99 -3
  108. package/src/presentation/routes/poll-demand.ts +18 -6
  109. package/src/presentation/routes/register.test.ts +116 -2
  110. package/src/presentation/routes/register.ts +7 -0
  111. package/src/presentation/routes/report-runner-instances.test.ts +33 -0
  112. package/src/presentation/routes/report-runner-instances.ts +1 -0
  113. package/src/presentation/routes/runner-enrollment.test.ts +65 -5
  114. package/src/temporal/activities/maintenance-activities.test.ts +24 -31
  115. package/test/env.ts +1 -0
  116. package/tsconfig.build.tsbuildinfo +1 -1
@@ -5,14 +5,19 @@ import { ClientError, defineRoute } from '@shipfox/node-fastify';
5
5
  import { logger } from '@shipfox/node-opentelemetry';
6
6
  import { config } from '#config.js';
7
7
  import { pollDemand, releaseReservationGrants } from '#core/demand.js';
8
+ import { sanitizeRunnerLabels } from '#core/runner-labels.js';
8
9
  import { publishWorkspaceProvisionerCapabilitySnapshot } from '#db/provisioner-capability-snapshots.js';
9
10
  import { listQueuedDemandWorkspaceIds, pollInstallationDemandAndReserve } from '#db/reservations.js';
10
11
  import { toPollDemandResponseDto } from '#presentation/dto/index.js';
11
12
  const TERMINATE_INTENT_LIMIT = 1000;
12
- function toReservationTemplates(templates) {
13
+ function toReservationTemplates(templates, scope) {
13
14
  return templates.map((template)=>({
14
15
  templateKey: template.template_key,
15
- labels: template.labels,
16
+ labels: sanitizeRunnerLabels(template.labels, {
17
+ scope,
18
+ logLevel: 'debug',
19
+ source: 'provisioner template advertisement'
20
+ }),
16
21
  availableSlots: template.available_slots,
17
22
  starting: template.starting,
18
23
  running: template.running
@@ -55,6 +60,7 @@ export function createPollDemandRoute(options = {}) {
55
60
  }
56
61
  });
57
62
  const provisionerContext = requireProvisionerContext(request);
63
+ const ttlSeconds = Math.min(request.body.reservation_ttl_seconds ?? config.RESERVATION_TTL_SECONDS, config.RESERVATION_TTL_MAX_SECONDS);
58
64
  if (provisionerContext.scope === 'installation') {
59
65
  if (!options.installationProvisioning) {
60
66
  throw new ClientError('Installation provisioner credentials are not accepted on this host', 'forbidden', {
@@ -63,11 +69,11 @@ export function createPollDemandRoute(options = {}) {
63
69
  }
64
70
  const candidateWorkspaceIds = await listQueuedDemandWorkspaceIds();
65
71
  const eligibleWorkspaceIds = await options.installationProvisioning.policy.filterEligibleWorkspaceIds(candidateWorkspaceIds);
66
- const templates = toReservationTemplates(request.body.templates);
72
+ const templates = toReservationTemplates(request.body.templates, 'installation');
67
73
  const result = await pollInstallationDemandAndReserve({
68
74
  provisionerId: provisionerContext.provisionerTokenId,
69
75
  maxReservations: request.body.max_reservations,
70
- ttlSeconds: config.RESERVATION_TTL_SECONDS,
76
+ ttlSeconds,
71
77
  templates,
72
78
  capabilityWindowSeconds: config.PROVISIONER_ACTIVE_WINDOW_SECONDS,
73
79
  eligibleWorkspaceIds,
@@ -82,7 +88,7 @@ export function createPollDemandRoute(options = {}) {
82
88
  });
83
89
  }
84
90
  const { provisionerTokenId, workspaceId } = requireWorkspaceProvisionerContext(request);
85
- const templates = toReservationTemplates(request.body.templates);
91
+ const templates = toReservationTemplates(request.body.templates, 'workspace');
86
92
  await publishWorkspaceProvisionerCapabilitySnapshot({
87
93
  workspaceId,
88
94
  provisionerId: provisionerTokenId,
@@ -93,7 +99,7 @@ export function createPollDemandRoute(options = {}) {
93
99
  provisionerId: provisionerTokenId,
94
100
  maxReservations: request.body.max_reservations,
95
101
  waitSeconds: request.body.wait_seconds,
96
- ttlSeconds: config.RESERVATION_TTL_SECONDS,
102
+ ttlSeconds,
97
103
  terminateIntentLimit: TERMINATE_INTENT_LIMIT,
98
104
  templates,
99
105
  signal: abortController.signal
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/presentation/routes/poll-demand.ts"],"sourcesContent":["import {\n requireProvisionerContext,\n requireWorkspaceProvisionerContext,\n} from '@shipfox/api-auth-context';\nimport type {PollDemandTemplateDto} from '@shipfox/api-runners-dto';\nimport {pollDemandBodySchema, pollDemandResponseSchema} from '@shipfox/api-runners-dto';\nimport {reportError} from '@shipfox/node-error-monitoring';\nimport {ClientError, defineRoute} from '@shipfox/node-fastify';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport {config} from '#config.js';\nimport {pollDemand, releaseReservationGrants} from '#core/demand.js';\nimport {publishWorkspaceProvisionerCapabilitySnapshot} from '#db/provisioner-capability-snapshots.js';\nimport {\n listQueuedDemandWorkspaceIds,\n pollInstallationDemandAndReserve,\n type ReservationGrant,\n type ReservationTemplate,\n} from '#db/reservations.js';\nimport type {CreateRunnersModuleOptions} from '#installation-provisioning.js';\nimport {toPollDemandResponseDto} from '#presentation/dto/index.js';\n\nconst TERMINATE_INTENT_LIMIT = 1000;\n\nfunction toReservationTemplates(templates: PollDemandTemplateDto[]): ReservationTemplate[] {\n return templates.map((template) => ({\n templateKey: template.template_key,\n labels: template.labels,\n availableSlots: template.available_slots,\n starting: template.starting,\n running: template.running,\n }));\n}\n\nexport function createPollDemandRoute(options: CreateRunnersModuleOptions = {}) {\n return defineRoute({\n method: 'POST',\n path: '/demand/poll',\n description: 'Poll aggregate runner demand and reserve capacity for a provisioner',\n schema: {\n body: pollDemandBodySchema,\n response: {\n 200: pollDemandResponseSchema,\n },\n },\n handler: async (request, reply) => {\n const abortController = new AbortController();\n let responseFinished = false;\n let responseReservations: ReservationGrant[] = [];\n reply.raw.on('finish', () => {\n responseFinished = true;\n });\n reply.raw.on('close', () => {\n if (!responseFinished) {\n abortController.abort();\n void releaseReservationGrants(responseReservations).catch((error) => {\n logger().error(\n {err: error, reservationCount: responseReservations.length},\n 'Failed to release reservations after provisioner disconnect',\n );\n reportError(error, {\n boundary: 'runners.cleanup',\n operation: 'release-disconnected-reservations',\n extra: {reservationCount: responseReservations.length},\n });\n });\n }\n });\n const provisionerContext = requireProvisionerContext(request);\n if (provisionerContext.scope === 'installation') {\n if (!options.installationProvisioning) {\n throw new ClientError(\n 'Installation provisioner credentials are not accepted on this host',\n 'forbidden',\n {\n status: 403,\n },\n );\n }\n const candidateWorkspaceIds = await listQueuedDemandWorkspaceIds();\n const eligibleWorkspaceIds =\n await options.installationProvisioning.policy.filterEligibleWorkspaceIds(\n candidateWorkspaceIds,\n );\n const templates = toReservationTemplates(request.body.templates);\n const result = await pollInstallationDemandAndReserve({\n provisionerId: provisionerContext.provisionerTokenId,\n maxReservations: request.body.max_reservations,\n ttlSeconds: config.RESERVATION_TTL_SECONDS,\n templates,\n capabilityWindowSeconds: config.PROVISIONER_ACTIVE_WINDOW_SECONDS,\n eligibleWorkspaceIds,\n signal: abortController.signal,\n onReservations: (reservations) => {\n responseReservations.push(...reservations);\n },\n });\n return toPollDemandResponseDto({\n ...result,\n terminateRunnerInstanceIds: [],\n });\n }\n const {provisionerTokenId, workspaceId} = requireWorkspaceProvisionerContext(request);\n\n const templates = toReservationTemplates(request.body.templates);\n await publishWorkspaceProvisionerCapabilitySnapshot({\n workspaceId,\n provisionerId: provisionerTokenId,\n templates,\n });\n\n const result = await pollDemand({\n workspaceId,\n provisionerId: provisionerTokenId,\n maxReservations: request.body.max_reservations,\n waitSeconds: request.body.wait_seconds,\n ttlSeconds: config.RESERVATION_TTL_SECONDS,\n terminateIntentLimit: TERMINATE_INTENT_LIMIT,\n templates,\n signal: abortController.signal,\n });\n responseReservations = result.reservations;\n\n return toPollDemandResponseDto(result);\n },\n });\n}\n\nexport const pollDemandRoute = createPollDemandRoute();\n"],"names":["requireProvisionerContext","requireWorkspaceProvisionerContext","pollDemandBodySchema","pollDemandResponseSchema","reportError","ClientError","defineRoute","logger","config","pollDemand","releaseReservationGrants","publishWorkspaceProvisionerCapabilitySnapshot","listQueuedDemandWorkspaceIds","pollInstallationDemandAndReserve","toPollDemandResponseDto","TERMINATE_INTENT_LIMIT","toReservationTemplates","templates","map","template","templateKey","template_key","labels","availableSlots","available_slots","starting","running","createPollDemandRoute","options","method","path","description","schema","body","response","handler","request","reply","abortController","AbortController","responseFinished","responseReservations","raw","on","abort","catch","error","err","reservationCount","length","boundary","operation","extra","provisionerContext","scope","installationProvisioning","status","candidateWorkspaceIds","eligibleWorkspaceIds","policy","filterEligibleWorkspaceIds","result","provisionerId","provisionerTokenId","maxReservations","max_reservations","ttlSeconds","RESERVATION_TTL_SECONDS","capabilityWindowSeconds","PROVISIONER_ACTIVE_WINDOW_SECONDS","signal","onReservations","reservations","push","terminateRunnerInstanceIds","workspaceId","waitSeconds","wait_seconds","terminateIntentLimit","pollDemandRoute"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/presentation/routes/poll-demand.ts"],"sourcesContent":["import {\n requireProvisionerContext,\n requireWorkspaceProvisionerContext,\n} from '@shipfox/api-auth-context';\nimport type {PollDemandTemplateDto} from '@shipfox/api-runners-dto';\nimport {pollDemandBodySchema, pollDemandResponseSchema} from '@shipfox/api-runners-dto';\nimport {reportError} from '@shipfox/node-error-monitoring';\nimport {ClientError, defineRoute} from '@shipfox/node-fastify';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport {config} from '#config.js';\nimport {pollDemand, releaseReservationGrants} from '#core/demand.js';\nimport {sanitizeRunnerLabels} from '#core/runner-labels.js';\nimport {publishWorkspaceProvisionerCapabilitySnapshot} from '#db/provisioner-capability-snapshots.js';\nimport {\n listQueuedDemandWorkspaceIds,\n pollInstallationDemandAndReserve,\n type ReservationGrant,\n type ReservationTemplate,\n} from '#db/reservations.js';\nimport type {CreateRunnersModuleOptions} from '#installation-provisioning.js';\nimport {toPollDemandResponseDto} from '#presentation/dto/index.js';\n\nconst TERMINATE_INTENT_LIMIT = 1000;\n\nfunction toReservationTemplates(\n templates: PollDemandTemplateDto[],\n scope: 'installation' | 'workspace',\n): ReservationTemplate[] {\n return templates.map((template) => ({\n templateKey: template.template_key,\n labels: sanitizeRunnerLabels(template.labels, {\n scope,\n logLevel: 'debug',\n source: 'provisioner template advertisement',\n }),\n availableSlots: template.available_slots,\n starting: template.starting,\n running: template.running,\n }));\n}\n\nexport function createPollDemandRoute(options: CreateRunnersModuleOptions = {}) {\n return defineRoute({\n method: 'POST',\n path: '/demand/poll',\n description: 'Poll aggregate runner demand and reserve capacity for a provisioner',\n schema: {\n body: pollDemandBodySchema,\n response: {\n 200: pollDemandResponseSchema,\n },\n },\n handler: async (request, reply) => {\n const abortController = new AbortController();\n let responseFinished = false;\n let responseReservations: ReservationGrant[] = [];\n reply.raw.on('finish', () => {\n responseFinished = true;\n });\n reply.raw.on('close', () => {\n if (!responseFinished) {\n abortController.abort();\n void releaseReservationGrants(responseReservations).catch((error) => {\n logger().error(\n {err: error, reservationCount: responseReservations.length},\n 'Failed to release reservations after provisioner disconnect',\n );\n reportError(error, {\n boundary: 'runners.cleanup',\n operation: 'release-disconnected-reservations',\n extra: {reservationCount: responseReservations.length},\n });\n });\n }\n });\n const provisionerContext = requireProvisionerContext(request);\n const ttlSeconds = Math.min(\n request.body.reservation_ttl_seconds ?? config.RESERVATION_TTL_SECONDS,\n config.RESERVATION_TTL_MAX_SECONDS,\n );\n if (provisionerContext.scope === 'installation') {\n if (!options.installationProvisioning) {\n throw new ClientError(\n 'Installation provisioner credentials are not accepted on this host',\n 'forbidden',\n {\n status: 403,\n },\n );\n }\n const candidateWorkspaceIds = await listQueuedDemandWorkspaceIds();\n const eligibleWorkspaceIds =\n await options.installationProvisioning.policy.filterEligibleWorkspaceIds(\n candidateWorkspaceIds,\n );\n const templates = toReservationTemplates(request.body.templates, 'installation');\n const result = await pollInstallationDemandAndReserve({\n provisionerId: provisionerContext.provisionerTokenId,\n maxReservations: request.body.max_reservations,\n ttlSeconds,\n templates,\n capabilityWindowSeconds: config.PROVISIONER_ACTIVE_WINDOW_SECONDS,\n eligibleWorkspaceIds,\n signal: abortController.signal,\n onReservations: (reservations) => {\n responseReservations.push(...reservations);\n },\n });\n return toPollDemandResponseDto({\n ...result,\n terminateRunnerInstanceIds: [],\n });\n }\n const {provisionerTokenId, workspaceId} = requireWorkspaceProvisionerContext(request);\n\n const templates = toReservationTemplates(request.body.templates, 'workspace');\n await publishWorkspaceProvisionerCapabilitySnapshot({\n workspaceId,\n provisionerId: provisionerTokenId,\n templates,\n });\n\n const result = await pollDemand({\n workspaceId,\n provisionerId: provisionerTokenId,\n maxReservations: request.body.max_reservations,\n waitSeconds: request.body.wait_seconds,\n ttlSeconds,\n terminateIntentLimit: TERMINATE_INTENT_LIMIT,\n templates,\n signal: abortController.signal,\n });\n responseReservations = result.reservations;\n\n return toPollDemandResponseDto(result);\n },\n });\n}\n\nexport const pollDemandRoute = createPollDemandRoute();\n"],"names":["requireProvisionerContext","requireWorkspaceProvisionerContext","pollDemandBodySchema","pollDemandResponseSchema","reportError","ClientError","defineRoute","logger","config","pollDemand","releaseReservationGrants","sanitizeRunnerLabels","publishWorkspaceProvisionerCapabilitySnapshot","listQueuedDemandWorkspaceIds","pollInstallationDemandAndReserve","toPollDemandResponseDto","TERMINATE_INTENT_LIMIT","toReservationTemplates","templates","scope","map","template","templateKey","template_key","labels","logLevel","source","availableSlots","available_slots","starting","running","createPollDemandRoute","options","method","path","description","schema","body","response","handler","request","reply","abortController","AbortController","responseFinished","responseReservations","raw","on","abort","catch","error","err","reservationCount","length","boundary","operation","extra","provisionerContext","ttlSeconds","Math","min","reservation_ttl_seconds","RESERVATION_TTL_SECONDS","RESERVATION_TTL_MAX_SECONDS","installationProvisioning","status","candidateWorkspaceIds","eligibleWorkspaceIds","policy","filterEligibleWorkspaceIds","result","provisionerId","provisionerTokenId","maxReservations","max_reservations","capabilityWindowSeconds","PROVISIONER_ACTIVE_WINDOW_SECONDS","signal","onReservations","reservations","push","terminateRunnerInstanceIds","workspaceId","waitSeconds","wait_seconds","terminateIntentLimit","pollDemandRoute"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/rate-limit.ts"],"names":[],"mappings":"AACA,OAAO,EAAc,KAAK,YAAY,EAAE,KAAK,cAAc,EAAC,MAAM,uBAAuB,CAAC;AAoD1F,wBAAgB,wCAAwC,KACxC,SAAS,cAAc,EAAE,OAAO,YAAY,KAAG,OAAO,CAAC,IAAI,CAAC,CAY3E;AAED,wBAAgB,0CAA0C,KAC1C,SAAS,cAAc,EAAE,OAAO,YAAY,KAAG,OAAO,CAAC,IAAI,CAAC,CAc3E"}
1
+ {"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/rate-limit.ts"],"names":[],"mappings":"AACA,OAAO,EAAc,KAAK,YAAY,EAAE,KAAK,cAAc,EAAC,MAAM,uBAAuB,CAAC;AAoD1F,wBAAgB,wCAAwC,cAC/B,cAAc,SAAS,YAAY,KAAG,OAAO,CAAC,IAAI,CAAC,CAY3E;AAED,wBAAgB,0CAA0C,cACjC,cAAc,SAAS,YAAY,KAAG,OAAO,CAAC,IAAI,CAAC,CAc3E"}
@@ -1 +1 @@
1
- {"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/register.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,oCAAoC,CAAC;AAY9E,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,qBAAqB,mDAkE9D"}
1
+ {"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/register.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,oCAAoC,CAAC;AAa9E,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,qBAAqB,mDAwE9D"}
@@ -1,6 +1,6 @@
1
1
  import { registerRunnerBodySchema, registerRunnerResponseSchema } from '@shipfox/api-runners-dto';
2
2
  import { ClientError, defineRoute } from '@shipfox/node-fastify';
3
- import { EmptyRunnerLabelsError, RegistrationTokenConsumedError, RegistrationTokenExpiredError } from '#core/errors.js';
3
+ import { EmptyRunnerLabelsError, RegistrationTokenConsumedError, RegistrationTokenExpiredError, RunnerLabelsReservedError } from '#core/errors.js';
4
4
  import { registerRunnerSession } from '#core/runner-sessions.js';
5
5
  import { getRunnerContext } from '#presentation/auth/index.js';
6
6
  import { createEphemeralRegisterRateLimitPreHandler } from './rate-limit.js';
@@ -17,6 +17,14 @@ export function createRegisterRoute(auth) {
17
17
  },
18
18
  preHandler: createEphemeralRegisterRateLimitPreHandler(),
19
19
  errorHandler: (error, request)=>{
20
+ if (error instanceof RunnerLabelsReservedError) {
21
+ throw new ClientError(error.message, 'runner-labels-reserved', {
22
+ details: {
23
+ labels: error.labels
24
+ },
25
+ status: 400
26
+ });
27
+ }
20
28
  if (error instanceof EmptyRunnerLabelsError) {
21
29
  throw new ClientError(error.message, 'empty-runner-labels', {
22
30
  status: 400
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/presentation/routes/register.ts"],"sourcesContent":["import type {AuthInterModuleClient} from '@shipfox/api-auth-dto/inter-module';\nimport {registerRunnerBodySchema, registerRunnerResponseSchema} from '@shipfox/api-runners-dto';\nimport {ClientError, defineRoute} from '@shipfox/node-fastify';\nimport {\n EmptyRunnerLabelsError,\n RegistrationTokenConsumedError,\n RegistrationTokenExpiredError,\n} from '#core/errors.js';\nimport {registerRunnerSession} from '#core/runner-sessions.js';\nimport {getRunnerContext} from '#presentation/auth/index.js';\nimport {createEphemeralRegisterRateLimitPreHandler} from './rate-limit.js';\n\nexport function createRegisterRoute(auth: AuthInterModuleClient) {\n return defineRoute({\n method: 'POST',\n path: '/register',\n description: 'Exchange a runner registration token for a runner session token',\n schema: {\n body: registerRunnerBodySchema,\n response: {\n 200: registerRunnerResponseSchema,\n },\n },\n preHandler: createEphemeralRegisterRateLimitPreHandler(),\n errorHandler: (error, request) => {\n if (error instanceof EmptyRunnerLabelsError) {\n throw new ClientError(error.message, 'empty-runner-labels', {status: 400});\n }\n if (error instanceof RegistrationTokenConsumedError) {\n const runner = getRunnerContext(request);\n if (runner.kind === 'ephemeral') {\n request.log.warn(\n {\n ephemeralTokenId: error.ephemeralTokenId,\n provisionerId: runner.provisionerId,\n },\n 'Ephemeral registration token reuse rejected',\n );\n }\n throw new ClientError(\n 'Registration token has already been consumed',\n 'registration-token-consumed',\n {\n status: 409,\n },\n );\n }\n if (error instanceof RegistrationTokenExpiredError) {\n throw new ClientError('Registration token has expired', 'registration-token-expired', {\n status: 401,\n });\n }\n throw error;\n },\n handler: async (request) => {\n const runner = getRunnerContext(request);\n const result = await registerRunnerSession({\n auth,\n credential:\n runner.kind === 'manual'\n ? {\n kind: 'manual',\n registrationTokenId: runner.registrationTokenId,\n workspaceId: runner.workspaceId,\n }\n : runner,\n labels: request.body.labels,\n toolCapabilities: request.body.capabilities ?? null,\n });\n\n return {\n session_token: result.sessionToken,\n session_id: result.session.id,\n mode: result.mode,\n max_claims: result.maxClaims,\n };\n },\n });\n}\n"],"names":["registerRunnerBodySchema","registerRunnerResponseSchema","ClientError","defineRoute","EmptyRunnerLabelsError","RegistrationTokenConsumedError","RegistrationTokenExpiredError","registerRunnerSession","getRunnerContext","createEphemeralRegisterRateLimitPreHandler","createRegisterRoute","auth","method","path","description","schema","body","response","preHandler","errorHandler","error","request","message","status","runner","kind","log","warn","ephemeralTokenId","provisionerId","handler","result","credential","registrationTokenId","workspaceId","labels","toolCapabilities","capabilities","session_token","sessionToken","session_id","session","id","mode","max_claims","maxClaims"],"mappings":"AACA,SAAQA,wBAAwB,EAAEC,4BAA4B,QAAO,2BAA2B;AAChG,SAAQC,WAAW,EAAEC,WAAW,QAAO,wBAAwB;AAC/D,SACEC,sBAAsB,EACtBC,8BAA8B,EAC9BC,6BAA6B,QACxB,kBAAkB;AACzB,SAAQC,qBAAqB,QAAO,2BAA2B;AAC/D,SAAQC,gBAAgB,QAAO,8BAA8B;AAC7D,SAAQC,0CAA0C,QAAO,kBAAkB;AAE3E,OAAO,SAASC,oBAAoBC,IAA2B;IAC7D,OAAOR,YAAY;QACjBS,QAAQ;QACRC,MAAM;QACNC,aAAa;QACbC,QAAQ;YACNC,MAAMhB;YACNiB,UAAU;gBACR,KAAKhB;YACP;QACF;QACAiB,YAAYT;QACZU,cAAc,CAACC,OAAOC;YACpB,IAAID,iBAAiBhB,wBAAwB;gBAC3C,MAAM,IAAIF,YAAYkB,MAAME,OAAO,EAAE,uBAAuB;oBAACC,QAAQ;gBAAG;YAC1E;YACA,IAAIH,iBAAiBf,gCAAgC;gBACnD,MAAMmB,SAAShB,iBAAiBa;gBAChC,IAAIG,OAAOC,IAAI,KAAK,aAAa;oBAC/BJ,QAAQK,GAAG,CAACC,IAAI,CACd;wBACEC,kBAAkBR,MAAMQ,gBAAgB;wBACxCC,eAAeL,OAAOK,aAAa;oBACrC,GACA;gBAEJ;gBACA,MAAM,IAAI3B,YACR,gDACA,+BACA;oBACEqB,QAAQ;gBACV;YAEJ;YACA,IAAIH,iBAAiBd,+BAA+B;gBAClD,MAAM,IAAIJ,YAAY,kCAAkC,8BAA8B;oBACpFqB,QAAQ;gBACV;YACF;YACA,MAAMH;QACR;QACAU,SAAS,OAAOT;YACd,MAAMG,SAAShB,iBAAiBa;YAChC,MAAMU,SAAS,MAAMxB,sBAAsB;gBACzCI;gBACAqB,YACER,OAAOC,IAAI,KAAK,WACZ;oBACEA,MAAM;oBACNQ,qBAAqBT,OAAOS,mBAAmB;oBAC/CC,aAAaV,OAAOU,WAAW;gBACjC,IACAV;gBACNW,QAAQd,QAAQL,IAAI,CAACmB,MAAM;gBAC3BC,kBAAkBf,QAAQL,IAAI,CAACqB,YAAY,IAAI;YACjD;YAEA,OAAO;gBACLC,eAAeP,OAAOQ,YAAY;gBAClCC,YAAYT,OAAOU,OAAO,CAACC,EAAE;gBAC7BC,MAAMZ,OAAOY,IAAI;gBACjBC,YAAYb,OAAOc,SAAS;YAC9B;QACF;IACF;AACF"}
1
+ {"version":3,"sources":["../../../src/presentation/routes/register.ts"],"sourcesContent":["import type {AuthInterModuleClient} from '@shipfox/api-auth-dto/inter-module';\nimport {registerRunnerBodySchema, registerRunnerResponseSchema} from '@shipfox/api-runners-dto';\nimport {ClientError, defineRoute} from '@shipfox/node-fastify';\nimport {\n EmptyRunnerLabelsError,\n RegistrationTokenConsumedError,\n RegistrationTokenExpiredError,\n RunnerLabelsReservedError,\n} from '#core/errors.js';\nimport {registerRunnerSession} from '#core/runner-sessions.js';\nimport {getRunnerContext} from '#presentation/auth/index.js';\nimport {createEphemeralRegisterRateLimitPreHandler} from './rate-limit.js';\n\nexport function createRegisterRoute(auth: AuthInterModuleClient) {\n return defineRoute({\n method: 'POST',\n path: '/register',\n description: 'Exchange a runner registration token for a runner session token',\n schema: {\n body: registerRunnerBodySchema,\n response: {\n 200: registerRunnerResponseSchema,\n },\n },\n preHandler: createEphemeralRegisterRateLimitPreHandler(),\n errorHandler: (error, request) => {\n if (error instanceof RunnerLabelsReservedError) {\n throw new ClientError(error.message, 'runner-labels-reserved', {\n details: {labels: error.labels},\n status: 400,\n });\n }\n if (error instanceof EmptyRunnerLabelsError) {\n throw new ClientError(error.message, 'empty-runner-labels', {status: 400});\n }\n if (error instanceof RegistrationTokenConsumedError) {\n const runner = getRunnerContext(request);\n if (runner.kind === 'ephemeral') {\n request.log.warn(\n {\n ephemeralTokenId: error.ephemeralTokenId,\n provisionerId: runner.provisionerId,\n },\n 'Ephemeral registration token reuse rejected',\n );\n }\n throw new ClientError(\n 'Registration token has already been consumed',\n 'registration-token-consumed',\n {\n status: 409,\n },\n );\n }\n if (error instanceof RegistrationTokenExpiredError) {\n throw new ClientError('Registration token has expired', 'registration-token-expired', {\n status: 401,\n });\n }\n throw error;\n },\n handler: async (request) => {\n const runner = getRunnerContext(request);\n const result = await registerRunnerSession({\n auth,\n credential:\n runner.kind === 'manual'\n ? {\n kind: 'manual',\n registrationTokenId: runner.registrationTokenId,\n workspaceId: runner.workspaceId,\n }\n : runner,\n labels: request.body.labels,\n toolCapabilities: request.body.capabilities ?? null,\n });\n\n return {\n session_token: result.sessionToken,\n session_id: result.session.id,\n mode: result.mode,\n max_claims: result.maxClaims,\n };\n },\n });\n}\n"],"names":["registerRunnerBodySchema","registerRunnerResponseSchema","ClientError","defineRoute","EmptyRunnerLabelsError","RegistrationTokenConsumedError","RegistrationTokenExpiredError","RunnerLabelsReservedError","registerRunnerSession","getRunnerContext","createEphemeralRegisterRateLimitPreHandler","createRegisterRoute","auth","method","path","description","schema","body","response","preHandler","errorHandler","error","request","message","details","labels","status","runner","kind","log","warn","ephemeralTokenId","provisionerId","handler","result","credential","registrationTokenId","workspaceId","toolCapabilities","capabilities","session_token","sessionToken","session_id","session","id","mode","max_claims","maxClaims"],"mappings":"AACA,SAAQA,wBAAwB,EAAEC,4BAA4B,QAAO,2BAA2B;AAChG,SAAQC,WAAW,EAAEC,WAAW,QAAO,wBAAwB;AAC/D,SACEC,sBAAsB,EACtBC,8BAA8B,EAC9BC,6BAA6B,EAC7BC,yBAAyB,QACpB,kBAAkB;AACzB,SAAQC,qBAAqB,QAAO,2BAA2B;AAC/D,SAAQC,gBAAgB,QAAO,8BAA8B;AAC7D,SAAQC,0CAA0C,QAAO,kBAAkB;AAE3E,OAAO,SAASC,oBAAoBC,IAA2B;IAC7D,OAAOT,YAAY;QACjBU,QAAQ;QACRC,MAAM;QACNC,aAAa;QACbC,QAAQ;YACNC,MAAMjB;YACNkB,UAAU;gBACR,KAAKjB;YACP;QACF;QACAkB,YAAYT;QACZU,cAAc,CAACC,OAAOC;YACpB,IAAID,iBAAiBd,2BAA2B;gBAC9C,MAAM,IAAIL,YAAYmB,MAAME,OAAO,EAAE,0BAA0B;oBAC7DC,SAAS;wBAACC,QAAQJ,MAAMI,MAAM;oBAAA;oBAC9BC,QAAQ;gBACV;YACF;YACA,IAAIL,iBAAiBjB,wBAAwB;gBAC3C,MAAM,IAAIF,YAAYmB,MAAME,OAAO,EAAE,uBAAuB;oBAACG,QAAQ;gBAAG;YAC1E;YACA,IAAIL,iBAAiBhB,gCAAgC;gBACnD,MAAMsB,SAASlB,iBAAiBa;gBAChC,IAAIK,OAAOC,IAAI,KAAK,aAAa;oBAC/BN,QAAQO,GAAG,CAACC,IAAI,CACd;wBACEC,kBAAkBV,MAAMU,gBAAgB;wBACxCC,eAAeL,OAAOK,aAAa;oBACrC,GACA;gBAEJ;gBACA,MAAM,IAAI9B,YACR,gDACA,+BACA;oBACEwB,QAAQ;gBACV;YAEJ;YACA,IAAIL,iBAAiBf,+BAA+B;gBAClD,MAAM,IAAIJ,YAAY,kCAAkC,8BAA8B;oBACpFwB,QAAQ;gBACV;YACF;YACA,MAAML;QACR;QACAY,SAAS,OAAOX;YACd,MAAMK,SAASlB,iBAAiBa;YAChC,MAAMY,SAAS,MAAM1B,sBAAsB;gBACzCI;gBACAuB,YACER,OAAOC,IAAI,KAAK,WACZ;oBACEA,MAAM;oBACNQ,qBAAqBT,OAAOS,mBAAmB;oBAC/CC,aAAaV,OAAOU,WAAW;gBACjC,IACAV;gBACNF,QAAQH,QAAQL,IAAI,CAACQ,MAAM;gBAC3Ba,kBAAkBhB,QAAQL,IAAI,CAACsB,YAAY,IAAI;YACjD;YAEA,OAAO;gBACLC,eAAeN,OAAOO,YAAY;gBAClCC,YAAYR,OAAOS,OAAO,CAACC,EAAE;gBAC7BC,MAAMX,OAAOW,IAAI;gBACjBC,YAAYZ,OAAOa,SAAS;YAC9B;QACF;IACF;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"report-runner-instances.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/report-runner-instances.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,0BAA0B,iDA8BrC,CAAC"}
1
+ {"version":3,"file":"report-runner-instances.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/report-runner-instances.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,0BAA0B,iDA+BrC,CAAC"}
@@ -16,6 +16,7 @@ export const reportRunnerInstancesRoute = defineRoute({
16
16
  handler: async (request)=>{
17
17
  const context = requireProvisionerContext(request);
18
18
  const result = await reportRunnerInstances({
19
+ scope: context.scope,
19
20
  workspaceId: context.scope === 'workspace' ? context.workspaceId : null,
20
21
  provisionerId: context.provisionerTokenId,
21
22
  events: request.body.events.map((event)=>({
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/presentation/routes/report-runner-instances.ts"],"sourcesContent":["import {requireProvisionerContext} from '@shipfox/api-auth-context';\nimport {\n reportRunnerInstancesBodySchema,\n reportRunnerInstancesResponseSchema,\n} from '@shipfox/api-runners-dto';\nimport {defineRoute} from '@shipfox/node-fastify';\nimport {reportRunnerInstances} from '#core/index.js';\nimport {toReportRunnerInstancesResponseDto} from '#presentation/dto/index.js';\n\nexport const reportRunnerInstancesRoute = defineRoute({\n method: 'POST',\n path: '/runner-instances/report',\n description: 'Report provisioned runner lifecycle state',\n schema: {\n body: reportRunnerInstancesBodySchema,\n response: {\n 200: reportRunnerInstancesResponseSchema,\n },\n },\n handler: async (request) => {\n const context = requireProvisionerContext(request);\n const result = await reportRunnerInstances({\n workspaceId: context.scope === 'workspace' ? context.workspaceId : null,\n provisionerId: context.provisionerTokenId,\n events: request.body.events.map((event) => ({\n providerRunnerId: event.provider_runner_id,\n reservationId: event.reservation_id ?? null,\n templateKey: event.template_key ?? null,\n labels: event.labels,\n state: event.state,\n reason: event.reason ?? null,\n runnerSessionId: event.runner_session_id ?? null,\n providerKind: event.provider_kind ?? null,\n reportedAt: new Date(event.reported_at),\n })),\n });\n\n return toReportRunnerInstancesResponseDto(result);\n },\n});\n"],"names":["requireProvisionerContext","reportRunnerInstancesBodySchema","reportRunnerInstancesResponseSchema","defineRoute","reportRunnerInstances","toReportRunnerInstancesResponseDto","reportRunnerInstancesRoute","method","path","description","schema","body","response","handler","request","context","result","workspaceId","scope","provisionerId","provisionerTokenId","events","map","event","providerRunnerId","provider_runner_id","reservationId","reservation_id","templateKey","template_key","labels","state","reason","runnerSessionId","runner_session_id","providerKind","provider_kind","reportedAt","Date","reported_at"],"mappings":"AAAA,SAAQA,yBAAyB,QAAO,4BAA4B;AACpE,SACEC,+BAA+B,EAC/BC,mCAAmC,QAC9B,2BAA2B;AAClC,SAAQC,WAAW,QAAO,wBAAwB;AAClD,SAAQC,qBAAqB,QAAO,iBAAiB;AACrD,SAAQC,kCAAkC,QAAO,6BAA6B;AAE9E,OAAO,MAAMC,6BAA6BH,YAAY;IACpDI,QAAQ;IACRC,MAAM;IACNC,aAAa;IACbC,QAAQ;QACNC,MAAMV;QACNW,UAAU;YACR,KAAKV;QACP;IACF;IACAW,SAAS,OAAOC;QACd,MAAMC,UAAUf,0BAA0Bc;QAC1C,MAAME,SAAS,MAAMZ,sBAAsB;YACzCa,aAAaF,QAAQG,KAAK,KAAK,cAAcH,QAAQE,WAAW,GAAG;YACnEE,eAAeJ,QAAQK,kBAAkB;YACzCC,QAAQP,QAAQH,IAAI,CAACU,MAAM,CAACC,GAAG,CAAC,CAACC,QAAW,CAAA;oBAC1CC,kBAAkBD,MAAME,kBAAkB;oBAC1CC,eAAeH,MAAMI,cAAc,IAAI;oBACvCC,aAAaL,MAAMM,YAAY,IAAI;oBACnCC,QAAQP,MAAMO,MAAM;oBACpBC,OAAOR,MAAMQ,KAAK;oBAClBC,QAAQT,MAAMS,MAAM,IAAI;oBACxBC,iBAAiBV,MAAMW,iBAAiB,IAAI;oBAC5CC,cAAcZ,MAAMa,aAAa,IAAI;oBACrCC,YAAY,IAAIC,KAAKf,MAAMgB,WAAW;gBACxC,CAAA;QACF;QAEA,OAAOlC,mCAAmCW;IAC5C;AACF,GAAG"}
1
+ {"version":3,"sources":["../../../src/presentation/routes/report-runner-instances.ts"],"sourcesContent":["import {requireProvisionerContext} from '@shipfox/api-auth-context';\nimport {\n reportRunnerInstancesBodySchema,\n reportRunnerInstancesResponseSchema,\n} from '@shipfox/api-runners-dto';\nimport {defineRoute} from '@shipfox/node-fastify';\nimport {reportRunnerInstances} from '#core/index.js';\nimport {toReportRunnerInstancesResponseDto} from '#presentation/dto/index.js';\n\nexport const reportRunnerInstancesRoute = defineRoute({\n method: 'POST',\n path: '/runner-instances/report',\n description: 'Report provisioned runner lifecycle state',\n schema: {\n body: reportRunnerInstancesBodySchema,\n response: {\n 200: reportRunnerInstancesResponseSchema,\n },\n },\n handler: async (request) => {\n const context = requireProvisionerContext(request);\n const result = await reportRunnerInstances({\n scope: context.scope,\n workspaceId: context.scope === 'workspace' ? context.workspaceId : null,\n provisionerId: context.provisionerTokenId,\n events: request.body.events.map((event) => ({\n providerRunnerId: event.provider_runner_id,\n reservationId: event.reservation_id ?? null,\n templateKey: event.template_key ?? null,\n labels: event.labels,\n state: event.state,\n reason: event.reason ?? null,\n runnerSessionId: event.runner_session_id ?? null,\n providerKind: event.provider_kind ?? null,\n reportedAt: new Date(event.reported_at),\n })),\n });\n\n return toReportRunnerInstancesResponseDto(result);\n },\n});\n"],"names":["requireProvisionerContext","reportRunnerInstancesBodySchema","reportRunnerInstancesResponseSchema","defineRoute","reportRunnerInstances","toReportRunnerInstancesResponseDto","reportRunnerInstancesRoute","method","path","description","schema","body","response","handler","request","context","result","scope","workspaceId","provisionerId","provisionerTokenId","events","map","event","providerRunnerId","provider_runner_id","reservationId","reservation_id","templateKey","template_key","labels","state","reason","runnerSessionId","runner_session_id","providerKind","provider_kind","reportedAt","Date","reported_at"],"mappings":"AAAA,SAAQA,yBAAyB,QAAO,4BAA4B;AACpE,SACEC,+BAA+B,EAC/BC,mCAAmC,QAC9B,2BAA2B;AAClC,SAAQC,WAAW,QAAO,wBAAwB;AAClD,SAAQC,qBAAqB,QAAO,iBAAiB;AACrD,SAAQC,kCAAkC,QAAO,6BAA6B;AAE9E,OAAO,MAAMC,6BAA6BH,YAAY;IACpDI,QAAQ;IACRC,MAAM;IACNC,aAAa;IACbC,QAAQ;QACNC,MAAMV;QACNW,UAAU;YACR,KAAKV;QACP;IACF;IACAW,SAAS,OAAOC;QACd,MAAMC,UAAUf,0BAA0Bc;QAC1C,MAAME,SAAS,MAAMZ,sBAAsB;YACzCa,OAAOF,QAAQE,KAAK;YACpBC,aAAaH,QAAQE,KAAK,KAAK,cAAcF,QAAQG,WAAW,GAAG;YACnEC,eAAeJ,QAAQK,kBAAkB;YACzCC,QAAQP,QAAQH,IAAI,CAACU,MAAM,CAACC,GAAG,CAAC,CAACC,QAAW,CAAA;oBAC1CC,kBAAkBD,MAAME,kBAAkB;oBAC1CC,eAAeH,MAAMI,cAAc,IAAI;oBACvCC,aAAaL,MAAMM,YAAY,IAAI;oBACnCC,QAAQP,MAAMO,MAAM;oBACpBC,OAAOR,MAAMQ,KAAK;oBAClBC,QAAQT,MAAMS,MAAM,IAAI;oBACxBC,iBAAiBV,MAAMW,iBAAiB,IAAI;oBAC5CC,cAAcZ,MAAMa,aAAa,IAAI;oBACrCC,YAAY,IAAIC,KAAKf,MAAMgB,WAAW;gBACxC,CAAA;QACF;QAEA,OAAOlC,mCAAmCW;IAC5C;AACF,GAAG"}