@stonyx/orm 0.3.2-beta.154 → 0.3.2-beta.156
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 +301 -85
- package/dist/manage-record.js +234 -9
- package/dist/orm-request.d.ts +60 -25
- package/dist/orm-request.js +118 -30
- package/dist/standalone-db.js +17 -5
- package/dist/utils.d.ts +44 -0
- package/dist/utils.js +47 -0
- package/package.json +1 -1
- package/src/manage-record.ts +253 -9
- package/src/orm-request.ts +120 -30
- package/src/standalone-db.ts +17 -6
- package/src/utils.ts +50 -0
package/src/standalone-db.ts
CHANGED
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
|
|
9
9
|
import fs from 'fs/promises';
|
|
10
10
|
import path from 'path';
|
|
11
|
+
// `./utils.js` pulls in `@stonyx/utils/string` and nothing else -- no ORM
|
|
12
|
+
// bootstrap, no `@stonyx/orm` index, no side effects -- so the "no framework
|
|
13
|
+
// dependencies" property above still holds.
|
|
14
|
+
import { maxNumericId } from './utils.js';
|
|
11
15
|
|
|
12
16
|
interface StandaloneDBOptions {
|
|
13
17
|
dbPath?: string;
|
|
@@ -131,12 +135,19 @@ export default class StandaloneDB {
|
|
|
131
135
|
const records = await this.readCollection(collection);
|
|
132
136
|
|
|
133
137
|
if (!data.id) {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
138
|
+
// SHARED WITH `assignRecordId` (src/manage-record.ts), which is the other
|
|
139
|
+
// place this repo picks a server-assigned id. It was a second copy of the
|
|
140
|
+
// reduce, and nothing here pointed at it — a maintainer editing this
|
|
141
|
+
// method could not discover the other existed. See `maxNumericId` for why
|
|
142
|
+
// it is not `Math.max` (abofs/stonyx-orm#203).
|
|
143
|
+
//
|
|
144
|
+
// THE TWO ARE NOT THE SAME FUNCTION beyond this line, deliberately.
|
|
145
|
+
// `StandaloneDB` has no model, id-type or transform concept, so `maxId + 1`
|
|
146
|
+
// IS its store key; `assignRecordId` has to map the candidate through the
|
|
147
|
+
// model's declared id transform first, and then walk past occupied keys.
|
|
148
|
+
// Transplanting this method's remaining logic into the ORM reproduces
|
|
149
|
+
// #203's landing-key defect exactly — which is what AC4 pins.
|
|
150
|
+
data.id = maxNumericId(records) + 1;
|
|
140
151
|
}
|
|
141
152
|
|
|
142
153
|
// Check for duplicate id
|
package/src/utils.ts
CHANGED
|
@@ -20,3 +20,53 @@ export function pluralize(word: string): string {
|
|
|
20
20
|
|
|
21
21
|
return basePluralize(word);
|
|
22
22
|
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The highest NUMERIC id held by a set of records, or `0` when there is none.
|
|
26
|
+
*
|
|
27
|
+
* ONE COPY, and the duplication it replaces is the reason it lives here. Three
|
|
28
|
+
* near-identical reduces existed at once: `assignRecordId` (server-assigned id
|
|
29
|
+
* selection), `StandaloneDB.create` (src/standalone-db.ts) and the #203 test
|
|
30
|
+
* helper. `docs/improvements.md`'s standing WET Code category prescribes
|
|
31
|
+
* exactly this remedy -- extract into the module that already acts as the
|
|
32
|
+
* shared utility -- and `assignRecordId` already imported `isOrmRecord` from
|
|
33
|
+
* here.
|
|
34
|
+
*
|
|
35
|
+
* NON-NUMBERS ARE SKIPPED RATHER THAN COERCED TO `0`, AND THAT IS STYLISTIC.
|
|
36
|
+
* `StandaloneDB`'s shape mapped them to `0`, which can never beat a seed of
|
|
37
|
+
* `0`. Measured over eleven input classes (`[]`, `1`, `NaN`, `'5'`, `'abc'`,
|
|
38
|
+
* `-3`, `0`, `null`, `undefined`, `Infinity`, and mixed arrays) the two shapes
|
|
39
|
+
* produce IDENTICAL output on every one. In particular `typeof NaN` is
|
|
40
|
+
* `'number'`, so NEITHER shape coerces `NaN` -- both reject it on `NaN > max`,
|
|
41
|
+
* which is `false`. An earlier revision of this code asserted that the skip was
|
|
42
|
+
* what made the `NaN` case work; it is not, the comparison is, and that claim
|
|
43
|
+
* has been removed rather than left standing.
|
|
44
|
+
*
|
|
45
|
+
* WHAT IS LOAD-BEARING is that this is not `Math.max(...ids)`. `Math.max`
|
|
46
|
+
* returns `NaN` if any operand is `NaN`, and a record CAN be held under the key
|
|
47
|
+
* `NaN` -- so the obvious fix assigns `NaN`, lands on that slot and overwrites
|
|
48
|
+
* it, which is abofs/stonyx-orm#203 in a new disguise. Pinned by
|
|
49
|
+
* test/unit/assign-record-id-test.ts AC2; before that file existed the whole
|
|
50
|
+
* suite scored 951/0 under exactly that fix.
|
|
51
|
+
*/
|
|
52
|
+
export function maxNumericId(records: { id?: unknown }[]): number {
|
|
53
|
+
return records.reduce((max: number, record) => {
|
|
54
|
+
const { id } = record;
|
|
55
|
+
|
|
56
|
+
return typeof id === 'number' && id > max ? id : max;
|
|
57
|
+
}, 0);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* The message prefix `assignRecordId` throws with when no free id can be
|
|
62
|
+
* derived for a model, and the ONE string `createHandler` matches on to answer
|
|
63
|
+
* `409` instead of letting the rejection reach express's default handler.
|
|
64
|
+
*
|
|
65
|
+
* It lives here rather than in either file because both need it and neither
|
|
66
|
+
* should own a copy: a literal in two places is how the two id coercions in
|
|
67
|
+
* orm-request.ts drifted apart (see `coerceId`). The repo has no error codes
|
|
68
|
+
* and no custom error classes -- 24 bare `throw new Error` sites across `src/`
|
|
69
|
+
* -- so a shared prefix is the narrowest way to make ONE failure distinguishable
|
|
70
|
+
* without inventing an error taxonomy this codebase does not use.
|
|
71
|
+
*/
|
|
72
|
+
export const NO_FREE_ID_ERROR = 'Cannot assign record ID: no free id available';
|