@soda-gql/core 0.13.2 → 0.14.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.
Files changed (34) hide show
  1. package/README.md +39 -39
  2. package/dist/adapter.cjs +1 -3
  3. package/dist/adapter.cjs.map +1 -1
  4. package/dist/adapter.d.cts +3 -5
  5. package/dist/adapter.d.cts.map +1 -1
  6. package/dist/adapter.d.ts +3 -5
  7. package/dist/adapter.d.ts.map +1 -1
  8. package/dist/adapter.js +1 -3
  9. package/dist/adapter.js.map +1 -1
  10. package/dist/{index-PnGDsBZE.d.cts → index-Bdt5dpFG.d.cts} +26 -5
  11. package/dist/{index-PnGDsBZE.d.cts.map → index-Bdt5dpFG.d.cts.map} +1 -1
  12. package/dist/{index-8FNIWzv-.d.ts → index-CRWc3q9X.d.cts} +292 -598
  13. package/dist/index-CRWc3q9X.d.cts.map +1 -0
  14. package/dist/{index-CcqI7_ms.d.ts → index-D1T79XaT.d.ts} +26 -5
  15. package/dist/{index-CcqI7_ms.d.ts.map → index-D1T79XaT.d.ts.map} +1 -1
  16. package/dist/{index-DTUPt4z4.d.cts → index-DJ-yqsXz.d.ts} +292 -598
  17. package/dist/index-DJ-yqsXz.d.ts.map +1 -0
  18. package/dist/index.cjs +421 -493
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.cts +9 -4
  21. package/dist/index.d.cts.map +1 -1
  22. package/dist/index.d.ts +9 -4
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +415 -489
  25. package/dist/index.js.map +1 -1
  26. package/dist/runtime.d.cts +2 -2
  27. package/dist/runtime.d.ts +2 -2
  28. package/dist/{schema-builder-B0DcWTQ-.d.ts → schema-builder-DTinHI5s.d.ts} +2 -2
  29. package/dist/{schema-builder-B0DcWTQ-.d.ts.map → schema-builder-DTinHI5s.d.ts.map} +1 -1
  30. package/dist/{schema-builder-C7bceM7O.d.cts → schema-builder-DfdeJY7k.d.cts} +2 -2
  31. package/dist/{schema-builder-C7bceM7O.d.cts.map → schema-builder-DfdeJY7k.d.cts.map} +1 -1
  32. package/package.json +1 -1
  33. package/dist/index-8FNIWzv-.d.ts.map +0 -1
  34. package/dist/index-DTUPt4z4.d.cts.map +0 -1
