@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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sembl/compiler",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Schema compiler for SEMBL: extracts runtime schemas from decorated TypeScript classes.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"llm",
|
|
@@ -23,13 +23,21 @@
|
|
|
23
23
|
"directory": "packages/compiler"
|
|
24
24
|
},
|
|
25
25
|
"type": "module",
|
|
26
|
-
"main": "./dist/index.
|
|
26
|
+
"main": "./dist/index.cjs",
|
|
27
|
+
"module": "./dist/index.js",
|
|
27
28
|
"types": "./dist/index.d.ts",
|
|
28
29
|
"exports": {
|
|
29
30
|
".": {
|
|
30
|
-
"
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
"import": {
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"default": "./dist/index.js"
|
|
34
|
+
},
|
|
35
|
+
"require": {
|
|
36
|
+
"types": "./dist/index.d.cts",
|
|
37
|
+
"default": "./dist/index.cjs"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"./package.json": "./package.json"
|
|
33
41
|
},
|
|
34
42
|
"files": [
|
|
35
43
|
"dist",
|
|
@@ -51,7 +59,8 @@
|
|
|
51
59
|
"commander": "^12.0.0",
|
|
52
60
|
"glob": "^11.0.0",
|
|
53
61
|
"ts-morph": "^24.0.0",
|
|
54
|
-
"@sembl/core": "0.
|
|
62
|
+
"@sembl/core": "0.2.0",
|
|
63
|
+
"@sembl/testing": "0.2.0"
|
|
55
64
|
},
|
|
56
65
|
"devDependencies": {
|
|
57
66
|
"@types/node": "^25.5.0",
|
|
@@ -59,7 +68,7 @@
|
|
|
59
68
|
"typescript": "^5.5.0"
|
|
60
69
|
},
|
|
61
70
|
"scripts": {
|
|
62
|
-
"build": "tsup",
|
|
71
|
+
"build": "rm -rf dist && tsup",
|
|
63
72
|
"dev": "tsup --watch"
|
|
64
73
|
}
|
|
65
74
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/extractor/decorator-parser.ts","../src/extractor/extraction-context.ts","../src/extractor/type-resolver.ts","../src/extractor/class-visitor.ts","../src/extractor/ast-extractor.ts","../src/generator/schema-emitter.ts"],"sourcesContent":["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 { 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 { 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"],"mappings":"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","names":[]}
|