@contractspec/lib.source-extractors 0.0.0-canary-20260119222405

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/README.md +86 -0
  2. package/dist/_virtual/rolldown_runtime.js +18 -0
  3. package/dist/codegen/index.d.ts +12 -0
  4. package/dist/codegen/index.d.ts.map +1 -0
  5. package/dist/codegen/index.js +17 -0
  6. package/dist/codegen/index.js.map +1 -0
  7. package/dist/codegen/operation-gen.d.ts +16 -0
  8. package/dist/codegen/operation-gen.d.ts.map +1 -0
  9. package/dist/codegen/operation-gen.js +91 -0
  10. package/dist/codegen/operation-gen.js.map +1 -0
  11. package/dist/codegen/registry-gen.d.ts +11 -0
  12. package/dist/codegen/registry-gen.d.ts.map +1 -0
  13. package/dist/codegen/registry-gen.js +47 -0
  14. package/dist/codegen/registry-gen.js.map +1 -0
  15. package/dist/codegen/schema-gen.d.ts +16 -0
  16. package/dist/codegen/schema-gen.d.ts.map +1 -0
  17. package/dist/codegen/schema-gen.js +93 -0
  18. package/dist/codegen/schema-gen.js.map +1 -0
  19. package/dist/codegen/types.d.ts +48 -0
  20. package/dist/codegen/types.d.ts.map +1 -0
  21. package/dist/detect.d.ts +47 -0
  22. package/dist/detect.d.ts.map +1 -0
  23. package/dist/detect.js +177 -0
  24. package/dist/detect.js.map +1 -0
  25. package/dist/extract.d.ts +24 -0
  26. package/dist/extract.d.ts.map +1 -0
  27. package/dist/extract.js +125 -0
  28. package/dist/extract.js.map +1 -0
  29. package/dist/extractors/base.d.ts +90 -0
  30. package/dist/extractors/base.d.ts.map +1 -0
  31. package/dist/extractors/base.js +152 -0
  32. package/dist/extractors/base.js.map +1 -0
  33. package/dist/extractors/elysia/extractor.d.ts +15 -0
  34. package/dist/extractors/elysia/extractor.d.ts.map +1 -0
  35. package/dist/extractors/elysia/extractor.js +58 -0
  36. package/dist/extractors/elysia/extractor.js.map +1 -0
  37. package/dist/extractors/express/extractor.d.ts +15 -0
  38. package/dist/extractors/express/extractor.d.ts.map +1 -0
  39. package/dist/extractors/express/extractor.js +61 -0
  40. package/dist/extractors/express/extractor.js.map +1 -0
  41. package/dist/extractors/fastify/extractor.d.ts +15 -0
  42. package/dist/extractors/fastify/extractor.d.ts.map +1 -0
  43. package/dist/extractors/fastify/extractor.js +61 -0
  44. package/dist/extractors/fastify/extractor.js.map +1 -0
  45. package/dist/extractors/hono/extractor.d.ts +15 -0
  46. package/dist/extractors/hono/extractor.d.ts.map +1 -0
  47. package/dist/extractors/hono/extractor.js +57 -0
  48. package/dist/extractors/hono/extractor.js.map +1 -0
  49. package/dist/extractors/index.d.ts +21 -0
  50. package/dist/extractors/index.d.ts.map +1 -0
  51. package/dist/extractors/index.js +42 -0
  52. package/dist/extractors/index.js.map +1 -0
  53. package/dist/extractors/nestjs/extractor.d.ts +29 -0
  54. package/dist/extractors/nestjs/extractor.d.ts.map +1 -0
  55. package/dist/extractors/nestjs/extractor.js +118 -0
  56. package/dist/extractors/nestjs/extractor.js.map +1 -0
  57. package/dist/extractors/next-api/extractor.d.ts +16 -0
  58. package/dist/extractors/next-api/extractor.d.ts.map +1 -0
  59. package/dist/extractors/next-api/extractor.js +80 -0
  60. package/dist/extractors/next-api/extractor.js.map +1 -0
  61. package/dist/extractors/trpc/extractor.d.ts +15 -0
  62. package/dist/extractors/trpc/extractor.d.ts.map +1 -0
  63. package/dist/extractors/trpc/extractor.js +71 -0
  64. package/dist/extractors/trpc/extractor.js.map +1 -0
  65. package/dist/extractors/zod/extractor.d.ts +16 -0
  66. package/dist/extractors/zod/extractor.d.ts.map +1 -0
  67. package/dist/extractors/zod/extractor.js +69 -0
  68. package/dist/extractors/zod/extractor.js.map +1 -0
  69. package/dist/index.d.ts +7 -0
  70. package/dist/index.js +7 -0
  71. package/dist/registry.d.ts +85 -0
  72. package/dist/registry.d.ts.map +1 -0
  73. package/dist/registry.js +87 -0
  74. package/dist/registry.js.map +1 -0
  75. package/dist/types.d.ts +272 -0
  76. package/dist/types.d.ts.map +1 -0
  77. package/dist/types.js +0 -0
  78. package/package.json +71 -0
