@soda-gql/core 0.11.25 → 0.12.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/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { t as applyContextTransformer } from "./context-transformer-sAzKGoyJ.js";
2
2
  import { i as wrapByKey, n as defineOperationRoots, r as defineScalar, t as defineEnum } from "./schema-builder-oVVErGOB.js";
3
- import { Kind, OperationTypeNode } from "graphql";
3
+ import { Kind, OperationTypeNode, parse } from "graphql";
4
4
 
5
5
  //#region packages/core/src/types/type-foundation/var-ref.ts
6
6
  var VarRef = class {
@@ -68,13 +68,15 @@ var DirectiveRef = class {
68
68
  const INPUT_KIND_MAP = {
69
69
  s: "scalar",
70
70
  e: "enum",
71
- i: "input"
71
+ i: "input",
72
+ x: "excluded"
72
73
  };
73
74
  const OUTPUT_KIND_MAP = {
74
75
  s: "scalar",
75
76
  e: "enum",
76
77
  o: "object",
77
- u: "union"
78
+ u: "union",
79
+ x: "excluded"
78
80
  };
79
81
  /**
80
82
  * Parse a deferred input specifier string into a structured object.
@@ -91,7 +93,8 @@ function parseInputSpecifier(spec) {
91
93
  const parts = spec.split("|");
92
94
  const kindChar = parts[0];
93
95
  const name = parts[1];
94
- const modifier = hasDefault ? parts[2] : parts.slice(2).join("|");
96
+ if (!kindChar || !name) throw new Error(`Invalid input specifier format: ${spec}`);
97
+ const modifier = hasDefault ? parts[2] ?? "" : parts.slice(2).join("|");
95
98
  const kind = INPUT_KIND_MAP[kindChar];
96
99
  if (!kind) throw new Error(`Invalid input specifier kind: ${kindChar}`);
97
100
  return {
@@ -115,6 +118,7 @@ function parseOutputSpecifier(spec) {
115
118
  const kindChar = parts[0];
116
119
  const name = parts[1];
117
120
  const modifier = parts[2];
121
+ if (!kindChar || !name || modifier === void 0) throw new Error(`Invalid output specifier format: ${spec}`);
118
122
  const kind = OUTPUT_KIND_MAP[kindChar];
119
123
  if (!kind) throw new Error(`Invalid output specifier kind: ${kindChar}`);
120
124
  return {
@@ -368,6 +372,14 @@ const buildUnionSelection = (union, schema) => {
368
372
  * @param typeName - Parent type name (required for shorthand expansion)
369
373
  */
370
374
  const buildField = (fields, schema, typeName) => Object.entries(fields).map(([alias, value]) => {
375
+ if (alias === "__typename" && isShorthand(value)) return {
376
+ kind: Kind.FIELD,
377
+ name: {
378
+ kind: Kind.NAME,
379
+ value: "__typename"
380
+ }
381
+ };
382
+ if (isShorthand(value) && !typeName) throw new Error(`typeName is required for shorthand expansion of field "${alias}"`);
371
383
  const { args, field, object, union, directives, type } = isShorthand(value) ? expandShorthand(schema, typeName, alias) : value;
372
384
  const parsedType = parseOutputField(type);
373
385
  const builtDirectives = buildDirectives(directives, "FIELD", schema);
@@ -634,6 +646,16 @@ const createColocateHelper = () => {
634
646
  return $colocate;
635
647
  };
636
648
 
649
+ //#endregion
650
+ //#region packages/core/src/types/element/compat-spec.ts
651
+ /**
652
+ * Type guard to distinguish TemplateCompatSpec from CompatSpec at runtime.
653
+ * Uses structural discrimination (presence of `graphqlSource` field).
654
+ */
655
+ const isTemplateCompatSpec = (spec) => {
656
+ return "graphqlSource" in spec && !("fieldsBuilder" in spec);
657
+ };
658
+
637
659
  //#endregion
638
660
  //#region packages/core/src/utils/promise.ts
639
661
  /**
@@ -998,6 +1020,46 @@ const createCompatComposer = (schema, operationType) => {
998
1020
  };
999
1021
  };
1000
1022
 
1023
+ //#endregion
1024
+ //#region packages/core/src/composer/compat-tagged-template.ts
1025
+ /**
1026
+ * Compat tagged template function for creating deferred GraphQL operation specs.
1027
+ * Callback builder compat path is in compat.ts.
1028
+ * @module
1029
+ */
1030
+ /**
1031
+ * Creates a curried tagged template function for compat mode operations.
1032
+ * New API: `query.compat("name")\`($vars) { fields }\`` returns GqlDefine<TemplateCompatSpec>.
1033
+ *
1034
+ * @param schema - The GraphQL schema definition
1035
+ * @param operationType - The operation type (query, mutation, subscription)
1036
+ */
1037
+ const createCompatTaggedTemplate = (schema, operationType) => {
1038
+ if (schema.operations[operationType] === null) throw new Error(`Operation type ${operationType} is not defined in schema roots`);
1039
+ return (operationName) => {
1040
+ return (strings, ...values) => {
1041
+ if (values.length > 0) throw new Error("Tagged templates must not contain interpolated expressions");
1042
+ const source = `${operationType} ${operationName} ${(strings[0] ?? "").trim()}`;
1043
+ let document;
1044
+ try {
1045
+ document = parse(source);
1046
+ } catch (error) {
1047
+ const message = error instanceof Error ? error.message : String(error);
1048
+ throw new Error(`GraphQL parse error in tagged template: ${message}`);
1049
+ }
1050
+ const opDefs = document.definitions.filter((def) => def.kind === Kind.OPERATION_DEFINITION);
1051
+ if (opDefs.length !== 1) throw new Error(`Internal error: expected exactly one operation definition in synthesized source`);
1052
+ if (opDefs[0].kind !== Kind.OPERATION_DEFINITION) throw new Error("Unexpected definition kind");
1053
+ return GqlDefine.create(() => ({
1054
+ schema,
1055
+ operationType,
1056
+ operationName,
1057
+ graphqlSource: source
1058
+ }));
1059
+ };
1060
+ };
1061
+ };
1062
+
1001
1063
  //#endregion
1002
1064
  //#region packages/core/src/composer/directive-builder.ts
1003
1065
  /**
@@ -1104,6 +1166,981 @@ const isDirectiveRef = (value) => {
1104
1166
  return value instanceof DirectiveRef;
1105
1167
  };
1106
1168
 
1169
+ //#endregion
1170
+ //#region packages/core/src/graphql/fragment-args-preprocessor.ts
1171
+ /**
1172
+ * Find the matching closing parenthesis for a balanced group.
1173
+ * Handles nested parentheses, string literals, and comments.
1174
+ * Returns the index of the closing ')' or -1 if not found.
1175
+ */
1176
+ const findMatchingParen = (content, openIndex) => {
1177
+ let depth = 1;
1178
+ let inString = false;
1179
+ for (let i = openIndex + 1; i < content.length; i++) {
1180
+ const ch = content[i];
1181
+ if (inString) {
1182
+ if (ch === inString) {
1183
+ let backslashes = 0;
1184
+ for (let j = i - 1; j >= 0 && content[j] === "\\"; j--) backslashes++;
1185
+ if (backslashes % 2 === 0) inString = false;
1186
+ }
1187
+ continue;
1188
+ }
1189
+ if (ch === "\"" || ch === "'") {
1190
+ inString = ch;
1191
+ continue;
1192
+ }
1193
+ if (ch === "(") depth++;
1194
+ else if (ch === ")") {
1195
+ depth--;
1196
+ if (depth === 0) return i;
1197
+ }
1198
+ }
1199
+ return -1;
1200
+ };
1201
+ /**
1202
+ * Replace a range [start, end] (inclusive) with spaces, preserving newlines.
1203
+ */
1204
+ const replaceWithSpaces = (content, start, end) => {
1205
+ let result = content.slice(0, start);
1206
+ for (let i = start; i <= end; i++) result += content[i] === "\n" ? "\n" : " ";
1207
+ result += content.slice(end + 1);
1208
+ return result;
1209
+ };
1210
+ const FRAGMENT_DEF_PATTERN = /\bfragment\s+(\w+)\s*\(/g;
1211
+ const FRAGMENT_SPREAD_PATTERN = /\.\.\.(\w+)\s*\(/g;
1212
+ /**
1213
+ * Preprocess Fragment Arguments RFC syntax by replacing argument lists with spaces.
1214
+ *
1215
+ * Transforms:
1216
+ * - `fragment UserProfile($showEmail: Boolean = false) on User` -> `fragment UserProfile on User`
1217
+ * - `...UserProfile(showEmail: true)` -> `...UserProfile `
1218
+ */
1219
+ const preprocessFragmentArgs = (content) => {
1220
+ let result = content;
1221
+ let modified = false;
1222
+ let match;
1223
+ FRAGMENT_DEF_PATTERN.lastIndex = 0;
1224
+ while ((match = FRAGMENT_DEF_PATTERN.exec(result)) !== null) {
1225
+ const openParenIndex = match.index + match[0].length - 1;
1226
+ const closeParenIndex = findMatchingParen(result, openParenIndex);
1227
+ if (closeParenIndex === -1) continue;
1228
+ if (!result.slice(closeParenIndex + 1).trimStart().startsWith("on")) continue;
1229
+ result = replaceWithSpaces(result, openParenIndex, closeParenIndex);
1230
+ modified = true;
1231
+ FRAGMENT_DEF_PATTERN.lastIndex = 0;
1232
+ }
1233
+ FRAGMENT_SPREAD_PATTERN.lastIndex = 0;
1234
+ while ((match = FRAGMENT_SPREAD_PATTERN.exec(result)) !== null) {
1235
+ const openParenIndex = match.index + match[0].length - 1;
1236
+ const closeParenIndex = findMatchingParen(result, openParenIndex);
1237
+ if (closeParenIndex === -1) continue;
1238
+ result = replaceWithSpaces(result, openParenIndex, closeParenIndex);
1239
+ modified = true;
1240
+ FRAGMENT_SPREAD_PATTERN.lastIndex = 0;
1241
+ }
1242
+ return {
1243
+ preprocessed: result,
1244
+ modified
1245
+ };
1246
+ };
1247
+
1248
+ //#endregion
1249
+ //#region packages/core/src/graphql/result.ts
1250
+ /** Create a successful Result */
1251
+ const ok = (value) => ({
1252
+ ok: true,
1253
+ value
1254
+ });
1255
+ /** Create a failed Result */
1256
+ const err = (error) => ({
1257
+ ok: false,
1258
+ error
1259
+ });
1260
+
1261
+ //#endregion
1262
+ //#region packages/core/src/graphql/parser.ts
1263
+ /**
1264
+ * GraphQL parser utilities.
1265
+ * Extracts operations and fragments from GraphQL source strings.
1266
+ * @module
1267
+ */
1268
+ /** Parse GraphQL source string directly. No file I/O. */
1269
+ const parseGraphqlSource = (source, sourceFile) => {
1270
+ try {
1271
+ const document = parse(source);
1272
+ return ok({
1273
+ document,
1274
+ ...extractFromDocument(document, sourceFile)
1275
+ });
1276
+ } catch (error) {
1277
+ return err({
1278
+ code: "GRAPHQL_PARSE_ERROR",
1279
+ message: `GraphQL parse error: ${error instanceof Error ? error.message : String(error)}`,
1280
+ filePath: sourceFile
1281
+ });
1282
+ }
1283
+ };
1284
+ /**
1285
+ * Parse a GraphQL TypeNode into type name and modifier.
1286
+ *
1287
+ * Format: inner nullability + list modifiers
1288
+ * - Inner: `!` (non-null) or `?` (nullable)
1289
+ * - List: `[]!` (non-null list) or `[]?` (nullable list)
1290
+ */
1291
+ const parseTypeNode = (node) => {
1292
+ const levels = [];
1293
+ const collect = (n, nonNull) => {
1294
+ if (n.kind === Kind.NON_NULL_TYPE) return collect(n.type, true);
1295
+ if (n.kind === Kind.LIST_TYPE) {
1296
+ levels.push({
1297
+ kind: "list",
1298
+ nonNull
1299
+ });
1300
+ return collect(n.type, false);
1301
+ }
1302
+ levels.push({
1303
+ kind: "named",
1304
+ nonNull
1305
+ });
1306
+ return n.name.value;
1307
+ };
1308
+ const typeName = collect(node, false);
1309
+ let modifier = "?";
1310
+ for (const level of levels.slice().reverse()) {
1311
+ if (level.kind === "named") {
1312
+ modifier = level.nonNull ? "!" : "?";
1313
+ continue;
1314
+ }
1315
+ const listSuffix = level.nonNull ? "[]!" : "[]?";
1316
+ modifier = `${modifier}${listSuffix}`;
1317
+ }
1318
+ return {
1319
+ typeName,
1320
+ modifier
1321
+ };
1322
+ };
1323
+ /** Extract operations and fragments from a parsed GraphQL document. */
1324
+ const extractFromDocument = (document, sourceFile) => {
1325
+ const operations = [];
1326
+ const fragments = [];
1327
+ for (const definition of document.definitions) if (definition.kind === Kind.OPERATION_DEFINITION) {
1328
+ const operation = extractOperation(definition, sourceFile);
1329
+ if (operation) operations.push(operation);
1330
+ } else if (definition.kind === Kind.FRAGMENT_DEFINITION) fragments.push(extractFragment(definition, sourceFile));
1331
+ return {
1332
+ operations,
1333
+ fragments
1334
+ };
1335
+ };
1336
+ /** Extract a single operation from an OperationDefinitionNode. */
1337
+ const extractOperation = (node, sourceFile) => {
1338
+ if (!node.name) return null;
1339
+ const variables = (node.variableDefinitions ?? []).map(extractVariable);
1340
+ const selections = extractSelections(node.selectionSet.selections);
1341
+ return {
1342
+ kind: node.operation,
1343
+ name: node.name.value,
1344
+ variables,
1345
+ selections,
1346
+ sourceFile
1347
+ };
1348
+ };
1349
+ /** Extract a fragment from a FragmentDefinitionNode. */
1350
+ const extractFragment = (node, sourceFile) => {
1351
+ const selections = extractSelections(node.selectionSet.selections);
1352
+ return {
1353
+ name: node.name.value,
1354
+ onType: node.typeCondition.name.value,
1355
+ selections,
1356
+ sourceFile
1357
+ };
1358
+ };
1359
+ /** Extract a variable definition. */
1360
+ const extractVariable = (node) => {
1361
+ const { typeName, modifier } = parseTypeNode(node.type);
1362
+ const defaultValue = node.defaultValue ? extractValue(node.defaultValue) : void 0;
1363
+ return {
1364
+ name: node.variable.name.value,
1365
+ typeName,
1366
+ modifier,
1367
+ typeKind: "scalar",
1368
+ defaultValue
1369
+ };
1370
+ };
1371
+ /** Extract selections from a SelectionSet. */
1372
+ const extractSelections = (selections) => {
1373
+ return selections.map(extractSelection);
1374
+ };
1375
+ /** Extract a single selection. */
1376
+ const extractSelection = (node) => {
1377
+ switch (node.kind) {
1378
+ case Kind.FIELD: return extractFieldSelection(node);
1379
+ case Kind.FRAGMENT_SPREAD: return extractFragmentSpread(node);
1380
+ case Kind.INLINE_FRAGMENT: return extractInlineFragment(node);
1381
+ }
1382
+ };
1383
+ /** Extract a field selection. */
1384
+ const extractFieldSelection = (node) => {
1385
+ const args = node.arguments?.length ? node.arguments.map(extractArgument) : void 0;
1386
+ const selections = node.selectionSet ? extractSelections(node.selectionSet.selections) : void 0;
1387
+ return {
1388
+ kind: "field",
1389
+ name: node.name.value,
1390
+ alias: node.alias?.value,
1391
+ arguments: args,
1392
+ selections
1393
+ };
1394
+ };
1395
+ /** Extract a fragment spread. */
1396
+ const extractFragmentSpread = (node) => {
1397
+ return {
1398
+ kind: "fragmentSpread",
1399
+ name: node.name.value
1400
+ };
1401
+ };
1402
+ /** Extract an inline fragment. */
1403
+ const extractInlineFragment = (node) => {
1404
+ return {
1405
+ kind: "inlineFragment",
1406
+ onType: node.typeCondition?.name.value ?? "",
1407
+ selections: extractSelections(node.selectionSet.selections)
1408
+ };
1409
+ };
1410
+ /** Extract an argument. */
1411
+ const extractArgument = (node) => {
1412
+ return {
1413
+ name: node.name.value,
1414
+ value: extractValue(node.value)
1415
+ };
1416
+ };
1417
+ /** Assert unreachable code path (for exhaustiveness checks). */
1418
+ const assertUnreachable = (value) => {
1419
+ throw new Error(`Unexpected value: ${JSON.stringify(value)}`);
1420
+ };
1421
+ /** Extract a value (literal or variable reference). */
1422
+ const extractValue = (node) => {
1423
+ switch (node.kind) {
1424
+ case Kind.VARIABLE: return {
1425
+ kind: "variable",
1426
+ name: node.name.value
1427
+ };
1428
+ case Kind.INT: return {
1429
+ kind: "int",
1430
+ value: node.value
1431
+ };
1432
+ case Kind.FLOAT: return {
1433
+ kind: "float",
1434
+ value: node.value
1435
+ };
1436
+ case Kind.STRING: return {
1437
+ kind: "string",
1438
+ value: node.value
1439
+ };
1440
+ case Kind.BOOLEAN: return {
1441
+ kind: "boolean",
1442
+ value: node.value
1443
+ };
1444
+ case Kind.NULL: return { kind: "null" };
1445
+ case Kind.ENUM: return {
1446
+ kind: "enum",
1447
+ value: node.value
1448
+ };
1449
+ case Kind.LIST: return {
1450
+ kind: "list",
1451
+ values: node.values.map(extractValue)
1452
+ };
1453
+ case Kind.OBJECT: return {
1454
+ kind: "object",
1455
+ fields: node.fields.map((field) => ({
1456
+ name: field.name.value,
1457
+ value: extractValue(field.value)
1458
+ }))
1459
+ };
1460
+ default: return assertUnreachable(node);
1461
+ }
1462
+ };
1463
+
1464
+ //#endregion
1465
+ //#region packages/core/src/graphql/schema-adapter.ts
1466
+ /**
1467
+ * Create a minimal SchemaIndex from AnyGraphqlSchema.
1468
+ *
1469
+ * IMPORTANT: This adapter produces a "name-resolution only" SchemaIndex.
1470
+ * Only the name-level Maps are populated (.has() lookups work).
1471
+ * Field-level data (FieldDefinitionNode, InputValueDefinitionNode, etc.)
1472
+ * is NOT populated -- those Maps are empty.
1473
+ *
1474
+ * Use this when you need SchemaIndex for type kind resolution only
1475
+ * (e.g., buildVarSpecifier). For full SchemaIndex with field-level data,
1476
+ * use createSchemaIndex(DocumentNode) from schema-index.ts.
1477
+ */
1478
+ const createSchemaIndexFromSchema = (schema) => {
1479
+ const scalars = new Map(Object.keys(schema.scalar).map((n) => [n, {
1480
+ name: n,
1481
+ directives: []
1482
+ }]));
1483
+ const enums = new Map(Object.keys(schema.enum).map((n) => [n, {
1484
+ name: n,
1485
+ values: /* @__PURE__ */ new Map(),
1486
+ directives: []
1487
+ }]));
1488
+ const inputs = new Map(Object.keys(schema.input).map((n) => [n, {
1489
+ name: n,
1490
+ fields: /* @__PURE__ */ new Map(),
1491
+ directives: []
1492
+ }]));
1493
+ return {
1494
+ objects: new Map(Object.keys(schema.object).map((n) => [n, {
1495
+ name: n,
1496
+ fields: /* @__PURE__ */ new Map(),
1497
+ directives: []
1498
+ }])),
1499
+ inputs,
1500
+ enums,
1501
+ unions: new Map(Object.keys(schema.union).map((n) => [n, {
1502
+ name: n,
1503
+ members: /* @__PURE__ */ new Map(),
1504
+ directives: []
1505
+ }])),
1506
+ scalars,
1507
+ directives: /* @__PURE__ */ new Map(),
1508
+ operationTypes: {
1509
+ query: schema.operations.query ?? void 0,
1510
+ mutation: schema.operations.mutation ?? void 0,
1511
+ subscription: schema.operations.subscription ?? void 0
1512
+ }
1513
+ };
1514
+ };
1515
+
1516
+ //#endregion
1517
+ //#region packages/core/src/graphql/schema-index.ts
1518
+ const ensureRecord = (collection, key, factory) => {
1519
+ const existing = collection.get(key);
1520
+ if (existing) return existing;
1521
+ const created = factory(key);
1522
+ collection.set(key, created);
1523
+ return created;
1524
+ };
1525
+ const addObjectFields = (target, fields) => {
1526
+ if (!fields) return;
1527
+ for (const field of fields) target.set(field.name.value, field);
1528
+ };
1529
+ const addInputFields = (target, fields) => {
1530
+ if (!fields) return;
1531
+ for (const field of fields) target.set(field.name.value, field);
1532
+ };
1533
+ const addEnumValues = (target, values) => {
1534
+ if (!values) return;
1535
+ for (const value of values) target.set(value.name.value, value);
1536
+ };
1537
+ const addUnionMembers = (target, members) => {
1538
+ if (!members) return;
1539
+ for (const member of members) target.set(member.name.value, member);
1540
+ };
1541
+ const mergeDirectives = (existing, incoming, precedence) => {
1542
+ const current = existing ?? [];
1543
+ const next = incoming ? Array.from(incoming) : [];
1544
+ return precedence === "definition" ? [...next, ...current] : [...current, ...next];
1545
+ };
1546
+ const updateOperationTypes = (operationTypes, definition) => {
1547
+ for (const operation of definition.operationTypes ?? []) {
1548
+ const typeName = operation.type.name.value;
1549
+ switch (operation.operation) {
1550
+ case "query":
1551
+ operationTypes.query = typeName;
1552
+ break;
1553
+ case "mutation":
1554
+ operationTypes.mutation = typeName;
1555
+ break;
1556
+ case "subscription":
1557
+ operationTypes.subscription = typeName;
1558
+ break;
1559
+ default: break;
1560
+ }
1561
+ }
1562
+ };
1563
+ const addDirectiveArgs = (target, args) => {
1564
+ if (!args) return;
1565
+ for (const arg of args) target.set(arg.name.value, arg);
1566
+ };
1567
+ /** Build a schema index from a parsed GraphQL schema document. */
1568
+ const createSchemaIndex = (document) => {
1569
+ const objects = /* @__PURE__ */ new Map();
1570
+ const inputs = /* @__PURE__ */ new Map();
1571
+ const enums = /* @__PURE__ */ new Map();
1572
+ const unions = /* @__PURE__ */ new Map();
1573
+ const scalars = /* @__PURE__ */ new Map();
1574
+ const directives = /* @__PURE__ */ new Map();
1575
+ const operationTypes = {};
1576
+ for (const definition of document.definitions) switch (definition.kind) {
1577
+ case Kind.OBJECT_TYPE_DEFINITION:
1578
+ case Kind.OBJECT_TYPE_EXTENSION: {
1579
+ const precedence = definition.kind === Kind.OBJECT_TYPE_DEFINITION ? "definition" : "extension";
1580
+ const record = ensureRecord(objects, definition.name.value, (name) => ({
1581
+ name,
1582
+ fields: /* @__PURE__ */ new Map(),
1583
+ directives: []
1584
+ }));
1585
+ addObjectFields(record.fields, definition.fields);
1586
+ record.directives = mergeDirectives(record.directives, definition.directives, precedence);
1587
+ break;
1588
+ }
1589
+ case Kind.INPUT_OBJECT_TYPE_DEFINITION:
1590
+ case Kind.INPUT_OBJECT_TYPE_EXTENSION: {
1591
+ const precedence = definition.kind === Kind.INPUT_OBJECT_TYPE_DEFINITION ? "definition" : "extension";
1592
+ const record = ensureRecord(inputs, definition.name.value, (name) => ({
1593
+ name,
1594
+ fields: /* @__PURE__ */ new Map(),
1595
+ directives: []
1596
+ }));
1597
+ addInputFields(record.fields, definition.fields);
1598
+ record.directives = mergeDirectives(record.directives, definition.directives, precedence);
1599
+ break;
1600
+ }
1601
+ case Kind.ENUM_TYPE_DEFINITION:
1602
+ case Kind.ENUM_TYPE_EXTENSION: {
1603
+ const precedence = definition.kind === Kind.ENUM_TYPE_DEFINITION ? "definition" : "extension";
1604
+ const record = ensureRecord(enums, definition.name.value, (name) => ({
1605
+ name,
1606
+ values: /* @__PURE__ */ new Map(),
1607
+ directives: []
1608
+ }));
1609
+ addEnumValues(record.values, definition.values);
1610
+ record.directives = mergeDirectives(record.directives, definition.directives, precedence);
1611
+ break;
1612
+ }
1613
+ case Kind.UNION_TYPE_DEFINITION:
1614
+ case Kind.UNION_TYPE_EXTENSION: {
1615
+ const precedence = definition.kind === Kind.UNION_TYPE_DEFINITION ? "definition" : "extension";
1616
+ const record = ensureRecord(unions, definition.name.value, (name) => ({
1617
+ name,
1618
+ members: /* @__PURE__ */ new Map(),
1619
+ directives: []
1620
+ }));
1621
+ addUnionMembers(record.members, definition.types);
1622
+ record.directives = mergeDirectives(record.directives, definition.directives, precedence);
1623
+ break;
1624
+ }
1625
+ case Kind.SCALAR_TYPE_DEFINITION:
1626
+ case Kind.SCALAR_TYPE_EXTENSION: {
1627
+ const precedence = definition.kind === Kind.SCALAR_TYPE_DEFINITION ? "definition" : "extension";
1628
+ const record = ensureRecord(scalars, definition.name.value, (name) => ({
1629
+ name,
1630
+ directives: []
1631
+ }));
1632
+ record.directives = mergeDirectives(record.directives, definition.directives, precedence);
1633
+ break;
1634
+ }
1635
+ case Kind.DIRECTIVE_DEFINITION: {
1636
+ const name = definition.name.value;
1637
+ if (name === "skip" || name === "include" || name === "deprecated" || name === "specifiedBy") break;
1638
+ const args = /* @__PURE__ */ new Map();
1639
+ addDirectiveArgs(args, definition.arguments);
1640
+ directives.set(name, {
1641
+ name,
1642
+ locations: definition.locations.map((loc) => loc.value),
1643
+ args,
1644
+ isRepeatable: definition.repeatable
1645
+ });
1646
+ break;
1647
+ }
1648
+ case Kind.SCHEMA_DEFINITION:
1649
+ case Kind.SCHEMA_EXTENSION:
1650
+ updateOperationTypes(operationTypes, definition);
1651
+ break;
1652
+ default: break;
1653
+ }
1654
+ if (!operationTypes.query && objects.has("Query")) operationTypes.query = "Query";
1655
+ if (!operationTypes.mutation && objects.has("Mutation")) operationTypes.mutation = "Mutation";
1656
+ if (!operationTypes.subscription && objects.has("Subscription")) operationTypes.subscription = "Subscription";
1657
+ return {
1658
+ objects,
1659
+ inputs,
1660
+ enums,
1661
+ unions,
1662
+ scalars,
1663
+ directives,
1664
+ operationTypes
1665
+ };
1666
+ };
1667
+
1668
+ //#endregion
1669
+ //#region packages/core/src/graphql/transformer.ts
1670
+ const builtinScalarTypes$1 = new Set([
1671
+ "ID",
1672
+ "String",
1673
+ "Int",
1674
+ "Float",
1675
+ "Boolean"
1676
+ ]);
1677
+ const parseModifierStructure = (modifier) => {
1678
+ const inner = modifier[0] === "!" ? "!" : "?";
1679
+ const lists = [];
1680
+ const listPattern = /\[\]([!?])/g;
1681
+ let match;
1682
+ while ((match = listPattern.exec(modifier)) !== null) lists.push(`[]${match[1]}`);
1683
+ return {
1684
+ inner,
1685
+ lists
1686
+ };
1687
+ };
1688
+ const buildModifier = (structure) => {
1689
+ return structure.inner + structure.lists.join("");
1690
+ };
1691
+ const isModifierAssignable = (source, target) => {
1692
+ const srcStruct = parseModifierStructure(source);
1693
+ const tgtStruct = parseModifierStructure(target);
1694
+ const depthDiff = tgtStruct.lists.length - srcStruct.lists.length;
1695
+ if (depthDiff < 0 || depthDiff > 1) return false;
1696
+ const tgtListsToCompare = depthDiff === 1 ? tgtStruct.lists.slice(1) : tgtStruct.lists;
1697
+ if (depthDiff === 1 && srcStruct.lists.length === 0 && srcStruct.inner === "?" && tgtStruct.lists[0] === "[]!") return false;
1698
+ if (srcStruct.inner === "?" && tgtStruct.inner === "!") return false;
1699
+ for (let i = 0; i < srcStruct.lists.length; i++) {
1700
+ const srcList = srcStruct.lists[i];
1701
+ const tgtList = tgtListsToCompare[i];
1702
+ if (srcList === "[]?" && tgtList === "[]!") return false;
1703
+ }
1704
+ return true;
1705
+ };
1706
+ const deriveMinimumModifier = (expectedModifier) => {
1707
+ const struct = parseModifierStructure(expectedModifier);
1708
+ if (struct.lists.length > 0) return buildModifier({
1709
+ inner: struct.inner,
1710
+ lists: struct.lists.slice(1)
1711
+ });
1712
+ return expectedModifier;
1713
+ };
1714
+ const mergeModifiers = (a, b) => {
1715
+ const structA = parseModifierStructure(a);
1716
+ const structB = parseModifierStructure(b);
1717
+ if (structA.lists.length !== structB.lists.length) return {
1718
+ ok: false,
1719
+ reason: `Incompatible list depths: "${a}" has ${structA.lists.length} list level(s), "${b}" has ${structB.lists.length}`
1720
+ };
1721
+ const mergedInner = structA.inner === "!" || structB.inner === "!" ? "!" : "?";
1722
+ const mergedLists = [];
1723
+ for (let i = 0; i < structA.lists.length; i++) {
1724
+ const listA = structA.lists[i];
1725
+ const listB = structB.lists[i];
1726
+ mergedLists.push(listA === "[]!" || listB === "[]!" ? "[]!" : "[]?");
1727
+ }
1728
+ return {
1729
+ ok: true,
1730
+ value: buildModifier({
1731
+ inner: mergedInner,
1732
+ lists: mergedLists
1733
+ })
1734
+ };
1735
+ };
1736
+ const getArgumentType = (schema, parentTypeName, fieldName, argumentName) => {
1737
+ const objectRecord = schema.objects.get(parentTypeName);
1738
+ if (!objectRecord) return null;
1739
+ const fieldDef = objectRecord.fields.get(fieldName);
1740
+ if (!fieldDef) return null;
1741
+ const argDef = fieldDef.arguments?.find((arg) => arg.name.value === argumentName);
1742
+ if (!argDef) return null;
1743
+ return parseTypeNode(argDef.type);
1744
+ };
1745
+ const getInputFieldType = (schema, inputTypeName, fieldName) => {
1746
+ const inputRecord = schema.inputs.get(inputTypeName);
1747
+ if (!inputRecord) return null;
1748
+ const fieldDef = inputRecord.fields.get(fieldName);
1749
+ if (!fieldDef) return null;
1750
+ return parseTypeNode(fieldDef.type);
1751
+ };
1752
+ const resolveTypeKindFromName = (schema, typeName) => {
1753
+ if (isScalarName(schema, typeName)) return "scalar";
1754
+ if (isEnumName(schema, typeName)) return "enum";
1755
+ if (schema.inputs.has(typeName)) return "input";
1756
+ return null;
1757
+ };
1758
+ const collectVariablesFromValue = (value, expectedTypeName, expectedModifier, schema, usages) => {
1759
+ if (value.kind === "variable") {
1760
+ const typeKind = resolveTypeKindFromName(schema, expectedTypeName);
1761
+ if (!typeKind) return {
1762
+ code: "GRAPHQL_UNKNOWN_TYPE",
1763
+ message: `Unknown type "${expectedTypeName}" for variable "$${value.name}"`,
1764
+ typeName: expectedTypeName
1765
+ };
1766
+ usages.push({
1767
+ name: value.name,
1768
+ typeName: expectedTypeName,
1769
+ expectedModifier,
1770
+ minimumModifier: deriveMinimumModifier(expectedModifier),
1771
+ typeKind
1772
+ });
1773
+ return null;
1774
+ }
1775
+ if (value.kind === "object") {
1776
+ for (const field of value.fields) {
1777
+ const fieldType = getInputFieldType(schema, expectedTypeName, field.name);
1778
+ if (!fieldType) return {
1779
+ code: "GRAPHQL_UNKNOWN_FIELD",
1780
+ message: `Unknown field "${field.name}" on input type "${expectedTypeName}"`,
1781
+ typeName: expectedTypeName,
1782
+ fieldName: field.name
1783
+ };
1784
+ const error = collectVariablesFromValue(field.value, fieldType.typeName, fieldType.modifier, schema, usages);
1785
+ if (error) return error;
1786
+ }
1787
+ return null;
1788
+ }
1789
+ if (value.kind === "list") {
1790
+ const struct = parseModifierStructure(expectedModifier);
1791
+ if (struct.lists.length > 0) {
1792
+ const innerModifier = buildModifier({
1793
+ inner: struct.inner,
1794
+ lists: struct.lists.slice(1)
1795
+ });
1796
+ for (const item of value.values) {
1797
+ const error = collectVariablesFromValue(item, expectedTypeName, innerModifier, schema, usages);
1798
+ if (error) return error;
1799
+ }
1800
+ }
1801
+ }
1802
+ return null;
1803
+ };
1804
+ const collectVariablesFromArguments = (args, parentTypeName, fieldName, schema, usages) => {
1805
+ for (const arg of args) {
1806
+ const argType = getArgumentType(schema, parentTypeName, fieldName, arg.name);
1807
+ if (!argType) return {
1808
+ code: "GRAPHQL_UNKNOWN_ARGUMENT",
1809
+ message: `Unknown argument "${arg.name}" on field "${fieldName}"`,
1810
+ fieldName,
1811
+ argumentName: arg.name
1812
+ };
1813
+ const error = collectVariablesFromValue(arg.value, argType.typeName, argType.modifier, schema, usages);
1814
+ if (error) return error;
1815
+ }
1816
+ return null;
1817
+ };
1818
+ const collectVariableUsages = (selections, parentTypeName, schema) => {
1819
+ const usages = [];
1820
+ const collect = (sels, parentType) => {
1821
+ for (const sel of sels) switch (sel.kind) {
1822
+ case "field":
1823
+ if (sel.arguments && sel.arguments.length > 0) {
1824
+ const error$1 = collectVariablesFromArguments(sel.arguments, parentType, sel.name, schema, usages);
1825
+ if (error$1) return error$1;
1826
+ }
1827
+ if (sel.selections && sel.selections.length > 0) {
1828
+ const fieldReturnType = getFieldReturnType(schema, parentType, sel.name);
1829
+ if (!fieldReturnType) return {
1830
+ code: "GRAPHQL_UNKNOWN_FIELD",
1831
+ message: `Unknown field "${sel.name}" on type "${parentType}"`,
1832
+ typeName: parentType,
1833
+ fieldName: sel.name
1834
+ };
1835
+ const error$1 = collect(sel.selections, fieldReturnType);
1836
+ if (error$1) return error$1;
1837
+ }
1838
+ break;
1839
+ case "inlineFragment": {
1840
+ const error$1 = collect(sel.selections, sel.onType);
1841
+ if (error$1) return error$1;
1842
+ break;
1843
+ }
1844
+ case "fragmentSpread": break;
1845
+ }
1846
+ return null;
1847
+ };
1848
+ const error = collect(selections, parentTypeName);
1849
+ if (error) return err(error);
1850
+ return ok(usages);
1851
+ };
1852
+ const getFieldReturnType = (schema, parentTypeName, fieldName) => {
1853
+ const objectRecord = schema.objects.get(parentTypeName);
1854
+ if (!objectRecord) return null;
1855
+ const fieldDef = objectRecord.fields.get(fieldName);
1856
+ if (!fieldDef) return null;
1857
+ const { typeName } = parseTypeNode(fieldDef.type);
1858
+ return typeName;
1859
+ };
1860
+ const mergeVariableUsages = (variableName, usages) => {
1861
+ if (usages.length === 0) return err({
1862
+ code: "GRAPHQL_UNDECLARED_VARIABLE",
1863
+ message: `No usages found for variable "${variableName}"`,
1864
+ variableName
1865
+ });
1866
+ const first = usages[0];
1867
+ for (const usage of usages) if (usage.typeName !== first.typeName) return err({
1868
+ code: "GRAPHQL_VARIABLE_TYPE_MISMATCH",
1869
+ message: `Variable "$${variableName}" has conflicting types: "${first.typeName}" and "${usage.typeName}"`,
1870
+ variableName
1871
+ });
1872
+ let candidateModifier = first.minimumModifier;
1873
+ for (let i = 1; i < usages.length; i++) {
1874
+ const result = mergeModifiers(candidateModifier, usages[i].minimumModifier);
1875
+ if (!result.ok) return err({
1876
+ code: "GRAPHQL_VARIABLE_MODIFIER_INCOMPATIBLE",
1877
+ message: `Variable "$${variableName}" has incompatible modifiers: ${result.reason}`,
1878
+ variableName
1879
+ });
1880
+ candidateModifier = result.value;
1881
+ }
1882
+ for (const usage of usages) if (!isModifierAssignable(candidateModifier, usage.expectedModifier)) return err({
1883
+ code: "GRAPHQL_VARIABLE_MODIFIER_INCOMPATIBLE",
1884
+ message: `Variable "$${variableName}" with modifier "${candidateModifier}" cannot satisfy expected "${usage.expectedModifier}"`,
1885
+ variableName
1886
+ });
1887
+ return ok({
1888
+ name: variableName,
1889
+ typeName: first.typeName,
1890
+ modifier: candidateModifier,
1891
+ typeKind: first.typeKind
1892
+ });
1893
+ };
1894
+ const inferVariablesFromUsages = (usages) => {
1895
+ const byName = /* @__PURE__ */ new Map();
1896
+ for (const usage of usages) {
1897
+ const existing = byName.get(usage.name);
1898
+ if (existing) existing.push(usage);
1899
+ else byName.set(usage.name, [usage]);
1900
+ }
1901
+ const variables = [];
1902
+ for (const [name, group] of byName) {
1903
+ const result = mergeVariableUsages(name, group);
1904
+ if (!result.ok) return err(result.error);
1905
+ variables.push(result.value);
1906
+ }
1907
+ variables.sort((a, b) => a.name.localeCompare(b.name));
1908
+ return ok(variables);
1909
+ };
1910
+ const isScalarName = (schema, name) => builtinScalarTypes$1.has(name) || schema.scalars.has(name);
1911
+ const sortFragmentsByDependency = (fragments) => {
1912
+ const graph = /* @__PURE__ */ new Map();
1913
+ for (const fragment of fragments) {
1914
+ const deps = collectFragmentDependenciesSet(fragment.selections);
1915
+ graph.set(fragment.name, deps);
1916
+ }
1917
+ const fragmentByName = /* @__PURE__ */ new Map();
1918
+ for (const f of fragments) fragmentByName.set(f.name, f);
1919
+ const sorted = [];
1920
+ const visited = /* @__PURE__ */ new Set();
1921
+ const visiting = /* @__PURE__ */ new Set();
1922
+ const visit = (name, path) => {
1923
+ if (visited.has(name)) return null;
1924
+ if (visiting.has(name)) {
1925
+ const cycleStart = path.indexOf(name);
1926
+ const cycle = path.slice(cycleStart).concat(name);
1927
+ return {
1928
+ code: "GRAPHQL_FRAGMENT_CIRCULAR_DEPENDENCY",
1929
+ message: `Circular dependency detected in fragments: ${cycle.join(" -> ")}`,
1930
+ fragmentNames: cycle
1931
+ };
1932
+ }
1933
+ const fragment = fragmentByName.get(name);
1934
+ if (!fragment) {
1935
+ visited.add(name);
1936
+ return null;
1937
+ }
1938
+ visiting.add(name);
1939
+ const deps = graph.get(name) ?? /* @__PURE__ */ new Set();
1940
+ for (const dep of deps) {
1941
+ const error = visit(dep, [...path, name]);
1942
+ if (error) return error;
1943
+ }
1944
+ visiting.delete(name);
1945
+ visited.add(name);
1946
+ sorted.push(fragment);
1947
+ return null;
1948
+ };
1949
+ for (const fragment of fragments) {
1950
+ const error = visit(fragment.name, []);
1951
+ if (error) return err(error);
1952
+ }
1953
+ return ok(sorted);
1954
+ };
1955
+ const collectFragmentDependenciesSet = (selections) => {
1956
+ const deps = /* @__PURE__ */ new Set();
1957
+ const collect = (sels) => {
1958
+ for (const sel of sels) switch (sel.kind) {
1959
+ case "fragmentSpread":
1960
+ deps.add(sel.name);
1961
+ break;
1962
+ case "field":
1963
+ if (sel.selections) collect(sel.selections);
1964
+ break;
1965
+ case "inlineFragment":
1966
+ collect(sel.selections);
1967
+ break;
1968
+ }
1969
+ };
1970
+ collect(selections);
1971
+ return deps;
1972
+ };
1973
+ const isEnumName = (schema, name) => schema.enums.has(name);
1974
+ const transformParsedGraphql = (parsed, options) => {
1975
+ const schema = createSchemaIndex(options.schemaDocument);
1976
+ const sortResult = sortFragmentsByDependency(parsed.fragments);
1977
+ if (!sortResult.ok) return err(sortResult.error);
1978
+ const sortedFragments = sortResult.value;
1979
+ const resolvedFragmentVariables = /* @__PURE__ */ new Map();
1980
+ const fragments = [];
1981
+ for (const frag of sortedFragments) {
1982
+ const result = transformFragment(frag, schema, resolvedFragmentVariables);
1983
+ if (!result.ok) return err(result.error);
1984
+ resolvedFragmentVariables.set(frag.name, result.value.variables);
1985
+ fragments.push(result.value);
1986
+ }
1987
+ const operations = [];
1988
+ for (const op of parsed.operations) {
1989
+ const result = transformOperation(op, schema);
1990
+ if (!result.ok) return err(result.error);
1991
+ operations.push(result.value);
1992
+ }
1993
+ return ok({
1994
+ operations,
1995
+ fragments
1996
+ });
1997
+ };
1998
+ const transformOperation = (op, schema) => {
1999
+ const variables = [];
2000
+ for (const v of op.variables) {
2001
+ const typeKind = resolveTypeKind$1(schema, v.typeName);
2002
+ if (typeKind === null) return err({
2003
+ code: "GRAPHQL_UNKNOWN_TYPE",
2004
+ message: `Unknown type "${v.typeName}" in variable "${v.name}"`,
2005
+ typeName: v.typeName
2006
+ });
2007
+ variables.push({
2008
+ ...v,
2009
+ typeKind
2010
+ });
2011
+ }
2012
+ const fragmentDependencies = collectFragmentDependencies(op.selections);
2013
+ return ok({
2014
+ ...op,
2015
+ variables,
2016
+ fragmentDependencies
2017
+ });
2018
+ };
2019
+ const transformFragment = (frag, schema, resolvedFragmentVariables) => {
2020
+ const fragmentDependencies = collectFragmentDependencies(frag.selections);
2021
+ const directUsagesResult = collectVariableUsages(frag.selections, frag.onType, schema);
2022
+ if (!directUsagesResult.ok) return err(directUsagesResult.error);
2023
+ const directUsages = directUsagesResult.value;
2024
+ const spreadVariables = [];
2025
+ for (const depName of fragmentDependencies) {
2026
+ const depVariables = resolvedFragmentVariables.get(depName);
2027
+ if (depVariables) spreadVariables.push(...depVariables);
2028
+ }
2029
+ const variablesResult = inferVariablesFromUsages([...directUsages, ...spreadVariables.map((v) => ({
2030
+ name: v.name,
2031
+ typeName: v.typeName,
2032
+ expectedModifier: v.modifier,
2033
+ minimumModifier: v.modifier,
2034
+ typeKind: v.typeKind
2035
+ }))]);
2036
+ if (!variablesResult.ok) return err(variablesResult.error);
2037
+ return ok({
2038
+ ...frag,
2039
+ fragmentDependencies,
2040
+ variables: variablesResult.value
2041
+ });
2042
+ };
2043
+ const resolveTypeKind$1 = (schema, typeName) => {
2044
+ if (isScalarName(schema, typeName)) return "scalar";
2045
+ if (isEnumName(schema, typeName)) return "enum";
2046
+ if (schema.inputs.has(typeName)) return "input";
2047
+ return null;
2048
+ };
2049
+ const collectFragmentDependencies = (selections) => {
2050
+ const fragments = /* @__PURE__ */ new Set();
2051
+ const collect = (sels) => {
2052
+ for (const sel of sels) switch (sel.kind) {
2053
+ case "fragmentSpread":
2054
+ fragments.add(sel.name);
2055
+ break;
2056
+ case "field":
2057
+ if (sel.selections) collect(sel.selections);
2058
+ break;
2059
+ case "inlineFragment":
2060
+ collect(sel.selections);
2061
+ break;
2062
+ }
2063
+ };
2064
+ collect(selections);
2065
+ return [...fragments];
2066
+ };
2067
+
2068
+ //#endregion
2069
+ //#region packages/core/src/graphql/var-specifier-builder.ts
2070
+ /**
2071
+ * Converts VariableDefinitionNode from graphql-js AST into VarSpecifier objects
2072
+ * compatible with the composer's GenericVarSpecifier type.
2073
+ *
2074
+ * Uses throw (not Result) because it will be called from the composer layer.
2075
+ * @module
2076
+ */
2077
+ const builtinScalarTypes = new Set([
2078
+ "ID",
2079
+ "String",
2080
+ "Int",
2081
+ "Float",
2082
+ "Boolean"
2083
+ ]);
2084
+ const resolveTypeKind = (schema, typeName) => {
2085
+ if (builtinScalarTypes.has(typeName) || schema.scalars.has(typeName)) return "scalar";
2086
+ if (schema.enums.has(typeName)) return "enum";
2087
+ if (schema.inputs.has(typeName)) return "input";
2088
+ throw new Error(`Cannot resolve type kind for "${typeName}": not found in schema as scalar, enum, or input`);
2089
+ };
2090
+ /**
2091
+ * Extract a constant value from a ValueNode (for default values).
2092
+ * Similar to graphql-js valueFromAST but without type coercion.
2093
+ */
2094
+ const extractConstValue = (node) => {
2095
+ switch (node.kind) {
2096
+ case Kind.INT: return Number.parseInt(node.value, 10);
2097
+ case Kind.FLOAT: return Number.parseFloat(node.value);
2098
+ case Kind.STRING: return node.value;
2099
+ case Kind.BOOLEAN: return node.value;
2100
+ case Kind.NULL: return null;
2101
+ case Kind.ENUM: return node.value;
2102
+ case Kind.LIST: return node.values.map(extractConstValue);
2103
+ case Kind.OBJECT: {
2104
+ const obj = {};
2105
+ for (const field of node.fields) obj[field.name.value] = extractConstValue(field.value);
2106
+ return obj;
2107
+ }
2108
+ case Kind.VARIABLE: throw new Error("Variable references are not allowed in default values");
2109
+ default: throw new Error(`Unexpected value kind: ${node.kind}`);
2110
+ }
2111
+ };
2112
+ const extractDefaultValue = (node) => {
2113
+ if (!node.defaultValue) return null;
2114
+ return { default: extractConstValue(node.defaultValue) };
2115
+ };
2116
+ /**
2117
+ * Convert a VariableDefinitionNode to a VarSpecifier.
2118
+ * Resolves `kind` (scalar/enum/input) from the schema index.
2119
+ *
2120
+ * @throws Error if type name cannot be resolved in schema
2121
+ */
2122
+ const buildVarSpecifier = (node, schema) => {
2123
+ const { typeName, modifier } = parseTypeNode(node.type);
2124
+ return {
2125
+ kind: resolveTypeKind(schema, typeName),
2126
+ name: typeName,
2127
+ modifier,
2128
+ defaultValue: extractDefaultValue(node),
2129
+ directives: {}
2130
+ };
2131
+ };
2132
+ /**
2133
+ * Convert all variable definitions from a list of VariableDefinitionNodes
2134
+ * into a record keyed by variable name.
2135
+ *
2136
+ * @throws Error if any type name cannot be resolved in schema
2137
+ */
2138
+ const buildVarSpecifiers = (nodes, schema) => {
2139
+ const result = {};
2140
+ for (const node of nodes) result[node.variable.name.value] = buildVarSpecifier(node, schema);
2141
+ return result;
2142
+ };
2143
+
1107
2144
  //#endregion
1108
2145
  //#region packages/core/src/types/metadata/adapter.ts
1109
2146
  /**
@@ -1369,6 +2406,11 @@ const createVarRefs = (definitions) => mapValues(definitions, (_, name) => creat
1369
2406
  //#endregion
1370
2407
  //#region packages/core/src/composer/operation-core.ts
1371
2408
  /**
2409
+ * Core operation building logic shared by operation and extend composers.
2410
+ * @module
2411
+ * @internal
2412
+ */
2413
+ /**
1372
2414
  * Builds an operation artifact from the provided parameters.
1373
2415
  *
1374
2416
  * This function contains the core logic for:
@@ -1381,25 +2423,39 @@ const createVarRefs = (definitions) => mapValues(definitions, (_, name) => creat
1381
2423
  * @param params - Operation building parameters
1382
2424
  * @returns Operation artifact or Promise of artifact (if async metadata)
1383
2425
  *
1384
- * @internal Used by operation.ts and extend.ts
2426
+ * @internal Used by operation.ts, extend.ts, and operation-tagged-template.ts
1385
2427
  */
1386
2428
  const buildOperationArtifact = (params) => {
1387
- const { schema, operationType, operationTypeName, operationName, variables, fieldsFactory, adapter, metadata: metadataBuilder, transformDocument: operationTransformDocument, adapterTransformDocument } = params;
2429
+ const { schema, operationType, operationTypeName, operationName, variables, adapter, metadata: metadataBuilder, transformDocument: operationTransformDocument, adapterTransformDocument } = params;
1388
2430
  const $ = createVarRefs(variables);
1389
- const f = createFieldFactories(schema, operationTypeName);
1390
- const { result: fields, usages: fragmentUsages } = withFragmentUsageCollection(() => fieldsFactory({
1391
- f,
1392
- $
1393
- }));
1394
- const document = buildDocument({
1395
- operationName,
1396
- operationType,
1397
- operationTypeName,
1398
- variables,
1399
- fields,
1400
- schema
1401
- });
1402
- const variableNames = Object.keys(variables);
2431
+ let document;
2432
+ let variableNames;
2433
+ let fields;
2434
+ let fragmentUsages;
2435
+ if (params.mode === "prebuilt") {
2436
+ document = params.prebuiltDocument;
2437
+ variableNames = params.prebuiltVariableNames ?? Object.keys(variables);
2438
+ fields = {};
2439
+ fragmentUsages = [];
2440
+ } else {
2441
+ const { fieldsFactory } = params;
2442
+ const f = createFieldFactories(schema, operationTypeName);
2443
+ const collected = withFragmentUsageCollection(() => fieldsFactory({
2444
+ f,
2445
+ $
2446
+ }));
2447
+ fields = collected.result;
2448
+ fragmentUsages = collected.usages;
2449
+ document = buildDocument({
2450
+ operationName,
2451
+ operationType,
2452
+ operationTypeName,
2453
+ variables,
2454
+ fields,
2455
+ schema
2456
+ });
2457
+ variableNames = Object.keys(variables);
2458
+ }
1403
2459
  if (!fragmentUsages.some((u) => u.metadataBuilder) && !metadataBuilder && !adapterTransformDocument && !operationTransformDocument) return {
1404
2460
  operationType,
1405
2461
  operationName,
@@ -1463,6 +2519,34 @@ const buildOperationArtifact = (params) => {
1463
2519
  if (isPromiseLike(operationMetadataResult)) return operationMetadataResult.then((metadata) => createArtifact({ metadata }));
1464
2520
  return createArtifact({ metadata: operationMetadataResult });
1465
2521
  };
2522
+ /**
2523
+ * Wraps a buildOperationArtifact call into an Operation.create() invocation,
2524
+ * handling both sync and async artifact results.
2525
+ *
2526
+ * @param artifactFactory - Factory that produces the artifact (may return Promise for async metadata)
2527
+ * @param overrideDocumentSource - When true, overrides documentSource to return empty object.
2528
+ * Required for pre-built document mode where fields = {} and the real documentSource is meaningless.
2529
+ * Must be false for fieldsFactory mode to preserve real field selections.
2530
+ *
2531
+ * @internal Used by extend.ts and operation-tagged-template.ts
2532
+ */
2533
+ const wrapArtifactAsOperation = (artifactFactory, overrideDocumentSource) => {
2534
+ return Operation.create((() => {
2535
+ const artifact = artifactFactory();
2536
+ if (overrideDocumentSource) {
2537
+ if (isPromiseLike(artifact)) return artifact.then((a) => ({
2538
+ ...a,
2539
+ documentSource: () => ({})
2540
+ }));
2541
+ return {
2542
+ ...artifact,
2543
+ documentSource: () => ({})
2544
+ };
2545
+ }
2546
+ if (isPromiseLike(artifact)) return artifact;
2547
+ return artifact;
2548
+ }));
2549
+ };
1466
2550
 
1467
2551
  //#endregion
1468
2552
  //#region packages/core/src/composer/extend.ts
@@ -1486,9 +2570,12 @@ const buildOperationArtifact = (params) => {
1486
2570
  const createExtendComposer = (schema, adapter, transformDocument) => {
1487
2571
  const resolvedAdapter = adapter ?? defaultMetadataAdapter;
1488
2572
  return (compat, options) => {
1489
- const { operationType, operationName, variables, fieldsBuilder } = compat.value;
2573
+ const spec = compat.value;
2574
+ if (isTemplateCompatSpec(spec)) return buildOperationFromTemplateSpec(schema, spec, resolvedAdapter, options, transformDocument);
2575
+ const { operationType, operationName, variables, fieldsBuilder } = spec;
1490
2576
  const operationTypeName = schema.operations[operationType];
1491
2577
  return Operation.create((() => buildOperationArtifact({
2578
+ mode: "fieldsFactory",
1492
2579
  schema,
1493
2580
  operationType,
1494
2581
  operationTypeName,
@@ -1502,52 +2589,298 @@ const createExtendComposer = (schema, adapter, transformDocument) => {
1502
2589
  })));
1503
2590
  };
1504
2591
  };
2592
+ /**
2593
+ * Builds an Operation from a TemplateCompatSpec by parsing the raw GraphQL source.
2594
+ * Delegates to buildOperationArtifact with pre-built document mode.
2595
+ */
2596
+ const buildOperationFromTemplateSpec = (schema, spec, adapter, options, adapterTransformDocument) => {
2597
+ const { operationType, operationName, graphqlSource } = spec;
2598
+ const document = parse(graphqlSource);
2599
+ const opDef = document.definitions.find((d) => d.kind === Kind.OPERATION_DEFINITION);
2600
+ if (!opDef) throw new Error("No operation definition found in compat template spec");
2601
+ const schemaIndex = createSchemaIndexFromSchema(spec.schema);
2602
+ const varSpecifiers = buildVarSpecifiers(opDef.variableDefinitions ?? [], schemaIndex);
2603
+ const operationTypeName = schema.operations[operationType];
2604
+ return wrapArtifactAsOperation(() => buildOperationArtifact({
2605
+ mode: "prebuilt",
2606
+ schema,
2607
+ operationType,
2608
+ operationTypeName,
2609
+ operationName,
2610
+ variables: varSpecifiers,
2611
+ prebuiltDocument: document,
2612
+ prebuiltVariableNames: Object.keys(varSpecifiers),
2613
+ adapter,
2614
+ metadata: options?.metadata,
2615
+ transformDocument: options?.transformDocument,
2616
+ adapterTransformDocument
2617
+ }), true);
2618
+ };
1505
2619
 
1506
2620
  //#endregion
1507
- //#region packages/core/src/composer/fragment.ts
2621
+ //#region packages/core/src/composer/merge-variable-definitions.ts
1508
2622
  /**
1509
- * Fragment composer factory for creating reusable field selections.
2623
+ * Shared utility for merging variable definitions from interpolated fragments.
2624
+ * Used by both fragment and operation tagged template implementations.
1510
2625
  * @module
1511
2626
  */
1512
2627
  /**
1513
- * Creates fragment builder functions for all object types in the schema.
1514
- *
1515
- * Returns an object with a builder for each type (e.g., `fragment.User`, `fragment.Post`).
1516
- * Each builder creates a `Fragment` that can be spread into operations.
1517
- *
1518
- * @param schema - The GraphQL schema definition
1519
- * @param _adapter - Optional metadata adapter (for fragment metadata)
1520
- * @returns Object mapping type names to fragment builder functions
1521
- *
1522
- * @internal Used by `createGqlElementComposer`
2628
+ * Merge variable definitions from interpolated fragments into the parent's variable definitions.
2629
+ * Deduplicates variables with matching names and types, throws on conflicting types.
1523
2630
  */
1524
- const createGqlFragmentComposers = (schema, _adapter) => {
1525
- const createFragmentComposer = (typename) => {
1526
- return (options) => {
1527
- const varDefinitions = options.variables ?? {};
1528
- const { key, metadata, fields } = options;
1529
- return Fragment.create(() => ({
1530
- typename,
1531
- key,
1532
- schemaLabel: schema.label,
1533
- variableDefinitions: varDefinitions,
1534
- spread: (variables) => {
1535
- const f = createFieldFactories(schema, typename);
1536
- const $ = createVarAssignments(varDefinitions, variables);
1537
- recordFragmentUsage({
1538
- metadataBuilder: metadata ? () => metadata({ $ }) : null,
1539
- path: getCurrentFieldPath()
1540
- });
1541
- return fields({
1542
- f,
1543
- $
2631
+ function mergeVariableDefinitions(parentVars, interpolationMap) {
2632
+ const merged = { ...parentVars };
2633
+ for (const value of interpolationMap.values()) if (value instanceof Fragment) {
2634
+ const childVars = value.variableDefinitions;
2635
+ for (const [varName, varDef] of Object.entries(childVars)) if (varName in merged) {
2636
+ const existing = merged[varName];
2637
+ if (existing?.kind !== varDef.kind || existing?.name !== varDef.name || existing?.modifier !== varDef.modifier) throw new Error(`Variable definition conflict: $${varName} is defined with incompatible types (${existing?.kind}:${existing?.name}:${existing?.modifier} vs ${varDef.kind}:${varDef.name}:${varDef.modifier})`);
2638
+ } else merged[varName] = varDef;
2639
+ }
2640
+ return merged;
2641
+ }
2642
+
2643
+ //#endregion
2644
+ //#region packages/core/src/composer/fragment-tagged-template.ts
2645
+ /**
2646
+ * Fragment tagged template function for creating GraphQL fragments from template literals.
2647
+ * Supports Fragment Arguments RFC syntax for parameterized fragments.
2648
+ * @module
2649
+ */
2650
+ /**
2651
+ * Extract the argument list text from a fragment definition with Fragment Arguments syntax.
2652
+ * Returns the text between parens in `fragment Name(...) on Type`, or null if no args.
2653
+ */
2654
+ function extractFragmentArgText(rawSource) {
2655
+ const match = /\bfragment\s+\w+\s*\(/.exec(rawSource);
2656
+ if (!match) return null;
2657
+ const openIndex = match.index + match[0].length - 1;
2658
+ const closeIndex = findMatchingParen(rawSource, openIndex);
2659
+ if (closeIndex === -1) return null;
2660
+ if (rawSource.slice(closeIndex + 1).trimStart().startsWith("on")) return rawSource.slice(openIndex + 1, closeIndex);
2661
+ return null;
2662
+ }
2663
+ /**
2664
+ * Extract variable definitions from Fragment Arguments syntax.
2665
+ * Wraps the argument list in a synthetic query to parse with graphql-js.
2666
+ */
2667
+ function extractFragmentVariables(rawSource, schemaIndex) {
2668
+ const argText = extractFragmentArgText(rawSource);
2669
+ if (!argText?.trim()) return {};
2670
+ const syntheticQuery = `query _Synthetic(${argText}) { __typename }`;
2671
+ let syntheticDoc;
2672
+ try {
2673
+ syntheticDoc = parse(syntheticQuery);
2674
+ } catch (error) {
2675
+ const message = error instanceof Error ? error.message : String(error);
2676
+ throw new Error(`Failed to parse fragment argument definitions: ${message}`);
2677
+ }
2678
+ const opDef = syntheticDoc.definitions[0];
2679
+ if (!opDef || opDef.kind !== Kind.OPERATION_DEFINITION) return {};
2680
+ return buildVarSpecifiers(opDef.variableDefinitions ?? [], schemaIndex);
2681
+ }
2682
+ /**
2683
+ * Builds field selections from a GraphQL AST SelectionSet by driving field factories.
2684
+ * Converts parsed AST selections into the AnyFieldsExtended format that the document builder expects.
2685
+ * Also used by typegen for static field extraction from tagged templates.
2686
+ */
2687
+ function buildFieldsFromSelectionSet(selectionSet, schema, typeName, varAssignments, interpolationMap) {
2688
+ const f = createFieldFactories(schema, typeName);
2689
+ const result = {};
2690
+ for (const selection of selectionSet.selections) if (selection.kind === Kind.FIELD) {
2691
+ const fieldName = selection.name.value;
2692
+ const alias = selection.alias?.value ?? fieldName;
2693
+ if (fieldName === "__typename") {
2694
+ result[alias] = true;
2695
+ continue;
2696
+ }
2697
+ const factory = f[fieldName];
2698
+ if (!factory) throw new Error(`Field "${fieldName}" is not defined on type "${typeName}"`);
2699
+ const args = buildArgsFromASTArguments(selection.arguments ?? [], varAssignments);
2700
+ const extras = alias !== fieldName ? { alias } : void 0;
2701
+ if (selection.selectionSet) {
2702
+ const curried = factory(args, extras);
2703
+ if (typeof curried === "function") {
2704
+ const fieldSpec = schema.object[typeName]?.fields[fieldName];
2705
+ const parsedType = parseOutputField(fieldSpec);
2706
+ if (parsedType.kind === "union") {
2707
+ const unionInput = {};
2708
+ let hasTypename = false;
2709
+ const unsupportedSelections = [];
2710
+ for (const sel of selection.selectionSet.selections) if (sel.kind === Kind.INLINE_FRAGMENT) {
2711
+ if (sel.directives?.length) throw new Error("Directives on inline fragments are not supported in tagged templates");
2712
+ if (!sel.typeCondition) throw new Error("Inline fragments without type conditions are not supported in tagged templates");
2713
+ const memberName = sel.typeCondition.name.value;
2714
+ if (!schema.union[parsedType.name]?.types[memberName]) throw new Error(`Type "${memberName}" is not a member of union "${parsedType.name}" in tagged template inline fragment`);
2715
+ if (memberName in unionInput) throw new Error(`Duplicate inline fragment for union member "${memberName}" in tagged template. Merge selections into a single "... on ${memberName} { ... }" block.`);
2716
+ const memberFields = buildFieldsFromSelectionSet(sel.selectionSet, schema, memberName, varAssignments, interpolationMap);
2717
+ unionInput[memberName] = ({ f: _f }) => memberFields;
2718
+ } else if (sel.kind === Kind.FIELD && sel.name.value === "__typename") {
2719
+ if (sel.alias) throw new Error("Aliases on __typename in union selections are not supported in tagged templates. Use \"__typename\" without an alias.");
2720
+ if (sel.directives?.length) throw new Error(`Directives on __typename in union selections are not supported in tagged templates.`);
2721
+ hasTypename = true;
2722
+ } else {
2723
+ const desc = sel.kind === Kind.FIELD ? `Field "${sel.name.value}"` : "Fragment spread";
2724
+ unsupportedSelections.push(desc);
2725
+ }
2726
+ const hasInlineFragments = Object.keys(unionInput).length > 0;
2727
+ if (unsupportedSelections.length > 0) {
2728
+ if (hasInlineFragments) throw new Error(`${unsupportedSelections[0]} alongside inline fragments in union selection is not supported in tagged templates. Use per-member inline fragments instead.`);
2729
+ throw new Error(`Union field "${fieldName}" requires at least __typename or inline fragment syntax (... on Type { fields }) in tagged templates`);
2730
+ }
2731
+ if (!hasInlineFragments && !hasTypename) throw new Error(`Union field "${fieldName}" requires at least __typename or inline fragment syntax (... on Type { fields }) in tagged templates`);
2732
+ if (hasTypename) unionInput.__typename = true;
2733
+ const fieldResult = curried(unionInput);
2734
+ Object.assign(result, fieldResult);
2735
+ } else {
2736
+ const nestedFields = buildFieldsFromSelectionSet(selection.selectionSet, schema, resolveFieldTypeName(schema, typeName, fieldName), varAssignments, interpolationMap);
2737
+ const fieldResult = curried(({ f: nestedFactories }) => {
2738
+ return nestedFields;
1544
2739
  });
2740
+ Object.assign(result, fieldResult);
1545
2741
  }
1546
- }));
2742
+ } else Object.assign(result, curried);
2743
+ } else {
2744
+ const fieldResult = factory(args, extras);
2745
+ if (typeof fieldResult === "function") {
2746
+ const emptyResult = fieldResult(() => ({}));
2747
+ Object.assign(result, emptyResult);
2748
+ } else Object.assign(result, fieldResult);
2749
+ }
2750
+ } else if (selection.kind === Kind.FRAGMENT_SPREAD) {
2751
+ const fragmentName = selection.name.value;
2752
+ if (interpolationMap?.has(fragmentName)) {
2753
+ const interpolatedValue = interpolationMap.get(fragmentName);
2754
+ if (!interpolatedValue) throw new Error(`Interpolation placeholder "${fragmentName}" has no value`);
2755
+ let spreadFields;
2756
+ if (interpolatedValue instanceof Fragment) spreadFields = interpolatedValue.spread(varAssignments);
2757
+ else {
2758
+ if (!varAssignments) throw new Error(`Callback interpolation requires variable context`);
2759
+ spreadFields = interpolatedValue({ $: varAssignments });
2760
+ }
2761
+ Object.assign(result, spreadFields);
2762
+ } else throw new Error(`Fragment spread "...${fragmentName}" in tagged template must use interpolation syntax. Use \`...@\${fragment}\` instead of \`...FragmentName\`.`);
2763
+ } else if (selection.kind === Kind.INLINE_FRAGMENT) throw new Error("Inline fragments (... on Type) at the top level are not supported in tagged templates. Use inline fragments only inside union field selections.");
2764
+ return result;
2765
+ }
2766
+ /**
2767
+ * Build a simple args object from GraphQL AST argument nodes.
2768
+ * Extracts literal values from the AST for passing to field factories.
2769
+ */
2770
+ function buildArgsFromASTArguments(args, varAssignments) {
2771
+ if (args.length === 0) return {};
2772
+ const result = {};
2773
+ for (const arg of args) result[arg.name.value] = extractASTValue(arg.value, varAssignments);
2774
+ return result;
2775
+ }
2776
+ /**
2777
+ * Extract a runtime value from a GraphQL AST ValueNode.
2778
+ */
2779
+ function extractASTValue(node, varAssignments) {
2780
+ switch (node.kind) {
2781
+ case Kind.INT: return Number.parseInt(node.value, 10);
2782
+ case Kind.FLOAT: return Number.parseFloat(node.value);
2783
+ case Kind.STRING:
2784
+ case Kind.BOOLEAN:
2785
+ case Kind.ENUM: return node.value;
2786
+ case Kind.NULL: return null;
2787
+ case Kind.LIST: return node.values?.map((v) => extractASTValue(v, varAssignments)) ?? [];
2788
+ case Kind.OBJECT: return Object.fromEntries((node.fields ?? []).map((f) => [f.name.value, extractASTValue(f.value, varAssignments)]));
2789
+ case Kind.VARIABLE: {
2790
+ const varName = node.name.value;
2791
+ if (varAssignments && varName in varAssignments) return varAssignments[varName];
2792
+ return createVarRefFromVariable(varName);
2793
+ }
2794
+ default: return;
2795
+ }
2796
+ }
2797
+ /**
2798
+ * Resolve the output type name for a field on a given type.
2799
+ * Looks up the field's type specifier in the schema and extracts the type name.
2800
+ * Handles both string specifiers ("o|Avatar|?") and object specifiers ({ spec: "o|Avatar|?" }).
2801
+ */
2802
+ function resolveFieldTypeName(schema, typeName, fieldName) {
2803
+ const typeDef = schema.object[typeName];
2804
+ if (!typeDef) throw new Error(`Type "${typeName}" is not defined in schema objects`);
2805
+ const fieldDef = typeDef.fields[fieldName];
2806
+ if (!fieldDef) throw new Error(`Field "${fieldName}" is not defined on type "${typeName}"`);
2807
+ return (typeof fieldDef === "string" ? fieldDef : fieldDef.spec).split("|")[1] ?? typeName;
2808
+ }
2809
+ /**
2810
+ * Construct a synthetic GraphQL fragment source from JS arguments and template body.
2811
+ * Handles optional variable declarations: `($var: Type!) { fields }` or `{ fields }`.
2812
+ */
2813
+ function buildSyntheticFragmentSource(name, typeName, body) {
2814
+ const trimmed = body.trim();
2815
+ if (trimmed.startsWith("(")) {
2816
+ const closeIndex = findMatchingParen(trimmed, 0);
2817
+ if (closeIndex === -1) throw new Error("Unmatched parenthesis in fragment variable declarations");
2818
+ return `fragment ${name}${trimmed.slice(0, closeIndex + 1)} on ${typeName} ${trimmed.slice(closeIndex + 1).trim()}`;
2819
+ }
2820
+ return `fragment ${name} on ${typeName} ${trimmed}`;
2821
+ }
2822
+ /**
2823
+ * Creates a curried tagged template function for fragments.
2824
+ * New API: `fragment("name", "type")\`{ fields }\`` returns TemplateResult<AnyFragment>.
2825
+ *
2826
+ * @param schema - The GraphQL schema definition
2827
+ */
2828
+ function createFragmentTaggedTemplate(schema) {
2829
+ const schemaIndex = createSchemaIndexFromSchema(schema);
2830
+ return (fragmentName, onType) => {
2831
+ if (!(onType in schema.object)) throw new Error(`Type "${onType}" is not defined in schema objects`);
2832
+ return (strings, ...values) => {
2833
+ for (let i = 0; i < values.length; i++) {
2834
+ const value = values[i];
2835
+ if (!(value instanceof Fragment) && typeof value !== "function") throw new Error(`Tagged templates only accept Fragment instances or callback functions as interpolated values. Received ${typeof value} at position ${i}.`);
2836
+ }
2837
+ let body = strings[0] ?? "";
2838
+ const interpolationMap = /* @__PURE__ */ new Map();
2839
+ for (let i = 0; i < values.length; i++) {
2840
+ const placeholderName = `__INTERPOLATION_${i}__`;
2841
+ interpolationMap.set(placeholderName, values[i]);
2842
+ body += placeholderName + (strings[i + 1] ?? "");
2843
+ }
2844
+ const rawSource = buildSyntheticFragmentSource(fragmentName, onType, body);
2845
+ let varSpecifiers = extractFragmentVariables(rawSource, schemaIndex);
2846
+ varSpecifiers = mergeVariableDefinitions(varSpecifiers, interpolationMap);
2847
+ const { preprocessed } = preprocessFragmentArgs(rawSource);
2848
+ let document;
2849
+ try {
2850
+ document = parse(preprocessed);
2851
+ } catch (error) {
2852
+ const message = error instanceof Error ? error.message : String(error);
2853
+ throw new Error(`GraphQL parse error in tagged template: ${message}`);
2854
+ }
2855
+ const fragmentDefs = document.definitions.filter((def) => def.kind === Kind.FRAGMENT_DEFINITION);
2856
+ if (fragmentDefs.length !== 1) throw new Error(`Internal error: expected exactly one fragment definition in synthesized source`);
2857
+ const fragNode = fragmentDefs[0];
2858
+ if (fragNode.kind !== Kind.FRAGMENT_DEFINITION) throw new Error("Unexpected definition kind");
2859
+ return (options) => {
2860
+ return Fragment.create(() => ({
2861
+ typename: onType,
2862
+ key: fragmentName,
2863
+ schemaLabel: schema.label,
2864
+ variableDefinitions: varSpecifiers,
2865
+ spread: (variables) => {
2866
+ const $ = createVarAssignments(varSpecifiers, variables);
2867
+ let metadataBuilder = null;
2868
+ if (options?.metadata !== void 0) {
2869
+ const metadata = options.metadata;
2870
+ if (typeof metadata === "function") metadataBuilder = () => metadata({ $ });
2871
+ else metadataBuilder = () => metadata;
2872
+ }
2873
+ recordFragmentUsage({
2874
+ metadataBuilder,
2875
+ path: null
2876
+ });
2877
+ return buildFieldsFromSelectionSet(fragNode.selectionSet, schema, onType, $, interpolationMap);
2878
+ }
2879
+ }));
2880
+ };
1547
2881
  };
1548
2882
  };
1549
- return mapValues(schema.object, (_, typename) => createFragmentComposer(typename));
1550
- };
2883
+ }
1551
2884
 
1552
2885
  //#endregion
1553
2886
  //#region packages/core/src/composer/operation.ts
@@ -1578,6 +2911,7 @@ const createOperationComposerFactory = (schema, adapter, transformDocument) => {
1578
2911
  if (operationTypeName === null) throw new Error(`Operation type ${operationType} is not defined in schema roots`);
1579
2912
  return (options) => {
1580
2913
  return Operation.create((() => buildOperationArtifact({
2914
+ mode: "fieldsFactory",
1581
2915
  schema,
1582
2916
  operationType,
1583
2917
  operationTypeName,
@@ -1593,6 +2927,106 @@ const createOperationComposerFactory = (schema, adapter, transformDocument) => {
1593
2927
  };
1594
2928
  };
1595
2929
 
2930
+ //#endregion
2931
+ //#region packages/core/src/composer/operation-tagged-template.ts
2932
+ /**
2933
+ * Operation tagged template function for creating GraphQL operations from template literals.
2934
+ * @module
2935
+ */
2936
+ /**
2937
+ * Construct a synthetic GraphQL operation source from JS arguments and template body.
2938
+ * Handles optional variable declarations: `($var: Type!) { fields }` or `{ fields }`.
2939
+ */
2940
+ function buildSyntheticOperationSource(operationType, operationName, body) {
2941
+ return `${operationType} ${operationName} ${body.trim()}`;
2942
+ }
2943
+ /**
2944
+ * Resolves a metadata option from OperationTemplateMetadataOptions into a MetadataBuilder
2945
+ * compatible with buildOperationArtifact.
2946
+ *
2947
+ * - `undefined` → `undefined` (no metadata)
2948
+ * - Raw value → `() => value` (static metadata)
2949
+ * - Callback → forwarded directly (receives full pipeline context)
2950
+ */
2951
+ const resolveMetadataOption = (metadataOption) => {
2952
+ if (metadataOption === void 0) return void 0;
2953
+ if (typeof metadataOption === "function") return metadataOption;
2954
+ return () => metadataOption;
2955
+ };
2956
+ /**
2957
+ * Creates a curried tagged template function for a specific operation type.
2958
+ * New API: `query("name")\`($var: Type!) { fields }\`` returns TemplateResult<Operation>.
2959
+ *
2960
+ * @param schema - The GraphQL schema definition
2961
+ * @param operationType - The operation type (query, mutation, subscription)
2962
+ * @param metadataAdapter - Optional metadata adapter for metadata aggregation
2963
+ * @param adapterTransformDocument - Optional document transformer from adapter
2964
+ */
2965
+ const createOperationTaggedTemplate = (schema, operationType, metadataAdapter, adapterTransformDocument) => {
2966
+ const schemaIndex = createSchemaIndexFromSchema(schema);
2967
+ return (operationName) => {
2968
+ return (strings, ...values) => {
2969
+ for (let i = 0; i < values.length; i++) {
2970
+ const value = values[i];
2971
+ if (!(value instanceof Fragment) && typeof value !== "function") throw new Error(`Tagged templates only accept Fragment instances or callback functions as interpolated values. Received ${typeof value} at position ${i}.`);
2972
+ }
2973
+ let body = strings[0] ?? "";
2974
+ const interpolationMap = /* @__PURE__ */ new Map();
2975
+ for (let i = 0; i < values.length; i++) {
2976
+ const placeholderName = `__INTERPOLATION_${i}__`;
2977
+ interpolationMap.set(placeholderName, values[i]);
2978
+ body += placeholderName + (strings[i + 1] ?? "");
2979
+ }
2980
+ const source = buildSyntheticOperationSource(operationType, operationName, body);
2981
+ let document;
2982
+ try {
2983
+ document = parse(source);
2984
+ } catch (error) {
2985
+ const message = error instanceof Error ? error.message : String(error);
2986
+ throw new Error(`GraphQL parse error in tagged template: ${message}`);
2987
+ }
2988
+ const opDefs = document.definitions.filter((def) => def.kind === Kind.OPERATION_DEFINITION);
2989
+ if (opDefs.length !== 1) throw new Error(`Internal error: expected exactly one operation definition in synthesized source`);
2990
+ const opNode = opDefs[0];
2991
+ if (opNode.kind !== Kind.OPERATION_DEFINITION) throw new Error("Unexpected definition kind");
2992
+ let varSpecifiers = buildVarSpecifiers(opNode.variableDefinitions ?? [], schemaIndex);
2993
+ varSpecifiers = mergeVariableDefinitions(varSpecifiers, interpolationMap);
2994
+ const operationTypeName = schema.operations[operationType];
2995
+ const resolvedAdapter = metadataAdapter ?? defaultMetadataAdapter;
2996
+ return (options) => {
2997
+ const resolvedMetadata = resolveMetadataOption(options?.metadata);
2998
+ if (interpolationMap.size === 0) return wrapArtifactAsOperation(() => buildOperationArtifact({
2999
+ mode: "prebuilt",
3000
+ schema,
3001
+ operationType,
3002
+ operationTypeName,
3003
+ operationName,
3004
+ variables: varSpecifiers,
3005
+ prebuiltDocument: document,
3006
+ prebuiltVariableNames: Object.keys(varSpecifiers),
3007
+ adapter: resolvedAdapter,
3008
+ metadata: resolvedMetadata,
3009
+ adapterTransformDocument
3010
+ }), true);
3011
+ return wrapArtifactAsOperation(() => buildOperationArtifact({
3012
+ mode: "fieldsFactory",
3013
+ schema,
3014
+ operationType,
3015
+ operationTypeName,
3016
+ operationName,
3017
+ variables: varSpecifiers,
3018
+ fieldsFactory: ({ $ }) => {
3019
+ return buildFieldsFromSelectionSet(opNode.selectionSet, schema, operationTypeName, $, interpolationMap);
3020
+ },
3021
+ adapter: resolvedAdapter,
3022
+ metadata: resolvedMetadata,
3023
+ adapterTransformDocument
3024
+ }), false);
3025
+ };
3026
+ };
3027
+ };
3028
+ };
3029
+
1596
3030
  //#endregion
1597
3031
  //#region packages/core/src/composer/var-ref-tools.ts
1598
3032
  /**
@@ -1811,7 +3245,7 @@ const createVarBuilder = (inputTypeMethods) => {
1811
3245
  *
1812
3246
  * This is the main entry point for defining GraphQL operations and fragments.
1813
3247
  * The returned function provides a context with:
1814
- * - `fragment`: Builders for each object type
3248
+ * - `fragment`: Tagged template function for fragment definitions
1815
3249
  * - `query/mutation/subscription`: Operation builders
1816
3250
  * - `$var`: Variable definition helpers
1817
3251
  * - `$dir`: Field directive helpers (@skip, @include)
@@ -1844,22 +3278,22 @@ const createGqlElementComposer = (schema, options) => {
1844
3278
  const helpers = adapter?.helpers;
1845
3279
  const metadataAdapter = adapter?.metadata;
1846
3280
  const transformDocument = adapter?.transformDocument;
1847
- const fragment = createGqlFragmentComposers(schema, metadataAdapter);
3281
+ const fragment = createFragmentTaggedTemplate(schema);
1848
3282
  const createOperationComposer = createOperationComposerFactory(schema, metadataAdapter, transformDocument);
1849
3283
  const transformedContext = applyContextTransformer({
1850
3284
  fragment,
1851
- query: {
3285
+ query: Object.assign(createOperationTaggedTemplate(schema, "query", metadataAdapter, transformDocument), {
1852
3286
  operation: createOperationComposer("query"),
1853
- compat: createCompatComposer(schema, "query")
1854
- },
1855
- mutation: {
3287
+ compat: createCompatTaggedTemplate(schema, "query")
3288
+ }),
3289
+ mutation: Object.assign(createOperationTaggedTemplate(schema, "mutation", metadataAdapter, transformDocument), {
1856
3290
  operation: createOperationComposer("mutation"),
1857
- compat: createCompatComposer(schema, "mutation")
1858
- },
1859
- subscription: {
3291
+ compat: createCompatTaggedTemplate(schema, "mutation")
3292
+ }),
3293
+ subscription: Object.assign(createOperationTaggedTemplate(schema, "subscription", metadataAdapter, transformDocument), {
1860
3294
  operation: createOperationComposer("subscription"),
1861
- compat: createCompatComposer(schema, "subscription")
1862
- },
3295
+ compat: createCompatTaggedTemplate(schema, "subscription")
3296
+ }),
1863
3297
  define: (factory) => GqlDefine.create(factory),
1864
3298
  extend: createExtendComposer(schema, metadataAdapter, transformDocument),
1865
3299
  $var: createVarBuilder(inputTypeMethods),
@@ -2202,5 +3636,5 @@ const generateInputTypeFromVarDefs = (schema, varDefs, options = {}) => {
2202
3636
  };
2203
3637
 
2204
3638
  //#endregion
2205
- export { Fragment, GqlDefine, GqlElement, Operation, VarRef, appendToPath, applyTypeModifier, buildArgumentValue, buildConstValueNode, buildDocument, buildOperationArtifact, buildOperationTypeNode, buildWithTypeModifier, calculateFieldType, calculateFieldsType, createColocateHelper, createCompatComposer, createDefaultAdapter, createDirectiveBuilder, createDirectiveMethod, createExtendComposer, createFieldFactories, createGqlElementComposer, createGqlFragmentComposers, createOperationComposerFactory, createStandardDirectives, createTypedDirectiveMethod, createVarAssignments, createVarBuilder, createVarMethod, createVarMethodFactory, createVarRefFromVariable, createVarRefs, defaultMetadataAdapter, defineEnum, defineOperationRoots, defineScalar, generateInputObjectType, generateInputType, generateInputTypeFromSpecifiers, generateInputTypeFromVarDefs, getCurrentFieldPath, getEnumType, getScalarInputType, getScalarOutputType, graphqlTypeToTypeScript, isDirectiveRef, isListType, parseInputSpecifier, parseOutputSpecifier, recordFragmentUsage, withFieldPath, withFragmentUsageCollection };
3639
+ export { Fragment, GqlDefine, GqlElement, Operation, VarRef, appendToPath, applyTypeModifier, buildArgumentValue, buildConstValueNode, buildDocument, buildFieldsFromSelectionSet, buildOperationArtifact, buildOperationTypeNode, buildVarSpecifier, buildVarSpecifiers, buildWithTypeModifier, calculateFieldType, calculateFieldsType, collectVariableUsages, createColocateHelper, createCompatComposer, createCompatTaggedTemplate, createDefaultAdapter, createDirectiveBuilder, createDirectiveMethod, createExtendComposer, createFieldFactories, createFragmentTaggedTemplate, createGqlElementComposer, createOperationComposerFactory, createOperationTaggedTemplate, createSchemaIndex, createSchemaIndexFromSchema, createStandardDirectives, createTypedDirectiveMethod, createVarAssignments, createVarBuilder, createVarMethod, createVarMethodFactory, createVarRefFromVariable, createVarRefs, defaultMetadataAdapter, defineEnum, defineOperationRoots, defineScalar, err, extractFragmentVariables, generateInputObjectType, generateInputType, generateInputTypeFromSpecifiers, generateInputTypeFromVarDefs, getArgumentType, getCurrentFieldPath, getEnumType, getFieldReturnType, getInputFieldType, getScalarInputType, getScalarOutputType, graphqlTypeToTypeScript, inferVariablesFromUsages, isDirectiveRef, isListType, isModifierAssignable, isTemplateCompatSpec, mergeModifiers, mergeVariableUsages, ok, parseGraphqlSource, parseInputSpecifier, parseOutputSpecifier, parseTypeNode, preprocessFragmentArgs, recordFragmentUsage, sortFragmentsByDependency, transformParsedGraphql, withFieldPath, withFragmentUsageCollection, wrapArtifactAsOperation };
2206
3640
  //# sourceMappingURL=index.js.map