jazz-tools 0.8.14 → 0.8.16

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. package/CHANGELOG.md +281 -268
  2. package/dist/native/coValues/account.js +3 -6
  3. package/dist/native/coValues/account.js.map +1 -1
  4. package/dist/native/coValues/coList.js +3 -7
  5. package/dist/native/coValues/coList.js.map +1 -1
  6. package/dist/native/coValues/coMap.js +8 -14
  7. package/dist/native/coValues/coMap.js.map +1 -1
  8. package/dist/native/coValues/coStream.js +7 -12
  9. package/dist/native/coValues/coStream.js.map +1 -1
  10. package/dist/native/coValues/deepLoading.js +6 -3
  11. package/dist/native/coValues/deepLoading.js.map +1 -1
  12. package/dist/native/coValues/extensions/imageDef.js.map +1 -1
  13. package/dist/native/coValues/group.js +3 -6
  14. package/dist/native/coValues/group.js.map +1 -1
  15. package/dist/native/coValues/interfaces.js +4 -3
  16. package/dist/native/coValues/interfaces.js.map +1 -1
  17. package/dist/native/exports.js +3 -9
  18. package/dist/native/exports.js.map +1 -1
  19. package/dist/native/implementation/createContext.js +7 -0
  20. package/dist/native/implementation/createContext.js.map +1 -1
  21. package/dist/native/implementation/devtoolsFormatters.js +5 -25
  22. package/dist/native/implementation/devtoolsFormatters.js.map +1 -1
  23. package/dist/native/implementation/refs.js +1 -2
  24. package/dist/native/implementation/refs.js.map +1 -1
  25. package/dist/native/implementation/schema.js +1 -1
  26. package/dist/native/implementation/schema.js.map +1 -1
  27. package/dist/native/implementation/subscriptionScope.js +2 -4
  28. package/dist/native/implementation/subscriptionScope.js.map +1 -1
  29. package/dist/native/index.native.js +1 -1
  30. package/dist/native/index.native.js.map +1 -1
  31. package/dist/native/lib/cache.js.map +1 -1
  32. package/dist/native/lib/cache.test.js +1 -1
  33. package/dist/native/lib/cache.test.js.map +1 -1
  34. package/dist/web/coValues/account.js +3 -6
  35. package/dist/web/coValues/account.js.map +1 -1
  36. package/dist/web/coValues/coList.js +3 -7
  37. package/dist/web/coValues/coList.js.map +1 -1
  38. package/dist/web/coValues/coMap.js +8 -14
  39. package/dist/web/coValues/coMap.js.map +1 -1
  40. package/dist/web/coValues/coStream.js +7 -12
  41. package/dist/web/coValues/coStream.js.map +1 -1
  42. package/dist/web/coValues/deepLoading.js +6 -3
  43. package/dist/web/coValues/deepLoading.js.map +1 -1
  44. package/dist/web/coValues/extensions/imageDef.js.map +1 -1
  45. package/dist/web/coValues/group.js +3 -6
  46. package/dist/web/coValues/group.js.map +1 -1
  47. package/dist/web/coValues/interfaces.js +4 -3
  48. package/dist/web/coValues/interfaces.js.map +1 -1
  49. package/dist/web/exports.js +3 -9
  50. package/dist/web/exports.js.map +1 -1
  51. package/dist/web/implementation/createContext.js +7 -0
  52. package/dist/web/implementation/createContext.js.map +1 -1
  53. package/dist/web/implementation/devtoolsFormatters.js +5 -25
  54. package/dist/web/implementation/devtoolsFormatters.js.map +1 -1
  55. package/dist/web/implementation/refs.js +1 -2
  56. package/dist/web/implementation/refs.js.map +1 -1
  57. package/dist/web/implementation/schema.js +1 -1
  58. package/dist/web/implementation/schema.js.map +1 -1
  59. package/dist/web/implementation/subscriptionScope.js +2 -4
  60. package/dist/web/implementation/subscriptionScope.js.map +1 -1
  61. package/dist/web/lib/cache.js.map +1 -1
  62. package/dist/web/lib/cache.test.js +1 -1
  63. package/dist/web/lib/cache.test.js.map +1 -1
  64. package/package.json +5 -9
  65. package/src/coValues/account.ts +330 -339
  66. package/src/coValues/coList.ts +474 -495
  67. package/src/coValues/coMap.ts +584 -604
  68. package/src/coValues/coStream.ts +624 -650
  69. package/src/coValues/deepLoading.ts +184 -200
  70. package/src/coValues/extensions/imageDef.ts +44 -44
  71. package/src/coValues/group.ts +196 -210
  72. package/src/coValues/interfaces.ts +197 -199
  73. package/src/exports.ts +38 -25
  74. package/src/implementation/createContext.ts +210 -204
  75. package/src/implementation/devtoolsFormatters.ts +80 -100
  76. package/src/implementation/refs.ts +127 -139
  77. package/src/implementation/schema.ts +124 -128
  78. package/src/implementation/subscriptionScope.ts +111 -121
  79. package/src/index.native.ts +3 -3
  80. package/src/lib/cache.test.ts +48 -48
  81. package/src/lib/cache.ts +9 -9
  82. package/src/tests/coList.test.ts +264 -283
  83. package/src/tests/coMap.test.ts +741 -761
  84. package/src/tests/coStream.test.ts +405 -438
  85. package/src/tests/deepLoading.test.ts +251 -256
  86. package/src/tests/groupsAndAccounts.test.ts +70 -74
  87. package/src/tests/schema.test.ts +198 -198
  88. package/src/tests/subscribe.test.ts +312 -299
  89. package/tsconfig.json +2 -4
  90. package/tsconfig.native.json +4 -10
  91. package/tsconfig.web.json +4 -10
  92. package/.eslintrc.cjs +0 -24
  93. package/.prettierrc.js +0 -9
@@ -1,51 +1,51 @@
1
1
  import {
2
- cojsonInternals,
3
- type CoValueUniqueness,
4
- type JsonValue,
5
- type RawCoMap,
2
+ type CoValueUniqueness,
3
+ type JsonValue,
4
+ type RawCoMap,
5
+ cojsonInternals,
6
6
  } from "cojson";
