@pipeline-builder/api-core 4.1.12 → 4.1.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. package/README.md +3 -2
  2. package/lib/constants/index.d.ts +0 -1
  3. package/lib/constants/index.js +1 -2
  4. package/lib/errors/app-errors.d.ts +8 -3
  5. package/lib/errors/app-errors.js +10 -5
  6. package/lib/helpers/access-helpers.d.ts +4 -6
  7. package/lib/helpers/access-helpers.js +6 -8
  8. package/lib/helpers/index.d.ts +3 -3
  9. package/lib/helpers/index.js +4 -4
  10. package/lib/helpers/org-hierarchy-http.d.ts +11 -10
  11. package/lib/helpers/org-hierarchy-http.js +24 -35
  12. package/lib/helpers/org-hierarchy.d.ts +37 -3
  13. package/lib/helpers/org-hierarchy.js +45 -4
  14. package/lib/helpers/restore-helpers.d.ts +44 -24
  15. package/lib/helpers/restore-helpers.js +41 -52
  16. package/lib/helpers/sse-helpers.d.ts +29 -7
  17. package/lib/helpers/sse-helpers.js +36 -9
  18. package/lib/index.d.ts +16 -14
  19. package/lib/index.js +17 -15
  20. package/lib/middleware/assurance.d.ts +116 -0
  21. package/lib/middleware/assurance.js +176 -0
  22. package/lib/middleware/auth.d.ts +5 -427
  23. package/lib/middleware/auth.js +36 -789
  24. package/lib/middleware/ecosystem-guard.d.ts +47 -0
  25. package/lib/middleware/ecosystem-guard.js +60 -0
  26. package/lib/middleware/index.d.ts +11 -4
  27. package/lib/middleware/index.js +12 -5
  28. package/lib/middleware/jwt-verify.d.ts +52 -0
  29. package/lib/middleware/jwt-verify.js +105 -0
  30. package/lib/middleware/permission-gates.d.ts +133 -0
  31. package/lib/middleware/permission-gates.js +260 -0
  32. package/lib/middleware/revocation.d.ts +96 -0
  33. package/lib/middleware/revocation.js +162 -0
  34. package/lib/middleware/route-table.d.ts +11 -0
  35. package/lib/middleware/route-table.js +4 -1
  36. package/lib/middleware/service-tokens.d.ts +83 -0
  37. package/lib/middleware/service-tokens.js +158 -0
  38. package/lib/middleware/step-up.d.ts +2 -2
  39. package/lib/middleware/step-up.js +8 -7
  40. package/lib/middleware/system-org.d.ts +20 -0
  41. package/lib/middleware/system-org.js +25 -0
  42. package/lib/openapi/routes/plugin-routes.js +133 -10
  43. package/lib/openapi/schema-registry.js +2 -5
  44. package/lib/routes/index.d.ts +1 -1
  45. package/lib/routes/index.js +2 -2
  46. package/lib/services/api-key-exchange.js +7 -5
  47. package/lib/services/audit-spool.d.ts +34 -10
  48. package/lib/services/audit-spool.js +75 -34
  49. package/lib/services/cache-service.d.ts +7 -0
  50. package/lib/services/cache-service.js +1 -1
  51. package/lib/services/circuit-breaker.d.ts +14 -1
  52. package/lib/services/circuit-breaker.js +36 -10
  53. package/lib/services/compliance-client.d.ts +10 -2
  54. package/lib/services/compliance-client.js +17 -10
  55. package/lib/services/compliance-event-subscriber.js +6 -6
  56. package/lib/services/durable-event-bus.js +8 -11
  57. package/lib/services/ecosystem-notify-client.d.ts +32 -0
  58. package/lib/services/ecosystem-notify-client.js +71 -0
  59. package/lib/services/env-redis.d.ts +4 -1
  60. package/lib/services/env-redis.js +32 -3
  61. package/lib/services/http-client.d.ts +19 -0
  62. package/lib/services/http-client.js +98 -19
  63. package/lib/services/index.d.ts +25 -24
  64. package/lib/services/index.js +25 -22
  65. package/lib/services/jwks-cache.js +5 -4
  66. package/lib/services/leader-lock.d.ts +22 -5
  67. package/lib/services/leader-lock.js +61 -6
  68. package/lib/services/quota.d.ts +9 -4
  69. package/lib/services/quota.js +34 -40
  70. package/lib/services/remote-audit-client.d.ts +28 -32
  71. package/lib/services/remote-audit-client.js +166 -49
  72. package/lib/services/retry-forever.js +5 -11
  73. package/lib/services/retry-strategy.d.ts +1 -1
  74. package/lib/services/retry-strategy.js +5 -4
  75. package/lib/services/scheduler.d.ts +8 -3
  76. package/lib/services/scheduler.js +17 -5
  77. package/lib/services/service-boot.d.ts +14 -13
  78. package/lib/services/service-boot.js +18 -15
  79. package/lib/services/service-keys.d.ts +1 -1
  80. package/lib/services/service-keys.js +12 -18
  81. package/lib/services/sse-ticket-store.d.ts +9 -1
  82. package/lib/services/sse-ticket-store.js +24 -9
  83. package/lib/services/system-notification.d.ts +27 -0
  84. package/lib/services/system-notification.js +50 -0
  85. package/lib/services/token-revocation.d.ts +50 -1
  86. package/lib/services/token-revocation.js +103 -3
  87. package/lib/types/audit-events.d.ts +6 -6
  88. package/lib/types/audit-events.js +1 -1
  89. package/lib/types/common.d.ts +32 -13
  90. package/lib/types/common.js +2 -1
  91. package/lib/types/ecosystem-notifications.d.ts +142 -0
  92. package/lib/types/ecosystem-notifications.js +210 -0
  93. package/lib/types/ecosystem.d.ts +77 -0
  94. package/lib/types/ecosystem.js +119 -0
  95. package/lib/types/error-codes.d.ts +81 -0
  96. package/lib/types/error-codes.js +108 -1
  97. package/lib/types/feature-flags.d.ts +21 -1
  98. package/lib/types/feature-flags.js +36 -2
  99. package/lib/types/index.d.ts +14 -9
  100. package/lib/types/index.js +15 -10
  101. package/lib/types/permissions.d.ts +46 -8
  102. package/lib/types/permissions.js +75 -12
  103. package/lib/types/plugin-catalog.d.ts +35 -0
  104. package/lib/types/plugin-catalog.js +39 -0
  105. package/lib/types/plugin-health.d.ts +102 -0
  106. package/lib/types/plugin-health.js +184 -0
  107. package/lib/types/quota-tiers.d.ts +15 -0
  108. package/lib/types/quota-tiers.js +23 -3
  109. package/lib/types/wire-vocabulary.d.ts +102 -0
  110. package/lib/types/wire-vocabulary.js +94 -0
  111. package/lib/utils/alias-resolver.js +2 -2
  112. package/lib/utils/audit.d.ts +5 -2
  113. package/lib/utils/audit.js +6 -3
  114. package/lib/utils/compliance-attributes.js +3 -4
  115. package/lib/utils/concurrency.d.ts +7 -0
  116. package/lib/utils/concurrency.js +13 -1
  117. package/lib/utils/env.d.ts +3 -4
  118. package/lib/utils/env.js +4 -5
  119. package/lib/utils/identity.d.ts +13 -4
  120. package/lib/utils/identity.js +18 -10
  121. package/lib/utils/index.d.ts +13 -10
  122. package/lib/utils/index.js +13 -10
  123. package/lib/utils/jwk.d.ts +4 -0
  124. package/lib/utils/jwk.js +13 -1
  125. package/lib/utils/logger.d.ts +1 -1
  126. package/lib/utils/logger.js +1 -1
  127. package/lib/utils/object.d.ts +11 -0
  128. package/lib/utils/object.js +24 -1
  129. package/lib/utils/params.d.ts +8 -22
  130. package/lib/utils/params.js +15 -43
  131. package/lib/utils/plugin-compliance.d.ts +98 -0
  132. package/lib/utils/plugin-compliance.js +78 -0
  133. package/lib/utils/proof-of-work.d.ts +69 -0
  134. package/lib/utils/proof-of-work.js +139 -0
  135. package/lib/utils/response.d.ts +16 -6
  136. package/lib/utils/response.js +18 -15
  137. package/lib/utils/retention.d.ts +17 -0
  138. package/lib/utils/retention.js +30 -0
  139. package/lib/utils/secret-encryption.d.ts +7 -7
  140. package/lib/utils/secret-encryption.js +9 -10
  141. package/lib/utils/secret-rotation.js +2 -2
  142. package/lib/utils/sensitive-patterns.js +4 -5
  143. package/lib/utils/service-registry.d.ts +15 -0
  144. package/lib/utils/service-registry.js +24 -0
  145. package/lib/utils/ssrf.d.ts +11 -2
  146. package/lib/utils/ssrf.js +112 -33
  147. package/lib/validation/ai-schemas.js +3 -2
  148. package/lib/validation/common-schemas.d.ts +0 -9
  149. package/lib/validation/common-schemas.js +1 -7
  150. package/lib/validation/dockerfile-static.d.ts +26 -0
  151. package/lib/validation/dockerfile-static.js +210 -0
  152. package/lib/validation/index.d.ts +13 -4
  153. package/lib/validation/index.js +14 -5
  154. package/lib/validation/message-schemas.js +6 -4
  155. package/lib/validation/pipeline-schemas.d.ts +8 -2
  156. package/lib/validation/pipeline-schemas.js +30 -4
  157. package/lib/validation/plugin-base-images.d.ts +20 -0
  158. package/lib/validation/plugin-base-images.js +17 -0
  159. package/lib/validation/plugin-catalog-detect.d.ts +86 -0
  160. package/lib/validation/plugin-catalog-detect.js +237 -0
  161. package/lib/validation/plugin-catalog-metadata.d.ts +141 -0
  162. package/lib/validation/plugin-catalog-metadata.js +162 -0
  163. package/lib/validation/plugin-heuristics.d.ts +41 -0
  164. package/lib/validation/plugin-heuristics.js +211 -0
  165. package/lib/validation/plugin-lint.d.ts +18 -0
  166. package/lib/validation/plugin-lint.js +136 -0
  167. package/lib/validation/plugin-name-confusable.d.ts +15 -0
  168. package/lib/validation/plugin-name-confusable.js +81 -0
  169. package/lib/validation/plugin-schemas.d.ts +40 -26
  170. package/lib/validation/plugin-schemas.js +26 -23
  171. package/lib/validation/plugin-spec-schema.d.ts +112 -0
  172. package/lib/validation/plugin-spec-schema.js +163 -0
  173. package/lib/validation/plugin-template-contract.d.ts +64 -0
  174. package/lib/validation/plugin-template-contract.js +113 -0
  175. package/package.json +69 -4
  176. package/lib/testing/mock-api-core.d.ts +0 -150
  177. package/lib/testing/mock-api-core.js +0 -246
  178. package/lib/testing/mock-drizzle.d.ts +0 -6
  179. package/lib/testing/mock-drizzle.js +0 -57
  180. package/lib/testing/route-coverage.d.ts +0 -64
  181. package/lib/testing/route-coverage.js +0 -183
  182. package/lib/testing/service-tokens.d.ts +0 -66
  183. package/lib/testing/service-tokens.js +0 -137
  184. package/lib/testing/tier-mock.d.ts +0 -44
  185. package/lib/testing/tier-mock.js +0 -65
  186. package/lib/testing/user-tokens.d.ts +0 -75
  187. package/lib/testing/user-tokens.js +0 -115
