@game-infra/valibot-to-csharp 0.1.0 → 0.2.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.
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Classifying a union: which field tags its members, what every member has in common, and the
3
+ * shapes the emitter knows how to render.
4
+ */
5
+ import type { ObjectField, ObjectNode, SchemaNode } from "./ir.js";
6
+ /**
7
+ * Decision: structural union shapes supported by the emitter.
8
+ *
9
+ * 'enum': every member is a literal / picklist string
10
+ * 'stringOrList': exactly string | string[]
11
+ * 'discriminated': every member is object({ type: literal('X'), ... })
12
+ * 'generic': fallback; emitted as JsonElement with a note
13
+ */
14
+ export type UnionShape = {
15
+ kind: "enum";
16
+ values: string[];
17
+ } | {
18
+ kind: "stringOrList";
19
+ } | {
20
+ kind: "discriminated";
21
+ discriminator: string;
22
+ members: ResolvedVariantMember[];
23
+ } | {
24
+ kind: "generic";
25
+ };
26
+ export interface ResolvedVariantMember {
27
+ /** Literal value of the discriminator for this member. */
28
+ tag: string;
29
+ /** The C# type name representing this variant. */
30
+ typeName: string;
31
+ /** The object schema powering this member. */
32
+ schema: ObjectNode;
33
+ /** Where it came from: the source const name, if it's a named schema. */
34
+ sourceName?: string;
35
+ }
36
+ /**
37
+ * The fields every member of a variant declares identically, discriminator excluded.
38
+ *
39
+ * "Identically" is by name, by nullability and by the whole schema shape: two members that both
40
+ * have a `payload` of different shapes share a name and nothing else, and hoisting that to the base
41
+ * would be a lie the compiler would then enforce.
42
+ */
43
+ export declare function sharedFields(members: readonly ResolvedVariantMember[], discriminator: string): ObjectField[];
44
+ /** A field that can carry a variant's discriminator: only a string literal can. */
45
+ export declare function isTagField(field: ObjectField): boolean;
46
+ /**
47
+ * The field a union tags its members by when no `variant(...)` declared one.
48
+ *
49
+ * Only a guess, and only for a bare `union([...])`: a `variant("op", ...)` states the
50
+ * discriminator outright and is taken at its word instead. A `schemaVersion: literal(1)`
51
+ * beside the tag is a constant field, not a second discriminator, so only string literals
52
+ * are eligible.
53
+ */
54
+ export declare function inferTagField(obj: ObjectNode): ObjectField | null;
55
+ /** How a member is named in a note, before it has resolved to a C# type. */
56
+ export declare function describeMember(node: SchemaNode): string;
57
+ /**
58
+ * Whether a named schema emits as a polymorphic base, as far as can be told without a module
59
+ * to resolve refs against. Used only to order the emit — bases before everything else, so a
60
+ * member is reserved by the time something references it — never for correctness.
61
+ */
62
+ export declare function isVariantLike(node: SchemaNode): boolean;
63
+ //# sourceMappingURL=variants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"variants.d.ts","sourceRoot":"","sources":["../src/variants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAEnE;;;;;;;GAOG;AACH,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,GACxB;IACE,IAAI,EAAE,eAAe,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,qBAAqB,EAAE,CAAC;CAClC,GACD;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC;AAExB,MAAM,WAAW,qBAAqB;IACpC,0DAA0D;IAC1D,GAAG,EAAE,MAAM,CAAC;IACZ,kDAAkD;IAClD,QAAQ,EAAE,MAAM,CAAC;IACjB,8CAA8C;IAC9C,MAAM,EAAE,UAAU,CAAC;IACnB,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,SAAS,qBAAqB,EAAE,EACzC,aAAa,EAAE,MAAM,GACpB,WAAW,EAAE,CAQf;AAWD,mFAAmF;AACnF,wBAAgB,UAAU,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAEtD;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,UAAU,GAAG,WAAW,GAAG,IAAI,CAMjE;AAED,4EAA4E;AAC5E,wBAAgB,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAEvD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAYvD"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Classifying a union: which field tags its members, what every member has in common, and the
3
+ * shapes the emitter knows how to render.
4
+ */
5
+ /**
6
+ * The fields every member of a variant declares identically, discriminator excluded.
7
+ *
8
+ * "Identically" is by name, by nullability and by the whole schema shape: two members that both
9
+ * have a `payload` of different shapes share a name and nothing else, and hoisting that to the base
10
+ * would be a lie the compiler would then enforce.
11
+ */
12
+ export function sharedFields(members, discriminator) {
13
+ const [first, ...rest] = members;
14
+ if (!first || members.length < 2)
15
+ return [];
16
+ return first.schema.fields.filter((candidate) => candidate.name !== discriminator &&
17
+ rest.every((member) => member.schema.fields.some((other) => sameField(candidate, other))));
18
+ }
19
+ function sameField(left, right) {
20
+ return (left.name === right.name &&
21
+ left.optional === right.optional &&
22
+ left.nullable === right.nullable &&
23
+ JSON.stringify(left.schema) === JSON.stringify(right.schema));
24
+ }
25
+ /** A field that can carry a variant's discriminator: only a string literal can. */
26
+ export function isTagField(field) {
27
+ return field.schema.kind === "literal" && typeof field.schema.value === "string";
28
+ }
29
+ /**
30
+ * The field a union tags its members by when no `variant(...)` declared one.
31
+ *
32
+ * Only a guess, and only for a bare `union([...])`: a `variant("op", ...)` states the
33
+ * discriminator outright and is taken at its word instead. A `schemaVersion: literal(1)`
34
+ * beside the tag is a constant field, not a second discriminator, so only string literals
35
+ * are eligible.
36
+ */
37
+ export function inferTagField(obj) {
38
+ return (obj.fields.find((field) => isTagField(field) && field.name === "type") ??
39
+ obj.fields.find(isTagField) ??
40
+ null);
41
+ }
42
+ /** How a member is named in a note, before it has resolved to a C# type. */
43
+ export function describeMember(node) {
44
+ return node.kind === "ref" ? `"${node.name}"` : "an inline member";
45
+ }
46
+ /**
47
+ * Whether a named schema emits as a polymorphic base, as far as can be told without a module
48
+ * to resolve refs against. Used only to order the emit — bases before everything else, so a
49
+ * member is reserved by the time something references it — never for correctness.
50
+ */
51
+ export function isVariantLike(node) {
52
+ if (node.kind === "variant")
53
+ return true;
54
+ if (node.kind === "union") {
55
+ // Heuristic: unions of all-object-literals with a shared discriminator.
56
+ // Re-classifying here would require the context; keep it simple and let
57
+ // the emitter decide. This ordering only affects when emitNamed runs for
58
+ // the base, not correctness.
59
+ return (node.members.length > 0 && node.members.every((m) => m.kind === "object" || m.kind === "ref"));
60
+ }
61
+ return false;
62
+ }
63
+ //# sourceMappingURL=variants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"variants.js","sourceRoot":"","sources":["../src/variants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAiCH;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAC1B,OAAyC,EACzC,aAAqB;IAErB,MAAM,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,GAAG,OAAO,CAAC;IACjC,IAAI,CAAC,KAAK,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IAC5C,OAAO,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAC/B,CAAC,SAAS,EAAE,EAAE,CACZ,SAAS,CAAC,IAAI,KAAK,aAAa;QAChC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,CAC5F,CAAC;AACJ,CAAC;AAED,SAAS,SAAS,CAAC,IAAiB,EAAE,KAAkB;IACtD,OAAO,CACL,IAAI,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI;QACxB,IAAI,CAAC,QAAQ,KAAK,KAAK,CAAC,QAAQ;QAChC,IAAI,CAAC,QAAQ,KAAK,KAAK,CAAC,QAAQ;QAChC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,CAC7D,CAAC;AACJ,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,UAAU,CAAC,KAAkB;IAC3C,OAAO,KAAK,CAAC,MAAM,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,KAAK,CAAC,MAAM,CAAC,KAAK,KAAK,QAAQ,CAAC;AACnF,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAAC,GAAe;IAC3C,OAAO,CACL,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,CAAC;QACtE,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC;QAC3B,IAAI,CACL,CAAC;AACJ,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,cAAc,CAAC,IAAgB;IAC7C,OAAO,IAAI,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,kBAAkB,CAAC;AACrE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,IAAgB;IAC5C,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACzC,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC1B,wEAAwE;QACxE,wEAAwE;QACxE,yEAAyE;QACzE,6BAA6B;QAC7B,OAAO,CACL,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,CAC9F,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@game-infra/valibot-to-csharp",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Generate idiomatic C# record types from TypeScript valibot schemas",
5
5
  "homepage": "https://github.com/kibertoad/game-infra/tree/main/packages/valibot-to-csharp#readme",
6
6
  "bugs": {
@@ -33,11 +33,11 @@
33
33
  "@typescript/typescript6": "^6.0.2"
34
34
  },
35
35
  "devDependencies": {
36
- "@types/node": "^26.1.2",
36
+ "@types/node": "^26.4.1",
37
37
  "rimraf": "^6.1.3",
38
38
  "typescript": "^7.0.2",
39
39
  "valibot": "^1.4.2",
40
- "vitest": "^4.1.10"
40
+ "vitest": "^4.1.11"
41
41
  },
42
42
  "scripts": {
43
43
  "build": "rimraf dist && tsc --project tsconfig.build.json",