7
7
  import type {
8
- CoValue,
9
- Schema,
10
- Group,
11
- ID,
12
- RefEncoded,
13
- IfCo,
14
- RefIfCoValue,
15
- DepthsIn,
16
- DeeplyLoaded,
17
- CoValueClass,
18
- AnonymousJazzAgent,
19
- co,
8
+ AnonymousJazzAgent,
9
+ CoValue,
10
+ CoValueClass,
11
+ DeeplyLoaded,
12
+ DepthsIn,
13
+ Group,
14
+ ID,
15
+ IfCo,
16
+ RefEncoded,
17
+ RefIfCoValue,
18
+ Schema,
19
+ co,
20
20
  } from "../internal.js";
21
21
  import {
22
- Account,
23
- CoValueBase,
24
- Ref,
25
- SchemaInit,
26
- inspect,
27
- makeRefs,
28
- subscriptionsScopes,
29
- ItemsSym,
30
- isRefEncoded,
31
- loadCoValue,
32
- subscribeToCoValue,
33
- ensureCoValueLoaded,
34
- subscribeToExistingCoValue,
22
+ Account,
23
+ CoValueBase,
24
+ ItemsSym,
25
+ Ref,
26
+ SchemaInit,
27
+ ensureCoValueLoaded,
28
+ inspect,
29
+ isRefEncoded,
30
+ loadCoValue,
31
+ makeRefs,
32
+ subscribeToCoValue,
33
+ subscribeToExistingCoValue,
34
+ subscriptionsScopes,
35
35
  } from "../internal.js";
36
36
 
37
37
  type CoMapEdit<V> = {
38
- value?: V;
39
- ref?: RefIfCoValue<V>;
40
- by?: Account;
41
- madeAt: Date;
38
+ value?: V;
39
+ ref?: RefIfCoValue<V>;
40
+ by?: Account;
41
+ madeAt: Date;
42
42
  };
43
43
 
44
44
  export type Simplify<A> = {
45
- [K in keyof A]: A[K];
45
+ [K in keyof A]: A[K];
46
46
  } extends infer B
47
- ? B
48
- : never;
47
+ ? B
48
+ : never;
49
49
 
50
50
  /**
51
51
  * CoMaps are collaborative versions of plain objects, mapping string-like keys to values.
@@ -81,451 +81,436 @@ export type Simplify<A> = {
81
81
  * @category CoValues
82
82
  * */
