@supalive/codegen 1.20.0 → 1.20.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { n as generateToDisk, r as readEntryConfig } from "./src-BG6JNyje.js";
2
+ import { n as generateToDisk, r as readEntryConfig } from "./src-BHtJlw3J.js";
3
3
  import path from "node:path";
4
4
  //#region src/cli.ts
5
5
  function parseArgs(argv) {
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import { a as emit, c as loadSchemaEnums, i as resolveConfig, n as generateToDisk, o as extractClient, r as readEntryConfig, s as SchemaEnumRegistry, t as generate } from "./src-BG6JNyje.js";
1
+ import { a as emit, c as loadSchemaEnums, i as resolveConfig, n as generateToDisk, o as extractClient, r as readEntryConfig, s as SchemaEnumRegistry, t as generate } from "./src-BHtJlw3J.js";
2
2
  export { SchemaEnumRegistry, emit, extractClient, generate, generateToDisk, loadSchemaEnums, readEntryConfig, resolveConfig };
@@ -186,7 +186,10 @@ function scanOutputSchemaNode(node) {
186
186
  if (methods.has("default") || methods.has("catch") || methods.has("prefault")) meta.optional = true;
187
187
  const description = topLevelZodDescription(init);
188
188
  if (description) meta.description = description;
189
- if (meta.numeric || meta.optional || meta.description) fieldMeta.set(key, meta);
189
+ const enumMeta = topLevelEnumMeta(init);
190
+ if (enumMeta?.modelName) meta.modelName = enumMeta.modelName;
191
+ if (enumMeta?.values) meta.enumValues = enumMeta.values;
192
+ if (meta.numeric || meta.optional || meta.description || meta.modelName || meta.enumValues) fieldMeta.set(key, meta);
190
193
  const nestedMeta = scanOutputSchemaNode(init);
191
194
  if (nestedMeta) nested.set(key, nestedMeta);
192
195
  }
@@ -348,7 +351,10 @@ function scanObjectSchema(schema) {
348
351
  if (methods.has("default") || methods.has("catch") || methods.has("prefault")) meta.optional = true;
349
352
  const description = topLevelZodDescription(init);
350
353
  if (description) meta.description = description;
351
- if (meta.numeric || meta.optional || meta.description) map.set(key, meta);
354
+ const enumMeta = topLevelEnumMeta(init);
355
+ if (enumMeta?.modelName) meta.modelName = enumMeta.modelName;
356
+ if (enumMeta?.values) meta.enumValues = enumMeta.values;
357
+ if (meta.numeric || meta.optional || meta.description || meta.modelName || meta.enumValues) map.set(key, meta);
352
358
  }
353
359
  return map;
354
360
  }
@@ -387,6 +393,72 @@ function topLevelZodDescription(node) {
387
393
  cur = expr.getExpression();
388
394
  }
389
395
  }
396
+ /**
397
+ * For a field whose init is a Zod enum, read the enum's wire values and the
398
+ * model name it provides via `.modelName("X")` / `.meta({ __genType: "X" })`.
399
+ * Works whether the enum is inlined in the field or referenced through a const
400
+ * (e.g. `ZodTenantBillingPlanType`), even when wrapped in `.meta({...})` /
401
+ * `.optional()` and defined through `z.lazy(() => ...)`. Returns `undefined`
402
+ * when the chain is not an enum, so object/array model names (which
403
+ * `scanOutputSchemaNode` already reads at the top level) are not captured.
404
+ */
405
+ function topLevelEnumMeta(node) {
406
+ let modelName;
407
+ let isEnum = false;
408
+ const values = [];
409
+ const walk = (n) => {
410
+ if (!n) return;
411
+ if (Node.isIdentifier(n)) {
412
+ const init = resolveIdentifierInit(n);
413
+ if (init) walk(init);
414
+ return;
415
+ }
416
+ if (Node.isPropertyAccessExpression(n)) return;
417
+ if (!Node.isCallExpression(n)) return;
418
+ const expr = n.getExpression();
419
+ if (!Node.isPropertyAccessExpression(expr)) return;
420
+ const method = expr.getName();
421
+ if (method === "enum") {
422
+ isEnum = true;
423
+ const arg = n.getArguments()[0];
424
+ if (arg && Node.isArrayLiteralExpression(arg)) {
425
+ for (const el of arg.getElements()) if (Node.isStringLiteral(el)) values.push(el.getLiteralValue());
426
+ }
427
+ } else if (method === "modelName") {
428
+ const arg = n.getArguments()[0];
429
+ if (arg && Node.isStringLiteral(arg)) modelName = arg.getLiteralValue();
430
+ } else if (method === "meta") {
431
+ const arg = n.getArguments()[0];
432
+ if (arg && Node.isObjectLiteralExpression(arg)) {
433
+ const genType = arg.getProperty("__genType");
434
+ if (genType && Node.isPropertyAssignment(genType)) {
435
+ const init = genType.getInitializer();
436
+ if (init && Node.isStringLiteral(init)) modelName = init.getLiteralValue();
437
+ }
438
+ }
439
+ } else if (method === "lazy") {
440
+ const arg = n.getArguments()[0];
441
+ if (arg && Node.isArrowFunction(arg)) {
442
+ const body = arg.getBody();
443
+ if (Node.isCallExpression(body)) {
444
+ walk(body);
445
+ return;
446
+ }
447
+ if (Node.isBlock(body)) {
448
+ for (const stmt of body.getStatements()) if (Node.isReturnStatement(stmt)) walk(stmt.getExpression());
449
+ return;
450
+ }
451
+ }
452
+ }
453
+ walk(expr.getExpression());
454
+ };
455
+ walk(node);
456
+ if (!isEnum) return void 0;
457
+ return {
458
+ modelName,
459
+ values: values.length > 0 ? values : void 0
460
+ };
461
+ }
390
462
  function findZodObjectLiteral(node) {
391
463
  let cur = node;
392
464
  while (cur && Node.isCallExpression(cur)) {
@@ -564,7 +636,7 @@ async function loadSchemaEnums(config) {
564
636
  } catch {}
565
637
  }
566
638
  } catch (err) {
567
- console.warn(`[supalive-codegen] Could not load schemaModule (${config.schemaModule}) for schema-aware enum names; falling back to structural names. ${err instanceof Error ? err.message : String(err)}`);
639
+ console.warn(`[supalive-codegen] Could not load schemaModule (${config.schemaModule}) for schema-aware enum names; falling back to structural names. Error: ${err instanceof Error ? err.message : String(err)}`);
568
640
  return SchemaEnumRegistry.empty();
569
641
  }
