@apollo-deploy/tesseract 1.9.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (194) hide show
  1. package/README.md +218 -110
  2. package/dist/adapters/csharp/index.d.ts +5 -8
  3. package/dist/adapters/csharp/index.d.ts.map +1 -1
  4. package/dist/adapters/csharp/index.js +314 -74
  5. package/dist/adapters/csharp/index.js.map +1 -1
  6. package/dist/adapters/documentation.d.ts +17 -0
  7. package/dist/adapters/documentation.d.ts.map +1 -0
  8. package/dist/adapters/documentation.js +268 -0
  9. package/dist/adapters/documentation.js.map +1 -0
  10. package/dist/adapters/go/index.d.ts +5 -8
  11. package/dist/adapters/go/index.d.ts.map +1 -1
  12. package/dist/adapters/go/index.js +197 -101
  13. package/dist/adapters/go/index.js.map +1 -1
  14. package/dist/adapters/kotlin/index.d.ts +5 -9
  15. package/dist/adapters/kotlin/index.d.ts.map +1 -1
  16. package/dist/adapters/kotlin/index.js +280 -212
  17. package/dist/adapters/kotlin/index.js.map +1 -1
  18. package/dist/adapters/php/index.d.ts +5 -15
  19. package/dist/adapters/php/index.d.ts.map +1 -1
  20. package/dist/adapters/php/index.js +362 -90
  21. package/dist/adapters/php/index.js.map +1 -1
  22. package/dist/adapters/python/index.d.ts +5 -8
  23. package/dist/adapters/python/index.d.ts.map +1 -1
  24. package/dist/adapters/python/index.js +240 -109
  25. package/dist/adapters/python/index.js.map +1 -1
  26. package/dist/adapters/ruby/index.d.ts +3 -3
  27. package/dist/adapters/ruby/index.d.ts.map +1 -1
  28. package/dist/adapters/ruby/index.js +235 -46
  29. package/dist/adapters/ruby/index.js.map +1 -1
  30. package/dist/adapters/rust/index.d.ts +5 -9
  31. package/dist/adapters/rust/index.d.ts.map +1 -1
  32. package/dist/adapters/rust/index.js +237 -169
  33. package/dist/adapters/rust/index.js.map +1 -1
  34. package/dist/adapters/shared.d.ts +7 -0
  35. package/dist/adapters/shared.d.ts.map +1 -0
  36. package/dist/adapters/shared.js +68 -0
  37. package/dist/adapters/shared.js.map +1 -0
  38. package/dist/adapters/type-utils.d.ts +13 -0
  39. package/dist/adapters/type-utils.d.ts.map +1 -0
  40. package/dist/adapters/type-utils.js +105 -0
  41. package/dist/adapters/type-utils.js.map +1 -0
  42. package/dist/adapters/types.d.ts.map +1 -1
  43. package/dist/adapters/typescript/emitter/emitSchema.d.ts +2 -2
  44. package/dist/adapters/typescript/emitter/emitSchema.js +39 -27
  45. package/dist/adapters/typescript/emitter/emitSchema.js.map +1 -1
  46. package/dist/adapters/typescript/emitter/emitTypeScriptModels.d.ts +2 -2
  47. package/dist/adapters/typescript/emitter/emitTypeScriptModels.js +15 -15
  48. package/dist/adapters/typescript/emitter/importCollector.d.ts +2 -4
  49. package/dist/adapters/typescript/emitter/importCollector.d.ts.map +1 -1
  50. package/dist/adapters/typescript/emitter/importCollector.js +40 -50
  51. package/dist/adapters/typescript/emitter/importCollector.js.map +1 -1
  52. package/dist/adapters/typescript/emitter/parsers.d.ts +1 -1
  53. package/dist/adapters/typescript/emitter/parsers.js +13 -13
  54. package/dist/adapters/typescript/emitter/typeHoister.d.ts +1 -1
  55. package/dist/adapters/typescript/emitter/typeHoister.d.ts.map +1 -1
  56. package/dist/adapters/typescript/emitter/typeHoister.js +12 -20
  57. package/dist/adapters/typescript/emitter/typeHoister.js.map +1 -1
  58. package/dist/adapters/typescript/index.d.ts +3 -3
  59. package/dist/adapters/typescript/index.d.ts.map +1 -1
  60. package/dist/adapters/typescript/index.js +148 -166
  61. package/dist/adapters/typescript/index.js.map +1 -1
  62. package/dist/adapters/typescript/shared.d.ts +2 -2
  63. package/dist/adapters/typescript/shared.js +12 -12
  64. package/dist/cli.js +50 -37
  65. package/dist/cli.js.map +1 -1
  66. package/dist/collector.d.ts +13 -3
  67. package/dist/collector.d.ts.map +1 -1
  68. package/dist/collector.js +69 -32
  69. package/dist/collector.js.map +1 -1
  70. package/dist/elysia.js +2 -2
  71. package/dist/elysia.js.map +1 -1
  72. package/dist/express.d.ts.map +1 -1
  73. package/dist/express.js.map +1 -1
  74. package/dist/fastify.d.ts +7 -0
  75. package/dist/fastify.d.ts.map +1 -1
  76. package/dist/fastify.js +39 -20
  77. package/dist/fastify.js.map +1 -1
  78. package/dist/helpers/handlebars.d.ts +7 -16
  79. package/dist/helpers/handlebars.d.ts.map +1 -1
  80. package/dist/helpers/handlebars.js +127 -177
  81. package/dist/helpers/handlebars.js.map +1 -1
  82. package/dist/hono.d.ts.map +1 -1
  83. package/dist/hono.js.map +1 -1
  84. package/dist/index.d.ts +14 -12
  85. package/dist/index.d.ts.map +1 -1
  86. package/dist/index.js +32 -30
  87. package/dist/index.js.map +1 -1
  88. package/dist/koa.d.ts.map +1 -1
  89. package/dist/koa.js.map +1 -1
  90. package/dist/nestjs.d.ts.map +1 -1
  91. package/dist/nestjs.js +41 -14
  92. package/dist/nestjs.js.map +1 -1
  93. package/dist/pipeline/enrich.d.ts +2 -13
  94. package/dist/pipeline/enrich.d.ts.map +1 -1
  95. package/dist/pipeline/enrich.js +157 -212
  96. package/dist/pipeline/enrich.js.map +1 -1
  97. package/dist/pipeline/intake.d.ts +3 -3
  98. package/dist/pipeline/intake.d.ts.map +1 -1
  99. package/dist/pipeline/intake.js +342 -238
  100. package/dist/pipeline/intake.js.map +1 -1
  101. package/dist/pipeline/write.d.ts +1 -14
  102. package/dist/pipeline/write.d.ts.map +1 -1
  103. package/dist/pipeline/write.js +73 -113
  104. package/dist/pipeline/write.js.map +1 -1
  105. package/dist/types/config.d.ts +14 -26
  106. package/dist/types/config.d.ts.map +1 -1
  107. package/dist/types/config.js +16 -12
  108. package/dist/types/config.js.map +1 -1
  109. package/dist/types/ir.d.ts +12 -8
  110. package/dist/types/ir.d.ts.map +1 -1
  111. package/dist/types/manifest.d.ts +10 -11
  112. package/dist/types/manifest.d.ts.map +1 -1
  113. package/dist/types/manifest.js +54 -14
  114. package/dist/types/manifest.js.map +1 -1
  115. package/dist/types/sdk-module.d.ts +2 -2
  116. package/dist/types/sdk-module.js +1 -1
  117. package/dist/utils/format.d.ts +1 -3
  118. package/dist/utils/format.d.ts.map +1 -1
  119. package/dist/utils/format.js +3 -10
  120. package/dist/utils/format.js.map +1 -1
  121. package/dist/utils/naming.d.ts.map +1 -1
  122. package/dist/utils/naming.js +174 -184
  123. package/dist/utils/naming.js.map +1 -1
  124. package/dist/utils/npm-token.d.ts +1 -17
  125. package/dist/utils/npm-token.d.ts.map +1 -1
  126. package/dist/utils/npm-token.js +27 -76
  127. package/dist/utils/npm-token.js.map +1 -1
  128. package/dist/utils/version.d.ts +14 -0
  129. package/dist/utils/version.d.ts.map +1 -0
  130. package/dist/utils/version.js +60 -0
  131. package/dist/utils/version.js.map +1 -0
  132. package/package.json +4 -3
  133. package/templates/csharp/client.hbs +26 -49
  134. package/templates/csharp/csproj.hbs +7 -0
  135. package/templates/csharp/domain.hbs +48 -130
  136. package/templates/csharp/errors.hbs +17 -25
  137. package/templates/csharp/readme.hbs +31 -44
  138. package/templates/csharp/transport.hbs +238 -136
  139. package/templates/csharp/types.hbs +30 -76
  140. package/templates/go/client.hbs +44 -109
  141. package/templates/go/domain.hbs +83 -25
  142. package/templates/go/go-mod.hbs +1 -4
  143. package/templates/go/readme.hbs +32 -29
  144. package/templates/go/transport.hbs +174 -173
  145. package/templates/go/types.hbs +17 -53
  146. package/templates/go/utils.hbs +3 -2
  147. package/templates/kotlin/build-gradle.hbs +1 -1
  148. package/templates/kotlin/client.hbs +18 -15
  149. package/templates/kotlin/domain.hbs +54 -51
  150. package/templates/kotlin/errors.hbs +1 -1
  151. package/templates/kotlin/readme.hbs +35 -137
  152. package/templates/kotlin/transport.hbs +140 -123
  153. package/templates/kotlin/types.hbs +30 -81
  154. package/templates/php/client.hbs +8 -46
  155. package/templates/php/composer.hbs +2 -1
  156. package/templates/php/domain.hbs +86 -117
  157. package/templates/php/errors.hbs +1 -1
  158. package/templates/php/readme.hbs +42 -49
  159. package/templates/php/transport.hbs +192 -145
  160. package/templates/php/types.hbs +63 -60
  161. package/templates/python/client.hbs +15 -2
  162. package/templates/python/domain.hbs +50 -108
  163. package/templates/python/pyproject.hbs +5 -0
  164. package/templates/python/readme.hbs +47 -30
  165. package/templates/python/resources-init.hbs +1 -1
  166. package/templates/python/transport.hbs +167 -53
  167. package/templates/python/types.hbs +22 -58
  168. package/templates/ruby/client.hbs +10 -6
  169. package/templates/ruby/domain.hbs +48 -67
  170. package/templates/ruby/errors.hbs +3 -3
  171. package/templates/ruby/gemspec.hbs +3 -10
  172. package/templates/ruby/index.hbs +3 -3
  173. package/templates/ruby/readme.hbs +33 -70
  174. package/templates/ruby/transport.hbs +129 -103
  175. package/templates/ruby/types.hbs +11 -16
  176. package/templates/ruby/version.hbs +1 -1
  177. package/templates/rust/cargo.hbs +2 -0
  178. package/templates/rust/client.hbs +10 -1
  179. package/templates/rust/domain.hbs +69 -52
  180. package/templates/rust/error.hbs +3 -1
  181. package/templates/rust/lib.hbs +4 -5
  182. package/templates/rust/readme.hbs +33 -46
  183. package/templates/rust/transport.hbs +170 -159
  184. package/templates/rust/types.hbs +34 -111
  185. package/templates/typescript/domain-class.hbs +14 -6
  186. package/templates/typescript/domain.hbs +13 -5
  187. package/templates/typescript/package-json.hbs +3 -1
  188. package/templates/typescript/readme.hbs +42 -42
  189. package/templates/typescript/transport.hbs +46 -8
  190. package/templates/typescript/types-errors.hbs +1 -1
  191. package/dist/adapters/typescript/model-emitter.d.ts +0 -13
  192. package/dist/adapters/typescript/model-emitter.d.ts.map +0 -1
  193. package/dist/adapters/typescript/model-emitter.js +0 -415
  194. package/dist/adapters/typescript/model-emitter.js.map +0 -1
