@phreshos/client 0.1.51 → 0.1.53
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/dist/client-memory.d.ts +3 -0
- package/dist/client-memory.js +57 -0
- package/dist/context.js +3 -0
- package/dist/domain.js +5 -2
- package/dist/storage.js +54 -3
- package/dist/system.js +5 -9
- package/package.json +3 -3
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import wire from "./wire.js";
|
|
2
|
+
/** The System, not this browser document, owns the values and ordering. */
|
|
3
|
+
export default function clientMemory(target) {
|
|
4
|
+
const snapshots = new Map();
|
|
5
|
+
const address = () => target ? Promise.resolve(target) : wire.identity().then(({ process, reference }) => ({ identity: process, reference }));
|
|
6
|
+
const ask = async (operation, key, value, expected) => {
|
|
7
|
+
const [result] = await wire.request(["client-memory", await address(), operation, key, value, expected]);
|
|
8
|
+
return result;
|
|
9
|
+
};
|
|
10
|
+
return {
|
|
11
|
+
async get(key) {
|
|
12
|
+
const snapshot = await ask("snapshot", key);
|
|
13
|
+
snapshots.set(key, snapshot);
|
|
14
|
+
return snapshot.value;
|
|
15
|
+
},
|
|
16
|
+
async set(key, value) { snapshots.set(key, await ask("set", key, value)); },
|
|
17
|
+
async update(key, updater) {
|
|
18
|
+
let snapshot = snapshots.get(key) ?? await ask("snapshot", key);
|
|
19
|
+
for (;;) {
|
|
20
|
+
const next = updater(snapshot.value);
|
|
21
|
+
const result = await ask("compareAndSet", key, next, snapshot);
|
|
22
|
+
snapshots.set(key, result.snapshot);
|
|
23
|
+
if (result.changed)
|
|
24
|
+
return result.snapshot.value;
|
|
25
|
+
if (result.snapshot.run !== snapshot.run)
|
|
26
|
+
throw new Error("The Client run changed during the update");
|
|
27
|
+
snapshot = result.snapshot;
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
async delete(key) { const deleted = await ask("delete", key); snapshots.delete(key); return deleted; },
|
|
31
|
+
async entries() { return await ask("entries"); },
|
|
32
|
+
subscribe(key, subscriber) {
|
|
33
|
+
let active = true;
|
|
34
|
+
let stop;
|
|
35
|
+
let latest;
|
|
36
|
+
void address().then(value => {
|
|
37
|
+
if (!active)
|
|
38
|
+
return;
|
|
39
|
+
// Register first; an older snapshot response cannot overtake a newer event.
|
|
40
|
+
stop = wire.on("host-client-memory", key, (identity, snapshot) => {
|
|
41
|
+
if (identity !== value.identity || !active)
|
|
42
|
+
return;
|
|
43
|
+
deliver(snapshot);
|
|
44
|
+
}, value.identity);
|
|
45
|
+
void ask("snapshot", key).then(snapshot => deliver(snapshot)).catch(() => undefined);
|
|
46
|
+
}).catch(() => undefined);
|
|
47
|
+
const deliver = (snapshot) => {
|
|
48
|
+
if (!active || (latest && (latest.run !== snapshot.run || latest.revision > snapshot.revision)))
|
|
49
|
+
return;
|
|
50
|
+
latest = snapshot;
|
|
51
|
+
snapshots.set(key, snapshot);
|
|
52
|
+
subscriber(snapshot.value);
|
|
53
|
+
};
|
|
54
|
+
return () => { active = false; stop?.(); };
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
package/dist/context.js
CHANGED
|
@@ -2,6 +2,7 @@ import Deadline from "./deadline.js";
|
|
|
2
2
|
import { ClientEndpoint, ServerEndpoint, ServerTrafficHandle, TrafficHandle, endpointLifecycle, endpointEvents, process, program, visibleEndpoint, presentation, window as windowHandle, } from "./domain.js";
|
|
3
3
|
import Events from "./events.js";
|
|
4
4
|
import wire from "./wire.js";
|
|
5
|
+
import clientMemory from "./client-memory.js";
|
|
5
6
|
import { contextPermissions } from "./permissions.js";
|
|
6
7
|
class ContextServerHandle extends ServerEndpoint {
|
|
7
8
|
owner;
|
|
@@ -68,6 +69,7 @@ class ContextClientHandle extends ClientEndpoint {
|
|
|
68
69
|
traffic = new TrafficHandle(null, "client");
|
|
69
70
|
lifecycle = endpointLifecycle(currentAddress, "client");
|
|
70
71
|
window = windowHandle(currentAddress);
|
|
72
|
+
memory = clientMemory(null);
|
|
71
73
|
constructor(owner) {
|
|
72
74
|
super();
|
|
73
75
|
this.owner = owner;
|
|
@@ -95,6 +97,7 @@ contextClient = new ContextClientHandle(owner);
|
|
|
95
97
|
class ClientContext extends Events {
|
|
96
98
|
server = contextServer;
|
|
97
99
|
window = contextClient.window;
|
|
100
|
+
memory = contextClient.memory;
|
|
98
101
|
presentation = presentation(currentAddress);
|
|
99
102
|
permissions = contextPermissions();
|
|
100
103
|
constructor() {
|
package/dist/domain.js
CHANGED
|
@@ -6,6 +6,7 @@ import { area, programLogs, sql, store } from "./storage.js";
|
|
|
6
6
|
import startup from "./startup.js";
|
|
7
7
|
import { programPermissions } from "./permissions.js";
|
|
8
8
|
import wire from "./wire.js";
|
|
9
|
+
import clientMemory from "./client-memory.js";
|
|
9
10
|
const handles = new HandleRegistry();
|
|
10
11
|
class ProgramHandle extends CoreProgram {
|
|
11
12
|
subscribe;
|
|
@@ -79,10 +80,10 @@ class ProgramHandle extends CoreProgram {
|
|
|
79
80
|
const answer = await wire.request(["program-processes", this.address]);
|
|
80
81
|
return answer[0].map(record => process(record));
|
|
81
82
|
}
|
|
82
|
-
async
|
|
83
|
+
async oldestProcess() {
|
|
83
84
|
return chronological(await this.processes())[0] ?? null;
|
|
84
85
|
}
|
|
85
|
-
async
|
|
86
|
+
async newestProcess() {
|
|
86
87
|
return chronological(await this.processes()).at(-1) ?? null;
|
|
87
88
|
}
|
|
88
89
|
async findProcess(identityOrName) {
|
|
@@ -285,12 +286,14 @@ class ClientEndpointHandle extends CoreClientEndpoint {
|
|
|
285
286
|
traffic;
|
|
286
287
|
lifecycle;
|
|
287
288
|
window;
|
|
289
|
+
memory;
|
|
288
290
|
constructor(owner) {
|
|
289
291
|
super();
|
|
290
292
|
this.owner = owner;
|
|
291
293
|
this.traffic = new TrafficHandle(owner.address, "client");
|
|
292
294
|
this.lifecycle = endpointLifecycle(() => Promise.resolve(owner.address), "client");
|
|
293
295
|
this.window = window(async () => owner.address);
|
|
296
|
+
this.memory = clientMemory(owner.address);
|
|
294
297
|
const events = endpointEvents(owner.address, "client");
|
|
295
298
|
this.subscribe = events.subscribe;
|
|
296
299
|
this.wait = events.wait;
|
package/dist/storage.js
CHANGED
|
@@ -200,16 +200,67 @@ function parseEntry(value) {
|
|
|
200
200
|
return { kind: entry.kind, path: entry.path };
|
|
201
201
|
}
|
|
202
202
|
export function store(program) {
|
|
203
|
+
const snapshots = new Map();
|
|
204
|
+
const local = new Set();
|
|
205
|
+
const notify = (key, snapshot) => { for (const listener of local)
|
|
206
|
+
listener(key, snapshot); };
|
|
203
207
|
async function ask(operation, ...values) {
|
|
204
208
|
const answer = await wire.request(["store", program, operation, ...values]);
|
|
205
209
|
return answer[0];
|
|
206
210
|
}
|
|
207
211
|
return {
|
|
208
212
|
get: (key) => ask("get", key),
|
|
209
|
-
set
|
|
210
|
-
|
|
213
|
+
async set(key, value, ttl) {
|
|
214
|
+
const result = await ask("set", key, value, ttl);
|
|
215
|
+
snapshots.delete(key);
|
|
216
|
+
return result;
|
|
217
|
+
},
|
|
218
|
+
async getOrSet(key, initial) {
|
|
219
|
+
// The response carries the authoritative revision so an older initial event cannot win hydration.
|
|
220
|
+
const snapshot = await ask("getOrSet", key, initial);
|
|
221
|
+
notify(key, snapshot);
|
|
222
|
+
return snapshot.value;
|
|
223
|
+
},
|
|
224
|
+
async update(key, updater) {
|
|
225
|
+
let snapshot = snapshots.get(key) ?? await ask("snapshot", key);
|
|
226
|
+
for (;;) {
|
|
227
|
+
const next = updater(snapshot.value);
|
|
228
|
+
const result = await ask("compareAndSet", key, next, snapshot);
|
|
229
|
+
snapshots.set(key, result.snapshot);
|
|
230
|
+
notify(key, result.snapshot);
|
|
231
|
+
if (result.changed)
|
|
232
|
+
return result.snapshot.value;
|
|
233
|
+
snapshot = result.snapshot;
|
|
234
|
+
}
|
|
235
|
+
},
|
|
236
|
+
async delete(key) {
|
|
237
|
+
const result = await ask("delete", key);
|
|
238
|
+
for (const name of Array.isArray(key) ? key : [key])
|
|
239
|
+
snapshots.delete(name);
|
|
240
|
+
return result;
|
|
241
|
+
},
|
|
211
242
|
has: (key) => ask("has", key),
|
|
212
|
-
clear
|
|
243
|
+
async clear() { await ask("clear"); snapshots.clear(); },
|
|
244
|
+
subscribe(key, subscriber) {
|
|
245
|
+
let active = true;
|
|
246
|
+
let latest;
|
|
247
|
+
const deliver = (snapshot) => {
|
|
248
|
+
if (!active || (latest?.run === snapshot.run && latest.revision >= snapshot.revision))
|
|
249
|
+
return;
|
|
250
|
+
latest = snapshot;
|
|
251
|
+
snapshots.set(key, snapshot);
|
|
252
|
+
subscriber(snapshot.value);
|
|
253
|
+
};
|
|
254
|
+
const stop = wire.on("program-host", "storeChange", (reference, changedKey, snapshot) => {
|
|
255
|
+
if (reference === program.reference && changedKey === key)
|
|
256
|
+
deliver(snapshot);
|
|
257
|
+
}, program.reference);
|
|
258
|
+
const localListener = (changedKey, snapshot) => { if (changedKey === key)
|
|
259
|
+
deliver(snapshot); };
|
|
260
|
+
local.add(localListener);
|
|
261
|
+
void ask("snapshot", key).then(deliver).catch(() => undefined);
|
|
262
|
+
return () => { active = false; local.delete(localListener); stop(); };
|
|
263
|
+
}
|
|
213
264
|
};
|
|
214
265
|
}
|
|
215
266
|
export function sql(kind, program) {
|
package/dist/system.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { parseShellEvent, parseAuthenticationRequirements, parseAuthenticationState, parsePermissions, parseSessionEndSnapshot, execute as executeRequest } from "@phreshos/core";
|
|
1
|
+
import { parseShellEvent, parseAuthenticationRequirements, parseAuthenticationState, parsePermissions, parseSessionEndSnapshot, parseSystemProgramListOptions, parseSystemServiceListOptions, execute as executeRequest } from "@phreshos/core";
|
|
2
2
|
import ClientAppearance from "./appearance.js";
|
|
3
3
|
import wire from "./wire.js";
|
|
4
4
|
import { prepareService } from "./service.js";
|
|
@@ -36,12 +36,8 @@ class SystemServiceHandle extends Events {
|
|
|
36
36
|
observer(event, prepareService(values[1]));
|
|
37
37
|
}));
|
|
38
38
|
}
|
|
39
|
-
async list() {
|
|
40
|
-
const [addresses] = await wire.request(["host-service-list"]);
|
|
41
|
-
return addresses.map(address => prepareService(address));
|
|
42
|
-
}
|
|
43
|
-
async search(name) {
|
|
44
|
-
const [addresses] = await wire.request(["host-service-search", name]);
|
|
39
|
+
async list(options = {}) {
|
|
40
|
+
const [addresses] = await wire.request(["host-service-list", parseSystemServiceListOptions(options)]);
|
|
45
41
|
return addresses.map(address => prepareService(address));
|
|
46
42
|
}
|
|
47
43
|
prepare(address) { return prepareService(address); }
|
|
@@ -53,8 +49,8 @@ class SystemProgramHandle extends Events {
|
|
|
53
49
|
observer(event, systemProgramEvent(event, values));
|
|
54
50
|
}));
|
|
55
51
|
}
|
|
56
|
-
async list(
|
|
57
|
-
const answer = await wire.request(["host-program-list",
|
|
52
|
+
async list(options = {}) {
|
|
53
|
+
const answer = await wire.request(["host-program-list", parseSystemProgramListOptions(options)]);
|
|
58
54
|
return answer[0].map(program);
|
|
59
55
|
}
|
|
60
56
|
async find(identity) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phreshos/client",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.53",
|
|
4
4
|
"description": "The SDK used by a Program's client endpoint.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/main.js",
|
|
@@ -46,13 +46,13 @@
|
|
|
46
46
|
"test": "vitest run --project default"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
|
-
"@phreshos/core": "^0.1.
|
|
49
|
+
"@phreshos/core": "^0.1.63"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"@the-link/messagepack": "^0.1.0"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
|
-
|
|
55
|
+
"@phreshos/core": "^0.1.63",
|
|
56
56
|
"typescript": "^6.0.3",
|
|
57
57
|
"vitest": "^4.1.10"
|
|
58
58
|
}
|