cojson 0.2.2 → 0.3.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.cjs +1 -0
- package/dist/account.d.ts +8 -8
- package/dist/account.js +2 -2
- package/dist/account.js.map +1 -1
- package/dist/coValue.d.ts +22 -27
- package/dist/coValue.js +21 -0
- package/dist/coValue.js.map +1 -1
- package/dist/coValueCore.d.ts +7 -7
- package/dist/coValueCore.js +11 -14
- package/dist/coValueCore.js.map +1 -1
- package/dist/coValues/coList.d.ts +107 -42
- package/dist/coValues/coList.js +163 -72
- package/dist/coValues/coList.js.map +1 -1
- package/dist/coValues/coMap.d.ts +109 -50
- package/dist/coValues/coMap.js +161 -109
- package/dist/coValues/coMap.js.map +1 -1
- package/dist/coValues/coStream.d.ts +78 -33
- package/dist/coValues/coStream.js +134 -53
- package/dist/coValues/coStream.js.map +1 -1
- package/dist/crypto.d.ts +8 -3
- package/dist/crypto.js +6 -6
- package/dist/crypto.js.map +1 -1
- package/dist/group.d.ts +59 -23
- package/dist/group.js +83 -25
- package/dist/group.js.map +1 -1
- package/dist/index.d.ts +14 -11
- package/dist/index.js +8 -8
- package/dist/index.js.map +1 -1
- package/dist/{node.d.ts → localNode.d.ts} +23 -11
- package/dist/{node.js → localNode.js} +80 -42
- package/dist/localNode.js.map +1 -0
- package/dist/media.d.ts +1 -2
- package/dist/permissions.js +6 -3
- package/dist/permissions.js.map +1 -1
- package/dist/queriedCoValues/queriedCoList.d.ts +66 -0
- package/dist/queriedCoValues/queriedCoList.js +120 -0
- package/dist/queriedCoValues/queriedCoList.js.map +1 -0
- package/dist/queriedCoValues/queriedCoMap.d.ts +47 -0
- package/dist/queriedCoValues/queriedCoMap.js +83 -0
- package/dist/queriedCoValues/queriedCoMap.js.map +1 -0
- package/dist/queriedCoValues/queriedCoStream.d.ts +40 -0
- package/dist/queriedCoValues/queriedCoStream.js +72 -0
- package/dist/queriedCoValues/queriedCoStream.js.map +1 -0
- package/dist/queries.d.ts +31 -0
- package/dist/queries.js +77 -0
- package/dist/queries.js.map +1 -0
- package/dist/sync.d.ts +1 -1
- package/dist/sync.js +1 -1
- package/dist/sync.js.map +1 -1
- package/dist/{testUtils.d.ts → tests/testUtils.d.ts} +9 -9
- package/dist/{testUtils.js → tests/testUtils.js} +9 -7
- package/dist/tests/testUtils.js.map +1 -0
- package/package.json +2 -2
- package/src/account.ts +6 -6
- package/src/coValue.ts +65 -34
- package/src/coValueCore.ts +18 -22
- package/src/coValues/coList.ts +272 -122
- package/src/coValues/coMap.ts +349 -152
- package/src/coValues/coStream.ts +258 -94
- package/src/crypto.ts +37 -24
- package/src/group.ts +112 -46
- package/src/index.ts +42 -30
- package/src/{node.ts → localNode.ts} +117 -66
- package/src/media.ts +1 -2
- package/src/permissions.ts +15 -18
- package/src/queriedCoValues/queriedCoList.ts +248 -0
- package/src/queriedCoValues/queriedCoMap.ts +180 -0
- package/src/queriedCoValues/queriedCoStream.ts +125 -0
- package/src/queries.ts +142 -0
- package/src/sync.ts +2 -2
- package/src/{account.test.ts → tests/account.test.ts} +6 -9
- package/src/{coValue.test.ts → tests/coValue.test.ts} +120 -114
- package/src/{coValueCore.test.ts → tests/coValueCore.test.ts} +7 -7
- package/src/{crypto.test.ts → tests/crypto.test.ts} +19 -21
- package/src/{group.test.ts → tests/group.test.ts} +2 -2
- package/src/{permissions.test.ts → tests/permissions.test.ts} +260 -247
- package/src/tests/queries.test.ts +318 -0
- package/src/{sync.test.ts → tests/sync.test.ts} +39 -39
- package/src/{testUtils.ts → tests/testUtils.ts} +10 -8
- package/dist/coValues/static.d.ts +0 -14
- package/dist/coValues/static.js +0 -20
- package/dist/coValues/static.js.map +0 -1
- package/dist/node.js.map +0 -1
- package/dist/testUtils.js.map +0 -1
- package/src/coValues/static.ts +0 -31
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { MutableCoMap } from "../coValues/coMap.js";
|
|
2
|
+
import { CoValueCore } from "../coValueCore.js";
|
|
3
|
+
import { Group } from "../group.js";
|
|
4
|
+
import { Account, AccountID, Profile, isAccountID } from "../account.js";
|
|
5
|
+
import { AnyCoMap, CoID, CoValue } from "../coValue.js";
|
|
6
|
+
import { TransactionID } from "../ids.js";
|
|
7
|
+
import { ValueOrSubQueried, QueryContext } from "../queries.js";
|
|
8
|
+
|
|
9
|
+
export type QueriedCoMap<M extends AnyCoMap> = {
|
|
10
|
+
[K in keyof M["_shape"] & string]: ValueOrSubQueried<M["_shape"][K]>;
|
|
11
|
+
} & QueriedCoMapBase<M>;
|
|
12
|
+
|
|
13
|
+
export type QueriedCoMapEdit<
|
|
14
|
+
M extends AnyCoMap,
|
|
15
|
+
K extends keyof M["_shape"]
|
|
16
|
+
> = {
|
|
17
|
+
by?: QueriedAccountAndProfile;
|
|
18
|
+
tx: TransactionID;
|
|
19
|
+
at: Date;
|
|
20
|
+
value: M["_shape"][K] extends CoValue
|
|
21
|
+
? CoID<M["_shape"][K]>
|
|
22
|
+
: Exclude<M["_shape"][K], CoValue>;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export class QueriedCoMapBase<M extends AnyCoMap> {
|
|
26
|
+
coMap!: M;
|
|
27
|
+
id!: CoID<M>;
|
|
28
|
+
type!: "comap";
|
|
29
|
+
|
|
30
|
+
/** @internal */
|
|
31
|
+
static newWithKVPairs<M extends AnyCoMap>(
|
|
32
|
+
coMap: M,
|
|
33
|
+
queryContext: QueryContext
|
|
34
|
+
): QueriedCoMap<M> {
|
|
35
|
+
const kv = {} as {
|
|
36
|
+
[K in keyof M["_shape"] & string]: ValueOrSubQueried<
|
|
37
|
+
M["_shape"][K]
|
|
38
|
+
>;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
if (coMap.meta?.type === "account") {
|
|
42
|
+
const profileID = coMap.get("profile");
|
|
43
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
44
|
+
(kv as any).profile =
|
|
45
|
+
profileID && queryContext.resolveValue(profileID);
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
47
|
+
(kv as any).isMe =
|
|
48
|
+
(coMap as unknown as Account).id ===
|
|
49
|
+
queryContext.node.account.id;
|
|
50
|
+
} else {
|
|
51
|
+
for (const key of coMap.keys()) {
|
|
52
|
+
const value = coMap.get(key);
|
|
53
|
+
|
|
54
|
+
if (value === undefined) continue;
|
|
55
|
+
|
|
56
|
+
kv[key as keyof typeof kv] = queryContext.resolveValue(
|
|
57
|
+
value
|
|
58
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
59
|
+
) as any;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return Object.assign(new QueriedCoMapBase(coMap, queryContext), kv);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** @internal */
|
|
67
|
+
constructor(coMap: M, queryContext: QueryContext) {
|
|
68
|
+
Object.defineProperties(this, {
|
|
69
|
+
coMap: { value: coMap, enumerable: false },
|
|
70
|
+
id: { value: coMap.id, enumerable: false },
|
|
71
|
+
type: { value: "comap", enumerable: false },
|
|
72
|
+
edits: {
|
|
73
|
+
value: Object.fromEntries(
|
|
74
|
+
coMap.keys().flatMap((key) => {
|
|
75
|
+
const edits = [...coMap.editsAt(key)].map((edit) => ({
|
|
76
|
+
by:
|
|
77
|
+
edit.by && isAccountID(edit.by)
|
|
78
|
+
? queryContext.resolveAccount(edit.by)
|
|
79
|
+
: undefined,
|
|
80
|
+
tx: edit.tx,
|
|
81
|
+
at: new Date(edit.at),
|
|
82
|
+
value:
|
|
83
|
+
edit.value &&
|
|
84
|
+
queryContext.resolveValue(edit.value),
|
|
85
|
+
}));
|
|
86
|
+
const lastEdit = edits[edits.length - 1];
|
|
87
|
+
if (!lastEdit) return [];
|
|
88
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
89
|
+
const editsAtKey = {
|
|
90
|
+
by: lastEdit.by,
|
|
91
|
+
tx: lastEdit.tx,
|
|
92
|
+
at: lastEdit.at,
|
|
93
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
94
|
+
value: lastEdit.value as any,
|
|
95
|
+
all: edits,
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
return [[key, editsAtKey]];
|
|
99
|
+
})
|
|
100
|
+
),
|
|
101
|
+
enumerable: false,
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
edits!: {
|
|
107
|
+
[K in keyof M["_shape"] & string]:
|
|
108
|
+
| (QueriedCoMapEdit<M, K> & {
|
|
109
|
+
all: QueriedCoMapEdit<M, K>[];
|
|
110
|
+
})
|
|
111
|
+
| undefined;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
get meta(): M["meta"] {
|
|
115
|
+
return this.coMap.meta;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
get group(): Group {
|
|
119
|
+
return this.coMap.group;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
get core(): CoValueCore {
|
|
123
|
+
return this.coMap.core;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
set<K extends keyof M["_shape"] & string>(
|
|
127
|
+
key: K,
|
|
128
|
+
value: M["_shape"][K] extends CoValue
|
|
129
|
+
? M["_shape"][K] | CoID<M["_shape"][K]>
|
|
130
|
+
: M["_shape"][K],
|
|
131
|
+
privacy?: "private" | "trusting"
|
|
132
|
+
): M;
|
|
133
|
+
set(
|
|
134
|
+
kv: {
|
|
135
|
+
[K in keyof M["_shape"] & string]?: M["_shape"][K] extends CoValue
|
|
136
|
+
? M["_shape"][K] | CoID<M["_shape"][K]>
|
|
137
|
+
: M["_shape"][K];
|
|
138
|
+
},
|
|
139
|
+
privacy?: "private" | "trusting"
|
|
140
|
+
): M;
|
|
141
|
+
set<K extends keyof M["_shape"] & string>(
|
|
142
|
+
...args:
|
|
143
|
+
| [
|
|
144
|
+
{
|
|
145
|
+
[K in keyof M["_shape"] &
|
|
146
|
+
string]?: M["_shape"][K] extends CoValue
|
|
147
|
+
? M["_shape"][K] | CoID<M["_shape"][K]>
|
|
148
|
+
: M["_shape"][K];
|
|
149
|
+
},
|
|
150
|
+
("private" | "trusting")?
|
|
151
|
+
]
|
|
152
|
+
| [
|
|
153
|
+
K,
|
|
154
|
+
M["_shape"][K] extends CoValue
|
|
155
|
+
? M["_shape"][K] | CoID<M["_shape"][K]>
|
|
156
|
+
: M["_shape"][K],
|
|
157
|
+
("private" | "trusting")?
|
|
158
|
+
]
|
|
159
|
+
): M {
|
|
160
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
161
|
+
return (this.coMap.set as Function)(...args);
|
|
162
|
+
}
|
|
163
|
+
delete(
|
|
164
|
+
key: keyof M["_shape"] & string,
|
|
165
|
+
privacy?: "private" | "trusting"
|
|
166
|
+
): M {
|
|
167
|
+
return this.coMap.delete(key, privacy);
|
|
168
|
+
}
|
|
169
|
+
mutate(
|
|
170
|
+
mutator: (mutable: MutableCoMap<M["_shape"], M["meta"]>) => void
|
|
171
|
+
): M {
|
|
172
|
+
return this.coMap.mutate(mutator);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export type QueriedAccountAndProfile = {
|
|
177
|
+
profile?: { name?: string; id: CoID<Profile> };
|
|
178
|
+
isMe?: boolean;
|
|
179
|
+
id: AccountID;
|
|
180
|
+
};
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { JsonValue } from "../jsonValue.js";
|
|
2
|
+
import { MutableCoStream } from "../coValues/coStream.js";
|
|
3
|
+
import { CoValueCore } from "../coValueCore.js";
|
|
4
|
+
import { Group } from "../group.js";
|
|
5
|
+
import { AccountID, isAccountID } from "../account.js";
|
|
6
|
+
import { AnyCoStream, CoID, CoValue } from "../coValue.js";
|
|
7
|
+
import { SessionID, TransactionID } from "../ids.js";
|
|
8
|
+
import { QueriedAccountAndProfile } from "./queriedCoMap.js";
|
|
9
|
+
import { ValueOrSubQueried, QueryContext } from "../queries.js";
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
export type QueriedCoStreamItems<Item extends JsonValue | CoValue> = {
|
|
13
|
+
last?: ValueOrSubQueried<Item>;
|
|
14
|
+
by?: QueriedAccountAndProfile;
|
|
15
|
+
tx?: TransactionID;
|
|
16
|
+
at?: Date;
|
|
17
|
+
all: {
|
|
18
|
+
value: ValueOrSubQueried<Item>;
|
|
19
|
+
by?: QueriedAccountAndProfile;
|
|
20
|
+
tx: TransactionID;
|
|
21
|
+
at: Date;
|
|
22
|
+
}[];
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export class QueriedCoStream<S extends AnyCoStream> {
|
|
26
|
+
coStream: S;
|
|
27
|
+
id: CoID<S>;
|
|
28
|
+
type = "costream" as const;
|
|
29
|
+
|
|
30
|
+
/** @internal */
|
|
31
|
+
constructor(coStream: S, queryContext: QueryContext) {
|
|
32
|
+
this.coStream = coStream;
|
|
33
|
+
this.id = coStream.id;
|
|
34
|
+
|
|
35
|
+
this.perSession = Object.fromEntries(
|
|
36
|
+
coStream.sessions().map((sessionID) => {
|
|
37
|
+
const items = [...coStream.itemsIn(sessionID)].map((item) => ({
|
|
38
|
+
by: item.by && isAccountID(item.by)
|
|
39
|
+
? queryContext.resolveAccount(item.by)
|
|
40
|
+
: undefined,
|
|
41
|
+
tx: item.tx,
|
|
42
|
+
at: new Date(item.at),
|
|
43
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
44
|
+
value: queryContext.resolveValue(item.value) as any,
|
|
45
|
+
}));
|
|
46
|
+
|
|
47
|
+
const lastItem = items[items.length - 1];
|
|
48
|
+
|
|
49
|
+
return [
|
|
50
|
+
sessionID,
|
|
51
|
+
{
|
|
52
|
+
last: lastItem?.value,
|
|
53
|
+
by: lastItem?.by,
|
|
54
|
+
tx: lastItem?.tx,
|
|
55
|
+
at: lastItem?.at,
|
|
56
|
+
all: items,
|
|
57
|
+
} satisfies QueriedCoStreamItems<S["_item"]>,
|
|
58
|
+
];
|
|
59
|
+
})
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
this.perAccount = Object.fromEntries(
|
|
63
|
+
[...coStream.accounts()].map((accountID) => {
|
|
64
|
+
const items = [...coStream.itemsBy(accountID)].map((item) => ({
|
|
65
|
+
by: item.by && isAccountID(item.by)
|
|
66
|
+
? queryContext.resolveAccount(item.by)
|
|
67
|
+
: undefined,
|
|
68
|
+
tx: item.tx,
|
|
69
|
+
at: new Date(item.at),
|
|
70
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
71
|
+
value: queryContext.resolveValue(item.value) as any,
|
|
72
|
+
}));
|
|
73
|
+
|
|
74
|
+
const lastItem = items[items.length - 1];
|
|
75
|
+
|
|
76
|
+
return [
|
|
77
|
+
accountID,
|
|
78
|
+
{
|
|
79
|
+
last: lastItem?.value,
|
|
80
|
+
by: lastItem?.by,
|
|
81
|
+
tx: lastItem?.tx,
|
|
82
|
+
at: lastItem?.at,
|
|
83
|
+
all: items,
|
|
84
|
+
} satisfies QueriedCoStreamItems<S["_item"]>,
|
|
85
|
+
];
|
|
86
|
+
})
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
this.me = isAccountID(queryContext.node.account.id)
|
|
90
|
+
? this.perAccount[queryContext.node.account.id]
|
|
91
|
+
: undefined;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
get meta(): S["meta"] {
|
|
95
|
+
return this.coStream.meta;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
get group(): Group {
|
|
99
|
+
return this.coStream.group;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
get core(): CoValueCore {
|
|
103
|
+
return this.coStream.core;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
me?: QueriedCoStreamItems<S["_item"]>;
|
|
107
|
+
perAccount: {
|
|
108
|
+
[account: AccountID]: QueriedCoStreamItems<S["_item"]>;
|
|
109
|
+
};
|
|
110
|
+
perSession: {
|
|
111
|
+
[session: SessionID]: QueriedCoStreamItems<S["_item"]>;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
push(
|
|
115
|
+
item: S["_item"] extends CoValue ? S["_item"] | CoID<S["_item"]> : S["_item"],
|
|
116
|
+
privacy?: "private" | "trusting"
|
|
117
|
+
): S {
|
|
118
|
+
return this.coStream.push(item, privacy);
|
|
119
|
+
}
|
|
120
|
+
mutate(
|
|
121
|
+
mutator: (mutable: MutableCoStream<S["_item"], S["meta"]>) => void
|
|
122
|
+
): S {
|
|
123
|
+
return this.coStream.mutate(mutator);
|
|
124
|
+
}
|
|
125
|
+
}
|
package/src/queries.ts
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { JsonValue } from "./jsonValue.js";
|
|
2
|
+
import { CoMap } from "./coValues/coMap.js";
|
|
3
|
+
import { CoStream } from "./coValues/coStream.js";
|
|
4
|
+
import { CoList } from "./coValues/coList.js";
|
|
5
|
+
import { AccountID } from "./account.js";
|
|
6
|
+
import { AnyCoList, AnyCoMap, AnyCoStream, CoID, CoValue } from "./coValue.js";
|
|
7
|
+
import { LocalNode } from "./localNode.js";
|
|
8
|
+
import {
|
|
9
|
+
QueriedAccountAndProfile,
|
|
10
|
+
QueriedCoMap,
|
|
11
|
+
QueriedCoMapBase,
|
|
12
|
+
} from "./queriedCoValues/queriedCoMap.js";
|
|
13
|
+
import { QueriedCoList } from "./queriedCoValues/queriedCoList.js";
|
|
14
|
+
import { QueriedCoStream } from "./queriedCoValues/queriedCoStream.js";
|
|
15
|
+
|
|
16
|
+
export type Queried<T extends CoValue> = T extends AnyCoMap
|
|
17
|
+
? QueriedCoMap<T>
|
|
18
|
+
: T extends AnyCoList
|
|
19
|
+
? QueriedCoList<T>
|
|
20
|
+
: T extends AnyCoStream
|
|
21
|
+
? T["meta"] extends { type: "binary" }
|
|
22
|
+
? never
|
|
23
|
+
: QueriedCoStream<T>
|
|
24
|
+
: never;
|
|
25
|
+
|
|
26
|
+
export type ValueOrSubQueried<
|
|
27
|
+
V extends JsonValue | CoValue | CoID<CoValue> | undefined
|
|
28
|
+
> = V extends CoID<infer C>
|
|
29
|
+
? Queried<C> | undefined
|
|
30
|
+
: V extends CoValue
|
|
31
|
+
? Queried<V> | undefined
|
|
32
|
+
: V;
|
|
33
|
+
|
|
34
|
+
export interface CleanupCallbackAndUsable {
|
|
35
|
+
(): void;
|
|
36
|
+
[Symbol.dispose]: () => void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export class QueryContext {
|
|
40
|
+
values: {
|
|
41
|
+
[id: CoID<CoValue>]: {
|
|
42
|
+
lastQueried: Queried<CoValue> | undefined;
|
|
43
|
+
unsubscribe: () => void;
|
|
44
|
+
};
|
|
45
|
+
} = {};
|
|
46
|
+
node: LocalNode;
|
|
47
|
+
onUpdate: () => void;
|
|
48
|
+
|
|
49
|
+
constructor(node: LocalNode, onUpdate: () => void) {
|
|
50
|
+
this.node = node;
|
|
51
|
+
this.onUpdate = onUpdate;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
getChildLastQueriedOrSubscribe<T extends CoValue>(valueID: CoID<T>) {
|
|
55
|
+
let value = this.values[valueID];
|
|
56
|
+
if (!value) {
|
|
57
|
+
value = {
|
|
58
|
+
lastQueried: undefined,
|
|
59
|
+
unsubscribe: query(valueID, this.node, (childQueried) => {
|
|
60
|
+
value!.lastQueried = childQueried as Queried<CoValue>;
|
|
61
|
+
this.onUpdate();
|
|
62
|
+
}),
|
|
63
|
+
};
|
|
64
|
+
this.values[valueID] = value;
|
|
65
|
+
}
|
|
66
|
+
return value.lastQueried as Queried<T> | undefined;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
resolveAccount(accountID: AccountID) {
|
|
70
|
+
return this.getChildLastQueriedOrSubscribe(
|
|
71
|
+
accountID
|
|
72
|
+
) as QueriedAccountAndProfile;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
resolveValue<T extends JsonValue>(
|
|
76
|
+
value: T
|
|
77
|
+
): T extends CoID<infer C> ? Queried<C> | undefined : T {
|
|
78
|
+
return (
|
|
79
|
+
typeof value === "string" && value.startsWith("co_")
|
|
80
|
+
? this.getChildLastQueriedOrSubscribe(value as CoID<CoValue>)
|
|
81
|
+
: value
|
|
82
|
+
) as T extends CoID<infer C> ? Queried<C> | undefined : T;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
cleanup() {
|
|
86
|
+
for (const child of Object.values(this.values)) {
|
|
87
|
+
child.unsubscribe();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function query<T extends CoValue>(
|
|
93
|
+
id: CoID<T>,
|
|
94
|
+
node: LocalNode,
|
|
95
|
+
callback: (queried: Queried<T> | undefined) => void,
|
|
96
|
+
parentContext?: QueryContext
|
|
97
|
+
): CleanupCallbackAndUsable {
|
|
98
|
+
console.log("querying", id);
|
|
99
|
+
|
|
100
|
+
const context = parentContext || new QueryContext(node, onUpdate);
|
|
101
|
+
|
|
102
|
+
const unsubscribe = node.subscribe(id, (update) => {
|
|
103
|
+
lastRootValue = update;
|
|
104
|
+
onUpdate();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
let lastRootValue: T | undefined;
|
|
108
|
+
|
|
109
|
+
function onUpdate() {
|
|
110
|
+
const rootValue = lastRootValue;
|
|
111
|
+
|
|
112
|
+
if (rootValue === undefined) {
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (rootValue instanceof CoMap) {
|
|
117
|
+
callback(
|
|
118
|
+
QueriedCoMapBase.newWithKVPairs(
|
|
119
|
+
rootValue,
|
|
120
|
+
context
|
|
121
|
+
) as Queried<T>
|
|
122
|
+
);
|
|
123
|
+
} else if (rootValue instanceof CoList) {
|
|
124
|
+
callback(new QueriedCoList(rootValue, context) as Queried<T>);
|
|
125
|
+
} else if (rootValue instanceof CoStream) {
|
|
126
|
+
if (rootValue.meta?.type === "binary") {
|
|
127
|
+
// Querying binary string not yet implemented
|
|
128
|
+
return {};
|
|
129
|
+
} else {
|
|
130
|
+
callback(new QueriedCoStream(rootValue, context) as Queried<T>);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const cleanup = function cleanup() {
|
|
136
|
+
context.cleanup();
|
|
137
|
+
unsubscribe();
|
|
138
|
+
} as CleanupCallbackAndUsable;
|
|
139
|
+
cleanup[Symbol.dispose] = cleanup;
|
|
140
|
+
|
|
141
|
+
return cleanup;
|
|
142
|
+
}
|
package/src/sync.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Signature } from "./crypto.js";
|
|
2
2
|
import { CoValueHeader, Transaction } from "./coValueCore.js";
|
|
3
3
|
import { CoValueCore } from "./coValueCore.js";
|
|
4
|
-
import { LocalNode } from "./
|
|
5
|
-
import { newLoadingState } from "./
|
|
4
|
+
import { LocalNode } from "./localNode.js";
|
|
5
|
+
import { newLoadingState } from "./localNode.js";
|
|
6
6
|
import {
|
|
7
7
|
ReadableStream,
|
|
8
8
|
WritableStream,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { newRandomSessionID } from "
|
|
2
|
-
import { cojsonReady } from "
|
|
3
|
-
import { LocalNode } from "
|
|
4
|
-
import { connectedPeers } from "
|
|
1
|
+
import { newRandomSessionID } from "../coValueCore.js";
|
|
2
|
+
import { cojsonReady } from "../index.js";
|
|
3
|
+
import { LocalNode } from "../localNode.js";
|
|
4
|
+
import { connectedPeers } from "../streamUtils.js";
|
|
5
5
|
|
|
6
6
|
beforeEach(async () => {
|
|
7
7
|
await cojsonReady;
|
|
@@ -19,9 +19,6 @@ test("Can create a node while creating a new account with profile", async () =>
|
|
|
19
19
|
expect(node.expectProfileLoaded(accountID).get("name")).toEqual(
|
|
20
20
|
"Hermes Puggington"
|
|
21
21
|
);
|
|
22
|
-
expect((await node.loadProfile(accountID)).get("name")).toEqual(
|
|
23
|
-
"Hermes Puggington"
|
|
24
|
-
);
|
|
25
22
|
});
|
|
26
23
|
|
|
27
24
|
test("A node with an account can create groups and and objects within them", async () => {
|
|
@@ -39,7 +36,7 @@ test("A node with an account can create groups and and objects within them", asy
|
|
|
39
36
|
|
|
40
37
|
expect(map.get("foo")).toEqual("bar");
|
|
41
38
|
|
|
42
|
-
expect(map.
|
|
39
|
+
expect(map.lastEditAt("foo")?.by).toEqual(accountID);
|
|
43
40
|
});
|
|
44
41
|
|
|
45
42
|
test("Can create account with one node, and then load it on another", async () => {
|
|
@@ -57,7 +54,7 @@ test("Can create account with one node, and then load it on another", async () =
|
|
|
57
54
|
|
|
58
55
|
const [node1asPeer, node2asPeer] = connectedPeers("node1", "node2", {trace: true, peer1role: "server", peer2role: "client"});
|
|
59
56
|
|
|
60
|
-
node.
|
|
57
|
+
node.syncManager.addPeer(node2asPeer);
|
|
61
58
|
|
|
62
59
|
const node2 = await LocalNode.withLoadedAccount(
|
|
63
60
|
accountID,
|