@procedures/store 0.44.0 → 0.45.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 +19 -0
- package/README.md +63 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/indexeddb/idb.d.ts +2 -1
- package/dist/indexeddb/idb.d.ts.map +1 -1
- package/dist/indexeddb/idb.js +2 -1
- package/dist/indexeddb/idb.js.map +1 -1
- package/dist/indexeddb/program-store.d.ts +7 -3
- package/dist/indexeddb/program-store.d.ts.map +1 -1
- package/dist/indexeddb/program-store.js +28 -6
- package/dist/indexeddb/program-store.js.map +1 -1
- package/dist/indexeddb.d.ts +1 -1
- package/dist/indexeddb.d.ts.map +1 -1
- package/dist/program-store/collection.d.ts +13 -4
- package/dist/program-store/collection.d.ts.map +1 -1
- package/dist/program-store/collection.js +29 -6
- package/dist/program-store/collection.js.map +1 -1
- package/dist/program-store/file.d.ts +7 -3
- package/dist/program-store/file.d.ts.map +1 -1
- package/dist/program-store/file.js +18 -5
- package/dist/program-store/file.js.map +1 -1
- package/dist/program-store/index.d.ts +4 -2
- package/dist/program-store/index.d.ts.map +1 -1
- package/dist/program-store/index.js +2 -1
- package/dist/program-store/index.js.map +1 -1
- package/dist/program-store/memory.d.ts +7 -4
- package/dist/program-store/memory.d.ts.map +1 -1
- package/dist/program-store/memory.js +12 -5
- package/dist/program-store/memory.js.map +1 -1
- package/dist/program-store/procedures.d.ts +6 -1
- package/dist/program-store/procedures.d.ts.map +1 -1
- package/dist/program-store/procedures.js +4 -2
- package/dist/program-store/procedures.js.map +1 -1
- package/dist/program-store/programs.d.ts +110 -0
- package/dist/program-store/programs.d.ts.map +1 -0
- package/dist/program-store/programs.js +202 -0
- package/dist/program-store/programs.js.map +1 -0
- package/dist/program-store/types.d.ts +53 -4
- package/dist/program-store/types.d.ts.map +1 -1
- package/dist/program-store/types.js +51 -0
- package/dist/program-store/types.js.map +1 -1
- package/package.json +4 -4
- package/src/indexeddb/idb.ts +3 -2
- package/src/indexeddb/program-store.ts +36 -6
- package/src/indexeddb.ts +1 -1
- package/src/program-store/collection.ts +39 -6
- package/src/program-store/file.ts +30 -5
- package/src/program-store/index.ts +12 -2
- package/src/program-store/memory.ts +20 -5
- package/src/program-store/procedures.ts +4 -2
- package/src/program-store/programs.ts +307 -0
- package/src/program-store/types.ts +85 -4
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import type { AggregationDefinition, ProcedurePath } from "@procedures/client";
|
|
11
|
+
import { versionConflict, VersionConflictError, type CurrentItem } from "@procedures/client/query";
|
|
11
12
|
|
|
12
13
|
/** One stored program. */
|
|
13
14
|
export interface StoredProgram {
|
|
@@ -21,11 +22,34 @@ export interface StoredProgram {
|
|
|
21
22
|
metadata?: Record<string, unknown> | undefined;
|
|
22
23
|
/** When the store got this version, in milliseconds since 1970. */
|
|
23
24
|
storedAt: number;
|
|
25
|
+
/** The name of the program, for a view or a tool that lists the programs. */
|
|
26
|
+
name?: string | undefined;
|
|
27
|
+
/** The description of the program, in words. */
|
|
28
|
+
description?: string | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* The version of the record. The store sets it on each put, and ignores the version of the
|
|
31
|
+
* input. The value is opaque: a caller compares it, and gives it back as `expectedVersion`.
|
|
32
|
+
*/
|
|
33
|
+
version?: string | number | undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** The options of a write of a program store (decision D11). */
|
|
37
|
+
export interface ProgramWriteOptions {
|
|
38
|
+
/**
|
|
39
|
+
* The version that the write expects for the current record. `null` expects no record. When the
|
|
40
|
+
* current record has another version, the write changes nothing and rejects with
|
|
41
|
+
* `VersionConflictError`. Absent: no precondition, and the last write wins.
|
|
42
|
+
*/
|
|
43
|
+
readonly expectedVersion?: string | number | null | undefined;
|
|
24
44
|
}
|
|
25
45
|
|
|
26
46
|
/**
|
|
27
47
|
* A store of programs. Each method gives a Promise: a caller cannot tell a local store from a
|
|
28
48
|
* remote one.
|
|
49
|
+
*
|
|
50
|
+
* The four backends of this package set the `version` of each record and test `expectedVersion`.
|
|
51
|
+
* A store of another package that ignores the options of `put` and `delete` keeps working, but it
|
|
52
|
+
* gives no conflict check: its writes always win.
|
|
29
53
|
*/
|
|
30
54
|
export interface ProgramStore {
|
|
31
55
|
/** The kind of the backend: "memory", "file", "collection" or another name. */
|
|
@@ -34,14 +58,62 @@ export interface ProgramStore {
|
|
|
34
58
|
get(key: string): Promise<StoredProgram | undefined>;
|
|
35
59
|
/** The programs, sorted by key. With a prefix, only the key and the keys under it (`prefix.x`). */
|
|
36
60
|
list(prefix?: string): Promise<StoredProgram[]>;
|
|
37
|
-
/**
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Write a program. A program of the same key is replaced. The store sets a new `version`. With
|
|
63
|
+
* `expectedVersion`, a conflict rejects with `VersionConflictError` and changes nothing.
|
|
64
|
+
*/
|
|
65
|
+
put(program: StoredProgram, options?: ProgramWriteOptions): Promise<void>;
|
|
66
|
+
/**
|
|
67
|
+
* Delete the program of a key. True when it existed. With `expectedVersion`, a conflict rejects
|
|
68
|
+
* with `VersionConflictError` and changes nothing.
|
|
69
|
+
*/
|
|
70
|
+
delete(key: string, options?: ProgramWriteOptions): Promise<boolean>;
|
|
41
71
|
/** Release the resources of the store. */
|
|
42
72
|
close?(): Promise<void>;
|
|
43
73
|
}
|
|
44
74
|
|
|
75
|
+
/**
|
|
76
|
+
* This function gives the version of a record as a string, or undefined for a record without a
|
|
77
|
+
* version. Give it as the `versionOf` of the collection storage of a `CollectionProgramStore`.
|
|
78
|
+
*/
|
|
79
|
+
export function storedProgramVersion(program: StoredProgram): string | undefined {
|
|
80
|
+
return program.version === undefined ? undefined : String(program.version);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* This function gives an expected version in the form of the collection storages: a string, or
|
|
85
|
+
* `null` for no record. A number becomes its string, so `3` and `"3"` are the same version.
|
|
86
|
+
*/
|
|
87
|
+
export function expectedVersionOf(options: ProgramWriteOptions | undefined): string | null | undefined {
|
|
88
|
+
const expected = options?.expectedVersion;
|
|
89
|
+
return expected === undefined || expected === null ? expected : String(expected);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* This function tests the precondition of a write against the current record. It throws
|
|
94
|
+
* `VersionConflictError` with a copy of the current record when the version is not the expected
|
|
95
|
+
* version. Without `expectedVersion`, it does nothing.
|
|
96
|
+
*/
|
|
97
|
+
export function checkProgramVersion(key: string, current: StoredProgram | undefined, options: ProgramWriteOptions | undefined): void {
|
|
98
|
+
const expected = expectedVersionOf(options);
|
|
99
|
+
if (expected === undefined) return;
|
|
100
|
+
const state: CurrentItem<StoredProgram> = current === undefined ? { found: false } : { found: true, value: structuredClone(current) };
|
|
101
|
+
const conflict = versionConflict(key, expected, state, storedProgramVersion);
|
|
102
|
+
if (conflict) throw new VersionConflictError([conflict]);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* This function gives the next version of a counter: one more than the counter and than each
|
|
107
|
+
* numeric version of the records. A record of an older release has no version: it counts as 0.
|
|
108
|
+
*/
|
|
109
|
+
export function nextCounterVersion(counter: number, programs: Iterable<StoredProgram>): number {
|
|
110
|
+
let highest = Number.isSafeInteger(counter) && counter > 0 ? counter : 0;
|
|
111
|
+
for (const program of programs) {
|
|
112
|
+
if (typeof program.version === "number" && Number.isSafeInteger(program.version) && program.version > highest) highest = program.version;
|
|
113
|
+
}
|
|
114
|
+
return highest + 1;
|
|
115
|
+
}
|
|
116
|
+
|
|
45
117
|
/** True when a key is the prefix or under it (`a.b` for the prefix `a`). */
|
|
46
118
|
export function keyMatches(key: string, prefix: string | undefined): boolean {
|
|
47
119
|
return prefix === undefined || prefix === "" || key === prefix || key.startsWith(`${prefix}.`);
|
|
@@ -75,5 +147,14 @@ export function checkStoredProgram(value: unknown): StoredProgram {
|
|
|
75
147
|
if (typeof record.storedAt !== "number" || !Number.isFinite(record.storedAt)) {
|
|
76
148
|
throw new TypeError(`The program of ${record.key} needs storedAt, a time in milliseconds`);
|
|
77
149
|
}
|
|
150
|
+
for (const field of ["name", "description"] as const) {
|
|
151
|
+
if (record[field] !== undefined && typeof record[field] !== "string") {
|
|
152
|
+
throw new TypeError(`The ${field} of the program ${record.key} must be a string`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const version = record.version;
|
|
156
|
+
if (version !== undefined && typeof version !== "string" && !(typeof version === "number" && Number.isFinite(version))) {
|
|
157
|
+
throw new TypeError(`The version of the program ${record.key} must be a string or a finite number`);
|
|
158
|
+
}
|
|
78
159
|
return record as StoredProgram;
|
|
79
160
|
}
|