@contractspec/lib.contracts-transformers 3.7.16 → 3.7.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,2375 +1,133 @@
1
1
  // @bun
2
- // src/common/utils.ts
3
- function toPascalCase(str) {
4
- return str.replace(/[-_./\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : "").replace(/^./, (c) => c.toUpperCase());
5
- }
6
- function toCamelCase(str) {
7
- const pascal = toPascalCase(str);
8
- return pascal.charAt(0).toLowerCase() + pascal.slice(1);
9
- }
10
- function toKebabCase(str) {
11
- return str.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[\s_./]+/g, "-").toLowerCase();
12
- }
13
- function toSnakeCase(str) {
14
- return str.replace(/([a-z])([A-Z])/g, "$1_$2").replace(/[\s\-./]+/g, "_").toLowerCase();
15
- }
16
- function toValidIdentifier(str) {
17
- let result = str.replace(/[^a-zA-Z0-9_$]/g, "_");
18
- if (/^[0-9]/.test(result)) {
19
- result = "_" + result;
20
- }
21
- return result;
22
- }
23
- function toSpecKey(operationId, prefix) {
24
- const key = toCamelCase(operationId);
25
- return prefix ? `${prefix}.${key}` : key;
26
- }
27
- function toFileName(specName) {
28
- return toKebabCase(specName.replace(/\./g, "-")) + ".ts";
29
- }
30
- function deepEqual(a, b) {
31
- if (a === b)
32
- return true;
33
- if (a === null || b === null)
34
- return false;
35
- if (typeof a !== typeof b)
36
- return false;
37
- if (typeof a === "object") {
38
- const aObj = a;
39
- const bObj = b;
40
- const aKeys = Object.keys(aObj);
41
- const bKeys = Object.keys(bObj);
42
- if (aKeys.length !== bKeys.length)
43
- return false;
44
- for (const key of aKeys) {
45
- if (!bKeys.includes(key))
46
- return false;
47
- if (!deepEqual(aObj[key], bObj[key]))
48
- return false;
49
- }
50
- return true;
51
- }
52
- return false;
53
- }
54
- function getByPath(obj, path) {
55
- const parts = path.split(".").filter(Boolean);
56
- let current = obj;
57
- for (const part of parts) {
58
- if (current === null || current === undefined)
59
- return;
60
- if (typeof current !== "object")
61
- return;
62
- current = current[part];
63
- }
64
- return current;
65
- }
66
- function extractPathParams(path) {
67
- const matches = path.match(/\{([^}]+)\}/g) || [];
68
- return matches.map((m) => m.slice(1, -1));
69
- }
70
- function normalizePath(path) {
71
- let normalized = path.replace(/^\/+|\/+$/g, "");
72
- normalized = normalized.replace(/\/+/g, "/");
73
- return "/" + normalized;
74
- }
75
- // src/openapi/differ.ts
76
- function compareValues(path, oldValue, newValue, description) {
77
- if (deepEqual(oldValue, newValue)) {
78
- return null;
79
- }
80
- let changeType = "modified";
81
- if (oldValue === undefined || oldValue === null) {
82
- changeType = "added";
83
- } else if (newValue === undefined || newValue === null) {
84
- changeType = "removed";
85
- } else if (typeof oldValue !== typeof newValue) {
86
- changeType = "type_changed";
87
- }
88
- return {
89
- path,
90
- type: changeType,
91
- oldValue,
92
- newValue,
93
- description
94
- };
95
- }
96
- function diffObjects(path, oldObj, newObj, options) {
97
- const changes = [];
98
- if (!oldObj && !newObj)
99
- return changes;
100
- if (!oldObj) {
101
- changes.push({
102
- path,
103
- type: "added",
104
- newValue: newObj,
105
- description: `Added ${path}`
106
- });
107
- return changes;
108
- }
109
- if (!newObj) {
110
- changes.push({
111
- path,
112
- type: "removed",
113
- oldValue: oldObj,
114
- description: `Removed ${path}`
115
- });
116
- return changes;
117
- }
118
- const allKeys = new Set([...Object.keys(oldObj), ...Object.keys(newObj)]);
119
- for (const key of allKeys) {
120
- const keyPath = path ? `${path}.${key}` : key;
121
- if (options.ignorePaths?.some((p) => keyPath.startsWith(p))) {
122
- continue;
123
- }
124
- const oldVal = oldObj[key];
125
- const newVal = newObj[key];
126
- if (typeof oldVal === "object" && typeof newVal === "object") {
127
- changes.push(...diffObjects(keyPath, oldVal, newVal, options));
128
- } else {
129
- const change = compareValues(keyPath, oldVal, newVal, `Changed ${keyPath}`);
130
- if (change) {
131
- changes.push(change);
132
- }
133
- }
134
- }
135
- return changes;
136
- }
137
- function diffSpecVsOperation(spec, operation, options = {}) {
138
- const changes = [];
139
- if (!options.ignoreDescriptions) {
140
- const descChange = compareValues("meta.description", spec.meta.description, operation.summary ?? operation.description, "Description changed");
141
- if (descChange)
142
- changes.push(descChange);
143
- }
144
- if (!options.ignoreTags) {
145
- const oldTags = [...spec.meta.tags ?? []].sort();
146
- const newTags = [...operation.tags].sort();
147
- if (!deepEqual(oldTags, newTags)) {
148
- changes.push({
149
- path: "meta.tags",
150
- type: "modified",
151
- oldValue: oldTags,
152
- newValue: newTags,
153
- description: "Tags changed"
154
- });
155
- }
156
- }
157
- if (!options.ignoreTransport) {
158
- const specMethod = spec.transport?.rest?.method ?? (spec.meta.kind === "query" ? "GET" : "POST");
159
- const opMethod = operation.method.toUpperCase();
160
- if (specMethod !== opMethod) {
161
- changes.push({
162
- path: "transport.rest.method",
163
- type: "modified",
164
- oldValue: specMethod,
165
- newValue: opMethod,
166
- description: "HTTP method changed"
167
- });
168
- }
169
- const specPath = spec.transport?.rest?.path;
170
- if (specPath && specPath !== operation.path) {
171
- changes.push({
172
- path: "transport.rest.path",
173
- type: "modified",
174
- oldValue: specPath,
175
- newValue: operation.path,
176
- description: "Path changed"
177
- });
178
- }
179
- }
180
- const specDeprecated = spec.meta.stability === "deprecated";
181
- if (specDeprecated !== operation.deprecated) {
182
- changes.push({
183
- path: "meta.stability",
184
- type: "modified",
185
- oldValue: spec.meta.stability,
186
- newValue: operation.deprecated ? "deprecated" : "stable",
187
- description: "Deprecation status changed"
188
- });
189
- }
190
- return changes;
191
- }
192
- function diffSpecs(oldSpec, newSpec, options = {}) {
193
- const changes = [];
194
- const metaChanges = diffObjects("meta", oldSpec.meta, newSpec.meta, {
195
- ...options,
196
- ignorePaths: [
197
- ...options.ignorePaths ?? [],
198
- ...options.ignoreDescriptions ? ["meta.description", "meta.goal", "meta.context"] : [],
199
- ...options.ignoreTags ? ["meta.tags"] : []
200
- ]
201
- });
202
- changes.push(...metaChanges);
203
- if (!options.ignoreTransport) {
204
- const transportChanges = diffObjects("transport", oldSpec.transport, newSpec.transport, options);
205
- changes.push(...transportChanges);
206
- }
207
- const policyChanges = diffObjects("policy", oldSpec.policy, newSpec.policy, options);
208
- changes.push(...policyChanges);
209
- return changes;
210
- }
211
- function createSpecDiff(operationId, existing, incoming, options = {}) {
212
- let changes = [];
213
- let isEquivalent = false;
214
- if (existing && incoming.operationSpec) {
215
- changes = diffSpecs(existing, incoming.operationSpec, options);
216
- isEquivalent = changes.length === 0;
217
- } else if (existing && !incoming.operationSpec) {
218
- changes = [
219
- {
220
- path: "",
221
- type: "modified",
222
- oldValue: existing,
223
- newValue: incoming.code,
224
- description: "Spec code imported from OpenAPI (runtime comparison not available)"
225
- }
226
- ];
227
- } else {
228
- changes = [
229
- {
230
- path: "",
231
- type: "added",
232
- newValue: incoming.operationSpec ?? incoming.code,
233
- description: "New spec imported from OpenAPI"
234
- }
235
- ];
236
- }
237
- return {
238
- operationId,
239
- existing,
240
- incoming,
241
- changes,
242
- isEquivalent
243
- };
244
- }
245
- function diffAll(existingSpecs, importedSpecs, options = {}) {
246
- const diffs = [];
247
- const matchedExisting = new Set;
248
- for (const imported of importedSpecs) {
249
- const operationId = imported.source.sourceId;
250
- let existing;
251
- for (const [key, spec] of existingSpecs) {
252
- const specName = spec.meta.key;
253
- if (key === operationId || specName.includes(operationId)) {
254
- existing = spec;
255
- matchedExisting.add(key);
256
- break;
257
- }
258
- }
259
- diffs.push(createSpecDiff(operationId, existing, imported, options));
260
- }
261
- for (const [key, spec] of existingSpecs) {
262
- if (!matchedExisting.has(key)) {
263
- diffs.push({
264
- operationId: key,
265
- existing: spec,
266
- incoming: undefined,
267
- changes: [
268
- {
269
- path: "",
270
- type: "removed",
271
- oldValue: spec,
272
- description: "Spec no longer exists in OpenAPI source"
273
- }
274
- ],
275
- isEquivalent: false
276
- });
277
- }
278
- }
279
- return diffs;
280
- }
281
- function formatDiffChanges(changes) {
282
- if (changes.length === 0) {
283
- return "No changes detected";
284
- }
285
- const lines = [];
286
- for (const change of changes) {
287
- const prefix = {
288
- added: "+",
289
- removed: "-",
290
- modified: "~",
291
- type_changed: "!",
292
- required_changed: "?"
293
- }[change.type];
294
- lines.push(`${prefix} ${change.path}: ${change.description}`);
295
- if (change.type === "modified" || change.type === "type_changed") {
296
- lines.push(` old: ${JSON.stringify(change.oldValue)}`);
297
- lines.push(` new: ${JSON.stringify(change.newValue)}`);
298
- } else if (change.type === "added") {
299
- lines.push(` value: ${JSON.stringify(change.newValue)}`);
300
- } else if (change.type === "removed") {
301
- lines.push(` was: ${JSON.stringify(change.oldValue)}`);
302
- }
303
- }
304
- return lines.join(`
305
- `);
306
- }
307
- // src/openapi/exporter/data-views.ts
308
- function exportDataViews(registry) {
309
- return registry.list().map((dv) => ({
310
- name: dv.meta.key,
311
- version: dv.meta.version,
312
- description: dv.meta.description,
313
- stability: dv.meta.stability,
314
- entity: dv.meta.entity,
315
- kind: dv.view.kind,
316
- source: dv.source,
317
- fields: dv.view.fields
318
- }));
319
- }
320
- function generateDataViewsRegistry(registry) {
321
- const dataViews = registry.list();
322
- const imports = new Set;
323
- const registrations = [];
324
- for (const dv of dataViews) {
325
- const dvVarName = dv.meta.key.replace(/\./g, "_") + `_v${dv.meta.version}`;
326
- imports.add(`import { ${dvVarName} } from './${dv.meta.key.split(".")[0]}';`);
327
- registrations.push(` .register(${dvVarName})`);
328
- }
329
- const code = `/**
2
+ function P(A){return A.replace(/[-_./\s]+(.)?/g,(Z,Q)=>Q?Q.toUpperCase():"").replace(/^./,(Z)=>Z.toUpperCase())}function mA(A){let Z=P(A);return Z.charAt(0).toLowerCase()+Z.slice(1)}function V(A){return A.replace(/([a-z])([A-Z])/g,"$1-$2").replace(/[\s_./]+/g,"-").toLowerCase()}function YZ(A){return A.replace(/([a-z])([A-Z])/g,"$1_$2").replace(/[\s\-./]+/g,"_").toLowerCase()}function F(A){let Z=A.replace(/[^a-zA-Z0-9_$]/g,"_");if(/^[0-9]/.test(Z))Z="_"+Z;return Z}function T(A,Z){let Q=mA(A);return Z?`${Z}.${Q}`:Q}function O(A){return V(A.replace(/\./g,"-"))+".ts"}function b(A,Z){if(A===Z)return!0;if(A===null||Z===null)return!1;if(typeof A!==typeof Z)return!1;if(typeof A==="object"){let Q=A,B=Z,X=Object.keys(Q),$=Object.keys(B);if(X.length!==$.length)return!1;for(let U of X){if(!$.includes(U))return!1;if(!b(Q[U],B[U]))return!1}return!0}return!1}function DZ(A,Z){let Q=Z.split(".").filter(Boolean),B=A;for(let X of Q){if(B===null||B===void 0)return;if(typeof B!=="object")return;B=B[X]}return B}function EZ(A){return(A.match(/\{([^}]+)\}/g)||[]).map((Q)=>Q.slice(1,-1))}function _Z(A){let Z=A.replace(/^\/+|\/+$/g,"");return Z=Z.replace(/\/+/g,"/"),"/"+Z}function HA(A,Z,Q,B){if(b(Z,Q))return null;let X="modified";if(Z===void 0||Z===null)X="added";else if(Q===void 0||Q===null)X="removed";else if(typeof Z!==typeof Q)X="type_changed";return{path:A,type:X,oldValue:Z,newValue:Q,description:B}}function y(A,Z,Q,B){let X=[];if(!Z&&!Q)return X;if(!Z)return X.push({path:A,type:"added",newValue:Q,description:`Added ${A}`}),X;if(!Q)return X.push({path:A,type:"removed",oldValue:Z,description:`Removed ${A}`}),X;let $=new Set([...Object.keys(Z),...Object.keys(Q)]);for(let U of $){let J=A?`${A}.${U}`:U;if(B.ignorePaths?.some((E)=>J.startsWith(E)))continue;let D=Z[U],W=Q[U];if(typeof D==="object"&&typeof W==="object")X.push(...y(J,D,W,B));else{let E=HA(J,D,W,`Changed ${J}`);if(E)X.push(E)}}return X}function cA(A,Z,Q={}){let B=[];if(!Q.ignoreDescriptions){let $=HA("meta.description",A.meta.description,Z.summary??Z.description,"Description changed");if($)B.push($)}if(!Q.ignoreTags){let $=[...A.meta.tags??[]].sort(),U=[...Z.tags].sort();if(!b($,U))B.push({path:"meta.tags",type:"modified",oldValue:$,newValue:U,description:"Tags changed"})}if(!Q.ignoreTransport){let $=A.transport?.rest?.method??(A.meta.kind==="query"?"GET":"POST"),U=Z.method.toUpperCase();if($!==U)B.push({path:"transport.rest.method",type:"modified",oldValue:$,newValue:U,description:"HTTP method changed"});let J=A.transport?.rest?.path;if(J&&J!==Z.path)B.push({path:"transport.rest.path",type:"modified",oldValue:J,newValue:Z.path,description:"Path changed"})}if(A.meta.stability==="deprecated"!==Z.deprecated)B.push({path:"meta.stability",type:"modified",oldValue:A.meta.stability,newValue:Z.deprecated?"deprecated":"stable",description:"Deprecation status changed"});return B}function IA(A,Z,Q={}){let B=[],X=y("meta",A.meta,Z.meta,{...Q,ignorePaths:[...Q.ignorePaths??[],...Q.ignoreDescriptions?["meta.description","meta.goal","meta.context"]:[],...Q.ignoreTags?["meta.tags"]:[]]});if(B.push(...X),!Q.ignoreTransport){let U=y("transport",A.transport,Z.transport,Q);B.push(...U)}let $=y("policy",A.policy,Z.policy,Q);return B.push(...$),B}function PA(A,Z,Q,B={}){let X=[],$=!1;if(Z&&Q.operationSpec)X=IA(Z,Q.operationSpec,B),$=X.length===0;else if(Z&&!Q.operationSpec)X=[{path:"",type:"modified",oldValue:Z,newValue:Q.code,description:"Spec code imported from OpenAPI (runtime comparison not available)"}];else X=[{path:"",type:"added",newValue:Q.operationSpec??Q.code,description:"New spec imported from OpenAPI"}];return{operationId:A,existing:Z,incoming:Q,changes:X,isEquivalent:$}}function pA(A,Z,Q={}){let B=[],X=new Set;for(let $ of Z){let U=$.source.sourceId,J;for(let[D,W]of A){let E=W.meta.key;if(D===U||E.includes(U)){J=W,X.add(D);break}}B.push(PA(U,J,$,Q))}for(let[$,U]of A)if(!X.has($))B.push({operationId:$,existing:U,incoming:void 0,changes:[{path:"",type:"removed",oldValue:U,description:"Spec no longer exists in OpenAPI source"}],isEquivalent:!1});return B}function iA(A){if(A.length===0)return"No changes detected";let Z=[];for(let Q of A){let B={added:"+",removed:"-",modified:"~",type_changed:"!",required_changed:"?"}[Q.type];if(Z.push(`${B} ${Q.path}: ${Q.description}`),Q.type==="modified"||Q.type==="type_changed")Z.push(` old: ${JSON.stringify(Q.oldValue)}`),Z.push(` new: ${JSON.stringify(Q.newValue)}`);else if(Q.type==="added")Z.push(` value: ${JSON.stringify(Q.newValue)}`);else if(Q.type==="removed")Z.push(` was: ${JSON.stringify(Q.oldValue)}`)}return Z.join(`
3
+ `)}function l(A){return A.list().map((Z)=>({name:Z.meta.key,version:Z.meta.version,description:Z.meta.description,stability:Z.meta.stability,entity:Z.meta.entity,kind:Z.view.kind,source:Z.source,fields:Z.view.fields}))}function m(A){let Z=A.list(),Q=new Set,B=[];for(let $ of Z){let U=$.meta.key.replace(/\./g,"_")+`_v${$.meta.version}`;Q.add(`import { ${U} } from './${$.meta.key.split(".")[0]}';`),B.push(` .register(${U})`)}return{code:`/**
330
4
  * Auto-generated data views registry.
331
5
  * DO NOT EDIT - This file is generated by ContractSpec exporter.
332
6
  */
333
7
  import { DataViewRegistry } from '@contractspec/lib.contracts-spec/data-views';
334
8
 
335
- ${Array.from(imports).join(`
9
+ ${Array.from(Q).join(`
336
10
  `)}
337
11
 
338
12
  export const dataViewsRegistry = new DataViewRegistry()
339
- ${registrations.join(`
13
+ ${B.join(`
340
14
  `)};
341
- `;
342
- return {
343
- code,
344
- fileName: "dataviews-registry.ts"
345
- };
346
- }
347
-
348
- // src/openapi/exporter/events.ts
349
- import { z } from "zod";
350
- function exportEvents(events) {
351
- return events.map((event) => ({
352
- name: event.meta.key,
353
- version: event.meta.version,
354
- description: event.meta.description,
355
- payload: event.payload ? z.toJSONSchema(event.payload.getZod()) : null,
356
- pii: event.pii
357
- }));
358
- }
359
- function generateEventsExports(events) {
360
- const eventExports = [];
361
- for (const event of events) {
362
- const eventVarName = event.meta.key.replace(/\./g, "_") + `_v${event.meta.version}`;
363
- eventExports.push(`export { ${eventVarName} } from './${event.meta.key.split(".")[0]}';`);
364
- }
365
- const code = `/**
15
+ `,fileName:"dataviews-registry.ts"}}import{z as nA}from"zod";function c(A){return A.map((Z)=>({name:Z.meta.key,version:Z.meta.version,description:Z.meta.description,payload:Z.payload?nA.toJSONSchema(Z.payload.getZod()):null,pii:Z.pii}))}function p(A){let Z=[];for(let B of A){let X=B.meta.key.replace(/\./g,"_")+`_v${B.meta.version}`;Z.push(`export { ${X} } from './${B.meta.key.split(".")[0]}';`)}return{code:`/**
366
16
  * Auto-generated events exports.
367
17
  * DO NOT EDIT - This file is generated by ContractSpec exporter.
368
18
  */
369
19
 
370
- ${eventExports.join(`
20
+ ${Z.join(`
371
21
  `)}
372
- `;
373
- return {
374
- code,
375
- fileName: "events-exports.ts"
376
- };
377
- }
378
-
379
- // src/openapi/exporter/features.ts
380
- function exportFeatures(registry) {
381
- return registry.list().map((feature) => ({
382
- key: feature.meta.key,
383
- description: feature.meta.description,
384
- owners: feature.meta.owners,
385
- stability: feature.meta.stability,
386
- operations: feature.operations,
387
- events: feature.events,
388
- presentations: feature.presentations
389
- }));
390
- }
391
- function generateFeaturesRegistry(registry) {
392
- const features = registry.list();
393
- const imports = new Set;
394
- const registrations = [];
395
- for (const feature of features) {
396
- const featureVarName = feature.meta.key.replace(/-/g, "_");
397
- imports.add(`import { ${featureVarName} } from './${feature.meta.key}';`);
398
- registrations.push(` .register(${featureVarName})`);
399
- }
400
- const code = `/**
22
+ `,fileName:"events-exports.ts"}}function i(A){return A.list().map((Z)=>({key:Z.meta.key,description:Z.meta.description,owners:Z.meta.owners,stability:Z.meta.stability,operations:Z.operations,events:Z.events,presentations:Z.presentations}))}function n(A){let Z=A.list(),Q=new Set,B=[];for(let $ of Z){let U=$.meta.key.replace(/-/g,"_");Q.add(`import { ${U} } from './${$.meta.key}';`),B.push(` .register(${U})`)}return{code:`/**
401
23
  * Auto-generated features registry.
402
24
  * DO NOT EDIT - This file is generated by ContractSpec exporter.
403
25
  */
404
26
  import { FeatureRegistry } from '@contractspec/lib.contracts-spec';
405
27
 
406
- ${Array.from(imports).join(`
28
+ ${Array.from(Q).join(`
407
29
  `)}
408
30
 
409
31
  export const featuresRegistry = new FeatureRegistry()
410
- ${registrations.join(`
32
+ ${B.join(`
411
33
  `)};
412
- `;
413
- return {
414
- code,
415
- fileName: "features-registry.ts"
416
- };
417
- }
418
-
419
- // src/openapi/exporter/forms.ts
420
- import { z as z2 } from "zod";
421
- function exportForms(registry) {
422
- return registry.list().map((form) => ({
423
- key: form.meta.key,
424
- version: form.meta.version,
425
- description: form.meta.description,
426
- stability: form.meta.stability,
427
- owners: form.meta.owners,
428
- fields: form.fields,
429
- model: form.model ? z2.toJSONSchema(form.model.getZod()) : null,
430
- actions: form.actions
431
- }));
432
- }
433
- function generateFormsRegistry(registry) {
434
- const forms = registry.list();
435
- const imports = new Set;
436
- const registrations = [];
437
- for (const form of forms) {
438
- const formVarName = form.meta.key.replace(/-/g, "_") + `_v${form.meta.version}`;
439
- imports.add(`import { ${formVarName} } from './${form.meta.key}';`);
440
- registrations.push(` .register(${formVarName})`);
441
- }
442
- const code = `/**
34
+ `,fileName:"features-registry.ts"}}import{z as rA}from"zod";function r(A){return A.list().map((Z)=>({key:Z.meta.key,version:Z.meta.version,description:Z.meta.description,stability:Z.meta.stability,owners:Z.meta.owners,fields:Z.fields,model:Z.model?rA.toJSONSchema(Z.model.getZod()):null,actions:Z.actions}))}function t(A){let Z=A.list(),Q=new Set,B=[];for(let $ of Z){let U=$.meta.key.replace(/-/g,"_")+`_v${$.meta.version}`;Q.add(`import { ${U} } from './${$.meta.key}';`),B.push(` .register(${U})`)}return{code:`/**
443
35
  * Auto-generated forms registry.
444
36
  * DO NOT EDIT - This file is generated by ContractSpec exporter.
445
37
  */
446
38
  import { FormRegistry } from '@contractspec/lib.contracts-spec';
447
39
 
448
- ${Array.from(imports).join(`
40
+ ${Array.from(Q).join(`
449
41
  `)}
450
42
 
451
43
  export const formsRegistry = new FormRegistry()
452
- ${registrations.join(`
44
+ ${B.join(`
453
45
  `)};
454
- `;
455
- return {
456
- code,
457
- fileName: "forms-registry.ts"
458
- };
459
- }
460
-
461
- // src/openapi/exporter/operations.ts
462
- import { compareVersions } from "compare-versions";
463
- import { z as z3 } from "zod";
464
- function toOperationId(name, version) {
465
- return `${name.replace(/\./g, "_")}_v${version.replace(/\./g, "_")}`;
466
- }
467
- function toSchemaName(prefix, name, version) {
468
- return `${prefix}_${toOperationId(name, version)}`;
469
- }
470
- function toHttpMethod(kind, override) {
471
- const method = override ?? (kind === "query" ? "GET" : "POST");
472
- return method.toLowerCase();
473
- }
474
- function defaultRestPath(name, version) {
475
- return `/${name.replace(/\./g, "/")}/v${version}`;
476
- }
477
- function toRestPath(spec) {
478
- const path = spec.transport?.rest?.path ?? defaultRestPath(spec.meta.key, spec.meta.version);
479
- return path.startsWith("/") ? path : `/${path}`;
480
- }
481
- function schemaModelToJsonSchema(schema) {
482
- if (!schema)
483
- return null;
484
- return z3.toJSONSchema(schema.getZod());
485
- }
486
- function jsonSchemaForSpec(spec) {
487
- return {
488
- input: schemaModelToJsonSchema(spec.io.input),
489
- output: schemaModelToJsonSchema(spec.io.output),
490
- meta: {
491
- key: spec.meta.key,
492
- version: spec.meta.version,
493
- kind: spec.meta.kind,
494
- description: spec.meta.description,
495
- tags: spec.meta.tags ?? [],
496
- stability: spec.meta.stability ?? "stable"
497
- }
498
- };
499
- }
500
- function exportOperations(registry) {
501
- const specs = Array.from(registry.list().values()).filter((s) => s.meta.kind === "command" || s.meta.kind === "query").sort((a, b) => {
502
- const byName = a.meta.key.localeCompare(b.meta.key);
503
- return byName !== 0 ? byName : compareVersions(a.meta.version, b.meta.version);
504
- });
505
- const paths = {};
506
- const schemas = {};
507
- for (const spec of specs) {
508
- const schema = jsonSchemaForSpec(spec);
509
- const method = toHttpMethod(spec.meta.kind, spec.transport?.rest?.method);
510
- const path = toRestPath(spec);
511
- const operationId = toOperationId(spec.meta.key, spec.meta.version);
512
- const pathItem = paths[path] ??= {};
513
- const op = {
514
- operationId,
515
- summary: spec.meta.description ?? spec.meta.key,
516
- description: spec.meta.description,
517
- tags: spec.meta.tags ?? [],
518
- "x-contractspec": {
519
- name: spec.meta.key,
520
- version: spec.meta.version,
521
- kind: spec.meta.kind
522
- },
523
- responses: {}
524
- };
525
- if (schema.input) {
526
- const inputName = toSchemaName("Input", spec.meta.key, spec.meta.version);
527
- schemas[inputName] = schema.input;
528
- op["requestBody"] = {
529
- required: true,
530
- content: {
531
- "application/json": {
532
- schema: { $ref: `#/components/schemas/${inputName}` }
533
- }
534
- }
535
- };
536
- }
537
- const responses = {};
538
- if (schema.output) {
539
- const outputName = toSchemaName("Output", spec.meta.key, spec.meta.version);
540
- schemas[outputName] = schema.output;
541
- responses["200"] = {
542
- description: "OK",
543
- content: {
544
- "application/json": {
545
- schema: { $ref: `#/components/schemas/${outputName}` }
546
- }
547
- }
548
- };
549
- } else {
550
- responses["200"] = { description: "OK" };
551
- }
552
- op["responses"] = responses;
553
- pathItem[method] = op;
554
- }
555
- return { paths, schemas };
556
- }
557
- function generateOperationsRegistry(registry) {
558
- const specs = Array.from(registry.list().values());
559
- const imports = new Set;
560
- const registrations = [];
561
- for (const spec of specs) {
562
- const specVarName = spec.meta.key.replace(/\./g, "_") + `_v${spec.meta.version.replace(/\./g, "_")}`;
563
- imports.add(`import { ${specVarName} } from './${spec.meta.key.split(".")[0]}';`);
564
- registrations.push(` .register(${specVarName})`);
565
- }
566
- const code = `/**
46
+ `,fileName:"forms-registry.ts"}}import{compareVersions as tA}from"compare-versions";import{z as aA}from"zod";function s(A,Z){return`${A.replace(/\./g,"_")}_v${Z.replace(/\./g,"_")}`}function a(A,Z,Q){return`${A}_${s(Z,Q)}`}function GA(A,Z){return(Z??(A==="query"?"GET":"POST")).toLowerCase()}function f(A,Z){return`/${A.replace(/\./g,"/")}/v${Z}`}function KA(A){let Z=A.transport?.rest?.path??f(A.meta.key,A.meta.version);return Z.startsWith("/")?Z:`/${Z}`}function o(A){if(!A)return null;return aA.toJSONSchema(A.getZod())}function xA(A){return{input:o(A.io.input),output:o(A.io.output),meta:{key:A.meta.key,version:A.meta.version,kind:A.meta.kind,description:A.meta.description,tags:A.meta.tags??[],stability:A.meta.stability??"stable"}}}function g(A){let Z=Array.from(A.list().values()).filter((X)=>X.meta.kind==="command"||X.meta.kind==="query").sort((X,$)=>{let U=X.meta.key.localeCompare($.meta.key);return U!==0?U:tA(X.meta.version,$.meta.version)}),Q={},B={};for(let X of Z){let $=xA(X),U=GA(X.meta.kind,X.transport?.rest?.method),J=KA(X),D=s(X.meta.key,X.meta.version),W=Q[J]??={},E={operationId:D,summary:X.meta.description??X.meta.key,description:X.meta.description,tags:X.meta.tags??[],"x-contractspec":{name:X.meta.key,version:X.meta.version,kind:X.meta.kind},responses:{}};if($.input){let _=a("Input",X.meta.key,X.meta.version);B[_]=$.input,E.requestBody={required:!0,content:{"application/json":{schema:{$ref:`#/components/schemas/${_}`}}}}}let Y={};if($.output){let _=a("Output",X.meta.key,X.meta.version);B[_]=$.output,Y["200"]={description:"OK",content:{"application/json":{schema:{$ref:`#/components/schemas/${_}`}}}}}else Y["200"]={description:"OK"};E.responses=Y,W[U]=E}return{paths:Q,schemas:B}}function e(A){let Z=Array.from(A.list().values()),Q=new Set,B=[];for(let $ of Z){let U=$.meta.key.replace(/\./g,"_")+`_v${$.meta.version.replace(/\./g,"_")}`;Q.add(`import { ${U} } from './${$.meta.key.split(".")[0]}';`),B.push(` .register(${U})`)}return{code:`/**
567
47
  * Auto-generated operations registry.
568
48
  * DO NOT EDIT - This file is generated by ContractSpec exporter.
569
49
  */
570
50
  import { OperationSpecRegistry } from '@contractspec/lib.contracts-spec';
571
51
 
572
- ${Array.from(imports).join(`
52
+ ${Array.from(Q).join(`
573
53
  `)}
574
54
 
575
55
  export const operationsRegistry = new OperationSpecRegistry()
576
- ${registrations.join(`
56
+ ${B.join(`
577
57
  `)};
578
- `;
579
- return {
580
- code,
581
- fileName: "operations-registry.ts"
582
- };
583
- }
584
-
585
- // src/openapi/exporter/presentations.ts
586
- function exportPresentations(registry) {
587
- return registry.list().map((pres) => ({
588
- name: pres.meta.key,
589
- version: pres.meta.version,
590
- description: pres.meta.description,
591
- stability: pres.meta.stability,
592
- sourceType: pres.source.type,
593
- targets: pres.targets,
594
- tags: pres.meta.tags
595
- }));
596
- }
597
- function exportPresentationsFromArray(descriptors) {
598
- return descriptors.map((desc) => ({
599
- name: desc.meta.key,
600
- version: desc.meta.version,
601
- description: desc.meta.description,
602
- stability: desc.meta.stability,
603
- sourceType: desc.source.type,
604
- targets: desc.targets,
605
- tags: desc.meta.tags
606
- }));
607
- }
608
- function generatePresentationsRegistry(registry) {
609
- const presentations = registry.list();
610
- const imports = new Set;
611
- const registrations = [];
612
- for (const pres of presentations) {
613
- const presVarName = pres.meta.key.replace(/\./g, "_") + `_v${pres.meta.version}`;
614
- imports.add(`import { ${presVarName} } from './${pres.meta.key.split(".")[0]}';`);
615
- registrations.push(` .register(${presVarName})`);
616
- }
617
- const code = `/**
58
+ `,fileName:"operations-registry.ts"}}function AA(A){return A.list().map((Z)=>({name:Z.meta.key,version:Z.meta.version,description:Z.meta.description,stability:Z.meta.stability,sourceType:Z.source.type,targets:Z.targets,tags:Z.meta.tags}))}function oA(A){return A.map((Z)=>({name:Z.meta.key,version:Z.meta.version,description:Z.meta.description,stability:Z.meta.stability,sourceType:Z.source.type,targets:Z.targets,tags:Z.meta.tags}))}function ZA(A){let Z=A.list(),Q=new Set,B=[];for(let $ of Z){let U=$.meta.key.replace(/\./g,"_")+`_v${$.meta.version}`;Q.add(`import { ${U} } from './${$.meta.key.split(".")[0]}';`),B.push(` .register(${U})`)}return{code:`/**
618
59
  * Auto-generated presentations registry.
619
60
  * DO NOT EDIT - This file is generated by ContractSpec exporter.
620
61
  */
621
62
  import { PresentationRegistry } from '@contractspec/lib.contracts-spec';
622
63
 
623
- ${Array.from(imports).join(`
64
+ ${Array.from(Q).join(`
624
65
  `)}
625
66
 
626
67
  export const presentationsRegistry = new PresentationRegistry()
627
- ${registrations.join(`
68
+ ${B.join(`
628
69
  `)};
629
- `;
630
- return {
631
- code,
632
- fileName: "presentations-registry.ts"
633
- };
634
- }
635
-
636
- // src/openapi/exporter/registries.ts
637
- function generateRegistryIndex(options = {}) {
638
- const {
639
- operations = true,
640
- events = true,
641
- features = true,
642
- presentations = true,
643
- forms = true,
644
- dataViews = true,
645
- workflows = true
646
- } = options;
647
- const exports = [];
648
- if (operations) {
649
- exports.push("export * from './operations-registry';");
650
- }
651
- if (events) {
652
- exports.push("export * from './events-exports';");
653
- }
654
- if (features) {
655
- exports.push("export * from './features-registry';");
656
- }
657
- if (presentations) {
658
- exports.push("export * from './presentations-registry';");
659
- }
660
- if (forms) {
661
- exports.push("export * from './forms-registry';");
662
- }
663
- if (dataViews) {
664
- exports.push("export * from './dataviews-registry';");
665
- }
666
- if (workflows) {
667
- exports.push("export * from './workflows-registry';");
668
- }
669
- const code = `/**
70
+ `,fileName:"presentations-registry.ts"}}function BA(A={}){let{operations:Z=!0,events:Q=!0,features:B=!0,presentations:X=!0,forms:$=!0,dataViews:U=!0,workflows:J=!0}=A,D=[];if(Z)D.push("export * from './operations-registry';");if(Q)D.push("export * from './events-exports';");if(B)D.push("export * from './features-registry';");if(X)D.push("export * from './presentations-registry';");if($)D.push("export * from './forms-registry';");if(U)D.push("export * from './dataviews-registry';");if(J)D.push("export * from './workflows-registry';");return{code:`/**
670
71
  * Auto-generated registry index.
671
72
  * DO NOT EDIT - This file is generated by ContractSpec exporter.
672
73
  */
673
74
 
674
- ${exports.join(`
75
+ ${D.join(`
675
76
  `)}
676
- `;
677
- return {
678
- code,
679
- fileName: "index.ts"
680
- };
681
- }
682
-
683
- // src/openapi/exporter/workflows.ts
684
- function exportWorkflows(registry) {
685
- return registry.list().map((wf) => ({
686
- name: wf.meta.key,
687
- version: wf.meta.version,
688
- description: wf.meta.description,
689
- stability: wf.meta.stability,
690
- owners: wf.meta.owners,
691
- steps: wf.definition.steps.map((s) => ({
692
- id: s.id,
693
- type: s.type,
694
- label: s.label
695
- })),
696
- transitions: wf.definition.transitions.map((t) => ({
697
- from: t.from,
698
- to: t.to,
699
- label: t.label
700
- }))
701
- }));
702
- }
703
- function generateWorkflowsRegistry(registry) {
704
- const workflows = registry.list();
705
- const imports = new Set;
706
- const registrations = [];
707
- for (const wf of workflows) {
708
- const wfVarName = wf.meta.key.replace(/\./g, "_") + `_v${wf.meta.version}`;
709
- imports.add(`import { ${wfVarName} } from './${wf.meta.key.split(".")[0]}';`);
710
- registrations.push(` .register(${wfVarName})`);
711
- }
712
- const code = `/**
77
+ `,fileName:"index.ts"}}function QA(A){return A.list().map((Z)=>({name:Z.meta.key,version:Z.meta.version,description:Z.meta.description,stability:Z.meta.stability,owners:Z.meta.owners,steps:Z.definition.steps.map((Q)=>({id:Q.id,type:Q.type,label:Q.label})),transitions:Z.definition.transitions.map((Q)=>({from:Q.from,to:Q.to,label:Q.label}))}))}function XA(A){let Z=A.list(),Q=new Set,B=[];for(let $ of Z){let U=$.meta.key.replace(/\./g,"_")+`_v${$.meta.version}`;Q.add(`import { ${U} } from './${$.meta.key.split(".")[0]}';`),B.push(` .register(${U})`)}return{code:`/**
713
78
  * Auto-generated workflows registry.
714
79
  * DO NOT EDIT - This file is generated by ContractSpec exporter.
715
80
  */
716
81
  import { WorkflowRegistry } from '@contractspec/lib.contracts-spec/workflow';
717
82
 
718
- ${Array.from(imports).join(`
83
+ ${Array.from(Q).join(`
719
84
  `)}
720
85
 
721
86
  export const workflowsRegistry = new WorkflowRegistry()
722
- ${registrations.join(`
87
+ ${B.join(`
723
88
  `)};
724
- `;
725
- return {
726
- code,
727
- fileName: "workflows-registry.ts"
728
- };
729
- }
730
-
731
- // src/openapi/exporter.ts
732
- function openApiForRegistry(registry, options = {}) {
733
- const { paths, schemas } = exportOperations(registry);
734
- return {
735
- openapi: "3.1.0",
736
- info: {
737
- title: options.title ?? "ContractSpec API",
738
- version: options.version ?? "0.0.0",
739
- ...options.description ? { description: options.description } : {}
740
- },
741
- ...options.servers ? { servers: options.servers } : {},
742
- paths,
743
- components: { schemas }
744
- };
745
- }
746
- function exportContractSpec(registries, options = {}) {
747
- const {
748
- operations: includeOps = true,
749
- events: includeEvents = true,
750
- features: includeFeatures = true,
751
- presentations: includePresentations = true,
752
- forms: includeForms = true,
753
- dataViews: includeDataViews = true,
754
- workflows: includeWorkflows = true,
755
- generateRegistries = true
756
- } = options;
757
- let paths = {};
758
- let schemas = {};
759
- if (includeOps && registries.operations) {
760
- const opResult = exportOperations(registries.operations);
761
- paths = opResult.paths;
762
- schemas = opResult.schemas;
763
- }
764
- const doc = {
765
- openapi: "3.1.0",
766
- info: {
767
- title: options.title ?? "ContractSpec API",
768
- version: options.version ?? "0.0.0",
769
- ...options.description ? { description: options.description } : {}
770
- },
771
- ...options.servers ? { servers: options.servers } : {},
772
- paths,
773
- components: { schemas }
774
- };
775
- if (includeEvents && registries.events?.length) {
776
- doc["x-contractspec-events"] = exportEvents(registries.events);
777
- }
778
- if (includeFeatures && registries.features) {
779
- doc["x-contractspec-features"] = exportFeatures(registries.features);
780
- }
781
- if (includePresentations && registries.presentations) {
782
- doc["x-contractspec-presentations"] = exportPresentations(registries.presentations);
783
- }
784
- if (includeForms && registries.forms) {
785
- doc["x-contractspec-forms"] = exportForms(registries.forms);
786
- }
787
- if (includeDataViews && registries.dataViews) {
788
- doc["x-contractspec-dataviews"] = exportDataViews(registries.dataViews);
789
- }
790
- if (includeWorkflows && registries.workflows) {
791
- doc["x-contractspec-workflows"] = exportWorkflows(registries.workflows);
792
- }
793
- const result = {
794
- openApi: doc
795
- };
796
- if (generateRegistries) {
797
- result.registries = {};
798
- if (includeOps && registries.operations) {
799
- result.registries.operations = generateOperationsRegistry(registries.operations);
800
- }
801
- if (includeEvents && registries.events?.length) {
802
- result.registries.events = generateEventsExports(registries.events);
803
- }
804
- if (includeFeatures && registries.features) {
805
- result.registries.features = generateFeaturesRegistry(registries.features);
806
- }
807
- if (includePresentations && registries.presentations) {
808
- result.registries.presentations = generatePresentationsRegistry(registries.presentations);
809
- }
810
- if (includeForms && registries.forms) {
811
- result.registries.forms = generateFormsRegistry(registries.forms);
812
- }
813
- if (includeDataViews && registries.dataViews) {
814
- result.registries.dataViews = generateDataViewsRegistry(registries.dataViews);
815
- }
816
- if (includeWorkflows && registries.workflows) {
817
- result.registries.workflows = generateWorkflowsRegistry(registries.workflows);
818
- }
819
- result.registries.index = generateRegistryIndex({
820
- operations: includeOps && !!registries.operations,
821
- events: includeEvents && !!registries.events?.length,
822
- features: includeFeatures && !!registries.features,
823
- presentations: includePresentations && !!registries.presentations,
824
- forms: includeForms && !!registries.forms,
825
- dataViews: includeDataViews && !!registries.dataViews,
826
- workflows: includeWorkflows && !!registries.workflows
827
- });
828
- }
829
- return result;
830
- }
831
- function openApiToJson(registry, options = {}) {
832
- const doc = openApiForRegistry(registry, options);
833
- return JSON.stringify(doc, null, 2);
834
- }
835
- function openApiToYaml(registry, options = {}) {
836
- const doc = openApiForRegistry(registry, options);
837
- return jsonToYaml(doc);
838
- }
839
- function contractSpecToJson(registries, options = {}) {
840
- const result = exportContractSpec(registries, options);
841
- return JSON.stringify(result.openApi, null, 2);
842
- }
843
- function contractSpecToYaml(registries, options = {}) {
844
- const result = exportContractSpec(registries, options);
845
- return jsonToYaml(result.openApi);
846
- }
847
- function jsonToYaml(obj, indent = 0) {
848
- const spaces = " ".repeat(indent);
849
- let yaml = "";
850
- if (Array.isArray(obj)) {
851
- for (const item of obj) {
852
- if (typeof item === "object" && item !== null) {
853
- yaml += `${spaces}-
854
- ${jsonToYaml(item, indent + 1)}`;
855
- } else {
856
- yaml += `${spaces}- ${JSON.stringify(item)}
857
- `;
858
- }
859
- }
860
- } else if (typeof obj === "object" && obj !== null) {
861
- for (const [key, value] of Object.entries(obj)) {
862
- if (Array.isArray(value)) {
863
- yaml += `${spaces}${key}:
864
- ${jsonToYaml(value, indent + 1)}`;
865
- } else if (typeof value === "object" && value !== null) {
866
- yaml += `${spaces}${key}:
867
- ${jsonToYaml(value, indent + 1)}`;
868
- } else {
869
- yaml += `${spaces}${key}: ${JSON.stringify(value)}
870
- `;
871
- }
872
- }
873
- }
874
- return yaml;
875
- }
876
- // src/openapi/schema-generators/index.ts
877
- var JSON_SCHEMA_TO_SCALAR = {
878
- string: "ScalarTypeEnum.String_unsecure",
879
- integer: "ScalarTypeEnum.Int_unsecure",
880
- number: "ScalarTypeEnum.Float_unsecure",
881
- boolean: "ScalarTypeEnum.Boolean",
882
- "string:date": "ScalarTypeEnum.Date",
883
- "string:date-time": "ScalarTypeEnum.DateTime",
884
- "string:email": "ScalarTypeEnum.EmailAddress",
885
- "string:uri": "ScalarTypeEnum.URL",
886
- "string:uuid": "ScalarTypeEnum.ID"
887
- };
888
- function isReference(schema) {
889
- return typeof schema === "object" && schema !== null && "$ref" in schema;
890
- }
891
- function typeNameFromRef(ref) {
892
- const parts = ref.split("/");
893
- return parts[parts.length - 1] ?? "Unknown";
894
- }
895
- function createSchemaGenerator(format, config) {
896
- switch (format) {
897
- case "zod":
898
- return new ZodSchemaGenerator(config);
899
- case "json-schema":
900
- return new JsonSchemaGenerator(config);
901
- case "graphql":
902
- return new GraphQLSchemaGenerator(config);
903
- case "contractspec":
904
- default:
905
- return new ContractSpecSchemaGenerator(config);
906
- }
907
- }
908
-
909
- class ContractSpecSchemaGenerator {
910
- format = "contractspec";
911
- config;
912
- constructor(config) {
913
- this.config = config;
914
- }
915
- generateModel(schema, name) {
916
- const model = this.generateContractSpecSchema(schema, name);
917
- const dependencyImports = this.config ? generateImports(model.fields, this.config, false).split(`
918
- `).filter(Boolean) : [];
919
- return {
920
- code: model.code,
921
- fileName: toKebabCase(name) + ".ts",
922
- imports: [...this.getBaseImports(), ...dependencyImports],
923
- name: model.name
924
- };
925
- }
926
- generateField(schema, fieldName, required) {
927
- const field = this.convertField(schema, fieldName, required);
928
- return {
929
- code: field.scalarType ? `${field.scalarType}()` : "ScalarTypeEnum.String_unsecure()",
930
- typeRef: field.type.type,
931
- isOptional: field.type.optional,
932
- isArray: field.type.array
933
- };
934
- }
935
- getBaseImports() {
936
- return [
937
- "import { defineSchemaModel, ScalarTypeEnum, EnumType } from '@contractspec/lib.schema';"
938
- ];
939
- }
940
- generateContractSpecSchema(schema, modelName, indent = 0) {
941
- const spaces = " ".repeat(indent);
942
- const fields = [];
943
- if (isReference(schema)) {
944
- return {
945
- name: toPascalCase(typeNameFromRef(schema.$ref)),
946
- fields: [],
947
- code: `// Reference to ${schema.$ref}`
948
- };
949
- }
950
- const schemaObj = schema;
951
- const description = schemaObj["description"];
952
- const properties = schemaObj["properties"];
953
- const required = schemaObj["required"] ?? [];
954
- const enumValues = schemaObj["enum"];
955
- if (enumValues && enumValues.length > 0) {
956
- const safeModelName2 = toPascalCase(toValidIdentifier(modelName));
957
- const enumCode = [
958
- `${spaces}/**`,
959
- `${spaces} * Enum type: ${safeModelName2}`,
960
- description ? `${spaces} * ${description}` : null,
961
- `${spaces} */`,
962
- `${spaces}export const ${safeModelName2} = new EnumType('${safeModelName2}', [${enumValues.map((v) => `'${String(v)}'`).join(", ")}]);`
963
- ].filter((line) => line !== null).join(`
964
- `);
965
- return {
966
- name: safeModelName2,
967
- description,
968
- fields: [],
969
- code: enumCode
970
- };
971
- }
972
- const schemaType = schemaObj["type"];
973
- if (schemaType && !properties && !enumValues) {
974
- const safeModelName2 = toPascalCase(toValidIdentifier(modelName));
975
- const format = schemaObj["format"];
976
- const scalarKey = format ? `${schemaType}:${format}` : schemaType;
977
- const scalarType = JSON_SCHEMA_TO_SCALAR[scalarKey] ?? JSON_SCHEMA_TO_SCALAR[schemaType];
978
- if (scalarType) {
979
- const aliasCode = [
980
- `${spaces}/**`,
981
- `${spaces} * Type alias: ${safeModelName2}`,
982
- description ? `${spaces} * ${description}` : null,
983
- `${spaces} * Underlying type: ${scalarType}`,
984
- `${spaces} */`,
985
- `${spaces}export const ${safeModelName2} = defineSchemaModel({`,
986
- `${spaces} name: '${safeModelName2}',`,
987
- description ? `${spaces} description: ${JSON.stringify(description)},` : null,
988
- `${spaces} fields: {`,
989
- `${spaces} value: {`,
990
- `${spaces} type: ${scalarType}(),`,
991
- `${spaces} isOptional: false,`,
992
- `${spaces} },`,
993
- `${spaces} },`,
994
- `${spaces}});`
995
- ].filter((line) => line !== null).join(`
996
- `);
997
- return {
998
- name: safeModelName2,
999
- description,
1000
- fields: [],
1001
- code: aliasCode
1002
- };
1003
- }
1004
- }
1005
- const additionalProperties = schemaObj["additionalProperties"];
1006
- if (additionalProperties && !properties) {
1007
- const safeModelName2 = toPascalCase(toValidIdentifier(modelName));
1008
- const dictCode = [
1009
- `${spaces}/**`,
1010
- `${spaces} * Dictionary/Record type: ${safeModelName2}`,
1011
- description ? `${spaces} * ${description}` : null,
1012
- `${spaces} * Use as: Record<string, unknown> - access via record[key]`,
1013
- `${spaces} */`,
1014
- `${spaces}export const ${safeModelName2} = ScalarTypeEnum.JSONObject();`
1015
- ].filter((line) => line !== null).join(`
1016
- `);
1017
- return {
1018
- name: safeModelName2,
1019
- description,
1020
- fields: [],
1021
- code: dictCode
1022
- };
1023
- }
1024
- if (!properties) {
1025
- const safeModelName2 = toPascalCase(toValidIdentifier(modelName));
1026
- const emptyModelCode = [
1027
- `${spaces}export const ${safeModelName2} = defineSchemaModel({`,
1028
- `${spaces} name: '${safeModelName2}',`,
1029
- description ? `${spaces} description: ${JSON.stringify(description)},` : null,
1030
- `${spaces} fields: {},`,
1031
- `${spaces}});`
1032
- ].filter((line) => line !== null).join(`
1033
- `);
1034
- return {
1035
- name: safeModelName2,
1036
- description,
1037
- fields: [],
1038
- code: emptyModelCode
1039
- };
1040
- }
1041
- const safeModelName = toPascalCase(toValidIdentifier(modelName));
1042
- for (const [propName, propSchema] of Object.entries(properties)) {
1043
- const isRequired = required.includes(propName);
1044
- fields.push(this.convertField(propSchema, propName, isRequired, safeModelName));
1045
- }
1046
- const lines = [];
1047
- for (const field of fields) {
1048
- if (field.nestedModel) {
1049
- lines.push(field.nestedModel.code);
1050
- lines.push("");
1051
- }
1052
- }
1053
- lines.push(`${spaces}export const ${safeModelName} = defineSchemaModel({`);
1054
- lines.push(`${spaces} name: '${safeModelName}',`);
1055
- if (description) {
1056
- lines.push(`${spaces} description: ${JSON.stringify(description)},`);
1057
- }
1058
- lines.push(`${spaces} fields: {`);
1059
- for (const field of fields) {
1060
- const fieldLines = this.generateFieldCodeHelper(field, indent + 2);
1061
- lines.push(fieldLines);
1062
- }
1063
- lines.push(`${spaces} },`);
1064
- lines.push(`${spaces}});`);
1065
- return {
1066
- name: safeModelName,
1067
- description,
1068
- fields,
1069
- code: lines.join(`
1070
- `),
1071
- imports: []
1072
- };
1073
- }
1074
- convertField(schema, fieldName, required, parentName) {
1075
- const type = jsonSchemaToType(schema, fieldName);
1076
- const scalarType = getScalarType(schema);
1077
- let enumValues;
1078
- let nestedModel;
1079
- if (!isReference(schema)) {
1080
- const schemaObj = schema;
1081
- const enumArr = schemaObj["enum"];
1082
- if (enumArr) {
1083
- enumValues = enumArr.map(String);
1084
- }
1085
- if (schemaObj["type"] === "object" && !scalarType && schemaObj["properties"] && !enumValues) {
1086
- const nestedName = (parentName ? parentName : "") + toPascalCase(fieldName);
1087
- nestedModel = this.generateContractSpecSchema(schema, nestedName);
1088
- type.type = nestedModel.name;
1089
- type.isReference = true;
1090
- }
1091
- }
1092
- return {
1093
- name: fieldName,
1094
- type: {
1095
- ...type,
1096
- optional: !required || type.optional,
1097
- description: !isReference(schema) ? schema["description"] : undefined
1098
- },
1099
- scalarType,
1100
- enumValues,
1101
- nestedModel
1102
- };
1103
- }
1104
- generateFieldCodeHelper(field, indent) {
1105
- const spaces = " ".repeat(indent);
1106
- const lines = [];
1107
- const isIdentifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(field.name);
1108
- const safeKey = isIdentifier ? field.name : `'${field.name}'`;
1109
- lines.push(`${spaces}${safeKey}: {`);
1110
- if (field.enumValues) {
1111
- const enumName = toPascalCase(field.name) + "Enum";
1112
- lines.push(`${spaces} type: new EnumType('${enumName}', [${field.enumValues.map((v) => `'${v}'`).join(", ")}]),`);
1113
- } else if (field.scalarType) {
1114
- lines.push(`${spaces} type: ${field.scalarType}(),`);
1115
- } else if (field.nestedModel) {
1116
- lines.push(`${spaces} type: ${field.nestedModel.name},`);
1117
- } else if (field.type.primitive) {
1118
- const fallbackScalar = field.type.type === "number" ? "ScalarTypeEnum.Float_unsecure" : field.type.type === "boolean" ? "ScalarTypeEnum.Boolean_unsecure" : "ScalarTypeEnum.String_unsecure";
1119
- lines.push(`${spaces} type: ${fallbackScalar}(),`);
1120
- } else if (field.type.isReference) {
1121
- lines.push(`${spaces} type: ${field.type.type},`);
1122
- } else {
1123
- lines.push(`${spaces} type: ScalarTypeEnum.JSONObject(), // TODO: Define nested model for ${field.type.type}`);
1124
- }
1125
- lines.push(`${spaces} isOptional: ${field.type.optional},`);
1126
- if (field.type.array) {
1127
- lines.push(`${spaces} isArray: true,`);
1128
- }
1129
- lines.push(`${spaces}},`);
1130
- return lines.join(`
1131
- `);
1132
- }
1133
- }
1134
-
1135
- class ZodSchemaGenerator {
1136
- format = "zod";
1137
- config;
1138
- constructor(config) {
1139
- this.config = config;
1140
- }
1141
- generateModel(schema, name, options) {
1142
- const schemaObj = schema;
1143
- const properties = schemaObj["properties"];
1144
- const _required = schemaObj["required"] ?? [];
1145
- const description = options?.description ?? schemaObj["description"];
1146
- const lines = [];
1147
- if (description) {
1148
- lines.push(`/**`);
1149
- lines.push(` * ${description}`);
1150
- lines.push(` */`);
1151
- }
1152
- const schemaName = `${name}Schema`;
1153
- let schemaCode;
1154
- if (properties) {
1155
- schemaCode = this.generateZodObject(schemaObj);
1156
- } else {
1157
- schemaCode = "z.object({})";
1158
- }
1159
- lines.push(`export const ${schemaName} = ${schemaCode};`);
1160
- lines.push(``);
1161
- lines.push(`export const ${name} = new ZodSchemaType(${schemaName}, { name: '${name}', description: ${JSON.stringify(description)} });`);
1162
- lines.push(``);
1163
- lines.push(`export type ${name} = z.infer<typeof ${schemaName}>;`);
1164
- return {
1165
- code: lines.join(`
1166
- `),
1167
- fileName: toKebabCase(name) + ".ts",
1168
- imports: this.getBaseImports(),
1169
- name
1170
- };
1171
- }
1172
- generateField(schema, _fieldName, required) {
1173
- const schemaObj = schema;
1174
- const type = schemaObj["type"];
1175
- const format = schemaObj["format"];
1176
- const nullable = schemaObj["nullable"];
1177
- let zodType;
1178
- if (type === "object" && schemaObj["properties"]) {
1179
- zodType = this.generateZodObject(schemaObj);
1180
- } else {
1181
- zodType = this.mapTypeToZod(type, format);
1182
- if (schemaObj["enum"]) {
1183
- const enumValues = schemaObj["enum"];
1184
- zodType = `z.enum([${enumValues.map((v) => `'${v}'`).join(", ")}])`;
1185
- }
1186
- if (type === "array") {
1187
- const items = schemaObj["items"];
1188
- if (items) {
1189
- const itemField = this.generateField(items, "item", true);
1190
- zodType = `z.array(${itemField.code.replace(".optional()", "")})`;
1191
- } else {
1192
- zodType = "z.array(z.unknown())";
1193
- }
1194
- }
1195
- }
1196
- if (type === "string") {
1197
- if (schemaObj["minLength"] !== undefined)
1198
- zodType += `.min(${schemaObj["minLength"]})`;
1199
- if (schemaObj["maxLength"] !== undefined)
1200
- zodType += `.max(${schemaObj["maxLength"]})`;
1201
- if (schemaObj["pattern"] !== undefined)
1202
- zodType += `.regex(/${schemaObj["pattern"]}/)`;
1203
- } else if (type === "integer" || type === "number") {
1204
- if (schemaObj["minimum"] !== undefined) {
1205
- zodType += schemaObj["exclusiveMinimum"] === true ? `.gt(${schemaObj["minimum"]})` : `.min(${schemaObj["minimum"]})`;
1206
- } else if (typeof schemaObj["exclusiveMinimum"] === "number") {
1207
- zodType += `.gt(${schemaObj["exclusiveMinimum"]})`;
1208
- }
1209
- if (schemaObj["maximum"] !== undefined) {
1210
- zodType += schemaObj["exclusiveMaximum"] === true ? `.lt(${schemaObj["maximum"]})` : `.max(${schemaObj["maximum"]})`;
1211
- } else if (typeof schemaObj["exclusiveMaximum"] === "number") {
1212
- zodType += `.lt(${schemaObj["exclusiveMaximum"]})`;
1213
- }
1214
- if (schemaObj["multipleOf"] !== undefined) {
1215
- zodType += `.step(${schemaObj["multipleOf"]})`;
1216
- }
1217
- } else if (type === "array") {
1218
- if (schemaObj["minItems"] !== undefined)
1219
- zodType += `.min(${schemaObj["minItems"]})`;
1220
- if (schemaObj["maxItems"] !== undefined)
1221
- zodType += `.max(${schemaObj["maxItems"]})`;
1222
- }
1223
- if (schemaObj["default"] !== undefined) {
1224
- zodType += `.default(${JSON.stringify(schemaObj["default"])})`;
1225
- }
1226
- if (!required || nullable) {
1227
- zodType = `${zodType}.optional()`;
1228
- }
1229
- return {
1230
- code: zodType,
1231
- typeRef: type ?? "unknown",
1232
- isOptional: !required || Boolean(nullable),
1233
- isArray: type === "array"
1234
- };
1235
- }
1236
- getBaseImports() {
1237
- return [
1238
- "import * as z from 'zod';",
1239
- "import { ZodSchemaType } from '@contractspec/lib.schema';"
1240
- ];
1241
- }
1242
- generateZodObject(schemaObj) {
1243
- const required = schemaObj["required"] ?? [];
1244
- const properties = schemaObj["properties"];
1245
- const lines = ["z.object({"];
1246
- for (const [propName, propSchema] of Object.entries(properties)) {
1247
- const isRequired = required.includes(propName);
1248
- const field = this.generateField(propSchema, propName, isRequired);
1249
- const safeName = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(propName) ? propName : `'${propName}'`;
1250
- lines.push(` ${safeName}: ${field.code},`);
1251
- }
1252
- lines.push("})");
1253
- return lines.join(`
1254
- `);
1255
- }
1256
- mapTypeToZod(type, format) {
1257
- if (format === "date-time")
1258
- return "z.string().datetime()";
1259
- if (format === "date")
1260
- return "z.string().date()";
1261
- if (format === "email")
1262
- return "z.string().email()";
1263
- if (format === "uri" || format === "url")
1264
- return "z.string().url()";
1265
- if (format === "uuid")
1266
- return "z.string().uuid()";
1267
- switch (type) {
1268
- case "string":
1269
- return "z.string()";
1270
- case "integer":
1271
- return "z.number().int()";
1272
- case "number":
1273
- return "z.number()";
1274
- case "boolean":
1275
- return "z.boolean()";
1276
- case "object":
1277
- return "z.record(z.string(), z.unknown())";
1278
- case "null":
1279
- return "z.null()";
1280
- default:
1281
- return "z.unknown()";
1282
- }
1283
- }
1284
- }
1285
-
1286
- class JsonSchemaGenerator {
1287
- format = "json-schema";
1288
- config;
1289
- constructor(config) {
1290
- this.config = config;
1291
- }
1292
- generateModel(schema, name, options) {
1293
- const schemaObj = schema;
1294
- const description = options?.description ?? schemaObj["description"];
1295
- const jsonSchema = {
1296
- $schema: "https://json-schema.org/draft/2020-12/schema",
1297
- title: name,
1298
- ...schemaObj
1299
- };
1300
- if (description) {
1301
- jsonSchema["description"] = description;
1302
- }
1303
- const lines = [];
1304
- lines.push(`/**`);
1305
- lines.push(` * JSON Schema: ${name}`);
1306
- if (description) {
1307
- lines.push(` * ${description}`);
1308
- }
1309
- lines.push(` */`);
1310
- const schemaName = `${name}Schema`;
1311
- lines.push(`export const ${schemaName} = ${JSON.stringify(jsonSchema, null, 2)} as const;`);
1312
- lines.push(``);
1313
- lines.push(`export const ${name} = new JsonSchemaType(${schemaName});`);
1314
- lines.push(``);
1315
- lines.push(`export type ${name} = unknown; // JSON Schema type inference not fully supported`);
1316
- return {
1317
- code: lines.join(`
1318
- `),
1319
- fileName: toKebabCase(name) + ".ts",
1320
- imports: this.getBaseImports(),
1321
- name
1322
- };
1323
- }
1324
- generateField(schema, _fieldName, required) {
1325
- const schemaObj = schema;
1326
- const type = schemaObj["type"];
1327
- const nullable = schemaObj["nullable"];
1328
- return {
1329
- code: JSON.stringify(schemaObj),
1330
- typeRef: type ?? "unknown",
1331
- isOptional: !required || Boolean(nullable),
1332
- isArray: type === "array"
1333
- };
1334
- }
1335
- getBaseImports() {
1336
- return ["import { JsonSchemaType } from '@contractspec/lib.schema';"];
1337
- }
1338
- }
1339
-
1340
- class GraphQLSchemaGenerator {
1341
- format = "graphql";
1342
- config;
1343
- constructor(config) {
1344
- this.config = config;
1345
- }
1346
- generateModel(schema, name, options) {
1347
- const schemaObj = schema;
1348
- const properties = schemaObj["properties"];
1349
- const required = schemaObj["required"] ?? [];
1350
- const description = options?.description ?? schemaObj["description"];
1351
- const lines = [];
1352
- if (description) {
1353
- lines.push(`"""${description}"""`);
1354
- }
1355
- lines.push(`type ${name} {`);
1356
- if (properties) {
1357
- for (const [propName, propSchema] of Object.entries(properties)) {
1358
- const isRequired = required.includes(propName);
1359
- const field = this.generateField(propSchema, propName, isRequired);
1360
- const nullMarker = isRequired ? "!" : "";
1361
- lines.push(` ${propName}: ${field.typeRef}${nullMarker}`);
1362
- }
1363
- }
1364
- lines.push(`}`);
1365
- const sdl = lines.join(`
1366
- `);
1367
- const tsLines = [];
1368
- tsLines.push(`/**`);
1369
- tsLines.push(` * GraphQL type definition: ${name}`);
1370
- tsLines.push(` */`);
1371
- tsLines.push(`export const ${name}TypeDef = \`${sdl}\`;`);
1372
- tsLines.push(``);
1373
- tsLines.push(`export const ${name} = new GraphQLSchemaType(${name}TypeDef, '${name}');`);
1374
- return {
1375
- code: tsLines.join(`
1376
- `),
1377
- fileName: toKebabCase(name) + ".ts",
1378
- imports: this.getBaseImports(),
1379
- name
1380
- };
1381
- }
1382
- generateField(schema, _fieldName, required) {
1383
- const schemaObj = schema;
1384
- const type = schemaObj["type"];
1385
- const format = schemaObj["format"];
1386
- const nullable = schemaObj["nullable"];
1387
- const gqlType = this.mapTypeToGraphQL(type, format);
1388
- return {
1389
- code: gqlType,
1390
- typeRef: gqlType,
1391
- isOptional: !required || Boolean(nullable),
1392
- isArray: type === "array"
1393
- };
1394
- }
1395
- getBaseImports() {
1396
- return ["import { GraphQLSchemaType } from '@contractspec/lib.schema';"];
1397
- }
1398
- mapTypeToGraphQL(type, format) {
1399
- if (format === "date-time")
1400
- return "DateTime";
1401
- if (format === "date")
1402
- return "Date";
1403
- if (format === "email")
1404
- return "String";
1405
- if (format === "uri" || format === "url")
1406
- return "String";
1407
- if (format === "uuid")
1408
- return "ID";
1409
- switch (type) {
1410
- case "string":
1411
- return "String";
1412
- case "integer":
1413
- return "Int";
1414
- case "number":
1415
- return "Float";
1416
- case "boolean":
1417
- return "Boolean";
1418
- case "object":
1419
- return "JSON";
1420
- case "array":
1421
- return "[JSON]";
1422
- default:
1423
- return "JSON";
1424
- }
1425
- }
1426
- }
1427
-
1428
- // src/openapi/schema-converter.ts
1429
- var JSON_SCHEMA_TO_SCALAR2 = {
1430
- string: "ScalarTypeEnum.String_unsecure",
1431
- integer: "ScalarTypeEnum.Int_unsecure",
1432
- number: "ScalarTypeEnum.Float_unsecure",
1433
- boolean: "ScalarTypeEnum.Boolean",
1434
- "string:date": "ScalarTypeEnum.Date",
1435
- "string:date-time": "ScalarTypeEnum.DateTime",
1436
- "string:email": "ScalarTypeEnum.EmailAddress",
1437
- "string:uri": "ScalarTypeEnum.URL",
1438
- "string:uuid": "ScalarTypeEnum.ID"
1439
- };
1440
- function isReference2(schema) {
1441
- return typeof schema === "object" && schema !== null && "$ref" in schema;
1442
- }
1443
- function typeNameFromRef2(ref) {
1444
- const parts = ref.split("/");
1445
- return parts[parts.length - 1] ?? "Unknown";
1446
- }
1447
- function jsonSchemaToType(schema, name) {
1448
- if (isReference2(schema)) {
1449
- return {
1450
- type: toPascalCase(typeNameFromRef2(schema.$ref)),
1451
- optional: false,
1452
- array: false,
1453
- primitive: false,
1454
- isReference: true
1455
- };
1456
- }
1457
- const schemaObj = schema;
1458
- const type = schemaObj["type"];
1459
- const format = schemaObj["format"];
1460
- const nullable = schemaObj["nullable"];
1461
- const originalTypeName = schemaObj["_originalTypeName"];
1462
- if (originalTypeName) {
1463
- return {
1464
- type: toPascalCase(originalTypeName),
1465
- optional: nullable ?? false,
1466
- array: false,
1467
- primitive: false,
1468
- isReference: true
1469
- };
1470
- }
1471
- if (type === "array") {
1472
- const items = schemaObj["items"];
1473
- if (items) {
1474
- const itemType = jsonSchemaToType(items, name);
1475
- return {
1476
- ...itemType,
1477
- array: true,
1478
- optional: nullable ?? false
1479
- };
1480
- }
1481
- return {
1482
- type: "unknown",
1483
- optional: nullable ?? false,
1484
- array: true,
1485
- primitive: false,
1486
- isReference: true
1487
- };
1488
- }
1489
- if (type === "object" || schemaObj["properties"]) {
1490
- return {
1491
- type: name ? toPascalCase(name) : "Record<string, unknown>",
1492
- optional: nullable ?? false,
1493
- array: false,
1494
- primitive: false,
1495
- isReference: true
1496
- };
1497
- }
1498
- if (schemaObj["enum"]) {
1499
- return {
1500
- type: name ? toPascalCase(name) : "string",
1501
- optional: nullable ?? false,
1502
- array: false,
1503
- primitive: false,
1504
- isReference: true
1505
- };
1506
- }
1507
- const scalarKey = format ? `${type}:${format}` : type;
1508
- if (scalarKey === "string") {
1509
- return {
1510
- type: "string",
1511
- optional: nullable ?? false,
1512
- array: false,
1513
- primitive: true
1514
- };
1515
- }
1516
- if (scalarKey === "integer" || type === "number") {
1517
- return {
1518
- type: "number",
1519
- optional: nullable ?? false,
1520
- array: false,
1521
- primitive: true
1522
- };
1523
- }
1524
- if (scalarKey === "boolean") {
1525
- return {
1526
- type: "boolean",
1527
- optional: nullable ?? false,
1528
- array: false,
1529
- primitive: true
1530
- };
1531
- }
1532
- return {
1533
- type: "unknown",
1534
- optional: nullable ?? false,
1535
- array: false,
1536
- primitive: false,
1537
- isReference: true
1538
- };
1539
- }
1540
- function getScalarType(schema) {
1541
- if (isReference2(schema)) {
1542
- return;
1543
- }
1544
- const schemaObj = schema;
1545
- const type = schemaObj["type"];
1546
- const format = schemaObj["format"];
1547
- if (!type)
1548
- return;
1549
- if (type === "array") {
1550
- const items = schemaObj["items"];
1551
- if (items) {
1552
- return getScalarType(items);
1553
- }
1554
- return;
1555
- }
1556
- const key = format ? `${type}:${format}` : type;
1557
- return JSON_SCHEMA_TO_SCALAR2[key] ?? JSON_SCHEMA_TO_SCALAR2[type] ?? undefined;
1558
- }
1559
- function generateSchemaModelCode(schema, modelName, schemaFormat = "contractspec", config) {
1560
- const generator = createSchemaGenerator(schemaFormat, config);
1561
- const result = generator.generateModel(schema, modelName, {
1562
- description: schema["description"]
1563
- });
1564
- return {
1565
- name: result.name,
1566
- description: schema["description"],
1567
- fields: [],
1568
- code: result.code,
1569
- imports: result.imports
1570
- };
1571
- }
1572
- function generateImports(fields, options, sameDirectory = true) {
1573
- const imports = new Set;
1574
- const modelsDir = sameDirectory ? "." : `../${options.conventions.models}`;
1575
- imports.add("import { defineSchemaModel, ScalarTypeEnum, EnumType } from '@contractspec/lib.schema';");
1576
- for (const field of fields) {
1577
- if (field.type.isReference && !field.type.primitive && !field.enumValues && !field.scalarType && !field.nestedModel) {
1578
- const modelName = field.type.type;
1579
- const kebabName = modelName.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
1580
- imports.add(`import { ${modelName} } from '${modelsDir}/${kebabName}';`);
1581
- }
1582
- }
1583
- return Array.from(imports).join(`
1584
- `);
1585
- }
1586
-
1587
- // src/openapi/importer/events.ts
1588
- function generateEventCode(event, options) {
1589
- const eventName = toValidIdentifier(event.name);
1590
- const modelName = toPascalCase(eventName) + "Payload";
1591
- const schemaFormat = options.schemaFormat || "contractspec";
1592
- const payloadModel = generateSchemaModelCode(event.payload, modelName, schemaFormat, options);
1593
- const imports = new Set;
1594
- imports.add("import { defineEvent, type EventSpec } from '@contractspec/lib.contracts-spec';");
1595
- if (payloadModel.imports && payloadModel.imports.length > 0) {
1596
- payloadModel.imports.forEach((i) => imports.add(i));
1597
- } else if (payloadModel.fields && payloadModel.fields.length > 0) {
1598
- const modelImports = generateImports(payloadModel.fields, options);
1599
- modelImports.split(`
1600
- `).filter(Boolean).forEach((i) => imports.add(i));
1601
- }
1602
- if (payloadModel.name !== modelName) {
1603
- const modelsDir = `../${options.conventions.models}`;
1604
- const kebabName = payloadModel.name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
1605
- imports.add(`import { ${payloadModel.name} } from '${modelsDir}/${kebabName}';`);
1606
- }
1607
- const allImports = Array.from(imports).join(`
1608
- `);
1609
- return `
1610
- ${allImports}
89
+ `,fileName:"workflows-registry.ts"}}function $A(A,Z={}){let{paths:Q,schemas:B}=g(A);return{openapi:"3.1.0",info:{title:Z.title??"ContractSpec API",version:Z.version??"0.0.0",...Z.description?{description:Z.description}:{}},...Z.servers?{servers:Z.servers}:{},paths:Q,components:{schemas:B}}}function UA(A,Z={}){let{operations:Q=!0,events:B=!0,features:X=!0,presentations:$=!0,forms:U=!0,dataViews:J=!0,workflows:D=!0,generateRegistries:W=!0}=Z,E={},Y={};if(Q&&A.operations){let L=g(A.operations);E=L.paths,Y=L.schemas}let _={openapi:"3.1.0",info:{title:Z.title??"ContractSpec API",version:Z.version??"0.0.0",...Z.description?{description:Z.description}:{}},...Z.servers?{servers:Z.servers}:{},paths:E,components:{schemas:Y}};if(B&&A.events?.length)_["x-contractspec-events"]=c(A.events);if(X&&A.features)_["x-contractspec-features"]=i(A.features);if($&&A.presentations)_["x-contractspec-presentations"]=AA(A.presentations);if(U&&A.forms)_["x-contractspec-forms"]=r(A.forms);if(J&&A.dataViews)_["x-contractspec-dataviews"]=l(A.dataViews);if(D&&A.workflows)_["x-contractspec-workflows"]=QA(A.workflows);let H={openApi:_};if(W){if(H.registries={},Q&&A.operations)H.registries.operations=e(A.operations);if(B&&A.events?.length)H.registries.events=p(A.events);if(X&&A.features)H.registries.features=n(A.features);if($&&A.presentations)H.registries.presentations=ZA(A.presentations);if(U&&A.forms)H.registries.forms=t(A.forms);if(J&&A.dataViews)H.registries.dataViews=m(A.dataViews);if(D&&A.workflows)H.registries.workflows=XA(A.workflows);H.registries.index=BA({operations:Q&&!!A.operations,events:B&&!!A.events?.length,features:X&&!!A.features,presentations:$&&!!A.presentations,forms:U&&!!A.forms,dataViews:J&&!!A.dataViews,workflows:D&&!!A.workflows})}return H}function sA(A,Z={}){let Q=$A(A,Z);return JSON.stringify(Q,null,2)}function eA(A,Z={}){let Q=$A(A,Z);return M(Q)}function AZ(A,Z={}){let Q=UA(A,Z);return JSON.stringify(Q.openApi,null,2)}function ZZ(A,Z={}){let Q=UA(A,Z);return M(Q.openApi)}function M(A,Z=0){let Q=" ".repeat(Z),B="";if(Array.isArray(A))for(let X of A)if(typeof X==="object"&&X!==null)B+=`${Q}-
90
+ ${M(X,Z+1)}`;else B+=`${Q}- ${JSON.stringify(X)}
91
+ `;else if(typeof A==="object"&&A!==null)for(let[X,$]of Object.entries(A))if(Array.isArray($))B+=`${Q}${X}:
92
+ ${M($,Z+1)}`;else if(typeof $==="object"&&$!==null)B+=`${Q}${X}:
93
+ ${M($,Z+1)}`;else B+=`${Q}${X}: ${JSON.stringify($)}
94
+ `;return B}var zA={string:"ScalarTypeEnum.String_unsecure",integer:"ScalarTypeEnum.Int_unsecure",number:"ScalarTypeEnum.Float_unsecure",boolean:"ScalarTypeEnum.Boolean","string:date":"ScalarTypeEnum.Date","string:date-time":"ScalarTypeEnum.DateTime","string:email":"ScalarTypeEnum.EmailAddress","string:uri":"ScalarTypeEnum.URL","string:uuid":"ScalarTypeEnum.ID"};function JA(A){return typeof A==="object"&&A!==null&&"$ref"in A}function BZ(A){let Z=A.split("/");return Z[Z.length-1]??"Unknown"}function FA(A,Z){switch(A){case"zod":return new qA(Z);case"json-schema":return new RA(Z);case"graphql":return new CA(Z);case"contractspec":default:return new kA(Z)}}class kA{format="contractspec";config;constructor(A){this.config=A}generateModel(A,Z){let Q=this.generateContractSpecSchema(A,Z),B=this.config?k(Q.fields,this.config,!1).split(`
95
+ `).filter(Boolean):[];return{code:Q.code,fileName:V(Z)+".ts",imports:[...this.getBaseImports(),...B],name:Q.name}}generateField(A,Z,Q){let B=this.convertField(A,Z,Q);return{code:B.scalarType?`${B.scalarType}()`:"ScalarTypeEnum.String_unsecure()",typeRef:B.type.type,isOptional:B.type.optional,isArray:B.type.array}}getBaseImports(){return["import { defineSchemaModel, ScalarTypeEnum, EnumType } from '@contractspec/lib.schema';"]}generateContractSpecSchema(A,Z,Q=0){let B=" ".repeat(Q),X=[];if(JA(A))return{name:P(BZ(A.$ref)),fields:[],code:`// Reference to ${A.$ref}`};let $=A,U=$.description,J=$.properties,D=$.required??[],W=$.enum;if(W&&W.length>0){let L=P(F(Z)),x=[`${B}/**`,`${B} * Enum type: ${L}`,U?`${B} * ${U}`:null,`${B} */`,`${B}export const ${L} = new EnumType('${L}', [${W.map((G)=>`'${String(G)}'`).join(", ")}]);`].filter((G)=>G!==null).join(`
96
+ `);return{name:L,description:U,fields:[],code:x}}let E=$.type;if(E&&!J&&!W){let L=P(F(Z)),x=$.format,G=x?`${E}:${x}`:E,I=zA[G]??zA[E];if(I){let C=[`${B}/**`,`${B} * Type alias: ${L}`,U?`${B} * ${U}`:null,`${B} * Underlying type: ${I}`,`${B} */`,`${B}export const ${L} = defineSchemaModel({`,`${B} name: '${L}',`,U?`${B} description: ${JSON.stringify(U)},`:null,`${B} fields: {`,`${B} value: {`,`${B} type: ${I}(),`,`${B} isOptional: false,`,`${B} },`,`${B} },`,`${B}});`].filter((j)=>j!==null).join(`
97
+ `);return{name:L,description:U,fields:[],code:C}}}if($.additionalProperties&&!J){let L=P(F(Z)),x=[`${B}/**`,`${B} * Dictionary/Record type: ${L}`,U?`${B} * ${U}`:null,`${B} * Use as: Record<string, unknown> - access via record[key]`,`${B} */`,`${B}export const ${L} = ScalarTypeEnum.JSONObject();`].filter((G)=>G!==null).join(`
98
+ `);return{name:L,description:U,fields:[],code:x}}if(!J){let L=P(F(Z)),x=[`${B}export const ${L} = defineSchemaModel({`,`${B} name: '${L}',`,U?`${B} description: ${JSON.stringify(U)},`:null,`${B} fields: {},`,`${B}});`].filter((G)=>G!==null).join(`
99
+ `);return{name:L,description:U,fields:[],code:x}}let _=P(F(Z));for(let[L,x]of Object.entries(J)){let G=D.includes(L);X.push(this.convertField(x,L,G,_))}let H=[];for(let L of X)if(L.nestedModel)H.push(L.nestedModel.code),H.push("");if(H.push(`${B}export const ${_} = defineSchemaModel({`),H.push(`${B} name: '${_}',`),U)H.push(`${B} description: ${JSON.stringify(U)},`);H.push(`${B} fields: {`);for(let L of X){let x=this.generateFieldCodeHelper(L,Q+2);H.push(x)}return H.push(`${B} },`),H.push(`${B}});`),{name:_,description:U,fields:X,code:H.join(`
100
+ `),imports:[]}}convertField(A,Z,Q,B){let X=u(A,Z),$=h(A),U,J;if(!JA(A)){let D=A,W=D.enum;if(W)U=W.map(String);if(D.type==="object"&&!$&&D.properties&&!U){let E=(B?B:"")+P(Z);J=this.generateContractSpecSchema(A,E),X.type=J.name,X.isReference=!0}}return{name:Z,type:{...X,optional:!Q||X.optional,description:!JA(A)?A.description:void 0},scalarType:$,enumValues:U,nestedModel:J}}generateFieldCodeHelper(A,Z){let Q=" ".repeat(Z),B=[],$=/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(A.name)?A.name:`'${A.name}'`;if(B.push(`${Q}${$}: {`),A.enumValues){let U=P(A.name)+"Enum";B.push(`${Q} type: new EnumType('${U}', [${A.enumValues.map((J)=>`'${J}'`).join(", ")}]),`)}else if(A.scalarType)B.push(`${Q} type: ${A.scalarType}(),`);else if(A.nestedModel)B.push(`${Q} type: ${A.nestedModel.name},`);else if(A.type.primitive){let U=A.type.type==="number"?"ScalarTypeEnum.Float_unsecure":A.type.type==="boolean"?"ScalarTypeEnum.Boolean_unsecure":"ScalarTypeEnum.String_unsecure";B.push(`${Q} type: ${U}(),`)}else if(A.type.isReference)B.push(`${Q} type: ${A.type.type},`);else B.push(`${Q} type: ScalarTypeEnum.JSONObject(), // TODO: Define nested model for ${A.type.type}`);if(B.push(`${Q} isOptional: ${A.type.optional},`),A.type.array)B.push(`${Q} isArray: true,`);return B.push(`${Q}},`),B.join(`
101
+ `)}}class qA{format="zod";config;constructor(A){this.config=A}generateModel(A,Z,Q){let B=A,X=B.properties,$=B.required??[],U=Q?.description??B.description,J=[];if(U)J.push("/**"),J.push(` * ${U}`),J.push(" */");let D=`${Z}Schema`,W;if(X)W=this.generateZodObject(B);else W="z.object({})";return J.push(`export const ${D} = ${W};`),J.push(""),J.push(`export const ${Z} = new ZodSchemaType(${D}, { name: '${Z}', description: ${JSON.stringify(U)} });`),J.push(""),J.push(`export type ${Z} = z.infer<typeof ${D}>;`),{code:J.join(`
102
+ `),fileName:V(Z)+".ts",imports:this.getBaseImports(),name:Z}}generateField(A,Z,Q){let B=A,X=B.type,$=B.format,U=B.nullable,J;if(X==="object"&&B.properties)J=this.generateZodObject(B);else{if(J=this.mapTypeToZod(X,$),B.enum)J=`z.enum([${B.enum.map((W)=>`'${W}'`).join(", ")}])`;if(X==="array"){let D=B.items;if(D)J=`z.array(${this.generateField(D,"item",!0).code.replace(".optional()","")})`;else J="z.array(z.unknown())"}}if(X==="string"){if(B.minLength!==void 0)J+=`.min(${B.minLength})`;if(B.maxLength!==void 0)J+=`.max(${B.maxLength})`;if(B.pattern!==void 0)J+=`.regex(/${B.pattern}/)`}else if(X==="integer"||X==="number"){if(B.minimum!==void 0)J+=B.exclusiveMinimum===!0?`.gt(${B.minimum})`:`.min(${B.minimum})`;else if(typeof B.exclusiveMinimum==="number")J+=`.gt(${B.exclusiveMinimum})`;if(B.maximum!==void 0)J+=B.exclusiveMaximum===!0?`.lt(${B.maximum})`:`.max(${B.maximum})`;else if(typeof B.exclusiveMaximum==="number")J+=`.lt(${B.exclusiveMaximum})`;if(B.multipleOf!==void 0)J+=`.step(${B.multipleOf})`}else if(X==="array"){if(B.minItems!==void 0)J+=`.min(${B.minItems})`;if(B.maxItems!==void 0)J+=`.max(${B.maxItems})`}if(B.default!==void 0)J+=`.default(${JSON.stringify(B.default)})`;if(!Q||U)J=`${J}.optional()`;return{code:J,typeRef:X??"unknown",isOptional:!Q||Boolean(U),isArray:X==="array"}}getBaseImports(){return["import * as z from 'zod';","import { ZodSchemaType } from '@contractspec/lib.schema';"]}generateZodObject(A){let Z=A.required??[],Q=A.properties,B=["z.object({"];for(let[X,$]of Object.entries(Q)){let U=Z.includes(X),J=this.generateField($,X,U),D=/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(X)?X:`'${X}'`;B.push(` ${D}: ${J.code},`)}return B.push("})"),B.join(`
103
+ `)}mapTypeToZod(A,Z){if(Z==="date-time")return"z.string().datetime()";if(Z==="date")return"z.string().date()";if(Z==="email")return"z.string().email()";if(Z==="uri"||Z==="url")return"z.string().url()";if(Z==="uuid")return"z.string().uuid()";switch(A){case"string":return"z.string()";case"integer":return"z.number().int()";case"number":return"z.number()";case"boolean":return"z.boolean()";case"object":return"z.record(z.string(), z.unknown())";case"null":return"z.null()";default:return"z.unknown()"}}}class RA{format="json-schema";config;constructor(A){this.config=A}generateModel(A,Z,Q){let B=A,X=Q?.description??B.description,$={$schema:"https://json-schema.org/draft/2020-12/schema",title:Z,...B};if(X)$.description=X;let U=[];if(U.push("/**"),U.push(` * JSON Schema: ${Z}`),X)U.push(` * ${X}`);U.push(" */");let J=`${Z}Schema`;return U.push(`export const ${J} = ${JSON.stringify($,null,2)} as const;`),U.push(""),U.push(`export const ${Z} = new JsonSchemaType(${J});`),U.push(""),U.push(`export type ${Z} = unknown; // JSON Schema type inference not fully supported`),{code:U.join(`
104
+ `),fileName:V(Z)+".ts",imports:this.getBaseImports(),name:Z}}generateField(A,Z,Q){let B=A,X=B.type,$=B.nullable;return{code:JSON.stringify(B),typeRef:X??"unknown",isOptional:!Q||Boolean($),isArray:X==="array"}}getBaseImports(){return["import { JsonSchemaType } from '@contractspec/lib.schema';"]}}class CA{format="graphql";config;constructor(A){this.config=A}generateModel(A,Z,Q){let B=A,X=B.properties,$=B.required??[],U=Q?.description??B.description,J=[];if(U)J.push(`"""${U}"""`);if(J.push(`type ${Z} {`),X)for(let[E,Y]of Object.entries(X)){let _=$.includes(E),H=this.generateField(Y,E,_),L=_?"!":"";J.push(` ${E}: ${H.typeRef}${L}`)}J.push("}");let D=J.join(`
105
+ `),W=[];return W.push("/**"),W.push(` * GraphQL type definition: ${Z}`),W.push(" */"),W.push(`export const ${Z}TypeDef = \`${D}\`;`),W.push(""),W.push(`export const ${Z} = new GraphQLSchemaType(${Z}TypeDef, '${Z}');`),{code:W.join(`
106
+ `),fileName:V(Z)+".ts",imports:this.getBaseImports(),name:Z}}generateField(A,Z,Q){let B=A,X=B.type,$=B.format,U=B.nullable,J=this.mapTypeToGraphQL(X,$);return{code:J,typeRef:J,isOptional:!Q||Boolean(U),isArray:X==="array"}}getBaseImports(){return["import { GraphQLSchemaType } from '@contractspec/lib.schema';"]}mapTypeToGraphQL(A,Z){if(Z==="date-time")return"DateTime";if(Z==="date")return"Date";if(Z==="email")return"String";if(Z==="uri"||Z==="url")return"String";if(Z==="uuid")return"ID";switch(A){case"string":return"String";case"integer":return"Int";case"number":return"Float";case"boolean":return"Boolean";case"object":return"JSON";case"array":return"[JSON]";default:return"JSON"}}}var VA={string:"ScalarTypeEnum.String_unsecure",integer:"ScalarTypeEnum.Int_unsecure",number:"ScalarTypeEnum.Float_unsecure",boolean:"ScalarTypeEnum.Boolean","string:date":"ScalarTypeEnum.Date","string:date-time":"ScalarTypeEnum.DateTime","string:email":"ScalarTypeEnum.EmailAddress","string:uri":"ScalarTypeEnum.URL","string:uuid":"ScalarTypeEnum.ID"};function TA(A){return typeof A==="object"&&A!==null&&"$ref"in A}function QZ(A){let Z=A.split("/");return Z[Z.length-1]??"Unknown"}function u(A,Z){if(TA(A))return{type:P(QZ(A.$ref)),optional:!1,array:!1,primitive:!1,isReference:!0};let Q=A,B=Q.type,X=Q.format,$=Q.nullable,U=Q._originalTypeName;if(U)return{type:P(U),optional:$??!1,array:!1,primitive:!1,isReference:!0};if(B==="array"){let D=Q.items;if(D)return{...u(D,Z),array:!0,optional:$??!1};return{type:"unknown",optional:$??!1,array:!0,primitive:!1,isReference:!0}}if(B==="object"||Q.properties)return{type:Z?P(Z):"Record<string, unknown>",optional:$??!1,array:!1,primitive:!1,isReference:!0};if(Q.enum)return{type:Z?P(Z):"string",optional:$??!1,array:!1,primitive:!1,isReference:!0};let J=X?`${B}:${X}`:B;if(J==="string")return{type:"string",optional:$??!1,array:!1,primitive:!0};if(J==="integer"||B==="number")return{type:"number",optional:$??!1,array:!1,primitive:!0};if(J==="boolean")return{type:"boolean",optional:$??!1,array:!1,primitive:!0};return{type:"unknown",optional:$??!1,array:!1,primitive:!1,isReference:!0}}function h(A){if(TA(A))return;let Z=A,Q=Z.type,B=Z.format;if(!Q)return;if(Q==="array"){let $=Z.items;if($)return h($);return}let X=B?`${Q}:${B}`:Q;return VA[X]??VA[Q]??void 0}function K(A,Z,Q="contractspec",B){let $=FA(Q,B).generateModel(A,Z,{description:A.description});return{name:$.name,description:A.description,fields:[],code:$.code,imports:$.imports}}function k(A,Z,Q=!0){let B=new Set,X=Q?".":`../${Z.conventions.models}`;B.add("import { defineSchemaModel, ScalarTypeEnum, EnumType } from '@contractspec/lib.schema';");for(let $ of A)if($.type.isReference&&!$.type.primitive&&!$.enumValues&&!$.scalarType&&!$.nestedModel){let U=$.type.type,J=U.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase();B.add(`import { ${U} } from '${X}/${J}';`)}return Array.from(B).join(`
107
+ `)}function wA(A,Z){let Q=F(A.name),B=P(Q)+"Payload",X=Z.schemaFormat||"contractspec",$=K(A.payload,B,X,Z),U=new Set;if(U.add("import { defineEvent, type EventSpec } from '@contractspec/lib.contracts-spec';"),$.imports&&$.imports.length>0)$.imports.forEach((D)=>U.add(D));else if($.fields&&$.fields.length>0)k($.fields,Z).split(`
108
+ `).filter(Boolean).forEach((W)=>U.add(W));if($.name!==B){let D=`../${Z.conventions.models}`,W=$.name.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase();U.add(`import { ${$.name} } from '${D}/${W}';`)}return`
109
+ ${Array.from(U).join(`
110
+ `)}
1611
111
 
1612
- ${payloadModel.code}
112
+ ${$.code}
1613
113
 
1614
- export const ${eventName} = defineEvent({
114
+ export const ${Q} = defineEvent({
1615
115
  meta: {
1616
- key: '${event.name}',
116
+ key: '${A.name}',
1617
117
  version: '1.0.0',
1618
- description: ${JSON.stringify(event.description ?? "")},
118
+ description: ${JSON.stringify(A.description??"")},
1619
119
  },
1620
- payload: ${payloadModel.name},
120
+ payload: ${$.name},
1621
121
  });
1622
- `.trim();
1623
- }
1624
-
1625
- // src/openapi/importer/analyzer.ts
1626
- var COMMAND_METHODS = ["post", "put", "delete", "patch"];
1627
- function inferOpKind(method) {
1628
- return COMMAND_METHODS.includes(method.toLowerCase()) ? "command" : "query";
1629
- }
1630
- function inferAuthLevel(operation, defaultAuth) {
1631
- if (!operation.security || operation.security.length === 0) {
1632
- return defaultAuth;
1633
- }
1634
- for (const sec of operation.security) {
1635
- if (Object.keys(sec).length === 0) {
1636
- return "anonymous";
1637
- }
1638
- }
1639
- return "user";
1640
- }
1641
-
1642
- // src/openapi/importer/generator.ts
1643
- function generateSpecCode(operation, contractspecConfig, options = {}, inputModel, outputModel, queryModel = null, paramsModel = null, headersModel = null) {
1644
- const specKey = toSpecKey(operation.operationId, options.prefix);
1645
- const kind = inferOpKind(operation.method);
1646
- const auth = inferAuthLevel(operation, options.defaultAuth ?? "user");
1647
- const lines = [];
1648
- lines.push("import { defineCommand, defineQuery } from '@contractspec/lib.contracts-spec';");
1649
- if (inputModel || outputModel || queryModel || paramsModel || headersModel) {
1650
- const collectedImports = new Set;
1651
- const models = [
1652
- inputModel,
1653
- outputModel,
1654
- queryModel,
1655
- paramsModel,
1656
- headersModel
1657
- ].filter((m) => !!m);
1658
- models.forEach((m) => {
1659
- if (m.imports && m.imports.length > 0) {
1660
- m.imports.forEach((i) => collectedImports.add(i));
1661
- }
1662
- });
1663
- const legacyModels = models.filter((m) => !m.imports || m.imports.length === 0);
1664
- const legacyFields = legacyModels.flatMap((m) => m.fields);
1665
- if (legacyFields.length > 0) {
1666
- const legacyImportStr = generateImports(legacyFields, contractspecConfig, false);
1667
- legacyImportStr.split(`
1668
- `).filter(Boolean).forEach((i) => collectedImports.add(i));
1669
- }
1670
- if (collectedImports.size > 0) {
1671
- lines.push(Array.from(collectedImports).sort().join(`
1672
- `));
1673
- }
1674
- }
1675
- lines.push("");
1676
- const schemaSections = [
1677
- { label: "Input schema", model: inputModel },
1678
- { label: "Query schema", model: queryModel },
1679
- { label: "Path schema", model: paramsModel },
1680
- { label: "Header schema", model: headersModel },
1681
- { label: "Output schema", model: outputModel }
1682
- ];
1683
- for (const section of schemaSections) {
1684
- if (section.model && section.model.code) {
1685
- lines.push(`// ${section.label}`);
1686
- lines.push(section.model.code);
1687
- lines.push("");
1688
- }
1689
- }
1690
- const defineFunc = kind === "command" ? "defineCommand" : "defineQuery";
1691
- const safeName = toValidIdentifier(toPascalCase(operation.operationId));
1692
- lines.push(`/**`);
1693
- lines.push(` * ${operation.summary ?? operation.operationId}`);
1694
- if (operation.description) {
1695
- lines.push(` *`);
1696
- lines.push(` * ${operation.description}`);
1697
- }
1698
- lines.push(` *`);
1699
- lines.push(` * @source OpenAPI: ${operation.method.toUpperCase()} ${operation.path}`);
1700
- lines.push(` */`);
1701
- lines.push(`export const ${safeName}Spec = ${defineFunc}({`);
1702
- lines.push(" meta: {");
1703
- lines.push(` key: '${specKey}',`);
1704
- lines.push(" version: '1.0.0',");
1705
- lines.push(` stability: '${options.defaultStability ?? "stable"}',`);
1706
- lines.push(` owners: [${(options.defaultOwners ?? []).map((o) => `'${o}'`).join(", ")}],`);
1707
- lines.push(` tags: [${operation.tags.map((t) => `'${t}'`).join(", ")}],`);
1708
- lines.push(` description: ${JSON.stringify(operation.summary ?? operation.operationId)},`);
1709
- lines.push(` goal: ${JSON.stringify(operation.description ?? "Imported from OpenAPI")},`);
1710
- lines.push(` context: 'Imported from OpenAPI: ${operation.method.toUpperCase()} ${operation.path}',`);
1711
- lines.push(" },");
1712
- lines.push(" io: {");
1713
- lines.push(` input: ${inputModel?.name ?? "null"},`);
1714
- if (queryModel)
1715
- lines.push(` query: ${queryModel.name},`);
1716
- if (paramsModel)
1717
- lines.push(` params: ${paramsModel.name},`);
1718
- if (headersModel)
1719
- lines.push(` headers: ${headersModel.name},`);
1720
- if (outputModel) {
1721
- lines.push(` output: ${outputModel.name},`);
1722
- } else {
1723
- lines.push(" output: null, // TODO: Define output schema");
1724
- }
1725
- lines.push(" },");
1726
- lines.push(" policy: {");
1727
- lines.push(` auth: '${auth}',`);
1728
- lines.push(" },");
1729
- const httpMethod = operation.method.toUpperCase();
1730
- const restMethod = httpMethod === "GET" ? "GET" : "POST";
1731
- lines.push(" transport: {");
1732
- lines.push(" rest: {");
1733
- lines.push(` method: '${restMethod}',`);
1734
- lines.push(` path: '${operation.path}',`);
1735
- lines.push(" },");
1736
- lines.push(" },");
1737
- lines.push("});");
1738
- return lines.join(`
1739
- `);
1740
- }
1741
-
1742
- // src/openapi/importer/grouping.ts
1743
- function resolveOperationGroupFolder(operation, conventions) {
1744
- const groupingRule = conventions.operationsGrouping;
1745
- if (!groupingRule || groupingRule.strategy === "none") {
1746
- return "";
1747
- }
1748
- return applyGroupingStrategy(groupingRule, {
1749
- name: operation.operationId,
1750
- tags: operation.tags,
1751
- path: operation.path
1752
- });
1753
- }
1754
- function resolveModelGroupFolder(modelName, conventions, relatedPath, relatedTags) {
1755
- const groupingRule = conventions.modelsGrouping;
1756
- if (!groupingRule || groupingRule.strategy === "none") {
1757
- return "";
1758
- }
1759
- return applyGroupingStrategy(groupingRule, {
1760
- name: modelName,
1761
- tags: relatedTags ?? [],
1762
- path: relatedPath
1763
- });
1764
- }
1765
- function resolveEventGroupFolder(eventName, conventions, relatedTags) {
1766
- const groupingRule = conventions.eventsGrouping;
1767
- if (!groupingRule || groupingRule.strategy === "none") {
1768
- return "";
1769
- }
1770
- return applyGroupingStrategy(groupingRule, {
1771
- name: eventName,
1772
- tags: relatedTags ?? []
1773
- });
1774
- }
1775
- function applyGroupingStrategy(rule, context) {
1776
- switch (rule.strategy) {
1777
- case "by-tag":
1778
- return context.tags?.[0] ?? "untagged";
1779
- case "by-owner":
1780
- return context.owners?.[0] ?? "unowned";
1781
- case "by-domain":
1782
- return extractDomain(context.name);
1783
- case "by-url-path-single":
1784
- return extractUrlPathLevel(context.path, 1);
1785
- case "by-url-path-multi":
1786
- return extractUrlPathLevel(context.path, rule.urlPathLevel ?? 2);
1787
- case "by-feature":
1788
- return extractDomain(context.name);
1789
- case "none":
1790
- default:
1791
- return "";
1792
- }
1793
- }
1794
- function extractDomain(name) {
1795
- if (name.includes(".")) {
1796
- return name.split(".")[0] ?? "default";
1797
- }
1798
- if (name.includes("_")) {
1799
- return name.split("_")[0] ?? "default";
1800
- }
1801
- const match = name.match(/^([a-z]+)/i);
1802
- return match?.[1]?.toLowerCase() ?? "default";
1803
- }
1804
- function extractUrlPathLevel(path, level) {
1805
- if (!path)
1806
- return "root";
1807
- const segments = path.split("/").filter(Boolean);
1808
- const nonParamSegments = segments.filter((s) => !s.startsWith("{"));
1809
- if (nonParamSegments.length === 0)
1810
- return "root";
1811
- return nonParamSegments.slice(0, level).join("/");
1812
- }
1813
-
1814
- // src/openapi/importer/models.ts
1815
- function generateModelCode(name, schema, options) {
1816
- const modelName = toPascalCase(toValidIdentifier(name));
1817
- const schemaFormat = options.schemaFormat || "contractspec";
1818
- const model = generateSchemaModelCode(schema, modelName, schemaFormat, options);
1819
- let imports = "";
1820
- if (model.imports && model.imports.length > 0) {
1821
- imports = model.imports.join(`
1822
- `);
1823
- } else if (model.fields.length > 0) {
1824
- imports = generateImports(model.fields, options);
1825
- }
1826
- return `
1827
- ${imports}
1828
-
1829
- ${model.code}
1830
- `.trim();
1831
- }
1832
-
1833
- // src/openapi/importer/schemas.ts
1834
- function buildInputSchemas(operation) {
1835
- const result = {};
1836
- if (operation.pathParams.length > 0) {
1837
- result.params = {
1838
- type: "object",
1839
- properties: operation.pathParams.reduce((acc, p) => {
1840
- acc[p.name] = p.schema;
1841
- return acc;
1842
- }, {}),
1843
- required: operation.pathParams.map((p) => p.name)
1844
- };
1845
- }
1846
- if (operation.queryParams.length > 0) {
1847
- result.query = {
1848
- type: "object",
1849
- properties: operation.queryParams.reduce((acc, p) => {
1850
- acc[p.name] = p.schema;
1851
- return acc;
1852
- }, {}),
1853
- required: operation.queryParams.filter((p) => p.required).map((p) => p.name)
1854
- };
1855
- }
1856
- const excludedHeaders = [
1857
- "authorization",
1858
- "content-type",
1859
- "accept",
1860
- "user-agent"
1861
- ];
1862
- const actualHeaders = operation.headerParams.filter((p) => !excludedHeaders.includes(p.name.toLowerCase()));
1863
- if (actualHeaders.length > 0) {
1864
- result.headers = {
1865
- type: "object",
1866
- properties: actualHeaders.reduce((acc, p) => {
1867
- acc[p.name] = p.schema;
1868
- return acc;
1869
- }, {}),
1870
- required: actualHeaders.filter((p) => p.required).map((p) => p.name)
1871
- };
1872
- }
1873
- if (operation.requestBody?.schema) {
1874
- result.body = operation.requestBody.schema;
1875
- }
1876
- return result;
1877
- }
1878
- function getOutputSchema(operation) {
1879
- const successCodes = ["200", "201", "202", "204"];
1880
- for (const code of successCodes) {
1881
- const response = operation.responses[code];
1882
- if (response?.schema) {
1883
- return response.schema;
1884
- }
1885
- }
1886
- for (const [code, response] of Object.entries(operation.responses)) {
1887
- if (code.startsWith("2") && response.schema) {
1888
- return response.schema;
1889
- }
1890
- }
1891
- return null;
1892
- }
1893
-
1894
- // src/openapi/importer/index.ts
1895
- var importFromOpenApi = (parseResult, contractspecOptions, importOptions = {}) => {
1896
- const { tags, exclude = [], include } = importOptions;
1897
- const specs = [];
1898
- const skipped = [];
1899
- const errors = [];
1900
- for (const operation of parseResult.operations) {
1901
- if (tags && tags.length > 0) {
1902
- const hasMatchingTag = operation.tags.some((t) => tags.includes(t));
1903
- if (!hasMatchingTag) {
1904
- skipped.push({
1905
- sourceId: operation.operationId,
1906
- reason: `No matching tags (has: ${operation.tags.join(", ")})`
1907
- });
1908
- continue;
1909
- }
1910
- }
1911
- if (include && include.length > 0) {
1912
- if (!include.includes(operation.operationId)) {
1913
- skipped.push({
1914
- sourceId: operation.operationId,
1915
- reason: "Not in include list"
1916
- });
1917
- continue;
1918
- }
1919
- } else if (exclude.includes(operation.operationId)) {
1920
- skipped.push({
1921
- sourceId: operation.operationId,
1922
- reason: "In exclude list"
1923
- });
1924
- continue;
1925
- }
1926
- if (operation.deprecated && importOptions.defaultStability !== "deprecated") {
1927
- skipped.push({
1928
- sourceId: operation.operationId,
1929
- reason: "Deprecated operation"
1930
- });
1931
- continue;
1932
- }
1933
- try {
1934
- const inputSchemas = buildInputSchemas(operation);
1935
- const schemaFormat = importOptions.schemaFormat || contractspecOptions.schemaFormat || "contractspec";
1936
- const inputModel = inputSchemas.body ? generateSchemaModelCode(inputSchemas.body, `${operation.operationId}Input`, schemaFormat, contractspecOptions) : null;
1937
- const queryModel = inputSchemas.query ? generateSchemaModelCode(inputSchemas.query, `${operation.operationId}Query`, schemaFormat, contractspecOptions) : null;
1938
- const paramsModel = inputSchemas.params ? generateSchemaModelCode(inputSchemas.params, `${operation.operationId}Params`, schemaFormat, contractspecOptions) : null;
1939
- const headersModel = inputSchemas.headers ? generateSchemaModelCode(inputSchemas.headers, `${operation.operationId}Headers`, schemaFormat, contractspecOptions) : null;
1940
- const outputSchema = getOutputSchema(operation);
1941
- let outputModel = outputSchema ? generateSchemaModelCode(outputSchema, `${operation.operationId}Output`, schemaFormat, contractspecOptions) : null;
1942
- if (outputModel && schemaFormat === "contractspec" && !outputModel.code.includes("defineSchemaModel")) {
1943
- outputModel = null;
1944
- }
1945
- const code = generateSpecCode(operation, contractspecOptions, importOptions, inputModel, outputModel, queryModel, paramsModel, headersModel);
1946
- const specName = toSpecKey(operation.operationId, importOptions.prefix);
1947
- const fileName = toFileName(specName);
1948
- const transportHints = {
1949
- rest: {
1950
- method: operation.method.toUpperCase(),
1951
- path: operation.path,
1952
- params: {
1953
- path: operation.pathParams.map((p) => p.name),
1954
- query: operation.queryParams.map((p) => p.name),
1955
- header: operation.headerParams.map((p) => p.name),
1956
- cookie: operation.cookieParams.map((p) => p.name)
1957
- }
1958
- }
1959
- };
1960
- const source = {
1961
- type: "openapi",
1962
- sourceId: operation.operationId,
1963
- operationId: operation.operationId,
1964
- openApiVersion: parseResult.version,
1965
- importedAt: new Date
1966
- };
1967
- const groupFolder = resolveOperationGroupFolder(operation, contractspecOptions.conventions);
1968
- specs.push({
1969
- code,
1970
- fileName,
1971
- groupFolder: groupFolder || undefined,
1972
- source,
1973
- transportHints
1974
- });
1975
- } catch (error) {
1976
- errors.push({
1977
- sourceId: operation.operationId,
1978
- error: error instanceof Error ? error.message : String(error)
1979
- });
1980
- }
1981
- }
1982
- for (const [name, schema] of Object.entries(parseResult.schemas)) {
1983
- try {
1984
- const code = generateModelCode(name, schema, {
1985
- ...contractspecOptions,
1986
- schemaFormat: importOptions.schemaFormat || contractspecOptions.schemaFormat
1987
- });
1988
- const fileName = toFileName(toSpecKey(name, importOptions.prefix));
1989
- const groupFolder = resolveModelGroupFolder(name, contractspecOptions.conventions);
1990
- specs.push({
1991
- code,
1992
- fileName,
1993
- groupFolder: groupFolder || undefined,
1994
- source: {
1995
- type: "openapi",
1996
- sourceId: name,
1997
- operationId: name,
1998
- openApiVersion: parseResult.version,
1999
- importedAt: new Date
2000
- },
2001
- transportHints: {}
2002
- });
2003
- } catch (error) {
2004
- errors.push({
2005
- sourceId: name,
2006
- error: error instanceof Error ? "Model conversion failed: " + error.message : String(error)
2007
- });
2008
- }
2009
- }
2010
- for (const event of parseResult.events) {
2011
- try {
2012
- const code = generateEventCode(event, {
2013
- ...contractspecOptions,
2014
- schemaFormat: importOptions.schemaFormat || contractspecOptions.schemaFormat
2015
- });
2016
- const fileName = toFileName(toSpecKey(event.name, importOptions.prefix));
2017
- const groupFolder = resolveEventGroupFolder(event.name, contractspecOptions.conventions);
2018
- specs.push({
2019
- code,
2020
- fileName,
2021
- groupFolder: groupFolder || undefined,
2022
- source: {
2023
- type: "openapi",
2024
- sourceId: event.name,
2025
- operationId: event.name,
2026
- openApiVersion: parseResult.version,
2027
- importedAt: new Date
2028
- },
2029
- transportHints: {}
2030
- });
2031
- } catch (error) {
2032
- errors.push({
2033
- sourceId: event.name,
2034
- error: error instanceof Error ? "Event conversion failed: " + error.message : String(error)
2035
- });
2036
- }
2037
- }
2038
- return {
2039
- operationSpecs: specs,
2040
- skipped,
2041
- errors,
2042
- summary: {
2043
- total: parseResult.operations.length + Object.keys(parseResult.schemas).length + parseResult.events.length,
2044
- imported: specs.length,
2045
- skipped: skipped.length,
2046
- errors: errors.length
2047
- }
2048
- };
2049
- };
2050
- function importOperation(operation, options = {}, contractspecOptions) {
2051
- const inputSchemas = buildInputSchemas(operation);
2052
- const schemaFormat = options.schemaFormat || contractspecOptions.schemaFormat || "contractspec";
2053
- const inputModel = inputSchemas.body ? generateSchemaModelCode(inputSchemas.body, `${operation.operationId}Input`, schemaFormat, contractspecOptions) : null;
2054
- const queryModel = inputSchemas.query ? generateSchemaModelCode(inputSchemas.query, `${operation.operationId}Query`, schemaFormat, contractspecOptions) : null;
2055
- const paramsModel = inputSchemas.params ? generateSchemaModelCode(inputSchemas.params, `${operation.operationId}Params`, schemaFormat, contractspecOptions) : null;
2056
- const headersModel = inputSchemas.headers ? generateSchemaModelCode(inputSchemas.headers, `${operation.operationId}Headers`, schemaFormat, contractspecOptions) : null;
2057
- const outputSchema = getOutputSchema(operation);
2058
- const outputModel = outputSchema ? generateSchemaModelCode(outputSchema, `${operation.operationId}Output`, schemaFormat, contractspecOptions) : null;
2059
- return generateSpecCode(operation, contractspecOptions, options, inputModel, outputModel, queryModel, paramsModel, headersModel);
2060
- }
2061
- // src/openapi/parser/resolvers.ts
2062
- function isReference3(obj) {
2063
- return typeof obj === "object" && obj !== null && "$ref" in obj;
2064
- }
2065
- function resolveRef(doc, ref) {
2066
- if (!ref.startsWith("#/")) {
2067
- return;
2068
- }
2069
- const path = ref.slice(2).split("/");
2070
- let current = doc;
2071
- for (const part of path) {
2072
- if (current === null || current === undefined)
2073
- return;
2074
- if (typeof current !== "object")
2075
- return;
2076
- current = current[part];
2077
- }
2078
- return current;
2079
- }
2080
- function dereferenceSchema(doc, schema, seen = new Set) {
2081
- if (!schema)
2082
- return;
2083
- if (isReference3(schema)) {
2084
- if (seen.has(schema.$ref)) {
2085
- return schema;
2086
- }
2087
- const newSeen = new Set(seen);
2088
- newSeen.add(schema.$ref);
2089
- const resolved = resolveRef(doc, schema.$ref);
2090
- if (!resolved)
2091
- return schema;
2092
- const dereferenced = dereferenceSchema(doc, resolved, newSeen);
2093
- if (!dereferenced)
2094
- return schema;
2095
- const refParts = schema.$ref.split("/");
2096
- const typeName = refParts[refParts.length - 1];
2097
- return {
2098
- ...dereferenced,
2099
- _originalRef: schema.$ref,
2100
- _originalTypeName: typeName
2101
- };
2102
- }
2103
- const schemaObj = { ...schema };
2104
- if (schemaObj.properties) {
2105
- const props = schemaObj.properties;
2106
- const newProps = {};
2107
- for (const [key, prop] of Object.entries(props)) {
2108
- newProps[key] = dereferenceSchema(doc, prop, seen) ?? prop;
2109
- }
2110
- schemaObj.properties = newProps;
2111
- }
2112
- if (schemaObj.items) {
2113
- schemaObj.items = dereferenceSchema(doc, schemaObj.items, seen);
2114
- }
2115
- const combinators = ["allOf", "anyOf", "oneOf"];
2116
- for (const comb of combinators) {
2117
- if (Array.isArray(schemaObj[comb])) {
2118
- schemaObj[comb] = schemaObj[comb].map((s) => dereferenceSchema(doc, s, seen) ?? s);
2119
- }
2120
- }
2121
- return schemaObj;
2122
- }
2123
-
2124
- // src/openapi/parser/parameters.ts
2125
- function parseParameters(doc, params) {
2126
- const result = {
2127
- path: [],
2128
- query: [],
2129
- header: [],
2130
- cookie: []
2131
- };
2132
- if (!params)
2133
- return result;
2134
- for (const param of params) {
2135
- let resolved;
2136
- if (isReference3(param)) {
2137
- const ref = resolveRef(doc, param.$ref);
2138
- if (!ref)
2139
- continue;
2140
- resolved = ref;
2141
- } else {
2142
- resolved = param;
2143
- }
2144
- const parsed = {
2145
- name: resolved.name,
2146
- in: resolved.in,
2147
- required: resolved.required ?? resolved.in === "path",
2148
- description: resolved.description,
2149
- schema: dereferenceSchema(doc, resolved.schema),
2150
- deprecated: resolved.deprecated ?? false
2151
- };
2152
- result[resolved.in]?.push(parsed);
2153
- }
2154
- return result;
2155
- }
2156
-
2157
- // src/openapi/parser/utils.ts
2158
- import { parse as parseYaml } from "yaml";
2159
- var HTTP_METHODS = [
2160
- "get",
2161
- "post",
2162
- "put",
2163
- "delete",
2164
- "patch",
2165
- "head",
2166
- "options",
2167
- "trace"
2168
- ];
2169
- function parseOpenApiString(content, format = "json") {
2170
- if (format === "yaml") {
2171
- return parseYaml(content);
2172
- }
2173
- return JSON.parse(content);
2174
- }
2175
- function detectFormat(content) {
2176
- const trimmed = content.trim();
2177
- if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
2178
- return "json";
2179
- }
2180
- return "yaml";
2181
- }
2182
- function detectVersion(doc) {
2183
- const version = doc.openapi;
2184
- if (version.startsWith("3.1")) {
2185
- return "3.1";
2186
- }
2187
- return "3.0";
2188
- }
2189
- function generateOperationId(method, path) {
2190
- const pathParts = path.split("/").filter(Boolean).map((part) => {
2191
- if (part.startsWith("{") && part.endsWith("}")) {
2192
- return "By" + part.slice(1, -1).charAt(0).toUpperCase() + part.slice(2, -1);
2193
- }
2194
- return part.charAt(0).toUpperCase() + part.slice(1);
2195
- });
2196
- return method + pathParts.join("");
2197
- }
2198
-
2199
- // src/openapi/parser/operation.ts
2200
- function parseOperation(doc, method, path, operation, pathParams) {
2201
- const allParams = [...pathParams ?? [], ...operation.parameters ?? []];
2202
- const params = parseParameters(doc, allParams);
2203
- let requestBody;
2204
- if (operation.requestBody) {
2205
- const body = isReference3(operation.requestBody) ? resolveRef(doc, operation.requestBody.$ref) : operation.requestBody;
2206
- if (body) {
2207
- const contentType = Object.keys(body.content ?? {})[0] ?? "application/json";
2208
- const content = body.content?.[contentType];
2209
- if (content?.schema) {
2210
- requestBody = {
2211
- required: body.required ?? false,
2212
- schema: dereferenceSchema(doc, content.schema) ?? {},
2213
- contentType
2214
- };
2215
- }
2216
- }
2217
- }
2218
- const responses = {};
2219
- for (const [status, response] of Object.entries(operation.responses ?? {})) {
2220
- const resolved = isReference3(response) ? resolveRef(doc, response.$ref) : response;
2221
- if (resolved) {
2222
- const contentType = Object.keys(resolved.content ?? {})[0];
2223
- const content = contentType ? resolved.content?.[contentType] : undefined;
2224
- responses[status] = {
2225
- description: resolved.description,
2226
- schema: content?.schema ? dereferenceSchema(doc, content.schema) : undefined,
2227
- contentType
2228
- };
2229
- }
2230
- }
2231
- const contractSpecMeta = operation?.["x-contractspec"];
2232
- return {
2233
- operationId: operation.operationId ?? generateOperationId(method, path),
2234
- method,
2235
- path,
2236
- summary: operation.summary,
2237
- description: operation.description,
2238
- tags: operation.tags ?? [],
2239
- pathParams: params.path,
2240
- queryParams: params.query,
2241
- headerParams: params.header,
2242
- cookieParams: params.cookie,
2243
- requestBody,
2244
- responses,
2245
- deprecated: operation.deprecated ?? false,
2246
- security: operation.security,
2247
- contractSpecMeta
2248
- };
2249
- }
122
+ `.trim()}var XZ=["post","put","delete","patch"];function SA(A){return XZ.includes(A.toLowerCase())?"command":"query"}function NA(A,Z){if(!A.security||A.security.length===0)return Z;for(let Q of A.security)if(Object.keys(Q).length===0)return"anonymous";return"user"}function WA(A,Z,Q={},B,X,$=null,U=null,J=null){let D=T(A.operationId,Q.prefix),W=SA(A.method),E=NA(A,Q.defaultAuth??"user"),Y=[];if(Y.push("import { defineCommand, defineQuery } from '@contractspec/lib.contracts-spec';"),B||X||$||U||J){let I=new Set,C=[B,X,$,U,J].filter((z)=>!!z);C.forEach((z)=>{if(z.imports&&z.imports.length>0)z.imports.forEach((N)=>I.add(N))});let v=C.filter((z)=>!z.imports||z.imports.length===0).flatMap((z)=>z.fields);if(v.length>0)k(v,Z,!1).split(`
123
+ `).filter(Boolean).forEach((N)=>I.add(N));if(I.size>0)Y.push(Array.from(I).sort().join(`
124
+ `))}Y.push("");let _=[{label:"Input schema",model:B},{label:"Query schema",model:$},{label:"Path schema",model:U},{label:"Header schema",model:J},{label:"Output schema",model:X}];for(let I of _)if(I.model&&I.model.code)Y.push(`// ${I.label}`),Y.push(I.model.code),Y.push("");let H=W==="command"?"defineCommand":"defineQuery",L=F(P(A.operationId));if(Y.push("/**"),Y.push(` * ${A.summary??A.operationId}`),A.description)Y.push(" *"),Y.push(` * ${A.description}`);if(Y.push(" *"),Y.push(` * @source OpenAPI: ${A.method.toUpperCase()} ${A.path}`),Y.push(" */"),Y.push(`export const ${L}Spec = ${H}({`),Y.push(" meta: {"),Y.push(` key: '${D}',`),Y.push(" version: '1.0.0',"),Y.push(` stability: '${Q.defaultStability??"stable"}',`),Y.push(` owners: [${(Q.defaultOwners??[]).map((I)=>`'${I}'`).join(", ")}],`),Y.push(` tags: [${A.tags.map((I)=>`'${I}'`).join(", ")}],`),Y.push(` description: ${JSON.stringify(A.summary??A.operationId)},`),Y.push(` goal: ${JSON.stringify(A.description??"Imported from OpenAPI")},`),Y.push(` context: 'Imported from OpenAPI: ${A.method.toUpperCase()} ${A.path}',`),Y.push(" },"),Y.push(" io: {"),Y.push(` input: ${B?.name??"null"},`),$)Y.push(` query: ${$.name},`);if(U)Y.push(` params: ${U.name},`);if(J)Y.push(` headers: ${J.name},`);if(X)Y.push(` output: ${X.name},`);else Y.push(" output: null, // TODO: Define output schema");Y.push(" },"),Y.push(" policy: {"),Y.push(` auth: '${E}',`),Y.push(" },");let G=A.method.toUpperCase()==="GET"?"GET":"POST";return Y.push(" transport: {"),Y.push(" rest: {"),Y.push(` method: '${G}',`),Y.push(` path: '${A.path}',`),Y.push(" },"),Y.push(" },"),Y.push("});"),Y.join(`
125
+ `)}function vA(A,Z){let Q=Z.operationsGrouping;if(!Q||Q.strategy==="none")return"";return YA(Q,{name:A.operationId,tags:A.tags,path:A.path})}function OA(A,Z,Q,B){let X=Z.modelsGrouping;if(!X||X.strategy==="none")return"";return YA(X,{name:A,tags:B??[],path:Q})}function bA(A,Z,Q){let B=Z.eventsGrouping;if(!B||B.strategy==="none")return"";return YA(B,{name:A,tags:Q??[]})}function YA(A,Z){switch(A.strategy){case"by-tag":return Z.tags?.[0]??"untagged";case"by-owner":return Z.owners?.[0]??"unowned";case"by-domain":return MA(Z.name);case"by-url-path-single":return jA(Z.path,1);case"by-url-path-multi":return jA(Z.path,A.urlPathLevel??2);case"by-feature":return MA(Z.name);case"none":default:return""}}function MA(A){if(A.includes("."))return A.split(".")[0]??"default";if(A.includes("_"))return A.split("_")[0]??"default";return A.match(/^([a-z]+)/i)?.[1]?.toLowerCase()??"default"}function jA(A,Z){if(!A)return"root";let B=A.split("/").filter(Boolean).filter((X)=>!X.startsWith("{"));if(B.length===0)return"root";return B.slice(0,Z).join("/")}function yA(A,Z,Q){let B=P(F(A)),X=Q.schemaFormat||"contractspec",$=K(Z,B,X,Q),U="";if($.imports&&$.imports.length>0)U=$.imports.join(`
126
+ `);else if($.fields.length>0)U=k($.fields,Q);return`
127
+ ${U}
2250
128
 
2251
- // src/openapi/parser/document.ts
2252
- function parseOpenApiDocument(doc, _options = {}) {
2253
- const version = detectVersion(doc);
2254
- const warnings = [];
2255
- const operations = [];
2256
- for (const [path, pathItem] of Object.entries(doc.paths ?? {})) {
2257
- if (!pathItem)
2258
- continue;
2259
- const pathParams = pathItem.parameters;
2260
- for (const method of HTTP_METHODS) {
2261
- const operation = pathItem[method];
2262
- if (operation) {
2263
- try {
2264
- operations.push(parseOperation(doc, method, path, operation, pathParams));
2265
- } catch (error) {
2266
- warnings.push(`Failed to parse ${method.toUpperCase()} ${path}: ${error}`);
2267
- }
2268
- }
2269
- }
2270
- }
2271
- const schemas2 = {};
2272
- const components = doc.components;
2273
- if (components?.schemas) {
2274
- for (const [name, schema] of Object.entries(components.schemas)) {
2275
- schemas2[name] = schema;
2276
- }
2277
- }
2278
- const servers = (doc.servers ?? []).map((s) => ({
2279
- url: s.url,
2280
- description: s.description,
2281
- variables: s.variables
2282
- }));
2283
- const events2 = [];
2284
- if ("webhooks" in doc && doc.webhooks) {
2285
- for (const [name, pathItem] of Object.entries(doc.webhooks)) {
2286
- if (typeof pathItem !== "object" || !pathItem)
2287
- continue;
2288
- const operation = pathItem["post"];
2289
- if (operation && operation.requestBody) {
2290
- if ("$ref" in operation.requestBody) {
2291
- throw new Error(`'$ref' isn't supported`);
2292
- }
2293
- const content = operation.requestBody.content?.["application/json"];
2294
- if (content?.schema) {
2295
- events2.push({
2296
- name,
2297
- description: operation.summary || operation.description,
2298
- payload: content.schema
2299
- });
2300
- }
2301
- }
2302
- }
2303
- }
2304
- return {
2305
- document: doc,
2306
- version,
2307
- info: {
2308
- title: doc.info.title,
2309
- version: doc.info.version,
2310
- description: doc.info.description
2311
- },
2312
- operations,
2313
- schemas: schemas2,
2314
- servers,
2315
- warnings,
2316
- events: events2
2317
- };
2318
- }
2319
- async function parseOpenApi(source, options = {}) {
2320
- const {
2321
- fetch: fetchFn = globalThis.fetch,
2322
- readFile,
2323
- timeout = 30000
2324
- } = options;
2325
- let content;
2326
- let format;
2327
- if (source.startsWith("http://") || source.startsWith("https://")) {
2328
- const controller = new AbortController;
2329
- const timeoutId = setTimeout(() => controller.abort(), timeout);
2330
- try {
2331
- const response = await fetchFn(source, { signal: controller.signal });
2332
- if (!response.ok) {
2333
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
2334
- }
2335
- content = await response.text();
2336
- } finally {
2337
- clearTimeout(timeoutId);
2338
- }
2339
- if (source.endsWith(".yaml") || source.endsWith(".yml")) {
2340
- format = "yaml";
2341
- } else if (source.endsWith(".json")) {
2342
- format = "json";
2343
- } else {
2344
- format = detectFormat(content);
2345
- }
2346
- } else {
2347
- if (!readFile) {
2348
- throw new Error("readFile adapter required for file paths");
2349
- }
2350
- content = await readFile(source);
2351
- if (source.endsWith(".yaml") || source.endsWith(".yml")) {
2352
- format = "yaml";
2353
- } else if (source.endsWith(".json")) {
2354
- format = "json";
2355
- } else {
2356
- format = detectFormat(content);
2357
- }
2358
- }
2359
- const doc = parseOpenApiString(content, format);
2360
- return parseOpenApiDocument(doc, options);
2361
- }
2362
- // src/openapi/index.ts
2363
- var tech_contracts_openapi_import_DocBlocks = [
2364
- {
2365
- id: "docs.tech.contracts.openapi-import",
2366
- title: "OpenAPI Import (OpenAPI 3.1) to ContractSpec",
2367
- summary: "Import OpenAPI specifications into ContractSpec models, or generate pure Zod/JSON-Schema/GraphQL representations.",
2368
- kind: "reference",
2369
- visibility: "public",
2370
- route: "/docs/tech/contracts/openapi-import",
2371
- tags: ["contracts", "openapi", "import", "codegen"],
2372
- body: `## OpenAPI Import (OpenAPI 3.1)
129
+ ${$.code}
130
+ `.trim()}function DA(A){let Z={};if(A.pathParams.length>0)Z.params={type:"object",properties:A.pathParams.reduce((X,$)=>{return X[$.name]=$.schema,X},{}),required:A.pathParams.map((X)=>X.name)};if(A.queryParams.length>0)Z.query={type:"object",properties:A.queryParams.reduce((X,$)=>{return X[$.name]=$.schema,X},{}),required:A.queryParams.filter((X)=>X.required).map((X)=>X.name)};let Q=["authorization","content-type","accept","user-agent"],B=A.headerParams.filter((X)=>!Q.includes(X.name.toLowerCase()));if(B.length>0)Z.headers={type:"object",properties:B.reduce((X,$)=>{return X[$.name]=$.schema,X},{}),required:B.filter((X)=>X.required).map((X)=>X.name)};if(A.requestBody?.schema)Z.body=A.requestBody.schema;return Z}function EA(A){let Z=["200","201","202","204"];for(let Q of Z){let B=A.responses[Q];if(B?.schema)return B.schema}for(let[Q,B]of Object.entries(A.responses))if(Q.startsWith("2")&&B.schema)return B.schema;return null}var $Z=(A,Z,Q={})=>{let{tags:B,exclude:X=[],include:$}=Q,U=[],J=[],D=[];for(let W of A.operations){if(B&&B.length>0){if(!W.tags.some((Y)=>B.includes(Y))){J.push({sourceId:W.operationId,reason:`No matching tags (has: ${W.tags.join(", ")})`});continue}}if($&&$.length>0){if(!$.includes(W.operationId)){J.push({sourceId:W.operationId,reason:"Not in include list"});continue}}else if(X.includes(W.operationId)){J.push({sourceId:W.operationId,reason:"In exclude list"});continue}if(W.deprecated&&Q.defaultStability!=="deprecated"){J.push({sourceId:W.operationId,reason:"Deprecated operation"});continue}try{let E=DA(W),Y=Q.schemaFormat||Z.schemaFormat||"contractspec",_=E.body?K(E.body,`${W.operationId}Input`,Y,Z):null,H=E.query?K(E.query,`${W.operationId}Query`,Y,Z):null,L=E.params?K(E.params,`${W.operationId}Params`,Y,Z):null,x=E.headers?K(E.headers,`${W.operationId}Headers`,Y,Z):null,G=EA(W),I=G?K(G,`${W.operationId}Output`,Y,Z):null;if(I&&Y==="contractspec"&&!I.code.includes("defineSchemaModel"))I=null;let C=WA(W,Z,Q,_,I,H,L,x),j=T(W.operationId,Q.prefix),v=O(j),z={rest:{method:W.method.toUpperCase(),path:W.path,params:{path:W.pathParams.map((R)=>R.name),query:W.queryParams.map((R)=>R.name),header:W.headerParams.map((R)=>R.name),cookie:W.cookieParams.map((R)=>R.name)}}},N={type:"openapi",sourceId:W.operationId,operationId:W.operationId,openApiVersion:A.version,importedAt:new Date},lA=vA(W,Z.conventions);U.push({code:C,fileName:v,groupFolder:lA||void 0,source:N,transportHints:z})}catch(E){D.push({sourceId:W.operationId,error:E instanceof Error?E.message:String(E)})}}for(let[W,E]of Object.entries(A.schemas))try{let Y=yA(W,E,{...Z,schemaFormat:Q.schemaFormat||Z.schemaFormat}),_=O(T(W,Q.prefix)),H=OA(W,Z.conventions);U.push({code:Y,fileName:_,groupFolder:H||void 0,source:{type:"openapi",sourceId:W,operationId:W,openApiVersion:A.version,importedAt:new Date},transportHints:{}})}catch(Y){D.push({sourceId:W,error:Y instanceof Error?"Model conversion failed: "+Y.message:String(Y)})}for(let W of A.events)try{let E=wA(W,{...Z,schemaFormat:Q.schemaFormat||Z.schemaFormat}),Y=O(T(W.name,Q.prefix)),_=bA(W.name,Z.conventions);U.push({code:E,fileName:Y,groupFolder:_||void 0,source:{type:"openapi",sourceId:W.name,operationId:W.name,openApiVersion:A.version,importedAt:new Date},transportHints:{}})}catch(E){D.push({sourceId:W.name,error:E instanceof Error?"Event conversion failed: "+E.message:String(E)})}return{operationSpecs:U,skipped:J,errors:D,summary:{total:A.operations.length+Object.keys(A.schemas).length+A.events.length,imported:U.length,skipped:J.length,errors:D.length}}};function UZ(A,Z={},Q){let B=DA(A),X=Z.schemaFormat||Q.schemaFormat||"contractspec",$=B.body?K(B.body,`${A.operationId}Input`,X,Q):null,U=B.query?K(B.query,`${A.operationId}Query`,X,Q):null,J=B.params?K(B.params,`${A.operationId}Params`,X,Q):null,D=B.headers?K(B.headers,`${A.operationId}Headers`,X,Q):null,W=EA(A),E=W?K(W,`${A.operationId}Output`,X,Q):null;return WA(A,Q,Z,$,E,U,J,D)}function w(A){return typeof A==="object"&&A!==null&&"$ref"in A}function S(A,Z){if(!Z.startsWith("#/"))return;let Q=Z.slice(2).split("/"),B=A;for(let X of Q){if(B===null||B===void 0)return;if(typeof B!=="object")return;B=B[X]}return B}function q(A,Z,Q=new Set){if(!Z)return;if(w(Z)){if(Q.has(Z.$ref))return Z;let $=new Set(Q);$.add(Z.$ref);let U=S(A,Z.$ref);if(!U)return Z;let J=q(A,U,$);if(!J)return Z;let D=Z.$ref.split("/"),W=D[D.length-1];return{...J,_originalRef:Z.$ref,_originalTypeName:W}}let B={...Z};if(B.properties){let $=B.properties,U={};for(let[J,D]of Object.entries($))U[J]=q(A,D,Q)??D;B.properties=U}if(B.items)B.items=q(A,B.items,Q);let X=["allOf","anyOf","oneOf"];for(let $ of X)if(Array.isArray(B[$]))B[$]=B[$].map((U)=>q(A,U,Q)??U);return B}function fA(A,Z){let Q={path:[],query:[],header:[],cookie:[]};if(!Z)return Q;for(let B of Z){let X;if(w(B)){let U=S(A,B.$ref);if(!U)continue;X=U}else X=B;let $={name:X.name,in:X.in,required:X.required??X.in==="path",description:X.description,schema:q(A,X.schema),deprecated:X.deprecated??!1};Q[X.in]?.push($)}return Q}import{parse as JZ}from"yaml";var gA=["get","post","put","delete","patch","head","options","trace"];function _A(A,Z="json"){if(Z==="yaml")return JZ(A);return JSON.parse(A)}function d(A){let Z=A.trim();if(Z.startsWith("{")||Z.startsWith("["))return"json";return"yaml"}function LA(A){if(A.openapi.startsWith("3.1"))return"3.1";return"3.0"}function uA(A,Z){let Q=Z.split("/").filter(Boolean).map((B)=>{if(B.startsWith("{")&&B.endsWith("}"))return"By"+B.slice(1,-1).charAt(0).toUpperCase()+B.slice(2,-1);return B.charAt(0).toUpperCase()+B.slice(1)});return A+Q.join("")}function hA(A,Z,Q,B,X){let $=[...X??[],...B.parameters??[]],U=fA(A,$),J;if(B.requestBody){let E=w(B.requestBody)?S(A,B.requestBody.$ref):B.requestBody;if(E){let Y=Object.keys(E.content??{})[0]??"application/json",_=E.content?.[Y];if(_?.schema)J={required:E.required??!1,schema:q(A,_.schema)??{},contentType:Y}}}let D={};for(let[E,Y]of Object.entries(B.responses??{})){let _=w(Y)?S(A,Y.$ref):Y;if(_){let H=Object.keys(_.content??{})[0],L=H?_.content?.[H]:void 0;D[E]={description:_.description,schema:L?.schema?q(A,L.schema):void 0,contentType:H}}}let W=B?.["x-contractspec"];return{operationId:B.operationId??uA(Z,Q),method:Z,path:Q,summary:B.summary,description:B.description,tags:B.tags??[],pathParams:U.path,queryParams:U.query,headerParams:U.header,cookieParams:U.cookie,requestBody:J,responses:D,deprecated:B.deprecated??!1,security:B.security,contractSpecMeta:W}}function dA(A,Z={}){let Q=LA(A),B=[],X=[];for(let[W,E]of Object.entries(A.paths??{})){if(!E)continue;let Y=E.parameters;for(let _ of gA){let H=E[_];if(H)try{X.push(hA(A,_,W,H,Y))}catch(L){B.push(`Failed to parse ${_.toUpperCase()} ${W}: ${L}`)}}}let $={},U=A.components;if(U?.schemas)for(let[W,E]of Object.entries(U.schemas))$[W]=E;let J=(A.servers??[]).map((W)=>({url:W.url,description:W.description,variables:W.variables})),D=[];if("webhooks"in A&&A.webhooks)for(let[W,E]of Object.entries(A.webhooks)){if(typeof E!=="object"||!E)continue;let Y=E.post;if(Y&&Y.requestBody){if("$ref"in Y.requestBody)throw Error("'$ref' isn't supported");let _=Y.requestBody.content?.["application/json"];if(_?.schema)D.push({name:W,description:Y.summary||Y.description,payload:_.schema})}}return{document:A,version:Q,info:{title:A.info.title,version:A.info.version,description:A.info.description},operations:X,schemas:$,servers:J,warnings:B,events:D}}async function WZ(A,Z={}){let{fetch:Q=globalThis.fetch,readFile:B,timeout:X=30000}=Z,$,U;if(A.startsWith("http://")||A.startsWith("https://")){let D=new AbortController,W=setTimeout(()=>D.abort(),X);try{let E=await Q(A,{signal:D.signal});if(!E.ok)throw Error(`HTTP ${E.status}: ${E.statusText}`);$=await E.text()}finally{clearTimeout(W)}if(A.endsWith(".yaml")||A.endsWith(".yml"))U="yaml";else if(A.endsWith(".json"))U="json";else U=d($)}else{if(!B)throw Error("readFile adapter required for file paths");if($=await B(A),A.endsWith(".yaml")||A.endsWith(".yml"))U="yaml";else if(A.endsWith(".json"))U="json";else U=d($)}let J=_A($,U);return dA(J,Z)}var a1=[{id:"docs.tech.contracts.openapi-import",title:"OpenAPI Import (OpenAPI 3.1) to ContractSpec",summary:"Import OpenAPI specifications into ContractSpec models, or generate pure Zod/JSON-Schema/GraphQL representations.",kind:"reference",visibility:"public",route:"/docs/tech/contracts/openapi-import",tags:["contracts","openapi","import","codegen"],body:`## OpenAPI Import (OpenAPI 3.1)
2373
131
 
2374
132
  ### Purpose
2375
133
 
@@ -2412,65 +170,4 @@ contractspec openapi import --file api.json --output ./src/zod --schema-format z
2412
170
  \`\`\`bash
2413
171
  contractspec openapi import --file <path-or-url> --output <dir> [--schema-format <format>]
2414
172
  \`\`\`
2415
- `
2416
- }
2417
- ];
2418
- export {
2419
- toValidIdentifier,
2420
- toSpecKey,
2421
- toSnakeCase,
2422
- toSchemaName,
2423
- toRestPath,
2424
- toPascalCase,
2425
- toOperationId,
2426
- toKebabCase,
2427
- toHttpMethod,
2428
- toFileName,
2429
- toCamelCase,
2430
- tech_contracts_openapi_import_DocBlocks,
2431
- schemaModelToJsonSchema,
2432
- parseOpenApiString,
2433
- parseOpenApiDocument,
2434
- parseOpenApi,
2435
- openApiToYaml,
2436
- openApiToJson,
2437
- openApiForRegistry,
2438
- normalizePath,
2439
- jsonSchemaToType,
2440
- jsonSchemaForSpec,
2441
- importOperation,
2442
- importFromOpenApi,
2443
- getScalarType,
2444
- getByPath,
2445
- generateWorkflowsRegistry,
2446
- generateSchemaModelCode,
2447
- generateRegistryIndex,
2448
- generatePresentationsRegistry,
2449
- generateOperationsRegistry,
2450
- generateImports,
2451
- generateFormsRegistry,
2452
- generateFeaturesRegistry,
2453
- generateEventsExports,
2454
- generateDataViewsRegistry,
2455
- formatDiffChanges,
2456
- extractPathParams,
2457
- exportWorkflows,
2458
- exportPresentationsFromArray,
2459
- exportPresentations,
2460
- exportOperations,
2461
- exportForms,
2462
- exportFeatures,
2463
- exportEvents,
2464
- exportDataViews,
2465
- exportContractSpec,
2466
- diffSpecs,
2467
- diffSpecVsOperation,
2468
- diffAll,
2469
- detectVersion,
2470
- detectFormat,
2471
- defaultRestPath,
2472
- deepEqual,
2473
- createSpecDiff,
2474
- contractSpecToYaml,
2475
- contractSpecToJson
2476
- };
173
+ `}];export{F as toValidIdentifier,T as toSpecKey,YZ as toSnakeCase,a as toSchemaName,KA as toRestPath,P as toPascalCase,s as toOperationId,V as toKebabCase,GA as toHttpMethod,O as toFileName,mA as toCamelCase,a1 as tech_contracts_openapi_import_DocBlocks,o as schemaModelToJsonSchema,_A as parseOpenApiString,dA as parseOpenApiDocument,WZ as parseOpenApi,eA as openApiToYaml,sA as openApiToJson,$A as openApiForRegistry,_Z as normalizePath,u as jsonSchemaToType,xA as jsonSchemaForSpec,UZ as importOperation,$Z as importFromOpenApi,h as getScalarType,DZ as getByPath,XA as generateWorkflowsRegistry,K as generateSchemaModelCode,BA as generateRegistryIndex,ZA as generatePresentationsRegistry,e as generateOperationsRegistry,k as generateImports,t as generateFormsRegistry,n as generateFeaturesRegistry,p as generateEventsExports,m as generateDataViewsRegistry,iA as formatDiffChanges,EZ as extractPathParams,QA as exportWorkflows,oA as exportPresentationsFromArray,AA as exportPresentations,g as exportOperations,r as exportForms,i as exportFeatures,c as exportEvents,l as exportDataViews,UA as exportContractSpec,IA as diffSpecs,cA as diffSpecVsOperation,pA as diffAll,LA as detectVersion,d as detectFormat,f as defaultRestPath,b as deepEqual,PA as createSpecDiff,ZZ as contractSpecToYaml,AZ as contractSpecToJson};