@lssm/lib.contracts-transformers 0.0.0-canary-20251217023603

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,91 @@
1
+ # @lssm/lib.contracts-transformers
2
+
3
+ Contract format transformations: bidirectional import/export between ContractSpec and external API specification formats.
4
+
5
+ ## Supported Formats
6
+
7
+ - **OpenAPI 3.x** - Import from and export to OpenAPI specifications (JSON/YAML, URL/file)
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ bun add @lssm/lib.contracts-transformers
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ ### Export ContractSpec to OpenAPI
18
+
19
+ ```typescript
20
+ import { openApiForRegistry } from '@lssm/lib.contracts-transformers/openapi';
21
+ import { SpecRegistry } from '@lssm/lib.contracts';
22
+
23
+ const registry = new SpecRegistry();
24
+ // ... register your specs ...
25
+
26
+ const openApiDoc = openApiForRegistry(registry, {
27
+ title: 'My API',
28
+ version: '1.0.0',
29
+ description: 'API generated from ContractSpec',
30
+ servers: [{ url: 'https://api.example.com' }],
31
+ });
32
+ ```
33
+
34
+ ### Import from OpenAPI
35
+
36
+ ```typescript
37
+ import { parseOpenApi, importFromOpenApi } from '@lssm/lib.contracts-transformers/openapi';
38
+
39
+ // Parse OpenAPI from file or URL
40
+ const openApiDoc = await parseOpenApi('./api.yaml');
41
+ // Or from URL
42
+ const openApiDoc = await parseOpenApi('https://api.example.com/openapi.json');
43
+
44
+ // Convert to ContractSpec specs
45
+ const importResult = importFromOpenApi(openApiDoc, {
46
+ prefix: 'myApi',
47
+ tags: ['users', 'orders'], // Optional: filter by tags
48
+ exclude: ['deprecated_endpoint'], // Optional: exclude by operationId
49
+ });
50
+
51
+ // importResult contains generated spec code as strings
52
+ for (const spec of importResult.specs) {
53
+ console.log(spec.name, spec.code);
54
+ }
55
+ ```
56
+
57
+ ### Diff ContractSpec vs OpenAPI
58
+
59
+ ```typescript
60
+ import { diffSpecs } from '@lssm/lib.contracts-transformers/openapi';
61
+
62
+ const diffs = diffSpecs(existingSpecs, importedSpecs);
63
+
64
+ for (const diff of diffs) {
65
+ console.log(`${diff.operationId}: ${diff.changes.length} changes`);
66
+ for (const change of diff.changes) {
67
+ console.log(` - ${change.path}: ${change.type}`);
68
+ }
69
+ }
70
+ ```
71
+
72
+ ## Architecture
73
+
74
+ This library is organized by format:
75
+
76
+ - `openapi/` - OpenAPI 3.x transformations
77
+ - `parser.ts` - Parse OpenAPI from JSON/YAML/URL
78
+ - `importer.ts` - Convert OpenAPI to ContractSpec
79
+ - `exporter.ts` - Convert ContractSpec to OpenAPI
80
+ - `differ.ts` - Diff specs for sync operations
81
+ - `schema-converter.ts` - JSON Schema <-> SchemaModel conversion
82
+ - `common/` - Shared utilities and types
83
+
84
+ ## Future Formats
85
+
86
+ The library is designed to be extensible for additional formats:
87
+
88
+ - AsyncAPI (event-driven APIs)
89
+ - gRPC/Protobuf
90
+ - GraphQL Schema
91
+
@@ -0,0 +1 @@
1
+ import{deepEqual as e,extractPathParams as t,getByPath as n,normalizePath as r,toCamelCase as i,toFileName as a,toKebabCase as o,toPascalCase as s,toSnakeCase as c,toSpecName as l,toValidIdentifier as u}from"./utils.js";export{e as deepEqual,t as extractPathParams,n as getByPath,r as normalizePath,i as toCamelCase,a as toFileName,o as toKebabCase,s as toPascalCase,c as toSnakeCase,l as toSpecName,u as toValidIdentifier};
@@ -0,0 +1 @@
1
+ function e(e){return e.replace(/[-_./\s]+(.)?/g,(e,t)=>t?t.toUpperCase():``).replace(/^./,e=>e.toUpperCase())}function t(t){let n=e(t);return n.charAt(0).toLowerCase()+n.slice(1)}function n(e){return e.replace(/([a-z])([A-Z])/g,`$1-$2`).replace(/[\s_./]+/g,`-`).toLowerCase()}function r(e){return e.replace(/([a-z])([A-Z])/g,`$1_$2`).replace(/[\s\-./]+/g,`_`).toLowerCase()}function i(e){let t=e.replace(/[^a-zA-Z0-9_$]/g,`_`);return/^[0-9]/.test(t)&&(t=`_`+t),t}function a(e,n){let r=t(e);return n?`${n}.${r}`:r}function o(e){return n(e.replace(/\./g,`-`))+`.ts`}function s(e,t){if(e===t)return!0;if(e===null||t===null||typeof e!=typeof t)return!1;if(typeof e==`object`){let n=e,r=t,i=Object.keys(n),a=Object.keys(r);if(i.length!==a.length)return!1;for(let e of i)if(!a.includes(e)||!s(n[e],r[e]))return!1;return!0}return!1}function c(e,t){let n=t.split(`.`).filter(Boolean),r=e;for(let e of n){if(typeof r!=`object`||!r)return;r=r[e]}return r}function l(e){return(e.match(/\{([^}]+)\}/g)||[]).map(e=>e.slice(1,-1))}function u(e){let t=e.replace(/^\/+|\/+$/g,``);return t=t.replace(/\/+/g,`/`),`/`+t}export{s as deepEqual,l as extractPathParams,c as getByPath,u as normalizePath,t as toCamelCase,o as toFileName,n as toKebabCase,e as toPascalCase,r as toSnakeCase,a as toSpecName,i as toValidIdentifier};
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ import{detectFormat as e,detectVersion as t,parseOpenApi as n,parseOpenApiDocument as r,parseOpenApiString as i}from"./openapi/parser.js";import{defaultRestPath as a,openApiForRegistry as o,openApiToJson as s,openApiToYaml as c}from"./openapi/exporter.js";import{deepEqual as l,extractPathParams as u,getByPath as d,normalizePath as f,toCamelCase as p,toFileName as m,toKebabCase as h,toPascalCase as g,toSnakeCase as _,toSpecName as v,toValidIdentifier as y}from"./common/utils.js";import{generateImports as b,generateSchemaModelCode as x,getScalarType as S,jsonSchemaToField as C,jsonSchemaToType as w}from"./openapi/schema-converter.js";import{importFromOpenApi as T,importOperation as E}from"./openapi/importer.js";import{createSpecDiff as D,diffAll as O,diffSpecVsOperation as k,diffSpecs as A,formatDiffChanges as j}from"./openapi/differ.js";import"./openapi/index.js";export{D as createSpecDiff,l as deepEqual,a as defaultRestPath,e as detectFormat,t as detectVersion,O as diffAll,k as diffSpecVsOperation,A as diffSpecs,u as extractPathParams,j as formatDiffChanges,b as generateImports,x as generateSchemaModelCode,d as getByPath,S as getScalarType,T as importFromOpenApi,E as importOperation,C as jsonSchemaToField,w as jsonSchemaToType,f as normalizePath,o as openApiForRegistry,s as openApiToJson,c as openApiToYaml,n as parseOpenApi,r as parseOpenApiDocument,i as parseOpenApiString,p as toCamelCase,m as toFileName,h as toKebabCase,g as toPascalCase,_ as toSnakeCase,v as toSpecName,y as toValidIdentifier};
@@ -0,0 +1,2 @@
1
+ import{deepEqual as e}from"../common/utils.js";function t(t,n,r,i){if(e(n,r))return null;let a=`modified`;return n==null?a=`added`:r==null?a=`removed`:typeof n!=typeof r&&(a=`type_changed`),{path:t,type:a,oldValue:n,newValue:r,description:i}}function n(e,r,i,a){let o=[];if(!r&&!i)return o;if(!r)return o.push({path:e,type:`added`,newValue:i,description:`Added ${e}`}),o;if(!i)return o.push({path:e,type:`removed`,oldValue:r,description:`Removed ${e}`}),o;let s=new Set([...Object.keys(r),...Object.keys(i)]);for(let c of s){let s=e?`${e}.${c}`:c;if(a.ignorePaths?.some(e=>s.startsWith(e)))continue;let l=r[c],u=i[c];if(typeof l==`object`&&typeof u==`object`)o.push(...n(s,l,u,a));else{let e=t(s,l,u,`Changed ${s}`);e&&o.push(e)}}return o}function r(n,r,i={}){let a=[];if(!i.ignoreDescriptions){let e=t(`meta.description`,n.meta.description,r.summary??r.description,`Description changed`);e&&a.push(e)}if(!i.ignoreTags){let t=[...n.meta.tags??[]].sort(),i=[...r.tags].sort();e(t,i)||a.push({path:`meta.tags`,type:`modified`,oldValue:t,newValue:i,description:`Tags changed`})}if(!i.ignoreTransport){let e=n.transport?.rest?.method??(n.meta.kind===`query`?`GET`:`POST`),t=r.method.toUpperCase();e!==t&&a.push({path:`transport.rest.method`,type:`modified`,oldValue:e,newValue:t,description:`HTTP method changed`});let i=n.transport?.rest?.path;i&&i!==r.path&&a.push({path:`transport.rest.path`,type:`modified`,oldValue:i,newValue:r.path,description:`Path changed`})}return n.meta.stability===`deprecated`!==r.deprecated&&a.push({path:`meta.stability`,type:`modified`,oldValue:n.meta.stability,newValue:r.deprecated?`deprecated`:`stable`,description:`Deprecation status changed`}),a}function i(e,t,r={}){let i=[],a=n(`meta`,e.meta,t.meta,{...r,ignorePaths:[...r.ignorePaths??[],...r.ignoreDescriptions?[`meta.description`,`meta.goal`,`meta.context`]:[],...r.ignoreTags?[`meta.tags`]:[]]});if(i.push(...a),!r.ignoreTransport){let a=n(`transport`,e.transport,t.transport,r);i.push(...a)}let o=n(`policy`,e.policy,t.policy,r);return i.push(...o),i}function a(e,t,n,r={}){let a=[],o=!1;return t?(a=i(t,n.spec,r),o=a.length===0):a=[{path:``,type:`added`,newValue:n.spec,description:`New spec imported from OpenAPI`}],{operationId:e,existing:t,incoming:n,changes:a,isEquivalent:o}}function o(e,t,n={}){let r=[],i=new Set;for(let o of t){let t=o.source.sourceId,s;for(let[n,r]of e){let e=r.meta.name;if(n===t||e.includes(t)){s=r,i.add(n);break}}r.push(a(t,s,o,n))}for(let[t,n]of e)i.has(t)||r.push({operationId:t,existing:n,incoming:void 0,changes:[{path:``,type:`removed`,oldValue:n,description:`Spec no longer exists in OpenAPI source`}],isEquivalent:!1});return r}function s(e){if(e.length===0)return`No changes detected`;let t=[];for(let n of e){let e={added:`+`,removed:`-`,modified:`~`,type_changed:`!`,required_changed:`?`}[n.type];t.push(`${e} ${n.path}: ${n.description}`),n.type===`modified`||n.type===`type_changed`?(t.push(` old: ${JSON.stringify(n.oldValue)}`),t.push(` new: ${JSON.stringify(n.newValue)}`)):n.type===`added`?t.push(` value: ${JSON.stringify(n.newValue)}`):n.type===`removed`&&t.push(` was: ${JSON.stringify(n.oldValue)}`)}return t.join(`
2
+ `)}export{a as createSpecDiff,o as diffAll,r as diffSpecVsOperation,i as diffSpecs,s as formatDiffChanges};
@@ -0,0 +1 @@
1
+ import{z as e}from"zod";function t(e,t){return`${e.replace(/\./g,`_`)}_v${t}`}function n(e,n,r){return`${e}_${t(n,r)}`}function r(e,t){return(t??(e===`query`?`GET`:`POST`)).toLowerCase()}function i(e,t){return`/${e.replace(/\./g,`/`)}/v${t}`}function a(e){let t=e.transport?.rest?.path??i(e.meta.name,e.meta.version);return t.startsWith(`/`)?t:`/${t}`}function o(t){return t?e.toJSONSchema(t.getZod()):null}function s(e){return{input:o(e.io.input),output:o(e.io.output),meta:{name:e.meta.name,version:e.meta.version,kind:e.meta.kind,description:e.meta.description,tags:e.meta.tags??[],stability:e.meta.stability??`stable`}}}function c(e,i={}){let o=e.listSpecs().filter(e=>e.meta.kind===`command`||e.meta.kind===`query`).slice().sort((e,t)=>{let n=e.meta.name.localeCompare(t.meta.name);return n===0?e.meta.version-t.meta.version:n}),c={openapi:`3.1.0`,info:{title:i.title??`ContractSpec API`,version:i.version??`0.0.0`,...i.description?{description:i.description}:{}},...i.servers?{servers:i.servers}:{},paths:{},components:{schemas:{}}};for(let e of o){let i=s(e),o=r(e.meta.kind,e.transport?.rest?.method),l=a(e),u=t(e.meta.name,e.meta.version),d=c.paths[l]??={},f={operationId:u,summary:e.meta.description??e.meta.name,description:e.meta.description,tags:e.meta.tags??[],"x-contractspec":{name:e.meta.name,version:e.meta.version,kind:e.meta.kind},responses:{}};if(i.input){let t=n(`Input`,e.meta.name,e.meta.version);c.components.schemas[t]=i.input,f.requestBody={required:!0,content:{"application/json":{schema:{$ref:`#/components/schemas/${t}`}}}}}let p={};if(i.output){let t=n(`Output`,e.meta.name,e.meta.version);c.components.schemas[t]=i.output,p[200]={description:`OK`,content:{"application/json":{schema:{$ref:`#/components/schemas/${t}`}}}}}else p[200]={description:`OK`};f.responses=p,d[o]=f}return c}function l(e,t={}){let n=c(e,t);return JSON.stringify(n,null,2)}function u(e,t={}){return d(c(e,t))}function d(e,t=0){let n=` `.repeat(t),r=``;if(Array.isArray(e))for(let i of e)typeof i==`object`&&i?r+=`${n}-\n${d(i,t+1)}`:r+=`${n}- ${JSON.stringify(i)}\n`;else if(typeof e==`object`&&e)for(let[i,a]of Object.entries(e))Array.isArray(a)||typeof a==`object`&&a?r+=`${n}${i}:\n${d(a,t+1)}`:r+=`${n}${i}: ${JSON.stringify(a)}\n`;return r}export{i as defaultRestPath,c as openApiForRegistry,l as openApiToJson,u as openApiToYaml};
@@ -0,0 +1,2 @@
1
+ import{toFileName as e,toPascalCase as t,toSpecName as n,toValidIdentifier as r}from"../common/utils.js";import{generateImports as i,generateSchemaModelCode as a}from"./schema-converter.js";const o=[`post`,`put`,`delete`,`patch`];function s(e){return o.includes(e.toLowerCase())?`command`:`query`}function c(e,t){if(!e.security||e.security.length===0)return t;for(let t of e.security)if(Object.keys(t).length===0)return`anonymous`;return`user`}function l(e){let t=[];for(let n of e.pathParams)t.push({name:n.name,schema:n.schema,required:!0});for(let n of e.queryParams)t.push({name:n.name,schema:n.schema,required:n.required});let n=[`authorization`,`content-type`,`accept`,`user-agent`];for(let r of e.headerParams)n.includes(r.name.toLowerCase())||t.push({name:r.name,schema:r.schema,required:r.required});if(e.requestBody?.schema){let n=e.requestBody.schema;if(`$ref`in n)t.push({name:`body`,schema:n,required:e.requestBody.required});else{let e=n,r=e.properties,i=e.required??[];if(r)for(let[e,n]of Object.entries(r))t.push({name:e,schema:n,required:i.includes(e)})}}return t.length===0?{schema:null,fields:[]}:{schema:{type:`object`,properties:t.reduce((e,t)=>(e[t.name]=t.schema,e),{}),required:t.filter(e=>e.required).map(e=>e.name)},fields:t}}function u(e){for(let t of[`200`,`201`,`202`,`204`]){let n=e.responses[t];if(n?.schema)return n.schema}for(let[t,n]of Object.entries(e.responses))if(t.startsWith(`2`)&&n.schema)return n.schema;return null}function d(e,a,o,l){let u=n(e.operationId,a.prefix),d=s(e.method),f=c(e,a.defaultAuth??`user`),p=[];p.push(`import { defineCommand, defineQuery } from '@lssm/lib.contracts';`),(o||l)&&p.push(i([...o?.fields??[],...l?.fields??[]])),p.push(``),o&&o.code&&(p.push(`// Input schema`),p.push(o.code),p.push(``)),l&&l.code&&(p.push(`// Output schema`),p.push(l.code),p.push(``));let m=d===`command`?`defineCommand`:`defineQuery`,h=r(t(e.operationId));return p.push(`/**`),p.push(` * ${e.summary??e.operationId}`),e.description&&(p.push(` *`),p.push(` * ${e.description}`)),p.push(` *`),p.push(` * @source OpenAPI: ${e.method.toUpperCase()} ${e.path}`),p.push(` */`),p.push(`export const ${h}Spec = ${m}({`),p.push(` meta: {`),p.push(` name: '${u}',`),p.push(` version: 1,`),p.push(` stability: '${a.defaultStability??`stable`}',`),p.push(` owners: [${(a.defaultOwners??[]).map(e=>`'${e}'`).join(`, `)}],`),p.push(` tags: [${e.tags.map(e=>`'${e}'`).join(`, `)}],`),p.push(` description: ${JSON.stringify(e.summary??e.operationId)},`),p.push(` goal: ${JSON.stringify(e.description??`Imported from OpenAPI`)},`),p.push(` context: 'Imported from OpenAPI: ${e.method.toUpperCase()} ${e.path}',`),p.push(` },`),p.push(` io: {`),o?p.push(` input: ${o.name},`):p.push(` input: null,`),l?p.push(` output: ${l.name},`):p.push(` output: null, // TODO: Define output schema`),p.push(` },`),p.push(` policy: {`),p.push(` auth: '${f}',`),p.push(` },`),p.push(` transport: {`),p.push(` rest: {`),p.push(` method: '${e.method.toUpperCase()}',`),p.push(` path: '${e.path}',`),p.push(` },`),p.push(` },`),p.push(`});`),p.join(`
2
+ `)}function f(t,r={}){let{tags:i,exclude:o=[],include:s}=r,c=[],f=[],p=[];for(let m of t.operations){if(i&&i.length>0&&!m.tags.some(e=>i.includes(e))){f.push({sourceId:m.operationId,reason:`No matching tags (has: ${m.tags.join(`, `)})`});continue}if(s&&s.length>0){if(!s.includes(m.operationId)){f.push({sourceId:m.operationId,reason:`Not in include list`});continue}}else if(o.includes(m.operationId)){f.push({sourceId:m.operationId,reason:`In exclude list`});continue}if(m.deprecated&&r.defaultStability!==`deprecated`){f.push({sourceId:m.operationId,reason:`Deprecated operation`});continue}try{let{schema:i}=l(m),o=i?a(i,`${m.operationId}Input`):null,s=u(m),f=d(m,r,o,s?a(s,`${m.operationId}Output`):null),p=e(n(m.operationId,r.prefix)),h={rest:{method:m.method.toUpperCase(),path:m.path,params:{path:m.pathParams.map(e=>e.name),query:m.queryParams.map(e=>e.name),header:m.headerParams.map(e=>e.name),cookie:m.cookieParams.map(e=>e.name)}}},g={type:`openapi`,sourceId:m.operationId,operationId:m.operationId,openApiVersion:t.version,importedAt:new Date};c.push({spec:{},code:f,fileName:p,source:g,transportHints:h})}catch(e){p.push({sourceId:m.operationId,error:e instanceof Error?e.message:String(e)})}}return{specs:c,skipped:f,errors:p,summary:{total:t.operations.length,imported:c.length,skipped:f.length,errors:p.length}}}function p(e,t={}){let{schema:n}=l(e),r=n?a(n,`${e.operationId}Input`):null,i=u(e);return d(e,t,r,i?a(i,`${e.operationId}Output`):null)}export{f as importFromOpenApi,p as importOperation};
@@ -0,0 +1 @@
1
+ import{detectFormat as e,detectVersion as t,parseOpenApi as n,parseOpenApiDocument as r,parseOpenApiString as i}from"./parser.js";import{defaultRestPath as a,openApiForRegistry as o,openApiToJson as s,openApiToYaml as c}from"./exporter.js";import{generateImports as l,generateSchemaModelCode as u,getScalarType as d,jsonSchemaToField as f,jsonSchemaToType as p}from"./schema-converter.js";import{importFromOpenApi as m,importOperation as h}from"./importer.js";import{createSpecDiff as g,diffAll as _,diffSpecVsOperation as v,diffSpecs as y,formatDiffChanges as b}from"./differ.js";export{g as createSpecDiff,a as defaultRestPath,e as detectFormat,t as detectVersion,_ as diffAll,v as diffSpecVsOperation,y as diffSpecs,b as formatDiffChanges,l as generateImports,u as generateSchemaModelCode,d as getScalarType,m as importFromOpenApi,h as importOperation,f as jsonSchemaToField,p as jsonSchemaToType,o as openApiForRegistry,s as openApiToJson,c as openApiToYaml,n as parseOpenApi,r as parseOpenApiDocument,i as parseOpenApiString};
@@ -0,0 +1 @@
1
+ import{parse as e}from"yaml";const t=[`get`,`post`,`put`,`delete`,`patch`,`head`,`options`,`trace`];function n(t,n=`json`){return n===`yaml`?e(t):JSON.parse(t)}function r(e){let t=e.trim();return t.startsWith(`{`)||t.startsWith(`[`)?`json`:`yaml`}function i(e){return e.openapi.startsWith(`3.1`)?`3.1`:`3.0`}function a(e){return typeof e==`object`&&!!e&&`$ref`in e}function o(e,t){if(!t.startsWith(`#/`))return;let n=t.slice(2).split(`/`),r=e;for(let e of n){if(typeof r!=`object`||!r)return;r=r[e]}return r}function s(e,t){if(t)return a(t)?o(e,t.$ref)??t:t}function c(e,t){let n={path:[],query:[],header:[],cookie:[]};if(!t)return n;for(let r of t){let t;if(a(r)){let n=o(e,r.$ref);if(!n)continue;t=n}else t=r;let i={name:t.name,in:t.in,required:t.required??t.in===`path`,description:t.description,schema:t.schema,deprecated:t.deprecated??!1};n[t.in]?.push(i)}return n}function l(e,t){return e+t.split(`/`).filter(Boolean).map(e=>e.startsWith(`{`)&&e.endsWith(`}`)?`By`+e.slice(1,-1).charAt(0).toUpperCase()+e.slice(2,-1):e.charAt(0).toUpperCase()+e.slice(1)).join(``)}function u(e,t,n,r,i){let u=c(e,[...i??[],...r.parameters??[]]),d;if(r.requestBody){let t=a(r.requestBody)?o(e,r.requestBody.$ref):r.requestBody;if(t){let n=Object.keys(t.content??{})[0]??`application/json`,r=t.content?.[n];r?.schema&&(d={required:t.required??!1,schema:s(e,r.schema),contentType:n})}}let f={};for(let[t,n]of Object.entries(r.responses??{})){let r=a(n)?o(e,n.$ref):n;if(r){let n=Object.keys(r.content??{})[0],i=n?r.content?.[n]:void 0;f[t]={description:r.description,schema:i?.schema?s(e,i.schema):void 0,contentType:n}}}let p=r?.[`x-contractspec`];return{operationId:r.operationId??l(t,n),method:t,path:n,summary:r.summary,description:r.description,tags:r.tags??[],pathParams:u.path,queryParams:u.query,headerParams:u.header,cookieParams:u.cookie,requestBody:d,responses:f,deprecated:r.deprecated??!1,security:r.security,contractSpecMeta:p}}function d(e,n={}){let r=i(e),a=[],o=[];for(let[n,r]of Object.entries(e.paths??{})){if(!r)continue;let i=r.parameters;for(let s of t){let t=r[s];if(t)try{o.push(u(e,s,n,t,i))}catch(e){a.push(`Failed to parse ${s.toUpperCase()} ${n}: ${e}`)}}}let s={},c=e.components;if(c?.schemas)for(let[e,t]of Object.entries(c.schemas))s[e]=t;let l=(e.servers??[]).map(e=>({url:e.url,description:e.description,variables:e.variables}));return{document:e,version:r,info:{title:e.info.title,version:e.info.version,description:e.info.description},operations:o,schemas:s,servers:l,warnings:a}}async function f(e,t={}){let{fetch:i=globalThis.fetch,readFile:a,timeout:o=3e4}=t,s,c;if(e.startsWith(`http://`)||e.startsWith(`https://`)){let t=new AbortController,n=setTimeout(()=>t.abort(),o);try{let n=await i(e,{signal:t.signal});if(!n.ok)throw Error(`HTTP ${n.status}: ${n.statusText}`);s=await n.text()}finally{clearTimeout(n)}c=e.endsWith(`.yaml`)||e.endsWith(`.yml`)?`yaml`:e.endsWith(`.json`)?`json`:r(s)}else{if(!a)throw Error(`readFile adapter required for file paths`);s=await a(e),c=e.endsWith(`.yaml`)||e.endsWith(`.yml`)?`yaml`:e.endsWith(`.json`)?`json`:r(s)}return d(n(s,c),t)}export{r as detectFormat,i as detectVersion,f as parseOpenApi,d as parseOpenApiDocument,n as parseOpenApiString};
@@ -0,0 +1,4 @@
1
+ import{toCamelCase as e,toPascalCase as t,toValidIdentifier as n}from"../common/utils.js";const r={string:`ScalarTypeEnum.STRING`,integer:`ScalarTypeEnum.INT`,number:`ScalarTypeEnum.FLOAT`,boolean:`ScalarTypeEnum.BOOLEAN`,"string:date":`ScalarTypeEnum.DATE`,"string:date-time":`ScalarTypeEnum.DATE_TIME`,"string:email":`ScalarTypeEnum.EMAIL`,"string:uri":`ScalarTypeEnum.URL`,"string:uuid":`ScalarTypeEnum.ID`};function i(e){return`$ref`in e}function a(e){let t=e.split(`/`);return t[t.length-1]??`Unknown`}function o(e,n){if(i(e))return{type:t(a(e.$ref)),optional:!1,array:!1,primitive:!1};let r=e,s=r.type,c=r.format,l=r.nullable;if(s===`array`){let e=r.items;return e?{...o(e,n),array:!0,optional:l??!1}:{type:`unknown`,optional:l??!1,array:!0,primitive:!1}}return s===`object`||r.properties?{type:n?t(n):`Record<string, unknown>`,optional:l??!1,array:!1,primitive:!1}:r.enum?{type:n?t(n):`string`,optional:l??!1,array:!1,primitive:!1}:(c&&`${s}${c}`,s===`string`?{type:`string`,optional:l??!1,array:!1,primitive:!0}:s===`integer`||s===`number`?{type:`number`,optional:l??!1,array:!1,primitive:!0}:s===`boolean`?{type:`boolean`,optional:l??!1,array:!1,primitive:!0}:{type:`unknown`,optional:l??!1,array:!1,primitive:!1})}function s(e){if(i(e))return;let t=e,n=t.type,a=t.format;if(n)return r[a?`${n}:${a}`:n]??r[n]}function c(t,r,a){let c=o(t,r),l=s(t),u;if(!i(t)){let e=t.enum;e&&(u=e.map(String))}return{name:n(e(r)),type:{...c,optional:!a||c.optional,description:i(t)?void 0:t.description},scalarType:l,enumValues:u}}function l(e,r,o=0){let s=` `.repeat(o),l=[],d;if(i(e))return{name:t(a(e.$ref)),fields:[],code:`// Reference to ${e.$ref}`};let f=e;d=f.description;let p=f.properties,m=f.required??[];if(!p)return{name:t(r),description:d,fields:[],code:`${s}// Empty schema for ${r}`};for(let[e,t]of Object.entries(p)){let n=m.includes(e);l.push(c(t,e,n))}let h=[],g=t(n(r));h.push(`${s}export const ${g} = defineSchemaModel({`),h.push(`${s} name: '${g}',`),d&&h.push(`${s} description: ${JSON.stringify(d)},`),h.push(`${s} fields: {`);for(let e of l){let t=u(e,o+2);h.push(t)}return h.push(`${s} },`),h.push(`${s}});`),{name:g,description:d,fields:l,code:h.join(`
2
+ `)}}function u(e,t){let n=` `.repeat(t),r=[];return r.push(`${n}${e.name}: {`),e.enumValues?r.push(`${n} type: new EnumType([${e.enumValues.map(e=>`'${e}'`).join(`, `)}]),`):e.scalarType?r.push(`${n} type: ${e.scalarType},`):r.push(`${n} type: ${e.type.type}, // TODO: Define or import this type`),e.type.optional&&r.push(`${n} isOptional: true,`),e.type.array&&r.push(`${n} isArray: true,`),r.push(`${n}},`),r.join(`
3
+ `)}function d(e){let t=new Set;t.add(`import { defineSchemaModel, ScalarTypeEnum, EnumType } from '@lssm/lib.schema';`);for(let t of e)!t.type.primitive&&!t.enumValues&&t.scalarType;return Array.from(t).join(`
4
+ `)}export{d as generateImports,l as generateSchemaModelCode,s as getScalarType,c as jsonSchemaToField,o as jsonSchemaToType};
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@lssm/lib.contracts-transformers",
3
+ "version": "0.0.0-canary-20251217023603",
4
+ "description": "Contract format transformations: import/export between ContractSpec and external formats (OpenAPI, AsyncAPI, etc.)",
5
+ "type": "module",
6
+ "scripts": {
7
+ "publish:pkg": "bun publish --tolerate-republish --ignore-scripts --verbose",
8
+ "build": "bun build:bundle && bun build:types",
9
+ "build:bundle": "tsdown",
10
+ "build:types": "tsc --noEmit",
11
+ "dev": "bun build:bundle --watch",
12
+ "clean": "rimraf dist .turbo",
13
+ "lint": "bun lint:fix",
14
+ "lint:fix": "eslint src --fix",
15
+ "lint:check": "eslint src",
16
+ "test": "bun test"
17
+ },
18
+ "dependencies": {
19
+ "@lssm/lib.contracts": "workspace:*",
20
+ "@lssm/lib.schema": "workspace:*",
21
+ "openapi-types": "^12.1.3",
22
+ "yaml": "^2.7.1",
23
+ "zod": "^4.1.13"
24
+ },
25
+ "devDependencies": {
26
+ "@lssm/tool.tsdown": "workspace:*",
27
+ "@lssm/tool.typescript": "workspace:*",
28
+ "tsdown": "^0.17.4",
29
+ "typescript": "^5.9.3"
30
+ },
31
+ "main": "./dist/index.js",
32
+ "types": "./dist/index.d.ts",
33
+ "files": [
34
+ "dist",
35
+ "README.md"
36
+ ],
37
+ "module": "./dist/index.js",
38
+ "exports": {
39
+ ".": "./src/index.ts",
40
+ "./common": "./src/common/index.ts",
41
+ "./openapi": "./src/openapi/index.ts",
42
+ "./*": "./*"
43
+ },
44
+ "publishConfig": {
45
+ "access": "public",
46
+ "exports": {
47
+ ".": "./dist/index.js",
48
+ "./common": "./dist/common/index.js",
49
+ "./openapi": "./dist/openapi/index.js",
50
+ "./*": "./*"
51
+ }
52
+ }
53
+ }