@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.
Files changed (187) hide show
  1. package/README.md +3 -2
  2. package/lib/constants/index.d.ts +0 -1
  3. package/lib/constants/index.js +1 -2
  4. package/lib/errors/app-errors.d.ts +8 -3
  5. package/lib/errors/app-errors.js +10 -5
  6. package/lib/helpers/access-helpers.d.ts +4 -6
  7. package/lib/helpers/access-helpers.js +6 -8
  8. package/lib/helpers/index.d.ts +3 -3
  9. package/lib/helpers/index.js +4 -4
  10. package/lib/helpers/org-hierarchy-http.d.ts +11 -10
  11. package/lib/helpers/org-hierarchy-http.js +24 -35
  12. package/lib/helpers/org-hierarchy.d.ts +37 -3
  13. package/lib/helpers/org-hierarchy.js +45 -4
  14. package/lib/helpers/restore-helpers.d.ts +44 -24
  15. package/lib/helpers/restore-helpers.js +41 -52
  16. package/lib/helpers/sse-helpers.d.ts +29 -7
  17. package/lib/helpers/sse-helpers.js +36 -9
  18. package/lib/index.d.ts +16 -14
  19. package/lib/index.js +17 -15
  20. package/lib/middleware/assurance.d.ts +116 -0
  21. package/lib/middleware/assurance.js +176 -0
  22. package/lib/middleware/auth.d.ts +5 -427
  23. package/lib/middleware/auth.js +36 -789
  24. package/lib/middleware/ecosystem-guard.d.ts +47 -0
  25. package/lib/middleware/ecosystem-guard.js +60 -0
  26. package/lib/middleware/index.d.ts +11 -4
  27. package/lib/middleware/index.js +12 -5
  28. package/lib/middleware/jwt-verify.d.ts +52 -0
  29. package/lib/middleware/jwt-verify.js +105 -0
  30. package/lib/middleware/permission-gates.d.ts +133 -0
  31. package/lib/middleware/permission-gates.js +260 -0
  32. package/lib/middleware/revocation.d.ts +96 -0
  33. package/lib/middleware/revocation.js +162 -0
  34. package/lib/middleware/route-table.d.ts +11 -0
  35. package/lib/middleware/route-table.js +4 -1
  36. package/lib/middleware/service-tokens.d.ts +83 -0
  37. package/lib/middleware/service-tokens.js +158 -0
  38. package/lib/middleware/step-up.d.ts +2 -2
  39. package/lib/middleware/step-up.js +8 -7
  40. package/lib/middleware/system-org.d.ts +20 -0
  41. package/lib/middleware/system-org.js +25 -0
  42. package/lib/openapi/routes/plugin-routes.js +133 -10
  43. package/lib/openapi/schema-registry.js +2 -5
  44. package/lib/routes/index.d.ts +1 -1
  45. package/lib/routes/index.js +2 -2
  46. package/lib/services/api-key-exchange.js +7 -5
  47. package/lib/services/audit-spool.d.ts +34 -10
  48. package/lib/services/audit-spool.js +75 -34
  49. package/lib/services/cache-service.d.ts +7 -0
  50. package/lib/services/cache-service.js +1 -1
  51. package/lib/services/circuit-breaker.d.ts +14 -1
  52. package/lib/services/circuit-breaker.js +36 -10
  53. package/lib/services/compliance-client.d.ts +10 -2
  54. package/lib/services/compliance-client.js +17 -10
  55. package/lib/services/compliance-event-subscriber.js +6 -6
  56. package/lib/services/durable-event-bus.js +8 -11
  57. package/lib/services/ecosystem-notify-client.d.ts +32 -0
  58. package/lib/services/ecosystem-notify-client.js +71 -0
  59. package/lib/services/env-redis.d.ts +4 -1
  60. package/lib/services/env-redis.js +32 -3
  61. package/lib/services/http-client.d.ts +19 -0
  62. package/lib/services/http-client.js +98 -19
  63. package/lib/services/index.d.ts +25 -24
  64. package/lib/services/index.js +25 -22
  65. package/lib/services/jwks-cache.js +5 -4
  66. package/lib/services/leader-lock.d.ts +22 -5
  67. package/lib/services/leader-lock.js +61 -6
  68. package/lib/services/quota.d.ts +9 -4
  69. package/lib/services/quota.js +34 -40
  70. package/lib/services/remote-audit-client.d.ts +28 -32
  71. package/lib/services/remote-audit-client.js +166 -49
  72. package/lib/services/retry-forever.js +5 -11
  73. package/lib/services/retry-strategy.d.ts +1 -1
  74. package/lib/services/retry-strategy.js +5 -4
  75. package/lib/services/scheduler.d.ts +8 -3
  76. package/lib/services/scheduler.js +17 -5
  77. package/lib/services/service-boot.d.ts +14 -13
  78. package/lib/services/service-boot.js +18 -15
  79. package/lib/services/service-keys.d.ts +1 -1
  80. package/lib/services/service-keys.js +12 -18
  81. package/lib/services/sse-ticket-store.d.ts +9 -1
  82. package/lib/services/sse-ticket-store.js +24 -9
  83. package/lib/services/system-notification.d.ts +27 -0
  84. package/lib/services/system-notification.js +50 -0
  85. package/lib/services/token-revocation.d.ts +50 -1
  86. package/lib/services/token-revocation.js +103 -3
  87. package/lib/types/audit-events.d.ts +6 -6
  88. package/lib/types/audit-events.js +1 -1
  89. package/lib/types/common.d.ts +32 -13
  90. package/lib/types/common.js +2 -1
  91. package/lib/types/ecosystem-notifications.d.ts +142 -0
  92. package/lib/types/ecosystem-notifications.js +210 -0
  93. package/lib/types/ecosystem.d.ts +77 -0
  94. package/lib/types/ecosystem.js +119 -0
  95. package/lib/types/error-codes.d.ts +81 -0
  96. package/lib/types/error-codes.js +108 -1
  97. package/lib/types/feature-flags.d.ts +21 -1
  98. package/lib/types/feature-flags.js +36 -2
  99. package/lib/types/index.d.ts +14 -9
  100. package/lib/types/index.js +15 -10
  101. package/lib/types/permissions.d.ts +46 -8
  102. package/lib/types/permissions.js +75 -12
  103. package/lib/types/plugin-catalog.d.ts +35 -0
  104. package/lib/types/plugin-catalog.js +39 -0
  105. package/lib/types/plugin-health.d.ts +102 -0
  106. package/lib/types/plugin-health.js +184 -0
  107. package/lib/types/quota-tiers.d.ts +15 -0
  108. package/lib/types/quota-tiers.js +23 -3
  109. package/lib/types/wire-vocabulary.d.ts +102 -0
  110. package/lib/types/wire-vocabulary.js +94 -0
  111. package/lib/utils/alias-resolver.js +2 -2
  112. package/lib/utils/audit.d.ts +5 -2
  113. package/lib/utils/audit.js +6 -3
  114. package/lib/utils/compliance-attributes.js +3 -4
  115. package/lib/utils/concurrency.d.ts +7 -0
  116. package/lib/utils/concurrency.js +13 -1
  117. package/lib/utils/env.d.ts +3 -4
  118. package/lib/utils/env.js +4 -5
  119. package/lib/utils/identity.d.ts +13 -4
  120. package/lib/utils/identity.js +18 -10
  121. package/lib/utils/index.d.ts +13 -10
  122. package/lib/utils/index.js +13 -10
  123. package/lib/utils/jwk.d.ts +4 -0
  124. package/lib/utils/jwk.js +13 -1
  125. package/lib/utils/logger.d.ts +1 -1
  126. package/lib/utils/logger.js +1 -1
  127. package/lib/utils/object.d.ts +11 -0
  128. package/lib/utils/object.js +24 -1
  129. package/lib/utils/params.d.ts +8 -22
  130. package/lib/utils/params.js +15 -43
  131. package/lib/utils/plugin-compliance.d.ts +98 -0
  132. package/lib/utils/plugin-compliance.js +78 -0
  133. package/lib/utils/proof-of-work.d.ts +69 -0
  134. package/lib/utils/proof-of-work.js +139 -0
  135. package/lib/utils/response.d.ts +16 -6
  136. package/lib/utils/response.js +18 -15
  137. package/lib/utils/retention.d.ts +17 -0
  138. package/lib/utils/retention.js +30 -0
  139. package/lib/utils/secret-encryption.d.ts +7 -7
  140. package/lib/utils/secret-encryption.js +9 -10
  141. package/lib/utils/secret-rotation.js +2 -2
  142. package/lib/utils/sensitive-patterns.js +4 -5
  143. package/lib/utils/service-registry.d.ts +15 -0
  144. package/lib/utils/service-registry.js +24 -0
  145. package/lib/utils/ssrf.d.ts +11 -2
  146. package/lib/utils/ssrf.js +112 -33
  147. package/lib/validation/ai-schemas.js +3 -2
  148. package/lib/validation/common-schemas.d.ts +0 -9
  149. package/lib/validation/common-schemas.js +1 -7
  150. package/lib/validation/dockerfile-static.d.ts +26 -0
  151. package/lib/validation/dockerfile-static.js +210 -0
  152. package/lib/validation/index.d.ts +13 -4
  153. package/lib/validation/index.js +14 -5
  154. package/lib/validation/message-schemas.js +6 -4
  155. package/lib/validation/pipeline-schemas.d.ts +8 -2
  156. package/lib/validation/pipeline-schemas.js +30 -4
  157. package/lib/validation/plugin-base-images.d.ts +20 -0
  158. package/lib/validation/plugin-base-images.js +17 -0
  159. package/lib/validation/plugin-catalog-detect.d.ts +86 -0
  160. package/lib/validation/plugin-catalog-detect.js +237 -0
  161. package/lib/validation/plugin-catalog-metadata.d.ts +141 -0
  162. package/lib/validation/plugin-catalog-metadata.js +162 -0
  163. package/lib/validation/plugin-heuristics.d.ts +41 -0
  164. package/lib/validation/plugin-heuristics.js +211 -0
  165. package/lib/validation/plugin-lint.d.ts +18 -0
  166. package/lib/validation/plugin-lint.js +136 -0
  167. package/lib/validation/plugin-name-confusable.d.ts +15 -0
  168. package/lib/validation/plugin-name-confusable.js +81 -0
  169. package/lib/validation/plugin-schemas.d.ts +40 -26
  170. package/lib/validation/plugin-schemas.js +26 -23
  171. package/lib/validation/plugin-spec-schema.d.ts +112 -0
  172. package/lib/validation/plugin-spec-schema.js +163 -0
  173. package/lib/validation/plugin-template-contract.d.ts +64 -0
  174. package/lib/validation/plugin-template-contract.js +113 -0
  175. package/package.json +69 -4
  176. package/lib/testing/mock-api-core.d.ts +0 -150
  177. package/lib/testing/mock-api-core.js +0 -246
  178. package/lib/testing/mock-drizzle.d.ts +0 -6
  179. package/lib/testing/mock-drizzle.js +0 -57
  180. package/lib/testing/route-coverage.d.ts +0 -64
  181. package/lib/testing/route-coverage.js +0 -183
  182. package/lib/testing/service-tokens.d.ts +0 -66
  183. package/lib/testing/service-tokens.js +0 -137
  184. package/lib/testing/tier-mock.d.ts +0 -44
  185. package/lib/testing/tier-mock.js +0 -65
  186. package/lib/testing/user-tokens.d.ts +0 -75
  187. package/lib/testing/user-tokens.js +0 -115
