jazz-tools 0.13.16 → 0.13.18

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.
Files changed (83) hide show
  1. package/.turbo/turbo-build.log +7 -7
  2. package/CHANGELOG.md +19 -0
  3. package/dist/{chunk-GIZWJXSR.js → chunk-ITSHLDQB.js} +731 -600
  4. package/dist/chunk-ITSHLDQB.js.map +1 -0
  5. package/dist/coValues/account.d.ts +5 -4
  6. package/dist/coValues/account.d.ts.map +1 -1
  7. package/dist/coValues/coFeed.d.ts +3 -3
  8. package/dist/coValues/coFeed.d.ts.map +1 -1
  9. package/dist/coValues/coList.d.ts +1 -0
  10. package/dist/coValues/coList.d.ts.map +1 -1
  11. package/dist/coValues/coMap.d.ts +4 -2
  12. package/dist/coValues/coMap.d.ts.map +1 -1
  13. package/dist/coValues/coPlainText.d.ts +2 -2
  14. package/dist/coValues/coPlainText.d.ts.map +1 -1
  15. package/dist/coValues/deepLoading.d.ts +1 -9
  16. package/dist/coValues/deepLoading.d.ts.map +1 -1
  17. package/dist/coValues/extensions/imageDef.d.ts.map +1 -1
  18. package/dist/coValues/group.d.ts.map +1 -1
  19. package/dist/coValues/inbox.d.ts.map +1 -1
  20. package/dist/coValues/interfaces.d.ts +4 -1
  21. package/dist/coValues/interfaces.d.ts.map +1 -1
  22. package/dist/implementation/createContext.d.ts.map +1 -1
  23. package/dist/implementation/refs.d.ts +5 -10
  24. package/dist/implementation/refs.d.ts.map +1 -1
  25. package/dist/index.js +1 -1
  26. package/dist/internal.d.ts +1 -1
  27. package/dist/internal.d.ts.map +1 -1
  28. package/dist/subscribe/CoValueCoreSubscription.d.ts +14 -0
  29. package/dist/subscribe/CoValueCoreSubscription.d.ts.map +1 -0
  30. package/dist/subscribe/JazzError.d.ts +16 -0
  31. package/dist/subscribe/JazzError.d.ts.map +1 -0
  32. package/dist/subscribe/SubscriptionScope.d.ts +42 -0
  33. package/dist/subscribe/SubscriptionScope.d.ts.map +1 -0
  34. package/dist/subscribe/index.d.ts +21 -0
  35. package/dist/subscribe/index.d.ts.map +1 -0
  36. package/dist/subscribe/types.d.ts +12 -0
  37. package/dist/subscribe/types.d.ts.map +1 -0
  38. package/dist/subscribe/utils.d.ts +10 -0
  39. package/dist/subscribe/utils.d.ts.map +1 -0
  40. package/dist/testing.js +2 -2
  41. package/dist/testing.js.map +1 -1
  42. package/dist/tests/coMap.record.test.d.ts +2 -0
  43. package/dist/tests/coMap.record.test.d.ts.map +1 -0
  44. package/dist/tests/utils.d.ts +2 -2
  45. package/dist/tests/utils.d.ts.map +1 -1
  46. package/package.json +2 -2
  47. package/src/coValues/account.ts +43 -31
  48. package/src/coValues/coFeed.ts +28 -13
  49. package/src/coValues/coList.ts +13 -17
  50. package/src/coValues/coMap.ts +72 -80
  51. package/src/coValues/coPlainText.ts +13 -2
  52. package/src/coValues/deepLoading.ts +4 -277
  53. package/src/coValues/extensions/imageDef.ts +1 -7
  54. package/src/coValues/group.ts +7 -6
  55. package/src/coValues/inbox.ts +4 -11
  56. package/src/coValues/interfaces.ts +54 -111
  57. package/src/implementation/createContext.ts +3 -4
  58. package/src/implementation/invites.ts +2 -2
  59. package/src/implementation/refs.ts +30 -121
  60. package/src/internal.ts +1 -2
  61. package/src/subscribe/CoValueCoreSubscription.ts +71 -0
  62. package/src/subscribe/JazzError.ts +48 -0
  63. package/src/subscribe/SubscriptionScope.ts +523 -0
  64. package/src/subscribe/index.ts +82 -0
  65. package/src/subscribe/types.ts +7 -0
  66. package/src/subscribe/utils.ts +36 -0
  67. package/src/testing.ts +1 -1
  68. package/src/tests/ContextManager.test.ts +13 -9
  69. package/src/tests/coFeed.test.ts +104 -4
  70. package/src/tests/coList.test.ts +304 -115
  71. package/src/tests/coMap.record.test.ts +325 -0
  72. package/src/tests/coMap.test.ts +718 -645
  73. package/src/tests/coPlainText.test.ts +2 -2
  74. package/src/tests/createContext.test.ts +8 -8
  75. package/src/tests/deepLoading.test.ts +8 -34
  76. package/src/tests/groupsAndAccounts.test.ts +6 -4
  77. package/src/tests/subscribe.test.ts +357 -42
  78. package/src/tests/utils.ts +8 -6
  79. package/tsconfig.json +2 -1
  80. package/dist/chunk-GIZWJXSR.js.map +0 -1
  81. package/dist/implementation/subscriptionScope.d.ts +0 -34
  82. package/dist/implementation/subscriptionScope.d.ts.map +0 -1
  83. package/src/implementation/subscriptionScope.ts +0 -165
