@soda-gql/core 0.8.2 → 0.9.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.cjs CHANGED
@@ -1,6 +1,37 @@
1
1
  const require_schema = require('./schema-D2MW4DOF.cjs');
2
2
  let graphql = require("graphql");
3
3
 
4
+ //#region packages/core/src/types/type-foundation/directive-ref.ts
5
+ /**
6
+ * A reference to a directive that can be applied to fields.
7
+ *
8
+ * DirectiveRef carries type information about the directive via the TBrand
9
+ * type parameter, but this information is only used for type inference,
10
+ * not for runtime validation.
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * const skipDirective = new DirectiveRef({
15
+ * name: "skip",
16
+ * arguments: { if: true },
17
+ * locations: ["FIELD", "FRAGMENT_SPREAD", "INLINE_FRAGMENT"],
18
+ * });
19
+ * ```
20
+ */
21
+ var DirectiveRef = class {
22
+ constructor(inner) {
23
+ this.inner = inner;
24
+ }
25
+ /**
26
+ * Extracts the inner structure from a DirectiveRef.
27
+ * Used by build-document.ts to generate DirectiveNode.
28
+ */
29
+ static getInner(ref) {
30
+ return ref.inner;
31
+ }
32
+ };
33
+
34
+ //#endregion
4
35
  //#region packages/core/src/types/type-foundation/var-ref.ts
5
36
  var VarRef = class {
6
37
  constructor(inner) {
@@ -97,6 +128,41 @@ const buildArguments = (args) => Object.entries(args ?? {}).map(([name, value])
97
128
  value: valueNode
98
129
  } : null;
99
130
  }).filter((item) => item !== null);
