@nxgt/shared-graphql 2.1.0 → 3.0.1

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 (68) hide show
  1. package/README.md +112 -52
  2. package/dist/directives/authenticated.d.ts +42 -0
  3. package/dist/directives/authenticated.d.ts.map +1 -0
  4. package/dist/directives/federation.d.ts +6 -0
  5. package/dist/directives/federation.d.ts.map +1 -1
  6. package/dist/directives/index.d.ts +1 -2
  7. package/dist/directives/index.d.ts.map +1 -1
  8. package/dist/directives/permission.d.ts +20 -6
  9. package/dist/directives/permission.d.ts.map +1 -1
  10. package/dist/directives/removed-check.d.ts +15 -0
  11. package/dist/directives/removed-check.d.ts.map +1 -0
  12. package/dist/directives/scope.d.ts +3 -3
  13. package/dist/directives/scope.d.ts.map +1 -1
  14. package/dist/directives/sdl.d.ts +14 -4
  15. package/dist/directives/sdl.d.ts.map +1 -1
  16. package/dist/directives/validate.d.ts +8 -14
  17. package/dist/directives/validate.d.ts.map +1 -1
  18. package/dist/index.js +1037 -926
  19. package/dist/index.js.map +35 -31
  20. package/dist/integrations/hono.d.ts +2 -2
  21. package/dist/integrations/hono.d.ts.map +1 -1
  22. package/dist/plugins/apply-authenticated.d.ts +21 -0
  23. package/dist/plugins/apply-authenticated.d.ts.map +1 -0
  24. package/dist/plugins/apply-keto-checks.d.ts +10 -7
  25. package/dist/plugins/apply-keto-checks.d.ts.map +1 -1
  26. package/dist/plugins/auth.d.ts +17 -1
  27. package/dist/plugins/auth.d.ts.map +1 -1
  28. package/dist/plugins/authenticated.d.ts +14 -0
  29. package/dist/plugins/authenticated.d.ts.map +1 -0
  30. package/dist/plugins/extract-jwt.d.ts +19 -6
  31. package/dist/plugins/extract-jwt.d.ts.map +1 -1
  32. package/dist/plugins/field-guard.d.ts +30 -0
  33. package/dist/plugins/field-guard.d.ts.map +1 -0
  34. package/dist/plugins/gateway-trust.d.ts +15 -0
  35. package/dist/plugins/gateway-trust.d.ts.map +1 -0
  36. package/dist/plugins/index.d.ts +3 -0
  37. package/dist/plugins/index.d.ts.map +1 -1
  38. package/dist/plugins/keto-checks.d.ts +3 -2
  39. package/dist/plugins/keto-checks.d.ts.map +1 -1
  40. package/dist/plugins/keto-helpers.d.ts +1 -1
  41. package/dist/plugins/keto-helpers.d.ts.map +1 -1
  42. package/dist/plugins/ory-auth.d.ts +3 -3
  43. package/dist/shared.d.ts +1 -1
  44. package/dist/shared.d.ts.map +1 -1
  45. package/dist/utils/errors/denial.d.ts +20 -0
  46. package/dist/utils/errors/denial.d.ts.map +1 -0
  47. package/dist/utils/errors/format-error.d.ts +2 -1
  48. package/dist/utils/errors/format-error.d.ts.map +1 -1
  49. package/dist/utils/errors/index.d.ts +1 -0
  50. package/dist/utils/errors/index.d.ts.map +1 -1
  51. package/dist/utils/errors/mask-error.d.ts +2 -1
  52. package/dist/utils/errors/mask-error.d.ts.map +1 -1
  53. package/dist/utils/ws-context.d.ts +1 -1
  54. package/docs/README.md +8 -6
  55. package/docs/guide/authentication.md +187 -0
  56. package/docs/guide/errors.md +37 -5
  57. package/docs/guide/migrating-to-3.md +276 -0
  58. package/docs/guide/permissions.md +39 -50
  59. package/docs/roadmap.md +30 -29
  60. package/docs/troubleshooting.md +151 -48
  61. package/package.json +7 -11
  62. package/dist/directives/check.d.ts +0 -32
  63. package/dist/directives/check.d.ts.map +0 -1
  64. package/dist/directives/deprecation.d.ts +0 -7
  65. package/dist/directives/deprecation.d.ts.map +0 -1
  66. package/dist/directives/requirements.d.ts +0 -14
  67. package/dist/directives/requirements.d.ts.map +0 -1
  68. package/graphql/directives/check.graphqls +0 -90
