@procedures/client 0.15.1 → 0.16.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 +31 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/browser.d.ts +1 -1
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +1 -1
- package/dist/browser.js.map +1 -1
- package/dist/events/publisher.d.ts +4 -4
- package/dist/events/publisher.d.ts.map +1 -1
- package/dist/events/publisher.js +4 -5
- package/dist/events/publisher.js.map +1 -1
- package/dist/procedures/collection/changes.d.ts +9 -8
- package/dist/procedures/collection/changes.d.ts.map +1 -1
- package/dist/procedures/collection/changes.js +11 -11
- package/dist/procedures/collection/changes.js.map +1 -1
- package/dist/procedures/collection/index.d.ts +1 -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 +4 -23
- package/dist/procedures/collection/partition.d.ts.map +1 -1
- package/dist/procedures/collection/partition.js +2 -28
- package/dist/procedures/collection/partition.js.map +1 -1
- package/dist/procedures/collection/topic.d.ts +86 -0
- package/dist/procedures/collection/topic.d.ts.map +1 -0
- package/dist/procedures/collection/topic.js +94 -0
- package/dist/procedures/collection/topic.js.map +1 -0
- package/dist/procedures/index.d.ts +1 -1
- 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/server/procedure-server.d.ts +4 -2
- package/dist/server/procedure-server.d.ts.map +1 -1
- package/dist/server/procedure-server.js +4 -6
- package/dist/server/procedure-server.js.map +1 -1
- package/package.json +1 -1
- package/src/browser.ts +1 -0
- package/src/events/publisher.ts +5 -6
- package/src/procedures/collection/changes.ts +12 -11
- package/src/procedures/collection/index.ts +2 -0
- package/src/procedures/collection/partition.ts +5 -27
- package/src/procedures/collection/topic.ts +115 -0
- package/src/procedures/index.ts +1 -0
- package/src/server/procedure-server.ts +4 -6
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The topics of the change events of a collection (roadmap CLI-15, CLI-17 and CLI-42). This module
|
|
3
|
+
* has the one function that makes a topic, the functions that read it, and the topic rule of a
|
|
4
|
+
* collection. Each part of the client that makes or reads the topic of a collection or of a
|
|
5
|
+
* partition uses them.
|
|
6
|
+
*
|
|
7
|
+
* - `collections.<name>` is the topic of a collection. The name stays as it is, also with a dot.
|
|
8
|
+
* - `collections.<name>.<partition>` is the topic of a partition. In this form, the name encodes
|
|
9
|
+
* `%` as `%25` and `.` as `%2E`. Thus the first dot after `collections.` ends the name, and the
|
|
10
|
+
* partition after that dot can have each character, also `.`, `/` and `%`.
|
|
11
|
+
* - The partition stays as it is, and so does a name without `.` and `%`. Thus each topic of a
|
|
12
|
+
* partition of a name without `.` and `%` stays the same as before CLI-42.
|
|
13
|
+
* - Only the canonical form of a topic counts: a parse gives a collection and a partition only when
|
|
14
|
+
* `collectionChangeTopic(collection, partition)` gives the same topic. Thus `%2e` is no alias of
|
|
15
|
+
* `%2E`, and a rule or an `allow` function that compares the strings sees each topic one way.
|
|
16
|
+
* - The topic of a collection whose name has a dot, `collections.a.b`, has the same form as the
|
|
17
|
+
* topic of the partition `b` of the collection `a`. Only the repository knows the collection, so
|
|
18
|
+
* `collectionTopicTarget()` decides with it.
|
|
19
|
+
*/
|
|
20
|
+
import type { RepositoryProvider } from "../types.js";
|
|
21
|
+
|
|
22
|
+
/** The prefix of the topic of each collection. */
|
|
23
|
+
const PREFIX = "collections.";
|
|
24
|
+
|
|
25
|
+
/** This function encodes the name of a collection for a topic with a partition: `%` and `.` become `%25` and `%2E`. */
|
|
26
|
+
function encodeTopicName(name: string): string {
|
|
27
|
+
return name.replace(/[%.]/g, (char) => (char === "%" ? "%25" : "%2E"));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** This function decodes the name of a topic with a partition. The parse then checks the canonical form. */
|
|
31
|
+
function decodeTopicName(segment: string): string {
|
|
32
|
+
return segment.replace(/%(25|2E)/g, (_sequence, code: string) => (code === "25" ? "%" : "."));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* This function gives the topic of the change events of a collection: `collections.<name>`, or
|
|
37
|
+
* `collections.<name>.<partition>` for a partition (roadmap CLI-17). In the topic of a partition,
|
|
38
|
+
* the name encodes `%` and `.` (roadmap CLI-42), so `collectionTopicPartition()` gives back the
|
|
39
|
+
* same collection and the same partition.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```typescript
|
|
43
|
+
* collectionChangeTopic("notes", "t1/board:b1"); // "collections.notes.t1/board:b1"
|
|
44
|
+
* collectionChangeTopic("graph.nodes", "acme/space"); // "collections.graph%2Enodes.acme/space"
|
|
45
|
+
* collectionChangeTopic("graph.nodes"); // "collections.graph.nodes"
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export function collectionChangeTopic(collection: string, partition?: string): string {
|
|
49
|
+
return partition === undefined ? `${PREFIX}${collection}` : `${PREFIX}${encodeTopicName(collection)}.${partition}`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* This function gives the collection and the partition of a topic `collections.<name>` or
|
|
54
|
+
* `collections.<name>.<partition>`, or undefined for another topic. It reads the topic alone:
|
|
55
|
+
* `collectionTopicTarget()` also knows the collections of a repository.
|
|
56
|
+
*
|
|
57
|
+
* - The first dot after the name ends the name. In a topic with a partition, the name is decoded:
|
|
58
|
+
* `%2E` is a dot and `%25` is a `%` (roadmap CLI-42).
|
|
59
|
+
* - Each character after that dot is the partition. A partition has one character at least.
|
|
60
|
+
* - A topic that is not in the canonical form gives undefined: `collectionChangeTopic()` of the
|
|
61
|
+
* result always gives the topic again. Thus `collections.a%2eb.t1` names no collection.
|
|
62
|
+
* - For each name and each partition, `collectionTopicPartition(collectionChangeTopic(name,
|
|
63
|
+
* partition))` gives the same name and the same partition.
|
|
64
|
+
*/
|
|
65
|
+
export function collectionTopicPartition(topic: string): { collection: string; partition: string | undefined } | undefined {
|
|
66
|
+
if (!topic.startsWith(PREFIX)) return undefined;
|
|
67
|
+
const rest = topic.slice(PREFIX.length);
|
|
68
|
+
const dot = rest.indexOf(".");
|
|
69
|
+
if (dot === -1) return rest === "" ? undefined : { collection: rest, partition: undefined };
|
|
70
|
+
if (dot === 0 || dot === rest.length - 1) return undefined;
|
|
71
|
+
const collection = decodeTopicName(rest.slice(0, dot));
|
|
72
|
+
const partition = rest.slice(dot + 1);
|
|
73
|
+
// Only the canonical form counts: an alias of an escape names no collection
|
|
74
|
+
return collectionChangeTopic(collection, partition) === topic ? { collection, partition } : undefined;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* This function gives the collection and the partition part of a topic of a call, with the
|
|
79
|
+
* repository of the call: one rule for the read check and for the listener of the publisher.
|
|
80
|
+
*
|
|
81
|
+
* - A collection of the repository whose name is the whole rest of the topic wins, with or without
|
|
82
|
+
* partitions: its name can have a dot. The call then asks for no partition.
|
|
83
|
+
* - Else `collectionTopicPartition()` reads the topic: the first dot after the name ends the name,
|
|
84
|
+
* and the rest is the partition that the call asks for. In this form, the name encodes `%` and
|
|
85
|
+
* `.`, so the name of a collection with partitions can have a dot (roadmap CLI-42).
|
|
86
|
+
*/
|
|
87
|
+
export function collectionTopicTarget(topic: string, repository: RepositoryProvider | undefined): { collection: string; partition: string | undefined } | undefined {
|
|
88
|
+
if (!topic.startsWith(PREFIX)) return undefined;
|
|
89
|
+
const whole = topic.slice(PREFIX.length);
|
|
90
|
+
if (whole !== "" && repository?.hasCollection(whole)) return { collection: whole, partition: undefined };
|
|
91
|
+
return collectionTopicPartition(topic);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* This function gives the topic rule of a collection for `EventPublisher` (the option `topics`):
|
|
96
|
+
* the topic of the collection, and the pattern of the topics of its partitions (roadmap CLI-42).
|
|
97
|
+
* Build the rule of a collection with it. Do not join the parts yourself: a dotted name needs its
|
|
98
|
+
* encoded form in the pattern.
|
|
99
|
+
*
|
|
100
|
+
* - The rule always has the two forms. For a collection with partitions, the plain topic asks for
|
|
101
|
+
* no partition, and the resolver of the partition decides.
|
|
102
|
+
* - For a collection without partitions, a topic of a partition passes the rule. The publisher then
|
|
103
|
+
* refuses it with `FORBIDDEN` (reason `partition-not-allowed`).
|
|
104
|
+
* - The pattern matches only the canonical form of a topic, so an alias of an escape gets
|
|
105
|
+
* `TOPIC_NOT_FOUND`.
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* ```typescript
|
|
109
|
+
* collectionTopicRule("graph.nodes"); // ["collections.graph.nodes", "collections.graph%2Enodes.*"]
|
|
110
|
+
* new EventPublisher({ bus, topics: ["orders.*", ...collectionTopicRule("graph.nodes")] });
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
export function collectionTopicRule(collection: string): string[] {
|
|
114
|
+
return [collectionChangeTopic(collection), `${PREFIX}${encodeTopicName(collection)}.*`];
|
|
115
|
+
}
|
package/src/procedures/index.ts
CHANGED
|
@@ -574,16 +574,14 @@ export class ProcedureServer extends Server implements RepositoryProvider, StopP
|
|
|
574
574
|
* for: the collection procedures and the subscriptions of `EventPublisher` use it. `storageOf`
|
|
575
575
|
* gives the storage of a partition: give the same storage for the same partition each time.
|
|
576
576
|
*
|
|
577
|
-
*
|
|
577
|
+
* The name can have a dot (roadmap CLI-42). The topic of a partition encodes the name, so the
|
|
578
|
+
* name and the partition of a topic stay apart (`collectionChangeTopic()`).
|
|
579
|
+
*
|
|
580
|
+
* @param name - Collection name
|
|
578
581
|
* @param partitions - The storage of each partition, and the resolver
|
|
579
582
|
* @returns this (for chaining)
|
|
580
|
-
* @throws Error if the name has a dot
|
|
581
583
|
*/
|
|
582
584
|
usePartitions<T>(name: string, partitions: CollectionPartitions<T>): this {
|
|
583
|
-
// The topic `collections.<name>.<partition>` names the partition after the first dot
|
|
584
|
-
if (name.includes(".")) {
|
|
585
|
-
throw new Error(`A collection with partitions cannot have a dot in its name: ${name}`);
|
|
586
|
-
}
|
|
587
585
|
this.partitions.set(name, partitions as CollectionPartitions<unknown>);
|
|
588
586
|
return this;
|
|
589
587
|
}
|