@@ -0,0 +1,523 @@
1
+ import type { LocalNode, RawCoValue } from "cojson";
2
+ import type { CoFeed, CoList, CoMap } from "../exports.js";
3
+ import {
4
+ type CoValue,
5
+ type ID,
6
+ type RefEncoded,
7
+ type RefsToResolve,
8
+ isRefEncoded,
9
+ } from "../internal.js";
10
+ import { CoValueCoreSubscription } from "./CoValueCoreSubscription.js";
11
+ import { JazzError, type JazzErrorIssue } from "./JazzError.js";
12
+ import type { SubscriptionValue, Unloaded } from "./types.js";
13
+ import { createCoValue, getOwnerFromRawValue } from "./utils.js";
14
+
15
+ export class SubscriptionScope<D extends CoValue> {
16
+ childNodes = new Map<string, SubscriptionScope<CoValue>>();
17
+ childValues: Map<string, SubscriptionValue<any, any> | Unloaded> = new Map<
18
+ string,
19
+ SubscriptionValue<D, any>
20
+ >();
21
+ value: SubscriptionValue<D, any> | Unloaded;
22
+ childErrors: Map<string, JazzError> = new Map();
23
+ validationErrors: Map<string, JazzError> = new Map();
24
+ errorFromChildren: JazzError | undefined;
25
+ subscription: CoValueCoreSubscription;
26
+ dirty = false;
27
+ resolve: RefsToResolve<any>;
28
+ idsSubscribed = new Set<string>();
29
+ autoloaded = new Set<string>();
30
+ totalValidTransactions = 0;
31
+
32
+ silenceUpdates = false;
33
+
34
+ constructor(
35
+ public node: LocalNode,
36
+ resolve: RefsToResolve<D>,
37
+ public id: ID<D>,
38
+ public schema: RefEncoded<D>,
39
+ ) {
40
+ this.resolve = resolve;
41
+ this.value = { type: "unloaded", id };
42
+ this.subscription = new CoValueCoreSubscription(node, id, (value) => {
43
+ this.handleUpdate(value);
44
+ });
45
+ }
46
+
47
+ updateValue(value: SubscriptionValue<D, any>) {
48
+ this.value = value;
49
+
50
+ // Flags that the value has changed and we need to trigger an update
51
+ this.dirty = true;
52
+ }
53
+
54
+ handleUpdate(update: RawCoValue | "unavailable") {
55
+ if (update === "unavailable") {
56
+ if (this.value.type === "unloaded") {
57
+ this.updateValue(
58
+ new JazzError(this.id, "unavailable", [
59
+ {
60
+ code: "unavailable",
61
+ message: "The value is unavailable",
62
+ params: {
63
+ id: this.id,
64
+ },
65
+ path: [],
66
+ },
67
+ ]),
68
+ );
69
+ }
70
+ this.triggerUpdate();
71
+ return;
72
+ }
73
+
74
+ const owner = getOwnerFromRawValue(update);
75
+
76
+ const ruleset = update.core.verified.header.ruleset;
77
+
78
+ // Groups and accounts are accessible by everyone, for the other coValues we use the role to check access
79
+ const hasAccess = ruleset.type === "group" || owner.myRole() !== undefined;
80
+
81
+ if (!hasAccess) {
82
+ if (this.value.type !== "unauthorized") {
83
+ this.updateValue(
84
+ new JazzError(this.id, "unauthorized", [
85
+ {
86
+ code: "unauthorized",
87
+ message:
88
+ "The current user is not authorized to access this value",
89
+ params: {
90
+ id: this.id,
91
+ },
92
+ path: [],
93
+ },
94
+ ]),
95
+ );
96
+ this.triggerUpdate();
97
+ }
98
+ return;
99
+ }
100
+
101
+ // When resolving a CoValue with available children, we want to trigger a single update
102
+ // after loading all the children, not one per children
103
+ this.silenceUpdates = true;
104
+
105
+ if (this.value.type !== "loaded") {
106
+ this.updateValue(createCoValue(this.schema, update, this));
107
+ this.loadChildren();
108
+ } else {
109
+ const hasChanged =
110
+ update.totalValidTransactions !== this.totalValidTransactions ||
111
+ // Checking the identity of the _raw value makes us cover the cases where the group
112
+ // has been updated and the coValues that don't update the totalValidTransactions value (e.g. FileStream)
113
+ this.value.value._raw !== update;
114
+
115
+ if (this.loadChildren()) {
116
+ this.updateValue(createCoValue(this.schema, update, this));
117
+ } else if (hasChanged) {
118
+ this.updateValue(createCoValue(this.schema, update, this));
119
+ }
120
+ }
121
+
122
+ this.totalValidTransactions = update.totalValidTransactions;
123
+
124
+ this.silenceUpdates = false;
125
+ this.triggerUpdate();
126
+ }
127
+
128
+ computeChildErrors() {
129
+ let issues: JazzErrorIssue[] = [];
130
+ let errorType: JazzError["type"] = "unavailable";
131
+
132
+ if (this.childErrors.size === 0 && this.validationErrors.size === 0) {
133
+ return undefined;
134
+ }
135
+
136
+ for (const value of this.childErrors.values()) {
137
+ errorType = value.type;
138
+ if (value.issues) {
139
+ issues.push(...value.issues);
140
+ }
141
+ }
142
+
143
+ for (const value of this.validationErrors.values()) {
144
+ errorType = value.type;
145
+ if (value.issues) {
146
+ issues.push(...value.issues);
147
+ }
148
+ }
149
+
150
+ return new JazzError(this.id, errorType, issues);
151
+ }
152
+
153
+ handleChildUpdate = (
154
+ id: string,
155
+ value: SubscriptionValue<any, any> | Unloaded,
156
+ key?: string,
157
+ ) => {
158
+ if (value.type === "unloaded") {
159
+ return;
160
+ }
161
+
162
+ this.childValues.set(id, value);
163
+
164
+ if (value.type === "unavailable" || value.type === "unauthorized") {
165
+ this.childErrors.set(id, value.prependPath(key ?? id));
166
+
167
+ this.errorFromChildren = this.computeChildErrors();
168
+ } else if (this.errorFromChildren && this.childErrors.has(id)) {
169
+ this.childErrors.delete(id);
170
+
171
+ this.errorFromChildren = this.computeChildErrors();
172
+ }
173
+
174
+ if (this.shouldSendUpdates()) {
175
+ if (this.value.type === "loaded") {
176
+ // On child updates, we re-create the value instance to make the updates
177
+ // seamless-immutable and so be compatible with React and the React compiler
178
+ this.updateValue(
179
+ createCoValue(this.schema, this.value.value._raw, this),
180
+ );
181
+ }
182
+ }
183
+
184
+ this.triggerUpdate();
185
+ };
186
+
187
+ shouldSendUpdates() {
188
+ if (this.value.type === "unloaded") return false;
189
+
190
+ // If the value is in error, we send the update regardless of the children statuses
191
+ if (this.value.type !== "loaded") return true;
192
+
193
+ for (const value of this.childValues.values()) {
194
+ // We don't wait for autoloaded values to be loaded, in order to stream updates
195
+ // on autoloaded lists or records
196
+ if (value.type === "unloaded" && !this.autoloaded.has(value.id)) {
197
+ return false;
198
+ }
199
+ }
200
+
201
+ return true;
202
+ }
203
+
204
+ triggerUpdate() {
205
+ if (!this.shouldSendUpdates()) return;
206
+ if (!this.dirty) return;
207
+ if (this.subscribers.size === 0) return;
208
+ if (this.silenceUpdates) return;
209
+
210
+ const error = this.errorFromChildren;
211
+ const value = this.value;
212
+
213
+ if (error) {
214
+ this.subscribers.forEach((listener) => listener(error));
215
+ } else if (value.type !== "unloaded") {
216
+ this.subscribers.forEach((listener) => listener(value));
217
+ }
218
+
219
+ this.dirty = false;
220
+ }
221
+
222
+ subscribers = new Set<(value: SubscriptionValue<D, any>) => void>();
223
+ subscribe(listener: (value: SubscriptionValue<D, any>) => void) {
224
+ this.subscribers.add(listener);
225
+
226
+ return () => {
227
+ this.subscribers.delete(listener);
228
+ };
229
+ }
230
+
231
+ setListener(listener: (value: SubscriptionValue<D, any>) => void) {
232
+ this.subscribers.add(listener);
233
+ this.triggerUpdate();
234
+ }
235
+
236
+ subscribeToKey(key: string) {
237
+ if (this.resolve === true || !this.resolve) {
238
+ this.resolve = {};
239
+ }
240
+
241
+ if (this.resolve.$each || key in this.resolve) {
242
+ return;
243
+ }
244
+
245
+ this.resolve[key as keyof typeof this.resolve] = true;
246
+
247
+ if (this.value.type !== "loaded") {
248
+ return;
249
+ }
250
+
251
+ const value = this.value.value;
252
+
253
+ // We don't want to trigger an update when autoloading available children
254
+ // because on userland it looks like nothing has changed since the value
255
+ // is available on the first access
256
+ // This helps alot with correctness when triggering the autoloading while rendering components (on React and Svelte)
257
+ this.silenceUpdates = true;
258
+
259
+ if (value._type === "CoMap" || value._type === "Account") {
260
+ const map = value as CoMap;
261
+
262
+ const id = this.loadCoMapKey(map, key, true);
263
+
264
+ if (id) {
265
+ this.autoloaded.add(id);
266
+ }
267
+ } else if (value._type === "CoList") {
268
+ const list = value as CoList;
269
+
270
+ const id = this.loadCoListKey(list, key, true);
271
+
272
+ if (id) {
273
+ this.autoloaded.add(id);
274
+ }
275
+ }
276
+
277
+ this.silenceUpdates = false;
278
+ }
279
+
280
+ subscribeToId(id: string, descriptor: RefEncoded<any>) {
281
+ if (this.idsSubscribed.has(id) || this.childValues.has(id)) {
282
+ return;
283
+ }
284
+
285
+ this.idsSubscribed.add(id);
286
+ this.autoloaded.add(id);
287
+
288
+ // We don't want to trigger an update when autoloading available children
289
+ // because on userland it looks like nothing has changed since the value
290
+ // is available on the first access
291
+ // This helps alot with correctness when triggering the autoloading while rendering components (on React and Svelte)
292
+ this.silenceUpdates = true;
293
+
294
+ this.childValues.set(id, { type: "unloaded", id });
295
+ const child = new SubscriptionScope(
296
+ this.node,
297
+ true,
298
+ id as ID<any>,
299
+ descriptor,
300
+ );
301
+ this.childNodes.set(id, child);
302
+ child.setListener((value) => this.handleChildUpdate(id, value));
303
+
304
+ this.silenceUpdates = false;
305
+ }
306
+
307
+ loadChildren() {
308
+ const { resolve } = this;
309
+
310
+ if (this.value.type !== "loaded") {
311
+ return false;
312
+ }
313
+
314
+ const value = this.value.value;
315
+
316
+ const depth =
317
+ typeof resolve !== "object" || resolve === null ? {} : (resolve as any);
318
+
319
+ let hasChanged = false;
320
+
321
+ const idsToLoad = new Set<string>(this.idsSubscribed);
322
+
323
+ const coValueType = value._type;
324
+
325
+ if (Object.keys(depth).length > 0) {
326
+ if (coValueType === "CoMap" || coValueType === "Account") {
327
+ const map = value as CoMap;
328
+ const keys = "$each" in depth ? map._raw.keys() : Object.keys(depth);
329
+
330
+ for (const key of keys) {
331
+ const id = this.loadCoMapKey(map, key, depth[key] ?? depth.$each);
332
+
333
+ if (id) {
334
+ idsToLoad.add(id);
335
+ }
336
+ }
337
+ } else if (value._type === "CoList") {
338
+ const list = value as CoList;
339
+
340
+ const descriptor = list.getItemsDescriptor();
341
+
342
+ if (descriptor && isRefEncoded(descriptor)) {
343
+ list._raw.processNewTransactions();
344
+ const entries = list._raw.entries();
345
+ const keys =
346
+ "$each" in depth ? Object.keys(entries) : Object.keys(depth);
347
+
348
+ for (const key of keys) {
349
+ const id = this.loadCoListKey(list, key, depth[key] ?? depth.$each);
350
+
351
+ if (id) {
352
+ idsToLoad.add(id);
353
+ }
354
+ }
355
+ }
356
+ } else if (value._type === "CoStream") {
357
+ const stream = value as CoFeed;
358
+ const descriptor = stream.getItemsDescriptor();
359
+
360
+ if (descriptor && isRefEncoded(descriptor)) {
361
+ for (const session of stream._raw.sessions()) {
362
+ const values = stream._raw.items[session] ?? [];
363
+
364
+ for (const [i, item] of values.entries()) {
365
+ const key = `${session}/${i}`;
366
+
367
+ if (!depth.$each && !depth[key]) {
368
+ continue;
369
+ }
370
+
371
+ const id = item.value as string | undefined;
372
+
373
+ if (id) {
374
+ idsToLoad.add(id);
375
+ this.loadChildNode(id, depth[key] ?? depth.$each, descriptor);
376
+ this.validationErrors.delete(key);
377
+ } else if (!descriptor.optional) {
378
+ this.validationErrors.set(
379
+ key,
380
+ new JazzError(undefined, "unavailable", [
381
+ {
382
+ code: "validationError",
383
+ message: `The ref on position ${key} requested on ${stream.constructor.name} is missing`,
384
+ params: {},
385
+ path: [key],
386
+ },
387
+ ]),
388
+ );
389
+ }
390
+ }
391
+ }
392
+ }
393
+ }
394
+ }
395
+
396
+ this.errorFromChildren = this.computeChildErrors();
397
+
398
+ // Collect all the deleted ids
399
+ for (const id of this.childNodes.keys()) {
400
+ if (!idsToLoad.has(id)) {
401
+ hasChanged = true;
402
+ const childNode = this.childNodes.get(id);
403
+
404
+ if (childNode) {
405
+ childNode.destroy();
406
+ }
407
+
408
+ this.childNodes.delete(id);
409
+ this.childValues.delete(id);
410
+ }
411
+ }
412
+
413
+ return hasChanged;
414
+ }
415
+
416
+ loadCoMapKey(map: CoMap, key: string, depth: Record<string, any> | true) {
417
+ const id = map._raw.get(key) as string | undefined;
418
+ const descriptor = map.getDescriptor(key);
419
+
420
+ if (!descriptor) {
421
+ this.childErrors.set(
422
+ key,
423
+ new JazzError(undefined, "unavailable", [
424
+ {
425
+ code: "validationError",
426
+ message: `The ref ${key} requested on ${map.constructor.name} is not defined in the schema`,
427
+ params: {},
428
+ path: [key],
429
+ },
430
+ ]),
431
+ );
432
+ return undefined;
433
+ }
434
+
435
+ if (isRefEncoded(descriptor)) {
436
+ if (id) {
437
+ this.loadChildNode(id, depth, descriptor, key);
438
+ this.validationErrors.delete(key);
439
+
440
+ return id;
441
+ } else if (!descriptor.optional) {
442
+ this.validationErrors.set(
443
+ key,
444
+ new JazzError(undefined, "unavailable", [
445
+ {
446
+ code: "validationError",
447
+ message: `The ref ${key} requested on ${map.constructor.name} is missing`,
448
+ params: {},
449
+ path: [key],
450
+ },
451
+ ]),
452
+ );
453
+ }
454
+ }
455
+
456
+ return undefined;
457
+ }
458
+
459
+ loadCoListKey(list: CoList, key: string, depth: Record<string, any> | true) {
460
+ const descriptor = list.getItemsDescriptor();
461
+
462
+ if (!descriptor || !isRefEncoded(descriptor)) {
463
+ return undefined;
464
+ }
465
+
466
+ const entries = list._raw.entries();
467
+ const entry = entries[Number(key)];
468
+
469
+ if (!entry) {
470
+ return undefined;
471
+ }
472
+
473
+ const id = entry.value as string | undefined;
474
+
475
+ if (id) {
476
+ this.loadChildNode(id, depth, descriptor, key);
477
+ this.validationErrors.delete(key);
478
+
479
+ return id;
480
+ } else if (!descriptor.optional) {
481
+ this.validationErrors.set(
482
+ key,
483
+ new JazzError(undefined, "unavailable", [
484
+ {
485
+ code: "validationError",
486
+ message: `The ref on position ${key} requested on ${list.constructor.name} is missing`,
487
+ params: {},
488
+ path: [key],
489
+ },
490
+ ]),
491
+ );
492
+ }
493
+
494
+ return undefined;
495
+ }
496
+
497
+ loadChildNode(
498
+ id: string,
499
+ query: RefsToResolve<any>,
500
+ descriptor: RefEncoded<any>,
501
+ key?: string,
502
+ ) {
503
+ if (this.childValues.has(id)) {
504
+ return;
505
+ }
506
+
507
+ this.childValues.set(id, { type: "unloaded", id });
508
+ const child = new SubscriptionScope(
509
+ this.node,
510
+ query,
511
+ id as ID<any>,
512
+ descriptor,
513
+ );
514
+ this.childNodes.set(id, child);
515
+ child.setListener((value) => this.handleChildUpdate(id, value, key));
516
+ }
517
+
518
+ destroy() {
519
+ this.subscription.unsubscribe();
520
+ this.subscribers.clear();
521
+ this.childNodes.forEach((child) => child.destroy());
522
+ }
523
+ }
@@ -0,0 +1,82 @@
1
+ import type { CoValue, CoValueClass, RefEncoded } from "../internal.js";
2
+ import { SubscriptionScope } from "./SubscriptionScope.js";
3
+
4
+ export function getSubscriptionScope<D extends CoValue>(value: D) {
5
+ const subscriptionScope = value._subscriptionScope;
6
+
7
+ if (subscriptionScope) {
8
+ return subscriptionScope;
9
+ }
10
+
11
+ const node = value._raw.core.node;
12
+ const resolve = true;
13
+ const id = value.id;
14
+
15
+ const newSubscriptionScope = new SubscriptionScope(node, resolve, id, {
16
+ ref: value.constructor as CoValueClass<D>,
17
+ optional: false,
18
+ });
19
+
20
+ Object.defineProperty(value, "_subscriptionScope", {
21
+ value: subscriptionScope,
22
+ writable: false,
23
+ enumerable: false,
24
+ configurable: false,
25
+ });
26
+
27
+ return newSubscriptionScope;
28
+ }
29
+
30
+ /** Autoload internals */
31
+
32
+ /**
33
+ * Given a coValue, access a child coValue by key
34
+ *
35
+ * By subscribing to a given key, the subscription will automatically react to the id changes
36
+ * on that key (e.g. deleting the key value will result on unsubscribing from the id)
37
+ */
38
+ export function accessChildByKey<D extends CoValue>(
39
+ parent: D,
40
+ childId: string,
41
+ key: string,
42
+ ) {
43
+ const subscriptionScope = getSubscriptionScope(parent);
44
+
45
+ if (!subscriptionScope.childValues.has(childId)) {
46
+ subscriptionScope.subscribeToKey(key);
47
+ }
48
+
49
+ const value = subscriptionScope.childValues.get(childId);
50
+
51
+ if (value?.type === "loaded") {
52
+ return value.value;
53
+ } else {
54
+ return null;
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Given a coValue, access a child coValue by id
60
+ *
61
+ * By subscribing to a given id, the subscription becomes permanent and will unsubscribe
62
+ * only when the root subscription scope is destroyed.
63
+ *
64
+ * Used for refs that never change (e.g. CoFeed entries, CoMap edits)
65
+ */
66
+ export function accessChildById<D extends CoValue>(
67
+ parent: D,
68
+ childId: string,
69
+ schema: RefEncoded<CoValue>,
70
+ ) {
71
+ const subscriptionScope = getSubscriptionScope(parent);
72
+
73
+ subscriptionScope.subscribeToId(childId, schema);
74
+
75
+ const value = subscriptionScope.childValues.get(childId);
76
+
77
+ if (value?.type === "loaded") {
78
+ return value.value;
79
+ } else {
80
+ return null;
81
+ }
82
+ }
@@ -0,0 +1,7 @@
1
+ import type { CoValue, RefsToResolve, Resolved } from "../internal.js";
2
+ import type { JazzError } from "./JazzError.js";
3
+
4
+ export type SubscriptionValue<D extends CoValue, R extends RefsToResolve<D>> =
5
+ | { type: "loaded"; value: Resolved<D, R>; id: string }
6
+ | JazzError;
7
+ export type Unloaded = { type: "unloaded"; id: string };
@@ -0,0 +1,36 @@
1
+ import { RawAccount, RawCoValue, RawGroup } from "cojson";
2
+ import { RegisteredSchemas } from "../coValues/registeredSchemas.js";
3
+ import { CoValue, RefEncoded, instantiateRefEncoded } from "../internal.js";
4
+ import { coValuesCache } from "../lib/cache.js";
5
+ import { SubscriptionScope } from "./SubscriptionScope.js";
6
+
7
+ export function getOwnerFromRawValue(raw: RawCoValue) {
8
+ let owner = raw instanceof RawGroup ? raw : raw.group;
9
+
10
+ return coValuesCache.get(owner as any, () =>
11
+ owner instanceof RawAccount
12
+ ? RegisteredSchemas["Account"].fromRaw(owner)
13
+ : RegisteredSchemas["Group"].fromRaw(owner as any),
14
+ );
15
+ }
16
+
17
+ export function createCoValue<D extends CoValue>(
18
+ ref: RefEncoded<D>,
19
+ raw: RawCoValue,
20
+ subscriptionScope: SubscriptionScope<D>,
21
+ ) {
22
+ const freshValueInstance = instantiateRefEncoded(ref, raw);
23
+
24
+ Object.defineProperty(freshValueInstance, "_subscriptionScope", {
25
+ value: subscriptionScope,
26
+ writable: false,
27
+ enumerable: false,
28
+ configurable: false,
29
+ });
30
+
31
+ return {
32
+ type: "loaded" as const,
33
+ value: freshValueInstance,
34
+ id: subscriptionScope.id,
35
+ };
36
+ }
package/src/testing.ts CHANGED
@@ -185,7 +185,7 @@ export class TestJazzContextManager<
185
185
 
186
186
  const credentials = {
187
187
  accountID: account.id,
188
- accountSecret: node.account.agentSecret,
188
+ accountSecret: node.getCurrentAgent().agentSecret,
189
189
  secretSeed: SecretSeedMap.get(account.id),
190
190
  provider,
191
191
  } satisfies AuthCredentials;