@@ -1,75 +0,0 @@
1
- /**
2
- * Test helpers for the ES256 user-token chain.
3
- *
4
- * Before #5 every suite that needed an authenticated request just signed a JWT
5
- * with a literal `JWT_SECRET` — one line, and seventeen independent copies of
6
- * it. User tokens are asymmetric now, which needs a keypair, a `kid` and a JWKS
7
- * the verifier can fetch, so that per-suite copy-paste would be ~15 lines each.
8
- * Hence one shared seam, in api-core next to the route-coverage helper, imported
9
- * by the suites of every package:
10
- *
11
- * ```ts
12
- * import { installTestJwks, signTestUserToken } from '@pipeline-builder/api-core/lib/testing/user-tokens.js';
13
- *
14
- * const jwks = installTestJwks();
15
- * const token = signTestUserToken({ sub: 'u1', role: 'member', ... });
16
- * ```
17
- *
18
- * `installTestJwks` replaces the process-wide JWKS cache with one backed by the
19
- * generated keys — no HTTP, and it counts fetches so a test can assert the
20
- * refresh / unknown-`kid` / negative-cache behaviour.
21
- */
22
- import { type KeyObject } from 'crypto';
23
- import { type JwksDocument, type PublicJwk } from '../utils/jwk.js';
24
- /** A generated signing key, in every form the tests need. */
25
- export interface TestSigningKey {
26
- kid: string;
27
- privateKey: KeyObject;
28
- publicKey: KeyObject;
29
- jwk: PublicJwk;
30
- }
31
- /** Generate one EC P-256 signing key, `kid` = its RFC 7638 thumbprint. */
32
- export declare function generateTestSigningKey(): TestSigningKey;
33
- /**
34
- * Sign `payload` as an ES256 JWT with `key`. `expiresIn` seconds (default 300).
35
- * Synchronous — `crypto.sign` is, and platform's async signer only exists
36
- * because KMS is a network call — so a suite can mint a token inline wherever it
37
- * used to call `jwt.sign`.
38
- */
39
- export declare function signTestUserToken(payload: Record<string, unknown>, options?: {
40
- key: TestSigningKey;
41
- expiresIn?: number;
42
- issuer?: string;
43
- audience?: string;
44
- kid?: string;
45
- notBefore?: number;
46
- }): string;
47
- /** The claim block a USER principal must carry to satisfy `hasValidIdentityClaims`. */
48
- export declare function testUserIdentityClaims(): Record<string, unknown>;
49
- /** Handle over the installed test JWKS — lets a test rotate keys and count fetches. */
50
- export interface TestJwksHandle {
51
- /** Keys currently published. */
52
- keys: TestSigningKey[];
53
- /** The first published key — what `signTestUserToken` uses by default. */
54
- primary: TestSigningKey;
55
- /** How many times the verifier has fetched the document. */
56
- fetchCount(): number;
57
- /** Make the next fetch(es) fail, to exercise the negative cache / fail-closed paths. */
58
- failNextFetches(count: number): void;
59
- /** Replace the published set (a rotation), leaving the cache to notice. */
60
- publish(keys: TestSigningKey[]): void;
61
- /** The document as the verifier sees it. */
62
- document(): JwksDocument;
63
- }
64
- /**
65
- * Install a JWKS cache backed by freshly generated keys (or `keys` when given)
66
- * as the process-wide one, and return a handle over it. Call in `beforeEach`;
67
- * `uninstallTestJwks()` restores the real HTTP-backed cache.
68
- */
69
- export declare function installTestJwks(keys?: TestSigningKey[], cacheOptions?: {
70
- refreshIntervalMs?: number;
71
- negativeTtlMs?: number;
72
- unknownKidCooldownMs?: number;
73
- }): TestJwksHandle;
74
- /** Restore the real (HTTP-backed) JWKS cache. */
75
- export declare function uninstallTestJwks(): void;
@@ -1,115 +0,0 @@
1
- // Copyright 2026 Pipeline Builder Contributors
2
- // SPDX-License-Identifier: Apache-2.0
3
- /**
4
- * Test helpers for the ES256 user-token chain.
5
- *
6
- * Before #5 every suite that needed an authenticated request just signed a JWT
7
- * with a literal `JWT_SECRET` — one line, and seventeen independent copies of
8
- * it. User tokens are asymmetric now, which needs a keypair, a `kid` and a JWKS
9
- * the verifier can fetch, so that per-suite copy-paste would be ~15 lines each.
10
- * Hence one shared seam, in api-core next to the route-coverage helper, imported
11
- * by the suites of every package:
12
- *
13
- * ```ts
14
- * import { installTestJwks, signTestUserToken } from '@pipeline-builder/api-core/lib/testing/user-tokens.js';
15
- *
16
- * const jwks = installTestJwks();
17
- * const token = signTestUserToken({ sub: 'u1', role: 'member', ... });
18
- * ```
19
- *
20
- * `installTestJwks` replaces the process-wide JWKS cache with one backed by the
21
- * generated keys — no HTTP, and it counts fetches so a test can assert the
22
- * refresh / unknown-`kid` / negative-cache behaviour.
23
- */
24
- import crypto, {} from 'crypto';
25
- import { JwksCache, setPlatformJwksCache } from '../services/jwks-cache.js';
26
- import { USER_TOKEN_ALGORITHM, publicJwkFrom } from '../utils/jwk.js';
27
- /** Generate one EC P-256 signing key, `kid` = its RFC 7638 thumbprint. */
28
- export function generateTestSigningKey() {
29
- const { privateKey, publicKey } = crypto.generateKeyPairSync('ec', { namedCurve: 'prime256v1' });
30
- const jwk = publicJwkFrom(publicKey);
31
- return { kid: jwk.kid, privateKey, publicKey, jwk };
32
- }
33
- /**
34
- * Sign `payload` as an ES256 JWT with `key`. `expiresIn` seconds (default 300).
35
- * Synchronous — `crypto.sign` is, and platform's async signer only exists
36
- * because KMS is a network call — so a suite can mint a token inline wherever it
37
- * used to call `jwt.sign`.
38
- */
39
- export function signTestUserToken(payload, options = {
40
- key: defaultKey(),
41
- }) {
42
- const key = options.key ?? defaultKey();
43
- const now = Math.floor(Date.now() / 1000);
44
- const body = {
45
- ...payload,
46
- iat: payload.iat ?? now,
47
- exp: payload.exp ?? now + (options.expiresIn ?? 300),
48
- ...(options.notBefore !== undefined ? { nbf: now + options.notBefore } : {}),
49
- ...(options.issuer ? { iss: options.issuer } : {}),
50
- ...(options.audience ? { aud: options.audience } : {}),
51
- };
52
- const b64 = (value) => Buffer.from(JSON.stringify(value), 'utf-8').toString('base64url');
53
- const signingInput = `${b64({ alg: USER_TOKEN_ALGORITHM, typ: 'JWT', kid: options.kid ?? key.kid })}.${b64(body)}`;
54
- const signature = crypto.sign('sha256', Buffer.from(signingInput, 'utf-8'), {
55
- key: key.privateKey,
56
- dsaEncoding: 'ieee-p1363',
57
- });
58
- return `${signingInput}.${signature.toString('base64url')}`;
59
- }
60
- /** The claim block a USER principal must carry to satisfy `hasValidIdentityClaims`. */
61
- export function testUserIdentityClaims() {
62
- return {
63
- type: 'access',
64
- principalType: 'user',
65
- token_use: 'access',
66
- amr: ['pwd'],
67
- aal: 1,
68
- auth_time: Math.floor(Date.now() / 1000),
69
- };
70
- }
71
- let installed;
72
- function defaultKey() {
73
- if (!installed)
74
- throw new Error('installTestJwks() must be called before signing a test user token');
75
- return installed.primary;
76
- }
77
- /**
78
- * Install a JWKS cache backed by freshly generated keys (or `keys` when given)
79
- * as the process-wide one, and return a handle over it. Call in `beforeEach`;
80
- * `uninstallTestJwks()` restores the real HTTP-backed cache.
81
- */
82
- export function installTestJwks(keys = [generateTestSigningKey()], cacheOptions = {}) {
83
- let published = keys;
84
- let fetches = 0;
85
- let failures = 0;
86
- const handle = {
87
- get keys() { return published; },
88
- get primary() { return published[0]; },
89
- fetchCount: () => fetches,
90
- failNextFetches: (count) => { failures = count; },
91
- publish: (next) => { published = next; },
92
- document: () => ({ keys: published.map((k) => k.jwk) }),
93
- };
94
- const cache = new JwksCache({
95
- source: 'test',
96
- fetch: async () => {
97
- fetches += 1;
98
- if (failures > 0) {
99
- failures -= 1;
100
- throw new Error('test JWKS fetch failure');
101
- }
102
- return handle.document();
103
- },
104
- ...cacheOptions,
105
- });
106
- setPlatformJwksCache(cache);
107
- installed = handle;
108
- return handle;
109
- }
110
- /** Restore the real (HTTP-backed) JWKS cache. */
111
- export function uninstallTestJwks() {
112
- setPlatformJwksCache(undefined);
113
- installed = undefined;
114
- }
115
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXNlci10b2tlbnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdGVzdGluZy91c2VyLXRva2Vucy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDOzs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQW9CRztBQUVILE9BQU8sTUFBTSxFQUFFLEVBQWtCLE1BQU0sUUFBUSxDQUFDO0FBQ2hELE9BQU8sRUFBRSxTQUFTLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUM1RSxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsYUFBYSxFQUFxQyxNQUFNLGlCQUFpQixDQUFDO0FBVXpHLDBFQUEwRTtBQUMxRSxNQUFNLFVBQVUsc0JBQXNCO0lBQ3BDLE1BQU0sRUFBRSxVQUFVLEVBQUUsU0FBUyxFQUFFLEdBQUcsTUFBTSxDQUFDLG1CQUFtQixDQUFDLElBQUksRUFBRSxFQUFFLFVBQVUsRUFBRSxZQUFZLEVBQUUsQ0FBQyxDQUFDO0lBQ2pHLE1BQU0sR0FBRyxHQUFHLGFBQWEsQ0FBQyxTQUFTLENBQUMsQ0FBQztJQUNyQyxPQUFPLEVBQUUsR0FBRyxFQUFFLEdBQUcsQ0FBQyxHQUFHLEVBQUUsVUFBVSxFQUFFLFNBQVMsRUFBRSxHQUFHLEVBQUUsQ0FBQztBQUN0RCxDQUFDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxNQUFNLFVBQVUsaUJBQWlCLENBQy9CLE9BQWdDLEVBQ2hDLE9BQU8sR0FBc0g7SUFDM0gsR0FBRyxFQUFFLFVBQVUsRUFBRTtDQUNsQjtJQUVELE1BQU0sR0FBRyxHQUFHLE9BQU8sQ0FBQyxHQUFHLElBQUksVUFBVSxFQUFFLENBQUM7SUFDeEMsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFDLENBQUM7SUFDMUMsTUFBTSxJQUFJLEdBQTRCO1FBQ3BDLEdBQUcsT0FBTztRQUNWLEdBQUcsRUFBRSxP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RyxLQUFLLENBQUMsQ0FBQyxDQUFDO1FBQ3pELE9BQU8sRUFBRSxDQUFDLElBQXNCLEVBQUUsRUFBRSxHQUFHLFNBQVMsR0FBRyxJQUFJLENBQUMsQ0FBQyxDQUFDO1FBQzFELFFBQVEsRUFBRSxHQUFHLEVBQUUsQ0FBQyxDQUFDLEVBQUUsSUFBSSxFQUFFLFNBQVMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDO0tBQ3hELENBQUM7SUFFRixNQUFNLEtBQUssR0FBRyxJQUFJLFNBQVMsQ0FBQztRQUMxQixNQUFNLEVBQUUsTUFBTTtRQUNkLEtBQUssRUFBRSxLQUFLLElBQUksRUFBRTtZQUNoQixPQUFPLElBQUksQ0FBQyxDQUFDO1lBQ2IsSUFBSSxRQUFRLEdBQUcsQ0FBQyxFQUFFLENBQUM7Z0JBQ2pCLFFBQVEsSUFBSSxDQUFDLENBQUM7Z0JBQ2QsTUFBTSxJQUFJLEtBQUssQ0FBQyx5QkFBeUIsQ0FBQyxDQUFDO1lBQzdDLENBQUM7WUFDRCxPQUFPLE1BQU0sQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUMzQixDQUFDO1FBQ0QsR0FBRyxZQUFZO0tBQ2hCLENBQUMsQ0FBQztJQUNILG9CQUFvQixDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQzVCLFNBQVMsR0FBRyxNQUFNLENBQUM7SUFDbkIsT0FBTyxNQUFNLENBQUM7QUFDaEIsQ0FBQztBQUVELGlEQUFpRDtBQUNqRCxNQUFNLFVBQVUsaUJBQWlCO0lBQy9CLG9CQUFvQixDQUFDLFNBQVMsQ0FBQyxDQUFDO0lBQ2hDLFNBQVMsR0FBRyxTQUFTLENBQUM7QUFDeEIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIENvcHlyaWdodCAyMDI2IFBpcGVsaW5lIEJ1aWxkZXIgQ29udHJpYnV0b3JzXG4vLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuXG4vKipcbiAqIFRlc3QgaGVscGVycyBmb3IgdGhlIEVTMjU2IHVzZXItdG9rZW4gY2hhaW4uXG4gKlxuICogQmVmb3JlICM1IGV2ZXJ5IHN1aXRlIHRoYXQgbmVlZGVkIGFuIGF1dGhlbnRpY2F0ZWQgcmVxdWVzdCBqdXN0IHNpZ25lZCBhIEpXVFxuICogd2l0aCBhIGxpdGVyYWwgYEpXVF9TRUNSRVRgIOKAlCBvbmUgbGluZSwgYW5kIHNldmVudGVlbiBpbmRlcGVuZGVudCBjb3BpZXMgb2ZcbiAqIGl0LiBVc2VyIHRva2VucyBhcmUgYXN5bW1ldHJpYyBub3csIHdoaWNoIG5lZWRzIGEga2V5cGFpciwgYSBga2lkYCBhbmQgYSBKV0tTXG4gKiB0aGUgdmVyaWZpZXIgY2FuIGZldGNoLCBzbyB0aGF0IHBlci1zdWl0ZSBjb3B5LXBhc3RlIHdvdWxkIGJlIH4xNSBsaW5lcyBlYWNoLlxuICogSGVuY2Ugb25lIHNoYXJlZCBzZWFtLCBpbiBhcGktY29yZSBuZXh0IHRvIHRoZSByb3V0ZS1jb3ZlcmFnZSBoZWxwZXIsIGltcG9ydGVkXG4gKiBieSB0aGUgc3VpdGVzIG9mIGV2ZXJ5IHBhY2thZ2U6XG4gKlxuICogYGBgdHNcbiAqIGltcG9ydCB7IGluc3RhbGxUZXN0Sndrcywgc2lnblRlc3RVc2VyVG9rZW4gfSBmcm9tICdAcGlwZWxpbmUtYnVpbGRlci9hcGktY29yZS9saWIvdGVzdGluZy91c2VyLXRva2Vucy5qcyc7XG4gKlxuICogY29uc3QgandrcyA9IGluc3RhbGxUZXN0SndrcygpO1xuICogY29uc3QgdG9rZW4gPSBzaWduVGVzdFVzZXJUb2tlbih7IHN1YjogJ3UxJywgcm9sZTogJ21lbWJlcicsIC4uLiB9KTtcbiAqIGBgYFxuICpcbiAqIGBpbnN0YWxsVGVzdEp3a3NgIHJlcGxhY2VzIHRoZSBwcm9jZXNzLXdpZGUgSldLUyBjYWNoZSB3aXRoIG9uZSBiYWNrZWQgYnkgdGhlXG4gKiBnZW5lcmF0ZWQga2V5cyDigJQgbm8gSFRUUCwgYW5kIGl0IGNvdW50cyBmZXRjaGVzIHNvIGEgdGVzdCBjYW4gYXNzZXJ0IHRoZVxuICogcmVmcmVzaCAvIHVua25vd24tYGtpZGAgLyBuZWdhdGl2ZS1jYWNoZSBiZWhhdmlvdXIuXG4gKi9cblxuaW1wb3J0IGNyeXB0bywgeyB0eXBlIEtleU9iamVjdCB9IGZyb20gJ2NyeXB0byc7XG5pbXBvcnQgeyBKd2tzQ2FjaGUsIHNldFBsYXRmb3JtSndrc0NhY2hlIH0gZnJvbSAnLi4vc2VydmljZXMvandrcy1jYWNoZS5qcyc7XG5pbXBvcnQgeyBVU0VSX1RPS0VOX0FMR09SSVRITSwgcHVibGljSndrRnJvbSwgdHlwZSBKd2tzRG9jdW1lbnQsIHR5cGUgUHVibGljSndrIH0gZnJvbSAnLi4vdXRpbHMvandrLmpzJztcblxuLyoqIEEgZ2VuZXJhdGVkIHNpZ25pbmcga2V5LCBpbiBldmVyeSBmb3JtIHRoZSB0ZXN0cyBuZWVkLiAqL1xuZXhwb3J0IGludGVyZmFjZSBUZXN0U2lnbmluZ0tleSB7XG4gIGtpZDogc3RyaW5nO1xuICBwcml2YXRlS2V5OiBLZXlPYmplY3Q7XG4gIHB1YmxpY0tleTogS2V5T2JqZWN0O1xuICBqd2s6IFB1YmxpY0p3aztcbn1cblxuLyoqIEdlbmVyYXRlIG9uZSBFQyBQLTI1NiBzaWduaW5nIGtleSwgYGtpZGAgPSBpdHMgUkZDIDc2MzggdGh1bWJwcmludC4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZW5lcmF0ZVRlc3RTaWduaW5nS2V5KCk6IFRlc3RTaWduaW5nS2V5IHtcbiAgY29uc3QgeyBwcml2YXRlS2V5LCBwdWJsaWNLZXkgfSA9IGNyeXB0by5nZW5lcmF0ZUtleVBhaXJTeW5jKCdlYycsIHsgbmFtZWRDdXJ2ZTogJ3ByaW1lMjU2djEnIH0pO1xuICBjb25zdCBqd2sgPSBwdWJsaWNKd2tGcm9tKHB1YmxpY0tleSk7XG4gIHJldHVybiB7IGtpZDogandrLmtpZCwgcHJpdmF0ZUtleSwgcHVibGljS2V5LCBqd2sgfTtcbn1cblxuLyoqXG4gKiBTaWduIGBwYXlsb2FkYCBhcyBhbiBFUzI1NiBKV1Qgd2l0aCBga2V5YC4gYGV4cGlyZXNJbmAgc2Vjb25kcyAoZGVmYXVsdCAzMDApLlxuICogU3luY2hyb25vdXMg4oCUIGBjcnlwdG8uc2lnbmAgaXMsIGFuZCBwbGF0Zm9ybSdzIGFzeW5jIHNpZ25lciBvbmx5IGV4aXN0c1xuICogYmVjYXVzZSBLTVMgaXMgYSBuZXR3b3JrIGNhbGwg4oCUIHNvIGEgc3VpdGUgY2FuIG1pbnQgYSB0b2tlbiBpbmxpbmUgd2hlcmV2ZXIgaXRcbiAqIHVzZWQgdG8gY2FsbCBgand0LnNpZ25gLlxuICovXG5leHBvcnQgZnVuY3Rpb24gc2lnblRlc3RVc2VyVG9rZW4oXG4gIHBheWxvYWQ6IFJlY29yZDxzdHJpbmcsIHVua25vd24+LFxuICBvcHRpb25zOiB7IGtleTogVGVzdFNpZ25pbmdLZXk7IGV4cGlyZXNJbj86IG51bWJlcjsgaXNzdWVyPzogc3RyaW5nOyBhdWRpZW5jZT86IHN0cmluZzsga2lkPzogc3RyaW5nOyBub3RCZWZvcmU/OiBudW1iZXIgfSA9IHtcbiAgICBrZXk6IGRlZmF1bHRLZXkoKSxcbiAgfSxcbik6IHN0cmluZyB7XG4gIGNvbnN0IGtleSA9IG9wdGlvbnMua2V5ID8/IGRlZmF1bHRLZXkoKTtcbiAgY29uc3Qgbm93ID0gTWF0aC5mbG9vcihEYXRlLm5vdygpIC8gMTAwMCk7XG4gIGNvbnN0IGJvZHk6IFJlY29yZDxzdHJpbmcsIHVua25vd24+ID0ge1xuICAgIC4uLnBheWxvYWQsXG4gICAgaWF0OiBwYXlsb2FkLmlhdCA/PyBub3csXG4gICAgZXhwOiBwYXlsb2FkLmV4cCA/PyBub3cgKyAob3B0aW9ucy5leHBpcmVzSW4gPz8gMzAwKSxcbiAgICAuLi4ob3B0aW9ucy5ub3RCZWZvcmUgIT09IHVuZGVmaW5lZCA/IHsgbmJmOiBub3cgKyBvcHRpb25zLm5vdEJlZm9yZSB9IDoge30pLFxuICAgIC4uLihvcHRpb25zLmlzc3VlciA/IHsgaXNzOiBvcHRpb25zLmlzc3VlciB9IDoge30pLFxuICAgIC4uLihvcHRpb25zLmF1ZGllbmNlID8geyBhdWQ6IG9wdGlvbnMuYXVkaWVuY2UgfSA6IHt9KSxcbiAgfTtcbiAgY29uc3QgYjY0ID0gKHZhbHVlOiB1bmtub3duKSA9PiBCdWZmZXIuZnJvbShKU09OLnN0cmluZ2lmeSh2YWx1ZSksICd1dGYtOCcpLnRvU3RyaW5nKCdiYXNlNjR1cmwnKTtcbiAgY29uc3Qgc2lnbmluZ0lucHV0ID0gYCR7YjY0KHsgYWxnOiBVU0VSX1RPS0VOX0FMR09SSVRITSwgdHlwOiAnSldUJywga2lkOiBvcHRpb25zLmtpZCA/PyBrZXkua2lkIH0pfS4ke2I2NChib2R5KX1gO1xuICBjb25zdCBzaWduYXR1cmUgPSBjcnlwdG8uc2lnbignc2hhMjU2JywgQnVmZmVyLmZyb20oc2lnbmluZ0lucHV0LCAndXRmLTgnKSwge1xuICAgIGtleToga2V5LnByaXZhdGVLZXksXG4gICAgZHNhRW5jb2Rpbmc6ICdpZWVlLXAxMzYzJyxcbiAgfSk7XG4gIHJldHVybiBgJHtzaWduaW5nSW5wdXR9LiR7c2lnbmF0dXJlLnRvU3RyaW5nKCdiYXNlNjR1cmwnKX1gO1xufVxuXG4vKiogVGhlIGNsYWltIGJsb2NrIGEgVVNFUiBwcmluY2lwYWwgbXVzdCBjYXJyeSB0byBzYXRpc2Z5IGBoYXNWYWxpZElkZW50aXR5Q2xhaW1zYC4gKi9cbmV4cG9ydCBmdW5jdGlvbiB0ZXN0VXNlcklkZW50aXR5Q2xhaW1zKCk6IFJlY29yZDxzdHJpbmcsIHVua25vd24+IHtcbiAgcmV0dXJuIHtcbiAgICB0eXBlOiAnYWNjZXNzJyxcbiAgICBwcmluY2lwYWxUeXBlOiAndXNlcicsXG4gICAgdG9rZW5fdXNlOiAnYWNjZXNzJyxcbiAgICBhbXI6IFsncHdkJ10sXG4gICAgYWFsOiAxLFxuICAgIGF1dGhfdGltZTogTWF0aC5mbG9vcihEYXRlLm5vdygpIC8gMTAwMCksXG4gIH07XG59XG5cbi8qKiBIYW5kbGUgb3ZlciB0aGUgaW5zdGFsbGVkIHRlc3QgSldLUyDigJQgbGV0cyBhIHRlc3Qgcm90YXRlIGtleXMgYW5kIGNvdW50IGZldGNoZXMuICovXG5leHBvcnQgaW50ZXJmYWNlIFRlc3RKd2tzSGFuZGxlIHtcbiAgLyoqIEtleXMgY3VycmVudGx5IHB1Ymxpc2hlZC4gKi9cbiAga2V5czogVGVzdFNpZ25pbmdLZXlbXTtcbiAgLyoqIFRoZSBmaXJzdCBwdWJsaXNoZWQga2V5IOKAlCB3aGF0IGBzaWduVGVzdFVzZXJUb2tlbmAgdXNlcyBieSBkZWZhdWx0LiAqL1xuICBwcmltYXJ5OiBUZXN0U2lnbmluZ0tleTtcbiAgLyoqIEhvdyBtYW55IHRpbWVzIHRoZSB2ZXJpZmllciBoYXMgZmV0Y2hlZCB0aGUgZG9jdW1lbnQuICovXG4gIGZldGNoQ291bnQoKTogbnVtYmVyO1xuICAvKiogTWFrZSB0aGUgbmV4dCBmZXRjaChlcykgZmFpbCwgdG8gZXhlcmNpc2UgdGhlIG5lZ2F0aXZlIGNhY2hlIC8gZmFpbC1jbG9zZWQgcGF0aHMuICovXG4gIGZhaWxOZXh0RmV0Y2hlcyhjb3VudDogbnVtYmVyKTogdm9pZDtcbiAgLyoqIFJlcGxhY2UgdGhlIHB1Ymxpc2hlZCBzZXQgKGEgcm90YXRpb24pLCBsZWF2aW5nIHRoZSBjYWNoZSB0byBub3RpY2UuICovXG4gIHB1Ymxpc2goa2V5czogVGVzdFNpZ25pbmdLZXlbXSk6IHZvaWQ7XG4gIC8qKiBUaGUgZG9jdW1lbnQgYXMgdGhlIHZlcmlmaWVyIHNlZXMgaXQuICovXG4gIGRvY3VtZW50KCk6IEp3a3NEb2N1bWVudDtcbn1cblxubGV0IGluc3RhbGxlZDogVGVzdEp3a3NIYW5kbGUgfCB1bmRlZmluZWQ7XG5cbmZ1bmN0aW9uIGRlZmF1bHRLZXkoKTogVGVzdFNpZ25pbmdLZXkge1xuICBpZiAoIWluc3RhbGxlZCkgdGhyb3cgbmV3IEVycm9yKCdpbnN0YWxsVGVzdEp3a3MoKSBtdXN0IGJlIGNhbGxlZCBiZWZvcmUgc2lnbmluZyBhIHRlc3QgdXNlciB0b2tlbicpO1xuICByZXR1cm4gaW5zdGFsbGVkLnByaW1hcnk7XG59XG5cbi8qKlxuICogSW5zdGFsbCBhIEpXS1MgY2FjaGUgYmFja2VkIGJ5IGZyZXNobHkgZ2VuZXJhdGVkIGtleXMgKG9yIGBrZXlzYCB3aGVuIGdpdmVuKVxuICogYXMgdGhlIHByb2Nlc3Mtd2lkZSBvbmUsIGFuZCByZXR1cm4gYSBoYW5kbGUgb3ZlciBpdC4gQ2FsbCBpbiBgYmVmb3JlRWFjaGA7XG4gKiBgdW5pbnN0YWxsVGVzdEp3a3MoKWAgcmVzdG9yZXMgdGhlIHJlYWwgSFRUUC1iYWNrZWQgY2FjaGUuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpbnN0YWxsVGVzdEp3a3MoXG4gIGtleXM6IFRlc3RTaWduaW5nS2V5W10gPSBbZ2VuZXJhdGVUZXN0U2lnbmluZ0tleSgpXSxcbiAgY2FjaGVPcHRpb25zOiB7IHJlZnJlc2hJbnRlcnZhbE1zPzogbnVtYmVyOyBuZWdhdGl2ZVR0bE1zPzogbnVtYmVyOyB1bmtub3duS2lkQ29vbGRvd25Ncz86IG51bWJlciB9ID0ge30sXG4pOiBUZXN0Sndrc0hhbmRsZSB7XG4gIGxldCBwdWJsaXNoZWQgPSBrZXlzO1xuICBsZXQgZmV0Y2hlcyA9IDA7XG4gIGxldCBmYWlsdXJlcyA9IDA7XG5cbiAgY29uc3QgaGFuZGxlOiBUZXN0Sndrc0hhbmRsZSA9IHtcbiAgICBnZXQga2V5cygpIHsgcmV0dXJuIHB1Ymxpc2hlZDsgfSxcbiAgICBnZXQgcHJpbWFyeSgpIHsgcmV0dXJuIHB1Ymxpc2hlZFswXTsgfSxcbiAgICBmZXRjaENvdW50OiAoKSA9PiBmZXRjaGVzLFxuICAgIGZhaWxOZXh0RmV0Y2hlczogKGNvdW50OiBudW1iZXIpID0+IHsgZmFpbHVyZXMgPSBjb3VudDsgfSxcbiAgICBwdWJsaXNoOiAobmV4dDogVGVzdFNpZ25pbmdLZXlbXSkgPT4geyBwdWJsaXNoZWQgPSBuZXh0OyB9LFxuICAgIGRvY3VtZW50OiAoKSA9PiAoeyBrZXlzOiBwdWJsaXNoZWQubWFwKChrKSA9PiBrLmp3aykgfSksXG4gIH07XG5cbiAgY29uc3QgY2FjaGUgPSBuZXcgSndrc0NhY2hlKHtcbiAgICBzb3VyY2U6ICd0ZXN0JyxcbiAgICBmZXRjaDogYXN5bmMgKCkgPT4ge1xuICAgICAgZmV0Y2hlcyArPSAxO1xuICAgICAgaWYgKGZhaWx1cmVzID4gMCkge1xuICAgICAgICBmYWlsdXJlcyAtPSAxO1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ3Rlc3QgSldLUyBmZXRjaCBmYWlsdXJlJyk7XG4gICAgICB9XG4gICAgICByZXR1cm4gaGFuZGxlLmRvY3VtZW50KCk7XG4gICAgfSxcbiAgICAuLi5jYWNoZU9wdGlvbnMsXG4gIH0pO1xuICBzZXRQbGF0Zm9ybUp3a3NDYWNoZShjYWNoZSk7XG4gIGluc3RhbGxlZCA9IGhhbmRsZTtcbiAgcmV0dXJuIGhhbmRsZTtcbn1cblxuLyoqIFJlc3RvcmUgdGhlIHJlYWwgKEhUVFAtYmFja2VkKSBKV0tTIGNhY2hlLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHVuaW5zdGFsbFRlc3RKd2tzKCk6IHZvaWQge1xuICBzZXRQbGF0Zm9ybUp3a3NDYWNoZSh1bmRlZmluZWQpO1xuICBpbnN0YWxsZWQgPSB1bmRlZmluZWQ7XG59XG4iXX0=