typebars 1.0.0
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/README.md +1582 -0
- package/dist/analyzer.d.ts +59 -0
- package/dist/analyzer.js +4 -0
- package/dist/analyzer.js.map +9 -0
- package/dist/chunk-1gm6cf0e.js +5 -0
- package/dist/chunk-1gm6cf0e.js.map +10 -0
- package/dist/chunk-1qwj7pjc.js +4 -0
- package/dist/chunk-1qwj7pjc.js.map +10 -0
- package/dist/chunk-4zv02svp.js +7 -0
- package/dist/chunk-4zv02svp.js.map +10 -0
- package/dist/chunk-60gk3q7z.js +5 -0
- package/dist/chunk-60gk3q7z.js.map +10 -0
- package/dist/chunk-6955jpr7.js +5 -0
- package/dist/chunk-6955jpr7.js.map +10 -0
- package/dist/chunk-6c0pw73w.js +7 -0
- package/dist/chunk-6c0pw73w.js.map +10 -0
- package/dist/chunk-7j6q1e3z.js +5 -0
- package/dist/chunk-7j6q1e3z.js.map +14 -0
- package/dist/chunk-8g0d6h85.js +5 -0
- package/dist/chunk-8g0d6h85.js.map +10 -0
- package/dist/chunk-9mg6qfrs.js +5 -0
- package/dist/chunk-9mg6qfrs.js.map +10 -0
- package/dist/chunk-a37yzqra.js +5 -0
- package/dist/chunk-a37yzqra.js.map +11 -0
- package/dist/chunk-awgj10qg.js +4 -0
- package/dist/chunk-awgj10qg.js.map +10 -0
- package/dist/chunk-fhvf5y4x.js +7 -0
- package/dist/chunk-fhvf5y4x.js.map +10 -0
- package/dist/chunk-ggdfaqhe.js +5 -0
- package/dist/chunk-ggdfaqhe.js.map +10 -0
- package/dist/chunk-hc1jnqaw.js +5 -0
- package/dist/chunk-hc1jnqaw.js.map +10 -0
- package/dist/chunk-kznb0bev.js +5 -0
- package/dist/chunk-kznb0bev.js.map +10 -0
- package/dist/chunk-mx8neh7q.js +5 -0
- package/dist/chunk-mx8neh7q.js.map +10 -0
- package/dist/chunk-p3xzf1ew.js +5 -0
- package/dist/chunk-p3xzf1ew.js.map +10 -0
- package/dist/chunk-qh2r1pa1.js +5 -0
- package/dist/chunk-qh2r1pa1.js.map +10 -0
- package/dist/chunk-qpzzr2rd.js +5 -0
- package/dist/chunk-qpzzr2rd.js.map +10 -0
- package/dist/chunk-vka4e61h.js +7 -0
- package/dist/chunk-vka4e61h.js.map +10 -0
- package/dist/chunk-xbvk4ygq.js +5 -0
- package/dist/chunk-xbvk4ygq.js.map +11 -0
- package/dist/chunk-ybh51hbe.js +7 -0
- package/dist/chunk-ybh51hbe.js.map +10 -0
- package/dist/chunk-yczpjh73.js +4 -0
- package/dist/chunk-yczpjh73.js.map +10 -0
- package/dist/chunk-yraqh2tz.js +5 -0
- package/dist/chunk-yraqh2tz.js.map +10 -0
- package/dist/chunk-z6yh5qvc.js +5 -0
- package/dist/chunk-z6yh5qvc.js.map +10 -0
- package/dist/compiled-template.d.ts +130 -0
- package/dist/compiled-template.js +4 -0
- package/dist/compiled-template.js.map +9 -0
- package/dist/errors.d.ts +93 -0
- package/dist/errors.js +4 -0
- package/dist/errors.js.map +9 -0
- package/dist/executor.d.ts +55 -0
- package/dist/executor.js +4 -0
- package/dist/executor.js.map +9 -0
- package/dist/helpers/helper-factory.d.ts +56 -0
- package/dist/helpers/index.d.ts +4 -0
- package/dist/helpers/logical-helpers.d.ts +15 -0
- package/dist/helpers/math-helpers.d.ts +13 -0
- package/dist/helpers/utils.d.ts +19 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +9 -0
- package/dist/parser.d.ts +146 -0
- package/dist/parser.js +4 -0
- package/dist/parser.js.map +9 -0
- package/dist/schema-resolver.d.ts +50 -0
- package/dist/schema-resolver.js +4 -0
- package/dist/schema-resolver.js.map +9 -0
- package/dist/typebars.d.ts +130 -0
- package/dist/typebars.js +4 -0
- package/dist/typebars.js.map +9 -0
- package/dist/types.d.ts +334 -0
- package/dist/types.js +4 -0
- package/dist/types.js.map +9 -0
- package/dist/utils.d.ts +113 -0
- package/dist/utils.js +4 -0
- package/dist/utils.js.map +9 -0
- package/package.json +42 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import type { JSONSchema7 } from "json-schema";
|
|
2
|
+
import { CompiledTemplate } from "./compiled-template.ts";
|
|
3
|
+
import type { AnalysisResult, AnalyzeAndExecuteOptions, ExecuteOptions, HelperDefinition, TemplateEngineOptions, TemplateInput, ValidationResult } from "./types.ts";
|
|
4
|
+
export declare class Typebars {
|
|
5
|
+
/** Isolated Handlebars environment — each engine has its own helpers */
|
|
6
|
+
private readonly hbs;
|
|
7
|
+
/** LRU cache of parsed ASTs (avoids re-parsing) */
|
|
8
|
+
private readonly astCache;
|
|
9
|
+
/** LRU cache of compiled Handlebars templates (avoids recompilation) */
|
|
10
|
+
private readonly compilationCache;
|
|
11
|
+
/** Custom helpers registered on this instance */
|
|
12
|
+
private readonly helpers;
|
|
13
|
+
constructor(options?: TemplateEngineOptions);
|
|
14
|
+
/**
|
|
15
|
+
* Compiles a template and returns a `CompiledTemplate` ready to be
|
|
16
|
+
* executed or analyzed without re-parsing.
|
|
17
|
+
*
|
|
18
|
+
* Accepts a `TemplateInput`: string, number, boolean, null, or object.
|
|
19
|
+
* For objects, each property is compiled recursively.
|
|
20
|
+
*
|
|
21
|
+
* @param template - The template to compile
|
|
22
|
+
* @returns A reusable `CompiledTemplate`
|
|
23
|
+
*/
|
|
24
|
+
compile(template: TemplateInput): CompiledTemplate;
|
|
25
|
+
/**
|
|
26
|
+
* Statically analyzes a template against a JSON Schema v7 describing
|
|
27
|
+
* the available context.
|
|
28
|
+
*
|
|
29
|
+
* Accepts a `TemplateInput`: string, number, boolean, null, or object.
|
|
30
|
+
* For objects, each property is analyzed recursively and the
|
|
31
|
+
* `outputSchema` reflects the object structure with resolved types.
|
|
32
|
+
*
|
|
33
|
+
* @param template - The template to analyze
|
|
34
|
+
* @param inputSchema - JSON Schema v7 describing the available variables
|
|
35
|
+
* @param identifierSchemas - (optional) Schemas by identifier `{ [id]: JSONSchema7 }`
|
|
36
|
+
*/
|
|
37
|
+
analyze(template: TemplateInput, inputSchema: JSONSchema7, identifierSchemas?: Record<number, JSONSchema7>): AnalysisResult;
|
|
38
|
+
/**
|
|
39
|
+
* Validates a template against a schema without returning the output type.
|
|
40
|
+
*
|
|
41
|
+
* This is an API shortcut for `analyze()` that only returns `valid` and
|
|
42
|
+
* `diagnostics`, without `outputSchema`. The full analysis (including type
|
|
43
|
+
* inference) is executed internally — this method provides no performance
|
|
44
|
+
* gain, only a simplified API.
|
|
45
|
+
*
|
|
46
|
+
* @param template - The template to validate
|
|
47
|
+
* @param inputSchema - JSON Schema v7 describing the available variables
|
|
48
|
+
* @param identifierSchemas - (optional) Schemas by identifier
|
|
49
|
+
*/
|
|
50
|
+
validate(template: TemplateInput, inputSchema: JSONSchema7, identifierSchemas?: Record<number, JSONSchema7>): ValidationResult;
|
|
51
|
+
/**
|
|
52
|
+
* Checks only that the template syntax is valid (parsing).
|
|
53
|
+
* Does not require a schema — useful for quick feedback in an editor.
|
|
54
|
+
*
|
|
55
|
+
* For objects, recursively checks each property.
|
|
56
|
+
*
|
|
57
|
+
* @param template - The template to validate
|
|
58
|
+
* @returns `true` if the template is syntactically correct
|
|
59
|
+
*/
|
|
60
|
+
isValidSyntax(template: TemplateInput): boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Executes a template with the provided data.
|
|
63
|
+
*
|
|
64
|
+
* Accepts a `TemplateInput`: string, number, boolean, null, or object.
|
|
65
|
+
* For objects, each property is executed recursively and an object with
|
|
66
|
+
* resolved values is returned.
|
|
67
|
+
*
|
|
68
|
+
* If a `schema` is provided in options, static analysis is performed
|
|
69
|
+
* before execution. A `TemplateAnalysisError` is thrown on errors.
|
|
70
|
+
*
|
|
71
|
+
* @param template - The template to execute
|
|
72
|
+
* @param data - The context data for rendering
|
|
73
|
+
* @param options - Execution options (schema, identifierData, identifierSchemas)
|
|
74
|
+
* @returns The execution result
|
|
75
|
+
*/
|
|
76
|
+
execute(template: TemplateInput, data: Record<string, unknown>, options?: ExecuteOptions): unknown;
|
|
77
|
+
/**
|
|
78
|
+
* Analyzes a template and, if valid, executes it with the provided data.
|
|
79
|
+
* Returns both the analysis result and the executed value.
|
|
80
|
+
*
|
|
81
|
+
* For objects, each property is analyzed and executed recursively.
|
|
82
|
+
* The entire object is considered invalid if at least one property is.
|
|
83
|
+
*
|
|
84
|
+
* @param template - The template
|
|
85
|
+
* @param inputSchema - JSON Schema v7 describing the available variables
|
|
86
|
+
* @param data - The context data for rendering
|
|
87
|
+
* @param options - (optional) Options for template identifiers
|
|
88
|
+
* @returns An object `{ analysis, value }` where `value` is `undefined`
|
|
89
|
+
* if analysis failed.
|
|
90
|
+
*/
|
|
91
|
+
analyzeAndExecute(template: TemplateInput, inputSchema: JSONSchema7, data: Record<string, unknown>, options?: AnalyzeAndExecuteOptions): {
|
|
92
|
+
analysis: AnalysisResult;
|
|
93
|
+
value: unknown;
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* Registers a custom helper on this engine instance.
|
|
97
|
+
*
|
|
98
|
+
* The helper is available for both execution (via Handlebars) and
|
|
99
|
+
* static analysis (via its declared `returnType`).
|
|
100
|
+
*
|
|
101
|
+
* @param name - Helper name (e.g. `"uppercase"`)
|
|
102
|
+
* @param definition - Helper definition (implementation + return type)
|
|
103
|
+
* @returns `this` to allow chaining
|
|
104
|
+
*/
|
|
105
|
+
registerHelper(name: string, definition: HelperDefinition): this;
|
|
106
|
+
/**
|
|
107
|
+
* Removes a custom helper from this engine instance.
|
|
108
|
+
*
|
|
109
|
+
* @param name - Name of the helper to remove
|
|
110
|
+
* @returns `this` to allow chaining
|
|
111
|
+
*/
|
|
112
|
+
unregisterHelper(name: string): this;
|
|
113
|
+
/**
|
|
114
|
+
* Checks whether a helper is registered on this instance.
|
|
115
|
+
*
|
|
116
|
+
* @param name - Helper name
|
|
117
|
+
* @returns `true` if the helper is registered
|
|
118
|
+
*/
|
|
119
|
+
hasHelper(name: string): boolean;
|
|
120
|
+
/**
|
|
121
|
+
* Clears all internal caches (AST + compilation).
|
|
122
|
+
*
|
|
123
|
+
* Useful after a configuration change or to free memory.
|
|
124
|
+
*/
|
|
125
|
+
clearCaches(): void;
|
|
126
|
+
/**
|
|
127
|
+
* Retrieves the AST of a template from the cache, or parses and caches it.
|
|
128
|
+
*/
|
|
129
|
+
private getCachedAst;
|
|
130
|
+
}
|
package/dist/typebars.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{a}from"./chunk-7j6q1e3z.js";import"./chunk-kznb0bev.js";import"./chunk-1qwj7pjc.js";import"./chunk-qpzzr2rd.js";import"./chunk-6955jpr7.js";import"./chunk-1gm6cf0e.js";import"./chunk-ybh51hbe.js";import"./chunk-8g0d6h85.js";import"./chunk-4zv02svp.js";export{a as Typebars};
|
|
2
|
+
|
|
3
|
+
//# debugId=AADE9F1482151B2C64756E2164756E21
|
|
4
|
+
//# sourceMappingURL=typebars.js.map
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
import type { JSONSchema7 } from "json-schema";
|
|
2
|
+
import type { FromSchema, JSONSchema } from "json-schema-to-ts";
|
|
3
|
+
/**
|
|
4
|
+
* Object where each property is a `TemplateInput` (recursive).
|
|
5
|
+
*
|
|
6
|
+
* Allows passing an entire structure as a template:
|
|
7
|
+
* ```
|
|
8
|
+
* engine.analyze({
|
|
9
|
+
* userName: "{{name}}",
|
|
10
|
+
* userAge: "{{age}}",
|
|
11
|
+
* nested: { x: "{{foo}}" },
|
|
12
|
+
* }, inputSchema);
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export interface TemplateInputObject {
|
|
16
|
+
[key: string]: TemplateInput;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Input type accepted by the template engine.
|
|
20
|
+
*
|
|
21
|
+
* - `string` → standard Handlebars template (parsed and executed)
|
|
22
|
+
* - `number` → numeric literal (passthrough)
|
|
23
|
+
* - `boolean` → boolean literal (passthrough)
|
|
24
|
+
* - `null` → null literal (passthrough)
|
|
25
|
+
* - `TemplateInputObject` → object where each property is a `TemplateInput`
|
|
26
|
+
*/
|
|
27
|
+
export type TemplateInput = string | number | boolean | null | TemplateInputObject;
|
|
28
|
+
/**
|
|
29
|
+
* Checks whether a value is a non-string primitive literal (number, boolean, null).
|
|
30
|
+
* These values are treated as passthrough by the engine.
|
|
31
|
+
*
|
|
32
|
+
* Note: objects (`TemplateInputObject`) are NOT literals.
|
|
33
|
+
*/
|
|
34
|
+
export declare function isLiteralInput(input: TemplateInput): input is number | boolean | null;
|
|
35
|
+
/**
|
|
36
|
+
* Checks whether a value is a template object (`TemplateInputObject`).
|
|
37
|
+
* Template objects are processed recursively by the engine:
|
|
38
|
+
* each property is analyzed/executed individually.
|
|
39
|
+
*/
|
|
40
|
+
export declare function isObjectInput(input: TemplateInput): input is TemplateInputObject;
|
|
41
|
+
/**
|
|
42
|
+
* Infers the JSON Schema of a non-string primitive value.
|
|
43
|
+
*
|
|
44
|
+
* @param value - The primitive value (number, boolean, null)
|
|
45
|
+
* @returns The corresponding JSON Schema
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```
|
|
49
|
+
* inferPrimitiveSchema(42) // → { type: "number" }
|
|
50
|
+
* inferPrimitiveSchema(true) // → { type: "boolean" }
|
|
51
|
+
* inferPrimitiveSchema(null) // → { type: "null" }
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export declare function inferPrimitiveSchema(value: number | boolean | null): JSONSchema7;
|
|
55
|
+
export type DiagnosticCode =
|
|
56
|
+
/** The referenced property does not exist in the context schema */
|
|
57
|
+
"UNKNOWN_PROPERTY"
|
|
58
|
+
/** Type mismatch (e.g. #each on a non-array) */
|
|
59
|
+
| "TYPE_MISMATCH"
|
|
60
|
+
/** A block helper is used without a required argument */
|
|
61
|
+
| "MISSING_ARGUMENT"
|
|
62
|
+
/** Unknown block helper (neither built-in nor registered) */
|
|
63
|
+
| "UNKNOWN_HELPER"
|
|
64
|
+
/** The expression cannot be statically analyzed */
|
|
65
|
+
| "UNANALYZABLE"
|
|
66
|
+
/** The {{key:N}} syntax is used but no identifierSchemas were provided */
|
|
67
|
+
| "MISSING_IDENTIFIER_SCHEMAS"
|
|
68
|
+
/** The identifier N does not exist in the provided identifierSchemas */
|
|
69
|
+
| "UNKNOWN_IDENTIFIER"
|
|
70
|
+
/** The property does not exist in the identifier's schema */
|
|
71
|
+
| "IDENTIFIER_PROPERTY_NOT_FOUND"
|
|
72
|
+
/** Syntax error in the template */
|
|
73
|
+
| "PARSE_ERROR";
|
|
74
|
+
export interface DiagnosticDetails {
|
|
75
|
+
/** Path of the expression that caused the error (e.g. `"user.name.foo"`) */
|
|
76
|
+
path?: string;
|
|
77
|
+
/** Name of the helper involved (for helper-related errors) */
|
|
78
|
+
helperName?: string;
|
|
79
|
+
/** What was expected (e.g. `"array"`, `"property to exist"`) */
|
|
80
|
+
expected?: string;
|
|
81
|
+
/** What was found (e.g. `"string"`, `"undefined"`) */
|
|
82
|
+
actual?: string;
|
|
83
|
+
/** Available properties in the current schema (for suggestions) */
|
|
84
|
+
availableProperties?: string[];
|
|
85
|
+
/** Template identifier number (for `{{key:N}}` errors) */
|
|
86
|
+
identifier?: number;
|
|
87
|
+
}
|
|
88
|
+
/** Diagnostic produced by the static analyzer */
|
|
89
|
+
export interface TemplateDiagnostic {
|
|
90
|
+
/** "error" blocks execution, "warning" is informational */
|
|
91
|
+
severity: "error" | "warning";
|
|
92
|
+
/** Machine-readable code identifying the error type */
|
|
93
|
+
code: DiagnosticCode;
|
|
94
|
+
/** Human-readable message describing the problem */
|
|
95
|
+
message: string;
|
|
96
|
+
/** Position in the template source (if available from the AST) */
|
|
97
|
+
loc?: {
|
|
98
|
+
start: {
|
|
99
|
+
line: number;
|
|
100
|
+
column: number;
|
|
101
|
+
};
|
|
102
|
+
end: {
|
|
103
|
+
line: number;
|
|
104
|
+
column: number;
|
|
105
|
+
};
|
|
106
|
+
};
|
|
107
|
+
/** Fragment of the template source around the error */
|
|
108
|
+
source?: string;
|
|
109
|
+
/** Structured information for debugging and frontend display */
|
|
110
|
+
details?: DiagnosticDetails;
|
|
111
|
+
}
|
|
112
|
+
/** Complete result of the static analysis */
|
|
113
|
+
export interface AnalysisResult {
|
|
114
|
+
/** true if no errors (warnings are tolerated) */
|
|
115
|
+
valid: boolean;
|
|
116
|
+
/** List of diagnostics (errors + warnings) */
|
|
117
|
+
diagnostics: TemplateDiagnostic[];
|
|
118
|
+
/** JSON Schema describing the template's return type */
|
|
119
|
+
outputSchema: JSONSchema7;
|
|
120
|
+
}
|
|
121
|
+
/** Lightweight validation result (without output type inference) */
|
|
122
|
+
export interface ValidationResult {
|
|
123
|
+
/** true if no errors (warnings are tolerated) */
|
|
124
|
+
valid: boolean;
|
|
125
|
+
/** List of diagnostics (errors + warnings) */
|
|
126
|
+
diagnostics: TemplateDiagnostic[];
|
|
127
|
+
}
|
|
128
|
+
export interface TemplateEngineOptions {
|
|
129
|
+
/**
|
|
130
|
+
* Capacity of the parsed AST cache. Each parsed template is cached
|
|
131
|
+
* to avoid costly re-parsing on repeated calls.
|
|
132
|
+
* @default 256
|
|
133
|
+
*/
|
|
134
|
+
astCacheSize?: number;
|
|
135
|
+
/**
|
|
136
|
+
* Capacity of the compiled Handlebars template cache.
|
|
137
|
+
* @default 256
|
|
138
|
+
*/
|
|
139
|
+
compilationCacheSize?: number;
|
|
140
|
+
/**
|
|
141
|
+
* Custom helpers to register during engine construction.
|
|
142
|
+
*
|
|
143
|
+
* Each entry describes a helper with its name, implementation,
|
|
144
|
+
* expected parameters, and return type.
|
|
145
|
+
*
|
|
146
|
+
* @example
|
|
147
|
+
* ```
|
|
148
|
+
* const engine = new Typebars({
|
|
149
|
+
* helpers: [
|
|
150
|
+
* {
|
|
151
|
+
* name: "uppercase",
|
|
152
|
+
* description: "Converts a string to uppercase",
|
|
153
|
+
* fn: (value: string) => String(value).toUpperCase(),
|
|
154
|
+
* params: [
|
|
155
|
+
* { name: "value", type: { type: "string" }, description: "The string to convert" },
|
|
156
|
+
* ],
|
|
157
|
+
* returnType: { type: "string" },
|
|
158
|
+
* },
|
|
159
|
+
* ],
|
|
160
|
+
* });
|
|
161
|
+
* ```
|
|
162
|
+
*/
|
|
163
|
+
helpers?: HelperConfig[];
|
|
164
|
+
}
|
|
165
|
+
export interface ExecuteOptions {
|
|
166
|
+
/** JSON Schema for pre-execution static validation */
|
|
167
|
+
schema?: JSONSchema7;
|
|
168
|
+
/** Data by identifier `{ [id]: { key: value } }` */
|
|
169
|
+
identifierData?: Record<number, Record<string, unknown>>;
|
|
170
|
+
/** Schemas by identifier (for static validation with identifiers) */
|
|
171
|
+
identifierSchemas?: Record<number, JSONSchema7>;
|
|
172
|
+
}
|
|
173
|
+
export interface AnalyzeAndExecuteOptions {
|
|
174
|
+
/** Schemas by identifier `{ [id]: JSONSchema7 }` for static analysis */
|
|
175
|
+
identifierSchemas?: Record<number, JSONSchema7>;
|
|
176
|
+
/** Data by identifier `{ [id]: { key: value } }` for execution */
|
|
177
|
+
identifierData?: Record<number, Record<string, unknown>>;
|
|
178
|
+
}
|
|
179
|
+
/** Describes a parameter expected by a helper */
|
|
180
|
+
export interface HelperParam {
|
|
181
|
+
/** Parameter name (for documentation / introspection) */
|
|
182
|
+
name: string;
|
|
183
|
+
/**
|
|
184
|
+
* JSON Schema describing the expected type for this parameter.
|
|
185
|
+
* Used for documentation and static validation.
|
|
186
|
+
*/
|
|
187
|
+
type?: JSONSchema7;
|
|
188
|
+
/** Human-readable description of the parameter */
|
|
189
|
+
description?: string;
|
|
190
|
+
/**
|
|
191
|
+
* Whether the parameter is optional.
|
|
192
|
+
* @default false
|
|
193
|
+
*/
|
|
194
|
+
optional?: boolean;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Definition of a helper registerable via `registerHelper()`.
|
|
198
|
+
*
|
|
199
|
+
* Contains the runtime implementation and typing metadata
|
|
200
|
+
* for static analysis.
|
|
201
|
+
*/
|
|
202
|
+
export interface HelperDefinition {
|
|
203
|
+
/**
|
|
204
|
+
* Runtime implementation of the helper — will be registered with Handlebars.
|
|
205
|
+
*
|
|
206
|
+
* For an inline helper `{{uppercase name}}`:
|
|
207
|
+
* `(value: string) => string`
|
|
208
|
+
*
|
|
209
|
+
* For a block helper `{{#repeat count}}...{{/repeat}}`:
|
|
210
|
+
* `function(this: any, count: number, options: Handlebars.HelperOptions) { ... }`
|
|
211
|
+
*/
|
|
212
|
+
fn: (...args: any[]) => unknown;
|
|
213
|
+
/**
|
|
214
|
+
* Parameters expected by the helper (for documentation and analysis).
|
|
215
|
+
*
|
|
216
|
+
* @example
|
|
217
|
+
* ```
|
|
218
|
+
* params: [
|
|
219
|
+
* { name: "value", type: { type: "number" }, description: "The value to round" },
|
|
220
|
+
* { name: "precision", type: { type: "number" }, description: "Decimal places", optional: true },
|
|
221
|
+
* ]
|
|
222
|
+
* ```
|
|
223
|
+
*/
|
|
224
|
+
params?: HelperParam[];
|
|
225
|
+
/**
|
|
226
|
+
* JSON Schema describing the helper's return type for static analysis.
|
|
227
|
+
* @default { type: "string" }
|
|
228
|
+
*/
|
|
229
|
+
returnType?: JSONSchema7;
|
|
230
|
+
/** Human-readable description of the helper */
|
|
231
|
+
description?: string;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Full helper configuration for registration via the `Typebars({ helpers: [...] })`
|
|
235
|
+
* constructor options.
|
|
236
|
+
*
|
|
237
|
+
* Extends `HelperDefinition` with a required `name`.
|
|
238
|
+
*
|
|
239
|
+
* @example
|
|
240
|
+
* ```
|
|
241
|
+
* const config: HelperConfig = {
|
|
242
|
+
* name: "round",
|
|
243
|
+
* description: "Rounds a number to a given precision",
|
|
244
|
+
* fn: (value: number, precision?: number) => { ... },
|
|
245
|
+
* params: [
|
|
246
|
+
* { name: "value", type: { type: "number" } },
|
|
247
|
+
* { name: "precision", type: { type: "number" }, optional: true },
|
|
248
|
+
* ],
|
|
249
|
+
* returnType: { type: "number" },
|
|
250
|
+
* };
|
|
251
|
+
* ```
|
|
252
|
+
*/
|
|
253
|
+
export interface HelperConfig extends HelperDefinition {
|
|
254
|
+
/** Name of the helper as used in templates (e.g. `"uppercase"`) */
|
|
255
|
+
name: string;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Param definition used for type inference.
|
|
259
|
+
* Accepts `JSONSchema` from `json-schema-to-ts` to allow `FromSchema`
|
|
260
|
+
* to resolve literal types.
|
|
261
|
+
*/
|
|
262
|
+
type TypedHelperParam = {
|
|
263
|
+
readonly name: string;
|
|
264
|
+
readonly type?: JSONSchema;
|
|
265
|
+
readonly description?: string;
|
|
266
|
+
readonly optional?: boolean;
|
|
267
|
+
};
|
|
268
|
+
/**
|
|
269
|
+
* Infers the TypeScript type of a single parameter from its JSON Schema.
|
|
270
|
+
* - If `optional: true`, the resolved type is unioned with `undefined`.
|
|
271
|
+
* - If `type` is not provided, the type is `unknown`.
|
|
272
|
+
*/
|
|
273
|
+
type InferParamType<P> = P extends {
|
|
274
|
+
readonly type: infer S extends JSONSchema;
|
|
275
|
+
readonly optional: true;
|
|
276
|
+
} ? FromSchema<S> | undefined : P extends {
|
|
277
|
+
readonly type: infer S extends JSONSchema;
|
|
278
|
+
} ? FromSchema<S> : unknown;
|
|
279
|
+
/**
|
|
280
|
+
* Maps a tuple of `TypedHelperParam` to a tuple of inferred TypeScript types,
|
|
281
|
+
* usable as the `fn` signature.
|
|
282
|
+
*
|
|
283
|
+
* @example
|
|
284
|
+
* ```
|
|
285
|
+
* type Args = InferArgs<readonly [
|
|
286
|
+
* { name: "a"; type: { type: "string" } },
|
|
287
|
+
* { name: "b"; type: { type: "number" }; optional: true },
|
|
288
|
+
* ]>;
|
|
289
|
+
* // => [string, number | undefined]
|
|
290
|
+
* ```
|
|
291
|
+
*/
|
|
292
|
+
type InferArgs<P extends readonly TypedHelperParam[]> = {
|
|
293
|
+
[K in keyof P]: InferParamType<P[K]>;
|
|
294
|
+
};
|
|
295
|
+
/**
|
|
296
|
+
* Helper configuration with generic parameter inference.
|
|
297
|
+
* Used exclusively by `defineHelper()`.
|
|
298
|
+
*/
|
|
299
|
+
interface TypedHelperConfig<P extends readonly TypedHelperParam[]> {
|
|
300
|
+
name: string;
|
|
301
|
+
description?: string;
|
|
302
|
+
params: P;
|
|
303
|
+
fn: (...args: InferArgs<P>) => unknown;
|
|
304
|
+
returnType?: JSONSchema;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Creates a `HelperConfig` with automatic type inference for `fn` arguments
|
|
308
|
+
* based on the JSON Schemas declared in `params`.
|
|
309
|
+
*
|
|
310
|
+
* The generic parameter `const P` preserves schema literal types
|
|
311
|
+
* (equivalent of `as const`), enabling `FromSchema` to resolve the
|
|
312
|
+
* corresponding TypeScript types.
|
|
313
|
+
*
|
|
314
|
+
* @example
|
|
315
|
+
* ```
|
|
316
|
+
* const helper = defineHelper({
|
|
317
|
+
* name: "concat",
|
|
318
|
+
* description: "Concatenates two strings",
|
|
319
|
+
* params: [
|
|
320
|
+
* { name: "a", type: { type: "string" }, description: "First string" },
|
|
321
|
+
* { name: "b", type: { type: "string" }, description: "Second string" },
|
|
322
|
+
* { name: "sep", type: { type: "string" }, description: "Separator", optional: true },
|
|
323
|
+
* ],
|
|
324
|
+
* fn: (a, b, sep) => {
|
|
325
|
+
* // a: string, b: string, sep: string | undefined
|
|
326
|
+
* const separator = sep ?? "";
|
|
327
|
+
* return `${a}${separator}${b}`;
|
|
328
|
+
* },
|
|
329
|
+
* returnType: { type: "string" },
|
|
330
|
+
* });
|
|
331
|
+
* ```
|
|
332
|
+
*/
|
|
333
|
+
export declare function defineHelper<const P extends readonly TypedHelperParam[]>(config: TypedHelperConfig<P>): HelperConfig;
|
|
334
|
+
export {};
|
package/dist/types.js
ADDED
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type { JSONSchema7 } from "json-schema";
|
|
2
|
+
import type { AnalysisResult } from "./types.ts";
|
|
3
|
+
/**
|
|
4
|
+
* Recursively compares two JSON-compatible values.
|
|
5
|
+
*
|
|
6
|
+
* @param a - First value
|
|
7
|
+
* @param b - Second value
|
|
8
|
+
* @returns `true` if the two values are structurally identical
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```
|
|
12
|
+
* deepEqual({ a: 1, b: 2 }, { b: 2, a: 1 }) // → true
|
|
13
|
+
* deepEqual([1, 2], [1, 2]) // → true
|
|
14
|
+
* deepEqual({ a: 1 }, { a: 2 }) // → false
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export declare function deepEqual(a: unknown, b: unknown): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Simple fixed-capacity LRU cache.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```
|
|
23
|
+
* const cache = new LRUCache<string, number>(2);
|
|
24
|
+
* cache.set("a", 1);
|
|
25
|
+
* cache.set("b", 2);
|
|
26
|
+
* cache.get("a"); // → 1 (marks "a" as recently used)
|
|
27
|
+
* cache.set("c", 3); // evicts "b" (least recently used)
|
|
28
|
+
* cache.get("b"); // → undefined
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export declare class LRUCache<K, V> {
|
|
32
|
+
private readonly capacity;
|
|
33
|
+
private readonly cache;
|
|
34
|
+
constructor(capacity: number);
|
|
35
|
+
/**
|
|
36
|
+
* Retrieves a value from the cache. Returns `undefined` if absent.
|
|
37
|
+
* Marks the entry as recently used.
|
|
38
|
+
*/
|
|
39
|
+
get(key: K): V | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Inserts or updates a value in the cache.
|
|
42
|
+
* If the cache is full, evicts the least recently used entry.
|
|
43
|
+
*/
|
|
44
|
+
set(key: K, value: V): void;
|
|
45
|
+
/**
|
|
46
|
+
* Checks whether a key exists in the cache (without affecting LRU order).
|
|
47
|
+
*/
|
|
48
|
+
has(key: K): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Removes an entry from the cache.
|
|
51
|
+
* @returns `true` if the entry existed and was removed
|
|
52
|
+
*/
|
|
53
|
+
delete(key: K): boolean;
|
|
54
|
+
/** Clears the entire cache. */
|
|
55
|
+
clear(): void;
|
|
56
|
+
/** Number of entries currently in the cache. */
|
|
57
|
+
get size(): number;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Extracts a template fragment around a given position.
|
|
61
|
+
*
|
|
62
|
+
* @param template - The full template source
|
|
63
|
+
* @param loc - The position (line/column, 1-based) of the error
|
|
64
|
+
* @returns The corresponding code fragment (trimmed)
|
|
65
|
+
*/
|
|
66
|
+
export declare function extractSourceSnippet(template: string, loc: {
|
|
67
|
+
start: {
|
|
68
|
+
line: number;
|
|
69
|
+
column: number;
|
|
70
|
+
};
|
|
71
|
+
end: {
|
|
72
|
+
line: number;
|
|
73
|
+
column: number;
|
|
74
|
+
};
|
|
75
|
+
}): string;
|
|
76
|
+
/**
|
|
77
|
+
* Lists the declared property names in a JSON Schema.
|
|
78
|
+
* Returns an empty array if the schema has no `properties`.
|
|
79
|
+
*/
|
|
80
|
+
export declare function getSchemaPropertyNames(schema: JSONSchema7): string[];
|
|
81
|
+
/**
|
|
82
|
+
* Aggregates analysis results from a set of named entries into a single
|
|
83
|
+
* `AnalysisResult` with an object-typed `outputSchema`.
|
|
84
|
+
*
|
|
85
|
+
* @param keys - The keys of the object to analyze
|
|
86
|
+
* @param analyzeEntry - Callback that analyzes an entry by its key
|
|
87
|
+
* @returns An aggregated `AnalysisResult`
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```
|
|
91
|
+
* aggregateObjectAnalysis(
|
|
92
|
+
* Object.keys(template),
|
|
93
|
+
* (key) => analyze(template[key], inputSchema),
|
|
94
|
+
* );
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
export declare function aggregateObjectAnalysis(keys: string[], analyzeEntry: (key: string) => AnalysisResult): AnalysisResult;
|
|
98
|
+
/**
|
|
99
|
+
* Aggregates both analysis **and** execution results from a set of named
|
|
100
|
+
* entries. Returns the aggregated `AnalysisResult` and the object of
|
|
101
|
+
* executed values (or `undefined` if at least one entry is invalid).
|
|
102
|
+
*
|
|
103
|
+
* @param keys - The keys of the object
|
|
104
|
+
* @param processEntry - Callback that analyzes and executes an entry by its key
|
|
105
|
+
* @returns Aggregated `{ analysis, value }`
|
|
106
|
+
*/
|
|
107
|
+
export declare function aggregateObjectAnalysisAndExecution(keys: string[], processEntry: (key: string) => {
|
|
108
|
+
analysis: AnalysisResult;
|
|
109
|
+
value: unknown;
|
|
110
|
+
}): {
|
|
111
|
+
analysis: AnalysisResult;
|
|
112
|
+
value: unknown;
|
|
113
|
+
};
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{M as a,N as b,O as c,P as d,Q as e,R as f}from"./chunk-4zv02svp.js";export{d as getSchemaPropertyNames,c as extractSourceSnippet,a as deepEqual,f as aggregateObjectAnalysisAndExecution,e as aggregateObjectAnalysis,b as LRUCache};
|
|
2
|
+
|
|
3
|
+
//# debugId=F71EB52AAB98830164756E2164756E21
|
|
4
|
+
//# sourceMappingURL=utils.js.map
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "typebars",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"description": "Typebars is a type-safe handlebars based template engine for generating object or content with static type checking",
|
|
6
|
+
"author": {
|
|
7
|
+
"email": "arthurtinseau@live.fr",
|
|
8
|
+
"url": "https://github.com/atinseau/typebars",
|
|
9
|
+
"name": "atinseau"
|
|
10
|
+
},
|
|
11
|
+
"main": "dist/index.js",
|
|
12
|
+
"types": "dist/index.d.ts",
|
|
13
|
+
"type": "module",
|
|
14
|
+
"sideEffects": false,
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"postinstall": "lefthook install",
|
|
21
|
+
"build:bun": "bun build src/*.ts --outdir ./dist --production --minify --root src --sourcemap --splitting --target node --format esm --packages external --chunk-naming='chunk-[hash].js'",
|
|
22
|
+
"build:tsc": "tsc --project tsconfig.build.json",
|
|
23
|
+
"build": "bun --parallel build:bun build:tsc",
|
|
24
|
+
"prepublishOnly": "bun run build",
|
|
25
|
+
"check-types": "tsc --noEmit",
|
|
26
|
+
"check": "biome check --write --unsafe",
|
|
27
|
+
"test": "bun test"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@biomejs/biome": "2.4.3",
|
|
31
|
+
"@types/bun": "latest",
|
|
32
|
+
"@types/json-schema": "7.0.15",
|
|
33
|
+
"lefthook": "2.1.1"
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"typescript": "^5"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"handlebars": "4.7.8",
|
|
40
|
+
"json-schema-to-ts": "3.1.1"
|
|
41
|
+
}
|
|
42
|
+
}
|