@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 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.accessFiles`, keyed
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 -> that model's `access` predicate (abofs/stonyx-orm#202).
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, or when no access configuration
57
- * could be loaded. Prefer {@link Orm#getAccess} over indexing this directly.
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
- accessFiles: Record<string, AccessFunction>;
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.accessFiles[modelName]` walks the
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 -> that model's `access` predicate (abofs/stonyx-orm#202).
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, or when no access configuration
62
- * could be loaded. Prefer {@link Orm#getAccess} over indexing this directly.
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
- accessFiles = {};
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.accessFiles[modelName]` walks the
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.accessFiles, modelName))
230
+ if (!Object.hasOwn(this.accessFunctions, modelName))
210
231
  return undefined;
211
- return this.accessFiles[modelName];
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 accessFiles = {};
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 (accessFiles[model])
28
+ if (accessFunctions[model])
29
29
  throw new Error(`Access for model "${model}" has already been defined by another access class.`);
30
- accessFiles[model] = accessInstance.access;
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
- // `accessFiles` used to be a function-local that was discarded at the return
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.accessFiles = accessFiles;
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(accessFiles)) {
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
@@ -4,7 +4,7 @@
4
4
  "stonyx-async",
5
5
  "stonyx-module"
6
6
  ],
7
- "version": "0.3.2-alpha.57",
7
+ "version": "0.3.2-alpha.58",
8
8
  "description": "",
9
9
  "main": "dist/index.js",
10
10
  "type": "module",
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 -> that model's `access` predicate (abofs/stonyx-orm#202).
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, or when no access configuration
94
- * could be loaded. Prefer {@link Orm#getAccess} over indexing this directly.
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
- accessFiles: Record<string, AccessFunction> = {};
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.accessFiles[modelName]` walks the
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.accessFiles, modelName)) return undefined;
283
+ if (!Object.hasOwn(this.accessFunctions, modelName)) return undefined;
263
284
 
264
- return this.accessFiles[modelName];
285
+ return this.accessFunctions[modelName];
265
286
  }
266
287
 
267
288
  async startup(): Promise<void> {
@@ -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 accessFiles: Record<string, AccessFunction> = {};
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 (accessFiles![model]) throw new Error(`Access for model "${model}" has already been defined by another access class.`);
40
+ if (accessFunctions![model]) throw new Error(`Access for model "${model}" has already been defined by another access class.`);
41
41
 
42
- accessFiles![model] = accessInstance.access;
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
- // `accessFiles` used to be a function-local that was discarded at the return
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.accessFiles = accessFiles;
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(accessFiles!)) {
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 } });