@colyseus/schema 5.0.9 → 5.0.10
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 +87 -51
- package/build/codegen/cli.cjs +13 -5
- package/build/codegen/cli.cjs.map +1 -1
- package/build/encoder/ChangeTree.d.ts +10 -2
- package/build/index.cjs +163 -47
- package/build/index.cjs.map +1 -1
- package/build/index.js +163 -47
- package/build/index.mjs +163 -47
- package/build/index.mjs.map +1 -1
- package/build/types/custom/ArraySchema.d.ts +9 -1
- package/build/types/custom/MapSchema.d.ts +18 -1
- package/package.json +9 -3
- package/src/codegen/cli.ts +4 -4
- package/src/codegen/parser.ts +10 -1
- package/src/decoder/DecodeOperation.ts +23 -7
- package/src/encoder/ChangeTree.ts +18 -10
- package/src/encoder/EncodeOperation.ts +14 -1
- package/src/types/custom/ArraySchema.ts +77 -30
- package/src/types/custom/MapSchema.ts +34 -1
|
@@ -189,7 +189,16 @@ export declare class ChangeTree<T extends Ref = any> implements ChangeRecorder {
|
|
|
189
189
|
* collection/field, which `Root.remove` does before this runs.
|
|
190
190
|
*/
|
|
191
191
|
recycle(): void;
|
|
192
|
-
|
|
192
|
+
/**
|
|
193
|
+
* ArraySchema#unshift(): re-key pending ops on both channels by
|
|
194
|
+
* `+count`, then record ADDs for the new items at indexes 0..count-1.
|
|
195
|
+
*
|
|
196
|
+
* The rebuilt map's insertion order IS the wire order: new ADDs first
|
|
197
|
+
* (ascending — the decoder splice-inserts each one, which only works
|
|
198
|
+
* lowest-index-first), then prior ops in their original relative order
|
|
199
|
+
* at their shifted positions. See ArraySchema#$setAt.
|
|
200
|
+
*/
|
|
201
|
+
unshift(count: number): void;
|
|
193
202
|
setRoot(root: Root): void;
|
|
194
203
|
setParent(parent: Ref, root?: Root, parentIndex?: number): void;
|
|
195
204
|
forEachChild(cb: (change: ChangeTree, at: any) => void): void;
|
|
@@ -214,7 +223,6 @@ export declare class ChangeTree<T extends Ref = any> implements ChangeRecorder {
|
|
|
214
223
|
private _routeAndRecord;
|
|
215
224
|
change(index: number, operation?: OPERATION): void;
|
|
216
225
|
indexedOperation(index: number, operation: OPERATION): void;
|
|
217
|
-
shiftChangeIndexes(shiftIndex: number): void;
|
|
218
226
|
getChange(index: number): OPERATION;
|
|
219
227
|
pause(): void;
|
|
220
228
|
resume(): void;
|
package/build/index.cjs
CHANGED
|
@@ -2950,14 +2950,31 @@ class ChangeTree {
|
|
|
2950
2950
|
this.tagViews = undefined;
|
|
2951
2951
|
this.subscribedViews = undefined;
|
|
2952
2952
|
}
|
|
2953
|
-
|
|
2953
|
+
/**
|
|
2954
|
+
* ArraySchema#unshift(): re-key pending ops on both channels by
|
|
2955
|
+
* `+count`, then record ADDs for the new items at indexes 0..count-1.
|
|
2956
|
+
*
|
|
2957
|
+
* The rebuilt map's insertion order IS the wire order: new ADDs first
|
|
2958
|
+
* (ascending — the decoder splice-inserts each one, which only works
|
|
2959
|
+
* lowest-index-first), then prior ops in their original relative order
|
|
2960
|
+
* at their shifted positions. See ArraySchema#$setAt.
|
|
2961
|
+
*/
|
|
2962
|
+
unshift(count) {
|
|
2954
2963
|
if (this._isSchema)
|
|
2955
|
-
throw new Error("ChangeTree (Schema):
|
|
2964
|
+
throw new Error("ChangeTree (Schema): unshift is not supported");
|
|
2956
2965
|
const src = this.collDirty;
|
|
2957
2966
|
const dst = new Map();
|
|
2967
|
+
const track = !this.paused && !this.isStatic;
|
|
2968
|
+
if (track) {
|
|
2969
|
+
for (let i = 0; i < count; i++)
|
|
2970
|
+
dst.set(i, exports.OPERATION.ADD);
|
|
2971
|
+
}
|
|
2958
2972
|
for (const [idx, val] of src)
|
|
2959
|
-
dst.set(idx +
|
|
2973
|
+
dst.set(idx + count, val);
|
|
2960
2974
|
this.collDirty = dst;
|
|
2975
|
+
this.unreliableRecorder?.shift(count);
|
|
2976
|
+
if (track)
|
|
2977
|
+
this.root?.enqueueChangeTree(this);
|
|
2961
2978
|
}
|
|
2962
2979
|
// Tree attachment + child iteration — see ./changeTree/treeAttachment.ts.
|
|
2963
2980
|
setRoot(root) { setRoot(this, root); }
|
|
@@ -3026,12 +3043,6 @@ class ChangeTree {
|
|
|
3026
3043
|
indexedOperation(index, operation) {
|
|
3027
3044
|
this._routeAndRecord(index, operation, true);
|
|
3028
3045
|
}
|
|
3029
|
-
// ArraySchema#unshift(): apply shift to both channels.
|
|
3030
|
-
// Unreliable recorder on an array is always a CollectionChangeRecorder.
|
|
3031
|
-
shiftChangeIndexes(shiftIndex) {
|
|
3032
|
-
this.shift(shiftIndex);
|
|
3033
|
-
this.unreliableRecorder?.shift(shiftIndex);
|
|
3034
|
-
}
|
|
3035
3046
|
getChange(index) {
|
|
3036
3047
|
return this.operationAt(index);
|
|
3037
3048
|
}
|
|
@@ -3372,7 +3383,8 @@ const encodeArray = function (encoder, bytes, changeTree, field, operation, it,
|
|
|
3372
3383
|
// ArraySchema stores its per-instance child type at `$childType`.
|
|
3373
3384
|
// This encoder is array-only — there's no Schema fallback to consider.
|
|
3374
3385
|
const type = ref[$childType];
|
|
3375
|
-
const
|
|
3386
|
+
const isSchemaChild = typeof type !== "string";
|
|
3387
|
+
const useOperationByRefId = hasView && changeTree.isFiltered && isSchemaChild;
|
|
3376
3388
|
let refOrIndex;
|
|
3377
3389
|
if (useOperationByRefId) {
|
|
3378
3390
|
const item = ref.tmpItems[field];
|
|
@@ -3388,6 +3400,20 @@ const encodeArray = function (encoder, bytes, changeTree, field, operation, it,
|
|
|
3388
3400
|
operation = exports.OPERATION.ADD_BY_REFID;
|
|
3389
3401
|
}
|
|
3390
3402
|
}
|
|
3403
|
+
else if (operation === exports.OPERATION.DELETE && isSchemaChild) {
|
|
3404
|
+
//
|
|
3405
|
+
// DELETE by identity: idempotent, so a stale positional DELETE
|
|
3406
|
+
// (pending in the shared queue when a client bootstraps via
|
|
3407
|
+
// encodeAll) can't corrupt that client — its snapshot no longer
|
|
3408
|
+
// holds the item, the refId is unknown, and the decoder skips.
|
|
3409
|
+
//
|
|
3410
|
+
const item = ref.tmpItems[field];
|
|
3411
|
+
if (!item) {
|
|
3412
|
+
return;
|
|
3413
|
+
}
|
|
3414
|
+
refOrIndex = item[$refId];
|
|
3415
|
+
operation = exports.OPERATION.DELETE_BY_REFID;
|
|
3416
|
+
}
|
|
3391
3417
|
else {
|
|
3392
3418
|
refOrIndex = field;
|
|
3393
3419
|
}
|
|
@@ -3779,7 +3805,10 @@ const decodeKeyValueOperation = function (decoder, bytes, it, ref, allChanges) {
|
|
|
3779
3805
|
tgt.$items.set(dynamicIndex, value);
|
|
3780
3806
|
break;
|
|
3781
3807
|
case CollectionKind.Array:
|
|
3782
|
-
|
|
3808
|
+
// resync snapshot ADDs are positional overwrites, not inserts
|
|
3809
|
+
tgt.$setAt(index, value, (decoder.resyncVisited !== null && operation === exports.OPERATION.ADD)
|
|
3810
|
+
? exports.OPERATION.REPLACE
|
|
3811
|
+
: operation);
|
|
3783
3812
|
break;
|
|
3784
3813
|
// SetSchema / CollectionSchema / StreamSchema — use the wire-
|
|
3785
3814
|
// index we decoded above so server/client `$items` stay in sync
|
|
@@ -3846,9 +3875,13 @@ const decodeArray = function (decoder, bytes, it, ref, allChanges) {
|
|
|
3846
3875
|
return;
|
|
3847
3876
|
}
|
|
3848
3877
|
else if (operation === exports.OPERATION.DELETE_BY_REFID) {
|
|
3849
|
-
// TODO: refactor here, try to follow same flow as below
|
|
3850
3878
|
const refId = decode.number(bytes, it);
|
|
3851
3879
|
const previousValue = decoder.root.refs.get(refId);
|
|
3880
|
+
// Stale DELETE — refId unknown to this decoder (e.g. it
|
|
3881
|
+
// bootstrapped via encodeAll after the item was already removed).
|
|
3882
|
+
if (previousValue === undefined) {
|
|
3883
|
+
return;
|
|
3884
|
+
}
|
|
3852
3885
|
// Decrement the removed child's ref-count so it can be garbage
|
|
3853
3886
|
// collected — this refId-based branch returns early and never reaches
|
|
3854
3887
|
// decodeValue(), so it must do the same DELETE bookkeeping itself.
|
|
@@ -3856,10 +3889,13 @@ const decodeArray = function (decoder, bytes, it, ref, allChanges) {
|
|
|
3856
3889
|
// different type → "field not defined" / "definition mismatch"
|
|
3857
3890
|
// (surfaces under StateView when a filtered ArraySchema element is
|
|
3858
3891
|
// spliced while its parent moves through view membership churn).
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
}
|
|
3892
|
+
// Must run even when the item is absent from THIS array (view churn).
|
|
3893
|
+
decoder.root.removeRef(refId);
|
|
3862
3894
|
index = tgt.findIndex((value) => value === previousValue);
|
|
3895
|
+
// Item not present in this decoder's array — nothing to remove locally.
|
|
3896
|
+
if (index === -1) {
|
|
3897
|
+
return;
|
|
3898
|
+
}
|
|
3863
3899
|
tgt[$deleteByIndex](index);
|
|
3864
3900
|
allChanges?.push({
|
|
3865
3901
|
ref,
|
|
@@ -3900,9 +3936,12 @@ const decodeArray = function (decoder, bytes, it, ref, allChanges) {
|
|
|
3900
3936
|
resyncTouchEntry(decoder, ref, operation, index, previousValue, value, allChanges);
|
|
3901
3937
|
}
|
|
3902
3938
|
if (value !== null && value !== undefined &&
|
|
3903
|
-
value !== previousValue // avoid setting same value twice (
|
|
3939
|
+
value !== previousValue // avoid setting same value twice (an ADD at an occupied index would splice-insert)
|
|
3904
3940
|
) {
|
|
3905
|
-
|
|
3941
|
+
// resync snapshot ADDs are positional overwrites, not inserts
|
|
3942
|
+
tgt.$setAt(index, value, (decoder.resyncVisited !== null && operation === exports.OPERATION.ADD)
|
|
3943
|
+
? exports.OPERATION.REPLACE
|
|
3944
|
+
: operation);
|
|
3906
3945
|
}
|
|
3907
3946
|
// add change
|
|
3908
3947
|
if (previousValue !== value) {
|
|
@@ -3959,30 +3998,35 @@ const ARRAY_PROXY_HANDLER = {
|
|
|
3959
3998
|
obj.$deleteAt(key);
|
|
3960
3999
|
}
|
|
3961
4000
|
else {
|
|
4001
|
+
// wire slot the write was recorded at; undefined = nothing
|
|
4002
|
+
// recorded (same value / skipped) — must NOT touch tmpItems
|
|
4003
|
+
// then, or a same-tick shifted layout gets clobbered.
|
|
4004
|
+
let wireIndex;
|
|
3962
4005
|
if (setValue[$changes]) {
|
|
3963
4006
|
assertInstanceType(setValue, obj[$childType], obj, key);
|
|
3964
4007
|
const previousValue = obj.items[key];
|
|
3965
4008
|
if (!obj.isMovingItems) {
|
|
3966
|
-
obj.$changeAt(Number(key), setValue);
|
|
4009
|
+
wireIndex = obj.$changeAt(Number(key), setValue);
|
|
3967
4010
|
}
|
|
3968
4011
|
else {
|
|
4012
|
+
wireIndex = obj.$wireIndex(Number(key));
|
|
3969
4013
|
if (previousValue !== undefined) {
|
|
3970
4014
|
if (setValue[$changes].isNew) {
|
|
3971
|
-
obj[$changes].indexedOperation(
|
|
4015
|
+
obj[$changes].indexedOperation(wireIndex, exports.OPERATION.MOVE_AND_ADD);
|
|
3972
4016
|
}
|
|
3973
4017
|
else {
|
|
3974
|
-
if ((obj[$changes].getChange(
|
|
3975
|
-
obj[$changes].indexedOperation(
|
|
4018
|
+
if ((obj[$changes].getChange(wireIndex) & exports.OPERATION.DELETE) === exports.OPERATION.DELETE) {
|
|
4019
|
+
obj[$changes].indexedOperation(wireIndex, exports.OPERATION.DELETE_AND_MOVE);
|
|
3976
4020
|
}
|
|
3977
4021
|
else {
|
|
3978
|
-
obj[$changes].indexedOperation(
|
|
4022
|
+
obj[$changes].indexedOperation(wireIndex, exports.OPERATION.MOVE);
|
|
3979
4023
|
}
|
|
3980
4024
|
}
|
|
3981
4025
|
}
|
|
3982
4026
|
else if (setValue[$changes].isNew) {
|
|
3983
|
-
obj[$changes].indexedOperation(
|
|
4027
|
+
obj[$changes].indexedOperation(wireIndex, exports.OPERATION.ADD);
|
|
3984
4028
|
}
|
|
3985
|
-
setValue[$changes].setParent(obj, obj[$changes].root,
|
|
4029
|
+
setValue[$changes].setParent(obj, obj[$changes].root, wireIndex);
|
|
3986
4030
|
}
|
|
3987
4031
|
if (previousValue !== undefined) {
|
|
3988
4032
|
// remove root reference from previous value
|
|
@@ -3990,10 +4034,12 @@ const ARRAY_PROXY_HANDLER = {
|
|
|
3990
4034
|
}
|
|
3991
4035
|
}
|
|
3992
4036
|
else {
|
|
3993
|
-
obj.$changeAt(Number(key), setValue);
|
|
4037
|
+
wireIndex = obj.$changeAt(Number(key), setValue);
|
|
3994
4038
|
}
|
|
3995
4039
|
obj.items[key] = setValue;
|
|
3996
|
-
|
|
4040
|
+
if (wireIndex !== undefined) {
|
|
4041
|
+
obj.tmpItems[wireIndex] = setValue;
|
|
4042
|
+
}
|
|
3997
4043
|
}
|
|
3998
4044
|
return true;
|
|
3999
4045
|
}
|
|
@@ -4176,40 +4222,68 @@ class ArraySchema {
|
|
|
4176
4222
|
index += this.length;
|
|
4177
4223
|
return this.items[index];
|
|
4178
4224
|
}
|
|
4179
|
-
|
|
4225
|
+
/**
|
|
4226
|
+
* items-index → wire (tmpItems) index. Identity while no deletions are
|
|
4227
|
+
* staged this tick; otherwise maps to the index-th live (non-deleted)
|
|
4228
|
+
* tmpItems slot — the same live-index walk `splice()` uses. Without the
|
|
4229
|
+
* translation, index writes recorded after a same-tick `shift()`/`splice()`
|
|
4230
|
+
* land on the wrong wire slots.
|
|
4231
|
+
*/
|
|
4232
|
+
$wireIndex(index) {
|
|
4233
|
+
const deletedIndexes = this.deletedIndexes;
|
|
4234
|
+
if (deletedIndexes.length === 0) {
|
|
4235
|
+
return index;
|
|
4236
|
+
}
|
|
4237
|
+
const tmpItems = this.tmpItems;
|
|
4238
|
+
let live = 0;
|
|
4239
|
+
for (let i = 0; i < tmpItems.length; i++) {
|
|
4240
|
+
if (deletedIndexes[i] !== true) {
|
|
4241
|
+
if (live === index) {
|
|
4242
|
+
return i;
|
|
4243
|
+
}
|
|
4244
|
+
live++;
|
|
4245
|
+
}
|
|
4246
|
+
}
|
|
4247
|
+
// beyond the live range: appends land after the staged tmpItems tail
|
|
4248
|
+
return tmpItems.length + (index - live);
|
|
4249
|
+
}
|
|
4250
|
+
// encoding only. Returns the wire index the change was recorded at
|
|
4251
|
+
// (undefined when nothing was recorded).
|
|
4180
4252
|
$changeAt(index, value) {
|
|
4181
4253
|
if (value === undefined || value === null) {
|
|
4182
|
-
console.error("ArraySchema items cannot be null nor undefined; Use `
|
|
4183
|
-
return;
|
|
4254
|
+
console.error("ArraySchema items cannot be null nor undefined; Use `splice(index, 1)` instead.");
|
|
4255
|
+
return undefined;
|
|
4184
4256
|
}
|
|
4185
4257
|
// skip if the value is the same as cached.
|
|
4186
4258
|
if (this.items[index] === value) {
|
|
4187
|
-
return;
|
|
4259
|
+
return undefined;
|
|
4188
4260
|
}
|
|
4189
4261
|
const operation = (this.items[index] !== undefined)
|
|
4190
4262
|
? typeof (value) === "object"
|
|
4191
4263
|
? exports.OPERATION.DELETE_AND_ADD // schema child
|
|
4192
4264
|
: exports.OPERATION.REPLACE // primitive
|
|
4193
4265
|
: exports.OPERATION.ADD;
|
|
4266
|
+
const wireIndex = this.$wireIndex(index);
|
|
4194
4267
|
const changeTree = this[$changes];
|
|
4195
|
-
changeTree.change(
|
|
4268
|
+
changeTree.change(wireIndex, operation);
|
|
4196
4269
|
//
|
|
4197
4270
|
// set value's parent after the value is set
|
|
4198
4271
|
// (to avoid encoding "refId" operations before parent's "ADD" operation)
|
|
4199
4272
|
//
|
|
4200
|
-
value[$changes]?.setParent(this, changeTree.root,
|
|
4273
|
+
value[$changes]?.setParent(this, changeTree.root, wireIndex);
|
|
4274
|
+
return wireIndex;
|
|
4201
4275
|
}
|
|
4202
4276
|
// encoding only
|
|
4203
4277
|
$deleteAt(index, operation) {
|
|
4204
|
-
this[$changes].delete(index, operation);
|
|
4278
|
+
this[$changes].delete(this.$wireIndex(index), operation);
|
|
4205
4279
|
}
|
|
4206
4280
|
// decoding only
|
|
4207
4281
|
$setAt(index, value, operation) {
|
|
4208
|
-
if (
|
|
4209
|
-
operation === exports.OPERATION.ADD &&
|
|
4282
|
+
if (operation === exports.OPERATION.ADD &&
|
|
4210
4283
|
this.items[index] !== undefined) {
|
|
4211
|
-
//
|
|
4212
|
-
|
|
4284
|
+
// ADD at an occupied index = insert (unshift / splice-insert):
|
|
4285
|
+
// shift existing items up instead of overwriting.
|
|
4286
|
+
this.items.splice(index, 0, value);
|
|
4213
4287
|
}
|
|
4214
4288
|
else if (operation === exports.OPERATION.DELETE_AND_MOVE) {
|
|
4215
4289
|
this.items.splice(index, 1);
|
|
@@ -4296,10 +4370,16 @@ class ArraySchema {
|
|
|
4296
4370
|
return undefined;
|
|
4297
4371
|
}
|
|
4298
4372
|
const changeTree = self[$changes];
|
|
4299
|
-
|
|
4300
|
-
|
|
4373
|
+
// items[0] ≡ first live (non-deleted) tmpItems slot. Value-based
|
|
4374
|
+
// findIndex is unsafe here: same-tick index writes can duplicate a
|
|
4375
|
+
// value across tmp slots and resolve the wrong one.
|
|
4376
|
+
const deletedIndexes = self.deletedIndexes;
|
|
4377
|
+
let index = 0;
|
|
4378
|
+
while (deletedIndexes[index] === true) {
|
|
4379
|
+
index++;
|
|
4380
|
+
}
|
|
4301
4381
|
changeTree.delete(index, exports.OPERATION.DELETE);
|
|
4302
|
-
|
|
4382
|
+
deletedIndexes[index] = true;
|
|
4303
4383
|
return items.shift();
|
|
4304
4384
|
}
|
|
4305
4385
|
/**
|
|
@@ -4397,13 +4477,18 @@ class ArraySchema {
|
|
|
4397
4477
|
unshift(...items) {
|
|
4398
4478
|
const self = this[$proxyTarget];
|
|
4399
4479
|
const changeTree = self[$changes];
|
|
4400
|
-
//
|
|
4401
|
-
//
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
items.
|
|
4405
|
-
changeTree.
|
|
4406
|
-
}
|
|
4480
|
+
// single recorder op: shifts pending indexes up and records the new
|
|
4481
|
+
// ADDs lowest-first (the decoder splice-inserts in ascending order).
|
|
4482
|
+
changeTree.unshift(items.length);
|
|
4483
|
+
// attach ref-type items — parent set AFTER recording, as in $changeAt
|
|
4484
|
+
for (let i = 0; i < items.length; i++) {
|
|
4485
|
+
items[i]?.[$changes]?.setParent(this, changeTree.root, i);
|
|
4486
|
+
}
|
|
4487
|
+
// keep staged-delete flags aligned with the prepended tmp slots
|
|
4488
|
+
const deletedIndexes = self.deletedIndexes;
|
|
4489
|
+
if (deletedIndexes.length > 0) {
|
|
4490
|
+
deletedIndexes.unshift(...new Array(items.length).fill(false));
|
|
4491
|
+
}
|
|
4407
4492
|
self.tmpItems.unshift(...items);
|
|
4408
4493
|
return self.items.unshift(...items);
|
|
4409
4494
|
}
|
|
@@ -5022,6 +5107,37 @@ class MapSchema {
|
|
|
5022
5107
|
get(key) {
|
|
5023
5108
|
return this.$items.get(key);
|
|
5024
5109
|
}
|
|
5110
|
+
/**
|
|
5111
|
+
* Returns the value for `key` if present. Otherwise inserts `defaultValue`
|
|
5112
|
+
* (tracked as an ADD change, like `set()`) and returns it.
|
|
5113
|
+
*
|
|
5114
|
+
* Mirrors `Map.prototype.getOrInsert` (TC39 "upsert" proposal, typed in
|
|
5115
|
+
* TypeScript 6's standard library).
|
|
5116
|
+
*/
|
|
5117
|
+
getOrInsert(key, defaultValue) {
|
|
5118
|
+
if (this.$items.has(key)) {
|
|
5119
|
+
return this.$items.get(key);
|
|
5120
|
+
}
|
|
5121
|
+
this.set(key, defaultValue);
|
|
5122
|
+
return defaultValue;
|
|
5123
|
+
}
|
|
5124
|
+
/**
|
|
5125
|
+
* Returns the value for `key` if present. Otherwise computes a value via
|
|
5126
|
+
* `callbackfn(key)`, inserts it (tracked as an ADD change, like `set()`)
|
|
5127
|
+
* and returns it. The callback is only invoked when the key is missing.
|
|
5128
|
+
*
|
|
5129
|
+
* Mirrors `Map.prototype.getOrInsertComputed` (TC39 "upsert" proposal,
|
|
5130
|
+
* typed in TypeScript 6's standard library).
|
|
5131
|
+
*/
|
|
5132
|
+
getOrInsertComputed(key, callbackfn) {
|
|
5133
|
+
if (this.$items.has(key)) {
|
|
5134
|
+
return this.$items.get(key);
|
|
5135
|
+
}
|
|
5136
|
+
const value = callbackfn(key);
|
|
5137
|
+
// per spec: overwrites even if callbackfn itself inserted `key`
|
|
5138
|
+
this.set(key, value);
|
|
5139
|
+
return value;
|
|
5140
|
+
}
|
|
5025
5141
|
delete(key) {
|
|
5026
5142
|
if (!this.$items.has(key)) {
|
|
5027
5143
|
return false;
|