@kurotako/ir 0.2.0 → 0.3.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,43 @@
1
1
  # @kurotako/ir
2
2
 
3
+ ## 0.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 9fe08b3: Add an `array` field type to the IR and bump the IR format to `'4'`.
8
+
9
+ - `@kurotako/ir`: new `{ kind: 'array', element: FieldType }` field type, so an
10
+ array-typed type alias, union variant or map value can be represented (the
11
+ boolean `Field.list` fast path stays as an optimisation for a plain
12
+ `property: { type: 'array', items: <scalar|ref> }`). Builders (`.array(...)`),
13
+ validation, reference traversal, cycle analysis and TypeScript-type helpers
14
+ recurse through it. The strict compatibility check now requires `irVersion` `'4'`.
15
+ - `@kurotako/gen-zod`: renders an array as `z.array(<element>)` and its TS type as
16
+ `<element>[]`.
17
+ - `@kurotako/gen-typescript`: renders an array as `<element>[]`, parenthesising a
18
+ union element.
19
+ - `@kurotako/gen-angular`: renders an array field as `FormControl<<element>[]>`
20
+ seeded with `[]`.
21
+ - `@kurotako/parser-openapi`: an array schema that is not a plain object property
22
+ (an array-typed response/component alias, a nested array, an array inside a
23
+ `oneOf`/`additionalProperties`) now maps to the `array` field type instead of
24
+ silently dropping the array wrapper. Nested arrays are supported.
25
+ - ecdb02c: Add the OpenAPI parser and IR typed maps.
26
+
27
+ - `@kurotako/ir`: adds the `{ kind: 'map', value: FieldType }`
28
+ field type and the optional `Entity.additionalProperties` slot. Builders,
29
+ validation, reference traversal, cycle analysis and TypeScript-type helpers
30
+ recurse through both.
31
+ - `@kurotako/gen-zod`: renders map values as `z.record(z.string(), value)` and an
32
+ entity catch-all as `.catchall(value)`.
33
+ - `@kurotako/gen-typescript`: renders map values as `Record<string, Value>` and an
34
+ entity catch-all as an index signature.
35
+ - `@kurotako/gen-angular`: renders a map field as `FormControl<Record<string, Value>>`
36
+ seeded with `{}`; entity-level catch-all values stay under the emitted Zod validator.
37
+ - `@kurotako/parser-openapi`: new package. Turns an OpenAPI 3.0/3.1 document (local
38
+ JSON/YAML or an unauthenticated HTTP(S) URL) into one `SourceIR`, mapping component
39
+ and inline operation schemas to deterministic entities, aliases, enums and typed maps.
40
+
3
41
  ## 0.2.0
4
42
 
5
43
  ### Minor Changes
