Compare commits
78 Commits
jazz-run@0
...
jazz-run@0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3c6a2a6092 | ||
|
|
e8a950e61a | ||
|
|
e2cbf035de | ||
|
|
47599b6307 | ||
|
|
901d0762ee | ||
|
|
d1c1b0c5cc | ||
|
|
cf4ad7285d | ||
|
|
2983c7bd58 | ||
|
|
ab6328f767 | ||
|
|
e0555debde | ||
|
|
247f4556e7 | ||
|
|
7903c737f4 | ||
|
|
6145da5525 | ||
|
|
fc0a2e77a3 | ||
|
|
334fbbbb7f | ||
|
|
eaac1e6580 | ||
|
|
114898d8a9 | ||
|
|
cbc3f0cc65 | ||
|
|
29c487e288 | ||
|
|
0b0590a364 | ||
|
|
1eb01997d8 | ||
|
|
0dc8d511a1 | ||
|
|
962213c712 | ||
|
|
427df8fcbb | ||
|
|
c40aad55dc | ||
|
|
dfca5926de | ||
|
|
9815ec61f0 | ||
|
|
fca60d213e | ||
|
|
b4fdab475b | ||
|
|
2b043abffa | ||
|
|
958c122c36 | ||
|
|
5842838371 | ||
|
|
e136e1b696 | ||
|
|
2475a46578 | ||
|
|
44f653a64b | ||
|
|
f8437042a6 | ||
|
|
acd908fbc2 | ||
|
|
4e61d1d191 | ||
|
|
db23582b4c | ||
|
|
4b0b6d8a69 | ||
|
|
d450b394fa | ||
|
|
0abc96e400 | ||
|
|
7562354b29 | ||
|
|
6c085a3919 | ||
|
|
6afff848bc | ||
|
|
47059845cc | ||
|
|
a1735a8232 | ||
|
|
1f5750d8c4 | ||
|
|
f756ce26b5 | ||
|
|
84f5bdda74 | ||
|
|
ee7aefa97c | ||
|
|
b0895981ba | ||
|
|
94f636b2ee | ||
|
|
331ab070f6 | ||
|
|
13e73adfb9 | ||
|
|
f1552b8262 | ||
|
|
6826ad8e45 | ||
|
|
7c1b757b62 | ||
|
|
326e1734a4 | ||
|
|
0cf027c91b | ||
|
|
e358881b76 | ||
|
|
cee8010918 | ||
|
|
cc877139ef | ||
|
|
56a9b89538 | ||
|
|
199c463e28 | ||
|
|
50e523d19c | ||
|
|
796ea24288 | ||
|
|
c8be86e823 | ||
|
|
41b7054aab | ||
|
|
101adcd024 | ||
|
|
a2854aeec9 | ||
|
|
bdc9aee689 | ||
|
|
2f53ae0ab8 | ||
|
|
f76c05448c | ||
|
|
585e7e8177 | ||
|
|
82d8d1d873 | ||
|
|
2c523c86ff | ||
|
|
6616668d4a |
@@ -1,5 +1,28 @@
|
||||
# passkey-svelte
|
||||
|
||||
## 0.0.101
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [d1c1b0c]
|
||||
- Updated dependencies [cf4ad72]
|
||||
- jazz-tools@0.15.12
|
||||
|
||||
## 0.0.100
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [bdc9aee]
|
||||
- jazz-tools@0.15.11
|
||||
|
||||
## 0.0.99
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [9815ec6]
|
||||
- Updated dependencies [b4fdab4]
|
||||
- jazz-tools@0.15.10
|
||||
|
||||
## 0.0.98
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "chat-svelte",
|
||||
"version": "0.0.98",
|
||||
"version": "0.0.101",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { co, z } from "jazz-tools";
|
||||
import { co } from "jazz-tools";
|
||||
|
||||
export const Message = co.map({
|
||||
text: co.plainText(),
|
||||
image: z.optional(co.image()),
|
||||
image: co.optional(co.image()),
|
||||
});
|
||||
export type Message = co.loaded<typeof Message>;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { co, z } from "jazz-tools";
|
||||
import { co } from "jazz-tools";
|
||||
|
||||
export const JazzProfile = co.profile({
|
||||
file: z.optional(co.fileStream()),
|
||||
file: co.optional(co.fileStream()),
|
||||
});
|
||||
|
||||
export const JazzAccount = co.account({
|
||||
|
||||
@@ -28,16 +28,16 @@ export const BubbleTeaOrder = co.map({
|
||||
addOns: ListOfBubbleTeaAddOns,
|
||||
deliveryDate: z.date(),
|
||||
withMilk: z.boolean(),
|
||||
instructions: z.optional(co.plainText()),
|
||||
instructions: co.optional(co.plainText()),
|
||||
});
|
||||
|
||||
export const DraftBubbleTeaOrder = co
|
||||
.map({
|
||||
baseTea: z.optional(z.literal([...BubbleTeaBaseTeaTypes])),
|
||||
addOns: z.optional(ListOfBubbleTeaAddOns),
|
||||
addOns: co.optional(ListOfBubbleTeaAddOns),
|
||||
deliveryDate: z.optional(z.date()),
|
||||
withMilk: z.optional(z.boolean()),
|
||||
instructions: z.optional(co.plainText()),
|
||||
instructions: co.optional(co.plainText()),
|
||||
})
|
||||
.withHelpers((Self) => ({
|
||||
hasChanges(order: Loaded<typeof Self> | undefined) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { co, z } from "jazz-tools";
|
||||
import { co } from "jazz-tools";
|
||||
|
||||
export const JazzProfile = co.profile({
|
||||
image: z.optional(co.image()),
|
||||
image: co.optional(co.image()),
|
||||
});
|
||||
|
||||
export const JazzAccount = co.account({
|
||||
|
||||
@@ -8,7 +8,7 @@ export type Player = co.loaded<typeof Player>;
|
||||
|
||||
export const Game = co.map({
|
||||
player1: Player,
|
||||
player2: z.optional(Player),
|
||||
player2: co.optional(Player),
|
||||
outcome: z.optional(z.literal(["player1", "player2", "draw"])),
|
||||
player1Score: z.number(),
|
||||
player2Score: z.number(),
|
||||
@@ -17,8 +17,8 @@ export type Game = co.loaded<typeof Game>;
|
||||
|
||||
export const WaitingRoom = co.map({
|
||||
account1: co.account(),
|
||||
account2: z.optional(co.account()),
|
||||
game: z.optional(Game),
|
||||
account2: co.optional(co.account()),
|
||||
game: co.optional(Game),
|
||||
});
|
||||
export type WaitingRoom = co.loaded<typeof WaitingRoom>;
|
||||
|
||||
@@ -47,7 +47,7 @@ export const JoinGameRequest = co.map({
|
||||
});
|
||||
export type JoinGameRequest = co.loaded<typeof JoinGameRequest>;
|
||||
|
||||
export const InboxMessage = z.discriminatedUnion("type", [
|
||||
export const InboxMessage = co.discriminatedUnion("type", [
|
||||
PlayIntent,
|
||||
NewGameIntent,
|
||||
CreateGameRequest,
|
||||
|
||||
@@ -66,7 +66,7 @@ export const MusicaAccountRoot = co.map({
|
||||
// track and playlist
|
||||
// You can also add the position in time if you want make it possible
|
||||
// to resume the song
|
||||
activeTrack: z.optional(MusicTrack),
|
||||
activeTrack: co.optional(MusicTrack),
|
||||
activePlaylist: Playlist,
|
||||
|
||||
exampleDataLoaded: z.optional(z.boolean()),
|
||||
|
||||
3
examples/server-side-validation/vercel.json
Normal file
3
examples/server-side-validation/vercel.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"ignoreCommand": "echo true"
|
||||
}
|
||||
@@ -202,7 +202,7 @@ See the corresponding sections for [creating](/docs/using-covalues/filestreams#c
|
||||
|
||||
### Unions of CoMaps (declaration)
|
||||
|
||||
You can declare unions of CoMaps that have discriminating fields, using `z.discriminatedUnion()`.
|
||||
You can declare unions of CoMaps that have discriminating fields, using `co.discriminatedUnion()`.
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
@@ -220,7 +220,7 @@ const SliderWidget = co.map({
|
||||
max: z.number(),
|
||||
});
|
||||
|
||||
const WidgetUnion = z.discriminatedUnion("type", [ButtonWidget, SliderWidget]);
|
||||
const WidgetUnion = co.discriminatedUnion("type", [ButtonWidget, SliderWidget]);
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
@@ -321,7 +321,7 @@ const Company = co.map({
|
||||
|
||||
#### Optional References
|
||||
|
||||
You can make references optional with `z.optional()`:
|
||||
You can make references optional with `co.optional()`:
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
@@ -331,7 +331,7 @@ const Pet = co.map({
|
||||
});
|
||||
// ---cut---
|
||||
const Person = co.map({
|
||||
pet: z.optional(Pet),
|
||||
pet: co.optional(Pet),
|
||||
});
|
||||
```
|
||||
</CodeGroup>
|
||||
@@ -372,7 +372,7 @@ const ListOfPeople = co.list(Person);
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
Note: similarly, if you use modifiers like `z.optional()` you'll need to help TypeScript along:
|
||||
Note: similarly, if you use modifiers like `co.optional()` you'll need to help TypeScript along:
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
@@ -381,7 +381,7 @@ import { co, z } from "jazz-tools";
|
||||
const Person = co.map({
|
||||
name: z.string(),
|
||||
get bestFriend(): z.ZodOptional<typeof Person> {
|
||||
return z.optional(Person);
|
||||
return co.optional(Person);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
@@ -24,7 +24,7 @@ const Project = co.map({
|
||||
name: z.string(),
|
||||
startDate: z.date(),
|
||||
status: z.literal(["planning", "active", "completed"]),
|
||||
coordinator: z.optional(Member),
|
||||
coordinator: co.optional(Member),
|
||||
});
|
||||
export type Project = co.loaded<typeof Project>;
|
||||
```
|
||||
@@ -54,7 +54,7 @@ const Project = co.map({
|
||||
name: z.string(),
|
||||
startDate: z.date(),
|
||||
status: z.literal(["planning", "active", "completed"]),
|
||||
coordinator: z.optional(Member),
|
||||
coordinator: co.optional(Member),
|
||||
});
|
||||
const Inventory = co.record(z.string(), z.number());
|
||||
// ---cut---
|
||||
@@ -90,7 +90,7 @@ const Project = co.map({
|
||||
name: z.string(),
|
||||
startDate: z.date(),
|
||||
status: z.literal(["planning", "active", "completed"]),
|
||||
coordinator: z.optional(Member),
|
||||
coordinator: co.optional(Member),
|
||||
});
|
||||
|
||||
// ---cut---
|
||||
@@ -134,7 +134,7 @@ const Project = co.map({
|
||||
name: z.string(),
|
||||
startDate: z.date(),
|
||||
status: z.literal(["planning", "active", "completed"]),
|
||||
coordinator: z.optional(Member),
|
||||
coordinator: co.optional(Member),
|
||||
});
|
||||
const project = Project.create(
|
||||
{
|
||||
@@ -165,7 +165,7 @@ const Project = co.map({
|
||||
name: z.string(),
|
||||
startDate: z.date(),
|
||||
status: z.literal(["planning", "active", "completed"]),
|
||||
coordinator: z.optional(Member),
|
||||
coordinator: co.optional(Member),
|
||||
});
|
||||
const project = Project.create(
|
||||
{
|
||||
@@ -197,7 +197,7 @@ const Project = co.map({
|
||||
name: z.string(),
|
||||
startDate: z.date(),
|
||||
status: z.literal(["planning", "active", "completed"]),
|
||||
coordinator: z.optional(Member),
|
||||
coordinator: co.optional(Member),
|
||||
get subProject() {
|
||||
return Project.optional();
|
||||
}
|
||||
@@ -219,9 +219,9 @@ const Project = co.map({
|
||||
name: z.string(),
|
||||
startDate: z.date(),
|
||||
status: z.literal(["planning", "active", "completed"]),
|
||||
coordinator: z.optional(Member),
|
||||
coordinator: co.optional(Member),
|
||||
get subProjects(): z.ZodOptional<CoListSchema<typeof Project>> {
|
||||
return z.optional(co.list(Project));
|
||||
return co.optional(co.list(Project));
|
||||
}
|
||||
});
|
||||
export type Project = co.loaded<typeof Project>;
|
||||
@@ -265,7 +265,7 @@ const Project = co.map({
|
||||
name: z.string(),
|
||||
startDate: z.date(),
|
||||
status: z.literal(["planning", "active", "completed"]),
|
||||
coordinator: z.optional(Member),
|
||||
coordinator: co.optional(Member),
|
||||
});
|
||||
const Inventory = co.record(z.string(), z.number());
|
||||
const project = Project.create(
|
||||
@@ -297,7 +297,7 @@ const Project = co.map({
|
||||
name: z.string(),
|
||||
startDate: z.date(),
|
||||
status: z.literal(["planning", "active", "completed"]),
|
||||
coordinator: z.optional(Member),
|
||||
coordinator: co.optional(Member),
|
||||
});
|
||||
const Inventory = co.record(z.string(), z.number());
|
||||
const project = Project.create(
|
||||
@@ -347,7 +347,7 @@ const Project = co.map({
|
||||
name: z.string(),
|
||||
startDate: z.date(),
|
||||
status: z.literal(["planning", "active", "completed"]),
|
||||
coordinator: z.optional(Member),
|
||||
coordinator: co.optional(Member),
|
||||
});
|
||||
const Inventory = co.record(z.string(), z.number());
|
||||
const project = Project.create(
|
||||
@@ -445,7 +445,7 @@ const TaskV2 = co.map({
|
||||
|
||||
// Export the discriminated union; because some users might
|
||||
// not be able to run the migration
|
||||
export const Task = z.discriminatedUnion("version", [
|
||||
export const Task = co.discriminatedUnion("version", [
|
||||
TaskV1,
|
||||
TaskV2,
|
||||
]);
|
||||
|
||||
@@ -6,7 +6,7 @@ export const metadata = {
|
||||
|
||||
# Connecting CoValues with direct linking
|
||||
CoValues can form relationships with each other by **linking directly to other CoValues**. This creates a powerful connection where one CoValue can point to the unique identity of another.
|
||||
Instead of embedding all of the details of one coValue directly within another, you use its Jazz-Tools schema as the field type. This allows multiple CoValues to point to the same piece of data effortlessly.
|
||||
Instead of embedding all the details of one CoValue directly within another, you use its Jazz-Tools schema as the field type. This allows multiple CoValues to point to the same piece of data effortlessly.
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
@@ -50,3 +50,51 @@ export type User = co.loaded<typeof User>;
|
||||
This direct linking approach offers a single source of truth. When you update a referenced CoValue, all other CoValues that point to it are automatically updated, ensuring data consistency across your application.
|
||||
|
||||
By connecting CoValues through these direct references, you can build robust and collaborative applications where data is consistent, efficient to manage, and relationships are clearly defined. The ability to link different CoValue types to the same underlying data is fundamental to building complex applications with Jazz.
|
||||
|
||||
|
||||
## Recursive references with DiscriminatedUnion
|
||||
In advanced schemas, you may want a CoValue that recursively references itself. For example, a `ReferenceItem` that contains a list of other items like `NoteItem` or `AttachmentItem`. This is common in tree-like structures such as threaded comments or nested project outlines.
|
||||
|
||||
You can model this with a Zod `z.discriminatedUnion`, but TypeScript’s type inference doesn't handle recursive unions well without a workaround.
|
||||
|
||||
Here’s how to structure your schema to avoid circular reference errors.
|
||||
|
||||
### Use this pattern for recursive discriminated unions
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
import { CoListSchema, co, z } from "jazz-tools";
|
||||
|
||||
// Recursive item modeling pattern using discriminated unions
|
||||
// First, define the non-recursive types
|
||||
export const NoteItem = co.map({
|
||||
type: z.literal("note"),
|
||||
internal: z.boolean(),
|
||||
content: co.plainText(),
|
||||
});
|
||||
|
||||
export const AttachmentItem = co.map({
|
||||
type: z.literal("attachment"),
|
||||
internal: z.boolean(),
|
||||
content: co.fileStream(),
|
||||
});
|
||||
|
||||
export const ReferenceItem = co.map({
|
||||
type: z.literal("reference"),
|
||||
internal: z.boolean(),
|
||||
content: z.string(),
|
||||
|
||||
// Workaround: declare the field type using CoListSchema and ZodDiscriminatedUnion so TS can safely recurse
|
||||
get children(): CoListSchema<z.ZodDiscriminatedUnion<[typeof NoteItem, typeof AttachmentItem, typeof ReferenceItem]>> {
|
||||
return ProjectContextItemList;
|
||||
},
|
||||
});
|
||||
|
||||
// Create the recursive union
|
||||
export const ProjectContextItem = z.discriminatedUnion("type", [NoteItem, AttachmentItem, ReferenceItem]);
|
||||
|
||||
// Final list of recursive types
|
||||
export const ProjectContextItemList = co.list(ProjectContextItem);
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
Even though this seems like a shortcut, TypeScript and Zod can't resolve the circular reference this way. Always define the discriminated union before introducing recursive links.
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
"changeset-version": "changeset version && pnpm i --no-frozen-lockfile",
|
||||
"release": "turbo run build --filter='./packages/*' && pnpm changeset publish && git push --follow-tags",
|
||||
"clean": "rm -rf ./packages/*/dist && rm -rf ./packages/*/node_modules && rm -rf ./examples/*/node_modules && rm -rf ./examples/*/dist",
|
||||
"postinstall": "lefthook install",
|
||||
"check-catalog-deps": "node scripts/check-catalog-deps.js"
|
||||
},
|
||||
"version": "0.0.0",
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
# cojson-storage-indexeddb
|
||||
|
||||
## 0.15.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cojson@0.15.12
|
||||
|
||||
## 0.15.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cojson@0.15.11
|
||||
|
||||
## 0.15.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cojson@0.15.10
|
||||
|
||||
## 0.15.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cojson-storage-indexeddb",
|
||||
"version": "0.15.9",
|
||||
"version": "0.15.12",
|
||||
"main": "dist/index.js",
|
||||
"type": "module",
|
||||
"types": "dist/index.d.ts",
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
# cojson-storage-sqlite
|
||||
|
||||
## 0.15.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cojson@0.15.12
|
||||
|
||||
## 0.15.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cojson@0.15.11
|
||||
|
||||
## 0.15.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cojson@0.15.10
|
||||
|
||||
## 0.15.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "cojson-storage-sqlite",
|
||||
"type": "module",
|
||||
"version": "0.15.9",
|
||||
"version": "0.15.12",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
# cojson-transport-nodejs-ws
|
||||
|
||||
## 0.15.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cojson@0.15.12
|
||||
|
||||
## 0.15.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cojson@0.15.11
|
||||
|
||||
## 0.15.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cojson@0.15.10
|
||||
|
||||
## 0.15.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "cojson-transport-ws",
|
||||
"type": "module",
|
||||
"version": "0.15.9",
|
||||
"version": "0.15.12",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# cojson
|
||||
|
||||
## 0.15.12
|
||||
|
||||
## 0.15.11
|
||||
|
||||
## 0.15.10
|
||||
|
||||
## 0.15.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
},
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"version": "0.15.9",
|
||||
"version": "0.15.12",
|
||||
"devDependencies": {
|
||||
"@opentelemetry/sdk-metrics": "^2.0.0",
|
||||
"libsql": "^0.5.13",
|
||||
|
||||
@@ -82,6 +82,11 @@ export class LocalNode {
|
||||
this.storage = storage;
|
||||
}
|
||||
|
||||
removeStorage() {
|
||||
this.storage?.close();
|
||||
this.storage = undefined;
|
||||
}
|
||||
|
||||
getCoValue(id: RawCoID) {
|
||||
let entry = this.coValues.get(id);
|
||||
|
||||
@@ -348,12 +353,10 @@ export class LocalNode {
|
||||
skipLoadingFromPeer?: PeerID,
|
||||
skipRetry?: boolean,
|
||||
): Promise<CoValueCore> {
|
||||
if (!id) {
|
||||
throw new Error("Trying to load CoValue with undefined id");
|
||||
}
|
||||
|
||||
if (!id.startsWith("co_z")) {
|
||||
throw new Error(`Trying to load CoValue with invalid id ${id}`);
|
||||
if (typeof id !== "string" || !id.startsWith("co_z")) {
|
||||
throw new TypeError(
|
||||
`Trying to load CoValue with invalid id ${Array.isArray(id) ? JSON.stringify(id) : id}`,
|
||||
);
|
||||
}
|
||||
|
||||
if (this.crashed) {
|
||||
|
||||
@@ -54,6 +54,38 @@ describe("loading coValues from server", () => {
|
||||
`);
|
||||
});
|
||||
|
||||
test("coValue load throws on invalid id", async () => {
|
||||
const { node } = setupTestNode({
|
||||
connected: true,
|
||||
});
|
||||
|
||||
expect(async () => await node.load("test" as any)).rejects.toThrow(
|
||||
"Trying to load CoValue with invalid id test",
|
||||
);
|
||||
expect(async () => await node.load(null as any)).rejects.toThrow(
|
||||
"Trying to load CoValue with invalid id null",
|
||||
);
|
||||
expect(async () => await node.load(undefined as any)).rejects.toThrow(
|
||||
"Trying to load CoValue with invalid id undefined",
|
||||
);
|
||||
expect(async () => await node.load(1 as any)).rejects.toThrow(
|
||||
"Trying to load CoValue with invalid id 1",
|
||||
);
|
||||
expect(async () => await node.load({} as any)).rejects.toThrow(
|
||||
"Trying to load CoValue with invalid id [object Object]",
|
||||
);
|
||||
expect(async () => await node.load([] as any)).rejects.toThrow(
|
||||
"Trying to load CoValue with invalid id []",
|
||||
);
|
||||
expect(async () => await node.load(["test"] as any)).rejects.toThrow(
|
||||
'Trying to load CoValue with invalid id ["test"]',
|
||||
);
|
||||
expect(async () => await node.load((() => {}) as any)).rejects.toThrow(
|
||||
"Trying to load CoValue with invalid id () => {\n }",
|
||||
);
|
||||
expect(async () => await node.load(new Date() as any)).rejects.toThrow();
|
||||
});
|
||||
|
||||
test("unavailable coValue retry with skipRetry set to true", async () => {
|
||||
const client = setupTestNode();
|
||||
const client2 = setupTestNode();
|
||||
|
||||
@@ -991,7 +991,7 @@ describe("LocalNode.load", () => {
|
||||
|
||||
// @ts-expect-error Testing with undefined ID
|
||||
await expect(client.node.load(undefined)).rejects.toThrow(
|
||||
"Trying to load CoValue with undefined id",
|
||||
"Trying to load CoValue with invalid id undefined",
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -2136,7 +2136,7 @@ See the corresponding sections for [creating](/docs/using-covalues/filestreams#c
|
||||
|
||||
### Unions of CoMaps (declaration)
|
||||
|
||||
You can declare unions of CoMaps that have discriminating fields, using `z.discriminatedUnion()`.
|
||||
You can declare unions of CoMaps that have discriminating fields, using `co.discriminatedUnion()`.
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
@@ -2153,7 +2153,7 @@ const SliderWidget = co.map({
|
||||
max: z.number(),
|
||||
});
|
||||
|
||||
const WidgetUnion = z.discriminatedUnion([ButtonWidget, SliderWidget]);
|
||||
const WidgetUnion = co.discriminatedUnion([ButtonWidget, SliderWidget]);
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
@@ -11911,4 +11911,4 @@ export function cn(...inputs: ClassValue[]) {
|
||||
```ts
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
```
|
||||
```
|
||||
|
||||
@@ -1,5 +1,34 @@
|
||||
# jazz-auth-betterauth
|
||||
|
||||
## 0.15.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [d1c1b0c]
|
||||
- Updated dependencies [cf4ad72]
|
||||
- jazz-tools@0.15.12
|
||||
- jazz-betterauth-client-plugin@0.15.12
|
||||
- cojson@0.15.12
|
||||
|
||||
## 0.15.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [bdc9aee]
|
||||
- jazz-tools@0.15.11
|
||||
- jazz-betterauth-client-plugin@0.15.11
|
||||
- cojson@0.15.11
|
||||
|
||||
## 0.15.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [9815ec6]
|
||||
- Updated dependencies [b4fdab4]
|
||||
- jazz-tools@0.15.10
|
||||
- jazz-betterauth-client-plugin@0.15.10
|
||||
- cojson@0.15.10
|
||||
|
||||
## 0.15.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-auth-betterauth",
|
||||
"version": "0.15.9",
|
||||
"version": "0.15.12",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.ts",
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
# jazz-betterauth-client-plugin
|
||||
|
||||
## 0.15.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-betterauth-server-plugin@0.15.12
|
||||
|
||||
## 0.15.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-betterauth-server-plugin@0.15.11
|
||||
|
||||
## 0.15.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-betterauth-server-plugin@0.15.10
|
||||
|
||||
## 0.15.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-betterauth-client-plugin",
|
||||
"version": "0.15.9",
|
||||
"version": "0.15.12",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.ts",
|
||||
|
||||
@@ -1,5 +1,31 @@
|
||||
# jazz-betterauth-server-plugin
|
||||
|
||||
## 0.15.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [d1c1b0c]
|
||||
- Updated dependencies [cf4ad72]
|
||||
- jazz-tools@0.15.12
|
||||
- cojson@0.15.12
|
||||
|
||||
## 0.15.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [bdc9aee]
|
||||
- jazz-tools@0.15.11
|
||||
- cojson@0.15.11
|
||||
|
||||
## 0.15.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [9815ec6]
|
||||
- Updated dependencies [b4fdab4]
|
||||
- jazz-tools@0.15.10
|
||||
- cojson@0.15.10
|
||||
|
||||
## 0.15.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-betterauth-server-plugin",
|
||||
"version": "0.15.9",
|
||||
"version": "0.15.12",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.ts",
|
||||
|
||||
@@ -1,5 +1,37 @@
|
||||
# jazz-react-auth-betterauth
|
||||
|
||||
## 0.15.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [d1c1b0c]
|
||||
- Updated dependencies [cf4ad72]
|
||||
- jazz-tools@0.15.12
|
||||
- jazz-auth-betterauth@0.15.12
|
||||
- jazz-betterauth-client-plugin@0.15.12
|
||||
- cojson@0.15.12
|
||||
|
||||
## 0.15.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [bdc9aee]
|
||||
- jazz-tools@0.15.11
|
||||
- jazz-auth-betterauth@0.15.11
|
||||
- jazz-betterauth-client-plugin@0.15.11
|
||||
- cojson@0.15.11
|
||||
|
||||
## 0.15.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [9815ec6]
|
||||
- Updated dependencies [b4fdab4]
|
||||
- jazz-tools@0.15.10
|
||||
- jazz-auth-betterauth@0.15.10
|
||||
- jazz-betterauth-client-plugin@0.15.10
|
||||
- cojson@0.15.10
|
||||
|
||||
## 0.15.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-react-auth-betterauth",
|
||||
"version": "0.15.9",
|
||||
"version": "0.15.12",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.tsx",
|
||||
|
||||
@@ -1,5 +1,37 @@
|
||||
# jazz-run
|
||||
|
||||
## 0.15.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [d1c1b0c]
|
||||
- Updated dependencies [cf4ad72]
|
||||
- jazz-tools@0.15.12
|
||||
- cojson@0.15.12
|
||||
- cojson-storage-sqlite@0.15.12
|
||||
- cojson-transport-ws@0.15.12
|
||||
|
||||
## 0.15.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [bdc9aee]
|
||||
- jazz-tools@0.15.11
|
||||
- cojson@0.15.11
|
||||
- cojson-storage-sqlite@0.15.11
|
||||
- cojson-transport-ws@0.15.11
|
||||
|
||||
## 0.15.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [9815ec6]
|
||||
- Updated dependencies [b4fdab4]
|
||||
- jazz-tools@0.15.10
|
||||
- cojson@0.15.10
|
||||
- cojson-storage-sqlite@0.15.10
|
||||
- cojson-transport-ws@0.15.10
|
||||
|
||||
## 0.15.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"bin": "./dist/index.js",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"version": "0.15.9",
|
||||
"version": "0.15.12",
|
||||
"exports": {
|
||||
"./startSyncServer": {
|
||||
"import": "./dist/startSyncServer.js",
|
||||
@@ -28,11 +28,11 @@
|
||||
"@effect/printer-ansi": "^0.34.5",
|
||||
"@effect/schema": "^0.71.1",
|
||||
"@effect/typeclass": "^0.25.5",
|
||||
"cojson": "workspace:0.15.9",
|
||||
"cojson-storage-sqlite": "workspace:0.15.9",
|
||||
"cojson-transport-ws": "workspace:0.15.9",
|
||||
"cojson": "workspace:0.15.12",
|
||||
"cojson-storage-sqlite": "workspace:0.15.12",
|
||||
"cojson-transport-ws": "workspace:0.15.12",
|
||||
"effect": "^3.6.5",
|
||||
"jazz-tools": "workspace:0.15.9",
|
||||
"jazz-tools": "workspace:0.15.12",
|
||||
"ws": "^8.14.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,5 +1,35 @@
|
||||
# jazz-tools
|
||||
|
||||
## 0.15.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- d1c1b0c: Fix stuck authentication when using onAnonymousAccountDiscarded with a storage
|
||||
- cf4ad72: fix unhandled rejection on CoValue.load
|
||||
- cojson@0.15.12
|
||||
- cojson-storage-indexeddb@0.15.12
|
||||
- cojson-transport-ws@0.15.12
|
||||
|
||||
## 0.15.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- bdc9aee: - Add `co.optional` and `co.discriminatedUnion`. You can now `load` and `subcribe` to schemas created with `co.discriminatedUnion`.
|
||||
- Improved type-checking around `z.` schemas to prevent invalid combinations with `co.` schemas.
|
||||
- cojson@0.15.11
|
||||
- cojson-storage-indexeddb@0.15.11
|
||||
- cojson-transport-ws@0.15.11
|
||||
|
||||
## 0.15.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 9815ec6: Export the z.ZodDiscriminatedUnion type
|
||||
- b4fdab4: Exposed the current Account's ID in unauthorized error message
|
||||
- cojson@0.15.10
|
||||
- cojson-storage-indexeddb@0.15.10
|
||||
- cojson-transport-ws@0.15.10
|
||||
|
||||
## 0.15.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -139,7 +139,7 @@
|
||||
},
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"version": "0.15.9",
|
||||
"version": "0.15.12",
|
||||
"dependencies": {
|
||||
"@manuscripts/prosemirror-recreate-steps": "^0.1.4",
|
||||
"@scure/base": "1.2.1",
|
||||
@@ -163,6 +163,7 @@
|
||||
"zod": "3.25.28"
|
||||
},
|
||||
"scripts": {
|
||||
"check": "tsc --noEmit",
|
||||
"format-and-lint": "biome check .",
|
||||
"format-and-lint:fix": "biome check . --write",
|
||||
"dev": "tsup --watch --dts",
|
||||
|
||||
@@ -1,16 +1,6 @@
|
||||
// @vitest-environment happy-dom
|
||||
|
||||
import {
|
||||
Account,
|
||||
CoMap,
|
||||
Loaded,
|
||||
RefsToResolve,
|
||||
Resolved,
|
||||
co,
|
||||
coField,
|
||||
z,
|
||||
zodSchemaToCoSchema,
|
||||
} from "jazz-tools";
|
||||
import { RefsToResolve, co, z, zodSchemaToCoSchema } from "jazz-tools";
|
||||
import { beforeEach, describe, expect, it } from "vitest";
|
||||
import { useAccount, useJazzContextManager } from "../hooks.js";
|
||||
import { useIsAuthenticated } from "../index.js";
|
||||
|
||||
@@ -47,6 +47,26 @@ describe("useCoState", () => {
|
||||
expect(result.current?.value).toBe("123");
|
||||
});
|
||||
|
||||
it("should return null on invalid id", async () => {
|
||||
const TestMap = co.map({
|
||||
value: z.string(),
|
||||
});
|
||||
|
||||
const account = await createJazzTestAccount({
|
||||
isCurrentActiveAccount: true,
|
||||
});
|
||||
|
||||
const { result } = renderHook(() => useCoState(TestMap, "test", {}), {
|
||||
account,
|
||||
});
|
||||
|
||||
expect(result.current).toBeUndefined();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
it("should update the value when the coValue changes", async () => {
|
||||
const TestMap = co.map({
|
||||
value: z.string(),
|
||||
|
||||
@@ -11,10 +11,10 @@ import {
|
||||
anySchemaToCoSchema,
|
||||
coValuesCache,
|
||||
inspect,
|
||||
isCoValueSchema,
|
||||
} from "../internal.js";
|
||||
import type {
|
||||
Account,
|
||||
CoValueClassFromZodSchema,
|
||||
Group,
|
||||
InstanceOfSchemaCoValuesNullable,
|
||||
} from "../internal.js";
|
||||
@@ -101,7 +101,7 @@ export class CoValueBase implements CoValue {
|
||||
: S extends z.core.$ZodType
|
||||
? NonNullable<InstanceOfSchemaCoValuesNullable<S>>
|
||||
: never {
|
||||
const cl = "getCoSchema" in schema ? (schema as any).getCoSchema() : schema;
|
||||
const cl = isCoValueSchema(schema) ? schema.getCoValueClass() : schema;
|
||||
|
||||
if (this.constructor === cl) {
|
||||
return this as any;
|
||||
|
||||
@@ -3,14 +3,12 @@ import { CoStreamItem, RawCoStream } from "cojson";
|
||||
import {
|
||||
type Account,
|
||||
CoValue,
|
||||
CoValueClass,
|
||||
CoValueOrZodSchema,
|
||||
ID,
|
||||
InstanceOfSchema,
|
||||
activeAccountContext,
|
||||
anySchemaToCoSchema,
|
||||
loadCoValue,
|
||||
zodSchemaToCoSchema,
|
||||
} from "../internal.js";
|
||||
|
||||
export type InboxInvite = `${CoID<MessagesStream>}/${InviteSecret}`;
|
||||
|
||||
@@ -1,8 +1,19 @@
|
||||
import {
|
||||
Account,
|
||||
AnonymousJazzAgent,
|
||||
CoValue,
|
||||
CoValueBase,
|
||||
CoValueClass,
|
||||
CoValueFromRaw,
|
||||
ID,
|
||||
RefsToResolve,
|
||||
RefsToResolveStrict,
|
||||
Resolved,
|
||||
SubscribeListenerOptions,
|
||||
SubscribeRestArgs,
|
||||
loadCoValueWithoutMe,
|
||||
parseSubscribeRestArgs,
|
||||
subscribeToCoValueWithoutMe,
|
||||
} from "../internal.js";
|
||||
|
||||
/**
|
||||
@@ -99,4 +110,55 @@ export abstract class SchemaUnion extends CoValueBase implements CoValue {
|
||||
static fromRaw<V extends CoValue>(this: CoValueClass<V>, raw: V["_raw"]): V {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a `SchemaUnion` with a given ID, as a given account.
|
||||
*
|
||||
* Note: The `resolve` option is not supported for `SchemaUnion`s due to https://github.com/garden-co/jazz/issues/2639
|
||||
*
|
||||
* @category Subscription & Loading
|
||||
*/
|
||||
static load<M extends SchemaUnion>(
|
||||
this: CoValueClass<M>,
|
||||
id: ID<M>,
|
||||
options?: {
|
||||
loadAs?: Account | AnonymousJazzAgent;
|
||||
skipRetry?: boolean;
|
||||
},
|
||||
): Promise<Resolved<M, true> | null> {
|
||||
return loadCoValueWithoutMe(this, id, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load and subscribe to a `CoMap` with a given ID, as a given account.
|
||||
*
|
||||
* Automatically also subscribes to updates to all referenced/nested CoValues as soon as they are accessed in the listener.
|
||||
*
|
||||
* Returns an unsubscribe function that you should call when you no longer need updates.
|
||||
*
|
||||
* Also see the `useCoState` hook to reactively subscribe to a CoValue in a React component.
|
||||
*
|
||||
* Note: The `resolve` option is not supported for `SchemaUnion`s due to https://github.com/garden-co/jazz/issues/2639
|
||||
*
|
||||
* @category Subscription & Loading
|
||||
*/
|
||||
static subscribe<M extends SchemaUnion>(
|
||||
this: CoValueClass<M>,
|
||||
id: ID<M>,
|
||||
listener: (value: Resolved<M, true>, unsubscribe: () => void) => void,
|
||||
): () => void;
|
||||
static subscribe<M extends SchemaUnion>(
|
||||
this: CoValueClass<M>,
|
||||
id: ID<M>,
|
||||
options: SubscribeListenerOptions<M, true>,
|
||||
listener: (value: Resolved<M, true>, unsubscribe: () => void) => void,
|
||||
): () => void;
|
||||
static subscribe<M extends SchemaUnion>(
|
||||
this: CoValueClass<M>,
|
||||
id: ID<M>,
|
||||
...args: SubscribeRestArgs<M, true>
|
||||
): () => void {
|
||||
const { options, listener } = parseSubscribeRestArgs(args);
|
||||
return subscribeToCoValueWithoutMe<M, true>(this, id, options, listener);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,8 +279,12 @@ export class JazzContextManager<
|
||||
},
|
||||
);
|
||||
|
||||
prevContext.node.syncManager.addPeer(currentAccountAsPeer);
|
||||
// Closing storage on the prevContext to avoid conflicting transactions and getting stuck on waitForAllCoValuesSync
|
||||
// The storage is reachable through currentContext using the connectedPeers
|
||||
prevContext.node.removeStorage();
|
||||
|
||||
currentContext.node.syncManager.addPeer(prevAccountAsPeer);
|
||||
prevContext.node.syncManager.addPeer(currentAccountAsPeer);
|
||||
|
||||
try {
|
||||
await this.props.onAnonymousAccountDiscarded?.(prevContext.me);
|
||||
|
||||
@@ -10,4 +10,6 @@ export {
|
||||
coImageDefiner as image,
|
||||
coAccountDefiner as account,
|
||||
coProfileDefiner as profile,
|
||||
coOptionalDefiner as optional,
|
||||
coDiscriminatedUnionDefiner as discriminatedUnion,
|
||||
} from "./zodCo.js";
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { CoMap, CoValueClass, isCoValueClass } from "../../../internal.js";
|
||||
import { coField } from "../../schema.js";
|
||||
import { isAnyCoOptionalSchema } from "../schemaTypes/CoOptionalSchema.js";
|
||||
import {
|
||||
isUnionOfCoMapsDeeply,
|
||||
isUnionOfPrimitivesDeeply,
|
||||
@@ -12,10 +13,19 @@ import {
|
||||
ZodReadonly,
|
||||
z,
|
||||
} from "../zodReExport.js";
|
||||
import { ZodPrimitiveSchema } from "../zodSchema.js";
|
||||
import { zodSchemaToCoSchemaOrKeepPrimitive } from "./zodSchemaToCoSchema.js";
|
||||
import { AnyCoSchema, ZodPrimitiveSchema } from "../zodSchema.js";
|
||||
import {
|
||||
isCoValueSchema,
|
||||
zodSchemaToCoSchemaOrKeepPrimitive,
|
||||
} from "./zodSchemaToCoSchema.js";
|
||||
|
||||
type FieldSchema =
|
||||
/**
|
||||
* Types of objects that can be nested inside CoValue schema containers
|
||||
*/
|
||||
type SchemaField =
|
||||
// Schemas created with co.map(), co.record(), co.list(), etc.
|
||||
| AnyCoSchema
|
||||
// CoValue classes created with class syntax, or framework-provided classes like Group
|
||||
| CoValueClass
|
||||
| ZodPrimitiveSchema
|
||||
| z.core.$ZodOptional<z.core.$ZodType>
|
||||
@@ -33,9 +43,16 @@ type FieldSchema =
|
||||
| z.core.$ZodCatch<z.core.$ZodType>
|
||||
| (z.core.$ZodCustom<any, any> & { builtin: any });
|
||||
|
||||
export function zodFieldToCoFieldDef(schema: FieldSchema) {
|
||||
export function schemaFieldToCoFieldDef(schema: SchemaField) {
|
||||
if (isCoValueClass(schema)) {
|
||||
return coField.ref(schema);
|
||||
} else if (isCoValueSchema(schema)) {
|
||||
if (isAnyCoOptionalSchema(schema)) {
|
||||
return coField.ref(schema.getCoValueClass(), {
|
||||
optional: true,
|
||||
});
|
||||
}
|
||||
return coField.ref(schema.getCoValueClass());
|
||||
} else {
|
||||
if ("_zod" in schema) {
|
||||
if (schema._zod.def.type === "optional") {
|
||||
@@ -45,7 +62,7 @@ export function zodFieldToCoFieldDef(schema: FieldSchema) {
|
||||
if (isCoValueClass(inner)) {
|
||||
return coField.ref(inner, { optional: true });
|
||||
} else {
|
||||
return zodFieldToCoFieldDef(inner);
|
||||
return schemaFieldToCoFieldDef(inner);
|
||||
}
|
||||
} else if (schema._zod.def.type === "string") {
|
||||
return coField.string;
|
||||
@@ -58,8 +75,8 @@ export function zodFieldToCoFieldDef(schema: FieldSchema) {
|
||||
} else if (schema._zod.def.type === "enum") {
|
||||
return coField.string;
|
||||
} else if (schema._zod.def.type === "readonly") {
|
||||
return zodFieldToCoFieldDef(
|
||||
(schema as unknown as ZodReadonly).def.innerType as FieldSchema,
|
||||
return schemaFieldToCoFieldDef(
|
||||
(schema as unknown as ZodReadonly).def.innerType as SchemaField,
|
||||
);
|
||||
} else if (schema._zod.def.type === "date") {
|
||||
return coField.optional.Date;
|
||||
@@ -67,8 +84,8 @@ export function zodFieldToCoFieldDef(schema: FieldSchema) {
|
||||
return coField.string;
|
||||
} else if (schema._zod.def.type === "lazy") {
|
||||
// Mostly to support z.json()
|
||||
return zodFieldToCoFieldDef(
|
||||
(schema as unknown as ZodLazy).unwrap() as FieldSchema,
|
||||
return schemaFieldToCoFieldDef(
|
||||
(schema as unknown as ZodLazy).unwrap() as SchemaField,
|
||||
);
|
||||
} else if (
|
||||
schema._zod.def.type === "default" ||
|
||||
@@ -78,9 +95,9 @@ export function zodFieldToCoFieldDef(schema: FieldSchema) {
|
||||
"z.default()/z.catch() are not supported in collaborative schemas. They will be ignored.",
|
||||
);
|
||||
|
||||
return zodFieldToCoFieldDef(
|
||||
return schemaFieldToCoFieldDef(
|
||||
(schema as unknown as ZodDefault | ZodCatch).def
|
||||
.innerType as FieldSchema,
|
||||
.innerType as SchemaField,
|
||||
);
|
||||
} else if (schema._zod.def.type === "literal") {
|
||||
if (
|
||||
@@ -112,7 +129,7 @@ export function zodFieldToCoFieldDef(schema: FieldSchema) {
|
||||
return coField.json();
|
||||
} else if (schema._zod.def.type === "custom") {
|
||||
if ("builtin" in schema) {
|
||||
return zodFieldToCoFieldDef(schema.builtin);
|
||||
return schemaFieldToCoFieldDef(schema.builtin);
|
||||
} else {
|
||||
throw new Error(`Unsupported custom zod type`);
|
||||
}
|
||||
|
||||
@@ -9,89 +9,140 @@ import {
|
||||
CoValueClass,
|
||||
FileStream,
|
||||
SchemaUnion,
|
||||
enrichAccountSchema,
|
||||
enrichCoDiscriminatedUnionSchema,
|
||||
enrichCoFeedSchema,
|
||||
enrichCoListSchema,
|
||||
enrichCoMapSchema,
|
||||
enrichFileStreamSchema,
|
||||
enrichPlainTextSchema,
|
||||
isCoValueClass,
|
||||
} from "../../../internal.js";
|
||||
import { coField } from "../../schema.js";
|
||||
import { isAnyCoOptionalSchema } from "../schemaTypes/CoOptionalSchema.js";
|
||||
import { enrichRichTextSchema } from "../schemaTypes/RichTextSchema.js";
|
||||
import {
|
||||
isUnionOfCoMapsDeeply,
|
||||
schemaUnionDiscriminatorFor,
|
||||
} from "../unionUtils.js";
|
||||
import { z } from "../zodReExport.js";
|
||||
import {
|
||||
CoValueClassFromZodSchema,
|
||||
AnyCoSchema,
|
||||
CoValueClassFromAnySchema,
|
||||
CoValueOrZodSchema,
|
||||
CoValueSchemaFromZodSchema,
|
||||
ZodPrimitiveSchema,
|
||||
getDef,
|
||||
isZodArray,
|
||||
isZodCustom,
|
||||
isZodObject,
|
||||
} from "../zodSchema.js";
|
||||
import { zodFieldToCoFieldDef } from "./zodFieldToCoFieldDef.js";
|
||||
import { schemaFieldToCoFieldDef } from "./zodFieldToCoFieldDef.js";
|
||||
|
||||
let coSchemasForZodSchemas = new Map<z.core.$ZodType, CoValueClass>();
|
||||
let coSchemasForZodSchemas = new Map<z.core.$ZodType, AnyCoSchema>();
|
||||
|
||||
export function tryZodSchemaToCoSchema<S extends z.core.$ZodType>(
|
||||
export function isAnyCoValueSchema(
|
||||
schema: z.core.$ZodType | CoValueClass,
|
||||
): schema is AnyCoSchema {
|
||||
return "collaborative" in schema && schema.collaborative === true;
|
||||
}
|
||||
|
||||
export function isCoValueSchema(
|
||||
schema: z.core.$ZodType | CoValueClass,
|
||||
): schema is CoValueSchemaFromZodSchema<AnyCoSchema> {
|
||||
return isAnyCoValueSchema(schema) && "getCoValueClass" in schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Zod schema into a CoValue schema.
|
||||
*
|
||||
* @param schema A Zod schema that may represent a CoValue schema
|
||||
* @returns The CoValue schema matching the provided ProtoCoSchema, or `null` if the Zod schema
|
||||
* does not match a CoValue schema.
|
||||
*/
|
||||
function tryZodSchemaToCoSchema<S extends z.core.$ZodType>(
|
||||
schema: S,
|
||||
): CoValueClassFromZodSchema<S> | null {
|
||||
if ("collaborative" in schema && schema.collaborative) {
|
||||
): CoValueSchemaFromZodSchema<S> | null {
|
||||
if (isAnyCoValueSchema(schema)) {
|
||||
if (coSchemasForZodSchemas.has(schema)) {
|
||||
return coSchemasForZodSchemas.get(schema) as CoValueClassFromZodSchema<S>;
|
||||
return coSchemasForZodSchemas.get(
|
||||
schema,
|
||||
) as CoValueSchemaFromZodSchema<S>;
|
||||
}
|
||||
|
||||
if (isZodObject(schema)) {
|
||||
if (isAnyCoOptionalSchema(schema)) {
|
||||
// Optional schemas are not supported as top-level schemas
|
||||
return null;
|
||||
} else if (isZodObject(schema)) {
|
||||
const def = getDef(schema);
|
||||
|
||||
const ClassToExtend =
|
||||
"builtin" in schema && schema.builtin === "Account" ? Account : CoMap;
|
||||
|
||||
const coSchema = class ZCoMap extends ClassToExtend {
|
||||
const coValueClass = class ZCoMap extends ClassToExtend {
|
||||
constructor(options: { fromRaw: RawCoMap } | undefined) {
|
||||
super(options);
|
||||
for (const [field, fieldType] of Object.entries(
|
||||
def.shape as z.core.$ZodShape,
|
||||
)) {
|
||||
(this as any)[field] = zodFieldToCoFieldDef(
|
||||
(this as any)[field] = schemaFieldToCoFieldDef(
|
||||
zodSchemaToCoSchemaOrKeepPrimitive(fieldType),
|
||||
);
|
||||
}
|
||||
if (def.catchall) {
|
||||
(this as any)[coField.items] = zodFieldToCoFieldDef(
|
||||
(this as any)[coField.items] = schemaFieldToCoFieldDef(
|
||||
zodSchemaToCoSchemaOrKeepPrimitive(def.catchall),
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
coSchemasForZodSchemas.set(schema, coSchema as unknown as CoValueClass);
|
||||
return coSchema as unknown as CoValueClassFromZodSchema<S>;
|
||||
const coValueSchema =
|
||||
ClassToExtend === Account
|
||||
? enrichAccountSchema(schema as any, coValueClass as any)
|
||||
: enrichCoMapSchema(schema as any, coValueClass as any);
|
||||
|
||||
coSchemasForZodSchemas.set(schema, coValueSchema);
|
||||
return coValueSchema as unknown as CoValueSchemaFromZodSchema<S>;
|
||||
} else if (isZodArray(schema)) {
|
||||
const def = getDef(schema);
|
||||
const coSchema = class ZCoList extends CoList {
|
||||
const coValueClass = class ZCoList extends CoList {
|
||||
constructor(options: { fromRaw: RawCoList } | undefined) {
|
||||
super(options);
|
||||
(this as any)[coField.items] = zodFieldToCoFieldDef(
|
||||
(this as any)[coField.items] = schemaFieldToCoFieldDef(
|
||||
zodSchemaToCoSchemaOrKeepPrimitive(def.element),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
coSchemasForZodSchemas.set(schema, coSchema);
|
||||
return coSchema as unknown as CoValueClassFromZodSchema<S>;
|
||||
const coValueSchema = enrichCoListSchema(schema, coValueClass as any);
|
||||
|
||||
coSchemasForZodSchemas.set(schema, coValueSchema);
|
||||
return coValueSchema as unknown as CoValueSchemaFromZodSchema<S>;
|
||||
} else if (isZodCustom(schema)) {
|
||||
if ("builtin" in schema) {
|
||||
if (schema.builtin === "CoFeed" && "element" in schema) {
|
||||
return CoFeed.Of(
|
||||
zodFieldToCoFieldDef(
|
||||
const coValueClass = CoFeed.Of(
|
||||
schemaFieldToCoFieldDef(
|
||||
zodSchemaToCoSchemaOrKeepPrimitive(
|
||||
schema.element as z.core.$ZodType,
|
||||
),
|
||||
),
|
||||
) as unknown as CoValueClassFromZodSchema<S>;
|
||||
);
|
||||
const coValueSchema = enrichCoFeedSchema(schema, coValueClass as any);
|
||||
return coValueSchema as unknown as CoValueSchemaFromZodSchema<S>;
|
||||
} else if (schema.builtin === "FileStream") {
|
||||
return FileStream as unknown as CoValueClassFromZodSchema<S>;
|
||||
const coValueClass = FileStream;
|
||||
const coValueSchema = enrichFileStreamSchema(schema, coValueClass);
|
||||
return coValueSchema as unknown as CoValueSchemaFromZodSchema<S>;
|
||||
} else if (schema.builtin === "CoPlainText") {
|
||||
return CoPlainText as unknown as CoValueClassFromZodSchema<S>;
|
||||
const coValueClass = CoPlainText;
|
||||
const coValueSchema = enrichPlainTextSchema(schema, coValueClass);
|
||||
return coValueSchema as unknown as CoValueSchemaFromZodSchema<S>;
|
||||
} else if (schema.builtin === "CoRichText") {
|
||||
return CoRichText as unknown as CoValueClassFromZodSchema<S>;
|
||||
const coValueClass = CoRichText;
|
||||
const coValueSchema = enrichRichTextSchema(schema, coValueClass);
|
||||
return coValueSchema as unknown as CoValueSchemaFromZodSchema<S>;
|
||||
} else {
|
||||
throw new Error(`Unsupported builtin type: ${schema.builtin}`);
|
||||
}
|
||||
@@ -105,9 +156,12 @@ export function tryZodSchemaToCoSchema<S extends z.core.$ZodType>(
|
||||
}
|
||||
} else if (schema instanceof z.core.$ZodDiscriminatedUnion) {
|
||||
if (isUnionOfCoMapsDeeply(schema)) {
|
||||
return SchemaUnion.Of(
|
||||
schemaUnionDiscriminatorFor(schema),
|
||||
) as unknown as CoValueClassFromZodSchema<S>;
|
||||
const coValueClass = SchemaUnion.Of(schemaUnionDiscriminatorFor(schema));
|
||||
const coValueSchema = enrichCoDiscriminatedUnionSchema(
|
||||
schema as any,
|
||||
coValueClass as any,
|
||||
);
|
||||
return coValueSchema as unknown as CoValueSchemaFromZodSchema<S>;
|
||||
} else {
|
||||
throw new Error(
|
||||
"z.discriminatedUnion() of non-collaborative types is not supported as a top-level schema",
|
||||
@@ -118,19 +172,9 @@ export function tryZodSchemaToCoSchema<S extends z.core.$ZodType>(
|
||||
}
|
||||
}
|
||||
|
||||
export function zodSchemaToCoSchema<
|
||||
S extends
|
||||
| z.core.$ZodType
|
||||
| (z.core.$ZodObject<any, any> & {
|
||||
builtin: "Account";
|
||||
migration?: (account: any, creationProps?: { name: string }) => void;
|
||||
})
|
||||
| (z.core.$ZodCustom<any, any> & { builtin: "FileStream" })
|
||||
| (z.core.$ZodCustom<any, any> & {
|
||||
builtin: "CoFeed";
|
||||
element: z.core.$ZodType;
|
||||
}),
|
||||
>(schema: S): CoValueClassFromZodSchema<S> {
|
||||
export function zodSchemaToCoSchema<S extends z.core.$ZodType | AnyCoSchema>(
|
||||
schema: S,
|
||||
): CoValueSchemaFromZodSchema<S> {
|
||||
const coSchema = tryZodSchemaToCoSchema(schema);
|
||||
if (!coSchema) {
|
||||
throw new Error(
|
||||
@@ -140,38 +184,24 @@ export function zodSchemaToCoSchema<
|
||||
return coSchema;
|
||||
}
|
||||
|
||||
export function anySchemaToCoSchema<
|
||||
S extends
|
||||
| CoValueClass
|
||||
| z.core.$ZodType
|
||||
| (z.core.$ZodObject<any, any> & {
|
||||
builtin: "Account";
|
||||
migration?: (account: any, creationProps?: { name: string }) => void;
|
||||
})
|
||||
| (z.core.$ZodCustom<any, any> & { builtin: "FileStream" })
|
||||
| (z.core.$ZodCustom<any, any> & {
|
||||
builtin: "CoFeed";
|
||||
element: z.core.$ZodType;
|
||||
}),
|
||||
>(
|
||||
// TODO this should be coValueClassOrAnySchemaToCoValueClass
|
||||
export function anySchemaToCoSchema<S extends CoValueOrZodSchema>(
|
||||
schema: S,
|
||||
): S extends CoValueClass
|
||||
? S
|
||||
: S extends z.core.$ZodType
|
||||
? CoValueClassFromZodSchema<S>
|
||||
: never {
|
||||
): CoValueClassFromAnySchema<S> {
|
||||
if (isCoValueClass(schema)) {
|
||||
return schema as any;
|
||||
} else if ("getCoSchema" in schema) {
|
||||
return (schema as any).getCoSchema() as any;
|
||||
} else if (isCoValueSchema(schema)) {
|
||||
return schema.getCoValueClass() as any;
|
||||
} else if ("def" in schema) {
|
||||
const coSchema = tryZodSchemaToCoSchema(schema as z.core.$ZodType);
|
||||
const coSchema = tryZodSchemaToCoSchema(
|
||||
schema as z.core.$ZodType | AnyCoSchema,
|
||||
);
|
||||
if (!coSchema) {
|
||||
throw new Error(
|
||||
`Unsupported zod type: ${(schema.def as any)?.type || JSON.stringify(schema)}`,
|
||||
);
|
||||
}
|
||||
return coSchema as any;
|
||||
return coSchema.getCoValueClass() as any;
|
||||
}
|
||||
|
||||
throw new Error(`Unsupported schema: ${JSON.stringify(schema)}`);
|
||||
@@ -179,7 +209,7 @@ export function anySchemaToCoSchema<
|
||||
|
||||
export function zodSchemaToCoSchemaOrKeepPrimitive<S extends z.core.$ZodType>(
|
||||
schema: S,
|
||||
): CoValueClassFromZodSchema<S> | ZodPrimitiveSchema {
|
||||
): CoValueSchemaFromZodSchema<S> | ZodPrimitiveSchema {
|
||||
const coSchema = tryZodSchemaToCoSchema(schema);
|
||||
if (!coSchema) {
|
||||
return schema as any;
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { CryptoProvider } from "cojson";
|
||||
import { Account, Group, RefsToResolveStrict } from "../../../internal.js";
|
||||
import {
|
||||
Account,
|
||||
AccountCreationProps,
|
||||
Group,
|
||||
RefsToResolveStrict,
|
||||
} from "../../../internal.js";
|
||||
import { AnonymousJazzAgent } from "../../anonymousJazzAgent.js";
|
||||
import { InstanceOrPrimitiveOfSchema } from "../typeConverters/InstanceOrPrimitiveOfSchema.js";
|
||||
import { InstanceOrPrimitiveOfSchemaCoValuesNullable } from "../typeConverters/InstanceOrPrimitiveOfSchemaCoValuesNullable.js";
|
||||
@@ -57,9 +62,63 @@ export type AccountSchema<
|
||||
) => void,
|
||||
): AccountSchema<Shape>;
|
||||
|
||||
getCoSchema: () => typeof Account;
|
||||
getCoValueClass: () => typeof Account;
|
||||
};
|
||||
|
||||
export function enrichAccountSchema<Shape extends BaseAccountShape>(
|
||||
schema: AnyAccountSchema<Shape>,
|
||||
coValueClass: typeof Account,
|
||||
): AccountSchema<Shape> {
|
||||
const enrichedSchema = Object.assign(schema, {
|
||||
create: (...args: any[]) => {
|
||||
// @ts-expect-error
|
||||
return coValueClass.create(...args);
|
||||
},
|
||||
createAs: (...args: any[]) => {
|
||||
// @ts-expect-error
|
||||
return coValueClass.createAs(...args);
|
||||
},
|
||||
getMe: (...args: any[]) => {
|
||||
// @ts-expect-error
|
||||
return coValueClass.getMe(...args);
|
||||
},
|
||||
load: (...args: any[]) => {
|
||||
// @ts-expect-error
|
||||
return coValueClass.load(...args);
|
||||
},
|
||||
subscribe: (...args: any[]) => {
|
||||
// @ts-expect-error
|
||||
return coValueClass.subscribe(...args);
|
||||
},
|
||||
withHelpers: (helpers: (Self: z.core.$ZodType) => object) => {
|
||||
return Object.assign(schema, helpers(schema));
|
||||
},
|
||||
fromRaw: (...args: any[]) => {
|
||||
// @ts-expect-error
|
||||
return coValueClass.fromRaw(...args);
|
||||
},
|
||||
withMigration: (
|
||||
migration: (
|
||||
value: any,
|
||||
creationProps?: AccountCreationProps,
|
||||
) => void | Promise<void>,
|
||||
) => {
|
||||
(coValueClass.prototype as Account).migrate = async function (
|
||||
this,
|
||||
creationProps,
|
||||
) {
|
||||
await migration(this, creationProps);
|
||||
};
|
||||
|
||||
return enrichedSchema;
|
||||
},
|
||||
getCoValueClass: () => {
|
||||
return coValueClass;
|
||||
},
|
||||
}) as unknown as AccountSchema<Shape>;
|
||||
return enrichedSchema;
|
||||
}
|
||||
|
||||
export type DefaultProfileShape = {
|
||||
name: z.core.$ZodString<string>;
|
||||
inbox: z.core.$ZodOptional<z.core.$ZodString>;
|
||||
@@ -71,7 +130,7 @@ export type CoProfileSchema<
|
||||
Config extends z.core.$ZodObjectConfig = z.core.$ZodObjectConfig,
|
||||
> = CoMapSchema<Shape & DefaultProfileShape, Config, Group>;
|
||||
|
||||
// less precise verion to avoid circularity issues and allow matching against
|
||||
// less precise version to avoid circularity issues and allow matching against
|
||||
export type AnyAccountSchema<
|
||||
Shape extends z.core.$ZodLooseShape = z.core.$ZodLooseShape,
|
||||
> = z.core.$ZodObject<Shape> & {
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
import {
|
||||
Account,
|
||||
AnonymousJazzAgent,
|
||||
AnyCoSchema,
|
||||
InstanceOrPrimitiveOfSchemaCoValuesNullable,
|
||||
RefsToResolve,
|
||||
RefsToResolveStrict,
|
||||
Resolved,
|
||||
SchemaUnion,
|
||||
SubscribeListenerOptions,
|
||||
} from "../../../internal.js";
|
||||
import { z } from "../zodReExport.js";
|
||||
|
||||
export type AnyDiscriminableCoSchema = AnyCoSchema &
|
||||
z.core.$ZodTypeDiscriminable;
|
||||
|
||||
export type AnyCoDiscriminatedUnionSchema<
|
||||
Types extends readonly [
|
||||
AnyDiscriminableCoSchema,
|
||||
...AnyDiscriminableCoSchema[],
|
||||
],
|
||||
> = z.ZodDiscriminatedUnion<Types> & {
|
||||
collaborative: true;
|
||||
};
|
||||
|
||||
export type CoDiscriminatedUnionSchema<
|
||||
Types extends readonly [
|
||||
AnyDiscriminableCoSchema,
|
||||
...AnyDiscriminableCoSchema[],
|
||||
],
|
||||
> = AnyCoDiscriminatedUnionSchema<Types> & {
|
||||
load(
|
||||
id: string,
|
||||
options?: {
|
||||
loadAs?: Account | AnonymousJazzAgent;
|
||||
skipRetry?: boolean;
|
||||
},
|
||||
): Promise<Resolved<
|
||||
CoDiscriminatedUnionInstanceCoValuesNullable<Types> & SchemaUnion,
|
||||
true
|
||||
> | null>;
|
||||
|
||||
subscribe(
|
||||
id: string,
|
||||
options: SubscribeListenerOptions<
|
||||
CoDiscriminatedUnionInstanceCoValuesNullable<Types> & SchemaUnion,
|
||||
true
|
||||
>,
|
||||
listener: (
|
||||
value: Resolved<
|
||||
CoDiscriminatedUnionInstanceCoValuesNullable<Types> & SchemaUnion,
|
||||
true
|
||||
>,
|
||||
unsubscribe: () => void,
|
||||
) => void,
|
||||
): () => void;
|
||||
|
||||
getCoValueClass: () => typeof SchemaUnion;
|
||||
};
|
||||
|
||||
export function enrichCoDiscriminatedUnionSchema<
|
||||
Types extends readonly [
|
||||
AnyDiscriminableCoSchema,
|
||||
...AnyDiscriminableCoSchema[],
|
||||
],
|
||||
>(
|
||||
schema: z.ZodDiscriminatedUnion<Types>,
|
||||
coValueClass: typeof SchemaUnion,
|
||||
): CoDiscriminatedUnionSchema<Types> {
|
||||
return Object.assign(schema, {
|
||||
load: (...args: [any, ...any]) => {
|
||||
// @ts-expect-error
|
||||
return coValueClass.load(...args);
|
||||
},
|
||||
subscribe: (...args: [any, ...any[]]) => {
|
||||
// @ts-expect-error
|
||||
return coValueClass.subscribe(...args);
|
||||
},
|
||||
getCoValueClass: () => {
|
||||
return coValueClass;
|
||||
},
|
||||
}) as unknown as CoDiscriminatedUnionSchema<Types>;
|
||||
}
|
||||
|
||||
type CoDiscriminatedUnionInstanceCoValuesNullable<
|
||||
Types extends readonly [
|
||||
AnyDiscriminableCoSchema,
|
||||
...AnyDiscriminableCoSchema[],
|
||||
],
|
||||
> = NonNullable<InstanceOrPrimitiveOfSchemaCoValuesNullable<Types[number]>>;
|
||||
@@ -58,10 +58,35 @@ export type CoFeedSchema<T extends z.core.$ZodType> = z.core.$ZodCustom<
|
||||
) => void,
|
||||
): () => void;
|
||||
|
||||
getCoSchema: () => typeof CoFeed;
|
||||
getCoValueClass: () => typeof CoFeed;
|
||||
};
|
||||
|
||||
// less precise verion to avoid circularity issues and allow matching against
|
||||
export function enrichCoFeedSchema<T extends z.core.$ZodType>(
|
||||
schema: AnyCoFeedSchema<T>,
|
||||
coValueClass: typeof CoFeed,
|
||||
): CoFeedSchema<T> {
|
||||
return Object.assign(schema, {
|
||||
create: (...args: [any, ...any[]]) => {
|
||||
return coValueClass.create(...args);
|
||||
},
|
||||
load: (...args: [any, ...any[]]) => {
|
||||
// @ts-expect-error
|
||||
return coValueClass.load(...args);
|
||||
},
|
||||
subscribe: (...args: [any, ...any[]]) => {
|
||||
// @ts-expect-error
|
||||
return coValueClass.subscribe(...args);
|
||||
},
|
||||
withHelpers: (helpers: (Self: z.core.$ZodType) => object) => {
|
||||
return Object.assign(schema, helpers(schema));
|
||||
},
|
||||
getCoValueClass: () => {
|
||||
return coValueClass;
|
||||
},
|
||||
}) as unknown as CoFeedSchema<T>;
|
||||
}
|
||||
|
||||
// less precise version to avoid circularity issues and allow matching against
|
||||
export type AnyCoFeedSchema<T extends z.core.$ZodType = z.core.$ZodType> =
|
||||
z.core.$ZodCustom<any, unknown> & {
|
||||
collaborative: true;
|
||||
|
||||
@@ -19,9 +19,7 @@ type CoListInit<T extends z.core.$ZodType> = Array<
|
||||
: NonNullable<InstanceOrPrimitiveOfSchemaCoValuesNullable<T>>
|
||||
>;
|
||||
|
||||
export type CoListSchema<T extends z.core.$ZodType> = z.core.$ZodArray<T> & {
|
||||
collaborative: true;
|
||||
|
||||
export type CoListSchema<T extends z.core.$ZodType> = AnyCoListSchema<T> & {
|
||||
create: (
|
||||
items: CoListInit<T>,
|
||||
options?: { owner: Account | Group } | Account | Group,
|
||||
@@ -52,10 +50,34 @@ export type CoListSchema<T extends z.core.$ZodType> = z.core.$ZodArray<T> & {
|
||||
helpers: (Self: S) => T,
|
||||
): WithHelpers<S, T>;
|
||||
|
||||
getCoSchema: () => typeof CoList;
|
||||
getCoValueClass: () => typeof CoList;
|
||||
};
|
||||
|
||||
// less precise verion to avoid circularity issues and allow matching against
|
||||
export function enrichCoListSchema<T extends z.core.$ZodType>(
|
||||
schema: AnyCoListSchema<T>,
|
||||
coValueClass: typeof CoList,
|
||||
): CoListSchema<T> {
|
||||
return Object.assign(schema, {
|
||||
create: (...args: [any, ...any[]]) => {
|
||||
return coValueClass.create(...args);
|
||||
},
|
||||
load: (...args: [any, ...any[]]) => {
|
||||
return coValueClass.load(...args);
|
||||
},
|
||||
subscribe: (...args: [any, ...any[]]) => {
|
||||
// @ts-expect-error
|
||||
return coValueClass.subscribe(...args);
|
||||
},
|
||||
withHelpers: (helpers: (Self: z.core.$ZodType) => object) => {
|
||||
return Object.assign(schema, helpers(schema));
|
||||
},
|
||||
getCoValueClass: () => {
|
||||
return coValueClass;
|
||||
},
|
||||
}) as unknown as CoListSchema<T>;
|
||||
}
|
||||
|
||||
// less precise version to avoid circularity issues and allow matching against
|
||||
export type AnyCoListSchema<T extends z.core.$ZodType = z.core.$ZodType> =
|
||||
z.core.$ZodArray<T> & { collaborative: true };
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
Resolved,
|
||||
Simplify,
|
||||
SubscribeListenerOptions,
|
||||
zodSchemaToCoSchema,
|
||||
} from "../../../internal.js";
|
||||
import { AnonymousJazzAgent } from "../../anonymousJazzAgent.js";
|
||||
import { InstanceOrPrimitiveOfSchema } from "../typeConverters/InstanceOrPrimitiveOfSchema.js";
|
||||
@@ -19,10 +20,8 @@ export type CoMapSchema<
|
||||
Shape extends z.core.$ZodLooseShape,
|
||||
Config extends z.core.$ZodObjectConfig = z.core.$ZodObjectConfig,
|
||||
Owner extends Account | Group = Account | Group,
|
||||
> = z.core.$ZodObject<Shape, Config> &
|
||||
> = AnyCoMapSchema<Shape, Config> &
|
||||
z.$ZodTypeDiscriminable & {
|
||||
collaborative: true;
|
||||
|
||||
create: (
|
||||
init: Simplify<CoMapInitZod<Shape>>,
|
||||
options?:
|
||||
@@ -145,9 +144,65 @@ export type CoMapSchema<
|
||||
) => undefined,
|
||||
): CoMapSchema<Shape, Config, Owner>;
|
||||
|
||||
getCoSchema: () => typeof CoMap;
|
||||
getCoValueClass: () => typeof CoMap;
|
||||
};
|
||||
|
||||
export function enrichCoMapSchema<
|
||||
Shape extends z.core.$ZodLooseShape,
|
||||
Config extends z.core.$ZodObjectConfig,
|
||||
>(
|
||||
schema: AnyCoMapSchema<Shape, Config>,
|
||||
coValueClass: typeof CoMap,
|
||||
): CoMapSchema<Shape, Config> {
|
||||
// @ts-expect-error schema is actually a z.ZodObject, but we need to use z.core.$ZodObject to avoid circularity issues
|
||||
const baseCatchall = schema.catchall;
|
||||
const coValueSchema = Object.assign(schema, {
|
||||
create: (...args: [any, ...any[]]) => {
|
||||
return coValueClass.create(...args);
|
||||
},
|
||||
load: (...args: [any, ...any[]]) => {
|
||||
return coValueClass.load(...args);
|
||||
},
|
||||
subscribe: (...args: [any, ...any[]]) => {
|
||||
// @ts-expect-error
|
||||
return coValueClass.subscribe(...args);
|
||||
},
|
||||
findUnique: (...args: [any, ...any[]]) => {
|
||||
// @ts-expect-error
|
||||
return coValueClass.findUnique(...args);
|
||||
},
|
||||
upsertUnique: (...args: [any, ...any[]]) => {
|
||||
// @ts-expect-error
|
||||
return coValueClass.upsertUnique(...args);
|
||||
},
|
||||
loadUnique: (...args: [any, ...any[]]) => {
|
||||
// @ts-expect-error
|
||||
return coValueClass.loadUnique(...args);
|
||||
},
|
||||
catchall: (index: z.core.$ZodType) => {
|
||||
const newSchema = baseCatchall(index);
|
||||
// TODO avoid repeating this with coMapDefiner
|
||||
const enrichedSchema = Object.assign(newSchema, {
|
||||
collaborative: true,
|
||||
}) as AnyCoMapSchema<Shape, Config>;
|
||||
return zodSchemaToCoSchema(enrichedSchema);
|
||||
},
|
||||
withHelpers: (helpers: (Self: z.core.$ZodType) => object) => {
|
||||
return Object.assign(schema, helpers(schema));
|
||||
},
|
||||
withMigration: (migration: (value: any) => undefined) => {
|
||||
// @ts-expect-error TODO check
|
||||
coValueClass.prototype.migrate = migration;
|
||||
|
||||
return coValueSchema;
|
||||
},
|
||||
getCoValueClass: () => {
|
||||
return coValueClass;
|
||||
},
|
||||
}) as unknown as CoMapSchema<Shape, Config>;
|
||||
return coValueSchema;
|
||||
}
|
||||
|
||||
export type optionalKeys<Shape extends z.core.$ZodLooseShape> = {
|
||||
[key in keyof Shape]: Shape[key] extends z.core.$ZodOptional<any>
|
||||
? key
|
||||
@@ -170,7 +225,7 @@ export type CoMapInitZod<Shape extends z.core.$ZodLooseShape> = {
|
||||
>;
|
||||
} & { [key in keyof Shape]?: unknown };
|
||||
|
||||
// less precise verion to avoid circularity issues and allow matching against
|
||||
// less precise version to avoid circularity issues and allow matching against
|
||||
export type AnyCoMapSchema<
|
||||
Shape extends z.core.$ZodLooseShape = z.core.$ZodLooseShape,
|
||||
Config extends z.core.$ZodObjectConfig = z.core.$ZodObjectConfig,
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { isAnyCoValueSchema } from "../runtimeConverters/zodSchemaToCoSchema.js";
|
||||
import { z } from "../zodReExport.js";
|
||||
import { AnyCoSchema, CoValueSchemaFromZodSchema } from "../zodSchema.js";
|
||||
|
||||
export type AnyCoOptionalSchema<
|
||||
Shape extends z.core.$ZodType = z.core.$ZodType,
|
||||
> = z.ZodOptional<Shape> & {
|
||||
collaborative: true;
|
||||
};
|
||||
|
||||
export type CoOptionalSchema<Shape extends z.core.$ZodType = z.core.$ZodType> =
|
||||
AnyCoOptionalSchema<Shape> & {
|
||||
getCoValueClass: () => CoValueSchemaFromZodSchema<AnyCoSchema>["getCoValueClass"];
|
||||
};
|
||||
|
||||
export function createCoOptionalSchema<T extends AnyCoSchema>(
|
||||
schema: T,
|
||||
): CoOptionalSchema<T> {
|
||||
return Object.assign(z.optional(schema), {
|
||||
collaborative: true,
|
||||
getCoValueClass: () => {
|
||||
return (
|
||||
schema as CoValueSchemaFromZodSchema<AnyCoSchema>
|
||||
).getCoValueClass();
|
||||
},
|
||||
}) as unknown as CoOptionalSchema<T>;
|
||||
}
|
||||
|
||||
export function isAnyCoOptionalSchema(
|
||||
schema: z.core.$ZodType,
|
||||
): schema is CoOptionalSchema<z.core.$ZodType> {
|
||||
return isAnyCoValueSchema(schema) && schema._zod.def.type === "optional";
|
||||
}
|
||||
@@ -28,9 +28,7 @@ type CoRecordInit<
|
||||
export type CoRecordSchema<
|
||||
K extends z.core.$ZodString<string>,
|
||||
V extends z.core.$ZodType,
|
||||
> = z.core.$ZodRecord<K, V> & {
|
||||
collaborative: true;
|
||||
|
||||
> = AnyCoRecordSchema<K, V> & {
|
||||
create: (
|
||||
init: Simplify<CoRecordInit<K, V>>,
|
||||
options?:
|
||||
@@ -83,10 +81,10 @@ export type CoRecordSchema<
|
||||
this: S,
|
||||
helpers: (Self: S) => T,
|
||||
): WithHelpers<S, T>;
|
||||
getCoSchema: () => typeof CoMap;
|
||||
getCoValueClass: () => typeof CoMap;
|
||||
};
|
||||
|
||||
// less precise verion to avoid circularity issues and allow matching against
|
||||
// less precise version to avoid circularity issues and allow matching against
|
||||
export type AnyCoRecordSchema<
|
||||
K extends z.core.$ZodString<string> = z.core.$ZodString<string>,
|
||||
V extends z.core.$ZodType = z.core.$ZodType,
|
||||
|
||||
@@ -6,9 +6,12 @@ import {
|
||||
} from "../../../internal.js";
|
||||
import { z } from "../zodReExport.js";
|
||||
|
||||
export type FileStreamSchema = z.core.$ZodCustom<FileStream, unknown> & {
|
||||
export type AnyFileStreamSchema = z.core.$ZodCustom<FileStream, unknown> & {
|
||||
collaborative: true;
|
||||
builtin: "FileStream";
|
||||
};
|
||||
|
||||
export type FileStreamSchema = AnyFileStreamSchema & {
|
||||
create(options?: { owner?: Account | Group } | Account | Group): FileStream;
|
||||
createFromBlob(
|
||||
blob: Blob | File,
|
||||
@@ -40,5 +43,32 @@ export type FileStreamSchema = z.core.$ZodCustom<FileStream, unknown> & {
|
||||
id: string,
|
||||
listener: (value: FileStream, unsubscribe: () => void) => void,
|
||||
): () => void;
|
||||
getCoSchema: () => typeof FileStream;
|
||||
getCoValueClass: () => typeof FileStream;
|
||||
};
|
||||
|
||||
export function enrichFileStreamSchema(
|
||||
schema: AnyFileStreamSchema,
|
||||
coValueClass: typeof FileStream,
|
||||
): FileStreamSchema {
|
||||
return Object.assign(schema, {
|
||||
create: (...args: any[]) => {
|
||||
return coValueClass.create(...args);
|
||||
},
|
||||
createFromBlob: (...args: [any, ...any[]]) => {
|
||||
return coValueClass.createFromBlob(...args);
|
||||
},
|
||||
load: (...args: [any, ...any[]]) => {
|
||||
return coValueClass.load(...args);
|
||||
},
|
||||
loadAsBlob: (...args: [any, ...any[]]) => {
|
||||
return coValueClass.loadAsBlob(...args);
|
||||
},
|
||||
subscribe: (...args: [any, ...any[]]) => {
|
||||
// @ts-expect-error
|
||||
return coValueClass.subscribe(...args);
|
||||
},
|
||||
getCoValueClass: () => {
|
||||
return coValueClass;
|
||||
},
|
||||
}) as unknown as FileStreamSchema;
|
||||
}
|
||||
|
||||
@@ -3,9 +3,12 @@ import { Account, CoPlainText, Group } from "../../../internal.js";
|
||||
import { AnonymousJazzAgent } from "../../anonymousJazzAgent.js";
|
||||
import { z } from "../zodReExport.js";
|
||||
|
||||
export type PlainTextSchema = z.core.$ZodCustom<CoPlainText, unknown> & {
|
||||
export type AnyPlainTextSchema = z.core.$ZodCustom<CoPlainText, unknown> & {
|
||||
collaborative: true;
|
||||
builtin: "CoPlainText";
|
||||
};
|
||||
|
||||
export type PlainTextSchema = AnyPlainTextSchema & {
|
||||
create(
|
||||
text: string,
|
||||
options?: { owner: Account | Group } | Account | Group,
|
||||
@@ -24,5 +27,30 @@ export type PlainTextSchema = z.core.$ZodCustom<CoPlainText, unknown> & {
|
||||
listener: (value: CoPlainText, unsubscribe: () => void) => void,
|
||||
): () => void;
|
||||
fromRaw(raw: RawCoPlainText): CoPlainText;
|
||||
getCoSchema: () => typeof CoPlainText;
|
||||
getCoValueClass: () => typeof CoPlainText;
|
||||
};
|
||||
|
||||
export function enrichPlainTextSchema(
|
||||
schema: AnyPlainTextSchema,
|
||||
coValueClass: typeof CoPlainText,
|
||||
): PlainTextSchema {
|
||||
return Object.assign(schema, {
|
||||
create: (...args: [any, ...any[]]) => {
|
||||
return coValueClass.create(...args);
|
||||
},
|
||||
load: (...args: [any, ...any[]]) => {
|
||||
return coValueClass.load(...args);
|
||||
},
|
||||
subscribe: (...args: [any, ...any[]]) => {
|
||||
// @ts-expect-error
|
||||
return coValueClass.subscribe(...args);
|
||||
},
|
||||
fromRaw: (...args: [any, ...any[]]) => {
|
||||
// @ts-expect-error
|
||||
return coValueClass.fromRaw(...args);
|
||||
},
|
||||
getCoValueClass: () => {
|
||||
return coValueClass;
|
||||
},
|
||||
}) as unknown as PlainTextSchema;
|
||||
}
|
||||
|
||||
@@ -2,9 +2,12 @@ import { Account, CoRichText, Group } from "../../../internal.js";
|
||||
import { AnonymousJazzAgent } from "../../anonymousJazzAgent.js";
|
||||
import { z } from "../zodReExport.js";
|
||||
|
||||
export type RichTextSchema = z.core.$ZodCustom<CoRichText, unknown> & {
|
||||
export type AnyRichTextSchema = z.core.$ZodCustom<CoRichText, unknown> & {
|
||||
collaborative: true;
|
||||
builtin: "CoRichText";
|
||||
};
|
||||
|
||||
export type RichTextSchema = AnyRichTextSchema & {
|
||||
create(
|
||||
text: string,
|
||||
options?: { owner: Account | Group } | Account | Group,
|
||||
@@ -22,5 +25,26 @@ export type RichTextSchema = z.core.$ZodCustom<CoRichText, unknown> & {
|
||||
id: string,
|
||||
listener: (value: CoRichText, unsubscribe: () => void) => void,
|
||||
): () => void;
|
||||
getCoSchema: () => typeof CoRichText;
|
||||
getCoValueClass: () => typeof CoRichText;
|
||||
};
|
||||
|
||||
export function enrichRichTextSchema(
|
||||
schema: AnyRichTextSchema,
|
||||
coValueClass: typeof CoRichText,
|
||||
): RichTextSchema {
|
||||
return Object.assign(schema, {
|
||||
create: (...args: [any, ...any[]]) => {
|
||||
return coValueClass.create(...args);
|
||||
},
|
||||
load: (...args: [any, ...any[]]) => {
|
||||
return coValueClass.load(...args);
|
||||
},
|
||||
subscribe: (...args: [any, ...any[]]) => {
|
||||
// @ts-expect-error
|
||||
return coValueClass.subscribe(...args);
|
||||
},
|
||||
getCoValueClass: () => {
|
||||
return coValueClass;
|
||||
},
|
||||
}) as unknown as RichTextSchema;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import {
|
||||
Account,
|
||||
AnyAccountSchema,
|
||||
AnyCoRecordSchema,
|
||||
CoFeed,
|
||||
CoList,
|
||||
CoMap,
|
||||
@@ -11,24 +13,19 @@ import {
|
||||
import { AnyCoFeedSchema } from "../schemaTypes/CoFeedSchema.js";
|
||||
import { AnyCoListSchema } from "../schemaTypes/CoListSchema.js";
|
||||
import { AnyCoMapSchema } from "../schemaTypes/CoMapSchema.js";
|
||||
import { FileStreamSchema } from "../schemaTypes/FileStreamSchema.js";
|
||||
import { PlainTextSchema } from "../schemaTypes/PlainTextSchema.js";
|
||||
import { RichTextSchema } from "../schemaTypes/RichTextSchema.js";
|
||||
import { AnyFileStreamSchema } from "../schemaTypes/FileStreamSchema.js";
|
||||
import { AnyPlainTextSchema } from "../schemaTypes/PlainTextSchema.js";
|
||||
import { AnyRichTextSchema } from "../schemaTypes/RichTextSchema.js";
|
||||
import { z } from "../zodReExport.js";
|
||||
import { InstanceOrPrimitiveOfSchema } from "./InstanceOrPrimitiveOfSchema.js";
|
||||
|
||||
export type InstanceOfSchema<S extends CoValueClass | z.core.$ZodType> =
|
||||
S extends z.core.$ZodType
|
||||
? S extends z.core.$ZodObject<infer Shape> & {
|
||||
collaborative: true;
|
||||
builtin: "Account";
|
||||
}
|
||||
? S extends AnyAccountSchema<infer Shape>
|
||||
? {
|
||||
[key in keyof Shape]: InstanceOrPrimitiveOfSchema<Shape[key]>;
|
||||
} & Account
|
||||
: S extends z.core.$ZodRecord<infer K, infer V> & {
|
||||
collaborative: true;
|
||||
}
|
||||
: S extends AnyCoRecordSchema<infer K, infer V>
|
||||
? {
|
||||
[key in z.output<K> & string]: InstanceOrPrimitiveOfSchema<V>;
|
||||
} & CoMap
|
||||
@@ -45,11 +42,11 @@ export type InstanceOfSchema<S extends CoValueClass | z.core.$ZodType> =
|
||||
? CoList<InstanceOrPrimitiveOfSchema<T>>
|
||||
: S extends AnyCoFeedSchema<infer T>
|
||||
? CoFeed<InstanceOrPrimitiveOfSchema<T>>
|
||||
: S extends PlainTextSchema
|
||||
: S extends AnyPlainTextSchema
|
||||
? CoPlainText
|
||||
: S extends RichTextSchema
|
||||
: S extends AnyRichTextSchema
|
||||
? CoRichText
|
||||
: S extends FileStreamSchema
|
||||
: S extends AnyFileStreamSchema
|
||||
? FileStream
|
||||
: S extends z.core.$ZodOptional<infer Inner>
|
||||
? InstanceOrPrimitiveOfSchema<Inner>
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import { RawAccount, RawCoList, RawCoMap } from "cojson";
|
||||
import { zodSchemaToCoSchema } from "./runtimeConverters/zodSchemaToCoSchema.js";
|
||||
import { CoValueClass, CoValueFromRaw } from "../../internal.js";
|
||||
import {
|
||||
isAnyCoValueSchema,
|
||||
zodSchemaToCoSchema,
|
||||
} from "./runtimeConverters/zodSchemaToCoSchema.js";
|
||||
import { AccountSchema } from "./schemaTypes/AccountSchema.js";
|
||||
import { CoListSchema } from "./schemaTypes/CoListSchema.js";
|
||||
import { CoMapSchema } from "./schemaTypes/CoMapSchema.js";
|
||||
import { z } from "./zodReExport.js";
|
||||
|
||||
export function schemaUnionDiscriminatorFor(
|
||||
@@ -8,7 +15,7 @@ export function schemaUnionDiscriminatorFor(
|
||||
if (isUnionOfCoMapsDeeply(schema)) {
|
||||
if (!schema._zod.disc || schema._zod.disc.size == 0) {
|
||||
throw new Error(
|
||||
"z.union() of collaborative types is not supported, use z.discriminatedUnion() instead",
|
||||
"z.union() of collaborative types is not supported, use co.discriminatedUnion() instead",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,14 +24,14 @@ export function schemaUnionDiscriminatorFor(
|
||||
|
||||
if (!field) {
|
||||
throw new Error(
|
||||
"z.discriminatedUnion() of collaborative types with non-existent discriminator key is not supported",
|
||||
"co.discriminatedUnion() of collaborative types with non-existent discriminator key is not supported",
|
||||
);
|
||||
}
|
||||
|
||||
for (const value of field.values) {
|
||||
if (typeof value !== "string" && typeof value !== "number") {
|
||||
throw new Error(
|
||||
"z.discriminatedUnion() of collaborative types with non-string or non-number discriminator value is not supported",
|
||||
"co.discriminatedUnion() of collaborative types with non-string or non-number discriminator value is not supported",
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -42,7 +49,7 @@ export function schemaUnionDiscriminatorFor(
|
||||
}
|
||||
} else {
|
||||
throw new Error(
|
||||
"Unsupported zod type in z.discriminatedUnion() of collaborative types",
|
||||
"Unsupported zod type in co.discriminatedUnion() of collaborative types",
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -50,7 +57,7 @@ export function schemaUnionDiscriminatorFor(
|
||||
const determineSchema = (_raw: RawCoMap | RawAccount | RawCoList) => {
|
||||
if (_raw instanceof RawCoList) {
|
||||
throw new Error(
|
||||
"z.discriminatedUnion() of collaborative types is not supported for CoLists",
|
||||
"co.discriminatedUnion() of collaborative types is not supported for CoLists",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -90,12 +97,17 @@ export function schemaUnionDiscriminatorFor(
|
||||
}
|
||||
|
||||
if (match) {
|
||||
return zodSchemaToCoSchema(option);
|
||||
const coValueSchema = zodSchemaToCoSchema(option) as
|
||||
| CoMapSchema<any>
|
||||
| AccountSchema
|
||||
| CoListSchema<any>;
|
||||
return coValueSchema.getCoValueClass() as CoValueClass<any> &
|
||||
CoValueFromRaw<any>;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
"z.discriminatedUnion() of collaborative types with no matching discriminator value found",
|
||||
"co.discriminatedUnion() of collaborative types with no matching discriminator value found",
|
||||
);
|
||||
};
|
||||
|
||||
@@ -120,11 +132,7 @@ export function isUnionOfCoMapsDeeply(
|
||||
function isCoMapOrUnionOfCoMapsDeeply(
|
||||
schema: z.core.$ZodType,
|
||||
): schema is z.core.$ZodDiscriminatedUnion {
|
||||
if (
|
||||
schema instanceof z.core.$ZodObject &&
|
||||
"collaborative" in schema &&
|
||||
schema.collaborative
|
||||
) {
|
||||
if (schema instanceof z.core.$ZodObject && isAnyCoValueSchema(schema)) {
|
||||
return true;
|
||||
} else if (schema instanceof z.core.$ZodUnion) {
|
||||
return schema._zod.def.options.every(isCoMapOrUnionOfCoMapsDeeply);
|
||||
@@ -137,6 +145,6 @@ export function isUnionOfPrimitivesDeeply(schema: z.core.$ZodType) {
|
||||
if (schema instanceof z.core.$ZodUnion) {
|
||||
return schema._zod.def.options.every(isUnionOfPrimitivesDeeply);
|
||||
} else {
|
||||
return !("collaborative" in schema);
|
||||
return !isAnyCoValueSchema(schema);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import {
|
||||
type Account,
|
||||
type AccountCreationProps,
|
||||
type AccountSchema,
|
||||
type AnyCoMapSchema,
|
||||
AnyAccountSchema,
|
||||
AnyCoFeedSchema,
|
||||
AnyCoListSchema,
|
||||
AnyCoSchema,
|
||||
AnyFileStreamSchema,
|
||||
AnyPlainTextSchema,
|
||||
BaseAccountShape,
|
||||
CoFeed,
|
||||
type CoFeedSchema,
|
||||
@@ -20,121 +23,31 @@ import {
|
||||
type Simplify,
|
||||
zodSchemaToCoSchema,
|
||||
} from "../../internal.js";
|
||||
import { RichTextSchema } from "./schemaTypes/RichTextSchema.js";
|
||||
import {
|
||||
AnyDiscriminableCoSchema,
|
||||
CoDiscriminatedUnionSchema,
|
||||
} from "./schemaTypes/CoDiscriminatedUnionSchema.js";
|
||||
import {
|
||||
CoOptionalSchema,
|
||||
createCoOptionalSchema,
|
||||
} from "./schemaTypes/CoOptionalSchema.js";
|
||||
import {
|
||||
AnyRichTextSchema,
|
||||
RichTextSchema,
|
||||
} from "./schemaTypes/RichTextSchema.js";
|
||||
import { z } from "./zodReExport.js";
|
||||
|
||||
function enrichCoMapSchema<Shape extends z.core.$ZodLooseShape>(
|
||||
schema: z.ZodObject<
|
||||
{ -readonly [P in keyof Shape]: Shape[P] },
|
||||
z.core.$strip
|
||||
>,
|
||||
) {
|
||||
const baseCatchall = schema.catchall;
|
||||
|
||||
const enrichedSchema = Object.assign(schema, {
|
||||
collaborative: true,
|
||||
create: (...args: any[]) => {
|
||||
return coSchema.create(...args);
|
||||
},
|
||||
load: (...args: any[]) => {
|
||||
return coSchema.load(...args);
|
||||
},
|
||||
subscribe: (...args: any[]) => {
|
||||
return coSchema.subscribe(...args);
|
||||
},
|
||||
findUnique: (...args: any[]) => {
|
||||
return coSchema.findUnique(...args);
|
||||
},
|
||||
upsertUnique: (...args: any[]) => {
|
||||
return coSchema.upsertUnique(...args);
|
||||
},
|
||||
loadUnique: (...args: any[]) => {
|
||||
return coSchema.loadUnique(...args);
|
||||
},
|
||||
catchall: (index: z.core.$ZodType) => {
|
||||
return enrichCoMapSchema(baseCatchall(index));
|
||||
},
|
||||
withHelpers: (helpers: (Self: z.core.$ZodType) => object) => {
|
||||
return Object.assign(schema, helpers(schema));
|
||||
},
|
||||
withMigration: (migration: (value: any) => undefined) => {
|
||||
coSchema.prototype.migrate = migration;
|
||||
|
||||
return enrichedSchema;
|
||||
},
|
||||
getCoSchema: () => {
|
||||
return coSchema;
|
||||
},
|
||||
}) as unknown as CoMapSchema<Shape>;
|
||||
|
||||
// Needs to be derived from the enriched schema
|
||||
const coSchema = zodSchemaToCoSchema(enrichedSchema) as any;
|
||||
|
||||
return enrichedSchema;
|
||||
}
|
||||
|
||||
export const coMapDefiner = <Shape extends z.core.$ZodLooseShape>(
|
||||
shape: Shape,
|
||||
): CoMapSchema<Shape> => {
|
||||
const objectSchema = z.object(shape).meta({
|
||||
collaborative: true,
|
||||
});
|
||||
|
||||
return enrichCoMapSchema(objectSchema);
|
||||
};
|
||||
|
||||
function enrichAccountSchema<Shape extends BaseAccountShape>(
|
||||
schema: z.ZodObject<Shape, z.core.$strip>,
|
||||
) {
|
||||
const enrichedSchema = Object.assign(schema, {
|
||||
const enrichedSchema = Object.assign(objectSchema, {
|
||||
collaborative: true,
|
||||
builtin: "Account",
|
||||
create: (...args: any[]) => {
|
||||
return coSchema.create(...args);
|
||||
},
|
||||
createAs: (...args: any[]) => {
|
||||
return coSchema.createAs(...args);
|
||||
},
|
||||
getMe: (...args: any[]) => {
|
||||
return coSchema.getMe(...args);
|
||||
},
|
||||
load: (...args: any[]) => {
|
||||
return coSchema.load(...args);
|
||||
},
|
||||
subscribe: (...args: any[]) => {
|
||||
return coSchema.subscribe(...args);
|
||||
},
|
||||
withHelpers: (helpers: (Self: z.core.$ZodType) => object) => {
|
||||
return Object.assign(schema, helpers(schema));
|
||||
},
|
||||
fromRaw: (...args: any[]) => {
|
||||
return coSchema.fromRaw(...args);
|
||||
},
|
||||
withMigration: (
|
||||
migration: (
|
||||
value: any,
|
||||
creationProps?: AccountCreationProps,
|
||||
) => void | Promise<void>,
|
||||
) => {
|
||||
(coSchema.prototype as Account).migrate = async function (
|
||||
this,
|
||||
creationProps,
|
||||
) {
|
||||
await migration(this, creationProps);
|
||||
};
|
||||
|
||||
return enrichedSchema;
|
||||
},
|
||||
getCoSchema: () => {
|
||||
return coSchema;
|
||||
},
|
||||
}) as unknown as AccountSchema<Shape>;
|
||||
|
||||
// Needs to be derived from the enriched schema
|
||||
const coSchema = zodSchemaToCoSchema(enrichedSchema) as any;
|
||||
|
||||
return enrichedSchema;
|
||||
}
|
||||
});
|
||||
return zodSchemaToCoSchema(enrichedSchema);
|
||||
};
|
||||
|
||||
/**
|
||||
* Defines a collaborative account schema for Jazz applications.
|
||||
@@ -183,11 +96,14 @@ export const coAccountDefiner = <Shape extends BaseAccountShape>(
|
||||
root: coMapDefiner({}),
|
||||
} as unknown as Shape,
|
||||
): AccountSchema<Shape> => {
|
||||
const objectSchema = z.object(shape).meta({
|
||||
const schema = z.object(shape).meta({
|
||||
collaborative: true,
|
||||
});
|
||||
|
||||
return enrichAccountSchema(objectSchema) as unknown as AccountSchema<Shape>;
|
||||
const enrichedSchema = Object.assign(schema, {
|
||||
collaborative: true,
|
||||
builtin: "Account",
|
||||
}) as AnyAccountSchema<Shape>;
|
||||
return zodSchemaToCoSchema(enrichedSchema);
|
||||
};
|
||||
|
||||
export const coRecordDefiner = <
|
||||
@@ -203,190 +119,86 @@ export const coRecordDefiner = <
|
||||
>;
|
||||
};
|
||||
|
||||
function enrichCoListSchema<T extends z.core.$ZodType>(schema: z.ZodArray<T>) {
|
||||
const enrichedSchema = Object.assign(schema, {
|
||||
collaborative: true,
|
||||
create: (...args: any[]) => {
|
||||
return coSchema.create(...args);
|
||||
},
|
||||
load: (...args: any[]) => {
|
||||
return coSchema.load(...args);
|
||||
},
|
||||
subscribe: (...args: any[]) => {
|
||||
return coSchema.subscribe(...args);
|
||||
},
|
||||
withHelpers: (helpers: (Self: z.core.$ZodType) => object) => {
|
||||
return Object.assign(schema, helpers(schema));
|
||||
},
|
||||
getCoSchema: () => {
|
||||
return coSchema;
|
||||
},
|
||||
}) as unknown as CoListSchema<T>;
|
||||
|
||||
// Needs to be derived from the enriched schema
|
||||
const coSchema = zodSchemaToCoSchema(enrichedSchema) as any;
|
||||
|
||||
return enrichedSchema;
|
||||
}
|
||||
|
||||
export const coListDefiner = <T extends z.core.$ZodType>(
|
||||
element: T,
|
||||
): CoListSchema<T> => {
|
||||
const arraySchema = z.array(element).meta({
|
||||
const schema = z.array(element).meta({
|
||||
collaborative: true,
|
||||
});
|
||||
|
||||
return enrichCoListSchema(arraySchema);
|
||||
const enrichedSchema = Object.assign(schema, {
|
||||
collaborative: true,
|
||||
}) as AnyCoListSchema<T>;
|
||||
return zodSchemaToCoSchema(enrichedSchema) as unknown as CoListSchema<T>;
|
||||
};
|
||||
|
||||
export const coProfileDefiner = <
|
||||
Shape extends z.core.$ZodLooseShape = Simplify<DefaultProfileShape>,
|
||||
>(
|
||||
shape: Shape & {
|
||||
name?: z.core.$ZodString<string>;
|
||||
inbox?: z.core.$ZodOptional<z.core.$ZodString>;
|
||||
inboxInvite?: z.core.$ZodOptional<z.core.$ZodString>;
|
||||
} = {} as any,
|
||||
shape: Shape & Partial<DefaultProfileShape> = {} as any,
|
||||
): CoProfileSchema<Shape> => {
|
||||
const ehnancedShape = Object.assign(shape ?? {}, {
|
||||
const ehnancedShape = Object.assign(shape, {
|
||||
name: z.string(),
|
||||
inbox: z.optional(z.string()),
|
||||
inboxInvite: z.optional(z.string()),
|
||||
});
|
||||
|
||||
return coMapDefiner(ehnancedShape) as CoProfileSchema<Shape>;
|
||||
};
|
||||
|
||||
function enrichCoFeedSchema<T extends z.core.$ZodType>(
|
||||
schema: z.ZodCustom<CoFeed<unknown>, unknown>,
|
||||
element: T,
|
||||
) {
|
||||
const enrichedSchema = Object.assign(schema, {
|
||||
collaborative: true,
|
||||
builtin: "CoFeed",
|
||||
element,
|
||||
create: (...args: any[]) => {
|
||||
return coSchema.create(...args);
|
||||
},
|
||||
load: (...args: any[]) => {
|
||||
return coSchema.load(...args);
|
||||
},
|
||||
subscribe: (...args: any[]) => {
|
||||
return coSchema.subscribe(...args);
|
||||
},
|
||||
withHelpers: (helpers: (Self: z.core.$ZodType) => object) => {
|
||||
return Object.assign(schema, helpers(schema));
|
||||
},
|
||||
getCoSchema: () => {
|
||||
return coSchema;
|
||||
},
|
||||
}) as unknown as CoFeedSchema<T>;
|
||||
|
||||
// Needs to be derived from the enriched schema
|
||||
const coSchema = zodSchemaToCoSchema(enrichedSchema) as any;
|
||||
|
||||
return enrichedSchema;
|
||||
}
|
||||
|
||||
export const coFeedDefiner = <T extends z.core.$ZodType>(
|
||||
element: T,
|
||||
): CoFeedSchema<T> => {
|
||||
return enrichCoFeedSchema(z.instanceof(CoFeed), element);
|
||||
const schema = z.instanceof(CoFeed);
|
||||
const enrichedSchema = Object.assign(schema, {
|
||||
collaborative: true,
|
||||
builtin: "CoFeed",
|
||||
element,
|
||||
}) as AnyCoFeedSchema<T>;
|
||||
return zodSchemaToCoSchema(enrichedSchema);
|
||||
};
|
||||
|
||||
function enrichFileStreamSchema(schema: z.ZodCustom<FileStream, unknown>) {
|
||||
export const coFileStreamDefiner = (): FileStreamSchema => {
|
||||
const schema = z.instanceof(FileStream);
|
||||
const enrichedSchema = Object.assign(schema, {
|
||||
collaborative: true,
|
||||
builtin: "FileStream",
|
||||
create: (...args: any[]) => {
|
||||
return coSchema.create(...args);
|
||||
},
|
||||
createFromBlob: (...args: any[]) => {
|
||||
return coSchema.createFromBlob(...args);
|
||||
},
|
||||
load: (...args: any[]) => {
|
||||
return coSchema.load(...args);
|
||||
},
|
||||
loadAsBlob: (...args: any[]) => {
|
||||
return coSchema.loadAsBlob(...args);
|
||||
},
|
||||
subscribe: (...args: any[]) => {
|
||||
return coSchema.subscribe(...args);
|
||||
},
|
||||
getCoSchema: () => {
|
||||
return coSchema;
|
||||
},
|
||||
}) as unknown as FileStreamSchema;
|
||||
|
||||
// Needs to be derived from the enriched schema
|
||||
const coSchema = zodSchemaToCoSchema(enrichedSchema) as any;
|
||||
|
||||
return enrichedSchema;
|
||||
}
|
||||
|
||||
export const coFileStreamDefiner = (): FileStreamSchema => {
|
||||
return enrichFileStreamSchema(z.instanceof(FileStream));
|
||||
}) as AnyFileStreamSchema;
|
||||
return zodSchemaToCoSchema(enrichedSchema);
|
||||
};
|
||||
|
||||
function enrichPlainTextSchema(schema: z.ZodCustom<CoPlainText, unknown>) {
|
||||
export const coPlainTextDefiner = (): PlainTextSchema => {
|
||||
const schema = z.instanceof(CoPlainText);
|
||||
const enrichedSchema = Object.assign(schema, {
|
||||
collaborative: true,
|
||||
builtin: "CoPlainText",
|
||||
create: (...args: any[]) => {
|
||||
return coSchema.create(...args);
|
||||
},
|
||||
load: (...args: any[]) => {
|
||||
return coSchema.load(...args);
|
||||
},
|
||||
subscribe: (...args: any[]) => {
|
||||
return coSchema.subscribe(...args);
|
||||
},
|
||||
fromRaw: (...args: any[]) => {
|
||||
return coSchema.fromRaw(...args);
|
||||
},
|
||||
getCoSchema: () => {
|
||||
return coSchema;
|
||||
},
|
||||
}) as unknown as PlainTextSchema;
|
||||
|
||||
// Needs to be derived from the enriched schema
|
||||
const coSchema = zodSchemaToCoSchema(enrichedSchema) as any;
|
||||
|
||||
return enrichedSchema;
|
||||
}
|
||||
|
||||
export const coPlainTextDefiner = (): PlainTextSchema => {
|
||||
return enrichPlainTextSchema(z.instanceof(CoPlainText));
|
||||
}) as AnyPlainTextSchema;
|
||||
return zodSchemaToCoSchema(enrichedSchema);
|
||||
};
|
||||
|
||||
function enrichRichTextSchema(schema: z.ZodCustom<CoRichText, unknown>) {
|
||||
export const coRichTextDefiner = (): RichTextSchema => {
|
||||
const schema = z.instanceof(CoRichText);
|
||||
const enrichedSchema = Object.assign(schema, {
|
||||
collaborative: true,
|
||||
builtin: "CoRichText",
|
||||
create: (...args: any[]) => {
|
||||
return coSchema.create(...args);
|
||||
},
|
||||
load: (...args: any[]) => {
|
||||
return coSchema.load(...args);
|
||||
},
|
||||
subscribe: (...args: any[]) => {
|
||||
return coSchema.subscribe(...args);
|
||||
},
|
||||
getCoSchema: () => {
|
||||
return coSchema;
|
||||
},
|
||||
}) as unknown as RichTextSchema;
|
||||
|
||||
// Needs to be derived from the enriched schema
|
||||
const coSchema = zodSchemaToCoSchema(enrichedSchema) as any;
|
||||
|
||||
return enrichedSchema;
|
||||
}
|
||||
|
||||
export const coRichTextDefiner = (): RichTextSchema => {
|
||||
return enrichRichTextSchema(z.instanceof(CoRichText));
|
||||
}) as AnyRichTextSchema;
|
||||
return zodSchemaToCoSchema(enrichedSchema);
|
||||
};
|
||||
|
||||
export const coImageDefiner = (): typeof ImageDefinition => {
|
||||
return ImageDefinition;
|
||||
};
|
||||
|
||||
export const coOptionalDefiner = <T extends AnyCoSchema>(
|
||||
schema: T,
|
||||
): CoOptionalSchema<T> => {
|
||||
return createCoOptionalSchema(schema);
|
||||
};
|
||||
|
||||
export const coDiscriminatedUnionDefiner = <
|
||||
T extends readonly [AnyDiscriminableCoSchema, ...AnyDiscriminableCoSchema[]],
|
||||
>(
|
||||
discriminator: string,
|
||||
schemas: T,
|
||||
): CoDiscriminatedUnionSchema<T> => {
|
||||
const schema = z.discriminatedUnion(discriminator, schemas as any);
|
||||
return zodSchemaToCoSchema(schema);
|
||||
};
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
import {
|
||||
ZodArray,
|
||||
ZodTuple,
|
||||
ZodUnion,
|
||||
core,
|
||||
array as zodArray,
|
||||
tuple as zodTuple,
|
||||
union as zodUnion,
|
||||
} from "zod/v4";
|
||||
export {
|
||||
string,
|
||||
number,
|
||||
boolean,
|
||||
union,
|
||||
object,
|
||||
array,
|
||||
templateLiteral,
|
||||
json,
|
||||
tuple,
|
||||
date,
|
||||
emoji,
|
||||
base64,
|
||||
@@ -29,8 +35,8 @@ export {
|
||||
int32,
|
||||
strictObject,
|
||||
discriminatedUnion,
|
||||
// intersection,
|
||||
// record,
|
||||
// record,
|
||||
// intersection,
|
||||
int,
|
||||
optional,
|
||||
type ZodOptional,
|
||||
@@ -39,5 +45,29 @@ export {
|
||||
type ZodDefault,
|
||||
type ZodCatch,
|
||||
type output as infer,
|
||||
type ZodDiscriminatedUnion,
|
||||
z,
|
||||
} from "zod/v4";
|
||||
|
||||
type NonCoZodType = core.$ZodType & { collaborative?: false };
|
||||
|
||||
export function union<const T extends readonly NonCoZodType[]>(
|
||||
options: T,
|
||||
params?: string | core.$ZodUnionParams,
|
||||
): ZodUnion<T> {
|
||||
return zodUnion(options, params);
|
||||
}
|
||||
|
||||
export function array<T extends NonCoZodType>(
|
||||
element: T,
|
||||
params?: string | core.$ZodArrayParams,
|
||||
): ZodArray<T> {
|
||||
return zodArray(element, params);
|
||||
}
|
||||
|
||||
export function tuple<T extends readonly [NonCoZodType, ...NonCoZodType[]]>(
|
||||
options: T,
|
||||
params?: string | core.$ZodTupleParams,
|
||||
): ZodTuple<T> {
|
||||
return zodTuple(options, params);
|
||||
}
|
||||
|
||||
@@ -2,23 +2,47 @@ import { LocalNode, RawAccount } from "cojson";
|
||||
import {
|
||||
Account,
|
||||
AccountClass,
|
||||
CoValue,
|
||||
CoValueClass,
|
||||
CoValueFromRaw,
|
||||
InstanceOfSchema,
|
||||
RefsToResolve,
|
||||
RefsToResolveStrict,
|
||||
Resolved,
|
||||
Simplify,
|
||||
} from "../../internal.js";
|
||||
import { AnyAccountSchema } from "./schemaTypes/AccountSchema.js";
|
||||
import { AnyCoFeedSchema } from "./schemaTypes/CoFeedSchema.js";
|
||||
import { AnyCoListSchema } from "./schemaTypes/CoListSchema.js";
|
||||
import { AnyCoMapSchema, CoMapInitZod } from "./schemaTypes/CoMapSchema.js";
|
||||
import { AnyCoRecordSchema } from "./schemaTypes/CoRecordSchema.js";
|
||||
import { FileStreamSchema } from "./schemaTypes/FileStreamSchema.js";
|
||||
import { PlainTextSchema } from "./schemaTypes/PlainTextSchema.js";
|
||||
import { RichTextSchema } from "./schemaTypes/RichTextSchema.js";
|
||||
import { InstanceOfSchema } from "./typeConverters/InstanceOfSchema.js";
|
||||
import {
|
||||
AccountSchema,
|
||||
AnyAccountSchema,
|
||||
BaseAccountShape,
|
||||
} from "./schemaTypes/AccountSchema.js";
|
||||
import {
|
||||
AnyDiscriminableCoSchema,
|
||||
CoDiscriminatedUnionSchema,
|
||||
} from "./schemaTypes/CoDiscriminatedUnionSchema.js";
|
||||
import { AnyCoFeedSchema, CoFeedSchema } from "./schemaTypes/CoFeedSchema.js";
|
||||
import { AnyCoListSchema, CoListSchema } from "./schemaTypes/CoListSchema.js";
|
||||
import {
|
||||
AnyCoMapSchema,
|
||||
CoMapInitZod,
|
||||
CoMapSchema,
|
||||
} from "./schemaTypes/CoMapSchema.js";
|
||||
import { AnyCoOptionalSchema } from "./schemaTypes/CoOptionalSchema.js";
|
||||
import {
|
||||
AnyCoRecordSchema,
|
||||
CoRecordSchema,
|
||||
} from "./schemaTypes/CoRecordSchema.js";
|
||||
import {
|
||||
AnyFileStreamSchema,
|
||||
FileStreamSchema,
|
||||
} from "./schemaTypes/FileStreamSchema.js";
|
||||
import {
|
||||
AnyPlainTextSchema,
|
||||
PlainTextSchema,
|
||||
} from "./schemaTypes/PlainTextSchema.js";
|
||||
import {
|
||||
AnyRichTextSchema,
|
||||
RichTextSchema,
|
||||
} from "./schemaTypes/RichTextSchema.js";
|
||||
import { InstanceOfSchemaCoValuesNullable } from "./typeConverters/InstanceOfSchemaCoValuesNullable.js";
|
||||
import { z } from "./zodReExport.js";
|
||||
|
||||
@@ -47,7 +71,7 @@ export type AnyCoUnionSchema = z.core.$ZodDiscriminatedUnion<
|
||||
// this is a series of hacks to work around z4 removing _zod at runtime from z.core.$ZodType
|
||||
export function isZodObject(
|
||||
schema: z.core.$ZodType,
|
||||
): schema is z.core.$ZodObject<any, any> {
|
||||
): schema is z.ZodObject<any, any> {
|
||||
return (schema as any).def?.type === "object";
|
||||
}
|
||||
|
||||
@@ -67,19 +91,53 @@ export function getDef<S extends z.core.$ZodType>(schema: S): S["_zod"]["def"] {
|
||||
return (schema as any).def;
|
||||
}
|
||||
|
||||
// TODO rename. This represents a CoValue class or a CoValue schema
|
||||
export type CoValueOrZodSchema = CoValueClass | AnyCoSchema;
|
||||
|
||||
export type CoValueClassFromZodSchema<S extends z.core.$ZodType> = CoValueClass<
|
||||
InstanceOfSchema<S>
|
||||
> &
|
||||
CoValueFromRaw<InstanceOfSchema<S>> &
|
||||
(S extends AnyAccountSchema ? AccountClassEssentials : {});
|
||||
// TODO rename to CoValueSchemaFromCoProtoSchema
|
||||
export type CoValueSchemaFromZodSchema<S extends z.core.$ZodType> =
|
||||
S extends z.core.$ZodType
|
||||
? S extends AnyAccountSchema<infer Shape extends BaseAccountShape>
|
||||
? AccountSchema<Shape>
|
||||
: S extends AnyCoRecordSchema<infer K, infer V>
|
||||
? CoRecordSchema<K, V>
|
||||
: S extends AnyCoMapSchema<infer Shape, infer Config>
|
||||
? CoMapSchema<Shape, Config>
|
||||
: S extends AnyCoListSchema<infer T>
|
||||
? CoListSchema<T>
|
||||
: S extends AnyCoFeedSchema<infer T>
|
||||
? CoFeedSchema<T>
|
||||
: S extends AnyPlainTextSchema
|
||||
? PlainTextSchema
|
||||
: S extends AnyRichTextSchema
|
||||
? RichTextSchema
|
||||
: S extends AnyFileStreamSchema
|
||||
? FileStreamSchema
|
||||
: S extends z.core.$ZodOptional<infer Inner>
|
||||
? CoValueSchemaFromZodSchema<Inner>
|
||||
: S extends z.core.$ZodUnion<
|
||||
infer Members extends readonly [
|
||||
AnyDiscriminableCoSchema,
|
||||
...AnyDiscriminableCoSchema[],
|
||||
]
|
||||
>
|
||||
? CoDiscriminatedUnionSchema<Members>
|
||||
: never
|
||||
: never;
|
||||
|
||||
export type CoValueClassFromAnySchema<S extends CoValueOrZodSchema> =
|
||||
S extends CoValueClass<any>
|
||||
? S
|
||||
: CoValueClass<InstanceOfSchema<S>> &
|
||||
CoValueFromRaw<InstanceOfSchema<S>> &
|
||||
(S extends AnyAccountSchema ? AccountClassEssentials : {});
|
||||
|
||||
type AccountClassEssentials = {
|
||||
fromRaw: <A extends Account>(this: AccountClass<A>, raw: RawAccount) => A;
|
||||
fromNode: <A extends Account>(this: AccountClass<A>, node: LocalNode) => A;
|
||||
};
|
||||
|
||||
// TODO rename to ProtoCoSchema?
|
||||
export type AnyCoSchema =
|
||||
| AnyCoMapSchema
|
||||
| AnyAccountSchema
|
||||
@@ -87,9 +145,10 @@ export type AnyCoSchema =
|
||||
| AnyCoListSchema
|
||||
| AnyCoFeedSchema
|
||||
| AnyCoUnionSchema
|
||||
| PlainTextSchema
|
||||
| RichTextSchema
|
||||
| FileStreamSchema;
|
||||
| AnyCoOptionalSchema
|
||||
| AnyPlainTextSchema
|
||||
| AnyRichTextSchema
|
||||
| AnyFileStreamSchema;
|
||||
|
||||
export type Loaded<
|
||||
T extends CoValueClass | AnyCoSchema,
|
||||
|
||||
@@ -33,6 +33,7 @@ export * from "./implementation/zodSchema/zodSchema.js";
|
||||
export * from "./implementation/zodSchema/zodCo.js";
|
||||
export * as co from "./implementation/zodSchema/coExport.js";
|
||||
export * from "./implementation/zodSchema/schemaTypes/CoMapSchema.js";
|
||||
export * from "./implementation/zodSchema/schemaTypes/CoDiscriminatedUnionSchema.js";
|
||||
export * from "./implementation/zodSchema/schemaTypes/CoRecordSchema.js";
|
||||
export * from "./implementation/zodSchema/schemaTypes/CoListSchema.js";
|
||||
export * from "./implementation/zodSchema/schemaTypes/CoFeedSchema.js";
|
||||
|
||||
@@ -28,6 +28,10 @@ export class CoValueCoreSubscription {
|
||||
this.subscribeToState();
|
||||
this.listener("unavailable");
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Unexpected error loading CoValue: ", error);
|
||||
this.listener("unavailable");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,8 +126,7 @@ export class SubscriptionScope<D extends CoValue> {
|
||||
new JazzError(this.id, "unauthorized", [
|
||||
{
|
||||
code: "unauthorized",
|
||||
message:
|
||||
"The current user is not authorized to access this value",
|
||||
message: `The current user (${this.node.getCurrentAgent().id}) is not authorized to access this value`,
|
||||
params: {
|
||||
id: this.id,
|
||||
},
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import { StorageAPI } from "cojson";
|
||||
import { WasmCrypto } from "cojson/crypto/WasmCrypto";
|
||||
import { beforeEach, describe, expect, test, vi } from "vitest";
|
||||
import {
|
||||
Account,
|
||||
AccountClass,
|
||||
AuthSecretStorage,
|
||||
CoMap,
|
||||
Group,
|
||||
InMemoryKVStore,
|
||||
JazzAuthContext,
|
||||
KvStoreContext,
|
||||
co,
|
||||
coField,
|
||||
z,
|
||||
} from "../exports";
|
||||
import {
|
||||
@@ -26,6 +25,7 @@ import {
|
||||
CoValueFromRaw,
|
||||
InstanceOfSchema,
|
||||
Loaded,
|
||||
anySchemaToCoSchema,
|
||||
zodSchemaToCoSchema,
|
||||
} from "../internal";
|
||||
import {
|
||||
@@ -33,6 +33,7 @@ import {
|
||||
getPeerConnectedToTestSyncServer,
|
||||
setupJazzTestSync,
|
||||
} from "../testing";
|
||||
import { createAsyncStorage, getDbPath } from "./testStorage";
|
||||
|
||||
const Crypto = await WasmCrypto.create();
|
||||
|
||||
@@ -41,12 +42,14 @@ class TestJazzContextManager<Acc extends Account> extends JazzContextManager<
|
||||
JazzContextManagerBaseProps<Acc> & {
|
||||
defaultProfileName?: string;
|
||||
AccountSchema?: AccountClass<Acc>;
|
||||
storage?: string;
|
||||
}
|
||||
> {
|
||||
async getNewContext(
|
||||
props: JazzContextManagerBaseProps<Acc> & {
|
||||
defaultProfileName?: string;
|
||||
AccountSchema?: AccountClass<Acc> & CoValueFromRaw<Acc>;
|
||||
storage?: string;
|
||||
},
|
||||
authProps?: JazzContextManagerAuthProps,
|
||||
) {
|
||||
@@ -59,6 +62,7 @@ class TestJazzContextManager<Acc extends Account> extends JazzContextManager<
|
||||
sessionProvider: randomSessionProvider,
|
||||
authSecretStorage: this.getAuthSecretStorage(),
|
||||
AccountSchema: props.AccountSchema,
|
||||
storage: await createAsyncStorage({ filename: props.storage }),
|
||||
});
|
||||
|
||||
return {
|
||||
@@ -77,12 +81,14 @@ class TestJazzContextManager<Acc extends Account> extends JazzContextManager<
|
||||
describe("ContextManager", () => {
|
||||
let manager: TestJazzContextManager<Account>;
|
||||
let authSecretStorage: AuthSecretStorage;
|
||||
let storage: StorageAPI;
|
||||
|
||||
function getCurrentValue() {
|
||||
return manager.getCurrentValue() as JazzAuthContext<Account>;
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
storage = await createAsyncStorage({});
|
||||
KvStoreContext.getInstance().initialize(new InMemoryKVStore());
|
||||
authSecretStorage = new AuthSecretStorage();
|
||||
await authSecretStorage.clear();
|
||||
@@ -233,6 +239,78 @@ describe("ContextManager", () => {
|
||||
expect(onAnonymousAccountDiscarded).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("onAnonymousAccountDiscarded should not block the authentication when storage is active", async () => {
|
||||
const dbFilename = getDbPath();
|
||||
|
||||
const AccountRoot = co.map({
|
||||
value: z.string(),
|
||||
get transferredRoot(): z.ZodOptional<typeof AccountRoot> {
|
||||
return co.optional(AccountRoot);
|
||||
},
|
||||
});
|
||||
|
||||
let lastRootId: string | undefined;
|
||||
|
||||
const CustomAccount = co
|
||||
.account({
|
||||
root: AccountRoot,
|
||||
profile: co.profile(),
|
||||
})
|
||||
.withMigration(async (account) => {
|
||||
account.root = AccountRoot.create(
|
||||
{
|
||||
value: "Hello",
|
||||
},
|
||||
Group.create(this).makePublic(),
|
||||
);
|
||||
});
|
||||
|
||||
const customManager = new TestJazzContextManager<
|
||||
InstanceOfSchema<typeof CustomAccount>
|
||||
>();
|
||||
|
||||
await customManager.createContext({
|
||||
AccountSchema: anySchemaToCoSchema(CustomAccount),
|
||||
storage: dbFilename,
|
||||
onAnonymousAccountDiscarded: async (anonymousAccount) => {
|
||||
const anonymousAccountWithRoot = await anonymousAccount.ensureLoaded({
|
||||
resolve: { root: true },
|
||||
});
|
||||
|
||||
const me = await CustomAccount.getMe().ensureLoaded({
|
||||
resolve: { root: true },
|
||||
});
|
||||
|
||||
me.root.transferredRoot = anonymousAccountWithRoot.root;
|
||||
},
|
||||
});
|
||||
|
||||
const prevContextNode = customManager.getCurrentValue()!.node;
|
||||
|
||||
expect(prevContextNode.storage).toBeDefined();
|
||||
|
||||
const account = (
|
||||
customManager.getCurrentValue() as JazzAuthContext<
|
||||
InstanceOfSchema<typeof CustomAccount>
|
||||
>
|
||||
).me;
|
||||
|
||||
await customManager.authenticate({
|
||||
accountID: account.id,
|
||||
accountSecret: account._raw.core.node.getCurrentAgent().agentSecret,
|
||||
provider: "test",
|
||||
});
|
||||
|
||||
// The storage should be closed and set to undefined
|
||||
expect(prevContextNode.storage).toBeUndefined();
|
||||
|
||||
const me = await CustomAccount.getMe().ensureLoaded({
|
||||
resolve: { root: { transferredRoot: true } },
|
||||
});
|
||||
|
||||
expect(me.root.transferredRoot?.value).toBe("Hello");
|
||||
});
|
||||
|
||||
test("the migration should be applied correctly on existing accounts", async () => {
|
||||
const AccountRoot = co.map({
|
||||
value: z.string(),
|
||||
@@ -258,7 +336,7 @@ describe("ContextManager", () => {
|
||||
|
||||
// Create initial anonymous context
|
||||
await customManager.createContext({
|
||||
AccountSchema: zodSchemaToCoSchema(CustomAccount),
|
||||
AccountSchema: anySchemaToCoSchema(CustomAccount),
|
||||
});
|
||||
|
||||
const account = (
|
||||
@@ -267,8 +345,6 @@ describe("ContextManager", () => {
|
||||
>
|
||||
).me;
|
||||
|
||||
console.log("before", account._refs.root?.id);
|
||||
|
||||
await customManager.authenticate({
|
||||
accountID: account.id,
|
||||
accountSecret: account._raw.core.node.getCurrentAgent().agentSecret,
|
||||
@@ -279,8 +355,6 @@ describe("ContextManager", () => {
|
||||
resolve: { root: true },
|
||||
});
|
||||
|
||||
console.log("after", me._refs.root?.id);
|
||||
|
||||
expect(me.root.id).toBe(lastRootId);
|
||||
});
|
||||
|
||||
@@ -313,7 +387,7 @@ describe("ContextManager", () => {
|
||||
|
||||
// Create initial anonymous context
|
||||
await customManager.createContext({
|
||||
AccountSchema: zodSchemaToCoSchema(CustomAccount),
|
||||
AccountSchema: anySchemaToCoSchema(CustomAccount),
|
||||
});
|
||||
|
||||
const account = (
|
||||
@@ -339,7 +413,7 @@ describe("ContextManager", () => {
|
||||
const AccountRoot = co.map({
|
||||
value: z.string(),
|
||||
get transferredRoot(): z.ZodOptional<typeof AccountRoot> {
|
||||
return z.optional(AccountRoot);
|
||||
return co.optional(AccountRoot);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -385,7 +459,7 @@ describe("ContextManager", () => {
|
||||
// Create initial anonymous context
|
||||
await customManager.createContext({
|
||||
onAnonymousAccountDiscarded,
|
||||
AccountSchema: zodSchemaToCoSchema(CustomAccount),
|
||||
AccountSchema: anySchemaToCoSchema(CustomAccount),
|
||||
});
|
||||
|
||||
const account = await createJazzTestAccount({
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
import { beforeEach, describe, test } from "vitest";
|
||||
import { Loaded, co, z } from "../exports.js";
|
||||
import { createJazzTestAccount, setupJazzTestSync } from "../testing.js";
|
||||
|
||||
describe("co.discriminatedUnion", () => {
|
||||
beforeEach(async () => {
|
||||
await setupJazzTestSync();
|
||||
|
||||
await createJazzTestAccount({
|
||||
isCurrentActiveAccount: true,
|
||||
creationProps: { name: "Hermes Puggington" },
|
||||
});
|
||||
});
|
||||
|
||||
test("can use co.discriminatedUnion with CoValue schemas as values", () => {
|
||||
const Dog = co.map({
|
||||
type: z.literal("dog"),
|
||||
});
|
||||
const Cat = co.map({
|
||||
type: z.literal("cat"),
|
||||
});
|
||||
const Person = co.map({
|
||||
pet: co.discriminatedUnion("type", [Dog, Cat]),
|
||||
});
|
||||
|
||||
const person = Person.create({
|
||||
pet: Dog.create({
|
||||
type: "dog",
|
||||
}),
|
||||
});
|
||||
|
||||
person.pet = Cat.create({
|
||||
type: "cat",
|
||||
});
|
||||
|
||||
type ExpectedType = {
|
||||
pet: Loaded<typeof Dog> | Loaded<typeof Cat>;
|
||||
};
|
||||
|
||||
function matches(value: ExpectedType) {
|
||||
return value;
|
||||
}
|
||||
|
||||
matches(person);
|
||||
});
|
||||
|
||||
test("cannot use co.discriminatedUnion with zod schemas as values", () => {
|
||||
const Person = co.map({
|
||||
pet: co.discriminatedUnion("type", [
|
||||
// @ts-expect-error: cannot use co.discriminatedUnion with a Zod schema
|
||||
z.object({
|
||||
type: z.string(),
|
||||
}),
|
||||
]),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,93 @@
|
||||
import { beforeEach, describe, expect, test, vi } from "vitest";
|
||||
import { Loaded, co, z } from "../exports.js";
|
||||
import { createJazzTestAccount, setupJazzTestSync } from "../testing.js";
|
||||
import { waitFor } from "./utils.js";
|
||||
|
||||
describe("co.discriminatedUnion", () => {
|
||||
beforeEach(async () => {
|
||||
await setupJazzTestSync();
|
||||
|
||||
await createJazzTestAccount({
|
||||
isCurrentActiveAccount: true,
|
||||
creationProps: { name: "Hermes Puggington" },
|
||||
});
|
||||
});
|
||||
|
||||
test("use co.discriminatedUnion with CoValue schemas as values", () => {
|
||||
const Dog = co.map({
|
||||
type: z.literal("dog"),
|
||||
});
|
||||
const Cat = co.map({
|
||||
type: z.literal("cat"),
|
||||
});
|
||||
const Person = co.map({
|
||||
pet: co.discriminatedUnion("type", [Dog, Cat]),
|
||||
});
|
||||
|
||||
const person = Person.create({
|
||||
pet: Dog.create({
|
||||
type: "dog",
|
||||
}),
|
||||
});
|
||||
|
||||
expect(person.pet.type).toEqual("dog");
|
||||
|
||||
person.pet = Cat.create({
|
||||
type: "cat",
|
||||
});
|
||||
|
||||
expect(person.pet.type).toEqual("cat");
|
||||
});
|
||||
|
||||
test("load CoValue instances using the DiscriminatedUnion schema", async () => {
|
||||
const Dog = co.map({
|
||||
type: z.literal("dog"),
|
||||
});
|
||||
const Cat = co.map({
|
||||
type: z.literal("cat"),
|
||||
});
|
||||
const Pet = co.discriminatedUnion("type", [Dog, Cat]);
|
||||
|
||||
const dog = Dog.create({ type: "dog" });
|
||||
const loadedPet = await Pet.load(dog.id);
|
||||
expect(loadedPet?.type).toEqual("dog");
|
||||
});
|
||||
|
||||
test("subscribe to CoValue instances using the DiscriminatedUnion schema", async () => {
|
||||
const Person = co.map({
|
||||
name: z.string(),
|
||||
});
|
||||
const Dog = co.map({
|
||||
type: z.literal("dog"),
|
||||
name: z.string(),
|
||||
owner: Person,
|
||||
});
|
||||
const Cat = co.map({
|
||||
type: z.literal("cat"),
|
||||
name: z.string(),
|
||||
});
|
||||
const Pet = co.discriminatedUnion("type", [Dog, Cat]);
|
||||
|
||||
const dog = Dog.create({
|
||||
type: "dog",
|
||||
name: "Rex",
|
||||
owner: Person.create({ name: "John Doe" }),
|
||||
});
|
||||
|
||||
const updates: Loaded<typeof Pet>[] = [];
|
||||
const spy = vi.fn((pet) => updates.push(pet));
|
||||
|
||||
Pet.subscribe(dog.id, {}, (pet) => {
|
||||
expect(pet.type).toEqual("dog");
|
||||
spy(pet);
|
||||
});
|
||||
|
||||
expect(spy).not.toHaveBeenCalled();
|
||||
|
||||
await waitFor(() => expect(spy).toHaveBeenCalled());
|
||||
|
||||
expect(spy).toHaveBeenCalledTimes(1);
|
||||
|
||||
expect(updates[0]?.name).toEqual("Rex");
|
||||
});
|
||||
});
|
||||
@@ -1,15 +1,7 @@
|
||||
import { WasmCrypto } from "cojson/crypto/WasmCrypto";
|
||||
import { beforeEach, describe, expect, test, vi } from "vitest";
|
||||
import {
|
||||
Account,
|
||||
CoList,
|
||||
CoMap,
|
||||
Group,
|
||||
coField,
|
||||
subscribeToCoValue,
|
||||
z,
|
||||
} from "../index.js";
|
||||
import { Loaded, co, zodSchemaToCoSchema } from "../internal.js";
|
||||
import { Account, Group, subscribeToCoValue, z } from "../index.js";
|
||||
import { Loaded, anySchemaToCoSchema, co } from "../internal.js";
|
||||
import { createJazzTestAccount, setupJazzTestSync } from "../testing.js";
|
||||
import { waitFor } from "./utils.js";
|
||||
|
||||
@@ -127,7 +119,7 @@ describe("Simple CoList operations", async () => {
|
||||
name: z.string(),
|
||||
});
|
||||
|
||||
const Recipe = co.list(z.optional(Ingredient));
|
||||
const Recipe = co.list(co.optional(Ingredient));
|
||||
|
||||
const recipe = Recipe.create(
|
||||
[
|
||||
@@ -588,7 +580,7 @@ describe("CoList subscription", async () => {
|
||||
const spy = vi.fn((list) => updates.push(list));
|
||||
|
||||
subscribeToCoValue(
|
||||
zodSchemaToCoSchema(TestList),
|
||||
anySchemaToCoSchema(TestList),
|
||||
list.id,
|
||||
{
|
||||
syncResolution: true,
|
||||
|
||||
@@ -281,7 +281,7 @@ describe("CoMap.Record", async () => {
|
||||
});
|
||||
|
||||
test("Is ok to omit optional fields", () => {
|
||||
const TestRecord = co.record(z.string(), z.optional(NestedRecord));
|
||||
const TestRecord = co.record(z.string(), co.optional(NestedRecord));
|
||||
|
||||
expectTypeOf<typeof TestRecord.create>().toBeCallableWith(
|
||||
{
|
||||
@@ -315,7 +315,7 @@ describe("CoMap.Record", async () => {
|
||||
|
||||
const PersonRecord = co.record(
|
||||
z.string(),
|
||||
z.discriminatedUnion("type", [Base, IssueRepro]),
|
||||
co.discriminatedUnion("type", [Base, IssueRepro]),
|
||||
);
|
||||
|
||||
const person = IssueRepro.create({
|
||||
@@ -350,7 +350,7 @@ describe("CoMap.Record", async () => {
|
||||
|
||||
const PersonRecord = co.record(
|
||||
z.string(),
|
||||
z.discriminatedUnion("type", [Base, IssueRepro]),
|
||||
co.discriminatedUnion("type", [Base, IssueRepro]),
|
||||
);
|
||||
|
||||
const person = IssueRepro.create({
|
||||
@@ -385,7 +385,7 @@ describe("CoMap.Record", async () => {
|
||||
|
||||
const PersonRecord = co.record(
|
||||
z.string(),
|
||||
z.discriminatedUnion("type", [Base, IssueRepro]),
|
||||
co.discriminatedUnion("type", [Base, IssueRepro]),
|
||||
);
|
||||
|
||||
const person = IssueRepro.create({
|
||||
|
||||
@@ -159,7 +159,7 @@ describe("CoMap", async () => {
|
||||
test("Comap with recursive optional reference", () => {
|
||||
const Recursive = co.map({
|
||||
get child(): z.ZodOptional<typeof Recursive> {
|
||||
return z.optional(Recursive);
|
||||
return co.optional(Recursive);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -185,7 +185,7 @@ describe("CoMap", async () => {
|
||||
age: z.number(),
|
||||
// TODO: would be nice if this didn't need a type annotation
|
||||
get friend(): z.ZodOptional<typeof Person> {
|
||||
return z.optional(Person);
|
||||
return co.optional(Person);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -280,7 +280,7 @@ describe("CoMap", async () => {
|
||||
|
||||
const MapWithEnumOfMaps = co.map({
|
||||
name: z.string(),
|
||||
child: z.discriminatedUnion("type", [ChildA, ChildB]),
|
||||
child: co.discriminatedUnion("type", [ChildA, ChildB]),
|
||||
});
|
||||
|
||||
const mapWithEnum = MapWithEnumOfMaps.create({
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
} from "vitest";
|
||||
import { Group, co, subscribeToCoValue, z } from "../exports.js";
|
||||
import { Account } from "../index.js";
|
||||
import { ID, Loaded, zodSchemaToCoSchema } from "../internal.js";
|
||||
import { Loaded, anySchemaToCoSchema } from "../internal.js";
|
||||
import {
|
||||
createJazzTestAccount,
|
||||
getPeerConnectedToTestSyncServer,
|
||||
@@ -145,7 +145,7 @@ describe("CoMap", async () => {
|
||||
age: z.number(),
|
||||
// TODO: would be nice if this didn't need a type annotation
|
||||
get friend(): z.ZodOptional<typeof Person> {
|
||||
return z.optional(Person);
|
||||
return co.optional(Person);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -182,7 +182,7 @@ describe("CoMap", async () => {
|
||||
name: z.string(),
|
||||
age: z.number(),
|
||||
get friend(): z.ZodOptional<typeof Person> {
|
||||
return z.optional(Person);
|
||||
return co.optional(Person);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -211,7 +211,7 @@ describe("CoMap", async () => {
|
||||
name: z.string(),
|
||||
age: z.number(),
|
||||
get friend(): z.ZodOptional<typeof Person> {
|
||||
return z.optional(Person);
|
||||
return co.optional(Person);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -400,7 +400,7 @@ describe("CoMap", async () => {
|
||||
|
||||
const MapWithEnumOfMaps = co.map({
|
||||
name: z.string(),
|
||||
child: z.discriminatedUnion("type", [ChildA, ChildB]),
|
||||
child: co.discriminatedUnion("type", [ChildA, ChildB]),
|
||||
});
|
||||
|
||||
const mapWithEnum = MapWithEnumOfMaps.create({
|
||||
@@ -801,7 +801,7 @@ describe("CoMap resolution", async () => {
|
||||
const spy = vi.fn((person) => updates.push(person));
|
||||
|
||||
subscribeToCoValue(
|
||||
zodSchemaToCoSchema(Person), // TODO: we should get rid of the conversion in the future
|
||||
anySchemaToCoSchema(Person), // TODO: we should get rid of the conversion in the future
|
||||
person.id,
|
||||
{
|
||||
syncResolution: true,
|
||||
@@ -1003,7 +1003,7 @@ describe("CoMap applyDiff", async () => {
|
||||
birthday: z.date(),
|
||||
nested: NestedMap,
|
||||
optionalField: z.string().optional(),
|
||||
optionalNested: z.optional(NestedMap),
|
||||
optionalNested: co.optional(NestedMap),
|
||||
});
|
||||
|
||||
test("Basic applyDiff", () => {
|
||||
@@ -1698,7 +1698,7 @@ describe("Creating and finding unique CoMaps", async () => {
|
||||
dateValue: z.date(),
|
||||
});
|
||||
|
||||
const AttributeValue = z.discriminatedUnion("type", [
|
||||
const AttributeValue = co.discriminatedUnion("type", [
|
||||
StringAttributeValue,
|
||||
NumberAttributeValue,
|
||||
DateAttributeValue,
|
||||
@@ -1714,7 +1714,7 @@ describe("Creating and finding unique CoMaps", async () => {
|
||||
attributeValue: AttributeValue,
|
||||
});
|
||||
|
||||
const Tag = z.discriminatedUnion("type", [
|
||||
const Tag = co.discriminatedUnion("type", [
|
||||
AttributeTag,
|
||||
StringTag,
|
||||
DateTag,
|
||||
@@ -1764,7 +1764,7 @@ describe("Creating and finding unique CoMaps", async () => {
|
||||
error: HttpError,
|
||||
});
|
||||
|
||||
const ErrorResponse = z.discriminatedUnion("type", [
|
||||
const ErrorResponse = co.discriminatedUnion("type", [
|
||||
ClientError,
|
||||
ServerError,
|
||||
NetworkError,
|
||||
|
||||
39
packages/jazz-tools/src/tools/tests/coOptional.test.ts
Normal file
39
packages/jazz-tools/src/tools/tests/coOptional.test.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { beforeEach, describe, test } from "vitest";
|
||||
import { CoPlainText, co, z } from "../exports.js";
|
||||
import { createJazzTestAccount, setupJazzTestSync } from "../testing.js";
|
||||
|
||||
describe("co.optional", () => {
|
||||
beforeEach(async () => {
|
||||
await setupJazzTestSync();
|
||||
|
||||
await createJazzTestAccount({
|
||||
isCurrentActiveAccount: true,
|
||||
creationProps: { name: "Hermes Puggington" },
|
||||
});
|
||||
});
|
||||
|
||||
test("can use co.optional with CoValue schemas as values", () => {
|
||||
const Person = co.map({
|
||||
preferredName: co.optional(co.plainText()),
|
||||
});
|
||||
|
||||
const person = Person.create({});
|
||||
|
||||
type ExpectedType = {
|
||||
preferredName?: CoPlainText;
|
||||
};
|
||||
|
||||
function matches(value: ExpectedType) {
|
||||
return value;
|
||||
}
|
||||
|
||||
matches(person);
|
||||
});
|
||||
|
||||
test("cannot use co.optional with zod schemas as values", () => {
|
||||
const Person = co.map({
|
||||
// @ts-expect-error: cannot use co.optional with a Zod schema
|
||||
preferredName: co.optional(z.string()),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AgentSecret, RawCoMap } from "cojson";
|
||||
import { AgentSecret } from "cojson";
|
||||
import { WasmCrypto } from "cojson/crypto/WasmCrypto";
|
||||
import { beforeEach, describe, expect, test, vi } from "vitest";
|
||||
import {
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
ID,
|
||||
InMemoryKVStore,
|
||||
KvStoreContext,
|
||||
anySchemaToCoSchema,
|
||||
co,
|
||||
createAnonymousJazzContext,
|
||||
createJazzContext,
|
||||
@@ -90,7 +91,7 @@ describe("createContext methods", () => {
|
||||
});
|
||||
|
||||
expect(context.account).toBeInstanceOf(
|
||||
zodSchemaToCoSchema(CustomAccount),
|
||||
anySchemaToCoSchema(CustomAccount),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -199,7 +200,7 @@ describe("createContext methods", () => {
|
||||
});
|
||||
|
||||
expect(context.account).toBeInstanceOf(
|
||||
zodSchemaToCoSchema(CustomAccount),
|
||||
anySchemaToCoSchema(CustomAccount),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -350,7 +351,7 @@ describe("createContext methods", () => {
|
||||
});
|
||||
|
||||
expect(context.account).toBeInstanceOf(
|
||||
zodSchemaToCoSchema(CustomAccount),
|
||||
anySchemaToCoSchema(CustomAccount),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -36,7 +36,7 @@ const TestList = co.list(InnerMap);
|
||||
|
||||
const TestMap = co.map({
|
||||
list: TestList,
|
||||
optionalRef: z.optional(InnermostMap),
|
||||
optionalRef: co.optional(InnermostMap),
|
||||
});
|
||||
|
||||
describe("Deep loading with depth arg", async () => {
|
||||
@@ -409,7 +409,7 @@ describe("Deep loading with unauthorized account", async () => {
|
||||
expect(mapOnAlice).toBe(null);
|
||||
|
||||
expect(errorSpy).toHaveBeenCalledWith(
|
||||
`The current user is not authorized to access this value from ${map.id}`,
|
||||
`The current user (${alice.id}) is not authorized to access this value from ${map.id}`,
|
||||
);
|
||||
|
||||
errorSpy.mockReset();
|
||||
@@ -431,7 +431,7 @@ describe("Deep loading with unauthorized account", async () => {
|
||||
expect(mapWithListOnAlice).toBe(null);
|
||||
|
||||
expect(errorSpy).toHaveBeenCalledWith(
|
||||
`The current user is not authorized to access this value from ${map.id} on path list`,
|
||||
`The current user (${alice.id}) is not authorized to access this value from ${map.id} on path list`,
|
||||
);
|
||||
|
||||
errorSpy.mockReset();
|
||||
@@ -465,7 +465,7 @@ describe("Deep loading with unauthorized account", async () => {
|
||||
expect(mapOnAlice).toBe(null);
|
||||
|
||||
expect(errorSpy).toHaveBeenCalledWith(
|
||||
`The current user is not authorized to access this value from ${map.id} on path list.0`,
|
||||
`The current user (${alice.id}) is not authorized to access this value from ${map.id} on path list.0`,
|
||||
);
|
||||
|
||||
errorSpy.mockReset();
|
||||
@@ -492,7 +492,7 @@ describe("Deep loading with unauthorized account", async () => {
|
||||
expect(mapOnAlice?.optionalRef?.value).toBe(undefined);
|
||||
|
||||
expect(errorSpy).toHaveBeenCalledWith(
|
||||
`The current user is not authorized to access this value from ${map.id} on path optionalRef`,
|
||||
`The current user (${alice.id}) is not authorized to access this value from ${map.id} on path optionalRef`,
|
||||
);
|
||||
|
||||
errorSpy.mockReset();
|
||||
@@ -551,7 +551,7 @@ describe("Deep loading with unauthorized account", async () => {
|
||||
expect(mapOnAlice).toBe(null);
|
||||
|
||||
expect(errorSpy).toHaveBeenCalledWith(
|
||||
`The current user is not authorized to access this value from ${map.id} on path list.0.stream`,
|
||||
`The current user (${alice.id}) is not authorized to access this value from ${map.id} on path list.0.stream`,
|
||||
);
|
||||
|
||||
errorSpy.mockReset();
|
||||
@@ -587,7 +587,7 @@ describe("Deep loading with unauthorized account", async () => {
|
||||
expect(mapOnAlice).toBe(null);
|
||||
|
||||
expect(errorSpy).toHaveBeenCalledWith(
|
||||
`The current user is not authorized to access this value from ${map.id} on path list.0.stream.${value.id}`,
|
||||
`The current user (${alice.id}) is not authorized to access this value from ${map.id} on path list.0.stream.${value.id}`,
|
||||
);
|
||||
|
||||
errorSpy.mockReset();
|
||||
@@ -599,7 +599,7 @@ describe("Deep loading with unauthorized account", async () => {
|
||||
});
|
||||
|
||||
const Lv2 = co.map({
|
||||
lv3: z.optional(Lv3),
|
||||
lv3: co.optional(Lv3),
|
||||
});
|
||||
|
||||
const Lv1 = co.map({
|
||||
@@ -737,7 +737,7 @@ describe("Deep loading with unauthorized account", async () => {
|
||||
const Person = co.map({
|
||||
name: z.string(),
|
||||
get friends(): z.ZodOptional<typeof Friends> {
|
||||
return z.optional(Friends);
|
||||
return co.optional(Friends);
|
||||
},
|
||||
});
|
||||
const Friends: CoListSchema<typeof Person> = co.list(Person); // TODO: annoying that we have to annotate
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
Account,
|
||||
CoMap,
|
||||
Group,
|
||||
Inbox,
|
||||
InboxSender,
|
||||
Profile,
|
||||
z,
|
||||
} from "../exports";
|
||||
import { Loaded, co, coField, zodSchemaToCoSchema } from "../internal";
|
||||
import { Group, Inbox, InboxSender, z } from "../exports";
|
||||
import { Loaded, anySchemaToCoSchema, co } from "../internal";
|
||||
import { setupTwoNodes, waitFor } from "./utils";
|
||||
|
||||
const Message = co.map({
|
||||
@@ -26,7 +18,7 @@ describe("Inbox", () => {
|
||||
|
||||
const { clientAccount: sender, serverAccount: receiver } =
|
||||
await setupTwoNodes({
|
||||
ServerAccountSchema: zodSchemaToCoSchema(WorkerAccount),
|
||||
ServerAccountSchema: anySchemaToCoSchema(WorkerAccount),
|
||||
});
|
||||
|
||||
await expect(() => InboxSender.load(receiver.id, sender)).rejects.toThrow(
|
||||
|
||||
@@ -33,6 +33,15 @@ test("load a value", async () => {
|
||||
expect(john?.name).toBe("John");
|
||||
});
|
||||
|
||||
test("return null if id is invalid", async () => {
|
||||
const Person = co.map({
|
||||
name: z.string(),
|
||||
});
|
||||
|
||||
const john = await Person.load("test");
|
||||
expect(john).toBeNull();
|
||||
});
|
||||
|
||||
test("retry an unavailable value", async () => {
|
||||
const Person = co.map({
|
||||
name: z.string(),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { assert, describe, expect, test } from "vitest";
|
||||
import { Account, CoList, CoMap, Group, ID, co, z } from "../../exports";
|
||||
import { Account, Group, co, z } from "../../exports";
|
||||
import { createJazzTestAccount, linkAccounts } from "../../testing";
|
||||
|
||||
const RequestToJoin = co.map({
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
Loaded,
|
||||
anySchemaToCoSchema,
|
||||
co,
|
||||
loadCoValue,
|
||||
subscribeToCoValue,
|
||||
z,
|
||||
} from "../exports.js";
|
||||
@@ -24,7 +23,7 @@ const BlueButtonWidget = co.map({
|
||||
blueness: z.number(),
|
||||
});
|
||||
|
||||
const ButtonWidget = z.discriminatedUnion("type", [
|
||||
const ButtonWidget = co.discriminatedUnion("type", [
|
||||
RedButtonWidget,
|
||||
BlueButtonWidget,
|
||||
]);
|
||||
@@ -40,7 +39,7 @@ const CheckboxWidget = co.map({
|
||||
checked: z.boolean(),
|
||||
});
|
||||
|
||||
const WidgetUnion = z.discriminatedUnion("type", [
|
||||
const WidgetUnion = co.discriminatedUnion("type", [
|
||||
ButtonWidget,
|
||||
SliderWidget,
|
||||
CheckboxWidget,
|
||||
@@ -72,25 +71,15 @@ describe("SchemaUnion", () => {
|
||||
{ owner: me },
|
||||
);
|
||||
|
||||
const loadedButtonWidget = await loadCoValue(
|
||||
anySchemaToCoSchema(WidgetUnion),
|
||||
buttonWidget.id,
|
||||
{
|
||||
loadAs: me,
|
||||
},
|
||||
);
|
||||
const loadedSliderWidget = await loadCoValue(
|
||||
anySchemaToCoSchema(WidgetUnion),
|
||||
sliderWidget.id,
|
||||
{
|
||||
loadAs: me,
|
||||
},
|
||||
);
|
||||
const loadedCheckboxWidget = await loadCoValue(
|
||||
anySchemaToCoSchema(WidgetUnion),
|
||||
checkboxWidget.id,
|
||||
{ loadAs: me },
|
||||
);
|
||||
const loadedButtonWidget = await WidgetUnion.load(buttonWidget.id, {
|
||||
loadAs: me,
|
||||
});
|
||||
const loadedSliderWidget = await WidgetUnion.load(sliderWidget.id, {
|
||||
loadAs: me,
|
||||
});
|
||||
const loadedCheckboxWidget = await WidgetUnion.load(checkboxWidget.id, {
|
||||
loadAs: me,
|
||||
});
|
||||
|
||||
expect(loadedButtonWidget?.type).toBe("button");
|
||||
expect(loadedSliderWidget?.type).toBe("slider");
|
||||
|
||||
@@ -7,27 +7,13 @@ import {
|
||||
onTestFinished,
|
||||
vi,
|
||||
} from "vitest";
|
||||
import { Account, Group, cojsonInternals, z } from "../index.js";
|
||||
import {
|
||||
Account,
|
||||
CoFeed,
|
||||
CoList,
|
||||
CoMap,
|
||||
FileStream,
|
||||
Group,
|
||||
coField,
|
||||
cojsonInternals,
|
||||
z,
|
||||
} from "../index.js";
|
||||
import {
|
||||
CoMapInstance,
|
||||
ID,
|
||||
InstanceOrPrimitiveOfSchema,
|
||||
Loaded,
|
||||
Resolved,
|
||||
anySchemaToCoSchema,
|
||||
co,
|
||||
createCoValueObservable,
|
||||
subscribeToCoValue,
|
||||
zodSchemaToCoSchema,
|
||||
} from "../internal.js";
|
||||
import {
|
||||
createJazzTestAccount,
|
||||
@@ -43,7 +29,7 @@ const ReactionsFeed = co.feed(z.string());
|
||||
const Message = co.map({
|
||||
text: z.string(),
|
||||
reactions: ReactionsFeed,
|
||||
attachment: z.optional(co.fileStream()),
|
||||
attachment: co.optional(co.fileStream()),
|
||||
});
|
||||
|
||||
const ChatRoom = co.map({
|
||||
@@ -84,7 +70,7 @@ describe("subscribeToCoValue", () => {
|
||||
let result = null as Loaded<typeof ChatRoom, true> | null;
|
||||
|
||||
const unsubscribe = subscribeToCoValue(
|
||||
zodSchemaToCoSchema(ChatRoom),
|
||||
anySchemaToCoSchema(ChatRoom),
|
||||
chatRoom.id,
|
||||
{ loadAs: meOnSecondPeer },
|
||||
(value) => {
|
||||
@@ -131,7 +117,7 @@ describe("subscribeToCoValue", () => {
|
||||
let result = null as Loaded<typeof ChatRoom, {}> | null;
|
||||
|
||||
const unsubscribe = subscribeToCoValue(
|
||||
zodSchemaToCoSchema(ChatRoom),
|
||||
anySchemaToCoSchema(ChatRoom),
|
||||
chatRoom.id,
|
||||
{
|
||||
loadAs: meOnSecondPeer,
|
||||
@@ -172,7 +158,7 @@ describe("subscribeToCoValue", () => {
|
||||
messages.push(createMessage(me, "Hello"));
|
||||
|
||||
const unsubscribe = subscribeToCoValue(
|
||||
zodSchemaToCoSchema(ChatRoom),
|
||||
anySchemaToCoSchema(ChatRoom),
|
||||
chatRoom.id,
|
||||
{
|
||||
loadAs: meOnSecondPeer,
|
||||
@@ -215,7 +201,7 @@ describe("subscribeToCoValue", () => {
|
||||
const updateFn = vi.fn();
|
||||
|
||||
const unsubscribe = subscribeToCoValue(
|
||||
zodSchemaToCoSchema(ChatRoom),
|
||||
anySchemaToCoSchema(ChatRoom),
|
||||
chatRoom.id,
|
||||
{
|
||||
loadAs: meOnSecondPeer,
|
||||
@@ -275,7 +261,7 @@ describe("subscribeToCoValue", () => {
|
||||
>[];
|
||||
|
||||
const unsubscribe = subscribeToCoValue(
|
||||
zodSchemaToCoSchema(ChatRoom),
|
||||
anySchemaToCoSchema(ChatRoom),
|
||||
chatRoom.id,
|
||||
{
|
||||
loadAs: meOnSecondPeer,
|
||||
@@ -346,7 +332,7 @@ describe("subscribeToCoValue", () => {
|
||||
const updateFn = vi.fn();
|
||||
|
||||
const unsubscribe = subscribeToCoValue(
|
||||
zodSchemaToCoSchema(ChatRoom),
|
||||
anySchemaToCoSchema(ChatRoom),
|
||||
chatRoom.id,
|
||||
{
|
||||
loadAs: meOnSecondPeer,
|
||||
@@ -410,7 +396,7 @@ describe("subscribeToCoValue", () => {
|
||||
const updateFn = vi.fn();
|
||||
|
||||
const unsubscribe = subscribeToCoValue(
|
||||
zodSchemaToCoSchema(TestList),
|
||||
anySchemaToCoSchema(TestList),
|
||||
list.id,
|
||||
{
|
||||
loadAs: account,
|
||||
@@ -471,7 +457,7 @@ describe("subscribeToCoValue", () => {
|
||||
const onUnauthorized = vi.fn();
|
||||
|
||||
const unsubscribe = subscribeToCoValue(
|
||||
zodSchemaToCoSchema(TestList),
|
||||
anySchemaToCoSchema(TestList),
|
||||
list.id,
|
||||
{
|
||||
loadAs: reader,
|
||||
@@ -542,7 +528,7 @@ describe("subscribeToCoValue", () => {
|
||||
const onUnavailable = vi.fn();
|
||||
|
||||
const unsubscribe = subscribeToCoValue(
|
||||
zodSchemaToCoSchema(TestList),
|
||||
anySchemaToCoSchema(TestList),
|
||||
list.id,
|
||||
{
|
||||
loadAs: reader,
|
||||
@@ -617,7 +603,7 @@ describe("subscribeToCoValue", () => {
|
||||
const onUnavailable = vi.fn();
|
||||
|
||||
const unsubscribe = subscribeToCoValue(
|
||||
zodSchemaToCoSchema(TestList),
|
||||
anySchemaToCoSchema(TestList),
|
||||
list.id,
|
||||
{
|
||||
loadAs: reader,
|
||||
@@ -654,7 +640,7 @@ describe("subscribeToCoValue", () => {
|
||||
value: z.string(),
|
||||
});
|
||||
|
||||
const TestList = co.list(z.optional(TestMap));
|
||||
const TestList = co.list(co.optional(TestMap));
|
||||
|
||||
const reader = await createJazzTestAccount({
|
||||
isCurrentActiveAccount: true,
|
||||
@@ -687,7 +673,7 @@ describe("subscribeToCoValue", () => {
|
||||
const onUnavailable = vi.fn();
|
||||
|
||||
const unsubscribe = subscribeToCoValue(
|
||||
zodSchemaToCoSchema(TestList),
|
||||
anySchemaToCoSchema(TestList),
|
||||
list.id,
|
||||
{
|
||||
loadAs: reader,
|
||||
@@ -718,7 +704,7 @@ describe("subscribeToCoValue", () => {
|
||||
value: z.string(),
|
||||
});
|
||||
|
||||
const TestList = co.list(z.optional(TestMap));
|
||||
const TestList = co.list(co.optional(TestMap));
|
||||
|
||||
const creator = await createJazzTestAccount({
|
||||
isCurrentActiveAccount: true,
|
||||
@@ -739,7 +725,7 @@ describe("subscribeToCoValue", () => {
|
||||
});
|
||||
|
||||
const unsubscribe = subscribeToCoValue(
|
||||
zodSchemaToCoSchema(TestList),
|
||||
anySchemaToCoSchema(TestList),
|
||||
list.id,
|
||||
{
|
||||
loadAs: creator,
|
||||
@@ -806,7 +792,7 @@ describe("subscribeToCoValue", () => {
|
||||
});
|
||||
|
||||
const unsubscribe = subscribeToCoValue(
|
||||
zodSchemaToCoSchema(TestList),
|
||||
anySchemaToCoSchema(TestList),
|
||||
list.id,
|
||||
{
|
||||
loadAs: creator,
|
||||
@@ -895,7 +881,7 @@ describe("subscribeToCoValue", () => {
|
||||
});
|
||||
|
||||
const unsubscribe = subscribeToCoValue(
|
||||
zodSchemaToCoSchema(Person),
|
||||
anySchemaToCoSchema(Person),
|
||||
person.id,
|
||||
{
|
||||
loadAs: reader,
|
||||
@@ -982,7 +968,7 @@ describe("subscribeToCoValue", () => {
|
||||
const onUnavailable = vi.fn();
|
||||
|
||||
const unsubscribe = subscribeToCoValue(
|
||||
zodSchemaToCoSchema(TestList),
|
||||
anySchemaToCoSchema(TestList),
|
||||
list.id,
|
||||
{
|
||||
loadAs: reader,
|
||||
@@ -1069,7 +1055,7 @@ describe("subscribeToCoValue", () => {
|
||||
const onUnavailable = vi.fn();
|
||||
|
||||
const unsubscribe = subscribeToCoValue(
|
||||
zodSchemaToCoSchema(PersonList),
|
||||
anySchemaToCoSchema(PersonList),
|
||||
list.id,
|
||||
{
|
||||
loadAs: reader,
|
||||
@@ -1165,7 +1151,7 @@ describe("subscribeToCoValue", () => {
|
||||
const onUnavailable = vi.fn();
|
||||
|
||||
const unsubscribe = subscribeToCoValue(
|
||||
zodSchemaToCoSchema(PersonList),
|
||||
anySchemaToCoSchema(PersonList),
|
||||
list.id,
|
||||
{
|
||||
loadAs: reader,
|
||||
@@ -1248,7 +1234,7 @@ describe("subscribeToCoValue", () => {
|
||||
|
||||
// Test subscribing to the large coValue
|
||||
const unsubscribe = subscribeToCoValue(
|
||||
zodSchemaToCoSchema(LargeDataset),
|
||||
anySchemaToCoSchema(LargeDataset),
|
||||
largeMap.id,
|
||||
{
|
||||
loadAs: alice,
|
||||
@@ -1314,7 +1300,7 @@ describe("createCoValueObservable", () => {
|
||||
const mockListener = vi.fn();
|
||||
|
||||
const unsubscribe = observable.subscribe(
|
||||
zodSchemaToCoSchema(TestMap),
|
||||
anySchemaToCoSchema(TestMap),
|
||||
testMap.id,
|
||||
{
|
||||
loadAs: meOnSecondPeer,
|
||||
@@ -1343,7 +1329,7 @@ describe("createCoValueObservable", () => {
|
||||
const mockListener = vi.fn();
|
||||
|
||||
const unsubscribe = observable.subscribe(
|
||||
zodSchemaToCoSchema(TestMap),
|
||||
anySchemaToCoSchema(TestMap),
|
||||
testMap.id,
|
||||
{
|
||||
loadAs: meOnSecondPeer,
|
||||
|
||||
70
packages/jazz-tools/src/tools/tests/testStorage.ts
Normal file
70
packages/jazz-tools/src/tools/tests/testStorage.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { unlinkSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { SQLiteDatabaseDriverAsync, getSqliteStorageAsync } from "cojson";
|
||||
import Database, { type Database as DatabaseT } from "libsql";
|
||||
import { onTestFinished } from "vitest";
|
||||
|
||||
class LibSQLSqliteAsyncDriver implements SQLiteDatabaseDriverAsync {
|
||||
private readonly db: DatabaseT;
|
||||
|
||||
constructor(filename: string) {
|
||||
this.db = new Database(filename, {});
|
||||
}
|
||||
|
||||
async initialize() {
|
||||
await this.db.pragma("journal_mode = WAL");
|
||||
}
|
||||
|
||||
async run(sql: string, params: unknown[]) {
|
||||
this.db.prepare(sql).run(params);
|
||||
}
|
||||
|
||||
async query<T>(sql: string, params: unknown[]): Promise<T[]> {
|
||||
return this.db.prepare(sql).all(params) as T[];
|
||||
}
|
||||
|
||||
async get<T>(sql: string, params: unknown[]): Promise<T | undefined> {
|
||||
return this.db.prepare(sql).get(params) as T | undefined;
|
||||
}
|
||||
|
||||
async transaction(callback: () => unknown) {
|
||||
await this.run("BEGIN TRANSACTION", []);
|
||||
|
||||
try {
|
||||
await callback();
|
||||
await this.run("COMMIT", []);
|
||||
} catch (error) {
|
||||
await this.run("ROLLBACK", []);
|
||||
}
|
||||
}
|
||||
|
||||
async closeDb() {
|
||||
this.db.close();
|
||||
}
|
||||
}
|
||||
|
||||
export async function createAsyncStorage({ filename }: { filename?: string }) {
|
||||
const storage = await getSqliteStorageAsync(
|
||||
new LibSQLSqliteAsyncDriver(getDbPath(filename)),
|
||||
);
|
||||
|
||||
onTestFinished(() => {
|
||||
storage.close();
|
||||
});
|
||||
|
||||
return storage;
|
||||
}
|
||||
|
||||
export function getDbPath(defaultDbPath?: string) {
|
||||
const dbPath = defaultDbPath ?? join(tmpdir(), `test-${randomUUID()}.db`);
|
||||
|
||||
if (!defaultDbPath) {
|
||||
onTestFinished(() => {
|
||||
unlinkSync(dbPath);
|
||||
});
|
||||
}
|
||||
|
||||
return dbPath;
|
||||
}
|
||||
70
packages/jazz-tools/src/tools/tests/zod.test-d.ts
Normal file
70
packages/jazz-tools/src/tools/tests/zod.test-d.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { describe, test } from "vitest";
|
||||
import { z } from "../exports";
|
||||
import { co } from "../internal";
|
||||
|
||||
describe("CoValue and Zod schema compatibility", () => {
|
||||
test("cannot use z.record with CoValue schemas as values", () => {
|
||||
const Dog = co.map({
|
||||
type: z.literal("dog"),
|
||||
});
|
||||
|
||||
const Person = co.map({
|
||||
// @ts-expect-error: cannot use z.record with a CoValue schema
|
||||
// (z.record is not exported by jazz-tools)
|
||||
pets: z.record(z.string(), Dog),
|
||||
});
|
||||
});
|
||||
|
||||
test("cannot use z.union with CoValue schemas as values", () => {
|
||||
const Dog = co.map({
|
||||
type: z.literal("dog"),
|
||||
});
|
||||
|
||||
const Cat = co.map({
|
||||
type: z.literal("cat"),
|
||||
});
|
||||
|
||||
const Person = co.map({
|
||||
// @ts-expect-error: cannot use z.union with a CoValue schema
|
||||
pets: z.union([Dog, Cat]),
|
||||
});
|
||||
});
|
||||
|
||||
test("cannot use z.intersection with CoValue schemas as values", () => {
|
||||
const Dog = co.map({
|
||||
type: z.literal("dog"),
|
||||
});
|
||||
|
||||
const Cat = co.map({
|
||||
type: z.literal("cat"),
|
||||
});
|
||||
|
||||
const Person = co.map({
|
||||
// @ts-expect-error: cannot use z.intersection with a CoValue schema
|
||||
// (z.intersection is not exported by jazz-tools)
|
||||
pets: z.intersection(Dog, Cat),
|
||||
});
|
||||
});
|
||||
|
||||
test("cannot use z.array with CoValue schemas as values", () => {
|
||||
const Dog = co.map({
|
||||
type: z.literal("dog"),
|
||||
});
|
||||
|
||||
const Person = co.map({
|
||||
// @ts-expect-error: cannot use z.array with a CoValue schema
|
||||
pets: z.array(Dog),
|
||||
});
|
||||
});
|
||||
|
||||
test("cannot use z.tuple with CoValue schemas as values", () => {
|
||||
const Dog = co.map({
|
||||
type: z.literal("dog"),
|
||||
});
|
||||
|
||||
const Person = co.map({
|
||||
// @ts-expect-error: cannot use z.tuple with a CoValue schema
|
||||
pets: z.tuple([Dog]),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,10 +1,18 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, describe, expect, it, vi } from "vitest";
|
||||
import { z } from "../exports.js";
|
||||
import { co } from "../internal.js";
|
||||
import { createJazzTestAccount } from "../testing.js";
|
||||
import { createJazzTestAccount, setupJazzTestSync } from "../testing.js";
|
||||
|
||||
describe("co.map and Zod schema compatibility", () => {
|
||||
// Helper function to create a test account
|
||||
beforeAll(async () => {
|
||||
await setupJazzTestSync();
|
||||
|
||||
await createJazzTestAccount({
|
||||
isCurrentActiveAccount: true,
|
||||
creationProps: { name: "Hermes Puggington" },
|
||||
});
|
||||
});
|
||||
|
||||
describe("Primitive types", () => {
|
||||
it("should handle string fields", async () => {
|
||||
const schema = co.map({
|
||||
@@ -348,12 +356,63 @@ describe("co.map and Zod schema compatibility", () => {
|
||||
// ]),
|
||||
// });
|
||||
// const account = await createJazzTestAccount();
|
||||
// const successMap = schema.create({ result: { status: "success", data: "data" } }, account);
|
||||
// const failedMap = schema.create({ result: { status: "failed", error: "error" } }, account);
|
||||
// const successMap = schema.create(
|
||||
// { result: { status: "success", data: "data" } },
|
||||
// account,
|
||||
// );
|
||||
// const failedMap = schema.create(
|
||||
// { result: { status: "failed", error: "error" } },
|
||||
// account,
|
||||
// );
|
||||
// expect(successMap.result).toEqual({ status: "success", data: "data" });
|
||||
// expect(failedMap.result).toEqual({ status: "failed", error: "error" });
|
||||
// });
|
||||
|
||||
it("should handle discriminated unions of CoValues", () => {
|
||||
const Dog = co.map({
|
||||
type: z.literal("dog"),
|
||||
breed: z.string(),
|
||||
});
|
||||
|
||||
const Cat = co.map({
|
||||
type: z.literal("cat"),
|
||||
name: z.string(),
|
||||
});
|
||||
|
||||
const Person = co.map({
|
||||
pet: z.discriminatedUnion("type", [Dog, Cat]),
|
||||
});
|
||||
|
||||
const person = Person.create({
|
||||
pet: Dog.create({ type: "dog", breed: "Labrador" }),
|
||||
});
|
||||
|
||||
expect(person.pet).toEqual({ type: "dog", breed: "Labrador" });
|
||||
|
||||
person.pet = Cat.create({ type: "cat", name: "Whiskers" });
|
||||
|
||||
expect(person.pet).toEqual({ type: "cat", name: "Whiskers" });
|
||||
});
|
||||
|
||||
it("should handle optional CoValues", () => {
|
||||
const Dog = co.map({
|
||||
name: z.string(),
|
||||
breed: z.string(),
|
||||
});
|
||||
|
||||
const Person = co.map({
|
||||
pet: z.optional(Dog),
|
||||
});
|
||||
|
||||
const person = Person.create({});
|
||||
|
||||
expect(person.pet).toBeUndefined();
|
||||
|
||||
person.pet = Dog.create({ name: "Rex", breed: "Labrador" });
|
||||
|
||||
expect(person.pet).toEqual({ name: "Rex", breed: "Labrador" });
|
||||
});
|
||||
|
||||
// it("should handle intersections", async () => {
|
||||
// const schema = co.map({
|
||||
// value: z.intersection(
|
||||
|
||||
10
pnpm-lock.yaml
generated
10
pnpm-lock.yaml
generated
@@ -1772,19 +1772,19 @@ importers:
|
||||
specifier: ^0.25.5
|
||||
version: 0.25.8(effect@3.11.9)
|
||||
cojson:
|
||||
specifier: workspace:0.15.9
|
||||
specifier: workspace:0.15.12
|
||||
version: link:../cojson
|
||||
cojson-storage-sqlite:
|
||||
specifier: workspace:0.15.9
|
||||
specifier: workspace:0.15.12
|
||||
version: link:../cojson-storage-sqlite
|
||||
cojson-transport-ws:
|
||||
specifier: workspace:0.15.9
|
||||
specifier: workspace:0.15.12
|
||||
version: link:../cojson-transport-ws
|
||||
effect:
|
||||
specifier: ^3.6.5
|
||||
version: 3.11.9
|
||||
jazz-tools:
|
||||
specifier: workspace:0.15.9
|
||||
specifier: workspace:0.15.12
|
||||
version: link:../jazz-tools
|
||||
ws:
|
||||
specifier: ^8.14.2
|
||||
@@ -17534,9 +17534,7 @@ snapshots:
|
||||
metro-runtime: 0.82.3
|
||||
transitivePeerDependencies:
|
||||
- '@babel/core'
|
||||
- bufferutil
|
||||
- supports-color
|
||||
- utf-8-validate
|
||||
|
||||
'@react-native/normalize-colors@0.79.2': {}
|
||||
|
||||
|
||||
@@ -1,5 +1,28 @@
|
||||
# jazz-react-tailwind-starter
|
||||
|
||||
## 0.0.132
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [d1c1b0c]
|
||||
- Updated dependencies [cf4ad72]
|
||||
- jazz-tools@0.15.12
|
||||
|
||||
## 0.0.131
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [bdc9aee]
|
||||
- jazz-tools@0.15.11
|
||||
|
||||
## 0.0.130
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [9815ec6]
|
||||
- Updated dependencies [b4fdab4]
|
||||
- jazz-tools@0.15.10
|
||||
|
||||
## 0.0.129
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-react-passkey-auth-starter",
|
||||
"private": true,
|
||||
"version": "0.0.129",
|
||||
"version": "0.0.132",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,28 @@
|
||||
# svelte-passkey-auth
|
||||
|
||||
## 0.0.106
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [d1c1b0c]
|
||||
- Updated dependencies [cf4ad72]
|
||||
- jazz-tools@0.15.12
|
||||
|
||||
## 0.0.105
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [bdc9aee]
|
||||
- jazz-tools@0.15.11
|
||||
|
||||
## 0.0.104
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [9815ec6]
|
||||
- Updated dependencies [b4fdab4]
|
||||
- jazz-tools@0.15.10
|
||||
|
||||
## 0.0.103
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "svelte-passkey-auth",
|
||||
"version": "0.0.103",
|
||||
"version": "0.0.106",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { zodSchemaToCoSchema } from "jazz-tools";
|
||||
import { anySchemaToCoSchema } from "jazz-tools";
|
||||
import { useAccount, useCoState } from "jazz-tools/react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { createCredentiallessIframe } from "../../lib/createCredentiallessIframe";
|
||||
@@ -44,7 +44,7 @@ export function UploaderPeer() {
|
||||
// The downloader peer will set the syncCompleted to true when the download is complete.
|
||||
// We use this to measure the sync duration.
|
||||
await waitForCoValue(
|
||||
zodSchemaToCoSchema(UploadedFile),
|
||||
anySchemaToCoSchema(UploadedFile),
|
||||
file.id,
|
||||
(value) => value.syncCompleted,
|
||||
{ loadAs: account.me },
|
||||
|
||||
Reference in New Issue
Block a user