@procedures/client 0.7.0 → 0.8.1
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 +107 -0
- package/README.md +115 -11
- package/dist/.tsbuildinfo +1 -1
- package/dist/adapters/http/client/transport.d.ts +4 -0
- package/dist/adapters/http/client/transport.d.ts.map +1 -1
- package/dist/adapters/http/client/transport.js +11 -1
- package/dist/adapters/http/client/transport.js.map +1 -1
- package/dist/adapters/http/client/types.d.ts +19 -0
- package/dist/adapters/http/client/types.d.ts.map +1 -1
- package/dist/adapters/http/server/cors.d.ts +64 -0
- package/dist/adapters/http/server/cors.d.ts.map +1 -0
- package/dist/adapters/http/server/cors.js +113 -0
- package/dist/adapters/http/server/cors.js.map +1 -0
- package/dist/adapters/http/server/handler.d.ts +136 -0
- package/dist/adapters/http/server/handler.d.ts.map +1 -0
- package/dist/adapters/http/server/handler.js +495 -0
- package/dist/adapters/http/server/handler.js.map +1 -0
- package/dist/adapters/http/server/strategies.d.ts +14 -5
- package/dist/adapters/http/server/strategies.d.ts.map +1 -1
- package/dist/adapters/http/server/strategies.js.map +1 -1
- package/dist/adapters/http/server/transport.d.ts +36 -46
- package/dist/adapters/http/server/transport.d.ts.map +1 -1
- package/dist/adapters/http/server/transport.js +58 -433
- package/dist/adapters/http/server/transport.js.map +1 -1
- package/dist/adapters/http/server/types.d.ts +13 -0
- package/dist/adapters/http/server/types.d.ts.map +1 -1
- package/dist/adapters/websocket/client/transport.d.ts +7 -3
- package/dist/adapters/websocket/client/transport.d.ts.map +1 -1
- package/dist/adapters/websocket/client/transport.js +9 -3
- package/dist/adapters/websocket/client/transport.js.map +1 -1
- package/dist/adapters/websocket/server/transport.d.ts +56 -1
- package/dist/adapters/websocket/server/transport.d.ts.map +1 -1
- package/dist/adapters/websocket/server/transport.js +174 -17
- package/dist/adapters/websocket/server/transport.js.map +1 -1
- package/dist/adapters/websocket/server/types.d.ts +10 -3
- package/dist/adapters/websocket/server/types.d.ts.map +1 -1
- package/dist/adapters/websocket/server/upgrade-router.d.ts +28 -0
- package/dist/adapters/websocket/server/upgrade-router.d.ts.map +1 -0
- package/dist/adapters/websocket/server/upgrade-router.js +62 -0
- package/dist/adapters/websocket/server/upgrade-router.js.map +1 -0
- package/dist/adapters/websocket/server/upgrade.d.ts +68 -0
- package/dist/adapters/websocket/server/upgrade.d.ts.map +1 -0
- package/dist/adapters/websocket/server/upgrade.js +49 -0
- package/dist/adapters/websocket/server/upgrade.js.map +1 -0
- package/dist/adapters/websocket/shared/rpc.d.ts +2 -0
- package/dist/adapters/websocket/shared/rpc.d.ts.map +1 -1
- package/dist/adapters/websocket/shared/rpc.js +8 -2
- package/dist/adapters/websocket/shared/rpc.js.map +1 -1
- package/dist/browser.d.ts +14 -6
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +12 -3
- package/dist/browser.js.map +1 -1
- package/dist/client/client.d.ts +43 -0
- package/dist/client/client.d.ts.map +1 -1
- package/dist/client/client.js +69 -1
- package/dist/client/client.js.map +1 -1
- package/dist/client/errors/registry.d.ts.map +1 -1
- package/dist/client/errors/registry.js +1 -0
- package/dist/client/errors/registry.js.map +1 -1
- package/dist/client/middleware/retry.d.ts +4 -0
- package/dist/client/middleware/retry.d.ts.map +1 -1
- package/dist/client/middleware/retry.js +86 -78
- package/dist/client/middleware/retry.js.map +1 -1
- package/dist/events/bus.d.ts +32 -14
- package/dist/events/bus.d.ts.map +1 -1
- package/dist/events/bus.js +119 -65
- package/dist/events/bus.js.map +1 -1
- package/dist/events/index.d.ts +3 -2
- package/dist/events/index.d.ts.map +1 -1
- package/dist/events/index.js.map +1 -1
- package/dist/events/publisher.d.ts +14 -0
- package/dist/events/publisher.d.ts.map +1 -1
- package/dist/events/publisher.js +62 -10
- package/dist/events/publisher.js.map +1 -1
- package/dist/events/remote.d.ts +80 -18
- package/dist/events/remote.d.ts.map +1 -1
- package/dist/events/remote.js +448 -58
- package/dist/events/remote.js.map +1 -1
- package/dist/events/rows.d.ts +31 -0
- package/dist/events/rows.d.ts.map +1 -0
- package/dist/events/rows.js +39 -0
- package/dist/events/rows.js.map +1 -0
- package/dist/events/types.d.ts +13 -2
- package/dist/events/types.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/node.d.ts +14 -0
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +9 -0
- package/dist/node.js.map +1 -1
- package/dist/procedures/caller.d.ts +33 -0
- package/dist/procedures/caller.d.ts.map +1 -0
- package/dist/procedures/caller.js +27 -0
- package/dist/procedures/caller.js.map +1 -0
- package/dist/procedures/capability.d.ts +6 -0
- package/dist/procedures/capability.d.ts.map +1 -1
- package/dist/procedures/capability.js +25 -0
- package/dist/procedures/capability.js.map +1 -1
- package/dist/procedures/collection/changes.d.ts +94 -0
- package/dist/procedures/collection/changes.d.ts.map +1 -0
- package/dist/procedures/collection/changes.js +182 -0
- package/dist/procedures/collection/changes.js.map +1 -0
- package/dist/procedures/collection/index.d.ts +8 -3
- package/dist/procedures/collection/index.d.ts.map +1 -1
- package/dist/procedures/collection/index.js +6 -1
- package/dist/procedures/collection/index.js.map +1 -1
- package/dist/procedures/collection/procedures.d.ts +39 -2
- package/dist/procedures/collection/procedures.d.ts.map +1 -1
- package/dist/procedures/collection/procedures.js +131 -8
- package/dist/procedures/collection/procedures.js.map +1 -1
- package/dist/procedures/collection/query.d.ts +246 -0
- package/dist/procedures/collection/query.d.ts.map +1 -0
- package/dist/procedures/collection/query.js +854 -0
- package/dist/procedures/collection/query.js.map +1 -0
- package/dist/procedures/collection/schemas.d.ts +59 -0
- package/dist/procedures/collection/schemas.d.ts.map +1 -1
- package/dist/procedures/collection/schemas.js +172 -6
- package/dist/procedures/collection/schemas.js.map +1 -1
- package/dist/procedures/collection/storage.d.ts +64 -6
- package/dist/procedures/collection/storage.d.ts.map +1 -1
- package/dist/procedures/collection/storage.js +23 -0
- package/dist/procedures/collection/storage.js.map +1 -1
- package/dist/procedures/elicit.d.ts +190 -0
- package/dist/procedures/elicit.d.ts.map +1 -0
- package/dist/procedures/elicit.js +127 -0
- package/dist/procedures/elicit.js.map +1 -0
- package/dist/procedures/hints.d.ts +43 -0
- package/dist/procedures/hints.d.ts.map +1 -0
- package/dist/procedures/hints.js +74 -0
- package/dist/procedures/hints.js.map +1 -0
- package/dist/procedures/index.d.ts +12 -3
- package/dist/procedures/index.d.ts.map +1 -1
- package/dist/procedures/index.js +7 -1
- package/dist/procedures/index.js.map +1 -1
- package/dist/procedures/invoke.d.ts +20 -0
- package/dist/procedures/invoke.d.ts.map +1 -1
- package/dist/procedures/invoke.js +16 -0
- package/dist/procedures/invoke.js.map +1 -1
- package/dist/procedures/progress.d.ts +24 -0
- package/dist/procedures/progress.d.ts.map +1 -0
- package/dist/procedures/progress.js +11 -0
- package/dist/procedures/progress.js.map +1 -0
- package/dist/procedures/registry.d.ts.map +1 -1
- package/dist/procedures/registry.js +5 -0
- package/dist/procedures/registry.js.map +1 -1
- package/dist/procedures/types.d.ts +92 -0
- package/dist/procedures/types.d.ts.map +1 -1
- package/dist/procedures/types.js.map +1 -1
- package/dist/procedures/untrusted.d.ts +28 -0
- package/dist/procedures/untrusted.d.ts.map +1 -0
- package/dist/procedures/untrusted.js +32 -0
- package/dist/procedures/untrusted.js.map +1 -0
- package/dist/protocol.d.ts +1 -1
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +1 -0
- package/dist/protocol.js.map +1 -1
- package/dist/server/browser-guard.d.ts +12 -2
- package/dist/server/browser-guard.d.ts.map +1 -1
- package/dist/server/browser-guard.js +43 -6
- package/dist/server/browser-guard.js.map +1 -1
- package/dist/server/host-policy.d.ts +10 -1
- package/dist/server/host-policy.d.ts.map +1 -1
- package/dist/server/host-policy.js +5 -0
- package/dist/server/host-policy.js.map +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/procedure-server.d.ts +75 -9
- package/dist/server/procedure-server.d.ts.map +1 -1
- package/dist/server/procedure-server.js +151 -17
- package/dist/server/procedure-server.js.map +1 -1
- package/dist/server/request-auth.d.ts +51 -4
- package/dist/server/request-auth.d.ts.map +1 -1
- package/dist/server/request-auth.js +116 -5
- package/dist/server/request-auth.js.map +1 -1
- package/dist/server/types.d.ts +73 -5
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/types.js.map +1 -1
- package/dist/timers.d.ts +5 -0
- package/dist/timers.d.ts.map +1 -1
- package/dist/timers.js +3 -1
- package/dist/timers.js.map +1 -1
- package/package.json +5 -1
- package/src/adapters/http/client/transport.ts +11 -1
- package/src/adapters/http/client/types.ts +22 -0
- package/src/adapters/http/server/cors.ts +144 -0
- package/src/adapters/http/server/handler.ts +624 -0
- package/src/adapters/http/server/strategies.ts +16 -6
- package/src/adapters/http/server/transport.ts +66 -479
- package/src/adapters/http/server/types.ts +15 -0
- package/src/adapters/websocket/client/transport.ts +9 -3
- package/src/adapters/websocket/server/transport.ts +189 -18
- package/src/adapters/websocket/server/types.ts +11 -3
- package/src/adapters/websocket/server/upgrade-router.ts +77 -0
- package/src/adapters/websocket/server/upgrade.ts +88 -0
- package/src/adapters/websocket/shared/rpc.ts +9 -2
- package/src/browser.ts +85 -2
- package/src/client/client.ts +83 -1
- package/src/client/errors/registry.ts +1 -0
- package/src/client/middleware/retry.ts +89 -78
- package/src/events/bus.ts +141 -73
- package/src/events/index.ts +3 -0
- package/src/events/publisher.ts +71 -10
- package/src/events/remote.ts +575 -73
- package/src/events/rows.ts +48 -0
- package/src/events/types.ts +15 -2
- package/src/index.ts +1 -1
- package/src/node.ts +21 -0
- package/src/procedures/caller.ts +45 -0
- package/src/procedures/capability.ts +21 -0
- package/src/procedures/collection/changes.ts +245 -0
- package/src/procedures/collection/index.ts +56 -1
- package/src/procedures/collection/procedures.ts +185 -7
- package/src/procedures/collection/query.ts +1009 -0
- package/src/procedures/collection/schemas.ts +233 -7
- package/src/procedures/collection/storage.ts +84 -6
- package/src/procedures/elicit.ts +320 -0
- package/src/procedures/hints.ts +91 -0
- package/src/procedures/index.ts +82 -2
- package/src/procedures/invoke.ts +31 -0
- package/src/procedures/progress.ts +25 -0
- package/src/procedures/registry.ts +4 -0
- package/src/procedures/types.ts +102 -0
- package/src/procedures/untrusted.ts +50 -0
- package/src/protocol.ts +3 -1
- package/src/server/browser-guard.ts +48 -6
- package/src/server/host-policy.ts +13 -1
- package/src/server/index.ts +3 -0
- package/src/server/procedure-server.ts +185 -20
- package/src/server/request-auth.ts +128 -6
- package/src/server/types.ts +82 -5
- package/src/timers.ts +8 -1
|
@@ -20,6 +20,8 @@ import {
|
|
|
20
20
|
getAllOutputSchema,
|
|
21
21
|
entriesInputSchema,
|
|
22
22
|
entriesOutputSchema,
|
|
23
|
+
idsInputSchema,
|
|
24
|
+
idsOutputSchema,
|
|
23
25
|
changesInputSchema,
|
|
24
26
|
changesOutputSchema,
|
|
25
27
|
sizeInputSchema,
|
|
@@ -32,6 +34,12 @@ import {
|
|
|
32
34
|
setBatchOutputSchema,
|
|
33
35
|
deleteBatchInputSchema,
|
|
34
36
|
deleteBatchOutputSchema,
|
|
37
|
+
findInputSchemaOf,
|
|
38
|
+
findOutputSchema,
|
|
39
|
+
patchInputSchema,
|
|
40
|
+
patchOutputSchema,
|
|
41
|
+
type FindInput,
|
|
42
|
+
type PatchInput,
|
|
35
43
|
type GetInput,
|
|
36
44
|
type GetFoundOutput,
|
|
37
45
|
type SetInput,
|
|
@@ -39,6 +47,7 @@ import {
|
|
|
39
47
|
type HasInput,
|
|
40
48
|
type GetAllInput,
|
|
41
49
|
type EntriesInput,
|
|
50
|
+
type IdsInput,
|
|
42
51
|
type ChangesInput,
|
|
43
52
|
type ChangesOutput,
|
|
44
53
|
type SizeInput,
|
|
@@ -47,6 +56,9 @@ import {
|
|
|
47
56
|
type SetBatchInput,
|
|
48
57
|
type DeleteBatchInput,
|
|
49
58
|
} from "./schemas.js";
|
|
59
|
+
import { DEFAULT_FIND_LIMIT, applyPatch, queryEntries, type StoragePatch, type StorageQueryOptions, type StorageQueryPolicy } from "./query.js";
|
|
60
|
+
import type { CollectionStorage, StorageWriteOptions } from "./storage.js";
|
|
61
|
+
import { collectionChangeTopic, startChangeChain, writeWithChangeEvent } from "./changes.js";
|
|
50
62
|
|
|
51
63
|
// =============================================================================
|
|
52
64
|
// Handler Factory
|
|
@@ -74,6 +86,93 @@ function getStorage<T>(ctx: ProcedureContext): ReturnType<NonNullable<ProcedureC
|
|
|
74
86
|
return ctx.repository.getStorage<T>(collection);
|
|
75
87
|
}
|
|
76
88
|
|
|
89
|
+
/** The options of a query from the input of `find`. */
|
|
90
|
+
function queryOptionsOf(input: FindInput, policy: StorageQueryPolicy): StorageQueryOptions {
|
|
91
|
+
// A find without a limit gets the maxLimit of the policy (roadmap CLI-14)
|
|
92
|
+
const limit = input.limit ?? (Number.isFinite(policy.maxLimit) ? policy.maxLimit : undefined);
|
|
93
|
+
return {
|
|
94
|
+
...(input.sort !== undefined && { sort: input.sort }),
|
|
95
|
+
...(input.skip !== undefined && { skip: input.skip }),
|
|
96
|
+
...(limit !== undefined && { limit }),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** The options of the change events of the collection procedures (roadmap CLI-15). */
|
|
101
|
+
export interface CollectionChangeEventsOptions {
|
|
102
|
+
/**
|
|
103
|
+
* The topic of the change events of a call, or undefined for no event. The partition of a
|
|
104
|
+
* collection (roadmap CLI-17) goes into the topic here, for example from the scope of the call.
|
|
105
|
+
* @default the topic `collections.<name>` of `collectionChangeTopic()`
|
|
106
|
+
*/
|
|
107
|
+
topic?: ((collection: string, ctx: ProcedureContext) => string | undefined) | undefined;
|
|
108
|
+
/**
|
|
109
|
+
* True: the events also carry the values of the changed items. Each row `{ id, value }` passes
|
|
110
|
+
* the output filter of the host (`admitItem`) for each subscriber, and a refused row stays out of
|
|
111
|
+
* that event: its id goes to `withheld`. Without it, the events carry only the ids and the
|
|
112
|
+
* versions, and a subscriber reads the values with `changes`.
|
|
113
|
+
* @default false
|
|
114
|
+
*/
|
|
115
|
+
values?: boolean | undefined;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** The options of the collection procedures. */
|
|
119
|
+
export interface CollectionProceduresOptions {
|
|
120
|
+
/**
|
|
121
|
+
* The query policy of `find`: the operators, and the limits of a selector and of the result. A
|
|
122
|
+
* selector or a limit that the policy does not permit is a `VALIDATION_ERROR`. The policy also
|
|
123
|
+
* goes to `query()` of the storage. `$regex` is off unless the policy has it in `operators`.
|
|
124
|
+
* @default DEFAULT_QUERY_POLICY, with `maxLimit` DEFAULT_FIND_LIMIT (1000)
|
|
125
|
+
*/
|
|
126
|
+
queryPolicy?: StorageQueryPolicy | undefined;
|
|
127
|
+
/**
|
|
128
|
+
* The change events of the writes (roadmap CLI-15). Each write publishes a
|
|
129
|
+
* `CollectionChangeEvent` on the bus of its context, while a subscriber listens to the topic.
|
|
130
|
+
* The event has the ids and the versions, and the values only with `values`. False: the
|
|
131
|
+
* procedures publish no event.
|
|
132
|
+
* @default true, with the topic `collections.<name>` and no values
|
|
133
|
+
*/
|
|
134
|
+
changeEvents?: boolean | CollectionChangeEventsOptions | undefined;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** The write options of an input with an origin. */
|
|
138
|
+
function writeOptionsOf(input: { origin?: string }): StorageWriteOptions | undefined {
|
|
139
|
+
return input.origin === undefined ? undefined : { origin: input.origin };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* The patches of a storage without `patch()` that run, for each id. A patch of an id waits for the
|
|
144
|
+
* patch before it, so two patches of one process keep each field. A write of another writer
|
|
145
|
+
* between the read and the write of a patch is lost: give the storage a `patch()` for one step.
|
|
146
|
+
*/
|
|
147
|
+
const PATCHES = new WeakMap<object, Map<string, Promise<unknown>>>();
|
|
148
|
+
|
|
149
|
+
/** This function patches an item of a storage without `patch()`: a read, a merge and a write. */
|
|
150
|
+
function patchInParts(storage: CollectionStorage<unknown>, id: string, fields: StoragePatch, options?: StorageWriteOptions): Promise<unknown> {
|
|
151
|
+
let running = PATCHES.get(storage);
|
|
152
|
+
if (!running) {
|
|
153
|
+
running = new Map();
|
|
154
|
+
PATCHES.set(storage, running);
|
|
155
|
+
}
|
|
156
|
+
const queue = running;
|
|
157
|
+
const before = queue.get(id) ?? Promise.resolve();
|
|
158
|
+
const patch = before.then(async () => {
|
|
159
|
+
const current = await storage.get(id);
|
|
160
|
+
if (current === undefined) return undefined;
|
|
161
|
+
const next = applyPatch(current, fields);
|
|
162
|
+
await storage.set(id, next, options);
|
|
163
|
+
return next;
|
|
164
|
+
});
|
|
165
|
+
const settled = patch.then(
|
|
166
|
+
() => undefined,
|
|
167
|
+
() => undefined,
|
|
168
|
+
);
|
|
169
|
+
queue.set(id, settled);
|
|
170
|
+
void settled.then(() => {
|
|
171
|
+
if (queue.get(id) === settled) queue.delete(id);
|
|
172
|
+
});
|
|
173
|
+
return patch;
|
|
174
|
+
}
|
|
175
|
+
|
|
77
176
|
// =============================================================================
|
|
78
177
|
// Collection Procedure Definitions
|
|
79
178
|
// =============================================================================
|
|
@@ -82,6 +181,7 @@ function getStorage<T>(ctx: ProcedureContext): ReturnType<NonNullable<ProcedureC
|
|
|
82
181
|
* Create collection procedures for a specific collection name.
|
|
83
182
|
*
|
|
84
183
|
* @param collectionName - Name of the collection
|
|
184
|
+
* @param options - The query policy of `find`
|
|
85
185
|
* @returns Array of procedure definitions
|
|
86
186
|
*
|
|
87
187
|
* @example
|
|
@@ -90,10 +190,21 @@ function getStorage<T>(ctx: ProcedureContext): ReturnType<NonNullable<ProcedureC
|
|
|
90
190
|
* registry.registerAll(userProcedures);
|
|
91
191
|
* ```
|
|
92
192
|
*/
|
|
93
|
-
export function createCollectionProcedures(collectionName: string): AnyProcedure[] {
|
|
193
|
+
export function createCollectionProcedures(collectionName: string, options: CollectionProceduresOptions = {}): AnyProcedure[] {
|
|
94
194
|
const basePath = ["collections", collectionName];
|
|
195
|
+
// A find of a caller gets a limit: DEFAULT_FIND_LIMIT, unless the policy gives its own maxLimit
|
|
196
|
+
const queryPolicy: StorageQueryPolicy = { ...options.queryPolicy, maxLimit: options.queryPolicy?.maxLimit ?? DEFAULT_FIND_LIMIT };
|
|
95
197
|
// Each collection is a resource of its own (CAPABILITIES-2026-10): a capability can permit one
|
|
96
198
|
const resource = `collection:${collectionName}`;
|
|
199
|
+
// The topic of the change events of a call (roadmap CLI-15). CLI-17 adds the partition with the option topic.
|
|
200
|
+
const events = options.changeEvents ?? true;
|
|
201
|
+
const topicRule: ((collection: string, ctx: ProcedureContext) => string | undefined) | undefined =
|
|
202
|
+
events === false ? undefined : (events !== true && events.topic) || ((name: string) => collectionChangeTopic(name));
|
|
203
|
+
const topicOf = (ctx: ProcedureContext): string | undefined => topicRule?.(collectionName, ctx);
|
|
204
|
+
const withValues = events !== false && events !== true && events.values === true;
|
|
205
|
+
/** A write, and then its change event on the bus of the context. */
|
|
206
|
+
const changing = <R>(ctx: ProcedureContext, storage: CollectionStorage<unknown>, ids: readonly string[] | "*", origin: string | undefined, write: () => Promise<R>): Promise<R> =>
|
|
207
|
+
writeWithChangeEvent(ctx.bus, storage, topicRule ? topicOf(ctx) : undefined, ids, origin, write, withValues);
|
|
97
208
|
|
|
98
209
|
return [
|
|
99
210
|
// Get
|
|
@@ -128,7 +239,7 @@ export function createCollectionProcedures(collectionName: string): AnyProcedure
|
|
|
128
239
|
},
|
|
129
240
|
handler: async (input: SetInput, ctx: ProcedureContext) => {
|
|
130
241
|
const storage = getStorage(ctx);
|
|
131
|
-
await storage.set(input.id, input.value);
|
|
242
|
+
await changing(ctx, storage, [input.id], input.origin, () => storage.set(input.id, input.value, writeOptionsOf(input)));
|
|
132
243
|
},
|
|
133
244
|
}),
|
|
134
245
|
|
|
@@ -144,7 +255,7 @@ export function createCollectionProcedures(collectionName: string): AnyProcedure
|
|
|
144
255
|
},
|
|
145
256
|
handler: async (input: DeleteInput, ctx: ProcedureContext) => {
|
|
146
257
|
const storage = getStorage(ctx);
|
|
147
|
-
return storage.delete(input.id);
|
|
258
|
+
return changing(ctx, storage, [input.id], input.origin, () => storage.delete(input.id, writeOptionsOf(input)));
|
|
148
259
|
},
|
|
149
260
|
}),
|
|
150
261
|
|
|
@@ -199,6 +310,25 @@ export function createCollectionProcedures(collectionName: string): AnyProcedure
|
|
|
199
310
|
},
|
|
200
311
|
}),
|
|
201
312
|
|
|
313
|
+
// Ids: the ids of the items without the values, for a full sync
|
|
314
|
+
defineProcedure({
|
|
315
|
+
path: [...basePath, "ids"],
|
|
316
|
+
input: idsInputSchema,
|
|
317
|
+
output: idsOutputSchema,
|
|
318
|
+
metadata: {
|
|
319
|
+
effects: { reads: [resource], writes: [] },
|
|
320
|
+
description: `Get the ids of the items of ${collectionName}, without the values`,
|
|
321
|
+
tags: ["collections", collectionName, "read"],
|
|
322
|
+
},
|
|
323
|
+
handler: async (_input: IdsInput, ctx: ProcedureContext) => {
|
|
324
|
+
const storage = getStorage(ctx);
|
|
325
|
+
if (storage.getIds) return storage.getIds();
|
|
326
|
+
// A storage without getIds() gives its entries: the values stay on the server
|
|
327
|
+
if (storage.getEntries) return (await storage.getEntries()).map(([id]) => id);
|
|
328
|
+
throw new Error(`The storage of ${collectionName} cannot give the ids of its items`);
|
|
329
|
+
},
|
|
330
|
+
}),
|
|
331
|
+
|
|
202
332
|
// Changes: the items that changed after a version, for a delta sync (roadmap R28)
|
|
203
333
|
defineProcedure({
|
|
204
334
|
path: [...basePath, "changes"],
|
|
@@ -213,6 +343,9 @@ export function createCollectionProcedures(collectionName: string): AnyProcedure
|
|
|
213
343
|
const storage = getStorage(ctx);
|
|
214
344
|
if (storage.getChangesSince) {
|
|
215
345
|
const changes = await storage.getChangesSince(input.since);
|
|
346
|
+
// The chain of the change events starts at this version: the caller then applies the
|
|
347
|
+
// next event without a read (roadmap CLI-15)
|
|
348
|
+
if (topicRule) startChangeChain(ctx.bus, storage, topicOf(ctx), changes.version);
|
|
216
349
|
return {
|
|
217
350
|
version: changes.version,
|
|
218
351
|
full: changes.full,
|
|
@@ -230,6 +363,51 @@ export function createCollectionProcedures(collectionName: string): AnyProcedure
|
|
|
230
363
|
},
|
|
231
364
|
}),
|
|
232
365
|
|
|
366
|
+
// Find: the items that match a JSON selector, with an order, a skip and a limit (roadmap CLI-14)
|
|
367
|
+
defineProcedure({
|
|
368
|
+
path: [...basePath, "find"],
|
|
369
|
+
input: findInputSchemaOf(queryPolicy),
|
|
370
|
+
output: findOutputSchema,
|
|
371
|
+
metadata: {
|
|
372
|
+
effects: { reads: [resource], writes: [] },
|
|
373
|
+
description: `Find the items of ${collectionName} that match a selector, with their ids, an order, a skip and a limit`,
|
|
374
|
+
tags: ["collections", collectionName, "read", "query"],
|
|
375
|
+
},
|
|
376
|
+
handler: async (input: FindInput, ctx: ProcedureContext) => {
|
|
377
|
+
const storage = getStorage(ctx);
|
|
378
|
+
const selector = input.selector ?? {};
|
|
379
|
+
const query = queryOptionsOf(input, queryPolicy);
|
|
380
|
+
// The storage runs the selector itself when it can: a database does not read each item. It
|
|
381
|
+
// gets the policy of the host, so it permits what the procedure permits.
|
|
382
|
+
if (storage.query) return (await storage.query(selector, query, queryPolicy)).map(([id, value]) => ({ id, value }));
|
|
383
|
+
if (!storage.getEntries) {
|
|
384
|
+
throw new Error(`The storage of ${collectionName} cannot run a selector or give the ids of its items`);
|
|
385
|
+
}
|
|
386
|
+
return queryEntries(await storage.getEntries(), selector, query, queryPolicy).map(([id, value]) => ({ id, value }));
|
|
387
|
+
},
|
|
388
|
+
}),
|
|
389
|
+
|
|
390
|
+
// Patch: merge fields into one item, a JSON Merge Patch (roadmap CLI-14)
|
|
391
|
+
defineProcedure({
|
|
392
|
+
path: [...basePath, "patch"],
|
|
393
|
+
input: patchInputSchema,
|
|
394
|
+
output: patchOutputSchema,
|
|
395
|
+
metadata: {
|
|
396
|
+
effects: { reads: [resource], writes: [resource], idempotent: true },
|
|
397
|
+
description: `Merge fields into one item of ${collectionName}: a JSON Merge Patch, or the operations $set and $unset`,
|
|
398
|
+
tags: ["collections", collectionName, "write"],
|
|
399
|
+
},
|
|
400
|
+
handler: async (input: PatchInput, ctx: ProcedureContext) => {
|
|
401
|
+
const storage = getStorage(ctx);
|
|
402
|
+
const write = writeOptionsOf(input);
|
|
403
|
+
const value = await changing(ctx, storage, [input.id], input.origin, () =>
|
|
404
|
+
storage.patch ? storage.patch(input.id, input.fields, write) : patchInParts(storage, input.id, input.fields, write),
|
|
405
|
+
);
|
|
406
|
+
const output: GetFoundOutput = value === undefined ? { found: false } : { found: true, value };
|
|
407
|
+
return output;
|
|
408
|
+
},
|
|
409
|
+
}),
|
|
410
|
+
|
|
233
411
|
// Size
|
|
234
412
|
defineProcedure({
|
|
235
413
|
path: [...basePath, "size"],
|
|
@@ -256,9 +434,9 @@ export function createCollectionProcedures(collectionName: string): AnyProcedure
|
|
|
256
434
|
description: `Clear all items from ${collectionName}`,
|
|
257
435
|
tags: ["collections", collectionName, "write"],
|
|
258
436
|
},
|
|
259
|
-
handler: async (
|
|
437
|
+
handler: async (input: ClearInput, ctx: ProcedureContext) => {
|
|
260
438
|
const storage = getStorage(ctx);
|
|
261
|
-
await storage.clear();
|
|
439
|
+
await changing(ctx, storage, "*", input.origin, () => storage.clear(writeOptionsOf(input)));
|
|
262
440
|
},
|
|
263
441
|
}),
|
|
264
442
|
|
|
@@ -296,7 +474,7 @@ export function createCollectionProcedures(collectionName: string): AnyProcedure
|
|
|
296
474
|
item.id,
|
|
297
475
|
item.value,
|
|
298
476
|
]);
|
|
299
|
-
await storage.setBatch(items);
|
|
477
|
+
await changing(ctx, storage, items.map(([id]) => id), input.origin, () => storage.setBatch(items, writeOptionsOf(input)));
|
|
300
478
|
},
|
|
301
479
|
}),
|
|
302
480
|
|
|
@@ -312,7 +490,7 @@ export function createCollectionProcedures(collectionName: string): AnyProcedure
|
|
|
312
490
|
},
|
|
313
491
|
handler: async (input: DeleteBatchInput, ctx: ProcedureContext) => {
|
|
314
492
|
const storage = getStorage(ctx);
|
|
315
|
-
return storage.deleteBatch(input.ids);
|
|
493
|
+
return changing(ctx, storage, input.ids, input.origin, () => storage.deleteBatch(input.ids, writeOptionsOf(input)));
|
|
316
494
|
},
|
|
317
495
|
}),
|
|
318
496
|
];
|