@pipeline-builder/api-core 0.0.1

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 (184) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +98 -0
  3. package/lib/constants/ai-providers.d.ts +52 -0
  4. package/lib/constants/ai-providers.js +129 -0
  5. package/lib/constants/http-status.d.ts +20 -0
  6. package/lib/constants/http-status.js +26 -0
  7. package/lib/constants/index.d.ts +3 -0
  8. package/lib/constants/index.js +6 -0
  9. package/lib/constants/time.d.ts +10 -0
  10. package/lib/constants/time.js +13 -0
  11. package/lib/errors/app-errors.d.ts +30 -0
  12. package/lib/errors/app-errors.js +53 -0
  13. package/lib/errors/index.d.ts +1 -0
  14. package/lib/errors/index.js +4 -0
  15. package/lib/helpers/access-helpers.d.ts +63 -0
  16. package/lib/helpers/access-helpers.js +82 -0
  17. package/lib/helpers/crud-helpers.d.ts +9 -0
  18. package/lib/helpers/crud-helpers.js +23 -0
  19. package/lib/helpers/index.d.ts +6 -0
  20. package/lib/helpers/index.js +9 -0
  21. package/lib/helpers/org-hierarchy-http.d.ts +74 -0
  22. package/lib/helpers/org-hierarchy-http.js +144 -0
  23. package/lib/helpers/org-hierarchy.d.ts +57 -0
  24. package/lib/helpers/org-hierarchy.js +110 -0
  25. package/lib/helpers/restore-helpers.d.ts +49 -0
  26. package/lib/helpers/restore-helpers.js +78 -0
  27. package/lib/helpers/sse-helpers.d.ts +20 -0
  28. package/lib/helpers/sse-helpers.js +45 -0
  29. package/lib/index.d.ts +58 -0
  30. package/lib/index.js +71 -0
  31. package/lib/middleware/auth.d.ts +208 -0
  32. package/lib/middleware/auth.js +588 -0
  33. package/lib/middleware/index.d.ts +3 -0
  34. package/lib/middleware/index.js +6 -0
  35. package/lib/middleware/mongo-sanitize.d.ts +32 -0
  36. package/lib/middleware/mongo-sanitize.js +100 -0
  37. package/lib/middleware/step-up.d.ts +45 -0
  38. package/lib/middleware/step-up.js +138 -0
  39. package/lib/openapi/extend-zod.d.ts +1 -0
  40. package/lib/openapi/extend-zod.js +6 -0
  41. package/lib/openapi/index.d.ts +2 -0
  42. package/lib/openapi/index.js +5 -0
  43. package/lib/openapi/registry.d.ts +17 -0
  44. package/lib/openapi/registry.js +38 -0
  45. package/lib/openapi/routes/ask-routes.d.ts +7 -0
  46. package/lib/openapi/routes/ask-routes.js +62 -0
  47. package/lib/openapi/routes/billing-routes.d.ts +1 -0
  48. package/lib/openapi/routes/billing-routes.js +66 -0
  49. package/lib/openapi/routes/image-registry-routes.d.ts +7 -0
  50. package/lib/openapi/routes/image-registry-routes.js +98 -0
  51. package/lib/openapi/routes/index.d.ts +5 -0
  52. package/lib/openapi/routes/index.js +28 -0
  53. package/lib/openapi/routes/message-routes.d.ts +1 -0
  54. package/lib/openapi/routes/message-routes.js +105 -0
  55. package/lib/openapi/routes/pipeline-routes.d.ts +1 -0
  56. package/lib/openapi/routes/pipeline-routes.js +105 -0
  57. package/lib/openapi/routes/pipeline-template-routes.d.ts +1 -0
  58. package/lib/openapi/routes/pipeline-template-routes.js +61 -0
  59. package/lib/openapi/routes/plugin-routes.d.ts +1 -0
  60. package/lib/openapi/routes/plugin-routes.js +96 -0
  61. package/lib/openapi/routes/quota-routes.d.ts +1 -0
  62. package/lib/openapi/routes/quota-routes.js +62 -0
  63. package/lib/openapi/schema-registry.d.ts +25 -0
  64. package/lib/openapi/schema-registry.js +91 -0
  65. package/lib/routes/health.d.ts +41 -0
  66. package/lib/routes/health.js +82 -0
  67. package/lib/routes/index.d.ts +1 -0
  68. package/lib/routes/index.js +4 -0
  69. package/lib/services/audit-spool.d.ts +80 -0
  70. package/lib/services/audit-spool.js +172 -0
  71. package/lib/services/cache-service.d.ts +124 -0
  72. package/lib/services/cache-service.js +276 -0
  73. package/lib/services/circuit-breaker.d.ts +45 -0
  74. package/lib/services/circuit-breaker.js +138 -0
  75. package/lib/services/compliance-client.d.ts +46 -0
  76. package/lib/services/compliance-client.js +128 -0
  77. package/lib/services/compliance-event-subscriber.d.ts +16 -0
  78. package/lib/services/compliance-event-subscriber.js +72 -0
  79. package/lib/services/durable-event-bus.d.ts +96 -0
  80. package/lib/services/durable-event-bus.js +204 -0
  81. package/lib/services/entity-events.d.ts +48 -0
  82. package/lib/services/entity-events.js +79 -0
  83. package/lib/services/env-redis.d.ts +44 -0
  84. package/lib/services/env-redis.js +94 -0
  85. package/lib/services/http-client.d.ts +143 -0
  86. package/lib/services/http-client.js +335 -0
  87. package/lib/services/index.d.ts +18 -0
  88. package/lib/services/index.js +20 -0
  89. package/lib/services/leader-lock.d.ts +45 -0
  90. package/lib/services/leader-lock.js +85 -0
  91. package/lib/services/quota.d.ts +130 -0
  92. package/lib/services/quota.js +264 -0
  93. package/lib/services/remote-audit-client.d.ts +160 -0
  94. package/lib/services/remote-audit-client.js +390 -0
  95. package/lib/services/retry-strategy.d.ts +74 -0
  96. package/lib/services/retry-strategy.js +118 -0
  97. package/lib/services/scheduler.d.ts +41 -0
  98. package/lib/services/scheduler.js +86 -0
  99. package/lib/services/service-boot.d.ts +20 -0
  100. package/lib/services/service-boot.js +28 -0
  101. package/lib/services/sse-ticket-store.d.ts +51 -0
  102. package/lib/services/sse-ticket-store.js +130 -0
  103. package/lib/services/token-revocation.d.ts +52 -0
  104. package/lib/services/token-revocation.js +120 -0
  105. package/lib/testing/tier-mock.d.ts +44 -0
  106. package/lib/testing/tier-mock.js +65 -0
  107. package/lib/types/audit-events.d.ts +69 -0
  108. package/lib/types/audit-events.js +4 -0
  109. package/lib/types/catalog-metadata.d.ts +36 -0
  110. package/lib/types/catalog-metadata.js +21 -0
  111. package/lib/types/common.d.ts +255 -0
  112. package/lib/types/common.js +41 -0
  113. package/lib/types/error-codes.d.ts +54 -0
  114. package/lib/types/error-codes.js +107 -0
  115. package/lib/types/feature-flags.d.ts +36 -0
  116. package/lib/types/feature-flags.js +128 -0
  117. package/lib/types/http.d.ts +27 -0
  118. package/lib/types/http.js +4 -0
  119. package/lib/types/index.d.ts +11 -0
  120. package/lib/types/index.js +14 -0
  121. package/lib/types/permissions.d.ts +65 -0
  122. package/lib/types/permissions.js +131 -0
  123. package/lib/types/pipeline-template.d.ts +30 -0
  124. package/lib/types/pipeline-template.js +13 -0
  125. package/lib/types/pipeline.d.ts +59 -0
  126. package/lib/types/pipeline.js +31 -0
  127. package/lib/types/quota-tiers.d.ts +79 -0
  128. package/lib/types/quota-tiers.js +221 -0
  129. package/lib/types/visibility.d.ts +20 -0
  130. package/lib/types/visibility.js +22 -0
  131. package/lib/utils/alias-resolver.d.ts +37 -0
  132. package/lib/utils/alias-resolver.js +68 -0
  133. package/lib/utils/audit.d.ts +15 -0
  134. package/lib/utils/audit.js +39 -0
  135. package/lib/utils/aws-scrub.d.ts +11 -0
  136. package/lib/utils/aws-scrub.js +67 -0
  137. package/lib/utils/compliance-attributes.d.ts +6 -0
  138. package/lib/utils/compliance-attributes.js +69 -0
  139. package/lib/utils/concurrency.d.ts +9 -0
  140. package/lib/utils/concurrency.js +29 -0
  141. package/lib/utils/crash-handlers.d.ts +18 -0
  142. package/lib/utils/crash-handlers.js +36 -0
  143. package/lib/utils/env.d.ts +28 -0
  144. package/lib/utils/env.js +54 -0
  145. package/lib/utils/headers.d.ts +18 -0
  146. package/lib/utils/headers.js +21 -0
  147. package/lib/utils/identity.d.ts +40 -0
  148. package/lib/utils/identity.js +51 -0
  149. package/lib/utils/index.d.ts +17 -0
  150. package/lib/utils/index.js +20 -0
  151. package/lib/utils/logger.d.ts +38 -0
  152. package/lib/utils/logger.js +174 -0
  153. package/lib/utils/metric-emitter.d.ts +36 -0
  154. package/lib/utils/metric-emitter.js +32 -0
  155. package/lib/utils/object.d.ts +13 -0
  156. package/lib/utils/object.js +18 -0
  157. package/lib/utils/params.d.ts +150 -0
  158. package/lib/utils/params.js +206 -0
  159. package/lib/utils/response.d.ts +117 -0
  160. package/lib/utils/response.js +223 -0
  161. package/lib/utils/safe-require.d.ts +13 -0
  162. package/lib/utils/safe-require.js +19 -0
  163. package/lib/utils/secret-encryption.d.ts +176 -0
  164. package/lib/utils/secret-encryption.js +374 -0
  165. package/lib/utils/ssrf.d.ts +30 -0
  166. package/lib/utils/ssrf.js +106 -0
  167. package/lib/validation/ai-schemas.d.ts +56 -0
  168. package/lib/validation/ai-schemas.js +78 -0
  169. package/lib/validation/common-schemas.d.ts +133 -0
  170. package/lib/validation/common-schemas.js +94 -0
  171. package/lib/validation/index.d.ts +7 -0
  172. package/lib/validation/index.js +10 -0
  173. package/lib/validation/message-schemas.d.ts +160 -0
  174. package/lib/validation/message-schemas.js +156 -0
  175. package/lib/validation/middleware.d.ts +72 -0
  176. package/lib/validation/middleware.js +65 -0
  177. package/lib/validation/pipeline-schemas.d.ts +181 -0
  178. package/lib/validation/pipeline-schemas.js +101 -0
  179. package/lib/validation/pipeline-template-schemas.d.ts +155 -0
  180. package/lib/validation/pipeline-template-schemas.js +66 -0
  181. package/lib/validation/plugin-schemas.d.ts +171 -0
  182. package/lib/validation/plugin-schemas.js +81 -0
  183. package/openapi.yaml +292 -0
  184. package/package.json +167 -0
