@nxgt/janus-graphql 0.3.0 → 0.4.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/README.md CHANGED
@@ -79,7 +79,7 @@ that reads files — `node_modules/@nxgt/janus-graphql/graphql/janus.graphqls`.
79
79
 
80
80
  | Export | What it is |
81
81
  | --- | --- |
82
- | `useJanus({ auth, access?, type?, clock?, loaders?, conditions? })` | The envelop plugin. Adds `ctx.janus` to every request's context, and applies the directives to every schema the server is given, once. `type` treats a user of any other type as anonymous. `clock` is what `@fresh` and `requireFresh()` read — the one given to `janus()`, when it is not the system's. `loaders` and `conditions` are what `@permission` needs beside `access` |
82
+ | `useJanus({ auth, access?, type?, clock?, loaders?, conditions? })` | The envelop plugin. Adds `ctx.janus` to every request's context, and applies the directives to every schema the server is given, once. `type` treats a user of any other type as anonymous. `clock` is what `@fresh` and `requireFresh()` read — the one given to `janus()`, when it is not the system's. `loaders` and `conditions` are what `@permission` needs beside `access`. Under Yoga, a session `authenticate` renewed is sent again as `Set-Cookie` on the response, to a request that presented it as the cookie |
83
83
  | `ctx.janus.user()`, `ctx.janus.session()` | Who the request belongs to, and the session it presented — `null` for an anonymous request. `auth.authenticate(request)` runs the first time either is asked, once per request, and never when neither is |
84
84
  | `ctx.janus.access` | The `permissions()` instance given to `useJanus()`. Absent from the type — and from the context — without one |
85
85
  | `janusTypeDefs` | The SDL: `@authenticated`, `@fresh`, `@permission` and the `JanusPermissionDenial` enum — prefixed, so it never collides with a type of your schema. The same text as `graphql/janus.graphqls` |
@@ -93,11 +93,11 @@ that reads files — `node_modules/@nxgt/janus-graphql/graphql/janus.graphqls`.
93
93
  | `requireFresh(ctx, maxAge)` | The request's session, once it proved who it is less than `maxAge` ago — a duration with its unit, `'10m'`, never a bare number — or a denial: `UNAUTHENTICATED`, `STEP_UP_REQUIRED` |
94
94
  | `can(ctx, permission, object, options?)` | `access.can` for the request's user, typed as `access.can` is. Anonymous answers `false`. Shares the request's checks with `@permission`: one question, one check per request |
95
95
  | `janusConnection({ auth, access?, type?, clock?, upgrade? })` | Subscriptions over graphql-ws: `onConnect` for its `useServer()`, which accepts a connection whose `connectionParams.authorization` — else whose upgrade request's headers or cookie — authenticates, as a user of `type` when given, and refuses any other `4403`; an outage rejects, closed `4500`. Each operation's `ctx.janus` then authenticates from that credential, so the directives hold unchanged. `context` builds `{ janus }` for a server without Yoga; `upgrade` reads the upgrade request where the transport's `extra` does not carry it — Bun |
96
- | `janusMaskError(fallback?)` | Yoga's `maskedErrors.maskError`: a `JanusError` a resolver let through answered with its code and status; anything else to `fallback` |
96
+ | `janusMaskError({ report?, fallback? })` | Yoga's `maskedErrors.maskError`: a `JanusError` a resolver let through answered with its code and status; anything else to `fallback`. `report` is called once with every `JanusError` answered 5xx — a directive's and a helper's too — and cannot change the answer. `janusMaskError(fallback)`, the fallback alone, still works |
97
97
  | `janusGraphQLError(error)` | A `JanusError` as the `GraphQLError` the client reads: its code, its status, and only what the client can act on |
98
98
  | `denial(code, message?)` | A denial of your own: `UNAUTHENTICATED` 401, `FORBIDDEN` 403, `NOT_FOUND` 404. For a stale session, call `requireFresh()`, whose `STEP_UP_REQUIRED` is `@nxgt/janus`'s |
99
99
  | `JanusContext<typeof auth, typeof access?, Type?>` | What `useJanus()` adds to the context, for your resolvers' `ctx` |
100
- | `JanusOptions`, `JanusOnContext`, `JanusConnectionOptions`, `JanusConnection`, `ConnectionContext`, `JanusDirectivesOptions`, `Loaders`, `Conditions`, `LoadedObject`, `RequireUserOptions`, `Auth`, `UserOfAuth`, `DenialCode`, `MaskError` | The types of the arguments and answers above |
100
+ | `JanusOptions`, `JanusOnContext`, `JanusConnectionOptions`, `JanusConnection`, `ConnectionContext`, `JanusDirectivesOptions`, `Loaders`, `Conditions`, `LoadedObject`, `RequireUserOptions`, `Auth`, `UserOfAuth`, `DenialCode`, `MaskError`, `JanusMaskErrorOptions` | The types of the arguments and answers above |
101
101
 
102
102
  ## Directives
103
103
 
@@ -212,6 +212,25 @@ else — never `reason`, `login`, a hash prefix or a cause. The message is a
212
212
  fixed one per status, never the core's: read `code`. Detail, and the status of a
213
213
  response with several errors: [the errors guide](docs/guide/errors.md).
214
214
 
215
+ The message carries nothing of the store's, and a directive answers an
216
+ outage itself, before any resolver of yours could log it. `report` sees
217
+ every `JanusError` answered 5xx, once, whichever path it took:
218
+
219
+ ```ts
220
+ import { maskError } from 'graphql-yoga';
221
+
222
+ createYoga({
223
+ schema,
224
+ plugins: [useJanus({ auth, access })],
225
+ maskedErrors: {
226
+ maskError: janusMaskError({
227
+ report: (error) => logger.error({ code: error.code, cause: error.cause }), // slot, operation: which call failed
228
+ fallback: maskError, // Yoga's own, for everything else
229
+ }),
230
+ },
231
+ });
232
+ ```
233
+
215
234
  ## Traps
216
235
 
217
236
  - **Without `janusMaskError()`, a `JanusError` from a resolver is Yoga's
@@ -229,10 +248,16 @@ response with several errors: [the errors guide](docs/guide/errors.md).
229
248
  the highest `extensions.http.status` among the errors, so `{ open me }`
230
249
  from an anonymous request is a 401 whose `data` still holds `open`. A client
231
250
  reads `data` and `errors`, not only the status.
232
- - **A renewed session is not sent back as a cookie.** `authenticate` renews
233
- a sliding session in passing, and nothing here sets `Set-Cookie` on the
234
- GraphQL response: a browser keeps the cookie with its old expiry. Sign in
235
- and renew through your HTTP routes, or send a bearer token.
251
+ - **A renewed session is sent back only on an HTTP response from Yoga.**
252
+ `useJanus()` sets it in Yoga's `onResponse`, which a server on envelop
253
+ alone never calls; and an operation over graphql-ws has no response at
254
+ all. There, `ctx.janus` still renews the session in the store, but the
255
+ browser keeps the cookie with its old expiry until the next renewal over
256
+ HTTP, a `renewAfter` later: a browser whose only traffic is the WebSocket
257
+ should renew through an HTTP route of yours.
258
+ - **An `auth` of your own needs `cookie` for the renewal.** A wrapper that
259
+ passes on only `authenticate` and `types` sends no renewed cookie; pass
260
+ `cookie: auth.cookie` along.
236
261
  - **`useJanus({ type })` narrows the whole server.** A user of any other type
237
262
  is anonymous everywhere, and a directive naming another type is refused
238
263
  when the schema is built.
@@ -297,7 +322,7 @@ response with several errors: [the errors guide](docs/guide/errors.md).
297
322
 
298
323
  ## Type safety, counted
299
324
 
300
- Thirty-five plausible mistakes are refused by the compiler, each with a
325
+ Thirty-seven plausible mistakes are refused by the compiler, each with a
301
326
  `@ts-expect-error` case in `test/types/`:
302
327
 
303
328
  - five in `context.ts`: reading the user or the session where either may be
@@ -310,10 +335,11 @@ Thirty-five plausible mistakes are refused by the compiler, each with a
310
335
  permission the object's type does not declare, an object type the model
311
336
  does not, an object without a field a `fromField` reads, a condition reached
312
337
  with no `ctx`, a `ctx` of the wrong shape, and a context with no `access`;
313
- - five in `plugin.ts`: `useJanus()` given a user type the instance does not
338
+ - seven in `plugin.ts`: `useJanus()` given a user type the instance does not
314
339
  know, something that is not what `janus()` answered, the `permissions()`
315
- instance as `auth`, and `applyJanusDirectives()` without `auth` or narrowed
316
- to a user type the instance does not know;
340
+ instance as `auth`, `applyJanusDirectives()` without `auth` or narrowed
341
+ to a user type the instance does not know, and `janusMaskError()` given a
342
+ misspelled `report` or one reading what a `JanusError` does not carry;
317
343
  - five in `wiring.ts`: `useJanus()`'s `loaders` for an object type the model
318
344
  does not declare, a loader answering an object without a field a
319
345
  `fromField` reads, `conditions` for a type no `when()` is reached on, a
