jazz-tools 0.7.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 +24 -0
- package/.turbo/turbo-build.log +24 -0
- package/CHANGELOG.md +42 -0
- package/LICENSE.txt +19 -0
- package/README.md +3 -0
- package/dist/coValueInterfaces.js +8 -0
- package/dist/coValueInterfaces.js.map +1 -0
- package/dist/coValues/account/account.js +11 -0
- package/dist/coValues/account/account.js.map +1 -0
- package/dist/coValues/account/accountOf.js +150 -0
- package/dist/coValues/account/accountOf.js.map +1 -0
- package/dist/coValues/account/migration.js +4 -0
- package/dist/coValues/account/migration.js.map +1 -0
- package/dist/coValues/coList/coList.js +2 -0
- package/dist/coValues/coList/coList.js.map +1 -0
- package/dist/coValues/coList/coListOf.js +235 -0
- package/dist/coValues/coList/coListOf.js.map +1 -0
- package/dist/coValues/coList/internalDocs.js +2 -0
- package/dist/coValues/coList/internalDocs.js.map +1 -0
- package/dist/coValues/coMap/coMap.js +2 -0
- package/dist/coValues/coMap/coMap.js.map +1 -0
- package/dist/coValues/coMap/coMapOf.js +262 -0
- package/dist/coValues/coMap/coMapOf.js.map +1 -0
- package/dist/coValues/coMap/internalDocs.js +2 -0
- package/dist/coValues/coMap/internalDocs.js.map +1 -0
- package/dist/coValues/coStream/coStream.js +2 -0
- package/dist/coValues/coStream/coStream.js.map +1 -0
- package/dist/coValues/coStream/coStreamOf.js +244 -0
- package/dist/coValues/coStream/coStreamOf.js.map +1 -0
- package/dist/coValues/construction.js +34 -0
- package/dist/coValues/construction.js.map +1 -0
- package/dist/coValues/extensions/imageDef.js +36 -0
- package/dist/coValues/extensions/imageDef.js.map +1 -0
- package/dist/coValues/group/group.js +2 -0
- package/dist/coValues/group/group.js.map +1 -0
- package/dist/coValues/group/groupOf.js +109 -0
- package/dist/coValues/group/groupOf.js.map +1 -0
- package/dist/coValues/resolution.js +66 -0
- package/dist/coValues/resolution.js.map +1 -0
- package/dist/errors.js +2 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/refs.js +95 -0
- package/dist/refs.js.map +1 -0
- package/dist/schemaHelpers.js +14 -0
- package/dist/schemaHelpers.js.map +1 -0
- package/dist/subscriptionScope.js +81 -0
- package/dist/subscriptionScope.js.map +1 -0
- package/dist/tests/coList.test.js +207 -0
- package/dist/tests/coList.test.js.map +1 -0
- package/dist/tests/coMap.test.js +238 -0
- package/dist/tests/coMap.test.js.map +1 -0
- package/dist/tests/coStream.test.js +263 -0
- package/dist/tests/coStream.test.js.map +1 -0
- package/dist/tests/types.test.js +33 -0
- package/dist/tests/types.test.js.map +1 -0
- package/package.json +23 -0
- package/src/coValueInterfaces.ts +105 -0
- package/src/coValues/account/account.ts +106 -0
- package/src/coValues/account/accountOf.ts +284 -0
- package/src/coValues/account/migration.ts +12 -0
- package/src/coValues/coList/coList.ts +57 -0
- package/src/coValues/coList/coListOf.ts +377 -0
- package/src/coValues/coList/internalDocs.ts +1 -0
- package/src/coValues/coMap/coMap.ts +110 -0
- package/src/coValues/coMap/coMapOf.ts +451 -0
- package/src/coValues/coMap/internalDocs.ts +1 -0
- package/src/coValues/coStream/coStream.ts +63 -0
- package/src/coValues/coStream/coStreamOf.ts +404 -0
- package/src/coValues/construction.ts +110 -0
- package/src/coValues/extensions/imageDef.ts +51 -0
- package/src/coValues/group/group.ts +27 -0
- package/src/coValues/group/groupOf.ts +183 -0
- package/src/coValues/resolution.ts +111 -0
- package/src/errors.ts +1 -0
- package/src/index.ts +68 -0
- package/src/refs.ts +128 -0
- package/src/schemaHelpers.ts +72 -0
- package/src/subscriptionScope.ts +118 -0
- package/src/tests/coList.test.ts +283 -0
- package/src/tests/coMap.test.ts +357 -0
- package/src/tests/coStream.test.ts +415 -0
- package/src/tests/types.test.ts +37 -0
- package/tsconfig.json +15 -0
@@ -0,0 +1,183 @@
|
|
1
|
+
import {
|
2
|
+
CoValueSchema,
|
3
|
+
ID,
|
4
|
+
inspect,
|
5
|
+
isCoValueSchema,
|
6
|
+
} from "../../coValueInterfaces.js";
|
7
|
+
import * as S from "@effect/schema/Schema";
|
8
|
+
import { AnyGroup, GroupSchema } from "./group.js";
|
9
|
+
import {
|
10
|
+
AnyAccount,
|
11
|
+
ControlledAccount,
|
12
|
+
isControlledAccount,
|
13
|
+
} from "../account/account.js";
|
14
|
+
import { AST, Schema } from "@effect/schema";
|
15
|
+
import { constructorOfSchemaSym } from "../resolution.js";
|
16
|
+
import { pipeArguments } from "effect/Pipeable";
|
17
|
+
import { Everyone, RawGroup, Role } from "cojson";
|
18
|
+
import { ValueRef } from "../../refs.js";
|
19
|
+
import { controlledAccountFromNode } from "../account/accountOf.js";
|
20
|
+
import { SharedCoValueConstructor } from "../construction.js";
|
21
|
+
|
22
|
+
export function GroupOf<
|
23
|
+
P extends CoValueSchema | S.Schema<null>,
|
24
|
+
R extends CoValueSchema | S.Schema<null>,
|
25
|
+
>(fields: { profile: P; root: R }) {
|
26
|
+
class GroupOfProfileAndRoot
|
27
|
+
extends SharedCoValueConstructor
|
28
|
+
implements AnyGroup<P, R>
|
29
|
+
{
|
30
|
+
static get ast() {
|
31
|
+
return AST.setAnnotation(
|
32
|
+
Schema.instanceOf(this).ast,
|
33
|
+
constructorOfSchemaSym,
|
34
|
+
this
|
35
|
+
);
|
36
|
+
}
|
37
|
+
static [Schema.TypeId]: Schema.Schema.Variance<
|
38
|
+
AnyGroup<P, R> & GroupOfProfileAndRoot,
|
39
|
+
AnyGroup<P, R> & GroupOfProfileAndRoot,
|
40
|
+
never
|
41
|
+
>[Schema.TypeId];
|
42
|
+
static pipe() {
|
43
|
+
// eslint-disable-next-line prefer-rest-params
|
44
|
+
return pipeArguments(this, arguments);
|
45
|
+
}
|
46
|
+
static type = "Group" as const;
|
47
|
+
|
48
|
+
id!: ID<this>;
|
49
|
+
_type!: "Group";
|
50
|
+
_owner!: AnyAccount | AnyGroup;
|
51
|
+
_refs!: AnyGroup<P, R>["_refs"];
|
52
|
+
_raw!: RawGroup;
|
53
|
+
_loadedAs!: ControlledAccount;
|
54
|
+
_schema!: typeof GroupOfProfileAndRoot;
|
55
|
+
|
56
|
+
constructor(options: { owner: AnyAccount | AnyGroup });
|
57
|
+
constructor(init: any, options: { fromRaw: RawGroup });
|
58
|
+
constructor(init: undefined, options: { owner: AnyAccount | AnyGroup });
|
59
|
+
constructor(
|
60
|
+
init: undefined | { owner: AnyAccount | AnyGroup },
|
61
|
+
options?: { fromRaw: RawGroup } | { owner: AnyAccount | AnyGroup }
|
62
|
+
) {
|
63
|
+
super();
|
64
|
+
let raw: RawGroup;
|
65
|
+
|
66
|
+
if (options && "fromRaw" in options) {
|
67
|
+
raw = options.fromRaw;
|
68
|
+
} else {
|
69
|
+
const initOwner = options?.owner || init?.owner;
|
70
|
+
if (!initOwner) throw new Error("No owner provided");
|
71
|
+
if (isControlledAccount(initOwner)) {
|
72
|
+
const rawOwner = initOwner._raw;
|
73
|
+
raw = rawOwner.createGroup();
|
74
|
+
} else {
|
75
|
+
throw new Error(
|
76
|
+
"Can only construct group as a controlled account"
|
77
|
+
);
|
78
|
+
}
|
79
|
+
}
|
80
|
+
|
81
|
+
const refs = {
|
82
|
+
get profile() {
|
83
|
+
if (isCoValueSchema(fields.profile)) {
|
84
|
+
const profileID = raw.get("profile");
|
85
|
+
return (
|
86
|
+
profileID &&
|
87
|
+
new ValueRef(
|
88
|
+
profileID as unknown as ID<
|
89
|
+
Schema.Schema.To<typeof fields.profile>
|
90
|
+
>,
|
91
|
+
controlledAccountFromNode(raw.core.node),
|
92
|
+
fields.profile
|
93
|
+
)
|
94
|
+
);
|
95
|
+
}
|
96
|
+
},
|
97
|
+
get root() {
|
98
|
+
if (isCoValueSchema(fields.root)) {
|
99
|
+
const rootID = raw.get("root");
|
100
|
+
return (
|
101
|
+
rootID &&
|
102
|
+
new ValueRef(
|
103
|
+
rootID as unknown as ID<
|
104
|
+
Schema.Schema.To<typeof fields.root>
|
105
|
+
>,
|
106
|
+
controlledAccountFromNode(raw.core.node),
|
107
|
+
fields.root
|
108
|
+
)
|
109
|
+
);
|
110
|
+
}
|
111
|
+
},
|
112
|
+
};
|
113
|
+
|
114
|
+
Object.defineProperties(this, {
|
115
|
+
id: { value: raw.id, enumerable: false },
|
116
|
+
_type: { value: "Group", enumerable: false },
|
117
|
+
_owner: { value: this, enumerable: false },
|
118
|
+
_refs: { value: refs, enumerable: false },
|
119
|
+
_raw: { value: raw, enumerable: false },
|
120
|
+
_loadedAs: {
|
121
|
+
get: () => controlledAccountFromNode(raw.core.node),
|
122
|
+
enumerable: false,
|
123
|
+
},
|
124
|
+
_schema: { value: GroupOfProfileAndRoot, enumerable: false },
|
125
|
+
});
|
126
|
+
}
|
127
|
+
|
128
|
+
static fromRaw(raw: RawGroup) {
|
129
|
+
return new GroupOfProfileAndRoot(undefined, {
|
130
|
+
fromRaw: raw,
|
131
|
+
});
|
132
|
+
}
|
133
|
+
|
134
|
+
get profile(): S.Schema.To<P> | undefined {
|
135
|
+
return this._refs.profile.accessFrom(this);
|
136
|
+
}
|
137
|
+
|
138
|
+
get root(): S.Schema.To<R> | undefined {
|
139
|
+
return this._refs.root.accessFrom(this);
|
140
|
+
}
|
141
|
+
|
142
|
+
addMember(member: AnyAccount | Everyone, role: Role) {
|
143
|
+
this._raw.addMember(
|
144
|
+
typeof member === "string" ? member : member._raw,
|
145
|
+
role
|
146
|
+
);
|
147
|
+
|
148
|
+
return this;
|
149
|
+
}
|
150
|
+
|
151
|
+
myRole(): Role | undefined {
|
152
|
+
return this._raw.myRole();
|
153
|
+
}
|
154
|
+
|
155
|
+
toJSON() {
|
156
|
+
return {
|
157
|
+
co: {
|
158
|
+
id: this.id,
|
159
|
+
type: this._type,
|
160
|
+
},
|
161
|
+
profile: this.profile?.toJSON(),
|
162
|
+
root: this.root?.toJSON(),
|
163
|
+
};
|
164
|
+
}
|
165
|
+
|
166
|
+
[inspect]() {
|
167
|
+
return this.toJSON();
|
168
|
+
}
|
169
|
+
|
170
|
+
static as<SubClass>() {
|
171
|
+
return this as unknown as GroupSchema<SubClass, P, R>;
|
172
|
+
}
|
173
|
+
}
|
174
|
+
|
175
|
+
return GroupOfProfileAndRoot as GroupSchema<GroupOfProfileAndRoot, P, R> & {
|
176
|
+
as<SubClass>(): GroupSchema<SubClass, P, R>;
|
177
|
+
};
|
178
|
+
}
|
179
|
+
|
180
|
+
export class Group extends GroupOf({
|
181
|
+
profile: S.null,
|
182
|
+
root: S.null,
|
183
|
+
}).as<Group>() {}
|
@@ -0,0 +1,111 @@
|
|
1
|
+
import { Schema, AST } from "@effect/schema";
|
2
|
+
import { RawCoValue } from "cojson";
|
3
|
+
import { CoValue, CoValueSchema } from "../coValueInterfaces.js";
|
4
|
+
|
5
|
+
// TODO: can we get rid of this because effect schema already has an annotation for the constructor?
|
6
|
+
export const constructorOfSchemaSym = Symbol.for("@jazz/constructorOfSymbol");
|
7
|
+
export type constructorOfSchemaSym = typeof constructorOfSchemaSym;
|
8
|
+
|
9
|
+
export function propertyIsCoValueSchema(
|
10
|
+
prop:
|
11
|
+
| Schema.Schema<any>
|
12
|
+
| Schema.PropertySignature<any, boolean, any, boolean, never>
|
13
|
+
): prop is CoValueSchema {
|
14
|
+
if ("propertySignatureAST" in prop) {
|
15
|
+
return astIsCoValueSchema(
|
16
|
+
(prop.propertySignatureAST as { from: AST.AST }).from
|
17
|
+
);
|
18
|
+
} else {
|
19
|
+
return astIsCoValueSchema((prop as Schema.Schema<unknown>).ast);
|
20
|
+
}
|
21
|
+
}
|
22
|
+
|
23
|
+
function astIsCoValueSchema(ast: AST.AST): boolean {
|
24
|
+
if (
|
25
|
+
(ast._tag === "Declaration") &&
|
26
|
+
ast.annotations[constructorOfSchemaSym]
|
27
|
+
) {
|
28
|
+
return true;
|
29
|
+
} else if (ast._tag === "Union") {
|
30
|
+
return ast.types.every(
|
31
|
+
(member) =>
|
32
|
+
member._tag === "UndefinedKeyword" || astIsCoValueSchema(member)
|
33
|
+
);
|
34
|
+
} else if (
|
35
|
+
ast._tag === "BooleanKeyword" ||
|
36
|
+
ast._tag === "NumberKeyword" ||
|
37
|
+
ast._tag === "StringKeyword" ||
|
38
|
+
ast._tag === "Literal" ||
|
39
|
+
ast._tag === "Tuple"
|
40
|
+
) {
|
41
|
+
return false;
|
42
|
+
} else if (ast._tag === "Refinement") {
|
43
|
+
return astIsCoValueSchema(ast.from);
|
44
|
+
} else if (ast._tag === "Transform") {
|
45
|
+
return astIsCoValueSchema(ast.from);
|
46
|
+
} else {
|
47
|
+
throw new Error(
|
48
|
+
`astIsCoValueSchema can't yet handle ${ast._tag}: ${JSON.stringify(
|
49
|
+
ast
|
50
|
+
)}`
|
51
|
+
);
|
52
|
+
}
|
53
|
+
}
|
54
|
+
|
55
|
+
export function getCoValueConstructorInProperty(
|
56
|
+
prop:
|
57
|
+
| Schema.Schema<unknown>
|
58
|
+
| Schema.PropertySignature<unknown, boolean, unknown, boolean, never>,
|
59
|
+
rawValue: RawCoValue
|
60
|
+
):
|
61
|
+
| (new (init: undefined, options: { fromRaw: RawCoValue }) => CoValue)
|
62
|
+
| undefined {
|
63
|
+
if ("propertySignatureAST" in prop) {
|
64
|
+
return getCoValueConstructorInAST(
|
65
|
+
(prop.propertySignatureAST as { from: AST.AST }).from,
|
66
|
+
rawValue
|
67
|
+
);
|
68
|
+
} else {
|
69
|
+
return getCoValueConstructorInAST(
|
70
|
+
(prop as Schema.Schema<unknown>).ast,
|
71
|
+
rawValue
|
72
|
+
);
|
73
|
+
}
|
74
|
+
}
|
75
|
+
|
76
|
+
// TODO (optimization): make this meta, so this creates a tailor-made function that will take a RawCoValue at call time
|
77
|
+
function getCoValueConstructorInAST(
|
78
|
+
ast: AST.AST,
|
79
|
+
rawValue: RawCoValue
|
80
|
+
):
|
81
|
+
| (new (init: undefined, options: { fromRaw: RawCoValue }) => CoValue)
|
82
|
+
| undefined {
|
83
|
+
if (
|
84
|
+
(ast._tag === "Declaration") &&
|
85
|
+
ast.annotations[constructorOfSchemaSym]
|
86
|
+
) {
|
87
|
+
return ast.annotations[constructorOfSchemaSym] as new (
|
88
|
+
init: undefined,
|
89
|
+
options: { fromRaw: RawCoValue }
|
90
|
+
) => CoValue;
|
91
|
+
} else if (ast._tag === "Union" && ast.types.length === 2) {
|
92
|
+
const [a, b] = ast.types;
|
93
|
+
if (a._tag === "UndefinedKeyword") {
|
94
|
+
return getCoValueConstructorInAST(b, rawValue);
|
95
|
+
} else if (b._tag === "UndefinedKeyword") {
|
96
|
+
return getCoValueConstructorInAST(a, rawValue);
|
97
|
+
} else {
|
98
|
+
throw new Error(
|
99
|
+
`getCoValueConstructorInAST can't yet handle Union of: ${JSON.stringify(
|
100
|
+
ast.types
|
101
|
+
)}`
|
102
|
+
);
|
103
|
+
}
|
104
|
+
} else {
|
105
|
+
throw new Error(
|
106
|
+
`getCoValueConstructorInAST can't yet handle ${
|
107
|
+
ast._tag
|
108
|
+
}: ${JSON.stringify(ast)}`
|
109
|
+
);
|
110
|
+
}
|
111
|
+
}
|
package/src/errors.ts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
export type UnavailableError = "unavailable";
|
package/src/index.ts
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
import { AccountOf } from "./coValues/account/accountOf.js";
|
2
|
+
import { CoListOf } from "./coValues/coList/coListOf.js";
|
3
|
+
import { CoMapOf } from "./coValues/coMap/coMapOf.js";
|
4
|
+
import {
|
5
|
+
BinaryCoStreamImpl,
|
6
|
+
CoStreamOf,
|
7
|
+
} from "./coValues/coStream/coStreamOf.js";
|
8
|
+
import { ImageDefinition } from "./coValues/extensions/imageDef.js";
|
9
|
+
import { GroupOf } from "./coValues/group/groupOf.js";
|
10
|
+
|
11
|
+
/** @hidden */
|
12
|
+
export * as S from "@effect/schema/Schema";
|
13
|
+
|
14
|
+
/** @category Schemas & CoValues - Schema definers */
|
15
|
+
export const Co = {
|
16
|
+
map: CoMapOf,
|
17
|
+
list: CoListOf,
|
18
|
+
stream: CoStreamOf,
|
19
|
+
binaryStream: BinaryCoStreamImpl,
|
20
|
+
account: AccountOf,
|
21
|
+
group: GroupOf,
|
22
|
+
media: {
|
23
|
+
imageDef: ImageDefinition
|
24
|
+
}
|
25
|
+
};
|
26
|
+
|
27
|
+
/** @category Internal types */
|
28
|
+
export {
|
29
|
+
cojsonReady as jazzReady,
|
30
|
+
InviteSecret,
|
31
|
+
Peer,
|
32
|
+
SessionID,
|
33
|
+
AgentID,
|
34
|
+
SyncMessage,
|
35
|
+
cojsonInternals,
|
36
|
+
MAX_RECOMMENDED_TX_SIZE,
|
37
|
+
} from "cojson";
|
38
|
+
|
39
|
+
export { ID, CoValue, CoValueSchema } from "./coValueInterfaces.js";
|
40
|
+
|
41
|
+
|
42
|
+
export { CoMap, CoMapSchema } from "./coValues/coMap/coMap.js";
|
43
|
+
/** @category Schemas & CoValues - CoMap */
|
44
|
+
export * as CoMapInternals from "./coValues/coMap/internalDocs.js";
|
45
|
+
|
46
|
+
export { CoList, CoListSchema } from "./coValues/coList/coList.js";
|
47
|
+
/** @category Schemas & CoValues - CoList */
|
48
|
+
export * as CoListInternals from "./coValues/coList/internalDocs.js";
|
49
|
+
|
50
|
+
export {
|
51
|
+
CoStream,
|
52
|
+
CoStreamSchema,
|
53
|
+
BinaryCoStream,
|
54
|
+
} from "./coValues/coStream/coStream.js";
|
55
|
+
|
56
|
+
export {
|
57
|
+
AnyAccount,
|
58
|
+
ControlledAccount,
|
59
|
+
AccountSchema,
|
60
|
+
controlledAccountSym,
|
61
|
+
} from "./coValues/account/account.js";
|
62
|
+
|
63
|
+
export { AccountMigration } from "./coValues/account/migration.js";
|
64
|
+
export { Account, BaseProfile } from "./coValues/account/accountOf.js";
|
65
|
+
export { AnyGroup } from "./coValues/group/group.js";
|
66
|
+
export { Group } from "./coValues/group/groupOf.js";
|
67
|
+
|
68
|
+
export { ImageDefinition } from "./coValues/extensions/imageDef.js";
|
package/src/refs.ts
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
import { Effect } from "effect";
|
2
|
+
import { CoValue, ID } from "./coValueInterfaces.js";
|
3
|
+
import { ControlledAccount } from "./coValues/account/account.js";
|
4
|
+
import { CoID, RawCoValue } from "cojson";
|
5
|
+
import { UnavailableError } from "./errors.js";
|
6
|
+
import { getCoValueConstructorInProperty } from "./coValues/resolution.js";
|
7
|
+
import { PropDef } from "./schemaHelpers.js";
|
8
|
+
import { subscriptionsScopes } from "./subscriptionScope.js";
|
9
|
+
|
10
|
+
export class ValueRef<V extends CoValue> {
|
11
|
+
private cachedValue: V | undefined;
|
12
|
+
|
13
|
+
constructor(
|
14
|
+
readonly id: ID<V>,
|
15
|
+
readonly controlledAccount: ControlledAccount,
|
16
|
+
readonly propDef: PropDef<any>
|
17
|
+
) {}
|
18
|
+
|
19
|
+
get value() {
|
20
|
+
if (this.cachedValue) return this.cachedValue;
|
21
|
+
// TODO: cache it for object identity!!!
|
22
|
+
const raw = this.controlledAccount._raw.core.node.getLoaded(
|
23
|
+
this.id as unknown as CoID<RawCoValue>
|
24
|
+
);
|
25
|
+
if (raw) {
|
26
|
+
const Constructor = getCoValueConstructorInProperty(
|
27
|
+
this.propDef,
|
28
|
+
raw
|
29
|
+
);
|
30
|
+
if (!Constructor) {
|
31
|
+
throw new Error(
|
32
|
+
"Couldn't extract CoValue constructor from property definition"
|
33
|
+
);
|
34
|
+
}
|
35
|
+
const value = new Constructor(undefined, { fromRaw: raw }) as V;
|
36
|
+
this.cachedValue = value;
|
37
|
+
return value;
|
38
|
+
}
|
39
|
+
}
|
40
|
+
|
41
|
+
loadEf() {
|
42
|
+
return Effect.async<V, UnavailableError>((fulfill) => {
|
43
|
+
this.loadHelper()
|
44
|
+
.then((value) => {
|
45
|
+
if (value === "unavailable") {
|
46
|
+
fulfill(Effect.fail<UnavailableError>("unavailable"));
|
47
|
+
} else {
|
48
|
+
fulfill(Effect.succeed(value));
|
49
|
+
}
|
50
|
+
})
|
51
|
+
.catch((e) => {
|
52
|
+
fulfill(Effect.die(e));
|
53
|
+
});
|
54
|
+
});
|
55
|
+
}
|
56
|
+
|
57
|
+
private async loadHelper(options?: {onProgress: (p: number) => void}): Promise<V | "unavailable"> {
|
58
|
+
const raw = await this.controlledAccount._raw.core.node.load(
|
59
|
+
this.id as unknown as CoID<RawCoValue>,
|
60
|
+
options?.onProgress
|
61
|
+
);
|
62
|
+
if (raw === "unavailable") {
|
63
|
+
return "unavailable";
|
64
|
+
} else {
|
65
|
+
return new ValueRef(this.id, this.controlledAccount, this.propDef)
|
66
|
+
.value!;
|
67
|
+
}
|
68
|
+
}
|
69
|
+
|
70
|
+
async load(options?: {onProgress: (p: number) => void}): Promise<V | undefined> {
|
71
|
+
const result = await this.loadHelper(options);
|
72
|
+
if (result === "unavailable") {
|
73
|
+
return undefined;
|
74
|
+
} else {
|
75
|
+
return result;
|
76
|
+
}
|
77
|
+
}
|
78
|
+
|
79
|
+
accessFrom(fromScopeValue: CoValue): V | undefined {
|
80
|
+
const subScope = subscriptionsScopes.get(fromScopeValue);
|
81
|
+
|
82
|
+
subScope?.onRefAccessedOrSet(this.id);
|
83
|
+
|
84
|
+
if (this.value && subScope) {
|
85
|
+
subscriptionsScopes.set(this.value, subScope);
|
86
|
+
}
|
87
|
+
|
88
|
+
return this.value;
|
89
|
+
}
|
90
|
+
}
|
91
|
+
|
92
|
+
export function makeRefs<F extends { [key: string | number]: CoValue }>(
|
93
|
+
getIdForKey: <K extends keyof F>(key: K) => F[K]["id"] | undefined,
|
94
|
+
getKeysWithIds: () => (keyof F)[],
|
95
|
+
controlledAccount: ControlledAccount,
|
96
|
+
propDefForKey: <K extends keyof F>(key: K) => PropDef<F[K]>
|
97
|
+
): { [K in keyof F]: ValueRef<F[K]> } {
|
98
|
+
const refs = {} as { [K in keyof F]: ValueRef<F[K]> };
|
99
|
+
return new Proxy(refs, {
|
100
|
+
get(target, key) {
|
101
|
+
if (key === Symbol.iterator) {
|
102
|
+
return function* () {
|
103
|
+
for (const key of getKeysWithIds()) {
|
104
|
+
yield new ValueRef(
|
105
|
+
getIdForKey(key)!,
|
106
|
+
controlledAccount,
|
107
|
+
propDefForKey(key)
|
108
|
+
);
|
109
|
+
}
|
110
|
+
};
|
111
|
+
}
|
112
|
+
if (typeof key === "symbol") return undefined;
|
113
|
+
if (key === "length") {
|
114
|
+
return getKeysWithIds().length;
|
115
|
+
}
|
116
|
+
const id = getIdForKey(key as keyof F);
|
117
|
+
if (!id) return undefined;
|
118
|
+
return new ValueRef(
|
119
|
+
id as ID<F[typeof key]>,
|
120
|
+
controlledAccount,
|
121
|
+
propDefForKey(key as keyof F)
|
122
|
+
);
|
123
|
+
},
|
124
|
+
ownKeys() {
|
125
|
+
return getKeysWithIds().map((key) => key.toString());
|
126
|
+
},
|
127
|
+
});
|
128
|
+
}
|
@@ -0,0 +1,72 @@
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
2
|
+
import { AST } from "@effect/schema";
|
3
|
+
import * as S from "@effect/schema/Schema";
|
4
|
+
|
5
|
+
export type SchemaWithOutput<I> = S.Schema<any, any, never> & {
|
6
|
+
[S.TypeId]: {
|
7
|
+
_I: (_: any) => I;
|
8
|
+
};
|
9
|
+
};
|
10
|
+
|
11
|
+
export type SchemaWithInputAndOutput<A, I = A> = S.Schema<any, any, never> & {
|
12
|
+
[S.TypeId]: {
|
13
|
+
_A: (_: any) => A;
|
14
|
+
_I: (_: any) => I;
|
15
|
+
};
|
16
|
+
};
|
17
|
+
|
18
|
+
export type PropertySignatureWithOutput<I> = S.PropertySignature<
|
19
|
+
any,
|
20
|
+
boolean,
|
21
|
+
any,
|
22
|
+
boolean,
|
23
|
+
never
|
24
|
+
> & {
|
25
|
+
[S.TypeId]: {
|
26
|
+
_I: (_: any) => I | undefined;
|
27
|
+
};
|
28
|
+
};
|
29
|
+
|
30
|
+
export type PropertySignatureWithInputAndOutput<I, A> = S.PropertySignature<
|
31
|
+
any,
|
32
|
+
boolean,
|
33
|
+
any,
|
34
|
+
boolean,
|
35
|
+
never
|
36
|
+
> & {
|
37
|
+
[S.TypeId]: {
|
38
|
+
_A: (_: any) => A | undefined;
|
39
|
+
_I: (_: any) => I | undefined;
|
40
|
+
};
|
41
|
+
};
|
42
|
+
|
43
|
+
export type PropDef<I> =
|
44
|
+
| S.Schema<I>
|
45
|
+
| S.PropertySignature<I, boolean, I, boolean, never>;
|
46
|
+
|
47
|
+
export function CoSchema<Raw extends S.Schema<any>>(
|
48
|
+
raw: Raw
|
49
|
+
): Raw & S.Schema<Raw, any, any> {
|
50
|
+
return raw as any;
|
51
|
+
}
|
52
|
+
|
53
|
+
export function propSigToSchema<
|
54
|
+
S extends S.Schema<any> | PropertySignatureWithInputAndOutput<any, any>,
|
55
|
+
>(
|
56
|
+
propSig: S
|
57
|
+
): S extends S.Schema<any>
|
58
|
+
? S
|
59
|
+
: S extends PropertySignatureWithInputAndOutput<infer A, infer I>
|
60
|
+
? S.Schema<A, I>
|
61
|
+
: never {
|
62
|
+
if ("ast" in propSig) {
|
63
|
+
return propSig as any;
|
64
|
+
} else {
|
65
|
+
const ast = (
|
66
|
+
propSig as unknown as {
|
67
|
+
propertySignatureAST: { from: AST.AST };
|
68
|
+
}
|
69
|
+
).propertySignatureAST;
|
70
|
+
return S.make<any, unknown, never>(ast.from) as any;
|
71
|
+
}
|
72
|
+
}
|
@@ -0,0 +1,118 @@
|
|
1
|
+
import { ControlledAccount } from "./coValues/account/account.js";
|
2
|
+
import { CoValueSchema, CoValue, ID } from "./coValueInterfaces.js";
|
3
|
+
import * as S from "@effect/schema/Schema";
|
4
|
+
import { RawCoValue } from "cojson";
|
5
|
+
|
6
|
+
export const subscriptionsScopes = new WeakMap<
|
7
|
+
CoValue,
|
8
|
+
SubscriptionScope<any>
|
9
|
+
>();
|
10
|
+
|
11
|
+
export class SubscriptionScope<
|
12
|
+
RootSchema extends CoValueSchema = CoValueSchema,
|
13
|
+
> {
|
14
|
+
scopeID: string = `scope-${Math.random().toString(36).slice(2)}`;
|
15
|
+
subscriber: ControlledAccount;
|
16
|
+
entries = new Map<
|
17
|
+
ID<CoValue>,
|
18
|
+
| { state: "loading"; immediatelyUnsub?: boolean }
|
19
|
+
| { state: "loaded"; rawUnsub: () => void }
|
20
|
+
>();
|
21
|
+
rootEntry: {
|
22
|
+
state: "loaded";
|
23
|
+
value: S.Schema.To<RootSchema>;
|
24
|
+
rawUnsub: () => void;
|
25
|
+
};
|
26
|
+
onUpdate: (newRoot: S.Schema.To<RootSchema>) => void;
|
27
|
+
scheduledUpdate: boolean = false;
|
28
|
+
|
29
|
+
constructor(
|
30
|
+
root: S.Schema.To<RootSchema>,
|
31
|
+
rootSchema: RootSchema,
|
32
|
+
onUpdate: (newRoot: S.Schema.To<RootSchema>) => void
|
33
|
+
) {
|
34
|
+
this.rootEntry = {
|
35
|
+
state: "loaded" as const,
|
36
|
+
value: root,
|
37
|
+
rawUnsub: () => {}, // placeholder
|
38
|
+
};
|
39
|
+
this.entries.set(root.id, this.rootEntry);
|
40
|
+
|
41
|
+
subscriptionsScopes.set(root, this);
|
42
|
+
|
43
|
+
this.subscriber = root._loadedAs;
|
44
|
+
this.onUpdate = onUpdate;
|
45
|
+
this.rootEntry.rawUnsub = root._raw.core.subscribe(
|
46
|
+
(rawUpdate: RawCoValue | undefined) => {
|
47
|
+
if (!rawUpdate) return;
|
48
|
+
this.rootEntry.value = rootSchema.fromRaw(
|
49
|
+
rawUpdate
|
50
|
+
) as S.Schema.To<RootSchema>;
|
51
|
+
// console.log("root update", this.rootEntry.value.toJSON());
|
52
|
+
subscriptionsScopes.set(this.rootEntry.value, this);
|
53
|
+
this.scheduleUpdate();
|
54
|
+
}
|
55
|
+
);
|
56
|
+
}
|
57
|
+
|
58
|
+
scheduleUpdate() {
|
59
|
+
if (!this.scheduledUpdate) {
|
60
|
+
this.scheduledUpdate = true;
|
61
|
+
queueMicrotask(() => {
|
62
|
+
this.scheduledUpdate = false;
|
63
|
+
this.onUpdate(this.rootEntry.value);
|
64
|
+
});
|
65
|
+
}
|
66
|
+
}
|
67
|
+
|
68
|
+
onRefAccessedOrSet(accessedOrSetId: ID<CoValue> | undefined) {
|
69
|
+
// console.log("onRefAccessedOrSet", this.scopeID, accessedOrSetId);
|
70
|
+
if (!accessedOrSetId) {
|
71
|
+
return;
|
72
|
+
}
|
73
|
+
|
74
|
+
if (!this.entries.has(accessedOrSetId)) {
|
75
|
+
const loadingEntry = {
|
76
|
+
state: "loading",
|
77
|
+
immediatelyUnsub: false,
|
78
|
+
} as const;
|
79
|
+
this.entries.set(accessedOrSetId, loadingEntry);
|
80
|
+
this.subscriber._raw.core.node
|
81
|
+
.loadCoValueCore(accessedOrSetId)
|
82
|
+
.then((core) => {
|
83
|
+
if (
|
84
|
+
loadingEntry.state === "loading" &&
|
85
|
+
loadingEntry.immediatelyUnsub
|
86
|
+
) {
|
87
|
+
return;
|
88
|
+
}
|
89
|
+
if (core !== "unavailable") {
|
90
|
+
const entry = {
|
91
|
+
state: "loaded" as const,
|
92
|
+
rawUnsub: () => {}, // placeholder
|
93
|
+
};
|
94
|
+
this.entries.set(accessedOrSetId, entry);
|
95
|
+
|
96
|
+
const rawUnsub = core.subscribe((rawUpdate) => {
|
97
|
+
// console.log("ref update", this.scopeID, accessedOrSetId, JSON.stringify(rawUpdate))
|
98
|
+
if (!rawUpdate) return;
|
99
|
+
this.scheduleUpdate();
|
100
|
+
});
|
101
|
+
|
102
|
+
entry.rawUnsub = rawUnsub;
|
103
|
+
}
|
104
|
+
});
|
105
|
+
}
|
106
|
+
}
|
107
|
+
|
108
|
+
unsubscribeAll() {
|
109
|
+
for (const entry of this.entries.values()) {
|
110
|
+
if (entry.state === "loaded") {
|
111
|
+
entry.rawUnsub();
|
112
|
+
} else {
|
113
|
+
entry.immediatelyUnsub = true;
|
114
|
+
}
|
115
|
+
}
|
116
|
+
this.entries.clear();
|
117
|
+
}
|
118
|
+
}
|