@@ -3,10 +3,10 @@
3
3
  *
4
4
  * This is the only intake path in Tesseract. No OpenAPI parsing.
5
5
  */
6
- import { readFileSync } from "node:fs";
7
- import { camelCase, pascalCase, kebabCase } from "change-case";
8
- import { deriveCleanMethodName } from "../utils/naming.js";
9
- import { validateManifest } from "../types/manifest.js";
6
+ import { readFileSync } from 'node:fs';
7
+ import { camelCase, pascalCase, kebabCase } from 'change-case';
8
+ import { deriveCleanMethodName } from '../utils/naming.js';
9
+ import { validateManifest } from '../types/manifest.js';
10
10
  // ── Entry point ──────────────────────────────────────────────────────────────
11
11
  export function intake(config) {
12
12
  let manifest;
@@ -15,17 +15,16 @@ export function intake(config) {
15
15
  }
16
16
  else {
17
17
  try {
18
- const raw = readFileSync(config.input, "utf-8");
19
- manifest = JSON.parse(raw);
18
+ manifest = JSON.parse(readFileSync(config.input, 'utf8'));
20
19
  }
21
- catch (err) {
22
- throw new Error(`Failed to read manifest at "${config.input}": ${err instanceof Error ? err.message : String(err)}`);
23
- }
24
- const validation = validateManifest(manifest);
25
- if (!validation.valid) {
26
- throw new Error(`Invalid manifest:\n ${validation.errors.join("\n ")}`);
20
+ catch (error) {
21
+ throw new Error(`Failed to read manifest at "${config.input}": ${error instanceof Error ? error.message : String(error)}`);
27
22
  }
28
23
  }
24
+ const validation = validateManifest(manifest);
25
+ if (!validation.valid) {
26
+ throw new Error(`Invalid manifest:\n ${validation.errors.join('\n ')}`);
27
+ }
29
28
  const schemaPackage = manifest.schemaPackage;
30
29
  const importPath = schemaPackage?.importPath ?? schemaPackage?.name;
31
30
  const externalRefNames = schemaPackage
@@ -38,11 +37,12 @@ export function intake(config) {
38
37
  isEnum: false,
39
38
  properties: [],
40
39
  ownership: {
41
- kind: "external",
40
+ kind: 'external',
42
41
  externalImport: {
43
42
  packageName: schemaPackage.name,
44
43
  importPath: importPath,
45
44
  exportName: name,
45
+ version: schemaPackage.version,
46
46
  },
47
47
  },
48
48
  }))
