@harborclient/sdk 1.0.54 → 1.0.55
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/dist/http/substitute.d.ts.map +1 -1
- package/dist/http/substitute.js +2 -11
- package/dist/snippets.d.ts +7 -6
- package/dist/variables/filters.d.ts +33 -0
- package/dist/variables/filters.d.ts.map +1 -0
- package/dist/variables/filters.js +49 -0
- package/dist/variables/index.d.ts +2 -1
- package/dist/variables/index.d.ts.map +1 -1
- package/dist/variables/index.js +2 -1
- package/dist/variables/tokens.d.ts +37 -1
- package/dist/variables/tokens.d.ts.map +1 -1
- package/dist/variables/tokens.js +158 -19
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"substitute.d.ts","sourceRoot":"","sources":["../../src/http/substitute.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"substitute.d.ts","sourceRoot":"","sources":["../../src/http/substitute.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG9C,KAAK,QAAQ,GAAG;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAE7F;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,UAAU,EAChB,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GACnC,UAAU,CAWZ;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,QAAQ,EAAE,EAChB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAClC,QAAQ,EAAE,CAKZ"}
|
package/dist/http/substitute.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { VARIABLE_TOKEN_PATTERN } from '../variables/tokens.js';
|
|
1
|
+
import { substituteVariablesFromMap } from '../variables/tokens.js';
|
|
3
2
|
/**
|
|
4
3
|
* Replaces {{key}} placeholders using a runtime variable map.
|
|
5
4
|
*
|
|
@@ -7,15 +6,7 @@ import { VARIABLE_TOKEN_PATTERN } from '../variables/tokens.js';
|
|
|
7
6
|
* @param runtimeVars - Current runtime variable values.
|
|
8
7
|
*/
|
|
9
8
|
export function substituteVariables(text, runtimeVars) {
|
|
10
|
-
|
|
11
|
-
return text.replace(pattern, (match, key) => {
|
|
12
|
-
const value = runtimeVars[key];
|
|
13
|
-
if (value !== undefined) {
|
|
14
|
-
return value;
|
|
15
|
-
}
|
|
16
|
-
const dynamic = resolveDynamicVariable(key);
|
|
17
|
-
return dynamic !== undefined ? dynamic : match;
|
|
18
|
-
});
|
|
9
|
+
return substituteVariablesFromMap(text, runtimeVars);
|
|
19
10
|
}
|
|
20
11
|
/**
|
|
21
12
|
* Resolves {{variable}} placeholders in auth credential fields.
|
package/dist/snippets.d.ts
CHANGED
|
@@ -11,12 +11,13 @@
|
|
|
11
11
|
* `src/main/scripting/scriptApi.ts` and editor completions in
|
|
12
12
|
* `src/renderer/src/scripting/hcCompletions.ts`.
|
|
13
13
|
*
|
|
14
|
-
* **Import and export** — snippets whose **Name** ends in `.js` (for example
|
|
15
|
-
* `pass-testing.js` or `utils/helpers.js`) may be imported from scripts with
|
|
16
|
-
* relative ESM paths: `import { fn } from './pass-testing.js'`.
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
14
|
+
* **Import and export** — scripts or snippets whose **Name** ends in `.js` (for example
|
|
15
|
+
* `pass-testing.js`, `before.js`, or `utils/helpers.js`) may be imported from scripts with
|
|
16
|
+
* relative ESM paths: `import { fn } from './pass-testing.js'`. Import targets include
|
|
17
|
+
* the snippet library and inline scripts in the same request and collection. Use standard
|
|
18
|
+
* `export function`, `export const`, and `export default` in module source.
|
|
19
|
+
* Cross-module imports are resolved at send time in HarborClient; the SDK does
|
|
20
|
+
* not type-check import paths between snippet or inline script files.
|
|
20
21
|
*
|
|
21
22
|
* **Not supported:** npm package imports, `require`, and Node.js built-ins.
|
|
22
23
|
* See [Request scripts — Snippets](https://harborclient.com/scripting#snippets).
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A filter invocation inside a `{{ variable | filter }}` expression.
|
|
3
|
+
*/
|
|
4
|
+
export interface FilterCall {
|
|
5
|
+
/**
|
|
6
|
+
* Filter name (e.g. `upper`, `urlencode`).
|
|
7
|
+
*/
|
|
8
|
+
name: string;
|
|
9
|
+
/**
|
|
10
|
+
* Positional arguments for future filter support; always empty for now.
|
|
11
|
+
*/
|
|
12
|
+
args: string[];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Registry of Twig-style variable filters.
|
|
16
|
+
*
|
|
17
|
+
* Add or edit entries here to extend filter support. Each filter receives the
|
|
18
|
+
* current piped string value and an argument list (unused until arg support ships).
|
|
19
|
+
*/
|
|
20
|
+
export declare const FILTERS: Record<string, (value: string, args: string[]) => string>;
|
|
21
|
+
/**
|
|
22
|
+
* Sorted list of registered filter names for autocomplete and documentation.
|
|
23
|
+
*/
|
|
24
|
+
export declare const FILTER_NAMES: string[];
|
|
25
|
+
/**
|
|
26
|
+
* Pipes a resolved variable value through a chain of filters left-to-right.
|
|
27
|
+
*
|
|
28
|
+
* @param value - Resolved variable value before filtering.
|
|
29
|
+
* @param filters - Ordered filter calls from the expression.
|
|
30
|
+
* @returns Filtered value, or null when an unknown filter name is encountered.
|
|
31
|
+
*/
|
|
32
|
+
export declare function applyFilters(value: string, filters: FilterCall[]): string | null;
|
|
33
|
+
//# sourceMappingURL=filters.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filters.d.ts","sourceRoot":"","sources":["../../src/variables/filters.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED;;;;;GAKG;AACH,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,MAAM,CAoB7E,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,YAAY,UAA8B,CAAC;AAExD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,MAAM,GAAG,IAAI,CAYhF"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registry of Twig-style variable filters.
|
|
3
|
+
*
|
|
4
|
+
* Add or edit entries here to extend filter support. Each filter receives the
|
|
5
|
+
* current piped string value and an argument list (unused until arg support ships).
|
|
6
|
+
*/
|
|
7
|
+
export const FILTERS = {
|
|
8
|
+
upper: (value) => value.toUpperCase(),
|
|
9
|
+
lower: (value) => value.toLowerCase(),
|
|
10
|
+
urlencode: (value) => encodeURIComponent(value),
|
|
11
|
+
trim: (value) => value.trim(),
|
|
12
|
+
length: (value) => String(value.length),
|
|
13
|
+
striptags: (value) => value.replace(/<[^>]*>/g, ''),
|
|
14
|
+
capitalize: (value) => {
|
|
15
|
+
if (value.length === 0) {
|
|
16
|
+
return value;
|
|
17
|
+
}
|
|
18
|
+
return value.charAt(0).toUpperCase() + value.slice(1).toLowerCase();
|
|
19
|
+
},
|
|
20
|
+
round: (value) => {
|
|
21
|
+
const numeric = Number(value);
|
|
22
|
+
if (Number.isNaN(numeric)) {
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
return String(Math.round(numeric));
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Sorted list of registered filter names for autocomplete and documentation.
|
|
30
|
+
*/
|
|
31
|
+
export const FILTER_NAMES = Object.keys(FILTERS).sort();
|
|
32
|
+
/**
|
|
33
|
+
* Pipes a resolved variable value through a chain of filters left-to-right.
|
|
34
|
+
*
|
|
35
|
+
* @param value - Resolved variable value before filtering.
|
|
36
|
+
* @param filters - Ordered filter calls from the expression.
|
|
37
|
+
* @returns Filtered value, or null when an unknown filter name is encountered.
|
|
38
|
+
*/
|
|
39
|
+
export function applyFilters(value, filters) {
|
|
40
|
+
let current = value;
|
|
41
|
+
for (const filter of filters) {
|
|
42
|
+
const handler = FILTERS[filter.name];
|
|
43
|
+
if (!handler) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
current = handler(current, filter.args);
|
|
47
|
+
}
|
|
48
|
+
return current;
|
|
49
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export { DYNAMIC_VARIABLES, DYNAMIC_VARIABLE_CATEGORIES, DYNAMIC_VARIABLE_NAMES, getDynamicVariableDescription, isDynamicVariable, resolveDynamicVariable, type DynamicVariableDefinition } from './dynamic.js';
|
|
2
|
-
export {
|
|
2
|
+
export { FILTER_NAMES, FILTERS, applyFilters, type FilterCall } from './filters.js';
|
|
3
|
+
export { VARIABLE_NAME_CHARS, VARIABLE_TOKEN_PATTERN, getVariableTokenAtOffset, getVariableTooltipContent, parseVariableTokens, resolveVariable, substituteVariables, substituteVariablesFromMap, substituteVariablesWithResolver, tokenizeVariables, type ParsedVariableToken, type VariableToken, type VariableTokenMatch, type VariableTooltipContent } from './tokens.js';
|
|
3
4
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/variables/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,2BAA2B,EAC3B,sBAAsB,EACtB,6BAA6B,EAC7B,iBAAiB,EACjB,sBAAsB,EACtB,KAAK,yBAAyB,EAC/B,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,wBAAwB,EACxB,yBAAyB,EACzB,eAAe,EACf,mBAAmB,EACnB,iBAAiB,EACjB,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,EAC5B,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/variables/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,2BAA2B,EAC3B,sBAAsB,EACtB,6BAA6B,EAC7B,iBAAiB,EACjB,sBAAsB,EACtB,KAAK,yBAAyB,EAC/B,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AACpF,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,wBAAwB,EACxB,yBAAyB,EACzB,mBAAmB,EACnB,eAAe,EACf,mBAAmB,EACnB,0BAA0B,EAC1B,+BAA+B,EAC/B,iBAAiB,EACjB,KAAK,mBAAmB,EACxB,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,EAC5B,MAAM,aAAa,CAAC"}
|
package/dist/variables/index.js
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
export { DYNAMIC_VARIABLES, DYNAMIC_VARIABLE_CATEGORIES, DYNAMIC_VARIABLE_NAMES, getDynamicVariableDescription, isDynamicVariable, resolveDynamicVariable } from './dynamic.js';
|
|
2
|
-
export {
|
|
2
|
+
export { FILTER_NAMES, FILTERS, applyFilters } from './filters.js';
|
|
3
|
+
export { VARIABLE_NAME_CHARS, VARIABLE_TOKEN_PATTERN, getVariableTokenAtOffset, getVariableTooltipContent, parseVariableTokens, resolveVariable, substituteVariables, substituteVariablesFromMap, substituteVariablesWithResolver, tokenizeVariables } from './tokens.js';
|
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
import type { Variable } from '../types.js';
|
|
2
|
+
import { type FilterCall } from './filters.js';
|
|
2
3
|
/**
|
|
3
4
|
* A segment of text, optionally marking a {{variable}} token.
|
|
4
5
|
*/
|
|
5
6
|
export interface VariableToken {
|
|
6
7
|
text: string;
|
|
7
8
|
key?: string;
|
|
9
|
+
filters?: FilterCall[];
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* A parsed `{{ variable | filter }}` placeholder with source offsets.
|
|
13
|
+
*/
|
|
14
|
+
export interface ParsedVariableToken {
|
|
15
|
+
raw: string;
|
|
16
|
+
key: string;
|
|
17
|
+
filters: FilterCall[];
|
|
18
|
+
start: number;
|
|
19
|
+
end: number;
|
|
8
20
|
}
|
|
9
21
|
/**
|
|
10
22
|
* Allowed characters inside a `{{variable}}` token name (excluding braces).
|
|
@@ -12,9 +24,16 @@ export interface VariableToken {
|
|
|
12
24
|
*/
|
|
13
25
|
export declare const VARIABLE_NAME_CHARS = "\\w$.-";
|
|
14
26
|
/**
|
|
15
|
-
* Global regex matching `{{variableName}}`
|
|
27
|
+
* Global regex matching `{{variableName}}` and filter chains for editor highlighting.
|
|
16
28
|
*/
|
|
17
29
|
export declare const VARIABLE_TOKEN_PATTERN: RegExp;
|
|
30
|
+
/**
|
|
31
|
+
* Scans text for `{{ variable | filter }}` placeholders using a hand-written lexer.
|
|
32
|
+
*
|
|
33
|
+
* @param text - Text containing variable placeholders.
|
|
34
|
+
* @returns Parsed tokens with character offsets; malformed `{{` sequences are skipped.
|
|
35
|
+
*/
|
|
36
|
+
export declare function parseVariableTokens(text: string): ParsedVariableToken[];
|
|
18
37
|
/**
|
|
19
38
|
* Splits text into plain and {{variable}} segments.
|
|
20
39
|
*
|
|
@@ -29,6 +48,7 @@ export interface VariableTokenMatch {
|
|
|
29
48
|
key: string;
|
|
30
49
|
start: number;
|
|
31
50
|
end: number;
|
|
51
|
+
filters?: FilterCall[];
|
|
32
52
|
}
|
|
33
53
|
/**
|
|
34
54
|
* Returns the variable token containing the given character offset, if any.
|
|
@@ -72,4 +92,20 @@ export declare function resolveVariable(key: string, variables: Variable[]): str
|
|
|
72
92
|
* @returns Text with known variables substituted; unknown tokens are left unchanged.
|
|
73
93
|
*/
|
|
74
94
|
export declare function substituteVariables(text: string, variables: Variable[]): string;
|
|
95
|
+
/**
|
|
96
|
+
* Replaces {{key}} placeholders using a custom key resolver.
|
|
97
|
+
*
|
|
98
|
+
* @param text - Text containing variable placeholders.
|
|
99
|
+
* @param resolveKey - Resolver for static/runtime variables; undefined leaves token unchanged.
|
|
100
|
+
* @returns Text with known variables substituted and filters applied.
|
|
101
|
+
*/
|
|
102
|
+
export declare function substituteVariablesWithResolver(text: string, resolveKey: (key: string) => string | undefined): string;
|
|
103
|
+
/**
|
|
104
|
+
* Replaces {{key}} placeholders using a runtime variable map.
|
|
105
|
+
*
|
|
106
|
+
* @param text - Text containing variable placeholders.
|
|
107
|
+
* @param runtimeVars - Current runtime variable values.
|
|
108
|
+
* @returns Text with known variables substituted and filters applied.
|
|
109
|
+
*/
|
|
110
|
+
export declare function substituteVariablesFromMap(text: string, runtimeVars: Record<string, string>): string;
|
|
75
111
|
//# sourceMappingURL=tokens.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/variables/tokens.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/variables/tokens.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,EAAE,KAAK,UAAU,EAAgB,MAAM,cAAc,CAAC;AAE7D;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,UAAU,EAAE,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;GAGG;AACH,eAAO,MAAM,mBAAmB,WAAW,CAAC;AAE5C;;GAEG;AACH,eAAO,MAAM,sBAAsB,QAGlC,CAAC;AA0CF;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,mBAAmB,EAAE,CAmCvE;AAyED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,EAAE,CA0B/D;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,UAAU,EAAE,CAAC;CACxB;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI,CAkBhG;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;CAChB;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,QAAQ,EAAE,GACpB,sBAAsB,CAYxB;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,MAAM,GAAG,SAAS,CAEtF;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,MAAM,CAG/E;AAED;;;;;;GAMG;AACH,wBAAgB,+BAA+B,CAC7C,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,GAC9C,MAAM,CAER;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAClC,MAAM,CAER"}
|
package/dist/variables/tokens.js
CHANGED
|
@@ -1,13 +1,84 @@
|
|
|
1
1
|
import { getDynamicVariableDescription, resolveDynamicVariable } from './dynamic.js';
|
|
2
|
+
import { applyFilters } from './filters.js';
|
|
2
3
|
/**
|
|
3
4
|
* Allowed characters inside a `{{variable}}` token name (excluding braces).
|
|
4
5
|
* Includes `$` so Postman-style dynamic names such as `$randomUUID` are recognized.
|
|
5
6
|
*/
|
|
6
7
|
export const VARIABLE_NAME_CHARS = '\\w$.-';
|
|
7
8
|
/**
|
|
8
|
-
* Global regex matching `{{variableName}}`
|
|
9
|
+
* Global regex matching `{{variableName}}` and filter chains for editor highlighting.
|
|
9
10
|
*/
|
|
10
|
-
export const VARIABLE_TOKEN_PATTERN = new RegExp(`\\{\\{\\s*([${VARIABLE_NAME_CHARS}]+)\\s*\\}\\}`, 'g');
|
|
11
|
+
export const VARIABLE_TOKEN_PATTERN = new RegExp(`\\{\\{\\s*([${VARIABLE_NAME_CHARS}]+)(\\s*\\|\\s*[${VARIABLE_NAME_CHARS}]+)*\\s*\\}\\}`, 'g');
|
|
12
|
+
const VALID_NAME_PATTERN = new RegExp(`^[${VARIABLE_NAME_CHARS}]+$`);
|
|
13
|
+
/**
|
|
14
|
+
* Returns whether a variable key or filter name uses only allowed characters.
|
|
15
|
+
*
|
|
16
|
+
* @param name - Identifier from inside `{{...}}` braces.
|
|
17
|
+
*/
|
|
18
|
+
function isValidName(name) {
|
|
19
|
+
return name.length > 0 && VALID_NAME_PATTERN.test(name);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Parses the inner expression of a `{{...}}` token into a key and filter chain.
|
|
23
|
+
*
|
|
24
|
+
* @param inner - Text between opening and closing braces.
|
|
25
|
+
* @returns Parsed key and filters, or null when malformed.
|
|
26
|
+
*/
|
|
27
|
+
function parseVariableExpression(inner) {
|
|
28
|
+
const segments = inner.split('|').map((segment) => segment.trim());
|
|
29
|
+
if (segments.length === 0 || segments[0] === '') {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
const key = segments[0];
|
|
33
|
+
if (!isValidName(key)) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
const filters = [];
|
|
37
|
+
for (let i = 1; i < segments.length; i++) {
|
|
38
|
+
const name = segments[i];
|
|
39
|
+
if (!isValidName(name)) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
filters.push({ name, args: [] });
|
|
43
|
+
}
|
|
44
|
+
return { key, filters };
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Scans text for `{{ variable | filter }}` placeholders using a hand-written lexer.
|
|
48
|
+
*
|
|
49
|
+
* @param text - Text containing variable placeholders.
|
|
50
|
+
* @returns Parsed tokens with character offsets; malformed `{{` sequences are skipped.
|
|
51
|
+
*/
|
|
52
|
+
export function parseVariableTokens(text) {
|
|
53
|
+
const tokens = [];
|
|
54
|
+
let index = 0;
|
|
55
|
+
while (index < text.length) {
|
|
56
|
+
const open = text.indexOf('{{', index);
|
|
57
|
+
if (open === -1) {
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
const close = text.indexOf('}}', open + 2);
|
|
61
|
+
if (close === -1) {
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
const raw = text.slice(open, close + 2);
|
|
65
|
+
const inner = text.slice(open + 2, close);
|
|
66
|
+
const parsed = parseVariableExpression(inner);
|
|
67
|
+
if (parsed) {
|
|
68
|
+
tokens.push({
|
|
69
|
+
raw,
|
|
70
|
+
key: parsed.key,
|
|
71
|
+
filters: parsed.filters,
|
|
72
|
+
start: open,
|
|
73
|
+
end: close + 2
|
|
74
|
+
});
|
|
75
|
+
index = close + 2;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
index = open + 2;
|
|
79
|
+
}
|
|
80
|
+
return tokens;
|
|
81
|
+
}
|
|
11
82
|
/**
|
|
12
83
|
* Builds a lookup map from collection variables.
|
|
13
84
|
*
|
|
@@ -19,6 +90,51 @@ function variableLookup(variables) {
|
|
|
19
90
|
.filter((v) => v.key.trim())
|
|
20
91
|
.map((v) => [v.key.trim(), v.value !== '' ? v.value : v.defaultValue]));
|
|
21
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* Resolves a variable key to a string value using a lookup function and dynamic variables.
|
|
95
|
+
*
|
|
96
|
+
* @param key - Base variable name from a parsed token.
|
|
97
|
+
* @param resolveKey - Resolver for static/runtime variables.
|
|
98
|
+
* @returns Resolved value, or undefined when the key is not defined.
|
|
99
|
+
*/
|
|
100
|
+
function resolveKeyValue(key, resolveKey) {
|
|
101
|
+
const value = resolveKey(key);
|
|
102
|
+
if (value !== undefined) {
|
|
103
|
+
return value;
|
|
104
|
+
}
|
|
105
|
+
return resolveDynamicVariable(key);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Substitutes parsed variable tokens in text using a key resolver.
|
|
109
|
+
*
|
|
110
|
+
* Unknown variables and unknown filters leave the original token unchanged.
|
|
111
|
+
*
|
|
112
|
+
* @param text - Text containing variable placeholders.
|
|
113
|
+
* @param resolveKey - Resolver for static/runtime variables.
|
|
114
|
+
* @returns Text with known variables substituted and filters applied.
|
|
115
|
+
*/
|
|
116
|
+
function substituteWithResolver(text, resolveKey) {
|
|
117
|
+
const parsedTokens = parseVariableTokens(text);
|
|
118
|
+
if (parsedTokens.length === 0) {
|
|
119
|
+
return text;
|
|
120
|
+
}
|
|
121
|
+
let result = '';
|
|
122
|
+
let lastIndex = 0;
|
|
123
|
+
for (const token of parsedTokens) {
|
|
124
|
+
result += text.slice(lastIndex, token.start);
|
|
125
|
+
const resolved = resolveKeyValue(token.key, resolveKey);
|
|
126
|
+
if (resolved === undefined) {
|
|
127
|
+
result += token.raw;
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
const filtered = applyFilters(resolved, token.filters);
|
|
131
|
+
result += filtered ?? token.raw;
|
|
132
|
+
}
|
|
133
|
+
lastIndex = token.end;
|
|
134
|
+
}
|
|
135
|
+
result += text.slice(lastIndex);
|
|
136
|
+
return result;
|
|
137
|
+
}
|
|
22
138
|
/**
|
|
23
139
|
* Splits text into plain and {{variable}} segments.
|
|
24
140
|
*
|
|
@@ -26,16 +142,22 @@ function variableLookup(variables) {
|
|
|
26
142
|
* @returns Ordered tokens for rendering or further processing.
|
|
27
143
|
*/
|
|
28
144
|
export function tokenizeVariables(text) {
|
|
145
|
+
const parsedTokens = parseVariableTokens(text);
|
|
146
|
+
if (parsedTokens.length === 0) {
|
|
147
|
+
return [{ text }];
|
|
148
|
+
}
|
|
29
149
|
const tokens = [];
|
|
30
|
-
const pattern = new RegExp(VARIABLE_TOKEN_PATTERN.source, 'g');
|
|
31
150
|
let lastIndex = 0;
|
|
32
|
-
for (const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
tokens.push({ text: text.slice(lastIndex, index) });
|
|
151
|
+
for (const token of parsedTokens) {
|
|
152
|
+
if (token.start > lastIndex) {
|
|
153
|
+
tokens.push({ text: text.slice(lastIndex, token.start) });
|
|
36
154
|
}
|
|
37
|
-
tokens.push({
|
|
38
|
-
|
|
155
|
+
tokens.push({
|
|
156
|
+
text: token.raw,
|
|
157
|
+
key: token.key,
|
|
158
|
+
filters: token.filters.length > 0 ? token.filters : undefined
|
|
159
|
+
});
|
|
160
|
+
lastIndex = token.end;
|
|
39
161
|
}
|
|
40
162
|
if (lastIndex < text.length) {
|
|
41
163
|
tokens.push({ text: text.slice(lastIndex) });
|
|
@@ -55,7 +177,12 @@ export function getVariableTokenAtOffset(text, offset) {
|
|
|
55
177
|
const start = position;
|
|
56
178
|
const end = position + token.text.length;
|
|
57
179
|
if (token.key && offset >= start && offset <= end) {
|
|
58
|
-
return {
|
|
180
|
+
return {
|
|
181
|
+
key: token.key,
|
|
182
|
+
start,
|
|
183
|
+
end,
|
|
184
|
+
filters: token.filters
|
|
185
|
+
};
|
|
59
186
|
}
|
|
60
187
|
position = end;
|
|
61
188
|
}
|
|
@@ -101,13 +228,25 @@ export function resolveVariable(key, variables) {
|
|
|
101
228
|
*/
|
|
102
229
|
export function substituteVariables(text, variables) {
|
|
103
230
|
const lookup = variableLookup(variables);
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
231
|
+
return substituteWithResolver(text, (key) => lookup.get(key));
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Replaces {{key}} placeholders using a custom key resolver.
|
|
235
|
+
*
|
|
236
|
+
* @param text - Text containing variable placeholders.
|
|
237
|
+
* @param resolveKey - Resolver for static/runtime variables; undefined leaves token unchanged.
|
|
238
|
+
* @returns Text with known variables substituted and filters applied.
|
|
239
|
+
*/
|
|
240
|
+
export function substituteVariablesWithResolver(text, resolveKey) {
|
|
241
|
+
return substituteWithResolver(text, resolveKey);
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Replaces {{key}} placeholders using a runtime variable map.
|
|
245
|
+
*
|
|
246
|
+
* @param text - Text containing variable placeholders.
|
|
247
|
+
* @param runtimeVars - Current runtime variable values.
|
|
248
|
+
* @returns Text with known variables substituted and filters applied.
|
|
249
|
+
*/
|
|
250
|
+
export function substituteVariablesFromMap(text, runtimeVars) {
|
|
251
|
+
return substituteWithResolver(text, (key) => runtimeVars[key]);
|
|
113
252
|
}
|