@vertesia/jst 1.5.0 → 1.5.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.
@@ -1,2 +1,10 @@
1
+ /**
2
+ * Every helper available to prompt templates: Handlebars' built-ins (`if`, `each`, `with`,
3
+ * `lookup`, ...) plus the custom ones above. Read from the environment itself so it cannot drift.
4
+ */
5
+ export declare const HANDLEBARS_HELPER_NAMES: readonly string[];
6
+ export declare function isHandlebarsHelper(name: string): boolean;
7
+ /** Built-in block helpers that render their block with a new context (`this` is the item). */
8
+ export declare const HANDLEBARS_CONTEXT_HELPERS: readonly string[];
1
9
  export declare function renderHandlebarsTemplate(template: string, input: unknown): string;
2
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/handlebars/index.ts"],"names":[],"mappings":"AAUA,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,CAIjF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/handlebars/index.ts"],"names":[],"mappings":"AA+CA;;;GAGG;AACH,eAAO,MAAM,uBAAuB,EAAE,SAAS,MAAM,EAG1C,CAAC;AAIZ,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAExD;AAED,8FAA8F;AAC9F,eAAO,MAAM,0BAA0B,EAAE,SAAS,MAAM,EAAqB,CAAC;AAE9E,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,CAGjF"}
@@ -1,15 +1,63 @@
1
1
  import Handlebars from 'handlebars';
2
- Handlebars.registerHelper('_now', () => new Date().toISOString());
3
- Handlebars.registerHelper('stringify', (value) => {
2
+ import { TEMPLATE_SYSTEM_VARIABLE_NAMES } from '../system.js';
3
+ /**
4
+ * System variables (see ../system.ts) are injected into the template data. Each one is also
5
+ * registered as a helper returning that injected value, because Handlebars resolves a name
6
+ * differently by position: a bare `{{_now}}` or a subexpression `(_now)` looks up helpers first,
7
+ * while an argument (`{{#if _now}}`) reads the data only. Registering both makes every position
8
+ * render the same value. `_now` falls back to the current time when no value was injected, which
9
+ * keeps templates rendered by callers that do not inject system variables working.
10
+ */
11
+ function systemVariableHelper(name) {
12
+ return (...args) => {
13
+ const options = args[args.length - 1];
14
+ const root = options?.data?.root;
15
+ const value = root && typeof root === 'object' ? root[name] : undefined;
16
+ if (value !== undefined && value !== null) {
17
+ return value;
18
+ }
19
+ return name === '_now' ? new Date().toISOString() : '';
20
+ };
21
+ }
22
+ function stringifyHelper(value) {
4
23
  if (value === null || value === undefined)
5
24
  return '';
6
25
  if (typeof value === 'string')
7
26
  return value;
8
27
  return new Handlebars.SafeString(JSON.stringify(value));
9
- });
28
+ }
29
+ const CUSTOM_HELPERS = {
30
+ ...Object.fromEntries(TEMPLATE_SYSTEM_VARIABLE_NAMES.map((name) => [name, systemVariableHelper(name)])),
31
+ stringify: stringifyHelper,
32
+ };
33
+ /**
34
+ * Prompt templates render with a private Handlebars environment, so helpers another module
35
+ * registers on the global instance never become available to prompts without the validator
36
+ * knowing about them.
37
+ */
38
+ const promptHandlebars = Handlebars.create();
39
+ for (const [name, helper] of Object.entries(CUSTOM_HELPERS)) {
40
+ promptHandlebars.registerHelper(name, helper);
41
+ // Kept on the global instance for code that compiled templates with the global Handlebars
42
+ // after importing this module, which used to be where these helpers were registered.
43
+ Handlebars.registerHelper(name, helper);
44
+ }
45
+ /**
46
+ * Every helper available to prompt templates: Handlebars' built-ins (`if`, `each`, `with`,
47
+ * `lookup`, ...) plus the custom ones above. Read from the environment itself so it cannot drift.
48
+ */
49
+ export const HANDLEBARS_HELPER_NAMES = Object.keys(promptHandlebars.helpers)
50
+ // Internal fallbacks Handlebars invokes itself; templates never call them by name.
51
+ .filter((name) => name !== 'helperMissing' && name !== 'blockHelperMissing')
52
+ .sort();
53
+ const HELPER_NAME_SET = new Set(HANDLEBARS_HELPER_NAMES);
54
+ export function isHandlebarsHelper(name) {
55
+ return HELPER_NAME_SET.has(name);
56
+ }
57
+ /** Built-in block helpers that render their block with a new context (`this` is the item). */
58
+ export const HANDLEBARS_CONTEXT_HELPERS = ['each', 'with'];
10
59
  export function renderHandlebarsTemplate(template, input) {
11
- // lazy load handlebars to speed up initial loading
12
- const compiled = Handlebars.compile(template);
60
+ const compiled = promptHandlebars.compile(template);
13
61
  return compiled(input);
14
62
  }
15
63
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/handlebars/index.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,YAAY,CAAC;AAEpC,UAAU,CAAC,cAAc,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;AAElE,UAAU,CAAC,cAAc,CAAC,WAAW,EAAE,CAAC,KAAc,EAAE,EAAE;IACtD,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IACrD,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5C,OAAO,IAAI,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;AAC5D,CAAC,CAAC,CAAC;AAEH,MAAM,UAAU,wBAAwB,CAAC,QAAgB,EAAE,KAAc;IACrE,mDAAmD;IACnD,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC9C,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC;AAC3B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/handlebars/index.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,8BAA8B,EAAE,MAAM,cAAc,CAAC;AAE9D;;;;;;;GAOG;AACH,SAAS,oBAAoB,CAAC,IAAY;IACtC,OAAO,CAAC,GAAG,IAAe,EAAE,EAAE;QAC1B,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAyC,CAAC;QAC9E,MAAM,IAAI,GAAY,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC;QAC1C,MAAM,KAAK,GAAG,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAE,IAAgC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACrG,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YACxC,OAAO,KAAK,CAAC;QACjB,CAAC;QACD,OAAO,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3D,CAAC,CAAC;AACN,CAAC;AAED,SAAS,eAAe,CAAC,KAAc;IACnC,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IACrD,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5C,OAAO,IAAI,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;AAC5D,CAAC;AAED,MAAM,cAAc,GAA8C;IAC9D,GAAG,MAAM,CAAC,WAAW,CAAC,8BAA8B,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACvG,SAAS,EAAE,eAAe;CAC7B,CAAC;AAEF;;;;GAIG;AACH,MAAM,gBAAgB,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC;AAC7C,KAAK,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,CAAC;IAC1D,gBAAgB,CAAC,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC9C,0FAA0F;IAC1F,qFAAqF;IACrF,UAAU,CAAC,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AAC5C,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAsB,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC;IAC3F,mFAAmF;KAClF,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,eAAe,IAAI,IAAI,KAAK,oBAAoB,CAAC;KAC3E,IAAI,EAAE,CAAC;AAEZ,MAAM,eAAe,GAAwB,IAAI,GAAG,CAAC,uBAAuB,CAAC,CAAC;AAE9E,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC3C,OAAO,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACrC,CAAC;AAED,8FAA8F;AAC9F,MAAM,CAAC,MAAM,0BAA0B,GAAsB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAE9E,MAAM,UAAU,wBAAwB,CAAC,QAAgB,EAAE,KAAc;IACrE,MAAM,QAAQ,GAAG,gBAAgB,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACpD,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC;AAC3B,CAAC"}
package/lib/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
- export { renderHandlebarsTemplate } from './handlebars/index.js';
1
+ export { HANDLEBARS_CONTEXT_HELPERS, HANDLEBARS_HELPER_NAMES, isHandlebarsHelper, renderHandlebarsTemplate, } from './handlebars/index.js';
2
2
  export * from './script.js';
3
+ export * from './system.js';
3
4
  export * from './template.js';
4
5
  export * from './validation.js';
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACjE,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,0BAA0B,EAC1B,uBAAuB,EACvB,kBAAkB,EAClB,wBAAwB,GAC3B,MAAM,uBAAuB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC"}
package/lib/index.js CHANGED
@@ -1,5 +1,6 @@
1
- export { renderHandlebarsTemplate } from './handlebars/index.js';
1
+ export { HANDLEBARS_CONTEXT_HELPERS, HANDLEBARS_HELPER_NAMES, isHandlebarsHelper, renderHandlebarsTemplate, } from './handlebars/index.js';
2
2
  export * from './script.js';
3
+ export * from './system.js';
3
4
  export * from './template.js';
4
5
  export * from './validation.js';
