@descryy/adapter-graphql 0.1.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/adapter.d.ts +113 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/adapter.js +294 -0
- package/dist/adapter.js.map +1 -0
- package/dist/document.d.ts +116 -0
- package/dist/document.d.ts.map +1 -0
- package/dist/document.js +415 -0
- package/dist/document.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/package.json +31 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The GraphQL SDL adapter.
|
|
3
|
+
*
|
|
4
|
+
* The third capability in this project that reaches **R3 with no language
|
|
5
|
+
* adapter at all**, after `adapter-sql` and `adapter-openapi`, and for the same
|
|
6
|
+
* reason all three do: a published schema is *declared*, not deduced. It is a
|
|
7
|
+
* `LanguageAdapter` because it is a per-format reader with a capability matrix,
|
|
8
|
+
* not because SDL is a programming language — `language: "graphql"` describes
|
|
9
|
+
* the artifact it read, DEC-026, exactly the reading `adapter-sql` and
|
|
10
|
+
* `adapter-openapi` get.
|
|
11
|
+
*
|
|
12
|
+
* ## What it mints, and why the identity is not this file's to choose
|
|
13
|
+
*
|
|
14
|
+
* `DEC-317` fixed GraphQL operation identity, transposing `DEC-316`'s gRPC
|
|
15
|
+
* precedent: `method` is the literal sentinel `"GRAPHQL"`, the path is
|
|
16
|
+
* `/<OperationType>/<field>`. That decision was written against Python's
|
|
17
|
+
* strawberry, and its "Consequences" section says any later GraphQL reader
|
|
18
|
+
* "must mint this identical shape". This is the first such reader, so this is
|
|
19
|
+
* the first place that claim is *tested* rather than asserted —
|
|
20
|
+
* `test/sdl-strawberry-join.test.ts` runs both producers over the same
|
|
21
|
+
* operation and compares the emitted ids, in the shape `DEC-321` established
|
|
22
|
+
* for the PHP/SQL `DATABASE_TABLE` join.
|
|
23
|
+
*
|
|
24
|
+
* ## Why this reader supersedes the class-structure one, and how
|
|
25
|
+
*
|
|
26
|
+
* `DEC-317` §"The departure" is candid that strawberry forced a compromise:
|
|
27
|
+
* with no artifact to read, the schema-facing name had to be derived from the
|
|
28
|
+
* class structure, and a name that could only be *computed* had to be refused
|
|
29
|
+
* as `graphqlFieldNameNotReadable`. An SDL field name is the schema-facing name
|
|
30
|
+
* by definition. There is nothing to compute and nothing to refuse.
|
|
31
|
+
*
|
|
32
|
+
* **The precedence is expressed through resolution, not through a priority
|
|
33
|
+
* rule, and that is deliberate.** This adapter emits at R3: the document is the
|
|
34
|
+
* schema. `adapter-python`'s strawberry reader emits its operations at R0 or R1
|
|
35
|
+
* (`extract.ts` — R1 once a field was reached through the import graph). Rule 3,
|
|
36
|
+
* the resolution cap, then does the ranking on its own: an SDL-derived finding
|
|
37
|
+
* is capped at reliability class A and a strawberry-derived one at C or B. No
|
|
38
|
+
* new machinery, no producer allow-list, no "SDL wins" branch anywhere above
|
|
39
|
+
* the IR — the mechanism the engine already has says the right thing, because
|
|
40
|
+
* the two producers genuinely differ in the strength of their evidence.
|
|
41
|
+
*
|
|
42
|
+
* **Where SDL and source disagree, they do not fight — they diverge visibly.**
|
|
43
|
+
* The two producers' `API_ROUTE` nodes are distinct by construction (`API_ROUTE`
|
|
44
|
+
* is language-scoped, so `graphql` and `python` routes never share an id), and
|
|
45
|
+
* that asymmetry is the contract mechanism working as designed: the route is
|
|
46
|
+
* each producer's local view. They meet at the `API_ENDPOINT`, which is fileless
|
|
47
|
+
* and language-less. So:
|
|
48
|
+
*
|
|
49
|
+
* - **agreement** — one `API_ENDPOINT`, two `SERVES_API` edges into it from two
|
|
50
|
+
* routes, at two resolutions. This is the join, and it is the case that
|
|
51
|
+
* proves the convention transposed correctly.
|
|
52
|
+
* - **disagreement on a name** — two `API_ENDPOINT` nodes, one per spelling.
|
|
53
|
+
* The SDL's is the correct one, and it carries R3 while the source-derived one
|
|
54
|
+
* carries R0/R1. A stale committed SDL is the same risk a stale committed
|
|
55
|
+
* `openapi.json` carries and is treated the same way: disclosed, not
|
|
56
|
+
* downgraded. The divergence being *visible* is the useful property — a
|
|
57
|
+
* published schema that no longer matches its source is a real finding, and a
|
|
58
|
+
* reader that silently preferred one would have destroyed the evidence for it.
|
|
59
|
+
*
|
|
60
|
+
* ## What it does not claim, and the two refusals are load-bearing
|
|
61
|
+
*
|
|
62
|
+
* **No `DTO`, and not for scope reasons.** `adapter-openapi` mints a `DTO` per
|
|
63
|
+
* `components.schemas` entry and a `USES_TYPE` edge from the operation that
|
|
64
|
+
* names it, because in REST the response body *is* the schema the operation
|
|
65
|
+
* returns. **GraphQL does not work that way.** A client selects the fields it
|
|
66
|
+
* wants, so a `USES_TYPE` edge asserting "this operation returns this shape"
|
|
67
|
+
* would be false for every caller that selects a subset — which is every caller.
|
|
68
|
+
* Emitting one would be a wrong edge, and rule 2 puts a wrong edge below a
|
|
69
|
+
* missing one because it corrupts every layer above it. Disclosed gap.
|
|
70
|
+
*
|
|
71
|
+
* **No `USES_API`, and this one is a gate.** `DEC-317` §"The one flagged risk"
|
|
72
|
+
* found that `nearestMiss` rule 2 collapses under GraphQL paths — every
|
|
73
|
+
* operation is arity-2, carries no `{param}`, and draws its first segment from a
|
|
74
|
+
* set of size three — and gated any caller-side GraphQL reader on that being
|
|
75
|
+
* fixed first. `DEC-319` has since given rule 2 a measured similarity floor,
|
|
76
|
+
* which narrows the collapse but does not by itself discharge a gate that was
|
|
77
|
+
* written about a rule's reachability. This adapter builds the **server half
|
|
78
|
+
* only**, so it does not reach `nearestMiss` at all: that function is called
|
|
79
|
+
* from exactly one place, `orphans.ts`'s `withCallers` loop, which requires an
|
|
80
|
+
* endpoint with a `USES_API` caller. Verified in `descry-core` for this change,
|
|
81
|
+
* not inherited.
|
|
82
|
+
*/
|
|
83
|
+
import { type LanguageAdapter, type ResolutionLevel } from "@descryy/ir";
|
|
84
|
+
import { type OperationSlot } from "./document.ts";
|
|
85
|
+
export declare const ADAPTER_ID = "adapter-graphql";
|
|
86
|
+
export declare const ADAPTER_VERSION = "0.1.0";
|
|
87
|
+
/**
|
|
88
|
+
* `.graphql` and `.gql`. An introspection `schema.json` is **not** read — see
|
|
89
|
+
* `document.ts`'s declared limits for the census that decided it.
|
|
90
|
+
*/
|
|
91
|
+
export declare const DEFAULT_SCHEMA_GLOBS: readonly ["**/*.graphql", "**/*.gql"];
|
|
92
|
+
/** DEC-026: the artifact read, not a programming language. Part of the id since DEC-054. */
|
|
93
|
+
export declare const LANGUAGE = "graphql";
|
|
94
|
+
/**
|
|
95
|
+
* `DEC-317`'s protocol sentinel, repeated here rather than imported.
|
|
96
|
+
*
|
|
97
|
+
* `adapter-python` exports the same constant from `packages/adapter-python/src/graphql.ts`,
|
|
98
|
+
* and importing it would make a format adapter depend on a language adapter to
|
|
99
|
+
* state its own identity — the dependency edge pointing the wrong way, and a
|
|
100
|
+
* package boundary crossed for one string. The value is pinned by the
|
|
101
|
+
* two-producer test instead, which is the guard that actually matters: a
|
|
102
|
+
* shared constant that both sides import can drift from the *decision* without
|
|
103
|
+
* either test noticing, whereas a test that runs both producers cannot.
|
|
104
|
+
*/
|
|
105
|
+
export declare const GRAPHQL_METHOD = "GRAPHQL";
|
|
106
|
+
/** `Query` + `circuit` -> `/Query/circuit`. `DEC-317`'s path shape, exactly. */
|
|
107
|
+
export declare function operationPath(slot: OperationSlot, fieldName: string): string;
|
|
108
|
+
export interface GraphqlAdapterOptions {
|
|
109
|
+
readonly maxResolution?: ResolutionLevel;
|
|
110
|
+
readonly parseGlobs?: readonly string[];
|
|
111
|
+
}
|
|
112
|
+
export declare function createGraphqlAdapter(options?: GraphqlAdapterOptions): LanguageAdapter;
|
|
113
|
+
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiFG;AAKH,OAAO,EAYL,KAAK,eAAe,EAGpB,KAAK,eAAe,EAErB,MAAM,aAAa,CAAC;AAIrB,OAAO,EAA4C,KAAK,aAAa,EAAe,MAAM,eAAe,CAAC;AAE1G,eAAO,MAAM,UAAU,oBAAoB,CAAC;AAC5C,eAAO,MAAM,eAAe,UAAU,CAAC;AAEvC;;;GAGG;AACH,eAAO,MAAM,oBAAoB,uCAAwC,CAAC;AAE1E,4FAA4F;AAC5F,eAAO,MAAM,QAAQ,YAAY,CAAC;AAElC;;;;;;;;;;GAUG;AACH,eAAO,MAAM,cAAc,YAAY,CAAC;AAIxC,gFAAgF;AAChF,wBAAgB,aAAa,CAAC,IAAI,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAE5E;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,aAAa,CAAC,EAAE,eAAe,CAAC;IACzC,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACzC;AAOD,wBAAgB,oBAAoB,CAAC,OAAO,GAAE,qBAA0B,GAAG,eAAe,CA2GzF"}
|
package/dist/adapter.js
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The GraphQL SDL adapter.
|
|
3
|
+
*
|
|
4
|
+
* The third capability in this project that reaches **R3 with no language
|
|
5
|
+
* adapter at all**, after `adapter-sql` and `adapter-openapi`, and for the same
|
|
6
|
+
* reason all three do: a published schema is *declared*, not deduced. It is a
|
|
7
|
+
* `LanguageAdapter` because it is a per-format reader with a capability matrix,
|
|
8
|
+
* not because SDL is a programming language — `language: "graphql"` describes
|
|
9
|
+
* the artifact it read, DEC-026, exactly the reading `adapter-sql` and
|
|
10
|
+
* `adapter-openapi` get.
|
|
11
|
+
*
|
|
12
|
+
* ## What it mints, and why the identity is not this file's to choose
|
|
13
|
+
*
|
|
14
|
+
* `DEC-317` fixed GraphQL operation identity, transposing `DEC-316`'s gRPC
|
|
15
|
+
* precedent: `method` is the literal sentinel `"GRAPHQL"`, the path is
|
|
16
|
+
* `/<OperationType>/<field>`. That decision was written against Python's
|
|
17
|
+
* strawberry, and its "Consequences" section says any later GraphQL reader
|
|
18
|
+
* "must mint this identical shape". This is the first such reader, so this is
|
|
19
|
+
* the first place that claim is *tested* rather than asserted —
|
|
20
|
+
* `test/sdl-strawberry-join.test.ts` runs both producers over the same
|
|
21
|
+
* operation and compares the emitted ids, in the shape `DEC-321` established
|
|
22
|
+
* for the PHP/SQL `DATABASE_TABLE` join.
|
|
23
|
+
*
|
|
24
|
+
* ## Why this reader supersedes the class-structure one, and how
|
|
25
|
+
*
|
|
26
|
+
* `DEC-317` §"The departure" is candid that strawberry forced a compromise:
|
|
27
|
+
* with no artifact to read, the schema-facing name had to be derived from the
|
|
28
|
+
* class structure, and a name that could only be *computed* had to be refused
|
|
29
|
+
* as `graphqlFieldNameNotReadable`. An SDL field name is the schema-facing name
|
|
30
|
+
* by definition. There is nothing to compute and nothing to refuse.
|
|
31
|
+
*
|
|
32
|
+
* **The precedence is expressed through resolution, not through a priority
|
|
33
|
+
* rule, and that is deliberate.** This adapter emits at R3: the document is the
|
|
34
|
+
* schema. `adapter-python`'s strawberry reader emits its operations at R0 or R1
|
|
35
|
+
* (`extract.ts` — R1 once a field was reached through the import graph). Rule 3,
|
|
36
|
+
* the resolution cap, then does the ranking on its own: an SDL-derived finding
|
|
37
|
+
* is capped at reliability class A and a strawberry-derived one at C or B. No
|
|
38
|
+
* new machinery, no producer allow-list, no "SDL wins" branch anywhere above
|
|
39
|
+
* the IR — the mechanism the engine already has says the right thing, because
|
|
40
|
+
* the two producers genuinely differ in the strength of their evidence.
|
|
41
|
+
*
|
|
42
|
+
* **Where SDL and source disagree, they do not fight — they diverge visibly.**
|
|
43
|
+
* The two producers' `API_ROUTE` nodes are distinct by construction (`API_ROUTE`
|
|
44
|
+
* is language-scoped, so `graphql` and `python` routes never share an id), and
|
|
45
|
+
* that asymmetry is the contract mechanism working as designed: the route is
|
|
46
|
+
* each producer's local view. They meet at the `API_ENDPOINT`, which is fileless
|
|
47
|
+
* and language-less. So:
|
|
48
|
+
*
|
|
49
|
+
* - **agreement** — one `API_ENDPOINT`, two `SERVES_API` edges into it from two
|
|
50
|
+
* routes, at two resolutions. This is the join, and it is the case that
|
|
51
|
+
* proves the convention transposed correctly.
|
|
52
|
+
* - **disagreement on a name** — two `API_ENDPOINT` nodes, one per spelling.
|
|
53
|
+
* The SDL's is the correct one, and it carries R3 while the source-derived one
|
|
54
|
+
* carries R0/R1. A stale committed SDL is the same risk a stale committed
|
|
55
|
+
* `openapi.json` carries and is treated the same way: disclosed, not
|
|
56
|
+
* downgraded. The divergence being *visible* is the useful property — a
|
|
57
|
+
* published schema that no longer matches its source is a real finding, and a
|
|
58
|
+
* reader that silently preferred one would have destroyed the evidence for it.
|
|
59
|
+
*
|
|
60
|
+
* ## What it does not claim, and the two refusals are load-bearing
|
|
61
|
+
*
|
|
62
|
+
* **No `DTO`, and not for scope reasons.** `adapter-openapi` mints a `DTO` per
|
|
63
|
+
* `components.schemas` entry and a `USES_TYPE` edge from the operation that
|
|
64
|
+
* names it, because in REST the response body *is* the schema the operation
|
|
65
|
+
* returns. **GraphQL does not work that way.** A client selects the fields it
|
|
66
|
+
* wants, so a `USES_TYPE` edge asserting "this operation returns this shape"
|
|
67
|
+
* would be false for every caller that selects a subset — which is every caller.
|
|
68
|
+
* Emitting one would be a wrong edge, and rule 2 puts a wrong edge below a
|
|
69
|
+
* missing one because it corrupts every layer above it. Disclosed gap.
|
|
70
|
+
*
|
|
71
|
+
* **No `USES_API`, and this one is a gate.** `DEC-317` §"The one flagged risk"
|
|
72
|
+
* found that `nearestMiss` rule 2 collapses under GraphQL paths — every
|
|
73
|
+
* operation is arity-2, carries no `{param}`, and draws its first segment from a
|
|
74
|
+
* set of size three — and gated any caller-side GraphQL reader on that being
|
|
75
|
+
* fixed first. `DEC-319` has since given rule 2 a measured similarity floor,
|
|
76
|
+
* which narrows the collapse but does not by itself discharge a gate that was
|
|
77
|
+
* written about a rule's reachability. This adapter builds the **server half
|
|
78
|
+
* only**, so it does not reach `nearestMiss` at all: that function is called
|
|
79
|
+
* from exactly one place, `orphans.ts`'s `withCallers` loop, which requires an
|
|
80
|
+
* endpoint with a `USES_API` caller. Verified in `descry-core` for this change,
|
|
81
|
+
* not inherited.
|
|
82
|
+
*/
|
|
83
|
+
import { readFileSync } from "node:fs";
|
|
84
|
+
import { join } from "node:path";
|
|
85
|
+
import { endpointQsp, nodeId, normaliseEndpointPath, } from "@descryy/ir";
|
|
86
|
+
import { discover } from "@descryy/adapter-common";
|
|
87
|
+
import { OPERATION_TYPE, readSchema, rootFieldsOf } from "./document.js";
|
|
88
|
+
export const ADAPTER_ID = "adapter-graphql";
|
|
89
|
+
export const ADAPTER_VERSION = "0.1.0";
|
|
90
|
+
/**
|
|
91
|
+
* `.graphql` and `.gql`. An introspection `schema.json` is **not** read — see
|
|
92
|
+
* `document.ts`'s declared limits for the census that decided it.
|
|
93
|
+
*/
|
|
94
|
+
export const DEFAULT_SCHEMA_GLOBS = ["**/*.graphql", "**/*.gql"];
|
|
95
|
+
/** DEC-026: the artifact read, not a programming language. Part of the id since DEC-054. */
|
|
96
|
+
export const LANGUAGE = "graphql";
|
|
97
|
+
/**
|
|
98
|
+
* `DEC-317`'s protocol sentinel, repeated here rather than imported.
|
|
99
|
+
*
|
|
100
|
+
* `adapter-python` exports the same constant from `packages/adapter-python/src/graphql.ts`,
|
|
101
|
+
* and importing it would make a format adapter depend on a language adapter to
|
|
102
|
+
* state its own identity — the dependency edge pointing the wrong way, and a
|
|
103
|
+
* package boundary crossed for one string. The value is pinned by the
|
|
104
|
+
* two-producer test instead, which is the guard that actually matters: a
|
|
105
|
+
* shared constant that both sides import can drift from the *decision* without
|
|
106
|
+
* either test noticing, whereas a test that runs both producers cannot.
|
|
107
|
+
*/
|
|
108
|
+
export const GRAPHQL_METHOD = "GRAPHQL";
|
|
109
|
+
const PRODUCED_BY = `${ADAPTER_ID}@${ADAPTER_VERSION}`;
|
|
110
|
+
/** `Query` + `circuit` -> `/Query/circuit`. `DEC-317`'s path shape, exactly. */
|
|
111
|
+
export function operationPath(slot, fieldName) {
|
|
112
|
+
return `/${OPERATION_TYPE[slot]}/${fieldName}`;
|
|
113
|
+
}
|
|
114
|
+
export function createGraphqlAdapter(options = {}) {
|
|
115
|
+
const ceiling = options.maxResolution ?? 3;
|
|
116
|
+
const globs = options.parseGlobs ?? DEFAULT_SCHEMA_GLOBS;
|
|
117
|
+
let root;
|
|
118
|
+
const read = (repoRoot, file) => {
|
|
119
|
+
try {
|
|
120
|
+
return { file, schema: readSchema(readFileSync(join(repoRoot.absolutePath, file), "utf8")) };
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
return { file, schema: undefined };
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
/** Every candidate file that actually read as a schema — an executable document does not. */
|
|
127
|
+
const schemasUnder = (repoRoot) => [...new Set(discover(repoRoot.absolutePath, globs))]
|
|
128
|
+
.sort()
|
|
129
|
+
.map((file) => read(repoRoot, file))
|
|
130
|
+
.filter((unit) => unit.schema !== undefined);
|
|
131
|
+
return {
|
|
132
|
+
id: ADAPTER_ID,
|
|
133
|
+
version: ADAPTER_VERSION,
|
|
134
|
+
async detect(repoRoot) {
|
|
135
|
+
const units = schemasUnder(repoRoot);
|
|
136
|
+
if (units.length === 0)
|
|
137
|
+
return { detected: false, roots: [], evidence: [] };
|
|
138
|
+
const operations = units.reduce((sum, unit) => sum + (unit.schema === undefined ? 0 : rootFieldsOf(unit.schema).length), 0);
|
|
139
|
+
return {
|
|
140
|
+
detected: true,
|
|
141
|
+
roots: [...new Set(units.map((unit) => unit.file.split("/")[0] ?? "."))].sort(),
|
|
142
|
+
evidence: [
|
|
143
|
+
`${units.length} GraphQL SDL document(s), ${operations} root operation(s)`,
|
|
144
|
+
"SDL only — an introspection schema.json is not read, by declared limit",
|
|
145
|
+
],
|
|
146
|
+
};
|
|
147
|
+
},
|
|
148
|
+
capabilities() {
|
|
149
|
+
return {
|
|
150
|
+
maxResolution: ceiling,
|
|
151
|
+
// No DTO and no USES_TYPE: a GraphQL client selects its own fields, so
|
|
152
|
+
// an edge asserting that an operation returns a whole named shape would
|
|
153
|
+
// be wrong for every caller. See the module header.
|
|
154
|
+
nodeTypes: ["API_ROUTE", "API_ENDPOINT"],
|
|
155
|
+
edgeTypes: ["SERVES_API"],
|
|
156
|
+
hasCallHierarchy: false,
|
|
157
|
+
hasTypeHierarchy: false,
|
|
158
|
+
frameworkExtractors: ["graphql-sdl"],
|
|
159
|
+
frameworkCapabilities: ["route-extractor"],
|
|
160
|
+
};
|
|
161
|
+
},
|
|
162
|
+
async prepare(ctx) {
|
|
163
|
+
root = ctx.root;
|
|
164
|
+
// Nothing to prepare and nothing that can fail: no dependency resolution,
|
|
165
|
+
// no type checker, no index. R3 is not optimism — the document *is* the
|
|
166
|
+
// schema, and every name in it is the served name.
|
|
167
|
+
return ceiling;
|
|
168
|
+
},
|
|
169
|
+
async parseFiles(files) {
|
|
170
|
+
const active = root;
|
|
171
|
+
return files.map((file) => ({
|
|
172
|
+
file,
|
|
173
|
+
tree: active === undefined ? { file: file.path, schema: undefined } : read(active, file.path),
|
|
174
|
+
}));
|
|
175
|
+
},
|
|
176
|
+
extractNodes(units) {
|
|
177
|
+
return build(units.map((unit) => unit.tree), root, ceiling).nodes;
|
|
178
|
+
},
|
|
179
|
+
extractEdges(units) {
|
|
180
|
+
return build(units.map((unit) => unit.tree), root, ceiling).edges;
|
|
181
|
+
},
|
|
182
|
+
async emit(ctx) {
|
|
183
|
+
const reached = await this.prepare(ctx);
|
|
184
|
+
const units = schemasUnder(ctx.root);
|
|
185
|
+
const { nodes, edges, unresolved } = build(units, ctx.root, reached);
|
|
186
|
+
return {
|
|
187
|
+
repo: ctx.root.repo,
|
|
188
|
+
...(ctx.root.workspace === undefined ? {} : { workspace: ctx.root.workspace }),
|
|
189
|
+
commitSha: ctx.root.commitSha,
|
|
190
|
+
producedBy: PRODUCED_BY,
|
|
191
|
+
sourceFiles: units.map((unit) => unit.file),
|
|
192
|
+
// `schemasUnder` already filters to units that read as a schema — a
|
|
193
|
+
// `.graphql` file holding an executable document is excluded from
|
|
194
|
+
// `units` (and so from `sourceFiles`) without reaching this session,
|
|
195
|
+
// exactly as `adapter-openapi` handles an unparseable contract.
|
|
196
|
+
skippedFiles: [],
|
|
197
|
+
reachedResolution: reached,
|
|
198
|
+
nodes,
|
|
199
|
+
edges,
|
|
200
|
+
unresolved,
|
|
201
|
+
};
|
|
202
|
+
},
|
|
203
|
+
async dispose() {
|
|
204
|
+
root = undefined;
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
function build(units, repoRoot, reached) {
|
|
209
|
+
if (repoRoot === undefined)
|
|
210
|
+
return { nodes: [], edges: [], unresolved: [] };
|
|
211
|
+
const nodes = new Map();
|
|
212
|
+
const edges = new Map();
|
|
213
|
+
const unresolved = [];
|
|
214
|
+
const scope = { repo: repoRoot.repo, workspace: repoRoot.workspace };
|
|
215
|
+
for (const unit of units) {
|
|
216
|
+
const schema = unit.schema;
|
|
217
|
+
if (schema === undefined)
|
|
218
|
+
continue;
|
|
219
|
+
let anchorId;
|
|
220
|
+
for (const field of rootFieldsOf(schema)) {
|
|
221
|
+
const served = operationPath(field.slot, field.name);
|
|
222
|
+
// `${method} ${served}` and `endpointQsp(method, served)` are the two
|
|
223
|
+
// minting sites `DEC-317` fixed, spelled here exactly as
|
|
224
|
+
// `adapter-python`'s `emitDjangoRoute` spells them. The two-producer test
|
|
225
|
+
// is what holds them equal; this comment is not.
|
|
226
|
+
const routeId = nodeId(scope, "API_ROUTE", `${GRAPHQL_METHOD} ${served}`, LANGUAGE);
|
|
227
|
+
if (!nodes.has(routeId)) {
|
|
228
|
+
anchorId ??= routeId;
|
|
229
|
+
nodes.set(routeId, {
|
|
230
|
+
id: routeId,
|
|
231
|
+
type: "API_ROUTE",
|
|
232
|
+
name: `${GRAPHQL_METHOD} ${served}`,
|
|
233
|
+
file: unit.file,
|
|
234
|
+
range: { startLine: field.line, endLine: field.line },
|
|
235
|
+
language: LANGUAGE,
|
|
236
|
+
producedBy: PRODUCED_BY,
|
|
237
|
+
resolution: reached,
|
|
238
|
+
attrs: {
|
|
239
|
+
method: GRAPHQL_METHOD,
|
|
240
|
+
pathTemplate: normaliseEndpointPath(served),
|
|
241
|
+
rawTemplate: served,
|
|
242
|
+
framework: "graphql-sdl",
|
|
243
|
+
operationType: OPERATION_TYPE[field.slot],
|
|
244
|
+
},
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
// Fileless AND language-less. This node is where an SDL-derived operation
|
|
248
|
+
// and a strawberry-derived one for the same real operation meet, and
|
|
249
|
+
// `nodeId` throws if a fileless type is hashed with a language — the guard
|
|
250
|
+
// against an id whose only symptom would be a join that silently does not
|
|
251
|
+
// happen.
|
|
252
|
+
const endpointId = nodeId(scope, "API_ENDPOINT", endpointQsp(GRAPHQL_METHOD, served), null);
|
|
253
|
+
if (!nodes.has(endpointId)) {
|
|
254
|
+
nodes.set(endpointId, {
|
|
255
|
+
id: endpointId,
|
|
256
|
+
type: "API_ENDPOINT",
|
|
257
|
+
name: `${GRAPHQL_METHOD} ${normaliseEndpointPath(served)}`,
|
|
258
|
+
file: null,
|
|
259
|
+
range: null,
|
|
260
|
+
language: null,
|
|
261
|
+
producedBy: PRODUCED_BY,
|
|
262
|
+
resolution: reached,
|
|
263
|
+
attrs: { method: GRAPHQL_METHOD, pathTemplate: normaliseEndpointPath(served) },
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
edges.set(`${routeId} ${endpointId} SERVES_API`, {
|
|
267
|
+
from: routeId,
|
|
268
|
+
to: endpointId,
|
|
269
|
+
type: "SERVES_API",
|
|
270
|
+
producedBy: PRODUCED_BY,
|
|
271
|
+
resolution: reached,
|
|
272
|
+
confidence: 0.95,
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
// Every refusal, anchored to a node the batch actually contains. A ledger
|
|
276
|
+
// entry naming a node that was never emitted is a dangling reference
|
|
277
|
+
// dressed up as a disclosure.
|
|
278
|
+
if (anchorId === undefined)
|
|
279
|
+
continue;
|
|
280
|
+
for (const refusal of schema.refused) {
|
|
281
|
+
unresolved.push({
|
|
282
|
+
fromNodeId: anchorId,
|
|
283
|
+
edgeType: "SERVES_API",
|
|
284
|
+
rawTarget: refusal.what.slice(0, 120),
|
|
285
|
+
file: unit.file,
|
|
286
|
+
line: refusal.line,
|
|
287
|
+
producedBy: PRODUCED_BY,
|
|
288
|
+
reason: refusal.why,
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return { nodes: [...nodes.values()], edges: [...edges.values()], unresolved };
|
|
293
|
+
}
|
|
294
|
+
//# sourceMappingURL=adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading a GraphQL SDL document, and why this is the *strong* half of the
|
|
3
|
+
* GraphQL story rather than a convenience.
|
|
4
|
+
*
|
|
5
|
+
* `DEC-317` built GraphQL operation identity for Python/strawberry, and was
|
|
6
|
+
* explicit about the compromise it had to make. gRPC's precedent
|
|
7
|
+
* (`DEC-316`) rests on **"the path is read, not assembled"** — protoc writes
|
|
8
|
+
* `Service_Method_FullMethodName = "/pkg.Service/Method"` and the reader takes
|
|
9
|
+
* that literal verbatim. strawberry writes no such artifact: the schema is
|
|
10
|
+
* built at import time out of the class structure, the schema-facing name is
|
|
11
|
+
* camel-cased by default, and so `DEC-317` had to narrow itself to the three
|
|
12
|
+
* cases in which a name is genuinely *read* and refuse the rest as
|
|
13
|
+
* `graphqlFieldNameNotReadable`.
|
|
14
|
+
*
|
|
15
|
+
* **An SDL artifact removes the compromise entirely.** A field name in SDL is
|
|
16
|
+
* the schema-facing name — not a Python attribute that a naming converter will
|
|
17
|
+
* later transform, not a value a default might change, but the string the
|
|
18
|
+
* server publishes and a client types. There is nothing to compute, nothing to
|
|
19
|
+
* resolve and nothing to refuse on naming grounds. `DEC-317` §"The three
|
|
20
|
+
* sub-questions" says so outright: where an SDL artifact exists it "**is** the
|
|
21
|
+
* protoc-const analogue and should be preferred over this mechanism outright".
|
|
22
|
+
* This reader is that preference, built.
|
|
23
|
+
*
|
|
24
|
+
* ## Why a lexer and not a regex
|
|
25
|
+
*
|
|
26
|
+
* The naive rule — *a `Name` followed by `:` inside a type body is a field* —
|
|
27
|
+
* fails on the first real schema it meets, in four separate ways that Saleor's
|
|
28
|
+
* `saleor/graphql/schema.graphql` contains thousands of instances of:
|
|
29
|
+
*
|
|
30
|
+
* | construct | what the naive rule does |
|
|
31
|
+
* | --- | --- |
|
|
32
|
+
* | `webhook(id: ID!, filter: F = { channel: "x" }): Webhook` | emits `id`, `filter` and `channel` as operations |
|
|
33
|
+
* | `webhookEvents: [E!] @doc(category: "Webhooks")` | emits `category` |
|
|
34
|
+
* | `"""A description with a { brace and a colon: here."""` | emits whatever the prose contains |
|
|
35
|
+
* | `# a comment` | the same |
|
|
36
|
+
*
|
|
37
|
+
* So the document is tokenised properly — block strings, strings, comments and
|
|
38
|
+
* punctuation — and a field is admitted only in the one position the grammar
|
|
39
|
+
* puts one: a `Name`, at the type body's own brace depth and outside every
|
|
40
|
+
* parenthesis, followed (past a balanced argument list, if it has one) by `:`.
|
|
41
|
+
*
|
|
42
|
+
* ## Declared limits
|
|
43
|
+
*
|
|
44
|
+
* **`.graphql`/`.gql` only — an introspection `schema.json` is not read.**
|
|
45
|
+
* `DEC-317` names it as a third artifact that could carry the same truth, and
|
|
46
|
+
* it could. It is not built because **no repository on hand contains one**: a
|
|
47
|
+
* census of 52 reference repositories plus Saleor found two `.graphql` files
|
|
48
|
+
* and zero GraphQL introspection JSON documents. Building a second format
|
|
49
|
+
* reader against documentation with no source to measure it on is the thing
|
|
50
|
+
* `DEC-317`'s own graphene paragraph refuses to do, and it would be no more
|
|
51
|
+
* defensible here. Disclosed gap, not a silent one.
|
|
52
|
+
*
|
|
53
|
+
* **One file is one document.** A schema block naming a root type that the same
|
|
54
|
+
* file does not declare earns a ledger row rather than a search of the other
|
|
55
|
+
* `.graphql` files in the repository. SDL *can* legally be split across files,
|
|
56
|
+
* and where it is this reader under-reports and says so; both real artifacts
|
|
57
|
+
* measured here are single-file, so cross-file stitching would be reach built
|
|
58
|
+
* against zero demand, and a wrong stitch — pairing a schema block with a
|
|
59
|
+
* same-named type from an unrelated schema — is a silently wrong answer rather
|
|
60
|
+
* than a missing one.
|
|
61
|
+
*
|
|
62
|
+
* **Executable documents are not schemas.** A `.graphql` file holding
|
|
63
|
+
* `query IntrospectionQuery { ... }` or a fragment is a *client* document;
|
|
64
|
+
* twenty ships exactly one. It yields no type-system definition, so it yields
|
|
65
|
+
* no schema, and `readSchema` returns `undefined` rather than minting
|
|
66
|
+
* operations named after an introspection payload.
|
|
67
|
+
*/
|
|
68
|
+
/** GraphQL's three root operation types, fixed by the specification. */
|
|
69
|
+
export declare const OPERATION_SLOTS: readonly ["query", "mutation", "subscription"];
|
|
70
|
+
export type OperationSlot = (typeof OPERATION_SLOTS)[number];
|
|
71
|
+
/**
|
|
72
|
+
* The path segment each slot contributes, and the type name each slot defaults
|
|
73
|
+
* to when the document declares no `schema` block.
|
|
74
|
+
*
|
|
75
|
+
* **These two columns are the same word for a reason, and it is not a
|
|
76
|
+
* coincidence worth collapsing.** The left is GraphQL's operation-type
|
|
77
|
+
* vocabulary, which `DEC-317` puts in the path; the right is the specification's
|
|
78
|
+
* default root type name (October 2021 §3.3.1), which a `schema` block may
|
|
79
|
+
* override. Saleor overrides nothing and still writes the block; twenty writes
|
|
80
|
+
* no block at all. Keeping them apart is what makes `schema { query: RootQuery }`
|
|
81
|
+
* yield `/Query/…` rather than `/RootQuery/…`.
|
|
82
|
+
*/
|
|
83
|
+
export declare const OPERATION_TYPE: Readonly<Record<OperationSlot, string>>;
|
|
84
|
+
export interface Refusal {
|
|
85
|
+
readonly what: string;
|
|
86
|
+
readonly why: string;
|
|
87
|
+
readonly line: number;
|
|
88
|
+
}
|
|
89
|
+
export interface RootField {
|
|
90
|
+
readonly slot: OperationSlot;
|
|
91
|
+
/**
|
|
92
|
+
* The schema-facing name, **read verbatim**. There is no other kind here —
|
|
93
|
+
* that is the whole reason this adapter is worth more than the class-structure
|
|
94
|
+
* mechanism it supersedes.
|
|
95
|
+
*/
|
|
96
|
+
readonly name: string;
|
|
97
|
+
readonly line: number;
|
|
98
|
+
}
|
|
99
|
+
export interface Schema {
|
|
100
|
+
/** Slot -> the type name occupying it, as the document states or defaults it. */
|
|
101
|
+
readonly roots: ReadonlyMap<OperationSlot, string>;
|
|
102
|
+
/** Whether the roots came from a `schema` block or from the specification's defaults. */
|
|
103
|
+
readonly rootsDeclared: boolean;
|
|
104
|
+
readonly fields: ReadonlyMap<string, readonly RootField[]>;
|
|
105
|
+
readonly refused: readonly Refusal[];
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* The type-system definitions of one document.
|
|
109
|
+
*
|
|
110
|
+
* Returns `undefined` when the text yields no root operation type at all —
|
|
111
|
+
* an executable document, a fragment library, or a file that is not SDL.
|
|
112
|
+
*/
|
|
113
|
+
export declare function readSchema(text: string): Schema | undefined;
|
|
114
|
+
/** Every root operation the document declares, in slot then declaration order. */
|
|
115
|
+
export declare function rootFieldsOf(schema: Schema): readonly RootField[];
|
|
116
|
+
//# sourceMappingURL=document.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document.d.ts","sourceRoot":"","sources":["../src/document.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkEG;AAEH,wEAAwE;AACxE,eAAO,MAAM,eAAe,gDAAiD,CAAC;AAC9E,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAE7D;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAIlE,CAAC;AAIF,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,MAAM;IACrB,iFAAiF;IACjF,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;IACnD,yFAAyF;IACzF,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,CAAC,CAAC;IAC3D,QAAQ,CAAC,OAAO,EAAE,SAAS,OAAO,EAAE,CAAC;CACtC;AAkND;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CA8H3D;AAED,kFAAkF;AAClF,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,SAAS,EAAE,CAQjE"}
|
package/dist/document.js
ADDED
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading a GraphQL SDL document, and why this is the *strong* half of the
|
|
3
|
+
* GraphQL story rather than a convenience.
|
|
4
|
+
*
|
|
5
|
+
* `DEC-317` built GraphQL operation identity for Python/strawberry, and was
|
|
6
|
+
* explicit about the compromise it had to make. gRPC's precedent
|
|
7
|
+
* (`DEC-316`) rests on **"the path is read, not assembled"** — protoc writes
|
|
8
|
+
* `Service_Method_FullMethodName = "/pkg.Service/Method"` and the reader takes
|
|
9
|
+
* that literal verbatim. strawberry writes no such artifact: the schema is
|
|
10
|
+
* built at import time out of the class structure, the schema-facing name is
|
|
11
|
+
* camel-cased by default, and so `DEC-317` had to narrow itself to the three
|
|
12
|
+
* cases in which a name is genuinely *read* and refuse the rest as
|
|
13
|
+
* `graphqlFieldNameNotReadable`.
|
|
14
|
+
*
|
|
15
|
+
* **An SDL artifact removes the compromise entirely.** A field name in SDL is
|
|
16
|
+
* the schema-facing name — not a Python attribute that a naming converter will
|
|
17
|
+
* later transform, not a value a default might change, but the string the
|
|
18
|
+
* server publishes and a client types. There is nothing to compute, nothing to
|
|
19
|
+
* resolve and nothing to refuse on naming grounds. `DEC-317` §"The three
|
|
20
|
+
* sub-questions" says so outright: where an SDL artifact exists it "**is** the
|
|
21
|
+
* protoc-const analogue and should be preferred over this mechanism outright".
|
|
22
|
+
* This reader is that preference, built.
|
|
23
|
+
*
|
|
24
|
+
* ## Why a lexer and not a regex
|
|
25
|
+
*
|
|
26
|
+
* The naive rule — *a `Name` followed by `:` inside a type body is a field* —
|
|
27
|
+
* fails on the first real schema it meets, in four separate ways that Saleor's
|
|
28
|
+
* `saleor/graphql/schema.graphql` contains thousands of instances of:
|
|
29
|
+
*
|
|
30
|
+
* | construct | what the naive rule does |
|
|
31
|
+
* | --- | --- |
|
|
32
|
+
* | `webhook(id: ID!, filter: F = { channel: "x" }): Webhook` | emits `id`, `filter` and `channel` as operations |
|
|
33
|
+
* | `webhookEvents: [E!] @doc(category: "Webhooks")` | emits `category` |
|
|
34
|
+
* | `"""A description with a { brace and a colon: here."""` | emits whatever the prose contains |
|
|
35
|
+
* | `# a comment` | the same |
|
|
36
|
+
*
|
|
37
|
+
* So the document is tokenised properly — block strings, strings, comments and
|
|
38
|
+
* punctuation — and a field is admitted only in the one position the grammar
|
|
39
|
+
* puts one: a `Name`, at the type body's own brace depth and outside every
|
|
40
|
+
* parenthesis, followed (past a balanced argument list, if it has one) by `:`.
|
|
41
|
+
*
|
|
42
|
+
* ## Declared limits
|
|
43
|
+
*
|
|
44
|
+
* **`.graphql`/`.gql` only — an introspection `schema.json` is not read.**
|
|
45
|
+
* `DEC-317` names it as a third artifact that could carry the same truth, and
|
|
46
|
+
* it could. It is not built because **no repository on hand contains one**: a
|
|
47
|
+
* census of 52 reference repositories plus Saleor found two `.graphql` files
|
|
48
|
+
* and zero GraphQL introspection JSON documents. Building a second format
|
|
49
|
+
* reader against documentation with no source to measure it on is the thing
|
|
50
|
+
* `DEC-317`'s own graphene paragraph refuses to do, and it would be no more
|
|
51
|
+
* defensible here. Disclosed gap, not a silent one.
|
|
52
|
+
*
|
|
53
|
+
* **One file is one document.** A schema block naming a root type that the same
|
|
54
|
+
* file does not declare earns a ledger row rather than a search of the other
|
|
55
|
+
* `.graphql` files in the repository. SDL *can* legally be split across files,
|
|
56
|
+
* and where it is this reader under-reports and says so; both real artifacts
|
|
57
|
+
* measured here are single-file, so cross-file stitching would be reach built
|
|
58
|
+
* against zero demand, and a wrong stitch — pairing a schema block with a
|
|
59
|
+
* same-named type from an unrelated schema — is a silently wrong answer rather
|
|
60
|
+
* than a missing one.
|
|
61
|
+
*
|
|
62
|
+
* **Executable documents are not schemas.** A `.graphql` file holding
|
|
63
|
+
* `query IntrospectionQuery { ... }` or a fragment is a *client* document;
|
|
64
|
+
* twenty ships exactly one. It yields no type-system definition, so it yields
|
|
65
|
+
* no schema, and `readSchema` returns `undefined` rather than minting
|
|
66
|
+
* operations named after an introspection payload.
|
|
67
|
+
*/
|
|
68
|
+
/** GraphQL's three root operation types, fixed by the specification. */
|
|
69
|
+
export const OPERATION_SLOTS = ["query", "mutation", "subscription"];
|
|
70
|
+
/**
|
|
71
|
+
* The path segment each slot contributes, and the type name each slot defaults
|
|
72
|
+
* to when the document declares no `schema` block.
|
|
73
|
+
*
|
|
74
|
+
* **These two columns are the same word for a reason, and it is not a
|
|
75
|
+
* coincidence worth collapsing.** The left is GraphQL's operation-type
|
|
76
|
+
* vocabulary, which `DEC-317` puts in the path; the right is the specification's
|
|
77
|
+
* default root type name (October 2021 §3.3.1), which a `schema` block may
|
|
78
|
+
* override. Saleor overrides nothing and still writes the block; twenty writes
|
|
79
|
+
* no block at all. Keeping them apart is what makes `schema { query: RootQuery }`
|
|
80
|
+
* yield `/Query/…` rather than `/RootQuery/…`.
|
|
81
|
+
*/
|
|
82
|
+
export const OPERATION_TYPE = {
|
|
83
|
+
query: "Query",
|
|
84
|
+
mutation: "Mutation",
|
|
85
|
+
subscription: "Subscription",
|
|
86
|
+
};
|
|
87
|
+
const DEFAULT_ROOT_TYPE = OPERATION_TYPE;
|
|
88
|
+
const isNameStart = (c) => /[_A-Za-z]/.test(c);
|
|
89
|
+
const isNameChar = (c) => /[_0-9A-Za-z]/.test(c);
|
|
90
|
+
/**
|
|
91
|
+
* Tokens, with comments and commas dropped.
|
|
92
|
+
*
|
|
93
|
+
* Numbers and the rest of the value grammar are deliberately *not* classified:
|
|
94
|
+
* nothing this reader decides depends on telling `1` from `x`, and every value
|
|
95
|
+
* it might meet sits inside a parenthesis or a brace it is already skipping.
|
|
96
|
+
* A digit run therefore falls through to the single-character `punct` arm,
|
|
97
|
+
* which is harmless — it can never be mistaken for a `name`, which is the only
|
|
98
|
+
* classification that carries a decision.
|
|
99
|
+
*/
|
|
100
|
+
function tokenise(text) {
|
|
101
|
+
const tokens = [];
|
|
102
|
+
let index = 0;
|
|
103
|
+
let line = 1;
|
|
104
|
+
while (index < text.length) {
|
|
105
|
+
const char = text[index];
|
|
106
|
+
if (char === "\n") {
|
|
107
|
+
line += 1;
|
|
108
|
+
index += 1;
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
if (char === " " || char === "\t" || char === "\r" || char === "," || char === "") {
|
|
112
|
+
index += 1;
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
// A comment runs to end of line and can say anything at all, including
|
|
116
|
+
// something shaped exactly like a field.
|
|
117
|
+
if (char === "#") {
|
|
118
|
+
while (index < text.length && text[index] !== "\n")
|
|
119
|
+
index += 1;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
if (char === '"') {
|
|
123
|
+
const startLine = line;
|
|
124
|
+
// A block string is the case that matters: Saleor's descriptions contain
|
|
125
|
+
// braces, quotes and colons, and a reader that scanned to the next `"`
|
|
126
|
+
// would resume parsing in the middle of prose.
|
|
127
|
+
if (text.startsWith('"""', index)) {
|
|
128
|
+
index += 3;
|
|
129
|
+
while (index < text.length) {
|
|
130
|
+
if (text[index] === "\\" && text.startsWith('"""', index + 1)) {
|
|
131
|
+
index += 4;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (text.startsWith('"""', index)) {
|
|
135
|
+
index += 3;
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
if (text[index] === "\n")
|
|
139
|
+
line += 1;
|
|
140
|
+
index += 1;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
index += 1;
|
|
145
|
+
while (index < text.length && text[index] !== '"') {
|
|
146
|
+
if (text[index] === "\\")
|
|
147
|
+
index += 1;
|
|
148
|
+
if (text[index] === "\n")
|
|
149
|
+
line += 1;
|
|
150
|
+
index += 1;
|
|
151
|
+
}
|
|
152
|
+
index += 1;
|
|
153
|
+
}
|
|
154
|
+
tokens.push({ kind: "string", value: "", line: startLine });
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
if (isNameStart(char)) {
|
|
158
|
+
const start = index;
|
|
159
|
+
while (index < text.length && isNameChar(text[index]))
|
|
160
|
+
index += 1;
|
|
161
|
+
tokens.push({ kind: "name", value: text.slice(start, index), line });
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
tokens.push({ kind: "punct", value: char, line });
|
|
165
|
+
index += 1;
|
|
166
|
+
}
|
|
167
|
+
return tokens;
|
|
168
|
+
}
|
|
169
|
+
/** The index just past the balanced `(`…`)` or `{`…`}` opening at `from`. */
|
|
170
|
+
function skipBalanced(tokens, from, open, close) {
|
|
171
|
+
let depth = 0;
|
|
172
|
+
let index = from;
|
|
173
|
+
for (; index < tokens.length; index += 1) {
|
|
174
|
+
const token = tokens[index];
|
|
175
|
+
if (token.kind !== "punct")
|
|
176
|
+
continue;
|
|
177
|
+
if (token.value === open)
|
|
178
|
+
depth += 1;
|
|
179
|
+
else if (token.value === close) {
|
|
180
|
+
depth -= 1;
|
|
181
|
+
if (depth === 0)
|
|
182
|
+
return index + 1;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return tokens.length;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* The fields a type body declares, read from the token just past its `{`.
|
|
189
|
+
*
|
|
190
|
+
* A field is a `Name` at the body's own depth, outside every parenthesis,
|
|
191
|
+
* followed — past its argument list if it has one — by `:`. That trailing colon
|
|
192
|
+
* is what does the precision work: `@doc(category: "x")` puts `doc` in exactly
|
|
193
|
+
* the same position as a field name and is separated from one only by what
|
|
194
|
+
* comes after its parentheses.
|
|
195
|
+
*/
|
|
196
|
+
function fieldsIn(tokens, bodyStart) {
|
|
197
|
+
const fields = [];
|
|
198
|
+
let index = bodyStart;
|
|
199
|
+
let depth = 1;
|
|
200
|
+
while (index < tokens.length && depth > 0) {
|
|
201
|
+
const token = tokens[index];
|
|
202
|
+
if (token.kind === "punct") {
|
|
203
|
+
if (token.value === "{") {
|
|
204
|
+
depth += 1;
|
|
205
|
+
index += 1;
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
if (token.value === "}") {
|
|
209
|
+
depth -= 1;
|
|
210
|
+
index += 1;
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
if (token.value === "(") {
|
|
214
|
+
index = skipBalanced(tokens, index, "(", ")");
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
// `@` — the directive's own name follows and is not a field. Skipping it
|
|
218
|
+
// here is belt-and-braces: the trailing-colon test already rejects it.
|
|
219
|
+
if (token.value === "@") {
|
|
220
|
+
index += 2;
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
index += 1;
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
if (token.kind !== "name" || depth !== 1) {
|
|
227
|
+
index += 1;
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
let next = index + 1;
|
|
231
|
+
if (tokens[next]?.kind === "punct" && tokens[next].value === "(") {
|
|
232
|
+
next = skipBalanced(tokens, next, "(", ")");
|
|
233
|
+
}
|
|
234
|
+
if (tokens[next]?.kind === "punct" && tokens[next].value === ":") {
|
|
235
|
+
fields.push({ slot: "query", name: token.value, line: token.line });
|
|
236
|
+
index = next + 1;
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
index += 1;
|
|
240
|
+
}
|
|
241
|
+
return { fields, end: index };
|
|
242
|
+
}
|
|
243
|
+
/** `schema { query: Query … }` — the slot map, and a ledger row for any slot that is not a bare name. */
|
|
244
|
+
function readSchemaBlock(tokens, bodyStart, roots, refused) {
|
|
245
|
+
let index = bodyStart;
|
|
246
|
+
while (index < tokens.length) {
|
|
247
|
+
const token = tokens[index];
|
|
248
|
+
if (token.kind === "punct" && token.value === "}")
|
|
249
|
+
return index + 1;
|
|
250
|
+
const slot = OPERATION_SLOTS.find((candidate) => token.kind === "name" && token.value === candidate);
|
|
251
|
+
if (slot === undefined) {
|
|
252
|
+
index += 1;
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
if (!(tokens[index + 1]?.kind === "punct" && tokens[index + 1].value === ":")) {
|
|
256
|
+
index += 1;
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
const value = tokens[index + 2];
|
|
260
|
+
if (value?.kind === "name") {
|
|
261
|
+
roots.set(slot, value.value);
|
|
262
|
+
index += 3;
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
// `query: [Query]` and friends. Legal SDL forbids it, but a hand-edited or
|
|
266
|
+
// generated document can contain anything, and picking the name out of a
|
|
267
|
+
// wrapper would be a guess about what the server actually serves.
|
|
268
|
+
refused.push({
|
|
269
|
+
what: slot,
|
|
270
|
+
why: `the schema block's ${slot} slot is not a bare type name, so the root type it names cannot be read`,
|
|
271
|
+
line: token.line,
|
|
272
|
+
});
|
|
273
|
+
index += 2;
|
|
274
|
+
}
|
|
275
|
+
return index;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* The type-system definitions of one document.
|
|
279
|
+
*
|
|
280
|
+
* Returns `undefined` when the text yields no root operation type at all —
|
|
281
|
+
* an executable document, a fragment library, or a file that is not SDL.
|
|
282
|
+
*/
|
|
283
|
+
export function readSchema(text) {
|
|
284
|
+
const tokens = tokenise(text);
|
|
285
|
+
const roots = new Map();
|
|
286
|
+
const refused = [];
|
|
287
|
+
const byType = new Map();
|
|
288
|
+
let sawSchemaBlock = false;
|
|
289
|
+
let sawTypeDefinition = false;
|
|
290
|
+
let index = 0;
|
|
291
|
+
while (index < tokens.length) {
|
|
292
|
+
const token = tokens[index];
|
|
293
|
+
if (token.kind !== "name") {
|
|
294
|
+
index += 1;
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
if (token.value === "schema") {
|
|
298
|
+
// `schema @link(...) { … }` is legal; skip to the body the same way a
|
|
299
|
+
// type definition's directives are skipped.
|
|
300
|
+
let cursor = index + 1;
|
|
301
|
+
while (cursor < tokens.length) {
|
|
302
|
+
const next = tokens[cursor];
|
|
303
|
+
if (next.kind === "punct" && next.value === "(") {
|
|
304
|
+
cursor = skipBalanced(tokens, cursor, "(", ")");
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
if (next.kind === "punct" && next.value === "{")
|
|
308
|
+
break;
|
|
309
|
+
if (next.kind === "punct" && next.value === "}")
|
|
310
|
+
break;
|
|
311
|
+
cursor += 1;
|
|
312
|
+
}
|
|
313
|
+
if (tokens[cursor]?.kind === "punct" && tokens[cursor].value === "{") {
|
|
314
|
+
sawSchemaBlock = true;
|
|
315
|
+
index = readSchemaBlock(tokens, cursor + 1, roots, refused);
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
index += 1;
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
321
|
+
// `type X` and `extend type X`. Only object types can be a root operation
|
|
322
|
+
// type — an `input`, `interface`, `enum`, `union` or `scalar` never is.
|
|
323
|
+
const isExtend = token.value === "extend" && tokens[index + 1]?.value === "type";
|
|
324
|
+
if (token.value !== "type" && !isExtend) {
|
|
325
|
+
index += 1;
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
const nameToken = tokens[index + (isExtend ? 2 : 1)];
|
|
329
|
+
if (nameToken?.kind !== "name") {
|
|
330
|
+
index += 1;
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
// Past `implements A & B` and any directives, to the body.
|
|
334
|
+
let cursor = index + (isExtend ? 3 : 2);
|
|
335
|
+
while (cursor < tokens.length) {
|
|
336
|
+
const next = tokens[cursor];
|
|
337
|
+
if (next.kind === "punct" && next.value === "(") {
|
|
338
|
+
cursor = skipBalanced(tokens, cursor, "(", ")");
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
if (next.kind === "punct" && next.value === "{")
|
|
342
|
+
break;
|
|
343
|
+
// A type extension may have no body at all (`extend type X @dir`), and a
|
|
344
|
+
// bare `type X` ends where the next definition begins.
|
|
345
|
+
if (next.kind === "name" && (next.value === "type" || next.value === "extend" || next.value === "schema")) {
|
|
346
|
+
break;
|
|
347
|
+
}
|
|
348
|
+
cursor += 1;
|
|
349
|
+
}
|
|
350
|
+
if (!(tokens[cursor]?.kind === "punct" && tokens[cursor].value === "{")) {
|
|
351
|
+
index = cursor;
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
sawTypeDefinition = true;
|
|
355
|
+
const { fields, end } = fieldsIn(tokens, cursor + 1);
|
|
356
|
+
const existing = byType.get(nameToken.value);
|
|
357
|
+
if (existing === undefined)
|
|
358
|
+
byType.set(nameToken.value, fields);
|
|
359
|
+
else
|
|
360
|
+
existing.push(...fields);
|
|
361
|
+
index = end;
|
|
362
|
+
}
|
|
363
|
+
if (!sawTypeDefinition)
|
|
364
|
+
return undefined;
|
|
365
|
+
if (!sawSchemaBlock) {
|
|
366
|
+
// The specification's defaults. This assembles no name that is not written
|
|
367
|
+
// in the file: the *field* names are still read verbatim, and the three
|
|
368
|
+
// root type words are GraphQL's own, fixed by the grammar — the same
|
|
369
|
+
// footing on which `DEC-317` assembles the `/Query/` prefix from a slot.
|
|
370
|
+
for (const slot of OPERATION_SLOTS) {
|
|
371
|
+
if (byType.has(DEFAULT_ROOT_TYPE[slot]))
|
|
372
|
+
roots.set(slot, DEFAULT_ROOT_TYPE[slot]);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
// **Not `roots.size === 0`.** A document whose `schema` block declares roots
|
|
376
|
+
// this reader could not read has zero roots and something to say, and
|
|
377
|
+
// returning `undefined` there would drop the refusal on the floor — the
|
|
378
|
+
// adapter would report "no GraphQL here" about a file that plainly contains
|
|
379
|
+
// a schema. Silence is what rule 7 forbids; the empty result must arrive
|
|
380
|
+
// carrying its reasons.
|
|
381
|
+
if (roots.size === 0 && refused.length === 0)
|
|
382
|
+
return undefined;
|
|
383
|
+
const fields = new Map();
|
|
384
|
+
for (const [slot, typeName] of roots) {
|
|
385
|
+
const declared = byType.get(typeName);
|
|
386
|
+
if (declared === undefined) {
|
|
387
|
+
refused.push({
|
|
388
|
+
what: typeName,
|
|
389
|
+
why: `the schema block names ${typeName} as the ${slot} root, but this document does not declare it — ` +
|
|
390
|
+
"SDL split across files is not stitched, by declared limit",
|
|
391
|
+
line: 1,
|
|
392
|
+
});
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
// Illegal in valid SDL, but a duplicate would otherwise report one
|
|
396
|
+
// operation twice and inflate every count this adapter is measured by.
|
|
397
|
+
const seen = new Set();
|
|
398
|
+
fields.set(typeName, declared
|
|
399
|
+
.filter((field) => (seen.has(field.name) ? false : (seen.add(field.name), true)))
|
|
400
|
+
.map((field) => ({ ...field, slot })));
|
|
401
|
+
}
|
|
402
|
+
return { roots, rootsDeclared: sawSchemaBlock, fields, refused };
|
|
403
|
+
}
|
|
404
|
+
/** Every root operation the document declares, in slot then declaration order. */
|
|
405
|
+
export function rootFieldsOf(schema) {
|
|
406
|
+
const out = [];
|
|
407
|
+
for (const slot of OPERATION_SLOTS) {
|
|
408
|
+
const typeName = schema.roots.get(slot);
|
|
409
|
+
if (typeName === undefined)
|
|
410
|
+
continue;
|
|
411
|
+
out.push(...(schema.fields.get(typeName) ?? []));
|
|
412
|
+
}
|
|
413
|
+
return out;
|
|
414
|
+
}
|
|
415
|
+
//# sourceMappingURL=document.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document.js","sourceRoot":"","sources":["../src/document.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { ADAPTER_ID, ADAPTER_VERSION, DEFAULT_SCHEMA_GLOBS, GRAPHQL_METHOD, LANGUAGE, createGraphqlAdapter, operationPath, type GraphqlAdapterOptions, } from "./adapter.ts";
|
|
2
|
+
export { OPERATION_SLOTS, OPERATION_TYPE, readSchema, rootFieldsOf, type OperationSlot, type Refusal, type RootField, type Schema, } from "./document.ts";
|
|
3
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,eAAe,EACf,oBAAoB,EACpB,cAAc,EACd,QAAQ,EACR,oBAAoB,EACpB,aAAa,EACb,KAAK,qBAAqB,GAC3B,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,eAAe,EACf,cAAc,EACd,UAAU,EACV,YAAY,EACZ,KAAK,aAAa,EAClB,KAAK,OAAO,EACZ,KAAK,SAAS,EACd,KAAK,MAAM,GACZ,MAAM,eAAe,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,eAAe,EACf,oBAAoB,EACpB,cAAc,EACd,QAAQ,EACR,oBAAoB,EACpB,aAAa,GAEd,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,eAAe,EACf,cAAc,EACd,UAAU,EACV,YAAY,GAKb,MAAM,eAAe,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@descryy/adapter-graphql",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "GraphQL SDL adapter. Reaches R3 with no language adapter at all \u2014 a published schema states its own operations, and their names are read rather than computed.",
|
|
6
|
+
"license": "UNLICENSED",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=22.5"
|
|
9
|
+
},
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"registry": "https://registry.npmjs.org",
|
|
21
|
+
"access": "public"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsc -b"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@descryy/adapter-common": "0.1.0",
|
|
28
|
+
"@descryy/ir": "0.3.0",
|
|
29
|
+
"@descryy/adapter-python": "0.1.0"
|
|
30
|
+
}
|
|
31
|
+
}
|