@pipeline-builder/api-core 4.1.13 → 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.
- package/README.md +3 -2
- package/lib/constants/index.d.ts +0 -1
- package/lib/constants/index.js +1 -2
- package/lib/errors/app-errors.d.ts +8 -3
- package/lib/errors/app-errors.js +10 -5
- package/lib/helpers/access-helpers.d.ts +4 -6
- package/lib/helpers/access-helpers.js +6 -8
- package/lib/helpers/index.d.ts +3 -3
- package/lib/helpers/index.js +4 -4
- package/lib/helpers/org-hierarchy-http.d.ts +11 -10
- package/lib/helpers/org-hierarchy-http.js +24 -35
- package/lib/helpers/org-hierarchy.d.ts +37 -3
- package/lib/helpers/org-hierarchy.js +45 -4
- package/lib/helpers/restore-helpers.d.ts +44 -24
- package/lib/helpers/restore-helpers.js +41 -52
- package/lib/helpers/sse-helpers.d.ts +29 -7
- package/lib/helpers/sse-helpers.js +36 -9
- package/lib/index.d.ts +16 -14
- package/lib/index.js +17 -15
- package/lib/middleware/assurance.d.ts +116 -0
- package/lib/middleware/assurance.js +176 -0
- package/lib/middleware/auth.d.ts +5 -427
- package/lib/middleware/auth.js +36 -789
- package/lib/middleware/ecosystem-guard.d.ts +47 -0
- package/lib/middleware/ecosystem-guard.js +60 -0
- package/lib/middleware/index.d.ts +11 -4
- package/lib/middleware/index.js +12 -5
- package/lib/middleware/jwt-verify.d.ts +52 -0
- package/lib/middleware/jwt-verify.js +105 -0
- package/lib/middleware/permission-gates.d.ts +133 -0
- package/lib/middleware/permission-gates.js +260 -0
- package/lib/middleware/revocation.d.ts +96 -0
- package/lib/middleware/revocation.js +162 -0
- package/lib/middleware/route-table.d.ts +11 -0
- package/lib/middleware/route-table.js +4 -1
- package/lib/middleware/service-tokens.d.ts +83 -0
- package/lib/middleware/service-tokens.js +158 -0
- package/lib/middleware/step-up.d.ts +2 -2
- package/lib/middleware/step-up.js +8 -7
- package/lib/middleware/system-org.d.ts +20 -0
- package/lib/middleware/system-org.js +25 -0
- package/lib/openapi/routes/plugin-routes.js +133 -10
- package/lib/openapi/schema-registry.js +2 -5
- package/lib/routes/index.d.ts +1 -1
- package/lib/routes/index.js +2 -2
- package/lib/services/api-key-exchange.js +7 -5
- package/lib/services/audit-spool.d.ts +34 -10
- package/lib/services/audit-spool.js +75 -34
- package/lib/services/cache-service.d.ts +7 -0
- package/lib/services/cache-service.js +1 -1
- package/lib/services/circuit-breaker.d.ts +14 -1
- package/lib/services/circuit-breaker.js +36 -10
- package/lib/services/compliance-client.d.ts +10 -2
- package/lib/services/compliance-client.js +17 -10
- package/lib/services/compliance-event-subscriber.js +6 -6
- package/lib/services/durable-event-bus.js +8 -11
- package/lib/services/ecosystem-notify-client.d.ts +32 -0
- package/lib/services/ecosystem-notify-client.js +71 -0
- package/lib/services/env-redis.d.ts +4 -1
- package/lib/services/env-redis.js +32 -3
- package/lib/services/http-client.d.ts +19 -0
- package/lib/services/http-client.js +98 -19
- package/lib/services/index.d.ts +25 -24
- package/lib/services/index.js +25 -22
- package/lib/services/jwks-cache.js +5 -4
- package/lib/services/leader-lock.d.ts +22 -5
- package/lib/services/leader-lock.js +61 -6
- package/lib/services/quota.d.ts +9 -4
- package/lib/services/quota.js +34 -40
- package/lib/services/remote-audit-client.d.ts +28 -32
- package/lib/services/remote-audit-client.js +166 -49
- package/lib/services/retry-forever.js +5 -11
- package/lib/services/retry-strategy.d.ts +1 -1
- package/lib/services/retry-strategy.js +5 -4
- package/lib/services/scheduler.d.ts +8 -3
- package/lib/services/scheduler.js +17 -5
- package/lib/services/service-boot.d.ts +14 -13
- package/lib/services/service-boot.js +18 -15
- package/lib/services/service-keys.d.ts +1 -1
- package/lib/services/service-keys.js +12 -18
- package/lib/services/sse-ticket-store.d.ts +9 -1
- package/lib/services/sse-ticket-store.js +24 -9
- package/lib/services/system-notification.d.ts +27 -0
- package/lib/services/system-notification.js +50 -0
- package/lib/services/token-revocation.d.ts +50 -1
- package/lib/services/token-revocation.js +103 -3
- package/lib/types/audit-events.d.ts +6 -6
- package/lib/types/audit-events.js +1 -1
- package/lib/types/common.d.ts +32 -13
- package/lib/types/common.js +2 -1
- package/lib/types/ecosystem-notifications.d.ts +142 -0
- package/lib/types/ecosystem-notifications.js +210 -0
- package/lib/types/ecosystem.d.ts +77 -0
- package/lib/types/ecosystem.js +119 -0
- package/lib/types/error-codes.d.ts +81 -0
- package/lib/types/error-codes.js +108 -1
- package/lib/types/feature-flags.d.ts +21 -1
- package/lib/types/feature-flags.js +36 -2
- package/lib/types/index.d.ts +14 -9
- package/lib/types/index.js +15 -10
- package/lib/types/permissions.d.ts +46 -8
- package/lib/types/permissions.js +75 -12
- package/lib/types/plugin-catalog.d.ts +35 -0
- package/lib/types/plugin-catalog.js +39 -0
- package/lib/types/plugin-health.d.ts +102 -0
- package/lib/types/plugin-health.js +184 -0
- package/lib/types/quota-tiers.d.ts +15 -0
- package/lib/types/quota-tiers.js +23 -3
- package/lib/types/wire-vocabulary.d.ts +102 -0
- package/lib/types/wire-vocabulary.js +94 -0
- package/lib/utils/alias-resolver.js +2 -2
- package/lib/utils/audit.d.ts +5 -2
- package/lib/utils/audit.js +6 -3
- package/lib/utils/compliance-attributes.js +3 -4
- package/lib/utils/concurrency.d.ts +7 -0
- package/lib/utils/concurrency.js +13 -1
- package/lib/utils/env.d.ts +3 -4
- package/lib/utils/env.js +4 -5
- package/lib/utils/identity.d.ts +13 -4
- package/lib/utils/identity.js +18 -10
- package/lib/utils/index.d.ts +13 -10
- package/lib/utils/index.js +13 -10
- package/lib/utils/jwk.d.ts +4 -0
- package/lib/utils/jwk.js +13 -1
- package/lib/utils/logger.d.ts +1 -1
- package/lib/utils/logger.js +1 -1
- package/lib/utils/object.d.ts +11 -0
- package/lib/utils/object.js +24 -1
- package/lib/utils/params.d.ts +8 -22
- package/lib/utils/params.js +15 -43
- package/lib/utils/plugin-compliance.d.ts +98 -0
- package/lib/utils/plugin-compliance.js +78 -0
- package/lib/utils/proof-of-work.d.ts +69 -0
- package/lib/utils/proof-of-work.js +139 -0
- package/lib/utils/response.d.ts +16 -6
- package/lib/utils/response.js +18 -15
- package/lib/utils/retention.d.ts +17 -0
- package/lib/utils/retention.js +30 -0
- package/lib/utils/secret-encryption.d.ts +7 -7
- package/lib/utils/secret-encryption.js +9 -10
- package/lib/utils/secret-rotation.js +2 -2
- package/lib/utils/sensitive-patterns.js +4 -5
- package/lib/utils/service-registry.d.ts +15 -0
- package/lib/utils/service-registry.js +24 -0
- package/lib/utils/ssrf.d.ts +11 -2
- package/lib/utils/ssrf.js +112 -33
- package/lib/validation/ai-schemas.js +3 -2
- package/lib/validation/common-schemas.d.ts +0 -9
- package/lib/validation/common-schemas.js +1 -7
- package/lib/validation/dockerfile-static.d.ts +26 -0
- package/lib/validation/dockerfile-static.js +210 -0
- package/lib/validation/index.d.ts +13 -4
- package/lib/validation/index.js +14 -5
- package/lib/validation/message-schemas.js +6 -4
- package/lib/validation/pipeline-schemas.d.ts +8 -2
- package/lib/validation/pipeline-schemas.js +30 -4
- package/lib/validation/plugin-base-images.d.ts +20 -0
- package/lib/validation/plugin-base-images.js +17 -0
- package/lib/validation/plugin-catalog-detect.d.ts +86 -0
- package/lib/validation/plugin-catalog-detect.js +237 -0
- package/lib/validation/plugin-catalog-metadata.d.ts +141 -0
- package/lib/validation/plugin-catalog-metadata.js +162 -0
- package/lib/validation/plugin-heuristics.d.ts +41 -0
- package/lib/validation/plugin-heuristics.js +211 -0
- package/lib/validation/plugin-lint.d.ts +18 -0
- package/lib/validation/plugin-lint.js +136 -0
- package/lib/validation/plugin-name-confusable.d.ts +15 -0
- package/lib/validation/plugin-name-confusable.js +81 -0
- package/lib/validation/plugin-schemas.d.ts +40 -26
- package/lib/validation/plugin-schemas.js +26 -23
- package/lib/validation/plugin-spec-schema.d.ts +112 -0
- package/lib/validation/plugin-spec-schema.js +163 -0
- package/lib/validation/plugin-template-contract.d.ts +64 -0
- package/lib/validation/plugin-template-contract.js +113 -0
- package/package.json +69 -4
- package/lib/testing/mock-api-core.d.ts +0 -150
- package/lib/testing/mock-api-core.js +0 -246
- package/lib/testing/mock-drizzle.d.ts +0 -6
- package/lib/testing/mock-drizzle.js +0 -57
- package/lib/testing/route-coverage.d.ts +0 -64
- package/lib/testing/route-coverage.js +0 -183
- package/lib/testing/service-tokens.d.ts +0 -66
- package/lib/testing/service-tokens.js +0 -137
- package/lib/testing/tier-mock.d.ts +0 -44
- package/lib/testing/tier-mock.js +0 -65
- package/lib/testing/user-tokens.d.ts +0 -75
- 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 {};
|
package/lib/testing/tier-mock.js
DELETED
|
@@ -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
|