@@ -56,7 +56,7 @@ export function intake(config) {
56
56
  const groups = [];
57
57
  const allInlineSchemas = [];
58
58
  for (const domain of manifest.domains) {
59
- const { group, inlineSchemas } = buildOperationGroup(domain, schemas);
59
+ const { group, inlineSchemas } = buildOperationGroup(domain);
60
60
  groups.push(group);
61
61
  allInlineSchemas.push(...inlineSchemas);
62
62
  }
@@ -67,8 +67,9 @@ export function intake(config) {
67
67
  // Only set packageVersion when the caller explicitly supplied it.
68
68
  // When undefined the TypeScript adapter queries npm and bumps the patch.
69
69
  packageVersion: config.packageVersion,
70
+ versionBump: config.versionBump ?? manifest.info.versionBump ?? 'patch',
70
71
  description: manifest.info.description,
71
- baseUrl: config.baseUrl ?? manifest.info.baseUrl ?? "http://localhost",
72
+ baseUrl: config.baseUrl ?? manifest.info.baseUrl ?? 'http://localhost',
72
73
  packageName: config.packageName ?? kebabCase(manifest.info.title).toLowerCase(),
73
74
  clientName: config.clientName ?? pascalCase(manifest.info.title),
74
75
  schemaPackage: schemaPackage
@@ -101,19 +102,68 @@ function collectExternalRefNames(manifest) {
101
102
  scanSchemaForExternalRefs(resp, names);
102
103
  }
103
104
  }
104
- // Register sseReturnType as an external ref so it gets an import stub
105
- if (route.sdk?.sseReturnType) {
106
- names.add(pascalCase(route.sdk.sseReturnType));
107
- }
105
+ collectNamedTypes(route.sdk?.sseReturnType, names);
106
+ collectNamedTypes(route.sdk?.returnType, names);
107
+ }
108
+ }
109
+ // Transitively pull nested refs from matching definitions (e.g. Pet → Tag)
110
+ // so dependent models are also imported from schemaPackage instead of
111
+ // regenerated locally.
112
+ const definitions = manifest.definitions ?? {};
113
+ const defByName = new Map(Object.entries(definitions).map(([k, v]) => [
114
+ pascalCase(k),
115
+ v,
116
+ ]));
117
+ let growing = true;
118
+ while (growing) {
119
+ growing = false;
120
+ for (const name of [...names]) {
121
+ const def = defByName.get(name);
122
+ if (!def)
123
+ continue;
124
+ const before = names.size;
125
+ scanSchemaForExternalRefs(def, names);
126
+ if (names.size > before)
127
+ growing = true;
108
128
  }
109
129
  }
110
130
  return names;
111
131
  }