package/dist/context.d.ts CHANGED
@@ -48,6 +48,12 @@ export interface ContextOptions {
48
48
  * here asked.
49
49
  */
50
50
  export declare function createJanusContext(request: RequestLike | undefined, options: ContextOptions): Built;
51
+ /**
52
+ * What `authenticate` answered for `janus`'s request, or `undefined` when it
53
+ * never ran — nothing asked `user()` or `session()`, or the context was not
54
+ * built here. Never runs it.
55
+ */
56
+ export declare function answerOf(janus: object): Promise<Authenticated<AnyUser> | null> | undefined;
51
57
  /**
52
58
  * The clock `@fresh` and `requireFresh()` read the time from: the one given
53
59
  * to `useJanus({ clock })`, or the system's.
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACN,KAAK,aAAa,EAClB,KAAK,KAAK,EACV,KAAK,WAAW,EAEhB,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,iFAAiF;AACjF,MAAM,MAAM,KAAK,GAAG,CACnB,OAAO,EAAE;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,EAC9D,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,EACtD,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAA;CAAE,KAChC,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtB,qEAAqE;AACrE,KAAK,OAAO,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9D,KAAK,SAAS,GAAG;IAChB,YAAY,CACX,OAAO,EAAE,WAAW,EACpB,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAClC,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;CAC1C,CAAC;AAEF,KAAK,KAAK,GAAG,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AAa9C,mEAAmE;AACnE,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE;QAAE,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAA;KAAE,GAAG,SAAS,CAAC;IACxD,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,SAAS,CAAC;IACnC,wEAAwE;IACxE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CACjC,OAAO,EAAE,WAAW,GAAG,SAAS,EAChC,OAAO,EAAE,cAAc,GACrB,KAAK,CAoCP;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,CAE5C;AAED;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,UAAQ,GAAG,KAAK,GAAG,IAAI,CAMlE;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,KAAK,CAQ3D"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACN,KAAK,aAAa,EAClB,KAAK,KAAK,EACV,KAAK,WAAW,EAEhB,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,iFAAiF;AACjF,MAAM,MAAM,KAAK,GAAG,CACnB,OAAO,EAAE;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,EAC9D,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,EACtD,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAA;CAAE,KAChC,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtB,qEAAqE;AACrE,KAAK,OAAO,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9D,KAAK,SAAS,GAAG;IAChB,YAAY,CACX,OAAO,EAAE,WAAW,EACpB,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAClC,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;CAC1C,CAAC;AAEF,KAAK,KAAK,GAAG,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AAuB9C,mEAAmE;AACnE,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE;QAAE,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAA;KAAE,GAAG,SAAS,CAAC;IACxD,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,SAAS,CAAC;IACnC,wEAAwE;IACxE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CACjC,OAAO,EAAE,WAAW,GAAG,SAAS,EAChC,OAAO,EAAE,cAAc,GACrB,KAAK,CAqCP;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CACvB,KAAK,EAAE,MAAM,GACX,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,GAAG,SAAS,CAEpD;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,CAE5C;AAED;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,UAAQ,GAAG,KAAK,GAAG,IAAI,CAMlE;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,KAAK,CAQ3D"}
package/dist/errors.d.ts CHANGED
@@ -26,6 +26,22 @@ export declare function janusGraphQLError(error: JanusError): GraphQLError;
26
26
  export declare function rethrown(error: unknown): never;
27
27
  /** Yoga's and envelop's `maskError` signature. */
28
28
  export type MaskError = (error: unknown, message: string, isDev?: boolean) => Error;
29
+ /** What `janusMaskError()` takes. */
30
+ export interface JanusMaskErrorOptions {
31
+ /**
32
+ * Called once with every `JanusError` answered 5xx — `STORE_FAILED`,
33
+ * `UNSUPPORTED`, `PERMISSION_DEPTH` — whichever path it took: a
34
+ * directive, `requireUser()`, `can()`, or a resolver that let it through.
35
+ * Its `slot`, `operation`, `reason` and cause are for your logs; the
36
+ * response never carries them.
37
+ *
38
+ * **It cannot change the answer**: one that throws, or rejects, is a
39
+ * `process.emitWarning`, and the 503 is sent all the same.
40
+ */
41
+ readonly report?: (error: JanusError) => unknown;
42
+ /** Every error that is not a `JanusError`; see `janusMaskError()`. */
43
+ readonly fallback?: MaskError;
44
+ }
29
45
  /**
30
46
  * Yoga's `maskedErrors.maskError`: a `JanusError` a resolver let through is
31
47
  * answered with its code and status — `STORE_FAILED` as 503, never masked
@@ -33,12 +49,19 @@ export type MaskError = (error: unknown, message: string, isDev?: boolean) => Er
33
49
  *
34
50
  * Without a `fallback`, a `GraphQLError` is kept and anything else becomes
35
51
  * `message`, with no detail. Pass Yoga's own `maskError` to keep its
36
- * development-mode details:
52
+ * development-mode details — as the options' `fallback`, or alone, as
53
+ * before `report` existed:
37
54
  *
38
55
  * ```ts
39
56
  * import { createYoga, maskError } from 'graphql-yoga';
40
- * createYoga({ schema, plugins, maskedErrors: { maskError: janusMaskError(maskError) } });
57
+ * createYoga({
58
+ * schema,
59
+ * plugins,
60
+ * maskedErrors: {
61
+ * maskError: janusMaskError({ report: (error) => logger.error(error), fallback: maskError }),
62
+ * },
63
+ * });
41
64
  * ```
42
65
  */
43
- export declare function janusMaskError(fallback?: MaskError): MaskError;
66
+ export declare function janusMaskError(options?: JanusMaskErrorOptions | MaskError): MaskError;
44
67
  //# sourceMappingURL=errors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAmC,MAAM,aAAa,CAAC;AAC1E,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,mFAAmF;AACnF,MAAM,MAAM,UAAU,GAAG,iBAAiB,GAAG,WAAW,GAAG,WAAW,CAAC;AAEvE,2EAA2E;AAC3E,wBAAgB,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,YAAY,CAKvE;AAaD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,UAAU,GAAG,YAAY,CAQjE;AA4CD;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAE9C;AAED,kDAAkD;AAClD,MAAM,MAAM,SAAS,GAAG,CACvB,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,OAAO,KACX,KAAK,CAAC;AAEX;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAAC,QAAQ,GAAE,SAAkB,GAAG,SAAS,CAiBtE"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAmC,MAAM,aAAa,CAAC;AAC1E,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAGvC,mFAAmF;AACnF,MAAM,MAAM,UAAU,GAAG,iBAAiB,GAAG,WAAW,GAAG,WAAW,CAAC;AAEvE,2EAA2E;AAC3E,wBAAgB,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,YAAY,CAKvE;AAaD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,UAAU,GAAG,YAAY,CAQjE;AA4CD;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAE9C;AAED,kDAAkD;AAClD,MAAM,MAAM,SAAS,GAAG,CACvB,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,OAAO,KACX,KAAK,CAAC;AAEX,qCAAqC;AACrC,MAAM,WAAW,qBAAqB;IACrC;;;;;;;;;OASG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC;IACjD,sEAAsE;IACtE,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,cAAc,CAC7B,OAAO,GAAE,qBAAqB,GAAG,SAAc,GAC7C,SAAS,CAqBX"}
package/dist/index.d.ts CHANGED
@@ -9,8 +9,9 @@
9
9
  * - `applyJanusDirectives(schema, { auth })` — the schema transform alone;
10
10
  * - `requireUser(ctx, { type? })`, `requireFresh(ctx, maxAge)` and
11
11
  * `can(ctx, permission, object)` — the calls a resolver makes itself;
12
- * - `janusMaskError(fallback?)` — Yoga's `maskError`: a `JanusError` as its
13
- * code and status, `STORE_FAILED` as 503 and never as 401 or 403.
12
+ * - `janusMaskError({ report?, fallback? })` — Yoga's `maskError`: a
13
+ * `JanusError` as its code and status, `STORE_FAILED` as 503 and never as
14
+ * 401 or 403, and every 5xx handed to `report`.
14
15
  * - `janusGraphQLError(error)` — a `JanusError` as the `GraphQLError` the
15
16
  * client reads, and `denial(code, message?)` — a denial of your own.
16
17
  * - `janusConnection({ auth, access?, type?, clock?, upgrade? })` —
@@ -24,7 +25,7 @@
24
25
  export { janusConnection } from './connection/connection';
25
26
  export type { ConnectionContext, JanusConnection, JanusConnectionOptions, } from './connection/types';
26
27
  export { applyJanusDirectives, type JanusDirectivesOptions, } from './directives/apply';
27
- export { type DenialCode, denial, janusGraphQLError, janusMaskError, type MaskError, } from './errors';
28
+ export { type DenialCode, denial, type JanusMaskErrorOptions, janusGraphQLError, janusMaskError, type MaskError, } from './errors';
28
29
  export { requireFresh } from './fresh';
29
30
  export { can, type RequireUserOptions, requireUser } from './helpers';
30
31
  export { useJanus } from './plugin';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,YAAY,EACX,iBAAiB,EACjB,eAAe,EACf,sBAAsB,GACtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACN,oBAAoB,EACpB,KAAK,sBAAsB,GAC3B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACN,KAAK,UAAU,EACf,MAAM,EACN,iBAAiB,EACjB,cAAc,EACd,KAAK,SAAS,GACd,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,GAAG,EAAE,KAAK,kBAAkB,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACtE,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,YAAY,EACX,IAAI,EACJ,YAAY,EACZ,cAAc,EACd,YAAY,EACZ,UAAU,GACV,MAAM,SAAS,CAAC;AACjB,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,YAAY,EACX,iBAAiB,EACjB,eAAe,EACf,sBAAsB,GACtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACN,oBAAoB,EACpB,KAAK,sBAAsB,GAC3B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACN,KAAK,UAAU,EACf,MAAM,EACN,KAAK,qBAAqB,EAC1B,iBAAiB,EACjB,cAAc,EACd,KAAK,SAAS,GACd,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,GAAG,EAAE,KAAK,kBAAkB,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACtE,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,YAAY,EACX,IAAI,EACJ,YAAY,EACZ,cAAc,EACd,YAAY,EACZ,UAAU,GACV,MAAM,SAAS,CAAC;AACjB,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC"}
package/dist/index.js CHANGED
@@ -34,6 +34,7 @@ function memoized(access) {
34
34
  // src/context.ts
35
35
  var checks = new WeakMap;
36
36
  var clocks = new WeakMap;
37
+ var answers = new WeakMap;
37
38
  function createJanusContext(request, options) {
38
39
  const { auth, access, type, clock, caller = "useJanus()" } = options;
39
40
  let answer;
@@ -59,8 +60,12 @@ function createJanusContext(request, options) {
59
60
  }
60
61
  if (clock !== undefined)
61
62
  clocks.set(janus, clock);
63
+ answers.set(janus, () => asked ? answer : undefined);
62
64
  return janus;
63
65
  }
66
+ function answerOf(janus) {
67
+ return answers.get(janus)?.();
68
+ }
64
69
  function clockOf(janus) {
65
70
  return clocks.get(janus) ?? systemClock;
66
71
  }
@@ -157,11 +162,40 @@ import { MapperKind, mapSchema } from "@graphql-tools/utils";
157
162
  import { defaultFieldResolver } from "graphql";
158
163
 
159
164
  // src/errors.ts
165
+ import { JanusError as JanusError2, statusOf as statusOf2 } from "@nxgt/janus";
166
+ import { GraphQLError as GraphQLError2 } from "graphql";
167
+
168
+ // src/report.ts
160
169
  import { JanusError, statusOf } from "@nxgt/janus";
161
170
  import { GraphQLError } from "graphql";
171
+ function janusErrorIn(error) {
172
+ let current = error;
173
+ while (current instanceof GraphQLError)
174
+ current = current.originalError;
175
+ return current instanceof JanusError ? current : null;
176
+ }
177
+ function reporting(report) {
178
+ const seen = new WeakSet;
179
+ return (error) => {
180
+ const janusError = janusErrorIn(error);
181
+ if (janusError === null || seen.has(janusError))
182
+ return;
183
+ if (statusOf(janusError.code) < 500)
184
+ return;
185
+ seen.add(janusError);
186
+ const warn = (failure) => process.emitWarning(`janusMaskError: report failed on ${janusError.code}: ${failure instanceof Error ? failure.name : typeof failure}`);
187
+ try {
188
+ Promise.resolve(report(janusError)).then(undefined, warn);
189
+ } catch (failure) {
190
+ warn(failure);
191
+ }
192
+ };
193
+ }
194
+
195
+ // src/errors.ts
162
196
  function denial(code, message) {
163
197
  const [status, fallback] = denialOf(code);
164
- return new GraphQLError(message ?? fallback, {
198
+ return new GraphQLError2(message ?? fallback, {
165
199
  extensions: { code, http: { status } }
166
200
  });
167
201
  }
@@ -176,9 +210,9 @@ function denialOf(code) {
176
210
  }
177
211
  }
178
212
  function janusGraphQLError(error) {
179
- const status = statusOf(error.code);
213
+ const status = statusOf2(error.code);
180
214
  const code = error.code === "STORE_FAILED" ? "SERVICE_UNAVAILABLE" : error.code;
181
- return new GraphQLError(messageOf(status), {
215
+ return new GraphQLError2(messageOf(status), {
182
216
  originalError: error,
183
217
  extensions: { code, http: { status }, ...actionable(error) }
184
218
  });
@@ -215,14 +249,17 @@ function actionable(error) {
215
249
  }
216
250
  }
217
251
  function rethrown(error) {
218
- throw error instanceof JanusError ? janusGraphQLError(error) : error;
252
+ throw error instanceof JanusError2 ? janusGraphQLError(error) : error;
219
253
  }
220
- function janusMaskError(fallback = masked) {
254
+ function janusMaskError(options = {}) {
255
+ const { report, fallback = masked } = typeof options === "function" ? { fallback: options } : options;
256
+ const reported = report === undefined ? undefined : reporting(report);
221
257
  return (error, message, isDev) => {
222
- const original = error instanceof GraphQLError ? error.originalError : error;
223
- if (original instanceof JanusError) {
258
+ reported?.(error);
259
+ const original = error instanceof GraphQLError2 ? error.originalError : error;
260
+ if (original instanceof JanusError2) {
224
261
  const answer = janusGraphQLError(original);
225
- return error instanceof GraphQLError ? new GraphQLError(answer.message, {
262
+ return error instanceof GraphQLError2 ? new GraphQLError2(answer.message, {
226
263
  nodes: error.nodes ?? null,
227
264
  path: error.path ?? null,
228
265
  originalError: original,
@@ -233,18 +270,18 @@ function janusMaskError(fallback = masked) {
233
270
  };
234
271
  }
235
272
  function masked(error, message) {
236
- if (error instanceof GraphQLError) {
273
+ if (error instanceof GraphQLError2) {
237
274
  const original = error.originalError;
238
- if (original === undefined || original instanceof GraphQLError) {
275
+ if (original === undefined || original instanceof GraphQLError2) {
239
276
  return error;
240
277
  }
241
- return new GraphQLError(message, {
278
+ return new GraphQLError2(message, {
242
279
  nodes: error.nodes ?? null,
243
280
  path: error.path ?? null,
244
281
  extensions: { code: "INTERNAL_SERVER_ERROR" }
245
282
  });
246
283
  }
247
- return new GraphQLError(message, {
284
+ return new GraphQLError2(message, {
248
285
  extensions: { code: "INTERNAL_SERVER_ERROR" }
249
286
  });
250
287
  }
@@ -698,6 +735,42 @@ async function can(ctx, permission, object, ...options) {
698
735
  const subject = await janus.user().then(undefined, rethrown);
699
736
  return check(subject, permission, object, options[0]).then(undefined, rethrown);
700
737
  }
738
+ // src/renewal.ts
739
+ var contexts = new WeakMap;
740
+ function track(request, janus) {
741
+ const known = contexts.get(request);
742
+ if (known === undefined)
743
+ contexts.set(request, [janus]);
744
+ else
745
+ known.push(janus);
746
+ }
747
+ async function resendRenewed({ request, response }, cookie) {
748
+ const known = contexts.get(request);
749
+ if (cookie === undefined || known === undefined)
750
+ return;
751
+ for (const janus of known) {
752
+ const current = await answerOf(janus)?.then((answer) => answer, () => null);
753
+ if (current?.renewed === true && cookieOf(request, cookie.name) === current.token && !sets(response, cookie.name)) {
754
+ response.headers.append("Set-Cookie", cookie.serialize(current.token, current.session));
755
+ }
756
+ }
757
+ }
758
+ function cookieOf(request, name) {
759
+ const header = request.headers.get("cookie");
760
+ if (header === null)
761
+ return null;
762
+ for (const pair of header.split(";")) {
763
+ const at = pair.indexOf("=");
764
+ if (at !== -1 && pair.slice(0, at).trim() === name) {
765
+ return pair.slice(at + 1).trim();
766
+ }
767
+ }
768
+ return null;
769
+ }
770
+ function sets(response, name) {
771
+ return response.headers.getSetCookie().some((value) => value.startsWith(`${name}=`));
772
+ }
773
+
701
774
  // src/plugin.ts
702
775
  function useJanus(options) {
703
776
  const { auth, access, type, clock, loaders, conditions } = options;
@@ -712,6 +785,9 @@ function useJanus(options) {
712
785
  type,
713
786
  clock
714
787
  });
788
+ if (typeof request === "object" && request !== null) {
789
+ track(request, janus);
790
+ }
715
791
  extendContext({ janus });
716
792
  },
717
793
  onSchemaChange: ({ schema, replaceSchema }) => {
@@ -726,7 +802,8 @@ function useJanus(options) {
726
802
  });
727
803
  applied.add(next);
728
804
  replaceSchema(next);
729
- }
805
+ },
806
+ onResponse: (payload) => resendRenewed(payload, auth.cookie)
730
807
  };
731
808
  }
732
809
  // src/sdl.ts
@@ -788,5 +865,5 @@ export {
788
865
  useJanus
789
866
  };
790
867
 
791
- //# debugId=C6BF0BFA8BF13A9C64756E2164756E21
868
+ //# debugId=33AAA17AADE0081364756E2164756E21
792
869
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1,14 +1,15 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/context.ts", "../src/memo.ts", "../src/options.ts", "../src/connection/credential.ts", "../src/connection/connection.ts", "../src/directives/apply.ts", "../src/errors.ts", "../src/fresh.ts", "../src/directives/permission/path.ts", "../src/directives/permission/enforce.ts", "../src/directives/guard.ts", "../src/directives/read.ts", "../src/directives/words.ts", "../src/directives/permission/model.ts", "../src/directives/permission/validate.ts", "../src/directives/validate.ts", "../src/helpers.ts", "../src/plugin.ts", "../src/sdl.ts"],
3
+ "sources": ["../src/context.ts", "../src/memo.ts", "../src/options.ts", "../src/connection/credential.ts", "../src/connection/connection.ts", "../src/directives/apply.ts", "../src/errors.ts", "../src/report.ts", "../src/fresh.ts", "../src/directives/permission/path.ts", "../src/directives/permission/enforce.ts", "../src/directives/guard.ts", "../src/directives/read.ts", "../src/directives/words.ts", "../src/directives/permission/model.ts", "../src/directives/permission/validate.ts", "../src/directives/validate.ts", "../src/helpers.ts", "../src/renewal.ts", "../src/plugin.ts", "../src/sdl.ts"],
4
4
  "sourcesContent": [
5
- "/**\n * `ctx.janus`, built once per request by `useJanus()`: a lazy\n * authentication, and the per-request check `can()` and the directives ask\n * through.\n */\n\nimport {\n\ttype Authenticated,\n\ttype Clock,\n\ttype RequestLike,\n\tsystemClock,\n} from '@nxgt/janus';\nimport { memoized } from './memo';\nimport type { JanusOnContext } from './types';\n\n/** One check, as `access.can` answers it — loose: the typing is the caller's. */\nexport type Check = (\n\tsubject: { readonly type: string; readonly id: string } | null,\n\tpermission: string,\n\tobject: { readonly type: string; readonly id: string },\n\toptions?: { readonly ctx?: unknown },\n) => Promise<boolean>;\n\n/** A user as this module reads one: the part every user type has. */\ntype AnyUser = { readonly type: string; readonly id: string };\n\ntype LooseAuth = {\n\tauthenticate(\n\t\trequest: RequestLike,\n\t\toptions?: { readonly type?: string },\n\t): Promise<Authenticated<AnyUser> | null>;\n};\n\ntype Built = JanusOnContext<AnyUser, unknown>;\n\n/**\n * The check of each context this module built. A `WeakMap` rather than a\n * property, so the context's own surface stays `user`, `session` and\n * `access`; it is the one place a request's checks pass through, and so the\n * one place a per-request memo of their answers goes.\n */\nconst checks = new WeakMap<object, { memo: Check; fresh: Check }>();\n\n/** The clock of each context this module built with one: what `@fresh` reads. */\nconst clocks = new WeakMap<object, Clock>();\n\n/** What `createJanusContext` reads from `useJanus()`'s options. */\nexport interface ContextOptions {\n\treadonly auth: LooseAuth;\n\treadonly access?: { readonly can: unknown } | undefined;\n\treadonly type?: string | undefined;\n\treadonly clock?: Clock | undefined;\n\t/** The call the consumer wrote, named by the no-request `TypeError`. */\n\treadonly caller?: string;\n}\n\n/**\n * `ctx.janus` for one request. `authenticate` runs the first time `user()`\n * or `session()` is asked, once, and its answer — or its failure — is the\n * answer of both from then on.\n *\n * A context with no request — a transport that is not HTTP, or a graphql-ws\n * connection no `janusConnection().onConnect` accepted — answers a\n * `TypeError` from `user()`, not `null`: anonymous is an answer, and nothing\n * here asked.\n */\nexport function createJanusContext(\n\trequest: RequestLike | undefined,\n\toptions: ContextOptions,\n): Built {\n\tconst { auth, access, type, clock, caller = 'useJanus()' } = options;\n\tlet answer: Promise<Authenticated<AnyUser> | null>;\n\tlet asked = false;\n\tconst authenticated = () => {\n\t\tif (!asked) {\n\t\t\tasked = true;\n\t\t\tanswer =\n\t\t\t\trequest === undefined\n\t\t\t\t\t? Promise.reject(\n\t\t\t\t\t\t\tnew TypeError(\n\t\t\t\t\t\t\t\t`${caller}: the GraphQL context has no request to authenticate — build the context from an HTTP request, or accept a graphql-ws connection with janusConnection().onConnect`,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t)\n\t\t\t\t\t: auth.authenticate(\n\t\t\t\t\t\t\trequest,\n\t\t\t\t\t\t\ttype === undefined ? undefined : { type },\n\t\t\t\t\t\t);\n\t\t}\n\t\treturn answer;\n\t};\n\n\tconst janus = Object.freeze({\n\t\tuser: () => authenticated().then((current) => current?.user ?? null),\n\t\tsession: () => authenticated().then((current) => current?.session ?? null),\n\t\t...(access === undefined ? {} : { access }),\n\t});\n\tif (access !== undefined) {\n\t\tconst can = access.can as Check;\n\t\tchecks.set(janus, {\n\t\t\tmemo: memoized(access),\n\t\t\tfresh: (...args) => can.apply(access, args),\n\t\t});\n\t}\n\tif (clock !== undefined) clocks.set(janus, clock);\n\treturn janus as Built;\n}\n\n/**\n * The clock `@fresh` and `requireFresh()` read the time from: the one given\n * to `useJanus({ clock })`, or the system's.\n */\nexport function clockOf(janus: object): Clock {\n\treturn clocks.get(janus) ?? systemClock;\n}\n\n/**\n * The check `ctx.janus` answers through: the one its context was built\n * with — `access.can`, memoized for the request (`memo.ts`) — or\n * `access.can` itself for a context built by hand. `null` when it has no\n * `access` at all.\n *\n * `fresh` skips the memo: a subscription is one request whose checks run\n * again on every event, and must see a `revoke()` made since it started.\n */\nexport function checkOf(janus: object, fresh = false): Check | null {\n\tconst built = checks.get(janus);\n\tif (built !== undefined) return fresh ? built.fresh : built.memo;\n\tconst access = (janus as { readonly access?: { readonly can?: unknown } })\n\t\t.access;\n\treturn typeof access?.can === 'function' ? (access.can as Check) : null;\n}\n\n/**\n * `ctx.janus`, or a wiring `TypeError` naming `caller` when nothing put it\n * there: `useJanus()` is missing from the plugins.\n */\nexport function janusOf(ctx: unknown, caller: string): Built {\n\tconst janus = (ctx as { readonly janus?: Built } | null)?.janus;\n\tif (janus === undefined || typeof janus.user !== 'function') {\n\t\tthrow new TypeError(\n\t\t\t`${caller}: ctx.janus is not set — add useJanus({ auth }) to the plugins`,\n\t\t);\n\t}\n\treturn janus;\n}\n",
5
+ "/**\n * `ctx.janus`, built once per request by `useJanus()`: a lazy\n * authentication, and the per-request check `can()` and the directives ask\n * through.\n */\n\nimport {\n\ttype Authenticated,\n\ttype Clock,\n\ttype RequestLike,\n\tsystemClock,\n} from '@nxgt/janus';\nimport { memoized } from './memo';\nimport type { JanusOnContext } from './types';\n\n/** One check, as `access.can` answers it — loose: the typing is the caller's. */\nexport type Check = (\n\tsubject: { readonly type: string; readonly id: string } | null,\n\tpermission: string,\n\tobject: { readonly type: string; readonly id: string },\n\toptions?: { readonly ctx?: unknown },\n) => Promise<boolean>;\n\n/** A user as this module reads one: the part every user type has. */\ntype AnyUser = { readonly type: string; readonly id: string };\n\ntype LooseAuth = {\n\tauthenticate(\n\t\trequest: RequestLike,\n\t\toptions?: { readonly type?: string },\n\t): Promise<Authenticated<AnyUser> | null>;\n};\n\ntype Built = JanusOnContext<AnyUser, unknown>;\n\n/**\n * The check of each context this module built. A `WeakMap` rather than a\n * property, so the context's own surface stays `user`, `session` and\n * `access`; it is the one place a request's checks pass through, and so the\n * one place a per-request memo of their answers goes.\n */\nconst checks = new WeakMap<object, { memo: Check; fresh: Check }>();\n\n/** The clock of each context this module built with one: what `@fresh` reads. */\nconst clocks = new WeakMap<object, Clock>();\n\n/**\n * The answer of `authenticate` for each context this module built — or\n * `undefined` while neither `user()` nor `session()` has been asked, so that\n * reading it never authenticates.\n */\nconst answers = new WeakMap<\n\tobject,\n\t() => Promise<Authenticated<AnyUser> | null> | undefined\n>();\n\n/** What `createJanusContext` reads from `useJanus()`'s options. */\nexport interface ContextOptions {\n\treadonly auth: LooseAuth;\n\treadonly access?: { readonly can: unknown } | undefined;\n\treadonly type?: string | undefined;\n\treadonly clock?: Clock | undefined;\n\t/** The call the consumer wrote, named by the no-request `TypeError`. */\n\treadonly caller?: string;\n}\n\n/**\n * `ctx.janus` for one request. `authenticate` runs the first time `user()`\n * or `session()` is asked, once, and its answer — or its failure — is the\n * answer of both from then on.\n *\n * A context with no request — a transport that is not HTTP, or a graphql-ws\n * connection no `janusConnection().onConnect` accepted — answers a\n * `TypeError` from `user()`, not `null`: anonymous is an answer, and nothing\n * here asked.\n */\nexport function createJanusContext(\n\trequest: RequestLike | undefined,\n\toptions: ContextOptions,\n): Built {\n\tconst { auth, access, type, clock, caller = 'useJanus()' } = options;\n\tlet answer: Promise<Authenticated<AnyUser> | null>;\n\tlet asked = false;\n\tconst authenticated = () => {\n\t\tif (!asked) {\n\t\t\tasked = true;\n\t\t\tanswer =\n\t\t\t\trequest === undefined\n\t\t\t\t\t? Promise.reject(\n\t\t\t\t\t\t\tnew TypeError(\n\t\t\t\t\t\t\t\t`${caller}: the GraphQL context has no request to authenticate — build the context from an HTTP request, or accept a graphql-ws connection with janusConnection().onConnect`,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t)\n\t\t\t\t\t: auth.authenticate(\n\t\t\t\t\t\t\trequest,\n\t\t\t\t\t\t\ttype === undefined ? undefined : { type },\n\t\t\t\t\t\t);\n\t\t}\n\t\treturn answer;\n\t};\n\n\tconst janus = Object.freeze({\n\t\tuser: () => authenticated().then((current) => current?.user ?? null),\n\t\tsession: () => authenticated().then((current) => current?.session ?? null),\n\t\t...(access === undefined ? {} : { access }),\n\t});\n\tif (access !== undefined) {\n\t\tconst can = access.can as Check;\n\t\tchecks.set(janus, {\n\t\t\tmemo: memoized(access),\n\t\t\tfresh: (...args) => can.apply(access, args),\n\t\t});\n\t}\n\tif (clock !== undefined) clocks.set(janus, clock);\n\tanswers.set(janus, () => (asked ? answer : undefined));\n\treturn janus as Built;\n}\n\n/**\n * What `authenticate` answered for `janus`'s request, or `undefined` when it\n * never ran — nothing asked `user()` or `session()`, or the context was not\n * built here. Never runs it.\n */\nexport function answerOf(\n\tjanus: object,\n): Promise<Authenticated<AnyUser> | null> | undefined {\n\treturn answers.get(janus)?.();\n}\n\n/**\n * The clock `@fresh` and `requireFresh()` read the time from: the one given\n * to `useJanus({ clock })`, or the system's.\n */\nexport function clockOf(janus: object): Clock {\n\treturn clocks.get(janus) ?? systemClock;\n}\n\n/**\n * The check `ctx.janus` answers through: the one its context was built\n * with — `access.can`, memoized for the request (`memo.ts`) — or\n * `access.can` itself for a context built by hand. `null` when it has no\n * `access` at all.\n *\n * `fresh` skips the memo: a subscription is one request whose checks run\n * again on every event, and must see a `revoke()` made since it started.\n */\nexport function checkOf(janus: object, fresh = false): Check | null {\n\tconst built = checks.get(janus);\n\tif (built !== undefined) return fresh ? built.fresh : built.memo;\n\tconst access = (janus as { readonly access?: { readonly can?: unknown } })\n\t\t.access;\n\treturn typeof access?.can === 'function' ? (access.can as Check) : null;\n}\n\n/**\n * `ctx.janus`, or a wiring `TypeError` naming `caller` when nothing put it\n * there: `useJanus()` is missing from the plugins.\n */\nexport function janusOf(ctx: unknown, caller: string): Built {\n\tconst janus = (ctx as { readonly janus?: Built } | null)?.janus;\n\tif (janus === undefined || typeof janus.user !== 'function') {\n\t\tthrow new TypeError(\n\t\t\t`${caller}: ctx.janus is not set — add useJanus({ auth }) to the plugins`,\n\t\t);\n\t}\n\treturn janus;\n}\n",
6
6
  "/**\n * The per-request memo of permission checks: the same question asked twice\n * in one request — by two fields, by a directive and a resolver's `can()`,\n * or by two fields at once — is one call to `access.can`.\n */\n\nimport type { Check } from './context';\n\ntype Ref = {\n\treadonly type: string;\n\treadonly id: string;\n\treadonly relation?: unknown;\n};\n\n/**\n * What no id in a key may hold: the notation reads `@` and `#` as its own,\n * so a key built from such an id could be another question's. `can()`\n * refuses or denies such an id anyway; it is asked, not remembered.\n */\nconst NOTATION = /[@#]/;\n\n/**\n * The key of one question, in the notation — `record:r1#view@staff:u1` —\n * or `null` when it is not remembered: an anonymous subject, which `can()`\n * answers `false` with no store call; a condition's `ctx`, which the key\n * does not hold; an id the notation could read two ways.\n */\nfunction keyOf(\n\tsubject: Ref | null,\n\tpermission: string,\n\tobject: Ref,\n\toptions: { readonly ctx?: unknown } | undefined,\n): string | null {\n\tif (subject === null || options?.ctx !== undefined) return null;\n\tif (NOTATION.test(object.id) || NOTATION.test(subject.id)) return null;\n\tconst set =\n\t\ttypeof subject.relation === 'string' ? `#${subject.relation}` : '';\n\treturn `${object.type}:${object.id}#${permission}@${subject.type}:${subject.id}${set}`;\n}\n\n/**\n * `access.can` for one request, remembered: a question asked again answers\n * the first answer's promise, so two asked at once share one call. A\n * failure is not remembered — an outage is no answer, and the next question\n * asks again.\n */\nexport function memoized(access: { readonly can: unknown }): Check {\n\tconst can = access.can as Check;\n\tconst answers = new Map<string, Promise<boolean>>();\n\tconst ask: Check = async (subject, permission, object, options) =>\n\t\tcan.call(access, subject, permission, object, options);\n\treturn (subject, permission, object, options) => {\n\t\tconst key = keyOf(subject, permission, object, options);\n\t\tif (key === null) return ask(subject, permission, object, options);\n\t\tconst known = answers.get(key);\n\t\tif (known !== undefined) return known;\n\t\tconst answer = ask(subject, permission, object, options);\n\t\tanswers.set(key, answer);\n\t\tanswer.then(undefined, () => answers.delete(key));\n\t\treturn answer;\n\t};\n}\n",
7
7
  "/**\n * The wiring checks `useJanus()` and `janusConnection()` share: what each\n * refuses at start-up, with a `TypeError` naming the call that was written.\n */\n\n/** What both calls take and check before anything runs. */\nexport interface Wiring {\n\treadonly auth: unknown;\n\treadonly access?: unknown;\n\treadonly clock?: unknown;\n}\n\n/**\n * Refuses an `auth` that is not what `janus()` answered, an `access` that\n * is not what `permissions()` answered, and a `clock` that is not a\n * `Clock` — each a `TypeError` naming `caller`.\n */\nexport function checkWiring(caller: string, { auth, access, clock }: Wiring) {\n\tif (\n\t\ttypeof (auth as { readonly authenticate?: unknown } | null)\n\t\t\t?.authenticate !== 'function'\n\t) {\n\t\tthrow new TypeError(\n\t\t\t`${caller}: auth is not what janus() answered — pass { auth }`,\n\t\t);\n\t}\n\tif (\n\t\taccess !== undefined &&\n\t\ttypeof (access as { readonly can?: unknown } | null)?.can !== 'function'\n\t) {\n\t\tthrow new TypeError(\n\t\t\t`${caller}: access is not what permissions() answered — pass { auth, access }`,\n\t\t);\n\t}\n\tif (\n\t\tclock !== undefined &&\n\t\ttypeof (clock as { readonly now?: unknown } | null)?.now !== 'function'\n\t) {\n\t\tthrow new TypeError(\n\t\t\t`${caller}: clock is not a Clock — pass the clock given to janus(), or leave it out for the system clock`,\n\t\t);\n\t}\n}\n",
8
8
  "/**\n * The credential a graphql-ws connection presents, and the connections\n * `janusConnection().onConnect` accepted: what each operation on one is\n * authenticated from.\n */\n\nimport type { RequestLike } from '@nxgt/janus';\nimport type { ConnectionContext } from './types';\n\n/** Where the upgrade request is, when the transport does not say `extra.request`. */\nexport type Upgrade = (ctx: ConnectionContext) => RequestLike | undefined;\n\n/** The scheme `@nxgt/janus` reads a session token from — any case, as it does. */\nconst BEARER = /^\\s*bearer(?:\\s|$)/i;\n\n/**\n * The connection's credential, as `auth.authenticate` reads one:\n *\n * 1. `connectionParams.authorization`, when it is a `Bearer` token — a\n * non-browser client's `'Bearer <token>'`;\n * 2. otherwise the upgrade request itself, read as any request is —\n * `Authorization`, then `X-Session-Token`, then the session cookie a\n * browser sends with it.\n *\n * **The first present wins, not the first valid one**, as in `@nxgt/janus`:\n * a lapsed token in `connectionParams` beside a live cookie is refused. An\n * `authorization` of another scheme (`Basic`) is not a session credential,\n * as in `@nxgt/janus`, and does not count. `null` when there is nothing to\n * read, or an `authorization` that is not a string — refused, as a\n * client's mistake.\n */\nexport function credentialOf(\n\tctx: ConnectionContext,\n\tupgrade: Upgrade | undefined,\n): RequestLike | null {\n\tconst params = ctx.connectionParams;\n\tif (params !== undefined && Object.hasOwn(params, 'authorization')) {\n\t\tconst { authorization } = params;\n\t\tif (typeof authorization !== 'string') return null;\n\t\t// A record, not `Headers`, which would throw on a value no header may\n\t\t// hold: a client's mistake is a refusal, never a 4500.\n\t\tif (BEARER.test(authorization)) return { authorization };\n\t}\n\tconst request =\n\t\tupgrade === undefined\n\t\t\t? (ctx.extra as { readonly request?: RequestLike } | null)?.request\n\t\t\t: upgrade(ctx);\n\treturn request ?? null;\n}\n\n/**\n * The credential of each connection `onConnect` accepted, by its `extra`:\n * the one object graphql-ws hands every callback of a connection, and that\n * Yoga's recipe spreads into the context it builds. A `WeakMap`, so a\n * closed connection takes its entry with it, and nothing a request carries\n * can name one.\n */\nconst accepted = new WeakMap<object, RequestLike>();\n\n/** Remembers `credential` as the one `ctx`'s connection presented. */\nexport function accept(ctx: ConnectionContext, credential: RequestLike) {\n\tconst { extra } = ctx;\n\tif (typeof extra !== 'object' || extra === null) {\n\t\tthrow new TypeError(\n\t\t\t\"janusConnection(): the connection's extra is not an object — pass the context graphql-ws gave onConnect\",\n\t\t);\n\t}\n\taccepted.set(extra, credential);\n}\n\n/**\n * The credential of the connection a context was built for, or `undefined`\n * for one no `onConnect` accepted — and for every HTTP request.\n */\nexport function acceptedOf(context: unknown): RequestLike | undefined {\n\tconst extra = (context as { readonly extra?: unknown } | null)?.extra;\n\tif (typeof extra !== 'object' || extra === null) return undefined;\n\treturn accepted.get(extra);\n}\n",
9
9
  "/**\n * `janusConnection()`: authenticated subscriptions over graphql-ws.\n */\n\nimport { createJanusContext } from '../context';\nimport { checkWiring } from '../options';\nimport type { Auth, JanusContext, UserOfAuth } from '../types';\nimport { accept, acceptedOf, credentialOf } from './credential';\nimport type { JanusConnection, JanusConnectionOptions } from './types';\n\n/**\n * The pieces of graphql-ws's `useServer()` that authenticate a WebSocket\n * connection, for Yoga's recommended setup:\n *\n * ```ts\n * const connection = janusConnection({ auth });\n * useServer({\n * onConnect: connection.onConnect,\n * execute: (args) => args.rootValue.execute(args),\n * subscribe: (args) => args.rootValue.subscribe(args),\n * onSubscribe: async (ctx, _id, params) => { … yoga.getEnveloped({ ...ctx, … }) … },\n * }, wsServer);\n * ```\n *\n * `onConnect` reads the connection's credential — `connectionParams`'\n * `authorization`, else the upgrade request's headers and cookie — and\n * refuses it unless it authenticates, as a user of `type` when it is given.\n * Each operation on the connection then gets a `ctx.janus` of its own from\n * `useJanus()`, authenticated again from that credential when a field asks:\n * `@authenticated`, `@fresh` and `@permission` hold as over HTTP, and a\n * session revoked since the connection opened is refused at the next\n * subscribe.\n *\n * A server without Yoga passes `context` too, and applies the directives\n * with `applyJanusDirectives()`.\n */\nexport function janusConnection<\n\tA extends Auth<{ readonly type: string; readonly id: string }>,\n\tP extends object | undefined = undefined,\n\tconst T extends UserOfAuth<A>['type'] = UserOfAuth<A>['type'],\n>(options: JanusConnectionOptions<A, P, T>): JanusConnection<A, P, T> {\n\tconst { auth, access, type, clock } = options;\n\tcheckWiring('janusConnection()', { auth, access, clock });\n\tif (options.upgrade !== undefined && typeof options.upgrade !== 'function') {\n\t\tthrow new TypeError(\n\t\t\t'janusConnection(): upgrade is not a function — pass (ctx) => the upgrade request, or leave it out to read ctx.extra.request',\n\t\t);\n\t}\n\tconst upgrade = options.upgrade?.bind(options);\n\tconst wiring = {\n\t\tauth,\n\t\taccess: access as { readonly can: unknown } | undefined,\n\t\ttype,\n\t\tclock,\n\t\tcaller: 'janusConnection().context',\n\t};\n\n\treturn {\n\t\tonConnect: async (ctx) => {\n\t\t\tconst credential = credentialOf(ctx, upgrade);\n\t\t\tif (credential === null) return false;\n\t\t\t// An outage rejects here, and graphql-ws closes the socket 4500:\n\t\t\t// never `false`, which a client reads as a refusal.\n\t\t\tconst current = await auth.authenticate(\n\t\t\t\tcredential,\n\t\t\t\ttype === undefined ? undefined : { type },\n\t\t\t);\n\t\t\tif (current === null) return false;\n\t\t\taccept(ctx, credential);\n\t\t\treturn true;\n\t\t},\n\t\t// The one cast at the boundary: the context is built loose, typed here.\n\t\tcontext: (ctx) =>\n\t\t\t({\n\t\t\t\tjanus: createJanusContext(acceptedOf(ctx), wiring),\n\t\t\t}) as unknown as JanusContext<A, P, T>,\n\t};\n}\n",
10
10
  "/**\n * The schema transform: every object field a directive guards gets its\n * resolver wrapped, once, when the schema is built.\n */\n\nimport { MapperKind, mapSchema } from '@graphql-tools/utils';\nimport { defaultFieldResolver, type GraphQLSchema } from 'graphql';\nimport { guarded } from './guard';\nimport { readField } from './read';\nimport { type Known, requirementOf } from './validate';\nimport { list, PREFIX } from './words';\n\ntype Entries = {\n\treadonly [type: string]: ((...args: never[]) => unknown) | undefined;\n};\n\n/** What `applyJanusDirectives()` takes. */\nexport interface JanusDirectivesOptions<T extends string = string> {\n\t/** What `janus()` answered: its `types` are the names `type:` may take. */\n\treadonly auth: { readonly types: readonly T[] };\n\t/** `useJanus({ type })`'s: the one user type the schema's users can have. */\n\treadonly type?: NoInfer<T>;\n\t/** What `permissions()` answered: its model is what `@permission` may name. Required once a schema uses it. */\n\treadonly access?: { readonly model: unknown } | undefined;\n\t/** `useJanus({ loaders })`'s. */\n\treadonly loaders?: Entries | undefined;\n\t/** `useJanus({ conditions })`'s. */\n\treadonly conditions?: Entries | undefined;\n}\n\n/**\n * The schema with every field guarded by `@authenticated`, `@fresh` or\n * `@permission` — on the field, on its type, or on an interface the type\n * implements — wrapped so its resolver runs only for a signed-in user, of\n * one of the types `type:` names when it names some, on a session that\n * proved who it is less than `maxAge` seconds ago, holding every permission\n * asked. Every directive that applies must hold.\n *\n * `useJanus()` calls it on every schema it is given. Call it yourself to\n * check a schema without a server: the guards it installs read `ctx.janus`,\n * which only `useJanus()` builds.\n *\n * **Refuses when the schema is built**, with a `TypeError` naming the\n * field: a user type `auth` does not know, a `type: []`, restrictions no\n * user type meets together, a `@fresh` whose `maxAge` is not above zero;\n * for `@permission`, an object type or a permission the model does not\n * declare, a malformed `id:`, an argument the field does not take, and a\n * loader or a condition's `ctx` it needs and was not given.\n */\nexport function applyJanusDirectives<const T extends string>(\n\tschema: GraphQLSchema,\n\toptions: JanusDirectivesOptions<T>,\n): GraphQLSchema {\n\tconst known = knownOf(options);\n\treturn mapSchema(schema, {\n\t\t[MapperKind.OBJECT_FIELD]: (field, fieldName, typeName) => {\n\t\t\tconst requirement = requirementOf(\n\t\t\t\t{\n\t\t\t\t\tname: `${typeName}.${fieldName}`,\n\t\t\t\t\targs: Object.keys(field.args ?? {}),\n\t\t\t\t},\n\t\t\t\treadField(schema, field, fieldName, typeName),\n\t\t\t\tknown,\n\t\t\t\toptions,\n\t\t\t);\n\t\t\tif (requirement === null) return field;\n\t\t\tconst resolve = field.resolve ?? defaultFieldResolver;\n\t\t\tif (field.subscribe === undefined) {\n\t\t\t\treturn { ...field, resolve: guarded(resolve, requirement) };\n\t\t\t}\n\t\t\t// A subscription's freshness is checked when it subscribes: its\n\t\t\t// events keep coming past maxAge, as its session keeps standing.\n\t\t\tconst onEvent = { ...requirement, maxAgeMs: null };\n\t\t\treturn {\n\t\t\t\t...field,\n\t\t\t\tsubscribe: guarded(field.subscribe, requirement),\n\t\t\t\tresolve: guarded(resolve, onEvent),\n\t\t\t};\n\t\t},\n\t});\n}\n\n/** The user types a directive may name — `type` alone when it is given, and one of `auth.types`. */\nfunction knownOf({ auth, type }: JanusDirectivesOptions): Known {\n\tif (type === undefined) return { types: auth.types };\n\tif (!auth.types.includes(type)) {\n\t\tthrow new TypeError(\n\t\t\t`${PREFIX}: type '${type}' is not a user type of auth — it knows ${list(auth.types)}`,\n\t\t);\n\t}\n\treturn { types: [type] };\n}\n",
11
- "/**\n * Every refusal as a `GraphQLError` whose `extensions` carry a `code` and the\n * HTTP status Yoga answers with: `{ code, http: { status } }`. This package\n * defines no error class of its own — a denial is a `GraphQLError`, and what\n * `@nxgt/janus` throws stays its own until `janusMaskError()` reads it.\n */\n\nimport { JanusError, type JanusErrorStatus, statusOf } from '@nxgt/janus';\nimport { GraphQLError } from 'graphql';\n\n/** What a guard answers without asking `@nxgt/janus`: no user, or not this one. */\nexport type DenialCode = 'UNAUTHENTICATED' | 'FORBIDDEN' | 'NOT_FOUND';\n\n/** A denial: `UNAUTHENTICATED` 401, `FORBIDDEN` 403 or `NOT_FOUND` 404. */\nexport function denial(code: DenialCode, message?: string): GraphQLError {\n\tconst [status, fallback] = denialOf(code);\n\treturn new GraphQLError(message ?? fallback, {\n\t\textensions: { code, http: { status } },\n\t});\n}\n\nfunction denialOf(code: DenialCode): readonly [number, string] {\n\tswitch (code) {\n\t\tcase 'UNAUTHENTICATED':\n\t\t\treturn [401, 'Not signed in'];\n\t\tcase 'FORBIDDEN':\n\t\t\treturn [403, 'Forbidden'];\n\t\tcase 'NOT_FOUND':\n\t\t\treturn [404, 'Not found'];\n\t}\n}\n\n/**\n * A `JanusError` as the client may read it: its code and status, and only\n * what the client can act on — `issues`, `minLength`, `attemptsLeft`. Never\n * `reason`, `login`, `slot` or a cause: those are for your logs.\n *\n * `STORE_FAILED` is `SERVICE_UNAVAILABLE` 503 — **an outage is never a\n * denial** — and no message of the core's reaches the client.\n */\nexport function janusGraphQLError(error: JanusError): GraphQLError {\n\tconst status = statusOf(error.code);\n\tconst code =\n\t\terror.code === 'STORE_FAILED' ? 'SERVICE_UNAVAILABLE' : error.code;\n\treturn new GraphQLError(messageOf(status), {\n\t\toriginalError: error,\n\t\textensions: { code, http: { status }, ...actionable(error) },\n\t});\n}\n\n/**\n * A fixed message per status: the core's own message names a hash prefix or\n * a version, which are for your logs. The client reads `extensions.code`.\n */\nfunction messageOf(status: JanusErrorStatus): string {\n\tswitch (status) {\n\t\tcase 400:\n\t\t\treturn 'Invalid request';\n\t\tcase 401:\n\t\t\treturn 'Invalid credentials';\n\t\tcase 403:\n\t\t\treturn 'Forbidden';\n\t\tcase 404:\n\t\t\treturn 'Not found';\n\t\tcase 409:\n\t\t\treturn 'Conflict';\n\t\tcase 503:\n\t\t\treturn 'The service is unavailable, retry later';\n\t\tcase 500:\n\t\tcase 501:\n\t\t\treturn 'Internal server error';\n\t}\n}\n\n/** The fields of a refusal a client can act on, as `@nxgt/janus-hono`'s `bodyOf`. */\nfunction actionable(error: JanusError): Record<string, unknown> {\n\tswitch (error.code) {\n\t\tcase 'USER_INVALID':\n\t\t\treturn { issues: error.issues ?? [] };\n\t\tcase 'PASSWORD_TOO_SHORT':\n\t\t\treturn error.minLength === undefined\n\t\t\t\t? {}\n\t\t\t\t: { minLength: error.minLength };\n\t\tcase 'CODE_INVALID':\n\t\t\treturn error.attemptsLeft === undefined\n\t\t\t\t? {}\n\t\t\t\t: { attemptsLeft: error.attemptsLeft };\n\t\tdefault:\n\t\t\treturn {};\n\t}\n}\n\n/**\n * A rejection handler: a `JanusError` becomes its `GraphQLError`, anything\n * else goes on as it is. `.then(undefined, rethrown)`.\n */\nexport function rethrown(error: unknown): never {\n\tthrow error instanceof JanusError ? janusGraphQLError(error) : error;\n}\n\n/** Yoga's and envelop's `maskError` signature. */\nexport type MaskError = (\n\terror: unknown,\n\tmessage: string,\n\tisDev?: boolean,\n) => Error;\n\n/**\n * Yoga's `maskedErrors.maskError`: a `JanusError` a resolver let through is\n * answered with its code and status — `STORE_FAILED` as 503, never masked\n * into a 500 and never a denial — and every other error goes to `fallback`.\n *\n * Without a `fallback`, a `GraphQLError` is kept and anything else becomes\n * `message`, with no detail. Pass Yoga's own `maskError` to keep its\n * development-mode details:\n *\n * ```ts\n * import { createYoga, maskError } from 'graphql-yoga';\n * createYoga({ schema, plugins, maskedErrors: { maskError: janusMaskError(maskError) } });\n * ```\n */\nexport function janusMaskError(fallback: MaskError = masked): MaskError {\n\treturn (error, message, isDev) => {\n\t\tconst original =\n\t\t\terror instanceof GraphQLError ? error.originalError : error;\n\t\tif (original instanceof JanusError) {\n\t\t\tconst answer = janusGraphQLError(original);\n\t\t\treturn error instanceof GraphQLError\n\t\t\t\t? new GraphQLError(answer.message, {\n\t\t\t\t\t\tnodes: error.nodes ?? null,\n\t\t\t\t\t\tpath: error.path ?? null,\n\t\t\t\t\t\toriginalError: original,\n\t\t\t\t\t\textensions: answer.extensions,\n\t\t\t\t\t})\n\t\t\t\t: answer;\n\t\t}\n\t\treturn fallback(error, message, isDev);\n\t};\n}\n\n/** The default `fallback`: a `GraphQLError` of our own kept, anything else `message`. */\nfunction masked(error: unknown, message: string): Error {\n\tif (error instanceof GraphQLError) {\n\t\tconst original = error.originalError;\n\t\tif (original === undefined || original instanceof GraphQLError) {\n\t\t\treturn error;\n\t\t}\n\t\treturn new GraphQLError(message, {\n\t\t\tnodes: error.nodes ?? null,\n\t\t\tpath: error.path ?? null,\n\t\t\textensions: { code: 'INTERNAL_SERVER_ERROR' },\n\t\t});\n\t}\n\treturn new GraphQLError(message, {\n\t\textensions: { code: 'INTERNAL_SERVER_ERROR' },\n\t});\n}\n",
11
+ "/**\n * Every refusal as a `GraphQLError` whose `extensions` carry a `code` and the\n * HTTP status Yoga answers with: `{ code, http: { status } }`. This package\n * defines no error class of its own — a denial is a `GraphQLError`, and what\n * `@nxgt/janus` throws stays its own until `janusMaskError()` reads it.\n */\n\nimport { JanusError, type JanusErrorStatus, statusOf } from '@nxgt/janus';\nimport { GraphQLError } from 'graphql';\nimport { reporting } from './report';\n\n/** What a guard answers without asking `@nxgt/janus`: no user, or not this one. */\nexport type DenialCode = 'UNAUTHENTICATED' | 'FORBIDDEN' | 'NOT_FOUND';\n\n/** A denial: `UNAUTHENTICATED` 401, `FORBIDDEN` 403 or `NOT_FOUND` 404. */\nexport function denial(code: DenialCode, message?: string): GraphQLError {\n\tconst [status, fallback] = denialOf(code);\n\treturn new GraphQLError(message ?? fallback, {\n\t\textensions: { code, http: { status } },\n\t});\n}\n\nfunction denialOf(code: DenialCode): readonly [number, string] {\n\tswitch (code) {\n\t\tcase 'UNAUTHENTICATED':\n\t\t\treturn [401, 'Not signed in'];\n\t\tcase 'FORBIDDEN':\n\t\t\treturn [403, 'Forbidden'];\n\t\tcase 'NOT_FOUND':\n\t\t\treturn [404, 'Not found'];\n\t}\n}\n\n/**\n * A `JanusError` as the client may read it: its code and status, and only\n * what the client can act on — `issues`, `minLength`, `attemptsLeft`. Never\n * `reason`, `login`, `slot` or a cause: those are for your logs.\n *\n * `STORE_FAILED` is `SERVICE_UNAVAILABLE` 503 — **an outage is never a\n * denial** — and no message of the core's reaches the client.\n */\nexport function janusGraphQLError(error: JanusError): GraphQLError {\n\tconst status = statusOf(error.code);\n\tconst code =\n\t\terror.code === 'STORE_FAILED' ? 'SERVICE_UNAVAILABLE' : error.code;\n\treturn new GraphQLError(messageOf(status), {\n\t\toriginalError: error,\n\t\textensions: { code, http: { status }, ...actionable(error) },\n\t});\n}\n\n/**\n * A fixed message per status: the core's own message names a hash prefix or\n * a version, which are for your logs. The client reads `extensions.code`.\n */\nfunction messageOf(status: JanusErrorStatus): string {\n\tswitch (status) {\n\t\tcase 400:\n\t\t\treturn 'Invalid request';\n\t\tcase 401:\n\t\t\treturn 'Invalid credentials';\n\t\tcase 403:\n\t\t\treturn 'Forbidden';\n\t\tcase 404:\n\t\t\treturn 'Not found';\n\t\tcase 409:\n\t\t\treturn 'Conflict';\n\t\tcase 503:\n\t\t\treturn 'The service is unavailable, retry later';\n\t\tcase 500:\n\t\tcase 501:\n\t\t\treturn 'Internal server error';\n\t}\n}\n\n/** The fields of a refusal a client can act on, as `@nxgt/janus-hono`'s `bodyOf`. */\nfunction actionable(error: JanusError): Record<string, unknown> {\n\tswitch (error.code) {\n\t\tcase 'USER_INVALID':\n\t\t\treturn { issues: error.issues ?? [] };\n\t\tcase 'PASSWORD_TOO_SHORT':\n\t\t\treturn error.minLength === undefined\n\t\t\t\t? {}\n\t\t\t\t: { minLength: error.minLength };\n\t\tcase 'CODE_INVALID':\n\t\t\treturn error.attemptsLeft === undefined\n\t\t\t\t? {}\n\t\t\t\t: { attemptsLeft: error.attemptsLeft };\n\t\tdefault:\n\t\t\treturn {};\n\t}\n}\n\n/**\n * A rejection handler: a `JanusError` becomes its `GraphQLError`, anything\n * else goes on as it is. `.then(undefined, rethrown)`.\n */\nexport function rethrown(error: unknown): never {\n\tthrow error instanceof JanusError ? janusGraphQLError(error) : error;\n}\n\n/** Yoga's and envelop's `maskError` signature. */\nexport type MaskError = (\n\terror: unknown,\n\tmessage: string,\n\tisDev?: boolean,\n) => Error;\n\n/** What `janusMaskError()` takes. */\nexport interface JanusMaskErrorOptions {\n\t/**\n\t * Called once with every `JanusError` answered 5xx — `STORE_FAILED`,\n\t * `UNSUPPORTED`, `PERMISSION_DEPTH` — whichever path it took: a\n\t * directive, `requireUser()`, `can()`, or a resolver that let it through.\n\t * Its `slot`, `operation`, `reason` and cause are for your logs; the\n\t * response never carries them.\n\t *\n\t * **It cannot change the answer**: one that throws, or rejects, is a\n\t * `process.emitWarning`, and the 503 is sent all the same.\n\t */\n\treadonly report?: (error: JanusError) => unknown;\n\t/** Every error that is not a `JanusError`; see `janusMaskError()`. */\n\treadonly fallback?: MaskError;\n}\n\n/**\n * Yoga's `maskedErrors.maskError`: a `JanusError` a resolver let through is\n * answered with its code and status — `STORE_FAILED` as 503, never masked\n * into a 500 and never a denial — and every other error goes to `fallback`.\n *\n * Without a `fallback`, a `GraphQLError` is kept and anything else becomes\n * `message`, with no detail. Pass Yoga's own `maskError` to keep its\n * development-mode details — as the options' `fallback`, or alone, as\n * before `report` existed:\n *\n * ```ts\n * import { createYoga, maskError } from 'graphql-yoga';\n * createYoga({\n * schema,\n * plugins,\n * maskedErrors: {\n * maskError: janusMaskError({ report: (error) => logger.error(error), fallback: maskError }),\n * },\n * });\n * ```\n */\nexport function janusMaskError(\n\toptions: JanusMaskErrorOptions | MaskError = {},\n): MaskError {\n\tconst { report, fallback = masked } =\n\t\ttypeof options === 'function' ? { fallback: options } : options;\n\tconst reported = report === undefined ? undefined : reporting(report);\n\treturn (error, message, isDev) => {\n\t\treported?.(error);\n\t\tconst original =\n\t\t\terror instanceof GraphQLError ? error.originalError : error;\n\t\tif (original instanceof JanusError) {\n\t\t\tconst answer = janusGraphQLError(original);\n\t\t\treturn error instanceof GraphQLError\n\t\t\t\t? new GraphQLError(answer.message, {\n\t\t\t\t\t\tnodes: error.nodes ?? null,\n\t\t\t\t\t\tpath: error.path ?? null,\n\t\t\t\t\t\toriginalError: original,\n\t\t\t\t\t\textensions: answer.extensions,\n\t\t\t\t\t})\n\t\t\t\t: answer;\n\t\t}\n\t\treturn fallback(error, message, isDev);\n\t};\n}\n\n/** The default `fallback`: a `GraphQLError` of our own kept, anything else `message`. */\nfunction masked(error: unknown, message: string): Error {\n\tif (error instanceof GraphQLError) {\n\t\tconst original = error.originalError;\n\t\tif (original === undefined || original instanceof GraphQLError) {\n\t\t\treturn error;\n\t\t}\n\t\treturn new GraphQLError(message, {\n\t\t\tnodes: error.nodes ?? null,\n\t\t\tpath: error.path ?? null,\n\t\t\textensions: { code: 'INTERNAL_SERVER_ERROR' },\n\t\t});\n\t}\n\treturn new GraphQLError(message, {\n\t\textensions: { code: 'INTERNAL_SERVER_ERROR' },\n\t});\n}\n",
12
+ "/**\n * `janusMaskError({ report })`: every `JanusError` answered 5xx, handed to\n * the application's logger before the response goes — the directives' and\n * the helpers' included, which reach the mask already a `GraphQLError`.\n */\n\nimport { JanusError, statusOf } from '@nxgt/janus';\nimport { GraphQLError } from 'graphql';\n\n/**\n * The `JanusError` behind `error`: itself, or the one a chain of\n * `GraphQLError`s carries as `originalError` — a directive's refusal,\n * located again by graphql-js. `null` when there is none.\n */\nexport function janusErrorIn(error: unknown): JanusError | null {\n\tlet current = error;\n\twhile (current instanceof GraphQLError) current = current.originalError;\n\treturn current instanceof JanusError ? current : null;\n}\n\n/**\n * A `report` that sees each failure once — a lazy `authenticate` shared by\n * two guarded fields fails both with one error — and only a 5xx: an outage,\n * `UNSUPPORTED`, `PERMISSION_DEPTH`, the ones the server must fix.\n *\n * **It cannot change the answer**: one that throws, or rejects, is a\n * `process.emitWarning`, and the response is sent all the same.\n */\nexport function reporting(\n\treport: (error: JanusError) => unknown,\n): (error: unknown) => void {\n\tconst seen = new WeakSet<JanusError>();\n\treturn (error) => {\n\t\tconst janusError = janusErrorIn(error);\n\t\tif (janusError === null || seen.has(janusError)) return;\n\t\tif (statusOf(janusError.code) < 500) return;\n\t\tseen.add(janusError);\n\t\tconst warn = (failure: unknown) =>\n\t\t\tprocess.emitWarning(\n\t\t\t\t`janusMaskError: report failed on ${janusError.code}: ${failure instanceof Error ? failure.name : typeof failure}`,\n\t\t\t);\n\t\ttry {\n\t\t\t// A thenable of any make, as a native promise: its rejection warns too.\n\t\t\tPromise.resolve(report(janusError)).then(undefined, warn);\n\t\t} catch (failure) {\n\t\t\twarn(failure);\n\t\t}\n\t};\n}\n",
12
13
  "/**\n * Freshness: whether the request's session proved who it is recently enough\n * — the check `@fresh` runs before a field, and `requireFresh()` in a\n * resolver. Both are `@nxgt/janus`'s `assertFresh`, on the clock given to\n * `useJanus({ clock })`.\n */\n\nimport {\n\tassertFresh,\n\ttype Duration,\n\tparseDuration,\n\ttype Session,\n} from '@nxgt/janus';\nimport { clockOf, janusOf } from './context';\nimport { denial, rethrown } from './errors';\n\n/**\n * The request's session, once it proved who it is less than `maxAgeMs` ago\n * — or a denial: `UNAUTHENTICATED` 401 for an anonymous request,\n * `STEP_UP_REQUIRED` 403 for an older session. A store that cannot answer is\n * `SERVICE_UNAVAILABLE` 503, never either of them.\n */\nexport async function freshSession(\n\tjanus: { session(): Promise<Session | null> },\n\tmaxAgeMs: number,\n): Promise<Session> {\n\tconst session = await janus.session().then(undefined, rethrown);\n\tif (session === null) throw denial('UNAUTHENTICATED');\n\ttry {\n\t\tassertFresh(session, maxAgeMs, clockOf(janus));\n\t} catch (error) {\n\t\trethrown(error);\n\t}\n\treturn session;\n}\n\n/**\n * The request's session, once it proved who it is less than `maxAge` ago —\n * signed in, or confirmed since by `auth.stepUp.confirm` — or a denial:\n * `UNAUTHENTICATED` 401 for an anonymous request, `STEP_UP_REQUIRED` 403 for\n * an older session, which tells the client to ask for a step-up.\n *\n * ```ts\n * await requireFresh(ctx, '10m'); // before changing the e-mail\n * ```\n *\n * `maxAge` is a duration with its unit — `'10m'`, `'600s'` — never a bare\n * number, which `@nxgt/janus` reads as milliseconds where `@fresh` reads\n * seconds: a `TypeError`, as any other malformed duration.\n */\nexport async function requireFresh(\n\tctx: {\n\t\treadonly janus: {\n\t\t\tuser(): Promise<unknown>;\n\t\t\tsession(): Promise<Session | null>;\n\t\t};\n\t},\n\tmaxAge: Exclude<Duration, number>,\n): Promise<Session> {\n\tif (typeof maxAge !== 'string') {\n\t\tthrow new TypeError(\n\t\t\t\"requireFresh(): maxAge is a duration with its unit, such as '10m' — a bare number would be milliseconds, where @fresh reads seconds\",\n\t\t);\n\t}\n\tconst maxAgeMs = parseDuration(maxAge, 'requireFresh(): maxAge');\n\treturn freshSession(janusOf(ctx, 'requireFresh()'), maxAgeMs);\n}\n",
13
14
  "/**\n * `@permission`'s `id:` — `args.<path>` or `parent.<path>` — parsed when the\n * schema is built, and read from a request's arguments or parent value when a\n * field is resolved.\n */\n\n/** A parsed `id:`. */\nexport interface IdPath {\n\t/** As written: `args.id`, `parent.wardId`, `args.input.ids`. */\n\treadonly text: string;\n\treadonly root: 'args' | 'parent';\n\t/** The names after the root, at least one. */\n\treadonly segments: readonly string[];\n}\n\nconst NAME = /^[_A-Za-z][_0-9A-Za-z]*$/;\n\n/** `text` parsed, or `null` when it is not `args.<name>…` or `parent.<name>…`. */\nexport function parsePath(text: string): IdPath | null {\n\tconst [root, ...segments] = text.split('.');\n\tif (root !== 'args' && root !== 'parent') return null;\n\tif (segments.length === 0 || !segments.every((name) => NAME.test(name))) {\n\t\treturn null;\n\t}\n\treturn { text, root, segments };\n}\n\n/** One object a check is about: its id, and the value that holds it when that value is the object. */\nexport interface Target {\n\treadonly id: string;\n\t/** The value the id was read from, when `id` is its `id` field under `parent`. */\n\treadonly holder: object | null;\n}\n\n/**\n * The objects `path` names in this request: one, or one per element where a\n * step reads a list. `null` when a step reads nothing, or an id is not a\n * string or an integer: the request resolved no object id. An empty list\n * under `parent` is `null` too — the object's data names nothing to check,\n * and its fields are not open to everyone for it; under `args` it is the\n * client asking for nothing, and there is nothing to refuse.\n */\nexport function targetsOf(\n\tpath: IdPath,\n\tparent: unknown,\n\targs: unknown,\n): readonly Target[] | null {\n\tconst containers = walk(\n\t\t[path.root === 'args' ? args : parent],\n\t\tpath.segments.slice(0, -1),\n\t);\n\tif (containers === null) return null;\n\tconst last = path.segments[path.segments.length - 1] as string;\n\tconst held = path.root === 'parent' && last === 'id';\n\tconst targets: Target[] = [];\n\tfor (const container of containers) {\n\t\tfor (const value of spread((container as Record<string, unknown>)[last])) {\n\t\t\tconst id = idOf(value);\n\t\t\tif (id === null) return null;\n\t\t\ttargets.push({ id, holder: held ? (container as object) : null });\n\t\t}\n\t}\n\treturn path.root === 'parent' && targets.length === 0 ? null : targets;\n}\n\n/** The values `segments` reach from `values`, a list read as its elements. */\nfunction walk(\n\tvalues: readonly unknown[],\n\tsegments: readonly string[],\n): readonly unknown[] | null {\n\tlet current = values;\n\tfor (const segment of segments) {\n\t\tconst next: unknown[] = [];\n\t\tfor (const value of current) {\n\t\t\tif (typeof value !== 'object' || value === null) return null;\n\t\t\tnext.push(...spread((value as Record<string, unknown>)[segment]));\n\t\t}\n\t\tcurrent = next;\n\t}\n\treturn current.every((value) => typeof value === 'object' && value !== null)\n\t\t? current\n\t\t: null;\n}\n\nfunction spread(value: unknown): readonly unknown[] {\n\treturn Array.isArray(value) ? value.flat(Number.POSITIVE_INFINITY) : [value];\n}\n\n/** An `ID` as graphql-js coerces one: a string, or an integer serialised. */\nfunction idOf(value: unknown): string | null {\n\tif (typeof value === 'string') return value;\n\tif (typeof value === 'number' && Number.isSafeInteger(value)) {\n\t\treturn String(value);\n\t}\n\treturn null;\n}\n",
14
15
  "/**\n * One `@permission`, asked at request time: the objects its `id:` names, each\n * checked with the request's check — memoized per request — and a denial\n * unless every one holds. A failure to answer is never a denial.\n */\n\nimport type { Check } from '../../context';\nimport { denial, rethrown } from '../../errors';\nimport { type Target, targetsOf } from './path';\nimport type { PermissionCheck } from './validate';\n\ntype ObjectLike = { readonly type: string; readonly id: string };\ntype Subject = { readonly type: string; readonly id: string };\n\n/** What the guard has in hand when it asks. */\nexport interface Asking {\n\treadonly check: Check;\n\treadonly user: Subject;\n\treadonly parent: unknown;\n\treadonly args: unknown;\n\t/** The GraphQL context, which `loaders` and `conditions` receive. */\n\treadonly ctx: unknown;\n}\n\n/** Refuses the request unless `user` holds `permission` on every object its `id:` names. */\nexport async function enforcePermission(\n\tpermission: PermissionCheck,\n\tasking: Asking,\n): Promise<void> {\n\tconst targets = targetsOf(permission.path, asking.parent, asking.args);\n\tif (targets === null) {\n\t\twarnNoId(permission);\n\t\tthrow denial('NOT_FOUND');\n\t}\n\tconst objects = await Promise.all(\n\t\ttargets.map((target) => objectOf(permission, target, asking.ctx)),\n\t).then(undefined, rethrown);\n\tconst found = objects.filter(\n\t\t(object): object is ObjectLike => object !== null,\n\t);\n\tif (found.length < objects.length) throw denial('NOT_FOUND');\n\tconst answers = await Promise.all(\n\t\tfound.map((object) => ask(permission, object, asking)),\n\t).then(undefined, rethrown);\n\tif (answers.includes(false)) throw denial(permission.onDeny);\n}\n\nasync function ask(\n\tpermission: PermissionCheck,\n\tobject: ObjectLike,\n\t{ check, user, ctx }: Asking,\n): Promise<boolean> {\n\tconst options =\n\t\tpermission.condition === null\n\t\t\t? undefined\n\t\t\t: { ctx: await permission.condition(object, ctx) };\n\treturn check(user, permission.name, object, options);\n}\n\n/**\n * What no object id can hold, as `@nxgt/janus` reads the notation: `can()`\n * refuses such an id with a `TypeError`, and one sent by a client is no\n * object's. A copy of the core's `RESERVED`, recorded in AGENTS.md's\n * *Deliberate duplications*.\n */\nconst UNNAMEABLE = /^$|[@#()]/;\n\n/**\n * Whether an id sent by a client can name an object: none of the characters\n * above, no NUL and no lone surrogate — which no store keeps, so `can()`\n * holds them for nobody, and the application's loader must never see them.\n * A copy of the core's `isStorable`, recorded beside `UNNAMEABLE`.\n */\nconst isNameable = (id: string): boolean =>\n\t!UNNAMEABLE.test(id) && !id.includes('\\u0000') && id.isWellFormed();\n\n/**\n * The object `can()` is given for one target: the value that holds the id,\n * the loader's answer, or `{ type, id }`. `null` when there is none: the\n * loader found none, or the id could name no object.\n */\nasync function objectOf(\n\tpermission: PermissionCheck,\n\t{ id, holder }: Target,\n\tctx: unknown,\n): Promise<ObjectLike | null> {\n\tconst { type, load } = permission;\n\tif (!isNameable(id)) return null;\n\tif (holder !== null) return view(holder, type, id);\n\tif (load === null) return { type, id };\n\tconst loaded = await load(id, ctx);\n\tif (loaded === null) return null;\n\tif (typeof loaded !== 'object') {\n\t\tthrow new TypeError(\n\t\t\t`${permission.label}: loaders.${type} answered ${typeof loaded} — answer the object, or null when there is none`,\n\t\t);\n\t}\n\treturn view(loaded, type, id);\n}\n\n/**\n * The object as `can()` reads it: `type` added — and `id`, the string read\n * from the path, so an integer id reads as graphql-js serialises it — every\n * other field read from the object itself, so a getter, a\n * class's `#private` state or an ORM document's accessors answer as they do\n * in the resolver. A spread would copy own enumerable fields only. The\n * proxy's target is an empty object rather than `object`, so a frozen\n * object whose own `type` means something else is read without breaking a\n * proxy invariant.\n */\nfunction view(object: object, type: string, id: string): ObjectLike {\n\treturn new Proxy(Object.create(null) as object, {\n\t\tget: (_, key) =>\n\t\t\tkey === 'type' ? type : key === 'id' ? id : Reflect.get(object, key),\n\t\thas: (_, key) => key === 'type' || Reflect.has(object, key),\n\t}) as ObjectLike;\n}\n\nconst warned = new WeakSet<PermissionCheck>();\n\n/** Says once per directive, on the only channel a library has, why a field answers `NOT_FOUND`. */\nfunction warnNoId(permission: PermissionCheck): void {\n\tif (warned.has(permission)) return;\n\twarned.add(permission);\n\tprocess.emitWarning(\n\t\t`${permission.label} resolved no object id from ${permission.path.text} — answered NOT_FOUND`,\n\t\t{ code: 'JANUS_GRAPHQL_NO_OBJECT_ID' },\n\t);\n}\n",
@@ -19,10 +20,11 @@
19
20
  "/**\n * One `@permission`, checked against the model and the field when the schema\n * is built, and turned into what the guard asks at request time. Everything\n * no request could pass is a `TypeError` naming the field.\n */\n\nimport type { PermissionUse } from '../read';\nimport { list, on, PREFIX } from '../words';\nimport {\n\tfieldsOf,\n\ttype ModelLike,\n\tmodelOf,\n\tnamesOf,\n\treachesCondition,\n} from './model';\nimport { type IdPath, parsePath } from './path';\n\n/** `useJanus({ loaders })`'s entry: the object of a type, by id, or `null`. */\nexport type Loader = (id: string, ctx: unknown) => unknown;\n/** `useJanus({ conditions })`'s entry: the `ctx` a type's conditions test. */\nexport type Condition = (object: unknown, ctx: unknown) => unknown;\n\n/** What `@permission` reads from `useJanus()`'s options. */\nexport interface PermissionWiring {\n\treadonly access?: unknown;\n\treadonly loaders?: { readonly [type: string]: unknown } | undefined;\n\treadonly conditions?: { readonly [type: string]: unknown } | undefined;\n}\n\n/** One `@permission`, as the guard asks it. */\nexport interface PermissionCheck {\n\t/** `@permission on Record (read by Record.title)`, for the messages. */\n\treadonly label: string;\n\treadonly name: string;\n\treadonly type: string;\n\treadonly onDeny: 'NOT_FOUND' | 'FORBIDDEN';\n\treadonly path: IdPath;\n\t/** Loads an object named by id alone; `null` passes `{ type, id }`. */\n\treadonly load: Loader | null;\n\t/** Answers the check's `ctx`; `null` when no condition is reachable. */\n\treadonly condition: Condition | null;\n}\n\n/** The field a directive is read for: `Type.field`, and the arguments it takes. */\nexport interface FieldShape {\n\treadonly name: string;\n\treadonly args: readonly string[];\n}\n\n/** `use`, checked, or a `TypeError` naming the field. */\nexport function permissionCheckOf(\n\tuse: PermissionUse,\n\tfield: FieldShape,\n\twiring: PermissionWiring,\n): PermissionCheck {\n\tconst label = `@permission on ${on(use.where, field.name)}`;\n\tconst refuse = (why: string) => new TypeError(`${PREFIX}: ${label} ${why}`);\n\tconst model = modelOf(wiring.access);\n\tif (model === null) {\n\t\tthrow refuse(\n\t\t\t'needs the permissions() instance — pass useJanus({ auth, access }), with access what permissions() answered',\n\t\t);\n\t}\n\tcheckNames(model, use, refuse);\n\tconst text = use.id ?? (use.onType ? 'parent.id' : 'args.id');\n\tconst path = parsePath(text);\n\tif (path === null) {\n\t\tthrow refuse(\n\t\t\t`reads its id from '${text}', which is not args.<name> or parent.<name>`,\n\t\t);\n\t}\n\tconst [first] = path.segments;\n\tif (path.root === 'args' && !field.args.includes(first as string)) {\n\t\tthrow refuse(\n\t\t\t`reads ${path.root}.${first}, and ${field.name} takes no argument ${first}${\n\t\t\t\tuse.id === null && use.onType === false\n\t\t\t\t\t? ' — name the id with id: \"parent.<field>\" or id: \"args.<name>\"'\n\t\t\t\t\t: ''\n\t\t\t}`,\n\t\t);\n\t}\n\treturn {\n\t\tlabel,\n\t\tname: use.name,\n\t\ttype: use.type,\n\t\tonDeny: use.onDeny,\n\t\tpath,\n\t\tload: loaderOf(model, use.type, path, wiring, refuse),\n\t\tcondition: conditionOf(model, use, wiring, refuse),\n\t};\n}\n\ntype Refuse = (why: string) => TypeError;\n\nfunction checkNames(model: ModelLike, use: PermissionUse, refuse: Refuse) {\n\tif (!model.types.includes(use.type)) {\n\t\tthrow refuse(\n\t\t\t`names the object type '${use.type}', which is not one of ${list(model.types)}`,\n\t\t);\n\t}\n\tconst names = namesOf(model, use.type);\n\tif (!names.includes(use.name)) {\n\t\tthrow refuse(\n\t\t\t`asks '${use.name}', which ${use.type} does not declare — it declares ${list(names)}`,\n\t\t);\n\t}\n}\n\n/** The loader a check needs: its type's, required when the object is named by id alone and reads its own fields. */\nfunction loaderOf(\n\tmodel: ModelLike,\n\ttype: string,\n\tpath: IdPath,\n\twiring: PermissionWiring,\n\trefuse: Refuse,\n): Loader | null {\n\tconst load = wiring.loaders?.[type];\n\tif (load !== undefined && typeof load !== 'function') {\n\t\tthrow refuse(`finds loaders.${type}, which is not a function`);\n\t}\n\tconst held = path.root === 'parent' && path.segments.at(-1) === 'id';\n\tif (held) return null;\n\tif (load !== undefined) return load as Loader;\n\tconst fields = fieldsOf(model, type);\n\tif (fields.length > 0) {\n\t\tthrow refuse(\n\t\t\t`reads the ${type}'s id from ${path.text}, and ${type} reads ${list(fields)} of the object itself (fromField) — pass useJanus({ loaders: { ${type}: (id, ctx) => … } })`,\n\t\t);\n\t}\n\treturn null;\n}\n\n/** The type's `conditions` entry, required when the permission reaches a `when`. */\nfunction conditionOf(\n\tmodel: ModelLike,\n\tuse: PermissionUse,\n\twiring: PermissionWiring,\n\trefuse: Refuse,\n): Condition | null {\n\tif (!reachesCondition(model, use.type, use.name)) return null;\n\tconst condition = wiring.conditions?.[use.type];\n\tif (typeof condition !== 'function') {\n\t\tthrow refuse(\n\t\t\t`asks '${use.name}' of ${use.type}, which reaches a when() — pass useJanus({ conditions: { ${use.type}: (object, ctx) => … } })`,\n\t\t);\n\t}\n\treturn condition as Condition;\n}\n",
20
21
  "/**\n * Decides, when the schema is built, what the directives on one field add up\n * to — and refuses with a `TypeError` what no request could ever pass: a user\n * type `auth` does not know, a `type: []`, restrictions that exclude each\n * other, a `@fresh` whose `maxAge` is not above zero, or a `@permission` the\n * model, the field or the wiring cannot answer (`permission/validate.ts`).\n */\n\nimport {\n\ttype FieldShape,\n\ttype PermissionCheck,\n\ttype PermissionWiring,\n\tpermissionCheckOf,\n} from './permission/validate';\nimport type { AuthenticatedUse, FieldDirectives, FreshUse } from './read';\nimport { list, on, PREFIX } from './words';\n\n/** What a field requires before its resolver runs. */\nexport interface Requirement {\n\t/** `Type.field`, for the messages. */\n\treadonly where: string;\n\t/** The directive a missing `ctx.janus` is reported against. */\n\treadonly label: string;\n\t/**\n\t * The user types allowed, every `type:` intersected; `null` when none\n\t * narrows it: any signed-in user.\n\t */\n\treadonly types: ReadonlySet<string> | null;\n\t/**\n\t * How long ago, in milliseconds, the session may have proved who it is at\n\t * most — the smallest `@fresh` that applies; `null` when none does.\n\t */\n\treadonly maxAgeMs: number | null;\n\t/** The permissions asked after that, in order: every one must hold. */\n\treadonly permissions: readonly PermissionCheck[];\n}\n\n/** The user types a schema may name: `auth.types`, or the one `useJanus({ type })` keeps. */\nexport interface Known {\n\treadonly types: readonly string[];\n}\n\n/**\n * The requirement of one field, or `null` when nothing guards it. Throws a\n * `TypeError` naming the field for a directive no request could pass.\n */\nexport function requirementOf(\n\tfield: FieldShape,\n\tdirectives: FieldDirectives,\n\tknown: Known,\n\twiring: PermissionWiring,\n): Requirement | null {\n\tconst { authenticated, fresh, permission } = directives;\n\tconst [name, first] = firstOf(directives);\n\tif (first === undefined) return null;\n\treturn {\n\t\twhere: field.name,\n\t\tlabel: `${name} on ${on(first.where, field.name)}`,\n\t\ttypes: typesOf(field.name, authenticated, known),\n\t\tmaxAgeMs: maxAgeOf(field.name, fresh),\n\t\tpermissions: permission.map((use) => permissionCheckOf(use, field, wiring)),\n\t};\n}\n\n/** Every `type:` intersected, or `null` when none names any. */\nfunction typesOf(\n\tfield: string,\n\tuses: readonly AuthenticatedUse[],\n\tknown: Known,\n): ReadonlySet<string> | null {\n\tlet types: Set<string> | null = null;\n\tfor (const { where, types: named } of uses) {\n\t\tif (named === null) continue;\n\t\tif (named.length === 0) {\n\t\t\tthrow new TypeError(\n\t\t\t\t`${PREFIX}: @authenticated on ${on(where, field)} names no user type — leave type: out to admit any signed-in user`,\n\t\t\t);\n\t\t}\n\t\tfor (const name of named) {\n\t\t\tif (!known.types.includes(name)) {\n\t\t\t\tthrow new TypeError(\n\t\t\t\t\t`${PREFIX}: @authenticated on ${on(where, field)} names the user type '${name}', which is not one of ${list(known.types)}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\ttypes =\n\t\t\ttypes === null\n\t\t\t\t? new Set(named)\n\t\t\t\t: new Set(named.filter((name) => types?.has(name)));\n\t}\n\tif (types !== null && types.size === 0) {\n\t\tthrow new TypeError(\n\t\t\t`${PREFIX}: the @authenticated on ${field} and on its type or interfaces admit no user type in common`,\n\t\t);\n\t}\n\treturn types;\n}\n\n/** The directive a missing `ctx.janus` is reported against: the first checked. */\nfunction firstOf({\n\tauthenticated,\n\tfresh,\n\tpermission,\n}: FieldDirectives): readonly [string, { readonly where: string } | undefined] {\n\tif (authenticated.length > 0) return ['@authenticated', authenticated[0]];\n\tif (fresh.length > 0) return ['@fresh', fresh[0]];\n\treturn ['@permission', permission[0]];\n}\n\n/** The smallest `maxAge`, in milliseconds, or `null` when no `@fresh` applies. */\nfunction maxAgeOf(field: string, uses: readonly FreshUse[]): number | null {\n\tlet seconds: number | null = null;\n\tfor (const { where, maxAge } of uses) {\n\t\tif (!Number.isSafeInteger(maxAge) || maxAge <= 0) {\n\t\t\tthrow new TypeError(\n\t\t\t\t`${PREFIX}: @fresh on ${on(where, field)} asks maxAge: ${maxAge} — write a number of seconds above zero, such as maxAge: 600 for ten minutes`,\n\t\t\t);\n\t\t}\n\t\tseconds = seconds === null ? maxAge : Math.min(seconds, maxAge);\n\t}\n\treturn seconds === null ? null : seconds * 1000;\n}\n",
21
22
  "/**\n * The two calls a resolver makes itself: `requireUser` where a directive\n * would not do, and `can` for a check the schema cannot express.\n */\n\nimport type {\n\tCheckArgs,\n\tCheckableOf,\n\tModelConfig,\n\tObjectRef,\n\tObjectTypeOf,\n\tPermissions,\n} from '@nxgt/janus/permissions';\nimport { checkOf, janusOf } from './context';\nimport { denial, rethrown } from './errors';\n\n/** What `requireUser()` takes. */\nexport interface RequireUserOptions<T extends string> {\n\t/**\n\t * Only a user of this type, or of one of these — never an empty list,\n\t * which no user could pass: any other is `FORBIDDEN`.\n\t */\n\treadonly type?: T | readonly [T, ...T[]];\n}\n\n/**\n * The signed-in user, narrowed to `type` when it is given — or a denial:\n * `UNAUTHENTICATED` 401 for an anonymous request, `FORBIDDEN` 403 for a user\n * of another type. A store that cannot answer is `SERVICE_UNAVAILABLE` 503,\n * never either of them.\n *\n * ```ts\n * const staff = await requireUser(ctx, { type: 'staff' });\n * staff.username; // typed: a staff member\n * ```\n */\nexport async function requireUser<\n\tU extends { readonly type: string },\n\tconst T extends U['type'] = U['type'],\n>(\n\tctx: { readonly janus: { user(): Promise<U | null> } },\n\toptions: RequireUserOptions<T> = {},\n): Promise<Extract<U, { readonly type: T }>> {\n\tconst { type } = options;\n\tconst allowed: readonly string[] | undefined =\n\t\ttypeof type === 'string' ? [type] : type;\n\tif (allowed?.length === 0) {\n\t\tthrow new TypeError(\n\t\t\t'requireUser(): type is an empty list, which no user could pass — name at least one user type, or leave type out',\n\t\t);\n\t}\n\tconst user = await janusOf(ctx, 'requireUser()')\n\t\t.user()\n\t\t.then(undefined, rethrown);\n\tif (user === null) throw denial('UNAUTHENTICATED');\n\tif (allowed !== undefined && !allowed.includes(user.type)) {\n\t\tthrow denial('FORBIDDEN');\n\t}\n\treturn user as unknown as Extract<U, { readonly type: T }>;\n}\n\n/**\n * Whether the request's user holds `permission` on `object`, typed as\n * `access.can` is: the permission must be one the object's type declares,\n * the object must carry every field its `fromField`s read, and `{ ctx }` is\n * required exactly when a condition is reachable. Anonymous answers `false`.\n *\n * **A failure throws**: a store that cannot answer is `SERVICE_UNAVAILABLE`\n * 503, never `false`.\n *\n * ```ts\n * if (!(await can(ctx, 'edit', { type: 'record', ...record }))) return null;\n * ```\n */\nexport async function can<\n\tC extends ModelConfig,\n\tconst T extends ObjectTypeOf<C>,\n\tconst P extends CheckableOf<C, T>,\n>(\n\tctx: {\n\t\treadonly janus: {\n\t\t\tuser(): Promise<{ readonly type: string; readonly id: string } | null>;\n\t\t\treadonly access: Pick<Permissions<C>, 'can'>;\n\t\t};\n\t},\n\tpermission: P,\n\tobject: ObjectRef<C, T>,\n\t...options: CheckArgs<C, T, P>\n): Promise<boolean> {\n\tconst janus = janusOf(ctx, 'can()');\n\tconst check = checkOf(janus);\n\tif (check === null) {\n\t\tthrow new TypeError(\n\t\t\t'can(): ctx.janus.access is not set — pass { access } to useJanus()',\n\t\t);\n\t}\n\tconst subject = await janus.user().then(undefined, rethrown);\n\treturn check(subject, permission, object, options[0]).then(\n\t\tundefined,\n\t\trethrown,\n\t);\n}\n",
22
- "/**\n * `useJanus()`, the envelop plugin — for Yoga, or any server built on\n * envelop.\n */\n\nimport type { Plugin } from '@envelop/core';\nimport type { RequestLike } from '@nxgt/janus';\nimport type { GraphQLSchema } from 'graphql';\nimport { acceptedOf } from './connection/credential';\nimport { createJanusContext } from './context';\nimport { applyJanusDirectives } from './directives/apply';\nimport { checkWiring } from './options';\nimport type { Auth, JanusContext, JanusOptions, UserOfAuth } from './types';\n\n/**\n * The plugin that wires `@nxgt/janus` into a GraphQL server:\n *\n * - every request's context gets `ctx.janus` — `user()` and `session()`,\n * authenticated lazily and once, and `access` when it is given;\n * - every schema the server is given has its directives applied, once, by\n * `applyJanusDirectives()`.\n *\n * ```ts\n * createYoga({\n * schema: createSchema({ typeDefs: [janusTypeDefs, typeDefs], resolvers }),\n * plugins: [useJanus({ auth, access })],\n * maskedErrors: { maskError: janusMaskError() },\n * });\n * ```\n *\n * `type` treats a user of any other type as anonymous, as\n * `auth.authenticate(request, { type })` does. `clock` is what `@fresh` and\n * `requireFresh()` read the time from — the one given to `janus()`.\n * `loaders` and `conditions` are what `@permission` needs beside `access`:\n * the objects it checks by id, and the `ctx` of the conditions it reaches.\n *\n * An operation over graphql-ws has no request: its `ctx.janus`\n * authenticates the credential its connection presented, once\n * `janusConnection().onConnect` accepted it.\n */\nexport function useJanus<\n\tA extends Auth<{ readonly type: string; readonly id: string }>,\n\tP extends object | undefined = undefined,\n\tconst T extends UserOfAuth<A>['type'] = UserOfAuth<A>['type'],\n>(options: JanusOptions<A, P, T>): Plugin<JanusContext<A, P, T>> {\n\tconst { auth, access, type, clock, loaders, conditions } = options;\n\tcheckWiring('useJanus()', { auth, access, clock });\n\tconst applied = new WeakSet<GraphQLSchema>();\n\n\treturn {\n\t\tonContextBuilding: ({ context, extendContext }) => {\n\t\t\tconst { request } = context as { readonly request?: RequestLike };\n\t\t\tconst janus = createJanusContext(request ?? acceptedOf(context), {\n\t\t\t\tauth,\n\t\t\t\taccess: access as { readonly can: unknown } | undefined,\n\t\t\t\ttype,\n\t\t\t\tclock,\n\t\t\t});\n\t\t\textendContext({ janus } as unknown as Partial<JanusContext<A, P, T>>);\n\t\t},\n\t\t// `replaceSchema` calls this hook again with the schema it was given:\n\t\t// the one it made is remembered, or it would transform it for ever.\n\t\tonSchemaChange: ({ schema, replaceSchema }) => {\n\t\t\tif (applied.has(schema)) return;\n\t\t\tconst next = applyJanusDirectives(schema, {\n\t\t\t\tauth,\n\t\t\t\t...(type === undefined ? {} : { type }),\n\t\t\t\taccess: access as { readonly model: unknown } | undefined,\n\t\t\t\tloaders,\n\t\t\t\tconditions,\n\t\t\t});\n\t\t\tapplied.add(next);\n\t\t\treplaceSchema(next);\n\t\t},\n\t};\n}\n",
23
+ "/**\n * The renewed session cookie: `authenticate` renews a sliding session in\n * passing, and `useJanus()` sends it again on the HTTP response — as\n * `@nxgt/janus-hono`'s `session()` does — through Yoga's `onResponse`.\n */\n\nimport type { Session } from '@nxgt/janus';\nimport { answerOf } from './context';\n\n/** The part of `auth.cookie` a renewal needs. */\nexport interface SessionCookie {\n\treadonly name: string;\n\tserialize(token: string, session: Session): string;\n}\n\n/** What Yoga hands `onResponse`: the part of it read here. */\nexport interface ResponsePayload {\n\treadonly request: Request;\n\treadonly response: Response;\n}\n\n/**\n * The contexts built for each HTTP request: one, or one per operation of a\n * batched request. A `WeakMap`, so an answered request takes its entry with\n * it.\n */\nconst contexts = new WeakMap<object, object[]>();\n\n/** Remembers `janus` as a context built for `request`. */\nexport function track(request: object, janus: object): void {\n\tconst known = contexts.get(request);\n\tif (known === undefined) contexts.set(request, [janus]);\n\telse known.push(janus);\n}\n\n/**\n * Sends the session cookie again when a context of `request` renewed it —\n * only when `authenticate` ran for it, so an operation that asked nothing\n * costs nothing here. As `session()` in `@nxgt/janus-hono`, only to a\n * request that presented the token as the session cookie — a client that sent\n * `Authorization: Bearer` or `X-Session-Token` is not handed a cookie it never\n * asked for — and never over a session cookie the response already sets.\n *\n * A failed `authenticate` sends nothing: its error is the response's.\n */\nexport async function resendRenewed(\n\t{ request, response }: ResponsePayload,\n\tcookie: SessionCookie | undefined,\n): Promise<void> {\n\tconst known = contexts.get(request);\n\tif (cookie === undefined || known === undefined) return;\n\tfor (const janus of known) {\n\t\tconst current = await answerOf(janus)?.then(\n\t\t\t(answer) => answer,\n\t\t\t() => null,\n\t\t);\n\t\tif (\n\t\t\tcurrent?.renewed === true &&\n\t\t\tcookieOf(request, cookie.name) === current.token &&\n\t\t\t!sets(response, cookie.name)\n\t\t) {\n\t\t\tresponse.headers.append(\n\t\t\t\t'Set-Cookie',\n\t\t\t\tcookie.serialize(current.token, current.session),\n\t\t\t);\n\t\t}\n\t}\n}\n\n/** The value of the cookie `name` the request sent, or `null`. */\nfunction cookieOf(request: Request, name: string): string | null {\n\tconst header = request.headers.get('cookie');\n\tif (header === null) return null;\n\tfor (const pair of header.split(';')) {\n\t\tconst at = pair.indexOf('=');\n\t\tif (at !== -1 && pair.slice(0, at).trim() === name) {\n\t\t\treturn pair.slice(at + 1).trim();\n\t\t}\n\t}\n\treturn null;\n}\n\n/** Whether the response already sets the cookie `name`. */\nfunction sets(response: Response, name: string): boolean {\n\treturn response.headers\n\t\t.getSetCookie()\n\t\t.some((value) => value.startsWith(`${name}=`));\n}\n",
24
+ "/**\n * `useJanus()`, the envelop plugin — for Yoga, or any server built on\n * envelop.\n */\n\nimport type { Plugin } from '@envelop/core';\nimport type { RequestLike } from '@nxgt/janus';\nimport type { GraphQLSchema } from 'graphql';\nimport { acceptedOf } from './connection/credential';\nimport { createJanusContext } from './context';\nimport { applyJanusDirectives } from './directives/apply';\nimport { checkWiring } from './options';\nimport { resendRenewed, track } from './renewal';\nimport type { Auth, JanusContext, JanusOptions, UserOfAuth } from './types';\n\n/**\n * The plugin that wires `@nxgt/janus` into a GraphQL server:\n *\n * - every request's context gets `ctx.janus` — `user()` and `session()`,\n * authenticated lazily and once, and `access` when it is given;\n * - every schema the server is given has its directives applied, once, by\n * `applyJanusDirectives()`.\n *\n * ```ts\n * createYoga({\n * schema: createSchema({ typeDefs: [janusTypeDefs, typeDefs], resolvers }),\n * plugins: [useJanus({ auth, access })],\n * maskedErrors: { maskError: janusMaskError() },\n * });\n * ```\n *\n * `type` treats a user of any other type as anonymous, as\n * `auth.authenticate(request, { type })` does. `clock` is what `@fresh` and\n * `requireFresh()` read the time from — the one given to `janus()`.\n * `loaders` and `conditions` are what `@permission` needs beside `access`:\n * the objects it checks by id, and the `ctx` of the conditions it reaches.\n *\n * A session `authenticate` renewed for a request that presented it as the\n * session cookie is sent again, `Set-Cookie` on Yoga's response\n * (`onResponse`), so a browser keeps the new expiry. Not to a bearer token or\n * `X-Session-Token`, and never when nothing authenticated.\n *\n * An operation over graphql-ws has no request: its `ctx.janus`\n * authenticates the credential its connection presented, once\n * `janusConnection().onConnect` accepted it — and has no response to carry a\n * renewed cookie.\n */\nexport function useJanus<\n\tA extends Auth<{ readonly type: string; readonly id: string }>,\n\tP extends object | undefined = undefined,\n\tconst T extends UserOfAuth<A>['type'] = UserOfAuth<A>['type'],\n>(\n\toptions: JanusOptions<A, P, T>,\n): Plugin<JanusContext<A, P, T>> & {\n\treadonly onResponse: (payload: {\n\t\treadonly request: Request;\n\t\treadonly response: Response;\n\t}) => Promise<void>;\n} {\n\tconst { auth, access, type, clock, loaders, conditions } = options;\n\tcheckWiring('useJanus()', { auth, access, clock });\n\tconst applied = new WeakSet<GraphQLSchema>();\n\n\treturn {\n\t\tonContextBuilding: ({ context, extendContext }) => {\n\t\t\tconst { request } = context as { readonly request?: RequestLike };\n\t\t\tconst janus = createJanusContext(request ?? acceptedOf(context), {\n\t\t\t\tauth,\n\t\t\t\taccess: access as { readonly can: unknown } | undefined,\n\t\t\t\ttype,\n\t\t\t\tclock,\n\t\t\t});\n\t\t\tif (typeof request === 'object' && request !== null) {\n\t\t\t\ttrack(request, janus);\n\t\t\t}\n\t\t\textendContext({ janus } as unknown as Partial<JanusContext<A, P, T>>);\n\t\t},\n\t\t// `replaceSchema` calls this hook again with the schema it was given:\n\t\t// the one it made is remembered, or it would transform it for ever.\n\t\tonSchemaChange: ({ schema, replaceSchema }) => {\n\t\t\tif (applied.has(schema)) return;\n\t\t\tconst next = applyJanusDirectives(schema, {\n\t\t\t\tauth,\n\t\t\t\t...(type === undefined ? {} : { type }),\n\t\t\t\taccess: access as { readonly model: unknown } | undefined,\n\t\t\t\tloaders,\n\t\t\t\tconditions,\n\t\t\t});\n\t\t\tapplied.add(next);\n\t\t\treplaceSchema(next);\n\t\t},\n\t\t// Yoga's own hook, which envelop ignores: the HTTP response, once made.\n\t\tonResponse: (payload) => resendRenewed(payload, auth.cookie),\n\t};\n}\n",
23
25
  "/**\n * The directives and the enum this package reads, as SDL: put it beside your\n * own type definitions. The same text ships as `graphql/janus.graphqls`, for\n * a code generator or an editor that reads files — `sdl.spec.ts` holds that\n * the two are identical.\n *\n * ```ts\n * createSchema({ typeDefs: [janusTypeDefs, typeDefs], resolvers });\n * ```\n */\nexport const janusTypeDefs: string = `\"\"\"\nOnly a signed-in user reaches this field, or every field of this type or\ninterface. With \\`type\\`, only a user of one of these user types: any other is\nanswered FORBIDDEN.\n\"\"\"\ndirective @authenticated(\n\ttype: [String!]\n) on OBJECT | INTERFACE | FIELD_DEFINITION\n\n\"\"\"\nOnly a session that proved who it is less than \\`maxAge\\` seconds ago reaches\nthis field, or every field of this type or interface: signed in, or confirmed\nsince by a step-up. Anonymous is answered UNAUTHENTICATED, an older session\nSTEP_UP_REQUIRED. Where several apply, the smallest \\`maxAge\\` holds.\n\"\"\"\ndirective @fresh(maxAge: Int!) on OBJECT | INTERFACE | FIELD_DEFINITION\n\n\"\"\"\nWhat a denied \\`@permission\\` answers.\n\"\"\"\nenum JanusPermissionDenial {\n\t\"\"\"\n\t404: the object is not told to exist. The default.\n\t\"\"\"\n\tNOT_FOUND\n\t\"\"\"\n\t403: the object exists, and this user may not do this to it.\n\t\"\"\"\n\tFORBIDDEN\n}\n\n\"\"\"\nOnly a user holding permission \\`name\\` on the object of \\`type\\` whose id \\`id\\`\nreads reaches this field, or every field of this type or interface. \\`id\\` is\n\\`args.<path>\\` or \\`parent.<path>\\`: \\`args.id\\` on a field and \\`parent.id\\` on a\ntype or an interface when absent. A list of ids requires the permission on\nevery one. Repeated, every one must hold, in the order written.\n\"\"\"\ndirective @permission(\n\tname: String!\n\ttype: String!\n\tid: String\n\tonDeny: JanusPermissionDenial! = NOT_FOUND\n) repeatable on OBJECT | INTERFACE | FIELD_DEFINITION\n`;\n"
24
26
  ],
25
- "mappings": ";AAMA;AAAA;AAAA;;;ACaA,IAAM,WAAW;AAQjB,SAAS,KAAK,CACb,SACA,YACA,QACA,SACgB;AAAA,EAChB,IAAI,YAAY,QAAQ,SAAS,QAAQ;AAAA,IAAW,OAAO;AAAA,EAC3D,IAAI,SAAS,KAAK,OAAO,EAAE,KAAK,SAAS,KAAK,QAAQ,EAAE;AAAA,IAAG,OAAO;AAAA,EAClE,MAAM,MACL,OAAO,QAAQ,aAAa,WAAW,IAAI,QAAQ,aAAa;AAAA,EACjE,OAAO,GAAG,OAAO,QAAQ,OAAO,MAAM,cAAc,QAAQ,QAAQ,QAAQ,KAAK;AAAA;AAS3E,SAAS,QAAQ,CAAC,QAA0C;AAAA,EAClE,MAAM,MAAM,OAAO;AAAA,EACnB,MAAM,UAAU,IAAI;AAAA,EACpB,MAAM,MAAa,OAAO,SAAS,YAAY,QAAQ,YACtD,IAAI,KAAK,QAAQ,SAAS,YAAY,QAAQ,OAAO;AAAA,EACtD,OAAO,CAAC,SAAS,YAAY,QAAQ,YAAY;AAAA,IAChD,MAAM,MAAM,MAAM,SAAS,YAAY,QAAQ,OAAO;AAAA,IACtD,IAAI,QAAQ;AAAA,MAAM,OAAO,IAAI,SAAS,YAAY,QAAQ,OAAO;AAAA,IACjE,MAAM,QAAQ,QAAQ,IAAI,GAAG;AAAA,IAC7B,IAAI,UAAU;AAAA,MAAW,OAAO;AAAA,IAChC,MAAM,SAAS,IAAI,SAAS,YAAY,QAAQ,OAAO;AAAA,IACvD,QAAQ,IAAI,KAAK,MAAM;AAAA,IACvB,OAAO,KAAK,WAAW,MAAM,QAAQ,OAAO,GAAG,CAAC;AAAA,IAChD,OAAO;AAAA;AAAA;;;ADlBT,IAAM,SAAS,IAAI;AAGnB,IAAM,SAAS,IAAI;AAsBZ,SAAS,kBAAkB,CACjC,SACA,SACQ;AAAA,EACR,QAAQ,MAAM,QAAQ,MAAM,OAAO,SAAS,iBAAiB;AAAA,EAC7D,IAAI;AAAA,EACJ,IAAI,QAAQ;AAAA,EACZ,MAAM,gBAAgB,MAAM;AAAA,IAC3B,IAAI,CAAC,OAAO;AAAA,MACX,QAAQ;AAAA,MACR,SACC,YAAY,YACT,QAAQ,OACR,IAAI,UACH,GAAG,yKACJ,CACD,IACC,KAAK,aACL,SACA,SAAS,YAAY,YAAY,EAAE,KAAK,CACzC;AAAA,IACJ;AAAA,IACA,OAAO;AAAA;AAAA,EAGR,MAAM,QAAQ,OAAO,OAAO;AAAA,IAC3B,MAAM,MAAM,cAAc,EAAE,KAAK,CAAC,YAAY,SAAS,QAAQ,IAAI;AAAA,IACnE,SAAS,MAAM,cAAc,EAAE,KAAK,CAAC,YAAY,SAAS,WAAW,IAAI;AAAA,OACrE,WAAW,YAAY,CAAC,IAAI,EAAE,OAAO;AAAA,EAC1C,CAAC;AAAA,EACD,IAAI,WAAW,WAAW;AAAA,IACzB,MAAM,MAAM,OAAO;AAAA,IACnB,OAAO,IAAI,OAAO;AAAA,MACjB,MAAM,SAAS,MAAM;AAAA,MACrB,OAAO,IAAI,SAAS,IAAI,MAAM,QAAQ,IAAI;AAAA,IAC3C,CAAC;AAAA,EACF;AAAA,EACA,IAAI,UAAU;AAAA,IAAW,OAAO,IAAI,OAAO,KAAK;AAAA,EAChD,OAAO;AAAA;AAOD,SAAS,OAAO,CAAC,OAAsB;AAAA,EAC7C,OAAO,OAAO,IAAI,KAAK,KAAK;AAAA;AAYtB,SAAS,OAAO,CAAC,OAAe,QAAQ,OAAqB;AAAA,EACnE,MAAM,QAAQ,OAAO,IAAI,KAAK;AAAA,EAC9B,IAAI,UAAU;AAAA,IAAW,OAAO,QAAQ,MAAM,QAAQ,MAAM;AAAA,EAC5D,MAAM,SAAU,MACd;AAAA,EACF,OAAO,OAAO,QAAQ,QAAQ,aAAc,OAAO,MAAgB;AAAA;AAO7D,SAAS,OAAO,CAAC,KAAc,QAAuB;AAAA,EAC5D,MAAM,QAAS,KAA2C;AAAA,EAC1D,IAAI,UAAU,aAAa,OAAO,MAAM,SAAS,YAAY;AAAA,IAC5D,MAAM,IAAI,UACT,GAAG,sEACJ;AAAA,EACD;AAAA,EACA,OAAO;AAAA;;;AE9HD,SAAS,WAAW,CAAC,UAAkB,MAAM,QAAQ,SAAiB;AAAA,EAC5E,IACC,OAAQ,MACL,iBAAiB,YACnB;AAAA,IACD,MAAM,IAAI,UACT,GAAG,2DACJ;AAAA,EACD;AAAA,EACA,IACC,WAAW,aACX,OAAQ,QAA8C,QAAQ,YAC7D;AAAA,IACD,MAAM,IAAI,UACT,GAAG,2EACJ;AAAA,EACD;AAAA,EACA,IACC,UAAU,aACV,OAAQ,OAA6C,QAAQ,YAC5D;AAAA,IACD,MAAM,IAAI,UACT,GAAG,sGACJ;AAAA,EACD;AAAA;;;AC5BD,IAAM,SAAS;AAkBR,SAAS,YAAY,CAC3B,KACA,SACqB;AAAA,EACrB,MAAM,SAAS,IAAI;AAAA,EACnB,IAAI,WAAW,aAAa,OAAO,OAAO,QAAQ,eAAe,GAAG;AAAA,IACnE,QAAQ,kBAAkB;AAAA,IAC1B,IAAI,OAAO,kBAAkB;AAAA,MAAU,OAAO;AAAA,IAG9C,IAAI,OAAO,KAAK,aAAa;AAAA,MAAG,OAAO,EAAE,cAAc;AAAA,EACxD;AAAA,EACA,MAAM,UACL,YAAY,YACR,IAAI,OAAqD,UAC1D,QAAQ,GAAG;AAAA,EACf,OAAO,WAAW;AAAA;AAUnB,IAAM,WAAW,IAAI;AAGd,SAAS,MAAM,CAAC,KAAwB,YAAyB;AAAA,EACvE,QAAQ,UAAU;AAAA,EAClB,IAAI,OAAO,UAAU,YAAY,UAAU,MAAM;AAAA,IAChD,MAAM,IAAI,UACT,yGACD;AAAA,EACD;AAAA,EACA,SAAS,IAAI,OAAO,UAAU;AAAA;AAOxB,SAAS,UAAU,CAAC,SAA2C;AAAA,EACrE,MAAM,QAAS,SAAiD;AAAA,EAChE,IAAI,OAAO,UAAU,YAAY,UAAU;AAAA,IAAM;AAAA,EACjD,OAAO,SAAS,IAAI,KAAK;AAAA;;;ACzCnB,SAAS,eAIf,CAAC,SAAoE;AAAA,EACrE,QAAQ,MAAM,QAAQ,MAAM,UAAU;AAAA,EACtC,YAAY,qBAAqB,EAAE,MAAM,QAAQ,MAAM,CAAC;AAAA,EACxD,IAAI,QAAQ,YAAY,aAAa,OAAO,QAAQ,YAAY,YAAY;AAAA,IAC3E,MAAM,IAAI,UACT,6HACD;AAAA,EACD;AAAA,EACA,MAAM,UAAU,QAAQ,SAAS,KAAK,OAAO;AAAA,EAC7C,MAAM,SAAS;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,EACT;AAAA,EAEA,OAAO;AAAA,IACN,WAAW,OAAO,QAAQ;AAAA,MACzB,MAAM,aAAa,aAAa,KAAK,OAAO;AAAA,MAC5C,IAAI,eAAe;AAAA,QAAM,OAAO;AAAA,MAGhC,MAAM,UAAU,MAAM,KAAK,aAC1B,YACA,SAAS,YAAY,YAAY,EAAE,KAAK,CACzC;AAAA,MACA,IAAI,YAAY;AAAA,QAAM,OAAO;AAAA,MAC7B,OAAO,KAAK,UAAU;AAAA,MACtB,OAAO;AAAA;AAAA,IAGR,SAAS,CAAC,SACR;AAAA,MACA,OAAO,mBAAmB,WAAW,GAAG,GAAG,MAAM;AAAA,IAClD;AAAA,EACF;AAAA;;ACvED;AACA;;;ACCA;AACA;AAMO,SAAS,MAAM,CAAC,MAAkB,SAAgC;AAAA,EACxE,OAAO,QAAQ,YAAY,SAAS,IAAI;AAAA,EACxC,OAAO,IAAI,aAAa,WAAW,UAAU;AAAA,IAC5C,YAAY,EAAE,MAAM,MAAM,EAAE,OAAO,EAAE;AAAA,EACtC,CAAC;AAAA;AAGF,SAAS,QAAQ,CAAC,MAA6C;AAAA,EAC9D,QAAQ;AAAA,SACF;AAAA,MACJ,OAAO,CAAC,KAAK,eAAe;AAAA,SACxB;AAAA,MACJ,OAAO,CAAC,KAAK,WAAW;AAAA,SACpB;AAAA,MACJ,OAAO,CAAC,KAAK,WAAW;AAAA;AAAA;AAYpB,SAAS,iBAAiB,CAAC,OAAiC;AAAA,EAClE,MAAM,SAAS,SAAS,MAAM,IAAI;AAAA,EAClC,MAAM,OACL,MAAM,SAAS,iBAAiB,wBAAwB,MAAM;AAAA,EAC/D,OAAO,IAAI,aAAa,UAAU,MAAM,GAAG;AAAA,IAC1C,eAAe;AAAA,IACf,YAAY,EAAE,MAAM,MAAM,EAAE,OAAO,MAAM,WAAW,KAAK,EAAE;AAAA,EAC5D,CAAC;AAAA;AAOF,SAAS,SAAS,CAAC,QAAkC;AAAA,EACpD,QAAQ;AAAA,SACF;AAAA,MACJ,OAAO;AAAA,SACH;AAAA,MACJ,OAAO;AAAA,SACH;AAAA,MACJ,OAAO;AAAA,SACH;AAAA,MACJ,OAAO;AAAA,SACH;AAAA,MACJ,OAAO;AAAA,SACH;AAAA,MACJ,OAAO;AAAA,SACH;AAAA,SACA;AAAA,MACJ,OAAO;AAAA;AAAA;AAKV,SAAS,UAAU,CAAC,OAA4C;AAAA,EAC/D,QAAQ,MAAM;AAAA,SACR;AAAA,MACJ,OAAO,EAAE,QAAQ,MAAM,UAAU,CAAC,EAAE;AAAA,SAChC;AAAA,MACJ,OAAO,MAAM,cAAc,YACxB,CAAC,IACD,EAAE,WAAW,MAAM,UAAU;AAAA,SAC5B;AAAA,MACJ,OAAO,MAAM,iBAAiB,YAC3B,CAAC,IACD,EAAE,cAAc,MAAM,aAAa;AAAA;AAAA,MAEtC,OAAO,CAAC;AAAA;AAAA;AAQJ,SAAS,QAAQ,CAAC,OAAuB;AAAA,EAC/C,MAAM,iBAAiB,aAAa,kBAAkB,KAAK,IAAI;AAAA;AAwBzD,SAAS,cAAc,CAAC,WAAsB,QAAmB;AAAA,EACvE,OAAO,CAAC,OAAO,SAAS,UAAU;AAAA,IACjC,MAAM,WACL,iBAAiB,eAAe,MAAM,gBAAgB;AAAA,IACvD,IAAI,oBAAoB,YAAY;AAAA,MACnC,MAAM,SAAS,kBAAkB,QAAQ;AAAA,MACzC,OAAO,iBAAiB,eACrB,IAAI,aAAa,OAAO,SAAS;AAAA,QACjC,OAAO,MAAM,SAAS;AAAA,QACtB,MAAM,MAAM,QAAQ;AAAA,QACpB,eAAe;AAAA,QACf,YAAY,OAAO;AAAA,MACpB,CAAC,IACA;AAAA,IACJ;AAAA,IACA,OAAO,SAAS,OAAO,SAAS,KAAK;AAAA;AAAA;AAKvC,SAAS,MAAM,CAAC,OAAgB,SAAwB;AAAA,EACvD,IAAI,iBAAiB,cAAc;AAAA,IAClC,MAAM,WAAW,MAAM;AAAA,IACvB,IAAI,aAAa,aAAa,oBAAoB,cAAc;AAAA,MAC/D,OAAO;AAAA,IACR;AAAA,IACA,OAAO,IAAI,aAAa,SAAS;AAAA,MAChC,OAAO,MAAM,SAAS;AAAA,MACtB,MAAM,MAAM,QAAQ;AAAA,MACpB,YAAY,EAAE,MAAM,wBAAwB;AAAA,IAC7C,CAAC;AAAA,EACF;AAAA,EACA,OAAO,IAAI,aAAa,SAAS;AAAA,IAChC,YAAY,EAAE,MAAM,wBAAwB;AAAA,EAC7C,CAAC;AAAA;;;ACpJF;AAAA;AAAA;AAAA;AAeA,eAAsB,YAAY,CACjC,OACA,UACmB;AAAA,EACnB,MAAM,UAAU,MAAM,MAAM,QAAQ,EAAE,KAAK,WAAW,QAAQ;AAAA,EAC9D,IAAI,YAAY;AAAA,IAAM,MAAM,OAAO,iBAAiB;AAAA,EACpD,IAAI;AAAA,IACH,YAAY,SAAS,UAAU,QAAQ,KAAK,CAAC;AAAA,IAC5C,OAAO,OAAO;AAAA,IACf,SAAS,KAAK;AAAA;AAAA,EAEf,OAAO;AAAA;AAiBR,eAAsB,YAAY,CACjC,KAMA,QACmB;AAAA,EACnB,IAAI,OAAO,WAAW,UAAU;AAAA,IAC/B,MAAM,IAAI,UACT,qIACD;AAAA,EACD;AAAA,EACA,MAAM,WAAW,cAAc,QAAQ,wBAAwB;AAAA,EAC/D,OAAO,aAAa,QAAQ,KAAK,gBAAgB,GAAG,QAAQ;AAAA;;;AClD7D,IAAM,OAAO;AAGN,SAAS,SAAS,CAAC,MAA6B;AAAA,EACtD,OAAO,SAAS,YAAY,KAAK,MAAM,GAAG;AAAA,EAC1C,IAAI,SAAS,UAAU,SAAS;AAAA,IAAU,OAAO;AAAA,EACjD,IAAI,SAAS,WAAW,KAAK,CAAC,SAAS,MAAM,CAAC,SAAS,KAAK,KAAK,IAAI,CAAC,GAAG;AAAA,IACxE,OAAO;AAAA,EACR;AAAA,EACA,OAAO,EAAE,MAAM,MAAM,SAAS;AAAA;AAkBxB,SAAS,SAAS,CACxB,MACA,QACA,MAC2B;AAAA,EAC3B,MAAM,aAAa,KAClB,CAAC,KAAK,SAAS,SAAS,OAAO,MAAM,GACrC,KAAK,SAAS,MAAM,GAAG,EAAE,CAC1B;AAAA,EACA,IAAI,eAAe;AAAA,IAAM,OAAO;AAAA,EAChC,MAAM,OAAO,KAAK,SAAS,KAAK,SAAS,SAAS;AAAA,EAClD,MAAM,OAAO,KAAK,SAAS,YAAY,SAAS;AAAA,EAChD,MAAM,UAAoB,CAAC;AAAA,EAC3B,WAAW,aAAa,YAAY;AAAA,IACnC,WAAW,SAAS,OAAQ,UAAsC,KAAK,GAAG;AAAA,MACzE,MAAM,KAAK,KAAK,KAAK;AAAA,MACrB,IAAI,OAAO;AAAA,QAAM,OAAO;AAAA,MACxB,QAAQ,KAAK,EAAE,IAAI,QAAQ,OAAQ,YAAuB,KAAK,CAAC;AAAA,IACjE;AAAA,EACD;AAAA,EACA,OAAO,KAAK,SAAS,YAAY,QAAQ,WAAW,IAAI,OAAO;AAAA;AAIhE,SAAS,IAAI,CACZ,QACA,UAC4B;AAAA,EAC5B,IAAI,UAAU;AAAA,EACd,WAAW,WAAW,UAAU;AAAA,IAC/B,MAAM,OAAkB,CAAC;AAAA,IACzB,WAAW,SAAS,SAAS;AAAA,MAC5B,IAAI,OAAO,UAAU,YAAY,UAAU;AAAA,QAAM,OAAO;AAAA,MACxD,KAAK,KAAK,GAAG,OAAQ,MAAkC,QAAQ,CAAC;AAAA,IACjE;AAAA,IACA,UAAU;AAAA,EACX;AAAA,EACA,OAAO,QAAQ,MAAM,CAAC,UAAU,OAAO,UAAU,YAAY,UAAU,IAAI,IACxE,UACA;AAAA;AAGJ,SAAS,MAAM,CAAC,OAAoC;AAAA,EACnD,OAAO,MAAM,QAAQ,KAAK,IAAI,MAAM,KAAK,OAAO,iBAAiB,IAAI,CAAC,KAAK;AAAA;AAI5E,SAAS,IAAI,CAAC,OAA+B;AAAA,EAC5C,IAAI,OAAO,UAAU;AAAA,IAAU,OAAO;AAAA,EACtC,IAAI,OAAO,UAAU,YAAY,OAAO,cAAc,KAAK,GAAG;AAAA,IAC7D,OAAO,OAAO,KAAK;AAAA,EACpB;AAAA,EACA,OAAO;AAAA;;;ACrER,eAAsB,iBAAiB,CACtC,YACA,QACgB;AAAA,EAChB,MAAM,UAAU,UAAU,WAAW,MAAM,OAAO,QAAQ,OAAO,IAAI;AAAA,EACrE,IAAI,YAAY,MAAM;AAAA,IACrB,SAAS,UAAU;AAAA,IACnB,MAAM,OAAO,WAAW;AAAA,EACzB;AAAA,EACA,MAAM,UAAU,MAAM,QAAQ,IAC7B,QAAQ,IAAI,CAAC,WAAW,SAAS,YAAY,QAAQ,OAAO,GAAG,CAAC,CACjE,EAAE,KAAK,WAAW,QAAQ;AAAA,EAC1B,MAAM,QAAQ,QAAQ,OACrB,CAAC,WAAiC,WAAW,IAC9C;AAAA,EACA,IAAI,MAAM,SAAS,QAAQ;AAAA,IAAQ,MAAM,OAAO,WAAW;AAAA,EAC3D,MAAM,UAAU,MAAM,QAAQ,IAC7B,MAAM,IAAI,CAAC,WAAW,IAAI,YAAY,QAAQ,MAAM,CAAC,CACtD,EAAE,KAAK,WAAW,QAAQ;AAAA,EAC1B,IAAI,QAAQ,SAAS,KAAK;AAAA,IAAG,MAAM,OAAO,WAAW,MAAM;AAAA;AAG5D,eAAe,GAAG,CACjB,YACA,UACE,OAAO,MAAM,OACI;AAAA,EACnB,MAAM,UACL,WAAW,cAAc,OACtB,YACA,EAAE,KAAK,MAAM,WAAW,UAAU,QAAQ,GAAG,EAAE;AAAA,EACnD,OAAO,MAAM,MAAM,WAAW,MAAM,QAAQ,OAAO;AAAA;AASpD,IAAM,aAAa;AAQnB,IAAM,aAAa,CAAC,OACnB,CAAC,WAAW,KAAK,EAAE,KAAK,CAAC,GAAG,SAAS,MAAQ,KAAK,GAAG,aAAa;AAOnE,eAAe,QAAQ,CACtB,cACE,IAAI,UACN,KAC6B;AAAA,EAC7B,QAAQ,MAAM,SAAS;AAAA,EACvB,IAAI,CAAC,WAAW,EAAE;AAAA,IAAG,OAAO;AAAA,EAC5B,IAAI,WAAW;AAAA,IAAM,OAAO,KAAK,QAAQ,MAAM,EAAE;AAAA,EACjD,IAAI,SAAS;AAAA,IAAM,OAAO,EAAE,MAAM,GAAG;AAAA,EACrC,MAAM,SAAS,MAAM,KAAK,IAAI,GAAG;AAAA,EACjC,IAAI,WAAW;AAAA,IAAM,OAAO;AAAA,EAC5B,IAAI,OAAO,WAAW,UAAU;AAAA,IAC/B,MAAM,IAAI,UACT,GAAG,WAAW,kBAAkB,iBAAiB,OAAO,wDACzD;AAAA,EACD;AAAA,EACA,OAAO,KAAK,QAAQ,MAAM,EAAE;AAAA;AAa7B,SAAS,IAAI,CAAC,QAAgB,MAAc,IAAwB;AAAA,EACnE,OAAO,IAAI,MAAM,OAAO,OAAO,IAAI,GAAa;AAAA,IAC/C,KAAK,CAAC,GAAG,QACR,QAAQ,SAAS,OAAO,QAAQ,OAAO,KAAK,QAAQ,IAAI,QAAQ,GAAG;AAAA,IACpE,KAAK,CAAC,GAAG,QAAQ,QAAQ,UAAU,QAAQ,IAAI,QAAQ,GAAG;AAAA,EAC3D,CAAC;AAAA;AAGF,IAAM,SAAS,IAAI;AAGnB,SAAS,QAAQ,CAAC,YAAmC;AAAA,EACpD,IAAI,OAAO,IAAI,UAAU;AAAA,IAAG;AAAA,EAC5B,OAAO,IAAI,UAAU;AAAA,EACrB,QAAQ,YACP,GAAG,WAAW,oCAAoC,WAAW,KAAK,6BAClE,EAAE,MAAM,6BAA6B,CACtC;AAAA;;;ACtGD,eAAsB,OAAO,CAC5B,eACE,QAAQ,MAAM,KAAK,SACL;AAAA,EAChB,MAAM,QAAQ,QAAQ,KAAK,YAAY,KAAK;AAAA,EAC5C,MAAM,OAAO,MAAM,MAAM,KAAK,EAAE,KAAK,WAAW,QAAQ;AAAA,EACxD,IAAI,SAAS;AAAA,IAAM,MAAM,OAAO,iBAAiB;AAAA,EACjD,IAAI,YAAY,UAAU,QAAQ,CAAC,YAAY,MAAM,IAAI,KAAK,IAAI,GAAG;AAAA,IACpE,MAAM,OAAO,WAAW;AAAA,EACzB;AAAA,EACA,IAAI,YAAY,aAAa,MAAM;AAAA,IAClC,MAAM,aAAa,OAAO,YAAY,QAAQ;AAAA,EAC/C;AAAA,EACA,OAAO,SAAS,YAAY;AAAA,EAC5B,IAAI,UAAU;AAAA,IAAW;AAAA,EACzB,MAAM,QAAQ,QAAQ,OAAO,KAAK;AAAA,EAClC,IAAI,UAAU,MAAM;AAAA,IACnB,MAAM,IAAI,UACT,GAAG,MAAM,oEACV;AAAA,EACD;AAAA,EAGA,WAAW,cAAc,YAAY,aAAa;AAAA,IACjD,MAAM,kBAAkB,YAAY,EAAE,OAAO,MAAM,QAAQ,MAAM,IAAI,CAAC;AAAA,EACvE;AAAA;AAIM,SAAS,OAAgB,CAC/B,SACA,aACgC;AAAA,EAChC,OAAO,OAAO,QAAQ,MAAM,KAAK,SAAS;AAAA,IACzC,MAAM,QAAQ,aAAa;AAAA,MAC1B,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA,OAAO,KAAK,UAAU,cAAc;AAAA,IACrC,CAAC;AAAA,IACD,OAAO,QAAQ,QAAQ,MAAM,KAAK,IAAI;AAAA;AAAA;;;AC1DxC;AA0DA,SAAS,SAAS,CACjB,QACA,OACA,WACA,UACY;AAAA,EACZ,MAAM,OAAO,OAAO,QAAQ,QAAQ;AAAA,EACpC,MAAM,QAAQ,KAAK,cAAc;AAAA,EACjC,MAAM,SAAoB,CAAC;AAAA,EAC3B,WAAW,QAAQ,OAAO;AAAA,IACzB,MAAM,MAAM,KAAK,UAAU,EAAE;AAAA,IAC7B,IAAI,QAAQ,WAAW;AAAA,MACtB,OAAO,KAAK;AAAA,QACX,OAAO,GAAG,KAAK,QAAQ;AAAA,QACvB,QAAQ;AAAA,QACR,MAAM;AAAA,MACP,CAAC;AAAA,IACF;AAAA,EACD;AAAA,EACA,OAAO;AAAA,IACN,GAAG,MAAM,IAAI,CAAC,UAAU,EAAE,OAAO,KAAK,MAAM,QAAQ,MAAM,MAAM,KAAK,EAAE;AAAA,IACvE,EAAE,OAAO,UAAU,QAAQ,MAAM,MAAM,KAAK;AAAA,IAC5C,GAAG;AAAA,IACH,EAAE,OAAO,GAAG,YAAY,aAAa,QAAQ,OAAO,MAAM,MAAM;AAAA,EACjE;AAAA;AAIM,SAAS,SAAS,CACxB,QACA,OACA,WACA,UACkB;AAAA,EAClB,MAAM,gBAAoC,CAAC;AAAA,EAC3C,MAAM,QAAoB,CAAC;AAAA,EAC3B,MAAM,aAA8B,CAAC;AAAA,EACrC,aAAa,OAAO,QAAQ,UAAU,UACrC,QACA,OACA,WACA,QACD,GAAG;AAAA,IACF,WAAW,QAAQ,aAAa,QAAQ,MAAM,eAAe,KAAK,CAAC,GAAG;AAAA,MACrE,MAAM,QAAQ,KAAK;AAAA,MACnB,cAAc,KAAK,EAAE,OAAO,OAAO,SAAS,KAAK,CAAC;AAAA,IACnD;AAAA,IACA,WAAW,QAAQ,aAAa,QAAQ,MAAM,OAAO,KAAK,CAAC,GAAG;AAAA,MAC7D,MAAM,KAAK,EAAE,OAAO,QAAQ,KAAK,OAAiB,CAAC;AAAA,IACpD;AAAA,IACA,WAAW,QAAQ,aAAa,QAAQ,MAAM,YAAY,KAAK,CAAC,GAAG;AAAA,MAClE,WAAW,KAAK;AAAA,QACf;AAAA,QACA;AAAA,QACA,MAAM,KAAK;AAAA,QACX,MAAM,KAAK;AAAA,QACX,IAAK,KAAK,MAAoC;AAAA,QAC9C,QAAQ,KAAK,WAAW,cAAc,cAAc;AAAA,MACrD,CAAC;AAAA,IACF;AAAA,EACD;AAAA,EACA,OAAO,EAAE,eAAe,OAAO,WAAW;AAAA;;;AC5HpC,IAAM,SAAS;AAGf,SAAS,EAAE,CAAC,OAAe,OAAuB;AAAA,EACxD,OAAO,UAAU,QAAQ,QAAQ,GAAG,kBAAkB;AAAA;AAIhD,SAAS,IAAI,CAAC,OAAkC;AAAA,EACtD,OAAO,MAAM,IAAI,CAAC,SAAS,IAAI,OAAO,EAAE,KAAK,IAAI;AAAA;;;ACU3C,SAAS,OAAO,CAAC,QAAmC;AAAA,EAC1D,MAAM,QAAS,QACZ;AAAA,EACH,IAAI,CAAC,MAAM,QAAQ,OAAO,KAAK,KAAK,MAAM,YAAY,UAAU,WAAW;AAAA,IAC1E,OAAO;AAAA,EACR;AAAA,EACA,OAAO;AAAA;AAID,SAAS,OAAO,CAAC,OAAkB,MAAiC;AAAA,EAC1E,MAAM,MAAM,MAAM,WAAW,MAAM;AAAA,EACnC,OAAO;AAAA,IACN,GAAG,OAAO,KAAK,KAAK,WAAW,CAAC,CAAC;AAAA,IACjC,GAAG,OAAO,KAAK,KAAK,WAAW,CAAC,CAAC;AAAA,EAClC;AAAA;AASD,SAAS,WAAW,CAAC,OAAwC;AAAA,EAC5D,OAAQ,OAA8C,SAAS;AAAA;AAIzD,SAAS,QAAQ,CAAC,OAAkB,MAAiC;AAAA,EAC3E,MAAM,UAAU,MAAM,WAAW,MAAM,OAAO,WAAW,CAAC;AAAA,EAC1D,OAAO,OAAO,OAAO,OAAO,EAC1B,OAAO,WAAW,EAClB,IAAI,CAAC,QAAQ,IAAI,KAAK;AAAA;AAQlB,SAAS,gBAAgB,CAC/B,OACA,MACA,MACA,OAAoB,IAAI,KACd;AAAA,EACV,MAAM,MAAM,GAAG,QAAQ;AAAA,EACvB,IAAI,KAAK,IAAI,GAAG;AAAA,IAAG,OAAO;AAAA,EAC1B,KAAK,IAAI,GAAG;AAAA,EACZ,MAAM,MAAM,MAAM,WAAW,MAAM;AAAA,EACnC,WAAW,QAAQ,KAAK,UAAU,SAAS,CAAC,GAAG;AAAA,IAC9C,IAAI,OAAO,SAAS;AAAA,MAAU,OAAO;AAAA,IACrC,OAAO,UAAU,UAAU,KAAK,MAAM,IAAI;AAAA,IAC1C,IAAI,WAAW,WAAW;AAAA,MACzB,IAAI,iBAAiB,OAAO,MAAM,MAAM,IAAI;AAAA,QAAG,OAAO;AAAA,MACtD;AAAA,IACD;AAAA,IACA,WAAW,QAAQ,aAAa,KAAK,UAAU,SAAmB,GAAG;AAAA,MACpE,IAAI,iBAAiB,OAAO,MAAM,QAAQ,IAAI;AAAA,QAAG,OAAO;AAAA,IACzD;AAAA,EACD;AAAA,EACA,OAAO;AAAA;AAIR,SAAS,YAAY,CAAC,UAAsC;AAAA,EAC3D,IAAI,YAAY,QAAQ,GAAG;AAAA,IAC1B,OAAO,CAAC,SAAS,OAAO;AAAA,EACzB;AAAA,EACA,IAAI,CAAC,MAAM,QAAQ,QAAQ;AAAA,IAAG,OAAO,CAAC;AAAA,EACtC,OAAO,SACL,OAAO,CAAC,WAA6B,OAAO,WAAW,QAAQ,EAC/D,IAAI,CAAC,WAAW,OAAO,MAAM,GAAG,EAAE,EAAY;AAAA;;;AC5C1C,SAAS,iBAAiB,CAChC,KACA,OACA,QACkB;AAAA,EAClB,MAAM,QAAQ,kBAAkB,GAAG,IAAI,OAAO,MAAM,IAAI;AAAA,EACxD,MAAM,SAAS,CAAC,QAAgB,IAAI,UAAU,GAAG,WAAW,SAAS,KAAK;AAAA,EAC1E,MAAM,QAAQ,QAAQ,OAAO,MAAM;AAAA,EACnC,IAAI,UAAU,MAAM;AAAA,IACnB,MAAM,OACL,6GACD;AAAA,EACD;AAAA,EACA,WAAW,OAAO,KAAK,MAAM;AAAA,EAC7B,MAAM,OAAO,IAAI,OAAO,IAAI,SAAS,cAAc;AAAA,EACnD,MAAM,OAAO,UAAU,IAAI;AAAA,EAC3B,IAAI,SAAS,MAAM;AAAA,IAClB,MAAM,OACL,sBAAsB,kDACvB;AAAA,EACD;AAAA,EACA,OAAO,SAAS,KAAK;AAAA,EACrB,IAAI,KAAK,SAAS,UAAU,CAAC,MAAM,KAAK,SAAS,KAAe,GAAG;AAAA,IAClE,MAAM,OACL,SAAS,KAAK,QAAQ,cAAc,MAAM,0BAA0B,QACnE,IAAI,OAAO,QAAQ,IAAI,WAAW,QAC/B,kEACA,IAEL;AAAA,EACD;AAAA,EACA,OAAO;AAAA,IACN;AAAA,IACA,MAAM,IAAI;AAAA,IACV,MAAM,IAAI;AAAA,IACV,QAAQ,IAAI;AAAA,IACZ;AAAA,IACA,MAAM,SAAS,OAAO,IAAI,MAAM,MAAM,QAAQ,MAAM;AAAA,IACpD,WAAW,YAAY,OAAO,KAAK,QAAQ,MAAM;AAAA,EAClD;AAAA;AAKD,SAAS,UAAU,CAAC,OAAkB,KAAoB,QAAgB;AAAA,EACzE,IAAI,CAAC,MAAM,MAAM,SAAS,IAAI,IAAI,GAAG;AAAA,IACpC,MAAM,OACL,0BAA0B,IAAI,8BAA8B,KAAK,MAAM,KAAK,GAC7E;AAAA,EACD;AAAA,EACA,MAAM,QAAQ,QAAQ,OAAO,IAAI,IAAI;AAAA,EACrC,IAAI,CAAC,MAAM,SAAS,IAAI,IAAI,GAAG;AAAA,IAC9B,MAAM,OACL,SAAS,IAAI,gBAAgB,IAAI,uCAAuC,KAAK,KAAK,GACnF;AAAA,EACD;AAAA;AAID,SAAS,QAAQ,CAChB,OACA,MACA,MACA,QACA,QACgB;AAAA,EAChB,MAAM,OAAO,OAAO,UAAU;AAAA,EAC9B,IAAI,SAAS,aAAa,OAAO,SAAS,YAAY;AAAA,IACrD,MAAM,OAAO,iBAAiB,+BAA+B;AAAA,EAC9D;AAAA,EACA,MAAM,OAAO,KAAK,SAAS,YAAY,KAAK,SAAS,GAAG,EAAE,MAAM;AAAA,EAChE,IAAI;AAAA,IAAM,OAAO;AAAA,EACjB,IAAI,SAAS;AAAA,IAAW,OAAO;AAAA,EAC/B,MAAM,SAAS,SAAS,OAAO,IAAI;AAAA,EACnC,IAAI,OAAO,SAAS,GAAG;AAAA,IACtB,MAAM,OACL,aAAa,kBAAkB,KAAK,aAAa,cAAc,KAAK,MAAM,mEAAmE,2BAC9I;AAAA,EACD;AAAA,EACA,OAAO;AAAA;AAIR,SAAS,WAAW,CACnB,OACA,KACA,QACA,QACmB;AAAA,EACnB,IAAI,CAAC,iBAAiB,OAAO,IAAI,MAAM,IAAI,IAAI;AAAA,IAAG,OAAO;AAAA,EACzD,MAAM,YAAY,OAAO,aAAa,IAAI;AAAA,EAC1C,IAAI,OAAO,cAAc,YAAY;AAAA,IACpC,MAAM,OACL,SAAS,IAAI,YAAY,IAAI,gEAAgE,IAAI,+BAClG;AAAA,EACD;AAAA,EACA,OAAO;AAAA;;;ACpGD,SAAS,aAAa,CAC5B,OACA,YACA,OACA,QACqB;AAAA,EACrB,QAAQ,eAAe,OAAO,eAAe;AAAA,EAC7C,OAAO,MAAM,SAAS,QAAQ,UAAU;AAAA,EACxC,IAAI,UAAU;AAAA,IAAW,OAAO;AAAA,EAChC,OAAO;AAAA,IACN,OAAO,MAAM;AAAA,IACb,OAAO,GAAG,WAAW,GAAG,MAAM,OAAO,MAAM,IAAI;AAAA,IAC/C,OAAO,QAAQ,MAAM,MAAM,eAAe,KAAK;AAAA,IAC/C,UAAU,SAAS,MAAM,MAAM,KAAK;AAAA,IACpC,aAAa,WAAW,IAAI,CAAC,QAAQ,kBAAkB,KAAK,OAAO,MAAM,CAAC;AAAA,EAC3E;AAAA;AAID,SAAS,OAAO,CACf,OACA,MACA,OAC6B;AAAA,EAC7B,IAAI,QAA4B;AAAA,EAChC,aAAa,OAAO,OAAO,WAAW,MAAM;AAAA,IAC3C,IAAI,UAAU;AAAA,MAAM;AAAA,IACpB,IAAI,MAAM,WAAW,GAAG;AAAA,MACvB,MAAM,IAAI,UACT,GAAG,6BAA6B,GAAG,OAAO,KAAK,oEAChD;AAAA,IACD;AAAA,IACA,WAAW,QAAQ,OAAO;AAAA,MACzB,IAAI,CAAC,MAAM,MAAM,SAAS,IAAI,GAAG;AAAA,QAChC,MAAM,IAAI,UACT,GAAG,6BAA6B,GAAG,OAAO,KAAK,0BAA0B,8BAA8B,KAAK,MAAM,KAAK,GACxH;AAAA,MACD;AAAA,IACD;AAAA,IACA,QACC,UAAU,OACP,IAAI,IAAI,KAAK,IACb,IAAI,IAAI,MAAM,OAAO,CAAC,SAAS,OAAO,IAAI,IAAI,CAAC,CAAC;AAAA,EACrD;AAAA,EACA,IAAI,UAAU,QAAQ,MAAM,SAAS,GAAG;AAAA,IACvC,MAAM,IAAI,UACT,GAAG,iCAAiC,kEACrC;AAAA,EACD;AAAA,EACA,OAAO;AAAA;AAIR,SAAS,OAAO;AAAA,EACf;AAAA,EACA;AAAA,EACA;AAAA,GAC8E;AAAA,EAC9E,IAAI,cAAc,SAAS;AAAA,IAAG,OAAO,CAAC,kBAAkB,cAAc,EAAE;AAAA,EACxE,IAAI,MAAM,SAAS;AAAA,IAAG,OAAO,CAAC,UAAU,MAAM,EAAE;AAAA,EAChD,OAAO,CAAC,eAAe,WAAW,EAAE;AAAA;AAIrC,SAAS,QAAQ,CAAC,OAAe,MAA0C;AAAA,EAC1E,IAAI,UAAyB;AAAA,EAC7B,aAAa,OAAO,YAAY,MAAM;AAAA,IACrC,IAAI,CAAC,OAAO,cAAc,MAAM,KAAK,UAAU,GAAG;AAAA,MACjD,MAAM,IAAI,UACT,GAAG,qBAAqB,GAAG,OAAO,KAAK,kBAAkB,oFAC1D;AAAA,IACD;AAAA,IACA,UAAU,YAAY,OAAO,SAAS,KAAK,IAAI,SAAS,MAAM;AAAA,EAC/D;AAAA,EACA,OAAO,YAAY,OAAO,OAAO,UAAU;AAAA;;;AVvErC,SAAS,oBAA4C,CAC3D,QACA,SACgB;AAAA,EAChB,MAAM,QAAQ,QAAQ,OAAO;AAAA,EAC7B,OAAO,UAAU,QAAQ;AAAA,KACvB,WAAW,eAAe,CAAC,OAAO,WAAW,aAAa;AAAA,MAC1D,MAAM,cAAc,cACnB;AAAA,QACC,MAAM,GAAG,YAAY;AAAA,QACrB,MAAM,OAAO,KAAK,MAAM,QAAQ,CAAC,CAAC;AAAA,MACnC,GACA,UAAU,QAAQ,OAAO,WAAW,QAAQ,GAC5C,OACA,OACD;AAAA,MACA,IAAI,gBAAgB;AAAA,QAAM,OAAO;AAAA,MACjC,MAAM,UAAU,MAAM,WAAW;AAAA,MACjC,IAAI,MAAM,cAAc,WAAW;AAAA,QAClC,OAAO,KAAK,OAAO,SAAS,QAAQ,SAAS,WAAW,EAAE;AAAA,MAC3D;AAAA,MAGA,MAAM,UAAU,KAAK,aAAa,UAAU,KAAK;AAAA,MACjD,OAAO;AAAA,WACH;AAAA,QACH,WAAW,QAAQ,MAAM,WAAW,WAAW;AAAA,QAC/C,SAAS,QAAQ,SAAS,OAAO;AAAA,MAClC;AAAA;AAAA,EAEF,CAAC;AAAA;AAIF,SAAS,OAAO,GAAG,MAAM,QAAuC;AAAA,EAC/D,IAAI,SAAS;AAAA,IAAW,OAAO,EAAE,OAAO,KAAK,MAAM;AAAA,EACnD,IAAI,CAAC,KAAK,MAAM,SAAS,IAAI,GAAG;AAAA,IAC/B,MAAM,IAAI,UACT,GAAG,iBAAiB,+CAA+C,KAAK,KAAK,KAAK,GACnF;AAAA,EACD;AAAA,EACA,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE;AAAA;;AWtDxB,eAAsB,WAGrB,CACA,KACA,UAAiC,CAAC,GACU;AAAA,EAC5C,QAAQ,SAAS;AAAA,EACjB,MAAM,UACL,OAAO,SAAS,WAAW,CAAC,IAAI,IAAI;AAAA,EACrC,IAAI,SAAS,WAAW,GAAG;AAAA,IAC1B,MAAM,IAAI,UACT,iHACD;AAAA,EACD;AAAA,EACA,MAAM,OAAO,MAAM,QAAQ,KAAK,eAAe,EAC7C,KAAK,EACL,KAAK,WAAW,QAAQ;AAAA,EAC1B,IAAI,SAAS;AAAA,IAAM,MAAM,OAAO,iBAAiB;AAAA,EACjD,IAAI,YAAY,aAAa,CAAC,QAAQ,SAAS,KAAK,IAAI,GAAG;AAAA,IAC1D,MAAM,OAAO,WAAW;AAAA,EACzB;AAAA,EACA,OAAO;AAAA;AAgBR,eAAsB,GAIrB,CACA,KAMA,YACA,WACG,SACgB;AAAA,EACnB,MAAM,QAAQ,QAAQ,KAAK,OAAO;AAAA,EAClC,MAAM,QAAQ,QAAQ,KAAK;AAAA,EAC3B,IAAI,UAAU,MAAM;AAAA,IACnB,MAAM,IAAI,UACT,oEACD;AAAA,EACD;AAAA,EACA,MAAM,UAAU,MAAM,MAAM,KAAK,EAAE,KAAK,WAAW,QAAQ;AAAA,EAC3D,OAAO,MAAM,SAAS,YAAY,QAAQ,QAAQ,EAAE,EAAE,KACrD,WACA,QACD;AAAA;;AC5DM,SAAS,QAIf,CAAC,SAA+D;AAAA,EAChE,QAAQ,MAAM,QAAQ,MAAM,OAAO,SAAS,eAAe;AAAA,EAC3D,YAAY,cAAc,EAAE,MAAM,QAAQ,MAAM,CAAC;AAAA,EACjD,MAAM,UAAU,IAAI;AAAA,EAEpB,OAAO;AAAA,IACN,mBAAmB,GAAG,SAAS,oBAAoB;AAAA,MAClD,QAAQ,YAAY;AAAA,MACpB,MAAM,QAAQ,mBAAmB,WAAW,WAAW,OAAO,GAAG;AAAA,QAChE;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACD,CAAC;AAAA,MACD,cAAc,EAAE,MAAM,CAA8C;AAAA;AAAA,IAIrE,gBAAgB,GAAG,QAAQ,oBAAoB;AAAA,MAC9C,IAAI,QAAQ,IAAI,MAAM;AAAA,QAAG;AAAA,MACzB,MAAM,OAAO,qBAAqB,QAAQ;AAAA,QACzC;AAAA,WACI,SAAS,YAAY,CAAC,IAAI,EAAE,KAAK;AAAA,QACrC;AAAA,QACA;AAAA,QACA;AAAA,MACD,CAAC;AAAA,MACD,QAAQ,IAAI,IAAI;AAAA,MAChB,cAAc,IAAI;AAAA;AAAA,EAEpB;AAAA;;AChEM,IAAM,gBAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;",
26
- "debugId": "C6BF0BFA8BF13A9C64756E2164756E21",
27
+ "mappings": "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",
28
+ "debugId": "33AAA17AADE0081364756E2164756E21",
27
29
  "names": []
28
30
  }
package/dist/plugin.d.ts CHANGED
@@ -26,12 +26,23 @@ import type { Auth, JanusContext, JanusOptions, UserOfAuth } from './types';
26
26
  * `loaders` and `conditions` are what `@permission` needs beside `access`:
27
27
  * the objects it checks by id, and the `ctx` of the conditions it reaches.
28
28
  *
29
+ * A session `authenticate` renewed for a request that presented it as the
30
+ * session cookie is sent again, `Set-Cookie` on Yoga's response
31
+ * (`onResponse`), so a browser keeps the new expiry. Not to a bearer token or
32
+ * `X-Session-Token`, and never when nothing authenticated.
33
+ *
29
34
  * An operation over graphql-ws has no request: its `ctx.janus`
30
35
  * authenticates the credential its connection presented, once
31
- * `janusConnection().onConnect` accepted it.
36
+ * `janusConnection().onConnect` accepted it — and has no response to carry a
37
+ * renewed cookie.
32
38
  */
33
39
  export declare function useJanus<A extends Auth<{
34
40
  readonly type: string;
35
41
  readonly id: string;
36
- }>, P extends object | undefined = undefined, const T extends UserOfAuth<A>['type'] = UserOfAuth<A>['type']>(options: JanusOptions<A, P, T>): Plugin<JanusContext<A, P, T>>;
42
+ }>, P extends object | undefined = undefined, const T extends UserOfAuth<A>['type'] = UserOfAuth<A>['type']>(options: JanusOptions<A, P, T>): Plugin<JanusContext<A, P, T>> & {
43
+ readonly onResponse: (payload: {
44
+ readonly request: Request;
45
+ readonly response: Response;
46
+ }) => Promise<void>;
47
+ };
37
48
  //# sourceMappingURL=plugin.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAO5C,OAAO,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE5E;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,QAAQ,CACvB,CAAC,SAAS,IAAI,CAAC;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,EAC9D,CAAC,SAAS,MAAM,GAAG,SAAS,GAAG,SAAS,EACxC,KAAK,CAAC,CAAC,SAAS,UAAU,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,EAC5D,OAAO,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CA+B/D"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAQ5C,OAAO,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE5E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,QAAQ,CACvB,CAAC,SAAS,IAAI,CAAC;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,EAC9D,CAAC,SAAS,MAAM,GAAG,SAAS,GAAG,SAAS,EACxC,KAAK,CAAC,CAAC,SAAS,UAAU,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,EAE7D,OAAO,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAC5B,MAAM,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG;IAClC,QAAQ,CAAC,UAAU,EAAE,CAAC,OAAO,EAAE;QAC9B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;QAC1B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;KAC5B,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB,CAoCA"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * The renewed session cookie: `authenticate` renews a sliding session in
3
+ * passing, and `useJanus()` sends it again on the HTTP response — as
4
+ * `@nxgt/janus-hono`'s `session()` does — through Yoga's `onResponse`.
5
+ */
6
+ import type { Session } from '@nxgt/janus';
7
+ /** The part of `auth.cookie` a renewal needs. */
8
+ export interface SessionCookie {
9
+ readonly name: string;
10
+ serialize(token: string, session: Session): string;
11
+ }
12
+ /** What Yoga hands `onResponse`: the part of it read here. */
13
+ export interface ResponsePayload {
14
+ readonly request: Request;
15
+ readonly response: Response;
16
+ }
17
+ /** Remembers `janus` as a context built for `request`. */
18
+ export declare function track(request: object, janus: object): void;
19
+ /**
20
+ * Sends the session cookie again when a context of `request` renewed it —
21
+ * only when `authenticate` ran for it, so an operation that asked nothing
22
+ * costs nothing here. As `session()` in `@nxgt/janus-hono`, only to a
23
+ * request that presented the token as the session cookie — a client that sent
24
+ * `Authorization: Bearer` or `X-Session-Token` is not handed a cookie it never
25
+ * asked for — and never over a session cookie the response already sets.
26
+ *
27
+ * A failed `authenticate` sends nothing: its error is the response's.
28
+ */
29
+ export declare function resendRenewed({ request, response }: ResponsePayload, cookie: SessionCookie | undefined): Promise<void>;
30
+ //# sourceMappingURL=renewal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renewal.d.ts","sourceRoot":"","sources":["../src/renewal.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAG3C,iDAAiD;AACjD,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM,CAAC;CACnD;AAED,8DAA8D;AAC9D,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;CAC5B;AASD,0DAA0D;AAC1D,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAI1D;AAED;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CAClC,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,eAAe,EACtC,MAAM,EAAE,aAAa,GAAG,SAAS,GAC/B,OAAO,CAAC,IAAI,CAAC,CAmBf"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * `janusMaskError({ report })`: every `JanusError` answered 5xx, handed to
3
+ * the application's logger before the response goes — the directives' and
4
+ * the helpers' included, which reach the mask already a `GraphQLError`.
5
+ */
6
+ import { JanusError } from '@nxgt/janus';
7
+ /**
8
+ * The `JanusError` behind `error`: itself, or the one a chain of
9
+ * `GraphQLError`s carries as `originalError` — a directive's refusal,
10
+ * located again by graphql-js. `null` when there is none.
11
+ */
12
+ export declare function janusErrorIn(error: unknown): JanusError | null;
13
+ /**
14
+ * A `report` that sees each failure once — a lazy `authenticate` shared by
15
+ * two guarded fields fails both with one error — and only a 5xx: an outage,
16
+ * `UNSUPPORTED`, `PERMISSION_DEPTH`, the ones the server must fix.
17
+ *
18
+ * **It cannot change the answer**: one that throws, or rejects, is a
19
+ * `process.emitWarning`, and the response is sent all the same.
20
+ */
21
+ export declare function reporting(report: (error: JanusError) => unknown): (error: unknown) => void;
22
+ //# sourceMappingURL=report.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,UAAU,EAAY,MAAM,aAAa,CAAC;AAGnD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,UAAU,GAAG,IAAI,CAI9D;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CACxB,MAAM,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,GACpC,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAkB1B"}
package/dist/types.d.ts CHANGED
@@ -4,10 +4,14 @@
4
4
  */
5
5
  import type { Clock, Session, SharedApi } from '@nxgt/janus';
6
6
  import type { PermissionWiringOf } from './wiring';
7
- /** Anything `janus()` answered: the part of it this package calls. */
7
+ /**
8
+ * Anything `janus()` answered: the part of it this package calls. `cookie`
9
+ * is what `useJanus()` sends a renewed session again with; an `auth` without
10
+ * one — a wrapper of your own — sends none.
11
+ */
8
12
  export type Auth<U extends {
9
13
  readonly type: string;
10
- }> = Pick<SharedApi<U>, 'authenticate' | 'types'>;
14
+ }> = Pick<SharedApi<U>, 'authenticate' | 'types'> & Partial<Pick<SharedApi<U>, 'cookie'>>;
11
15
  /** The users an `auth` instance knows, as a union narrowed by `user.type`. */
12
16
  export type UserOfAuth<A> = A extends Auth<infer U> ? U : never;
13
17
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAEnD,sEAAsE;AACtE,MAAM,MAAM,IAAI,CAAC,CAAC,SAAS;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,IAAI,IAAI,CAC3D,SAAS,CAAC,CAAC,CAAC,EACZ,cAAc,GAAG,OAAO,CACxB,CAAC;AAEF,8EAA8E;AAC9E,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;AAEhE;;;;GAIG;AACH,MAAM,MAAM,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,SAAS,MAAM,IAAI;IAClD,+BAA+B;IAC/B,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IACjB;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACpB,8EAA8E;IAC9E,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAClB;;;OAGG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC;CACvB,GAAG,kBAAkB,CACrB,CAAC,EACD;IACC,QAAQ,CAAC,KAAK,EAAE,cAAc,CAC7B,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAA;KAAE,CAAC,EAC5C,CAAC,CACD,CAAC;CACF,CACD,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,CAAC,CAAC,EAAE,CAAC,IAAI;IAClC;;;;OAIG;IACH,IAAI,IAAI,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAC1B,yEAAyE;IACzE,OAAO,IAAI,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;CACnC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,GAAG,OAAO,GAAG;IAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAA;CAAE,CAAC,CAAC;AAEjE;;;;;GAKG;AACH,MAAM,MAAM,YAAY,CACvB,CAAC,EACD,CAAC,GAAG,SAAS,EACb,CAAC,SAAS,UAAU,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IACpD;IACH,QAAQ,CAAC,KAAK,EAAE,cAAc,CAC7B,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAA;KAAE,CAAC,EAC5C,CAAC,CACD,CAAC;CACF,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAEnD;;;;GAIG;AACH,MAAM,MAAM,IAAI,CAAC,CAAC,SAAS;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,IAAI,IAAI,CAC3D,SAAS,CAAC,CAAC,CAAC,EACZ,cAAc,GAAG,OAAO,CACxB,GACA,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC;AAEvC,8EAA8E;AAC9E,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;AAEhE;;;;GAIG;AACH,MAAM,MAAM,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,SAAS,MAAM,IAAI;IAClD,+BAA+B;IAC/B,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IACjB;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACpB,8EAA8E;IAC9E,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAClB;;;OAGG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC;CACvB,GAAG,kBAAkB,CACrB,CAAC,EACD;IACC,QAAQ,CAAC,KAAK,EAAE,cAAc,CAC7B,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAA;KAAE,CAAC,EAC5C,CAAC,CACD,CAAC;CACF,CACD,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,CAAC,CAAC,EAAE,CAAC,IAAI;IAClC;;;;OAIG;IACH,IAAI,IAAI,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAC1B,yEAAyE;IACzE,OAAO,IAAI,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;CACnC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,GAAG,OAAO,GAAG;IAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAA;CAAE,CAAC,CAAC;AAEjE;;;;;GAKG;AACH,MAAM,MAAM,YAAY,CACvB,CAAC,EACD,CAAC,GAAG,SAAS,EACb,CAAC,SAAS,UAAU,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IACpD;IACH,QAAQ,CAAC,KAAK,EAAE,cAAc,CAC7B,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAA;KAAE,CAAC,EAC5C,CAAC,CACD,CAAC;CACF,CAAC"}
package/docs/README.md CHANGED
@@ -8,12 +8,12 @@ give the detail.
8
8
  | [Directives](guide/directives.md) | You are guarding fields, types or interfaces with `@authenticated`, `@fresh` or `@permission`: where the id is read, loaders and conditions, lists, the order, the denials, one check per question, and what is refused at start-up |
9
9
  | [The step-up over GraphQL](guide/step-up.md) | A mutation must run only on a session that proved who it is recently: `@fresh`, `STEP_UP_REQUIRED`, `stepUp.request` and `confirm` as mutations, `requireFresh()`, subscriptions, and testing with a clock |
10
10
  | [Subscriptions over graphql-ws](guide/subscriptions.md) | You serve subscriptions over a WebSocket: `janusConnection()`, the cookie or `connectionParams`, the close codes, a session revoked mid-stream, Bun, and a server without Yoga |
11
- | [The context](guide/context.md) | You are reading the user in a resolver, narrowing it to a user type, or checking a permission or freshness yourself with `requireUser()`, `requireFresh()` and `can()` |
12
- | [Errors](guide/errors.md) | You want to know what a client receives for a denial or an outage, what the status of the response is, and how `janusMaskError()` fits Yoga's masking |
11
+ | [The context](guide/context.md) | You are reading the user in a resolver, narrowing it to a user type, checking a permission or freshness yourself with `requireUser()`, `requireFresh()` and `can()`, or wondering when the renewed session cookie is sent |
12
+ | [Errors](guide/errors.md) | You want to know what a client receives for a denial or an outage, what the status of the response is, how `janusMaskError()` fits Yoga's masking, and how `report` logs every outage |
13
13
  | [Telemetry](guide/telemetry.md) | You want a span per authentication and per permission check, with `@nxgt/janus-telemetry` |
14
14
  | [Federation](guide/federation.md) | The server is a subgraph: where the directives are checked, what the router must forward, and `@composeDirective` |
15
15
  | [Troubleshooting](troubleshooting.md) | A query answers something you did not expect, the server refuses to start, or the compiler refuses a resolver |
16
- | [Roadmap](roadmap.md) | You want to know what is coming — the renewed session cookie — and what is deliberately not planned |
16
+ | [Roadmap](roadmap.md) | You want to know what is coming — anonymous graphql-ws connections — and what is deliberately not planned |
17
17
 
18
18
  The words — user, user type, session, subject, permission, failure, denial —
19
19
  are [`@nxgt/janus`'s vocabulary](https://github.com/softistx/nxgt-janus/blob/develop/packages/janus/docs/guide/vocabulary.md),
@@ -81,8 +81,36 @@ with `STORE_FAILED` rather than answering `null`, and the client receives
81
81
 
82
82
  `authenticate` reads the request as it does anywhere: `Authorization:
83
83
  Bearer`, then `X-Session-Token`, then the cookie. It renews a sliding session
84
- in passing, and **nothing here sends the renewed cookie back**: a GraphQL
85
- response carries no `Set-Cookie` from this package.
84
+ in passing, and **`useJanus()` sends the renewed cookie back**, as
85
+ `@nxgt/janus-hono`'s `session()` does:
86
+
87
+ | The request | The response |
88
+ | --- | --- |
89
+ | Presented the session cookie, and `authenticate` renewed it | `Set-Cookie` with the session's new `Expires` — once, for a batch of operations too |
90
+ | Presented the cookie, not yet due for renewal | No `Set-Cookie` |
91
+ | `Authorization: Bearer` or `X-Session-Token` | No `Set-Cookie`, renewed or not: the client never asked for a cookie, and reads the new expiry from `session().expiresAt` |
92
+ | Anonymous, or no field asked `user()` or `session()` | No `Set-Cookie`, and no store call |
93
+ | The store failed | No `Set-Cookie`: the response is the 503 |
94
+ | A mutation of yours already set the session cookie — a sign-in, a sign-out | Yours: the renewal never overwrites it |
95
+
96
+ The cookie is `auth.cookie.serialize(token, session)`, under the attributes
97
+ `janus({ cookie })` resolved. It is set in Yoga's `onResponse` hook:
98
+
99
+ - **A server on envelop alone** never calls it, and sends no renewed cookie.
100
+ - **An operation over graphql-ws** has no response per operation, so none is
101
+ sent: the store holds the renewed session, but the browser keeps its
102
+ cookie's old expiry. That renewal is spent — an HTTP query right after
103
+ finds the session not yet due, and sends nothing — so the cookie is sent
104
+ again by the next renewal over HTTP, a `renewAfter` later. A client whose
105
+ only traffic is the WebSocket for longer than the lifespan minus
106
+ `renewAfter` should renew through an HTTP route of yours. See
107
+ [subscriptions](subscriptions.md).
108
+ - **A session first read after the response started** — by a field under
109
+ `@defer`, when no field of the initial payload read it — is not sent: the
110
+ headers had already gone.
111
+ - **An `auth` of your own** — a wrapper that counts or caches — must pass on
112
+ `cookie: auth.cookie` beside `authenticate` and `types`; without it, no
113
+ renewed cookie is sent.
86
114
 
87
115
  `JanusContext<typeof auth, typeof access, Type>` takes the third argument
88
116
  when `useJanus({ type })` narrows the server:
@@ -64,7 +64,7 @@ would not.
64
64
  | `requireUser(ctx)`, `can(ctx, …)` | `SERVICE_UNAVAILABLE`, 503 — with or without `janusMaskError()` |
65
65
  | `ctx.janus.user()` read in a resolver, any `auth.*` or `access.*` call | `SERVICE_UNAVAILABLE`, 503 with `janusMaskError()`; Yoga's masked 500 without it |
66
66
 
67
- ## `janusMaskError(fallback?)`
67
+ ## `janusMaskError({ report?, fallback? })`
68
68
 
69
69
  Yoga masks every error that is not a `GraphQLError` into `Unexpected error.`
70
70
  with a 500. That is right for a bug and wrong for a `JanusError`: a wrong
@@ -77,7 +77,7 @@ import { createYoga, maskError } from 'graphql-yoga';
77
77
  createYoga({
78
78
  schema,
79
79
  plugins: [useJanus({ auth })],
80
- maskedErrors: { maskError: janusMaskError(maskError) },
80
+ maskedErrors: { maskError: janusMaskError({ fallback: maskError }) },
81
81
  });
82
82
  ```
83
83
 
@@ -88,8 +88,51 @@ else goes to `fallback`.
88
88
  Without a `fallback`, a `GraphQLError` of your own is kept as it is, and
89
89
  anything else becomes the mask's message with the code
90
90
  `INTERNAL_SERVER_ERROR` and no detail. Pass Yoga's own `maskError`, as above,
91
- to keep what it shows in development. The signature is envelop's too, so the
92
- same function fits `useMaskedErrors({ maskError })`.
91
+ to keep what it shows in development — as `{ fallback }`, or alone:
92
+ `janusMaskError(maskError)`, the form from before `report`, still works. The
93
+ signature is envelop's too, so the same function fits
94
+ `useMaskedErrors({ maskError })`.
95
+
96
+ ## Every outage in your logs: `report`
97
+
98
+ The client reads a fixed message, and nothing of the store's: which store
99
+ failed, and why, is for your logs. A directive — `@authenticated`, `@fresh`,
100
+ `@permission` — and `requireUser()`, `requireFresh()` and `can()` answer an
101
+ outage 503 themselves, before any resolver of yours runs, so there is no
102
+ `try` of yours to log it in. `report` is where it goes:
103
+
104
+ ```ts
105
+ createYoga({
106
+ schema,
107
+ plugins: [useJanus({ auth, access })],
108
+ maskedErrors: {
109
+ maskError: janusMaskError({
110
+ report: (error) => logger.error({ code: error.code, cause: error.cause }, error.message),
111
+ fallback: maskError,
112
+ }),
113
+ },
114
+ });
115
+ ```
116
+
117
+ - **Every 5xx, whichever path it took**: `STORE_FAILED`, `UNSUPPORTED`,
118
+ `PERMISSION_DEPTH` — from a directive, a helper, a loader, or a resolver
119
+ that let it through. `report` is given `@nxgt/janus`'s own error: a
120
+ `StoreFailure` carries the `slot` and `operation` of the call that failed,
121
+ its message and its `cause`.
122
+ - **Once per failure.** The user is authenticated once per request, so an
123
+ outage that fails three guarded fields is one error, reported once — and
124
+ answered three times.
125
+ - **Never a 4xx**: a denial, `CREDENTIALS_INVALID`, `STEP_UP_REQUIRED` are the
126
+ client's to fix, not yours.
127
+ - **It cannot change the answer.** A `report` that throws, or rejects, is a
128
+ `process.emitWarning` naming the code, and the 503 is sent all the same.
129
+ What it answers is ignored.
130
+
131
+ Yoga's own logger does not cover this: it logs an error only when the mask
132
+ replaced it, and a directive's refusal reaches the mask already a
133
+ `GraphQLError`, kept as it is — so it is never logged. The same `report` is
134
+ `janusErrors({ report })` in `@nxgt/janus-hono`, for the HTTP routes beside
135
+ the GraphQL server.
93
136
 
94
137
  `janusGraphQLError(error)` is the conversion alone, for a resolver that
95
138
  catches a `JanusError` to answer it itself:
@@ -106,6 +149,27 @@ try {
106
149
  }
107
150
  ```
108
151
 
152
+ **Rate-limit a sign-in mutation per login and per client: `@nxgt/janus`
153
+ counts no failed password**, so without a limit anyone can guess as fast as
154
+ the server hashes. A sign-in code, a second factor and a step-up count their
155
+ guesses; a password does not. Check the limit in the resolver, before
156
+ `signIn`, with the limiter you already run:
157
+
158
+ ```ts
159
+ import { GraphQLError } from 'graphql';
160
+
161
+ if (!(await limiter.consume(`sign-in:${input.email.trim().toLowerCase()}`))) {
162
+ throw new GraphQLError('Too many attempts', {
163
+ extensions: { code: 'TOO_MANY_REQUESTS', http: { status: 429 } },
164
+ });
165
+ }
166
+ return await auth.patient.signIn(input);
167
+ ```
168
+
169
+ Yoga answers the `http.status` of the extensions. Add a second limit per
170
+ client address, and limit a `changePassword` mutation per user: it compares
171
+ the current password too.
172
+
109
173
  ## The status of a response
110
174
 
111
175
  Yoga answers **the highest status among the errors**. A query whose fields
@@ -248,4 +248,8 @@ cookie is never read.
248
248
  operation on a connection no `onConnect` accepted has no credential: its
249
249
  first guarded field, or `ctx.janus.user()`, throws a `TypeError`, a 500.
250
250
  - **The renewed session cookie.** A WebSocket has no response to set one
251
- on: renew through your HTTP routes.
251
+ on. An operation that renews the session renews it in the store, and the
252
+ browser's cookie keeps its old expiry until the next renewal over HTTP,
253
+ which `useJanus()` sends — a `renewAfter` later. A browser whose only
254
+ traffic is the WebSocket should renew through an HTTP route of yours:
255
+ [the context guide](context.md#ctxjanus) has the detail.
package/docs/roadmap.md CHANGED
@@ -9,10 +9,7 @@ Nothing between releases.
9
9
 
10
10
  ## Next
11
11
 
12
- - **The renewed session cookie on GraphQL responses** — `authenticate`
13
- renews a sliding session in passing; the response would then carry its
14
- `Set-Cookie`, so a browser keeps the new expiry without an HTTP route of
15
- its own.
12
+ Nothing yet.
16
13
 
17
14
  ## Later
18
15
 
@@ -43,6 +40,16 @@ Nothing between releases.
43
40
 
44
41
  Newest first; from the first release on, the package's CHANGELOG holds every one.
45
42
 
43
+ - **The renewed session cookie, and `report` for every outage, v0.4.0.**
44
+ Under Yoga, `useJanus()` sends a session `authenticate` renewed back as
45
+ `Set-Cookie`, so a browser keeps the new expiry without an HTTP route of
46
+ its own — only to a request that presented the session cookie, never to a
47
+ bearer token or `X-Session-Token`, and never when nothing authenticated.
48
+ `janusMaskError({ report, fallback })` hands every `JanusError` answered
49
+ 5xx to your logger, once, including the outages a directive or a helper
50
+ answers 503 on its own; a `report` that throws never changes the answer.
51
+ `janusMaskError(fallback)` still works.
52
+
46
53
  - **Subscriptions over graphql-ws, `janusConnection()`, v0.3.0.** The same
47
54
  `@authenticated`, `@fresh` and `@permission` on subscriptions served over
48
55
  a WebSocket with graphql-ws: `onConnect` for its `useServer()`
@@ -59,6 +59,8 @@ for what causes each.
59
59
  - [`Unexpected error.`, 500, on a field `@permission` guards](#unexpected-error-500-on-a-field-permission-guards)
60
60
  - [`TypeError: @permission on …: ctx.janus.access is not set`](#typeerror-permission-on--ctxjanusaccess-is-not-set)
61
61
  - [`TypeError: @permission on …: loaders.… answered …`](#typeerror-permission-on--loaders-answered-)
62
+ - [The session cookie expires while the user is active](#the-session-cookie-expires-while-the-user-is-active)
63
+ - [`Warning: janusMaskError: report failed on …`](#warning-janusmaskerror-report-failed-on-)
62
64
  - [A 401 whose `data` still holds the other fields](#a-401-whose-data-still-holds-the-other-fields)
63
65
 
64
66
  **Types**
@@ -410,8 +412,16 @@ for `can()` and `@permission` — and a loader of yours that threw
410
412
  `StoreFailure`. It is never answered `UNAUTHENTICATED`, `FORBIDDEN` or `STEP_UP_REQUIRED`.
411
413
 
412
414
  **Fix:** the store: its connection, its credentials, its availability. The
413
- message carries nothing of the store's; log the error where you call
414
- `auth` — `@nxgt/janus`'s `slot` and `operation` name the call that failed.
415
+ message carries nothing of the store's, and a directive or a helper answers
416
+ the outage before any resolver of yours could catch it: pass `report` to
417
+ the mask, which is given `@nxgt/janus`'s error — its `slot` and `operation`
418
+ name the call that failed, its `cause` the driver's error:
419
+
420
+ ```ts
421
+ maskedErrors: {
422
+ maskError: janusMaskError({ report: (error) => logger.error(error), fallback: maskError }),
423
+ },
424
+ ```
415
425
 
416
426
  ### `Unexpected error.`, 500, where a `JanusError` was thrown
417
427
 
@@ -580,6 +590,34 @@ from the same options.
580
590
 
581
591
  **Fix:** answer the object, or `null` when there is none.
582
592
 
593
+ ### The session cookie expires while the user is active
594
+
595
+ **Why:** `authenticate` renewed the session in the store, and no
596
+ `Set-Cookie` carried the new expiry back, so the browser drops the cookie at
597
+ the expiry it was given at sign-in. `useJanus()` sends it only on Yoga's
598
+ HTTP response (`onResponse`), and not when:
599
+
600
+ - the server runs on envelop without Yoga, which never calls `onResponse`;
601
+ - the `auth` given to `useJanus()` is a wrapper of yours without `cookie`;
602
+ - the only traffic is over graphql-ws, which has no response to carry it;
603
+ - the client sent `Authorization: Bearer` or `X-Session-Token`, which is
604
+ never answered with a cookie.
605
+
606
+ **Fix:** under Yoga, pass `janus()`'s own instance, or a wrapper with
607
+ `cookie: auth.cookie`. Elsewhere — and for a browser that talks only over
608
+ the WebSocket — renew through an HTTP route of yours that sends
609
+ `auth.cookie.serialize(token, session)`, as `@nxgt/janus-hono`'s
610
+ `session()` does.
611
+
612
+ ### `Warning: janusMaskError: report failed on …`
613
+
614
+ **Why:** the `report` given to `janusMaskError()` threw, or rejected — a
615
+ logger that is down, a serializer that meets a circular `cause`. The name
616
+ after the code is the class of what it threw.
617
+
618
+ **Fix:** your `report`. The response was sent all the same: a `report`
619
+ never changes the answer, so an outage is still a 503 and never a 500.
620
+
583
621
  ### A 401 whose `data` still holds the other fields
584
622
 
585
623
  **Why:** Yoga answers the highest status among the errors. One refused field
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxgt/janus-graphql",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "GraphQL for @nxgt/janus: an envelop plugin with the signed-in user on the context, fields guarded by @authenticated, @fresh and @permission, and every error with the status it deserves",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -55,7 +55,7 @@
55
55
  "devDependencies": {
56
56
  "@envelop/core": "^5.6.1",
57
57
  "@graphql-tools/utils": "^12.0.1",
58
- "@nxgt/janus": "^0.12.0",
58
+ "@nxgt/janus": "^0.13.0",
59
59
  "@types/bun": "^1.4.2",
60
60
  "@types/ws": "^8.18.1",
61
61
  "graphql": "^17.0.2",
@@ -67,7 +67,7 @@
67
67
  "peerDependencies": {
68
68
  "@envelop/core": "^5.0.0",
69
69
  "@graphql-tools/utils": ">=10.0.0 <13",
70
- "@nxgt/janus": "^0.12.0",
70
+ "@nxgt/janus": "^0.13.0",
71
71
  "graphql": "^16.9.0 || ^17.0.0",
72
72
  "graphql-ws": "^6.0.0",
73
73
  "typescript": "^6.0.3"