typebars 1.0.19 → 1.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/analyzer.js +1 -1
- package/dist/cjs/analyzer.js.map +1 -1
- package/dist/cjs/executor.js +1 -1
- package/dist/cjs/executor.js.map +1 -1
- package/dist/cjs/schema-resolver.d.ts +36 -3
- package/dist/cjs/schema-resolver.js +1 -1
- package/dist/cjs/schema-resolver.js.map +1 -1
- package/dist/cjs/types.d.ts +3 -1
- package/dist/cjs/types.js.map +1 -1
- package/dist/cjs/utils.js +1 -1
- package/dist/cjs/utils.js.map +1 -1
- package/dist/esm/analyzer.js +1 -1
- package/dist/esm/analyzer.js.map +1 -1
- package/dist/esm/executor.js +1 -1
- package/dist/esm/executor.js.map +1 -1
- package/dist/esm/schema-resolver.d.ts +36 -3
- package/dist/esm/schema-resolver.js +1 -1
- package/dist/esm/schema-resolver.js.map +1 -1
- package/dist/esm/types.d.ts +3 -1
- package/dist/esm/types.js.map +1 -1
- package/dist/esm/utils.js +1 -1
- package/dist/esm/utils.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { JSONSchema7 } from "json-schema";
|
|
2
2
|
/**
|
|
3
|
-
* Recursively
|
|
4
|
-
*
|
|
3
|
+
* Recursively scans a JSON Schema tree for `if/then/else` conditional keywords
|
|
4
|
+
* and returns their locations as an array of `{ keyword, schemaPath }` objects.
|
|
5
5
|
*
|
|
6
|
-
* This
|
|
6
|
+
* This is the non-throwing counterpart of `assertNoConditionalSchema`.
|
|
7
|
+
* It traverses the entire schema tree, including:
|
|
7
8
|
* - `properties` values
|
|
8
9
|
* - `additionalProperties` (when it's a schema)
|
|
9
10
|
* - `items` (single schema or tuple)
|
|
@@ -14,6 +15,38 @@ import type { JSONSchema7 } from "json-schema";
|
|
|
14
15
|
* A `Set<object>` is used to track visited schemas and prevent infinite loops
|
|
15
16
|
* from circular structures.
|
|
16
17
|
*
|
|
18
|
+
* @param schema - The JSON Schema to scan
|
|
19
|
+
* @param path - The current JSON pointer path (for location reporting)
|
|
20
|
+
* @param visited - Set of already-visited schema objects (cycle protection)
|
|
21
|
+
* @returns Array of locations where `if/then/else` keywords were found
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```
|
|
25
|
+
* // Returns [{ keyword: "if/then/else", schemaPath: "/" }]:
|
|
26
|
+
* findConditionalSchemaLocations({
|
|
27
|
+
* type: "object",
|
|
28
|
+
* if: { properties: { kind: { const: "a" } } },
|
|
29
|
+
* then: { properties: { a: { type: "string" } } },
|
|
30
|
+
* });
|
|
31
|
+
*
|
|
32
|
+
* // Returns []:
|
|
33
|
+
* findConditionalSchemaLocations({
|
|
34
|
+
* type: "object",
|
|
35
|
+
* properties: { name: { type: "string" } },
|
|
36
|
+
* });
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export declare function findConditionalSchemaLocations(schema: JSONSchema7, path?: string, visited?: Set<object>): Array<{
|
|
40
|
+
keyword: string;
|
|
41
|
+
schemaPath: string;
|
|
42
|
+
}>;
|
|
43
|
+
/**
|
|
44
|
+
* Recursively validates that a JSON Schema does not contain `if/then/else`
|
|
45
|
+
* conditional keywords. Throws an `UnsupportedSchemaError` if any are found.
|
|
46
|
+
*
|
|
47
|
+
* @deprecated Use `findConditionalSchemaLocations` for diagnostic-based reporting.
|
|
48
|
+
* This function is preserved for backward compatibility.
|
|
49
|
+
*
|
|
17
50
|
* @param schema - The JSON Schema to validate
|
|
18
51
|
* @param path - The current JSON pointer path (for error reporting)
|
|
19
52
|
* @param visited - Set of already-visited schema objects (cycle protection)
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{UnsupportedSchemaError}from"./errors.js";import{deepEqual}from"./utils.js";export function
|
|
1
|
+
import{UnsupportedSchemaError}from"./errors.js";import{deepEqual}from"./utils.js";export function findConditionalSchemaLocations(schema,path="",visited=new Set){const locations=[];if(visited.has(schema))return locations;visited.add(schema);for(const kw of["if","then","else"]){if(schema[kw]!==undefined){locations.push({keyword:"if/then/else",schemaPath:path||"/"});break}}if(schema.properties){for(const[key,prop]of Object.entries(schema.properties)){if(prop&&typeof prop!=="boolean"){locations.push(...findConditionalSchemaLocations(prop,`${path}/properties/${key}`,visited))}}}if(schema.additionalProperties&&typeof schema.additionalProperties==="object"){locations.push(...findConditionalSchemaLocations(schema.additionalProperties,`${path}/additionalProperties`,visited))}if(schema.items){if(Array.isArray(schema.items)){for(let i=0;i<schema.items.length;i++){const item=schema.items[i];if(item&&typeof item!=="boolean"){locations.push(...findConditionalSchemaLocations(item,`${path}/items/${i}`,visited))}}}else if(typeof schema.items!=="boolean"){locations.push(...findConditionalSchemaLocations(schema.items,`${path}/items`,visited))}}for(const keyword of["allOf","anyOf","oneOf"]){const branches=schema[keyword];if(branches){for(let i=0;i<branches.length;i++){const branch=branches[i];if(branch&&typeof branch!=="boolean"){locations.push(...findConditionalSchemaLocations(branch,`${path}/${keyword}/${i}`,visited))}}}}if(schema.not&&typeof schema.not!=="boolean"){locations.push(...findConditionalSchemaLocations(schema.not,`${path}/not`,visited))}for(const defsKey of["definitions","$defs"]){const defs=schema[defsKey];if(defs){for(const[name,def]of Object.entries(defs)){if(def&&typeof def!=="boolean"){locations.push(...findConditionalSchemaLocations(def,`${path}/${defsKey}/${name}`,visited))}}}}return locations}export function assertNoConditionalSchema(schema,path="",visited=new Set){const locations=findConditionalSchemaLocations(schema,path,visited);if(locations.length>0){const first=locations[0];throw new UnsupportedSchemaError(first.keyword,first.schemaPath)}}export function resolveRef(schema,root){if(!schema.$ref)return schema;const ref=schema.$ref;const match=ref.match(/^#\/(definitions|\$defs)\/(.+)$/);if(!match){throw new Error(`Unsupported $ref format: "${ref}". Only internal #/definitions/ references are supported.`)}const defsKey=match[1];const name=match[2]??"";const defs=defsKey==="definitions"?root.definitions:root.$defs;if(!defs||!(name in defs)){throw new Error(`Cannot resolve $ref "${ref}": definition "${name}" not found.`)}const def=defs[name];if(!def||typeof def==="boolean"){throw new Error(`Cannot resolve $ref "${ref}": definition "${name}" not found.`)}return resolveRef(def,root)}function resolveSegment(schema,segment,root){const resolved=resolveRef(schema,root);if(resolved.properties&&segment in resolved.properties){const prop=resolved.properties[segment];if(prop&&typeof prop!=="boolean")return resolveRef(prop,root);if(prop===true)return{}}if(resolved.additionalProperties!==undefined&&resolved.additionalProperties!==false){if(resolved.additionalProperties===true){return{}}return resolveRef(resolved.additionalProperties,root)}const schemaType=resolved.type;const isArray=schemaType==="array"||Array.isArray(schemaType)&&schemaType.includes("array");if(isArray&&segment==="length"){return{type:"integer"}}if(isArray&&/^\d+$/.test(segment)){if(resolved.items===undefined){return{}}if(typeof resolved.items==="boolean"){return{}}if(Array.isArray(resolved.items)){const idx=Number.parseInt(segment,10);const item=resolved.items[idx];if(item!==undefined&&typeof item!=="boolean"){return resolveRef(item,root)}if(item!==undefined&&typeof item==="boolean"){return{}}if(resolved.additionalItems===false){return undefined}if(resolved.additionalItems!==undefined&&resolved.additionalItems!==true&&typeof resolved.additionalItems==="object"){return resolveRef(resolved.additionalItems,root)}return{}}return resolveRef(resolved.items,root)}const combinatorResult=resolveInCombinators(resolved,segment,root);if(combinatorResult)return combinatorResult;return undefined}function resolveInCombinators(schema,segment,root){if(schema.allOf){const matches=schema.allOf.filter(b=>typeof b!=="boolean").map(branch=>resolveSegment(branch,segment,root)).filter(s=>s!==undefined);if(matches.length===1)return matches[0];if(matches.length>1)return{allOf:matches}}for(const key of["anyOf","oneOf"]){if(!schema[key])continue;const matches=schema[key].filter(b=>typeof b!=="boolean").map(branch=>resolveSegment(branch,segment,root)).filter(s=>s!==undefined);if(matches.length===1)return matches[0];if(matches.length>1)return{[key]:matches}}return undefined}export function resolveSchemaPath(schema,path){if(path.length===0)return resolveRef(schema,schema);let current=resolveRef(schema,schema);const root=schema;for(const segment of path){const next=resolveSegment(current,segment,root);if(next===undefined)return undefined;current=next}return current}export function resolveArrayItems(schema,root){const resolved=resolveRef(schema,root);const schemaType=resolved.type;const isArray=schemaType==="array"||Array.isArray(schemaType)&&schemaType.includes("array");if(!isArray&&resolved.items===undefined){return undefined}if(resolved.items===undefined){return{}}if(typeof resolved.items==="boolean"){return{}}if(Array.isArray(resolved.items)){const schemas=resolved.items.filter(item=>typeof item!=="boolean").map(item=>resolveRef(item,root));if(schemas.length===0)return{};return{oneOf:schemas}}return resolveRef(resolved.items,root)}export function simplifySchema(schema){for(const key of["oneOf","anyOf"]){const arr=schema[key];if(arr&&arr.length===1){const first=arr[0];if(first!==undefined&&typeof first!=="boolean")return simplifySchema(first)}}if(schema.allOf&&schema.allOf.length===1){const first=schema.allOf[0];if(first!==undefined&&typeof first!=="boolean")return simplifySchema(first)}for(const key of["oneOf","anyOf"]){const arr=schema[key];if(arr&&arr.length>1){const unique=[];for(const entry of arr){if(typeof entry==="boolean")continue;const isDuplicate=unique.some(existing=>deepEqual(existing,entry));if(!isDuplicate){unique.push(simplifySchema(entry))}}if(unique.length===1)return unique[0];return{...schema,[key]:unique}}}return schema}
|
|
2
2
|
//# sourceMappingURL=schema-resolver.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/schema-resolver.ts"],"sourcesContent":["import type { JSONSchema7 } from \"json-schema\";\nimport { UnsupportedSchemaError } from \"./errors.ts\";\nimport { deepEqual } from \"./utils.ts\";\n\n// ─── JSON Schema Resolver ────────────────────────────────────────────────────\n// Utility for navigating a JSON Schema Draft v7 by following a property path\n// (e.g. [\"user\", \"address\", \"city\"]).\n//\n// Handles:\n// - `$ref` resolution (internal references #/definitions/...)\n// - Navigation through `properties`\n// - Navigation through `items` (array elements)\n// - Combinators `allOf`, `anyOf`, `oneOf` (searches each branch)\n// - `additionalProperties` when the property is not explicitly declared\n//\n// Rejects:\n// - Conditional schemas (`if/then/else`) — non-resolvable without runtime data\n\n// ─── Conditional Schema Detection ────────────────────────────────────────────\n// JSON Schema Draft v7 introduced `if/then/else` conditional schemas.\n// These are fundamentally non-resolvable during static analysis because\n// they depend on runtime data values. Rather than silently ignoring them\n// (which would produce incorrect results — missing properties, wrong types),\n// we fail fast with a clear error pointing to the exact location in the schema.\n\n/**\n * Recursively validates that a JSON Schema does not contain `if/then/else`\n * conditional keywords. Throws an `UnsupportedSchemaError` if any are found.\n *\n * This check traverses the entire schema tree, including:\n * - `properties` values\n * - `additionalProperties` (when it's a schema)\n * - `items` (single schema or tuple)\n * - `allOf`, `anyOf`, `oneOf` branches\n * - `not`\n * - `definitions` / `$defs` values\n *\n * A `Set<object>` is used to track visited schemas and prevent infinite loops\n * from circular structures.\n *\n * @param schema - The JSON Schema to validate\n * @param path - The current JSON pointer path (for error reporting)\n * @param visited - Set of already-visited schema objects (cycle protection)\n *\n * @throws {UnsupportedSchemaError} if `if`, `then`, or `else` is found\n *\n * @example\n * ```\n * // Throws UnsupportedSchemaError:\n * assertNoConditionalSchema({\n * type: \"object\",\n * if: { properties: { kind: { const: \"a\" } } },\n * then: { properties: { a: { type: \"string\" } } },\n * });\n *\n * // OK — no conditional keywords:\n * assertNoConditionalSchema({\n * type: \"object\",\n * properties: { name: { type: \"string\" } },\n * });\n * ```\n */\nexport function assertNoConditionalSchema(\n\tschema: JSONSchema7,\n\tpath = \"\",\n\tvisited: Set<object> = new Set(),\n): void {\n\t// Cycle protection — avoid infinite loops on circular schema structures\n\tif (visited.has(schema)) return;\n\tvisited.add(schema);\n\n\t// ── Detect if/then/else at the current level ─────────────────────────\n\tif (schema.if !== undefined) {\n\t\tthrow new UnsupportedSchemaError(\"if/then/else\", path || \"/\");\n\t}\n\t// `then` or `else` without `if` is unusual but still unsupported\n\tif (schema.then !== undefined) {\n\t\tthrow new UnsupportedSchemaError(\"if/then/else\", path || \"/\");\n\t}\n\tif (schema.else !== undefined) {\n\t\tthrow new UnsupportedSchemaError(\"if/then/else\", path || \"/\");\n\t}\n\n\t// ── Recurse into properties ──────────────────────────────────────────\n\tif (schema.properties) {\n\t\tfor (const [key, prop] of Object.entries(schema.properties)) {\n\t\t\tif (prop && typeof prop !== \"boolean\") {\n\t\t\t\tassertNoConditionalSchema(prop, `${path}/properties/${key}`, visited);\n\t\t\t}\n\t\t}\n\t}\n\n\t// ── Recurse into additionalProperties ────────────────────────────────\n\tif (\n\t\tschema.additionalProperties &&\n\t\ttypeof schema.additionalProperties === \"object\"\n\t) {\n\t\tassertNoConditionalSchema(\n\t\t\tschema.additionalProperties,\n\t\t\t`${path}/additionalProperties`,\n\t\t\tvisited,\n\t\t);\n\t}\n\n\t// ── Recurse into items ───────────────────────────────────────────────\n\tif (schema.items) {\n\t\tif (Array.isArray(schema.items)) {\n\t\t\tfor (let i = 0; i < schema.items.length; i++) {\n\t\t\t\tconst item = schema.items[i];\n\t\t\t\tif (item && typeof item !== \"boolean\") {\n\t\t\t\t\tassertNoConditionalSchema(item, `${path}/items/${i}`, visited);\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (typeof schema.items !== \"boolean\") {\n\t\t\tassertNoConditionalSchema(schema.items, `${path}/items`, visited);\n\t\t}\n\t}\n\n\t// ── Recurse into combinators ─────────────────────────────────────────\n\tfor (const keyword of [\"allOf\", \"anyOf\", \"oneOf\"] as const) {\n\t\tconst branches = schema[keyword];\n\t\tif (branches) {\n\t\t\tfor (let i = 0; i < branches.length; i++) {\n\t\t\t\tconst branch = branches[i];\n\t\t\t\tif (branch && typeof branch !== \"boolean\") {\n\t\t\t\t\tassertNoConditionalSchema(branch, `${path}/${keyword}/${i}`, visited);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// ── Recurse into not ─────────────────────────────────────────────────\n\tif (schema.not && typeof schema.not !== \"boolean\") {\n\t\tassertNoConditionalSchema(schema.not, `${path}/not`, visited);\n\t}\n\n\t// ── Recurse into definitions / $defs ─────────────────────────────────\n\tfor (const defsKey of [\"definitions\", \"$defs\"] as const) {\n\t\tconst defs = schema[defsKey];\n\t\tif (defs) {\n\t\t\tfor (const [name, def] of Object.entries(defs)) {\n\t\t\t\tif (def && typeof def !== \"boolean\") {\n\t\t\t\t\tassertNoConditionalSchema(def, `${path}/${defsKey}/${name}`, visited);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n// ─── $ref Resolution ─────────────────────────────────────────────────────────\n// Only supports internal references in the format `#/definitions/Foo`\n// or `#/$defs/Foo` (JSON Schema Draft 2019+). Remote $refs (URLs) are\n// not supported — that is outside the scope of a template engine.\n\n/**\n * Recursively resolves `$ref` in a schema using the root schema as the\n * source of definitions.\n */\nexport function resolveRef(\n\tschema: JSONSchema7,\n\troot: JSONSchema7,\n): JSONSchema7 {\n\tif (!schema.$ref) return schema;\n\n\tconst ref = schema.$ref;\n\n\t// Expected format: #/definitions/Name or #/$defs/Name\n\tconst match = ref.match(/^#\\/(definitions|\\$defs)\\/(.+)$/);\n\tif (!match) {\n\t\tthrow new Error(\n\t\t\t`Unsupported $ref format: \"${ref}\". Only internal #/definitions/ references are supported.`,\n\t\t);\n\t}\n\n\tconst defsKey = match[1] as \"definitions\" | \"$defs\";\n\tconst name = match[2] ?? \"\";\n\n\tconst defs = defsKey === \"definitions\" ? root.definitions : root.$defs;\n\n\tif (!defs || !(name in defs)) {\n\t\tthrow new Error(\n\t\t\t`Cannot resolve $ref \"${ref}\": definition \"${name}\" not found.`,\n\t\t);\n\t}\n\n\t// Recursive resolution in case the definition itself contains a $ref\n\tconst def = defs[name];\n\tif (!def || typeof def === \"boolean\") {\n\t\tthrow new Error(\n\t\t\t`Cannot resolve $ref \"${ref}\": definition \"${name}\" not found.`,\n\t\t);\n\t}\n\treturn resolveRef(def, root);\n}\n\n// ─── Single-Segment Path Navigation ─────────────────────────────────────────\n\n/**\n * Resolves a single path segment (a property name) within a schema.\n * Returns the corresponding sub-schema, or `undefined` if the path is invalid.\n *\n * @param schema - The current schema (already resolved, no $ref)\n * @param segment - The property name to resolve\n * @param root - The root schema (for resolving any internal $refs)\n */\nfunction resolveSegment(\n\tschema: JSONSchema7,\n\tsegment: string,\n\troot: JSONSchema7,\n): JSONSchema7 | undefined {\n\tconst resolved = resolveRef(schema, root);\n\n\t// 1. Explicit properties\n\tif (resolved.properties && segment in resolved.properties) {\n\t\tconst prop = resolved.properties[segment];\n\t\tif (prop && typeof prop !== \"boolean\") return resolveRef(prop, root);\n\t\tif (prop === true) return {};\n\t}\n\n\t// 2. additionalProperties (when the property is not declared)\n\tif (\n\t\tresolved.additionalProperties !== undefined &&\n\t\tresolved.additionalProperties !== false\n\t) {\n\t\tif (resolved.additionalProperties === true) {\n\t\t\t// additionalProperties: true → type is unknown\n\t\t\treturn {};\n\t\t}\n\t\treturn resolveRef(resolved.additionalProperties, root);\n\t}\n\n\t// 3. Intrinsic array properties (e.g. `.length`)\n\tconst schemaType = resolved.type;\n\tconst isArray =\n\t\tschemaType === \"array\" ||\n\t\t(Array.isArray(schemaType) && schemaType.includes(\"array\"));\n\n\tif (isArray && segment === \"length\") {\n\t\treturn { type: \"integer\" };\n\t}\n\n\t// 3b. Numeric index access on arrays (e.g. `users.[0]` → items schema)\n\tif (isArray && /^\\d+$/.test(segment)) {\n\t\tif (resolved.items === undefined) {\n\t\t\t// array without items → element type is unknown\n\t\t\treturn {};\n\t\t}\n\t\tif (typeof resolved.items === \"boolean\") {\n\t\t\treturn {};\n\t\t}\n\t\t// Tuple: items is an array of schemas — resolve by index if possible\n\t\tif (Array.isArray(resolved.items)) {\n\t\t\tconst idx = Number.parseInt(segment, 10);\n\t\t\tconst item = resolved.items[idx];\n\t\t\tif (item !== undefined && typeof item !== \"boolean\") {\n\t\t\t\treturn resolveRef(item, root);\n\t\t\t}\n\t\t\tif (item !== undefined && typeof item === \"boolean\") {\n\t\t\t\treturn {};\n\t\t\t}\n\t\t\t// Index out of bounds for tuple → check additionalItems (Draft 7)\n\t\t\t// additionalItems: false → no additional elements allowed\n\t\t\tif (resolved.additionalItems === false) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\t// additionalItems: schema → additional elements have this type\n\t\t\tif (\n\t\t\t\tresolved.additionalItems !== undefined &&\n\t\t\t\tresolved.additionalItems !== true &&\n\t\t\t\ttypeof resolved.additionalItems === \"object\"\n\t\t\t) {\n\t\t\t\treturn resolveRef(resolved.additionalItems, root);\n\t\t\t}\n\t\t\t// additionalItems absent or true → type is unknown\n\t\t\treturn {};\n\t\t}\n\t\t// Single items schema — all elements share the same type\n\t\treturn resolveRef(resolved.items, root);\n\t}\n\n\t// 4. Combinators — search within each branch\n\tconst combinatorResult = resolveInCombinators(resolved, segment, root);\n\tif (combinatorResult) return combinatorResult;\n\n\treturn undefined;\n}\n\n/**\n * Searches for a segment within `allOf`, `anyOf`, `oneOf` branches.\n * Returns the first matching sub-schema, or `undefined`.\n * For `allOf`, found results are merged into a single `allOf`.\n */\nfunction resolveInCombinators(\n\tschema: JSONSchema7,\n\tsegment: string,\n\troot: JSONSchema7,\n): JSONSchema7 | undefined {\n\t// allOf: the property can be defined in any branch, and all constraints\n\t// apply simultaneously.\n\tif (schema.allOf) {\n\t\tconst matches = schema.allOf\n\t\t\t.filter((b): b is JSONSchema7 => typeof b !== \"boolean\")\n\t\t\t.map((branch) => resolveSegment(branch, segment, root))\n\t\t\t.filter((s): s is JSONSchema7 => s !== undefined);\n\n\t\tif (matches.length === 1) return matches[0] as JSONSchema7;\n\t\tif (matches.length > 1) return { allOf: matches };\n\t}\n\n\t// anyOf / oneOf: the property can come from any branch.\n\tfor (const key of [\"anyOf\", \"oneOf\"] as const) {\n\t\tif (!schema[key]) continue;\n\t\tconst matches = schema[key]\n\t\t\t.filter((b): b is JSONSchema7 => typeof b !== \"boolean\")\n\t\t\t.map((branch) => resolveSegment(branch, segment, root))\n\t\t\t.filter((s): s is JSONSchema7 => s !== undefined);\n\n\t\tif (matches.length === 1) return matches[0] as JSONSchema7;\n\t\tif (matches.length > 1) return { [key]: matches };\n\t}\n\n\treturn undefined;\n}\n\n// ─── Public API ──────────────────────────────────────────────────────────────\n\n/**\n * Resolves a full path (e.g. [\"user\", \"address\", \"city\"]) within a JSON\n * Schema and returns the corresponding sub-schema.\n *\n * @param schema - The root schema describing the template context\n * @param path - Array of segments (property names)\n * @returns The sub-schema at the end of the path, or `undefined` if the path\n * cannot be resolved.\n *\n * @example\n * ```\n * const schema = {\n * type: \"object\",\n * properties: {\n * user: {\n * type: \"object\",\n * properties: {\n * name: { type: \"string\" }\n * }\n * }\n * }\n * };\n * resolveSchemaPath(schema, [\"user\", \"name\"]);\n * // → { type: \"string\" }\n * ```\n */\nexport function resolveSchemaPath(\n\tschema: JSONSchema7,\n\tpath: string[],\n): JSONSchema7 | undefined {\n\tif (path.length === 0) return resolveRef(schema, schema);\n\n\tlet current: JSONSchema7 = resolveRef(schema, schema);\n\tconst root = schema;\n\n\tfor (const segment of path) {\n\t\tconst next = resolveSegment(current, segment, root);\n\t\tif (next === undefined) return undefined;\n\t\tcurrent = next;\n\t}\n\n\treturn current;\n}\n\n/**\n * Resolves the item schema of an array.\n * If the schema is not of type `array` or has no `items`, returns `undefined`.\n *\n * @param schema - The array schema\n * @param root - The root schema (for resolving $refs)\n */\nexport function resolveArrayItems(\n\tschema: JSONSchema7,\n\troot: JSONSchema7,\n): JSONSchema7 | undefined {\n\tconst resolved = resolveRef(schema, root);\n\n\t// Verify that it's actually an array\n\tconst schemaType = resolved.type;\n\tconst isArray =\n\t\tschemaType === \"array\" ||\n\t\t(Array.isArray(schemaType) && schemaType.includes(\"array\"));\n\n\tif (!isArray && resolved.items === undefined) {\n\t\treturn undefined;\n\t}\n\n\tif (resolved.items === undefined) {\n\t\t// array without items → element type is unknown\n\t\treturn {};\n\t}\n\n\t// items can be a boolean (true = anything, false = nothing)\n\tif (typeof resolved.items === \"boolean\") {\n\t\treturn {};\n\t}\n\n\t// items can be a single schema or a tuple (array of schemas).\n\t// For template loops, we handle the single-schema case.\n\tif (Array.isArray(resolved.items)) {\n\t\t// Tuple: create a oneOf of all possible types\n\t\tconst schemas = resolved.items\n\t\t\t.filter((item): item is JSONSchema7 => typeof item !== \"boolean\")\n\t\t\t.map((item) => resolveRef(item, root));\n\t\tif (schemas.length === 0) return {};\n\t\treturn { oneOf: schemas };\n\t}\n\n\treturn resolveRef(resolved.items, root);\n}\n\n/**\n * Simplifies an output schema to avoid unnecessarily complex constructs\n * (e.g. `oneOf` with a single element, duplicates, etc.).\n *\n * Uses `deepEqual` for deduplication — more robust and performant than\n * `JSON.stringify` (independent of key order, no intermediate string\n * allocations).\n */\nexport function simplifySchema(schema: JSONSchema7): JSONSchema7 {\n\t// oneOf / anyOf with a single element → unwrap\n\tfor (const key of [\"oneOf\", \"anyOf\"] as const) {\n\t\tconst arr = schema[key];\n\t\tif (arr && arr.length === 1) {\n\t\t\tconst first = arr[0];\n\t\t\tif (first !== undefined && typeof first !== \"boolean\")\n\t\t\t\treturn simplifySchema(first);\n\t\t}\n\t}\n\n\t// allOf with a single element → unwrap\n\tif (schema.allOf && schema.allOf.length === 1) {\n\t\tconst first = schema.allOf[0];\n\t\tif (first !== undefined && typeof first !== \"boolean\")\n\t\t\treturn simplifySchema(first);\n\t}\n\n\t// Deduplicate identical entries in oneOf/anyOf\n\tfor (const key of [\"oneOf\", \"anyOf\"] as const) {\n\t\tconst arr = schema[key];\n\t\tif (arr && arr.length > 1) {\n\t\t\tconst unique: JSONSchema7[] = [];\n\t\t\tfor (const entry of arr) {\n\t\t\t\tif (typeof entry === \"boolean\") continue;\n\t\t\t\t// Use deepEqual instead of JSON.stringify for structural\n\t\t\t\t// comparison — more robust (key order independent) and\n\t\t\t\t// more performant (no string allocations).\n\t\t\t\tconst isDuplicate = unique.some((existing) =>\n\t\t\t\t\tdeepEqual(existing, entry),\n\t\t\t\t);\n\t\t\t\tif (!isDuplicate) {\n\t\t\t\t\tunique.push(simplifySchema(entry));\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (unique.length === 1) return unique[0] as JSONSchema7;\n\t\t\treturn { ...schema, [key]: unique };\n\t\t}\n\t}\n\n\treturn schema;\n}\n"],"names":["UnsupportedSchemaError","deepEqual","assertNoConditionalSchema","schema","path","visited","Set","has","add","if","undefined","then","else","properties","key","prop","Object","entries","additionalProperties","items","Array","isArray","i","length","item","keyword","branches","branch","not","defsKey","defs","name","def","resolveRef","root","$ref","ref","match","Error","definitions","$defs","resolveSegment","segment","resolved","schemaType","type","includes","test","idx","Number","parseInt","additionalItems","combinatorResult","resolveInCombinators","allOf","matches","filter","b","map","s","resolveSchemaPath","current","next","resolveArrayItems","schemas","oneOf","simplifySchema","arr","first","unique","entry","isDuplicate","some","existing","push"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/schema-resolver.ts"],"sourcesContent":["import type { JSONSchema7 } from \"json-schema\";\nimport { UnsupportedSchemaError } from \"./errors.ts\";\nimport { deepEqual } from \"./utils.ts\";\n\n// ─── JSON Schema Resolver ────────────────────────────────────────────────────\n// Utility for navigating a JSON Schema Draft v7 by following a property path\n// (e.g. [\"user\", \"address\", \"city\"]).\n//\n// Handles:\n// - `$ref` resolution (internal references #/definitions/...)\n// - Navigation through `properties`\n// - Navigation through `items` (array elements)\n// - Combinators `allOf`, `anyOf`, `oneOf` (searches each branch)\n// - `additionalProperties` when the property is not explicitly declared\n//\n// Rejects:\n// - Conditional schemas (`if/then/else`) — non-resolvable without runtime data\n\n// ─── Conditional Schema Detection ────────────────────────────────────────────\n// JSON Schema Draft v7 introduced `if/then/else` conditional schemas.\n// These are fundamentally non-resolvable during static analysis because\n// they depend on runtime data values. Rather than silently ignoring them\n// (which would produce incorrect results — missing properties, wrong types),\n// we fail fast with a clear error pointing to the exact location in the schema.\n\n/**\n * Recursively scans a JSON Schema tree for `if/then/else` conditional keywords\n * and returns their locations as an array of `{ keyword, schemaPath }` objects.\n *\n * This is the non-throwing counterpart of `assertNoConditionalSchema`.\n * It traverses the entire schema tree, including:\n * - `properties` values\n * - `additionalProperties` (when it's a schema)\n * - `items` (single schema or tuple)\n * - `allOf`, `anyOf`, `oneOf` branches\n * - `not`\n * - `definitions` / `$defs` values\n *\n * A `Set<object>` is used to track visited schemas and prevent infinite loops\n * from circular structures.\n *\n * @param schema - The JSON Schema to scan\n * @param path - The current JSON pointer path (for location reporting)\n * @param visited - Set of already-visited schema objects (cycle protection)\n * @returns Array of locations where `if/then/else` keywords were found\n *\n * @example\n * ```\n * // Returns [{ keyword: \"if/then/else\", schemaPath: \"/\" }]:\n * findConditionalSchemaLocations({\n * type: \"object\",\n * if: { properties: { kind: { const: \"a\" } } },\n * then: { properties: { a: { type: \"string\" } } },\n * });\n *\n * // Returns []:\n * findConditionalSchemaLocations({\n * type: \"object\",\n * properties: { name: { type: \"string\" } },\n * });\n * ```\n */\nexport function findConditionalSchemaLocations(\n\tschema: JSONSchema7,\n\tpath = \"\",\n\tvisited: Set<object> = new Set(),\n): Array<{ keyword: string; schemaPath: string }> {\n\tconst locations: Array<{ keyword: string; schemaPath: string }> = [];\n\n\t// Cycle protection — avoid infinite loops on circular schema structures\n\tif (visited.has(schema)) return locations;\n\tvisited.add(schema);\n\n\t// ── Detect if/then/else at the current level ─────────────────────────\n\t// One diagnostic per schema node is enough — no need to report `then`\n\t// and `else` separately if `if` is already present.\n\tfor (const kw of [\"if\", \"then\", \"else\"] as const) {\n\t\tif (schema[kw] !== undefined) {\n\t\t\tlocations.push({ keyword: \"if/then/else\", schemaPath: path || \"/\" });\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t// ── Recurse into properties ──────────────────────────────────────────\n\tif (schema.properties) {\n\t\tfor (const [key, prop] of Object.entries(schema.properties)) {\n\t\t\tif (prop && typeof prop !== \"boolean\") {\n\t\t\t\tlocations.push(\n\t\t\t\t\t...findConditionalSchemaLocations(\n\t\t\t\t\t\tprop,\n\t\t\t\t\t\t`${path}/properties/${key}`,\n\t\t\t\t\t\tvisited,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t// ── Recurse into additionalProperties ────────────────────────────────\n\tif (\n\t\tschema.additionalProperties &&\n\t\ttypeof schema.additionalProperties === \"object\"\n\t) {\n\t\tlocations.push(\n\t\t\t...findConditionalSchemaLocations(\n\t\t\t\tschema.additionalProperties,\n\t\t\t\t`${path}/additionalProperties`,\n\t\t\t\tvisited,\n\t\t\t),\n\t\t);\n\t}\n\n\t// ── Recurse into items ───────────────────────────────────────────────\n\tif (schema.items) {\n\t\tif (Array.isArray(schema.items)) {\n\t\t\tfor (let i = 0; i < schema.items.length; i++) {\n\t\t\t\tconst item = schema.items[i];\n\t\t\t\tif (item && typeof item !== \"boolean\") {\n\t\t\t\t\tlocations.push(\n\t\t\t\t\t\t...findConditionalSchemaLocations(\n\t\t\t\t\t\t\titem,\n\t\t\t\t\t\t\t`${path}/items/${i}`,\n\t\t\t\t\t\t\tvisited,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (typeof schema.items !== \"boolean\") {\n\t\t\tlocations.push(\n\t\t\t\t...findConditionalSchemaLocations(\n\t\t\t\t\tschema.items,\n\t\t\t\t\t`${path}/items`,\n\t\t\t\t\tvisited,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t}\n\n\t// ── Recurse into combinators ─────────────────────────────────────────\n\tfor (const keyword of [\"allOf\", \"anyOf\", \"oneOf\"] as const) {\n\t\tconst branches = schema[keyword];\n\t\tif (branches) {\n\t\t\tfor (let i = 0; i < branches.length; i++) {\n\t\t\t\tconst branch = branches[i];\n\t\t\t\tif (branch && typeof branch !== \"boolean\") {\n\t\t\t\t\tlocations.push(\n\t\t\t\t\t\t...findConditionalSchemaLocations(\n\t\t\t\t\t\t\tbranch,\n\t\t\t\t\t\t\t`${path}/${keyword}/${i}`,\n\t\t\t\t\t\t\tvisited,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// ── Recurse into not ─────────────────────────────────────────────────\n\tif (schema.not && typeof schema.not !== \"boolean\") {\n\t\tlocations.push(\n\t\t\t...findConditionalSchemaLocations(schema.not, `${path}/not`, visited),\n\t\t);\n\t}\n\n\t// ── Recurse into definitions / $defs ─────────────────────────────────\n\tfor (const defsKey of [\"definitions\", \"$defs\"] as const) {\n\t\tconst defs = schema[defsKey];\n\t\tif (defs) {\n\t\t\tfor (const [name, def] of Object.entries(defs)) {\n\t\t\t\tif (def && typeof def !== \"boolean\") {\n\t\t\t\t\tlocations.push(\n\t\t\t\t\t\t...findConditionalSchemaLocations(\n\t\t\t\t\t\t\tdef,\n\t\t\t\t\t\t\t`${path}/${defsKey}/${name}`,\n\t\t\t\t\t\t\tvisited,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn locations;\n}\n\n/**\n * Recursively validates that a JSON Schema does not contain `if/then/else`\n * conditional keywords. Throws an `UnsupportedSchemaError` if any are found.\n *\n * @deprecated Use `findConditionalSchemaLocations` for diagnostic-based reporting.\n * This function is preserved for backward compatibility.\n *\n * @param schema - The JSON Schema to validate\n * @param path - The current JSON pointer path (for error reporting)\n * @param visited - Set of already-visited schema objects (cycle protection)\n *\n * @throws {UnsupportedSchemaError} if `if`, `then`, or `else` is found\n *\n * @example\n * ```\n * // Throws UnsupportedSchemaError:\n * assertNoConditionalSchema({\n * type: \"object\",\n * if: { properties: { kind: { const: \"a\" } } },\n * then: { properties: { a: { type: \"string\" } } },\n * });\n *\n * // OK — no conditional keywords:\n * assertNoConditionalSchema({\n * type: \"object\",\n * properties: { name: { type: \"string\" } },\n * });\n * ```\n */\nexport function assertNoConditionalSchema(\n\tschema: JSONSchema7,\n\tpath = \"\",\n\tvisited: Set<object> = new Set(),\n): void {\n\tconst locations = findConditionalSchemaLocations(schema, path, visited);\n\tif (locations.length > 0) {\n\t\t// biome-ignore lint: length check guarantees defined\n\t\tconst first = locations[0]!;\n\t\tthrow new UnsupportedSchemaError(first.keyword, first.schemaPath);\n\t}\n}\n\n// ─── $ref Resolution ─────────────────────────────────────────────────────────\n// Only supports internal references in the format `#/definitions/Foo`\n// or `#/$defs/Foo` (JSON Schema Draft 2019+). Remote $refs (URLs) are\n// not supported — that is outside the scope of a template engine.\n\n/**\n * Recursively resolves `$ref` in a schema using the root schema as the\n * source of definitions.\n */\nexport function resolveRef(\n\tschema: JSONSchema7,\n\troot: JSONSchema7,\n): JSONSchema7 {\n\tif (!schema.$ref) return schema;\n\n\tconst ref = schema.$ref;\n\n\t// Expected format: #/definitions/Name or #/$defs/Name\n\tconst match = ref.match(/^#\\/(definitions|\\$defs)\\/(.+)$/);\n\tif (!match) {\n\t\tthrow new Error(\n\t\t\t`Unsupported $ref format: \"${ref}\". Only internal #/definitions/ references are supported.`,\n\t\t);\n\t}\n\n\tconst defsKey = match[1] as \"definitions\" | \"$defs\";\n\tconst name = match[2] ?? \"\";\n\n\tconst defs = defsKey === \"definitions\" ? root.definitions : root.$defs;\n\n\tif (!defs || !(name in defs)) {\n\t\tthrow new Error(\n\t\t\t`Cannot resolve $ref \"${ref}\": definition \"${name}\" not found.`,\n\t\t);\n\t}\n\n\t// Recursive resolution in case the definition itself contains a $ref\n\tconst def = defs[name];\n\tif (!def || typeof def === \"boolean\") {\n\t\tthrow new Error(\n\t\t\t`Cannot resolve $ref \"${ref}\": definition \"${name}\" not found.`,\n\t\t);\n\t}\n\treturn resolveRef(def, root);\n}\n\n// ─── Single-Segment Path Navigation ─────────────────────────────────────────\n\n/**\n * Resolves a single path segment (a property name) within a schema.\n * Returns the corresponding sub-schema, or `undefined` if the path is invalid.\n *\n * @param schema - The current schema (already resolved, no $ref)\n * @param segment - The property name to resolve\n * @param root - The root schema (for resolving any internal $refs)\n */\nfunction resolveSegment(\n\tschema: JSONSchema7,\n\tsegment: string,\n\troot: JSONSchema7,\n): JSONSchema7 | undefined {\n\tconst resolved = resolveRef(schema, root);\n\n\t// 1. Explicit properties\n\tif (resolved.properties && segment in resolved.properties) {\n\t\tconst prop = resolved.properties[segment];\n\t\tif (prop && typeof prop !== \"boolean\") return resolveRef(prop, root);\n\t\tif (prop === true) return {};\n\t}\n\n\t// 2. additionalProperties (when the property is not declared)\n\tif (\n\t\tresolved.additionalProperties !== undefined &&\n\t\tresolved.additionalProperties !== false\n\t) {\n\t\tif (resolved.additionalProperties === true) {\n\t\t\t// additionalProperties: true → type is unknown\n\t\t\treturn {};\n\t\t}\n\t\treturn resolveRef(resolved.additionalProperties, root);\n\t}\n\n\t// 3. Intrinsic array properties (e.g. `.length`)\n\tconst schemaType = resolved.type;\n\tconst isArray =\n\t\tschemaType === \"array\" ||\n\t\t(Array.isArray(schemaType) && schemaType.includes(\"array\"));\n\n\tif (isArray && segment === \"length\") {\n\t\treturn { type: \"integer\" };\n\t}\n\n\t// 3b. Numeric index access on arrays (e.g. `users.[0]` → items schema)\n\tif (isArray && /^\\d+$/.test(segment)) {\n\t\tif (resolved.items === undefined) {\n\t\t\t// array without items → element type is unknown\n\t\t\treturn {};\n\t\t}\n\t\tif (typeof resolved.items === \"boolean\") {\n\t\t\treturn {};\n\t\t}\n\t\t// Tuple: items is an array of schemas — resolve by index if possible\n\t\tif (Array.isArray(resolved.items)) {\n\t\t\tconst idx = Number.parseInt(segment, 10);\n\t\t\tconst item = resolved.items[idx];\n\t\t\tif (item !== undefined && typeof item !== \"boolean\") {\n\t\t\t\treturn resolveRef(item, root);\n\t\t\t}\n\t\t\tif (item !== undefined && typeof item === \"boolean\") {\n\t\t\t\treturn {};\n\t\t\t}\n\t\t\t// Index out of bounds for tuple → check additionalItems (Draft 7)\n\t\t\t// additionalItems: false → no additional elements allowed\n\t\t\tif (resolved.additionalItems === false) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\t// additionalItems: schema → additional elements have this type\n\t\t\tif (\n\t\t\t\tresolved.additionalItems !== undefined &&\n\t\t\t\tresolved.additionalItems !== true &&\n\t\t\t\ttypeof resolved.additionalItems === \"object\"\n\t\t\t) {\n\t\t\t\treturn resolveRef(resolved.additionalItems, root);\n\t\t\t}\n\t\t\t// additionalItems absent or true → type is unknown\n\t\t\treturn {};\n\t\t}\n\t\t// Single items schema — all elements share the same type\n\t\treturn resolveRef(resolved.items, root);\n\t}\n\n\t// 4. Combinators — search within each branch\n\tconst combinatorResult = resolveInCombinators(resolved, segment, root);\n\tif (combinatorResult) return combinatorResult;\n\n\treturn undefined;\n}\n\n/**\n * Searches for a segment within `allOf`, `anyOf`, `oneOf` branches.\n * Returns the first matching sub-schema, or `undefined`.\n * For `allOf`, found results are merged into a single `allOf`.\n */\nfunction resolveInCombinators(\n\tschema: JSONSchema7,\n\tsegment: string,\n\troot: JSONSchema7,\n): JSONSchema7 | undefined {\n\t// allOf: the property can be defined in any branch, and all constraints\n\t// apply simultaneously.\n\tif (schema.allOf) {\n\t\tconst matches = schema.allOf\n\t\t\t.filter((b): b is JSONSchema7 => typeof b !== \"boolean\")\n\t\t\t.map((branch) => resolveSegment(branch, segment, root))\n\t\t\t.filter((s): s is JSONSchema7 => s !== undefined);\n\n\t\tif (matches.length === 1) return matches[0] as JSONSchema7;\n\t\tif (matches.length > 1) return { allOf: matches };\n\t}\n\n\t// anyOf / oneOf: the property can come from any branch.\n\tfor (const key of [\"anyOf\", \"oneOf\"] as const) {\n\t\tif (!schema[key]) continue;\n\t\tconst matches = schema[key]\n\t\t\t.filter((b): b is JSONSchema7 => typeof b !== \"boolean\")\n\t\t\t.map((branch) => resolveSegment(branch, segment, root))\n\t\t\t.filter((s): s is JSONSchema7 => s !== undefined);\n\n\t\tif (matches.length === 1) return matches[0] as JSONSchema7;\n\t\tif (matches.length > 1) return { [key]: matches };\n\t}\n\n\treturn undefined;\n}\n\n// ─── Public API ──────────────────────────────────────────────────────────────\n\n/**\n * Resolves a full path (e.g. [\"user\", \"address\", \"city\"]) within a JSON\n * Schema and returns the corresponding sub-schema.\n *\n * @param schema - The root schema describing the template context\n * @param path - Array of segments (property names)\n * @returns The sub-schema at the end of the path, or `undefined` if the path\n * cannot be resolved.\n *\n * @example\n * ```\n * const schema = {\n * type: \"object\",\n * properties: {\n * user: {\n * type: \"object\",\n * properties: {\n * name: { type: \"string\" }\n * }\n * }\n * }\n * };\n * resolveSchemaPath(schema, [\"user\", \"name\"]);\n * // → { type: \"string\" }\n * ```\n */\nexport function resolveSchemaPath(\n\tschema: JSONSchema7,\n\tpath: string[],\n): JSONSchema7 | undefined {\n\tif (path.length === 0) return resolveRef(schema, schema);\n\n\tlet current: JSONSchema7 = resolveRef(schema, schema);\n\tconst root = schema;\n\n\tfor (const segment of path) {\n\t\tconst next = resolveSegment(current, segment, root);\n\t\tif (next === undefined) return undefined;\n\t\tcurrent = next;\n\t}\n\n\treturn current;\n}\n\n/**\n * Resolves the item schema of an array.\n * If the schema is not of type `array` or has no `items`, returns `undefined`.\n *\n * @param schema - The array schema\n * @param root - The root schema (for resolving $refs)\n */\nexport function resolveArrayItems(\n\tschema: JSONSchema7,\n\troot: JSONSchema7,\n): JSONSchema7 | undefined {\n\tconst resolved = resolveRef(schema, root);\n\n\t// Verify that it's actually an array\n\tconst schemaType = resolved.type;\n\tconst isArray =\n\t\tschemaType === \"array\" ||\n\t\t(Array.isArray(schemaType) && schemaType.includes(\"array\"));\n\n\tif (!isArray && resolved.items === undefined) {\n\t\treturn undefined;\n\t}\n\n\tif (resolved.items === undefined) {\n\t\t// array without items → element type is unknown\n\t\treturn {};\n\t}\n\n\t// items can be a boolean (true = anything, false = nothing)\n\tif (typeof resolved.items === \"boolean\") {\n\t\treturn {};\n\t}\n\n\t// items can be a single schema or a tuple (array of schemas).\n\t// For template loops, we handle the single-schema case.\n\tif (Array.isArray(resolved.items)) {\n\t\t// Tuple: create a oneOf of all possible types\n\t\tconst schemas = resolved.items\n\t\t\t.filter((item): item is JSONSchema7 => typeof item !== \"boolean\")\n\t\t\t.map((item) => resolveRef(item, root));\n\t\tif (schemas.length === 0) return {};\n\t\treturn { oneOf: schemas };\n\t}\n\n\treturn resolveRef(resolved.items, root);\n}\n\n/**\n * Simplifies an output schema to avoid unnecessarily complex constructs\n * (e.g. `oneOf` with a single element, duplicates, etc.).\n *\n * Uses `deepEqual` for deduplication — more robust and performant than\n * `JSON.stringify` (independent of key order, no intermediate string\n * allocations).\n */\nexport function simplifySchema(schema: JSONSchema7): JSONSchema7 {\n\t// oneOf / anyOf with a single element → unwrap\n\tfor (const key of [\"oneOf\", \"anyOf\"] as const) {\n\t\tconst arr = schema[key];\n\t\tif (arr && arr.length === 1) {\n\t\t\tconst first = arr[0];\n\t\t\tif (first !== undefined && typeof first !== \"boolean\")\n\t\t\t\treturn simplifySchema(first);\n\t\t}\n\t}\n\n\t// allOf with a single element → unwrap\n\tif (schema.allOf && schema.allOf.length === 1) {\n\t\tconst first = schema.allOf[0];\n\t\tif (first !== undefined && typeof first !== \"boolean\")\n\t\t\treturn simplifySchema(first);\n\t}\n\n\t// Deduplicate identical entries in oneOf/anyOf\n\tfor (const key of [\"oneOf\", \"anyOf\"] as const) {\n\t\tconst arr = schema[key];\n\t\tif (arr && arr.length > 1) {\n\t\t\tconst unique: JSONSchema7[] = [];\n\t\t\tfor (const entry of arr) {\n\t\t\t\tif (typeof entry === \"boolean\") continue;\n\t\t\t\t// Use deepEqual instead of JSON.stringify for structural\n\t\t\t\t// comparison — more robust (key order independent) and\n\t\t\t\t// more performant (no string allocations).\n\t\t\t\tconst isDuplicate = unique.some((existing) =>\n\t\t\t\t\tdeepEqual(existing, entry),\n\t\t\t\t);\n\t\t\t\tif (!isDuplicate) {\n\t\t\t\t\tunique.push(simplifySchema(entry));\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (unique.length === 1) return unique[0] as JSONSchema7;\n\t\t\treturn { ...schema, [key]: unique };\n\t\t}\n\t}\n\n\treturn schema;\n}\n"],"names":["UnsupportedSchemaError","deepEqual","findConditionalSchemaLocations","schema","path","visited","Set","locations","has","add","kw","undefined","push","keyword","schemaPath","properties","key","prop","Object","entries","additionalProperties","items","Array","isArray","i","length","item","branches","branch","not","defsKey","defs","name","def","assertNoConditionalSchema","first","resolveRef","root","$ref","ref","match","Error","definitions","$defs","resolveSegment","segment","resolved","schemaType","type","includes","test","idx","Number","parseInt","additionalItems","combinatorResult","resolveInCombinators","allOf","matches","filter","b","map","s","resolveSchemaPath","current","next","resolveArrayItems","schemas","oneOf","simplifySchema","arr","unique","entry","isDuplicate","some","existing"],"mappings":"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"}
|
package/dist/esm/types.d.ts
CHANGED
|
@@ -102,7 +102,9 @@ export type DiagnosticCode =
|
|
|
102
102
|
/** Syntax error in the template */
|
|
103
103
|
| "PARSE_ERROR"
|
|
104
104
|
/** The $root token is used with path traversal (e.g. $root.name) */
|
|
105
|
-
| "ROOT_PATH_TRAVERSAL"
|
|
105
|
+
| "ROOT_PATH_TRAVERSAL"
|
|
106
|
+
/** Unsupported JSON Schema feature (e.g. if/then/else conditional schemas) */
|
|
107
|
+
| "UNSUPPORTED_SCHEMA";
|
|
106
108
|
export interface DiagnosticDetails {
|
|
107
109
|
/** Path of the expression that caused the error (e.g. `"user.name.foo"`) */
|
|
108
110
|
path?: string;
|
package/dist/esm/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/types.ts"],"sourcesContent":["import type { JSONSchema7 } from \"json-schema\";\nimport type { FromSchema, JSONSchema } from \"json-schema-to-ts\";\n\n// ─── Template Input ──────────────────────────────────────────────────────────\n// The engine accepts primitive values in addition to template strings.\n// When a non-string value is passed, it is treated as a literal passthrough:\n// analysis returns the inferred type, and execution returns the value as-is.\n\n/**\n * Object where each property is a `TemplateInput` (recursive).\n *\n * Allows passing an entire structure as a template:\n * ```\n * engine.analyze({\n * userName: \"{{name}}\",\n * userAge: \"{{age}}\",\n * nested: { x: \"{{foo}}\" },\n * }, inputSchema);\n * ```\n */\nexport interface TemplateInputObject {\n\t[key: string]: TemplateInput;\n}\n\n/**\n * Array where each element is a `TemplateInput` (recursive).\n *\n * Allows passing an array as a template:\n * ```\n * engine.analyze([\"{{name}}\", \"{{age}}\"], inputSchema);\n * engine.execute([\"{{name}}\", 42], data);\n * ```\n */\nexport type TemplateInputArray = TemplateInput[];\n\n/**\n * Input type accepted by the template engine.\n *\n * - `string` → standard Handlebars template (parsed and executed)\n * - `number` → numeric literal (passthrough)\n * - `boolean` → boolean literal (passthrough)\n * - `null` → null literal (passthrough)\n * - `TemplateInputArray` → array where each element is a `TemplateInput`\n * - `TemplateInputObject` → object where each property is a `TemplateInput`\n */\nexport type TemplateInput =\n\t| string\n\t| number\n\t| boolean\n\t| null\n\t| TemplateInputArray\n\t| TemplateInputObject;\n\n// ─── Template Data ───────────────────────────────────────────────────────────\n// The data parameter accepted by `execute()` and `analyzeAndExecute()`.\n// In most cases this is a `Record<string, unknown>` (object context), but\n// primitives are also allowed — for example when using `{{$root}}` to\n// reference the entire data value directly.\n\n/**\n * Data type accepted by the template engine's execution methods.\n *\n * - `Record<string, unknown>` → standard object context (most common)\n * - `string` → primitive value (e.g. for `{{$root}}`)\n * - `number` → primitive value\n * - `boolean` → primitive value\n * - `null` → null value\n * - `unknown[]` → array value\n */\nexport type TemplateData =\n\t| Record<string, unknown>\n\t| string\n\t| number\n\t| boolean\n\t| null\n\t| unknown[];\n\n/**\n * Checks whether a value is a non-string primitive literal (number, boolean, null).\n * These values are treated as passthrough by the engine.\n *\n * Note: objects (`TemplateInputObject`) and arrays (`TemplateInputArray`) are NOT literals.\n */\nexport function isLiteralInput(\n\tinput: TemplateInput,\n): input is number | boolean | null {\n\treturn (\n\t\tinput === null || (typeof input !== \"string\" && typeof input !== \"object\")\n\t);\n}\n\n/**\n * Checks whether a value is a template array (`TemplateInputArray`).\n * Template arrays are processed recursively by the engine:\n * each element is analyzed/executed individually and the result is an array.\n */\nexport function isArrayInput(\n\tinput: TemplateInput,\n): input is TemplateInputArray {\n\treturn Array.isArray(input);\n}\n\n/**\n * Checks whether a value is a template object (`TemplateInputObject`).\n * Template objects are processed recursively by the engine:\n * each property is analyzed/executed individually.\n *\n * Note: arrays are excluded — use `isArrayInput()` first.\n */\nexport function isObjectInput(\n\tinput: TemplateInput,\n): input is TemplateInputObject {\n\treturn input !== null && typeof input === \"object\" && !Array.isArray(input);\n}\n\n/**\n * Infers the JSON Schema of a non-string primitive value.\n *\n * @param value - The primitive value (number, boolean, null)\n * @returns The corresponding JSON Schema\n *\n * @example\n * ```\n * inferPrimitiveSchema(42) // → { type: \"number\" }\n * inferPrimitiveSchema(true) // → { type: \"boolean\" }\n * inferPrimitiveSchema(null) // → { type: \"null\" }\n * ```\n */\nexport function inferPrimitiveSchema(\n\tvalue: number | boolean | null,\n): JSONSchema7 {\n\tif (value === null) return { type: \"null\" };\n\tif (typeof value === \"boolean\") return { type: \"boolean\" };\n\tif (typeof value === \"number\") {\n\t\treturn Number.isInteger(value) ? { type: \"integer\" } : { type: \"number\" };\n\t}\n\t// Exhaustiveness check — all branches are covered above.\n\t// If the type of `value` changes, TypeScript will raise an error here.\n\tvalue satisfies never;\n\treturn { type: \"null\" };\n}\n\n// ─── Diagnostic Codes ────────────────────────────────────────────────────────\n// Machine-readable codes for each error/warning type, enabling the frontend\n// to react programmatically without parsing the human-readable message.\n\nexport type DiagnosticCode =\n\t/** The referenced property does not exist in the context schema */\n\t| \"UNKNOWN_PROPERTY\"\n\t/** Type mismatch (e.g. #each on a non-array) */\n\t| \"TYPE_MISMATCH\"\n\t/** A block helper is used without a required argument */\n\t| \"MISSING_ARGUMENT\"\n\t/** Unknown block helper (neither built-in nor registered) */\n\t| \"UNKNOWN_HELPER\"\n\t/** The expression cannot be statically analyzed */\n\t| \"UNANALYZABLE\"\n\t/** The {{key:N}} syntax is used but no identifierSchemas were provided */\n\t| \"MISSING_IDENTIFIER_SCHEMAS\"\n\t/** The identifier N does not exist in the provided identifierSchemas */\n\t| \"UNKNOWN_IDENTIFIER\"\n\t/** The property does not exist in the identifier's schema */\n\t| \"IDENTIFIER_PROPERTY_NOT_FOUND\"\n\t/** Syntax error in the template */\n\t| \"PARSE_ERROR\"\n\t/** The $root token is used with path traversal (e.g. $root.name) */\n\t| \"ROOT_PATH_TRAVERSAL\";\n\n// ─── Diagnostic Details ──────────────────────────────────────────────────────\n// Supplementary information to understand the exact cause of the error.\n// Designed to be easily JSON-serializable and consumable by a frontend.\n\nexport interface DiagnosticDetails {\n\t/** Path of the expression that caused the error (e.g. `\"user.name.foo\"`) */\n\tpath?: string;\n\t/** Name of the helper involved (for helper-related errors) */\n\thelperName?: string;\n\t/** What was expected (e.g. `\"array\"`, `\"property to exist\"`) */\n\texpected?: string;\n\t/** What was found (e.g. `\"string\"`, `\"undefined\"`) */\n\tactual?: string;\n\t/** Available properties in the current schema (for suggestions) */\n\tavailableProperties?: string[];\n\t/** Template identifier number (for `{{key:N}}` errors) */\n\tidentifier?: number;\n}\n\n// ─── Static Analysis Result ──────────────────────────────────────────────────\n\n/** Diagnostic produced by the static analyzer */\nexport interface TemplateDiagnostic {\n\t/** \"error\" blocks execution, \"warning\" is informational */\n\tseverity: \"error\" | \"warning\";\n\n\t/** Machine-readable code identifying the error type */\n\tcode: DiagnosticCode;\n\n\t/** Human-readable message describing the problem */\n\tmessage: string;\n\n\t/** Position in the template source (if available from the AST) */\n\tloc?: {\n\t\tstart: { line: number; column: number };\n\t\tend: { line: number; column: number };\n\t};\n\n\t/** Fragment of the template source around the error */\n\tsource?: string;\n\n\t/** Structured information for debugging and frontend display */\n\tdetails?: DiagnosticDetails;\n}\n\n/** Complete result of the static analysis */\nexport interface AnalysisResult {\n\t/** true if no errors (warnings are tolerated) */\n\tvalid: boolean;\n\t/** List of diagnostics (errors + warnings) */\n\tdiagnostics: TemplateDiagnostic[];\n\t/** JSON Schema describing the template's return type */\n\toutputSchema: JSONSchema7;\n}\n\n/** Lightweight validation result (without output type inference) */\nexport interface ValidationResult {\n\t/** true if no errors (warnings are tolerated) */\n\tvalid: boolean;\n\t/** List of diagnostics (errors + warnings) */\n\tdiagnostics: TemplateDiagnostic[];\n}\n\n// ─── Public Engine Options ───────────────────────────────────────────────────\n\nexport interface TemplateEngineOptions {\n\t/**\n\t * Capacity of the parsed AST cache. Each parsed template is cached\n\t * to avoid costly re-parsing on repeated calls.\n\t * @default 256\n\t */\n\tastCacheSize?: number;\n\n\t/**\n\t * Capacity of the compiled Handlebars template cache.\n\t * @default 256\n\t */\n\tcompilationCacheSize?: number;\n\n\t/**\n\t * Custom helpers to register during engine construction.\n\t *\n\t * Each entry describes a helper with its name, implementation,\n\t * expected parameters, and return type.\n\t *\n\t * @example\n\t * ```\n\t * const engine = new Typebars({\n\t * helpers: [\n\t * {\n\t * name: \"uppercase\",\n\t * description: \"Converts a string to uppercase\",\n\t * fn: (value: string) => String(value).toUpperCase(),\n\t * params: [\n\t * { name: \"value\", type: { type: \"string\" }, description: \"The string to convert\" },\n\t * ],\n\t * returnType: { type: \"string\" },\n\t * },\n\t * ],\n\t * });\n\t * ```\n\t */\n\thelpers?: HelperConfig[];\n}\n\nexport interface CommonTypebarsOptions {\n\t/**\n\t * Explicit coercion schema for the output value.\n\t * When provided with a primitive type, the execution result will be\n\t * coerced to match the declared type instead of using auto-detection.\n\t */\n\tcoerceSchema?: JSONSchema7;\n\t/**\n\t * When `true`, properties whose values contain Handlebars expressions\n\t * (i.e. any `{{…}}` syntax) are excluded from the execution result.\n\t *\n\t * - **Object**: properties with template expressions are omitted from\n\t * the resulting object.\n\t * - **Array**: elements with template expressions are omitted from\n\t * the resulting array.\n\t * - **Root string** with expressions: returns `null` (there is no\n\t * parent to exclude from).\n\t * - **Literals** (number, boolean, null): unaffected.\n\t *\n\t * This mirrors the analysis-side `excludeTemplateExpression` option\n\t * but applied at runtime.\n\t *\n\t * @default false\n\t */\n\texcludeTemplateExpression?: boolean;\n}\n\n// ─── Execution Options ───────────────────────────────────────────────────────\n// Optional options object for `execute()`, replacing multiple positional\n// parameters for better ergonomics.\n\nexport interface ExecuteOptions extends CommonTypebarsOptions {\n\t/** JSON Schema for pre-execution static validation */\n\tschema?: JSONSchema7;\n\t/** Data by identifier `{ [id]: { key: value } }` */\n\tidentifierData?: Record<number, Record<string, unknown>>;\n\t/** Schemas by identifier (for static validation with identifiers) */\n\tidentifierSchemas?: Record<number, JSONSchema7>;\n}\n\n// ─── Combined Analyze-and-Execute Options ────────────────────────────────────\n// Optional options object for `analyzeAndExecute()`, grouping parameters\n// related to template identifiers.\n\nexport interface AnalyzeAndExecuteOptions extends CommonTypebarsOptions {\n\t/** Schemas by identifier `{ [id]: JSONSchema7 }` for static analysis */\n\tidentifierSchemas?: Record<number, JSONSchema7>;\n\t/** Data by identifier `{ [id]: { key: value } }` for execution */\n\tidentifierData?: Record<number, Record<string, unknown>>;\n}\n\n// ─── Custom Helpers ──────────────────────────────────────────────────────────\n// Allows registering custom helpers with their type signature for static\n// analysis support.\n\n/** Describes a parameter expected by a helper */\nexport interface HelperParam {\n\t/** Parameter name (for documentation / introspection) */\n\tname: string;\n\n\t/**\n\t * JSON Schema describing the expected type for this parameter.\n\t * Used for documentation and static validation.\n\t */\n\ttype?: JSONSchema7;\n\n\t/** Human-readable description of the parameter */\n\tdescription?: string;\n\n\t/**\n\t * Whether the parameter is optional.\n\t * @default false\n\t */\n\toptional?: boolean;\n}\n\n/**\n * Definition of a helper registerable via `registerHelper()`.\n *\n * Contains the runtime implementation and typing metadata\n * for static analysis.\n */\nexport interface HelperDefinition {\n\t/**\n\t * Runtime implementation of the helper — will be registered with Handlebars.\n\t *\n\t * For an inline helper `{{uppercase name}}`:\n\t * `(value: string) => string`\n\t *\n\t * For a block helper `{{#repeat count}}...{{/repeat}}`:\n\t * `function(this: any, count: number, options: Handlebars.HelperOptions) { ... }`\n\t */\n\t// biome-ignore lint/suspicious/noExplicitAny: Handlebars helper signatures are inherently dynamic\n\tfn: (...args: any[]) => unknown;\n\n\t/**\n\t * Parameters expected by the helper (for documentation and analysis).\n\t *\n\t * @example\n\t * ```\n\t * params: [\n\t * { name: \"value\", type: { type: \"number\" }, description: \"The value to round\" },\n\t * { name: \"precision\", type: { type: \"number\" }, description: \"Decimal places\", optional: true },\n\t * ]\n\t * ```\n\t */\n\tparams?: HelperParam[];\n\n\t/**\n\t * JSON Schema describing the helper's return type for static analysis.\n\t * @default { type: \"string\" }\n\t */\n\treturnType?: JSONSchema7;\n\n\t/** Human-readable description of the helper */\n\tdescription?: string;\n}\n\n/**\n * Full helper configuration for registration via the `Typebars({ helpers: [...] })`\n * constructor options.\n *\n * Extends `HelperDefinition` with a required `name`.\n *\n * @example\n * ```\n * const config: HelperConfig = {\n * name: \"round\",\n * description: \"Rounds a number to a given precision\",\n * fn: (value: number, precision?: number) => { ... },\n * params: [\n * { name: \"value\", type: { type: \"number\" } },\n * { name: \"precision\", type: { type: \"number\" }, optional: true },\n * ],\n * returnType: { type: \"number\" },\n * };\n * ```\n */\nexport interface HelperConfig extends HelperDefinition {\n\t/** Name of the helper as used in templates (e.g. `\"uppercase\"`) */\n\tname: string;\n}\n\n// ─── Automatic Type Inference via json-schema-to-ts ──────────────────────────\n// Allows `defineHelper()` to infer TypeScript types for `fn` arguments\n// from the JSON Schemas declared in `params`.\n\n/**\n * Param definition used for type inference.\n * Accepts `JSONSchema` from `json-schema-to-ts` to allow `FromSchema`\n * to resolve literal types.\n */\ntype TypedHelperParam = {\n\treadonly name: string;\n\treadonly type?: JSONSchema;\n\treadonly description?: string;\n\treadonly optional?: boolean;\n};\n\n/**\n * Infers the TypeScript type of a single parameter from its JSON Schema.\n * - If `optional: true`, the resolved type is unioned with `undefined`.\n * - If `type` is not provided, the type is `unknown`.\n */\ntype InferParamType<P> = P extends {\n\treadonly type: infer S extends JSONSchema;\n\treadonly optional: true;\n}\n\t? FromSchema<S> | undefined\n\t: P extends { readonly type: infer S extends JSONSchema }\n\t\t? FromSchema<S>\n\t\t: unknown;\n\n/**\n * Maps a tuple of `TypedHelperParam` to a tuple of inferred TypeScript types,\n * usable as the `fn` signature.\n *\n * @example\n * ```\n * type Args = InferArgs<readonly [\n * { name: \"a\"; type: { type: \"string\" } },\n * { name: \"b\"; type: { type: \"number\" }; optional: true },\n * ]>;\n * // => [string, number | undefined]\n * ```\n */\ntype InferArgs<P extends readonly TypedHelperParam[]> = {\n\t[K in keyof P]: InferParamType<P[K]>;\n};\n\n/**\n * Helper configuration with generic parameter inference.\n * Used exclusively by `defineHelper()`.\n */\ninterface TypedHelperConfig<P extends readonly TypedHelperParam[]> {\n\tname: string;\n\tdescription?: string;\n\tparams: P;\n\tfn: (...args: InferArgs<P>) => unknown;\n\treturnType?: JSONSchema;\n}\n\n/**\n * Creates a `HelperConfig` with automatic type inference for `fn` arguments\n * based on the JSON Schemas declared in `params`.\n *\n * The generic parameter `const P` preserves schema literal types\n * (equivalent of `as const`), enabling `FromSchema` to resolve the\n * corresponding TypeScript types.\n *\n * @example\n * ```\n * const helper = defineHelper({\n * name: \"concat\",\n * description: \"Concatenates two strings\",\n * params: [\n * { name: \"a\", type: { type: \"string\" }, description: \"First string\" },\n * { name: \"b\", type: { type: \"string\" }, description: \"Second string\" },\n * { name: \"sep\", type: { type: \"string\" }, description: \"Separator\", optional: true },\n * ],\n * fn: (a, b, sep) => {\n * // a: string, b: string, sep: string | undefined\n * const separator = sep ?? \"\";\n * return `${a}${separator}${b}`;\n * },\n * returnType: { type: \"string\" },\n * });\n * ```\n */\nexport function defineHelper<const P extends readonly TypedHelperParam[]>(\n\tconfig: TypedHelperConfig<P>,\n): HelperConfig {\n\treturn config as unknown as HelperConfig;\n}\n"],"names":["isLiteralInput","input","isArrayInput","Array","isArray","isObjectInput","inferPrimitiveSchema","value","type","Number","isInteger","defineHelper","config"],"mappings":"AAmFA,OAAO,SAASA,eACfC,KAAoB,EAEpB,OACCA,QAAU,MAAS,OAAOA,QAAU,UAAY,OAAOA,QAAU,QAEnE,CAOA,OAAO,SAASC,aACfD,KAAoB,EAEpB,OAAOE,MAAMC,OAAO,CAACH,MACtB,CASA,OAAO,SAASI,cACfJ,KAAoB,EAEpB,OAAOA,QAAU,MAAQ,OAAOA,QAAU,UAAY,CAACE,MAAMC,OAAO,CAACH,MACtE,CAeA,OAAO,SAASK,qBACfC,KAA8B,EAE9B,GAAIA,QAAU,KAAM,MAAO,CAAEC,KAAM,MAAO,EAC1C,GAAI,OAAOD,QAAU,UAAW,MAAO,CAAEC,KAAM,SAAU,EACzD,GAAI,OAAOD,QAAU,SAAU,CAC9B,OAAOE,OAAOC,SAAS,CAACH,OAAS,CAAEC,KAAM,SAAU,EAAI,CAAEA,KAAM,QAAS,CACzE,CAGAD,MACA,MAAO,CAAEC,KAAM,MAAO,CACvB,CA0WA,OAAO,SAASG,aACfC,MAA4B,EAE5B,OAAOA,MACR"}
|
|
1
|
+
{"version":3,"sources":["../../src/types.ts"],"sourcesContent":["import type { JSONSchema7 } from \"json-schema\";\nimport type { FromSchema, JSONSchema } from \"json-schema-to-ts\";\n\n// ─── Template Input ──────────────────────────────────────────────────────────\n// The engine accepts primitive values in addition to template strings.\n// When a non-string value is passed, it is treated as a literal passthrough:\n// analysis returns the inferred type, and execution returns the value as-is.\n\n/**\n * Object where each property is a `TemplateInput` (recursive).\n *\n * Allows passing an entire structure as a template:\n * ```\n * engine.analyze({\n * userName: \"{{name}}\",\n * userAge: \"{{age}}\",\n * nested: { x: \"{{foo}}\" },\n * }, inputSchema);\n * ```\n */\nexport interface TemplateInputObject {\n\t[key: string]: TemplateInput;\n}\n\n/**\n * Array where each element is a `TemplateInput` (recursive).\n *\n * Allows passing an array as a template:\n * ```\n * engine.analyze([\"{{name}}\", \"{{age}}\"], inputSchema);\n * engine.execute([\"{{name}}\", 42], data);\n * ```\n */\nexport type TemplateInputArray = TemplateInput[];\n\n/**\n * Input type accepted by the template engine.\n *\n * - `string` → standard Handlebars template (parsed and executed)\n * - `number` → numeric literal (passthrough)\n * - `boolean` → boolean literal (passthrough)\n * - `null` → null literal (passthrough)\n * - `TemplateInputArray` → array where each element is a `TemplateInput`\n * - `TemplateInputObject` → object where each property is a `TemplateInput`\n */\nexport type TemplateInput =\n\t| string\n\t| number\n\t| boolean\n\t| null\n\t| TemplateInputArray\n\t| TemplateInputObject;\n\n// ─── Template Data ───────────────────────────────────────────────────────────\n// The data parameter accepted by `execute()` and `analyzeAndExecute()`.\n// In most cases this is a `Record<string, unknown>` (object context), but\n// primitives are also allowed — for example when using `{{$root}}` to\n// reference the entire data value directly.\n\n/**\n * Data type accepted by the template engine's execution methods.\n *\n * - `Record<string, unknown>` → standard object context (most common)\n * - `string` → primitive value (e.g. for `{{$root}}`)\n * - `number` → primitive value\n * - `boolean` → primitive value\n * - `null` → null value\n * - `unknown[]` → array value\n */\nexport type TemplateData =\n\t| Record<string, unknown>\n\t| string\n\t| number\n\t| boolean\n\t| null\n\t| unknown[];\n\n/**\n * Checks whether a value is a non-string primitive literal (number, boolean, null).\n * These values are treated as passthrough by the engine.\n *\n * Note: objects (`TemplateInputObject`) and arrays (`TemplateInputArray`) are NOT literals.\n */\nexport function isLiteralInput(\n\tinput: TemplateInput,\n): input is number | boolean | null {\n\treturn (\n\t\tinput === null || (typeof input !== \"string\" && typeof input !== \"object\")\n\t);\n}\n\n/**\n * Checks whether a value is a template array (`TemplateInputArray`).\n * Template arrays are processed recursively by the engine:\n * each element is analyzed/executed individually and the result is an array.\n */\nexport function isArrayInput(\n\tinput: TemplateInput,\n): input is TemplateInputArray {\n\treturn Array.isArray(input);\n}\n\n/**\n * Checks whether a value is a template object (`TemplateInputObject`).\n * Template objects are processed recursively by the engine:\n * each property is analyzed/executed individually.\n *\n * Note: arrays are excluded — use `isArrayInput()` first.\n */\nexport function isObjectInput(\n\tinput: TemplateInput,\n): input is TemplateInputObject {\n\treturn input !== null && typeof input === \"object\" && !Array.isArray(input);\n}\n\n/**\n * Infers the JSON Schema of a non-string primitive value.\n *\n * @param value - The primitive value (number, boolean, null)\n * @returns The corresponding JSON Schema\n *\n * @example\n * ```\n * inferPrimitiveSchema(42) // → { type: \"number\" }\n * inferPrimitiveSchema(true) // → { type: \"boolean\" }\n * inferPrimitiveSchema(null) // → { type: \"null\" }\n * ```\n */\nexport function inferPrimitiveSchema(\n\tvalue: number | boolean | null,\n): JSONSchema7 {\n\tif (value === null) return { type: \"null\" };\n\tif (typeof value === \"boolean\") return { type: \"boolean\" };\n\tif (typeof value === \"number\") {\n\t\treturn Number.isInteger(value) ? { type: \"integer\" } : { type: \"number\" };\n\t}\n\t// Exhaustiveness check — all branches are covered above.\n\t// If the type of `value` changes, TypeScript will raise an error here.\n\tvalue satisfies never;\n\treturn { type: \"null\" };\n}\n\n// ─── Diagnostic Codes ────────────────────────────────────────────────────────\n// Machine-readable codes for each error/warning type, enabling the frontend\n// to react programmatically without parsing the human-readable message.\n\nexport type DiagnosticCode =\n\t/** The referenced property does not exist in the context schema */\n\t| \"UNKNOWN_PROPERTY\"\n\t/** Type mismatch (e.g. #each on a non-array) */\n\t| \"TYPE_MISMATCH\"\n\t/** A block helper is used without a required argument */\n\t| \"MISSING_ARGUMENT\"\n\t/** Unknown block helper (neither built-in nor registered) */\n\t| \"UNKNOWN_HELPER\"\n\t/** The expression cannot be statically analyzed */\n\t| \"UNANALYZABLE\"\n\t/** The {{key:N}} syntax is used but no identifierSchemas were provided */\n\t| \"MISSING_IDENTIFIER_SCHEMAS\"\n\t/** The identifier N does not exist in the provided identifierSchemas */\n\t| \"UNKNOWN_IDENTIFIER\"\n\t/** The property does not exist in the identifier's schema */\n\t| \"IDENTIFIER_PROPERTY_NOT_FOUND\"\n\t/** Syntax error in the template */\n\t| \"PARSE_ERROR\"\n\t/** The $root token is used with path traversal (e.g. $root.name) */\n\t| \"ROOT_PATH_TRAVERSAL\"\n\t/** Unsupported JSON Schema feature (e.g. if/then/else conditional schemas) */\n\t| \"UNSUPPORTED_SCHEMA\";\n\n// ─── Diagnostic Details ──────────────────────────────────────────────────────\n// Supplementary information to understand the exact cause of the error.\n// Designed to be easily JSON-serializable and consumable by a frontend.\n\nexport interface DiagnosticDetails {\n\t/** Path of the expression that caused the error (e.g. `\"user.name.foo\"`) */\n\tpath?: string;\n\t/** Name of the helper involved (for helper-related errors) */\n\thelperName?: string;\n\t/** What was expected (e.g. `\"array\"`, `\"property to exist\"`) */\n\texpected?: string;\n\t/** What was found (e.g. `\"string\"`, `\"undefined\"`) */\n\tactual?: string;\n\t/** Available properties in the current schema (for suggestions) */\n\tavailableProperties?: string[];\n\t/** Template identifier number (for `{{key:N}}` errors) */\n\tidentifier?: number;\n}\n\n// ─── Static Analysis Result ──────────────────────────────────────────────────\n\n/** Diagnostic produced by the static analyzer */\nexport interface TemplateDiagnostic {\n\t/** \"error\" blocks execution, \"warning\" is informational */\n\tseverity: \"error\" | \"warning\";\n\n\t/** Machine-readable code identifying the error type */\n\tcode: DiagnosticCode;\n\n\t/** Human-readable message describing the problem */\n\tmessage: string;\n\n\t/** Position in the template source (if available from the AST) */\n\tloc?: {\n\t\tstart: { line: number; column: number };\n\t\tend: { line: number; column: number };\n\t};\n\n\t/** Fragment of the template source around the error */\n\tsource?: string;\n\n\t/** Structured information for debugging and frontend display */\n\tdetails?: DiagnosticDetails;\n}\n\n/** Complete result of the static analysis */\nexport interface AnalysisResult {\n\t/** true if no errors (warnings are tolerated) */\n\tvalid: boolean;\n\t/** List of diagnostics (errors + warnings) */\n\tdiagnostics: TemplateDiagnostic[];\n\t/** JSON Schema describing the template's return type */\n\toutputSchema: JSONSchema7;\n}\n\n/** Lightweight validation result (without output type inference) */\nexport interface ValidationResult {\n\t/** true if no errors (warnings are tolerated) */\n\tvalid: boolean;\n\t/** List of diagnostics (errors + warnings) */\n\tdiagnostics: TemplateDiagnostic[];\n}\n\n// ─── Public Engine Options ───────────────────────────────────────────────────\n\nexport interface TemplateEngineOptions {\n\t/**\n\t * Capacity of the parsed AST cache. Each parsed template is cached\n\t * to avoid costly re-parsing on repeated calls.\n\t * @default 256\n\t */\n\tastCacheSize?: number;\n\n\t/**\n\t * Capacity of the compiled Handlebars template cache.\n\t * @default 256\n\t */\n\tcompilationCacheSize?: number;\n\n\t/**\n\t * Custom helpers to register during engine construction.\n\t *\n\t * Each entry describes a helper with its name, implementation,\n\t * expected parameters, and return type.\n\t *\n\t * @example\n\t * ```\n\t * const engine = new Typebars({\n\t * helpers: [\n\t * {\n\t * name: \"uppercase\",\n\t * description: \"Converts a string to uppercase\",\n\t * fn: (value: string) => String(value).toUpperCase(),\n\t * params: [\n\t * { name: \"value\", type: { type: \"string\" }, description: \"The string to convert\" },\n\t * ],\n\t * returnType: { type: \"string\" },\n\t * },\n\t * ],\n\t * });\n\t * ```\n\t */\n\thelpers?: HelperConfig[];\n}\n\nexport interface CommonTypebarsOptions {\n\t/**\n\t * Explicit coercion schema for the output value.\n\t * When provided with a primitive type, the execution result will be\n\t * coerced to match the declared type instead of using auto-detection.\n\t */\n\tcoerceSchema?: JSONSchema7;\n\t/**\n\t * When `true`, properties whose values contain Handlebars expressions\n\t * (i.e. any `{{…}}` syntax) are excluded from the execution result.\n\t *\n\t * - **Object**: properties with template expressions are omitted from\n\t * the resulting object.\n\t * - **Array**: elements with template expressions are omitted from\n\t * the resulting array.\n\t * - **Root string** with expressions: returns `null` (there is no\n\t * parent to exclude from).\n\t * - **Literals** (number, boolean, null): unaffected.\n\t *\n\t * This mirrors the analysis-side `excludeTemplateExpression` option\n\t * but applied at runtime.\n\t *\n\t * @default false\n\t */\n\texcludeTemplateExpression?: boolean;\n}\n\n// ─── Execution Options ───────────────────────────────────────────────────────\n// Optional options object for `execute()`, replacing multiple positional\n// parameters for better ergonomics.\n\nexport interface ExecuteOptions extends CommonTypebarsOptions {\n\t/** JSON Schema for pre-execution static validation */\n\tschema?: JSONSchema7;\n\t/** Data by identifier `{ [id]: { key: value } }` */\n\tidentifierData?: Record<number, Record<string, unknown>>;\n\t/** Schemas by identifier (for static validation with identifiers) */\n\tidentifierSchemas?: Record<number, JSONSchema7>;\n}\n\n// ─── Combined Analyze-and-Execute Options ────────────────────────────────────\n// Optional options object for `analyzeAndExecute()`, grouping parameters\n// related to template identifiers.\n\nexport interface AnalyzeAndExecuteOptions extends CommonTypebarsOptions {\n\t/** Schemas by identifier `{ [id]: JSONSchema7 }` for static analysis */\n\tidentifierSchemas?: Record<number, JSONSchema7>;\n\t/** Data by identifier `{ [id]: { key: value } }` for execution */\n\tidentifierData?: Record<number, Record<string, unknown>>;\n}\n\n// ─── Custom Helpers ──────────────────────────────────────────────────────────\n// Allows registering custom helpers with their type signature for static\n// analysis support.\n\n/** Describes a parameter expected by a helper */\nexport interface HelperParam {\n\t/** Parameter name (for documentation / introspection) */\n\tname: string;\n\n\t/**\n\t * JSON Schema describing the expected type for this parameter.\n\t * Used for documentation and static validation.\n\t */\n\ttype?: JSONSchema7;\n\n\t/** Human-readable description of the parameter */\n\tdescription?: string;\n\n\t/**\n\t * Whether the parameter is optional.\n\t * @default false\n\t */\n\toptional?: boolean;\n}\n\n/**\n * Definition of a helper registerable via `registerHelper()`.\n *\n * Contains the runtime implementation and typing metadata\n * for static analysis.\n */\nexport interface HelperDefinition {\n\t/**\n\t * Runtime implementation of the helper — will be registered with Handlebars.\n\t *\n\t * For an inline helper `{{uppercase name}}`:\n\t * `(value: string) => string`\n\t *\n\t * For a block helper `{{#repeat count}}...{{/repeat}}`:\n\t * `function(this: any, count: number, options: Handlebars.HelperOptions) { ... }`\n\t */\n\t// biome-ignore lint/suspicious/noExplicitAny: Handlebars helper signatures are inherently dynamic\n\tfn: (...args: any[]) => unknown;\n\n\t/**\n\t * Parameters expected by the helper (for documentation and analysis).\n\t *\n\t * @example\n\t * ```\n\t * params: [\n\t * { name: \"value\", type: { type: \"number\" }, description: \"The value to round\" },\n\t * { name: \"precision\", type: { type: \"number\" }, description: \"Decimal places\", optional: true },\n\t * ]\n\t * ```\n\t */\n\tparams?: HelperParam[];\n\n\t/**\n\t * JSON Schema describing the helper's return type for static analysis.\n\t * @default { type: \"string\" }\n\t */\n\treturnType?: JSONSchema7;\n\n\t/** Human-readable description of the helper */\n\tdescription?: string;\n}\n\n/**\n * Full helper configuration for registration via the `Typebars({ helpers: [...] })`\n * constructor options.\n *\n * Extends `HelperDefinition` with a required `name`.\n *\n * @example\n * ```\n * const config: HelperConfig = {\n * name: \"round\",\n * description: \"Rounds a number to a given precision\",\n * fn: (value: number, precision?: number) => { ... },\n * params: [\n * { name: \"value\", type: { type: \"number\" } },\n * { name: \"precision\", type: { type: \"number\" }, optional: true },\n * ],\n * returnType: { type: \"number\" },\n * };\n * ```\n */\nexport interface HelperConfig extends HelperDefinition {\n\t/** Name of the helper as used in templates (e.g. `\"uppercase\"`) */\n\tname: string;\n}\n\n// ─── Automatic Type Inference via json-schema-to-ts ──────────────────────────\n// Allows `defineHelper()` to infer TypeScript types for `fn` arguments\n// from the JSON Schemas declared in `params`.\n\n/**\n * Param definition used for type inference.\n * Accepts `JSONSchema` from `json-schema-to-ts` to allow `FromSchema`\n * to resolve literal types.\n */\ntype TypedHelperParam = {\n\treadonly name: string;\n\treadonly type?: JSONSchema;\n\treadonly description?: string;\n\treadonly optional?: boolean;\n};\n\n/**\n * Infers the TypeScript type of a single parameter from its JSON Schema.\n * - If `optional: true`, the resolved type is unioned with `undefined`.\n * - If `type` is not provided, the type is `unknown`.\n */\ntype InferParamType<P> = P extends {\n\treadonly type: infer S extends JSONSchema;\n\treadonly optional: true;\n}\n\t? FromSchema<S> | undefined\n\t: P extends { readonly type: infer S extends JSONSchema }\n\t\t? FromSchema<S>\n\t\t: unknown;\n\n/**\n * Maps a tuple of `TypedHelperParam` to a tuple of inferred TypeScript types,\n * usable as the `fn` signature.\n *\n * @example\n * ```\n * type Args = InferArgs<readonly [\n * { name: \"a\"; type: { type: \"string\" } },\n * { name: \"b\"; type: { type: \"number\" }; optional: true },\n * ]>;\n * // => [string, number | undefined]\n * ```\n */\ntype InferArgs<P extends readonly TypedHelperParam[]> = {\n\t[K in keyof P]: InferParamType<P[K]>;\n};\n\n/**\n * Helper configuration with generic parameter inference.\n * Used exclusively by `defineHelper()`.\n */\ninterface TypedHelperConfig<P extends readonly TypedHelperParam[]> {\n\tname: string;\n\tdescription?: string;\n\tparams: P;\n\tfn: (...args: InferArgs<P>) => unknown;\n\treturnType?: JSONSchema;\n}\n\n/**\n * Creates a `HelperConfig` with automatic type inference for `fn` arguments\n * based on the JSON Schemas declared in `params`.\n *\n * The generic parameter `const P` preserves schema literal types\n * (equivalent of `as const`), enabling `FromSchema` to resolve the\n * corresponding TypeScript types.\n *\n * @example\n * ```\n * const helper = defineHelper({\n * name: \"concat\",\n * description: \"Concatenates two strings\",\n * params: [\n * { name: \"a\", type: { type: \"string\" }, description: \"First string\" },\n * { name: \"b\", type: { type: \"string\" }, description: \"Second string\" },\n * { name: \"sep\", type: { type: \"string\" }, description: \"Separator\", optional: true },\n * ],\n * fn: (a, b, sep) => {\n * // a: string, b: string, sep: string | undefined\n * const separator = sep ?? \"\";\n * return `${a}${separator}${b}`;\n * },\n * returnType: { type: \"string\" },\n * });\n * ```\n */\nexport function defineHelper<const P extends readonly TypedHelperParam[]>(\n\tconfig: TypedHelperConfig<P>,\n): HelperConfig {\n\treturn config as unknown as HelperConfig;\n}\n"],"names":["isLiteralInput","input","isArrayInput","Array","isArray","isObjectInput","inferPrimitiveSchema","value","type","Number","isInteger","defineHelper","config"],"mappings":"AAmFA,OAAO,SAASA,eACfC,KAAoB,EAEpB,OACCA,QAAU,MAAS,OAAOA,QAAU,UAAY,OAAOA,QAAU,QAEnE,CAOA,OAAO,SAASC,aACfD,KAAoB,EAEpB,OAAOE,MAAMC,OAAO,CAACH,MACtB,CASA,OAAO,SAASI,cACfJ,KAAoB,EAEpB,OAAOA,QAAU,MAAQ,OAAOA,QAAU,UAAY,CAACE,MAAMC,OAAO,CAACH,MACtE,CAeA,OAAO,SAASK,qBACfC,KAA8B,EAE9B,GAAIA,QAAU,KAAM,MAAO,CAAEC,KAAM,MAAO,EAC1C,GAAI,OAAOD,QAAU,UAAW,MAAO,CAAEC,KAAM,SAAU,EACzD,GAAI,OAAOD,QAAU,SAAU,CAC9B,OAAOE,OAAOC,SAAS,CAACH,OAAS,CAAEC,KAAM,SAAU,EAAI,CAAEA,KAAM,QAAS,CACzE,CAGAD,MACA,MAAO,CAAEC,KAAM,MAAO,CACvB,CA4WA,OAAO,SAASG,aACfC,MAA4B,EAE5B,OAAOA,MACR"}
|
package/dist/esm/utils.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
function _define_property(obj,key,value){if(key in obj){Object.defineProperty(obj,key,{value:value,enumerable:true,configurable:true,writable:true})}else{obj[key]=value}return obj}export function deepEqual(a,b){if(a===b)return true;if(a===null||b===null)return false;if(typeof a!==typeof b)return false;if(Array.isArray(a)){if(!Array.isArray(b))return false;if(a.length!==b.length)return false;for(let i=0;i<a.length;i++){if(!deepEqual(a[i],b[i]))return false}return true}if(typeof a==="object"){const objA=a;const objB=b;const keysA=Object.keys(objA);const keysB=Object.keys(objB);if(keysA.length!==keysB.length)return false;for(const key of keysA){if(!(key in objB)||!deepEqual(objA[key],objB[key]))return false}return true}return false}export class LRUCache{get(key){if(!this.cache.has(key))return undefined;const value=this.cache.get(key);this.cache.delete(key);this.cache.set(key,value);return value}set(key,value){if(this.cache.has(key)){this.cache.delete(key)}else if(this.cache.size>=this.capacity){const oldestKey=this.cache.keys().next().value;if(oldestKey!==undefined){this.cache.delete(oldestKey)}}this.cache.set(key,value)}has(key){return this.cache.has(key)}delete(key){return this.cache.delete(key)}clear(){this.cache.clear()}get size(){return this.cache.size}constructor(capacity){_define_property(this,"capacity",void 0);_define_property(this,"cache",void 0);this.capacity=capacity;this.cache=new Map;if(capacity<1){throw new Error("LRUCache capacity must be at least 1")}}}export function extractSourceSnippet(template,loc){const lines=template.split("\n");const startLine=loc.start.line-1;const endLine=loc.end.line-1;if(startLine<0||startLine>=lines.length)return"";if(startLine===endLine){const line=lines[startLine]??"";return line.trim()}const clampedEnd=Math.min(endLine,lines.length-1);return lines.slice(startLine,clampedEnd+1).map(l=>l.trimEnd()).join("\n").trim()}export function getSchemaPropertyNames(schema){const names=new Set;if(schema.properties){for(const key of Object.keys(schema.properties)){names.add(key)}}for(const combinator of["allOf","anyOf","oneOf"]){const branches=schema[combinator];if(branches){for(const branch of branches){if(typeof branch==="boolean")continue;if(branch.properties){for(const key of Object.keys(branch.properties)){names.add(key)}}}}}return Array.from(names).sort()}export function aggregateObjectAnalysis(keys,analyzeEntry){const allDiagnostics=[];const properties={};let allValid=true;for(const key of keys){const child=analyzeEntry(key);if(!child.valid)allValid=false;allDiagnostics.push(...child.diagnostics);properties[key]=child.outputSchema}return{valid:allValid,diagnostics:allDiagnostics,outputSchema:{type:"object",properties,required:keys}}}export function aggregateObjectAnalysisAndExecution(keys,processEntry){const allDiagnostics=[];const properties={};const resultValues={};let allValid=true;for(const key of keys){const child=processEntry(key);if(!child.analysis.valid)allValid=false;allDiagnostics.push(...child.analysis.diagnostics);properties[key]=child.analysis.outputSchema;resultValues[key]=child.value}const analysis={valid:allValid,diagnostics:allDiagnostics,outputSchema:{type:"object",properties,required:keys}};return{analysis,value:allValid?resultValues:undefined}}function computeArrayItemsSchema(elementSchemas){if(elementSchemas.length===0)return{};const unique=[];for(const schema of elementSchemas){if(!unique.some(u=>deepEqual(u,schema))){unique.push(schema)}}if(unique.length===1){return unique[0]}return{oneOf:unique}}export function aggregateArrayAnalysis(length,analyzeEntry){const allDiagnostics=[];const elementSchemas=[];let allValid=true;for(let i=0;i<length;i++){const child=analyzeEntry(i);if(!child.valid)allValid=false;allDiagnostics.push(...child.diagnostics);elementSchemas.push(child.outputSchema)}return{valid:allValid,diagnostics:allDiagnostics,outputSchema:{type:"array",items:computeArrayItemsSchema(elementSchemas)}}}export function aggregateArrayAnalysisAndExecution(length,processEntry){const allDiagnostics=[];const elementSchemas=[];const resultValues=[];let allValid=true;for(let i=0;i<length;i++){const child=processEntry(i);if(!child.analysis.valid)allValid=false;allDiagnostics.push(...child.analysis.diagnostics);elementSchemas.push(child.analysis.outputSchema);resultValues.push(child.value)}const analysis={valid:allValid,diagnostics:allDiagnostics,outputSchema:{type:"array",items:computeArrayItemsSchema(elementSchemas)}};return{analysis,value:allValid?resultValues:undefined}}
|
|
1
|
+
function _define_property(obj,key,value){if(key in obj){Object.defineProperty(obj,key,{value:value,enumerable:true,configurable:true,writable:true})}else{obj[key]=value}return obj}export function deepEqual(a,b){if(a===b)return true;if(a===null||b===null)return false;if(typeof a!==typeof b)return false;if(Array.isArray(a)){if(!Array.isArray(b))return false;if(a.length!==b.length)return false;for(let i=0;i<a.length;i++){if(!deepEqual(a[i],b[i]))return false}return true}if(typeof a==="object"){const objA=a;const objB=b;const keysA=Object.keys(objA);const keysB=Object.keys(objB);if(keysA.length!==keysB.length)return false;for(const key of keysA){if(!(key in objB)||!deepEqual(objA[key],objB[key]))return false}return true}return false}export class LRUCache{get(key){if(!this.cache.has(key))return undefined;const value=this.cache.get(key);this.cache.delete(key);this.cache.set(key,value);return value}set(key,value){if(this.cache.has(key)){this.cache.delete(key)}else if(this.cache.size>=this.capacity){const oldestKey=this.cache.keys().next().value;if(oldestKey!==undefined){this.cache.delete(oldestKey)}}this.cache.set(key,value)}has(key){return this.cache.has(key)}delete(key){return this.cache.delete(key)}clear(){this.cache.clear()}get size(){return this.cache.size}constructor(capacity){_define_property(this,"capacity",void 0);_define_property(this,"cache",void 0);this.capacity=capacity;this.cache=new Map;if(capacity<1){throw new Error("LRUCache capacity must be at least 1")}}}export function extractSourceSnippet(template,loc){const lines=template.split("\n");const startLine=loc.start.line-1;const endLine=loc.end.line-1;if(startLine<0||startLine>=lines.length)return"";if(startLine===endLine){const line=lines[startLine]??"";return line.trim()}const clampedEnd=Math.min(endLine,lines.length-1);return lines.slice(startLine,clampedEnd+1).map(l=>l.trimEnd()).join("\n").trim()}export function getSchemaPropertyNames(schema){const names=new Set;if(schema.properties){for(const key of Object.keys(schema.properties)){names.add(key)}}for(const combinator of["allOf","anyOf","oneOf"]){const branches=schema[combinator];if(branches){for(const branch of branches){if(typeof branch==="boolean")continue;if(branch.properties){for(const key of Object.keys(branch.properties)){names.add(key)}}}}}return Array.from(names).sort()}export function aggregateObjectAnalysis(keys,analyzeEntry){const allDiagnostics=[];const properties={};let allValid=true;for(const key of keys){const child=analyzeEntry(key);if(!child.valid)allValid=false;allDiagnostics.push(...child.diagnostics);properties[key]=child.outputSchema}return{valid:allValid,diagnostics:allDiagnostics,outputSchema:{type:"object",properties,required:keys}}}export function aggregateObjectAnalysisAndExecution(keys,processEntry){const allDiagnostics=[];const properties={};const resultValues={};let allValid=true;for(const key of keys){const child=processEntry(key);if(!child.analysis.valid)allValid=false;allDiagnostics.push(...child.analysis.diagnostics);properties[key]=child.analysis.outputSchema;resultValues[key]=child.value}const analysis={valid:allValid,diagnostics:allDiagnostics,outputSchema:{type:"object",properties,required:keys}};return{analysis,value:allValid?resultValues:undefined}}function computeArrayItemsSchema(elementSchemas){if(elementSchemas.length===0)return{};const unique=[];for(const schema of elementSchemas){if(!unique.some(u=>deepEqual(u,schema))){unique.push(schema)}}if(unique.length===1){return unique[0]}return{oneOf:unique}}export function aggregateArrayAnalysis(length,analyzeEntry){const allDiagnostics=[];const elementSchemas=[];let allValid=true;for(let i=0;i<length;i++){const child=analyzeEntry(i);if(!child.valid)allValid=false;allDiagnostics.push(...child.diagnostics);elementSchemas.push(child.outputSchema)}return{valid:allValid,diagnostics:allDiagnostics,outputSchema:{type:"array",items:computeArrayItemsSchema(elementSchemas),minItems:length,maxItems:length}}}export function aggregateArrayAnalysisAndExecution(length,processEntry){const allDiagnostics=[];const elementSchemas=[];const resultValues=[];let allValid=true;for(let i=0;i<length;i++){const child=processEntry(i);if(!child.analysis.valid)allValid=false;allDiagnostics.push(...child.analysis.diagnostics);elementSchemas.push(child.analysis.outputSchema);resultValues.push(child.value)}const analysis={valid:allValid,diagnostics:allDiagnostics,outputSchema:{type:"array",items:computeArrayItemsSchema(elementSchemas),minItems:length,maxItems:length}};return{analysis,value:allValid?resultValues:undefined}}
|
|
2
2
|
//# sourceMappingURL=utils.js.map
|
package/dist/esm/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils.ts"],"sourcesContent":["import type { JSONSchema7 } from \"json-schema\";\nimport type { AnalysisResult, TemplateDiagnostic } from \"./types.ts\";\n\n// ─── Utilities ───────────────────────────────────────────────────────────────\n// Shared utility functions and classes used across the different modules\n// of the template engine.\n\n// ─── Deep Equality ───────────────────────────────────────────────────────────\n// Deep structural comparison for JSON-compatible values.\n// More robust than `JSON.stringify` because it is independent of key order\n// and does not allocate intermediate strings.\n\n/**\n * Recursively compares two JSON-compatible values.\n *\n * @param a - First value\n * @param b - Second value\n * @returns `true` if the two values are structurally identical\n *\n * @example\n * ```\n * deepEqual({ a: 1, b: 2 }, { b: 2, a: 1 }) // → true\n * deepEqual([1, 2], [1, 2]) // → true\n * deepEqual({ a: 1 }, { a: 2 }) // → false\n * ```\n */\nexport function deepEqual(a: unknown, b: unknown): boolean {\n\t// Strict identity (covers primitives, same ref; NaN !== NaN is intentional)\n\tif (a === b) return true;\n\n\t// null is typeof \"object\" in JS — handle it separately\n\tif (a === null || b === null) return false;\n\tif (typeof a !== typeof b) return false;\n\n\t// ── Arrays ───────────────────────────────────────────────────────────────\n\tif (Array.isArray(a)) {\n\t\tif (!Array.isArray(b)) return false;\n\t\tif (a.length !== b.length) return false;\n\t\tfor (let i = 0; i < a.length; i++) {\n\t\t\tif (!deepEqual(a[i], b[i])) return false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t// ── Objects ──────────────────────────────────────────────────────────────\n\tif (typeof a === \"object\") {\n\t\tconst objA = a as Record<string, unknown>;\n\t\tconst objB = b as Record<string, unknown>;\n\t\tconst keysA = Object.keys(objA);\n\t\tconst keysB = Object.keys(objB);\n\n\t\tif (keysA.length !== keysB.length) return false;\n\n\t\tfor (const key of keysA) {\n\t\t\tif (!(key in objB) || !deepEqual(objA[key], objB[key])) return false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t// Different primitives (already covered by a === b at the top)\n\treturn false;\n}\n\n// ─── LRU Cache ───────────────────────────────────────────────────────────────\n// Fixed-capacity cache with Least Recently Used (LRU) eviction.\n// Leverages `Map` insertion order to track access: the oldest entry\n// is always in the first position.\n\n/**\n * Simple fixed-capacity LRU cache.\n *\n * @example\n * ```\n * const cache = new LRUCache<string, number>(2);\n * cache.set(\"a\", 1);\n * cache.set(\"b\", 2);\n * cache.get(\"a\"); // → 1 (marks \"a\" as recently used)\n * cache.set(\"c\", 3); // evicts \"b\" (least recently used)\n * cache.get(\"b\"); // → undefined\n * ```\n */\nexport class LRUCache<K, V> {\n\tprivate readonly cache = new Map<K, V>();\n\n\tconstructor(private readonly capacity: number) {\n\t\tif (capacity < 1) {\n\t\t\tthrow new Error(\"LRUCache capacity must be at least 1\");\n\t\t}\n\t}\n\n\t/**\n\t * Retrieves a value from the cache. Returns `undefined` if absent.\n\t * Marks the entry as recently used.\n\t */\n\tget(key: K): V | undefined {\n\t\tif (!this.cache.has(key)) return undefined;\n\n\t\t// Move to the end of the Map (= most recently used)\n\t\tconst value = this.cache.get(key) as V;\n\t\tthis.cache.delete(key);\n\t\tthis.cache.set(key, value);\n\t\treturn value;\n\t}\n\n\t/**\n\t * Inserts or updates a value in the cache.\n\t * If the cache is full, evicts the least recently used entry.\n\t */\n\tset(key: K, value: V): void {\n\t\tif (this.cache.has(key)) {\n\t\t\tthis.cache.delete(key);\n\t\t} else if (this.cache.size >= this.capacity) {\n\t\t\t// Evict the first entry (the oldest one)\n\t\t\tconst oldestKey = this.cache.keys().next().value;\n\t\t\tif (oldestKey !== undefined) {\n\t\t\t\tthis.cache.delete(oldestKey);\n\t\t\t}\n\t\t}\n\t\tthis.cache.set(key, value);\n\t}\n\n\t/**\n\t * Checks whether a key exists in the cache (without affecting LRU order).\n\t */\n\thas(key: K): boolean {\n\t\treturn this.cache.has(key);\n\t}\n\n\t/**\n\t * Removes an entry from the cache.\n\t * @returns `true` if the entry existed and was removed\n\t */\n\tdelete(key: K): boolean {\n\t\treturn this.cache.delete(key);\n\t}\n\n\t/** Clears the entire cache. */\n\tclear(): void {\n\t\tthis.cache.clear();\n\t}\n\n\t/** Number of entries currently in the cache. */\n\tget size(): number {\n\t\treturn this.cache.size;\n\t}\n}\n\n// ─── Source Snippet Extraction ────────────────────────────────────────────────\n// Used to enrich diagnostics with the template fragment that caused the error.\n\n/**\n * Extracts a template fragment around a given position.\n *\n * @param template - The full template source\n * @param loc - The position (line/column, 1-based) of the error\n * @returns The corresponding code fragment (trimmed)\n */\nexport function extractSourceSnippet(\n\ttemplate: string,\n\tloc: {\n\t\tstart: { line: number; column: number };\n\t\tend: { line: number; column: number };\n\t},\n): string {\n\tconst lines = template.split(\"\\n\");\n\tconst startLine = loc.start.line - 1; // 0-based\n\tconst endLine = loc.end.line - 1;\n\n\tif (startLine < 0 || startLine >= lines.length) return \"\";\n\n\tif (startLine === endLine) {\n\t\t// Same line — extract the portion between start.column and end.column\n\t\tconst line = lines[startLine] ?? \"\";\n\t\treturn line.trim();\n\t}\n\n\t// Multi-line — return the affected lines\n\tconst clampedEnd = Math.min(endLine, lines.length - 1);\n\treturn lines\n\t\t.slice(startLine, clampedEnd + 1)\n\t\t.map((l) => l.trimEnd())\n\t\t.join(\"\\n\")\n\t\t.trim();\n}\n\n// ─── Schema Properties ──────────────────────────────────────────────────────\n// Utility for listing available properties in a schema, used to enrich\n// error messages with suggestions.\n\n/**\n * Lists the declared property names in a JSON Schema.\n * Returns an empty array if the schema has no `properties`.\n */\nexport function getSchemaPropertyNames(schema: JSONSchema7): string[] {\n\tconst names = new Set<string>();\n\n\t// Direct properties\n\tif (schema.properties) {\n\t\tfor (const key of Object.keys(schema.properties)) {\n\t\t\tnames.add(key);\n\t\t}\n\t}\n\n\t// Properties within combinators\n\tfor (const combinator of [\"allOf\", \"anyOf\", \"oneOf\"] as const) {\n\t\tconst branches = schema[combinator];\n\t\tif (branches) {\n\t\t\tfor (const branch of branches) {\n\t\t\t\tif (typeof branch === \"boolean\") continue;\n\t\t\t\tif (branch.properties) {\n\t\t\t\t\tfor (const key of Object.keys(branch.properties)) {\n\t\t\t\t\t\tnames.add(key);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn Array.from(names).sort();\n}\n\n// ─── Object Analysis Aggregation ─────────────────────────────────────────────\n// Factorizes the common recursion pattern over template objects:\n// iterate the keys, analyze each entry via a callback, accumulate\n// diagnostics, and build the output object schema.\n//\n// Used by:\n// - `analyzer.ts` (analyzeObjectTemplate)\n// - `Typebars.analyzeObject()` (typebars.ts)\n// - `CompiledTemplate.analyze()` in object mode (compiled-template.ts)\n\n/**\n * Aggregates analysis results from a set of named entries into a single\n * `AnalysisResult` with an object-typed `outputSchema`.\n *\n * @param keys - The keys of the object to analyze\n * @param analyzeEntry - Callback that analyzes an entry by its key\n * @returns An aggregated `AnalysisResult`\n *\n * @example\n * ```\n * aggregateObjectAnalysis(\n * Object.keys(template),\n * (key) => analyze(template[key], inputSchema),\n * );\n * ```\n */\nexport function aggregateObjectAnalysis(\n\tkeys: string[],\n\tanalyzeEntry: (key: string) => AnalysisResult,\n): AnalysisResult {\n\tconst allDiagnostics: TemplateDiagnostic[] = [];\n\tconst properties: Record<string, JSONSchema7> = {};\n\tlet allValid = true;\n\n\tfor (const key of keys) {\n\t\tconst child = analyzeEntry(key);\n\t\tif (!child.valid) allValid = false;\n\t\tallDiagnostics.push(...child.diagnostics);\n\t\tproperties[key] = child.outputSchema;\n\t}\n\n\treturn {\n\t\tvalid: allValid,\n\t\tdiagnostics: allDiagnostics,\n\t\toutputSchema: {\n\t\t\ttype: \"object\",\n\t\t\tproperties,\n\t\t\trequired: keys,\n\t\t},\n\t};\n}\n\n/**\n * Aggregates both analysis **and** execution results from a set of named\n * entries. Returns the aggregated `AnalysisResult` and the object of\n * executed values (or `undefined` if at least one entry is invalid).\n *\n * @param keys - The keys of the object\n * @param processEntry - Callback that analyzes and executes an entry by its key\n * @returns Aggregated `{ analysis, value }`\n */\nexport function aggregateObjectAnalysisAndExecution(\n\tkeys: string[],\n\tprocessEntry: (key: string) => { analysis: AnalysisResult; value: unknown },\n): { analysis: AnalysisResult; value: unknown } {\n\tconst allDiagnostics: TemplateDiagnostic[] = [];\n\tconst properties: Record<string, JSONSchema7> = {};\n\tconst resultValues: Record<string, unknown> = {};\n\tlet allValid = true;\n\n\tfor (const key of keys) {\n\t\tconst child = processEntry(key);\n\t\tif (!child.analysis.valid) allValid = false;\n\t\tallDiagnostics.push(...child.analysis.diagnostics);\n\t\tproperties[key] = child.analysis.outputSchema;\n\t\tresultValues[key] = child.value;\n\t}\n\n\tconst analysis: AnalysisResult = {\n\t\tvalid: allValid,\n\t\tdiagnostics: allDiagnostics,\n\t\toutputSchema: {\n\t\t\ttype: \"object\",\n\t\t\tproperties,\n\t\t\trequired: keys,\n\t\t},\n\t};\n\n\treturn {\n\t\tanalysis,\n\t\tvalue: allValid ? resultValues : undefined,\n\t};\n}\n\n// ─── Array Analysis Aggregation ──────────────────────────────────────────────\n// Factorizes the common recursion pattern over template arrays:\n// iterate the elements, analyze each entry via a callback, accumulate\n// diagnostics, and build the output array schema with a proper `items`.\n//\n// Used by:\n// - `analyzer.ts` (analyzeArrayTemplate)\n// - `Typebars.analyze()` (typebars.ts)\n// - `CompiledTemplate.analyze()` in array mode (compiled-template.ts)\n\n/**\n * Computes the `items` schema for an array from the output schemas of its\n * elements:\n * - If all elements share the same schema → that schema\n * - If there are multiple distinct schemas → `{ oneOf: [...] }`\n * - If the array is empty → `{}` (any type)\n */\nfunction computeArrayItemsSchema(elementSchemas: JSONSchema7[]): JSONSchema7 {\n\tif (elementSchemas.length === 0) return {};\n\n\t// Deduplicate schemas using deep equality\n\tconst unique: JSONSchema7[] = [];\n\tfor (const schema of elementSchemas) {\n\t\tif (!unique.some((u) => deepEqual(u, schema))) {\n\t\t\tunique.push(schema);\n\t\t}\n\t}\n\n\tif (unique.length === 1) {\n\t\treturn unique[0] as JSONSchema7;\n\t}\n\n\treturn { oneOf: unique };\n}\n\n/**\n * Aggregates analysis results from a set of array elements into a single\n * `AnalysisResult` with an array-typed `outputSchema`.\n *\n * @param length - The number of elements in the array\n * @param analyzeEntry - Callback that analyzes an element by its index\n * @returns An aggregated `AnalysisResult`\n *\n * @example\n * ```\n * aggregateArrayAnalysis(\n * template.length,\n * (index) => analyze(template[index], inputSchema),\n * );\n * ```\n */\nexport function aggregateArrayAnalysis(\n\tlength: number,\n\tanalyzeEntry: (index: number) => AnalysisResult,\n): AnalysisResult {\n\tconst allDiagnostics: TemplateDiagnostic[] = [];\n\tconst elementSchemas: JSONSchema7[] = [];\n\tlet allValid = true;\n\n\tfor (let i = 0; i < length; i++) {\n\t\tconst child = analyzeEntry(i);\n\t\tif (!child.valid) allValid = false;\n\t\tallDiagnostics.push(...child.diagnostics);\n\t\telementSchemas.push(child.outputSchema);\n\t}\n\n\treturn {\n\t\tvalid: allValid,\n\t\tdiagnostics: allDiagnostics,\n\t\toutputSchema: {\n\t\t\ttype: \"array\",\n\t\t\titems: computeArrayItemsSchema(elementSchemas),\n\t\t},\n\t};\n}\n\n/**\n * Aggregates both analysis **and** execution results from a set of array\n * elements. Returns the aggregated `AnalysisResult` and the array of\n * executed values (or `undefined` if at least one element is invalid).\n *\n * @param length - The number of elements\n * @param processEntry - Callback that analyzes and executes an element by index\n * @returns Aggregated `{ analysis, value }`\n */\nexport function aggregateArrayAnalysisAndExecution(\n\tlength: number,\n\tprocessEntry: (index: number) => { analysis: AnalysisResult; value: unknown },\n): { analysis: AnalysisResult; value: unknown } {\n\tconst allDiagnostics: TemplateDiagnostic[] = [];\n\tconst elementSchemas: JSONSchema7[] = [];\n\tconst resultValues: unknown[] = [];\n\tlet allValid = true;\n\n\tfor (let i = 0; i < length; i++) {\n\t\tconst child = processEntry(i);\n\t\tif (!child.analysis.valid) allValid = false;\n\t\tallDiagnostics.push(...child.analysis.diagnostics);\n\t\telementSchemas.push(child.analysis.outputSchema);\n\t\tresultValues.push(child.value);\n\t}\n\n\tconst analysis: AnalysisResult = {\n\t\tvalid: allValid,\n\t\tdiagnostics: allDiagnostics,\n\t\toutputSchema: {\n\t\t\ttype: \"array\",\n\t\t\titems: computeArrayItemsSchema(elementSchemas),\n\t\t},\n\t};\n\n\treturn {\n\t\tanalysis,\n\t\tvalue: allValid ? resultValues : undefined,\n\t};\n}\n"],"names":["deepEqual","a","b","Array","isArray","length","i","objA","objB","keysA","Object","keys","keysB","key","LRUCache","get","cache","has","undefined","value","delete","set","size","capacity","oldestKey","next","clear","Map","Error","extractSourceSnippet","template","loc","lines","split","startLine","start","line","endLine","end","trim","clampedEnd","Math","min","slice","map","l","trimEnd","join","getSchemaPropertyNames","schema","names","Set","properties","add","combinator","branches","branch","from","sort","aggregateObjectAnalysis","analyzeEntry","allDiagnostics","allValid","child","valid","push","diagnostics","outputSchema","type","required","aggregateObjectAnalysisAndExecution","processEntry","resultValues","analysis","computeArrayItemsSchema","elementSchemas","unique","some","u","oneOf","aggregateArrayAnalysis","items","aggregateArrayAnalysisAndExecution"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/utils.ts"],"sourcesContent":["import type { JSONSchema7 } from \"json-schema\";\nimport type { AnalysisResult, TemplateDiagnostic } from \"./types.ts\";\n\n// ─── Utilities ───────────────────────────────────────────────────────────────\n// Shared utility functions and classes used across the different modules\n// of the template engine.\n\n// ─── Deep Equality ───────────────────────────────────────────────────────────\n// Deep structural comparison for JSON-compatible values.\n// More robust than `JSON.stringify` because it is independent of key order\n// and does not allocate intermediate strings.\n\n/**\n * Recursively compares two JSON-compatible values.\n *\n * @param a - First value\n * @param b - Second value\n * @returns `true` if the two values are structurally identical\n *\n * @example\n * ```\n * deepEqual({ a: 1, b: 2 }, { b: 2, a: 1 }) // → true\n * deepEqual([1, 2], [1, 2]) // → true\n * deepEqual({ a: 1 }, { a: 2 }) // → false\n * ```\n */\nexport function deepEqual(a: unknown, b: unknown): boolean {\n\t// Strict identity (covers primitives, same ref; NaN !== NaN is intentional)\n\tif (a === b) return true;\n\n\t// null is typeof \"object\" in JS — handle it separately\n\tif (a === null || b === null) return false;\n\tif (typeof a !== typeof b) return false;\n\n\t// ── Arrays ───────────────────────────────────────────────────────────────\n\tif (Array.isArray(a)) {\n\t\tif (!Array.isArray(b)) return false;\n\t\tif (a.length !== b.length) return false;\n\t\tfor (let i = 0; i < a.length; i++) {\n\t\t\tif (!deepEqual(a[i], b[i])) return false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t// ── Objects ──────────────────────────────────────────────────────────────\n\tif (typeof a === \"object\") {\n\t\tconst objA = a as Record<string, unknown>;\n\t\tconst objB = b as Record<string, unknown>;\n\t\tconst keysA = Object.keys(objA);\n\t\tconst keysB = Object.keys(objB);\n\n\t\tif (keysA.length !== keysB.length) return false;\n\n\t\tfor (const key of keysA) {\n\t\t\tif (!(key in objB) || !deepEqual(objA[key], objB[key])) return false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t// Different primitives (already covered by a === b at the top)\n\treturn false;\n}\n\n// ─── LRU Cache ───────────────────────────────────────────────────────────────\n// Fixed-capacity cache with Least Recently Used (LRU) eviction.\n// Leverages `Map` insertion order to track access: the oldest entry\n// is always in the first position.\n\n/**\n * Simple fixed-capacity LRU cache.\n *\n * @example\n * ```\n * const cache = new LRUCache<string, number>(2);\n * cache.set(\"a\", 1);\n * cache.set(\"b\", 2);\n * cache.get(\"a\"); // → 1 (marks \"a\" as recently used)\n * cache.set(\"c\", 3); // evicts \"b\" (least recently used)\n * cache.get(\"b\"); // → undefined\n * ```\n */\nexport class LRUCache<K, V> {\n\tprivate readonly cache = new Map<K, V>();\n\n\tconstructor(private readonly capacity: number) {\n\t\tif (capacity < 1) {\n\t\t\tthrow new Error(\"LRUCache capacity must be at least 1\");\n\t\t}\n\t}\n\n\t/**\n\t * Retrieves a value from the cache. Returns `undefined` if absent.\n\t * Marks the entry as recently used.\n\t */\n\tget(key: K): V | undefined {\n\t\tif (!this.cache.has(key)) return undefined;\n\n\t\t// Move to the end of the Map (= most recently used)\n\t\tconst value = this.cache.get(key) as V;\n\t\tthis.cache.delete(key);\n\t\tthis.cache.set(key, value);\n\t\treturn value;\n\t}\n\n\t/**\n\t * Inserts or updates a value in the cache.\n\t * If the cache is full, evicts the least recently used entry.\n\t */\n\tset(key: K, value: V): void {\n\t\tif (this.cache.has(key)) {\n\t\t\tthis.cache.delete(key);\n\t\t} else if (this.cache.size >= this.capacity) {\n\t\t\t// Evict the first entry (the oldest one)\n\t\t\tconst oldestKey = this.cache.keys().next().value;\n\t\t\tif (oldestKey !== undefined) {\n\t\t\t\tthis.cache.delete(oldestKey);\n\t\t\t}\n\t\t}\n\t\tthis.cache.set(key, value);\n\t}\n\n\t/**\n\t * Checks whether a key exists in the cache (without affecting LRU order).\n\t */\n\thas(key: K): boolean {\n\t\treturn this.cache.has(key);\n\t}\n\n\t/**\n\t * Removes an entry from the cache.\n\t * @returns `true` if the entry existed and was removed\n\t */\n\tdelete(key: K): boolean {\n\t\treturn this.cache.delete(key);\n\t}\n\n\t/** Clears the entire cache. */\n\tclear(): void {\n\t\tthis.cache.clear();\n\t}\n\n\t/** Number of entries currently in the cache. */\n\tget size(): number {\n\t\treturn this.cache.size;\n\t}\n}\n\n// ─── Source Snippet Extraction ────────────────────────────────────────────────\n// Used to enrich diagnostics with the template fragment that caused the error.\n\n/**\n * Extracts a template fragment around a given position.\n *\n * @param template - The full template source\n * @param loc - The position (line/column, 1-based) of the error\n * @returns The corresponding code fragment (trimmed)\n */\nexport function extractSourceSnippet(\n\ttemplate: string,\n\tloc: {\n\t\tstart: { line: number; column: number };\n\t\tend: { line: number; column: number };\n\t},\n): string {\n\tconst lines = template.split(\"\\n\");\n\tconst startLine = loc.start.line - 1; // 0-based\n\tconst endLine = loc.end.line - 1;\n\n\tif (startLine < 0 || startLine >= lines.length) return \"\";\n\n\tif (startLine === endLine) {\n\t\t// Same line — extract the portion between start.column and end.column\n\t\tconst line = lines[startLine] ?? \"\";\n\t\treturn line.trim();\n\t}\n\n\t// Multi-line — return the affected lines\n\tconst clampedEnd = Math.min(endLine, lines.length - 1);\n\treturn lines\n\t\t.slice(startLine, clampedEnd + 1)\n\t\t.map((l) => l.trimEnd())\n\t\t.join(\"\\n\")\n\t\t.trim();\n}\n\n// ─── Schema Properties ──────────────────────────────────────────────────────\n// Utility for listing available properties in a schema, used to enrich\n// error messages with suggestions.\n\n/**\n * Lists the declared property names in a JSON Schema.\n * Returns an empty array if the schema has no `properties`.\n */\nexport function getSchemaPropertyNames(schema: JSONSchema7): string[] {\n\tconst names = new Set<string>();\n\n\t// Direct properties\n\tif (schema.properties) {\n\t\tfor (const key of Object.keys(schema.properties)) {\n\t\t\tnames.add(key);\n\t\t}\n\t}\n\n\t// Properties within combinators\n\tfor (const combinator of [\"allOf\", \"anyOf\", \"oneOf\"] as const) {\n\t\tconst branches = schema[combinator];\n\t\tif (branches) {\n\t\t\tfor (const branch of branches) {\n\t\t\t\tif (typeof branch === \"boolean\") continue;\n\t\t\t\tif (branch.properties) {\n\t\t\t\t\tfor (const key of Object.keys(branch.properties)) {\n\t\t\t\t\t\tnames.add(key);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn Array.from(names).sort();\n}\n\n// ─── Object Analysis Aggregation ─────────────────────────────────────────────\n// Factorizes the common recursion pattern over template objects:\n// iterate the keys, analyze each entry via a callback, accumulate\n// diagnostics, and build the output object schema.\n//\n// Used by:\n// - `analyzer.ts` (analyzeObjectTemplate)\n// - `Typebars.analyzeObject()` (typebars.ts)\n// - `CompiledTemplate.analyze()` in object mode (compiled-template.ts)\n\n/**\n * Aggregates analysis results from a set of named entries into a single\n * `AnalysisResult` with an object-typed `outputSchema`.\n *\n * @param keys - The keys of the object to analyze\n * @param analyzeEntry - Callback that analyzes an entry by its key\n * @returns An aggregated `AnalysisResult`\n *\n * @example\n * ```\n * aggregateObjectAnalysis(\n * Object.keys(template),\n * (key) => analyze(template[key], inputSchema),\n * );\n * ```\n */\nexport function aggregateObjectAnalysis(\n\tkeys: string[],\n\tanalyzeEntry: (key: string) => AnalysisResult,\n): AnalysisResult {\n\tconst allDiagnostics: TemplateDiagnostic[] = [];\n\tconst properties: Record<string, JSONSchema7> = {};\n\tlet allValid = true;\n\n\tfor (const key of keys) {\n\t\tconst child = analyzeEntry(key);\n\t\tif (!child.valid) allValid = false;\n\t\tallDiagnostics.push(...child.diagnostics);\n\t\tproperties[key] = child.outputSchema;\n\t}\n\n\treturn {\n\t\tvalid: allValid,\n\t\tdiagnostics: allDiagnostics,\n\t\toutputSchema: {\n\t\t\ttype: \"object\",\n\t\t\tproperties,\n\t\t\trequired: keys,\n\t\t},\n\t};\n}\n\n/**\n * Aggregates both analysis **and** execution results from a set of named\n * entries. Returns the aggregated `AnalysisResult` and the object of\n * executed values (or `undefined` if at least one entry is invalid).\n *\n * @param keys - The keys of the object\n * @param processEntry - Callback that analyzes and executes an entry by its key\n * @returns Aggregated `{ analysis, value }`\n */\nexport function aggregateObjectAnalysisAndExecution(\n\tkeys: string[],\n\tprocessEntry: (key: string) => { analysis: AnalysisResult; value: unknown },\n): { analysis: AnalysisResult; value: unknown } {\n\tconst allDiagnostics: TemplateDiagnostic[] = [];\n\tconst properties: Record<string, JSONSchema7> = {};\n\tconst resultValues: Record<string, unknown> = {};\n\tlet allValid = true;\n\n\tfor (const key of keys) {\n\t\tconst child = processEntry(key);\n\t\tif (!child.analysis.valid) allValid = false;\n\t\tallDiagnostics.push(...child.analysis.diagnostics);\n\t\tproperties[key] = child.analysis.outputSchema;\n\t\tresultValues[key] = child.value;\n\t}\n\n\tconst analysis: AnalysisResult = {\n\t\tvalid: allValid,\n\t\tdiagnostics: allDiagnostics,\n\t\toutputSchema: {\n\t\t\ttype: \"object\",\n\t\t\tproperties,\n\t\t\trequired: keys,\n\t\t},\n\t};\n\n\treturn {\n\t\tanalysis,\n\t\tvalue: allValid ? resultValues : undefined,\n\t};\n}\n\n// ─── Array Analysis Aggregation ──────────────────────────────────────────────\n// Factorizes the common recursion pattern over template arrays:\n// iterate the elements, analyze each entry via a callback, accumulate\n// diagnostics, and build the output array schema with a proper `items`.\n//\n// Used by:\n// - `analyzer.ts` (analyzeArrayTemplate)\n// - `Typebars.analyze()` (typebars.ts)\n// - `CompiledTemplate.analyze()` in array mode (compiled-template.ts)\n\n/**\n * Computes the `items` schema for an array from the output schemas of its\n * elements:\n * - If all elements share the same schema → that schema\n * - If there are multiple distinct schemas → `{ oneOf: [...] }`\n * - If the array is empty → `{}` (any type)\n */\nfunction computeArrayItemsSchema(elementSchemas: JSONSchema7[]): JSONSchema7 {\n\tif (elementSchemas.length === 0) return {};\n\n\t// Deduplicate schemas using deep equality\n\tconst unique: JSONSchema7[] = [];\n\tfor (const schema of elementSchemas) {\n\t\tif (!unique.some((u) => deepEqual(u, schema))) {\n\t\t\tunique.push(schema);\n\t\t}\n\t}\n\n\tif (unique.length === 1) {\n\t\treturn unique[0] as JSONSchema7;\n\t}\n\n\treturn { oneOf: unique };\n}\n\n/**\n * Aggregates analysis results from a set of array elements into a single\n * `AnalysisResult` with an array-typed `outputSchema`.\n *\n * @param length - The number of elements in the array\n * @param analyzeEntry - Callback that analyzes an element by its index\n * @returns An aggregated `AnalysisResult`\n *\n * @example\n * ```\n * aggregateArrayAnalysis(\n * template.length,\n * (index) => analyze(template[index], inputSchema),\n * );\n * ```\n */\nexport function aggregateArrayAnalysis(\n\tlength: number,\n\tanalyzeEntry: (index: number) => AnalysisResult,\n): AnalysisResult {\n\tconst allDiagnostics: TemplateDiagnostic[] = [];\n\tconst elementSchemas: JSONSchema7[] = [];\n\tlet allValid = true;\n\n\tfor (let i = 0; i < length; i++) {\n\t\tconst child = analyzeEntry(i);\n\t\tif (!child.valid) allValid = false;\n\t\tallDiagnostics.push(...child.diagnostics);\n\t\telementSchemas.push(child.outputSchema);\n\t}\n\n\treturn {\n\t\tvalid: allValid,\n\t\tdiagnostics: allDiagnostics,\n\t\toutputSchema: {\n\t\t\ttype: \"array\",\n\t\t\titems: computeArrayItemsSchema(elementSchemas),\n\t\t\tminItems: length,\n\t\t\tmaxItems: length,\n\t\t},\n\t};\n}\n\n/**\n * Aggregates both analysis **and** execution results from a set of array\n * elements. Returns the aggregated `AnalysisResult` and the array of\n * executed values (or `undefined` if at least one element is invalid).\n *\n * @param length - The number of elements\n * @param processEntry - Callback that analyzes and executes an element by index\n * @returns Aggregated `{ analysis, value }`\n */\nexport function aggregateArrayAnalysisAndExecution(\n\tlength: number,\n\tprocessEntry: (index: number) => { analysis: AnalysisResult; value: unknown },\n): { analysis: AnalysisResult; value: unknown } {\n\tconst allDiagnostics: TemplateDiagnostic[] = [];\n\tconst elementSchemas: JSONSchema7[] = [];\n\tconst resultValues: unknown[] = [];\n\tlet allValid = true;\n\n\tfor (let i = 0; i < length; i++) {\n\t\tconst child = processEntry(i);\n\t\tif (!child.analysis.valid) allValid = false;\n\t\tallDiagnostics.push(...child.analysis.diagnostics);\n\t\telementSchemas.push(child.analysis.outputSchema);\n\t\tresultValues.push(child.value);\n\t}\n\n\tconst analysis: AnalysisResult = {\n\t\tvalid: allValid,\n\t\tdiagnostics: allDiagnostics,\n\t\toutputSchema: {\n\t\t\ttype: \"array\",\n\t\t\titems: computeArrayItemsSchema(elementSchemas),\n\t\t\tminItems: length,\n\t\t\tmaxItems: length,\n\t\t},\n\t};\n\n\treturn {\n\t\tanalysis,\n\t\tvalue: allValid ? resultValues : undefined,\n\t};\n}\n"],"names":["deepEqual","a","b","Array","isArray","length","i","objA","objB","keysA","Object","keys","keysB","key","LRUCache","get","cache","has","undefined","value","delete","set","size","capacity","oldestKey","next","clear","Map","Error","extractSourceSnippet","template","loc","lines","split","startLine","start","line","endLine","end","trim","clampedEnd","Math","min","slice","map","l","trimEnd","join","getSchemaPropertyNames","schema","names","Set","properties","add","combinator","branches","branch","from","sort","aggregateObjectAnalysis","analyzeEntry","allDiagnostics","allValid","child","valid","push","diagnostics","outputSchema","type","required","aggregateObjectAnalysisAndExecution","processEntry","resultValues","analysis","computeArrayItemsSchema","elementSchemas","unique","some","u","oneOf","aggregateArrayAnalysis","items","minItems","maxItems","aggregateArrayAnalysisAndExecution"],"mappings":"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"}
|
package/package.json
CHANGED