131
+ /**
132
+ * Validates that a directive can be used at the specified location.
133
+ *
134
+ * @param directive - The directive reference to validate
135
+ * @param expectedLocation - The location where the directive is being used
136
+ * @throws Error if the directive is not valid at the specified location
137
+ */
138
+ const validateDirectiveLocation = (directive, expectedLocation) => {
139
+ const inner = DirectiveRef.getInner(directive);
140
+ if (!inner.locations.includes(expectedLocation)) throw new Error(`Directive @${inner.name} cannot be used on ${expectedLocation}. Valid locations: ${inner.locations.join(", ")}`);
141
+ };
142
+ /**
143
+ * Builds DirectiveNode array from field directives.
144
+ *
145
+ * Filters for DirectiveRef instances, validates their locations,
146
+ * and converts them to GraphQL AST DirectiveNode objects.
147
+ *
148
+ * @param directives - Array of directive references (or unknown values)
149
+ * @param location - The location context for validation
150
+ * @returns Array of DirectiveNode for the GraphQL AST
151
+ */
152
+ const buildDirectives = (directives, location) => {
153
+ return directives.filter((d) => d instanceof DirectiveRef).map((directive) => {
154
+ validateDirectiveLocation(directive, location);
155
+ const inner = DirectiveRef.getInner(directive);
156
+ return {
157
+ kind: graphql.Kind.DIRECTIVE,
158
+ name: {
159
+ kind: graphql.Kind.NAME,
160
+ value: inner.name
161
+ },
162
+ arguments: buildArguments(inner.arguments)
163
+ };
164
+ });
165
+ };
100
166
  const buildUnionSelection = (union) => Object.entries(union).map(([typeName, object]) => {
101
167
  return object ? {
102
168
  kind: graphql.Kind.INLINE_FRAGMENT,
@@ -113,25 +179,29 @@ const buildUnionSelection = (union) => Object.entries(union).map(([typeName, obj
113
179
  }
114
180
  } : null;
115
181
  }).filter((item) => item !== null);
116
- const buildField = (field) => Object.entries(field).map(([alias, { args, field: field$1, object, union }]) => ({
117
- kind: graphql.Kind.FIELD,
118
- name: {
119
- kind: graphql.Kind.NAME,
120
- value: field$1
121
- },
122
- alias: alias !== field$1 ? {
123
- kind: graphql.Kind.NAME,
124
- value: alias
125
- } : void 0,
126
- arguments: buildArguments(args),
127
- selectionSet: object ? {
128
- kind: graphql.Kind.SELECTION_SET,
129
- selections: buildField(object)
130
- } : union ? {
131
- kind: graphql.Kind.SELECTION_SET,
132
- selections: buildUnionSelection(union)
133
- } : void 0
134
- }));
182
+ const buildField = (field) => Object.entries(field).map(([alias, { args, field: field$1, object, union, directives }]) => {
183
+ const builtDirectives = buildDirectives(directives, "FIELD");
184
+ return {
185
+ kind: graphql.Kind.FIELD,
186
+ name: {
187
+ kind: graphql.Kind.NAME,
188
+ value: field$1
189
+ },
190
+ alias: alias !== field$1 ? {
191
+ kind: graphql.Kind.NAME,
192
+ value: alias
193
+ } : void 0,
194
+ arguments: buildArguments(args),
195
+ directives: builtDirectives.length > 0 ? builtDirectives : void 0,
196
+ selectionSet: object ? {
197
+ kind: graphql.Kind.SELECTION_SET,
198
+ selections: buildField(object)
199
+ } : union ? {
200
+ kind: graphql.Kind.SELECTION_SET,
201
+ selections: buildUnionSelection(union)
202
+ } : void 0
203
+ };
204
+ });
135
205
  /**
136
206
  * Converts a constant value to a GraphQL AST ConstValueNode.
137
207
  *
@@ -341,6 +411,85 @@ const createColocateHelper = () => {
341
411
  return $colocate;
342
412
  };
343
413
 
414
+ //#endregion
415
+ //#region packages/core/src/composer/directive-builder.ts
416
+ /**
417
+ * Directive builder utilities for creating field-level directives.
418
+ *
419
+ * Provides type-safe methods for creating directive references that can be
420
+ * applied to field selections. The builder follows a similar pattern to
421
+ * the variable builder ($var).
422
+ *
423
+ * @module
424
+ */
425
+ /**
426
+ * Creates a directive method factory for a specific directive.
427
+ *
428
+ * @param name - The directive name (without @)
429
+ * @param locations - Valid locations where the directive can be applied
430
+ * @returns A function that creates DirectiveRef instances
431
+ *
432
+ * @example
433
+ * ```typescript
434
+ * const skipMethod = createDirectiveMethod("skip", ["FIELD", "FRAGMENT_SPREAD", "INLINE_FRAGMENT"] as const);
435
+ * const skipDirective = skipMethod({ if: true });
436
+ * ```
437
+ */
438
+ const createDirectiveMethod = (name, locations) => {
439
+ return (args) => new DirectiveRef({
440
+ name,
441
+ arguments: args,
442
+ locations
443
+ });
444
+ };
445
+ /**
446
+ * Standard directive locations for @skip and @include.
447
+ */
448
+ const CONDITIONAL_DIRECTIVE_LOCATIONS = [
449
+ "FIELD",
450
+ "FRAGMENT_SPREAD",
451
+ "INLINE_FRAGMENT"
452
+ ];
453
+ /**
454
+ * Creates the standard GraphQL directives (@skip, @include).
455
+ * These are always available regardless of schema definition.
456
+ *
457
+ * @returns Object containing skip and include directive methods
458
+ *
459
+ * @example
460
+ * ```typescript
461
+ * const $dir = createStandardDirectives();
462
+ * const skipDirective = $dir.skip({ if: true });
463
+ * ```
464
+ */
465
+ const createStandardDirectives = () => ({
466
+ skip: createDirectiveMethod("skip", CONDITIONAL_DIRECTIVE_LOCATIONS),
467
+ include: createDirectiveMethod("include", CONDITIONAL_DIRECTIVE_LOCATIONS)
468
+ });
469
+ /**
470
+ * Creates a directive builder with standard directives and optional custom directives.
471
+ *
472
+ * @param customDirectives - Additional directive methods from schema (generated by codegen)
473
+ * @returns Combined directive builder with all available directives
474
+ *
475
+ * @internal Used by codegen to create schema-specific directive builders
476
+ */
477
+ const createDirectiveBuilder = (customDirectives) => {
478
+ return {
479
+ ...createStandardDirectives(),
480
+ ...customDirectives ?? {}
481
+ };
482
+ };
483
+ /**
484
+ * Type guard to check if a value is a DirectiveRef.
485
+ *
486
+ * @param value - Value to check
487
+ * @returns True if value is a DirectiveRef instance
488
+ */
489
+ const isDirectiveRef = (value) => {
490
+ return value instanceof DirectiveRef;
491
+ };
492
+
344
493
  //#endregion
345
494
  //#region packages/core/src/composer/field-path-context.ts
346
495
  /**
@@ -450,6 +599,7 @@ const createFieldFactoriesInner = (schema, typeName) => {
450
599
  const entries = Object.entries(typeDef.fields).map(([fieldName, type]) => {
451
600
  const factory = (fieldArgs, extras) => {
452
601
  const wrap = (value) => require_schema.wrapByKey(extras?.alias ?? fieldName, value);
602
+ const directives = extras?.directives ?? [];
453
603
  if (type.kind === "object") {
454
604
  const factoryReturn = ((nest) => {
455
605
  const nestedFields = withFieldPath(appendToPath(getCurrentFieldPath(), {
@@ -462,7 +612,7 @@ const createFieldFactoriesInner = (schema, typeName) => {
462
612
  field: fieldName,
463
613
  type,
464
614
  args: fieldArgs ?? {},
465
- directives: extras?.directives ?? {},
615
+ directives,
466
616
  object: nestedFields,
467
617
  union: null
468
618
  });
@@ -484,7 +634,7 @@ const createFieldFactoriesInner = (schema, typeName) => {
484
634
  field: fieldName,
485
635
  type,
486
636
  args: fieldArgs ?? {},
487
- directives: extras?.directives ?? {},
637
+ directives,
488
638
  object: null,
489
639
  union: nestedUnion
490
640
  });
@@ -496,7 +646,7 @@ const createFieldFactoriesInner = (schema, typeName) => {
496
646
  field: fieldName,
497
647
  type,
498
648
  args: fieldArgs ?? {},
499
- directives: extras?.directives ?? {},
649
+ directives,
500
650
  object: null,
501
651
  union: null
502
652
  });
@@ -1150,6 +1300,7 @@ const createVarBuilder = (inputTypeMethods) => {
1150
1300
  * - `fragment`: Builders for each object type
1151
1301
  * - `query/mutation/subscription`: Operation builders
1152
1302
  * - `$var`: Variable definition helpers
1303
+ * - `$dir`: Field directive helpers (@skip, @include)
1153
1304
  * - `$colocate`: Fragment colocation utilities
1154
1305
  *
1155
1306
  * @param schema - The GraphQL schema definition
@@ -1160,17 +1311,22 @@ const createVarBuilder = (inputTypeMethods) => {
1160
1311
  * ```typescript
1161
1312
  * const gql = createGqlElementComposer(schema, { inputTypeMethods });
1162
1313
  *
1163
- * const GetUser = gql(({ query, $var }) =>
1314
+ * const GetUser = gql(({ query, $var, $dir }) =>
1164
1315
  * query.operation({
1165
1316
  * name: "GetUser",
1166
- * variables: { id: $var.ID("!") },
1167
- * fields: ({ f, $ }) => ({ ...f.user({ id: $.id })(...) }),
1317
+ * variables: { showEmail: $var("showEmail").Boolean("!") },
1318
+ * fields: ({ f, $ }) => ({
1319
+ * ...f.user({ id: "1" })(({ f }) => ({
1320
+ * ...f.name(),
1321
+ * ...f.email({}, { directives: [$dir.skip({ if: $.showEmail })] }),
1322
+ * })),
1323
+ * }),
1168
1324
  * })
1169
1325
  * );
1170
1326
  * ```
1171
1327
  */
1172
1328
  const createGqlElementComposer = (schema, options) => {
1173
- const { adapter, inputTypeMethods } = options;
1329
+ const { adapter, inputTypeMethods, directiveMethods } = options;
1174
1330
  const helpers = adapter?.helpers;
1175
1331
  const metadataAdapter = adapter?.metadata;
1176
1332
  const fragment = createGqlFragmentComposers(schema, metadataAdapter);
@@ -1181,6 +1337,7 @@ const createGqlElementComposer = (schema, options) => {
1181
1337
  mutation: { operation: createOperationComposer("mutation") },
1182
1338
  subscription: { operation: createOperationComposer("subscription") },
1183
1339
  $var: createVarBuilder(inputTypeMethods),
1340
+ $dir: directiveMethods ?? createStandardDirectives(),
1184
1341
  $colocate: createColocateHelper(),
1185
1342
  ...helpers ?? {}
1186
1343
  };
@@ -1200,10 +1357,13 @@ exports.buildOperationTypeNode = buildOperationTypeNode;
1200
1357
  exports.buildWithTypeModifier = buildWithTypeModifier;
1201
1358
  exports.createColocateHelper = createColocateHelper;
1202
1359
  exports.createDefaultAdapter = createDefaultAdapter;
1360
+ exports.createDirectiveBuilder = createDirectiveBuilder;
1361
+ exports.createDirectiveMethod = createDirectiveMethod;
1203
1362
  exports.createFieldFactories = createFieldFactories;
1204
1363
  exports.createGqlElementComposer = createGqlElementComposer;
1205
1364
  exports.createGqlFragmentComposers = createGqlFragmentComposers;
1206
1365
  exports.createOperationComposerFactory = createOperationComposerFactory;
1366
+ exports.createStandardDirectives = createStandardDirectives;
1207
1367
  exports.createVarAssignments = createVarAssignments;
1208
1368
  exports.createVarBuilder = createVarBuilder;
1209
1369
  exports.createVarMethod = createVarMethod;
@@ -1214,6 +1374,7 @@ exports.define = require_schema.define;
1214
1374
  exports.defineOperationRoots = require_schema.defineOperationRoots;
1215
1375
  exports.defineScalar = require_schema.defineScalar;
1216
1376
  exports.getCurrentFieldPath = getCurrentFieldPath;
1377
+ exports.isDirectiveRef = isDirectiveRef;
1217
1378
  exports.isListType = isListType;
1218
1379
  exports.recordFragmentUsage = recordFragmentUsage;
1219
1380
  exports.unsafeInputType = require_schema.unsafeInputType;