@fabricorg/gen-graphql 1.1.0 → 3.0.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,57 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - c504d0a: Make generated artifacts carry their own weight instead of handing back a seam.
8
+
9
+ `gen-graphql` projects each view's JSON Schema into named GraphQL types rather than an opaque
10
+ `JSON` scalar, declares the Federation 2 `@link` directive, and emits `createResolvers()` — an
11
+ executable resolver map whose Query fields read through `ProjectionHost` and whose Mutation fields
12
+ submit through `submitAction()`. Shapes with no GraphQL projection (`oneOf`, `anyOf`, `allOf`, tuple
13
+ `items`, multi-type unions, non-local `$ref`) now fail the generator instead of silently degrading
14
+ to `JSON`; only a schema that genuinely admits any value projects as `JSON`. Recursive `#/$defs`
15
+ references resolve to a single named type.
16
+
17
+ `gen-conformance` emits a runnable suite alongside the plan. The ten Phase 1 checks split into eight
18
+ `MODULE_CONFORMANCE_CHECKS` a module verifies against a governed host and two
19
+ `PLATFORM_CONFORMANCE_CHECKS` a repository owns. A check a module cannot exercise reports
20
+ `not-applicable` with a reason rather than passing silently, and `@fabricorg/gen-conformance/memory`
21
+ ships a reference harness so a suite runs with no infrastructure.
22
+
23
+ `gen-capability` emits a usage contract with its own `formatVersion`, validated by
24
+ `assertUsageContract` and described by `USAGE_CONTRACT_JSON_SCHEMA`.
25
+
26
+ ### Patch Changes
27
+
28
+ - Updated dependencies [c504d0a]
29
+ - Updated dependencies [c504d0a]
30
+ - @fabricorg/platform-host@5.0.0
31
+ - @fabricorg/compiler@2.1.0
32
+
33
+ ## 2.0.0
34
+
35
+ ### Minor Changes
36
+
37
+ - Make the capability runtime vertical-neutral and instance-scoped. Move experience and display
38
+ semantics into a namespaced companion package, require explicit host registries, replace privacy
39
+ field defaults with caller-owned policy, add PostgreSQL projection adapters, and generate executable
40
+ ProjectionHost read and PlatformHost mutation seams.
41
+
42
+ ### Patch Changes
43
+
44
+ - Updated dependencies
45
+ - @fabricorg/platform@1.0.0
46
+ - @fabricorg/platform-host@4.0.0
47
+ - @fabricorg/projection-host@1.0.0
48
+ - @fabricorg/compiler@2.0.0
49
+
50
+ ## 1.2.0
51
+
52
+ - Generate executable mutation helpers that call `PlatformHost.submitAction()` with trusted resolver context.
53
+ - Keep generated queries routed through `ProjectionHost.project()`.
54
+
3
55
  ## 1.1.0
4
56
 
5
57
  - Generate version-pinned view identities and a `ProjectionHost`-backed query resolver helper.
package/dist/index.cjs CHANGED
@@ -24,43 +24,183 @@ __export(index_exports, {
24
24
  });
25
25
  module.exports = __toCommonJS(index_exports);
26
26
  var pascal = (value) => value.split(/[-._/]/).filter(Boolean).map((part) => part[0]?.toUpperCase() + part.slice(1)).join("");
27
- var gql = (schema) => {
28
- const type = Array.isArray(schema.type) ? schema.type[0] : schema.type;
29
- return type === "integer" ? "Int" : type === "number" ? "Float" : type === "boolean" ? "Boolean" : type === "string" ? "String" : "JSON";
30
- };
31
27
  var identifier = (value) => {
32
28
  const normalized = value.replace(/[^_0-9A-Za-z]/g, "_");
33
29
  return /^[_A-Za-z]/.test(normalized) ? normalized : `field_${normalized}`;
34
30
  };