package/dist/index.cjs CHANGED
@@ -144,6 +144,8 @@ var FieldTypeSchema = v.lazy(
144
144
  v.object({ kind: v.literal("enum"), ref: v.string() }),
145
145
  v.object({ kind: v.literal("unknown"), hint: v.optional(v.string()) }),
146
146
  v.object({ kind: v.literal("ref"), ref: v.string() }),
147
+ v.object({ kind: v.literal("map"), value: FieldTypeSchema }),
148
+ v.object({ kind: v.literal("array"), element: FieldTypeSchema }),
147
149
  v.object({
148
150
  kind: v.literal("union"),
149
151
  variants: v.array(FieldTypeSchema),
@@ -228,6 +230,7 @@ var CompositeUniqueSchema = v.object({
228
230
  var EntitySchema = v.object({
229
231
  name: v.string(),
230
232
  fields: v.array(FieldSchema),
233
+ additionalProperties: v.optional(FieldTypeSchema),
231
234
  relations: v.array(RelationSchema),
232
235
  enums: v.optional(v.record(v.string(), EnumDefSchema)),
233
236
  primaryKey: v.optional(v.array(v.string())),
@@ -253,7 +256,7 @@ var IrSchema = v.object({
253
256
  var v2 = __toESM(require("valibot"), 1);
254
257
 
255
258
  // src/version.ts
256
- var IR_VERSION = "2";
259
+ var IR_VERSION = "4";
257
260
  function isCompatible(irVersion) {
258
261
  return irVersion === IR_VERSION;
259
262
  }
@@ -351,6 +354,19 @@ function walkFieldType(type, path, entity, source, issues, info) {
351
354
  }
352
355
  return;
353
356
  }
357
+ case "map":
358
+ walkFieldType(type.value, `${path}.value`, entity, source, issues, info);
359
+ return;
360
+ case "array":
361
+ walkFieldType(
362
+ type.element,
363
+ `${path}.element`,
364
+ entity,
365
+ source,
366
+ issues,
367
+ info
368
+ );
369
+ return;
354
370
  case "union": {
355
371
  if (type.variants.length < 2) {
356
372
  pushIssue(
@@ -397,6 +413,10 @@ function collectRefs(type, out) {
397
413
  for (const variant2 of type.variants) {
398
414
  collectRefs(variant2, out);
399
415
  }
416
+ } else if (type.kind === "map") {
417
+ collectRefs(type.value, out);
418
+ } else if (type.kind === "array") {
419
+ collectRefs(type.element, out);
400
420
  }
401
421
  }
402
422
  function checkRefCycles(namespace, source, info) {
@@ -486,6 +506,16 @@ function checkSource(namespace, source, lookupEntity, isNamespacePresent, issues
486
506
  checkConstraints(issues, `${fPath}.constraints`, field.constraints);
487
507
  walkFieldType(field.type, fPath, entity, source, issues, info);
488
508
  }
509
+ if (entity.additionalProperties !== void 0) {
510
+ walkFieldType(
511
+ entity.additionalProperties,
512
+ `${ePath}.additionalProperties`,
513
+ entity,
514
+ source,
515
+ issues,
516
+ info
517
+ );
518
+ }
489
519
  for (const [localKey, def] of Object.entries(entity.enums ?? {})) {
490
520
  if (def.name !== localKey) {
491
521
  pushIssue(
@@ -736,6 +766,18 @@ var UnionBuilderImpl = class _UnionBuilderImpl {
736
766
  }
737
767
  return this;
738
768
  }
769
+ map(build) {
770
+ const value = new TypeAliasBuilderImpl(this.#path, "map-value");
771
+ build(value);
772
+ this.#variants.push({ kind: "map", value: value.build().type });
773
+ return this;
774
+ }
775
+ array(build) {
776
+ const element = new TypeAliasBuilderImpl(this.#path, "array-element");
777
+ build(element);
778
+ this.#variants.push({ kind: "array", element: element.build().type });
779
+ return this;
780
+ }
739
781
  unknown(hint) {
740
782
  this.#variants.push(
741
783
  hint === void 0 ? { kind: "unknown" } : { kind: "unknown", hint }
@@ -777,7 +819,7 @@ var UnionBuilderImpl = class _UnionBuilderImpl {
777
819
  return type;
778
820
  }
779
821
  };
780
- var TypeAliasBuilderImpl = class {
822
+ var TypeAliasBuilderImpl = class _TypeAliasBuilderImpl {
781
823
  #path;
782
824
  #name;
783
825
  #type = { kind: "unknown" };
@@ -804,6 +846,18 @@ var TypeAliasBuilderImpl = class {
804
846
  this.#type = nested.build();
805
847
  return this;
806
848
  }
849
+ map(build) {
850
+ const value = new _TypeAliasBuilderImpl(this.#path, "map-value");
851
+ build(value);
852
+ this.#type = { kind: "map", value: value.build().type };
853
+ return this;
854
+ }
855
+ array(build) {
856
+ const element = new _TypeAliasBuilderImpl(this.#path, "array-element");
857
+ build(element);
858
+ this.#type = { kind: "array", element: element.build().type };
859
+ return this;
860
+ }
807
861
  unknown(hint) {
808
862
  this.#type = hint === void 0 ? { kind: "unknown" } : { kind: "unknown", hint };
809
863
  return this;
@@ -857,6 +911,18 @@ var FieldBuilderImpl = class {
857
911
  this.#field.type = builder.build();
858
912
  return this;
859
913
  }
914
+ map(build) {
915
+ const value = new TypeAliasBuilderImpl(this.#path, "map-value");
916
+ build(value);
917
+ this.#field.type = { kind: "map", value: value.build().type };
918
+ return this;
919
+ }
920
+ array(build) {
921
+ const element = new TypeAliasBuilderImpl(this.#path, "array-element");
922
+ build(element);
923
+ this.#field.type = { kind: "array", element: element.build().type };
924
+ return this;
925
+ }
860
926
  unknown(hint) {
861
927
  this.#field.type = hint === void 0 ? { kind: "unknown" } : { kind: "unknown", hint };
862
928
  return this;
@@ -1000,6 +1066,7 @@ var EntityBuilderImpl = class {
1000
1066
  #uniques = [];
1001
1067
  #doc;
1002
1068
  #dbName;
1069
+ #additionalProperties;
1003
1070
  constructor(namespace, name) {
1004
1071
  this.#namespace = namespace;
1005
1072
  this.#name = name;
@@ -1026,6 +1093,15 @@ var EntityBuilderImpl = class {
1026
1093
  this.#fields.push(builder);
1027
1094
  return this;
1028
1095
  }
1096
+ additionalProperties(def) {
1097
+ const value = new TypeAliasBuilderImpl(
1098
+ `${this.#namespace}.${this.#name}.additionalProperties`,
1099
+ "additionalProperties"
1100
+ );
1101
+ def(value);
1102
+ this.#additionalProperties = value.build().type;
1103
+ return this;
1104
+ }
1029
1105
  relation(name, def) {
1030
1106
  const builder = new RelationBuilderImpl(name);
1031
1107
  def(builder);
@@ -1091,6 +1167,9 @@ var EntityBuilderImpl = class {
1091
1167
  if (this.#dbName !== void 0) {
1092
1168
  entity.dbName = this.#dbName;
1093
1169
  }
1170
+ if (this.#additionalProperties !== void 0) {
1171
+ entity.additionalProperties = this.#additionalProperties;
1172
+ }
1094
1173
  return entity;
1095
1174
  }
1096
1175
  };
@@ -1209,6 +1288,10 @@ function collectRefNames(type, into = /* @__PURE__ */ new Set()) {
1209
1288
  for (const variant2 of type.variants) {
1210
1289
  collectRefNames(variant2, into);
1211
1290
  }
1291
+ } else if (type.kind === "map") {
1292
+ collectRefNames(type.value, into);
1293
+ } else if (type.kind === "array") {
1294
+ collectRefNames(type.element, into);
1212
1295
  }
1213
1296
  return into;
1214
1297
  }
@@ -1230,6 +1313,9 @@ function refCycleMembers(source) {
1230
1313
  for (const field of entity.fields) {
1231
1314
  collectRefNames(field.type, set);
1232
1315
  }
1316
+ if (entity.additionalProperties !== void 0) {
1317
+ collectRefNames(entity.additionalProperties, set);
1318
+ }
1233
1319
  }
1234
1320
  const canReachSelf = (start) => {
1235
1321
  const seen = /* @__PURE__ */ new Set();
@@ -1270,6 +1356,10 @@ function fieldTypeKey(type) {
1270
1356
  return `ref:${type.ref}`;
1271
1357
  case "unknown":
1272
1358
  return `unknown:${type.hint ?? ""}`;
1359
+ case "map":
1360
+ return `map:${fieldTypeKey(type.value)}`;
1361
+ case "array":
1362
+ return `array:${fieldTypeKey(type.element)}`;
1273
1363
  case "union":
1274
1364
  return `union:${flattenUnion(type).map(fieldTypeKey).join(",")}`;
1275
1365
  }
@@ -1357,6 +1447,12 @@ function scalarTsType(type) {
1357
1447
  return flattenUnion(type).map(
1358
1448
  (variant2) => variant2.kind === "union" ? `(${scalarTsType(variant2)})` : scalarTsType(variant2)
1359
1449
  ).join(" | ");
1450
+ case "map":
1451
+ return `Record<string, ${scalarTsType(type.value)}>`;
1452
+ case "array": {
1453
+ const element = scalarTsType(type.element);
1454
+ return type.element.kind === "union" ? `(${element})[]` : `${element}[]`;
1455
+ }
1360
1456
  }
1361
1457
  }
1362
1458
  function mapScalar(scalar) {