@@ -1,17 +1,80 @@
1
1
  # Troubleshooting `@nxgt/shared-graphql`
2
2
 
3
3
  Each entry is headed by the message you see; the parts in `<angle brackets>`
4
- vary. Messages thrown when the schema is built are `TypeError`s naming
5
- `Type.field`, and stop the server from booting. They start `@permission on`
6
- or `@check on`, after the directive that carries the mistake; the entries show
7
- one of the two. The last section holds the
8
- traps that throw nothing.
4
+ vary. Messages thrown when the server starts or the schema is built are
5
+ `TypeError`s, and stop the server from booting; the schema ones name
6
+ `Type.field` after the directive that carries the mistake. The last section
7
+ holds the traps that throw nothing.
8
+
9
+ Upgrading from 2.x, most of these come from [the
10
+ migration](./guide/migrating-to-3.md).
11
+
12
+ ## When the server starts
13
+
14
+ ### ``useAuth(): name the gateway allowed to set the caller — useAuth({ trustedGateway: gatewaySecret({ secret }) }). …``
15
+
16
+ Also `extractJwtPlugin(): name the gateway allowed to set the caller — …`.
17
+
18
+ Since 3.0 both need to know which gateway may put the caller in the request's
19
+ `extensions`, which a client can otherwise write. Pass one:
20
+
21
+ ```ts
22
+ const trustedGateway = gatewaySecret({ secret: process.env.GATEWAY_SECRET! });
23
+ createYoga({ plugins: [useAuth({ trustedGateway })] });
24
+ new ApolloServer({ plugins: [extractJwtPlugin({ trustedGateway })] });
25
+ ```
26
+
27
+ `extractJwtPlugin` is a function now: `plugins: [extractJwtPlugin]` passes
28
+ the function itself, not a plugin. An API that authenticates its own callers
29
+ uses `useOryAuth(ory)` and needs neither.
30
+
31
+ ### ``gatewaySecret(): `secret` must be a string of at least 16 characters — is its environment variable set?``
32
+
33
+ The secret is missing or shorter than 16 characters — most often an
34
+ environment variable that is not set where the server runs. Set it, to the
35
+ same value the gateway sends.
36
+
37
+ ### `Cannot find module '@envelop/generic-auth'`
38
+
39
+ Or `@graphql-hive/gateway`, `@envelop/extended-validation`,
40
+ `@hono/zod-validator`, `zod`. 3.0 dropped them from its dependencies — nothing
41
+ here imported them — and your app was using the copy this package brought.
42
+ Declare it yourself:
43
+
44
+ ```sh
45
+ bun add @envelop/generic-auth
46
+ ```
47
+
48
+ ### ``Module '"@nxgt/shared-graphql"' has no exported member 'sandboxExpolorer'. Did you mean 'sandboxExplorer'?``
49
+
50
+ The typo was corrected in 3.0, with no alias. Import `sandboxExplorer`.
9
51
 
10
52
  ## When the schema is built
11
53
 
