Compare commits

...

9 Commits

Author SHA1 Message Date
Anselm
641f1dbfbe Release 2024-06-03 17:33:59 +01:00
Anselm
58d9a104d6 Fix CoMaps not initialising properly when passing too many init options 2024-06-03 17:33:24 +01:00
Anselm
7b9d24c8ef Release 2024-06-01 17:25:06 +02:00
Anselm Eickhoff
4225fdd537 Merge pull request #200 from gdorsi/main
fix: improve compatibility with React compiler and concurrent features
2024-06-01 17:24:17 +02:00
Anselm
9fdc91c6de Add changeset 2024-06-01 17:23:18 +02:00
Anselm
93d8c85e5c Formatting 2024-06-01 17:22:38 +02:00
Anselm
929cddc3c3 Release 2024-06-01 17:13:02 +02:00
Anselm
e0bc63f016 Provide way to create accounts as another account 2024-06-01 17:12:42 +02:00
Guido D'Orsi
3325ff1cd6 fix: make Jazz react compiler ready 2024-05-31 10:19:33 +02:00
27 changed files with 286 additions and 149 deletions

View File

@@ -1,5 +1,28 @@
# jazz-example-chat
## 0.0.55
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.8
- jazz-react@0.7.8
## 0.0.54
### Patch Changes
- Updated dependencies [9fdc91c]
- jazz-react@0.7.7
## 0.0.53
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.6
- jazz-react@0.7.6
## 0.0.52
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-chat",
"private": true,
"version": "0.0.52",
"version": "0.0.55",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,5 +1,30 @@
# jazz-example-pets
## 0.0.73
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.8
- jazz-browser-media-images@0.7.8
- jazz-react@0.7.8
## 0.0.72
### Patch Changes
- Updated dependencies [9fdc91c]
- jazz-react@0.7.7
## 0.0.71
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.6
- jazz-browser-media-images@0.7.6
- jazz-react@0.7.6
## 0.0.70
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-pets",
"private": true,
"version": "0.0.70",
"version": "0.0.73",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,5 +1,28 @@
# jazz-example-todo
## 0.0.72
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.8
- jazz-react@0.7.8
## 0.0.71
### Patch Changes
- Updated dependencies [9fdc91c]
- jazz-react@0.7.7
## 0.0.70
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.6
- jazz-react@0.7.6
## 0.0.69
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-todo",
"private": true,
"version": "0.0.69",
"version": "0.0.72",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,5 +1,21 @@
# jazz-browser-media-images
## 0.7.8
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.8
- jazz-browser@0.7.8
## 0.7.6
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.6
- jazz-browser@0.7.6
## 0.7.5
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "jazz-browser-media-images",
"version": "0.7.5",
"version": "0.7.8",
"type": "module",
"main": "dist/index.js",
"types": "src/index.ts",

View File

@@ -1,5 +1,19 @@
# jazz-browser
## 0.7.8
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.8
## 0.7.6
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.6
## 0.7.5
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "jazz-browser",
"version": "0.7.5",
"version": "0.7.8",
"type": "module",
"main": "dist/index.js",
"types": "src/index.ts",

View File

@@ -1,5 +1,19 @@
# jazz-autosub
## 0.7.8
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.8
## 0.7.6
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.6
## 0.7.3
### Patch Changes

View File

