@kinotic-ai/spawn 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,14 @@
1
+ # @kinotic-ai/spawn
2
+
3
+ Host-agnostic engine for rendering Spawns: file trees containing liquid
4
+ templates, liquid expressions in file paths, and an optional `spawn.json`
5
+ declaring globals, a property schema, and inheritance.
6
+
7
+ The engine performs no IO — it takes an in-memory tree of `path -> content`
8
+ and returns the rendered tree. Hosts supply the adapters:
9
+
10
+ - the Kinotic CLI loads spawns from disk and prompts for missing properties
11
+ via a `PropertyResolver`
12
+ - the Kinotic server runs the same engine embedded in the JVM (GraalJS) to
13
+ render project baselines into freshly provisioned repositories, with the
14
+ context derived entirely from the `Project`
package/dist/index.cjs ADDED
@@ -0,0 +1,167 @@
1
+ var import_node_module = require("node:module");
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ function __accessProp(key) {
7
+ return this[key];
8
+ }
9
+ var __toCommonJS = (from) => {
10
+ var entry = (__moduleCache ??= new WeakMap).get(from), desc;
11
+ if (entry)
12
+ return entry;
13
+ entry = __defProp({}, "__esModule", { value: true });
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (var key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(entry, key))
17
+ __defProp(entry, key, {
18
+ get: __accessProp.bind(from, key),
19
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
20
+ });
21
+ }
22
+ __moduleCache.set(from, entry);
23
+ return entry;
24
+ };
25
+ var __moduleCache;
26
+ var __returnValue = (v) => v;
27
+ function __exportSetter(name, newValue) {
28
+ this[name] = __returnValue.bind(null, newValue);
29
+ }
30
+ var __export = (target, all) => {
31
+ for (var name in all)
32
+ __defProp(target, name, {
33
+ get: all[name],
34
+ enumerable: true,
35
+ configurable: true,
36
+ set: __exportSetter.bind(all, name)
37
+ });
38
+ };
39
+
40
+ // packages/spawn/src/index.ts
41
+ var exports_src = {};
42
+ __export(exports_src, {
43
+ SpawnEngine: () => SpawnEngine
44
+ });
45
+ module.exports = __toCommonJS(exports_src);
46
+
47
+ // packages/spawn/src/api/SpawnEngine.ts
48
+ var import_liquidjs = require("liquidjs");
49
+ var import_zod = require("zod");
50
+ var PropertySchemaSchema = import_zod.z.object({
51
+ type: import_zod.z.enum(["string", "number", "integer", "boolean"]).optional(),
52
+ description: import_zod.z.string().optional(),
53
+ default: import_zod.z.union([import_zod.z.string(), import_zod.z.number(), import_zod.z.boolean()]).optional(),
54
+ enum: import_zod.z.array(import_zod.z.string()).optional()
55
+ });
56
+ var SpawnConfigSchema = import_zod.z.object({
57
+ inherits: import_zod.z.string().optional(),
58
+ globals: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional(),
59
+ propertySchema: import_zod.z.record(import_zod.z.string(), PropertySchemaSchema).optional()
60
+ });
61
+ var IGNORED_FILE_NAMES = ["spawn.json", ".DS_Store"];
62
+ function upperFirst(s) {
63
+ return s.charAt(0).toUpperCase() + s.slice(1);
64
+ }
65
+ function camelCase(s) {
66
+ return s.replace(/[-_\s]+(.)/g, (_, c) => c.toUpperCase()).replace(/^(.)/, (_, c) => c.toLowerCase());
67
+ }
68
+
69
+ class SpawnEngine {
70
+ engine;
71
+ constructor() {
72
+ this.engine = new import_liquidjs.Liquid({ cache: true });
73
+ this.engine.registerFilter("packageToPath", (v) => v.replaceAll(".", "/"));
74
+ this.engine.registerFilter("encodePackage", (v) => {
75
+ v = v.replaceAll("-", "_");
76
+ v = v.replace(/\.(\d+)/g, "._$1");
77
+ return v;
78
+ });
79
+ this.engine.registerFilter("camelCase", (v) => camelCase(v));
80
+ this.engine.registerFilter("upperFirst", (v) => upperFirst(v));
81
+ }
82
+ async renderSpawn(spawn, options) {
83
+ const trees = [spawn];
84
+ const configs = [];
85
+ let currentConfig = this.parseConfig(spawn);
86
+ if (currentConfig) {
87
+ configs.push(currentConfig);
88
+ }
89
+ while (currentConfig?.inherits) {
90
+ if (!options?.loadInherited) {
91
+ throw new Error(`Spawn inherits '${currentConfig.inherits}' but no loadInherited callback was provided`);
92
+ }
93
+ const inherited = await options.loadInherited(currentConfig.inherits);
94
+ trees.push(inherited);
95
+ currentConfig = this.parseConfig(inherited);
96
+ if (currentConfig) {
97
+ configs.push(currentConfig);
98
+ }
99
+ }
100
+ let globals = {};
101
+ let propertySchemas = {};
102
+ for (const config of [...configs].reverse()) {
103
+ if (config.globals) {
104
+ globals = { ...globals, ...config.globals };
105
+ }
106
+ if (config.propertySchema) {
107
+ propertySchemas = { ...propertySchemas, ...config.propertySchema };
108
+ }
109
+ }
110
+ let context = { ...globals, ...options?.context };
111
+ context = await this.resolveMissingProperties(propertySchemas, context, options?.propertyResolver);
112
+ const files = {};
113
+ for (const tree of [...trees].reverse()) {
114
+ for (const source of Object.keys(tree).sort()) {
115
+ const fileName = source.substring(source.lastIndexOf("/") + 1);
116
+ if (IGNORED_FILE_NAMES.includes(fileName)) {
117
+ continue;
118
+ }
119
+ let destination = source;
120
+ if (destination.includes("{{")) {
121
+ destination = await this.engine.parseAndRender(destination, context);
122
+ }
123
+ let content = tree[source];
124
+ if (destination.endsWith(".liquid")) {
125
+ destination = destination.substring(0, destination.length - 7);
126
+ if (typeof content !== "string") {
127
+ throw new Error(`Template ${source} must contain text content`);
128
+ }
129
+ content = await this.engine.parseAndRender(content, context);
130
+ }
131
+ files[destination] = content;
132
+ }
133
+ }
134
+ return { files, context };
135
+ }
136
+ parseConfig(tree) {
137
+ const raw = tree["spawn.json"];
138
+ if (raw === undefined) {
139
+ return;
140
+ }
141
+ const text = typeof raw === "string" ? raw : new TextDecoder().decode(raw);
142
+ return SpawnConfigSchema.parse(JSON.parse(text));
143
+ }
144
+ async resolveMissingProperties(propertySchemas, context, resolver) {
145
+ const ret = { ...context };
146
+ for (const key in propertySchemas) {
147
+ if (!Object.prototype.hasOwnProperty.call(ret, key)) {
148
+ if (!resolver) {
149
+ throw new Error(`No value provided for required property '${key}'`);
150
+ }
151
+ const schema = propertySchemas[key];
152
+ let message;
153
+ if (schema.description?.includes("{{")) {
154
+ message = this.engine.parseAndRenderSync(schema.description, ret);
155
+ } else {
156
+ message = schema.description ?? key;
157
+ }
158
+ let defaultValue = schema.default;
159
+ if (typeof schema.default === "string" && schema.default.includes("{{")) {
160
+ defaultValue = this.engine.parseAndRenderSync(schema.default, ret);
161
+ }
162
+ ret[key] = await resolver.resolve(key, schema, message, defaultValue);
163
+ }
164
+ }
165
+ return ret;
166
+ }
167
+ }
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Declares a single property a spawn needs to render, as found in a
3
+ * spawn.json propertySchema. Descriptions and defaults may contain liquid
4
+ * expressions, which are rendered against the context resolved so far before
5
+ * the property is requested from a {@link PropertyResolver}.
6
+ */
7
+ interface PropertySchema {
8
+ type?: "string" | "number" | "integer" | "boolean";
9
+ description?: string;
10
+ default?: string | number | boolean;
11
+ enum?: string[];
12
+ }
13
+ /**
14
+ * Supplies values for properties declared in a spawn's propertySchema that are
15
+ * absent from the render context. An interactive host can prompt the user; a
16
+ * non-interactive host can omit the resolver entirely, in which case rendering
17
+ * fails on the first missing property.
18
+ */
19
+ interface PropertyResolver {
20
+ /**
21
+ * Returns the value to use for {@code key}.
22
+ *
23
+ * @param key the property name as declared in the propertySchema
24
+ * @param schema the declaration for the property (type, enum, etc.)
25
+ * @param message the property description with any liquid expressions already
26
+ * rendered against the context resolved so far, or the key when the
27
+ * schema has no description
28
+ * @param defaultValue the schema default with any liquid expressions already
29
+ * rendered, or undefined when the schema has no default
30
+ */
31
+ resolve(key: string, schema: PropertySchema, message: string, defaultValue?: unknown): Promise<unknown>;
32
+ }
33
+ /**
34
+ * The files of a spawn or of a rendered result, keyed by POSIX-style relative
35
+ * path (no leading slash). Text files are strings; binary files are
36
+ * {@link Uint8Array} and are copied verbatim, never rendered.
37
+ */
38
+ type SpawnTree = Record<string, string | Uint8Array>;
39
+ /**
40
+ * Options for {@link SpawnEngine#renderSpawn}.
41
+ */
42
+ interface RenderSpawnOptions {
43
+ /**
44
+ * Values made available to the templates. Entries here override globals
45
+ * declared in spawn.json and are never re-requested from the
46
+ * {@link PropertyResolver}.
47
+ */
48
+ context?: Record<string, unknown>;
49
+ /**
50
+ * Supplies values for propertySchema entries missing from the context.
51
+ * When omitted, a missing property fails the render.
52
+ */
53
+ propertyResolver?: PropertyResolver;
54
+ /**
55
+ * Loads the spawn referenced by a spawn.json {@code inherits} value. The ref
56
+ * is passed verbatim; resolving it (e.g. against the location of the spawn
57
+ * that declared it) is the host's responsibility. Inheritance chains are
58
+ * linear, so refs are requested in declaration order: each call refers to the
59
+ * spawn loaded by the previous one. Required when the spawn uses inheritance.
60
+ */
61
+ loadInherited?: (ref: string) => Promise<SpawnTree>;
62
+ }
63
+ /**
64
+ * The outcome of rendering a spawn.
65
+ */
66
+ interface SpawnRenderResult {
67
+ /**
68
+ * The rendered files keyed by destination path: liquid expressions in paths
69
+ * are rendered, {@code .liquid} suffixes are stripped, and files from derived
70
+ * spawns overwrite same-destination files from inherited spawns.
71
+ */
72
+ files: SpawnTree;
73
+ /**
74
+ * The full context the templates were rendered with: merged globals,
75
+ * caller-provided values, and any values supplied by the property resolver.
76
+ */
77
+ context: Record<string, unknown>;
78
+ }
79
+ /**
80
+ * Renders a Spawn: a file tree containing liquid templates, an optional
81
+ * spawn.json, and liquid expressions in file paths. The engine is
82
+ * host-agnostic — it operates on in-memory {@link SpawnTree}s and performs no
83
+ * IO, so the same implementation runs in Node and embedded in the JVM.
84
+ *
85
+ * Rendering applies the following rules:
86
+ *
87
+ * - spawn.json declares globals, a propertySchema, and an optional inherited
88
+ * spawn; inheritance chains are followed via
89
+ * {@link RenderSpawnOptions#loadInherited}
90
+ * - globals and propertySchema entries from derived spawns override inherited
91
+ * ones; values supplied in the context override globals
92
+ * - propertySchema entries missing from the context are obtained from the
93
+ * {@link PropertyResolver}, or fail the render when no resolver is given
94
+ * - paths containing liquid expressions are rendered; files ending in
95
+ * {@code .liquid} have their content rendered and the suffix stripped
96
+ * - files from derived spawns overwrite same-destination files from inherited
97
+ * spawns
98
+ */
99
+ declare class SpawnEngine {
100
+ private engine;
101
+ constructor();
102
+ /**
103
+ * Renders the given spawn and returns the rendered files along with the full
104
+ * context used, including values added by the property resolver.
105
+ */
106
+ renderSpawn(spawn: SpawnTree, options?: RenderSpawnOptions): Promise<SpawnRenderResult>;
107
+ private parseConfig;
108
+ private resolveMissingProperties;
109
+ }
110
+ export { SpawnTree, SpawnRenderResult, SpawnEngine, RenderSpawnOptions, PropertySchema, PropertyResolver };
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Declares a single property a spawn needs to render, as found in a
3
+ * spawn.json propertySchema. Descriptions and defaults may contain liquid
4
+ * expressions, which are rendered against the context resolved so far before
5
+ * the property is requested from a {@link PropertyResolver}.
6
+ */
7
+ interface PropertySchema {
8
+ type?: "string" | "number" | "integer" | "boolean";
9
+ description?: string;
10
+ default?: string | number | boolean;
11
+ enum?: string[];
12
+ }
13
+ /**
14
+ * Supplies values for properties declared in a spawn's propertySchema that are
15
+ * absent from the render context. An interactive host can prompt the user; a
16
+ * non-interactive host can omit the resolver entirely, in which case rendering
17
+ * fails on the first missing property.
18
+ */
19
+ interface PropertyResolver {
20
+ /**
21
+ * Returns the value to use for {@code key}.
22
+ *
23
+ * @param key the property name as declared in the propertySchema
24
+ * @param schema the declaration for the property (type, enum, etc.)
25
+ * @param message the property description with any liquid expressions already
26
+ * rendered against the context resolved so far, or the key when the
27
+ * schema has no description
28
+ * @param defaultValue the schema default with any liquid expressions already
29
+ * rendered, or undefined when the schema has no default
30
+ */
31
+ resolve(key: string, schema: PropertySchema, message: string, defaultValue?: unknown): Promise<unknown>;
32
+ }
33
+ /**
34
+ * The files of a spawn or of a rendered result, keyed by POSIX-style relative
35
+ * path (no leading slash). Text files are strings; binary files are
36
+ * {@link Uint8Array} and are copied verbatim, never rendered.
37
+ */
38
+ type SpawnTree = Record<string, string | Uint8Array>;
39
+ /**
40
+ * Options for {@link SpawnEngine#renderSpawn}.
41
+ */
42
+ interface RenderSpawnOptions {
43
+ /**
44
+ * Values made available to the templates. Entries here override globals
45
+ * declared in spawn.json and are never re-requested from the
46
+ * {@link PropertyResolver}.
47
+ */
48
+ context?: Record<string, unknown>;
49
+ /**
50
+ * Supplies values for propertySchema entries missing from the context.
51
+ * When omitted, a missing property fails the render.
52
+ */
53
+ propertyResolver?: PropertyResolver;
54
+ /**
55
+ * Loads the spawn referenced by a spawn.json {@code inherits} value. The ref
56
+ * is passed verbatim; resolving it (e.g. against the location of the spawn
57
+ * that declared it) is the host's responsibility. Inheritance chains are
58
+ * linear, so refs are requested in declaration order: each call refers to the
59
+ * spawn loaded by the previous one. Required when the spawn uses inheritance.
60
+ */
61
+ loadInherited?: (ref: string) => Promise<SpawnTree>;
62
+ }
63
+ /**
64
+ * The outcome of rendering a spawn.
65
+ */
66
+ interface SpawnRenderResult {
67
+ /**
68
+ * The rendered files keyed by destination path: liquid expressions in paths
69
+ * are rendered, {@code .liquid} suffixes are stripped, and files from derived
70
+ * spawns overwrite same-destination files from inherited spawns.
71
+ */
72
+ files: SpawnTree;
73
+ /**
74
+ * The full context the templates were rendered with: merged globals,
75
+ * caller-provided values, and any values supplied by the property resolver.
76
+ */
77
+ context: Record<string, unknown>;
78
+ }
79
+ /**
80
+ * Renders a Spawn: a file tree containing liquid templates, an optional
81
+ * spawn.json, and liquid expressions in file paths. The engine is
82
+ * host-agnostic — it operates on in-memory {@link SpawnTree}s and performs no
83
+ * IO, so the same implementation runs in Node and embedded in the JVM.
84
+ *
85
+ * Rendering applies the following rules:
86
+ *
87
+ * - spawn.json declares globals, a propertySchema, and an optional inherited
88
+ * spawn; inheritance chains are followed via
89
+ * {@link RenderSpawnOptions#loadInherited}
90
+ * - globals and propertySchema entries from derived spawns override inherited
91
+ * ones; values supplied in the context override globals
92
+ * - propertySchema entries missing from the context are obtained from the
93
+ * {@link PropertyResolver}, or fail the render when no resolver is given
94
+ * - paths containing liquid expressions are rendered; files ending in
95
+ * {@code .liquid} have their content rendered and the suffix stripped
96
+ * - files from derived spawns overwrite same-destination files from inherited
97
+ * spawns
98
+ */
99
+ declare class SpawnEngine {
100
+ private engine;
101
+ constructor();
102
+ /**
103
+ * Renders the given spawn and returns the rendered files along with the full
104
+ * context used, including values added by the property resolver.
105
+ */
106
+ renderSpawn(spawn: SpawnTree, options?: RenderSpawnOptions): Promise<SpawnRenderResult>;
107
+ private parseConfig;
108
+ private resolveMissingProperties;
109
+ }
110
+ export { SpawnTree, SpawnRenderResult, SpawnEngine, RenderSpawnOptions, PropertySchema, PropertyResolver };
package/dist/index.js ADDED
@@ -0,0 +1,124 @@
1
+ // packages/spawn/src/api/SpawnEngine.ts
2
+ import { Liquid } from "liquidjs";
3
+ import { z } from "zod";
4
+ var PropertySchemaSchema = z.object({
5
+ type: z.enum(["string", "number", "integer", "boolean"]).optional(),
6
+ description: z.string().optional(),
7
+ default: z.union([z.string(), z.number(), z.boolean()]).optional(),
8
+ enum: z.array(z.string()).optional()
9
+ });
10
+ var SpawnConfigSchema = z.object({
11
+ inherits: z.string().optional(),
12
+ globals: z.record(z.string(), z.unknown()).optional(),
13
+ propertySchema: z.record(z.string(), PropertySchemaSchema).optional()
14
+ });
15
+ var IGNORED_FILE_NAMES = ["spawn.json", ".DS_Store"];
16
+ function upperFirst(s) {
17
+ return s.charAt(0).toUpperCase() + s.slice(1);
18
+ }
19
+ function camelCase(s) {
20
+ return s.replace(/[-_\s]+(.)/g, (_, c) => c.toUpperCase()).replace(/^(.)/, (_, c) => c.toLowerCase());
21
+ }
22
+
23
+ class SpawnEngine {
24
+ engine;
25
+ constructor() {
26
+ this.engine = new Liquid({ cache: true });
27
+ this.engine.registerFilter("packageToPath", (v) => v.replaceAll(".", "/"));
28
+ this.engine.registerFilter("encodePackage", (v) => {
29
+ v = v.replaceAll("-", "_");
30
+ v = v.replace(/\.(\d+)/g, "._$1");
31
+ return v;
32
+ });
33
+ this.engine.registerFilter("camelCase", (v) => camelCase(v));
34
+ this.engine.registerFilter("upperFirst", (v) => upperFirst(v));
35
+ }
36
+ async renderSpawn(spawn, options) {
37
+ const trees = [spawn];
38
+ const configs = [];
39
+ let currentConfig = this.parseConfig(spawn);
40
+ if (currentConfig) {
41
+ configs.push(currentConfig);
42
+ }
43
+ while (currentConfig?.inherits) {
44
+ if (!options?.loadInherited) {
45
+ throw new Error(`Spawn inherits '${currentConfig.inherits}' but no loadInherited callback was provided`);
46
+ }
47
+ const inherited = await options.loadInherited(currentConfig.inherits);
48
+ trees.push(inherited);
49
+ currentConfig = this.parseConfig(inherited);
50
+ if (currentConfig) {
51
+ configs.push(currentConfig);
52
+ }
53
+ }
54
+ let globals = {};
55
+ let propertySchemas = {};
56
+ for (const config of [...configs].reverse()) {
57
+ if (config.globals) {
58
+ globals = { ...globals, ...config.globals };
59
+ }
60
+ if (config.propertySchema) {
61
+ propertySchemas = { ...propertySchemas, ...config.propertySchema };
62
+ }
63
+ }
64
+ let context = { ...globals, ...options?.context };
65
+ context = await this.resolveMissingProperties(propertySchemas, context, options?.propertyResolver);
66
+ const files = {};
67
+ for (const tree of [...trees].reverse()) {
68
+ for (const source of Object.keys(tree).sort()) {
69
+ const fileName = source.substring(source.lastIndexOf("/") + 1);
70
+ if (IGNORED_FILE_NAMES.includes(fileName)) {
71
+ continue;
72
+ }
73
+ let destination = source;
74
+ if (destination.includes("{{")) {
75
+ destination = await this.engine.parseAndRender(destination, context);
76
+ }
77
+ let content = tree[source];
78
+ if (destination.endsWith(".liquid")) {
79
+ destination = destination.substring(0, destination.length - 7);
80
+ if (typeof content !== "string") {
81
+ throw new Error(`Template ${source} must contain text content`);
82
+ }
83
+ content = await this.engine.parseAndRender(content, context);
84
+ }
85
+ files[destination] = content;
86
+ }
87
+ }
88
+ return { files, context };
89
+ }
90
+ parseConfig(tree) {
91
+ const raw = tree["spawn.json"];
92
+ if (raw === undefined) {
93
+ return;
94
+ }
95
+ const text = typeof raw === "string" ? raw : new TextDecoder().decode(raw);
96
+ return SpawnConfigSchema.parse(JSON.parse(text));
97
+ }
98
+ async resolveMissingProperties(propertySchemas, context, resolver) {
99
+ const ret = { ...context };
100
+ for (const key in propertySchemas) {
101
+ if (!Object.prototype.hasOwnProperty.call(ret, key)) {
102
+ if (!resolver) {
103
+ throw new Error(`No value provided for required property '${key}'`);
104
+ }
105
+ const schema = propertySchemas[key];
106
+ let message;
107
+ if (schema.description?.includes("{{")) {
108
+ message = this.engine.parseAndRenderSync(schema.description, ret);
109
+ } else {
110
+ message = schema.description ?? key;
111
+ }
112
+ let defaultValue = schema.default;
113
+ if (typeof schema.default === "string" && schema.default.includes("{{")) {
114
+ defaultValue = this.engine.parseAndRenderSync(schema.default, ret);
115
+ }
116
+ ret[key] = await resolver.resolve(key, schema, message, defaultValue);
117
+ }
118
+ }
119
+ return ret;
120
+ }
121
+ }
122
+ export {
123
+ SpawnEngine
124
+ };
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@kinotic-ai/spawn",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "files": [
6
+ "dist"
7
+ ],
8
+ "module": "./dist/index.js",
9
+ "main": "./dist/index.cjs",
10
+ "types": "./dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "import": {
14
+ "types": "./dist/index.d.ts",
15
+ "default": "./dist/index.js"
16
+ },
17
+ "require": {
18
+ "types": "./dist/index.d.cts",
19
+ "default": "./dist/index.cjs"
20
+ }
21
+ },
22
+ "./package.json": "./package.json"
23
+ },
24
+ "homepage": "https://github.com/kinotic-ai/kinotic",
25
+ "license": "Elastic License 2.0",
26
+ "publishConfig": {
27
+ "access": "public"
28
+ },
29
+ "scripts": {
30
+ "type-check": "tsc --noEmit",
31
+ "test": "vitest run",
32
+ "coverage": "vitest run --coverage"
33
+ },
34
+ "dependencies": {
35
+ "liquidjs": "10.25.7",
36
+ "zod": "^4.3.6"
37
+ },
38
+ "devDependencies": {
39
+ "@types/node": "^25.3.2",
40
+ "@vitest/coverage-v8": "^4.0.18",
41
+ "typescript": "^5.9.3",
42
+ "vitest": "^4.0.18"
43
+ },
44
+ "peerDependenciesMeta": {
45
+ "typescript": {
46
+ "optional": true
47
+ }
48
+ }
49
+ }