@contractspec/lib.source-extractors 2.7.20 → 2.7.21

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.
@@ -1,16 +1,16 @@
1
- var e=Object.defineProperty;var ss=(s)=>s;function ts(s,d){this[s]=ss.bind(null,d)}var F=(s,d)=>{for(var t in d)e(s,t,{get:d[t],enumerable:!0,configurable:!0,set:ts.bind(d,t)})};var A={};F(A,{generateSchemas:()=>P,generateSchema:()=>_,generateRegistry:()=>W,generateOperations:()=>L,generateOperation:()=>Y});function Y(s,d){let t=ds(s),o=`${os(s)}.ts`,i=is(s,t,d);return{path:o,content:i,type:"operation"}}function L(s,d){return s.endpoints.map((t)=>Y(t,d))}function ds(s){let d=s.path.replace(/^\//,"").split("/").filter((i)=>i&&!i.startsWith(":")&&!i.startsWith("{")),t=s.method.toLowerCase(),o=d.map((i)=>i.charAt(0).toUpperCase()+i.slice(1)).join("");return`${t}${o}Spec`}function os(s){let d=s.path.replace(/^\//,"").split("/").filter((i)=>i&&!i.startsWith(":")&&!i.startsWith("{")),t=s.method.toLowerCase(),o=d.join("-");return`${t}-${o}`.replace(/--+/g,"-")}function is(s,d,t){let i=s.kind==="command"?"defineCommand":"defineQuery",n=t.defaultAuth??"user",u=t.defaultOwners??["team"];return["/**",` * ${s.method} ${s.path}`," *",` * Generated from: ${s.source.file}:${s.source.startLine}`,` * Confidence: ${s.confidence.level}`," */","",`import { ${i} } from '@contractspec/lib.contracts-spec';`,"import { fromZod } from '@contractspec/lib.schema';","import { z } from 'zod';","","// TODO: Define input schema based on extracted information","const inputSchema = fromZod(z.object({"," // Add fields here","}));","","// TODO: Define output schema","const outputSchema = fromZod(z.object({"," // Add fields here","}));","",`export const ${d} = ${i}({`," meta: {",` name: '${s.handlerName??s.id}',`," version: 1,"," stability: 'experimental',",` owners: ${JSON.stringify(u)},`," goal: 'TODO: Describe the business goal',",` context: 'Generated from ${s.source.file}',`," },"," io: {"," input: inputSchema,"," output: outputSchema,"," },"," policy: {",` auth: '${n}',`," },"," transport: {"," rest: {",` method: '${s.method}',`,` path: '${s.path}',`," },"," },","});",""].join(`
2
- `)}function W(s){let d=s.filter((o)=>o.type==="operation").map((o)=>{let i=o.path.replace(".ts","").replace(/-/g,"_"),n=us(i)+"Spec";return{path:o.path,name:i,specName:n}}),t=["/**"," * Generated operation registry."," */","","import { OperationSpecRegistry } from '@contractspec/lib.contracts-spec';",""];for(let o of d){let i=`./${o.path.replace(".ts","")}`;t.push(`import { ${o.specName} } from '${i}';`)}t.push(""),t.push("export const operationRegistry = new OperationSpecRegistry();"),t.push("");for(let o of d)t.push(`operationRegistry.register(${o.specName});`);return t.push(""),{path:"registry.ts",content:t.join(`
3
- `),type:"registry"}}function us(s){return s.split(/[-_]/).map((d)=>d.charAt(0).toUpperCase()+d.slice(1)).join("")}function _(s,d){let t=`${ns(s.name)}.ts`,o=as(s);return{path:`schemas/${t}`,content:o,type:"schema"}}function P(s,d){return s.schemas.map((t)=>_(t,d))}function ns(s){return s.replace(/([a-z])([A-Z])/g,"$1-$2").replace(/([A-Z])([A-Z][a-z])/g,"$1-$2").toLowerCase()}function as(s){let d=["/**",` * ${s.name}`," *",` * Generated from: ${s.source.file}:${s.source.startLine}`,` * Schema type: ${s.schemaType}`,` * Confidence: ${s.confidence.level}`," */","","import { fromZod } from '@contractspec/lib.schema';","import { z } from 'zod';",""];if(s.rawDefinition&&s.schemaType==="zod")d.push("// Original definition from source:"),d.push(`// ${s.rawDefinition.split(`
1
+ var e=Object.defineProperty;var ss=(s)=>s;function ts(s,d){this[s]=ss.bind(null,d)}var L=(s,d)=>{for(var t in d)e(s,t,{get:d[t],enumerable:!0,configurable:!0,set:ts.bind(d,t)})};var A={};L(A,{generateSchemas:()=>P,generateSchema:()=>_,generateRegistry:()=>F,generateOperations:()=>W,generateOperation:()=>Y});function Y(s,d){let t=ds(s),o=`${os(s)}.ts`,i=is(s,t,d);return{path:o,content:i,type:"operation"}}function W(s,d){return s.endpoints.map((t)=>Y(t,d))}function ds(s){let d=s.path.replace(/^\//,"").split("/").filter((i)=>i&&!i.startsWith(":")&&!i.startsWith("{")),t=s.method.toLowerCase(),o=d.map((i)=>i.charAt(0).toUpperCase()+i.slice(1)).join("");return`${t}${o}Spec`}function os(s){let d=s.path.replace(/^\//,"").split("/").filter((i)=>i&&!i.startsWith(":")&&!i.startsWith("{")),t=s.method.toLowerCase(),o=d.join("-");return`${t}-${o}`.replace(/--+/g,"-")}function is(s,d,t){let i=s.kind==="command"?"defineCommand":"defineQuery",u=t.defaultAuth??"user",n=t.defaultOwners??["team"];return["/**",` * ${s.method} ${s.path}`," *",` * Generated from: ${s.source.file}:${s.source.startLine}`,` * Confidence: ${s.confidence.level}`," */","",`import { ${i} } from '@contractspec/lib.contracts-spec';`,"import { fromZod } from '@contractspec/lib.schema';","import { z } from 'zod';","","// TODO: Define input schema based on extracted information","const inputSchema = fromZod(z.object({"," // Add fields here","}));","","// TODO: Define output schema","const outputSchema = fromZod(z.object({"," // Add fields here","}));","",`export const ${d} = ${i}({`," meta: {",` name: '${s.handlerName??s.id}',`," version: 1,"," stability: 'experimental',",` owners: ${JSON.stringify(n)},`," goal: 'TODO: Describe the business goal',",` context: 'Generated from ${s.source.file}',`," },"," io: {"," input: inputSchema,"," output: outputSchema,"," },"," policy: {",` auth: '${u}',`," },"," transport: {"," rest: {",` method: '${s.method}',`,` path: '${s.path}',`," },"," },","});",""].join(`
2
+ `)}function F(s){let d=s.filter((o)=>o.type==="operation").map((o)=>{let i=o.path.replace(".ts","").replace(/-/g,"_"),u=ns(i)+"Spec";return{path:o.path,name:i,specName:u}}),t=["/**"," * Generated operation registry."," */","","import { OperationSpecRegistry } from '@contractspec/lib.contracts-spec';",""];for(let o of d){let i=`./${o.path.replace(".ts","")}`;t.push(`import { ${o.specName} } from '${i}';`)}t.push(""),t.push("export const operationRegistry = new OperationSpecRegistry();"),t.push("");for(let o of d)t.push(`operationRegistry.register(${o.specName});`);return t.push(""),{path:"registry.ts",content:t.join(`
3
+ `),type:"registry"}}function ns(s){return s.split(/[-_]/).map((d)=>d.charAt(0).toUpperCase()+d.slice(1)).join("")}function _(s,d){let t=`${us(s.name)}.ts`,o=as(s);return{path:`schemas/${t}`,content:o,type:"schema"}}function P(s,d){return s.schemas.map((t)=>_(t,d))}function us(s){return s.replace(/([a-z])([A-Z])/g,"$1-$2").replace(/([A-Z])([A-Z][a-z])/g,"$1-$2").toLowerCase()}function as(s){let d=["/**",` * ${s.name}`," *",` * Generated from: ${s.source.file}:${s.source.startLine}`,` * Schema type: ${s.schemaType}`,` * Confidence: ${s.confidence.level}`," */","","import { fromZod } from '@contractspec/lib.schema';","import { z } from 'zod';",""];if(s.rawDefinition&&s.schemaType==="zod")d.push("// Original definition from source:"),d.push(`// ${s.rawDefinition.split(`
4
4
  `)[0]}`),d.push("");if(d.push(`export const ${s.name}Schema = fromZod(z.object({`),s.fields&&s.fields.length>0)for(let t of s.fields){let o=Es(t.type,t.optional);d.push(` ${t.name}: ${o},`)}else d.push(" // TODO: Define schema fields");return d.push("}));"),d.push(""),d.push(`export type ${s.name} = z.infer<typeof ${s.name}Schema.zodSchema>;`),d.push(""),d.join(`
5
- `)}function Es(s,d){let t;switch(s.toLowerCase()){case"string":t="z.string()";break;case"number":t="z.number()";break;case"boolean":t="z.boolean()";break;case"date":t="z.date()";break;case"string[]":case"array<string>":t="z.array(z.string())";break;case"number[]":case"array<number>":t="z.array(z.number())";break;default:t="z.unknown()"}return d?`${t}.optional()`:t}var j={};F(j,{registerAllExtractors:()=>ps,ZodSchemaExtractor:()=>Z,TrpcExtractor:()=>S,NextApiExtractor:()=>V,NestJsExtractor:()=>q,HonoExtractor:()=>f,FastifyExtractor:()=>O,ExpressExtractor:()=>G,ElysiaExtractor:()=>h,BaseExtractor:()=>C});class J{extractors=new Map;register(s){this.extractors.set(s.id,s)}unregister(s){return this.extractors.delete(s)}get(s){return this.extractors.get(s)}getAll(){return Array.from(this.extractors.values())}async findMatching(s){let d=[];for(let t of this.extractors.values())try{if(await t.detect(s))d.push(t)}catch{}return d.sort((t,o)=>o.priority-t.priority)}findByFramework(s){let d=[];for(let t of this.extractors.values())if(t.frameworks.includes(s))d.push(t);return d.sort((t,o)=>o.priority-t.priority)}findForFramework(s){return this.findByFramework(s)}hasExtractorFor(s){for(let d of this.extractors.values())if(d.frameworks.includes(s)||d.id===s)return!0;return!1}getSupportedFrameworks(){let s=new Set;for(let d of this.extractors.values()){s.add(d.id);for(let t of d.frameworks)s.add(t)}return Array.from(s)}}var v=new J;function Os(){}async function Vs(s,d={}){let t;if(d.framework){if(t=v.findByFramework(d.framework),t.length===0)return{success:!1,errors:[{code:"EXTRACTOR_NOT_FOUND",message:`No extractor found for framework: ${d.framework}`,recoverable:!1}]}}else if(t=await v.findMatching(s),t.length===0)return{success:!1,errors:[{code:"NO_FRAMEWORK_DETECTED",message:"No supported framework detected in project",recoverable:!1}]};let o=t[0];if(!o)return{success:!1,errors:[{code:"NO_EXTRACTOR",message:"No extractor available",recoverable:!1}]};return await o.extract(s,d)}function Ss(s){if(s.length===0)throw Error("Cannot merge empty IR array");if(s.length===1){if(!s[0])throw Error("First IR is undefined");return s[0]}let d=s[0];if(!d)throw Error("First IR is undefined");let t={version:"1.0",extractedAt:new Date().toISOString(),project:d.project,endpoints:[],schemas:[],errors:[],events:[],ambiguities:[],stats:{filesScanned:0,endpointsFound:0,schemasFound:0,errorsFound:0,eventsFound:0,ambiguitiesFound:0,highConfidence:0,mediumConfidence:0,lowConfidence:0}};for(let o of s)t.endpoints.push(...o.endpoints),t.schemas.push(...o.schemas),t.errors.push(...o.errors),t.events.push(...o.events),t.ambiguities.push(...o.ambiguities),t.stats.filesScanned+=o.stats.filesScanned,t.stats.endpointsFound+=o.stats.endpointsFound,t.stats.schemasFound+=o.stats.schemasFound,t.stats.errorsFound+=o.stats.errorsFound,t.stats.eventsFound+=o.stats.eventsFound,t.stats.ambiguitiesFound+=o.stats.ambiguitiesFound,t.stats.highConfidence+=o.stats.highConfidence,t.stats.mediumConfidence+=o.stats.mediumConfidence,t.stats.lowConfidence+=o.stats.lowConfidence;return t}function m(s){return{version:"1.0",extractedAt:new Date().toISOString(),project:s,endpoints:[],schemas:[],errors:[],events:[],ambiguities:[],stats:{filesScanned:0,endpointsFound:0,schemasFound:0,errorsFound:0,eventsFound:0,ambiguitiesFound:0,highConfidence:0,mediumConfidence:0,lowConfidence:0}}}class C{priority=10;fs;setFs(s){this.fs=s}async detect(s){return s.frameworks.some((d)=>this.frameworks.includes(d.id))}async extract(s,d){if(!this.fs)return{success:!1,errors:[{code:"NO_FS_ADAPTER",message:"File system adapter not configured",recoverable:!1}]};let t=m(s),o={project:s,options:d,fs:this.fs,ir:t};try{return await this.doExtract(o),this.calculateStats(t),{success:!0,ir:t}}catch(i){return{success:!1,errors:[{code:"EXTRACTION_ERROR",message:i instanceof Error?i.message:String(i),recoverable:!1}]}}}calculateStats(s){s.stats.endpointsFound=s.endpoints.length,s.stats.schemasFound=s.schemas.length,s.stats.errorsFound=s.errors.length,s.stats.eventsFound=s.events.length,s.stats.ambiguitiesFound=s.ambiguities.length;let d=[...s.endpoints,...s.schemas,...s.errors,...s.events];for(let t of d)switch(t.confidence.level){case"high":s.stats.highConfidence++;break;case"medium":s.stats.mediumConfidence++;break;case"low":case"ambiguous":s.stats.lowConfidence++;break}}generateEndpointId(s,d,t){let o=d.replace(/^\//,"").replace(/\//g,".").replace(/:/g,"").replace(/\{/g,"").replace(/\}/g,""),i=`${s.toLowerCase()}.${o}`;return t?`${i}.${t}`:i}generateSchemaId(s,d){return`${d.replace(/\.ts$/,"").replace(/\//g,".").replace(/^\.+/,"")}.${s}`}methodToOpKind(s){switch(s){case"GET":case"HEAD":case"OPTIONS":return"query";default:return"command"}}createLocation(s,d,t){return{file:s,startLine:d,endLine:t}}createConfidence(s,...d){return{level:s,reasons:d}}addEndpoint(s,d){s.ir.endpoints.push(d)}addSchema(s,d){s.ir.schemas.push(d)}}var b={route:/\.(get|post|put|patch|delete)\s*\(\s*['"`]([^'"`]+)['"`]/gi,tSchema:/body:\s*t\.\w+/g};class h extends C{id="elysia";name="Elysia Extractor";frameworks=["elysia"];priority=15;async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((u)=>`${u}/**/*.ts`).join(","):"**/*.ts",n=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=n.length;for(let u of n){if(u.includes("node_modules")||u.includes(".test."))continue;let E=`${d.rootPath}/${u}`,a=await o.readFile(E);if(!a.includes("elysia"))continue;await this.extractRoutes(s,u,a)}}async extractRoutes(s,d,t){let o=[...t.matchAll(b.route)];for(let i of o){let n=i[1]?.toUpperCase()??"GET",u=i[2]??"/",E=i.index??0,a=t.slice(0,E).split(`
6
- `).length,p=t.slice(E,E+500),c=b.tSchema.test(p),w={id:this.generateEndpointId(n,u),method:n,path:u,kind:this.methodToOpKind(n),handlerName:"handler",source:this.createLocation(d,a,a+5),confidence:this.createConfidence(c?"high":"medium",c?"explicit-schema":"decorator-hints")};this.addEndpoint(s,w)}}}var I={route:/(?:app|router)\.(get|post|put|patch|delete|head|options)\s*\(\s*['"`]([^'"`]+)['"`]/gi,routerUse:/(?:app|router)\.use\s*\(\s*['"`]([^'"`]+)['"`]/gi,zodValidate:/validate\s*\(\s*(\w+)\s*\)/g};class G extends C{id="express";name="Express Extractor";frameworks=["express"];priority=15;async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((u)=>`${u}/**/*.ts`).join(","):"**/*.ts",n=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=n.length;for(let u of n){if(u.includes("node_modules")||u.includes(".test."))continue;let E=`${d.rootPath}/${u}`,a=await o.readFile(E);await this.extractRoutes(s,u,a)}}async extractRoutes(s,d,t){let o=[...t.matchAll(I.route)];for(let i of o){let n=i[1]?.toUpperCase()??"GET",u=i[2]??"/",E=i.index??0,a=t.slice(0,E).split(`
7
- `).length,p=t.slice(E,E+500),c=p.match(/(?:async\s+)?(?:function\s+)?(\w+)|,\s*(\w+)\s*\)/),w=c?.[1]??c?.[2]??"handler",g=I.zodValidate.test(p),y={id:this.generateEndpointId(n,u,w),method:n,path:u,kind:this.methodToOpKind(n),handlerName:w,source:this.createLocation(d,a,a+5),confidence:this.createConfidence(g?"high":"medium",g?"explicit-schema":"decorator-hints")};this.addEndpoint(s,y)}}}var k={route:/(?:fastify|app|server)\.(get|post|put|patch|delete|head|options)\s*\(\s*['"`]([^'"`]+)['"`]/gi,schemaOption:/schema\s*:\s*\{/g,bodySchema:/body\s*:\s*(\w+)/g,responseSchema:/response\s*:\s*\{/g};class O extends C{id="fastify";name="Fastify Extractor";frameworks=["fastify"];priority=15;async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((u)=>`${u}/**/*.ts`).join(","):"**/*.ts",n=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=n.length;for(let u of n){if(u.includes("node_modules")||u.includes(".test."))continue;let E=`${d.rootPath}/${u}`,a=await o.readFile(E);await this.extractRoutes(s,u,a)}}async extractRoutes(s,d,t){let o=[...t.matchAll(k.route)];for(let i of o){let n=i[1]?.toUpperCase()??"GET",u=i[2]??"/",E=i.index??0,a=t.slice(0,E).split(`
8
- `).length,p=t.slice(E,E+1000),c=k.schemaOption.test(p),w={id:this.generateEndpointId(n,u),method:n,path:u,kind:this.methodToOpKind(n),handlerName:"handler",source:this.createLocation(d,a,a+10),confidence:this.createConfidence(c?"high":"medium",c?"explicit-schema":"decorator-hints"),frameworkMeta:{hasSchema:c}};this.addEndpoint(s,w)}}}var T={route:/(?:app|hono)\.(get|post|put|patch|delete|all)\s*\(\s*['"`]([^'"`]+)['"`]/gi,zodValidator:/zValidator\s*\(\s*['"`](\w+)['"`]\s*,\s*(\w+)/g};class f extends C{id="hono";name="Hono Extractor";frameworks=["hono"];priority=15;async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((u)=>`${u}/**/*.ts`).join(","):"**/*.ts",n=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=n.length;for(let u of n){if(u.includes("node_modules")||u.includes(".test."))continue;let E=`${d.rootPath}/${u}`,a=await o.readFile(E);await this.extractRoutes(s,u,a)}}async extractRoutes(s,d,t){let o=[...t.matchAll(T.route)];for(let i of o){let n=i[1]?.toUpperCase()??"GET",u=i[2]??"/",E=i.index??0,a=t.slice(0,E).split(`
9
- `).length,p=t.slice(E,E+500),c=T.zodValidator.test(p),w={id:this.generateEndpointId(n,u),method:n,path:u,kind:this.methodToOpKind(n),handlerName:"handler",source:this.createLocation(d,a,a+5),confidence:this.createConfidence(c?"high":"medium",c?"explicit-schema":"decorator-hints")};this.addEndpoint(s,w)}}}var H={controller:/@Controller\s*\(\s*['"`]([^'"`]*)['"`]\s*\)/g,route:/@(Get|Post|Put|Patch|Delete|Head|Options)\s*\(\s*(?:['"`]([^'"`]*)['"`])?\s*\)/g,body:/@Body\s*\(\s*\)/g,param:/@Param\s*\(\s*(?:['"`]([^'"`]*)['"`])?\s*\)/g,query:/@Query\s*\(\s*(?:['"`]([^'"`]*)['"`])?\s*\)/g,dto:/class\s+(\w+(?:Dto|DTO|Request|Response|Input|Output))\s*\{/g,classValidator:/@(IsString|IsNumber|IsBoolean|IsArray|IsOptional|IsNotEmpty|Min|Max|Length|Matches)/g};class q extends C{id="nestjs";name="NestJS Extractor";frameworks=["nestjs"];priority=20;async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((u)=>`${u}/**/*.ts`).join(","):"**/*.ts",n=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=n.length;for(let u of n){if(u.includes("node_modules")||u.includes(".spec.")||u.includes(".test."))continue;let E=`${d.rootPath}/${u}`,a=await o.readFile(E);await this.extractControllers(s,u,a),await this.extractDtos(s,u,a)}}async extractControllers(s,d,t){let o=[...t.matchAll(H.controller)];for(let i of o){let n=i[1]||"",u=i.index??0,p=t.slice(u).match(/class\s+(\w+)/)?.[1]??"UnknownController",c=t.indexOf("@Controller",u+1),w=c>0?t.slice(u,c):t.slice(u),g=[...w.matchAll(H.route)];for(let y of g){let $=y[1]?.toUpperCase(),X=y[2]||"",K=this.normalizePath(`/${n}/${X}`),Q=w.slice(y.index??0),z=Q.match(/(?:async\s+)?(\w+)\s*\([^)]*\)\s*(?::\s*\w+(?:<[^>]+>)?)?\s*\{/)?.[1]??"unknownHandler",N=u+(y.index??0),D=t.slice(0,N).split(`
10
- `).length,l=H.body.test(Q.slice(0,200)),R=H.param.test(Q.slice(0,200)),x=H.query.test(Q.slice(0,200)),r={id:this.generateEndpointId($,K,z),method:$,path:K,kind:this.methodToOpKind($),handlerName:z,controllerName:p,source:this.createLocation(d,D,D+10),confidence:this.createConfidence("medium","decorator-hints"),frameworkMeta:{hasBody:l,hasParams:R,hasQuery:x}};this.addEndpoint(s,r)}}}async extractDtos(s,d,t){let o=[...t.matchAll(H.dto)];for(let i of o){let n=i[1]??"UnknownDto",u=i.index??0,E=t.slice(0,u).split(`
11
- `).length,a=t.includes("class-validator")||t.includes("@IsString")||t.includes("@IsNumber"),p={id:this.generateSchemaId(n,d),name:n,schemaType:a?"class-validator":"typescript",source:this.createLocation(d,E,E+20),confidence:this.createConfidence(a?"high":"medium",a?"explicit-schema":"inferred-types")};this.addSchema(s,p)}}normalizePath(s){return"/"+s.replace(/\/+/g,"/").replace(/^\/+|\/+$/g,"")}}var U={appRouterExport:/export\s+(?:async\s+)?function\s+(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)/gi,pagesHandler:/export\s+default\s+(?:async\s+)?function/g,zodSchema:/z\.\w+\(/g};class V extends C{id="next-api";name="Next.js API Extractor";frameworks=["next-api"];priority=15;async doExtract(s){let{project:d,fs:t}=s,o=await t.glob("**/app/api/**/route.ts",{cwd:d.rootPath}),i=await t.glob("**/pages/api/**/*.ts",{cwd:d.rootPath}),n=[...o,...i];s.ir.stats.filesScanned=n.length;for(let u of n){let E=`${d.rootPath}/${u}`,a=await t.readFile(E);if(u.includes("/app/api/"))await this.extractAppRoutes(s,u,a);else await this.extractPagesRoutes(s,u,a)}}async extractAppRoutes(s,d,t){let o=d.match(/app\/api\/(.+)\/route\.ts$/),i=o?`/api/${o[1]}`:"/api",n=[...t.matchAll(U.appRouterExport)];for(let u of n){let E=u[1]?.toUpperCase()??"GET",a=u.index??0,p=t.slice(0,a).split(`
12
- `).length,c=U.zodSchema.test(t),w={id:this.generateEndpointId(E,i),method:E,path:i.replace(/\[(\w+)\]/g,":$1"),kind:this.methodToOpKind(E),handlerName:E,source:this.createLocation(d,p,p+10),confidence:this.createConfidence(c?"high":"medium",c?"explicit-schema":"inferred-types"),frameworkMeta:{routeType:"app-router"}};this.addEndpoint(s,w)}}async extractPagesRoutes(s,d,t){let o=d.match(/pages\/api\/(.+)\.ts$/),i=o?`/api/${o[1]}`:"/api";if(!U.pagesHandler.test(t))return;let n=1,u=U.zodSchema.test(t),E=["GET","POST"];for(let a of E){let p={id:this.generateEndpointId(a,i),method:a,path:i.replace(/\[(\w+)\]/g,":$1"),kind:this.methodToOpKind(a),handlerName:"handler",source:this.createLocation(d,n,n+20),confidence:this.createConfidence("low","naming-convention"),frameworkMeta:{routeType:"pages-router"}};this.addEndpoint(s,p)}}}var B={procedure:/\.(query|mutation)\s*\(\s*(?:\{[^}]*\}|[^)]+)\)/gi,procedureName:/(\w+)\s*:\s*(?:publicProcedure|protectedProcedure|procedure)/g,zodInput:/\.input\s*\(\s*(\w+)/g,zodOutput:/\.output\s*\(\s*(\w+)/g};class S extends C{id="trpc";name="tRPC Extractor";frameworks=["trpc"];priority=15;async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((u)=>`${u}/**/*.ts`).join(","):"**/*.ts",n=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=n.length;for(let u of n){if(u.includes("node_modules")||u.includes(".test."))continue;let E=`${d.rootPath}/${u}`,a=await o.readFile(E);if(!a.includes("trpc")&&!a.includes("Procedure"))continue;await this.extractProcedures(s,u,a)}}async extractProcedures(s,d,t){let o=[...t.matchAll(B.procedureName)];for(let i of o){let n=i[1]??"unknownProcedure",u=i.index??0,E=t.slice(0,u).split(`
13
- `).length,a=t.slice(u,u+500),p=a.includes(".query("),c=a.includes(".mutation(");if(!p&&!c)continue;let w=B.zodInput.test(a),g=B.zodOutput.test(a),y=w||g,$=c?"POST":"GET",X={id:`trpc.${n}`,method:$,path:`/trpc/${n}`,kind:c?"command":"query",handlerName:n,source:this.createLocation(d,E,E+10),confidence:this.createConfidence(y?"high":"medium",y?"explicit-schema":"inferred-types"),frameworkMeta:{procedureType:c?"mutation":"query",hasInput:w,hasOutput:g}};this.addEndpoint(s,X)}}}var cs={zodSchema:/(?:export\s+)?const\s+(\w+)\s*=\s*z\.(?:object|string|number|boolean|array|enum|union|intersection|literal|tuple|record)/g,zodInfer:/type\s+(\w+)\s*=\s*z\.infer<typeof\s+(\w+)>/g};class Z extends C{id="zod";name="Zod Schema Extractor";frameworks=["zod"];priority=5;async detect(){return!0}async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((u)=>`${u}/**/*.ts`).join(","):"**/*.ts",n=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=n.length;for(let u of n){if(u.includes("node_modules")||u.includes(".test."))continue;let E=`${d.rootPath}/${u}`,a=await o.readFile(E);if(!a.includes("z."))continue;await this.extractSchemas(s,u,a)}}async extractSchemas(s,d,t){let o=[...t.matchAll(cs.zodSchema)];for(let i of o){let n=i[1]??"unknownSchema",u=i.index??0,E=t.slice(0,u).split(`
14
- `).length,a=0,p=u;for(let y=u;y<t.length;y++){let $=t[y];if($==="("||$==="{"||$==="[")a++;if($===")"||$==="}"||$==="]")a--;if(a===0&&($===";"||$===`
15
- `)){p=y;break}}let c=t.slice(u,p+1),w=E+c.split(`
16
- `).length-1,g={id:this.generateSchemaId(n,d),name:n,schemaType:"zod",rawDefinition:c,source:this.createLocation(d,E,w),confidence:this.createConfidence("high","explicit-schema")};this.addSchema(s,g)}}}function ps(){v.register(new q),v.register(new G),v.register(new O),v.register(new f),v.register(new h),v.register(new S),v.register(new V),v.register(new Z)}var M=[{id:"nestjs",name:"NestJS",packages:["@nestjs/core","@nestjs/common"],importPatterns:[/@nestjs\//]},{id:"express",name:"Express",packages:["express"],importPatterns:[/from ['"]express['"]/]},{id:"fastify",name:"Fastify",packages:["fastify"],importPatterns:[/from ['"]fastify['"]/]},{id:"hono",name:"Hono",packages:["hono"],importPatterns:[/from ['"]hono['"]/]},{id:"elysia",name:"Elysia",packages:["elysia"],importPatterns:[/from ['"]elysia['"]/]},{id:"trpc",name:"tRPC",packages:["@trpc/server"],importPatterns:[/@trpc\/server/]},{id:"next-api",name:"Next.js API",packages:["next"],filePatterns:[/app\/api\/.*\/route\.ts$/,/pages\/api\/.*\.ts$/]},{id:"koa",name:"Koa",packages:["koa","@koa/router"],importPatterns:[/from ['"]koa['"]/]},{id:"hapi",name:"Hapi",packages:["@hapi/hapi"],importPatterns:[/@hapi\/hapi/]}];function Cs(s){let d={...s.dependencies,...s.devDependencies,...s.peerDependencies},t=[];for(let o of M)for(let i of o.packages)if(i in d){t.push({id:o.id,name:o.name,version:d[i],confidence:"high"});break}return t}function Et(s){let d=[],t=new Set;for(let o of M){if(!o.importPatterns)continue;for(let i of o.importPatterns)if(i.test(s)&&!t.has(o.id)){d.push({id:o.id,name:o.name,confidence:"medium"}),t.add(o.id);break}}return d}function ct(s){let d=[],t=new Set;for(let o of M){if(!o.filePatterns)continue;for(let i of o.filePatterns)for(let n of s)if(i.test(n)&&!t.has(o.id)){d.push({id:o.id,name:o.name,confidence:"medium"}),t.add(o.id);break}}return d}function pt(...s){let d=new Map,t={high:3,medium:2,low:1,ambiguous:0};for(let o of s)for(let i of o){let n=d.get(i.id);if(!n||t[i.confidence]>t[n.confidence])d.set(i.id,i)}return Array.from(d.values())}async function Ct(s,d){let t={rootPath:s,frameworks:[]};if(d?.readFile)try{let o=`${s}/package.json`,i=await d.readFile(o),n=JSON.parse(i);t.packageJsonPath=o,t.frameworks=Cs(n)}catch{}if(d?.readFile)try{let o=`${s}/tsconfig.json`;await d.readFile(o),t.tsConfigPath=o}catch{}return t}function wt(){return M.map((s)=>s.id)}function yt(s){return M.some((d)=>d.id===s)}export{Os as registerBuiltInExtractors,Ss as mergeIRs,pt as mergeFrameworkDetections,yt as isFrameworkSupported,wt as getSupportedFrameworks,j as extractors,v as extractorRegistry,Vs as extractFromProject,ct as detectFrameworksFromPaths,Cs as detectFrameworksFromPackageJson,Et as detectFrameworksFromCode,Ct as detectFramework,m as createEmptyIR,A as codegen,J as ExtractorRegistry};
5
+ `)}function Es(s,d){let t;switch(s.toLowerCase()){case"string":t="z.string()";break;case"number":t="z.number()";break;case"boolean":t="z.boolean()";break;case"date":t="z.date()";break;case"string[]":case"array<string>":t="z.array(z.string())";break;case"number[]":case"array<number>":t="z.array(z.number())";break;default:t="z.unknown()"}return d?`${t}.optional()`:t}var j={};L(j,{registerAllExtractors:()=>Cs,ZodSchemaExtractor:()=>Z,TrpcExtractor:()=>S,NextApiExtractor:()=>f,NestJsExtractor:()=>V,HonoExtractor:()=>q,FastifyExtractor:()=>O,ExpressExtractor:()=>G,ElysiaExtractor:()=>h,BaseExtractor:()=>c});class J{extractors=new Map;register(s){this.extractors.set(s.id,s)}unregister(s){return this.extractors.delete(s)}get(s){return this.extractors.get(s)}getAll(){return Array.from(this.extractors.values())}async findMatching(s){let d=[];for(let t of this.extractors.values())try{if(await t.detect(s))d.push(t)}catch{}return d.sort((t,o)=>o.priority-t.priority)}findByFramework(s){let d=[];for(let t of this.extractors.values())if(t.frameworks.includes(s))d.push(t);return d.sort((t,o)=>o.priority-t.priority)}findForFramework(s){return this.findByFramework(s)}hasExtractorFor(s){for(let d of this.extractors.values())if(d.frameworks.includes(s)||d.id===s)return!0;return!1}getSupportedFrameworks(){let s=new Set;for(let d of this.extractors.values()){s.add(d.id);for(let t of d.frameworks)s.add(t)}return Array.from(s)}}var v=new J;function qs(){}async function Ss(s,d={}){let t;if(d.framework){if(t=v.findByFramework(d.framework),t.length===0)return{success:!1,errors:[{code:"EXTRACTOR_NOT_FOUND",message:`No extractor found for framework: ${d.framework}`,recoverable:!1}]}}else if(t=await v.findMatching(s),t.length===0)return{success:!1,errors:[{code:"NO_FRAMEWORK_DETECTED",message:"No supported framework detected in project",recoverable:!1}]};let o=t[0];if(!o)return{success:!1,errors:[{code:"NO_EXTRACTOR",message:"No extractor available",recoverable:!1}]};return await o.extract(s,d)}function Zs(s){if(s.length===0)throw Error("Cannot merge empty IR array");if(s.length===1){if(!s[0])throw Error("First IR is undefined");return s[0]}let d=s[0];if(!d)throw Error("First IR is undefined");let t={version:"1.0",extractedAt:new Date().toISOString(),project:d.project,endpoints:[],schemas:[],errors:[],events:[],ambiguities:[],stats:{filesScanned:0,endpointsFound:0,schemasFound:0,errorsFound:0,eventsFound:0,ambiguitiesFound:0,highConfidence:0,mediumConfidence:0,lowConfidence:0}};for(let o of s)t.endpoints.push(...o.endpoints),t.schemas.push(...o.schemas),t.errors.push(...o.errors),t.events.push(...o.events),t.ambiguities.push(...o.ambiguities),t.stats.filesScanned+=o.stats.filesScanned,t.stats.endpointsFound+=o.stats.endpointsFound,t.stats.schemasFound+=o.stats.schemasFound,t.stats.errorsFound+=o.stats.errorsFound,t.stats.eventsFound+=o.stats.eventsFound,t.stats.ambiguitiesFound+=o.stats.ambiguitiesFound,t.stats.highConfidence+=o.stats.highConfidence,t.stats.mediumConfidence+=o.stats.mediumConfidence,t.stats.lowConfidence+=o.stats.lowConfidence;return t}function m(s){return{version:"1.0",extractedAt:new Date().toISOString(),project:s,endpoints:[],schemas:[],errors:[],events:[],ambiguities:[],stats:{filesScanned:0,endpointsFound:0,schemasFound:0,errorsFound:0,eventsFound:0,ambiguitiesFound:0,highConfidence:0,mediumConfidence:0,lowConfidence:0}}}class c{priority=10;fs;setFs(s){this.fs=s}async detect(s){return s.frameworks.some((d)=>this.frameworks.includes(d.id))}async extract(s,d){if(!this.fs)return{success:!1,errors:[{code:"NO_FS_ADAPTER",message:"File system adapter not configured",recoverable:!1}]};let t=m(s),o={project:s,options:d,fs:this.fs,ir:t};try{return await this.doExtract(o),this.calculateStats(t),{success:!0,ir:t}}catch(i){return{success:!1,errors:[{code:"EXTRACTION_ERROR",message:i instanceof Error?i.message:String(i),recoverable:!1}]}}}calculateStats(s){s.stats.endpointsFound=s.endpoints.length,s.stats.schemasFound=s.schemas.length,s.stats.errorsFound=s.errors.length,s.stats.eventsFound=s.events.length,s.stats.ambiguitiesFound=s.ambiguities.length;let d=[...s.endpoints,...s.schemas,...s.errors,...s.events];for(let t of d)switch(t.confidence.level){case"high":s.stats.highConfidence++;break;case"medium":s.stats.mediumConfidence++;break;case"low":case"ambiguous":s.stats.lowConfidence++;break}}generateEndpointId(s,d,t){let o=d.replace(/^\//,"").replace(/\//g,".").replace(/:/g,"").replace(/\{/g,"").replace(/\}/g,""),i=`${s.toLowerCase()}.${o}`;return t?`${i}.${t}`:i}generateSchemaId(s,d){return`${d.replace(/\.ts$/,"").replace(/\//g,".").replace(/^\.+/,"")}.${s}`}methodToOpKind(s){switch(s){case"GET":case"HEAD":case"OPTIONS":return"query";default:return"command"}}createLocation(s,d,t){return{file:s,startLine:d,endLine:t}}createConfidence(s,...d){return{level:s,reasons:d}}addEndpoint(s,d){s.ir.endpoints.push(d)}addSchema(s,d){s.ir.schemas.push(d)}}var b={route:/\.(get|post|put|patch|delete)\s*\(\s*['"`]([^'"`]+)['"`]/gi,tSchema:/body:\s*t\.\w+/g};class h extends c{id="elysia";name="Elysia Extractor";frameworks=["elysia"];priority=15;async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((n)=>`${n}/**/*.ts`).join(","):"**/*.ts",u=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=u.length;for(let n of u){if(n.includes("node_modules")||n.includes(".test."))continue;let E=`${d.rootPath}/${n}`,a=await o.readFile(E);if(!a.includes("elysia"))continue;await this.extractRoutes(s,n,a)}}async extractRoutes(s,d,t){let o=[...t.matchAll(b.route)];for(let i of o){let u=i[1]?.toUpperCase()??"GET",n=i[2]??"/",E=i.index??0,a=t.slice(0,E).split(`
6
+ `).length,C=t.slice(E,E+500),p=b.tSchema.test(C),w={id:this.generateEndpointId(u,n),method:u,path:n,kind:this.methodToOpKind(u),handlerName:"handler",source:this.createLocation(d,a,a+5),confidence:this.createConfidence(p?"high":"medium",p?"explicit-schema":"decorator-hints")};this.addEndpoint(s,w)}}}var I={route:/(?:app|router)\.(get|post|put|patch|delete|head|options)\s*\(\s*['"`]([^'"`]+)['"`]/gi,routerUse:/(?:app|router)\.use\s*\(\s*['"`]([^'"`]+)['"`]/gi,zodValidate:/validate\s*\(\s*(\w+)\s*\)/g};class G extends c{id="express";name="Express Extractor";frameworks=["express"];priority=15;async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((n)=>`${n}/**/*.ts`).join(","):"**/*.ts",u=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=u.length;for(let n of u){if(n.includes("node_modules")||n.includes(".test."))continue;let E=`${d.rootPath}/${n}`,a=await o.readFile(E);await this.extractRoutes(s,n,a)}}async extractRoutes(s,d,t){let o=[...t.matchAll(I.route)];for(let i of o){let u=i[1]?.toUpperCase()??"GET",n=i[2]??"/",E=i.index??0,a=t.slice(0,E).split(`
7
+ `).length,C=t.slice(E,E+500),p=C.match(/(?:async\s+)?(?:function\s+)?(\w+)|,\s*(\w+)\s*\)/),w=p?.[1]??p?.[2]??"handler",g=I.zodValidate.test(C),y={id:this.generateEndpointId(u,n,w),method:u,path:n,kind:this.methodToOpKind(u),handlerName:w,source:this.createLocation(d,a,a+5),confidence:this.createConfidence(g?"high":"medium",g?"explicit-schema":"decorator-hints")};this.addEndpoint(s,y)}}}var k={route:/(?:fastify|app|server)\.(get|post|put|patch|delete|head|options)\s*\(\s*['"`]([^'"`]+)['"`]/gi,schemaOption:/schema\s*:\s*\{/g,bodySchema:/body\s*:\s*(\w+)/g,responseSchema:/response\s*:\s*\{/g};class O extends c{id="fastify";name="Fastify Extractor";frameworks=["fastify"];priority=15;async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((n)=>`${n}/**/*.ts`).join(","):"**/*.ts",u=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=u.length;for(let n of u){if(n.includes("node_modules")||n.includes(".test."))continue;let E=`${d.rootPath}/${n}`,a=await o.readFile(E);await this.extractRoutes(s,n,a)}}async extractRoutes(s,d,t){let o=[...t.matchAll(k.route)];for(let i of o){let u=i[1]?.toUpperCase()??"GET",n=i[2]??"/",E=i.index??0,a=t.slice(0,E).split(`
8
+ `).length,C=t.slice(E,E+1000),p=k.schemaOption.test(C),w={id:this.generateEndpointId(u,n),method:u,path:n,kind:this.methodToOpKind(u),handlerName:"handler",source:this.createLocation(d,a,a+10),confidence:this.createConfidence(p?"high":"medium",p?"explicit-schema":"decorator-hints"),frameworkMeta:{hasSchema:p}};this.addEndpoint(s,w)}}}var T={route:/(?:app|hono)\.(get|post|put|patch|delete|all)\s*\(\s*['"`]([^'"`]+)['"`]/gi,zodValidator:/zValidator\s*\(\s*['"`](\w+)['"`]\s*,\s*(\w+)/g};class q extends c{id="hono";name="Hono Extractor";frameworks=["hono"];priority=15;async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((n)=>`${n}/**/*.ts`).join(","):"**/*.ts",u=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=u.length;for(let n of u){if(n.includes("node_modules")||n.includes(".test."))continue;let E=`${d.rootPath}/${n}`,a=await o.readFile(E);await this.extractRoutes(s,n,a)}}async extractRoutes(s,d,t){let o=[...t.matchAll(T.route)];for(let i of o){let u=i[1]?.toUpperCase()??"GET",n=i[2]??"/",E=i.index??0,a=t.slice(0,E).split(`
9
+ `).length,C=t.slice(E,E+500),p=T.zodValidator.test(C),w={id:this.generateEndpointId(u,n),method:u,path:n,kind:this.methodToOpKind(u),handlerName:"handler",source:this.createLocation(d,a,a+5),confidence:this.createConfidence(p?"high":"medium",p?"explicit-schema":"decorator-hints")};this.addEndpoint(s,w)}}}var H={controller:/@Controller\s*\(\s*['"`]([^'"`]*)['"`]\s*\)/g,route:/@(Get|Post|Put|Patch|Delete|Head|Options)\s*\(\s*(?:['"`]([^'"`]*)['"`])?\s*\)/g,body:/@Body\s*\(\s*\)/g,param:/@Param\s*\(\s*(?:['"`]([^'"`]*)['"`])?\s*\)/g,query:/@Query\s*\(\s*(?:['"`]([^'"`]*)['"`])?\s*\)/g,dto:/class\s+(\w+(?:Dto|DTO|Request|Response|Input|Output))\s*\{/g,classValidator:/@(IsString|IsNumber|IsBoolean|IsArray|IsOptional|IsNotEmpty|Min|Max|Length|Matches)/g};class V extends c{id="nestjs";name="NestJS Extractor";frameworks=["nestjs"];priority=20;async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((n)=>`${n}/**/*.ts`).join(","):"**/*.ts",u=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=u.length;for(let n of u){if(n.includes("node_modules")||n.includes(".spec.")||n.includes(".test."))continue;let E=`${d.rootPath}/${n}`,a=await o.readFile(E);await this.extractControllers(s,n,a),await this.extractDtos(s,n,a)}}async extractControllers(s,d,t){let o=[...t.matchAll(H.controller)];for(let i of o){let u=i[1]||"",n=i.index??0,C=t.slice(n).match(/class\s+(\w+)/)?.[1]??"UnknownController",p=t.indexOf("@Controller",n+1),w=p>0?t.slice(n,p):t.slice(n),g=[...w.matchAll(H.route)];for(let y of g){let $=y[1]?.toUpperCase(),X=y[2]||"",K=this.normalizePath(`/${u}/${X}`),Q=w.slice(y.index??0),z=Q.match(/(?:async\s+)?(\w+)\s*\([^)]*\)\s*(?::\s*\w+(?:<[^>]+>)?)?\s*\{/)?.[1]??"unknownHandler",N=n+(y.index??0),D=t.slice(0,N).split(`
10
+ `).length,l=H.body.test(Q.slice(0,200)),R=H.param.test(Q.slice(0,200)),x=H.query.test(Q.slice(0,200)),r={id:this.generateEndpointId($,K,z),method:$,path:K,kind:this.methodToOpKind($),handlerName:z,controllerName:C,source:this.createLocation(d,D,D+10),confidence:this.createConfidence("medium","decorator-hints"),frameworkMeta:{hasBody:l,hasParams:R,hasQuery:x}};this.addEndpoint(s,r)}}}async extractDtos(s,d,t){let o=[...t.matchAll(H.dto)];for(let i of o){let u=i[1]??"UnknownDto",n=i.index??0,E=t.slice(0,n).split(`
11
+ `).length,a=t.includes("class-validator")||t.includes("@IsString")||t.includes("@IsNumber"),C={id:this.generateSchemaId(u,d),name:u,schemaType:a?"class-validator":"typescript",source:this.createLocation(d,E,E+20),confidence:this.createConfidence(a?"high":"medium",a?"explicit-schema":"inferred-types")};this.addSchema(s,C)}}normalizePath(s){return"/"+s.replace(/\/+/g,"/").replace(/^\/+|\/+$/g,"")}}var U={appRouterExport:/export\s+(?:async\s+)?function\s+(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)/gi,pagesHandler:/export\s+default\s+(?:async\s+)?function/g,zodSchema:/z\.\w+\(/g};class f extends c{id="next-api";name="Next.js API Extractor";frameworks=["next-api"];priority=15;async doExtract(s){let{project:d,fs:t}=s,o=await t.glob("**/app/api/**/route.ts",{cwd:d.rootPath}),i=await t.glob("**/pages/api/**/*.ts",{cwd:d.rootPath}),u=[...o,...i];s.ir.stats.filesScanned=u.length;for(let n of u){let E=`${d.rootPath}/${n}`,a=await t.readFile(E);if(n.includes("/app/api/"))await this.extractAppRoutes(s,n,a);else await this.extractPagesRoutes(s,n,a)}}async extractAppRoutes(s,d,t){let o=d.match(/app\/api\/(.+)\/route\.ts$/),i=o?`/api/${o[1]}`:"/api",u=[...t.matchAll(U.appRouterExport)];for(let n of u){let E=n[1]?.toUpperCase()??"GET",a=n.index??0,C=t.slice(0,a).split(`
12
+ `).length,p=U.zodSchema.test(t),w={id:this.generateEndpointId(E,i),method:E,path:i.replace(/\[(\w+)\]/g,":$1"),kind:this.methodToOpKind(E),handlerName:E,source:this.createLocation(d,C,C+10),confidence:this.createConfidence(p?"high":"medium",p?"explicit-schema":"inferred-types"),frameworkMeta:{routeType:"app-router"}};this.addEndpoint(s,w)}}async extractPagesRoutes(s,d,t){let o=d.match(/pages\/api\/(.+)\.ts$/),i=o?`/api/${o[1]}`:"/api";if(!U.pagesHandler.test(t))return;let u=1,n=U.zodSchema.test(t),E=["GET","POST"];for(let a of E){let C={id:this.generateEndpointId(a,i),method:a,path:i.replace(/\[(\w+)\]/g,":$1"),kind:this.methodToOpKind(a),handlerName:"handler",source:this.createLocation(d,u,u+20),confidence:this.createConfidence("low","naming-convention"),frameworkMeta:{routeType:"pages-router"}};this.addEndpoint(s,C)}}}var B={procedure:/\.(query|mutation)\s*\(\s*(?:\{[^}]*\}|[^)]+)\)/gi,procedureName:/(\w+)\s*:\s*(?:publicProcedure|protectedProcedure|procedure)/g,zodInput:/\.input\s*\(\s*(\w+)/g,zodOutput:/\.output\s*\(\s*(\w+)/g};class S extends c{id="trpc";name="tRPC Extractor";frameworks=["trpc"];priority=15;async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((n)=>`${n}/**/*.ts`).join(","):"**/*.ts",u=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=u.length;for(let n of u){if(n.includes("node_modules")||n.includes(".test."))continue;let E=`${d.rootPath}/${n}`,a=await o.readFile(E);if(!a.includes("trpc")&&!a.includes("Procedure"))continue;await this.extractProcedures(s,n,a)}}async extractProcedures(s,d,t){let o=[...t.matchAll(B.procedureName)];for(let i of o){let u=i[1]??"unknownProcedure",n=i.index??0,E=t.slice(0,n).split(`
13
+ `).length,a=t.slice(n,n+500),C=a.includes(".query("),p=a.includes(".mutation(");if(!C&&!p)continue;let w=B.zodInput.test(a),g=B.zodOutput.test(a),y=w||g,$=p?"POST":"GET",X={id:`trpc.${u}`,method:$,path:`/trpc/${u}`,kind:p?"command":"query",handlerName:u,source:this.createLocation(d,E,E+10),confidence:this.createConfidence(y?"high":"medium",y?"explicit-schema":"inferred-types"),frameworkMeta:{procedureType:p?"mutation":"query",hasInput:w,hasOutput:g}};this.addEndpoint(s,X)}}}var ps={zodSchema:/(?:export\s+)?const\s+(\w+)\s*=\s*z\.(?:object|string|number|boolean|array|enum|union|intersection|literal|tuple|record)/g,zodInfer:/type\s+(\w+)\s*=\s*z\.infer<typeof\s+(\w+)>/g};class Z extends c{id="zod";name="Zod Schema Extractor";frameworks=["zod"];priority=5;async detect(){return!0}async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((n)=>`${n}/**/*.ts`).join(","):"**/*.ts",u=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=u.length;for(let n of u){if(n.includes("node_modules")||n.includes(".test."))continue;let E=`${d.rootPath}/${n}`,a=await o.readFile(E);if(!a.includes("z."))continue;await this.extractSchemas(s,n,a)}}async extractSchemas(s,d,t){let o=[...t.matchAll(ps.zodSchema)];for(let i of o){let u=i[1]??"unknownSchema",n=i.index??0,E=t.slice(0,n).split(`
14
+ `).length,a=0,C=n;for(let y=n;y<t.length;y++){let $=t[y];if($==="("||$==="{"||$==="[")a++;if($===")"||$==="}"||$==="]")a--;if(a===0&&($===";"||$===`
15
+ `)){C=y;break}}let p=t.slice(n,C+1),w=E+p.split(`
16
+ `).length-1,g={id:this.generateSchemaId(u,d),name:u,schemaType:"zod",rawDefinition:p,source:this.createLocation(d,E,w),confidence:this.createConfidence("high","explicit-schema")};this.addSchema(s,g)}}}function Cs(){v.register(new V),v.register(new G),v.register(new O),v.register(new q),v.register(new h),v.register(new S),v.register(new f),v.register(new Z)}var M=[{id:"nestjs",name:"NestJS",packages:["@nestjs/core","@nestjs/common"],importPatterns:[/@nestjs\//]},{id:"express",name:"Express",packages:["express"],importPatterns:[/from ['"]express['"]/]},{id:"fastify",name:"Fastify",packages:["fastify"],importPatterns:[/from ['"]fastify['"]/]},{id:"hono",name:"Hono",packages:["hono"],importPatterns:[/from ['"]hono['"]/]},{id:"elysia",name:"Elysia",packages:["elysia"],importPatterns:[/from ['"]elysia['"]/]},{id:"trpc",name:"tRPC",packages:["@trpc/server"],importPatterns:[/@trpc\/server/]},{id:"next-api",name:"Next.js API",packages:["next"],filePatterns:[/app\/api\/.*\/route\.ts$/,/pages\/api\/.*\.ts$/]},{id:"koa",name:"Koa",packages:["koa","@koa/router"],importPatterns:[/from ['"]koa['"]/]},{id:"hapi",name:"Hapi",packages:["@hapi/hapi"],importPatterns:[/@hapi\/hapi/]}];function cs(s){let d={...s.dependencies,...s.devDependencies,...s.peerDependencies},t=[];for(let o of M)for(let i of o.packages)if(i in d){t.push({id:o.id,name:o.name,version:d[i],confidence:"high"});break}return t}function pt(s){let d=[],t=new Set;for(let o of M){if(!o.importPatterns)continue;for(let i of o.importPatterns)if(i.test(s)&&!t.has(o.id)){d.push({id:o.id,name:o.name,confidence:"medium"}),t.add(o.id);break}}return d}function Ct(s){let d=[],t=new Set;for(let o of M){if(!o.filePatterns)continue;for(let i of o.filePatterns)for(let u of s)if(i.test(u)&&!t.has(o.id)){d.push({id:o.id,name:o.name,confidence:"medium"}),t.add(o.id);break}}return d}function ct(...s){let d=new Map,t={high:3,medium:2,low:1,ambiguous:0};for(let o of s)for(let i of o){let u=d.get(i.id);if(!u||t[i.confidence]>t[u.confidence])d.set(i.id,i)}return Array.from(d.values())}async function wt(s,d){let t={rootPath:s,frameworks:[]};if(d?.readFile)try{let o=`${s}/package.json`,i=await d.readFile(o),u=JSON.parse(i);t.packageJsonPath=o,t.frameworks=cs(u)}catch{}if(d?.readFile)try{let o=`${s}/tsconfig.json`;await d.readFile(o),t.tsConfigPath=o}catch{}return t}function yt(){return M.map((s)=>s.id)}function $t(s){return M.some((d)=>d.id===s)}import{defineFeature as ws}from"@contractspec/lib.contracts-spec/features";var Ht=ws({meta:{key:"libs.source-extractors",version:"1.0.0",title:"Source Extractors",description:"Extract contract candidates from TypeScript source code across multiple frameworks (NestJS, Express, Fastify, Hono, Elysia, tRPC, Next.js)",domain:"source-extractors",owners:["@contractspec-core"],tags:["package","libs","source-extractors"],stability:"experimental"}});export{qs as registerBuiltInExtractors,Zs as mergeIRs,ct as mergeFrameworkDetections,$t as isFrameworkSupported,yt as getSupportedFrameworks,j as extractors,v as extractorRegistry,Ss as extractFromProject,Ct as detectFrameworksFromPaths,cs as detectFrameworksFromPackageJson,pt as detectFrameworksFromCode,wt as detectFramework,m as createEmptyIR,A as codegen,Ht as SourceExtractorsFeature,J as ExtractorRegistry};
package/dist/index.d.ts CHANGED
@@ -26,4 +26,5 @@ export * from './detect';
26
26
  export * from './extract';
27
27
  export * as extractors from './extractors/index';
28
28
  export * from './registry';
29
+ export * from './source-extractors.feature';
29
30
  export * from './types';
package/dist/index.js CHANGED
@@ -1,17 +1,17 @@
1
1
  // @bun
2
- var e=Object.defineProperty;var ss=(s)=>s;function ts(s,d){this[s]=ss.bind(null,d)}var F=(s,d)=>{for(var t in d)e(s,t,{get:d[t],enumerable:!0,configurable:!0,set:ts.bind(d,t)})};var A={};F(A,{generateSchemas:()=>P,generateSchema:()=>_,generateRegistry:()=>W,generateOperations:()=>L,generateOperation:()=>Y});function Y(s,d){let t=ds(s),o=`${os(s)}.ts`,i=is(s,t,d);return{path:o,content:i,type:"operation"}}function L(s,d){return s.endpoints.map((t)=>Y(t,d))}function ds(s){let d=s.path.replace(/^\//,"").split("/").filter((i)=>i&&!i.startsWith(":")&&!i.startsWith("{")),t=s.method.toLowerCase(),o=d.map((i)=>i.charAt(0).toUpperCase()+i.slice(1)).join("");return`${t}${o}Spec`}function os(s){let d=s.path.replace(/^\//,"").split("/").filter((i)=>i&&!i.startsWith(":")&&!i.startsWith("{")),t=s.method.toLowerCase(),o=d.join("-");return`${t}-${o}`.replace(/--+/g,"-")}function is(s,d,t){let i=s.kind==="command"?"defineCommand":"defineQuery",n=t.defaultAuth??"user",u=t.defaultOwners??["team"];return["/**",` * ${s.method} ${s.path}`," *",` * Generated from: ${s.source.file}:${s.source.startLine}`,` * Confidence: ${s.confidence.level}`," */","",`import { ${i} } from '@contractspec/lib.contracts-spec';`,"import { fromZod } from '@contractspec/lib.schema';","import { z } from 'zod';","","// TODO: Define input schema based on extracted information","const inputSchema = fromZod(z.object({"," // Add fields here","}));","","// TODO: Define output schema","const outputSchema = fromZod(z.object({"," // Add fields here","}));","",`export const ${d} = ${i}({`," meta: {",` name: '${s.handlerName??s.id}',`," version: 1,"," stability: 'experimental',",` owners: ${JSON.stringify(u)},`," goal: 'TODO: Describe the business goal',",` context: 'Generated from ${s.source.file}',`," },"," io: {"," input: inputSchema,"," output: outputSchema,"," },"," policy: {",` auth: '${n}',`," },"," transport: {"," rest: {",` method: '${s.method}',`,` path: '${s.path}',`," },"," },","});",""].join(`
3
- `)}function W(s){let d=s.filter((o)=>o.type==="operation").map((o)=>{let i=o.path.replace(".ts","").replace(/-/g,"_"),n=us(i)+"Spec";return{path:o.path,name:i,specName:n}}),t=["/**"," * Generated operation registry."," */","","import { OperationSpecRegistry } from '@contractspec/lib.contracts-spec';",""];for(let o of d){let i=`./${o.path.replace(".ts","")}`;t.push(`import { ${o.specName} } from '${i}';`)}t.push(""),t.push("export const operationRegistry = new OperationSpecRegistry();"),t.push("");for(let o of d)t.push(`operationRegistry.register(${o.specName});`);return t.push(""),{path:"registry.ts",content:t.join(`
4
- `),type:"registry"}}function us(s){return s.split(/[-_]/).map((d)=>d.charAt(0).toUpperCase()+d.slice(1)).join("")}function _(s,d){let t=`${ns(s.name)}.ts`,o=as(s);return{path:`schemas/${t}`,content:o,type:"schema"}}function P(s,d){return s.schemas.map((t)=>_(t,d))}function ns(s){return s.replace(/([a-z])([A-Z])/g,"$1-$2").replace(/([A-Z])([A-Z][a-z])/g,"$1-$2").toLowerCase()}function as(s){let d=["/**",` * ${s.name}`," *",` * Generated from: ${s.source.file}:${s.source.startLine}`,` * Schema type: ${s.schemaType}`,` * Confidence: ${s.confidence.level}`," */","","import { fromZod } from '@contractspec/lib.schema';","import { z } from 'zod';",""];if(s.rawDefinition&&s.schemaType==="zod")d.push("// Original definition from source:"),d.push(`// ${s.rawDefinition.split(`
2
+ var e=Object.defineProperty;var ss=(s)=>s;function ts(s,d){this[s]=ss.bind(null,d)}var L=(s,d)=>{for(var t in d)e(s,t,{get:d[t],enumerable:!0,configurable:!0,set:ts.bind(d,t)})};var A={};L(A,{generateSchemas:()=>P,generateSchema:()=>_,generateRegistry:()=>F,generateOperations:()=>W,generateOperation:()=>Y});function Y(s,d){let t=ds(s),o=`${os(s)}.ts`,i=is(s,t,d);return{path:o,content:i,type:"operation"}}function W(s,d){return s.endpoints.map((t)=>Y(t,d))}function ds(s){let d=s.path.replace(/^\//,"").split("/").filter((i)=>i&&!i.startsWith(":")&&!i.startsWith("{")),t=s.method.toLowerCase(),o=d.map((i)=>i.charAt(0).toUpperCase()+i.slice(1)).join("");return`${t}${o}Spec`}function os(s){let d=s.path.replace(/^\//,"").split("/").filter((i)=>i&&!i.startsWith(":")&&!i.startsWith("{")),t=s.method.toLowerCase(),o=d.join("-");return`${t}-${o}`.replace(/--+/g,"-")}function is(s,d,t){let i=s.kind==="command"?"defineCommand":"defineQuery",u=t.defaultAuth??"user",n=t.defaultOwners??["team"];return["/**",` * ${s.method} ${s.path}`," *",` * Generated from: ${s.source.file}:${s.source.startLine}`,` * Confidence: ${s.confidence.level}`," */","",`import { ${i} } from '@contractspec/lib.contracts-spec';`,"import { fromZod } from '@contractspec/lib.schema';","import { z } from 'zod';","","// TODO: Define input schema based on extracted information","const inputSchema = fromZod(z.object({"," // Add fields here","}));","","// TODO: Define output schema","const outputSchema = fromZod(z.object({"," // Add fields here","}));","",`export const ${d} = ${i}({`," meta: {",` name: '${s.handlerName??s.id}',`," version: 1,"," stability: 'experimental',",` owners: ${JSON.stringify(n)},`," goal: 'TODO: Describe the business goal',",` context: 'Generated from ${s.source.file}',`," },"," io: {"," input: inputSchema,"," output: outputSchema,"," },"," policy: {",` auth: '${u}',`," },"," transport: {"," rest: {",` method: '${s.method}',`,` path: '${s.path}',`," },"," },","});",""].join(`
3
+ `)}function F(s){let d=s.filter((o)=>o.type==="operation").map((o)=>{let i=o.path.replace(".ts","").replace(/-/g,"_"),u=ns(i)+"Spec";return{path:o.path,name:i,specName:u}}),t=["/**"," * Generated operation registry."," */","","import { OperationSpecRegistry } from '@contractspec/lib.contracts-spec';",""];for(let o of d){let i=`./${o.path.replace(".ts","")}`;t.push(`import { ${o.specName} } from '${i}';`)}t.push(""),t.push("export const operationRegistry = new OperationSpecRegistry();"),t.push("");for(let o of d)t.push(`operationRegistry.register(${o.specName});`);return t.push(""),{path:"registry.ts",content:t.join(`
4
+ `),type:"registry"}}function ns(s){return s.split(/[-_]/).map((d)=>d.charAt(0).toUpperCase()+d.slice(1)).join("")}function _(s,d){let t=`${us(s.name)}.ts`,o=as(s);return{path:`schemas/${t}`,content:o,type:"schema"}}function P(s,d){return s.schemas.map((t)=>_(t,d))}function us(s){return s.replace(/([a-z])([A-Z])/g,"$1-$2").replace(/([A-Z])([A-Z][a-z])/g,"$1-$2").toLowerCase()}function as(s){let d=["/**",` * ${s.name}`," *",` * Generated from: ${s.source.file}:${s.source.startLine}`,` * Schema type: ${s.schemaType}`,` * Confidence: ${s.confidence.level}`," */","","import { fromZod } from '@contractspec/lib.schema';","import { z } from 'zod';",""];if(s.rawDefinition&&s.schemaType==="zod")d.push("// Original definition from source:"),d.push(`// ${s.rawDefinition.split(`
5
5
  `)[0]}`),d.push("");if(d.push(`export const ${s.name}Schema = fromZod(z.object({`),s.fields&&s.fields.length>0)for(let t of s.fields){let o=Es(t.type,t.optional);d.push(` ${t.name}: ${o},`)}else d.push(" // TODO: Define schema fields");return d.push("}));"),d.push(""),d.push(`export type ${s.name} = z.infer<typeof ${s.name}Schema.zodSchema>;`),d.push(""),d.join(`
6
- `)}function Es(s,d){let t;switch(s.toLowerCase()){case"string":t="z.string()";break;case"number":t="z.number()";break;case"boolean":t="z.boolean()";break;case"date":t="z.date()";break;case"string[]":case"array<string>":t="z.array(z.string())";break;case"number[]":case"array<number>":t="z.array(z.number())";break;default:t="z.unknown()"}return d?`${t}.optional()`:t}var j={};F(j,{registerAllExtractors:()=>ps,ZodSchemaExtractor:()=>Z,TrpcExtractor:()=>S,NextApiExtractor:()=>V,NestJsExtractor:()=>q,HonoExtractor:()=>f,FastifyExtractor:()=>O,ExpressExtractor:()=>G,ElysiaExtractor:()=>h,BaseExtractor:()=>C});class J{extractors=new Map;register(s){this.extractors.set(s.id,s)}unregister(s){return this.extractors.delete(s)}get(s){return this.extractors.get(s)}getAll(){return Array.from(this.extractors.values())}async findMatching(s){let d=[];for(let t of this.extractors.values())try{if(await t.detect(s))d.push(t)}catch{}return d.sort((t,o)=>o.priority-t.priority)}findByFramework(s){let d=[];for(let t of this.extractors.values())if(t.frameworks.includes(s))d.push(t);return d.sort((t,o)=>o.priority-t.priority)}findForFramework(s){return this.findByFramework(s)}hasExtractorFor(s){for(let d of this.extractors.values())if(d.frameworks.includes(s)||d.id===s)return!0;return!1}getSupportedFrameworks(){let s=new Set;for(let d of this.extractors.values()){s.add(d.id);for(let t of d.frameworks)s.add(t)}return Array.from(s)}}var v=new J;function Os(){}async function Vs(s,d={}){let t;if(d.framework){if(t=v.findByFramework(d.framework),t.length===0)return{success:!1,errors:[{code:"EXTRACTOR_NOT_FOUND",message:`No extractor found for framework: ${d.framework}`,recoverable:!1}]}}else if(t=await v.findMatching(s),t.length===0)return{success:!1,errors:[{code:"NO_FRAMEWORK_DETECTED",message:"No supported framework detected in project",recoverable:!1}]};let o=t[0];if(!o)return{success:!1,errors:[{code:"NO_EXTRACTOR",message:"No extractor available",recoverable:!1}]};return await o.extract(s,d)}function Ss(s){if(s.length===0)throw Error("Cannot merge empty IR array");if(s.length===1){if(!s[0])throw Error("First IR is undefined");return s[0]}let d=s[0];if(!d)throw Error("First IR is undefined");let t={version:"1.0",extractedAt:new Date().toISOString(),project:d.project,endpoints:[],schemas:[],errors:[],events:[],ambiguities:[],stats:{filesScanned:0,endpointsFound:0,schemasFound:0,errorsFound:0,eventsFound:0,ambiguitiesFound:0,highConfidence:0,mediumConfidence:0,lowConfidence:0}};for(let o of s)t.endpoints.push(...o.endpoints),t.schemas.push(...o.schemas),t.errors.push(...o.errors),t.events.push(...o.events),t.ambiguities.push(...o.ambiguities),t.stats.filesScanned+=o.stats.filesScanned,t.stats.endpointsFound+=o.stats.endpointsFound,t.stats.schemasFound+=o.stats.schemasFound,t.stats.errorsFound+=o.stats.errorsFound,t.stats.eventsFound+=o.stats.eventsFound,t.stats.ambiguitiesFound+=o.stats.ambiguitiesFound,t.stats.highConfidence+=o.stats.highConfidence,t.stats.mediumConfidence+=o.stats.mediumConfidence,t.stats.lowConfidence+=o.stats.lowConfidence;return t}function m(s){return{version:"1.0",extractedAt:new Date().toISOString(),project:s,endpoints:[],schemas:[],errors:[],events:[],ambiguities:[],stats:{filesScanned:0,endpointsFound:0,schemasFound:0,errorsFound:0,eventsFound:0,ambiguitiesFound:0,highConfidence:0,mediumConfidence:0,lowConfidence:0}}}class C{priority=10;fs;setFs(s){this.fs=s}async detect(s){return s.frameworks.some((d)=>this.frameworks.includes(d.id))}async extract(s,d){if(!this.fs)return{success:!1,errors:[{code:"NO_FS_ADAPTER",message:"File system adapter not configured",recoverable:!1}]};let t=m(s),o={project:s,options:d,fs:this.fs,ir:t};try{return await this.doExtract(o),this.calculateStats(t),{success:!0,ir:t}}catch(i){return{success:!1,errors:[{code:"EXTRACTION_ERROR",message:i instanceof Error?i.message:String(i),recoverable:!1}]}}}calculateStats(s){s.stats.endpointsFound=s.endpoints.length,s.stats.schemasFound=s.schemas.length,s.stats.errorsFound=s.errors.length,s.stats.eventsFound=s.events.length,s.stats.ambiguitiesFound=s.ambiguities.length;let d=[...s.endpoints,...s.schemas,...s.errors,...s.events];for(let t of d)switch(t.confidence.level){case"high":s.stats.highConfidence++;break;case"medium":s.stats.mediumConfidence++;break;case"low":case"ambiguous":s.stats.lowConfidence++;break}}generateEndpointId(s,d,t){let o=d.replace(/^\//,"").replace(/\//g,".").replace(/:/g,"").replace(/\{/g,"").replace(/\}/g,""),i=`${s.toLowerCase()}.${o}`;return t?`${i}.${t}`:i}generateSchemaId(s,d){return`${d.replace(/\.ts$/,"").replace(/\//g,".").replace(/^\.+/,"")}.${s}`}methodToOpKind(s){switch(s){case"GET":case"HEAD":case"OPTIONS":return"query";default:return"command"}}createLocation(s,d,t){return{file:s,startLine:d,endLine:t}}createConfidence(s,...d){return{level:s,reasons:d}}addEndpoint(s,d){s.ir.endpoints.push(d)}addSchema(s,d){s.ir.schemas.push(d)}}var b={route:/\.(get|post|put|patch|delete)\s*\(\s*['"`]([^'"`]+)['"`]/gi,tSchema:/body:\s*t\.\w+/g};class h extends C{id="elysia";name="Elysia Extractor";frameworks=["elysia"];priority=15;async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((u)=>`${u}/**/*.ts`).join(","):"**/*.ts",n=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=n.length;for(let u of n){if(u.includes("node_modules")||u.includes(".test."))continue;let E=`${d.rootPath}/${u}`,a=await o.readFile(E);if(!a.includes("elysia"))continue;await this.extractRoutes(s,u,a)}}async extractRoutes(s,d,t){let o=[...t.matchAll(b.route)];for(let i of o){let n=i[1]?.toUpperCase()??"GET",u=i[2]??"/",E=i.index??0,a=t.slice(0,E).split(`
7
- `).length,p=t.slice(E,E+500),c=b.tSchema.test(p),w={id:this.generateEndpointId(n,u),method:n,path:u,kind:this.methodToOpKind(n),handlerName:"handler",source:this.createLocation(d,a,a+5),confidence:this.createConfidence(c?"high":"medium",c?"explicit-schema":"decorator-hints")};this.addEndpoint(s,w)}}}var I={route:/(?:app|router)\.(get|post|put|patch|delete|head|options)\s*\(\s*['"`]([^'"`]+)['"`]/gi,routerUse:/(?:app|router)\.use\s*\(\s*['"`]([^'"`]+)['"`]/gi,zodValidate:/validate\s*\(\s*(\w+)\s*\)/g};class G extends C{id="express";name="Express Extractor";frameworks=["express"];priority=15;async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((u)=>`${u}/**/*.ts`).join(","):"**/*.ts",n=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=n.length;for(let u of n){if(u.includes("node_modules")||u.includes(".test."))continue;let E=`${d.rootPath}/${u}`,a=await o.readFile(E);await this.extractRoutes(s,u,a)}}async extractRoutes(s,d,t){let o=[...t.matchAll(I.route)];for(let i of o){let n=i[1]?.toUpperCase()??"GET",u=i[2]??"/",E=i.index??0,a=t.slice(0,E).split(`
8
- `).length,p=t.slice(E,E+500),c=p.match(/(?:async\s+)?(?:function\s+)?(\w+)|,\s*(\w+)\s*\)/),w=c?.[1]??c?.[2]??"handler",g=I.zodValidate.test(p),y={id:this.generateEndpointId(n,u,w),method:n,path:u,kind:this.methodToOpKind(n),handlerName:w,source:this.createLocation(d,a,a+5),confidence:this.createConfidence(g?"high":"medium",g?"explicit-schema":"decorator-hints")};this.addEndpoint(s,y)}}}var k={route:/(?:fastify|app|server)\.(get|post|put|patch|delete|head|options)\s*\(\s*['"`]([^'"`]+)['"`]/gi,schemaOption:/schema\s*:\s*\{/g,bodySchema:/body\s*:\s*(\w+)/g,responseSchema:/response\s*:\s*\{/g};class O extends C{id="fastify";name="Fastify Extractor";frameworks=["fastify"];priority=15;async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((u)=>`${u}/**/*.ts`).join(","):"**/*.ts",n=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=n.length;for(let u of n){if(u.includes("node_modules")||u.includes(".test."))continue;let E=`${d.rootPath}/${u}`,a=await o.readFile(E);await this.extractRoutes(s,u,a)}}async extractRoutes(s,d,t){let o=[...t.matchAll(k.route)];for(let i of o){let n=i[1]?.toUpperCase()??"GET",u=i[2]??"/",E=i.index??0,a=t.slice(0,E).split(`
9
- `).length,p=t.slice(E,E+1000),c=k.schemaOption.test(p),w={id:this.generateEndpointId(n,u),method:n,path:u,kind:this.methodToOpKind(n),handlerName:"handler",source:this.createLocation(d,a,a+10),confidence:this.createConfidence(c?"high":"medium",c?"explicit-schema":"decorator-hints"),frameworkMeta:{hasSchema:c}};this.addEndpoint(s,w)}}}var T={route:/(?:app|hono)\.(get|post|put|patch|delete|all)\s*\(\s*['"`]([^'"`]+)['"`]/gi,zodValidator:/zValidator\s*\(\s*['"`](\w+)['"`]\s*,\s*(\w+)/g};class f extends C{id="hono";name="Hono Extractor";frameworks=["hono"];priority=15;async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((u)=>`${u}/**/*.ts`).join(","):"**/*.ts",n=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=n.length;for(let u of n){if(u.includes("node_modules")||u.includes(".test."))continue;let E=`${d.rootPath}/${u}`,a=await o.readFile(E);await this.extractRoutes(s,u,a)}}async extractRoutes(s,d,t){let o=[...t.matchAll(T.route)];for(let i of o){let n=i[1]?.toUpperCase()??"GET",u=i[2]??"/",E=i.index??0,a=t.slice(0,E).split(`
10
- `).length,p=t.slice(E,E+500),c=T.zodValidator.test(p),w={id:this.generateEndpointId(n,u),method:n,path:u,kind:this.methodToOpKind(n),handlerName:"handler",source:this.createLocation(d,a,a+5),confidence:this.createConfidence(c?"high":"medium",c?"explicit-schema":"decorator-hints")};this.addEndpoint(s,w)}}}var H={controller:/@Controller\s*\(\s*['"`]([^'"`]*)['"`]\s*\)/g,route:/@(Get|Post|Put|Patch|Delete|Head|Options)\s*\(\s*(?:['"`]([^'"`]*)['"`])?\s*\)/g,body:/@Body\s*\(\s*\)/g,param:/@Param\s*\(\s*(?:['"`]([^'"`]*)['"`])?\s*\)/g,query:/@Query\s*\(\s*(?:['"`]([^'"`]*)['"`])?\s*\)/g,dto:/class\s+(\w+(?:Dto|DTO|Request|Response|Input|Output))\s*\{/g,classValidator:/@(IsString|IsNumber|IsBoolean|IsArray|IsOptional|IsNotEmpty|Min|Max|Length|Matches)/g};class q extends C{id="nestjs";name="NestJS Extractor";frameworks=["nestjs"];priority=20;async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((u)=>`${u}/**/*.ts`).join(","):"**/*.ts",n=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=n.length;for(let u of n){if(u.includes("node_modules")||u.includes(".spec.")||u.includes(".test."))continue;let E=`${d.rootPath}/${u}`,a=await o.readFile(E);await this.extractControllers(s,u,a),await this.extractDtos(s,u,a)}}async extractControllers(s,d,t){let o=[...t.matchAll(H.controller)];for(let i of o){let n=i[1]||"",u=i.index??0,p=t.slice(u).match(/class\s+(\w+)/)?.[1]??"UnknownController",c=t.indexOf("@Controller",u+1),w=c>0?t.slice(u,c):t.slice(u),g=[...w.matchAll(H.route)];for(let y of g){let $=y[1]?.toUpperCase(),X=y[2]||"",K=this.normalizePath(`/${n}/${X}`),Q=w.slice(y.index??0),z=Q.match(/(?:async\s+)?(\w+)\s*\([^)]*\)\s*(?::\s*\w+(?:<[^>]+>)?)?\s*\{/)?.[1]??"unknownHandler",N=u+(y.index??0),D=t.slice(0,N).split(`
11
- `).length,l=H.body.test(Q.slice(0,200)),R=H.param.test(Q.slice(0,200)),x=H.query.test(Q.slice(0,200)),r={id:this.generateEndpointId($,K,z),method:$,path:K,kind:this.methodToOpKind($),handlerName:z,controllerName:p,source:this.createLocation(d,D,D+10),confidence:this.createConfidence("medium","decorator-hints"),frameworkMeta:{hasBody:l,hasParams:R,hasQuery:x}};this.addEndpoint(s,r)}}}async extractDtos(s,d,t){let o=[...t.matchAll(H.dto)];for(let i of o){let n=i[1]??"UnknownDto",u=i.index??0,E=t.slice(0,u).split(`
12
- `).length,a=t.includes("class-validator")||t.includes("@IsString")||t.includes("@IsNumber"),p={id:this.generateSchemaId(n,d),name:n,schemaType:a?"class-validator":"typescript",source:this.createLocation(d,E,E+20),confidence:this.createConfidence(a?"high":"medium",a?"explicit-schema":"inferred-types")};this.addSchema(s,p)}}normalizePath(s){return"/"+s.replace(/\/+/g,"/").replace(/^\/+|\/+$/g,"")}}var U={appRouterExport:/export\s+(?:async\s+)?function\s+(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)/gi,pagesHandler:/export\s+default\s+(?:async\s+)?function/g,zodSchema:/z\.\w+\(/g};class V extends C{id="next-api";name="Next.js API Extractor";frameworks=["next-api"];priority=15;async doExtract(s){let{project:d,fs:t}=s,o=await t.glob("**/app/api/**/route.ts",{cwd:d.rootPath}),i=await t.glob("**/pages/api/**/*.ts",{cwd:d.rootPath}),n=[...o,...i];s.ir.stats.filesScanned=n.length;for(let u of n){let E=`${d.rootPath}/${u}`,a=await t.readFile(E);if(u.includes("/app/api/"))await this.extractAppRoutes(s,u,a);else await this.extractPagesRoutes(s,u,a)}}async extractAppRoutes(s,d,t){let o=d.match(/app\/api\/(.+)\/route\.ts$/),i=o?`/api/${o[1]}`:"/api",n=[...t.matchAll(U.appRouterExport)];for(let u of n){let E=u[1]?.toUpperCase()??"GET",a=u.index??0,p=t.slice(0,a).split(`
13
- `).length,c=U.zodSchema.test(t),w={id:this.generateEndpointId(E,i),method:E,path:i.replace(/\[(\w+)\]/g,":$1"),kind:this.methodToOpKind(E),handlerName:E,source:this.createLocation(d,p,p+10),confidence:this.createConfidence(c?"high":"medium",c?"explicit-schema":"inferred-types"),frameworkMeta:{routeType:"app-router"}};this.addEndpoint(s,w)}}async extractPagesRoutes(s,d,t){let o=d.match(/pages\/api\/(.+)\.ts$/),i=o?`/api/${o[1]}`:"/api";if(!U.pagesHandler.test(t))return;let n=1,u=U.zodSchema.test(t),E=["GET","POST"];for(let a of E){let p={id:this.generateEndpointId(a,i),method:a,path:i.replace(/\[(\w+)\]/g,":$1"),kind:this.methodToOpKind(a),handlerName:"handler",source:this.createLocation(d,n,n+20),confidence:this.createConfidence("low","naming-convention"),frameworkMeta:{routeType:"pages-router"}};this.addEndpoint(s,p)}}}var B={procedure:/\.(query|mutation)\s*\(\s*(?:\{[^}]*\}|[^)]+)\)/gi,procedureName:/(\w+)\s*:\s*(?:publicProcedure|protectedProcedure|procedure)/g,zodInput:/\.input\s*\(\s*(\w+)/g,zodOutput:/\.output\s*\(\s*(\w+)/g};class S extends C{id="trpc";name="tRPC Extractor";frameworks=["trpc"];priority=15;async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((u)=>`${u}/**/*.ts`).join(","):"**/*.ts",n=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=n.length;for(let u of n){if(u.includes("node_modules")||u.includes(".test."))continue;let E=`${d.rootPath}/${u}`,a=await o.readFile(E);if(!a.includes("trpc")&&!a.includes("Procedure"))continue;await this.extractProcedures(s,u,a)}}async extractProcedures(s,d,t){let o=[...t.matchAll(B.procedureName)];for(let i of o){let n=i[1]??"unknownProcedure",u=i.index??0,E=t.slice(0,u).split(`
14
- `).length,a=t.slice(u,u+500),p=a.includes(".query("),c=a.includes(".mutation(");if(!p&&!c)continue;let w=B.zodInput.test(a),g=B.zodOutput.test(a),y=w||g,$=c?"POST":"GET",X={id:`trpc.${n}`,method:$,path:`/trpc/${n}`,kind:c?"command":"query",handlerName:n,source:this.createLocation(d,E,E+10),confidence:this.createConfidence(y?"high":"medium",y?"explicit-schema":"inferred-types"),frameworkMeta:{procedureType:c?"mutation":"query",hasInput:w,hasOutput:g}};this.addEndpoint(s,X)}}}var cs={zodSchema:/(?:export\s+)?const\s+(\w+)\s*=\s*z\.(?:object|string|number|boolean|array|enum|union|intersection|literal|tuple|record)/g,zodInfer:/type\s+(\w+)\s*=\s*z\.infer<typeof\s+(\w+)>/g};class Z extends C{id="zod";name="Zod Schema Extractor";frameworks=["zod"];priority=5;async detect(){return!0}async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((u)=>`${u}/**/*.ts`).join(","):"**/*.ts",n=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=n.length;for(let u of n){if(u.includes("node_modules")||u.includes(".test."))continue;let E=`${d.rootPath}/${u}`,a=await o.readFile(E);if(!a.includes("z."))continue;await this.extractSchemas(s,u,a)}}async extractSchemas(s,d,t){let o=[...t.matchAll(cs.zodSchema)];for(let i of o){let n=i[1]??"unknownSchema",u=i.index??0,E=t.slice(0,u).split(`
15
- `).length,a=0,p=u;for(let y=u;y<t.length;y++){let $=t[y];if($==="("||$==="{"||$==="[")a++;if($===")"||$==="}"||$==="]")a--;if(a===0&&($===";"||$===`
16
- `)){p=y;break}}let c=t.slice(u,p+1),w=E+c.split(`
17
- `).length-1,g={id:this.generateSchemaId(n,d),name:n,schemaType:"zod",rawDefinition:c,source:this.createLocation(d,E,w),confidence:this.createConfidence("high","explicit-schema")};this.addSchema(s,g)}}}function ps(){v.register(new q),v.register(new G),v.register(new O),v.register(new f),v.register(new h),v.register(new S),v.register(new V),v.register(new Z)}var M=[{id:"nestjs",name:"NestJS",packages:["@nestjs/core","@nestjs/common"],importPatterns:[/@nestjs\//]},{id:"express",name:"Express",packages:["express"],importPatterns:[/from ['"]express['"]/]},{id:"fastify",name:"Fastify",packages:["fastify"],importPatterns:[/from ['"]fastify['"]/]},{id:"hono",name:"Hono",packages:["hono"],importPatterns:[/from ['"]hono['"]/]},{id:"elysia",name:"Elysia",packages:["elysia"],importPatterns:[/from ['"]elysia['"]/]},{id:"trpc",name:"tRPC",packages:["@trpc/server"],importPatterns:[/@trpc\/server/]},{id:"next-api",name:"Next.js API",packages:["next"],filePatterns:[/app\/api\/.*\/route\.ts$/,/pages\/api\/.*\.ts$/]},{id:"koa",name:"Koa",packages:["koa","@koa/router"],importPatterns:[/from ['"]koa['"]/]},{id:"hapi",name:"Hapi",packages:["@hapi/hapi"],importPatterns:[/@hapi\/hapi/]}];function Cs(s){let d={...s.dependencies,...s.devDependencies,...s.peerDependencies},t=[];for(let o of M)for(let i of o.packages)if(i in d){t.push({id:o.id,name:o.name,version:d[i],confidence:"high"});break}return t}function Et(s){let d=[],t=new Set;for(let o of M){if(!o.importPatterns)continue;for(let i of o.importPatterns)if(i.test(s)&&!t.has(o.id)){d.push({id:o.id,name:o.name,confidence:"medium"}),t.add(o.id);break}}return d}function ct(s){let d=[],t=new Set;for(let o of M){if(!o.filePatterns)continue;for(let i of o.filePatterns)for(let n of s)if(i.test(n)&&!t.has(o.id)){d.push({id:o.id,name:o.name,confidence:"medium"}),t.add(o.id);break}}return d}function pt(...s){let d=new Map,t={high:3,medium:2,low:1,ambiguous:0};for(let o of s)for(let i of o){let n=d.get(i.id);if(!n||t[i.confidence]>t[n.confidence])d.set(i.id,i)}return Array.from(d.values())}async function Ct(s,d){let t={rootPath:s,frameworks:[]};if(d?.readFile)try{let o=`${s}/package.json`,i=await d.readFile(o),n=JSON.parse(i);t.packageJsonPath=o,t.frameworks=Cs(n)}catch{}if(d?.readFile)try{let o=`${s}/tsconfig.json`;await d.readFile(o),t.tsConfigPath=o}catch{}return t}function wt(){return M.map((s)=>s.id)}function yt(s){return M.some((d)=>d.id===s)}export{Os as registerBuiltInExtractors,Ss as mergeIRs,pt as mergeFrameworkDetections,yt as isFrameworkSupported,wt as getSupportedFrameworks,j as extractors,v as extractorRegistry,Vs as extractFromProject,ct as detectFrameworksFromPaths,Cs as detectFrameworksFromPackageJson,Et as detectFrameworksFromCode,Ct as detectFramework,m as createEmptyIR,A as codegen,J as ExtractorRegistry};
6
+ `)}function Es(s,d){let t;switch(s.toLowerCase()){case"string":t="z.string()";break;case"number":t="z.number()";break;case"boolean":t="z.boolean()";break;case"date":t="z.date()";break;case"string[]":case"array<string>":t="z.array(z.string())";break;case"number[]":case"array<number>":t="z.array(z.number())";break;default:t="z.unknown()"}return d?`${t}.optional()`:t}var j={};L(j,{registerAllExtractors:()=>Cs,ZodSchemaExtractor:()=>Z,TrpcExtractor:()=>S,NextApiExtractor:()=>f,NestJsExtractor:()=>V,HonoExtractor:()=>q,FastifyExtractor:()=>O,ExpressExtractor:()=>G,ElysiaExtractor:()=>h,BaseExtractor:()=>c});class J{extractors=new Map;register(s){this.extractors.set(s.id,s)}unregister(s){return this.extractors.delete(s)}get(s){return this.extractors.get(s)}getAll(){return Array.from(this.extractors.values())}async findMatching(s){let d=[];for(let t of this.extractors.values())try{if(await t.detect(s))d.push(t)}catch{}return d.sort((t,o)=>o.priority-t.priority)}findByFramework(s){let d=[];for(let t of this.extractors.values())if(t.frameworks.includes(s))d.push(t);return d.sort((t,o)=>o.priority-t.priority)}findForFramework(s){return this.findByFramework(s)}hasExtractorFor(s){for(let d of this.extractors.values())if(d.frameworks.includes(s)||d.id===s)return!0;return!1}getSupportedFrameworks(){let s=new Set;for(let d of this.extractors.values()){s.add(d.id);for(let t of d.frameworks)s.add(t)}return Array.from(s)}}var v=new J;function qs(){}async function Ss(s,d={}){let t;if(d.framework){if(t=v.findByFramework(d.framework),t.length===0)return{success:!1,errors:[{code:"EXTRACTOR_NOT_FOUND",message:`No extractor found for framework: ${d.framework}`,recoverable:!1}]}}else if(t=await v.findMatching(s),t.length===0)return{success:!1,errors:[{code:"NO_FRAMEWORK_DETECTED",message:"No supported framework detected in project",recoverable:!1}]};let o=t[0];if(!o)return{success:!1,errors:[{code:"NO_EXTRACTOR",message:"No extractor available",recoverable:!1}]};return await o.extract(s,d)}function Zs(s){if(s.length===0)throw Error("Cannot merge empty IR array");if(s.length===1){if(!s[0])throw Error("First IR is undefined");return s[0]}let d=s[0];if(!d)throw Error("First IR is undefined");let t={version:"1.0",extractedAt:new Date().toISOString(),project:d.project,endpoints:[],schemas:[],errors:[],events:[],ambiguities:[],stats:{filesScanned:0,endpointsFound:0,schemasFound:0,errorsFound:0,eventsFound:0,ambiguitiesFound:0,highConfidence:0,mediumConfidence:0,lowConfidence:0}};for(let o of s)t.endpoints.push(...o.endpoints),t.schemas.push(...o.schemas),t.errors.push(...o.errors),t.events.push(...o.events),t.ambiguities.push(...o.ambiguities),t.stats.filesScanned+=o.stats.filesScanned,t.stats.endpointsFound+=o.stats.endpointsFound,t.stats.schemasFound+=o.stats.schemasFound,t.stats.errorsFound+=o.stats.errorsFound,t.stats.eventsFound+=o.stats.eventsFound,t.stats.ambiguitiesFound+=o.stats.ambiguitiesFound,t.stats.highConfidence+=o.stats.highConfidence,t.stats.mediumConfidence+=o.stats.mediumConfidence,t.stats.lowConfidence+=o.stats.lowConfidence;return t}function m(s){return{version:"1.0",extractedAt:new Date().toISOString(),project:s,endpoints:[],schemas:[],errors:[],events:[],ambiguities:[],stats:{filesScanned:0,endpointsFound:0,schemasFound:0,errorsFound:0,eventsFound:0,ambiguitiesFound:0,highConfidence:0,mediumConfidence:0,lowConfidence:0}}}class c{priority=10;fs;setFs(s){this.fs=s}async detect(s){return s.frameworks.some((d)=>this.frameworks.includes(d.id))}async extract(s,d){if(!this.fs)return{success:!1,errors:[{code:"NO_FS_ADAPTER",message:"File system adapter not configured",recoverable:!1}]};let t=m(s),o={project:s,options:d,fs:this.fs,ir:t};try{return await this.doExtract(o),this.calculateStats(t),{success:!0,ir:t}}catch(i){return{success:!1,errors:[{code:"EXTRACTION_ERROR",message:i instanceof Error?i.message:String(i),recoverable:!1}]}}}calculateStats(s){s.stats.endpointsFound=s.endpoints.length,s.stats.schemasFound=s.schemas.length,s.stats.errorsFound=s.errors.length,s.stats.eventsFound=s.events.length,s.stats.ambiguitiesFound=s.ambiguities.length;let d=[...s.endpoints,...s.schemas,...s.errors,...s.events];for(let t of d)switch(t.confidence.level){case"high":s.stats.highConfidence++;break;case"medium":s.stats.mediumConfidence++;break;case"low":case"ambiguous":s.stats.lowConfidence++;break}}generateEndpointId(s,d,t){let o=d.replace(/^\//,"").replace(/\//g,".").replace(/:/g,"").replace(/\{/g,"").replace(/\}/g,""),i=`${s.toLowerCase()}.${o}`;return t?`${i}.${t}`:i}generateSchemaId(s,d){return`${d.replace(/\.ts$/,"").replace(/\//g,".").replace(/^\.+/,"")}.${s}`}methodToOpKind(s){switch(s){case"GET":case"HEAD":case"OPTIONS":return"query";default:return"command"}}createLocation(s,d,t){return{file:s,startLine:d,endLine:t}}createConfidence(s,...d){return{level:s,reasons:d}}addEndpoint(s,d){s.ir.endpoints.push(d)}addSchema(s,d){s.ir.schemas.push(d)}}var b={route:/\.(get|post|put|patch|delete)\s*\(\s*['"`]([^'"`]+)['"`]/gi,tSchema:/body:\s*t\.\w+/g};class h extends c{id="elysia";name="Elysia Extractor";frameworks=["elysia"];priority=15;async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((n)=>`${n}/**/*.ts`).join(","):"**/*.ts",u=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=u.length;for(let n of u){if(n.includes("node_modules")||n.includes(".test."))continue;let E=`${d.rootPath}/${n}`,a=await o.readFile(E);if(!a.includes("elysia"))continue;await this.extractRoutes(s,n,a)}}async extractRoutes(s,d,t){let o=[...t.matchAll(b.route)];for(let i of o){let u=i[1]?.toUpperCase()??"GET",n=i[2]??"/",E=i.index??0,a=t.slice(0,E).split(`
7
+ `).length,C=t.slice(E,E+500),p=b.tSchema.test(C),w={id:this.generateEndpointId(u,n),method:u,path:n,kind:this.methodToOpKind(u),handlerName:"handler",source:this.createLocation(d,a,a+5),confidence:this.createConfidence(p?"high":"medium",p?"explicit-schema":"decorator-hints")};this.addEndpoint(s,w)}}}var I={route:/(?:app|router)\.(get|post|put|patch|delete|head|options)\s*\(\s*['"`]([^'"`]+)['"`]/gi,routerUse:/(?:app|router)\.use\s*\(\s*['"`]([^'"`]+)['"`]/gi,zodValidate:/validate\s*\(\s*(\w+)\s*\)/g};class G extends c{id="express";name="Express Extractor";frameworks=["express"];priority=15;async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((n)=>`${n}/**/*.ts`).join(","):"**/*.ts",u=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=u.length;for(let n of u){if(n.includes("node_modules")||n.includes(".test."))continue;let E=`${d.rootPath}/${n}`,a=await o.readFile(E);await this.extractRoutes(s,n,a)}}async extractRoutes(s,d,t){let o=[...t.matchAll(I.route)];for(let i of o){let u=i[1]?.toUpperCase()??"GET",n=i[2]??"/",E=i.index??0,a=t.slice(0,E).split(`
8
+ `).length,C=t.slice(E,E+500),p=C.match(/(?:async\s+)?(?:function\s+)?(\w+)|,\s*(\w+)\s*\)/),w=p?.[1]??p?.[2]??"handler",g=I.zodValidate.test(C),y={id:this.generateEndpointId(u,n,w),method:u,path:n,kind:this.methodToOpKind(u),handlerName:w,source:this.createLocation(d,a,a+5),confidence:this.createConfidence(g?"high":"medium",g?"explicit-schema":"decorator-hints")};this.addEndpoint(s,y)}}}var k={route:/(?:fastify|app|server)\.(get|post|put|patch|delete|head|options)\s*\(\s*['"`]([^'"`]+)['"`]/gi,schemaOption:/schema\s*:\s*\{/g,bodySchema:/body\s*:\s*(\w+)/g,responseSchema:/response\s*:\s*\{/g};class O extends c{id="fastify";name="Fastify Extractor";frameworks=["fastify"];priority=15;async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((n)=>`${n}/**/*.ts`).join(","):"**/*.ts",u=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=u.length;for(let n of u){if(n.includes("node_modules")||n.includes(".test."))continue;let E=`${d.rootPath}/${n}`,a=await o.readFile(E);await this.extractRoutes(s,n,a)}}async extractRoutes(s,d,t){let o=[...t.matchAll(k.route)];for(let i of o){let u=i[1]?.toUpperCase()??"GET",n=i[2]??"/",E=i.index??0,a=t.slice(0,E).split(`
9
+ `).length,C=t.slice(E,E+1000),p=k.schemaOption.test(C),w={id:this.generateEndpointId(u,n),method:u,path:n,kind:this.methodToOpKind(u),handlerName:"handler",source:this.createLocation(d,a,a+10),confidence:this.createConfidence(p?"high":"medium",p?"explicit-schema":"decorator-hints"),frameworkMeta:{hasSchema:p}};this.addEndpoint(s,w)}}}var T={route:/(?:app|hono)\.(get|post|put|patch|delete|all)\s*\(\s*['"`]([^'"`]+)['"`]/gi,zodValidator:/zValidator\s*\(\s*['"`](\w+)['"`]\s*,\s*(\w+)/g};class q extends c{id="hono";name="Hono Extractor";frameworks=["hono"];priority=15;async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((n)=>`${n}/**/*.ts`).join(","):"**/*.ts",u=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=u.length;for(let n of u){if(n.includes("node_modules")||n.includes(".test."))continue;let E=`${d.rootPath}/${n}`,a=await o.readFile(E);await this.extractRoutes(s,n,a)}}async extractRoutes(s,d,t){let o=[...t.matchAll(T.route)];for(let i of o){let u=i[1]?.toUpperCase()??"GET",n=i[2]??"/",E=i.index??0,a=t.slice(0,E).split(`
10
+ `).length,C=t.slice(E,E+500),p=T.zodValidator.test(C),w={id:this.generateEndpointId(u,n),method:u,path:n,kind:this.methodToOpKind(u),handlerName:"handler",source:this.createLocation(d,a,a+5),confidence:this.createConfidence(p?"high":"medium",p?"explicit-schema":"decorator-hints")};this.addEndpoint(s,w)}}}var H={controller:/@Controller\s*\(\s*['"`]([^'"`]*)['"`]\s*\)/g,route:/@(Get|Post|Put|Patch|Delete|Head|Options)\s*\(\s*(?:['"`]([^'"`]*)['"`])?\s*\)/g,body:/@Body\s*\(\s*\)/g,param:/@Param\s*\(\s*(?:['"`]([^'"`]*)['"`])?\s*\)/g,query:/@Query\s*\(\s*(?:['"`]([^'"`]*)['"`])?\s*\)/g,dto:/class\s+(\w+(?:Dto|DTO|Request|Response|Input|Output))\s*\{/g,classValidator:/@(IsString|IsNumber|IsBoolean|IsArray|IsOptional|IsNotEmpty|Min|Max|Length|Matches)/g};class V extends c{id="nestjs";name="NestJS Extractor";frameworks=["nestjs"];priority=20;async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((n)=>`${n}/**/*.ts`).join(","):"**/*.ts",u=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=u.length;for(let n of u){if(n.includes("node_modules")||n.includes(".spec.")||n.includes(".test."))continue;let E=`${d.rootPath}/${n}`,a=await o.readFile(E);await this.extractControllers(s,n,a),await this.extractDtos(s,n,a)}}async extractControllers(s,d,t){let o=[...t.matchAll(H.controller)];for(let i of o){let u=i[1]||"",n=i.index??0,C=t.slice(n).match(/class\s+(\w+)/)?.[1]??"UnknownController",p=t.indexOf("@Controller",n+1),w=p>0?t.slice(n,p):t.slice(n),g=[...w.matchAll(H.route)];for(let y of g){let $=y[1]?.toUpperCase(),X=y[2]||"",K=this.normalizePath(`/${u}/${X}`),Q=w.slice(y.index??0),z=Q.match(/(?:async\s+)?(\w+)\s*\([^)]*\)\s*(?::\s*\w+(?:<[^>]+>)?)?\s*\{/)?.[1]??"unknownHandler",N=n+(y.index??0),D=t.slice(0,N).split(`
11
+ `).length,l=H.body.test(Q.slice(0,200)),R=H.param.test(Q.slice(0,200)),x=H.query.test(Q.slice(0,200)),r={id:this.generateEndpointId($,K,z),method:$,path:K,kind:this.methodToOpKind($),handlerName:z,controllerName:C,source:this.createLocation(d,D,D+10),confidence:this.createConfidence("medium","decorator-hints"),frameworkMeta:{hasBody:l,hasParams:R,hasQuery:x}};this.addEndpoint(s,r)}}}async extractDtos(s,d,t){let o=[...t.matchAll(H.dto)];for(let i of o){let u=i[1]??"UnknownDto",n=i.index??0,E=t.slice(0,n).split(`
12
+ `).length,a=t.includes("class-validator")||t.includes("@IsString")||t.includes("@IsNumber"),C={id:this.generateSchemaId(u,d),name:u,schemaType:a?"class-validator":"typescript",source:this.createLocation(d,E,E+20),confidence:this.createConfidence(a?"high":"medium",a?"explicit-schema":"inferred-types")};this.addSchema(s,C)}}normalizePath(s){return"/"+s.replace(/\/+/g,"/").replace(/^\/+|\/+$/g,"")}}var U={appRouterExport:/export\s+(?:async\s+)?function\s+(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)/gi,pagesHandler:/export\s+default\s+(?:async\s+)?function/g,zodSchema:/z\.\w+\(/g};class f extends c{id="next-api";name="Next.js API Extractor";frameworks=["next-api"];priority=15;async doExtract(s){let{project:d,fs:t}=s,o=await t.glob("**/app/api/**/route.ts",{cwd:d.rootPath}),i=await t.glob("**/pages/api/**/*.ts",{cwd:d.rootPath}),u=[...o,...i];s.ir.stats.filesScanned=u.length;for(let n of u){let E=`${d.rootPath}/${n}`,a=await t.readFile(E);if(n.includes("/app/api/"))await this.extractAppRoutes(s,n,a);else await this.extractPagesRoutes(s,n,a)}}async extractAppRoutes(s,d,t){let o=d.match(/app\/api\/(.+)\/route\.ts$/),i=o?`/api/${o[1]}`:"/api",u=[...t.matchAll(U.appRouterExport)];for(let n of u){let E=n[1]?.toUpperCase()??"GET",a=n.index??0,C=t.slice(0,a).split(`
13
+ `).length,p=U.zodSchema.test(t),w={id:this.generateEndpointId(E,i),method:E,path:i.replace(/\[(\w+)\]/g,":$1"),kind:this.methodToOpKind(E),handlerName:E,source:this.createLocation(d,C,C+10),confidence:this.createConfidence(p?"high":"medium",p?"explicit-schema":"inferred-types"),frameworkMeta:{routeType:"app-router"}};this.addEndpoint(s,w)}}async extractPagesRoutes(s,d,t){let o=d.match(/pages\/api\/(.+)\.ts$/),i=o?`/api/${o[1]}`:"/api";if(!U.pagesHandler.test(t))return;let u=1,n=U.zodSchema.test(t),E=["GET","POST"];for(let a of E){let C={id:this.generateEndpointId(a,i),method:a,path:i.replace(/\[(\w+)\]/g,":$1"),kind:this.methodToOpKind(a),handlerName:"handler",source:this.createLocation(d,u,u+20),confidence:this.createConfidence("low","naming-convention"),frameworkMeta:{routeType:"pages-router"}};this.addEndpoint(s,C)}}}var B={procedure:/\.(query|mutation)\s*\(\s*(?:\{[^}]*\}|[^)]+)\)/gi,procedureName:/(\w+)\s*:\s*(?:publicProcedure|protectedProcedure|procedure)/g,zodInput:/\.input\s*\(\s*(\w+)/g,zodOutput:/\.output\s*\(\s*(\w+)/g};class S extends c{id="trpc";name="tRPC Extractor";frameworks=["trpc"];priority=15;async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((n)=>`${n}/**/*.ts`).join(","):"**/*.ts",u=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=u.length;for(let n of u){if(n.includes("node_modules")||n.includes(".test."))continue;let E=`${d.rootPath}/${n}`,a=await o.readFile(E);if(!a.includes("trpc")&&!a.includes("Procedure"))continue;await this.extractProcedures(s,n,a)}}async extractProcedures(s,d,t){let o=[...t.matchAll(B.procedureName)];for(let i of o){let u=i[1]??"unknownProcedure",n=i.index??0,E=t.slice(0,n).split(`
14
+ `).length,a=t.slice(n,n+500),C=a.includes(".query("),p=a.includes(".mutation(");if(!C&&!p)continue;let w=B.zodInput.test(a),g=B.zodOutput.test(a),y=w||g,$=p?"POST":"GET",X={id:`trpc.${u}`,method:$,path:`/trpc/${u}`,kind:p?"command":"query",handlerName:u,source:this.createLocation(d,E,E+10),confidence:this.createConfidence(y?"high":"medium",y?"explicit-schema":"inferred-types"),frameworkMeta:{procedureType:p?"mutation":"query",hasInput:w,hasOutput:g}};this.addEndpoint(s,X)}}}var ps={zodSchema:/(?:export\s+)?const\s+(\w+)\s*=\s*z\.(?:object|string|number|boolean|array|enum|union|intersection|literal|tuple|record)/g,zodInfer:/type\s+(\w+)\s*=\s*z\.infer<typeof\s+(\w+)>/g};class Z extends c{id="zod";name="Zod Schema Extractor";frameworks=["zod"];priority=5;async detect(){return!0}async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((n)=>`${n}/**/*.ts`).join(","):"**/*.ts",u=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=u.length;for(let n of u){if(n.includes("node_modules")||n.includes(".test."))continue;let E=`${d.rootPath}/${n}`,a=await o.readFile(E);if(!a.includes("z."))continue;await this.extractSchemas(s,n,a)}}async extractSchemas(s,d,t){let o=[...t.matchAll(ps.zodSchema)];for(let i of o){let u=i[1]??"unknownSchema",n=i.index??0,E=t.slice(0,n).split(`
15
+ `).length,a=0,C=n;for(let y=n;y<t.length;y++){let $=t[y];if($==="("||$==="{"||$==="[")a++;if($===")"||$==="}"||$==="]")a--;if(a===0&&($===";"||$===`
16
+ `)){C=y;break}}let p=t.slice(n,C+1),w=E+p.split(`
17
+ `).length-1,g={id:this.generateSchemaId(u,d),name:u,schemaType:"zod",rawDefinition:p,source:this.createLocation(d,E,w),confidence:this.createConfidence("high","explicit-schema")};this.addSchema(s,g)}}}function Cs(){v.register(new V),v.register(new G),v.register(new O),v.register(new q),v.register(new h),v.register(new S),v.register(new f),v.register(new Z)}var M=[{id:"nestjs",name:"NestJS",packages:["@nestjs/core","@nestjs/common"],importPatterns:[/@nestjs\//]},{id:"express",name:"Express",packages:["express"],importPatterns:[/from ['"]express['"]/]},{id:"fastify",name:"Fastify",packages:["fastify"],importPatterns:[/from ['"]fastify['"]/]},{id:"hono",name:"Hono",packages:["hono"],importPatterns:[/from ['"]hono['"]/]},{id:"elysia",name:"Elysia",packages:["elysia"],importPatterns:[/from ['"]elysia['"]/]},{id:"trpc",name:"tRPC",packages:["@trpc/server"],importPatterns:[/@trpc\/server/]},{id:"next-api",name:"Next.js API",packages:["next"],filePatterns:[/app\/api\/.*\/route\.ts$/,/pages\/api\/.*\.ts$/]},{id:"koa",name:"Koa",packages:["koa","@koa/router"],importPatterns:[/from ['"]koa['"]/]},{id:"hapi",name:"Hapi",packages:["@hapi/hapi"],importPatterns:[/@hapi\/hapi/]}];function cs(s){let d={...s.dependencies,...s.devDependencies,...s.peerDependencies},t=[];for(let o of M)for(let i of o.packages)if(i in d){t.push({id:o.id,name:o.name,version:d[i],confidence:"high"});break}return t}function pt(s){let d=[],t=new Set;for(let o of M){if(!o.importPatterns)continue;for(let i of o.importPatterns)if(i.test(s)&&!t.has(o.id)){d.push({id:o.id,name:o.name,confidence:"medium"}),t.add(o.id);break}}return d}function Ct(s){let d=[],t=new Set;for(let o of M){if(!o.filePatterns)continue;for(let i of o.filePatterns)for(let u of s)if(i.test(u)&&!t.has(o.id)){d.push({id:o.id,name:o.name,confidence:"medium"}),t.add(o.id);break}}return d}function ct(...s){let d=new Map,t={high:3,medium:2,low:1,ambiguous:0};for(let o of s)for(let i of o){let u=d.get(i.id);if(!u||t[i.confidence]>t[u.confidence])d.set(i.id,i)}return Array.from(d.values())}async function wt(s,d){let t={rootPath:s,frameworks:[]};if(d?.readFile)try{let o=`${s}/package.json`,i=await d.readFile(o),u=JSON.parse(i);t.packageJsonPath=o,t.frameworks=cs(u)}catch{}if(d?.readFile)try{let o=`${s}/tsconfig.json`;await d.readFile(o),t.tsConfigPath=o}catch{}return t}function yt(){return M.map((s)=>s.id)}function $t(s){return M.some((d)=>d.id===s)}import{defineFeature as ws}from"@contractspec/lib.contracts-spec/features";var Ht=ws({meta:{key:"libs.source-extractors",version:"1.0.0",title:"Source Extractors",description:"Extract contract candidates from TypeScript source code across multiple frameworks (NestJS, Express, Fastify, Hono, Elysia, tRPC, Next.js)",domain:"source-extractors",owners:["@contractspec-core"],tags:["package","libs","source-extractors"],stability:"experimental"}});export{qs as registerBuiltInExtractors,Zs as mergeIRs,ct as mergeFrameworkDetections,$t as isFrameworkSupported,yt as getSupportedFrameworks,j as extractors,v as extractorRegistry,Ss as extractFromProject,Ct as detectFrameworksFromPaths,cs as detectFrameworksFromPackageJson,pt as detectFrameworksFromCode,wt as detectFramework,m as createEmptyIR,A as codegen,Ht as SourceExtractorsFeature,J as ExtractorRegistry};
@@ -1,16 +1,16 @@
1
- var e=Object.defineProperty;var ss=(s)=>s;function ts(s,d){this[s]=ss.bind(null,d)}var F=(s,d)=>{for(var t in d)e(s,t,{get:d[t],enumerable:!0,configurable:!0,set:ts.bind(d,t)})};var A={};F(A,{generateSchemas:()=>P,generateSchema:()=>_,generateRegistry:()=>W,generateOperations:()=>L,generateOperation:()=>Y});function Y(s,d){let t=ds(s),o=`${os(s)}.ts`,i=is(s,t,d);return{path:o,content:i,type:"operation"}}function L(s,d){return s.endpoints.map((t)=>Y(t,d))}function ds(s){let d=s.path.replace(/^\//,"").split("/").filter((i)=>i&&!i.startsWith(":")&&!i.startsWith("{")),t=s.method.toLowerCase(),o=d.map((i)=>i.charAt(0).toUpperCase()+i.slice(1)).join("");return`${t}${o}Spec`}function os(s){let d=s.path.replace(/^\//,"").split("/").filter((i)=>i&&!i.startsWith(":")&&!i.startsWith("{")),t=s.method.toLowerCase(),o=d.join("-");return`${t}-${o}`.replace(/--+/g,"-")}function is(s,d,t){let i=s.kind==="command"?"defineCommand":"defineQuery",n=t.defaultAuth??"user",u=t.defaultOwners??["team"];return["/**",` * ${s.method} ${s.path}`," *",` * Generated from: ${s.source.file}:${s.source.startLine}`,` * Confidence: ${s.confidence.level}`," */","",`import { ${i} } from '@contractspec/lib.contracts-spec';`,"import { fromZod } from '@contractspec/lib.schema';","import { z } from 'zod';","","// TODO: Define input schema based on extracted information","const inputSchema = fromZod(z.object({"," // Add fields here","}));","","// TODO: Define output schema","const outputSchema = fromZod(z.object({"," // Add fields here","}));","",`export const ${d} = ${i}({`," meta: {",` name: '${s.handlerName??s.id}',`," version: 1,"," stability: 'experimental',",` owners: ${JSON.stringify(u)},`," goal: 'TODO: Describe the business goal',",` context: 'Generated from ${s.source.file}',`," },"," io: {"," input: inputSchema,"," output: outputSchema,"," },"," policy: {",` auth: '${n}',`," },"," transport: {"," rest: {",` method: '${s.method}',`,` path: '${s.path}',`," },"," },","});",""].join(`
2
- `)}function W(s){let d=s.filter((o)=>o.type==="operation").map((o)=>{let i=o.path.replace(".ts","").replace(/-/g,"_"),n=us(i)+"Spec";return{path:o.path,name:i,specName:n}}),t=["/**"," * Generated operation registry."," */","","import { OperationSpecRegistry } from '@contractspec/lib.contracts-spec';",""];for(let o of d){let i=`./${o.path.replace(".ts","")}`;t.push(`import { ${o.specName} } from '${i}';`)}t.push(""),t.push("export const operationRegistry = new OperationSpecRegistry();"),t.push("");for(let o of d)t.push(`operationRegistry.register(${o.specName});`);return t.push(""),{path:"registry.ts",content:t.join(`
3
- `),type:"registry"}}function us(s){return s.split(/[-_]/).map((d)=>d.charAt(0).toUpperCase()+d.slice(1)).join("")}function _(s,d){let t=`${ns(s.name)}.ts`,o=as(s);return{path:`schemas/${t}`,content:o,type:"schema"}}function P(s,d){return s.schemas.map((t)=>_(t,d))}function ns(s){return s.replace(/([a-z])([A-Z])/g,"$1-$2").replace(/([A-Z])([A-Z][a-z])/g,"$1-$2").toLowerCase()}function as(s){let d=["/**",` * ${s.name}`," *",` * Generated from: ${s.source.file}:${s.source.startLine}`,` * Schema type: ${s.schemaType}`,` * Confidence: ${s.confidence.level}`," */","","import { fromZod } from '@contractspec/lib.schema';","import { z } from 'zod';",""];if(s.rawDefinition&&s.schemaType==="zod")d.push("// Original definition from source:"),d.push(`// ${s.rawDefinition.split(`
1
+ var e=Object.defineProperty;var ss=(s)=>s;function ts(s,d){this[s]=ss.bind(null,d)}var L=(s,d)=>{for(var t in d)e(s,t,{get:d[t],enumerable:!0,configurable:!0,set:ts.bind(d,t)})};var A={};L(A,{generateSchemas:()=>P,generateSchema:()=>_,generateRegistry:()=>F,generateOperations:()=>W,generateOperation:()=>Y});function Y(s,d){let t=ds(s),o=`${os(s)}.ts`,i=is(s,t,d);return{path:o,content:i,type:"operation"}}function W(s,d){return s.endpoints.map((t)=>Y(t,d))}function ds(s){let d=s.path.replace(/^\//,"").split("/").filter((i)=>i&&!i.startsWith(":")&&!i.startsWith("{")),t=s.method.toLowerCase(),o=d.map((i)=>i.charAt(0).toUpperCase()+i.slice(1)).join("");return`${t}${o}Spec`}function os(s){let d=s.path.replace(/^\//,"").split("/").filter((i)=>i&&!i.startsWith(":")&&!i.startsWith("{")),t=s.method.toLowerCase(),o=d.join("-");return`${t}-${o}`.replace(/--+/g,"-")}function is(s,d,t){let i=s.kind==="command"?"defineCommand":"defineQuery",u=t.defaultAuth??"user",n=t.defaultOwners??["team"];return["/**",` * ${s.method} ${s.path}`," *",` * Generated from: ${s.source.file}:${s.source.startLine}`,` * Confidence: ${s.confidence.level}`," */","",`import { ${i} } from '@contractspec/lib.contracts-spec';`,"import { fromZod } from '@contractspec/lib.schema';","import { z } from 'zod';","","// TODO: Define input schema based on extracted information","const inputSchema = fromZod(z.object({"," // Add fields here","}));","","// TODO: Define output schema","const outputSchema = fromZod(z.object({"," // Add fields here","}));","",`export const ${d} = ${i}({`," meta: {",` name: '${s.handlerName??s.id}',`," version: 1,"," stability: 'experimental',",` owners: ${JSON.stringify(n)},`," goal: 'TODO: Describe the business goal',",` context: 'Generated from ${s.source.file}',`," },"," io: {"," input: inputSchema,"," output: outputSchema,"," },"," policy: {",` auth: '${u}',`," },"," transport: {"," rest: {",` method: '${s.method}',`,` path: '${s.path}',`," },"," },","});",""].join(`
2
+ `)}function F(s){let d=s.filter((o)=>o.type==="operation").map((o)=>{let i=o.path.replace(".ts","").replace(/-/g,"_"),u=ns(i)+"Spec";return{path:o.path,name:i,specName:u}}),t=["/**"," * Generated operation registry."," */","","import { OperationSpecRegistry } from '@contractspec/lib.contracts-spec';",""];for(let o of d){let i=`./${o.path.replace(".ts","")}`;t.push(`import { ${o.specName} } from '${i}';`)}t.push(""),t.push("export const operationRegistry = new OperationSpecRegistry();"),t.push("");for(let o of d)t.push(`operationRegistry.register(${o.specName});`);return t.push(""),{path:"registry.ts",content:t.join(`
3
+ `),type:"registry"}}function ns(s){return s.split(/[-_]/).map((d)=>d.charAt(0).toUpperCase()+d.slice(1)).join("")}function _(s,d){let t=`${us(s.name)}.ts`,o=as(s);return{path:`schemas/${t}`,content:o,type:"schema"}}function P(s,d){return s.schemas.map((t)=>_(t,d))}function us(s){return s.replace(/([a-z])([A-Z])/g,"$1-$2").replace(/([A-Z])([A-Z][a-z])/g,"$1-$2").toLowerCase()}function as(s){let d=["/**",` * ${s.name}`," *",` * Generated from: ${s.source.file}:${s.source.startLine}`,` * Schema type: ${s.schemaType}`,` * Confidence: ${s.confidence.level}`," */","","import { fromZod } from '@contractspec/lib.schema';","import { z } from 'zod';",""];if(s.rawDefinition&&s.schemaType==="zod")d.push("// Original definition from source:"),d.push(`// ${s.rawDefinition.split(`
4
4
  `)[0]}`),d.push("");if(d.push(`export const ${s.name}Schema = fromZod(z.object({`),s.fields&&s.fields.length>0)for(let t of s.fields){let o=Es(t.type,t.optional);d.push(` ${t.name}: ${o},`)}else d.push(" // TODO: Define schema fields");return d.push("}));"),d.push(""),d.push(`export type ${s.name} = z.infer<typeof ${s.name}Schema.zodSchema>;`),d.push(""),d.join(`
5
- `)}function Es(s,d){let t;switch(s.toLowerCase()){case"string":t="z.string()";break;case"number":t="z.number()";break;case"boolean":t="z.boolean()";break;case"date":t="z.date()";break;case"string[]":case"array<string>":t="z.array(z.string())";break;case"number[]":case"array<number>":t="z.array(z.number())";break;default:t="z.unknown()"}return d?`${t}.optional()`:t}var j={};F(j,{registerAllExtractors:()=>ps,ZodSchemaExtractor:()=>Z,TrpcExtractor:()=>S,NextApiExtractor:()=>V,NestJsExtractor:()=>q,HonoExtractor:()=>f,FastifyExtractor:()=>O,ExpressExtractor:()=>G,ElysiaExtractor:()=>h,BaseExtractor:()=>C});class J{extractors=new Map;register(s){this.extractors.set(s.id,s)}unregister(s){return this.extractors.delete(s)}get(s){return this.extractors.get(s)}getAll(){return Array.from(this.extractors.values())}async findMatching(s){let d=[];for(let t of this.extractors.values())try{if(await t.detect(s))d.push(t)}catch{}return d.sort((t,o)=>o.priority-t.priority)}findByFramework(s){let d=[];for(let t of this.extractors.values())if(t.frameworks.includes(s))d.push(t);return d.sort((t,o)=>o.priority-t.priority)}findForFramework(s){return this.findByFramework(s)}hasExtractorFor(s){for(let d of this.extractors.values())if(d.frameworks.includes(s)||d.id===s)return!0;return!1}getSupportedFrameworks(){let s=new Set;for(let d of this.extractors.values()){s.add(d.id);for(let t of d.frameworks)s.add(t)}return Array.from(s)}}var v=new J;function Os(){}async function Vs(s,d={}){let t;if(d.framework){if(t=v.findByFramework(d.framework),t.length===0)return{success:!1,errors:[{code:"EXTRACTOR_NOT_FOUND",message:`No extractor found for framework: ${d.framework}`,recoverable:!1}]}}else if(t=await v.findMatching(s),t.length===0)return{success:!1,errors:[{code:"NO_FRAMEWORK_DETECTED",message:"No supported framework detected in project",recoverable:!1}]};let o=t[0];if(!o)return{success:!1,errors:[{code:"NO_EXTRACTOR",message:"No extractor available",recoverable:!1}]};return await o.extract(s,d)}function Ss(s){if(s.length===0)throw Error("Cannot merge empty IR array");if(s.length===1){if(!s[0])throw Error("First IR is undefined");return s[0]}let d=s[0];if(!d)throw Error("First IR is undefined");let t={version:"1.0",extractedAt:new Date().toISOString(),project:d.project,endpoints:[],schemas:[],errors:[],events:[],ambiguities:[],stats:{filesScanned:0,endpointsFound:0,schemasFound:0,errorsFound:0,eventsFound:0,ambiguitiesFound:0,highConfidence:0,mediumConfidence:0,lowConfidence:0}};for(let o of s)t.endpoints.push(...o.endpoints),t.schemas.push(...o.schemas),t.errors.push(...o.errors),t.events.push(...o.events),t.ambiguities.push(...o.ambiguities),t.stats.filesScanned+=o.stats.filesScanned,t.stats.endpointsFound+=o.stats.endpointsFound,t.stats.schemasFound+=o.stats.schemasFound,t.stats.errorsFound+=o.stats.errorsFound,t.stats.eventsFound+=o.stats.eventsFound,t.stats.ambiguitiesFound+=o.stats.ambiguitiesFound,t.stats.highConfidence+=o.stats.highConfidence,t.stats.mediumConfidence+=o.stats.mediumConfidence,t.stats.lowConfidence+=o.stats.lowConfidence;return t}function m(s){return{version:"1.0",extractedAt:new Date().toISOString(),project:s,endpoints:[],schemas:[],errors:[],events:[],ambiguities:[],stats:{filesScanned:0,endpointsFound:0,schemasFound:0,errorsFound:0,eventsFound:0,ambiguitiesFound:0,highConfidence:0,mediumConfidence:0,lowConfidence:0}}}class C{priority=10;fs;setFs(s){this.fs=s}async detect(s){return s.frameworks.some((d)=>this.frameworks.includes(d.id))}async extract(s,d){if(!this.fs)return{success:!1,errors:[{code:"NO_FS_ADAPTER",message:"File system adapter not configured",recoverable:!1}]};let t=m(s),o={project:s,options:d,fs:this.fs,ir:t};try{return await this.doExtract(o),this.calculateStats(t),{success:!0,ir:t}}catch(i){return{success:!1,errors:[{code:"EXTRACTION_ERROR",message:i instanceof Error?i.message:String(i),recoverable:!1}]}}}calculateStats(s){s.stats.endpointsFound=s.endpoints.length,s.stats.schemasFound=s.schemas.length,s.stats.errorsFound=s.errors.length,s.stats.eventsFound=s.events.length,s.stats.ambiguitiesFound=s.ambiguities.length;let d=[...s.endpoints,...s.schemas,...s.errors,...s.events];for(let t of d)switch(t.confidence.level){case"high":s.stats.highConfidence++;break;case"medium":s.stats.mediumConfidence++;break;case"low":case"ambiguous":s.stats.lowConfidence++;break}}generateEndpointId(s,d,t){let o=d.replace(/^\//,"").replace(/\//g,".").replace(/:/g,"").replace(/\{/g,"").replace(/\}/g,""),i=`${s.toLowerCase()}.${o}`;return t?`${i}.${t}`:i}generateSchemaId(s,d){return`${d.replace(/\.ts$/,"").replace(/\//g,".").replace(/^\.+/,"")}.${s}`}methodToOpKind(s){switch(s){case"GET":case"HEAD":case"OPTIONS":return"query";default:return"command"}}createLocation(s,d,t){return{file:s,startLine:d,endLine:t}}createConfidence(s,...d){return{level:s,reasons:d}}addEndpoint(s,d){s.ir.endpoints.push(d)}addSchema(s,d){s.ir.schemas.push(d)}}var b={route:/\.(get|post|put|patch|delete)\s*\(\s*['"`]([^'"`]+)['"`]/gi,tSchema:/body:\s*t\.\w+/g};class h extends C{id="elysia";name="Elysia Extractor";frameworks=["elysia"];priority=15;async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((u)=>`${u}/**/*.ts`).join(","):"**/*.ts",n=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=n.length;for(let u of n){if(u.includes("node_modules")||u.includes(".test."))continue;let E=`${d.rootPath}/${u}`,a=await o.readFile(E);if(!a.includes("elysia"))continue;await this.extractRoutes(s,u,a)}}async extractRoutes(s,d,t){let o=[...t.matchAll(b.route)];for(let i of o){let n=i[1]?.toUpperCase()??"GET",u=i[2]??"/",E=i.index??0,a=t.slice(0,E).split(`
6
- `).length,p=t.slice(E,E+500),c=b.tSchema.test(p),w={id:this.generateEndpointId(n,u),method:n,path:u,kind:this.methodToOpKind(n),handlerName:"handler",source:this.createLocation(d,a,a+5),confidence:this.createConfidence(c?"high":"medium",c?"explicit-schema":"decorator-hints")};this.addEndpoint(s,w)}}}var I={route:/(?:app|router)\.(get|post|put|patch|delete|head|options)\s*\(\s*['"`]([^'"`]+)['"`]/gi,routerUse:/(?:app|router)\.use\s*\(\s*['"`]([^'"`]+)['"`]/gi,zodValidate:/validate\s*\(\s*(\w+)\s*\)/g};class G extends C{id="express";name="Express Extractor";frameworks=["express"];priority=15;async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((u)=>`${u}/**/*.ts`).join(","):"**/*.ts",n=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=n.length;for(let u of n){if(u.includes("node_modules")||u.includes(".test."))continue;let E=`${d.rootPath}/${u}`,a=await o.readFile(E);await this.extractRoutes(s,u,a)}}async extractRoutes(s,d,t){let o=[...t.matchAll(I.route)];for(let i of o){let n=i[1]?.toUpperCase()??"GET",u=i[2]??"/",E=i.index??0,a=t.slice(0,E).split(`
7
- `).length,p=t.slice(E,E+500),c=p.match(/(?:async\s+)?(?:function\s+)?(\w+)|,\s*(\w+)\s*\)/),w=c?.[1]??c?.[2]??"handler",g=I.zodValidate.test(p),y={id:this.generateEndpointId(n,u,w),method:n,path:u,kind:this.methodToOpKind(n),handlerName:w,source:this.createLocation(d,a,a+5),confidence:this.createConfidence(g?"high":"medium",g?"explicit-schema":"decorator-hints")};this.addEndpoint(s,y)}}}var k={route:/(?:fastify|app|server)\.(get|post|put|patch|delete|head|options)\s*\(\s*['"`]([^'"`]+)['"`]/gi,schemaOption:/schema\s*:\s*\{/g,bodySchema:/body\s*:\s*(\w+)/g,responseSchema:/response\s*:\s*\{/g};class O extends C{id="fastify";name="Fastify Extractor";frameworks=["fastify"];priority=15;async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((u)=>`${u}/**/*.ts`).join(","):"**/*.ts",n=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=n.length;for(let u of n){if(u.includes("node_modules")||u.includes(".test."))continue;let E=`${d.rootPath}/${u}`,a=await o.readFile(E);await this.extractRoutes(s,u,a)}}async extractRoutes(s,d,t){let o=[...t.matchAll(k.route)];for(let i of o){let n=i[1]?.toUpperCase()??"GET",u=i[2]??"/",E=i.index??0,a=t.slice(0,E).split(`
8
- `).length,p=t.slice(E,E+1000),c=k.schemaOption.test(p),w={id:this.generateEndpointId(n,u),method:n,path:u,kind:this.methodToOpKind(n),handlerName:"handler",source:this.createLocation(d,a,a+10),confidence:this.createConfidence(c?"high":"medium",c?"explicit-schema":"decorator-hints"),frameworkMeta:{hasSchema:c}};this.addEndpoint(s,w)}}}var T={route:/(?:app|hono)\.(get|post|put|patch|delete|all)\s*\(\s*['"`]([^'"`]+)['"`]/gi,zodValidator:/zValidator\s*\(\s*['"`](\w+)['"`]\s*,\s*(\w+)/g};class f extends C{id="hono";name="Hono Extractor";frameworks=["hono"];priority=15;async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((u)=>`${u}/**/*.ts`).join(","):"**/*.ts",n=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=n.length;for(let u of n){if(u.includes("node_modules")||u.includes(".test."))continue;let E=`${d.rootPath}/${u}`,a=await o.readFile(E);await this.extractRoutes(s,u,a)}}async extractRoutes(s,d,t){let o=[...t.matchAll(T.route)];for(let i of o){let n=i[1]?.toUpperCase()??"GET",u=i[2]??"/",E=i.index??0,a=t.slice(0,E).split(`
9
- `).length,p=t.slice(E,E+500),c=T.zodValidator.test(p),w={id:this.generateEndpointId(n,u),method:n,path:u,kind:this.methodToOpKind(n),handlerName:"handler",source:this.createLocation(d,a,a+5),confidence:this.createConfidence(c?"high":"medium",c?"explicit-schema":"decorator-hints")};this.addEndpoint(s,w)}}}var H={controller:/@Controller\s*\(\s*['"`]([^'"`]*)['"`]\s*\)/g,route:/@(Get|Post|Put|Patch|Delete|Head|Options)\s*\(\s*(?:['"`]([^'"`]*)['"`])?\s*\)/g,body:/@Body\s*\(\s*\)/g,param:/@Param\s*\(\s*(?:['"`]([^'"`]*)['"`])?\s*\)/g,query:/@Query\s*\(\s*(?:['"`]([^'"`]*)['"`])?\s*\)/g,dto:/class\s+(\w+(?:Dto|DTO|Request|Response|Input|Output))\s*\{/g,classValidator:/@(IsString|IsNumber|IsBoolean|IsArray|IsOptional|IsNotEmpty|Min|Max|Length|Matches)/g};class q extends C{id="nestjs";name="NestJS Extractor";frameworks=["nestjs"];priority=20;async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((u)=>`${u}/**/*.ts`).join(","):"**/*.ts",n=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=n.length;for(let u of n){if(u.includes("node_modules")||u.includes(".spec.")||u.includes(".test."))continue;let E=`${d.rootPath}/${u}`,a=await o.readFile(E);await this.extractControllers(s,u,a),await this.extractDtos(s,u,a)}}async extractControllers(s,d,t){let o=[...t.matchAll(H.controller)];for(let i of o){let n=i[1]||"",u=i.index??0,p=t.slice(u).match(/class\s+(\w+)/)?.[1]??"UnknownController",c=t.indexOf("@Controller",u+1),w=c>0?t.slice(u,c):t.slice(u),g=[...w.matchAll(H.route)];for(let y of g){let $=y[1]?.toUpperCase(),X=y[2]||"",K=this.normalizePath(`/${n}/${X}`),Q=w.slice(y.index??0),z=Q.match(/(?:async\s+)?(\w+)\s*\([^)]*\)\s*(?::\s*\w+(?:<[^>]+>)?)?\s*\{/)?.[1]??"unknownHandler",N=u+(y.index??0),D=t.slice(0,N).split(`
10
- `).length,l=H.body.test(Q.slice(0,200)),R=H.param.test(Q.slice(0,200)),x=H.query.test(Q.slice(0,200)),r={id:this.generateEndpointId($,K,z),method:$,path:K,kind:this.methodToOpKind($),handlerName:z,controllerName:p,source:this.createLocation(d,D,D+10),confidence:this.createConfidence("medium","decorator-hints"),frameworkMeta:{hasBody:l,hasParams:R,hasQuery:x}};this.addEndpoint(s,r)}}}async extractDtos(s,d,t){let o=[...t.matchAll(H.dto)];for(let i of o){let n=i[1]??"UnknownDto",u=i.index??0,E=t.slice(0,u).split(`
11
- `).length,a=t.includes("class-validator")||t.includes("@IsString")||t.includes("@IsNumber"),p={id:this.generateSchemaId(n,d),name:n,schemaType:a?"class-validator":"typescript",source:this.createLocation(d,E,E+20),confidence:this.createConfidence(a?"high":"medium",a?"explicit-schema":"inferred-types")};this.addSchema(s,p)}}normalizePath(s){return"/"+s.replace(/\/+/g,"/").replace(/^\/+|\/+$/g,"")}}var U={appRouterExport:/export\s+(?:async\s+)?function\s+(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)/gi,pagesHandler:/export\s+default\s+(?:async\s+)?function/g,zodSchema:/z\.\w+\(/g};class V extends C{id="next-api";name="Next.js API Extractor";frameworks=["next-api"];priority=15;async doExtract(s){let{project:d,fs:t}=s,o=await t.glob("**/app/api/**/route.ts",{cwd:d.rootPath}),i=await t.glob("**/pages/api/**/*.ts",{cwd:d.rootPath}),n=[...o,...i];s.ir.stats.filesScanned=n.length;for(let u of n){let E=`${d.rootPath}/${u}`,a=await t.readFile(E);if(u.includes("/app/api/"))await this.extractAppRoutes(s,u,a);else await this.extractPagesRoutes(s,u,a)}}async extractAppRoutes(s,d,t){let o=d.match(/app\/api\/(.+)\/route\.ts$/),i=o?`/api/${o[1]}`:"/api",n=[...t.matchAll(U.appRouterExport)];for(let u of n){let E=u[1]?.toUpperCase()??"GET",a=u.index??0,p=t.slice(0,a).split(`
12
- `).length,c=U.zodSchema.test(t),w={id:this.generateEndpointId(E,i),method:E,path:i.replace(/\[(\w+)\]/g,":$1"),kind:this.methodToOpKind(E),handlerName:E,source:this.createLocation(d,p,p+10),confidence:this.createConfidence(c?"high":"medium",c?"explicit-schema":"inferred-types"),frameworkMeta:{routeType:"app-router"}};this.addEndpoint(s,w)}}async extractPagesRoutes(s,d,t){let o=d.match(/pages\/api\/(.+)\.ts$/),i=o?`/api/${o[1]}`:"/api";if(!U.pagesHandler.test(t))return;let n=1,u=U.zodSchema.test(t),E=["GET","POST"];for(let a of E){let p={id:this.generateEndpointId(a,i),method:a,path:i.replace(/\[(\w+)\]/g,":$1"),kind:this.methodToOpKind(a),handlerName:"handler",source:this.createLocation(d,n,n+20),confidence:this.createConfidence("low","naming-convention"),frameworkMeta:{routeType:"pages-router"}};this.addEndpoint(s,p)}}}var B={procedure:/\.(query|mutation)\s*\(\s*(?:\{[^}]*\}|[^)]+)\)/gi,procedureName:/(\w+)\s*:\s*(?:publicProcedure|protectedProcedure|procedure)/g,zodInput:/\.input\s*\(\s*(\w+)/g,zodOutput:/\.output\s*\(\s*(\w+)/g};class S extends C{id="trpc";name="tRPC Extractor";frameworks=["trpc"];priority=15;async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((u)=>`${u}/**/*.ts`).join(","):"**/*.ts",n=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=n.length;for(let u of n){if(u.includes("node_modules")||u.includes(".test."))continue;let E=`${d.rootPath}/${u}`,a=await o.readFile(E);if(!a.includes("trpc")&&!a.includes("Procedure"))continue;await this.extractProcedures(s,u,a)}}async extractProcedures(s,d,t){let o=[...t.matchAll(B.procedureName)];for(let i of o){let n=i[1]??"unknownProcedure",u=i.index??0,E=t.slice(0,u).split(`
13
- `).length,a=t.slice(u,u+500),p=a.includes(".query("),c=a.includes(".mutation(");if(!p&&!c)continue;let w=B.zodInput.test(a),g=B.zodOutput.test(a),y=w||g,$=c?"POST":"GET",X={id:`trpc.${n}`,method:$,path:`/trpc/${n}`,kind:c?"command":"query",handlerName:n,source:this.createLocation(d,E,E+10),confidence:this.createConfidence(y?"high":"medium",y?"explicit-schema":"inferred-types"),frameworkMeta:{procedureType:c?"mutation":"query",hasInput:w,hasOutput:g}};this.addEndpoint(s,X)}}}var cs={zodSchema:/(?:export\s+)?const\s+(\w+)\s*=\s*z\.(?:object|string|number|boolean|array|enum|union|intersection|literal|tuple|record)/g,zodInfer:/type\s+(\w+)\s*=\s*z\.infer<typeof\s+(\w+)>/g};class Z extends C{id="zod";name="Zod Schema Extractor";frameworks=["zod"];priority=5;async detect(){return!0}async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((u)=>`${u}/**/*.ts`).join(","):"**/*.ts",n=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=n.length;for(let u of n){if(u.includes("node_modules")||u.includes(".test."))continue;let E=`${d.rootPath}/${u}`,a=await o.readFile(E);if(!a.includes("z."))continue;await this.extractSchemas(s,u,a)}}async extractSchemas(s,d,t){let o=[...t.matchAll(cs.zodSchema)];for(let i of o){let n=i[1]??"unknownSchema",u=i.index??0,E=t.slice(0,u).split(`
14
- `).length,a=0,p=u;for(let y=u;y<t.length;y++){let $=t[y];if($==="("||$==="{"||$==="[")a++;if($===")"||$==="}"||$==="]")a--;if(a===0&&($===";"||$===`
15
- `)){p=y;break}}let c=t.slice(u,p+1),w=E+c.split(`
16
- `).length-1,g={id:this.generateSchemaId(n,d),name:n,schemaType:"zod",rawDefinition:c,source:this.createLocation(d,E,w),confidence:this.createConfidence("high","explicit-schema")};this.addSchema(s,g)}}}function ps(){v.register(new q),v.register(new G),v.register(new O),v.register(new f),v.register(new h),v.register(new S),v.register(new V),v.register(new Z)}var M=[{id:"nestjs",name:"NestJS",packages:["@nestjs/core","@nestjs/common"],importPatterns:[/@nestjs\//]},{id:"express",name:"Express",packages:["express"],importPatterns:[/from ['"]express['"]/]},{id:"fastify",name:"Fastify",packages:["fastify"],importPatterns:[/from ['"]fastify['"]/]},{id:"hono",name:"Hono",packages:["hono"],importPatterns:[/from ['"]hono['"]/]},{id:"elysia",name:"Elysia",packages:["elysia"],importPatterns:[/from ['"]elysia['"]/]},{id:"trpc",name:"tRPC",packages:["@trpc/server"],importPatterns:[/@trpc\/server/]},{id:"next-api",name:"Next.js API",packages:["next"],filePatterns:[/app\/api\/.*\/route\.ts$/,/pages\/api\/.*\.ts$/]},{id:"koa",name:"Koa",packages:["koa","@koa/router"],importPatterns:[/from ['"]koa['"]/]},{id:"hapi",name:"Hapi",packages:["@hapi/hapi"],importPatterns:[/@hapi\/hapi/]}];function Cs(s){let d={...s.dependencies,...s.devDependencies,...s.peerDependencies},t=[];for(let o of M)for(let i of o.packages)if(i in d){t.push({id:o.id,name:o.name,version:d[i],confidence:"high"});break}return t}function Et(s){let d=[],t=new Set;for(let o of M){if(!o.importPatterns)continue;for(let i of o.importPatterns)if(i.test(s)&&!t.has(o.id)){d.push({id:o.id,name:o.name,confidence:"medium"}),t.add(o.id);break}}return d}function ct(s){let d=[],t=new Set;for(let o of M){if(!o.filePatterns)continue;for(let i of o.filePatterns)for(let n of s)if(i.test(n)&&!t.has(o.id)){d.push({id:o.id,name:o.name,confidence:"medium"}),t.add(o.id);break}}return d}function pt(...s){let d=new Map,t={high:3,medium:2,low:1,ambiguous:0};for(let o of s)for(let i of o){let n=d.get(i.id);if(!n||t[i.confidence]>t[n.confidence])d.set(i.id,i)}return Array.from(d.values())}async function Ct(s,d){let t={rootPath:s,frameworks:[]};if(d?.readFile)try{let o=`${s}/package.json`,i=await d.readFile(o),n=JSON.parse(i);t.packageJsonPath=o,t.frameworks=Cs(n)}catch{}if(d?.readFile)try{let o=`${s}/tsconfig.json`;await d.readFile(o),t.tsConfigPath=o}catch{}return t}function wt(){return M.map((s)=>s.id)}function yt(s){return M.some((d)=>d.id===s)}export{Os as registerBuiltInExtractors,Ss as mergeIRs,pt as mergeFrameworkDetections,yt as isFrameworkSupported,wt as getSupportedFrameworks,j as extractors,v as extractorRegistry,Vs as extractFromProject,ct as detectFrameworksFromPaths,Cs as detectFrameworksFromPackageJson,Et as detectFrameworksFromCode,Ct as detectFramework,m as createEmptyIR,A as codegen,J as ExtractorRegistry};
5
+ `)}function Es(s,d){let t;switch(s.toLowerCase()){case"string":t="z.string()";break;case"number":t="z.number()";break;case"boolean":t="z.boolean()";break;case"date":t="z.date()";break;case"string[]":case"array<string>":t="z.array(z.string())";break;case"number[]":case"array<number>":t="z.array(z.number())";break;default:t="z.unknown()"}return d?`${t}.optional()`:t}var j={};L(j,{registerAllExtractors:()=>Cs,ZodSchemaExtractor:()=>Z,TrpcExtractor:()=>S,NextApiExtractor:()=>f,NestJsExtractor:()=>V,HonoExtractor:()=>q,FastifyExtractor:()=>O,ExpressExtractor:()=>G,ElysiaExtractor:()=>h,BaseExtractor:()=>c});class J{extractors=new Map;register(s){this.extractors.set(s.id,s)}unregister(s){return this.extractors.delete(s)}get(s){return this.extractors.get(s)}getAll(){return Array.from(this.extractors.values())}async findMatching(s){let d=[];for(let t of this.extractors.values())try{if(await t.detect(s))d.push(t)}catch{}return d.sort((t,o)=>o.priority-t.priority)}findByFramework(s){let d=[];for(let t of this.extractors.values())if(t.frameworks.includes(s))d.push(t);return d.sort((t,o)=>o.priority-t.priority)}findForFramework(s){return this.findByFramework(s)}hasExtractorFor(s){for(let d of this.extractors.values())if(d.frameworks.includes(s)||d.id===s)return!0;return!1}getSupportedFrameworks(){let s=new Set;for(let d of this.extractors.values()){s.add(d.id);for(let t of d.frameworks)s.add(t)}return Array.from(s)}}var v=new J;function qs(){}async function Ss(s,d={}){let t;if(d.framework){if(t=v.findByFramework(d.framework),t.length===0)return{success:!1,errors:[{code:"EXTRACTOR_NOT_FOUND",message:`No extractor found for framework: ${d.framework}`,recoverable:!1}]}}else if(t=await v.findMatching(s),t.length===0)return{success:!1,errors:[{code:"NO_FRAMEWORK_DETECTED",message:"No supported framework detected in project",recoverable:!1}]};let o=t[0];if(!o)return{success:!1,errors:[{code:"NO_EXTRACTOR",message:"No extractor available",recoverable:!1}]};return await o.extract(s,d)}function Zs(s){if(s.length===0)throw Error("Cannot merge empty IR array");if(s.length===1){if(!s[0])throw Error("First IR is undefined");return s[0]}let d=s[0];if(!d)throw Error("First IR is undefined");let t={version:"1.0",extractedAt:new Date().toISOString(),project:d.project,endpoints:[],schemas:[],errors:[],events:[],ambiguities:[],stats:{filesScanned:0,endpointsFound:0,schemasFound:0,errorsFound:0,eventsFound:0,ambiguitiesFound:0,highConfidence:0,mediumConfidence:0,lowConfidence:0}};for(let o of s)t.endpoints.push(...o.endpoints),t.schemas.push(...o.schemas),t.errors.push(...o.errors),t.events.push(...o.events),t.ambiguities.push(...o.ambiguities),t.stats.filesScanned+=o.stats.filesScanned,t.stats.endpointsFound+=o.stats.endpointsFound,t.stats.schemasFound+=o.stats.schemasFound,t.stats.errorsFound+=o.stats.errorsFound,t.stats.eventsFound+=o.stats.eventsFound,t.stats.ambiguitiesFound+=o.stats.ambiguitiesFound,t.stats.highConfidence+=o.stats.highConfidence,t.stats.mediumConfidence+=o.stats.mediumConfidence,t.stats.lowConfidence+=o.stats.lowConfidence;return t}function m(s){return{version:"1.0",extractedAt:new Date().toISOString(),project:s,endpoints:[],schemas:[],errors:[],events:[],ambiguities:[],stats:{filesScanned:0,endpointsFound:0,schemasFound:0,errorsFound:0,eventsFound:0,ambiguitiesFound:0,highConfidence:0,mediumConfidence:0,lowConfidence:0}}}class c{priority=10;fs;setFs(s){this.fs=s}async detect(s){return s.frameworks.some((d)=>this.frameworks.includes(d.id))}async extract(s,d){if(!this.fs)return{success:!1,errors:[{code:"NO_FS_ADAPTER",message:"File system adapter not configured",recoverable:!1}]};let t=m(s),o={project:s,options:d,fs:this.fs,ir:t};try{return await this.doExtract(o),this.calculateStats(t),{success:!0,ir:t}}catch(i){return{success:!1,errors:[{code:"EXTRACTION_ERROR",message:i instanceof Error?i.message:String(i),recoverable:!1}]}}}calculateStats(s){s.stats.endpointsFound=s.endpoints.length,s.stats.schemasFound=s.schemas.length,s.stats.errorsFound=s.errors.length,s.stats.eventsFound=s.events.length,s.stats.ambiguitiesFound=s.ambiguities.length;let d=[...s.endpoints,...s.schemas,...s.errors,...s.events];for(let t of d)switch(t.confidence.level){case"high":s.stats.highConfidence++;break;case"medium":s.stats.mediumConfidence++;break;case"low":case"ambiguous":s.stats.lowConfidence++;break}}generateEndpointId(s,d,t){let o=d.replace(/^\//,"").replace(/\//g,".").replace(/:/g,"").replace(/\{/g,"").replace(/\}/g,""),i=`${s.toLowerCase()}.${o}`;return t?`${i}.${t}`:i}generateSchemaId(s,d){return`${d.replace(/\.ts$/,"").replace(/\//g,".").replace(/^\.+/,"")}.${s}`}methodToOpKind(s){switch(s){case"GET":case"HEAD":case"OPTIONS":return"query";default:return"command"}}createLocation(s,d,t){return{file:s,startLine:d,endLine:t}}createConfidence(s,...d){return{level:s,reasons:d}}addEndpoint(s,d){s.ir.endpoints.push(d)}addSchema(s,d){s.ir.schemas.push(d)}}var b={route:/\.(get|post|put|patch|delete)\s*\(\s*['"`]([^'"`]+)['"`]/gi,tSchema:/body:\s*t\.\w+/g};class h extends c{id="elysia";name="Elysia Extractor";frameworks=["elysia"];priority=15;async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((n)=>`${n}/**/*.ts`).join(","):"**/*.ts",u=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=u.length;for(let n of u){if(n.includes("node_modules")||n.includes(".test."))continue;let E=`${d.rootPath}/${n}`,a=await o.readFile(E);if(!a.includes("elysia"))continue;await this.extractRoutes(s,n,a)}}async extractRoutes(s,d,t){let o=[...t.matchAll(b.route)];for(let i of o){let u=i[1]?.toUpperCase()??"GET",n=i[2]??"/",E=i.index??0,a=t.slice(0,E).split(`
6
+ `).length,C=t.slice(E,E+500),p=b.tSchema.test(C),w={id:this.generateEndpointId(u,n),method:u,path:n,kind:this.methodToOpKind(u),handlerName:"handler",source:this.createLocation(d,a,a+5),confidence:this.createConfidence(p?"high":"medium",p?"explicit-schema":"decorator-hints")};this.addEndpoint(s,w)}}}var I={route:/(?:app|router)\.(get|post|put|patch|delete|head|options)\s*\(\s*['"`]([^'"`]+)['"`]/gi,routerUse:/(?:app|router)\.use\s*\(\s*['"`]([^'"`]+)['"`]/gi,zodValidate:/validate\s*\(\s*(\w+)\s*\)/g};class G extends c{id="express";name="Express Extractor";frameworks=["express"];priority=15;async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((n)=>`${n}/**/*.ts`).join(","):"**/*.ts",u=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=u.length;for(let n of u){if(n.includes("node_modules")||n.includes(".test."))continue;let E=`${d.rootPath}/${n}`,a=await o.readFile(E);await this.extractRoutes(s,n,a)}}async extractRoutes(s,d,t){let o=[...t.matchAll(I.route)];for(let i of o){let u=i[1]?.toUpperCase()??"GET",n=i[2]??"/",E=i.index??0,a=t.slice(0,E).split(`
7
+ `).length,C=t.slice(E,E+500),p=C.match(/(?:async\s+)?(?:function\s+)?(\w+)|,\s*(\w+)\s*\)/),w=p?.[1]??p?.[2]??"handler",g=I.zodValidate.test(C),y={id:this.generateEndpointId(u,n,w),method:u,path:n,kind:this.methodToOpKind(u),handlerName:w,source:this.createLocation(d,a,a+5),confidence:this.createConfidence(g?"high":"medium",g?"explicit-schema":"decorator-hints")};this.addEndpoint(s,y)}}}var k={route:/(?:fastify|app|server)\.(get|post|put|patch|delete|head|options)\s*\(\s*['"`]([^'"`]+)['"`]/gi,schemaOption:/schema\s*:\s*\{/g,bodySchema:/body\s*:\s*(\w+)/g,responseSchema:/response\s*:\s*\{/g};class O extends c{id="fastify";name="Fastify Extractor";frameworks=["fastify"];priority=15;async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((n)=>`${n}/**/*.ts`).join(","):"**/*.ts",u=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=u.length;for(let n of u){if(n.includes("node_modules")||n.includes(".test."))continue;let E=`${d.rootPath}/${n}`,a=await o.readFile(E);await this.extractRoutes(s,n,a)}}async extractRoutes(s,d,t){let o=[...t.matchAll(k.route)];for(let i of o){let u=i[1]?.toUpperCase()??"GET",n=i[2]??"/",E=i.index??0,a=t.slice(0,E).split(`
8
+ `).length,C=t.slice(E,E+1000),p=k.schemaOption.test(C),w={id:this.generateEndpointId(u,n),method:u,path:n,kind:this.methodToOpKind(u),handlerName:"handler",source:this.createLocation(d,a,a+10),confidence:this.createConfidence(p?"high":"medium",p?"explicit-schema":"decorator-hints"),frameworkMeta:{hasSchema:p}};this.addEndpoint(s,w)}}}var T={route:/(?:app|hono)\.(get|post|put|patch|delete|all)\s*\(\s*['"`]([^'"`]+)['"`]/gi,zodValidator:/zValidator\s*\(\s*['"`](\w+)['"`]\s*,\s*(\w+)/g};class q extends c{id="hono";name="Hono Extractor";frameworks=["hono"];priority=15;async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((n)=>`${n}/**/*.ts`).join(","):"**/*.ts",u=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=u.length;for(let n of u){if(n.includes("node_modules")||n.includes(".test."))continue;let E=`${d.rootPath}/${n}`,a=await o.readFile(E);await this.extractRoutes(s,n,a)}}async extractRoutes(s,d,t){let o=[...t.matchAll(T.route)];for(let i of o){let u=i[1]?.toUpperCase()??"GET",n=i[2]??"/",E=i.index??0,a=t.slice(0,E).split(`
9
+ `).length,C=t.slice(E,E+500),p=T.zodValidator.test(C),w={id:this.generateEndpointId(u,n),method:u,path:n,kind:this.methodToOpKind(u),handlerName:"handler",source:this.createLocation(d,a,a+5),confidence:this.createConfidence(p?"high":"medium",p?"explicit-schema":"decorator-hints")};this.addEndpoint(s,w)}}}var H={controller:/@Controller\s*\(\s*['"`]([^'"`]*)['"`]\s*\)/g,route:/@(Get|Post|Put|Patch|Delete|Head|Options)\s*\(\s*(?:['"`]([^'"`]*)['"`])?\s*\)/g,body:/@Body\s*\(\s*\)/g,param:/@Param\s*\(\s*(?:['"`]([^'"`]*)['"`])?\s*\)/g,query:/@Query\s*\(\s*(?:['"`]([^'"`]*)['"`])?\s*\)/g,dto:/class\s+(\w+(?:Dto|DTO|Request|Response|Input|Output))\s*\{/g,classValidator:/@(IsString|IsNumber|IsBoolean|IsArray|IsOptional|IsNotEmpty|Min|Max|Length|Matches)/g};class V extends c{id="nestjs";name="NestJS Extractor";frameworks=["nestjs"];priority=20;async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((n)=>`${n}/**/*.ts`).join(","):"**/*.ts",u=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=u.length;for(let n of u){if(n.includes("node_modules")||n.includes(".spec.")||n.includes(".test."))continue;let E=`${d.rootPath}/${n}`,a=await o.readFile(E);await this.extractControllers(s,n,a),await this.extractDtos(s,n,a)}}async extractControllers(s,d,t){let o=[...t.matchAll(H.controller)];for(let i of o){let u=i[1]||"",n=i.index??0,C=t.slice(n).match(/class\s+(\w+)/)?.[1]??"UnknownController",p=t.indexOf("@Controller",n+1),w=p>0?t.slice(n,p):t.slice(n),g=[...w.matchAll(H.route)];for(let y of g){let $=y[1]?.toUpperCase(),X=y[2]||"",K=this.normalizePath(`/${u}/${X}`),Q=w.slice(y.index??0),z=Q.match(/(?:async\s+)?(\w+)\s*\([^)]*\)\s*(?::\s*\w+(?:<[^>]+>)?)?\s*\{/)?.[1]??"unknownHandler",N=n+(y.index??0),D=t.slice(0,N).split(`
10
+ `).length,l=H.body.test(Q.slice(0,200)),R=H.param.test(Q.slice(0,200)),x=H.query.test(Q.slice(0,200)),r={id:this.generateEndpointId($,K,z),method:$,path:K,kind:this.methodToOpKind($),handlerName:z,controllerName:C,source:this.createLocation(d,D,D+10),confidence:this.createConfidence("medium","decorator-hints"),frameworkMeta:{hasBody:l,hasParams:R,hasQuery:x}};this.addEndpoint(s,r)}}}async extractDtos(s,d,t){let o=[...t.matchAll(H.dto)];for(let i of o){let u=i[1]??"UnknownDto",n=i.index??0,E=t.slice(0,n).split(`
11
+ `).length,a=t.includes("class-validator")||t.includes("@IsString")||t.includes("@IsNumber"),C={id:this.generateSchemaId(u,d),name:u,schemaType:a?"class-validator":"typescript",source:this.createLocation(d,E,E+20),confidence:this.createConfidence(a?"high":"medium",a?"explicit-schema":"inferred-types")};this.addSchema(s,C)}}normalizePath(s){return"/"+s.replace(/\/+/g,"/").replace(/^\/+|\/+$/g,"")}}var U={appRouterExport:/export\s+(?:async\s+)?function\s+(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)/gi,pagesHandler:/export\s+default\s+(?:async\s+)?function/g,zodSchema:/z\.\w+\(/g};class f extends c{id="next-api";name="Next.js API Extractor";frameworks=["next-api"];priority=15;async doExtract(s){let{project:d,fs:t}=s,o=await t.glob("**/app/api/**/route.ts",{cwd:d.rootPath}),i=await t.glob("**/pages/api/**/*.ts",{cwd:d.rootPath}),u=[...o,...i];s.ir.stats.filesScanned=u.length;for(let n of u){let E=`${d.rootPath}/${n}`,a=await t.readFile(E);if(n.includes("/app/api/"))await this.extractAppRoutes(s,n,a);else await this.extractPagesRoutes(s,n,a)}}async extractAppRoutes(s,d,t){let o=d.match(/app\/api\/(.+)\/route\.ts$/),i=o?`/api/${o[1]}`:"/api",u=[...t.matchAll(U.appRouterExport)];for(let n of u){let E=n[1]?.toUpperCase()??"GET",a=n.index??0,C=t.slice(0,a).split(`
12
+ `).length,p=U.zodSchema.test(t),w={id:this.generateEndpointId(E,i),method:E,path:i.replace(/\[(\w+)\]/g,":$1"),kind:this.methodToOpKind(E),handlerName:E,source:this.createLocation(d,C,C+10),confidence:this.createConfidence(p?"high":"medium",p?"explicit-schema":"inferred-types"),frameworkMeta:{routeType:"app-router"}};this.addEndpoint(s,w)}}async extractPagesRoutes(s,d,t){let o=d.match(/pages\/api\/(.+)\.ts$/),i=o?`/api/${o[1]}`:"/api";if(!U.pagesHandler.test(t))return;let u=1,n=U.zodSchema.test(t),E=["GET","POST"];for(let a of E){let C={id:this.generateEndpointId(a,i),method:a,path:i.replace(/\[(\w+)\]/g,":$1"),kind:this.methodToOpKind(a),handlerName:"handler",source:this.createLocation(d,u,u+20),confidence:this.createConfidence("low","naming-convention"),frameworkMeta:{routeType:"pages-router"}};this.addEndpoint(s,C)}}}var B={procedure:/\.(query|mutation)\s*\(\s*(?:\{[^}]*\}|[^)]+)\)/gi,procedureName:/(\w+)\s*:\s*(?:publicProcedure|protectedProcedure|procedure)/g,zodInput:/\.input\s*\(\s*(\w+)/g,zodOutput:/\.output\s*\(\s*(\w+)/g};class S extends c{id="trpc";name="tRPC Extractor";frameworks=["trpc"];priority=15;async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((n)=>`${n}/**/*.ts`).join(","):"**/*.ts",u=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=u.length;for(let n of u){if(n.includes("node_modules")||n.includes(".test."))continue;let E=`${d.rootPath}/${n}`,a=await o.readFile(E);if(!a.includes("trpc")&&!a.includes("Procedure"))continue;await this.extractProcedures(s,n,a)}}async extractProcedures(s,d,t){let o=[...t.matchAll(B.procedureName)];for(let i of o){let u=i[1]??"unknownProcedure",n=i.index??0,E=t.slice(0,n).split(`
13
+ `).length,a=t.slice(n,n+500),C=a.includes(".query("),p=a.includes(".mutation(");if(!C&&!p)continue;let w=B.zodInput.test(a),g=B.zodOutput.test(a),y=w||g,$=p?"POST":"GET",X={id:`trpc.${u}`,method:$,path:`/trpc/${u}`,kind:p?"command":"query",handlerName:u,source:this.createLocation(d,E,E+10),confidence:this.createConfidence(y?"high":"medium",y?"explicit-schema":"inferred-types"),frameworkMeta:{procedureType:p?"mutation":"query",hasInput:w,hasOutput:g}};this.addEndpoint(s,X)}}}var ps={zodSchema:/(?:export\s+)?const\s+(\w+)\s*=\s*z\.(?:object|string|number|boolean|array|enum|union|intersection|literal|tuple|record)/g,zodInfer:/type\s+(\w+)\s*=\s*z\.infer<typeof\s+(\w+)>/g};class Z extends c{id="zod";name="Zod Schema Extractor";frameworks=["zod"];priority=5;async detect(){return!0}async doExtract(s){let{project:d,options:t,fs:o}=s,i=t.scope?.length?t.scope.map((n)=>`${n}/**/*.ts`).join(","):"**/*.ts",u=await o.glob(i,{cwd:d.rootPath});s.ir.stats.filesScanned=u.length;for(let n of u){if(n.includes("node_modules")||n.includes(".test."))continue;let E=`${d.rootPath}/${n}`,a=await o.readFile(E);if(!a.includes("z."))continue;await this.extractSchemas(s,n,a)}}async extractSchemas(s,d,t){let o=[...t.matchAll(ps.zodSchema)];for(let i of o){let u=i[1]??"unknownSchema",n=i.index??0,E=t.slice(0,n).split(`
14
+ `).length,a=0,C=n;for(let y=n;y<t.length;y++){let $=t[y];if($==="("||$==="{"||$==="[")a++;if($===")"||$==="}"||$==="]")a--;if(a===0&&($===";"||$===`
15
+ `)){C=y;break}}let p=t.slice(n,C+1),w=E+p.split(`
16
+ `).length-1,g={id:this.generateSchemaId(u,d),name:u,schemaType:"zod",rawDefinition:p,source:this.createLocation(d,E,w),confidence:this.createConfidence("high","explicit-schema")};this.addSchema(s,g)}}}function Cs(){v.register(new V),v.register(new G),v.register(new O),v.register(new q),v.register(new h),v.register(new S),v.register(new f),v.register(new Z)}var M=[{id:"nestjs",name:"NestJS",packages:["@nestjs/core","@nestjs/common"],importPatterns:[/@nestjs\//]},{id:"express",name:"Express",packages:["express"],importPatterns:[/from ['"]express['"]/]},{id:"fastify",name:"Fastify",packages:["fastify"],importPatterns:[/from ['"]fastify['"]/]},{id:"hono",name:"Hono",packages:["hono"],importPatterns:[/from ['"]hono['"]/]},{id:"elysia",name:"Elysia",packages:["elysia"],importPatterns:[/from ['"]elysia['"]/]},{id:"trpc",name:"tRPC",packages:["@trpc/server"],importPatterns:[/@trpc\/server/]},{id:"next-api",name:"Next.js API",packages:["next"],filePatterns:[/app\/api\/.*\/route\.ts$/,/pages\/api\/.*\.ts$/]},{id:"koa",name:"Koa",packages:["koa","@koa/router"],importPatterns:[/from ['"]koa['"]/]},{id:"hapi",name:"Hapi",packages:["@hapi/hapi"],importPatterns:[/@hapi\/hapi/]}];function cs(s){let d={...s.dependencies,...s.devDependencies,...s.peerDependencies},t=[];for(let o of M)for(let i of o.packages)if(i in d){t.push({id:o.id,name:o.name,version:d[i],confidence:"high"});break}return t}function pt(s){let d=[],t=new Set;for(let o of M){if(!o.importPatterns)continue;for(let i of o.importPatterns)if(i.test(s)&&!t.has(o.id)){d.push({id:o.id,name:o.name,confidence:"medium"}),t.add(o.id);break}}return d}function Ct(s){let d=[],t=new Set;for(let o of M){if(!o.filePatterns)continue;for(let i of o.filePatterns)for(let u of s)if(i.test(u)&&!t.has(o.id)){d.push({id:o.id,name:o.name,confidence:"medium"}),t.add(o.id);break}}return d}function ct(...s){let d=new Map,t={high:3,medium:2,low:1,ambiguous:0};for(let o of s)for(let i of o){let u=d.get(i.id);if(!u||t[i.confidence]>t[u.confidence])d.set(i.id,i)}return Array.from(d.values())}async function wt(s,d){let t={rootPath:s,frameworks:[]};if(d?.readFile)try{let o=`${s}/package.json`,i=await d.readFile(o),u=JSON.parse(i);t.packageJsonPath=o,t.frameworks=cs(u)}catch{}if(d?.readFile)try{let o=`${s}/tsconfig.json`;await d.readFile(o),t.tsConfigPath=o}catch{}return t}function yt(){return M.map((s)=>s.id)}function $t(s){return M.some((d)=>d.id===s)}import{defineFeature as ws}from"@contractspec/lib.contracts-spec/features";var Ht=ws({meta:{key:"libs.source-extractors",version:"1.0.0",title:"Source Extractors",description:"Extract contract candidates from TypeScript source code across multiple frameworks (NestJS, Express, Fastify, Hono, Elysia, tRPC, Next.js)",domain:"source-extractors",owners:["@contractspec-core"],tags:["package","libs","source-extractors"],stability:"experimental"}});export{qs as registerBuiltInExtractors,Zs as mergeIRs,ct as mergeFrameworkDetections,$t as isFrameworkSupported,yt as getSupportedFrameworks,j as extractors,v as extractorRegistry,Ss as extractFromProject,Ct as detectFrameworksFromPaths,cs as detectFrameworksFromPackageJson,pt as detectFrameworksFromCode,wt as detectFramework,m as createEmptyIR,A as codegen,Ht as SourceExtractorsFeature,J as ExtractorRegistry};
@@ -0,0 +1 @@
1
+ export declare const SourceExtractorsFeature: import("@contractspec/lib.contracts-spec").FeatureModuleSpec;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contractspec/lib.source-extractors",
3
- "version": "2.7.20",
3
+ "version": "2.7.21",
4
4
  "description": "Extract contract candidates from TypeScript source code across multiple frameworks (NestJS, Express, Fastify, Hono, Elysia, tRPC, Next.js)",
5
5
  "keywords": [
6
6
  "contractspec",
@@ -30,14 +30,14 @@
30
30
  "typecheck": "tsc --noEmit"
31
31
  },
32
32
  "dependencies": {
33
- "@contractspec/lib.contracts-spec": "5.4.0",
33
+ "@contractspec/lib.contracts-spec": "5.5.0",
34
34
  "@contractspec/lib.schema": "3.7.14",
35
35
  "typescript": "^5.9.3",
36
36
  "zod": "^4.3.5"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@contractspec/tool.typescript": "3.7.13",
40
- "@contractspec/tool.bun": "3.7.14"
40
+ "@contractspec/tool.bun": "3.7.15"
41
41
  },
42
42
  "types": "./dist/index.d.ts",
43
43
  "files": [