5
6
  //# sourceMappingURL=index.js.map
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACjE,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,0BAA0B,EAC1B,uBAAuB,EACvB,kBAAkB,EAClB,wBAAwB,GAC3B,MAAM,uBAAuB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Registry of the values the prompt runtime supplies to every template, in addition to the
3
+ * caller's input. This is the single source of truth: the renderer injects exactly these
4
+ * names, the validator accepts exactly these names without a schema declaration, and tool
5
+ * descriptions list them from here. Add a system variable here and nowhere else.
6
+ */
7
+ export interface TemplateSystemVariable {
8
+ /** Template name. Always starts with `_` so it cannot collide with ordinary schema properties. */
9
+ name: string;
10
+ /** JSON type of the injected value. */
11
+ type: 'string';
12
+ /** Human-readable description, surfaced in tool descriptions and validation messages. */
13
+ description: string;
14
+ }
15
+ export declare const TEMPLATE_SYSTEM_VARIABLES: readonly [{
16
+ readonly name: '_now';
17
+ readonly type: 'string';
18
+ readonly description: 'Current date and time as an ISO 8601 string (UTC)';
19
+ }, {
20
+ readonly name: '_model';
21
+ readonly type: 'string';
22
+ readonly description: 'Identifier of the model the prompt is executed with';
23
+ }];
24
+ export type TemplateSystemVariableName = (typeof TEMPLATE_SYSTEM_VARIABLES)[number]['name'];
25
+ export declare const TEMPLATE_SYSTEM_VARIABLE_NAMES: readonly TemplateSystemVariableName[];
26
+ export declare function isTemplateSystemVariable(name: string): name is TemplateSystemVariableName;
27
+ /** Runtime context the system variable values are computed from. */
28
+ export interface TemplateSystemContext {
29
+ /** Model id of the execution. `_model` is omitted when unknown. */
30
+ model?: string;
31
+ /** Reference time for `_now`. Defaults to the current time. */
32
+ now?: Date;
33
+ }
34
+ /** Compute the system variable values for one rendering. */
35
+ export declare function buildTemplateSystemVariables(context?: TemplateSystemContext): Record<string, string>;
36
+ /**
37
+ * Return the template data with the system variables added. System values win over caller input
38
+ * of the same name: the names are reserved, so a caller cannot spoof `_model` or `_now`.
39
+ */
40
+ export declare function withTemplateSystemVariables(input: Record<string, unknown>, context?: TemplateSystemContext): Record<string, unknown>;
41
+ /** One-line summary of the system variables, for tool descriptions and error messages. */
42
+ export declare function describeTemplateSystemVariables(): string;
43
+ //# sourceMappingURL=system.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"system.d.ts","sourceRoot":"","sources":["../src/system.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACnC,kGAAkG;IAClG,IAAI,EAAE,MAAM,CAAC;IACb,uCAAuC;IACvC,IAAI,EAAE,QAAQ,CAAC;IACf,yFAAyF;IACzF,WAAW,EAAE,MAAM,CAAC;CACvB;AAED,eAAO,MAAM,yBAAyB;mBAExB,MAAM;mBACN,QAAQ;0BACD,mDAAmD;;mBAG1D,QAAQ;mBACR,QAAQ;0BACD,qDAAqD;EAEpB,CAAC;AAEvD,MAAM,MAAM,0BAA0B,GAAG,CAAC,OAAO,yBAAyB,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5F,eAAO,MAAM,8BAA8B,EAAE,SAAS,0BAA0B,EAE/E,CAAC;AAIF,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,IAAI,0BAA0B,CAEzF;AAED,oEAAoE;AACpE,MAAM,WAAW,qBAAqB;IAClC,mEAAmE;IACnE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+DAA+D;IAC/D,GAAG,CAAC,EAAE,IAAI,CAAC;CACd;AAED,4DAA4D;AAC5D,wBAAgB,4BAA4B,CAAC,OAAO,GAAE,qBAA0B,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAQxG;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CACvC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,OAAO,GAAE,qBAA0B,GACpC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzB;AAED,0FAA0F;AAC1F,wBAAgB,+BAA+B,IAAI,MAAM,CAExD"}
package/lib/system.js ADDED
@@ -0,0 +1,39 @@
1
+ export const TEMPLATE_SYSTEM_VARIABLES = [
2
+ {
3
+ name: '_now',
4
+ type: 'string',
5
+ description: 'Current date and time as an ISO 8601 string (UTC)',
6
+ },
7
+ {
8
+ name: '_model',
9
+ type: 'string',
10
+ description: 'Identifier of the model the prompt is executed with',
11
+ },
12
+ ];
13
+ export const TEMPLATE_SYSTEM_VARIABLE_NAMES = TEMPLATE_SYSTEM_VARIABLES.map((v) => v.name);
14
+ const SYSTEM_VARIABLE_NAME_SET = new Set(TEMPLATE_SYSTEM_VARIABLE_NAMES);
15
+ export function isTemplateSystemVariable(name) {
16
+ return SYSTEM_VARIABLE_NAME_SET.has(name);
17
+ }
18
+ /** Compute the system variable values for one rendering. */
19
+ export function buildTemplateSystemVariables(context = {}) {
20
+ const values = {
21
+ _now: (context.now ?? new Date()).toISOString(),
22
+ };
23
+ if (context.model) {
24
+ values._model = context.model;
25
+ }
26
+ return values;
27
+ }
28
+ /**
29
+ * Return the template data with the system variables added. System values win over caller input
30
+ * of the same name: the names are reserved, so a caller cannot spoof `_model` or `_now`.
31
+ */
32
+ export function withTemplateSystemVariables(input, context = {}) {
33
+ return { ...input, ...buildTemplateSystemVariables(context) };
34
+ }
35
+ /** One-line summary of the system variables, for tool descriptions and error messages. */
36
+ export function describeTemplateSystemVariables() {
37
+ return TEMPLATE_SYSTEM_VARIABLES.map((v) => `${v.name} (${v.description})`).join(', ');
38
+ }
39
+ //# sourceMappingURL=system.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"system.js","sourceRoot":"","sources":["../src/system.ts"],"names":[],"mappings":"AAeA,MAAM,CAAC,MAAM,yBAAyB,GAAG;IACrC;QACI,IAAI,EAAE,MAAM;QACZ,IAAI,EAAE,QAAQ;QACd,WAAW,EAAE,mDAAmD;KACnE;IACD;QACI,IAAI,EAAE,QAAQ;QACd,IAAI,EAAE,QAAQ;QACd,WAAW,EAAE,qDAAqD;KACrE;CACiD,CAAC;AAIvD,MAAM,CAAC,MAAM,8BAA8B,GAA0C,yBAAyB,CAAC,GAAG,CAC9G,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAChB,CAAC;AAEF,MAAM,wBAAwB,GAAwB,IAAI,GAAG,CAAC,8BAA8B,CAAC,CAAC;AAE9F,MAAM,UAAU,wBAAwB,CAAC,IAAY;IACjD,OAAO,wBAAwB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AAC9C,CAAC;AAUD,4DAA4D;AAC5D,MAAM,UAAU,4BAA4B,CAAC,OAAO,GAA0B,EAAE;IAC5E,MAAM,MAAM,GAA2B;QACnC,IAAI,EAAE,CAAC,OAAO,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE;KAClD,CAAC;IACF,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QAChB,MAAM,CAAC,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;IAClC,CAAC;IACD,OAAO,MAAM,CAAC;AAClB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,2BAA2B,CACvC,KAA8B,EAC9B,OAAO,GAA0B,EAAE;IAEnC,OAAO,EAAE,GAAG,KAAK,EAAE,GAAG,4BAA4B,CAAC,OAAO,CAAC,EAAE,CAAC;AAClE,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU,+BAA+B;IAC3C,OAAO,yBAAyB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,WAAW,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC3F,CAAC"}
package/lib/template.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  import { Script } from './script.js';
2
+ /** Identifiers every JST template can read besides its declared inputs. */
3
+ export declare const JST_TEMPLATE_GLOBALS: readonly string[];
2
4
  export declare class CompiledTemplate extends Script<string> {
3
5
  constructor(code: string, globals: string[]);
4
6
  validate(): import("./validation.js").Scope;
@@ -1 +1 @@
1
- {"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../src/template.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAGrC,qBAAa,gBAAiB,SAAQ,MAAM,CAAC,MAAM,CAAC;IAChD,YAAY,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAE1C;IACD,QAAQ,oCAOP;IACD,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAE5C;CACJ;AACD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAiBvG"}
1
+ {"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../src/template.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAIrC,2EAA2E;AAC3E,eAAO,MAAM,oBAAoB,EAAE,SAAS,MAAM,EAA6D,CAAC;AAEhH,qBAAa,gBAAiB,SAAQ,MAAM,CAAC,MAAM,CAAC;IAChD,YAAY,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAI1C;IACD,QAAQ,oCAOP;IACD,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAE5C;CACJ;AACD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAiBvG"}
package/lib/template.js CHANGED
@@ -2,10 +2,15 @@ import dayjs from 'dayjs';
2
2
  import { addLineNumbers } from './functions/addLineNumbers.js';
3
3
  import { jsonToCsv, loadCsv } from './functions/csv.js';
4
4
  import { Script } from './script.js';
5
+ import { TEMPLATE_SYSTEM_VARIABLE_NAMES } from './system.js';
5
6
  import { CompositeError } from './validation.js';
7
+ /** Identifiers every JST template can read besides its declared inputs. */
8
+ export const JST_TEMPLATE_GLOBALS = ['_', 'Array', 'Set', ...TEMPLATE_SYSTEM_VARIABLE_NAMES];
6
9
  export class CompiledTemplate extends Script {
7
10
  constructor(code, globals) {
8
- super(code, globals.concat('_', 'Array', 'Set'));
11
+ // Deduplicate: the globals become a destructured parameter list, where a repeated name
12
+ // (e.g. a schema that also declares `_model`) is a syntax error.
13
+ super(code, [...new Set([...globals, ...JST_TEMPLATE_GLOBALS])]);
9
14
  }
10
15
  validate() {
11
16
  return super.validate({
@@ -1 +1 @@
1
- {"version":3,"file":"template.js","sourceRoot":"","sources":["../src/template.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD,MAAM,OAAO,gBAAiB,SAAQ,MAAc;IAChD,YAAY,IAAY,EAAE,OAAiB;QACvC,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;IACrD,CAAC;IACD,QAAQ;QACJ,OAAO,KAAK,CAAC,QAAQ,CAAC;YAClB,KAAK,EAAE;gBACH,0BAA0B,EAAE,IAAI;gBAChC,SAAS,EAAE,IAAI;aAClB;SACJ,CAAC,CAAC;IACP,CAAC;IACD,GAAG,CAAC,OAAgC;QAChC,OAAO,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IAC7D,CAAC;CACJ;AACD,MAAM,UAAU,gBAAgB,CAAC,IAAY,EAAE,OAAiB,EAAE,IAA6B;IAC3F,MAAM,MAAM,GAAG,IAAI,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACnD,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC;IAChC,IAAI,KAAK,CAAC,SAAS,EAAE,EAAE,CAAC;QACpB,MAAM,IAAI,cAAc,CAAC,KAAK,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;IAChE,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC;QACvB,GAAG,IAAI;QACP,CAAC,EAAE;YACC,OAAO;YACP,SAAS;YACT,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,cAAc,EAAE,cAAc;YAC9B,KAAK,EAAE,KAAK;SACf;KACJ,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AACnB,CAAC"}
1
+ {"version":3,"file":"template.js","sourceRoot":"","sources":["../src/template.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,8BAA8B,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD,2EAA2E;AAC3E,MAAM,CAAC,MAAM,oBAAoB,GAAsB,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,8BAA8B,CAAC,CAAC;AAEhH,MAAM,OAAO,gBAAiB,SAAQ,MAAc;IAChD,YAAY,IAAY,EAAE,OAAiB;QACvC,uFAAuF;QACvF,iEAAiE;QACjE,KAAK,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,oBAAoB,CAAC,CAAC,CAAC,CAAC,CAAC;IACrE,CAAC;IACD,QAAQ;QACJ,OAAO,KAAK,CAAC,QAAQ,CAAC;YAClB,KAAK,EAAE;gBACH,0BAA0B,EAAE,IAAI;gBAChC,SAAS,EAAE,IAAI;aAClB;SACJ,CAAC,CAAC;IACP,CAAC;IACD,GAAG,CAAC,OAAgC;QAChC,OAAO,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IAC7D,CAAC;CACJ;AACD,MAAM,UAAU,gBAAgB,CAAC,IAAY,EAAE,OAAiB,EAAE,IAA6B;IAC3F,MAAM,MAAM,GAAG,IAAI,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACnD,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC;IAChC,IAAI,KAAK,CAAC,SAAS,EAAE,EAAE,CAAC;QACpB,MAAM,IAAI,cAAc,CAAC,KAAK,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;IAChE,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC;QACvB,GAAG,IAAI;QACP,CAAC,EAAE;YACC,OAAO;YACP,SAAS;YACT,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,cAAc,EAAE,cAAc;YAC9B,KAAK,EAAE,KAAK;SACf;KACJ,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AACnB,CAAC"}
@@ -1,5 +1,5 @@
1
- import e from"handlebars";import{parse as t}from"acorn";import{base as n,recursive as r}from"acorn-walk";import i from"dayjs";import a from"papaparse";e.registerHelper(`_now`,()=>new Date().toISOString()),e.registerHelper(`stringify`,t=>t==null?``:typeof t==`string`?t:new e.SafeString(JSON.stringify(t)));function o(t,n){return e.compile(t)(n)}const s=n,c={line:0,column:0};var l=class extends Error{node;constructor(e,t){super(e),this.node=t,this.name=`ValidationError`}get range(){let e=this.node.loc;return e?[e.start,e.end]:[c,c]}get start(){return this.node.loc?.start||c}get end(){return this.node.loc?.end||c}get location(){return this.node.loc?`${this.node.loc.start.line}:${this.node.loc.start.column}`:`${this.node.range}`}},u=class{name;node;parent;errors;children=[];locals=new Set;constructor(e,t,n){this.name=e,this.node=t,this.parent=n,n?.children.push(this),this.errors=n?n.errors:[],t.$scope=this}define(e){this.locals.add(e)}isDefined(e){return this.locals.has(e)?!0:this.parent?this.parent.isDefined(e):!1}pushError(e,t){this.errors.push(new l(e,t))}hasErrors(){return this.errors.length>0}get ok(){return this.errors.length===0}print(){console.log(this.name,`=>`,this.locals);for(let e of this.children)e.print()}};function d(e,t){for(let n of e.params)n.type===`Identifier`?(t.locals.add(n.name),f(n)):n.type===`AssignmentPattern`?n.left.type===`Identifier`&&(t.locals.add(n.left.name),f(n.left)):n.type===`RestElement`&&n.argument.type===`Identifier`&&(t.locals.add(n.argument.name),f(n.argument))}function f(e){e.___safe_identifier=!0}function p(e){return!!e.___safe_identifier}const m={ecmaVersion:2020,sourceType:`script`};function h(e,n){let i=n.acorn?{...m,...n.acorn}:m,a=t(e,i),o=new u(`#root`,a,null);if(n.globals){for(let e of n.globals)o.locals.add(e);a.$scope=o}let c=new Set(n.propsBlacklist?n.propsBlacklist:[`constructor`,`prototype`,`__proto__`]);return r(a,o,{WithStatement(e,t,n){t.pushError(`"with" keyword is not supported`,e),s.WithStatement(e,t,n)},ForStatement(e,t,n){t.pushError(`"for" keyword is not supported`,e),s.ForStatement(e,t,n)},WhileStatement(e,t,n){t.pushError(`"while" keyword is not supported`,e),s.WhileStatement(e,t,n)},ImportExpression(e,t,n){t.pushError(`"import" keyword is not supported`,e),s.ImportExpression(e,t,n)},ImportDeclaration(e,t,n){t.pushError(`"import" keyword is not supported`,e),s.ImportDeclaration(e,t,n)},Class(e,t,n){t.pushError(`Classes are not supported`,e),s.Class(e,t,n)},ThisExpression(e,t,r){n.allowThis||t.pushError(`"this" keyword is not supported`,e),s.ThisExpression(e,t,r)},ObjectPattern(e,t,n){for(let n of e.properties)n.type===`Property`?n.key.type===`Identifier`&&(f(n.key),n.kind===`init`&&c.has(n.key.name)&&t.pushError(`Property "${n.key.name}" is not allowed`,n.key)):n.type===`RestElement`&&n.argument.type===`Identifier`&&f(n.argument);s.ObjectPattern(e,t,n)},ArrayPattern(e,t,n){for(let t of e.elements)t&&(t.type===`Identifier`?f(t):t.type===`RestElement`&&t.argument.type===`Identifier`&&f(t.argument));s.ArrayPattern(e,t,n)},MemberExpression(e,t,n){let r=e.property;r.type===`Identifier`?(f(r),c.has(r.name)&&t.pushError(`Property "${r.name}" is not allowed`,r)):r.type===`Literal`?c.has(String(r.value))&&t.pushError(`Property "${r.value}" is not allowed`,r):t.pushError(`Dynamic property lookup is not supported`,r),s.MemberExpression(e,t,n)},Function(e,t,n){let r=e.id?.name;r&&t.define(r);let i=new u(r||`#anonymous`,e,t);d(e,i),s.Function(e,i,n)},VariableDeclaration(e,t,n){for(let n of e.declarations)if(n.id.type===`Identifier`)f(n.id),t.define(n.id.name);else if(n.id.type===`ObjectPattern`)for(let e of n.id.properties)e.type===`Property`?e.value.type===`Identifier`&&t.define(e.value.name):e.type===`RestElement`&&e.argument.type===`Identifier`&&t.define(e.argument.name);else if(n.id.type===`ArrayPattern`)for(let e of n.id.elements)e.type===`Identifier`?t.define(e.name):e.type===`RestElement`&&e.argument.type===`Identifier`&&t.define(e.argument.name);s.VariableDeclaration(e,t,n)}}),{root:o,program:a}}function g(e,t={}){let{root:n,program:i}=h(e,t);return r(i,n,{Function(e,t,n){s.Function(e,e.$scope||t,n)},Identifier(e,t,n){let r=e;!p(e)&&!t.isDefined(r.name)&&t.pushError(`Unknown identifier "${r.name}"`,e),s.Identifier(e,t,n)}}),n}function _(e,t={}){let{root:n,program:i}=h(e,t),a=new Set;return r(i,n,{Function(e,t,n){s.Function(e,e.$scope||t,n)},Identifier(e,t,n){let r=e;!p(e)&&!t.isDefined(r.name)&&a.add(r.name),s.Identifier(e,t,n)}}),{vars:a,errors:n.errors}}var v=class extends Error{errors;constructor(e,t){super(`${t||`Composite Error`}\n${e.map(e=>e.message).join(`
2
- * `)}`),this.errors=e,this.errors=e,this.name=`CompositeError`}},y=class{code;globals;fn;constructor(e,t){this.code=e,this.globals=t;let n=t&&t.length>0?`{${t.join(`,`)}}`:``;this.fn=Function(n,`return (function(){"use strict";${e}})()`)}validate(e={}){return g(this.code,Object.assign({globals:this.globals},e))}run(e){return this.fn(e)}};function b(e){let t=e.split(`
1
+ import e from"handlebars";import{parse as t}from"acorn";import{base as n,recursive as r}from"acorn-walk";import i from"dayjs";import a from"papaparse";const o=[{name:`_now`,type:`string`,description:`Current date and time as an ISO 8601 string (UTC)`},{name:`_model`,type:`string`,description:`Identifier of the model the prompt is executed with`}],s=o.map(e=>e.name),c=new Set(s);function l(e){return c.has(e)}function u(e={}){let t={_now:(e.now??new Date).toISOString()};return e.model&&(t._model=e.model),t}function d(e,t={}){return{...e,...u(t)}}function f(){return o.map(e=>`${e.name} (${e.description})`).join(`, `)}function p(e){return(...t)=>{let n=t[t.length-1]?.data?.root;return(n&&typeof n==`object`?n[e]:void 0)??(e===`_now`?new Date().toISOString():``)}}function m(t){return t==null?``:typeof t==`string`?t:new e.SafeString(JSON.stringify(t))}const h={...Object.fromEntries(s.map(e=>[e,p(e)])),stringify:m},g=e.create();for(let[t,n]of Object.entries(h))g.registerHelper(t,n),e.registerHelper(t,n);const _=Object.keys(g.helpers).filter(e=>e!==`helperMissing`&&e!==`blockHelperMissing`).sort(),v=new Set(_);function y(e){return v.has(e)}const b=[`each`,`with`];function x(e,t){return g.compile(e)(t)}const S=n,C={line:0,column:0};var w=class extends Error{node;constructor(e,t){super(e),this.node=t,this.name=`ValidationError`}get range(){let e=this.node.loc;return e?[e.start,e.end]:[C,C]}get start(){return this.node.loc?.start||C}get end(){return this.node.loc?.end||C}get location(){return this.node.loc?`${this.node.loc.start.line}:${this.node.loc.start.column}`:`${this.node.range}`}},T=class{name;node;parent;errors;children=[];locals=new Set;constructor(e,t,n){this.name=e,this.node=t,this.parent=n,n?.children.push(this),this.errors=n?n.errors:[],t.$scope=this}define(e){this.locals.add(e)}isDefined(e){return this.locals.has(e)?!0:this.parent?this.parent.isDefined(e):!1}pushError(e,t){this.errors.push(new w(e,t))}hasErrors(){return this.errors.length>0}get ok(){return this.errors.length===0}print(){console.log(this.name,`=>`,this.locals);for(let e of this.children)e.print()}};function E(e,t){for(let n of e.params)n.type===`Identifier`?(t.locals.add(n.name),D(n)):n.type===`AssignmentPattern`?n.left.type===`Identifier`&&(t.locals.add(n.left.name),D(n.left)):n.type===`RestElement`&&n.argument.type===`Identifier`&&(t.locals.add(n.argument.name),D(n.argument))}function D(e){e.___safe_identifier=!0}function O(e){return!!e.___safe_identifier}const k={ecmaVersion:2020,sourceType:`script`};function A(e,n){let i=n.acorn?{...k,...n.acorn}:k,a=t(e,i),o=new T(`#root`,a,null);if(n.globals){for(let e of n.globals)o.locals.add(e);a.$scope=o}let s=new Set(n.propsBlacklist?n.propsBlacklist:[`constructor`,`prototype`,`__proto__`]);return r(a,o,{WithStatement(e,t,n){t.pushError(`"with" keyword is not supported`,e),S.WithStatement(e,t,n)},ForStatement(e,t,n){t.pushError(`"for" keyword is not supported`,e),S.ForStatement(e,t,n)},WhileStatement(e,t,n){t.pushError(`"while" keyword is not supported`,e),S.WhileStatement(e,t,n)},ImportExpression(e,t,n){t.pushError(`"import" keyword is not supported`,e),S.ImportExpression(e,t,n)},ImportDeclaration(e,t,n){t.pushError(`"import" keyword is not supported`,e),S.ImportDeclaration(e,t,n)},Class(e,t,n){t.pushError(`Classes are not supported`,e),S.Class(e,t,n)},ThisExpression(e,t,r){n.allowThis||t.pushError(`"this" keyword is not supported`,e),S.ThisExpression(e,t,r)},ObjectPattern(e,t,n){for(let n of e.properties)n.type===`Property`?n.key.type===`Identifier`&&(D(n.key),n.kind===`init`&&s.has(n.key.name)&&t.pushError(`Property "${n.key.name}" is not allowed`,n.key)):n.type===`RestElement`&&n.argument.type===`Identifier`&&D(n.argument);S.ObjectPattern(e,t,n)},ArrayPattern(e,t,n){for(let t of e.elements)t&&(t.type===`Identifier`?D(t):t.type===`RestElement`&&t.argument.type===`Identifier`&&D(t.argument));S.ArrayPattern(e,t,n)},MemberExpression(e,t,n){let r=e.property;r.type===`Identifier`?(D(r),s.has(r.name)&&t.pushError(`Property "${r.name}" is not allowed`,r)):r.type===`Literal`?s.has(String(r.value))&&t.pushError(`Property "${r.value}" is not allowed`,r):t.pushError(`Dynamic property lookup is not supported`,r),S.MemberExpression(e,t,n)},Function(e,t,n){let r=e.id?.name;r&&t.define(r);let i=new T(r||`#anonymous`,e,t);E(e,i),S.Function(e,i,n)},VariableDeclaration(e,t,n){for(let n of e.declarations)if(n.id.type===`Identifier`)D(n.id),t.define(n.id.name);else if(n.id.type===`ObjectPattern`)for(let e of n.id.properties)e.type===`Property`?e.value.type===`Identifier`&&t.define(e.value.name):e.type===`RestElement`&&e.argument.type===`Identifier`&&t.define(e.argument.name);else if(n.id.type===`ArrayPattern`)for(let e of n.id.elements)e.type===`Identifier`?t.define(e.name):e.type===`RestElement`&&e.argument.type===`Identifier`&&t.define(e.argument.name);S.VariableDeclaration(e,t,n)}}),{root:o,program:a}}function j(e,t={}){let{root:n,program:i}=A(e,t);return r(i,n,{Function(e,t,n){S.Function(e,e.$scope||t,n)},Identifier(e,t,n){let r=e;!O(e)&&!t.isDefined(r.name)&&t.pushError(`Unknown identifier "${r.name}"`,e),S.Identifier(e,t,n)}}),n}function M(e,t={}){let{root:n,program:i}=A(e,t),a=new Set;return r(i,n,{Function(e,t,n){S.Function(e,e.$scope||t,n)},Identifier(e,t,n){let r=e;!O(e)&&!t.isDefined(r.name)&&a.add(r.name),S.Identifier(e,t,n)}}),{vars:a,errors:n.errors}}var N=class extends Error{errors;constructor(e,t){super(`${t||`Composite Error`}\n${e.map(e=>e.message).join(`
2
+ * `)}`),this.errors=e,this.errors=e,this.name=`CompositeError`}},P=class{code;globals;fn;constructor(e,t){this.code=e,this.globals=t;let n=t&&t.length>0?`{${t.join(`,`)}}`:``;this.fn=Function(n,`return (function(){"use strict";${e}})()`)}validate(e={}){return j(this.code,Object.assign({globals:this.globals},e))}run(e){return this.fn(e)}};function F(e){let t=e.split(`
3
3
  `),n=Math.ceil(Math.log10(t.length));return t.map((e,t)=>`{% ${(t+1).toString().padStart(n,` `)} %} ${e}`).join(`
4
- `)}function x(e,t={}){return a.parse(e,t)}function S(e,t={}){return a.unparse(e,t)}var C=class extends y{constructor(e,t){super(e,t.concat(`_`,`Array`,`Set`))}validate(){return super.validate({acorn:{allowReturnOutsideFunction:!0,locations:!0}})}run(e){return super.run({...e,Set,Array})}};function w(e,t,n){let r=new C(e,t),a=r.validate();if(a.hasErrors())throw new v(a.errors,`Validation errors`);return r.run({...n,_:{loadCsv:x,jsonToCsv:S,stringify:JSON.stringify,addLineNumbers:b,dayjs:i}})}export{C as CompiledTemplate,v as CompositeError,u as Scope,y as Script,l as ValidationError,_ as getFreeVariables,o as renderHandlebarsTemplate,w as renderJsTemplate,g as validate};
4
+ `)}function I(e,t={}){return a.parse(e,t)}function L(e,t={}){return a.unparse(e,t)}const R=[`_`,`Array`,`Set`,...s];var z=class extends P{constructor(e,t){super(e,[...new Set([...t,...R])])}validate(){return super.validate({acorn:{allowReturnOutsideFunction:!0,locations:!0}})}run(e){return super.run({...e,Set,Array})}};function B(e,t,n){let r=new z(e,t),a=r.validate();if(a.hasErrors())throw new N(a.errors,`Validation errors`);return r.run({...n,_:{loadCsv:I,jsonToCsv:L,stringify:JSON.stringify,addLineNumbers:F,dayjs:i}})}export{z as CompiledTemplate,N as CompositeError,b as HANDLEBARS_CONTEXT_HELPERS,_ as HANDLEBARS_HELPER_NAMES,R as JST_TEMPLATE_GLOBALS,T as Scope,P as Script,o as TEMPLATE_SYSTEM_VARIABLES,s as TEMPLATE_SYSTEM_VARIABLE_NAMES,w as ValidationError,u as buildTemplateSystemVariables,f as describeTemplateSystemVariables,M as getFreeVariables,y as isHandlebarsHelper,l as isTemplateSystemVariable,x as renderHandlebarsTemplate,B as renderJsTemplate,j as validate,d as withTemplateSystemVariables};
5
5
  //# sourceMappingURL=vertesia-jst.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"vertesia-jst.js","names":["baseV"],"sources":["handlebars/index.js","validation.js","script.js","functions/addLineNumbers.js","functions/csv.js","template.js"],"sourcesContent":["import Handlebars from 'handlebars';\nHandlebars.registerHelper('_now', () => new Date().toISOString());\nHandlebars.registerHelper('stringify', (value) => {\n if (value === null || value === undefined)\n return '';\n if (typeof value === 'string')\n return value;\n return new Handlebars.SafeString(JSON.stringify(value));\n});\nexport function renderHandlebarsTemplate(template, input) {\n // lazy load handlebars to speed up initial loading\n const compiled = Handlebars.compile(template);\n return compiled(input);\n}\n//# sourceMappingURL=index.js.map","import { parse, } from 'acorn';\nimport { base as baseV, recursive as recursiveWalk } from 'acorn-walk';\nconst baseVisitor = baseV;\nconst UnknownPosition = {\n line: 0,\n column: 0,\n};\nexport class ValidationError extends Error {\n node;\n constructor(message, node) {\n super(message);\n this.node = node;\n this.name = 'ValidationError';\n }\n get range() {\n const loc = this.node.loc;\n return loc ? [loc.start, loc.end] : [UnknownPosition, UnknownPosition];\n }\n get start() {\n return this.node.loc?.start || UnknownPosition;\n }\n get end() {\n return this.node.loc?.end || UnknownPosition;\n }\n get location() {\n if (this.node.loc) {\n return `${this.node.loc.start.line}:${this.node.loc.start.column}`;\n }\n else {\n return `${this.node.range}`;\n }\n }\n}\nexport class Scope {\n name;\n node;\n parent;\n errors;\n children = [];\n locals = new Set();\n constructor(name, node, parent) {\n this.name = name;\n this.node = node;\n this.parent = parent;\n parent?.children.push(this);\n this.errors = parent ? parent.errors : [];\n node.$scope = this;\n }\n define(name) {\n this.locals.add(name);\n }\n isDefined(name) {\n if (this.locals.has(name)) {\n return true;\n }\n if (this.parent) {\n return this.parent.isDefined(name);\n }\n return false;\n }\n pushError(message, node) {\n this.errors.push(new ValidationError(message, node));\n }\n hasErrors() {\n return this.errors.length > 0;\n }\n get ok() {\n return this.errors.length === 0;\n }\n print() {\n console.log(this.name, '=>', this.locals);\n for (const child of this.children) {\n child.print();\n }\n }\n}\nfunction addFunctionParams(node, scope) {\n for (const param of node.params) {\n if (param.type === 'Identifier') {\n scope.locals.add(param.name);\n markSafeIdentifier(param);\n }\n else if (param.type === 'AssignmentPattern') {\n if (param.left.type === 'Identifier') {\n scope.locals.add(param.left.name);\n markSafeIdentifier(param.left);\n }\n }\n else if (param.type === 'RestElement') {\n if (param.argument.type === 'Identifier') {\n scope.locals.add(param.argument.name);\n markSafeIdentifier(param.argument);\n }\n }\n }\n}\nfunction markSafeIdentifier(node) {\n node.___safe_identifier = true;\n}\nfunction isSafeIdentifier(node) {\n return !!node.___safe_identifier;\n}\nconst defaultAcornOpts = {\n ecmaVersion: 2020,\n sourceType: 'script',\n};\n/**\n * Parse `code` and build the scope tree (one full AST walk). The returned root scope\n * has `locals` populated for every nested function/block and has unsafe-construct\n * errors accumulated in `root.errors`. Identifier resolution (the \"Unknown identifier\"\n * check) is a separate second walk done by `validate` / `getFreeVariables`.\n */\nfunction buildScopeTree(code, opts) {\n const acornOpts = opts.acorn ? { ...defaultAcornOpts, ...opts.acorn } : defaultAcornOpts;\n const program = parse(code, acornOpts);\n const root = new Scope('#root', program, null);\n if (opts.globals) {\n for (const identifier of opts.globals) {\n root.locals.add(identifier);\n }\n program.$scope = root;\n }\n const propsBlacklist = new Set(opts.propsBlacklist ? opts.propsBlacklist : ['constructor', 'prototype', '__proto__']);\n recursiveWalk(program, root, {\n WithStatement(node, state, c) {\n state.pushError('\"with\" keyword is not supported', node);\n baseVisitor.WithStatement(node, state, c);\n },\n ForStatement(node, state, c) {\n state.pushError('\"for\" keyword is not supported', node);\n baseVisitor.ForStatement(node, state, c);\n },\n WhileStatement(node, state, c) {\n state.pushError('\"while\" keyword is not supported', node);\n baseVisitor.WhileStatement(node, state, c);\n },\n ImportExpression(node, state, c) {\n state.pushError('\"import\" keyword is not supported', node);\n baseVisitor.ImportExpression(node, state, c);\n },\n ImportDeclaration(node, state, c) {\n state.pushError('\"import\" keyword is not supported', node);\n baseVisitor.ImportDeclaration(node, state, c);\n },\n Class(node, state, c) {\n state.pushError('Classes are not supported', node);\n baseVisitor.Class(node, state, c);\n },\n ThisExpression(node, state, c) {\n if (!opts.allowThis) {\n state.pushError('\"this\" keyword is not supported', node);\n }\n baseVisitor.ThisExpression(node, state, c);\n },\n ObjectPattern(node, state, c) {\n for (const prop of node.properties) {\n if (prop.type === 'Property') {\n if (prop.key.type === 'Identifier') {\n markSafeIdentifier(prop.key);\n if (prop.kind === 'init' && propsBlacklist.has(prop.key.name)) {\n state.pushError(`Property \"${prop.key.name}\" is not allowed`, prop.key);\n }\n }\n }\n else if (prop.type === 'RestElement') {\n if (prop.argument.type === 'Identifier') {\n markSafeIdentifier(prop.argument);\n }\n }\n }\n baseVisitor.ObjectPattern(node, state, c);\n },\n ArrayPattern(node, state, c) {\n for (const prop of node.elements) {\n if (prop) {\n if (prop.type === 'Identifier') {\n markSafeIdentifier(prop);\n //TODO is this really needed?\n // if (propBlacklist.has(prop.name)) {\n // state.pushError(`Property \"${prop.name}\" is not allowed`, prop);\n // }\n }\n else if (prop.type === 'RestElement') {\n if (prop.argument.type === 'Identifier') {\n markSafeIdentifier(prop.argument);\n }\n }\n }\n }\n baseVisitor.ArrayPattern(node, state, c);\n },\n MemberExpression(node, state, c) {\n const prop = node.property;\n if (prop.type === 'Identifier') {\n markSafeIdentifier(prop);\n if (propsBlacklist.has(prop.name)) {\n state.pushError(`Property \"${prop.name}\" is not allowed`, prop);\n }\n }\n else if (prop.type === 'Literal') {\n if (propsBlacklist.has(String(prop.value))) {\n state.pushError(`Property \"${prop.value}\" is not allowed`, prop);\n }\n }\n else {\n state.pushError('Dynamic property lookup is not supported', prop);\n }\n baseVisitor.MemberExpression(node, state, c);\n },\n Function(node, state, c) {\n const name = node.id?.name;\n if (name) {\n // arrow functions have no name\n state.define(name);\n }\n const scope = new Scope(name || '#anonymous', node, state);\n addFunctionParams(node, scope);\n baseVisitor.Function(node, scope, c);\n },\n VariableDeclaration(node, state, c) {\n for (const decl of node.declarations) {\n if (decl.id.type === 'Identifier') {\n markSafeIdentifier(decl.id);\n state.define(decl.id.name);\n }\n else if (decl.id.type === 'ObjectPattern') {\n //the ObjectPattern rule will call markSafeIdentifier\n for (const prop of decl.id.properties) {\n if (prop.type === 'Property') {\n if (prop.value.type === 'Identifier')\n state.define(prop.value.name);\n }\n else if (prop.type === 'RestElement') {\n if (prop.argument.type === 'Identifier')\n state.define(prop.argument.name);\n }\n }\n }\n else if (decl.id.type === 'ArrayPattern') {\n //the ArrayPattern rule will call markSafeIdentifier\n for (const prop of decl.id.elements) {\n if (prop.type === 'Identifier') {\n state.define(prop.name);\n }\n else if (prop.type === 'RestElement') {\n if (prop.argument.type === 'Identifier')\n state.define(prop.argument.name);\n }\n }\n }\n }\n baseVisitor.VariableDeclaration(node, state, c);\n },\n });\n return { root, program };\n}\nexport function validate(code, opts = {}) {\n const { root, program } = buildScopeTree(code, opts);\n recursiveWalk(program, root, {\n Function(node, state, c) {\n baseVisitor.Function(node, node.$scope || state, c);\n },\n Identifier(node, state, c) {\n const identifier = node;\n if (!isSafeIdentifier(node) && !state.isDefined(identifier.name)) {\n state.pushError(`Unknown identifier \"${identifier.name}\"`, node);\n }\n baseVisitor.Identifier(node, state, c);\n },\n });\n return root;\n}\n/**\n * Walk `code` and return every identifier that is referenced but not bound by any\n * enclosing function/variable declaration and not listed in `opts.globals`. Pass\n * known runtime-injected names (e.g. JST's `_`, `Array`, `Set`) in `opts.globals`\n * so they don't appear as free vars. Also surfaces unsafe-construct errors from\n * the same parse pass.\n */\nexport function getFreeVariables(code, opts = {}) {\n const { root, program } = buildScopeTree(code, opts);\n const vars = new Set();\n recursiveWalk(program, root, {\n Function(node, state, c) {\n baseVisitor.Function(node, node.$scope || state, c);\n },\n Identifier(node, state, c) {\n const identifier = node;\n if (!isSafeIdentifier(node) && !state.isDefined(identifier.name)) {\n vars.add(identifier.name);\n }\n baseVisitor.Identifier(node, state, c);\n },\n });\n return { vars, errors: root.errors };\n}\nexport class CompositeError extends Error {\n errors;\n constructor(errors, message) {\n super(`${message || 'Composite Error'}\\n${errors.map((err) => err.message).join('\\n* ')}`);\n this.errors = errors;\n this.errors = errors;\n this.name = 'CompositeError';\n }\n}\n//# sourceMappingURL=validation.js.map","import { validate } from './validation.js';\nexport class Script {\n code;\n globals;\n fn;\n constructor(code, globals) {\n this.code = code;\n this.globals = globals;\n const args = globals && globals.length > 0 ? `{${globals.join(',')}}` : '';\n this.fn = new Function(args, `return (function(){\"use strict\";${code}})()`);\n }\n validate(opts = {}) {\n return validate(this.code, Object.assign({ globals: this.globals }, opts));\n }\n run(context) {\n return this.fn(context);\n }\n}\n//# sourceMappingURL=script.js.map","//add line numbers with {% addLineNumbers(content) %}\nexport function addLineNumbers(content) {\n const lines = content.split('\\n');\n const width = Math.ceil(Math.log10(lines.length));\n return lines\n .map((line, index) => {\n const number = (index + 1).toString().padStart(width, ' ');\n return `{% ${number} %} ${line}`;\n })\n .join('\\n');\n}\n//# sourceMappingURL=addLineNumbers.js.map","import Papa from 'papaparse';\nexport function loadCsv(content, options = {}) {\n return Papa.parse(content, options);\n}\nexport function jsonToCsv(obj, options = {}) {\n return Papa.unparse(obj, options);\n}\n//# sourceMappingURL=csv.js.map","import dayjs from 'dayjs';\nimport { addLineNumbers } from './functions/addLineNumbers.js';\nimport { jsonToCsv, loadCsv } from './functions/csv.js';\nimport { Script } from './script.js';\nimport { CompositeError } from './validation.js';\nexport class CompiledTemplate extends Script {\n constructor(code, globals) {\n super(code, globals.concat('_', 'Array', 'Set'));\n }\n validate() {\n return super.validate({\n acorn: {\n allowReturnOutsideFunction: true,\n locations: true,\n },\n });\n }\n run(context) {\n return super.run({ ...context, Set: Set, Array: Array });\n }\n}\nexport function renderJsTemplate(code, globals, data) {\n const script = new CompiledTemplate(code, globals);\n const state = script.validate();\n if (state.hasErrors()) {\n throw new CompositeError(state.errors, 'Validation errors');\n }\n const content = script.run({\n ...data,\n _: {\n loadCsv,\n jsonToCsv,\n stringify: JSON.stringify,\n addLineNumbers: addLineNumbers,\n dayjs: dayjs,\n },\n });\n return content;\n}\n//# sourceMappingURL=template.js.map"],"mappings":"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"}
1
+ {"version":3,"file":"vertesia-jst.js","names":["baseV"],"sources":["system.js","handlebars/index.js","validation.js","script.js","functions/addLineNumbers.js","functions/csv.js","template.js"],"sourcesContent":["export const TEMPLATE_SYSTEM_VARIABLES = [\n {\n name: '_now',\n type: 'string',\n description: 'Current date and time as an ISO 8601 string (UTC)',\n },\n {\n name: '_model',\n type: 'string',\n description: 'Identifier of the model the prompt is executed with',\n },\n];\nexport const TEMPLATE_SYSTEM_VARIABLE_NAMES = TEMPLATE_SYSTEM_VARIABLES.map((v) => v.name);\nconst SYSTEM_VARIABLE_NAME_SET = new Set(TEMPLATE_SYSTEM_VARIABLE_NAMES);\nexport function isTemplateSystemVariable(name) {\n return SYSTEM_VARIABLE_NAME_SET.has(name);\n}\n/** Compute the system variable values for one rendering. */\nexport function buildTemplateSystemVariables(context = {}) {\n const values = {\n _now: (context.now ?? new Date()).toISOString(),\n };\n if (context.model) {\n values._model = context.model;\n }\n return values;\n}\n/**\n * Return the template data with the system variables added. System values win over caller input\n * of the same name: the names are reserved, so a caller cannot spoof `_model` or `_now`.\n */\nexport function withTemplateSystemVariables(input, context = {}) {\n return { ...input, ...buildTemplateSystemVariables(context) };\n}\n/** One-line summary of the system variables, for tool descriptions and error messages. */\nexport function describeTemplateSystemVariables() {\n return TEMPLATE_SYSTEM_VARIABLES.map((v) => `${v.name} (${v.description})`).join(', ');\n}\n//# sourceMappingURL=system.js.map","import Handlebars from 'handlebars';\nimport { TEMPLATE_SYSTEM_VARIABLE_NAMES } from '../system.js';\n/**\n * System variables (see ../system.ts) are injected into the template data. Each one is also\n * registered as a helper returning that injected value, because Handlebars resolves a name\n * differently by position: a bare `{{_now}}` or a subexpression `(_now)` looks up helpers first,\n * while an argument (`{{#if _now}}`) reads the data only. Registering both makes every position\n * render the same value. `_now` falls back to the current time when no value was injected, which\n * keeps templates rendered by callers that do not inject system variables working.\n */\nfunction systemVariableHelper(name) {\n return (...args) => {\n const options = args[args.length - 1];\n const root = options?.data?.root;\n const value = root && typeof root === 'object' ? root[name] : undefined;\n if (value !== undefined && value !== null) {\n return value;\n }\n return name === '_now' ? new Date().toISOString() : '';\n };\n}\nfunction stringifyHelper(value) {\n if (value === null || value === undefined)\n return '';\n if (typeof value === 'string')\n return value;\n return new Handlebars.SafeString(JSON.stringify(value));\n}\nconst CUSTOM_HELPERS = {\n ...Object.fromEntries(TEMPLATE_SYSTEM_VARIABLE_NAMES.map((name) => [name, systemVariableHelper(name)])),\n stringify: stringifyHelper,\n};\n/**\n * Prompt templates render with a private Handlebars environment, so helpers another module\n * registers on the global instance never become available to prompts without the validator\n * knowing about them.\n */\nconst promptHandlebars = Handlebars.create();\nfor (const [name, helper] of Object.entries(CUSTOM_HELPERS)) {\n promptHandlebars.registerHelper(name, helper);\n // Kept on the global instance for code that compiled templates with the global Handlebars\n // after importing this module, which used to be where these helpers were registered.\n Handlebars.registerHelper(name, helper);\n}\n/**\n * Every helper available to prompt templates: Handlebars' built-ins (`if`, `each`, `with`,\n * `lookup`, ...) plus the custom ones above. Read from the environment itself so it cannot drift.\n */\nexport const HANDLEBARS_HELPER_NAMES = Object.keys(promptHandlebars.helpers)\n // Internal fallbacks Handlebars invokes itself; templates never call them by name.\n .filter((name) => name !== 'helperMissing' && name !== 'blockHelperMissing')\n .sort();\nconst HELPER_NAME_SET = new Set(HANDLEBARS_HELPER_NAMES);\nexport function isHandlebarsHelper(name) {\n return HELPER_NAME_SET.has(name);\n}\n/** Built-in block helpers that render their block with a new context (`this` is the item). */\nexport const HANDLEBARS_CONTEXT_HELPERS = ['each', 'with'];\nexport function renderHandlebarsTemplate(template, input) {\n const compiled = promptHandlebars.compile(template);\n return compiled(input);\n}\n//# sourceMappingURL=index.js.map","import { parse, } from 'acorn';\nimport { base as baseV, recursive as recursiveWalk } from 'acorn-walk';\nconst baseVisitor = baseV;\nconst UnknownPosition = {\n line: 0,\n column: 0,\n};\nexport class ValidationError extends Error {\n node;\n constructor(message, node) {\n super(message);\n this.node = node;\n this.name = 'ValidationError';\n }\n get range() {\n const loc = this.node.loc;\n return loc ? [loc.start, loc.end] : [UnknownPosition, UnknownPosition];\n }\n get start() {\n return this.node.loc?.start || UnknownPosition;\n }\n get end() {\n return this.node.loc?.end || UnknownPosition;\n }\n get location() {\n if (this.node.loc) {\n return `${this.node.loc.start.line}:${this.node.loc.start.column}`;\n }\n else {\n return `${this.node.range}`;\n }\n }\n}\nexport class Scope {\n name;\n node;\n parent;\n errors;\n children = [];\n locals = new Set();\n constructor(name, node, parent) {\n this.name = name;\n this.node = node;\n this.parent = parent;\n parent?.children.push(this);\n this.errors = parent ? parent.errors : [];\n node.$scope = this;\n }\n define(name) {\n this.locals.add(name);\n }\n isDefined(name) {\n if (this.locals.has(name)) {\n return true;\n }\n if (this.parent) {\n return this.parent.isDefined(name);\n }\n return false;\n }\n pushError(message, node) {\n this.errors.push(new ValidationError(message, node));\n }\n hasErrors() {\n return this.errors.length > 0;\n }\n get ok() {\n return this.errors.length === 0;\n }\n print() {\n console.log(this.name, '=>', this.locals);\n for (const child of this.children) {\n child.print();\n }\n }\n}\nfunction addFunctionParams(node, scope) {\n for (const param of node.params) {\n if (param.type === 'Identifier') {\n scope.locals.add(param.name);\n markSafeIdentifier(param);\n }\n else if (param.type === 'AssignmentPattern') {\n if (param.left.type === 'Identifier') {\n scope.locals.add(param.left.name);\n markSafeIdentifier(param.left);\n }\n }\n else if (param.type === 'RestElement') {\n if (param.argument.type === 'Identifier') {\n scope.locals.add(param.argument.name);\n markSafeIdentifier(param.argument);\n }\n }\n }\n}\nfunction markSafeIdentifier(node) {\n node.___safe_identifier = true;\n}\nfunction isSafeIdentifier(node) {\n return !!node.___safe_identifier;\n}\nconst defaultAcornOpts = {\n ecmaVersion: 2020,\n sourceType: 'script',\n};\n/**\n * Parse `code` and build the scope tree (one full AST walk). The returned root scope\n * has `locals` populated for every nested function/block and has unsafe-construct\n * errors accumulated in `root.errors`. Identifier resolution (the \"Unknown identifier\"\n * check) is a separate second walk done by `validate` / `getFreeVariables`.\n */\nfunction buildScopeTree(code, opts) {\n const acornOpts = opts.acorn ? { ...defaultAcornOpts, ...opts.acorn } : defaultAcornOpts;\n const program = parse(code, acornOpts);\n const root = new Scope('#root', program, null);\n if (opts.globals) {\n for (const identifier of opts.globals) {\n root.locals.add(identifier);\n }\n program.$scope = root;\n }\n const propsBlacklist = new Set(opts.propsBlacklist ? opts.propsBlacklist : ['constructor', 'prototype', '__proto__']);\n recursiveWalk(program, root, {\n WithStatement(node, state, c) {\n state.pushError('\"with\" keyword is not supported', node);\n baseVisitor.WithStatement(node, state, c);\n },\n ForStatement(node, state, c) {\n state.pushError('\"for\" keyword is not supported', node);\n baseVisitor.ForStatement(node, state, c);\n },\n WhileStatement(node, state, c) {\n state.pushError('\"while\" keyword is not supported', node);\n baseVisitor.WhileStatement(node, state, c);\n },\n ImportExpression(node, state, c) {\n state.pushError('\"import\" keyword is not supported', node);\n baseVisitor.ImportExpression(node, state, c);\n },\n ImportDeclaration(node, state, c) {\n state.pushError('\"import\" keyword is not supported', node);\n baseVisitor.ImportDeclaration(node, state, c);\n },\n Class(node, state, c) {\n state.pushError('Classes are not supported', node);\n baseVisitor.Class(node, state, c);\n },\n ThisExpression(node, state, c) {\n if (!opts.allowThis) {\n state.pushError('\"this\" keyword is not supported', node);\n }\n baseVisitor.ThisExpression(node, state, c);\n },\n ObjectPattern(node, state, c) {\n for (const prop of node.properties) {\n if (prop.type === 'Property') {\n if (prop.key.type === 'Identifier') {\n markSafeIdentifier(prop.key);\n if (prop.kind === 'init' && propsBlacklist.has(prop.key.name)) {\n state.pushError(`Property \"${prop.key.name}\" is not allowed`, prop.key);\n }\n }\n }\n else if (prop.type === 'RestElement') {\n if (prop.argument.type === 'Identifier') {\n markSafeIdentifier(prop.argument);\n }\n }\n }\n baseVisitor.ObjectPattern(node, state, c);\n },\n ArrayPattern(node, state, c) {\n for (const prop of node.elements) {\n if (prop) {\n if (prop.type === 'Identifier') {\n markSafeIdentifier(prop);\n //TODO is this really needed?\n // if (propBlacklist.has(prop.name)) {\n // state.pushError(`Property \"${prop.name}\" is not allowed`, prop);\n // }\n }\n else if (prop.type === 'RestElement') {\n if (prop.argument.type === 'Identifier') {\n markSafeIdentifier(prop.argument);\n }\n }\n }\n }\n baseVisitor.ArrayPattern(node, state, c);\n },\n MemberExpression(node, state, c) {\n const prop = node.property;\n if (prop.type === 'Identifier') {\n markSafeIdentifier(prop);\n if (propsBlacklist.has(prop.name)) {\n state.pushError(`Property \"${prop.name}\" is not allowed`, prop);\n }\n }\n else if (prop.type === 'Literal') {\n if (propsBlacklist.has(String(prop.value))) {\n state.pushError(`Property \"${prop.value}\" is not allowed`, prop);\n }\n }\n else {\n state.pushError('Dynamic property lookup is not supported', prop);\n }\n baseVisitor.MemberExpression(node, state, c);\n },\n Function(node, state, c) {\n const name = node.id?.name;\n if (name) {\n // arrow functions have no name\n state.define(name);\n }\n const scope = new Scope(name || '#anonymous', node, state);\n addFunctionParams(node, scope);\n baseVisitor.Function(node, scope, c);\n },\n VariableDeclaration(node, state, c) {\n for (const decl of node.declarations) {\n if (decl.id.type === 'Identifier') {\n markSafeIdentifier(decl.id);\n state.define(decl.id.name);\n }\n else if (decl.id.type === 'ObjectPattern') {\n //the ObjectPattern rule will call markSafeIdentifier\n for (const prop of decl.id.properties) {\n if (prop.type === 'Property') {\n if (prop.value.type === 'Identifier')\n state.define(prop.value.name);\n }\n else if (prop.type === 'RestElement') {\n if (prop.argument.type === 'Identifier')\n state.define(prop.argument.name);\n }\n }\n }\n else if (decl.id.type === 'ArrayPattern') {\n //the ArrayPattern rule will call markSafeIdentifier\n for (const prop of decl.id.elements) {\n if (prop.type === 'Identifier') {\n state.define(prop.name);\n }\n else if (prop.type === 'RestElement') {\n if (prop.argument.type === 'Identifier')\n state.define(prop.argument.name);\n }\n }\n }\n }\n baseVisitor.VariableDeclaration(node, state, c);\n },\n });\n return { root, program };\n}\nexport function validate(code, opts = {}) {\n const { root, program } = buildScopeTree(code, opts);\n recursiveWalk(program, root, {\n Function(node, state, c) {\n baseVisitor.Function(node, node.$scope || state, c);\n },\n Identifier(node, state, c) {\n const identifier = node;\n if (!isSafeIdentifier(node) && !state.isDefined(identifier.name)) {\n state.pushError(`Unknown identifier \"${identifier.name}\"`, node);\n }\n baseVisitor.Identifier(node, state, c);\n },\n });\n return root;\n}\n/**\n * Walk `code` and return every identifier that is referenced but not bound by any\n * enclosing function/variable declaration and not listed in `opts.globals`. Pass\n * known runtime-injected names (e.g. JST's `_`, `Array`, `Set`) in `opts.globals`\n * so they don't appear as free vars. Also surfaces unsafe-construct errors from\n * the same parse pass.\n */\nexport function getFreeVariables(code, opts = {}) {\n const { root, program } = buildScopeTree(code, opts);\n const vars = new Set();\n recursiveWalk(program, root, {\n Function(node, state, c) {\n baseVisitor.Function(node, node.$scope || state, c);\n },\n Identifier(node, state, c) {\n const identifier = node;\n if (!isSafeIdentifier(node) && !state.isDefined(identifier.name)) {\n vars.add(identifier.name);\n }\n baseVisitor.Identifier(node, state, c);\n },\n });\n return { vars, errors: root.errors };\n}\nexport class CompositeError extends Error {\n errors;\n constructor(errors, message) {\n super(`${message || 'Composite Error'}\\n${errors.map((err) => err.message).join('\\n* ')}`);\n this.errors = errors;\n this.errors = errors;\n this.name = 'CompositeError';\n }\n}\n//# sourceMappingURL=validation.js.map","import { validate } from './validation.js';\nexport class Script {\n code;\n globals;\n fn;\n constructor(code, globals) {\n this.code = code;\n this.globals = globals;\n const args = globals && globals.length > 0 ? `{${globals.join(',')}}` : '';\n this.fn = new Function(args, `return (function(){\"use strict\";${code}})()`);\n }\n validate(opts = {}) {\n return validate(this.code, Object.assign({ globals: this.globals }, opts));\n }\n run(context) {\n return this.fn(context);\n }\n}\n//# sourceMappingURL=script.js.map","//add line numbers with {% addLineNumbers(content) %}\nexport function addLineNumbers(content) {\n const lines = content.split('\\n');\n const width = Math.ceil(Math.log10(lines.length));\n return lines\n .map((line, index) => {\n const number = (index + 1).toString().padStart(width, ' ');\n return `{% ${number} %} ${line}`;\n })\n .join('\\n');\n}\n//# sourceMappingURL=addLineNumbers.js.map","import Papa from 'papaparse';\nexport function loadCsv(content, options = {}) {\n return Papa.parse(content, options);\n}\nexport function jsonToCsv(obj, options = {}) {\n return Papa.unparse(obj, options);\n}\n//# sourceMappingURL=csv.js.map","import dayjs from 'dayjs';\nimport { addLineNumbers } from './functions/addLineNumbers.js';\nimport { jsonToCsv, loadCsv } from './functions/csv.js';\nimport { Script } from './script.js';\nimport { TEMPLATE_SYSTEM_VARIABLE_NAMES } from './system.js';\nimport { CompositeError } from './validation.js';\n/** Identifiers every JST template can read besides its declared inputs. */\nexport const JST_TEMPLATE_GLOBALS = ['_', 'Array', 'Set', ...TEMPLATE_SYSTEM_VARIABLE_NAMES];\nexport class CompiledTemplate extends Script {\n constructor(code, globals) {\n // Deduplicate: the globals become a destructured parameter list, where a repeated name\n // (e.g. a schema that also declares `_model`) is a syntax error.\n super(code, [...new Set([...globals, ...JST_TEMPLATE_GLOBALS])]);\n }\n validate() {\n return super.validate({\n acorn: {\n allowReturnOutsideFunction: true,\n locations: true,\n },\n });\n }\n run(context) {\n return super.run({ ...context, Set: Set, Array: Array });\n }\n}\nexport function renderJsTemplate(code, globals, data) {\n const script = new CompiledTemplate(code, globals);\n const state = script.validate();\n if (state.hasErrors()) {\n throw new CompositeError(state.errors, 'Validation errors');\n }\n const content = script.run({\n ...data,\n _: {\n loadCsv,\n jsonToCsv,\n stringify: JSON.stringify,\n addLineNumbers: addLineNumbers,\n dayjs: dayjs,\n },\n });\n return content;\n}\n//# sourceMappingURL=template.js.map"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vertesia/jst",
3
- "version": "1.5.0",
3
+ "version": "1.5.1",
4
4
  "type": "module",
5
5
  "description": "Javascript text template",
6
6
  "license": "Apache-2.0",
@@ -1,15 +1,69 @@
1
1
  import Handlebars from 'handlebars';
2
+ import { TEMPLATE_SYSTEM_VARIABLE_NAMES } from '../system.js';
2
3
 
3
- Handlebars.registerHelper('_now', () => new Date().toISOString());
4
+ /**
5
+ * System variables (see ../system.ts) are injected into the template data. Each one is also
6
+ * registered as a helper returning that injected value, because Handlebars resolves a name
7
+ * differently by position: a bare `{{_now}}` or a subexpression `(_now)` looks up helpers first,
8
+ * while an argument (`{{#if _now}}`) reads the data only. Registering both makes every position
9
+ * render the same value. `_now` falls back to the current time when no value was injected, which
10
+ * keeps templates rendered by callers that do not inject system variables working.
11
+ */
12
+ function systemVariableHelper(name: string): Handlebars.HelperDelegate {
13
+ return (...args: unknown[]) => {
14
+ const options = args[args.length - 1] as Handlebars.HelperOptions | undefined;
15
+ const root: unknown = options?.data?.root;
16
+ const value = root && typeof root === 'object' ? (root as Record<string, unknown>)[name] : undefined;
17
+ if (value !== undefined && value !== null) {
18
+ return value;
19
+ }
20
+ return name === '_now' ? new Date().toISOString() : '';
21
+ };
22
+ }
4
23
 
5
- Handlebars.registerHelper('stringify', (value: unknown) => {
24
+ function stringifyHelper(value: unknown): string | Handlebars.SafeString {
6
25
  if (value === null || value === undefined) return '';
7
26
  if (typeof value === 'string') return value;
8
27
  return new Handlebars.SafeString(JSON.stringify(value));
9
- });
28
+ }
29
+
30
+ const CUSTOM_HELPERS: Record<string, Handlebars.HelperDelegate> = {
31
+ ...Object.fromEntries(TEMPLATE_SYSTEM_VARIABLE_NAMES.map((name) => [name, systemVariableHelper(name)])),
32
+ stringify: stringifyHelper,
33
+ };
34
+
35
+ /**
36
+ * Prompt templates render with a private Handlebars environment, so helpers another module
37
+ * registers on the global instance never become available to prompts without the validator
38
+ * knowing about them.
39
+ */
40
+ const promptHandlebars = Handlebars.create();
41
+ for (const [name, helper] of Object.entries(CUSTOM_HELPERS)) {
42
+ promptHandlebars.registerHelper(name, helper);
43
+ // Kept on the global instance for code that compiled templates with the global Handlebars
44
+ // after importing this module, which used to be where these helpers were registered.
45
+ Handlebars.registerHelper(name, helper);
46
+ }
47
+
48
+ /**
49
+ * Every helper available to prompt templates: Handlebars' built-ins (`if`, `each`, `with`,
50
+ * `lookup`, ...) plus the custom ones above. Read from the environment itself so it cannot drift.
51
+ */
52
+ export const HANDLEBARS_HELPER_NAMES: readonly string[] = Object.keys(promptHandlebars.helpers)
53
+ // Internal fallbacks Handlebars invokes itself; templates never call them by name.
54
+ .filter((name) => name !== 'helperMissing' && name !== 'blockHelperMissing')
55
+ .sort();
56
+
57
+ const HELPER_NAME_SET: ReadonlySet<string> = new Set(HANDLEBARS_HELPER_NAMES);
58
+
59
+ export function isHandlebarsHelper(name: string): boolean {
60
+ return HELPER_NAME_SET.has(name);
61
+ }
62
+
63
+ /** Built-in block helpers that render their block with a new context (`this` is the item). */
64
+ export const HANDLEBARS_CONTEXT_HELPERS: readonly string[] = ['each', 'with'];
10
65
 
11
66
  export function renderHandlebarsTemplate(template: string, input: unknown): string {
12
- // lazy load handlebars to speed up initial loading
13
- const compiled = Handlebars.compile(template);
67
+ const compiled = promptHandlebars.compile(template);
14
68
  return compiled(input);
15
69
  }
package/src/index.ts CHANGED
@@ -1,4 +1,10 @@
1
- export { renderHandlebarsTemplate } from './handlebars/index.js';
1
+ export {
2
+ HANDLEBARS_CONTEXT_HELPERS,
3
+ HANDLEBARS_HELPER_NAMES,
4
+ isHandlebarsHelper,
5
+ renderHandlebarsTemplate,
6
+ } from './handlebars/index.js';
2
7
  export * from './script.js';
8
+ export * from './system.js';
3
9
  export * from './template.js';
4
10
  export * from './validation.js';
package/src/system.ts ADDED
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Registry of the values the prompt runtime supplies to every template, in addition to the
3
+ * caller's input. This is the single source of truth: the renderer injects exactly these
4
+ * names, the validator accepts exactly these names without a schema declaration, and tool
5
+ * descriptions list them from here. Add a system variable here and nowhere else.
6
+ */
7
+ export interface TemplateSystemVariable {
8
+ /** Template name. Always starts with `_` so it cannot collide with ordinary schema properties. */
9
+ name: string;
10
+ /** JSON type of the injected value. */
11
+ type: 'string';
12
+ /** Human-readable description, surfaced in tool descriptions and validation messages. */
13
+ description: string;
14
+ }
15
+
16
+ export const TEMPLATE_SYSTEM_VARIABLES = [
17
+ {
18
+ name: '_now',
19
+ type: 'string',
20
+ description: 'Current date and time as an ISO 8601 string (UTC)',
21
+ },
22
+ {
23
+ name: '_model',
24
+ type: 'string',
25
+ description: 'Identifier of the model the prompt is executed with',
26
+ },
27
+ ] as const satisfies readonly TemplateSystemVariable[];
28
+
29
+ export type TemplateSystemVariableName = (typeof TEMPLATE_SYSTEM_VARIABLES)[number]['name'];
30
+
31
+ export const TEMPLATE_SYSTEM_VARIABLE_NAMES: readonly TemplateSystemVariableName[] = TEMPLATE_SYSTEM_VARIABLES.map(
32
+ (v) => v.name,
33
+ );
34
+
35
+ const SYSTEM_VARIABLE_NAME_SET: ReadonlySet<string> = new Set(TEMPLATE_SYSTEM_VARIABLE_NAMES);
36
+
37
+ export function isTemplateSystemVariable(name: string): name is TemplateSystemVariableName {
38
+ return SYSTEM_VARIABLE_NAME_SET.has(name);
39
+ }
40
+
41
+ /** Runtime context the system variable values are computed from. */
42
+ export interface TemplateSystemContext {
43
+ /** Model id of the execution. `_model` is omitted when unknown. */
44
+ model?: string;
45
+ /** Reference time for `_now`. Defaults to the current time. */
46
+ now?: Date;
47
+ }
48
+
49
+ /** Compute the system variable values for one rendering. */
50
+ export function buildTemplateSystemVariables(context: TemplateSystemContext = {}): Record<string, string> {
51
+ const values: Record<string, string> = {
52
+ _now: (context.now ?? new Date()).toISOString(),
53
+ };
54
+ if (context.model) {
55
+ values._model = context.model;
56
+ }
57
+ return values;
58
+ }
59
+
60
+ /**
61
+ * Return the template data with the system variables added. System values win over caller input
62
+ * of the same name: the names are reserved, so a caller cannot spoof `_model` or `_now`.
63
+ */
64
+ export function withTemplateSystemVariables(
65
+ input: Record<string, unknown>,
66
+ context: TemplateSystemContext = {},
67
+ ): Record<string, unknown> {
68
+ return { ...input, ...buildTemplateSystemVariables(context) };
69
+ }
70
+
71
+ /** One-line summary of the system variables, for tool descriptions and error messages. */
72
+ export function describeTemplateSystemVariables(): string {
73
+ return TEMPLATE_SYSTEM_VARIABLES.map((v) => `${v.name} (${v.description})`).join(', ');
74
+ }
package/src/template.ts CHANGED
@@ -2,11 +2,17 @@ import dayjs from 'dayjs';
2
2
  import { addLineNumbers } from './functions/addLineNumbers.js';
3
3
  import { jsonToCsv, loadCsv } from './functions/csv.js';
4
4
  import { Script } from './script.js';
5
+ import { TEMPLATE_SYSTEM_VARIABLE_NAMES } from './system.js';
5
6
  import { CompositeError } from './validation.js';
6
7
 
8
+ /** Identifiers every JST template can read besides its declared inputs. */
9
+ export const JST_TEMPLATE_GLOBALS: readonly string[] = ['_', 'Array', 'Set', ...TEMPLATE_SYSTEM_VARIABLE_NAMES];
10
+
7
11
  export class CompiledTemplate extends Script<string> {
8
12
  constructor(code: string, globals: string[]) {
9
- super(code, globals.concat('_', 'Array', 'Set'));
13
+ // Deduplicate: the globals become a destructured parameter list, where a repeated name
14
+ // (e.g. a schema that also declares `_model`) is a syntax error.
15
+ super(code, [...new Set([...globals, ...JST_TEMPLATE_GLOBALS])]);
10
16
  }
11
17
  validate() {
12
18
  return super.validate({