@peerbit/program 1.0.6 → 2.1.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.
@@ -0,0 +1,339 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var Program_1;
8
+ import { PublicSignKey, getPublicKeyFromPeerId } from "@peerbit/crypto";
9
+ import { getSchema, variant } from "@dao-xyz/borsh";
10
+ import { getValuesWithType } from "./utils.js";
11
+ import { serialize, deserialize } from "@dao-xyz/borsh";
12
+ import { CustomEvent, EventEmitter } from "@libp2p/interfaces/events";
13
+ import { waitForAsync } from "@peerbit/time";
14
+ import { Handler, } from "./handler.js";
15
+ const intersection = (a, b) => {
16
+ const newSet = new Set();
17
+ for (const el of b) {
18
+ if (!a || a.has(el)) {
19
+ newSet.add(el);
20
+ }
21
+ }
22
+ return newSet;
23
+ };
24
+ const getAllParentAddresses = (p) => {
25
+ return getAllParent(p, [])
26
+ .filter((x) => x instanceof Program)
27
+ .map((x) => x.address);
28
+ };
29
+ const getAllParent = (a, arr = [], includeThis = false) => {
30
+ includeThis && arr.push(a);
31
+ if (a.parents) {
32
+ for (const p of a.parents) {
33
+ if (p) {
34
+ getAllParent(p, arr, true);
35
+ }
36
+ }
37
+ }
38
+ return arr;
39
+ };
40
+ class ProgramHandler extends Handler {
41
+ constructor(properties) {
42
+ super({
43
+ client: properties.client,
44
+ shouldMonitor: (p) => p instanceof Program,
45
+ load: Program.load,
46
+ });
47
+ }
48
+ }
49
+ export { ProgramHandler };
50
+ export let Program = Program_1 = class Program {
51
+ _node;
52
+ _allPrograms;
53
+ _events;
54
+ _closed;
55
+ parents;
56
+ children;
57
+ _address;
58
+ get address() {
59
+ if (!this._address) {
60
+ throw new Error("Address does not exist, please open or save this program once to obtain it");
61
+ }
62
+ return this._address;
63
+ }
64
+ set address(address) {
65
+ this._address = address;
66
+ }
67
+ addParent(program) {
68
+ (this.parents || (this.parents = [])).push(program);
69
+ if (program) {
70
+ (program.children || (program.children = [])).push(this);
71
+ }
72
+ }
73
+ get events() {
74
+ return this._events || (this._events = new EventEmitter());
75
+ }
76
+ get closed() {
77
+ if (this._closed == null) {
78
+ return true;
79
+ }
80
+ return this._closed;
81
+ }
82
+ set closed(closed) {
83
+ this._closed = closed;
84
+ }
85
+ get node() {
86
+ return this._node;
87
+ }
88
+ set node(node) {
89
+ this._node = node;
90
+ }
91
+ _eventOptions;
92
+ async beforeOpen(node, options) {
93
+ // check that a discriminator exist
94
+ const schema = getSchema(this.constructor);
95
+ if (!schema || typeof schema.variant !== "string") {
96
+ throw new Error(`Expecting class to be decorated with a string variant. Example:\n\'import { variant } "@dao-xyz/borsh"\n@variant("example-db")\nclass ${this.constructor.name} { ...`);
97
+ }
98
+ await this.save(node.services.blocks);
99
+ if (getAllParentAddresses(this).includes(this.address)) {
100
+ throw new Error("Subprogram has same address as some parent program. This is not currently supported");
101
+ }
102
+ if (!this.closed) {
103
+ this.addParent(options?.parent);
104
+ return;
105
+ }
106
+ else {
107
+ this.addParent(options?.parent);
108
+ }
109
+ this._eventOptions = options;
110
+ this.node = node;
111
+ const nexts = this.programs;
112
+ for (const next of nexts) {
113
+ await next.beforeOpen(node, { ...options, parent: this });
114
+ }
115
+ await this.node.services.pubsub.addEventListener("subscribe", this._subscriptionEventListener ||
116
+ (this._subscriptionEventListener = (s) => !this.closed && this._emitJoinNetworkEvents(s.detail)));
117
+ await this.node.services.pubsub.addEventListener("unsubscribe", this._unsubscriptionEventListener ||
118
+ (this._unsubscriptionEventListener = (s) => !this.closed && this._emitLeaveNetworkEvents(s.detail)));
119
+ await this._eventOptions?.onBeforeOpen?.(this);
120
+ }
121
+ async afterOpen() {
122
+ this.emitEvent(new CustomEvent("open", { detail: this }), true);
123
+ await this._eventOptions?.onOpen?.(this);
124
+ this.closed = false;
125
+ const nexts = this.programs;
126
+ for (const next of nexts) {
127
+ await next.afterOpen();
128
+ }
129
+ }
130
+ _clear() {
131
+ this._allPrograms = undefined;
132
+ }
133
+ async _emitJoinNetworkEvents(s) {
134
+ const allTopics = this.programs
135
+ .map((x) => x.getTopics?.())
136
+ .filter((x) => x)
137
+ .flat();
138
+ // if subscribing to all topics, emit "join" event
139
+ for (const topic of allTopics) {
140
+ if (!(await this.node.services.pubsub.getSubscribers(topic))?.has(s.from.hashcode())) {
141
+ return;
142
+ }
143
+ }
144
+ this.events.dispatchEvent(new CustomEvent("join", { detail: s.from }));
145
+ }
146
+ async _emitLeaveNetworkEvents(s) {
147
+ const allTopics = this.programs
148
+ .map((x) => x.getTopics?.())
149
+ .filter((x) => x)
150
+ .flat();
151
+ // if subscribing not subscribing to any topics, emit "leave" event
152
+ for (const topic of allTopics) {
153
+ if ((await this.node.services.pubsub.getSubscribers(topic))?.has(s.from.hashcode())) {
154
+ return;
155
+ }
156
+ }
157
+ this.events.dispatchEvent(new CustomEvent("leave", { detail: s.from }));
158
+ }
159
+ _subscriptionEventListener;
160
+ _unsubscriptionEventListener;
161
+ async processEnd(type) {
162
+ if (!this.closed) {
163
+ this.emitEvent(new CustomEvent(type, { detail: this }), true);
164
+ if (type === "close") {
165
+ this._eventOptions?.onClose?.(this);
166
+ }
167
+ else if (type === "drop") {
168
+ this._eventOptions?.onDrop?.(this);
169
+ }
170
+ else {
171
+ throw new Error("Unsupported event type: " + type);
172
+ }
173
+ const promises = [];
174
+ if (this.children) {
175
+ for (const program of this.children) {
176
+ promises.push(program[type](this)); // TODO types
177
+ }
178
+ this.children = [];
179
+ }
180
+ await Promise.all(promises);
181
+ this._clear();
182
+ this.closed = true;
183
+ return true;
184
+ }
185
+ else {
186
+ this._clear();
187
+ return true;
188
+ }
189
+ }
190
+ async end(type, from) {
191
+ if (this.closed) {
192
+ return true;
193
+ }
194
+ let parentIdx = -1;
195
+ let close = true;
196
+ if (this.parents) {
197
+ parentIdx = this.parents.findIndex((x) => x == from);
198
+ if (parentIdx !== -1) {
199
+ if (this.parents.length === 1) {
200
+ close = true;
201
+ }
202
+ else {
203
+ this.parents.splice(parentIdx, 1);
204
+ close = false;
205
+ }
206
+ }
207
+ else if (from) {
208
+ throw new Error("Could not find from in parents");
209
+ }
210
+ }
211
+ const end = close && (await this.processEnd(type));
212
+ if (end) {
213
+ this.node?.services.pubsub.removeEventListener("subscribe", this._subscriptionEventListener);
214
+ this.node?.services.pubsub.removeEventListener("unsubscribe", this._unsubscriptionEventListener);
215
+ this._eventOptions = undefined;
216
+ if (parentIdx !== -1) {
217
+ this.parents.splice(parentIdx, 1); // We splice this here because this._end depends on this parent to exist
218
+ }
219
+ }
220
+ return end;
221
+ }
222
+ async close(from) {
223
+ return this.end("close", from);
224
+ }
225
+ async drop(from) {
226
+ const dropped = await this.end("drop", from);
227
+ if (dropped) {
228
+ await this.delete();
229
+ }
230
+ return dropped;
231
+ }
232
+ emitEvent(event, parents = false) {
233
+ this.events.dispatchEvent(event);
234
+ if (parents) {
235
+ if (this.parents) {
236
+ for (const parent of this.parents) {
237
+ parent?.emitEvent(event);
238
+ }
239
+ }
240
+ }
241
+ }
242
+ /**
243
+ * Wait for another peer to be 'ready' to talk with you for this particular program
244
+ * @param other
245
+ */
246
+ async waitFor(...other) {
247
+ const expectedHashes = new Set(other.map((x) => x instanceof PublicSignKey
248
+ ? x.hashcode()
249
+ : getPublicKeyFromPeerId(x).hashcode()));
250
+ await waitForAsync(async () => {
251
+ return (intersection(expectedHashes, await this.getReady()).size ===
252
+ expectedHashes.size);
253
+ }, { delayInterval: 200, timeout: 10 * 1000 }); // 200 ms delay since this is an expensive op. TODO, make event based instead
254
+ }
255
+ async getReady() {
256
+ // all peers that subscribe to all topics
257
+ let ready = undefined; // the interesection of all ready
258
+ for (const program of this.allPrograms) {
259
+ if (program.getTopics) {
260
+ const topics = program.getTopics();
261
+ for (const topic of topics) {
262
+ const subscribers = await this.node.services.pubsub.getSubscribers(topic);
263
+ if (!subscribers) {
264
+ throw new Error("client is not subscriber to topic data, do not have any info about peer readiness");
265
+ }
266
+ ready = intersection(ready, subscribers.keys());
267
+ }
268
+ }
269
+ }
270
+ if (ready == null) {
271
+ throw new Error("Do not have any info about peer readiness");
272
+ }
273
+ return ready;
274
+ }
275
+ get allPrograms() {
276
+ if (this._allPrograms) {
277
+ return this._allPrograms;
278
+ }
279
+ const arr = this.programs;
280
+ const nexts = this.programs;
281
+ for (const next of nexts) {
282
+ arr.push(...next.allPrograms);
283
+ }
284
+ this._allPrograms = arr;
285
+ return this._allPrograms;
286
+ }
287
+ get programs() {
288
+ return getValuesWithType(this, Program_1);
289
+ }
290
+ clone() {
291
+ return deserialize(serialize(this), this.constructor);
292
+ }
293
+ async save(store = this.node.services.blocks) {
294
+ const existingAddress = this._address;
295
+ const hash = await store.put(serialize(this));
296
+ this._address = hash;
297
+ if (!this.address) {
298
+ throw new Error("Unexpected");
299
+ }
300
+ if (existingAddress && existingAddress !== this.address) {
301
+ throw new Error("Program properties has been changed after constructor so that the hash has changed. Make sure that the 'setup(...)' function does not modify any properties that are to be serialized");
302
+ }
303
+ return this._address;
304
+ }
305
+ async delete() {
306
+ if (this.address) {
307
+ return this.node.services.blocks.rm(this.address);
308
+ }
309
+ // Not saved
310
+ }
311
+ static async load(address, store, options) {
312
+ const bytes = await store.get(address, options);
313
+ if (!bytes) {
314
+ return undefined;
315
+ }
316
+ const der = deserialize(bytes, Program_1);
317
+ der.address = address;
318
+ return der;
319
+ }
320
+ static async open(address, node, options) {
321
+ const p = await Program_1.load(address, node.services.blocks);
322
+ if (!p) {
323
+ throw new Error("Failed to load program");
324
+ }
325
+ await node.open(p, options);
326
+ return p;
327
+ }
328
+ };
329
+ Program = Program_1 = __decorate([
330
+ variant(0)
331
+ ], Program);
332
+ export const getProgramFromVariants = () => {
333
+ const deps = Program.prototype[1000]; /// TODO improve BORSH lib to provide all necessary utility methods
334
+ return (deps || []);
335
+ };
336
+ export const getProgramFromVariant = (variant) => {
337
+ return getProgramFromVariants().filter((x) => getSchema(x).variant === variant)[0];
338
+ };
339
+ //# sourceMappingURL=program.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"program.js","sourceRoot":"","sources":["../../src/program.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peerbit/program",
3
- "version": "1.0.6",
3
+ "version": "2.1.0",
4
4
  "description": "Program interface",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -30,10 +30,10 @@
