@procedures/store 0.43.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 +28 -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
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# @procedures/store
|
|
2
2
|
|
|
3
|
+
## 0.45.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- d68723a: `@procedures/store` gets the procedures of the saved programs of a tenant, and versions for each program store (AG1-B). This change is additive. Thus it is a PATCH of the client group (decision D13).
|
|
8
|
+
|
|
9
|
+
- `StoredProgram` gets the optional fields `name`, `description` and `version`. `checkStoredProgram` accepts them. The store sets `version` on each `put`.
|
|
10
|
+
- `put(program, { expectedVersion })` and `delete(key, { expectedVersion })` are optimistic writes (decision D11). A conflict rejects with `VersionConflictError` and changes nothing. `null` expects no record.
|
|
11
|
+
- The four backends test the version. `MemoryProgramStore`, `FileProgramStore` and `IndexedDbProgramStore` keep a counter. `CollectionProgramStore` uses the precondition of its storage: give the storage `versionOf: storedProgramVersion`.
|
|
12
|
+
- A `ProgramStore` of another package that ignores the options keeps working, but it gives no conflict check.
|
|
13
|
+
- `createProgramProcedures({ partition, storeFor, namespace? })` gives `programs.save`, `get`, `list` and `remove` over a store of each partition. `createProgramModule` gives them as a module. An import registers nothing.
|
|
14
|
+
- The resolver of the host decides the partition of each call, and a `null` result refuses the call with `FORBIDDEN`, reason `partition-not-allowed`. One tenant does not read the programs of another tenant.
|
|
15
|
+
- The procedures declare their effects. A read-only capability permits `get` and `list`, and refuses `save` and `remove`.
|
|
16
|
+
- Updated dependencies [fbb50fa]
|
|
17
|
+
- Updated dependencies [845902b]
|
|
18
|
+
- Updated dependencies [70b2245]
|
|
19
|
+
- @procedures/client@0.45.0
|
|
20
|
+
- @procedures/collections@0.45.0
|
|
21
|
+
|
|
22
|
+
## 0.44.0
|
|
23
|
+
|
|
24
|
+
### Patch Changes
|
|
25
|
+
|
|
26
|
+
- Updated dependencies [4dada47]
|
|
27
|
+
- Updated dependencies [19b7a25]
|
|
28
|
+
- @procedures/client@0.44.0
|
|
29
|
+
- @procedures/collections@0.44.0
|
|
30
|
+
|
|
3
31
|
## 0.43.0
|
|
4
32
|
|
|
5
33
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -72,6 +72,69 @@ Without a store, `procedure.store`, `load` and `sync` fail with the code `NOT_CO
|
|
|
72
72
|
|
|
73
73
|
A loaded program is a runtime-defined procedure, so it is data-driven: a server with an expose rule lets it call only the exposed procedures. Load programs only from a store that you trust. A host with no expose rule does not limit the calls of a program.
|
|
74
74
|
|
|
75
|
+
### The versions of a program
|
|
76
|
+
|
|
77
|
+
A stored program has the optional fields `name`, `description` and `version`. The store sets `version` on each `put`. It ignores the version of the input. The value is opaque: compare it, and give it back as `expectedVersion`.
|
|
78
|
+
|
|
79
|
+
`put(program, { expectedVersion })` and `delete(key, { expectedVersion })` are optimistic writes (decision D11):
|
|
80
|
+
|
|
81
|
+
- When the current record has another version, the write changes nothing. It rejects with `VersionConflictError` of `@procedures/client`, with the code `VERSION_CONFLICT`.
|
|
82
|
+
- An `expectedVersion` for a key without a record is a conflict.
|
|
83
|
+
- `null` expects no record, as in the collection storages. A `put` with `null` writes only a new program.
|
|
84
|
+
- Without `expectedVersion`, the last write wins, as before.
|
|
85
|
+
|
|
86
|
+
| Backend | The version |
|
|
87
|
+
|---|---|
|
|
88
|
+
| `MemoryProgramStore` | A counter of the store, a number. A deleted program does not get an old version back. |
|
|
89
|
+
| `FileProgramStore` | A counter in the field `counter` of the file, a number. The lock of the file holds the test and the write together. |
|
|
90
|
+
| `IndexedDbProgramStore` | One more than the highest version in the database, a number. The test and the write are one transaction. After a delete of the record with the highest version, a later `put` can give that version again. |
|
|
91
|
+
| `CollectionProgramStore` | A revision string, for example `"3-k2j9x0aq"`. The storage tests the version itself, with its own `versionOf`. |
|
|
92
|
+
|
|
93
|
+
Give the storage of a `CollectionProgramStore` the option `versionOf: storedProgramVersion`. A storage without `versionOf` refuses a write with `expectedVersion` (the code `PRECONDITION_NOT_SUPPORTED`).
|
|
94
|
+
|
|
95
|
+
A `ProgramStore` of another package can ignore the second argument of `put` and `delete`. It keeps working, but it gives no conflict check: its writes always win.
|
|
96
|
+
|
|
97
|
+
### The programs of a tenant
|
|
98
|
+
|
|
99
|
+
One program store has two scopes:
|
|
100
|
+
|
|
101
|
+
- The scope "host" is the store of a registry (`attachProgramStore`). `procedure.store`, `load` and `sync` use it, and `procedure.load` defines its programs in the registry.
|
|
102
|
+
- The scope "tenant" is a store of each partition. `createProgramProcedures` gives the procedures of this scope. They do not define a program in a registry. A view lists the programs, and a caller starts one with `client.chain` or `client.exec`.
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
import { createRegistry, type PartitionResolver } from "@procedures/client";
|
|
106
|
+
import { createProgramModule, MemoryProgramStore, type ProgramStore } from "@procedures/store";
|
|
107
|
+
|
|
108
|
+
// The tenant comes from the principal. The input only selects a scope under it.
|
|
109
|
+
const partition: PartitionResolver = (ctx, requested) => {
|
|
110
|
+
const tenant = ctx.principal?.claims?.["tenant"];
|
|
111
|
+
if (typeof tenant !== "string") return null;
|
|
112
|
+
return requested === undefined ? tenant : `${tenant}/${requested}`;
|
|
113
|
+
};
|
|
114
|
+
const stores = new Map<string | undefined, ProgramStore>();
|
|
115
|
+
const storeFor = (name: string | undefined): ProgramStore => {
|
|
116
|
+
if (!stores.has(name)) stores.set(name, new MemoryProgramStore());
|
|
117
|
+
return stores.get(name)!;
|
|
118
|
+
};
|
|
119
|
+
const registry = createRegistry(createProgramModule({ partition, storeFor }));
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
| Procedure | What it does | Effects |
|
|
123
|
+
|---|---|---|
|
|
124
|
+
| `programs.save` | Writes `{ path, program, name?, description?, expectedVersion? }`. It gives the stored record with its new version. | It reads and writes `programs`. |
|
|
125
|
+
| `programs.get` | Reads `{ path }` or `{ key }`. A missing program is the error `NOT_FOUND`. | It reads `programs`, and writes nothing. |
|
|
126
|
+
| `programs.list` | Reads the programs of `{ prefix? }`, sorted by key. | It reads `programs`, and writes nothing. |
|
|
127
|
+
| `programs.remove` | Deletes `{ path, expectedVersion? }`. It gives `{ removed, key }`. | It writes `programs`, and it is destructive. |
|
|
128
|
+
|
|
129
|
+
- Each procedure takes an optional `partition`, which only selects. The resolver gets the context and the requested partition, and it decides.
|
|
130
|
+
- A `null` result of the resolver refuses the call with `FORBIDDEN`, reason `partition-not-allowed`, as `createCollectionProcedures` does.
|
|
131
|
+
- The function `storeFor` gives the store of a partition. Give the same store for the same partition each time, and a store of its own to each tenant. Thus one tenant does not read the programs of another tenant.
|
|
132
|
+
- `programs.save` checks the record with `checkStoredProgram`, and the program with the check of `procedure.define`. It takes the program as data (the tag `raw-input`), so the save runs nothing of it.
|
|
133
|
+
- The option `namespace` changes the first segments of the paths. The default is `["programs"]`. `createProgramProcedures` gives the procedures as a list, and `createProgramModule` gives them as a module.
|
|
134
|
+
- An import registers nothing. The entry is the package root only, because a host serves the programs. A browser uses the procedures of its server.
|
|
135
|
+
|
|
136
|
+
Under a read-only capability (`{ effects: { writes: [] } }`, or `hostPolicy({ readOnly: true })`), `programs.get` and `programs.list` work. `programs.save` and `programs.remove` are refused with `FORBIDDEN`, reason `effects-exceeded`. A saved program that a read-only caller starts with `client.chain` gets the same refusal at each step that writes.
|
|
137
|
+
|
|
75
138
|
## The storage layer
|
|
76
139
|
|
|
77
140
|
These parts moved here from `@procedures/client` (roadmap 3.2):
|