@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
|
@@ -6,6 +6,16 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type { ZodLike, ZodErrorLike } from "../../client/validation/types.js";
|
|
9
|
+
import {
|
|
10
|
+
DEFAULT_FIND_LIMIT,
|
|
11
|
+
patchProblems,
|
|
12
|
+
queryOptionsProblems,
|
|
13
|
+
selectorProblems,
|
|
14
|
+
type StoragePatch,
|
|
15
|
+
type StorageQueryPolicy,
|
|
16
|
+
type StorageSelector,
|
|
17
|
+
type StorageSort,
|
|
18
|
+
} from "./query.js";
|
|
9
19
|
|
|
10
20
|
// =============================================================================
|
|
11
21
|
// Generic Schema Builder
|
|
@@ -53,6 +63,26 @@ function isObject(value: unknown): value is Record<string, unknown> {
|
|
|
53
63
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
54
64
|
}
|
|
55
65
|
|
|
66
|
+
/** The longest origin of a write, in characters. */
|
|
67
|
+
const MAX_ORIGIN_LENGTH = 256;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The error of the field `origin` of a write input (roadmap CLI-15), or undefined. An origin is a
|
|
71
|
+
* string of 1 to 256 characters. The procedure gives it to the storage and to the change event.
|
|
72
|
+
*/
|
|
73
|
+
function originError(data: Record<string, unknown>): ZodErrorLike | undefined {
|
|
74
|
+
const origin = data["origin"];
|
|
75
|
+
if (origin === undefined || (isString(origin) && origin.length > 0 && origin.length <= MAX_ORIGIN_LENGTH)) return undefined;
|
|
76
|
+
const message = `origin must be a string of 1 to ${MAX_ORIGIN_LENGTH} characters`;
|
|
77
|
+
return { message, errors: [{ path: ["origin"], message }] };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** The input with the origin of the data, when the data has one. */
|
|
81
|
+
function withOriginOf<I extends object>(data: Record<string, unknown>, input: I): I & { origin?: string } {
|
|
82
|
+
const origin = data["origin"];
|
|
83
|
+
return (isString(origin) ? { ...input, origin } : input) as I & { origin?: string };
|
|
84
|
+
}
|
|
85
|
+
|
|
56
86
|
// =============================================================================
|
|
57
87
|
// Common Schemas
|
|
58
88
|
// =============================================================================
|
|
@@ -185,6 +215,8 @@ export const getOutputSchema : ZodLike<unknown> = createSchema<unknown>((data) =
|
|
|
185
215
|
export interface SetInput {
|
|
186
216
|
id: string;
|
|
187
217
|
value: unknown;
|
|
218
|
+
/** The origin of the write, for the observers of the storage and the change event (roadmap CLI-15). */
|
|
219
|
+
origin?: string;
|
|
188
220
|
}
|
|
189
221
|
|
|
190
222
|
export const setInputSchema : ZodLike<SetInput> = createSchema<SetInput>((data) => {
|
|
@@ -215,7 +247,9 @@ export const setInputSchema : ZodLike<SetInput> = createSchema<SetInput>((data)
|
|
|
215
247
|
},
|
|
216
248
|
};
|
|
217
249
|
}
|
|
218
|
-
|
|
250
|
+
const error = originError(data);
|
|
251
|
+
if (error) return { success: false, error };
|
|
252
|
+
return { success: true, data: withOriginOf(data, { id: data["id"], value: data["value"] }) };
|
|
219
253
|
});
|
|
220
254
|
|
|
221
255
|
/**
|
|
@@ -234,6 +268,8 @@ export const setOutputSchema : ZodLike<void> = createSchema<void>((_data) => {
|
|
|
234
268
|
*/
|
|
235
269
|
export interface DeleteInput {
|
|
236
270
|
id: string;
|
|
271
|
+
/** The origin of the write, for the observers of the storage and the change event (roadmap CLI-15). */
|
|
272
|
+
origin?: string;
|
|
237
273
|
}
|
|
238
274
|
|
|
239
275
|
export const deleteInputSchema : ZodLike<DeleteInput> = createSchema<DeleteInput>((data) => {
|
|
@@ -255,7 +291,9 @@ export const deleteInputSchema : ZodLike<DeleteInput> = createSchema<DeleteInput
|
|
|
255
291
|
},
|
|
256
292
|
};
|
|
257
293
|
}
|
|
258
|
-
|
|
294
|
+
const error = originError(data);
|
|
295
|
+
if (error) return { success: false, error };
|
|
296
|
+
return { success: true, data: withOriginOf(data, { id: data["id"] }) };
|
|
259
297
|
});
|
|
260
298
|
|
|
261
299
|
/**
|
|
@@ -406,6 +444,181 @@ export const entriesOutputSchema : ZodLike<CollectionEntry[]> = createSchema<Col
|
|
|
406
444
|
};
|
|
407
445
|
});
|
|
408
446
|
|
|
447
|
+
// =============================================================================
|
|
448
|
+
// Find Operation Schemas
|
|
449
|
+
// =============================================================================
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Input for find operation: a JSON selector, and the order, the skip and the limit. The selector
|
|
453
|
+
* is the selector of `query()` of a storage.
|
|
454
|
+
*/
|
|
455
|
+
export interface FindInput {
|
|
456
|
+
/** The selector. Without it, each item matches. */
|
|
457
|
+
selector?: StorageSelector;
|
|
458
|
+
/** The order of the result: field paths with 1 (ascending) or -1 (descending). */
|
|
459
|
+
sort?: StorageSort;
|
|
460
|
+
/** The number of items to skip after the sort. */
|
|
461
|
+
skip?: number;
|
|
462
|
+
/** The largest number of items in the result. */
|
|
463
|
+
limit?: number;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/** The problems of a find input under a query policy, as the errors of a schema. */
|
|
467
|
+
function findErrors(data: Record<string, unknown>, policy: StorageQueryPolicy): Array<{ path: Array<string | number>; message: string }> {
|
|
468
|
+
const errors: Array<{ path: Array<string | number>; message: string }> = [];
|
|
469
|
+
for (const key of Object.keys(data)) {
|
|
470
|
+
if (data[key] !== undefined && key !== "selector" && key !== "sort" && key !== "skip" && key !== "limit") {
|
|
471
|
+
errors.push({ path: [key], message: `${key} is not a field of the input: use selector, sort, skip and limit` });
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
if (data["selector"] !== undefined) {
|
|
475
|
+
for (const message of selectorProblems(data["selector"], policy)) errors.push({ path: ["selector"], message });
|
|
476
|
+
}
|
|
477
|
+
const options = { sort: data["sort"], skip: data["skip"], limit: data["limit"] };
|
|
478
|
+
for (const message of queryOptionsProblems(options, policy)) errors.push({ path: [/^(sort|skip|limit)/.exec(message)?.[1] ?? "options"], message });
|
|
479
|
+
return errors;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* The input schema of find under a query policy: a selector or options that the policy does not
|
|
484
|
+
* permit are a validation error (`VALIDATION_ERROR`), with the reason.
|
|
485
|
+
*/
|
|
486
|
+
export function findInputSchemaOf(policy: StorageQueryPolicy): ZodLike<FindInput> {
|
|
487
|
+
return createSchema<FindInput>((data) => findInputOf(data, policy));
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/** The find input of the data, or the errors of the schema. */
|
|
491
|
+
function findInputOf(data: unknown, policy: StorageQueryPolicy): { success: true; data: FindInput } | { success: false; error: ZodErrorLike } {
|
|
492
|
+
if (!isObject(data)) {
|
|
493
|
+
return {
|
|
494
|
+
success: false,
|
|
495
|
+
error: {
|
|
496
|
+
message: "Input must be an object",
|
|
497
|
+
errors: [{ path: [], message: "Input must be an object" }],
|
|
498
|
+
},
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
const errors = findErrors(data, policy);
|
|
502
|
+
if (errors.length > 0) {
|
|
503
|
+
return { success: false, error: { message: errors.map((error) => error.message).join(". "), errors } };
|
|
504
|
+
}
|
|
505
|
+
const input: FindInput = {};
|
|
506
|
+
if (data["selector"] !== undefined) input.selector = data["selector"] as StorageSelector;
|
|
507
|
+
if (data["sort"] !== undefined) input.sort = data["sort"] as StorageSort;
|
|
508
|
+
if (data["skip"] !== undefined) input.skip = data["skip"] as number;
|
|
509
|
+
if (data["limit"] !== undefined) input.limit = data["limit"] as number;
|
|
510
|
+
return { success: true, data: input };
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/** The input schema of find under the default query policy: no `$regex`, at most `DEFAULT_FIND_LIMIT` items. */
|
|
514
|
+
export const findInputSchema : ZodLike<FindInput> = findInputSchemaOf({ maxLimit: DEFAULT_FIND_LIMIT });
|
|
515
|
+
|
|
516
|
+
/** Output for find operation: the matching items with their ids, in the order of the query. */
|
|
517
|
+
export const findOutputSchema : ZodLike<CollectionEntry[]> = createSchema<CollectionEntry[]>((data) => entriesOutputSchema.safeParse(data));
|
|
518
|
+
|
|
519
|
+
// =============================================================================
|
|
520
|
+
// Patch Operation Schemas
|
|
521
|
+
// =============================================================================
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Input for patch operation: the id, and the fields. The fields are a JSON Merge Patch (RFC 7386),
|
|
525
|
+
* or the operations `{ $set, $unset }`, which can set `null` and nested field paths.
|
|
526
|
+
*/
|
|
527
|
+
export interface PatchInput {
|
|
528
|
+
id: string;
|
|
529
|
+
fields: StoragePatch;
|
|
530
|
+
/** The origin of the write, for the observers of the storage and the change event (roadmap CLI-15). */
|
|
531
|
+
origin?: string;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
export const patchInputSchema : ZodLike<PatchInput> = createSchema<PatchInput>((data) => {
|
|
535
|
+
if (!isObject(data)) {
|
|
536
|
+
return {
|
|
537
|
+
success: false,
|
|
538
|
+
error: {
|
|
539
|
+
message: "Input must be an object",
|
|
540
|
+
errors: [{ path: [], message: "Input must be an object" }],
|
|
541
|
+
},
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
if (!isString(data["id"]) || data["id"].length === 0) {
|
|
545
|
+
return {
|
|
546
|
+
success: false,
|
|
547
|
+
error: {
|
|
548
|
+
message: "id must be a non-empty string",
|
|
549
|
+
errors: [{ path: ["id"], message: "id must be a non-empty string" }],
|
|
550
|
+
},
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
const [problem] = patchProblems(data["fields"]);
|
|
554
|
+
if (problem !== undefined) {
|
|
555
|
+
return {
|
|
556
|
+
success: false,
|
|
557
|
+
error: {
|
|
558
|
+
message: problem,
|
|
559
|
+
errors: [{ path: ["fields"], message: problem }],
|
|
560
|
+
},
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
const error = originError(data);
|
|
564
|
+
if (error) return { success: false, error };
|
|
565
|
+
return { success: true, data: withOriginOf(data, { id: data["id"], fields: data["fields"] as StoragePatch }) };
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* Output for patch operation: `found` is true when the collection has the id. Then `value` is the
|
|
570
|
+
* new item.
|
|
571
|
+
*/
|
|
572
|
+
export const patchOutputSchema : ZodLike<GetFoundOutput> = createSchema<GetFoundOutput>((data) => {
|
|
573
|
+
if (isObject(data) && typeof data["found"] === "boolean") {
|
|
574
|
+
return { success: true, data: data as unknown as GetFoundOutput };
|
|
575
|
+
}
|
|
576
|
+
return {
|
|
577
|
+
success: false,
|
|
578
|
+
error: {
|
|
579
|
+
message: "Output must be { found, value }",
|
|
580
|
+
errors: [{ path: [], message: "Output must be { found, value }" }],
|
|
581
|
+
},
|
|
582
|
+
};
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
// =============================================================================
|
|
586
|
+
// Ids Operation Schemas
|
|
587
|
+
// =============================================================================
|
|
588
|
+
|
|
589
|
+
/** Input for ids operation - empty object. */
|
|
590
|
+
export interface IdsInput {}
|
|
591
|
+
|
|
592
|
+
export const idsInputSchema : ZodLike<IdsInput> = createSchema<IdsInput>((data) => {
|
|
593
|
+
if (!isObject(data)) {
|
|
594
|
+
return {
|
|
595
|
+
success: false,
|
|
596
|
+
error: {
|
|
597
|
+
message: "Input must be an object",
|
|
598
|
+
errors: [{ path: [], message: "Input must be an object" }],
|
|
599
|
+
},
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
return { success: true, data: {} };
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* Output for ids operation: the ids of the items, in the order of the entries, without the
|
|
607
|
+
* values. A full sync needs only the ids.
|
|
608
|
+
*/
|
|
609
|
+
export const idsOutputSchema : ZodLike<string[]> = createSchema<string[]>((data) => {
|
|
610
|
+
if (isArray(data) && data.every((id) => isString(id))) {
|
|
611
|
+
return { success: true, data: data as string[] };
|
|
612
|
+
}
|
|
613
|
+
return {
|
|
614
|
+
success: false,
|
|
615
|
+
error: {
|
|
616
|
+
message: "Output must be an array of ids",
|
|
617
|
+
errors: [{ path: [], message: "Output must be an array of ids" }],
|
|
618
|
+
},
|
|
619
|
+
};
|
|
620
|
+
});
|
|
621
|
+
|
|
409
622
|
// =============================================================================
|
|
410
623
|
// Changes Operation Schemas
|
|
411
624
|
// =============================================================================
|
|
@@ -516,7 +729,10 @@ export const sizeOutputSchema : ZodLike<number> = createSchema<number>((data) =>
|
|
|
516
729
|
/**
|
|
517
730
|
* Input for clear operation - empty object.
|
|
518
731
|
*/
|
|
519
|
-
export interface ClearInput {
|
|
732
|
+
export interface ClearInput {
|
|
733
|
+
/** The origin of the write, for the observers of the storage and the change event (roadmap CLI-15). */
|
|
734
|
+
origin?: string;
|
|
735
|
+
}
|
|
520
736
|
|
|
521
737
|
export const clearInputSchema : ZodLike<ClearInput> = createSchema<ClearInput>((data) => {
|
|
522
738
|
if (!isObject(data)) {
|
|
@@ -528,7 +744,9 @@ export const clearInputSchema : ZodLike<ClearInput> = createSchema<ClearInput>((
|
|
|
528
744
|
},
|
|
529
745
|
};
|
|
530
746
|
}
|
|
531
|
-
|
|
747
|
+
const error = originError(data);
|
|
748
|
+
if (error) return { success: false, error };
|
|
749
|
+
return { success: true, data: withOriginOf(data, {}) };
|
|
532
750
|
});
|
|
533
751
|
|
|
534
752
|
/**
|
|
@@ -592,6 +810,8 @@ export const getBatchOutputSchema : ZodLike<Record<string, unknown>> = createSch
|
|
|
592
810
|
*/
|
|
593
811
|
export interface SetBatchInput {
|
|
594
812
|
items: Array<{ id: string; value: unknown }>;
|
|
813
|
+
/** The origin of the write, for the observers of the storage and the change event (roadmap CLI-15). */
|
|
814
|
+
origin?: string;
|
|
595
815
|
}
|
|
596
816
|
|
|
597
817
|
export const setBatchInputSchema : ZodLike<SetBatchInput> = createSchema<SetBatchInput>((data) => {
|
|
@@ -625,11 +845,13 @@ export const setBatchInputSchema : ZodLike<SetBatchInput> = createSchema<SetBatc
|
|
|
625
845
|
};
|
|
626
846
|
}
|
|
627
847
|
}
|
|
848
|
+
const error = originError(data);
|
|
849
|
+
if (error) return { success: false, error };
|
|
628
850
|
return {
|
|
629
851
|
success: true,
|
|
630
|
-
data: {
|
|
852
|
+
data: withOriginOf(data, {
|
|
631
853
|
items: data["items"].map((item: any) => ({ id: item["id"], value: item["value"] })),
|
|
632
|
-
},
|
|
854
|
+
}),
|
|
633
855
|
};
|
|
634
856
|
});
|
|
635
857
|
|
|
@@ -645,6 +867,8 @@ export const setBatchOutputSchema : ZodLike<void> = createSchema<void>((_data) =
|
|
|
645
867
|
*/
|
|
646
868
|
export interface DeleteBatchInput {
|
|
647
869
|
ids: string[];
|
|
870
|
+
/** The origin of the write, for the observers of the storage and the change event (roadmap CLI-15). */
|
|
871
|
+
origin?: string;
|
|
648
872
|
}
|
|
649
873
|
|
|
650
874
|
export const deleteBatchInputSchema : ZodLike<DeleteBatchInput> = createSchema<DeleteBatchInput>((data) => {
|
|
@@ -666,7 +890,9 @@ export const deleteBatchInputSchema : ZodLike<DeleteBatchInput> = createSchema<D
|
|
|
666
890
|
},
|
|
667
891
|
};
|
|
668
892
|
}
|
|
669
|
-
|
|
893
|
+
const error = originError(data);
|
|
894
|
+
if (error) return { success: false, error };
|
|
895
|
+
return { success: true, data: withOriginOf(data, { ids: data["ids"] as string[] }) };
|
|
670
896
|
});
|
|
671
897
|
|
|
672
898
|
/**
|
|
@@ -13,10 +13,29 @@
|
|
|
13
13
|
* The contract (the suite of `@procedures/collections/testing` tests it for each storage):
|
|
14
14
|
* - A storage keeps a copy of each value that a write takes, and each read gives a new copy.
|
|
15
15
|
* - A stored `null` reads back as `null`. A missing id reads back as `undefined`.
|
|
16
|
+
* - `getIds()` gives the ids of `getEntries()`, in the same order.
|
|
16
17
|
* - A delta of `getChangesSince` follows the order of the last change of each id.
|
|
17
18
|
* - After `close()`, each operation rejects with an error whose message says "closed". A second
|
|
18
19
|
* `close()` does nothing.
|
|
20
|
+
* - `query()` gives the result of `queryEntries()` of `@procedures/collections` for the entries.
|
|
21
|
+
* - `observe()` gives one batch for each write that changes the storage, before the write resolves.
|
|
22
|
+
* - `patch()` merges the fields into the item in one step.
|
|
19
23
|
*/
|
|
24
|
+
|
|
25
|
+
import type { StoragePatch, StorageQueryOptions, StorageQueryPolicy, StorageSelector } from "./query.js";
|
|
26
|
+
|
|
27
|
+
export type {
|
|
28
|
+
StorageFieldOperators,
|
|
29
|
+
StoragePatch,
|
|
30
|
+
StoragePatchOperations,
|
|
31
|
+
StorageQueryOperator,
|
|
32
|
+
StorageQueryOptions,
|
|
33
|
+
StorageQueryPolicy,
|
|
34
|
+
StorageSelector,
|
|
35
|
+
StorageSort,
|
|
36
|
+
StorageSortDirection,
|
|
37
|
+
} from "./query.js";
|
|
38
|
+
|
|
20
39
|
export interface CollectionStorage<T> {
|
|
21
40
|
/** The item with this id, or undefined. A stored null is null. */
|
|
22
41
|
get(id: string): Promise<T | undefined>;
|
|
@@ -27,6 +46,11 @@ export interface CollectionStorage<T> {
|
|
|
27
46
|
* them does not have this method.
|
|
28
47
|
*/
|
|
29
48
|
getEntries?(): Promise<Array<[string, T]>>;
|
|
49
|
+
/**
|
|
50
|
+
* The ids of the items, in the order of the entries, without the values. A storage that cannot
|
|
51
|
+
* give the ids cheaply does not have this method.
|
|
52
|
+
*/
|
|
53
|
+
getIds?(): Promise<string[]>;
|
|
30
54
|
/**
|
|
31
55
|
* The changes after a version (roadmap R28). A storage without a change log does not have
|
|
32
56
|
* this method, and a sync uses `getEntries()`.
|
|
@@ -36,20 +60,38 @@ export interface CollectionStorage<T> {
|
|
|
36
60
|
getChangesSince?(since?: string): Promise<StorageChanges<T>>;
|
|
37
61
|
/** The items for which the predicate gives true. */
|
|
38
62
|
find(predicate: (item: T) => boolean): Promise<T[]>;
|
|
63
|
+
/**
|
|
64
|
+
* The entries whose items match a JSON selector, with an order, a skip and a limit. The
|
|
65
|
+
* procedure `find` uses it, and gives the query policy of the host. A storage without it gives
|
|
66
|
+
* its entries, and the procedure runs the selector itself. Without a policy, the storage uses its
|
|
67
|
+
* own policy: `$regex` is off by default.
|
|
68
|
+
*/
|
|
69
|
+
query?(selector: StorageSelector, options?: StorageQueryOptions, policy?: StorageQueryPolicy): Promise<Array<[string, T]>>;
|
|
70
|
+
/**
|
|
71
|
+
* This method sends a batch of changes to the observer for each write that changes the storage,
|
|
72
|
+
* before the write resolves. It gives the function that stops the batches.
|
|
73
|
+
*/
|
|
74
|
+
observe?(observer: StorageObserver<T>): () => void;
|
|
39
75
|
/** True when an item has this id. */
|
|
40
76
|
has(id: string): Promise<boolean>;
|
|
41
77
|
/** The number of items. */
|
|
42
78
|
size(): Promise<number>;
|
|
43
|
-
/** Set the item of an id. */
|
|
44
|
-
set(id: string, value: T): Promise<void>;
|
|
79
|
+
/** Set the item of an id. The options give the origin of the write to the observers. */
|
|
80
|
+
set(id: string, value: T, options?: StorageWriteOptions): Promise<void>;
|
|
45
81
|
/** Delete the item of an id. True when it existed. */
|
|
46
|
-
delete(id: string): Promise<boolean>;
|
|
82
|
+
delete(id: string, options?: StorageWriteOptions): Promise<boolean>;
|
|
47
83
|
/** Delete every item. */
|
|
48
|
-
clear(): Promise<void>;
|
|
84
|
+
clear(options?: StorageWriteOptions): Promise<void>;
|
|
85
|
+
/**
|
|
86
|
+
* This method changes the item of an id in one step, and gives the new item, or undefined for a
|
|
87
|
+
* missing id. The fields are a JSON Merge Patch, or the operations `{ $set, $unset }`. The
|
|
88
|
+
* procedure `patch` uses it. A storage without it gets a read and a write of the item.
|
|
89
|
+
*/
|
|
90
|
+
patch?(id: string, fields: StoragePatch, options?: StorageWriteOptions): Promise<T | undefined>;
|
|
49
91
|
/** Set several items. */
|
|
50
|
-
setBatch(items: Array<[string, T]
|
|
92
|
+
setBatch(items: Array<[string, T]>, options?: StorageWriteOptions): Promise<void>;
|
|
51
93
|
/** Delete several items. The number of items that existed. */
|
|
52
|
-
deleteBatch(ids: string[]): Promise<number>;
|
|
94
|
+
deleteBatch(ids: string[], options?: StorageWriteOptions): Promise<number>;
|
|
53
95
|
/** The items of several ids. An id with no item is not in the map. */
|
|
54
96
|
getBatch(ids: string[]): Promise<Map<string, T>>;
|
|
55
97
|
/**
|
|
@@ -66,6 +108,42 @@ export interface CollectionStorage<T> {
|
|
|
66
108
|
getMetadata?(): Promise<StorageMetadata>;
|
|
67
109
|
}
|
|
68
110
|
|
|
111
|
+
/** The options of a write. */
|
|
112
|
+
export interface StorageWriteOptions {
|
|
113
|
+
/** The tag of the writer. Each change that the write gives has it. */
|
|
114
|
+
readonly origin?: string | undefined;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** A change of `observe()`: an id got a value. */
|
|
118
|
+
export interface StorageSetChange<T> {
|
|
119
|
+
readonly type: "set";
|
|
120
|
+
readonly id: string;
|
|
121
|
+
readonly value: T;
|
|
122
|
+
readonly origin?: string | undefined;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** A change of `observe()`: an id lost its item. */
|
|
126
|
+
export interface StorageDeleteChange {
|
|
127
|
+
readonly type: "delete";
|
|
128
|
+
readonly id: string;
|
|
129
|
+
readonly origin?: string | undefined;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** A change of `observe()`: the storage lost each item. */
|
|
133
|
+
export interface StorageClearChange {
|
|
134
|
+
readonly type: "clear";
|
|
135
|
+
readonly origin?: string | undefined;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** One change of `observe()`. */
|
|
139
|
+
export type StorageChangeEvent<T> = StorageSetChange<T> | StorageDeleteChange | StorageClearChange;
|
|
140
|
+
|
|
141
|
+
/** The changes of one write, in the order of the write. */
|
|
142
|
+
export type StorageChangeBatch<T> = readonly StorageChangeEvent<T>[];
|
|
143
|
+
|
|
144
|
+
/** The function that gets each batch of `observe()`. */
|
|
145
|
+
export type StorageObserver<T> = (changes: StorageChangeBatch<T>) => void;
|
|
146
|
+
|
|
69
147
|
/**
|
|
70
148
|
* The changes of a storage after a version (roadmap R28).
|
|
71
149
|
*
|