@peerbit/program 1.0.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/LICENSE +22 -0
- package/lib/esm/address.d.ts +1 -0
- package/lib/esm/address.js +2 -0
- package/lib/esm/address.js.map +1 -0
- package/lib/esm/index.d.ts +99 -0
- package/lib/esm/index.js +344 -0
- package/lib/esm/index.js.map +1 -0
- package/lib/esm/node.d.ts +31 -0
- package/lib/esm/node.js +2 -0
- package/lib/esm/node.js.map +1 -0
- package/lib/esm/package.json +3 -0
- package/lib/esm/utils.d.ts +2 -0
- package/lib/esm/utils.js +58 -0
- package/lib/esm/utils.js.map +1 -0
- package/package.json +40 -0
- package/src/address.ts +1 -0
- package/src/index.ts +516 -0
- package/src/node.ts +30 -0
- package/src/utils.ts +73 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021 dao.xyz
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type Address = string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"address.js","sourceRoot":"","sources":["../../src/address.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { PublicSignKey } from "@peerbit/crypto";
|
|
2
|
+
import { Constructor } from "@dao-xyz/borsh";
|
|
3
|
+
import { EventEmitter } from "@libp2p/interfaces/events";
|
|
4
|
+
import { Client } from "./node.js";
|
|
5
|
+
import { Blocks } from "@peerbit/blocks-interface";
|
|
6
|
+
import { PeerId as Libp2pPeerId } from "@libp2p/interface-peer-id";
|
|
7
|
+
import { Address } from "./address.js";
|
|
8
|
+
export type { Address };
|
|
9
|
+
export interface Addressable {
|
|
10
|
+
address?: Address | undefined;
|
|
11
|
+
}
|
|
12
|
+
export interface Saveable {
|
|
13
|
+
save(store: Blocks, options?: {
|
|
14
|
+
format?: string;
|
|
15
|
+
timeout?: number;
|
|
16
|
+
}): Promise<Address>;
|
|
17
|
+
delete(): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
export type OpenProgram = (program: Program) => Promise<Program>;
|
|
20
|
+
export interface NetworkEvents {
|
|
21
|
+
join: CustomEvent<PublicSignKey>;
|
|
22
|
+
leave: CustomEvent<PublicSignKey>;
|
|
23
|
+
}
|
|
24
|
+
export interface LifeCycleEvents {
|
|
25
|
+
drop: CustomEvent<Program>;
|
|
26
|
+
open: CustomEvent<Program>;
|
|
27
|
+
close: CustomEvent<Program>;
|
|
28
|
+
}
|
|
29
|
+
export interface ProgramEvents extends NetworkEvents, LifeCycleEvents {
|
|
30
|
+
}
|
|
31
|
+
type EventOptions = {
|
|
32
|
+
onOpen?: (program: AbstractProgram<any>) => Promise<void> | void;
|
|
33
|
+
onDrop?: (program: AbstractProgram<any>) => Promise<void> | void;
|
|
34
|
+
onClose?: (program: AbstractProgram<any>) => Promise<void> | void;
|
|
35
|
+
};
|
|
36
|
+
export type ProgramInitializationOptions<Args> = {
|
|
37
|
+
args?: Args;
|
|
38
|
+
parent?: AbstractProgram;
|
|
39
|
+
} & EventOptions;
|
|
40
|
+
export type ProgramClient = Client<Program, AbstractProgram>;
|
|
41
|
+
export declare abstract class AbstractProgram<Args = any, Events extends ProgramEvents = ProgramEvents> {
|
|
42
|
+
private _node;
|
|
43
|
+
private _allPrograms;
|
|
44
|
+
private _events;
|
|
45
|
+
private _closed;
|
|
46
|
+
parents: (AbstractProgram | undefined)[];
|
|
47
|
+
children: AbstractProgram[];
|
|
48
|
+
addParent(program: AbstractProgram | undefined): void;
|
|
49
|
+
get events(): EventEmitter<Events>;
|
|
50
|
+
get closed(): boolean;
|
|
51
|
+
set closed(closed: boolean);
|
|
52
|
+
get node(): ProgramClient;
|
|
53
|
+
set node(node: ProgramClient);
|
|
54
|
+
private _eventOptions;
|
|
55
|
+
beforeOpen(node: ProgramClient, options?: ProgramInitializationOptions<Args>): Promise<this>;
|
|
56
|
+
afterOpen(): Promise<this>;
|
|
57
|
+
abstract open(args?: Args): Promise<void>;
|
|
58
|
+
private _clear;
|
|
59
|
+
private _emitJoinNetworkEvents;
|
|
60
|
+
private _emitLeaveNetworkEvents;
|
|
61
|
+
private _subscriptionEventListener;
|
|
62
|
+
private _unsubscriptionEventListener;
|
|
63
|
+
private _end;
|
|
64
|
+
close(from?: AbstractProgram): Promise<boolean>;
|
|
65
|
+
drop(): Promise<void>;
|
|
66
|
+
emitEvent(event: CustomEvent, parents?: boolean): void;
|
|
67
|
+
/**
|
|
68
|
+
* Wait for another peer to be 'ready' to talk with you for this particular program
|
|
69
|
+
* @param other
|
|
70
|
+
*/
|
|
71
|
+
waitFor(...other: (PublicSignKey | Libp2pPeerId)[]): Promise<void>;
|
|
72
|
+
getReady(): Promise<Set<string>>;
|
|
73
|
+
get allPrograms(): AbstractProgram[];
|
|
74
|
+
get programs(): AbstractProgram[];
|
|
75
|
+
clone(): this;
|
|
76
|
+
getTopics?(): string[];
|
|
77
|
+
}
|
|
78
|
+
export interface CanTrust {
|
|
79
|
+
isTrusted(keyHash: string): Promise<boolean> | boolean;
|
|
80
|
+
}
|
|
81
|
+
export declare abstract class Program<Args = any, Events extends ProgramEvents = ProgramEvents> extends AbstractProgram<Args, Events> implements Addressable, Saveable {
|
|
82
|
+
private _address?;
|
|
83
|
+
constructor();
|
|
84
|
+
get address(): Address;
|
|
85
|
+
set address(address: Address);
|
|
86
|
+
beforeOpen(node: ProgramClient, options?: ProgramInitializationOptions<Args>): Promise<this>;
|
|
87
|
+
static open<T extends Program<Args>, Args = any>(this: Constructor<T>, address: Address, node: ProgramClient, options?: ProgramInitializationOptions<Args>): Promise<T>;
|
|
88
|
+
save(store?: Blocks): Promise<Address>;
|
|
89
|
+
delete(): Promise<void>;
|
|
90
|
+
static load<P extends Program<any>>(address: Address, store: Blocks, options?: {
|
|
91
|
+
timeout?: number;
|
|
92
|
+
}): Promise<P | undefined>;
|
|
93
|
+
drop(): Promise<void>;
|
|
94
|
+
}
|
|
95
|
+
/**eve
|
|
96
|
+
* Building block, but not something you use as a standalone
|
|
97
|
+
*/
|
|
98
|
+
export declare abstract class ComposableProgram<Args = any, Events extends ProgramEvents = ProgramEvents> extends AbstractProgram<Args, Events> {
|
|
99
|
+
}
|
package/lib/esm/index.js
ADDED
|
@@ -0,0 +1,344 @@
|
|
|
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 __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
var AbstractProgram_1, Program_1;
|
|
11
|
+
import { PublicSignKey, getPublicKeyFromPeerId } from "@peerbit/crypto";
|
|
12
|
+
import { getSchema, variant } from "@dao-xyz/borsh";
|
|
13
|
+
import { getValuesWithType } from "./utils.js";
|
|
14
|
+
import { serialize, deserialize } from "@dao-xyz/borsh";
|
|
15
|
+
import { CustomEvent, EventEmitter } from "@libp2p/interfaces/events";
|
|
16
|
+
import { waitForAsync } from "@peerbit/time";
|
|
17
|
+
import { setMaxListeners } from "events";
|
|
18
|
+
setMaxListeners(Infinity);
|
|
19
|
+
const intersection = (a, b) => {
|
|
20
|
+
const newSet = new Set();
|
|
21
|
+
for (const el of b) {
|
|
22
|
+
if (!a || a.has(el)) {
|
|
23
|
+
newSet.add(el);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return newSet;
|
|
27
|
+
};
|
|
28
|
+
const getAllParentAddresses = (p) => {
|
|
29
|
+
return getAllParent(p, [])
|
|
30
|
+
.filter((x) => x instanceof Program)
|
|
31
|
+
.map((x) => x.address);
|
|
32
|
+
};
|
|
33
|
+
const getAllParent = (a, arr = [], includeThis = false) => {
|
|
34
|
+
includeThis && arr.push(a);
|
|
35
|
+
if (a.parents) {
|
|
36
|
+
for (const p of a.parents) {
|
|
37
|
+
if (p) {
|
|
38
|
+
getAllParent(p, arr, true);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return arr;
|
|
43
|
+
};
|
|
44
|
+
export let AbstractProgram = AbstractProgram_1 = class AbstractProgram {
|
|
45
|
+
_node;
|
|
46
|
+
_allPrograms;
|
|
47
|
+
_events;
|
|
48
|
+
_closed;
|
|
49
|
+
parents;
|
|
50
|
+
children;
|
|
51
|
+
addParent(program) {
|
|
52
|
+
(this.parents || (this.parents = [])).push(program);
|
|
53
|
+
if (program) {
|
|
54
|
+
(program.children || (program.children = [])).push(this);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
get events() {
|
|
58
|
+
return this._events || (this._events = new EventEmitter());
|
|
59
|
+
}
|
|
60
|
+
get closed() {
|
|
61
|
+
if (this._closed == null) {
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
return this._closed;
|
|
65
|
+
}
|
|
66
|
+
set closed(closed) {
|
|
67
|
+
this._closed = closed;
|
|
68
|
+
}
|
|
69
|
+
get node() {
|
|
70
|
+
return this._node;
|
|
71
|
+
}
|
|
72
|
+
set node(node) {
|
|
73
|
+
this._node = node;
|
|
74
|
+
}
|
|
75
|
+
_eventOptions;
|
|
76
|
+
async beforeOpen(node, options) {
|
|
77
|
+
if (!this.closed) {
|
|
78
|
+
this.addParent(options?.parent);
|
|
79
|
+
return this;
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
this.addParent(options?.parent);
|
|
83
|
+
}
|
|
84
|
+
this._eventOptions = options;
|
|
85
|
+
this.node = node;
|
|
86
|
+
const nexts = this.programs;
|
|
87
|
+
for (const next of nexts) {
|
|
88
|
+
await next.beforeOpen(node, { ...options, parent: this });
|
|
89
|
+
}
|
|
90
|
+
this.node.services.pubsub.addEventListener("subscribe", this._subscriptionEventListener ||
|
|
91
|
+
(this._subscriptionEventListener = (s) => !this.closed && this._emitJoinNetworkEvents(s.detail)));
|
|
92
|
+
this.node.services.pubsub.addEventListener("unsubscribe", this._unsubscriptionEventListener ||
|
|
93
|
+
(this._unsubscriptionEventListener = (s) => !this.closed && this._emitLeaveNetworkEvents(s.detail)));
|
|
94
|
+
return this;
|
|
95
|
+
}
|
|
96
|
+
async afterOpen() {
|
|
97
|
+
this.emitEvent(new CustomEvent("open", { detail: this }), true);
|
|
98
|
+
this._eventOptions?.onOpen?.(this);
|
|
99
|
+
this.closed = false;
|
|
100
|
+
const nexts = this.programs;
|
|
101
|
+
for (const next of nexts) {
|
|
102
|
+
await next.afterOpen();
|
|
103
|
+
}
|
|
104
|
+
return this;
|
|
105
|
+
}
|
|
106
|
+
_clear() {
|
|
107
|
+
this._allPrograms = undefined;
|
|
108
|
+
}
|
|
109
|
+
async _emitJoinNetworkEvents(s) {
|
|
110
|
+
const allTopics = this.programs
|
|
111
|
+
.map((x) => x.getTopics?.())
|
|
112
|
+
.filter((x) => x)
|
|
113
|
+
.flat();
|
|
114
|
+
// if subscribing to all topics, emit "join" event
|
|
115
|
+
for (const topic of allTopics) {
|
|
116
|
+
if (!this.node.services.pubsub.getSubscribers(topic)?.has(s.from.hashcode())) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
this.events.dispatchEvent(new CustomEvent("join", { detail: s.from }));
|
|
121
|
+
}
|
|
122
|
+
async _emitLeaveNetworkEvents(s) {
|
|
123
|
+
const allTopics = this.programs
|
|
124
|
+
.map((x) => x.getTopics?.())
|
|
125
|
+
.filter((x) => x)
|
|
126
|
+
.flat();
|
|
127
|
+
// if subscribing not subscribing to any topics, emit "leave" event
|
|
128
|
+
for (const topic of allTopics) {
|
|
129
|
+
if (this.node.services.pubsub.getSubscribers(topic)?.has(s.from.hashcode())) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
this.events.dispatchEvent(new CustomEvent("leave", { detail: s.from }));
|
|
134
|
+
}
|
|
135
|
+
_subscriptionEventListener;
|
|
136
|
+
_unsubscriptionEventListener;
|
|
137
|
+
async _end(type) {
|
|
138
|
+
if (!this.closed) {
|
|
139
|
+
this.emitEvent(new CustomEvent(type, { detail: this }), true);
|
|
140
|
+
if (type === "close") {
|
|
141
|
+
this._eventOptions?.onClose?.(this);
|
|
142
|
+
}
|
|
143
|
+
else if (type === "drop") {
|
|
144
|
+
this._eventOptions?.onDrop?.(this);
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
throw new Error("Unsupported event type: " + type);
|
|
148
|
+
}
|
|
149
|
+
const promises = [];
|
|
150
|
+
if (this.children) {
|
|
151
|
+
for (const program of this.children) {
|
|
152
|
+
promises.push(program[type](this)); // TODO types
|
|
153
|
+
}
|
|
154
|
+
this.children = [];
|
|
155
|
+
}
|
|
156
|
+
await Promise.all(promises);
|
|
157
|
+
this._clear();
|
|
158
|
+
this.closed = true;
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
this._clear();
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
async close(from) {
|
|
167
|
+
if (this.closed) {
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
let parentIdx = -1;
|
|
171
|
+
let close = true;
|
|
172
|
+
if (this.parents) {
|
|
173
|
+
parentIdx = this.parents.findIndex((x) => x == from);
|
|
174
|
+
if (parentIdx !== -1) {
|
|
175
|
+
if (this.parents.length === 1) {
|
|
176
|
+
close = true;
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
this.parents.splice(parentIdx, 1);
|
|
180
|
+
close = false;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
else if (from) {
|
|
184
|
+
throw new Error("Could not find from in parents");
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
const end = close && (await this._end("close"));
|
|
188
|
+
if (end) {
|
|
189
|
+
this.node?.services.pubsub.removeEventListener("subscribe", this._subscriptionEventListener);
|
|
190
|
+
this.node?.services.pubsub.removeEventListener("unsubscribe", this._unsubscriptionEventListener);
|
|
191
|
+
this._eventOptions = undefined;
|
|
192
|
+
if (parentIdx !== -1) {
|
|
193
|
+
this.parents.splice(parentIdx, 1); // We splice this here because this._end depends on this parent to exist
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return end;
|
|
197
|
+
}
|
|
198
|
+
async drop() {
|
|
199
|
+
await this._end("drop");
|
|
200
|
+
}
|
|
201
|
+
emitEvent(event, parents = false) {
|
|
202
|
+
this.events.dispatchEvent(event);
|
|
203
|
+
if (parents) {
|
|
204
|
+
if (this.parents) {
|
|
205
|
+
for (const parent of this.parents) {
|
|
206
|
+
parent?.emitEvent(event);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Wait for another peer to be 'ready' to talk with you for this particular program
|
|
213
|
+
* @param other
|
|
214
|
+
*/
|
|
215
|
+
async waitFor(...other) {
|
|
216
|
+
const expectedHashes = new Set(other.map((x) => x instanceof PublicSignKey
|
|
217
|
+
? x.hashcode()
|
|
218
|
+
: getPublicKeyFromPeerId(x).hashcode()));
|
|
219
|
+
await waitForAsync(async () => {
|
|
220
|
+
return (intersection(expectedHashes, await this.getReady()).size ===
|
|
221
|
+
expectedHashes.size);
|
|
222
|
+
}, { delayInterval: 200, timeout: 10 * 1000 }); // 200 ms delay since this is an expensive op. TODO, make event based instead
|
|
223
|
+
}
|
|
224
|
+
async getReady() {
|
|
225
|
+
// all peers that subscribe to all topics
|
|
226
|
+
let ready = undefined; // the interesection of all ready
|
|
227
|
+
for (const program of this.allPrograms) {
|
|
228
|
+
if (program.getTopics) {
|
|
229
|
+
const topics = program.getTopics();
|
|
230
|
+
for (const topic of topics) {
|
|
231
|
+
const subscribers = await this.node.services.pubsub.getSubscribers(topic);
|
|
232
|
+
if (!subscribers) {
|
|
233
|
+
throw new Error("client is not subscriber to topic data, do not have any info about peer readiness");
|
|
234
|
+
}
|
|
235
|
+
ready = intersection(ready, subscribers.keys());
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
if (ready == null) {
|
|
240
|
+
throw new Error("Do not have any info about peer readiness");
|
|
241
|
+
}
|
|
242
|
+
return ready;
|
|
243
|
+
}
|
|
244
|
+
get allPrograms() {
|
|
245
|
+
if (this._allPrograms) {
|
|
246
|
+
return this._allPrograms;
|
|
247
|
+
}
|
|
248
|
+
const arr = this.programs;
|
|
249
|
+
const nexts = this.programs;
|
|
250
|
+
for (const next of nexts) {
|
|
251
|
+
arr.push(...next.allPrograms);
|
|
252
|
+
}
|
|
253
|
+
this._allPrograms = arr;
|
|
254
|
+
return this._allPrograms;
|
|
255
|
+
}
|
|
256
|
+
get programs() {
|
|
257
|
+
return getValuesWithType(this, AbstractProgram_1);
|
|
258
|
+
}
|
|
259
|
+
clone() {
|
|
260
|
+
return deserialize(serialize(this), this.constructor);
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
AbstractProgram = AbstractProgram_1 = __decorate([
|
|
264
|
+
variant(0)
|
|
265
|
+
], AbstractProgram);
|
|
266
|
+
export let Program = Program_1 = class Program extends AbstractProgram {
|
|
267
|
+
_address;
|
|
268
|
+
constructor() {
|
|
269
|
+
super();
|
|
270
|
+
}
|
|
271
|
+
get address() {
|
|
272
|
+
if (!this._address) {
|
|
273
|
+
throw new Error("Address does not exist, please open or save this program once to obtain it");
|
|
274
|
+
}
|
|
275
|
+
return this._address;
|
|
276
|
+
}
|
|
277
|
+
set address(address) {
|
|
278
|
+
this._address = address;
|
|
279
|
+
}
|
|
280
|
+
async beforeOpen(node, options) {
|
|
281
|
+
// check that a discriminator exist
|
|
282
|
+
const schema = getSchema(this.constructor);
|
|
283
|
+
if (!schema || typeof schema.variant !== "string") {
|
|
284
|
+
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} { ...`);
|
|
285
|
+
}
|
|
286
|
+
await this.save(node.services.blocks);
|
|
287
|
+
if (getAllParentAddresses(this).includes(this.address)) {
|
|
288
|
+
throw new Error("Subprogram has same address as some parent program. This is not currently supported");
|
|
289
|
+
}
|
|
290
|
+
return super.beforeOpen(node, options);
|
|
291
|
+
}
|
|
292
|
+
static async open(address, node, options) {
|
|
293
|
+
const p = await Program_1.load(address, node.services.blocks);
|
|
294
|
+
if (!p) {
|
|
295
|
+
throw new Error("Failed to load program");
|
|
296
|
+
}
|
|
297
|
+
await node.open(p, options);
|
|
298
|
+
return p;
|
|
299
|
+
}
|
|
300
|
+
async save(store = this.node.services.blocks) {
|
|
301
|
+
const existingAddress = this._address;
|
|
302
|
+
const hash = await store.put(serialize(this));
|
|
303
|
+
this._address = hash;
|
|
304
|
+
if (!this.address) {
|
|
305
|
+
throw new Error("Unexpected");
|
|
306
|
+
}
|
|
307
|
+
if (existingAddress && existingAddress !== this.address) {
|
|
308
|
+
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");
|
|
309
|
+
}
|
|
310
|
+
return this._address;
|
|
311
|
+
}
|
|
312
|
+
async delete() {
|
|
313
|
+
if (this.address) {
|
|
314
|
+
return this.node.services.blocks.rm(this.address);
|
|
315
|
+
}
|
|
316
|
+
// Not saved
|
|
317
|
+
}
|
|
318
|
+
static async load(address, store, options) {
|
|
319
|
+
const bytes = await store.get(address, options);
|
|
320
|
+
if (!bytes) {
|
|
321
|
+
return undefined;
|
|
322
|
+
}
|
|
323
|
+
const der = deserialize(bytes, Program_1);
|
|
324
|
+
der.address = address;
|
|
325
|
+
return der;
|
|
326
|
+
}
|
|
327
|
+
async drop() {
|
|
328
|
+
await super.drop();
|
|
329
|
+
return this.delete();
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
Program = Program_1 = __decorate([
|
|
333
|
+
variant(0),
|
|
334
|
+
__metadata("design:paramtypes", [])
|
|
335
|
+
], Program);
|
|
336
|
+
/**eve
|
|
337
|
+
* Building block, but not something you use as a standalone
|
|
338
|
+
*/
|
|
339
|
+
export let ComposableProgram = class ComposableProgram extends AbstractProgram {
|
|
340
|
+
};
|
|
341
|
+
ComposableProgram = __decorate([
|
|
342
|
+
variant(1)
|
|
343
|
+
], ComposableProgram);
|
|
344
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { PeerId as Libp2pPeerId } from "@libp2p/interface-peer-id";
|
|
2
|
+
import { Blocks } from "@peerbit/blocks-interface";
|
|
3
|
+
import { PubSub } from "@peerbit/pubsub-interface";
|
|
4
|
+
import { Ed25519PublicKey, Identity, Keychain } from "@peerbit/crypto";
|
|
5
|
+
import type { SimpleLevel } from "@peerbit/lazy-level";
|
|
6
|
+
import { Multiaddr } from "@multiformats/multiaddr";
|
|
7
|
+
import { Address } from "./address";
|
|
8
|
+
export type WithArgs<Args> = {
|
|
9
|
+
args?: Args;
|
|
10
|
+
};
|
|
11
|
+
export type WithParent<T> = {
|
|
12
|
+
parent?: T;
|
|
13
|
+
};
|
|
14
|
+
export type CanOpen<Args> = {
|
|
15
|
+
open(args?: Args): Promise<void>;
|
|
16
|
+
};
|
|
17
|
+
export interface Client<T extends P, P> {
|
|
18
|
+
peerId: Libp2pPeerId;
|
|
19
|
+
identity: Identity<Ed25519PublicKey>;
|
|
20
|
+
getMultiaddrs: () => Multiaddr[];
|
|
21
|
+
dial(address: string | Multiaddr | Multiaddr[]): Promise<boolean>;
|
|
22
|
+
services: {
|
|
23
|
+
pubsub: PubSub;
|
|
24
|
+
blocks: Blocks;
|
|
25
|
+
};
|
|
26
|
+
memory?: SimpleLevel;
|
|
27
|
+
keychain?: Keychain;
|
|
28
|
+
start(): Promise<void>;
|
|
29
|
+
stop(): Promise<void>;
|
|
30
|
+
open<TExt extends T & CanOpen<Args>, Args = any>(program: TExt | Address, options?: WithArgs<Args> & WithParent<P>): Promise<TExt>;
|
|
31
|
+
}
|
package/lib/esm/node.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.js","sourceRoot":"","sources":["../../src/node.ts"],"names":[],"mappings":""}
|
package/lib/esm/utils.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { getSchema, } from "@dao-xyz/borsh";
|
|
2
|
+
const MAX_PROTOTYPE_SEARCH = 500;
|
|
3
|
+
const PROTOTYPE_DESERIALIZATION_HANDLER_OFFSET = 500;
|
|
4
|
+
const PROTOTYPE_DEPENDENCY_HANDLER_OFFSET = PROTOTYPE_DESERIALIZATION_HANDLER_OFFSET + MAX_PROTOTYPE_SEARCH;
|
|
5
|
+
const getDependencies = (ctor, offset) => ctor.prototype[PROTOTYPE_DEPENDENCY_HANDLER_OFFSET + offset];
|
|
6
|
+
const getSchemasBottomUp = (ctor) => {
|
|
7
|
+
let last = undefined;
|
|
8
|
+
const ret = [];
|
|
9
|
+
for (let i = 0; i < 1000; i++) {
|
|
10
|
+
const curr = getSchema(ctor, i);
|
|
11
|
+
if (!curr) {
|
|
12
|
+
if (last && !getDependencies(ctor, i)?.length) {
|
|
13
|
+
return ret;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
ret.push(curr);
|
|
18
|
+
last = curr;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return ret;
|
|
22
|
+
};
|
|
23
|
+
export const getValuesWithType = (from, type, stopAtType) => {
|
|
24
|
+
const schemas = getSchemasBottomUp(from.constructor);
|
|
25
|
+
const values = [];
|
|
26
|
+
for (const schema of schemas) {
|
|
27
|
+
for (const field of schema.fields) {
|
|
28
|
+
const value = from[field.key];
|
|
29
|
+
if (!value) {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
const p = (element) => {
|
|
33
|
+
if (element && element instanceof type) {
|
|
34
|
+
values.push(element);
|
|
35
|
+
}
|
|
36
|
+
else if (typeof element === "object") {
|
|
37
|
+
if (stopAtType && element instanceof stopAtType) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const nestedValues = getValuesWithType(element, type, stopAtType);
|
|
41
|
+
nestedValues.forEach((v) => {
|
|
42
|
+
values.push(v);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
if (Array.isArray(value)) {
|
|
47
|
+
for (const element of value) {
|
|
48
|
+
p(element);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
p(value);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return values;
|
|
57
|
+
};
|
|
58
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAGN,SAAS,GAET,MAAM,gBAAgB,CAAC;AAExB,MAAM,oBAAoB,GAAG,GAAG,CAAC;AACjC,MAAM,wCAAwC,GAAG,GAAG,CAAC;AACrD,MAAM,mCAAmC,GACxC,wCAAwC,GAAG,oBAAoB,CAAC;AACjE,MAAM,eAAe,GAAG,CACvB,IAAsC,EACtC,MAAc,EACiC,EAAE,CACjD,IAAI,CAAC,SAAS,CAAC,mCAAmC,GAAG,MAAM,CAAC,CAAC;AAE9D,MAAM,kBAAkB,GAAG,CAC1B,IAAsC,EACvB,EAAE;IACjB,IAAI,IAAI,GAA2B,SAAS,CAAC;IAC7C,MAAM,GAAG,GAAiB,EAAE,CAAC;IAC7B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,EAAE,EAAE;QAC9B,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAChC,IAAI,CAAC,IAAI,EAAE;YACV,IAAI,IAAI,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE;gBAC9C,OAAO,GAAG,CAAC;aACX;SACD;aAAM;YACN,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACf,IAAI,GAAG,IAAI,CAAC;SACZ;KACD;IACD,OAAO,GAAG,CAAC;AACZ,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAChC,IAAS,EACT,IAAsC,EACtC,UAAiD,EAC3C,EAAE;IACR,MAAM,OAAO,GAAG,kBAAkB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACrD,MAAM,MAAM,GAAQ,EAAE,CAAC;IACvB,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE;QAC7B,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE;YAClC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC9B,IAAI,CAAC,KAAK,EAAE;gBACX,SAAS;aACT;YACD,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE;gBACrB,IAAI,OAAO,IAAI,OAAO,YAAY,IAAI,EAAE;oBACvC,MAAM,CAAC,IAAI,CAAC,OAAY,CAAC,CAAC;iBAC1B;qBAAM,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE;oBACvC,IAAI,UAAU,IAAI,OAAO,YAAY,UAAU,EAAE;wBAChD,OAAO;qBACP;oBACD,MAAM,YAAY,GAAG,iBAAiB,CAAC,OAAO,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;oBAClE,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE;wBAC1B,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;oBAChB,CAAC,CAAC,CAAC;iBACH;YACF,CAAC,CAAC;YACF,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;gBACzB,KAAK,MAAM,OAAO,IAAI,KAAK,EAAE;oBAC5B,CAAC,CAAC,OAAO,CAAC,CAAC;iBACX;aACD;iBAAM;gBACN,CAAC,CAAC,KAAK,CAAC,CAAC;aACT;SACD;KACD;IACD,OAAO,MAAM,CAAC;AACf,CAAC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@peerbit/program",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Program interface",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"module": "lib/esm/index.js",
|
|
8
|
+
"types": "lib/esm/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
"import": "./lib/esm/index.js"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"lib",
|
|
14
|
+
"src",
|
|
15
|
+
"!src/**/__tests__",
|
|
16
|
+
"!lib/**/__tests__",
|
|
17
|
+
"LICENSE"
|
|
18
|
+
],
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public"
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"clean": "shx rm -rf lib/*",
|
|
24
|
+
"build": "yarn clean && tsc -p tsconfig.json",
|
|
25
|
+
"postbuild": "echo '{\"type\":\"module\"} ' | node ../../../node_modules/.bin/json > lib/esm/package.json",
|
|
26
|
+
"test": "node ../../../node_modules/.bin/jest test -c ../../../jest.config.ts --runInBand --forceExit",
|
|
27
|
+
"test:unit": "node ../../../node_modules/.bin/jest test -c ../../../jest.config.unit.ts --runInBand --forceExit",
|
|
28
|
+
"test:integration": "node ../node_modules/.bin/jest test -c ../../../jest.config.integration.ts --runInBand --forceExit"
|
|
29
|
+
},
|
|
30
|
+
"author": "dao.xyz",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@dao-xyz/borsh": "^5.1.5",
|
|
34
|
+
"@peerbit/blocks-interface": "^1.0.1",
|
|
35
|
+
"@peerbit/crypto": "1.0.1",
|
|
36
|
+
"@peerbit/lazy-level": "^1.0.0",
|
|
37
|
+
"@peerbit/pubsub-interface": "^1.0.1"
|
|
38
|
+
},
|
|
39
|
+
"gitHead": "069ce2f62a76c342875a1cc695c6f210beff13fd"
|
|
40
|
+
}
|
package/src/address.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type Address = string;
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
import { PublicSignKey, getPublicKeyFromPeerId } from "@peerbit/crypto";
|
|
2
|
+
import { Constructor, getSchema, option, variant } from "@dao-xyz/borsh";
|
|
3
|
+
import { getValuesWithType } from "./utils.js";
|
|
4
|
+
import { serialize, deserialize } from "@dao-xyz/borsh";
|
|
5
|
+
import { CustomEvent, EventEmitter } from "@libp2p/interfaces/events";
|
|
6
|
+
import { Client } from "./node.js";
|
|
7
|
+
import { waitForAsync } from "@peerbit/time";
|
|
8
|
+
import { Blocks } from "@peerbit/blocks-interface";
|
|
9
|
+
import { PeerId as Libp2pPeerId } from "@libp2p/interface-peer-id";
|
|
10
|
+
import {
|
|
11
|
+
SubscriptionEvent,
|
|
12
|
+
UnsubcriptionEvent,
|
|
13
|
+
} from "@peerbit/pubsub-interface";
|
|
14
|
+
import { Address } from "./address.js";
|
|
15
|
+
import { setMaxListeners } from "events";
|
|
16
|
+
setMaxListeners(Infinity);
|
|
17
|
+
|
|
18
|
+
export type { Address };
|
|
19
|
+
|
|
20
|
+
export interface Addressable {
|
|
21
|
+
address?: Address | undefined;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const intersection = (
|
|
25
|
+
a: Set<string> | undefined,
|
|
26
|
+
b: Set<string> | IterableIterator<string>
|
|
27
|
+
) => {
|
|
28
|
+
const newSet = new Set<string>();
|
|
29
|
+
for (const el of b) {
|
|
30
|
+
if (!a || a.has(el)) {
|
|
31
|
+
newSet.add(el);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return newSet;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export interface Saveable {
|
|
38
|
+
save(
|
|
39
|
+
store: Blocks,
|
|
40
|
+
options?: {
|
|
41
|
+
format?: string;
|
|
42
|
+
timeout?: number;
|
|
43
|
+
}
|
|
44
|
+
): Promise<Address>;
|
|
45
|
+
|
|
46
|
+
delete(): Promise<void>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export type OpenProgram = (program: Program) => Promise<Program>;
|
|
50
|
+
|
|
51
|
+
export interface NetworkEvents {
|
|
52
|
+
join: CustomEvent<PublicSignKey>;
|
|
53
|
+
leave: CustomEvent<PublicSignKey>;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface LifeCycleEvents {
|
|
57
|
+
drop: CustomEvent<Program>;
|
|
58
|
+
open: CustomEvent<Program>;
|
|
59
|
+
close: CustomEvent<Program>;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface ProgramEvents extends NetworkEvents, LifeCycleEvents {}
|
|
63
|
+
|
|
64
|
+
type EventOptions = {
|
|
65
|
+
onOpen?: (program: AbstractProgram<any>) => Promise<void> | void;
|
|
66
|
+
onDrop?: (program: AbstractProgram<any>) => Promise<void> | void;
|
|
67
|
+
onClose?: (program: AbstractProgram<any>) => Promise<void> | void;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export type ProgramInitializationOptions<Args> = {
|
|
71
|
+
// TODO
|
|
72
|
+
// reset: boolean
|
|
73
|
+
args?: Args;
|
|
74
|
+
parent?: AbstractProgram;
|
|
75
|
+
} & EventOptions;
|
|
76
|
+
|
|
77
|
+
const getAllParentAddresses = (p: AbstractProgram): string[] => {
|
|
78
|
+
return getAllParent(p, [])
|
|
79
|
+
.filter((x) => x instanceof Program)
|
|
80
|
+
.map((x) => (x as Program).address);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const getAllParent = (
|
|
84
|
+
a: AbstractProgram,
|
|
85
|
+
arr: AbstractProgram[] = [],
|
|
86
|
+
includeThis = false
|
|
87
|
+
) => {
|
|
88
|
+
includeThis && arr.push(a);
|
|
89
|
+
if (a.parents) {
|
|
90
|
+
for (const p of a.parents) {
|
|
91
|
+
if (p) {
|
|
92
|
+
getAllParent(p, arr, true);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return arr;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export type ProgramClient = Client<Program, AbstractProgram>;
|
|
100
|
+
|
|
101
|
+
@variant(0)
|
|
102
|
+
export abstract class AbstractProgram<
|
|
103
|
+
Args = any,
|
|
104
|
+
Events extends ProgramEvents = ProgramEvents
|
|
105
|
+
> {
|
|
106
|
+
private _node: ProgramClient;
|
|
107
|
+
private _allPrograms: AbstractProgram[] | undefined;
|
|
108
|
+
|
|
109
|
+
private _events: EventEmitter<ProgramEvents>;
|
|
110
|
+
private _closed: boolean;
|
|
111
|
+
|
|
112
|
+
parents: (AbstractProgram | undefined)[];
|
|
113
|
+
children: AbstractProgram[];
|
|
114
|
+
|
|
115
|
+
addParent(program: AbstractProgram | undefined) {
|
|
116
|
+
(this.parents || (this.parents = [])).push(program);
|
|
117
|
+
if (program) {
|
|
118
|
+
(program.children || (program.children = [])).push(this);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
get events(): EventEmitter<Events> {
|
|
123
|
+
return this._events || (this._events = new EventEmitter());
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
get closed(): boolean {
|
|
127
|
+
if (this._closed == null) {
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
return this._closed;
|
|
131
|
+
}
|
|
132
|
+
set closed(closed: boolean) {
|
|
133
|
+
this._closed = closed;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
get node(): ProgramClient {
|
|
137
|
+
return this._node;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
set node(node: ProgramClient) {
|
|
141
|
+
this._node = node;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
private _eventOptions: EventOptions | undefined;
|
|
145
|
+
|
|
146
|
+
async beforeOpen(
|
|
147
|
+
node: ProgramClient,
|
|
148
|
+
options?: ProgramInitializationOptions<Args>
|
|
149
|
+
) {
|
|
150
|
+
if (!this.closed) {
|
|
151
|
+
this.addParent(options?.parent);
|
|
152
|
+
return this;
|
|
153
|
+
} else {
|
|
154
|
+
this.addParent(options?.parent);
|
|
155
|
+
}
|
|
156
|
+
this._eventOptions = options;
|
|
157
|
+
this.node = node;
|
|
158
|
+
const nexts = this.programs;
|
|
159
|
+
for (const next of nexts) {
|
|
160
|
+
await next.beforeOpen(node, { ...options, parent: this });
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
this.node.services.pubsub.addEventListener(
|
|
164
|
+
"subscribe",
|
|
165
|
+
this._subscriptionEventListener ||
|
|
166
|
+
(this._subscriptionEventListener = (s) =>
|
|
167
|
+
!this.closed && this._emitJoinNetworkEvents(s.detail))
|
|
168
|
+
);
|
|
169
|
+
this.node.services.pubsub.addEventListener(
|
|
170
|
+
"unsubscribe",
|
|
171
|
+
this._unsubscriptionEventListener ||
|
|
172
|
+
(this._unsubscriptionEventListener = (s) =>
|
|
173
|
+
!this.closed && this._emitLeaveNetworkEvents(s.detail))
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
return this;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async afterOpen() {
|
|
180
|
+
this.emitEvent(new CustomEvent("open", { detail: this }), true);
|
|
181
|
+
this._eventOptions?.onOpen?.(this);
|
|
182
|
+
this.closed = false;
|
|
183
|
+
const nexts = this.programs;
|
|
184
|
+
for (const next of nexts) {
|
|
185
|
+
await next.afterOpen();
|
|
186
|
+
}
|
|
187
|
+
return this;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
abstract open(args?: Args): Promise<void>;
|
|
191
|
+
|
|
192
|
+
private _clear() {
|
|
193
|
+
this._allPrograms = undefined;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
private async _emitJoinNetworkEvents(s: SubscriptionEvent) {
|
|
197
|
+
const allTopics = this.programs
|
|
198
|
+
.map((x) => x.getTopics?.())
|
|
199
|
+
.filter((x) => x)
|
|
200
|
+
.flat() as string[];
|
|
201
|
+
|
|
202
|
+
// if subscribing to all topics, emit "join" event
|
|
203
|
+
for (const topic of allTopics) {
|
|
204
|
+
if (
|
|
205
|
+
!this.node.services.pubsub.getSubscribers(topic)?.has(s.from.hashcode())
|
|
206
|
+
) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
this.events.dispatchEvent(new CustomEvent("join", { detail: s.from }));
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
private async _emitLeaveNetworkEvents(s: UnsubcriptionEvent) {
|
|
214
|
+
const allTopics = this.programs
|
|
215
|
+
.map((x) => x.getTopics?.())
|
|
216
|
+
.filter((x) => x)
|
|
217
|
+
.flat() as string[];
|
|
218
|
+
|
|
219
|
+
// if subscribing not subscribing to any topics, emit "leave" event
|
|
220
|
+
for (const topic of allTopics) {
|
|
221
|
+
if (
|
|
222
|
+
this.node.services.pubsub.getSubscribers(topic)?.has(s.from.hashcode())
|
|
223
|
+
) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
this.events.dispatchEvent(new CustomEvent("leave", { detail: s.from }));
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
private _subscriptionEventListener: (
|
|
231
|
+
e: CustomEvent<SubscriptionEvent>
|
|
232
|
+
) => void;
|
|
233
|
+
private _unsubscriptionEventListener: (
|
|
234
|
+
e: CustomEvent<UnsubcriptionEvent>
|
|
235
|
+
) => void;
|
|
236
|
+
|
|
237
|
+
private async _end(type: "drop" | "close") {
|
|
238
|
+
if (!this.closed) {
|
|
239
|
+
this.emitEvent(new CustomEvent(type, { detail: this }), true);
|
|
240
|
+
if (type === "close") {
|
|
241
|
+
this._eventOptions?.onClose?.(this);
|
|
242
|
+
} else if (type === "drop") {
|
|
243
|
+
this._eventOptions?.onDrop?.(this);
|
|
244
|
+
} else {
|
|
245
|
+
throw new Error("Unsupported event type: " + type);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const promises: Promise<void | boolean>[] = [];
|
|
249
|
+
|
|
250
|
+
if (this.children) {
|
|
251
|
+
for (const program of this.children) {
|
|
252
|
+
promises.push(program[type](this as AbstractProgram)); // TODO types
|
|
253
|
+
}
|
|
254
|
+
this.children = [];
|
|
255
|
+
}
|
|
256
|
+
await Promise.all(promises);
|
|
257
|
+
|
|
258
|
+
this._clear();
|
|
259
|
+
this.closed = true;
|
|
260
|
+
return true;
|
|
261
|
+
} else {
|
|
262
|
+
this._clear();
|
|
263
|
+
return true;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
async close(from?: AbstractProgram): Promise<boolean> {
|
|
268
|
+
if (this.closed) {
|
|
269
|
+
return true;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
let parentIdx = -1;
|
|
273
|
+
let close = true;
|
|
274
|
+
if (this.parents) {
|
|
275
|
+
parentIdx = this.parents.findIndex((x) => x == from);
|
|
276
|
+
if (parentIdx !== -1) {
|
|
277
|
+
if (this.parents.length === 1) {
|
|
278
|
+
close = true;
|
|
279
|
+
} else {
|
|
280
|
+
this.parents.splice(parentIdx, 1);
|
|
281
|
+
close = false;
|
|
282
|
+
}
|
|
283
|
+
} else if (from) {
|
|
284
|
+
throw new Error("Could not find from in parents");
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const end = close && (await this._end("close"));
|
|
289
|
+
if (end) {
|
|
290
|
+
this.node?.services.pubsub.removeEventListener(
|
|
291
|
+
"subscribe",
|
|
292
|
+
this._subscriptionEventListener
|
|
293
|
+
);
|
|
294
|
+
this.node?.services.pubsub.removeEventListener(
|
|
295
|
+
"unsubscribe",
|
|
296
|
+
this._unsubscriptionEventListener
|
|
297
|
+
);
|
|
298
|
+
|
|
299
|
+
this._eventOptions = undefined;
|
|
300
|
+
|
|
301
|
+
if (parentIdx !== -1) {
|
|
302
|
+
this.parents.splice(parentIdx, 1); // We splice this here because this._end depends on this parent to exist
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
return end;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
async drop(): Promise<void> {
|
|
310
|
+
await this._end("drop");
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
emitEvent(event: CustomEvent, parents = false) {
|
|
314
|
+
this.events.dispatchEvent(event);
|
|
315
|
+
if (parents) {
|
|
316
|
+
if (this.parents) {
|
|
317
|
+
for (const parent of this.parents) {
|
|
318
|
+
parent?.emitEvent(event);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Wait for another peer to be 'ready' to talk with you for this particular program
|
|
326
|
+
* @param other
|
|
327
|
+
*/
|
|
328
|
+
async waitFor(...other: (PublicSignKey | Libp2pPeerId)[]): Promise<void> {
|
|
329
|
+
const expectedHashes = new Set(
|
|
330
|
+
other.map((x) =>
|
|
331
|
+
x instanceof PublicSignKey
|
|
332
|
+
? x.hashcode()
|
|
333
|
+
: getPublicKeyFromPeerId(x).hashcode()
|
|
334
|
+
)
|
|
335
|
+
);
|
|
336
|
+
await waitForAsync(
|
|
337
|
+
async () => {
|
|
338
|
+
return (
|
|
339
|
+
intersection(expectedHashes, await this.getReady()).size ===
|
|
340
|
+
expectedHashes.size
|
|
341
|
+
);
|
|
342
|
+
},
|
|
343
|
+
{ delayInterval: 200, timeout: 10 * 1000 }
|
|
344
|
+
); // 200 ms delay since this is an expensive op. TODO, make event based instead
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
async getReady(): Promise<Set<string>> {
|
|
348
|
+
// all peers that subscribe to all topics
|
|
349
|
+
let ready: Set<string> | undefined = undefined; // the interesection of all ready
|
|
350
|
+
for (const program of this.allPrograms) {
|
|
351
|
+
if (program.getTopics) {
|
|
352
|
+
const topics = program.getTopics();
|
|
353
|
+
for (const topic of topics) {
|
|
354
|
+
const subscribers = await this.node.services.pubsub.getSubscribers(
|
|
355
|
+
topic
|
|
356
|
+
);
|
|
357
|
+
if (!subscribers) {
|
|
358
|
+
throw new Error(
|
|
359
|
+
"client is not subscriber to topic data, do not have any info about peer readiness"
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
ready = intersection(ready, subscribers.keys());
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
if (ready == null) {
|
|
367
|
+
throw new Error("Do not have any info about peer readiness");
|
|
368
|
+
}
|
|
369
|
+
return ready;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
get allPrograms(): AbstractProgram[] {
|
|
373
|
+
if (this._allPrograms) {
|
|
374
|
+
return this._allPrograms;
|
|
375
|
+
}
|
|
376
|
+
const arr: AbstractProgram[] = this.programs;
|
|
377
|
+
const nexts = this.programs;
|
|
378
|
+
for (const next of nexts) {
|
|
379
|
+
arr.push(...next.allPrograms);
|
|
380
|
+
}
|
|
381
|
+
this._allPrograms = arr;
|
|
382
|
+
return this._allPrograms;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
get programs(): AbstractProgram[] {
|
|
386
|
+
return getValuesWithType(this, AbstractProgram);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
clone(): this {
|
|
390
|
+
return deserialize(serialize(this), this.constructor);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
getTopics?(): string[];
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
export interface CanTrust {
|
|
397
|
+
isTrusted(keyHash: string): Promise<boolean> | boolean;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
@variant(0)
|
|
401
|
+
export abstract class Program<
|
|
402
|
+
Args = any,
|
|
403
|
+
Events extends ProgramEvents = ProgramEvents
|
|
404
|
+
>
|
|
405
|
+
extends AbstractProgram<Args, Events>
|
|
406
|
+
implements Addressable, Saveable
|
|
407
|
+
{
|
|
408
|
+
private _address?: Address;
|
|
409
|
+
|
|
410
|
+
constructor() {
|
|
411
|
+
super();
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
get address(): Address {
|
|
415
|
+
if (!this._address) {
|
|
416
|
+
throw new Error(
|
|
417
|
+
"Address does not exist, please open or save this program once to obtain it"
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
return this._address;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
set address(address: Address) {
|
|
424
|
+
this._address = address;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
async beforeOpen(
|
|
428
|
+
node: ProgramClient,
|
|
429
|
+
options?: ProgramInitializationOptions<Args>
|
|
430
|
+
) {
|
|
431
|
+
// check that a discriminator exist
|
|
432
|
+
const schema = getSchema(this.constructor);
|
|
433
|
+
if (!schema || typeof schema.variant !== "string") {
|
|
434
|
+
throw new Error(
|
|
435
|
+
`Expecting class to be decorated with a string variant. Example:\n\'import { variant } "@dao-xyz/borsh"\n@variant("example-db")\nclass ${this.constructor.name} { ...`
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
await this.save(node.services.blocks);
|
|
440
|
+
if (getAllParentAddresses(this as AbstractProgram).includes(this.address)) {
|
|
441
|
+
throw new Error(
|
|
442
|
+
"Subprogram has same address as some parent program. This is not currently supported"
|
|
443
|
+
);
|
|
444
|
+
}
|
|
445
|
+
return super.beforeOpen(node, options);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
static async open<T extends Program<Args>, Args = any>(
|
|
449
|
+
this: Constructor<T>,
|
|
450
|
+
address: Address,
|
|
451
|
+
node: ProgramClient,
|
|
452
|
+
options?: ProgramInitializationOptions<Args>
|
|
453
|
+
): Promise<T> {
|
|
454
|
+
const p = await Program.load<T>(address, node.services.blocks);
|
|
455
|
+
|
|
456
|
+
if (!p) {
|
|
457
|
+
throw new Error("Failed to load program");
|
|
458
|
+
}
|
|
459
|
+
await node.open(p, options);
|
|
460
|
+
return p as T;
|
|
461
|
+
}
|
|
462
|
+
async save(store: Blocks = this.node.services.blocks): Promise<Address> {
|
|
463
|
+
const existingAddress = this._address;
|
|
464
|
+
const hash = await store.put(serialize(this));
|
|
465
|
+
|
|
466
|
+
this._address = hash;
|
|
467
|
+
if (!this.address) {
|
|
468
|
+
throw new Error("Unexpected");
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
if (existingAddress && existingAddress !== this.address) {
|
|
472
|
+
throw new Error(
|
|
473
|
+
"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"
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
return this._address!;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
async delete(): Promise<void> {
|
|
481
|
+
if (this.address) {
|
|
482
|
+
return this.node.services.blocks.rm(this.address);
|
|
483
|
+
}
|
|
484
|
+
// Not saved
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
static async load<P extends Program<any>>(
|
|
488
|
+
address: Address,
|
|
489
|
+
store: Blocks,
|
|
490
|
+
options?: {
|
|
491
|
+
timeout?: number;
|
|
492
|
+
}
|
|
493
|
+
): Promise<P | undefined> {
|
|
494
|
+
const bytes = await store.get(address, options);
|
|
495
|
+
if (!bytes) {
|
|
496
|
+
return undefined;
|
|
497
|
+
}
|
|
498
|
+
const der = deserialize(bytes, Program);
|
|
499
|
+
der.address = address;
|
|
500
|
+
return der as P;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
async drop(): Promise<void> {
|
|
504
|
+
await super.drop();
|
|
505
|
+
return this.delete();
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**eve
|
|
510
|
+
* Building block, but not something you use as a standalone
|
|
511
|
+
*/
|
|
512
|
+
@variant(1)
|
|
513
|
+
export abstract class ComposableProgram<
|
|
514
|
+
Args = any,
|
|
515
|
+
Events extends ProgramEvents = ProgramEvents
|
|
516
|
+
> extends AbstractProgram<Args, Events> {}
|
package/src/node.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { PeerId as Libp2pPeerId } from "@libp2p/interface-peer-id";
|
|
2
|
+
import { Blocks } from "@peerbit/blocks-interface";
|
|
3
|
+
import { PubSub } from "@peerbit/pubsub-interface";
|
|
4
|
+
import { Ed25519PublicKey, Identity, Keychain } from "@peerbit/crypto";
|
|
5
|
+
import type { SimpleLevel } from "@peerbit/lazy-level";
|
|
6
|
+
import { Multiaddr } from "@multiformats/multiaddr";
|
|
7
|
+
import { Address } from "./address";
|
|
8
|
+
|
|
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> {
|
|
14
|
+
peerId: Libp2pPeerId;
|
|
15
|
+
identity: Identity<Ed25519PublicKey>;
|
|
16
|
+
getMultiaddrs: () => Multiaddr[];
|
|
17
|
+
dial(address: string | Multiaddr | Multiaddr[]): Promise<boolean>;
|
|
18
|
+
services: {
|
|
19
|
+
pubsub: PubSub;
|
|
20
|
+
blocks: Blocks;
|
|
21
|
+
};
|
|
22
|
+
memory?: SimpleLevel;
|
|
23
|
+
keychain?: Keychain;
|
|
24
|
+
start(): Promise<void>;
|
|
25
|
+
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>;
|
|
30
|
+
}
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AbstractType,
|
|
3
|
+
Constructor,
|
|
4
|
+
getSchema,
|
|
5
|
+
StructKind,
|
|
6
|
+
} from "@dao-xyz/borsh";
|
|
7
|
+
|
|
8
|
+
const MAX_PROTOTYPE_SEARCH = 500;
|
|
9
|
+
const PROTOTYPE_DESERIALIZATION_HANDLER_OFFSET = 500;
|
|
10
|
+
const PROTOTYPE_DEPENDENCY_HANDLER_OFFSET =
|
|
11
|
+
PROTOTYPE_DESERIALIZATION_HANDLER_OFFSET + MAX_PROTOTYPE_SEARCH;
|
|
12
|
+
const getDependencies = <T>(
|
|
13
|
+
ctor: Constructor<T> | AbstractType<T>,
|
|
14
|
+
offset: number
|
|
15
|
+
): Constructor<T> | AbstractType<T> | undefined =>
|
|
16
|
+
ctor.prototype[PROTOTYPE_DEPENDENCY_HANDLER_OFFSET + offset];
|
|
17
|
+
|
|
18
|
+
const getSchemasBottomUp = <T>(
|
|
19
|
+
ctor: Constructor<T> | AbstractType<T>
|
|
20
|
+
): StructKind[] => {
|
|
21
|
+
let last: StructKind | undefined = undefined;
|
|
22
|
+
const ret: StructKind[] = [];
|
|
23
|
+
for (let i = 0; i < 1000; i++) {
|
|
24
|
+
const curr = getSchema(ctor, i);
|
|
25
|
+
if (!curr) {
|
|
26
|
+
if (last && !getDependencies(ctor, i)?.length) {
|
|
27
|
+
return ret;
|
|
28
|
+
}
|
|
29
|
+
} else {
|
|
30
|
+
ret.push(curr);
|
|
31
|
+
last = curr;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return ret;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const getValuesWithType = <T>(
|
|
38
|
+
from: any,
|
|
39
|
+
type: Constructor<T> | AbstractType<T>,
|
|
40
|
+
stopAtType?: Constructor<any> | AbstractType<any>
|
|
41
|
+
): T[] => {
|
|
42
|
+
const schemas = getSchemasBottomUp(from.constructor);
|
|
43
|
+
const values: T[] = [];
|
|
44
|
+
for (const schema of schemas) {
|
|
45
|
+
for (const field of schema.fields) {
|
|
46
|
+
const value = from[field.key];
|
|
47
|
+
if (!value) {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
const p = (element) => {
|
|
51
|
+
if (element && element instanceof type) {
|
|
52
|
+
values.push(element as T);
|
|
53
|
+
} else if (typeof element === "object") {
|
|
54
|
+
if (stopAtType && element instanceof stopAtType) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const nestedValues = getValuesWithType(element, type, stopAtType);
|
|
58
|
+
nestedValues.forEach((v) => {
|
|
59
|
+
values.push(v);
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
if (Array.isArray(value)) {
|
|
64
|
+
for (const element of value) {
|
|
65
|
+
p(element);
|
|
66
|
+
}
|
|
67
|
+
} else {
|
|
68
|
+
p(value);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return values;
|
|
73
|
+
};
|