30
30
  "license": "MIT",
31
31
  "dependencies": {
32
32
  "@dao-xyz/borsh": "^5.1.5",
33
- "@peerbit/blocks-interface": "^1.0.4",
34
- "@peerbit/crypto": "1.0.3",
35
- "@peerbit/lazy-level": "^1.0.1",
36
- "@peerbit/pubsub-interface": "^1.0.4"
33
+ "@peerbit/blocks-interface": "^1.0.5",
34
+ "@peerbit/crypto": "1.0.4",
35
+ "@peerbit/lazy-level": "^1.0.2",
36
+ "@peerbit/pubsub-interface": "^1.0.5"
37
37
  },
38
- "gitHead": "99e30817f094e45ffcd60f4babd90a717bd26a22"
38
+ "gitHead": "4752c301fb49b2f937b4039533ff5bc3537da3e0"
39
39
  }
@@ -4,13 +4,10 @@ import { PubSub } from "@peerbit/pubsub-interface";
4
4
  import { Ed25519PublicKey, Identity, Keychain } from "@peerbit/crypto";
5
5
  import type { SimpleLevel } from "@peerbit/lazy-level";
6
6
  import { Multiaddr } from "@multiformats/multiaddr";
7
- import { Address } from "./address";
7
+ import { Address } from "./address.js";
8
+ import { CanOpen, Manageable, OpenOptions } from "./handler.js";
8
9
 
