@pipeline-builder/api-core 4.1.12 → 4.1.14

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 (187) hide show
  1. package/README.md +3 -2
  2. package/lib/constants/index.d.ts +0 -1
  3. package/lib/constants/index.js +1 -2
  4. package/lib/errors/app-errors.d.ts +8 -3
  5. package/lib/errors/app-errors.js +10 -5
  6. package/lib/helpers/access-helpers.d.ts +4 -6
  7. package/lib/helpers/access-helpers.js +6 -8
  8. package/lib/helpers/index.d.ts +3 -3
  9. package/lib/helpers/index.js +4 -4
  10. package/lib/helpers/org-hierarchy-http.d.ts +11 -10
  11. package/lib/helpers/org-hierarchy-http.js +24 -35
  12. package/lib/helpers/org-hierarchy.d.ts +37 -3
  13. package/lib/helpers/org-hierarchy.js +45 -4
  14. package/lib/helpers/restore-helpers.d.ts +44 -24
  15. package/lib/helpers/restore-helpers.js +41 -52
  16. package/lib/helpers/sse-helpers.d.ts +29 -7
  17. package/lib/helpers/sse-helpers.js +36 -9
  18. package/lib/index.d.ts +16 -14
  19. package/lib/index.js +17 -15
  20. package/lib/middleware/assurance.d.ts +116 -0
  21. package/lib/middleware/assurance.js +176 -0
  22. package/lib/middleware/auth.d.ts +5 -427
  23. package/lib/middleware/auth.js +36 -789
  24. package/lib/middleware/ecosystem-guard.d.ts +47 -0
  25. package/lib/middleware/ecosystem-guard.js +60 -0
  26. package/lib/middleware/index.d.ts +11 -4
  27. package/lib/middleware/index.js +12 -5
  28. package/lib/middleware/jwt-verify.d.ts +52 -0
  29. package/lib/middleware/jwt-verify.js +105 -0
  30. package/lib/middleware/permission-gates.d.ts +133 -0
  31. package/lib/middleware/permission-gates.js +260 -0
  32. package/lib/middleware/revocation.d.ts +96 -0
  33. package/lib/middleware/revocation.js +162 -0
  34. package/lib/middleware/route-table.d.ts +11 -0
  35. package/lib/middleware/route-table.js +4 -1
  36. package/lib/middleware/service-tokens.d.ts +83 -0
  37. package/lib/middleware/service-tokens.js +158 -0
  38. package/lib/middleware/step-up.d.ts +2 -2
  39. package/lib/middleware/step-up.js +8 -7
  40. package/lib/middleware/system-org.d.ts +20 -0
  41. package/lib/middleware/system-org.js +25 -0
  42. package/lib/openapi/routes/plugin-routes.js +133 -10
  43. package/lib/openapi/schema-registry.js +2 -5
  44. package/lib/routes/index.d.ts +1 -1
  45. package/lib/routes/index.js +2 -2
  46. package/lib/services/api-key-exchange.js +7 -5
  47. package/lib/services/audit-spool.d.ts +34 -10
  48. package/lib/services/audit-spool.js +75 -34
  49. package/lib/services/cache-service.d.ts +7 -0
  50. package/lib/services/cache-service.js +1 -1
  51. package/lib/services/circuit-breaker.d.ts +14 -1
  52. package/lib/services/circuit-breaker.js +36 -10
  53. package/lib/services/compliance-client.d.ts +10 -2
  54. package/lib/services/compliance-client.js +17 -10
  55. package/lib/services/compliance-event-subscriber.js +6 -6
  56. package/lib/services/durable-event-bus.js +8 -11
  57. package/lib/services/ecosystem-notify-client.d.ts +32 -0
  58. package/lib/services/ecosystem-notify-client.js +71 -0
  59. package/lib/services/env-redis.d.ts +4 -1
  60. package/lib/services/env-redis.js +32 -3
  61. package/lib/services/http-client.d.ts +19 -0
  62. package/lib/services/http-client.js +98 -19
  63. package/lib/services/index.d.ts +25 -24
  64. package/lib/services/index.js +25 -22
  65. package/lib/services/jwks-cache.js +5 -4
  66. package/lib/services/leader-lock.d.ts +22 -5
  67. package/lib/services/leader-lock.js +61 -6
  68. package/lib/services/quota.d.ts +9 -4
  69. package/lib/services/quota.js +34 -40
  70. package/lib/services/remote-audit-client.d.ts +28 -32
  71. package/lib/services/remote-audit-client.js +166 -49
  72. package/lib/services/retry-forever.js +5 -11
  73. package/lib/services/retry-strategy.d.ts +1 -1
  74. package/lib/services/retry-strategy.js +5 -4
  75. package/lib/services/scheduler.d.ts +8 -3
  76. package/lib/services/scheduler.js +17 -5
  77. package/lib/services/service-boot.d.ts +14 -13
  78. package/lib/services/service-boot.js +18 -15
  79. package/lib/services/service-keys.d.ts +1 -1
  80. package/lib/services/service-keys.js +12 -18
  81. package/lib/services/sse-ticket-store.d.ts +9 -1
  82. package/lib/services/sse-ticket-store.js +24 -9
  83. package/lib/services/system-notification.d.ts +27 -0
  84. package/lib/services/system-notification.js +50 -0
  85. package/lib/services/token-revocation.d.ts +50 -1
  86. package/lib/services/token-revocation.js +103 -3
  87. package/lib/types/audit-events.d.ts +6 -6
  88. package/lib/types/audit-events.js +1 -1
  89. package/lib/types/common.d.ts +32 -13
  90. package/lib/types/common.js +2 -1
  91. package/lib/types/ecosystem-notifications.d.ts +142 -0
  92. package/lib/types/ecosystem-notifications.js +210 -0
  93. package/lib/types/ecosystem.d.ts +77 -0
  94. package/lib/types/ecosystem.js +119 -0
  95. package/lib/types/error-codes.d.ts +81 -0
  96. package/lib/types/error-codes.js +108 -1
  97. package/lib/types/feature-flags.d.ts +21 -1
  98. package/lib/types/feature-flags.js +36 -2
  99. package/lib/types/index.d.ts +14 -9
  100. package/lib/types/index.js +15 -10
  101. package/lib/types/permissions.d.ts +46 -8
  102. package/lib/types/permissions.js +75 -12
  103. package/lib/types/plugin-catalog.d.ts +35 -0
  104. package/lib/types/plugin-catalog.js +39 -0
  105. package/lib/types/plugin-health.d.ts +102 -0
  106. package/lib/types/plugin-health.js +184 -0
  107. package/lib/types/quota-tiers.d.ts +15 -0
  108. package/lib/types/quota-tiers.js +23 -3
  109. package/lib/types/wire-vocabulary.d.ts +102 -0
  110. package/lib/types/wire-vocabulary.js +94 -0
  111. package/lib/utils/alias-resolver.js +2 -2
  112. package/lib/utils/audit.d.ts +5 -2
  113. package/lib/utils/audit.js +6 -3
  114. package/lib/utils/compliance-attributes.js +3 -4
  115. package/lib/utils/concurrency.d.ts +7 -0
  116. package/lib/utils/concurrency.js +13 -1
  117. package/lib/utils/env.d.ts +3 -4
  118. package/lib/utils/env.js +4 -5
  119. package/lib/utils/identity.d.ts +13 -4
  120. package/lib/utils/identity.js +18 -10
  121. package/lib/utils/index.d.ts +13 -10
  122. package/lib/utils/index.js +13 -10
  123. package/lib/utils/jwk.d.ts +4 -0
  124. package/lib/utils/jwk.js +13 -1
  125. package/lib/utils/logger.d.ts +1 -1
  126. package/lib/utils/logger.js +1 -1
  127. package/lib/utils/object.d.ts +11 -0
  128. package/lib/utils/object.js +24 -1
  129. package/lib/utils/params.d.ts +8 -22
  130. package/lib/utils/params.js +15 -43
  131. package/lib/utils/plugin-compliance.d.ts +98 -0
  132. package/lib/utils/plugin-compliance.js +78 -0
  133. package/lib/utils/proof-of-work.d.ts +69 -0
  134. package/lib/utils/proof-of-work.js +139 -0
  135. package/lib/utils/response.d.ts +16 -6
  136. package/lib/utils/response.js +18 -15
  137. package/lib/utils/retention.d.ts +17 -0
  138. package/lib/utils/retention.js +30 -0
  139. package/lib/utils/secret-encryption.d.ts +7 -7
  140. package/lib/utils/secret-encryption.js +9 -10
  141. package/lib/utils/secret-rotation.js +2 -2
  142. package/lib/utils/sensitive-patterns.js +4 -5
  143. package/lib/utils/service-registry.d.ts +15 -0
  144. package/lib/utils/service-registry.js +24 -0
  145. package/lib/utils/ssrf.d.ts +11 -2
  146. package/lib/utils/ssrf.js +112 -33
  147. package/lib/validation/ai-schemas.js +3 -2
  148. package/lib/validation/common-schemas.d.ts +0 -9
  149. package/lib/validation/common-schemas.js +1 -7
  150. package/lib/validation/dockerfile-static.d.ts +26 -0
  151. package/lib/validation/dockerfile-static.js +210 -0
  152. package/lib/validation/index.d.ts +13 -4
  153. package/lib/validation/index.js +14 -5
  154. package/lib/validation/message-schemas.js +6 -4
  155. package/lib/validation/pipeline-schemas.d.ts +8 -2
  156. package/lib/validation/pipeline-schemas.js +30 -4
  157. package/lib/validation/plugin-base-images.d.ts +20 -0
  158. package/lib/validation/plugin-base-images.js +17 -0
  159. package/lib/validation/plugin-catalog-detect.d.ts +86 -0
  160. package/lib/validation/plugin-catalog-detect.js +237 -0
  161. package/lib/validation/plugin-catalog-metadata.d.ts +141 -0
  162. package/lib/validation/plugin-catalog-metadata.js +162 -0
  163. package/lib/validation/plugin-heuristics.d.ts +41 -0
  164. package/lib/validation/plugin-heuristics.js +211 -0
  165. package/lib/validation/plugin-lint.d.ts +18 -0
  166. package/lib/validation/plugin-lint.js +136 -0
  167. package/lib/validation/plugin-name-confusable.d.ts +15 -0
  168. package/lib/validation/plugin-name-confusable.js +81 -0
  169. package/lib/validation/plugin-schemas.d.ts +40 -26
  170. package/lib/validation/plugin-schemas.js +26 -23
  171. package/lib/validation/plugin-spec-schema.d.ts +112 -0
  172. package/lib/validation/plugin-spec-schema.js +163 -0
  173. package/lib/validation/plugin-template-contract.d.ts +64 -0
  174. package/lib/validation/plugin-template-contract.js +113 -0
  175. package/package.json +69 -4
  176. package/lib/testing/mock-api-core.d.ts +0 -150
  177. package/lib/testing/mock-api-core.js +0 -246
  178. package/lib/testing/mock-drizzle.d.ts +0 -6
  179. package/lib/testing/mock-drizzle.js +0 -57
  180. package/lib/testing/route-coverage.d.ts +0 -64
  181. package/lib/testing/route-coverage.js +0 -183
  182. package/lib/testing/service-tokens.d.ts +0 -66
  183. package/lib/testing/service-tokens.js +0 -137
  184. package/lib/testing/tier-mock.d.ts +0 -44
  185. package/lib/testing/tier-mock.js +0 -65
  186. package/lib/testing/user-tokens.d.ts +0 -75
  187. package/lib/testing/user-tokens.js +0 -115
