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