package/dist/index.cjs CHANGED
@@ -195,7 +195,7 @@ const buildArgumentValue = (value, enumLookup) => {
195
195
  fields: Object.entries(value).map(([key, fieldValue]) => {
196
196
  let fieldTypeSpecifier = null;
197
197
  if (enumLookup.typeSpecifier?.kind === "input") {
198
- const fieldSpec = enumLookup.schema.input[enumLookup.typeSpecifier.name]?.fields[key];
198
+ const fieldSpec = (enumLookup.schema.input?.[enumLookup.typeSpecifier.name])?.fields[key];
199
199
  fieldTypeSpecifier = fieldSpec ? parseInputSpecifier(fieldSpec) : null;
200
200
  }
201
201
  const valueNode = buildArgumentValue(fieldValue, {
@@ -323,12 +323,12 @@ const isShorthand = (value) => {
323
323
  const expandShorthand = (schema, typeName, fieldName) => {
324
324
  const typeDef = schema.object[typeName];
325
325
  if (!typeDef) throw new Error(`Type "${typeName}" not found in schema`);
326
- const fieldSpec = typeDef.fields[fieldName];
327
- if (!fieldSpec) throw new Error(`Field "${fieldName}" not found on type "${typeName}"`);
326
+ const fieldDef = typeDef.fields[fieldName];
327
+ if (!fieldDef) throw new Error(`Field "${fieldName}" not found on type "${typeName}"`);
328
328
  return {
329
329
  parent: typeName,
330
330
  field: fieldName,
331
- type: fieldSpec,
331
+ type: typeof fieldDef === "string" ? fieldDef : fieldDef.spec,
332
332
  args: {},
333
333
  directives: [],
334
334
  object: null,
@@ -337,8 +337,10 @@ const expandShorthand = (schema, typeName, fieldName) => {
337
337
  };
338
338
  const buildUnionSelection = (union, schema) => {
339
339
  const { selections, __typename: hasTypenameFlag } = union;
340
- const inlineFragments = Object.entries(selections).map(([typeName, object]) => {
341
- if (!object || typeof object !== "object") return null;
340
+ const inlineFragments = Object.entries(selections).map(([typeName, member]) => {
341
+ if (!member || typeof member !== "object") return null;
342
+ const { fields, directives: memberDirectives } = member;
343
+ const builtDirectives = buildDirectives(memberDirectives, "INLINE_FRAGMENT", schema);
342
344
  return {
343
345
  kind: graphql.Kind.INLINE_FRAGMENT,
344
346
  typeCondition: {
@@ -348,9 +350,10 @@ const buildUnionSelection = (union, schema) => {
348
350
  value: typeName
349
351
  }
350
352
  },
353
+ ...builtDirectives.length > 0 ? { directives: builtDirectives } : {},
351
354
  selectionSet: {
352
355
  kind: graphql.Kind.SELECTION_SET,
353
- selections: buildField(object, schema, typeName)
356
+ selections: buildField(fields, schema, typeName)
354
357
  }
355
358
  };
356
359
  }).filter((item) => item !== null);
@@ -426,7 +429,7 @@ const buildConstValueNode = (value, enumLookup) => {
426
429
  fields: Object.entries(value).map(([key, fieldValue]) => {
427
430
  let fieldTypeSpecifier = null;
428
431
  if (enumLookup.typeSpecifier?.kind === "input") {
429
- const fieldSpec = enumLookup.schema.input[enumLookup.typeSpecifier.name]?.fields[key];
432
+ const fieldSpec = (enumLookup.schema.input?.[enumLookup.typeSpecifier.name])?.fields[key];
430
433
  fieldTypeSpecifier = fieldSpec ? parseInputSpecifier(fieldSpec) : null;
431
434
  }
432
435
  const valueNode = buildConstValueNode(fieldValue, {
@@ -470,10 +473,10 @@ const buildConstValueNode = (value, enumLookup) => {
470
473
  * followed by `[]?` or `[]!` pairs for lists.
471
474
  *
472
475
  * @example
473
- * - `"!"` `String!`
474
- * - `"?"` `String`
475
- * - `"![]!"` `[String!]!`
476
- * - `"?[]?"` `[String]`
476
+ * - `"!"` -> `String!`
477
+ * - `"?"` -> `String`
478
+ * - `"![]!"` -> `[String!]!`
479
+ * - `"?[]?"` -> `[String]`
477
480
  */
478
481
  const buildWithTypeModifier = (modifier, buildType) => {
479
482
  const baseType = buildType();
@@ -532,6 +535,10 @@ const buildWithTypeModifier = (modifier, buildType) => {
532
535
  }
533
536
  return curr.type;
534
537
  };
538
+ /**
539
+ * Builds VariableDefinitionNode[] from VarSpecifier records.
540
+ * Kept as private fallback for callers that don't provide pre-parsed nodes.
541
+ */
535
542
  const buildVariables = (variables, schema) => {
536
543
  return Object.entries(variables).map(([name, varSpec]) => {
537
544
  let defaultValue;
@@ -589,7 +596,8 @@ const buildOperationTypeNode = (operation) => {
589
596
  * @returns TypedDocumentNode with inferred input/output types
590
597
  */
591
598
  const buildDocument = (options) => {
592
- const { operationName, operationType, operationTypeName, variables, fields, schema } = options;
599
+ const { operationName, operationType, operationTypeName, variableDefinitionNodes, variables, fields, schema } = options;
600
+ const varDefs = variableDefinitionNodes ?? buildVariables(variables, schema);
593
601
  return {
594
602
  kind: graphql.Kind.DOCUMENT,
595
603
  definitions: [{
@@ -599,7 +607,7 @@ const buildDocument = (options) => {
599
607
  kind: graphql.Kind.NAME,
600
608
  value: operationName
601
609
  },
602
- variableDefinitions: buildVariables(variables, schema),
610
+ variableDefinitions: varDefs,
603
611
  selectionSet: {
604
612
  kind: graphql.Kind.SELECTION_SET,
605
613
  selections: buildField(fields, schema, operationTypeName)
@@ -620,13 +628,13 @@ const buildDocument = (options) => {
620
628
  * @example
621
629
  * ```typescript
622
630
  * // In operation definition
623
- * query.operation({
624
- * name: "GetData",
625
- * fields: ({ $ }) => $colocate({
626
- * userCard: userCardFragment.spread({ userId: $.userId }),
627
- * posts: postsFragment.spread({ userId: $.userId }),
631
+ * query("GetData")({
632
+ * variables: `($userId: ID!)`,
633
+ * fields: ({ f, $ }) => $colocate({
634
+ * userCard: { ...f("user", { id: $.userId })(() => ({ ...userCardFragment.spread() })) },
635
+ * posts: { ...f("posts", { userId: $.userId })(() => ({ ...postsFragment.spread() })) },
628
636
  * }),
629
- * });
637
+ * })({});
630
638
  *
631
639
  * // In parser definition (same labels)
632
640
  * createExecutionResultParser({
@@ -646,16 +654,6 @@ const createColocateHelper = () => {
646
654
  return $colocate;
647
655
  };
648
656
 
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
-
659
657
  //#endregion
660
658
  //#region packages/core/src/utils/promise.ts
661
659
  /**
@@ -884,7 +882,7 @@ var GqlDefine = class GqlDefine extends GqlElement {
884
882
  /**
885
883
  * Represents a reusable GraphQL field selection on a specific type.
886
884
  *
887
- * Fragments are created via `gql(({ fragment }) => fragment.TypeName({ ... }))`.
885
+ * Fragments are created via `gql(({ fragment }) => fragment("Name", "TypeName")\`{ ... }\`())`.
888
886
  * Use `spread()` to include the fragment's fields in an operation.
889
887
  *
890
888
  * @template TTypeName - The GraphQL type this fragment selects from
@@ -934,7 +932,7 @@ var Fragment = class Fragment extends GqlElement {
934
932
  /**
935
933
  * Represents a GraphQL operation (query, mutation, or subscription).
936
934
  *
937
- * Operations are created via `gql(({ query }) => query.operation({ ... }))`.
935
+ * Operations are created via `gql(({ query }) => query("Name")\`{ ... }\`())`.
938
936
  * Produces a TypedDocumentNode for type-safe execution with GraphQL clients.
939
937
  *
940
938
  * @template TOperationType - 'query' | 'mutation' | 'subscription'
@@ -989,42 +987,10 @@ var Operation = class Operation extends GqlElement {
989
987
  }
990
988
  };
991
989
 
992
- //#endregion
993
- //#region packages/core/src/composer/compat.ts
994
- /**
995
- * Compat composer factory for creating GraphQL operation specifications.
996
- * @module
997
- */
998
- /**
999
- * Creates a factory for composing compat operation specifications.
1000
- *
1001
- * Returns a function that creates a `GqlDefine<CompatSpec<...>>` storing
1002
- * the operation specification with unevaluated fieldsBuilder.
1003
- *
1004
- * @param schema - The GraphQL schema definition
1005
- * @param operationType - The operation type ('query' | 'mutation' | 'subscription')
1006
- * @returns Compat operation composer function
1007
- *
1008
- * @internal Used by `createGqlElementComposer`
1009
- */
1010
- const createCompatComposer = (schema, operationType) => {
1011
- if (schema.operations[operationType] === null) throw new Error(`Operation type ${operationType} is not defined in schema roots`);
1012
- return (options) => {
1013
- return GqlDefine.create(() => ({
1014
- schema,
1015
- operationType,
1016
- operationName: options.name,
1017
- variables: options.variables ?? {},
1018
- fieldsBuilder: options.fields
1019
- }));
1020
- };
1021
- };
1022
-
1023
990
  //#endregion
1024
991
  //#region packages/core/src/composer/compat-tagged-template.ts
1025
992
  /**
1026
993
  * Compat tagged template function for creating deferred GraphQL operation specs.
1027
- * Callback builder compat path is in compat.ts.
1028
994
  * @module
1029
995
  */
1030
996
  /**
@@ -2231,14 +2197,14 @@ const ensureCacheMapBySchema = (schema) => {
2231
2197
  return cacheMap;
2232
2198
  };
2233
2199
  /**
2234
- * Creates field selection factories for a given object type.
2200
+ * Creates a field accessor function for a given object type.
2235
2201
  *
2236
- * Returns an object with a factory for each field defined on the type.
2202
+ * Returns a function f("fieldName", args, extras) for building field selections.
2237
2203
  * Factories are cached per schema+type to avoid recreation.
2238
2204
  *
2239
2205
  * @param schema - The GraphQL schema definition
2240
2206
  * @param typeName - The object type name to create factories for
2241
- * @returns Object mapping field names to their selection factories
2207
+ * @returns FieldAccessorFunction for building field selections
2242
2208
  *
2243
2209
  * @internal Used by operation and fragment composers
2244
2210
  */
@@ -2246,70 +2212,88 @@ const createFieldFactories = (schema, typeName) => {
2246
2212
  const cacheMap = ensureCacheMapBySchema(schema);
2247
2213
  const cached = cacheMap.get(typeName);
2248
2214
  if (cached) return cached;
2249
- const factories = createFieldFactoriesInner(schema, typeName);
2250
- cacheMap.set(typeName, factories);
2251
- return factories;
2215
+ const factory = createFieldFactoriesInner(schema, typeName);
2216
+ cacheMap.set(typeName, factory);
2217
+ return factory;
2252
2218
  };
2253
2219
  const createFieldFactoriesInner = (schema, typeName) => {
2254
2220
  const typeDef = schema.object[typeName];
2255
2221
  if (!typeDef) throw new Error(`Type ${typeName} is not defined in schema objects`);
2256
- const entries = Object.entries(typeDef.fields).map(([fieldName, typeSpecifier]) => {
2222
+ return (fieldName, fieldArgs, extras) => {
2223
+ if (fieldName === "__typename") {
2224
+ const wrap$1 = (value) => require_schema_builder.wrapByKey(extras?.alias ?? fieldName, value);
2225
+ return (() => wrap$1({
2226
+ parent: typeName,
2227
+ field: fieldName,
2228
+ type: "s|String|!",
2229
+ args: {},
2230
+ directives: extras?.directives ?? [],
2231
+ object: null,
2232
+ union: null
2233
+ }));
2234
+ }
2235
+ const fieldDef = typeDef.fields[fieldName];
2236
+ if (!fieldDef) throw new Error(`Field "${fieldName}" is not defined on type "${typeName}"`);
2237
+ const typeSpecifier = typeof fieldDef === "string" ? fieldDef : fieldDef.spec;
2257
2238
  const parsedType = parseOutputField(typeSpecifier);
2258
- const factory = (fieldArgs, extras) => {
2259
- const wrap = (value) => require_schema_builder.wrapByKey(extras?.alias ?? fieldName, value);
2260
- const directives = extras?.directives ?? [];
2261
- if (parsedType.kind === "object") {
2262
- const factoryReturn = ((nest) => {
2263
- const nestedFields = withFieldPath(appendToPath(getCurrentFieldPath(), {
2264
- field: fieldName,
2265
- parentType: typeName,
2266
- isList: isListType(parsedType.modifier)
2267
- }), () => nest({ f: createFieldFactories(schema, parsedType.name) }));
2268
- return wrap({
2269
- parent: typeName,
2270
- field: fieldName,
2271
- type: typeSpecifier,
2272
- args: fieldArgs ?? {},
2273
- directives,
2274
- object: nestedFields,
2275
- union: null
2276
- });
2239
+ const wrap = (value) => require_schema_builder.wrapByKey(extras?.alias ?? fieldName, value);
2240
+ const directives = extras?.directives ?? [];
2241
+ if (parsedType.kind === "object") {
2242
+ const factoryReturn = ((nest) => {
2243
+ const nestedFields = withFieldPath(appendToPath(getCurrentFieldPath(), {
2244
+ field: fieldName,
2245
+ parentType: typeName,
2246
+ isList: isListType(parsedType.modifier)
2247
+ }), () => nest({ f: createFieldFactories(schema, parsedType.name) }));
2248
+ return wrap({
2249
+ parent: typeName,
2250
+ field: fieldName,
2251
+ type: typeSpecifier,
2252
+ args: fieldArgs ?? {},
2253
+ directives,
2254
+ object: nestedFields,
2255
+ union: null
2277
2256
  });
2278
- return factoryReturn;
2279
- }
2280
- if (parsedType.kind === "union") {
2281
- const factoryReturn = ((nest) => {
2282
- const newPath = appendToPath(getCurrentFieldPath(), {
2283
- field: fieldName,
2284
- parentType: typeName,
2285
- isList: isListType(parsedType.modifier)
2286
- });
2287
- const typenameFlag = nest.__typename;
2288
- const selections = withFieldPath(newPath, () => {
2289
- const result = {};
2290
- for (const [memberName, builder] of Object.entries(nest)) {
2291
- if (memberName === "__typename") continue;
2292
- if (typeof builder !== "function") continue;
2293
- result[memberName] = builder({ f: createFieldFactories(schema, memberName) });
2294
- }
2295
- return result;
2296
- });
2297
- return wrap({
2298
- parent: typeName,
2299
- field: fieldName,
2300
- type: typeSpecifier,
2301
- args: fieldArgs ?? {},
2302
- directives,
2303
- object: null,
2304
- union: {
2305
- selections,
2306
- __typename: typenameFlag === true
2307
- }
2308
- });
2257
+ });
2258
+ return factoryReturn;
2259
+ }
2260
+ if (parsedType.kind === "union") {
2261
+ const factoryReturn = ((nest) => {
2262
+ const newPath = appendToPath(getCurrentFieldPath(), {
2263
+ field: fieldName,
2264
+ parentType: typeName,
2265
+ isList: isListType(parsedType.modifier)
2309
2266
  });
2310
- return factoryReturn;
2311
- }
2312
- if (parsedType.kind === "scalar" || parsedType.kind === "enum") return wrap({
2267
+ const typenameFlag = nest.__typename;
2268
+ const selections = withFieldPath(newPath, () => {
2269
+ const result = {};
2270
+ for (const [memberName, builder] of Object.entries(nest)) {
2271
+ if (memberName === "__typename") continue;
2272
+ if (typeof builder === "function") result[memberName] = {
2273
+ fields: builder({ f: createFieldFactories(schema, memberName) }),
2274
+ directives: []
2275
+ };
2276
+ else if (builder && typeof builder === "object" && "fields" in builder) result[memberName] = builder;
2277
+ }
2278
+ return result;
2279
+ });
2280
+ return wrap({
2281
+ parent: typeName,
2282
+ field: fieldName,
2283
+ type: typeSpecifier,
2284
+ args: fieldArgs ?? {},
2285
+ directives,
2286
+ object: null,
2287
+ union: {
2288
+ selections,
2289
+ __typename: typenameFlag === true
2290
+ }
2291
+ });
2292
+ });
2293
+ return factoryReturn;
2294
+ }
2295
+ if (parsedType.kind === "scalar" || parsedType.kind === "enum") {
2296
+ const factoryReturn = (() => wrap({
2313
2297
  parent: typeName,
2314
2298
  field: fieldName,
2315
2299
  type: typeSpecifier,
@@ -2317,12 +2301,11 @@ const createFieldFactoriesInner = (schema, typeName) => {
2317
2301
  directives,
2318
2302
  object: null,
2319
2303
  union: null
2320
- });
2321
- throw new Error(`Unsupported field type kind: ${parsedType.kind}`);
2322
- };
2323
- return [fieldName, factory];
2324
- });
2325
- return Object.fromEntries(entries);
2304
+ }));
2305
+ return factoryReturn;
2306
+ }
2307
+ throw new Error(`Unsupported field type kind: ${parsedType.kind}`);
2308
+ };
2326
2309
  };
2327
2310
 
2328
2311
  //#endregion
@@ -2426,6 +2409,155 @@ function mergeVariableDefinitions(parentVars, interpolationMap) {
2426
2409
  return merged;
2427
2410
  }
2428
2411
 
2412
+ //#endregion
2413
+ //#region packages/core/src/composer/var-ref-tools.ts
2414
+ /**
2415
+ * Recursively checks if a NestedValue contains any VarRef.
2416
+ *
2417
+ * Used by getVarRefValue to determine if it's safe to return as ConstValue.
2418
+ * @internal
2419
+ */
2420
+ const hasVarRefInside = (value) => {
2421
+ if (value instanceof VarRef) return true;
2422
+ if (Array.isArray(value)) return value.some(hasVarRefInside);
2423
+ if (typeof value === "object" && value !== null) return Object.values(value).some(hasVarRefInside);
2424
+ return false;
2425
+ };
2426
+ /**
2427
+ * Get the variable name from a VarRef.
2428
+ * Throws if the VarRef contains a nested-value instead of a variable reference.
2429
+ */
2430
+ const getVarRefName = (varRef) => {
2431
+ const inner = VarRef.getInner(varRef);
2432
+ if (inner.type !== "variable") throw new Error("Expected variable reference, got nested-value");
2433
+ return inner.name;
2434
+ };
2435
+ /**
2436
+ * Get the const value from a VarRef.
2437
+ * Throws if the VarRef contains a variable reference instead of a nested-value,
2438
+ * or if the nested-value contains any VarRef inside.
2439
+ */
2440
+ const getVarRefValue = (varRef) => {
2441
+ const inner = VarRef.getInner(varRef);
2442
+ if (inner.type !== "nested-value") throw new Error("Expected nested-value, got variable reference");
2443
+ if (hasVarRefInside(inner.value)) throw new Error("Cannot get const value: nested-value contains VarRef");
2444
+ return inner.value;
2445
+ };
2446
+ const SelectableProxyInnerRegistry = /* @__PURE__ */ new WeakMap();
2447
+ const getSelectableProxyInner = (proxy) => {
2448
+ const inner = SelectableProxyInnerRegistry.get(proxy);
2449
+ if (!inner) throw new Error(`Proxy inner not found`);
2450
+ return inner;
2451
+ };
2452
+ const createSelectableProxy = (current) => {
2453
+ const proxy = new Proxy(Object.create(null), { get(_, property) {
2454
+ if (typeof property === "symbol") throw new Error(`Prohibited property access: ${String(property)}`);
2455
+ const nextSegments = [...current.segments, property];
2456
+ if (current.varInner.type === "virtual") return createSelectableProxy({
2457
+ varInner: current.varInner,
2458
+ segments: nextSegments
2459
+ });
2460
+ if (current.varInner.type === "variable") return createSelectableProxy({
2461
+ varInner: {
2462
+ type: "virtual",
2463
+ varName: current.varInner.name,
2464
+ varSegments: nextSegments
2465
+ },
2466
+ segments: nextSegments
2467
+ });
2468
+ if (typeof current.varInner.value === "object" && current.varInner.value !== null) {
2469
+ const value = current.varInner.value[property];
2470
+ return createSelectableProxy({
2471
+ varInner: value instanceof VarRef ? VarRef.getInner(value) : {
2472
+ type: "nested-value",
2473
+ value
2474
+ },
2475
+ segments: nextSegments
2476
+ });
2477
+ }
2478
+ throw new Error(`Cannot access children of primitive value at path [${current.segments.join(".")}]`);
2479
+ } });
2480
+ SelectableProxyInnerRegistry.set(proxy, current);
2481
+ return proxy;
2482
+ };
2483
+ /**
2484
+ * Get the variable name from a VarRef at a specific path.
2485
+ *
2486
+ * @param varRef - The VarRef containing a nested-value
2487
+ * @param selector - Path builder function, e.g., p => p.user.age
2488
+ * @returns The variable name at the specified path
2489
+ * @throws If path doesn't lead to a VarRef with type "variable"
2490
+ *
2491
+ * @example
2492
+ * const ref = createVarRefFromNestedValue({
2493
+ * user: { age: someVariableRef }
2494
+ * });
2495
+ * getNameAt(ref, p => p.user.age); // returns the variable name
2496
+ */
2497
+ const getNameAt = (varRef, selector) => {
2498
+ const inner = getSelectableProxyInner(selector(createSelectableProxy({
2499
+ varInner: VarRef.getInner(varRef),
2500
+ segments: []
2501
+ })));
2502
+ if (inner.varInner.type === "virtual") throw new Error(`Value at path [${inner.segments.join(".")}] is inside a variable`);
2503
+ if (inner.varInner.type !== "variable") throw new Error(`Value at path [${inner.segments.join(".")}] is not a variable`);
2504
+ return inner.varInner.name;
2505
+ };
2506
+ /**
2507
+ * Get the const value from a nested-value VarRef at a specific path.
2508
+ *
2509
+ * @param varRef - The VarRef containing a nested-value
2510
+ * @param pathFn - Path builder function, e.g., p => p.user.name
2511
+ * @returns The const value at the specified path
2512
+ * @throws If path leads to a VarRef or if value contains VarRef inside
2513
+ *
2514
+ * @example
2515
+ * const ref = createVarRefFromNestedValue({
2516
+ * user: { name: "Alice", age: someVariableRef }
2517
+ * });
2518
+ * getValueAt(ref, p => p.user.name); // returns "Alice"
2519
+ */
2520
+ const getValueAt = (varRef, selector) => {
2521
+ const inner = getSelectableProxyInner(selector(createSelectableProxy({
2522
+ varInner: VarRef.getInner(varRef),
2523
+ segments: []
2524
+ })));
2525
+ if (inner.varInner.type === "virtual") throw new Error(`Value at path [${inner.segments.join(".")}] is inside a variable`);
2526
+ if (inner.varInner.type !== "nested-value") throw new Error(`Value at path [${inner.segments.join(".")}] is not a nested-value`);
2527
+ if (hasVarRefInside(inner.varInner.value)) throw new Error(`Value at path [${inner.segments.join(".")}] contains nested VarRef`);
2528
+ return inner.varInner.value;
2529
+ };
2530
+ /**
2531
+ * Gets the full path to a variable within a nested structure.
2532
+ *
2533
+ * Returns path segments starting with `$variableName` followed by
2534
+ * property accesses within that variable's value.
2535
+ *
2536
+ * @example
2537
+ * ```typescript
2538
+ * getVariablePath($.filter, p => p.user.id)
2539
+ * // Returns: ["$filter", "user", "id"]
2540
+ * ```
2541
+ */
2542
+ const getVariablePath = (varRef, selector) => {
2543
+ const inner = getSelectableProxyInner(selector(createSelectableProxy({
2544
+ varInner: VarRef.getInner(varRef),
2545
+ segments: []
2546
+ })));
2547
+ if (inner.varInner.type === "virtual") return [`$${inner.varInner.varName}`, ...inner.segments.slice(inner.varInner.varSegments.length)];
2548
+ if (inner.varInner.type === "variable") return [`$${inner.varInner.name}`];
2549
+ throw new Error(`Value at path [${inner.segments.join(".")}] is not a variable or inside a variable`);
2550
+ };
2551
+ /** Pre-built tools object passed as `$var` in metadata builder callbacks. */
2552
+ const varRefTools = Object.freeze({
2553
+ getName: getVarRefName,
2554
+ getValue: getVarRefValue,
2555
+ getNameAt,
2556
+ getValueAt,
2557
+ getPath: getVariablePath,
2558
+ hasVarRefInside
2559
+ });
2560
+
2429
2561
  //#endregion
2430
2562
  //#region packages/core/src/composer/fragment-tagged-template.ts
2431
2563
  /**
@@ -2504,27 +2636,33 @@ function buildFieldsFromSelectionSet(selectionSet, schema, typeName, varAssignme
2504
2636
  result[alias] = true;
2505
2637
  continue;
2506
2638
  }
2507
- const factory = f[fieldName];
2508
- if (!factory) throw new Error(`Field "${fieldName}" is not defined on type "${typeName}"`);
2509
2639
  const args = buildArgsFromASTArguments(selection.arguments ?? [], varAssignments);
2510
- const extras = alias !== fieldName ? { alias } : void 0;
2640
+ const directives = buildDirectivesFromAST(selection.directives, varAssignments);
2641
+ const hasAlias = alias !== fieldName;
2642
+ const extras = hasAlias || directives.length > 0 ? {
2643
+ ...hasAlias ? { alias } : {},
2644
+ ...directives.length > 0 ? { directives } : {}
2645
+ } : void 0;
2511
2646
  if (selection.selectionSet) {
2512
- const curried = factory(args, extras);
2647
+ const curried = f(fieldName, args, extras);
2513
2648
  if (typeof curried === "function") {
2514
- const fieldSpec = schema.object[typeName]?.fields[fieldName];
2515
- const parsedType = parseOutputField(fieldSpec);
2649
+ const fieldDefRaw = schema.object[typeName]?.fields[fieldName];
2650
+ const parsedType = parseOutputField(typeof fieldDefRaw === "string" ? fieldDefRaw : fieldDefRaw?.spec);
2516
2651
  if (parsedType.kind === "union") {
2517
2652
  const unionInput = {};
2518
2653
  let hasTypename = false;
2519
2654
  const unsupportedSelections = [];
2520
2655
  for (const sel of selection.selectionSet.selections) if (sel.kind === graphql.Kind.INLINE_FRAGMENT) {
2521
- if (sel.directives?.length) throw new Error("Directives on inline fragments are not supported in tagged templates");
2522
2656
  if (!sel.typeCondition) throw new Error("Inline fragments without type conditions are not supported in tagged templates");
2523
2657
  const memberName = sel.typeCondition.name.value;
2524
- 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`);
2658
+ const unionDef = schema.union[parsedType.name];
2659
+ if (!unionDef) throw new Error(`Union "${parsedType.name}" is not defined in schema`);
2660
+ if (!(memberName in unionDef.types)) throw new Error(`Type "${memberName}" is not a member of union "${parsedType.name}" in tagged template inline fragment`);
2525
2661
  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.`);
2526
- const memberFields = buildFieldsFromSelectionSet(sel.selectionSet, schema, memberName, varAssignments, interpolationMap);
2527
- unionInput[memberName] = ({ f: _f }) => memberFields;
2662
+ unionInput[memberName] = {
2663
+ fields: buildFieldsFromSelectionSet(sel.selectionSet, schema, memberName, varAssignments, interpolationMap),
2664
+ directives: buildDirectivesFromAST(sel.directives, varAssignments, "INLINE_FRAGMENT")
2665
+ };
2528
2666
  } else if (sel.kind === graphql.Kind.FIELD && sel.name.value === "__typename") {
2529
2667
  if (sel.alias) throw new Error("Aliases on __typename in union selections are not supported in tagged templates. Use \"__typename\" without an alias.");
2530
2668
  if (sel.directives?.length) throw new Error(`Directives on __typename in union selections are not supported in tagged templates.`);
@@ -2551,7 +2689,7 @@ function buildFieldsFromSelectionSet(selectionSet, schema, typeName, varAssignme
2551
2689
  }
2552
2690
  } else Object.assign(result, curried);
2553
2691
  } else {
2554
- const fieldResult = factory(args, extras);
2692
+ const fieldResult = f(fieldName, args, extras);
2555
2693
  if (typeof fieldResult === "function") {
2556
2694
  const emptyResult = fieldResult(() => ({}));
2557
2695
  Object.assign(result, emptyResult);
@@ -2574,6 +2712,18 @@ function buildFieldsFromSelectionSet(selectionSet, schema, typeName, varAssignme
2574
2712
  return result;
2575
2713
  }
2576
2714
  /**
2715
+ * Convert GraphQL AST DirectiveNodes to DirectiveRef instances.
2716
+ * Reuses buildArgsFromASTArguments for directive argument conversion.
2717
+ */
2718
+ function buildDirectivesFromAST(directives, varAssignments, location = "FIELD") {
2719
+ if (!directives || directives.length === 0) return [];
2720
+ return directives.map((d) => new DirectiveRef({
2721
+ name: d.name.value,
2722
+ arguments: buildArgsFromASTArguments(d.arguments ?? [], varAssignments),
2723
+ locations: [location]
2724
+ }));
2725
+ }
2726
+ /**
2577
2727
  * Build a simple args object from GraphQL AST argument nodes.
2578
2728
  * Extracts literal values from the AST for passing to field factories.
2579
2729
  */
@@ -2677,7 +2827,10 @@ function createFragmentTaggedTemplate(schema) {
2677
2827
  let metadataBuilder = null;
2678
2828
  if (options?.metadata !== void 0) {
2679
2829
  const metadata = options.metadata;
2680
- if (typeof metadata === "function") metadataBuilder = () => metadata({ $ });
2830
+ if (typeof metadata === "function") metadataBuilder = () => metadata({
2831
+ $,
2832
+ $var: varRefTools
2833
+ });
2681
2834
  else metadataBuilder = () => metadata;
2682
2835
  }
2683
2836
  recordFragmentUsage({
@@ -2695,11 +2848,6 @@ function createFragmentTaggedTemplate(schema) {
2695
2848
  //#endregion
2696
2849
  //#region packages/core/src/composer/operation-core.ts
2697
2850
  /**
2698
- * Core operation building logic shared by operation and extend composers.
2699
- * @module
2700
- * @internal
2701
- */
2702
- /**
2703
2851
  * Builds an operation artifact from the provided parameters.
2704
2852
  *
2705
2853
  * This function contains the core logic for:
@@ -2712,10 +2860,10 @@ function createFragmentTaggedTemplate(schema) {
2712
2860
  * @param params - Operation building parameters
2713
2861
  * @returns Operation artifact or Promise of artifact (if async metadata)
2714
2862
  *
2715
- * @internal Used by operation.ts, extend.ts, and operation-tagged-template.ts
2863
+ * @internal Used by extend.ts and operation-tagged-template.ts
2716
2864
  */
2717
2865
  const buildOperationArtifact = (params) => {
2718
- const { schema, operationType, operationTypeName, operationName, variables, adapter, metadata: metadataBuilder, transformDocument: operationTransformDocument, adapterTransformDocument } = params;
2866
+ const { schema, operationType, operationTypeName, operationName, variables, variableDefinitionNodes, adapter, metadata: metadataBuilder, transformDocument: operationTransformDocument, adapterTransformDocument } = params;
2719
2867
  const $ = createVarRefs(variables);
2720
2868
  const { fieldsFactory } = params;
2721
2869
  const f = createFieldFactories(schema, operationTypeName);
@@ -2729,6 +2877,7 @@ const buildOperationArtifact = (params) => {
2729
2877
  operationName,
2730
2878
  operationType,
2731
2879
  operationTypeName,
2880
+ variableDefinitionNodes,
2732
2881
  variables,
2733
2882
  fields,
2734
2883
  schema
@@ -2756,6 +2905,7 @@ const buildOperationArtifact = (params) => {
2756
2905
  const schemaLevel = adapter.schemaLevel;
2757
2906
  return metadataBuilder?.({
2758
2907
  $,
2908
+ $var: varRefTools,
2759
2909
  document,
2760
2910
  fragmentMetadata: aggregatedFragmentMetadata,
2761
2911
  schemaLevel
@@ -2812,14 +2962,14 @@ const wrapArtifactAsOperation = (artifactFactory) => {
2812
2962
  //#endregion
2813
2963
  //#region packages/core/src/composer/extend.ts
2814
2964
  /**
2815
- * Extend composer factory for creating Operations from compat specs.
2965
+ * Extend composer factory for creating Operations from TemplateCompatSpec.
2816
2966
  * @module
2817
2967
  */
2818
2968
  /**
2819
2969
  * Creates a factory for extending compat specs into full operations.
2820
2970
  *
2821
- * The extend function takes a compat spec (created by `query.compat()`) and
2822
- * optional metadata/transformDocument options, then creates a full Operation.
2971
+ * The extend function takes a TemplateCompatSpec (created by `query.compat("Name")\`...\``)
2972
+ * and optional metadata/transformDocument options, then creates a full Operation.
2823
2973
  *
2824
2974
  * @param schema - The GraphQL schema definition
2825
2975
  * @param adapter - Optional metadata adapter for custom metadata handling
@@ -2832,21 +2982,7 @@ const createExtendComposer = (schema, adapter, transformDocument) => {
2832
2982
  const resolvedAdapter = adapter ?? defaultMetadataAdapter;
2833
2983
  return (compat, options) => {
2834
2984
  const spec = compat.value;
2835
- if (isTemplateCompatSpec(spec)) return buildOperationFromTemplateSpec(schema, spec, resolvedAdapter, options, transformDocument);
2836
- const { operationType, operationName, variables, fieldsBuilder } = spec;
2837
- const operationTypeName = schema.operations[operationType];
2838
- return Operation.create((() => buildOperationArtifact({
2839
- schema,
2840
- operationType,
2841
- operationTypeName,
2842
- operationName,
2843
- variables,
2844
- fieldsFactory: fieldsBuilder,
2845
- adapter: resolvedAdapter,
2846
- metadata: options?.metadata,
2847
- transformDocument: options?.transformDocument,
2848
- adapterTransformDocument: transformDocument
2849
- })));
2985
+ return buildOperationFromTemplateSpec(schema, spec, resolvedAdapter, options, transformDocument);
2850
2986
  };
2851
2987
  };
2852
2988
  /**
@@ -2867,6 +3003,7 @@ const buildOperationFromTemplateSpec = (schema, spec, adapter, options, adapterT
2867
3003
  operationTypeName,
2868
3004
  operationName,
2869
3005
  variables: varSpecifiers,
3006
+ variableDefinitionNodes: opDef.variableDefinitions ?? [],
2870
3007
  fieldsFactory: ({ $ }) => {
2871
3008
  return buildFieldsFromSelectionSet(filteredSelectionSet, schema, operationTypeName, $);
2872
3009
  },
@@ -2877,54 +3014,11 @@ const buildOperationFromTemplateSpec = (schema, spec, adapter, options, adapterT
2877
3014
  }));
2878
3015
  };
2879
3016
 
2880
- //#endregion
2881
- //#region packages/core/src/composer/operation.ts
2882
- /**
2883
- * Operation composer factory for creating typed GraphQL operations.
2884
- * @module
2885
- */
2886
- /**
2887
- * Creates a factory for composing GraphQL operations.
2888
- *
2889
- * Returns a curried function: first select operation type (query/mutation/subscription),
2890
- * then define the operation with name, variables, and fields.
2891
- *
2892
- * Handles metadata aggregation from fragments (sync or async) and builds
2893
- * the TypedDocumentNode automatically.
2894
- *
2895
- * @param schema - The GraphQL schema definition
2896
- * @param adapter - Optional metadata adapter for custom metadata handling
2897
- * @param transformDocument - Optional document transformer called after building
2898
- * @returns Operation type selector function
2899
- *
2900
- * @internal Used by `createGqlElementComposer`
2901
- */
2902
- const createOperationComposerFactory = (schema, adapter, transformDocument) => {
2903
- const resolvedAdapter = adapter ?? defaultMetadataAdapter;
2904
- return (operationType) => {
2905
- const operationTypeName = schema.operations[operationType];
2906
- if (operationTypeName === null) throw new Error(`Operation type ${operationType} is not defined in schema roots`);
2907
- return (options) => {
2908
- return Operation.create((() => buildOperationArtifact({
2909
- schema,
2910
- operationType,
2911
- operationTypeName,
2912
- operationName: options.name,
2913
- variables: options.variables ?? {},
2914
- fieldsFactory: options.fields,
2915
- adapter: resolvedAdapter,
2916
- metadata: options.metadata,
2917
- transformDocument: options.transformDocument,
2918
- adapterTransformDocument: transformDocument
2919
- })));
2920
- };
2921
- };
2922
- };
2923
-
2924
3017
  //#endregion
2925
3018
  //#region packages/core/src/composer/operation-tagged-template.ts
2926
3019
  /**
2927
3020
  * Operation tagged template function for creating GraphQL operations from template literals.
3021
+ * Also supports options object dispatch for callback builder path.
2928
3022
  * @module
2929
3023
  */
2930
3024
  /**
@@ -2938,9 +3032,9 @@ function buildSyntheticOperationSource(operationType, operationName, body) {
2938
3032
  * Resolves a metadata option from OperationTemplateMetadataOptions into a MetadataBuilder
2939
3033
  * compatible with buildOperationArtifact.
2940
3034
  *
2941
- * - `undefined` `undefined` (no metadata)
2942
- * - Raw value `() => value` (static metadata)
2943
- * - Callback forwarded directly (receives full pipeline context)
3035
+ * - `undefined` -> `undefined` (no metadata)
3036
+ * - Raw value -> `() => value` (static metadata)
3037
+ * - Callback -> forwarded directly (receives full pipeline context)
2944
3038
  */
2945
3039
  const resolveMetadataOption = (metadataOption) => {
2946
3040
  if (metadataOption === void 0) return void 0;
@@ -2948,8 +3042,11 @@ const resolveMetadataOption = (metadataOption) => {
2948
3042
  return () => metadataOption;
2949
3043
  };
2950
3044
  /**
2951
- * Creates a curried tagged template function for a specific operation type.
2952
- * New API: `query("name")\`($var: Type!) { fields }\`` returns TemplateResult<Operation>.
3045
+ * Creates a curried function for a specific operation type.
3046
+ * Supports both tagged template and options object dispatch.
3047
+ *
3048
+ * Tagged template: `query("name")\`($var: Type!) { fields }\`` returns TemplateResult<Operation>.
3049
+ * Options object: `query("name")({ variables, fields })` returns TemplateResult<Operation>.
2953
3050
  *
2954
3051
  * @param schema - The GraphQL schema definition
2955
3052
  * @param operationType - The operation type (query, mutation, subscription)
@@ -2959,250 +3056,99 @@ const resolveMetadataOption = (metadataOption) => {
2959
3056
  const createOperationTaggedTemplate = (schema, operationType, metadataAdapter, adapterTransformDocument) => {
2960
3057
  const schemaIndex = createSchemaIndexFromSchema(schema);
2961
3058
  return (operationName) => {
2962
- return (strings, ...values) => {
2963
- for (let i = 0; i < values.length; i++) {
2964
- const value = values[i];
2965
- 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}.`);
2966
- }
2967
- let body = strings[0] ?? "";
2968
- const interpolationMap = /* @__PURE__ */ new Map();
2969
- for (let i = 0; i < values.length; i++) {
2970
- const placeholderName = `__INTERPOLATION_${i}__`;
2971
- interpolationMap.set(placeholderName, values[i]);
2972
- body += placeholderName + (strings[i + 1] ?? "");
2973
- }
2974
- const source = buildSyntheticOperationSource(operationType, operationName, body);
2975
- let document;
2976
- try {
2977
- document = (0, graphql.parse)(source);
2978
- } catch (error) {
2979
- const message = error instanceof Error ? error.message : String(error);
2980
- throw new Error(`GraphQL parse error in tagged template: ${message}`);
2981
- }
2982
- const opDefs = document.definitions.filter((def) => def.kind === graphql.Kind.OPERATION_DEFINITION);
2983
- if (opDefs.length !== 1) throw new Error(`Internal error: expected exactly one operation definition in synthesized source`);
2984
- const opNode = opDefs[0];
2985
- if (opNode.kind !== graphql.Kind.OPERATION_DEFINITION) throw new Error("Unexpected definition kind");
2986
- let varSpecifiers = buildVarSpecifiers(opNode.variableDefinitions ?? [], schemaIndex);
2987
- varSpecifiers = mergeVariableDefinitions(varSpecifiers, interpolationMap);
2988
- const operationTypeName = schema.operations[operationType];
2989
- if (operationTypeName === null) throw new Error(`Operation type ${operationType} is not defined in schema roots`);
2990
- const resolvedAdapter = metadataAdapter ?? defaultMetadataAdapter;
2991
- return (options) => {
2992
- const resolvedMetadata = resolveMetadataOption(options?.metadata);
2993
- return wrapArtifactAsOperation(() => buildOperationArtifact({
2994
- schema,
2995
- operationType,
2996
- operationTypeName,
2997
- operationName,
2998
- variables: varSpecifiers,
2999
- fieldsFactory: ({ $ }) => {
3000
- return buildFieldsFromSelectionSet(filterUnresolvedFragmentSpreads(opNode.selectionSet, interpolationMap), schema, operationTypeName, $, interpolationMap);
3001
- },
3002
- adapter: resolvedAdapter,
3003
- metadata: resolvedMetadata,
3004
- adapterTransformDocument
3005
- }));
3006
- };
3059
+ const dispatch = (firstArg, ...rest) => {
3060
+ if ("raw" in firstArg) return handleTaggedTemplate(schema, schemaIndex, operationType, operationName, metadataAdapter, adapterTransformDocument, firstArg, rest);
3061
+ return handleOptionsObject(schema, schemaIndex, operationType, operationName, metadataAdapter, adapterTransformDocument, firstArg);
3007
3062
  };
3063
+ return dispatch;
3008
3064
  };
3009
3065
  };
3010
-
3011
- //#endregion
3012
- //#region packages/core/src/composer/var-ref-tools.ts
3013
- /**
3014
- * Recursively checks if a NestedValue contains any VarRef.
3015
- *
3016
- * Used by getVarRefValue to determine if it's safe to return as ConstValue.
3017
- * @internal
3018
- */
3019
- const hasVarRefInside = (value) => {
3020
- if (value instanceof VarRef) return true;
3021
- if (Array.isArray(value)) return value.some(hasVarRefInside);
3022
- if (typeof value === "object" && value !== null) return Object.values(value).some(hasVarRefInside);
3023
- return false;
3024
- };
3025
- /**
3026
- * Get the variable name from a VarRef.
3027
- * Throws if the VarRef contains a nested-value instead of a variable reference.
3028
- */
3029
- const getVarRefName = (varRef) => {
3030
- const inner = VarRef.getInner(varRef);
3031
- if (inner.type !== "variable") throw new Error("Expected variable reference, got nested-value");
3032
- return inner.name;
3033
- };
3034
3066
  /**
3035
- * Get the const value from a VarRef.
3036
- * Throws if the VarRef contains a variable reference instead of a nested-value,
3037
- * or if the nested-value contains any VarRef inside.
3067
+ * Handles the tagged template path.
3038
3068
  */
3039
- const getVarRefValue = (varRef) => {
3040
- const inner = VarRef.getInner(varRef);
3041
- if (inner.type !== "nested-value") throw new Error("Expected nested-value, got variable reference");
3042
- if (hasVarRefInside(inner.value)) throw new Error("Cannot get const value: nested-value contains VarRef");
3043
- return inner.value;
3044
- };
3045
- const SelectableProxyInnerRegistry = /* @__PURE__ */ new WeakMap();
3046
- const getSelectableProxyInner = (proxy) => {
3047
- const inner = SelectableProxyInnerRegistry.get(proxy);
3048
- if (!inner) throw new Error(`Proxy inner not found`);
3049
- return inner;
3050
- };
3051
- const createSelectableProxy = (current) => {
3052
- const proxy = new Proxy(Object.create(null), { get(_, property) {
3053
- if (typeof property === "symbol") throw new Error(`Prohibited property access: ${String(property)}`);
3054
- const nextSegments = [...current.segments, property];
3055
- if (current.varInner.type === "virtual") return createSelectableProxy({
3056
- varInner: current.varInner,
3057
- segments: nextSegments
3058
- });
3059
- if (current.varInner.type === "variable") return createSelectableProxy({
3060
- varInner: {
3061
- type: "virtual",
3062
- varName: current.varInner.name,
3063
- varSegments: nextSegments
3069
+ function handleTaggedTemplate(schema, schemaIndex, operationType, operationName, metadataAdapter, adapterTransformDocument, strings, values) {
3070
+ for (let i = 0; i < values.length; i++) {
3071
+ const value = values[i];
3072
+ 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}.`);
3073
+ }
3074
+ let body = strings[0] ?? "";
3075
+ const interpolationMap = /* @__PURE__ */ new Map();
3076
+ for (let i = 0; i < values.length; i++) {
3077
+ const placeholderName = `__INTERPOLATION_${i}__`;
3078
+ interpolationMap.set(placeholderName, values[i]);
3079
+ body += placeholderName + (strings[i + 1] ?? "");
3080
+ }
3081
+ const source = buildSyntheticOperationSource(operationType, operationName, body);
3082
+ let document;
3083
+ try {
3084
+ document = (0, graphql.parse)(source);
3085
+ } catch (error) {
3086
+ const message = error instanceof Error ? error.message : String(error);
3087
+ throw new Error(`GraphQL parse error in tagged template: ${message}`);
3088
+ }
3089
+ const opDefs = document.definitions.filter((def) => def.kind === graphql.Kind.OPERATION_DEFINITION);
3090
+ if (opDefs.length !== 1) throw new Error(`Internal error: expected exactly one operation definition in synthesized source`);
3091
+ const opNode = opDefs[0];
3092
+ if (opNode.kind !== graphql.Kind.OPERATION_DEFINITION) throw new Error("Unexpected definition kind");
3093
+ const varDefNodes = opNode.variableDefinitions ?? [];
3094
+ let varSpecifiers = buildVarSpecifiers(varDefNodes, schemaIndex);
3095
+ varSpecifiers = mergeVariableDefinitions(varSpecifiers, interpolationMap);
3096
+ const hasInterpolatedFragmentVars = [...interpolationMap.values()].some((v) => v instanceof Fragment && Object.keys(v.variableDefinitions).length > 0);
3097
+ const operationTypeName = schema.operations[operationType];
3098
+ if (operationTypeName === null) throw new Error(`Operation type ${operationType} is not defined in schema roots`);
3099
+ const resolvedAdapter = metadataAdapter ?? defaultMetadataAdapter;
3100
+ return (options) => {
3101
+ const resolvedMetadata = resolveMetadataOption(options?.metadata);
3102
+ return wrapArtifactAsOperation(() => buildOperationArtifact({
3103
+ schema,
3104
+ operationType,
3105
+ operationTypeName,
3106
+ operationName,
3107
+ variables: varSpecifiers,
3108
+ variableDefinitionNodes: hasInterpolatedFragmentVars ? void 0 : varDefNodes,
3109
+ fieldsFactory: ({ $ }) => {
3110
+ return buildFieldsFromSelectionSet(filterUnresolvedFragmentSpreads(opNode.selectionSet, interpolationMap), schema, operationTypeName, $, interpolationMap);
3064
3111
  },
3065
- segments: nextSegments
3066
- });
3067
- if (typeof current.varInner.value === "object" && current.varInner.value !== null) {
3068
- const value = current.varInner.value[property];
3069
- return createSelectableProxy({
3070
- varInner: value instanceof VarRef ? VarRef.getInner(value) : {
3071
- type: "nested-value",
3072
- value
3073
- },
3074
- segments: nextSegments
3075
- });
3076
- }
3077
- throw new Error(`Cannot access children of primitive value at path [${current.segments.join(".")}]`);
3078
- } });
3079
- SelectableProxyInnerRegistry.set(proxy, current);
3080
- return proxy;
3081
- };
3082
- /**
3083
- * Get the variable name from a VarRef at a specific path.
3084
- *
3085
- * @param varRef - The VarRef containing a nested-value
3086
- * @param selector - Path builder function, e.g., p => p.user.age
3087
- * @returns The variable name at the specified path
3088
- * @throws If path doesn't lead to a VarRef with type "variable"
3089
- *
3090
- * @example
3091
- * const ref = createVarRefFromNestedValue({
3092
- * user: { age: someVariableRef }
3093
- * });
3094
- * getNameAt(ref, p => p.user.age); // returns the variable name
3095
- */
3096
- const getNameAt = (varRef, selector) => {
3097
- const inner = getSelectableProxyInner(selector(createSelectableProxy({
3098
- varInner: VarRef.getInner(varRef),
3099
- segments: []
3100
- })));
3101
- if (inner.varInner.type === "virtual") throw new Error(`Value at path [${inner.segments.join(".")}] is inside a variable`);
3102
- if (inner.varInner.type !== "variable") throw new Error(`Value at path [${inner.segments.join(".")}] is not a variable`);
3103
- return inner.varInner.name;
3104
- };
3105
- /**
3106
- * Get the const value from a nested-value VarRef at a specific path.
3107
- *
3108
- * @param varRef - The VarRef containing a nested-value
3109
- * @param pathFn - Path builder function, e.g., p => p.user.name
3110
- * @returns The const value at the specified path
3111
- * @throws If path leads to a VarRef or if value contains VarRef inside
3112
- *
3113
- * @example
3114
- * const ref = createVarRefFromNestedValue({
3115
- * user: { name: "Alice", age: someVariableRef }
3116
- * });
3117
- * getValueAt(ref, p => p.user.name); // returns "Alice"
3118
- */
3119
- const getValueAt = (varRef, selector) => {
3120
- const inner = getSelectableProxyInner(selector(createSelectableProxy({
3121
- varInner: VarRef.getInner(varRef),
3122
- segments: []
3123
- })));
3124
- if (inner.varInner.type === "virtual") throw new Error(`Value at path [${inner.segments.join(".")}] is inside a variable`);
3125
- if (inner.varInner.type !== "nested-value") throw new Error(`Value at path [${inner.segments.join(".")}] is not a nested-value`);
3126
- if (hasVarRefInside(inner.varInner.value)) throw new Error(`Value at path [${inner.segments.join(".")}] contains nested VarRef`);
3127
- return inner.varInner.value;
3128
- };
3129
- /**
3130
- * Gets the full path to a variable within a nested structure.
3131
- *
3132
- * Returns path segments starting with `$variableName` followed by
3133
- * property accesses within that variable's value.
3134
- *
3135
- * @example
3136
- * ```typescript
3137
- * getVariablePath($.filter, p => p.user.id)
3138
- * // Returns: ["$filter", "user", "id"]
3139
- * ```
3140
- */
3141
- const getVariablePath = (varRef, selector) => {
3142
- const inner = getSelectableProxyInner(selector(createSelectableProxy({
3143
- varInner: VarRef.getInner(varRef),
3144
- segments: []
3145
- })));
3146
- if (inner.varInner.type === "virtual") return [`$${inner.varInner.varName}`, ...inner.segments.slice(inner.varInner.varSegments.length)];
3147
- if (inner.varInner.type === "variable") return [`$${inner.varInner.name}`];
3148
- throw new Error(`Value at path [${inner.segments.join(".")}] is not a variable or inside a variable`);
3149
- };
3150
-
3151
- //#endregion
3152
- //#region packages/core/src/composer/var-builder.ts
3153
- /**
3154
- * Creates a factory function for generating schema-scoped variable methods.
3155
- * This ensures proper type inference for nested input objects by binding the schema type upfront.
3156
- *
3157
- * @example
3158
- * ```typescript
3159
- * const createMethod = createVarMethodFactory<typeof schema>();
3160
- * const inputTypeMethods = {
3161
- * Boolean: createMethod("scalar", "Boolean"),
3162
- * user_bool_exp: createMethod("input", "user_bool_exp"),
3163
- * } satisfies InputTypeMethods<typeof schema>;
3164
- * ```
3165
- */
3166
- const createVarMethodFactory = () => {
3167
- return (kind, typeName) => {
3168
- return ((modifier, extras) => ({
3169
- kind,
3170
- name: typeName,
3171
- modifier,
3172
- defaultValue: extras?.default ? { default: extras.default() } : null,
3173
- directives: extras?.directives ?? {}
3112
+ adapter: resolvedAdapter,
3113
+ metadata: resolvedMetadata,
3114
+ transformDocument: options?.transformDocument,
3115
+ adapterTransformDocument
3174
3116
  }));
3175
3117
  };
3176
- };
3118
+ }
3177
3119
  /**
3178
- * Creates a variable builder using injected input type methods.
3179
- *
3180
- * The returned builder provides type-safe variable definition methods
3181
- * for all input types in the schema. Also includes utilities for
3182
- * extracting variable names and values from VarRefs.
3183
- *
3184
- * @param inputTypeMethods - Methods for each input type (from codegen)
3185
- * @returns Variable builder with methods for all input types
3186
- *
3187
- * @internal Used by `createGqlElementComposer`
3120
+ * Handles the options object path.
3188
3121
  */
3189
- const createVarBuilder = (inputTypeMethods) => {
3190
- const varBuilder = (varName) => {
3191
- const wrappedMethods = {};
3192
- for (const [typeName, method] of Object.entries(inputTypeMethods)) Object.defineProperty(wrappedMethods, typeName, {
3193
- value: ((modifier, extras) => require_schema_builder.wrapByKey(varName, method(modifier, extras))),
3194
- writable: false,
3195
- configurable: true
3196
- });
3197
- return wrappedMethods;
3122
+ function handleOptionsObject(schema, schemaIndex, operationType, operationName, metadataAdapter, adapterTransformDocument, options) {
3123
+ let varSpecifiers = {};
3124
+ let varDefNodes = [];
3125
+ if (options.variables) {
3126
+ const opDef = (0, graphql.parse)(`query __var_parse__ ${String(options.variables).trim()} { __typename }`).definitions[0];
3127
+ if (opDef?.kind === graphql.Kind.OPERATION_DEFINITION) {
3128
+ varDefNodes = opDef.variableDefinitions ?? [];
3129
+ varSpecifiers = buildVarSpecifiers(varDefNodes, schemaIndex);
3130
+ }
3131
+ }
3132
+ const operationTypeName = schema.operations[operationType];
3133
+ if (operationTypeName === null) throw new Error(`Operation type ${operationType} is not defined in schema roots`);
3134
+ const resolvedAdapter = metadataAdapter ?? defaultMetadataAdapter;
3135
+ return (step2Options) => {
3136
+ const resolvedMetadata = resolveMetadataOption(step2Options?.metadata);
3137
+ return wrapArtifactAsOperation(() => buildOperationArtifact({
3138
+ schema,
3139
+ operationType,
3140
+ operationTypeName,
3141
+ operationName,
3142
+ variables: varSpecifiers,
3143
+ variableDefinitionNodes: varDefNodes,
3144
+ fieldsFactory: options.fields,
3145
+ adapter: resolvedAdapter,
3146
+ metadata: resolvedMetadata,
3147
+ transformDocument: step2Options?.transformDocument,
3148
+ adapterTransformDocument
3149
+ }));
3198
3150
  };
3199
- varBuilder.getName = getVarRefName;
3200
- varBuilder.getValue = getVarRefValue;
3201
- varBuilder.getNameAt = getNameAt;
3202
- varBuilder.getValueAt = getValueAt;
3203
- varBuilder.getVariablePath = getVariablePath;
3204
- return varBuilder;
3205
- };
3151
+ }
3206
3152
 
3207
3153
  //#endregion
3208
3154
  //#region packages/core/src/composer/gql-composer.ts
@@ -3212,57 +3158,37 @@ const createVarBuilder = (inputTypeMethods) => {
3212
3158
  * This is the main entry point for defining GraphQL operations and fragments.
3213
3159
  * The returned function provides a context with:
3214
3160
  * - `fragment`: Tagged template function for fragment definitions
3215
- * - `query/mutation/subscription`: Operation builders
3216
- * - `$var`: Variable definition helpers
3161
+ * - `query/mutation/subscription`: Operation builders (tagged template + options object + .compat)
3217
3162
  * - `$dir`: Field directive helpers (@skip, @include)
3218
3163
  * - `$colocate`: Fragment colocation utilities
3219
3164
  *
3220
3165
  * @param schema - The GraphQL schema definition
3221
- * @param options - Configuration including input type methods and optional adapter
3166
+ * @param options - Configuration including optional adapter
3222
3167
  * @returns Element composer function
3223
3168
  *
3224
3169
  * @example
3225
3170
  * ```typescript
3226
- * const gql = createGqlElementComposer(schema, { inputTypeMethods });
3227
- *
3228
- * const GetUser = gql(({ query, $var, $dir }) =>
3229
- * query.operation({
3230
- * name: "GetUser",
3231
- * variables: { showEmail: $var("showEmail").Boolean("!") },
3232
- * fields: ({ f, $ }) => ({
3233
- * ...f.user({ id: "1" })(({ f }) => ({
3234
- * ...f.name(),
3235
- * ...f.email({}, { directives: [$dir.skip({ if: $.showEmail })] }),
3236
- * })),
3237
- * }),
3238
- * })
3171
+ * const gql = createGqlElementComposer(schema, {});
3172
+ *
3173
+ * const GetUser = gql(({ query, $dir }) =>
3174
+ * query("GetUser")`($id: ID!) {
3175
+ * user(id: $id) { name email }
3176
+ * }`()
3239
3177
  * );
3240
3178
  * ```
3241
3179
  */
3242
3180
  const createGqlElementComposer = (schema, options) => {
3243
- const { adapter, inputTypeMethods, directiveMethods } = options;
3181
+ const { adapter, directiveMethods } = options;
3244
3182
  const helpers = adapter?.helpers;
3245
3183
  const metadataAdapter = adapter?.metadata;
3246
3184
  const transformDocument = adapter?.transformDocument;
3247
- const fragment = createFragmentTaggedTemplate(schema);
3248
- const createOperationComposer = createOperationComposerFactory(schema, metadataAdapter, transformDocument);
3249
3185
  const transformedContext = require_context_transformer.applyContextTransformer({
3250
- fragment,
3251
- query: Object.assign(createOperationTaggedTemplate(schema, "query", metadataAdapter, transformDocument), {
3252
- operation: createOperationComposer("query"),
3253
- compat: createCompatTaggedTemplate(schema, "query")
3254
- }),
3255
- mutation: Object.assign(createOperationTaggedTemplate(schema, "mutation", metadataAdapter, transformDocument), {
3256
- operation: createOperationComposer("mutation"),
3257
- compat: createCompatTaggedTemplate(schema, "mutation")
3258
- }),
3259
- subscription: Object.assign(createOperationTaggedTemplate(schema, "subscription", metadataAdapter, transformDocument), {
3260
- operation: createOperationComposer("subscription"),
3261
- compat: createCompatTaggedTemplate(schema, "subscription")
3262
- }),
3186
+ fragment: createFragmentTaggedTemplate(schema),
3187
+ query: Object.assign(createOperationTaggedTemplate(schema, "query", metadataAdapter, transformDocument), { compat: createCompatTaggedTemplate(schema, "query") }),
3188
+ mutation: Object.assign(createOperationTaggedTemplate(schema, "mutation", metadataAdapter, transformDocument), { compat: createCompatTaggedTemplate(schema, "mutation") }),
3189
+ subscription: Object.assign(createOperationTaggedTemplate(schema, "subscription", metadataAdapter, transformDocument), { compat: createCompatTaggedTemplate(schema, "subscription") }),
3263
3190
  define: (factory) => GqlDefine.create(factory),
3264
3191
  extend: createExtendComposer(schema, metadataAdapter, transformDocument),
3265
- $var: createVarBuilder(inputTypeMethods),
3266
3192
  $dir: directiveMethods ?? createStandardDirectives(),
3267
3193
  $colocate: createColocateHelper(),
3268
3194
  ...helpers ?? {}
@@ -3433,15 +3359,15 @@ const calculateUnionType = (schema, union, formatters, unionTypeName) => {
3433
3359
  if (unionDef) {
3434
3360
  const allMemberNames = Object.keys(unionDef.types);
3435
3361
  for (const typeName of allMemberNames) {
3436
- const fields = selections[typeName];
3437
- if (fields && typeof fields === "object") {
3438
- const fieldsType = calculateFieldsType(schema, fields, formatters, typeName);
3362
+ const member = selections[typeName];
3363
+ if (member && typeof member === "object") {
3364
+ const fieldsType = calculateFieldsType(schema, member.fields, formatters, typeName);
3439
3365
  memberTypes.push(`${fieldsType} & { readonly __typename: "${typeName}" }`);
3440
3366
  } else memberTypes.push(`{ readonly __typename: "${typeName}" }`);
3441
3367
  }
3442
3368
  }
3443
- } else for (const [typeName, fields] of Object.entries(selections)) if (fields && typeof fields === "object") {
3444
- const memberType = calculateFieldsType(schema, fields, formatters, typeName);
3369
+ } else for (const [typeName, member] of Object.entries(selections)) if (member && typeof member === "object") {
3370
+ const memberType = calculateFieldsType(schema, member.fields, formatters, typeName);
3445
3371
  memberTypes.push(memberType);
3446
3372
  }
3447
3373
  if (memberTypes.length === 0) return "never";
@@ -3583,7 +3509,7 @@ const generateInputTypeFromSpecifiers = (schema, specifiers, options = {}) => {
3583
3509
  * Generate a TypeScript type string for input variables from VariableDefinitions.
3584
3510
  *
3585
3511
  * Unlike generateInputTypeFromSpecifiers which works with deferred specifier strings,
3586
- * this function works with VarSpecifier objects created by $var().
3512
+ * this function works with VarSpecifier objects from variable definitions.
3587
3513
  * Used for generating Fragment input types in prebuilt mode.
3588
3514
  *
3589
3515
  * @param schema - The GraphQL schema
@@ -3622,7 +3548,6 @@ exports.calculateFieldType = calculateFieldType;
3622
3548
  exports.calculateFieldsType = calculateFieldsType;
3623
3549
  exports.collectVariableUsages = collectVariableUsages;
3624
3550
  exports.createColocateHelper = createColocateHelper;
3625
- exports.createCompatComposer = createCompatComposer;
3626
3551
  exports.createCompatTaggedTemplate = createCompatTaggedTemplate;
3627
3552
  exports.createDefaultAdapter = createDefaultAdapter;
3628
3553
  exports.createDirectiveBuilder = createDirectiveBuilder;
@@ -3631,15 +3556,12 @@ exports.createExtendComposer = createExtendComposer;
3631
3556
  exports.createFieldFactories = createFieldFactories;
3632
3557
  exports.createFragmentTaggedTemplate = createFragmentTaggedTemplate;
3633
3558
  exports.createGqlElementComposer = createGqlElementComposer;
3634
- exports.createOperationComposerFactory = createOperationComposerFactory;
3635
3559
  exports.createOperationTaggedTemplate = createOperationTaggedTemplate;
3636
3560
  exports.createSchemaIndex = createSchemaIndex;
3637
3561
  exports.createSchemaIndexFromSchema = createSchemaIndexFromSchema;
3638
3562
  exports.createStandardDirectives = createStandardDirectives;
3639
3563
  exports.createTypedDirectiveMethod = createTypedDirectiveMethod;
3640
3564
  exports.createVarAssignments = createVarAssignments;
3641
- exports.createVarBuilder = createVarBuilder;
3642
- exports.createVarMethodFactory = createVarMethodFactory;
3643
3565
  exports.createVarRefFromVariable = createVarRefFromVariable;
3644
3566
  exports.createVarRefs = createVarRefs;
3645
3567
  exports.defaultMetadataAdapter = defaultMetadataAdapter;
@@ -3658,14 +3580,19 @@ exports.getCurrentFieldPath = getCurrentFieldPath;
3658
3580
  exports.getEnumType = getEnumType;
3659
3581
  exports.getFieldReturnType = getFieldReturnType;
3660
3582
  exports.getInputFieldType = getInputFieldType;
3583
+ exports.getNameAt = getNameAt;
3661
3584
  exports.getScalarInputType = getScalarInputType;
3662
3585
  exports.getScalarOutputType = getScalarOutputType;
3586
+ exports.getValueAt = getValueAt;
3587
+ exports.getVarRefName = getVarRefName;
3588
+ exports.getVarRefValue = getVarRefValue;
3589
+ exports.getVariablePath = getVariablePath;
3663
3590
  exports.graphqlTypeToTypeScript = graphqlTypeToTypeScript;
3591
+ exports.hasVarRefInside = hasVarRefInside;
3664
3592
  exports.inferVariablesFromUsages = inferVariablesFromUsages;
3665
3593
  exports.isDirectiveRef = isDirectiveRef;
3666
3594
  exports.isListType = isListType;
3667
3595
  exports.isModifierAssignable = isModifierAssignable;
3668
- exports.isTemplateCompatSpec = isTemplateCompatSpec;
3669
3596
  exports.mergeModifiers = mergeModifiers;
3670
3597
  exports.mergeVariableUsages = mergeVariableUsages;
3671
3598
  exports.ok = ok;
@@ -3677,6 +3604,7 @@ exports.preprocessFragmentArgs = preprocessFragmentArgs;
3677
3604
  exports.recordFragmentUsage = recordFragmentUsage;
3678
3605
  exports.sortFragmentsByDependency = sortFragmentsByDependency;
3679
3606
  exports.transformParsedGraphql = transformParsedGraphql;
3607
+ exports.varRefTools = varRefTools;
3680
3608
  exports.withFieldPath = withFieldPath;
3681
3609
  exports.withFragmentUsageCollection = withFragmentUsageCollection;
3682
3610
  exports.wrapArtifactAsOperation = wrapArtifactAsOperation;