31
+ var SCALARS = { string: "String", integer: "Int", number: "Float", boolean: "Boolean" };
32
+ function unsupported(projection, path, reason) {
33
+ throw new Error(`${projection.subject}${path} ${reason}. Give the view or object an explicit GraphQL-projectable shape.`);
34
+ }
35
+ function reserve(projection, preferred) {
36
+ let name = preferred;
37
+ for (let suffix = 2; projection.types.has(name); suffix += 1) name = `${preferred}${suffix}`;
38
+ projection.types.set(name, "");
39
+ return name;
40
+ }
41
+ function resolveRef(projection, ref, path) {
42
+ const match = /^#\/\$defs\/([^/]+)$/.exec(ref);
43
+ if (!match) unsupported(projection, path, `uses $ref "${ref}", which is not a local "#/$defs/<name>" pointer`);
44
+ const key = decodeURIComponent(match[1].replace(/~1/g, "/").replace(/~0/g, "~"));
45
+ const target = projection.root.$defs?.[key];
46
+ if (!target) unsupported(projection, path, `references missing definition "#/$defs/${key}"`);
47
+ return { schema: target, key };
48
+ }
49
+ function isObjectShape(schema) {
50
+ return schema.$ref === void 0 && (schema.type === void 0 || schema.type === "object") && !Array.isArray(schema.enum) && Object.keys(schema.properties ?? {}).length > 0;
51
+ }
52
+ function admitsNull(schema, projection, seen = /* @__PURE__ */ new Set()) {
53
+ if (schema.$ref !== void 0) {
54
+ const match = /^#\/\$defs\/([^/]+)$/.exec(schema.$ref);
55
+ if (!match) return false;
56
+ const key = decodeURIComponent(match[1].replace(/~1/g, "/").replace(/~0/g, "~"));
57
+ if (seen.has(key)) return false;
58
+ seen.add(key);
59
+ const target = projection.root.$defs?.[key];
60
+ return target ? admitsNull(target, projection, seen) : false;
61
+ }
62
+ if (Array.isArray(schema.type)) return schema.type.includes("null");
63
+ if (schema.type === "null") return true;
64
+ return Array.isArray(schema.enum) ? schema.enum.includes(null) : false;
65
+ }
66
+ function enumType(projection, schema, preferred) {
67
+ const values = schema.enum;
68
+ if (!Array.isArray(values) || values.length === 0) return void 0;
69
+ const members = values.filter((value) => value !== null);
70
+ if (members.length === 0) return void 0;
71
+ if (!members.every((value) => typeof value === "string" && /^[_A-Za-z][_0-9A-Za-z]*$/.test(value))) return void 0;
72
+ const unique = [...new Set(members)];
73
+ const name = reserve(projection, preferred);
74
+ projection.types.set(name, `enum ${name} {
75
+ ${unique.map((member) => ` ${member}`).join("\n")}
76
+ }`);
77
+ return name;
78
+ }
79
+ function project(schema, preferred, projection, path) {
80
+ for (const keyword of ["oneOf", "anyOf", "allOf", "not", "if"]) {
81
+ if (schema[keyword] !== void 0) unsupported(projection, path, `uses "${keyword}", which has no GraphQL projection`);
82
+ }
83
+ if (schema.$ref !== void 0) {
84
+ const { schema: target, key } = resolveRef(projection, schema.$ref, path);
85
+ const claimed = projection.refs.get(key);
86
+ if (claimed) return claimed;
87
+ const preferredRefName = `${projection.rootName}${pascal(key)}`;
88
+ if (isObjectShape(target)) {
89
+ const name = reserve(projection, preferredRefName);
90
+ projection.refs.set(key, name);
91
+ projection.types.set(name, objectSdl(name, Object.entries(target.properties ?? {}), target, projection, `${path} -> $defs.${key}`));
92
+ return name;
93
+ }
94
+ const projected = project(target, preferredRefName, projection, `${path} -> $defs.${key}`);
95
+ projection.refs.set(key, projected);
96
+ return projected;
97
+ }
98
+ const declared = Array.isArray(schema.type) ? schema.type.filter((entry) => entry !== "null") : schema.type === void 0 ? [] : [schema.type];
99
+ if (Array.isArray(schema.type) && declared.length > 1) {
100
+ unsupported(projection, path, `declares the union type [${schema.type.join(", ")}], which has no GraphQL projection`);
101
+ }
102
+ if (Array.isArray(schema.type) && declared.length === 0) {
103
+ unsupported(projection, path, `declares only the null type, which has no GraphQL projection`);
104
+ }
105
+ const type = declared[0];
106
+ if (type === "null") unsupported(projection, path, `declares only the null type, which has no GraphQL projection`);
107
+ if (type === void 0 || type === "object") {
108
+ const named2 = enumType(projection, schema, preferred);
109
+ if (named2) return named2;
110
+ const properties = Object.entries(schema.properties ?? {});
111
+ if (properties.length === 0) {
112
+ for (const keyword of ["patternProperties", "propertyNames"]) {
113
+ if (schema[keyword] !== void 0) unsupported(projection, path, `uses "${keyword}" with no declared properties, which has no GraphQL projection`);
114
+ }
115
+ if (schema.additionalProperties !== void 0 && typeof schema.additionalProperties !== "boolean") {
116
+ unsupported(projection, path, `constrains "additionalProperties" with no declared properties, which has no GraphQL projection`);
117
+ }
118
+ return "JSON";
119
+ }
120
+ if (schema.patternProperties !== void 0) unsupported(projection, path, `uses "patternProperties" alongside "properties", which has no GraphQL projection`);
121
+ const name = reserve(projection, preferred);
122
+ projection.types.set(name, objectSdl(name, properties, schema, projection, path));
123
+ return name;
124
+ }
125
+ if (type === "array") {
126
+ if (schema.prefixItems !== void 0) unsupported(projection, path, `uses "prefixItems", which has no GraphQL projection`);
127
+ if (Array.isArray(schema.items)) unsupported(projection, path, `uses tuple "items", which has no GraphQL projection`);
128
+ if (schema.items === void 0) unsupported(projection, path, `declares an array without "items", so its element type has no GraphQL projection`);
129
+ const item = schema.items;
130
+ const reference = project(item, `${preferred}Item`, projection, `${path}[]`);
131
+ return `[${reference}${admitsNull(item, projection) ? "" : "!"}]`;
132
+ }
133
+ const named = enumType(projection, schema, preferred);
134
+ if (named) return named;
135
+ const scalar = SCALARS[type];
136
+ if (!scalar) unsupported(projection, path, `declares unsupported type "${type}"`);
137
+ return scalar;
138
+ }
139
+ function objectSdl(name, properties, schema, projection, path) {
140
+ const required = new Set(schema.required ?? []);
141
+ const fieldNames = properties.map(([property]) => identifier(property));
142
+ if (new Set(fieldNames).size !== fieldNames.length) {
143
+ throw new Error(`${projection.subject}${path} produces colliding GraphQL field names.`);
144
+ }
145
+ const fields = properties.map(([property, child], index) => {
146
+ const reference = project(child, `${name}${pascal(property)}`, projection, `${path}.${property}`);
147
+ const nonNull = required.has(property) && !admitsNull(child, projection);
148
+ return ` ${fieldNames[index]}: ${reference}${nonNull ? "!" : ""}`;
149
+ });
150
+ return `type ${name} {
151
+ ${fields.join("\n")}
152
+ }`;
153
+ }
35
154
  var graphqlGenerator = {
36
155
  name: "@fabricorg/gen-graphql",
37
- version: "1.1.0",
156
+ version: "3.0.0",
38
157
  generate(ir) {
39
158
  return ir.modules.flatMap((module2) => {
40
- const types = module2.objectTypes.map((objectType) => {
41
- const required = new Set(objectType.schema?.required ?? []);
42
- const properties = Object.entries(objectType.schema?.properties ?? {}).filter(([name]) => !["id", "tenantId", "spaceId"].includes(name));
159
+ const namespaceType = pascal(module2.namespace);
160
+ const types = /* @__PURE__ */ new Map();
161
+ const projectionFor = (root, rootName, subject) => ({ types, refs: /* @__PURE__ */ new Map(), root, rootName, subject });
162
+ for (const objectType of module2.objectTypes) {
163
+ const subject = `Module "${module2.namespace}" object "${objectType.type}"`;
164
+ const schema = objectType.schema ?? {};
165
+ const required = new Set(schema.required ?? []);
166
+ const properties = Object.entries(schema.properties ?? {}).filter(([name]) => !["id", "tenantId", "spaceId"].includes(name));
43
167
  const fieldNames = properties.map(([name]) => identifier(name));
44
168
  if (new Set(fieldNames).size !== fieldNames.length) throw new Error(`Object "${module2.namespace}.${objectType.type}" produces colliding GraphQL field names.`);
45
- const fields = properties.map(
46
- ([name, schema]) => ` ${identifier(name)}: ${gql(schema)}${required.has(name) ? "!" : ""}`
47
- );
48
- return [`type ${pascal(module2.namespace)}${objectType.type} @key(fields: "id") {`, " id: ID!", " tenantId: ID!", " spaceId: ID!", ...fields, "}"].join("\n");
49
- });
169
+ const entityName = `${namespaceType}${objectType.type}`;
170
+ if (types.has(entityName)) throw new Error(`Module "${module2.namespace}" produces two GraphQL types named "${entityName}".`);
171
+ types.set(entityName, "");
172
+ const projection = projectionFor(schema, entityName, subject);
173
+ for (const keyword of ["oneOf", "anyOf", "allOf", "not", "if"]) {
174
+ if (schema[keyword] !== void 0) unsupported(projection, "", `uses "${keyword}", which has no GraphQL projection`);
175
+ }
176
+ const fields = properties.map(([name, child], index) => {
177
+ const reference = project(child, `${entityName}${pascal(name)}`, projection, `.${name}`);
178
+ const nonNull = required.has(name) && !admitsNull(child, projection);
179
+ return ` ${fieldNames[index]}: ${reference}${nonNull ? "!" : ""}`;
180
+ });
181
+ types.set(entityName, [`type ${entityName} @key(fields: "id") {`, " id: ID!", " tenantId: ID!", " spaceId: ID!", ...fields, "}"].join("\n"));
182
+ }
50
183
  const fieldName = (value) => {
51
184
  const name = pascal(value);
52
185
  return `${name[0]?.toLowerCase() ?? ""}${name.slice(1)}`;
53
186
  };
54
- const queryFields = module2.views.map((view) => ` ${fieldName(view.name)}: JSON!`);
187
+ const viewPayloads = module2.views.map((view) => {
188
+ const rootName = `${pascal(view.name)}V${view.version.replace(/[^0-9A-Za-z]/g, "_")}`;
189
+ const projection = projectionFor(view.shape, rootName, `Module "${module2.namespace}" view "${view.name}@${view.version}" shape`);
190
+ const reference = project(view.shape, rootName, projection, "");
191
+ return { field: fieldName(view.name), reference, nonNull: !admitsNull(view.shape, projection) };
192
+ });
193
+ const queryFields = viewPayloads.map((view) => ` ${view.field}: ${view.reference}${view.nonNull ? "!" : ""}`);
55
194
  const mutations = module2.actions.map((action) => ` ${fieldName(action.actionId)}(parameters: JSON!): GovernedActionSubmission!`);
56
- const operationNames = [...module2.views.map((view) => fieldName(view.name)), ...module2.actions.map((action) => fieldName(action.actionId))];
195
+ const operationNames = [...viewPayloads.map((view) => view.field), ...module2.actions.map((action) => fieldName(action.actionId))];
57
196
  if (new Set(operationNames).size !== operationNames.length) throw new Error(`Module "${module2.namespace}" produces colliding GraphQL operation names.`);
58
197
  const content = [
59
198
  `# Generated by ${graphqlGenerator.name}@${graphqlGenerator.version}`,
60
199
  `# Manifest digest: ${ir.manifestDigest}`,
200
+ 'extend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@key"])',
61
201
  "scalar JSON",
62
202
  "type GovernedActionSubmission { actionInvocationId: ID!, status: String! }",
63
- ...types,
203
+ ...[...types.values()].filter(Boolean),
64
204
  `type Query {
65
205
  ${queryFields.length ? queryFields.join("\n") : " _fabric: Boolean!"}
66
206
  }`,
@@ -69,18 +209,48 @@ ${mutations.length ? mutations.join("\n") : " _fabric: Boolean!"}
69
209
  }`,
70
210
  ""
71
211
  ].join("\n\n");
212
+ const actionIds = Object.fromEntries(module2.actions.map((action) => [fieldName(action.actionId), action.actionId]));
213
+ const viewIds = Object.fromEntries(module2.views.map((view) => [fieldName(view.name), { name: view.name, version: view.version }]));
72
214
  const resolvers = [
73
215
  'import type { ProjectionHost, ProjectionQuery } from "@fabricorg/projection-host";',
216
+ 'import type { GovernedActionHost, SubmitActionInput, SubmitActionResult } from "@fabricorg/platform-host";',
74
217
  "",
75
218
  "// Generated resolver seam. Authenticate first; derive actor and tenant server-side.",
76
- `export const actionIds = ${JSON.stringify(Object.fromEntries(module2.actions.map((action) => [fieldName(action.actionId), action.actionId])), null, 2)} as const;`,
77
- `export const viewIds = ${JSON.stringify(Object.fromEntries(module2.views.map((view) => [fieldName(view.name), { name: view.name, version: view.version }])), null, 2)} as const;`,
219
+ `export const actionIds = ${JSON.stringify(actionIds, null, 2)} as const;`,
220
+ `export const viewIds = ${JSON.stringify(viewIds, null, 2)} as const;`,
221
+ "",
78
222
  'export async function resolveProjection(host: Pick<ProjectionHost, "project">, field: keyof typeof viewIds, context: Omit<ProjectionQuery, "view">): Promise<unknown> {',
79
223
  " const result = await host.project({ ...context, view: viewIds[field] });",
80
224
  " return result.state;",
81
225
  "}",
82
- "// Query resolvers call resolveProjection(); never read a projection store directly.",
83
- "// Each mutation resolver must call createGovernedActionHost().submitAction(); never call handlers directly.",
226
+ 'export type GovernedMutationContext = Omit<SubmitActionInput, "actionId" | "parameters">;',
227
+ 'export async function submitMutation(host: Pick<GovernedActionHost, "submitAction">, field: keyof typeof actionIds, parameters: Record<string, unknown>, context: GovernedMutationContext): Promise<SubmitActionResult> {',
228
+ " return await host.submitAction({ ...context, actionId: actionIds[field], parameters });",
229
+ "}",
230
+ "",
231
+ "export interface ResolverWiring<TContext> {",
232
+ ' projectionHost: Pick<ProjectionHost, "project">;',
233
+ ' actionHost: Pick<GovernedActionHost, "submitAction">;',
234
+ " /** Derive tenant, space and actor from the authenticated request. Never from client input. */",
235
+ ' projectionContext: (context: TContext) => Omit<ProjectionQuery, "view"> | Promise<Omit<ProjectionQuery, "view">>;',
236
+ " mutationContext: (context: TContext) => GovernedMutationContext | Promise<GovernedMutationContext>;",
237
+ "}",
238
+ "",
239
+ "/** Executable resolver map. Query fields read through ProjectionHost; Mutation fields submit through the governed Host. */",
240
+ "export function createResolvers<TContext>(wiring: ResolverWiring<TContext>) {",
241
+ " return {",
242
+ " Query: {",
243
+ ...Object.keys(viewIds).map(
244
+ (field) => ` ${JSON.stringify(field)}: async (_parent: unknown, _args: unknown, context: TContext) => resolveProjection(wiring.projectionHost, ${JSON.stringify(field)}, await wiring.projectionContext(context)),`
245
+ ),
246
+ " },",
247
+ " Mutation: {",
248
+ ...Object.keys(actionIds).map(
249
+ (field) => ` ${JSON.stringify(field)}: async (_parent: unknown, args: { parameters: Record<string, unknown> }, context: TContext) => submitMutation(wiring.actionHost, ${JSON.stringify(field)}, args.parameters, await wiring.mutationContext(context)),`
250
+ ),
251
+ " },",
252
+ " };",
253
+ "}",
84
254
  ""
85
255
  ].join("\n");
86
256
  return [{ path: `${module2.namespace}/schema.graphql`, content }, { path: `${module2.namespace}/resolver-contract.ts`, content: resolvers }];
@@ -1 +1 @@
1
- {"version":3,"sources":["../index.ts"],"sourcesContent":["import type { GeneratorTarget } from \"@fabricorg/compiler\";\nimport type { PortableJsonSchema } from \"@fabricorg/platform\";\n\nconst pascal = (value: string): string =>\n\tvalue.split(/[-._/]/).filter(Boolean).map((part) => part[0]?.toUpperCase() + part.slice(1)).join(\"\");\nconst gql = (schema: PortableJsonSchema): string => {\n\tconst type = Array.isArray(schema.type) ? schema.type[0] : schema.type;\n\treturn type === \"integer\" ? \"Int\" : type === \"number\" ? \"Float\" : type === \"boolean\" ? \"Boolean\" : type === \"string\" ? \"String\" : \"JSON\";\n};\nconst identifier = (value: string): string => {\n\tconst normalized = value.replace(/[^_0-9A-Za-z]/g, \"_\");\n\treturn /^[_A-Za-z]/.test(normalized) ? normalized : `field_${normalized}`;\n};\n\nexport const graphqlGenerator: GeneratorTarget = {\n\tname: \"@fabricorg/gen-graphql\",\n\tversion: \"1.1.0\",\n\tgenerate(ir) {\n\t\treturn ir.modules.flatMap((module) => {\n\t\t\tconst types = module.objectTypes.map((objectType) => {\n\t\t\t\tconst required = new Set(objectType.schema?.required ?? []);\n\t\t\t\tconst properties = Object.entries(objectType.schema?.properties ?? {}).filter(([name]) => ![\"id\", \"tenantId\", \"spaceId\"].includes(name));\n\t\t\t\tconst fieldNames = properties.map(([name]) => identifier(name));\n\t\t\t\tif (new Set(fieldNames).size !== fieldNames.length) throw new Error(`Object \"${module.namespace}.${objectType.type}\" produces colliding GraphQL field names.`);\n\t\t\t\tconst fields = properties.map(\n\t\t\t\t\t([name, schema]) => ` ${identifier(name)}: ${gql(schema)}${required.has(name) ? \"!\" : \"\"}`,\n\t\t\t\t);\n\t\t\t\treturn [`type ${pascal(module.namespace)}${objectType.type} @key(fields: \"id\") {`, \" id: ID!\", \" tenantId: ID!\", \" spaceId: ID!\", ...fields, \"}\"].join(\"\\n\");\n\t\t\t});\n\t\t\tconst fieldName = (value: string): string => {\n\t\t\t\tconst name = pascal(value);\n\t\t\t\treturn `${name[0]?.toLowerCase() ?? \"\"}${name.slice(1)}`;\n\t\t\t};\n\t\t\tconst queryFields = module.views.map((view) => ` ${fieldName(view.name)}: JSON!`);\n\t\t\tconst mutations = module.actions.map((action) => ` ${fieldName(action.actionId)}(parameters: JSON!): GovernedActionSubmission!`);\n\t\t\tconst operationNames = [...module.views.map((view) => fieldName(view.name)), ...module.actions.map((action) => fieldName(action.actionId))];\n\t\t\tif (new Set(operationNames).size !== operationNames.length) throw new Error(`Module \"${module.namespace}\" produces colliding GraphQL operation names.`);\n\t\t\tconst content = [\n\t\t\t\t`# Generated by ${graphqlGenerator.name}@${graphqlGenerator.version}`,\n\t\t\t\t`# Manifest digest: ${ir.manifestDigest}`,\n\t\t\t\t\"scalar JSON\",\n\t\t\t\t\"type GovernedActionSubmission { actionInvocationId: ID!, status: String! }\",\n\t\t\t\t...types,\n\t\t\t\t`type Query {\\n${queryFields.length ? queryFields.join(\"\\n\") : \" _fabric: Boolean!\"}\\n}`,\n\t\t\t\t`type Mutation {\\n${mutations.length ? mutations.join(\"\\n\") : \" _fabric: Boolean!\"}\\n}`,\n\t\t\t\t\"\",\n\t\t\t].join(\"\\n\\n\");\n\t\t\tconst resolvers = [\n\t\t\t\t'import type { ProjectionHost, ProjectionQuery } from \"@fabricorg/projection-host\";',\n\t\t\t\t\"\",\n\t\t\t\t\"// Generated resolver seam. Authenticate first; derive actor and tenant server-side.\",\n\t\t\t\t`export const actionIds = ${JSON.stringify(Object.fromEntries(module.actions.map((action) => [fieldName(action.actionId), action.actionId])), null, 2)} as const;`,\n\t\t\t\t`export const viewIds = ${JSON.stringify(Object.fromEntries(module.views.map((view) => [fieldName(view.name), { name: view.name, version: view.version }])), null, 2)} as const;`,\n\t\t\t\t\"export async function resolveProjection(host: Pick<ProjectionHost, \\\"project\\\">, field: keyof typeof viewIds, context: Omit<ProjectionQuery, \\\"view\\\">): Promise<unknown> {\",\n\t\t\t\t\" const result = await host.project({ ...context, view: viewIds[field] });\",\n\t\t\t\t\" return result.state;\",\n\t\t\t\t\"}\",\n\t\t\t\t\"// Query resolvers call resolveProjection(); never read a projection store directly.\",\n\t\t\t\t\"// Each mutation resolver must call createGovernedActionHost().submitAction(); never call handlers directly.\",\n\t\t\t\t\"\",\n\t\t\t].join(\"\\n\");\n\t\t\treturn [{ path: `${module.namespace}/schema.graphql`, content }, { path: `${module.namespace}/resolver-contract.ts`, content: resolvers }];\n\t\t});\n\t},\n};\n"],"mappings":"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","names":["module"]}
1
+ {"version":3,"sources":["../index.ts"],"sourcesContent":["import type { GeneratorTarget } from \"@fabricorg/compiler\";\nimport type { PortableJsonSchema } from \"@fabricorg/platform\";\n\nconst pascal = (value: string): string =>\n\tvalue.split(/[-._/]/).filter(Boolean).map((part) => part[0]?.toUpperCase() + part.slice(1)).join(\"\");\n\nconst identifier = (value: string): string => {\n\tconst normalized = value.replace(/[^_0-9A-Za-z]/g, \"_\");\n\treturn /^[_A-Za-z]/.test(normalized) ? normalized : `field_${normalized}`;\n};\n\nconst SCALARS: Record<string, string> = { string: \"String\", integer: \"Int\", number: \"Float\", boolean: \"Boolean\" };\n\n/**\n * Named SDL blocks discovered while projecting one document, plus the `$defs`\n * names already claimed so a recursive `$ref` resolves to the type being built\n * rather than recursing forever.\n */\ninterface Projection {\n\t/** Insertion-ordered so emitted SDL is deterministic for a given manifest. Empty value means reserved-but-unfilled. */\n\ttypes: Map<string, string>;\n\trefs: Map<string, string>;\n\t/** The whole document, so a `$ref` in any property resolves against its `$defs`. */\n\troot: PortableJsonSchema;\n\trootName: string;\n\tsubject: string;\n}\n\nfunction unsupported(projection: Projection, path: string, reason: string): never {\n\tthrow new Error(`${projection.subject}${path} ${reason}. Give the view or object an explicit GraphQL-projectable shape.`);\n}\n\n/** Take a unique type name before projecting children, so a recursive `$ref` back to this type resolves to the same name. */\nfunction reserve(projection: Projection, preferred: string): string {\n\tlet name = preferred;\n\tfor (let suffix = 2; projection.types.has(name); suffix += 1) name = `${preferred}${suffix}`;\n\tprojection.types.set(name, \"\");\n\treturn name;\n}\n\n/** Resolve `#/$defs/<key>` only; any other pointer is not portable across generators. */\nfunction resolveRef(projection: Projection, ref: string, path: string): { schema: PortableJsonSchema; key: string } {\n\tconst match = /^#\\/\\$defs\\/([^/]+)$/.exec(ref);\n\tif (!match) unsupported(projection, path, `uses $ref \"${ref}\", which is not a local \"#/$defs/<name>\" pointer`);\n\tconst key = decodeURIComponent(match[1]!.replace(/~1/g, \"/\").replace(/~0/g, \"~\"));\n\tconst target = projection.root.$defs?.[key];\n\tif (!target) unsupported(projection, path, `references missing definition \"#/$defs/${key}\"`);\n\treturn { schema: target, key };\n}\n\n/** A shape that becomes a named GraphQL object type rather than a scalar, list or JSON. */\nfunction isObjectShape(schema: PortableJsonSchema): boolean {\n\treturn schema.$ref === undefined\n\t\t&& (schema.type === undefined || schema.type === \"object\")\n\t\t&& !Array.isArray(schema.enum)\n\t\t&& Object.keys(schema.properties ?? {}).length > 0;\n}\n\n/**\n * Whether a value may legitimately be null, following local refs. GraphQL\n * non-null is a promise to the client, so a required field whose definition\n * admits null must still be nullable.\n */\nfunction admitsNull(schema: PortableJsonSchema, projection: Projection, seen: Set<string> = new Set()): boolean {\n\tif (schema.$ref !== undefined) {\n\t\tconst match = /^#\\/\\$defs\\/([^/]+)$/.exec(schema.$ref);\n\t\tif (!match) return false;\n\t\tconst key = decodeURIComponent(match[1]!.replace(/~1/g, \"/\").replace(/~0/g, \"~\"));\n\t\tif (seen.has(key)) return false;\n\t\tseen.add(key);\n\t\tconst target = projection.root.$defs?.[key];\n\t\treturn target ? admitsNull(target, projection, seen) : false;\n\t}\n\tif (Array.isArray(schema.type)) return schema.type.includes(\"null\");\n\tif (schema.type === \"null\") return true;\n\treturn Array.isArray(schema.enum) ? schema.enum.includes(null) : false;\n}\n\nfunction enumType(projection: Projection, schema: PortableJsonSchema, preferred: string): string | undefined {\n\tconst values = schema.enum;\n\tif (!Array.isArray(values) || values.length === 0) return undefined;\n\tconst members = values.filter((value) => value !== null);\n\tif (members.length === 0) return undefined;\n\tif (!members.every((value) => typeof value === \"string\" && /^[_A-Za-z][_0-9A-Za-z]*$/.test(value))) return undefined;\n\tconst unique = [...new Set(members as string[])];\n\tconst name = reserve(projection, preferred);\n\tprojection.types.set(name, `enum ${name} {\\n${unique.map((member) => ` ${member}`).join(\"\\n\")}\\n}`);\n\treturn name;\n}\n\n/** Returns a GraphQL type reference without nullability; callers append `!`. */\nfunction project(schema: PortableJsonSchema, preferred: string, projection: Projection, path: string): string {\n\tfor (const keyword of [\"oneOf\", \"anyOf\", \"allOf\", \"not\", \"if\"] as const) {\n\t\tif (schema[keyword] !== undefined) unsupported(projection, path, `uses \"${keyword}\", which has no GraphQL projection`);\n\t}\n\n\tif (schema.$ref !== undefined) {\n\t\tconst { schema: target, key } = resolveRef(projection, schema.$ref, path);\n\t\tconst claimed = projection.refs.get(key);\n\t\tif (claimed) return claimed;\n\t\tconst preferredRefName = `${projection.rootName}${pascal(key)}`;\n\t\tif (isObjectShape(target)) {\n\t\t\t// Reserve before projecting the definition's own fields, so a definition that\n\t\t\t// refers back to itself resolves to this very name and nothing is ever unbound.\n\t\t\tconst name = reserve(projection, preferredRefName);\n\t\t\tprojection.refs.set(key, name);\n\t\t\tprojection.types.set(name, objectSdl(name, Object.entries(target.properties ?? {}), target, projection, `${path} -> $defs.${key}`));\n\t\t\treturn name;\n\t\t}\n\t\tconst projected = project(target, preferredRefName, projection, `${path} -> $defs.${key}`);\n\t\tprojection.refs.set(key, projected);\n\t\treturn projected;\n\t}\n\n\tconst declared = Array.isArray(schema.type) ? schema.type.filter((entry) => entry !== \"null\") : schema.type === undefined ? [] : [schema.type];\n\tif (Array.isArray(schema.type) && declared.length > 1) {\n\t\tunsupported(projection, path, `declares the union type [${schema.type.join(\", \")}], which has no GraphQL projection`);\n\t}\n\tif (Array.isArray(schema.type) && declared.length === 0) {\n\t\tunsupported(projection, path, `declares only the null type, which has no GraphQL projection`);\n\t}\n\tconst type = declared[0];\n\tif (type === \"null\") unsupported(projection, path, `declares only the null type, which has no GraphQL projection`);\n\n\tif (type === undefined || type === \"object\") {\n\t\tconst named = enumType(projection, schema, preferred);\n\t\tif (named) return named;\n\t\tconst properties = Object.entries(schema.properties ?? {});\n\t\tif (properties.length === 0) {\n\t\t\t// A shape constrained to keys or values we cannot name is not \"anything\";\n\t\t\t// it is a map, and a map has no GraphQL projection.\n\t\t\tfor (const keyword of [\"patternProperties\", \"propertyNames\"] as const) {\n\t\t\t\tif (schema[keyword] !== undefined) unsupported(projection, path, `uses \"${keyword}\" with no declared properties, which has no GraphQL projection`);\n\t\t\t}\n\t\t\tif (schema.additionalProperties !== undefined && typeof schema.additionalProperties !== \"boolean\") {\n\t\t\t\tunsupported(projection, path, `constrains \"additionalProperties\" with no declared properties, which has no GraphQL projection`);\n\t\t\t}\n\t\t\t// Genuinely unconstrained: any JSON object is valid here.\n\t\t\treturn \"JSON\";\n\t\t}\n\t\tif (schema.patternProperties !== undefined) unsupported(projection, path, `uses \"patternProperties\" alongside \"properties\", which has no GraphQL projection`);\n\t\tconst name = reserve(projection, preferred);\n\t\tprojection.types.set(name, objectSdl(name, properties, schema, projection, path));\n\t\treturn name;\n\t}\n\n\tif (type === \"array\") {\n\t\tif (schema.prefixItems !== undefined) unsupported(projection, path, `uses \"prefixItems\", which has no GraphQL projection`);\n\t\tif (Array.isArray(schema.items)) unsupported(projection, path, `uses tuple \"items\", which has no GraphQL projection`);\n\t\t// A list whose element type is undeclared cannot be expressed as a GraphQL list.\n\t\tif (schema.items === undefined) unsupported(projection, path, `declares an array without \"items\", so its element type has no GraphQL projection`);\n\t\tconst item = schema.items;\n\t\tconst reference = project(item, `${preferred}Item`, projection, `${path}[]`);\n\t\treturn `[${reference}${admitsNull(item, projection) ? \"\" : \"!\"}]`;\n\t}\n\n\tconst named = enumType(projection, schema, preferred);\n\tif (named) return named;\n\tconst scalar = SCALARS[type];\n\tif (!scalar) unsupported(projection, path, `declares unsupported type \"${type}\"`);\n\treturn scalar;\n}\n\nfunction objectSdl(\n\tname: string,\n\tproperties: Array<[string, PortableJsonSchema]>,\n\tschema: PortableJsonSchema,\n\tprojection: Projection,\n\tpath: string,\n): string {\n\tconst required = new Set(schema.required ?? []);\n\tconst fieldNames = properties.map(([property]) => identifier(property));\n\tif (new Set(fieldNames).size !== fieldNames.length) {\n\t\tthrow new Error(`${projection.subject}${path} produces colliding GraphQL field names.`);\n\t}\n\tconst fields = properties.map(([property, child], index) => {\n\t\tconst reference = project(child, `${name}${pascal(property)}`, projection, `${path}.${property}`);\n\t\tconst nonNull = required.has(property) && !admitsNull(child, projection);\n\t\treturn ` ${fieldNames[index]}: ${reference}${nonNull ? \"!\" : \"\"}`;\n\t});\n\treturn `type ${name} {\\n${fields.join(\"\\n\")}\\n}`;\n}\n\nexport const graphqlGenerator: GeneratorTarget = {\n\tname: \"@fabricorg/gen-graphql\",\n\tversion: \"3.0.0\",\n\tgenerate(ir) {\n\t\treturn ir.modules.flatMap((module) => {\n\t\t\tconst namespaceType = pascal(module.namespace);\n\t\t\t// One registry per module: entity types, their nested types and every view\n\t\t\t// payload draw names from the same pool, so a collision is impossible rather\n\t\t\t// than merely unlikely.\n\t\t\tconst types = new Map<string, string>();\n\n\t\t\t// One projection per document, so a `$ref` in any property resolves against the\n\t\t\t// entity's own `$defs` rather than against that single property.\n\t\t\tconst projectionFor = (root: PortableJsonSchema, rootName: string, subject: string): Projection =>\n\t\t\t\t({ types, refs: new Map(), root, rootName, subject });\n\n\t\t\tfor (const objectType of module.objectTypes) {\n\t\t\t\tconst subject = `Module \"${module.namespace}\" object \"${objectType.type}\"`;\n\t\t\t\tconst schema = objectType.schema ?? {};\n\t\t\t\tconst required = new Set(schema.required ?? []);\n\t\t\t\tconst properties = Object.entries(schema.properties ?? {}).filter(([name]) => ![\"id\", \"tenantId\", \"spaceId\"].includes(name));\n\t\t\t\tconst fieldNames = properties.map(([name]) => identifier(name));\n\t\t\t\tif (new Set(fieldNames).size !== fieldNames.length) throw new Error(`Object \"${module.namespace}.${objectType.type}\" produces colliding GraphQL field names.`);\n\t\t\t\tconst entityName = `${namespaceType}${objectType.type}`;\n\t\t\t\tif (types.has(entityName)) throw new Error(`Module \"${module.namespace}\" produces two GraphQL types named \"${entityName}\".`);\n\t\t\t\ttypes.set(entityName, \"\");\n\t\t\t\tconst projection = projectionFor(schema, entityName, subject);\n\t\t\t\tfor (const keyword of [\"oneOf\", \"anyOf\", \"allOf\", \"not\", \"if\"] as const) {\n\t\t\t\t\tif (schema[keyword] !== undefined) unsupported(projection, \"\", `uses \"${keyword}\", which has no GraphQL projection`);\n\t\t\t\t}\n\t\t\t\tconst fields = properties.map(([name, child], index) => {\n\t\t\t\t\tconst reference = project(child, `${entityName}${pascal(name)}`, projection, `.${name}`);\n\t\t\t\t\tconst nonNull = required.has(name) && !admitsNull(child, projection);\n\t\t\t\t\treturn ` ${fieldNames[index]}: ${reference}${nonNull ? \"!\" : \"\"}`;\n\t\t\t\t});\n\t\t\t\ttypes.set(entityName, [`type ${entityName} @key(fields: \"id\") {`, \" id: ID!\", \" tenantId: ID!\", \" spaceId: ID!\", ...fields, \"}\"].join(\"\\n\"));\n\t\t\t}\n\n\t\t\tconst fieldName = (value: string): string => {\n\t\t\t\tconst name = pascal(value);\n\t\t\t\treturn `${name[0]?.toLowerCase() ?? \"\"}${name.slice(1)}`;\n\t\t\t};\n\n\t\t\tconst viewPayloads = module.views.map((view) => {\n\t\t\t\tconst rootName = `${pascal(view.name)}V${view.version.replace(/[^0-9A-Za-z]/g, \"_\")}`;\n\t\t\t\tconst projection = projectionFor(view.shape, rootName, `Module \"${module.namespace}\" view \"${view.name}@${view.version}\" shape`);\n\t\t\t\tconst reference = project(view.shape, rootName, projection, \"\");\n\t\t\t\treturn { field: fieldName(view.name), reference, nonNull: !admitsNull(view.shape, projection) };\n\t\t\t});\n\n\t\t\tconst queryFields = viewPayloads.map((view) => ` ${view.field}: ${view.reference}${view.nonNull ? \"!\" : \"\"}`);\n\t\t\tconst mutations = module.actions.map((action) => ` ${fieldName(action.actionId)}(parameters: JSON!): GovernedActionSubmission!`);\n\t\t\tconst operationNames = [...viewPayloads.map((view) => view.field), ...module.actions.map((action) => fieldName(action.actionId))];\n\t\t\tif (new Set(operationNames).size !== operationNames.length) throw new Error(`Module \"${module.namespace}\" produces colliding GraphQL operation names.`);\n\n\t\t\tconst content = [\n\t\t\t\t`# Generated by ${graphqlGenerator.name}@${graphqlGenerator.version}`,\n\t\t\t\t`# Manifest digest: ${ir.manifestDigest}`,\n\t\t\t\t'extend schema @link(url: \"https://specs.apollo.dev/federation/v2.7\", import: [\"@key\"])',\n\t\t\t\t\"scalar JSON\",\n\t\t\t\t\"type GovernedActionSubmission { actionInvocationId: ID!, status: String! }\",\n\t\t\t\t...[...types.values()].filter(Boolean),\n\t\t\t\t`type Query {\\n${queryFields.length ? queryFields.join(\"\\n\") : \" _fabric: Boolean!\"}\\n}`,\n\t\t\t\t`type Mutation {\\n${mutations.length ? mutations.join(\"\\n\") : \" _fabric: Boolean!\"}\\n}`,\n\t\t\t\t\"\",\n\t\t\t].join(\"\\n\\n\");\n\n\t\t\tconst actionIds = Object.fromEntries(module.actions.map((action) => [fieldName(action.actionId), action.actionId]));\n\t\t\tconst viewIds = Object.fromEntries(module.views.map((view) => [fieldName(view.name), { name: view.name, version: view.version }]));\n\n\t\t\tconst resolvers = [\n\t\t\t\t'import type { ProjectionHost, ProjectionQuery } from \"@fabricorg/projection-host\";',\n\t\t\t\t'import type { GovernedActionHost, SubmitActionInput, SubmitActionResult } from \"@fabricorg/platform-host\";',\n\t\t\t\t\"\",\n\t\t\t\t\"// Generated resolver seam. Authenticate first; derive actor and tenant server-side.\",\n\t\t\t\t`export const actionIds = ${JSON.stringify(actionIds, null, 2)} as const;`,\n\t\t\t\t`export const viewIds = ${JSON.stringify(viewIds, null, 2)} as const;`,\n\t\t\t\t\"\",\n\t\t\t\t'export async function resolveProjection(host: Pick<ProjectionHost, \"project\">, field: keyof typeof viewIds, context: Omit<ProjectionQuery, \"view\">): Promise<unknown> {',\n\t\t\t\t\" const result = await host.project({ ...context, view: viewIds[field] });\",\n\t\t\t\t\" return result.state;\",\n\t\t\t\t\"}\",\n\t\t\t\t'export type GovernedMutationContext = Omit<SubmitActionInput, \"actionId\" | \"parameters\">;',\n\t\t\t\t'export async function submitMutation(host: Pick<GovernedActionHost, \"submitAction\">, field: keyof typeof actionIds, parameters: Record<string, unknown>, context: GovernedMutationContext): Promise<SubmitActionResult> {',\n\t\t\t\t\" return await host.submitAction({ ...context, actionId: actionIds[field], parameters });\",\n\t\t\t\t\"}\",\n\t\t\t\t\"\",\n\t\t\t\t\"export interface ResolverWiring<TContext> {\",\n\t\t\t\t' projectionHost: Pick<ProjectionHost, \"project\">;',\n\t\t\t\t' actionHost: Pick<GovernedActionHost, \"submitAction\">;',\n\t\t\t\t\" /** Derive tenant, space and actor from the authenticated request. Never from client input. */\",\n\t\t\t\t' projectionContext: (context: TContext) => Omit<ProjectionQuery, \"view\"> | Promise<Omit<ProjectionQuery, \"view\">>;',\n\t\t\t\t\" mutationContext: (context: TContext) => GovernedMutationContext | Promise<GovernedMutationContext>;\",\n\t\t\t\t\"}\",\n\t\t\t\t\"\",\n\t\t\t\t\"/** Executable resolver map. Query fields read through ProjectionHost; Mutation fields submit through the governed Host. */\",\n\t\t\t\t\"export function createResolvers<TContext>(wiring: ResolverWiring<TContext>) {\",\n\t\t\t\t\" return {\",\n\t\t\t\t\" Query: {\",\n\t\t\t\t...Object.keys(viewIds).map((field) =>\n\t\t\t\t\t` ${JSON.stringify(field)}: async (_parent: unknown, _args: unknown, context: TContext) => resolveProjection(wiring.projectionHost, ${JSON.stringify(field)}, await wiring.projectionContext(context)),`,\n\t\t\t\t),\n\t\t\t\t\" },\",\n\t\t\t\t\" Mutation: {\",\n\t\t\t\t...Object.keys(actionIds).map((field) =>\n\t\t\t\t\t` ${JSON.stringify(field)}: async (_parent: unknown, args: { parameters: Record<string, unknown> }, context: TContext) => submitMutation(wiring.actionHost, ${JSON.stringify(field)}, args.parameters, await wiring.mutationContext(context)),`,\n\t\t\t\t),\n\t\t\t\t\" },\",\n\t\t\t\t\" };\",\n\t\t\t\t\"}\",\n\t\t\t\t\"\",\n\t\t\t].join(\"\\n\");\n\n\t\t\treturn [{ path: `${module.namespace}/schema.graphql`, content }, { path: `${module.namespace}/resolver-contract.ts`, content: resolvers }];\n\t\t});\n\t},\n};\n"],"mappings":"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","names":["named","module"]}
package/dist/index.js CHANGED
@@ -1,42 +1,182 @@
1
1
  // index.ts
2
2
  var pascal = (value) => value.split(/[-._/]/).filter(Boolean).map((part) => part[0]?.toUpperCase() + part.slice(1)).join("");
3
- var gql = (schema) => {
4
- const type = Array.isArray(schema.type) ? schema.type[0] : schema.type;
5
- return type === "integer" ? "Int" : type === "number" ? "Float" : type === "boolean" ? "Boolean" : type === "string" ? "String" : "JSON";
6
- };
7
3
  var identifier = (value) => {
8
4
  const normalized = value.replace(/[^_0-9A-Za-z]/g, "_");
9
5
  return /^[_A-Za-z]/.test(normalized) ? normalized : `field_${normalized}`;
10
6
  };
7
+ var SCALARS = { string: "String", integer: "Int", number: "Float", boolean: "Boolean" };
8
+ function unsupported(projection, path, reason) {
9
+ throw new Error(`${projection.subject}${path} ${reason}. Give the view or object an explicit GraphQL-projectable shape.`);
10
+ }
11
+ function reserve(projection, preferred) {
12
+ let name = preferred;
13
+ for (let suffix = 2; projection.types.has(name); suffix += 1) name = `${preferred}${suffix}`;
14
+ projection.types.set(name, "");
15
+ return name;
16
+ }
17
+ function resolveRef(projection, ref, path) {
18
+ const match = /^#\/\$defs\/([^/]+)$/.exec(ref);
19
+ if (!match) unsupported(projection, path, `uses $ref "${ref}", which is not a local "#/$defs/<name>" pointer`);
20
+ const key = decodeURIComponent(match[1].replace(/~1/g, "/").replace(/~0/g, "~"));
21
+ const target = projection.root.$defs?.[key];
22
+ if (!target) unsupported(projection, path, `references missing definition "#/$defs/${key}"`);
23
+ return { schema: target, key };
24
+ }
25
+ function isObjectShape(schema) {
26
+ return schema.$ref === void 0 && (schema.type === void 0 || schema.type === "object") && !Array.isArray(schema.enum) && Object.keys(schema.properties ?? {}).length > 0;
27
+ }
28
+ function admitsNull(schema, projection, seen = /* @__PURE__ */ new Set()) {
29
+ if (schema.$ref !== void 0) {
30
+ const match = /^#\/\$defs\/([^/]+)$/.exec(schema.$ref);
31
+ if (!match) return false;
32
+ const key = decodeURIComponent(match[1].replace(/~1/g, "/").replace(/~0/g, "~"));
33
+ if (seen.has(key)) return false;
34
+ seen.add(key);
35
+ const target = projection.root.$defs?.[key];
36
+ return target ? admitsNull(target, projection, seen) : false;
37
+ }
38
+ if (Array.isArray(schema.type)) return schema.type.includes("null");
39
+ if (schema.type === "null") return true;
40
+ return Array.isArray(schema.enum) ? schema.enum.includes(null) : false;
41
+ }
42
+ function enumType(projection, schema, preferred) {
43
+ const values = schema.enum;
44
+ if (!Array.isArray(values) || values.length === 0) return void 0;
45
+ const members = values.filter((value) => value !== null);
46
+ if (members.length === 0) return void 0;
47
+ if (!members.every((value) => typeof value === "string" && /^[_A-Za-z][_0-9A-Za-z]*$/.test(value))) return void 0;
48
+ const unique = [...new Set(members)];
49
+ const name = reserve(projection, preferred);
50
+ projection.types.set(name, `enum ${name} {
51
+ ${unique.map((member) => ` ${member}`).join("\n")}
52
+ }`);
53
+ return name;
54
+ }
55
+ function project(schema, preferred, projection, path) {
56
+ for (const keyword of ["oneOf", "anyOf", "allOf", "not", "if"]) {
57
+ if (schema[keyword] !== void 0) unsupported(projection, path, `uses "${keyword}", which has no GraphQL projection`);
58
+ }
59
+ if (schema.$ref !== void 0) {
60
+ const { schema: target, key } = resolveRef(projection, schema.$ref, path);
61
+ const claimed = projection.refs.get(key);
62
+ if (claimed) return claimed;
63
+ const preferredRefName = `${projection.rootName}${pascal(key)}`;
64
+ if (isObjectShape(target)) {
65
+ const name = reserve(projection, preferredRefName);
66
+ projection.refs.set(key, name);
67
+ projection.types.set(name, objectSdl(name, Object.entries(target.properties ?? {}), target, projection, `${path} -> $defs.${key}`));
68
+ return name;
69
+ }
70
+ const projected = project(target, preferredRefName, projection, `${path} -> $defs.${key}`);
71
+ projection.refs.set(key, projected);
72
+ return projected;
73
+ }
74
+ const declared = Array.isArray(schema.type) ? schema.type.filter((entry) => entry !== "null") : schema.type === void 0 ? [] : [schema.type];
75
+ if (Array.isArray(schema.type) && declared.length > 1) {
76
+ unsupported(projection, path, `declares the union type [${schema.type.join(", ")}], which has no GraphQL projection`);
77
+ }
78
+ if (Array.isArray(schema.type) && declared.length === 0) {
79
+ unsupported(projection, path, `declares only the null type, which has no GraphQL projection`);
80
+ }
81
+ const type = declared[0];
82
+ if (type === "null") unsupported(projection, path, `declares only the null type, which has no GraphQL projection`);
83
+ if (type === void 0 || type === "object") {
84
+ const named2 = enumType(projection, schema, preferred);
85
+ if (named2) return named2;
86
+ const properties = Object.entries(schema.properties ?? {});
87
+ if (properties.length === 0) {
88
+ for (const keyword of ["patternProperties", "propertyNames"]) {
89
+ if (schema[keyword] !== void 0) unsupported(projection, path, `uses "${keyword}" with no declared properties, which has no GraphQL projection`);
90
+ }
91
+ if (schema.additionalProperties !== void 0 && typeof schema.additionalProperties !== "boolean") {
92
+ unsupported(projection, path, `constrains "additionalProperties" with no declared properties, which has no GraphQL projection`);
93
+ }
94
+ return "JSON";
95
+ }
96
+ if (schema.patternProperties !== void 0) unsupported(projection, path, `uses "patternProperties" alongside "properties", which has no GraphQL projection`);
97
+ const name = reserve(projection, preferred);
98
+ projection.types.set(name, objectSdl(name, properties, schema, projection, path));
99
+ return name;
100
+ }
101
+ if (type === "array") {
102
+ if (schema.prefixItems !== void 0) unsupported(projection, path, `uses "prefixItems", which has no GraphQL projection`);
103
+ if (Array.isArray(schema.items)) unsupported(projection, path, `uses tuple "items", which has no GraphQL projection`);
104
+ if (schema.items === void 0) unsupported(projection, path, `declares an array without "items", so its element type has no GraphQL projection`);
105
+ const item = schema.items;
106
+ const reference = project(item, `${preferred}Item`, projection, `${path}[]`);
107
+ return `[${reference}${admitsNull(item, projection) ? "" : "!"}]`;
108
+ }
109
+ const named = enumType(projection, schema, preferred);
110
+ if (named) return named;
111
+ const scalar = SCALARS[type];
112
+ if (!scalar) unsupported(projection, path, `declares unsupported type "${type}"`);
113
+ return scalar;
114
+ }
115
+ function objectSdl(name, properties, schema, projection, path) {
116
+ const required = new Set(schema.required ?? []);
117
+ const fieldNames = properties.map(([property]) => identifier(property));
118
+ if (new Set(fieldNames).size !== fieldNames.length) {
119
+ throw new Error(`${projection.subject}${path} produces colliding GraphQL field names.`);
120
+ }
121
+ const fields = properties.map(([property, child], index) => {
122
+ const reference = project(child, `${name}${pascal(property)}`, projection, `${path}.${property}`);
123
+ const nonNull = required.has(property) && !admitsNull(child, projection);
124
+ return ` ${fieldNames[index]}: ${reference}${nonNull ? "!" : ""}`;
125
+ });
126
+ return `type ${name} {
127
+ ${fields.join("\n")}
128
+ }`;
129
+ }
11
130
  var graphqlGenerator = {
12
131
  name: "@fabricorg/gen-graphql",
13
- version: "1.1.0",
132
+ version: "3.0.0",
14
133
  generate(ir) {
15
134
  return ir.modules.flatMap((module) => {
16
- const types = module.objectTypes.map((objectType) => {
17
- const required = new Set(objectType.schema?.required ?? []);
18
- const properties = Object.entries(objectType.schema?.properties ?? {}).filter(([name]) => !["id", "tenantId", "spaceId"].includes(name));
135
+ const namespaceType = pascal(module.namespace);
136
+ const types = /* @__PURE__ */ new Map();
137
+ const projectionFor = (root, rootName, subject) => ({ types, refs: /* @__PURE__ */ new Map(), root, rootName, subject });
138
+ for (const objectType of module.objectTypes) {
139
+ const subject = `Module "${module.namespace}" object "${objectType.type}"`;
140
+ const schema = objectType.schema ?? {};
141
+ const required = new Set(schema.required ?? []);
142
+ const properties = Object.entries(schema.properties ?? {}).filter(([name]) => !["id", "tenantId", "spaceId"].includes(name));
19
143
  const fieldNames = properties.map(([name]) => identifier(name));
20
144
  if (new Set(fieldNames).size !== fieldNames.length) throw new Error(`Object "${module.namespace}.${objectType.type}" produces colliding GraphQL field names.`);
21
- const fields = properties.map(
22
- ([name, schema]) => ` ${identifier(name)}: ${gql(schema)}${required.has(name) ? "!" : ""}`
23
- );
24
- return [`type ${pascal(module.namespace)}${objectType.type} @key(fields: "id") {`, " id: ID!", " tenantId: ID!", " spaceId: ID!", ...fields, "}"].join("\n");
25
- });
145
+ const entityName = `${namespaceType}${objectType.type}`;
146
+ if (types.has(entityName)) throw new Error(`Module "${module.namespace}" produces two GraphQL types named "${entityName}".`);
147
+ types.set(entityName, "");
148
+ const projection = projectionFor(schema, entityName, subject);
149
+ for (const keyword of ["oneOf", "anyOf", "allOf", "not", "if"]) {
150
+ if (schema[keyword] !== void 0) unsupported(projection, "", `uses "${keyword}", which has no GraphQL projection`);
151
+ }
152
+ const fields = properties.map(([name, child], index) => {
153
+ const reference = project(child, `${entityName}${pascal(name)}`, projection, `.${name}`);
154
+ const nonNull = required.has(name) && !admitsNull(child, projection);
155
+ return ` ${fieldNames[index]}: ${reference}${nonNull ? "!" : ""}`;
156
+ });
157
+ types.set(entityName, [`type ${entityName} @key(fields: "id") {`, " id: ID!", " tenantId: ID!", " spaceId: ID!", ...fields, "}"].join("\n"));
158
+ }
26
159
  const fieldName = (value) => {
27
160
  const name = pascal(value);
28
161
  return `${name[0]?.toLowerCase() ?? ""}${name.slice(1)}`;
29
162
  };
30
- const queryFields = module.views.map((view) => ` ${fieldName(view.name)}: JSON!`);
163
+ const viewPayloads = module.views.map((view) => {
164
+ const rootName = `${pascal(view.name)}V${view.version.replace(/[^0-9A-Za-z]/g, "_")}`;
165
+ const projection = projectionFor(view.shape, rootName, `Module "${module.namespace}" view "${view.name}@${view.version}" shape`);
166
+ const reference = project(view.shape, rootName, projection, "");
167
+ return { field: fieldName(view.name), reference, nonNull: !admitsNull(view.shape, projection) };
168
+ });
169
+ const queryFields = viewPayloads.map((view) => ` ${view.field}: ${view.reference}${view.nonNull ? "!" : ""}`);
31
170
  const mutations = module.actions.map((action) => ` ${fieldName(action.actionId)}(parameters: JSON!): GovernedActionSubmission!`);
32
- const operationNames = [...module.views.map((view) => fieldName(view.name)), ...module.actions.map((action) => fieldName(action.actionId))];
171
+ const operationNames = [...viewPayloads.map((view) => view.field), ...module.actions.map((action) => fieldName(action.actionId))];
33
172
  if (new Set(operationNames).size !== operationNames.length) throw new Error(`Module "${module.namespace}" produces colliding GraphQL operation names.`);
34
173
  const content = [
35
174
  `# Generated by ${graphqlGenerator.name}@${graphqlGenerator.version}`,
36
175
  `# Manifest digest: ${ir.manifestDigest}`,
176
+ 'extend schema @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@key"])',
37
177
  "scalar JSON",
38
178
  "type GovernedActionSubmission { actionInvocationId: ID!, status: String! }",
39
- ...types,
179
+ ...[...types.values()].filter(Boolean),
40
180
  `type Query {
41
181
  ${queryFields.length ? queryFields.join("\n") : " _fabric: Boolean!"}
42
182
  }`,
@@ -45,18 +185,48 @@ ${mutations.length ? mutations.join("\n") : " _fabric: Boolean!"}
45
185
  }`,
46
186
  ""
47
187
  ].join("\n\n");
188
+ const actionIds = Object.fromEntries(module.actions.map((action) => [fieldName(action.actionId), action.actionId]));
189
+ const viewIds = Object.fromEntries(module.views.map((view) => [fieldName(view.name), { name: view.name, version: view.version }]));
48
190
  const resolvers = [
49
191
  'import type { ProjectionHost, ProjectionQuery } from "@fabricorg/projection-host";',
192
+ 'import type { GovernedActionHost, SubmitActionInput, SubmitActionResult } from "@fabricorg/platform-host";',
50
193
  "",
51
194
  "// Generated resolver seam. Authenticate first; derive actor and tenant server-side.",
52
- `export const actionIds = ${JSON.stringify(Object.fromEntries(module.actions.map((action) => [fieldName(action.actionId), action.actionId])), null, 2)} as const;`,
53
- `export const viewIds = ${JSON.stringify(Object.fromEntries(module.views.map((view) => [fieldName(view.name), { name: view.name, version: view.version }])), null, 2)} as const;`,
195
+ `export const actionIds = ${JSON.stringify(actionIds, null, 2)} as const;`,
196
+ `export const viewIds = ${JSON.stringify(viewIds, null, 2)} as const;`,
197
+ "",
54
198
  'export async function resolveProjection(host: Pick<ProjectionHost, "project">, field: keyof typeof viewIds, context: Omit<ProjectionQuery, "view">): Promise<unknown> {',
55
199
  " const result = await host.project({ ...context, view: viewIds[field] });",
56
200
  " return result.state;",
57
201
  "}",
58
- "// Query resolvers call resolveProjection(); never read a projection store directly.",
59
- "// Each mutation resolver must call createGovernedActionHost().submitAction(); never call handlers directly.",
202
+ 'export type GovernedMutationContext = Omit<SubmitActionInput, "actionId" | "parameters">;',
203
+ 'export async function submitMutation(host: Pick<GovernedActionHost, "submitAction">, field: keyof typeof actionIds, parameters: Record<string, unknown>, context: GovernedMutationContext): Promise<SubmitActionResult> {',
204
+ " return await host.submitAction({ ...context, actionId: actionIds[field], parameters });",
205
+ "}",
206
+ "",
207
+ "export interface ResolverWiring<TContext> {",
208
+ ' projectionHost: Pick<ProjectionHost, "project">;',
209
+ ' actionHost: Pick<GovernedActionHost, "submitAction">;',
210
+ " /** Derive tenant, space and actor from the authenticated request. Never from client input. */",
211
+ ' projectionContext: (context: TContext) => Omit<ProjectionQuery, "view"> | Promise<Omit<ProjectionQuery, "view">>;',
212
+ " mutationContext: (context: TContext) => GovernedMutationContext | Promise<GovernedMutationContext>;",
213
+ "}",
214
+ "",
215
+ "/** Executable resolver map. Query fields read through ProjectionHost; Mutation fields submit through the governed Host. */",
216
+ "export function createResolvers<TContext>(wiring: ResolverWiring<TContext>) {",
217
+ " return {",
218
+ " Query: {",
219
+ ...Object.keys(viewIds).map(
220
+ (field) => ` ${JSON.stringify(field)}: async (_parent: unknown, _args: unknown, context: TContext) => resolveProjection(wiring.projectionHost, ${JSON.stringify(field)}, await wiring.projectionContext(context)),`
221
+ ),
222
+ " },",
223
+ " Mutation: {",
224
+ ...Object.keys(actionIds).map(
225
+ (field) => ` ${JSON.stringify(field)}: async (_parent: unknown, args: { parameters: Record<string, unknown> }, context: TContext) => submitMutation(wiring.actionHost, ${JSON.stringify(field)}, args.parameters, await wiring.mutationContext(context)),`
226
+ ),
227
+ " },",
228
+ " };",
229
+ "}",
60
230
  ""
61
231
  ].join("\n");
62
232
  return [{ path: `${module.namespace}/schema.graphql`, content }, { path: `${module.namespace}/resolver-contract.ts`, content: resolvers }];
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../index.ts"],"sourcesContent":["import type { GeneratorTarget } from \"@fabricorg/compiler\";\nimport type { PortableJsonSchema } from \"@fabricorg/platform\";\n\nconst pascal = (value: string): string =>\n\tvalue.split(/[-._/]/).filter(Boolean).map((part) => part[0]?.toUpperCase() + part.slice(1)).join(\"\");\nconst gql = (schema: PortableJsonSchema): string => {\n\tconst type = Array.isArray(schema.type) ? schema.type[0] : schema.type;\n\treturn type === \"integer\" ? \"Int\" : type === \"number\" ? \"Float\" : type === \"boolean\" ? \"Boolean\" : type === \"string\" ? \"String\" : \"JSON\";\n};\nconst identifier = (value: string): string => {\n\tconst normalized = value.replace(/[^_0-9A-Za-z]/g, \"_\");\n\treturn /^[_A-Za-z]/.test(normalized) ? normalized : `field_${normalized}`;\n};\n\nexport const graphqlGenerator: GeneratorTarget = {\n\tname: \"@fabricorg/gen-graphql\",\n\tversion: \"1.1.0\",\n\tgenerate(ir) {\n\t\treturn ir.modules.flatMap((module) => {\n\t\t\tconst types = module.objectTypes.map((objectType) => {\n\t\t\t\tconst required = new Set(objectType.schema?.required ?? []);\n\t\t\t\tconst properties = Object.entries(objectType.schema?.properties ?? {}).filter(([name]) => ![\"id\", \"tenantId\", \"spaceId\"].includes(name));\n\t\t\t\tconst fieldNames = properties.map(([name]) => identifier(name));\n\t\t\t\tif (new Set(fieldNames).size !== fieldNames.length) throw new Error(`Object \"${module.namespace}.${objectType.type}\" produces colliding GraphQL field names.`);\n\t\t\t\tconst fields = properties.map(\n\t\t\t\t\t([name, schema]) => ` ${identifier(name)}: ${gql(schema)}${required.has(name) ? \"!\" : \"\"}`,\n\t\t\t\t);\n\t\t\t\treturn [`type ${pascal(module.namespace)}${objectType.type} @key(fields: \"id\") {`, \" id: ID!\", \" tenantId: ID!\", \" spaceId: ID!\", ...fields, \"}\"].join(\"\\n\");\n\t\t\t});\n\t\t\tconst fieldName = (value: string): string => {\n\t\t\t\tconst name = pascal(value);\n\t\t\t\treturn `${name[0]?.toLowerCase() ?? \"\"}${name.slice(1)}`;\n\t\t\t};\n\t\t\tconst queryFields = module.views.map((view) => ` ${fieldName(view.name)}: JSON!`);\n\t\t\tconst mutations = module.actions.map((action) => ` ${fieldName(action.actionId)}(parameters: JSON!): GovernedActionSubmission!`);\n\t\t\tconst operationNames = [...module.views.map((view) => fieldName(view.name)), ...module.actions.map((action) => fieldName(action.actionId))];\n\t\t\tif (new Set(operationNames).size !== operationNames.length) throw new Error(`Module \"${module.namespace}\" produces colliding GraphQL operation names.`);\n\t\t\tconst content = [\n\t\t\t\t`# Generated by ${graphqlGenerator.name}@${graphqlGenerator.version}`,\n\t\t\t\t`# Manifest digest: ${ir.manifestDigest}`,\n\t\t\t\t\"scalar JSON\",\n\t\t\t\t\"type GovernedActionSubmission { actionInvocationId: ID!, status: String! }\",\n\t\t\t\t...types,\n\t\t\t\t`type Query {\\n${queryFields.length ? queryFields.join(\"\\n\") : \" _fabric: Boolean!\"}\\n}`,\n\t\t\t\t`type Mutation {\\n${mutations.length ? mutations.join(\"\\n\") : \" _fabric: Boolean!\"}\\n}`,\n\t\t\t\t\"\",\n\t\t\t].join(\"\\n\\n\");\n\t\t\tconst resolvers = [\n\t\t\t\t'import type { ProjectionHost, ProjectionQuery } from \"@fabricorg/projection-host\";',\n\t\t\t\t\"\",\n\t\t\t\t\"// Generated resolver seam. Authenticate first; derive actor and tenant server-side.\",\n\t\t\t\t`export const actionIds = ${JSON.stringify(Object.fromEntries(module.actions.map((action) => [fieldName(action.actionId), action.actionId])), null, 2)} as const;`,\n\t\t\t\t`export const viewIds = ${JSON.stringify(Object.fromEntries(module.views.map((view) => [fieldName(view.name), { name: view.name, version: view.version }])), null, 2)} as const;`,\n\t\t\t\t\"export async function resolveProjection(host: Pick<ProjectionHost, \\\"project\\\">, field: keyof typeof viewIds, context: Omit<ProjectionQuery, \\\"view\\\">): Promise<unknown> {\",\n\t\t\t\t\" const result = await host.project({ ...context, view: viewIds[field] });\",\n\t\t\t\t\" return result.state;\",\n\t\t\t\t\"}\",\n\t\t\t\t\"// Query resolvers call resolveProjection(); never read a projection store directly.\",\n\t\t\t\t\"// Each mutation resolver must call createGovernedActionHost().submitAction(); never call handlers directly.\",\n\t\t\t\t\"\",\n\t\t\t].join(\"\\n\");\n\t\t\treturn [{ path: `${module.namespace}/schema.graphql`, content }, { path: `${module.namespace}/resolver-contract.ts`, content: resolvers }];\n\t\t});\n\t},\n};\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../index.ts"],"sourcesContent":["import type { GeneratorTarget } from \"@fabricorg/compiler\";\nimport type { PortableJsonSchema } from \"@fabricorg/platform\";\n\nconst pascal = (value: string): string =>\n\tvalue.split(/[-._/]/).filter(Boolean).map((part) => part[0]?.toUpperCase() + part.slice(1)).join(\"\");\n\nconst identifier = (value: string): string => {\n\tconst normalized = value.replace(/[^_0-9A-Za-z]/g, \"_\");\n\treturn /^[_A-Za-z]/.test(normalized) ? normalized : `field_${normalized}`;\n};\n\nconst SCALARS: Record<string, string> = { string: \"String\", integer: \"Int\", number: \"Float\", boolean: \"Boolean\" };\n\n/**\n * Named SDL blocks discovered while projecting one document, plus the `$defs`\n * names already claimed so a recursive `$ref` resolves to the type being built\n * rather than recursing forever.\n */\ninterface Projection {\n\t/** Insertion-ordered so emitted SDL is deterministic for a given manifest. Empty value means reserved-but-unfilled. */\n\ttypes: Map<string, string>;\n\trefs: Map<string, string>;\n\t/** The whole document, so a `$ref` in any property resolves against its `$defs`. */\n\troot: PortableJsonSchema;\n\trootName: string;\n\tsubject: string;\n}\n\nfunction unsupported(projection: Projection, path: string, reason: string): never {\n\tthrow new Error(`${projection.subject}${path} ${reason}. Give the view or object an explicit GraphQL-projectable shape.`);\n}\n\n/** Take a unique type name before projecting children, so a recursive `$ref` back to this type resolves to the same name. */\nfunction reserve(projection: Projection, preferred: string): string {\n\tlet name = preferred;\n\tfor (let suffix = 2; projection.types.has(name); suffix += 1) name = `${preferred}${suffix}`;\n\tprojection.types.set(name, \"\");\n\treturn name;\n}\n\n/** Resolve `#/$defs/<key>` only; any other pointer is not portable across generators. */\nfunction resolveRef(projection: Projection, ref: string, path: string): { schema: PortableJsonSchema; key: string } {\n\tconst match = /^#\\/\\$defs\\/([^/]+)$/.exec(ref);\n\tif (!match) unsupported(projection, path, `uses $ref \"${ref}\", which is not a local \"#/$defs/<name>\" pointer`);\n\tconst key = decodeURIComponent(match[1]!.replace(/~1/g, \"/\").replace(/~0/g, \"~\"));\n\tconst target = projection.root.$defs?.[key];\n\tif (!target) unsupported(projection, path, `references missing definition \"#/$defs/${key}\"`);\n\treturn { schema: target, key };\n}\n\n/** A shape that becomes a named GraphQL object type rather than a scalar, list or JSON. */\nfunction isObjectShape(schema: PortableJsonSchema): boolean {\n\treturn schema.$ref === undefined\n\t\t&& (schema.type === undefined || schema.type === \"object\")\n\t\t&& !Array.isArray(schema.enum)\n\t\t&& Object.keys(schema.properties ?? {}).length > 0;\n}\n\n/**\n * Whether a value may legitimately be null, following local refs. GraphQL\n * non-null is a promise to the client, so a required field whose definition\n * admits null must still be nullable.\n */\nfunction admitsNull(schema: PortableJsonSchema, projection: Projection, seen: Set<string> = new Set()): boolean {\n\tif (schema.$ref !== undefined) {\n\t\tconst match = /^#\\/\\$defs\\/([^/]+)$/.exec(schema.$ref);\n\t\tif (!match) return false;\n\t\tconst key = decodeURIComponent(match[1]!.replace(/~1/g, \"/\").replace(/~0/g, \"~\"));\n\t\tif (seen.has(key)) return false;\n\t\tseen.add(key);\n\t\tconst target = projection.root.$defs?.[key];\n\t\treturn target ? admitsNull(target, projection, seen) : false;\n\t}\n\tif (Array.isArray(schema.type)) return schema.type.includes(\"null\");\n\tif (schema.type === \"null\") return true;\n\treturn Array.isArray(schema.enum) ? schema.enum.includes(null) : false;\n}\n\nfunction enumType(projection: Projection, schema: PortableJsonSchema, preferred: string): string | undefined {\n\tconst values = schema.enum;\n\tif (!Array.isArray(values) || values.length === 0) return undefined;\n\tconst members = values.filter((value) => value !== null);\n\tif (members.length === 0) return undefined;\n\tif (!members.every((value) => typeof value === \"string\" && /^[_A-Za-z][_0-9A-Za-z]*$/.test(value))) return undefined;\n\tconst unique = [...new Set(members as string[])];\n\tconst name = reserve(projection, preferred);\n\tprojection.types.set(name, `enum ${name} {\\n${unique.map((member) => ` ${member}`).join(\"\\n\")}\\n}`);\n\treturn name;\n}\n\n/** Returns a GraphQL type reference without nullability; callers append `!`. */\nfunction project(schema: PortableJsonSchema, preferred: string, projection: Projection, path: string): string {\n\tfor (const keyword of [\"oneOf\", \"anyOf\", \"allOf\", \"not\", \"if\"] as const) {\n\t\tif (schema[keyword] !== undefined) unsupported(projection, path, `uses \"${keyword}\", which has no GraphQL projection`);\n\t}\n\n\tif (schema.$ref !== undefined) {\n\t\tconst { schema: target, key } = resolveRef(projection, schema.$ref, path);\n\t\tconst claimed = projection.refs.get(key);\n\t\tif (claimed) return claimed;\n\t\tconst preferredRefName = `${projection.rootName}${pascal(key)}`;\n\t\tif (isObjectShape(target)) {\n\t\t\t// Reserve before projecting the definition's own fields, so a definition that\n\t\t\t// refers back to itself resolves to this very name and nothing is ever unbound.\n\t\t\tconst name = reserve(projection, preferredRefName);\n\t\t\tprojection.refs.set(key, name);\n\t\t\tprojection.types.set(name, objectSdl(name, Object.entries(target.properties ?? {}), target, projection, `${path} -> $defs.${key}`));\n\t\t\treturn name;\n\t\t}\n\t\tconst projected = project(target, preferredRefName, projection, `${path} -> $defs.${key}`);\n\t\tprojection.refs.set(key, projected);\n\t\treturn projected;\n\t}\n\n\tconst declared = Array.isArray(schema.type) ? schema.type.filter((entry) => entry !== \"null\") : schema.type === undefined ? [] : [schema.type];\n\tif (Array.isArray(schema.type) && declared.length > 1) {\n\t\tunsupported(projection, path, `declares the union type [${schema.type.join(\", \")}], which has no GraphQL projection`);\n\t}\n\tif (Array.isArray(schema.type) && declared.length === 0) {\n\t\tunsupported(projection, path, `declares only the null type, which has no GraphQL projection`);\n\t}\n\tconst type = declared[0];\n\tif (type === \"null\") unsupported(projection, path, `declares only the null type, which has no GraphQL projection`);\n\n\tif (type === undefined || type === \"object\") {\n\t\tconst named = enumType(projection, schema, preferred);\n\t\tif (named) return named;\n\t\tconst properties = Object.entries(schema.properties ?? {});\n\t\tif (properties.length === 0) {\n\t\t\t// A shape constrained to keys or values we cannot name is not \"anything\";\n\t\t\t// it is a map, and a map has no GraphQL projection.\n\t\t\tfor (const keyword of [\"patternProperties\", \"propertyNames\"] as const) {\n\t\t\t\tif (schema[keyword] !== undefined) unsupported(projection, path, `uses \"${keyword}\" with no declared properties, which has no GraphQL projection`);\n\t\t\t}\n\t\t\tif (schema.additionalProperties !== undefined && typeof schema.additionalProperties !== \"boolean\") {\n\t\t\t\tunsupported(projection, path, `constrains \"additionalProperties\" with no declared properties, which has no GraphQL projection`);\n\t\t\t}\n\t\t\t// Genuinely unconstrained: any JSON object is valid here.\n\t\t\treturn \"JSON\";\n\t\t}\n\t\tif (schema.patternProperties !== undefined) unsupported(projection, path, `uses \"patternProperties\" alongside \"properties\", which has no GraphQL projection`);\n\t\tconst name = reserve(projection, preferred);\n\t\tprojection.types.set(name, objectSdl(name, properties, schema, projection, path));\n\t\treturn name;\n\t}\n\n\tif (type === \"array\") {\n\t\tif (schema.prefixItems !== undefined) unsupported(projection, path, `uses \"prefixItems\", which has no GraphQL projection`);\n\t\tif (Array.isArray(schema.items)) unsupported(projection, path, `uses tuple \"items\", which has no GraphQL projection`);\n\t\t// A list whose element type is undeclared cannot be expressed as a GraphQL list.\n\t\tif (schema.items === undefined) unsupported(projection, path, `declares an array without \"items\", so its element type has no GraphQL projection`);\n\t\tconst item = schema.items;\n\t\tconst reference = project(item, `${preferred}Item`, projection, `${path}[]`);\n\t\treturn `[${reference}${admitsNull(item, projection) ? \"\" : \"!\"}]`;\n\t}\n\n\tconst named = enumType(projection, schema, preferred);\n\tif (named) return named;\n\tconst scalar = SCALARS[type];\n\tif (!scalar) unsupported(projection, path, `declares unsupported type \"${type}\"`);\n\treturn scalar;\n}\n\nfunction objectSdl(\n\tname: string,\n\tproperties: Array<[string, PortableJsonSchema]>,\n\tschema: PortableJsonSchema,\n\tprojection: Projection,\n\tpath: string,\n): string {\n\tconst required = new Set(schema.required ?? []);\n\tconst fieldNames = properties.map(([property]) => identifier(property));\n\tif (new Set(fieldNames).size !== fieldNames.length) {\n\t\tthrow new Error(`${projection.subject}${path} produces colliding GraphQL field names.`);\n\t}\n\tconst fields = properties.map(([property, child], index) => {\n\t\tconst reference = project(child, `${name}${pascal(property)}`, projection, `${path}.${property}`);\n\t\tconst nonNull = required.has(property) && !admitsNull(child, projection);\n\t\treturn ` ${fieldNames[index]}: ${reference}${nonNull ? \"!\" : \"\"}`;\n\t});\n\treturn `type ${name} {\\n${fields.join(\"\\n\")}\\n}`;\n}\n\nexport const graphqlGenerator: GeneratorTarget = {\n\tname: \"@fabricorg/gen-graphql\",\n\tversion: \"3.0.0\",\n\tgenerate(ir) {\n\t\treturn ir.modules.flatMap((module) => {\n\t\t\tconst namespaceType = pascal(module.namespace);\n\t\t\t// One registry per module: entity types, their nested types and every view\n\t\t\t// payload draw names from the same pool, so a collision is impossible rather\n\t\t\t// than merely unlikely.\n\t\t\tconst types = new Map<string, string>();\n\n\t\t\t// One projection per document, so a `$ref` in any property resolves against the\n\t\t\t// entity's own `$defs` rather than against that single property.\n\t\t\tconst projectionFor = (root: PortableJsonSchema, rootName: string, subject: string): Projection =>\n\t\t\t\t({ types, refs: new Map(), root, rootName, subject });\n\n\t\t\tfor (const objectType of module.objectTypes) {\n\t\t\t\tconst subject = `Module \"${module.namespace}\" object \"${objectType.type}\"`;\n\t\t\t\tconst schema = objectType.schema ?? {};\n\t\t\t\tconst required = new Set(schema.required ?? []);\n\t\t\t\tconst properties = Object.entries(schema.properties ?? {}).filter(([name]) => ![\"id\", \"tenantId\", \"spaceId\"].includes(name));\n\t\t\t\tconst fieldNames = properties.map(([name]) => identifier(name));\n\t\t\t\tif (new Set(fieldNames).size !== fieldNames.length) throw new Error(`Object \"${module.namespace}.${objectType.type}\" produces colliding GraphQL field names.`);\n\t\t\t\tconst entityName = `${namespaceType}${objectType.type}`;\n\t\t\t\tif (types.has(entityName)) throw new Error(`Module \"${module.namespace}\" produces two GraphQL types named \"${entityName}\".`);\n\t\t\t\ttypes.set(entityName, \"\");\n\t\t\t\tconst projection = projectionFor(schema, entityName, subject);\n\t\t\t\tfor (const keyword of [\"oneOf\", \"anyOf\", \"allOf\", \"not\", \"if\"] as const) {\n\t\t\t\t\tif (schema[keyword] !== undefined) unsupported(projection, \"\", `uses \"${keyword}\", which has no GraphQL projection`);\n\t\t\t\t}\n\t\t\t\tconst fields = properties.map(([name, child], index) => {\n\t\t\t\t\tconst reference = project(child, `${entityName}${pascal(name)}`, projection, `.${name}`);\n\t\t\t\t\tconst nonNull = required.has(name) && !admitsNull(child, projection);\n\t\t\t\t\treturn ` ${fieldNames[index]}: ${reference}${nonNull ? \"!\" : \"\"}`;\n\t\t\t\t});\n\t\t\t\ttypes.set(entityName, [`type ${entityName} @key(fields: \"id\") {`, \" id: ID!\", \" tenantId: ID!\", \" spaceId: ID!\", ...fields, \"}\"].join(\"\\n\"));\n\t\t\t}\n\n\t\t\tconst fieldName = (value: string): string => {\n\t\t\t\tconst name = pascal(value);\n\t\t\t\treturn `${name[0]?.toLowerCase() ?? \"\"}${name.slice(1)}`;\n\t\t\t};\n\n\t\t\tconst viewPayloads = module.views.map((view) => {\n\t\t\t\tconst rootName = `${pascal(view.name)}V${view.version.replace(/[^0-9A-Za-z]/g, \"_\")}`;\n\t\t\t\tconst projection = projectionFor(view.shape, rootName, `Module \"${module.namespace}\" view \"${view.name}@${view.version}\" shape`);\n\t\t\t\tconst reference = project(view.shape, rootName, projection, \"\");\n\t\t\t\treturn { field: fieldName(view.name), reference, nonNull: !admitsNull(view.shape, projection) };\n\t\t\t});\n\n\t\t\tconst queryFields = viewPayloads.map((view) => ` ${view.field}: ${view.reference}${view.nonNull ? \"!\" : \"\"}`);\n\t\t\tconst mutations = module.actions.map((action) => ` ${fieldName(action.actionId)}(parameters: JSON!): GovernedActionSubmission!`);\n\t\t\tconst operationNames = [...viewPayloads.map((view) => view.field), ...module.actions.map((action) => fieldName(action.actionId))];\n\t\t\tif (new Set(operationNames).size !== operationNames.length) throw new Error(`Module \"${module.namespace}\" produces colliding GraphQL operation names.`);\n\n\t\t\tconst content = [\n\t\t\t\t`# Generated by ${graphqlGenerator.name}@${graphqlGenerator.version}`,\n\t\t\t\t`# Manifest digest: ${ir.manifestDigest}`,\n\t\t\t\t'extend schema @link(url: \"https://specs.apollo.dev/federation/v2.7\", import: [\"@key\"])',\n\t\t\t\t\"scalar JSON\",\n\t\t\t\t\"type GovernedActionSubmission { actionInvocationId: ID!, status: String! }\",\n\t\t\t\t...[...types.values()].filter(Boolean),\n\t\t\t\t`type Query {\\n${queryFields.length ? queryFields.join(\"\\n\") : \" _fabric: Boolean!\"}\\n}`,\n\t\t\t\t`type Mutation {\\n${mutations.length ? mutations.join(\"\\n\") : \" _fabric: Boolean!\"}\\n}`,\n\t\t\t\t\"\",\n\t\t\t].join(\"\\n\\n\");\n\n\t\t\tconst actionIds = Object.fromEntries(module.actions.map((action) => [fieldName(action.actionId), action.actionId]));\n\t\t\tconst viewIds = Object.fromEntries(module.views.map((view) => [fieldName(view.name), { name: view.name, version: view.version }]));\n\n\t\t\tconst resolvers = [\n\t\t\t\t'import type { ProjectionHost, ProjectionQuery } from \"@fabricorg/projection-host\";',\n\t\t\t\t'import type { GovernedActionHost, SubmitActionInput, SubmitActionResult } from \"@fabricorg/platform-host\";',\n\t\t\t\t\"\",\n\t\t\t\t\"// Generated resolver seam. Authenticate first; derive actor and tenant server-side.\",\n\t\t\t\t`export const actionIds = ${JSON.stringify(actionIds, null, 2)} as const;`,\n\t\t\t\t`export const viewIds = ${JSON.stringify(viewIds, null, 2)} as const;`,\n\t\t\t\t\"\",\n\t\t\t\t'export async function resolveProjection(host: Pick<ProjectionHost, \"project\">, field: keyof typeof viewIds, context: Omit<ProjectionQuery, \"view\">): Promise<unknown> {',\n\t\t\t\t\" const result = await host.project({ ...context, view: viewIds[field] });\",\n\t\t\t\t\" return result.state;\",\n\t\t\t\t\"}\",\n\t\t\t\t'export type GovernedMutationContext = Omit<SubmitActionInput, \"actionId\" | \"parameters\">;',\n\t\t\t\t'export async function submitMutation(host: Pick<GovernedActionHost, \"submitAction\">, field: keyof typeof actionIds, parameters: Record<string, unknown>, context: GovernedMutationContext): Promise<SubmitActionResult> {',\n\t\t\t\t\" return await host.submitAction({ ...context, actionId: actionIds[field], parameters });\",\n\t\t\t\t\"}\",\n\t\t\t\t\"\",\n\t\t\t\t\"export interface ResolverWiring<TContext> {\",\n\t\t\t\t' projectionHost: Pick<ProjectionHost, \"project\">;',\n\t\t\t\t' actionHost: Pick<GovernedActionHost, \"submitAction\">;',\n\t\t\t\t\" /** Derive tenant, space and actor from the authenticated request. Never from client input. */\",\n\t\t\t\t' projectionContext: (context: TContext) => Omit<ProjectionQuery, \"view\"> | Promise<Omit<ProjectionQuery, \"view\">>;',\n\t\t\t\t\" mutationContext: (context: TContext) => GovernedMutationContext | Promise<GovernedMutationContext>;\",\n\t\t\t\t\"}\",\n\t\t\t\t\"\",\n\t\t\t\t\"/** Executable resolver map. Query fields read through ProjectionHost; Mutation fields submit through the governed Host. */\",\n\t\t\t\t\"export function createResolvers<TContext>(wiring: ResolverWiring<TContext>) {\",\n\t\t\t\t\" return {\",\n\t\t\t\t\" Query: {\",\n\t\t\t\t...Object.keys(viewIds).map((field) =>\n\t\t\t\t\t` ${JSON.stringify(field)}: async (_parent: unknown, _args: unknown, context: TContext) => resolveProjection(wiring.projectionHost, ${JSON.stringify(field)}, await wiring.projectionContext(context)),`,\n\t\t\t\t),\n\t\t\t\t\" },\",\n\t\t\t\t\" Mutation: {\",\n\t\t\t\t...Object.keys(actionIds).map((field) =>\n\t\t\t\t\t` ${JSON.stringify(field)}: async (_parent: unknown, args: { parameters: Record<string, unknown> }, context: TContext) => submitMutation(wiring.actionHost, ${JSON.stringify(field)}, args.parameters, await wiring.mutationContext(context)),`,\n\t\t\t\t),\n\t\t\t\t\" },\",\n\t\t\t\t\" };\",\n\t\t\t\t\"}\",\n\t\t\t\t\"\",\n\t\t\t].join(\"\\n\");\n\n\t\t\treturn [{ path: `${module.namespace}/schema.graphql`, content }, { path: `${module.namespace}/resolver-contract.ts`, content: resolvers }];\n\t\t});\n\t},\n};\n"],"mappings":"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","names":["named"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fabricorg/gen-graphql",
3
- "version": "1.1.0",
3
+ "version": "3.0.0",
4
4
  "description": "Federation-ready GraphQL contract generator for Fabric compiler IR.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -25,23 +25,20 @@
25
25
  "CHANGELOG.md"
26
26
  ],
27
27
  "dependencies": {
28
- "@fabricorg/compiler": "^1.0.0"
28
+ "@fabricorg/compiler": "^2.1.0"
29
29
  },
30
30
  "peerDependencies": {
31
- "@fabricorg/platform": "^0.12.1",
32
- "@fabricorg/projection-host": "^0.1.0"
33
- },
34
- "peerDependenciesMeta": {
35
- "@fabricorg/projection-host": {
36
- "optional": true
37
- }
31
+ "@fabricorg/platform": "^1.1.0",
32
+ "@fabricorg/platform-host": "^5.0.0",
33
+ "@fabricorg/projection-host": "^1.0.0"
38
34
  },
39
35
  "devDependencies": {
40
36
  "@types/node": "^22.10.0",
41
37
  "tsup": "^8.5.0",
42
38
  "typescript": "^5.7.3",
43
39
  "vitest": "^4.1.5",
44
- "@fabricorg/platform": "^0.12.1"
40
+ "@fabricorg/platform-host": "^5.0.0",
41
+ "@fabricorg/platform": "^1.1.0"
45
42
  },
46
43
  "engines": {
47
44
  "node": ">=20"