@procedures/collections 0.8.0 → 0.9.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/CHANGELOG.md +41 -0
- package/README.md +104 -10
- package/dist/.tsbuildinfo +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/overlay/index.d.ts +6 -0
- package/dist/overlay/index.d.ts.map +1 -0
- package/dist/overlay/index.js +6 -0
- package/dist/overlay/index.js.map +1 -0
- package/dist/overlay/overlay.d.ts +55 -0
- package/dist/overlay/overlay.d.ts.map +1 -0
- package/dist/overlay/overlay.js +82 -0
- package/dist/overlay/overlay.js.map +1 -0
- package/dist/overlay/stacks.d.ts +44 -0
- package/dist/overlay/stacks.d.ts.map +1 -0
- package/dist/overlay/stacks.js +43 -0
- package/dist/overlay/stacks.js.map +1 -0
- package/dist/storage/index.d.ts +13 -8
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +13 -7
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/interface.d.ts +105 -13
- package/dist/storage/interface.d.ts.map +1 -1
- package/dist/storage/interface.js +10 -8
- package/dist/storage/interface.js.map +1 -1
- package/dist/storage/listeners.d.ts +39 -0
- package/dist/storage/listeners.d.ts.map +1 -0
- package/dist/storage/listeners.js +65 -0
- package/dist/storage/listeners.js.map +1 -0
- package/dist/storage/memory.d.ts +46 -6
- package/dist/storage/memory.d.ts.map +1 -1
- package/dist/storage/memory.js +81 -10
- package/dist/storage/memory.js.map +1 -1
- package/dist/testing/storage-contract.d.ts +13 -0
- package/dist/testing/storage-contract.d.ts.map +1 -1
- package/dist/testing/storage-contract.js +472 -0
- package/dist/testing/storage-contract.js.map +1 -1
- package/package.json +4 -1
- package/src/index.ts +3 -0
- package/src/overlay/index.ts +15 -0
- package/src/overlay/overlay.ts +119 -0
- package/src/overlay/stacks.ts +76 -0
- package/src/storage/index.ts +54 -8
- package/src/storage/interface.ts +117 -13
- package/src/storage/listeners.ts +72 -0
- package/src/storage/memory.ts +110 -11
- package/src/testing/storage-contract.ts +522 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/overlay/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EACL,KAAK,EACL,aAAa,EACb,WAAW,EACX,OAAO,EACP,WAAW,EACX,WAAW,GAIZ,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,aAAa,EAAE,OAAO,EAA0D,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The overlay: one logical document read through a stack of layers (for example the scopes of a
|
|
3
|
+
* space, a board, a group and a user). Each layer can hold its own copy of the document. A read
|
|
4
|
+
* merges the copies, top layer first:
|
|
5
|
+
*
|
|
6
|
+
* - The mode `field` (the default): each top-level field comes from the highest copy that has it. A
|
|
7
|
+
* copy that sets a field to `UNSET` hides the lower values of that field.
|
|
8
|
+
* - The mode `doc`: the highest copy wins whole.
|
|
9
|
+
* - A copy with `_deleted: true` hides the whole logical document, and in the mode `field` also the
|
|
10
|
+
* copies under it.
|
|
11
|
+
*
|
|
12
|
+
* A copy of the mode `field` is the same as the patch operations of `applyPatch` over the copies
|
|
13
|
+
* under it: its fields are `$set`, and its `UNSET` fields are `$unset` (refer to `copyAsPatch`).
|
|
14
|
+
* It is not a JSON Merge Patch: in a copy, `null` is a value, and an object field replaces the
|
|
15
|
+
* lower object whole.
|
|
16
|
+
*/
|
|
17
|
+
import type { StoragePatchOperations } from "@procedures/client/query";
|
|
18
|
+
/**
|
|
19
|
+
* The value that hides a field of the lower layers (`{ field: UNSET }` in an upper copy). Its key has
|
|
20
|
+
* no `$`, because MongoDB refuses or warns about field names that start with `$`.
|
|
21
|
+
*/
|
|
22
|
+
export declare const UNSET: Readonly<{
|
|
23
|
+
__unset: true;
|
|
24
|
+
}>;
|
|
25
|
+
/** The field of a copy that hides the whole logical document. */
|
|
26
|
+
export declare const DELETED_FIELD = "_deleted";
|
|
27
|
+
/** The field of a copy that names its layer. A merge removes it. */
|
|
28
|
+
export declare const LAYER_FIELD = "_scope";
|
|
29
|
+
/** How the copies of a document merge: field by field, or the top copy whole. */
|
|
30
|
+
export type MergeMode = "field" | "doc";
|
|
31
|
+
/** The options of `mergeCopies` and `copyAsPatch`: the names of the fields of the overlay. */
|
|
32
|
+
export interface OverlayFields {
|
|
33
|
+
/** The field that hides the whole document (default `_deleted`). */
|
|
34
|
+
readonly deleted?: string | undefined;
|
|
35
|
+
/** The field that names the layer of a copy (default `_scope`). A merge removes it. */
|
|
36
|
+
readonly layer?: string | undefined;
|
|
37
|
+
}
|
|
38
|
+
/** A copy of a logical document in one layer. */
|
|
39
|
+
export type LayerCopy = Readonly<Record<string, unknown>>;
|
|
40
|
+
/** True when a value is the `UNSET` marker (an object with only `__unset: true`). */
|
|
41
|
+
export declare function isUnset(value: unknown): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* This function merges the copies of one logical document. `copies` is in the order of the stack,
|
|
44
|
+
* top layer first, with `undefined` for a layer without a copy. It gives the merged document
|
|
45
|
+
* without the fields of the overlay, or `undefined` when no copy exists or the top copy is deleted.
|
|
46
|
+
*/
|
|
47
|
+
export declare function mergeCopies(copies: ReadonlyArray<LayerCopy | undefined>, mode?: MergeMode, fields?: OverlayFields): Record<string, unknown> | undefined;
|
|
48
|
+
/**
|
|
49
|
+
* This function gives a copy of the mode `field` as the patch operations of `applyPatch`: its
|
|
50
|
+
* fields as `$set` and its `UNSET` fields as `$unset`. `applyPatch(lower, copyAsPatch(copy))` then
|
|
51
|
+
* gives the merge of the copy over `lower`. A field name with a dot is a path for `applyPatch`, so
|
|
52
|
+
* the two agree only for field names without a dot.
|
|
53
|
+
*/
|
|
54
|
+
export declare function copyAsPatch(copy: LayerCopy, fields?: OverlayFields): StoragePatchOperations;
|
|
55
|
+
//# sourceMappingURL=overlay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overlay.d.ts","sourceRoot":"","sources":["../../src/overlay/overlay.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAEvE;;;GAGG;AACH,eAAO,MAAM,KAAK,EAAE,QAAQ,CAAC;IAAE,OAAO,EAAE,IAAI,CAAA;CAAE,CAA6C,CAAC;AAE5F,iEAAiE;AACjE,eAAO,MAAM,aAAa,aAAa,CAAC;AAExC,oEAAoE;AACpE,eAAO,MAAM,WAAW,WAAW,CAAC;AAEpC,iFAAiF;AACjF,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,KAAK,CAAC;AAExC,8FAA8F;AAC9F,MAAM,WAAW,aAAa;IAC5B,oEAAoE;IACpE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,uFAAuF;IACvF,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACrC;AAED,iDAAiD;AACjD,MAAM,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAE1D,qFAAqF;AACrF,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAQ/C;AAgBD;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,aAAa,CAAC,SAAS,GAAG,SAAS,CAAC,EAC5C,IAAI,GAAE,SAAmB,EACzB,MAAM,GAAE,aAAkB,GACzB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAwBrC;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,GAAE,aAAkB,GAAG,sBAAsB,CAQ/F"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The value that hides a field of the lower layers (`{ field: UNSET }` in an upper copy). Its key has
|
|
3
|
+
* no `$`, because MongoDB refuses or warns about field names that start with `$`.
|
|
4
|
+
*/
|
|
5
|
+
export const UNSET = Object.freeze({ __unset: true });
|
|
6
|
+
/** The field of a copy that hides the whole logical document. */
|
|
7
|
+
export const DELETED_FIELD = "_deleted";
|
|
8
|
+
/** The field of a copy that names its layer. A merge removes it. */
|
|
9
|
+
export const LAYER_FIELD = "_scope";
|
|
10
|
+
/** True when a value is the `UNSET` marker (an object with only `__unset: true`). */
|
|
11
|
+
export function isUnset(value) {
|
|
12
|
+
return (typeof value === "object" &&
|
|
13
|
+
value !== null &&
|
|
14
|
+
!Array.isArray(value) &&
|
|
15
|
+
value.__unset === true &&
|
|
16
|
+
Object.keys(value).length === 1);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* This function sets an own field. An assignment with the key `__proto__` changes the prototype,
|
|
20
|
+
* and a copy from `JSON.parse` or a database can have that key (the same rule as `applyPatch`).
|
|
21
|
+
*/
|
|
22
|
+
function put(target, key, value) {
|
|
23
|
+
Object.defineProperty(target, key, { value, enumerable: true, writable: true, configurable: true });
|
|
24
|
+
}
|
|
25
|
+
/** The fields of a copy without the fields of the overlay. */
|
|
26
|
+
function withoutOverlayFields(copy, fields) {
|
|
27
|
+
const { [fields.deleted ?? DELETED_FIELD]: _deleted, [fields.layer ?? LAYER_FIELD]: _layer, ...rest } = copy;
|
|
28
|
+
return rest;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* This function merges the copies of one logical document. `copies` is in the order of the stack,
|
|
32
|
+
* top layer first, with `undefined` for a layer without a copy. It gives the merged document
|
|
33
|
+
* without the fields of the overlay, or `undefined` when no copy exists or the top copy is deleted.
|
|
34
|
+
*/
|
|
35
|
+
export function mergeCopies(copies, mode = "field", fields = {}) {
|
|
36
|
+
const deleted = fields.deleted ?? DELETED_FIELD;
|
|
37
|
+
const present = copies.filter((copy) => copy !== undefined && copy !== null);
|
|
38
|
+
const top = present[0];
|
|
39
|
+
if (top === undefined || top[deleted] === true)
|
|
40
|
+
return undefined;
|
|
41
|
+
if (mode === "doc") {
|
|
42
|
+
const whole = withoutOverlayFields(top, fields);
|
|
43
|
+
for (const key of Object.keys(whole))
|
|
44
|
+
if (isUnset(whole[key]))
|
|
45
|
+
delete whole[key];
|
|
46
|
+
return whole;
|
|
47
|
+
}
|
|
48
|
+
// From the bottom copy up: a deleted copy lower down hides only the copies under it
|
|
49
|
+
let merged = {};
|
|
50
|
+
for (let index = present.length - 1; index >= 0; index--) {
|
|
51
|
+
const copy = present[index];
|
|
52
|
+
if (copy[deleted] === true) {
|
|
53
|
+
merged = {};
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
for (const [key, value] of Object.entries(withoutOverlayFields(copy, fields))) {
|
|
57
|
+
if (isUnset(value))
|
|
58
|
+
delete merged[key];
|
|
59
|
+
else
|
|
60
|
+
put(merged, key, value);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return merged;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* This function gives a copy of the mode `field` as the patch operations of `applyPatch`: its
|
|
67
|
+
* fields as `$set` and its `UNSET` fields as `$unset`. `applyPatch(lower, copyAsPatch(copy))` then
|
|
68
|
+
* gives the merge of the copy over `lower`. A field name with a dot is a path for `applyPatch`, so
|
|
69
|
+
* the two agree only for field names without a dot.
|
|
70
|
+
*/
|
|
71
|
+
export function copyAsPatch(copy, fields = {}) {
|
|
72
|
+
const set = {};
|
|
73
|
+
const unset = [];
|
|
74
|
+
for (const [key, value] of Object.entries(withoutOverlayFields(copy, fields))) {
|
|
75
|
+
if (isUnset(value))
|
|
76
|
+
unset.push(key);
|
|
77
|
+
else
|
|
78
|
+
put(set, key, value);
|
|
79
|
+
}
|
|
80
|
+
return { $set: set, $unset: unset };
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=overlay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overlay.js","sourceRoot":"","sources":["../../src/overlay/overlay.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The stacks of the overlay from rules. Each rule gives, for collections (and, when it names them,
|
|
3
|
+
* for some documents), a read stack (top layer first), the write targets (the first one is the
|
|
4
|
+
* default) and a merge mode. A rule names layers with variables, and the app resolves them, for
|
|
5
|
+
* example `$me` to the layer of the signed-in user. The first rule for a document wins, so a rule
|
|
6
|
+
* for some documents goes before the rule of their collection.
|
|
7
|
+
*/
|
|
8
|
+
import type { MergeMode } from "./overlay.js";
|
|
9
|
+
/** A rule of the overlay, with the layers as variables or names. */
|
|
10
|
+
export interface StackRule {
|
|
11
|
+
/** A collection, a list of collections, or a pattern that the app gives a meaning (for example `*`). */
|
|
12
|
+
readonly collection: string | readonly string[];
|
|
13
|
+
/** Only these documents (logical ids) of the collection. */
|
|
14
|
+
readonly doc?: string | readonly string[] | undefined;
|
|
15
|
+
/** The read stack, top layer first. */
|
|
16
|
+
readonly read: readonly string[];
|
|
17
|
+
/** The write targets. The default is the top layer of the read stack. */
|
|
18
|
+
readonly write?: string | readonly string[] | undefined;
|
|
19
|
+
/** The merge mode (default `field`). */
|
|
20
|
+
readonly mode?: MergeMode | undefined;
|
|
21
|
+
}
|
|
22
|
+
/** A resolved stack: for some documents (`docs`) or for all the documents of a collection. */
|
|
23
|
+
export interface ResolvedStack {
|
|
24
|
+
readonly docs?: readonly string[];
|
|
25
|
+
/** The read stack, top layer first. */
|
|
26
|
+
readonly layers: readonly string[];
|
|
27
|
+
/** The write targets. The first one is the default. */
|
|
28
|
+
readonly write: readonly string[];
|
|
29
|
+
readonly mode: MergeMode;
|
|
30
|
+
}
|
|
31
|
+
/** The function that gives the layer of a variable or a name, or `undefined` for none. */
|
|
32
|
+
export type LayerResolver = (variable: string) => string | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* This function resolves the rules to the stacks of each collection. A rule whose read stack
|
|
35
|
+
* resolves to no layer does not apply. After a rule for all the documents of a collection, a later
|
|
36
|
+
* rule of that collection does not apply.
|
|
37
|
+
*/
|
|
38
|
+
export declare function resolveStacks(rules: readonly StackRule[] | undefined, resolve: LayerResolver): Map<string, ResolvedStack[]>;
|
|
39
|
+
/**
|
|
40
|
+
* This function gives the stack of a document: the first stack of its collection that names the
|
|
41
|
+
* document or names no documents, or `undefined` when the collection has no stack.
|
|
42
|
+
*/
|
|
43
|
+
export declare function stackOf(stacks: ReadonlyMap<string, readonly ResolvedStack[]>, collection: string, id?: string): ResolvedStack | undefined;
|
|
44
|
+
//# sourceMappingURL=stacks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stacks.d.ts","sourceRoot":"","sources":["../../src/overlay/stacks.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,oEAAoE;AACpE,MAAM,WAAW,SAAS;IACxB,wGAAwG;IACxG,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;IAChD,4DAA4D;IAC5D,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IACtD,uCAAuC;IACvC,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,yEAAyE;IACzE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IACxD,wCAAwC;IACxC,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;CACvC;AAED,8FAA8F;AAC9F,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,uCAAuC;IACvC,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,uDAAuD;IACvD,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;CAC1B;AAED,0FAA0F;AAC1F,MAAM,MAAM,aAAa,GAAG,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;AAIrE;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,SAAS,EAAE,OAAO,EAAE,aAAa,GAAG,GAAG,CAAC,MAAM,EAAE,aAAa,EAAE,CAAC,CAwB3H;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAEzI"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
const listOf = (value) => (value === undefined ? [] : typeof value === "string" ? [value] : [...value]);
|
|
2
|
+
/**
|
|
3
|
+
* This function resolves the rules to the stacks of each collection. A rule whose read stack
|
|
4
|
+
* resolves to no layer does not apply. After a rule for all the documents of a collection, a later
|
|
5
|
+
* rule of that collection does not apply.
|
|
6
|
+
*/
|
|
7
|
+
export function resolveStacks(rules, resolve) {
|
|
8
|
+
const stacks = new Map();
|
|
9
|
+
for (const rule of rules ?? []) {
|
|
10
|
+
if (typeof rule !== "object" || rule === null)
|
|
11
|
+
continue;
|
|
12
|
+
const resolved = (values) => values.map((value) => resolve(value)).filter((layer) => typeof layer === "string" && layer !== "");
|
|
13
|
+
const layers = [...new Set(resolved(listOf(rule.read)))];
|
|
14
|
+
if (layers.length === 0)
|
|
15
|
+
continue;
|
|
16
|
+
const write = resolved(listOf(rule.write));
|
|
17
|
+
const docs = rule.doc === undefined ? undefined : listOf(rule.doc);
|
|
18
|
+
const stack = {
|
|
19
|
+
...(docs ? { docs } : {}),
|
|
20
|
+
layers,
|
|
21
|
+
write: write.length > 0 ? write : [layers[0]],
|
|
22
|
+
mode: rule.mode === "doc" ? "doc" : "field",
|
|
23
|
+
};
|
|
24
|
+
for (const collection of listOf(rule.collection)) {
|
|
25
|
+
if (typeof collection !== "string" || collection === "")
|
|
26
|
+
continue;
|
|
27
|
+
const list = stacks.get(collection) ?? [];
|
|
28
|
+
if (list.some((entry) => !entry.docs))
|
|
29
|
+
continue;
|
|
30
|
+
list.push(stack);
|
|
31
|
+
stacks.set(collection, list);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return stacks;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* This function gives the stack of a document: the first stack of its collection that names the
|
|
38
|
+
* document or names no documents, or `undefined` when the collection has no stack.
|
|
39
|
+
*/
|
|
40
|
+
export function stackOf(stacks, collection, id) {
|
|
41
|
+
return stacks.get(collection)?.find((stack) => !stack.docs || (id !== undefined && stack.docs.includes(id)));
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=stacks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stacks.js","sourceRoot":"","sources":["../../src/overlay/stacks.ts"],"names":[],"mappings":"AAoCA,MAAM,MAAM,GAAG,CAAC,KAA6C,EAAY,EAAE,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC;AAE1J;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,KAAuC,EAAE,OAAsB;IAC3F,MAAM,MAAM,GAAG,IAAI,GAAG,EAA2B,CAAC;IAClD,KAAK,MAAM,IAAI,IAAI,KAAK,IAAI,EAAE,EAAE,CAAC;QAC/B,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI;YAAE,SAAS;QACxD,MAAM,QAAQ,GAAG,CAAC,MAAgB,EAAY,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAmB,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,EAAE,CAAC,CAAC;QACrK,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QACzD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QAClC,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACnE,MAAM,KAAK,GAAkB;YAC3B,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACzB,MAAM;YACN,KAAK,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAE,CAAC;YAC9C,IAAI,EAAE,IAAI,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO;SAC5C,CAAC;QACF,KAAK,MAAM,UAAU,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;YACjD,IAAI,OAAO,UAAU,KAAK,QAAQ,IAAI,UAAU,KAAK,EAAE;gBAAE,SAAS;YAClE,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;YAC1C,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC;gBAAE,SAAS;YAChD,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACjB,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,MAAqD,EAAE,UAAkB,EAAE,EAAW;IAC5G,OAAO,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,EAAE,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAC/G,CAAC"}
|
package/dist/storage/index.d.ts
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* The storages of the collections.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* This package has the contract `CollectionStorage` and one storage, `InMemoryStorage` (fast,
|
|
5
|
+
* in memory only).
|
|
6
6
|
*
|
|
7
|
-
* The
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* The storages that need a `Client` are in `@procedures/store`: `ApiStorage` (a remote
|
|
8
|
+
* collection) and `HybridStorage` (a local copy that syncs with a remote). `MongoStorage` is in
|
|
9
|
+
* `@procedures/mongo`, and `SqliteStorage` is in `@procedures/sqlite`. This package uses only the
|
|
10
|
+
* query language of the core, from the entry `@procedures/client/query`, which imports nothing
|
|
11
|
+
* else (roadmap CLI-34).
|
|
11
12
|
*/
|
|
12
|
-
export type { CollectionStorage, StorageChanges, StorageMetadata } from "./interface.js";
|
|
13
|
+
export type { CollectionStorage, StorageChanges, StorageMetadata, StorageWriteOptions, StorageSetChange, StorageDeleteChange, StorageClearChange, StorageChangeEvent, StorageChangeBatch, StorageObserver, } from "./interface.js";
|
|
13
14
|
export { normalizeStorageResult } from "./interface.js";
|
|
15
|
+
export { DEFAULT_FIND_LIMIT, DEFAULT_QUERY_OPERATORS, DEFAULT_QUERY_POLICY, QUERY_OPERATORS, applyPatch, compareStorageValues, compileSelector, isPatchOperations, matchesSelector, mergePatch, patchProblems, queryEntries, queryOptionsProblems, selectorProblems, } from "@procedures/client/query";
|
|
16
|
+
export type { StorageFieldOperators, StoragePatch, StoragePatchOperations, StorageQueryOperator, StorageQueryOptions, StorageQueryPolicy, StorageSelector, StorageSort, StorageSortDirection, } from "@procedures/client/query";
|
|
17
|
+
export { StorageListeners, reportListenerError } from "./listeners.js";
|
|
18
|
+
export type { ListenerErrorHandler } from "./listeners.js";
|
|
14
19
|
export { InMemoryStorage } from "./memory.js";
|
|
15
20
|
export type { InMemoryStorageOptions } from "./memory.js";
|
|
16
21
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,YAAY,EACV,iBAAiB,EACjB,cAAc,EACd,eAAe,EACf,mBAAmB,EACnB,gBAAgB,EAChB,mBAAmB,EACnB,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,eAAe,GAChB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAIxD,OAAO,EACL,kBAAkB,EAClB,uBAAuB,EACvB,oBAAoB,EACpB,eAAe,EACf,UAAU,EACV,oBAAoB,EACpB,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,UAAU,EACV,aAAa,EACb,YAAY,EACZ,oBAAoB,EACpB,gBAAgB,GACjB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EACV,qBAAqB,EACrB,YAAY,EACZ,sBAAsB,EACtB,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,EAClB,eAAe,EACf,WAAW,EACX,oBAAoB,GACrB,MAAM,0BAA0B,CAAC;AAGlC,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACvE,YAAY,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAE3D,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,YAAY,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/storage/index.js
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* The storages of the collections.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* This package has the contract `CollectionStorage` and one storage, `InMemoryStorage` (fast,
|
|
5
|
+
* in memory only).
|
|
6
6
|
*
|
|
7
|
-
* The
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* The storages that need a `Client` are in `@procedures/store`: `ApiStorage` (a remote
|
|
8
|
+
* collection) and `HybridStorage` (a local copy that syncs with a remote). `MongoStorage` is in
|
|
9
|
+
* `@procedures/mongo`, and `SqliteStorage` is in `@procedures/sqlite`. This package uses only the
|
|
10
|
+
* query language of the core, from the entry `@procedures/client/query`, which imports nothing
|
|
11
|
+
* else (roadmap CLI-34).
|
|
11
12
|
*/
|
|
12
13
|
export { normalizeStorageResult } from "./interface.js";
|
|
14
|
+
// The query language: the selectors, the options, the query policy and the patches (roadmap
|
|
15
|
+
// CLI-14, CLI-20). The one copy is in the core (CLI-34): these names come from there.
|
|
16
|
+
export { DEFAULT_FIND_LIMIT, DEFAULT_QUERY_OPERATORS, DEFAULT_QUERY_POLICY, QUERY_OPERATORS, applyPatch, compareStorageValues, compileSelector, isPatchOperations, matchesSelector, mergePatch, patchProblems, queryEntries, queryOptionsProblems, selectorProblems, } from "@procedures/client/query";
|
|
17
|
+
// The listeners of the events of a storage
|
|
18
|
+
export { StorageListeners, reportListenerError } from "./listeners.js";
|
|
13
19
|
export { InMemoryStorage } from "./memory.js";
|
|
14
20
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAcH,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAExD,4FAA4F;AAC5F,sFAAsF;AACtF,OAAO,EACL,kBAAkB,EAClB,uBAAuB,EACvB,oBAAoB,EACpB,eAAe,EACf,UAAU,EACV,oBAAoB,EACpB,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,UAAU,EACV,aAAa,EACb,YAAY,EACZ,oBAAoB,EACpB,gBAAgB,GACjB,MAAM,0BAA0B,CAAC;AAalC,2CAA2C;AAC3C,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAGvE,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* The interface of the collection storages. These storages implement it:
|
|
3
|
+
* - `InMemoryStorage` (this package): a fast storage in a `Map`, in memory only.
|
|
4
|
+
* - `ApiStorage` (`@procedures/store`): a remote collection through a `Client`.
|
|
5
|
+
* - `HybridStorage` (`@procedures/store`): a local copy that syncs with a remote, with a
|
|
6
|
+
* conflict policy and a queue of the local writes.
|
|
7
|
+
* - `MongoStorage` (`@procedures/mongo`).
|
|
3
8
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* - ApiStorage: Remote persistence via universal client
|
|
7
|
-
* - HybridStorage: Local cache + remote sync with conflict resolution
|
|
8
|
-
* - SyncedStorage: Periodic background sync with offline support
|
|
9
|
+
* `@procedures/client` declares a copy of this interface with the same shape, so that the core
|
|
10
|
+
* does not depend on this package. `SqliteStorage` (`@procedures/sqlite`) implements that copy.
|
|
9
11
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
+
* Each method gives a `Promise`. Thus the code of a caller cannot tell a local storage from a
|
|
13
|
+
* remote storage.
|
|
12
14
|
*/
|
|
15
|
+
import type { StoragePatch, StorageQueryOptions, StorageQueryPolicy, StorageSelector } from "@procedures/client/query";
|
|
13
16
|
/**
|
|
14
17
|
* Storage backend interface for collections.
|
|
15
18
|
*
|
|
@@ -25,6 +28,11 @@
|
|
|
25
28
|
* - A delta of `getChangesSince` follows the order of the last change of each id.
|
|
26
29
|
* - After `close()`, each operation rejects with an error whose message says "closed". A second
|
|
27
30
|
* `close()` does nothing.
|
|
31
|
+
* - `query()` gives the result of `queryEntries()` for the entries of the storage.
|
|
32
|
+
* - `observe()` gives one batch for each write that changes the storage, before the write resolves.
|
|
33
|
+
* - `patch()` changes the item in one step: two patches at the same time keep each field.
|
|
34
|
+
*
|
|
35
|
+
* The optional methods are features: a caller tests for each one before it uses it.
|
|
28
36
|
*/
|
|
29
37
|
export interface CollectionStorage<T> {
|
|
30
38
|
/**
|
|
@@ -71,6 +79,35 @@ export interface CollectionStorage<T> {
|
|
|
71
79
|
* @returns Promise resolving to array of matching items
|
|
72
80
|
*/
|
|
73
81
|
find(predicate: (item: T) => boolean): Promise<T[]>;
|
|
82
|
+
/**
|
|
83
|
+
* The entries whose items match a selector, with an order, a skip and a limit. The selector is
|
|
84
|
+
* JSON, so a remote storage sends it to its server, and a database can run it itself. The result
|
|
85
|
+
* is the result of `queryEntries()` for the entries of the storage. A storage without this method
|
|
86
|
+
* does not run selectors: a caller uses `queryEntries()` on the entries.
|
|
87
|
+
*
|
|
88
|
+
* @param selector - The selector. `{}` matches each item.
|
|
89
|
+
* @param options - The order, the skip and the limit
|
|
90
|
+
* @param policy - The query policy of the caller: the procedure `find` gives the policy of the
|
|
91
|
+
* host. Without it, the storage uses its own policy. The default policy does not permit `$regex`.
|
|
92
|
+
* @returns Promise resolving to the [id, item] pairs, with copies of the items. A selector or
|
|
93
|
+
* options with a problem, or that the policy does not permit, reject with a `TypeError`.
|
|
94
|
+
*/
|
|
95
|
+
query?(selector: StorageSelector, options?: StorageQueryOptions, policy?: StorageQueryPolicy): Promise<Array<[string, T]>>;
|
|
96
|
+
/**
|
|
97
|
+
* This method starts to send the changes of the storage to an observer. Each write that changes
|
|
98
|
+
* the storage gives one batch, before the write resolves. A write that changes nothing gives no
|
|
99
|
+
* batch. A storage without this method does not tell its changes.
|
|
100
|
+
*
|
|
101
|
+
* - A set gives a `set` change with a copy of the value. A patch gives a `set` change with the new item.
|
|
102
|
+
* - A delete gives a `delete` change for each id that existed.
|
|
103
|
+
* - A clear of a storage with items gives one `clear` change.
|
|
104
|
+
* - Each change has the `origin` of the write options, or no origin.
|
|
105
|
+
*
|
|
106
|
+
* @param observer - The function that gets each batch. Its error does not change the write.
|
|
107
|
+
* @returns The function that stops the batches. A second use of it does nothing.
|
|
108
|
+
* @throws Error after `close()`
|
|
109
|
+
*/
|
|
110
|
+
observe?(observer: StorageObserver<T>): () => void;
|
|
74
111
|
/**
|
|
75
112
|
* Check if item exists.
|
|
76
113
|
*
|
|
@@ -89,36 +126,55 @@ export interface CollectionStorage<T> {
|
|
|
89
126
|
*
|
|
90
127
|
* @param id - Item identifier
|
|
91
128
|
* @param value - Item to store
|
|
129
|
+
* @param options - The origin of the write, for the observers
|
|
92
130
|
* @returns Promise resolving when operation completes
|
|
93
131
|
*/
|
|
94
|
-
set(id: string, value: T): Promise<void>;
|
|
132
|
+
set(id: string, value: T, options?: StorageWriteOptions): Promise<void>;
|
|
95
133
|
/**
|
|
96
134
|
* Delete item by ID.
|
|
97
135
|
*
|
|
98
136
|
* @param id - Item identifier
|
|
137
|
+
* @param options - The origin of the write, for the observers
|
|
99
138
|
* @returns Promise resolving to true if item was deleted, false if not found
|
|
100
139
|
*/
|
|
101
|
-
delete(id: string): Promise<boolean>;
|
|
140
|
+
delete(id: string, options?: StorageWriteOptions): Promise<boolean>;
|
|
102
141
|
/**
|
|
103
142
|
* Clear all items from collection.
|
|
104
143
|
*
|
|
144
|
+
* @param options - The origin of the write, for the observers
|
|
105
145
|
* @returns Promise resolving when operation completes
|
|
106
146
|
*/
|
|
107
|
-
clear(): Promise<void>;
|
|
147
|
+
clear(options?: StorageWriteOptions): Promise<void>;
|
|
148
|
+
/**
|
|
149
|
+
* This method changes one item in one step. The fields are a JSON Merge Patch (RFC 7386): each
|
|
150
|
+
* field replaces the field of the item, an object field merges into an object field, and `null`
|
|
151
|
+
* removes the field. Or they are the operations `{ $set, $unset }`: `$set` can set `null` and
|
|
152
|
+
* nested field paths. Two patches of one item at the same time keep the fields of each. A storage
|
|
153
|
+
* without this method cannot change an item in one step.
|
|
154
|
+
*
|
|
155
|
+
* @param id - Item identifier
|
|
156
|
+
* @param fields - The fields: an object of one form. Another value rejects with a `TypeError`.
|
|
157
|
+
* @param options - The origin of the write, for the observers
|
|
158
|
+
* @returns Promise resolving to a copy of the new item, or undefined when the id has no item. A
|
|
159
|
+
* missing id stays missing.
|
|
160
|
+
*/
|
|
161
|
+
patch?(id: string, fields: StoragePatch, options?: StorageWriteOptions): Promise<T | undefined>;
|
|
108
162
|
/**
|
|
109
163
|
* Set multiple items at once (more efficient than individual sets).
|
|
110
164
|
*
|
|
111
165
|
* @param items - Array of [id, value] tuples
|
|
166
|
+
* @param options - The origin of the write, for the observers
|
|
112
167
|
* @returns Promise resolving when operation completes
|
|
113
168
|
*/
|
|
114
|
-
setBatch(items: Array<[string, T]
|
|
169
|
+
setBatch(items: Array<[string, T]>, options?: StorageWriteOptions): Promise<void>;
|
|
115
170
|
/**
|
|
116
171
|
* Delete multiple items at once.
|
|
117
172
|
*
|
|
118
173
|
* @param ids - Array of item identifiers
|
|
174
|
+
* @param options - The origin of the write, for the observers
|
|
119
175
|
* @returns Promise resolving to number of items actually deleted
|
|
120
176
|
*/
|
|
121
|
-
deleteBatch(ids: string[]): Promise<number>;
|
|
177
|
+
deleteBatch(ids: string[], options?: StorageWriteOptions): Promise<number>;
|
|
122
178
|
/**
|
|
123
179
|
* Get multiple items by ID at once.
|
|
124
180
|
*
|
|
@@ -149,6 +205,42 @@ export interface CollectionStorage<T> {
|
|
|
149
205
|
*/
|
|
150
206
|
getMetadata?(): Promise<StorageMetadata>;
|
|
151
207
|
}
|
|
208
|
+
/** The options of a write. */
|
|
209
|
+
export interface StorageWriteOptions {
|
|
210
|
+
/**
|
|
211
|
+
* The tag of the writer. Each change that the write gives has it, so an observer knows its own
|
|
212
|
+
* writes: a sync does not send back the changes that it wrote itself.
|
|
213
|
+
*/
|
|
214
|
+
readonly origin?: string | undefined;
|
|
215
|
+
}
|
|
216
|
+
/** A change of `observe()`: an id got a value. */
|
|
217
|
+
export interface StorageSetChange<T> {
|
|
218
|
+
readonly type: "set";
|
|
219
|
+
readonly id: string;
|
|
220
|
+
/** A copy of the new item. */
|
|
221
|
+
readonly value: T;
|
|
222
|
+
/** The origin of the write, or undefined when the write had none. */
|
|
223
|
+
readonly origin?: string | undefined;
|
|
224
|
+
}
|
|
225
|
+
/** A change of `observe()`: an id lost its item. */
|
|
226
|
+
export interface StorageDeleteChange {
|
|
227
|
+
readonly type: "delete";
|
|
228
|
+
readonly id: string;
|
|
229
|
+
/** The origin of the write, or undefined when the write had none. */
|
|
230
|
+
readonly origin?: string | undefined;
|
|
231
|
+
}
|
|
232
|
+
/** A change of `observe()`: the storage lost each item. */
|
|
233
|
+
export interface StorageClearChange {
|
|
234
|
+
readonly type: "clear";
|
|
235
|
+
/** The origin of the write, or undefined when the write had none. */
|
|
236
|
+
readonly origin?: string | undefined;
|
|
237
|
+
}
|
|
238
|
+
/** One change of `observe()`. */
|
|
239
|
+
export type StorageChangeEvent<T> = StorageSetChange<T> | StorageDeleteChange | StorageClearChange;
|
|
240
|
+
/** The changes of one write, in the order of the write. The observers share it: do not change it. */
|
|
241
|
+
export type StorageChangeBatch<T> = readonly StorageChangeEvent<T>[];
|
|
242
|
+
/** The function that gets each batch of `observe()`. */
|
|
243
|
+
export type StorageObserver<T> = (changes: StorageChangeBatch<T>) => void;
|
|
152
244
|
/**
|
|
153
245
|
* The changes of a storage after a version (roadmap R28).
|
|
154
246
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../src/storage/interface.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../src/storage/interface.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* The interface of the collection storages. These storages implement it:
|
|
3
|
+
* - `InMemoryStorage` (this package): a fast storage in a `Map`, in memory only.
|
|
4
|
+
* - `ApiStorage` (`@procedures/store`): a remote collection through a `Client`.
|
|
5
|
+
* - `HybridStorage` (`@procedures/store`): a local copy that syncs with a remote, with a
|
|
6
|
+
* conflict policy and a queue of the local writes.
|
|
7
|
+
* - `MongoStorage` (`@procedures/mongo`).
|
|
3
8
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* - ApiStorage: Remote persistence via universal client
|
|
7
|
-
* - HybridStorage: Local cache + remote sync with conflict resolution
|
|
8
|
-
* - SyncedStorage: Periodic background sync with offline support
|
|
9
|
+
* `@procedures/client` declares a copy of this interface with the same shape, so that the core
|
|
10
|
+
* does not depend on this package. `SqliteStorage` (`@procedures/sqlite`) implements that copy.
|
|
9
11
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
+
* Each method gives a `Promise`. Thus the code of a caller cannot tell a local storage from a
|
|
13
|
+
* remote storage.
|
|
12
14
|
*/
|
|
13
15
|
/**
|
|
14
16
|
* @deprecated All storage methods now return Promise.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interface.js","sourceRoot":"","sources":["../../src/storage/interface.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"interface.js","sourceRoot":"","sources":["../../src/storage/interface.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAwUH;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB,CAAI,KAAiB;IACzD,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A set of listeners for the events of a storage: the change batches of `observe()`, or the status
|
|
3
|
+
* of a sync. A storage package uses it, so each storage treats its listeners in the same way.
|
|
4
|
+
*/
|
|
5
|
+
/** The function that gets the error of a listener. */
|
|
6
|
+
export type ListenerErrorHandler = (error: unknown) => void;
|
|
7
|
+
/**
|
|
8
|
+
* The default handler of the error of a listener. It throws the error again in a microtask, as an
|
|
9
|
+
* `EventTarget` reports the error of a listener. Thus the error is not lost, and the write that
|
|
10
|
+
* sent the event does not fail.
|
|
11
|
+
*/
|
|
12
|
+
export declare const reportListenerError: ListenerErrorHandler;
|
|
13
|
+
/**
|
|
14
|
+
* The listeners of one kind of event.
|
|
15
|
+
*
|
|
16
|
+
* - `emit()` gives the event to each listener, in the order of `add()`.
|
|
17
|
+
* - A listener that `add()` gets during an `emit()` gets the next event, not this one.
|
|
18
|
+
* - The error of a listener goes to the error handler. The other listeners get the event.
|
|
19
|
+
* - The same function can be added two times. Each `add()` gives its own function that removes it.
|
|
20
|
+
*/
|
|
21
|
+
export declare class StorageListeners<E> {
|
|
22
|
+
private readonly listeners;
|
|
23
|
+
private readonly onError;
|
|
24
|
+
/** @param onError - The function that gets the error of a listener. The default is `reportListenerError`. */
|
|
25
|
+
constructor(onError?: ListenerErrorHandler);
|
|
26
|
+
/** The number of listeners. */
|
|
27
|
+
get size(): number;
|
|
28
|
+
/**
|
|
29
|
+
* This method adds a listener.
|
|
30
|
+
*
|
|
31
|
+
* @returns The function that removes the listener. A second use of it does nothing.
|
|
32
|
+
*/
|
|
33
|
+
add(listener: (event: E) => void): () => void;
|
|
34
|
+
/** This method gives the event to each listener. */
|
|
35
|
+
emit(event: E): void;
|
|
36
|
+
/** This method removes each listener. */
|
|
37
|
+
clear(): void;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=listeners.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"listeners.d.ts","sourceRoot":"","sources":["../../src/storage/listeners.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,sDAAsD;AACtD,MAAM,MAAM,oBAAoB,GAAG,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;AAE5D;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,oBAIjC,CAAC;AAEF;;;;;;;GAOG;AACH,qBAAa,gBAAgB,CAAC,CAAC;IAC7B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAwD;IAClF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuB;IAE/C,6GAA6G;IAC7G,YAAY,OAAO,GAAE,oBAA0C,EAE9D;IAED,+BAA+B;IAC/B,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED;;;;OAIG;IACH,GAAG,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,GAAG,MAAM,IAAI,CAM5C;IAED,oDAAoD;IACpD,IAAI,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAUnB;IAED,yCAAyC;IACzC,KAAK,IAAI,IAAI,CAEZ;CACF"}
|