83
83
  export class CoMap extends CoValueBase implements CoValue {
84
- /**
85
- * The ID of this `CoMap`
86
- * @category Content */
87
- declare id: ID<this>;
88
- /** @category Type Helpers */
89
- declare _type: "CoMap";
90
- static {
91
- this.prototype._type = "CoMap";
92
- }
93
- /** @category Internals */
94
- declare _raw: RawCoMap;
95
-
96
- /** @internal */
97
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
98
- static _schema: any;
99
- /** @internal */
100
- get _schema() {
101
- return (this.constructor as typeof CoMap)._schema as {
102
- [key: string]: Schema;
103
- } & { [ItemsSym]?: Schema };
104
- }
105
-
106
- /**
107
- * If property `prop` is a `co.ref(...)`, you can use `coMaps._refs.prop` to access
108
- * the `Ref` instead of the potentially loaded/null value.
109
- *
110
- * This allows you to always get the ID or load the value manually.
111
- *
112
- * @example
113
- * ```ts
114
- * person._refs.pet.id; // => ID<Animal>
115
- * person._refs.pet.value;
116
- * // => Animal | null
117
- * const pet = await person._refs.pet.load();
118
- * ```
119
- *
120
- * @category Content
121
- **/
122
- get _refs(): {
123
- [Key in CoKeys<this>]: IfCo<this[Key], RefIfCoValue<this[Key]>>;
124
- } {
125
- return makeRefs<CoKeys<this>>(
126
- (key) => this._raw.get(key as string) as unknown as ID<CoValue>,
127
- () => {
128
- const keys = this._raw.keys().filter((key) => {
129
- const schema =
130
- this._schema[key as keyof typeof this._schema] ||
131
- (this._schema[ItemsSym] as Schema | undefined);
132
- return schema && schema !== "json" && isRefEncoded(schema);
133
- }) as CoKeys<this>[];
134
-
135
- return keys;
136
- },
137
- this._loadedAs,
138
- (key) =>
139
- (this._schema[key] ||
140
- this._schema[ItemsSym]) as RefEncoded<CoValue>,
141
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
142
- ) as any;
143
- }
144
-
145
- /** @category Collaboration */
146
- get _edits() {
147
- return new Proxy(this, {
148
- get(target, key) {
149
- const rawEdit = target._raw.lastEditAt(key as string);
150
- if (!rawEdit) return undefined;
151
-
152
- const descriptor = target._schema[
153
- key as keyof typeof target._schema
154
- ] as Schema;
155
-
156
- return {
157
- value:
158
- descriptor === "json"
159
- ? rawEdit.value
160
- : "encoded" in descriptor
161
- ? descriptor.encoded.encode(rawEdit.value)
162
- : new Ref(
163
- rawEdit.value as ID<CoValue>,
164
- target._loadedAs,
165
- descriptor,
166
- ).accessFrom(
167
- target,
168
- "_edits." + key.toString() + ".value",
169
- ),
170
- ref:
171
- descriptor !== "json" && isRefEncoded(descriptor)
172
- ? new Ref(
173
- rawEdit.value as ID<CoValue>,
174
- target._loadedAs,
175
- descriptor,
176
- )
177
- : undefined,
178
- by:
179
- rawEdit.by &&
180
- new Ref<Account>(
181
- rawEdit.by as ID<Account>,
182
- target._loadedAs,
183
- {
184
- ref: Account,
185
- optional: false,
186
- },
187
- ).accessFrom(
188
- target,
189
- "_edits." + key.toString() + ".by",
190
- ),
191
- madeAt: rawEdit.at,
192
- };
193
- },
194
- }) as {
195
- [Key in CoKeys<this>]: IfCo<this[Key], CoMapEdit<this[Key]>>;
196
- };
197
- }
84
+ /**
85
+ * The ID of this `CoMap`
86
+ * @category Content */
87
+ declare id: ID<this>;
88
+ /** @category Type Helpers */
89
+ declare _type: "CoMap";
90
+ static {
91
+ this.prototype._type = "CoMap";
92
+ }
93
+ /** @category Internals */
94
+ declare _raw: RawCoMap;
95
+
96
+ /** @internal */
97
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
98
+ static _schema: any;
99
+ /** @internal */
100
+ get _schema() {
101
+ return (this.constructor as typeof CoMap)._schema as {
102
+ [key: string]: Schema;
103
+ } & { [ItemsSym]?: Schema };
104
+ }
105
+
106
+ /**
107
+ * If property `prop` is a `co.ref(...)`, you can use `coMaps._refs.prop` to access
108
+ * the `Ref` instead of the potentially loaded/null value.
109
+ *
110
+ * This allows you to always get the ID or load the value manually.
111
+ *
112
+ * @example
113
+ * ```ts
114
+ * person._refs.pet.id; // => ID<Animal>
115
+ * person._refs.pet.value;
116
+ * // => Animal | null
117
+ * const pet = await person._refs.pet.load();
118
+ * ```
119
+ *
120
+ * @category Content
121
+ **/
122
+ get _refs(): {
123
+ [Key in CoKeys<this>]: IfCo<this[Key], RefIfCoValue<this[Key]>>;
124
+ } {
125
+ return makeRefs<CoKeys<this>>(
126
+ (key) => this._raw.get(key as string) as unknown as ID<CoValue>,
127
+ () => {
128
+ const keys = this._raw.keys().filter((key) => {
129
+ const schema =
130
+ this._schema[key as keyof typeof this._schema] ||
131
+ (this._schema[ItemsSym] as Schema | undefined);
132
+ return schema && schema !== "json" && isRefEncoded(schema);
133
+ }) as CoKeys<this>[];
198
134
 
199
- /** @internal */
200
- constructor(
201
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
202
- options: { fromRaw: RawCoMap } | undefined,
203
- ) {
204
- super();
205
-
206
- if (options) {
207
- if ("fromRaw" in options) {
208
- Object.defineProperties(this, {
209
- id: {
210
- value: options.fromRaw.id as unknown as ID<this>,
211
- enumerable: false,
212
- },
213
- _raw: { value: options.fromRaw, enumerable: false },
214
- });
215
- } else {
216
- throw new Error("Invalid CoMap constructor arguments");
217
- }
218
- }
219
-
220
- return new Proxy(this, CoMapProxyHandler as ProxyHandler<this>);
221
- }
222
-
223
- /**
224
- * Create a new CoMap with the given initial values and owner.
225
- *
226
- * The owner (a Group or Account) determines access rights to the CoMap.
227
- *
228
- * The CoMap will immediately be persisted and synced to connected peers.
229
- *
230
- * @example
231
- * ```ts
232
- * const person = Person.create({
233
- * name: "Alice",
234
- * age: 42,
235
- * pet: cat,
236
- * }, { owner: friendGroup });
237
- * ```
238
- *
239
- * @category Creation
240
- **/
241
- static create<M extends CoMap>(
242
- this: CoValueClass<M>,
243
- init: Simplify<CoMapInit<M>>,
244
- options: {
245
- owner: Account | Group;
246
- unique?: CoValueUniqueness["uniqueness"];
247
- },
248
- ) {
249
- const instance = new this();
250
- const raw = instance.rawFromInit(
251
- init,
252
- options.owner,
253
- options.unique === undefined
254
- ? undefined
255
- : { uniqueness: options.unique },
256
- );
257
- Object.defineProperties(instance, {
258
- id: {
259
- value: raw.id,
260
- enumerable: false,
261
- },
262
- _raw: { value: raw, enumerable: false },
263
- });
264
- return instance;
265
- }
266
-
267
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
268
- toJSON(_key?: string, seenAbove?: ID<CoValue>[]): any[] {
269
- const jsonedFields = this._raw.keys().map((key) => {
270
- const tKey = key as CoKeys<this>;
271
- const descriptor = (this._schema[tKey] ||
272
- this._schema[ItemsSym]) as Schema;
273
-
274
- if (descriptor == "json" || "encode" in descriptor) {
275
- return [key, this._raw.get(key)];
276
- } else if (isRefEncoded(descriptor)) {
277
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
278
- if (seenAbove?.includes((this as any)[tKey]?.id)) {
279
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
280
- return [key, { _circular: (this as any)[tKey]?.id }];
281
- }
282
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
283
- const jsonedRef = (this as any)[tKey]?.toJSON(tKey, [
284
- ...(seenAbove || []),
285
- this.id,
286
- ]);
287
- return [key, jsonedRef];
288
- } else {
289
- return [key, undefined];
290
- }
291
- });
135
+ return keys;
136
+ },
137
+ this._loadedAs,
138
+ (key) =>
139
+ (this._schema[key] || this._schema[ItemsSym]) as RefEncoded<CoValue>,
140
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
141
+ ) as any;
142
+ }
143
+
144
+ /** @category Collaboration */
145
+ get _edits() {
146
+ return new Proxy(this, {
147
+ get(target, key) {
148
+ const rawEdit = target._raw.lastEditAt(key as string);
149
+ if (!rawEdit) return undefined;
150
+
151
+ const descriptor = target._schema[
152
+ key as keyof typeof target._schema
153
+ ] as Schema;
292
154
 
293
155
  return {
294
- id: this.id,
295
- _type: this._type,
296
- ...Object.fromEntries(jsonedFields),
156
+ value:
157
+ descriptor === "json"
158
+ ? rawEdit.value
159
+ : "encoded" in descriptor
160
+ ? descriptor.encoded.encode(rawEdit.value)
161
+ : new Ref(
162
+ rawEdit.value as ID<CoValue>,
163
+ target._loadedAs,
164
+ descriptor,
165
+ ).accessFrom(target, "_edits." + key.toString() + ".value"),
166
+ ref:
167
+ descriptor !== "json" && isRefEncoded(descriptor)
168
+ ? new Ref(
169
+ rawEdit.value as ID<CoValue>,
170
+ target._loadedAs,
171
+ descriptor,
172
+ )
173
+ : undefined,
174
+ by:
175
+ rawEdit.by &&
176
+ new Ref<Account>(rawEdit.by as ID<Account>, target._loadedAs, {
177
+ ref: Account,
178
+ optional: false,
179
+ }).accessFrom(target, "_edits." + key.toString() + ".by"),
180
+ madeAt: rawEdit.at,
297
181
  };
298
- }
299
-
300
- [inspect]() {
301
- return this.toJSON();
302
- }
303
-
304
- /** @internal */
182
+ },
183
+ }) as {
184
+ [Key in CoKeys<this>]: IfCo<this[Key], CoMapEdit<this[Key]>>;
185
+ };
186
+ }
187
+
188
+ /** @internal */
189
+ constructor(
305
190
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
306
- rawFromInit<Fields extends object = Record<string, any>>(
307
- init: Simplify<CoMapInit<Fields>> | undefined,
308
- owner: Account | Group,
309
- uniqueness?: CoValueUniqueness,
310
- ) {
311
- const rawOwner = owner._raw;
312
-
313
- const rawInit = {} as {
314
- [key in keyof Fields]: JsonValue | undefined;
315
- };
316
-
317
- if (init)
318
- for (const key of Object.keys(init) as (keyof Fields)[]) {
319
- const initValue = init[key as keyof typeof init];
320
-
321
- const descriptor = (this._schema[
322
- key as keyof typeof this._schema
323
- ] || this._schema[ItemsSym]) as Schema;
324
-
325
- if (!descriptor) {
326
- continue;
327
- }
328
-
329
- if (descriptor === "json") {
330
- rawInit[key] = initValue as JsonValue;
331
- } else if (isRefEncoded(descriptor)) {
332
- if (initValue) {
333
- rawInit[key] = (initValue as unknown as CoValue).id;
334
- }
335
- } else if ("encoded" in descriptor) {
336
- rawInit[key] = descriptor.encoded.encode(
337
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
338
- initValue as any,
339
- );
340
- }
341
- }
342
-
343
- return rawOwner.createMap(rawInit, null, "private", uniqueness);
191
+ options: { fromRaw: RawCoMap } | undefined,
192
+ ) {
193
+ super();
194
+
195
+ if (options) {
196
+ if ("fromRaw" in options) {
197
+ Object.defineProperties(this, {
198
+ id: {
199
+ value: options.fromRaw.id as unknown as ID<this>,
200
+ enumerable: false,
201
+ },
202
+ _raw: { value: options.fromRaw, enumerable: false },
203
+ });
204
+ } else {
205
+ throw new Error("Invalid CoMap constructor arguments");
206
+ }
344
207
  }
345
208
 
346
- /**
347
- * Declare a Record-like CoMap schema, by extending `CoMap.Record(...)` and passing the value schema using `co`. Keys are always `string`.
348
- *
349
- * @example
350
- * ```ts
351
- * import { co, CoMap } from "jazz-tools";
352
- *
353
- * class ColorToFruitMap extends CoMap.Record(
354
- * co.ref(Fruit)
355
- * ) {}
356
- *
357
- * // assume we have map: ColorToFruitMap
358
- * // and strawberry: Fruit
359
- * map["red"] = strawberry;
360
- * ```
361
- *
362
- * @category Declaration
363
- */
364
- static Record<Value>(value: IfCo<Value, Value>) {
365
- // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
366
- class RecordLikeCoMap extends CoMap {
367
- [ItemsSym] = value;
209
+ return new Proxy(this, CoMapProxyHandler as ProxyHandler<this>);
210
+ }
211
+
212
+ /**
213
+ * Create a new CoMap with the given initial values and owner.
214
+ *
215
+ * The owner (a Group or Account) determines access rights to the CoMap.
216
+ *
217
+ * The CoMap will immediately be persisted and synced to connected peers.
218
+ *
219
+ * @example
220
+ * ```ts
221
+ * const person = Person.create({
222
+ * name: "Alice",
223
+ * age: 42,
224
+ * pet: cat,
225
+ * }, { owner: friendGroup });
226
+ * ```
227
+ *
228
+ * @category Creation
229
+ **/
230
+ static create<M extends CoMap>(
231
+ this: CoValueClass<M>,
232
+ init: Simplify<CoMapInit<M>>,
233
+ options: {
234
+ owner: Account | Group;
235
+ unique?: CoValueUniqueness["uniqueness"];
236
+ },
237
+ ) {
238
+ const instance = new this();
239
+ const raw = instance.rawFromInit(
240
+ init,
241
+ options.owner,
242
+ options.unique === undefined ? undefined : { uniqueness: options.unique },
243
+ );
244
+ Object.defineProperties(instance, {
245
+ id: {
246
+ value: raw.id,
247
+ enumerable: false,
248
+ },
249
+ _raw: { value: raw, enumerable: false },
250
+ });
251
+ return instance;
252
+ }
253
+
254
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
255
+ toJSON(_key?: string, seenAbove?: ID<CoValue>[]): any[] {
256
+ const jsonedFields = this._raw.keys().map((key) => {
257
+ const tKey = key as CoKeys<this>;
258
+ const descriptor = (this._schema[tKey] ||
259
+ this._schema[ItemsSym]) as Schema;
260
+
261
+ if (descriptor == "json" || "encode" in descriptor) {
262
+ return [key, this._raw.get(key)];
263
+ } else if (isRefEncoded(descriptor)) {
264
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
265
+ if (seenAbove?.includes((this as any)[tKey]?.id)) {
266
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
267
+ return [key, { _circular: (this as any)[tKey]?.id }];
268
+ }
269
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
270
+ const jsonedRef = (this as any)[tKey]?.toJSON(tKey, [
271
+ ...(seenAbove || []),
272
+ this.id,
273
+ ]);
274
+ return [key, jsonedRef];
275
+ } else {
276
+ return [key, undefined];
277
+ }
278
+ });
279
+
280
+ return {
281
+ id: this.id,
282
+ _type: this._type,
283
+ ...Object.fromEntries(jsonedFields),
284
+ };
285
+ }
286
+
287
+ [inspect]() {
288
+ return this.toJSON();
289
+ }
290
+
291
+ /** @internal */
292
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
293
+ rawFromInit<Fields extends object = Record<string, any>>(
294
+ init: Simplify<CoMapInit<Fields>> | undefined,
295
+ owner: Account | Group,
296
+ uniqueness?: CoValueUniqueness,
297
+ ) {
298
+ const rawOwner = owner._raw;
299
+
300
+ const rawInit = {} as {
301
+ [key in keyof Fields]: JsonValue | undefined;
302
+ };
303
+
304
+ if (init)
305
+ for (const key of Object.keys(init) as (keyof Fields)[]) {
306
+ const initValue = init[key as keyof typeof init];
307
+
308
+ const descriptor = (this._schema[key as keyof typeof this._schema] ||
309
+ this._schema[ItemsSym]) as Schema;
310
+
311
+ if (!descriptor) {
312
+ continue;
368
313
  }
369
- // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
370
- interface RecordLikeCoMap extends Record<string, Value> {}
371
-
372
- return RecordLikeCoMap;
373
- }
374
-
375
- /**
376
- * Load a `CoMap` with a given ID, as a given account.
377
- *
378
- * `depth` specifies which (if any) fields that reference other CoValues to load as well before resolving.
379
- * The `DeeplyLoaded` return type guarantees that corresponding referenced CoValues are loaded to the specified depth.
380
- *
381
- * You can pass `[]` or `{}` for shallowly loading only this CoMap, or `{ fieldA: depthA, fieldB: depthB }` for recursively loading referenced CoValues.
382
- *
383
- * Check out the `load` methods on `CoMap`/`CoList`/`CoStream`/`Group`/`Account` to see which depth structures are valid to nest.
384
- *
385
- * @example
386
- * ```ts
387
- * const person = await Person.load(
388
- * "co_zdsMhHtfG6VNKt7RqPUPvUtN2Ax",
389
- * me,
390
- * { pet: {} }
391
- * );
392
- * ```
393
- *
394
- * @category Subscription & Loading
395
- */
396
- static load<M extends CoMap, Depth>(
397
- this: CoValueClass<M>,
398
- id: ID<M>,
399
- as: Account,
400
- depth: Depth & DepthsIn<M>,
401
- ): Promise<DeeplyLoaded<M, Depth> | undefined> {
402
- return loadCoValue(this, id, as, depth);
403
- }
404
-
405
- /**
406
- * Load and subscribe to a `CoMap` with a given ID, as a given account.
407
- *
408
- * Automatically also subscribes to updates to all referenced/nested CoValues as soon as they are accessed in the listener.
409
- *
410
- * `depth` specifies which (if any) fields that reference other CoValues to load as well before calling `listener` for the first time.
411
- * The `DeeplyLoaded` return type guarantees that corresponding referenced CoValues are loaded to the specified depth.
412
- *
413
- * You can pass `[]` or `{}` for shallowly loading only this CoMap, or `{ fieldA: depthA, fieldB: depthB }` for recursively loading referenced CoValues.
414
- *
415
- * Check out the `load` methods on `CoMap`/`CoList`/`CoStream`/`Group`/`Account` to see which depth structures are valid to nest.
416
- *
417
- * Returns an unsubscribe function that you should call when you no longer need updates.
418
- *
419
- * Also see the `useCoState` hook to reactively subscribe to a CoValue in a React component.
420
- *
421
- * @example
422
- * ```ts
423
- * const unsub = Person.subscribe(
424
- * "co_zdsMhHtfG6VNKt7RqPUPvUtN2Ax",
425
- * me,
426
- * { pet: {} },
427
- * (person) => console.log(person)
428
- * );
429
- * ```
430
- *
431
- * @category Subscription & Loading
432
- */
433
- static subscribe<M extends CoMap, Depth>(
434
- this: CoValueClass<M>,
435
- id: ID<M>,
436
- as: Account,
437
- depth: Depth & DepthsIn<M>,
438
- listener: (value: DeeplyLoaded<M, Depth>) => void,
439
- ): () => void {
440
- return subscribeToCoValue<M, Depth>(this, id, as, depth, listener);
441
- }
442
-
443
- static findUnique<M extends CoMap>(
444
- this: CoValueClass<M>,
445
- unique: CoValueUniqueness["uniqueness"],
446
- ownerID: ID<Account> | ID<Group>,
447
- as: Account | Group | AnonymousJazzAgent,
448
- ) {
449
- const header = {
450
- type: "comap" as const,
451
- ruleset: {
452
- type: "ownedByGroup" as const,
453
- group: ownerID,
454
- },
455
- meta: null,
456
- uniqueness: unique,
457
- };
458
- const crypto =
459
- as._type === "Anonymous" ? as.node.crypto : as._raw.core.crypto;
460
- return cojsonInternals.idforHeader(header, crypto) as ID<M>;
461
- }
462
-
463
- /**
464
- * Given an already loaded `CoMap`, ensure that the specified fields are loaded to the specified depth.
465
- *
466
- * Works like `CoMap.load()`, but you don't need to pass the ID or the account to load as again.
467
- *
468
- * @category Subscription & Loading
469
- */
470
- ensureLoaded<M extends CoMap, Depth>(
471
- this: M,
472
- depth: Depth & DepthsIn<M>,
473
- ): Promise<DeeplyLoaded<M, Depth> | undefined> {
474
- return ensureCoValueLoaded(this, depth);
475
- }
476
314
 
477
- /**
478
- * Given an already loaded `CoMap`, subscribe to updates to the `CoMap` and ensure that the specified fields are loaded to the specified depth.
479
- *
480
- * Works like `CoMap.subscribe()`, but you don't need to pass the ID or the account to load as again.
481
- *
482
- * Returns an unsubscribe function that you should call when you no longer need updates.
483
- *
484
- * @category Subscription & Loading
485
- **/
486
- subscribe<M extends CoMap, Depth>(
487
- this: M,
488
- depth: Depth & DepthsIn<M>,
489
- listener: (value: DeeplyLoaded<M, Depth>) => void,
490
- ): () => void {
491
- return subscribeToExistingCoValue(this, depth, listener);
315
+ if (descriptor === "json") {
316
+ rawInit[key] = initValue as JsonValue;
317
+ } else if (isRefEncoded(descriptor)) {
318
+ if (initValue) {
319
+ rawInit[key] = (initValue as unknown as CoValue).id;
320
+ }
321
+ } else if ("encoded" in descriptor) {
322
+ rawInit[key] = descriptor.encoded.encode(
323
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
324
+ initValue as any,
325
+ );
326
+ }
327
+ }
328
+
329
+ return rawOwner.createMap(rawInit, null, "private", uniqueness);
330
+ }
331
+
332
+ /**
333
+ * Declare a Record-like CoMap schema, by extending `CoMap.Record(...)` and passing the value schema using `co`. Keys are always `string`.
334
+ *
335
+ * @example
336
+ * ```ts
337
+ * import { co, CoMap } from "jazz-tools";
338
+ *
339
+ * class ColorToFruitMap extends CoMap.Record(
340
+ * co.ref(Fruit)
341
+ * ) {}
342
+ *
343
+ * // assume we have map: ColorToFruitMap
344
+ * // and strawberry: Fruit
345
+ * map["red"] = strawberry;
346
+ * ```
347
+ *
348
+ * @category Declaration
349
+ */
350
+ static Record<Value>(value: IfCo<Value, Value>) {
351
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
352
+ class RecordLikeCoMap extends CoMap {
353
+ [ItemsSym] = value;
492
354
  }
493
-
494
- applyDiff<N extends Partial<CoMapInit<this>>>(newValues: N) {
495
- for (const key in newValues) {
496
- if (Object.prototype.hasOwnProperty.call(newValues, key)) {
497
- const tKey = key as keyof typeof newValues & keyof this;
498
- const descriptor = (this._schema[tKey as string] ||
499
- this._schema[ItemsSym]) as Schema;
500
-
501
- if (tKey in this._schema) {
502
- const newValue = newValues[tKey];
503
- const currentValue = (this as unknown as N)[tKey];
504
-
505
- if (descriptor === "json" || "encoded" in descriptor) {
506
- if (currentValue !== newValue) {
507
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
508
- (this as any)[tKey] = newValue;
509
- }
510
- } else if (isRefEncoded(descriptor)) {
511
- const currentId = (currentValue as CoValue | undefined)
512
- ?.id;
513
- const newId = (newValue as CoValue | undefined)?.id;
514
- if (currentId !== newId) {
515
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
516
- (this as any)[tKey] = newValue;
517
- }
518
- }
519
- }
355
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
356
+ interface RecordLikeCoMap extends Record<string, Value> {}
357
+
358
+ return RecordLikeCoMap;
359
+ }
360
+
361
+ /**
362
+ * Load a `CoMap` with a given ID, as a given account.
363
+ *
364
+ * `depth` specifies which (if any) fields that reference other CoValues to load as well before resolving.
365
+ * The `DeeplyLoaded` return type guarantees that corresponding referenced CoValues are loaded to the specified depth.
366
+ *
367
+ * You can pass `[]` or `{}` for shallowly loading only this CoMap, or `{ fieldA: depthA, fieldB: depthB }` for recursively loading referenced CoValues.
368
+ *
369
+ * Check out the `load` methods on `CoMap`/`CoList`/`CoStream`/`Group`/`Account` to see which depth structures are valid to nest.
370
+ *
371
+ * @example
372
+ * ```ts
373
+ * const person = await Person.load(
374
+ * "co_zdsMhHtfG6VNKt7RqPUPvUtN2Ax",
375
+ * me,
376
+ * { pet: {} }
377
+ * );
378
+ * ```
379
+ *
380
+ * @category Subscription & Loading
381
+ */
382
+ static load<M extends CoMap, Depth>(
383
+ this: CoValueClass<M>,
384
+ id: ID<M>,
385
+ as: Account,
386
+ depth: Depth & DepthsIn<M>,
387
+ ): Promise<DeeplyLoaded<M, Depth> | undefined> {
388
+ return loadCoValue(this, id, as, depth);
389
+ }
390
+
391
+ /**
392
+ * Load and subscribe to a `CoMap` with a given ID, as a given account.
393
+ *
394
+ * Automatically also subscribes to updates to all referenced/nested CoValues as soon as they are accessed in the listener.
395
+ *
396
+ * `depth` specifies which (if any) fields that reference other CoValues to load as well before calling `listener` for the first time.
397
+ * The `DeeplyLoaded` return type guarantees that corresponding referenced CoValues are loaded to the specified depth.
398
+ *
399
+ * You can pass `[]` or `{}` for shallowly loading only this CoMap, or `{ fieldA: depthA, fieldB: depthB }` for recursively loading referenced CoValues.
400
+ *
401
+ * Check out the `load` methods on `CoMap`/`CoList`/`CoStream`/`Group`/`Account` to see which depth structures are valid to nest.
402
+ *
403
+ * Returns an unsubscribe function that you should call when you no longer need updates.
404
+ *
405
+ * Also see the `useCoState` hook to reactively subscribe to a CoValue in a React component.
406
+ *
407
+ * @example
408
+ * ```ts
409
+ * const unsub = Person.subscribe(
410
+ * "co_zdsMhHtfG6VNKt7RqPUPvUtN2Ax",
411
+ * me,
412
+ * { pet: {} },
413
+ * (person) => console.log(person)
414
+ * );
415
+ * ```
416
+ *
417
+ * @category Subscription & Loading
418
+ */
419
+ static subscribe<M extends CoMap, Depth>(
420
+ this: CoValueClass<M>,
421
+ id: ID<M>,
422
+ as: Account,
423
+ depth: Depth & DepthsIn<M>,
424
+ listener: (value: DeeplyLoaded<M, Depth>) => void,
425
+ ): () => void {
426
+ return subscribeToCoValue<M, Depth>(this, id, as, depth, listener);
427
+ }
428
+
429
+ static findUnique<M extends CoMap>(
430
+ this: CoValueClass<M>,
431
+ unique: CoValueUniqueness["uniqueness"],
432
+ ownerID: ID<Account> | ID<Group>,
433
+ as: Account | Group | AnonymousJazzAgent,
434
+ ) {
435
+ const header = {
436
+ type: "comap" as const,
437
+ ruleset: {
438
+ type: "ownedByGroup" as const,
439
+ group: ownerID,
440
+ },
441
+ meta: null,
442
+ uniqueness: unique,
443
+ };
444
+ const crypto =
445
+ as._type === "Anonymous" ? as.node.crypto : as._raw.core.crypto;
446
+ return cojsonInternals.idforHeader(header, crypto) as ID<M>;
447
+ }
448
+
449
+ /**
450
+ * Given an already loaded `CoMap`, ensure that the specified fields are loaded to the specified depth.
451
+ *
452
+ * Works like `CoMap.load()`, but you don't need to pass the ID or the account to load as again.
453
+ *
454
+ * @category Subscription & Loading
455
+ */
456
+ ensureLoaded<M extends CoMap, Depth>(
457
+ this: M,
458
+ depth: Depth & DepthsIn<M>,
459
+ ): Promise<DeeplyLoaded<M, Depth> | undefined> {
460
+ return ensureCoValueLoaded(this, depth);
461
+ }
462
+
463
+ /**
464
+ * Given an already loaded `CoMap`, subscribe to updates to the `CoMap` and ensure that the specified fields are loaded to the specified depth.
465
+ *
466
+ * Works like `CoMap.subscribe()`, but you don't need to pass the ID or the account to load as again.
467
+ *
468
+ * Returns an unsubscribe function that you should call when you no longer need updates.
469
+ *
470
+ * @category Subscription & Loading
471
+ **/
472
+ subscribe<M extends CoMap, Depth>(
473
+ this: M,
474
+ depth: Depth & DepthsIn<M>,
475
+ listener: (value: DeeplyLoaded<M, Depth>) => void,
476
+ ): () => void {
477
+ return subscribeToExistingCoValue(this, depth, listener);
478
+ }
479
+
480
+ applyDiff<N extends Partial<CoMapInit<this>>>(newValues: N) {
481
+ for (const key in newValues) {
482
+ if (Object.prototype.hasOwnProperty.call(newValues, key)) {
483
+ const tKey = key as keyof typeof newValues & keyof this;
484
+ const descriptor = (this._schema[tKey as string] ||
485
+ this._schema[ItemsSym]) as Schema;
486
+
487
+ if (tKey in this._schema) {
488
+ const newValue = newValues[tKey];
489
+ const currentValue = (this as unknown as N)[tKey];
490
+
491
+ if (descriptor === "json" || "encoded" in descriptor) {
492
+ if (currentValue !== newValue) {
493
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
494
+ (this as any)[tKey] = newValue;
520
495
  }
496
+ } else if (isRefEncoded(descriptor)) {
497
+ const currentId = (currentValue as CoValue | undefined)?.id;
498
+ const newId = (newValue as CoValue | undefined)?.id;
499
+ if (currentId !== newId) {
500
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
501
+ (this as any)[tKey] = newValue;
502
+ }
503
+ }
521
504
  }
522
- return this;
505
+ }
523
506
  }
507
+ return this;
508
+ }
524
509
  }
525
510
 
526
511
  export type CoKeys<Map extends object> = Exclude<
527
- keyof Map & string,
528
- keyof CoMap
512
+ keyof Map & string,
513
+ keyof CoMap
529
514
  >;
530
515
 
531
516
  /**
@@ -554,155 +539,150 @@ export type CoKeys<Map extends object> = Exclude<
554
539
  * map.requiredRef // this value is still nullable
555
540
  */
556
541
  type ForceRequiredRef<V> = V extends co<InstanceType<CoValueClass> | null>
557
- ? NonNullable<V>
558
- : V extends co<InstanceType<CoValueClass> | undefined>
559
- ? V | null
560
- : V;
542
+ ? NonNullable<V>
543
+ : V extends co<InstanceType<CoValueClass> | undefined>
544
+ ? V | null
545
+ : V;
561
546
 
562
547
  export type CoMapInit<Map extends object> = {
563
- [Key in CoKeys<Map> as undefined extends Map[Key]
564
- ? never
565
- : IfCo<Map[Key], Key>]: ForceRequiredRef<Map[Key]>;
548
+ [Key in CoKeys<Map> as undefined extends Map[Key]
549
+ ? never
550
+ : IfCo<Map[Key], Key>]: ForceRequiredRef<Map[Key]>;
566
551
  } & {
567
- [Key in CoKeys<Map> as IfCo<Map[Key], Key>]?: ForceRequiredRef<Map[Key]>;
552
+ [Key in CoKeys<Map> as IfCo<Map[Key], Key>]?: ForceRequiredRef<Map[Key]>;
568
553
  };
569
554
 
570
555
  // TODO: cache handlers per descriptor for performance?
571
556
  const CoMapProxyHandler: ProxyHandler<CoMap> = {
572
- get(target, key, receiver) {
573
- if (key === "_schema") {
574
- return Reflect.get(target, key);
575
- } else if (key in target) {
576
- return Reflect.get(target, key, receiver);
577
- } else {
578
- const descriptor = (target._schema[key as keyof CoMap["_schema"]] ||
579
- target._schema[ItemsSym]) as Schema;
580
- if (descriptor && typeof key === "string") {
581
- const raw = target._raw.get(key);
582
-
583
- if (descriptor === "json") {
584
- return raw;
585
- } else if ("encoded" in descriptor) {
586
- return raw === undefined
587
- ? undefined
588
- : descriptor.encoded.decode(raw);
589
- } else if (isRefEncoded(descriptor)) {
590
- return raw === undefined
591
- ? undefined
592
- : new Ref(
593
- raw as unknown as ID<CoValue>,
594
- target._loadedAs,
595
- descriptor,
596
- ).accessFrom(receiver, key);
597
- }
598
- } else {
599
- return undefined;
600
- }
601
- }
602
- },
603
- set(target, key, value, receiver) {
604
- if (
605
- (typeof key === "string" || ItemsSym) &&
606
- typeof value === "object" &&
607
- value !== null &&
608
- SchemaInit in value
609
- ) {
610
- (target.constructor as typeof CoMap)._schema ||= {};
611
- (target.constructor as typeof CoMap)._schema[key] =
612
- value[SchemaInit];
613
- return true;
614
- }
615
-
616
- const descriptor = (target._schema[key as keyof CoMap["_schema"]] ||
617
- target._schema[ItemsSym]) as Schema;
618
- if (descriptor && typeof key === "string") {
619
- if (descriptor === "json") {
620
- target._raw.set(key, value);
621
- } else if ("encoded" in descriptor) {
622
- target._raw.set(key, descriptor.encoded.encode(value));
623
- } else if (isRefEncoded(descriptor)) {
624
- if (value === null) {
625
- if (descriptor.optional) {
626
- target._raw.set(key, null);
627
- } else {
628
- throw new Error(
629
- `Cannot set required reference ${key} to null`,
630
- );
631
- }
632
- } else {
633
- target._raw.set(key, value.id);
634
- subscriptionsScopes
635
- .get(target)
636
- ?.onRefAccessedOrSet(target.id, value.id);
637
- }
638
- }
639
- return true;
640
- } else {
641
- return Reflect.set(target, key, value, receiver);
642
- }
643
- },
644
- defineProperty(target, key, attributes) {
645
- if (
646
- "value" in attributes &&
647
- typeof attributes.value === "object" &&
648
- SchemaInit in attributes.value
649
- ) {
650
- (target.constructor as typeof CoMap)._schema ||= {};
651
- (target.constructor as typeof CoMap)._schema[key as string] =
652
- attributes.value[SchemaInit];
653
- return true;
654
- } else {
655
- return Reflect.defineProperty(target, key, attributes);
656
- }
657
- },
658
- ownKeys(target) {
659
- const keys = Reflect.ownKeys(target).filter((k) => k !== ItemsSym);
660
- // for (const key of Reflect.ownKeys(target._schema)) {
661
- // if (key !== ItemsSym && !keys.includes(key)) {
662
- // keys.push(key);
663
- // }
664
- // }
665
- for (const key of target._raw.keys()) {
666
- if (!keys.includes(key)) {
667
- keys.push(key);
668
- }
557
+ get(target, key, receiver) {
558
+ if (key === "_schema") {
559
+ return Reflect.get(target, key);
560
+ } else if (key in target) {
561
+ return Reflect.get(target, key, receiver);
562
+ } else {
563
+ const descriptor = (target._schema[key as keyof CoMap["_schema"]] ||
564
+ target._schema[ItemsSym]) as Schema;
565
+ if (descriptor && typeof key === "string") {
566
+ const raw = target._raw.get(key);
567
+
568
+ if (descriptor === "json") {
569
+ return raw;
570
+ } else if ("encoded" in descriptor) {
571
+ return raw === undefined ? undefined : descriptor.encoded.decode(raw);
572
+ } else if (isRefEncoded(descriptor)) {
573
+ return raw === undefined
574
+ ? undefined
575
+ : new Ref(
576
+ raw as unknown as ID<CoValue>,
577
+ target._loadedAs,
578
+ descriptor,
579
+ ).accessFrom(receiver, key);
669
580
  }
581
+ } else {
582
+ return undefined;
583
+ }
584
+ }
585
+ },
586
+ set(target, key, value, receiver) {
587
+ if (
588
+ (typeof key === "string" || ItemsSym) &&
589
+ typeof value === "object" &&
590
+ value !== null &&
591
+ SchemaInit in value
592
+ ) {
593
+ (target.constructor as typeof CoMap)._schema ||= {};
594
+ (target.constructor as typeof CoMap)._schema[key] = value[SchemaInit];
595
+ return true;
596
+ }
670
597
 
671
- return keys;
672
- },
673
- getOwnPropertyDescriptor(target, key) {
674
- if (key in target) {
675
- return Reflect.getOwnPropertyDescriptor(target, key);
598
+ const descriptor = (target._schema[key as keyof CoMap["_schema"]] ||
599
+ target._schema[ItemsSym]) as Schema;
600
+ if (descriptor && typeof key === "string") {
601
+ if (descriptor === "json") {
602
+ target._raw.set(key, value);
603
+ } else if ("encoded" in descriptor) {
604
+ target._raw.set(key, descriptor.encoded.encode(value));
605
+ } else if (isRefEncoded(descriptor)) {
606
+ if (value === null) {
607
+ if (descriptor.optional) {
608
+ target._raw.set(key, null);
609
+ } else {
610
+ throw new Error(`Cannot set required reference ${key} to null`);
611
+ }
676
612
  } else {
677
- const descriptor = (target._schema[key as keyof CoMap["_schema"]] ||
678
- target._schema[ItemsSym]) as Schema;
679
- if (descriptor || key in target._raw.ops) {
680
- return {
681
- enumerable: true,
682
- configurable: true,
683
- writable: true,
684
- };
685
- }
613
+ target._raw.set(key, value.id);
614
+ subscriptionsScopes
615
+ .get(target)
616
+ ?.onRefAccessedOrSet(target.id, value.id);
686
617
  }
687
- },
688
- has(target, key) {
689
- const descriptor = (target._schema?.[key as keyof CoMap["_schema"]] ||
690
- target._schema?.[ItemsSym]) as Schema;
618
+ }
619
+ return true;
620
+ } else {
621
+ return Reflect.set(target, key, value, receiver);
622
+ }
623
+ },
624
+ defineProperty(target, key, attributes) {
625
+ if (
626
+ "value" in attributes &&
627
+ typeof attributes.value === "object" &&
628
+ SchemaInit in attributes.value
629
+ ) {
630
+ (target.constructor as typeof CoMap)._schema ||= {};
631
+ (target.constructor as typeof CoMap)._schema[key as string] =
632
+ attributes.value[SchemaInit];
633
+ return true;
634
+ } else {
635
+ return Reflect.defineProperty(target, key, attributes);
636
+ }
637
+ },
638
+ ownKeys(target) {
639
+ const keys = Reflect.ownKeys(target).filter((k) => k !== ItemsSym);
640
+ // for (const key of Reflect.ownKeys(target._schema)) {
641
+ // if (key !== ItemsSym && !keys.includes(key)) {
642
+ // keys.push(key);
643
+ // }
644
+ // }
645
+ for (const key of target._raw.keys()) {
646
+ if (!keys.includes(key)) {
647
+ keys.push(key);
648
+ }
649
+ }
691
650
 
692
- if (target._raw && typeof key === "string" && descriptor) {
693
- return target._raw.get(key) !== undefined;
694
- } else {
695
- return Reflect.has(target, key);
696
- }
697
- },
698
- deleteProperty(target, key) {
699
- const descriptor = (target._schema[key as keyof CoMap["_schema"]] ||
700
- target._schema[ItemsSym]) as Schema;
701
- if (typeof key === "string" && descriptor) {
702
- target._raw.delete(key);
703
- return true;
704
- } else {
705
- return Reflect.deleteProperty(target, key);
706
- }
707
- },
651
+ return keys;
652
+ },
653
+ getOwnPropertyDescriptor(target, key) {
654
+ if (key in target) {
655
+ return Reflect.getOwnPropertyDescriptor(target, key);
656
+ } else {
657
+ const descriptor = (target._schema[key as keyof CoMap["_schema"]] ||
658
+ target._schema[ItemsSym]) as Schema;
659
+ if (descriptor || key in target._raw.ops) {
660
+ return {
661
+ enumerable: true,
662
+ configurable: true,
663
+ writable: true,
664
+ };
665
+ }
666
+ }
667
+ },
668
+ has(target, key) {
669
+ const descriptor = (target._schema?.[key as keyof CoMap["_schema"]] ||
670
+ target._schema?.[ItemsSym]) as Schema;
671
+
672
+ if (target._raw && typeof key === "string" && descriptor) {
673
+ return target._raw.get(key) !== undefined;
674
+ } else {
675
+ return Reflect.has(target, key);
676
+ }
677
+ },
678
+ deleteProperty(target, key) {
679
+ const descriptor = (target._schema[key as keyof CoMap["_schema"]] ||
680
+ target._schema[ItemsSym]) as Schema;
681
+ if (typeof key === "string" && descriptor) {
682
+ target._raw.delete(key);
683
+ return true;
684
+ } else {
685
+ return Reflect.deleteProperty(target, key);
686
+ }
687
+ },
708
688
  };