@abapify/openai-codegen 0.4.33 → 0.4.38

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.
@@ -166,77 +166,91 @@ function normalizeSecuritySchemes(raw) {
166
166
  if (!isRecord$4(raw)) return out;
167
167
  for (const [name, schemeRaw] of Object.entries(raw)) {
168
168
  if (!isRecord$4(schemeRaw)) continue;
169
- const type = asString(schemeRaw.type);
170
- const description = asString(schemeRaw.description);
171
- switch (type) {
172
- case "apiKey": {
173
- const inLoc = asString(schemeRaw.in);
174
- if (inLoc !== "header" && inLoc !== "query" && inLoc !== "cookie") continue;
175
- const scheme = {
176
- type: "apiKey",
177
- name: asString(schemeRaw.name) ?? name,
178
- in: inLoc
179
- };
180
- if (description !== void 0) scheme.description = description;
181
- out[name] = scheme;
182
- break;
183
- }
184
- case "http": {
185
- const scheme = {
186
- type: "http",
187
- scheme: asString(schemeRaw.scheme) ?? "bearer"
188
- };
189
- const bf = asString(schemeRaw.bearerFormat);
190
- if (bf !== void 0) scheme.bearerFormat = bf;
191
- if (description !== void 0) scheme.description = description;
192
- out[name] = scheme;
193
- break;
194
- }
195
- case "oauth2": {
196
- const flowsRaw = isRecord$4(schemeRaw.flows) ? schemeRaw.flows : {};
197
- const scheme = {
198
- type: "oauth2",
199
- flows: {}
200
- };
201
- for (const flowName of [
202
- "implicit",
203
- "password",
204
- "clientCredentials",
205
- "authorizationCode"
206
- ]) {
207
- const f = flowsRaw[flowName];
208
- if (!isRecord$4(f)) continue;
209
- const flow = { scopes: {} };
210
- if (isRecord$4(f.scopes)) {
211
- for (const [k, v] of Object.entries(f.scopes)) if (typeof v === "string") flow.scopes[k] = v;
212
- }
213
- const au = asString(f.authorizationUrl);
214
- if (au !== void 0) flow.authorizationUrl = au;
215
- const tu = asString(f.tokenUrl);
216
- if (tu !== void 0) flow.tokenUrl = tu;
217
- const ru = asString(f.refreshUrl);
218
- if (ru !== void 0) flow.refreshUrl = ru;
219
- scheme.flows[flowName] = flow;
220
- }
221
- if (description !== void 0) scheme.description = description;
222
- out[name] = scheme;
223
- break;
224
- }
225
- case "openIdConnect": {
226
- const url = asString(schemeRaw.openIdConnectUrl);
227
- if (url === void 0) continue;
228
- const scheme = {
229
- type: "openIdConnect",
230
- openIdConnectUrl: url
231
- };
232
- if (description !== void 0) scheme.description = description;
233
- out[name] = scheme;
234
- break;
235
- }
236
- }
169
+ const scheme = normalizeSecurityScheme(name, schemeRaw);
170
+ if (scheme !== void 0) out[name] = scheme;
237
171
  }
238
172
  return out;
239
173
  }
