@haverstack/core 0.40.0 → 0.42.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 +121 -59
- package/dist/access.d.ts +34 -36
- package/dist/access.d.ts.map +1 -1
- package/dist/access.js +65 -42
- package/dist/access.js.map +1 -1
- package/dist/{combine.d.ts → adapter/combine.d.ts} +8 -1
- package/dist/adapter/combine.d.ts.map +1 -0
- package/dist/{combine.js → adapter/combine.js} +7 -0
- package/dist/adapter/combine.js.map +1 -0
- package/dist/{adapter-entry.d.ts → adapter/index.d.ts} +6 -5
- package/dist/adapter/index.d.ts.map +1 -0
- package/dist/{adapter-entry.js → adapter/index.js} +5 -4
- package/dist/adapter/index.js.map +1 -0
- package/dist/{sort.d.ts → adapter/sort.d.ts} +20 -20
- package/dist/adapter/sort.d.ts.map +1 -0
- package/dist/{sort.js → adapter/sort.js} +37 -27
- package/dist/adapter/sort.js.map +1 -0
- package/dist/associations/identity.d.ts +93 -0
- package/dist/associations/identity.d.ts.map +1 -0
- package/dist/associations/identity.js +198 -0
- package/dist/associations/identity.js.map +1 -0
- package/dist/associations/validation.d.ts +90 -0
- package/dist/associations/validation.d.ts.map +1 -0
- package/dist/associations/validation.js +339 -0
- package/dist/associations/validation.js.map +1 -0
- package/dist/attachment-records.d.ts +48 -0
- package/dist/attachment-records.d.ts.map +1 -0
- package/dist/attachment-records.js +52 -0
- package/dist/attachment-records.js.map +1 -0
- package/dist/collection.d.ts +182 -0
- package/dist/collection.d.ts.map +1 -0
- package/dist/collection.js +407 -0
- package/dist/collection.js.map +1 -0
- package/dist/content-match.d.ts +25 -0
- package/dist/content-match.d.ts.map +1 -0
- package/dist/content-match.js +51 -0
- package/dist/content-match.js.map +1 -0
- package/dist/content-translation.d.ts +42 -0
- package/dist/content-translation.d.ts.map +1 -0
- package/dist/content-translation.js +108 -0
- package/dist/content-translation.js.map +1 -0
- package/dist/did/did.d.ts.map +1 -0
- package/dist/{did.js → did/did.js} +3 -4
- package/dist/did/did.js.map +1 -0
- package/dist/{did-entry.d.ts → did/index.d.ts} +1 -1
- package/dist/did/index.d.ts.map +1 -0
- package/dist/{did-entry.js → did/index.js} +3 -3
- package/dist/did/index.js.map +1 -0
- package/dist/errors.d.ts +36 -63
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +35 -58
- package/dist/errors.js.map +1 -1
- package/dist/grants.d.ts +20 -43
- package/dist/grants.d.ts.map +1 -1
- package/dist/grants.js +71 -134
- package/dist/grants.js.map +1 -1
- package/dist/id.d.ts +13 -31
- package/dist/id.d.ts.map +1 -1
- package/dist/id.js +13 -31
- package/dist/id.js.map +1 -1
- package/dist/index.d.ts +14 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -5
- package/dist/index.js.map +1 -1
- package/dist/install.d.ts +141 -0
- package/dist/install.d.ts.map +1 -0
- package/dist/install.js +219 -0
- package/dist/install.js.map +1 -0
- package/dist/merge.d.ts.map +1 -1
- package/dist/merge.js +5 -11
- package/dist/merge.js.map +1 -1
- package/dist/migration-steps.d.ts +102 -0
- package/dist/migration-steps.d.ts.map +1 -0
- package/dist/migration-steps.js +293 -0
- package/dist/migration-steps.js.map +1 -0
- package/dist/query-validation.d.ts +28 -94
- package/dist/query-validation.d.ts.map +1 -1
- package/dist/query-validation.js +156 -336
- package/dist/query-validation.js.map +1 -1
- package/dist/record-changes.d.ts +2 -117
- package/dist/record-changes.d.ts.map +1 -1
- package/dist/record-changes.js +10 -212
- package/dist/record-changes.js.map +1 -1
- package/dist/schema.d.ts +18 -7
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +59 -30
- package/dist/schema.js.map +1 -1
- package/dist/scoped-stack/authority.d.ts +156 -0
- package/dist/scoped-stack/authority.d.ts.map +1 -0
- package/dist/scoped-stack/authority.js +384 -0
- package/dist/scoped-stack/authority.js.map +1 -0
- package/dist/scoped-stack/scoped-stack.d.ts +338 -0
- package/dist/scoped-stack/scoped-stack.d.ts.map +1 -0
- package/dist/scoped-stack/scoped-stack.js +776 -0
- package/dist/scoped-stack/scoped-stack.js.map +1 -0
- package/dist/scoped-stack/subscription.d.ts +55 -0
- package/dist/scoped-stack/subscription.d.ts.map +1 -0
- package/dist/scoped-stack/subscription.js +110 -0
- package/dist/scoped-stack/subscription.js.map +1 -0
- package/dist/stack/attachments.d.ts +24 -0
- package/dist/stack/attachments.d.ts.map +1 -0
- package/dist/stack/attachments.js +113 -0
- package/dist/stack/attachments.js.map +1 -0
- package/dist/{changes.d.ts → stack/changes.d.ts} +29 -65
- package/dist/stack/changes.d.ts.map +1 -0
- package/dist/{changes.js → stack/changes.js} +32 -66
- package/dist/stack/changes.js.map +1 -0
- package/dist/stack/client.d.ts +271 -0
- package/dist/stack/client.d.ts.map +1 -0
- package/dist/stack/client.js +9 -0
- package/dist/stack/client.js.map +1 -0
- package/dist/stack/identity-bindings.d.ts.map +1 -0
- package/dist/{identity-bindings.js → stack/identity-bindings.js} +8 -14
- package/dist/stack/identity-bindings.js.map +1 -0
- package/dist/stack/install-app.d.ts +22 -0
- package/dist/stack/install-app.d.ts.map +1 -0
- package/dist/stack/install-app.js +260 -0
- package/dist/stack/install-app.js.map +1 -0
- package/dist/stack/integrity.d.ts +94 -0
- package/dist/stack/integrity.d.ts.map +1 -0
- package/dist/stack/integrity.js +268 -0
- package/dist/stack/integrity.js.map +1 -0
- package/dist/{limits.d.ts → stack/limits.d.ts} +2 -5
- package/dist/stack/limits.d.ts.map +1 -0
- package/dist/{limits.js → stack/limits.js} +3 -6
- package/dist/stack/limits.js.map +1 -0
- package/dist/stack/migrations.d.ts +59 -0
- package/dist/stack/migrations.d.ts.map +1 -0
- package/dist/stack/migrations.js +141 -0
- package/dist/stack/migrations.js.map +1 -0
- package/dist/{stack-reads.d.ts → stack/reads.d.ts} +33 -6
- package/dist/stack/reads.d.ts.map +1 -0
- package/dist/{stack-reads.js → stack/reads.js} +49 -7
- package/dist/stack/reads.js.map +1 -0
- package/dist/stack/record-id.d.ts +35 -0
- package/dist/stack/record-id.d.ts.map +1 -0
- package/dist/stack/record-id.js +82 -0
- package/dist/stack/record-id.js.map +1 -0
- package/dist/stack/stack.d.ts +438 -0
- package/dist/stack/stack.d.ts.map +1 -0
- package/dist/stack/stack.js +1366 -0
- package/dist/stack/stack.js.map +1 -0
- package/dist/stack/system-types.d.ts +16 -0
- package/dist/stack/system-types.d.ts.map +1 -0
- package/dist/stack/system-types.js +111 -0
- package/dist/stack/system-types.js.map +1 -0
- package/dist/stack/type-grants.d.ts +20 -0
- package/dist/stack/type-grants.d.ts.map +1 -0
- package/dist/stack/type-grants.js +69 -0
- package/dist/stack/type-grants.js.map +1 -0
- package/dist/stack/write-expectation.d.ts +78 -0
- package/dist/stack/write-expectation.d.ts.map +1 -0
- package/dist/stack/write-expectation.js +71 -0
- package/dist/stack/write-expectation.js.map +1 -0
- package/dist/testing.d.ts +21 -26
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +50 -70
- package/dist/testing.js.map +1 -1
- package/dist/type-handle.d.ts +67 -78
- package/dist/type-handle.d.ts.map +1 -1
- package/dist/type-handle.js +44 -111
- package/dist/type-handle.js.map +1 -1
- package/dist/types/adapter.d.ts +223 -0
- package/dist/types/adapter.d.ts.map +1 -0
- package/dist/types/adapter.js +5 -0
- package/dist/types/adapter.js.map +1 -0
- package/dist/types/associations.d.ts +156 -0
- package/dist/types/associations.d.ts.map +1 -0
- package/dist/types/associations.js +13 -0
- package/dist/types/associations.js.map +1 -0
- package/dist/types/capabilities.d.ts +89 -0
- package/dist/types/capabilities.d.ts.map +1 -0
- package/dist/types/capabilities.js +5 -0
- package/dist/types/capabilities.js.map +1 -0
- package/dist/types/events.d.ts +155 -0
- package/dist/types/events.d.ts.map +1 -0
- package/dist/types/events.js +5 -0
- package/dist/types/events.js.map +1 -0
- package/dist/types/ids.d.ts +43 -0
- package/dist/types/ids.d.ts.map +1 -0
- package/dist/types/ids.js +5 -0
- package/dist/types/ids.js.map +1 -0
- package/dist/types/index.d.ts +19 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +19 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/journal.d.ts +47 -0
- package/dist/types/journal.d.ts.map +1 -0
- package/dist/types/journal.js +5 -0
- package/dist/types/journal.js.map +1 -0
- package/dist/types/query.d.ts +185 -0
- package/dist/types/query.d.ts.map +1 -0
- package/dist/types/query.js +10 -0
- package/dist/types/query.js.map +1 -0
- package/dist/types/records.d.ts +76 -0
- package/dist/types/records.d.ts.map +1 -0
- package/dist/types/records.js +5 -0
- package/dist/types/records.js.map +1 -0
- package/dist/types/schema.d.ts +74 -0
- package/dist/types/schema.d.ts.map +1 -0
- package/dist/types/schema.js +5 -0
- package/dist/types/schema.js.map +1 -0
- package/dist/types/system.d.ts +161 -0
- package/dist/types/system.d.ts.map +1 -0
- package/dist/types/system.js +32 -0
- package/dist/types/system.js.map +1 -0
- package/dist/types/tokens.d.ts +39 -0
- package/dist/types/tokens.d.ts.map +1 -0
- package/dist/types/tokens.js +5 -0
- package/dist/types/tokens.js.map +1 -0
- package/dist/validate.d.ts +29 -70
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +65 -97
- package/dist/validate.js.map +1 -1
- package/dist/{attachment-download.d.ts → wire/attachment-download.d.ts} +0 -57
- package/dist/wire/attachment-download.d.ts.map +1 -0
- package/dist/{attachment-download.js → wire/attachment-download.js} +0 -61
- package/dist/wire/attachment-download.js.map +1 -0
- package/dist/{auth.d.ts → wire/auth.d.ts} +5 -8
- package/dist/wire/auth.d.ts.map +1 -0
- package/dist/{auth.js → wire/auth.js} +5 -8
- package/dist/wire/auth.js.map +1 -0
- package/dist/{wire-body.d.ts → wire/body.d.ts} +13 -5
- package/dist/wire/body.d.ts.map +1 -0
- package/dist/{wire-body.js → wire/body.js} +65 -7
- package/dist/wire/body.js.map +1 -0
- package/dist/{wire-entry.d.ts → wire/index.d.ts} +12 -12
- package/dist/wire/index.d.ts.map +1 -0
- package/dist/{wire-entry.js → wire/index.js} +13 -17
- package/dist/wire/index.js.map +1 -0
- package/dist/{wire-record.d.ts → wire/record.d.ts} +10 -22
- package/dist/wire/record.d.ts.map +1 -0
- package/dist/{wire-record.js → wire/record.js} +12 -24
- package/dist/wire/record.js.map +1 -0
- package/dist/{wire-request.d.ts → wire/request.d.ts} +20 -37
- package/dist/wire/request.d.ts.map +1 -0
- package/dist/{wire-request.js → wire/request.js} +79 -54
- package/dist/wire/request.js.map +1 -0
- package/package.json +7 -7
- package/dist/adapter-entry.d.ts.map +0 -1
- package/dist/adapter-entry.js.map +0 -1
- package/dist/attachment-download.d.ts.map +0 -1
- package/dist/attachment-download.js.map +0 -1
- package/dist/auth.d.ts.map +0 -1
- package/dist/auth.js.map +0 -1
- package/dist/changes.d.ts.map +0 -1
- package/dist/changes.js.map +0 -1
- package/dist/combine.d.ts.map +0 -1
- package/dist/combine.js.map +0 -1
- package/dist/did-entry.d.ts.map +0 -1
- package/dist/did-entry.js.map +0 -1
- package/dist/did.d.ts.map +0 -1
- package/dist/did.js.map +0 -1
- package/dist/identity-bindings.d.ts.map +0 -1
- package/dist/identity-bindings.js.map +0 -1
- package/dist/limits.d.ts.map +0 -1
- package/dist/limits.js.map +0 -1
- package/dist/record-id.d.ts +0 -70
- package/dist/record-id.d.ts.map +0 -1
- package/dist/record-id.js +0 -127
- package/dist/record-id.js.map +0 -1
- package/dist/scoped-stack.d.ts +0 -532
- package/dist/scoped-stack.d.ts.map +0 -1
- package/dist/scoped-stack.js +0 -1219
- package/dist/scoped-stack.js.map +0 -1
- package/dist/sort.d.ts.map +0 -1
- package/dist/sort.js.map +0 -1
- package/dist/stack-reads.d.ts.map +0 -1
- package/dist/stack-reads.js.map +0 -1
- package/dist/stack.d.ts +0 -971
- package/dist/stack.d.ts.map +0 -1
- package/dist/stack.js +0 -2012
- package/dist/stack.js.map +0 -1
- package/dist/types.d.ts +0 -1250
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -49
- package/dist/types.js.map +0 -1
- package/dist/wire-body.d.ts.map +0 -1
- package/dist/wire-body.js.map +0 -1
- package/dist/wire-entry.d.ts.map +0 -1
- package/dist/wire-entry.js.map +0 -1
- package/dist/wire-record.d.ts.map +0 -1
- package/dist/wire-record.js.map +0 -1
- package/dist/wire-request.d.ts.map +0 -1
- package/dist/wire-request.js.map +0 -1
- /package/dist/{did.d.ts → did/did.d.ts} +0 -0
- /package/dist/{identity-bindings.d.ts → stack/identity-bindings.d.ts} +0 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migration steps
|
|
3
|
+
* -------------------------------------------------------
|
|
4
|
+
* A migration is a list of declared steps — rename, map, add, drop and
|
|
5
|
+
* derive — rather than a function, so that core can read what became of
|
|
6
|
+
* each field. Two things are built from one list: the in-memory `migrate`
|
|
7
|
+
* that `Stack.open()`, a collection's reads and `migrateAll()` run, and the
|
|
8
|
+
* field origins a collection translates a content filter or sort through.
|
|
9
|
+
*
|
|
10
|
+
* `fieldOrigins()` also checks the list against both schemas, so a step
|
|
11
|
+
* the target Type would refuse fails when `migration()` runs rather than
|
|
12
|
+
* on the first record `migrateAll()` writes. See docs/spec/data-model.md
|
|
13
|
+
* § Migration steps.
|
|
14
|
+
*/
|
|
15
|
+
import { validateContent } from './validate.js';
|
|
16
|
+
/** Move a field's value to a new name. */
|
|
17
|
+
export const rename = (from, to) => Object.freeze({ op: 'rename', from, to });
|
|
18
|
+
/**
|
|
19
|
+
* Replace each value `values` lists with its mapping, keeping any other,
|
|
20
|
+
* element-wise through an array. A stored enum may hold a value no handle
|
|
21
|
+
* lists, and it is carried through rather than lost.
|
|
22
|
+
*/
|
|
23
|
+
export const map = (field, values) => Object.freeze({ op: 'map', field, values: Object.freeze({ ...values }) });
|
|
24
|
+
/** Set a field to a constant. */
|
|
25
|
+
export const add = (field, value) => Object.freeze({ op: 'add', field, value });
|
|
26
|
+
/** Remove a field. */
|
|
27
|
+
export const drop = (field) => Object.freeze({ op: 'drop', field });
|
|
28
|
+
/**
|
|
29
|
+
* Set a field to what `fn` computes from the record as stored at the
|
|
30
|
+
* migration's `from`; `undefined` leaves it absent. The one opaque step: a
|
|
31
|
+
* field it produces does not translate across versions.
|
|
32
|
+
*/
|
|
33
|
+
export const derive = (field, fn) => Object.freeze({ op: 'derive', field, derive: fn });
|
|
34
|
+
const describe = (step) => {
|
|
35
|
+
switch (step.op) {
|
|
36
|
+
case 'rename':
|
|
37
|
+
return `rename("${step.from}", "${step.to}")`;
|
|
38
|
+
case 'add':
|
|
39
|
+
return `add("${step.field}", …)`;
|
|
40
|
+
default:
|
|
41
|
+
return `${step.op}("${step.field}"${step.op === 'drop' ? '' : ', …'})`;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
// -------------------------------------------------------
|
|
45
|
+
// Applying steps
|
|
46
|
+
// -------------------------------------------------------
|
|
47
|
+
const remap = (value, values) => {
|
|
48
|
+
if (Array.isArray(value))
|
|
49
|
+
return value.map((v) => remap(v, values));
|
|
50
|
+
return typeof value === 'string' && Object.hasOwn(values, value) ? values[value] : value;
|
|
51
|
+
};
|
|
52
|
+
/** The content `steps` produce from `content`, which is left untouched. */
|
|
53
|
+
export const applySteps = (steps, content) => {
|
|
54
|
+
const out = { ...content };
|
|
55
|
+
for (const step of steps) {
|
|
56
|
+
switch (step.op) {
|
|
57
|
+
case 'rename':
|
|
58
|
+
if (Object.hasOwn(out, step.from))
|
|
59
|
+
out[step.to] = out[step.from];
|
|
60
|
+
delete out[step.from];
|
|
61
|
+
break;
|
|
62
|
+
case 'map':
|
|
63
|
+
if (out[step.field] !== undefined)
|
|
64
|
+
out[step.field] = remap(out[step.field], step.values);
|
|
65
|
+
break;
|
|
66
|
+
case 'add':
|
|
67
|
+
out[step.field] = step.value;
|
|
68
|
+
break;
|
|
69
|
+
case 'drop':
|
|
70
|
+
delete out[step.field];
|
|
71
|
+
break;
|
|
72
|
+
case 'derive': {
|
|
73
|
+
const value = step.derive(content);
|
|
74
|
+
if (value === undefined)
|
|
75
|
+
delete out[step.field];
|
|
76
|
+
else
|
|
77
|
+
out[step.field] = value;
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return out;
|
|
83
|
+
};
|
|
84
|
+
/** `outer` after `inner`, as one table over both domains. */
|
|
85
|
+
const composeValues = (inner, outer) => {
|
|
86
|
+
if (!inner)
|
|
87
|
+
return outer;
|
|
88
|
+
if (!outer)
|
|
89
|
+
return inner;
|
|
90
|
+
const composed = {};
|
|
91
|
+
for (const [from, to] of Object.entries(inner))
|
|
92
|
+
composed[from] = remap(to, outer);
|
|
93
|
+
for (const [from, to] of Object.entries(outer)) {
|
|
94
|
+
if (!Object.hasOwn(inner, from))
|
|
95
|
+
composed[from] = to;
|
|
96
|
+
}
|
|
97
|
+
return composed;
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* The origins across two migrations end to end: `lower` takes A to B and
|
|
101
|
+
* `upper` takes B to C, so the result says what each of C's fields is in
|
|
102
|
+
* terms of A's.
|
|
103
|
+
*/
|
|
104
|
+
export const composeOrigins = (lower, upper) => {
|
|
105
|
+
const composed = new Map();
|
|
106
|
+
for (const [field, origin] of upper) {
|
|
107
|
+
if (origin.kind !== 'field') {
|
|
108
|
+
composed.set(field, origin);
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
// A name the lower step neither declares nor removed passes through it.
|
|
112
|
+
const inner = lower.get(origin.field) ?? { kind: 'field', field: origin.field };
|
|
113
|
+
composed.set(field, inner.kind === 'field'
|
|
114
|
+
? { kind: 'field', field: inner.field, values: composeValues(inner.values, origin.values) }
|
|
115
|
+
: inner.kind === 'constant'
|
|
116
|
+
? {
|
|
117
|
+
kind: 'constant',
|
|
118
|
+
value: origin.values ? remap(inner.value, origin.values) : inner.value,
|
|
119
|
+
}
|
|
120
|
+
: inner);
|
|
121
|
+
}
|
|
122
|
+
return composed;
|
|
123
|
+
};
|
|
124
|
+
const isStringKind = (def) => def?.kind === 'enum' || def?.kind === 'string' || def?.kind === 'text';
|
|
125
|
+
/** What `map` may remap: a string-shaped scalar, or a declared array of one. */
|
|
126
|
+
const isMappable = (def) => isStringKind(def) || (def?.kind === 'array' && !def.open && isStringKind(def.items));
|
|
127
|
+
const MAX_CARRY_DEPTH = 32;
|
|
128
|
+
/**
|
|
129
|
+
* Why a value stored under `source` could fail `target` once `values`
|
|
130
|
+
* remaps it, or null when it cannot. A carried field is written as it
|
|
131
|
+
* stands, so this is write validity, not the read compatibility
|
|
132
|
+
* isCompatible() answers.
|
|
133
|
+
*/
|
|
134
|
+
const carryProblem = (path, source, target, values, depth) => {
|
|
135
|
+
if (depth > MAX_CARRY_DEPTH)
|
|
136
|
+
return `"${path}" nests too deeply to check`;
|
|
137
|
+
if (target.required && !source.required) {
|
|
138
|
+
return `"${path}" is optional at the source and required at the target`;
|
|
139
|
+
}
|
|
140
|
+
if (target.kind === 'array') {
|
|
141
|
+
if (source.kind !== 'array')
|
|
142
|
+
return `"${path}" changes kind from ${source.kind} to array`;
|
|
143
|
+
if (target.open)
|
|
144
|
+
return null;
|
|
145
|
+
if (source.open)
|
|
146
|
+
return `"${path}" is an open array at the source and declared at the target`;
|
|
147
|
+
return carryProblem(`${path}[]`, source.items, target.items, values, depth + 1);
|
|
148
|
+
}
|
|
149
|
+
if (target.kind === 'object') {
|
|
150
|
+
if (source.kind !== 'object')
|
|
151
|
+
return `"${path}" changes kind from ${source.kind} to object`;
|
|
152
|
+
if (target.open)
|
|
153
|
+
return null;
|
|
154
|
+
if (source.open)
|
|
155
|
+
return `"${path}" is an open object at the source and declared at the target`;
|
|
156
|
+
for (const [key, def] of Object.entries(target.properties)) {
|
|
157
|
+
const from = source.properties[key];
|
|
158
|
+
if (!from) {
|
|
159
|
+
if (def.required)
|
|
160
|
+
return `"${path}.${key}" is required at the target and not at the source`;
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
const problem = carryProblem(`${path}.${key}`, from, def, undefined, depth + 1);
|
|
164
|
+
if (problem)
|
|
165
|
+
return problem;
|
|
166
|
+
}
|
|
167
|
+
for (const key of Object.keys(source.properties)) {
|
|
168
|
+
if (!Object.hasOwn(target.properties, key)) {
|
|
169
|
+
return `"${path}.${key}" is not declared at the target`;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
if (target.kind === 'enum') {
|
|
175
|
+
if (source.kind !== 'enum')
|
|
176
|
+
return `"${path}" changes kind from ${source.kind} to enum`;
|
|
177
|
+
const allowed = new Set(target.values);
|
|
178
|
+
const missing = source.values.find((v) => !allowed.has(remap(v, values ?? {})));
|
|
179
|
+
return missing === undefined
|
|
180
|
+
? null
|
|
181
|
+
: `"${path}" holds "${missing}"${values && Object.hasOwn(values, missing) ? ` (mapped to "${values[missing]}")` : ''}, which the target's enum does not list`;
|
|
182
|
+
}
|
|
183
|
+
if (target.kind === 'string' || target.kind === 'text') {
|
|
184
|
+
return isStringKind(source)
|
|
185
|
+
? null
|
|
186
|
+
: `"${path}" changes kind from ${source.kind} to ${target.kind}`;
|
|
187
|
+
}
|
|
188
|
+
return source.kind === target.kind
|
|
189
|
+
? null
|
|
190
|
+
: `"${path}" changes kind from ${source.kind} to ${target.kind}`;
|
|
191
|
+
};
|
|
192
|
+
/**
|
|
193
|
+
* What each of `to`'s top-level fields is in terms of `from`'s, after
|
|
194
|
+
* `steps`. Returns the problem that makes the list unusable as a string
|
|
195
|
+
* instead: a step naming a field that is not there, a field `to` would
|
|
196
|
+
* refuse, or one it requires that nothing produces. See
|
|
197
|
+
* docs/spec/data-model.md § Migration steps.
|
|
198
|
+
*/
|
|
199
|
+
export const fieldOrigins = (steps, from, to) => {
|
|
200
|
+
const current = new Map(Object.keys(from).map((field) => [field, { kind: 'field', field }]));
|
|
201
|
+
const removed = new Set();
|
|
202
|
+
for (const step of steps) {
|
|
203
|
+
const at = describe(step);
|
|
204
|
+
const named = step.op === 'rename' ? step.from : step.field;
|
|
205
|
+
if (step.op !== 'add' && step.op !== 'derive' && !current.has(named)) {
|
|
206
|
+
return `${at}: "${named}" is not a field at this point`;
|
|
207
|
+
}
|
|
208
|
+
switch (step.op) {
|
|
209
|
+
case 'rename':
|
|
210
|
+
if (current.has(step.to))
|
|
211
|
+
return `${at}: "${step.to}" is already a field at this point`;
|
|
212
|
+
current.set(step.to, current.get(step.from));
|
|
213
|
+
current.delete(step.from);
|
|
214
|
+
removed.add(step.from);
|
|
215
|
+
break;
|
|
216
|
+
case 'map': {
|
|
217
|
+
const origin = current.get(step.field);
|
|
218
|
+
if (origin.kind === 'field') {
|
|
219
|
+
if (!isMappable(from[origin.field])) {
|
|
220
|
+
return `${at}: only an enum, string or text field, or an array of one, can be mapped`;
|
|
221
|
+
}
|
|
222
|
+
current.set(step.field, {
|
|
223
|
+
...origin,
|
|
224
|
+
values: composeValues(origin.values, step.values),
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
else if (origin.kind === 'constant') {
|
|
228
|
+
current.set(step.field, { kind: 'constant', value: remap(origin.value, step.values) });
|
|
229
|
+
}
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
case 'add':
|
|
233
|
+
if (current.has(step.field))
|
|
234
|
+
return `${at}: "${step.field}" is already a field at this point`;
|
|
235
|
+
if (step.value === undefined)
|
|
236
|
+
return `${at}: an added field needs a value`;
|
|
237
|
+
current.set(step.field, { kind: 'constant', value: step.value });
|
|
238
|
+
break;
|
|
239
|
+
case 'drop':
|
|
240
|
+
current.delete(step.field);
|
|
241
|
+
removed.add(step.field);
|
|
242
|
+
break;
|
|
243
|
+
case 'derive':
|
|
244
|
+
current.set(step.field, { kind: 'derived' });
|
|
245
|
+
break;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
for (const field of current.keys()) {
|
|
249
|
+
if (!Object.hasOwn(to, field)) {
|
|
250
|
+
return `"${field}" is carried to a Type that does not declare it; drop or rename it`;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
for (const [field, def] of Object.entries(to)) {
|
|
254
|
+
const origin = current.get(field);
|
|
255
|
+
if (!origin) {
|
|
256
|
+
if (def.required)
|
|
257
|
+
return `"${field}" is required at the target, and no step produces it`;
|
|
258
|
+
// migrate() carries every field no step names, so one an older
|
|
259
|
+
// version gained in place arrives under its own name.
|
|
260
|
+
current.set(field, removed.has(field) ? { kind: 'constant', value: undefined } : { kind: 'field', field });
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
if (origin.kind === 'field') {
|
|
264
|
+
const problem = carryProblem(field, from[origin.field], def, origin.values, 0);
|
|
265
|
+
if (problem)
|
|
266
|
+
return problem;
|
|
267
|
+
}
|
|
268
|
+
else if (origin.kind === 'constant') {
|
|
269
|
+
const errors = validateContent({ [field]: origin.value }, { [field]: def });
|
|
270
|
+
if (errors.length > 0)
|
|
271
|
+
return `"${field}" is set to a value the target refuses: ${errors[0].message}`;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
for (const field of removed) {
|
|
275
|
+
if (!current.has(field))
|
|
276
|
+
current.set(field, { kind: 'constant', value: undefined });
|
|
277
|
+
}
|
|
278
|
+
return current;
|
|
279
|
+
};
|
|
280
|
+
// -------------------------------------------------------
|
|
281
|
+
// Registry of origins
|
|
282
|
+
// -------------------------------------------------------
|
|
283
|
+
/**
|
|
284
|
+
* The origins `migration()` computed for each migration it built. A
|
|
285
|
+
* migration assembled by hand has none, and a collection reads every field
|
|
286
|
+
* across it as derived.
|
|
287
|
+
*/
|
|
288
|
+
const ORIGINS = new WeakMap();
|
|
289
|
+
export const recordOrigins = (migration, origins) => {
|
|
290
|
+
ORIGINS.set(migration, origins);
|
|
291
|
+
};
|
|
292
|
+
export const originsOf = (migration) => ORIGINS.get(migration);
|
|
293
|
+
//# sourceMappingURL=migration-steps.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration-steps.js","sourceRoot":"","sources":["../src/migration-steps.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Query
|
|
2
|
+
* Query validation
|
|
3
3
|
* -------------------------------------------------------
|
|
4
|
-
* The sanitizers a query passes through before any adapter sees it
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* raw JSON that no compiler has seen.
|
|
4
|
+
* The sanitizers a query passes through before any adapter sees it. A
|
|
5
|
+
* `QuerySort` field or a filter's discriminated union is a compile-time
|
|
6
|
+
* promise, and a server mapping a request onto either supplies raw JSON
|
|
7
|
+
* that no compiler has seen.
|
|
9
8
|
*
|
|
10
9
|
* These live in the invariant layer for the reason validation and
|
|
11
10
|
* `_config` protection do — so no adapter can forget one. `assertQueryCapabilities`
|
|
@@ -16,8 +15,7 @@
|
|
|
16
15
|
* the query — one rule, one answer.
|
|
17
16
|
* See docs/spec/data-model.md § Capability-gated filters.
|
|
18
17
|
*/
|
|
19
|
-
import type {
|
|
20
|
-
import type { Association, AssociationEdit, AuthorityAssociation, DataAssociation, GrantGrantee, JournalQuery, VersionsQuery, QuerySort, RecordFilter, RelationshipTarget, StackCapabilities } from './types.js';
|
|
18
|
+
import type { JournalQuery, QuerySort, RecordFilter, StackCapabilities, VersionsQuery } from './types/index.js';
|
|
21
19
|
/**
|
|
22
20
|
* Fail loud rather than silently widen: a filter the adapter can't honor
|
|
23
21
|
* would otherwise be dropped, returning an unfiltered superset presented
|
|
@@ -26,6 +24,14 @@ import type { Association, AssociationEdit, AuthorityAssociation, DataAssociatio
|
|
|
26
24
|
* § Capability-gated filters.
|
|
27
25
|
*/
|
|
28
26
|
export declare function assertQueryCapabilities(filter: RecordFilter | undefined, capabilities: Pick<StackCapabilities, 'filter'>): void;
|
|
27
|
+
/**
|
|
28
|
+
* Hold `filter.contentByType` to a split along Types: at least one arm,
|
|
29
|
+
* each pinned to a TypeId and flat, and never beside a top-level content
|
|
30
|
+
* predicate, whose relation to the arms would be ambiguous. A map that
|
|
31
|
+
* could arrive empty would match everything once it crossed the wire. See
|
|
32
|
+
* docs/spec/data-model.md § Content by type.
|
|
33
|
+
*/
|
|
34
|
+
export declare function assertValidContentByType(filter: RecordFilter | undefined): void;
|
|
29
35
|
/**
|
|
30
36
|
* Whether a single-segment content filter can be pushed down to the
|
|
31
37
|
* adapter. Callers that read a content field pair this with an in-memory
|
|
@@ -42,15 +48,10 @@ export declare function filtersContent(features: Pick<StackCapabilities, 'filter
|
|
|
42
48
|
*/
|
|
43
49
|
export declare function parseContentFilterKey(key: string): string[];
|
|
44
50
|
/**
|
|
45
|
-
* Reject a sort
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* adapter interpolates the direction straight into `ORDER BY`, so an
|
|
50
|
-
* unvalidated value there is a SQL-injection sink reachable from every
|
|
51
|
-
* untrusted caller. Validating in the invariant layer — the same reason
|
|
52
|
-
* emission and _config protection live here — means no adapter can forget
|
|
53
|
-
* it. See docs/spec/data-model.md § Sorting and pagination.
|
|
51
|
+
* Reject a sort outside the closed set the types promise. A type is not a
|
|
52
|
+
* runtime guard, and a SQLite adapter interpolates the direction straight
|
|
53
|
+
* into `ORDER BY`, so an unvalidated one is an injection sink reachable by
|
|
54
|
+
* any untrusted caller. See docs/spec/data-model.md § Sorting and pagination.
|
|
54
55
|
*/
|
|
55
56
|
export declare function assertValidSort(sort: QuerySort | undefined): void;
|
|
56
57
|
/**
|
|
@@ -71,72 +72,6 @@ export declare function normalizeSort(sort: QuerySort | undefined): (QuerySort &
|
|
|
71
72
|
* — see docs/spec/data-model.md § Capability-gated filters.
|
|
72
73
|
*/
|
|
73
74
|
export declare function assertSortCapability(sort: QuerySort | undefined, capabilities: Pick<StackCapabilities, 'sort'>): void;
|
|
74
|
-
/** Every key each relationship target arm defines. */
|
|
75
|
-
export declare const TARGET_KEYS: Record<RelationshipTarget['kind'], readonly string[]>;
|
|
76
|
-
/** Every key each grantee arm defines, the default tier included. */
|
|
77
|
-
export declare const GRANTEE_KEYS: Record<GrantGrantee['kind'], readonly string[]>;
|
|
78
|
-
/**
|
|
79
|
-
* The keys of `value` its arm does not define. An element carrying one is
|
|
80
|
-
* refused rather than stored: an adapter keeps only the keys it has
|
|
81
|
-
* columns for, so the rest would answer 200 and then vanish.
|
|
82
|
-
* See docs/spec/data-model.md § Associations.
|
|
83
|
-
*/
|
|
84
|
-
export declare function unknownKeys(value: object, keys: readonly string[]): string[];
|
|
85
|
-
/** unknownKeys() as a refusal. 400, not 422: the key addresses nothing. */
|
|
86
|
-
export declare function assertKnownKeys(value: object, keys: readonly string[], path: string): void;
|
|
87
|
-
/**
|
|
88
|
-
* Reject a relationship target outside the closed set the types promise.
|
|
89
|
-
* A discriminated union is not a runtime guard — a server mapping a
|
|
90
|
-
* request body onto an association supplies raw JSON — and an
|
|
91
|
-
* unrecognized kind would otherwise be stored under the one arm that
|
|
92
|
-
* names a Record in this stack. See docs/spec/data-model.md
|
|
93
|
-
* § Relationship targets.
|
|
94
|
-
*
|
|
95
|
-
* A key the element's kind does not define is thrown as a 400 rather than
|
|
96
|
-
* collected, at every depth: it addresses nothing, the way an unknown key
|
|
97
|
-
* on a request body does. See docs/spec/data-model.md § Associations.
|
|
98
|
-
*/
|
|
99
|
-
export declare function validateAssociation(association: Association, path?: string): ValidationError[];
|
|
100
|
-
/**
|
|
101
|
-
* Refuse an authority element reaching the data half of the partition.
|
|
102
|
-
* The two share a table, a delta shape and a durability tier; they never
|
|
103
|
-
* share a call, because an app editing tags would otherwise be able to
|
|
104
|
-
* replace an ACL it was never shown. Thrown rather than collected: the
|
|
105
|
-
* caller named the wrong surface, not a malformed value, and the verb it
|
|
106
|
-
* wanted is `grantAccess()`/`revokeAccess()`.
|
|
107
|
-
*
|
|
108
|
-
* Lives here, in the invariant layer, so an unscoped `Stack`, an import
|
|
109
|
-
* and a server mapping a request body are all held to it.
|
|
110
|
-
* See docs/spec/access-control.md § Record-level permissions.
|
|
111
|
-
*/
|
|
112
|
-
export declare function assertDataAssociations(associations: readonly Association[], surface: string): asserts associations is DataAssociation[];
|
|
113
|
-
/**
|
|
114
|
-
* The mirror of assertDataAssociations(): the `permissions` surface takes
|
|
115
|
-
* authority kinds alone, so a tag routed through it is refused rather than
|
|
116
|
-
* quietly becoming an ACL entry the `associations` projection never shows.
|
|
117
|
-
*/
|
|
118
|
-
export declare function assertAuthorityAssociations(permissions: readonly Association[], surface: string): asserts permissions is AuthorityAssociation[];
|
|
119
|
-
/** validateAssociation() over a create's `associations` array. */
|
|
120
|
-
/**
|
|
121
|
-
* Every association in a list, plus the one rule a list has that a single
|
|
122
|
-
* association does not: **identities are distinct**. A list naming one
|
|
123
|
-
* identity twice describes a state no store can hold — an adapter keys
|
|
124
|
-
* associations by identity, so the second entry displaces the first — and
|
|
125
|
-
* which of the two the record ends up with is a question the caller did
|
|
126
|
-
* not mean to ask. Refused rather than collapsed, for the reason an empty
|
|
127
|
-
* change set is refused: every way of producing one is a caller bug.
|
|
128
|
-
* See docs/spec/data-model.md § Associations.
|
|
129
|
-
*/
|
|
130
|
-
export declare function validateAssociations(associations: Association[] | undefined, path?: string): ValidationError[];
|
|
131
|
-
/**
|
|
132
|
-
* Hold an association edit list to the rules every verb shares: non-empty,
|
|
133
|
-
* every element a known `op` over a well-formed association, one surface,
|
|
134
|
-
* and each identity named once — a list that both removes and adds one
|
|
135
|
-
* identity is the same ambiguity as naming it twice. `repoint` is the
|
|
136
|
-
* journal's word, never a request. Asked of the whole list before anything
|
|
137
|
-
* is read or written. See docs/spec/data-model.md § Mutations.
|
|
138
|
-
*/
|
|
139
|
-
export declare function assertAssociationEdits(changes: unknown, surface: string, half: 'data' | 'authority'): asserts changes is AssociationEdit[];
|
|
140
75
|
/**
|
|
141
76
|
* Reject an association filter that names neither of its halves, or whose
|
|
142
77
|
* relationship target is malformed. `RelatedToFilter` and `AttachmentFilter`
|
|
@@ -145,6 +80,12 @@ export declare function assertAssociationEdits(changes: unknown, surface: string
|
|
|
145
80
|
* any association of that kind. See docs/spec/data-model.md § Filter.
|
|
146
81
|
*/
|
|
147
82
|
export declare function assertValidAssociationFilters(filter: RecordFilter | undefined): void;
|
|
83
|
+
/**
|
|
84
|
+
* Refuse a `baseId` argument that cannot name a type family, as an
|
|
85
|
+
* argument error prefixed with the `surface` it was passed to. See
|
|
86
|
+
* familyIdProblem().
|
|
87
|
+
*/
|
|
88
|
+
export declare function assertFamilyId(baseId: unknown, surface: string): void;
|
|
148
89
|
/**
|
|
149
90
|
* Refuse a `baseId` carrying an `@version` suffix. A family filter resolves
|
|
150
91
|
* against registered families, so a TypeId there would match nothing and
|
|
@@ -154,17 +95,10 @@ export declare function assertValidBaseIdFilter(filter: {
|
|
|
154
95
|
baseId?: string | string[];
|
|
155
96
|
} | undefined): void;
|
|
156
97
|
/**
|
|
157
|
-
* Hold a journal window to
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
* LIMIT as "no ceiling" and returns the whole log. Neither is what the
|
|
162
|
-
* caller asked for, and the disagreement is invisible until a stack
|
|
163
|
-
* changes adapters. Refuse at the surface so no adapter has to guess.
|
|
164
|
-
*
|
|
165
|
-
* No ceiling is imposed: unlike a query, omitting `limit` reads the whole
|
|
166
|
-
* log by contract, so a clamp would silently truncate exactly the caller
|
|
167
|
-
* reconstructing an association's full history.
|
|
98
|
+
* Hold a journal window to a shape both adapters honor identically: a
|
|
99
|
+
* negative `limit` drops the newest entry from a JS slice but reads as no
|
|
100
|
+
* ceiling in SQLite. No ceiling is imposed — omitting `limit` reads the
|
|
101
|
+
* whole log by contract, which a clamp would silently truncate.
|
|
168
102
|
*/
|
|
169
103
|
export declare function assertValidJournalQuery(query: JournalQuery | undefined): void;
|
|
170
104
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query-validation.d.ts","sourceRoot":"","sources":["../src/query-validation.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"query-validation.d.ts","sourceRoot":"","sources":["../src/query-validation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAWH,OAAO,KAAK,EACV,YAAY,EACZ,SAAS,EACT,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACd,MAAM,kBAAkB,CAAC;AAE1B;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,YAAY,GAAG,SAAS,EAChC,YAAY,EAAE,IAAI,CAAC,iBAAiB,EAAE,QAAQ,CAAC,GAC9C,IAAI,CAsDN;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,YAAY,GAAG,SAAS,GAAG,IAAI,CAgD/E;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,IAAI,CAAC,iBAAiB,EAAE,QAAQ,CAAC,GAAG,OAAO,CAEnF;AAKD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAwB3D;AAaD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,GAAG,IAAI,CAsBjE;AAsDD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,SAAS,GAAG,SAAS,GAC1B,CAAC,SAAS,GAAG;IAAE,SAAS,EAAE,KAAK,GAAG,MAAM,CAAA;CAAE,CAAC,GAAG,SAAS,CAKzD;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,SAAS,GAAG,SAAS,EAC3B,YAAY,EAAE,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,GAC5C,IAAI,CAiCN;AAED;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,YAAY,GAAG,SAAS,GAAG,IAAI,CAmBpF;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAKrE;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;CAAE,GAAG,SAAS,GAAG,IAAI,CAOhG;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,YAAY,GAAG,SAAS,GAAG,IAAI,CAW7E;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,aAAa,GAAG,SAAS,GAAG,IAAI,CAW/E"}
|