@gleanql/codegen 0.1.14 → 0.1.15

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/index.mjs CHANGED
@@ -138,7 +138,7 @@ function generateTypes(schema, options = {}) {
138
138
  function renderTypeBlock(type, ctx) {
139
139
  switch (type.kind) {
140
140
  case "OBJECT": return renderObjectLike(type, ctx, JSON.stringify(type.name));
141
- case "INTERFACE": return renderObjectLike(type, ctx, typenameUnionOf(type));
141
+ case "INTERFACE": return renderInterface(type, ctx);
142
142
  case "UNION": return renderUnion(type);
143
143
  case "ENUM": return renderEnum(type);
144
144
  case "INPUT_OBJECT": return renderInputObject(type, ctx);
@@ -159,6 +159,21 @@ function renderUnion(type) {
159
159
  const body = members.length > 0 ? members.join(" | ") : "never";
160
160
  return `export type ${type.name} = ${body};`;
161
161
  }
162
+ /**
163
+ * An interface is narrowable: render it as the union of its possible types, so a
164
+ * `x.__typename === "Product"` guard refines the value to the concrete type and
165
+ * its fields (`x.title`) become accessible — the shape GraphQL selections on an
166
+ * interface root (`nodes(ids:) { ... on Product { title } }`) actually return.
167
+ * Each implementer already carries every interface field (introspection includes
168
+ * inherited fields on each object type), so common-field access (`x.id`) is
169
+ * preserved across the union. Falls back to a thin interface when no possible
170
+ * types surface (e.g. a schema exposing an interface no type implements).
171
+ */
172
+ function renderInterface(type, ctx) {
173
+ const members = (type.possibleTypes ?? []).map((p) => namedTypeName(p)).filter((name) => !isInternalType(name));
174
+ if (members.length === 0) return renderObjectLike(type, ctx, typenameUnionOf(type));
175
+ return `export type ${type.name} = ${members.join(" | ")};`;
176
+ }
162
177
  function renderEnum(type) {
163
178
  const values = (type.enumValues ?? []).map((v) => JSON.stringify(v.name));
164
179
  const body = values.length > 0 ? values.join(" | ") : "never";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gleanql/codegen",
3
- "version": "0.1.14",
3
+ "version": "0.1.15",
4
4
  "description": "Glean's schema codegen: introspection to schema model, branded types and graph accessors",
5
5
  "license": "MIT",
6
6
  "author": "Alexander Liljengard",
@@ -26,7 +26,7 @@
26
26
  "access": "public"
27
27
  },
28
28
  "dependencies": {
29
- "@gleanql/core": "0.1.14"
29
+ "@gleanql/core": "0.1.15"
30
30
  },
31
31
  "homepage": "https://gleanql.com",
32
32
  "bugs": "https://github.com/gleanql/gleanql/issues",
package/src/types.ts CHANGED
@@ -49,7 +49,7 @@ function renderTypeBlock(type: IntrospectionType, ctx: Ctx): string | undefined
49
49
  case "OBJECT":
50
50
  return renderObjectLike(type, ctx, JSON.stringify(type.name));
51
51
  case "INTERFACE":
52
- return renderObjectLike(type, ctx, typenameUnionOf(type));
52
+ return renderInterface(type, ctx);
53
53
  case "UNION":
54
54
  return renderUnion(type);
55
55
  case "ENUM":
@@ -82,6 +82,24 @@ function renderUnion(type: IntrospectionType): string {
82
82
  return `export type ${type.name} = ${body};`;
83
83
  }
84
84
 
85
+ /**
86
+ * An interface is narrowable: render it as the union of its possible types, so a
87
+ * `x.__typename === "Product"` guard refines the value to the concrete type and
88
+ * its fields (`x.title`) become accessible — the shape GraphQL selections on an
89
+ * interface root (`nodes(ids:) { ... on Product { title } }`) actually return.
90
+ * Each implementer already carries every interface field (introspection includes
91
+ * inherited fields on each object type), so common-field access (`x.id`) is
92
+ * preserved across the union. Falls back to a thin interface when no possible
93
+ * types surface (e.g. a schema exposing an interface no type implements).
94
+ */
95
+ function renderInterface(type: IntrospectionType, ctx: Ctx): string {
96
+ const members = (type.possibleTypes ?? [])
97
+ .map((p) => namedTypeName(p))
98
+ .filter((name) => !isInternalType(name));
99
+ if (members.length === 0) return renderObjectLike(type, ctx, typenameUnionOf(type));
100
+ return `export type ${type.name} = ${members.join(" | ")};`;
101
+ }
102
+
85
103
  function renderEnum(type: IntrospectionType): string {
86
104
  const values = (type.enumValues ?? []).map((v) => JSON.stringify(v.name));
87
105
  const body = values.length > 0 ? values.join(" | ") : "never";