@sembl/compiler 0.1.0 → 0.2.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/dist/cli/index.js +542 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/{chunk-Y4FKH2ZU.js → index.cjs} +136 -36
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +220 -0
- package/dist/index.d.ts +41 -2
- package/dist/index.js +524 -12
- package/dist/index.js.map +1 -1
- package/package.json +16 -7
- package/dist/chunk-Y4FKH2ZU.js.map +0 -1
package/dist/cli/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/cli/index.ts","../../src/cli/commands/extract.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { extractCommand } from \"./commands/extract.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"sembl\")\n .description(\"SEMBL schema compiler — extract runtime schemas from decorated TypeScript classes\")\n .version(\"0.1.0\");\n\nprogram\n .command(\"extract\")\n .description(\"Extract @Schema-decorated classes into RuntimeSchema files\")\n .requiredOption(\"-i, --input <path>\", \"Input directory containing decorated schema classes\")\n .requiredOption(\"-o, --output <path>\", \"Output directory for generated .schema.ts files\")\n .option(\"--tsconfig <path>\", \"Path to tsconfig.json\")\n .option(\"--strict\", \"Exit non-zero if extraction produced any warnings\")\n .action(async (options) => {\n const result = await extractCommand({\n input: options.input,\n output: options.output,\n tsconfig: options.tsconfig,\n strict: options.strict,\n });\n // Set the code rather than exiting, so buffered stdout/stderr still flush.\n process.exitCode = result.exitCode;\n });\n\nprogram.parse();\n","import { resolve } from \"node:path\";\nimport { glob } from \"glob\";\nimport { extractSchemas } from \"../../extractor/ast-extractor.js\";\nimport { emitSchemas } from \"../../generator/schema-emitter.js\";\n\nexport interface ExtractCommandOptions {\n input: string;\n output: string;\n tsconfig?: string;\n /** Treat extraction warnings as errors. For CI and pre-build hooks. */\n strict?: boolean;\n}\n\n/**\n * What the command did. Returned rather than exited on, so the process owns\n * its exit code in one place and tests can assert without spawning.\n */\nexport interface ExtractCommandResult {\n /** Number of schemas emitted, including any synthesized for inline types. */\n schemaCount: number;\n /** Absolute paths of the files written. */\n emittedFiles: string[];\n /** Warnings raised during extraction, in discovery order. */\n warnings: string[];\n /** 0 on success; 1 on a misconfiguration, or on any warning under --strict. */\n exitCode: number;\n}\n\nexport async function extractCommand(\n options: ExtractCommandOptions,\n): Promise<ExtractCommandResult> {\n const inputDir = resolve(options.input);\n const outputDir = resolve(options.output);\n\n // Find all .ts files in the input directory\n const files = await glob(\"**/*.ts\", {\n cwd: inputDir,\n absolute: true,\n ignore: [\"**/*.d.ts\", \"**/*.schema.ts\", \"**/node_modules/**\"],\n });\n\n if (files.length === 0) {\n // Extract runs ahead of the build, and the build imports the bundle this\n // step writes. Succeeding quietly here just moves the failure to a later,\n // less legible \"cannot find ./generated/index.js\".\n console.error(`sembl extract: no TypeScript files found in ${inputDir}`);\n return { schemaCount: 0, emittedFiles: [], warnings: [], exitCode: 1 };\n }\n\n console.log(`Found ${files.length} source file(s) in ${inputDir}`);\n\n const result = extractSchemas({\n filePatterns: files,\n tsconfigPath: options.tsconfig,\n });\n\n // Warnings go to stderr so they survive a pipeline that captures stdout, and\n // are printed before the summary so the last line is the outcome.\n for (const warning of result.warnings) {\n console.error(`sembl extract: warning: ${warning}`);\n }\n\n const schemaCount = Object.keys(result.schemas).length;\n if (schemaCount === 0) {\n console.error(\n `sembl extract: no @Schema-decorated classes found in ${inputDir}`,\n );\n return {\n schemaCount: 0,\n emittedFiles: [],\n warnings: result.warnings,\n exitCode: 1,\n };\n }\n\n const emitted = emitSchemas(result, outputDir);\n console.log(\n `Extracted ${schemaCount} schema(s), emitted ${emitted.length} file(s) to ${outputDir}`,\n );\n\n const failOnWarnings = options.strict === true && result.warnings.length > 0;\n if (result.warnings.length > 0) {\n console.error(\n `sembl extract: ${result.warnings.length} warning(s).` +\n (failOnWarnings ? \" Failing because --strict is set.\" : \"\"),\n );\n }\n\n return {\n schemaCount,\n emittedFiles: emitted,\n warnings: result.warnings,\n exitCode: failOnWarnings ? 1 : 0,\n };\n}\n"],"mappings":";;;;;;;AAAA,SAAS,eAAe;;;ACAxB,SAAS,eAAe;AACxB,SAAS,YAAY;AA2BrB,eAAsB,eACpB,SAC+B;AAC/B,QAAM,WAAW,QAAQ,QAAQ,KAAK;AACtC,QAAM,YAAY,QAAQ,QAAQ,MAAM;AAGxC,QAAM,QAAQ,MAAM,KAAK,WAAW;AAAA,IAClC,KAAK;AAAA,IACL,UAAU;AAAA,IACV,QAAQ,CAAC,aAAa,kBAAkB,oBAAoB;AAAA,EAC9D,CAAC;AAED,MAAI,MAAM,WAAW,GAAG;AAItB,YAAQ,MAAM,+CAA+C,QAAQ,EAAE;AACvE,WAAO,EAAE,aAAa,GAAG,cAAc,CAAC,GAAG,UAAU,CAAC,GAAG,UAAU,EAAE;AAAA,EACvE;AAEA,UAAQ,IAAI,SAAS,MAAM,MAAM,sBAAsB,QAAQ,EAAE;AAEjE,QAAM,SAAS,eAAe;AAAA,IAC5B,cAAc;AAAA,IACd,cAAc,QAAQ;AAAA,EACxB,CAAC;AAID,aAAW,WAAW,OAAO,UAAU;AACrC,YAAQ,MAAM,2BAA2B,OAAO,EAAE;AAAA,EACpD;AAEA,QAAM,cAAc,OAAO,KAAK,OAAO,OAAO,EAAE;AAChD,MAAI,gBAAgB,GAAG;AACrB,YAAQ;AAAA,MACN,wDAAwD,QAAQ;AAAA,IAClE;AACA,WAAO;AAAA,MACL,aAAa;AAAA,MACb,cAAc,CAAC;AAAA,MACf,UAAU,OAAO;AAAA,MACjB,UAAU;AAAA,IACZ;AAAA,EACF;AAEA,QAAM,UAAU,YAAY,QAAQ,SAAS;AAC7C,UAAQ;AAAA,IACN,aAAa,WAAW,uBAAuB,QAAQ,MAAM,eAAe,SAAS;AAAA,EACvF;AAEA,QAAM,iBAAiB,QAAQ,WAAW,QAAQ,OAAO,SAAS,SAAS;AAC3E,MAAI,OAAO,SAAS,SAAS,GAAG;AAC9B,YAAQ;AAAA,MACN,kBAAkB,OAAO,SAAS,MAAM,kBACrC,iBAAiB,sCAAsC;AAAA,IAC5D;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA,cAAc;AAAA,IACd,UAAU,OAAO;AAAA,IACjB,UAAU,iBAAiB,IAAI;AAAA,EACjC;AACF;;;AD3FA,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,OAAO,EACZ,YAAY,wFAAmF,EAC/F,QAAQ,OAAO;AAElB,QACG,QAAQ,SAAS,EACjB,YAAY,4DAA4D,EACxE,eAAe,sBAAsB,qDAAqD,EAC1F,eAAe,uBAAuB,iDAAiD,EACvF,OAAO,qBAAqB,uBAAuB,EACnD,OAAO,YAAY,mDAAmD,EACtE,OAAO,OAAO,YAAY;AACzB,QAAM,SAAS,MAAM,eAAe;AAAA,IAClC,OAAO,QAAQ;AAAA,IACf,QAAQ,QAAQ;AAAA,IAChB,UAAU,QAAQ;AAAA,IAClB,QAAQ,QAAQ;AAAA,EAClB,CAAC;AAED,UAAQ,WAAW,OAAO;AAC5B,CAAC;AAEH,QAAQ,MAAM;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/cli/index.ts","../../src/cli/commands/extract.ts","../../src/extractor/ast-extractor.ts","../../src/extractor/decorator-parser.ts","../../src/extractor/extraction-context.ts","../../src/extractor/type-resolver.ts","../../src/extractor/class-visitor.ts","../../src/generator/schema-emitter.ts","../../src/cli/commands/eval.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { extractCommand } from \"./commands/extract.js\";\nimport { evalCommand } from \"./commands/eval.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"sembl\")\n .description(\"SEMBL schema compiler — extract runtime schemas from decorated TypeScript classes\")\n .version(\"0.1.0\");\n\nprogram\n .command(\"extract\")\n .description(\"Extract @Schema-decorated classes into RuntimeSchema files\")\n .requiredOption(\"-i, --input <path>\", \"Input directory containing decorated schema classes\")\n .requiredOption(\"-o, --output <path>\", \"Output directory for generated .schema.ts files\")\n .option(\"--tsconfig <path>\", \"Path to tsconfig.json\")\n .option(\"--strict\", \"Exit non-zero if extraction produced any warnings\")\n .action(async (options) => {\n const result = await extractCommand({\n input: options.input,\n output: options.output,\n tsconfig: options.tsconfig,\n strict: options.strict,\n });\n // Set the code rather than exiting, so buffered stdout/stderr still flush.\n process.exitCode = result.exitCode;\n });\n\nprogram\n .command(\"eval\")\n .description(\"Run fixtures through a schema and report per-field precision and recall\")\n .requiredOption(\"-c, --config <path>\", \"JS module exporting { schema, provider, … }\")\n .requiredOption(\"-f, --fixtures <dir>\", \"Directory of fixture JSON files\")\n .option(\"-o, --out <file>\", \"Where to write the report (default: <fixtures>/.sembl-eval/last-run.json)\")\n .option(\"--mode <mode>\", \"coerce or partialCoerce\", \"coerce\")\n .option(\"--provenance\", \"Ask for provenance and show confidence on mismatches\")\n .option(\"--concurrency <n>\", \"Fixtures to run at once\", \"1\")\n .option(\"--replay <dir>\", \"Replay recordings from this directory; record misses through the provider\")\n .option(\"--min-recall <fraction>\", \"Fail when overall recall is below this\")\n .option(\"--min-precision <fraction>\", \"Fail when overall precision is below this\")\n .action(async (options) => {\n const mode = options.mode === \"partialCoerce\" ? \"partialCoerce\" : \"coerce\";\n const result = await evalCommand({\n config: options.config,\n fixtures: options.fixtures,\n out: options.out,\n mode,\n provenance: options.provenance,\n concurrency: Number(options.concurrency),\n replay: options.replay,\n minRecall: options.minRecall !== undefined ? Number(options.minRecall) : undefined,\n minPrecision: options.minPrecision !== undefined ? Number(options.minPrecision) : undefined,\n });\n process.exitCode = result.exitCode;\n });\n\nprogram.parse();\n","import { resolve } from \"node:path\";\nimport { glob } from \"glob\";\nimport { extractSchemas } from \"../../extractor/ast-extractor.js\";\nimport { emitSchemas } from \"../../generator/schema-emitter.js\";\n\nexport interface ExtractCommandOptions {\n input: string;\n output: string;\n tsconfig?: string;\n /** Treat extraction warnings as errors. For CI and pre-build hooks. */\n strict?: boolean;\n}\n\n/**\n * What the command did. Returned rather than exited on, so the process owns\n * its exit code in one place and tests can assert without spawning.\n */\nexport interface ExtractCommandResult {\n /** Number of schemas emitted, including any synthesized for inline types. */\n schemaCount: number;\n /** Absolute paths of the files written. */\n emittedFiles: string[];\n /** Warnings raised during extraction, in discovery order. */\n warnings: string[];\n /** 0 on success; 1 on a misconfiguration, or on any warning under --strict. */\n exitCode: number;\n}\n\nexport async function extractCommand(\n options: ExtractCommandOptions,\n): Promise<ExtractCommandResult> {\n const inputDir = resolve(options.input);\n const outputDir = resolve(options.output);\n\n // Find all .ts files in the input directory\n const files = await glob(\"**/*.ts\", {\n cwd: inputDir,\n absolute: true,\n ignore: [\"**/*.d.ts\", \"**/*.schema.ts\", \"**/node_modules/**\"],\n });\n\n if (files.length === 0) {\n // Extract runs ahead of the build, and the build imports the bundle this\n // step writes. Succeeding quietly here just moves the failure to a later,\n // less legible \"cannot find ./generated/index.js\".\n console.error(`sembl extract: no TypeScript files found in ${inputDir}`);\n return { schemaCount: 0, emittedFiles: [], warnings: [], exitCode: 1 };\n }\n\n console.log(`Found ${files.length} source file(s) in ${inputDir}`);\n\n const result = extractSchemas({\n filePatterns: files,\n tsconfigPath: options.tsconfig,\n });\n\n // Warnings go to stderr so they survive a pipeline that captures stdout, and\n // are printed before the summary so the last line is the outcome.\n for (const warning of result.warnings) {\n console.error(`sembl extract: warning: ${warning}`);\n }\n\n const schemaCount = Object.keys(result.schemas).length;\n if (schemaCount === 0) {\n console.error(\n `sembl extract: no @Schema-decorated classes found in ${inputDir}`,\n );\n return {\n schemaCount: 0,\n emittedFiles: [],\n warnings: result.warnings,\n exitCode: 1,\n };\n }\n\n const emitted = emitSchemas(result, outputDir);\n console.log(\n `Extracted ${schemaCount} schema(s), emitted ${emitted.length} file(s) to ${outputDir}`,\n );\n\n const failOnWarnings = options.strict === true && result.warnings.length > 0;\n if (result.warnings.length > 0) {\n console.error(\n `sembl extract: ${result.warnings.length} warning(s).` +\n (failOnWarnings ? \" Failing because --strict is set.\" : \"\"),\n );\n }\n\n return {\n schemaCount,\n emittedFiles: emitted,\n warnings: result.warnings,\n exitCode: failOnWarnings ? 1 : 0,\n };\n}\n","import { Project, ScriptTarget } from \"ts-morph\";\nimport type { RuntimeSchema } from \"@sembl/core\";\nimport { visitClass } from \"./class-visitor.js\";\nimport { createExtractionContext } from \"./extraction-context.js\";\nimport type { ExtractionResult } from \"../types.js\";\n\nexport interface ExtractOptions {\n /** Glob patterns for source files */\n filePatterns: string[];\n /** Optional tsconfig.json path */\n tsconfigPath?: string;\n}\n\n/**\n * Extract all @Schema-decorated classes from the given source files.\n *\n * Returns the schemas alongside any warnings raised while resolving them —\n * unsupported field types, annotations that could not be read. Extraction\n * never throws on a bad field, so the warnings are the only signal that the\n * emitted schemas are not what the source described.\n */\nexport function extractSchemas(options: ExtractOptions): ExtractionResult {\n const project = new Project({\n tsConfigFilePath: options.tsconfigPath,\n skipAddingFilesFromTsConfig: true,\n compilerOptions: {\n experimentalDecorators: true,\n strict: true,\n // Without a target the default lib is ES5, so anything newer — `Map`,\n // `Set` — resolves to `any` and lands in the unsupported-type warning as\n // \"any\", naming a type the author never wrote.\n target: ScriptTarget.ES2022,\n },\n });\n\n // Add source files matching the patterns\n for (const pattern of options.filePatterns) {\n project.addSourceFilesAtPaths(pattern);\n }\n\n const context = createExtractionContext();\n const schemas: Record<string, RuntimeSchema> = {};\n\n for (const sourceFile of project.getSourceFiles()) {\n for (const classDecl of sourceFile.getClasses()) {\n const schema = visitClass(classDecl, context);\n if (schema) {\n schemas[schema.id] = schema;\n }\n }\n }\n\n // Schemas synthesized for inline object types are emitted like any other, so\n // the prompt builder and the JSON Schema converter can resolve them by id.\n for (const [id, schema] of Object.entries(context.synthesizedSchemas)) {\n schemas[id] = schema;\n }\n\n // Only now is the full set of ids known, so a nested reference to a class\n // declared in a file visited later is not mistaken for a dangling one.\n context.reportUnresolvedNestedSchemas(new Set(Object.keys(schemas)));\n\n return { schemas, warnings: [...context.warnings] };\n}\n","import {\n Node,\n SyntaxKind,\n type ClassDeclaration,\n type PropertyDeclaration,\n type Decorator,\n} from \"ts-morph\";\nimport type { FieldConstraints } from \"@sembl/core\";\nimport type { FieldScope } from \"./extraction-context.js\";\n\n/**\n * Every key `FieldConstraints` allows, with the literal kind its value must be.\n *\n * Typed against `FieldConstraints` on purpose: if core adds or renames a\n * constraint, this table stops compiling instead of silently rejecting the new\n * key as unknown.\n */\nconst CONSTRAINT_KEYS: Record<\n keyof Required<FieldConstraints>,\n \"number\" | \"string\"\n> = {\n maxLength: \"number\",\n minLength: \"number\",\n minimum: \"number\",\n maximum: \"number\",\n minItems: \"number\",\n maxItems: \"number\",\n pattern: \"string\",\n};\n\n/**\n * Extract the string argument from a decorator call expression.\n * e.g. @Schema(\"some description\") → \"some description\"\n */\nfunction getDecoratorStringArg(decorator: Decorator): string | undefined {\n if (!decorator.isDecoratorFactory()) {\n return undefined;\n }\n const args = decorator.getArguments();\n if (args.length === 0) {\n return undefined;\n }\n const arg = args[0];\n // Strip quotes from string literal\n const text = arg.getText();\n if (\n (text.startsWith('\"') && text.endsWith('\"')) ||\n (text.startsWith(\"'\") && text.endsWith(\"'\"))\n ) {\n return text.slice(1, -1);\n }\n // Handle template literals\n if (text.startsWith(\"`\") && text.endsWith(\"`\")) {\n return text.slice(1, -1);\n }\n return undefined;\n}\n\n/**\n * Extract the @Schema description from a class declaration.\n * Returns undefined if the class doesn't have a @Schema decorator.\n */\nexport function parseSchemaDecorator(\n classDecl: ClassDeclaration,\n): string | undefined {\n const decorator = classDecl.getDecorator(\"Schema\");\n if (!decorator) {\n return undefined;\n }\n return getDecoratorStringArg(decorator);\n}\n\n/**\n * Extract the @Describe description from a property declaration.\n * Returns undefined if the property doesn't have a @Describe decorator.\n */\nexport function parseDescribeDecorator(\n propDecl: PropertyDeclaration,\n): string | undefined {\n const decorator = propDecl.getDecorator(\"Describe\");\n if (!decorator) {\n return undefined;\n }\n return getDecoratorStringArg(decorator);\n}\n\n/**\n * Read a number written directly in source.\n *\n * A negative bound is a prefix minus applied to a numeric literal rather than\n * a literal of its own, so it needs unwrapping.\n */\nfunction readNumberLiteral(node: Node): number | undefined {\n if (Node.isNumericLiteral(node)) {\n return node.getLiteralValue();\n }\n if (Node.isPrefixUnaryExpression(node)) {\n const operand = node.getOperand();\n if (Node.isNumericLiteral(operand)) {\n const operator = node.getOperatorToken();\n if (operator === SyntaxKind.MinusToken) {\n return -operand.getLiteralValue();\n }\n if (operator === SyntaxKind.PlusToken) {\n return operand.getLiteralValue();\n }\n }\n }\n return undefined;\n}\n\n/**\n * Read a string written directly in source.\n */\nfunction readStringLiteral(node: Node): string | undefined {\n if (Node.isStringLiteral(node) || Node.isNoSubstitutionTemplateLiteral(node)) {\n return node.getLiteralValue();\n }\n return undefined;\n}\n\n/**\n * Read one `key: value` pair of a @Constrain object literal into `constraints`.\n * Returns true if a value was accepted.\n */\nfunction readConstraintEntry(\n property: Node,\n constraints: FieldConstraints,\n scope: FieldScope,\n): boolean {\n if (!Node.isPropertyAssignment(property)) {\n // A shorthand (`{ maxLength }`), a spread (`{ ...shared }`), or a method\n // all resolve through a binding the compiler never evaluates.\n scope.context.warn(\n scope,\n `@Constrain entry \\`${property.getText()}\\` is not a \\`key: value\\` pair of ` +\n `compile-time constants and cannot be read from source. Skipping it.`,\n );\n return false;\n }\n\n const nameNode = property.getNameNode();\n if (!Node.isIdentifier(nameNode) && !Node.isStringLiteral(nameNode)) {\n scope.context.warn(\n scope,\n `@Constrain key \\`${nameNode.getText()}\\` is computed and cannot be read from source. Skipping it.`,\n );\n return false;\n }\n\n const key = nameNode.getText().replace(/^[\"']|[\"']$/g, \"\");\n if (!Object.prototype.hasOwnProperty.call(CONSTRAINT_KEYS, key)) {\n scope.context.warn(\n scope,\n `@Constrain key \"${key}\" is not a FieldConstraints property. ` +\n `Expected one of: ${Object.keys(CONSTRAINT_KEYS).join(\", \")}. Skipping it.`,\n );\n return false;\n }\n\n const expected = CONSTRAINT_KEYS[key as keyof typeof CONSTRAINT_KEYS];\n const initializer = property.getInitializerOrThrow();\n const value =\n expected === \"number\"\n ? readNumberLiteral(initializer)\n : readStringLiteral(initializer);\n\n if (value === undefined) {\n scope.context.warn(\n scope,\n `@Constrain value for \"${key}\" is \\`${initializer.getText()}\\`, which is not ` +\n `a ${expected} literal the compiler can read from source. Skipping it.`,\n );\n return false;\n }\n\n (constraints as Record<string, number | string>)[key] = value;\n return true;\n}\n\n/**\n * Extract the @Constrain bounds from a property declaration.\n *\n * The decorator's argument has to be an inline object literal: decorators are\n * never evaluated, so a value that is not written out in source cannot be\n * resolved. Unreadable and unknown entries are warned about and skipped\n * individually, so one bad bound does not discard the rest.\n *\n * Returns undefined if there is no @Constrain decorator, or if nothing in it\n * could be read.\n */\nexport function parseConstrainDecorator(\n propDecl: PropertyDeclaration,\n scope: FieldScope,\n): FieldConstraints | undefined {\n const decorator = propDecl.getDecorator(\"Constrain\");\n if (!decorator) {\n return undefined;\n }\n\n const args = decorator.isDecoratorFactory() ? decorator.getArguments() : [];\n const argument = args[0];\n if (argument === undefined || !Node.isObjectLiteralExpression(argument)) {\n scope.context.warn(\n scope,\n `@Constrain expects an inline object literal of compile-time constants, but ` +\n `${argument === undefined ? \"it was called with no argument\" : `was given \\`${argument.getText()}\\``}. ` +\n `Ignoring the decorator.`,\n );\n return undefined;\n }\n\n const constraints: FieldConstraints = {};\n let accepted = 0;\n for (const property of argument.getProperties()) {\n if (readConstraintEntry(property, constraints, scope)) {\n accepted += 1;\n }\n }\n\n return accepted > 0 ? constraints : undefined;\n}\n\n/**\n * Extract the enum source id from a property's @ValuesFrom decorator.\n *\n * Returns undefined if there is no @ValuesFrom decorator, or if its argument\n * is not a string literal.\n */\nexport function parseValuesFromDecorator(\n propDecl: PropertyDeclaration,\n scope: FieldScope,\n): string | undefined {\n const decorator = propDecl.getDecorator(\"ValuesFrom\");\n if (!decorator) {\n return undefined;\n }\n\n const sourceId = getDecoratorStringArg(decorator);\n if (sourceId === undefined) {\n scope.context.warn(\n scope,\n `@ValuesFrom expects a string literal naming the enum source, which the caller ` +\n `resolves at coercion time. Ignoring the decorator.`,\n );\n return undefined;\n }\n return sourceId;\n}\n","import { createHash } from \"node:crypto\";\nimport type { Node } from \"ts-morph\";\nimport type { RuntimeSchema } from \"@sembl/core\";\n\n/**\n * Where a field sits in the source, carried through type resolution and\n * decorator parsing.\n *\n * Every diagnostic names the class and the property, because a warning that\n * only says \"unsupported type\" sends the reader hunting through the whole\n * input directory for it.\n */\nexport interface FieldScope {\n /** Name of the @Schema class that owns the field. */\n className: string;\n /** Property name, dotted through synthesized inline objects. */\n propertyPath: string;\n /**\n * Declaration the type was read from. Members of an anonymous object type\n * have no declaration of their own to resolve against, so they are typed\n * relative to this node.\n */\n node: Node;\n /** Collector for diagnostics and synthesized schemas. */\n context: ExtractionContext;\n}\n\n/**\n * An `object` field type pointing at another schema by id, remembered so the\n * id can be checked once every source file has been visited.\n */\ninterface NestedReference {\n className: string;\n propertyPath: string;\n nestedSchemaId: string;\n /** Source text of the type, for a diagnostic the reader can act on. */\n typeText: string;\n}\n\n/**\n * Accumulates everything an extraction produces beyond the schemas themselves:\n * diagnostics, schemas synthesized for anonymous object types, and the nested\n * schema references that can only be validated once all files are visited.\n */\nexport interface ExtractionContext {\n /** Diagnostics raised so far, in discovery order. */\n readonly warnings: readonly string[];\n /** Schemas synthesized for anonymous inline object types, keyed by id. */\n readonly synthesizedSchemas: Readonly<Record<string, RuntimeSchema>>;\n /** Record a diagnostic against a field. */\n warn(scope: FieldScope, message: string): void;\n /** Add a schema synthesized for an inline object type. */\n registerSynthesizedSchema(schema: RuntimeSchema): void;\n /** Note that a field points at `nestedSchemaId`, to be checked later. */\n recordNestedReference(\n scope: FieldScope,\n nestedSchemaId: string,\n typeText: string,\n ): void;\n /**\n * Warn about every recorded reference whose target is not among\n * `knownSchemaIds`. Call once, after all classes have been visited.\n */\n reportUnresolvedNestedSchemas(knownSchemaIds: ReadonlySet<string>): void;\n}\n\n/**\n * Create an empty {@link ExtractionContext} for a single extraction run.\n */\nexport function createExtractionContext(): ExtractionContext {\n const warnings: string[] = [];\n const synthesizedSchemas: Record<string, RuntimeSchema> = {};\n const nestedReferences: NestedReference[] = [];\n\n return {\n warnings,\n synthesizedSchemas,\n\n warn(scope, message) {\n warnings.push(`${scope.className}.${scope.propertyPath}: ${message}`);\n },\n\n registerSynthesizedSchema(schema) {\n synthesizedSchemas[schema.id] = schema;\n },\n\n recordNestedReference(scope, nestedSchemaId, typeText) {\n nestedReferences.push({\n className: scope.className,\n propertyPath: scope.propertyPath,\n nestedSchemaId,\n typeText,\n });\n },\n\n reportUnresolvedNestedSchemas(knownSchemaIds) {\n for (const reference of nestedReferences) {\n if (knownSchemaIds.has(reference.nestedSchemaId)) {\n continue;\n }\n // A named object type that is not a @Schema class — a `Date`, a `Map`,\n // a plain interface — resolves to an id nothing in the bundle answers\n // to, and emits as an object with no properties. Nothing fails; the\n // field just comes back empty at runtime.\n warnings.push(\n `${reference.className}.${reference.propertyPath}: type \\`${reference.typeText}\\` ` +\n `resolves to nested schema \"${reference.nestedSchemaId}\", which is not a ` +\n `@Schema-decorated class in this extraction. It will emit as an object with ` +\n `no properties. Decorate it with @Schema if it is yours to change, or use a type ` +\n `the contract supports — a \\`Date\\`, for instance, extracts as an ISO-8601 string.`,\n );\n }\n },\n };\n}\n\n/**\n * Derive the id of a schema synthesized from an anonymous object type.\n *\n * The id becomes a filename and an exported binding in the generated output\n * (`<id>.schema.ts`, `<id>Schema`), so it has to be a valid identifier. The\n * path prefix keeps generated files traceable back to the declaration they\n * came from; the digest of the resolved shape makes the id collision-resistant\n * against a hand-written @Schema class and stable across runs and machines —\n * it is derived from the resolved fields, never from absolute source paths.\n */\nexport function synthesizedSchemaId(\n scope: FieldScope,\n structuralSignature: string,\n): string {\n const path = `${scope.className}_${scope.propertyPath}`.replace(\n /[^A-Za-z0-9_]/g,\n \"_\",\n );\n const digest = createHash(\"sha256\")\n .update(structuralSignature)\n .digest(\"hex\")\n .slice(0, 8);\n return `${path}__${digest}`;\n}\n","import type { Type } from \"ts-morph\";\nimport type { FieldDescriptor, FieldType } from \"@sembl/core\";\nimport { synthesizedSchemaId, type FieldScope } from \"./extraction-context.js\";\n\n/**\n * Report a type the schema contract cannot express and fall back to a string.\n *\n * The fallback keeps the rest of the extraction going, but the warning is the\n * point: a field silently mistyped as a string still builds, still validates,\n * and only shows up as a wrong extraction at runtime.\n */\nfunction reportUnsupported(\n type: Type,\n scope: FieldScope,\n reason: string,\n): FieldType {\n scope.context.warn(\n scope,\n `unsupported type \\`${type.getText(scope.node)}\\` — ${reason}. Falling back to string.`,\n );\n return { kind: \"string\" };\n}\n\n/**\n * Build a schema for an anonymous object type (`{ description: string }`) and\n * register it so it is emitted like any hand-written one.\n *\n * Without this an inline type resolves to an id no schema answers to, and the\n * field reaches the model as an object with no properties.\n */\nfunction resolveInlineObjectType(type: Type, scope: FieldScope): FieldType {\n const properties = type.getProperties();\n if (properties.length === 0) {\n // An index signature declares no properties to extract, and FieldType has\n // no map kind to express one. The contract is frozen, so there is nothing\n // correct to emit — only something to say.\n const isMap =\n type.getStringIndexType() !== undefined ||\n type.getNumberIndexType() !== undefined;\n return reportUnsupported(\n type,\n scope,\n isMap\n ? \"a map with an index signature has no FieldType equivalent, so its entries cannot be \" +\n \"described to the model; declare a @Schema class with the keys you expect, or take \" +\n \"the value as a JSON string and parse it yourself\"\n : \"an object type with no properties has nothing to extract\",\n );\n }\n\n const fields: FieldDescriptor[] = properties.map((property) => {\n const name = property.getName();\n return {\n name,\n // Members of an inline type carry no @Describe, so the owning field's\n // description is the only semantics the model gets for them.\n description: \"\",\n type: resolveFieldType(property.getTypeAtLocation(scope.node), {\n ...scope,\n propertyPath: `${scope.propertyPath}.${name}`,\n }),\n required: !property.isOptional(),\n };\n });\n\n // Hash the resolved fields rather than the source text: type text can embed\n // absolute import paths, which would make the id differ between machines.\n const id = synthesizedSchemaId(\n scope,\n JSON.stringify(fields.map((f) => [f.name, f.required, f.type])),\n );\n scope.context.registerSynthesizedSchema({\n id,\n description: `Inline object type declared at ${scope.className}.${scope.propertyPath}.`,\n fields,\n });\n return { kind: \"object\", nestedSchemaId: id };\n}\n\n/**\n * Resolve a TypeScript type to a FieldType descriptor.\n *\n * Maps TS types to the schema type system: string, number, boolean, array,\n * object, enum. Anything the contract cannot express is reported through\n * `scope.context` rather than quietly coerced.\n */\nexport function resolveFieldType(type: Type, scope: FieldScope): FieldType {\n // Optional and nullable fields arrive as unions with undefined/null. The\n // question mark already carries optionality, so resolve the value type.\n if (type.isUnion()) {\n const members = type\n .getUnionTypes()\n .filter((t) => !t.isUndefined() && !t.isNull());\n\n if (members.length === 1) {\n return resolveFieldType(members[0], scope);\n }\n\n if (members.length === 0) {\n return reportUnsupported(type, scope, \"there is no value type to extract\");\n }\n\n if (members.every((t) => t.isStringLiteral())) {\n return {\n kind: \"enum\",\n values: members.map((t) => t.getLiteralValue() as string),\n };\n }\n\n // `boolean` is modelled as `true | false`, so an optional boolean would\n // otherwise look like a mixed union and fall through to the fallback.\n if (members.every((t) => t.isBoolean() || t.isBooleanLiteral())) {\n return { kind: \"boolean\" };\n }\n\n // A numeric enum, or a union of number literals, widens to number:\n // FieldType has no numeric enum kind to narrow it to.\n if (members.every((t) => t.isNumber() || t.isNumberLiteral())) {\n return { kind: \"number\" };\n }\n\n return reportUnsupported(\n type,\n scope,\n \"a union mixing several kinds of value has no single FieldType; split it into \" +\n \"separate fields, or narrow it to one kind\",\n );\n }\n\n if (type.isString() || type.isStringLiteral()) {\n return { kind: \"string\" };\n }\n\n if (type.isNumber() || type.isNumberLiteral()) {\n return { kind: \"number\" };\n }\n\n if (type.isBoolean() || type.isBooleanLiteral()) {\n return { kind: \"boolean\" };\n }\n\n if (type.isArray()) {\n const elementType = type.getArrayElementTypeOrThrow();\n return { kind: \"array\", items: resolveFieldType(elementType, scope) };\n }\n\n if (type.isEnum()) {\n const members = type\n .getUnionTypes()\n .map((t) => t.getLiteralValue())\n .filter((v): v is string => typeof v === \"string\");\n if (members.length > 0) {\n return { kind: \"enum\", values: members };\n }\n return reportUnsupported(\n type,\n scope,\n \"its members are not string values, so they cannot be offered to the model as an enum\",\n );\n }\n\n if (type.isObject()) {\n const symbol = type.getSymbol() ?? type.getAliasSymbol();\n const typeName = symbol?.getName();\n if (typeName && typeName !== \"__type\" && typeName !== \"Object\") {\n // Assume a named object type is another @Schema class. Whether it really\n // is one cannot be known until every file has been visited, so record it\n // for the check in reportUnresolvedNestedSchemas.\n scope.context.recordNestedReference(\n scope,\n typeName,\n type.getText(scope.node),\n );\n return { kind: \"object\", nestedSchemaId: typeName };\n }\n return resolveInlineObjectType(type, scope);\n }\n\n return reportUnsupported(\n type,\n scope,\n \"it maps to none of string, number, boolean, array, enum, or a @Schema class\",\n );\n}\n","import type { ClassDeclaration } from \"ts-morph\";\nimport type { RuntimeSchema, FieldDescriptor, FieldType } from \"@sembl/core\";\nimport {\n parseSchemaDecorator,\n parseDescribeDecorator,\n parseConstrainDecorator,\n parseValuesFromDecorator,\n} from \"./decorator-parser.js\";\nimport { resolveFieldType } from \"./type-resolver.js\";\nimport type { ExtractionContext, FieldScope } from \"./extraction-context.js\";\n\n/**\n * Render a FieldType as something readable in a diagnostic.\n */\nfunction describeKind(type: FieldType): string {\n return type.kind === \"array\" ? `${describeKind(type.items)}[]` : type.kind;\n}\n\n/**\n * Point a resolved field type at a runtime-resolved enum source.\n *\n * Only a string, or an array of strings, has values for a source to constrain.\n * Anywhere else the decorator is on the wrong field — a mistake worth hearing\n * about at build time rather than discovering as an annotation that did\n * nothing, so the type is left alone and the field still extracts.\n */\nfunction applyValuesFrom(\n type: FieldType,\n sourceId: string,\n scope: FieldScope,\n): FieldType {\n if (type.kind === \"string\") {\n return { kind: \"dynamicEnum\", sourceId };\n }\n if (type.kind === \"array\" && type.items.kind === \"string\") {\n return { kind: \"array\", items: { kind: \"dynamicEnum\", sourceId } };\n }\n scope.context.warn(\n scope,\n `@ValuesFrom(\"${sourceId}\") applies to a string or string[] field, but this field ` +\n `resolved to ${describeKind(type)}. Leaving the type unchanged.`,\n );\n return type;\n}\n\n/**\n * Visit a class declaration and extract a RuntimeSchema if it has @Schema decorator.\n * Returns undefined if the class is not decorated with @Schema.\n *\n * Diagnostics and schemas synthesized for inline object types are collected\n * into `context` rather than thrown, so one questionable field does not stop\n * the rest of the extraction.\n */\nexport function visitClass(\n classDecl: ClassDeclaration,\n context: ExtractionContext,\n): RuntimeSchema | undefined {\n const description = parseSchemaDecorator(classDecl);\n if (description === undefined) {\n return undefined;\n }\n\n const className = classDecl.getName();\n if (!className) {\n return undefined;\n }\n\n const fields: FieldDescriptor[] = [];\n\n for (const prop of classDecl.getProperties()) {\n const fieldDescription = parseDescribeDecorator(prop);\n if (fieldDescription === undefined) {\n continue;\n }\n\n const name = prop.getName();\n const isOptional = prop.hasQuestionToken();\n const scope: FieldScope = {\n className,\n propertyPath: name,\n node: prop,\n context,\n };\n\n let type = resolveFieldType(prop.getType(), scope);\n\n // @ValuesFrom rewrites the resolved type, so it has to run after the type\n // is known — it is the declared TS type that decides whether the source\n // can apply at all.\n const sourceId = parseValuesFromDecorator(prop, scope);\n if (sourceId !== undefined) {\n type = applyValuesFrom(type, sourceId, scope);\n }\n\n const constraints = parseConstrainDecorator(prop, scope);\n\n fields.push({\n name,\n description: fieldDescription,\n type,\n required: !isOptional,\n ...(constraints !== undefined ? { constraints } : {}),\n });\n }\n\n return {\n id: className,\n description,\n fields,\n };\n}\n","import { mkdirSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { RuntimeSchema, SchemaBundle } from \"@sembl/core\";\n\n/**\n * Serialize a RuntimeSchema to a TypeScript source string.\n */\nfunction schemaToSource(schema: RuntimeSchema): string {\n const json = JSON.stringify(schema, null, 2);\n return `// Auto-generated by sembl extract — do not edit\nimport type { RuntimeSchema } from \"@sembl/core\";\n\nexport const ${schema.id}Schema: RuntimeSchema = ${json};\n`;\n}\n\n/**\n * Emit a SchemaBundle as a set of individual .schema.ts files\n * plus a bundle index file.\n */\nexport function emitSchemas(bundle: SchemaBundle, outputDir: string): string[] {\n mkdirSync(outputDir, { recursive: true });\n\n const emittedFiles: string[] = [];\n const schemaIds: string[] = [];\n\n for (const [id, schema] of Object.entries(bundle.schemas)) {\n const fileName = `${id}.schema.ts`;\n const filePath = join(outputDir, fileName);\n writeFileSync(filePath, schemaToSource(schema), \"utf-8\");\n emittedFiles.push(filePath);\n schemaIds.push(id);\n }\n\n // Emit bundle index file\n const indexLines = [\n \"// Auto-generated by sembl extract — do not edit\",\n 'import type { SchemaBundle } from \"@sembl/core\";',\n \"\",\n ];\n\n for (const id of schemaIds) {\n indexLines.push(`import { ${id}Schema } from \"./${id}.schema.js\";`);\n }\n\n indexLines.push(\"\");\n indexLines.push(\"export const bundle: SchemaBundle = {\");\n indexLines.push(\" schemas: {\");\n for (const id of schemaIds) {\n indexLines.push(` ${id}: ${id}Schema,`);\n }\n indexLines.push(\" },\");\n indexLines.push(\"};\");\n indexLines.push(\"\");\n\n // Re-export individual schemas\n for (const id of schemaIds) {\n indexLines.push(`export { ${id}Schema } from \"./${id}.schema.js\";`);\n }\n indexLines.push(\"\");\n\n const indexPath = join(outputDir, \"index.ts\");\n writeFileSync(indexPath, indexLines.join(\"\\n\"), \"utf-8\");\n emittedFiles.push(indexPath);\n\n return emittedFiles;\n}\n","import { pathToFileURL } from \"node:url\";\nimport { resolve, join } from \"node:path\";\nimport type { Provider, RuntimeSchema, SchemaBundle, EnumResolver, CoerceOptions } from \"@sembl/core\";\nimport {\n runEval,\n loadFixtures,\n loadReport,\n saveReport,\n diffReports,\n formatReport,\n replayOrRecord,\n} from \"@sembl/testing\";\nimport type { TokenPrices, EvalReport, EvalDiff } from \"@sembl/testing\";\n\nexport interface EvalCommandOptions {\n /** A JS module exporting the schema and provider to evaluate with. */\n config: string;\n /** Directory of fixture JSON files. */\n fixtures: string;\n /** Where to write the report; defaults to `<fixtures>/.sembl-eval/last-run.json`. */\n out?: string;\n mode?: \"coerce\" | \"partialCoerce\";\n provenance?: boolean;\n concurrency?: number;\n /** Replay recordings from this directory, recording misses through the provider. */\n replay?: string;\n /** Exit non-zero when overall recall lands below this fraction. */\n minRecall?: number;\n /** Exit non-zero when overall precision lands below this fraction. */\n minPrecision?: number;\n}\n\n/**\n * What an eval config module exports. Written as a plain JS module so the\n * CLI can `import()` it without a TypeScript loader; a TS project can point\n * at its build output, or keep the config in `.mjs`.\n */\nexport interface EvalConfig {\n schema: RuntimeSchema;\n provider: Provider;\n bundle?: SchemaBundle;\n enumResolver?: EnumResolver;\n prices?: TokenPrices;\n /** Any other coercion options: `onInvalidField`, `maxRepairAttempts`, `maxInputChars`, … */\n coerceOptions?: Partial<Omit<CoerceOptions, \"schema\" | \"provider\" | \"bundle\" | \"enumResolver\">>;\n}\n\nexport interface EvalCommandResult {\n report?: EvalReport;\n diff?: EvalDiff;\n exitCode: number;\n}\n\nasync function loadConfig(path: string): Promise<EvalConfig> {\n const url = pathToFileURL(resolve(path)).href;\n const mod = (await import(url)) as { default?: Partial<EvalConfig> } & Partial<EvalConfig>;\n const config: Partial<EvalConfig> = mod.default ?? mod;\n if (!config.schema || typeof config.schema !== \"object\" || !(\"id\" in config.schema)) {\n throw new Error(`${path} must export a \\`schema\\` (a RuntimeSchema or defineSchema result)`);\n }\n if (!config.provider || typeof config.provider.complete !== \"function\") {\n throw new Error(`${path} must export a \\`provider\\``);\n }\n return config as EvalConfig;\n}\n\nexport async function evalCommand(options: EvalCommandOptions): Promise<EvalCommandResult> {\n let config: EvalConfig;\n let fixtures;\n try {\n config = await loadConfig(options.config);\n fixtures = loadFixtures(options.fixtures);\n } catch (error) {\n console.error(`sembl eval: ${error instanceof Error ? error.message : String(error)}`);\n return { exitCode: 1 };\n }\n if (fixtures.length === 0) {\n console.error(`sembl eval: no fixtures found in ${resolve(options.fixtures)}`);\n return { exitCode: 1 };\n }\n\n const provider = options.replay ? replayOrRecord(resolve(options.replay), config.provider) : config.provider;\n const out = options.out ?? join(resolve(options.fixtures), \".sembl-eval\", \"last-run.json\");\n const previous = loadReport(out);\n\n const report = await runEval({\n ...config.coerceOptions,\n schema: config.schema,\n bundle: config.bundle,\n enumResolver: config.enumResolver,\n prices: config.prices,\n provider,\n fixtures,\n mode: options.mode,\n provenance: options.provenance,\n concurrency: options.concurrency,\n });\n const diff = previous ? diffReports(previous, report) : undefined;\n\n console.log(formatReport(report, diff));\n saveReport(report, out);\n console.log(`\\nReport written to ${out}${previous ? \" (deltas are against the previous run)\" : \"\"}`);\n\n const below = (value: number | null, floor: number | undefined) =>\n floor !== undefined && (value === null || value < floor);\n let exitCode = 0;\n if (below(report.totals.recall, options.minRecall)) {\n console.error(`sembl eval: recall ${fmt(report.totals.recall)} is below --min-recall ${options.minRecall}`);\n exitCode = 1;\n }\n if (below(report.totals.precision, options.minPrecision)) {\n console.error(`sembl eval: precision ${fmt(report.totals.precision)} is below --min-precision ${options.minPrecision}`);\n exitCode = 1;\n }\n return { report, diff, exitCode };\n}\n\nfunction fmt(value: number | null): string {\n return value === null ? \"n/a\" : value.toFixed(2);\n}\n"],"mappings":"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","names":["resolve","join"]}
|
|
@@ -1,10 +1,44 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
createExtractionContext: () => createExtractionContext,
|
|
24
|
+
emitSchemas: () => emitSchemas,
|
|
25
|
+
evalCommand: () => evalCommand,
|
|
26
|
+
extractSchemas: () => extractSchemas,
|
|
27
|
+
parseConstrainDecorator: () => parseConstrainDecorator,
|
|
28
|
+
parseDescribeDecorator: () => parseDescribeDecorator,
|
|
29
|
+
parseSchemaDecorator: () => parseSchemaDecorator,
|
|
30
|
+
parseValuesFromDecorator: () => parseValuesFromDecorator,
|
|
31
|
+
resolveFieldType: () => resolveFieldType,
|
|
32
|
+
synthesizedSchemaId: () => synthesizedSchemaId,
|
|
33
|
+
visitClass: () => visitClass
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(index_exports);
|
|
36
|
+
|
|
37
|
+
// src/extractor/ast-extractor.ts
|
|
38
|
+
var import_ts_morph2 = require("ts-morph");
|
|
2
39
|
|
|
3
40
|
// src/extractor/decorator-parser.ts
|
|
4
|
-
|
|
5
|
-
Node,
|
|
6
|
-
SyntaxKind
|
|
7
|
-
} from "ts-morph";
|
|
41
|
+
var import_ts_morph = require("ts-morph");
|
|
8
42
|
var CONSTRAINT_KEYS = {
|
|
9
43
|
maxLength: "number",
|
|
10
44
|
minLength: "number",
|
|
@@ -47,17 +81,17 @@ function parseDescribeDecorator(propDecl) {
|
|
|
47
81
|
return getDecoratorStringArg(decorator);
|
|
48
82
|
}
|
|
49
83
|
function readNumberLiteral(node) {
|
|
50
|
-
if (Node.isNumericLiteral(node)) {
|
|
84
|
+
if (import_ts_morph.Node.isNumericLiteral(node)) {
|
|
51
85
|
return node.getLiteralValue();
|
|
52
86
|
}
|
|
53
|
-
if (Node.isPrefixUnaryExpression(node)) {
|
|
87
|
+
if (import_ts_morph.Node.isPrefixUnaryExpression(node)) {
|
|
54
88
|
const operand = node.getOperand();
|
|
55
|
-
if (Node.isNumericLiteral(operand)) {
|
|
89
|
+
if (import_ts_morph.Node.isNumericLiteral(operand)) {
|
|
56
90
|
const operator = node.getOperatorToken();
|
|
57
|
-
if (operator === SyntaxKind.MinusToken) {
|
|
91
|
+
if (operator === import_ts_morph.SyntaxKind.MinusToken) {
|
|
58
92
|
return -operand.getLiteralValue();
|
|
59
93
|
}
|
|
60
|
-
if (operator === SyntaxKind.PlusToken) {
|
|
94
|
+
if (operator === import_ts_morph.SyntaxKind.PlusToken) {
|
|
61
95
|
return operand.getLiteralValue();
|
|
62
96
|
}
|
|
63
97
|
}
|
|
@@ -65,13 +99,13 @@ function readNumberLiteral(node) {
|
|
|
65
99
|
return void 0;
|
|
66
100
|
}
|
|
67
101
|
function readStringLiteral(node) {
|
|
68
|
-
if (Node.isStringLiteral(node) || Node.isNoSubstitutionTemplateLiteral(node)) {
|
|
102
|
+
if (import_ts_morph.Node.isStringLiteral(node) || import_ts_morph.Node.isNoSubstitutionTemplateLiteral(node)) {
|
|
69
103
|
return node.getLiteralValue();
|
|
70
104
|
}
|
|
71
105
|
return void 0;
|
|
72
106
|
}
|
|
73
107
|
function readConstraintEntry(property, constraints, scope) {
|
|
74
|
-
if (!Node.isPropertyAssignment(property)) {
|
|
108
|
+
if (!import_ts_morph.Node.isPropertyAssignment(property)) {
|
|
75
109
|
scope.context.warn(
|
|
76
110
|
scope,
|
|
77
111
|
`@Constrain entry \`${property.getText()}\` is not a \`key: value\` pair of compile-time constants and cannot be read from source. Skipping it.`
|
|
@@ -79,7 +113,7 @@ function readConstraintEntry(property, constraints, scope) {
|
|
|
79
113
|
return false;
|
|
80
114
|
}
|
|
81
115
|
const nameNode = property.getNameNode();
|
|
82
|
-
if (!Node.isIdentifier(nameNode) && !Node.isStringLiteral(nameNode)) {
|
|
116
|
+
if (!import_ts_morph.Node.isIdentifier(nameNode) && !import_ts_morph.Node.isStringLiteral(nameNode)) {
|
|
83
117
|
scope.context.warn(
|
|
84
118
|
scope,
|
|
85
119
|
`@Constrain key \`${nameNode.getText()}\` is computed and cannot be read from source. Skipping it.`
|
|
@@ -114,7 +148,7 @@ function parseConstrainDecorator(propDecl, scope) {
|
|
|
114
148
|
}
|
|
115
149
|
const args = decorator.isDecoratorFactory() ? decorator.getArguments() : [];
|
|
116
150
|
const argument = args[0];
|
|
117
|
-
if (argument === void 0 || !Node.isObjectLiteralExpression(argument)) {
|
|
151
|
+
if (argument === void 0 || !import_ts_morph.Node.isObjectLiteralExpression(argument)) {
|
|
118
152
|
scope.context.warn(
|
|
119
153
|
scope,
|
|
120
154
|
`@Constrain expects an inline object literal of compile-time constants, but ${argument === void 0 ? "it was called with no argument" : `was given \`${argument.getText()}\``}. Ignoring the decorator.`
|
|
@@ -147,7 +181,7 @@ function parseValuesFromDecorator(propDecl, scope) {
|
|
|
147
181
|
}
|
|
148
182
|
|
|
149
183
|
// src/extractor/extraction-context.ts
|
|
150
|
-
|
|
184
|
+
var import_node_crypto = require("crypto");
|
|
151
185
|
function createExtractionContext() {
|
|
152
186
|
const warnings = [];
|
|
153
187
|
const synthesizedSchemas = {};
|
|
@@ -186,7 +220,7 @@ function synthesizedSchemaId(scope, structuralSignature) {
|
|
|
186
220
|
/[^A-Za-z0-9_]/g,
|
|
187
221
|
"_"
|
|
188
222
|
);
|
|
189
|
-
const digest = createHash("sha256").update(structuralSignature).digest("hex").slice(0, 8);
|
|
223
|
+
const digest = (0, import_node_crypto.createHash)("sha256").update(structuralSignature).digest("hex").slice(0, 8);
|
|
190
224
|
return `${path}__${digest}`;
|
|
191
225
|
}
|
|
192
226
|
|
|
@@ -366,9 +400,8 @@ function visitClass(classDecl, context) {
|
|
|
366
400
|
}
|
|
367
401
|
|
|
368
402
|
// src/extractor/ast-extractor.ts
|
|
369
|
-
import { Project, ScriptTarget } from "ts-morph";
|
|
370
403
|
function extractSchemas(options) {
|
|
371
|
-
const project = new Project({
|
|
404
|
+
const project = new import_ts_morph2.Project({
|
|
372
405
|
tsConfigFilePath: options.tsconfigPath,
|
|
373
406
|
skipAddingFilesFromTsConfig: true,
|
|
374
407
|
compilerOptions: {
|
|
@@ -377,7 +410,7 @@ function extractSchemas(options) {
|
|
|
377
410
|
// Without a target the default lib is ES5, so anything newer — `Map`,
|
|
378
411
|
// `Set` — resolves to `any` and lands in the unsupported-type warning as
|
|
379
412
|
// "any", naming a type the author never wrote.
|
|
380
|
-
target: ScriptTarget.ES2022
|
|
413
|
+
target: import_ts_morph2.ScriptTarget.ES2022
|
|
381
414
|
}
|
|
382
415
|
});
|
|
383
416
|
for (const pattern of options.filePatterns) {
|
|
@@ -401,8 +434,8 @@ function extractSchemas(options) {
|
|
|
401
434
|
}
|
|
402
435
|
|
|
403
436
|
// src/generator/schema-emitter.ts
|
|
404
|
-
|
|
405
|
-
|
|
437
|
+
var import_node_fs = require("fs");
|
|
438
|
+
var import_node_path = require("path");
|
|
406
439
|
function schemaToSource(schema) {
|
|
407
440
|
const json = JSON.stringify(schema, null, 2);
|
|
408
441
|
return `// Auto-generated by sembl extract \u2014 do not edit
|
|
@@ -412,13 +445,13 @@ export const ${schema.id}Schema: RuntimeSchema = ${json};
|
|
|
412
445
|
`;
|
|
413
446
|
}
|
|
414
447
|
function emitSchemas(bundle, outputDir) {
|
|
415
|
-
mkdirSync(outputDir, { recursive: true });
|
|
448
|
+
(0, import_node_fs.mkdirSync)(outputDir, { recursive: true });
|
|
416
449
|
const emittedFiles = [];
|
|
417
450
|
const schemaIds = [];
|
|
418
451
|
for (const [id, schema] of Object.entries(bundle.schemas)) {
|
|
419
452
|
const fileName = `${id}.schema.ts`;
|
|
420
|
-
const filePath = join(outputDir, fileName);
|
|
421
|
-
writeFileSync(filePath, schemaToSource(schema), "utf-8");
|
|
453
|
+
const filePath = (0, import_node_path.join)(outputDir, fileName);
|
|
454
|
+
(0, import_node_fs.writeFileSync)(filePath, schemaToSource(schema), "utf-8");
|
|
422
455
|
emittedFiles.push(filePath);
|
|
423
456
|
schemaIds.push(id);
|
|
424
457
|
}
|
|
@@ -443,22 +476,89 @@ function emitSchemas(bundle, outputDir) {
|
|
|
443
476
|
indexLines.push(`export { ${id}Schema } from "./${id}.schema.js";`);
|
|
444
477
|
}
|
|
445
478
|
indexLines.push("");
|
|
446
|
-
const indexPath = join(outputDir, "index.ts");
|
|
447
|
-
writeFileSync(indexPath, indexLines.join("\n"), "utf-8");
|
|
479
|
+
const indexPath = (0, import_node_path.join)(outputDir, "index.ts");
|
|
480
|
+
(0, import_node_fs.writeFileSync)(indexPath, indexLines.join("\n"), "utf-8");
|
|
448
481
|
emittedFiles.push(indexPath);
|
|
449
482
|
return emittedFiles;
|
|
450
483
|
}
|
|
451
484
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
485
|
+
// src/cli/commands/eval.ts
|
|
486
|
+
var import_node_url = require("url");
|
|
487
|
+
var import_node_path2 = require("path");
|
|
488
|
+
var import_testing = require("@sembl/testing");
|
|
489
|
+
async function loadConfig(path) {
|
|
490
|
+
const url = (0, import_node_url.pathToFileURL)((0, import_node_path2.resolve)(path)).href;
|
|
491
|
+
const mod = await import(url);
|
|
492
|
+
const config = mod.default ?? mod;
|
|
493
|
+
if (!config.schema || typeof config.schema !== "object" || !("id" in config.schema)) {
|
|
494
|
+
throw new Error(`${path} must export a \`schema\` (a RuntimeSchema or defineSchema result)`);
|
|
495
|
+
}
|
|
496
|
+
if (!config.provider || typeof config.provider.complete !== "function") {
|
|
497
|
+
throw new Error(`${path} must export a \`provider\``);
|
|
498
|
+
}
|
|
499
|
+
return config;
|
|
500
|
+
}
|
|
501
|
+
async function evalCommand(options) {
|
|
502
|
+
let config;
|
|
503
|
+
let fixtures;
|
|
504
|
+
try {
|
|
505
|
+
config = await loadConfig(options.config);
|
|
506
|
+
fixtures = (0, import_testing.loadFixtures)(options.fixtures);
|
|
507
|
+
} catch (error) {
|
|
508
|
+
console.error(`sembl eval: ${error instanceof Error ? error.message : String(error)}`);
|
|
509
|
+
return { exitCode: 1 };
|
|
510
|
+
}
|
|
511
|
+
if (fixtures.length === 0) {
|
|
512
|
+
console.error(`sembl eval: no fixtures found in ${(0, import_node_path2.resolve)(options.fixtures)}`);
|
|
513
|
+
return { exitCode: 1 };
|
|
514
|
+
}
|
|
515
|
+
const provider = options.replay ? (0, import_testing.replayOrRecord)((0, import_node_path2.resolve)(options.replay), config.provider) : config.provider;
|
|
516
|
+
const out = options.out ?? (0, import_node_path2.join)((0, import_node_path2.resolve)(options.fixtures), ".sembl-eval", "last-run.json");
|
|
517
|
+
const previous = (0, import_testing.loadReport)(out);
|
|
518
|
+
const report = await (0, import_testing.runEval)({
|
|
519
|
+
...config.coerceOptions,
|
|
520
|
+
schema: config.schema,
|
|
521
|
+
bundle: config.bundle,
|
|
522
|
+
enumResolver: config.enumResolver,
|
|
523
|
+
prices: config.prices,
|
|
524
|
+
provider,
|
|
525
|
+
fixtures,
|
|
526
|
+
mode: options.mode,
|
|
527
|
+
provenance: options.provenance,
|
|
528
|
+
concurrency: options.concurrency
|
|
529
|
+
});
|
|
530
|
+
const diff = previous ? (0, import_testing.diffReports)(previous, report) : void 0;
|
|
531
|
+
console.log((0, import_testing.formatReport)(report, diff));
|
|
532
|
+
(0, import_testing.saveReport)(report, out);
|
|
533
|
+
console.log(`
|
|
534
|
+
Report written to ${out}${previous ? " (deltas are against the previous run)" : ""}`);
|
|
535
|
+
const below = (value, floor) => floor !== void 0 && (value === null || value < floor);
|
|
536
|
+
let exitCode = 0;
|
|
537
|
+
if (below(report.totals.recall, options.minRecall)) {
|
|
538
|
+
console.error(`sembl eval: recall ${fmt(report.totals.recall)} is below --min-recall ${options.minRecall}`);
|
|
539
|
+
exitCode = 1;
|
|
540
|
+
}
|
|
541
|
+
if (below(report.totals.precision, options.minPrecision)) {
|
|
542
|
+
console.error(`sembl eval: precision ${fmt(report.totals.precision)} is below --min-precision ${options.minPrecision}`);
|
|
543
|
+
exitCode = 1;
|
|
544
|
+
}
|
|
545
|
+
return { report, diff, exitCode };
|
|
546
|
+
}
|
|
547
|
+
function fmt(value) {
|
|
548
|
+
return value === null ? "n/a" : value.toFixed(2);
|
|
549
|
+
}
|
|
550
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
551
|
+
0 && (module.exports = {
|
|
552
|
+
createExtractionContext,
|
|
553
|
+
emitSchemas,
|
|
554
|
+
evalCommand,
|
|
555
|
+
extractSchemas,
|
|
455
556
|
parseConstrainDecorator,
|
|
557
|
+
parseDescribeDecorator,
|
|
558
|
+
parseSchemaDecorator,
|
|
456
559
|
parseValuesFromDecorator,
|
|
457
|
-
createExtractionContext,
|
|
458
|
-
synthesizedSchemaId,
|
|
459
560
|
resolveFieldType,
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
//# sourceMappingURL=chunk-Y4FKH2ZU.js.map
|
|
561
|
+
synthesizedSchemaId,
|
|
562
|
+
visitClass
|
|
563
|
+
});
|
|
564
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/extractor/ast-extractor.ts","../src/extractor/decorator-parser.ts","../src/extractor/extraction-context.ts","../src/extractor/type-resolver.ts","../src/extractor/class-visitor.ts","../src/generator/schema-emitter.ts","../src/cli/commands/eval.ts"],"sourcesContent":["export { extractSchemas } from \"./extractor/ast-extractor.js\";\nexport type { ExtractOptions } from \"./extractor/ast-extractor.js\";\nexport { emitSchemas } from \"./generator/schema-emitter.js\";\nexport { visitClass } from \"./extractor/class-visitor.js\";\nexport {\n parseSchemaDecorator,\n parseDescribeDecorator,\n parseConstrainDecorator,\n parseValuesFromDecorator,\n} from \"./extractor/decorator-parser.js\";\nexport { resolveFieldType } from \"./extractor/type-resolver.js\";\nexport {\n createExtractionContext,\n synthesizedSchemaId,\n} from \"./extractor/extraction-context.js\";\nexport type {\n ExtractionContext,\n FieldScope,\n} from \"./extractor/extraction-context.js\";\nexport type {\n SchemaAnnotation,\n FieldAnnotation,\n CompilerConfig,\n ExtractionResult,\n} from \"./types.js\";\nexport { evalCommand } from \"./cli/commands/eval.js\";\nexport type { EvalCommandOptions, EvalCommandResult, EvalConfig } from \"./cli/commands/eval.js\";\n","import { Project, ScriptTarget } from \"ts-morph\";\nimport type { RuntimeSchema } from \"@sembl/core\";\nimport { visitClass } from \"./class-visitor.js\";\nimport { createExtractionContext } from \"./extraction-context.js\";\nimport type { ExtractionResult } from \"../types.js\";\n\nexport interface ExtractOptions {\n /** Glob patterns for source files */\n filePatterns: string[];\n /** Optional tsconfig.json path */\n tsconfigPath?: string;\n}\n\n/**\n * Extract all @Schema-decorated classes from the given source files.\n *\n * Returns the schemas alongside any warnings raised while resolving them —\n * unsupported field types, annotations that could not be read. Extraction\n * never throws on a bad field, so the warnings are the only signal that the\n * emitted schemas are not what the source described.\n */\nexport function extractSchemas(options: ExtractOptions): ExtractionResult {\n const project = new Project({\n tsConfigFilePath: options.tsconfigPath,\n skipAddingFilesFromTsConfig: true,\n compilerOptions: {\n experimentalDecorators: true,\n strict: true,\n // Without a target the default lib is ES5, so anything newer — `Map`,\n // `Set` — resolves to `any` and lands in the unsupported-type warning as\n // \"any\", naming a type the author never wrote.\n target: ScriptTarget.ES2022,\n },\n });\n\n // Add source files matching the patterns\n for (const pattern of options.filePatterns) {\n project.addSourceFilesAtPaths(pattern);\n }\n\n const context = createExtractionContext();\n const schemas: Record<string, RuntimeSchema> = {};\n\n for (const sourceFile of project.getSourceFiles()) {\n for (const classDecl of sourceFile.getClasses()) {\n const schema = visitClass(classDecl, context);\n if (schema) {\n schemas[schema.id] = schema;\n }\n }\n }\n\n // Schemas synthesized for inline object types are emitted like any other, so\n // the prompt builder and the JSON Schema converter can resolve them by id.\n for (const [id, schema] of Object.entries(context.synthesizedSchemas)) {\n schemas[id] = schema;\n }\n\n // Only now is the full set of ids known, so a nested reference to a class\n // declared in a file visited later is not mistaken for a dangling one.\n context.reportUnresolvedNestedSchemas(new Set(Object.keys(schemas)));\n\n return { schemas, warnings: [...context.warnings] };\n}\n","import {\n Node,\n SyntaxKind,\n type ClassDeclaration,\n type PropertyDeclaration,\n type Decorator,\n} from \"ts-morph\";\nimport type { FieldConstraints } from \"@sembl/core\";\nimport type { FieldScope } from \"./extraction-context.js\";\n\n/**\n * Every key `FieldConstraints` allows, with the literal kind its value must be.\n *\n * Typed against `FieldConstraints` on purpose: if core adds or renames a\n * constraint, this table stops compiling instead of silently rejecting the new\n * key as unknown.\n */\nconst CONSTRAINT_KEYS: Record<\n keyof Required<FieldConstraints>,\n \"number\" | \"string\"\n> = {\n maxLength: \"number\",\n minLength: \"number\",\n minimum: \"number\",\n maximum: \"number\",\n minItems: \"number\",\n maxItems: \"number\",\n pattern: \"string\",\n};\n\n/**\n * Extract the string argument from a decorator call expression.\n * e.g. @Schema(\"some description\") → \"some description\"\n */\nfunction getDecoratorStringArg(decorator: Decorator): string | undefined {\n if (!decorator.isDecoratorFactory()) {\n return undefined;\n }\n const args = decorator.getArguments();\n if (args.length === 0) {\n return undefined;\n }\n const arg = args[0];\n // Strip quotes from string literal\n const text = arg.getText();\n if (\n (text.startsWith('\"') && text.endsWith('\"')) ||\n (text.startsWith(\"'\") && text.endsWith(\"'\"))\n ) {\n return text.slice(1, -1);\n }\n // Handle template literals\n if (text.startsWith(\"`\") && text.endsWith(\"`\")) {\n return text.slice(1, -1);\n }\n return undefined;\n}\n\n/**\n * Extract the @Schema description from a class declaration.\n * Returns undefined if the class doesn't have a @Schema decorator.\n */\nexport function parseSchemaDecorator(\n classDecl: ClassDeclaration,\n): string | undefined {\n const decorator = classDecl.getDecorator(\"Schema\");\n if (!decorator) {\n return undefined;\n }\n return getDecoratorStringArg(decorator);\n}\n\n/**\n * Extract the @Describe description from a property declaration.\n * Returns undefined if the property doesn't have a @Describe decorator.\n */\nexport function parseDescribeDecorator(\n propDecl: PropertyDeclaration,\n): string | undefined {\n const decorator = propDecl.getDecorator(\"Describe\");\n if (!decorator) {\n return undefined;\n }\n return getDecoratorStringArg(decorator);\n}\n\n/**\n * Read a number written directly in source.\n *\n * A negative bound is a prefix minus applied to a numeric literal rather than\n * a literal of its own, so it needs unwrapping.\n */\nfunction readNumberLiteral(node: Node): number | undefined {\n if (Node.isNumericLiteral(node)) {\n return node.getLiteralValue();\n }\n if (Node.isPrefixUnaryExpression(node)) {\n const operand = node.getOperand();\n if (Node.isNumericLiteral(operand)) {\n const operator = node.getOperatorToken();\n if (operator === SyntaxKind.MinusToken) {\n return -operand.getLiteralValue();\n }\n if (operator === SyntaxKind.PlusToken) {\n return operand.getLiteralValue();\n }\n }\n }\n return undefined;\n}\n\n/**\n * Read a string written directly in source.\n */\nfunction readStringLiteral(node: Node): string | undefined {\n if (Node.isStringLiteral(node) || Node.isNoSubstitutionTemplateLiteral(node)) {\n return node.getLiteralValue();\n }\n return undefined;\n}\n\n/**\n * Read one `key: value` pair of a @Constrain object literal into `constraints`.\n * Returns true if a value was accepted.\n */\nfunction readConstraintEntry(\n property: Node,\n constraints: FieldConstraints,\n scope: FieldScope,\n): boolean {\n if (!Node.isPropertyAssignment(property)) {\n // A shorthand (`{ maxLength }`), a spread (`{ ...shared }`), or a method\n // all resolve through a binding the compiler never evaluates.\n scope.context.warn(\n scope,\n `@Constrain entry \\`${property.getText()}\\` is not a \\`key: value\\` pair of ` +\n `compile-time constants and cannot be read from source. Skipping it.`,\n );\n return false;\n }\n\n const nameNode = property.getNameNode();\n if (!Node.isIdentifier(nameNode) && !Node.isStringLiteral(nameNode)) {\n scope.context.warn(\n scope,\n `@Constrain key \\`${nameNode.getText()}\\` is computed and cannot be read from source. Skipping it.`,\n );\n return false;\n }\n\n const key = nameNode.getText().replace(/^[\"']|[\"']$/g, \"\");\n if (!Object.prototype.hasOwnProperty.call(CONSTRAINT_KEYS, key)) {\n scope.context.warn(\n scope,\n `@Constrain key \"${key}\" is not a FieldConstraints property. ` +\n `Expected one of: ${Object.keys(CONSTRAINT_KEYS).join(\", \")}. Skipping it.`,\n );\n return false;\n }\n\n const expected = CONSTRAINT_KEYS[key as keyof typeof CONSTRAINT_KEYS];\n const initializer = property.getInitializerOrThrow();\n const value =\n expected === \"number\"\n ? readNumberLiteral(initializer)\n : readStringLiteral(initializer);\n\n if (value === undefined) {\n scope.context.warn(\n scope,\n `@Constrain value for \"${key}\" is \\`${initializer.getText()}\\`, which is not ` +\n `a ${expected} literal the compiler can read from source. Skipping it.`,\n );\n return false;\n }\n\n (constraints as Record<string, number | string>)[key] = value;\n return true;\n}\n\n/**\n * Extract the @Constrain bounds from a property declaration.\n *\n * The decorator's argument has to be an inline object literal: decorators are\n * never evaluated, so a value that is not written out in source cannot be\n * resolved. Unreadable and unknown entries are warned about and skipped\n * individually, so one bad bound does not discard the rest.\n *\n * Returns undefined if there is no @Constrain decorator, or if nothing in it\n * could be read.\n */\nexport function parseConstrainDecorator(\n propDecl: PropertyDeclaration,\n scope: FieldScope,\n): FieldConstraints | undefined {\n const decorator = propDecl.getDecorator(\"Constrain\");\n if (!decorator) {\n return undefined;\n }\n\n const args = decorator.isDecoratorFactory() ? decorator.getArguments() : [];\n const argument = args[0];\n if (argument === undefined || !Node.isObjectLiteralExpression(argument)) {\n scope.context.warn(\n scope,\n `@Constrain expects an inline object literal of compile-time constants, but ` +\n `${argument === undefined ? \"it was called with no argument\" : `was given \\`${argument.getText()}\\``}. ` +\n `Ignoring the decorator.`,\n );\n return undefined;\n }\n\n const constraints: FieldConstraints = {};\n let accepted = 0;\n for (const property of argument.getProperties()) {\n if (readConstraintEntry(property, constraints, scope)) {\n accepted += 1;\n }\n }\n\n return accepted > 0 ? constraints : undefined;\n}\n\n/**\n * Extract the enum source id from a property's @ValuesFrom decorator.\n *\n * Returns undefined if there is no @ValuesFrom decorator, or if its argument\n * is not a string literal.\n */\nexport function parseValuesFromDecorator(\n propDecl: PropertyDeclaration,\n scope: FieldScope,\n): string | undefined {\n const decorator = propDecl.getDecorator(\"ValuesFrom\");\n if (!decorator) {\n return undefined;\n }\n\n const sourceId = getDecoratorStringArg(decorator);\n if (sourceId === undefined) {\n scope.context.warn(\n scope,\n `@ValuesFrom expects a string literal naming the enum source, which the caller ` +\n `resolves at coercion time. Ignoring the decorator.`,\n );\n return undefined;\n }\n return sourceId;\n}\n","import { createHash } from \"node:crypto\";\nimport type { Node } from \"ts-morph\";\nimport type { RuntimeSchema } from \"@sembl/core\";\n\n/**\n * Where a field sits in the source, carried through type resolution and\n * decorator parsing.\n *\n * Every diagnostic names the class and the property, because a warning that\n * only says \"unsupported type\" sends the reader hunting through the whole\n * input directory for it.\n */\nexport interface FieldScope {\n /** Name of the @Schema class that owns the field. */\n className: string;\n /** Property name, dotted through synthesized inline objects. */\n propertyPath: string;\n /**\n * Declaration the type was read from. Members of an anonymous object type\n * have no declaration of their own to resolve against, so they are typed\n * relative to this node.\n */\n node: Node;\n /** Collector for diagnostics and synthesized schemas. */\n context: ExtractionContext;\n}\n\n/**\n * An `object` field type pointing at another schema by id, remembered so the\n * id can be checked once every source file has been visited.\n */\ninterface NestedReference {\n className: string;\n propertyPath: string;\n nestedSchemaId: string;\n /** Source text of the type, for a diagnostic the reader can act on. */\n typeText: string;\n}\n\n/**\n * Accumulates everything an extraction produces beyond the schemas themselves:\n * diagnostics, schemas synthesized for anonymous object types, and the nested\n * schema references that can only be validated once all files are visited.\n */\nexport interface ExtractionContext {\n /** Diagnostics raised so far, in discovery order. */\n readonly warnings: readonly string[];\n /** Schemas synthesized for anonymous inline object types, keyed by id. */\n readonly synthesizedSchemas: Readonly<Record<string, RuntimeSchema>>;\n /** Record a diagnostic against a field. */\n warn(scope: FieldScope, message: string): void;\n /** Add a schema synthesized for an inline object type. */\n registerSynthesizedSchema(schema: RuntimeSchema): void;\n /** Note that a field points at `nestedSchemaId`, to be checked later. */\n recordNestedReference(\n scope: FieldScope,\n nestedSchemaId: string,\n typeText: string,\n ): void;\n /**\n * Warn about every recorded reference whose target is not among\n * `knownSchemaIds`. Call once, after all classes have been visited.\n */\n reportUnresolvedNestedSchemas(knownSchemaIds: ReadonlySet<string>): void;\n}\n\n/**\n * Create an empty {@link ExtractionContext} for a single extraction run.\n */\nexport function createExtractionContext(): ExtractionContext {\n const warnings: string[] = [];\n const synthesizedSchemas: Record<string, RuntimeSchema> = {};\n const nestedReferences: NestedReference[] = [];\n\n return {\n warnings,\n synthesizedSchemas,\n\n warn(scope, message) {\n warnings.push(`${scope.className}.${scope.propertyPath}: ${message}`);\n },\n\n registerSynthesizedSchema(schema) {\n synthesizedSchemas[schema.id] = schema;\n },\n\n recordNestedReference(scope, nestedSchemaId, typeText) {\n nestedReferences.push({\n className: scope.className,\n propertyPath: scope.propertyPath,\n nestedSchemaId,\n typeText,\n });\n },\n\n reportUnresolvedNestedSchemas(knownSchemaIds) {\n for (const reference of nestedReferences) {\n if (knownSchemaIds.has(reference.nestedSchemaId)) {\n continue;\n }\n // A named object type that is not a @Schema class — a `Date`, a `Map`,\n // a plain interface — resolves to an id nothing in the bundle answers\n // to, and emits as an object with no properties. Nothing fails; the\n // field just comes back empty at runtime.\n warnings.push(\n `${reference.className}.${reference.propertyPath}: type \\`${reference.typeText}\\` ` +\n `resolves to nested schema \"${reference.nestedSchemaId}\", which is not a ` +\n `@Schema-decorated class in this extraction. It will emit as an object with ` +\n `no properties. Decorate it with @Schema if it is yours to change, or use a type ` +\n `the contract supports — a \\`Date\\`, for instance, extracts as an ISO-8601 string.`,\n );\n }\n },\n };\n}\n\n/**\n * Derive the id of a schema synthesized from an anonymous object type.\n *\n * The id becomes a filename and an exported binding in the generated output\n * (`<id>.schema.ts`, `<id>Schema`), so it has to be a valid identifier. The\n * path prefix keeps generated files traceable back to the declaration they\n * came from; the digest of the resolved shape makes the id collision-resistant\n * against a hand-written @Schema class and stable across runs and machines —\n * it is derived from the resolved fields, never from absolute source paths.\n */\nexport function synthesizedSchemaId(\n scope: FieldScope,\n structuralSignature: string,\n): string {\n const path = `${scope.className}_${scope.propertyPath}`.replace(\n /[^A-Za-z0-9_]/g,\n \"_\",\n );\n const digest = createHash(\"sha256\")\n .update(structuralSignature)\n .digest(\"hex\")\n .slice(0, 8);\n return `${path}__${digest}`;\n}\n","import type { Type } from \"ts-morph\";\nimport type { FieldDescriptor, FieldType } from \"@sembl/core\";\nimport { synthesizedSchemaId, type FieldScope } from \"./extraction-context.js\";\n\n/**\n * Report a type the schema contract cannot express and fall back to a string.\n *\n * The fallback keeps the rest of the extraction going, but the warning is the\n * point: a field silently mistyped as a string still builds, still validates,\n * and only shows up as a wrong extraction at runtime.\n */\nfunction reportUnsupported(\n type: Type,\n scope: FieldScope,\n reason: string,\n): FieldType {\n scope.context.warn(\n scope,\n `unsupported type \\`${type.getText(scope.node)}\\` — ${reason}. Falling back to string.`,\n );\n return { kind: \"string\" };\n}\n\n/**\n * Build a schema for an anonymous object type (`{ description: string }`) and\n * register it so it is emitted like any hand-written one.\n *\n * Without this an inline type resolves to an id no schema answers to, and the\n * field reaches the model as an object with no properties.\n */\nfunction resolveInlineObjectType(type: Type, scope: FieldScope): FieldType {\n const properties = type.getProperties();\n if (properties.length === 0) {\n // An index signature declares no properties to extract, and FieldType has\n // no map kind to express one. The contract is frozen, so there is nothing\n // correct to emit — only something to say.\n const isMap =\n type.getStringIndexType() !== undefined ||\n type.getNumberIndexType() !== undefined;\n return reportUnsupported(\n type,\n scope,\n isMap\n ? \"a map with an index signature has no FieldType equivalent, so its entries cannot be \" +\n \"described to the model; declare a @Schema class with the keys you expect, or take \" +\n \"the value as a JSON string and parse it yourself\"\n : \"an object type with no properties has nothing to extract\",\n );\n }\n\n const fields: FieldDescriptor[] = properties.map((property) => {\n const name = property.getName();\n return {\n name,\n // Members of an inline type carry no @Describe, so the owning field's\n // description is the only semantics the model gets for them.\n description: \"\",\n type: resolveFieldType(property.getTypeAtLocation(scope.node), {\n ...scope,\n propertyPath: `${scope.propertyPath}.${name}`,\n }),\n required: !property.isOptional(),\n };\n });\n\n // Hash the resolved fields rather than the source text: type text can embed\n // absolute import paths, which would make the id differ between machines.\n const id = synthesizedSchemaId(\n scope,\n JSON.stringify(fields.map((f) => [f.name, f.required, f.type])),\n );\n scope.context.registerSynthesizedSchema({\n id,\n description: `Inline object type declared at ${scope.className}.${scope.propertyPath}.`,\n fields,\n });\n return { kind: \"object\", nestedSchemaId: id };\n}\n\n/**\n * Resolve a TypeScript type to a FieldType descriptor.\n *\n * Maps TS types to the schema type system: string, number, boolean, array,\n * object, enum. Anything the contract cannot express is reported through\n * `scope.context` rather than quietly coerced.\n */\nexport function resolveFieldType(type: Type, scope: FieldScope): FieldType {\n // Optional and nullable fields arrive as unions with undefined/null. The\n // question mark already carries optionality, so resolve the value type.\n if (type.isUnion()) {\n const members = type\n .getUnionTypes()\n .filter((t) => !t.isUndefined() && !t.isNull());\n\n if (members.length === 1) {\n return resolveFieldType(members[0], scope);\n }\n\n if (members.length === 0) {\n return reportUnsupported(type, scope, \"there is no value type to extract\");\n }\n\n if (members.every((t) => t.isStringLiteral())) {\n return {\n kind: \"enum\",\n values: members.map((t) => t.getLiteralValue() as string),\n };\n }\n\n // `boolean` is modelled as `true | false`, so an optional boolean would\n // otherwise look like a mixed union and fall through to the fallback.\n if (members.every((t) => t.isBoolean() || t.isBooleanLiteral())) {\n return { kind: \"boolean\" };\n }\n\n // A numeric enum, or a union of number literals, widens to number:\n // FieldType has no numeric enum kind to narrow it to.\n if (members.every((t) => t.isNumber() || t.isNumberLiteral())) {\n return { kind: \"number\" };\n }\n\n return reportUnsupported(\n type,\n scope,\n \"a union mixing several kinds of value has no single FieldType; split it into \" +\n \"separate fields, or narrow it to one kind\",\n );\n }\n\n if (type.isString() || type.isStringLiteral()) {\n return { kind: \"string\" };\n }\n\n if (type.isNumber() || type.isNumberLiteral()) {\n return { kind: \"number\" };\n }\n\n if (type.isBoolean() || type.isBooleanLiteral()) {\n return { kind: \"boolean\" };\n }\n\n if (type.isArray()) {\n const elementType = type.getArrayElementTypeOrThrow();\n return { kind: \"array\", items: resolveFieldType(elementType, scope) };\n }\n\n if (type.isEnum()) {\n const members = type\n .getUnionTypes()\n .map((t) => t.getLiteralValue())\n .filter((v): v is string => typeof v === \"string\");\n if (members.length > 0) {\n return { kind: \"enum\", values: members };\n }\n return reportUnsupported(\n type,\n scope,\n \"its members are not string values, so they cannot be offered to the model as an enum\",\n );\n }\n\n if (type.isObject()) {\n const symbol = type.getSymbol() ?? type.getAliasSymbol();\n const typeName = symbol?.getName();\n if (typeName && typeName !== \"__type\" && typeName !== \"Object\") {\n // Assume a named object type is another @Schema class. Whether it really\n // is one cannot be known until every file has been visited, so record it\n // for the check in reportUnresolvedNestedSchemas.\n scope.context.recordNestedReference(\n scope,\n typeName,\n type.getText(scope.node),\n );\n return { kind: \"object\", nestedSchemaId: typeName };\n }\n return resolveInlineObjectType(type, scope);\n }\n\n return reportUnsupported(\n type,\n scope,\n \"it maps to none of string, number, boolean, array, enum, or a @Schema class\",\n );\n}\n","import type { ClassDeclaration } from \"ts-morph\";\nimport type { RuntimeSchema, FieldDescriptor, FieldType } from \"@sembl/core\";\nimport {\n parseSchemaDecorator,\n parseDescribeDecorator,\n parseConstrainDecorator,\n parseValuesFromDecorator,\n} from \"./decorator-parser.js\";\nimport { resolveFieldType } from \"./type-resolver.js\";\nimport type { ExtractionContext, FieldScope } from \"./extraction-context.js\";\n\n/**\n * Render a FieldType as something readable in a diagnostic.\n */\nfunction describeKind(type: FieldType): string {\n return type.kind === \"array\" ? `${describeKind(type.items)}[]` : type.kind;\n}\n\n/**\n * Point a resolved field type at a runtime-resolved enum source.\n *\n * Only a string, or an array of strings, has values for a source to constrain.\n * Anywhere else the decorator is on the wrong field — a mistake worth hearing\n * about at build time rather than discovering as an annotation that did\n * nothing, so the type is left alone and the field still extracts.\n */\nfunction applyValuesFrom(\n type: FieldType,\n sourceId: string,\n scope: FieldScope,\n): FieldType {\n if (type.kind === \"string\") {\n return { kind: \"dynamicEnum\", sourceId };\n }\n if (type.kind === \"array\" && type.items.kind === \"string\") {\n return { kind: \"array\", items: { kind: \"dynamicEnum\", sourceId } };\n }\n scope.context.warn(\n scope,\n `@ValuesFrom(\"${sourceId}\") applies to a string or string[] field, but this field ` +\n `resolved to ${describeKind(type)}. Leaving the type unchanged.`,\n );\n return type;\n}\n\n/**\n * Visit a class declaration and extract a RuntimeSchema if it has @Schema decorator.\n * Returns undefined if the class is not decorated with @Schema.\n *\n * Diagnostics and schemas synthesized for inline object types are collected\n * into `context` rather than thrown, so one questionable field does not stop\n * the rest of the extraction.\n */\nexport function visitClass(\n classDecl: ClassDeclaration,\n context: ExtractionContext,\n): RuntimeSchema | undefined {\n const description = parseSchemaDecorator(classDecl);\n if (description === undefined) {\n return undefined;\n }\n\n const className = classDecl.getName();\n if (!className) {\n return undefined;\n }\n\n const fields: FieldDescriptor[] = [];\n\n for (const prop of classDecl.getProperties()) {\n const fieldDescription = parseDescribeDecorator(prop);\n if (fieldDescription === undefined) {\n continue;\n }\n\n const name = prop.getName();\n const isOptional = prop.hasQuestionToken();\n const scope: FieldScope = {\n className,\n propertyPath: name,\n node: prop,\n context,\n };\n\n let type = resolveFieldType(prop.getType(), scope);\n\n // @ValuesFrom rewrites the resolved type, so it has to run after the type\n // is known — it is the declared TS type that decides whether the source\n // can apply at all.\n const sourceId = parseValuesFromDecorator(prop, scope);\n if (sourceId !== undefined) {\n type = applyValuesFrom(type, sourceId, scope);\n }\n\n const constraints = parseConstrainDecorator(prop, scope);\n\n fields.push({\n name,\n description: fieldDescription,\n type,\n required: !isOptional,\n ...(constraints !== undefined ? { constraints } : {}),\n });\n }\n\n return {\n id: className,\n description,\n fields,\n };\n}\n","import { mkdirSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { RuntimeSchema, SchemaBundle } from \"@sembl/core\";\n\n/**\n * Serialize a RuntimeSchema to a TypeScript source string.\n */\nfunction schemaToSource(schema: RuntimeSchema): string {\n const json = JSON.stringify(schema, null, 2);\n return `// Auto-generated by sembl extract — do not edit\nimport type { RuntimeSchema } from \"@sembl/core\";\n\nexport const ${schema.id}Schema: RuntimeSchema = ${json};\n`;\n}\n\n/**\n * Emit a SchemaBundle as a set of individual .schema.ts files\n * plus a bundle index file.\n */\nexport function emitSchemas(bundle: SchemaBundle, outputDir: string): string[] {\n mkdirSync(outputDir, { recursive: true });\n\n const emittedFiles: string[] = [];\n const schemaIds: string[] = [];\n\n for (const [id, schema] of Object.entries(bundle.schemas)) {\n const fileName = `${id}.schema.ts`;\n const filePath = join(outputDir, fileName);\n writeFileSync(filePath, schemaToSource(schema), \"utf-8\");\n emittedFiles.push(filePath);\n schemaIds.push(id);\n }\n\n // Emit bundle index file\n const indexLines = [\n \"// Auto-generated by sembl extract — do not edit\",\n 'import type { SchemaBundle } from \"@sembl/core\";',\n \"\",\n ];\n\n for (const id of schemaIds) {\n indexLines.push(`import { ${id}Schema } from \"./${id}.schema.js\";`);\n }\n\n indexLines.push(\"\");\n indexLines.push(\"export const bundle: SchemaBundle = {\");\n indexLines.push(\" schemas: {\");\n for (const id of schemaIds) {\n indexLines.push(` ${id}: ${id}Schema,`);\n }\n indexLines.push(\" },\");\n indexLines.push(\"};\");\n indexLines.push(\"\");\n\n // Re-export individual schemas\n for (const id of schemaIds) {\n indexLines.push(`export { ${id}Schema } from \"./${id}.schema.js\";`);\n }\n indexLines.push(\"\");\n\n const indexPath = join(outputDir, \"index.ts\");\n writeFileSync(indexPath, indexLines.join(\"\\n\"), \"utf-8\");\n emittedFiles.push(indexPath);\n\n return emittedFiles;\n}\n","import { pathToFileURL } from \"node:url\";\nimport { resolve, join } from \"node:path\";\nimport type { Provider, RuntimeSchema, SchemaBundle, EnumResolver, CoerceOptions } from \"@sembl/core\";\nimport {\n runEval,\n loadFixtures,\n loadReport,\n saveReport,\n diffReports,\n formatReport,\n replayOrRecord,\n} from \"@sembl/testing\";\nimport type { TokenPrices, EvalReport, EvalDiff } from \"@sembl/testing\";\n\nexport interface EvalCommandOptions {\n /** A JS module exporting the schema and provider to evaluate with. */\n config: string;\n /** Directory of fixture JSON files. */\n fixtures: string;\n /** Where to write the report; defaults to `<fixtures>/.sembl-eval/last-run.json`. */\n out?: string;\n mode?: \"coerce\" | \"partialCoerce\";\n provenance?: boolean;\n concurrency?: number;\n /** Replay recordings from this directory, recording misses through the provider. */\n replay?: string;\n /** Exit non-zero when overall recall lands below this fraction. */\n minRecall?: number;\n /** Exit non-zero when overall precision lands below this fraction. */\n minPrecision?: number;\n}\n\n/**\n * What an eval config module exports. Written as a plain JS module so the\n * CLI can `import()` it without a TypeScript loader; a TS project can point\n * at its build output, or keep the config in `.mjs`.\n */\nexport interface EvalConfig {\n schema: RuntimeSchema;\n provider: Provider;\n bundle?: SchemaBundle;\n enumResolver?: EnumResolver;\n prices?: TokenPrices;\n /** Any other coercion options: `onInvalidField`, `maxRepairAttempts`, `maxInputChars`, … */\n coerceOptions?: Partial<Omit<CoerceOptions, \"schema\" | \"provider\" | \"bundle\" | \"enumResolver\">>;\n}\n\nexport interface EvalCommandResult {\n report?: EvalReport;\n diff?: EvalDiff;\n exitCode: number;\n}\n\nasync function loadConfig(path: string): Promise<EvalConfig> {\n const url = pathToFileURL(resolve(path)).href;\n const mod = (await import(url)) as { default?: Partial<EvalConfig> } & Partial<EvalConfig>;\n const config: Partial<EvalConfig> = mod.default ?? mod;\n if (!config.schema || typeof config.schema !== \"object\" || !(\"id\" in config.schema)) {\n throw new Error(`${path} must export a \\`schema\\` (a RuntimeSchema or defineSchema result)`);\n }\n if (!config.provider || typeof config.provider.complete !== \"function\") {\n throw new Error(`${path} must export a \\`provider\\``);\n }\n return config as EvalConfig;\n}\n\nexport async function evalCommand(options: EvalCommandOptions): Promise<EvalCommandResult> {\n let config: EvalConfig;\n let fixtures;\n try {\n config = await loadConfig(options.config);\n fixtures = loadFixtures(options.fixtures);\n } catch (error) {\n console.error(`sembl eval: ${error instanceof Error ? error.message : String(error)}`);\n return { exitCode: 1 };\n }\n if (fixtures.length === 0) {\n console.error(`sembl eval: no fixtures found in ${resolve(options.fixtures)}`);\n return { exitCode: 1 };\n }\n\n const provider = options.replay ? replayOrRecord(resolve(options.replay), config.provider) : config.provider;\n const out = options.out ?? join(resolve(options.fixtures), \".sembl-eval\", \"last-run.json\");\n const previous = loadReport(out);\n\n const report = await runEval({\n ...config.coerceOptions,\n schema: config.schema,\n bundle: config.bundle,\n enumResolver: config.enumResolver,\n prices: config.prices,\n provider,\n fixtures,\n mode: options.mode,\n provenance: options.provenance,\n concurrency: options.concurrency,\n });\n const diff = previous ? diffReports(previous, report) : undefined;\n\n console.log(formatReport(report, diff));\n saveReport(report, out);\n console.log(`\\nReport written to ${out}${previous ? \" (deltas are against the previous run)\" : \"\"}`);\n\n const below = (value: number | null, floor: number | undefined) =>\n floor !== undefined && (value === null || value < floor);\n let exitCode = 0;\n if (below(report.totals.recall, options.minRecall)) {\n console.error(`sembl eval: recall ${fmt(report.totals.recall)} is below --min-recall ${options.minRecall}`);\n exitCode = 1;\n }\n if (below(report.totals.precision, options.minPrecision)) {\n console.error(`sembl eval: precision ${fmt(report.totals.precision)} is below --min-precision ${options.minPrecision}`);\n exitCode = 1;\n }\n return { report, diff, exitCode };\n}\n\nfunction fmt(value: number | null): string {\n return value === null ? \"n/a\" : value.toFixed(2);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAAA,mBAAsC;;;ACAtC,sBAMO;AAWP,IAAM,kBAGF;AAAA,EACF,WAAW;AAAA,EACX,WAAW;AAAA,EACX,SAAS;AAAA,EACT,SAAS;AAAA,EACT,UAAU;AAAA,EACV,UAAU;AAAA,EACV,SAAS;AACX;AAMA,SAAS,sBAAsB,WAA0C;AACvE,MAAI,CAAC,UAAU,mBAAmB,GAAG;AACnC,WAAO;AAAA,EACT;AACA,QAAM,OAAO,UAAU,aAAa;AACpC,MAAI,KAAK,WAAW,GAAG;AACrB,WAAO;AAAA,EACT;AACA,QAAM,MAAM,KAAK,CAAC;AAElB,QAAM,OAAO,IAAI,QAAQ;AACzB,MACG,KAAK,WAAW,GAAG,KAAK,KAAK,SAAS,GAAG,KACzC,KAAK,WAAW,GAAG,KAAK,KAAK,SAAS,GAAG,GAC1C;AACA,WAAO,KAAK,MAAM,GAAG,EAAE;AAAA,EACzB;AAEA,MAAI,KAAK,WAAW,GAAG,KAAK,KAAK,SAAS,GAAG,GAAG;AAC9C,WAAO,KAAK,MAAM,GAAG,EAAE;AAAA,EACzB;AACA,SAAO;AACT;AAMO,SAAS,qBACd,WACoB;AACpB,QAAM,YAAY,UAAU,aAAa,QAAQ;AACjD,MAAI,CAAC,WAAW;AACd,WAAO;AAAA,EACT;AACA,SAAO,sBAAsB,SAAS;AACxC;AAMO,SAAS,uBACd,UACoB;AACpB,QAAM,YAAY,SAAS,aAAa,UAAU;AAClD,MAAI,CAAC,WAAW;AACd,WAAO;AAAA,EACT;AACA,SAAO,sBAAsB,SAAS;AACxC;AAQA,SAAS,kBAAkB,MAAgC;AACzD,MAAI,qBAAK,iBAAiB,IAAI,GAAG;AAC/B,WAAO,KAAK,gBAAgB;AAAA,EAC9B;AACA,MAAI,qBAAK,wBAAwB,IAAI,GAAG;AACtC,UAAM,UAAU,KAAK,WAAW;AAChC,QAAI,qBAAK,iBAAiB,OAAO,GAAG;AAClC,YAAM,WAAW,KAAK,iBAAiB;AACvC,UAAI,aAAa,2BAAW,YAAY;AACtC,eAAO,CAAC,QAAQ,gBAAgB;AAAA,MAClC;AACA,UAAI,aAAa,2BAAW,WAAW;AACrC,eAAO,QAAQ,gBAAgB;AAAA,MACjC;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAKA,SAAS,kBAAkB,MAAgC;AACzD,MAAI,qBAAK,gBAAgB,IAAI,KAAK,qBAAK,gCAAgC,IAAI,GAAG;AAC5E,WAAO,KAAK,gBAAgB;AAAA,EAC9B;AACA,SAAO;AACT;AAMA,SAAS,oBACP,UACA,aACA,OACS;AACT,MAAI,CAAC,qBAAK,qBAAqB,QAAQ,GAAG;AAGxC,UAAM,QAAQ;AAAA,MACZ;AAAA,MACA,sBAAsB,SAAS,QAAQ,CAAC;AAAA,IAE1C;AACA,WAAO;AAAA,EACT;AAEA,QAAM,WAAW,SAAS,YAAY;AACtC,MAAI,CAAC,qBAAK,aAAa,QAAQ,KAAK,CAAC,qBAAK,gBAAgB,QAAQ,GAAG;AACnE,UAAM,QAAQ;AAAA,MACZ;AAAA,MACA,oBAAoB,SAAS,QAAQ,CAAC;AAAA,IACxC;AACA,WAAO;AAAA,EACT;AAEA,QAAM,MAAM,SAAS,QAAQ,EAAE,QAAQ,gBAAgB,EAAE;AACzD,MAAI,CAAC,OAAO,UAAU,eAAe,KAAK,iBAAiB,GAAG,GAAG;AAC/D,UAAM,QAAQ;AAAA,MACZ;AAAA,MACA,mBAAmB,GAAG,0DACA,OAAO,KAAK,eAAe,EAAE,KAAK,IAAI,CAAC;AAAA,IAC/D;AACA,WAAO;AAAA,EACT;AAEA,QAAM,WAAW,gBAAgB,GAAmC;AACpE,QAAM,cAAc,SAAS,sBAAsB;AACnD,QAAM,QACJ,aAAa,WACT,kBAAkB,WAAW,IAC7B,kBAAkB,WAAW;AAEnC,MAAI,UAAU,QAAW;AACvB,UAAM,QAAQ;AAAA,MACZ;AAAA,MACA,yBAAyB,GAAG,UAAU,YAAY,QAAQ,CAAC,sBACpD,QAAQ;AAAA,IACjB;AACA,WAAO;AAAA,EACT;AAEA,EAAC,YAAgD,GAAG,IAAI;AACxD,SAAO;AACT;AAaO,SAAS,wBACd,UACA,OAC8B;AAC9B,QAAM,YAAY,SAAS,aAAa,WAAW;AACnD,MAAI,CAAC,WAAW;AACd,WAAO;AAAA,EACT;AAEA,QAAM,OAAO,UAAU,mBAAmB,IAAI,UAAU,aAAa,IAAI,CAAC;AAC1E,QAAM,WAAW,KAAK,CAAC;AACvB,MAAI,aAAa,UAAa,CAAC,qBAAK,0BAA0B,QAAQ,GAAG;AACvE,UAAM,QAAQ;AAAA,MACZ;AAAA,MACA,8EACK,aAAa,SAAY,mCAAmC,eAAe,SAAS,QAAQ,CAAC,IAAI;AAAA,IAExG;AACA,WAAO;AAAA,EACT;AAEA,QAAM,cAAgC,CAAC;AACvC,MAAI,WAAW;AACf,aAAW,YAAY,SAAS,cAAc,GAAG;AAC/C,QAAI,oBAAoB,UAAU,aAAa,KAAK,GAAG;AACrD,kBAAY;AAAA,IACd;AAAA,EACF;AAEA,SAAO,WAAW,IAAI,cAAc;AACtC;AAQO,SAAS,yBACd,UACA,OACoB;AACpB,QAAM,YAAY,SAAS,aAAa,YAAY;AACpD,MAAI,CAAC,WAAW;AACd,WAAO;AAAA,EACT;AAEA,QAAM,WAAW,sBAAsB,SAAS;AAChD,MAAI,aAAa,QAAW;AAC1B,UAAM,QAAQ;AAAA,MACZ;AAAA,MACA;AAAA,IAEF;AACA,WAAO;AAAA,EACT;AACA,SAAO;AACT;;;ACxPA,yBAA2B;AAqEpB,SAAS,0BAA6C;AAC3D,QAAM,WAAqB,CAAC;AAC5B,QAAM,qBAAoD,CAAC;AAC3D,QAAM,mBAAsC,CAAC;AAE7C,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IAEA,KAAK,OAAO,SAAS;AACnB,eAAS,KAAK,GAAG,MAAM,SAAS,IAAI,MAAM,YAAY,KAAK,OAAO,EAAE;AAAA,IACtE;AAAA,IAEA,0BAA0B,QAAQ;AAChC,yBAAmB,OAAO,EAAE,IAAI;AAAA,IAClC;AAAA,IAEA,sBAAsB,OAAO,gBAAgB,UAAU;AACrD,uBAAiB,KAAK;AAAA,QACpB,WAAW,MAAM;AAAA,QACjB,cAAc,MAAM;AAAA,QACpB;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH;AAAA,IAEA,8BAA8B,gBAAgB;AAC5C,iBAAW,aAAa,kBAAkB;AACxC,YAAI,eAAe,IAAI,UAAU,cAAc,GAAG;AAChD;AAAA,QACF;AAKA,iBAAS;AAAA,UACP,GAAG,UAAU,SAAS,IAAI,UAAU,YAAY,YAAY,UAAU,QAAQ,iCAC9C,UAAU,cAAc;AAAA,QAI1D;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAYO,SAAS,oBACd,OACA,qBACQ;AACR,QAAM,OAAO,GAAG,MAAM,SAAS,IAAI,MAAM,YAAY,GAAG;AAAA,IACtD;AAAA,IACA;AAAA,EACF;AACA,QAAM,aAAS,+BAAW,QAAQ,EAC/B,OAAO,mBAAmB,EAC1B,OAAO,KAAK,EACZ,MAAM,GAAG,CAAC;AACb,SAAO,GAAG,IAAI,KAAK,MAAM;AAC3B;;;AChIA,SAAS,kBACP,MACA,OACA,QACW;AACX,QAAM,QAAQ;AAAA,IACZ;AAAA,IACA,sBAAsB,KAAK,QAAQ,MAAM,IAAI,CAAC,aAAQ,MAAM;AAAA,EAC9D;AACA,SAAO,EAAE,MAAM,SAAS;AAC1B;AASA,SAAS,wBAAwB,MAAY,OAA8B;AACzE,QAAM,aAAa,KAAK,cAAc;AACtC,MAAI,WAAW,WAAW,GAAG;AAI3B,UAAM,QACJ,KAAK,mBAAmB,MAAM,UAC9B,KAAK,mBAAmB,MAAM;AAChC,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,QACI,2NAGA;AAAA,IACN;AAAA,EACF;AAEA,QAAM,SAA4B,WAAW,IAAI,CAAC,aAAa;AAC7D,UAAM,OAAO,SAAS,QAAQ;AAC9B,WAAO;AAAA,MACL;AAAA;AAAA;AAAA,MAGA,aAAa;AAAA,MACb,MAAM,iBAAiB,SAAS,kBAAkB,MAAM,IAAI,GAAG;AAAA,QAC7D,GAAG;AAAA,QACH,cAAc,GAAG,MAAM,YAAY,IAAI,IAAI;AAAA,MAC7C,CAAC;AAAA,MACD,UAAU,CAAC,SAAS,WAAW;AAAA,IACjC;AAAA,EACF,CAAC;AAID,QAAM,KAAK;AAAA,IACT;AAAA,IACA,KAAK,UAAU,OAAO,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC;AAAA,EAChE;AACA,QAAM,QAAQ,0BAA0B;AAAA,IACtC;AAAA,IACA,aAAa,kCAAkC,MAAM,SAAS,IAAI,MAAM,YAAY;AAAA,IACpF;AAAA,EACF,CAAC;AACD,SAAO,EAAE,MAAM,UAAU,gBAAgB,GAAG;AAC9C;AASO,SAAS,iBAAiB,MAAY,OAA8B;AAGzE,MAAI,KAAK,QAAQ,GAAG;AAClB,UAAM,UAAU,KACb,cAAc,EACd,OAAO,CAAC,MAAM,CAAC,EAAE,YAAY,KAAK,CAAC,EAAE,OAAO,CAAC;AAEhD,QAAI,QAAQ,WAAW,GAAG;AACxB,aAAO,iBAAiB,QAAQ,CAAC,GAAG,KAAK;AAAA,IAC3C;AAEA,QAAI,QAAQ,WAAW,GAAG;AACxB,aAAO,kBAAkB,MAAM,OAAO,mCAAmC;AAAA,IAC3E;AAEA,QAAI,QAAQ,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,GAAG;AAC7C,aAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAQ,QAAQ,IAAI,CAAC,MAAM,EAAE,gBAAgB,CAAW;AAAA,MAC1D;AAAA,IACF;AAIA,QAAI,QAAQ,MAAM,CAAC,MAAM,EAAE,UAAU,KAAK,EAAE,iBAAiB,CAAC,GAAG;AAC/D,aAAO,EAAE,MAAM,UAAU;AAAA,IAC3B;AAIA,QAAI,QAAQ,MAAM,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,gBAAgB,CAAC,GAAG;AAC7D,aAAO,EAAE,MAAM,SAAS;AAAA,IAC1B;AAEA,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,IAEF;AAAA,EACF;AAEA,MAAI,KAAK,SAAS,KAAK,KAAK,gBAAgB,GAAG;AAC7C,WAAO,EAAE,MAAM,SAAS;AAAA,EAC1B;AAEA,MAAI,KAAK,SAAS,KAAK,KAAK,gBAAgB,GAAG;AAC7C,WAAO,EAAE,MAAM,SAAS;AAAA,EAC1B;AAEA,MAAI,KAAK,UAAU,KAAK,KAAK,iBAAiB,GAAG;AAC/C,WAAO,EAAE,MAAM,UAAU;AAAA,EAC3B;AAEA,MAAI,KAAK,QAAQ,GAAG;AAClB,UAAM,cAAc,KAAK,2BAA2B;AACpD,WAAO,EAAE,MAAM,SAAS,OAAO,iBAAiB,aAAa,KAAK,EAAE;AAAA,EACtE;AAEA,MAAI,KAAK,OAAO,GAAG;AACjB,UAAM,UAAU,KACb,cAAc,EACd,IAAI,CAAC,MAAM,EAAE,gBAAgB,CAAC,EAC9B,OAAO,CAAC,MAAmB,OAAO,MAAM,QAAQ;AACnD,QAAI,QAAQ,SAAS,GAAG;AACtB,aAAO,EAAE,MAAM,QAAQ,QAAQ,QAAQ;AAAA,IACzC;AACA,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,MAAI,KAAK,SAAS,GAAG;AACnB,UAAM,SAAS,KAAK,UAAU,KAAK,KAAK,eAAe;AACvD,UAAM,WAAW,QAAQ,QAAQ;AACjC,QAAI,YAAY,aAAa,YAAY,aAAa,UAAU;AAI9D,YAAM,QAAQ;AAAA,QACZ;AAAA,QACA;AAAA,QACA,KAAK,QAAQ,MAAM,IAAI;AAAA,MACzB;AACA,aAAO,EAAE,MAAM,UAAU,gBAAgB,SAAS;AAAA,IACpD;AACA,WAAO,wBAAwB,MAAM,KAAK;AAAA,EAC5C;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;ACzKA,SAAS,aAAa,MAAyB;AAC7C,SAAO,KAAK,SAAS,UAAU,GAAG,aAAa,KAAK,KAAK,CAAC,OAAO,KAAK;AACxE;AAUA,SAAS,gBACP,MACA,UACA,OACW;AACX,MAAI,KAAK,SAAS,UAAU;AAC1B,WAAO,EAAE,MAAM,eAAe,SAAS;AAAA,EACzC;AACA,MAAI,KAAK,SAAS,WAAW,KAAK,MAAM,SAAS,UAAU;AACzD,WAAO,EAAE,MAAM,SAAS,OAAO,EAAE,MAAM,eAAe,SAAS,EAAE;AAAA,EACnE;AACA,QAAM,QAAQ;AAAA,IACZ;AAAA,IACA,gBAAgB,QAAQ,wEACP,aAAa,IAAI,CAAC;AAAA,EACrC;AACA,SAAO;AACT;AAUO,SAAS,WACd,WACA,SAC2B;AAC3B,QAAM,cAAc,qBAAqB,SAAS;AAClD,MAAI,gBAAgB,QAAW;AAC7B,WAAO;AAAA,EACT;AAEA,QAAM,YAAY,UAAU,QAAQ;AACpC,MAAI,CAAC,WAAW;AACd,WAAO;AAAA,EACT;AAEA,QAAM,SAA4B,CAAC;AAEnC,aAAW,QAAQ,UAAU,cAAc,GAAG;AAC5C,UAAM,mBAAmB,uBAAuB,IAAI;AACpD,QAAI,qBAAqB,QAAW;AAClC;AAAA,IACF;AAEA,UAAM,OAAO,KAAK,QAAQ;AAC1B,UAAM,aAAa,KAAK,iBAAiB;AACzC,UAAM,QAAoB;AAAA,MACxB;AAAA,MACA,cAAc;AAAA,MACd,MAAM;AAAA,MACN;AAAA,IACF;AAEA,QAAI,OAAO,iBAAiB,KAAK,QAAQ,GAAG,KAAK;AAKjD,UAAM,WAAW,yBAAyB,MAAM,KAAK;AACrD,QAAI,aAAa,QAAW;AAC1B,aAAO,gBAAgB,MAAM,UAAU,KAAK;AAAA,IAC9C;AAEA,UAAM,cAAc,wBAAwB,MAAM,KAAK;AAEvD,WAAO,KAAK;AAAA,MACV;AAAA,MACA,aAAa;AAAA,MACb;AAAA,MACA,UAAU,CAAC;AAAA,MACX,GAAI,gBAAgB,SAAY,EAAE,YAAY,IAAI,CAAC;AAAA,IACrD,CAAC;AAAA,EACH;AAEA,SAAO;AAAA,IACL,IAAI;AAAA,IACJ;AAAA,IACA;AAAA,EACF;AACF;;;AJzFO,SAAS,eAAe,SAA2C;AACxE,QAAM,UAAU,IAAI,yBAAQ;AAAA,IAC1B,kBAAkB,QAAQ;AAAA,IAC1B,6BAA6B;AAAA,IAC7B,iBAAiB;AAAA,MACf,wBAAwB;AAAA,MACxB,QAAQ;AAAA;AAAA;AAAA;AAAA,MAIR,QAAQ,8BAAa;AAAA,IACvB;AAAA,EACF,CAAC;AAGD,aAAW,WAAW,QAAQ,cAAc;AAC1C,YAAQ,sBAAsB,OAAO;AAAA,EACvC;AAEA,QAAM,UAAU,wBAAwB;AACxC,QAAM,UAAyC,CAAC;AAEhD,aAAW,cAAc,QAAQ,eAAe,GAAG;AACjD,eAAW,aAAa,WAAW,WAAW,GAAG;AAC/C,YAAM,SAAS,WAAW,WAAW,OAAO;AAC5C,UAAI,QAAQ;AACV,gBAAQ,OAAO,EAAE,IAAI;AAAA,MACvB;AAAA,IACF;AAAA,EACF;AAIA,aAAW,CAAC,IAAI,MAAM,KAAK,OAAO,QAAQ,QAAQ,kBAAkB,GAAG;AACrE,YAAQ,EAAE,IAAI;AAAA,EAChB;AAIA,UAAQ,8BAA8B,IAAI,IAAI,OAAO,KAAK,OAAO,CAAC,CAAC;AAEnE,SAAO,EAAE,SAAS,UAAU,CAAC,GAAG,QAAQ,QAAQ,EAAE;AACpD;;;AK/DA,qBAAyC;AACzC,uBAAqB;AAMrB,SAAS,eAAe,QAA+B;AACrD,QAAM,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;AAC3C,SAAO;AAAA;AAAA;AAAA,eAGM,OAAO,EAAE,2BAA2B,IAAI;AAAA;AAEvD;AAMO,SAAS,YAAY,QAAsB,WAA6B;AAC7E,gCAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AAExC,QAAM,eAAyB,CAAC;AAChC,QAAM,YAAsB,CAAC;AAE7B,aAAW,CAAC,IAAI,MAAM,KAAK,OAAO,QAAQ,OAAO,OAAO,GAAG;AACzD,UAAM,WAAW,GAAG,EAAE;AACtB,UAAM,eAAW,uBAAK,WAAW,QAAQ;AACzC,sCAAc,UAAU,eAAe,MAAM,GAAG,OAAO;AACvD,iBAAa,KAAK,QAAQ;AAC1B,cAAU,KAAK,EAAE;AAAA,EACnB;AAGA,QAAM,aAAa;AAAA,IACjB;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,aAAW,MAAM,WAAW;AAC1B,eAAW,KAAK,YAAY,EAAE,oBAAoB,EAAE,cAAc;AAAA,EACpE;AAEA,aAAW,KAAK,EAAE;AAClB,aAAW,KAAK,uCAAuC;AACvD,aAAW,KAAK,cAAc;AAC9B,aAAW,MAAM,WAAW;AAC1B,eAAW,KAAK,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EAC3C;AACA,aAAW,KAAK,MAAM;AACtB,aAAW,KAAK,IAAI;AACpB,aAAW,KAAK,EAAE;AAGlB,aAAW,MAAM,WAAW;AAC1B,eAAW,KAAK,YAAY,EAAE,oBAAoB,EAAE,cAAc;AAAA,EACpE;AACA,aAAW,KAAK,EAAE;AAElB,QAAM,gBAAY,uBAAK,WAAW,UAAU;AAC5C,oCAAc,WAAW,WAAW,KAAK,IAAI,GAAG,OAAO;AACvD,eAAa,KAAK,SAAS;AAE3B,SAAO;AACT;;;AClEA,sBAA8B;AAC9B,IAAAC,oBAA8B;AAE9B,qBAQO;AA0CP,eAAe,WAAW,MAAmC;AAC3D,QAAM,UAAM,mCAAc,2BAAQ,IAAI,CAAC,EAAE;AACzC,QAAM,MAAO,MAAM,OAAO;AAC1B,QAAM,SAA8B,IAAI,WAAW;AACnD,MAAI,CAAC,OAAO,UAAU,OAAO,OAAO,WAAW,YAAY,EAAE,QAAQ,OAAO,SAAS;AACnF,UAAM,IAAI,MAAM,GAAG,IAAI,oEAAoE;AAAA,EAC7F;AACA,MAAI,CAAC,OAAO,YAAY,OAAO,OAAO,SAAS,aAAa,YAAY;AACtE,UAAM,IAAI,MAAM,GAAG,IAAI,6BAA6B;AAAA,EACtD;AACA,SAAO;AACT;AAEA,eAAsB,YAAY,SAAyD;AACzF,MAAI;AACJ,MAAI;AACJ,MAAI;AACF,aAAS,MAAM,WAAW,QAAQ,MAAM;AACxC,mBAAW,6BAAa,QAAQ,QAAQ;AAAA,EAC1C,SAAS,OAAO;AACd,YAAQ,MAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AACrF,WAAO,EAAE,UAAU,EAAE;AAAA,EACvB;AACA,MAAI,SAAS,WAAW,GAAG;AACzB,YAAQ,MAAM,wCAAoC,2BAAQ,QAAQ,QAAQ,CAAC,EAAE;AAC7E,WAAO,EAAE,UAAU,EAAE;AAAA,EACvB;AAEA,QAAM,WAAW,QAAQ,aAAS,mCAAe,2BAAQ,QAAQ,MAAM,GAAG,OAAO,QAAQ,IAAI,OAAO;AACpG,QAAM,MAAM,QAAQ,WAAO,4BAAK,2BAAQ,QAAQ,QAAQ,GAAG,eAAe,eAAe;AACzF,QAAM,eAAW,2BAAW,GAAG;AAE/B,QAAM,SAAS,UAAM,wBAAQ;AAAA,IAC3B,GAAG,OAAO;AAAA,IACV,QAAQ,OAAO;AAAA,IACf,QAAQ,OAAO;AAAA,IACf,cAAc,OAAO;AAAA,IACrB,QAAQ,OAAO;AAAA,IACf;AAAA,IACA;AAAA,IACA,MAAM,QAAQ;AAAA,IACd,YAAY,QAAQ;AAAA,IACpB,aAAa,QAAQ;AAAA,EACvB,CAAC;AACD,QAAM,OAAO,eAAW,4BAAY,UAAU,MAAM,IAAI;AAExD,UAAQ,QAAI,6BAAa,QAAQ,IAAI,CAAC;AACtC,iCAAW,QAAQ,GAAG;AACtB,UAAQ,IAAI;AAAA,oBAAuB,GAAG,GAAG,WAAW,2CAA2C,EAAE,EAAE;AAEnG,QAAM,QAAQ,CAAC,OAAsB,UACnC,UAAU,WAAc,UAAU,QAAQ,QAAQ;AACpD,MAAI,WAAW;AACf,MAAI,MAAM,OAAO,OAAO,QAAQ,QAAQ,SAAS,GAAG;AAClD,YAAQ,MAAM,sBAAsB,IAAI,OAAO,OAAO,MAAM,CAAC,0BAA0B,QAAQ,SAAS,EAAE;AAC1G,eAAW;AAAA,EACb;AACA,MAAI,MAAM,OAAO,OAAO,WAAW,QAAQ,YAAY,GAAG;AACxD,YAAQ,MAAM,yBAAyB,IAAI,OAAO,OAAO,SAAS,CAAC,6BAA6B,QAAQ,YAAY,EAAE;AACtH,eAAW;AAAA,EACb;AACA,SAAO,EAAE,QAAQ,MAAM,SAAS;AAClC;AAEA,SAAS,IAAI,OAA8B;AACzC,SAAO,UAAU,OAAO,QAAQ,MAAM,QAAQ,CAAC;AACjD;","names":["import_ts_morph","import_node_path"]}
|