@pipeline-builder/api-core 4.1.13 → 4.1.15
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,150 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared `@pipeline-builder/api-core` mock factory for ESM suites.
|
|
3
|
-
*
|
|
4
|
-
* WHY: every project kept its own `test/helpers/mock-api-core.ts` — twelve forks
|
|
5
|
-
* of the same ~150-line file, ranging from 23 to 61 overridden keys against an
|
|
6
|
-
* api-core barrel exporting ~357 symbols. Each fork re-declared the same logger
|
|
7
|
-
* stub, the same `ErrorCode` proxy, the same error classes and the same
|
|
8
|
-
* service-gate re-implementations, and each drifted independently.
|
|
9
|
-
*
|
|
10
|
-
* THE FIX: the shared parts live here, once. A project's helper keeps ONLY the
|
|
11
|
-
* defaults that are genuinely project-specific (its tier fixtures, its compliance
|
|
12
|
-
* client, its response helpers) and layers them on top:
|
|
13
|
-
*
|
|
14
|
-
* const actualApiCore = jest.requireActual('@pipeline-builder/api-core') as Record<string, unknown>;
|
|
15
|
-
* export function apiCoreMock(overrides: Record<string, unknown> = {}) {
|
|
16
|
-
* return baseApiCoreMock(actualApiCore, { ...projectDefaults, ...overrides });
|
|
17
|
-
* }
|
|
18
|
-
*
|
|
19
|
-
* Both this module and the per-project helpers start from `requireActual`, so an
|
|
20
|
-
* export ADDED to api-core is inherited automatically and can never again break a
|
|
21
|
-
* suite with "does not provide an export named X".
|
|
22
|
-
*
|
|
23
|
-
* WHY IT LIVES IN api-core: the deep path
|
|
24
|
-
* `@pipeline-builder/api-core/lib/testing/mock-api-core.js` is a different
|
|
25
|
-
* specifier from the `@pipeline-builder/api-core` barrel, so a suite's module
|
|
26
|
-
* mock does NOT intercept it — the factory can therefore read the real module
|
|
27
|
-
* without recursing into its own mock. Same trick as `tier-mock.ts`.
|
|
28
|
-
*/
|
|
29
|
-
import { jest } from '@jest/globals';
|
|
30
|
-
/** Minimal Express-ish shapes the gate/response stubs below touch. */
|
|
31
|
-
type MockRes = {
|
|
32
|
-
status: (n: number) => {
|
|
33
|
-
json: (b: unknown) => unknown;
|
|
34
|
-
};
|
|
35
|
-
};
|
|
36
|
-
type Next = () => void;
|
|
37
|
-
/** A bare `jest.fn()`, named so the emitted `.d.ts` stays portable (TS2883). */
|
|
38
|
-
type MockFn = ReturnType<typeof jest.fn>;
|
|
39
|
-
/** The winston double every suite asserts on: four spies, nothing else. */
|
|
40
|
-
export interface MockLogger {
|
|
41
|
-
info: MockFn;
|
|
42
|
-
warn: MockFn;
|
|
43
|
-
error: MockFn;
|
|
44
|
-
debug: MockFn;
|
|
45
|
-
}
|
|
46
|
-
/** The 4-method winston stub every suite repeats; a fresh set of spies per call. */
|
|
47
|
-
export declare const loggerMock: () => MockLogger;
|
|
48
|
-
/** Mirrors api-core: `ErrorCode.ANY_CODE` resolves to the string `'ANY_CODE'`. */
|
|
49
|
-
export declare const mockErrorCode: Record<string, string>;
|
|
50
|
-
/** No-op guard: the default mock covers route wiring, not the permission gate. */
|
|
51
|
-
export declare const passThroughMiddleware: (_req: unknown, _res: unknown, next: Next) => void;
|
|
52
|
-
/** Mirrors api-core's `AppError` base (typed HTTP error: statusCode + code). */
|
|
53
|
-
export declare class MockAppError extends Error {
|
|
54
|
-
readonly statusCode: number;
|
|
55
|
-
readonly code: string;
|
|
56
|
-
constructor(statusCode: number, code: string, message?: string);
|
|
57
|
-
}
|
|
58
|
-
/** Mirrors api-core's NotFoundError (404 / NOT_FOUND). */
|
|
59
|
-
export declare class MockNotFoundError extends MockAppError {
|
|
60
|
-
constructor(message?: string);
|
|
61
|
-
}
|
|
62
|
-
/** Mirrors api-core's ConflictError (409 / CONFLICT). */
|
|
63
|
-
export declare class MockConflictError extends MockAppError {
|
|
64
|
-
constructor(message?: string);
|
|
65
|
-
}
|
|
66
|
-
/** Mirrors api-core's ForbiddenError (403 / INSUFFICIENT_PERMISSIONS). */
|
|
67
|
-
export declare class MockForbiddenError extends MockAppError {
|
|
68
|
-
constructor(message?: string);
|
|
69
|
-
}
|
|
70
|
-
/** Mirrors api-core's ValidationError (400 / VALIDATION_ERROR). */
|
|
71
|
-
export declare class MockValidationError extends MockAppError {
|
|
72
|
-
constructor(message?: string);
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Capability-aware stand-in for api-core's `requirePermission` /
|
|
76
|
-
* `requireAllPermissions` gate factories. Mirrors the real gate's decision (minus
|
|
77
|
-
* the denial-audit side effect): superadmin passes, otherwise the caller must
|
|
78
|
-
* hold the permission(s); a miss is a 403 with the real message shape.
|
|
79
|
-
*
|
|
80
|
-
* `onAnonymous` picks what happens when there is no `req.user` at all: `'pass'`
|
|
81
|
-
* (suites with no auth layer mounted) or `'401'` (suites that drive the gate).
|
|
82
|
-
* `shape` picks the denial envelope the project's suites assert on.
|
|
83
|
-
*/
|
|
84
|
-
export declare function mockPermissionGate(options?: {
|
|
85
|
-
mode?: 'some' | 'every';
|
|
86
|
-
allowService?: boolean;
|
|
87
|
-
onAnonymous?: 'pass' | '401';
|
|
88
|
-
shape?: 'error' | 'message';
|
|
89
|
-
}): (...permissions: string[]) => (req: {
|
|
90
|
-
user?: {
|
|
91
|
-
isSuperAdmin?: boolean;
|
|
92
|
-
permissions?: string[];
|
|
93
|
-
sub?: string;
|
|
94
|
-
};
|
|
95
|
-
}, res: MockRes, next: Next) => unknown;
|
|
96
|
-
/**
|
|
97
|
-
* The defaults shared by every service / package mock: the logger stub, the
|
|
98
|
-
* pagination constants, the inert scheduler / leader-lock / redis-audit wiring a
|
|
99
|
-
* boot module links at load, and the error + cache shapes. A project layers its
|
|
100
|
-
* own defaults on top by passing them in `overrides`.
|
|
101
|
-
*/
|
|
102
|
-
export declare function serviceApiCoreDefaults(): Record<string, unknown>;
|
|
103
|
-
/**
|
|
104
|
-
* The audit / security wiring a service's boot module (`src/index.ts`,
|
|
105
|
-
* `src/services/audit.ts`) links at load time. Inert by default: nothing asserts
|
|
106
|
-
* on the registration, and pulling the real graph in would drag Redis + HTTP
|
|
107
|
-
* clients into every route suite.
|
|
108
|
-
*/
|
|
109
|
-
export declare function serviceAuditDefaults(): Record<string, unknown>;
|
|
110
|
-
/**
|
|
111
|
-
* Build an api-core mock namespace: REAL api-core, then the shared service
|
|
112
|
-
* defaults, then `overrides` (a project's own defaults merged with the suite's).
|
|
113
|
-
*
|
|
114
|
-
* `actualApiCore` is supplied by the CALLER (`jest.requireActual(...)` at the top
|
|
115
|
-
* of the project's `test/helpers/mock-api-core.ts`) rather than resolved here.
|
|
116
|
-
* That is deliberate: `requireActual` on an ESM barrel only succeeds at a moment
|
|
117
|
-
* when nothing else is mid-`import()` of it, and the project helper — a static
|
|
118
|
-
* import of the test file, evaluated before any `await import(SUT)` — is the one
|
|
119
|
-
* point where that always holds. Resolving it from inside this module (which
|
|
120
|
-
* lives in the api-core package itself) races that import in some suites.
|
|
121
|
-
*/
|
|
122
|
-
export declare function baseApiCoreMock(actualApiCore: Record<string, unknown>, overrides?: Record<string, unknown>): Record<string, unknown>;
|
|
123
|
-
/**
|
|
124
|
-
* Build an api-core mock namespace with NO service defaults — REAL api-core plus
|
|
125
|
-
* the logger stub and `ErrorCode` proxy only. Platform uses this: it deliberately
|
|
126
|
-
* keeps api-core's real pagination/report constants and supplies its own
|
|
127
|
-
* (large) identity/tier/permission default set.
|
|
128
|
-
*/
|
|
129
|
-
export declare function primitiveApiCoreMock(actualApiCore: Record<string, unknown>, overrides?: Record<string, unknown>): Record<string, unknown>;
|
|
130
|
-
/**
|
|
131
|
-
* Mirror api-core's `requireInternalService({ callers })`: refuse any user token,
|
|
132
|
-
* then refuse a service whose name is not in the route's caller list. Resolved
|
|
133
|
-
* from the MERGED mock (rather than inherited from `requireActual`) so it uses
|
|
134
|
-
* the suite's `isServicePrincipal` / `serviceNameOf` / `sendError`, which the real
|
|
135
|
-
* one closes over and would write past.
|
|
136
|
-
*/
|
|
137
|
-
export declare function withInternalServiceGate(mock: Record<string, unknown>, overrides: Record<string, unknown>): Record<string, unknown>;
|
|
138
|
-
/**
|
|
139
|
-
* Mirror api-core's `requireServicePrincipal`: reject a non-service caller with a
|
|
140
|
-
* 403 INSUFFICIENT_PERMISSIONS (an authorization refusal, not a 400), resolving
|
|
141
|
-
* `isServicePrincipal` / `sendError` from the merged mock.
|
|
142
|
-
*/
|
|
143
|
-
export declare function withServicePrincipalGate(mock: Record<string, unknown>, overrides: Record<string, unknown>): Record<string, unknown>;
|
|
144
|
-
/**
|
|
145
|
-
* `sendBadRequest` delegating to the (possibly-overridden) `sendError`, mirroring
|
|
146
|
-
* the real implementation — so a suite spying on `sendError` also observes the
|
|
147
|
-
* 400s routed through `sendBadRequest`.
|
|
148
|
-
*/
|
|
149
|
-
export declare function withDelegatingSendBadRequest(mock: Record<string, unknown>, overrides: Record<string, unknown>): Record<string, unknown>;
|
|
150
|
-
export {};
|
|
@@ -1,246 +0,0 @@
|
|
|
1
|
-
// Copyright 2026 Pipeline Builder Contributors
|
|
2
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
-
/**
|
|
4
|
-
* Shared `@pipeline-builder/api-core` mock factory for ESM suites.
|
|
5
|
-
*
|
|
6
|
-
* WHY: every project kept its own `test/helpers/mock-api-core.ts` — twelve forks
|
|
7
|
-
* of the same ~150-line file, ranging from 23 to 61 overridden keys against an
|
|
8
|
-
* api-core barrel exporting ~357 symbols. Each fork re-declared the same logger
|
|
9
|
-
* stub, the same `ErrorCode` proxy, the same error classes and the same
|
|
10
|
-
* service-gate re-implementations, and each drifted independently.
|
|
11
|
-
*
|
|
12
|
-
* THE FIX: the shared parts live here, once. A project's helper keeps ONLY the
|
|
13
|
-
* defaults that are genuinely project-specific (its tier fixtures, its compliance
|
|
14
|
-
* client, its response helpers) and layers them on top:
|
|
15
|
-
*
|
|
16
|
-
* const actualApiCore = jest.requireActual('@pipeline-builder/api-core') as Record<string, unknown>;
|
|
17
|
-
* export function apiCoreMock(overrides: Record<string, unknown> = {}) {
|
|
18
|
-
* return baseApiCoreMock(actualApiCore, { ...projectDefaults, ...overrides });
|
|
19
|
-
* }
|
|
20
|
-
*
|
|
21
|
-
* Both this module and the per-project helpers start from `requireActual`, so an
|
|
22
|
-
* export ADDED to api-core is inherited automatically and can never again break a
|
|
23
|
-
* suite with "does not provide an export named X".
|
|
24
|
-
*
|
|
25
|
-
* WHY IT LIVES IN api-core: the deep path
|
|
26
|
-
* `@pipeline-builder/api-core/lib/testing/mock-api-core.js` is a different
|
|
27
|
-
* specifier from the `@pipeline-builder/api-core` barrel, so a suite's module
|
|
28
|
-
* mock does NOT intercept it — the factory can therefore read the real module
|
|
29
|
-
* without recursing into its own mock. Same trick as `tier-mock.ts`.
|
|
30
|
-
*/
|
|
31
|
-
import { jest } from '@jest/globals';
|
|
32
|
-
/** The 4-method winston stub every suite repeats; a fresh set of spies per call. */
|
|
33
|
-
export const loggerMock = () => ({
|
|
34
|
-
info: jest.fn(),
|
|
35
|
-
warn: jest.fn(),
|
|
36
|
-
error: jest.fn(),
|
|
37
|
-
debug: jest.fn(),
|
|
38
|
-
});
|
|
39
|
-
/** Mirrors api-core: `ErrorCode.ANY_CODE` resolves to the string `'ANY_CODE'`. */
|
|
40
|
-
export const mockErrorCode = new Proxy({}, { get: (_t, key) => key });
|
|
41
|
-
/** No-op guard: the default mock covers route wiring, not the permission gate. */
|
|
42
|
-
export const passThroughMiddleware = (_req, _res, next) => next();
|
|
43
|
-
/** Mirrors api-core's `AppError` base (typed HTTP error: statusCode + code). */
|
|
44
|
-
export class MockAppError extends Error {
|
|
45
|
-
statusCode;
|
|
46
|
-
code;
|
|
47
|
-
constructor(statusCode, code, message) {
|
|
48
|
-
super(message);
|
|
49
|
-
this.statusCode = statusCode;
|
|
50
|
-
this.code = code;
|
|
51
|
-
this.name = 'AppError';
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
/** Mirrors api-core's NotFoundError (404 / NOT_FOUND). */
|
|
55
|
-
export class MockNotFoundError extends MockAppError {
|
|
56
|
-
constructor(message) {
|
|
57
|
-
super(404, 'NOT_FOUND', message);
|
|
58
|
-
this.name = 'NotFoundError';
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
/** Mirrors api-core's ConflictError (409 / CONFLICT). */
|
|
62
|
-
export class MockConflictError extends MockAppError {
|
|
63
|
-
constructor(message) {
|
|
64
|
-
super(409, 'CONFLICT', message);
|
|
65
|
-
this.name = 'ConflictError';
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
/** Mirrors api-core's ForbiddenError (403 / INSUFFICIENT_PERMISSIONS). */
|
|
69
|
-
export class MockForbiddenError extends MockAppError {
|
|
70
|
-
constructor(message) {
|
|
71
|
-
super(403, 'INSUFFICIENT_PERMISSIONS', message);
|
|
72
|
-
this.name = 'ForbiddenError';
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
/** Mirrors api-core's ValidationError (400 / VALIDATION_ERROR). */
|
|
76
|
-
export class MockValidationError extends MockAppError {
|
|
77
|
-
constructor(message) {
|
|
78
|
-
super(400, 'VALIDATION_ERROR', message);
|
|
79
|
-
this.name = 'ValidationError';
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Capability-aware stand-in for api-core's `requirePermission` /
|
|
84
|
-
* `requireAllPermissions` gate factories. Mirrors the real gate's decision (minus
|
|
85
|
-
* the denial-audit side effect): superadmin passes, otherwise the caller must
|
|
86
|
-
* hold the permission(s); a miss is a 403 with the real message shape.
|
|
87
|
-
*
|
|
88
|
-
* `onAnonymous` picks what happens when there is no `req.user` at all: `'pass'`
|
|
89
|
-
* (suites with no auth layer mounted) or `'401'` (suites that drive the gate).
|
|
90
|
-
* `shape` picks the denial envelope the project's suites assert on.
|
|
91
|
-
*/
|
|
92
|
-
export function mockPermissionGate(options = {}) {
|
|
93
|
-
const { mode = 'some', allowService = false, onAnonymous = 'pass', shape = 'message' } = options;
|
|
94
|
-
const joiner = mode === 'some' ? ' or ' : ' and ';
|
|
95
|
-
const deny = (msg) => (shape === 'error' ? { error: msg } : { success: false, message: msg });
|
|
96
|
-
return (...permissions) => {
|
|
97
|
-
const gate = (req, res, next) => {
|
|
98
|
-
const user = req?.user;
|
|
99
|
-
if (!user)
|
|
100
|
-
return onAnonymous === 'pass' ? next() : res.status(401).json({ error: 'Authentication required' });
|
|
101
|
-
if (allowService && typeof user.sub === 'string' && user.sub.startsWith('service:'))
|
|
102
|
-
return next();
|
|
103
|
-
if (user.isSuperAdmin === true)
|
|
104
|
-
return next();
|
|
105
|
-
const held = Array.isArray(user.permissions) ? user.permissions : [];
|
|
106
|
-
const ok = mode === 'some' ? permissions.some((p) => held.includes(p)) : permissions.every((p) => held.includes(p));
|
|
107
|
-
if (ok)
|
|
108
|
-
return next();
|
|
109
|
-
return res.status(403).json(deny(`Missing required permission: ${permissions.join(joiner)}`));
|
|
110
|
-
};
|
|
111
|
-
// Tags let a wiring suite locate the gate layer in a router stack and assert
|
|
112
|
-
// which flavour was mounted.
|
|
113
|
-
gate.__permission = permissions.join(joiner);
|
|
114
|
-
gate.__allowService = allowService;
|
|
115
|
-
return gate;
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* The defaults shared by every service / package mock: the logger stub, the
|
|
120
|
-
* pagination constants, the inert scheduler / leader-lock / redis-audit wiring a
|
|
121
|
-
* boot module links at load, and the error + cache shapes. A project layers its
|
|
122
|
-
* own defaults on top by passing them in `overrides`.
|
|
123
|
-
*/
|
|
124
|
-
export function serviceApiCoreDefaults() {
|
|
125
|
-
return {
|
|
126
|
-
createLogger: loggerMock,
|
|
127
|
-
MAX_PAGE_LIMIT: 1000,
|
|
128
|
-
DEFAULT_PAGE_LIMIT: 100,
|
|
129
|
-
closeLeaderLock: async () => undefined,
|
|
130
|
-
loadAndRestore: async () => null,
|
|
131
|
-
REPORT_INTERVALS: ['day', 'week', 'month'],
|
|
132
|
-
scrubAwsIdentifiersFromString: (s) => s,
|
|
133
|
-
scrubAwsIdentifiers: (v) => v,
|
|
134
|
-
createScheduler: () => ({ start: () => undefined, stop: () => undefined }),
|
|
135
|
-
createEnvRedisLock: () => null,
|
|
136
|
-
requireStepUp: (_req, _res, next) => next(),
|
|
137
|
-
SYSTEM_ORG_ID: '000000000000000000000001',
|
|
138
|
-
ComputeType: { SMALL: 'SMALL', MEDIUM: 'MEDIUM', LARGE: 'LARGE', X2_LARGE: 'X2_LARGE' },
|
|
139
|
-
PluginType: { CODE_BUILD_STEP: 'CodeBuildStep', SHELL_STEP: 'ShellStep', MANUAL_APPROVAL_STEP: 'ManualApprovalStep' },
|
|
140
|
-
ErrorCode: mockErrorCode,
|
|
141
|
-
errorMessage: (e) => (e instanceof Error ? e.message : String(e)),
|
|
142
|
-
NotFoundError: MockNotFoundError,
|
|
143
|
-
createCacheService: () => ({
|
|
144
|
-
getOrSet: (_key, factory) => factory(),
|
|
145
|
-
invalidatePattern: () => Promise.resolve(0),
|
|
146
|
-
}),
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
/**
|
|
150
|
-
* The audit / security wiring a service's boot module (`src/index.ts`,
|
|
151
|
-
* `src/services/audit.ts`) links at load time. Inert by default: nothing asserts
|
|
152
|
-
* on the registration, and pulling the real graph in would drag Redis + HTTP
|
|
153
|
-
* clients into every route suite.
|
|
154
|
-
*/
|
|
155
|
-
export function serviceAuditDefaults() {
|
|
156
|
-
return {
|
|
157
|
-
createRemoteAuditClient: () => ({ record: jest.fn() }),
|
|
158
|
-
createEnvRedisAuditSpool: () => null,
|
|
159
|
-
createServiceAuditClient: () => ({ emit: jest.fn(), client: { record: jest.fn() } }),
|
|
160
|
-
createRemoteAuditAccessor: () => ({ getAuditClient: () => ({ record: jest.fn() }), emit: jest.fn() }),
|
|
161
|
-
setAuthzDenialAuditor: () => { },
|
|
162
|
-
wireAuthzDenialAuditor: () => { },
|
|
163
|
-
wireServiceSecurity: () => { },
|
|
164
|
-
setTokenRevocationStore: () => { },
|
|
165
|
-
createRedisTokenRevocationStore: () => ({ getCurrentVersion: async () => null }),
|
|
166
|
-
createEnvRedisTokenRevocationStore: () => ({ getCurrentVersion: async () => null }),
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
/**
|
|
170
|
-
* Build an api-core mock namespace: REAL api-core, then the shared service
|
|
171
|
-
* defaults, then `overrides` (a project's own defaults merged with the suite's).
|
|
172
|
-
*
|
|
173
|
-
* `actualApiCore` is supplied by the CALLER (`jest.requireActual(...)` at the top
|
|
174
|
-
* of the project's `test/helpers/mock-api-core.ts`) rather than resolved here.
|
|
175
|
-
* That is deliberate: `requireActual` on an ESM barrel only succeeds at a moment
|
|
176
|
-
* when nothing else is mid-`import()` of it, and the project helper — a static
|
|
177
|
-
* import of the test file, evaluated before any `await import(SUT)` — is the one
|
|
178
|
-
* point where that always holds. Resolving it from inside this module (which
|
|
179
|
-
* lives in the api-core package itself) races that import in some suites.
|
|
180
|
-
*/
|
|
181
|
-
export function baseApiCoreMock(actualApiCore, overrides = {}) {
|
|
182
|
-
return { ...actualApiCore, ...serviceApiCoreDefaults(), ...overrides };
|
|
183
|
-
}
|
|
184
|
-
/**
|
|
185
|
-
* Build an api-core mock namespace with NO service defaults — REAL api-core plus
|
|
186
|
-
* the logger stub and `ErrorCode` proxy only. Platform uses this: it deliberately
|
|
187
|
-
* keeps api-core's real pagination/report constants and supplies its own
|
|
188
|
-
* (large) identity/tier/permission default set.
|
|
189
|
-
*/
|
|
190
|
-
export function primitiveApiCoreMock(actualApiCore, overrides = {}) {
|
|
191
|
-
return { ...actualApiCore, createLogger: loggerMock, ErrorCode: mockErrorCode, ...overrides };
|
|
192
|
-
}
|
|
193
|
-
/**
|
|
194
|
-
* Mirror api-core's `requireInternalService({ callers })`: refuse any user token,
|
|
195
|
-
* then refuse a service whose name is not in the route's caller list. Resolved
|
|
196
|
-
* from the MERGED mock (rather than inherited from `requireActual`) so it uses
|
|
197
|
-
* the suite's `isServicePrincipal` / `serviceNameOf` / `sendError`, which the real
|
|
198
|
-
* one closes over and would write past.
|
|
199
|
-
*/
|
|
200
|
-
export function withInternalServiceGate(mock, overrides) {
|
|
201
|
-
if (overrides.requireInternalService !== undefined)
|
|
202
|
-
return mock;
|
|
203
|
-
mock.requireInternalService = ({ callers }) => (req, res, next) => {
|
|
204
|
-
const isSvc = mock.isServicePrincipal;
|
|
205
|
-
const nameOf = mock.serviceNameOf;
|
|
206
|
-
const caller = nameOf?.(req.user);
|
|
207
|
-
if (isSvc?.(req) && caller && callers.includes(caller)) {
|
|
208
|
-
next();
|
|
209
|
-
return;
|
|
210
|
-
}
|
|
211
|
-
const sendError = mock.sendError;
|
|
212
|
-
sendError(res, 403, 'Internal service calls only', 'INSUFFICIENT_PERMISSIONS');
|
|
213
|
-
};
|
|
214
|
-
return mock;
|
|
215
|
-
}
|
|
216
|
-
/**
|
|
217
|
-
* Mirror api-core's `requireServicePrincipal`: reject a non-service caller with a
|
|
218
|
-
* 403 INSUFFICIENT_PERMISSIONS (an authorization refusal, not a 400), resolving
|
|
219
|
-
* `isServicePrincipal` / `sendError` from the merged mock.
|
|
220
|
-
*/
|
|
221
|
-
export function withServicePrincipalGate(mock, overrides) {
|
|
222
|
-
if (overrides.requireServicePrincipal !== undefined)
|
|
223
|
-
return mock;
|
|
224
|
-
mock.requireServicePrincipal = (req, res, next) => {
|
|
225
|
-
const isSvc = mock.isServicePrincipal;
|
|
226
|
-
if (isSvc?.(req)) {
|
|
227
|
-
next();
|
|
228
|
-
return;
|
|
229
|
-
}
|
|
230
|
-
const sendError = mock.sendError;
|
|
231
|
-
sendError(res, 403, 'Internal service calls only', 'INSUFFICIENT_PERMISSIONS');
|
|
232
|
-
};
|
|
233
|
-
return mock;
|
|
234
|
-
}
|
|
235
|
-
/**
|
|
236
|
-
* `sendBadRequest` delegating to the (possibly-overridden) `sendError`, mirroring
|
|
237
|
-
* the real implementation — so a suite spying on `sendError` also observes the
|
|
238
|
-
* 400s routed through `sendBadRequest`.
|
|
239
|
-
*/
|
|
240
|
-
export function withDelegatingSendBadRequest(mock, overrides) {
|
|
241
|
-
if ('sendBadRequest' in overrides)
|
|
242
|
-
return mock;
|
|
243
|
-
mock.sendBadRequest = (res, message, code) => mock.sendError(res, 400, message, code ?? mockErrorCode.VALIDATION_ERROR);
|
|
244
|
-
return mock;
|
|
245
|
-
}
|
|
246
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9jay1hcGktY29yZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy90ZXN0aW5nL21vY2stYXBpLWNvcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsK0NBQStDO0FBQy9DLHNDQUFzQztBQUV0Qzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBMkJHO0FBRUgsT0FBTyxFQUFFLElBQUksRUFBRSxNQUFNLGVBQWUsQ0FBQztBQWlCckMsb0ZBQW9GO0FBQ3BGLE1BQU0sQ0FBQyxNQUFNLFVBQVUsR0FBRyxHQUFlLEVBQUUsQ0FBQyxDQUFDO0lBQzNDLElBQUksRUFBRSxJQUFJLENBQUMsRUFBRSxFQUFFO0lBQ2YsSUFBSSxFQUFFLElBQUksQ0FBQyxFQUFFLEVBQUU7SUFDZixLQUFLLEVBQUUsSUFBSSxDQUFDLEVBQUUsRUFBRTtJQUNoQixLQUFLLEVBQUUsSUFBSSxDQUFDLEVBQUUsRUFBRTtDQUNqQixDQUFDLENBQUM7QUFFSCxrRkFBa0Y7QUFDbEYsTUFBTSxDQUFDLE1BQU0sYUFBYSxHQUFHLElBQUksS0FBSyxDQUFDLEVBQUUsRUFBRSxFQUFFLEdBQUcsRUFBRSxDQUFDLEVBQUUsRUFBRSxHQUFHLEVBQUUsRUFBRSxDQUFDLEdBQUcsRUFBRSxDQUEyQixDQUFDO0FBRWhHLGtGQUFrRjtBQUNsRixNQUFNLENBQUMsTUFBTSxxQkFBcUIsR0FBRyxDQUFDLElBQWEsRUFBRSxJQUFhLEVBQUUsSUFBVSxFQUFRLEVBQUUsQ0FBQyxJQUFJLEVBQUUsQ0FBQztBQUVoRyxnRkFBZ0Y7QUFDaEYsTUFBTSxPQUFPLFlBQWEsU0FBUSxLQUFLO0lBQ1QsVUFBVTtJQUEwQixJQUFJO0lBQXBFLFlBQTRCLFVBQWtCLEVBQWtCLElBQVksRUFBRSxPQUFnQjtRQUM1RixLQUFLLENBQUMsT0FBTyxDQUFDLENBQUM7MEJBRFcsVUFBVTtvQkFBMEIsSUFBSTtRQUVsRSxJQUFJLENBQUMsSUFBSSxHQUFHLFVBQVUsQ0FBQztJQUN6QixD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QUUsR0FBWSxFQUFFLElBQVUsRUFBRSxFQUFFO1FBQ3hFLE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxrQkFBMkQsQ0FBQztRQUMvRSxJQUFJLEtBQUssRUFBRSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUM7WUFDakIsSUFBSSxFQUFFLENBQUM7WUFDUCxPQUFPO1FBQ1QsQ0FBQztRQUNELE1BQU0sU0FBUyxHQUFHLElBQUksQ0FBQyxTQUFxRSxDQUFDO1FBQzdGLFNBQVMsQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLDZCQUE2QixFQUFFLDBCQUEwQixDQUFDLENBQUM7SUFDakYsQ0FBQyxDQUFDO0lBQ0YsT0FBTyxJQUFJLENBQUM7QUFDZCxDQUFDO0FBRUQ7Ozs7R0FJRztBQUNILE1BQU0sVUFBVSw0QkFBNEIsQ0FBQyxJQUE2QixFQUFFLFNBQWtDO0lBQzVHLElBQUksZ0JBQWdCLElBQUksU0FBUztRQUFFLE9BQU8sSUFBSSxDQUFDO0lBQy9DLElBQUksQ0FBQyxjQUFjLEdBQUcsQ0FBQyxHQUFZLEVBQUUsT0FBZSxFQUFFLElBQWEsRUFBRSxFQUFFLENBQ3BFLElBQUksQ0FBQyxTQUF1RSxDQUMzRSxHQUFHLEVBQUUsR0FBRyxFQUFFLE9BQU8sRUFBRSxJQUFJLElBQUksYUFBYSxDQUFDLGdCQUFnQixDQUFDLENBQUM7SUFDL0QsT0FBTyxJQUFJLENBQUM7QUFDZCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbi8qKlxuICogU2hhcmVkIGBAcGlwZWxpbmUtYnVpbGRlci9hcGktY29yZWAgbW9jayBmYWN0b3J5IGZvciBFU00gc3VpdGVzLlxuICpcbiAqIFdIWTogZXZlcnkgcHJvamVjdCBrZXB0IGl0cyBvd24gYHRlc3QvaGVscGVycy9tb2NrLWFwaS1jb3JlLnRzYCDigJQgdHdlbHZlIGZvcmtzXG4gKiBvZiB0aGUgc2FtZSB+MTUwLWxpbmUgZmlsZSwgcmFuZ2luZyBmcm9tIDIzIHRvIDYxIG92ZXJyaWRkZW4ga2V5cyBhZ2FpbnN0IGFuXG4gKiBhcGktY29yZSBiYXJyZWwgZXhwb3J0aW5nIH4zNTcgc3ltYm9scy4gRWFjaCBmb3JrIHJlLWRlY2xhcmVkIHRoZSBzYW1lIGxvZ2dlclxuICogc3R1YiwgdGhlIHNhbWUgYEVycm9yQ29kZWAgcHJveHksIHRoZSBzYW1lIGVycm9yIGNsYXNzZXMgYW5kIHRoZSBzYW1lXG4gKiBzZXJ2aWNlLWdhdGUgcmUtaW1wbGVtZW50YXRpb25zLCBhbmQgZWFjaCBkcmlmdGVkIGluZGVwZW5kZW50bHkuXG4gKlxuICogVEhFIEZJWDogdGhlIHNoYXJlZCBwYXJ0cyBsaXZlIGhlcmUsIG9uY2UuIEEgcHJvamVjdCdzIGhlbHBlciBrZWVwcyBPTkxZIHRoZVxuICogZGVmYXVsdHMgdGhhdCBhcmUgZ2VudWluZWx5IHByb2plY3Qtc3BlY2lmaWMgKGl0cyB0aWVyIGZpeHR1cmVzLCBpdHMgY29tcGxpYW5jZVxuICogY2xpZW50LCBpdHMgcmVzcG9uc2UgaGVscGVycykgYW5kIGxheWVycyB0aGVtIG9uIHRvcDpcbiAqXG4gKiAgIGNvbnN0IGFjdHVhbEFwaUNvcmUgPSBqZXN0LnJlcXVpcmVBY3R1YWwoJ0BwaXBlbGluZS1idWlsZGVyL2FwaS1jb3JlJykgYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj47XG4gKiAgIGV4cG9ydCBmdW5jdGlvbiBhcGlDb3JlTW9jayhvdmVycmlkZXM6IFJlY29yZDxzdHJpbmcsIHVua25vd24+ID0ge30pIHtcbiAqICAgICByZXR1cm4gYmFzZUFwaUNvcmVNb2NrKGFjdHVhbEFwaUNvcmUsIHsgLi4ucHJvamVjdERlZmF1bHRzLCAuLi5vdmVycmlkZXMgfSk7XG4gKiAgIH1cbiAqXG4gKiBCb3RoIHRoaXMgbW9kdWxlIGFuZCB0aGUgcGVyLXByb2plY3QgaGVscGVycyBzdGFydCBmcm9tIGByZXF1aXJlQWN0dWFsYCwgc28gYW5cbiAqIGV4cG9ydCBBRERFRCB0byBhcGktY29yZSBpcyBpbmhlcml0ZWQgYXV0b21hdGljYWxseSBhbmQgY2FuIG5ldmVyIGFnYWluIGJyZWFrIGFcbiAqIHN1aXRlIHdpdGggXCJkb2VzIG5vdCBwcm92aWRlIGFuIGV4cG9ydCBuYW1lZCBYXCIuXG4gKlxuICogV0hZIElUIExJVkVTIElOIGFwaS1jb3JlOiB0aGUgZGVlcCBwYXRoXG4gKiBgQHBpcGVsaW5lLWJ1aWxkZXIvYXBpLWNvcmUvbGliL3Rlc3RpbmcvbW9jay1hcGktY29yZS5qc2AgaXMgYSBkaWZmZXJlbnRcbiAqIHNwZWNpZmllciBmcm9tIHRoZSBgQHBpcGVsaW5lLWJ1aWxkZXIvYXBpLWNvcmVgIGJhcnJlbCwgc28gYSBzdWl0ZSdzIG1vZHVsZVxuICogbW9jayBkb2VzIE5PVCBpbnRlcmNlcHQgaXQg4oCUIHRoZSBmYWN0b3J5IGNhbiB0aGVyZWZvcmUgcmVhZCB0aGUgcmVhbCBtb2R1bGVcbiAqIHdpdGhvdXQgcmVjdXJzaW5nIGludG8gaXRzIG93biBtb2NrLiBTYW1lIHRyaWNrIGFzIGB0aWVyLW1vY2sudHNgLlxuICovXG5cbmltcG9ydCB7IGplc3QgfSBmcm9tICdAamVzdC9nbG9iYWxzJztcblxuLyoqIE1pbmltYWwgRXhwcmVzcy1pc2ggc2hhcGVzIHRoZSBnYXRlL3Jlc3BvbnNlIHN0dWJzIGJlbG93IHRvdWNoLiAqL1xudHlwZSBNb2NrUmVzID0geyBzdGF0dXM6IChuOiBudW1iZXIpID0+IHsganNvbjogKGI6IHVua25vd24pID0+IHVua25vd24gfSB9O1xudHlwZSBOZXh0ID0gKCkgPT4gdm9pZDtcblxuLyoqIEEgYmFyZSBgamVzdC5mbigpYCwgbmFtZWQgc28gdGhlIGVtaXR0ZWQgYC5kLnRzYCBzdGF5cyBwb3J0YWJsZSAoVFMyODgzKS4gKi9cbnR5cGUgTW9ja0ZuID0gUmV0dXJuVHlwZTx0eXBlb2YgamVzdC5mbj47XG5cbi8qKiBUaGUgd2luc3RvbiBkb3VibGUgZXZlcnkgc3VpdGUgYXNzZXJ0cyBvbjogZm91ciBzcGllcywgbm90aGluZyBlbHNlLiAqL1xuZXhwb3J0IGludGVyZmFjZSBNb2NrTG9nZ2VyIHtcbiAgaW5mbzogTW9ja0ZuO1xuICB3YXJuOiBNb2NrRm47XG4gIGVycm9yOiBNb2NrRm47XG4gIGRlYnVnOiBNb2NrRm47XG59XG5cbi8qKiBUaGUgNC1tZXRob2Qgd2luc3RvbiBzdHViIGV2ZXJ5IHN1aXRlIHJlcGVhdHM7IGEgZnJlc2ggc2V0IG9mIHNwaWVzIHBlciBjYWxsLiAqL1xuZXhwb3J0IGNvbnN0IGxvZ2dlck1vY2sgPSAoKTogTW9ja0xvZ2dlciA9PiAoe1xuICBpbmZvOiBqZXN0LmZuKCksXG4gIHdhcm46IGplc3QuZm4oKSxcbiAgZXJyb3I6IGplc3QuZm4oKSxcbiAgZGVidWc6IGplc3QuZm4oKSxcbn0pO1xuXG4vKiogTWlycm9ycyBhcGktY29yZTogYEVycm9yQ29kZS5BTllfQ09ERWAgcmVzb2x2ZXMgdG8gdGhlIHN0cmluZyBgJ0FOWV9DT0RFJ2AuICovXG5leHBvcnQgY29uc3QgbW9ja0Vycm9yQ29kZSA9IG5ldyBQcm94eSh7fSwgeyBnZXQ6IChfdCwga2V5KSA9PiBrZXkgfSkgYXMgUmVjb3JkPHN0cmluZywgc3RyaW5nPjtcblxuLyoqIE5vLW9wIGd1YXJkOiB0aGUgZGVmYXVsdCBtb2NrIGNvdmVycyByb3V0ZSB3aXJpbmcsIG5vdCB0aGUgcGVybWlzc2lvbiBnYXRlLiAqL1xuZXhwb3J0IGNvbnN0IHBhc3NUaHJvdWdoTWlkZGxld2FyZSA9IChfcmVxOiB1bmtub3duLCBfcmVzOiB1bmtub3duLCBuZXh0OiBOZXh0KTogdm9pZCA9PiBuZXh0KCk7XG5cbi8qKiBNaXJyb3JzIGFwaS1jb3JlJ3MgYEFwcEVycm9yYCBiYXNlICh0eXBlZCBIVFRQIGVycm9yOiBzdGF0dXNDb2RlICsgY29kZSkuICovXG5leHBvcnQgY2xhc3MgTW9ja0FwcEVycm9yIGV4dGVuZHMgRXJyb3Ige1xuICBjb25zdHJ1Y3RvcihwdWJsaWMgcmVhZG9ubHkgc3RhdHVzQ29kZTogbnVtYmVyLCBwdWJsaWMgcmVhZG9ubHkgY29kZTogc3RyaW5nLCBtZXNzYWdlPzogc3RyaW5nKSB7XG4gICAgc3VwZXIobWVzc2FnZSk7XG4gICAgdGhpcy5uYW1lID0gJ0FwcEVycm9yJztcbiAgfVxufVxuXG4vKiogTWlycm9ycyBhcGktY29yZSdzIE5vdEZvdW5kRXJyb3IgKDQwNCAvIE5PVF9GT1VORCkuICovXG5leHBvcnQgY2xhc3MgTW9ja05vdEZvdW5kRXJyb3IgZXh0ZW5kcyBNb2NrQXBwRXJyb3Ige1xuICBjb25zdHJ1Y3RvcihtZXNzYWdlPzogc3RyaW5nKSB7XG4gICAgc3VwZXIoNDA0LCAnTk9UX0ZPVU5EJywgbWVzc2FnZSk7XG4gICAgdGhpcy5uYW1lID0gJ05vdEZvdW5kRXJyb3InO1xuICB9XG59XG5cbi8qKiBNaXJyb3JzIGFwaS1jb3JlJ3MgQ29uZmxpY3RFcnJvciAoNDA5IC8gQ09ORkxJQ1QpLiAqL1xuZXhwb3J0IGNsYXNzIE1vY2tDb25mbGljdEVycm9yIGV4dGVuZHMgTW9ja0FwcEVycm9yIHtcbiAgY29uc3RydWN0b3IobWVzc2FnZT86IHN0cmluZykge1xuICAgIHN1cGVyKDQwOSwgJ0NPTkZMSUNUJywgbWVzc2FnZSk7XG4gICAgdGhpcy5uYW1lID0gJ0NvbmZsaWN0RXJyb3InO1xuICB9XG59XG5cbi8qKiBNaXJyb3JzIGFwaS1jb3JlJ3MgRm9yYmlkZGVuRXJyb3IgKDQwMyAvIElOU1VGRklDSUVOVF9QRVJNSVNTSU9OUykuICovXG5leHBvcnQgY2xhc3MgTW9ja0ZvcmJpZGRlbkVycm9yIGV4dGVuZHMgTW9ja0FwcEVycm9yIHtcbiAgY29uc3RydWN0b3IobWVzc2FnZT86IHN0cmluZykge1xuICAgIHN1cGVyKDQwMywgJ0lOU1VGRklDSUVOVF9QRVJNSVNTSU9OUycsIG1lc3NhZ2UpO1xuICAgIHRoaXMubmFtZSA9ICdGb3JiaWRkZW5FcnJvcic7XG4gIH1cbn1cblxuLyoqIE1pcnJvcnMgYXBpLWNvcmUncyBWYWxpZGF0aW9uRXJyb3IgKDQwMCAvIFZBTElEQVRJT05fRVJST1IpLiAqL1xuZXhwb3J0IGNsYXNzIE1vY2tWYWxpZGF0aW9uRXJyb3IgZXh0ZW5kcyBNb2NrQXBwRXJyb3Ige1xuICBjb25zdHJ1Y3RvcihtZXNzYWdlPzogc3RyaW5nKSB7XG4gICAgc3VwZXIoNDAwLCAnVkFMSURBVElPTl9FUlJPUicsIG1lc3NhZ2UpO1xuICAgIHRoaXMubmFtZSA9ICdWYWxpZGF0aW9uRXJyb3InO1xuICB9XG59XG5cbi8qKlxuICogQ2FwYWJpbGl0eS1hd2FyZSBzdGFuZC1pbiBmb3IgYXBpLWNvcmUncyBgcmVxdWlyZVBlcm1pc3Npb25gIC9cbiAqIGByZXF1aXJlQWxsUGVybWlzc2lvbnNgIGdhdGUgZmFjdG9yaWVzLiBNaXJyb3JzIHRoZSByZWFsIGdhdGUncyBkZWNpc2lvbiAobWludXNcbiAqIHRoZSBkZW5pYWwtYXVkaXQgc2lkZSBlZmZlY3QpOiBzdXBlcmFkbWluIHBhc3Nlcywgb3RoZXJ3aXNlIHRoZSBjYWxsZXIgbXVzdFxuICogaG9sZCB0aGUgcGVybWlzc2lvbihzKTsgYSBtaXNzIGlzIGEgNDAzIHdpdGggdGhlIHJlYWwgbWVzc2FnZSBzaGFwZS5cbiAqXG4gKiBgb25Bbm9ueW1vdXNgIHBpY2tzIHdoYXQgaGFwcGVucyB3aGVuIHRoZXJlIGlzIG5vIGByZXEudXNlcmAgYXQgYWxsOiBgJ3Bhc3MnYFxuICogKHN1aXRlcyB3aXRoIG5vIGF1dGggbGF5ZXIgbW91bnRlZCkgb3IgYCc0MDEnYCAoc3VpdGVzIHRoYXQgZHJpdmUgdGhlIGdhdGUpLlxuICogYHNoYXBlYCBwaWNrcyB0aGUgZGVuaWFsIGVudmVsb3BlIHRoZSBwcm9qZWN0J3Mgc3VpdGVzIGFzc2VydCBvbi5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIG1vY2tQZXJtaXNzaW9uR2F0ZShvcHRpb25zOiB7XG4gIG1vZGU/OiAnc29tZScgfCAnZXZlcnknO1xuICBhbGxvd1NlcnZpY2U/OiBib29sZWFuO1xuICBvbkFub255bW91cz86ICdwYXNzJyB8ICc0MDEnO1xuICBzaGFwZT86ICdlcnJvcicgfCAnbWVzc2FnZSc7XG59ID0ge30pIHtcbiAgY29uc3QgeyBtb2RlID0gJ3NvbWUnLCBhbGxvd1NlcnZpY2UgPSBmYWxzZSwgb25Bbm9ueW1vdXMgPSAncGFzcycsIHNoYXBlID0gJ21lc3NhZ2UnIH0gPSBvcHRpb25zO1xuICBjb25zdCBqb2luZXIgPSBtb2RlID09PSAnc29tZScgPyAnIG9yICcgOiAnIGFuZCAnO1xuICBjb25zdCBkZW55ID0gKG1zZzogc3RyaW5nKSA9PiAoc2hhcGUgPT09ICdlcnJvcicgPyB7IGVycm9yOiBtc2cgfSA6IHsgc3VjY2VzczogZmFsc2UsIG1lc3NhZ2U6IG1zZyB9KTtcbiAgcmV0dXJuICguLi5wZXJtaXNzaW9uczogc3RyaW5nW10pID0+IHtcbiAgICBjb25zdCBnYXRlID0gKHJlcTogeyB1c2VyPzogeyBpc1N1cGVyQWRtaW4/OiBib29sZWFuOyBwZXJtaXNzaW9ucz86IHN0cmluZ1tdOyBzdWI/OiBzdHJpbmcgfSB9LCByZXM6IE1vY2tSZXMsIG5leHQ6IE5leHQpOiB1bmtub3duID0+IHtcbiAgICAgIGNvbnN0IHVzZXIgPSByZXE/LnVzZXI7XG4gICAgICBpZiAoIXVzZXIpIHJldHVybiBvbkFub255bW91cyA9PT0gJ3Bhc3MnID8gbmV4dCgpIDogcmVzLnN0YXR1cyg0MDEpLmpzb24oeyBlcnJvcjogJ0F1dGhlbnRpY2F0aW9uIHJlcXVpcmVkJyB9KTtcbiAgICAgIGlmIChhbGxvd1NlcnZpY2UgJiYgdHlwZW9mIHVzZXIuc3ViID09PSAnc3RyaW5nJyAmJiB1c2VyLnN1Yi5zdGFydHNXaXRoKCdzZXJ2aWNlOicpKSByZXR1cm4gbmV4dCgpO1xuICAgICAgaWYgKHVzZXIuaXNTdXBlckFkbWluID09PSB0cnVlKSByZXR1cm4gbmV4dCgpO1xuICAgICAgY29uc3QgaGVsZCA9IEFycmF5LmlzQXJyYXkodXNlci5wZXJtaXNzaW9ucykgPyB1c2VyLnBlcm1pc3Npb25zIDogW107XG4gICAgICBjb25zdCBvayA9IG1vZGUgPT09ICdzb21lJyA/IHBlcm1pc3Npb25zLnNvbWUoKHApID0+IGhlbGQuaW5jbHVkZXMocCkpIDogcGVybWlzc2lvbnMuZXZlcnkoKHApID0+IGhlbGQuaW5jbHVkZXMocCkpO1xuICAgICAgaWYgKG9rKSByZXR1cm4gbmV4dCgpO1xuICAgICAgcmV0dXJuIHJlcy5zdGF0dXMoNDAzKS5qc29uKGRlbnkoYE1pc3NpbmcgcmVxdWlyZWQgcGVybWlzc2lvbjogJHtwZXJtaXNzaW9ucy5qb2luKGpvaW5lcil9YCkpO1xuICAgIH07XG4gICAgLy8gVGFncyBsZXQgYSB3aXJpbmcgc3VpdGUgbG9jYXRlIHRoZSBnYXRlIGxheWVyIGluIGEgcm91dGVyIHN0YWNrIGFuZCBhc3NlcnRcbiAgICAvLyB3aGljaCBmbGF2b3VyIHdhcyBtb3VudGVkLlxuICAgIChnYXRlIGFzIHVua25vd24gYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4pLl9fcGVybWlzc2lvbiA9IHBlcm1pc3Npb25zLmpvaW4oam9pbmVyKTtcbiAgICAoZ2F0ZSBhcyB1bmtub3duIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+KS5fX2FsbG93U2VydmljZSA9IGFsbG93U2VydmljZTtcbiAgICByZXR1cm4gZ2F0ZTtcbiAgfTtcbn1cblxuLyoqXG4gKiBUaGUgZGVmYXVsdHMgc2hhcmVkIGJ5IGV2ZXJ5IHNlcnZpY2UgLyBwYWNrYWdlIG1vY2s6IHRoZSBsb2dnZXIgc3R1YiwgdGhlXG4gKiBwYWdpbmF0aW9uIGNvbnN0YW50cywgdGhlIGluZXJ0IHNjaGVkdWxlciAvIGxlYWRlci1sb2NrIC8gcmVkaXMtYXVkaXQgd2lyaW5nIGFcbiAqIGJvb3QgbW9kdWxlIGxpbmtzIGF0IGxvYWQsIGFuZCB0aGUgZXJyb3IgKyBjYWNoZSBzaGFwZXMuIEEgcHJvamVjdCBsYXllcnMgaXRzXG4gKiBvd24gZGVmYXVsdHMgb24gdG9wIGJ5IHBhc3NpbmcgdGhlbSBpbiBgb3ZlcnJpZGVzYC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHNlcnZpY2VBcGlDb3JlRGVmYXVsdHMoKTogUmVjb3JkPHN0cmluZywgdW5rbm93bj4ge1xuICByZXR1cm4ge1xuICAgIGNyZWF0ZUxvZ2dlcjogbG9nZ2VyTW9jayxcbiAgICBNQVhfUEFHRV9MSU1JVDogMTAwMCxcbiAgICBERUZBVUxUX1BBR0VfTElNSVQ6IDEwMCxcbiAgICBjbG9zZUxlYWRlckxvY2s6IGFzeW5jICgpID0+IHVuZGVmaW5lZCxcbiAgICBsb2FkQW5kUmVzdG9yZTogYXN5bmMgKCkgPT4gbnVsbCxcbiAgICBSRVBPUlRfSU5URVJWQUxTOiBbJ2RheScsICd3ZWVrJywgJ21vbnRoJ10sXG4gICAgc2NydWJBd3NJZGVudGlmaWVyc0Zyb21TdHJpbmc6IChzOiBzdHJpbmcpID0+IHMsXG4gICAgc2NydWJBd3NJZGVudGlmaWVyczogPFQ+KHY6IFQpOiBUID0+IHYsXG4gICAgY3JlYXRlU2NoZWR1bGVyOiAoKSA9PiAoeyBzdGFydDogKCkgPT4gdW5kZWZpbmVkLCBzdG9wOiAoKSA9PiB1bmRlZmluZWQgfSksXG4gICAgY3JlYXRlRW52UmVkaXNMb2NrOiAoKSA9PiBudWxsLFxuICAgIHJlcXVpcmVTdGVwVXA6IChfcmVxOiB1bmtub3duLCBfcmVzOiB1bmtub3duLCBuZXh0OiBOZXh0KSA9PiBuZXh0KCksXG4gICAgU1lTVEVNX09SR19JRDogJzAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMScsXG4gICAgQ29tcHV0ZVR5cGU6IHsgU01BTEw6ICdTTUFMTCcsIE1FRElVTTogJ01FRElVTScsIExBUkdFOiAnTEFSR0UnLCBYMl9MQVJHRTogJ1gyX0xBUkdFJyB9LFxuICAgIFBsdWdpblR5cGU6IHsgQ09ERV9CVUlMRF9TVEVQOiAnQ29kZUJ1aWxkU3RlcCcsIFNIRUxMX1NURVA6ICdTaGVsbFN0ZXAnLCBNQU5VQUxfQVBQUk9WQUxfU1RFUDogJ01hbnVhbEFwcHJvdmFsU3RlcCcgfSxcbiAgICBFcnJvckNvZGU6IG1vY2tFcnJvckNvZGUsXG4gICAgZXJyb3JNZXNzYWdlOiAoZTogdW5rbm93bikgPT4gKGUgaW5zdGFuY2VvZiBFcnJvciA/IGUubWVzc2FnZSA6IFN0cmluZyhlKSksXG4gICAgTm90Rm91bmRFcnJvcjogTW9ja05vdEZvdW5kRXJyb3IsXG4gICAgY3JlYXRlQ2FjaGVTZXJ2aWNlOiAoKSA9PiAoe1xuICAgICAgZ2V0T3JTZXQ6IChfa2V5OiBzdHJpbmcsIGZhY3Rvcnk6ICgpID0+IFByb21pc2U8dW5rbm93bj4pID0+IGZhY3RvcnkoKSxcbiAgICAgIGludmFsaWRhdGVQYXR0ZXJuOiAoKSA9PiBQcm9taXNlLnJlc29sdmUoMCksXG4gICAgfSksXG4gIH07XG59XG5cbi8qKlxuICogVGhlIGF1ZGl0IC8gc2VjdXJpdHkgd2lyaW5nIGEgc2VydmljZSdzIGJvb3QgbW9kdWxlIChgc3JjL2luZGV4LnRzYCxcbiAqIGBzcmMvc2VydmljZXMvYXVkaXQudHNgKSBsaW5rcyBhdCBsb2FkIHRpbWUuIEluZXJ0IGJ5IGRlZmF1bHQ6IG5vdGhpbmcgYXNzZXJ0c1xuICogb24gdGhlIHJlZ2lzdHJhdGlvbiwgYW5kIHB1bGxpbmcgdGhlIHJlYWwgZ3JhcGggaW4gd291bGQgZHJhZyBSZWRpcyArIEhUVFBcbiAqIGNsaWVudHMgaW50byBldmVyeSByb3V0ZSBzdWl0ZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHNlcnZpY2VBdWRpdERlZmF1bHRzKCk6IFJlY29yZDxzdHJpbmcsIHVua25vd24+IHtcbiAgcmV0dXJuIHtcbiAgICBjcmVhdGVSZW1vdGVBdWRpdENsaWVudDogKCkgPT4gKHsgcmVjb3JkOiBqZXN0LmZuKCkgfSksXG4gICAgY3JlYXRlRW52UmVkaXNBdWRpdFNwb29sOiAoKSA9PiBudWxsLFxuICAgIGNyZWF0ZVNlcnZpY2VBdWRpdENsaWVudDogKCkgPT4gKHsgZW1pdDogamVzdC5mbigpLCBjbGllbnQ6IHsgcmVjb3JkOiBqZXN0LmZuKCkgfSB9KSxcbiAgICBjcmVhdGVSZW1vdGVBdWRpdEFjY2Vzc29yOiAoKSA9PiAoeyBnZXRBdWRpdENsaWVudDogKCkgPT4gKHsgcmVjb3JkOiBqZXN0LmZuKCkgfSksIGVtaXQ6IGplc3QuZm4oKSB9KSxcbiAgICBzZXRBdXRoekRlbmlhbEF1ZGl0b3I6ICgpID0+IHt9LFxuICAgIHdpcmVBdXRoekRlbmlhbEF1ZGl0b3I6ICgpID0+IHt9LFxuICAgIHdpcmVTZXJ2aWNlU2VjdXJpdHk6ICgpID0+IHt9LFxuICAgIHNldFRva2VuUmV2b2NhdGlvblN0b3JlOiAoKSA9PiB7fSxcbiAgICBjcmVhdGVSZWRpc1Rva2VuUmV2b2NhdGlvblN0b3JlOiAoKSA9PiAoeyBnZXRDdXJyZW50VmVyc2lvbjogYXN5bmMgKCkgPT4gbnVsbCB9KSxcbiAgICBjcmVhdGVFbnZSZWRpc1Rva2VuUmV2b2NhdGlvblN0b3JlOiAoKSA9PiAoeyBnZXRDdXJyZW50VmVyc2lvbjogYXN5bmMgKCkgPT4gbnVsbCB9KSxcbiAgfTtcbn1cblxuLyoqXG4gKiBCdWlsZCBhbiBhcGktY29yZSBtb2NrIG5hbWVzcGFjZTogUkVBTCBhcGktY29yZSwgdGhlbiB0aGUgc2hhcmVkIHNlcnZpY2VcbiAqIGRlZmF1bHRzLCB0aGVuIGBvdmVycmlkZXNgIChhIHByb2plY3QncyBvd24gZGVmYXVsdHMgbWVyZ2VkIHdpdGggdGhlIHN1aXRlJ3MpLlxuICpcbiAqIGBhY3R1YWxBcGlDb3JlYCBpcyBzdXBwbGllZCBieSB0aGUgQ0FMTEVSIChgamVzdC5yZXF1aXJlQWN0dWFsKC4uLilgIGF0IHRoZSB0b3BcbiAqIG9mIHRoZSBwcm9qZWN0J3MgYHRlc3QvaGVscGVycy9tb2NrLWFwaS1jb3JlLnRzYCkgcmF0aGVyIHRoYW4gcmVzb2x2ZWQgaGVyZS5cbiAqIFRoYXQgaXMgZGVsaWJlcmF0ZTogYHJlcXVpcmVBY3R1YWxgIG9uIGFuIEVTTSBiYXJyZWwgb25seSBzdWNjZWVkcyBhdCBhIG1vbWVudFxuICogd2hlbiBub3RoaW5nIGVsc2UgaXMgbWlkLWBpbXBvcnQoKWAgb2YgaXQsIGFuZCB0aGUgcHJvamVjdCBoZWxwZXIg4oCUIGEgc3RhdGljXG4gKiBpbXBvcnQgb2YgdGhlIHRlc3QgZmlsZSwgZXZhbHVhdGVkIGJlZm9yZSBhbnkgYGF3YWl0IGltcG9ydChTVVQpYCDigJQgaXMgdGhlIG9uZVxuICogcG9pbnQgd2hlcmUgdGhhdCBhbHdheXMgaG9sZHMuIFJlc29sdmluZyBpdCBmcm9tIGluc2lkZSB0aGlzIG1vZHVsZSAod2hpY2hcbiAqIGxpdmVzIGluIHRoZSBhcGktY29yZSBwYWNrYWdlIGl0c2VsZikgcmFjZXMgdGhhdCBpbXBvcnQgaW4gc29tZSBzdWl0ZXMuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBiYXNlQXBpQ29yZU1vY2soXG4gIGFjdHVhbEFwaUNvcmU6IFJlY29yZDxzdHJpbmcsIHVua25vd24+LFxuICBvdmVycmlkZXM6IFJlY29yZDxzdHJpbmcsIHVua25vd24+ID0ge30sXG4pOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB7XG4gIHJldHVybiB7IC4uLmFjdHVhbEFwaUNvcmUsIC4uLnNlcnZpY2VBcGlDb3JlRGVmYXVsdHMoKSwgLi4ub3ZlcnJpZGVzIH07XG59XG5cbi8qKlxuICogQnVpbGQgYW4gYXBpLWNvcmUgbW9jayBuYW1lc3BhY2Ugd2l0aCBOTyBzZXJ2aWNlIGRlZmF1bHRzIOKAlCBSRUFMIGFwaS1jb3JlIHBsdXNcbiAqIHRoZSBsb2dnZXIgc3R1YiBhbmQgYEVycm9yQ29kZWAgcHJveHkgb25seS4gUGxhdGZvcm0gdXNlcyB0aGlzOiBpdCBkZWxpYmVyYXRlbHlcbiAqIGtlZXBzIGFwaS1jb3JlJ3MgcmVhbCBwYWdpbmF0aW9uL3JlcG9ydCBjb25zdGFudHMgYW5kIHN1cHBsaWVzIGl0cyBvd25cbiAqIChsYXJnZSkgaWRlbnRpdHkvdGllci9wZXJtaXNzaW9uIGRlZmF1bHQgc2V0LlxuICovXG5leHBvcnQgZnVuY3Rpb24gcHJpbWl0aXZlQXBpQ29yZU1vY2soXG4gIGFjdHVhbEFwaUNvcmU6IFJlY29yZDxzdHJpbmcsIHVua25vd24+LFxuICBvdmVycmlkZXM6IFJlY29yZDxzdHJpbmcsIHVua25vd24+ID0ge30sXG4pOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB7XG4gIHJldHVybiB7IC4uLmFjdHVhbEFwaUNvcmUsIGNyZWF0ZUxvZ2dlcjogbG9nZ2VyTW9jaywgRXJyb3JDb2RlOiBtb2NrRXJyb3JDb2RlLCAuLi5vdmVycmlkZXMgfTtcbn1cblxuLyoqXG4gKiBNaXJyb3IgYXBpLWNvcmUncyBgcmVxdWlyZUludGVybmFsU2VydmljZSh7IGNhbGxlcnMgfSlgOiByZWZ1c2UgYW55IHVzZXIgdG9rZW4sXG4gKiB0aGVuIHJlZnVzZSBhIHNlcnZpY2Ugd2hvc2UgbmFtZSBpcyBub3QgaW4gdGhlIHJvdXRlJ3MgY2FsbGVyIGxpc3QuIFJlc29sdmVkXG4gKiBmcm9tIHRoZSBNRVJHRUQgbW9jayAocmF0aGVyIHRoYW4gaW5oZXJpdGVkIGZyb20gYHJlcXVpcmVBY3R1YWxgKSBzbyBpdCB1c2VzXG4gKiB0aGUgc3VpdGUncyBgaXNTZXJ2aWNlUHJpbmNpcGFsYCAvIGBzZXJ2aWNlTmFtZU9mYCAvIGBzZW5kRXJyb3JgLCB3aGljaCB0aGUgcmVhbFxuICogb25lIGNsb3NlcyBvdmVyIGFuZCB3b3VsZCB3cml0ZSBwYXN0LlxuICovXG5leHBvcnQgZnVuY3Rpb24gd2l0aEludGVybmFsU2VydmljZUdhdGUobW9jazogUmVjb3JkPHN0cmluZywgdW5rbm93bj4sIG92ZXJyaWRlczogUmVjb3JkPHN0cmluZywgdW5rbm93bj4pOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB7XG4gIGlmIChvdmVycmlkZXMucmVxdWlyZUludGVybmFsU2VydmljZSAhPT0gdW5kZWZpbmVkKSByZXR1cm4gbW9jaztcbiAgbW9jay5yZXF1aXJlSW50ZXJuYWxTZXJ2aWNlID0gKHsgY2FsbGVycyB9OiB7IGNhbGxlcnM6IHJlYWRvbmx5IHN0cmluZ1tdIH0pID0+XG4gICAgKHJlcTogdW5rbm93biwgcmVzOiB1bmtub3duLCBuZXh0OiBOZXh0KSA9PiB7XG4gICAgICBjb25zdCBpc1N2YyA9IG1vY2suaXNTZXJ2aWNlUHJpbmNpcGFsIGFzICgocjogdW5rbm93bikgPT4gYm9vbGVhbikgfCB1bmRlZmluZWQ7XG4gICAgICBjb25zdCBuYW1lT2YgPSBtb2NrLnNlcnZpY2VOYW1lT2YgYXMgKChjOiB1bmtub3duKSA9PiBzdHJpbmcgfCB1bmRlZmluZWQpIHwgdW5kZWZpbmVkO1xuICAgICAgY29uc3QgY2FsbGVyID0gbmFtZU9mPy4oKHJlcSBhcyB7IHVzZXI/OiB1bmtub3duIH0pLnVzZXIpO1xuICAgICAgaWYgKGlzU3ZjPy4ocmVxKSAmJiBjYWxsZXIgJiYgY2FsbGVycy5pbmNsdWRlcyhjYWxsZXIpKSB7XG4gICAgICAgIG5leHQoKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgY29uc3Qgc2VuZEVycm9yID0gbW9jay5zZW5kRXJyb3IgYXMgKHI6IHVua25vd24sIHM6IG51bWJlciwgbTogc3RyaW5nLCBjOiBzdHJpbmcpID0+IHVua25vd247XG4gICAgICBzZW5kRXJyb3IocmVzLCA0MDMsICdJbnRlcm5hbCBzZXJ2aWNlIGNhbGxzIG9ubHknLCAnSU5TVUZGSUNJRU5UX1BFUk1JU1NJT05TJyk7XG4gICAgfTtcbiAgcmV0dXJuIG1vY2s7XG59XG5cbi8qKlxuICogTWlycm9yIGFwaS1jb3JlJ3MgYHJlcXVpcmVTZXJ2aWNlUHJpbmNpcGFsYDogcmVqZWN0IGEgbm9uLXNlcnZpY2UgY2FsbGVyIHdpdGggYVxuICogNDAzIElOU1VGRklDSUVOVF9QRVJNSVNTSU9OUyAoYW4gYXV0aG9yaXphdGlvbiByZWZ1c2FsLCBub3QgYSA0MDApLCByZXNvbHZpbmdcbiAqIGBpc1NlcnZpY2VQcmluY2lwYWxgIC8gYHNlbmRFcnJvcmAgZnJvbSB0aGUgbWVyZ2VkIG1vY2suXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB3aXRoU2VydmljZVByaW5jaXBhbEdhdGUobW9jazogUmVjb3JkPHN0cmluZywgdW5rbm93bj4sIG92ZXJyaWRlczogUmVjb3JkPHN0cmluZywgdW5rbm93bj4pOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB7XG4gIGlmIChvdmVycmlkZXMucmVxdWlyZVNlcnZpY2VQcmluY2lwYWwgIT09IHVuZGVmaW5lZCkgcmV0dXJuIG1vY2s7XG4gIG1vY2sucmVxdWlyZVNlcnZpY2VQcmluY2lwYWwgPSAocmVxOiB1bmtub3duLCByZXM6IHVua25vd24sIG5leHQ6IE5leHQpID0+IHtcbiAgICBjb25zdCBpc1N2YyA9IG1vY2suaXNTZXJ2aWNlUHJpbmNpcGFsIGFzICgocjogdW5rbm93bikgPT4gYm9vbGVhbikgfCB1bmRlZmluZWQ7XG4gICAgaWYgKGlzU3ZjPy4ocmVxKSkge1xuICAgICAgbmV4dCgpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCBzZW5kRXJyb3IgPSBtb2NrLnNlbmRFcnJvciBhcyAocjogdW5rbm93biwgczogbnVtYmVyLCBtOiBzdHJpbmcsIGM6IHN0cmluZykgPT4gdW5rbm93bjtcbiAgICBzZW5kRXJyb3IocmVzLCA0MDMsICdJbnRlcm5hbCBzZXJ2aWNlIGNhbGxzIG9ubHknLCAnSU5TVUZGSUNJRU5UX1BFUk1JU1NJT05TJyk7XG4gIH07XG4gIHJldHVybiBtb2NrO1xufVxuXG4vKipcbiAqIGBzZW5kQmFkUmVxdWVzdGAgZGVsZWdhdGluZyB0byB0aGUgKHBvc3NpYmx5LW92ZXJyaWRkZW4pIGBzZW5kRXJyb3JgLCBtaXJyb3JpbmdcbiAqIHRoZSByZWFsIGltcGxlbWVudGF0aW9uIOKAlCBzbyBhIHN1aXRlIHNweWluZyBvbiBgc2VuZEVycm9yYCBhbHNvIG9ic2VydmVzIHRoZVxuICogNDAwcyByb3V0ZWQgdGhyb3VnaCBgc2VuZEJhZFJlcXVlc3RgLlxuICovXG5leHBvcnQgZnVuY3Rpb24gd2l0aERlbGVnYXRpbmdTZW5kQmFkUmVxdWVzdChtb2NrOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiwgb3ZlcnJpZGVzOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPik6IFJlY29yZDxzdHJpbmcsIHVua25vd24+IHtcbiAgaWYgKCdzZW5kQmFkUmVxdWVzdCcgaW4gb3ZlcnJpZGVzKSByZXR1cm4gbW9jaztcbiAgbW9jay5zZW5kQmFkUmVxdWVzdCA9IChyZXM6IHVua25vd24sIG1lc3NhZ2U6IHN0cmluZywgY29kZT86IHN0cmluZykgPT5cbiAgICAobW9jay5zZW5kRXJyb3IgYXMgKHI6IHVua25vd24sIHM6IG51bWJlciwgbTogc3RyaW5nLCBjPzogc3RyaW5nKSA9PiB1bmtub3duKShcbiAgICAgIHJlcywgNDAwLCBtZXNzYWdlLCBjb2RlID8/IG1vY2tFcnJvckNvZGUuVkFMSURBVElPTl9FUlJPUik7XG4gIHJldHVybiBtb2NrO1xufVxuIl19
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Build a `drizzle-orm` mock namespace: every real export, with `overrides`
|
|
3
|
-
* spread last so a suite can swap an operator for an inspectable stub (or a
|
|
4
|
-
* `jest.fn()` it asserts on) without having to enumerate the rest.
|
|
5
|
-
*/
|
|
6
|
-
export declare function drizzleMock(overrides?: Record<string, unknown>): Record<string, unknown>;
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
// Copyright 2026 Pipeline Builder Contributors
|
|
2
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
-
/**
|
|
4
|
-
* Shared `drizzle-orm` module mock for ESM suites.
|
|
5
|
-
*
|
|
6
|
-
* WHY: eighteen suites hand-wrote `jest.unstable_mockModule('drizzle-orm', () => ({ … }))`
|
|
7
|
-
* with whatever operator subset they happened to need. `unstable_mockModule`
|
|
8
|
-
* replaces the WHOLE namespace, so the moment production code reached for an
|
|
9
|
-
* operator a suite had not listed (`desc` was present in 6 of the 18, absent in
|
|
10
|
-
* 12) ESM linking failed with "does not provide an export named X" — a build
|
|
11
|
-
* break with no relationship to the change that triggered it.
|
|
12
|
-
*
|
|
13
|
-
* THE FIX: start from the REAL module and overlay only what a suite asserts on.
|
|
14
|
-
* A new drizzle export — or a new operator used by production code — is inherited
|
|
15
|
-
* automatically, so the drift class is gone rather than merely re-synchronised.
|
|
16
|
-
*
|
|
17
|
-
* WHY IT LIVES IN api-core: every service already deep-imports test fixtures from
|
|
18
|
-
* `@pipeline-builder/api-core/lib/testing/*` (see tier-mock.ts). The deep path is
|
|
19
|
-
* NOT intercepted by a suite's `@pipeline-builder/api-core` module mock, so the
|
|
20
|
-
* factory is safe to import from inside a suite that mocks the api-core barrel.
|
|
21
|
-
*
|
|
22
|
-
* USAGE:
|
|
23
|
-
* import { drizzleMock } from '@pipeline-builder/api-core/lib/testing/mock-drizzle.js';
|
|
24
|
-
* jest.unstable_mockModule('drizzle-orm', () => drizzleMock({
|
|
25
|
-
* eq: (col: unknown, val: unknown) => ({ _kind: 'eq', col, val }),
|
|
26
|
-
* }));
|
|
27
|
-
*
|
|
28
|
-
* The eslint `no-restricted-syntax` rule added in `.projenrc.ts` fails any suite
|
|
29
|
-
* that goes back to an inline object literal here.
|
|
30
|
-
*/
|
|
31
|
-
import { jest } from '@jest/globals';
|
|
32
|
-
/**
|
|
33
|
-
* The REAL drizzle-orm namespace, resolved LAZILY (and memoised) — evaluating it
|
|
34
|
-
* at module load races the ESM loader when a suite's static import graph already
|
|
35
|
-
* has drizzle in flight. `requireActual` bypasses the module mock, so this is
|
|
36
|
-
* safe to call from inside the very factory that replaces it.
|
|
37
|
-
*/
|
|
38
|
-
let actualDrizzleCache;
|
|
39
|
-
try {
|
|
40
|
-
actualDrizzleCache = jest.requireActual('drizzle-orm');
|
|
41
|
-
}
|
|
42
|
-
catch {
|
|
43
|
-
actualDrizzleCache = undefined;
|
|
44
|
-
}
|
|
45
|
-
function actualDrizzle() {
|
|
46
|
-
actualDrizzleCache ??= jest.requireActual('drizzle-orm');
|
|
47
|
-
return actualDrizzleCache;
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Build a `drizzle-orm` mock namespace: every real export, with `overrides`
|
|
51
|
-
* spread last so a suite can swap an operator for an inspectable stub (or a
|
|
52
|
-
* `jest.fn()` it asserts on) without having to enumerate the rest.
|
|
53
|
-
*/
|
|
54
|
-
export function drizzleMock(overrides = {}) {
|
|
55
|
-
return { ...actualDrizzle(), ...overrides };
|
|
56
|
-
}
|
|
57
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9jay1kcml6emxlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3Rlc3RpbmcvbW9jay1kcml6emxlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUFFdEM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQTJCRztBQUVILE9BQU8sRUFBRSxJQUFJLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFFckM7Ozs7O0dBS0c7QUFDSCxJQUFJLGtCQUF1RCxDQUFDO0FBQzVELElBQUksQ0FBQztJQUNILGtCQUFrQixHQUFHLElBQUksQ0FBQyxhQUFhLENBQUMsYUFBYSxDQUE0QixDQUFDO0FBQ3BGLENBQUM7QUFBQyxNQUFNLENBQUM7SUFDUCxrQkFBa0IsR0FBRyxTQUFTLENBQUM7QUFDakMsQ0FBQztBQUNELFNBQVMsYUFBYTtJQUNwQixrQkFBa0IsS0FBSyxJQUFJLENBQUMsYUFBYSxDQUFDLGFBQWEsQ0FBNEIsQ0FBQztJQUNwRixPQUFPLGtCQUFrQixDQUFDO0FBQzVCLENBQUM7QUFFRDs7OztHQUlHO0FBQ0gsTUFBTSxVQUFVLFdBQVcsQ0FBQyxTQUFTLEdBQTRCLEVBQUU7SUFDakUsT0FBTyxFQUFFLEdBQUcsYUFBYSxFQUFFLEVBQUUsR0FBRyxTQUFTLEVBQUUsQ0FBQztBQUM5QyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbi8qKlxuICogU2hhcmVkIGBkcml6emxlLW9ybWAgbW9kdWxlIG1vY2sgZm9yIEVTTSBzdWl0ZXMuXG4gKlxuICogV0hZOiBlaWdodGVlbiBzdWl0ZXMgaGFuZC13cm90ZSBgamVzdC51bnN0YWJsZV9tb2NrTW9kdWxlKCdkcml6emxlLW9ybScsICgpID0+ICh7IOKApiB9KSlgXG4gKiB3aXRoIHdoYXRldmVyIG9wZXJhdG9yIHN1YnNldCB0aGV5IGhhcHBlbmVkIHRvIG5lZWQuIGB1bnN0YWJsZV9tb2NrTW9kdWxlYFxuICogcmVwbGFjZXMgdGhlIFdIT0xFIG5hbWVzcGFjZSwgc28gdGhlIG1vbWVudCBwcm9kdWN0aW9uIGNvZGUgcmVhY2hlZCBmb3IgYW5cbiAqIG9wZXJhdG9yIGEgc3VpdGUgaGFkIG5vdCBsaXN0ZWQgKGBkZXNjYCB3YXMgcHJlc2VudCBpbiA2IG9mIHRoZSAxOCwgYWJzZW50IGluXG4gKiAxMikgRVNNIGxpbmtpbmcgZmFpbGVkIHdpdGggXCJkb2VzIG5vdCBwcm92aWRlIGFuIGV4cG9ydCBuYW1lZCBYXCIg4oCUIGEgYnVpbGRcbiAqIGJyZWFrIHdpdGggbm8gcmVsYXRpb25zaGlwIHRvIHRoZSBjaGFuZ2UgdGhhdCB0cmlnZ2VyZWQgaXQuXG4gKlxuICogVEhFIEZJWDogc3RhcnQgZnJvbSB0aGUgUkVBTCBtb2R1bGUgYW5kIG92ZXJsYXkgb25seSB3aGF0IGEgc3VpdGUgYXNzZXJ0cyBvbi5cbiAqIEEgbmV3IGRyaXp6bGUgZXhwb3J0IOKAlCBvciBhIG5ldyBvcGVyYXRvciB1c2VkIGJ5IHByb2R1Y3Rpb24gY29kZSDigJQgaXMgaW5oZXJpdGVkXG4gKiBhdXRvbWF0aWNhbGx5LCBzbyB0aGUgZHJpZnQgY2xhc3MgaXMgZ29uZSByYXRoZXIgdGhhbiBtZXJlbHkgcmUtc3luY2hyb25pc2VkLlxuICpcbiAqIFdIWSBJVCBMSVZFUyBJTiBhcGktY29yZTogZXZlcnkgc2VydmljZSBhbHJlYWR5IGRlZXAtaW1wb3J0cyB0ZXN0IGZpeHR1cmVzIGZyb21cbiAqIGBAcGlwZWxpbmUtYnVpbGRlci9hcGktY29yZS9saWIvdGVzdGluZy8qYCAoc2VlIHRpZXItbW9jay50cykuIFRoZSBkZWVwIHBhdGggaXNcbiAqIE5PVCBpbnRlcmNlcHRlZCBieSBhIHN1aXRlJ3MgYEBwaXBlbGluZS1idWlsZGVyL2FwaS1jb3JlYCBtb2R1bGUgbW9jaywgc28gdGhlXG4gKiBmYWN0b3J5IGlzIHNhZmUgdG8gaW1wb3J0IGZyb20gaW5zaWRlIGEgc3VpdGUgdGhhdCBtb2NrcyB0aGUgYXBpLWNvcmUgYmFycmVsLlxuICpcbiAqIFVTQUdFOlxuICogICBpbXBvcnQgeyBkcml6emxlTW9jayB9IGZyb20gJ0BwaXBlbGluZS1idWlsZGVyL2FwaS1jb3JlL2xpYi90ZXN0aW5nL21vY2stZHJpenpsZS5qcyc7XG4gKiAgIGplc3QudW5zdGFibGVfbW9ja01vZHVsZSgnZHJpenpsZS1vcm0nLCAoKSA9PiBkcml6emxlTW9jayh7XG4gKiAgICAgZXE6IChjb2w6IHVua25vd24sIHZhbDogdW5rbm93bikgPT4gKHsgX2tpbmQ6ICdlcScsIGNvbCwgdmFsIH0pLFxuICogICB9KSk7XG4gKlxuICogVGhlIGVzbGludCBgbm8tcmVzdHJpY3RlZC1zeW50YXhgIHJ1bGUgYWRkZWQgaW4gYC5wcm9qZW5yYy50c2AgZmFpbHMgYW55IHN1aXRlXG4gKiB0aGF0IGdvZXMgYmFjayB0byBhbiBpbmxpbmUgb2JqZWN0IGxpdGVyYWwgaGVyZS5cbiAqL1xuXG5pbXBvcnQgeyBqZXN0IH0gZnJvbSAnQGplc3QvZ2xvYmFscyc7XG5cbi8qKlxuICogVGhlIFJFQUwgZHJpenpsZS1vcm0gbmFtZXNwYWNlLCByZXNvbHZlZCBMQVpJTFkgKGFuZCBtZW1vaXNlZCkg4oCUIGV2YWx1YXRpbmcgaXRcbiAqIGF0IG1vZHVsZSBsb2FkIHJhY2VzIHRoZSBFU00gbG9hZGVyIHdoZW4gYSBzdWl0ZSdzIHN0YXRpYyBpbXBvcnQgZ3JhcGggYWxyZWFkeVxuICogaGFzIGRyaXp6bGUgaW4gZmxpZ2h0LiBgcmVxdWlyZUFjdHVhbGAgYnlwYXNzZXMgdGhlIG1vZHVsZSBtb2NrLCBzbyB0aGlzIGlzXG4gKiBzYWZlIHRvIGNhbGwgZnJvbSBpbnNpZGUgdGhlIHZlcnkgZmFjdG9yeSB0aGF0IHJlcGxhY2VzIGl0LlxuICovXG5sZXQgYWN0dWFsRHJpenpsZUNhY2hlOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB8IHVuZGVmaW5lZDtcbnRyeSB7XG4gIGFjdHVhbERyaXp6bGVDYWNoZSA9IGplc3QucmVxdWlyZUFjdHVhbCgnZHJpenpsZS1vcm0nKSBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbn0gY2F0Y2gge1xuICBhY3R1YWxEcml6emxlQ2FjaGUgPSB1bmRlZmluZWQ7XG59XG5mdW5jdGlvbiBhY3R1YWxEcml6emxlKCk6IFJlY29yZDxzdHJpbmcsIHVua25vd24+IHtcbiAgYWN0dWFsRHJpenpsZUNhY2hlID8/PSBqZXN0LnJlcXVpcmVBY3R1YWwoJ2RyaXp6bGUtb3JtJykgYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj47XG4gIHJldHVybiBhY3R1YWxEcml6emxlQ2FjaGU7XG59XG5cbi8qKlxuICogQnVpbGQgYSBgZHJpenpsZS1vcm1gIG1vY2sgbmFtZXNwYWNlOiBldmVyeSByZWFsIGV4cG9ydCwgd2l0aCBgb3ZlcnJpZGVzYFxuICogc3ByZWFkIGxhc3Qgc28gYSBzdWl0ZSBjYW4gc3dhcCBhbiBvcGVyYXRvciBmb3IgYW4gaW5zcGVjdGFibGUgc3R1YiAob3IgYVxuICogYGplc3QuZm4oKWAgaXQgYXNzZXJ0cyBvbikgd2l0aG91dCBoYXZpbmcgdG8gZW51bWVyYXRlIHRoZSByZXN0LlxuICovXG5leHBvcnQgZnVuY3Rpb24gZHJpenpsZU1vY2sob3ZlcnJpZGVzOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiA9IHt9KTogUmVjb3JkPHN0cmluZywgdW5rbm93bj4ge1xuICByZXR1cm4geyAuLi5hY3R1YWxEcml6emxlKCksIC4uLm92ZXJyaWRlcyB9O1xufVxuIl19
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { type RouteTableEntry } from '../middleware/route-table.js';
|
|
2
|
-
/** A rule a route is allowed not to satisfy, with the reason it is exempt. */
|
|
3
|
-
export interface RouteCoverageException {
|
|
4
|
-
/** HTTP method, or '*' for any. */
|
|
5
|
-
method?: string;
|
|
6
|
-
/** Exact route path, or a pattern matched against `METHOD /path`. */
|
|
7
|
-
path: string | RegExp;
|
|
8
|
-
/** Which rule is waived: the permission gate, the audit declaration, or both. */
|
|
9
|
-
waive: 'permission' | 'audit' | 'all';
|
|
10
|
-
/** Why this route can't carry the gate/audit. Required — no silent allowlisting. */
|
|
11
|
-
reason: string;
|
|
12
|
-
/**
|
|
13
|
-
* Skip the staleness check for this entry. Only for the shared
|
|
14
|
-
* {@link INFRA_ROUTE_EXCEPTIONS}, whose routes depend on per-service
|
|
15
|
-
* `createApp` options (OpenAPI docs, log stream) — a service that doesn't
|
|
16
|
-
* enable them would otherwise fail on an "unused" shared exception.
|
|
17
|
-
*/
|
|
18
|
-
optional?: boolean;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Exceptions every service inherits from `createApp`'s infrastructure routes:
|
|
22
|
-
* unauthenticated probes, the service-principal-gated warm-up, the metrics
|
|
23
|
-
* scrape endpoint, the OpenAPI docs, and the ticket-gated SSE log stream.
|
|
24
|
-
*/
|
|
25
|
-
export declare const INFRA_ROUTE_EXCEPTIONS: readonly RouteCoverageException[];
|
|
26
|
-
/** The outcome of a coverage check: what fails, and which exceptions went unused. */
|
|
27
|
-
export interface RouteCoverageResult {
|
|
28
|
-
/** One human-readable line per uncovered route (empty ⇒ full coverage). */
|
|
29
|
-
violations: string[];
|
|
30
|
-
/** Exceptions that waived nothing — stale entries to delete. */
|
|
31
|
-
unusedExceptions: string[];
|
|
32
|
-
}
|
|
33
|
-
/** Check a service's route table against the coverage rules. */
|
|
34
|
-
export declare function findRouteCoverageViolations(table: readonly RouteTableEntry[], exceptions?: readonly RouteCoverageException[]): RouteCoverageResult;
|
|
35
|
-
/** One internal route a service declares, and the services allowed to call it. */
|
|
36
|
-
export interface InternalRouteDeclaration {
|
|
37
|
-
method: string;
|
|
38
|
-
path: string;
|
|
39
|
-
/** The `callers` passed to `requireInternalService` — also what the mesh policy names. */
|
|
40
|
-
callers: string[];
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Check a service's INTERNAL routes (#14) against what it declares.
|
|
44
|
-
*
|
|
45
|
-
* The declaration is the single list a service maintains, and it is checked in
|
|
46
|
-
* BOTH directions, so neither side can drift:
|
|
47
|
-
*
|
|
48
|
-
* - every declared route must exist and be gated by `requireInternalService`
|
|
49
|
-
* with exactly the declared callers (and `requireAuth` before it);
|
|
50
|
-
* - every route the table says is internal must be declared — so adding one
|
|
51
|
-
* forces a visible change here, and the Istio `AuthorizationPolicy` that
|
|
52
|
-
* names the same callers (`deploy/*/k8s/istio-internal-routes.yaml`) has
|
|
53
|
-
* one authoritative list to be reviewed against.
|
|
54
|
-
*/
|
|
55
|
-
export declare function findInternalRouteViolations(table: readonly RouteTableEntry[], declared: readonly InternalRouteDeclaration[]): string[];
|
|
56
|
-
/** Every audit action declared anywhere in a route table. */
|
|
57
|
-
export declare function declaredAuditActions(table: readonly RouteTableEntry[]): string[];
|
|
58
|
-
/**
|
|
59
|
-
* Compare a service's route table to its checked-in copy under
|
|
60
|
-
* `frontend/src/generated/route-table/` — the file the frontend's `can(...)`
|
|
61
|
-
* tests read. Returns a diff message, or `null` when they match. Set
|
|
62
|
-
* `UPDATE_ROUTE_TABLES=1` to rewrite the file after an intended route change.
|
|
63
|
-
*/
|
|
64
|
-
export declare function compareRouteTableSnapshot(table: readonly RouteTableEntry[], file: string): string | null;
|