174
+ function normalizeSecurityScheme(name, schemeRaw) {
175
+ const type = asString(schemeRaw.type);
176
+ let scheme;
177
+ switch (type) {
178
+ case "apiKey":
179
+ scheme = normalizeApiKeyScheme(name, schemeRaw);
180
+ break;
181
+ case "http":
182
+ scheme = normalizeHttpScheme(schemeRaw);
183
+ break;
184
+ case "oauth2":
185
+ scheme = normalizeOAuth2Scheme(schemeRaw);
186
+ break;
187
+ case "openIdConnect":
188
+ scheme = normalizeOpenIdConnectScheme(schemeRaw);
189
+ break;
190
+ default: scheme = void 0;
191
+ }
192
+ if (scheme === void 0) return void 0;
193
+ const description = asString(schemeRaw.description);
194
+ if (description !== void 0) scheme.description = description;
195
+ return scheme;
196
+ }
197
+ function normalizeApiKeyScheme(name, raw) {
198
+ const inLoc = asString(raw.in);
199
+ if (inLoc !== "header" && inLoc !== "query" && inLoc !== "cookie") return;
200
+ return {
201
+ type: "apiKey",
202
+ name: asString(raw.name) ?? name,
203
+ in: inLoc
204
+ };
205
+ }
206
+ function normalizeHttpScheme(raw) {
207
+ const scheme = {
208
+ type: "http",
209
+ scheme: asString(raw.scheme) ?? "bearer"
210
+ };
211
+ const bf = asString(raw.bearerFormat);
212
+ if (bf !== void 0) scheme.bearerFormat = bf;
213
+ return scheme;
214
+ }
215
+ function normalizeOAuth2Scheme(raw) {
216
+ const flowsRaw = isRecord$4(raw.flows) ? raw.flows : {};
217
+ const scheme = {
218
+ type: "oauth2",
219
+ flows: {}
220
+ };
221
+ for (const flowName of [
222
+ "implicit",
223
+ "password",
224
+ "clientCredentials",
225
+ "authorizationCode"
226
+ ]) {
227
+ const f = flowsRaw[flowName];
228
+ if (!isRecord$4(f)) continue;
229
+ scheme.flows[flowName] = normalizeOAuth2Flow(f);
230
+ }
231
+ return scheme;
232
+ }
233
+ function normalizeOAuth2Flow(f) {
234
+ const flow = { scopes: {} };
235
+ if (isRecord$4(f.scopes)) {
236
+ for (const [k, v] of Object.entries(f.scopes)) if (typeof v === "string") flow.scopes[k] = v;
237
+ }
238
+ const au = asString(f.authorizationUrl);
239
+ if (au !== void 0) flow.authorizationUrl = au;
240
+ const tu = asString(f.tokenUrl);
241
+ if (tu !== void 0) flow.tokenUrl = tu;
242
+ const ru = asString(f.refreshUrl);
243
+ if (ru !== void 0) flow.refreshUrl = ru;
244
+ return flow;
245
+ }
246
+ function normalizeOpenIdConnectScheme(raw) {
247
+ const url = asString(raw.openIdConnectUrl);
248
+ if (url === void 0) return void 0;
249
+ return {
250
+ type: "openIdConnect",
251
+ openIdConnectUrl: url
252
+ };
253
+ }
240
254
  function normalizeSecurityRequirements(raw) {
241
255
  return asArray(raw).filter(isRecord$4).map((req) => {
242
256
  const out = {};
@@ -253,28 +267,31 @@ function normalizeOperations(pathsRaw, topLevelSecurity) {
253
267
  for (const method of HTTP_METHODS) {
254
268
  const opRaw = pathItemRaw[method];
255
269
  if (!isRecord$4(opRaw)) continue;
256
- const parameters = mergeParameters(pathLevelParams, normalizeParameters(opRaw.parameters));
257
- const op = {
258
- operationId: asString(opRaw.operationId) ?? synthesizeOperationId(method, path),
259
- method,
260
- path,
261
- tags: asStringArray(opRaw.tags),
262
- deprecated: asBool(opRaw.deprecated, false),
263
- parameters,
264
- responses: normalizeResponses(opRaw.responses),
265
- security: opRaw.security === void 0 ? topLevelSecurity : normalizeSecurityRequirements(opRaw.security)
266
- };
267
- const summary = asString(opRaw.summary);
268
- if (summary !== void 0) op.summary = summary;
269
- const description = asString(opRaw.description);
270
- if (description !== void 0) op.description = description;
271
- const requestBody = normalizeRequestBody(opRaw.requestBody);
272
- if (requestBody !== void 0) op.requestBody = requestBody;
273
- ops.push(op);
270
+ ops.push(buildOperation(method, path, opRaw, pathLevelParams, topLevelSecurity));
274
271
  }
275
272
  }
276
273
  return ops;
277
274
  }
275
+ function buildOperation(method, path, opRaw, pathLevelParams, topLevelSecurity) {
276
+ const parameters = mergeParameters(pathLevelParams, normalizeParameters(opRaw.parameters));
277
+ const op = {
278
+ operationId: asString(opRaw.operationId) ?? synthesizeOperationId(method, path),
279
+ method,
280
+ path,
281
+ tags: asStringArray(opRaw.tags),
282
+ deprecated: asBool(opRaw.deprecated, false),
283
+ parameters,
284
+ responses: normalizeResponses(opRaw.responses),
285
+ security: opRaw.security === void 0 ? topLevelSecurity : normalizeSecurityRequirements(opRaw.security)
286
+ };
287
+ const summary = asString(opRaw.summary);
288
+ if (summary !== void 0) op.summary = summary;
289
+ const description = asString(opRaw.description);
290
+ if (description !== void 0) op.description = description;
291
+ const requestBody = normalizeRequestBody(opRaw.requestBody);
292
+ if (requestBody !== void 0) op.requestBody = requestBody;
293
+ return op;
294
+ }
278
295
  function normalizeParameters(raw) {
279
296
  return asArray(raw).filter(isRecord$4).map((p) => {
280
297
  const inLoc = asString(p.in);
@@ -303,16 +320,19 @@ function mergeParameters(pathLevel, opLevel) {
303
320
  for (const p of opLevel) merged.set(key(p), p);
304
321
  return [...merged.values()];
305
322
  }
306
- function normalizeRequestBody(raw) {
307
- if (!isRecord$4(raw)) return void 0;
323
+ function normalizeContentMap(raw) {
308
324
  const content = {};
309
- if (isRecord$4(raw.content)) for (const [mt, mtRaw] of Object.entries(raw.content)) {
325
+ if (isRecord$4(raw)) for (const [mt, mtRaw] of Object.entries(raw)) {
310
326
  if (!isRecord$4(mtRaw)) continue;
311
327
  content[mt] = { schema: isRecord$4(mtRaw.schema) ? mtRaw.schema : {} };
312
328
  }
329
+ return content;
330
+ }
331
+ function normalizeRequestBody(raw) {
332
+ if (!isRecord$4(raw)) return void 0;
313
333
  const body = {
314
334
  required: asBool(raw.required, false),
315
- content
335
+ content: normalizeContentMap(raw.content)
316
336
  };
317
337
  const desc = asString(raw.description);
318
338
  if (desc !== void 0) body.description = desc;
@@ -322,37 +342,40 @@ function normalizeResponses(raw) {
322
342
  if (!isRecord$4(raw)) return [];
323
343
  const entries = Object.entries(raw).filter((entry) => isRecord$4(entry[1]));
324
344
  const has2xx = entries.some(([code]) => /^2\d\d$/.test(code));
325
- return entries.map(([statusCode, respRaw]) => {
326
- const isSuccess = /^2\d\d$/.test(statusCode) || statusCode === "default" && !has2xx;
327
- const isError = /^(4|5)\d\d$/.test(statusCode) || statusCode === "default" && has2xx;
328
- const content = {};
329
- if (isRecord$4(respRaw.content)) for (const [mt, mtRaw] of Object.entries(respRaw.content)) {
330
- if (!isRecord$4(mtRaw)) continue;
331
- content[mt] = { schema: isRecord$4(mtRaw.schema) ? mtRaw.schema : {} };
332
- }
333
- const headers = {};
334
- if (isRecord$4(respRaw.headers)) for (const [hn, hRaw] of Object.entries(respRaw.headers)) {
335
- if (!isRecord$4(hRaw)) continue;
336
- const header = { schema: isRecord$4(hRaw.schema) ? hRaw.schema : {} };
337
- const desc = asString(hRaw.description);
338
- if (desc !== void 0) header.description = desc;
339
- if (typeof hRaw.required === "boolean") header.required = hRaw.required;
340
- if (typeof hRaw.deprecated === "boolean") header.deprecated = hRaw.deprecated;
341
- headers[hn] = header;
342
- }
343
- const resp = {
344
- statusCode,
345
- isSuccess,
346
- isError,
347
- content,
348
- headers
349
- };
350
- const desc = asString(respRaw.description);
351
- if (desc !== void 0) resp.description = desc;
352
- return resp;
353
- });
354
- }
355
- function synthesizeOperationId(method, path) {
345
+ return entries.map(([statusCode, respRaw]) => normalizeResponse(statusCode, respRaw, has2xx));
346
+ }
347
+ function normalizeResponse(statusCode, respRaw, has2xx) {
348
+ const resp = {
349
+ statusCode,
350
+ isSuccess: /^2\d\d$/.test(statusCode) || statusCode === "default" && !has2xx,
351
+ isError: /^[45]\d\d$/.test(statusCode) || statusCode === "default" && has2xx,
352
+ content: normalizeContentMap(respRaw.content),
353
+ headers: normalizeResponseHeaders(respRaw.headers)
354
+ };
355
+ const desc = asString(respRaw.description);
356
+ if (desc !== void 0) resp.description = desc;
357
+ return resp;
358
+ }
359
+ function normalizeResponseHeaders(raw) {
360
+ const headers = {};
361
+ if (isRecord$4(raw)) for (const [hn, hRaw] of Object.entries(raw)) {
362
+ if (!isRecord$4(hRaw)) continue;
363
+ headers[hn] = normalizeHeader(hRaw);
364
+ }
365
+ return headers;
366
+ }
367
+ function normalizeHeader(hRaw) {
368
+ const header = { schema: isRecord$4(hRaw.schema) ? hRaw.schema : {} };
369
+ const desc = asString(hRaw.description);
370
+ if (desc !== void 0) header.description = desc;
371
+ if (typeof hRaw.required === "boolean") header.required = hRaw.required;
372
+ if (typeof hRaw.deprecated === "boolean") header.deprecated = hRaw.deprecated;
373
+ return header;
374
+ }
375
+ function isAlnumChar(ch) {
376
+ return ch >= "A" && ch <= "Z" || ch >= "a" && ch <= "z" || ch >= "0" && ch <= "9";
377
+ }
378
+ function sanitizePathChars(path) {
356
379
  const out = [];
357
380
  let underscore = false;
358
381
  let braceDepth = 0;
@@ -365,7 +388,7 @@ function synthesizeOperationId(method, path) {
365
388
  if (braceDepth > 0) braceDepth -= 1;
366
389
  continue;
367
390
  }
368
- if (ch >= "A" && ch <= "Z" || ch >= "a" && ch <= "z" || ch >= "0" && ch <= "9") {
391
+ if (isAlnumChar(ch)) {
369
392
  out.push(ch);
370
393
  underscore = false;
371
394
  } else if (!underscore) {
@@ -373,6 +396,10 @@ function synthesizeOperationId(method, path) {
373
396
  underscore = true;
374
397
  }
375
398
  }
399
+ return out;
400
+ }
401
+ function synthesizeOperationId(method, path) {
402
+ const out = sanitizePathChars(path);
376
403
  let start = 0;
377
404
  let end = out.length;
378
405
  while (start < end && out[start] === "_") start += 1;
@@ -408,7 +435,7 @@ function sanitizeIdent(raw, kind, opts = {}) {
408
435
  if (kind === "class") base = words.map((w) => w.toUpperCase()).join(joiner);
409
436
  else base = words.map((w) => w.toLowerCase()).join(joiner);
410
437
  if (!base) base = kind === "class" ? "X" : "x";
411
- if (/^[0-9]/.test(base)) base = (kind === "class" ? "N_" : "n_") + base;
438
+ if (/^\d/.test(base)) base = (kind === "class" ? "N_" : "n_") + base;
412
439
  let full = prefix + base;
413
440
  if (full.length > maxLen) {
414
441
  const suffix = "_" + shortHash(raw, 4);
@@ -487,67 +514,86 @@ function isNonTrivialInline(schema) {
487
514
  */
488
515
  function collectFromSchema(rootId, rootSchema, plannedIds, enqueueInline, schemaToId) {
489
516
  const deps = /* @__PURE__ */ new Set();
517
+ const ctx = {
518
+ rootId,
519
+ deps,
520
+ plannedIds,
521
+ enqueueInline,
522
+ schemaToId
523
+ };
490
524
  const visit = (schema, path, atRoot) => {
491
525
  if (!isRecord$3(schema)) return;
492
- if (isRef(schema)) {
493
- const name = componentNameFromRef$2(schema.$ref);
494
- if (name) {
495
- const depId = `components.schemas.${name}`;
496
- deps.add(depId);
497
- }
498
- return;
526
+ if (classifyVisit(schema, path, atRoot, ctx)) return;
527
+ descendSchema(schema, path, visit);
528
+ };
529
+ visit(rootSchema, [], true);
530
+ return [...deps];
531
+ }
532
+ /** Classify a non-trivial schema node: register a dep (or inline entry) and
533
+ * report whether descent should stop. */
534
+ function classifyVisit(schema, path, atRoot, ctx) {
535
+ const { rootId, deps, schemaToId } = ctx;
536
+ if (isRef(schema)) {
537
+ const name = componentNameFromRef$2(schema.$ref);
538
+ if (name) {
539
+ const depId = `components.schemas.${name}`;
540
+ deps.add(depId);
499
541
  }
500
- if (!atRoot) {
501
- const hitId = schemaToId.get(schema);
502
- if (hitId && hitId !== rootId) {
503
- deps.add(hitId);
504
- return;
505
- }
506
- if (hitId === rootId) {
507
- deps.add(rootId);
508
- return;
509
- }
542
+ return true;
543
+ }
544
+ if (!atRoot) {
545
+ const hitId = schemaToId.get(schema);
546
+ if (hitId && hitId !== rootId) {
547
+ deps.add(hitId);
548
+ return true;
510
549
  }
511
- if (!atRoot && isNonTrivialInline(schema)) {
512
- const id = `${rootId}.${path.join(".")}`;
513
- if (!plannedIds.has(id)) {
514
- enqueueInline(id, schema);
515
- schemaToId.set(schema, id);
516
- }
517
- deps.add(id);
518
- return;
550
+ if (hitId === rootId) {
551
+ deps.add(rootId);
552
+ return true;
519
553
  }
520
- const rec = schema;
521
- if (isRecord$3(rec.properties)) for (const [name, sub] of Object.entries(rec.properties)) visit(sub, [
522
- ...path,
523
- "properties",
524
- name
525
- ], false);
526
- if (rec.items !== void 0) visit(rec.items, [...path, "items"], false);
527
- if (isRecord$3(rec.additionalProperties)) visit(rec.additionalProperties, [...path, "additionalProperties"], false);
528
- for (const c of [
529
- "allOf",
530
- "anyOf",
531
- "oneOf"
532
- ]) {
533
- const arr = rec[c];
534
- if (Array.isArray(arr)) arr.forEach((sub, idx) => {
535
- visit(sub, [
536
- ...path,
537
- c,
538
- String(idx)
539
- ], false);
540
- });
554
+ if (isNonTrivialInline(schema)) {
555
+ deps.add(registerInlineEntry(schema, path, ctx));
556
+ return true;
541
557
  }
542
- };
543
- visit(rootSchema, [], true);
544
- return [...deps];
558
+ }
559
+ return false;
560
+ }
561
+ function registerInlineEntry(schema, path, ctx) {
562
+ const { rootId, plannedIds, enqueueInline, schemaToId } = ctx;
563
+ const id = `${rootId}.${path.join(".")}`;
564
+ if (!plannedIds.has(id)) {
565
+ enqueueInline(id, schema);
566
+ schemaToId.set(schema, id);
567
+ }
568
+ return id;
569
+ }
570
+ function descendSchema(schema, path, visit) {
571
+ if (isRecord$3(schema.properties)) for (const [name, sub] of Object.entries(schema.properties)) visit(sub, [
572
+ ...path,
573
+ "properties",
574
+ name
575
+ ], false);
576
+ if (schema.items !== void 0) visit(schema.items, [...path, "items"], false);
577
+ if (isRecord$3(schema.additionalProperties)) visit(schema.additionalProperties, [...path, "additionalProperties"], false);
578
+ for (const c of [
579
+ "allOf",
580
+ "anyOf",
581
+ "oneOf"
582
+ ]) {
583
+ const arr = schema[c];
584
+ if (Array.isArray(arr)) arr.forEach((sub, idx) => {
585
+ visit(sub, [
586
+ ...path,
587
+ c,
588
+ String(idx)
589
+ ], false);
590
+ });
591
+ }
545
592
  }
546
593
  /** Plan the ABAP type universe for a normalized spec. */
547
594
  function planTypes(spec, opts) {
548
595
  const alloc = makeNameAllocator(/* @__PURE__ */ new Set());
549
596
  const entries = [];
550
- const byId = /* @__PURE__ */ new Map();
551
597
  const plannedIds = /* @__PURE__ */ new Set();
552
598
  const schemaToId = /* @__PURE__ */ new Map();
553
599
  const queue = [];
@@ -573,15 +619,13 @@ function planTypes(spec, opts) {
573
619
  source: item.source
574
620
  };
575
621
  entries.push(entry);
576
- byId.set(item.id, entry);
577
622
  entry.dependencies = collectFromSchema(item.id, item.schema, plannedIds, (id, schema) => {
578
623
  enqueue(id, schema, "inline");
579
624
  }, schemaToId);
580
625
  }
581
626
  if (entries.some((e) => hasOpenAdditional(e.schema))) {
582
- const kvId = "aux.kv";
583
627
  const kvEntry = {
584
- id: kvId,
628
+ id: "aux.kv",
585
629
  abapName: alloc("kv", "type", { prefix: "ty_" + (opts.typePrefix ?? "") }),
586
630
  schema: {
587
631
  type: "object",
@@ -594,7 +638,6 @@ function planTypes(spec, opts) {
594
638
  source: "auxiliary"
595
639
  };
596
640
  entries.push(kvEntry);
597
- byId.set(kvId, kvEntry);
598
641
  }
599
642
  const sorted = topoSort(entries);
600
643
  return {
@@ -636,16 +679,7 @@ function topoSort(entries) {
636
679
  const entry = byId.get(id);
637
680
  if (!entry) return;
638
681
  if (s === "gray") {
639
- const cycleStart = stack.indexOf(id);
640
- const cycle = cycleStart >= 0 ? stack.slice(cycleStart) : [id];
641
- if (cycle.length === 1) {
642
- entry.selfReferential = true;
643
- return;
644
- }
645
- for (const pid of cycle) {
646
- const ent = byId.get(pid);
647
- if (ent) ent.selfReferential = true;
648
- }
682
+ markCycleParticipants(id, stack, byId, entry);
649
683
  return;
650
684
  }
651
685
  state.set(id, "gray");
@@ -657,12 +691,7 @@ function topoSort(entries) {
657
691
  }
658
692
  if (!byId.has(dep)) continue;
659
693
  if (state.get(dep) === "gray") {
660
- const cycleStart = stack.indexOf(dep);
661
- const cycle = cycleStart >= 0 ? stack.slice(cycleStart) : [dep];
662
- for (const pid of [...cycle, id]) {
663
- const ent = byId.get(pid);
664
- if (ent) ent.selfReferential = true;
665
- }
694
+ markBackEdgeCycle(dep, id, stack, byId);
666
695
  continue;
667
696
  }
668
697
  visit(dep, stack);
@@ -675,6 +704,30 @@ function topoSort(entries) {
675
704
  if (out.length !== entries.length) throw new CyclicTypeError(`planTypes: unable to topologically sort ${entries.length} entries (got ${out.length}).`);
676
705
  return out;
677
706
  }
707
+ /** Mark the participants of a cycle discovered on a gray node and allow the
708
+ * traversal to continue — the emitter will insert a TYPE REF TO indirection
709
+ * for the back edge. */
710
+ function markCycleParticipants(id, stack, byId, entry) {
711
+ const cycleStart = stack.indexOf(id);
712
+ const cycle = cycleStart >= 0 ? stack.slice(cycleStart) : [id];
713
+ if (cycle.length === 1) {
714
+ entry.selfReferential = true;
715
+ return;
716
+ }
717
+ for (const pid of cycle) {
718
+ const ent = byId.get(pid);
719
+ if (ent) ent.selfReferential = true;
720
+ }
721
+ }
722
+ /** Mark participants of a back-edge cycle (dep already on the gray stack). */
723
+ function markBackEdgeCycle(dep, id, stack, byId) {
724
+ const cycleStart = stack.indexOf(dep);
725
+ const cycle = cycleStart >= 0 ? stack.slice(cycleStart) : [dep];
726
+ for (const pid of [...cycle, id]) {
727
+ const ent = byId.get(pid);
728
+ if (ent) ent.selfReferential = true;
729
+ }
730
+ }
678
731
 
679
732
  // src/types/map.ts
680
733
  function isRecord$2(x) {
@@ -719,11 +772,7 @@ function isNullable$1(schema) {
719
772
  }
720
773
  /** Resolve a schema to an ABAP TypeRef. */
721
774
  function mapSchemaToTypeRef(schema, plan, ctx = {}) {
722
- if (isRef(schema)) {
723
- const entry = refToEntry(schema.$ref, plan);
724
- if (!entry) throw new UnsupportedSchemaError(`mapSchemaToTypeRef: unresolved $ref "${schema.$ref}"`);
725
- return namedTypeRef({ name: entry.abapName });
726
- }
775
+ if (isRef(schema)) return refTypeRef(schema.$ref, plan);
727
776
  const type = pluckType$2(schema);
728
777
  if (type === "array") {
729
778
  const items = schema.items;
@@ -733,26 +782,33 @@ function mapSchemaToTypeRef(schema, plan, ctx = {}) {
733
782
  tableKind: "standard"
734
783
  });
735
784
  }
736
- if (type === "object" || isRecord$2(schema.properties)) {
785
+ if (type === "object" || isRecord$2(schema.properties)) return objectTypeRef(schema, plan, ctx);
786
+ if (type === void 0) return untypedTypeRef(schema, plan, ctx);
787
+ return builtinType({ name: mapPrimitive(schema) });
788
+ }
789
+ function refTypeRef(ref, plan) {
790
+ const entry = refToEntry(ref, plan);
791
+ if (!entry) throw new UnsupportedSchemaError(`mapSchemaToTypeRef: unresolved $ref "${ref}"`);
792
+ return namedTypeRef({ name: entry.abapName });
793
+ }
794
+ function objectTypeRef(schema, plan, ctx) {
795
+ const matched = findPlannedEntryBySchema$1(schema, plan, ctx.entryId);
796
+ if (matched) return namedTypeRef({ name: matched.abapName });
797
+ const fields = buildStructureFields$1(schema, plan, ctx);
798
+ if (fields.length === 0) return builtinType({ name: "string" });
799
+ return structureType({ fields });
800
+ }
801
+ function untypedTypeRef(schema, plan, ctx) {
802
+ for (const c of [
803
+ "allOf",
804
+ "oneOf",
805
+ "anyOf"
806
+ ]) if (Array.isArray(schema[c])) {
737
807
  const matched = findPlannedEntryBySchema$1(schema, plan, ctx.entryId);
738
808
  if (matched) return namedTypeRef({ name: matched.abapName });
739
- const fields = buildStructureFields$1(schema, plan, ctx);
740
- if (fields.length === 0) return builtinType({ name: "string" });
741
- return structureType({ fields });
742
- }
743
- if (type === void 0) {
744
- for (const c of [
745
- "allOf",
746
- "oneOf",
747
- "anyOf"
748
- ]) if (Array.isArray(schema[c])) {
749
- const matched = findPlannedEntryBySchema$1(schema, plan, ctx.entryId);
750
- if (matched) return namedTypeRef({ name: matched.abapName });
751
- return builtinType({ name: "string" });
752
- }
753
809
  return builtinType({ name: "string" });
754
810
  }
755
- return builtinType({ name: mapPrimitive(schema) });
811
+ return builtinType({ name: "string" });
756
812
  }
757
813
  /** If a schema is identical-by-reference to a planned entry, return it. */
758
814
  function findPlannedEntryBySchema$1(schema, plan, excludeId) {
@@ -780,49 +836,59 @@ function buildEnumMembers(values) {
780
836
  function buildStructureFields$1(schema, plan, ctx) {
781
837
  const fields = [];
782
838
  const seen = /* @__PURE__ */ new Map();
783
- const addField = (name, type, src) => {
784
- const sanitized = sanitizeIdent(name, "param");
785
- const prev = seen.get(sanitized);
786
- if (prev) {
787
- if (prev === type || shallowTypeRefEqual(prev, type)) return;
788
- throw new CollisionError(`buildStructureFields: duplicate field "${sanitized}" with conflicting types`);
789
- }
790
- seen.set(sanitized, type);
791
- fields.push({
792
- name: sanitized,
793
- type
794
- });
795
- if (isNullable$1(src)) {
796
- const flagName = sanitizeIdent(`${name}_is_null`, "param");
797
- if (!seen.has(flagName)) {
798
- const flagType = builtinType({ name: "abap_bool" });
799
- seen.set(flagName, flagType);
800
- fields.push({
801
- name: flagName,
802
- type: flagType
803
- });
804
- }
805
- }
806
- };
807
- const allOf = schema.allOf;
808
- if (Array.isArray(allOf)) for (const sub of allOf) {
809
- if (!isRecord$2(sub)) continue;
810
- const subSchema = sub;
811
- const subProps = isRef(subSchema) ? (() => {
812
- const entry = refToEntry(subSchema.$ref, plan);
813
- if (!entry) return void 0;
814
- return entry.schema.properties;
815
- })() : subSchema.properties;
816
- if (isRecord$2(subProps)) for (const [name, propSchema] of Object.entries(subProps)) {
817
- if (!isRecord$2(propSchema)) continue;
818
- addField(name, mapSchemaToTypeRef(propSchema, plan, ctx), propSchema);
839
+ addAllOfFields(schema, plan, ctx, fields, seen);
840
+ addPropertyFields(schema.properties, plan, ctx, fields, seen);
841
+ addUnionFields(schema, plan, ctx, fields, seen);
842
+ addExtraField(schema, plan, fields, seen);
843
+ return fields;
844
+ }
845
+ function addStructureField(fields, seen, name, type, src) {
846
+ const sanitized = sanitizeIdent(name, "param");
847
+ const prev = seen.get(sanitized);
848
+ if (prev) {
849
+ if (prev === type || shallowTypeRefEqual(prev, type)) return;
850
+ throw new CollisionError(`buildStructureFields: duplicate field "${sanitized}" with conflicting types`);
851
+ }
852
+ seen.set(sanitized, type);
853
+ fields.push({
854
+ name: sanitized,
855
+ type
856
+ });
857
+ if (isNullable$1(src)) {
858
+ const flagName = sanitizeIdent(`${name}_is_null`, "param");
859
+ if (!seen.has(flagName)) {
860
+ const flagType = builtinType({ name: "abap_bool" });
861
+ seen.set(flagName, flagType);
862
+ fields.push({
863
+ name: flagName,
864
+ type: flagType
865
+ });
819
866
  }
820
867
  }
821
- const props = schema.properties;
822
- if (isRecord$2(props)) for (const [name, propSchema] of Object.entries(props)) {
868
+ }
869
+ function addPropertyFields(props, plan, ctx, fields, seen) {
870
+ if (!isRecord$2(props)) return;
871
+ for (const [name, propSchema] of Object.entries(props)) {
823
872
  if (!isRecord$2(propSchema)) continue;
824
- addField(name, mapSchemaToTypeRef(propSchema, plan, ctx), propSchema);
873
+ addStructureField(fields, seen, name, mapSchemaToTypeRef(propSchema, plan, ctx), propSchema);
825
874
  }
875
+ }
876
+ function addAllOfFields(schema, plan, ctx, fields, seen) {
877
+ const allOf = schema.allOf;
878
+ if (!Array.isArray(allOf)) return;
879
+ for (const sub of allOf) {
880
+ if (!isRecord$2(sub)) continue;
881
+ const subProps = resolvedProperties(sub, plan);
882
+ if (isRecord$2(subProps)) addPropertyFields(subProps, plan, ctx, fields, seen);
883
+ }
884
+ }
885
+ function resolvedProperties(subSchema, plan) {
886
+ if (!isRef(subSchema)) return subSchema.properties;
887
+ const entry = refToEntry(subSchema.$ref, plan);
888
+ if (!entry) return void 0;
889
+ return entry.schema.properties;
890
+ }
891
+ function addUnionFields(schema, plan, ctx, fields, seen) {
826
892
  for (const combinator of ["oneOf", "anyOf"]) {
827
893
  const arr = schema[combinator];
828
894
  if (!Array.isArray(arr)) continue;
@@ -831,8 +897,7 @@ function buildStructureFields$1(schema, plan, ctx) {
831
897
  addFieldRaw(fields, seen, "kind", builtinType({ name: "string" }));
832
898
  arr.forEach((sub, idx) => {
833
899
  if (!isRecord$2(sub)) return;
834
- const variantName = variantFieldName(sub, idx);
835
- addFieldRaw(fields, seen, variantName, mapSchemaToTypeRef(sub, plan, ctx));
900
+ addFieldRaw(fields, seen, variantFieldName(sub, idx), mapSchemaToTypeRef(sub, plan, ctx));
836
901
  });
837
902
  } else {
838
903
  addFieldRaw(fields, seen, "variant_kind", builtinType({ name: "string" }));
@@ -844,17 +909,18 @@ function buildStructureFields$1(schema, plan, ctx) {
844
909
  });
845
910
  }
846
911
  }
912
+ }
913
+ function addExtraField(schema, plan, fields, seen) {
847
914
  const ap = schema.additionalProperties;
848
- if (ap === true || isRecord$2(ap)) {
849
- const kvEntry = plan.byId.get("aux.kv");
850
- if (kvEntry) addFieldRaw(fields, seen, "_extra", tableType({
851
- rowType: namedTypeRef({ name: kvEntry.abapName }),
852
- tableKind: "hashed",
853
- uniqueness: "unique",
854
- keyFields: ["key"]
855
- }));
856
- }
857
- return fields;
915
+ if (ap !== true && !isRecord$2(ap)) return;
916
+ const kvEntry = plan.byId.get("aux.kv");
917
+ if (!kvEntry) return;
918
+ addFieldRaw(fields, seen, "_extra", tableType({
919
+ rowType: namedTypeRef({ name: kvEntry.abapName }),
920
+ tableKind: "hashed",
921
+ uniqueness: "unique",
922
+ keyFields: ["key"]
923
+ }));
858
924
  }
859
925
  function addFieldRaw(fields, seen, rawName, type) {
860
926
  const name = sanitizeIdent(rawName, "param");
@@ -959,7 +1025,7 @@ function validateLocal(field, value) {
959
1025
  function deriveFromBase(base) {
960
1026
  const trimmed = base.trim();
961
1027
  if (trimmed.length === 0) throw new NamesConfigError("Invalid \"base\" name: value is empty or whitespace.");
962
- if (!/^[A-Za-z][A-Za-z0-9_]*$/.test(trimmed)) throw new NamesConfigError(`Invalid "base" name: "${base}" must start with a letter and contain only letters, digits, and underscores.`);
1028
+ if (!/^[A-Za-z]\w*$/.test(trimmed)) throw new NamesConfigError(`Invalid "base" name: "${base}" must start with a letter and contain only letters, digits, and underscores.`);
963
1029
  const upper = trimmed.toUpperCase();
964
1030
  return {
965
1031
  typesInterface: `ZIF_${upper}_TYPES`,
@@ -1037,10 +1103,7 @@ function emitTypesInterface(spec, plan, opts) {
1037
1103
  for (const entry of plan.entries) {
1038
1104
  if (entry.source === "auxiliary") continue;
1039
1105
  const aux = ctx.mapAux.get(entry.id);
1040
- if (aux) {
1041
- typeDefs.push(aux.entryTypeDef);
1042
- typeDefs.push(aux.mapTypeDef);
1043
- }
1106
+ if (aux) typeDefs.push(aux.entryTypeDef, aux.mapTypeDef);
1044
1107
  typeDefs.push(buildEntryTypeDef(entry, ctx));
1045
1108
  }
1046
1109
  return {
@@ -1080,7 +1143,7 @@ function originalNameFromId(id) {
1080
1143
  }
1081
1144
  /** Normalise a raw name for v2: replace `$` with `_`, then snake_case ≤ 30. */
1082
1145
  function v2Name(raw, allocator) {
1083
- return allocator(raw.replaceAll(/\$/g, "_"), "type");
1146
+ return allocator(raw.replaceAll("$", "_"), "type");
1084
1147
  }
1085
1148
  function buildContext(spec, plan, opts) {
1086
1149
  const alloc = makeNameAllocator(/* @__PURE__ */ new Set());
@@ -1153,45 +1216,10 @@ function deriveRawName(entry) {
1153
1216
  function buildEntryTypeDef(entry, ctx) {
1154
1217
  const name = ctx.nameOf.get(entry.id);
1155
1218
  const schema = entry.schema;
1156
- const leadingDoc = [];
1157
- const original = ctx.originalNameOf.get(entry.id);
1158
- if (original !== void 0) leadingDoc.push(`@openapi-schema ${original}`);
1159
- else if (entry.source === "inline") leadingDoc.push(`@openapi-path ${entry.id}`);
1160
- const description = schema.description;
1161
- if (typeof description === "string" && description.trim()) for (const line of description.split(/\r?\n/)) leadingDoc.push(`@openapi-description ${line}`);
1162
- const enumValues = schema.enum;
1219
+ const leadingDoc = buildLeadingDoc(entry, ctx);
1220
+ const enumDef = tryEnumTypeDef(entry, name, leadingDoc);
1221
+ if (enumDef !== void 0) return enumDef;
1163
1222
  const schemaType = pluckType$1(schema);
1164
- if (Array.isArray(enumValues) && (schemaType === "string" || schemaType === void 0)) {
1165
- const members = [];
1166
- const seen = /* @__PURE__ */ new Set();
1167
- for (const v of enumValues) if (typeof v === "string") {
1168
- let nm = sanitizeIdent(v, "type");
1169
- let i = 2;
1170
- while (seen.has(nm)) nm = `${nm}_${i++}`;
1171
- seen.add(nm);
1172
- members.push({
1173
- name: nm,
1174
- value: v
1175
- });
1176
- } else if (typeof v === "number") {
1177
- let nm = sanitizeIdent(`v_${v}`, "type");
1178
- let i = 2;
1179
- while (seen.has(nm)) nm = `${nm}_${i++}`;
1180
- seen.add(nm);
1181
- members.push({
1182
- name: nm,
1183
- value: v
1184
- });
1185
- }
1186
- if (members.length > 0) return typeDef({
1187
- name,
1188
- type: enumType({
1189
- baseType: builtinType({ name: "string" }),
1190
- members
1191
- }),
1192
- abapDoc: leadingDoc.length > 0 ? leadingDoc : void 0
1193
- });
1194
- }
1195
1223
  if (schemaType === "array") {
1196
1224
  const items = schema.items;
1197
1225
  const rowSchema = isRecord$1(items) ? items : {};
@@ -1222,6 +1250,54 @@ function buildEntryTypeDef(entry, ctx) {
1222
1250
  abapDoc: finaliseDoc(leadingDoc, ctx.backRefs.get(entry.id) ?? [])
1223
1251
  });
1224
1252
  }
1253
+ function buildLeadingDoc(entry, ctx) {
1254
+ const leadingDoc = [];
1255
+ const original = ctx.originalNameOf.get(entry.id);
1256
+ if (original !== void 0) leadingDoc.push(`@openapi-schema ${original}`);
1257
+ else if (entry.source === "inline") leadingDoc.push(`@openapi-path ${entry.id}`);
1258
+ const description = entry.schema.description;
1259
+ if (typeof description === "string" && description.trim()) for (const line of description.split(/\r?\n/)) leadingDoc.push(`@openapi-description ${line}`);
1260
+ return leadingDoc;
1261
+ }
1262
+ /** Enum shortcut: string with enum values. Returns undefined when the entry
1263
+ * is not enum-shaped or has no usable members. */
1264
+ function tryEnumTypeDef(entry, name, leadingDoc) {
1265
+ const schema = entry.schema;
1266
+ const enumValues = schema.enum;
1267
+ const schemaType = pluckType$1(schema);
1268
+ if (!Array.isArray(enumValues)) return void 0;
1269
+ if (schemaType !== "string" && schemaType !== void 0) return void 0;
1270
+ const members = buildDedupedEnumMembers(enumValues);
1271
+ if (members.length === 0) return void 0;
1272
+ return typeDef({
1273
+ name,
1274
+ type: enumType({
1275
+ baseType: builtinType({ name: "string" }),
1276
+ members
1277
+ }),
1278
+ abapDoc: leadingDoc.length > 0 ? leadingDoc : void 0
1279
+ });
1280
+ }
1281
+ function buildDedupedEnumMembers(enumValues) {
1282
+ const members = [];
1283
+ const seen = /* @__PURE__ */ new Set();
1284
+ for (const v of enumValues) if (typeof v === "string") members.push({
1285
+ name: uniqueEnumName(sanitizeIdent(v, "type"), seen),
1286
+ value: v
1287
+ });
1288
+ else if (typeof v === "number") members.push({
1289
+ name: uniqueEnumName(sanitizeIdent(`v_${v}`, "type"), seen),
1290
+ value: v
1291
+ });
1292
+ return members;
1293
+ }
1294
+ function uniqueEnumName(base, seen) {
1295
+ let nm = base;
1296
+ let i = 2;
1297
+ while (seen.has(nm)) nm = `${nm}_${i++}`;
1298
+ seen.add(nm);
1299
+ return nm;
1300
+ }
1225
1301
  function finaliseDoc(base, backRefs) {
1226
1302
  const out = [...base];
1227
1303
  for (const br of backRefs) out.push(br);
@@ -1233,7 +1309,7 @@ function buildStructureFields(entry, ctx) {
1233
1309
  const seen = /* @__PURE__ */ new Map();
1234
1310
  const addField = (rawName, rawType, src) => {
1235
1311
  const type = asFieldTypeRef(rawType);
1236
- const fieldName = sanitizeIdent(rawName.replaceAll(/\$/g, "_"), "param");
1312
+ const fieldName = sanitizeIdent(rawName.replaceAll("$", "_"), "param");
1237
1313
  const prev = seen.get(fieldName);
1238
1314
  if (prev !== void 0) {
1239
1315
  if (!typeRefEqual(prev, type)) throw new PolymorphismConflictError(`buildStructureFields: conflicting types for field "${fieldName}" in "${entry.id}"`);
@@ -1264,26 +1340,9 @@ function buildStructureFields(entry, ctx) {
1264
1340
  addField(propName, ref, propSchema);
1265
1341
  }
1266
1342
  };
1267
- const allOf = schema.allOf;
1268
- if (Array.isArray(allOf)) for (const sub of allOf) {
1269
- if (!isRecord$1(sub)) continue;
1270
- if (isRef(sub)) {
1271
- const refEntry = resolveRefEntry(sub.$ref, ctx);
1272
- if (refEntry !== void 0) mergeProperties(refEntry.schema);
1273
- } else mergeProperties(sub);
1274
- }
1343
+ mergeVariantList(schema.allOf, ctx, mergeProperties);
1275
1344
  mergeProperties(schema);
1276
- for (const combinator of ["oneOf", "anyOf"]) {
1277
- const arr = schema[combinator];
1278
- if (!Array.isArray(arr)) continue;
1279
- for (const sub of arr) {
1280
- if (!isRecord$1(sub)) continue;
1281
- if (isRef(sub)) {
1282
- const refEntry = resolveRefEntry(sub.$ref, ctx);
1283
- if (refEntry !== void 0) mergeProperties(refEntry.schema);
1284
- } else mergeProperties(sub);
1285
- }
1286
- }
1345
+ for (const combinator of ["oneOf", "anyOf"]) mergeVariantList(schema[combinator], ctx, mergeProperties);
1287
1346
  const aux = ctx.mapAux.get(entry.id);
1288
1347
  if (aux) {
1289
1348
  const fieldName = sanitizeIdent("entries", "param");
@@ -1298,6 +1357,17 @@ function buildStructureFields(entry, ctx) {
1298
1357
  }
1299
1358
  return fields;
1300
1359
  }
1360
+ function mergeVariantList(variants, ctx, merge) {
1361
+ if (!Array.isArray(variants)) return;
1362
+ for (const sub of variants) mergeVariantProperties(sub, ctx, merge);
1363
+ }
1364
+ function mergeVariantProperties(sub, ctx, merge) {
1365
+ if (!isRecord$1(sub)) return;
1366
+ if (isRef(sub)) {
1367
+ const refEntry = resolveRefEntry(sub.$ref, ctx);
1368
+ if (refEntry !== void 0) merge(refEntry.schema);
1369
+ } else merge(sub);
1370
+ }
1301
1371
  function resolveRefEntry(ref, ctx) {
1302
1372
  const name = componentNameFromRef$1(ref);
1303
1373
  if (!name) return void 0;
@@ -1326,23 +1396,7 @@ function asFieldTypeRef(t) {
1326
1396
  return namedTypeRef({ name: `STANDARD TABLE OF ${rowExpr} WITH EMPTY KEY` });
1327
1397
  }
1328
1398
  function resolveTypeRef(schema, ownerEntryId, ctx, fieldName) {
1329
- if (isRef(schema)) {
1330
- const target = resolveRefEntry(schema.$ref, ctx);
1331
- if (target === void 0) return builtinType({ name: "string" });
1332
- const ownerPos = ctx.positionOf.get(ownerEntryId) ?? -1;
1333
- const targetPos = ctx.positionOf.get(target.id) ?? -1;
1334
- const isBackEdge = targetPos >= 0 && ownerPos >= 0 && targetPos >= ownerPos;
1335
- if (target.id === ownerEntryId || isBackEdge) {
1336
- const targetOriginal = ctx.originalNameOf.get(target.id) ?? ctx.nameOf.get(target.id) ?? target.id;
1337
- const backRefs = ctx.backRefs.get(ownerEntryId);
1338
- if (backRefs !== void 0) {
1339
- const label = fieldName ? `${fieldName}:${targetOriginal}` : targetOriginal;
1340
- backRefs.push(`@openapi-ref ${label}`);
1341
- }
1342
- return namedTypeRef({ name: "REF TO data" });
1343
- }
1344
- return namedTypeRef({ name: ctx.nameOf.get(target.id) ?? target.abapName });
1345
- }
1399
+ if (isRef(schema)) return resolveNamedTypeRef(schema, ownerEntryId, ctx, fieldName);
1346
1400
  const type = pluckType$1(schema);
1347
1401
  if (type === "array") {
1348
1402
  const items = schema.items;
@@ -1353,31 +1407,45 @@ function resolveTypeRef(schema, ownerEntryId, ctx, fieldName) {
1353
1407
  });
1354
1408
  }
1355
1409
  const props = schema.properties;
1356
- if (type === "object" || isRecord$1(props)) {
1357
- const matched = findPlannedEntryBySchema(schema, ctx.plan, ownerEntryId);
1358
- if (matched !== void 0) {
1359
- const name = ctx.nameOf.get(matched.id);
1360
- if (name !== void 0) return namedTypeRef({ name });
1361
- }
1362
- return builtinType({ name: "string" });
1363
- }
1364
- if (type === void 0) {
1365
- for (const c of [
1366
- "allOf",
1367
- "oneOf",
1368
- "anyOf"
1369
- ]) if (Array.isArray(schema[c])) {
1370
- const matched = findPlannedEntryBySchema(schema, ctx.plan, ownerEntryId);
1371
- if (matched !== void 0) {
1372
- const name = ctx.nameOf.get(matched.id);
1373
- if (name !== void 0) return namedTypeRef({ name });
1374
- }
1375
- return builtinType({ name: "string" });
1376
- }
1377
- return builtinType({ name: "string" });
1378
- }
1410
+ if (type === "object" || isRecord$1(props)) return plannedEntryRef(schema, ctx, ownerEntryId) ?? builtinType({ name: "string" });
1411
+ if (type === void 0) return resolveUntypedTypeRef(schema, ctx, ownerEntryId);
1379
1412
  return builtinType({ name: mapPrimitiveLocal(schema) });
1380
1413
  }
1414
+ function resolveNamedTypeRef(schema, ownerEntryId, ctx, fieldName) {
1415
+ const target = resolveRefEntry(schema.$ref, ctx);
1416
+ if (target === void 0) return builtinType({ name: "string" });
1417
+ const ownerPos = ctx.positionOf.get(ownerEntryId) ?? -1;
1418
+ const targetPos = ctx.positionOf.get(target.id) ?? -1;
1419
+ const isBackEdge = targetPos >= 0 && ownerPos >= 0 && targetPos >= ownerPos;
1420
+ if (target.id === ownerEntryId || isBackEdge) return buildBackEdgeRef(target, ownerEntryId, ctx, fieldName);
1421
+ return namedTypeRef({ name: ctx.nameOf.get(target.id) ?? target.abapName });
1422
+ }
1423
+ function buildBackEdgeRef(target, ownerEntryId, ctx, fieldName) {
1424
+ const targetOriginal = ctx.originalNameOf.get(target.id) ?? ctx.nameOf.get(target.id) ?? target.id;
1425
+ const backRefs = ctx.backRefs.get(ownerEntryId);
1426
+ if (backRefs !== void 0) {
1427
+ const label = fieldName ? `${fieldName}:${targetOriginal}` : targetOriginal;
1428
+ backRefs.push(`@openapi-ref ${label}`);
1429
+ }
1430
+ return namedTypeRef({ name: "REF TO data" });
1431
+ }
1432
+ /** Name ref for a schema object that has a planned entry; undefined when no
1433
+ * planned entry (or no emitted name) exists. */
1434
+ function plannedEntryRef(schema, ctx, ownerEntryId) {
1435
+ const matched = findPlannedEntryBySchema(schema, ctx.plan, ownerEntryId);
1436
+ if (matched === void 0) return void 0;
1437
+ const name = ctx.nameOf.get(matched.id);
1438
+ if (name === void 0) return void 0;
1439
+ return namedTypeRef({ name });
1440
+ }
1441
+ function resolveUntypedTypeRef(schema, ctx, ownerEntryId) {
1442
+ for (const c of [
1443
+ "allOf",
1444
+ "oneOf",
1445
+ "anyOf"
1446
+ ]) if (Array.isArray(schema[c])) return plannedEntryRef(schema, ctx, ownerEntryId) ?? builtinType({ name: "string" });
1447
+ return builtinType({ name: "string" });
1448
+ }
1381
1449
  function findPlannedEntryBySchema(schema, plan, excludeId) {
1382
1450
  for (const entry of plan.entries) {
1383
1451
  if (entry.id === excludeId) continue;
@@ -1622,8 +1690,7 @@ function orderParams(params) {
1622
1690
  }
1623
1691
  function buildMethodAbapDoc(op, cookieSkipped) {
1624
1692
  const lines = [];
1625
- lines.push(`@openapi-operation ${op.operationId}`);
1626
- lines.push(`@openapi-path ${op.method.toUpperCase()} ${op.path}`);
1693
+ lines.push(`@openapi-operation ${op.operationId}`, `@openapi-path ${op.method.toUpperCase()} ${op.path}`);
1627
1694
  if (op.summary) {
1628
1695
  for (const l of op.summary.split(/\r?\n/)) if (l.trim().length > 0) lines.push(l.trim());
1629
1696
  }
@@ -1639,81 +1706,95 @@ function emitOperationsInterface(spec, _plan, opts) {
1639
1706
  const tableAliases = createTableAliasRegistry();
1640
1707
  const opsInterfaceName = opts.name;
1641
1708
  for (const op of spec.operations) {
1642
- const methodName = allocMethod(op.operationId, "method");
1643
- const { emitted, cookieSkipped } = orderParams(op.parameters);
1644
- const allocParam = makeNameAllocator(/* @__PURE__ */ new Set());
1645
- const abapParams = [];
1646
- const paramMappings = [];
1647
- for (const p of emitted) {
1648
- const abapName = allocParam(p.name, "param");
1649
- const ref = schemaToTypeRef(p.schema, compIndex, opts.typesInterfaceName, opsInterfaceName, tableAliases);
1650
- abapParams.push(methodParam({
1651
- paramKind: "importing",
1652
- name: abapName,
1653
- typeRef: ref,
1654
- optional: !p.required
1655
- }));
1656
- paramMappings.push({
1657
- name: abapName,
1658
- openapiName: p.name,
1659
- location: p.in,
1660
- required: p.required,
1661
- typeRefSource: renderTypeRefSource(ref),
1662
- isTable: pluckType(p.schema) === "array"
1663
- });
1664
- }
1665
- const bodySchema = pickRequestBodySchema$1(op);
1666
- const hasBody = bodySchema !== void 0;
1667
- if (hasBody) {
1668
- const bodyName = allocParam("body", "param");
1669
- const ref = schemaToTypeRef(bodySchema, compIndex, opts.typesInterfaceName, opsInterfaceName, tableAliases);
1670
- abapParams.push(methodParam({
1671
- paramKind: "importing",
1672
- name: bodyName,
1673
- typeRef: ref,
1674
- optional: op.requestBody?.required === false ? true : false
1675
- }));
1676
- }
1677
- const success = pickSuccess(op);
1678
- let successStatus;
1679
- let successIsEmptyBody;
1680
- let returningName;
1681
- let returningRef;
1682
- if (!success) {
1683
- successStatus = 200;
1684
- successIsEmptyBody = true;
1685
- returningName = allocParam("success", "param");
1686
- returningRef = builtinType({ name: "abap_bool" });
1687
- } else if (success.schema === void 0) {
1688
- successStatus = success.status;
1689
- successIsEmptyBody = true;
1690
- returningName = allocParam("success", "param");
1691
- returningRef = builtinType({ name: "abap_bool" });
1692
- } else {
1693
- successStatus = success.status;
1694
- successIsEmptyBody = false;
1695
- returningName = allocParam(deriveReturningName(success.schema, compIndex), "param");
1696
- returningRef = schemaToTypeRef(success.schema, compIndex, opts.typesInterfaceName, opsInterfaceName, tableAliases);
1697
- }
1709
+ const { method, mapping } = buildOperationMethod(op, allocMethod(op.operationId, "method"), compIndex, opts, opsInterfaceName, tableAliases);
1710
+ methods.push(method);
1711
+ mappings.push(mapping);
1712
+ }
1713
+ const aliasTypeDefs = buildAliasTypeDefs(tableAliases);
1714
+ return {
1715
+ interface: interfaceDef({
1716
+ name: opts.name.toLowerCase(),
1717
+ members: [...aliasTypeDefs, ...methods],
1718
+ abapDoc: opts.interfaceAbapDoc
1719
+ }),
1720
+ operations: mappings
1721
+ };
1722
+ }
1723
+ function buildOperationMethod(op, methodName, compIndex, opts, opsInterfaceName, tableAliases) {
1724
+ const { emitted, cookieSkipped } = orderParams(op.parameters);
1725
+ const allocParam = makeNameAllocator(/* @__PURE__ */ new Set());
1726
+ const abapParams = [];
1727
+ const paramMappings = [];
1728
+ for (const p of emitted) {
1729
+ const abapName = allocParam(p.name, "param");
1730
+ const ref = schemaToTypeRef(p.schema, compIndex, opts.typesInterfaceName, opsInterfaceName, tableAliases);
1698
1731
  abapParams.push(methodParam({
1699
- paramKind: "returning",
1700
- name: returningName,
1701
- typeRef: returningRef
1732
+ paramKind: "importing",
1733
+ name: abapName,
1734
+ typeRef: ref,
1735
+ optional: !p.required
1702
1736
  }));
1703
- const errorResponses = {};
1704
- for (const r of op.responses) {
1705
- if (!r.isError) continue;
1706
- errorResponses[r.statusCode] = r.description ? { description: r.description } : {};
1707
- }
1708
- const method = methodDef({
1737
+ paramMappings.push({
1738
+ name: abapName,
1739
+ openapiName: p.name,
1740
+ location: p.in,
1741
+ required: p.required,
1742
+ typeRefSource: renderTypeRefSource(ref),
1743
+ isTable: pluckType(p.schema) === "array"
1744
+ });
1745
+ }
1746
+ const bodySchema = pickRequestBodySchema$1(op);
1747
+ const hasBody = bodySchema !== void 0;
1748
+ if (hasBody) {
1749
+ const bodyName = allocParam("body", "param");
1750
+ const ref = schemaToTypeRef(bodySchema, compIndex, opts.typesInterfaceName, opsInterfaceName, tableAliases);
1751
+ abapParams.push(methodParam({
1752
+ paramKind: "importing",
1753
+ name: bodyName,
1754
+ typeRef: ref,
1755
+ optional: op.requestBody?.required === false
1756
+ }));
1757
+ }
1758
+ const success = pickSuccess(op);
1759
+ let successStatus;
1760
+ let successIsEmptyBody;
1761
+ let returningName;
1762
+ let returningRef;
1763
+ if (!success) {
1764
+ successStatus = 200;
1765
+ successIsEmptyBody = true;
1766
+ returningName = allocParam("success", "param");
1767
+ returningRef = builtinType({ name: "abap_bool" });
1768
+ } else if (success.schema === void 0) {
1769
+ successStatus = success.status;
1770
+ successIsEmptyBody = true;
1771
+ returningName = allocParam("success", "param");
1772
+ returningRef = builtinType({ name: "abap_bool" });
1773
+ } else {
1774
+ successStatus = success.status;
1775
+ successIsEmptyBody = false;
1776
+ returningName = allocParam(deriveReturningName(success.schema, compIndex), "param");
1777
+ returningRef = schemaToTypeRef(success.schema, compIndex, opts.typesInterfaceName, opsInterfaceName, tableAliases);
1778
+ }
1779
+ abapParams.push(methodParam({
1780
+ paramKind: "returning",
1781
+ name: returningName,
1782
+ typeRef: returningRef
1783
+ }));
1784
+ const errorResponses = {};
1785
+ for (const r of op.responses) {
1786
+ if (!r.isError) continue;
1787
+ errorResponses[r.statusCode] = r.description ? { description: r.description } : {};
1788
+ }
1789
+ return {
1790
+ method: methodDef({
1709
1791
  name: methodName,
1710
1792
  visibility: "public",
1711
1793
  params: abapParams,
1712
1794
  raising: [namedTypeRef({ name: opts.exceptionClassName.toLowerCase() })],
1713
1795
  abapDoc: buildMethodAbapDoc(op, cookieSkipped)
1714
- });
1715
- methods.push(method);
1716
- mappings.push({
1796
+ }),
1797
+ mapping: {
1717
1798
  operationId: op.operationId,
1718
1799
  methodName,
1719
1800
  method: op.method.toUpperCase(),
@@ -1724,16 +1805,7 @@ function emitOperationsInterface(spec, _plan, opts) {
1724
1805
  successIsEmptyBody,
1725
1806
  errorResponses,
1726
1807
  returningName
1727
- });
1728
- }
1729
- const aliasTypeDefs = buildAliasTypeDefs(tableAliases);
1730
- return {
1731
- interface: interfaceDef({
1732
- name: opts.name.toLowerCase(),
1733
- members: [...aliasTypeDefs, ...methods],
1734
- abapDoc: opts.interfaceAbapDoc
1735
- }),
1736
- operations: mappings
1808
+ }
1737
1809
  };
1738
1810
  }
1739
1811
  function renderTypeRefSource(ref) {
@@ -1873,7 +1945,6 @@ function emitExceptionClass(opts) {
1873
1945
  // src/emit/templates/locals-def.abap.ts
1874
1946
  function buildLocalsDef(names) {
1875
1947
  const { localJsonClass: JSON_CLS, localJsonParserClass: JSON_PARSER, localResponseClass: RESPONSE, localHttpClass: HTTP } = names;
1876
- names.exceptionClass;
1877
1948
  return `* Generated by @abapify/openai-codegen - local class declarations.
1878
1949
  * ZERO Z-dependencies: these locals wrap SAP kernel APIs only.
1879
1950
  * Per-operation methods map HTTP status codes to the custom exception.
@@ -2240,9 +2311,7 @@ function renderSuccessBody(opts) {
2240
2311
  function renderErrorBranch(statusCode, description, opts) {
2241
2312
  const responseVar = opts.responseVarName ?? "response";
2242
2313
  const lines = [];
2243
- lines.push(` WHEN ${statusCode}.`);
2244
- lines.push(` RAISE EXCEPTION NEW ${opts.exceptionClassName}(`);
2245
- lines.push(` status = ${statusCode}`);
2314
+ lines.push(` WHEN ${statusCode}.`, ` RAISE EXCEPTION NEW ${opts.exceptionClassName}(`, ` status = ${statusCode}`);
2246
2315
  if (description !== void 0 && description.length > 0) lines.push(` description = '${escapeAbapLiteral(description)}'`);
2247
2316
  lines.push(` body = ${responseVar}->body( ) ).`);
2248
2317
  return lines;
@@ -2250,9 +2319,7 @@ function renderErrorBranch(statusCode, description, opts) {
2250
2319
  function renderOthersBranch(description, opts) {
2251
2320
  const responseVar = opts.responseVarName ?? "response";
2252
2321
  const lines = [];
2253
- lines.push(` WHEN OTHERS.`);
2254
- lines.push(` RAISE EXCEPTION NEW ${opts.exceptionClassName}(`);
2255
- lines.push(` status = ${responseVar}->status( )`);
2322
+ lines.push(` WHEN OTHERS.`, ` RAISE EXCEPTION NEW ${opts.exceptionClassName}(`, ` status = ${responseVar}->status( )`);
2256
2323
  if (description !== void 0 && description.length > 0) lines.push(` description = '${escapeAbapLiteral(description)}'`);
2257
2324
  lines.push(` body = ${responseVar}->body( ) ).`);
2258
2325
  return lines;
@@ -2262,23 +2329,18 @@ function mapResponseHandling(operation, opts) {
2262
2329
  const lines = [];
2263
2330
  lines.push(`CASE ${responseVar}->status( ).`);
2264
2331
  const success = pickSuccessStatus(operation.responses);
2265
- const successConsumedOthers = success !== void 0 && success.whenLabel === "OTHERS";
2266
- if (success !== void 0 && !successConsumedOthers) {
2267
- lines.push(` WHEN ${success.whenLabel}.`);
2268
- lines.push(` ${renderSuccessBody(opts)}`);
2269
- }
2332
+ const successConsumedOthers = success?.whenLabel === "OTHERS";
2333
+ if (success !== void 0 && !successConsumedOthers) lines.push(` WHEN ${success.whenLabel}.`, ` ${renderSuccessBody(opts)}`);
2270
2334
  for (const resp of operation.responses) {
2271
2335
  if (!resp.isError) continue;
2272
2336
  if (resp.statusCode === "default") continue;
2273
2337
  if (!/^\d+$/.test(resp.statusCode)) continue;
2274
2338
  lines.push(...renderErrorBranch(resp.statusCode, resp.description, opts));
2275
2339
  }
2276
- if (successConsumedOthers) {
2277
- lines.push(` WHEN OTHERS.`);
2278
- lines.push(` ${renderSuccessBody(opts)}`);
2279
- } else {
2340
+ if (successConsumedOthers) lines.push(` WHEN OTHERS.`, ` ${renderSuccessBody(opts)}`);
2341
+ else {
2280
2342
  const defaultResp = operation.responses.find((r) => r.statusCode === "default");
2281
- const description = defaultResp !== void 0 && defaultResp.isError ? defaultResp.description : void 0;
2343
+ const description = defaultResp?.isError ? defaultResp.description : void 0;
2282
2344
  lines.push(...renderOthersBranch(description, opts));
2283
2345
  }
2284
2346
  lines.push(`ENDCASE.`);
@@ -2353,7 +2415,7 @@ function buildClasXml(className, description, language) {
2353
2415
  `;
2354
2416
  }
2355
2417
  function escapeXml(value) {
2356
- return value.replaceAll(/&/g, "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;");
2418
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;");
2357
2419
  }
2358
2420
  async function writeAbapgitLayout(artifact, outDir) {
2359
2421
  assertValidClassName(artifact.className);
@@ -2575,15 +2637,13 @@ function writeLayout(artifact, format, outDir) {
2575
2637
  }
2576
2638
  async function writeClientBundle(artifacts, format, outDir) {
2577
2639
  const all = /* @__PURE__ */ new Set();
2578
- for (const artifact of [
2640
+ const layouts = await Promise.all([
2579
2641
  artifacts.types,
2580
2642
  artifacts.operations,
2581
2643
  artifacts.exception,
2582
2644
  artifacts.implementation
2583
- ]) {
2584
- const r = await writeLayout(artifact, format, outDir);
2585
- for (const f of r.files) all.add(f);
2586
- }
2645
+ ].map((artifact) => writeLayout(artifact, format, outDir)));
2646
+ for (const r of layouts) for (const f of r.files) all.add(f);
2587
2647
  return { files: [...all].sort((a, b) => a.localeCompare(b)) };
2588
2648
  }
2589
2649
 
@@ -2693,8 +2753,7 @@ function renderFetchCall(mapping, bodyIsBinary, jsonLocalClass) {
2693
2753
  args.push({
2694
2754
  key: "method",
2695
2755
  value: `'${mapping.method}'`
2696
- });
2697
- args.push({
2756
+ }, {
2698
2757
  key: "path",
2699
2758
  value: renderPathValue(mapping)
2700
2759
  });
@@ -2934,4 +2993,4 @@ async function generate(options) {
2934
2993
  }
2935
2994
 
2936
2995
  export { makeNameAllocator as C, isRef as D, normalizeSpec as E, UnsupportedSchemaError as S, loadSpec as T, mapSchemaToTypeDef as _, writeGctsLayout as a, CollisionError as b, mapResponseHandling as c, emitOperationsInterface as d, PolymorphismConflictError as f, mapPrimitive as g, resolveNames as h, writeGctsInterface as i, emitLocalClasses as l, NamesConfigError as m, writeClientBundle as n, writeAbapgitInterface as o, emitTypesInterface as p, writeLayout as r, writeAbapgitLayout as s, generate as t, emitExceptionClass as u, mapSchemaToTypeRef as v, sanitizeIdent as w, CyclicTypeError as x, planTypes as y };
2937
- //# sourceMappingURL=generate-DgXsowgP.mjs.map
2996
+ //# sourceMappingURL=generate-BevNTSFF.mjs.map