@@ -0,0 +1,3 @@
1
+ export * from './auth.js';
2
+ export * from './mongo-sanitize.js';
3
+ export * from './step-up.js';
@@ -0,0 +1,6 @@
1
+ // Copyright 2026 Pipeline Builder Contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ export * from './auth.js';
4
+ export * from './mongo-sanitize.js';
5
+ export * from './step-up.js';
6
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvbWlkZGxld2FyZS9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDLGNBQWMsV0FBVyxDQUFDO0FBQzFCLGNBQWMscUJBQXFCLENBQUM7QUFDcEMsY0FBYyxjQUFjLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuZXhwb3J0ICogZnJvbSAnLi9hdXRoLmpzJztcbmV4cG9ydCAqIGZyb20gJy4vbW9uZ28tc2FuaXRpemUuanMnO1xuZXhwb3J0ICogZnJvbSAnLi9zdGVwLXVwLmpzJztcbiJdfQ==
@@ -0,0 +1,32 @@
1
+ import type { NextFunction, Request, Response } from 'express';
2
+ /** Maximum nesting depth accepted by {@link mongoSanitize}. */
3
+ export declare const MAX_SANITIZE_DEPTH = 10;
4
+ /**
5
+ * Thrown when a request payload nests deeper than {@link MAX_SANITIZE_DEPTH}.
6
+ * The middleware maps this to HTTP 400 instead of silently truncating the walk
7
+ * (which would let operator-shaped keys below the cap slip through unsanitized).
8
+ */
9
+ export declare class PayloadTooDeepError extends Error {
10
+ readonly statusCode: 400;
11
+ constructor(message?: string);
12
+ }
13
+ /**
14
+ * MongoDB operator-injection middleware.
15
+ *
16
+ * Recursively strips object keys starting with `$` (Mongo operators like
17
+ * `$ne`, `$gt`, `$where`) and keys containing `.` (dot-walks) from
18
+ * `req.body`, `req.query`, and `req.params`. Without this, a JSON request
19
+ * like `{"email": {"$ne": null}}` against a Mongo-backed service can match
20
+ * any document — Zod catches the wrong *type* but won't reach into nested
21
+ * objects to strip operator-shaped keys.
22
+ *
23
+ * Apply BEFORE any Mongo query handler (typically right after
24
+ * `express.json()`). Postgres-backed services don't need it.
25
+ *
26
+ * `req.body` and `req.params` are plain writable objects, so they're sanitized
27
+ * in place. On Express 5 `req.query` is a GETTER that re-parses the URL and
28
+ * returns a fresh object on every access — mutating it is a no-op — so we
29
+ * sanitize a snapshot and pin it onto the request instance with
30
+ * `Object.defineProperty`, shadowing the getter for all downstream reads.
31
+ */
32
+ export declare function mongoSanitize(): (req: Request, res: Response, next: NextFunction) => void;
@@ -0,0 +1,100 @@
1
+ // Copyright 2026 Pipeline Builder Contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { HttpStatus } from '../constants/http-status.js';
4
+ import { ErrorCode } from '../types/error-codes.js';
5
+ import { sendError } from '../utils/response.js';
6
+ /** Maximum nesting depth accepted by {@link mongoSanitize}. */
7
+ export const MAX_SANITIZE_DEPTH = 10;
8
+ /**
9
+ * Thrown when a request payload nests deeper than {@link MAX_SANITIZE_DEPTH}.
10
+ * The middleware maps this to HTTP 400 instead of silently truncating the walk
11
+ * (which would let operator-shaped keys below the cap slip through unsanitized).
12
+ */
13
+ export class PayloadTooDeepError extends Error {
14
+ statusCode = HttpStatus.BAD_REQUEST;
15
+ constructor(message = `Request payload nesting exceeds the maximum depth of ${MAX_SANITIZE_DEPTH}`) {
16
+ super(message);
17
+ this.name = 'PayloadTooDeepError';
18
+ }
19
+ }
20
+ /**
21
+ * MongoDB operator-injection middleware.
22
+ *
23
+ * Recursively strips object keys starting with `$` (Mongo operators like
24
+ * `$ne`, `$gt`, `$where`) and keys containing `.` (dot-walks) from
25
+ * `req.body`, `req.query`, and `req.params`. Without this, a JSON request
26
+ * like `{"email": {"$ne": null}}` against a Mongo-backed service can match
27
+ * any document — Zod catches the wrong *type* but won't reach into nested
28
+ * objects to strip operator-shaped keys.
29
+ *
30
+ * Apply BEFORE any Mongo query handler (typically right after
31
+ * `express.json()`). Postgres-backed services don't need it.
32
+ *
33
+ * `req.body` and `req.params` are plain writable objects, so they're sanitized
34
+ * in place. On Express 5 `req.query` is a GETTER that re-parses the URL and
35
+ * returns a fresh object on every access — mutating it is a no-op — so we
36
+ * sanitize a snapshot and pin it onto the request instance with
37
+ * `Object.defineProperty`, shadowing the getter for all downstream reads.
38
+ */
39
+ export function mongoSanitize() {
40
+ return (req, res, next) => {
41
+ try {
42
+ if (req.body && typeof req.body === 'object')
43
+ sanitizeObject(req.body);
44
+ if (req.params && typeof req.params === 'object')
45
+ sanitizeObject(req.params);
46
+ if (req.query && typeof req.query === 'object') {
47
+ const q = req.query;
48
+ sanitizeObject(q);
49
+ // Pin the sanitized snapshot so the Express 5 getter can't re-parse a
50
+ // fresh (unsanitized) object on the next access.
51
+ try {
52
+ Object.defineProperty(req, 'query', { value: q, writable: true, configurable: true, enumerable: true });
53
+ }
54
+ catch {
55
+ // Express 4 (writable data prop) — the in-place sanitize above sufficed.
56
+ }
57
+ }
58
+ }
59
+ catch (err) {
60
+ // Reject over-deep payloads with a 400 rather than silently truncating
61
+ // the sanitize walk (which risked leaving deep operator keys in place).
62
+ if (err instanceof PayloadTooDeepError) {
63
+ return sendError(res, err.statusCode, err.message, ErrorCode.VALIDATION_ERROR);
64
+ }
65
+ throw err;
66
+ }
67
+ next();
68
+ };
69
+ }
70
+ // Prototype-pollution keys — these never belong in JSON request bodies, and a
71
+ // downstream recursive merge/assign on the parsed object could otherwise walk
72
+ // `constructor.prototype` to inject Mongo operators or pollute Object.prototype.
73
+ const FORBIDDEN_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
74
+ function sanitizeObject(obj, depth = 0) {
75
+ // Reject pathologically nested input rather than silently returning — a
76
+ // silent cap would leave any operator/dot/prototype keys deeper than the
77
+ // cap un-sanitized. The middleware maps this to a 400.
78
+ if (depth > MAX_SANITIZE_DEPTH) {
79
+ throw new PayloadTooDeepError();
80
+ }
81
+ for (const key of Object.keys(obj)) {
82
+ if (key.startsWith('$') || key.includes('.') || FORBIDDEN_KEYS.has(key)) {
83
+ delete obj[key];
84
+ continue;
85
+ }
86
+ const v = obj[key];
87
+ if (v && typeof v === 'object') {
88
+ if (Array.isArray(v)) {
89
+ for (const item of v) {
90
+ if (item && typeof item === 'object')
91
+ sanitizeObject(item, depth + 1);
92
+ }
93
+ }
94
+ else {
95
+ sanitizeObject(v, depth + 1);
96
+ }
97
+ }
98
+ }
99
+ }
100
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9uZ28tc2FuaXRpemUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvbWlkZGxld2FyZS9tb25nby1zYW5pdGl6ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBR3RDLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUN6RCxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDcEQsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBRWpELCtEQUErRDtBQUMvRCxNQUFNLENBQUMsTUFBTSxrQkFBa0IsR0FBRyxFQUFFLENBQUM7QUFFckM7Ozs7R0FJRztBQUNILE1BQU0sT0FBTyxtQkFBb0IsU0FBUSxLQUFLO0lBQ25DLFVBQVUsR0FBRyxVQUFVLENBQUMsV0FBVyxDQUFDO0lBQzdDLFlBQVksT0FBTyxHQUFHLHdEQUF3RCxrQkFBa0IsRUFBRTtRQUNoRyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDZixJQUFJLENBQUMsSUFBSSxHQUFHLHFCQUFxQixDQUFDO0lBQ3BDLENBQUM7Q0FDRjtBQUVEOzs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FrQkc7QUFDSCxNQUFNLFVBQVUsYUFBYTtJQUMzQixPQUFPLENBQUMsR0FBWSxFQUFFLEdBQWEsRUFBRSxJQUFrQixFQUFFLEVBQUU7UUFDekQsSUFBSSxDQUFDO1lBQ0gsSUFBSSxHQUFHLENBQUMsSUFBSSxJQUFJLE9BQU8sR0FBRyxDQUFDLElBQUksS0FBSyxRQUFRO2dCQUFFLGNBQWMsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDdkUsSUFBSSxHQUFHLENBQUMsTUFBTSxJQUFJLE9BQU8sR0FBRyxDQUFDLE1BQU0sS0FBSyxRQUFRO2dCQUFFLGNBQWMsQ0FBQyxHQUFHLENBQUMsTUFBaUMsQ0FBQyxDQUFDO1lBQ3hHLElBQUksR0FBRyxDQUFDLEtBQUssSUFBSSxPQUFPLEdBQUcsQ0FBQyxLQUFLLEtBQUssUUFBUSxFQUFFLENBQUM7Z0JBQy9DLE1BQU0sQ0FBQyxHQUFHLEdBQUcsQ0FBQyxLQUFnQyxDQUFDO2dCQUMvQyxjQUFjLENBQUMsQ0FBQyxDQUFDLENBQUM7Z0JBQ2xCLHNFQUFzRTtnQkFDdEUsaURBQWlEO2dCQUNqRCxJQUFJLENBQUM7b0JBQ0gsTUFBTSxDQUFDLGNBQWMsQ0FBQyxHQUFHLEVBQUUsT0FBTyxFQUFFLEVBQUUsS0FBSyxFQUFFLENBQUMsRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFLFlBQVksRUFBRSxJQUFJLEVBQUUsVUFBVSxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7Z0JBQzFHLENBQUM7Z0JBQUMsTUFBTSxDQUFDO29CQUNQLHlFQUF5RTtnQkFDM0UsQ0FBQztZQUNILENBQUM7UUFDSCxDQUFDO1FBQUMsT0FBTyxHQUFHLEVBQUUsQ0FBQztZQUNiLHVFQUF1RTtZQUN2RSx3RUFBd0U7WUFDeEUsSUFBSSxHQUFHLFlBQVksbUJBQW1CLEVBQUUsQ0FBQztnQkFDdkMsT0FBTyxTQUFTLENBQUMsR0FBRyxFQUFFLEdBQUcsQ0FBQyxVQUFVLEVBQUUsR0FBRyxDQUFDLE9BQU8sRUFBRSxTQUFTLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztZQUNqRixDQUFDO1lBQ0QsTUFBTSxHQUFHLENBQUM7UUFDWixDQUFDO1FBQ0QsSUFBSSxFQUFFLENBQUM7SUFDVCxDQUFDLENBQUM7QUFDSixDQUFDO0FBRUQsOEVBQThFO0FBQzlFLDhFQUE4RTtBQUM5RSxpRkFBaUY7QUFDakYsTUFBTSxjQUFjLEdBQUcsSUFBSSxHQUFHLENBQUMsQ0FBQyxXQUFXLEVBQUUsYUFBYSxFQUFFLFdBQVcsQ0FBQyxDQUFDLENBQUM7QUFFMUUsU0FBUyxjQUFjLENBQUMsR0FBNEIsRUFBRSxLQUFLLEdBQUcsQ0FBQztJQUM3RCx3RUFBd0U7SUFDeEUseUVBQXlFO0lBQ3pFLHVEQUF1RDtJQUN2RCxJQUFJLEtBQUssR0FBRyxrQkFBa0IsRUFBRSxDQUFDO1FBQy9CLE1BQU0sSUFBSSxtQkFBbUIsRUFBRSxDQUFDO0lBQ2xDLENBQUM7SUFDRCxLQUFLLE1BQU0sR0FBRyxJQUFJLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztRQUNuQyxJQUFJLEdBQUcsQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLElBQUksR0FBRyxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsSUFBSSxjQUFjLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUM7WUFDeEUsT0FBTyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDaEIsU0FBUztRQUNYLENBQUM7UUFDRCxNQUFNLENBQUMsR0FBRyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDbkIsSUFBSSxDQUFDLElBQUksT0FBTyxDQUFDLEtBQUssUUFBUSxFQUFFLENBQUM7WUFDL0IsSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7Z0JBQ3JCLEtBQUssTUFBTSxJQUFJLElBQUksQ0FBQyxFQUFFLENBQUM7b0JBQ3JCLElBQUksSUFBSSxJQUFJLE9BQU8sSUFBSSxLQUFLLFFBQVE7d0JBQUUsY0FBYyxDQUFDLElBQStCLEVBQUUsS0FBSyxHQUFHLENBQUMsQ0FBQyxDQUFDO2dCQUNuRyxDQUFDO1lBQ0gsQ0FBQztpQkFBTSxDQUFDO2dCQUNOLGNBQWMsQ0FBQyxDQUE0QixFQUFFLEtBQUssR0FBRyxDQUFDLENBQUMsQ0FBQztZQUMxRCxDQUFDO1FBQ0gsQ0FBQztJQUNILENBQUM7QUFDSCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbmltcG9ydCB0eXBlIHsgTmV4dEZ1bmN0aW9uLCBSZXF1ZXN0LCBSZXNwb25zZSB9IGZyb20gJ2V4cHJlc3MnO1xuaW1wb3J0IHsgSHR0cFN0YXR1cyB9IGZyb20gJy4uL2NvbnN0YW50cy9odHRwLXN0YXR1cy5qcyc7XG5pbXBvcnQgeyBFcnJvckNvZGUgfSBmcm9tICcuLi90eXBlcy9lcnJvci1jb2Rlcy5qcyc7XG5pbXBvcnQgeyBzZW5kRXJyb3IgfSBmcm9tICcuLi91dGlscy9yZXNwb25zZS5qcyc7XG5cbi8qKiBNYXhpbXVtIG5lc3RpbmcgZGVwdGggYWNjZXB0ZWQgYnkge0BsaW5rIG1vbmdvU2FuaXRpemV9LiAqL1xuZXhwb3J0IGNvbnN0IE1BWF9TQU5JVElaRV9ERVBUSCA9IDEwO1xuXG4vKipcbiAqIFRocm93biB3aGVuIGEgcmVxdWVzdCBwYXlsb2FkIG5lc3RzIGRlZXBlciB0aGFuIHtAbGluayBNQVhfU0FOSVRJWkVfREVQVEh9LlxuICogVGhlIG1pZGRsZXdhcmUgbWFwcyB0aGlzIHRvIEhUVFAgNDAwIGluc3RlYWQgb2Ygc2lsZW50bHkgdHJ1bmNhdGluZyB0aGUgd2Fsa1xuICogKHdoaWNoIHdvdWxkIGxldCBvcGVyYXRvci1zaGFwZWQga2V5cyBiZWxvdyB0aGUgY2FwIHNsaXAgdGhyb3VnaCB1bnNhbml0aXplZCkuXG4gKi9cbmV4cG9ydCBjbGFzcyBQYXlsb2FkVG9vRGVlcEVycm9yIGV4dGVuZHMgRXJyb3Ige1xuICByZWFkb25seSBzdGF0dXNDb2RlID0gSHR0cFN0YXR1cy5CQURfUkVRVUVTVDtcbiAgY29uc3RydWN0b3IobWVzc2FnZSA9IGBSZXF1ZXN0IHBheWxvYWQgbmVzdGluZyBleGNlZWRzIHRoZSBtYXhpbXVtIGRlcHRoIG9mICR7TUFYX1NBTklUSVpFX0RFUFRIfWApIHtcbiAgICBzdXBlcihtZXNzYWdlKTtcbiAgICB0aGlzLm5hbWUgPSAnUGF5bG9hZFRvb0RlZXBFcnJvcic7XG4gIH1cbn1cblxuLyoqXG4gKiBNb25nb0RCIG9wZXJhdG9yLWluamVjdGlvbiBtaWRkbGV3YXJlLlxuICpcbiAqIFJlY3Vyc2l2ZWx5IHN0cmlwcyBvYmplY3Qga2V5cyBzdGFydGluZyB3aXRoIGAkYCAoTW9uZ28gb3BlcmF0b3JzIGxpa2VcbiAqIGAkbmVgLCBgJGd0YCwgYCR3aGVyZWApIGFuZCBrZXlzIGNvbnRhaW5pbmcgYC5gIChkb3Qtd2Fsa3MpIGZyb21cbiAqIGByZXEuYm9keWAsIGByZXEucXVlcnlgLCBhbmQgYHJlcS5wYXJhbXNgLiBXaXRob3V0IHRoaXMsIGEgSlNPTiByZXF1ZXN0XG4gKiBsaWtlIGB7XCJlbWFpbFwiOiB7XCIkbmVcIjogbnVsbH19YCBhZ2FpbnN0IGEgTW9uZ28tYmFja2VkIHNlcnZpY2UgY2FuIG1hdGNoXG4gKiBhbnkgZG9jdW1lbnQg4oCUIFpvZCBjYXRjaGVzIHRoZSB3cm9uZyAqdHlwZSogYnV0IHdvbid0IHJlYWNoIGludG8gbmVzdGVkXG4gKiBvYmplY3RzIHRvIHN0cmlwIG9wZXJhdG9yLXNoYXBlZCBrZXlzLlxuICpcbiAqIEFwcGx5IEJFRk9SRSBhbnkgTW9uZ28gcXVlcnkgaGFuZGxlciAodHlwaWNhbGx5IHJpZ2h0IGFmdGVyXG4gKiBgZXhwcmVzcy5qc29uKClgKS4gUG9zdGdyZXMtYmFja2VkIHNlcnZpY2VzIGRvbid0IG5lZWQgaXQuXG4gKlxuICogYHJlcS5ib2R5YCBhbmQgYHJlcS5wYXJhbXNgIGFyZSBwbGFpbiB3cml0YWJsZSBvYmplY3RzLCBzbyB0aGV5J3JlIHNhbml0aXplZFxuICogaW4gcGxhY2UuIE9uIEV4cHJlc3MgNSBgcmVxLnF1ZXJ5YCBpcyBhIEdFVFRFUiB0aGF0IHJlLXBhcnNlcyB0aGUgVVJMIGFuZFxuICogcmV0dXJucyBhIGZyZXNoIG9iamVjdCBvbiBldmVyeSBhY2Nlc3Mg4oCUIG11dGF0aW5nIGl0IGlzIGEgbm8tb3Ag4oCUIHNvIHdlXG4gKiBzYW5pdGl6ZSBhIHNuYXBzaG90IGFuZCBwaW4gaXQgb250byB0aGUgcmVxdWVzdCBpbnN0YW5jZSB3aXRoXG4gKiBgT2JqZWN0LmRlZmluZVByb3BlcnR5YCwgc2hhZG93aW5nIHRoZSBnZXR0ZXIgZm9yIGFsbCBkb3duc3RyZWFtIHJlYWRzLlxuICovXG5leHBvcnQgZnVuY3Rpb24gbW9uZ29TYW5pdGl6ZSgpIHtcbiAgcmV0dXJuIChyZXE6IFJlcXVlc3QsIHJlczogUmVzcG9uc2UsIG5leHQ6IE5leHRGdW5jdGlvbikgPT4ge1xuICAgIHRyeSB7XG4gICAgICBpZiAocmVxLmJvZHkgJiYgdHlwZW9mIHJlcS5ib2R5ID09PSAnb2JqZWN0Jykgc2FuaXRpemVPYmplY3QocmVxLmJvZHkpO1xuICAgICAgaWYgKHJlcS5wYXJhbXMgJiYgdHlwZW9mIHJlcS5wYXJhbXMgPT09ICdvYmplY3QnKSBzYW5pdGl6ZU9iamVjdChyZXEucGFyYW1zIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+KTtcbiAgICAgIGlmIChyZXEucXVlcnkgJiYgdHlwZW9mIHJlcS5xdWVyeSA9PT0gJ29iamVjdCcpIHtcbiAgICAgICAgY29uc3QgcSA9IHJlcS5xdWVyeSBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbiAgICAgICAgc2FuaXRpemVPYmplY3QocSk7XG4gICAgICAgIC8vIFBpbiB0aGUgc2FuaXRpemVkIHNuYXBzaG90IHNvIHRoZSBFeHByZXNzIDUgZ2V0dGVyIGNhbid0IHJlLXBhcnNlIGFcbiAgICAgICAgLy8gZnJlc2ggKHVuc2FuaXRpemVkKSBvYmplY3Qgb24gdGhlIG5leHQgYWNjZXNzLlxuICAgICAgICB0cnkge1xuICAgICAgICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShyZXEsICdxdWVyeScsIHsgdmFsdWU6IHEsIHdyaXRhYmxlOiB0cnVlLCBjb25maWd1cmFibGU6IHRydWUsIGVudW1lcmFibGU6IHRydWUgfSk7XG4gICAgICAgIH0gY2F0Y2gge1xuICAgICAgICAgIC8vIEV4cHJlc3MgNCAod3JpdGFibGUgZGF0YSBwcm9wKSDigJQgdGhlIGluLXBsYWNlIHNhbml0aXplIGFib3ZlIHN1ZmZpY2VkLlxuICAgICAgICB9XG4gICAgICB9XG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAvLyBSZWplY3Qgb3Zlci1kZWVwIHBheWxvYWRzIHdpdGggYSA0MDAgcmF0aGVyIHRoYW4gc2lsZW50bHkgdHJ1bmNhdGluZ1xuICAgICAgLy8gdGhlIHNhbml0aXplIHdhbGsgKHdoaWNoIHJpc2tlZCBsZWF2aW5nIGRlZXAgb3BlcmF0b3Iga2V5cyBpbiBwbGFjZSkuXG4gICAgICBpZiAoZXJyIGluc3RhbmNlb2YgUGF5bG9hZFRvb0RlZXBFcnJvcikge1xuICAgICAgICByZXR1cm4gc2VuZEVycm9yKHJlcywgZXJyLnN0YXR1c0NvZGUsIGVyci5tZXNzYWdlLCBFcnJvckNvZGUuVkFMSURBVElPTl9FUlJPUik7XG4gICAgICB9XG4gICAgICB0aHJvdyBlcnI7XG4gICAgfVxuICAgIG5leHQoKTtcbiAgfTtcbn1cblxuLy8gUHJvdG90eXBlLXBvbGx1dGlvbiBrZXlzIOKAlCB0aGVzZSBuZXZlciBiZWxvbmcgaW4gSlNPTiByZXF1ZXN0IGJvZGllcywgYW5kIGFcbi8vIGRvd25zdHJlYW0gcmVjdXJzaXZlIG1lcmdlL2Fzc2lnbiBvbiB0aGUgcGFyc2VkIG9iamVjdCBjb3VsZCBvdGhlcndpc2Ugd2Fsa1xuLy8gYGNvbnN0cnVjdG9yLnByb3RvdHlwZWAgdG8gaW5qZWN0IE1vbmdvIG9wZXJhdG9ycyBvciBwb2xsdXRlIE9iamVjdC5wcm90b3R5cGUuXG5jb25zdCBGT1JCSURERU5fS0VZUyA9IG5ldyBTZXQoWydfX3Byb3RvX18nLCAnY29uc3RydWN0b3InLCAncHJvdG90eXBlJ10pO1xuXG5mdW5jdGlvbiBzYW5pdGl6ZU9iamVjdChvYmo6IFJlY29yZDxzdHJpbmcsIHVua25vd24+LCBkZXB0aCA9IDApOiB2b2lkIHtcbiAgLy8gUmVqZWN0IHBhdGhvbG9naWNhbGx5IG5lc3RlZCBpbnB1dCByYXRoZXIgdGhhbiBzaWxlbnRseSByZXR1cm5pbmcg4oCUIGFcbiAgLy8gc2lsZW50IGNhcCB3b3VsZCBsZWF2ZSBhbnkgb3BlcmF0b3IvZG90L3Byb3RvdHlwZSBrZXlzIGRlZXBlciB0aGFuIHRoZVxuICAvLyBjYXAgdW4tc2FuaXRpemVkLiBUaGUgbWlkZGxld2FyZSBtYXBzIHRoaXMgdG8gYSA0MDAuXG4gIGlmIChkZXB0aCA+IE1BWF9TQU5JVElaRV9ERVBUSCkge1xuICAgIHRocm93IG5ldyBQYXlsb2FkVG9vRGVlcEVycm9yKCk7XG4gIH1cbiAgZm9yIChjb25zdCBrZXkgb2YgT2JqZWN0LmtleXMob2JqKSkge1xuICAgIGlmIChrZXkuc3RhcnRzV2l0aCgnJCcpIHx8IGtleS5pbmNsdWRlcygnLicpIHx8IEZPUkJJRERFTl9LRVlTLmhhcyhrZXkpKSB7XG4gICAgICBkZWxldGUgb2JqW2tleV07XG4gICAgICBjb250aW51ZTtcbiAgICB9XG4gICAgY29uc3QgdiA9IG9ialtrZXldO1xuICAgIGlmICh2ICYmIHR5cGVvZiB2ID09PSAnb2JqZWN0Jykge1xuICAgICAgaWYgKEFycmF5LmlzQXJyYXkodikpIHtcbiAgICAgICAgZm9yIChjb25zdCBpdGVtIG9mIHYpIHtcbiAgICAgICAgICBpZiAoaXRlbSAmJiB0eXBlb2YgaXRlbSA9PT0gJ29iamVjdCcpIHNhbml0aXplT2JqZWN0KGl0ZW0gYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4sIGRlcHRoICsgMSk7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHNhbml0aXplT2JqZWN0KHYgYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4sIGRlcHRoICsgMSk7XG4gICAgICB9XG4gICAgfVxuICB9XG59XG4iXX0=
@@ -0,0 +1,45 @@
1
+ /**
2
+ * `requireStepUp` — a shared step-up (recent-password-reverify) gate for the
3
+ * stateless api services, mirroring the platform middleware of the same name.
4
+ *
5
+ * Flow: the UI hits `POST /api/auth/step-up` (platform) to re-verify the user's
6
+ * password; platform mints a short-lived (60s) `step-up` JWT signed with the
7
+ * SAME `JWT_SECRET` these services already verify access tokens with. The UI
8
+ * replays it as the `X-Step-Up-Token` header on the destructive/sensitive call;
9
+ * this middleware verifies it, binds it to the caller (`sub` match), and
10
+ * consumes its `jti` once (Redis-backed cross-instance when configured, else a
11
+ * bounded process-local set) so a replay inside the TTL is rejected.
12
+ *
13
+ * WHY here: restore endpoints on the api services (pipeline/plugin/...) are
14
+ * step-up-gated, but step-up previously lived only in `platform`. This ports the
15
+ * verify + single-use jti into api-core so any service can require it.
16
+ */
17
+ import type { Request, Response, NextFunction } from 'express';
18
+ /** Payload of a platform-issued step-up token (see platform issueStepUpToken). */
19
+ export interface StepUpTokenPayload {
20
+ type: 'step-up';
21
+ sub: string;
22
+ jti: string;
23
+ iat: number;
24
+ exp: number;
25
+ }
26
+ /**
27
+ * Verify a step-up token: valid signature (primary `JWT_SECRET`, or
28
+ * `JWT_SECRET_PREVIOUS` during a rotation), allow-listed algorithm, AND the
29
+ * `type: 'step-up'` + `jti` claims — a plain access token shares the secret and
30
+ * `sub`, so without asserting the step-up shape it would bypass the gate.
31
+ * Throws on any failure. Does NOT bind to a caller — `requireStepUp` does the
32
+ * `sub` match and single-use consume.
33
+ */
34
+ export declare function verifyStepUpToken(token: string): StepUpTokenPayload;
35
+ /**
36
+ * Consume a step-up `jti` exactly once. Returns true if this call claimed it
37
+ * (first use), false if it was already consumed (replay). `expEpochSeconds` is
38
+ * the token's `exp`, so the marker auto-expires with the token.
39
+ */
40
+ export declare function consumeStepUpJti(jti: string, expEpochSeconds: number): Promise<boolean>;
41
+ /**
42
+ * Express middleware: require a valid, caller-bound, single-use step-up token in
43
+ * the `X-Step-Up-Token` header. Must run AFTER `requireAuth` (needs `req.user`).
44
+ */
45
+ export declare function requireStepUp(req: Request, res: Response, next: NextFunction): Promise<void>;
@@ -0,0 +1,138 @@
1
+ // Copyright 2026 Pipeline Builder Contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import jwt from 'jsonwebtoken';
4
+ import { isServicePrincipal } from './auth.js';
5
+ import { createEnvRedisClient } from '../services/env-redis.js';
6
+ import { getHeaderString } from '../utils/headers.js';
7
+ import { createLogger } from '../utils/logger.js';
8
+ import { sendError } from '../utils/response.js';
9
+ const logger = createLogger('step-up');
10
+ const HEADER = 'x-step-up-token';
11
+ function jwtAlgorithm() {
12
+ return (process.env.JWT_ALGORITHM || 'HS256');
13
+ }
14
+ /**
15
+ * Verify a step-up token: valid signature (primary `JWT_SECRET`, or
16
+ * `JWT_SECRET_PREVIOUS` during a rotation), allow-listed algorithm, AND the
17
+ * `type: 'step-up'` + `jti` claims — a plain access token shares the secret and
18
+ * `sub`, so without asserting the step-up shape it would bypass the gate.
19
+ * Throws on any failure. Does NOT bind to a caller — `requireStepUp` does the
20
+ * `sub` match and single-use consume.
21
+ */
22
+ export function verifyStepUpToken(token) {
23
+ const secret = process.env.JWT_SECRET;
24
+ if (!secret)
25
+ throw new Error('JWT_SECRET environment variable is required');
26
+ const opts = { algorithms: [jwtAlgorithm()] };
27
+ let payload;
28
+ try {
29
+ payload = jwt.verify(token, secret, opts);
30
+ }
31
+ catch (primaryErr) {
32
+ const previous = process.env.JWT_SECRET_PREVIOUS;
33
+ if (!previous)
34
+ throw primaryErr;
35
+ payload = jwt.verify(token, previous, opts);
36
+ }
37
+ if (payload.type !== 'step-up' || !payload.jti || !payload.sub) {
38
+ throw new Error('INVALID_STEP_UP_TOKEN');
39
+ }
40
+ return payload;
41
+ }
42
+ // -- Single-use jti store -----------------------------------------------------
43
+ // Redis SET NX EX when Redis is configured (cross-instance single-use); else a
44
+ // bounded process-local map with TTL sweep (per-process). A step-up token is
45
+ // 60s-lived. When Redis IS configured it's the authoritative store: a Redis
46
+ // error FAILS CLOSED (throws → STEP_UP_INVALID, user re-verifies) rather than
47
+ // degrading to the per-instance mem guard, which would accept a cross-instance
48
+ // replay during the outage. The mem guard is used ONLY when Redis is absent.
49
+ let _redis;
50
+ function redis() {
51
+ if (_redis === undefined) {
52
+ _redis = createEnvRedisClient('step-up-jti');
53
+ }
54
+ return _redis;
55
+ }
56
+ const memJti = new Map(); // jti -> expiry epoch ms
57
+ const memSweep = setInterval(() => {
58
+ const now = Date.now();
59
+ for (const [jti, exp] of memJti)
60
+ if (exp <= now)
61
+ memJti.delete(jti);
62
+ }, 30_000);
63
+ memSweep.unref?.();
64
+ /**
65
+ * Consume a step-up `jti` exactly once. Returns true if this call claimed it
66
+ * (first use), false if it was already consumed (replay). `expEpochSeconds` is
67
+ * the token's `exp`, so the marker auto-expires with the token.
68
+ */
69
+ export async function consumeStepUpJti(jti, expEpochSeconds) {
70
+ const ttlSeconds = Math.max(1, expEpochSeconds - Math.floor(Date.now() / 1000));
71
+ const client = redis();
72
+ if (client) {
73
+ // Redis configured ⇒ authoritative cross-instance store. SET NX EX → 'OK'
74
+ // when newly set, null when already consumed. A thrown Redis error is NOT
75
+ // caught here: it propagates so `requireStepUp` fails closed (STEP_UP_INVALID)
76
+ // instead of silently degrading to the per-instance mem guard.
77
+ const res = await client.set(`stepup:jti:${jti}`, '1', 'EX', ttlSeconds, 'NX');
78
+ return res === 'OK';
79
+ }
80
+ // No Redis ⇒ single-instance deployment; the process-local map IS the store.
81
+ if (memJti.has(jti))
82
+ return false;
83
+ memJti.set(jti, Date.now() + ttlSeconds * 1000);
84
+ return true;
85
+ }
86
+ /**
87
+ * Express middleware: require a valid, caller-bound, single-use step-up token in
88
+ * the `X-Step-Up-Token` header. Must run AFTER `requireAuth` (needs `req.user`).
89
+ */
90
+ export async function requireStepUp(req, res, next) {
91
+ // Verified internal service principals are EXEMPT: step-up is a re-verify-the-
92
+ // HUMAN gate (they replay a password-reverify token), which a service token
93
+ // structurally cannot produce. Services already bypass `requirePermission` on
94
+ // the same trust basis (a signed service JWT), and internal callers of a
95
+ // step-up-gated route — e.g. the platform org-cascade's `DELETE /quotas/:orgId`
96
+ // or the billing→quota entitlement sync — would otherwise be hard-blocked.
97
+ // Safe: a service token can only be minted with `JWT_SECRET`, so this can't be
98
+ // spoofed by an external client to skip step-up.
99
+ if (isServicePrincipal(req)) {
100
+ next();
101
+ return;
102
+ }
103
+ const callerSub = req.user?.sub;
104
+ if (!callerSub) {
105
+ sendError(res, 401, 'Authentication required', 'UNAUTHORIZED');
106
+ return;
107
+ }
108
+ const token = getHeaderString(req.headers[HEADER]);
109
+ if (!token) {
110
+ sendError(res, 401, 'Step-up verification required', 'STEP_UP_REQUIRED');
111
+ return;
112
+ }
113
+ let payload;
114
+ try {
115
+ payload = verifyStepUpToken(token);
116
+ }
117
+ catch {
118
+ sendError(res, 401, 'Step-up token invalid or expired', 'STEP_UP_INVALID');
119
+ return;
120
+ }
121
+ if (payload.sub !== callerSub) {
122
+ sendError(res, 401, 'Step-up token does not match session', 'STEP_UP_MISMATCH');
123
+ return;
124
+ }
125
+ try {
126
+ if (!(await consumeStepUpJti(payload.jti, payload.exp))) {
127
+ sendError(res, 401, 'Step-up token already used or expired', 'STEP_UP_REPLAY');
128
+ return;
129
+ }
130
+ }
131
+ catch (err) {
132
+ logger.error('Step-up jti consume errored', { error: String(err) });
133
+ sendError(res, 401, 'Step-up verification failed', 'STEP_UP_INVALID');
134
+ return;
135
+ }
136
+ next();
137
+ }
138
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RlcC11cC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9taWRkbGV3YXJlL3N0ZXAtdXAudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsK0NBQStDO0FBQy9DLHNDQUFzQztBQW9CdEMsT0FBTyxHQUFHLE1BQU0sY0FBYyxDQUFDO0FBQy9CLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLFdBQVcsQ0FBQztBQUMvQyxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUNoRSxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDdEQsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQ2xELE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUVqRCxNQUFNLE1BQU0sR0FBRyxZQUFZLENBQUMsU0FBUyxDQUFDLENBQUM7QUFDdkMsTUFBTSxNQUFNLEdBQUcsaUJBQWlCLENBQUM7QUFXakMsU0FBUyxZQUFZO0lBQ25CLE9BQU8sQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLGFBQWEsSUFBSSxPQUFPLENBQWtCLENBQUM7QUFDakUsQ0FBQztBQUVEOzs7Ozs7O0dBT0c7QUFDSCxNQUFNLFVBQVUsaUJBQWlCLENBQUMsS0FBYTtJQUM3QyxNQUFNLE1BQU0sR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDLFVBQVUsQ0FBQztJQUN0QyxJQUFJLENBQUMsTUFBTTtRQUFFLE1BQU0sSUFBSSxLQUFLLENBQUMsNkNBQTZDLENBQUMsQ0FBQztJQUM1RSxNQUFNLElBQUksR0FBc0IsRUFBRSxVQUFVLEVBQUUsQ0FBQyxZQUFZLEVBQUUsQ0FBQyxFQUFFLENBQUM7SUFFakUsSUFBSSxPQUEyQixDQUFDO0lBQ2hDLElBQUksQ0FBQztRQUNILE9BQU8sR0FBRyxHQUFHLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRSxNQUFNLEVBQUUsSUFBSSxDQUF1QixDQUFDO0lB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QkFBZ0IsQ0FBQyxDQUFDO1lBQy9FLE9BQU87UUFDVCxDQUFDO0lBQ0gsQ0FBQztJQUFDLE9BQU8sR0FBRyxFQUFFLENBQUM7UUFDYixNQUFNLENBQUMsS0FBSyxDQUFDLDZCQUE2QixFQUFFLEVBQUUsS0FBSyxFQUFFLE1BQU0sQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDcEUsU0FBUyxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsNkJBQTZCLEVBQUUsaUJBQWlCLENBQUMsQ0FBQztRQUN0RSxPQUFPO0lBQ1QsQ0FBQztJQUVELElBQUksRUFBRSxDQUFDO0FBQ1QsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIENvcHlyaWdodCAyMDI2IFBpcGVsaW5lIEJ1aWxkZXIgQ29udHJpYnV0b3JzXG4vLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuXG4vKipcbiAqIGByZXF1aXJlU3RlcFVwYCDigJQgYSBzaGFyZWQgc3RlcC11cCAocmVjZW50LXBhc3N3b3JkLXJldmVyaWZ5KSBnYXRlIGZvciB0aGVcbiAqIHN0YXRlbGVzcyBhcGkgc2VydmljZXMsIG1pcnJvcmluZyB0aGUgcGxhdGZvcm0gbWlkZGxld2FyZSBvZiB0aGUgc2FtZSBuYW1lLlxuICpcbiAqIEZsb3c6IHRoZSBVSSBoaXRzIGBQT1NUIC9hcGkvYXV0aC9zdGVwLXVwYCAocGxhdGZvcm0pIHRvIHJlLXZlcmlmeSB0aGUgdXNlcidzXG4gKiBwYXNzd29yZDsgcGxhdGZvcm0gbWludHMgYSBzaG9ydC1saXZlZCAoNjBzKSBgc3RlcC11cGAgSldUIHNpZ25lZCB3aXRoIHRoZVxuICogU0FNRSBgSldUX1NFQ1JFVGAgdGhlc2Ugc2VydmljZXMgYWxyZWFkeSB2ZXJpZnkgYWNjZXNzIHRva2VucyB3aXRoLiBUaGUgVUlcbiAqIHJlcGxheXMgaXQgYXMgdGhlIGBYLVN0ZXAtVXAtVG9rZW5gIGhlYWRlciBvbiB0aGUgZGVzdHJ1Y3RpdmUvc2Vuc2l0aXZlIGNhbGw7XG4gKiB0aGlzIG1pZGRsZXdhcmUgdmVyaWZpZXMgaXQsIGJpbmRzIGl0IHRvIHRoZSBjYWxsZXIgKGBzdWJgIG1hdGNoKSwgYW5kXG4gKiBjb25zdW1lcyBpdHMgYGp0aWAgb25jZSAoUmVkaXMtYmFja2VkIGNyb3NzLWluc3RhbmNlIHdoZW4gY29uZmlndXJlZCwgZWxzZSBhXG4gKiBib3VuZGVkIHByb2Nlc3MtbG9jYWwgc2V0KSBzbyBhIHJlcGxheSBpbnNpZGUgdGhlIFRUTCBpcyByZWplY3RlZC5cbiAqXG4gKiBXSFkgaGVyZTogcmVzdG9yZSBlbmRwb2ludHMgb24gdGhlIGFwaSBzZXJ2aWNlcyAocGlwZWxpbmUvcGx1Z2luLy4uLikgYXJlXG4gKiBzdGVwLXVwLWdhdGVkLCBidXQgc3RlcC11cCBwcmV2aW91c2x5IGxpdmVkIG9ubHkgaW4gYHBsYXRmb3JtYC4gVGhpcyBwb3J0cyB0aGVcbiAqIHZlcmlmeSArIHNpbmdsZS11c2UganRpIGludG8gYXBpLWNvcmUgc28gYW55IHNlcnZpY2UgY2FuIHJlcXVpcmUgaXQuXG4gKi9cblxuaW1wb3J0IHR5cGUgeyBSZXF1ZXN0LCBSZXNwb25zZSwgTmV4dEZ1bmN0aW9uIH0gZnJvbSAnZXhwcmVzcyc7XG5pbXBvcnQgand0IGZyb20gJ2pzb253ZWJ0b2tlbic7XG5pbXBvcnQgeyBpc1NlcnZpY2VQcmluY2lwYWwgfSBmcm9tICcuL2F1dGguanMnO1xuaW1wb3J0IHsgY3JlYXRlRW52UmVkaXNDbGllbnQgfSBmcm9tICcuLi9zZXJ2aWNlcy9lbnYtcmVkaXMuanMnO1xuaW1wb3J0IHsgZ2V0SGVhZGVyU3RyaW5nIH0gZnJvbSAnLi4vdXRpbHMvaGVhZGVycy5qcyc7XG5pbXBvcnQgeyBjcmVhdGVMb2dnZXIgfSBmcm9tICcuLi91dGlscy9sb2dnZXIuanMnO1xuaW1wb3J0IHsgc2VuZEVycm9yIH0gZnJvbSAnLi4vdXRpbHMvcmVzcG9uc2UuanMnO1xuXG5jb25zdCBsb2dnZXIgPSBjcmVhdGVMb2dnZXIoJ3N0ZXAtdXAnKTtcbmNvbnN0IEhFQURFUiA9ICd4LXN0ZXAtdXAtdG9rZW4nO1xuXG4vKiogUGF5bG9hZCBvZiBhIHBsYXRmb3JtLWlzc3VlZCBzdGVwLXVwIHRva2VuIChzZWUgcGxhdGZvcm0gaXNzdWVTdGVwVXBUb2tlbikuICovXG5leHBvcnQgaW50ZXJmYWNlIFN0ZXBVcFRva2VuUGF5bG9hZCB7XG4gIHR5cGU6ICdzdGVwLXVwJztcbiAgc3ViOiBzdHJpbmc7XG4gIGp0aTogc3RyaW5nO1xuICBpYXQ6IG51bWJlcjtcbiAgZXhwOiBudW1iZXI7XG59XG5cbmZ1bmN0aW9uIGp3dEFsZ29yaXRobSgpOiBqd3QuQWxnb3JpdGhtIHtcbiAgcmV0dXJuIChwcm9jZXNzLmVudi5KV1RfQUxHT1JJVEhNIHx8ICdIUzI1NicpIGFzIGp3dC5BbGdvcml0aG07XG59XG5cbi8qKlxuICogVmVyaWZ5IGEgc3RlcC11cCB0b2tlbjogdmFsaWQgc2lnbmF0dXJlIChwcmltYXJ5IGBKV1RfU0VDUkVUYCwgb3JcbiAqIGBKV1RfU0VDUkVUX1BSRVZJT1VTYCBkdXJpbmcgYSByb3RhdGlvbiksIGFsbG93LWxpc3RlZCBhbGdvcml0aG0sIEFORCB0aGVcbiAqIGB0eXBlOiAnc3RlcC11cCdgICsgYGp0aWAgY2xhaW1zIOKAlCBhIHBsYWluIGFjY2VzcyB0b2tlbiBzaGFyZXMgdGhlIHNlY3JldCBhbmRcbiAqIGBzdWJgLCBzbyB3aXRob3V0IGFzc2VydGluZyB0aGUgc3RlcC11cCBzaGFwZSBpdCB3b3VsZCBieXBhc3MgdGhlIGdhdGUuXG4gKiBUaHJvd3Mgb24gYW55IGZhaWx1cmUuIERvZXMgTk9UIGJpbmQgdG8gYSBjYWxsZXIg4oCUIGByZXF1aXJlU3RlcFVwYCBkb2VzIHRoZVxuICogYHN1YmAgbWF0Y2ggYW5kIHNpbmdsZS11c2UgY29uc3VtZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHZlcmlmeVN0ZXBVcFRva2VuKHRva2VuOiBzdHJpbmcpOiBTdGVwVXBUb2tlblBheWxvYWQge1xuICBjb25zdCBzZWNyZXQgPSBwcm9jZXNzLmVudi5KV1RfU0VDUkVUO1xuICBpZiAoIXNlY3JldCkgdGhyb3cgbmV3IEVycm9yKCdKV1RfU0VDUkVUIGVudmlyb25tZW50IHZhcmlhYmxlIGlzIHJlcXVpcmVkJyk7XG4gIGNvbnN0IG9wdHM6IGp3dC5WZXJpZnlPcHRpb25zID0geyBhbGdvcml0aG1zOiBband0QWxnb3JpdGhtKCldIH07XG5cbiAgbGV0IHBheWxvYWQ6IFN0ZXBVcFRva2VuUGF5bG9hZDtcbiAgdHJ5IHtcbiAgICBwYXlsb2FkID0gand0LnZlcmlmeSh0b2tlbiwgc2VjcmV0LCBvcHRzKSBhcyBTdGVwVXBUb2tlblBheWxvYWQ7XG4gIH0gY2F0Y2ggKHByaW1hcnlFcnIpIHtcbiAgICBjb25zdCBwcmV2aW91cyA9IHByb2Nlc3MuZW52LkpXVF9TRUNSRVRfUFJFVklPVVM7XG4gICAgaWYgKCFwcmV2aW91cykgdGhyb3cgcHJpbWFyeUVycjtcbiAgICBwYXlsb2FkID0gand0LnZlcmlmeSh0b2tlbiwgcHJldmlvdXMsIG9wdHMpIGFzIFN0ZXBVcFRva2VuUGF5bG9hZDtcbiAgfVxuXG4gIGlmIChwYXlsb2FkLnR5cGUgIT09ICdzdGVwLXVwJyB8fCAhcGF5bG9hZC5qdGkgfHwgIXBheWxvYWQuc3ViKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdJTlZBTElEX1NURVBfVVBfVE9LRU4nKTtcbiAgfVxuICByZXR1cm4gcGF5bG9hZDtcbn1cblxuLy8gLS0gU2luZ2xlLXVzZSBqdGkgc3RvcmUgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbi8vIFJlZGlzIFNFVCBOWCBFWCB3aGVuIFJlZGlzIGlzIGNvbmZpZ3VyZWQgKGNyb3NzLWluc3RhbmNlIHNpbmdsZS11c2UpOyBlbHNlIGFcbi8vIGJvdW5kZWQgcHJvY2Vzcy1sb2NhbCBtYXAgd2l0aCBUVEwgc3dlZXAgKHBlci1wcm9jZXNzKS4gQSBzdGVwLXVwIHRva2VuIGlzXG4vLyA2MHMtbGl2ZWQuIFdoZW4gUmVkaXMgSVMgY29uZmlndXJlZCBpdCdzIHRoZSBhdXRob3JpdGF0aXZlIHN0b3JlOiBhIFJlZGlzXG4vLyBlcnJvciBGQUlMUyBDTE9TRUQgKHRocm93cyDihpIgU1RFUF9VUF9JTlZBTElELCB1c2VyIHJlLXZlcmlmaWVzKSByYXRoZXIgdGhhblxuLy8gZGVncmFkaW5nIHRvIHRoZSBwZXItaW5zdGFuY2UgbWVtIGd1YXJkLCB3aGljaCB3b3VsZCBhY2NlcHQgYSBjcm9zcy1pbnN0YW5jZVxuLy8gcmVwbGF5IGR1cmluZyB0aGUgb3V0YWdlLiBUaGUgbWVtIGd1YXJkIGlzIHVzZWQgT05MWSB3aGVuIFJlZGlzIGlzIGFic2VudC5cblxubGV0IF9yZWRpczogeyBzZXQ6ICguLi5hcmdzOiB1bmtub3duW10pID0+IFByb21pc2U8dW5rbm93bj4gfSB8IG51bGwgfCB1bmRlZmluZWQ7XG5mdW5jdGlvbiByZWRpcygpOiB7IHNldDogKC4uLmFyZ3M6IHVua25vd25bXSkgPT4gUHJvbWlzZTx1bmtub3duPiB9IHwgbnVsbCB7XG4gIGlmIChfcmVkaXMgPT09IHVuZGVmaW5lZCkge1xuICAgIF9yZWRpcyA9IGNyZWF0ZUVudlJlZGlzQ2xpZW50PHsgc2V0OiAoLi4uYXJnczogdW5rbm93bltdKSA9PiBQcm9taXNlPHVua25vd24+IH0+KCdzdGVwLXVwLWp0aScpO1xuICB9XG4gIHJldHVybiBfcmVkaXM7XG59XG5cbmNvbnN0IG1lbUp0aSA9IG5ldyBNYXA8c3RyaW5nLCBudW1iZXI+KCk7IC8vIGp0aSAtPiBleHBpcnkgZXBvY2ggbXNcbmNvbnN0IG1lbVN3ZWVwID0gc2V0SW50ZXJ2YWwoKCkgPT4ge1xuICBjb25zdCBub3cgPSBEYXRlLm5vdygpO1xuICBmb3IgKGNvbnN0IFtqdGksIGV4cF0gb2YgbWVtSnRpKSBpZiAoZXhwIDw9IG5vdykgbWVtSnRpLmRlbGV0ZShqdGkpO1xufSwgMzBfMDAwKTtcbm1lbVN3ZWVwLnVucmVmPy4oKTtcblxuLyoqXG4gKiBDb25zdW1lIGEgc3RlcC11cCBganRpYCBleGFjdGx5IG9uY2UuIFJldHVybnMgdHJ1ZSBpZiB0aGlzIGNhbGwgY2xhaW1lZCBpdFxuICogKGZpcnN0IHVzZSksIGZhbHNlIGlmIGl0IHdhcyBhbHJlYWR5IGNvbnN1bWVkIChyZXBsYXkpLiBgZXhwRXBvY2hTZWNvbmRzYCBpc1xuICogdGhlIHRva2VuJ3MgYGV4cGAsIHNvIHRoZSBtYXJrZXIgYXV0by1leHBpcmVzIHdpdGggdGhlIHRva2VuLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY29uc3VtZVN0ZXBVcEp0aShqdGk6IHN0cmluZywgZXhwRXBvY2hTZWNvbmRzOiBudW1iZXIpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgY29uc3QgdHRsU2Vjb25kcyA9IE1hdGgubWF4KDEsIGV4cEVwb2NoU2Vjb25kcyAtIE1hdGguZmxvb3IoRGF0ZS5ub3coKSAvIDEwMDApKTtcbiAgY29uc3QgY2xpZW50ID0gcmVkaXMoKTtcbiAgaWYgKGNsaWVudCkge1xuICAgIC8vIFJlZGlzIGNvbmZpZ3VyZWQg4oeSIGF1dGhvcml0YXRpdmUgY3Jvc3MtaW5zdGFuY2Ugc3RvcmUuIFNFVCBOWCBFWCDihpIgJ09LJ1xuICAgIC8vIHdoZW4gbmV3bHkgc2V0LCBudWxsIHdoZW4gYWxyZWFkeSBjb25zdW1lZC4gQSB0aHJvd24gUmVkaXMgZXJyb3IgaXMgTk9UXG4gICAgLy8gY2F1Z2h0IGhlcmU6IGl0IHByb3BhZ2F0ZXMgc28gYHJlcXVpcmVTdGVwVXBgIGZhaWxzIGNsb3NlZCAoU1RFUF9VUF9JTlZBTElEKVxuICAgIC8vIGluc3RlYWQgb2Ygc2lsZW50bHkgZGVncmFkaW5nIHRvIHRoZSBwZXItaW5zdGFuY2UgbWVtIGd1YXJkLlxuICAgIGNvbnN0IHJlcyA9IGF3YWl0IGNsaWVudC5zZXQoYHN0ZXB1cDpqdGk6JHtqdGl9YCwgJzEnLCAnRVgnLCB0dGxTZWNvbmRzLCAnTlgnKTtcbiAgICByZXR1cm4gcmVzID09PSAnT0snO1xuICB9XG4gIC8vIE5vIFJlZGlzIOKHkiBzaW5nbGUtaW5zdGFuY2UgZGVwbG95bWVudDsgdGhlIHByb2Nlc3MtbG9jYWwgbWFwIElTIHRoZSBzdG9yZS5cbiAgaWYgKG1lbUp0aS5oYXMoanRpKSkgcmV0dXJuIGZhbHNlO1xuICBtZW1KdGkuc2V0KGp0aSwgRGF0ZS5ub3coKSArIHR0bFNlY29uZHMgKiAxMDAwKTtcbiAgcmV0dXJuIHRydWU7XG59XG5cbi8qKlxuICogRXhwcmVzcyBtaWRkbGV3YXJlOiByZXF1aXJlIGEgdmFsaWQsIGNhbGxlci1ib3VuZCwgc2luZ2xlLXVzZSBzdGVwLXVwIHRva2VuIGluXG4gKiB0aGUgYFgtU3RlcC1VcC1Ub2tlbmAgaGVhZGVyLiBNdXN0IHJ1biBBRlRFUiBgcmVxdWlyZUF1dGhgIChuZWVkcyBgcmVxLnVzZXJgKS5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHJlcXVpcmVTdGVwVXAocmVxOiBSZXF1ZXN0LCByZXM6IFJlc3BvbnNlLCBuZXh0OiBOZXh0RnVuY3Rpb24pOiBQcm9taXNlPHZvaWQ+IHtcbiAgLy8gVmVyaWZpZWQgaW50ZXJuYWwgc2VydmljZSBwcmluY2lwYWxzIGFyZSBFWEVNUFQ6IHN0ZXAtdXAgaXMgYSByZS12ZXJpZnktdGhlLVxuICAvLyBIVU1BTiBnYXRlICh0aGV5IHJlcGxheSBhIHBhc3N3b3JkLXJldmVyaWZ5IHRva2VuKSwgd2hpY2ggYSBzZXJ2aWNlIHRva2VuXG4gIC8vIHN0cnVjdHVyYWxseSBjYW5ub3QgcHJvZHVjZS4gU2VydmljZXMgYWxyZWFkeSBieXBhc3MgYHJlcXVpcmVQZXJtaXNzaW9uYCBvblxuICAvLyB0aGUgc2FtZSB0cnVzdCBiYXNpcyAoYSBzaWduZWQgc2VydmljZSBKV1QpLCBhbmQgaW50ZXJuYWwgY2FsbGVycyBvZiBhXG4gIC8vIHN0ZXAtdXAtZ2F0ZWQgcm91dGUg4oCUIGUuZy4gdGhlIHBsYXRmb3JtIG9yZy1jYXNjYWRlJ3MgYERFTEVURSAvcXVvdGFzLzpvcmdJZGBcbiAgLy8gb3IgdGhlIGJpbGxpbmfihpJxdW90YSBlbnRpdGxlbWVudCBzeW5jIOKAlCB3b3VsZCBvdGhlcndpc2UgYmUgaGFyZC1ibG9ja2VkLlxuICAvLyBTYWZlOiBhIHNlcnZpY2UgdG9rZW4gY2FuIG9ubHkgYmUgbWludGVkIHdpdGggYEpXVF9TRUNSRVRgLCBzbyB0aGlzIGNhbid0IGJlXG4gIC8vIHNwb29mZWQgYnkgYW4gZXh0ZXJuYWwgY2xpZW50IHRvIHNraXAgc3RlcC11cC5cbiAgaWYgKGlzU2VydmljZVByaW5jaXBhbChyZXEpKSB7XG4gICAgbmV4dCgpO1xuICAgIHJldHVybjtcbiAgfVxuXG4gIGNvbnN0IGNhbGxlclN1YiA9IChyZXEgYXMgUmVxdWVzdCAmIHsgdXNlcj86IHsgc3ViPzogc3RyaW5nIH0gfSkudXNlcj8uc3ViO1xuICBpZiAoIWNhbGxlclN1Yikge1xuICAgIHNlbmRFcnJvcihyZXMsIDQwMSwgJ0F1dGhlbnRpY2F0aW9uIHJlcXVpcmVkJywgJ1VOQVVUSE9SSVpFRCcpO1xuICAgIHJldHVybjtcbiAgfVxuXG4gIGNvbnN0IHRva2VuID0gZ2V0SGVhZGVyU3RyaW5nKHJlcS5oZWFkZXJzW0hFQURFUl0pO1xuICBpZiAoIXRva2VuKSB7XG4gICAgc2VuZEVycm9yKHJlcywgNDAxLCAnU3RlcC11cCB2ZXJpZmljYXRpb24gcmVxdWlyZWQnLCAnU1RFUF9VUF9SRVFVSVJFRCcpO1xuICAgIHJldHVybjtcbiAgfVxuXG4gIGxldCBwYXlsb2FkOiBTdGVwVXBUb2tlblBheWxvYWQ7XG4gIHRyeSB7XG4gICAgcGF5bG9hZCA9IHZlcmlmeVN0ZXBVcFRva2VuKHRva2VuKTtcbiAgfSBjYXRjaCB7XG4gICAgc2VuZEVycm9yKHJlcywgNDAxLCAnU3RlcC11cCB0b2tlbiBpbnZhbGlkIG9yIGV4cGlyZWQnLCAnU1RFUF9VUF9JTlZBTElEJyk7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgaWYgKHBheWxvYWQuc3ViICE9PSBjYWxsZXJTdWIpIHtcbiAgICBzZW5kRXJyb3IocmVzLCA0MDEsICdTdGVwLXVwIHRva2VuIGRvZXMgbm90IG1hdGNoIHNlc3Npb24nLCAnU1RFUF9VUF9NSVNNQVRDSCcpO1xuICAgIHJldHVybjtcbiAgfVxuXG4gIHRyeSB7XG4gICAgaWYgKCEoYXdhaXQgY29uc3VtZVN0ZXBVcEp0aShwYXlsb2FkLmp0aSwgcGF5bG9hZC5leHApKSkge1xuICAgICAgc2VuZEVycm9yKHJlcywgNDAxLCAnU3RlcC11cCB0b2tlbiBhbHJlYWR5IHVzZWQgb3IgZXhwaXJlZCcsICdTVEVQX1VQX1JFUExBWScpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgfSBjYXRjaCAoZXJyKSB7XG4gICAgbG9nZ2VyLmVycm9yKCdTdGVwLXVwIGp0aSBjb25zdW1lIGVycm9yZWQnLCB7IGVycm9yOiBTdHJpbmcoZXJyKSB9KTtcbiAgICBzZW5kRXJyb3IocmVzLCA0MDEsICdTdGVwLXVwIHZlcmlmaWNhdGlvbiBmYWlsZWQnLCAnU1RFUF9VUF9JTlZBTElEJyk7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgbmV4dCgpO1xufVxuIl19
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,6 @@
1
+ // Copyright 2026 Pipeline Builder Contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi';
4
+ import { z } from 'zod';
5
+ extendZodWithOpenApi(z);
6
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXh0ZW5kLXpvZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9vcGVuYXBpL2V4dGVuZC16b2QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsK0NBQStDO0FBQy9DLHNDQUFzQztBQUV0QyxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUN0RSxPQUFPLEVBQUUsQ0FBQyxFQUFFLE1BQU0sS0FBSyxDQUFDO0FBRXhCLG9CQUFvQixDQUFDLENBQUMsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbmltcG9ydCB7IGV4dGVuZFpvZFdpdGhPcGVuQXBpIH0gZnJvbSAnQGFzdGVhc29sdXRpb25zL3pvZC10by1vcGVuYXBpJztcbmltcG9ydCB7IHogfSBmcm9tICd6b2QnO1xuXG5leHRlbmRab2RXaXRoT3BlbkFwaSh6KTtcbiJdfQ==
@@ -0,0 +1,2 @@
1
+ export { registry, generateOpenApiSpec } from './registry.js';
2
+ export type { OpenApiSpecOptions } from './registry.js';
@@ -0,0 +1,5 @@
1
+ // Copyright 2026 Pipeline Builder Contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ // Public API
4
+ export { registry, generateOpenApiSpec } from './registry.js';
5
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvb3BlbmFwaS9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDLGFBQWE7QUFDYixPQUFPLEVBQUUsUUFBUSxFQUFFLG1CQUFtQixFQUFFLE1BQU0sZUFBZSxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbi8vIFB1YmxpYyBBUElcbmV4cG9ydCB7IHJlZ2lzdHJ5LCBnZW5lcmF0ZU9wZW5BcGlTcGVjIH0gZnJvbSAnLi9yZWdpc3RyeS5qcyc7XG5leHBvcnQgdHlwZSB7IE9wZW5BcGlTcGVjT3B0aW9ucyB9IGZyb20gJy4vcmVnaXN0cnkuanMnO1xuIl19
@@ -0,0 +1,17 @@
1
+ import { OpenAPIRegistry, OpenApiGeneratorV31 } from '@asteasolutions/zod-to-openapi';
2
+ /** Shared OpenAPI registry for all schema and path registrations. */
3
+ export declare const registry: OpenAPIRegistry;
4
+ /** Options for customizing the generated OpenAPI spec. */
5
+ export interface OpenApiSpecOptions {
6
+ title?: string;
7
+ version?: string;
8
+ description?: string;
9
+ serverUrl?: string;
10
+ }
11
+ /**
12
+ * Generate the complete OpenAPI 3.1.0 specification document.
13
+ *
14
+ * @param options - Optional overrides for spec metadata
15
+ * @returns OpenAPI 3.1 specification object
16
+ */
17
+ export declare function generateOpenApiSpec(options?: OpenApiSpecOptions): ReturnType<OpenApiGeneratorV31['generateDocument']>;
@@ -0,0 +1,38 @@
1
+ // Copyright 2026 Pipeline Builder Contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { OpenAPIRegistry, OpenApiGeneratorV31 } from '@asteasolutions/zod-to-openapi';
4
+ import { registerAllRoutes } from './routes/index.js';
5
+ import { registerSchemas } from './schema-registry.js';
6
+ /** Shared OpenAPI registry for all schema and path registrations. */
7
+ export const registry = new OpenAPIRegistry();
8
+ let _initialized = false;
9
+ /** Execute all registrations once. */
10
+ function ensureInitialized() {
11
+ if (_initialized)
12
+ return;
13
+ _initialized = true;
14
+ registerSchemas();
15
+ registerAllRoutes();
16
+ }
17
+ /**
18
+ * Generate the complete OpenAPI 3.1.0 specification document.
19
+ *
20
+ * @param options - Optional overrides for spec metadata
21
+ * @returns OpenAPI 3.1 specification object
22
+ */
23
+ export function generateOpenApiSpec(options) {
24
+ ensureInitialized();
25
+ const generator = new OpenApiGeneratorV31(registry.definitions);
26
+ return generator.generateDocument({
27
+ openapi: '3.1.0',
28
+ info: {
29
+ title: options?.title ?? 'Pipeline Builder API',
30
+ version: options?.version ?? '1.0.0',
31
+ description: options?.description ?? 'CI/CD Pipeline Builder Platform API — manage pipelines, plugins, messages, quotas, and billing.',
32
+ },
33
+ servers: [
34
+ { url: options?.serverUrl ?? '/api', description: 'API base path' },
35
+ ],
36
+ });
37
+ }
38
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVnaXN0cnkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvb3BlbmFwaS9yZWdpc3RyeS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDLE9BQU8sRUFBRSxlQUFlLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUV0RixPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQUN0RCxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFFdkQscUVBQXFFO0FBQ3JFLE1BQU0sQ0FBQyxNQUFNLFFBQVEsR0FBRyxJQUFJLGVBQWUsRUFBRSxDQUFDO0FBVTlDLElBQUksWUFBWSxHQUFHLEtBQUssQ0FBQztBQUV6QixzQ0FBc0M7QUFDdEMsU0FBUyxpQkFBaUI7SUFDeEIsSUFBSSxZQUFZO1FBQUUsT0FBTztJQUN6QixZQUFZLEdBQUcsSUFBSSxDQUFDO0lBQ3BCLGVBQWUsRUFBRSxDQUFDO0lBQ2xCLGlCQUFpQixFQUFFLENBQUM7QUFDdEIsQ0FBQztBQUVEOzs7OztHQUtHO0FBQ0gsTUFBTSxVQUFVLG1CQUFtQixDQUNqQyxPQUE0QjtJQUU1QixpQkFBaUIsRUFBRSxDQUFDO0lBRXBCLE1BQU0sU0FBUyxHQUFHLElBQUksbUJBQW1CLENBQUMsUUFBUSxDQUFDLFdBQVcsQ0FBQyxDQUFDO0lBQ2hFLE9BQU8sU0FBUyxDQUFDLGdCQUFnQixDQUFDO1FBQ2hDLE9BQU8sRUFBRSxPQUFPO1FBQ2hCLElBQUksRUFBRTtZQUNKLEtBQUssRUFBRSxPQUFPLEVBQUUsS0FBSyxJQUFJLHNCQUFzQjtZQUMvQyxPQUFPLEVBQUUsT0FBTyxFQUFFLE9BQU8sSUFBSSxPQUFPO1lBQ3BDLFdBQVcsRUFBRSxPQUFPLEVBQUUsV0FBVyxJQUFJLGlHQUFpRztTQUN2STtRQUNELE9BQU8sRUFBRTtZQUNQLEVBQUUsR0FBRyxFQUFFLE9BQU8sRUFBRSxTQUFTLElBQUksTUFBTSxFQUFFLFdBQVcsRUFBRSxlQUFlLEVBQUU7U0FDcEU7S0FDRixDQUFDLENBQUM7QUFDTCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbmltcG9ydCB7IE9wZW5BUElSZWdpc3RyeSwgT3BlbkFwaUdlbmVyYXRvclYzMSB9IGZyb20gJ0Bhc3RlYXNvbHV0aW9ucy96b2QtdG8tb3BlbmFwaSc7XG5cbmltcG9ydCB7IHJlZ2lzdGVyQWxsUm91dGVzIH0gZnJvbSAnLi9yb3V0ZXMvaW5kZXguanMnO1xuaW1wb3J0IHsgcmVnaXN0ZXJTY2hlbWFzIH0gZnJvbSAnLi9zY2hlbWEtcmVnaXN0cnkuanMnO1xuXG4vKiogU2hhcmVkIE9wZW5BUEkgcmVnaXN0cnkgZm9yIGFsbCBzY2hlbWEgYW5kIHBhdGggcmVnaXN0cmF0aW9ucy4gKi9cbmV4cG9ydCBjb25zdCByZWdpc3RyeSA9IG5ldyBPcGVuQVBJUmVnaXN0cnkoKTtcblxuLyoqIE9wdGlvbnMgZm9yIGN1c3RvbWl6aW5nIHRoZSBnZW5lcmF0ZWQgT3BlbkFQSSBzcGVjLiAqL1xuZXhwb3J0IGludGVyZmFjZSBPcGVuQXBpU3BlY09wdGlvbnMge1xuICB0aXRsZT86IHN0cmluZztcbiAgdmVyc2lvbj86IHN0cmluZztcbiAgZGVzY3JpcHRpb24/OiBzdHJpbmc7XG4gIHNlcnZlclVybD86IHN0cmluZztcbn1cblxubGV0IF9pbml0aWFsaXplZCA9IGZhbHNlO1xuXG4vKiogRXhlY3V0ZSBhbGwgcmVnaXN0cmF0aW9ucyBvbmNlLiAqL1xuZnVuY3Rpb24gZW5zdXJlSW5pdGlhbGl6ZWQoKTogdm9pZCB7XG4gIGlmIChfaW5pdGlhbGl6ZWQpIHJldHVybjtcbiAgX2luaXRpYWxpemVkID0gdHJ1ZTtcbiAgcmVnaXN0ZXJTY2hlbWFzKCk7XG4gIHJlZ2lzdGVyQWxsUm91dGVzKCk7XG59XG5cbi8qKlxuICogR2VuZXJhdGUgdGhlIGNvbXBsZXRlIE9wZW5BUEkgMy4xLjAgc3BlY2lmaWNhdGlvbiBkb2N1bWVudC5cbiAqXG4gKiBAcGFyYW0gb3B0aW9ucyAtIE9wdGlvbmFsIG92ZXJyaWRlcyBmb3Igc3BlYyBtZXRhZGF0YVxuICogQHJldHVybnMgT3BlbkFQSSAzLjEgc3BlY2lmaWNhdGlvbiBvYmplY3RcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdlbmVyYXRlT3BlbkFwaVNwZWMoXG4gIG9wdGlvbnM/OiBPcGVuQXBpU3BlY09wdGlvbnMsXG4pOiBSZXR1cm5UeXBlPE9wZW5BcGlHZW5lcmF0b3JWMzFbJ2dlbmVyYXRlRG9jdW1lbnQnXT4ge1xuICBlbnN1cmVJbml0aWFsaXplZCgpO1xuXG4gIGNvbnN0IGdlbmVyYXRvciA9IG5ldyBPcGVuQXBpR2VuZXJhdG9yVjMxKHJlZ2lzdHJ5LmRlZmluaXRpb25zKTtcbiAgcmV0dXJuIGdlbmVyYXRvci5nZW5lcmF0ZURvY3VtZW50KHtcbiAgICBvcGVuYXBpOiAnMy4xLjAnLFxuICAgIGluZm86IHtcbiAgICAgIHRpdGxlOiBvcHRpb25zPy50aXRsZSA/PyAnUGlwZWxpbmUgQnVpbGRlciBBUEknLFxuICAgICAgdmVyc2lvbjogb3B0aW9ucz8udmVyc2lvbiA/PyAnMS4wLjAnLFxuICAgICAgZGVzY3JpcHRpb246IG9wdGlvbnM/LmRlc2NyaXB0aW9uID8/ICdDSS9DRCBQaXBlbGluZSBCdWlsZGVyIFBsYXRmb3JtIEFQSSDigJQgbWFuYWdlIHBpcGVsaW5lcywgcGx1Z2lucywgbWVzc2FnZXMsIHF1b3RhcywgYW5kIGJpbGxpbmcuJyxcbiAgICB9LFxuICAgIHNlcnZlcnM6IFtcbiAgICAgIHsgdXJsOiBvcHRpb25zPy5zZXJ2ZXJVcmwgPz8gJy9hcGknLCBkZXNjcmlwdGlvbjogJ0FQSSBiYXNlIHBhdGgnIH0sXG4gICAgXSxcbiAgfSk7XG59XG4iXX0=
@@ -0,0 +1,7 @@
1
+ /**
2
+ * OpenAPI definitions for the "Ask" assistant service (api/ask), mounted at
3
+ * `/ask`. All routes are gated by the `ai_generation` feature and are READ /
4
+ * PROPOSE only — the agent never mutates; `propose_*` tool results are drafts the
5
+ * user confirms in the UI.
6
+ */
7
+ export declare function registerAskRoutes(): void;
@@ -0,0 +1,62 @@
1
+ // Copyright 2026 Pipeline Builder Contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { registry } from '../registry.js';
4
+ const tags = ['Ask'];
5
+ const auth = [{ bearerAuth: [] }];
6
+ /**
7
+ * OpenAPI definitions for the "Ask" assistant service (api/ask), mounted at
8
+ * `/ask`. All routes are gated by the `ai_generation` feature and are READ /
9
+ * PROPOSE only — the agent never mutates; `propose_*` tool results are drafts the
10
+ * user confirms in the UI.
11
+ */
12
+ export function registerAskRoutes() {
13
+ registry.registerPath({
14
+ method: 'get',
15
+ path: '/ask/providers',
16
+ summary: 'List configured AI providers',
17
+ description: 'The AI providers/models available to the Ask assistant (from server env — cloud keys and/or the OpenAI-compatible local endpoint).',
18
+ tags,
19
+ security: auth,
20
+ responses: { 200: { description: 'Configured providers' }, 403: { description: 'ai_generation feature required' } },
21
+ });
22
+ registry.registerPath({
23
+ method: 'post',
24
+ path: '/ask',
25
+ summary: 'Grounded how-to answer (non-streaming)',
26
+ description: 'Answer a platform how-to question grounded in the docs corpus. Reserves one aiCalls quota slot.',
27
+ tags,
28
+ security: auth,
29
+ responses: {
30
+ 200: { description: 'Answer with grounded sources' },
31
+ 403: { description: 'ai_generation feature required' },
32
+ 429: { description: 'aiCalls quota exceeded or rate limited' },
33
+ },
34
+ });
35
+ registry.registerPath({
36
+ method: 'post',
37
+ path: '/ask/stream',
38
+ summary: 'Grounded how-to answer (SSE stream)',
39
+ description: 'Same as POST /ask but streams the grounded sources then answer tokens as Server-Sent Events.',
40
+ tags,
41
+ security: auth,
42
+ responses: {
43
+ 200: { description: 'text/event-stream of sources + answer tokens' },
44
+ 403: { description: 'ai_generation feature required' },
45
+ 429: { description: 'aiCalls quota exceeded or rate limited' },
46
+ },
47
+ });
48
+ registry.registerPath({
49
+ method: 'post',
50
+ path: '/ask/agent/stream',
51
+ summary: 'Tool-calling Ask agent (SSE stream)',
52
+ description: 'Runs the tool-calling assistant loop and streams reasoning tokens, tool-call events, and reviewable `propose_*` drafts (pipeline/plugin/template) as SSE. Read-only: proposals are confirmed separately in the UI.',
53
+ tags,
54
+ security: auth,
55
+ responses: {
56
+ 200: { description: 'text/event-stream of tokens, tool-calls, and proposal drafts' },
57
+ 403: { description: 'ai_generation feature required' },
58
+ 429: { description: 'aiCalls quota exceeded or rate limited' },
59
+ },
60
+ });
61
+ }
62
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXNrLXJvdXRlcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9vcGVuYXBpL3JvdXRlcy9hc2stcm91dGVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUFFdEMsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRTFDLE1BQU0sSUFBSSxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUM7QUFDckIsTUFBTSxJQUFJLEdBQUcsQ0FBQyxFQUFFLFVBQVUsRUFBRSxFQUFFLEVBQUUsQ0FBQyxDQUFDO0FBRWxDOzs7OztHQUtHO0FBQ0gsTUFBTSxVQUFVLGlCQUFpQjtJQUMvQixRQUFRLENBQUMsWUFBWSxDQUFDO1FBQ3BCLE1BQU0sRUFBRSxLQUFLO1FBQ2IsSUFBSSxFQUFFLGdCQUFnQjtRQUN0QixPQUFPLEVBQUUsOEJBQThCO1FBQ3ZDLFdBQVcsRUFBRSxvSUFBb0k7UUFDakosSUFBSTtRQUNKLFFBQVEsRUFBRSxJQUFJO1FBQ2QsU0FBUyxFQUFFLEVBQUUsR0FBRyxFQUFFLEVBQUUsV0FBVyxFQUFFLHNCQUFzQixFQUFFLEVBQUUsR0FBRyxFQUFFLEVBQUUsV0FBVyxFQUFFLGdDQUFnQyxFQUFFLEVBQUU7S0FDcEgsQ0FBQyxDQUFDO0lBRUgsUUFBUSxDQUFDLFlBQVksQ0FBQztRQUNwQixNQUFNLEVBQUUsTUFBTTtRQUNkLElBQUksRUFBRSxNQUFNO1FBQ1osT0FBTyxFQUFFLHdDQUF3QztRQUNqRCxXQUFXLEVBQUUsaUdBQWlHO1FBQzlHLElBQUk7UUFDSixRQUFRLEVBQUUsSUFBSTtRQUNkLFNBQVMsRUFBRTtZQUNULEdBQUcsRUFBRSxFQUFFLFdBQVcsRUFBRSw4QkFBOEIsRUFBRTtZQUNwRCxHQUFHLEVBQUUsRUFBRSxXQUFXLEVBQUUsZ0NBQWdDLEVBQUU7WUFDdEQsR0FBRyxFQUFFLEVBQUUsV0FBVyxFQUFFLHdDQUF3QyxFQUFFO1NBQy9EO0tBQ0YsQ0FBQyxDQUFDO0lBRUgsUUFBUSxDQUFDLFlBQVksQ0FBQztRQUNwQixNQUFNLEVBQUUsTUFBTTtRQUNkLElBQUksRUFBRSxhQUFhO1FBQ25CLE9BQU8sRUFBRSxxQ0FBcUM7UUFDOUMsV0FBVyxFQUFFLDhGQUE4RjtRQUMzRyxJQUFJO1FBQ0osUUFBUSxFQUFFLElBQUk7UUFDZCxTQUFTLEVBQUU7WUFDVCxHQUFHLEVBQUUsRUFBRSxXQUFXLEVBQUUsOENBQThDLEVBQUU7WUFDcEUsR0FBRyxFQUFFLEVBQUUsV0FBVyxFQUFFLGdDQUFnQyxFQUFFO1lBQ3RELEdBQUcsRUFBRSxFQUFFLFdBQVcsRUFBRSx3Q0FBd0MsRUFBRTtTQUMvRDtLQUNGLENBQUMsQ0FBQztJQUVILFFBQVEsQ0FBQyxZQUFZLENBQUM7UUFDcEIsTUFBTSxFQUFFLE1BQU07UUFDZCxJQUFJLEVBQUUsbUJBQW1CO1FBQ3pCLE9BQU8sRUFBRSxxQ0FBcUM7UUFDOUMsV0FBVyxFQUFFLG9OQUFvTjtRQUNqTyxJQUFJO1FBQ0osUUFBUSxFQUFFLElBQUk7UUFDZCxTQUFTLEVBQUU7WUFDVCxHQUFHLEVBQUUsRUFBRSxXQUFXLEVBQUUsOERBQThELEVBQUU7WUFDcEYsR0FBRyxFQUFFLEVBQUUsV0FBVyxFQUFFLGdDQUFnQyxFQUFFO1lBQ3RELEdBQUcsRUFBRSxFQUFFLFdBQVcsRUFBRSx3Q0FBd0MsRUFBRTtTQUMvRDtLQUNGLENBQUMsQ0FBQztBQUNMLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuaW1wb3J0IHsgcmVnaXN0cnkgfSBmcm9tICcuLi9yZWdpc3RyeS5qcyc7XG5cbmNvbnN0IHRhZ3MgPSBbJ0FzayddO1xuY29uc3QgYXV0aCA9IFt7IGJlYXJlckF1dGg6IFtdIH1dO1xuXG4vKipcbiAqIE9wZW5BUEkgZGVmaW5pdGlvbnMgZm9yIHRoZSBcIkFza1wiIGFzc2lzdGFudCBzZXJ2aWNlIChhcGkvYXNrKSwgbW91bnRlZCBhdFxuICogYC9hc2tgLiBBbGwgcm91dGVzIGFyZSBnYXRlZCBieSB0aGUgYGFpX2dlbmVyYXRpb25gIGZlYXR1cmUgYW5kIGFyZSBSRUFEIC9cbiAqIFBST1BPU0Ugb25seSDigJQgdGhlIGFnZW50IG5ldmVyIG11dGF0ZXM7IGBwcm9wb3NlXypgIHRvb2wgcmVzdWx0cyBhcmUgZHJhZnRzIHRoZVxuICogdXNlciBjb25maXJtcyBpbiB0aGUgVUkuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZWdpc3RlckFza1JvdXRlcygpOiB2b2lkIHtcbiAgcmVnaXN0cnkucmVnaXN0ZXJQYXRoKHtcbiAgICBtZXRob2Q6ICdnZXQnLFxuICAgIHBhdGg6ICcvYXNrL3Byb3ZpZGVycycsXG4gICAgc3VtbWFyeTogJ0xpc3QgY29uZmlndXJlZCBBSSBwcm92aWRlcnMnLFxuICAgIGRlc2NyaXB0aW9uOiAnVGhlIEFJIHByb3ZpZGVycy9tb2RlbHMgYXZhaWxhYmxlIHRvIHRoZSBBc2sgYXNzaXN0YW50IChmcm9tIHNlcnZlciBlbnYg4oCUIGNsb3VkIGtleXMgYW5kL29yIHRoZSBPcGVuQUktY29tcGF0aWJsZSBsb2NhbCBlbmRwb2ludCkuJyxcbiAgICB0YWdzLFxuICAgIHNlY3VyaXR5OiBhdXRoLFxuICAgIHJlc3BvbnNlczogeyAyMDA6IHsgZGVzY3JpcHRpb246ICdDb25maWd1cmVkIHByb3ZpZGVycycgfSwgNDAzOiB7IGRlc2NyaXB0aW9uOiAnYWlfZ2VuZXJhdGlvbiBmZWF0dXJlIHJlcXVpcmVkJyB9IH0sXG4gIH0pO1xuXG4gIHJlZ2lzdHJ5LnJlZ2lzdGVyUGF0aCh7XG4gICAgbWV0aG9kOiAncG9zdCcsXG4gICAgcGF0aDogJy9hc2snLFxuICAgIHN1bW1hcnk6ICdHcm91bmRlZCBob3ctdG8gYW5zd2VyIChub24tc3RyZWFtaW5nKScsXG4gICAgZGVzY3JpcHRpb246ICdBbnN3ZXIgYSBwbGF0Zm9ybSBob3ctdG8gcXVlc3Rpb24gZ3JvdW5kZWQgaW4gdGhlIGRvY3MgY29ycHVzLiBSZXNlcnZlcyBvbmUgYWlDYWxscyBxdW90YSBzbG90LicsXG4gICAgdGFncyxcbiAgICBzZWN1cml0eTogYXV0aCxcbiAgICByZXNwb25zZXM6IHtcbiAgICAgIDIwMDogeyBkZXNjcmlwdGlvbjogJ0Fuc3dlciB3aXRoIGdyb3VuZGVkIHNvdXJjZXMnIH0sXG4gICAgICA0MDM6IHsgZGVzY3JpcHRpb246ICdhaV9nZW5lcmF0aW9uIGZlYXR1cmUgcmVxdWlyZWQnIH0sXG4gICAgICA0Mjk6IHsgZGVzY3JpcHRpb246ICdhaUNhbGxzIHF1b3RhIGV4Y2VlZGVkIG9yIHJhdGUgbGltaXRlZCcgfSxcbiAgICB9LFxuICB9KTtcblxuICByZWdpc3RyeS5yZWdpc3RlclBhdGgoe1xuICAgIG1ldGhvZDogJ3Bvc3QnLFxuICAgIHBhdGg6ICcvYXNrL3N0cmVhbScsXG4gICAgc3VtbWFyeTogJ0dyb3VuZGVkIGhvdy10byBhbnN3ZXIgKFNTRSBzdHJlYW0pJyxcbiAgICBkZXNjcmlwdGlvbjogJ1NhbWUgYXMgUE9TVCAvYXNrIGJ1dCBzdHJlYW1zIHRoZSBncm91bmRlZCBzb3VyY2VzIHRoZW4gYW5zd2VyIHRva2VucyBhcyBTZXJ2ZXItU2VudCBFdmVudHMuJyxcbiAgICB0YWdzLFxuICAgIHNlY3VyaXR5OiBhdXRoLFxuICAgIHJlc3BvbnNlczoge1xuICAgICAgMjAwOiB7IGRlc2NyaXB0aW9uOiAndGV4dC9ldmVudC1zdHJlYW0gb2Ygc291cmNlcyArIGFuc3dlciB0b2tlbnMnIH0sXG4gICAgICA0MDM6IHsgZGVzY3JpcHRpb246ICdhaV9nZW5lcmF0aW9uIGZlYXR1cmUgcmVxdWlyZWQnIH0sXG4gICAgICA0Mjk6IHsgZGVzY3JpcHRpb246ICdhaUNhbGxzIHF1b3RhIGV4Y2VlZGVkIG9yIHJhdGUgbGltaXRlZCcgfSxcbiAgICB9LFxuICB9KTtcblxuICByZWdpc3RyeS5yZWdpc3RlclBhdGgoe1xuICAgIG1ldGhvZDogJ3Bvc3QnLFxuICAgIHBhdGg6ICcvYXNrL2FnZW50L3N0cmVhbScsXG4gICAgc3VtbWFyeTogJ1Rvb2wtY2FsbGluZyBBc2sgYWdlbnQgKFNTRSBzdHJlYW0pJyxcbiAgICBkZXNjcmlwdGlvbjogJ1J1bnMgdGhlIHRvb2wtY2FsbGluZyBhc3Npc3RhbnQgbG9vcCBhbmQgc3RyZWFtcyByZWFzb25pbmcgdG9rZW5zLCB0b29sLWNhbGwgZXZlbnRzLCBhbmQgcmV2aWV3YWJsZSBgcHJvcG9zZV8qYCBkcmFmdHMgKHBpcGVsaW5lL3BsdWdpbi90ZW1wbGF0ZSkgYXMgU1NFLiBSZWFkLW9ubHk6IHByb3Bvc2FscyBhcmUgY29uZmlybWVkIHNlcGFyYXRlbHkgaW4gdGhlIFVJLicsXG4gICAgdGFncyxcbiAgICBzZWN1cml0eTogYXV0aCxcbiAgICByZXNwb25zZXM6IHtcbiAgICAgIDIwMDogeyBkZXNjcmlwdGlvbjogJ3RleHQvZXZlbnQtc3RyZWFtIG9mIHRva2VucywgdG9vbC1jYWxscywgYW5kIHByb3Bvc2FsIGRyYWZ0cycgfSxcbiAgICAgIDQwMzogeyBkZXNjcmlwdGlvbjogJ2FpX2dlbmVyYXRpb24gZmVhdHVyZSByZXF1aXJlZCcgfSxcbiAgICAgIDQyOTogeyBkZXNjcmlwdGlvbjogJ2FpQ2FsbHMgcXVvdGEgZXhjZWVkZWQgb3IgcmF0ZSBsaW1pdGVkJyB9LFxuICAgIH0sXG4gIH0pO1xufVxuIl19
@@ -0,0 +1 @@
1
+ export declare function registerBillingRoutes(): void;
@@ -0,0 +1,66 @@
1
+ // Copyright 2026 Pipeline Builder Contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { registry } from '../registry.js';
4
+ const tags = ['Billing'];
5
+ const auth = [{ bearerAuth: [] }];
6
+ export function registerBillingRoutes() {
7
+ registry.registerPath({
8
+ method: 'get',
9
+ path: '/billing/plans',
10
+ summary: 'List active plans',
11
+ description: 'List all active billing plans. No authentication required.',
12
+ tags,
13
+ responses: { 200: { description: 'List of active billing plans' } },
14
+ });
15
+ registry.registerPath({
16
+ method: 'get',
17
+ path: '/billing/plans/{planId}',
18
+ summary: 'Get plan by ID',
19
+ tags,
20
+ responses: { 200: { description: 'Plan details' }, 404: { description: 'Plan not found' } },
21
+ });
22
+ registry.registerPath({
23
+ method: 'post',
24
+ path: '/billing/subscriptions',
25
+ summary: 'Create subscription',
26
+ description: 'Create a new billing subscription for the current organization.',
27
+ tags,
28
+ security: auth,
29
+ responses: { 201: { description: 'Subscription created' }, 400: { description: 'Validation error' } },
30
+ });
31
+ registry.registerPath({
32
+ method: 'get',
33
+ path: '/billing/subscriptions',
34
+ summary: 'List subscriptions',
35
+ description: 'List billing subscriptions for the current organization.',
36
+ tags,
37
+ security: auth,
38
+ responses: { 200: { description: 'List of subscriptions' } },
39
+ });
40
+ registry.registerPath({
41
+ method: 'put',
42
+ path: '/billing/subscriptions/{id}',
43
+ summary: 'Update subscription',
44
+ tags,
45
+ security: auth,
46
+ responses: { 200: { description: 'Subscription updated' }, 404: { description: 'Not found' } },
47
+ });
48
+ registry.registerPath({
49
+ method: 'delete',
50
+ path: '/billing/subscriptions/{id}',
51
+ summary: 'Cancel subscription',
52
+ tags,
53
+ security: auth,
54
+ responses: { 200: { description: 'Subscription cancelled' }, 404: { description: 'Not found' } },
55
+ });
56
+ registry.registerPath({
57
+ method: 'post',
58
+ path: '/billing/marketplace',
59
+ summary: 'AWS Marketplace integration',
60
+ description: 'Handle AWS Marketplace subscription events.',
61
+ tags,
62
+ security: auth,
63
+ responses: { 200: { description: 'Marketplace event processed' } },
64
+ });
65
+ }
66
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmlsbGluZy1yb3V0ZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvb3BlbmFwaS9yb3V0ZXMvYmlsbGluZy1yb3V0ZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsK0NBQStDO0FBQy9DLHNDQUFzQztBQUV0QyxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFFMUMsTUFBTSxJQUFJLEdBQUcsQ0FBQyxTQUFTLENBQUMsQ0FBQztBQUN6QixNQUFNLElBQUksR0FBRyxDQUFDLEVBQUUsVUFBVSxFQUFFLEVBQUUsRUFBRSxDQUFDLENBQUM7QUFFbEMsTUFBTSxVQUFVLHFCQUFxQjtJQUNuQyxRQUFRLENBQUMsWUFBWSxDQUFDO1FBQ3BCLE1BQU0sRUFBRSxLQUFLO1FBQ2IsSUFBSSxFQUFFLGdCQUFnQjtRQUN0QixPQUFPLEVBQUUsbUJBQW1CO1FBQzVCLFdBQVcsRUFBRSw0REFBNEQ7UUFDekUsSUFBSTtRQUNKLFNBQVMsRUFBRSxFQUFFLEdBQUcsRUFBRSxFQUFFLFdBQVcsRUFBRSw4QkFBOEIsRUFBRSxFQUFFO0tBQ3BFLENBQUMsQ0FBQztJQUVILFFBQVEsQ0FBQyxZQUFZLENBQUM7UUFDcEIsTUFBTSxFQUFFLEtBQUs7UUFDYixJQUFJLEVBQUUseUJBQXlCO1FBQy9CLE9BQU8sRUFBRSxnQkFBZ0I7UUFDekIsSUFBSTtRQUNKLFNBQVMsRUFBRSxFQUFFLEdBQUcsRUFBRSxFQUFFLFdBQVcsRUFBRSxjQUFjLEVBQUUsRUFBRSxHQUFHLEVBQUUsRUFBRSxXQUFXLEVBQUUsZ0JBQWdCLEVBQUUsRUFBRTtLQUM1RixDQUFDLENBQUM7SUFFSCxRQUFRLENBQUMsWUFBWSxDQUFDO1FBQ3BCLE1BQU0sRUFBRSxNQUFNO1FBQ2QsSUFBSSxFQUFFLHdCQUF3QjtRQUM5QixPQUFPLEVBQUUscUJBQXFCO1FBQzlCLFdBQVcsRUFBRSxpRUFBaUU7UUFDOUUsSUFBSTtRQUNKLFFBQVEsRUFBRSxJQUFJO1FBQ2QsU0FBUyxFQUFFLEVBQUUsR0FBRyxFQUFFLEVBQUUsV0FBVyxFQUFFLHNCQUFzQixFQUFFLEVBQUUsR0FBRyxFQUFFLEVBQUUsV0FBVyxFQUFFLGtCQUFrQixFQUFFLEVBQUU7S0FDdEcsQ0FBQyxDQUFDO0lBRUgsUUFBUSxDQUFDLFlBQVksQ0FBQztRQUNwQixNQUFNLEVBQUUsS0FBSztRQUNiLElBQUksRUFBRSx3QkFBd0I7UUFDOUIsT0FBTyxFQUFFLG9CQUFvQjtRQUM3QixXQUFXLEVBQUUsMERBQTBEO1FBQ3ZFLElBQUk7UUFDSixRQUFRLEVBQUUsSUFBSTtRQUNkLFNBQVMsRUFBRSxFQUFFLEdBQUcsRUFBRSxFQUFFLFdBQVcsRUFBRSx1QkFBdUIsRUFBRSxFQUFFO0tBQzdELENBQUMsQ0FBQztJQUVILFFBQVEsQ0FBQyxZQUFZLENBQUM7UUFDcEIsTUFBTSxFQUFFLEtBQUs7UUFDYixJQUFJLEVBQUUsNkJBQTZCO1FBQ25DLE9BQU8sRUFBRSxxQkFBcUI7UUFDOUIsSUFBSTtRQUNKLFFBQVEsRUFBRSxJQUFJO1FBQ2QsU0FBUyxFQUFFLEVBQUUsR0FBRyxFQUFFLEVBQUUsV0FBVyxFQUFFLHNCQUFzQixFQUFFLEVBQUUsR0FBRyxFQUFFLEVBQUUsV0FBVyxFQUFFLFdBQVcsRUFBRSxFQUFFO0tBQy9GLENBQUMsQ0FBQztJQUVILFFBQVEsQ0FBQyxZQUFZLENBQUM7UUFDcEIsTUFBTSxFQUFFLFFBQVE7UUFDaEIsSUFBSSxFQUFFLDZCQUE2QjtRQUNuQyxPQUFPLEVBQUUscUJBQXFCO1FBQzlCLElBQUk7UUFDSixRQUFRLEVBQUUsSUFBSTtRQUNkLFNBQVMsRUFBRSxFQUFFLEdBQUcsRUFBRSxFQUFFLFdBQVcsRUFBRSx3QkFBd0IsRUFBRSxFQUFFLEdBQUcsRUFBRSxFQUFFLFdBQVcsRUFBRSxXQUFXLEVBQUUsRUFBRTtLQUNqRyxDQUFDLENBQUM7SUFFSCxRQUFRLENBQUMsWUFBWSxDQUFDO1FBQ3BCLE1BQU0sRUFBRSxNQUFNO1FBQ2QsSUFBSSxFQUFFLHNCQUFzQjtRQUM1QixPQUFPLEVBQUUsNkJBQTZCO1FBQ3RDLFdBQVcsRUFBRSw2Q0FBNkM7UUFDMUQsSUFBSTtRQUNKLFFBQVEsRUFBRSxJQUFJO1FBQ2QsU0FBUyxFQUFFLEVBQUUsR0FBRyxFQUFFLEVBQUUsV0FBVyxFQUFFLDZCQUE2QixFQUFFLEVBQUU7S0FDbkUsQ0FBQyxDQUFDO0FBQ0wsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIENvcHlyaWdodCAyMDI2IFBpcGVsaW5lIEJ1aWxkZXIgQ29udHJpYnV0b3JzXG4vLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuXG5pbXBvcnQgeyByZWdpc3RyeSB9IGZyb20gJy4uL3JlZ2lzdHJ5LmpzJztcblxuY29uc3QgdGFncyA9IFsnQmlsbGluZyddO1xuY29uc3QgYXV0aCA9IFt7IGJlYXJlckF1dGg6IFtdIH1dO1xuXG5leHBvcnQgZnVuY3Rpb24gcmVnaXN0ZXJCaWxsaW5nUm91dGVzKCk6IHZvaWQge1xuICByZWdpc3RyeS5yZWdpc3RlclBhdGgoe1xuICAgIG1ldGhvZDogJ2dldCcsXG4gICAgcGF0aDogJy9iaWxsaW5nL3BsYW5zJyxcbiAgICBzdW1tYXJ5OiAnTGlzdCBhY3RpdmUgcGxhbnMnLFxuICAgIGRlc2NyaXB0aW9uOiAnTGlzdCBhbGwgYWN0aXZlIGJpbGxpbmcgcGxhbnMuIE5vIGF1dGhlbnRpY2F0aW9uIHJlcXVpcmVkLicsXG4gICAgdGFncyxcbiAgICByZXNwb25zZXM6IHsgMjAwOiB7IGRlc2NyaXB0aW9uOiAnTGlzdCBvZiBhY3RpdmUgYmlsbGluZyBwbGFucycgfSB9LFxuICB9KTtcblxuICByZWdpc3RyeS5yZWdpc3RlclBhdGgoe1xuICAgIG1ldGhvZDogJ2dldCcsXG4gICAgcGF0aDogJy9iaWxsaW5nL3BsYW5zL3twbGFuSWR9JyxcbiAgICBzdW1tYXJ5OiAnR2V0IHBsYW4gYnkgSUQnLFxuICAgIHRhZ3MsXG4gICAgcmVzcG9uc2VzOiB7IDIwMDogeyBkZXNjcmlwdGlvbjogJ1BsYW4gZGV0YWlscycgfSwgNDA0OiB7IGRlc2NyaXB0aW9uOiAnUGxhbiBub3QgZm91bmQnIH0gfSxcbiAgfSk7XG5cbiAgcmVnaXN0cnkucmVnaXN0ZXJQYXRoKHtcbiAgICBtZXRob2Q6ICdwb3N0JyxcbiAgICBwYXRoOiAnL2JpbGxpbmcvc3Vic2NyaXB0aW9ucycsXG4gICAgc3VtbWFyeTogJ0NyZWF0ZSBzdWJzY3JpcHRpb24nLFxuICAgIGRlc2NyaXB0aW9uOiAnQ3JlYXRlIGEgbmV3IGJpbGxpbmcgc3Vic2NyaXB0aW9uIGZvciB0aGUgY3VycmVudCBvcmdhbml6YXRpb24uJyxcbiAgICB0YWdzLFxuICAgIHNlY3VyaXR5OiBhdXRoLFxuICAgIHJlc3BvbnNlczogeyAyMDE6IHsgZGVzY3JpcHRpb246ICdTdWJzY3JpcHRpb24gY3JlYXRlZCcgfSwgNDAwOiB7IGRlc2NyaXB0aW9uOiAnVmFsaWRhdGlvbiBlcnJvcicgfSB9LFxuICB9KTtcblxuICByZWdpc3RyeS5yZWdpc3RlclBhdGgoe1xuICAgIG1ldGhvZDogJ2dldCcsXG4gICAgcGF0aDogJy9iaWxsaW5nL3N1YnNjcmlwdGlvbnMnLFxuICAgIHN1bW1hcnk6ICdMaXN0IHN1YnNjcmlwdGlvbnMnLFxuICAgIGRlc2NyaXB0aW9uOiAnTGlzdCBiaWxsaW5nIHN1YnNjcmlwdGlvbnMgZm9yIHRoZSBjdXJyZW50IG9yZ2FuaXphdGlvbi4nLFxuICAgIHRhZ3MsXG4gICAgc2VjdXJpdHk6IGF1dGgsXG4gICAgcmVzcG9uc2VzOiB7IDIwMDogeyBkZXNjcmlwdGlvbjogJ0xpc3Qgb2Ygc3Vic2NyaXB0aW9ucycgfSB9LFxuICB9KTtcblxuICByZWdpc3RyeS5yZWdpc3RlclBhdGgoe1xuICAgIG1ldGhvZDogJ3B1dCcsXG4gICAgcGF0aDogJy9iaWxsaW5nL3N1YnNjcmlwdGlvbnMve2lkfScsXG4gICAgc3VtbWFyeTogJ1VwZGF0ZSBzdWJzY3JpcHRpb24nLFxuICAgIHRhZ3MsXG4gICAgc2VjdXJpdHk6IGF1dGgsXG4gICAgcmVzcG9uc2VzOiB7IDIwMDogeyBkZXNjcmlwdGlvbjogJ1N1YnNjcmlwdGlvbiB1cGRhdGVkJyB9LCA0MDQ6IHsgZGVzY3JpcHRpb246ICdOb3QgZm91bmQnIH0gfSxcbiAgfSk7XG5cbiAgcmVnaXN0cnkucmVnaXN0ZXJQYXRoKHtcbiAgICBtZXRob2Q6ICdkZWxldGUnLFxuICAgIHBhdGg6ICcvYmlsbGluZy9zdWJzY3JpcHRpb25zL3tpZH0nLFxuICAgIHN1bW1hcnk6ICdDYW5jZWwgc3Vic2NyaXB0aW9uJyxcbiAgICB0YWdzLFxuICAgIHNlY3VyaXR5OiBhdXRoLFxuICAgIHJlc3BvbnNlczogeyAyMDA6IHsgZGVzY3JpcHRpb246ICdTdWJzY3JpcHRpb24gY2FuY2VsbGVkJyB9LCA0MDQ6IHsgZGVzY3JpcHRpb246ICdOb3QgZm91bmQnIH0gfSxcbiAgfSk7XG5cbiAgcmVnaXN0cnkucmVnaXN0ZXJQYXRoKHtcbiAgICBtZXRob2Q6ICdwb3N0JyxcbiAgICBwYXRoOiAnL2JpbGxpbmcvbWFya2V0cGxhY2UnLFxuICAgIHN1bW1hcnk6ICdBV1MgTWFya2V0cGxhY2UgaW50ZWdyYXRpb24nLFxuICAgIGRlc2NyaXB0aW9uOiAnSGFuZGxlIEFXUyBNYXJrZXRwbGFjZSBzdWJzY3JpcHRpb24gZXZlbnRzLicsXG4gICAgdGFncyxcbiAgICBzZWN1cml0eTogYXV0aCxcbiAgICByZXNwb25zZXM6IHsgMjAwOiB7IGRlc2NyaXB0aW9uOiAnTWFya2V0cGxhY2UgZXZlbnQgcHJvY2Vzc2VkJyB9IH0sXG4gIH0pO1xufVxuIl19
@@ -0,0 +1,7 @@
1
+ /**
2
+ * OpenAPI definitions for the image-registry service (api/image-registry): the
3
+ * Docker-registry token endpoint (`/token`), the org-scoped image API
4
+ * (`/api/images`), and the admin API (`/api/admin`). Image read/write is gated by
5
+ * the `registry:read` / `registry:write` permissions.
6
+ */
7
+ export declare function registerImageRegistryRoutes(): void;