9
- export type WithArgs<Args> = { args?: Args };
10
- export type WithParent<T> = { parent?: T };
11
- export type CanOpen<Args> = { open(args?: Args): Promise<void> };
12
-
13
- export interface Client<T extends P, P> {
10
+ export interface Client<T extends Manageable<any>> {
14
11
  peerId: Libp2pPeerId;
15
12
  identity: Identity<Ed25519PublicKey>;
16
13
  getMultiaddrs: () => Multiaddr[];
@@ -19,12 +16,12 @@ export interface Client<T extends P, P> {
19
16
  pubsub: PubSub;
20
17
  blocks: Blocks;
21
18
  };
22
- memory?: SimpleLevel;
23
- keychain?: Keychain;
19
+ memory: SimpleLevel;
20
+ keychain: Keychain;
24
21
  start(): Promise<void>;
25
22
  stop(): Promise<void>;
26
- open<TExt extends T & CanOpen<Args>, Args = any>(
27
- program: TExt | Address,
28
- options?: WithArgs<Args> & WithParent<P>
29
- ): Promise<TExt>;
23
+ open<S extends T & CanOpen<Args>, Args = any>(
24
+ program: S | Address,
25
+ options?: OpenOptions<Args, S>
26
+ ): Promise<S>;
30
27
  }
package/src/handler.ts ADDED
@@ -0,0 +1,244 @@
1
+ import { Blocks } from "@peerbit/blocks-interface";
2
+ import PQueue from "p-queue";
3
+ import { Address } from "./address.js";
4
+ import { logger as loggerFn } from "@peerbit/logger";
5
+
6
+ export const logger = loggerFn({ module: "program-handler" });
7
+
8
+ type ProgramMergeStrategy = "replace" | "reject" | "reuse";
9
+
10
+ export type EventOptions = {
11
+ onBeforeOpen?: (program: Manageable<any>) => Promise<void> | void;
12
+ onOpen?: (program: Manageable<any>) => Promise<void> | void;
13
+ onDrop?: (program: Manageable<any>) => Promise<void> | void;
14
+ onClose?: (program: Manageable<any>) => Promise<void> | void;
15
+ };
16
+
17
+ export type OpenOptions<Args, T extends Manageable<Args>> = {
18
+ timeout?: number;
19
+ existing?: ProgramMergeStrategy;
20
+ /*
21
+ reset?: boolean; */
22
+ } & ProgramInitializationOptions<Args, T>;
23
+
24
+ export type WithArgs<Args> = { args?: Args };
25
+ export type WithParent<T> = { parent?: T };
26
+ export type Closeable = { closed: boolean; close(): Promise<boolean> };
27
+ export type Addressable = { address: Address };
28
+ export interface Saveable {
29
+ save(
30
+ store: Blocks,
31
+ options?: {
32
+ format?: string;
33
+ timeout?: number;
34
+ }
35
+ ): Promise<Address>;
36
+
37
+ delete(): Promise<void>;
38
+ }
39
+ export type CanEmit = { emitEvent: (event: CustomEvent) => void };
40
+
41
+ export type CanOpen<Args> = {
42
+ beforeOpen: (client: any, options?: EventOptions) => Promise<void>;
43
+ open(args?: Args): Promise<void>;
44
+ afterOpen: () => Promise<void>;
45
+ };
46
+
47
+ export type Manageable<Args> = Closeable &
48
+ Addressable &
49
+ CanOpen<Args> &
50
+ Saveable &
51
+ CanEmit & {
52
+ children: Manageable<any>[];
53
+ parents: (Manageable<any> | undefined)[];
54
+ };
55
+
56
+ export type ProgramInitializationOptions<Args, T extends Manageable<Args>> = {
57
+ // TODO
58
+ // reset: boolean
59
+ } & WithArgs<Args> &
60
+ WithParent<T> &
61
+ EventOptions;
62
+
63
+ export class Handler<T extends Manageable<any>> {
64
+ items: Map<string, T>;
65
+ private _openQueue: PQueue;
66
+
67
+ constructor(
68
+ readonly properties: {
69
+ client: { services: { blocks: Blocks }; stop: () => Promise<void> };
70
+ load: (
71
+ address: Address,
72
+ blocks: Blocks,
73
+ options?: { timeout?: number }
74
+ ) => Promise<T | undefined>;
75
+ shouldMonitor: (thing: any) => boolean;
76
+ }
77
+ ) {
78
+ this._openQueue = new PQueue({ concurrency: 1 });
79
+ this.items = new Map();
80
+ }
81
+
82
+ async stop() {
83
+ this._openQueue.clear();
84
+ await this._openQueue.onIdle();
85
+
86
+ // Close all open databases
87
+ await Promise.all(
88
+ [...this.items.values()].map((program) => program.close())
89
+ );
90
+
91
+ // Remove all databases from the state
92
+ this.items = new Map();
93
+ }
94
+
95
+ private _onProgamClose(program: Manageable<any>) {
96
+ this.items.delete(program.address!.toString());
97
+ }
98
+
99
+ private async _onProgramOpen(
100
+ program: T,
101
+ mergeSrategy?: ProgramMergeStrategy
102
+ ) {
103
+ const programAddress = program.address?.toString();
104
+ if (!programAddress) {
105
+ throw new Error("Missing program address");
106
+ }
107
+ if (this.items.has(programAddress)) {
108
+ // second condition only makes this throw error if we are to add a new instance with the same address
109
+ const existing = await this.checkProcessExisting(
110
+ programAddress,
111
+ program,
112
+ mergeSrategy
113
+ );
114
+ if (!existing) {
115
+ throw new Error("Unexpected");
116
+ }
117
+ this.items.set(programAddress, program);
118
+ } else {
119
+ this.items.set(programAddress, program);
120
+ }
121
+ }
122
+
123
+ private async checkProcessExisting<S extends T>(
124
+ address: Address,
125
+ toOpen: Manageable<any>,
126
+ mergeSrategy: ProgramMergeStrategy = "reject"
127
+ ): Promise<S | undefined> {
128
+ const prev = this.items.get(address);
129
+ if (mergeSrategy === "reject") {
130
+ if (prev) {
131
+ throw new Error(`Program at ${address} is already open`);
132
+ }
133
+ } else if (mergeSrategy === "replace") {
134
+ if (prev && prev !== toOpen) {
135
+ await prev.close(); // clouse previous
136
+ }
137
+ } else if (mergeSrategy === "reuse") {
138
+ return prev as S;
139
+ }
140
+ }
141
+
142
+ async open<S extends T, Args = any>(
143
+ storeOrAddress: S | Address | string,
144
+ options: OpenOptions<Args, S> = {}
145
+ ): Promise<S> {
146
+ const fn = async (): Promise<S> => {
147
+ // TODO add locks for store lifecycle, e.g. what happens if we try to open and close a store at the same time?
148
+ let program = storeOrAddress as S;
149
+ if (typeof storeOrAddress === "string") {
150
+ try {
151
+ if (this.items?.has(storeOrAddress.toString())) {
152
+ const existing = await this.checkProcessExisting(
153
+ storeOrAddress.toString(),
154
+ program,
155
+ options?.existing
156
+ );
157
+ if (existing) {
158
+ return existing as S;
159
+ }
160
+ } else {
161
+ program = (await this.properties.load(
162
+ storeOrAddress,
163
+ this.properties.client.services.blocks,
164
+ options
165
+ )) as S; // TODO fix typings
166
+ if (!this.properties.shouldMonitor(program)) {
167
+ throw new Error(
168
+ `Failed to open program because program is of type ${program?.constructor.name} `
169
+ );
170
+ }
171
+ }
172
+ } catch (error) {
173
+ logger.error(
174
+ "Failed to load store with address: " + storeOrAddress.toString()
175
+ );
176
+ throw error;
177
+ }
178
+ } else if (!program.closed) {
179
+ const existing = this.items.get(program.address);
180
+ if (existing === program) {
181
+ return program;
182
+ } else if (existing) {
183
+ const existing = await this.checkProcessExisting(
184
+ program.address,
185
+ program,
186
+ options?.existing
187
+ );
188
+
189
+ if (existing) {
190
+ return existing as S;
191
+ }
192
+ }
193
+ }
194
+
195
+ logger.debug(`Open database '${program.constructor.name}`);
196
+ const address = await program.save(
197
+ this.properties.client.services.blocks
198
+ );
199
+ const existing = await this.checkProcessExisting(
200
+ address,
201
+ program,
202
+ options?.existing
203
+ );
204
+ if (existing) {
205
+ return existing as S;
206
+ }
207
+ await program.beforeOpen(this.properties.client, {
208
+ onBeforeOpen: async (p) => {
209
+ if (
210
+ this.properties.shouldMonitor(p) &&
211
+ p.parents.length === 1 &&
212
+ !p.parents[0]
213
+ ) {
214
+ return this._onProgramOpen(p as T, options?.existing); // TODO types
215
+ }
216
+ },
217
+ onClose: (p) => {
218
+ if (this.properties.shouldMonitor(p)) {
219
+ return this._onProgamClose(p as T); // TODO types
220
+ }
221
+ },
222
+ onDrop: (p) => {
223
+ if (this.properties.shouldMonitor(p)) {
224
+ return this._onProgamClose(p);
225
+ }
226
+ },
227
+ ...options,
228
+ // If the program opens more programs
229
+ // reset: options.reset,
230
+ });
231
+
232
+ await program.open(options.args);
233
+ await program.afterOpen();
234
+ return program as S;
235
+ };
236
+
237
+ // Prevent deadlocks when a program is opened by another program
238
+ // TODO make proper deduplciation behaviour
239
+ if (options?.parent) {
240
+ return fn();
241
+ }
242
+ return this._openQueue.add(fn) as any as S; // TODO p-queue seem to return void type ;
243
+ }
244
+ }