@@ -0,0 +1,163 @@
1
+ // Copyright 2026 Pipeline Builder Contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * The plugin PACKAGE schemas: `plugin-spec.yaml` and `config.yaml`, exactly as
5
+ * the plugin service validates an upload. Shared from api-core so the upload
6
+ * path (api/plugin) and the CLI (`pipeline-manager plugin validate` /
7
+ * `plugin publish`) apply ONE schema and can't drift.
8
+ *
9
+ * Pure: no YAML parsing, no filesystem. Callers parse (with their own size and
10
+ * alias bounds) and hand the resulting object to {@link checkPluginSpec} /
11
+ * {@link checkPluginConfig}.
12
+ */
13
+ import { z } from 'zod';
14
+ import { PLUGIN_CHANGELOG_MAX_BYTES } from '../types/plugin-catalog.js';
15
+ import { IconKeySchema, PLUGIN_CATALOG_FIELD_SCHEMAS, ProjectUrlSchema, isAllowedSpdxId, } from './plugin-catalog-metadata.js';
16
+ // -----------------------------------------------------------------------------
17
+ // Vocabularies
18
+ // -----------------------------------------------------------------------------
19
+ export const PLUGIN_TYPES = ['CodeBuildStep', 'ShellStep', 'ManualApprovalStep'];
20
+ export const PLUGIN_COMPUTE_TYPES = ['SMALL', 'MEDIUM', 'LARGE', 'X2_LARGE'];
21
+ export const PLUGIN_FAILURE_BEHAVIORS = ['fail', 'warn', 'ignore'];
22
+ export const PLUGIN_BUILD_TYPES = ['build_image', 'prebuilt', 'metadata_only'];
23
+ /** Plugin name: lowercase letters, digits and hyphens. */
24
+ export const PLUGIN_NAME_PATTERN = /^[a-z0-9-]+$/;
25
+ /** Plugin version: semver, with optional `-prerelease` and `+build` metadata. */
26
+ export const PLUGIN_VERSION_PATTERN = /^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$/;
27
+ // -----------------------------------------------------------------------------
28
+ // network.egress (spec-only trust metadata)
29
+ // -----------------------------------------------------------------------------
30
+ /** Max declared `network.egress` hostnames. */
31
+ export const EGRESS_MAX_HOSTS = 50;
32
+ /**
33
+ * A bare DNS hostname, optionally with ONE leading `*.` wildcard label. No
34
+ * scheme, port, path, userinfo or IP literal — a declared egress target is a
35
+ * name the reviewer can read.
36
+ */
37
+ const EGRESS_HOST_PATTERN = /^(?:\*\.)?(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z][a-z0-9-]{0,61}[a-z0-9]$/;
38
+ /** True when `host` is an acceptable `network.egress` entry. */
39
+ export function isValidEgressHost(host) {
40
+ return host.length <= 253 && EGRESS_HOST_PATTERN.test(host);
41
+ }
42
+ // -----------------------------------------------------------------------------
43
+ // config.yaml
44
+ // -----------------------------------------------------------------------------
45
+ export const PluginConfigSchema = z.object({
46
+ pluginSpec: z.string().optional(),
47
+ dockerfile: z.string().optional(),
48
+ buildType: z.enum(PLUGIN_BUILD_TYPES).optional(),
49
+ // Deterministic build tag written by build-plugin-images.sh
50
+ // (`p-<name>-<sha256:12>`). Informational only on the upload path —
51
+ // the platform doesn't act on it, but it lives in the same config
52
+ // file as the rest of the build metadata so re-runs can short-circuit
53
+ // unchanged plugins without needing a separate .image-hash sidecar.
54
+ imageTag: z.string().optional(),
55
+ }).strict()
56
+ .refine(d => !(d.buildType === 'prebuilt' && d.dockerfile), {
57
+ message: 'dockerfile is not allowed when buildType is prebuilt',
58
+ })
59
+ .refine(d => !(d.buildType === 'metadata_only' && d.dockerfile), {
60
+ message: 'dockerfile is not allowed when buildType is metadata_only',
61
+ });
62
+ // -----------------------------------------------------------------------------
63
+ // plugin-spec.yaml
64
+ // -----------------------------------------------------------------------------
65
+ // name/version/commands are the required-field contract, but they're checked by
66
+ // {@link pluginSpecRequiredFieldsProblem} (the stable "name, version, and
67
+ // commands are required" message and the ManualApprovalStep carve-out). Here
68
+ // they're optional strings; the schema's job is to reject malformed *types* on
69
+ // the fields that flow downstream unchecked.
70
+ export const PluginSpecSchema = z.object({
71
+ name: z.string().optional(),
72
+ description: z.string().optional(),
73
+ keywords: z.array(z.string()).optional(),
74
+ category: z.string().optional(),
75
+ version: z.string().optional(),
76
+ pluginType: z.enum(PLUGIN_TYPES).optional(),
77
+ computeType: z.enum(PLUGIN_COMPUTE_TYPES).optional(),
78
+ // >= 0 minutes: ManualApprovalStep specs legitimately declare `timeout: 0`.
79
+ timeout: z.number().int().nonnegative().optional(),
80
+ failureBehavior: z.enum(PLUGIN_FAILURE_BEHAVIORS).optional(),
81
+ secrets: z.array(z.object({
82
+ name: z.string().min(1),
83
+ required: z.boolean(),
84
+ description: z.string().optional(),
85
+ })).optional(),
86
+ // Accept an explicit `primaryOutputDirectory: null` (a ManualApprovalStep /
87
+ // output-less step legitimately declares it) and normalize it to `undefined`.
88
+ // Without `.nullish()` a present YAML `null` fails the bare string check and
89
+ // 400s the whole upload.
90
+ primaryOutputDirectory: z.string().nullish().transform((v) => v ?? undefined),
91
+ metadata: z.record(z.string(), z.union([z.string(), z.number(), z.boolean()])).optional(),
92
+ dockerfile: z.string().optional(),
93
+ installCommands: z.array(z.string()).optional(),
94
+ commands: z.array(z.string()).optional(),
95
+ env: z.record(z.string(), z.string()).optional(),
96
+ buildArgs: z.record(z.string(), z.string()).optional(),
97
+ requiredMetadata: z.array(z.string()).optional(),
98
+ requiredVars: z.array(z.string()).optional(),
99
+ // Kept as free-string maps rather than a value enum: shipped specs use both
100
+ // `bool` and `boolean`, and the coercion-vs-declared-type consistency is
101
+ // enforced separately by the template contract check. The schema only
102
+ // guarantees they're string-valued records here.
103
+ metadataTypes: z.record(z.string(), z.string()).optional(),
104
+ varsTypes: z.record(z.string(), z.string()).optional(),
105
+ // A command the build tooling runs against the built image
106
+ // (build-plugin-images.sh, test-plugins.sh --build, `plugin test`).
107
+ smokeTest: z.string().optional(),
108
+ // Documentation + trust metadata. The README is NOT a spec field — it
109
+ // is README.md at the zip root.
110
+ license: z.string().refine(isAllowedSpdxId, {
111
+ message: 'must be a supported SPDX license identifier (e.g. Apache-2.0, MIT)',
112
+ }).optional(),
113
+ changelog: z.string().refine((v) => new TextEncoder().encode(v).length <= PLUGIN_CHANGELOG_MAX_BYTES, {
114
+ message: `must be at most ${PLUGIN_CHANGELOG_MAX_BYTES} bytes`,
115
+ }).optional(),
116
+ homepageUrl: ProjectUrlSchema.optional(),
117
+ sourceUrl: ProjectUrlSchema.optional(),
118
+ // Catalog metadata: the card one-liner and the docs link. Same
119
+ // validators as a value typed into the upload form.
120
+ summary: PLUGIN_CATALOG_FIELD_SCHEMAS.summary.optional(),
121
+ documentationUrl: ProjectUrlSchema.optional(),
122
+ icon: z.union([
123
+ IconKeySchema,
124
+ z.object({ key: IconKeySchema, badge: IconKeySchema.optional() }).strict(),
125
+ ]).optional(),
126
+ network: z.object({
127
+ egress: z.array(z.string().refine(isValidEgressHost, {
128
+ message: 'must be a bare hostname (no scheme, port or path; one leading *. allowed)',
129
+ })).max(EGRESS_MAX_HOSTS).optional(),
130
+ }).strict().optional(),
131
+ }).strict();
132
+ const isMapping = (raw) => typeof raw === 'object' && raw !== null && !Array.isArray(raw);
133
+ function checkAgainst(file, raw, schema, withPath) {
134
+ if (!isMapping(raw)) {
135
+ const message = `${file} must be a YAML mapping`;
136
+ return { ok: false, issues: [message], message };
137
+ }
138
+ const result = schema.safeParse(raw);
139
+ if (result.success)
140
+ return { ok: true, value: result.data };
141
+ const issues = result.error.issues.map(i => (withPath ? `${i.path.join('.') || '(root)'}: ${i.message}` : i.message));
142
+ return { ok: false, issues, message: `${file}: ${issues.join('; ')}` };
143
+ }
144
+ /** Check a parsed `plugin-spec.yaml` document against {@link PluginSpecSchema}. */
145
+ export function checkPluginSpec(raw) {
146
+ return checkAgainst('plugin-spec.yaml', raw, PluginSpecSchema, true);
147
+ }
148
+ /** Check a parsed `config.yaml` document against {@link PluginConfigSchema}. */
149
+ export function checkPluginConfig(raw) {
150
+ return checkAgainst('config.yaml', raw, PluginConfigSchema, false);
151
+ }
152
+ /**
153
+ * The required-field contract: `name` and `version` always, `commands` for
154
+ * every type except `ManualApprovalStep`. Returns the upload's message, or null.
155
+ */
156
+ export function pluginSpecRequiredFieldsProblem(spec) {
157
+ const isApprovalStep = spec.pluginType === 'ManualApprovalStep';
158
+ if (!spec.name || !spec.version || (!isApprovalStep && !spec.commands)) {
159
+ return 'Invalid spec: name, version, and commands are required';
160
+ }
161
+ return null;
162
+ }
163
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2luLXNwZWMtc2NoZW1hLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3ZhbGlkYXRpb24vcGx1Z2luLXNwZWMtc2NoZW1hLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUFFdEM7Ozs7Ozs7OztHQVNHO0FBRUgsT0FBTyxFQUFFLENBQUMsRUFBRSxNQUFNLEtBQUssQ0FBQztBQUV4QixPQUFPLEVBQUUsMEJBQTBCLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUN4RSxPQUFPLEVBQ0wsYUFBYSxFQUFFLDRCQUE0QixFQUFFLGdCQUFnQixFQUFFLGVBQWUsR0FDL0UsTUFBTSw4QkFBOEIsQ0FBQztBQUV0QyxnRkFBZ0Y7QUFDaEYsZUFBZTtBQUNmLGdGQUFnRjtBQUVoRixNQUFNLENBQUMsTUFBTSxZQUFZLEdBQUcsQ0FBQyxlQUFlLEVBQUUsV0FBVyxFQUFFLG9CQUFvQixDQUFVLENBQUM7QUFDMUYsTUFBTSxDQUFDLE1BQU0sb0JBQW9CLEdBQUcsQ0FBQyxPQUFPLEVBQUUsUUFBUSxFQUFFLE9BQU8sRUFBRSxVQUFVLENBQVUsQ0FBQztBQUN0RixNQUFNLENBQUMsTUFBTSx3QkFBd0IsR0FBRyxDQUFDLE1BQU0sRUFBRSxNQUFNLEVBQUUsUUFBUSxDQUFVLENBQUM7QUFDNUUsTUFBTSxDQUFDLE1BQU0sa0JBQWtCLEdBQUcsQ0FBQyxhQUFhLEVBQUUsVUFBVSxFQUFFLGVBQWUsQ0FBVSxDQUFDO0FBR3hGLDBEQUEwRDtBQUMxRCxNQUFNLENBQUMsTUFBTSxtQkFBbUIsR0FBRyxjQUFjLENBQUM7QUFDbEQsaUZBQWlGO0FBQ2pGLE1BQU0sQ0FBQyxNQUFNLHNCQUFzQixHQUFHLHNEQUFzRCxDQUFDO0FBRTdGLGdGQUFnRjtBQUNoRiw0Q0FBNEM7QUFDNUMsZ0ZBQWdGO0FBRWhGLCtDQUErQztBQUMvQyxNQUFNLENBQUMsTUFBTSxnQkFBZ0IsR0FBRyxFQUFFLENBQUM7QUFFbkM7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QUFJLENBQUM7QUFDZCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbi8qKlxuICogVGhlIHBsdWdpbiBQQUNLQUdFIHNjaGVtYXM6IGBwbHVnaW4tc3BlYy55YW1sYCBhbmQgYGNvbmZpZy55YW1sYCwgZXhhY3RseSBhc1xuICogdGhlIHBsdWdpbiBzZXJ2aWNlIHZhbGlkYXRlcyBhbiB1cGxvYWQuIFNoYXJlZCBmcm9tIGFwaS1jb3JlIHNvIHRoZSB1cGxvYWRcbiAqIHBhdGggKGFwaS9wbHVnaW4pIGFuZCB0aGUgQ0xJIChgcGlwZWxpbmUtbWFuYWdlciBwbHVnaW4gdmFsaWRhdGVgIC9cbiAqIGBwbHVnaW4gcHVibGlzaGApIGFwcGx5IE9ORSBzY2hlbWEgYW5kIGNhbid0IGRyaWZ0LlxuICpcbiAqIFB1cmU6IG5vIFlBTUwgcGFyc2luZywgbm8gZmlsZXN5c3RlbS4gQ2FsbGVycyBwYXJzZSAod2l0aCB0aGVpciBvd24gc2l6ZSBhbmRcbiAqIGFsaWFzIGJvdW5kcykgYW5kIGhhbmQgdGhlIHJlc3VsdGluZyBvYmplY3QgdG8ge0BsaW5rIGNoZWNrUGx1Z2luU3BlY30gL1xuICoge0BsaW5rIGNoZWNrUGx1Z2luQ29uZmlnfS5cbiAqL1xuXG5pbXBvcnQgeyB6IH0gZnJvbSAnem9kJztcblxuaW1wb3J0IHsgUExVR0lOX0NIQU5HRUxPR19NQVhfQllURVMgfSBmcm9tICcuLi90eXBlcy9wbHVnaW4tY2F0YWxvZy5qcyc7XG5pbXBvcnQge1xuICBJY29uS2V5U2NoZW1hLCBQTFVHSU5fQ0FUQUxPR19GSUVMRF9TQ0hFTUFTLCBQcm9qZWN0VXJsU2NoZW1hLCBpc0FsbG93ZWRTcGR4SWQsXG59IGZyb20gJy4vcGx1Z2luLWNhdGFsb2ctbWV0YWRhdGEuanMnO1xuXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuLy8gVm9jYWJ1bGFyaWVzXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG5leHBvcnQgY29uc3QgUExVR0lOX1RZUEVTID0gWydDb2RlQnVpbGRTdGVwJywgJ1NoZWxsU3RlcCcsICdNYW51YWxBcHByb3ZhbFN0ZXAnXSBhcyBjb25zdDtcbmV4cG9ydCBjb25zdCBQTFVHSU5fQ09NUFVURV9UWVBFUyA9IFsnU01BTEwnLCAnTUVESVVNJywgJ0xBUkdFJywgJ1gyX0xBUkdFJ10gYXMgY29uc3Q7XG5leHBvcnQgY29uc3QgUExVR0lOX0ZBSUxVUkVfQkVIQVZJT1JTID0gWydmYWlsJywgJ3dhcm4nLCAnaWdub3JlJ10gYXMgY29uc3Q7XG5leHBvcnQgY29uc3QgUExVR0lOX0JVSUxEX1RZUEVTID0gWydidWlsZF9pbWFnZScsICdwcmVidWlsdCcsICdtZXRhZGF0YV9vbmx5J10gYXMgY29uc3Q7XG5leHBvcnQgdHlwZSBQbHVnaW5CdWlsZFR5cGUgPSB0eXBlb2YgUExVR0lOX0JVSUxEX1RZUEVTW251bWJlcl07XG5cbi8qKiBQbHVnaW4gbmFtZTogbG93ZXJjYXNlIGxldHRlcnMsIGRpZ2l0cyBhbmQgaHlwaGVucy4gKi9cbmV4cG9ydCBjb25zdCBQTFVHSU5fTkFNRV9QQVRURVJOID0gL15bYS16MC05LV0rJC87XG4vKiogUGx1Z2luIHZlcnNpb246IHNlbXZlciwgd2l0aCBvcHRpb25hbCBgLXByZXJlbGVhc2VgIGFuZCBgK2J1aWxkYCBtZXRhZGF0YS4gKi9cbmV4cG9ydCBjb25zdCBQTFVHSU5fVkVSU0lPTl9QQVRURVJOID0gL15cXGQrXFwuXFxkK1xcLlxcZCsoLVthLXpBLVowLTkuLV0rKT8oXFwrW2EtekEtWjAtOS4tXSspPyQvO1xuXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuLy8gbmV0d29yay5lZ3Jlc3MgKHNwZWMtb25seSB0cnVzdCBtZXRhZGF0YSlcbi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi8qKiBNYXggZGVjbGFyZWQgYG5ldHdvcmsuZWdyZXNzYCBob3N0bmFtZXMuICovXG5leHBvcnQgY29uc3QgRUdSRVNTX01BWF9IT1NUUyA9IDUwO1xuXG4vKipcbiAqIEEgYmFyZSBETlMgaG9zdG5hbWUsIG9wdGlvbmFsbHkgd2l0aCBPTkUgbGVhZGluZyBgKi5gIHdpbGRjYXJkIGxhYmVsLiBOb1xuICogc2NoZW1lLCBwb3J0LCBwYXRoLCB1c2VyaW5mbyBvciBJUCBsaXRlcmFsIOKAlCBhIGRlY2xhcmVkIGVncmVzcyB0YXJnZXQgaXMgYVxuICogbmFtZSB0aGUgcmV2aWV3ZXIgY2FuIHJlYWQuXG4gKi9cbmNvbnN0IEVHUkVTU19IT1NUX1BBVFRFUk4gPSAvXig/OlxcKlxcLik/KD86W2EtejAtOV0oPzpbYS16MC05LV17MCw2MX1bYS16MC05XSk/XFwuKStbYS16XVthLXowLTktXXswLDYxfVthLXowLTldJC87XG5cbi8qKiBUcnVlIHdoZW4gYGhvc3RgIGlzIGFuIGFjY2VwdGFibGUgYG5ldHdvcmsuZWdyZXNzYCBlbnRyeS4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpc1ZhbGlkRWdyZXNzSG9zdChob3N0OiBzdHJpbmcpOiBib29sZWFuIHtcbiAgcmV0dXJuIGhvc3QubGVuZ3RoIDw9IDI1MyAmJiBFR1JFU1NfSE9TVF9QQVRURVJOLnRlc3QoaG9zdCk7XG59XG5cbi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4vLyBjb25maWcueWFtbFxuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuZXhwb3J0IGNvbnN0IFBsdWdpbkNvbmZpZ1NjaGVtYSA9IHoub2JqZWN0KHtcbiAgcGx1Z2luU3BlYzogei5zdHJpbmcoKS5vcHRpb25hbCgpLFxuICBkb2NrZXJmaWxlOiB6LnN0cmluZygpLm9wdGlvbmFsKCksXG4gIGJ1aWxkVHlwZTogei5lbnVtKFBMVUdJTl9CVUlMRF9UWVBFUykub3B0aW9uYWwoKSxcbiAgLy8gRGV0ZXJtaW5pc3RpYyBidWlsZCB0YWcgd3JpdHRlbiBieSBidWlsZC1wbHVnaW4taW1hZ2VzLnNoXG4gIC8vIChgcC08bmFtZT4tPHNoYTI1NjoxMj5gKS4gSW5mb3JtYXRpb25hbCBvbmx5IG9uIHRoZSB1cGxvYWQgcGF0aCDigJRcbiAgLy8gdGhlIHBsYXRmb3JtIGRvZXNuJ3QgYWN0IG9uIGl0LCBidXQgaXQgbGl2ZXMgaW4gdGhlIHNhbWUgY29uZmlnXG4gIC8vIGZpbGUgYXMgdGhlIHJlc3Qgb2YgdGhlIGJ1aWxkIG1ldGFkYXRhIHNvIHJlLXJ1bnMgY2FuIHNob3J0LWNpcmN1aXRcbiAgLy8gdW5jaGFuZ2VkIHBsdWdpbnMgd2l0aG91dCBuZWVkaW5nIGEgc2VwYXJhdGUgLmltYWdlLWhhc2ggc2lkZWNhci5cbiAgaW1hZ2VUYWc6IHouc3RyaW5nKCkub3B0aW9uYWwoKSxcbn0pLnN0cmljdCgpXG4gIC5yZWZpbmUoZCA9PiAhKGQuYnVpbGRUeXBlID09PSAncHJlYnVpbHQnICYmIGQuZG9ja2VyZmlsZSksIHtcbiAgICBtZXNzYWdlOiAnZG9ja2VyZmlsZSBpcyBub3QgYWxsb3dlZCB3aGVuIGJ1aWxkVHlwZSBpcyBwcmVidWlsdCcsXG4gIH0pXG4gIC5yZWZpbmUoZCA9PiAhKGQuYnVpbGRUeXBlID09PSAnbWV0YWRhdGFfb25seScgJiYgZC5kb2NrZXJmaWxlKSwge1xuICAgIG1lc3NhZ2U6ICdkb2NrZXJmaWxlIGlzIG5vdCBhbGxvd2VkIHdoZW4gYnVpbGRUeXBlIGlzIG1ldGFkYXRhX29ubHknLFxuICB9KTtcbmV4cG9ydCB0eXBlIFBsdWdpbkNvbmZpZ0lucHV0ID0gei5pbmZlcjx0eXBlb2YgUGx1Z2luQ29uZmlnU2NoZW1hPjtcblxuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbi8vIHBsdWdpbi1zcGVjLnlhbWxcbi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi8vIG5hbWUvdmVyc2lvbi9jb21tYW5kcyBhcmUgdGhlIHJlcXVpcmVkLWZpZWxkIGNvbnRyYWN0LCBidXQgdGhleSdyZSBjaGVja2VkIGJ5XG4vLyB7QGxpbmsgcGx1Z2luU3BlY1JlcXVpcmVkRmllbGRzUHJvYmxlbX0gKHRoZSBzdGFibGUgXCJuYW1lLCB2ZXJzaW9uLCBhbmRcbi8vIGNvbW1hbmRzIGFyZSByZXF1aXJlZFwiIG1lc3NhZ2UgYW5kIHRoZSBNYW51YWxBcHByb3ZhbFN0ZXAgY2FydmUtb3V0KS4gSGVyZVxuLy8gdGhleSdyZSBvcHRpb25hbCBzdHJpbmdzOyB0aGUgc2NoZW1hJ3Mgam9iIGlzIHRvIHJlamVjdCBtYWxmb3JtZWQgKnR5cGVzKiBvblxuLy8gdGhlIGZpZWxkcyB0aGF0IGZsb3cgZG93bnN0cmVhbSB1bmNoZWNrZWQuXG5leHBvcnQgY29uc3QgUGx1Z2luU3BlY1NjaGVtYSA9IHoub2JqZWN0KHtcbiAgbmFtZTogei5zdHJpbmcoKS5vcHRpb25hbCgpLFxuICBkZXNjcmlwdGlvbjogei5zdHJpbmcoKS5vcHRpb25hbCgpLFxuICBrZXl3b3Jkczogei5hcnJheSh6LnN0cmluZygpKS5vcHRpb25hbCgpLFxuICBjYXRlZ29yeTogei5zdHJpbmcoKS5vcHRpb25hbCgpLFxuICB2ZXJzaW9uOiB6LnN0cmluZygpLm9wdGlvbmFsKCksXG4gIHBsdWdpblR5cGU6IHouZW51bShQTFVHSU5fVFlQRVMpLm9wdGlvbmFsKCksXG4gIGNvbXB1dGVUeXBlOiB6LmVudW0oUExVR0lOX0NPTVBVVEVfVFlQRVMpLm9wdGlvbmFsKCksXG4gIC8vID49IDAgbWludXRlczogTWFudWFsQXBwcm92YWxTdGVwIHNwZWNzIGxlZ2l0aW1hdGVseSBkZWNsYXJlIGB0aW1lb3V0OiAwYC5cbiAgdGltZW91dDogei5udW1iZXIoKS5pbnQoKS5ub25uZWdhdGl2ZSgpLm9wdGlvbmFsKCksXG4gIGZhaWx1cmVCZWhhdmlvcjogei5lbnVtKFBMVUdJTl9GQUlMVVJFX0JFSEFWSU9SUykub3B0aW9uYWwoKSxcbiAgc2VjcmV0czogei5hcnJheSh6Lm9iamVjdCh7XG4gICAgbmFtZTogei5zdHJpbmcoKS5taW4oMSksXG4gICAgcmVxdWlyZWQ6IHouYm9vbGVhbigpLFxuICAgIGRlc2NyaXB0aW9uOiB6LnN0cmluZygpLm9wdGlvbmFsKCksXG4gIH0pKS5vcHRpb25hbCgpLFxuICAvLyBBY2NlcHQgYW4gZXhwbGljaXQgYHByaW1hcnlPdXRwdXREaXJlY3Rvcnk6IG51bGxgIChhIE1hbnVhbEFwcHJvdmFsU3RlcCAvXG4gIC8vIG91dHB1dC1sZXNzIHN0ZXAgbGVnaXRpbWF0ZWx5IGRlY2xhcmVzIGl0KSBhbmQgbm9ybWFsaXplIGl0IHRvIGB1bmRlZmluZWRgLlxuICAvLyBXaXRob3V0IGAubnVsbGlzaCgpYCBhIHByZXNlbnQgWUFNTCBgbnVsbGAgZmFpbHMgdGhlIGJhcmUgc3RyaW5nIGNoZWNrIGFuZFxuICAvLyA0MDBzIHRoZSB3aG9sZSB1cGxvYWQuXG4gIHByaW1hcnlPdXRwdXREaXJlY3Rvcnk6IHouc3RyaW5nKCkubnVsbGlzaCgpLnRyYW5zZm9ybSgodikgPT4gdiA/PyB1bmRlZmluZWQpLFxuICBtZXRhZGF0YTogei5yZWNvcmQoei5zdHJpbmcoKSwgei51bmlvbihbei5zdHJpbmcoKSwgei5udW1iZXIoKSwgei5ib29sZWFuKCldKSkub3B0aW9uYWwoKSxcbiAgZG9ja2VyZmlsZTogei5zdHJpbmcoKS5vcHRpb25hbCgpLFxuICBpbnN0YWxsQ29tbWFuZHM6IHouYXJyYXkoei5zdHJpbmcoKSkub3B0aW9uYWwoKSxcbiAgY29tbWFuZHM6IHouYXJyYXkoei5zdHJpbmcoKSkub3B0aW9uYWwoKSxcbiAgZW52OiB6LnJlY29yZCh6LnN0cmluZygpLCB6LnN0cmluZygpKS5vcHRpb25hbCgpLFxuICBidWlsZEFyZ3M6IHoucmVjb3JkKHouc3RyaW5nKCksIHouc3RyaW5nKCkpLm9wdGlvbmFsKCksXG4gIHJlcXVpcmVkTWV0YWRhdGE6IHouYXJyYXkoei5zdHJpbmcoKSkub3B0aW9uYWwoKSxcbiAgcmVxdWlyZWRWYXJzOiB6LmFycmF5KHouc3RyaW5nKCkpLm9wdGlvbmFsKCksXG4gIC8vIEtlcHQgYXMgZnJlZS1zdHJpbmcgbWFwcyByYXRoZXIgdGhhbiBhIHZhbHVlIGVudW06IHNoaXBwZWQgc3BlY3MgdXNlIGJvdGhcbiAgLy8gYGJvb2xgIGFuZCBgYm9vbGVhbmAsIGFuZCB0aGUgY29lcmNpb24tdnMtZGVjbGFyZWQtdHlwZSBjb25zaXN0ZW5jeSBpc1xuICAvLyBlbmZvcmNlZCBzZXBhcmF0ZWx5IGJ5IHRoZSB0ZW1wbGF0ZSBjb250cmFjdCBjaGVjay4gVGhlIHNjaGVtYSBvbmx5XG4gIC8vIGd1YXJhbnRlZXMgdGhleSdyZSBzdHJpbmctdmFsdWVkIHJlY29yZHMgaGVyZS5cbiAgbWV0YWRhdGFUeXBlczogei5yZWNvcmQoei5zdHJpbmcoKSwgei5zdHJpbmcoKSkub3B0aW9uYWwoKSxcbiAgdmFyc1R5cGVzOiB6LnJlY29yZCh6LnN0cmluZygpLCB6LnN0cmluZygpKS5vcHRpb25hbCgpLFxuICAvLyBBIGNvbW1hbmQgdGhlIGJ1aWxkIHRvb2xpbmcgcnVucyBhZ2FpbnN0IHRoZSBidWlsdCBpbWFnZVxuICAvLyAoYnVpbGQtcGx1Z2luLWltYWdlcy5zaCwgdGVzdC1wbHVnaW5zLnNoIC0tYnVpbGQsIGBwbHVnaW4gdGVzdGApLlxuICBzbW9rZVRlc3Q6IHouc3RyaW5nKCkub3B0aW9uYWwoKSxcbiAgLy8gRG9jdW1lbnRhdGlvbiArIHRydXN0IG1ldGFkYXRhLiBUaGUgUkVBRE1FIGlzIE5PVCBhIHNwZWMgZmllbGQg4oCUIGl0XG4gIC8vIGlzIFJFQURNRS5tZCBhdCB0aGUgemlwIHJvb3QuXG4gIGxpY2Vuc2U6IHouc3RyaW5nKCkucmVmaW5lKGlzQWxsb3dlZFNwZHhJZCwge1xuICAgIG1lc3NhZ2U6ICdtdXN0IGJlIGEgc3VwcG9ydGVkIFNQRFggbGljZW5zZSBpZGVudGlmaWVyIChlLmcuIEFwYWNoZS0yLjAsIE1JVCknLFxuICB9KS5vcHRpb25hbCgpLFxuICBjaGFuZ2Vsb2c6IHouc3RyaW5nKCkucmVmaW5lKCh2KSA9PiBuZXcgVGV4dEVuY29kZXIoKS5lbmNvZGUodikubGVuZ3RoIDw9IFBMVUdJTl9DSEFOR0VMT0dfTUFYX0JZVEVTLCB7XG4gICAgbWVzc2FnZTogYG11c3QgYmUgYXQgbW9zdCAke1BMVUdJTl9DSEFOR0VMT0dfTUFYX0JZVEVTfSBieXRlc2AsXG4gIH0pLm9wdGlvbmFsKCksXG4gIGhvbWVwYWdlVXJsOiBQcm9qZWN0VXJsU2NoZW1hLm9wdGlvbmFsKCksXG4gIHNvdXJjZVVybDogUHJvamVjdFVybFNjaGVtYS5vcHRpb25hbCgpLFxuICAvLyBDYXRhbG9nIG1ldGFkYXRhOiB0aGUgY2FyZCBvbmUtbGluZXIgYW5kIHRoZSBkb2NzIGxpbmsuIFNhbWVcbiAgLy8gdmFsaWRhdG9ycyBhcyBhIHZhbHVlIHR5cGVkIGludG8gdGhlIHVwbG9hZCBmb3JtLlxuICBzdW1tYXJ5OiBQTFVHSU5fQ0FUQUxPR19GSUVMRF9TQ0hFTUFTLnN1bW1hcnkub3B0aW9uYWwoKSxcbiAgZG9jdW1lbnRhdGlvblVybDogUHJvamVjdFVybFNjaGVtYS5vcHRpb25hbCgpLFxuICBpY29uOiB6LnVuaW9uKFtcbiAgICBJY29uS2V5U2NoZW1hLFxuICAgIHoub2JqZWN0KHsga2V5OiBJY29uS2V5U2NoZW1hLCBiYWRnZTogSWNvbktleVNjaGVtYS5vcHRpb25hbCgpIH0pLnN0cmljdCgpLFxuICBdKS5vcHRpb25hbCgpLFxuICBuZXR3b3JrOiB6Lm9iamVjdCh7XG4gICAgZWdyZXNzOiB6LmFycmF5KHouc3RyaW5nKCkucmVmaW5lKGlzVmFsaWRFZ3Jlc3NIb3N0LCB7XG4gICAgICBtZXNzYWdlOiAnbXVzdCBiZSBhIGJhcmUgaG9zdG5hbWUgKG5vIHNjaGVtZSwgcG9ydCBvciBwYXRoOyBvbmUgbGVhZGluZyAqLiBhbGxvd2VkKScsXG4gICAgfSkpLm1heChFR1JFU1NfTUFYX0hPU1RTKS5vcHRpb25hbCgpLFxuICB9KS5zdHJpY3QoKS5vcHRpb25hbCgpLFxufSkuc3RyaWN0KCk7XG5leHBvcnQgdHlwZSBQbHVnaW5TcGVjSW5wdXQgPSB6LmluZmVyPHR5cGVvZiBQbHVnaW5TcGVjU2NoZW1hPjtcblxuLyoqXG4gKiBSZXN1bHQgb2YgY2hlY2tpbmcgYSBwYXJzZWQgWUFNTCBkb2N1bWVudCBhZ2FpbnN0IGEgcGFja2FnZSBzY2hlbWE6XG4gKiBgaXNzdWVzYCBhcmUgdGhlIGluZGl2aWR1YWwgcHJvYmxlbXMsIGBtZXNzYWdlYCB0aGUgb25lLWxpbmUgZm9ybSB0aGUgdXBsb2FkXG4gKiBBUEkgcmVwb3J0cyAoYHBsdWdpbi1zcGVjLnlhbWw6IDxwYXRoPjogPHByb2JsZW0+OyDigKZgKS5cbiAqL1xuZXhwb3J0IHR5cGUgUGFja2FnZUNoZWNrPFQ+ID0geyBvazogdHJ1ZTsgdmFsdWU6IFQgfSB8IHsgb2s6IGZhbHNlOyBpc3N1ZXM6IHN0cmluZ1tdOyBtZXNzYWdlOiBzdHJpbmcgfTtcblxuY29uc3QgaXNNYXBwaW5nID0gKHJhdzogdW5rbm93bik6IHJhdyBpcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiA9PlxuICB0eXBlb2YgcmF3ID09PSAnb2JqZWN0JyAmJiByYXcgIT09IG51bGwgJiYgIUFycmF5LmlzQXJyYXkocmF3KTtcblxuZnVuY3Rpb24gY2hlY2tBZ2FpbnN0PFQ+KGZpbGU6IHN0cmluZywgcmF3OiB1bmtub3duLCBzY2hlbWE6IHouWm9kVHlwZTxUPiwgd2l0aFBhdGg6IGJvb2xlYW4pOiBQYWNrYWdlQ2hlY2s8VD4ge1xuICBpZiAoIWlzTWFwcGluZyhyYXcpKSB7XG4gICAgY29uc3QgbWVzc2FnZSA9IGAke2ZpbGV9IG11c3QgYmUgYSBZQU1MIG1hcHBpbmdgO1xuICAgIHJldHVybiB7IG9rOiBmYWxzZSwgaXNzdWVzOiBbbWVzc2FnZV0sIG1lc3NhZ2UgfTtcbiAgfVxuICBjb25zdCByZXN1bHQgPSBzY2hlbWEuc2FmZVBhcnNlKHJhdyk7XG4gIGlmIChyZXN1bHQuc3VjY2VzcykgcmV0dXJuIHsgb2s6IHRydWUsIHZhbHVlOiByZXN1bHQuZGF0YSB9O1xuICBjb25zdCBpc3N1ZXMgPSByZXN1bHQuZXJyb3IuaXNzdWVzLm1hcChpID0+ICh3aXRoUGF0aCA/IGAke2kucGF0aC5qb2luKCcuJykgfHwgJyhyb290KSd9OiAke2kubWVzc2FnZX1gIDogaS5tZXNzYWdlKSk7XG4gIHJldHVybiB7IG9rOiBmYWxzZSwgaXNzdWVzLCBtZXNzYWdlOiBgJHtmaWxlfTogJHtpc3N1ZXMuam9pbignOyAnKX1gIH07XG59XG5cbi8qKiBDaGVjayBhIHBhcnNlZCBgcGx1Z2luLXNwZWMueWFtbGAgZG9jdW1lbnQgYWdhaW5zdCB7QGxpbmsgUGx1Z2luU3BlY1NjaGVtYX0uICovXG5leHBvcnQgZnVuY3Rpb24gY2hlY2tQbHVnaW5TcGVjKHJhdzogdW5rbm93bik6IFBhY2thZ2VDaGVjazxQbHVnaW5TcGVjSW5wdXQ+IHtcbiAgcmV0dXJuIGNoZWNrQWdhaW5zdCgncGx1Z2luLXNwZWMueWFtbCcsIHJhdywgUGx1Z2luU3BlY1NjaGVtYSwgdHJ1ZSk7XG59XG5cbi8qKiBDaGVjayBhIHBhcnNlZCBgY29uZmlnLnlhbWxgIGRvY3VtZW50IGFnYWluc3Qge0BsaW5rIFBsdWdpbkNvbmZpZ1NjaGVtYX0uICovXG5leHBvcnQgZnVuY3Rpb24gY2hlY2tQbHVnaW5Db25maWcocmF3OiB1bmtub3duKTogUGFja2FnZUNoZWNrPFBsdWdpbkNvbmZpZ0lucHV0PiB7XG4gIHJldHVybiBjaGVja0FnYWluc3QoJ2NvbmZpZy55YW1sJywgcmF3LCBQbHVnaW5Db25maWdTY2hlbWEsIGZhbHNlKTtcbn1cblxuLyoqXG4gKiBUaGUgcmVxdWlyZWQtZmllbGQgY29udHJhY3Q6IGBuYW1lYCBhbmQgYHZlcnNpb25gIGFsd2F5cywgYGNvbW1hbmRzYCBmb3JcbiAqIGV2ZXJ5IHR5cGUgZXhjZXB0IGBNYW51YWxBcHByb3ZhbFN0ZXBgLiBSZXR1cm5zIHRoZSB1cGxvYWQncyBtZXNzYWdlLCBvciBudWxsLlxuICovXG5leHBvcnQgZnVuY3Rpb24gcGx1Z2luU3BlY1JlcXVpcmVkRmllbGRzUHJvYmxlbShzcGVjOiBQaWNrPFBsdWdpblNwZWNJbnB1dCwgJ25hbWUnIHwgJ3ZlcnNpb24nIHwgJ2NvbW1hbmRzJyB8ICdwbHVnaW5UeXBlJz4pOiBzdHJpbmcgfCBudWxsIHtcbiAgY29uc3QgaXNBcHByb3ZhbFN0ZXAgPSBzcGVjLnBsdWdpblR5cGUgPT09ICdNYW51YWxBcHByb3ZhbFN0ZXAnO1xuICBpZiAoIXNwZWMubmFtZSB8fCAhc3BlYy52ZXJzaW9uIHx8ICghaXNBcHByb3ZhbFN0ZXAgJiYgIXNwZWMuY29tbWFuZHMpKSB7XG4gICAgcmV0dXJuICdJbnZhbGlkIHNwZWM6IG5hbWUsIHZlcnNpb24sIGFuZCBjb21tYW5kcyBhcmUgcmVxdWlyZWQnO1xuICB9XG4gIHJldHVybiBudWxsO1xufVxuIl19
@@ -0,0 +1,64 @@
1
+ /**
2
+ * The plugin spec's `{{ ... }}` TEMPLATE CONTRACT, exactly as the plugin
3
+ * service checks an upload. Shared from api-core so the upload path
4
+ * (api/plugin) and the CLI (`pipeline-manager plugin validate` / `test` /
5
+ * `publish`) apply ONE check and can't drift (plugin-ecosystem W6).
6
+ *
7
+ * Pure: it returns issues and never throws. The template engine (tokenizer and
8
+ * scope validator) lives in pipeline-core, which depends on api-core, so the
9
+ * caller hands it in as {@link PluginTemplateEngine}; pipeline-core's module
10
+ * satisfies the interface as-is.
11
+ */
12
+ /** Scope roots a plugin template may reference. */
13
+ export declare const PLUGIN_TEMPLATE_SCOPE_ROOTS: readonly ['pipeline', 'plugin', 'env'];
14
+ /** Spec fields whose strings may carry `{{ ... }}` templates. */
15
+ export declare const PLUGIN_TEMPLATABLE_FIELDS: readonly ['description', 'commands', 'installCommands', 'env', 'buildArgs'];
16
+ /** True when a walked field path (`commands[2]`, `env.STAGE`) is templatable. */
17
+ export declare function isPluginTemplatableField(field: string): boolean;
18
+ /** The slice of pipeline-core's template engine this check uses. */
19
+ export interface PluginTemplateEngine {
20
+ allowedScopeRoots(roots: string[]): (path: string[]) => boolean;
21
+ validateTemplates(doc: object, isTemplatable: (field: string) => boolean, isKnownPath: (path: string[]) => boolean): {
22
+ errors: ReadonlyArray<{
23
+ field: string;
24
+ line?: number;
25
+ col?: number;
26
+ message: string;
27
+ }>;
28
+ };
29
+ tokenize(source: string): ReadonlyArray<{
30
+ kind: string;
31
+ path?: string[];
32
+ defaultValue?: string;
33
+ coerce?: string;
34
+ }>;
35
+ }
36
+ /**
37
+ * One template-contract problem.
38
+ * - `template`: a parse error, unknown scope root or reserved `secrets.*` path.
39
+ * - `undeclared`: `{{ pipeline.metadata.X }}` / `{{ pipeline.vars.X }}` without
40
+ * `X` in `requiredMetadata` / `requiredVars` (and no `| default:`).
41
+ * - `type-mismatch`: a `| number|bool|json` filter whose declared type differs.
42
+ */
43
+ export interface PluginTemplateIssue {
44
+ kind: 'template' | 'undeclared' | 'type-mismatch';
45
+ /** The spec field (`commands[0]`) for `template` issues; the scope path otherwise. */
46
+ field: string;
47
+ line?: number;
48
+ col?: number;
49
+ message: string;
50
+ }
51
+ /**
52
+ * Every `{{ ... }}` problem in a plugin spec: parse errors and scope roots
53
+ * (only `pipeline`, `plugin`, `env`; `secrets` is reserved), then per
54
+ * `{{ pipeline.metadata.X }}` / `{{ pipeline.vars.X }}` reference (a) that `X`
55
+ * is declared in `requiredMetadata` / `requiredVars` unless it has a
56
+ * `| default:`, and (b) that a coercion filter matches the declared type in
57
+ * `metadataTypes` / `varsTypes` (undeclared = `string`; `boolean` = `bool`).
58
+ *
59
+ * Takes the raw spec object, so a spec that failed the schema is still checked.
60
+ * Issues are de-duplicated; an empty array means the spec passes.
61
+ */
62
+ export declare function checkPluginTemplates(spec: object, engine: PluginTemplateEngine): PluginTemplateIssue[];
63
+ /** One line per issue: `[commands[0]:1:5] message` for template issues, the message otherwise. */
64
+ export declare function formatPluginTemplateIssue(issue: PluginTemplateIssue): string;
@@ -0,0 +1,113 @@
1
+ // Copyright 2026 Pipeline Builder Contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * The plugin spec's `{{ ... }}` TEMPLATE CONTRACT, exactly as the plugin
5
+ * service checks an upload. Shared from api-core so the upload path
6
+ * (api/plugin) and the CLI (`pipeline-manager plugin validate` / `test` /
7
+ * `publish`) apply ONE check and can't drift (plugin-ecosystem W6).
8
+ *
9
+ * Pure: it returns issues and never throws. The template engine (tokenizer and
10
+ * scope validator) lives in pipeline-core, which depends on api-core, so the
11
+ * caller hands it in as {@link PluginTemplateEngine}; pipeline-core's module
12
+ * satisfies the interface as-is.
13
+ */
14
+ /** Scope roots a plugin template may reference. */
15
+ export const PLUGIN_TEMPLATE_SCOPE_ROOTS = ['pipeline', 'plugin', 'env'];
16
+ /** Spec fields whose strings may carry `{{ ... }}` templates. */
17
+ export const PLUGIN_TEMPLATABLE_FIELDS = ['description', 'commands', 'installCommands', 'env', 'buildArgs'];
18
+ /** True when a walked field path (`commands[2]`, `env.STAGE`) is templatable. */
19
+ export function isPluginTemplatableField(field) {
20
+ return PLUGIN_TEMPLATABLE_FIELDS.some((f) => field === f || field.startsWith(`${f}[`) || field.startsWith(`${f}.`));
21
+ }
22
+ const COERCE_TO_TYPE = { number: 'number', bool: 'bool', json: 'json' };
23
+ const stringList = (v) => (Array.isArray(v) ? v.filter((x) => typeof x === 'string') : []);
24
+ const stringValues = (v) => (v && typeof v === 'object' && !Array.isArray(v)
25
+ ? Object.values(v).filter((x) => typeof x === 'string')
26
+ : []);
27
+ const stringMap = (v) => (v && typeof v === 'object' && !Array.isArray(v) ? v : {});
28
+ /**
29
+ * Every `{{ ... }}` problem in a plugin spec: parse errors and scope roots
30
+ * (only `pipeline`, `plugin`, `env`; `secrets` is reserved), then per
31
+ * `{{ pipeline.metadata.X }}` / `{{ pipeline.vars.X }}` reference (a) that `X`
32
+ * is declared in `requiredMetadata` / `requiredVars` unless it has a
33
+ * `| default:`, and (b) that a coercion filter matches the declared type in
34
+ * `metadataTypes` / `varsTypes` (undeclared = `string`; `boolean` = `bool`).
35
+ *
36
+ * Takes the raw spec object, so a spec that failed the schema is still checked.
37
+ * Issues are de-duplicated; an empty array means the spec passes.
38
+ */
39
+ export function checkPluginTemplates(spec, engine) {
40
+ const s = spec;
41
+ const doc = {
42
+ description: s.description,
43
+ commands: s.commands,
44
+ installCommands: s.installCommands,
45
+ env: s.env,
46
+ buildArgs: s.buildArgs,
47
+ };
48
+ const issues = [];
49
+ const { errors } = engine.validateTemplates(doc, isPluginTemplatableField, engine.allowedScopeRoots([...PLUGIN_TEMPLATE_SCOPE_ROOTS]));
50
+ for (const e of errors) {
51
+ issues.push({ kind: 'template', field: e.field, ...(e.line !== undefined && { line: e.line, col: e.col }), message: e.message });
52
+ }
53
+ const required = { metadata: new Set(stringList(s.requiredMetadata)), vars: new Set(stringList(s.requiredVars)) };
54
+ const types = { metadata: stringMap(s.metadataTypes), vars: stringMap(s.varsTypes) };
55
+ const sources = [
56
+ ...(typeof s.description === 'string' ? [s.description] : []),
57
+ ...stringList(s.commands), ...stringList(s.installCommands), ...stringValues(s.env), ...stringValues(s.buildArgs),
58
+ ];
59
+ const seen = new Set();
60
+ const add = (issue) => {
61
+ const key = `${issue.kind}\u0000${issue.message}`;
62
+ if (!seen.has(key)) {
63
+ seen.add(key);
64
+ issues.push(issue);
65
+ }
66
+ };
67
+ for (const source of sources) {
68
+ if (!source.includes('{{'))
69
+ continue;
70
+ let tokens;
71
+ try {
72
+ tokens = engine.tokenize(source);
73
+ }
74
+ catch {
75
+ continue; /* reported as a `template` issue above */
76
+ }
77
+ for (const t of tokens) {
78
+ if (t.kind !== 'expr' || !t.path)
79
+ continue;
80
+ const [root, sub, key] = t.path;
81
+ if (root !== 'pipeline' || (sub !== 'metadata' && sub !== 'vars') || !key)
82
+ continue;
83
+ const scopePath = `pipeline.${sub}.${key}`;
84
+ const declaredIn = sub === 'metadata' ? 'requiredMetadata' : 'requiredVars';
85
+ const typesIn = sub === 'metadata' ? 'metadataTypes' : 'varsTypes';
86
+ if (t.defaultValue === undefined && !required[sub].has(key)) {
87
+ add({ kind: 'undeclared', field: scopePath, message: `${scopePath} is not declared in '${declaredIn}' (declare it, or add '| default: ...')` });
88
+ }
89
+ const expected = t.coerce ? COERCE_TO_TYPE[t.coerce] : undefined;
90
+ if (expected) {
91
+ // Shipped specs spell the boolean type both `bool` and `boolean`; the
92
+ // stored contract (and the filter) use `bool`.
93
+ const raw = types[sub][key] ?? 'string';
94
+ const declared = raw === 'boolean' ? 'bool' : raw;
95
+ if (declared !== expected) {
96
+ add({
97
+ kind: 'type-mismatch',
98
+ field: scopePath,
99
+ message: `${scopePath} uses '| ${t.coerce}' but declared type is '${declared}' (add '${key}: ${expected}' to ${typesIn})`,
100
+ });
101
+ }
102
+ }
103
+ }
104
+ }
105
+ return issues;
106
+ }
107
+ /** One line per issue: `[commands[0]:1:5] message` for template issues, the message otherwise. */
108
+ export function formatPluginTemplateIssue(issue) {
109
+ if (issue.kind !== 'template')
110
+ return issue.message;
111
+ return `[${issue.field}${issue.line !== undefined ? `:${issue.line}:${issue.col}` : ''}] ${issue.message}`;
112
+ }
113
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2luLXRlbXBsYXRlLWNvbnRyYWN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3ZhbGlkYXRpb24vcGx1Z2luLXRlbXBsYXRlLWNvbnRyYWN0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUFFdEM7Ozs7Ozs7Ozs7R0FVRztBQUVILG1EQUFtRDtBQUNuRCxNQUFNLENBQUMsTUFBTSwyQkFBMkIsR0FBRyxDQUFDLFVBQVUsRUFBRSxRQUFRLEVBQUUsS0FBSyxDQUFVLENBQUM7QUFFbEYsaUVBQWlFO0FBQ2pFLE1BQU0sQ0FBQyxNQUFNLHlCQUF5QixHQUFHLENBQUMsYUFBYSxFQUFFLFVBQVUsRUFBRSxpQkFBaUIsRUFBRSxLQUFLLEVBQUUsV0FBVyxDQUFVLENBQUM7QUFFckgsaUZBQWlGO0FBQ2pGLE1BQU0sVUFBVSx3QkFBd0IsQ0FBQyxLQUFhO0lBQ3BELE9BQU8seUJBQXlCLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxLQUFLLEtBQUssQ0FBQyxJQUFJLEtBQUssQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxJQUFJLEtBQUssQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7QUFDdEgsQ0FBQztBQTZCRCxNQUFNLGNBQWMsR0FBcUMsRUFBRSxNQUFNLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFLE1BQU0sRUFBRSxDQUFDO0FBRTFHLE1BQU0sVUFBVSxHQUFHLENBQUMsQ0FBVSxFQUFZLEVBQUUsQ0FDMUMsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFlLEVBQUUsQ0FBQyxPQUFPLENBQUMsS0FBSyxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxD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SUFDSCxDQUFDO0lBQ0QsT0FBTyxNQUFNLENBQUM7QUFDaEIsQ0FBQztBQUVELGtHQUFrRztBQUNsRyxNQUFNLFVBQVUseUJBQXlCLENBQUMsS0FBMEI7SUFDbEUsSUFBSSxLQUFLLENBQUMsSUFBSSxLQUFLLFVBQVU7UUFBRSxPQUFPLEtBQUssQ0FBQyxPQUFPLENBQUM7SUFDcEQsT0FBTyxJQUFJLEtBQUssQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDLElBQUksS0FBSyxTQUFTLENBQUMsQ0FBQyxDQUFDLElBQUksS0FBSyxDQUFDLElBQUksSUFBSSxLQUFLLENBQUMsR0FBRyxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsS0FBSyxLQUFLLENBQUMsT0FBTyxFQUFFLENBQUM7QUFDN0csQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIENvcHlyaWdodCAyMDI2IFBpcGVsaW5lIEJ1aWxkZXIgQ29udHJpYnV0b3JzXG4vLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuXG4vKipcbiAqIFRoZSBwbHVnaW4gc3BlYydzIGB7eyAuLi4gfX1gIFRFTVBMQVRFIENPTlRSQUNULCBleGFjdGx5IGFzIHRoZSBwbHVnaW5cbiAqIHNlcnZpY2UgY2hlY2tzIGFuIHVwbG9hZC4gU2hhcmVkIGZyb20gYXBpLWNvcmUgc28gdGhlIHVwbG9hZCBwYXRoXG4gKiAoYXBpL3BsdWdpbikgYW5kIHRoZSBDTEkgKGBwaXBlbGluZS1tYW5hZ2VyIHBsdWdpbiB2YWxpZGF0ZWAgLyBgdGVzdGAgL1xuICogYHB1Ymxpc2hgKSBhcHBseSBPTkUgY2hlY2sgYW5kIGNhbid0IGRyaWZ0IChwbHVnaW4tZWNvc3lzdGVtIFc2KS5cbiAqXG4gKiBQdXJlOiBpdCByZXR1cm5zIGlzc3VlcyBhbmQgbmV2ZXIgdGhyb3dzLiBUaGUgdGVtcGxhdGUgZW5naW5lICh0b2tlbml6ZXIgYW5kXG4gKiBzY29wZSB2YWxpZGF0b3IpIGxpdmVzIGluIHBpcGVsaW5lLWNvcmUsIHdoaWNoIGRlcGVuZHMgb24gYXBpLWNvcmUsIHNvIHRoZVxuICogY2FsbGVyIGhhbmRzIGl0IGluIGFzIHtAbGluayBQbHVnaW5UZW1wbGF0ZUVuZ2luZX07IHBpcGVsaW5lLWNvcmUncyBtb2R1bGVcbiAqIHNhdGlzZmllcyB0aGUgaW50ZXJmYWNlIGFzLWlzLlxuICovXG5cbi8qKiBTY29wZSByb290cyBhIHBsdWdpbiB0ZW1wbGF0ZSBtYXkgcmVmZXJlbmNlLiAqL1xuZXhwb3J0IGNvbnN0IFBMVUdJTl9URU1QTEFURV9TQ09QRV9ST09UUyA9IFsncGlwZWxpbmUnLCAncGx1Z2luJywgJ2VudiddIGFzIGNvbnN0O1xuXG4vKiogU3BlYyBmaWVsZHMgd2hvc2Ugc3RyaW5ncyBtYXkgY2FycnkgYHt7IC4uLiB9fWAgdGVtcGxhdGVzLiAqL1xuZXhwb3J0IGNvbnN0IFBMVUdJTl9URU1QTEFUQUJMRV9GSUVMRFMgPSBbJ2Rlc2NyaXB0aW9uJywgJ2NvbW1hbmRzJywgJ2luc3RhbGxDb21tYW5kcycsICdlbnYnLCAnYnVpbGRBcmdzJ10gYXMgY29uc3Q7XG5cbi8qKiBUcnVlIHdoZW4gYSB3YWxrZWQgZmllbGQgcGF0aCAoYGNvbW1hbmRzWzJdYCwgYGVudi5TVEFHRWApIGlzIHRlbXBsYXRhYmxlLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGlzUGx1Z2luVGVtcGxhdGFibGVGaWVsZChmaWVsZDogc3RyaW5nKTogYm9vbGVhbiB7XG4gIHJldHVybiBQTFVHSU5fVEVNUExBVEFCTEVfRklFTERTLnNvbWUoKGYpID0+IGZpZWxkID09PSBmIHx8IGZpZWxkLnN0YXJ0c1dpdGgoYCR7Zn1bYCkgfHwgZmllbGQuc3RhcnRzV2l0aChgJHtmfS5gKSk7XG59XG5cbi8qKiBUaGUgc2xpY2Ugb2YgcGlwZWxpbmUtY29yZSdzIHRlbXBsYXRlIGVuZ2luZSB0aGlzIGNoZWNrIHVzZXMuICovXG5leHBvcnQgaW50ZXJmYWNlIFBsdWdpblRlbXBsYXRlRW5naW5lIHtcbiAgYWxsb3dlZFNjb3BlUm9vdHMocm9vdHM6IHN0cmluZ1tdKTogKHBhdGg6IHN0cmluZ1tdKSA9PiBib29sZWFuO1xuICB2YWxpZGF0ZVRlbXBsYXRlcyhcbiAgICBkb2M6IG9iamVjdCxcbiAgICBpc1RlbXBsYXRhYmxlOiAoZmllbGQ6IHN0cmluZykgPT4gYm9vbGVhbixcbiAgICBpc0tub3duUGF0aDogKHBhdGg6IHN0cmluZ1tdKSA9PiBib29sZWFuLFxuICApOiB7IGVycm9yczogUmVhZG9ubHlBcnJheTx7IGZpZWxkOiBzdHJpbmc7IGxpbmU/OiBudW1iZXI7IGNvbD86IG51bWJlcjsgbWVzc2FnZTogc3RyaW5nIH0+IH07XG4gIHRva2VuaXplKHNvdXJjZTogc3RyaW5nKTogUmVhZG9ubHlBcnJheTx7IGtpbmQ6IHN0cmluZzsgcGF0aD86IHN0cmluZ1tdOyBkZWZhdWx0VmFsdWU/OiBzdHJpbmc7IGNvZXJjZT86IHN0cmluZyB9Pjtcbn1cblxuLyoqXG4gKiBPbmUgdGVtcGxhdGUtY29udHJhY3QgcHJvYmxlbS5cbiAqIC0gYHRlbXBsYXRlYDogYSBwYXJzZSBlcnJvciwgdW5rbm93biBzY29wZSByb290IG9yIHJlc2VydmVkIGBzZWNyZXRzLipgIHBhdGguXG4gKiAtIGB1bmRlY2xhcmVkYDogYHt7IHBpcGVsaW5lLm1ldGFkYXRhLlggfX1gIC8gYHt7IHBpcGVsaW5lLnZhcnMuWCB9fWAgd2l0aG91dFxuICogICBgWGAgaW4gYHJlcXVpcmVkTWV0YWRhdGFgIC8gYHJlcXVpcmVkVmFyc2AgKGFuZCBubyBgfCBkZWZhdWx0OmApLlxuICogLSBgdHlwZS1taXNtYXRjaGA6IGEgYHwgbnVtYmVyfGJvb2x8anNvbmAgZmlsdGVyIHdob3NlIGRlY2xhcmVkIHR5cGUgZGlmZmVycy5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBQbHVnaW5UZW1wbGF0ZUlzc3VlIHtcbiAga2luZDogJ3RlbXBsYXRlJyB8ICd1bmRlY2xhcmVkJyB8ICd0eXBlLW1pc21hdGNoJztcbiAgLyoqIFRoZSBzcGVjIGZpZWxkIChgY29tbWFuZHNbMF1gKSBmb3IgYHRlbXBsYXRlYCBpc3N1ZXM7IHRoZSBzY29wZSBwYXRoIG90aGVyd2lzZS4gKi9cbiAgZmllbGQ6IHN0cmluZztcbiAgbGluZT86IG51bWJlcjtcbiAgY29sPzogbnVtYmVyO1xuICBtZXNzYWdlOiBzdHJpbmc7XG59XG5cbmNvbnN0IENPRVJDRV9UT19UWVBFOiBSZWFkb25seTxSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+PiA9IHsgbnVtYmVyOiAnbnVtYmVyJywgYm9vbDogJ2Jvb2wnLCBqc29uOiAnanNvbicgfTtcblxuY29uc3Qgc3RyaW5nTGlzdCA9ICh2OiB1bmtub3duKTogc3RyaW5nW10gPT5cbiAgKEFycmF5LmlzQXJyYXkodikgPyB2LmZpbHRlcigoeCk6IHggaXMgc3RyaW5nID0+IHR5cGVvZiB4ID09PSAnc3RyaW5nJykgOiBbXSk7XG5jb25zdCBzdHJpbmdWYWx1ZXMgPSAodjogdW5rbm93bik6IHN0cmluZ1tdID0+XG4gICh2ICYmIHR5cGVvZiB2ID09PSAnb2JqZWN0JyAmJiAhQXJyYXkuaXNBcnJheSh2KVxuICAgID8gT2JqZWN0LnZhbHVlcyh2IGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+KS5maWx0ZXIoKHgpOiB4IGlzIHN0cmluZyA9PiB0eXBlb2YgeCA9PT0gJ3N0cmluZycpXG4gICAgOiBbXSk7XG5jb25zdCBzdHJpbmdNYXAgPSAodjogdW5rbm93bik6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPT5cbiAgKHYgJiYgdHlwZW9mIHYgPT09ICdvYmplY3QnICYmICFBcnJheS5pc0FycmF5KHYpID8gdiBhcyBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+IDoge30pO1xuXG4vKipcbiAqIEV2ZXJ5IGB7eyAuLi4gfX1gIHByb2JsZW0gaW4gYSBwbHVnaW4gc3BlYzogcGFyc2UgZXJyb3JzIGFuZCBzY29wZSByb290c1xuICogKG9ubHkgYHBpcGVsaW5lYCwgYHBsdWdpbmAsIGBlbnZgOyBgc2VjcmV0c2AgaXMgcmVzZXJ2ZWQpLCB0aGVuIHBlclxuICogYHt7IHBpcGVsaW5lLm1ldGFkYXRhLlggfX1gIC8gYHt7IHBpcGVsaW5lLnZhcnMuWCB9fWAgcmVmZXJlbmNlIChhKSB0aGF0IGBYYFxuICogaXMgZGVjbGFyZWQgaW4gYHJlcXVpcmVkTWV0YWRhdGFgIC8gYHJlcXVpcmVkVmFyc2AgdW5sZXNzIGl0IGhhcyBhXG4gKiBgfCBkZWZhdWx0OmAsIGFuZCAoYikgdGhhdCBhIGNvZXJjaW9uIGZpbHRlciBtYXRjaGVzIHRoZSBkZWNsYXJlZCB0eXBlIGluXG4gKiBgbWV0YWRhdGFUeXBlc2AgLyBgdmFyc1R5cGVzYCAodW5kZWNsYXJlZCA9IGBzdHJpbmdgOyBgYm9vbGVhbmAgPSBgYm9vbGApLlxuICpcbiAqIFRha2VzIHRoZSByYXcgc3BlYyBvYmplY3QsIHNvIGEgc3BlYyB0aGF0IGZhaWxlZCB0aGUgc2NoZW1hIGlzIHN0aWxsIGNoZWNrZWQuXG4gKiBJc3N1ZXMgYXJlIGRlLWR1cGxpY2F0ZWQ7IGFuIGVtcHR5IGFycmF5IG1lYW5zIHRoZSBzcGVjIHBhc3Nlcy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNoZWNrUGx1Z2luVGVtcGxhdGVzKHNwZWM6IG9iamVjdCwgZW5naW5lOiBQbHVnaW5UZW1wbGF0ZUVuZ2luZSk6IFBsdWdpblRlbXBsYXRlSXNzdWVbXSB7XG4gIGNvbnN0IHMgPSBzcGVjIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xuICBjb25zdCBkb2MgPSB7XG4gICAgZGVzY3JpcHRpb246IHMuZGVzY3JpcHRpb24sXG4gICAgY29tbWFuZHM6IHMuY29tbWFuZHMsXG4gICAgaW5zdGFsbENvbW1hbmRzOiBzLmluc3RhbGxDb21tYW5kcyxcbiAgICBlbnY6IHMuZW52LFxuICAgIGJ1aWxkQXJnczogcy5idWlsZEFyZ3MsXG4gIH07XG4gIGNvbnN0IGlzc3VlczogUGx1Z2luVGVtcGxhdGVJc3N1ZVtdID0gW107XG5cbiAgY29uc3QgeyBlcnJvcnMgfSA9IGVuZ2luZS52YWxpZGF0ZVRlbXBsYXRlcyhkb2MsIGlzUGx1Z2luVGVtcGxhdGFibGVGaWVsZCwgZW5naW5lLmFsbG93ZWRTY29wZVJvb3RzKFsuLi5QTFVHSU5fVEVNUExBVEVfU0NPUEVfUk9PVFNdKSk7XG4gIGZvciAoY29uc3QgZSBvZiBlcnJvcnMpIHtcbiAgICBpc3N1ZXMucHVzaCh7IGtpbmQ6ICd0ZW1wbGF0ZScsIGZpZWxkOiBlLmZpZWxkLCAuLi4oZS5saW5lICE9PSB1bmRlZmluZWQgJiYgeyBsaW5lOiBlLmxpbmUsIGNvbDogZS5jb2wgfSksIG1lc3NhZ2U6IGUubWVzc2FnZSB9KTtcbiAgfVxuXG4gIGNvbnN0IHJlcXVpcmVkID0geyBtZXRhZGF0YTogbmV3IFNldChzdHJpbmdMaXN0KHMucmVxdWlyZWRNZXRhZGF0YSkpLCB2YXJzOiBuZXcgU2V0KHN0cmluZ0xpc3Qocy5yZXF1aXJlZFZhcnMpKSB9O1xuICBjb25zdCB0eXBlcyA9IHsgbWV0YWRhdGE6IHN0cmluZ01hcChzLm1ldGFkYXRhVHlwZXMpLCB2YXJzOiBzdHJpbmdNYXAocy52YXJzVHlwZXMpIH07XG4gIGNvbnN0IHNvdXJjZXMgPSBbXG4gICAgLi4uKHR5cGVvZiBzLmRlc2NyaXB0aW9uID09PSAnc3RyaW5nJyA/IFtzLmRlc2NyaXB0aW9uXSA6IFtdKSxcbiAgICAuLi5zdHJpbmdMaXN0KHMuY29tbWFuZHMpLCAuLi5zdHJpbmdMaXN0KHMuaW5zdGFsbENvbW1hbmRzKSwgLi4uc3RyaW5nVmFsdWVzKHMuZW52KSwgLi4uc3RyaW5nVmFsdWVzKHMuYnVpbGRBcmdzKSxcbiAgXTtcbiAgY29uc3Qgc2VlbiA9IG5ldyBTZXQ8c3RyaW5nPigpO1xuICBjb25zdCBhZGQgPSAoaXNzdWU6IFBsdWdpblRlbXBsYXRlSXNzdWUpID0+IHtcbiAgICBjb25zdCBrZXkgPSBgJHtpc3N1ZS5raW5kfVxcdTAwMDAke2lzc3VlLm1lc3NhZ2V9YDtcbiAgICBpZiAoIXNlZW4uaGFzKGtleSkpIHsgc2Vlbi5hZGQoa2V5KTsgaXNzdWVzLnB1c2goaXNzdWUpOyB9XG4gIH07XG5cbiAgZm9yIChjb25zdCBzb3VyY2Ugb2Ygc291cmNlcykge1xuICAgIGlmICghc291cmNlLmluY2x1ZGVzKCd7eycpKSBjb250aW51ZTtcbiAgICBsZXQgdG9rZW5zO1xuICAgIHRyeSB7IHRva2VucyA9IGVuZ2luZS50b2tlbml6ZShzb3VyY2UpOyB9IGNhdGNoIHsgY29udGludWU7IC8qIHJlcG9ydGVkIGFzIGEgYHRlbXBsYXRlYCBpc3N1ZSBhYm92ZSAqLyB9XG4gICAgZm9yIChjb25zdCB0IG9mIHRva2Vucykge1xuICAgICAgaWYgKHQua2luZCAhPT0gJ2V4cHInIHx8ICF0LnBhdGgpIGNvbnRpbnVlO1xuICAgICAgY29uc3QgW3Jvb3QsIHN1Yiwga2V5XSA9IHQucGF0aDtcbiAgICAgIGlmIChyb290ICE9PSAncGlwZWxpbmUnIHx8IChzdWIgIT09ICdtZXRhZGF0YScgJiYgc3ViICE9PSAndmFycycpIHx8ICFrZXkpIGNvbnRpbnVlO1xuICAgICAgY29uc3Qgc2NvcGVQYXRoID0gYHBpcGVsaW5lLiR7c3VifS4ke2tleX1gO1xuICAgICAgY29uc3QgZGVjbGFyZWRJbiA9IHN1YiA9PT0gJ21ldGFkYXRhJyA/ICdyZXF1aXJlZE1ldGFkYXRhJyA6ICdyZXF1aXJlZFZhcnMnO1xuICAgICAgY29uc3QgdHlwZXNJbiA9IHN1YiA9PT0gJ21ldGFkYXRhJyA/ICdtZXRhZGF0YVR5cGVzJyA6ICd2YXJzVHlwZXMnO1xuXG4gICAgICBpZiAodC5kZWZhdWx0VmFsdWUgPT09IHVuZGVmaW5lZCAmJiAhcmVxdWlyZWRbc3ViXS5oYXMoa2V5KSkge1xuICAgICAgICBhZGQoeyBraW5kOiAndW5kZWNsYXJlZCcsIGZpZWxkOiBzY29wZVBhdGgsIG1lc3NhZ2U6IGAke3Njb3BlUGF0aH0gaXMgbm90IGRlY2xhcmVkIGluICcke2RlY2xhcmVkSW59JyAoZGVjbGFyZSBpdCwgb3IgYWRkICd8IGRlZmF1bHQ6IC4uLicpYCB9KTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IGV4cGVjdGVkID0gdC5jb2VyY2UgPyBDT0VSQ0VfVE9fVFlQRVt0LmNvZXJjZV0gOiB1bmRlZmluZWQ7XG4gICAgICBpZiAoZXhwZWN0ZWQpIHtcbiAgICAgICAgLy8gU2hpcHBlZCBzcGVjcyBzcGVsbCB0aGUgYm9vbGVhbiB0eXBlIGJvdGggYGJvb2xgIGFuZCBgYm9vbGVhbmA7IHRoZVxuICAgICAgICAvLyBzdG9yZWQgY29udHJhY3QgKGFuZCB0aGUgZmlsdGVyKSB1c2UgYGJvb2xgLlxuICAgICAgICBjb25zdCByYXcgPSB0eXBlc1tzdWJdW2tleV0gPz8gJ3N0cmluZyc7XG4gICAgICAgIGNvbnN0IGRlY2xhcmVkID0gcmF3ID09PSAnYm9vbGVhbicgPyAnYm9vbCcgOiByYXc7XG4gICAgICAgIGlmIChkZWNsYXJlZCAhPT0gZXhwZWN0ZWQpIHtcbiAgICAgICAgICBhZGQoe1xuICAgICAgICAgICAga2luZDogJ3R5cGUtbWlzbWF0Y2gnLFxuICAgICAgICAgICAgZmllbGQ6IHNjb3BlUGF0aCxcbiAgICAgICAgICAgIG1lc3NhZ2U6IGAke3Njb3BlUGF0aH0gdXNlcyAnfCAke3QuY29lcmNlfScgYnV0IGRlY2xhcmVkIHR5cGUgaXMgJyR7ZGVjbGFyZWR9JyAoYWRkICcke2tleX06ICR7ZXhwZWN0ZWR9JyB0byAke3R5cGVzSW59KWAsXG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgcmV0dXJuIGlzc3Vlcztcbn1cblxuLyoqIE9uZSBsaW5lIHBlciBpc3N1ZTogYFtjb21tYW5kc1swXToxOjVdIG1lc3NhZ2VgIGZvciB0ZW1wbGF0ZSBpc3N1ZXMsIHRoZSBtZXNzYWdlIG90aGVyd2lzZS4gKi9cbmV4cG9ydCBmdW5jdGlvbiBmb3JtYXRQbHVnaW5UZW1wbGF0ZUlzc3VlKGlzc3VlOiBQbHVnaW5UZW1wbGF0ZUlzc3VlKTogc3RyaW5nIHtcbiAgaWYgKGlzc3VlLmtpbmQgIT09ICd0ZW1wbGF0ZScpIHJldHVybiBpc3N1ZS5tZXNzYWdlO1xuICByZXR1cm4gYFske2lzc3VlLmZpZWxkfSR7aXNzdWUubGluZSAhPT0gdW5kZWZpbmVkID8gYDoke2lzc3VlLmxpbmV9OiR7aXNzdWUuY29sfWAgOiAnJ31dICR7aXNzdWUubWVzc2FnZX1gO1xufVxuIl19
package/package.json CHANGED
@@ -10,7 +10,7 @@
10
10
  "@stylistic/eslint-plugin": "^2",
