@pipeline-builder/pipeline-core 0.0.1
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/LICENSE +202 -0
- package/README.md +69 -0
- package/lib/cdk.d.ts +38 -0
- package/lib/cdk.js +44 -0
- package/lib/config/app-config.d.ts +109 -0
- package/lib/config/app-config.js +197 -0
- package/lib/config/aws-config-cdk.d.ts +49 -0
- package/lib/config/aws-config-cdk.js +65 -0
- package/lib/config/billing-config.d.ts +24 -0
- package/lib/config/billing-config.js +497 -0
- package/lib/config/config-types.d.ts +346 -0
- package/lib/config/config-types.js +4 -0
- package/lib/config/entitlements.d.ts +23 -0
- package/lib/config/entitlements.js +46 -0
- package/lib/config/handler-constants.d.ts +10 -0
- package/lib/config/handler-constants.js +39 -0
- package/lib/config/infrastructure-config.d.ts +66 -0
- package/lib/config/infrastructure-config.js +197 -0
- package/lib/config/server-config.d.ts +53 -0
- package/lib/config/server-config.js +175 -0
- package/lib/config/service-client.d.ts +13 -0
- package/lib/config/service-client.js +20 -0
- package/lib/core/artifact-manager.d.ts +62 -0
- package/lib/core/artifact-manager.js +83 -0
- package/lib/core/id-generator.d.ts +50 -0
- package/lib/core/id-generator.js +50 -0
- package/lib/core/metadata-builder.d.ts +48 -0
- package/lib/core/metadata-builder.js +278 -0
- package/lib/core/metadata-helpers.d.ts +31 -0
- package/lib/core/metadata-helpers.js +55 -0
- package/lib/core/network-types.d.ts +200 -0
- package/lib/core/network-types.js +4 -0
- package/lib/core/network.d.ts +33 -0
- package/lib/core/network.js +112 -0
- package/lib/core/pipeline-helpers.d.ts +61 -0
- package/lib/core/pipeline-helpers.js +563 -0
- package/lib/core/pipeline-types.d.ts +118 -0
- package/lib/core/pipeline-types.js +131 -0
- package/lib/core/role-types.d.ts +254 -0
- package/lib/core/role-types.js +4 -0
- package/lib/core/role.d.ts +14 -0
- package/lib/core/role.js +117 -0
- package/lib/core/security-group-types.d.ts +84 -0
- package/lib/core/security-group-types.js +4 -0
- package/lib/core/security-group.d.ts +14 -0
- package/lib/core/security-group.js +33 -0
- package/lib/handlers/plugin-lookup-handler.d.ts +32 -0
- package/lib/handlers/plugin-lookup-handler.js +292 -0
- package/lib/handlers/pnpm-lock.yaml +12 -0
- package/lib/index.d.ts +45 -0
- package/lib/index.js +53 -0
- package/lib/pipeline/pipeline-builder.d.ts +123 -0
- package/lib/pipeline/pipeline-builder.js +476 -0
- package/lib/pipeline/pipeline-configuration.d.ts +81 -0
- package/lib/pipeline/pipeline-configuration.js +214 -0
- package/lib/pipeline/plugin-lookup.d.ts +131 -0
- package/lib/pipeline/plugin-lookup.js +316 -0
- package/lib/pipeline/plugin-spec.d.ts +138 -0
- package/lib/pipeline/plugin-spec.js +4 -0
- package/lib/pipeline/source-builder.d.ts +62 -0
- package/lib/pipeline/source-builder.js +164 -0
- package/lib/pipeline/source-types.d.ts +221 -0
- package/lib/pipeline/source-types.js +4 -0
- package/lib/pipeline/stage-builder.d.ts +78 -0
- package/lib/pipeline/stage-builder.js +129 -0
- package/lib/pipeline/step-types.d.ts +222 -0
- package/lib/pipeline/step-types.js +4 -0
- package/lib/template/evaluator.d.ts +42 -0
- package/lib/template/evaluator.js +164 -0
- package/lib/template/index.d.ts +34 -0
- package/lib/template/index.js +118 -0
- package/lib/template/metrics.d.ts +10 -0
- package/lib/template/metrics.js +43 -0
- package/lib/template/plugin-resolver.d.ts +8 -0
- package/lib/template/plugin-resolver.js +48 -0
- package/lib/template/tokenizer.d.ts +49 -0
- package/lib/template/tokenizer.js +233 -0
- package/lib/template/topo-sort.d.ts +17 -0
- package/lib/template/topo-sort.js +88 -0
- package/lib/template/validate.d.ts +45 -0
- package/lib/template/validate.js +96 -0
- package/lib/template/walker.d.ts +34 -0
- package/lib/template/walker.js +83 -0
- package/package.json +184 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export { tokenize, hasTemplate, TokenizerError, MAX_FIELD_SIZE_BYTES, MAX_PATH_DEPTH, MAX_IDENTIFIER_LENGTH, type Token, type LiteralToken, type ExprToken, type SourcePosition, } from './tokenizer.js';
|
|
2
|
+
export { resolve, lookupPath, dependencies, type Scope, type EvalError, } from './evaluator.js';
|
|
3
|
+
export { walkAndBind, type WalkEntry, type FieldPredicate, } from './walker.js';
|
|
4
|
+
export { topoSort, type TopoNode, type TopoResult, } from './topo-sort.js';
|
|
5
|
+
export { validateTemplates, detectCycles, allowedScopeRoots, type TemplateError, type ValidationResult, } from './validate.js';
|
|
6
|
+
export { recordResolution, templateResolutionsTotal, templateResolutionDurationMs, } from './metrics.js';
|
|
7
|
+
import { ErrorCode } from '@pipeline-builder/api-core';
|
|
8
|
+
import { type Scope } from './evaluator.js';
|
|
9
|
+
import { type FieldPredicate } from './walker.js';
|
|
10
|
+
export interface ResolveResult {
|
|
11
|
+
errors: Array<{
|
|
12
|
+
code: ErrorCode;
|
|
13
|
+
message: string;
|
|
14
|
+
field?: string;
|
|
15
|
+
path?: string;
|
|
16
|
+
}>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Resolve all templates inside `doc` by mutating string fields in place.
|
|
20
|
+
* `doc` is treated as opaque — only string fields that match `isTemplatable`
|
|
21
|
+
* are rewritten. Returns collected errors (empty array on success).
|
|
22
|
+
*
|
|
23
|
+
* `docType` is used for metrics; pass 'pipeline' or 'plugin'.
|
|
24
|
+
*/
|
|
25
|
+
export declare function resolveTemplates<T extends object>(doc: T, scope: Scope, isTemplatable: FieldPredicate, docType?: 'pipeline' | 'plugin'): ResolveResult;
|
|
26
|
+
/**
|
|
27
|
+
* Resolve a self-referencing document (e.g. pipeline.json where
|
|
28
|
+
* metadata fields reference each other). Topologically orders the
|
|
29
|
+
* resolution so dependencies come first. Mutates `doc` in place.
|
|
30
|
+
*
|
|
31
|
+
* `fieldToScopePath` maps each field (e.g. `metadata.env`) to the scope
|
|
32
|
+
* path it populates (typically the same, sans any array indices).
|
|
33
|
+
*/
|
|
34
|
+
export declare function resolveSelfReferencing<T extends object>(doc: T, scope: Scope, isTemplatable: FieldPredicate, fieldToScopePath: (field: string) => string | null, docType?: 'pipeline' | 'plugin'): ResolveResult;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
// Copyright 2026 Pipeline Builder Contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
export { tokenize, hasTemplate, TokenizerError, MAX_FIELD_SIZE_BYTES, MAX_PATH_DEPTH, MAX_IDENTIFIER_LENGTH, } from './tokenizer.js';
|
|
4
|
+
export { resolve, lookupPath, dependencies, } from './evaluator.js';
|
|
5
|
+
export { walkAndBind, } from './walker.js';
|
|
6
|
+
export { topoSort, } from './topo-sort.js';
|
|
7
|
+
export { validateTemplates, detectCycles, allowedScopeRoots, } from './validate.js';
|
|
8
|
+
export { recordResolution, templateResolutionsTotal, templateResolutionDurationMs, } from './metrics.js';
|
|
9
|
+
// -- Convenience: high-level resolve() that walks + resolves + measures
|
|
10
|
+
import { ErrorCode } from '@pipeline-builder/api-core';
|
|
11
|
+
import { resolve as evaluatorResolve } from './evaluator.js';
|
|
12
|
+
import { recordResolution } from './metrics.js';
|
|
13
|
+
import { topoSort } from './topo-sort.js';
|
|
14
|
+
import { walkAndBind } from './walker.js';
|
|
15
|
+
/**
|
|
16
|
+
* Resolve all templates inside `doc` by mutating string fields in place.
|
|
17
|
+
* `doc` is treated as opaque — only string fields that match `isTemplatable`
|
|
18
|
+
* are rewritten. Returns collected errors (empty array on success).
|
|
19
|
+
*
|
|
20
|
+
* `docType` is used for metrics; pass 'pipeline' or 'plugin'.
|
|
21
|
+
*/
|
|
22
|
+
export function resolveTemplates(doc, scope, isTemplatable, docType = 'plugin') {
|
|
23
|
+
const start = Date.now();
|
|
24
|
+
const errors = [];
|
|
25
|
+
// includeLiteralOnly: literal-only fields (a bare `{{{{` escape) still need
|
|
26
|
+
// rewriting so the tokenizer's `{{{{` → `{{` unescape lands in the document.
|
|
27
|
+
const entries = walkAndBind(doc, isTemplatable, true);
|
|
28
|
+
let success = true;
|
|
29
|
+
for (const entry of entries) {
|
|
30
|
+
try {
|
|
31
|
+
const value = evaluatorResolve(entry.tokens, scope, entry.field);
|
|
32
|
+
entry.set(value);
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
success = false;
|
|
36
|
+
const e = err;
|
|
37
|
+
errors.push({
|
|
38
|
+
code: e.code ?? ErrorCode.TEMPLATE_PARSE_ERROR,
|
|
39
|
+
message: e.message,
|
|
40
|
+
field: entry.field,
|
|
41
|
+
path: e.path,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
recordResolution(docType, Date.now() - start, success);
|
|
46
|
+
return { errors };
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Resolve a self-referencing document (e.g. pipeline.json where
|
|
50
|
+
* metadata fields reference each other). Topologically orders the
|
|
51
|
+
* resolution so dependencies come first. Mutates `doc` in place.
|
|
52
|
+
*
|
|
53
|
+
* `fieldToScopePath` maps each field (e.g. `metadata.env`) to the scope
|
|
54
|
+
* path it populates (typically the same, sans any array indices).
|
|
55
|
+
*/
|
|
56
|
+
export function resolveSelfReferencing(doc, scope, isTemplatable, fieldToScopePath, docType = 'pipeline') {
|
|
57
|
+
const start = Date.now();
|
|
58
|
+
// includeLiteralOnly: a self-referencing field that is only a `{{{{` escape
|
|
59
|
+
// still needs its unescape written back (same rationale as resolveTemplates).
|
|
60
|
+
const entries = walkAndBind(doc, isTemplatable, true);
|
|
61
|
+
// Build topo nodes keyed by target scope path
|
|
62
|
+
const nodes = entries
|
|
63
|
+
.map(e => {
|
|
64
|
+
const key = fieldToScopePath(e.field);
|
|
65
|
+
if (!key)
|
|
66
|
+
return null;
|
|
67
|
+
const deps = [];
|
|
68
|
+
for (const t of e.tokens)
|
|
69
|
+
if (t.kind === 'expr')
|
|
70
|
+
deps.push(t.path.join('.'));
|
|
71
|
+
return { key, deps, entry: e };
|
|
72
|
+
})
|
|
73
|
+
.filter((n) => n !== null);
|
|
74
|
+
const { ordered, cycles } = topoSort(nodes.map(n => ({ key: n.key, deps: n.deps })));
|
|
75
|
+
const errors = [];
|
|
76
|
+
if (cycles.length) {
|
|
77
|
+
for (const c of cycles) {
|
|
78
|
+
errors.push({
|
|
79
|
+
code: ErrorCode.TEMPLATE_CYCLE,
|
|
80
|
+
message: `Template cycle detected: ${c.join(' -> ')}`,
|
|
81
|
+
path: c.join(' -> '),
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
recordResolution(docType, Date.now() - start, false);
|
|
85
|
+
return { errors };
|
|
86
|
+
}
|
|
87
|
+
// Resolve in topo order; multiple entries may share a scope key (e.g. one
|
|
88
|
+
// pipeline field could map to the same scope path — unlikely here), so
|
|
89
|
+
// iterate all entries whose key == ordered[i].
|
|
90
|
+
const byKey = new Map();
|
|
91
|
+
for (const n of nodes) {
|
|
92
|
+
const list = byKey.get(n.key) ?? [];
|
|
93
|
+
list.push(n);
|
|
94
|
+
byKey.set(n.key, list);
|
|
95
|
+
}
|
|
96
|
+
let success = true;
|
|
97
|
+
for (const key of ordered) {
|
|
98
|
+
for (const n of byKey.get(key) ?? []) {
|
|
99
|
+
try {
|
|
100
|
+
const value = evaluatorResolve(n.entry.tokens, scope, n.entry.field);
|
|
101
|
+
n.entry.set(value);
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
success = false;
|
|
105
|
+
const e = err;
|
|
106
|
+
errors.push({
|
|
107
|
+
code: e.code ?? ErrorCode.TEMPLATE_PARSE_ERROR,
|
|
108
|
+
message: e.message,
|
|
109
|
+
field: n.entry.field,
|
|
110
|
+
path: e.path,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
recordResolution(docType, Date.now() - start, success);
|
|
116
|
+
return { errors };
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdGVtcGxhdGUvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsK0NBQStDO0FBQy9DLHNDQUFzQztBQUV0QyxPQUFPLEVBQ0wsUUFBUSxFQUNSLFdBQVcsRUFDWCxjQUFjLEVBQ2Qsb0JBQW9CLEVBQ3BCLGNBQWMsRUFDZCxxQkFBcUIsR0FLdEIsTUFBTSxnQkFBZ0IsQ0FBQztBQUV4QixPQUFPLEVBQ0wsT0FBTyxFQUNQLFVBQVUsRUFDVixZQUFZLEdBR2IsTUFBTSxnQkFBZ0IsQ0FBQztBQUV4QixPQUFPLEVBQ0wsV0FBVyxHQUdaLE1BQU0sYUFBYSxDQUFDO0FBRXJCLE9BQU8sRUFDTCxRQUFRLEdBR1QsTUFBTSxnQkFBZ0IsQ0FBQztBQUV4QixPQUFPLEVBQ0wsaUJBQWlCLEVBQ2pCLFlBQVksRUFDWixpQkFBaUIsR0FHbEIsTUFBTSxlQUFlLENBQUM7QUFFdkIsT0FBTyxFQUNMLGdCQUFnQixFQUNoQix3QkFBd0IsRUFDeEIsNEJBQTRCLEdBQzdCLE1BQU0sY0FBYyxDQUFDO0FBRXRCLHdFQUF3RTtBQUV4RSxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFDdkQsT0FBTyxFQUFFLE9BQU8sSUFBSSxnQkFBZ0IsRUFBYyxNQUFNLGdCQUFnQixDQUFDO0FBQ3pFLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUNoRCxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDMUMsT0FBTyxFQUFFLFdBQVcsRUFBdUIsTUFBTSxhQUFhLENBQUM7QUFNL0Q7Ozs7OztHQU1HO0FBQ0gsTUFBTSxVQUFVLGdCQUFnQixDQUM5QixHQUFNLEVBQ04sS0FBWSxFQUNaLGFBQTZCLEVBQzdCLE9BQU8sR0FBMEIsUUFBUTtJQUV6QyxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7SUFD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LEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQztZQUNyQixDQUFDO1lBQUMsT0FBTyxHQUFHLEVBQUUsQ0FBQztnQkFDYixPQUFPLEdBQUcsS0FBSyxDQUFDO2dCQUNoQixNQUFNLENBQUMsR0FBRyxHQUFrRCxDQUFDO2dCQUM3RCxNQUFNLENBQUMsSUFBSSxDQUFDO29CQUNWLElBQUksRUFBRSxDQUFDLENBQUMsSUFBSSxJQUFJLFNBQVMsQ0FBQyxvQkFBb0I7b0JBQzlDLE9BQU8sRUFBRSxDQUFDLENBQUMsT0FBTztvQkFDbEIsS0FBSyxFQUFFLENBQUMsQ0FBQyxLQUFLLENBQUMsS0FBSztvQkFDcEIsSUFBSSxFQUFFLENBQUMsQ0FBQyxJQUFJO2lCQUNiLENBQUMsQ0FBQztZQUNMLENBQUM7UUFDSCxDQUFDO0lBQ0gsQ0FBQztJQUNELGdCQUFnQixDQUFDLE9BQU8sRUFBRSxJQUFJLENBQUMsR0FBRyxFQUFFLEdBQUcsS0FBSyxFQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQ3ZELE9BQU8sRUFBRSxNQUFNLEVBQUUsQ0FBQztBQUNwQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbmV4cG9ydCB7XG4gIHRva2VuaXplLFxuICBoYXNUZW1wbGF0ZSxcbiAgVG9rZW5pemVyRXJyb3IsXG4gIE1BWF9GSUVMRF9TSVpFX0JZVEVTLFxuICBNQVhfUEFUSF9ERVBUSCxcbiAgTUFYX0lERU5USUZJRVJfTEVOR1RILFxuICB0eXBlIFRva2VuLFxuICB0eXBlIExpdGVyYWxUb2tlbixcbiAgdHlwZSBFeHByVG9rZW4sXG4gIHR5cGUgU291cmNlUG9zaXRpb24sXG59IGZyb20gJy4vdG9rZW5pemVyLmpzJztcblxuZXhwb3J0IHtcbiAgcmVzb2x2ZSxcbiAgbG9va3VwUGF0aCxcbiAgZGVwZW5kZW5jaWVzLFxuICB0eXBlIFNjb3BlLFxuICB0eXBlIEV2YWxFcnJvcixcbn0gZnJvbSAnLi9ldmFsdWF0b3IuanMnO1xuXG5leHBvcnQge1xuICB3YWxrQW5kQmluZCxcbiAgdHlwZSBXYWxrRW50cnksXG4gIHR5cGUgRmllbGRQcmVkaWNhdGUsXG59IGZyb20gJy4vd2Fsa2VyLmpzJztcblxuZXhwb3J0IHtcbiAgdG9wb1NvcnQsXG4gIHR5cGUgVG9wb05vZGUsXG4gIHR5cGUgVG9wb1Jlc3VsdCxcbn0gZnJvbSAnLi90b3BvLXNvcnQuanMnO1xuXG5leHBvcnQge1xuICB2YWxpZGF0ZVRlbXBsYXRlcyxcbiAgZGV0ZWN0Q3ljbGVzLFxuICBhbGxvd2VkU2NvcGVSb290cyxcbiAgdHlwZSBUZW1wbGF0ZUVycm9yLFxuICB0eXBlIFZhbGlkYXRpb25SZXN1bHQsXG59IGZyb20gJy4vdmFsaWRhdGUuanMnO1xuXG5leHBvcnQge1xuICByZWNvcmRSZXNvbHV0aW9uLFxuICB0ZW1wbGF0ZVJlc29sdXRpb25zVG90YWwsXG4gIHRlbXBsYXRlUmVzb2x1dGlvbkR1cmF0aW9uTXMsXG59IGZyb20gJy4vbWV0cmljcy5qcyc7XG5cbi8vIC0tIENvbnZlbmllbmNlOiBoaWdoLWxldmVsIHJlc29sdmUoKSB0aGF0IHdhbGtzICsgcmVzb2x2ZXMgKyBtZWFzdXJlc1xuXG5pbXBvcnQgeyBFcnJvckNvZGUgfSBmcm9tICdAcGlwZWxpbmUtYnVpbGRlci9hcGktY29yZSc7XG5pbXBvcnQgeyByZXNvbHZlIGFzIGV2YWx1YXRvclJlc29sdmUsIHR5cGUgU2NvcGUgfSBmcm9tICcuL2V2YWx1YXRvci5qcyc7XG5pbXBvcnQgeyByZWNvcmRSZXNvbHV0aW9uIH0gZnJvbSAnLi9tZXRyaWNzLmpzJztcbmltcG9ydCB7IHRvcG9Tb3J0IH0gZnJvbSAnLi90b3BvLXNvcnQuanMnO1xuaW1wb3J0IHsgd2Fsa0FuZEJpbmQsIHR5cGUgRmllbGRQcmVkaWNhdGUgfSBmcm9tICcuL3dhbGtlci5qcyc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgUmVzb2x2ZVJlc3VsdCB7XG4gIGVycm9yczogQXJyYXk8eyBjb2RlOiBFcnJvckNvZGU7IG1lc3NhZ2U6IHN0cmluZzsgZmllbGQ/OiBzdHJpbmc7IHBhdGg/OiBzdHJpbmcgfT47XG59XG5cbi8qKlxuICogUmVzb2x2ZSBhbGwgdGVtcGxhdGVzIGluc2lkZSBgZG9jYCBieSBtdXRhdGluZyBzdHJpbmcgZmllbGRzIGluIHBsYWNlLlxuICogYGRvY2AgaXMgdHJlYXRlZCBhcyBvcGFxdWUg4oCUIG9ubHkgc3RyaW5nIGZpZWxkcyB0aGF0IG1hdGNoIGBpc1RlbXBsYXRhYmxlYFxuICogYXJlIHJld3JpdHRlbi4gUmV0dXJucyBjb2xsZWN0ZWQgZXJyb3JzIChlbXB0eSBhcnJheSBvbiBzdWNjZXNzKS5cbiAqXG4gKiBgZG9jVHlwZWAgaXMgdXNlZCBmb3IgbWV0cmljczsgcGFzcyAncGlwZWxpbmUnIG9yICdwbHVnaW4nLlxuICovXG5leHBvcnQgZnVuY3Rpb24gcmVzb2x2ZVRlbXBsYXRlczxUIGV4dGVuZHMgb2JqZWN0PihcbiAgZG9jOiBULFxuICBzY29wZTogU2NvcGUsXG4gIGlzVGVtcGxhdGFibGU6IEZpZWxkUHJlZGljYXRlLFxuICBkb2NUeXBlOiAncGlwZWxpbmUnIHwgJ3BsdWdpbicgPSAncGx1Z2luJyxcbik6IFJlc29sdmVSZXN1bHQge1xuICBjb25zdCBzdGFydCA9IERhdGUubm93KCk7XG4gIGNvbnN0IGVycm9yczogUmVzb2x2ZVJlc3VsdFsnZXJyb3JzJ10gPSBbXTtcbiAgLy8gaW5jbHVkZUxpdGVyYWxPbmx5OiBsaXRlcmFsLW9ubHkgZmllbGRzIChhIGJhcmUgYHt7e3tgIGVzY2FwZSkgc3RpbGwgbmVlZFxuICAvLyByZXdyaXRpbmcgc28gdGhlIHRva2VuaXplcidzIGB7e3t7YCDihpIgYHt7YCB1bmVzY2FwZSBsYW5kcyBpbiB0aGUgZG9jdW1lbnQuXG4gIGNvbnN0IGVudHJpZXMgPSB3YWxrQW5kQmluZChkb2MsIGlzVGVtcGxhdGFibGUsIHRydWUpO1xuICBsZXQgc3VjY2VzcyA9IHRydWU7XG4gIGZvciAoY29uc3QgZW50cnkgb2YgZW50cmllcykge1xuICAgIHRyeSB7XG4gICAgICBjb25zdCB2YWx1ZSA9IGV2YWx1YXRvclJlc29sdmUoZW50cnkudG9rZW5zLCBzY29wZSwgZW50cnkuZmllbGQpO1xuICAgICAgZW50cnkuc2V0KHZhbHVlKTtcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIHN1Y2Nlc3MgPSBmYWxzZTtcbiAgICAgIGNvbnN0IGUgPSBlcnIgYXMgRXJyb3IgJiB7IGNvZGU/OiBFcnJvckNvZGU7IHBhdGg/OiBzdHJpbmcgfTtcbiAgICAgIGVycm9ycy5wdXNoKHtcbiAgICAgICAgY29kZTogZS5jb2RlID8/IEVycm9yQ29kZS5URU1QTEFURV9QQVJTRV9FUlJPUixcbiAgICAgICAgbWVzc2FnZTogZS5tZXNzYWdlLFxuICAgICAgICBmaWVsZDogZW50cnkuZmllbGQsXG4gICAgICAgIHBhdGg6IGUucGF0aCxcbiAgICAgIH0pO1xuICAgIH1cbiAgfVxuICByZWNvcmRSZXNvbHV0aW9uKGRvY1R5cGUsIERhdGUubm93KCkgLSBzdGFydCwgc3VjY2Vzcyk7XG4gIHJldHVybiB7IGVycm9ycyB9O1xufVxuXG4vKipcbiAqIFJlc29sdmUgYSBzZWxmLXJlZmVyZW5jaW5nIGRvY3VtZW50IChlLmcuIHBpcGVsaW5lLmpzb24gd2hlcmVcbiAqIG1ldGFkYXRhIGZpZWxkcyByZWZlcmVuY2UgZWFjaCBvdGhlcikuIFRvcG9sb2dpY2FsbHkgb3JkZXJzIHRoZVxuICogcmVzb2x1dGlvbiBzbyBkZXBlbmRlbmNpZXMgY29tZSBmaXJzdC4gTXV0YXRlcyBgZG9jYCBpbiBwbGFjZS5cbiAqXG4gKiBgZmllbGRUb1Njb3BlUGF0aGAgbWFwcyBlYWNoIGZpZWxkIChlLmcuIGBtZXRhZGF0YS5lbnZgKSB0byB0aGUgc2NvcGVcbiAqIHBhdGggaXQgcG9wdWxhdGVzICh0eXBpY2FsbHkgdGhlIHNhbWUsIHNhbnMgYW55IGFycmF5IGluZGljZXMpLlxuICovXG5leHBvcnQgZnVuY3Rpb24gcmVzb2x2ZVNlbGZSZWZlcmVuY2luZzxUIGV4dGVuZHMgb2JqZWN0PihcbiAgZG9jOiBULFxuICBzY29wZTogU2NvcGUsXG4gIGlzVGVtcGxhdGFibGU6IEZpZWxkUHJlZGljYXRlLFxuICBmaWVsZFRvU2NvcGVQYXRoOiAoZmllbGQ6IHN0cmluZykgPT4gc3RyaW5nIHwgbnVsbCxcbiAgZG9jVHlwZTogJ3BpcGVsaW5lJyB8ICdwbHVnaW4nID0gJ3BpcGVsaW5lJyxcbik6IFJlc29sdmVSZXN1bHQge1xuICBjb25zdCBzdGFydCA9IERhdGUubm93KCk7XG4gIC8vIGluY2x1ZGVMaXRlcmFsT25seTogYSBzZWxmLXJlZmVyZW5jaW5nIGZpZWxkIHRoYXQgaXMgb25seSBhIGB7e3t7YCBlc2NhcGVcbiAgLy8gc3RpbGwgbmVlZHMgaXRzIHVuZXNjYXBlIHdyaXR0ZW4gYmFjayAoc2FtZSByYXRpb25hbGUgYXMgcmVzb2x2ZVRlbXBsYXRlcykuXG4gIGNvbnN0IGVudHJpZXMgPSB3YWxrQW5kQmluZChkb2MsIGlzVGVtcGxhdGFibGUsIHRydWUpO1xuICAvLyBCdWlsZCB0b3BvIG5vZGVzIGtleWVkIGJ5IHRhcmdldCBzY29wZSBwYXRoXG4gIGNvbnN0IG5vZGVzID0gZW50cmllc1xuICAgIC5tYXAoZSA9PiB7XG4gICAgICBjb25zdCBrZXkgPSBmaWVsZFRvU2NvcGVQYXRoKGUuZmllbGQpO1xuICAgICAgaWYgKCFrZXkpIHJldHVybiBudWxsO1xuICAgICAgY29uc3QgZGVwczogc3RyaW5nW10gPSBbXTtcbiAgICAgIGZvciAoY29uc3QgdCBvZiBlLnRva2VucykgaWYgKHQua2luZCA9PT0gJ2V4cHInKSBkZXBzLnB1c2godC5wYXRoLmpvaW4oJy4nKSk7XG4gICAgICByZXR1cm4geyBrZXksIGRlcHMsIGVudHJ5OiBlIH07XG4gICAgfSlcbiAgICAuZmlsdGVyKChuKTogbiBpcyBOb25OdWxsYWJsZTx0eXBlb2Ygbj4gPT4gbiAhPT0gbnVsbCk7XG5cbiAgY29uc3QgeyBvcmRlcmVkLCBjeWNsZXMgfSA9IHRvcG9Tb3J0KG5vZGVzLm1hcChuID0+ICh7IGtleTogbi5rZXksIGRlcHM6IG4uZGVwcyB9KSkpO1xuICBjb25zdCBlcnJvcnM6IFJlc29sdmVSZXN1bHRbJ2Vycm9ycyddID0gW107XG4gIGlmIChjeWNsZXMubGVuZ3RoKSB7XG4gICAgZm9yIChjb25zdCBjIG9mIGN5Y2xlcykge1xuICAgICAgZXJyb3JzLnB1c2goe1xuICAgICAgICBjb2RlOiBFcnJvckNvZGUuVEVNUExBVEVfQ1lDTEUsXG4gICAgICAgIG1lc3NhZ2U6IGBUZW1wbGF0ZSBjeWNsZSBkZXRlY3RlZDogJHtjLmpvaW4oJyAtPiAnKX1gLFxuICAgICAgICBwYXRoOiBjLmpvaW4oJyAtPiAnKSxcbiAgICAgIH0pO1xuICAgIH1cbiAgICByZWNvcmRSZXNvbHV0aW9uKGRvY1R5cGUsIERhdGUubm93KCkgLSBzdGFydCwgZmFsc2UpO1xuICAgIHJldHVybiB7IGVycm9ycyB9O1xuICB9XG5cbiAgLy8gUmVzb2x2ZSBpbiB0b3BvIG9yZGVyOyBtdWx0aXBsZSBlbnRyaWVzIG1heSBzaGFyZSBhIHNjb3BlIGtleSAoZS5nLiBvbmVcbiAgLy8gcGlwZWxpbmUgZmllbGQgY291bGQgbWFwIHRvIHRoZSBzYW1lIHNjb3BlIHBhdGgg4oCUIHVubGlrZWx5IGhlcmUpLCBzb1xuICAvLyBpdGVyYXRlIGFsbCBlbnRyaWVzIHdob3NlIGtleSA9PSBvcmRlcmVkW2ldLlxuICBjb25zdCBieUtleSA9IG5ldyBNYXA8c3RyaW5nLCB0eXBlb2Ygbm9kZXM+KCk7XG4gIGZvciAoY29uc3QgbiBvZiBub2Rlcykge1xuICAgIGNvbnN0IGxpc3QgPSBieUtleS5nZXQobi5rZXkpID8/IFtdO1xuICAgIGxpc3QucHVzaChuKTtcbiAgICBieUtleS5zZXQobi5rZXksIGxpc3QpO1xuICB9XG5cbiAgbGV0IHN1Y2Nlc3MgPSB0cnVlO1xuICBmb3IgKGNvbnN0IGtleSBvZiBvcmRlcmVkKSB7XG4gICAgZm9yIChjb25zdCBuIG9mIGJ5S2V5LmdldChrZXkpID8/IFtdKSB7XG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCB2YWx1ZSA9IGV2YWx1YXRvclJlc29sdmUobi5lbnRyeS50b2tlbnMsIHNjb3BlLCBuLmVudHJ5LmZpZWxkKTtcbiAgICAgICAgbi5lbnRyeS5zZXQodmFsdWUpO1xuICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgIHN1Y2Nlc3MgPSBmYWxzZTtcbiAgICAgICAgY29uc3QgZSA9IGVyciBhcyBFcnJvciAmIHsgY29kZT86IEVycm9yQ29kZTsgcGF0aD86IHN0cmluZyB9O1xuICAgICAgICBlcnJvcnMucHVzaCh7XG4gICAgICAgICAgY29kZTogZS5jb2RlID8/IEVycm9yQ29kZS5URU1QTEFURV9QQVJTRV9FUlJPUixcbiAgICAgICAgICBtZXNzYWdlOiBlLm1lc3NhZ2UsXG4gICAgICAgICAgZmllbGQ6IG4uZW50cnkuZmllbGQsXG4gICAgICAgICAgcGF0aDogZS5wYXRoLFxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgcmVjb3JkUmVzb2x1dGlvbihkb2NUeXBlLCBEYXRlLm5vdygpIC0gc3RhcnQsIHN1Y2Nlc3MpO1xuICByZXR1cm4geyBlcnJvcnMgfTtcbn1cblxuIl19
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
interface Counter {
|
|
2
|
+
inc(labels?: Record<string, string>, value?: number): void;
|
|
3
|
+
}
|
|
4
|
+
interface Histogram {
|
|
5
|
+
observe(labels: Record<string, string>, value: number): void;
|
|
6
|
+
}
|
|
7
|
+
export declare const templateResolutionsTotal: Counter;
|
|
8
|
+
export declare const templateResolutionDurationMs: Histogram;
|
|
9
|
+
export declare function recordResolution(doc: 'pipeline' | 'plugin', durationMs: number, success: boolean): void;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Copyright 2026 Pipeline Builder Contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* Prometheus metrics for template resolution. Optional — the module is
|
|
5
|
+
* only wired up when `prom-client` is already present in the host
|
|
6
|
+
* process (as is the case for api-server based services). Falls back to
|
|
7
|
+
* no-op stubs when unavailable so pipeline-core stays usable outside
|
|
8
|
+
* server contexts.
|
|
9
|
+
*/
|
|
10
|
+
import { safeCreateRequire } from '@pipeline-builder/api-core';
|
|
11
|
+
// ESM has no global `require`; safeCreateRequire enables the synchronous,
|
|
12
|
+
// OPTIONAL lazy-load of prom-client below (no-op stubs when it isn't installed).
|
|
13
|
+
// (CJS-bundle safe — see api-core's safe-require.ts.)
|
|
14
|
+
const require = safeCreateRequire(import.meta.url);
|
|
15
|
+
function resolvePromClient() {
|
|
16
|
+
try {
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
18
|
+
return require('prom-client');
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const promClient = resolvePromClient();
|
|
25
|
+
function makeCounter(name, help, labelNames) {
|
|
26
|
+
if (!promClient)
|
|
27
|
+
return { inc: () => { } };
|
|
28
|
+
const existing = promClient.register.getSingleMetric(name);
|
|
29
|
+
return existing ?? new promClient.Counter({ name, help, labelNames });
|
|
30
|
+
}
|
|
31
|
+
function makeHistogram(name, help, labelNames, buckets) {
|
|
32
|
+
if (!promClient)
|
|
33
|
+
return { observe: () => { } };
|
|
34
|
+
const existing = promClient.register.getSingleMetric(name);
|
|
35
|
+
return existing ?? new promClient.Histogram({ name, help, labelNames, buckets });
|
|
36
|
+
}
|
|
37
|
+
export const templateResolutionsTotal = makeCounter('pipeline_builder_template_resolutions_total', 'Total number of template resolution operations', ['outcome', 'doc']);
|
|
38
|
+
export const templateResolutionDurationMs = makeHistogram('pipeline_builder_template_resolution_duration_ms', 'Template resolution duration in milliseconds', ['doc'], [1, 5, 10, 50, 100, 500, 1000]);
|
|
39
|
+
export function recordResolution(doc, durationMs, success) {
|
|
40
|
+
templateResolutionsTotal.inc({ outcome: success ? 'success' : 'error', doc });
|
|
41
|
+
templateResolutionDurationMs.observe({ doc }, durationMs);
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWV0cmljcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy90ZW1wbGF0ZS9tZXRyaWNzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUFFdEM7Ozs7OztHQU1HO0FBRUgsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFFL0QsMEVBQTBFO0FBQzFFLGlGQUFpRjtBQUNqRixzREFBc0Q7QUFDdEQsTUFBTSxPQUFPLEdBQUcsaUJBQWlCLENBQUMsT0FBTyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7QUFLbkQsU0FBUyxpQkFBaUI7SUFDeEIsSUFBSSxDQUFDO1FBQ0gsaUVBQWlFO1FBQ2pFLE9BQU8sT0FBTyxDQUFDLGFBQWEsQ0FBQyxDQUFDO0lBQ2hDLENBQUM7SUFBQyxNQUFNLENBQUM7UUFDUCxPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7QUFDSCxDQUFDO0FBRUQsTUFBTSxVQUFVLEdBQUcsaUJBQWlCLEVBQUUsQ0FBQztBQUV2QyxTQUFTLFdBQVcsQ0FBQyxJQUFZLEVBQUUsSUFBWSxFQUFFLFVBQW9CO0lBQ25FLElBQUksQ0FBQyxVQUFVO1FBQUUsT0FBTyxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBYyxDQUFDLEVBQUUsQ0FBQztJQUN0RCxNQUFNLFFBQVEsR0FBRyxVQUFVLENBQUMsUUFBUSxDQUFDLGVBQWUsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUMzRCxPQUFPLFFBQVEsSUFBSSxJQUFJLFVBQVUsQ0FBQyxPQUFPLENBQUMsRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLFVBQVUsRUFBRSxDQUFDLENBQUM7QUFDeEUsQ0FBQztBQUVELFNBQVMsYUFBYSxDQUFDLElBQVksRUFBRSxJQUFZLEVBQUUsVUFBb0IsRUFBRSxPQUFpQjtJQUN4RixJQUFJLENBQUMsVUFBVTtRQUFFLE9BQU8sRUFBRSxPQUFPLEVBQUUsR0FBRyxFQUFFLEdBQWMsQ0FBQyxFQUFFLENBQUM7SUFDMUQsTUFBTSxRQUFRLEdBQUcsVUFBVSxDQUFDLFFBQVEsQ0FBQyxlQUFlLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDM0QsT0FBTyxRQUFRLElBQUksSUFBSSxVQUFVLENBQUMsU0FBUyxDQUFDLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxVQUFVLEVBQUUsT0FBTyxFQUFFLENBQUMsQ0FBQztBQUNuRixDQUFDO0FBRUQsTUFBTSxDQUFDLE1BQU0sd0JBQXdCLEdBQUcsV0FBVyxDQUNqRCw2Q0FBNkMsRUFDN0MsZ0RBQWdELEVBQ2hELENBQUMsU0FBUyxFQUFFLEtBQUssQ0FBQyxDQUNuQixDQUFDO0FBRUYsTUFBTSxDQUFDLE1BQU0sNEJBQTRCLEdBQUcsYUFBYSxDQUN2RCxrREFBa0QsRUFDbEQsOENBQThDLEVBQzlDLENBQUMsS0FBSyxDQUFDLEVBQ1AsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxJQUFJLENBQUMsQ0FDL0IsQ0FBQztBQUVGLE1BQU0sVUFBVSxnQkFBZ0IsQ0FBQyxHQUEwQixFQUFFLFVBQWtCLEVBQUUsT0FBZ0I7SUFDL0Ysd0JBQXdCLENBQUMsR0FBRyxDQUFDLEVBQUUsT0FBTyxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxPQUFPLEVBQUUsR0FBRyxFQUFFLENBQUMsQ0FBQztJQUM5RSw0QkFBNEIsQ0FBQyxPQUFPLENBQUMsRUFBRSxHQUFHLEVBQUUsRUFBRSxVQUFVLENBQUMsQ0FBQztBQUM1RCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbi8qKlxuICogUHJvbWV0aGV1cyBtZXRyaWNzIGZvciB0ZW1wbGF0ZSByZXNvbHV0aW9uLiBPcHRpb25hbCDigJQgdGhlIG1vZHVsZSBpc1xuICogb25seSB3aXJlZCB1cCB3aGVuIGBwcm9tLWNsaWVudGAgaXMgYWxyZWFkeSBwcmVzZW50IGluIHRoZSBob3N0XG4gKiBwcm9jZXNzIChhcyBpcyB0aGUgY2FzZSBmb3IgYXBpLXNlcnZlciBiYXNlZCBzZXJ2aWNlcykuIEZhbGxzIGJhY2sgdG9cbiAqIG5vLW9wIHN0dWJzIHdoZW4gdW5hdmFpbGFibGUgc28gcGlwZWxpbmUtY29yZSBzdGF5cyB1c2FibGUgb3V0c2lkZVxuICogc2VydmVyIGNvbnRleHRzLlxuICovXG5cbmltcG9ydCB7IHNhZmVDcmVhdGVSZXF1aXJlIH0gZnJvbSAnQHBpcGVsaW5lLWJ1aWxkZXIvYXBpLWNvcmUnO1xuXG4vLyBFU00gaGFzIG5vIGdsb2JhbCBgcmVxdWlyZWA7IHNhZmVDcmVhdGVSZXF1aXJlIGVuYWJsZXMgdGhlIHN5bmNocm9ub3VzLFxuLy8gT1BUSU9OQUwgbGF6eS1sb2FkIG9mIHByb20tY2xpZW50IGJlbG93IChuby1vcCBzdHVicyB3aGVuIGl0IGlzbid0IGluc3RhbGxlZCkuXG4vLyAoQ0pTLWJ1bmRsZSBzYWZlIOKAlCBzZWUgYXBpLWNvcmUncyBzYWZlLXJlcXVpcmUudHMuKVxuY29uc3QgcmVxdWlyZSA9IHNhZmVDcmVhdGVSZXF1aXJlKGltcG9ydC5tZXRhLnVybCk7XG5cbmludGVyZmFjZSBDb3VudGVyIHsgaW5jKGxhYmVscz86IFJlY29yZDxzdHJpbmcsIHN0cmluZz4sIHZhbHVlPzogbnVtYmVyKTogdm9pZCB9XG5pbnRlcmZhY2UgSGlzdG9ncmFtIHsgb2JzZXJ2ZShsYWJlbHM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4sIHZhbHVlOiBudW1iZXIpOiB2b2lkIH1cblxuZnVuY3Rpb24gcmVzb2x2ZVByb21DbGllbnQoKTogYW55IHwgbnVsbCB7XG4gIHRyeSB7XG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1yZXF1aXJlLWltcG9ydHNcbiAgICByZXR1cm4gcmVxdWlyZSgncHJvbS1jbGllbnQnKTtcbiAgfSBjYXRjaCB7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cbn1cblxuY29uc3QgcHJvbUNsaWVudCA9IHJlc29sdmVQcm9tQ2xpZW50KCk7XG5cbmZ1bmN0aW9uIG1ha2VDb3VudGVyKG5hbWU6IHN0cmluZywgaGVscDogc3RyaW5nLCBsYWJlbE5hbWVzOiBzdHJpbmdbXSk6IENvdW50ZXIge1xuICBpZiAoIXByb21DbGllbnQpIHJldHVybiB7IGluYzogKCkgPT4geyAvKiBub29wICovIH0gfTtcbiAgY29uc3QgZXhpc3RpbmcgPSBwcm9tQ2xpZW50LnJlZ2lzdGVyLmdldFNpbmdsZU1ldHJpYyhuYW1lKTtcbiAgcmV0dXJuIGV4aXN0aW5nID8/IG5ldyBwcm9tQ2xpZW50LkNvdW50ZXIoeyBuYW1lLCBoZWxwLCBsYWJlbE5hbWVzIH0pO1xufVxuXG5mdW5jdGlvbiBtYWtlSGlzdG9ncmFtKG5hbWU6IHN0cmluZywgaGVscDogc3RyaW5nLCBsYWJlbE5hbWVzOiBzdHJpbmdbXSwgYnVja2V0czogbnVtYmVyW10pOiBIaXN0b2dyYW0ge1xuICBpZiAoIXByb21DbGllbnQpIHJldHVybiB7IG9ic2VydmU6ICgpID0+IHsgLyogbm9vcCAqLyB9IH07XG4gIGNvbnN0IGV4aXN0aW5nID0gcHJvbUNsaWVudC5yZWdpc3Rlci5nZXRTaW5nbGVNZXRyaWMobmFtZSk7XG4gIHJldHVybiBleGlzdGluZyA/PyBuZXcgcHJvbUNsaWVudC5IaXN0b2dyYW0oeyBuYW1lLCBoZWxwLCBsYWJlbE5hbWVzLCBidWNrZXRzIH0pO1xufVxuXG5leHBvcnQgY29uc3QgdGVtcGxhdGVSZXNvbHV0aW9uc1RvdGFsID0gbWFrZUNvdW50ZXIoXG4gICdwaXBlbGluZV9idWlsZGVyX3RlbXBsYXRlX3Jlc29sdXRpb25zX3RvdGFsJyxcbiAgJ1RvdGFsIG51bWJlciBvZiB0ZW1wbGF0ZSByZXNvbHV0aW9uIG9wZXJhdGlvbnMnLFxuICBbJ291dGNvbWUnLCAnZG9jJ10sXG4pO1xuXG5leHBvcnQgY29uc3QgdGVtcGxhdGVSZXNvbHV0aW9uRHVyYXRpb25NcyA9IG1ha2VIaXN0b2dyYW0oXG4gICdwaXBlbGluZV9idWlsZGVyX3RlbXBsYXRlX3Jlc29sdXRpb25fZHVyYXRpb25fbXMnLFxuICAnVGVtcGxhdGUgcmVzb2x1dGlvbiBkdXJhdGlvbiBpbiBtaWxsaXNlY29uZHMnLFxuICBbJ2RvYyddLFxuICBbMSwgNSwgMTAsIDUwLCAxMDAsIDUwMCwgMTAwMF0sXG4pO1xuXG5leHBvcnQgZnVuY3Rpb24gcmVjb3JkUmVzb2x1dGlvbihkb2M6ICdwaXBlbGluZScgfCAncGx1Z2luJywgZHVyYXRpb25NczogbnVtYmVyLCBzdWNjZXNzOiBib29sZWFuKTogdm9pZCB7XG4gIHRlbXBsYXRlUmVzb2x1dGlvbnNUb3RhbC5pbmMoeyBvdXRjb21lOiBzdWNjZXNzID8gJ3N1Y2Nlc3MnIDogJ2Vycm9yJywgZG9jIH0pO1xuICB0ZW1wbGF0ZVJlc29sdXRpb25EdXJhdGlvbk1zLm9ic2VydmUoeyBkb2MgfSwgZHVyYXRpb25Ncyk7XG59XG4iXX0=
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Plugin } from '@pipeline-builder/pipeline-data';
|
|
2
|
+
export declare function isPluginTemplatableField(field: string): boolean;
|
|
3
|
+
/**
|
|
4
|
+
* Return a shallow clone of `plugin` with all `{{ ... }}` templates
|
|
5
|
+
* resolved against the given pipeline scope. Caller must pre-populate
|
|
6
|
+
* `pipelineScope` with `{ pipeline, plugin, env }` keys.
|
|
7
|
+
*/
|
|
8
|
+
export declare function resolvePluginTemplates(plugin: Plugin, pipelineScope: Record<string, unknown>): Plugin;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Copyright 2026 Pipeline Builder Contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { resolveTemplates } from './index.js';
|
|
4
|
+
/**
|
|
5
|
+
* Fields inside a Plugin record that accept `{{ ... }}` templates.
|
|
6
|
+
*
|
|
7
|
+
* Pure-string leaves only. `name`, `version`, `pluginType`, `computeType`,
|
|
8
|
+
* `timeout`, `secrets`, `failureBehavior`, `requiredMetadata`, `requiredVars`
|
|
9
|
+
* stay literal. `metadata.*` is excluded because CDK-metadata values are
|
|
10
|
+
* structural, not user-interpolated.
|
|
11
|
+
*/
|
|
12
|
+
const TEMPLATABLE_FIELDS = [
|
|
13
|
+
'description',
|
|
14
|
+
'commands', // string[]
|
|
15
|
+
'installCommands', // string[]
|
|
16
|
+
'env', // Record<string, string>
|
|
17
|
+
'buildArgs', // Record<string, string>
|
|
18
|
+
];
|
|
19
|
+
export function isPluginTemplatableField(field) {
|
|
20
|
+
// `commands` / `installCommands` are arrays of strings → entries like 'commands[0]'
|
|
21
|
+
// `env` / `buildArgs` are objects → entries like 'env.STAGE'
|
|
22
|
+
return TEMPLATABLE_FIELDS.some(f => field === f || field.startsWith(`${f}[`) || field.startsWith(`${f}.`));
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Return a shallow clone of `plugin` with all `{{ ... }}` templates
|
|
26
|
+
* resolved against the given pipeline scope. Caller must pre-populate
|
|
27
|
+
* `pipelineScope` with `{ pipeline, plugin, env }` keys.
|
|
28
|
+
*/
|
|
29
|
+
export function resolvePluginTemplates(plugin, pipelineScope) {
|
|
30
|
+
// Deep clone so mutations don't leak back to the caller's Plugin.
|
|
31
|
+
// Structural fields we care about are plain JSON; structuredClone is safe.
|
|
32
|
+
const clone = structuredClone(plugin);
|
|
33
|
+
const scope = {
|
|
34
|
+
...pipelineScope,
|
|
35
|
+
plugin: { name: plugin.name, version: plugin.version },
|
|
36
|
+
env: plugin.env ?? {},
|
|
37
|
+
};
|
|
38
|
+
const { errors } = resolveTemplates(clone, scope, isPluginTemplatableField, 'plugin');
|
|
39
|
+
if (errors.length > 0) {
|
|
40
|
+
// First error wins — resolver errors should never be batched at synth time
|
|
41
|
+
// because a broken template is a programmer error, not a validation step.
|
|
42
|
+
const e = errors[0];
|
|
43
|
+
const msg = `Template resolution failed in plugin "${plugin.name}" at field '${e.field}': ${e.message}`;
|
|
44
|
+
throw new Error(msg);
|
|
45
|
+
}
|
|
46
|
+
return clone;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2luLXJlc29sdmVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3RlbXBsYXRlL3BsdWdpbi1yZXNvbHZlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBR3RDLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLFlBQVksQ0FBQztBQUU5Qzs7Ozs7OztHQU9HO0FBQ0gsTUFBTSxrQkFBa0IsR0FBRztJQUN6QixhQUFhO0lBQ2IsVUFBVSxFQUFFLFdBQVc7SUFDdkIsaUJBQWlCLEVBQUUsV0FBVztJQUM5QixLQUFLLEVBQUUseUJBQXlCO0lBQ2hDLFdBQVcsRUFBRSx5QkFBeUI7Q0FDOUIsQ0FBQztBQUVYLE1BQU0sVUFBVSx3QkFBd0IsQ0FBQyxLQUFhO0lBQ3BELG9GQUFvRjtJQUNwRiw2REFBNkQ7SUFDN0QsT0FBTyxrQkFBa0IsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxLQUFLLEtBQUssQ0FBQyxJQUFJLEtBQUssQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxJQUFJLEtBQUssQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7QUFDN0csQ0FBQztBQUVEOzs7O0dBSUc7QUFDSCxNQUFNLFVBQVUsc0JBQXNCLENBQ3BDLE1BQWMsRUFDZCxhQUFzQztJQUV0QyxrRUFBa0U7SUFDbEUsMkVBQTJFO0lBQzNFLE1BQU0sS0FBSyxHQUFHLGVBQWUsQ0FBQyxNQUFNLENBQXFDLENBQUM7SUFFMUUsTUFBTSxLQUFLLEdBQUc7UUFDWixHQUFHLGFBQWE7UUFDaEIsTUFBTSxFQUFFLEVBQUUsSUFBSSxFQUFFLE1BQU0sQ0FBQyxJQUFJLEVBQUUsT0FBTyxFQUFFLE1BQU0sQ0FBQyxPQUFPLEVBQUU7UUFDdEQsR0FBRyxFQUFFLE1BQU0sQ0FBQyxHQUFHLElBQUksRUFBRTtLQUN0QixDQUFDO0lBRUYsTUFBTSxFQUFFLE1BQU0sRUFBRSxHQUFHLGdCQUFnQixDQUFDLEtBQUssRUFBRSxLQUFLLEVBQUUsd0JBQXdCLEVBQUUsUUFBUSxDQUFDLENBQUM7SUFDdEYsSUFBSSxNQUFNLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRSxDQUFDO1FBQ3RCLDJFQUEyRTtRQUMzRSwwRUFBMEU7UUFDMUUsTUFBTSxDQUFDLEdBQUcsTUFBTSxDQUFDLENBQUMsQ0FBRSxDQUFDO1FBQ3JCLE1BQU0sR0FBRyxHQUFHLHlDQUF5QyxNQUFNLENBQUMsSUFBSSxlQUFlLENBQUMsQ0FBQyxLQUFLLE1BQU0sQ0FBQyxDQUFDLE9BQU8sRUFBRSxDQUFDO1FBQ3hHLE1BQU0sSUFBSSxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDdkIsQ0FBQztJQUNELE9BQU8sS0FBSyxDQUFDO0FBQ2YsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIENvcHlyaWdodCAyMDI2IFBpcGVsaW5lIEJ1aWxkZXIgQ29udHJpYnV0b3JzXG4vLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuXG5pbXBvcnQgdHlwZSB7IFBsdWdpbiB9IGZyb20gJ0BwaXBlbGluZS1idWlsZGVyL3BpcGVsaW5lLWRhdGEnO1xuaW1wb3J0IHsgcmVzb2x2ZVRlbXBsYXRlcyB9IGZyb20gJy4vaW5kZXguanMnO1xuXG4vKipcbiAqIEZpZWxkcyBpbnNpZGUgYSBQbHVnaW4gcmVjb3JkIHRoYXQgYWNjZXB0IGB7eyAuLi4gfX1gIHRlbXBsYXRlcy5cbiAqXG4gKiBQdXJlLXN0cmluZyBsZWF2ZXMgb25seS4gYG5hbWVgLCBgdmVyc2lvbmAsIGBwbHVnaW5UeXBlYCwgYGNvbXB1dGVUeXBlYCxcbiAqIGB0aW1lb3V0YCwgYHNlY3JldHNgLCBgZmFpbHVyZUJlaGF2aW9yYCwgYHJlcXVpcmVkTWV0YWRhdGFgLCBgcmVxdWlyZWRWYXJzYFxuICogc3RheSBsaXRlcmFsLiBgbWV0YWRhdGEuKmAgaXMgZXhjbHVkZWQgYmVjYXVzZSBDREstbWV0YWRhdGEgdmFsdWVzIGFyZVxuICogc3RydWN0dXJhbCwgbm90IHVzZXItaW50ZXJwb2xhdGVkLlxuICovXG5jb25zdCBURU1QTEFUQUJMRV9GSUVMRFMgPSBbXG4gICdkZXNjcmlwdGlvbicsXG4gICdjb21tYW5kcycsIC8vIHN0cmluZ1tdXG4gICdpbnN0YWxsQ29tbWFuZHMnLCAvLyBzdHJpbmdbXVxuICAnZW52JywgLy8gUmVjb3JkPHN0cmluZywgc3RyaW5nPlxuICAnYnVpbGRBcmdzJywgLy8gUmVjb3JkPHN0cmluZywgc3RyaW5nPlxuXSBhcyBjb25zdDtcblxuZXhwb3J0IGZ1bmN0aW9uIGlzUGx1Z2luVGVtcGxhdGFibGVGaWVsZChmaWVsZDogc3RyaW5nKTogYm9vbGVhbiB7XG4gIC8vIGBjb21tYW5kc2AgLyBgaW5zdGFsbENvbW1hbmRzYCBhcmUgYXJyYXlzIG9mIHN0cmluZ3Mg4oaSIGVudHJpZXMgbGlrZSAnY29tbWFuZHNbMF0nXG4gIC8vIGBlbnZgIC8gYGJ1aWxkQXJnc2AgYXJlIG9iamVjdHMg4oaSIGVudHJpZXMgbGlrZSAnZW52LlNUQUdFJ1xuICByZXR1cm4gVEVNUExBVEFCTEVfRklFTERTLnNvbWUoZiA9PiBmaWVsZCA9PT0gZiB8fCBmaWVsZC5zdGFydHNXaXRoKGAke2Z9W2ApIHx8IGZpZWxkLnN0YXJ0c1dpdGgoYCR7Zn0uYCkpO1xufVxuXG4vKipcbiAqIFJldHVybiBhIHNoYWxsb3cgY2xvbmUgb2YgYHBsdWdpbmAgd2l0aCBhbGwgYHt7IC4uLiB9fWAgdGVtcGxhdGVzXG4gKiByZXNvbHZlZCBhZ2FpbnN0IHRoZSBnaXZlbiBwaXBlbGluZSBzY29wZS4gQ2FsbGVyIG11c3QgcHJlLXBvcHVsYXRlXG4gKiBgcGlwZWxpbmVTY29wZWAgd2l0aCBgeyBwaXBlbGluZSwgcGx1Z2luLCBlbnYgfWAga2V5cy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlc29sdmVQbHVnaW5UZW1wbGF0ZXMoXG4gIHBsdWdpbjogUGx1Z2luLFxuICBwaXBlbGluZVNjb3BlOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPixcbik6IFBsdWdpbiB7XG4gIC8vIERlZXAgY2xvbmUgc28gbXV0YXRpb25zIGRvbid0IGxlYWsgYmFjayB0byB0aGUgY2FsbGVyJ3MgUGx1Z2luLlxuICAvLyBTdHJ1Y3R1cmFsIGZpZWxkcyB3ZSBjYXJlIGFib3V0IGFyZSBwbGFpbiBKU09OOyBzdHJ1Y3R1cmVkQ2xvbmUgaXMgc2FmZS5cbiAgY29uc3QgY2xvbmUgPSBzdHJ1Y3R1cmVkQ2xvbmUocGx1Z2luKSBhcyBQbHVnaW4gJiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcblxuICBjb25zdCBzY29wZSA9IHtcbiAgICAuLi5waXBlbGluZVNjb3BlLFxuICAgIHBsdWdpbjogeyBuYW1lOiBwbHVnaW4ubmFtZSwgdmVyc2lvbjogcGx1Z2luLnZlcnNpb24gfSxcbiAgICBlbnY6IHBsdWdpbi5lbnYgPz8ge30sXG4gIH07XG5cbiAgY29uc3QgeyBlcnJvcnMgfSA9IHJlc29sdmVUZW1wbGF0ZXMoY2xvbmUsIHNjb3BlLCBpc1BsdWdpblRlbXBsYXRhYmxlRmllbGQsICdwbHVnaW4nKTtcbiAgaWYgKGVycm9ycy5sZW5ndGggPiAwKSB7XG4gICAgLy8gRmlyc3QgZXJyb3Igd2lucyDigJQgcmVzb2x2ZXIgZXJyb3JzIHNob3VsZCBuZXZlciBiZSBiYXRjaGVkIGF0IHN5bnRoIHRpbWVcbiAgICAvLyBiZWNhdXNlIGEgYnJva2VuIHRlbXBsYXRlIGlzIGEgcHJvZ3JhbW1lciBlcnJvciwgbm90IGEgdmFsaWRhdGlvbiBzdGVwLlxuICAgIGNvbnN0IGUgPSBlcnJvcnNbMF0hO1xuICAgIGNvbnN0IG1zZyA9IGBUZW1wbGF0ZSByZXNvbHV0aW9uIGZhaWxlZCBpbiBwbHVnaW4gXCIke3BsdWdpbi5uYW1lfVwiIGF0IGZpZWxkICcke2UuZmllbGR9JzogJHtlLm1lc3NhZ2V9YDtcbiAgICB0aHJvdyBuZXcgRXJyb3IobXNnKTtcbiAgfVxuICByZXR1cm4gY2xvbmU7XG59XG4iXX0=
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tokenizer for the pipeline-builder template grammar.
|
|
3
|
+
*
|
|
4
|
+
* Grammar:
|
|
5
|
+
* Template := (Literal | Expr)*
|
|
6
|
+
* Expr := "{{" ws Path (ws Filter)? ws "}}"
|
|
7
|
+
* Path := Identifier ("." Identifier)*
|
|
8
|
+
* Identifier := [a-zA-Z_][a-zA-Z0-9_]{0,63}
|
|
9
|
+
* Filter := "|" ws "default" ws ":" ws Quoted
|
|
10
|
+
* Quoted := "'" ... "'" | "\"" ... "\""
|
|
11
|
+
*
|
|
12
|
+
* `{{{{` is the escape sequence for a literal `{{`.
|
|
13
|
+
*/
|
|
14
|
+
export declare const MAX_FIELD_SIZE_BYTES: number;
|
|
15
|
+
export declare const MAX_PATH_DEPTH = 5;
|
|
16
|
+
export declare const MAX_IDENTIFIER_LENGTH = 64;
|
|
17
|
+
export interface SourcePosition {
|
|
18
|
+
line: number;
|
|
19
|
+
col: number;
|
|
20
|
+
}
|
|
21
|
+
export interface LiteralToken {
|
|
22
|
+
kind: 'literal';
|
|
23
|
+
value: string;
|
|
24
|
+
pos: SourcePosition;
|
|
25
|
+
}
|
|
26
|
+
export type CoerceKind = 'number' | 'bool' | 'json';
|
|
27
|
+
export interface ExprToken {
|
|
28
|
+
kind: 'expr';
|
|
29
|
+
path: string[];
|
|
30
|
+
defaultValue?: string;
|
|
31
|
+
coerce?: CoerceKind;
|
|
32
|
+
source: string;
|
|
33
|
+
pos: SourcePosition;
|
|
34
|
+
}
|
|
35
|
+
export type Token = LiteralToken | ExprToken;
|
|
36
|
+
export declare class TokenizerError extends Error {
|
|
37
|
+
readonly pos: SourcePosition;
|
|
38
|
+
constructor(message: string, pos: SourcePosition);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Tokenize a template source string. Never throws on valid input;
|
|
42
|
+
* throws `TokenizerError` with source position on malformed templates.
|
|
43
|
+
*/
|
|
44
|
+
export declare function tokenize(source: string): Token[];
|
|
45
|
+
/**
|
|
46
|
+
* Convenience: returns true if the source contains any templates.
|
|
47
|
+
* Used as a fast-path to skip tokenization for strings that are obviously literal.
|
|
48
|
+
*/
|
|
49
|
+
export declare function hasTemplate(source: string): boolean;
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
// Copyright 2026 Pipeline Builder Contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* Tokenizer for the pipeline-builder template grammar.
|
|
5
|
+
*
|
|
6
|
+
* Grammar:
|
|
7
|
+
* Template := (Literal | Expr)*
|
|
8
|
+
* Expr := "{{" ws Path (ws Filter)? ws "}}"
|
|
9
|
+
* Path := Identifier ("." Identifier)*
|
|
10
|
+
* Identifier := [a-zA-Z_][a-zA-Z0-9_]{0,63}
|
|
11
|
+
* Filter := "|" ws "default" ws ":" ws Quoted
|
|
12
|
+
* Quoted := "'" ... "'" | "\"" ... "\""
|
|
13
|
+
*
|
|
14
|
+
* `{{{{` is the escape sequence for a literal `{{`.
|
|
15
|
+
*/
|
|
16
|
+
export const MAX_FIELD_SIZE_BYTES = 4 * 1024;
|
|
17
|
+
export const MAX_PATH_DEPTH = 5;
|
|
18
|
+
export const MAX_IDENTIFIER_LENGTH = 64;
|
|
19
|
+
export class TokenizerError extends Error {
|
|
20
|
+
pos;
|
|
21
|
+
constructor(message, pos) {
|
|
22
|
+
super(`${message} at line ${pos.line}, col ${pos.col}`);
|
|
23
|
+
this.pos = pos;
|
|
24
|
+
this.name = 'TokenizerError';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Tokenize a template source string. Never throws on valid input;
|
|
29
|
+
* throws `TokenizerError` with source position on malformed templates.
|
|
30
|
+
*/
|
|
31
|
+
export function tokenize(source) {
|
|
32
|
+
// Byte count, not `.length` (UTF-16 code units): the cap is stated in bytes,
|
|
33
|
+
// and multi-byte characters (emoji, non-ASCII) would otherwise be undercounted.
|
|
34
|
+
if (Buffer.byteLength(source, 'utf8') > MAX_FIELD_SIZE_BYTES) {
|
|
35
|
+
throw new TokenizerError(`Field exceeds max size of ${MAX_FIELD_SIZE_BYTES} bytes`, { line: 1, col: 1 });
|
|
36
|
+
}
|
|
37
|
+
const tokens = [];
|
|
38
|
+
let i = 0;
|
|
39
|
+
let line = 1;
|
|
40
|
+
let col = 1;
|
|
41
|
+
let literalStart = 0;
|
|
42
|
+
let literalStartPos = { line: 1, col: 1 };
|
|
43
|
+
const advance = (n) => {
|
|
44
|
+
for (let k = 0; k < n; k++) {
|
|
45
|
+
if (source[i + k] === '\n') {
|
|
46
|
+
line++;
|
|
47
|
+
col = 1;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
col++;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
i += n;
|
|
54
|
+
};
|
|
55
|
+
const flushLiteral = (endIdx) => {
|
|
56
|
+
if (endIdx > literalStart) {
|
|
57
|
+
// Unescape `{{{{` → `{{`
|
|
58
|
+
const raw = source.slice(literalStart, endIdx);
|
|
59
|
+
const value = raw.replace(/\{\{\{\{/g, '{{');
|
|
60
|
+
tokens.push({ kind: 'literal', value, pos: literalStartPos });
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
const startLiteral = () => {
|
|
64
|
+
literalStart = i;
|
|
65
|
+
literalStartPos = { line, col };
|
|
66
|
+
};
|
|
67
|
+
startLiteral();
|
|
68
|
+
while (i < source.length) {
|
|
69
|
+
// Escape: `{{{{` → literal `{{`
|
|
70
|
+
if (source.startsWith('{{{{', i)) {
|
|
71
|
+
advance(4);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
// Expression start
|
|
75
|
+
if (source.startsWith('{{', i)) {
|
|
76
|
+
flushLiteral(i);
|
|
77
|
+
const exprPos = { line, col };
|
|
78
|
+
advance(2);
|
|
79
|
+
const expr = readExpression(source, i, line, col);
|
|
80
|
+
tokens.push({
|
|
81
|
+
kind: 'expr',
|
|
82
|
+
path: expr.path,
|
|
83
|
+
defaultValue: expr.defaultValue,
|
|
84
|
+
coerce: expr.coerce,
|
|
85
|
+
source: source.slice(i - 2, expr.endIdx),
|
|
86
|
+
pos: exprPos,
|
|
87
|
+
});
|
|
88
|
+
advance(expr.endIdx - i);
|
|
89
|
+
startLiteral();
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
// Stray `}}` outside an expression
|
|
93
|
+
if (source.startsWith('}}', i)) {
|
|
94
|
+
throw new TokenizerError("Unexpected '}}' outside expression", { line, col });
|
|
95
|
+
}
|
|
96
|
+
advance(1);
|
|
97
|
+
}
|
|
98
|
+
flushLiteral(i);
|
|
99
|
+
return tokens;
|
|
100
|
+
}
|
|
101
|
+
function readExpression(source, startIdx, startLine, startCol) {
|
|
102
|
+
let i = startIdx;
|
|
103
|
+
let line = startLine;
|
|
104
|
+
let col = startCol;
|
|
105
|
+
const skipWs = () => {
|
|
106
|
+
while (i < source.length && (source[i] === ' ' || source[i] === '\t')) {
|
|
107
|
+
i++;
|
|
108
|
+
col++;
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
const readIdentifier = () => {
|
|
112
|
+
const startI = i;
|
|
113
|
+
if (i >= source.length || !/[a-zA-Z_]/.test(source[i])) {
|
|
114
|
+
throw new TokenizerError('Expected identifier inside template expression', { line, col });
|
|
115
|
+
}
|
|
116
|
+
while (i < source.length && /[a-zA-Z0-9_]/.test(source[i])) {
|
|
117
|
+
i++;
|
|
118
|
+
col++;
|
|
119
|
+
}
|
|
120
|
+
const ident = source.slice(startI, i);
|
|
121
|
+
if (ident.length > MAX_IDENTIFIER_LENGTH) {
|
|
122
|
+
throw new TokenizerError(`Identifier exceeds max length of ${MAX_IDENTIFIER_LENGTH}`, { line, col: col - ident.length });
|
|
123
|
+
}
|
|
124
|
+
return ident;
|
|
125
|
+
};
|
|
126
|
+
const readQuoted = () => {
|
|
127
|
+
const quote = source[i];
|
|
128
|
+
if (quote !== '"' && quote !== "'") {
|
|
129
|
+
throw new TokenizerError('Expected quoted string', { line, col });
|
|
130
|
+
}
|
|
131
|
+
i++;
|
|
132
|
+
col++;
|
|
133
|
+
const parts = [];
|
|
134
|
+
while (i < source.length && source[i] !== quote) {
|
|
135
|
+
if (source[i] === '\\') {
|
|
136
|
+
const next = source[i + 1];
|
|
137
|
+
if (next === '\\' || next === quote) {
|
|
138
|
+
parts.push(next);
|
|
139
|
+
i += 2;
|
|
140
|
+
col += 2;
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
throw new TokenizerError(`Invalid escape '\\${next ?? ''}' in quoted string`, { line, col });
|
|
144
|
+
}
|
|
145
|
+
if (source[i] === '\n') {
|
|
146
|
+
throw new TokenizerError('Unterminated quoted string', { line, col });
|
|
147
|
+
}
|
|
148
|
+
parts.push(source[i]);
|
|
149
|
+
i++;
|
|
150
|
+
col++;
|
|
151
|
+
}
|
|
152
|
+
if (i >= source.length) {
|
|
153
|
+
throw new TokenizerError('Unterminated quoted string', { line, col });
|
|
154
|
+
}
|
|
155
|
+
i++;
|
|
156
|
+
col++; // consume closing quote
|
|
157
|
+
return parts.join('');
|
|
158
|
+
};
|
|
159
|
+
skipWs();
|
|
160
|
+
const path = [readIdentifier()];
|
|
161
|
+
while (i < source.length && source[i] === '.') {
|
|
162
|
+
i++;
|
|
163
|
+
col++;
|
|
164
|
+
path.push(readIdentifier());
|
|
165
|
+
if (path.length > MAX_PATH_DEPTH) {
|
|
166
|
+
throw new TokenizerError(`Path depth exceeds max of ${MAX_PATH_DEPTH}`, { line, col });
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
skipWs();
|
|
170
|
+
let defaultValue;
|
|
171
|
+
let coerce;
|
|
172
|
+
// Filters chain: `| default: 'x' | number`, `| bool`, `| json`, etc.
|
|
173
|
+
// - `default` takes a quoted argument — applied first (before coercion)
|
|
174
|
+
// - `number`, `bool`, `json` are argument-less coercion filters — applied last
|
|
175
|
+
while (source[i] === '|') {
|
|
176
|
+
i++;
|
|
177
|
+
col++;
|
|
178
|
+
skipWs();
|
|
179
|
+
if (source.startsWith('default', i)) {
|
|
180
|
+
if (defaultValue !== undefined) {
|
|
181
|
+
throw new TokenizerError("'default' filter may only appear once", { line, col });
|
|
182
|
+
}
|
|
183
|
+
i += 'default'.length;
|
|
184
|
+
col += 'default'.length;
|
|
185
|
+
skipWs();
|
|
186
|
+
if (source[i] !== ':') {
|
|
187
|
+
throw new TokenizerError("Expected ':' after 'default'", { line, col });
|
|
188
|
+
}
|
|
189
|
+
i++;
|
|
190
|
+
col++;
|
|
191
|
+
skipWs();
|
|
192
|
+
defaultValue = readQuoted();
|
|
193
|
+
skipWs();
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
let matchedCoerce = null;
|
|
197
|
+
for (const kind of ['number', 'bool', 'json']) {
|
|
198
|
+
if (source.startsWith(kind, i)) {
|
|
199
|
+
// Ensure identifier boundary — `numberish` must not match `number`
|
|
200
|
+
const next = source[i + kind.length];
|
|
201
|
+
if (!next || !/[a-zA-Z0-9_]/.test(next)) {
|
|
202
|
+
matchedCoerce = kind;
|
|
203
|
+
i += kind.length;
|
|
204
|
+
col += kind.length;
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
if (!matchedCoerce) {
|
|
210
|
+
throw new TokenizerError("Unknown filter — supported: 'default', 'number', 'bool', 'json'", { line, col });
|
|
211
|
+
}
|
|
212
|
+
if (coerce) {
|
|
213
|
+
throw new TokenizerError('Only one coercion filter is allowed per expression', { line, col });
|
|
214
|
+
}
|
|
215
|
+
coerce = matchedCoerce;
|
|
216
|
+
skipWs();
|
|
217
|
+
}
|
|
218
|
+
if (!source.startsWith('}}', i)) {
|
|
219
|
+
throw new TokenizerError("Expected '}}'", { line, col });
|
|
220
|
+
}
|
|
221
|
+
i += 2;
|
|
222
|
+
return { path, defaultValue, coerce, endIdx: i };
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Convenience: returns true if the source contains any templates.
|
|
226
|
+
* Used as a fast-path to skip tokenization for strings that are obviously literal.
|
|
227
|
+
*/
|
|
228
|
+
export function hasTemplate(source) {
|
|
229
|
+
// Not completely accurate (a lone `{{{{` would return true) but that's fine —
|
|
230
|
+
// tokenize() will produce a literal-only token list and round-trip correctly.
|
|
231
|
+
return source.includes('{{');
|
|
232
|
+
}
|
|
233
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9rZW5pemVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3RlbXBsYXRlL3Rva2VuaXplci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDOzs7Ozs7Ozs7Ozs7R0FZRztBQUVILE1BQU0sQ0FBQyxNQUFNLG9CQUFvQixHQUFHLENBQUMsR0FBRyxJQUFJLENBQUM7QUFDN0MsTUFBTSxDQUFDLE1BQU0sY0FBYyxHQUFHLENBQUMsQ0FBQztBQUNoQyxNQUFNLENBQUMsTUFBTSxxQkFBcUIsR0FBRyxFQUFFLENBQUM7QUEwQnhDLE1BQU0sT0FBTyxjQUFlLFNBQVEsS0FBSztJQUdyQixHQUFHO0lBRnJCLFlBQ0UsT0FBZSxFQUNDLEdBQW1CO1FBRW5DLEtBQUssQ0FBQyxHQUFHLE9BQU8sWUFBWSxHQUFHLENBQUMsSUFBSSxTQUFTLEdBQUcsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxDQUFDO21CQUZ4QyxHQUFHO1FBR25CLElBQUksQ0FBQyxJQUFJLEdBQUcsZ0JBQWdCLENBQUM7SUFDL0IsQ0FBQztDQUNGO0FBRUQ7OztHQUdHO0FBQ0gsTUFBTSxVQUFVLFFBQVEsQ0FBQyxNQUFjO0lBQ3JDLDZFQUE2RTtJQUM3RSxnRkFBZ0Y7SUFDaEYsSUFBSSxNQUFNLENBQUMsVUFBVSxDQUFDLE1BQU0sRUFBRSxNQUFNLENBQUMsR0FBRyxvQkFBb0IsRUFBRSxDQUFDO1FBQzdELE1BQU0sSUFBSSxjQUFjLENBQ3RCLDZCQUE2QixvQkFBb0IsUUFBUSxFQUN6RCxFQUFFLElBQUksRUFBRSxDQUFDLEVBQUUsR0FBRyxFQUFFLENBQUMsRUFBRSxDQUNwQixDQUFDO0lBQ0osQ0FBQztJQUVELE1BQU0sTUFBTSxHQUFZLEVBQUUsQ0FBQztJQUMzQixJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDVixJQUFJLElBQUksR0FBRyxDQUFDLENBQUM7SUFDYixJQUFJLEdBQUcsR0FBRyxDQUFDLENBQUM7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LFlBQVksRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLENBQUMsRUFBRSxDQUFDO0FBQ25ELENBQUM7QUFFRDs7O0dBR0c7QUFDSCxNQUFNLFVBQVUsV0FBVyxDQUFDLE1BQWM7SUFDeEMsOEVBQThFO0lBQzlFLDhFQUE4RTtJQUM5RSxPQUFPLE1BQU0sQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLENBQUM7QUFDL0IsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIENvcHlyaWdodCAyMDI2IFBpcGVsaW5lIEJ1aWxkZXIgQ29udHJpYnV0b3JzXG4vLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuXG4vKipcbiAqIFRva2VuaXplciBmb3IgdGhlIHBpcGVsaW5lLWJ1aWxkZXIgdGVtcGxhdGUgZ3JhbW1hci5cbiAqXG4gKiBHcmFtbWFyOlxuICogICBUZW1wbGF0ZSAgIDo9IChMaXRlcmFsIHwgRXhwcikqXG4gKiAgIEV4cHIgICAgICAgOj0gXCJ7e1wiIHdzIFBhdGggKHdzIEZpbHRlcik/IHdzIFwifX1cIlxuICogICBQYXRoICAgICAgIDo9IElkZW50aWZpZXIgKFwiLlwiIElkZW50aWZpZXIpKlxuICogICBJZGVudGlmaWVyIDo9IFthLXpBLVpfXVthLXpBLVowLTlfXXswLDYzfVxuICogICBGaWx0ZXIgICAgIDo9IFwifFwiIHdzIFwiZGVmYXVsdFwiIHdzIFwiOlwiIHdzIFF1b3RlZFxuICogICBRdW90ZWQgICAgIDo9IFwiJ1wiIC4uLiBcIidcIiB8IFwiXFxcIlwiIC4uLiBcIlxcXCJcIlxuICpcbiAqIGB7e3t7YCBpcyB0aGUgZXNjYXBlIHNlcXVlbmNlIGZvciBhIGxpdGVyYWwgYHt7YC5cbiAqL1xuXG5leHBvcnQgY29uc3QgTUFYX0ZJRUxEX1NJWkVfQllURVMgPSA0ICogMTAyNDtcbmV4cG9ydCBjb25zdCBNQVhfUEFUSF9ERVBUSCA9IDU7XG5leHBvcnQgY29uc3QgTUFYX0lERU5USUZJRVJfTEVOR1RIID0gNjQ7XG5cbmV4cG9ydCBpbnRlcmZhY2UgU291cmNlUG9zaXRpb24ge1xuICBsaW5lOiBudW1iZXI7XG4gIGNvbDogbnVtYmVyO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIExpdGVyYWxUb2tlbiB7XG4gIGtpbmQ6ICdsaXRlcmFsJztcbiAgdmFsdWU6IHN0cmluZztcbiAgcG9zOiBTb3VyY2VQb3NpdGlvbjtcbn1cblxuZXhwb3J0IHR5cGUgQ29lcmNlS2luZCA9ICdudW1iZXInIHwgJ2Jvb2wnIHwgJ2pzb24nO1xuXG5leHBvcnQgaW50ZXJmYWNlIEV4cHJUb2tlbiB7XG4gIGtpbmQ6ICdleHByJztcbiAgcGF0aDogc3RyaW5nW107IC8vIGUuZy4gWydwaXBlbGluZScsICdtZXRhZGF0YScsICdlbnYnXVxuICBkZWZhdWx0VmFsdWU/OiBzdHJpbmc7IC8vIHZhbHVlIGZvciBgfCBkZWZhdWx0OiAnLi4uJ2AgZmlsdGVyXG4gIGNvZXJjZT86IENvZXJjZUtpbmQ7IC8vIGB8IG51bWJlcmAsIGB8IGJvb2xgLCBgfCBqc29uYFxuICBzb3VyY2U6IHN0cmluZzsgLy8gb3JpZ2luYWwgXCJ7eyAuLi4gfX1cIiB0ZXh0IChmb3IgZXJyb3IgbWVzc2FnZXMpXG4gIHBvczogU291cmNlUG9zaXRpb247XG59XG5cbmV4cG9ydCB0eXBlIFRva2VuID0gTGl0ZXJhbFRva2VuIHwgRXhwclRva2VuO1xuXG5leHBvcnQgY2xhc3MgVG9rZW5pemVyRXJyb3IgZXh0ZW5kcyBFcnJvciB7XG4gIGNvbnN0cnVjdG9yKFxuICAgIG1lc3NhZ2U6IHN0cmluZyxcbiAgICBwdWJsaWMgcmVhZG9ubHkgcG9zOiBTb3VyY2VQb3NpdGlvbixcbiAgKSB7XG4gICAgc3VwZXIoYCR7bWVzc2FnZX0gYXQgbGluZSAke3Bvcy5saW5lfSwgY29sICR7cG9zLmNvbH1gKTtcbiAgICB0aGlzLm5hbWUgPSAnVG9rZW5pemVyRXJyb3InO1xuICB9XG59XG5cbi8qKlxuICogVG9rZW5pemUgYSB0ZW1wbGF0ZSBzb3VyY2Ugc3RyaW5nLiBOZXZlciB0aHJvd3Mgb24gdmFsaWQgaW5wdXQ7XG4gKiB0aHJvd3MgYFRva2VuaXplckVycm9yYCB3aXRoIHNvdXJjZSBwb3NpdGlvbiBvbiBtYWxmb3JtZWQgdGVtcGxhdGVzLlxuICovXG5leHBvcnQgZnVuY3Rpb24gdG9rZW5pemUoc291cmNlOiBzdHJpbmcpOiBUb2tlbltdIHtcbiAgLy8gQnl0ZSBjb3VudCwgbm90IGAubGVuZ3RoYCAoVVRGLTE2IGNvZGUgdW5pdHMpOiB0aGUgY2FwIGlzIHN0YXRlZCBpbiBieXRlcyxcbiAgLy8gYW5kIG11bHRpLWJ5dGUgY2hhcmFjdGVycyAoZW1vamksIG5vbi1BU0NJSSkgd291bGQgb3RoZXJ3aXNlIGJlIHVuZGVyY291bnRlZC5cbiAgaWYgKEJ1ZmZlci5ieXRlTGVuZ3RoKHNvdXJjZSwgJ3V0ZjgnKSA+IE1BWF9GSUVMRF9TSVpFX0JZVEVTKSB7XG4gICAgdGhyb3cgbmV3IFRva2VuaXplckVycm9yKFxuICAgICAgYEZpZWxkIGV4Y2VlZHMgbWF4IHNpemUgb2YgJHtNQVhfRklFTERfU0laRV9CWVRFU30gYnl0ZXNgLFxuICAgICAgeyBsaW5lOiAxLCBjb2w6IDEgfSxcbiAgICApO1xuICB9XG5cbiAgY29uc3QgdG9rZW5zOiBUb2tlbltdID0gW107XG4gIGxldCBpID0gMDtcbiAgbGV0IGxpbmUgPSAxO1xuICBsZXQgY29sID0gMTtcbiAgbGV0IGxpdGVyYWxTdGFydCA9IDA7XG4gIGxldCBsaXRlcmFsU3RhcnRQb3M6IFNvdXJjZVBvc2l0aW9uID0geyBsaW5lOiAxLCBjb2w6IDEgfTtcblxuICBjb25zdCBhZHZhbmNlID0gKG46IG51bWJlcik6IHZvaWQgPT4ge1xuICAgIGZvciAobGV0IGsgPSAwOyBrIDwgbjsgaysrKSB7XG4gICAgICBpZiAoc291cmNlW2kgKyBrXSA9PT0gJ1xcbicpIHtcbiAgICAgICAgbGluZSsrO1xuICAgICAgICBjb2wgPSAxO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgY29sKys7XG4gICAgICB9XG4gICAgfVxuICAgIGkgKz0gbjtcbiAgfTtcblxuICBjb25zdCBmbHVzaExpdGVyYWwgPSAoZW5kSWR4OiBudW1iZXIpOiB2b2lkID0+IHtcbiAgICBpZiAoZW5kSWR4ID4gbGl0ZXJhbFN0YXJ0KSB7XG4gICAgICAvLyBVbmVzY2FwZSBge3t7e2Ag4oaSIGB7e2BcbiAgICAgIGNvbnN0IHJhdyA9IHNvdXJjZS5zbGljZShsaXRlcmFsU3RhcnQsIGVuZElkeCk7XG4gICAgICBjb25zdCB2YWx1ZSA9IHJhdy5yZXBsYWNlKC9cXHtcXHtcXHtcXHsvZywgJ3t7Jyk7XG4gICAgICB0b2tlbnMucHVzaCh7IGtpbmQ6ICdsaXRlcmFsJywgdmFsdWUsIHBvczogbGl0ZXJhbFN0YXJ0UG9zIH0pO1xuICAgIH1cbiAgfTtcblxuICBjb25zdCBzdGFydExpdGVyYWwgPSAoKTogdm9pZCA9PiB7XG4gICAgbGl0ZXJhbFN0YXJ0ID0gaTtcbiAgICBsaXRlcmFsU3RhcnRQb3MgPSB7IGxpbmUsIGNvbCB9O1xuICB9O1xuXG4gIHN0YXJ0TGl0ZXJhbCgpO1xuXG4gIHdoaWxlIChpIDwgc291cmNlLmxlbmd0aCkge1xuICAgIC8vIEVzY2FwZTogYHt7e3tgIOKGkiBsaXRlcmFsIGB7e2BcbiAgICBpZiAoc291cmNlLnN0YXJ0c1dpdGgoJ3t7e3snLCBpKSkge1xuICAgICAgYWR2YW5jZSg0KTtcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cbiAgICAvLyBFeHByZXNzaW9uIHN0YXJ0XG4gICAgaWYgKHNvdXJjZS5zdGFydHNXaXRoKCd7eycsIGkpKSB7XG4gICAgICBmbHVzaExpdGVyYWwoaSk7XG4gICAgICBjb25zdCBleHByUG9zOiBTb3VyY2VQb3NpdGlvbiA9IHsgbGluZSwgY29sIH07XG4gICAgICBhZHZhbmNlKDIpO1xuICAgICAgY29uc3QgZXhwciA9IHJlYWRFeHByZXNzaW9uKHNvdXJjZSwgaSwgbGluZSwgY29sKTtcbiAgICAgIHRva2Vucy5wdXNoKHtcbiAgICAgICAga2luZDogJ2V4cHInLFxuICAgICAgICBwYXRoOiBleHByLnBhdGgsXG4gICAgICAgIGRlZmF1bHRWYWx1ZTogZXhwci5kZWZhdWx0VmFsdWUsXG4gICAgICAgIGNvZXJjZTogZXhwci5jb2VyY2UsXG4gICAgICAgIHNvdXJjZTogc291cmNlLnNsaWNlKGkgLSAyLCBleHByLmVuZElkeCksXG4gICAgICAgIHBvczogZXhwclBvcyxcbiAgICAgIH0pO1xuICAgICAgYWR2YW5jZShleHByLmVuZElkeCAtIGkpO1xuICAgICAgc3RhcnRMaXRlcmFsKCk7XG4gICAgICBjb250aW51ZTtcbiAgICB9XG4gICAgLy8gU3RyYXkgYH19YCBvdXRzaWRlIGFuIGV4cHJlc3Npb25cbiAgICBpZiAoc291cmNlLnN0YXJ0c1dpdGgoJ319JywgaSkpIHtcbiAgICAgIHRocm93IG5ldyBUb2tlbml6ZXJFcnJvcihcIlVuZXhwZWN0ZWQgJ319JyBvdXRzaWRlIGV4cHJlc3Npb25cIiwgeyBsaW5lLCBjb2wgfSk7XG4gICAgfVxuICAgIGFkdmFuY2UoMSk7XG4gIH1cblxuICBmbHVzaExpdGVyYWwoaSk7XG4gIHJldHVybiB0b2tlbnM7XG59XG5cbmludGVyZmFjZSBQYXJzZWRFeHByIHtcbiAgcGF0aDogc3RyaW5nW107XG4gIGRlZmF1bHRWYWx1ZT86IHN0cmluZztcbiAgY29lcmNlPzogQ29lcmNlS2luZDtcbiAgZW5kSWR4OiBudW1iZXI7XG59XG5cbmZ1bmN0aW9uIHJlYWRFeHByZXNzaW9uKFxuICBzb3VyY2U6IHN0cmluZyxcbiAgc3RhcnRJZHg6IG51bWJlcixcbiAgc3RhcnRMaW5lOiBudW1iZXIsXG4gIHN0YXJ0Q29sOiBudW1iZXIsXG4pOiBQYXJzZWRFeHByIHtcbiAgbGV0IGkgPSBzdGFydElkeDtcbiAgbGV0IGxpbmUgPSBzdGFydExpbmU7XG4gIGxldCBjb2wgPSBzdGFydENvbDtcblxuICBjb25zdCBza2lwV3MgPSAoKTogdm9pZCA9PiB7XG4gICAgd2hpbGUgKGkgPCBzb3VyY2UubGVuZ3RoICYmIChzb3VyY2VbaV0gPT09ICcgJyB8fCBzb3VyY2VbaV0gPT09ICdcXHQnKSkge1xuICAgICAgaSsrO1xuICAgICAgY29sKys7XG4gICAgfVxuICB9O1xuXG4gIGNvbnN0IHJlYWRJZGVudGlmaWVyID0gKCk6IHN0cmluZyA9PiB7XG4gICAgY29uc3Qgc3RhcnRJID0gaTtcbiAgICBpZiAoaSA+PSBzb3VyY2UubGVuZ3RoIHx8ICEvW2EtekEtWl9dLy50ZXN0KHNvdXJjZVtpXSEpKSB7XG4gICAgICB0aHJvdyBuZXcgVG9rZW5pemVyRXJyb3IoXG4gICAgICAgICdFeHBlY3RlZCBpZGVudGlmaWVyIGluc2lkZSB0ZW1wbGF0ZSBleHByZXNzaW9uJyxcbiAgICAgICAgeyBsaW5lLCBjb2wgfSxcbiAgICAgICk7XG4gICAgfVxuICAgIHdoaWxlIChpIDwgc291cmNlLmxlbmd0aCAmJiAvW2EtekEtWjAtOV9dLy50ZXN0KHNvdXJjZVtpXSEpKSB7XG4gICAgICBpKys7XG4gICAgICBjb2wrKztcbiAgICB9XG4gICAgY29uc3QgaWRlbnQgPSBzb3VyY2Uuc2xpY2Uoc3RhcnRJLCBpKTtcbiAgICBpZiAoaWRlbnQubGVuZ3RoID4gTUFYX0lERU5USUZJRVJfTEVOR1RIKSB7XG4gICAgICB0aHJvdyBuZXcgVG9rZW5pemVyRXJyb3IoXG4gICAgICAgIGBJZGVudGlmaWVyIGV4Y2VlZHMgbWF4IGxlbmd0aCBvZiAke01BWF9JREVOVElGSUVSX0xFTkdUSH1gLFxuICAgICAgICB7IGxpbmUsIGNvbDogY29sIC0gaWRlbnQubGVuZ3RoIH0sXG4gICAgICApO1xuICAgIH1cbiAgICByZXR1cm4gaWRlbnQ7XG4gIH07XG5cbiAgY29uc3QgcmVhZFF1b3RlZCA9ICgpOiBzdHJpbmcgPT4ge1xuICAgIGNvbnN0IHF1b3RlID0gc291cmNlW2ldO1xuICAgIGlmIChxdW90ZSAhPT0gJ1wiJyAmJiBxdW90ZSAhPT0gXCInXCIpIHtcbiAgICAgIHRocm93IG5ldyBUb2tlbml6ZXJFcnJvcignRXhwZWN0ZWQgcXVvdGVkIHN0cmluZycsIHsgbGluZSwgY29sIH0pO1xuICAgIH1cbiAgICBpKys7IGNvbCsrO1xuICAgIGNvbnN0IHBhcnRzOiBzdHJpbmdbXSA9IFtdO1xuICAgIHdoaWxlIChpIDwgc291cmNlLmxlbmd0aCAmJiBzb3VyY2VbaV0gIT09IHF1b3RlKSB7XG4gICAgICBpZiAoc291cmNlW2ldID09PSAnXFxcXCcpIHtcbiAgICAgICAgY29uc3QgbmV4dCA9IHNvdXJjZVtpICsgMV07XG4gICAgICAgIGlmIChuZXh0ID09PSAnXFxcXCcgfHwgbmV4dCA9PT0gcXVvdGUpIHtcbiAgICAgICAgICBwYXJ0cy5wdXNoKG5leHQpO1xuICAgICAgICAgIGkgKz0gMjtcbiAgICAgICAgICBjb2wgKz0gMjtcbiAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgfVxuICAgICAgICB0aHJvdyBuZXcgVG9rZW5pemVyRXJyb3IoXG4gICAgICAgICAgYEludmFsaWQgZXNjYXBlICdcXFxcJHtuZXh0ID8/ICcnfScgaW4gcXVvdGVkIHN0cmluZ2AsXG4gICAgICAgICAgeyBsaW5lLCBjb2wgfSxcbiAgICAgICAgKTtcbiAgICAgIH1cbiAgICAgIGlmIChzb3VyY2VbaV0gPT09ICdcXG4nKSB7XG4gICAgICAgIHRocm93IG5ldyBUb2tlbml6ZXJFcnJvcignVW50ZXJtaW5hdGVkIHF1b3RlZCBzdHJpbmcnLCB7IGxpbmUsIGNvbCB9KTtcbiAgICAgIH1cbiAgICAgIHBhcnRzLnB1c2goc291cmNlW2ldISk7XG4gICAgICBpKys7IGNvbCsrO1xuICAgIH1cbiAgICBpZiAoaSA+PSBzb3VyY2UubGVuZ3RoKSB7XG4gICAgICB0aHJvdyBuZXcgVG9rZW5pemVyRXJyb3IoJ1VudGVybWluYXRlZCBxdW90ZWQgc3RyaW5nJywgeyBsaW5lLCBjb2wgfSk7XG4gICAgfVxuICAgIGkrKzsgY29sKys7IC8vIGNvbnN1bWUgY2xvc2luZyBxdW90ZVxuICAgIHJldHVybiBwYXJ0cy5qb2luKCcnKTtcbiAgfTtcblxuICBza2lwV3MoKTtcbiAgY29uc3QgcGF0aDogc3RyaW5nW10gPSBbcmVhZElkZW50aWZpZXIoKV07XG4gIHdoaWxlIChpIDwgc291cmNlLmxlbmd0aCAmJiBzb3VyY2VbaV0gPT09ICcuJykge1xuICAgIGkrKzsgY29sKys7XG4gICAgcGF0aC5wdXNoKHJlYWRJZGVudGlmaWVyKCkpO1xuICAgIGlmIChwYXRoLmxlbmd0aCA+IE1BWF9QQVRIX0RFUFRIKSB7XG4gICAgICB0aHJvdyBuZXcgVG9rZW5pemVyRXJyb3IoXG4gICAgICAgIGBQYXRoIGRlcHRoIGV4Y2VlZHMgbWF4IG9mICR7TUFYX1BBVEhfREVQVEh9YCxcbiAgICAgICAgeyBsaW5lLCBjb2wgfSxcbiAgICAgICk7XG4gICAgfVxuICB9XG4gIHNraXBXcygpO1xuXG4gIGxldCBkZWZhdWx0VmFsdWU6IHN0cmluZyB8IHVuZGVmaW5lZDtcbiAgbGV0IGNvZXJjZTogQ29lcmNlS2luZCB8IHVuZGVmaW5lZDtcblxuICAvLyBGaWx0ZXJzIGNoYWluOiBgfCBkZWZhdWx0OiAneCcgfCBudW1iZXJgLCBgfCBib29sYCwgYHwganNvbmAsIGV0Yy5cbiAgLy8gLSBgZGVmYXVsdGAgdGFrZXMgYSBxdW90ZWQgYXJndW1lbnQg4oCUIGFwcGxpZWQgZmlyc3QgKGJlZm9yZSBjb2VyY2lvbilcbiAgLy8gLSBgbnVtYmVyYCwgYGJvb2xgLCBganNvbmAgYXJlIGFyZ3VtZW50LWxlc3MgY29lcmNpb24gZmlsdGVycyDigJQgYXBwbGllZCBsYXN0XG4gIHdoaWxlIChzb3VyY2VbaV0gPT09ICd8Jykge1xuICAgIGkrKzsgY29sKys7XG4gICAgc2tpcFdzKCk7XG5cbiAgICBpZiAoc291cmNlLnN0YXJ0c1dpdGgoJ2RlZmF1bHQnLCBpKSkge1xuICAgICAgaWYgKGRlZmF1bHRWYWx1ZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHRocm93IG5ldyBUb2tlbml6ZXJFcnJvcihcIidkZWZhdWx0JyBmaWx0ZXIgbWF5IG9ubHkgYXBwZWFyIG9uY2VcIiwgeyBsaW5lLCBjb2wgfSk7XG4gICAgICB9XG4gICAgICBpICs9ICdkZWZhdWx0Jy5sZW5ndGg7XG4gICAgICBjb2wgKz0gJ2RlZmF1bHQnLmxlbmd0aDtcbiAgICAgIHNraXBXcygpO1xuICAgICAgaWYgKHNvdXJjZVtpXSAhPT0gJzonKSB7XG4gICAgICAgIHRocm93IG5ldyBUb2tlbml6ZXJFcnJvcihcIkV4cGVjdGVkICc6JyBhZnRlciAnZGVmYXVsdCdcIiwgeyBsaW5lLCBjb2wgfSk7XG4gICAgICB9XG4gICAgICBpKys7IGNvbCsrO1xuICAgICAgc2tpcFdzKCk7XG4gICAgICBkZWZhdWx0VmFsdWUgPSByZWFkUXVvdGVkKCk7XG4gICAgICBza2lwV3MoKTtcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cblxuICAgIGxldCBtYXRjaGVkQ29lcmNlOiBDb2VyY2VLaW5kIHwgbnVsbCA9IG51bGw7XG4gICAgZm9yIChjb25zdCBraW5kIG9mIFsnbnVtYmVyJywgJ2Jvb2wnLCAnanNvbiddIGFzIGNvbnN0KSB7XG4gICAgICBpZiAoc291cmNlLnN0YXJ0c1dpdGgoa2luZCwgaSkpIHtcbiAgICAgICAgLy8gRW5zdXJlIGlkZW50aWZpZXIgYm91bmRhcnkg4oCUIGBudW1iZXJpc2hgIG11c3Qgbm90IG1hdGNoIGBudW1iZXJgXG4gICAgICAgIGNvbnN0IG5leHQgPSBzb3VyY2VbaSArIGtpbmQubGVuZ3RoXTtcbiAgICAgICAgaWYgKCFuZXh0IHx8ICEvW2EtekEtWjAtOV9dLy50ZXN0KG5leHQpKSB7XG4gICAgICAgICAgbWF0Y2hlZENvZXJjZSA9IGtpbmQ7XG4gICAgICAgICAgaSArPSBraW5kLmxlbmd0aDtcbiAgICAgICAgICBjb2wgKz0ga2luZC5sZW5ndGg7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoIW1hdGNoZWRDb2VyY2UpIHtcbiAgICAgIHRocm93IG5ldyBUb2tlbml6ZXJFcnJvcihcbiAgICAgICAgXCJVbmtub3duIGZpbHRlciDigJQgc3VwcG9ydGVkOiAnZGVmYXVsdCcsICdudW1iZXInLCAnYm9vbCcsICdqc29uJ1wiLFxuICAgICAgICB7IGxpbmUsIGNvbCB9LFxuICAgICAgKTtcbiAgICB9XG4gICAgaWYgKGNvZXJjZSkge1xuICAgICAgdGhyb3cgbmV3IFRva2VuaXplckVycm9yKCdPbmx5IG9uZSBjb2VyY2lvbiBmaWx0ZXIgaXMgYWxsb3dlZCBwZXIgZXhwcmVzc2lvbicsIHsgbGluZSwgY29sIH0pO1xuICAgIH1cbiAgICBjb2VyY2UgPSBtYXRjaGVkQ29lcmNlO1xuICAgIHNraXBXcygpO1xuICB9XG5cbiAgaWYgKCFzb3VyY2Uuc3RhcnRzV2l0aCgnfX0nLCBpKSkge1xuICAgIHRocm93IG5ldyBUb2tlbml6ZXJFcnJvcihcIkV4cGVjdGVkICd9fSdcIiwgeyBsaW5lLCBjb2wgfSk7XG4gIH1cbiAgaSArPSAyO1xuICByZXR1cm4geyBwYXRoLCBkZWZhdWx0VmFsdWUsIGNvZXJjZSwgZW5kSWR4OiBpIH07XG59XG5cbi8qKlxuICogQ29udmVuaWVuY2U6IHJldHVybnMgdHJ1ZSBpZiB0aGUgc291cmNlIGNvbnRhaW5zIGFueSB0ZW1wbGF0ZXMuXG4gKiBVc2VkIGFzIGEgZmFzdC1wYXRoIHRvIHNraXAgdG9rZW5pemF0aW9uIGZvciBzdHJpbmdzIHRoYXQgYXJlIG9idmlvdXNseSBsaXRlcmFsLlxuICovXG5leHBvcnQgZnVuY3Rpb24gaGFzVGVtcGxhdGUoc291cmNlOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgLy8gTm90IGNvbXBsZXRlbHkgYWNjdXJhdGUgKGEgbG9uZSBge3t7e2Agd291bGQgcmV0dXJuIHRydWUpIGJ1dCB0aGF0J3MgZmluZSDigJRcbiAgLy8gdG9rZW5pemUoKSB3aWxsIHByb2R1Y2UgYSBsaXRlcmFsLW9ubHkgdG9rZW4gbGlzdCBhbmQgcm91bmQtdHJpcCBjb3JyZWN0bHkuXG4gIHJldHVybiBzb3VyY2UuaW5jbHVkZXMoJ3t7Jyk7XG59XG4iXX0=
|