@@ -5,7 +5,7 @@
"types": "src/index.ts",
"type": "module",
"license": "MIT",
"version": "0.7.3",
"version": "0.7.8",
"dependencies": {
"cojson": "workspace:*",
"cojson-transport-nodejs-ws": "workspace:*",

View File

@@ -1,5 +1,27 @@
# jazz-react
## 0.7.8
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.8
- jazz-browser@0.7.8
## 0.7.7
### Patch Changes
- 9fdc91c: Improve compatibility with React compiler and concurrent features
## 0.7.6
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.6
- jazz-browser@0.7.6
## 0.7.5
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "jazz-react",
"version": "0.7.5",
"version": "0.7.8",
"type": "module",
"main": "dist/index.js",
"types": "src/index.ts",

View File

@@ -1,10 +1,9 @@
import React, { useEffect, useRef, useState } from "react";
import React, { useEffect, useState } from "react";
import {
consumeInviteLinkFromWindowLocation,
createJazzBrowserContext,
} from "jazz-browser";
import {
Account,
CoValue,
@@ -18,7 +17,7 @@ import { AuthState, ReactAuthHook } from "./auth/auth.js";
/** @category Context & Hooks */
export function createJazzReactContext<Acc extends Account>({
auth: authHook,
auth: useAuthHook,
peer,
storage = "indexedDB",
}: {
@@ -43,7 +42,7 @@ export function createJazzReactContext<Acc extends Account>({
}) {
const [me, setMe] = useState<Acc | undefined>();
const [authState, setAuthState] = useState<AuthState>("loading");
const { auth, AuthUI, logOut } = authHook(setAuthState);
const { auth, AuthUI, logOut } = useAuthHook(setAuthState);
useEffect(() => {
let done: (() => void) | undefined = undefined;
@@ -128,24 +127,20 @@ export function createJazzReactContext<Acc extends Account>({
id: ID<V> | undefined,
depth: D & DepthsIn<V> = [] as D & DepthsIn<V>,
): DeeplyLoaded<V, D> | undefined {
// for some reason (at least in React 18) - if we use state directly,
// some updates get swallowed/UI doesn't update
const [_, setUpdates] = useState<number>(0);
const state = useRef<DeeplyLoaded<V, D> | undefined>(undefined);
const [state, setState] = useState<{
value: DeeplyLoaded<V, D> | undefined;
}>({ value: undefined });
const me = React.useContext(JazzContext)?.me;
useEffect(() => {
if (!id || !me) return;
return subscribeToCoValue(Schema, id, me, depth, (update) => {
state.current = update as DeeplyLoaded<V, D>;
setUpdates((u) => {
return u + 1;
});
return subscribeToCoValue(Schema, id, me, depth, (value) => {
setState({ value });
});
}, [Schema, id, me]);
return state.current;
return state.value;
}
function useAcceptInvite<V extends CoValue>({

View File

@@ -1,5 +1,19 @@
# jazz-autosub
## 0.7.8
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.8
## 0.7.6
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.6
## 0.7.3
### Patch Changes

View File

@@ -3,7 +3,7 @@
"bin": "./dist/index.js",
"type": "module",
"license": "MIT",
"version": "0.7.3",
"version": "0.7.8",
"scripts": {
"lint": "eslint . --ext ts,tsx",
"format": "prettier --write './src/**/*.{ts,tsx}'",

View File

@@ -1,5 +1,17 @@
# jazz-autosub
## 0.7.8
### Patch Changes
- Fix CoMaps not initialising properly when passing too many init options
## 0.7.6
### Patch Changes
- Provide way to create accounts as another account
## 0.7.3
### Patch Changes

View File

@@ -5,7 +5,7 @@
"types": "./src/index.ts",
"type": "module",
"license": "MIT",
"version": "0.7.3",
"version": "0.7.8",
"dependencies": {
"@effect/schema": "^0.66.16",
"cojson": "workspace:*",

View File

@@ -1,4 +1,4 @@
import { LocalNode } from "cojson";
import { LocalNode, cojsonInternals } from "cojson";
import type {
AgentSecret,
CoID,
@@ -214,6 +214,29 @@ export class Account extends CoValueBase implements CoValue {
return this.fromNode(node) as A;
}
static createAs<A extends Account>(
this: CoValueClass<A> & typeof Account,
as: Account,
options: {
creationProps: { name: string };
},
) {
// TODO: is there a cleaner way to do this?
const connectedPeers = cojsonInternals.connectedPeers(
"creatingAccount",
"createdAccount",
{ peer1role: "server", peer2role: "client" },
);
as._raw.core.node.syncManager.addPeer(connectedPeers[1]);
return this.create<A>({
creationProps: options.creationProps,
crypto: as._raw.core.node.crypto,
peersToLoadFrom: [connectedPeers[0]],
});
}
static fromNode<A extends Account>(
this: CoValueClass<A>,
node: LocalNode,

View File

@@ -17,7 +17,6 @@ import type {
import {
Account,
Group,
InitValues,
ItemsSym,
Ref,
SchemaInit,
@@ -172,17 +171,14 @@ export class CoList<Item = any> extends Array<Item> implements CoValue {
return Account.fromNode(this._raw.core.node);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[InitValues]?: any;
static get [Symbol.species]() {
return Array;
}
constructor(
options:
| { init: Item[]; owner: Account | Group }
| { fromRaw: RawCoList },
| { fromRaw: RawCoList }
| undefined
) {
super();
@@ -191,12 +187,7 @@ export class CoList<Item = any> extends Array<Item> implements CoValue {
enumerable: false,
});
if ("owner" in options) {
this[InitValues] = {
init: options.init,
owner: options.owner,
};
} else if ("fromRaw" in options) {
if (options && "fromRaw" in options) {
Object.defineProperties(this, {
id: {
value: options.fromRaw.id,
@@ -235,7 +226,20 @@ export class CoList<Item = any> extends Array<Item> implements CoValue {
items: UnCo<L[number]>[],
options: { owner: Account | Group },
) {
return new this({ init: items, owner: options.owner });
const instance = new this({ init: items, owner: options.owner });
const raw = options.owner._raw.createList(
toRawItems(items, instance._schema[ItemsSym]),
);
Object.defineProperties(instance, {
id: {
value: raw.id,
enumerable: false,
},
_raw: { value: raw, enumerable: false },
});
return instance;
}
push(...items: Item[]): number {
@@ -489,24 +493,6 @@ function toRawItems<Item>(items: Item[], itemDescriptor: Schema) {
return rawItems;
}
function init(list: CoList) {
if (list[InitValues]) {
const { init, owner } = list[InitValues];
const raw = owner._raw.createList(
toRawItems(init, list._schema[ItemsSym]),
);
Object.defineProperties(list, {
id: {
value: raw.id,
enumerable: false,
},
_raw: { value: raw, enumerable: false },
});
delete list[InitValues];
}
}
const CoListProxyHandler: ProxyHandler<CoList> = {
get(target, key, receiver) {
if (typeof key === "string" && !isNaN(+key)) {
@@ -542,7 +528,6 @@ const CoListProxyHandler: ProxyHandler<CoList> = {
(target.constructor as typeof CoList)._schema ||= {};
(target.constructor as typeof CoList)._schema[ItemsSym] =
value[SchemaInit];
init(target);
return true;
}
if (typeof key === "string" && !isNaN(+key)) {
@@ -571,7 +556,6 @@ const CoListProxyHandler: ProxyHandler<CoList> = {
(target.constructor as typeof CoList)._schema ||= {};
(target.constructor as typeof CoList)._schema[ItemsSym] =
descriptor.value[SchemaInit];
init(target);
return true;
} else {
return Reflect.defineProperty(target, key, descriptor);

View File

@@ -24,7 +24,6 @@ import {
makeRefs,
subscriptionsScopes,
ItemsSym,
InitValues,
isRefEncoded,
loadCoValue,
loadCoValueEf,
@@ -42,11 +41,6 @@ type CoMapEdit<V> = {
madeAt: Date;
};
type InitValuesFor<C extends CoMap> = {
init: Simplify<CoMapInit<C>>;
owner: Account | Group;
};
/**
* CoMaps are collaborative versions of plain objects, mapping string-like keys to values.
*
@@ -201,32 +195,25 @@ export class CoMap extends CoValueBase implements CoValue {
return Account.fromNode(this._raw.core.node);
}
/** @internal */
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[InitValues]?: any;
/** @internal */
constructor(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
options: { fromRaw: RawCoMap } | { init: any; owner: Account | Group },
options: { fromRaw: RawCoMap } | undefined
) {
super();
if ("owner" in options) {
this[InitValues] = {
init: options.init,
owner: options.owner,
} as InitValuesFor<this>;
} else if ("fromRaw" in options) {
Object.defineProperties(this, {
id: {
value: options.fromRaw.id as unknown as ID<this>,
enumerable: false,
},
_raw: { value: options.fromRaw, enumerable: false },
});
} else {
throw new Error("Invalid CoMap constructor arguments");
if (options) {
if ("fromRaw" in options) {
Object.defineProperties(this, {
id: {
value: options.fromRaw.id as unknown as ID<this>,
enumerable: false,
},
_raw: { value: options.fromRaw, enumerable: false },
});
} else {
throw new Error("Invalid CoMap constructor arguments");
}
}
return new Proxy(this, CoMapProxyHandler as ProxyHandler<this>);
@@ -255,7 +242,19 @@ export class CoMap extends CoValueBase implements CoValue {
init: Simplify<CoMapInit<M>>,
options: { owner: Account | Group },
) {
return new this({ init, owner: options.owner });
const instance = new this();
const raw = instance.rawFromInit(
init,
options.owner,
);
Object.defineProperties(instance, {
id: {
value: raw.id,
enumerable: false,
},
_raw: { value: raw, enumerable: false },
});
return instance;
}
toJSON() {
@@ -306,6 +305,10 @@ export class CoMap extends CoValueBase implements CoValue {
key as keyof typeof this._schema
] || this._schema[ItemsSym]) as Schema;
if (!descriptor) {
continue;
}
if (descriptor === "json") {
rawInit[key] = initValue as JsonValue;
} else if (isRefEncoded(descriptor)) {
@@ -493,30 +496,6 @@ export type CoMapInit<Map extends object> = {
: IfCo<Map[Key], Key>]: Map[Key];
} & { [Key in CoKeys<Map> as IfCo<Map[Key], Key>]?: Map[Key] };
function tryInit(map: CoMap) {
if (
map[InitValues] &&
(map._schema[ItemsSym] ||
Object.keys(map[InitValues].init).every(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(key) => (map._schema as any)[key],
))
) {
const raw = map.rawFromInit(
map[InitValues].init,
map[InitValues].owner,
);
Object.defineProperties(map, {
id: {
value: raw.id,
enumerable: false,
},
_raw: { value: raw, enumerable: false },
});
delete map[InitValues];
}
}
// TODO: cache handlers per descriptor for performance?
const CoMapProxyHandler: ProxyHandler<CoMap> = {
get(target, key, receiver) {
@@ -559,7 +538,6 @@ const CoMapProxyHandler: ProxyHandler<CoMap> = {
(target.constructor as typeof CoMap)._schema ||= {};
(target.constructor as typeof CoMap)._schema[key] =
value[SchemaInit];
tryInit(target);
return true;
}
@@ -590,7 +568,6 @@ const CoMapProxyHandler: ProxyHandler<CoMap> = {
(target.constructor as typeof CoMap)._schema ||= {};
(target.constructor as typeof CoMap)._schema[key as string] =
attributes.value[SchemaInit];
tryInit(target);
return true;
} else {
return Reflect.defineProperty(target, key, attributes);

View File

@@ -30,7 +30,6 @@ import {
Ref,
inspect,
co,
InitValues,
SchemaInit,
isRefEncoded,
loadCoValue,
@@ -93,9 +92,6 @@ export class CoStream<Item = any> extends CoValueBase implements CoValue {
return this.perSession[this._loadedAs.sessionID!];
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[InitValues]?: any;
constructor(
options:
| { init: Item[]; owner: Account | Group }
@@ -103,7 +99,7 @@ export class CoStream<Item = any> extends CoValueBase implements CoValue {
) {
super();
if ("fromRaw" in options) {
if (options && "fromRaw" in options) {
Object.defineProperties(this, {
id: {
value: options.fromRaw.id,
@@ -111,11 +107,6 @@ export class CoStream<Item = any> extends CoValueBase implements CoValue {
},
_raw: { value: options.fromRaw, enumerable: false },
});
} else {
this[InitValues] = {
init: options.init,
owner: options.owner,
};
}
return new Proxy(this, CoStreamProxyHandler as ProxyHandler<this>);
@@ -126,7 +117,21 @@ export class CoStream<Item = any> extends CoValueBase implements CoValue {
init: S extends CoStream<infer Item> ? UnCo<Item>[] : never,
options: { owner: Account | Group },
) {
return new this({ init, owner: options.owner });
const instance = new this({ init, owner: options.owner });
const raw = options.owner._raw.createStream();
Object.defineProperties(instance, {
id: {
value: raw.id,
enumerable: false,
},
_raw: { value: raw, enumerable: false },
});
if (init) {
instance.push(...init);
}
return instance;
}
push(...items: Item[]) {
@@ -317,27 +322,6 @@ function entryFromRawEntry<Item>(
};
}
function init(stream: CoStream) {
const init = stream[InitValues];
if (!init) return;
const raw = init.owner._raw.createStream();
Object.defineProperties(stream, {
id: {
value: raw.id,
enumerable: false,
},
_raw: { value: raw, enumerable: false },
});
if (init.init) {
stream.push(...init.init);
}
delete stream[InitValues];
}
export const CoStreamProxyHandler: ProxyHandler<CoStream> = {
get(target, key, receiver) {
if (typeof key === "string" && key.startsWith("co_")) {
@@ -391,7 +375,6 @@ export const CoStreamProxyHandler: ProxyHandler<CoStream> = {
(target.constructor as typeof CoStream)._schema ||= {};
(target.constructor as typeof CoStream)._schema[ItemsSym] =
value[SchemaInit];
init(target);
return true;
} else {
return Reflect.set(target, key, value, receiver);
@@ -407,7 +390,6 @@ export const CoStreamProxyHandler: ProxyHandler<CoStream> = {
(target.constructor as typeof CoStream)._schema ||= {};
(target.constructor as typeof CoStream)._schema[ItemsSym] =
descriptor.value[SchemaInit];
init(target);
return true;
} else {
return Reflect.defineProperty(target, key, descriptor);

View File

@@ -114,7 +114,7 @@ export class Group extends CoValueBase implements CoValue {
const initOwner = options.owner;
if (!initOwner) throw new Error("No owner provided");
if (
initOwner instanceof Account &&
initOwner._type === "Account" &&
isControlledAccount(initOwner)
) {
const rawOwner = initOwner._raw;

View File

@@ -1,9 +1,6 @@
export const SchemaInit = Symbol.for("SchemaInit");
export type SchemaInit = typeof SchemaInit;
export const InitValues = Symbol.for("InitValues");
export type InitValues = typeof InitValues;
export const ItemsSym = Symbol.for("items");
export type ItemsSym = typeof ItemsSym;

View File

@@ -17,7 +17,7 @@ export type { ID, CoValue } from "./internal.js";
export { Encoders, co } from "./internal.js";
export { CoMap } from "./internal.js";
export { CoMap, type CoMapInit } from "./internal.js";
export { CoList } from "./internal.js";
export { CoStream, BinaryCoStream } from "./internal.js";
export { Group, Profile } from "./internal.js";

View File

@@ -52,6 +52,22 @@ describe("Simple CoMap operations", async () => {
expect(Object.keys(map)).toEqual(["color", "_height", "birthday"]);
});
test("Construction with too many things provided", () => {
const mapWithExtra = TestMap.create(
{
color: "red",
_height: 10,
birthday: birthday,
name: "Hermes",
extra: "extra",
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any,
{ owner: me },
);
expect(mapWithExtra.color).toEqual("red");
})
describe("Mutation", () => {
test("assignment & deletion", () => {
map.color = "blue";