11
11
  "@types/express": "5.0.6",
12
12
  "@types/jsonwebtoken": "9.0.10",
13
- "@types/node": "26.1.2",
13
+ "@types/node": "^24",
14
14
  "@typescript-eslint/eslint-plugin": "^8",
15
15
  "@typescript-eslint/parser": "^8",
16
16
  "@typescript/native": "npm:typescript@^7.0.2",
@@ -94,10 +94,16 @@
94
94
  ],
95
95
  "metadata-keys": [
96
96
  "lib/types/metadata-keys.d.ts"
97
+ ],
98
+ "feature-flags": [
99
+ "lib/types/feature-flags.d.ts"
100
+ ],
101
+ "plugin-catalog": [
102
+ "lib/types/plugin-catalog.d.ts"
97
103
  ]
98
104
  }
99
105
  },
100
- "version": "4.1.12",
106
+ "version": "4.1.14",
101
107
  "bugs": {
102
108
  "url": "https://github.com/mwashburn160/pipeline-builder/issues"
103
109
  },
@@ -161,11 +167,31 @@
161
167
  "setupFilesAfterEnv": [
162
168
  "<rootDir>/../../jest-env-guard.js"
163
169
  ],
170
+ "collectCoverageFrom": [
171
+ "src/**/*.ts",
172
+ "!src/**/*.d.ts",
173
+ "!src/testing/**",
174
+ "!src/constants/index.ts",
175
+ "!src/errors/index.ts",
176
+ "!src/helpers/index.ts",
177
+ "!src/index.ts",
178
+ "!src/middleware/index.ts",
179
+ "!src/openapi/index.ts",
180
+ "!src/routes/index.ts",
181
+ "!src/services/index.ts",
182
+ "!src/testing/any-fn.ts",
183
+ "!src/testing/index.ts",
184
+ "!src/types/audit-events.ts",
185
+ "!src/types/http.ts",
186
+ "!src/types/index.ts",
187
+ "!src/utils/index.ts",
188
+ "!src/validation/index.ts"
189
+ ],
164
190
  "coverageThreshold": {
165
191
  "global": {
166
192
  "statements": 95,
167
- "branches": 88,
168
- "functions": 88,
193
+ "branches": 91,
194
+ "functions": 91,
169
195
  "lines": 95
170
196
  },
171
197
  "**/src/utils/audit.ts": {
@@ -191,20 +217,59 @@
191
217
  "types": "./lib/types/metadata-keys.d.ts",
192
218
  "default": "./lib/types/metadata-keys.js"
193
219
  },
220
+ "./feature-flags": {
221
+ "types": "./lib/types/feature-flags.d.ts",
222
+ "default": "./lib/types/feature-flags.js"
223
+ },
224
+ "./plugin-catalog": {
225
+ "types": "./lib/types/plugin-catalog.d.ts",
226
+ "default": "./lib/types/plugin-catalog.js"
227
+ },
228
+ "./testing": {
229
+ "types": "./lib/testing/index.d.ts",
230
+ "default": "./lib/testing/index.js"
231
+ },
194
232
  "./lib/*": "./lib/*",
195
233
  "./package.json": "./package.json"
196
234
  },
