@stonyx/orm 0.3.2-alpha.57 → 0.3.2-alpha.58
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 +1 -1
- package/dist/main.d.ts +26 -5
- package/dist/main.js +28 -7
- package/dist/setup-rest-server.js +6 -6
- package/package.json +1 -1
- package/src/main.ts +28 -7
- package/src/setup-rest-server.ts +6 -6
package/README.md
CHANGED
|
@@ -463,7 +463,7 @@ const verdict = predicate?.(request, { model: 'animal', operation: 'read' });
|
|
|
463
463
|
```
|
|
464
464
|
|
|
465
465
|
`Orm.instance.getAccess(modelName)` returns the predicate, or `undefined` when
|
|
466
|
-
that model has no access class. The raw map is `Orm.instance.
|
|
466
|
+
that model has no access class. The raw map is `Orm.instance.accessFunctions`, keyed
|
|
467
467
|
by model name; prefer `getAccess()`.
|
|
468
468
|
|
|
469
469
|
Passing the context explicitly is what makes the answer **model-correct**. A
|
package/dist/main.d.ts
CHANGED
|
@@ -34,7 +34,25 @@ export default class Orm {
|
|
|
34
34
|
transforms: Record<string, (value: unknown) => unknown>;
|
|
35
35
|
warnings: Set<string>;
|
|
36
36
|
/**
|
|
37
|
-
* Model name ->
|
|
37
|
+
* Model name -> the `access` predicate of the access class that CLAIMS that
|
|
38
|
+
* model (abofs/stonyx-orm#202).
|
|
39
|
+
*
|
|
40
|
+
* Not "that model's own predicate". One access class may claim many models
|
|
41
|
+
* -- `GlobalAccess` in this repo's fixtures declares five, and `models = '*'`
|
|
42
|
+
* claims every model in the store -- and it declares ONE `access` method, so
|
|
43
|
+
* the same function object is registered under every one of those keys.
|
|
44
|
+
* `getAccess('owner') === getAccess('animal')` is `true` there. The
|
|
45
|
+
* one-to-one guarantee below is key -> function, never function -> model,
|
|
46
|
+
* and a caller must not read a resolved predicate as being animal-specific.
|
|
47
|
+
* What makes the ANSWER model-specific is the context the caller passes and
|
|
48
|
+
* the predicate actually reading it -- see {@link Orm#getAccess}.
|
|
49
|
+
*
|
|
50
|
+
* NAMED FOR WHAT IT HOLDS. It was `accessFiles` through review, inherited
|
|
51
|
+
* from the function-local in `setup-rest-server.ts` where the values came
|
|
52
|
+
* straight out of `forEachFileImport` and "files" was defensible. The values
|
|
53
|
+
* are `AccessFunction`s, and the sibling public registries on this class
|
|
54
|
+
* (`models`, `serializers`, `views`, `transforms`) are all plural nouns of
|
|
55
|
+
* the thing held. Renamed here because #202 is the last moment it is free.
|
|
38
56
|
*
|
|
39
57
|
* Populated by `setup-rest-server.ts` at boot, from the access classes under
|
|
40
58
|
* `config.orm.paths.access`, BEFORE any route is mounted -- so it is complete
|
|
@@ -53,10 +71,13 @@ export default class Orm {
|
|
|
53
71
|
* request routed to model Y -- inexpressible, which is the capability
|
|
54
72
|
* abofs/stonyx-orm#196 and abofs/stonyx-orm#207 are built on.
|
|
55
73
|
*
|
|
56
|
-
* Empty when the REST server is disabled,
|
|
57
|
-
*
|
|
74
|
+
* Empty when the REST server is disabled, and PARTIAL when one access file
|
|
75
|
+
* failed to load (`setup-rest-server.ts` catches, warns and assigns whatever
|
|
76
|
+
* it had). So a missing key does NOT mean the model has no access class.
|
|
77
|
+
* Prefer {@link Orm#getAccess} over indexing this directly -- it is guarded
|
|
78
|
+
* against the prototype chain and this is not.
|
|
58
79
|
*/
|
|
59
|
-
|
|
80
|
+
accessFunctions: Record<string, AccessFunction>;
|
|
60
81
|
options: OrmOptions;
|
|
61
82
|
sqlDb?: SqlDb;
|
|
62
83
|
db?: OrmDB | SqlDb;
|
|
@@ -81,7 +102,7 @@ export default class Orm {
|
|
|
81
102
|
* collection the request is ADDRESSED TO -- owners -- while being asked about
|
|
82
103
|
* animals, and per #202's thesis it answers wrong in the granting direction.
|
|
83
104
|
*
|
|
84
|
-
* OWN PROPERTIES ONLY. A bare `this.
|
|
105
|
+
* OWN PROPERTIES ONLY. A bare `this.accessFunctions[modelName]` walks the
|
|
85
106
|
* prototype chain, so `getAccess('constructor')` resolved `Object` and
|
|
86
107
|
* `getAccess('toString')` resolved `Object.prototype.toString` -- both
|
|
87
108
|
* callable, and the documented `predicate?.(request, ctx)` pattern then
|
package/dist/main.js
CHANGED
|
@@ -39,7 +39,25 @@ export default class Orm {
|
|
|
39
39
|
transforms = { ...baseTransforms };
|
|
40
40
|
warnings = new Set();
|
|
41
41
|
/**
|
|
42
|
-
* Model name ->
|
|
42
|
+
* Model name -> the `access` predicate of the access class that CLAIMS that
|
|
43
|
+
* model (abofs/stonyx-orm#202).
|
|
44
|
+
*
|
|
45
|
+
* Not "that model's own predicate". One access class may claim many models
|
|
46
|
+
* -- `GlobalAccess` in this repo's fixtures declares five, and `models = '*'`
|
|
47
|
+
* claims every model in the store -- and it declares ONE `access` method, so
|
|
48
|
+
* the same function object is registered under every one of those keys.
|
|
49
|
+
* `getAccess('owner') === getAccess('animal')` is `true` there. The
|
|
50
|
+
* one-to-one guarantee below is key -> function, never function -> model,
|
|
51
|
+
* and a caller must not read a resolved predicate as being animal-specific.
|
|
52
|
+
* What makes the ANSWER model-specific is the context the caller passes and
|
|
53
|
+
* the predicate actually reading it -- see {@link Orm#getAccess}.
|
|
54
|
+
*
|
|
55
|
+
* NAMED FOR WHAT IT HOLDS. It was `accessFiles` through review, inherited
|
|
56
|
+
* from the function-local in `setup-rest-server.ts` where the values came
|
|
57
|
+
* straight out of `forEachFileImport` and "files" was defensible. The values
|
|
58
|
+
* are `AccessFunction`s, and the sibling public registries on this class
|
|
59
|
+
* (`models`, `serializers`, `views`, `transforms`) are all plural nouns of
|
|
60
|
+
* the thing held. Renamed here because #202 is the last moment it is free.
|
|
43
61
|
*
|
|
44
62
|
* Populated by `setup-rest-server.ts` at boot, from the access classes under
|
|
45
63
|
* `config.orm.paths.access`, BEFORE any route is mounted -- so it is complete
|
|
@@ -58,10 +76,13 @@ export default class Orm {
|
|
|
58
76
|
* request routed to model Y -- inexpressible, which is the capability
|
|
59
77
|
* abofs/stonyx-orm#196 and abofs/stonyx-orm#207 are built on.
|
|
60
78
|
*
|
|
61
|
-
* Empty when the REST server is disabled,
|
|
62
|
-
*
|
|
79
|
+
* Empty when the REST server is disabled, and PARTIAL when one access file
|
|
80
|
+
* failed to load (`setup-rest-server.ts` catches, warns and assigns whatever
|
|
81
|
+
* it had). So a missing key does NOT mean the model has no access class.
|
|
82
|
+
* Prefer {@link Orm#getAccess} over indexing this directly -- it is guarded
|
|
83
|
+
* against the prototype chain and this is not.
|
|
63
84
|
*/
|
|
64
|
-
|
|
85
|
+
accessFunctions = {};
|
|
65
86
|
options;
|
|
66
87
|
sqlDb;
|
|
67
88
|
db;
|
|
@@ -187,7 +208,7 @@ export default class Orm {
|
|
|
187
208
|
* collection the request is ADDRESSED TO -- owners -- while being asked about
|
|
188
209
|
* animals, and per #202's thesis it answers wrong in the granting direction.
|
|
189
210
|
*
|
|
190
|
-
* OWN PROPERTIES ONLY. A bare `this.
|
|
211
|
+
* OWN PROPERTIES ONLY. A bare `this.accessFunctions[modelName]` walks the
|
|
191
212
|
* prototype chain, so `getAccess('constructor')` resolved `Object` and
|
|
192
213
|
* `getAccess('toString')` resolved `Object.prototype.toString` -- both
|
|
193
214
|
* callable, and the documented `predicate?.(request, ctx)` pattern then
|
|
@@ -206,9 +227,9 @@ export default class Orm {
|
|
|
206
227
|
* note above. Treat it as deny.
|
|
207
228
|
*/
|
|
208
229
|
getAccess(modelName) {
|
|
209
|
-
if (!Object.hasOwn(this.
|
|
230
|
+
if (!Object.hasOwn(this.accessFunctions, modelName))
|
|
210
231
|
return undefined;
|
|
211
|
-
return this.
|
|
232
|
+
return this.accessFunctions[modelName];
|
|
212
233
|
}
|
|
213
234
|
async startup() {
|
|
214
235
|
if (this.sqlDb)
|
|
@@ -8,7 +8,7 @@ import { dbKey } from './db.js';
|
|
|
8
8
|
import { getPluralName } from './plural-registry.js';
|
|
9
9
|
import log from 'stonyx/log';
|
|
10
10
|
export default async function (route, accessPath, metaRoute) {
|
|
11
|
-
const
|
|
11
|
+
const accessFunctions = {};
|
|
12
12
|
try {
|
|
13
13
|
await forEachFileImport(accessPath, (accessClass) => {
|
|
14
14
|
const accessInstance = new accessClass();
|
|
@@ -25,9 +25,9 @@ export default async function (route, accessPath, metaRoute) {
|
|
|
25
25
|
continue;
|
|
26
26
|
if (!store.data.has(model))
|
|
27
27
|
throw new Error(`Unable to define access for Invalid Model "${model}". Model does not exist`);
|
|
28
|
-
if (
|
|
28
|
+
if (accessFunctions[model])
|
|
29
29
|
throw new Error(`Access for model "${model}" has already been defined by another access class.`);
|
|
30
|
-
|
|
30
|
+
accessFunctions[model] = accessInstance.access;
|
|
31
31
|
}
|
|
32
32
|
});
|
|
33
33
|
}
|
|
@@ -38,7 +38,7 @@ export default async function (route, accessPath, metaRoute) {
|
|
|
38
38
|
// -------------------------------------------------------------------------
|
|
39
39
|
// #202 -- the registry has to survive this function.
|
|
40
40
|
//
|
|
41
|
-
// `
|
|
41
|
+
// `accessFunctions` used to be a function-local that was discarded at the return
|
|
42
42
|
// below, so the only thing that ever saw it was the mount loop. Each mounted
|
|
43
43
|
// OrmRequest then held its OWN model's predicate and nothing held the map, so
|
|
44
44
|
// at request time there was no route from a model NAME to that model's
|
|
@@ -56,12 +56,12 @@ export default async function (route, accessPath, metaRoute) {
|
|
|
56
56
|
// set of predicates that is actually enforcing. A guard here that skipped the
|
|
57
57
|
// assignment would let the registry go silently missing, which is precisely
|
|
58
58
|
// the failure #202's AC8 exists to catch.
|
|
59
|
-
Orm.instance.
|
|
59
|
+
Orm.instance.accessFunctions = accessFunctions;
|
|
60
60
|
await waitForModule('rest-server');
|
|
61
61
|
// Remove "/" prefix and name mount point accordingly
|
|
62
62
|
const name = route === '/' ? 'index' : (route[0] === '/' ? route.slice(1) : route);
|
|
63
63
|
// Configure endpoints for models and views with access configuration
|
|
64
|
-
for (const [model, access] of Object.entries(
|
|
64
|
+
for (const [model, access] of Object.entries(accessFunctions)) {
|
|
65
65
|
const pluralizedModel = getPluralName(model);
|
|
66
66
|
const modelName = name === 'index' ? pluralizedModel : `${name}/${pluralizedModel}`;
|
|
67
67
|
RestServer.instance.mountRoute(OrmRequest, { name: modelName, options: { model, access } });
|
package/package.json
CHANGED
package/src/main.ts
CHANGED
|
@@ -71,7 +71,25 @@ export default class Orm {
|
|
|
71
71
|
warnings: Set<string> = new Set();
|
|
72
72
|
|
|
73
73
|
/**
|
|
74
|
-
* Model name ->
|
|
74
|
+
* Model name -> the `access` predicate of the access class that CLAIMS that
|
|
75
|
+
* model (abofs/stonyx-orm#202).
|
|
76
|
+
*
|
|
77
|
+
* Not "that model's own predicate". One access class may claim many models
|
|
78
|
+
* -- `GlobalAccess` in this repo's fixtures declares five, and `models = '*'`
|
|
79
|
+
* claims every model in the store -- and it declares ONE `access` method, so
|
|
80
|
+
* the same function object is registered under every one of those keys.
|
|
81
|
+
* `getAccess('owner') === getAccess('animal')` is `true` there. The
|
|
82
|
+
* one-to-one guarantee below is key -> function, never function -> model,
|
|
83
|
+
* and a caller must not read a resolved predicate as being animal-specific.
|
|
84
|
+
* What makes the ANSWER model-specific is the context the caller passes and
|
|
85
|
+
* the predicate actually reading it -- see {@link Orm#getAccess}.
|
|
86
|
+
*
|
|
87
|
+
* NAMED FOR WHAT IT HOLDS. It was `accessFiles` through review, inherited
|
|
88
|
+
* from the function-local in `setup-rest-server.ts` where the values came
|
|
89
|
+
* straight out of `forEachFileImport` and "files" was defensible. The values
|
|
90
|
+
* are `AccessFunction`s, and the sibling public registries on this class
|
|
91
|
+
* (`models`, `serializers`, `views`, `transforms`) are all plural nouns of
|
|
92
|
+
* the thing held. Renamed here because #202 is the last moment it is free.
|
|
75
93
|
*
|
|
76
94
|
* Populated by `setup-rest-server.ts` at boot, from the access classes under
|
|
77
95
|
* `config.orm.paths.access`, BEFORE any route is mounted -- so it is complete
|
|
@@ -90,10 +108,13 @@ export default class Orm {
|
|
|
90
108
|
* request routed to model Y -- inexpressible, which is the capability
|
|
91
109
|
* abofs/stonyx-orm#196 and abofs/stonyx-orm#207 are built on.
|
|
92
110
|
*
|
|
93
|
-
* Empty when the REST server is disabled,
|
|
94
|
-
*
|
|
111
|
+
* Empty when the REST server is disabled, and PARTIAL when one access file
|
|
112
|
+
* failed to load (`setup-rest-server.ts` catches, warns and assigns whatever
|
|
113
|
+
* it had). So a missing key does NOT mean the model has no access class.
|
|
114
|
+
* Prefer {@link Orm#getAccess} over indexing this directly -- it is guarded
|
|
115
|
+
* against the prototype chain and this is not.
|
|
95
116
|
*/
|
|
96
|
-
|
|
117
|
+
accessFunctions: Record<string, AccessFunction> = {};
|
|
97
118
|
|
|
98
119
|
options!: OrmOptions;
|
|
99
120
|
sqlDb?: SqlDb;
|
|
@@ -240,7 +261,7 @@ export default class Orm {
|
|
|
240
261
|
* collection the request is ADDRESSED TO -- owners -- while being asked about
|
|
241
262
|
* animals, and per #202's thesis it answers wrong in the granting direction.
|
|
242
263
|
*
|
|
243
|
-
* OWN PROPERTIES ONLY. A bare `this.
|
|
264
|
+
* OWN PROPERTIES ONLY. A bare `this.accessFunctions[modelName]` walks the
|
|
244
265
|
* prototype chain, so `getAccess('constructor')` resolved `Object` and
|
|
245
266
|
* `getAccess('toString')` resolved `Object.prototype.toString` -- both
|
|
246
267
|
* callable, and the documented `predicate?.(request, ctx)` pattern then
|
|
@@ -259,9 +280,9 @@ export default class Orm {
|
|
|
259
280
|
* note above. Treat it as deny.
|
|
260
281
|
*/
|
|
261
282
|
getAccess(modelName: string): AccessFunction | undefined {
|
|
262
|
-
if (!Object.hasOwn(this.
|
|
283
|
+
if (!Object.hasOwn(this.accessFunctions, modelName)) return undefined;
|
|
263
284
|
|
|
264
|
-
return this.
|
|
285
|
+
return this.accessFunctions[modelName];
|
|
265
286
|
}
|
|
266
287
|
|
|
267
288
|
async startup(): Promise<void> {
|
package/src/setup-rest-server.ts
CHANGED
|
@@ -20,7 +20,7 @@ interface AccessInstance {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
export default async function(route: string, accessPath: string, metaRoute: boolean): Promise<void> {
|
|
23
|
-
const
|
|
23
|
+
const accessFunctions: Record<string, AccessFunction> = {};
|
|
24
24
|
|
|
25
25
|
try {
|
|
26
26
|
await forEachFileImport(accessPath, (accessClass: unknown) => {
|
|
@@ -37,9 +37,9 @@ export default async function(route: string, accessPath: string, metaRoute: bool
|
|
|
37
37
|
for (const model of models === '*' ? availableModels : models) {
|
|
38
38
|
if (model === dbKey) continue;
|
|
39
39
|
if (!store.data.has(model)) throw new Error(`Unable to define access for Invalid Model "${model}". Model does not exist`);
|
|
40
|
-
if (
|
|
40
|
+
if (accessFunctions![model]) throw new Error(`Access for model "${model}" has already been defined by another access class.`);
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
accessFunctions![model] = accessInstance.access;
|
|
43
43
|
}
|
|
44
44
|
});
|
|
45
45
|
} catch (error) {
|
|
@@ -50,7 +50,7 @@ export default async function(route: string, accessPath: string, metaRoute: bool
|
|
|
50
50
|
// -------------------------------------------------------------------------
|
|
51
51
|
// #202 -- the registry has to survive this function.
|
|
52
52
|
//
|
|
53
|
-
// `
|
|
53
|
+
// `accessFunctions` used to be a function-local that was discarded at the return
|
|
54
54
|
// below, so the only thing that ever saw it was the mount loop. Each mounted
|
|
55
55
|
// OrmRequest then held its OWN model's predicate and nothing held the map, so
|
|
56
56
|
// at request time there was no route from a model NAME to that model's
|
|
@@ -68,7 +68,7 @@ export default async function(route: string, accessPath: string, metaRoute: bool
|
|
|
68
68
|
// set of predicates that is actually enforcing. A guard here that skipped the
|
|
69
69
|
// assignment would let the registry go silently missing, which is precisely
|
|
70
70
|
// the failure #202's AC8 exists to catch.
|
|
71
|
-
Orm.instance.
|
|
71
|
+
Orm.instance.accessFunctions = accessFunctions;
|
|
72
72
|
|
|
73
73
|
await waitForModule('rest-server');
|
|
74
74
|
|
|
@@ -76,7 +76,7 @@ export default async function(route: string, accessPath: string, metaRoute: bool
|
|
|
76
76
|
const name = route === '/' ? 'index' : (route[0] === '/' ? route.slice(1) : route);
|
|
77
77
|
|
|
78
78
|
// Configure endpoints for models and views with access configuration
|
|
79
|
-
for (const [model, access] of Object.entries(
|
|
79
|
+
for (const [model, access] of Object.entries(accessFunctions!)) {
|
|
80
80
|
const pluralizedModel = getPluralName(model);
|
|
81
81
|
const modelName = name === 'index' ? pluralizedModel : `${name}/${pluralizedModel}`;
|
|
82
82
|
RestServer.instance.mountRoute(OrmRequest, { name: modelName, options: { model, access } });
|