@automerge/automerge-repo-react-hooks 2.5.3 → 2.6.0-subduction.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/dist/index.js +228 -1039
- package/dist/index.js.map +1 -1
- package/dist/usePresence.d.ts +4 -26
- package/dist/usePresence.d.ts.map +1 -1
- package/package.json +12 -12
- package/src/usePresence.ts +10 -46
package/dist/index.js
CHANGED
|
@@ -2146,969 +2146,103 @@ const useLocalAwareness = ({
|
|
|
2146
2146
|
return [localState, setState];
|
|
2147
2147
|
};
|
|
2148
2148
|
|
|
2149
|
-
//
|
|
2150
|
-
const STATE = Symbol.for("_am_meta"); // symbol used to hide application metadata on automerge objects
|
|
2151
|
-
const TRACE = Symbol.for("_am_trace"); // used for debugging
|
|
2152
|
-
const OBJECT_ID = Symbol.for("_am_objectId"); // symbol used to hide the object id on automerge objects
|
|
2153
|
-
const IS_PROXY = Symbol.for("_am_isProxy"); // symbol used to test if the document is a proxy object
|
|
2154
|
-
const CLEAR_CACHE = Symbol.for("_am_clearCache"); // symbol used to tell a proxy object to clear its cache
|
|
2155
|
-
const UINT = Symbol.for("_am_uint");
|
|
2156
|
-
const INT = Symbol.for("_am_int");
|
|
2157
|
-
const F64 = Symbol.for("_am_f64");
|
|
2158
|
-
const COUNTER = Symbol.for("_am_counter");
|
|
2159
|
-
const IMMUTABLE_STRING = Symbol.for("_am_immutableString");
|
|
2149
|
+
BigInt("9223372036854775807"); // 2n ** 63n - 1n;
|
|
2160
2150
|
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2151
|
+
let wasm;
|
|
2152
|
+
|
|
2153
|
+
let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
|
|
2154
|
+
|
|
2155
|
+
cachedTextDecoder.decode();
|
|
2156
|
+
|
|
2157
|
+
const cachedTextEncoder = new TextEncoder();
|
|
2158
|
+
|
|
2159
|
+
if (!('encodeInto' in cachedTextEncoder)) {
|
|
2160
|
+
cachedTextEncoder.encodeInto = function (arg, view) {
|
|
2161
|
+
const buf = cachedTextEncoder.encode(arg);
|
|
2162
|
+
view.set(buf);
|
|
2163
|
+
return {
|
|
2164
|
+
read: arg.length,
|
|
2165
|
+
written: buf.length
|
|
2166
|
+
};
|
|
2167
|
+
};
|
|
2168
|
+
}
|
|
2169
|
+
|
|
2170
|
+
function takeFromExternrefTable0(idx) {
|
|
2171
|
+
const value = wasm.__wbindgen_export_4.get(idx);
|
|
2172
|
+
wasm.__externref_table_dealloc(idx);
|
|
2173
|
+
return value;
|
|
2174
|
+
}
|
|
2175
|
+
|
|
2176
|
+
(typeof FinalizationRegistry === 'undefined')
|
|
2177
|
+
? { }
|
|
2178
|
+
: new FinalizationRegistry(ptr => wasm.__wbg_automerge_free(ptr >>> 0, 1));
|
|
2179
|
+
|
|
2180
|
+
const SyncStateFinalization = (typeof FinalizationRegistry === 'undefined')
|
|
2181
|
+
? { register: () => {}, unregister: () => {} }
|
|
2182
|
+
: new FinalizationRegistry(ptr => wasm.__wbg_syncstate_free(ptr >>> 0, 1));
|
|
2183
|
+
|
|
2184
|
+
class SyncState {
|
|
2185
|
+
|
|
2186
|
+
static __wrap(ptr) {
|
|
2187
|
+
ptr = ptr >>> 0;
|
|
2188
|
+
const obj = Object.create(SyncState.prototype);
|
|
2189
|
+
obj.__wbg_ptr = ptr;
|
|
2190
|
+
SyncStateFinalization.register(obj, obj.__wbg_ptr, obj);
|
|
2191
|
+
return obj;
|
|
2170
2192
|
}
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/valueOf
|
|
2178
|
-
*/
|
|
2179
|
-
valueOf() {
|
|
2180
|
-
return this.value;
|
|
2193
|
+
|
|
2194
|
+
__destroy_into_raw() {
|
|
2195
|
+
const ptr = this.__wbg_ptr;
|
|
2196
|
+
this.__wbg_ptr = 0;
|
|
2197
|
+
SyncStateFinalization.unregister(this);
|
|
2198
|
+
return ptr;
|
|
2181
2199
|
}
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
*/
|
|
2187
|
-
toString() {
|
|
2188
|
-
return this.valueOf().toString();
|
|
2200
|
+
|
|
2201
|
+
free() {
|
|
2202
|
+
const ptr = this.__destroy_into_raw();
|
|
2203
|
+
wasm.__wbg_syncstate_free(ptr, 0);
|
|
2189
2204
|
}
|
|
2190
2205
|
/**
|
|
2191
|
-
*
|
|
2192
|
-
* document represents the counter simply as an integer.
|
|
2206
|
+
* @returns {Heads}
|
|
2193
2207
|
*/
|
|
2194
|
-
|
|
2195
|
-
|
|
2208
|
+
get sharedHeads() {
|
|
2209
|
+
const ret = wasm.syncstate_sharedHeads(this.__wbg_ptr);
|
|
2210
|
+
return ret;
|
|
2196
2211
|
}
|
|
2197
2212
|
/**
|
|
2198
|
-
*
|
|
2199
|
-
* increases the value of the counter by 1.
|
|
2200
|
-
*
|
|
2201
|
-
* Will throw an error if used outside of a change callback.
|
|
2213
|
+
* @returns {Heads}
|
|
2202
2214
|
*/
|
|
2203
|
-
|
|
2204
|
-
|
|
2215
|
+
get lastSentHeads() {
|
|
2216
|
+
const ret = wasm.syncstate_lastSentHeads(this.__wbg_ptr);
|
|
2217
|
+
return ret;
|
|
2205
2218
|
}
|
|
2206
2219
|
/**
|
|
2207
|
-
*
|
|
2208
|
-
* decreases the value of the counter by 1.
|
|
2209
|
-
*
|
|
2210
|
-
* Will throw an error if used outside of a change callback.
|
|
2220
|
+
* @param {Heads} heads
|
|
2211
2221
|
*/
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
* An instance of this class is used when a counter is accessed within a change
|
|
2218
|
-
* callback.
|
|
2219
|
-
*/
|
|
2220
|
-
class WriteableCounter extends Counter {
|
|
2221
|
-
constructor(value, context, path, objectId, key) {
|
|
2222
|
-
super(value);
|
|
2223
|
-
this.context = context;
|
|
2224
|
-
this.path = path;
|
|
2225
|
-
this.objectId = objectId;
|
|
2226
|
-
this.key = key;
|
|
2227
|
-
}
|
|
2228
|
-
/**
|
|
2229
|
-
* Increases the value of the counter by `delta`. If `delta` is not given,
|
|
2230
|
-
* increases the value of the counter by 1.
|
|
2231
|
-
*/
|
|
2232
|
-
increment(delta) {
|
|
2233
|
-
delta = typeof delta === "number" ? delta : 1;
|
|
2234
|
-
this.context.increment(this.objectId, this.key, delta);
|
|
2235
|
-
this.value += delta;
|
|
2236
|
-
return this.value;
|
|
2222
|
+
set lastSentHeads(heads) {
|
|
2223
|
+
const ret = wasm.syncstate_set_lastSentHeads(this.__wbg_ptr, heads);
|
|
2224
|
+
if (ret[1]) {
|
|
2225
|
+
throw takeFromExternrefTable0(ret[0]);
|
|
2226
|
+
}
|
|
2237
2227
|
}
|
|
2238
2228
|
/**
|
|
2239
|
-
*
|
|
2240
|
-
* decreases the value of the counter by 1.
|
|
2229
|
+
* @param {Heads} hashes
|
|
2241
2230
|
*/
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
* Returns an instance of `WriteableCounter` for use in a change callback.
|
|
2248
|
-
* `context` is the proxy context that keeps track of the mutations.
|
|
2249
|
-
* `objectId` is the ID of the object containing the counter, and `key` is
|
|
2250
|
-
* the property name (key in map, or index in list) where the counter is
|
|
2251
|
-
* located.
|
|
2252
|
-
*/
|
|
2253
|
-
function getWriteableCounter(value, context, path, objectId, key) {
|
|
2254
|
-
return new WriteableCounter(value, context, path, objectId, key);
|
|
2255
|
-
}
|
|
2256
|
-
//module.exports = { Counter, getWriteableCounter }
|
|
2257
|
-
|
|
2258
|
-
var _a;
|
|
2259
|
-
class ImmutableString {
|
|
2260
|
-
constructor(val) {
|
|
2261
|
-
// Used to detect whether a value is a ImmutableString object rather than using an instanceof check
|
|
2262
|
-
this[_a] = true;
|
|
2263
|
-
this.val = val;
|
|
2231
|
+
set sentHashes(hashes) {
|
|
2232
|
+
const ret = wasm.syncstate_set_sentHashes(this.__wbg_ptr, hashes);
|
|
2233
|
+
if (ret[1]) {
|
|
2234
|
+
throw takeFromExternrefTable0(ret[0]);
|
|
2235
|
+
}
|
|
2264
2236
|
}
|
|
2265
2237
|
/**
|
|
2266
|
-
*
|
|
2238
|
+
* @returns {SyncState}
|
|
2267
2239
|
*/
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
toJSON() {
|
|
2272
|
-
return this.val;
|
|
2240
|
+
clone() {
|
|
2241
|
+
const ret = wasm.syncstate_clone(this.__wbg_ptr);
|
|
2242
|
+
return SyncState.__wrap(ret);
|
|
2273
2243
|
}
|
|
2274
2244
|
}
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
function parseListIndex(key) {
|
|
2278
|
-
if (typeof key === "string" && /^[0-9]+$/.test(key))
|
|
2279
|
-
key = parseInt(key, 10);
|
|
2280
|
-
if (typeof key !== "number") {
|
|
2281
|
-
return key;
|
|
2282
|
-
}
|
|
2283
|
-
if (key < 0 || isNaN(key) || key === Infinity || key === -Infinity) {
|
|
2284
|
-
throw new RangeError("A list index must be positive, but you passed " + key);
|
|
2285
|
-
}
|
|
2286
|
-
return key;
|
|
2287
|
-
}
|
|
2288
|
-
function valueAt(target, prop) {
|
|
2289
|
-
const { context, objectId, path } = target;
|
|
2290
|
-
const value = context.getWithType(objectId, prop);
|
|
2291
|
-
if (value === null) {
|
|
2292
|
-
return;
|
|
2293
|
-
}
|
|
2294
|
-
const datatype = value[0];
|
|
2295
|
-
const val = value[1];
|
|
2296
|
-
switch (datatype) {
|
|
2297
|
-
case undefined:
|
|
2298
|
-
return;
|
|
2299
|
-
case "map":
|
|
2300
|
-
return mapProxy(context, val, [...path, prop]);
|
|
2301
|
-
case "list":
|
|
2302
|
-
return listProxy(context, val, [...path, prop]);
|
|
2303
|
-
case "text":
|
|
2304
|
-
return context.text(val);
|
|
2305
|
-
case "str":
|
|
2306
|
-
return new ImmutableString(val);
|
|
2307
|
-
case "uint":
|
|
2308
|
-
return val;
|
|
2309
|
-
case "int":
|
|
2310
|
-
return val;
|
|
2311
|
-
case "f64":
|
|
2312
|
-
return val;
|
|
2313
|
-
case "boolean":
|
|
2314
|
-
return val;
|
|
2315
|
-
case "null":
|
|
2316
|
-
return null;
|
|
2317
|
-
case "bytes":
|
|
2318
|
-
return val;
|
|
2319
|
-
case "timestamp":
|
|
2320
|
-
return val;
|
|
2321
|
-
case "counter": {
|
|
2322
|
-
const counter = getWriteableCounter(val, context, path, objectId, prop);
|
|
2323
|
-
return counter;
|
|
2324
|
-
}
|
|
2325
|
-
default:
|
|
2326
|
-
throw RangeError(`datatype ${datatype} unimplemented`);
|
|
2327
|
-
}
|
|
2328
|
-
}
|
|
2329
|
-
function import_value(value, path, context) {
|
|
2330
|
-
const type = typeof value;
|
|
2331
|
-
switch (type) {
|
|
2332
|
-
case "object":
|
|
2333
|
-
if (value == null) {
|
|
2334
|
-
return [null, "null"];
|
|
2335
|
-
}
|
|
2336
|
-
else if (value[UINT]) {
|
|
2337
|
-
return [value.value, "uint"];
|
|
2338
|
-
}
|
|
2339
|
-
else if (value[INT]) {
|
|
2340
|
-
return [value.value, "int"];
|
|
2341
|
-
}
|
|
2342
|
-
else if (value[F64]) {
|
|
2343
|
-
return [value.value, "f64"];
|
|
2344
|
-
}
|
|
2345
|
-
else if (value[COUNTER]) {
|
|
2346
|
-
return [value.value, "counter"];
|
|
2347
|
-
}
|
|
2348
|
-
else if (value instanceof Date) {
|
|
2349
|
-
return [value.getTime(), "timestamp"];
|
|
2350
|
-
}
|
|
2351
|
-
else if (isImmutableString(value)) {
|
|
2352
|
-
return [value.toString(), "str"];
|
|
2353
|
-
}
|
|
2354
|
-
else if (value instanceof Uint8Array) {
|
|
2355
|
-
return [value, "bytes"];
|
|
2356
|
-
}
|
|
2357
|
-
else if (value instanceof Array) {
|
|
2358
|
-
return [value, "list"];
|
|
2359
|
-
}
|
|
2360
|
-
else if (Object.prototype.toString.call(value) === "[object Object]") {
|
|
2361
|
-
return [value, "map"];
|
|
2362
|
-
}
|
|
2363
|
-
else if (isSameDocument(value, context)) {
|
|
2364
|
-
throw new RangeError("Cannot create a reference to an existing document object");
|
|
2365
|
-
}
|
|
2366
|
-
else {
|
|
2367
|
-
throw new RangeError(`Cannot assign unknown object: ${value}`);
|
|
2368
|
-
}
|
|
2369
|
-
case "boolean":
|
|
2370
|
-
return [value, "boolean"];
|
|
2371
|
-
case "number":
|
|
2372
|
-
if (Number.isInteger(value)) {
|
|
2373
|
-
return [value, "int"];
|
|
2374
|
-
}
|
|
2375
|
-
else {
|
|
2376
|
-
return [value, "f64"];
|
|
2377
|
-
}
|
|
2378
|
-
case "string":
|
|
2379
|
-
return [value, "text"];
|
|
2380
|
-
case "undefined":
|
|
2381
|
-
throw new RangeError([
|
|
2382
|
-
`Cannot assign undefined value at ${printPath(path)}, `,
|
|
2383
|
-
"because `undefined` is not a valid JSON data type. ",
|
|
2384
|
-
"You might consider setting the property's value to `null`, ",
|
|
2385
|
-
"or using `delete` to remove it altogether.",
|
|
2386
|
-
].join(""));
|
|
2387
|
-
default:
|
|
2388
|
-
throw new RangeError([
|
|
2389
|
-
`Cannot assign ${type} value at ${printPath(path)}. `,
|
|
2390
|
-
`All JSON primitive datatypes (object, array, string, number, boolean, null) `,
|
|
2391
|
-
`are supported in an Automerge document; ${type} values are not. `,
|
|
2392
|
-
].join(""));
|
|
2393
|
-
}
|
|
2394
|
-
}
|
|
2395
|
-
// When we assign a value to a property in a proxy we recursively walk through
|
|
2396
|
-
// the value we are assigning and copy it into the document. This is generally
|
|
2397
|
-
// desirable behaviour. However, a very common bug is to accidentally assign a
|
|
2398
|
-
// value which is already in the document to another key within the same
|
|
2399
|
-
// document, this often leads to surprising behaviour where users expected to
|
|
2400
|
-
// _move_ the object, but it is instead copied. To avoid this we check if the
|
|
2401
|
-
// value is from the same document and if it is we throw an error, this means
|
|
2402
|
-
// we require an explicit Object.assign call to copy the object, thus avoiding
|
|
2403
|
-
// the footgun
|
|
2404
|
-
function isSameDocument(val, context) {
|
|
2405
|
-
var _b, _c;
|
|
2406
|
-
// Date is technically an object, but immutable, so allowing people to assign
|
|
2407
|
-
// a date from one place in the document to another place in the document is
|
|
2408
|
-
// not likely to be a bug
|
|
2409
|
-
if (val instanceof Date) {
|
|
2410
|
-
return false;
|
|
2411
|
-
}
|
|
2412
|
-
// this depends on __wbg_ptr being the wasm pointer
|
|
2413
|
-
// a new version of wasm-bindgen will break this
|
|
2414
|
-
// but the tests should expose the break
|
|
2415
|
-
if (val && ((_c = (_b = val[STATE]) === null || _b === void 0 ? void 0 : _b.handle) === null || _c === void 0 ? void 0 : _c.__wbg_ptr) === context.__wbg_ptr) {
|
|
2416
|
-
return true;
|
|
2417
|
-
}
|
|
2418
|
-
return false;
|
|
2419
|
-
}
|
|
2420
|
-
const MapHandler = {
|
|
2421
|
-
get(target, key) {
|
|
2422
|
-
const { context, objectId, cache } = target;
|
|
2423
|
-
if (key === Symbol.toStringTag) {
|
|
2424
|
-
return target[Symbol.toStringTag];
|
|
2425
|
-
}
|
|
2426
|
-
if (key === OBJECT_ID)
|
|
2427
|
-
return objectId;
|
|
2428
|
-
if (key === IS_PROXY)
|
|
2429
|
-
return true;
|
|
2430
|
-
if (key === TRACE)
|
|
2431
|
-
return target.trace;
|
|
2432
|
-
if (key === STATE)
|
|
2433
|
-
return { handle: context };
|
|
2434
|
-
if (!cache[key]) {
|
|
2435
|
-
cache[key] = valueAt(target, key);
|
|
2436
|
-
}
|
|
2437
|
-
return cache[key];
|
|
2438
|
-
},
|
|
2439
|
-
set(target, key, val) {
|
|
2440
|
-
const { context, objectId, path } = target;
|
|
2441
|
-
target.cache = {}; // reset cache on set
|
|
2442
|
-
if (isSameDocument(val, context)) {
|
|
2443
|
-
throw new RangeError("Cannot create a reference to an existing document object");
|
|
2444
|
-
}
|
|
2445
|
-
if (key === TRACE) {
|
|
2446
|
-
target.trace = val;
|
|
2447
|
-
return true;
|
|
2448
|
-
}
|
|
2449
|
-
if (key === CLEAR_CACHE) {
|
|
2450
|
-
return true;
|
|
2451
|
-
}
|
|
2452
|
-
const [value, datatype] = import_value(val, [...path, key], context);
|
|
2453
|
-
switch (datatype) {
|
|
2454
|
-
case "list": {
|
|
2455
|
-
const list = context.putObject(objectId, key, []);
|
|
2456
|
-
const proxyList = listProxy(context, list, [...path, key]);
|
|
2457
|
-
for (let i = 0; i < value.length; i++) {
|
|
2458
|
-
proxyList[i] = value[i];
|
|
2459
|
-
}
|
|
2460
|
-
break;
|
|
2461
|
-
}
|
|
2462
|
-
case "text": {
|
|
2463
|
-
context.putObject(objectId, key, value);
|
|
2464
|
-
break;
|
|
2465
|
-
}
|
|
2466
|
-
case "map": {
|
|
2467
|
-
const map = context.putObject(objectId, key, {});
|
|
2468
|
-
const proxyMap = mapProxy(context, map, [...path, key]);
|
|
2469
|
-
for (const key in value) {
|
|
2470
|
-
proxyMap[key] = value[key];
|
|
2471
|
-
}
|
|
2472
|
-
break;
|
|
2473
|
-
}
|
|
2474
|
-
default:
|
|
2475
|
-
context.put(objectId, key, value, datatype);
|
|
2476
|
-
}
|
|
2477
|
-
return true;
|
|
2478
|
-
},
|
|
2479
|
-
deleteProperty(target, key) {
|
|
2480
|
-
const { context, objectId } = target;
|
|
2481
|
-
target.cache = {}; // reset cache on delete
|
|
2482
|
-
context.delete(objectId, key);
|
|
2483
|
-
return true;
|
|
2484
|
-
},
|
|
2485
|
-
has(target, key) {
|
|
2486
|
-
const value = this.get(target, key);
|
|
2487
|
-
return value !== undefined;
|
|
2488
|
-
},
|
|
2489
|
-
getOwnPropertyDescriptor(target, key) {
|
|
2490
|
-
// const { context, objectId } = target
|
|
2491
|
-
const value = this.get(target, key);
|
|
2492
|
-
if (typeof value !== "undefined") {
|
|
2493
|
-
return {
|
|
2494
|
-
configurable: true,
|
|
2495
|
-
enumerable: true,
|
|
2496
|
-
value,
|
|
2497
|
-
};
|
|
2498
|
-
}
|
|
2499
|
-
},
|
|
2500
|
-
ownKeys(target) {
|
|
2501
|
-
const { context, objectId } = target;
|
|
2502
|
-
// FIXME - this is a tmp workaround until fix the dupe key bug in keys()
|
|
2503
|
-
const keys = context.keys(objectId);
|
|
2504
|
-
return [...new Set(keys)];
|
|
2505
|
-
},
|
|
2506
|
-
};
|
|
2507
|
-
const ListHandler = {
|
|
2508
|
-
get(target, index) {
|
|
2509
|
-
const { context, objectId } = target;
|
|
2510
|
-
index = parseListIndex(index);
|
|
2511
|
-
if (index === Symbol.hasInstance) {
|
|
2512
|
-
return (instance) => {
|
|
2513
|
-
return Array.isArray(instance);
|
|
2514
|
-
};
|
|
2515
|
-
}
|
|
2516
|
-
if (index === Symbol.toStringTag) {
|
|
2517
|
-
return target[Symbol.toStringTag];
|
|
2518
|
-
}
|
|
2519
|
-
if (index === OBJECT_ID)
|
|
2520
|
-
return objectId;
|
|
2521
|
-
if (index === IS_PROXY)
|
|
2522
|
-
return true;
|
|
2523
|
-
if (index === TRACE)
|
|
2524
|
-
return target.trace;
|
|
2525
|
-
if (index === STATE)
|
|
2526
|
-
return { handle: context };
|
|
2527
|
-
if (index === "length")
|
|
2528
|
-
return context.length(objectId);
|
|
2529
|
-
if (typeof index === "number") {
|
|
2530
|
-
return valueAt(target, index);
|
|
2531
|
-
}
|
|
2532
|
-
else {
|
|
2533
|
-
return listMethods(target)[index];
|
|
2534
|
-
}
|
|
2535
|
-
},
|
|
2536
|
-
set(target, index, val) {
|
|
2537
|
-
const { context, objectId, path } = target;
|
|
2538
|
-
index = parseListIndex(index);
|
|
2539
|
-
if (isSameDocument(val, context)) {
|
|
2540
|
-
throw new RangeError("Cannot create a reference to an existing document object");
|
|
2541
|
-
}
|
|
2542
|
-
if (index === CLEAR_CACHE) {
|
|
2543
|
-
return true;
|
|
2544
|
-
}
|
|
2545
|
-
if (index === TRACE) {
|
|
2546
|
-
target.trace = val;
|
|
2547
|
-
return true;
|
|
2548
|
-
}
|
|
2549
|
-
if (typeof index == "string") {
|
|
2550
|
-
throw new RangeError("list index must be a number");
|
|
2551
|
-
}
|
|
2552
|
-
const [value, datatype] = import_value(val, [...path, index], context);
|
|
2553
|
-
switch (datatype) {
|
|
2554
|
-
case "list": {
|
|
2555
|
-
let list;
|
|
2556
|
-
if (index >= context.length(objectId)) {
|
|
2557
|
-
list = context.insertObject(objectId, index, []);
|
|
2558
|
-
}
|
|
2559
|
-
else {
|
|
2560
|
-
list = context.putObject(objectId, index, []);
|
|
2561
|
-
}
|
|
2562
|
-
const proxyList = listProxy(context, list, [...path, index]);
|
|
2563
|
-
proxyList.splice(0, 0, ...value);
|
|
2564
|
-
break;
|
|
2565
|
-
}
|
|
2566
|
-
case "text": {
|
|
2567
|
-
if (index >= context.length(objectId)) {
|
|
2568
|
-
context.insertObject(objectId, index, value);
|
|
2569
|
-
}
|
|
2570
|
-
else {
|
|
2571
|
-
context.putObject(objectId, index, value);
|
|
2572
|
-
}
|
|
2573
|
-
break;
|
|
2574
|
-
}
|
|
2575
|
-
case "map": {
|
|
2576
|
-
let map;
|
|
2577
|
-
if (index >= context.length(objectId)) {
|
|
2578
|
-
map = context.insertObject(objectId, index, {});
|
|
2579
|
-
}
|
|
2580
|
-
else {
|
|
2581
|
-
map = context.putObject(objectId, index, {});
|
|
2582
|
-
}
|
|
2583
|
-
const proxyMap = mapProxy(context, map, [...path, index]);
|
|
2584
|
-
for (const key in value) {
|
|
2585
|
-
proxyMap[key] = value[key];
|
|
2586
|
-
}
|
|
2587
|
-
break;
|
|
2588
|
-
}
|
|
2589
|
-
default:
|
|
2590
|
-
if (index >= context.length(objectId)) {
|
|
2591
|
-
context.insert(objectId, index, value, datatype);
|
|
2592
|
-
}
|
|
2593
|
-
else {
|
|
2594
|
-
context.put(objectId, index, value, datatype);
|
|
2595
|
-
}
|
|
2596
|
-
}
|
|
2597
|
-
return true;
|
|
2598
|
-
},
|
|
2599
|
-
deleteProperty(target, index) {
|
|
2600
|
-
const { context, objectId } = target;
|
|
2601
|
-
index = parseListIndex(index);
|
|
2602
|
-
const elem = context.get(objectId, index);
|
|
2603
|
-
if (elem != null && elem[0] == "counter") {
|
|
2604
|
-
throw new TypeError("Unsupported operation: deleting a counter from a list");
|
|
2605
|
-
}
|
|
2606
|
-
context.delete(objectId, index);
|
|
2607
|
-
return true;
|
|
2608
|
-
},
|
|
2609
|
-
has(target, index) {
|
|
2610
|
-
const { context, objectId } = target;
|
|
2611
|
-
index = parseListIndex(index);
|
|
2612
|
-
if (typeof index === "number") {
|
|
2613
|
-
return index < context.length(objectId);
|
|
2614
|
-
}
|
|
2615
|
-
return index === "length";
|
|
2616
|
-
},
|
|
2617
|
-
getOwnPropertyDescriptor(target, index) {
|
|
2618
|
-
const { context, objectId } = target;
|
|
2619
|
-
if (index === "length")
|
|
2620
|
-
return { writable: true, value: context.length(objectId) };
|
|
2621
|
-
if (index === OBJECT_ID)
|
|
2622
|
-
return { configurable: false, enumerable: false, value: objectId };
|
|
2623
|
-
index = parseListIndex(index);
|
|
2624
|
-
const value = valueAt(target, index);
|
|
2625
|
-
return { configurable: true, enumerable: true, value };
|
|
2626
|
-
},
|
|
2627
|
-
getPrototypeOf(target) {
|
|
2628
|
-
return Object.getPrototypeOf(target);
|
|
2629
|
-
},
|
|
2630
|
-
ownKeys( /*target*/) {
|
|
2631
|
-
const keys = [];
|
|
2632
|
-
// uncommenting this causes assert.deepEqual() to fail when comparing to a pojo array
|
|
2633
|
-
// but not uncommenting it causes for (i in list) {} to not enumerate values properly
|
|
2634
|
-
//const {context, objectId } = target
|
|
2635
|
-
//for (let i = 0; i < target.context.length(objectId); i++) { keys.push(i.toString()) }
|
|
2636
|
-
keys.push("length");
|
|
2637
|
-
return keys;
|
|
2638
|
-
},
|
|
2639
|
-
};
|
|
2640
|
-
Object.assign({}, ListHandler, {
|
|
2641
|
-
get(target, index) {
|
|
2642
|
-
const { context, objectId } = target;
|
|
2643
|
-
index = parseListIndex(index);
|
|
2644
|
-
if (index === Symbol.hasInstance) {
|
|
2645
|
-
return (instance) => {
|
|
2646
|
-
return Array.isArray(instance);
|
|
2647
|
-
};
|
|
2648
|
-
}
|
|
2649
|
-
if (index === Symbol.toStringTag) {
|
|
2650
|
-
return target[Symbol.toStringTag];
|
|
2651
|
-
}
|
|
2652
|
-
if (index === OBJECT_ID)
|
|
2653
|
-
return objectId;
|
|
2654
|
-
if (index === IS_PROXY)
|
|
2655
|
-
return true;
|
|
2656
|
-
if (index === TRACE)
|
|
2657
|
-
return target.trace;
|
|
2658
|
-
if (index === STATE)
|
|
2659
|
-
return { handle: context };
|
|
2660
|
-
if (index === "length")
|
|
2661
|
-
return context.length(objectId);
|
|
2662
|
-
if (typeof index === "number") {
|
|
2663
|
-
return valueAt(target, index);
|
|
2664
|
-
}
|
|
2665
|
-
else {
|
|
2666
|
-
return textMethods(target)[index] || listMethods(target)[index];
|
|
2667
|
-
}
|
|
2668
|
-
},
|
|
2669
|
-
getPrototypeOf( /*target*/) {
|
|
2670
|
-
return Object.getPrototypeOf(new Text());
|
|
2671
|
-
},
|
|
2672
|
-
});
|
|
2673
|
-
function mapProxy(context, objectId, path) {
|
|
2674
|
-
const target = {
|
|
2675
|
-
context,
|
|
2676
|
-
objectId,
|
|
2677
|
-
path: path || [],
|
|
2678
|
-
cache: {},
|
|
2679
|
-
};
|
|
2680
|
-
const proxied = {};
|
|
2681
|
-
Object.assign(proxied, target);
|
|
2682
|
-
const result = new Proxy(proxied, MapHandler);
|
|
2683
|
-
// conversion through unknown is necessary because the types are so different
|
|
2684
|
-
return result;
|
|
2685
|
-
}
|
|
2686
|
-
function listProxy(context, objectId, path) {
|
|
2687
|
-
const target = {
|
|
2688
|
-
context,
|
|
2689
|
-
objectId,
|
|
2690
|
-
path: path || [],
|
|
2691
|
-
cache: {},
|
|
2692
|
-
};
|
|
2693
|
-
const proxied = [];
|
|
2694
|
-
Object.assign(proxied, target);
|
|
2695
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
2696
|
-
// @ts-ignore
|
|
2697
|
-
return new Proxy(proxied, ListHandler);
|
|
2698
|
-
}
|
|
2699
|
-
function listMethods(target) {
|
|
2700
|
-
const { context, objectId, path } = target;
|
|
2701
|
-
const methods = {
|
|
2702
|
-
at(index) {
|
|
2703
|
-
return valueAt(target, index);
|
|
2704
|
-
},
|
|
2705
|
-
deleteAt(index, numDelete) {
|
|
2706
|
-
if (typeof numDelete === "number") {
|
|
2707
|
-
context.splice(objectId, index, numDelete);
|
|
2708
|
-
}
|
|
2709
|
-
else {
|
|
2710
|
-
context.delete(objectId, index);
|
|
2711
|
-
}
|
|
2712
|
-
return this;
|
|
2713
|
-
},
|
|
2714
|
-
fill(val, start, end) {
|
|
2715
|
-
const [value, datatype] = import_value(val, [...path, start], context);
|
|
2716
|
-
const length = context.length(objectId);
|
|
2717
|
-
start = parseListIndex(start || 0);
|
|
2718
|
-
end = parseListIndex(end || length);
|
|
2719
|
-
for (let i = start; i < Math.min(end, length); i++) {
|
|
2720
|
-
if (datatype === "list" || datatype === "map") {
|
|
2721
|
-
context.putObject(objectId, i, value);
|
|
2722
|
-
}
|
|
2723
|
-
else if (datatype === "text") {
|
|
2724
|
-
context.putObject(objectId, i, value);
|
|
2725
|
-
}
|
|
2726
|
-
else {
|
|
2727
|
-
context.put(objectId, i, value, datatype);
|
|
2728
|
-
}
|
|
2729
|
-
}
|
|
2730
|
-
return this;
|
|
2731
|
-
},
|
|
2732
|
-
indexOf(searchElement, start = 0) {
|
|
2733
|
-
const length = context.length(objectId);
|
|
2734
|
-
for (let i = start; i < length; i++) {
|
|
2735
|
-
const valueWithType = context.getWithType(objectId, i);
|
|
2736
|
-
if (!valueWithType) {
|
|
2737
|
-
continue;
|
|
2738
|
-
}
|
|
2739
|
-
const [valType, value] = valueWithType;
|
|
2740
|
-
// Either the target element is an object, and we return if we have found
|
|
2741
|
-
// the same object or it is a primitive value and we return if it matches
|
|
2742
|
-
// the current value
|
|
2743
|
-
const isObject = ["map", "list", "text"].includes(valType);
|
|
2744
|
-
if (!isObject) {
|
|
2745
|
-
// If the element is not an object, then check if the value is equal to the target
|
|
2746
|
-
if (value === searchElement) {
|
|
2747
|
-
return i;
|
|
2748
|
-
}
|
|
2749
|
-
else {
|
|
2750
|
-
continue;
|
|
2751
|
-
}
|
|
2752
|
-
}
|
|
2753
|
-
// if it's an object, but the type of the search element is a string, then we
|
|
2754
|
-
// need to check if the object is a text object with the same value as the search element
|
|
2755
|
-
if (valType === "text" && typeof searchElement === "string") {
|
|
2756
|
-
if (searchElement === valueAt(target, i)) {
|
|
2757
|
-
return i;
|
|
2758
|
-
}
|
|
2759
|
-
}
|
|
2760
|
-
// The only possible match now is if the searchElement is an object already in the
|
|
2761
|
-
// automerge document with the same object ID as the value
|
|
2762
|
-
if (searchElement[OBJECT_ID] === value) {
|
|
2763
|
-
return i;
|
|
2764
|
-
}
|
|
2765
|
-
}
|
|
2766
|
-
return -1;
|
|
2767
|
-
},
|
|
2768
|
-
insertAt(index, ...values) {
|
|
2769
|
-
this.splice(index, 0, ...values);
|
|
2770
|
-
return this;
|
|
2771
|
-
},
|
|
2772
|
-
pop() {
|
|
2773
|
-
const length = context.length(objectId);
|
|
2774
|
-
if (length == 0) {
|
|
2775
|
-
return undefined;
|
|
2776
|
-
}
|
|
2777
|
-
const last = valueAt(target, length - 1);
|
|
2778
|
-
context.delete(objectId, length - 1);
|
|
2779
|
-
return last;
|
|
2780
|
-
},
|
|
2781
|
-
push(...values) {
|
|
2782
|
-
const len = context.length(objectId);
|
|
2783
|
-
this.splice(len, 0, ...values);
|
|
2784
|
-
return context.length(objectId);
|
|
2785
|
-
},
|
|
2786
|
-
shift() {
|
|
2787
|
-
if (context.length(objectId) == 0)
|
|
2788
|
-
return;
|
|
2789
|
-
const first = valueAt(target, 0);
|
|
2790
|
-
context.delete(objectId, 0);
|
|
2791
|
-
return first;
|
|
2792
|
-
},
|
|
2793
|
-
splice(index, del, ...vals) {
|
|
2794
|
-
index = parseListIndex(index);
|
|
2795
|
-
// if del is undefined, delete until the end of the list
|
|
2796
|
-
if (typeof del !== "number") {
|
|
2797
|
-
del = context.length(objectId) - index;
|
|
2798
|
-
}
|
|
2799
|
-
del = parseListIndex(del);
|
|
2800
|
-
for (const val of vals) {
|
|
2801
|
-
if (isSameDocument(val, context)) {
|
|
2802
|
-
throw new RangeError("Cannot create a reference to an existing document object");
|
|
2803
|
-
}
|
|
2804
|
-
}
|
|
2805
|
-
const result = [];
|
|
2806
|
-
for (let i = 0; i < del; i++) {
|
|
2807
|
-
const value = valueAt(target, index);
|
|
2808
|
-
if (value !== undefined) {
|
|
2809
|
-
result.push(value);
|
|
2810
|
-
}
|
|
2811
|
-
context.delete(objectId, index);
|
|
2812
|
-
}
|
|
2813
|
-
const values = vals.map((val, index) => {
|
|
2814
|
-
try {
|
|
2815
|
-
return import_value(val, [...path], context);
|
|
2816
|
-
}
|
|
2817
|
-
catch (e) {
|
|
2818
|
-
if (e instanceof RangeError) {
|
|
2819
|
-
throw new RangeError(`${e.message} (at index ${index} in the input)`);
|
|
2820
|
-
}
|
|
2821
|
-
else {
|
|
2822
|
-
throw e;
|
|
2823
|
-
}
|
|
2824
|
-
}
|
|
2825
|
-
});
|
|
2826
|
-
for (const [value, datatype] of values) {
|
|
2827
|
-
switch (datatype) {
|
|
2828
|
-
case "list": {
|
|
2829
|
-
const list = context.insertObject(objectId, index, []);
|
|
2830
|
-
const proxyList = listProxy(context, list, [...path, index]);
|
|
2831
|
-
proxyList.splice(0, 0, ...value);
|
|
2832
|
-
break;
|
|
2833
|
-
}
|
|
2834
|
-
case "text": {
|
|
2835
|
-
context.insertObject(objectId, index, value);
|
|
2836
|
-
break;
|
|
2837
|
-
}
|
|
2838
|
-
case "map": {
|
|
2839
|
-
const map = context.insertObject(objectId, index, {});
|
|
2840
|
-
const proxyMap = mapProxy(context, map, [...path, index]);
|
|
2841
|
-
for (const key in value) {
|
|
2842
|
-
proxyMap[key] = value[key];
|
|
2843
|
-
}
|
|
2844
|
-
break;
|
|
2845
|
-
}
|
|
2846
|
-
default:
|
|
2847
|
-
context.insert(objectId, index, value, datatype);
|
|
2848
|
-
}
|
|
2849
|
-
index += 1;
|
|
2850
|
-
}
|
|
2851
|
-
return result;
|
|
2852
|
-
},
|
|
2853
|
-
unshift(...values) {
|
|
2854
|
-
this.splice(0, 0, ...values);
|
|
2855
|
-
return context.length(objectId);
|
|
2856
|
-
},
|
|
2857
|
-
entries() {
|
|
2858
|
-
let i = 0;
|
|
2859
|
-
const iterator = {
|
|
2860
|
-
next: () => {
|
|
2861
|
-
const value = valueAt(target, i);
|
|
2862
|
-
if (value === undefined) {
|
|
2863
|
-
return { value: undefined, done: true };
|
|
2864
|
-
}
|
|
2865
|
-
else {
|
|
2866
|
-
return { value: [i++, value], done: false };
|
|
2867
|
-
}
|
|
2868
|
-
},
|
|
2869
|
-
[Symbol.iterator]() {
|
|
2870
|
-
return this;
|
|
2871
|
-
},
|
|
2872
|
-
};
|
|
2873
|
-
return iterator;
|
|
2874
|
-
},
|
|
2875
|
-
keys() {
|
|
2876
|
-
let i = 0;
|
|
2877
|
-
const len = context.length(objectId);
|
|
2878
|
-
const iterator = {
|
|
2879
|
-
next: () => {
|
|
2880
|
-
if (i < len) {
|
|
2881
|
-
return { value: i++, done: false };
|
|
2882
|
-
}
|
|
2883
|
-
return { value: undefined, done: true };
|
|
2884
|
-
},
|
|
2885
|
-
[Symbol.iterator]() {
|
|
2886
|
-
return this;
|
|
2887
|
-
},
|
|
2888
|
-
};
|
|
2889
|
-
return iterator;
|
|
2890
|
-
},
|
|
2891
|
-
values() {
|
|
2892
|
-
let i = 0;
|
|
2893
|
-
const iterator = {
|
|
2894
|
-
next: () => {
|
|
2895
|
-
const value = valueAt(target, i++);
|
|
2896
|
-
if (value === undefined) {
|
|
2897
|
-
return { value: undefined, done: true };
|
|
2898
|
-
}
|
|
2899
|
-
else {
|
|
2900
|
-
return { value, done: false };
|
|
2901
|
-
}
|
|
2902
|
-
},
|
|
2903
|
-
[Symbol.iterator]() {
|
|
2904
|
-
return this;
|
|
2905
|
-
},
|
|
2906
|
-
};
|
|
2907
|
-
return iterator;
|
|
2908
|
-
},
|
|
2909
|
-
toArray() {
|
|
2910
|
-
const list = [];
|
|
2911
|
-
let value;
|
|
2912
|
-
do {
|
|
2913
|
-
value = valueAt(target, list.length);
|
|
2914
|
-
if (value !== undefined) {
|
|
2915
|
-
list.push(value);
|
|
2916
|
-
}
|
|
2917
|
-
} while (value !== undefined);
|
|
2918
|
-
return list;
|
|
2919
|
-
},
|
|
2920
|
-
map(f) {
|
|
2921
|
-
return this.toArray().map(f);
|
|
2922
|
-
},
|
|
2923
|
-
toString() {
|
|
2924
|
-
return this.toArray().toString();
|
|
2925
|
-
},
|
|
2926
|
-
toLocaleString() {
|
|
2927
|
-
return this.toArray().toLocaleString();
|
|
2928
|
-
},
|
|
2929
|
-
forEach(f) {
|
|
2930
|
-
return this.toArray().forEach(f);
|
|
2931
|
-
},
|
|
2932
|
-
// todo: real concat function is different
|
|
2933
|
-
concat(other) {
|
|
2934
|
-
return this.toArray().concat(other);
|
|
2935
|
-
},
|
|
2936
|
-
every(f) {
|
|
2937
|
-
return this.toArray().every(f);
|
|
2938
|
-
},
|
|
2939
|
-
filter(f) {
|
|
2940
|
-
return this.toArray().filter(f);
|
|
2941
|
-
},
|
|
2942
|
-
find(f) {
|
|
2943
|
-
let index = 0;
|
|
2944
|
-
for (const v of this) {
|
|
2945
|
-
if (f(v, index)) {
|
|
2946
|
-
return v;
|
|
2947
|
-
}
|
|
2948
|
-
index += 1;
|
|
2949
|
-
}
|
|
2950
|
-
},
|
|
2951
|
-
findIndex(f) {
|
|
2952
|
-
let index = 0;
|
|
2953
|
-
for (const v of this) {
|
|
2954
|
-
if (f(v, index)) {
|
|
2955
|
-
return index;
|
|
2956
|
-
}
|
|
2957
|
-
index += 1;
|
|
2958
|
-
}
|
|
2959
|
-
return -1;
|
|
2960
|
-
},
|
|
2961
|
-
includes(elem) {
|
|
2962
|
-
return this.find(e => e === elem) !== undefined;
|
|
2963
|
-
},
|
|
2964
|
-
join(sep) {
|
|
2965
|
-
return this.toArray().join(sep);
|
|
2966
|
-
},
|
|
2967
|
-
reduce(f, initialValue) {
|
|
2968
|
-
return this.toArray().reduce(f, initialValue);
|
|
2969
|
-
},
|
|
2970
|
-
reduceRight(f, initialValue) {
|
|
2971
|
-
return this.toArray().reduceRight(f, initialValue);
|
|
2972
|
-
},
|
|
2973
|
-
lastIndexOf(search, fromIndex = +Infinity) {
|
|
2974
|
-
// this can be faster
|
|
2975
|
-
return this.toArray().lastIndexOf(search, fromIndex);
|
|
2976
|
-
},
|
|
2977
|
-
slice(index, num) {
|
|
2978
|
-
return this.toArray().slice(index, num);
|
|
2979
|
-
},
|
|
2980
|
-
some(f) {
|
|
2981
|
-
let index = 0;
|
|
2982
|
-
for (const v of this) {
|
|
2983
|
-
if (f(v, index)) {
|
|
2984
|
-
return true;
|
|
2985
|
-
}
|
|
2986
|
-
index += 1;
|
|
2987
|
-
}
|
|
2988
|
-
return false;
|
|
2989
|
-
},
|
|
2990
|
-
[Symbol.iterator]: function* () {
|
|
2991
|
-
let i = 0;
|
|
2992
|
-
let value = valueAt(target, i);
|
|
2993
|
-
while (value !== undefined) {
|
|
2994
|
-
yield value;
|
|
2995
|
-
i += 1;
|
|
2996
|
-
value = valueAt(target, i);
|
|
2997
|
-
}
|
|
2998
|
-
},
|
|
2999
|
-
};
|
|
3000
|
-
return methods;
|
|
3001
|
-
}
|
|
3002
|
-
function textMethods(target) {
|
|
3003
|
-
const { context, objectId } = target;
|
|
3004
|
-
const methods = {
|
|
3005
|
-
set(index, value) {
|
|
3006
|
-
return (this[index] = value);
|
|
3007
|
-
},
|
|
3008
|
-
get(index) {
|
|
3009
|
-
return this[index];
|
|
3010
|
-
},
|
|
3011
|
-
toString() {
|
|
3012
|
-
return context.text(objectId).replace(//g, "");
|
|
3013
|
-
},
|
|
3014
|
-
toSpans() {
|
|
3015
|
-
const spans = [];
|
|
3016
|
-
let chars = "";
|
|
3017
|
-
const length = context.length(objectId);
|
|
3018
|
-
for (let i = 0; i < length; i++) {
|
|
3019
|
-
const value = this[i];
|
|
3020
|
-
if (typeof value === "string") {
|
|
3021
|
-
chars += value;
|
|
3022
|
-
}
|
|
3023
|
-
else {
|
|
3024
|
-
if (chars.length > 0) {
|
|
3025
|
-
spans.push(chars);
|
|
3026
|
-
chars = "";
|
|
3027
|
-
}
|
|
3028
|
-
spans.push(value);
|
|
3029
|
-
}
|
|
3030
|
-
}
|
|
3031
|
-
if (chars.length > 0) {
|
|
3032
|
-
spans.push(chars);
|
|
3033
|
-
}
|
|
3034
|
-
return spans;
|
|
3035
|
-
},
|
|
3036
|
-
toJSON() {
|
|
3037
|
-
return this.toString();
|
|
3038
|
-
},
|
|
3039
|
-
indexOf(o, start = 0) {
|
|
3040
|
-
const text = context.text(objectId);
|
|
3041
|
-
return text.indexOf(o, start);
|
|
3042
|
-
},
|
|
3043
|
-
insertAt(index, ...values) {
|
|
3044
|
-
if (values.every(v => typeof v === "string")) {
|
|
3045
|
-
context.splice(objectId, index, 0, values.join(""));
|
|
3046
|
-
}
|
|
3047
|
-
else {
|
|
3048
|
-
listMethods(target).insertAt(index, ...values);
|
|
3049
|
-
}
|
|
3050
|
-
},
|
|
3051
|
-
};
|
|
3052
|
-
return methods;
|
|
3053
|
-
}
|
|
3054
|
-
function printPath(path) {
|
|
3055
|
-
// print the path as a json pointer
|
|
3056
|
-
const jsonPointerComponents = path.map(component => {
|
|
3057
|
-
// if its a number just turn it into a string
|
|
3058
|
-
if (typeof component === "number") {
|
|
3059
|
-
return component.toString();
|
|
3060
|
-
}
|
|
3061
|
-
else if (typeof component === "string") {
|
|
3062
|
-
// otherwise we have to escape `/` and `~` characters
|
|
3063
|
-
return component.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
3064
|
-
}
|
|
3065
|
-
});
|
|
3066
|
-
if (path.length === 0) {
|
|
3067
|
-
return "";
|
|
3068
|
-
}
|
|
3069
|
-
else {
|
|
3070
|
-
return "/" + jsonPointerComponents.join("/");
|
|
3071
|
-
}
|
|
3072
|
-
}
|
|
3073
|
-
/*
|
|
3074
|
-
* Check if an object is a {@link ImmutableString}
|
|
3075
|
-
*/
|
|
3076
|
-
function isImmutableString(obj) {
|
|
3077
|
-
// We used to determine whether something was a ImmutableString by doing an instanceof check, but
|
|
3078
|
-
// this doesn't work if the automerge module is loaded twice somehow. Instead, use the presence
|
|
3079
|
-
// of a symbol to determine if something is a ImmutableString
|
|
3080
|
-
return (typeof obj === "object" &&
|
|
3081
|
-
obj !== null &&
|
|
3082
|
-
Object.prototype.hasOwnProperty.call(obj, IMMUTABLE_STRING));
|
|
3083
|
-
}
|
|
3084
|
-
|
|
3085
|
-
let wasm;
|
|
3086
|
-
|
|
3087
|
-
const cachedTextEncoder = (typeof TextEncoder !== 'undefined' ? new TextEncoder('utf-8') : { encode: () => { throw Error('TextEncoder not available') } } );
|
|
3088
|
-
|
|
3089
|
-
(typeof cachedTextEncoder.encodeInto === 'function'
|
|
3090
|
-
? function (arg, view) {
|
|
3091
|
-
return cachedTextEncoder.encodeInto(arg, view);
|
|
3092
|
-
}
|
|
3093
|
-
: function (arg, view) {
|
|
3094
|
-
const buf = cachedTextEncoder.encode(arg);
|
|
3095
|
-
view.set(buf);
|
|
3096
|
-
return {
|
|
3097
|
-
read: arg.length,
|
|
3098
|
-
written: buf.length
|
|
3099
|
-
};
|
|
3100
|
-
});
|
|
3101
|
-
|
|
3102
|
-
const cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } );
|
|
3103
|
-
|
|
3104
|
-
if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); }
|
|
3105
|
-
(typeof FinalizationRegistry === 'undefined')
|
|
3106
|
-
? { }
|
|
3107
|
-
: new FinalizationRegistry(ptr => wasm.__wbg_automerge_free(ptr >>> 0, 1));
|
|
3108
|
-
|
|
3109
|
-
(typeof FinalizationRegistry === 'undefined')
|
|
3110
|
-
? { }
|
|
3111
|
-
: new FinalizationRegistry(ptr => wasm.__wbg_syncstate_free(ptr >>> 0, 1));
|
|
2245
|
+
if (Symbol.dispose) SyncState.prototype[Symbol.dispose] = SyncState.prototype.free;
|
|
3112
2246
|
|
|
3113
2247
|
(undefined && undefined.__rest) || function (s, e) {
|
|
3114
2248
|
var t = {};
|
|
@@ -6241,13 +5375,6 @@ const REUSE_BUFFER_MODE = 512;
|
|
|
6241
5375
|
const RESET_BUFFER_MODE = 1024;
|
|
6242
5376
|
const THROW_ON_ITERABLE = 2048;
|
|
6243
5377
|
|
|
6244
|
-
/**
|
|
6245
|
-
* FinalizationRegistry for automatic cleanup of Ref instances.
|
|
6246
|
-
* This ensures subscriptions are cleaned up when Refs are garbage collected,
|
|
6247
|
-
* even if dispose() is never called.
|
|
6248
|
-
*/
|
|
6249
|
-
new FinalizationRegistry(cleanup => cleanup());
|
|
6250
|
-
|
|
6251
5378
|
var sha256$1 = {exports: {}};
|
|
6252
5379
|
|
|
6253
5380
|
var sha256 = sha256$1.exports;
|
|
@@ -6692,67 +5819,139 @@ const DEFAULT_HEARTBEAT_INTERVAL_MS = 15_000;
|
|
|
6692
5819
|
const DEFAULT_PEER_TTL_MS = 3 * DEFAULT_HEARTBEAT_INTERVAL_MS;
|
|
6693
5820
|
const PRESENCE_MESSAGE_MARKER = "__presence";
|
|
6694
5821
|
|
|
6695
|
-
|
|
6696
|
-
|
|
6697
|
-
|
|
5822
|
+
function unique(items) {
|
|
5823
|
+
return Array.from(new Set(items));
|
|
5824
|
+
}
|
|
5825
|
+
|
|
6698
5826
|
class PeerStateView {
|
|
6699
5827
|
value;
|
|
6700
5828
|
constructor(value) {
|
|
6701
5829
|
this.value = value;
|
|
6702
5830
|
}
|
|
6703
5831
|
/**
|
|
6704
|
-
* Get
|
|
6705
|
-
*
|
|
5832
|
+
* Get all users.
|
|
5833
|
+
*
|
|
5834
|
+
* @returns Array of user presence {@link State}s
|
|
5835
|
+
*/
|
|
5836
|
+
get users() {
|
|
5837
|
+
const userIds = unique(Object.values(this.value).map(peerState => peerState.userId));
|
|
5838
|
+
return userIds.map(u => this.getUserState(u));
|
|
5839
|
+
}
|
|
5840
|
+
/**
|
|
5841
|
+
* Get all devices.
|
|
5842
|
+
*
|
|
5843
|
+
* @returns Array of device presence {@link State}s
|
|
5844
|
+
*/
|
|
5845
|
+
get devices() {
|
|
5846
|
+
const deviceIds = unique(Object.values(this.value).map(peerState => peerState.deviceId));
|
|
5847
|
+
return deviceIds.map(d => this.getDeviceState(d));
|
|
5848
|
+
}
|
|
5849
|
+
/**
|
|
5850
|
+
* Get all peers.
|
|
5851
|
+
*
|
|
5852
|
+
* @returns Array of peer presence {@link State}s
|
|
5853
|
+
*/
|
|
5854
|
+
get peers() {
|
|
5855
|
+
return Object.values(this.value);
|
|
5856
|
+
}
|
|
5857
|
+
/**
|
|
5858
|
+
* Get all peer ids for this user.
|
|
5859
|
+
*
|
|
5860
|
+
* @param userId
|
|
5861
|
+
* @returns Array of peer ids for this user
|
|
5862
|
+
*/
|
|
5863
|
+
getUserPeers(userId) {
|
|
5864
|
+
return Object.values(this.value)
|
|
5865
|
+
.filter(peerState => peerState.userId === userId)
|
|
5866
|
+
.map(peerState => peerState.peerId);
|
|
5867
|
+
}
|
|
5868
|
+
/**
|
|
5869
|
+
* Get all peers for this device.
|
|
5870
|
+
*
|
|
5871
|
+
* @param deviceId
|
|
5872
|
+
* @returns Array of peer ids for this device
|
|
5873
|
+
*/
|
|
5874
|
+
getDevicePeers(deviceId) {
|
|
5875
|
+
return Object.values(this.value)
|
|
5876
|
+
.filter(peerState => peerState.deviceId === deviceId)
|
|
5877
|
+
.map(peerState => peerState.peerId);
|
|
5878
|
+
}
|
|
5879
|
+
/**
|
|
5880
|
+
* Return the most-recently-seen peer from this group.
|
|
6706
5881
|
*
|
|
6707
|
-
* @param
|
|
6708
|
-
* @returns
|
|
5882
|
+
* @param peers
|
|
5883
|
+
* @returns id of most recently seen peer
|
|
6709
5884
|
*/
|
|
6710
|
-
|
|
6711
|
-
|
|
6712
|
-
|
|
6713
|
-
|
|
6714
|
-
|
|
6715
|
-
|
|
6716
|
-
if (!(key in byKey)) {
|
|
6717
|
-
byKey[key] = [];
|
|
5885
|
+
getLastSeenPeer(peers) {
|
|
5886
|
+
let freshestLastSeenAt;
|
|
5887
|
+
return peers.reduce((freshest, curr) => {
|
|
5888
|
+
const lastSeenAt = this.value[curr]?.lastActiveAt;
|
|
5889
|
+
if (!lastSeenAt) {
|
|
5890
|
+
return freshest;
|
|
6718
5891
|
}
|
|
6719
|
-
|
|
6720
|
-
|
|
6721
|
-
|
|
6722
|
-
|
|
6723
|
-
|
|
6724
|
-
|
|
6725
|
-
}, {});
|
|
5892
|
+
if (!freshest || lastSeenAt > freshestLastSeenAt) {
|
|
5893
|
+
freshestLastSeenAt = lastSeenAt;
|
|
5894
|
+
return curr;
|
|
5895
|
+
}
|
|
5896
|
+
return freshest;
|
|
5897
|
+
}, undefined);
|
|
6726
5898
|
}
|
|
6727
|
-
|
|
6728
|
-
|
|
6729
|
-
|
|
6730
|
-
|
|
6731
|
-
|
|
6732
|
-
|
|
6733
|
-
|
|
6734
|
-
|
|
6735
|
-
|
|
6736
|
-
|
|
6737
|
-
|
|
6738
|
-
|
|
6739
|
-
|
|
6740
|
-
|
|
6741
|
-
|
|
6742
|
-
|
|
6743
|
-
|
|
6744
|
-
let freshestLastActiveAt;
|
|
6745
|
-
return peers.reduce((freshest, curr) => {
|
|
6746
|
-
const lastActiveAt = curr.lastActiveAt;
|
|
6747
|
-
if (!lastActiveAt) {
|
|
5899
|
+
/**
|
|
5900
|
+
* Return the peer from this group that sent a state update most recently
|
|
5901
|
+
*
|
|
5902
|
+
* @param peers
|
|
5903
|
+
* @returns id of most recently seen peer
|
|
5904
|
+
*/
|
|
5905
|
+
getLastActivePeer(peers) {
|
|
5906
|
+
let freshestLastActiveAt;
|
|
5907
|
+
return peers.reduce((freshest, curr) => {
|
|
5908
|
+
const lastActiveAt = this.value[curr]?.lastActiveAt;
|
|
5909
|
+
if (!lastActiveAt) {
|
|
5910
|
+
return freshest;
|
|
5911
|
+
}
|
|
5912
|
+
if (!freshest || lastActiveAt > freshestLastActiveAt) {
|
|
5913
|
+
freshestLastActiveAt = lastActiveAt;
|
|
5914
|
+
return curr;
|
|
5915
|
+
}
|
|
6748
5916
|
return freshest;
|
|
5917
|
+
}, undefined);
|
|
5918
|
+
}
|
|
5919
|
+
/**
|
|
5920
|
+
* Get current ephemeral state value for this user's most-recently-active
|
|
5921
|
+
* peer.
|
|
5922
|
+
*
|
|
5923
|
+
* @param userId
|
|
5924
|
+
* @returns user's {@link State}
|
|
5925
|
+
*/
|
|
5926
|
+
getUserState(userId) {
|
|
5927
|
+
const peers = this.getUserPeers(userId);
|
|
5928
|
+
if (!peers) {
|
|
5929
|
+
return undefined;
|
|
6749
5930
|
}
|
|
6750
|
-
|
|
6751
|
-
|
|
6752
|
-
return
|
|
5931
|
+
const peer = this.getLastActivePeer(peers);
|
|
5932
|
+
if (!peer) {
|
|
5933
|
+
return undefined;
|
|
6753
5934
|
}
|
|
6754
|
-
return
|
|
6755
|
-
}
|
|
5935
|
+
return this.value[peer];
|
|
5936
|
+
}
|
|
5937
|
+
/**
|
|
5938
|
+
* Get current ephemeral state value for this device's most-recently-active
|
|
5939
|
+
* peer.
|
|
5940
|
+
*
|
|
5941
|
+
* @param deviceId
|
|
5942
|
+
* @returns device's {@link State}
|
|
5943
|
+
*/
|
|
5944
|
+
getDeviceState(deviceId) {
|
|
5945
|
+
const peers = this.getDevicePeers(deviceId);
|
|
5946
|
+
if (!peers) {
|
|
5947
|
+
return undefined;
|
|
5948
|
+
}
|
|
5949
|
+
const peer = this.getLastActivePeer(peers);
|
|
5950
|
+
if (!peer) {
|
|
5951
|
+
return undefined;
|
|
5952
|
+
}
|
|
5953
|
+
return this.value[peer];
|
|
5954
|
+
}
|
|
6756
5955
|
}
|
|
6757
5956
|
|
|
6758
5957
|
class PeerPresenceInfo {
|
|
@@ -6776,15 +5975,11 @@ class PeerPresenceInfo {
|
|
|
6776
5975
|
* @param peerId
|
|
6777
5976
|
*/
|
|
6778
5977
|
markSeen(peerId) {
|
|
6779
|
-
if (!(peerId in this.#peerStates.value)) {
|
|
6780
|
-
// Ignore heartbeats from peers we have not seen before: they will send a snapshot
|
|
6781
|
-
return;
|
|
6782
|
-
}
|
|
6783
5978
|
this.#peerStates = new PeerStateView({
|
|
6784
5979
|
...this.#peerStates.value,
|
|
6785
5980
|
[peerId]: {
|
|
6786
5981
|
...this.#peerStates.value[peerId],
|
|
6787
|
-
|
|
5982
|
+
lastSeen: Date.now(),
|
|
6788
5983
|
},
|
|
6789
5984
|
});
|
|
6790
5985
|
}
|
|
@@ -6795,7 +5990,7 @@ class PeerPresenceInfo {
|
|
|
6795
5990
|
* @param peerId
|
|
6796
5991
|
* @param value
|
|
6797
5992
|
*/
|
|
6798
|
-
update({ peerId, value }) {
|
|
5993
|
+
update({ peerId, deviceId, userId, value, }) {
|
|
6799
5994
|
const peerState = this.#peerStates.value[peerId];
|
|
6800
5995
|
const existingState = peerState?.value ?? {};
|
|
6801
5996
|
const now = Date.now();
|
|
@@ -6803,8 +5998,10 @@ class PeerPresenceInfo {
|
|
|
6803
5998
|
...this.#peerStates.value,
|
|
6804
5999
|
[peerId]: {
|
|
6805
6000
|
peerId,
|
|
6806
|
-
|
|
6001
|
+
deviceId,
|
|
6002
|
+
userId,
|
|
6807
6003
|
lastActiveAt: now,
|
|
6004
|
+
lastUpdateAt: now,
|
|
6808
6005
|
value: {
|
|
6809
6006
|
...existingState,
|
|
6810
6007
|
...value,
|
|
@@ -6828,15 +6025,9 @@ class PeerPresenceInfo {
|
|
|
6828
6025
|
*/
|
|
6829
6026
|
prune() {
|
|
6830
6027
|
const threshold = Date.now() - this.ttl;
|
|
6831
|
-
|
|
6832
|
-
|
|
6833
|
-
const keep = state.lastSeenAt >= threshold;
|
|
6834
|
-
if (!keep) {
|
|
6835
|
-
pruned.push(id);
|
|
6836
|
-
}
|
|
6837
|
-
return keep;
|
|
6028
|
+
this.#peerStates = new PeerStateView(Object.fromEntries(Object.entries(this.#peerStates).filter(([, state]) => {
|
|
6029
|
+
return state.lastActiveAt >= threshold;
|
|
6838
6030
|
})));
|
|
6839
|
-
return pruned;
|
|
6840
6031
|
}
|
|
6841
6032
|
/**
|
|
6842
6033
|
* Get a snapshot of the current peer states
|
|
@@ -6859,6 +6050,8 @@ class PeerPresenceInfo {
|
|
|
6859
6050
|
*/
|
|
6860
6051
|
class Presence extends EventEmitter {
|
|
6861
6052
|
#handle;
|
|
6053
|
+
deviceId;
|
|
6054
|
+
userId;
|
|
6862
6055
|
#peers;
|
|
6863
6056
|
#localState;
|
|
6864
6057
|
#heartbeatMs;
|
|
@@ -6873,11 +6066,13 @@ class Presence extends EventEmitter {
|
|
|
6873
6066
|
* @param config see {@link PresenceConfig}
|
|
6874
6067
|
* @returns
|
|
6875
6068
|
*/
|
|
6876
|
-
constructor({ handle }) {
|
|
6069
|
+
constructor({ handle, deviceId, userId, }) {
|
|
6877
6070
|
super();
|
|
6878
6071
|
this.#handle = handle;
|
|
6879
6072
|
this.#peers = new PeerPresenceInfo(DEFAULT_PEER_TTL_MS);
|
|
6880
6073
|
this.#localState = {};
|
|
6074
|
+
this.userId = userId;
|
|
6075
|
+
this.deviceId = deviceId;
|
|
6881
6076
|
}
|
|
6882
6077
|
/**
|
|
6883
6078
|
* Start listening to ephemeral messages on the handle, broadcast initial
|
|
@@ -6901,6 +6096,7 @@ class Presence extends EventEmitter {
|
|
|
6901
6096
|
return;
|
|
6902
6097
|
}
|
|
6903
6098
|
const message = envelope[PRESENCE_MESSAGE_MARKER];
|
|
6099
|
+
const { deviceId, userId } = message;
|
|
6904
6100
|
if (!this.#peers.has(peerId)) {
|
|
6905
6101
|
this.announce();
|
|
6906
6102
|
}
|
|
@@ -6916,11 +6112,15 @@ class Presence extends EventEmitter {
|
|
|
6916
6112
|
case "update":
|
|
6917
6113
|
this.#peers.update({
|
|
6918
6114
|
peerId,
|
|
6115
|
+
deviceId,
|
|
6116
|
+
userId,
|
|
6919
6117
|
value: { [message.channel]: message.value },
|
|
6920
6118
|
});
|
|
6921
6119
|
this.emit("update", {
|
|
6922
6120
|
type: "update",
|
|
6923
6121
|
peerId,
|
|
6122
|
+
deviceId,
|
|
6123
|
+
userId,
|
|
6924
6124
|
channel: message.channel,
|
|
6925
6125
|
value: message.value,
|
|
6926
6126
|
});
|
|
@@ -6928,11 +6128,15 @@ class Presence extends EventEmitter {
|
|
|
6928
6128
|
case "snapshot":
|
|
6929
6129
|
this.#peers.update({
|
|
6930
6130
|
peerId,
|
|
6131
|
+
deviceId,
|
|
6132
|
+
userId,
|
|
6931
6133
|
value: message.state,
|
|
6932
6134
|
});
|
|
6933
6135
|
this.emit("snapshot", {
|
|
6934
6136
|
type: "snapshot",
|
|
6935
6137
|
peerId,
|
|
6138
|
+
deviceId,
|
|
6139
|
+
userId,
|
|
6936
6140
|
state: message.state,
|
|
6937
6141
|
});
|
|
6938
6142
|
break;
|
|
@@ -7027,6 +6231,8 @@ class Presence extends EventEmitter {
|
|
|
7027
6231
|
}
|
|
7028
6232
|
doBroadcast(type, extra) {
|
|
7029
6233
|
this.send({
|
|
6234
|
+
userId: this.userId,
|
|
6235
|
+
deviceId: this.deviceId,
|
|
7030
6236
|
type,
|
|
7031
6237
|
...extra,
|
|
7032
6238
|
});
|
|
@@ -7062,10 +6268,7 @@ class Presence extends EventEmitter {
|
|
|
7062
6268
|
// to minimize variance between peer expiration, since the heartbeat frequency
|
|
7063
6269
|
// is expected to be several times higher.
|
|
7064
6270
|
this.#pruningInterval = setInterval(() => {
|
|
7065
|
-
|
|
7066
|
-
if (pruned.length > 0) {
|
|
7067
|
-
this.emit("pruning", { pruned });
|
|
7068
|
-
}
|
|
6271
|
+
this.#peers.prune();
|
|
7069
6272
|
}, this.#heartbeatMs);
|
|
7070
6273
|
}
|
|
7071
6274
|
stopPruningPeers() {
|
|
@@ -7079,6 +6282,8 @@ class Presence extends EventEmitter {
|
|
|
7079
6282
|
|
|
7080
6283
|
function usePresence({
|
|
7081
6284
|
handle,
|
|
6285
|
+
userId,
|
|
6286
|
+
deviceId,
|
|
7082
6287
|
initialState,
|
|
7083
6288
|
heartbeatMs,
|
|
7084
6289
|
peerTtlMs
|
|
@@ -7090,7 +6295,9 @@ function usePresence({
|
|
|
7090
6295
|
peerTtlMs
|
|
7091
6296
|
});
|
|
7092
6297
|
const firstInitialState = useRef(initialState);
|
|
7093
|
-
const [presence] = useState(
|
|
6298
|
+
const [presence] = useState(
|
|
6299
|
+
() => new Presence({ handle, userId, deviceId })
|
|
6300
|
+
);
|
|
7094
6301
|
useEffect(() => {
|
|
7095
6302
|
presence.start({
|
|
7096
6303
|
initialState: firstInitialState.current,
|
|
@@ -7100,26 +6307,10 @@ function usePresence({
|
|
|
7100
6307
|
presence.on("snapshot", () => setPeerStates(presence.getPeerStates()));
|
|
7101
6308
|
presence.on("update", () => setPeerStates(presence.getPeerStates()));
|
|
7102
6309
|
presence.on("goodbye", () => setPeerStates(presence.getPeerStates()));
|
|
7103
|
-
presence.on("pruning", () => setPeerStates(presence.getPeerStates()));
|
|
7104
6310
|
return () => {
|
|
7105
6311
|
presence.stop();
|
|
7106
6312
|
};
|
|
7107
|
-
}, [presence, firstInitialState, firstOpts]);
|
|
7108
|
-
const start = useCallback(
|
|
7109
|
-
(config) => {
|
|
7110
|
-
const initialState2 = config?.initialState ?? presence.getLocalState();
|
|
7111
|
-
const opts = {
|
|
7112
|
-
...firstOpts.current,
|
|
7113
|
-
...config,
|
|
7114
|
-
initialState: initialState2
|
|
7115
|
-
};
|
|
7116
|
-
presence.start(opts);
|
|
7117
|
-
},
|
|
7118
|
-
[presence, firstOpts]
|
|
7119
|
-
);
|
|
7120
|
-
const stop = useCallback(() => {
|
|
7121
|
-
presence.stop();
|
|
7122
|
-
}, [presence]);
|
|
6313
|
+
}, [presence, userId, deviceId, firstInitialState, firstOpts]);
|
|
7123
6314
|
const update = useCallback(
|
|
7124
6315
|
(channel, msg) => {
|
|
7125
6316
|
presence.broadcast(channel, msg);
|
|
@@ -7131,9 +6322,7 @@ function usePresence({
|
|
|
7131
6322
|
return {
|
|
7132
6323
|
peerStates,
|
|
7133
6324
|
localState,
|
|
7134
|
-
update
|
|
7135
|
-
start,
|
|
7136
|
-
stop
|
|
6325
|
+
update
|
|
7137
6326
|
};
|
|
7138
6327
|
}
|
|
7139
6328
|
|