statewire 0.0.1 → 0.0.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 +14 -13
- package/dist/client/changes.d.ts +2 -2
- package/dist/client/changes.d.ts.map +1 -1
- package/dist/client/changes.js +3 -10
- package/dist/client/changes.js.map +1 -1
- package/dist/client/connection.d.ts +18 -39
- package/dist/client/connection.d.ts.map +1 -1
- package/dist/client/connection.js +61 -54
- package/dist/client/connection.js.map +1 -1
- package/dist/client/core.d.ts +7 -11
- package/dist/client/core.d.ts.map +1 -1
- package/dist/client/core.js +58 -55
- package/dist/client/core.js.map +1 -1
- package/dist/client/session.d.ts +12 -26
- package/dist/client/session.d.ts.map +1 -1
- package/dist/client/session.js +70 -43
- package/dist/client/session.js.map +1 -1
- package/dist/client/sessionStore.d.ts +1 -1
- package/dist/client/sessionStore.js.map +1 -1
- package/dist/client/timing.d.ts +2 -1
- package/dist/client/timing.d.ts.map +1 -1
- package/dist/client/timing.js +5 -1
- package/dist/client/timing.js.map +1 -1
- package/dist/host/adapter.d.ts +8 -0
- package/dist/host/adapter.d.ts.map +1 -0
- package/dist/host/adapter.js +23 -0
- package/dist/host/adapter.js.map +1 -0
- package/dist/host/draft.d.ts +21 -7
- package/dist/host/draft.d.ts.map +1 -1
- package/dist/host/draft.js +190 -103
- package/dist/host/draft.js.map +1 -1
- package/dist/host/host.d.ts +19 -35
- package/dist/host/host.d.ts.map +1 -1
- package/dist/host/host.js +86 -54
- package/dist/host/host.js.map +1 -1
- package/dist/host/socket.d.ts +14 -0
- package/dist/host/socket.d.ts.map +1 -0
- package/dist/host/socket.js +511 -0
- package/dist/host/socket.js.map +1 -0
- package/dist/host-internal.d.ts +27 -0
- package/dist/host-internal.d.ts.map +1 -0
- package/dist/host-internal.js +3 -0
- package/dist/host.d.ts +54 -0
- package/dist/host.d.ts.map +1 -0
- package/dist/host.js +4 -0
- package/dist/index.d.ts +5 -28
- package/dist/index.js +2 -3
- package/dist/statewire.d.ts +97 -10
- package/dist/statewire.d.ts.map +1 -1
- package/dist/statewire.js +32 -36
- package/dist/statewire.js.map +1 -1
- package/dist/transports/fake-sse.d.ts +12 -0
- package/dist/transports/fake-sse.d.ts.map +1 -0
- package/dist/transports/fake-sse.js +39 -0
- package/dist/transports/fake-sse.js.map +1 -0
- package/dist/transports/fixtures.d.ts +33 -0
- package/dist/transports/fixtures.d.ts.map +1 -0
- package/dist/transports/fixtures.js +50 -0
- package/dist/transports/fixtures.js.map +1 -0
- package/dist/transports/remote.d.ts +6 -13
- package/dist/transports/remote.d.ts.map +1 -1
- package/dist/transports/remote.js +7 -25
- package/dist/transports/remote.js.map +1 -1
- package/dist/transports/sse.d.ts +32 -20
- package/dist/transports/sse.d.ts.map +1 -1
- package/dist/transports/sse.js +57 -46
- package/dist/transports/sse.js.map +1 -1
- package/dist/transports/testing.d.ts +8 -6
- package/dist/transports/testing.d.ts.map +1 -1
- package/dist/transports/testing.js +19 -46
- package/dist/transports/testing.js.map +1 -1
- package/dist/transports/ws.d.ts +4 -13
- package/dist/transports/ws.d.ts.map +1 -1
- package/dist/transports/ws.js +59 -63
- package/dist/transports/ws.js.map +1 -1
- package/dist/types.d.ts +55 -100
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/util/draftTraps.d.ts +6 -0
- package/dist/util/draftTraps.d.ts.map +1 -0
- package/dist/util/draftTraps.js +25 -0
- package/dist/util/draftTraps.js.map +1 -0
- package/dist/util/macrotask.d.ts +5 -0
- package/dist/util/macrotask.d.ts.map +1 -0
- package/dist/util/macrotask.js +25 -0
- package/dist/util/macrotask.js.map +1 -0
- package/dist/util/methodProxy.d.ts +5 -0
- package/dist/util/methodProxy.d.ts.map +1 -0
- package/dist/util/methodProxy.js +17 -0
- package/dist/util/methodProxy.js.map +1 -0
- package/dist/util/randomId.d.ts +5 -0
- package/dist/util/randomId.d.ts.map +1 -0
- package/dist/util/randomId.js +6 -0
- package/dist/util/randomId.js.map +1 -0
- package/dist/{client/optimistic.d.ts → util/recordMutations.d.ts} +2 -2
- package/dist/util/recordMutations.d.ts.map +1 -0
- package/dist/{client/optimistic.js → util/recordMutations.js} +5 -24
- package/dist/util/recordMutations.js.map +1 -0
- package/dist/util/statePath.d.ts +14 -0
- package/dist/util/statePath.d.ts.map +1 -0
- package/dist/util/statePath.js +31 -0
- package/dist/util/statePath.js.map +1 -0
- package/dist/wire/meta.d.ts +6 -1
- package/dist/wire/meta.d.ts.map +1 -1
- package/dist/wire/meta.js +30 -28
- package/dist/wire/meta.js.map +1 -1
- package/dist/wire/ops.d.ts +5 -26
- package/dist/wire/ops.d.ts.map +1 -1
- package/dist/wire/ops.js +67 -80
- package/dist/wire/ops.js.map +1 -1
- package/package.json +15 -11
- package/src/client/changes.ts +5 -15
- package/src/client/connection.ts +87 -113
- package/src/client/core.ts +57 -91
- package/src/client/session.ts +129 -90
- package/src/client/sessionStore.ts +1 -1
- package/src/client/timing.ts +6 -0
- package/src/host/adapter.ts +33 -0
- package/src/host/draft.ts +293 -125
- package/src/host/host.ts +122 -110
- package/src/host/socket.ts +618 -0
- package/src/host-internal.ts +28 -0
- package/src/host.ts +72 -0
- package/src/index.ts +2 -62
- package/src/statewire.ts +168 -61
- package/src/transports/fake-sse.ts +51 -0
- package/src/transports/fixtures.ts +60 -0
- package/src/transports/remote.ts +14 -51
- package/src/transports/sse.ts +87 -89
- package/src/transports/testing.ts +33 -63
- package/src/transports/ws.ts +78 -100
- package/src/types.ts +52 -131
- package/src/util/draftTraps.ts +32 -0
- package/src/util/macrotask.ts +25 -0
- package/src/util/methodProxy.ts +16 -0
- package/src/util/randomId.ts +4 -0
- package/src/{client/optimistic.ts → util/recordMutations.ts} +0 -0
- package/src/util/statePath.ts +61 -0
- package/src/wire/meta.ts +42 -47
- package/src/wire/ops.ts +107 -106
- package/LICENSE +0 -21
- package/dist/client/optimistic.d.ts.map +0 -1
- package/dist/client/optimistic.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/server.d.ts +0 -15
- package/dist/server.d.ts.map +0 -1
- package/dist/server.js +0 -3
- package/src/server.ts +0 -32
package/dist/host/draft.js
CHANGED
|
@@ -1,124 +1,211 @@
|
|
|
1
|
+
import { sharedDraftTraps } from "../util/draftTraps.js";
|
|
2
|
+
import { isUnsafeKey } from "../util/statePath.js";
|
|
3
|
+
import { sanitizeValue } from "../wire/ops.js";
|
|
1
4
|
//#region src/host/draft.ts
|
|
2
|
-
const
|
|
3
|
-
if (value instanceof Date) return new Date(value.getTime());
|
|
4
|
-
if (Array.isArray(value)) return value.map((item) => frozen(item) ?? null);
|
|
5
|
-
if (typeof value === "object" && value !== null) {
|
|
6
|
-
const out = {};
|
|
7
|
-
for (const key of Object.keys(value)) {
|
|
8
|
-
const item = frozen(value[key]);
|
|
9
|
-
if (item !== void 0) out[key] = item;
|
|
10
|
-
}
|
|
11
|
-
return out;
|
|
12
|
-
}
|
|
13
|
-
return typeof value === "function" ? void 0 : value;
|
|
14
|
-
};
|
|
15
|
-
const lookup = (root, path) => {
|
|
16
|
-
let node = root;
|
|
17
|
-
for (const key of path) node = node[key];
|
|
18
|
-
return node;
|
|
19
|
-
};
|
|
5
|
+
const NODE = Symbol("statewire.draftNode");
|
|
20
6
|
const isIndex = (key) => /^(0|[1-9]\d*)$/.test(key);
|
|
21
7
|
const invalid = (path, message) => /* @__PURE__ */ new Error(`statewire: ${message} at path: ${path.join(".")}`);
|
|
22
|
-
const createStateDraft = (host
|
|
23
|
-
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
});
|
|
33
|
-
return;
|
|
8
|
+
const createStateDraft = (host) => {
|
|
9
|
+
let gen = 0;
|
|
10
|
+
const dirty = [];
|
|
11
|
+
const latest = (node) => node.copy ?? node.base;
|
|
12
|
+
const resolve = (node) => {
|
|
13
|
+
if (node.gen === gen) return node;
|
|
14
|
+
node.gen = gen;
|
|
15
|
+
if (node.parent === null) {
|
|
16
|
+
if (node.copy === null) node.base = host.snapshot();
|
|
17
|
+
return node;
|
|
34
18
|
}
|
|
19
|
+
const value = latest(resolve(node.parent))[node.key];
|
|
20
|
+
if (value !== latest(node)) {
|
|
21
|
+
node.base = value;
|
|
22
|
+
node.copy = null;
|
|
23
|
+
}
|
|
24
|
+
return node;
|
|
25
|
+
};
|
|
26
|
+
const prepareForWrite = (node) => {
|
|
27
|
+
resolve(node);
|
|
28
|
+
if (node.copy !== null) return node.copy;
|
|
29
|
+
const base = node.base;
|
|
30
|
+
node.copy = Array.isArray(base) ? base.slice() : { ...base };
|
|
31
|
+
dirty.push(node);
|
|
32
|
+
if (node.parent !== null) prepareForWrite(node.parent)[node.key] = node.copy;
|
|
33
|
+
return node.copy;
|
|
34
|
+
};
|
|
35
|
+
const changedPathFor = (node, key) => Array.isArray(latest(node)) ? node.path : [...node.path, key];
|
|
36
|
+
const applyAppend = (node, rawKey, suffix) => {
|
|
37
|
+
const container = latest(resolve(node));
|
|
38
|
+
const key = Array.isArray(container) && typeof rawKey === "string" && isIndex(rawKey) ? Number(rawKey) : rawKey;
|
|
39
|
+
const existing = Object.hasOwn(container, key) ? container[key] : void 0;
|
|
40
|
+
if (typeof existing !== "string") throw invalid([...node.path, key], "cannot append to a non-string");
|
|
41
|
+
if (suffix === "") return;
|
|
42
|
+
prepareForWrite(node)[key] = existing + suffix;
|
|
35
43
|
host.record({
|
|
36
|
-
op: "
|
|
37
|
-
path: [
|
|
38
|
-
|
|
39
|
-
|
|
44
|
+
op: "add",
|
|
45
|
+
path: [
|
|
46
|
+
...node.path,
|
|
47
|
+
key,
|
|
48
|
+
existing.length
|
|
49
|
+
],
|
|
50
|
+
value: suffix
|
|
51
|
+
}, changedPathFor(node, key));
|
|
40
52
|
};
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
53
|
+
const createNode = (parent, key) => {
|
|
54
|
+
const seed = parent === null ? host.snapshot() : latest(parent)[key];
|
|
55
|
+
const node = {
|
|
56
|
+
parent,
|
|
57
|
+
key,
|
|
58
|
+
path: parent === null ? [] : [...parent.path, key],
|
|
59
|
+
gen,
|
|
60
|
+
base: seed,
|
|
61
|
+
copy: null,
|
|
62
|
+
children: /* @__PURE__ */ new Map(),
|
|
63
|
+
isArray: Array.isArray(seed),
|
|
64
|
+
proxy: null,
|
|
65
|
+
handle: null
|
|
66
|
+
};
|
|
67
|
+
node.handle = { append: (childKey, suffix) => applyAppend(node, childKey, suffix) };
|
|
68
|
+
const current = () => latest(resolve(node));
|
|
69
|
+
const childProxy = (prop, childKey, value) => {
|
|
70
|
+
let child = node.children.get(prop);
|
|
71
|
+
if (child === void 0 || child.isArray !== Array.isArray(value)) {
|
|
72
|
+
child = createNode(node, childKey);
|
|
73
|
+
node.children.set(prop, child);
|
|
74
|
+
}
|
|
75
|
+
return child.proxy;
|
|
76
|
+
};
|
|
77
|
+
const assign = (container, key, value) => {
|
|
78
|
+
const old = Object.hasOwn(container, key) ? container[key] : void 0;
|
|
79
|
+
if (old === value) return;
|
|
80
|
+
const isObjectContainer = !Array.isArray(container);
|
|
81
|
+
if (isObjectContainer && isUnsafeKey(key, node.path[node.path.length - 1])) throw invalid([...node.path, key], `cannot set unsafe key "${key}"`);
|
|
82
|
+
if (value === void 0) {
|
|
83
|
+
if (!isObjectContainer) throw invalid([...node.path, key], "cannot serialize undefined");
|
|
84
|
+
delete prepareForWrite(node)[key];
|
|
85
|
+
host.record({
|
|
86
|
+
op: "remove",
|
|
87
|
+
path: [...node.path, key]
|
|
88
|
+
}, [...node.path, key]);
|
|
89
|
+
gen++;
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
prepareForWrite(node)[key] = isObjectContainer ? sanitizeValue(value, key) : sanitizeValue(value);
|
|
93
|
+
host.record({
|
|
94
|
+
op: "replace",
|
|
95
|
+
path: [...node.path, key],
|
|
96
|
+
value
|
|
97
|
+
}, changedPathFor(node, key));
|
|
98
|
+
if (typeof old === "object" && old !== null || typeof value === "object" && value !== null) gen++;
|
|
99
|
+
};
|
|
100
|
+
node.proxy = new Proxy(seed, {
|
|
101
|
+
get: (_target, prop) => {
|
|
102
|
+
if (prop === NODE) return node.handle;
|
|
103
|
+
const container = current();
|
|
104
|
+
if (typeof prop === "symbol") return container[prop];
|
|
105
|
+
const c = container;
|
|
106
|
+
const key = Array.isArray(c) && isIndex(prop) ? Number(prop) : prop;
|
|
107
|
+
const value = c[key];
|
|
108
|
+
if (typeof value === "object" && value !== null) return childProxy(prop, key, value);
|
|
109
|
+
return value;
|
|
110
|
+
},
|
|
111
|
+
set: (_target, prop, value) => {
|
|
112
|
+
if (typeof prop === "symbol") return false;
|
|
113
|
+
const container = current();
|
|
114
|
+
if (Array.isArray(container)) {
|
|
115
|
+
if (prop === "length") {
|
|
116
|
+
const next = Number(value);
|
|
117
|
+
if (!Number.isInteger(next) || next < 0 || next > container.length) throw invalid(node.path, `cannot set list length to ${String(value)}`);
|
|
118
|
+
if (next === container.length) return true;
|
|
119
|
+
const copy = prepareForWrite(node);
|
|
120
|
+
for (let i = container.length - 1; i >= next; i--) {
|
|
121
|
+
copy.splice(i, 1);
|
|
122
|
+
host.record({
|
|
123
|
+
op: "remove",
|
|
124
|
+
path: [...node.path, i]
|
|
125
|
+
}, node.path);
|
|
126
|
+
}
|
|
127
|
+
gen++;
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
if (!isIndex(prop)) throw invalid(node.path, `cannot set key "${prop}" on a list`);
|
|
131
|
+
const index = Number(prop);
|
|
132
|
+
if (index > container.length) throw invalid(node.path, `list index ${index} out of range`);
|
|
133
|
+
if (index === container.length) {
|
|
134
|
+
if (value === void 0) throw invalid([...node.path, index], "cannot serialize undefined");
|
|
135
|
+
prepareForWrite(node)[index] = sanitizeValue(value);
|
|
136
|
+
host.record({
|
|
137
|
+
op: "add",
|
|
138
|
+
path: [...node.path, index],
|
|
139
|
+
value
|
|
140
|
+
}, node.path);
|
|
141
|
+
gen++;
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
assign(container, index, value);
|
|
62
145
|
return true;
|
|
63
146
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
147
|
+
assign(container, prop, value);
|
|
148
|
+
return true;
|
|
149
|
+
},
|
|
150
|
+
deleteProperty: (_target, prop) => {
|
|
151
|
+
if (typeof prop === "symbol") return false;
|
|
152
|
+
const container = current();
|
|
153
|
+
if (Array.isArray(container)) {
|
|
154
|
+
if (!isIndex(prop) || Number(prop) >= container.length) return true;
|
|
155
|
+
const index = Number(prop);
|
|
156
|
+
prepareForWrite(node).splice(index, 1);
|
|
68
157
|
host.record({
|
|
69
|
-
op: "
|
|
70
|
-
path: [...path, index]
|
|
71
|
-
|
|
72
|
-
|
|
158
|
+
op: "remove",
|
|
159
|
+
path: [...node.path, index]
|
|
160
|
+
}, node.path);
|
|
161
|
+
gen++;
|
|
73
162
|
return true;
|
|
74
163
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
assign(current, prop, value);
|
|
79
|
-
return true;
|
|
80
|
-
},
|
|
81
|
-
deleteProperty: (_target, prop) => {
|
|
82
|
-
if (typeof prop === "symbol") return false;
|
|
83
|
-
const current = node();
|
|
84
|
-
if (Array.isArray(current)) {
|
|
85
|
-
if (!isIndex(prop) || Number(prop) >= current.length) return true;
|
|
164
|
+
if (!Object.hasOwn(container, prop)) return true;
|
|
165
|
+
if (isUnsafeKey(prop, node.path[node.path.length - 1])) throw invalid([...node.path, prop], `cannot remove unsafe key "${prop}"`);
|
|
166
|
+
delete prepareForWrite(node)[prop];
|
|
86
167
|
host.record({
|
|
87
168
|
op: "remove",
|
|
88
|
-
path: [...path,
|
|
89
|
-
});
|
|
169
|
+
path: [...node.path, prop]
|
|
170
|
+
}, [...node.path, prop]);
|
|
171
|
+
gen++;
|
|
90
172
|
return true;
|
|
173
|
+
},
|
|
174
|
+
has: (_target, prop) => Reflect.has(current(), prop),
|
|
175
|
+
...sharedDraftTraps(() => current(), "on a state draft")
|
|
176
|
+
});
|
|
177
|
+
return node;
|
|
178
|
+
};
|
|
179
|
+
const root = createNode(null, "");
|
|
180
|
+
return {
|
|
181
|
+
proxy: root.proxy,
|
|
182
|
+
flush: () => {
|
|
183
|
+
if (dirty.length === 0) return host.snapshot();
|
|
184
|
+
for (const node of dirty) if (node.copy !== null) {
|
|
185
|
+
node.base = node.copy;
|
|
186
|
+
node.copy = null;
|
|
91
187
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
op: "remove",
|
|
95
|
-
path: [...path, prop]
|
|
96
|
-
});
|
|
97
|
-
return true;
|
|
98
|
-
},
|
|
99
|
-
defineProperty: () => {
|
|
100
|
-
throw new Error("statewire: defineProperty is not supported on a state draft");
|
|
101
|
-
},
|
|
102
|
-
setPrototypeOf: () => {
|
|
103
|
-
throw new Error("statewire: setPrototypeOf is not supported on a state draft");
|
|
188
|
+
dirty.length = 0;
|
|
189
|
+
return root.base;
|
|
104
190
|
},
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
},
|
|
108
|
-
has: (_target, prop) => Reflect.has(node(), prop),
|
|
109
|
-
ownKeys: () => Reflect.ownKeys(node()),
|
|
110
|
-
getOwnPropertyDescriptor: (_target, prop) => {
|
|
111
|
-
const current = node();
|
|
112
|
-
const desc = Reflect.getOwnPropertyDescriptor(current, prop);
|
|
113
|
-
if (!desc) return void 0;
|
|
114
|
-
return {
|
|
115
|
-
...desc,
|
|
116
|
-
configurable: !Array.isArray(current) || prop !== "length"
|
|
117
|
-
};
|
|
191
|
+
invalidate: () => {
|
|
192
|
+
gen++;
|
|
118
193
|
}
|
|
119
|
-
}
|
|
194
|
+
};
|
|
195
|
+
};
|
|
196
|
+
/**
|
|
197
|
+
* Append `suffix` to the string at `parent[key]` on a statewire state draft,
|
|
198
|
+
* recording a single end-offset text-insert `add` op instead of a full
|
|
199
|
+
* `replace` — use it on token-streaming hot paths. Throws when the current
|
|
200
|
+
* value is not a string.
|
|
201
|
+
*/
|
|
202
|
+
const append = (parent, key, suffix) => {
|
|
203
|
+
const handle = parent[NODE];
|
|
204
|
+
if (handle === void 0) throw new Error("statewire: append expects a state draft object");
|
|
205
|
+
if (typeof suffix !== "string") throw new Error("statewire: append suffix must be a string");
|
|
206
|
+
handle.append(key, suffix);
|
|
120
207
|
};
|
|
121
208
|
//#endregion
|
|
122
|
-
export {
|
|
209
|
+
export { append, createStateDraft };
|
|
123
210
|
|
|
124
211
|
//# sourceMappingURL=draft.js.map
|
package/dist/host/draft.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"draft.js","names":["StatewireOp","StatewirePath","ServerOp","Exclude","op","DraftHost","root","record","frozen","value","Date","getTime","Array","isArray","map","item","out","Record","key","Object","keys","undefined","lookup","path","node","isIndex","test","invalid","message","Error","join","createStateDraft","host","T","assign","container","old","hasOwn","startsWith","slice","length","Proxy","get","_target","prop","current","Number","set","rawValue","next","isInteger","String","i","index","deleteProperty","defineProperty","setPrototypeOf","preventExtensions","has","Reflect","ownKeys","getOwnPropertyDescriptor","desc","configurable"],"sources":["../../src/host/draft.ts"],"sourcesContent":["import type { StatewireOp, StatewirePath } from \"../wire/ops\";\n\nexport type ServerOp = Exclude<StatewireOp, { op: \"event\" }>;\n\nexport type DraftHost = {\n root: () => unknown;\n record: (op: ServerOp) => void;\n};\n\nexport const frozen = (value: unknown): unknown => {\n if (value instanceof Date) return new Date(value.getTime());\n if (Array.isArray(value)) {\n return value.map((item) => frozen(item) ?? null);\n }\n if (typeof value === \"object\" && value !== null) {\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(value)) {\n const item = frozen((value as Record<string, unknown>)[key]);\n if (item !== undefined) out[key] = item;\n }\n return out;\n }\n return typeof value === \"function\" ? undefined : value;\n};\n\nconst lookup = (root: unknown, path: StatewirePath): unknown => {\n let node = root;\n for (const key of path) {\n node = (node as Record<string | number, unknown>)[key];\n }\n return node;\n};\n\nconst isIndex = (key: string) => /^(0|[1-9]\\d*)$/.test(key);\n\nconst invalid = (path: StatewirePath, message: string) =>\n new Error(`statewire: ${message} at path: ${path.join(\".\")}`);\n\nexport const createStateDraft = <T>(\n host: DraftHost,\n path: StatewirePath = [],\n): T => {\n const node = () => lookup(host.root(), path);\n\n const assign = (\n container: Record<string | number, unknown>,\n key: string | number,\n value: unknown,\n ) => {\n const old = Object.hasOwn(container, key) ? container[key] : undefined;\n if (old === value) return;\n if (\n typeof old === \"string\" &&\n typeof value === \"string\" &&\n value.startsWith(old)\n ) {\n host.record({\n op: \"append\",\n path: [...path, key],\n value: value.slice(old.length),\n });\n return;\n }\n host.record({ op: \"replace\", path: [...path, key], value });\n };\n\n return new Proxy(node() as object, {\n get: (_target, prop) => {\n const current = node() as Record<string | number, unknown>;\n if (typeof prop === \"symbol\") {\n return (current as unknown as Record<symbol, unknown>)[prop];\n }\n const key = Array.isArray(current) && isIndex(prop) ? Number(prop) : prop;\n const value = current[key];\n if (typeof value === \"object\" && value !== null) {\n return createStateDraft(host, [...path, key]);\n }\n return value;\n },\n set: (_target, prop, rawValue) => {\n if (typeof prop === \"symbol\") return false;\n const current = node();\n const value = frozen(rawValue);\n if (Array.isArray(current)) {\n if (prop === \"length\") {\n const next = Number(value);\n if (!Number.isInteger(next) || next < 0 || next > current.length) {\n throw invalid(path, `cannot set list length to ${String(value)}`);\n }\n for (let i = current.length - 1; i >= next; i--) {\n host.record({ op: \"remove\", path: [...path, i] });\n }\n return true;\n }\n if (!isIndex(prop)) {\n throw invalid(path, `cannot set key \"${prop}\" on a list`);\n }\n const index = Number(prop);\n if (index > current.length) {\n throw invalid(path, `list index ${index} out of range`);\n }\n if (index === current.length) {\n host.record({ op: \"add\", path: [...path, index], value });\n return true;\n }\n assign(\n current as unknown as Record<string | number, unknown>,\n index,\n value,\n );\n return true;\n }\n assign(current as Record<string, unknown>, prop, value);\n return true;\n },\n deleteProperty: (_target, prop) => {\n if (typeof prop === \"symbol\") return false;\n const current = node();\n if (Array.isArray(current)) {\n if (!isIndex(prop) || Number(prop) >= current.length) return true;\n host.record({ op: \"remove\", path: [...path, Number(prop)] });\n return true;\n }\n if (!Object.hasOwn(current as object, prop)) return true;\n host.record({ op: \"remove\", path: [...path, prop] });\n return true;\n },\n defineProperty: () => {\n throw new Error(\n \"statewire: defineProperty is not supported on a state draft\",\n );\n },\n setPrototypeOf: () => {\n throw new Error(\n \"statewire: setPrototypeOf is not supported on a state draft\",\n );\n },\n preventExtensions: () => {\n throw new Error(\n \"statewire: preventExtensions is not supported on a state draft\",\n );\n },\n has: (_target, prop) => Reflect.has(node() as object, prop),\n ownKeys: () => Reflect.ownKeys(node() as object),\n getOwnPropertyDescriptor: (_target, prop) => {\n const current = node() as object;\n const desc = Reflect.getOwnPropertyDescriptor(current, prop);\n if (!desc) return undefined;\n return {\n ...desc,\n configurable: !Array.isArray(current) || prop !== \"length\",\n };\n },\n }) as T;\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"draft.js","names":["Statewire","StatewirePath","sanitizeValue","isUnsafeKey","sharedDraftTraps","ServerOp","Exclude","Op","op","DraftHost","snapshot","record","changedPath","StateDraft","proxy","T","flush","invalidate","NODE","Symbol","NodeHandle","append","key","suffix","isIndex","test","invalid","path","message","Error","join","Container","Record","Node","parent","gen","base","copy","children","Map","isArray","handle","createStateDraft","host","dirty","latest","node","resolve","value","prepareForWrite","Array","slice","push","changedPathFor","applyAppend","rawKey","container","Number","existing","Object","hasOwn","undefined","length","createNode","seed","childKey","current","childProxy","prop","child","get","set","assign","old","isObjectContainer","Proxy","_target","c","next","isInteger","String","i","splice","index","deleteProperty","has","Reflect","root","TKey"],"sources":["../../src/host/draft.ts"],"sourcesContent":["import type { Statewire } from \"../types\";\nimport type { StatewirePath } from \"../wire/ops\";\nimport { sanitizeValue } from \"../wire/ops\";\nimport { isUnsafeKey } from \"../util/statePath\";\nimport { sharedDraftTraps } from \"../util/draftTraps\";\n\nexport type ServerOp = Exclude<Statewire.Op, { op: \"event\" }>;\n\nexport type DraftHost = {\n snapshot: () => unknown;\n record: (op: ServerOp, changedPath: StatewirePath) => void;\n};\n\nexport type StateDraft<T> = {\n proxy: T;\n /** Seal pending writes and return the committed state. */\n flush: () => unknown;\n /** Committed state was replaced externally; cached nodes re-resolve. */\n invalidate: () => void;\n};\n\nconst NODE = Symbol(\"statewire.draftNode\");\n\ntype NodeHandle = {\n append: (key: string | number, suffix: string) => void;\n};\n\nconst isIndex = (key: string) => /^(0|[1-9]\\d*)$/.test(key);\n\nconst invalid = (path: StatewirePath, message: string) =>\n new Error(`statewire: ${message} at path: ${path.join(\".\")}`);\n\ntype Container = Record<string | number, unknown>;\n\ntype Node = {\n parent: Node | null;\n key: string | number;\n path: StatewirePath;\n gen: number;\n base: unknown;\n copy: Container | null;\n children: Map<string, Node>;\n isArray: boolean;\n proxy: object;\n handle: NodeHandle;\n};\n\nexport const createStateDraft = <T>(host: DraftHost): StateDraft<T> => {\n let gen = 0;\n const dirty: Node[] = [];\n\n const latest = (node: Node): unknown => node.copy ?? node.base;\n\n const resolve = (node: Node): Node => {\n if (node.gen === gen) return node;\n node.gen = gen;\n if (node.parent === null) {\n if (node.copy === null) node.base = host.snapshot();\n return node;\n }\n const value = (latest(resolve(node.parent)) as Container)[node.key];\n if (value !== latest(node)) {\n node.base = value;\n node.copy = null;\n }\n return node;\n };\n\n const prepareForWrite = (node: Node): Container => {\n resolve(node);\n if (node.copy !== null) return node.copy;\n const base = node.base;\n node.copy = Array.isArray(base)\n ? (base.slice() as unknown as Container)\n : { ...(base as Container) };\n dirty.push(node);\n if (node.parent !== null) {\n prepareForWrite(node.parent)[node.key] = node.copy;\n }\n return node.copy;\n };\n\n const changedPathFor = (node: Node, key: string | number): StatewirePath =>\n Array.isArray(latest(node)) ? node.path : [...node.path, key];\n\n const applyAppend = (node: Node, rawKey: string | number, suffix: string) => {\n const container = latest(resolve(node)) as Container;\n const key =\n Array.isArray(container) && typeof rawKey === \"string\" && isIndex(rawKey)\n ? Number(rawKey)\n : rawKey;\n const existing = Object.hasOwn(container as object, key)\n ? container[key]\n : undefined;\n if (typeof existing !== \"string\") {\n throw invalid([...node.path, key], \"cannot append to a non-string\");\n }\n if (suffix === \"\") return;\n prepareForWrite(node)[key] = existing + suffix;\n host.record(\n { op: \"add\", path: [...node.path, key, existing.length], value: suffix },\n changedPathFor(node, key),\n );\n };\n\n const createNode = (parent: Node | null, key: string | number): Node => {\n const seed =\n parent === null ? host.snapshot() : (latest(parent) as Container)[key];\n const node: Node = {\n parent,\n key,\n path: parent === null ? [] : [...parent.path, key],\n gen,\n base: seed,\n copy: null,\n children: new Map(),\n isArray: Array.isArray(seed),\n proxy: null!,\n handle: null!,\n };\n node.handle = {\n append: (childKey, suffix) => applyAppend(node, childKey, suffix),\n };\n\n const current = () => latest(resolve(node));\n\n const childProxy = (\n prop: string,\n childKey: string | number,\n value: object,\n ) => {\n let child = node.children.get(prop);\n if (child === undefined || child.isArray !== Array.isArray(value)) {\n child = createNode(node, childKey);\n node.children.set(prop, child);\n }\n return child.proxy;\n };\n\n const assign = (\n container: Container,\n key: string | number,\n value: unknown,\n ) => {\n const old = Object.hasOwn(container as object, key)\n ? container[key]\n : undefined;\n if (old === value) return;\n const isObjectContainer = !Array.isArray(container);\n if (\n isObjectContainer &&\n isUnsafeKey(key, node.path[node.path.length - 1])\n ) {\n throw invalid([...node.path, key], `cannot set unsafe key \"${key}\"`);\n }\n if (value === undefined) {\n if (!isObjectContainer) {\n throw invalid([...node.path, key], \"cannot serialize undefined\");\n }\n delete prepareForWrite(node)[key];\n host.record({ op: \"remove\", path: [...node.path, key] }, [\n ...node.path,\n key,\n ]);\n gen++;\n return;\n }\n prepareForWrite(node)[key] = isObjectContainer\n ? sanitizeValue(value, key as string)\n : sanitizeValue(value);\n host.record(\n { op: \"replace\", path: [...node.path, key], value },\n changedPathFor(node, key),\n );\n if (\n (typeof old === \"object\" && old !== null) ||\n (typeof value === \"object\" && value !== null)\n ) {\n gen++;\n }\n };\n\n node.proxy = new Proxy(seed as object, {\n get: (_target, prop) => {\n if (prop === NODE) return node.handle;\n const container = current();\n if (typeof prop === \"symbol\") {\n return (container as Record<symbol, unknown>)[prop];\n }\n const c = container as Container;\n const key = Array.isArray(c) && isIndex(prop) ? Number(prop) : prop;\n const value = c[key];\n if (typeof value === \"object\" && value !== null) {\n return childProxy(prop, key, value);\n }\n return value;\n },\n set: (_target, prop, value) => {\n if (typeof prop === \"symbol\") return false;\n const container = current();\n if (Array.isArray(container)) {\n if (prop === \"length\") {\n const next = Number(value);\n if (\n !Number.isInteger(next) ||\n next < 0 ||\n next > container.length\n ) {\n throw invalid(\n node.path,\n `cannot set list length to ${String(value)}`,\n );\n }\n if (next === container.length) return true;\n const copy = prepareForWrite(node) as unknown as unknown[];\n for (let i = container.length - 1; i >= next; i--) {\n copy.splice(i, 1);\n host.record({ op: \"remove\", path: [...node.path, i] }, node.path);\n }\n gen++;\n return true;\n }\n if (!isIndex(prop)) {\n throw invalid(node.path, `cannot set key \"${prop}\" on a list`);\n }\n const index = Number(prop);\n if (index > container.length) {\n throw invalid(node.path, `list index ${index} out of range`);\n }\n if (index === container.length) {\n if (value === undefined) {\n throw invalid(\n [...node.path, index],\n \"cannot serialize undefined\",\n );\n }\n prepareForWrite(node)[index] = sanitizeValue(value);\n host.record(\n { op: \"add\", path: [...node.path, index], value },\n node.path,\n );\n gen++;\n return true;\n }\n assign(container as unknown as Container, index, value);\n return true;\n }\n assign(container as Container, prop, value);\n return true;\n },\n deleteProperty: (_target, prop) => {\n if (typeof prop === \"symbol\") return false;\n const container = current();\n if (Array.isArray(container)) {\n if (!isIndex(prop) || Number(prop) >= container.length) return true;\n const index = Number(prop);\n (prepareForWrite(node) as unknown as unknown[]).splice(index, 1);\n host.record({ op: \"remove\", path: [...node.path, index] }, node.path);\n gen++;\n return true;\n }\n if (!Object.hasOwn(container as object, prop)) return true;\n if (isUnsafeKey(prop, node.path[node.path.length - 1])) {\n throw invalid(\n [...node.path, prop],\n `cannot remove unsafe key \"${prop}\"`,\n );\n }\n delete prepareForWrite(node)[prop];\n host.record({ op: \"remove\", path: [...node.path, prop] }, [\n ...node.path,\n prop,\n ]);\n gen++;\n return true;\n },\n has: (_target, prop) => Reflect.has(current() as object, prop),\n ...sharedDraftTraps(() => current() as object, \"on a state draft\"),\n });\n return node;\n };\n\n const root = createNode(null, \"\");\n\n return {\n proxy: root.proxy as T,\n flush: () => {\n if (dirty.length === 0) return host.snapshot();\n for (const node of dirty) {\n if (node.copy !== null) {\n node.base = node.copy;\n node.copy = null;\n }\n }\n dirty.length = 0;\n return root.base;\n },\n invalidate: () => {\n gen++;\n },\n };\n};\n\n/**\n * Append `suffix` to the string at `parent[key]` on a statewire state draft,\n * recording a single end-offset text-insert `add` op instead of a full\n * `replace` — use it on token-streaming hot paths. Throws when the current\n * value is not a string.\n */\nexport const append = <TKey extends string | number>(\n parent: { readonly [P in TKey]: string },\n key: TKey,\n suffix: string,\n): void => {\n const handle = (parent as { [NODE]?: NodeHandle })[NODE];\n if (handle === undefined) {\n throw new Error(\"statewire: append expects a state draft object\");\n }\n if (typeof suffix !== \"string\") {\n throw new Error(\"statewire: append suffix must be a string\");\n }\n handle.append(key, suffix);\n};\n"],"mappings":"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"}
|
package/dist/host/host.d.ts
CHANGED
|
@@ -1,32 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { ServerOp } from "./draft.js";
|
|
1
|
+
import { StatewireHost } from "../host.js";
|
|
2
|
+
import { Statewire } from "../types.js";
|
|
4
3
|
//#region src/host/host.d.ts
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
};
|
|
9
|
-
type StateHost = StatewireStateSource & {
|
|
10
|
-
/** Synchronous, per locally-recorded op; the host owns batching. */
|
|
11
|
-
setOnOp: (listener: ((op: ServerOp) => void) | null) => void;
|
|
12
|
-
};
|
|
13
|
-
declare const getStateHost: (state: object) => StateHost | undefined;
|
|
14
|
-
declare const useStatewireState: <TState>(init: () => TState) => readonly [TState, StatewireStateCtl];
|
|
15
|
-
declare const executionResult: unique symbol;
|
|
16
|
-
/**
|
|
17
|
-
* Handle a command handler returns to keep the command open past its own
|
|
18
|
-
* return: `ack()` releases the client's optimistic overlay / ack watermark,
|
|
19
|
-
* `resolve`/`reject` settle the command (and imply ack). `ack` is idempotent
|
|
20
|
-
* and a no-op after settle.
|
|
21
|
-
*/
|
|
22
|
-
type CommandExecution<T = void> = {
|
|
23
|
-
readonly [executionResult]: (value: T) => void;
|
|
24
|
-
};
|
|
25
|
-
type CommandExecutionResolvers<T = void> = {
|
|
26
|
-
ack: () => void;
|
|
27
|
-
resolve: (value: T) => void;
|
|
28
|
-
reject: (error: unknown) => void;
|
|
29
|
-
};
|
|
4
|
+
/** Host-side accessor for a `useStatewireState` state object's source (`snapshot()` reads the committed state). */
|
|
5
|
+
declare const getStateHost: (state: object) => StatewireHost.StateHost | undefined;
|
|
6
|
+
declare const useStatewireState: <TState>(init: () => TState) => readonly [TState, StatewireHost.StateCtl<TState>];
|
|
30
7
|
/**
|
|
31
8
|
* `CommandExecution(executor)` runs the (possibly async) `executor({ ack })`
|
|
32
9
|
* immediately: its return value resolves the execution, a throw/rejection
|
|
@@ -36,8 +13,8 @@ type CommandExecutionResolvers<T = void> = {
|
|
|
36
13
|
*/
|
|
37
14
|
declare const CommandExecution: (<T = void>(executor: (resolvers: {
|
|
38
15
|
ack: () => void;
|
|
39
|
-
}) => T | void | PromiseLike<T | void>) =>
|
|
40
|
-
withResolvers: <T = void>() => [
|
|
16
|
+
}) => T | void | PromiseLike<T | void>) => StatewireHost.Execution<T>) & {
|
|
17
|
+
withResolvers: <T = void>() => [StatewireHost.Execution<T>, StatewireHost.ExecutionResolvers<T>];
|
|
41
18
|
};
|
|
42
19
|
type CommandsInvocation = {
|
|
43
20
|
readonly method: string;
|
|
@@ -61,19 +38,26 @@ type CommandsListener = {
|
|
|
61
38
|
onCovered?: (inv: CommandsInvocation) => void;
|
|
62
39
|
onSettle: (inv: CommandsInvocation, outcome: CommandsOutcome) => void;
|
|
63
40
|
};
|
|
41
|
+
type CommandsSubmission = {
|
|
42
|
+
method: string;
|
|
43
|
+
params: readonly unknown[];
|
|
44
|
+
clientId?: string;
|
|
45
|
+
caller?: StatewireHost.Caller;
|
|
46
|
+
onAck?: () => void;
|
|
47
|
+
onSettle?: (outcome: CommandsOutcome) => void;
|
|
48
|
+
};
|
|
64
49
|
type CommandsHost = {
|
|
65
50
|
setListener: (listener: CommandsListener | null) => void;
|
|
51
|
+
submit?: (call: CommandsSubmission) => Promise<unknown>;
|
|
66
52
|
};
|
|
67
53
|
declare const getCommandsHost: (commands: object) => CommandsHost | undefined;
|
|
68
54
|
declare const bindCommandsHost: (commands: object, host: CommandsHost) => void;
|
|
69
|
-
|
|
70
|
-
type StatewireHandledCommands<THandlers extends StatewireCommands> = { [K in keyof THandlers]: (...args: Parameters<THandlers[K]>) => UnwrapExecution<Awaited<ReturnType<THandlers[K]>>>; };
|
|
71
|
-
declare const useStatewireCommands: <THandlers extends StatewireCommands>(commands: THandlers | undefined | null | false) => StatewireCommandsProxy<StatewireHandledCommands<THandlers>>;
|
|
55
|
+
declare const useStatewireCommands: <THandlers extends Statewire.Commands>(commands: (THandlers & ThisType<StatewireHost.Caller>) | undefined | null | false) => Statewire.CommandsProxy<StatewireHost.HandledCommands<THandlers>>;
|
|
72
56
|
type EmitHost = {
|
|
73
57
|
setSink: (sink: ((event: unknown) => void) | null) => void;
|
|
74
58
|
};
|
|
75
|
-
declare const getEmitHost: (emit: object) => EmitHost | undefined;
|
|
59
|
+
declare const getEmitHost: (emit: object | undefined) => EmitHost | undefined;
|
|
76
60
|
declare const useStatewireEmit: <TEvent>() => ((event: TEvent) => void);
|
|
77
61
|
//#endregion
|
|
78
|
-
export { CommandExecution,
|
|
62
|
+
export { CommandExecution, CommandsHost, CommandsInvocation, CommandsListener, CommandsOutcome, CommandsSubmission, EmitHost, bindCommandsHost, getCommandsHost, getEmitHost, getStateHost, useStatewireCommands, useStatewireEmit, useStatewireState };
|
|
79
63
|
//# sourceMappingURL=host.d.ts.map
|
package/dist/host/host.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host.d.ts","names":[],"sources":["../../src/host/host.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"host.d.ts","names":[],"sources":["../../src/host/host.ts"],"mappings":";;;;cAmBa,eACX,kBACC,cAAc;cAaJ,oBAAqB,QAChC,YAAY,qBACD,QAAQ,cAAc,SAAS;;;;;;;;cA4F/B,oBACV,UAAC,WACW;EACT;MACI,WAAW,YAAY,cAC5B,cAAc,UAAU;EA2BT,gBAAA,gBACd,cAAc,UAAU,IACxB,cAAc,mBAAmB;;KAkB3B;WACD;WACA;WACA;WACA;WACA;;KAGC;EACN;EAAkB;;EAClB;EAAkB;;KAEZ;EACV,UAAU,KAAK;EACf,YAAY,KAAK;EACjB,QAAQ,KAAK;;EAEb,aAAa,KAAK;EAClB,WAAW,KAAK,oBAAoB,SAAS;;KAGnC;EACV;EACA;EACA;EACA,SAAS,cAAc;EACvB;EACA,YAAY,SAAS;;KAGX;EACV,cAAc,UAAU;EACxB,UAAU,MAAM,uBAAuB;;cAK5B,kBAAmB,qBAAmB;cAGtC,mBAAoB,kBAAkB,MAAM;cA2J5C,uBAAwB,kBAAkB,UAAU,UAC/D,WACK,YAAY,SAAS,cAAc,wCAIvC,UAAU,cAAc,cAAc,gBAAgB;KAqF7C;EACV,UAAU,QAAQ;;cAKP,cAAe,6BAA2B;cAG1C,mBAAoB,eAAa,OAAO"}
|