570
642
  }
@@ -672,6 +744,8 @@ var Extractor = class {
672
744
  if (meta.numeric && (f.type.kind === "double" || f.type.kind === "int")) f.type = { kind: meta.numeric };
673
745
  if (meta.optional) f.optional = true;
674
746
  if (meta.description && !f.doc) f.doc = meta.description;
747
+ if (meta.modelName && f.type.kind === "enum") this.setEnumCustomName(f.type, meta.modelName);
748
+ else if (meta.modelName && meta.enumValues && f.type.kind === "string") this.upgradeStringToEnum(f, meta, argsType.name);
675
749
  }
676
750
  }
677
751
  const ret = unwrapPromise(sig.getReturnType());
@@ -692,20 +766,24 @@ var Extractor = class {
692
766
  /**
693
767
  * Recursively apply output schema metadata to a DartType tree.
694
768
  * Patches numeric types (int/bigint), renames models via __genType,
695
- * and recurses into nested objects and lists.
769
+ * names enums from their `.modelName("X")`, and recurses into nested
770
+ * objects and lists.
696
771
  */
697
772
  applyOutputMeta(dt, meta) {
698
- if (meta.modelName && dt.kind === "model") {
699
- const existing = this.modelRegistry.get(dt.name);
700
- if (existing && !this.modelRegistry.has(meta.modelName)) {
701
- const renamed = {
702
- ...existing,
703
- name: meta.modelName
704
- };
705
- this.modelRegistry.set(meta.modelName, renamed);
706
- this.modelRegistry.delete(dt.name);
707
- }
708
- dt.name = meta.modelName;
773
+ if (meta.modelName) {
774
+ if (dt.kind === "model") {
775
+ const existing = this.modelRegistry.get(dt.name);
776
+ if (existing && !this.modelRegistry.has(meta.modelName)) {
777
+ const renamed = {
778
+ ...existing,
779
+ name: meta.modelName
780
+ };
781
+ this.modelRegistry.set(meta.modelName, renamed);
782
+ this.modelRegistry.delete(dt.name);
783
+ }
784
+ dt.name = meta.modelName;
785
+ } else if (dt.kind === "enum") this.setEnumCustomName(dt, meta.modelName);
786
+ else if (dt.kind === "list" && dt.element.kind === "enum") this.setEnumCustomName(dt.element, meta.modelName);
709
787
  }
710
788
  if (dt.kind === "model") {
711
789
  const model = this.modelRegistry.get(dt.name);
@@ -715,6 +793,8 @@ var Extractor = class {
715
793
  if (fMeta.numeric && (f.type.kind === "double" || f.type.kind === "int")) f.type = { kind: fMeta.numeric };
716
794
  if (fMeta.optional) f.optional = true;
717
795
  if (fMeta.description && !f.doc) f.doc = fMeta.description;
796
+ if (fMeta.modelName && f.type.kind === "enum") this.setEnumCustomName(f.type, fMeta.modelName);
797
+ else if (fMeta.modelName && fMeta.enumValues && f.type.kind === "string") this.upgradeStringToEnum(f, fMeta, dt.name);
718
798
  }
719
799
  const nestedMeta = meta.nested.get(f.jsonKey);
720
800
  if (nestedMeta) this.applyOutputMeta(f.type, nestedMeta);
@@ -724,6 +804,26 @@ var Extractor = class {
724
804
  if (elMeta) this.applyOutputMeta(dt.element, elMeta);
725
805
  }
726
806
  }
807
+ /** Give an enum a canonical name from `.modelName()` / `__genType`. */
808
+ setEnumCustomName(type, name) {
809
+ if (type.name === name) return;
810
+ const group = this.enumGroups.get(type.name);
811
+ if (group) group.customName = name;
812
+ }
813
+ /**
814
+ * A field whose TS type widened to `String` but whose returns/args Zod schema
815
+ * declares an enum (e.g. a handler returning literal `plan` strings) is
816
+ * upgraded to the enum, named from the schema's `.modelName("X")`.
817
+ */
818
+ upgradeStringToEnum(f, meta, stem) {
819
+ const type = this.registerEnum(meta.enumValues, {
820
+ hint: f.jsonKey,
821
+ field: f.jsonKey,
822
+ stem: cleanStem(stem)
823
+ });
824
+ this.setEnumCustomName(type, meta.modelName);
825
+ f.type = type;
826
+ }
727
827
  walk(type, hint) {
728
828
  const { core } = splitNullish(type);
729
829
  if (core.length === 0) return { kind: "dynamic" };
@@ -909,19 +1009,45 @@ var Extractor = class {
909
1009
  this.enumsFinalized = true;
910
1010
  const used = new Set(this.modelRegistry.keys());
911
1011
  const groups = [...this.enumGroups.values()];
912
- const schemaGroups = groups.filter((g) => g.schemaName).sort((a, b) => a.schemaName.localeCompare(b.schemaName));
913
- for (const g of schemaGroups) {
914
- const name = this.ensureUnique(g.schemaName, used);
915
- g.type.name = name;
916
- this.pushEnumIR(name, g.values);
1012
+ const namedOwners = /* @__PURE__ */ new Map();
1013
+ const assignNamed = (g, name) => {
1014
+ const owner = namedOwners.get(name);
1015
+ if (owner) {
1016
+ g.type.name = owner.type.name;
1017
+ return;
1018
+ }
1019
+ const finalName = used.has(name) ? this.ensureUnique(name, used) : name;
1020
+ used.add(finalName);
1021
+ g.type.name = finalName;
1022
+ namedOwners.set(name, g);
1023
+ this.pushEnumIR(finalName, g.values);
1024
+ };
1025
+ const customGroups = groups.filter((g) => g.customName).sort((a, b) => a.customName.localeCompare(b.customName));
1026
+ for (const g of customGroups) assignNamed(g, g.customName);
1027
+ const schemaGroups = groups.filter((g) => g.schemaName && !g.customName).sort((a, b) => a.schemaName.localeCompare(b.schemaName));
1028
+ for (const g of schemaGroups) assignNamed(g, g.schemaName);
1029
+ const namedByField = /* @__PURE__ */ new Map();
1030
+ for (const g of groups) {
1031
+ if (!g.customName && !g.schemaName || !g.field) continue;
1032
+ const arr = namedByField.get(g.field) ?? [];
1033
+ if (!arr.some((x) => x.type.name === g.type.name)) arr.push(g);
1034
+ namedByField.set(g.field, arr);
917
1035
  }
918
- const fallbackGroups = groups.filter((g) => !g.schemaName).sort((a, b) => fallbackBase(a).localeCompare(fallbackBase(b)));
1036
+ const sameValueSet = (a, b) => a.length === b.length && [...a].sort().join("\0") === [...b].sort().join("\0");
1037
+ const fallbackGroups = groups.filter((g) => !g.schemaName && !g.customName).sort((a, b) => fallbackBase(a).localeCompare(fallbackBase(b)));
919
1038
  const baseCount = /* @__PURE__ */ new Map();
920
1039
  for (const g of fallbackGroups) {
921
1040
  const base = fallbackBase(g);
922
1041
  baseCount.set(base, (baseCount.get(base) ?? 0) + 1);
923
1042
  }
924
1043
  for (const g of fallbackGroups) {
1044
+ if (g.field) {
1045
+ const matches = (namedByField.get(g.field) ?? []).filter((n) => sameValueSet(n.values, g.values));
1046
+ if (matches.length === 1) {
1047
+ g.type.name = matches[0].type.name;
1048
+ continue;
1049
+ }
1050
+ }
925
1051
  const base = fallbackBase(g);
926
1052
  let name;
927
1053
  if (baseCount.get(base) === 1 && !used.has(base)) name = base;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supalive/codegen",
3
- "version": "1.20.0",
3
+ "version": "1.20.2",
4
4
  "description": "Generate type-safe client code (Dart, and more) from a Supalive TypeScript router.",
5
5
  "author": "Rebaz Raouf",
6
6
  "license": "MIT",