jazz-tools 0.13.17 → 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.
- package/.turbo/turbo-build.log +7 -7
- package/CHANGELOG.md +11 -0
- package/dist/{chunk-PYBQOYML.js → chunk-ITSHLDQB.js} +729 -595
- package/dist/chunk-ITSHLDQB.js.map +1 -0
- package/dist/coValues/account.d.ts +5 -4
- package/dist/coValues/account.d.ts.map +1 -1
- package/dist/coValues/coFeed.d.ts +1 -0
- package/dist/coValues/coFeed.d.ts.map +1 -1
- package/dist/coValues/coList.d.ts +1 -0
- package/dist/coValues/coList.d.ts.map +1 -1
- package/dist/coValues/coMap.d.ts +4 -2
- package/dist/coValues/coMap.d.ts.map +1 -1
- package/dist/coValues/coPlainText.d.ts +2 -2
- package/dist/coValues/coPlainText.d.ts.map +1 -1
- package/dist/coValues/deepLoading.d.ts +1 -9
- package/dist/coValues/deepLoading.d.ts.map +1 -1
- package/dist/coValues/extensions/imageDef.d.ts.map +1 -1
- package/dist/coValues/group.d.ts.map +1 -1
- package/dist/coValues/inbox.d.ts.map +1 -1
- package/dist/coValues/interfaces.d.ts +4 -1
- package/dist/coValues/interfaces.d.ts.map +1 -1
- package/dist/implementation/createContext.d.ts.map +1 -1
- package/dist/implementation/refs.d.ts +5 -10
- package/dist/implementation/refs.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/internal.d.ts +1 -1
- package/dist/internal.d.ts.map +1 -1
- package/dist/subscribe/CoValueCoreSubscription.d.ts +14 -0
- package/dist/subscribe/CoValueCoreSubscription.d.ts.map +1 -0
- package/dist/subscribe/JazzError.d.ts +16 -0
- package/dist/subscribe/JazzError.d.ts.map +1 -0
- package/dist/subscribe/SubscriptionScope.d.ts +42 -0
- package/dist/subscribe/SubscriptionScope.d.ts.map +1 -0
- package/dist/subscribe/index.d.ts +21 -0
- package/dist/subscribe/index.d.ts.map +1 -0
- package/dist/subscribe/types.d.ts +12 -0
- package/dist/subscribe/types.d.ts.map +1 -0
- package/dist/subscribe/utils.d.ts +10 -0
- package/dist/subscribe/utils.d.ts.map +1 -0
- package/dist/testing.js +2 -2
- package/dist/testing.js.map +1 -1
- package/dist/tests/coMap.record.test.d.ts +2 -0
- package/dist/tests/coMap.record.test.d.ts.map +1 -0
- package/dist/tests/utils.d.ts +2 -2
- package/dist/tests/utils.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/coValues/account.ts +43 -31
- package/src/coValues/coFeed.ts +11 -7
- package/src/coValues/coList.ts +13 -17
- package/src/coValues/coMap.ts +72 -80
- package/src/coValues/coPlainText.ts +13 -2
- package/src/coValues/deepLoading.ts +4 -277
- package/src/coValues/extensions/imageDef.ts +1 -7
- package/src/coValues/group.ts +7 -6
- package/src/coValues/inbox.ts +4 -11
- package/src/coValues/interfaces.ts +54 -111
- package/src/implementation/createContext.ts +3 -4
- package/src/implementation/invites.ts +2 -2
- package/src/implementation/refs.ts +30 -121
- package/src/internal.ts +1 -2
- package/src/subscribe/CoValueCoreSubscription.ts +71 -0
- package/src/subscribe/JazzError.ts +48 -0
- package/src/subscribe/SubscriptionScope.ts +523 -0
- package/src/subscribe/index.ts +82 -0
- package/src/subscribe/types.ts +7 -0
- package/src/subscribe/utils.ts +36 -0
- package/src/testing.ts +1 -1
- package/src/tests/ContextManager.test.ts +13 -9
- package/src/tests/coFeed.test.ts +6 -6
- package/src/tests/coList.test.ts +304 -115
- package/src/tests/coMap.record.test.ts +325 -0
- package/src/tests/coMap.test.ts +718 -645
- package/src/tests/coPlainText.test.ts +2 -2
- package/src/tests/createContext.test.ts +8 -8
- package/src/tests/deepLoading.test.ts +8 -34
- package/src/tests/groupsAndAccounts.test.ts +6 -4
- package/src/tests/subscribe.test.ts +357 -42
- package/src/tests/utils.ts +8 -6
- package/dist/chunk-PYBQOYML.js.map +0 -1
- package/dist/implementation/subscriptionScope.d.ts +0 -34
- package/dist/implementation/subscriptionScope.d.ts.map +0 -1
- package/src/implementation/subscriptionScope.ts +0 -165
package/src/coValues/inbox.ts
CHANGED
@@ -1,11 +1,4 @@
|
|
1
|
-
import {
|
2
|
-
CoID,
|
3
|
-
InviteSecret,
|
4
|
-
RawAccount,
|
5
|
-
RawCoMap,
|
6
|
-
RawControlledAccount,
|
7
|
-
SessionID,
|
8
|
-
} from "cojson";
|
1
|
+
import { CoID, InviteSecret, RawAccount, RawCoMap, SessionID } from "cojson";
|
9
2
|
import { CoStreamItem, RawCoStream } from "cojson";
|
10
3
|
import { activeAccountContext } from "../implementation/activeAccountContext.js";
|
11
4
|
import { type Account } from "./account.js";
|
@@ -32,9 +25,9 @@ export function createInboxRoot(account: Account) {
|
|
32
25
|
throw new Error("Account is not controlled");
|
33
26
|
}
|
34
27
|
|
35
|
-
const rawAccount = account._raw
|
28
|
+
const rawAccount = account._raw;
|
36
29
|
|
37
|
-
const group = rawAccount.createGroup();
|
30
|
+
const group = rawAccount.core.node.createGroup();
|
38
31
|
const messagesFeed = group.createStream<MessagesStream>();
|
39
32
|
|
40
33
|
const inboxRoot = rawAccount.createMap<InboxRoot>();
|
@@ -386,7 +379,7 @@ async function acceptInvite(invite: string, account?: Account) {
|
|
386
379
|
throw new Error("Account is not controlled");
|
387
380
|
}
|
388
381
|
|
389
|
-
await
|
382
|
+
await account._raw.core.node.acceptInvite(id, inviteSecret);
|
390
383
|
|
391
384
|
return id;
|
392
385
|
}
|
@@ -3,23 +3,15 @@ import type {
|
|
3
3
|
CojsonInternalTypes,
|
4
4
|
RawCoValue,
|
5
5
|
} from "cojson";
|
6
|
-
import { RawAccount } from "cojson";
|
6
|
+
import { ControlledAccount, RawAccount } from "cojson";
|
7
7
|
import { activeAccountContext } from "../implementation/activeAccountContext.js";
|
8
8
|
import { AnonymousJazzAgent } from "../implementation/anonymousJazzAgent.js";
|
9
|
-
import {
|
10
|
-
Ref,
|
11
|
-
SubscriptionScope,
|
12
|
-
inspect,
|
13
|
-
subscriptionsScopes,
|
14
|
-
} from "../internal.js";
|
9
|
+
import { inspect } from "../internal.js";
|
15
10
|
import { coValuesCache } from "../lib/cache.js";
|
11
|
+
import { SubscriptionScope } from "../subscribe/SubscriptionScope.js";
|
12
|
+
import { SubscriptionValue } from "../subscribe/types.js";
|
16
13
|
import { type Account } from "./account.js";
|
17
|
-
import {
|
18
|
-
RefsToResolve,
|
19
|
-
RefsToResolveStrict,
|
20
|
-
Resolved,
|
21
|
-
fulfillsDepth,
|
22
|
-
} from "./deepLoading.js";
|
14
|
+
import { RefsToResolve, RefsToResolveStrict, Resolved } from "./deepLoading.js";
|
23
15
|
import { type Group } from "./group.js";
|
24
16
|
import { RegisteredSchemas } from "./registeredSchemas.js";
|
25
17
|
|
@@ -46,6 +38,10 @@ export interface CoValue {
|
|
46
38
|
_owner: Account | Group;
|
47
39
|
/** @category Internals */
|
48
40
|
_raw: RawCoValue;
|
41
|
+
|
42
|
+
/** @internal */
|
43
|
+
_subscriptionScope?: SubscriptionScope<this>;
|
44
|
+
|
49
45
|
/** @internal */
|
50
46
|
readonly _loadedAs: Account | AnonymousJazzAgent;
|
51
47
|
/** @category Stringifying & Inspection */
|
@@ -93,27 +89,22 @@ export class CoValueBase implements CoValue {
|
|
93
89
|
declare _instanceID: string;
|
94
90
|
|
95
91
|
get _owner(): Account | Group {
|
96
|
-
const owner =
|
92
|
+
const owner = coValuesCache.get(this._raw.group, () =>
|
97
93
|
this._raw.group instanceof RawAccount
|
98
94
|
? RegisteredSchemas["Account"].fromRaw(this._raw.group)
|
99
|
-
: RegisteredSchemas["Group"].fromRaw(this._raw.group)
|
100
|
-
|
101
|
-
const subScope = subscriptionsScopes.get(this);
|
102
|
-
if (subScope) {
|
103
|
-
subScope.onRefAccessedOrSet(this.id, owner.id);
|
104
|
-
subscriptionsScopes.set(owner, subScope);
|
105
|
-
}
|
95
|
+
: RegisteredSchemas["Group"].fromRaw(this._raw.group),
|
96
|
+
);
|
106
97
|
|
107
98
|
return owner;
|
108
99
|
}
|
109
100
|
|
110
101
|
/** @private */
|
111
102
|
get _loadedAs() {
|
112
|
-
const
|
103
|
+
const agent = this._raw.core.node.getCurrentAgent();
|
113
104
|
|
114
|
-
if (
|
115
|
-
return coValuesCache.get(
|
116
|
-
RegisteredSchemas["Account"].fromRaw(
|
105
|
+
if (agent instanceof ControlledAccount) {
|
106
|
+
return coValuesCache.get(agent.account, () =>
|
107
|
+
RegisteredSchemas["Account"].fromRaw(agent.account),
|
117
108
|
);
|
118
109
|
}
|
119
110
|
|
@@ -150,12 +141,7 @@ export class CoValueBase implements CoValue {
|
|
150
141
|
castAs<Cl extends CoValueClass & CoValueFromRaw<CoValue>>(
|
151
142
|
cl: Cl,
|
152
143
|
): InstanceType<Cl> {
|
153
|
-
|
154
|
-
const subscriptionScope = subscriptionsScopes.get(this);
|
155
|
-
if (subscriptionScope) {
|
156
|
-
subscriptionsScopes.set(casted, subscriptionScope);
|
157
|
-
}
|
158
|
-
return casted;
|
144
|
+
return cl.fromRaw(this._raw) as InstanceType<Cl>;
|
159
145
|
}
|
160
146
|
}
|
161
147
|
|
@@ -194,6 +180,7 @@ export function loadCoValue<
|
|
194
180
|
{
|
195
181
|
resolve: options.resolve,
|
196
182
|
loadAs: options.loadAs,
|
183
|
+
syncResolution: true,
|
197
184
|
onUnavailable: () => {
|
198
185
|
resolve(null);
|
199
186
|
},
|
@@ -317,102 +304,58 @@ export function subscribeToCoValue<
|
|
317
304
|
resolve?: RefsToResolveStrict<V, R>;
|
318
305
|
loadAs: Account | AnonymousJazzAgent;
|
319
306
|
onUnavailable?: () => void;
|
320
|
-
onUnauthorized?: (
|
307
|
+
onUnauthorized?: () => void;
|
321
308
|
syncResolution?: boolean;
|
322
309
|
},
|
323
310
|
listener: SubscribeListener<V, R>,
|
324
311
|
): () => void {
|
325
|
-
const
|
312
|
+
const loadAs = options.loadAs ?? activeAccountContext.get();
|
313
|
+
const node = "node" in loadAs ? loadAs.node : loadAs._raw.core.node;
|
326
314
|
|
327
|
-
|
328
|
-
let unsubscribe: (() => void) | undefined;
|
315
|
+
const resolve = options.resolve ?? true;
|
329
316
|
|
330
|
-
|
331
|
-
const value = ref.getValueWithoutAccessCheck();
|
317
|
+
let unsubscribed = false;
|
332
318
|
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
319
|
+
const rootNode = new SubscriptionScope<V>(node, resolve, id as ID<V>, {
|
320
|
+
ref: cls,
|
321
|
+
optional: false,
|
322
|
+
});
|
337
323
|
|
324
|
+
const handleUpdate = (value: SubscriptionValue<V, any>) => {
|
338
325
|
if (unsubscribed) return;
|
339
326
|
|
340
|
-
|
341
|
-
|
342
|
-
cls as CoValueClass<V> & CoValueFromRaw<V>,
|
343
|
-
(update, subscription) => {
|
344
|
-
if (subscription.syncResolution) return;
|
345
|
-
|
346
|
-
if (!ref.hasReadAccess()) {
|
347
|
-
console.error(
|
348
|
-
"Not enough permissions to load / subscribe to CoValue",
|
349
|
-
id,
|
350
|
-
);
|
351
|
-
options.onUnauthorized?.([]);
|
352
|
-
return;
|
353
|
-
}
|
354
|
-
|
355
|
-
let result;
|
356
|
-
|
357
|
-
try {
|
358
|
-
subscription.syncResolution = true;
|
359
|
-
result = fulfillsDepth(options.resolve, update);
|
360
|
-
} catch (e) {
|
361
|
-
console.error(
|
362
|
-
"Failed to load / subscribe to CoValue",
|
363
|
-
e,
|
364
|
-
e instanceof Error ? e.stack : undefined,
|
365
|
-
);
|
366
|
-
options.onUnavailable?.();
|
367
|
-
return;
|
368
|
-
} finally {
|
369
|
-
subscription.syncResolution = false;
|
370
|
-
}
|
371
|
-
|
372
|
-
if (result.status === "unauthorized") {
|
373
|
-
console.error(
|
374
|
-
"Not enough permissions to load / subscribe to CoValue",
|
375
|
-
id,
|
376
|
-
"on path",
|
377
|
-
result.path.join("."),
|
378
|
-
"unaccessible value:",
|
379
|
-
result.id,
|
380
|
-
);
|
381
|
-
options.onUnauthorized?.(result.path);
|
382
|
-
return;
|
383
|
-
}
|
384
|
-
|
385
|
-
if (result.status === "fulfilled") {
|
386
|
-
listener(update as Resolved<V, R>, subscription.unsubscribeAll);
|
387
|
-
}
|
388
|
-
},
|
389
|
-
);
|
327
|
+
if (value.type === "unavailable") {
|
328
|
+
options.onUnavailable?.();
|
390
329
|
|
391
|
-
|
392
|
-
|
330
|
+
console.error(value.toString());
|
331
|
+
} else if (value.type === "unauthorized") {
|
332
|
+
options.onUnauthorized?.();
|
393
333
|
|
394
|
-
|
334
|
+
console.error(value.toString());
|
335
|
+
} else if (value.type === "loaded") {
|
336
|
+
listener(value.value as Resolved<V, R>, unsubscribe);
|
337
|
+
}
|
338
|
+
};
|
395
339
|
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
.then(() =>
|
402
|
-
|
403
|
-
console.error(
|
404
|
-
"Failed to load / subscribe to CoValue",
|
405
|
-
e,
|
406
|
-
e instanceof Error ? e.stack : undefined,
|
407
|
-
);
|
408
|
-
options.onUnavailable?.();
|
340
|
+
let shouldDefer = !options.syncResolution;
|
341
|
+
|
342
|
+
rootNode.setListener((value) => {
|
343
|
+
if (shouldDefer) {
|
344
|
+
shouldDefer = false;
|
345
|
+
Promise.resolve().then(() => {
|
346
|
+
handleUpdate(value);
|
409
347
|
});
|
410
|
-
|
348
|
+
} else {
|
349
|
+
handleUpdate(value);
|
350
|
+
}
|
351
|
+
});
|
411
352
|
|
412
|
-
|
353
|
+
function unsubscribe() {
|
413
354
|
unsubscribed = true;
|
414
|
-
|
415
|
-
}
|
355
|
+
rootNode.destroy();
|
356
|
+
}
|
357
|
+
|
358
|
+
return unsubscribe;
|
416
359
|
}
|
417
360
|
|
418
361
|
export function createCoValueObservable<
|
@@ -248,7 +248,7 @@ export async function createJazzContext<Acc extends Account>(options: {
|
|
248
248
|
await authSecretStorage.setWithoutNotify({
|
249
249
|
accountID: context.account.id,
|
250
250
|
secretSeed,
|
251
|
-
accountSecret: context.node.
|
251
|
+
accountSecret: context.node.getCurrentAgent().agentSecret,
|
252
252
|
provider: "anonymous",
|
253
253
|
});
|
254
254
|
}
|
@@ -268,11 +268,10 @@ export async function createAnonymousJazzContext({
|
|
268
268
|
crypto: CryptoProvider;
|
269
269
|
}): Promise<JazzContextWithAgent> {
|
270
270
|
const agentSecret = crypto.newRandomAgentSecret();
|
271
|
-
const rawAgent = new ControlledAgent(agentSecret, crypto);
|
272
271
|
|
273
272
|
const node = new LocalNode(
|
274
|
-
|
275
|
-
crypto.newRandomSessionID(
|
273
|
+
agentSecret,
|
274
|
+
crypto.newRandomSessionID(crypto.getAgentID(agentSecret)),
|
276
275
|
crypto,
|
277
276
|
);
|
278
277
|
|
@@ -12,11 +12,11 @@ export function createInviteLink<C extends CoValue>(
|
|
12
12
|
const coValueCore = value._raw.core;
|
13
13
|
let currentCoValue = coValueCore;
|
14
14
|
|
15
|
-
while (currentCoValue.header.ruleset.type === "ownedByGroup") {
|
15
|
+
while (currentCoValue.verified.header.ruleset.type === "ownedByGroup") {
|
16
16
|
currentCoValue = currentCoValue.getGroup().core;
|
17
17
|
}
|
18
18
|
|
19
|
-
const { ruleset, meta } = currentCoValue.header;
|
19
|
+
const { ruleset, meta } = currentCoValue.verified.header;
|
20
20
|
|
21
21
|
if (ruleset.type !== "group" || meta?.type === "account") {
|
22
22
|
throw new Error("Can't create invite link for object without group");
|
@@ -1,4 +1,3 @@
|
|
1
|
-
import { type CoID, RawAccount, type RawCoValue, RawGroup } from "cojson";
|
2
1
|
import { type Account } from "../coValues/account.js";
|
3
2
|
import type {
|
4
3
|
AnonymousJazzAgent,
|
@@ -7,153 +6,61 @@ import type {
|
|
7
6
|
RefEncoded,
|
8
7
|
UnCo,
|
9
8
|
} from "../internal.js";
|
10
|
-
import {
|
11
|
-
|
12
|
-
isRefEncoded,
|
13
|
-
subscriptionsScopes,
|
14
|
-
} from "../internal.js";
|
15
|
-
import { coValuesCache } from "../lib/cache.js";
|
16
|
-
|
17
|
-
const TRACE_ACCESSES = false;
|
9
|
+
import { isRefEncoded } from "../internal.js";
|
10
|
+
import { accessChildById, getSubscriptionScope } from "../subscribe/index.js";
|
18
11
|
|
19
12
|
export class Ref<out V extends CoValue> {
|
20
13
|
constructor(
|
21
14
|
readonly id: ID<V>,
|
22
15
|
readonly controlledAccount: Account | AnonymousJazzAgent,
|
23
16
|
readonly schema: RefEncoded<V>,
|
17
|
+
readonly parent: CoValue,
|
24
18
|
) {
|
25
19
|
if (!isRefEncoded(schema)) {
|
26
20
|
throw new Error("Ref must be constructed with a ref schema");
|
27
21
|
}
|
28
22
|
}
|
29
23
|
|
30
|
-
|
31
|
-
|
32
|
-
? this.controlledAccount.node
|
33
|
-
: this.controlledAccount._raw.core.node;
|
34
|
-
}
|
35
|
-
|
36
|
-
hasReadAccess() {
|
37
|
-
const node = this.getNode();
|
38
|
-
|
39
|
-
const raw = node.getLoaded(this.id as unknown as CoID<RawCoValue>);
|
40
|
-
|
41
|
-
if (!raw) {
|
42
|
-
return true;
|
43
|
-
}
|
44
|
-
|
45
|
-
if (raw instanceof RawAccount || raw instanceof RawGroup) {
|
46
|
-
return true;
|
47
|
-
}
|
48
|
-
|
49
|
-
const group = raw.core.getGroup();
|
24
|
+
async load(): Promise<V | null> {
|
25
|
+
const subscriptionScope = getSubscriptionScope(this.parent);
|
50
26
|
|
51
|
-
|
52
|
-
if (node.account.id !== group.id) {
|
53
|
-
return false;
|
54
|
-
}
|
55
|
-
} else if (group.myRole() === undefined) {
|
56
|
-
return false;
|
57
|
-
}
|
58
|
-
|
59
|
-
return true;
|
60
|
-
}
|
61
|
-
|
62
|
-
getValueWithoutAccessCheck() {
|
63
|
-
const node = this.getNode();
|
64
|
-
const raw = node.getLoaded(this.id as unknown as CoID<RawCoValue>);
|
27
|
+
subscriptionScope.subscribeToId(this.id, this.schema);
|
65
28
|
|
66
|
-
|
67
|
-
return coValuesCache.get(raw, () =>
|
68
|
-
instantiateRefEncoded(this.schema, raw),
|
69
|
-
);
|
70
|
-
} else {
|
71
|
-
return null;
|
72
|
-
}
|
73
|
-
}
|
29
|
+
const node = subscriptionScope.childNodes.get(this.id);
|
74
30
|
|
75
|
-
|
76
|
-
if (!this.hasReadAccess()) {
|
31
|
+
if (!node) {
|
77
32
|
return null;
|
78
33
|
}
|
79
34
|
|
80
|
-
|
81
|
-
}
|
82
|
-
|
83
|
-
private async loadHelper(): Promise<V | "unavailable"> {
|
84
|
-
const node =
|
85
|
-
"node" in this.controlledAccount
|
86
|
-
? this.controlledAccount.node
|
87
|
-
: this.controlledAccount._raw.core.node;
|
88
|
-
const raw = await node.load(this.id as unknown as CoID<RawCoValue>);
|
89
|
-
if (raw === "unavailable") {
|
90
|
-
return "unavailable";
|
91
|
-
} else {
|
92
|
-
return new Ref(this.id, this.controlledAccount, this.schema).value!;
|
93
|
-
}
|
94
|
-
}
|
95
|
-
|
96
|
-
syncLoad(): V | undefined {
|
97
|
-
const node =
|
98
|
-
"node" in this.controlledAccount
|
99
|
-
? this.controlledAccount.node
|
100
|
-
: this.controlledAccount._raw.core.node;
|
101
|
-
|
102
|
-
const entry = node.coValuesStore.get(
|
103
|
-
this.id as unknown as CoID<RawCoValue>,
|
104
|
-
);
|
105
|
-
|
106
|
-
if (entry.highLevelState === "available") {
|
107
|
-
return new Ref(this.id, this.controlledAccount, this.schema).value!;
|
108
|
-
}
|
109
|
-
|
110
|
-
return undefined;
|
111
|
-
}
|
35
|
+
const value = node.value;
|
112
36
|
|
113
|
-
|
114
|
-
|
115
|
-
if (result === "unavailable") {
|
116
|
-
return undefined;
|
37
|
+
if (value?.type === "loaded") {
|
38
|
+
return value.value as V;
|
117
39
|
} else {
|
118
|
-
return
|
40
|
+
return new Promise((resolve) => {
|
41
|
+
const unsubscribe = node.subscribe((value) => {
|
42
|
+
if (value?.type === "loaded") {
|
43
|
+
unsubscribe();
|
44
|
+
resolve(value.value as V);
|
45
|
+
} else if (value?.type === "unavailable") {
|
46
|
+
unsubscribe();
|
47
|
+
resolve(null);
|
48
|
+
} else if (value?.type === "unauthorized") {
|
49
|
+
unsubscribe();
|
50
|
+
resolve(null);
|
51
|
+
}
|
52
|
+
});
|
53
|
+
});
|
119
54
|
}
|
120
55
|
}
|
121
56
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
subScope?.onRefAccessedOrSet(fromScopeValue.id, this.id);
|
126
|
-
TRACE_ACCESSES &&
|
127
|
-
console.log(subScope?.scopeID, "accessing", fromScopeValue, key, this.id);
|
128
|
-
|
129
|
-
if (this.value && subScope) {
|
130
|
-
subscriptionsScopes.set(this.value, subScope);
|
131
|
-
}
|
132
|
-
|
133
|
-
if (subScope) {
|
134
|
-
const cached = subScope.cachedValues[this.id];
|
135
|
-
if (cached) {
|
136
|
-
TRACE_ACCESSES && console.log("cached", cached);
|
137
|
-
return cached as V;
|
138
|
-
} else if (this.value !== null) {
|
139
|
-
const freshValueInstance = instantiateRefEncoded(
|
140
|
-
this.schema,
|
141
|
-
this.value._raw,
|
142
|
-
);
|
143
|
-
TRACE_ACCESSES && console.log("freshValueInstance", freshValueInstance);
|
144
|
-
subScope.cachedValues[this.id] = freshValueInstance;
|
145
|
-
subscriptionsScopes.set(freshValueInstance, subScope);
|
146
|
-
return freshValueInstance as V;
|
147
|
-
} else {
|
148
|
-
return null;
|
149
|
-
}
|
150
|
-
} else {
|
151
|
-
return this.value;
|
152
|
-
}
|
57
|
+
get value(): V | null | undefined {
|
58
|
+
return accessChildById(this.parent, this.id, this.schema);
|
153
59
|
}
|
154
60
|
}
|
155
61
|
|
156
62
|
export function makeRefs<Keys extends string | number>(
|
63
|
+
parent: CoValue,
|
157
64
|
getIdForKey: (key: Keys) => ID<CoValue> | undefined,
|
158
65
|
getKeysWithIds: () => Keys[],
|
159
66
|
controlledAccount: Account | AnonymousJazzAgent,
|
@@ -175,6 +82,7 @@ export function makeRefs<Keys extends string | number>(
|
|
175
82
|
getIdForKey(key)!,
|
176
83
|
controlledAccount,
|
177
84
|
refSchemaForKey(key),
|
85
|
+
parent,
|
178
86
|
);
|
179
87
|
}
|
180
88
|
};
|
@@ -189,6 +97,7 @@ export function makeRefs<Keys extends string | number>(
|
|
189
97
|
id as ID<CoValue>,
|
190
98
|
controlledAccount,
|
191
99
|
refSchemaForKey(key as Keys),
|
100
|
+
parent,
|
192
101
|
);
|
193
102
|
},
|
194
103
|
ownKeys() {
|
package/src/internal.ts
CHANGED
@@ -7,8 +7,7 @@ export * from "./implementation/anonymousJazzAgent.js";
|
|
7
7
|
export * from "./implementation/errors.js";
|
8
8
|
export * from "./implementation/refs.js";
|
9
9
|
export * from "./implementation/schema.js";
|
10
|
-
export * from "./
|
11
|
-
|
10
|
+
export * from "./subscribe/index.js";
|
12
11
|
export * from "./implementation/createContext.js";
|
13
12
|
|
14
13
|
import "./implementation/devtoolsFormatters.js";
|
@@ -0,0 +1,71 @@
|
|
1
|
+
import { CoValueCore, LocalNode, RawCoMap, RawCoValue } from "cojson";
|
2
|
+
|
3
|
+
export class CoValueCoreSubscription {
|
4
|
+
_unsubscribe: () => void = () => {};
|
5
|
+
unsubscribed = false;
|
6
|
+
|
7
|
+
value: RawCoMap | undefined;
|
8
|
+
|
9
|
+
constructor(
|
10
|
+
public node: LocalNode,
|
11
|
+
public id: string,
|
12
|
+
public listener: (value: RawCoValue | "unavailable") => void,
|
13
|
+
) {
|
14
|
+
const entry = this.node.getCoValue(this.id as any);
|
15
|
+
|
16
|
+
if (entry?.isAvailable()) {
|
17
|
+
this.subscribe(entry.getCurrentContent());
|
18
|
+
} else {
|
19
|
+
this.node.loadCoValueCore(this.id as any).then((value) => {
|
20
|
+
if (this.unsubscribed) return;
|
21
|
+
|
22
|
+
if (value.isAvailable()) {
|
23
|
+
this.subscribe(value.getCurrentContent());
|
24
|
+
} else {
|
25
|
+
this.listener("unavailable");
|
26
|
+
this.subscribeToState();
|
27
|
+
}
|
28
|
+
});
|
29
|
+
}
|
30
|
+
}
|
31
|
+
|
32
|
+
subscribeToState() {
|
33
|
+
const entry = this.node.getCoValue(this.id as any);
|
34
|
+
const handleStateChange = (
|
35
|
+
core: CoValueCore,
|
36
|
+
unsubFromStateChange: () => void,
|
37
|
+
) => {
|
38
|
+
if (this.unsubscribed) {
|
39
|
+
unsubFromStateChange();
|
40
|
+
return;
|
41
|
+
}
|
42
|
+
|
43
|
+
if (core.isAvailable()) {
|
44
|
+
this.subscribe(core.getCurrentContent());
|
45
|
+
unsubFromStateChange();
|
46
|
+
}
|
47
|
+
};
|
48
|
+
|
49
|
+
const unsubFromStateChange = entry.subscribe(handleStateChange);
|
50
|
+
|
51
|
+
this._unsubscribe = () => {
|
52
|
+
unsubFromStateChange();
|
53
|
+
};
|
54
|
+
}
|
55
|
+
|
56
|
+
subscribe(value: RawCoValue) {
|
57
|
+
if (this.unsubscribed) return;
|
58
|
+
|
59
|
+
this._unsubscribe = value.subscribe((value) => {
|
60
|
+
this.listener(value);
|
61
|
+
});
|
62
|
+
|
63
|
+
this.listener(value);
|
64
|
+
}
|
65
|
+
|
66
|
+
unsubscribe() {
|
67
|
+
if (this.unsubscribed) return;
|
68
|
+
this.unsubscribed = true;
|
69
|
+
this._unsubscribe();
|
70
|
+
}
|
71
|
+
}
|
@@ -0,0 +1,48 @@
|
|
1
|
+
import type { CoValue, ID } from "../internal.js";
|
2
|
+
|
3
|
+
export class JazzError {
|
4
|
+
constructor(
|
5
|
+
public id: ID<CoValue> | undefined,
|
6
|
+
public type: "unavailable" | "unauthorized",
|
7
|
+
public issues: JazzErrorIssue[],
|
8
|
+
) {}
|
9
|
+
|
10
|
+
toString() {
|
11
|
+
return this.issues
|
12
|
+
.map((issue) => {
|
13
|
+
let message = `${issue.message}`;
|
14
|
+
|
15
|
+
if (this.id) {
|
16
|
+
message += ` from ${this.id}`;
|
17
|
+
}
|
18
|
+
|
19
|
+
if (issue.path.length > 0) {
|
20
|
+
message += ` on path ${issue.path.join(".")}`;
|
21
|
+
}
|
22
|
+
|
23
|
+
return message;
|
24
|
+
})
|
25
|
+
.join("\n");
|
26
|
+
}
|
27
|
+
|
28
|
+
prependPath(item: string) {
|
29
|
+
if (this.issues.length === 0) {
|
30
|
+
return this;
|
31
|
+
}
|
32
|
+
|
33
|
+
const issues = this.issues.map((issue) => {
|
34
|
+
return {
|
35
|
+
...issue,
|
36
|
+
path: [item].concat(issue.path),
|
37
|
+
};
|
38
|
+
});
|
39
|
+
|
40
|
+
return new JazzError(this.id, this.type, issues);
|
41
|
+
}
|
42
|
+
}
|
43
|
+
export type JazzErrorIssue = {
|
44
|
+
code: "unavailable" | "unauthorized" | "validationError";
|
45
|
+
message: string;
|
46
|
+
params: Record<string, any>;
|
47
|
+
path: string[];
|
48
|
+
};
|