@formspec/cli 0.1.0-alpha.3

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.
Files changed (66) hide show
  1. package/README.md +314 -0
  2. package/dist/__tests__/analyzer.test.d.ts +5 -0
  3. package/dist/__tests__/analyzer.test.d.ts.map +1 -0
  4. package/dist/__tests__/analyzer.test.js +141 -0
  5. package/dist/__tests__/analyzer.test.js.map +1 -0
  6. package/dist/__tests__/codegen.test.d.ts +5 -0
  7. package/dist/__tests__/codegen.test.d.ts.map +1 -0
  8. package/dist/__tests__/codegen.test.js +482 -0
  9. package/dist/__tests__/codegen.test.js.map +1 -0
  10. package/dist/__tests__/edge-cases.test.d.ts +14 -0
  11. package/dist/__tests__/edge-cases.test.d.ts.map +1 -0
  12. package/dist/__tests__/edge-cases.test.js +432 -0
  13. package/dist/__tests__/edge-cases.test.js.map +1 -0
  14. package/dist/__tests__/fixtures/edge-cases.d.ts +110 -0
  15. package/dist/__tests__/fixtures/edge-cases.d.ts.map +1 -0
  16. package/dist/__tests__/fixtures/edge-cases.js +135 -0
  17. package/dist/__tests__/fixtures/edge-cases.js.map +1 -0
  18. package/dist/__tests__/fixtures/sample-forms.d.ts +55 -0
  19. package/dist/__tests__/fixtures/sample-forms.d.ts.map +1 -0
  20. package/dist/__tests__/fixtures/sample-forms.js +78 -0
  21. package/dist/__tests__/fixtures/sample-forms.js.map +1 -0
  22. package/dist/__tests__/integration.test.d.ts +5 -0
  23. package/dist/__tests__/integration.test.d.ts.map +1 -0
  24. package/dist/__tests__/integration.test.js +159 -0
  25. package/dist/__tests__/integration.test.js.map +1 -0
  26. package/dist/analyzer/class-analyzer.d.ts +75 -0
  27. package/dist/analyzer/class-analyzer.d.ts.map +1 -0
  28. package/dist/analyzer/class-analyzer.js +151 -0
  29. package/dist/analyzer/class-analyzer.js.map +1 -0
  30. package/dist/analyzer/decorator-extractor.d.ts +87 -0
  31. package/dist/analyzer/decorator-extractor.d.ts.map +1 -0
  32. package/dist/analyzer/decorator-extractor.js +193 -0
  33. package/dist/analyzer/decorator-extractor.js.map +1 -0
  34. package/dist/analyzer/program.d.ts +37 -0
  35. package/dist/analyzer/program.d.ts.map +1 -0
  36. package/dist/analyzer/program.js +89 -0
  37. package/dist/analyzer/program.js.map +1 -0
  38. package/dist/analyzer/type-converter.d.ts +97 -0
  39. package/dist/analyzer/type-converter.d.ts.map +1 -0
  40. package/dist/analyzer/type-converter.js +353 -0
  41. package/dist/analyzer/type-converter.js.map +1 -0
  42. package/dist/codegen/index.d.ts +74 -0
  43. package/dist/codegen/index.d.ts.map +1 -0
  44. package/dist/codegen/index.js +501 -0
  45. package/dist/codegen/index.js.map +1 -0
  46. package/dist/generators/class-schema.d.ts +43 -0
  47. package/dist/generators/class-schema.d.ts.map +1 -0
  48. package/dist/generators/class-schema.js +61 -0
  49. package/dist/generators/class-schema.js.map +1 -0
  50. package/dist/generators/method-schema.d.ts +57 -0
  51. package/dist/generators/method-schema.d.ts.map +1 -0
  52. package/dist/generators/method-schema.js +108 -0
  53. package/dist/generators/method-schema.js.map +1 -0
  54. package/dist/index.d.ts +19 -0
  55. package/dist/index.d.ts.map +1 -0
  56. package/dist/index.js +282 -0
  57. package/dist/index.js.map +1 -0
  58. package/dist/output/writer.d.ts +82 -0
  59. package/dist/output/writer.d.ts.map +1 -0
  60. package/dist/output/writer.js +152 -0
  61. package/dist/output/writer.js.map +1 -0
  62. package/dist/runtime/formspec-loader.d.ts +80 -0
  63. package/dist/runtime/formspec-loader.d.ts.map +1 -0
  64. package/dist/runtime/formspec-loader.js +154 -0
  65. package/dist/runtime/formspec-loader.js.map +1 -0
  66. package/package.json +46 -0
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Output writer for creating the schema folder structure.
3
+ *
4
+ * Creates directories and writes JSON files for:
5
+ * - Class schema and UI Schema
6
+ * - Instance method schemas
7
+ * - Static method schemas
8
+ * - Standalone FormSpec exports (chain DSL)
9
+ */
10
+ import * as fs from "node:fs";
11
+ import * as path from "node:path";
12
+ /**
13
+ * Writes all schemas for a class to the output directory.
14
+ *
15
+ * Creates the following structure:
16
+ * ```
17
+ * {outDir}/{className}/
18
+ * ├── schema.json
19
+ * ├── ux_spec.json
20
+ * ├── instance_methods/
21
+ * │ └── {methodName}/
22
+ * │ ├── params.schema.json
23
+ * │ ├── params.ux_spec.json (if FormSpec-based)
24
+ * │ └── return_type.schema.json
25
+ * └── static_methods/
26
+ * └── {methodName}/
27
+ * └── ...
28
+ * ```
29
+ *
30
+ * @param className - Name of the class
31
+ * @param classSchemas - Generated class schemas
32
+ * @param instanceMethods - Instance method schemas
33
+ * @param staticMethods - Static method schemas
34
+ * @param options - Write options
35
+ * @returns Write result with paths
36
+ */
37
+ export function writeClassSchemas(className, classSchemas, instanceMethods, staticMethods, options) {
38
+ const { outDir, indent = 2 } = options;
39
+ const classDir = path.join(outDir, className);
40
+ const files = [];
41
+ // Ensure class directory exists
42
+ ensureDir(classDir);
43
+ // Write class schema
44
+ const schemaPath = path.join(classDir, "schema.json");
45
+ writeJson(schemaPath, classSchemas.jsonSchema, indent);
46
+ files.push(schemaPath);
47
+ // Write class UI Schema
48
+ const uxSpecPath = path.join(classDir, "ux_spec.json");
49
+ writeJson(uxSpecPath, classSchemas.uxSpec, indent);
50
+ files.push(uxSpecPath);
51
+ // Write instance methods
52
+ if (instanceMethods.length > 0) {
53
+ const instanceDir = path.join(classDir, "instance_methods");
54
+ ensureDir(instanceDir);
55
+ for (const method of instanceMethods) {
56
+ const methodFiles = writeMethodSchemas(method, instanceDir, indent);
57
+ files.push(...methodFiles);
58
+ }
59
+ }
60
+ // Write static methods
61
+ if (staticMethods.length > 0) {
62
+ const staticDir = path.join(classDir, "static_methods");
63
+ ensureDir(staticDir);
64
+ for (const method of staticMethods) {
65
+ const methodFiles = writeMethodSchemas(method, staticDir, indent);
66
+ files.push(...methodFiles);
67
+ }
68
+ }
69
+ return { dir: classDir, files };
70
+ }
71
+ /**
72
+ * Writes schemas for a single method.
73
+ *
74
+ * @param method - Method schemas
75
+ * @param parentDir - Parent directory (instance_methods or static_methods)
76
+ * @param indent - JSON indentation
77
+ * @returns Array of written file paths
78
+ */
79
+ function writeMethodSchemas(method, parentDir, indent) {
80
+ const methodDir = path.join(parentDir, method.name);
81
+ ensureDir(methodDir);
82
+ const files = [];
83
+ // Write params schema
84
+ if (method.params) {
85
+ const paramsSchemaPath = path.join(methodDir, "params.schema.json");
86
+ writeJson(paramsSchemaPath, method.params.jsonSchema, indent);
87
+ files.push(paramsSchemaPath);
88
+ // Write params UI Schema if available (from FormSpec)
89
+ if (method.params.uxSpec) {
90
+ const paramsUxPath = path.join(methodDir, "params.ux_spec.json");
91
+ writeJson(paramsUxPath, method.params.uxSpec, indent);
92
+ files.push(paramsUxPath);
93
+ }
94
+ }
95
+ // Write return type schema
96
+ const returnPath = path.join(methodDir, "return_type.schema.json");
97
+ writeJson(returnPath, method.returnType, indent);
98
+ files.push(returnPath);
99
+ return files;
100
+ }
101
+ /**
102
+ * Writes standalone FormSpec schemas (chain DSL exports).
103
+ *
104
+ * Creates the following structure:
105
+ * ```
106
+ * {outDir}/formspecs/
107
+ * └── {exportName}/
108
+ * ├── schema.json
109
+ * └── ux_spec.json
110
+ * ```
111
+ *
112
+ * @param formSpecs - Map of FormSpec export names to their schemas
113
+ * @param options - Write options
114
+ * @returns Write result with paths
115
+ */
116
+ export function writeFormSpecSchemas(formSpecs, options) {
117
+ const { outDir, indent = 2 } = options;
118
+ const formspecsDir = path.join(outDir, "formspecs");
119
+ const files = [];
120
+ if (formSpecs.size === 0) {
121
+ return { dir: formspecsDir, files };
122
+ }
123
+ ensureDir(formspecsDir);
124
+ for (const [name, schemas] of formSpecs) {
125
+ const exportDir = path.join(formspecsDir, name);
126
+ ensureDir(exportDir);
127
+ // Write JSON Schema
128
+ const schemaPath = path.join(exportDir, "schema.json");
129
+ writeJson(schemaPath, schemas.jsonSchema, indent);
130
+ files.push(schemaPath);
131
+ // Write UI Schema
132
+ const uxSpecPath = path.join(exportDir, "ux_spec.json");
133
+ writeJson(uxSpecPath, schemas.uiSchema, indent);
134
+ files.push(uxSpecPath);
135
+ }
136
+ return { dir: formspecsDir, files };
137
+ }
138
+ /**
139
+ * Ensures a directory exists, creating it if necessary.
140
+ */
141
+ function ensureDir(dir) {
142
+ if (!fs.existsSync(dir)) {
143
+ fs.mkdirSync(dir, { recursive: true });
144
+ }
145
+ }
146
+ /**
147
+ * Writes a JSON object to a file.
148
+ */
149
+ function writeJson(filePath, data, indent) {
150
+ fs.writeFileSync(filePath, JSON.stringify(data, null, indent) + "\n");
151
+ }
152
+ //# sourceMappingURL=writer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"writer.js","sourceRoot":"","sources":["../../src/output/writer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * FormSpec loader for dynamic import and runtime access.
3
+ *
4
+ * Loads compiled TypeScript modules and extracts:
5
+ * - Exported FormSpec constants (chain DSL)
6
+ * - Specific exports by name (for method parameters)
7
+ *
8
+ * Uses the @formspec/build package to generate schemas from FormSpec objects.
9
+ */
10
+ import type { JSONSchema7, UISchema } from "@formspec/build";
11
+ /**
12
+ * A FormSpec object (duck-typed for runtime detection).
13
+ */
14
+ interface FormSpecLike {
15
+ elements: unknown[];
16
+ }
17
+ /**
18
+ * Result of loading and generating schemas from a FormSpec.
19
+ */
20
+ export interface FormSpecSchemas {
21
+ /** The FormSpec export name */
22
+ name: string;
23
+ /** Generated JSON Schema */
24
+ jsonSchema: JSONSchema7;
25
+ /** Generated UI Schema (FormSpec/JSON Forms) */
26
+ uiSchema: UISchema;
27
+ }
28
+ /**
29
+ * Result of loading all FormSpecs from a module.
30
+ */
31
+ export interface ModuleFormSpecs {
32
+ /** All FormSpec exports found in the module */
33
+ formSpecs: Map<string, FormSpecSchemas>;
34
+ /** The raw module for accessing other exports */
35
+ module: Record<string, unknown>;
36
+ }
37
+ /**
38
+ * Checks if a value is a FormSpec object.
39
+ *
40
+ * Uses duck typing since the actual FormSpec type may come from
41
+ * different package versions.
42
+ */
43
+ export declare function isFormSpec(value: unknown): value is FormSpecLike;
44
+ /**
45
+ * Loads a module and extracts all FormSpec exports.
46
+ *
47
+ * This handles both:
48
+ * - Chain DSL: `export const MyForm = formspec(...)`
49
+ * - Method parameters: exports referenced by `InferSchema<typeof X>`
50
+ *
51
+ * @param filePath - Path to the compiled JavaScript file
52
+ * @returns Map of export names to their generated schemas
53
+ */
54
+ export declare function loadFormSpecs(filePath: string): Promise<ModuleFormSpecs>;
55
+ /**
56
+ * Loads specific FormSpec exports by name.
57
+ *
58
+ * Use this when you know which exports to load (e.g., from static analysis
59
+ * of `InferSchema<typeof X>` patterns in method parameters).
60
+ *
61
+ * @param filePath - Path to the compiled JavaScript file
62
+ * @param exportNames - Names of exports to load
63
+ * @returns Map of found FormSpecs with their schemas
64
+ */
65
+ export declare function loadNamedFormSpecs(filePath: string, exportNames: string[]): Promise<Map<string, FormSpecSchemas>>;
66
+ /**
67
+ * Resolves the compiled JS path from a TS source path.
68
+ *
69
+ * This handles common TypeScript output patterns:
70
+ * - ./src/*.ts → ./dist/*.js
71
+ * - ./src/*.ts → ./build/*.js
72
+ * - ./src/*.ts → ./src/*.js (in-place compilation)
73
+ *
74
+ * @param tsPath - Path to the TypeScript source file
75
+ * @param outDir - Optional explicit output directory
76
+ * @returns Path to the expected compiled JS file
77
+ */
78
+ export declare function resolveCompiledPath(tsPath: string, outDir?: string): string;
79
+ export {};
80
+ //# sourceMappingURL=formspec-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formspec-loader.d.ts","sourceRoot":"","sources":["../../src/runtime/formspec-loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAI7D;;GAEG;AACH,UAAU,YAAY;IACpB,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,4BAA4B;IAC5B,UAAU,EAAE,WAAW,CAAC;IACxB,gDAAgD;IAChD,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,+CAA+C;IAC/C,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IACxC,iDAAiD;IACjD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,YAAY,CAoBhE;AAED;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAoC9E;AAED;;;;;;;;;GASG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EAAE,GACpB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,CAmCvC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAiC3E"}
@@ -0,0 +1,154 @@
1
+ /**
2
+ * FormSpec loader for dynamic import and runtime access.
3
+ *
4
+ * Loads compiled TypeScript modules and extracts:
5
+ * - Exported FormSpec constants (chain DSL)
6
+ * - Specific exports by name (for method parameters)
7
+ *
8
+ * Uses the @formspec/build package to generate schemas from FormSpec objects.
9
+ */
10
+ import { generateJsonSchema, generateUiSchema } from "@formspec/build";
11
+ import * as path from "node:path";
12
+ import { pathToFileURL } from "node:url";
13
+ /**
14
+ * Checks if a value is a FormSpec object.
15
+ *
16
+ * Uses duck typing since the actual FormSpec type may come from
17
+ * different package versions.
18
+ */
19
+ export function isFormSpec(value) {
20
+ if (value === null || typeof value !== "object") {
21
+ return false;
22
+ }
23
+ const obj = value;
24
+ // FormSpec objects have an 'elements' array property
25
+ const elements = obj["elements"];
26
+ if (!Array.isArray(elements)) {
27
+ return false;
28
+ }
29
+ // Each element should have a _type property with a string value
30
+ return elements.every((el) => el !== null &&
31
+ typeof el === "object" &&
32
+ typeof el._type === "string");
33
+ }
34
+ /**
35
+ * Loads a module and extracts all FormSpec exports.
36
+ *
37
+ * This handles both:
38
+ * - Chain DSL: `export const MyForm = formspec(...)`
39
+ * - Method parameters: exports referenced by `InferSchema<typeof X>`
40
+ *
41
+ * @param filePath - Path to the compiled JavaScript file
42
+ * @returns Map of export names to their generated schemas
43
+ */
44
+ export async function loadFormSpecs(filePath) {
45
+ const absolutePath = path.resolve(filePath);
46
+ // Convert to file URL for ESM import
47
+ const fileUrl = pathToFileURL(absolutePath).href;
48
+ // Dynamic import
49
+ const module = (await import(fileUrl));
50
+ const formSpecs = new Map();
51
+ // Find all FormSpec exports
52
+ for (const [name, value] of Object.entries(module)) {
53
+ if (isFormSpec(value)) {
54
+ try {
55
+ // Use @formspec/build generators
56
+ // The types expect FormSpec<readonly FormElement[]>
57
+ // but we're duck-typing at runtime
58
+ const jsonSchema = generateJsonSchema(value);
59
+ const uiSchema = generateUiSchema(value);
60
+ formSpecs.set(name, {
61
+ name,
62
+ jsonSchema,
63
+ uiSchema,
64
+ });
65
+ }
66
+ catch (error) {
67
+ console.warn(`Warning: Failed to generate schemas for export "${name}":`, error instanceof Error ? error.message : error);
68
+ }
69
+ }
70
+ }
71
+ return { formSpecs, module };
72
+ }
73
+ /**
74
+ * Loads specific FormSpec exports by name.
75
+ *
76
+ * Use this when you know which exports to load (e.g., from static analysis
77
+ * of `InferSchema<typeof X>` patterns in method parameters).
78
+ *
79
+ * @param filePath - Path to the compiled JavaScript file
80
+ * @param exportNames - Names of exports to load
81
+ * @returns Map of found FormSpecs with their schemas
82
+ */
83
+ export async function loadNamedFormSpecs(filePath, exportNames) {
84
+ const { formSpecs, module } = await loadFormSpecs(filePath);
85
+ const result = new Map();
86
+ for (const name of exportNames) {
87
+ // Check if we already have it from the full scan
88
+ const existing = formSpecs.get(name);
89
+ if (existing) {
90
+ result.set(name, existing);
91
+ continue;
92
+ }
93
+ // Try to access it directly from the module
94
+ const value = module[name];
95
+ if (value && isFormSpec(value)) {
96
+ try {
97
+ const jsonSchema = generateJsonSchema(value);
98
+ const uiSchema = generateUiSchema(value);
99
+ result.set(name, { name, jsonSchema, uiSchema });
100
+ }
101
+ catch (error) {
102
+ console.warn(`Warning: Failed to generate schemas for "${name}":`, error instanceof Error ? error.message : error);
103
+ }
104
+ }
105
+ else if (value) {
106
+ console.warn(`Warning: Export "${name}" exists but is not a valid FormSpec object`);
107
+ }
108
+ else {
109
+ console.warn(`Warning: Export "${name}" not found in module`);
110
+ }
111
+ }
112
+ return result;
113
+ }
114
+ /**
115
+ * Resolves the compiled JS path from a TS source path.
116
+ *
117
+ * This handles common TypeScript output patterns:
118
+ * - ./src/*.ts → ./dist/*.js
119
+ * - ./src/*.ts → ./build/*.js
120
+ * - ./src/*.ts → ./src/*.js (in-place compilation)
121
+ *
122
+ * @param tsPath - Path to the TypeScript source file
123
+ * @param outDir - Optional explicit output directory
124
+ * @returns Path to the expected compiled JS file
125
+ */
126
+ export function resolveCompiledPath(tsPath, outDir) {
127
+ const absolutePath = path.resolve(tsPath);
128
+ const ext = path.extname(absolutePath);
129
+ // Already a JS file
130
+ if (ext === ".js" || ext === ".mjs" || ext === ".cjs") {
131
+ return absolutePath;
132
+ }
133
+ // Replace .ts/.tsx/.mts extension with .js
134
+ const basePath = absolutePath.replace(/\.(ts|tsx|mts)$/, ".js");
135
+ if (outDir) {
136
+ // If outDir is specified, replace src dir with outDir
137
+ const fileName = path.basename(basePath);
138
+ const dirName = path.dirname(absolutePath);
139
+ // Try to find 'src' in the path and replace with outDir
140
+ const srcPattern = path.sep + "src";
141
+ const srcIndex = dirName.lastIndexOf(srcPattern);
142
+ if (srcIndex !== -1) {
143
+ const baseDir = dirName.substring(0, srcIndex);
144
+ const subPath = dirName.substring(srcIndex + srcPattern.length);
145
+ return path.join(baseDir, outDir, subPath, fileName);
146
+ }
147
+ // Fallback: just use outDir relative to file's directory
148
+ return path.join(path.dirname(absolutePath), outDir, fileName);
149
+ }
150
+ // Return basePath (actual existence check happens at import time)
151
+ // Future enhancement: could check fs.existsSync for common patterns
152
+ return basePath;
153
+ }
154
+ //# sourceMappingURL=formspec-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formspec-loader.js","sourceRoot":"","sources":["../../src/runtime/formspec-loader.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@formspec/cli",
3
+ "version": "0.1.0-alpha.3",
4
+ "description": "CLI tool for generating JSON Schema and FormSpec from TypeScript classes",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "bin": {
9
+ "formspec": "./dist/index.js"
10
+ },
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.js"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "dependencies": {
21
+ "typescript": "^5.0.0",
22
+ "@formspec/build": "0.1.0-alpha.2"
23
+ },
24
+ "devDependencies": {
25
+ "vitest": "^3.0.0",
26
+ "@formspec/core": "0.1.0-alpha.2",
27
+ "@formspec/dsl": "0.1.0-alpha.2"
28
+ },
29
+ "publishConfig": {
30
+ "access": "public"
31
+ },
32
+ "license": "UNLICENSED",
33
+ "keywords": [
34
+ "formspec",
35
+ "cli",
36
+ "typescript",
37
+ "json-schema",
38
+ "code-generation"
39
+ ],
40
+ "scripts": {
41
+ "build": "tsc",
42
+ "clean": "rm -rf dist",
43
+ "typecheck": "tsc --noEmit",
44
+ "test": "vitest run"
45
+ }
46
+ }