235
+ "nx": {
236
+ "targets": {
237
+ "build": {
238
+ "inputs": [
239
+ "default",
240
+ "^default",
241
+ "sharedGlobals",
242
+ "{workspaceRoot}/docs/environment-variables.md",
243
+ "!{projectRoot}/lib/**/*",
244
+ "!{projectRoot}/dist/**/*"
245
+ ]
246
+ },
247
+ "test": {
248
+ "inputs": [
249
+ "default",
250
+ "^default",
251
+ "sharedGlobals",
252
+ "{workspaceRoot}/docs/environment-variables.md",
253
+ "!{projectRoot}/lib/**/*",
254
+ "!{projectRoot}/dist/**/*"
255
+ ]
256
+ }
257
+ }
258
+ },
197
259
  "//": "~~ Generated by projen. To modify, edit .projenrc.js and run \"pnpm dlx projen\".",
198
260
  "scripts": {
199
261
  "build": "pnpm dlx projen build",
200
262
  "compile": "pnpm dlx projen compile",
201
263
  "default": "pnpm dlx projen default",
202
264
  "eslint": "pnpm dlx projen eslint",
265
+ "lint:fix": "pnpm dlx projen lint:fix",
203
266
  "package": "pnpm dlx projen package",
204
267
  "post-compile": "pnpm dlx projen post-compile",
205
268
  "pre-compile": "pnpm dlx projen pre-compile",
206
269
  "test": "pnpm dlx projen test",
270
+ "test:update": "pnpm dlx projen test:update",
207
271
  "test:watch": "pnpm dlx projen test:watch",
272
+ "typecheck:tests": "pnpm dlx projen typecheck:tests",
208
273
  "watch": "pnpm dlx projen watch",
209
274
  "projen": "pnpm dlx projen"
210
275
  }