@trackunit/react-graphql-tools 1.16.1 → 1.16.2-alpha-62031fc7c43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,29 +1,3 @@
1
- ## 1.16.1 (2026-08-25)
2
-
3
- ### 🧱 Updated Dependencies
4
-
5
- - Updated iris-app-build-utilities to 2.4.17
6
- - Updated react-vite-test-setup to 0.0.113
7
-
8
- ## 1.16.0 (2026-08-24)
9
-
10
- ### 🚀 Features
11
-
12
- - **react-graphql-tools:** document required authorization scopes in generated mocks ([8b500687a87](https://github.com/Trackunit/manager/commit/8b500687a87))
13
-
14
- ### 🩹 Fixes
15
-
16
- - **react-graphql-tools:** repair scope collection after applied review suggestions ([e0fe4fad522](https://github.com/Trackunit/manager/commit/e0fe4fad522))
17
-
18
- ### 🧱 Updated Dependencies
19
-
20
- - Updated iris-app-build-utilities to 2.4.16
21
-
22
- ### ❤️ Thank You
23
-
24
- - Cursor @cursoragent
25
- - Mikkel Thorbjørn Andersen
26
-
27
1
  ## 1.15.17 (2026-08-20)
28
2
 
29
3
  ### 🧱 Updated Dependencies
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trackunit/react-graphql-tools",
3
- "version": "1.16.1",
3
+ "version": "1.16.2-alpha-62031fc7c43.0",
4
4
  "main": "src/index.js",
5
5
  "executors": "./executors.json",
6
6
  "generators": "./generators.json",
@@ -18,9 +18,7 @@
18
18
  "win-ca": "^3.5.1",
19
19
  "tslib": "^2.6.2",
20
20
  "@nx/devkit": "23.1.0",
21
- "@nx/js": "23.1.0",
22
- "@trackunit/iris-app-build-utilities": "2.4.17",
23
- "graphql": "^16.10.0"
21
+ "@nx/js": "23.1.0"
24
22
  },
25
23
  "migrations": "./migrations.json",
26
24
  "types": "./src/index.d.ts",
@@ -93,7 +93,7 @@ const scrubFile = async (file, nxRoot, isVerbose, generateMocks = true) => {
93
93
  // mutates the shared source file via insertStatements().
94
94
  const scrubbedFileContent = await (0, exports.scrubFileContent)(fileContent, nxRoot, isVerbose, sharedSourceFile);
95
95
  const scrubbedMockFileContent = generateMocks
96
- ? await (0, scrubMockFile_1.scrubMockFileContent)(fileContent, nxRoot, isVerbose, sharedSourceFile, file)
96
+ ? await (0, scrubMockFile_1.scrubMockFileContent)(fileContent, nxRoot, isVerbose, sharedSourceFile)
97
97
  : undefined;
98
98
  const prettierOptions = await getPrettierOptions(nxRoot);
99
99
  let prettySrc = scrubbedFileContent;
@@ -2,4 +2,4 @@ import { SourceFile } from "ts-morph";
2
2
  /**
3
3
  * Generates React hooks from your graphql files.
4
4
  */
5
- export declare const scrubMockFileContent: (fileContent: string, nxRoot: string, isVerbose?: boolean, providedSourceFile?: SourceFile, generatedFilePath?: string) => Promise<string>;
5
+ export declare const scrubMockFileContent: (fileContent: string, _nxRoot: string, isVerbose?: boolean, providedSourceFile?: SourceFile) => Promise<string>;
@@ -4,7 +4,6 @@ exports.scrubMockFileContent = void 0;
4
4
  /* eslint-disable no-console */
5
5
  const faker_1 = require("@faker-js/faker");
6
6
  const ts_morph_1 = require("ts-morph");
7
- const collectPermissions_1 = require("./collectPermissions");
8
7
  const UNDEFINED_SENTINEL = "__UNDEFINED__";
9
8
  /**
10
9
  * Strips surrounding double-quotes from a ts-morph property name.
@@ -282,7 +281,7 @@ const generateMockFromType = (type, text) => {
282
281
  /**
283
282
  * Generates React hooks from your graphql files.
284
283
  */
285
- const scrubMockFileContent = async (fileContent, nxRoot, isVerbose, providedSourceFile, generatedFilePath) => {
284
+ const scrubMockFileContent = async (fileContent, _nxRoot, isVerbose, providedSourceFile) => {
286
285
  const start = new Date().getTime();
287
286
  const generatedFile = providedSourceFile ??
288
287
  new ts_morph_1.Project({
@@ -308,10 +307,6 @@ const scrubMockFileContent = async (fileContent, nxRoot, isVerbose, providedSour
308
307
  const time = end - start;
309
308
  console.log(`Mock Execution time: ${time}`);
310
309
  }
311
- // Resolve which authorization scopes each mocked operation requires. `@authorization(allow: [...])`
312
- // directives only live in the schema, so we walk each operation's selection set against it.
313
- const schema = generatedFilePath ? (0, collectPermissions_1.loadAuthSchema)(nxRoot) : null;
314
- const permissionsByOperation = schema && generatedFilePath ? (0, collectPermissions_1.collectOperationPermissions)(schema, generatedFilePath) : new Map();
315
310
  const mock = mockThese.map(mocker => {
316
311
  const postFix = (mocker.getName().includes("Query") ? "Query" : "Mutation").length;
317
312
  const variablesTypeName = `${mocker.getName()}Variables`;
@@ -351,15 +346,7 @@ const scrubMockFileContent = async (fileContent, nxRoot, isVerbose, providedSour
351
346
  : `
352
347
  const default${mocker.getName()}Variables: gql.${variablesTypeName} = ${defaultVarsSource};
353
348
  `;
354
- const permissions = permissionsByOperation.get(docName) ?? [];
355
- const permissionsComment = permissions.length > 0
356
- ? `
357
- /**
358
- * Required authorization scopes (the token needs at least one allowed scope per accessed field):
359
- ${permissions.map(scope => ` * - ${scope}`).join("\n")}
360
- */`
361
- : "";
362
- return `${constBlock}${permissionsComment}
349
+ return `${constBlock}
363
350
  export const mockFor${mocker.getName()} = (${variablesParam}, data?: DeepPartialNullable<gql.${mocker.getName()}> ) => {
364
351
  return queryFor(gql.${docName}Document, mergeDeepVars(${defaultVarsRef}, variables),
365
352
  mergeDeep(
@@ -368,11 +355,6 @@ ${permissions.map(scope => ` * - ${scope}`).join("\n")}
368
355
  );
369
356
  }`;
370
357
  });
371
- const mockedOperationNames = mockThese.map(mocker => {
372
- const postFix = (mocker.getName().includes("Query") ? "Query" : "Mutation").length;
373
- return mocker.getName().substring(0, mocker.getName().length - postFix);
374
- });
375
- logRequiredPermissions(mockedOperationNames, permissionsByOperation, generatedFilePath, isVerbose);
376
358
  return `
377
359
  import { mergeDeep } from "@apollo/client/utilities";
378
360
  import { mergeDeepVars, queryFor, DeepPartialNullable, PickDeepPartialNullable } from "@trackunit/react-core-contexts-test";
@@ -382,21 +364,4 @@ ${permissions.map(scope => ` * - ${scope}`).join("\n")}
382
364
  `;
383
365
  };
384
366
  exports.scrubMockFileContent = scrubMockFileContent;
385
- /**
386
- * Prints a summary of the authorization scopes required by the mocked operations, plus the full
387
- * de-duplicated list of scopes a token must be granted to satisfy every mock in the file.
388
- */
389
- const logRequiredPermissions = (operationNames, permissionsByOperation, generatedFilePath, isVerbose) => {
390
- if (!isVerbose || permissionsByOperation.size === 0)
391
- return;
392
- const allScopes = new Set();
393
- console.log(`[graphql-mock] Required authorization scopes${generatedFilePath ? ` for ${generatedFilePath}` : ""}:`);
394
- operationNames.forEach(operationName => {
395
- const scopes = permissionsByOperation.get(operationName) ?? [];
396
- scopes.forEach(scope => allScopes.add(scope));
397
- console.log(` ${operationName}: ${scopes.length > 0 ? scopes.join(", ") : "(none)"}`);
398
- });
399
- const sortedScopes = [...allScopes].sort();
400
- console.log(`[graphql-mock] All scopes referenced (${sortedScopes.length}): ${sortedScopes.length > 0 ? sortedScopes.join(", ") : "(none)"}`);
401
- };
402
367
  //# sourceMappingURL=scrubMockFile.js.map
@@ -1,25 +0,0 @@
1
- import { GraphQLSchema } from "graphql";
2
- /**
3
- * Loads the authorization-annotated schema, reusing codegen's own schema resolution
4
- * (`resolveSchema`) so scopes are read from the exact schema operations are generated against - the
5
- * internal superset (which carries every `@authorization` directive) when checked out, otherwise
6
- * the public schema. Returns null when only the remote endpoint is available (nothing to read from
7
- * disk) or the schema file is missing (e.g. isolated test workspaces).
8
- */
9
- export declare const loadAuthSchema: (nxRoot: string) => GraphQLSchema | null;
10
- /**
11
- * Builds a map of operation name -> sorted, de-duplicated authorization scopes required by that
12
- * operation, from raw GraphQL operation/fragment source documents.
13
- *
14
- * @param schema The authorization-annotated schema to resolve field directives against.
15
- * @param documents Raw `.graphql` source strings (operations and any fragments they spread).
16
- */
17
- export declare const collectPermissionsFromDocuments: (schema: GraphQLSchema, documents: Array<string>) => Map<string, Array<string>>;
18
- /**
19
- * Builds a map of operation name -> sorted, de-duplicated authorization scopes required by that
20
- * operation, based on the `@authorization` directives of every field the operation touches.
21
- *
22
- * @param schema The authorization-annotated schema to resolve field directives against.
23
- * @param generatedFilePath Path to the generated `graphql.ts`; its lib `src` root holds the operations.
24
- */
25
- export declare const collectOperationPermissions: (schema: GraphQLSchema, generatedFilePath: string) => Map<string, Array<string>>;
@@ -1,200 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.collectOperationPermissions = exports.collectPermissionsFromDocuments = exports.loadAuthSchema = void 0;
4
- const iris_app_build_utilities_1 = require("@trackunit/iris-app-build-utilities");
5
- const fs_1 = require("fs");
6
- const graphql_1 = require("graphql");
7
- const path_1 = require("path");
8
- const AUTH_DIRECTIVE = "authorization";
9
- const ALLOW_ARGUMENT = "allow";
10
- /**
11
- * Schemas are large (tens of thousands of lines) and every generated lib triggers a scrub, so cache
12
- * the built schema per absolute path for the lifetime of the process.
13
- */
14
- const schemaCache = new Map();
15
- /**
16
- * Loads the authorization-annotated schema, reusing codegen's own schema resolution
17
- * (`resolveSchema`) so scopes are read from the exact schema operations are generated against - the
18
- * internal superset (which carries every `@authorization` directive) when checked out, otherwise
19
- * the public schema. Returns null when only the remote endpoint is available (nothing to read from
20
- * disk) or the schema file is missing (e.g. isolated test workspaces).
21
- */
22
- const loadAuthSchema = (nxRoot) => {
23
- const schemaPointer = (0, iris_app_build_utilities_1.resolveSchema)("auto");
24
- // `resolveSchema` returns the public endpoint URL when no schema file is checked out; we can only
25
- // inspect directives from a schema we can read off disk, so skip in that case.
26
- if (/^https?:\/\//.test(schemaPointer))
27
- return null;
28
- const schemaPath = (0, path_1.isAbsolute)(schemaPointer) ? schemaPointer : (0, path_1.join)(nxRoot, schemaPointer);
29
- if (!(0, fs_1.existsSync)(schemaPath))
30
- return null;
31
- const cached = schemaCache.get(schemaPath);
32
- if (cached !== undefined)
33
- return cached;
34
- try {
35
- const schema = (0, graphql_1.buildSchema)((0, fs_1.readFileSync)(schemaPath, { encoding: "utf-8" }), { assumeValidSDL: true });
36
- schemaCache.set(schemaPath, schema);
37
- return schema;
38
- }
39
- catch {
40
- schemaCache.set(schemaPath, null);
41
- return null;
42
- }
43
- };
44
- exports.loadAuthSchema = loadAuthSchema;
45
- /**
46
- * Flattens a GraphQL value node into the string scopes it contains.
47
- * `allow` accepts both a single string (`allow: "asset.view"`) and a list (`allow: ["asset.view"]`).
48
- */
49
- const valueToScopes = (value) => {
50
- if (value.kind === "StringValue")
51
- return [value.value];
52
- if (value.kind === "ListValue")
53
- return value.values.flatMap(valueToScopes);
54
- return [];
55
- };
56
- /**
57
- * Reads the scopes declared by any `@authorization(allow: ...)` directive in the given list.
58
- */
59
- const scopesFromDirectives = (directives) => {
60
- if (!directives)
61
- return [];
62
- return directives
63
- .filter(directive => directive.name.value === AUTH_DIRECTIVE)
64
- .flatMap(directive => {
65
- const allowArgument = directive.arguments?.find(argument => argument.name.value === ALLOW_ARGUMENT);
66
- return allowArgument ? valueToScopes(allowArgument.value) : [];
67
- });
68
- };
69
- /**
70
- * Collects the object-level `@authorization` scopes declared directly on a type definition
71
- * (the directive is valid on both FIELD_DEFINITION and OBJECT).
72
- */
73
- const scopesFromType = (type) => {
74
- const definitionDirectives = scopesFromDirectives(type.astNode?.directives);
75
- const extensionDirectives = type.extensionASTNodes.flatMap(extension => scopesFromDirectives(extension.directives));
76
- return [...definitionDirectives, ...extensionDirectives];
77
- };
78
- /**
79
- * Walks a selection set against the schema, following inline fragments and fragment spreads, and
80
- * accumulates every authorization scope referenced by the accessed fields and their return types.
81
- */
82
- const collectSelectionScopes = (schema, parentType, selectionSet, fragments, scopes, visitedFragments) => {
83
- if (!parentType)
84
- return;
85
- for (const selection of selectionSet.selections) {
86
- if (selection.kind === "Field") {
87
- if (selection.name.value === "__typename")
88
- continue;
89
- if (!(0, graphql_1.isObjectType)(parentType) && !(0, graphql_1.isInterfaceType)(parentType))
90
- continue;
91
- const fieldDefinition = parentType.getFields()[selection.name.value];
92
- if (!fieldDefinition)
93
- continue;
94
- scopesFromDirectives(fieldDefinition.astNode?.directives).forEach(scope => scopes.add(scope));
95
- const fieldType = (0, graphql_1.getNamedType)(fieldDefinition.type);
96
- scopesFromType(fieldType).forEach(scope => scopes.add(scope));
97
- if (selection.selectionSet) {
98
- collectSelectionScopes(schema, fieldType, selection.selectionSet, fragments, scopes, visitedFragments);
99
- }
100
- }
101
- else if (selection.kind === "InlineFragment") {
102
- const targetType = selection.typeCondition ? schema.getType(selection.typeCondition.name.value) : parentType;
103
- if (targetType)
104
- scopesFromType(targetType).forEach(scope => scopes.add(scope));
105
- collectSelectionScopes(schema, targetType ?? undefined, selection.selectionSet, fragments, scopes, visitedFragments);
106
- }
107
- else {
108
- const fragmentName = selection.name.value;
109
- if (visitedFragments.has(fragmentName))
110
- continue;
111
- visitedFragments.add(fragmentName);
112
- const fragment = fragments.get(fragmentName);
113
- if (!fragment)
114
- continue;
115
- const targetType = schema.getType(fragment.typeCondition.name.value);
116
- if (targetType)
117
- scopesFromType(targetType).forEach(scope => scopes.add(scope));
118
- collectSelectionScopes(schema, targetType ?? undefined, fragment.selectionSet, fragments, scopes, visitedFragments);
119
- }
120
- }
121
- };
122
- /**
123
- * Recursively reads every `.graphql` source document under a directory, skipping generated output.
124
- * `*-manager.graphql` is excluded to match codegen's default document globs, so an operation that was
125
- * never generated can't shadow a generated one of the same name.
126
- */
127
- const readGraphqlDocuments = (directory) => {
128
- if (!(0, fs_1.existsSync)(directory))
129
- return [];
130
- const documents = [];
131
- for (const entry of (0, fs_1.readdirSync)(directory, { withFileTypes: true })) {
132
- const fullPath = (0, path_1.join)(directory, entry.name);
133
- if (entry.isDirectory()) {
134
- if (entry.name === "generated" || entry.name === "node_modules")
135
- continue;
136
- documents.push(...readGraphqlDocuments(fullPath));
137
- }
138
- else if (entry.name.endsWith(".graphql") && !entry.name.endsWith("-manager.graphql")) {
139
- documents.push((0, fs_1.readFileSync)(fullPath, { encoding: "utf-8" }));
140
- }
141
- }
142
- return documents;
143
- };
144
- /**
145
- * Builds a map of operation name -> sorted, de-duplicated authorization scopes required by that
146
- * operation, from raw GraphQL operation/fragment source documents.
147
- *
148
- * @param schema The authorization-annotated schema to resolve field directives against.
149
- * @param documents Raw `.graphql` source strings (operations and any fragments they spread).
150
- */
151
- const collectPermissionsFromDocuments = (schema, documents) => {
152
- const result = new Map();
153
- if (documents.length === 0)
154
- return result;
155
- const definitions = [];
156
- for (const document of documents) {
157
- try {
158
- definitions.push(...(0, graphql_1.parse)(document).definitions);
159
- }
160
- catch (error) {
161
- // A single unparseable document must not silently strip scope docs from every other operation.
162
- // eslint-disable-next-line no-console
163
- console.warn(`[graphql-mock] Skipping unparseable GraphQL document: ${error.message}`);
164
- }
165
- }
166
- const fragments = new Map();
167
- for (const definition of definitions) {
168
- if (definition.kind === "FragmentDefinition") {
169
- fragments.set(definition.name.value, definition);
170
- }
171
- }
172
- for (const definition of definitions) {
173
- if (definition.kind !== "OperationDefinition" || !definition.name)
174
- continue;
175
- const rootType = definition.operation === "query"
176
- ? schema.getQueryType()
177
- : definition.operation === "mutation"
178
- ? schema.getMutationType()
179
- : schema.getSubscriptionType();
180
- const scopes = new Set();
181
- collectSelectionScopes(schema, rootType ?? undefined, definition.selectionSet, fragments, scopes, new Set());
182
- result.set(definition.name.value, [...scopes].sort());
183
- }
184
- return result;
185
- };
186
- exports.collectPermissionsFromDocuments = collectPermissionsFromDocuments;
187
- /**
188
- * Builds a map of operation name -> sorted, de-duplicated authorization scopes required by that
189
- * operation, based on the `@authorization` directives of every field the operation touches.
190
- *
191
- * @param schema The authorization-annotated schema to resolve field directives against.
192
- * @param generatedFilePath Path to the generated `graphql.ts`; its lib `src` root holds the operations.
193
- */
194
- const collectOperationPermissions = (schema, generatedFilePath) => {
195
- // <lib>/src/generated/graphql-api/graphql.ts -> <lib>/src
196
- const documentsDirectory = (0, path_1.join)((0, path_1.dirname)(generatedFilePath), "..", "..");
197
- return (0, exports.collectPermissionsFromDocuments)(schema, readGraphqlDocuments(documentsDirectory));
198
- };
199
- exports.collectOperationPermissions = collectOperationPermissions;
200
- //# sourceMappingURL=collectPermissions.js.map