@@ -1,183 +0,0 @@
1
- // Copyright 2026 Pipeline Builder Contributors
2
- // SPDX-License-Identifier: Apache-2.0
3
- /**
4
- * Route-coverage checks + the generated route table each service publishes.
5
- *
6
- * One test per service builds its app's {@link RouteTableEntry} list (see
7
- * `middleware/route-table.ts`) and runs it through {@link findRouteCoverageViolations}:
8
- *
9
- * - a WRITE route (anything but GET/HEAD/OPTIONS) must carry a permission
10
- * gate (or `requireSystemAdmin`, or `requireInternalService`) AND declare
11
- * its audit action via `audited()`;
12
- * - a READ route must carry one of those gates;
13
- * - a route with a permission gate must also run `requireAuth`;
14
- * - an `/internal/…` path must be gated by `requireInternalService` (#14),
15
- * with no exception list — a peer-service API a browser can reach is the
16
- * failure this rule exists to prevent. {@link findInternalRouteViolations}
17
- * then checks each service's internal routes against the caller list it
18
- * declares (the same list the Istio policies name).
19
- *
20
- * Anything that legitimately can't satisfy a rule — health/metrics probes, signed
21
- * webhooks, public auth endpoints, internal service-principal hooks, handler-level
22
- * dynamic authorization — goes in the service's `exceptions` list with an explicit
23
- * reason. Exceptions are themselves checked: one that no longer waives a real
24
- * violation fails the test, so the allowlist can't rot into a rubber stamp.
25
- *
26
- * `assertRouteTableSnapshot` writes/compares the table the frontend imports
27
- * (`frontend/src/generated/route-table/<service>.json`), so the UI's `can(...)`
28
- * checks are tested against the permissions the routes actually require.
29
- */
30
- import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
31
- import { dirname } from 'node:path';
32
- import { isWriteMethod } from '../middleware/route-table.js';
33
- /**
34
- * Exceptions every service inherits from `createApp`'s infrastructure routes:
35
- * unauthenticated probes, the service-principal-gated warm-up, the metrics
36
- * scrape endpoint, the OpenAPI docs, and the ticket-gated SSE log stream.
37
- */
38
- export const INFRA_ROUTE_EXCEPTIONS = [
39
- { path: '/health', waive: 'all', optional: true, reason: 'Liveness probe — must answer before auth is possible.' },
40
- { path: '/ready', waive: 'all', optional: true, reason: 'Readiness probe — must answer before dependencies connect.' },
41
- { path: '/metrics', waive: 'all', optional: true, reason: 'Prometheus scrape; gated by METRICS_SCRAPE_TOKEN when set, never by a user permission.' },
42
- { path: '/warmup', waive: 'all', optional: true, reason: 'Pool warm-up; verifies a signed service principal inline (404s everyone else).' },
43
- { path: '/docs/openapi.json', waive: 'all', optional: true, reason: 'Public API description; disabled in production.' },
44
- { path: /^GET \/docs/, waive: 'all', optional: true, reason: 'Swagger UI assets; disabled in production.' },
45
- { path: /^(GET|POST) \/logs/, waive: 'all', optional: true, reason: 'Build-log SSE stream + its ticket mint — authorized by a single-use, org-bound ticket.' },
46
- ];
47
- function matches(e, entry) {
48
- if (e.method && e.method !== '*' && e.method.toUpperCase() !== entry.method)
49
- return false;
50
- if (typeof e.path === 'string')
51
- return e.path === entry.path;
52
- return e.path.test(`${entry.method} ${entry.path}`);
53
- }
54
- function isGated(entry) {
55
- // An INTERNAL route (#14) counts as gated: `requireInternalService` refuses
56
- // every user token and admits only the named peer services, which is a
57
- // STRONGER requirement than any user permission — so these routes no longer
58
- // need a "service principal, no user permission applies" exception.
59
- return entry.permissions.length > 0 || entry.systemAdmin || entry.internalCallers.length > 0;
60
- }
61
- /** Check a service's route table against the coverage rules. */
62
- export function findRouteCoverageViolations(table, exceptions = []) {
63
- const violations = [];
64
- const used = new Set();
65
- const waived = (entry, rule) => {
66
- const hit = exceptions.find((e) => (e.waive === rule || e.waive === 'all') && matches(e, entry));
67
- if (hit)
68
- used.add(hit);
69
- return hit !== undefined;
70
- };
71
- for (const entry of table) {
72
- if (entry.method === 'OPTIONS')
73
- continue;
74
- const where = `${entry.method} ${entry.path}`;
75
- // An `/internal/…` path is internal by name, so it must be internal by gate
76
- // too (#14). Checked for EVERY service, with no exception list: a new
77
- // internal route that forgets `requireInternalService` is a browser-reachable
78
- // peer-service API, which is the failure mode this rule exists to prevent.
79
- if (/(^|\/)internal(\/|$)/.test(entry.path) && entry.internalCallers.length === 0) {
80
- violations.push(`${where}: an /internal route without requireInternalService({ callers: [...] }) — user tokens would reach it`);
81
- }
82
- if (!isGated(entry)) {
83
- if (!waived(entry, 'permission')) {
84
- violations.push(`${where}: no permission gate (add requirePermission/requireSystemAdmin, or an exception with a reason)`);
85
- }
86
- }
87
- else if (!entry.auth) {
88
- violations.push(`${where}: permission gate without requireAuth — the gate can never see a user`);
89
- }
90
- if (isWriteMethod(entry.method) && entry.audit.length === 0 && !waived(entry, 'audit')) {
91
- violations.push(`${where}: write route declares no audit action (wrap the handler chain in audited('<action>'))`);
92
- }
93
- }
94
- const unusedExceptions = exceptions
95
- .filter((e) => !used.has(e) && e.optional !== true)
96
- .map((e) => `${e.method ?? '*'} ${String(e.path)} — ${e.reason}`);
97
- return { violations, unusedExceptions };
98
- }
99
- /**
100
- * Check a service's INTERNAL routes (#14) against what it declares.
101
- *
102
- * The declaration is the single list a service maintains, and it is checked in
103
- * BOTH directions, so neither side can drift:
104
- *
105
- * - every declared route must exist and be gated by `requireInternalService`
106
- * with exactly the declared callers (and `requireAuth` before it);
107
- * - every route the table says is internal must be declared — so adding one
108
- * forces a visible change here, and the Istio `AuthorizationPolicy` that
109
- * names the same callers (`deploy/*​/k8s/istio-internal-routes.yaml`) has
110
- * one authoritative list to be reviewed against.
111
- */
112
- export function findInternalRouteViolations(table, declared) {
113
- const violations = [];
114
- const key = (method, path) => `${method.toUpperCase()} ${path}`;
115
- const declaredByKey = new Map(declared.map((d) => [key(d.method, d.path), d]));
116
- for (const d of declared) {
117
- const entry = table.find((e) => key(e.method, e.path) === key(d.method, d.path));
118
- if (!entry) {
119
- violations.push(`${key(d.method, d.path)}: declared internal but the route does not exist`);
120
- continue;
121
- }
122
- if (!entry.auth)
123
- violations.push(`${key(d.method, d.path)}: internal route without requireAuth`);
124
- const actual = [...entry.internalCallers].sort().join(', ');
125
- const expected = [...d.callers].sort().join(', ');
126
- if (actual !== expected) {
127
- violations.push(`${key(d.method, d.path)}: internal callers are [${actual}] but [${expected}] were declared`);
128
- }
129
- }
130
- for (const entry of table) {
131
- if (entry.internalCallers.length === 0)
132
- continue;
133
- if (!declaredByKey.has(key(entry.method, entry.path))) {
134
- violations.push(`${key(entry.method, entry.path)}: gated by requireInternalService but not declared (add it here and to the mesh policy)`);
135
- }
136
- }
137
- return violations;
138
- }
139
- /** Every audit action declared anywhere in a route table. */
140
- export function declaredAuditActions(table) {
141
- return [...new Set(table.flatMap((e) => e.audit))].sort();
142
- }
143
- /**
144
- * Compare a service's route table to its checked-in copy under
145
- * `frontend/src/generated/route-table/` — the file the frontend's `can(...)`
146
- * tests read. Returns a diff message, or `null` when they match. Set
147
- * `UPDATE_ROUTE_TABLES=1` to rewrite the file after an intended route change.
148
- */
149
- export function compareRouteTableSnapshot(table, file) {
150
- const serialized = `${JSON.stringify(table, null, 2)}\n`;
151
- if (process.env.UPDATE_ROUTE_TABLES === '1') {
152
- mkdirSync(dirname(file), { recursive: true });
153
- writeFileSync(file, serialized, 'utf8');
154
- return null;
155
- }
156
- let current;
157
- try {
158
- current = readFileSync(file, 'utf8');
159
- }
160
- catch {
161
- return `${file} is missing. Re-run this test with UPDATE_ROUTE_TABLES=1 to generate it.`;
162
- }
163
- if (current === serialized)
164
- return null;
165
- const expected = JSON.parse(current);
166
- const key = (e) => `${e.method} ${e.path}`;
167
- const before = new Set(expected.map(key));
168
- const after = new Set(table.map(key));
169
- const added = [...after].filter((k) => !before.has(k));
170
- const removed = [...before].filter((k) => !after.has(k));
171
- const changed = table
172
- .filter((e) => before.has(key(e)))
173
- .filter((e) => JSON.stringify(e) !== JSON.stringify(expected.find((x) => key(x) === key(e))))
174
- .map(key);
175
- return [
176
- `${file} is out of date (the frontend reads it to check its can(...) gates).`,
177
- added.length ? ` added: ${added.join(', ')}` : '',
178
- removed.length ? ` removed: ${removed.join(', ')}` : '',
179
- changed.length ? ` changed: ${changed.join(', ')}` : '',
180
- ' Re-run with UPDATE_ROUTE_TABLES=1 to refresh it, then review the diff.',
181
- ].filter(Boolean).join('\n');
182
- }
183
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUtY292ZXJhZ2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdGVzdGluZy9yb3V0ZS1jb3ZlcmFnZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQTBCRztBQUVILE9BQU8sRUFBRSxTQUFTLEVBQUUsWUFBWSxFQUFFLGFBQWEsRUFBRSxNQUFNLFNBQVMsQ0FBQztBQUNqRSxPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQ3BDLE9BQU8sRUFBRSxhQUFhLEVBQXdCLE1BQU0sOEJBQThCLENBQUM7QUFxQm5GOzs7O0dBSUc7QUFDSCxNQUFNLENBQUMsTUFBTSxzQkFBc0IsR0FBc0M7SUFDdkUsRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUUsdURBQXVELEVBQUU7SUFDbEgsRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUUsNERBQTRELEVBQUU7SUFDdEgsRUFBRSxJQUFJLEVBQUUsVUFBVSxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUUsd0ZBQXdGLEVBQUU7SUFDcEosRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUUsZ0ZBQWdGLEVBQUU7SUFDM0ksRUFBRSxJQUFJLEVBQUUsb0JBQW9CLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFLE1BQU0sRUFBRSxpREFBaUQsRUFBRTtJQUN2SCxFQUFFLElBQUksRUFBRSxhQUFhLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFB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Q0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLEtBQUssSUFBSSxDQUFDLFNBQVMsQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEtBQUssR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztTQUM1RixHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDWixPQUFPO1FBQ0wsR0FBRyxJQUFJLHNFQUFzRTtRQUM3RSxLQUFLLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxjQUFjLEtBQUssQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRTtRQUNwRCxPQUFPLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxjQUFjLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRTtRQUN4RCxPQUFPLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxjQUFjLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRTtRQUN4RCwwRUFBMEU7S0FDM0UsQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO0FBQy9CLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuLyoqXG4gKiBSb3V0ZS1jb3ZlcmFnZSBjaGVja3MgKyB0aGUgZ2VuZXJhdGVkIHJvdXRlIHRhYmxlIGVhY2ggc2VydmljZSBwdWJsaXNoZXMuXG4gKlxuICogT25lIHRlc3QgcGVyIHNlcnZpY2UgYnVpbGRzIGl0cyBhcHAncyB7QGxpbmsgUm91dGVUYWJsZUVudHJ5fSBsaXN0IChzZWVcbiAqIGBtaWRkbGV3YXJlL3JvdXRlLXRhYmxlLnRzYCkgYW5kIHJ1bnMgaXQgdGhyb3VnaCB7QGxpbmsgZmluZFJvdXRlQ292ZXJhZ2VWaW9sYXRpb25zfTpcbiAqXG4gKiAgIC0gYSBXUklURSByb3V0ZSAoYW55dGhpbmcgYnV0IEdFVC9IRUFEL09QVElPTlMpIG11c3QgY2FycnkgYSBwZXJtaXNzaW9uXG4gKiAgICAgZ2F0ZSAob3IgYHJlcXVpcmVTeXN0ZW1BZG1pbmAsIG9yIGByZXF1aXJlSW50ZXJuYWxTZXJ2aWNlYCkgQU5EIGRlY2xhcmVcbiAqICAgICBpdHMgYXVkaXQgYWN0aW9uIHZpYSBgYXVkaXRlZCgpYDtcbiAqICAgLSBhIFJFQUQgcm91dGUgbXVzdCBjYXJyeSBvbmUgb2YgdGhvc2UgZ2F0ZXM7XG4gKiAgIC0gYSByb3V0ZSB3aXRoIGEgcGVybWlzc2lvbiBnYXRlIG11c3QgYWxzbyBydW4gYHJlcXVpcmVBdXRoYDtcbiAqICAgLSBhbiBgL2ludGVybmFsL+KApmAgcGF0aCBtdXN0IGJlIGdhdGVkIGJ5IGByZXF1aXJlSW50ZXJuYWxTZXJ2aWNlYCAoIzE0KSxcbiAqICAgICB3aXRoIG5vIGV4Y2VwdGlvbiBsaXN0IOKAlCBhIHBlZXItc2VydmljZSBBUEkgYSBicm93c2VyIGNhbiByZWFjaCBpcyB0aGVcbiAqICAgICBmYWlsdXJlIHRoaXMgcnVsZSBleGlzdHMgdG8gcHJldmVudC4ge0BsaW5rIGZpbmRJbnRlcm5hbFJvdXRlVmlvbGF0aW9uc31cbiAqICAgICB0aGVuIGNoZWNrcyBlYWNoIHNlcnZpY2UncyBpbnRlcm5hbCByb3V0ZXMgYWdhaW5zdCB0aGUgY2FsbGVyIGxpc3QgaXRcbiAqICAgICBkZWNsYXJlcyAodGhlIHNhbWUgbGlzdCB0aGUgSXN0aW8gcG9saWNpZXMgbmFtZSkuXG4gKlxuICogQW55dGhpbmcgdGhhdCBsZWdpdGltYXRlbHkgY2FuJ3Qgc2F0aXNmeSBhIHJ1bGUg4oCUIGhlYWx0aC9tZXRyaWNzIHByb2Jlcywgc2lnbmVkXG4gKiB3ZWJob29rcywgcHVibGljIGF1dGggZW5kcG9pbnRzLCBpbnRlcm5hbCBzZXJ2aWNlLXByaW5jaXBhbCBob29rcywgaGFuZGxlci1sZXZlbFxuICogZHluYW1pYyBhdXRob3JpemF0aW9uIOKAlCBnb2VzIGluIHRoZSBzZXJ2aWNlJ3MgYGV4Y2VwdGlvbnNgIGxpc3Qgd2l0aCBhbiBleHBsaWNpdFxuICogcmVhc29uLiBFeGNlcHRpb25zIGFyZSB0aGVtc2VsdmVzIGNoZWNrZWQ6IG9uZSB0aGF0IG5vIGxvbmdlciB3YWl2ZXMgYSByZWFsXG4gKiB2aW9sYXRpb24gZmFpbHMgdGhlIHRlc3QsIHNvIHRoZSBhbGxvd2xpc3QgY2FuJ3Qgcm90IGludG8gYSBydWJiZXIgc3RhbXAuXG4gKlxuICogYGFzc2VydFJvdXRlVGFibGVTbmFwc2hvdGAgd3JpdGVzL2NvbXBhcmVzIHRoZSB0YWJsZSB0aGUgZnJvbnRlbmQgaW1wb3J0c1xuICogKGBmcm9udGVuZC9zcmMvZ2VuZXJhdGVkL3JvdXRlLXRhYmxlLzxzZXJ2aWNlPi5qc29uYCksIHNvIHRoZSBVSSdzIGBjYW4oLi4uKWBcbiAqIGNoZWNrcyBhcmUgdGVzdGVkIGFnYWluc3QgdGhlIHBlcm1pc3Npb25zIHRoZSByb3V0ZXMgYWN0dWFsbHkgcmVxdWlyZS5cbiAqL1xuXG5pbXBvcnQgeyBta2RpclN5bmMsIHJlYWRGaWxlU3luYywgd3JpdGVGaWxlU3luYyB9IGZyb20gJ25vZGU6ZnMnO1xuaW1wb3J0IHsgZGlybmFtZSB9IGZyb20gJ25vZGU6cGF0aCc7XG5pbXBvcnQgeyBpc1dyaXRlTWV0aG9kLCB0eXBlIFJvdXRlVGFibGVFbnRyeSB9IGZyb20gJy4uL21pZGRsZXdhcmUvcm91dGUtdGFibGUuanMnO1xuXG4vKiogQSBydWxlIGEgcm91dGUgaXMgYWxsb3dlZCBub3QgdG8gc2F0aXNmeSwgd2l0aCB0aGUgcmVhc29uIGl0IGlzIGV4ZW1wdC4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgUm91dGVDb3ZlcmFnZUV4Y2VwdGlvbiB7XG4gIC8qKiBIVFRQIG1ldGhvZCwgb3IgJyonIGZvciBhbnkuICovXG4gIG1ldGhvZD86IHN0cmluZztcbiAgLyoqIEV4YWN0IHJvdXRlIHBhdGgsIG9yIGEgcGF0dGVybiBtYXRjaGVkIGFnYWluc3QgYE1FVEhPRCAvcGF0aGAuICovXG4gIHBhdGg6IHN0cmluZyB8IFJlZ0V4cDtcbiAgLyoqIFdoaWNoIHJ1bGUgaXMgd2FpdmVkOiB0aGUgcGVybWlzc2lvbiBnYXRlLCB0aGUgYXVkaXQgZGVjbGFyYXRpb24sIG9yIGJvdGguICovXG4gIHdhaXZlOiAncGVybWlzc2lvbicgfCAnYXVkaXQnIHwgJ2FsbCc7XG4gIC8qKiBXaHkgdGhpcyByb3V0ZSBjYW4ndCBjYXJyeSB0aGUgZ2F0ZS9hdWRpdC4gUmVxdWlyZWQg4oCUIG5vIHNpbGVudCBhbGxvd2xpc3RpbmcuICovXG4gIHJlYXNvbjogc3RyaW5nO1xuICAvKipcbiAgICogU2tpcCB0aGUgc3RhbGVuZXNzIGNoZWNrIGZvciB0aGlzIGVudHJ5LiBPbmx5IGZvciB0aGUgc2hhcmVkXG4gICAqIHtAbGluayBJTkZSQV9ST1VURV9FWENFUFRJT05TfSwgd2hvc2Ugcm91dGVzIGRlcGVuZCBvbiBwZXItc2VydmljZVxuICAgKiBgY3JlYXRlQXBwYCBvcHRpb25zIChPcGVuQVBJIGRvY3MsIGxvZyBzdHJlYW0pIOKAlCBhIHNlcnZpY2UgdGhhdCBkb2Vzbid0XG4gICAqIGVuYWJsZSB0aGVtIHdvdWxkIG90aGVyd2lzZSBmYWlsIG9uIGFuIFwidW51c2VkXCIgc2hhcmVkIGV4Y2VwdGlvbi5cbiAgICovXG4gIG9wdGlvbmFsPzogYm9vbGVhbjtcbn1cblxuLyoqXG4gKiBFeGNlcHRpb25zIGV2ZXJ5IHNlcnZpY2UgaW5oZXJpdHMgZnJvbSBgY3JlYXRlQXBwYCdzIGluZnJhc3RydWN0dXJlIHJvdXRlczpcbiAqIHVuYXV0aGVudGljYXRlZCBwcm9iZXMsIHRoZSBzZXJ2aWNlLXByaW5jaXBhbC1nYXRlZCB3YXJtLXVwLCB0aGUgbWV0cmljc1xuICogc2NyYXBlIGVuZHBvaW50LCB0aGUgT3BlbkFQSSBkb2NzLCBhbmQgdGhlIHRpY2tldC1nYXRlZCBTU0UgbG9nIHN0cmVhbS5cbiAqL1xuZXhwb3J0IGNvbnN0IElORlJBX1JPVVRFX0VYQ0VQVElPTlM6IHJlYWRvbmx5IFJvdXRlQ292ZXJhZ2VFeGNlcHRpb25bXSA9IFtcbiAgeyBwYXRoOiAnL2hlYWx0aCcsIHdhaXZlOiAnYWxsJywgb3B0aW9uYWw6IHRydWUsIHJlYXNvbjogJ0xpdmVuZXNzIHByb2JlIOKAlCBtdXN0IGFuc3dlciBiZWZvcmUgYXV0aCBpcyBwb3NzaWJsZS4nIH0sXG4gIHsgcGF0aDogJy9yZWFkeScsIHdhaXZlOiAnYWxsJywgb3B0aW9uYWw6IHRydWUsIHJlYXNvbjogJ1JlYWRpbmVzcyBwcm9iZSDigJQgbXVzdCBhbnN3ZXIgYmVmb3JlIGRlcGVuZGVuY2llcyBjb25uZWN0LicgfSxcbiAgeyBwYXRoOiAnL21ldHJpY3MnLCB3YWl2ZTogJ2FsbCcsIG9wdGlvbmFsOiB0cnVlLCByZWFzb246ICdQcm9tZXRoZXVzIHNjcmFwZTsgZ2F0ZWQgYnkgTUVUUklDU19TQ1JBUEVfVE9LRU4gd2hlbiBzZXQsIG5ldmVyIGJ5IGEgdXNlciBwZXJtaXNzaW9uLicgfSxcbiAgeyBwYXRoOiAnL3dhcm11cCcsIHdhaXZlOiAnYWxsJywgb3B0aW9uYWw6IHRydWUsIHJlYXNvbjogJ1Bvb2wgd2FybS11cDsgdmVyaWZpZXMgYSBzaWduZWQgc2VydmljZSBwcmluY2lwYWwgaW5saW5lICg0MDRzIGV2ZXJ5b25lIGVsc2UpLicgfSxcbiAgeyBwYXRoOiAnL2RvY3Mvb3BlbmFwaS5qc29uJywgd2FpdmU6ICdhbGwnLCBvcHRpb25hbDogdHJ1ZSwgcmVhc29uOiAnUHVibGljIEFQSSBkZXNjcmlwdGlvbjsgZGlzYWJsZWQgaW4gcHJvZHVjdGlvbi4nIH0sXG4gIHsgcGF0aDogL15HRVQgXFwvZG9jcy8sIHdhaXZlOiAnYWxsJywgb3B0aW9uYWw6IHRydWUsIHJlYXNvbjogJ1N3YWdnZXIgVUkgYXNzZXRzOyBkaXNhYmxlZCBpbiBwcm9kdWN0aW9uLicgfSxcbiAgeyBwYXRoOiAvXihHRVR8UE9TVCkgXFwvbG9ncy8sIHdhaXZlOiAnYWxsJywgb3B0aW9uYWw6IHRydWUsIHJlYXNvbjogJ0J1aWxkLWxvZyBTU0Ugc3RyZWFtICsgaXRzIHRpY2tldCBtaW50IOKAlCBhdXRob3JpemVkIGJ5IGEgc2luZ2xlLXVzZSwgb3JnLWJvdW5kIHRpY2tldC4nIH0sXG5dO1xuXG5mdW5jdGlvbiBtYXRjaGVzKGU6IFJvdXRlQ292ZXJhZ2VFeGNlcHRpb24sIGVudHJ5OiBSb3V0ZVRhYmxlRW50cnkpOiBib29sZWFuIHtcbiAgaWYgKGUubWV0aG9kICYmIGUubWV0aG9kICE9PSAnKicgJiYgZS5tZXRob2QudG9VcHBlckNhc2UoKSAhPT0gZW50cnkubWV0aG9kKSByZXR1cm4gZmFsc2U7XG4gIGlmICh0eXBlb2YgZS5wYXRoID09PSAnc3RyaW5nJykgcmV0dXJuIGUucGF0aCA9PT0gZW50cnkucGF0aDtcbiAgcmV0dXJuIGUucGF0aC50ZXN0KGAke2VudHJ5Lm1ldGhvZH0gJHtlbnRyeS5wYXRofWApO1xufVxuXG5mdW5jdGlvbiBpc0dhdGVkKGVudHJ5OiBSb3V0ZVRhYmxlRW50cnkpOiBib29sZWFuIHtcbiAgLy8gQW4gSU5URVJOQUwgcm91dGUgKCMxNCkgY291bnRzIGFzIGdhdGVkOiBgcmVxdWlyZUludGVybmFsU2VydmljZWAgcmVmdXNlc1xuICAvLyBldmVyeSB1c2VyIHRva2VuIGFuZCBhZG1pdHMgb25seSB0aGUgbmFtZWQgcGVlciBzZXJ2aWNlcywgd2hpY2ggaXMgYVxuICAvLyBTVFJPTkdFUiByZXF1aXJlbWVudCB0aGFuIGFueSB1c2VyIHBlcm1pc3Npb24g4oCUIHNvIHRoZXNlIHJvdXRlcyBubyBsb25nZXJcbiAgLy8gbmVlZCBhIFwic2VydmljZSBwcmluY2lwYWwsIG5vIHVzZXIgcGVybWlzc2lvbiBhcHBsaWVzXCIgZXhjZXB0aW9uLlxuICByZXR1cm4gZW50cnkucGVybWlzc2lvbnMubGVuZ3RoID4gMCB8fCBlbnRyeS5zeXN0ZW1BZG1pbiB8fCBlbnRyeS5pbnRlcm5hbENhbGxlcnMubGVuZ3RoID4gMDtcbn1cblxuLyoqIFRoZSBvdXRjb21lIG9mIGEgY292ZXJhZ2UgY2hlY2s6IHdoYXQgZmFpbHMsIGFuZCB3aGljaCBleGNlcHRpb25zIHdlbnQgdW51c2VkLiAqL1xuZXhwb3J0IGludGVyZmFjZSBSb3V0ZUNvdmVyYWdlUmVzdWx0IHtcbiAgLyoqIE9uZSBodW1hbi1yZWFkYWJsZSBsaW5lIHBlciB1bmNvdmVyZWQgcm91dGUgKGVtcHR5IOKHkiBmdWxsIGNvdmVyYWdlKS4gKi9cbiAgdmlvbGF0aW9uczogc3RyaW5nW107XG4gIC8qKiBFeGNlcHRpb25zIHRoYXQgd2FpdmVkIG5vdGhpbmcg4oCUIHN0YWxlIGVudHJpZXMgdG8gZGVsZXRlLiAqL1xuICB1bnVzZWRFeGNlcHRpb25zOiBzdHJpbmdbXTtcbn1cblxuLyoqIENoZWNrIGEgc2VydmljZSdzIHJvdXRlIHRhYmxlIGFnYWluc3QgdGhlIGNvdmVyYWdlIHJ1bGVzLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGZpbmRSb3V0ZUNvdmVyYWdlVmlvbGF0aW9ucyhcbiAgdGFibGU6IHJlYWRvbmx5IFJvdXRlVGFibGVFbnRyeVtdLFxuICBleGNlcHRpb25zOiByZWFkb25seSBSb3V0ZUNvdmVyYWdlRXhjZXB0aW9uW10gPSBbXSxcbik6IFJvdXRlQ292ZXJhZ2VSZXN1bHQge1xuICBjb25zdCB2aW9sYXRpb25zOiBzdHJpbmdbXSA9IFtdO1xuICBjb25zdCB1c2VkID0gbmV3IFNldDxSb3V0ZUNvdmVyYWdlRXhjZXB0aW9uPigpO1xuXG4gIGNvbnN0IHdhaXZlZCA9IChlbnRyeTogUm91dGVUYWJsZUVudHJ5LCBydWxlOiAncGVybWlzc2lvbicgfCAnYXVkaXQnKTogYm9vbGVhbiA9PiB7XG4gICAgY29uc3QgaGl0ID0gZXhjZXB0aW9ucy5maW5kKChlKSA9PiAoZS53YWl2ZSA9PT0gcnVsZSB8fCBlLndhaXZlID09PSAnYWxsJykgJiYgbWF0Y2hlcyhlLCBlbnRyeSkpO1xuICAgIGlmIChoaXQpIHVzZWQuYWRkKGhpdCk7XG4gICAgcmV0dXJuIGhpdCAhPT0gdW5kZWZpbmVkO1xuICB9O1xuXG4gIGZvciAoY29uc3QgZW50cnkgb2YgdGFibGUpIHtcbiAgICBpZiAoZW50cnkubWV0aG9kID09PSAnT1BUSU9OUycpIGNvbnRpbnVlO1xuICAgIGNvbnN0IHdoZXJlID0gYCR7ZW50cnkubWV0aG9kfSAke2VudHJ5LnBhdGh9YDtcbiAgICAvLyBBbiBgL2ludGVybmFsL+KApmAgcGF0aCBpcyBpbnRlcm5hbCBieSBuYW1lLCBzbyBpdCBtdXN0IGJlIGludGVybmFsIGJ5IGdhdGVcbiAgICAvLyB0b28gKCMxNCkuIENoZWNrZWQgZm9yIEVWRVJZIHNlcnZpY2UsIHdpdGggbm8gZXhjZXB0aW9uIGxpc3Q6IGEgbmV3XG4gICAgLy8gaW50ZXJuYWwgcm91dGUgdGhhdCBmb3JnZXRzIGByZXF1aXJlSW50ZXJuYWxTZXJ2aWNlYCBpcyBhIGJyb3dzZXItcmVhY2hhYmxlXG4gICAgLy8gcGVlci1zZXJ2aWNlIEFQSSwgd2hpY2ggaXMgdGhlIGZhaWx1cmUgbW9kZSB0aGlzIHJ1bGUgZXhpc3RzIHRvIHByZXZlbnQuXG4gICAgaWYgKC8oXnxcXC8paW50ZXJuYWwoXFwvfCQpLy50ZXN0KGVudHJ5LnBhdGgpICYmIGVudHJ5LmludGVybmFsQ2FsbGVycy5sZW5ndGggPT09IDApIHtcbiAgICAgIHZpb2xhdGlvbnMucHVzaChgJHt3aGVyZX06IGFuIC9pbnRlcm5hbCByb3V0ZSB3aXRob3V0IHJlcXVpcmVJbnRlcm5hbFNlcnZpY2UoeyBjYWxsZXJzOiBbLi4uXSB9KSDigJQgdXNlciB0b2tlbnMgd291bGQgcmVhY2ggaXRgKTtcbiAgICB9XG4gICAgaWYgKCFpc0dhdGVkKGVudHJ5KSkge1xuICAgICAgaWYgKCF3YWl2ZWQoZW50cnksICdwZXJtaXNzaW9uJykpIHtcbiAgICAgICAgdmlvbGF0aW9ucy5wdXNoKGAke3doZXJlfTogbm8gcGVybWlzc2lvbiBnYXRlIChhZGQgcmVxdWlyZVBlcm1pc3Npb24vcmVxdWlyZVN5c3RlbUFkbWluLCBvciBhbiBleGNlcHRpb24gd2l0aCBhIHJlYXNvbilgKTtcbiAgICAgIH1cbiAgICB9IGVsc2UgaWYgKCFlbnRyeS5hdXRoKSB7XG4gICAgICB2aW9sYXRpb25zLnB1c2goYCR7d2hlcmV9OiBwZXJtaXNzaW9uIGdhdGUgd2l0aG91dCByZXF1aXJlQXV0aCDigJQgdGhlIGdhdGUgY2FuIG5ldmVyIHNlZSBhIHVzZXJgKTtcbiAgICB9XG4gICAgaWYgKGlzV3JpdGVNZXRob2QoZW50cnkubWV0aG9kKSAmJiBlbnRyeS5hdWRpdC5sZW5ndGggPT09IDAgJiYgIXdhaXZlZChlbnRyeSwgJ2F1ZGl0JykpIHtcbiAgICAgIHZpb2xhdGlvbnMucHVzaChgJHt3aGVyZX06IHdyaXRlIHJvdXRlIGRlY2xhcmVzIG5vIGF1ZGl0IGFjdGlvbiAod3JhcCB0aGUgaGFuZGxlciBjaGFpbiBpbiBhdWRpdGVkKCc8YWN0aW9uPicpKWApO1xuICAgIH1cbiAgfVxuXG4gIGNvbnN0IHVudXNlZEV4Y2VwdGlvbnMgPSBleGNlcHRpb25zXG4gICAgLmZpbHRlcigoZSkgPT4gIXVzZWQuaGFzKGUpICYmIGUub3B0aW9uYWwgIT09IHRydWUpXG4gICAgLm1hcCgoZSkgPT4gYCR7ZS5tZXRob2QgPz8gJyonfSAke1N0cmluZyhlLnBhdGgpfSDigJQgJHtlLnJlYXNvbn1gKTtcbiAgcmV0dXJuIHsgdmlvbGF0aW9ucywgdW51c2VkRXhjZXB0aW9ucyB9O1xufVxuXG4vKiogT25lIGludGVybmFsIHJvdXRlIGEgc2VydmljZSBkZWNsYXJlcywgYW5kIHRoZSBzZXJ2aWNlcyBhbGxvd2VkIHRvIGNhbGwgaXQuICovXG5leHBvcnQgaW50ZXJmYWNlIEludGVybmFsUm91dGVEZWNsYXJhdGlvbiB7XG4gIG1ldGhvZDogc3RyaW5nO1xuICBwYXRoOiBzdHJpbmc7XG4gIC8qKiBUaGUgYGNhbGxlcnNgIHBhc3NlZCB0byBgcmVxdWlyZUludGVybmFsU2VydmljZWAg4oCUIGFsc28gd2hhdCB0aGUgbWVzaCBwb2xpY3kgbmFtZXMuICovXG4gIGNhbGxlcnM6IHN0cmluZ1tdO1xufVxuXG4vKipcbiAqIENoZWNrIGEgc2VydmljZSdzIElOVEVSTkFMIHJvdXRlcyAoIzE0KSBhZ2FpbnN0IHdoYXQgaXQgZGVjbGFyZXMuXG4gKlxuICogVGhlIGRlY2xhcmF0aW9uIGlzIHRoZSBzaW5nbGUgbGlzdCBhIHNlcnZpY2UgbWFpbnRhaW5zLCBhbmQgaXQgaXMgY2hlY2tlZCBpblxuICogQk9USCBkaXJlY3Rpb25zLCBzbyBuZWl0aGVyIHNpZGUgY2FuIGRyaWZ0OlxuICpcbiAqICAtIGV2ZXJ5IGRlY2xhcmVkIHJvdXRlIG11c3QgZXhpc3QgYW5kIGJlIGdhdGVkIGJ5IGByZXF1aXJlSW50ZXJuYWxTZXJ2aWNlYFxuICogICAgd2l0aCBleGFjdGx5IHRoZSBkZWNsYXJlZCBjYWxsZXJzIChhbmQgYHJlcXVpcmVBdXRoYCBiZWZvcmUgaXQpO1xuICogIC0gZXZlcnkgcm91dGUgdGhlIHRhYmxlIHNheXMgaXMgaW50ZXJuYWwgbXVzdCBiZSBkZWNsYXJlZCDigJQgc28gYWRkaW5nIG9uZVxuICogICAgZm9yY2VzIGEgdmlzaWJsZSBjaGFuZ2UgaGVyZSwgYW5kIHRoZSBJc3RpbyBgQXV0aG9yaXphdGlvblBvbGljeWAgdGhhdFxuICogICAgbmFtZXMgdGhlIHNhbWUgY2FsbGVycyAoYGRlcGxveS8q4oCLL2s4cy9pc3Rpby1pbnRlcm5hbC1yb3V0ZXMueWFtbGApIGhhc1xuICogICAgb25lIGF1dGhvcml0YXRpdmUgbGlzdCB0byBiZSByZXZpZXdlZCBhZ2FpbnN0LlxuICovXG5leHBvcnQgZnVuY3Rpb24gZmluZEludGVybmFsUm91dGVWaW9sYXRpb25zKFxuICB0YWJsZTogcmVhZG9ubHkgUm91dGVUYWJsZUVudHJ5W10sXG4gIGRlY2xhcmVkOiByZWFkb25seSBJbnRlcm5hbFJvdXRlRGVjbGFyYXRpb25bXSxcbik6IHN0cmluZ1tdIHtcbiAgY29uc3QgdmlvbGF0aW9uczogc3RyaW5nW10gPSBbXTtcbiAgY29uc3Qga2V5ID0gKG1ldGhvZDogc3RyaW5nLCBwYXRoOiBzdHJpbmcpOiBzdHJpbmcgPT4gYCR7bWV0aG9kLnRvVXBwZXJDYXNlKCl9ICR7cGF0aH1gO1xuICBjb25zdCBkZWNsYXJlZEJ5S2V5ID0gbmV3IE1hcChkZWNsYXJlZC5tYXAoKGQpID0+IFtrZXkoZC5tZXRob2QsIGQucGF0aCksIGRdKSk7XG5cbiAgZm9yIChjb25zdCBkIG9mIGRlY2xhcmVkKSB7XG4gICAgY29uc3QgZW50cnkgPSB0YWJsZS5maW5kKChlKSA9PiBrZXkoZS5tZXRob2QsIGUucGF0aCkgPT09IGtleShkLm1ldGhvZCwgZC5wYXRoKSk7XG4gICAgaWYgKCFlbnRyeSkge1xuICAgICAgdmlvbGF0aW9ucy5wdXNoKGAke2tleShkLm1ldGhvZCwgZC5wYXRoKX06IGRlY2xhcmVkIGludGVybmFsIGJ1dCB0aGUgcm91dGUgZG9lcyBub3QgZXhpc3RgKTtcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cbiAgICBpZiAoIWVudHJ5LmF1dGgpIHZpb2xhdGlvbnMucHVzaChgJHtrZXkoZC5tZXRob2QsIGQucGF0aCl9OiBpbnRlcm5hbCByb3V0ZSB3aXRob3V0IHJlcXVpcmVBdXRoYCk7XG4gICAgY29uc3QgYWN0dWFsID0gWy4uLmVudHJ5LmludGVybmFsQ2FsbGVyc10uc29ydCgpLmpvaW4oJywgJyk7XG4gICAgY29uc3QgZXhwZWN0ZWQgPSBbLi4uZC5jYWxsZXJzXS5zb3J0KCkuam9pbignLCAnKTtcbiAgICBpZiAoYWN0dWFsICE9PSBleHBlY3RlZCkge1xuICAgICAgdmlvbGF0aW9ucy5wdXNoKGAke2tleShkLm1ldGhvZCwgZC5wYXRoKX06IGludGVybmFsIGNhbGxlcnMgYXJlIFske2FjdHVhbH1dIGJ1dCBbJHtleHBlY3RlZH1dIHdlcmUgZGVjbGFyZWRgKTtcbiAgICB9XG4gIH1cblxuICBmb3IgKGNvbnN0IGVudHJ5IG9mIHRhYmxlKSB7XG4gICAgaWYgKGVudHJ5LmludGVybmFsQ2FsbGVycy5sZW5ndGggPT09IDApIGNvbnRpbnVlO1xuICAgIGlmICghZGVjbGFyZWRCeUtleS5oYXMoa2V5KGVudHJ5Lm1ldGhvZCwgZW50cnkucGF0aCkpKSB7XG4gICAgICB2aW9sYXRpb25zLnB1c2goYCR7a2V5KGVudHJ5Lm1ldGhvZCwgZW50cnkucGF0aCl9OiBnYXRlZCBieSByZXF1aXJlSW50ZXJuYWxTZXJ2aWNlIGJ1dCBub3QgZGVjbGFyZWQgKGFkZCBpdCBoZXJlIGFuZCB0byB0aGUgbWVzaCBwb2xpY3kpYCk7XG4gICAgfVxuICB9XG4gIHJldHVybiB2aW9sYXRpb25zO1xufVxuXG4vKiogRXZlcnkgYXVkaXQgYWN0aW9uIGRlY2xhcmVkIGFueXdoZXJlIGluIGEgcm91dGUgdGFibGUuICovXG5leHBvcnQgZnVuY3Rpb24gZGVjbGFyZWRBdWRpdEFjdGlvbnModGFibGU6IHJlYWRvbmx5IFJvdXRlVGFibGVFbnRyeVtdKTogc3RyaW5nW10ge1xuICByZXR1cm4gWy4uLm5ldyBTZXQodGFibGUuZmxhdE1hcCgoZSkgPT4gZS5hdWRpdCkpXS5zb3J0KCk7XG59XG5cbi8qKlxuICogQ29tcGFyZSBhIHNlcnZpY2UncyByb3V0ZSB0YWJsZSB0byBpdHMgY2hlY2tlZC1pbiBjb3B5IHVuZGVyXG4gKiBgZnJvbnRlbmQvc3JjL2dlbmVyYXRlZC9yb3V0ZS10YWJsZS9gIOKAlCB0aGUgZmlsZSB0aGUgZnJvbnRlbmQncyBgY2FuKC4uLilgXG4gKiB0ZXN0cyByZWFkLiBSZXR1cm5zIGEgZGlmZiBtZXNzYWdlLCBvciBgbnVsbGAgd2hlbiB0aGV5IG1hdGNoLiBTZXRcbiAqIGBVUERBVEVfUk9VVEVfVEFCTEVTPTFgIHRvIHJld3JpdGUgdGhlIGZpbGUgYWZ0ZXIgYW4gaW50ZW5kZWQgcm91dGUgY2hhbmdlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gY29tcGFyZVJvdXRlVGFibGVTbmFwc2hvdCh0YWJsZTogcmVhZG9ubHkgUm91dGVUYWJsZUVudHJ5W10sIGZpbGU6IHN0cmluZyk6IHN0cmluZyB8IG51bGwge1xuICBjb25zdCBzZXJpYWxpemVkID0gYCR7SlNPTi5zdHJpbmdpZnkodGFibGUsIG51bGwsIDIpfVxcbmA7XG4gIGlmIChwcm9jZXNzLmVudi5VUERBVEVfUk9VVEVfVEFCTEVTID09PSAnMScpIHtcbiAgICBta2RpclN5bmMoZGlybmFtZShmaWxlKSwgeyByZWN1cnNpdmU6IHRydWUgfSk7XG4gICAgd3JpdGVGaWxlU3luYyhmaWxlLCBzZXJpYWxpemVkLCAndXRmOCcpO1xuICAgIHJldHVybiBudWxsO1xuICB9XG4gIGxldCBjdXJyZW50OiBzdHJpbmc7XG4gIHRyeSB7XG4gICAgY3VycmVudCA9IHJlYWRGaWxlU3luYyhmaWxlLCAndXRmOCcpO1xuICB9IGNhdGNoIHtcbiAgICByZXR1cm4gYCR7ZmlsZX0gaXMgbWlzc2luZy4gUmUtcnVuIHRoaXMgdGVzdCB3aXRoIFVQREFURV9ST1VURV9UQUJMRVM9MSB0byBnZW5lcmF0ZSBpdC5gO1xuICB9XG4gIGlmIChjdXJyZW50ID09PSBzZXJpYWxpemVkKSByZXR1cm4gbnVsbDtcbiAgY29uc3QgZXhwZWN0ZWQ6IFJvdXRlVGFibGVFbnRyeVtdID0gSlNPTi5wYXJzZShjdXJyZW50KTtcbiAgY29uc3Qga2V5ID0gKGU6IFJvdXRlVGFibGVFbnRyeSk6IHN0cmluZyA9PiBgJHtlLm1ldGhvZH0gJHtlLnBhdGh9YDtcbiAgY29uc3QgYmVmb3JlID0gbmV3IFNldChleHBlY3RlZC5tYXAoa2V5KSk7XG4gIGNvbnN0IGFmdGVyID0gbmV3IFNldCh0YWJsZS5tYXAoa2V5KSk7XG4gIGNvbnN0IGFkZGVkID0gWy4uLmFmdGVyXS5maWx0ZXIoKGspID0+ICFiZWZvcmUuaGFzKGspKTtcbiAgY29uc3QgcmVtb3ZlZCA9IFsuLi5iZWZvcmVdLmZpbHRlcigoaykgPT4gIWFmdGVyLmhhcyhrKSk7XG4gIGNvbnN0IGNoYW5nZWQgPSB0YWJsZVxuICAgIC5maWx0ZXIoKGUpID0+IGJlZm9yZS5oYXMoa2V5KGUpKSlcbiAgICAuZmlsdGVyKChlKSA9PiBKU09OLnN0cmluZ2lmeShlKSAhPT0gSlNPTi5zdHJpbmdpZnkoZXhwZWN0ZWQuZmluZCgoeCkgPT4ga2V5KHgpID09PSBrZXkoZSkpKSlcbiAgICAubWFwKGtleSk7XG4gIHJldHVybiBbXG4gICAgYCR7ZmlsZX0gaXMgb3V0IG9mIGRhdGUgKHRoZSBmcm9udGVuZCByZWFkcyBpdCB0byBjaGVjayBpdHMgY2FuKC4uLikgZ2F0ZXMpLmAsXG4gICAgYWRkZWQubGVuZ3RoID8gYCAgYWRkZWQ6ICAgJHthZGRlZC5qb2luKCcsICcpfWAgOiAnJyxcbiAgICByZW1vdmVkLmxlbmd0aCA/IGAgIHJlbW92ZWQ6ICR7cmVtb3ZlZC5qb2luKCcsICcpfWAgOiAnJyxcbiAgICBjaGFuZ2VkLmxlbmd0aCA/IGAgIGNoYW5nZWQ6ICR7Y2hhbmdlZC5qb2luKCcsICcpfWAgOiAnJyxcbiAgICAnICBSZS1ydW4gd2l0aCBVUERBVEVfUk9VVEVfVEFCTEVTPTEgdG8gcmVmcmVzaCBpdCwgdGhlbiByZXZpZXcgdGhlIGRpZmYuJyxcbiAgXS5maWx0ZXIoQm9vbGVhbikuam9pbignXFxuJyk7XG59XG4iXX0=
@@ -1,66 +0,0 @@
1
- /**
2
- * Test helper for the PER-SERVICE internal-token chain (#14) in its REAL,
3
- * `configured` shape: private keys on disk, one public bundle, `SERVICE_NAME`
4
- * naming the signer.
5
- *
6
- * Most suites need none of this — with no `SERVICE_SIGNING_KEY_FILE` the signer
7
- * runs in ephemeral mode and a single process can mint and verify its own
8
- * service tokens with zero setup (see `services/service-keys.ts`). This helper
9
- * exists for the tests that must exercise the DEPLOYED configuration: the
10
- * mesh-less internal-route test, and the key-rotation test. It writes the same
11
- * files `deploy/bin/service-signing-keys.sh` generates, so what the tests verify
12
- * is what the deploy produces.
13
- *
14
- * ```ts
15
- * const keys = installTestServiceKeys(['billing', 'compliance']);
16
- * const token = keys.sign('billing', { role: 'member', orgId: 'o1' });
17
- * // …
18
- * keys.uninstall();
19
- * ```
20
- */
21
- import { type KeyObject } from 'crypto';
22
- import { type PublicJwk } from '../utils/jwk.js';
23
- /** One generated service keypair, in the forms a test needs. */
24
- export interface TestServiceKey {
25
- serviceName: string;
26
- kid: string;
27
- privateKey: KeyObject;
28
- jwk: PublicJwk;
29
- /** Path of the PKCS#8 PEM written for this service. */
30
- keyFile: string;
31
- }
32
- /** Handle over an installed set of per-service keys. */
33
- export interface TestServiceKeysHandle {
34
- keys: Map<string, TestServiceKey>;
35
- /** Directory holding the key files and `bundle.json`. */
36
- dir: string;
37
- bundleFile: string;
38
- /** Act AS `serviceName` for subsequent `signServiceToken` calls. */
39
- becomeService(serviceName: string): void;
40
- /** Mint a well-formed internal token for `serviceName`, as that service would. */
41
- sign(serviceName: string, claims?: Record<string, unknown>, expiresInSeconds?: number): string;
42
- /**
43
- * Mint a token whose `sub` names `subjectService` but which is SIGNED with
44
- * `signerService`'s key — the cross-service forgery the per-service keys make
45
- * detectable. Every verifier must refuse it.
46
- */
47
- signAs(signerService: string, subjectService: string, claims?: Record<string, unknown>): string;
48
- /** Rewrite `bundle.json` with only the named services' keys (rotation / trust changes). */
49
- publish(serviceNames: string[]): void;
50
- /**
51
- * Rewrite `bundle.json` from an explicit service → keys map, so a test can
52
- * publish TWO keys for one service (the rotation-overlap shape) or publish a
53
- * key under a name it was not generated for.
54
- */
55
- publishKeys(keysByService: Record<string, TestServiceKey[]>): void;
56
- /** Restore the previous environment and delete the generated files. */
57
- uninstall(): void;
58
- }
59
- /** The claim block a SERVICE principal must carry to satisfy `hasValidIdentityClaims`. */
60
- export declare function testServiceIdentityClaims(serviceName: string, role?: 'owner' | 'admin' | 'member'): Record<string, unknown>;
61
- /**
62
- * Generate a keypair per name, write the private keys and the public bundle to a
63
- * temp directory, and point `SERVICE_SIGNING_KEY_FILE` / `SERVICE_KEY_BUNDLE_FILE`
64
- * / `SERVICE_NAME` at them. The first name becomes this process's identity.
65
- */
66
- export declare function installTestServiceKeys(serviceNames: string[]): TestServiceKeysHandle;
@@ -1,137 +0,0 @@
1
- // Copyright 2026 Pipeline Builder Contributors
2
- // SPDX-License-Identifier: Apache-2.0
3
- /**
4
- * Test helper for the PER-SERVICE internal-token chain (#14) in its REAL,
5
- * `configured` shape: private keys on disk, one public bundle, `SERVICE_NAME`
6
- * naming the signer.
7
- *
8
- * Most suites need none of this — with no `SERVICE_SIGNING_KEY_FILE` the signer
9
- * runs in ephemeral mode and a single process can mint and verify its own
10
- * service tokens with zero setup (see `services/service-keys.ts`). This helper
11
- * exists for the tests that must exercise the DEPLOYED configuration: the
12
- * mesh-less internal-route test, and the key-rotation test. It writes the same
13
- * files `deploy/bin/service-signing-keys.sh` generates, so what the tests verify
14
- * is what the deploy produces.
15
- *
16
- * ```ts
17
- * const keys = installTestServiceKeys(['billing', 'compliance']);
18
- * const token = keys.sign('billing', { role: 'member', orgId: 'o1' });
19
- * // …
20
- * keys.uninstall();
21
- * ```
22
- */
23
- import crypto, {} from 'crypto';
24
- import { mkdtempSync, rmSync, writeFileSync } from 'fs';
25
- import { tmpdir } from 'os';
26
- import { join } from 'path';
27
- import { _resetServiceKeysForTests, signServiceJwt } from '../services/service-keys.js';
28
- import { publicJwkFrom } from '../utils/jwk.js';
29
- /** The claim block a SERVICE principal must carry to satisfy `hasValidIdentityClaims`. */
30
- export function testServiceIdentityClaims(serviceName, role = 'member') {
31
- return {
32
- username: `${serviceName}-service`,
33
- email: `${serviceName}@internal`,
34
- principalType: 'service',
35
- token_use: 'access',
36
- type: 'access',
37
- role,
38
- isAdmin: role === 'owner' || role === 'admin',
39
- };
40
- }
41
- /** base64url of a JSON value, the JWS way. */
42
- function b64uJson(value) {
43
- return Buffer.from(JSON.stringify(value), 'utf-8').toString('base64url');
44
- }
45
- /**
46
- * Generate a keypair per name, write the private keys and the public bundle to a
47
- * temp directory, and point `SERVICE_SIGNING_KEY_FILE` / `SERVICE_KEY_BUNDLE_FILE`
48
- * / `SERVICE_NAME` at them. The first name becomes this process's identity.
49
- */
50
- export function installTestServiceKeys(serviceNames) {
51
- if (serviceNames.length === 0)
52
- throw new Error('installTestServiceKeys needs at least one service name');
53
- const dir = mkdtempSync(join(tmpdir(), 'pb-service-keys-'));
54
- const bundleFile = join(dir, 'bundle.json');
55
- const previous = {
56
- SERVICE_NAME: process.env.SERVICE_NAME,
57
- SERVICE_SIGNING_KEY_FILE: process.env.SERVICE_SIGNING_KEY_FILE,
58
- SERVICE_KEY_BUNDLE_FILE: process.env.SERVICE_KEY_BUNDLE_FILE,
59
- };
60
- const keys = new Map();
61
- for (const serviceName of serviceNames) {
62
- const { privateKey, publicKey } = crypto.generateKeyPairSync('ec', { namedCurve: 'prime256v1' });
63
- const keyFile = join(dir, `${serviceName}.key`);
64
- writeFileSync(keyFile, privateKey.export({ type: 'pkcs8', format: 'pem' }), 'utf8');
65
- const jwk = publicJwkFrom(publicKey);
66
- keys.set(serviceName, { serviceName, kid: jwk.kid, privateKey, jwk, keyFile });
67
- }
68
- const writeBundleFrom = (keysByService) => {
69
- const bundle = { services: {} };
70
- for (const [name, entries] of Object.entries(keysByService)) {
71
- bundle.services[name] = { keys: entries.map((k) => k.jwk) };
72
- }
73
- writeFileSync(bundleFile, `${JSON.stringify(bundle, null, 2)}\n`, 'utf8');
74
- _resetServiceKeysForTests();
75
- };
76
- const writeBundle = (names) => {
77
- writeBundleFrom(Object.fromEntries(names.map((name) => {
78
- const key = keys.get(name);
79
- if (!key)
80
- throw new Error(`No generated key for ${name}`);
81
- return [name, [key]];
82
- })));
83
- };
84
- const become = (serviceName) => {
85
- const key = keys.get(serviceName);
86
- if (!key)
87
- throw new Error(`No generated key for ${serviceName}`);
88
- process.env.SERVICE_NAME = serviceName;
89
- process.env.SERVICE_SIGNING_KEY_FILE = key.keyFile;
90
- _resetServiceKeysForTests();
91
- };
92
- process.env.SERVICE_KEY_BUNDLE_FILE = bundleFile;
93
- writeBundle(serviceNames);
94
- become(serviceNames[0]);
95
- return {
96
- keys,
97
- dir,
98
- bundleFile,
99
- becomeService: become,
100
- sign(serviceName, claims = {}, expiresInSeconds = 300) {
101
- become(serviceName);
102
- return signServiceJwt({ ...testServiceIdentityClaims(serviceName), ...claims }, { serviceName, expiresInSeconds });
103
- },
104
- signAs(signerService, subjectService, claims = {}) {
105
- // Deliberately bypasses `signServiceJwt`, which refuses to sign for a name
106
- // it does not hold — the point here is to produce the forgery a verifier
107
- // must catch, not to reproduce it through the legitimate signer.
108
- const signer = keys.get(signerService);
109
- if (!signer)
110
- throw new Error(`No generated key for ${signerService}`);
111
- const now = Math.floor(Date.now() / 1000);
112
- const body = {
113
- ...testServiceIdentityClaims(subjectService),
114
- ...claims,
115
- sub: `service:${subjectService}`,
116
- iat: now,
117
- exp: now + 300,
118
- };
119
- const signingInput = `${b64uJson({ alg: 'ES256', typ: 'JWT', kid: signer.kid })}.${b64uJson(body)}`;
120
- const signature = crypto.sign('sha256', Buffer.from(signingInput, 'utf-8'), { key: signer.privateKey, dsaEncoding: 'ieee-p1363' });
121
- return `${signingInput}.${signature.toString('base64url')}`;
122
- },
123
- publish: writeBundle,
124
- publishKeys: writeBundleFrom,
125
- uninstall() {
126
- for (const [name, value] of Object.entries(previous)) {
127
- if (value === undefined)
128
- delete process.env[name];
129
- else
130
- process.env[name] = value;
131
- }
132
- _resetServiceKeysForTests();
133
- rmSync(dir, { recursive: true, force: true });
134
- },
135
- };
136
- }
137
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmljZS10b2tlbnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdGVzdGluZy9zZXJ2aWNlLXRva2Vucy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDOzs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBbUJHO0FBRUgsT0FBTyxNQUFNLEVBQUUsRUFBa0IsTUFBTSxRQUFRLENBQUM7QUFDaEQsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLEVBQUUsYUFBYSxFQUFFLE1BQU0sSUFBSSxDQUFDO0FBQ3hELE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxJQUFJLENBQUM7QUFDNUIsT0FBTyxFQUFFLElBQUksRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUM1QixPQUFPLEVBQUUseUJBQXlCLEVBQUUsY0FBYyxFQUF5QixNQUFNLDZCQUE2QixDQUFDO0FBQy9HLE9BQU8sRUFBRSxhQUFhLEVBQWtCLE1BQU0saUJBQWlCLENBQUM7QUF3Q2hFLDBGQUEwRjtBQUMxRixNQUFNLFVBQVUseUJBQXlCLENBQUMsV0FBbUIsRUFBRSxJQUFJLEdBQWlDLFFBQVE7SUFDMUcsT0FBTztRQUNMLFFBQVEsRUFBRSxHQUFHLFdBQVcsVUFBVTtRQUNsQyxLQUFLLEVBQUUsR0FBRyxXQUFXLFdBQVc7UUFDaEMsYUFBYSxFQUFFLFNBQVM7UUFDeEIsU0FBUyxFQUFFLFFBQVE7UUFDbkIsSUFBSSxFQUFFLFFBQVE7UUFDZCxJQUFJO1FBQ0osT0FBTyxFQUFFLElBQUksS0FBSyxPQUFPLElBQUksSUFBSSxLQUFLLE9BQU87S0FDOUMsQ0FBQztBQUNKLENBQUM7QUFFRCw4Q0FBOEM7QUFDOUMsU0FBUyxRQUFRLENBQUMsS0FBYztJQUM5QixPQUFPLE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsRUFBRSxPQUFPLENBQUMsQ0FBQyxRQUFRLENBQUMsV0FBVyxDQUFDLENBQUM7QUFD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Q0FBQyxHQUFHLEtBQUssQ0FBQztZQUNqQyxDQUFDO1lBQ0QseUJBQXlCLEVBQUUsQ0FBQztZQUM1QixNQUFNLENBQUMsR0FBRyxFQUFFLEVBQUUsU0FBUyxFQUFFLElBQUksRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztRQUNoRCxDQUFDO0tBQ0YsQ0FBQztBQUNKLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuLyoqXG4gKiBUZXN0IGhlbHBlciBmb3IgdGhlIFBFUi1TRVJWSUNFIGludGVybmFsLXRva2VuIGNoYWluICgjMTQpIGluIGl0cyBSRUFMLFxuICogYGNvbmZpZ3VyZWRgIHNoYXBlOiBwcml2YXRlIGtleXMgb24gZGlzaywgb25lIHB1YmxpYyBidW5kbGUsIGBTRVJWSUNFX05BTUVgXG4gKiBuYW1pbmcgdGhlIHNpZ25lci5cbiAqXG4gKiBNb3N0IHN1aXRlcyBuZWVkIG5vbmUgb2YgdGhpcyDigJQgd2l0aCBubyBgU0VSVklDRV9TSUdOSU5HX0tFWV9GSUxFYCB0aGUgc2lnbmVyXG4gKiBydW5zIGluIGVwaGVtZXJhbCBtb2RlIGFuZCBhIHNpbmdsZSBwcm9jZXNzIGNhbiBtaW50IGFuZCB2ZXJpZnkgaXRzIG93blxuICogc2VydmljZSB0b2tlbnMgd2l0aCB6ZXJvIHNldHVwIChzZWUgYHNlcnZpY2VzL3NlcnZpY2Uta2V5cy50c2ApLiBUaGlzIGhlbHBlclxuICogZXhpc3RzIGZvciB0aGUgdGVzdHMgdGhhdCBtdXN0IGV4ZXJjaXNlIHRoZSBERVBMT1lFRCBjb25maWd1cmF0aW9uOiB0aGVcbiAqIG1lc2gtbGVzcyBpbnRlcm5hbC1yb3V0ZSB0ZXN0LCBhbmQgdGhlIGtleS1yb3RhdGlvbiB0ZXN0LiBJdCB3cml0ZXMgdGhlIHNhbWVcbiAqIGZpbGVzIGBkZXBsb3kvYmluL3NlcnZpY2Utc2lnbmluZy1rZXlzLnNoYCBnZW5lcmF0ZXMsIHNvIHdoYXQgdGhlIHRlc3RzIHZlcmlmeVxuICogaXMgd2hhdCB0aGUgZGVwbG95IHByb2R1Y2VzLlxuICpcbiAqIGBgYHRzXG4gKiBjb25zdCBrZXlzID0gaW5zdGFsbFRlc3RTZXJ2aWNlS2V5cyhbJ2JpbGxpbmcnLCAnY29tcGxpYW5jZSddKTtcbiAqIGNvbnN0IHRva2VuID0ga2V5cy5zaWduKCdiaWxsaW5nJywgeyByb2xlOiAnbWVtYmVyJywgb3JnSWQ6ICdvMScgfSk7XG4gKiAvLyDigKZcbiAqIGtleXMudW5pbnN0YWxsKCk7XG4gKiBgYGBcbiAqL1xuXG5pbXBvcnQgY3J5cHRvLCB7IHR5cGUgS2V5T2JqZWN0IH0gZnJvbSAnY3J5cHRvJztcbmltcG9ydCB7IG1rZHRlbXBTeW5jLCBybVN5bmMsIHdyaXRlRmlsZVN5bmMgfSBmcm9tICdmcyc7XG5pbXBvcnQgeyB0bXBkaXIgfSBmcm9tICdvcyc7XG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XG5pbXBvcnQgeyBfcmVzZXRTZXJ2aWNlS2V5c0ZvclRlc3RzLCBzaWduU2VydmljZUp3dCwgdHlwZSBTZXJ2aWNlS2V5QnVuZGxlIH0gZnJvbSAnLi4vc2VydmljZXMvc2VydmljZS1rZXlzLmpzJztcbmltcG9ydCB7IHB1YmxpY0p3a0Zyb20sIHR5cGUgUHVibGljSndrIH0gZnJvbSAnLi4vdXRpbHMvandrLmpzJztcblxuLyoqIE9uZSBnZW5lcmF0ZWQgc2VydmljZSBrZXlwYWlyLCBpbiB0aGUgZm9ybXMgYSB0ZXN0IG5lZWRzLiAqL1xuZXhwb3J0IGludGVyZmFjZSBUZXN0U2VydmljZUtleSB7XG4gIHNlcnZpY2VOYW1lOiBzdHJpbmc7XG4gIGtpZDogc3RyaW5nO1xuICBwcml2YXRlS2V5OiBLZXlPYmplY3Q7XG4gIGp3azogUHVibGljSndrO1xuICAvKiogUGF0aCBvZiB0aGUgUEtDUyM4IFBFTSB3cml0dGVuIGZvciB0aGlzIHNlcnZpY2UuICovXG4gIGtleUZpbGU6IHN0cmluZztcbn1cblxuLyoqIEhhbmRsZSBvdmVyIGFuIGluc3RhbGxlZCBzZXQgb2YgcGVyLXNlcnZpY2Uga2V5cy4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgVGVzdFNlcnZpY2VLZXlzSGFuZGxlIHtcbiAga2V5czogTWFwPHN0cmluZywgVGVzdFNlcnZpY2VLZXk+O1xuICAvKiogRGlyZWN0b3J5IGhvbGRpbmcgdGhlIGtleSBmaWxlcyBhbmQgYGJ1bmRsZS5qc29uYC4gKi9cbiAgZGlyOiBzdHJpbmc7XG4gIGJ1bmRsZUZpbGU6IHN0cmluZztcbiAgLyoqIEFjdCBBUyBgc2VydmljZU5hbWVgIGZvciBzdWJzZXF1ZW50IGBzaWduU2VydmljZVRva2VuYCBjYWxscy4gKi9cbiAgYmVjb21lU2VydmljZShzZXJ2aWNlTmFtZTogc3RyaW5nKTogdm9pZDtcbiAgLyoqIE1pbnQgYSB3ZWxsLWZvcm1lZCBpbnRlcm5hbCB0b2tlbiBmb3IgYHNlcnZpY2VOYW1lYCwgYXMgdGhhdCBzZXJ2aWNlIHdvdWxkLiAqL1xuICBzaWduKHNlcnZpY2VOYW1lOiBzdHJpbmcsIGNsYWltcz86IFJlY29yZDxzdHJpbmcsIHVua25vd24+LCBleHBpcmVzSW5TZWNvbmRzPzogbnVtYmVyKTogc3RyaW5nO1xuICAvKipcbiAgICogTWludCBhIHRva2VuIHdob3NlIGBzdWJgIG5hbWVzIGBzdWJqZWN0U2VydmljZWAgYnV0IHdoaWNoIGlzIFNJR05FRCB3aXRoXG4gICAqIGBzaWduZXJTZXJ2aWNlYCdzIGtleSDigJQgdGhlIGNyb3NzLXNlcnZpY2UgZm9yZ2VyeSB0aGUgcGVyLXNlcnZpY2Uga2V5cyBtYWtlXG4gICAqIGRldGVjdGFibGUuIEV2ZXJ5IHZlcmlmaWVyIG11c3QgcmVmdXNlIGl0LlxuICAgKi9cbiAgc2lnbkFzKHNpZ25lclNlcnZpY2U6IHN0cmluZywgc3ViamVjdFNlcnZpY2U6IHN0cmluZywgY2xhaW1zPzogUmVjb3JkPHN0cmluZywgdW5rbm93bj4pOiBzdHJpbmc7XG4gIC8qKiBSZXdyaXRlIGBidW5kbGUuanNvbmAgd2l0aCBvbmx5IHRoZSBuYW1lZCBzZXJ2aWNlcycga2V5cyAocm90YXRpb24gLyB0cnVzdCBjaGFuZ2VzKS4gKi9cbiAgcHVibGlzaChzZXJ2aWNlTmFtZXM6IHN0cmluZ1tdKTogdm9pZDtcbiAgLyoqXG4gICAqIFJld3JpdGUgYGJ1bmRsZS5qc29uYCBmcm9tIGFuIGV4cGxpY2l0IHNlcnZpY2Ug4oaSIGtleXMgbWFwLCBzbyBhIHRlc3QgY2FuXG4gICAqIHB1Ymxpc2ggVFdPIGtleXMgZm9yIG9uZSBzZXJ2aWNlICh0aGUgcm90YXRpb24tb3ZlcmxhcCBzaGFwZSkgb3IgcHVibGlzaCBhXG4gICAqIGtleSB1bmRlciBhIG5hbWUgaXQgd2FzIG5vdCBnZW5lcmF0ZWQgZm9yLlxuICAgKi9cbiAgcHVibGlzaEtleXMoa2V5c0J5U2VydmljZTogUmVjb3JkPHN0cmluZywgVGVzdFNlcnZpY2VLZXlbXT4pOiB2b2lkO1xuICAvKiogUmVzdG9yZSB0aGUgcHJldmlvdXMgZW52aXJvbm1lbnQgYW5kIGRlbGV0ZSB0aGUgZ2VuZXJhdGVkIGZpbGVzLiAqL1xuICB1bmluc3RhbGwoKTogdm9pZDtcbn1cblxuLyoqIFRoZSBjbGFpbSBibG9jayBhIFNFUlZJQ0UgcHJpbmNpcGFsIG11c3QgY2FycnkgdG8gc2F0aXNmeSBgaGFzVmFsaWRJZGVudGl0eUNsYWltc2AuICovXG5leHBvcnQgZnVuY3Rpb24gdGVzdFNlcnZpY2VJZGVudGl0eUNsYWltcyhzZXJ2aWNlTmFtZTogc3RyaW5nLCByb2xlOiAnb3duZXInIHwgJ2FkbWluJyB8ICdtZW1iZXInID0gJ21lbWJlcicpOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB7XG4gIHJldHVybiB7XG4gICAgdXNlcm5hbWU6IGAke3NlcnZpY2VOYW1lfS1zZXJ2aWNlYCxcbiAgICBlbWFpbDogYCR7c2VydmljZU5hbWV9QGludGVybmFsYCxcbiAgICBwcmluY2lwYWxUeXBlOiAnc2VydmljZScsXG4gICAgdG9rZW5fdXNlOiAnYWNjZXNzJyxcbiAgICB0eXBlOiAnYWNjZXNzJyxcbiAgICByb2xlLFxuICAgIGlzQWRtaW46IHJvbGUgPT09ICdvd25lcicgfHwgcm9sZSA9PT0gJ2FkbWluJyxcbiAgfTtcbn1cblxuLyoqIGJhc2U2NHVybCBvZiBhIEpTT04gdmFsdWUsIHRoZSBKV1Mgd2F5LiAqL1xuZnVuY3Rpb24gYjY0dUpzb24odmFsdWU6IHVua25vd24pOiBzdHJpbmcge1xuICByZXR1cm4gQnVmZmVyLmZyb20oSlNPTi5zdHJpbmdpZnkodmFsdWUpLCAndXRmLTgnKS50b1N0cmluZygnYmFzZTY0dXJsJyk7XG59XG5cbi8qKlxuICogR2VuZXJhdGUgYSBrZXlwYWlyIHBlciBuYW1lLCB3cml0ZSB0aGUgcHJpdmF0ZSBrZXlzIGFuZCB0aGUgcHVibGljIGJ1bmRsZSB0byBhXG4gKiB0ZW1wIGRpcmVjdG9yeSwgYW5kIHBvaW50IGBTRVJWSUNFX1NJR05JTkdfS0VZX0ZJTEVgIC8gYFNFUlZJQ0VfS0VZX0JVTkRMRV9GSUxFYFxuICogLyBgU0VSVklDRV9OQU1FYCBhdCB0aGVtLiBUaGUgZmlyc3QgbmFtZSBiZWNvbWVzIHRoaXMgcHJvY2VzcydzIGlkZW50aXR5LlxuICovXG5leHBvcnQgZnVuY3Rpb24gaW5zdGFsbFRlc3RTZXJ2aWNlS2V5cyhzZXJ2aWNlTmFtZXM6IHN0cmluZ1tdKTogVGVzdFNlcnZpY2VLZXlzSGFuZGxlIHtcbiAgaWYgKHNlcnZpY2VOYW1lcy5sZW5ndGggPT09IDApIHRocm93IG5ldyBFcnJvcignaW5zdGFsbFRlc3RTZXJ2aWNlS2V5cyBuZWVkcyBhdCBsZWFzdCBvbmUgc2VydmljZSBuYW1lJyk7XG4gIGNvbnN0IGRpciA9IG1rZHRlbXBTeW5jKGpvaW4odG1wZGlyKCksICdwYi1zZXJ2aWNlLWtleXMtJykpO1xuICBjb25zdCBidW5kbGVGaWxlID0gam9pbihkaXIsICdidW5kbGUuanNvbicpO1xuICBjb25zdCBwcmV2aW91cyA9IHtcbiAgICBTRVJWSUNFX05BTUU6IHByb2Nlc3MuZW52LlNFUlZJQ0VfTkFNRSxcbiAgICBTRVJWSUNFX1NJR05JTkdfS0VZX0ZJTEU6IHByb2Nlc3MuZW52LlNFUlZJQ0VfU0lHTklOR19LRVlfRklMRSxcbiAgICBTRVJWSUNFX0tFWV9CVU5ETEVfRklMRTogcHJvY2Vzcy5lbnYuU0VSVklDRV9LRVlfQlVORExFX0ZJTEUsXG4gIH07XG5cbiAgY29uc3Qga2V5cyA9IG5ldyBNYXA8c3RyaW5nLCBUZXN0U2VydmljZUtleT4oKTtcbiAgZm9yIChjb25zdCBzZXJ2aWNlTmFtZSBvZiBzZXJ2aWNlTmFtZXMpIHtcbiAgICBjb25zdCB7IHByaXZhdGVLZXksIHB1YmxpY0tleSB9ID0gY3J5cHRvLmdlbmVyYXRlS2V5UGFpclN5bmMoJ2VjJywgeyBuYW1lZEN1cnZlOiAncHJpbWUyNTZ2MScgfSk7XG4gICAgY29uc3Qga2V5RmlsZSA9IGpvaW4oZGlyLCBgJHtzZXJ2aWNlTmFtZX0ua2V5YCk7XG4gICAgd3JpdGVGaWxlU3luYyhrZXlGaWxlLCBwcml2YXRlS2V5LmV4cG9ydCh7IHR5cGU6ICdwa2NzOCcsIGZvcm1hdDogJ3BlbScgfSkgYXMgc3RyaW5nLCAndXRmOCcpO1xuICAgIGNvbnN0IGp3ayA9IHB1YmxpY0p3a0Zyb20ocHVibGljS2V5KTtcbiAgICBrZXlzLnNldChzZXJ2aWNlTmFtZSwgeyBzZXJ2aWNlTmFtZSwga2lkOiBqd2sua2lkLCBwcml2YXRlS2V5LCBqd2ssIGtleUZpbGUgfSk7XG4gIH1cblxuICBjb25zdCB3cml0ZUJ1bmRsZUZyb20gPSAoa2V5c0J5U2VydmljZTogUmVjb3JkPHN0cmluZywgVGVzdFNlcnZpY2VLZXlbXT4pOiB2b2lkID0+IHtcbiAgICBjb25zdCBidW5kbGU6IFNlcnZpY2VLZXlCdW5kbGUgPSB7IHNlcnZpY2VzOiB7fSB9O1xuICAgIGZvciAoY29uc3QgW25hbWUsIGVudHJpZXNdIG9mIE9iamVjdC5lbnRyaWVzKGtleXNCeVNlcnZpY2UpKSB7XG4gICAgICBidW5kbGUuc2VydmljZXNbbmFtZV0gPSB7IGtleXM6IGVudHJpZXMubWFwKChrKSA9PiBrLmp3aykgfTtcbiAgICB9XG4gICAgd3JpdGVGaWxlU3luYyhidW5kbGVGaWxlLCBgJHtKU09OLnN0cmluZ2lmeShidW5kbGUsIG51bGwsIDIpfVxcbmAsICd1dGY4Jyk7XG4gICAgX3Jlc2V0U2VydmljZUtleXNGb3JUZXN0cygpO1xuICB9O1xuXG4gIGNvbnN0IHdyaXRlQnVuZGxlID0gKG5hbWVzOiBzdHJpbmdbXSk6IHZvaWQgPT4ge1xuICAgIHdyaXRlQnVuZGxlRnJvbShPYmplY3QuZnJvbUVudHJpZXMobmFtZXMubWFwKChuYW1lKSA9PiB7XG4gICAgICBjb25zdCBrZXkgPSBrZXlzLmdldChuYW1lKTtcbiAgICAgIGlmICgha2V5KSB0aHJvdyBuZXcgRXJyb3IoYE5vIGdlbmVyYXRlZCBrZXkgZm9yICR7bmFtZX1gKTtcbiAgICAgIHJldHVybiBbbmFtZSwgW2tleV1dO1xuICAgIH0pKSk7XG4gIH07XG5cbiAgY29uc3QgYmVjb21lID0gKHNlcnZpY2VOYW1lOiBzdHJpbmcpOiB2b2lkID0+IHtcbiAgICBjb25zdCBrZXkgPSBrZXlzLmdldChzZXJ2aWNlTmFtZSk7XG4gICAgaWYgKCFrZXkpIHRocm93IG5ldyBFcnJvcihgTm8gZ2VuZXJhdGVkIGtleSBmb3IgJHtzZXJ2aWNlTmFtZX1gKTtcbiAgICBwcm9jZXNzLmVudi5TRVJWSUNFX05BTUUgPSBzZXJ2aWNlTmFtZTtcbiAgICBwcm9jZXNzLmVudi5TRVJWSUNFX1NJR05JTkdfS0VZX0ZJTEUgPSBrZXkua2V5RmlsZTtcbiAgICBfcmVzZXRTZXJ2aWNlS2V5c0ZvclRlc3RzKCk7XG4gIH07XG5cbiAgcHJvY2Vzcy5lbnYuU0VSVklDRV9LRVlfQlVORExFX0ZJTEUgPSBidW5kbGVGaWxlO1xuICB3cml0ZUJ1bmRsZShzZXJ2aWNlTmFtZXMpO1xuICBiZWNvbWUoc2VydmljZU5hbWVzWzBdKTtcblxuICByZXR1cm4ge1xuICAgIGtleXMsXG4gICAgZGlyLFxuICAgIGJ1bmRsZUZpbGUsXG4gICAgYmVjb21lU2VydmljZTogYmVjb21lLFxuICAgIHNpZ24oc2VydmljZU5hbWUsIGNsYWltcyA9IHt9LCBleHBpcmVzSW5TZWNvbmRzID0gMzAwKSB7XG4gICAgICBiZWNvbWUoc2VydmljZU5hbWUpO1xuICAgICAgcmV0dXJuIHNpZ25TZXJ2aWNlSnd0KHsgLi4udGVzdFNlcnZpY2VJZGVudGl0eUNsYWltcyhzZXJ2aWNlTmFtZSksIC4uLmNsYWltcyB9LCB7IHNlcnZpY2VOYW1lLCBleHBpcmVzSW5TZWNvbmRzIH0pO1xuICAgIH0sXG4gICAgc2lnbkFzKHNpZ25lclNlcnZpY2UsIHN1YmplY3RTZXJ2aWNlLCBjbGFpbXMgPSB7fSkge1xuICAgICAgLy8gRGVsaWJlcmF0ZWx5IGJ5cGFzc2VzIGBzaWduU2VydmljZUp3dGAsIHdoaWNoIHJlZnVzZXMgdG8gc2lnbiBmb3IgYSBuYW1lXG4gICAgICAvLyBpdCBkb2VzIG5vdCBob2xkIOKAlCB0aGUgcG9pbnQgaGVyZSBpcyB0byBwcm9kdWNlIHRoZSBmb3JnZXJ5IGEgdmVyaWZpZXJcbiAgICAgIC8vIG11c3QgY2F0Y2gsIG5vdCB0byByZXByb2R1Y2UgaXQgdGhyb3VnaCB0aGUgbGVnaXRpbWF0ZSBzaWduZXIuXG4gICAgICBjb25zdCBzaWduZXIgPSBrZXlzLmdldChzaWduZXJTZXJ2aWNlKTtcbiAgICAgIGlmICghc2lnbmVyKSB0aHJvdyBuZXcgRXJyb3IoYE5vIGdlbmVyYXRlZCBrZXkgZm9yICR7c2lnbmVyU2VydmljZX1gKTtcbiAgICAgIGNvbnN0IG5vdyA9IE1hdGguZmxvb3IoRGF0ZS5ub3coKSAvIDEwMDApO1xuICAgICAgY29uc3QgYm9keSA9IHtcbiAgICAgICAgLi4udGVzdFNlcnZpY2VJZGVudGl0eUNsYWltcyhzdWJqZWN0U2VydmljZSksXG4gICAgICAgIC4uLmNsYWltcyxcbiAgICAgICAgc3ViOiBgc2VydmljZToke3N1YmplY3RTZXJ2aWNlfWAsXG4gICAgICAgIGlhdDogbm93LFxuICAgICAgICBleHA6IG5vdyArIDMwMCxcbiAgICAgIH07XG4gICAgICBjb25zdCBzaWduaW5nSW5wdXQgPSBgJHtiNjR1SnNvbih7IGFsZzogJ0VTMjU2JywgdHlwOiAnSldUJywga2lkOiBzaWduZXIua2lkIH0pfS4ke2I2NHVKc29uKGJvZHkpfWA7XG4gICAgICBjb25zdCBzaWduYXR1cmUgPSBjcnlwdG8uc2lnbignc2hhMjU2JywgQnVmZmVyLmZyb20oc2lnbmluZ0lucHV0LCAndXRmLTgnKSwgeyBrZXk6IHNpZ25lci5wcml2YXRlS2V5LCBkc2FFbmNvZGluZzogJ2llZWUtcDEzNjMnIH0pO1xuICAgICAgcmV0dXJuIGAke3NpZ25pbmdJbnB1dH0uJHtzaWduYXR1cmUudG9TdHJpbmcoJ2Jhc2U2NHVybCcpfWA7XG4gICAgfSxcbiAgICBwdWJsaXNoOiB3cml0ZUJ1bmRsZSxcbiAgICBwdWJsaXNoS2V5czogd3JpdGVCdW5kbGVGcm9tLFxuICAgIHVuaW5zdGFsbCgpIHtcbiAgICAgIGZvciAoY29uc3QgW25hbWUsIHZhbHVlXSBvZiBPYmplY3QuZW50cmllcyhwcmV2aW91cykpIHtcbiAgICAgICAgaWYgKHZhbHVlID09PSB1bmRlZmluZWQpIGRlbGV0ZSBwcm9jZXNzLmVudltuYW1lXTtcbiAgICAgICAgZWxzZSBwcm9jZXNzLmVudltuYW1lXSA9IHZhbHVlO1xuICAgICAgfVxuICAgICAgX3Jlc2V0U2VydmljZUtleXNGb3JUZXN0cygpO1xuICAgICAgcm1TeW5jKGRpciwgeyByZWN1cnNpdmU6IHRydWUsIGZvcmNlOiB0cnVlIH0pO1xuICAgIH0sXG4gIH07XG59XG4iXX0=
@@ -1,44 +0,0 @@
1
- /**
2
- * Shared tier fixture for the per-package `@pipeline-builder/api-core` test
3
- * mocks (each suite's `test/helpers/mock-api-core.ts`).
4
- *
5
- * WHY A SEPARATE MODULE: every service mocks `@pipeline-builder/api-core` via
6
- * `jest.unstable_mockModule('@pipeline-builder/api-core', …)`. That intercepts the
7
- * *package* specifier only — this file is imported through the DEEP built path
8
- * `@pipeline-builder/api-core/lib/testing/tier-mock.js`, a different specifier the
9
- * mock does NOT intercept, so a mock can pull real, complete tier data without
10
- * self-referencing its own mock. api-core's `exports` map keeps `./lib/*` open
11
- * for exactly this; both sides are ESM so there's no interop wrinkle.
12
- *
13
- * WHAT IT BUYS: the tier NAME LIST is sourced from the real `VALID_TIERS`, so
14
- * adding a tier in `quota-tiers.ts` flows into every mock automatically — no more
15
- * "add a tier → hand-edit N mocks, discover the misses one gate round at a time"
16
- * (which is exactly how the `unlimited` tier broke pipeline-core/api-server/platform).
17
- * `mockQuotaTiers()` builds a COMPLETE `QUOTA_TIERS` (an entry for every tier) from
18
- * caller-supplied per-tier limits, defaulting any unspecified tier to uncapped — so
19
- * a new tier can never leave a `QUOTA_TIERS[tier].limits` undefined at mock load.
20
- */
21
- import { type FeatureFlag } from '../types/feature-flags.js';
22
- import { type QuotaTier, type QuotaTierLimits } from '../types/quota-tiers.js';
23
- /** The real, complete tier-name list — the single source mocks derive from. */
24
- export declare const MOCK_TIER_NAMES: readonly QuotaTier[];
25
- /** Mirrors api-core's `isValidTier`, driven by the shared name list. */
26
- export declare const mockIsValidTier: (t: string) => boolean;
27
- type TierPreset = {
28
- label: string;
29
- limits: QuotaTierLimits;
30
- };
31
- /**
32
- * Build a COMPLETE `QUOTA_TIERS` map (one entry per real tier) for a mock.
33
- * Pass the per-tier limit overrides a suite actually asserts on; every other
34
- * dimension (and every unspecified tier) falls back to uncapped, so the map is
35
- * always exhaustive over `VALID_TIERS` — the property that keeps a newly-added
36
- * tier from crashing mock load.
37
- */
38
- export declare function mockQuotaTiers(limitsByTier?: Partial<Record<QuotaTier, Partial<QuotaTierLimits>>>): Record<QuotaTier, TierPreset>;
39
- /**
40
- * Build a COMPLETE `TIER_FEATURES` map. Defaults every tier to all feature flags
41
- * (the common mock stance); pass overrides for suites that assert a narrower set.
42
- */
43
- export declare function mockTierFeatures(featuresByTier?: Partial<Record<QuotaTier, readonly FeatureFlag[]>>): Record<QuotaTier, readonly FeatureFlag[]>;
44
- export {};
@@ -1,65 +0,0 @@
1
- // Copyright 2026 Pipeline Builder Contributors
2
- // SPDX-License-Identifier: Apache-2.0
3
- /**
4
- * Shared tier fixture for the per-package `@pipeline-builder/api-core` test
5
- * mocks (each suite's `test/helpers/mock-api-core.ts`).
6
- *
7
- * WHY A SEPARATE MODULE: every service mocks `@pipeline-builder/api-core` via
8
- * `jest.unstable_mockModule('@pipeline-builder/api-core', …)`. That intercepts the
9
- * *package* specifier only — this file is imported through the DEEP built path
10
- * `@pipeline-builder/api-core/lib/testing/tier-mock.js`, a different specifier the
11
- * mock does NOT intercept, so a mock can pull real, complete tier data without
12
- * self-referencing its own mock. api-core's `exports` map keeps `./lib/*` open
13
- * for exactly this; both sides are ESM so there's no interop wrinkle.
14
- *
15
- * WHAT IT BUYS: the tier NAME LIST is sourced from the real `VALID_TIERS`, so
16
- * adding a tier in `quota-tiers.ts` flows into every mock automatically — no more
17
- * "add a tier → hand-edit N mocks, discover the misses one gate round at a time"
18
- * (which is exactly how the `unlimited` tier broke pipeline-core/api-server/platform).
19
- * `mockQuotaTiers()` builds a COMPLETE `QUOTA_TIERS` (an entry for every tier) from
20
- * caller-supplied per-tier limits, defaulting any unspecified tier to uncapped — so
21
- * a new tier can never leave a `QUOTA_TIERS[tier].limits` undefined at mock load.
22
- */
23
- import { ALL_FEATURE_FLAGS } from '../types/feature-flags.js';
24
- import { VALID_TIERS } from '../types/quota-tiers.js';
25
- /** The real, complete tier-name list — the single source mocks derive from. */
26
- export const MOCK_TIER_NAMES = VALID_TIERS;
27
- /** Mirrors api-core's `isValidTier`, driven by the shared name list. */
28
- export const mockIsValidTier = (t) => MOCK_TIER_NAMES.includes(t);
29
- /** Every quota dimension uncapped — the default for tiers a caller doesn't specify. */
30
- const uncappedLimits = () => ({
31
- seats: -1,
32
- plugins: -1,
33
- pipelines: -1,
34
- apiCalls: -1,
35
- aiCalls: -1,
36
- storageBytes: -1,
37
- dashboards: -1,
38
- alertRules: -1,
39
- alertDestinations: -1,
40
- idpConfigs: -1,
41
- eventRetentionDays: -1,
42
- doraRetentionDays: -1,
43
- });
44
- const titleCase = (t) => t.charAt(0).toUpperCase() + t.slice(1);
45
- /**
46
- * Build a COMPLETE `QUOTA_TIERS` map (one entry per real tier) for a mock.
47
- * Pass the per-tier limit overrides a suite actually asserts on; every other
48
- * dimension (and every unspecified tier) falls back to uncapped, so the map is
49
- * always exhaustive over `VALID_TIERS` — the property that keeps a newly-added
50
- * tier from crashing mock load.
51
- */
52
- export function mockQuotaTiers(limitsByTier = {}) {
53
- return Object.fromEntries(MOCK_TIER_NAMES.map((t) => [
54
- t,
55
- { label: titleCase(t), limits: { ...uncappedLimits(), ...(limitsByTier[t] ?? {}) } },
56
- ]));
57
- }
58
- /**
59
- * Build a COMPLETE `TIER_FEATURES` map. Defaults every tier to all feature flags
60
- * (the common mock stance); pass overrides for suites that assert a narrower set.
61
- */
62
- export function mockTierFeatures(featuresByTier = {}) {
63
- return Object.fromEntries(MOCK_TIER_NAMES.map((t) => [t, featuresByTier[t] ?? [...ALL_FEATURE_FLAGS]]));
64
- }
65
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGllci1tb2NrLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3Rlc3RpbmcvdGllci1tb2NrLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUFFdEM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FtQkc7QUFFSCxPQUFPLEVBQUUsaUJBQWlCLEVBQW9CLE1BQU0sMkJBQTJCLENBQUM7QUFDaEYsT0FBTyxFQUFFLFdBQVcsRUFBd0MsTUFBTSx5QkFBeUIsQ0FBQztBQUU1RiwrRUFBK0U7QUFDL0UsTUFBTSxDQUFDLE1BQU0sZUFBZSxHQUF5QixXQUFXLENBQUM7QUFFakUsd0VBQXdFO0FBQ3hFLE1BQU0sQ0FBQyxNQUFNLGVBQWUsR0FBRyxDQUFDLENBQVMsRUFBVyxFQUFFLENBQ25ELGVBQXFDLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBRXJELHVGQUF1RjtBQUN2RixNQUFNLGNBQWMsR0FBRyxHQUFvQixFQUFFLENBQUMsQ0FBQztJQUM3QyxLQUFLLEVBQUUsQ0FBQyxDQUFDO0lBQ1QsT0FBTyxFQUFFLENBQUMsQ0FBQztJQUNYLFNBQVMsRUFBRSxDQUFDLENBQUM7SUFDYixRQUFRLEVBQUUsQ0FBQyxDQUFDO0lBQ1osT0FBTyxFQUFFLENBQUMsQ0FBQztJQUNYLFlBQVksRUFBRSxDQUFDLENBQUM7SUFDaEIsVUFBVSxFQUFFLENBQUMsQ0FBQztJQUNkLFVBQVUsRUFBRSxDQUFDLENBQUM7SUFDZCxpQkFBaUIsRUFBRSxDQUFDLENBQUM7SUFDckIsVUFBVSxFQUFFLENBQUMsQ0FBQztJQUNkLGtCQUFrQixFQUFFLENBQUMsQ0FBQztJQUN0QixpQkFBaUIsRUFBRSxDQUFDLENBQUM7Q0FDdEIsQ0FBQyxDQUFDO0FBRUgsTUFBTSxTQUFTLEdBQUcsQ0FBQyxDQUFTLEVBQVUsRUFBRSxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsV0FBVyxFQUFFLEdBQUcsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztBQUloRjs7Ozs7O0dBTUc7QUFDSCxNQUFNLFVBQVUsY0FBYyxDQUM1QixZQUFZLEdBQXlELEVBQUU7SUFFdkUsT0FBTyxNQUFNLENBQUMsV0FBVyxDQUN2QixlQUFlLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQztRQUN6QixDQUFDO1FBQ0QsRUFBRSxLQUFLLEVBQUUsU0FBUyxDQUFDLENBQUMsQ0FBQyxFQUFFLE1BQU0sRUFBRSxFQUFFLEdBQUcsY0FBYyxFQUFFLEVBQUUsR0FBRyxDQUFDLFlBQVksQ0FBQyxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUMsRUFBRSxFQUFFO0tBQ3JGLENBQUMsQ0FDOEIsQ0FBQztBQUNyQyxDQUFDO0FBRUQ7OztHQUdHO0FBQ0gsTUFBTSxVQUFVLGdCQUFnQixDQUM5QixjQUFjLEdBQXVELEVBQUU7SUFFdkUsT0FBTyxNQUFNLENBQUMsV0FBVyxDQUN2QixlQUFlLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxjQUFjLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLGlCQUFpQixDQUFDLENBQUMsQ0FBQyxDQUNoQyxDQUFDO0FBQ2pELENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuLyoqXG4gKiBTaGFyZWQgdGllciBmaXh0dXJlIGZvciB0aGUgcGVyLXBhY2thZ2UgYEBwaXBlbGluZS1idWlsZGVyL2FwaS1jb3JlYCB0ZXN0XG4gKiBtb2NrcyAoZWFjaCBzdWl0ZSdzIGB0ZXN0L2hlbHBlcnMvbW9jay1hcGktY29yZS50c2ApLlxuICpcbiAqIFdIWSBBIFNFUEFSQVRFIE1PRFVMRTogZXZlcnkgc2VydmljZSBtb2NrcyBgQHBpcGVsaW5lLWJ1aWxkZXIvYXBpLWNvcmVgIHZpYVxuICogYGplc3QudW5zdGFibGVfbW9ja01vZHVsZSgnQHBpcGVsaW5lLWJ1aWxkZXIvYXBpLWNvcmUnLCDigKYpYC4gVGhhdCBpbnRlcmNlcHRzIHRoZVxuICogKnBhY2thZ2UqIHNwZWNpZmllciBvbmx5IOKAlCB0aGlzIGZpbGUgaXMgaW1wb3J0ZWQgdGhyb3VnaCB0aGUgREVFUCBidWlsdCBwYXRoXG4gKiBgQHBpcGVsaW5lLWJ1aWxkZXIvYXBpLWNvcmUvbGliL3Rlc3RpbmcvdGllci1tb2NrLmpzYCwgYSBkaWZmZXJlbnQgc3BlY2lmaWVyIHRoZVxuICogbW9jayBkb2VzIE5PVCBpbnRlcmNlcHQsIHNvIGEgbW9jayBjYW4gcHVsbCByZWFsLCBjb21wbGV0ZSB0aWVyIGRhdGEgd2l0aG91dFxuICogc2VsZi1yZWZlcmVuY2luZyBpdHMgb3duIG1vY2suIGFwaS1jb3JlJ3MgYGV4cG9ydHNgIG1hcCBrZWVwcyBgLi9saWIvKmAgb3BlblxuICogZm9yIGV4YWN0bHkgdGhpczsgYm90aCBzaWRlcyBhcmUgRVNNIHNvIHRoZXJlJ3Mgbm8gaW50ZXJvcCB3cmlua2xlLlxuICpcbiAqIFdIQVQgSVQgQlVZUzogdGhlIHRpZXIgTkFNRSBMSVNUIGlzIHNvdXJjZWQgZnJvbSB0aGUgcmVhbCBgVkFMSURfVElFUlNgLCBzb1xuICogYWRkaW5nIGEgdGllciBpbiBgcXVvdGEtdGllcnMudHNgIGZsb3dzIGludG8gZXZlcnkgbW9jayBhdXRvbWF0aWNhbGx5IOKAlCBubyBtb3JlXG4gKiBcImFkZCBhIHRpZXIg4oaSIGhhbmQtZWRpdCBOIG1vY2tzLCBkaXNjb3ZlciB0aGUgbWlzc2VzIG9uZSBnYXRlIHJvdW5kIGF0IGEgdGltZVwiXG4gKiAod2hpY2ggaXMgZXhhY3RseSBob3cgdGhlIGB1bmxpbWl0ZWRgIHRpZXIgYnJva2UgcGlwZWxpbmUtY29yZS9hcGktc2VydmVyL3BsYXRmb3JtKS5cbiAqIGBtb2NrUXVvdGFUaWVycygpYCBidWlsZHMgYSBDT01QTEVURSBgUVVPVEFfVElFUlNgIChhbiBlbnRyeSBmb3IgZXZlcnkgdGllcikgZnJvbVxuICogY2FsbGVyLXN1cHBsaWVkIHBlci10aWVyIGxpbWl0cywgZGVmYXVsdGluZyBhbnkgdW5zcGVjaWZpZWQgdGllciB0byB1bmNhcHBlZCDigJQgc29cbiAqIGEgbmV3IHRpZXIgY2FuIG5ldmVyIGxlYXZlIGEgYFFVT1RBX1RJRVJTW3RpZXJdLmxpbWl0c2AgdW5kZWZpbmVkIGF0IG1vY2sgbG9hZC5cbiAqL1xuXG5pbXBvcnQgeyBBTExfRkVBVFVSRV9GTEFHUywgdHlwZSBGZWF0dXJlRmxhZyB9IGZyb20gJy4uL3R5cGVzL2ZlYXR1cmUtZmxhZ3MuanMnO1xuaW1wb3J0IHsgVkFMSURfVElFUlMsIHR5cGUgUXVvdGFUaWVyLCB0eXBlIFF1b3RhVGllckxpbWl0cyB9IGZyb20gJy4uL3R5cGVzL3F1b3RhLXRpZXJzLmpzJztcblxuLyoqIFRoZSByZWFsLCBjb21wbGV0ZSB0aWVyLW5hbWUgbGlzdCDigJQgdGhlIHNpbmdsZSBzb3VyY2UgbW9ja3MgZGVyaXZlIGZyb20uICovXG5leHBvcnQgY29uc3QgTU9DS19USUVSX05BTUVTOiByZWFkb25seSBRdW90YVRpZXJbXSA9IFZBTElEX1RJRVJTO1xuXG4vKiogTWlycm9ycyBhcGktY29yZSdzIGBpc1ZhbGlkVGllcmAsIGRyaXZlbiBieSB0aGUgc2hhcmVkIG5hbWUgbGlzdC4gKi9cbmV4cG9ydCBjb25zdCBtb2NrSXNWYWxpZFRpZXIgPSAodDogc3RyaW5nKTogYm9vbGVhbiA9PlxuICAoTU9DS19USUVSX05BTUVTIGFzIHJlYWRvbmx5IHN0cmluZ1tdKS5pbmNsdWRlcyh0KTtcblxuLyoqIEV2ZXJ5IHF1b3RhIGRpbWVuc2lvbiB1bmNhcHBlZCDigJQgdGhlIGRlZmF1bHQgZm9yIHRpZXJzIGEgY2FsbGVyIGRvZXNuJ3Qgc3BlY2lmeS4gKi9cbmNvbnN0IHVuY2FwcGVkTGltaXRzID0gKCk6IFF1b3RhVGllckxpbWl0cyA9PiAoe1xuICBzZWF0czogLTEsXG4gIHBsdWdpbnM6IC0xLFxuICBwaXBlbGluZXM6IC0xLFxuICBhcGlDYWxsczogLTEsXG4gIGFpQ2FsbHM6IC0xLFxuICBzdG9yYWdlQnl0ZXM6IC0xLFxuICBkYXNoYm9hcmRzOiAtMSxcbiAgYWxlcnRSdWxlczogLTEsXG4gIGFsZXJ0RGVzdGluYXRpb25zOiAtMSxcbiAgaWRwQ29uZmlnczogLTEsXG4gIGV2ZW50UmV0ZW50aW9uRGF5czogLTEsXG4gIGRvcmFSZXRlbnRpb25EYXlzOiAtMSxcbn0pO1xuXG5jb25zdCB0aXRsZUNhc2UgPSAodDogc3RyaW5nKTogc3RyaW5nID0+IHQuY2hhckF0KDApLnRvVXBwZXJDYXNlKCkgKyB0LnNsaWNlKDEpO1xuXG50eXBlIFRpZXJQcmVzZXQgPSB7IGxhYmVsOiBzdHJpbmc7IGxpbWl0czogUXVvdGFUaWVyTGltaXRzIH07XG5cbi8qKlxuICogQnVpbGQgYSBDT01QTEVURSBgUVVPVEFfVElFUlNgIG1hcCAob25lIGVudHJ5IHBlciByZWFsIHRpZXIpIGZvciBhIG1vY2suXG4gKiBQYXNzIHRoZSBwZXItdGllciBsaW1pdCBvdmVycmlkZXMgYSBzdWl0ZSBhY3R1YWxseSBhc3NlcnRzIG9uOyBldmVyeSBvdGhlclxuICogZGltZW5zaW9uIChhbmQgZXZlcnkgdW5zcGVjaWZpZWQgdGllcikgZmFsbHMgYmFjayB0byB1bmNhcHBlZCwgc28gdGhlIG1hcCBpc1xuICogYWx3YXlzIGV4aGF1c3RpdmUgb3ZlciBgVkFMSURfVElFUlNgIOKAlCB0aGUgcHJvcGVydHkgdGhhdCBrZWVwcyBhIG5ld2x5LWFkZGVkXG4gKiB0aWVyIGZyb20gY3Jhc2hpbmcgbW9jayBsb2FkLlxuICovXG5leHBvcnQgZnVuY3Rpb24gbW9ja1F1b3RhVGllcnMoXG4gIGxpbWl0c0J5VGllcjogUGFydGlhbDxSZWNvcmQ8UXVvdGFUaWVyLCBQYXJ0aWFsPFF1b3RhVGllckxpbWl0cz4+PiA9IHt9LFxuKTogUmVjb3JkPFF1b3RhVGllciwgVGllclByZXNldD4ge1xuICByZXR1cm4gT2JqZWN0LmZyb21FbnRyaWVzKFxuICAgIE1PQ0tfVElFUl9OQU1FUy5tYXAoKHQpID0+IFtcbiAgICAgIHQsXG4gICAgICB7IGxhYmVsOiB0aXRsZUNhc2UodCksIGxpbWl0czogeyAuLi51bmNhcHBlZExpbWl0cygpLCAuLi4obGltaXRzQnlUaWVyW3RdID8/IHt9KSB9IH0sXG4gICAgXSksXG4gICkgYXMgUmVjb3JkPFF1b3RhVGllciwgVGllclByZXNldD47XG59XG5cbi8qKlxuICogQnVpbGQgYSBDT01QTEVURSBgVElFUl9GRUFUVVJFU2AgbWFwLiBEZWZhdWx0cyBldmVyeSB0aWVyIHRvIGFsbCBmZWF0dXJlIGZsYWdzXG4gKiAodGhlIGNvbW1vbiBtb2NrIHN0YW5jZSk7IHBhc3Mgb3ZlcnJpZGVzIGZvciBzdWl0ZXMgdGhhdCBhc3NlcnQgYSBuYXJyb3dlciBzZXQuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBtb2NrVGllckZlYXR1cmVzKFxuICBmZWF0dXJlc0J5VGllcjogUGFydGlhbDxSZWNvcmQ8UXVvdGFUaWVyLCByZWFkb25seSBGZWF0dXJlRmxhZ1tdPj4gPSB7fSxcbik6IFJlY29yZDxRdW90YVRpZXIsIHJlYWRvbmx5IEZlYXR1cmVGbGFnW10+IHtcbiAgcmV0dXJuIE9iamVjdC5mcm9tRW50cmllcyhcbiAgICBNT0NLX1RJRVJfTkFNRVMubWFwKCh0KSA9PiBbdCwgZmVhdHVyZXNCeVRpZXJbdF0gPz8gWy4uLkFMTF9GRUFUVVJFX0ZMQUdTXV0pLFxuICApIGFzIFJlY29yZDxRdW90YVRpZXIsIHJlYWRvbmx5IEZlYXR1cmVGbGFnW10+O1xufVxuIl19