@stonyx/orm 0.3.2-alpha.106 → 0.3.2-alpha.108

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
@@ -381,8 +381,33 @@ to keep in sync — abofs/stonyx-orm#270. It is `undefined` on collection routes
381
381
  which is how you tell a collection request from a record request. `params.id` is
382
382
  left untouched, so anything that needs the raw client text still has it.
383
383
 
384
- The same function is exported for the places `access()` does not reach hooks,
385
- custom handlers, your own lookups:
384
+ **Version floor: `request.recordId` requires the release that carries
385
+ abofs/stonyx-orm#270.** Measured on the published tarballs: `0.3.2-alpha.106`
386
+ (this change's alpha) has `request.recordId` and exports `normalizeRecordId`;
387
+ `0.3.2-beta.231`, the newest build on the `beta` tag at the time of writing, has
388
+ neither, and `latest` is `0.3.1`. **On any earlier build the samples above fail
389
+ open, completely** — measured against `origin/dev`: `recordId` is `undefined` on
390
+ every route, so `if (recordId === undefined) return record => …` fires on the
391
+ *record* route, and a function return authorizes the request outright.
392
+ `GET /owners/angela` and `GET /animals/7` both returned 200 and served the
393
+ protected record, and `DELETE` on both returned 204 and destroyed it.
394
+
395
+ If you cannot pin the version, make the samples fail *closed* instead — one line,
396
+ a no-op on a new build and a refusal on an old one:
397
+
398
+ ```javascript
399
+ access(request) {
400
+ // Old builds do not attach recordId. Refuse rather than fall through to the
401
+ // collection branch, which would authorize the record route outright.
402
+ if (!('recordId' in request)) return false;
403
+
404
+ const { recordId } = request;
405
+ // …
406
+ }
407
+ ```
408
+
409
+ The same normalisation is exported for the places `access()` does not reach —
410
+ hooks, custom handlers, your own lookups:
386
411
 
387
412
  ```javascript
388
413
  import { normalizeRecordId } from '@stonyx/orm';
@@ -393,6 +418,15 @@ normalizeRecordId('0x7'); // 7 — the same value the ORM resolves by
393
418
  Do not re-implement it. A hand-written copy is correct only for as long as it
394
419
  happens to match, and nothing holds the two together.
395
420
 
421
+ **`normalizeRecordId(undefined)` is `''`, not `undefined`.** The two values this
422
+ section documents side by side do not agree, and the difference is load-bearing:
423
+ `request.recordId` is `undefined` on a collection route, while
424
+ `normalizeRecordId` returns `''` for any falsy id — including `undefined` — because
425
+ `store.get(key, undefined)` returns the whole model Map rather than a record
426
+ (abofs/stonyx-orm#167). So `normalizeRecordId(context.params.id) === undefined`
427
+ is **never** true and a collection branch written that way never runs. Branch on
428
+ `request.recordId === undefined`, or compare against `''`.
429
+
396
430
  ```javascript
397
431
  export default class AnimalAccess {
398
432
  models = ['animal'];
@@ -633,9 +667,13 @@ beforeHook('create', 'animal', (context) => {
633
667
  }
634
668
  });
635
669
 
636
- // Return an object to send a custom response
670
+ // Return an object to send a custom response.
671
+ // Look the record up by `context.request.recordId` — the value the ORM
672
+ // resolved the record by. `context.params.id` is the raw client text and is a
673
+ // different value for every alias of the same id, so a lookup keyed on it
674
+ // finds nothing on a numeric-id model (abofs/stonyx-orm#270).
637
675
  beforeHook('delete', 'animal', (context) => {
638
- const animal = store.get('animal', context.params.id);
676
+ const animal = store.get('animal', context.request.recordId);
639
677
  if (animal.protected) {
640
678
  return { errors: [{ detail: 'Cannot delete protected animals' }] };
641
679
  }
@@ -676,9 +714,12 @@ afterHook('update', 'animal', async (context) => {
676
714
  }
677
715
  });
678
716
 
679
- // Cache invalidation
717
+ // Cache invalidation.
718
+ // Keyed on `recordId`, not `params.id`: `/animals/7` and `/animals/007` are
719
+ // one record but two strings, so a raw-text key invalidates two entries and
720
+ // misses the one the write used.
680
721
  afterHook('delete', 'animal', async (context) => {
681
- await cache.invalidate(`owner:${context.params.id}:pets`);
722
+ await cache.invalidate(`owner:${context.request.recordId}:pets`);
682
723
  });
683
724
  ```
684
725
 
@@ -719,10 +760,16 @@ afterHook('delete', 'animal', async (context) => {
719
760
  #### Authorization
720
761
 
721
762
  ```javascript
722
- // Additional access control - halt with 403 if unauthorized
763
+ // Additional access control - halt with 403 if unauthorized.
764
+ //
765
+ // `context.request.recordId` is the id the ORM resolved the record by — the
766
+ // same value `access()` is handed. Authorizing on `context.params.id` instead
767
+ // looks up the raw client text: on a numeric-id model that lookup returns
768
+ // `undefined` for EVERY spelling, `animal.owner` throws, and the check never
769
+ // runs. Measured; abofs/stonyx-orm#270.
723
770
  beforeHook('delete', 'animal', (context) => {
724
771
  const user = context.state.currentUser;
725
- const animal = store.get('animal', context.params.id);
772
+ const animal = store.get('animal', context.request.recordId);
726
773
 
727
774
  if (animal.owner !== user.id && !user.isAdmin) {
728
775
  return 403; // Forbidden
@@ -923,6 +970,48 @@ test('validation hook rejects negative age', async () => {
923
970
  | `afterHook` | Register an after hook for post-operation logic. |
924
971
  | `clearHook` | Clear hooks for a specific operation:model. |
925
972
  | `clearAllHooks` | Clear all registered hooks (useful for testing). |
973
+ | `normalizeRecordId` | Turn a raw URL id into the value the ORM resolves the record by. |
974
+
975
+ ### `normalizeRecordId(id)`
976
+
977
+ ```ts
978
+ normalizeRecordId(id?: string | null): string | number
979
+ ```
980
+
981
+ The **one** implementation of URL-id normalisation in the package
982
+ (abofs/stonyx-orm#270). `access()` is already handed its result as
983
+ `request.recordId`; import it for the places `access()` does not reach — hooks,
984
+ custom handlers, your own lookups. Synchronous, and it must stay synchronous:
985
+ `auth()` is invoked without `await`.
986
+
987
+ | Input | Returns | Note |
988
+ | --- | --- | --- |
989
+ | `'7'`, `'007'`, `'7.0'`, `'7.9'`, `'7e0'`, `'0x7'`, `'+7'`, `' 7'` | `7` (number) | `parseInt` with **no radix**; passing a radix of 10 would make this `0` |
990
+ | `'angela'`, `'ANGELA'` | the same string, case included | a non-numeric id is passed through untouched |
991
+ | `'0'`, `'00'`, `'-0'`, `'0x0'` | `0` (number) | falsy, and a legitimate record id |
992
+ | `' '`, `'\t'`, `'\n'`, `'\u00a0'` | `NaN` | whitespace-only ids are numeric to `isNaN` but parse to nothing |
993
+ | `''`, `null`, `undefined` | `''` (empty string) | **not** `undefined` — see the trap below |
994
+
995
+ **The trap.** `request.recordId` is `undefined` on a collection route;
996
+ `normalizeRecordId(undefined)` is `''`. They are different values and a
997
+ collection check written against the wrong one silently never fires:
998
+
999
+ ```javascript
1000
+ // WRONG — never true, so this branch never runs
1001
+ if (normalizeRecordId(context.params.id) === undefined) { /* … */ }
1002
+
1003
+ // Right — the ORM attaches undefined for "this route carries no :id"
1004
+ if (context.request.recordId === undefined) { /* collection route */ }
1005
+ ```
1006
+
1007
+ The `''` is deliberate: `store.get(key, undefined)` returns the whole model Map
1008
+ rather than a record (abofs/stonyx-orm#167), so the resolution path depends on
1009
+ the empty string this function returns today.
1010
+
1011
+ **A normalised id is not a promise that a record exists.** `0` and `NaN` are both
1012
+ possible returns and neither addresses a record you can rely on — `NaN` is not
1013
+ even `===` itself, so `recordId === NaN` can never be written as a guard. Treat
1014
+ `recordId` as "the key the lookup will use", not as "a record is there".
926
1015
 
927
1016
  ## Project Structure
928
1017
 
package/dist/index.d.ts CHANGED
@@ -13,4 +13,5 @@ export { Model, View, Serializer };
13
13
  export { attr, belongsTo, hasMany, createRecord, updateRecord };
14
14
  export { count, avg, sum, min, max };
15
15
  export { default as normalizeRecordId } from './normalize-record-id.js';
16
+ export type { OrmRequest$ as OrmAccessRequest } from './orm-request.js';
16
17
  export { beforeHook, afterHook, clearHook, clearAllHooks } from './hooks.js';
@@ -2,9 +2,12 @@
2
2
  * The ONE place a URL id is turned into the value a record is resolved by —
3
3
  * abofs/stonyx-orm#270.
4
4
  *
5
- * Before this existed the same expression was written out seven times: twice in
5
+ * Before this existed this coercion was written out seven times: twice in
6
6
  * README.md, once in docs/usage-patterns.md, and four times inside the
7
- * framework. The framework's copy`getId()`, module-private in
7
+ * framework. Seven copies, not seven identical copies the three persistence
8
+ * ones (abofs/stonyx-orm#282) omit the `if (!id) return ''` guard below, which
9
+ * is the point: nothing held them together, so they had already drifted.
10
+ * The framework's copy — `getId()`, module-private in
8
11
  * orm-request.ts, unreachable through the package `exports` map — was the one
9
12
  * that decided which record a request addressed, and a consumer's `access()`
10
13
  * predicate had no way to obtain it. So the framework resolved the record by
@@ -47,6 +50,17 @@
47
50
  * @stonyx/rest-server, so a promise here would be handed to `access()` as the
48
51
  * record id.
49
52
  *
53
+ * FALSY AND `NaN` RETURNS ARE LOAD-BEARING ELSEWHERE — abofs/stonyx-orm#287.
54
+ * `''` is returned for any falsy id because `store.get(key, undefined)` returns
55
+ * the whole model Map rather than a record (abofs/stonyx-orm#167). But `''` is
56
+ * not the only falsy return: `'0'`, `'00'`, `'-0'` and `'0x0'` all normalise to
57
+ * `0`, and `' '`, `'\t'`, `'\n'`, `'\u00a0'` all normalise to `NaN` (their
58
+ * `Number()` is `0`, so the `isNaN` guard does not fire and `parseInt` runs).
59
+ * `store.remove(key, id)` branches on truthiness, so those spellings reach a
60
+ * fall-through this function does not own. Tracked as #287; every row is pinned
61
+ * in test/unit/normalize-record-id-test.ts so a cleanup here cannot move the
62
+ * boundary #287 is measured against.
63
+ *
50
64
  * @param id the raw, already-URL-decoded id text from `request.params.id`
51
65
  * @returns the value the ORM resolves the record by
52
66
  */
@@ -17,9 +17,12 @@
17
17
  * The ONE place a URL id is turned into the value a record is resolved by —
18
18
  * abofs/stonyx-orm#270.
19
19
  *
20
- * Before this existed the same expression was written out seven times: twice in
20
+ * Before this existed this coercion was written out seven times: twice in
21
21
  * README.md, once in docs/usage-patterns.md, and four times inside the
22
- * framework. The framework's copy`getId()`, module-private in
22
+ * framework. Seven copies, not seven identical copies the three persistence
23
+ * ones (abofs/stonyx-orm#282) omit the `if (!id) return ''` guard below, which
24
+ * is the point: nothing held them together, so they had already drifted.
25
+ * The framework's copy — `getId()`, module-private in
23
26
  * orm-request.ts, unreachable through the package `exports` map — was the one
24
27
  * that decided which record a request addressed, and a consumer's `access()`
25
28
  * predicate had no way to obtain it. So the framework resolved the record by
@@ -62,6 +65,17 @@
62
65
  * @stonyx/rest-server, so a promise here would be handed to `access()` as the
63
66
  * record id.
64
67
  *
68
+ * FALSY AND `NaN` RETURNS ARE LOAD-BEARING ELSEWHERE — abofs/stonyx-orm#287.
69
+ * `''` is returned for any falsy id because `store.get(key, undefined)` returns
70
+ * the whole model Map rather than a record (abofs/stonyx-orm#167). But `''` is
71
+ * not the only falsy return: `'0'`, `'00'`, `'-0'` and `'0x0'` all normalise to
72
+ * `0`, and `' '`, `'\t'`, `'\n'`, `'\u00a0'` all normalise to `NaN` (their
73
+ * `Number()` is `0`, so the `isNaN` guard does not fire and `parseInt` runs).
74
+ * `store.remove(key, id)` branches on truthiness, so those spellings reach a
75
+ * fall-through this function does not own. Tracked as #287; every row is pinned
76
+ * in test/unit/normalize-record-id-test.ts so a cleanup here cannot move the
77
+ * boundary #287 is measured against.
78
+ *
65
79
  * @param id the raw, already-URL-decoded id text from `request.params.id`
66
80
  * @returns the value the ORM resolves the record by
67
81
  */
@@ -1,12 +1,26 @@
1
1
  import { Request } from '@stonyx/rest-server';
2
- interface OrmRequest$ extends Request {
2
+ /**
3
+ * The request object a consumer's `access()` predicate receives.
4
+ *
5
+ * Exported because `recordId` is public API — README's `access()` samples
6
+ * destructure it — and a public runtime field with an unreachable type asks the
7
+ * consumer to re-declare something the framework already knows, which is
8
+ * abofs/stonyx-orm#270's own defect shape one layer over into the type surface.
9
+ * `HookContext` (src/hooks.ts) is this repo's precedent for exporting the
10
+ * interface a consumer is handed. Re-exported from the root barrel as
11
+ * `OrmAccessRequest` (src/index.ts) — NOT as `OrmRequest`, which is already the
12
+ * default-exported CLASS in this file and means something else. The
13
+ * `./orm-request` subpath is not in the `exports` map, so the barrel is the
14
+ * only reachable spelling.
15
+ */
16
+ export interface OrmRequest$ extends Request {
3
17
  protocol?: string;
4
18
  baseUrl?: string;
5
19
  method: string;
6
20
  params: {
7
21
  [key: string]: string;
8
22
  };
9
- recordId?: string | number;
23
+ recordId?: string | number | undefined;
10
24
  body?: {
11
25
  [key: string]: unknown;
12
26
  };
@@ -66,11 +66,21 @@ function getBaseUrl(request, pluralizedModel) {
66
66
  const prefix = mountPath.endsWith(modelSegment) ? mountPath.slice(0, -modelSegment.length) : '';
67
67
  return `${protocol}://${host}${prefix}`;
68
68
  }
69
- // Kept as a name because twelve call sites read `getId(request.params)`, and
70
- // docs/hooks.md documents that spelling. It is now a thin delegate: there is
71
- // exactly ONE normalisation in the repo, and it is the exported one a consumer
72
- // can import. A second implementation here is the defect abofs/stonyx-orm#270
73
- // exists to remove see src/normalize-record-id.ts.
69
+ // Kept as a name because twelve `getId(...)` call sites read it. It is now a
70
+ // thin delegate: there is exactly ONE normalisation of a URL id in the repo,
71
+ // and it is the exported one a consumer can import.
72
+ //
73
+ // "of a URL id" is the load-bearing qualifier, and it is the same one
74
+ // src/normalize-record-id.ts:18 carries. Three copies of the coercion survive
75
+ // at this head — src/orm-request.ts (the create-response path),
76
+ // src/postgres/postgres-db.ts and src/mysql/mysql-db.ts — but each normalises a
77
+ // RESPONSE id (`response?.data?.id`), not a URL id, and each omits this
78
+ // function's `if (!id) return ''` guard. They are tracked as
79
+ // abofs/stonyx-orm#282 and enumerated by name in AC-5's allowlist
80
+ // (test/integration/readme-sample-test.ts).
81
+ //
82
+ // A second implementation of the URL-id normalisation here is the defect
83
+ // abofs/stonyx-orm#270 exists to remove — see src/normalize-record-id.ts.
74
84
  function getId(params) {
75
85
  return normalizeRecordId(params.id);
76
86
  }
@@ -493,10 +503,10 @@ export default class OrmRequest extends Request {
493
503
  // handlers; this line is the contract.
494
504
  //
495
505
  // `request.params` is deliberately NOT mutated. Twelve `getId(...)` call
496
- // sites, the serializer, and consumer hooks (docs/hooks.md documents
497
- // `getId(request.params)`) all read it, and changing `params.id` from
498
- // string to number underneath them is a silent behaviour change on paths
499
- // this issue is not about.
506
+ // sites read it, and `_withHooks` assigns `params: request.params` onto the
507
+ // hook context, so every consumer hook reads the same object. Changing
508
+ // `params.id` from string to number underneath them is a silent behaviour
509
+ // change on paths this issue is not about.
500
510
  //
501
511
  // Synchronous by necessity: @stonyx/rest-server calls auth() without
502
512
  // awaiting it.
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "stonyx-async",
5
5
  "stonyx-module"
6
6
  ],
7
- "version": "0.3.2-alpha.106",
7
+ "version": "0.3.2-alpha.108",
8
8
  "description": "",
9
9
  "main": "dist/index.js",
10
10
  "type": "module",
package/src/index.ts CHANGED
@@ -31,6 +31,7 @@ export { Model, View, Serializer }; // base classes
31
31
  export { attr, belongsTo, hasMany, createRecord, updateRecord }; // helpers
32
32
  export { count, avg, sum, min, max }; // aggregate helpers
33
33
  export { default as normalizeRecordId } from './normalize-record-id.js'; // canonical URL-id -> record-id resolution (#270)
34
+ export type { OrmRequest$ as OrmAccessRequest } from './orm-request.js'; // the request access() is handed, incl. recordId (#270)
34
35
  export { beforeHook, afterHook, clearHook, clearAllHooks } from './hooks.js'; // middleware hooks
35
36
 
36
37
  // Store API:
@@ -18,9 +18,12 @@
18
18
  * The ONE place a URL id is turned into the value a record is resolved by —
19
19
  * abofs/stonyx-orm#270.
20
20
  *
21
- * Before this existed the same expression was written out seven times: twice in
21
+ * Before this existed this coercion was written out seven times: twice in
22
22
  * README.md, once in docs/usage-patterns.md, and four times inside the
23
- * framework. The framework's copy`getId()`, module-private in
23
+ * framework. Seven copies, not seven identical copies the three persistence
24
+ * ones (abofs/stonyx-orm#282) omit the `if (!id) return ''` guard below, which
25
+ * is the point: nothing held them together, so they had already drifted.
26
+ * The framework's copy — `getId()`, module-private in
24
27
  * orm-request.ts, unreachable through the package `exports` map — was the one
25
28
  * that decided which record a request addressed, and a consumer's `access()`
26
29
  * predicate had no way to obtain it. So the framework resolved the record by
@@ -63,6 +66,17 @@
63
66
  * @stonyx/rest-server, so a promise here would be handed to `access()` as the
64
67
  * record id.
65
68
  *
69
+ * FALSY AND `NaN` RETURNS ARE LOAD-BEARING ELSEWHERE — abofs/stonyx-orm#287.
70
+ * `''` is returned for any falsy id because `store.get(key, undefined)` returns
71
+ * the whole model Map rather than a record (abofs/stonyx-orm#167). But `''` is
72
+ * not the only falsy return: `'0'`, `'00'`, `'-0'` and `'0x0'` all normalise to
73
+ * `0`, and `' '`, `'\t'`, `'\n'`, `'\u00a0'` all normalise to `NaN` (their
74
+ * `Number()` is `0`, so the `isNaN` guard does not fire and `parseInt` runs).
75
+ * `store.remove(key, id)` branches on truthiness, so those spellings reach a
76
+ * fall-through this function does not own. Tracked as #287; every row is pinned
77
+ * in test/unit/normalize-record-id-test.ts so a cleanup here cannot move the
78
+ * boundary #287 is measured against.
79
+ *
66
80
  * @param id the raw, already-URL-decoded id text from `request.params.id`
67
81
  * @returns the value the ORM resolves the record by
68
82
  */
@@ -9,7 +9,21 @@ import type { OrmRecord } from './types/orm-types.js';
9
9
  import { isOrmRecord } from './utils.js';
10
10
  import normalizeRecordId from './normalize-record-id.js';
11
11
 
12
- interface OrmRequest$ extends Request {
12
+ /**
13
+ * The request object a consumer's `access()` predicate receives.
14
+ *
15
+ * Exported because `recordId` is public API — README's `access()` samples
16
+ * destructure it — and a public runtime field with an unreachable type asks the
17
+ * consumer to re-declare something the framework already knows, which is
18
+ * abofs/stonyx-orm#270's own defect shape one layer over into the type surface.
19
+ * `HookContext` (src/hooks.ts) is this repo's precedent for exporting the
20
+ * interface a consumer is handed. Re-exported from the root barrel as
21
+ * `OrmAccessRequest` (src/index.ts) — NOT as `OrmRequest`, which is already the
22
+ * default-exported CLASS in this file and means something else. The
23
+ * `./orm-request` subpath is not in the `exports` map, so the barrel is the
24
+ * only reachable spelling.
25
+ */
26
+ export interface OrmRequest$ extends Request {
13
27
  protocol?: string;
14
28
  // Express sets this to the path the router was mounted at, e.g. '/api/animals'
15
29
  // when orm.restServer.route is '/api'. Optional because non-Express callers
@@ -20,7 +34,11 @@ interface OrmRequest$ extends Request {
20
34
  // Attached by auth() before access() runs — abofs/stonyx-orm#270. This is the
21
35
  // value the ORM resolves the record by; `params.id` remains the raw client
22
36
  // text it was parsed from.
23
- recordId?: string | number;
37
+ //
38
+ // `undefined` is part of the contract, not an absence: it is how a collection
39
+ // route is told from a record route, and both documented samples branch on
40
+ // it. Spelled out rather than left to `?:` for that reason.
41
+ recordId?: string | number | undefined;
24
42
  body?: { [key: string]: unknown };
25
43
  query?: { [key: string]: string };
26
44
  get(header: string): string;
@@ -112,11 +130,21 @@ function getBaseUrl(request: OrmRequest$, pluralizedModel: string): string {
112
130
  return `${protocol}://${host}${prefix}`;
113
131
  }
114
132
 
115
- // Kept as a name because twelve call sites read `getId(request.params)`, and
116
- // docs/hooks.md documents that spelling. It is now a thin delegate: there is
117
- // exactly ONE normalisation in the repo, and it is the exported one a consumer
118
- // can import. A second implementation here is the defect abofs/stonyx-orm#270
119
- // exists to remove see src/normalize-record-id.ts.
133
+ // Kept as a name because twelve `getId(...)` call sites read it. It is now a
134
+ // thin delegate: there is exactly ONE normalisation of a URL id in the repo,
135
+ // and it is the exported one a consumer can import.
136
+ //
137
+ // "of a URL id" is the load-bearing qualifier, and it is the same one
138
+ // src/normalize-record-id.ts:18 carries. Three copies of the coercion survive
139
+ // at this head — src/orm-request.ts (the create-response path),
140
+ // src/postgres/postgres-db.ts and src/mysql/mysql-db.ts — but each normalises a
141
+ // RESPONSE id (`response?.data?.id`), not a URL id, and each omits this
142
+ // function's `if (!id) return ''` guard. They are tracked as
143
+ // abofs/stonyx-orm#282 and enumerated by name in AC-5's allowlist
144
+ // (test/integration/readme-sample-test.ts).
145
+ //
146
+ // A second implementation of the URL-id normalisation here is the defect
147
+ // abofs/stonyx-orm#270 exists to remove — see src/normalize-record-id.ts.
120
148
  function getId(params: { id?: string; [key: string]: unknown }): string | number {
121
149
  return normalizeRecordId(params.id);
122
150
  }
@@ -615,10 +643,10 @@ export default class OrmRequest extends Request {
615
643
  // handlers; this line is the contract.
616
644
  //
617
645
  // `request.params` is deliberately NOT mutated. Twelve `getId(...)` call
618
- // sites, the serializer, and consumer hooks (docs/hooks.md documents
619
- // `getId(request.params)`) all read it, and changing `params.id` from
620
- // string to number underneath them is a silent behaviour change on paths
621
- // this issue is not about.
646
+ // sites read it, and `_withHooks` assigns `params: request.params` onto the
647
+ // hook context, so every consumer hook reads the same object. Changing
648
+ // `params.id` from string to number underneath them is a silent behaviour
649
+ // change on paths this issue is not about.
622
650
  //
623
651
  // Synchronous by necessity: @stonyx/rest-server calls auth() without
624
652
  // awaiting it.