nervur 0.20.1 → 0.20.3
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/README.md +174 -12
- package/dist/browser/index.d.ts +35 -0
- package/dist/browser/index.js +188 -0
- package/dist/browser/worker.d.ts +12 -0
- package/dist/browser/worker.js +40 -0
- package/dist/browser.d.ts +1 -0
- package/dist/browser.js +7 -0
- package/dist/cli/command.d.ts +1 -1
- package/dist/cli/command.js +84 -20
- package/dist/cli/harbor.d.ts +7 -12
- package/dist/cli/harbor.js +73 -123
- package/dist/contract/index.d.ts +2 -1
- package/dist/contract/index.js +3 -7
- package/dist/edge/index.d.ts +71 -0
- package/dist/edge/index.js +334 -0
- package/dist/edge.d.ts +1 -0
- package/dist/edge.js +8 -0
- package/dist/folder/index.d.ts +4 -0
- package/dist/folder/index.js +57 -14
- package/dist/harbor/carrying.d.ts +17 -0
- package/dist/harbor/carrying.js +38 -0
- package/dist/harbor/catalogue.d.ts +2 -0
- package/dist/harbor/catalogue.js +28 -20
- package/dist/harbor/dock.d.ts +22 -3
- package/dist/harbor/dock.js +69 -13
- package/dist/harbor/harbor.d.ts +14 -4
- package/dist/harbor/harbor.js +144 -15
- package/dist/harbor/index.d.ts +6 -3
- package/dist/harbor/index.js +4 -1
- package/dist/harbor/package.d.ts +10 -0
- package/dist/harbor/package.js +52 -1
- package/dist/harbor/probe.d.ts +18 -0
- package/dist/harbor/probe.js +15 -0
- package/dist/harbor/registry.d.ts +1 -0
- package/dist/harbor/registry.js +11 -0
- package/dist/harbor/relay.d.ts +15 -0
- package/dist/harbor/relay.js +406 -0
- package/dist/harbor/root-line.js +4 -2
- package/dist/harbor/terrain.d.ts +12 -0
- package/dist/harbor/terrain.js +19 -0
- package/dist/http/index.d.ts +4 -0
- package/dist/http/index.js +156 -0
- package/dist/http/websocket.d.ts +29 -0
- package/dist/http/websocket.js +133 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +8 -2
- package/dist/line/answer.d.ts +8 -0
- package/dist/line/answer.js +60 -0
- package/dist/line/frame.d.ts +34 -0
- package/dist/line/frame.js +81 -0
- package/dist/line/ground.d.ts +3 -0
- package/dist/line/ground.js +9 -0
- package/dist/line/index.d.ts +5 -0
- package/dist/line/index.js +9 -0
- package/dist/line/push.d.ts +7 -0
- package/dist/line/push.js +92 -0
- package/dist/line/web.d.ts +30 -0
- package/dist/line/web.js +181 -0
- package/dist/node/index.d.ts +36 -0
- package/dist/node/index.js +228 -0
- package/dist/node.d.ts +1 -0
- package/dist/node.js +7 -0
- package/dist/pointer/bodies.d.ts +2 -0
- package/dist/pointer/bodies.js +10 -0
- package/dist/pointer/world.d.ts +7 -1
- package/dist/pointer/world.js +6 -0
- package/dist/quo/address.d.ts +19 -0
- package/dist/quo/address.js +84 -0
- package/dist/quo/door.d.ts +1 -1
- package/dist/quo/door.js +4 -2
- package/dist/quo/index.d.ts +1 -0
- package/dist/quo/index.js +1 -0
- package/dist/quo/invitation.d.ts +1 -0
- package/dist/quo/invitation.js +9 -5
- package/dist/tcp/frame.d.ts +0 -4
- package/dist/tcp/frame.js +0 -10
- package/dist/tcp/index.d.ts +8 -2
- package/dist/tcp/index.js +123 -27
- package/dist/ward/index.d.ts +2 -2
- package/dist/ward/index.js +1 -1
- package/dist/ward/stance.d.ts +2 -0
- package/dist/ward/stance.js +3 -1
- package/dist/ward/ward-being.d.ts +2 -1
- package/dist/ward/ward-being.js +1 -1
- package/dist/ward/ward.d.ts +7 -0
- package/dist/ward/ward.js +6 -0
- package/package.json +21 -2
- package/src/browser/index.ts +207 -0
- package/src/browser/worker.ts +66 -0
- package/src/browser.ts +9 -0
- package/src/cli/command.ts +74 -20
- package/src/cli/harbor.ts +73 -116
- package/src/contract/index.ts +11 -8
- package/src/edge/index.ts +413 -0
- package/src/edge.ts +10 -0
- package/src/folder/index.ts +57 -12
- package/src/harbor/carrying.ts +56 -0
- package/src/harbor/catalogue.ts +32 -17
- package/src/harbor/dock.ts +88 -17
- package/src/harbor/harbor.ts +146 -17
- package/src/harbor/index.ts +6 -3
- package/src/harbor/package.ts +55 -1
- package/src/harbor/probe.ts +34 -0
- package/src/harbor/registry.ts +11 -0
- package/src/harbor/relay.ts +418 -0
- package/src/harbor/root-line.ts +4 -1
- package/src/harbor/terrain.ts +28 -2
- package/src/http/index.ts +150 -0
- package/src/http/websocket.ts +124 -0
- package/src/index.ts +35 -2
- package/src/line/answer.ts +59 -0
- package/src/line/frame.ts +88 -0
- package/src/line/ground.ts +19 -0
- package/src/line/index.ts +9 -0
- package/src/line/push.ts +112 -0
- package/src/line/web.ts +227 -0
- package/src/node/index.ts +234 -0
- package/src/node.ts +9 -0
- package/src/pointer/bodies.ts +11 -0
- package/src/pointer/world.ts +18 -2
- package/src/quo/address.ts +87 -0
- package/src/quo/door.ts +4 -2
- package/src/quo/index.ts +1 -0
- package/src/quo/invitation.ts +10 -6
- package/src/stand/stand.ts +60 -21
- package/src/tcp/index.ts +142 -31
- package/src/ward/index.ts +2 -2
- package/src/ward/stance.ts +6 -1
- package/src/ward/ward-being.ts +2 -2
- package/src/ward/ward.ts +16 -0
- package/src/tcp/frame.ts +0 -78
package/dist/cli/command.js
CHANGED
|
@@ -7,13 +7,27 @@
|
|
|
7
7
|
// invitation for whoever made it
|
|
8
8
|
// nervur serve [--dir D] [--host H] [--port P] the harbor served: Quo over TCP,
|
|
9
9
|
// and the root line on its socket
|
|
10
|
-
// nervur module add <file>
|
|
10
|
+
// nervur module add <file | package | id> a module the catalogue runs, kept
|
|
11
|
+
// by the id it declares
|
|
11
12
|
// nervur module remove <module> a module it runs no more
|
|
12
13
|
// nervur modules what the catalogue runs
|
|
13
14
|
// nervur census [--ward W] the empty ask
|
|
15
|
+
// nervur sweep every place of the folder this
|
|
16
|
+
// harbor does not name, gone
|
|
17
|
+
// nervur export [--with-seed] the harbor as one JSON line, to
|
|
18
|
+
// carry it to another ground; with
|
|
19
|
+
// --with-seed the seed goes too
|
|
20
|
+
// nervur import <file> a carried harbor into an empty
|
|
21
|
+
// folder, `-` being this input
|
|
22
|
+
// nervur edge pack <file> the harbor for an edge: its seed
|
|
23
|
+
// printed, for the worker's
|
|
24
|
+
// NERVUR_SEED, and its sealed
|
|
25
|
+
// package written to the file
|
|
14
26
|
// nervur open <key> <kind> a faculty stands in the box ward
|
|
15
27
|
// nervur host <ward> [--seed S] a hosted ward stands
|
|
16
|
-
// nervur route <ward pk> <
|
|
28
|
+
// nervur route <ward pk> <address... | none> where a ward is reached, in order
|
|
29
|
+
// nervur reach <address...> where this harbor is reached, as
|
|
30
|
+
// every invitation it gives says
|
|
17
31
|
// nervur boot [--ward W] <key> <kind> a being stands
|
|
18
32
|
// nervur unboot [--ward W] <being> a being goes
|
|
19
33
|
// nervur public [--ward W] <key | none> the being a stranger reaches
|
|
@@ -21,8 +35,9 @@
|
|
|
21
35
|
// nervur ask [--ward W] [being] [method] [args] a being, asked as the owner
|
|
22
36
|
// nervur own [--ward W] <id> an invitation for an owner
|
|
23
37
|
// nervur disown [--ward W] <id> an owner no more
|
|
24
|
-
// nervur pilot <name> <invitation>
|
|
25
|
-
// pilot being of the box ward
|
|
38
|
+
// nervur pilot <name> <invitation> [address] a far ward held as its owner, by a
|
|
39
|
+
// pilot being of the box ward, at
|
|
40
|
+
// the address or the invitation's at
|
|
26
41
|
// nervur <command> --via <name> ... the command asked of the far ward
|
|
27
42
|
// that pilot holds, as its owner
|
|
28
43
|
//
|
|
@@ -31,11 +46,16 @@
|
|
|
31
46
|
// ward, and a pilot holds one ward, so --via takes no --ward. The exit is 1
|
|
32
47
|
// when the answer is an error.
|
|
33
48
|
import { existsSync } from 'node:fs';
|
|
34
|
-
import {
|
|
49
|
+
import { readFile } from 'node:fs/promises';
|
|
35
50
|
import { join, resolve } from 'node:path';
|
|
36
|
-
import {
|
|
37
|
-
|
|
51
|
+
import { WEB_OPENING } from '../line/index.js';
|
|
52
|
+
import { harborDir, isFile } from '../node/index.js';
|
|
53
|
+
import { readAt, webAddress } from '../quo/index.js';
|
|
54
|
+
import { carry, isServed, land, open, pack, request, serve } from './harbor.js';
|
|
55
|
+
const USAGE = 'usage: nervur <init|serve|module|modules|census|sweep|open|host|route|reach|boot|unboot|public|invite|ask|own|disown|pilot|export|import|edge> [args] [--dir D] [--ward W] [--via V] [--seed S] [--with-seed] [--host H] [--port P]';
|
|
38
56
|
const FLAGS = new Set(['dir', 'ward', 'via', 'seed', 'host', 'port']);
|
|
57
|
+
// The one flag that stands alone, since what it names is a yes.
|
|
58
|
+
const ALONE = 'with-seed';
|
|
39
59
|
export const parse = (argv) => {
|
|
40
60
|
const flags = {};
|
|
41
61
|
const words = [];
|
|
@@ -44,6 +64,8 @@ export const parse = (argv) => {
|
|
|
44
64
|
const name = arg.startsWith('--') ? arg.slice(2) : undefined;
|
|
45
65
|
if (name === undefined)
|
|
46
66
|
words.push(arg);
|
|
67
|
+
else if (name === ALONE)
|
|
68
|
+
flags[name] = 'yes';
|
|
47
69
|
else if (!FLAGS.has(name) || argv[i + 1] === undefined)
|
|
48
70
|
throw new Error(`--${name} is no flag with a value`);
|
|
49
71
|
else
|
|
@@ -52,6 +74,13 @@ export const parse = (argv) => {
|
|
|
52
74
|
const [command, ...rest] = words;
|
|
53
75
|
return { ...(command === undefined ? {} : { command }), words: rest, flags };
|
|
54
76
|
};
|
|
77
|
+
// What this command was handed on its input, for `import -`.
|
|
78
|
+
const standingIn = async () => {
|
|
79
|
+
let text = '';
|
|
80
|
+
for await (const chunk of process.stdin)
|
|
81
|
+
text += chunk.toString('utf8');
|
|
82
|
+
return text;
|
|
83
|
+
};
|
|
55
84
|
const none = (word) => (word === 'none' ? null : word);
|
|
56
85
|
const json = (word) => (word === undefined ? undefined : JSON.parse(word));
|
|
57
86
|
const catalogue = (method, args = {}) => ({ method: 'ask', args: { being: 'catalogue', method, args } });
|
|
@@ -67,13 +96,15 @@ export const requestOf = (command, words, flags) => {
|
|
|
67
96
|
case 'module': {
|
|
68
97
|
const [verb, what] = need(2);
|
|
69
98
|
if (verb === 'add')
|
|
70
|
-
return catalogue('add', { from: resolve(what) });
|
|
99
|
+
return catalogue('add', { from: isFile(what) ? resolve(what) : what });
|
|
71
100
|
if (verb === 'remove')
|
|
72
101
|
return catalogue('remove', { module: what });
|
|
73
102
|
throw new Error('module is add or remove');
|
|
74
103
|
}
|
|
75
104
|
case 'modules':
|
|
76
105
|
return (need(0), catalogue('modules'));
|
|
106
|
+
case 'sweep':
|
|
107
|
+
return (need(0), { method: 'sweep', args: {} });
|
|
77
108
|
case 'census':
|
|
78
109
|
return (need(0), at);
|
|
79
110
|
case 'open': {
|
|
@@ -85,9 +116,15 @@ export const requestOf = (command, words, flags) => {
|
|
|
85
116
|
return { method: 'host', args: { ward: ward, ...(flags.seed === undefined ? {} : { seed: flags.seed }) } };
|
|
86
117
|
}
|
|
87
118
|
case 'route': {
|
|
88
|
-
const [ward, where] =
|
|
89
|
-
|
|
119
|
+
const [ward, ...where] = words;
|
|
120
|
+
if (ward === undefined || where.length === 0)
|
|
121
|
+
throw new Error('route takes a ward pk and its addresses, or none');
|
|
122
|
+
return { method: 'route', args: { ward, at: where.length === 1 && where[0] === 'none' ? null : where } };
|
|
90
123
|
}
|
|
124
|
+
case 'reach':
|
|
125
|
+
if (words.length === 0)
|
|
126
|
+
throw new Error('reach takes the addresses this harbor is reached at');
|
|
127
|
+
return { method: 'reach', args: { at: words } };
|
|
91
128
|
case 'boot': {
|
|
92
129
|
const [key, kind] = need(2);
|
|
93
130
|
return { ...at, method: 'boot', args: { key: key, class: kind } };
|
|
@@ -124,7 +161,7 @@ export const main = async (argv, env, print) => {
|
|
|
124
161
|
const { command, words, flags } = parse(argv);
|
|
125
162
|
if (command === undefined)
|
|
126
163
|
throw new Error(USAGE);
|
|
127
|
-
const dir = flags.dir ?? env
|
|
164
|
+
const dir = flags.dir ?? harborDir(env);
|
|
128
165
|
const out = (answer) => {
|
|
129
166
|
print(JSON.stringify(answer));
|
|
130
167
|
return failed(answer) ? 1 : 0;
|
|
@@ -134,15 +171,36 @@ export const main = async (argv, env, print) => {
|
|
|
134
171
|
throw new Error('init takes no argument');
|
|
135
172
|
if (existsSync(join(dir, 'seed')))
|
|
136
173
|
return out({ error: `a harbor stands at ${dir}` });
|
|
137
|
-
const
|
|
174
|
+
const harbor = await open(dir);
|
|
138
175
|
try {
|
|
139
176
|
const owner = await harbor.root('own', { id: 'owner' });
|
|
140
177
|
return out({ pk: harbor.box.pk, owner: owner });
|
|
141
178
|
}
|
|
142
179
|
finally {
|
|
143
|
-
await
|
|
180
|
+
await harbor.close();
|
|
144
181
|
}
|
|
145
182
|
}
|
|
183
|
+
if (command === 'export' || command === 'import') {
|
|
184
|
+
if (words.length !== (command === 'import' ? 1 : 0))
|
|
185
|
+
throw new Error(`${command} takes ${command === 'import' ? 'one file' : 'no argument'}`);
|
|
186
|
+
if (flags.via !== undefined)
|
|
187
|
+
throw new Error(`${command} reads this folder, so it takes no --via`);
|
|
188
|
+
if (await isServed(dir))
|
|
189
|
+
throw new Error(`a harbor is served from ${dir}: stop it first`);
|
|
190
|
+
if (command === 'export')
|
|
191
|
+
return out((await carry(dir, flags[ALONE] !== undefined)));
|
|
192
|
+
const text = words[0] === '-' ? await standingIn() : await readFile(words[0], 'utf8');
|
|
193
|
+
return out({ landed: await land(dir, JSON.parse(text)) });
|
|
194
|
+
}
|
|
195
|
+
if (command === 'edge') {
|
|
196
|
+
if (words[0] !== 'pack' || words.length !== 2)
|
|
197
|
+
throw new Error('edge takes pack and a file');
|
|
198
|
+
if (flags.via !== undefined)
|
|
199
|
+
throw new Error('edge pack reads this folder, so it takes no --via');
|
|
200
|
+
if (await isServed(dir))
|
|
201
|
+
throw new Error(`a harbor is served from ${dir}: stop it first`);
|
|
202
|
+
return out(await pack(dir, words[1]));
|
|
203
|
+
}
|
|
146
204
|
if (command === 'serve') {
|
|
147
205
|
if (words.length > 0)
|
|
148
206
|
throw new Error('serve takes no argument');
|
|
@@ -150,15 +208,17 @@ export const main = async (argv, env, print) => {
|
|
|
150
208
|
const stop = () => void served.close().then(() => process.exit(0));
|
|
151
209
|
process.once('SIGINT', stop);
|
|
152
210
|
process.once('SIGTERM', stop);
|
|
153
|
-
print(JSON.stringify({ pk: served.pk, at: served.at, root: served.root }));
|
|
211
|
+
print(JSON.stringify({ pk: served.pk, at: served.at, listening: served.listening, root: served.root }));
|
|
154
212
|
return SERVING;
|
|
155
213
|
}
|
|
156
214
|
if (command === 'pilot') {
|
|
157
|
-
if (words.length !== 3)
|
|
158
|
-
throw new Error('pilot takes
|
|
215
|
+
if (words.length !== 2 && words.length !== 3)
|
|
216
|
+
throw new Error('pilot takes a name, an invitation, and an address where the invitation carries none');
|
|
159
217
|
const [name, invitation, at] = words;
|
|
160
218
|
const held = JSON.parse(invitation);
|
|
161
|
-
|
|
219
|
+
const census = (await request(dir, {}));
|
|
220
|
+
const web = census.answer?.beings !== undefined && Object.hasOwn(census.answer.beings, WEB_OPENING.key);
|
|
221
|
+
for (const req of pilotRequests(name, held, at, web)) {
|
|
162
222
|
const answer = await request(dir, req);
|
|
163
223
|
if (!failed(answer))
|
|
164
224
|
continue;
|
|
@@ -174,9 +234,13 @@ export const main = async (argv, env, print) => {
|
|
|
174
234
|
throw new Error('--via takes no --ward');
|
|
175
235
|
return out(farAnswer(await request(dir, via(flags.via, requestOf(command, words, flags)))));
|
|
176
236
|
};
|
|
177
|
-
// The root requests that hold a far ward:
|
|
178
|
-
|
|
179
|
-
|
|
237
|
+
// The root requests that hold a far ward: the web carrier opened, where the
|
|
238
|
+
// far ward is reached on the web and this harbor has none; its route where
|
|
239
|
+
// one is named, else the take learns it from the invitation's `at`; the
|
|
240
|
+
// pilot; the take.
|
|
241
|
+
export const pilotRequests = (name, invitation, at, web = false) => [
|
|
242
|
+
...(!web && readAt([...(at === undefined ? [] : [at]), ...readAt(invitation.at)]).some((a) => webAddress(a) !== null) ? [{ method: 'open', args: { key: WEB_OPENING.key, class: WEB_OPENING.kind } }] : []),
|
|
243
|
+
...(at === undefined ? [] : [{ method: 'route', args: { ward: invitation.ward ?? null, at: [at] } }]),
|
|
180
244
|
{ method: 'boot', args: { key: name, class: 'org.nervur.pilot' } },
|
|
181
245
|
{ method: 'ask', args: { being: name, method: 'hold', args: { invitation } } },
|
|
182
246
|
];
|
package/dist/cli/harbor.d.ts
CHANGED
|
@@ -1,20 +1,15 @@
|
|
|
1
1
|
import type { JsonObject } from '../being/index.ts';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
export declare const socketOf: (dir: string) => string;
|
|
6
|
-
export declare class ImportLoader extends Loader {
|
|
7
|
-
load(from: string): Promise<Loaded | null>;
|
|
8
|
-
}
|
|
9
|
-
export type Opened = {
|
|
10
|
-
harbor: Harbor;
|
|
11
|
-
carrier: TcpCarrier;
|
|
12
|
-
};
|
|
13
|
-
export declare const open: (dir: string) => Promise<Opened>;
|
|
2
|
+
import { Harbor, type Carried, type RootRequest } from '../harbor/index.ts';
|
|
3
|
+
export declare const open: (dir: string) => Promise<Harbor>;
|
|
4
|
+
export declare const isServed: (dir: string) => Promise<boolean>;
|
|
14
5
|
export declare const request: (dir: string, req: RootRequest) => Promise<JsonObject>;
|
|
6
|
+
export declare const carry: (dir: string, seed: boolean) => Promise<Carried>;
|
|
7
|
+
export declare const pack: (dir: string, file: string) => Promise<JsonObject>;
|
|
8
|
+
export declare const land: (dir: string, value: unknown) => Promise<string[]>;
|
|
15
9
|
export type Served = {
|
|
16
10
|
pk: string;
|
|
17
11
|
at: string;
|
|
12
|
+
listening: string[];
|
|
18
13
|
root: string;
|
|
19
14
|
close(): Promise<void>;
|
|
20
15
|
};
|
package/dist/cli/harbor.js
CHANGED
|
@@ -1,141 +1,91 @@
|
|
|
1
|
-
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
2
|
-
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
3
|
-
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
4
|
-
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
5
|
-
});
|
|
6
|
-
}
|
|
7
|
-
return path;
|
|
8
|
-
};
|
|
9
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
10
|
-
// A harbor on this machine, as the `nervur` command stands it:
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
// local socket beside them.
|
|
14
|
-
//
|
|
15
|
-
// <dir>/seed the harbor's seed
|
|
16
|
-
// <dir>/<place>/ its sealed memory
|
|
17
|
-
// <dir>/root.sock the root line while the harbor is served, open to
|
|
18
|
-
// this user alone, or in the temporary folder for a
|
|
19
|
-
// folder too deep for a socket path
|
|
2
|
+
// A harbor on this machine, as the `nervur` command stands it: a harbor of
|
|
3
|
+
// the Node ground in a folder, served while `serve` runs, and asked over
|
|
4
|
+
// its root line's socket.
|
|
20
5
|
//
|
|
21
6
|
// A root request goes to a served harbor over its socket. With none served,
|
|
22
7
|
// the command unpacks the harbor from its folder, asks, and closes it, so
|
|
23
8
|
// one process at a time writes the folder.
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
import { join, resolve } from 'node:path';
|
|
29
|
-
import { pathToFileURL } from 'node:url';
|
|
30
|
-
import { Loader } from '../contract/index.js';
|
|
9
|
+
import { existsSync } from 'node:fs';
|
|
10
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
import { hex } from '../crypto/index.js';
|
|
31
13
|
import { FolderCustody, FolderMemory } from '../folder/index.js';
|
|
32
|
-
import { Harbor, rootLine } from '../harbor/index.js';
|
|
33
|
-
import {
|
|
34
|
-
import {
|
|
35
|
-
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if (Buffer.byteLength(beside) <= SOCKET_PATH)
|
|
42
|
-
return beside;
|
|
43
|
-
return join(tmpdir(), `nervur-${createHash('sha256').update(resolve(dir)).digest('hex').slice(0, 32)}.sock`);
|
|
44
|
-
};
|
|
45
|
-
// Code from a file: a module that exports `module`, `version` and
|
|
46
|
-
// `classes`, found again by its absolute path. A file that cannot be
|
|
47
|
-
// imported, or exports no module, is none.
|
|
48
|
-
export class ImportLoader extends Loader {
|
|
49
|
-
async load(from) {
|
|
50
|
-
const path = resolve(from);
|
|
51
|
-
let exported;
|
|
52
|
-
try {
|
|
53
|
-
exported = (await import(__rewriteRelativeImportExtension(pathToFileURL(path).href)));
|
|
54
|
-
}
|
|
55
|
-
catch {
|
|
56
|
-
return null;
|
|
57
|
-
}
|
|
58
|
-
const { module, version, classes } = exported;
|
|
59
|
-
if (typeof module !== 'string' || typeof version !== 'string' || !Array.isArray(classes))
|
|
60
|
-
return null;
|
|
61
|
-
return { module, version, classes, from: path };
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
export const open = async (dir) => {
|
|
65
|
-
const entropy = new CryptoEntropy();
|
|
66
|
-
const carrier = new TcpCarrier();
|
|
67
|
-
const harbor = await Harbor.open(new PointerTerrain({ entropy, carrier, loader: new ImportLoader(), custody: new FolderCustody(dir, entropy), memory: new FolderMemory(dir) }));
|
|
68
|
-
return { harbor, carrier };
|
|
69
|
-
};
|
|
70
|
-
// One root request over a served harbor's socket, or undefined where none
|
|
71
|
-
// is served.
|
|
72
|
-
const overSocket = (dir, request) => new Promise((done, fail) => {
|
|
73
|
-
const socket = connect(socketOf(dir));
|
|
74
|
-
let text = '';
|
|
75
|
-
socket.once('connect', () => socket.write(`${JSON.stringify(request)}\n`));
|
|
76
|
-
socket.on('data', (chunk) => {
|
|
77
|
-
text += chunk.toString('utf8');
|
|
78
|
-
const end = text.indexOf('\n');
|
|
79
|
-
if (end < 0)
|
|
80
|
-
return;
|
|
81
|
-
socket.end();
|
|
82
|
-
done(JSON.parse(text.slice(0, end)));
|
|
83
|
-
});
|
|
84
|
-
socket.once('error', (e) => (e.code === 'ENOENT' || e.code === 'ECONNREFUSED' ? done(undefined) : fail(e)));
|
|
85
|
-
});
|
|
14
|
+
import { carryPlaces, Harbor, landPlaces, readCarried, rootLine } from '../harbor/index.js';
|
|
15
|
+
import { askServed, MODULES, NodeTerrain, socketOf } from '../node/index.js';
|
|
16
|
+
import { CryptoEntropy } from '../pointer/index.js';
|
|
17
|
+
import { TCP_OPENING } from '../tcp/index.js';
|
|
18
|
+
// A harbor of a folder, opened for one ask: it listens nowhere and serves
|
|
19
|
+
// nothing.
|
|
20
|
+
export const open = (dir) => Harbor.open(new NodeTerrain({ dir, wakes: false, serves: false }));
|
|
21
|
+
// Whether a harbor of this folder is served now.
|
|
22
|
+
export const isServed = async (dir) => (await askServed(dir, {})) !== undefined;
|
|
86
23
|
export const request = async (dir, req) => {
|
|
87
|
-
const served = await
|
|
24
|
+
const served = await askServed(dir, req);
|
|
88
25
|
if (served !== undefined)
|
|
89
26
|
return served;
|
|
90
|
-
const
|
|
27
|
+
const harbor = await open(dir);
|
|
91
28
|
try {
|
|
92
29
|
return await rootLine(harbor, req);
|
|
93
30
|
}
|
|
94
31
|
finally {
|
|
95
|
-
await
|
|
32
|
+
await harbor.close();
|
|
96
33
|
}
|
|
97
34
|
};
|
|
98
|
-
// The harbor
|
|
99
|
-
//
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const
|
|
104
|
-
const
|
|
105
|
-
const root = socketOf(dir);
|
|
106
|
-
await rm(root, { force: true });
|
|
107
|
-
const server = createServer((socket) => {
|
|
108
|
-
let text = '';
|
|
109
|
-
socket.on('data', (chunk) => {
|
|
110
|
-
text += chunk.toString('utf8');
|
|
111
|
-
for (let end = text.indexOf('\n'); end >= 0; end = text.indexOf('\n')) {
|
|
112
|
-
const line = text.slice(0, end);
|
|
113
|
-
text = text.slice(end + 1);
|
|
114
|
-
let value;
|
|
115
|
-
try {
|
|
116
|
-
value = JSON.parse(line);
|
|
117
|
-
}
|
|
118
|
-
catch {
|
|
119
|
-
value = undefined;
|
|
120
|
-
}
|
|
121
|
-
void rootLine(harbor, value)
|
|
122
|
-
.catch(() => ({ error: 'the harbor threw' }))
|
|
123
|
-
.then((answer) => socket.write(`${JSON.stringify(answer)}\n`));
|
|
124
|
-
}
|
|
125
|
-
});
|
|
126
|
-
socket.on('error', () => socket.destroy());
|
|
127
|
-
});
|
|
128
|
-
await new Promise((done) => server.listen(root, done));
|
|
129
|
-
await chmod(root, 0o600);
|
|
35
|
+
// The harbor of a folder, read as bytes alone, its seed only where the
|
|
36
|
+
// one who asks says so, since custody holds it and a package without it
|
|
37
|
+
// opens nowhere. No harbor opens, so nothing is asked and nothing is
|
|
38
|
+
// written.
|
|
39
|
+
export const carry = async (dir, seed) => {
|
|
40
|
+
const places = await carryPlaces(new FolderMemory(dir));
|
|
41
|
+
const modules = await readFile(join(dir, MODULES), 'utf8').then((text) => JSON.parse(text), () => undefined);
|
|
130
42
|
return {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
close: async () => {
|
|
135
|
-
await new Promise((done) => server.close(done));
|
|
136
|
-
await rm(root, { force: true });
|
|
137
|
-
await listener.close();
|
|
138
|
-
await carrier.close();
|
|
139
|
-
},
|
|
43
|
+
...(seed ? { seed: hex(await new FolderCustody(dir, new CryptoEntropy()).seed()) } : {}),
|
|
44
|
+
places,
|
|
45
|
+
...(modules ? { modules } : {}),
|
|
140
46
|
};
|
|
141
47
|
};
|
|
48
|
+
// What `nervur edge pack` hands a deploy: the seed, for the worker's
|
|
49
|
+
// `NERVUR_SEED`, and the sealed package without it, for the bundle.
|
|
50
|
+
export const pack = async (dir, file) => {
|
|
51
|
+
if (!existsSync(join(dir, 'seed')))
|
|
52
|
+
throw new Error(`no harbor stands at ${dir}`);
|
|
53
|
+
const { seed, places } = await carry(dir, true);
|
|
54
|
+
await writeFile(file, `${JSON.stringify({ places })}\n`, { mode: 0o600 });
|
|
55
|
+
return { seed: seed, package: file, places: Object.keys(places).length };
|
|
56
|
+
};
|
|
57
|
+
// A carried harbor into an empty folder. A folder holding a seed or a
|
|
58
|
+
// place is left as it stands, and a package with no seed needs one there
|
|
59
|
+
// already, which a terrain that seals its custody writes its own way.
|
|
60
|
+
export const land = async (dir, value) => {
|
|
61
|
+
const held = readCarried(value);
|
|
62
|
+
if (held === null)
|
|
63
|
+
throw new Error('that is no carried harbor');
|
|
64
|
+
const memory = new FolderMemory(dir);
|
|
65
|
+
if ((await memory.places()).length > 0 || (held.seed !== undefined && existsSync(join(dir, 'seed'))))
|
|
66
|
+
throw new Error(`a harbor stands at ${dir}`);
|
|
67
|
+
await mkdir(dir, { recursive: true, mode: 0o700 });
|
|
68
|
+
if (held.seed !== undefined)
|
|
69
|
+
await writeFile(join(dir, 'seed'), `${held.seed}\n`, { mode: 0o600, flag: 'wx' });
|
|
70
|
+
else if (!existsSync(join(dir, 'seed')))
|
|
71
|
+
throw new Error('the package carries no seed, and none is held here');
|
|
72
|
+
if (held.modules && !existsSync(join(dir, MODULES)))
|
|
73
|
+
await writeFile(join(dir, MODULES), `${JSON.stringify(held.modules, null, 2)}\n`, { mode: 0o600 });
|
|
74
|
+
return landPlaces(memory, held);
|
|
75
|
+
};
|
|
76
|
+
// The harbor served: a harbor of the Node ground, with Quo over TCP on
|
|
77
|
+
// host and port.
|
|
78
|
+
export const serve = async (dir, host, port) => {
|
|
79
|
+
const harbor = await Harbor.open(new NodeTerrain({ dir }));
|
|
80
|
+
if (!harbor.box.being(TCP_OPENING.key))
|
|
81
|
+
await harbor.root('open', { key: TCP_OPENING.key, class: TCP_OPENING.kind });
|
|
82
|
+
const tcp = harbor.box.being(TCP_OPENING.key);
|
|
83
|
+
if (host !== undefined || port !== undefined || tcp.listening().length === 0) {
|
|
84
|
+
const heard = (await harbor.root('ask', { being: TCP_OPENING.key, method: 'listen', args: { ...(host === undefined ? {} : { host }), ...(port === undefined ? {} : { port }) } }));
|
|
85
|
+
if (typeof heard.answer?.at !== 'string') {
|
|
86
|
+
await harbor.close();
|
|
87
|
+
throw new Error(`the harbor does not listen: ${JSON.stringify(heard)}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return { pk: harbor.box.pk, at: tcp.listening()[0], listening: harbor.carriers.flatMap((c) => c.listening()), root: socketOf(dir), close: () => harbor.close() };
|
|
91
|
+
};
|
package/dist/contract/index.d.ts
CHANGED
|
@@ -18,6 +18,8 @@ export interface Rows {
|
|
|
18
18
|
export declare abstract class Memory {
|
|
19
19
|
abstract read(place: string): Promise<Map<string, Uint8Array>>;
|
|
20
20
|
abstract write(place: string, entries: ReadonlyMap<string, Uint8Array | null>): Promise<void>;
|
|
21
|
+
abstract places(): Promise<string[]>;
|
|
22
|
+
abstract forget(place: string): Promise<void>;
|
|
21
23
|
}
|
|
22
24
|
export declare abstract class Custody {
|
|
23
25
|
abstract seed(): Promise<Uint8Array>;
|
|
@@ -35,5 +37,4 @@ export declare abstract class Loader {
|
|
|
35
37
|
}
|
|
36
38
|
export declare abstract class Carrier {
|
|
37
39
|
abstract carry(pk: string, bytes: Uint8Array): Promise<Uint8Array | null>;
|
|
38
|
-
route(_pk: string, _at: string | null): boolean;
|
|
39
40
|
}
|
package/dist/contract/index.js
CHANGED
|
@@ -22,12 +22,8 @@ export class Custody {
|
|
|
22
22
|
// one thing beside the seed a terrain hands in: a map, an import, a bundle.
|
|
23
23
|
export class Loader {
|
|
24
24
|
}
|
|
25
|
-
// Bytes to a ward pk in; bytes, or null for nothing, out.
|
|
25
|
+
// Bytes to a ward pk in; bytes, or null for nothing, out. A harbor hands a
|
|
26
|
+
// terrain's carrier what it holds no route for; what it routes goes to its
|
|
27
|
+
// carrier faculties.
|
|
26
28
|
export class Carrier {
|
|
27
|
-
// Where a ward pk is reached, or null to forget it: true when this
|
|
28
|
-
// carrier dials by address and took it. The addresses live in the dock's
|
|
29
|
-
// cells, and the harbor hands them here as it unpacks.
|
|
30
|
-
route(_pk, _at) {
|
|
31
|
-
return false;
|
|
32
|
-
}
|
|
33
29
|
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Custody, Memory, type Entropy, type Module } from '../contract/index.ts';
|
|
2
|
+
import { Harbor, type Carried, type GroundFaculty, type GroundProbe } from '../harbor/index.ts';
|
|
3
|
+
import { type WebListen } from '../line/index.ts';
|
|
4
|
+
import { PointerTerrain } from '../pointer/index.ts';
|
|
5
|
+
export type { Carried } from '../harbor/index.ts';
|
|
6
|
+
export declare const EDGE = "edge";
|
|
7
|
+
export interface EdgeStorage {
|
|
8
|
+
get(key: string): Promise<unknown>;
|
|
9
|
+
put(entries: Record<string, unknown>): Promise<void>;
|
|
10
|
+
delete(keys: string[]): Promise<number>;
|
|
11
|
+
list(options: {
|
|
12
|
+
prefix: string;
|
|
13
|
+
}): Promise<Map<string, unknown>>;
|
|
14
|
+
transaction<T>(work: (txn: EdgeStorage) => Promise<T>): Promise<T>;
|
|
15
|
+
}
|
|
16
|
+
export interface EdgeState {
|
|
17
|
+
readonly storage: EdgeStorage;
|
|
18
|
+
blockConcurrencyWhile<T>(work: () => Promise<T>): Promise<T>;
|
|
19
|
+
}
|
|
20
|
+
export type EdgeEnv = {
|
|
21
|
+
readonly NERVUR_SEED?: string;
|
|
22
|
+
readonly NERVUR_SECRET?: string;
|
|
23
|
+
};
|
|
24
|
+
export type EdgeGiven = {
|
|
25
|
+
readonly state: EdgeState;
|
|
26
|
+
readonly env: EdgeEnv;
|
|
27
|
+
readonly package?: Carried;
|
|
28
|
+
};
|
|
29
|
+
export declare class EdgeMemory extends Memory {
|
|
30
|
+
#private;
|
|
31
|
+
constructor(storage: EdgeStorage, root?: string);
|
|
32
|
+
read(place: string): Promise<Map<string, Uint8Array>>;
|
|
33
|
+
write(place: string, entries: ReadonlyMap<string, Uint8Array | null>): Promise<void>;
|
|
34
|
+
places(): Promise<string[]>;
|
|
35
|
+
forget(place: string): Promise<void>;
|
|
36
|
+
}
|
|
37
|
+
export type EdgeKeys = {
|
|
38
|
+
readonly seed?: string | undefined;
|
|
39
|
+
readonly secret?: string | undefined;
|
|
40
|
+
};
|
|
41
|
+
export declare class EdgeCustody extends Custody {
|
|
42
|
+
#private;
|
|
43
|
+
constructor(storage: EdgeStorage, keys: EdgeKeys, entropy?: Entropy, root?: string);
|
|
44
|
+
seed(): Promise<Uint8Array>;
|
|
45
|
+
}
|
|
46
|
+
export declare const edgeTcp: GroundFaculty;
|
|
47
|
+
export type EdgeParts = {
|
|
48
|
+
readonly given: EdgeGiven;
|
|
49
|
+
readonly modules?: readonly Module[];
|
|
50
|
+
};
|
|
51
|
+
export declare class EdgeTerrain extends PointerTerrain {
|
|
52
|
+
#private;
|
|
53
|
+
readonly given: EdgeGiven;
|
|
54
|
+
listening: WebListen | undefined;
|
|
55
|
+
constructor(parts: EdgeParts);
|
|
56
|
+
claim(): Promise<void>;
|
|
57
|
+
stand(harbor: Harbor): Promise<void>;
|
|
58
|
+
answer(request: Request): Promise<Response>;
|
|
59
|
+
}
|
|
60
|
+
export declare const edgeGround: GroundProbe;
|
|
61
|
+
export type HarborObjectParts = {
|
|
62
|
+
readonly modules: readonly Module[];
|
|
63
|
+
readonly package?: Carried | ((env: EdgeEnv) => Carried | undefined);
|
|
64
|
+
readonly path?: string;
|
|
65
|
+
readonly stood?: (harbor: Harbor) => Promise<void>;
|
|
66
|
+
};
|
|
67
|
+
export type HarborObjectClass = new (state: EdgeState, env: EdgeEnv) => {
|
|
68
|
+
readonly harbor: Promise<Harbor>;
|
|
69
|
+
fetch(request: Request): Promise<Response>;
|
|
70
|
+
};
|
|
71
|
+
export declare const harborObject: ({ modules, package: carried, path, stood }: HarborObjectParts) => HarborObjectClass;
|