54
+ ### ``@check on <Type.field>: @check was removed in @nxgt/shared-graphql 3.0 — write one @permission(name: "<permit>", type: "<namespace>") per term, and move an OR into the Keto model``
55
+
56
+ The schema declares 2.x's `@check` itself and a field still uses it. This
57
+ package no longer answers it, so the field would boot unguarded; it is refused
58
+ instead. Rewrite it:
59
+
60
+ ```graphql
61
+ # before
62
+ note(id: ID!): Note @check(permissions: [[{ namespace: "Note", permit: "view" }]])
63
+ # after
64
+ note(id: ID!): Note @permission(name: "view", type: "Note")
65
+ ```
66
+
67
+ Then drop your `@check` declaration. [The migration guide](./guide/migrating-to-3.md#2-check-is-removed)
68
+ covers an AND and an OR.
69
+
70
+ ### `Unknown directive "@check".`
71
+
72
+ The same, in a schema that loads this package's `graphql/` and nothing else:
73
+ 3.0 no longer ships `@check`'s declaration. Rewrite the field as above.
74
+
12
75
  ### ``@permission on <Type.field>: `id` must be "args.<path>", "parent.<path>" or "source.<path>", got "<id>"``
13
76
 
14
- The `id` argument (or a `@check` term's `id`) names no root. Prefix it:
77
+ The `id` argument names no root. Prefix it:
15
78
 
16
79
  ```graphql
17
80
  note(noteId: ID!): Note @permission(name: "view", type: "Note", id: "args.noteId")
@@ -24,28 +87,12 @@ The path reads an argument the field does not have — usually the default
24
87
  an undeclared argument on `args`, so every request would have failed. Name the
25
88
  argument in `id`, or read the parent with `parent.<path>`.
26
89
 
27
- ### `@check on <Type.field>: an empty group admits EVERYONE — a conjunction over no terms is true`
28
-
29
- `@check(permissions: [[]])`. An AND over nothing is true, so the field would
30
- be open to every signed-in caller while looking guarded. Name a permission,
31
- or remove the directive.
90
+ In 2.x a `@check` booted with this and logged a warning; a `@permission`
91
+ refuses it.
32
92
 
33
- ### `@check on <Type.field>: an empty permission requirement admits nobody — remove it, or name a permission`
93
+ ### ``@permission on <Type.field>: every term needs `namespace`, `permit` and `id`, got <term>``
34
94
 
35
- `@check(permissions: [])`: an OR over nothing refuses everyone.
36
-
37
- ### ``<…> — @check still boots with this; the next major refuses it, as @permission does``
38
-
39
- A warning, not an error: one of the two mistakes above — an argument the
40
- field does not declare, or a guard on an interface field — on a `@check`. The
41
- server boots as it did in 2.x, but the field answers 500 on every request, or
42
- is not guarded at all. Fix it as the entry for the same message describes; the
43
- next major refuses it at build.
44
-
45
- ### ``@check on <Type.field>: every term needs `namespace`, `permit` and `id`, got <term>``
46
-
47
- A term, or a `@permission`, has an empty `namespace`/`type` or
48
- `permit`/`name` — `@permission(name: "", type: "Note")`. Name both.
95
+ `@permission(name: "", type: "Note")`: an empty `name` or `type`. Name both.
49
96
 
50
97
  ### `@permission on <Type.field>: unknown namespace "<type>" — known: <namespaces>`
51
98
 
@@ -54,7 +101,7 @@ names one outside them — most often a typo. Keto would have answered `false`
54
101
  for ever, without an error. Fix the name, or add the namespace to the list
55
102
  when the OPL document gained it.
56
103
 
57
- ### `<Type.field>: @check / @permission on an interface field guards nothing — no resolver runs there. Put it on each implementing type's field`
104
+ ### `<Type.field>: @permission on an interface field guards nothing — no resolver runs there. Put it on each implementing type's field`
58
105
 
59
106
  A resolver runs on an object type's field, never on an interface's. Move the
60
107
  directive to every implementing type:
@@ -67,6 +114,39 @@ type Note implements Node {
67
114
  }
68
115
  ```
69
116
 
117
+ In 2.x a `@check` there booted with a warning; a `@permission` is refused.
118
+
119
+ ### ``@authenticated on <where>: `type: []` admits no caller — name a type, or drop `type` ``
120
+
121
+ `<where>` is where the directive sits: `Type.field`, or a type, interface,
122
+ scalar or enum name.
123
+
124
+ An empty list admits nobody. Name the kinds of caller, or drop `type` to
125
+ admit any caller.
126
+
127
+ ### `@authenticated on <where>: unknown type "<type>" — known: <types>`
128
+
129
+ `type:` names a value outside the caller types `useAuthenticated` knows —
130
+ `session` and `token` by default. Fix the name, or list your own:
131
+
132
+ ```ts
133
+ useAuthenticated({ types: [...CALLER_TYPES, 'service'] });
134
+ ```
135
+
136
+ ### ``@authenticated on <Type.field>: the field's, its type's and its interfaces' `type`s have none in common — no caller could pass``
137
+
138
+ Every `@authenticated` that applies to a field must hold — the field's, its
139
+ type's, its interfaces'. Two of them name disjoint types, so no caller could
140
+ reach the field. Drop or widen one.
141
+
142
+ ### `Unknown argument "type" on directive "@authenticated".`
143
+
144
+ The schema declares `@authenticated` without an argument — federation's
145
+ declaration, imported through `@link`, or your own — and a field writes
146
+ `type:`. In a subgraph keep federation's form and drop `type:`; in a schema
147
+ of your own, load `SHARED_TYPE_DEFS` or `AUTHENTICATED_DIRECTIVE_SDL` in place
148
+ of your declaration.
149
+
70
150
  ### `@nxgt/shared-graphql: no package root`
71
151
 
72
152
  Thrown when the package is imported, if no `package.json` sits anywhere
@@ -93,7 +173,7 @@ alone.
93
173
  ### `Cannot use GraphQLSchema "<schema>" from another module or realm.`
94
174
 
95
175
  Two copies of `graphql` are installed: `graphql` is a peer of this package
96
- (`^16.4.2 || ^17.0.0`), so your app must declare it once, and every GraphQL
176
+ (`^16.9.0 || ^17.0.0`), so your app must declare it once, and every GraphQL
97
177
  library must resolve that one. Add `graphql` to your `dependencies` if it was
98
178
  only there through this package, and check with `bun pm ls graphql` (or
99
179
  `npm ls graphql`) that one version remains. graphql runs this check only
@@ -102,13 +182,12 @@ with less telling errors.
102
182
 
103
183
  ## When a request runs
104
184
 
105
- The three messages below are plain `Error`s thrown inside a resolver:
106
- `createMaskError` answers the client `Unexpected error.`
107
- (`INTERNAL_SERVER_ERROR`). The text is in the server log, and in
108
- `extensions.debugMessage` when `isDev` is on.
109
-
110
185
  ### `<Type.field>: "<path>" resolved no object id`
111
186
 
187
+ A plain `Error` thrown inside a resolver: `createMaskError` answers the client
188
+ `Unexpected error.` (`INTERNAL_SERVER_ERROR`); the text is in the server log,
189
+ and in `extensions.debugMessage` when `isDev` is on.
190
+
112
191
  The path was valid, but at request time it pointed at nothing — a nullable
113
192
  argument nobody passed, an empty list, a parent field the parent resolver did
114
193
  not load. It is a 500, never an allow. Make the argument required, or make
@@ -118,7 +197,7 @@ sure the parent object carries the field.
118
197
 
119
198
  The schema was transformed with `applyKetoChecks` but the plugin that puts the
120
199
  per-request checker on the context is missing. Register
121
- `useKetoChecks(ory)` after `useOryAuth(ory)`.
200
+ `useKetoChecks(ory)` after `useOryAuth(ory)`. Answered as the previous entry.
122
201
 
123
202
  ### `can(): no checker on the context — useKetoChecks(ory) is not registered`
124
203
 
@@ -130,10 +209,12 @@ Keto (or Kratos, Hydra — the name varies) did not answer. This is on
130
209
  purpose: an outage is never turned into a denial or an anonymous caller. The
131
210
  client can retry; `extensions.debugMessage` carries what the SDK saw.
132
211
 
133
- ### `Unexpected error.` with `INTERNAL_SERVER_ERROR` where a 404 or 403 was expected
212
+ ### A denial's message reads `notes.errors.not-found`
134
213
 
135
- `createMaskError` is not registered, so Yoga masks the `CustomException` a
136
- denial throws as it masks any non-GraphQL error:
214
+ A `@permission(message: "notes.errors.not-found")`, or your own
215
+ `denial(code, key)`, reached the client untranslated. A denial translates its
216
+ key with `@nxgt/i18n`'s own resources, which do not hold your app's keys.
217
+ Register the mask with your translator:
137
218
 
138
219
  ```ts
139
220
  createYoga({ maskedErrors: { maskError: createMaskError(translate) } });
@@ -141,30 +222,52 @@ createYoga({ maskedErrors: { maskError: createMaskError(translate) } });
141
222
 
142
223
  Under Apollo Server, the counterpart is `formatError: createFormatError(translate)`.
143
224
 
225
+ ### `Unexpected error.` with `INTERNAL_SERVER_ERROR` where a service's 404 or 403 was expected
226
+
227
+ A service threw a `CustomException` and `createMaskError` is not registered,
228
+ so Yoga masks it as any non-GraphQL error. The directives and `requireUser` /
229
+ `can` are not concerned — their denials carry their own status — but a
230
+ service's `CustomException.notFound()` is. Register `createMaskError`, as
231
+ above, or throw `denial(code, message)` from the resolver.
232
+
144
233
  ### `Unexpected error.` where a resolver's own message used to reach the client
145
234
 
146
- Since this release `createMaskError` masks a plain `Error` a resolver threw,
147
- as Yoga's default does — before, its message (a driver error, a host name)
148
- reached the client. Throw a `CustomException` or a `GraphQLError` for a
235
+ `createMaskError` masks a plain `Error` a resolver threw, as Yoga's default
236
+ does. Throw a `CustomException`, a `denial()` or a `GraphQLError` for a
149
237
  message meant for the caller.
150
238
 
151
239
  ## Traps that throw nothing
152
240
 
241
+ ### Every caller is anonymous behind the gateway
242
+
243
+ `useAuth({ trustedGateway })` reads no caller from a request that does not
244
+ carry the gateway's proof. The gateway is not sending the header — or sends
245
+ another secret — on its subgraph requests. Make it add
246
+ `x-gateway-secret: <secret>` (or your `header`) to every one, with the same
247
+ value the subgraph was given.
248
+
249
+ ### `FORBIDDEN` from `@authenticated(type:)` for a caller who should pass
250
+
251
+ The caller's type is `context.ory.kind`, or `context.user.tokenType` without
252
+ Ory. Behind a gateway that writes no `tokenType` in the `user` it forwards,
253
+ every caller has no type, and every `type:` refuses them. Make the gateway
254
+ write it, or drop `type:` on those fields.
255
+
153
256
  ### A guarded field answers unguarded in the supergraph
154
257
 
155
258
  A subgraph composed by federation drops a directive it was not told to keep.
156
- Add `@composeDirective(name: "@permission")` (and `"@check"` if used) and
157
- import the directive in the subgraph's `@link`.
259
+ Add `@composeDirective(name: "@permission")` and import the directive in the
260
+ subgraph's `@link`.
158
261
 
159
- ### `useAuth()` trusts the request body
262
+ ### `@authenticated(type:)` in a subgraph
160
263
 
161
- `useAuth()` copies `user` and `token` from the GraphQL request's
162
- `extensions`, which a client writes. It is safe only behind a gateway that
163
- sets them and a network that stops callers from reaching the service
164
- directly. An API that resolves its own callers uses `useOryAuth(ory)`.
264
+ Federation declares `@authenticated` without an argument, and a subgraph
265
+ imports that declaration. Keep it: `useAuthenticated` enforces it as "any
266
+ caller". A `type:` restriction is for a schema that declares `@authenticated`
267
+ through `SHARED_TYPE_DEFS` or `AUTHENTICATED_DIRECTIVE_SDL`.
165
268
 
166
269
  ### `OryForbidden` from `ory.requireAllowed` is a 500
167
270
 
168
271
  `createMaskError` does not map `OryForbidden`: whether a refusal is a 404 or a
169
272
  403 is the caller's decision. In a resolver, use `can` and throw the
170
- `CustomException` you mean.
273
+ `denial` you mean.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxgt/shared-graphql",
3
- "version": "2.1.0",
3
+ "version": "3.0.1",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -49,16 +49,12 @@
49
49
  "dependencies": {
50
50
  "@apollo/server": "^5.5.1",
51
51
  "@apollo/subgraph": "^2.14.3",
52
- "@envelop/extended-validation": "^7.1.1",
53
- "@envelop/generic-auth": "^11.1.1",
54
- "@graphql-hive/gateway": "^2.11.2",
55
52
  "@graphql-tools/load-files": "^7.0.1",
56
53
  "@graphql-tools/merge": "^9.2.3",
57
54
  "@graphql-tools/utils": "^12.0.0",
58
55
  "@graphql-yoga/redis-event-target": "^3.0.3",
59
- "@hono/zod-validator": "^0.9.0",
60
56
  "@nxgt/i18n": "^1.1.0",
61
- "@nxgt/security": "^4.0.1",
57
+ "@nxgt/security": "^4.2.0",
62
58
  "@nxgt/shared": "^1.0.5",
63
59
  "@nxgt/shared-exceptions": "^1.0.4",
64
60
  "@nxgt/shared-logging": "^1.0.4",
@@ -72,19 +68,19 @@
72
68
  "hono": "^4.13.4",
73
69
  "ioredis": "^6.0.0",
74
70
  "lodash": "^4.18.1",
75
- "ws": "^8.21.3",
76
- "zod": "^4.4.3"
71
+ "ws": "^8.21.3"
77
72
  },
78
73
  "devDependencies": {
74
+ "@graphql-tools/schema": "^10.1.0",
79
75
  "@types/bun": "^1.4.0",
80
76
  "@types/graphql-upload": "^17.0.0",
81
77
  "@types/lodash": "^4.17.25",
82
78
  "@types/ws": "^8.18.1",
83
- "graphql": "^16.4.2"
79
+ "graphql": "^16.9.0"
84
80
  },
85
81
  "peerDependencies": {
86
- "@nxgt/ory-sdk": ">=0.1.0",
87
- "graphql": "^16.4.2 || ^17.0.0",
82
+ "@nxgt/ory-sdk": ">=0.1.0 <1",
83
+ "graphql": "^16.9.0 || ^17.0.0",
88
84
  "stx-sdk": ">=1.1.0",
89
85
  "typescript": "^6.0.3"
90
86
  }
@@ -1,32 +0,0 @@
1
- import type { PermissionRequirement } from '@nxgt/ory-sdk';
2
- import type { GraphQLSchema } from 'graphql';
3
- import { type FieldNode, type ReadOptions } from './scope';
4
- /**
5
- * `@check` — the list-of-lists form of `@permission`, deprecated in its
6
- * favour and kept working.
7
- *
8
- * The declaration itself is SDL, in `graphql/directives/check.graphqls`, so it
9
- * reaches every consumer of `SHARED_SCHEMA_PATH`; `CHECK_DIRECTIVE_SDL` is the
10
- * same text as a string, held equal by a spec. This module only reads it.
11
- */
12
- export declare const CHECK_DIRECTIVE_NAME = "check";
13
- export type CheckDenial = 'NOT_FOUND' | 'FORBIDDEN';
14
- export type CheckArgs = {
15
- permissions: PermissionRequirement;
16
- onDeny: CheckDenial;
17
- /** i18n key; the shared `errors.*` one when the field does not say. */
18
- message?: string;
19
- };
20
- /**
21
- * Every `@check` on a field, in declaration order, validated.
22
- *
23
- * `@check` is repeatable, so this is a list and the order is load-bearing:
24
- * `view` then `edit` is what turns a denial into 404 for a stranger and 403
25
- * for a viewer.
26
- *
27
- * The `id` and `onDeny` defaults are applied here as well as in the SDL:
28
- * graphql 17 no longer hands an input field's default to `getDirective`, and a
29
- * term without an id would otherwise refuse the whole schema.
30
- */
31
- export declare function readChecks(schema: GraphQLSchema, node: FieldNode, where: string, options?: ReadOptions): CheckArgs[];
32
- //# sourceMappingURL=check.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/directives/check.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAkB,MAAM,eAAe,CAAC;AAC3E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAG7C,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,WAAW,EAAW,MAAM,SAAS,CAAC;AAGpE;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,UAAU,CAAC;AAE5C,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,WAAW,CAAC;AAEpD,MAAM,MAAM,SAAS,GAAG;IACvB,WAAW,EAAE,qBAAqB,CAAC;IACnC,MAAM,EAAE,WAAW,CAAC;IACpB,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CACzB,MAAM,EAAE,aAAa,EACrB,IAAI,EAAE,SAAS,EACf,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,WAAgB,GACvB,SAAS,EAAE,CAgBb"}
@@ -1,7 +0,0 @@
1
- /**
2
- * How a `@check` mistake that 2.x let boot is reported: a warning at build,
3
- * naming the field, that says it becomes a refusal in the next major.
4
- * `@permission` is new, so the same mistake there throws.
5
- */
6
- export declare function warnCheckMistake(message: string): void;
7
- //# sourceMappingURL=deprecation.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"deprecation.d.ts","sourceRoot":"","sources":["../../src/directives/deprecation.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,QAI/C"}
@@ -1,14 +0,0 @@
1
- import type { GraphQLSchema } from 'graphql';
2
- import { type CheckArgs } from './check';
3
- import type { FieldNode, ReadOptions } from './scope';
4
- /**
5
- * Every `@check` and `@permission` on a field, validated, in the order they
6
- * are written — across the two names, so a field migrating one directive at a
7
- * time keeps its 404-then-403 ladder.
8
- *
9
- * `getDirective` answers per name, so the interleaving is read back off the
10
- * field's AST. A field built without one (a schema made in code) has no order
11
- * to keep across names, and gets its `@check`s first.
12
- */
13
- export declare function readRequirements(schema: GraphQLSchema, node: FieldNode, where: string, options?: ReadOptions): CheckArgs[];
14
- //# sourceMappingURL=requirements.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"requirements.d.ts","sourceRoot":"","sources":["../../src/directives/requirements.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,aAAa,EAAE,MAAM,SAAS,CAAC;AAC5D,OAAO,EAAwB,KAAK,SAAS,EAAc,MAAM,SAAS,CAAC;AAE3E,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEtD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC/B,MAAM,EAAE,aAAa,EACrB,IAAI,EAAE,SAAS,EACf,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,WAAgB,GACvB,SAAS,EAAE,CAeb"}
@@ -1,90 +0,0 @@
1
- # The Ory-native counterpart to @policy.
2
- #
3
- # @policy asks whether the caller CARRIES an authority. An Ory principal
4
- # carries none — `toPrincipal` sets `authorities: []` on purpose — because
5
- # Keto answers per OBJECT: "may this subject `view` `Note:<id>`". @check is how
6
- # a schema asks that, and `useKetoChecks(ory)` is what answers it.
7
- #
8
- # It lives here, in the shipped SDL, rather than in SHARED_TYPE_DEFS, so that
9
- # every consumer of SHARED_SCHEMA_PATH sees it — the subgraphs built with
10
- # `buildSubgraphSchema` included, which never load SHARED_TYPE_DEFS.
11
-
12
- """
13
- One question, with the object left as a path instead of a value.
14
- """
15
- input CheckPermission {
16
- """
17
- The Keto namespace, e.g. "Note" — from the stack's OPL document.
18
- """
19
- namespace: String!
20
-
21
- """
22
- The permit asked of it, e.g. "view". A plain relation works too; Keto
23
- answers `false`, not an error, for a name it does not know.
24
- """
25
- permit: String!
26
-
27
- """
28
- Where the object id is read: `args.<path>`, or `parent.<path>` /
29
- `source.<path>` (the same root under two names), dotted paths allowed. A value that turns out to be a LIST requires the permit on every
30
- element — that is what a mutation taking `ids: [ID!]!` means.
31
- """
32
- id: String = "args.id"
33
- }
34
-
35
- """
36
- What a denial looks like from outside.
37
- """
38
- enum CheckDenial {
39
- """
40
- The same answer as for an id that never existed, so ids cannot be probed.
41
- The default, and the one to keep unless the caller already knows the object
42
- is there.
43
- """
44
- NOT_FOUND
45
-
46
- """
47
- For a second check on an object the caller can already see: a viewer asked
48
- to edit already knows it exists, and "you may not change it" is honest.
49
- """
50
- FORBIDDEN
51
- }
52
-
53
- """
54
- Deprecated: write `@permission(name:, type:)` in a new schema — one Keto
55
- question per directive, repeated for AND, with OR kept in the Keto model. This
56
- form keeps working and is evaluated in declaration order with @permission.
57
-
58
- The permission a field requires, in disjunctive normal form: the OUTER list is
59
- OR, the INNER list is AND. `[[A, B], [C]]` reads "(A and B) or C" — the same
60
- shape `@policy(policies: [["ADMIN"]])` uses.
61
-
62
- Repeatable, and evaluated in declaration order, each with its own `onDeny`.
63
- That is how the 404-then-403 ladder is written:
64
-
65
- updateNote(id: ID!, input: UpdateNoteInput!): Note!
66
- @check(permissions: [[{ namespace: "Note", permit: "view" }]])
67
- @check(permissions: [[{ namespace: "Note", permit: "edit" }]], onDeny: FORBIDDEN)
68
-
69
- NOT for a field that answers a LIST the caller is entitled to. "Which notes may
70
- I see" is not a check, it is a Keto query folded into the database filter
71
- before the read. A directive there would have to fetch everything and filter
72
- after, which makes `totalCount` and the cursors lie.
73
- """
74
- directive @check(
75
- permissions: [[CheckPermission!]!]!
76
- onDeny: CheckDenial! = NOT_FOUND
77
-
78
- """
79
- The i18n key the denial carries, e.g. "notes.errors.not-found". Defaults to
80
- `errors.not-found` / `errors.insufficient-permissions`, the shared keys.
81
-
82
- Set it whenever the API's own service layer answers the same refusal with a
83
- domain message. Two layers guard these fields — the directive, and the
84
- `require<M>Access` the service calls — and if they word the same 404
85
- differently, the wording tells a caller WHICH one refused: a generic message
86
- means "you may not", a domain one means "it is gone". That is precisely the
87
- distinction NOT_FOUND exists to hide.
88
- """
89
- message: String
90
- ) repeatable on FIELD_DEFINITION