@procedures/client 0.8.1 → 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 +20 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/adapters/websocket/client/transport.d.ts.map +1 -1
- package/dist/adapters/websocket/client/transport.js +4 -12
- package/dist/adapters/websocket/client/transport.js.map +1 -1
- package/dist/browser.d.ts +2 -2
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +2 -0
- package/dist/browser.js.map +1 -1
- package/dist/events/publisher.d.ts +11 -1
- package/dist/events/publisher.d.ts.map +1 -1
- package/dist/events/publisher.js +27 -8
- package/dist/events/publisher.js.map +1 -1
- package/dist/procedures/collection/changes.d.ts +3 -3
- package/dist/procedures/collection/changes.d.ts.map +1 -1
- package/dist/procedures/collection/changes.js +4 -4
- package/dist/procedures/collection/changes.js.map +1 -1
- package/dist/procedures/collection/index.d.ts +2 -0
- package/dist/procedures/collection/index.d.ts.map +1 -1
- package/dist/procedures/collection/index.js +2 -0
- package/dist/procedures/collection/index.js.map +1 -1
- package/dist/procedures/collection/partition.d.ts +67 -0
- package/dist/procedures/collection/partition.d.ts.map +1 -0
- package/dist/procedures/collection/partition.js +82 -0
- package/dist/procedures/collection/partition.js.map +1 -0
- package/dist/procedures/collection/procedures.d.ts +4 -4
- package/dist/procedures/collection/procedures.d.ts.map +1 -1
- package/dist/procedures/collection/procedures.js +55 -55
- package/dist/procedures/collection/procedures.js.map +1 -1
- package/dist/procedures/index.d.ts +2 -2
- package/dist/procedures/index.d.ts.map +1 -1
- package/dist/procedures/index.js +1 -1
- package/dist/procedures/index.js.map +1 -1
- package/dist/procedures/invoke.d.ts +4 -2
- package/dist/procedures/invoke.d.ts.map +1 -1
- package/dist/procedures/invoke.js +1 -1
- package/dist/procedures/invoke.js.map +1 -1
- package/dist/procedures/types.d.ts +13 -0
- package/dist/procedures/types.d.ts.map +1 -1
- package/dist/procedures/types.js.map +1 -1
- package/dist/server/procedure-server.d.ts +30 -1
- package/dist/server/procedure-server.d.ts.map +1 -1
- package/dist/server/procedure-server.js +59 -11
- package/dist/server/procedure-server.js.map +1 -1
- package/package.json +3 -3
- package/src/adapters/websocket/client/transport.ts +4 -12
- package/src/browser.ts +6 -0
- package/src/events/publisher.ts +29 -9
- package/src/procedures/collection/changes.ts +4 -4
- package/src/procedures/collection/index.ts +4 -0
- package/src/procedures/collection/partition.ts +123 -0
- package/src/procedures/collection/procedures.ts +72 -73
- package/src/procedures/index.ts +5 -0
- package/src/procedures/invoke.ts +4 -2
- package/src/procedures/types.ts +16 -0
- package/src/server/procedure-server.ts +65 -12
package/src/procedures/invoke.ts
CHANGED
|
@@ -73,8 +73,10 @@ export type InvocationErrorCode =
|
|
|
73
73
|
* to such a host (`createHttpModule({ allow })` of `@procedures/http`).
|
|
74
74
|
* - `topic-not-allowed`: the `allow` function of an `EventPublisher` refuses a topic of a
|
|
75
75
|
* subscription (`documentation/EVENTS.md`).
|
|
76
|
+
* - `partition-not-allowed`: the partition resolver of the collection procedures refuses the
|
|
77
|
+
* partition of a call (roadmap CLI-17).
|
|
76
78
|
*/
|
|
77
|
-
export type ForbiddenReason = CapabilityRefusalReason | "outside-root" | "host-not-allowed" | "topic-not-allowed";
|
|
79
|
+
export type ForbiddenReason = CapabilityRefusalReason | "outside-root" | "host-not-allowed" | "topic-not-allowed" | "partition-not-allowed";
|
|
78
80
|
|
|
79
81
|
/** An error of the invocation itself. An error that the handler throws passes through unchanged. */
|
|
80
82
|
export class InvocationError extends Error {
|
|
@@ -84,7 +86,7 @@ export class InvocationError extends Error {
|
|
|
84
86
|
readonly retryable: boolean = false;
|
|
85
87
|
/**
|
|
86
88
|
* The reason of a `FORBIDDEN` error: `effects-undeclared`, `effects-exceeded`, `clearance`,
|
|
87
|
-
* `deadline`, `outside-root`, `host-not-allowed` or `
|
|
89
|
+
* `deadline`, `outside-root`, `host-not-allowed`, `topic-not-allowed` or `partition-not-allowed`.
|
|
88
90
|
*/
|
|
89
91
|
readonly reason: ForbiddenReason | undefined;
|
|
90
92
|
|
package/src/procedures/types.ts
CHANGED
|
@@ -259,6 +259,22 @@ export interface RepositoryProvider {
|
|
|
259
259
|
*/
|
|
260
260
|
getStorage<T>(collection: string): CollectionStorage<T>;
|
|
261
261
|
|
|
262
|
+
/**
|
|
263
|
+
* The storage of one partition of a collection (roadmap CLI-17), for example the items of one
|
|
264
|
+
* tenant. A repository without this method has no partitions: the collection procedures then
|
|
265
|
+
* refuse a call with a partition, and never give it the storage of all partitions.
|
|
266
|
+
*/
|
|
267
|
+
getPartitionStorage?<T>(collection: string, partition: string): CollectionStorage<T>;
|
|
268
|
+
|
|
269
|
+
/** True when the collection has partitions (roadmap CLI-17). Then a call resolves its partition with `resolvePartition`. */
|
|
270
|
+
hasPartitions?(collection: string): boolean;
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* The partition of a call to a partitioned collection (roadmap CLI-17): from the context (the
|
|
274
|
+
* tenant of `ctx.principal`) and the partition that the call asks for. `null` refuses the call.
|
|
275
|
+
*/
|
|
276
|
+
resolvePartition?(collection: string, ctx: ProcedureContext, requested: string | undefined): string | undefined | null | Promise<string | undefined | null>;
|
|
277
|
+
|
|
262
278
|
/**
|
|
263
279
|
* Check if a collection exists.
|
|
264
280
|
*
|
|
@@ -18,7 +18,8 @@ import { pathToKey } from "../procedures/types.js";
|
|
|
18
18
|
import { ProcedureRegistry, PROCEDURE_REGISTRY } from "../procedures/registry.js";
|
|
19
19
|
import { createCollectionProcedures } from "../procedures/collection/procedures.js";
|
|
20
20
|
import { InvocationError, invokeProcedure, type AdmitItem, type Interceptor, type Invocation } from "../procedures/invoke.js";
|
|
21
|
-
import type { Capability, ClearanceOrder, ResourcesOf } from "../procedures/types.js";
|
|
21
|
+
import type { Capability, ClearanceOrder, ProcedureContext, ResourcesOf } from "../procedures/types.js";
|
|
22
|
+
import type { CollectionPartitions } from "../procedures/collection/partition.js";
|
|
22
23
|
import { TIMEOUT_METADATA_KEY, effectsOf, joinNarrowing, narrowCapability } from "../procedures/capability.js";
|
|
23
24
|
import { IdempotencyCache, idempotencyKeyOf, isRepeatable, type IdempotencyOptions } from "../procedures/idempotency.js";
|
|
24
25
|
import { optionalMs, type Duration } from "../duration.js";
|
|
@@ -316,6 +317,8 @@ export function errorResponse(id: string, error: unknown): ServerResponse<unknow
|
|
|
316
317
|
export class ProcedureServer extends Server implements RepositoryProvider {
|
|
317
318
|
private readonly procedureRegistry: ProcedureRegistry;
|
|
318
319
|
private readonly storages = new Map<string, CollectionStorage<unknown>>();
|
|
320
|
+
/** The partitions of each partitioned collection: their storages and their resolver (roadmap CLI-17) */
|
|
321
|
+
private readonly partitions = new Map<string, CollectionPartitions<unknown>>();
|
|
319
322
|
/** For each storage: the calls in flight that took it, and the closes that wait for them */
|
|
320
323
|
private readonly storageUses = new Map<CollectionStorage<unknown>, { calls: number; waiters: Array<() => void> }>();
|
|
321
324
|
/** The clock of the wait of `useStorage` */
|
|
@@ -421,7 +424,54 @@ export class ProcedureServer extends Server implements RepositoryProvider {
|
|
|
421
424
|
* @param collection - Collection name
|
|
422
425
|
*/
|
|
423
426
|
hasCollection(collection: string): boolean {
|
|
424
|
-
return this.storages.has(collection);
|
|
427
|
+
return this.storages.has(collection) || this.partitions.has(collection);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* The storage of one partition of a collection (roadmap CLI-17).
|
|
432
|
+
*
|
|
433
|
+
* @throws Error if the collection has no partitions
|
|
434
|
+
*/
|
|
435
|
+
getPartitionStorage<T>(collection: string, partition: string): CollectionStorage<T> {
|
|
436
|
+
const partitions = this.partitions.get(collection);
|
|
437
|
+
if (!partitions) {
|
|
438
|
+
throw new Error(`Collection has no partitions: ${collection}`);
|
|
439
|
+
}
|
|
440
|
+
return partitions.storageOf(partition) as CollectionStorage<T>;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/** True when the collection has partitions (roadmap CLI-17). */
|
|
444
|
+
hasPartitions(collection: string): boolean {
|
|
445
|
+
return this.partitions.has(collection);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* The partition of a call to a partitioned collection (roadmap CLI-17), from the resolver of
|
|
450
|
+
* `usePartitions`. `null` refuses the call.
|
|
451
|
+
*
|
|
452
|
+
* @throws Error if the collection has no partitions
|
|
453
|
+
*/
|
|
454
|
+
resolvePartition(collection: string, ctx: ProcedureContext, requested: string | undefined): string | undefined | null | Promise<string | undefined | null> {
|
|
455
|
+
const partitions = this.partitions.get(collection);
|
|
456
|
+
if (!partitions) {
|
|
457
|
+
throw new Error(`Collection has no partitions: ${collection}`);
|
|
458
|
+
}
|
|
459
|
+
return partitions.resolve(ctx, requested);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Configure the partitions of a collection (roadmap CLI-17). `resolve` gives the partition of
|
|
464
|
+
* each call from its context (the tenant of `ctx.principal`) and the partition that the call asks
|
|
465
|
+
* for: the collection procedures and the subscriptions of `EventPublisher` use it. `storageOf`
|
|
466
|
+
* gives the storage of a partition: give the same storage for the same partition each time.
|
|
467
|
+
*
|
|
468
|
+
* @param name - Collection name
|
|
469
|
+
* @param partitions - The storage of each partition, and the resolver
|
|
470
|
+
* @returns this (for chaining)
|
|
471
|
+
*/
|
|
472
|
+
usePartitions<T>(name: string, partitions: CollectionPartitions<T>): this {
|
|
473
|
+
this.partitions.set(name, partitions as CollectionPartitions<unknown>);
|
|
474
|
+
return this;
|
|
425
475
|
}
|
|
426
476
|
|
|
427
477
|
// ===========================================================================
|
|
@@ -503,17 +553,20 @@ export class ProcedureServer extends Server implements RepositoryProvider {
|
|
|
503
553
|
private callRepository(): { repository: RepositoryProvider; release: () => void } {
|
|
504
554
|
const taken = new Set<CollectionStorage<unknown>>();
|
|
505
555
|
let released = false;
|
|
556
|
+
const take = <T>(storage: CollectionStorage<unknown>): CollectionStorage<T> => {
|
|
557
|
+
if (!released && !taken.has(storage)) {
|
|
558
|
+
taken.add(storage);
|
|
559
|
+
const use = this.storageUses.get(storage) ?? { calls: 0, waiters: [] };
|
|
560
|
+
use.calls++;
|
|
561
|
+
this.storageUses.set(storage, use);
|
|
562
|
+
}
|
|
563
|
+
return storage as CollectionStorage<T>;
|
|
564
|
+
};
|
|
506
565
|
const repository: RepositoryProvider = {
|
|
507
|
-
getStorage: <T>(collection: string): CollectionStorage<T> =>
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
const use = this.storageUses.get(storage) ?? { calls: 0, waiters: [] };
|
|
512
|
-
use.calls++;
|
|
513
|
-
this.storageUses.set(storage, use);
|
|
514
|
-
}
|
|
515
|
-
return storage as CollectionStorage<T>;
|
|
516
|
-
},
|
|
566
|
+
getStorage: <T>(collection: string): CollectionStorage<T> => take<T>(this.getStorage<unknown>(collection)),
|
|
567
|
+
getPartitionStorage: <T>(collection: string, partition: string): CollectionStorage<T> => take<T>(this.getPartitionStorage<unknown>(collection, partition)),
|
|
568
|
+
hasPartitions: (collection: string): boolean => this.hasPartitions(collection),
|
|
569
|
+
resolvePartition: (collection: string, ctx: ProcedureContext, requested: string | undefined) => this.resolvePartition(collection, ctx, requested),
|
|
517
570
|
hasCollection: (collection: string): boolean => this.hasCollection(collection),
|
|
518
571
|
};
|
|
519
572
|
const release = (): void => {
|