132
+ const BUILT_IN_TYPES = new Set([
133
+ 'Array',
134
+ 'Record',
135
+ 'Promise',
136
+ 'AsyncIterable',
137
+ 'SSEEvent',
138
+ 'Date',
139
+ 'ReadonlyArray',
140
+ 'Map',
141
+ 'Set',
142
+ 'Partial',
143
+ 'Pick',
144
+ 'Omit',
145
+ 'Required',
146
+ 'Readonly',
147
+ ]);
148
+ function collectNamedTypes(type, out) {
149
+ if (!type)
150
+ return;
151
+ const unquoted = type.replace(/(["'`])(?:\\.|(?!\1).)*\1/g, '');
152
+ for (const name of unquoted.match(/\b[A-Z][A-Za-z0-9_]*\b/g) ?? []) {
153
+ if (!BUILT_IN_TYPES.has(name))
154
+ out.add(name);
155
+ }
156
+ }
112
157
  function scanSchemaForExternalRefs(schema, out) {
113
158
  const s = schema;
114
- if (typeof s.$ref === "string") {
115
- if (!s.$ref.startsWith("#")) {
116
- out.add(pascalCase(s.$ref.split("/").pop()));
159
+ if (typeof s.$ref === 'string') {
160
+ // Bare refs (`User`) and local definition refs (`#/definitions/User`,
161
+ // `#/$defs/User`) all resolve to a named type. When schemaPackage is set,
162
+ // intake treats those names as external imports instead of regenerating.
163
+ const ref = s.$ref;
164
+ const isLocalDef = ref.startsWith('#/definitions/') || ref.startsWith('#/$defs/');
165
+ if (!ref.startsWith('#') || isLocalDef) {
166
+ out.add(pascalCase(ref.split('/').pop()));
117
167
  }
118
168
  return;
119
169
  }
@@ -122,12 +172,12 @@ function scanSchemaForExternalRefs(schema, out) {
122
172
  for (const v of Object.values(props))
123
173
  scanSchemaForExternalRefs(v, out);
124
174
  }
125
- if (s.additionalProperties && typeof s.additionalProperties === "object") {
175
+ if (s.additionalProperties && typeof s.additionalProperties === 'object') {
126
176
  scanSchemaForExternalRefs(s.additionalProperties, out);
127
177
  }
128
178
  if (s.items)
129
179
  scanSchemaForExternalRefs(s.items, out);
130
- for (const key of ["allOf", "oneOf", "anyOf"]) {
180
+ for (const key of ['allOf', 'oneOf', 'anyOf']) {
131
181
  const arr = s[key];
132
182
  if (Array.isArray(arr)) {
133
183
  for (const m of arr)
@@ -141,17 +191,28 @@ function buildSchemas(definitions) {
141
191
  }
142
192
  function jsonSchemaToDefinition(name, schema) {
143
193
  const s = schema;
144
- if ("enum" in s && Array.isArray(s.enum)) {
194
+ if ('enum' in s && Array.isArray(s.enum)) {
195
+ const values = s.enum;
196
+ if (values.every((value) => typeof value === 'string' || typeof value === 'number')) {
197
+ return {
198
+ name,
199
+ description: s.description,
200
+ isEnum: true,
201
+ enumValues: values,
202
+ properties: [],
203
+ };
204
+ }
145
205
  return {
146
206
  name,
147
207
  description: s.description,
148
- isEnum: true,
149
- enumValues: s.enum.filter((v) => v !== null),
208
+ isEnum: false,
209
+ isUnionType: true,
210
+ unionMembers: [...new Set(values.map((value) => JSON.stringify(value)))],
150
211
  properties: [],
151
212
  isTypeAlias: true,
152
213
  };
153
214
  }
154
- if ("oneOf" in s && Array.isArray(s.oneOf)) {
215
+ if ('oneOf' in s && Array.isArray(s.oneOf)) {
155
216
  return {
156
217
  name,
157
218
  description: s.description,
@@ -162,7 +223,7 @@ function jsonSchemaToDefinition(name, schema) {
162
223
  isTypeAlias: true,
163
224
  };
164
225
  }
165
- if ("anyOf" in s && Array.isArray(s.anyOf)) {
226
+ if ('anyOf' in s && Array.isArray(s.anyOf)) {
166
227
  return {
167
228
  name,
168
229
  description: s.description,
@@ -173,11 +234,11 @@ function jsonSchemaToDefinition(name, schema) {
173
234
  isTypeAlias: true,
174
235
  };
175
236
  }
176
- if ("allOf" in s && Array.isArray(s.allOf)) {
237
+ if ('allOf' in s && Array.isArray(s.allOf)) {
177
238
  const members = s.allOf;
178
239
  if (members.length === 2) {
179
- const refMember = members.find((m) => "$ref" in m);
180
- const objectMember = members.find((m) => !("$ref" in m) && m.type === "object");
240
+ const refMember = members.find((m) => '$ref' in m);
241
+ const objectMember = members.find((m) => !('$ref' in m) && m.type === 'object');
181
242
  if (refMember && objectMember) {
182
243
  const baseDef = jsonSchemaToDefinition(name, objectMember);
183
244
  return { ...baseDef, extends: jsonSchemaToType(refMember) };
@@ -193,7 +254,7 @@ function jsonSchemaToDefinition(name, schema) {
193
254
  isTypeAlias: true,
194
255
  };
195
256
  }
196
- if (s.type === "object") {
257
+ if (s.type === 'object') {
197
258
  const required = new Set(Array.isArray(s.required) ? s.required : []);
198
259
  const rawProps = s.properties ?? {};
199
260
  const properties = Object.entries(rawProps).map(([propName, propSchema]) => {
@@ -207,12 +268,16 @@ function jsonSchemaToDefinition(name, schema) {
207
268
  format: ps.format,
208
269
  readOnly: ps.readOnly,
209
270
  writeOnly: ps.writeOnly,
271
+ deprecated: ps.deprecated,
272
+ defaultValue: ps.default,
210
273
  };
211
274
  });
212
275
  const ap = s.additionalProperties;
213
- const additionalPropertiesType = ap && typeof ap === "object"
214
- ? jsonSchemaToType(ap)
215
- : undefined;
276
+ const additionalPropertiesType = ap === true
277
+ ? 'unknown'
278
+ : ap && typeof ap === 'object'
279
+ ? jsonSchemaToType(ap)
280
+ : undefined;
216
281
  return {
217
282
  name,
218
283
  description: s.description,
@@ -226,77 +291,102 @@ function jsonSchemaToDefinition(name, schema) {
226
291
  description: s.description,
227
292
  isEnum: false,
228
293
  isTypeAlias: true,
294
+ aliasType: jsonSchemaToType(schema),
229
295
  properties: [],
230
296
  };
231
297
  }
232
298
  // ── Type string conversion ───────────────────────────────────────────────────
233
299
  export function jsonSchemaToType(schema) {
234
- const s = schema;
235
- if ("$ref" in s && typeof s.$ref === "string") {
236
- return pascalCase(s.$ref.split("/").pop());
300
+ const value = schema;
301
+ let type;
302
+ if (typeof value.$ref === 'string') {
303
+ type = pascalCase(value.$ref.split('/').pop());
237
304
  }
238
- if ("enum" in s && Array.isArray(s.enum) && s.enum.length > 0) {
239
- return s.enum.map((v) => JSON.stringify(v)).join(" | ");
305
+ else if (Array.isArray(value.enum) && value.enum.length > 0) {
306
+ type = [...new Set(value.enum.map((item) => JSON.stringify(item)))].join(' | ');
240
307
  }
241
- if (Array.isArray(s.allOf))
242
- return s.allOf.map(jsonSchemaToType).join(" & ");
243
- if (Array.isArray(s.oneOf)) {
244
- const members = [...new Set(s.oneOf.map(jsonSchemaToType).filter(Boolean))];
245
- return members.join(" | ");
308
+ else if (Array.isArray(value.allOf)) {
309
+ type = uniqueTypes(value.allOf).join(' & ');
246
310
  }
247
- if (Array.isArray(s.anyOf)) {
248
- const members = [...new Set(s.anyOf.map(jsonSchemaToType).filter(Boolean))];
249
- return members.join(" | ");
311
+ else if (Array.isArray(value.oneOf)) {
312
+ type = uniqueTypes(value.oneOf).join(' | ');
250
313
  }
251
- const type = s.type;
252
- if (type === "object") {
253
- const required = new Set(Array.isArray(s.required) ? s.required : []);
254
- const rawProps = s.properties;
255
- if (rawProps) {
256
- const entries = Object.entries(rawProps)
257
- .map(([k, v]) => `${k}${required.has(k) ? "" : "?"}: ${jsonSchemaToType(v)}`)
258
- .join("; ");
259
- const ap = s.additionalProperties;
260
- if (ap && typeof ap === "object") {
261
- return `{ ${entries}; [key: string]: ${jsonSchemaToType(ap)} }`;
262
- }
263
- return entries ? `{ ${entries} }` : "Record<string, unknown>";
314
+ else if (Array.isArray(value.anyOf)) {
315
+ type = uniqueTypes(value.anyOf).join(' | ');
316
+ }
317
+ else {
318
+ type = schemaType(value);
319
+ }
320
+ if (value.nullable === true &&
321
+ !type.split('|').some((part) => part.trim() === 'null')) {
322
+ return `${type.includes('|') || type.includes('&') ? `(${type})` : type} | null`;
323
+ }
324
+ return type;
325
+ }
326
+ function uniqueTypes(schemas) {
327
+ return [...new Set(schemas.map(jsonSchemaToType))];
328
+ }
329
+ function schemaType(schema) {
330
+ switch (schema.type) {
331
+ case 'object':
332
+ return objectType(schema);
333
+ case 'array': {
334
+ const items = schema.items;
335
+ return `Array<${items ? jsonSchemaToType(items) : 'unknown'}>`;
336
+ }
337
+ case 'string':
338
+ case 'date':
339
+ return 'string';
340
+ case 'number':
341
+ return 'number';
342
+ case 'integer':
343
+ return 'integer';
344
+ case 'boolean':
345
+ return 'boolean';
346
+ case 'null':
347
+ return 'null';
348
+ case 'never':
349
+ return 'never';
350
+ case 'nullable': {
351
+ const definition = schema.def;
352
+ return definition?.innerType
353
+ ? `${jsonSchemaToType(definition.innerType)} | null`
354
+ : 'unknown | null';
264
355
  }
265
- if (s.additionalProperties && typeof s.additionalProperties === "object") {
266
- return `Record<string, ${jsonSchemaToType(s.additionalProperties)}>`;
356
+ case 'union': {
357
+ const options = schema.options;
358
+ return options ? uniqueTypes(options).join(' | ') : 'unknown';
267
359
  }
268
- return "Record<string, unknown>";
269
- }
270
- if (type === "array") {
271
- const items = s.items;
272
- return `Array<${items ? jsonSchemaToType(items) : "unknown"}>`;
273
- }
274
- if (type === "string" || type === "date")
275
- return "string";
276
- if (type === "number" || type === "integer")
277
- return "number";
278
- if (type === "boolean")
279
- return "boolean";
280
- if (type === "null")
281
- return "null";
282
- if (type === "never")
283
- return "never";
284
- if (type === "nullable") {
285
- const def = s.def;
286
- if (def?.innerType)
287
- return jsonSchemaToType(def.innerType) + " | null";
288
- return "unknown | null";
289
- }
290
- if (type === "union") {
291
- const options = s.options;
292
- if (options)
293
- return options.map(jsonSchemaToType).join(" | ");
294
- }
295
- return "unknown";
360
+ default:
361
+ return 'unknown';
362
+ }
363
+ }
364
+ function objectType(schema) {
365
+ const properties = schema.properties;
366
+ const additionalProperties = schema.additionalProperties;
367
+ if (!properties) {
368
+ return additionalProperties && typeof additionalProperties === 'object'
369
+ ? `Record<string, ${jsonSchemaToType(additionalProperties)}>`
370
+ : 'Record<string, unknown>';
371
+ }
372
+ const required = new Set(Array.isArray(schema.required) ? schema.required : []);
373
+ const entries = Object.entries(properties).map(([name, property]) => `${typePropertyName(name)}${required.has(name) ? '' : '?'}: ${jsonSchemaToType(property)}`);
374
+ if (additionalProperties === true) {
375
+ entries.push('[key: string]: unknown');
376
+ }
377
+ else if (additionalProperties && typeof additionalProperties === 'object') {
378
+ entries.push(`[key: string]: ${jsonSchemaToType(additionalProperties)}`);
379
+ }
380
+ return entries.length
381
+ ? `{ ${entries.join('; ')} }`
382
+ : 'Record<string, unknown>';
383
+ }
384
+ function typePropertyName(name) {
385
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) ? name : JSON.stringify(name);
296
386
  }
297
387
  // ── Group / operation building ───────────────────────────────────────────────
298
388
  function isComplexInlineSchema(s) {
299
- if (s.type === "object") {
389
+ if (s.type === 'object') {
300
390
  // An object with only additionalProperties (no explicit `properties`) is
301
391
  // representable as an inline `Record<string, V>` type — no need to hoist it
302
392
  // into a named inline schema, which would lose the response type entirely.
@@ -307,7 +397,7 @@ function isComplexInlineSchema(s) {
307
397
  if (Array.isArray(s.allOf))
308
398
  return true;
309
399
  // anyOf / oneOf that is just `SomeRef | null` is not complex — emit inline
310
- for (const key of ["anyOf", "oneOf"]) {
400
+ for (const key of ['anyOf', 'oneOf']) {
311
401
  const arr = s[key];
312
402
  if (!Array.isArray(arr))
313
403
  continue;
@@ -321,12 +411,12 @@ function isComplexInlineSchema(s) {
321
411
  function isNullableRefUnion(members) {
322
412
  if (members.length !== 2)
323
413
  return false;
324
- const refMember = members.find((m) => "$ref" in m);
325
- const nullMember = members.find((m) => m.type === "null");
414
+ const refMember = members.find((m) => '$ref' in m);
415
+ const nullMember = members.find((m) => m.type === 'null');
326
416
  return !!refMember && !!nullMember;
327
417
  }
328
- function buildOperationGroup(domain, _schemas) {
329
- const domainName = domain.domain ?? domain.prefix;
418
+ function buildOperationGroup(domain) {
419
+ const domainName = domain.domain ?? domain.prefix.split('/').filter(Boolean).pop() ?? 'root';
330
420
  const name = camelCase(domainName);
331
421
  const fileName = domain.fileName ?? kebabCase(domainName);
332
422
  const allInlineSchemas = [];
@@ -337,12 +427,12 @@ function buildOperationGroup(domain, _schemas) {
337
427
  const { operation, inlineSchemas } = buildOperation(route, name, domain.prefix);
338
428
  operations.push({
339
429
  ...operation,
340
- visibility: route.sdk?.internal ? "internal" : "public",
430
+ visibility: route.sdk?.internal ? 'internal' : 'public',
341
431
  });
342
432
  for (const s of inlineSchemas) {
343
433
  allInlineSchemas.push({
344
434
  ...s,
345
- ownership: { kind: "generated", domainFile: fileName },
435
+ ownership: { kind: 'generated', domainFile: fileName },
346
436
  });
347
437
  }
348
438
  }
@@ -350,20 +440,17 @@ function buildOperationGroup(domain, _schemas) {
350
440
  group: {
351
441
  name,
352
442
  fileName,
353
- interfaceName: pascalCase(domainName) + "API",
354
- factoryName: "create" + pascalCase(domainName) + "API",
443
+ interfaceName: pascalCase(domainName) + 'API',
444
+ factoryName: 'create' + pascalCase(domainName) + 'API',
355
445
  description: domain.description,
356
- visibility: domain.stability === "internal" ? "internal" : "public",
446
+ visibility: domain.stability === 'internal' ? 'internal' : 'public',
357
447
  operations,
358
448
  },
359
449
  inlineSchemas: allInlineSchemas,
360
450
  };
361
451
  }
362
452
  function buildOperation(route, groupName, domainPrefix) {
363
- const prefix = (route.sdk?.prefix ?? domainPrefix).replace(/\/$/, "");
364
- // When route.url is '/' it means the route URL exactly matched the domain
365
- // prefix — avoid doubling the slash (e.g. /signal/projects + / = /signal/projects/).
366
- const fullUrl = route.url === "/" ? prefix || "/" : prefix + route.url;
453
+ const fullUrl = joinRoutePath(route.sdk?.prefix ?? domainPrefix, route.url);
367
454
  const httpMethod = (Array.isArray(route.method) ? route.method[0] : route.method).toUpperCase();
368
455
  const pathParams = extractPathParams(fullUrl, route.schema?.params);
369
456
  const querySchema = route.schema?.querystring ?? route.schema?.query;
@@ -372,32 +459,26 @@ function buildOperation(route, groupName, domainPrefix) {
372
459
  const inlineSchemas = [];
373
460
  const name = route.sdk?.methodName ??
374
461
  deriveCleanMethodName({
375
- operationId: route.schema?.operationId ?? route.sdk?.operationId,
462
+ operationId: route.sdk?.operationId ?? route.schema?.operationId,
376
463
  summary: route.schema?.summary,
377
- path: route.url.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, "{$1}"),
464
+ path: route.url.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, '{$1}'),
378
465
  httpMethod: httpMethod.toLowerCase(),
379
466
  tagName: groupName,
380
467
  });
381
468
  // Response type
382
- let responseType = "void";
383
- let statusCode = 200;
384
- // SDK config override: explicit returnType takes priority over schema derivation.
385
- // Useful for paginated list endpoints that return a wrapper but should expose the inner array type.
386
- if (route.sdk?.returnType) {
387
- responseType = route.sdk.returnType;
388
- }
389
- else {
390
- const successResponse = pickSuccessResponse(route.schema?.response);
391
- if (successResponse) {
392
- const { schema: successSchema, statusCode: sc } = successResponse;
393
- statusCode = sc;
394
- const rs = successSchema;
395
- if (rs.type === "null" ||
396
- (rs.type === "object" && !rs.properties && !rs.$ref && !rs.additionalProperties)) {
397
- responseType = "void";
398
- }
399
- else if (typeof rs.$ref !== "string" && isComplexInlineSchema(rs)) {
400
- const typeName = pascalCase(name) + "Response";
469
+ const successResponse = pickSuccessResponse(route.schema?.response);
470
+ let statusCode = successResponse?.statusCode ?? 200;
471
+ let responseType = route.sdk?.returnType ?? 'void';
472
+ if (!route.sdk?.returnType && successResponse) {
473
+ const successSchema = successResponse.schema;
474
+ const schema = successSchema;
475
+ if (schema.type !== 'null' &&
476
+ !(schema.type === 'object' &&
477
+ !schema.properties &&
478
+ !schema.$ref &&
479
+ !schema.additionalProperties)) {
480
+ if (typeof schema.$ref !== 'string' && isComplexInlineSchema(schema)) {
481
+ const typeName = `${pascalCase(name)}Response`;
401
482
  inlineSchemas.push(jsonSchemaToDefinition(typeName, successSchema));
402
483
  responseType = typeName;
403
484
  }
@@ -406,35 +487,47 @@ function buildOperation(route, groupName, domainPrefix) {
406
487
  }
407
488
  }
408
489
  }
409
- // Request body
490
+ // Request body — empty object schemas (no properties / $ref / additionalProperties)
491
+ // are treated as "no body planned", matching void response handling. Domain
492
+ // templates still emit an empty JSON body (`{}`) for body-capable methods.
410
493
  let requestBody;
411
494
  if (route.schema?.body) {
412
495
  const bodySchema = route.schema.body;
413
496
  const bs = bodySchema;
414
- let bodyType;
415
- if (typeof bs.$ref !== "string" && isComplexInlineSchema(bs)) {
416
- const typeName = pascalCase(name) + "Input";
417
- inlineSchemas.push(jsonSchemaToDefinition(typeName, bodySchema));
418
- bodyType = typeName;
419
- }
420
- else {
421
- bodyType = jsonSchemaToType(bodySchema);
497
+ const isEmptyBodySchema = bs.type === 'null' ||
498
+ (bs.type === 'object' &&
499
+ !bs.properties &&
500
+ !bs.$ref &&
501
+ !bs.additionalProperties &&
502
+ !bs.allOf &&
503
+ !bs.anyOf &&
504
+ !bs.oneOf);
505
+ if (!isEmptyBodySchema) {
506
+ let bodyType;
507
+ if (typeof bs.$ref !== 'string' && isComplexInlineSchema(bs)) {
508
+ const typeName = pascalCase(name) + 'Input';
509
+ inlineSchemas.push(jsonSchemaToDefinition(typeName, bodySchema));
510
+ bodyType = typeName;
511
+ }
512
+ else {
513
+ bodyType = jsonSchemaToType(bodySchema);
514
+ }
515
+ requestBody = {
516
+ type: bodyType,
517
+ required: true,
518
+ contentType: route.sdk?.transport === 'multipart'
519
+ ? 'multipart/form-data'
520
+ : route.sdk?.transport === 'binary'
521
+ ? 'application/octet-stream'
522
+ : (route.schema?.consumes?.[0] ?? 'application/json'),
523
+ };
422
524
  }
423
- requestBody = {
424
- type: bodyType,
425
- required: true,
426
- contentType: route.sdk?.transport === "multipart"
427
- ? "multipart/form-data"
428
- : route.sdk?.transport === "binary"
429
- ? "application/octet-stream"
430
- : "application/json",
431
- };
432
525
  }
433
526
  // Query type
434
527
  let queryType;
435
528
  if (querySchema) {
436
529
  const qs = querySchema;
437
- if (typeof qs.$ref === "string") {
530
+ if (typeof qs.$ref === 'string') {
438
531
  queryType = jsonSchemaToType(querySchema);
439
532
  }
440
533
  else if (queryParams.length > 0) {
@@ -443,31 +536,37 @@ function buildOperation(route, groupName, domainPrefix) {
443
536
  }
444
537
  // Required headers from sdk config
445
538
  if (route.sdk?.requiredHeaders) {
446
- for (const rh of route.sdk.requiredHeaders) {
447
- headerParams.push({
448
- name: camelCase(rh.paramName),
449
- originalName: rh.name,
450
- type: "string",
539
+ for (const header of route.sdk.requiredHeaders) {
540
+ const existingIndex = headerParams.findIndex(({ originalName }) => originalName.toLowerCase() === header.name.toLowerCase());
541
+ const parameter = {
542
+ name: safeParamName(camelCase(header.paramName)),
543
+ originalName: header.name,
544
+ type: 'string',
451
545
  required: true,
452
- description: rh.description,
453
- });
546
+ description: header.description,
547
+ };
548
+ if (existingIndex >= 0)
549
+ headerParams[existingIndex] = parameter;
550
+ else
551
+ headerParams.push(parameter);
454
552
  }
455
553
  }
456
554
  // Header type (inline object type from headerParams)
457
555
  let headerType;
458
556
  if (headerParams.length > 0) {
459
557
  const props = headerParams.map((p) => {
460
- const opt = p.required ? "" : "?";
558
+ const opt = p.required ? '' : '?';
461
559
  return `${p.name}${opt}: ${p.type}`;
462
560
  });
463
- headerType = `{ ${props.join("; ")} }`;
561
+ headerType = `{ ${props.join('; ')} }`;
464
562
  }
563
+ const isEventStream = route.sse ?? route.sdk?.transport === 'stream';
465
564
  return {
466
565
  operation: {
467
566
  name,
468
- operationId: route.schema?.operationId ?? route.sdk?.operationId,
567
+ operationId: route.sdk?.operationId ?? route.schema?.operationId,
469
568
  summary: route.schema?.summary,
470
- description: route.schema?.description ?? route.sdk?.description,
569
+ description: route.sdk?.description ?? route.schema?.description,
471
570
  httpMethod,
472
571
  path: fastifyPathToIR(fullUrl, pathParams),
473
572
  pathParams,
@@ -481,12 +580,13 @@ function buildOperation(route, groupName, domainPrefix) {
481
580
  responseType,
482
581
  statusCode,
483
582
  deprecated: route.sdk?.deprecated ? true : false,
484
- deprecationMessage: typeof route.sdk?.deprecated === "string"
583
+ deprecationMessage: typeof route.sdk?.deprecated === 'string'
485
584
  ? route.sdk.deprecated
486
585
  : undefined,
487
586
  timeoutMs: route.sdk?.timeout,
488
- visibility: "public",
489
- isEventStream: route.sse ?? route.sdk?.transport === "stream",
587
+ visibility: 'public',
588
+ isEventStream,
589
+ eventSchema: isEventStream ? responseType : undefined,
490
590
  sseReturnType: route.sdk?.sseReturnType,
491
591
  },
492
592
  inlineSchemas,
@@ -494,61 +594,61 @@ function buildOperation(route, groupName, domainPrefix) {
494
594
  }
495
595
  // ── Parameter extraction ─────────────────────────────────────────────────────
496
596
  const TS_RESERVED_WORDS = new Set([
497
- "break",
498
- "case",
499
- "catch",
500
- "class",
501
- "const",
502
- "continue",
503
- "debugger",
504
- "default",
505
- "delete",
506
- "do",
507
- "else",
508
- "enum",
509
- "export",
510
- "extends",
511
- "false",
512
- "finally",
513
- "for",
514
- "function",
515
- "if",
516
- "import",
517
- "in",
518
- "instanceof",
519
- "new",
520
- "null",
521
- "return",
522
- "super",
523
- "switch",
524
- "this",
525
- "throw",
526
- "true",
527
- "try",
528
- "typeof",
529
- "var",
530
- "void",
531
- "while",
532
- "with",
533
- "as",
534
- "implements",
535
- "interface",
536
- "let",
537
- "package",
538
- "private",
539
- "protected",
540
- "public",
541
- "static",
542
- "yield",
543
- "type",
597
+ 'break',
598
+ 'case',
599
+ 'catch',
600
+ 'class',
601
+ 'const',
602
+ 'continue',
603
+ 'debugger',
604
+ 'default',
605
+ 'delete',
606
+ 'do',
607
+ 'else',
608
+ 'enum',
609
+ 'export',
610
+ 'extends',
611
+ 'false',
612
+ 'finally',
613
+ 'for',
614
+ 'function',
615
+ 'if',
616
+ 'import',
617
+ 'in',
618
+ 'instanceof',
619
+ 'new',
620
+ 'null',
621
+ 'return',
622
+ 'super',
623
+ 'switch',
624
+ 'this',
625
+ 'throw',
626
+ 'true',
627
+ 'try',
628
+ 'typeof',
629
+ 'var',
630
+ 'void',
631
+ 'while',
632
+ 'with',
633
+ 'as',
634
+ 'implements',
635
+ 'interface',
636
+ 'let',
637
+ 'package',
638
+ 'private',
639
+ 'protected',
640
+ 'public',
641
+ 'static',
642
+ 'yield',
643
+ 'type',
544
644
  ]);
545
645
  /** Parameter names reserved by the generated method signatures. */
546
646
  const FIXED_PARAM_NAMES = new Set([
547
- "input",
548
- "query",
549
- "headerOptions",
550
- "cookies",
551
- "options",
647
+ 'input',
648
+ 'query',
649
+ 'headerOptions',
650
+ 'cookies',
651
+ 'options',
552
652
  ]);
553
653
  function safeParamName(name) {
554
654
  if (TS_RESERVED_WORDS.has(name) || FIXED_PARAM_NAMES.has(name)) {
@@ -561,7 +661,7 @@ function extractPathParams(url, paramsSchema) {
561
661
  if (urlParams.length === 0)
562
662
  return [];
563
663
  const s = paramsSchema;
564
- const props = typeof s?.properties === "object" && !Array.isArray(s?.properties)
664
+ const props = typeof s?.properties === 'object' && !Array.isArray(s?.properties)
565
665
  ? s.properties
566
666
  : undefined;
567
667
  const required = new Set(Array.isArray(s?.required) ? s.required : []);
@@ -570,8 +670,8 @@ function extractPathParams(url, paramsSchema) {
570
670
  return {
571
671
  name: safeParamName(camelCase(paramName)),
572
672
  originalName: paramName,
573
- type: propSchema ? jsonSchemaToType(propSchema) : "string",
574
- required: required.size === 0 || required.has(paramName),
673
+ type: propSchema ? jsonSchemaToType(propSchema) : 'string',
674
+ required: true,
575
675
  description: propSchema
576
676
  ? propSchema.description
577
677
  : undefined,
@@ -582,12 +682,12 @@ function extractQueryParams(querySchema) {
582
682
  if (!querySchema)
583
683
  return [];
584
684
  const s = querySchema;
585
- if (s.type !== "object" || !s.properties)
685
+ if (s.type !== 'object' || !s.properties)
586
686
  return [];
587
687
  const required = new Set(Array.isArray(s.required) ? s.required : []);
588
688
  const props = s.properties;
589
689
  return Object.entries(props).map(([name, schema]) => ({
590
- name: camelCase(name),
690
+ name: safeParamName(camelCase(name)),
591
691
  originalName: name,
592
692
  type: jsonSchemaToType(schema),
593
693
  required: required.has(name),
@@ -596,25 +696,25 @@ function extractQueryParams(querySchema) {
596
696
  }));
597
697
  }
598
698
  const SYSTEM_HEADERS = new Set([
599
- "content-type",
600
- "content-length",
601
- "transfer-encoding",
602
- "host",
603
- "accept",
604
- "accept-encoding",
699
+ 'content-type',
700
+ 'content-length',
701
+ 'transfer-encoding',
702
+ 'host',
703
+ 'accept',
704
+ 'accept-encoding',
605
705
  ]);
606
706
  function extractHeaderParams(headersSchema) {
607
707
  if (!headersSchema)
608
708
  return [];
609
709
  const s = headersSchema;
610
- if (s.type !== "object" || !s.properties)
710
+ if (s.type !== 'object' || !s.properties)
611
711
  return [];
612
712
  const required = new Set(Array.isArray(s.required) ? s.required : []);
613
713
  const props = s.properties;
614
714
  return Object.entries(props)
615
715
  .filter(([name]) => !SYSTEM_HEADERS.has(name.toLowerCase()))
616
716
  .map(([name, schema]) => ({
617
- name: camelCase(name),
717
+ name: safeParamName(camelCase(name)),
618
718
  originalName: name,
619
719
  type: jsonSchemaToType(schema),
620
720
  required: required.has(name),
@@ -622,27 +722,31 @@ function extractHeaderParams(headersSchema) {
622
722
  }));
623
723
  }
624
724
  // ── Helpers ──────────────────────────────────────────────────────────────────
725
+ function joinRoutePath(prefix, routePath) {
726
+ const left = prefix === '/' ? '' : prefix.replace(/\/$/, '');
727
+ const right = routePath === '/' ? '' : `/${routePath.replace(/^\//, '')}`;
728
+ return `${left}${right}` || '/';
729
+ }
625
730
  function fastifyPathToIR(url, pathParams) {
626
731
  const nameMap = new Map(pathParams.map((p) => [p.originalName, p.name]));
627
732
  return url.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, (_, raw) => {
628
733
  const name = nameMap.get(raw) ?? raw;
629
- return "${" + name + "}";
734
+ return '${' + name + '}';
630
735
  });
631
736
  }
632
737
  function pickSuccessResponse(response) {
633
738
  if (!response)
634
739
  return undefined;
635
- for (const [code, schema] of [
636
- [200, response["200"] ?? response[200]],
637
- [201, response["201"] ?? response[201]],
638
- [202, response["202"] ?? response[202]],
639
- [204, response["204"] ?? response[204]],
640
- [200, response["2xx"]],
641
- [200, response["default"]],
642
- ]) {
643
- if (schema)
644
- return { schema, statusCode: code };
645
- }
740
+ const numeric = Object.entries(response)
741
+ .map(([code, schema]) => ({ statusCode: Number(code), schema }))
742
+ .filter(({ statusCode }) => statusCode >= 200 && statusCode < 300)
743
+ .sort((left, right) => left.statusCode - right.statusCode)[0];
744
+ if (numeric)
745
+ return numeric;
746
+ if (response['2xx'])
747
+ return { schema: response['2xx'], statusCode: 200 };
748
+ if (response.default)
749
+ return { schema: response.default, statusCode: 200 };
646
750
  return undefined;
647
751
  }
648
752
  //# sourceMappingURL=intake.js.map