@earendil-works/pi-client 0.84.4 → 0.85.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2 -0
- package/README.md +37 -28
- package/dist/client.d.ts +20 -15
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +248 -245
- package/dist/client.js.map +1 -1
- package/dist/connection.d.ts +4 -3
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +13 -9
- package/dist/connection.js.map +1 -1
- package/dist/errors.d.ts +6 -15
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +10 -28
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +3 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +14 -8
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/unix.d.ts +14 -1
- package/dist/unix.d.ts.map +1 -1
- package/dist/unix.js +138 -8
- package/dist/unix.js.map +1 -1
- package/package.json +3 -2
- package/dist/session-handle.d.ts +0 -54
- package/dist/session-handle.d.ts.map +0 -1
- package/dist/session-handle.js +0 -51
- package/dist/session-handle.js.map +0 -1
- package/dist/state.d.ts +0 -22
- package/dist/state.d.ts.map +0 -1
- package/dist/state.js +0 -145
- package/dist/state.js.map +0 -1
package/dist/state.js
DELETED
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
import { toError } from "./errors.js";
|
|
2
|
-
export class ClientState {
|
|
3
|
-
#sessionSnapshots = new Map();
|
|
4
|
-
#attachedSessionIds = new Set();
|
|
5
|
-
#snapshotListeners = new Set();
|
|
6
|
-
#eventListeners = new Set();
|
|
7
|
-
#sessionSnapshotListeners = new Map();
|
|
8
|
-
#sessionEventListeners = new Map();
|
|
9
|
-
#onListenerError;
|
|
10
|
-
#snapshot;
|
|
11
|
-
constructor(onListenerError) {
|
|
12
|
-
this.#onListenerError = onListenerError;
|
|
13
|
-
}
|
|
14
|
-
get snapshot() {
|
|
15
|
-
return this.#snapshot;
|
|
16
|
-
}
|
|
17
|
-
reset() {
|
|
18
|
-
this.#snapshot = undefined;
|
|
19
|
-
this.#sessionSnapshots.clear();
|
|
20
|
-
this.#attachedSessionIds.clear();
|
|
21
|
-
}
|
|
22
|
-
clearAttachments() {
|
|
23
|
-
this.#attachedSessionIds.clear();
|
|
24
|
-
}
|
|
25
|
-
dispose() {
|
|
26
|
-
this.reset();
|
|
27
|
-
this.#snapshotListeners.clear();
|
|
28
|
-
this.#eventListeners.clear();
|
|
29
|
-
this.#sessionSnapshotListeners.clear();
|
|
30
|
-
this.#sessionEventListeners.clear();
|
|
31
|
-
}
|
|
32
|
-
getSessionSnapshot(sessionId) {
|
|
33
|
-
return this.#sessionSnapshots.get(sessionId);
|
|
34
|
-
}
|
|
35
|
-
isSessionAttached(sessionId) {
|
|
36
|
-
return this.#attachedSessionIds.has(sessionId);
|
|
37
|
-
}
|
|
38
|
-
forgetSessionSnapshot(sessionId) {
|
|
39
|
-
const previous = this.#sessionSnapshots.get(sessionId);
|
|
40
|
-
this.#sessionSnapshots.delete(sessionId);
|
|
41
|
-
return previous;
|
|
42
|
-
}
|
|
43
|
-
restoreSessionSnapshot(snapshot) {
|
|
44
|
-
if (!this.#sessionSnapshots.has(snapshot.id))
|
|
45
|
-
this.#sessionSnapshots.set(snapshot.id, snapshot);
|
|
46
|
-
}
|
|
47
|
-
subscribe(listener) {
|
|
48
|
-
this.#snapshotListeners.add(listener);
|
|
49
|
-
return () => this.#snapshotListeners.delete(listener);
|
|
50
|
-
}
|
|
51
|
-
onEvent(listener) {
|
|
52
|
-
this.#eventListeners.add(listener);
|
|
53
|
-
return () => this.#eventListeners.delete(listener);
|
|
54
|
-
}
|
|
55
|
-
subscribeSession(sessionId, listener) {
|
|
56
|
-
return addMappedListener(this.#sessionSnapshotListeners, sessionId, listener);
|
|
57
|
-
}
|
|
58
|
-
onSessionEvent(sessionId, listener) {
|
|
59
|
-
return addMappedListener(this.#sessionEventListeners, sessionId, listener);
|
|
60
|
-
}
|
|
61
|
-
applyResult(result) {
|
|
62
|
-
if (result.command === "list")
|
|
63
|
-
return;
|
|
64
|
-
if (result.command === "detach") {
|
|
65
|
-
this.#attachedSessionIds.delete(result.sessionId);
|
|
66
|
-
const snapshot = this.#sessionSnapshots.get(result.sessionId);
|
|
67
|
-
if (snapshot)
|
|
68
|
-
this.#applySessionSnapshot({ ...snapshot, attached: false }, true);
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
this.#applySessionSnapshot(result.session);
|
|
72
|
-
}
|
|
73
|
-
applyEvent(event) {
|
|
74
|
-
if (event.type === "server_snapshot")
|
|
75
|
-
this.applyServerSnapshot(event.snapshot);
|
|
76
|
-
if (event.type === "session_snapshot")
|
|
77
|
-
this.#applySessionSnapshot(event.snapshot);
|
|
78
|
-
if (event.type === "session_removed") {
|
|
79
|
-
this.#sessionSnapshots.delete(event.sessionId);
|
|
80
|
-
this.#attachedSessionIds.delete(event.sessionId);
|
|
81
|
-
}
|
|
82
|
-
this.#notify(this.#eventListeners, event);
|
|
83
|
-
const sessionId = getEventSessionId(event);
|
|
84
|
-
if (sessionId)
|
|
85
|
-
this.#notify(this.#sessionEventListeners.get(sessionId), event);
|
|
86
|
-
}
|
|
87
|
-
applyServerSnapshot(snapshot) {
|
|
88
|
-
if (this.#snapshot && snapshot.revision < this.#snapshot.revision)
|
|
89
|
-
return;
|
|
90
|
-
this.#snapshot = snapshot;
|
|
91
|
-
this.#notify(this.#snapshotListeners, snapshot);
|
|
92
|
-
}
|
|
93
|
-
#applySessionSnapshot(snapshot, force = false) {
|
|
94
|
-
const current = this.#sessionSnapshots.get(snapshot.id);
|
|
95
|
-
if (!force && current && snapshot.revision < current.revision)
|
|
96
|
-
return;
|
|
97
|
-
this.#sessionSnapshots.set(snapshot.id, snapshot);
|
|
98
|
-
if (snapshot.attached)
|
|
99
|
-
this.#attachedSessionIds.add(snapshot.id);
|
|
100
|
-
else
|
|
101
|
-
this.#attachedSessionIds.delete(snapshot.id);
|
|
102
|
-
this.#notify(this.#sessionSnapshotListeners.get(snapshot.id), snapshot);
|
|
103
|
-
}
|
|
104
|
-
#notify(listeners, value) {
|
|
105
|
-
for (const listener of listeners ?? []) {
|
|
106
|
-
try {
|
|
107
|
-
listener(value);
|
|
108
|
-
}
|
|
109
|
-
catch (error) {
|
|
110
|
-
this.#reportListenerError(error);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
#reportListenerError(error) {
|
|
115
|
-
if (!this.#onListenerError)
|
|
116
|
-
return;
|
|
117
|
-
try {
|
|
118
|
-
this.#onListenerError(toError(error));
|
|
119
|
-
}
|
|
120
|
-
catch {
|
|
121
|
-
// Diagnostics cannot affect client state.
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
function addMappedListener(listenersById, id, listener) {
|
|
126
|
-
let listeners = listenersById.get(id);
|
|
127
|
-
if (!listeners) {
|
|
128
|
-
listeners = new Set();
|
|
129
|
-
listenersById.set(id, listeners);
|
|
130
|
-
}
|
|
131
|
-
listeners.add(listener);
|
|
132
|
-
return () => {
|
|
133
|
-
listeners.delete(listener);
|
|
134
|
-
if (listeners.size === 0)
|
|
135
|
-
listenersById.delete(id);
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
function getEventSessionId(event) {
|
|
139
|
-
if (event.type === "session_snapshot")
|
|
140
|
-
return event.snapshot.id;
|
|
141
|
-
if (event.type === "session_progress" || event.type === "session_removed")
|
|
142
|
-
return event.sessionId;
|
|
143
|
-
return undefined;
|
|
144
|
-
}
|
|
145
|
-
//# sourceMappingURL=state.js.map
|
package/dist/state.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"state.js","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"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","sourcesContent":["import type { CommandResult, ServerEvent, ServerSnapshot, SessionSnapshot } from \"@earendil-works/pi-protocol\";\nimport { toError } from \"./errors.ts\";\nimport type { ListenerErrorHandler, Unsubscribe } from \"./types.ts\";\n\nexport class ClientState {\n\treadonly #sessionSnapshots = new Map<string, SessionSnapshot>();\n\treadonly #attachedSessionIds = new Set<string>();\n\treadonly #snapshotListeners = new Set<(snapshot: ServerSnapshot) => void>();\n\treadonly #eventListeners = new Set<(event: ServerEvent) => void>();\n\treadonly #sessionSnapshotListeners = new Map<string, Set<(snapshot: SessionSnapshot) => void>>();\n\treadonly #sessionEventListeners = new Map<string, Set<(event: ServerEvent) => void>>();\n\treadonly #onListenerError: ListenerErrorHandler | undefined;\n\t#snapshot: ServerSnapshot | undefined;\n\n\tconstructor(onListenerError?: ListenerErrorHandler) {\n\t\tthis.#onListenerError = onListenerError;\n\t}\n\n\tget snapshot(): ServerSnapshot | undefined {\n\t\treturn this.#snapshot;\n\t}\n\n\treset(): void {\n\t\tthis.#snapshot = undefined;\n\t\tthis.#sessionSnapshots.clear();\n\t\tthis.#attachedSessionIds.clear();\n\t}\n\n\tclearAttachments(): void {\n\t\tthis.#attachedSessionIds.clear();\n\t}\n\n\tdispose(): void {\n\t\tthis.reset();\n\t\tthis.#snapshotListeners.clear();\n\t\tthis.#eventListeners.clear();\n\t\tthis.#sessionSnapshotListeners.clear();\n\t\tthis.#sessionEventListeners.clear();\n\t}\n\n\tgetSessionSnapshot(sessionId: string): SessionSnapshot | undefined {\n\t\treturn this.#sessionSnapshots.get(sessionId);\n\t}\n\n\tisSessionAttached(sessionId: string): boolean {\n\t\treturn this.#attachedSessionIds.has(sessionId);\n\t}\n\n\tforgetSessionSnapshot(sessionId: string): SessionSnapshot | undefined {\n\t\tconst previous = this.#sessionSnapshots.get(sessionId);\n\t\tthis.#sessionSnapshots.delete(sessionId);\n\t\treturn previous;\n\t}\n\n\trestoreSessionSnapshot(snapshot: SessionSnapshot): void {\n\t\tif (!this.#sessionSnapshots.has(snapshot.id)) this.#sessionSnapshots.set(snapshot.id, snapshot);\n\t}\n\n\tsubscribe(listener: (snapshot: ServerSnapshot) => void): Unsubscribe {\n\t\tthis.#snapshotListeners.add(listener);\n\t\treturn () => this.#snapshotListeners.delete(listener);\n\t}\n\n\tonEvent(listener: (event: ServerEvent) => void): Unsubscribe {\n\t\tthis.#eventListeners.add(listener);\n\t\treturn () => this.#eventListeners.delete(listener);\n\t}\n\n\tsubscribeSession(sessionId: string, listener: (snapshot: SessionSnapshot) => void): Unsubscribe {\n\t\treturn addMappedListener(this.#sessionSnapshotListeners, sessionId, listener);\n\t}\n\n\tonSessionEvent(sessionId: string, listener: (event: ServerEvent) => void): Unsubscribe {\n\t\treturn addMappedListener(this.#sessionEventListeners, sessionId, listener);\n\t}\n\n\tapplyResult(result: CommandResult): void {\n\t\tif (result.command === \"list\") return;\n\t\tif (result.command === \"detach\") {\n\t\t\tthis.#attachedSessionIds.delete(result.sessionId);\n\t\t\tconst snapshot = this.#sessionSnapshots.get(result.sessionId);\n\t\t\tif (snapshot) this.#applySessionSnapshot({ ...snapshot, attached: false }, true);\n\t\t\treturn;\n\t\t}\n\t\tthis.#applySessionSnapshot(result.session);\n\t}\n\n\tapplyEvent(event: ServerEvent): void {\n\t\tif (event.type === \"server_snapshot\") this.applyServerSnapshot(event.snapshot);\n\t\tif (event.type === \"session_snapshot\") this.#applySessionSnapshot(event.snapshot);\n\t\tif (event.type === \"session_removed\") {\n\t\t\tthis.#sessionSnapshots.delete(event.sessionId);\n\t\t\tthis.#attachedSessionIds.delete(event.sessionId);\n\t\t}\n\t\tthis.#notify(this.#eventListeners, event);\n\t\tconst sessionId = getEventSessionId(event);\n\t\tif (sessionId) this.#notify(this.#sessionEventListeners.get(sessionId), event);\n\t}\n\n\tapplyServerSnapshot(snapshot: ServerSnapshot): void {\n\t\tif (this.#snapshot && snapshot.revision < this.#snapshot.revision) return;\n\t\tthis.#snapshot = snapshot;\n\t\tthis.#notify(this.#snapshotListeners, snapshot);\n\t}\n\n\t#applySessionSnapshot(snapshot: SessionSnapshot, force = false): void {\n\t\tconst current = this.#sessionSnapshots.get(snapshot.id);\n\t\tif (!force && current && snapshot.revision < current.revision) return;\n\t\tthis.#sessionSnapshots.set(snapshot.id, snapshot);\n\t\tif (snapshot.attached) this.#attachedSessionIds.add(snapshot.id);\n\t\telse this.#attachedSessionIds.delete(snapshot.id);\n\t\tthis.#notify(this.#sessionSnapshotListeners.get(snapshot.id), snapshot);\n\t}\n\n\t#notify<T>(listeners: Iterable<(value: T) => void> | undefined, value: T): void {\n\t\tfor (const listener of listeners ?? []) {\n\t\t\ttry {\n\t\t\t\tlistener(value);\n\t\t\t} catch (error) {\n\t\t\t\tthis.#reportListenerError(error);\n\t\t\t}\n\t\t}\n\t}\n\n\t#reportListenerError(error: unknown): void {\n\t\tif (!this.#onListenerError) return;\n\t\ttry {\n\t\t\tthis.#onListenerError(toError(error));\n\t\t} catch {\n\t\t\t// Diagnostics cannot affect client state.\n\t\t}\n\t}\n}\n\nfunction addMappedListener<T>(\n\tlistenersById: Map<string, Set<(value: T) => void>>,\n\tid: string,\n\tlistener: (value: T) => void,\n): Unsubscribe {\n\tlet listeners = listenersById.get(id);\n\tif (!listeners) {\n\t\tlisteners = new Set();\n\t\tlistenersById.set(id, listeners);\n\t}\n\tlisteners.add(listener);\n\treturn () => {\n\t\tlisteners.delete(listener);\n\t\tif (listeners.size === 0) listenersById.delete(id);\n\t};\n}\n\nfunction getEventSessionId(event: ServerEvent): string | undefined {\n\tif (event.type === \"session_snapshot\") return event.snapshot.id;\n\tif (event.type === \"session_progress\" || event.type === \"session_removed\") return event.sessionId;\n\treturn undefined;\n}\n"]}
|