CoMaps
CoMaps are key-value objects that work like JavaScript objects. You can access properties with dot notation and define typed fields that provide TypeScript safety. They're ideal for structured data that needs type validation.
Creating CoMaps
CoMaps are typically defined with co.map()
and specifying primitive fields using z
(see Defining schemas: CoValues for more details on primitive fields):
import {
import co
co,import z
z } from "jazz-tools"; constProject =
const Project: CoMapSchema<{ name: z.z.ZodString; startDate: z.z.ZodDate; status: z.z.ZodLiteral<"planning" | "active" | "completed">; coordinator: z.ZodOptional<CoMapSchema<{ name: z.z.ZodString; }>>; }>
import co
co.map({
map<{ name: z.z.ZodString; startDate: z.z.ZodDate; status: z.z.ZodLiteral<"planning" | "active" | "completed">; coordinator: z.ZodOptional<CoMapSchema<{ name: z.z.ZodString; }>>; }>(shape: { name: z.z.ZodString; startDate: z.z.ZodDate; status: z.z.ZodLiteral<"planning" | "active" | "completed">; coordinator: z.ZodOptional<CoMapSchema<{ name: z.z.ZodString; }>>; }): CoMapSchema<...> export map
name: z.z.ZodString
name:import z
z.string(),
function string(params?: string | z.z.core.$ZodStringParams): z.z.ZodString export string
startDate: z.z.ZodDate
startDate:import z
z.date(),
function date(params?: string | z.z.core.$ZodDateParams): z.z.ZodDate export date
status: z.z.ZodLiteral<"planning" | "active" | "completed">
status:import z
z.literal(["planning", "active", "completed"]),
literal<["planning", "active", "completed"]>(value: ["planning", "active", "completed"], params?: string | z.z.core.$ZodLiteralParams): z.z.ZodLiteral<"planning" | "active" | "completed"> (+1 overload) export literal
coordinator:
coordinator: z.ZodOptional<CoMapSchema<{ name: z.z.ZodString; }>>
import z
z.optional(
optional<CoMapSchema<{ name: z.z.ZodString; }>>(innerType: CoMapSchema<{ name: z.z.ZodString; }>): z.ZodOptional<CoMapSchema<{ name: z.z.ZodString; }>> export optional
Member), });
const Member: CoMapSchema<{ name: z.z.ZodString; }>
You can create either struct-like CoMaps with fixed fields (as above) or record-like CoMaps for key-value pairs:
const
const Inventory: CoRecordSchema<z.z.ZodString, z.z.ZodNumber>
Inventory =import co
co.record(
record<z.z.ZodString, z.z.ZodNumber>(_keyType: z.z.ZodString, valueType: z.z.ZodNumber): CoRecordSchema<z.z.ZodString, z.z.ZodNumber> export record
import z
z.string(),
function string(params?: string | z.z.core.$ZodStringParams): z.z.ZodString export string
import z
z.number());
function number(params?: string | z.z.core.$ZodNumberParams): z.z.ZodNumber export number
To instantiate a CoMap:
const
project =
const project: { name: string; startDate: Date; status: "planning" | "active" | "completed"; coordinator: ({ name: string; } & CoMap) | undefined; } & CoMap
Project.
const Project: CoMapSchema<{ name: z.z.ZodString; startDate: z.z.ZodDate; status: z.z.ZodLiteral<"planning" | "active" | "completed">; coordinator: z.ZodOptional<CoMapSchema<{ name: z.z.ZodString; }>>; }>
create({
create: (init: { coordinator?: ({ name: string; } & CoMap) | undefined; name: string; startDate: Date; status: NonNullable<"planning" | "active" | "completed">; }, options?: Account | ... 2 more ... | undefined) => { ...; } & CoMap
name: string
name: "Spring Planting",startDate: Date
startDate: newDate("2025-03-15"),
var Date: DateConstructor new (value: number | string | Date) => Date (+4 overloads)
status: NonNullable<"planning" | "active" | "completed">
status: "planning", }); constinventory =
const inventory: { [x: string]: number; } & CoMap
const Inventory: CoRecordSchema<z.z.ZodString, z.z.ZodNumber>
Inventory.create({
create: (init: { [x: string]: number; }, options?: { owner: Account | Group; unique?: CoValueUniqueness["uniqueness"]; } | Account | Group) => { ...; } & CoMap
tomatoes: number
tomatoes: 48,basil: number
basil: 12, });
Ownership
When creating CoMaps, you can specify ownership to control access:
// Create with default owner (current user) const
privateProject =
const privateProject: { name: string; startDate: Date; status: "planning" | "active" | "completed"; coordinator: ({ name: string; } & CoMap) | undefined; } & CoMap
Project.
const Project: CoMapSchema<{ name: z.z.ZodString; startDate: z.z.ZodDate; status: z.z.ZodLiteral<"planning" | "active" | "completed">; coordinator: z.ZodOptional<CoMapSchema<{ name: z.z.ZodString; }>>; }>
create({
create: (init: { coordinator?: ({ name: string; } & CoMap) | undefined; name: string; startDate: Date; status: NonNullable<"planning" | "active" | "completed">; }, options?: Account | ... 2 more ... | undefined) => { ...; } & CoMap
name: string
name: "My Herb Garden",startDate: Date
startDate: newDate("2025-04-01"),
var Date: DateConstructor new (value: number | string | Date) => Date (+4 overloads)
status: NonNullable<"planning" | "active" | "completed">
status: "planning", }); // Create with shared ownership constconst gardenGroup: Group
gardenGroup =class Group
Group.create();
Group.create<Group>(this: CoValueClass<Group>, options?: { owner: Account; } | Account): Group
const gardenGroup: Group
gardenGroup.Group.addMember(member: Account, role: AccountRole): void (+1 overload)
addMember(memberAccount, "writer"); const
const memberAccount: Account | ({ [x: string]: any; } & Account)
communityProject =
const communityProject: { name: string; startDate: Date; status: "planning" | "active" | "completed"; coordinator: ({ name: string; } & CoMap) | undefined; } & CoMap
Project.
const Project: CoMapSchema<{ name: z.z.ZodString; startDate: z.z.ZodDate; status: z.z.ZodLiteral<"planning" | "active" | "completed">; coordinator: z.ZodOptional<CoMapSchema<{ name: z.z.ZodString; }>>; }>
create( {
create: (init: { coordinator?: ({ name: string; } & CoMap) | undefined; name: string; startDate: Date; status: NonNullable<"planning" | "active" | "completed">; }, options?: Account | ... 2 more ... | undefined) => { ...; } & CoMap
name: string
name: "Community Vegetable Plot",startDate: Date
startDate: newDate("2025-03-20"),
var Date: DateConstructor new (value: number | string | Date) => Date (+4 overloads)
status: NonNullable<"planning" | "active" | "completed">
status: "planning", }, {owner: Account | Group
owner:const gardenGroup: Group
gardenGroup }, );
See Groups as permission scopes for more information on how to use groups to control access to CoMaps.
Reading from CoMaps
CoMaps can be accessed using familiar JavaScript object notation:
var console: Console
The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v20.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```console.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v20.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v20.x/api/util.html#utilformatformat-args) for more information.log(project.
const project: { name: string; startDate: Date; status: "planning" | "active" | "completed"; coordinator: ({ name: string; } & CoMap) | undefined; } & CoMap
name: string
name); // "Spring Planting"var console: Console
The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v20.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```console.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v20.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v20.x/api/util.html#utilformatformat-args) for more information.log(project.
const project: { name: string; startDate: Date; status: "planning" | "active" | "completed"; coordinator: ({ name: string; } & CoMap) | undefined; } & CoMap
status: "planning" | "active" | "completed"
status); // "planning"
Handling Optional Fields
Optional fields require checks before access:
if (
project.
const project: { name: string; startDate: Date; status: "planning" | "active" | "completed"; coordinator: ({ name: string; } & CoMap) | undefined; } & CoMap
coordinator) {
coordinator: ({ name: string; } & CoMap) | undefined
var console: Console
The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v20.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```console.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v20.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v20.x/api/util.html#utilformatformat-args) for more information.log(project.
const project: { name: string; startDate: Date; status: "planning" | "active" | "completed"; coordinator: ({ name: string; } & CoMap) | undefined; } & CoMap
coordinator.
coordinator: { name: string; } & CoMap
name: string
name); // Safe access }
Working with Record CoMaps
For record-type CoMaps, you can access values using bracket notation:
const
inventory =
const inventory: { [x: string]: number; } & CoMap
const Inventory: CoRecordSchema<z.z.ZodString, z.z.ZodNumber>
Inventory.create({
create: (init: { [x: string]: number; }, options?: { owner: Account | Group; unique?: CoValueUniqueness["uniqueness"]; } | Account | Group) => { ...; } & CoMap
tomatoes: number
tomatoes: 48,peppers: number
peppers: 24,basil: number
basil: 12 });var console: Console
The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v20.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```console.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v20.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v20.x/api/util.html#utilformatformat-args) for more information.log(inventory["tomatoes"]); // 48
const inventory: { [x: string]: number; } & CoMap
Updating CoMaps
Updating CoMap properties uses standard JavaScript assignment:
project.
const project: { name: string; startDate: Date; status: "planning" | "active" | "completed"; coordinator: ({ name: string; } & CoMap) | undefined; } & CoMap
name: string
name = "Spring Vegetable Garden"; // Update nameproject.
const project: { name: string; startDate: Date; status: "planning" | "active" | "completed"; coordinator: ({ name: string; } & CoMap) | undefined; } & CoMap
startDate: Date
startDate = newDate("2025-03-20"); // Update date
var Date: DateConstructor new (value: number | string | Date) => Date (+4 overloads)
Type Safety
CoMaps are fully typed in TypeScript, giving you autocomplete and error checking:
project.
const project: { name: string; startDate: Date; status: "planning" | "active" | "completed"; coordinator: ({ name: string; } & CoMap) | undefined; } & CoMap
name: string
name = "Spring Vegetable Planting"; // ✓ Valid string project.startDate = "2025-03-15"; // ✗ Type error: expected Date
Deleting Properties
You can delete properties from CoMaps:
delete
inventory["basil"]; // Remove a key-value pair // For optional fields in struct-like CoMaps
const inventory: { [x: string]: number; } & CoMap
project.
const project: { name: string; startDate: Date; status: "planning" | "active" | "completed"; coordinator: ({ name: string; } & CoMap) | undefined; } & CoMap
coordinator =
coordinator: ({ name: string; } & CoMap) | undefined
var undefined
undefined; // Remove the reference
Running migrations on CoMaps
Migrations are functions that run when a CoMap is loaded, allowing you to update existing data to match new schema versions. Use them when you need to modify the structure of CoMaps that already exist in your app. Unlike Account migrations, CoMap migrations are not run when a CoMap is created.
Note: Migrations are run synchronously and cannot be run asynchronously.
Here's an example of a migration that adds the priority
field to the Task
CoMap:
const
Task =
const Task: CoMapSchema<{ done: z.z.ZodBoolean; text: PlainTextSchema; version: z.z.ZodLiteral<1 | 2>; priority: z.z.ZodEnum<{ low: "low"; medium: "medium"; high: "high"; }>; }, z.z.core.$ZodObjectConfig, Account | Group>
import co
co .map({
map<{ done: z.z.ZodBoolean; text: PlainTextSchema; version: z.z.ZodLiteral<1 | 2>; priority: z.z.ZodEnum<{ low: "low"; medium: "medium"; high: "high"; }>; }>(shape: { done: z.z.ZodBoolean; text: PlainTextSchema; version: z.z.ZodLiteral<1 | 2>; priority: z.z.ZodEnum<{ low: "low"; medium: "medium"; high: "high"; }>; }): CoMapSchema<...> export map
done: z.z.ZodBoolean
done:import z
z.boolean(),
function boolean(params?: string | z.z.core.$ZodBooleanParams): z.z.ZodBoolean export boolean
text: PlainTextSchema
text:import co
co.plainText(),
function plainText(): PlainTextSchema export plainText
version: z.z.ZodLiteral<1 | 2>
version:import z
z.literal([1, 2]),
literal<[1, 2]>(value: [1, 2], params?: string | z.z.core.$ZodLiteralParams): z.z.ZodLiteral<1 | 2> (+1 overload) export literal
priority:
priority: z.z.ZodEnum<{ low: "low"; medium: "medium"; high: "high"; }>
import z
z.enum(["low", "medium", "high"]), // new field }) .
enum<readonly ["low", "medium", "high"]>(values: readonly ["low", "medium", "high"], params?: string | z.z.core.$ZodEnumParams): z.z.ZodEnum<{ low: "low"; medium: "medium"; high: "high"; }> (+1 overload) export enum
withMigration((
function withMigration(migration: (value: { done: boolean; text: CoPlainText | null; version: 1 | 2; priority: "low" | "medium" | "high"; } & CoMap) => undefined): CoMapSchema<{ done: z.z.ZodBoolean; text: PlainTextSchema; version: z.z.ZodLiteral<...>; priority: z.z.ZodEnum<...>; }, z.z.core.$ZodObjectConfig, Account | Group>
task) => { if (
task: { done: boolean; text: CoPlainText | null; version: 1 | 2; priority: "low" | "medium" | "high"; } & CoMap
task.
task: { done: boolean; text: CoPlainText | null; version: 1 | 2; priority: "low" | "medium" | "high"; } & CoMap
version: 1 | 2
version === 1) {task.
task: { done: boolean; text: CoPlainText | null; version: 1 | 2; priority: "low" | "medium" | "high"; } & CoMap
priority: "low" | "medium" | "high"
priority = "medium"; // Upgrade the version so the migration won't run againtask.
task: { done: boolean; text: CoPlainText | null; version: 1 | 2; priority: "low" | "medium" | "high"; } & CoMap
version: 1 | 2
version = 2; } });
Migration best practices
Design your schema changes to be compatible with existing data:
- Add, don't change: Only add new fields; avoid renaming or changing types of existing fields
- Make new fields optional: This prevents errors when loading older data
- Use version fields: Track schema versions to run migrations only when needed
Migration & reader permissions
Migrations need write access to modify CoMaps. If some users only have read permissions, they can't run migrations on those CoMaps.
Forward-compatible schemas (where new fields are optional) handle this gracefully - users can still use the app even if migrations haven't run.
Non-compatible changes require handling both schema versions in your app code using discriminated unions.
When you can't guarantee all users can run migrations, handle multiple schema versions explicitly:
const
TaskV1 =
const TaskV1: CoMapSchema<{ version: z.z.ZodLiteral<1>; done: z.z.ZodBoolean; text: z.z.ZodString; }>
import co
co.map({
map<{ version: z.z.ZodLiteral<1>; done: z.z.ZodBoolean; text: z.z.ZodString; }>(shape: { version: z.z.ZodLiteral<1>; done: z.z.ZodBoolean; text: z.z.ZodString; }): CoMapSchema<{ version: z.z.ZodLiteral<1>; done: z.z.ZodBoolean; text: z.z.ZodString; }> export map
version: z.z.ZodLiteral<1>
version:import z
z.literal(1),
literal<1>(value: 1, params?: string | z.z.core.$ZodLiteralParams): z.z.ZodLiteral<1> (+1 overload) export literal
done: z.z.ZodBoolean
done:import z
z.boolean(),
function boolean(params?: string | z.z.core.$ZodBooleanParams): z.z.ZodBoolean export boolean
text: z.z.ZodString
text:import z
z.string(), }); const
function string(params?: string | z.z.core.$ZodStringParams): z.z.ZodString export string
TaskV2 =
const TaskV2: CoMapSchema<{ version: z.z.ZodLiteral<2>; done: z.z.ZodBoolean; text: z.z.ZodString; priority: z.z.ZodEnum<{ low: "low"; medium: "medium"; high: "high"; }>; }, z.z.core.$ZodObjectConfig, Account | Group>
import co
co.map({ // We need to be more strict about the version to make the // discriminated union work
map<{ version: z.z.ZodLiteral<2>; done: z.z.ZodBoolean; text: z.z.ZodString; priority: z.z.ZodEnum<{ low: "low"; medium: "medium"; high: "high"; }>; }>(shape: { version: z.z.ZodLiteral<2>; done: z.z.ZodBoolean; text: z.z.ZodString; priority: z.z.ZodEnum<{ low: "low"; medium: "medium"; high: "high"; }>; }): CoMapSchema<...> export map
version: z.z.ZodLiteral<2>
version:import z
z.literal(2),
literal<2>(value: 2, params?: string | z.z.core.$ZodLiteralParams): z.z.ZodLiteral<2> (+1 overload) export literal
done: z.z.ZodBoolean
done:import z
z.boolean(),
function boolean(params?: string | z.z.core.$ZodBooleanParams): z.z.ZodBoolean export boolean
text: z.z.ZodString
text:import z
z.string(),
function string(params?: string | z.z.core.$ZodStringParams): z.z.ZodString export string
priority:
priority: z.z.ZodEnum<{ low: "low"; medium: "medium"; high: "high"; }>
import z
z.enum(["low", "medium", "high"]), }).
enum<readonly ["low", "medium", "high"]>(values: readonly ["low", "medium", "high"], params?: string | z.z.core.$ZodEnumParams): z.z.ZodEnum<{ low: "low"; medium: "medium"; high: "high"; }> (+1 overload) export enum
withMigration((
function withMigration(migration: (value: { version: 2; done: boolean; text: string; priority: "low" | "medium" | "high"; } & CoMap) => undefined): CoMapSchema<{ version: z.z.ZodLiteral<2>; done: z.z.ZodBoolean; text: z.z.ZodString; priority: z.z.ZodEnum<...>; }, z.z.core.$ZodObjectConfig, Account | Group>
task) => { // @ts-expect-error - check if we need to run the migration if (
task: { version: 2; done: boolean; text: string; priority: "low" | "medium" | "high"; } & CoMap
task.
task: { version: 2; done: boolean; text: string; priority: "low" | "medium" | "high"; } & CoMap
version: 2
version === 1) {task.
task: { version: 2; done: boolean; text: string; priority: "low" | "medium" | "high"; } & CoMap
version: 2
version = 2;task.
task: { version: 2; done: boolean; text: string; priority: "low" | "medium" | "high"; } & CoMap
priority: "low" | "medium" | "high"
priority = "medium"; } }); // Export the discriminated union; because some users might // not be able to run the migration export constTask =
const Task: z.z.ZodDiscriminatedUnion<[CoMapSchema<{ version: z.z.ZodLiteral<1>; done: z.z.ZodBoolean; text: z.z.ZodString; }>, CoMapSchema<{ version: z.z.ZodLiteral<2>; done: z.z.ZodBoolean; text: z.z.ZodString; priority: z.z.ZodEnum<{ low: "low"; medium: "medium"; high: "high"; }>; }, z.z.core.$ZodObjectConfig, Account | Group>]>
import z
z.discriminatedUnion("version", [
discriminatedUnion<[CoMapSchema<{ version: z.z.ZodLiteral<1>; done: z.z.ZodBoolean; text: z.z.ZodString; }>, CoMapSchema<{ version: z.z.ZodLiteral<2>; done: z.z.ZodBoolean; text: z.z.ZodString; priority: z.z.ZodEnum<...>; }, z.z.core.$ZodObjectConfig, Account | Group>]>(discriminator: string, options: [...], params?: string | z.z.core.$ZodDiscriminatedUnionParams): z.z.ZodDiscriminatedUnion<...> export discriminatedUnion
TaskV1,
const TaskV1: CoMapSchema<{ version: z.z.ZodLiteral<1>; done: z.z.ZodBoolean; text: z.z.ZodString; }>
TaskV2, ]);
const TaskV2: CoMapSchema<{ version: z.z.ZodLiteral<2>; done: z.z.ZodBoolean; text: z.z.ZodString; priority: z.z.ZodEnum<{ low: "low"; medium: "medium"; high: "high"; }>; }, z.z.core.$ZodObjectConfig, Account | Group>
Best Practices
Structuring Data
- Use struct-like CoMaps for entities with fixed, known properties
- Use record-like CoMaps for dynamic key-value collections
- Group related properties into nested CoMaps for better organization
Common Patterns
Helper methods
You should define helper methods of CoValue schemas separately, in standalone functions:
import {
import co
co,import z
z } from "jazz-tools"; constProject =
const Project: CoMapSchema<{ name: z.z.ZodString; startDate: z.z.ZodDate; endDate: z.ZodOptional<z.z.ZodDate>; }>
import co
co.map({
map<{ name: z.z.ZodString; startDate: z.z.ZodDate; endDate: z.ZodOptional<z.z.ZodDate>; }>(shape: { name: z.z.ZodString; startDate: z.z.ZodDate; endDate: z.ZodOptional<z.z.ZodDate>; }): CoMapSchema<...> export map
name: z.z.ZodString
name:import z
z.string(),
function string(params?: string | z.z.core.$ZodStringParams): z.z.ZodString export string
startDate: z.z.ZodDate
startDate:import z
z.date(),
function date(params?: string | z.z.core.$ZodDateParams): z.z.ZodDate export date
endDate: z.ZodOptional<z.z.ZodDate>
endDate:import z
z.optional(
optional<z.z.ZodDate>(innerType: z.z.ZodDate): z.ZodOptional<z.z.ZodDate> export optional
import z
z.date()), }); type
function date(params?: string | z.z.core.$ZodDateParams): z.z.ZodDate export date
Project =
type Project = { name: string; startDate: Date; endDate: Date | undefined; } & CoMap
import co
co.loaded<typeof
type loaded<T extends CoValueClass | AnyCoSchema, R extends ResolveQuery<T> = true> = R extends boolean | undefined ? NonNullable<InstanceOfSchemaCoValuesNullable<T>> : [NonNullable<InstanceOfSchemaCoValuesNullable<T>>] extends [...] ? Exclude<...> extends CoValue ? R extends { ...; } ? ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<... export loaded
Project>; export function
const Project: CoMapSchema<{ name: z.z.ZodString; startDate: z.z.ZodDate; endDate: z.ZodOptional<z.z.ZodDate>; }>
function isProjectActive(project: Project): boolean
isProjectActive(project:
project: { name: string; startDate: Date; endDate: Date | undefined; } & CoMap
Project) { const
type Project = { name: string; startDate: Date; endDate: Date | undefined; } & CoMap
const now: Date
now = newDate(); return
var Date: DateConstructor new () => Date (+4 overloads)
const now: Date
now >=project.
project: { name: string; startDate: Date; endDate: Date | undefined; } & CoMap
startDate: Date
startDate && (!project.
project: { name: string; startDate: Date; endDate: Date | undefined; } & CoMap
endDate: Date | undefined
endDate ||const now: Date
now <=project.
project: { name: string; startDate: Date; endDate: Date | undefined; } & CoMap
endDate: Date
endDate); } export functionfunction formatProjectDuration(project: Project, format: "short" | "full"): string
formatProjectDuration(project:
project: { name: string; startDate: Date; endDate: Date | undefined; } & CoMap
Project,
type Project = { name: string; startDate: Date; endDate: Date | undefined; } & CoMap
format: "short" | "full"
format: "short" | "full") { constconst start: string
start =project.
project: { name: string; startDate: Date; endDate: Date | undefined; } & CoMap
startDate: Date
startDate.Date.toLocaleDateString(locales?: Intl.LocalesArgument, options?: Intl.DateTimeFormatOptions): string (+2 overloads)
Converts a date to a string by using the current or specified locale.toLocaleDateString(); if (!project.
project: { name: string; startDate: Date; endDate: Date | undefined; } & CoMap
endDate: Date | undefined
endDate) { returnformat: "short" | "full"
format === "full" ? `Started on ${const start: string
start}, ongoing` : `From ${const start: string
start}`; } constconst end: string
end =project.
project: { name: string; startDate: Date; endDate: Date | undefined; } & CoMap
endDate: Date
endDate.Date.toLocaleDateString(locales?: Intl.LocalesArgument, options?: Intl.DateTimeFormatOptions): string (+2 overloads)
Converts a date to a string by using the current or specified locale.toLocaleDateString(); returnformat: "short" | "full"
format === "full" ? `From ${const start: string
start} to ${const end: string
end}` : `${(project.
project: { name: string; startDate: Date; endDate: Date | undefined; } & CoMap
endDate: Date
endDate.Date.getTime(): number
Returns the stored time value in milliseconds since midnight, January 1, 1970 UTC.getTime() -project.
project: { name: string; startDate: Date; endDate: Date | undefined; } & CoMap
startDate: Date
startDate.Date.getTime(): number
Returns the stored time value in milliseconds since midnight, January 1, 1970 UTC.getTime()) / 86400000} days`; } constproject =
const project: { name: string; startDate: Date; endDate: Date | undefined; } & CoMap
Project.
const Project: CoMapSchema<{ name: z.z.ZodString; startDate: z.z.ZodDate; endDate: z.ZodOptional<z.z.ZodDate>; }>
create({
create: (init: { endDate?: Date | undefined; name: string; startDate: Date; }, options?: Account | Group | { owner: Account | Group; unique?: CoValueUniqueness["uniqueness"]; } | undefined) => { ...; } & CoMap
name: string
name: "My project",startDate: Date
startDate: newDate("2025-04-01"),
var Date: DateConstructor new (value: number | string | Date) => Date (+4 overloads)
endDate?: Date | undefined
endDate: newDate("2025-04-04"), });
var Date: DateConstructor new (value: number | string | Date) => Date (+4 overloads)
var console: Console
The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v20.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```console.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v20.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v20.x/api/util.html#utilformatformat-args) for more information.log(function isProjectActive(project: Project): boolean
isProjectActive(project)); // false
const project: { name: string; startDate: Date; endDate: Date | undefined; } & CoMap
var console: Console
The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v20.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```console.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v20.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v20.x/api/util.html#utilformatformat-args) for more information.log(function formatProjectDuration(project: Project, format: "short" | "full"): string
formatProjectDuration(project, "short")); // "3 days"
const project: { name: string; startDate: Date; endDate: Date | undefined; } & CoMap