package/README.md ADDED
@@ -0,0 +1,86 @@
1
+ # @contractspec/lib.source-extractors
2
+
3
+ Extract contract candidates from TypeScript source code across multiple frameworks.
4
+
5
+ ## Supported Frameworks
6
+
7
+ | Framework | Detection | Routes | Schemas | Status |
8
+ |-----------|-----------|--------|---------|--------|
9
+ | NestJS | ✅ | ✅ | ✅ | Ready |
10
+ | Express | ✅ | ✅ | ✅ | Ready |
11
+ | Fastify | ✅ | ✅ | ✅ | Ready |
12
+ | Hono | ✅ | ✅ | ✅ | Ready |
13
+ | Elysia | ✅ | ✅ | ✅ | Ready |
14
+ | tRPC | ✅ | ✅ | ✅ | Ready |
15
+ | Next.js API | ✅ | ✅ | ✅ | Ready |
16
+ | Zod Schemas | ✅ | N/A | ✅ | Ready |
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ bun add @contractspec/lib.source-extractors
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ ```typescript
27
+ import { detectFramework, extractFromProject } from '@contractspec/lib.source-extractors';
28
+ import { registerAllExtractors } from '@contractspec/lib.source-extractors/extractors';
29
+ import { generateOperations } from '@contractspec/lib.source-extractors/codegen';
30
+
31
+ // Register all built-in extractors
32
+ registerAllExtractors();
33
+
34
+ // Detect frameworks in a project
35
+ const project = await detectFramework('./my-project', {
36
+ readFile: (path) => fs.readFile(path, 'utf-8'),
37
+ glob: (pattern) => glob(pattern),
38
+ });
39
+
40
+ // Extract contract candidates
41
+ const result = await extractFromProject(project, {
42
+ scope: ['src/controllers'], // Optional: limit scope
43
+ });
44
+
45
+ if (result.success && result.ir) {
46
+ console.log(`Found ${result.ir.endpoints.length} endpoints`);
47
+ console.log(`Found ${result.ir.schemas.length} schemas`);
48
+
49
+ // Generate ContractSpec code
50
+ const files = generateOperations(result.ir, {
51
+ outputDir: './generated',
52
+ defaultAuth: 'user',
53
+ });
54
+ }
55
+ ```
56
+
57
+ ## IR Schema
58
+
59
+ The Intermediate Representation (IR) provides a framework-agnostic view of extracted contracts:
60
+
61
+ ```typescript
62
+ interface ImportIR {
63
+ version: '1.0';
64
+ extractedAt: string;
65
+ project: ProjectInfo;
66
+ endpoints: EndpointCandidate[];
67
+ schemas: SchemaCandidate[];
68
+ errors: ErrorCandidate[];
69
+ events: EventCandidate[];
70
+ ambiguities: Ambiguity[];
71
+ stats: { ... };
72
+ }
73
+ ```
74
+
75
+ ## Confidence Levels
76
+
77
+ Each extracted item has a confidence score:
78
+
79
+ - **high**: Explicit schema found (Zod, class-validator, JSON Schema)
80
+ - **medium**: TypeScript types or framework decorators present
81
+ - **low**: Inferred from naming conventions or partial information
82
+ - **ambiguous**: Requires manual review
83
+
84
+ ## License
85
+
86
+ MIT
@@ -0,0 +1,18 @@
1
+ //#region rolldown:runtime
2
+ var __defProp = Object.defineProperty;
3
+ var __exportAll = (all, symbols) => {
4
+ let target = {};
5
+ for (var name in all) {
6
+ __defProp(target, name, {
7
+ get: all[name],
8
+ enumerable: true
9
+ });
10
+ }
11
+ if (symbols) {
12
+ __defProp(target, Symbol.toStringTag, { value: "Module" });
13
+ }
14
+ return target;
15
+ };
16
+
17
+ //#endregion
18
+ export { __exportAll };
@@ -0,0 +1,12 @@
1
+ import { GeneratedFile, GenerationOptions, GenerationResult } from "./types.js";
2
+ import { generateOperation, generateOperations } from "./operation-gen.js";
3
+ import { generateSchema, generateSchemas } from "./schema-gen.js";
4
+ import { generateRegistry } from "./registry-gen.js";
5
+
6
+ //#region src/codegen/index.d.ts
7
+ declare namespace index_d_exports {
8
+ export { GeneratedFile, GenerationOptions, GenerationResult, generateOperation, generateOperations, generateRegistry, generateSchema, generateSchemas };
9
+ }
10
+ //#endregion
11
+ export { type GeneratedFile, type GenerationOptions, type GenerationResult, generateOperation, generateOperations, generateRegistry, generateSchema, generateSchemas, index_d_exports };
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/codegen/index.ts"],"sourcesContent":[],"mappings":""}
@@ -0,0 +1,17 @@
1
+ import { __exportAll } from "../_virtual/rolldown_runtime.js";
2
+ import { generateOperation, generateOperations } from "./operation-gen.js";
3
+ import { generateSchema, generateSchemas } from "./schema-gen.js";
4
+ import { generateRegistry } from "./registry-gen.js";
5
+
6
+ //#region src/codegen/index.ts
7
+ var codegen_exports = /* @__PURE__ */ __exportAll({
8
+ generateOperation: () => generateOperation,
9
+ generateOperations: () => generateOperations,
10
+ generateRegistry: () => generateRegistry,
11
+ generateSchema: () => generateSchema,
12
+ generateSchemas: () => generateSchemas
13
+ });
14
+
15
+ //#endregion
16
+ export { codegen_exports, generateOperation, generateOperations, generateRegistry, generateSchema, generateSchemas };
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/codegen/index.ts"],"sourcesContent":["/**\n * Code generation module.\n *\n * Generates ContractSpec definitions from the Intermediate Representation.\n */\n\n// Operation generators\nexport { generateOperation, generateOperations } from './operation-gen';\n\n// Schema generators\nexport { generateSchema, generateSchemas } from './schema-gen';\n\n// Registry generators\nexport { generateRegistry } from './registry-gen';\n\n// Types\nexport type {\n GeneratedFile,\n GenerationOptions,\n GenerationResult,\n} from './types';\n"],"mappings":""}
@@ -0,0 +1,16 @@
1
+ import { EndpointCandidate, ImportIR } from "../types.js";
2
+ import { GeneratedFile, GenerationOptions } from "./types.js";
3
+
4
+ //#region src/codegen/operation-gen.d.ts
5
+
6
+ /**
7
+ * Generate a single operation spec file.
8
+ */
9
+ declare function generateOperation(endpoint: EndpointCandidate, options: GenerationOptions): GeneratedFile;
10
+ /**
11
+ * Generate all operation spec files from IR.
12
+ */
13
+ declare function generateOperations(ir: ImportIR, options: GenerationOptions): GeneratedFile[];
14
+ //#endregion
15
+ export { generateOperation, generateOperations };
16
+ //# sourceMappingURL=operation-gen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operation-gen.d.ts","names":[],"sources":["../../src/codegen/operation-gen.ts"],"sourcesContent":[],"mappings":";;;;;;;AA+BA;AACM,iBApBU,iBAAA,CAoBV,QAAA,EAnBM,iBAmBN,EAAA,OAAA,EAlBK,iBAkBL,CAAA,EAjBH,aAiBG;;;;iBADU,kBAAA,KACV,mBACK,oBACR"}
@@ -0,0 +1,91 @@
1
+ //#region src/codegen/operation-gen.ts
2
+ /**
3
+ * Generate a single operation spec file.
4
+ */
5
+ function generateOperation(endpoint, options) {
6
+ const specName = toSpecName(endpoint);
7
+ return {
8
+ path: `${toFileName(endpoint)}.ts`,
9
+ content: generateOperationCode(endpoint, specName, options),
10
+ type: "operation"
11
+ };
12
+ }
13
+ /**
14
+ * Generate all operation spec files from IR.
15
+ */
16
+ function generateOperations(ir, options) {
17
+ return ir.endpoints.map((endpoint) => generateOperation(endpoint, options));
18
+ }
19
+ /**
20
+ * Convert endpoint to spec name (PascalCase).
21
+ */
22
+ function toSpecName(endpoint) {
23
+ const parts = endpoint.path.replace(/^\//, "").split("/").filter((p) => p && !p.startsWith(":") && !p.startsWith("{"));
24
+ return `${endpoint.method.toLowerCase()}${parts.map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join("")}Spec`;
25
+ }
26
+ /**
27
+ * Convert endpoint to file name (kebab-case).
28
+ */
29
+ function toFileName(endpoint) {
30
+ const parts = endpoint.path.replace(/^\//, "").split("/").filter((p) => p && !p.startsWith(":") && !p.startsWith("{"));
31
+ return `${endpoint.method.toLowerCase()}-${parts.join("-")}`.replace(/--+/g, "-");
32
+ }
33
+ /**
34
+ * Generate the operation spec code.
35
+ */
36
+ function generateOperationCode(endpoint, specName, options) {
37
+ const defineFunc = endpoint.kind === "command" ? "defineCommand" : "defineQuery";
38
+ const auth = options.defaultAuth ?? "user";
39
+ const owners = options.defaultOwners ?? ["team"];
40
+ return [
41
+ `/**`,
42
+ ` * ${endpoint.method} ${endpoint.path}`,
43
+ ` *`,
44
+ ` * Generated from: ${endpoint.source.file}:${endpoint.source.startLine}`,
45
+ ` * Confidence: ${endpoint.confidence.level}`,
46
+ ` */`,
47
+ ``,
48
+ `import { ${defineFunc} } from '@contractspec/lib.contracts';`,
49
+ `import { fromZod } from '@contractspec/lib.schema';`,
50
+ `import { z } from 'zod';`,
51
+ ``,
52
+ `// TODO: Define input schema based on extracted information`,
53
+ `const inputSchema = fromZod(z.object({`,
54
+ ` // Add fields here`,
55
+ `}));`,
56
+ ``,
57
+ `// TODO: Define output schema`,
58
+ `const outputSchema = fromZod(z.object({`,
59
+ ` // Add fields here`,
60
+ `}));`,
61
+ ``,
62
+ `export const ${specName} = ${defineFunc}({`,
63
+ ` meta: {`,
64
+ ` name: '${endpoint.handlerName ?? endpoint.id}',`,
65
+ ` version: 1,`,
66
+ ` stability: 'experimental',`,
67
+ ` owners: ${JSON.stringify(owners)},`,
68
+ ` goal: 'TODO: Describe the business goal',`,
69
+ ` context: 'Generated from ${endpoint.source.file}',`,
70
+ ` },`,
71
+ ` io: {`,
72
+ ` input: inputSchema,`,
73
+ ` output: outputSchema,`,
74
+ ` },`,
75
+ ` policy: {`,
76
+ ` auth: '${auth}',`,
77
+ ` },`,
78
+ ` transport: {`,
79
+ ` rest: {`,
80
+ ` method: '${endpoint.method}',`,
81
+ ` path: '${endpoint.path}',`,
82
+ ` },`,
83
+ ` },`,
84
+ `});`,
85
+ ``
86
+ ].join("\n");
87
+ }
88
+
89
+ //#endregion
90
+ export { generateOperation, generateOperations };
91
+ //# sourceMappingURL=operation-gen.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operation-gen.js","names":[],"sources":["../../src/codegen/operation-gen.ts"],"sourcesContent":["/**\n * Operation code generator.\n *\n * Generates defineCommand/defineQuery specs from endpoint candidates.\n */\n\nimport type { EndpointCandidate, ImportIR } from '../types';\nimport type { GeneratedFile, GenerationOptions } from './types';\n\n/**\n * Generate a single operation spec file.\n */\nexport function generateOperation(\n endpoint: EndpointCandidate,\n options: GenerationOptions\n): GeneratedFile {\n const specName = toSpecName(endpoint);\n const fileName = `${toFileName(endpoint)}.ts`;\n\n const code = generateOperationCode(endpoint, specName, options);\n\n return {\n path: fileName,\n content: code,\n type: 'operation',\n };\n}\n\n/**\n * Generate all operation spec files from IR.\n */\nexport function generateOperations(\n ir: ImportIR,\n options: GenerationOptions\n): GeneratedFile[] {\n return ir.endpoints.map((endpoint) => generateOperation(endpoint, options));\n}\n\n/**\n * Convert endpoint to spec name (PascalCase).\n */\nfunction toSpecName(endpoint: EndpointCandidate): string {\n const parts = endpoint.path\n .replace(/^\\//, '')\n .split('/')\n .filter((p) => p && !p.startsWith(':') && !p.startsWith('{'));\n\n const methodPart = endpoint.method.toLowerCase();\n const pathPart = parts\n .map((p) => p.charAt(0).toUpperCase() + p.slice(1))\n .join('');\n\n return `${methodPart}${pathPart}Spec`;\n}\n\n/**\n * Convert endpoint to file name (kebab-case).\n */\nfunction toFileName(endpoint: EndpointCandidate): string {\n const parts = endpoint.path\n .replace(/^\\//, '')\n .split('/')\n .filter((p) => p && !p.startsWith(':') && !p.startsWith('{'));\n\n const methodPart = endpoint.method.toLowerCase();\n const pathPart = parts.join('-');\n\n return `${methodPart}-${pathPart}`.replace(/--+/g, '-');\n}\n\n/**\n * Generate the operation spec code.\n */\nfunction generateOperationCode(\n endpoint: EndpointCandidate,\n specName: string,\n options: GenerationOptions\n): string {\n const isCommand = endpoint.kind === 'command';\n const defineFunc = isCommand ? 'defineCommand' : 'defineQuery';\n const auth = options.defaultAuth ?? 'user';\n const owners = options.defaultOwners ?? ['team'];\n\n const lines = [\n `/**`,\n ` * ${endpoint.method} ${endpoint.path}`,\n ` *`,\n ` * Generated from: ${endpoint.source.file}:${endpoint.source.startLine}`,\n ` * Confidence: ${endpoint.confidence.level}`,\n ` */`,\n ``,\n `import { ${defineFunc} } from '@contractspec/lib.contracts';`,\n `import { fromZod } from '@contractspec/lib.schema';`,\n `import { z } from 'zod';`,\n ``,\n `// TODO: Define input schema based on extracted information`,\n `const inputSchema = fromZod(z.object({`,\n ` // Add fields here`,\n `}));`,\n ``,\n `// TODO: Define output schema`,\n `const outputSchema = fromZod(z.object({`,\n ` // Add fields here`,\n `}));`,\n ``,\n `export const ${specName} = ${defineFunc}({`,\n ` meta: {`,\n ` name: '${endpoint.handlerName ?? endpoint.id}',`,\n ` version: 1,`,\n ` stability: 'experimental',`,\n ` owners: ${JSON.stringify(owners)},`,\n ` goal: 'TODO: Describe the business goal',`,\n ` context: 'Generated from ${endpoint.source.file}',`,\n ` },`,\n ` io: {`,\n ` input: inputSchema,`,\n ` output: outputSchema,`,\n ` },`,\n ` policy: {`,\n ` auth: '${auth}',`,\n ` },`,\n ` transport: {`,\n ` rest: {`,\n ` method: '${endpoint.method}',`,\n ` path: '${endpoint.path}',`,\n ` },`,\n ` },`,\n `});`,\n ``,\n ];\n\n return lines.join('\\n');\n}\n"],"mappings":";;;;AAYA,SAAgB,kBACd,UACA,SACe;CACf,MAAM,WAAW,WAAW,SAAS;AAKrC,QAAO;EACL,MALe,GAAG,WAAW,SAAS,CAAC;EAMvC,SAJW,sBAAsB,UAAU,UAAU,QAAQ;EAK7D,MAAM;EACP;;;;;AAMH,SAAgB,mBACd,IACA,SACiB;AACjB,QAAO,GAAG,UAAU,KAAK,aAAa,kBAAkB,UAAU,QAAQ,CAAC;;;;;AAM7E,SAAS,WAAW,UAAqC;CACvD,MAAM,QAAQ,SAAS,KACpB,QAAQ,OAAO,GAAG,CAClB,MAAM,IAAI,CACV,QAAQ,MAAM,KAAK,CAAC,EAAE,WAAW,IAAI,IAAI,CAAC,EAAE,WAAW,IAAI,CAAC;AAO/D,QAAO,GALY,SAAS,OAAO,aAAa,GAC/B,MACd,KAAK,MAAM,EAAE,OAAO,EAAE,CAAC,aAAa,GAAG,EAAE,MAAM,EAAE,CAAC,CAClD,KAAK,GAAG,CAEqB;;;;;AAMlC,SAAS,WAAW,UAAqC;CACvD,MAAM,QAAQ,SAAS,KACpB,QAAQ,OAAO,GAAG,CAClB,MAAM,IAAI,CACV,QAAQ,MAAM,KAAK,CAAC,EAAE,WAAW,IAAI,IAAI,CAAC,EAAE,WAAW,IAAI,CAAC;AAK/D,QAAO,GAHY,SAAS,OAAO,aAAa,CAG3B,GAFJ,MAAM,KAAK,IAAI,GAEG,QAAQ,QAAQ,IAAI;;;;;AAMzD,SAAS,sBACP,UACA,UACA,SACQ;CAER,MAAM,aADY,SAAS,SAAS,YACL,kBAAkB;CACjD,MAAM,OAAO,QAAQ,eAAe;CACpC,MAAM,SAAS,QAAQ,iBAAiB,CAAC,OAAO;AAkDhD,QAhDc;EACZ;EACA,MAAM,SAAS,OAAO,GAAG,SAAS;EAClC;EACA,sBAAsB,SAAS,OAAO,KAAK,GAAG,SAAS,OAAO;EAC9D,kBAAkB,SAAS,WAAW;EACtC;EACA;EACA,YAAY,WAAW;EACvB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,gBAAgB,SAAS,KAAK,WAAW;EACzC;EACA,cAAc,SAAS,eAAe,SAAS,GAAG;EAClD;EACA;EACA,eAAe,KAAK,UAAU,OAAO,CAAC;EACtC;EACA,gCAAgC,SAAS,OAAO,KAAK;EACrD;EACA;EACA;EACA;EACA;EACA;EACA,cAAc,KAAK;EACnB;EACA;EACA;EACA,kBAAkB,SAAS,OAAO;EAClC,gBAAgB,SAAS,KAAK;EAC9B;EACA;EACA;EACA;EACD,CAEY,KAAK,KAAK"}
@@ -0,0 +1,11 @@
1
+ import { GeneratedFile } from "./types.js";
2
+
3
+ //#region src/codegen/registry-gen.d.ts
4
+
5
+ /**
6
+ * Generate a registry file for the generated operations.
7
+ */
8
+ declare function generateRegistry(operationFiles: GeneratedFile[]): GeneratedFile;
9
+ //#endregion
10
+ export { generateRegistry };
11
+ //# sourceMappingURL=registry-gen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry-gen.d.ts","names":[],"sources":["../../src/codegen/registry-gen.ts"],"sourcesContent":[],"mappings":";;;;;;;iBAWgB,gBAAA,iBACE,kBACf"}
@@ -0,0 +1,47 @@
1
+ //#region src/codegen/registry-gen.ts
2
+ /**
3
+ * Generate a registry file for the generated operations.
4
+ */
5
+ function generateRegistry(operationFiles) {
6
+ const operationImports = operationFiles.filter((f) => f.type === "operation").map((f) => {
7
+ const name = f.path.replace(".ts", "").replace(/-/g, "_");
8
+ const specName = toPascalCase(name) + "Spec";
9
+ return {
10
+ path: f.path,
11
+ name,
12
+ specName
13
+ };
14
+ });
15
+ const lines = [
16
+ `/**`,
17
+ ` * Generated operation registry.`,
18
+ ` */`,
19
+ ``,
20
+ `import { OperationSpecRegistry } from '@contractspec/lib.contracts';`,
21
+ ``
22
+ ];
23
+ for (const op of operationImports) {
24
+ const importPath = `./${op.path.replace(".ts", "")}`;
25
+ lines.push(`import { ${op.specName} } from '${importPath}';`);
26
+ }
27
+ lines.push(``);
28
+ lines.push(`export const operationRegistry = new OperationSpecRegistry();`);
29
+ lines.push(``);
30
+ for (const op of operationImports) lines.push(`operationRegistry.register(${op.specName});`);
31
+ lines.push(``);
32
+ return {
33
+ path: "registry.ts",
34
+ content: lines.join("\n"),
35
+ type: "registry"
36
+ };
37
+ }
38
+ /**
39
+ * Convert kebab-case to PascalCase.
40
+ */
41
+ function toPascalCase(str) {
42
+ return str.split(/[-_]/).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
43
+ }
44
+
45
+ //#endregion
46
+ export { generateRegistry };
47
+ //# sourceMappingURL=registry-gen.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry-gen.js","names":[],"sources":["../../src/codegen/registry-gen.ts"],"sourcesContent":["/**\n * Registry code generator.\n *\n * Generates OperationSpecRegistry from generated operations.\n */\n\nimport type { GeneratedFile } from './types';\n\n/**\n * Generate a registry file for the generated operations.\n */\nexport function generateRegistry(\n operationFiles: GeneratedFile[]\n): GeneratedFile {\n const operationImports = operationFiles\n .filter((f) => f.type === 'operation')\n .map((f) => {\n const name = f.path.replace('.ts', '').replace(/-/g, '_');\n const specName = toPascalCase(name) + 'Spec';\n return { path: f.path, name, specName };\n });\n\n const lines = [\n `/**`,\n ` * Generated operation registry.`,\n ` */`,\n ``,\n `import { OperationSpecRegistry } from '@contractspec/lib.contracts';`,\n ``,\n ];\n\n // Add imports\n for (const op of operationImports) {\n const importPath = `./${op.path.replace('.ts', '')}`;\n lines.push(`import { ${op.specName} } from '${importPath}';`);\n }\n\n lines.push(``);\n lines.push(`export const operationRegistry = new OperationSpecRegistry();`);\n lines.push(``);\n\n // Register operations\n for (const op of operationImports) {\n lines.push(`operationRegistry.register(${op.specName});`);\n }\n\n lines.push(``);\n\n return {\n path: 'registry.ts',\n content: lines.join('\\n'),\n type: 'registry',\n };\n}\n\n/**\n * Convert kebab-case to PascalCase.\n */\nfunction toPascalCase(str: string): string {\n return str\n .split(/[-_]/)\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join('');\n}\n"],"mappings":";;;;AAWA,SAAgB,iBACd,gBACe;CACf,MAAM,mBAAmB,eACtB,QAAQ,MAAM,EAAE,SAAS,YAAY,CACrC,KAAK,MAAM;EACV,MAAM,OAAO,EAAE,KAAK,QAAQ,OAAO,GAAG,CAAC,QAAQ,MAAM,IAAI;EACzD,MAAM,WAAW,aAAa,KAAK,GAAG;AACtC,SAAO;GAAE,MAAM,EAAE;GAAM;GAAM;GAAU;GACvC;CAEJ,MAAM,QAAQ;EACZ;EACA;EACA;EACA;EACA;EACA;EACD;AAGD,MAAK,MAAM,MAAM,kBAAkB;EACjC,MAAM,aAAa,KAAK,GAAG,KAAK,QAAQ,OAAO,GAAG;AAClD,QAAM,KAAK,YAAY,GAAG,SAAS,WAAW,WAAW,IAAI;;AAG/D,OAAM,KAAK,GAAG;AACd,OAAM,KAAK,gEAAgE;AAC3E,OAAM,KAAK,GAAG;AAGd,MAAK,MAAM,MAAM,iBACf,OAAM,KAAK,8BAA8B,GAAG,SAAS,IAAI;AAG3D,OAAM,KAAK,GAAG;AAEd,QAAO;EACL,MAAM;EACN,SAAS,MAAM,KAAK,KAAK;EACzB,MAAM;EACP;;;;;AAMH,SAAS,aAAa,KAAqB;AACzC,QAAO,IACJ,MAAM,OAAO,CACb,KAAK,SAAS,KAAK,OAAO,EAAE,CAAC,aAAa,GAAG,KAAK,MAAM,EAAE,CAAC,CAC3D,KAAK,GAAG"}
@@ -0,0 +1,16 @@
1
+ import { ImportIR, SchemaCandidate } from "../types.js";
2
+ import { GeneratedFile, GenerationOptions } from "./types.js";
3
+
4
+ //#region src/codegen/schema-gen.d.ts
5
+
6
+ /**
7
+ * Generate a single schema file.
8
+ */
9
+ declare function generateSchema(schema: SchemaCandidate, _options: GenerationOptions): GeneratedFile;
10
+ /**
11
+ * Generate all schema files from IR.
12
+ */
13
+ declare function generateSchemas(ir: ImportIR, options: GenerationOptions): GeneratedFile[];
14
+ //#endregion
15
+ export { generateSchema, generateSchemas };
16
+ //# sourceMappingURL=schema-gen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-gen.d.ts","names":[],"sources":["../../src/codegen/schema-gen.ts"],"sourcesContent":[],"mappings":";;;;;;;AA6BA;AACM,iBAlBU,cAAA,CAkBV,MAAA,EAjBI,eAiBJ,EAAA,QAAA,EAhBM,iBAgBN,CAAA,EAfH,aAeG;;;;iBADU,eAAA,KACV,mBACK,oBACR"}
@@ -0,0 +1,93 @@
1
+ //#region src/codegen/schema-gen.ts
2
+ /**
3
+ * Generate a single schema file.
4
+ */
5
+ function generateSchema(schema, _options) {
6
+ const fileName = `${toFileName(schema.name)}.ts`;
7
+ const code = generateSchemaCode(schema);
8
+ return {
9
+ path: `schemas/${fileName}`,
10
+ content: code,
11
+ type: "schema"
12
+ };
13
+ }
14
+ /**
15
+ * Generate all schema files from IR.
16
+ */
17
+ function generateSchemas(ir, options) {
18
+ return ir.schemas.map((schema) => generateSchema(schema, options));
19
+ }
20
+ /**
21
+ * Convert name to file name (kebab-case).
22
+ */
23
+ function toFileName(name) {
24
+ return name.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/([A-Z])([A-Z][a-z])/g, "$1-$2").toLowerCase();
25
+ }
26
+ /**
27
+ * Generate schema code.
28
+ */
29
+ function generateSchemaCode(schema) {
30
+ const lines = [
31
+ `/**`,
32
+ ` * ${schema.name}`,
33
+ ` *`,
34
+ ` * Generated from: ${schema.source.file}:${schema.source.startLine}`,
35
+ ` * Schema type: ${schema.schemaType}`,
36
+ ` * Confidence: ${schema.confidence.level}`,
37
+ ` */`,
38
+ ``,
39
+ `import { fromZod } from '@contractspec/lib.schema';`,
40
+ `import { z } from 'zod';`,
41
+ ``
42
+ ];
43
+ if (schema.rawDefinition && schema.schemaType === "zod") {
44
+ lines.push(`// Original definition from source:`);
45
+ lines.push(`// ${schema.rawDefinition.split("\n")[0]}`);
46
+ lines.push(``);
47
+ }
48
+ lines.push(`export const ${schema.name}Schema = fromZod(z.object({`);
49
+ if (schema.fields && schema.fields.length > 0) for (const field of schema.fields) {
50
+ const zodType = mapToZodType(field.type, field.optional);
51
+ lines.push(` ${field.name}: ${zodType},`);
52
+ }
53
+ else lines.push(` // TODO: Define schema fields`);
54
+ lines.push(`}));`);
55
+ lines.push(``);
56
+ lines.push(`export type ${schema.name} = z.infer<typeof ${schema.name}Schema.zodSchema>;`);
57
+ lines.push(``);
58
+ return lines.join("\n");
59
+ }
60
+ /**
61
+ * Map TypeScript type to Zod type.
62
+ */
63
+ function mapToZodType(tsType, optional) {
64
+ let zodType;
65
+ switch (tsType.toLowerCase()) {
66
+ case "string":
67
+ zodType = "z.string()";
68
+ break;
69
+ case "number":
70
+ zodType = "z.number()";
71
+ break;
72
+ case "boolean":
73
+ zodType = "z.boolean()";
74
+ break;
75
+ case "date":
76
+ zodType = "z.date()";
77
+ break;
78
+ case "string[]":
79
+ case "array<string>":
80
+ zodType = "z.array(z.string())";
81
+ break;
82
+ case "number[]":
83
+ case "array<number>":
84
+ zodType = "z.array(z.number())";
85
+ break;
86
+ default: zodType = "z.unknown()";
87
+ }
88
+ return optional ? `${zodType}.optional()` : zodType;
89
+ }
90
+
91
+ //#endregion
92
+ export { generateSchema, generateSchemas };
93
+ //# sourceMappingURL=schema-gen.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-gen.js","names":[],"sources":["../../src/codegen/schema-gen.ts"],"sourcesContent":["/**\n * Schema code generator.\n *\n * Generates defineSchemaModel specs from schema candidates.\n */\n\nimport type { SchemaCandidate, ImportIR } from '../types';\nimport type { GeneratedFile, GenerationOptions } from './types';\n\n/**\n * Generate a single schema file.\n */\nexport function generateSchema(\n schema: SchemaCandidate,\n _options: GenerationOptions\n): GeneratedFile {\n const fileName = `${toFileName(schema.name)}.ts`;\n const code = generateSchemaCode(schema);\n\n return {\n path: `schemas/${fileName}`,\n content: code,\n type: 'schema',\n };\n}\n\n/**\n * Generate all schema files from IR.\n */\nexport function generateSchemas(\n ir: ImportIR,\n options: GenerationOptions\n): GeneratedFile[] {\n return ir.schemas.map((schema) => generateSchema(schema, options));\n}\n\n/**\n * Convert name to file name (kebab-case).\n */\nfunction toFileName(name: string): string {\n return name\n .replace(/([a-z])([A-Z])/g, '$1-$2')\n .replace(/([A-Z])([A-Z][a-z])/g, '$1-$2')\n .toLowerCase();\n}\n\n/**\n * Generate schema code.\n */\nfunction generateSchemaCode(schema: SchemaCandidate): string {\n const lines = [\n `/**`,\n ` * ${schema.name}`,\n ` *`,\n ` * Generated from: ${schema.source.file}:${schema.source.startLine}`,\n ` * Schema type: ${schema.schemaType}`,\n ` * Confidence: ${schema.confidence.level}`,\n ` */`,\n ``,\n `import { fromZod } from '@contractspec/lib.schema';`,\n `import { z } from 'zod';`,\n ``,\n ];\n\n if (schema.rawDefinition && schema.schemaType === 'zod') {\n // Use the raw Zod definition if available\n lines.push(`// Original definition from source:`);\n lines.push(`// ${schema.rawDefinition.split('\\n')[0]}`);\n lines.push(``);\n }\n\n lines.push(`export const ${schema.name}Schema = fromZod(z.object({`);\n\n if (schema.fields && schema.fields.length > 0) {\n for (const field of schema.fields) {\n const zodType = mapToZodType(field.type, field.optional);\n lines.push(` ${field.name}: ${zodType},`);\n }\n } else {\n lines.push(` // TODO: Define schema fields`);\n }\n\n lines.push(`}));`);\n lines.push(``);\n lines.push(\n `export type ${schema.name} = z.infer<typeof ${schema.name}Schema.zodSchema>;`\n );\n lines.push(``);\n\n return lines.join('\\n');\n}\n\n/**\n * Map TypeScript type to Zod type.\n */\nfunction mapToZodType(tsType: string, optional: boolean): string {\n let zodType: string;\n\n switch (tsType.toLowerCase()) {\n case 'string':\n zodType = 'z.string()';\n break;\n case 'number':\n zodType = 'z.number()';\n break;\n case 'boolean':\n zodType = 'z.boolean()';\n break;\n case 'date':\n zodType = 'z.date()';\n break;\n case 'string[]':\n case 'array<string>':\n zodType = 'z.array(z.string())';\n break;\n case 'number[]':\n case 'array<number>':\n zodType = 'z.array(z.number())';\n break;\n default:\n zodType = 'z.unknown()';\n }\n\n return optional ? `${zodType}.optional()` : zodType;\n}\n"],"mappings":";;;;AAYA,SAAgB,eACd,QACA,UACe;CACf,MAAM,WAAW,GAAG,WAAW,OAAO,KAAK,CAAC;CAC5C,MAAM,OAAO,mBAAmB,OAAO;AAEvC,QAAO;EACL,MAAM,WAAW;EACjB,SAAS;EACT,MAAM;EACP;;;;;AAMH,SAAgB,gBACd,IACA,SACiB;AACjB,QAAO,GAAG,QAAQ,KAAK,WAAW,eAAe,QAAQ,QAAQ,CAAC;;;;;AAMpE,SAAS,WAAW,MAAsB;AACxC,QAAO,KACJ,QAAQ,mBAAmB,QAAQ,CACnC,QAAQ,wBAAwB,QAAQ,CACxC,aAAa;;;;;AAMlB,SAAS,mBAAmB,QAAiC;CAC3D,MAAM,QAAQ;EACZ;EACA,MAAM,OAAO;EACb;EACA,sBAAsB,OAAO,OAAO,KAAK,GAAG,OAAO,OAAO;EAC1D,mBAAmB,OAAO;EAC1B,kBAAkB,OAAO,WAAW;EACpC;EACA;EACA;EACA;EACA;EACD;AAED,KAAI,OAAO,iBAAiB,OAAO,eAAe,OAAO;AAEvD,QAAM,KAAK,sCAAsC;AACjD,QAAM,KAAK,MAAM,OAAO,cAAc,MAAM,KAAK,CAAC,KAAK;AACvD,QAAM,KAAK,GAAG;;AAGhB,OAAM,KAAK,gBAAgB,OAAO,KAAK,6BAA6B;AAEpE,KAAI,OAAO,UAAU,OAAO,OAAO,SAAS,EAC1C,MAAK,MAAM,SAAS,OAAO,QAAQ;EACjC,MAAM,UAAU,aAAa,MAAM,MAAM,MAAM,SAAS;AACxD,QAAM,KAAK,KAAK,MAAM,KAAK,IAAI,QAAQ,GAAG;;KAG5C,OAAM,KAAK,kCAAkC;AAG/C,OAAM,KAAK,OAAO;AAClB,OAAM,KAAK,GAAG;AACd,OAAM,KACJ,eAAe,OAAO,KAAK,oBAAoB,OAAO,KAAK,oBAC5D;AACD,OAAM,KAAK,GAAG;AAEd,QAAO,MAAM,KAAK,KAAK;;;;;AAMzB,SAAS,aAAa,QAAgB,UAA2B;CAC/D,IAAI;AAEJ,SAAQ,OAAO,aAAa,EAA5B;EACE,KAAK;AACH,aAAU;AACV;EACF,KAAK;AACH,aAAU;AACV;EACF,KAAK;AACH,aAAU;AACV;EACF,KAAK;AACH,aAAU;AACV;EACF,KAAK;EACL,KAAK;AACH,aAAU;AACV;EACF,KAAK;EACL,KAAK;AACH,aAAU;AACV;EACF,QACE,WAAU;;AAGd,QAAO,WAAW,GAAG,QAAQ,eAAe"}
@@ -0,0 +1,48 @@
1
+ //#region src/codegen/types.d.ts
2
+ /**
3
+ * Code generation types.
4
+ */
5
+ /**
6
+ * A generated file.
7
+ */
8
+ interface GeneratedFile {
9
+ /** Relative path from output directory */
10
+ path: string;
11
+ /** File content */
12
+ content: string;
13
+ /** Type of generated file */
14
+ type: 'operation' | 'schema' | 'registry' | 'index';
15
+ }
16
+ /**
17
+ * Options for code generation.
18
+ */
19
+ interface GenerationOptions {
20
+ /** Output directory */
21
+ outputDir: string;
22
+ /** Whether to overwrite existing files */
23
+ overwrite?: boolean;
24
+ /** Schema format to use */
25
+ schemaFormat?: 'zod' | 'typescript';
26
+ /** Default auth level */
27
+ defaultAuth?: 'anonymous' | 'user' | 'admin';
28
+ /** Default owners */
29
+ defaultOwners?: string[];
30
+ /** Prefix for generated names */
31
+ prefix?: string;
32
+ }
33
+ /**
34
+ * Result of code generation.
35
+ */
36
+ interface GenerationResult {
37
+ /** Generated files */
38
+ files: GeneratedFile[];
39
+ /** Number of operations generated */
40
+ operationsGenerated: number;
41
+ /** Number of schemas generated */
42
+ schemasGenerated: number;
43
+ /** Warnings during generation */
44
+ warnings: string[];
45
+ }
46
+ //#endregion
47
+ export { GeneratedFile, GenerationOptions, GenerationResult };
48
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","names":[],"sources":["../../src/codegen/types.ts"],"sourcesContent":[],"mappings":";;AAOA;AAYA;AAkBA;;;UA9BiB,aAAA;;;;;;;;;;;UAYA,iBAAA;;;;;;;;;;;;;;;;;UAkBA,gBAAA;;SAER"}
@@ -0,0 +1,47 @@
1
+ import { FrameworkInfo, ProjectInfo } from "./types.js";
2
+
3
+ //#region src/detect.d.ts
4
+
5
+ /**
6
+ * Dependencies from package.json.
7
+ */
8
+ interface PackageDependencies {
9
+ dependencies?: Record<string, string>;
10
+ devDependencies?: Record<string, string>;
11
+ peerDependencies?: Record<string, string>;
12
+ }
13
+ /**
14
+ * Detect frameworks from package.json dependencies.
15
+ */
16
+ declare function detectFrameworksFromPackageJson(packageJson: PackageDependencies): FrameworkInfo[];
17
+ /**
18
+ * Detect frameworks from source code imports.
19
+ */
20
+ declare function detectFrameworksFromCode(sourceCode: string): FrameworkInfo[];
21
+ /**
22
+ * Detect frameworks from file paths.
23
+ */
24
+ declare function detectFrameworksFromPaths(filePaths: string[]): FrameworkInfo[];
25
+ /**
26
+ * Merge framework detections, preferring higher confidence.
27
+ */
28
+ declare function mergeFrameworkDetections(...detections: FrameworkInfo[][]): FrameworkInfo[];
29
+ /**
30
+ * Detect frameworks for a project.
31
+ * This is a convenience function that combines all detection methods.
32
+ */
33
+ declare function detectFramework(rootPath: string, options?: {
34
+ readFile?: (path: string) => Promise<string>;
35
+ glob?: (pattern: string) => Promise<string[]>;
36
+ }): Promise<ProjectInfo>;
37
+ /**
38
+ * Get all supported framework IDs.
39
+ */
40
+ declare function getSupportedFrameworks(): string[];
41
+ /**
42
+ * Check if a framework ID is supported.
43
+ */
44
+ declare function isFrameworkSupported(id: string): boolean;
45
+ //#endregion
46
+ export { detectFramework, detectFrameworksFromCode, detectFrameworksFromPackageJson, detectFrameworksFromPaths, getSupportedFrameworks, isFrameworkSupported, mergeFrameworkDetections };
47
+ //# sourceMappingURL=detect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect.d.ts","names":[],"sources":["../src/detect.ts"],"sourcesContent":[],"mappings":";;;;;;AA8FA;AA+BA,UAxCU,mBAAA,CAwCM;EA0BA,YAAA,CAAA,EAjEC,MAiED,CAAA,MAAyB,EAAA,MAAA,CAAA;EA8BzB,eAAA,CAAA,EA9FI,MA8FJ,CAAA,MAAwB,EAAA,MACvB,CAAA;EA8BK,gBAAA,CAAA,EA5HD,MA4HgB,CAAA,MAAA,EAAA,MAAA,CAAA;;;;;AAM3B,iBA5HM,+BAAA,CA4HN,WAAA,EA3HK,mBA2HL,CAAA,EA1HP,aA0HO,EAAA;AAoCV;AAOA;;iBAxIgB,wBAAA,sBAA8C;;;;iBA0B9C,yBAAA,uBAEb;;;;iBA4Ba,wBAAA,gBACC,oBACd;;;;;iBA6BmB,eAAA;+BAGW;8BACD;IAE7B,QAAQ;;;;iBAoCK,sBAAA,CAAA;;;;iBAOA,oBAAA"}