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: co.Map<{ name: z.z.ZodString; startDate: z.z.ZodDate; status: z.z.ZodLiteral<"planning" | "active" | "completed">; coordinator: co.Optional<co.Map<{ name: z.z.ZodString; }, unknown, Account | Group>>; }, unknown, Account | Group>
import co
co.map({
map<{ name: z.z.ZodString; startDate: z.z.ZodDate; status: z.z.ZodLiteral<"planning" | "active" | "completed">; coordinator: co.Optional<co.Map<{ name: z.z.ZodString; }, unknown, Account | Group>>; }>(shape: { ...; }): co.Map<...> export map
name: z.z.ZodString
name:import z
z.string(),
function string(params?: string | z.z.core.$ZodStringParams): z.z.ZodString (+1 overload) 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<readonly ["planning", "active", "completed"]>(value: readonly ["planning", "active", "completed"], params?: string | z.z.core.$ZodLiteralParams): z.z.ZodLiteral<"planning" | "active" | "completed"> (+1 overload) export literal
coordinator:
coordinator: co.Optional<co.Map<{ name: z.z.ZodString; }, unknown, Account | Group>>
import co
co.optional(
optional<co.Map<{ name: z.z.ZodString; }, unknown, Account | Group>>(schema: co.Map<{ name: z.z.ZodString; }, unknown, Account | Group>): co.Optional<co.Map<{ name: z.z.ZodString; }, unknown, Account | Group>> export optional
Member), }); export type
const Member: co.Map<{ name: z.z.ZodString; }, unknown, Account | Group>
Project =
type Project = { readonly name: string; readonly startDate: Date; readonly status: "planning" | "active" | "completed"; readonly coordinator: ({ readonly name: string; } & CoMap) | null | undefined; } & CoMap
import co
co.loaded<typeof
type loaded<T extends CoValueClassOrSchema, R extends ResolveQuery<T> = true> = R extends boolean | undefined ? NonNullable<InstanceOfSchemaCoValuesNullable<T>> : [NonNullable<InstanceOfSchemaCoValuesNullable<T>>] extends [...] ? Exclude<...> extends CoValue ? R extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean ... export loaded
Project>; export type
const Project: co.Map<{ name: z.z.ZodString; startDate: z.z.ZodDate; status: z.z.ZodLiteral<"planning" | "active" | "completed">; coordinator: co.Optional<co.Map<{ name: z.z.ZodString; }, unknown, Account | Group>>; }, unknown, Account | Group>
ProjectInitShape =
type ProjectInitShape = { name: string; startDate: Date; status: "planning" | "active" | "completed"; coordinator?: ({ readonly name: string; } & CoMap) | { name: string; } | undefined; }
import co
co.
type input<S extends CoValueClass | AnyZodOrCoValueSchema> = S extends CoreCoValueSchema ? Exclude<CoFieldSchemaInit<S>, NonNullable<InstanceOfSchemaCoValuesNullable<S>>> : CoFieldSchemaInit<...> export input
The convenience type for extracting the init type of a CoValue schema.input<typeofProject>; // type accepted by `Project.create`
const Project: co.Map<{ name: z.z.ZodString; startDate: z.z.ZodDate; status: z.z.ZodLiteral<"planning" | "active" | "completed">; coordinator: co.Optional<co.Map<{ name: z.z.ZodString; }, unknown, Account | Group>>; }, unknown, Account | Group>
You can create either struct-like CoMaps with fixed fields (as above) or record-like CoMaps for key-value pairs:
const
const Inventory: co.Record<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): co.Record<z.z.ZodString, z.z.ZodNumber> export record
import z
z.string(),
function string(params?: string | z.z.core.$ZodStringParams): z.z.ZodString (+1 overload) 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: { readonly name: string; readonly startDate: Date; readonly status: "planning" | "active" | "completed"; readonly coordinator: ({ readonly name: string; } & CoMap) | undefined; } & CoMap
Project.
const Project: co.Map<{ name: z.z.ZodString; startDate: z.z.ZodDate; status: z.z.ZodLiteral<"planning" | "active" | "completed">; coordinator: co.Optional<co.Map<{ name: z.z.ZodString; }, unknown, Account | Group>>; }, unknown, Account | Group>
create({
CoMapSchema<{ name: ZodString; startDate: ZodDate; status: ZodLiteral<"planning" | "active" | "completed">; coordinator: CoOptionalSchema<CoMapSchema<{ name: ZodString; }, unknown, Account | Group>>; }, unknown, Account | Group>.create(init: { name: string; startDate: Date; status: "planning" | "active" | "completed"; coordinator?: ({ readonly name: string; } & CoMap) | { name: string; } | undefined; }, options?: { owner?: Group; unique?: CoValueUniqueness["uniqueness"]; } | Group): { ...; } & CoMap (+1 overload)
name: string
name: "Spring Planting",startDate: Date
startDate: newDate("2025-03-15"),
var Date: DateConstructor new (value: number | string | Date) => Date (+4 overloads)
status: "planning" | "active" | "completed"
status: "planning", }); constconst inventory: CoRecordInstanceShape<z.z.ZodString, z.z.ZodNumber> & CoMap
inventory =const Inventory: co.Record<z.z.ZodString, z.z.ZodNumber>
Inventory.create({
CoRecordSchema<ZodString, ZodNumber>.create(init: { [x: string]: number; }, options?: { owner: Group; unique?: CoValueUniqueness["uniqueness"]; } | Group): CoRecordInstanceShape<...> & CoMap (+1 overload)
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: { readonly name: string; readonly startDate: Date; readonly status: "planning" | "active" | "completed"; readonly coordinator: ({ readonly name: string; } & CoMap) | undefined; } & CoMap
Project.
const Project: co.Map<{ name: z.z.ZodString; startDate: z.z.ZodDate; status: z.z.ZodLiteral<"planning" | "active" | "completed">; coordinator: co.Optional<co.Map<{ name: z.z.ZodString; }, unknown, Account | Group>>; }, unknown, Account | Group>
create({
CoMapSchema<{ name: ZodString; startDate: ZodDate; status: ZodLiteral<"planning" | "active" | "completed">; coordinator: CoOptionalSchema<CoMapSchema<{ name: ZodString; }, unknown, Account | Group>>; }, unknown, Account | Group>.create(init: { name: string; startDate: Date; status: "planning" | "active" | "completed"; coordinator?: ({ readonly name: string; } & CoMap) | { name: string; } | undefined; }, options?: { owner?: Group; unique?: CoValueUniqueness["uniqueness"]; } | Group): { ...; } & CoMap (+1 overload)
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: "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 (+3 overloads)
addMember(memberAccount, "writer"); const
const memberAccount: Account | ({ readonly [x: string]: any; } & Account)
communityProject =
const communityProject: { readonly name: string; readonly startDate: Date; readonly status: "planning" | "active" | "completed"; readonly coordinator: ({ readonly name: string; } & CoMap) | undefined; } & CoMap
Project.
const Project: co.Map<{ name: z.z.ZodString; startDate: z.z.ZodDate; status: z.z.ZodLiteral<"planning" | "active" | "completed">; coordinator: co.Optional<co.Map<{ name: z.z.ZodString; }, unknown, Account | Group>>; }, unknown, Account | Group>
create( {
CoMapSchema<{ name: ZodString; startDate: ZodDate; status: ZodLiteral<"planning" | "active" | "completed">; coordinator: CoOptionalSchema<CoMapSchema<{ name: ZodString; }, unknown, Account | Group>>; }, unknown, Account | Group>.create(init: { name: string; startDate: Date; status: "planning" | "active" | "completed"; coordinator?: ({ readonly name: string; } & CoMap) | { name: string; } | undefined; }, options?: { owner?: Group; unique?: CoValueUniqueness["uniqueness"]; } | Group): { ...; } & CoMap (+1 overload)
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: "planning" | "active" | "completed"
status: "planning", }, {owner?: Group | undefined
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 (+2 overloads)
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log(project.
const project: { readonly name: string; readonly startDate: Date; readonly status: "planning" | "active" | "completed"; readonly coordinator: ({ readonly 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 (+2 overloads)
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log(project.
const project: { readonly name: string; readonly startDate: Date; readonly status: "planning" | "active" | "completed"; readonly coordinator: ({ readonly name: string; } & CoMap) | undefined; } & CoMap
status: "planning" | "active" | "completed"
status); // "planning"
Handling Optional Fields
Optional fields require checks before access:
if (
project.
const project: { readonly name: string; readonly startDate: Date; readonly status: "planning" | "active" | "completed"; readonly coordinator: ({ readonly name: string; } & CoMap) | undefined; } & CoMap
coordinator) {
coordinator: ({ readonly 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 (+2 overloads)
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log(project.
const project: { readonly name: string; readonly startDate: Date; readonly status: "planning" | "active" | "completed"; readonly coordinator: ({ readonly name: string; } & CoMap) | undefined; } & CoMap
coordinator.
coordinator: { readonly name: string; } & CoMap
name: string
name); // Safe access }
Recursive references
CoMaps can reference themselves recursively:
import {
import co
co,import z
z } from "jazz-tools"; constProject =
const Project: co.Map<{ name: z.z.ZodString; startDate: z.z.ZodDate; status: z.z.ZodLiteral<"planning" | "active" | "completed">; coordinator: co.Optional<co.Map<{ name: z.z.ZodString; }, unknown, Account | Group>>; readonly subProject: co.Optional<...>; }, unknown, Account | Group>
import co
co.map({
map<{ name: z.z.ZodString; startDate: z.z.ZodDate; status: z.z.ZodLiteral<"planning" | "active" | "completed">; coordinator: co.Optional<co.Map<{ name: z.z.ZodString; }, unknown, Account | Group>>; readonly subProject: co.Optional<...>; }>(shape: { ...; }): co.Map<...> export map
name: z.z.ZodString
name:import z
z.string(),
function string(params?: string | z.z.core.$ZodStringParams): z.z.ZodString (+1 overload) 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<readonly ["planning", "active", "completed"]>(value: readonly ["planning", "active", "completed"], params?: string | z.z.core.$ZodLiteralParams): z.z.ZodLiteral<"planning" | "active" | "completed"> (+1 overload) export literal
coordinator:
coordinator: co.Optional<co.Map<{ name: z.z.ZodString; }, unknown, Account | Group>>
import co
co.optional(
optional<co.Map<{ name: z.z.ZodString; }, unknown, Account | Group>>(schema: co.Map<{ name: z.z.ZodString; }, unknown, Account | Group>): co.Optional<co.Map<{ name: z.z.ZodString; }, unknown, Account | Group>> export optional
Member), get
const Member: co.Map<{ name: z.z.ZodString; }, unknown, Account | Group>
subProject() { return
subProject: co.Optional<co.Map<{ name: z.z.ZodString; startDate: z.z.ZodDate; status: z.z.ZodLiteral<"planning" | "active" | "completed">; coordinator: co.Optional<co.Map<{ name: z.z.ZodString; }, unknown, Account | Group>>; readonly subProject: co.Optional<...>; }, unknown, Account | Group>>
Project.
const Project: co.Map<{ name: z.z.ZodString; startDate: z.z.ZodDate; status: z.z.ZodLiteral<"planning" | "active" | "completed">; coordinator: co.Optional<co.Map<{ name: z.z.ZodString; }, unknown, Account | Group>>; readonly subProject: co.Optional<...>; }, unknown, Account | Group>
optional(); } }); export type
CoMapSchema<{ name: ZodString; startDate: ZodDate; status: ZodLiteral<"planning" | "active" | "completed">; coordinator: CoOptionalSchema<CoMapSchema<{ name: ZodString; }, unknown, Account | Group>>; readonly subProject: CoOptionalSchema<...>; }, unknown, Account | Group>.optional(): co.Optional<co.Map<{ name: z.z.ZodString; startDate: z.z.ZodDate; status: z.z.ZodLiteral<"planning" | "active" | "completed">; coordinator: co.Optional<co.Map<{ name: z.z.ZodString; }, unknown, Account | Group>>; readonly subProject: co.Optional<...>; }, unknown, Account | Group>>
Project =
type Project = { readonly name: string; readonly startDate: Date; readonly status: "planning" | "active" | "completed"; readonly coordinator: ({ readonly name: string; } & CoMap) | null | undefined; readonly subProject: (... & CoMap) | ... 1 more ... | undefined; } & CoMap
import co
co.loaded<typeof
type loaded<T extends CoValueClassOrSchema, R extends ResolveQuery<T> = true> = R extends boolean | undefined ? NonNullable<InstanceOfSchemaCoValuesNullable<T>> : [NonNullable<InstanceOfSchemaCoValuesNullable<T>>] extends [...] ? Exclude<...> extends CoValue ? R extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean ... export loaded
Project>;
const Project: co.Map<{ name: z.z.ZodString; startDate: z.z.ZodDate; status: z.z.ZodLiteral<"planning" | "active" | "completed">; coordinator: co.Optional<co.Map<{ name: z.z.ZodString; }, unknown, Account | Group>>; readonly subProject: co.Optional<...>; }, unknown, Account | Group>
When the recursive references involve more complex types, it is sometimes required to specify the getter return type:
import {
import co
co,import z
z } from "jazz-tools"; constProject =
const Project: co.Map<{ name: z.z.ZodString; startDate: z.z.ZodDate; status: z.z.ZodLiteral<"planning" | "active" | "completed">; coordinator: co.Optional<co.Map<{ name: z.z.ZodString; }, unknown, Account | Group>>; readonly subProjects: co.Optional<...>; }, unknown, Account | Group>
import co
co.map({
map<{ name: z.z.ZodString; startDate: z.z.ZodDate; status: z.z.ZodLiteral<"planning" | "active" | "completed">; coordinator: co.Optional<co.Map<{ name: z.z.ZodString; }, unknown, Account | Group>>; readonly subProjects: co.Optional<...>; }>(shape: { ...; }): co.Map<...> export map
name: z.z.ZodString
name:import z
z.string(),
function string(params?: string | z.z.core.$ZodStringParams): z.z.ZodString (+1 overload) 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<readonly ["planning", "active", "completed"]>(value: readonly ["planning", "active", "completed"], params?: string | z.z.core.$ZodLiteralParams): z.z.ZodLiteral<"planning" | "active" | "completed"> (+1 overload) export literal
coordinator:
coordinator: co.Optional<co.Map<{ name: z.z.ZodString; }, unknown, Account | Group>>
import co
co.optional(
optional<co.Map<{ name: z.z.ZodString; }, unknown, Account | Group>>(schema: co.Map<{ name: z.z.ZodString; }, unknown, Account | Group>): co.Optional<co.Map<{ name: z.z.ZodString; }, unknown, Account | Group>> export optional
Member), get
const Member: co.Map<{ name: z.z.ZodString; }, unknown, Account | Group>
subProjects():
subProjects: co.Optional<co.List<co.Map<{ name: z.z.ZodString; startDate: z.z.ZodDate; status: z.z.ZodLiteral<"planning" | "active" | "completed">; coordinator: co.Optional<co.Map<{ name: z.z.ZodString; }, unknown, Account | Group>>; readonly subProjects: co.Optional<...>; }, unknown, Account | Group>>>
import co
co.Optional<
class Optional<Shape extends CoreCoValueSchema = CoreCoValueSchema> export Optional
import co
co.List<typeof
class List<T extends AnyZodOrCoValueSchema> export List
Project>> { return
const Project: co.Map<{ name: z.z.ZodString; startDate: z.z.ZodDate; status: z.z.ZodLiteral<"planning" | "active" | "completed">; coordinator: co.Optional<co.Map<{ name: z.z.ZodString; }, unknown, Account | Group>>; readonly subProjects: co.Optional<...>; }, unknown, Account | Group>
import co
co.optional(
optional<co.List<co.Map<{ name: z.z.ZodString; startDate: z.z.ZodDate; status: z.z.ZodLiteral<"planning" | "active" | "completed">; coordinator: co.Optional<co.Map<{ name: z.z.ZodString; }, unknown, Account | Group>>; readonly subProjects: co.Optional<...>; }, unknown, Account | Group>>>(schema: co.List<...>): co.Optional<...> export optional
import co
co.list(
list<co.Map<{ name: z.z.ZodString; startDate: z.z.ZodDate; status: z.z.ZodLiteral<"planning" | "active" | "completed">; coordinator: co.Optional<co.Map<{ name: z.z.ZodString; }, unknown, Account | Group>>; readonly subProjects: co.Optional<...>; }, unknown, Account | Group>>(element: co.Map<...>): co.List<...> export list
Project)); } }); export type
const Project: co.Map<{ name: z.z.ZodString; startDate: z.z.ZodDate; status: z.z.ZodLiteral<"planning" | "active" | "completed">; coordinator: co.Optional<co.Map<{ name: z.z.ZodString; }, unknown, Account | Group>>; readonly subProjects: co.Optional<...>; }, unknown, Account | Group>
Project =
type Project = { readonly name: string; readonly startDate: Date; readonly status: "planning" | "active" | "completed"; readonly coordinator: ({ readonly name: string; } & CoMap) | null | undefined; readonly subProjects: CoList<...> | ... 1 more ... | undefined; } & CoMap
import co
co.loaded<typeof
type loaded<T extends CoValueClassOrSchema, R extends ResolveQuery<T> = true> = R extends boolean | undefined ? NonNullable<InstanceOfSchemaCoValuesNullable<T>> : [NonNullable<InstanceOfSchemaCoValuesNullable<T>>] extends [...] ? Exclude<...> extends CoValue ? R extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean ... export loaded
Project>;
const Project: co.Map<{ name: z.z.ZodString; startDate: z.z.ZodDate; status: z.z.ZodLiteral<"planning" | "active" | "completed">; coordinator: co.Optional<co.Map<{ name: z.z.ZodString; }, unknown, Account | Group>>; readonly subProjects: co.Optional<...>; }, unknown, Account | Group>
Partial
For convenience Jazz provies a dedicated API for making all the properties of a CoMap optional:
import {
import co
co,import z
z } from "jazz-tools"; constProject =
const Project: co.Map<{ name: z.z.ZodString; startDate: z.z.ZodDate; status: z.z.ZodLiteral<"planning" | "active" | "completed">; }, unknown, Account | Group>
import co
co.map({
map<{ name: z.z.ZodString; startDate: z.z.ZodDate; status: z.z.ZodLiteral<"planning" | "active" | "completed">; }>(shape: { name: z.z.ZodString; startDate: z.z.ZodDate; status: z.z.ZodLiteral<...>; }): co.Map<...> export map
name: z.z.ZodString
name:import z
z.string(),
function string(params?: string | z.z.core.$ZodStringParams): z.z.ZodString (+1 overload) 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"]), }); const
literal<readonly ["planning", "active", "completed"]>(value: readonly ["planning", "active", "completed"], params?: string | z.z.core.$ZodLiteralParams): z.z.ZodLiteral<"planning" | "active" | "completed"> (+1 overload) export literal
ProjectDraft =
const ProjectDraft: co.Map<{ name: z.ZodOptional<z.z.ZodString>; startDate: z.ZodOptional<z.z.ZodDate>; status: z.ZodOptional<z.z.ZodLiteral<"planning" | "active" | "completed">>; }, unknown, Account | Group>
Project.
const Project: co.Map<{ name: z.z.ZodString; startDate: z.z.ZodDate; status: z.z.ZodLiteral<"planning" | "active" | "completed">; }, unknown, Account | Group>
CoMapSchema<{ name: ZodString; startDate: ZodDate; status: ZodLiteral<"planning" | "active" | "completed">; }, unknown, Account | Group>.partial(): co.Map<{ name: z.ZodOptional<z.z.ZodString>; startDate: z.ZodOptional<z.z.ZodDate>; status: z.ZodOptional<z.z.ZodLiteral<"planning" | "active" | "completed">>; }, unknown, Account | Group>
Creates a new CoMap schema by making all fields optional.partial(); // The fields are all optional now constproject =
const project: { readonly name: string | undefined; readonly startDate: Date | undefined; readonly status: "planning" | "active" | "completed" | undefined; } & CoMap
ProjectDraft.
const ProjectDraft: co.Map<{ name: z.ZodOptional<z.z.ZodString>; startDate: z.ZodOptional<z.z.ZodDate>; status: z.ZodOptional<z.z.ZodLiteral<"planning" | "active" | "completed">>; }, unknown, Account | Group>
create({});
CoMapSchema<{ name: ZodOptional<ZodString>; startDate: ZodOptional<ZodDate>; status: ZodOptional<ZodLiteral<"planning" | "active" | "completed">>; }, unknown, Account | Group>.create(init: { name?: string | undefined; startDate?: Date | undefined; status?: "planning" | "active" | "completed" | undefined; }, options?: { owner?: Group; unique?: CoValueUniqueness["uniqueness"]; } | Group): { ...; } & CoMap (+1 overload)
Pick
You can also pick specific fields from a CoMap:
import {
import co
co,import z
z } from "jazz-tools"; constProject =
const Project: co.Map<{ name: z.z.ZodString; startDate: z.z.ZodDate; status: z.z.ZodLiteral<"planning" | "active" | "completed">; }, unknown, Account | Group>
import co
co.map({
map<{ name: z.z.ZodString; startDate: z.z.ZodDate; status: z.z.ZodLiteral<"planning" | "active" | "completed">; }>(shape: { name: z.z.ZodString; startDate: z.z.ZodDate; status: z.z.ZodLiteral<...>; }): co.Map<...> export map
name: z.z.ZodString
name:import z
z.string(),
function string(params?: string | z.z.core.$ZodStringParams): z.z.ZodString (+1 overload) 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"]), }); const
literal<readonly ["planning", "active", "completed"]>(value: readonly ["planning", "active", "completed"], params?: string | z.z.core.$ZodLiteralParams): z.z.ZodLiteral<"planning" | "active" | "completed"> (+1 overload) export literal
ProjectStep1 =
const ProjectStep1: co.Map<{ name: z.z.ZodString; startDate: z.z.ZodDate; }, unknown, Account | Group>
Project.
const Project: co.Map<{ name: z.z.ZodString; startDate: z.z.ZodDate; status: z.z.ZodLiteral<"planning" | "active" | "completed">; }, unknown, Account | Group>
CoMapSchema<{ name: ZodString; startDate: ZodDate; status: ZodLiteral<"planning" | "active" | "completed">; }, unknown, Account | Group>.pick<"name" | "startDate">(keys: { name: true; startDate: true; }): co.Map<{ name: z.z.ZodString; startDate: z.z.ZodDate; }, unknown, Account | Group>
Creates a new CoMap schema by picking the specified keys from the original schema.pick({name: true
name: true,startDate: true
startDate: true, }); // We don't provide the status field constproject =
const project: { readonly name: string; readonly startDate: Date; } & CoMap
ProjectStep1.
const ProjectStep1: co.Map<{ name: z.z.ZodString; startDate: z.z.ZodDate; }, unknown, Account | Group>
create({
CoMapSchema<{ name: ZodString; startDate: ZodDate; }, unknown, Account | Group>.create(init: { name: string; startDate: Date; }, options?: { owner?: Group; unique?: CoValueUniqueness["uniqueness"]; } | Group): { ...; } & CoMap (+1 overload)
name: string
name: "My project",startDate: Date
startDate: newDate("2025-04-01"), });
var Date: DateConstructor new (value: number | string | Date) => Date (+4 overloads)
Working with Record CoMaps
For record-type CoMaps, you can access values using bracket notation:
const
const inventory: CoRecordInstanceShape<z.z.ZodString, z.z.ZodNumber> & CoMap
inventory =const Inventory: co.Record<z.z.ZodString, z.z.ZodNumber>
Inventory.create({
CoRecordSchema<ZodString, ZodNumber>.create(init: { [x: string]: number; }, options?: { owner: Group; unique?: CoValueUniqueness["uniqueness"]; } | Group): CoRecordInstanceShape<...> & CoMap (+1 overload)
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 (+2 overloads)
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log(const inventory: CoRecordInstanceShape<z.z.ZodString, z.z.ZodNumber> & CoMap
inventory["tomatoes"]); // 48
Updating CoMaps
To update a CoMap's properties, use the $jazz.set
method:
project.
const project: { readonly name: string; readonly startDate: Date; readonly status: "planning" | "active" | "completed"; readonly coordinator: ({ readonly name: string; } & CoMap) | undefined; } & CoMap
CoMap.$jazz: CoMapJazzApi<{ readonly name: string; readonly startDate: Date; readonly status: "planning" | "active" | "completed"; readonly coordinator: ({ readonly name: string; } & CoMap) | undefined; } & CoMap>
Jazz methods for CoMaps are inside this property. This allows CoMaps to be used as plain objects while still having access to Jazz methods, and also doesn't limit which key names can be used inside CoMaps.$jazz.CoMapJazzApi<{ readonly name: string; readonly startDate: Date; readonly status: "planning" | "active" | "completed"; readonly coordinator: ({ readonly name: string; } & CoMap) | undefined; } & CoMap>.set<"name">(key: "name", value: string): void
Set a value on the CoMapset("name", "Spring Vegetable Garden"); // Update nameproject.
const project: { readonly name: string; readonly startDate: Date; readonly status: "planning" | "active" | "completed"; readonly coordinator: ({ readonly name: string; } & CoMap) | undefined; } & CoMap
CoMap.$jazz: CoMapJazzApi<{ readonly name: string; readonly startDate: Date; readonly status: "planning" | "active" | "completed"; readonly coordinator: ({ readonly name: string; } & CoMap) | undefined; } & CoMap>
Jazz methods for CoMaps are inside this property. This allows CoMaps to be used as plain objects while still having access to Jazz methods, and also doesn't limit which key names can be used inside CoMaps.$jazz.CoMapJazzApi<{ readonly name: string; readonly startDate: Date; readonly status: "planning" | "active" | "completed"; readonly coordinator: ({ readonly name: string; } & CoMap) | undefined; } & CoMap>.set<"startDate">(key: "startDate", value: Date): void
Set a value on the CoMapset("startDate", newDate("2025-03-20")); // Update date
var Date: DateConstructor new (value: number | string | Date) => Date (+4 overloads)
The $jazz
namespace is available on all CoValues, and provides access to methods to modify and load CoValues,
as well as access common properties like id
and owner
.
When updating references to other CoValues, you can provide both the new CoValue or a JSON object from which the new CoValue will be created.
const
Dog =
const Dog: co.Map<{ name: co.PlainText; }, unknown, Account | Group>
import co
co.map({
map<{ name: co.PlainText; }>(shape: { name: co.PlainText; }): co.Map<{ name: co.PlainText; }, unknown, Account | Group> export map
name: co.PlainText
name:import co
co.plainText(), }); const
function plainText(): co.PlainText export plainText
Person =
const Person: co.Map<{ name: co.PlainText; dog: co.Map<{ name: co.PlainText; }, unknown, Account | Group>; }, unknown, Account | Group>
import co
co.map({
map<{ name: co.PlainText; dog: co.Map<{ name: co.PlainText; }, unknown, Account | Group>; }>(shape: { name: co.PlainText; dog: co.Map<{ name: co.PlainText; }, unknown, Account | Group>; }): co.Map<...> export map
name: co.PlainText
name:import co
co.plainText(),
function plainText(): co.PlainText export plainText
dog:
dog: co.Map<{ name: co.PlainText; }, unknown, Account | Group>
Dog, }) const
const Dog: co.Map<{ name: co.PlainText; }, unknown, Account | Group>
person =
const person: { readonly name: CoPlainText; readonly dog: { readonly name: CoPlainText; } & CoMap; } & CoMap
Person.
const Person: co.Map<{ name: co.PlainText; dog: co.Map<{ name: co.PlainText; }, unknown, Account | Group>; }, unknown, Account | Group>
create({
CoMapSchema<{ name: PlainTextSchema; dog: CoMapSchema<{ name: PlainTextSchema; }, unknown, Account | Group>; }, unknown, Account | Group>.create(init: { name: string | CoPlainText; dog: ({ readonly name: CoPlainText | null; } & CoMap) | { name: string | CoPlainText; }; }, options?: { owner?: Group; unique?: CoValueUniqueness["uniqueness"]; } | Group): { ...; } & CoMap (+1 overload)
name: string | CoPlainText
name: "John",dog: {
dog: ({ readonly name: CoPlainText | null; } & CoMap) | { name: string | CoPlainText; }
name: string
name: "Rex" }, }); // Update the dog field using a CoValueperson.
const person: { readonly name: CoPlainText; readonly dog: { readonly name: CoPlainText; } & CoMap; } & CoMap
CoMap.$jazz: CoMapJazzApi<{ readonly name: CoPlainText; readonly dog: { readonly name: CoPlainText; } & CoMap; } & CoMap>
Jazz methods for CoMaps are inside this property. This allows CoMaps to be used as plain objects while still having access to Jazz methods, and also doesn't limit which key names can be used inside CoMaps.$jazz.
CoMapJazzApi<{ readonly name: CoPlainText; readonly dog: { readonly name: CoPlainText; } & CoMap; } & CoMap>.set<"dog">(key: "dog", value: ({ readonly name: CoPlainText; } & CoMap) | CoMapInit<{ readonly name: CoPlainText; } & CoMap>): void
Set a value on the CoMapset('dog',Dog.
const Dog: co.Map<{ name: co.PlainText; }, unknown, Account | Group>
create({
CoMapSchema<{ name: PlainTextSchema; }, unknown, Account | Group>.create(init: { name: string | CoPlainText; }, options?: { owner?: Group; unique?: CoValueUniqueness["uniqueness"]; } | Group): { ...; } & CoMap (+1 overload)
name: string | CoPlainText
name:import co
co.plainText().
function plainText(): co.PlainText export plainText
create("Fido") })); // Or use a plain JSON object
PlainTextSchema.create(text: string, options?: { owner: Group; } | Group): CoPlainText (+1 overload)
person.
const person: { readonly name: CoPlainText; readonly dog: { readonly name: CoPlainText; } & CoMap; } & CoMap
CoMap.$jazz: CoMapJazzApi<{ readonly name: CoPlainText; readonly dog: { readonly name: CoPlainText; } & CoMap; } & CoMap>
Jazz methods for CoMaps are inside this property. This allows CoMaps to be used as plain objects while still having access to Jazz methods, and also doesn't limit which key names can be used inside CoMaps.$jazz.
CoMapJazzApi<{ readonly name: CoPlainText; readonly dog: { readonly name: CoPlainText; } & CoMap; } & CoMap>.set<"dog">(key: "dog", value: ({ readonly name: CoPlainText; } & CoMap) | CoMapInit<{ readonly name: CoPlainText; } & CoMap>): void
Set a value on the CoMapset("dog", {name: string
name: "Fido" });
When providing a JSON object, Jazz will automatically create the CoValues for you. To learn more about how permissions work in this case, refer to Ownership on implicit CoValue creation.
Type Safety
CoMaps are fully typed in TypeScript, giving you autocomplete and error checking:
project.
const project: { readonly name: string; readonly startDate: Date; readonly status: "planning" | "active" | "completed"; readonly coordinator: ({ readonly name: string; } & CoMap) | undefined; } & CoMap
CoMap.$jazz: CoMapJazzApi<{ readonly name: string; readonly startDate: Date; readonly status: "planning" | "active" | "completed"; readonly coordinator: ({ readonly name: string; } & CoMap) | undefined; } & CoMap>
Jazz methods for CoMaps are inside this property. This allows CoMaps to be used as plain objects while still having access to Jazz methods, and also doesn't limit which key names can be used inside CoMaps.$jazz.CoMapJazzApi<{ readonly name: string; readonly startDate: Date; readonly status: "planning" | "active" | "completed"; readonly coordinator: ({ readonly name: string; } & CoMap) | undefined; } & CoMap>.set<"name">(key: "name", value: string): void
Set a value on the CoMapset("name", "Spring Vegetable Planting"); // ✓ Valid stringproject.
const project: { readonly name: string; readonly startDate: Date; readonly status: "planning" | "active" | "completed"; readonly coordinator: ({ readonly name: string; } & CoMap) | undefined; } & CoMap
CoMap.$jazz: CoMapJazzApi<{ readonly name: string; readonly startDate: Date; readonly status: "planning" | "active" | "completed"; readonly coordinator: ({ readonly name: string; } & CoMap) | undefined; } & CoMap>
Jazz methods for CoMaps are inside this property. This allows CoMaps to be used as plain objects while still having access to Jazz methods, and also doesn't limit which key names can be used inside CoMaps.$jazz.CoMapJazzApi<{ readonly name: string; readonly startDate: Date; readonly status: "planning" | "active" | "completed"; readonly coordinator: ({ readonly name: string; } & CoMap) | undefined; } & CoMap>.set<"startDate">(key: "startDate", value: Date): void
Set a value on the CoMapset("startDate", "2025-03-15"); // ✗ Type error: expected Date
Soft Deletion
Implementing a soft deletion pattern by using a deleted
flag allows you to maintain data for potential recovery and auditing.
const
Project =
const Project: co.Map<{ name: z.z.ZodString; deleted: z.ZodOptional<z.z.ZodBoolean>; }, unknown, Account | Group>
import co
co.map({
map<{ name: z.z.ZodString; deleted: z.ZodOptional<z.z.ZodBoolean>; }>(shape: { name: z.z.ZodString; deleted: z.ZodOptional<z.z.ZodBoolean>; }): co.Map<{ name: z.z.ZodString; deleted: z.ZodOptional<z.z.ZodBoolean>; }, unknown, Account | Group> export map
name: z.z.ZodString
name:import z
z.string(),
function string(params?: string | z.z.core.$ZodStringParams): z.z.ZodString (+1 overload) export string
deleted: z.ZodOptional<z.z.ZodBoolean>
deleted:import z
z.optional(
optional<z.z.ZodBoolean>(innerType: z.z.ZodBoolean): z.ZodOptional<z.z.ZodBoolean> export optional
import z
z.boolean()), });
function boolean(params?: string | z.z.core.$ZodBooleanParams): z.z.ZodBoolean export boolean
When an object needs to be "deleted", instead of removing it from the system, the deleted flag is set to true. This gives us a property to omit it in the future.
Deleting Properties
You can delete properties from CoMaps:
const inventory: CoRecordInstanceShape<z.z.ZodString, z.z.ZodNumber> & CoMap
inventory.CoMap.$jazz: CoMapJazzApi<CoRecordInstanceShape<z.z.ZodString, z.z.ZodNumber> & CoMap>
Jazz methods for CoMaps are inside this property. This allows CoMaps to be used as plain objects while still having access to Jazz methods, and also doesn't limit which key names can be used inside CoMaps.$jazz.CoMapJazzApi<CoRecordInstanceShape<ZodString, ZodNumber> & CoMap>.delete(key: string): void
Delete a value from a CoMap. For record-like CoMaps (created with `co.record`), any string key can be deleted. For struct-like CoMaps (created with `co.map`), only optional properties can be deleted.delete("basil"); // Remove a key-value pair // For optional fields in struct-like CoMapsproject.
const project: { readonly name: string; readonly startDate: Date; readonly status: "planning" | "active" | "completed"; readonly coordinator: ({ readonly name: string; } & CoMap) | undefined; } & CoMap
CoMap.$jazz: CoMapJazzApi<{ readonly name: string; readonly startDate: Date; readonly status: "planning" | "active" | "completed"; readonly coordinator: ({ readonly name: string; } & CoMap) | undefined; } & CoMap>
Jazz methods for CoMaps are inside this property. This allows CoMaps to be used as plain objects while still having access to Jazz methods, and also doesn't limit which key names can be used inside CoMaps.$jazz.
CoMapJazzApi<{ readonly name: string; readonly startDate: Date; readonly status: "planning" | "active" | "completed"; readonly coordinator: ({ readonly name: string; } & CoMap) | undefined; } & CoMap>.set<"coordinator">(key: "coordinator", value: ({ readonly name: string; } & CoMap) | CoMapInit<{ readonly name: string; } & CoMap> | undefined): void
Set a value on the CoMapset("coordinator",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: co.Map<{ done: z.z.ZodBoolean; text: co.PlainText; version: z.z.ZodLiteral<1 | 2>; priority: z.z.ZodEnum<{ low: "low"; medium: "medium"; high: "high"; }>; }, unknown, Account | Group>
import co
co .map({
map<{ done: z.z.ZodBoolean; text: co.PlainText; version: z.z.ZodLiteral<1 | 2>; priority: z.z.ZodEnum<{ low: "low"; medium: "medium"; high: "high"; }>; }>(shape: { done: z.z.ZodBoolean; text: co.PlainText; version: z.z.ZodLiteral<...>; priority: z.z.ZodEnum<...>; }): co.Map<...> 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: co.PlainText
text:import co
co.plainText(),
function plainText(): co.PlainText export plainText
version: z.z.ZodLiteral<1 | 2>
version:import z
z.literal([1, 2]),
literal<readonly [1, 2]>(value: readonly [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((
CoMapSchema<{ done: ZodBoolean; text: PlainTextSchema; version: ZodLiteral<1 | 2>; priority: ZodEnum<{ low: "low"; medium: "medium"; high: "high"; }>; }, unknown, Account | Group>.withMigration(migration: (value: { readonly done: boolean; readonly text: CoPlainText | null; readonly version: 1 | 2; readonly priority: "low" | "medium" | "high"; } & CoMap) => undefined): co.Map<...>
task) => { if (
task: { readonly done: boolean; readonly text: CoPlainText | null; readonly version: 1 | 2; readonly priority: "low" | "medium" | "high"; } & CoMap
task.
task: { readonly done: boolean; readonly text: CoPlainText | null; readonly version: 1 | 2; readonly priority: "low" | "medium" | "high"; } & CoMap
version: 1 | 2
version === 1) {task.
task: { readonly done: boolean; readonly text: CoPlainText | null; readonly version: 1 | 2; readonly priority: "low" | "medium" | "high"; } & CoMap
CoMap.$jazz: CoMapJazzApi<{ readonly done: boolean; readonly text: CoPlainText | null; readonly version: 1 | 2; readonly priority: "low" | "medium" | "high"; } & CoMap>
Jazz methods for CoMaps are inside this property. This allows CoMaps to be used as plain objects while still having access to Jazz methods, and also doesn't limit which key names can be used inside CoMaps.$jazz.CoMapJazzApi<{ readonly done: boolean; readonly text: CoPlainText | null; readonly version: 1 | 2; readonly priority: "low" | "medium" | "high"; } & CoMap>.set<"priority">(key: "priority", value: "low" | "medium" | "high"): void
Set a value on the CoMapset("priority", "medium"); // Upgrade the version so the migration won't run againtask.
task: { readonly done: boolean; readonly text: CoPlainText | null; readonly version: 1 | 2; readonly priority: "low" | "medium" | "high"; } & CoMap
CoMap.$jazz: CoMapJazzApi<{ readonly done: boolean; readonly text: CoPlainText | null; readonly version: 1 | 2; readonly priority: "low" | "medium" | "high"; } & CoMap>
Jazz methods for CoMaps are inside this property. This allows CoMaps to be used as plain objects while still having access to Jazz methods, and also doesn't limit which key names can be used inside CoMaps.$jazz.CoMapJazzApi<{ readonly done: boolean; readonly text: CoPlainText | null; readonly version: 1 | 2; readonly priority: "low" | "medium" | "high"; } & CoMap>.set<"version">(key: "version", value: 1 | 2): void
Set a value on the CoMapset("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: co.Map<{ version: z.z.ZodLiteral<1>; done: z.z.ZodBoolean; text: z.z.ZodString; }, unknown, Account | Group>
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; }): co.Map<{ version: z.z.ZodLiteral<1>; done: z.z.ZodBoolean; text: z.z.ZodString; }, unknown, Account | Group> 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 (+1 overload) export string
TaskV2 =
const TaskV2: co.Map<{ version: z.z.ZodLiteral<2>; done: z.z.ZodBoolean; text: z.z.ZodString; priority: z.z.ZodEnum<{ low: "low"; medium: "medium"; high: "high"; }>; }, unknown, 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<...>; }): co.Map<...> 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 (+1 overload) 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((
CoMapSchema<{ version: ZodLiteral<2>; done: ZodBoolean; text: ZodString; priority: ZodEnum<{ low: "low"; medium: "medium"; high: "high"; }>; }, unknown, Account | Group>.withMigration(migration: (value: { readonly version: 2; readonly done: boolean; readonly text: string; readonly priority: "low" | "medium" | "high"; } & CoMap) => undefined): co.Map<{ version: z.z.ZodLiteral<...>; done: z.z.ZodBoolean; text: z.z.ZodString; priority: z.z.ZodEnum<...>; }, unknown, Account | Group>
task) => { // @ts-expect-error - check if we need to run the migration if (
task: { readonly version: 2; readonly done: boolean; readonly text: string; readonly priority: "low" | "medium" | "high"; } & CoMap
task.
task: { readonly version: 2; readonly done: boolean; readonly text: string; readonly priority: "low" | "medium" | "high"; } & CoMap
version: 2
version === 1) {task.
task: { readonly version: 2; readonly done: boolean; readonly text: string; readonly priority: "low" | "medium" | "high"; } & CoMap
CoMap.$jazz: CoMapJazzApi<{ readonly version: 2; readonly done: boolean; readonly text: string; readonly priority: "low" | "medium" | "high"; } & CoMap>
Jazz methods for CoMaps are inside this property. This allows CoMaps to be used as plain objects while still having access to Jazz methods, and also doesn't limit which key names can be used inside CoMaps.$jazz.CoMapJazzApi<{ readonly version: 2; readonly done: boolean; readonly text: string; readonly priority: "low" | "medium" | "high"; } & CoMap>.set<"version">(key: "version", value: 2): void
Set a value on the CoMapset("version", 2);task.
task: { readonly version: 2; readonly done: boolean; readonly text: string; readonly priority: "low" | "medium" | "high"; } & CoMap
CoMap.$jazz: CoMapJazzApi<{ readonly version: 2; readonly done: boolean; readonly text: string; readonly priority: "low" | "medium" | "high"; } & CoMap>
Jazz methods for CoMaps are inside this property. This allows CoMaps to be used as plain objects while still having access to Jazz methods, and also doesn't limit which key names can be used inside CoMaps.$jazz.CoMapJazzApi<{ readonly version: 2; readonly done: boolean; readonly text: string; readonly priority: "low" | "medium" | "high"; } & CoMap>.set<"priority">(key: "priority", value: "low" | "medium" | "high"): void
Set a value on the CoMapset("priority", "medium"); } }); // Export the discriminated union; because some users might // not be able to run the migration export constTask =
const Task: co.DiscriminatedUnion<[co.Map<{ version: z.z.ZodLiteral<1>; done: z.z.ZodBoolean; text: z.z.ZodString; }, unknown, Account | Group>, co.Map<{ version: z.z.ZodLiteral<2>; done: z.z.ZodBoolean; text: z.z.ZodString; priority: z.z.ZodEnum<{ low: "low"; medium: "medium"; high: "high"; }>; }, unknown, Account | Group>]>
import co
co.discriminatedUnion("version", [
discriminatedUnion<[co.Map<{ version: z.z.ZodLiteral<1>; done: z.z.ZodBoolean; text: z.z.ZodString; }, unknown, Account | Group>, co.Map<{ version: z.z.ZodLiteral<2>; done: z.z.ZodBoolean; text: z.z.ZodString; priority: z.z.ZodEnum<...>; }, unknown, Account | Group>]>(discriminator: string, schemas: [...]): co.DiscriminatedUnion<...> export discriminatedUnion
TaskV1,
const TaskV1: co.Map<{ version: z.z.ZodLiteral<1>; done: z.z.ZodBoolean; text: z.z.ZodString; }, unknown, Account | Group>
TaskV2, ]); export type
const TaskV2: co.Map<{ version: z.z.ZodLiteral<2>; done: z.z.ZodBoolean; text: z.z.ZodString; priority: z.z.ZodEnum<{ low: "low"; medium: "medium"; high: "high"; }>; }, unknown, Account | Group>
Task =
type Task = ({ readonly version: 1; readonly done: boolean; readonly text: string; } & CoMap) | ({ readonly version: 2; readonly done: boolean; readonly text: string; readonly priority: "low" | "medium" | "high"; } & CoMap)
import co
co.loaded<typeof
type loaded<T extends CoValueClassOrSchema, R extends ResolveQuery<T> = true> = R extends boolean | undefined ? NonNullable<InstanceOfSchemaCoValuesNullable<T>> : [NonNullable<InstanceOfSchemaCoValuesNullable<T>>] extends [...] ? Exclude<...> extends CoValue ? R extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean ... export loaded
Task>;
const Task: co.DiscriminatedUnion<[co.Map<{ version: z.z.ZodLiteral<1>; done: z.z.ZodBoolean; text: z.z.ZodString; }, unknown, Account | Group>, co.Map<{ version: z.z.ZodLiteral<2>; done: z.z.ZodBoolean; text: z.z.ZodString; priority: z.z.ZodEnum<{ low: "low"; medium: "medium"; high: "high"; }>; }, unknown, 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: co.Map<{ name: z.z.ZodString; startDate: z.z.ZodDate; endDate: z.ZodOptional<z.z.ZodDate>; }, unknown, Account | Group>
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>; }): co.Map<...> export map
name: z.z.ZodString
name:import z
z.string(),
function string(params?: string | z.z.core.$ZodStringParams): z.z.ZodString (+1 overload) 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 = { readonly name: string; readonly startDate: Date; readonly endDate: Date | undefined; } & CoMap
import co
co.loaded<typeof
type loaded<T extends CoValueClassOrSchema, R extends ResolveQuery<T> = true> = R extends boolean | undefined ? NonNullable<InstanceOfSchemaCoValuesNullable<T>> : [NonNullable<InstanceOfSchemaCoValuesNullable<T>>] extends [...] ? Exclude<...> extends CoValue ? R extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean ... export loaded
Project>; export function
const Project: co.Map<{ name: z.z.ZodString; startDate: z.z.ZodDate; endDate: z.ZodOptional<z.z.ZodDate>; }, unknown, Account | Group>
function isProjectActive(project: Project): boolean
isProjectActive(project:
project: { readonly name: string; readonly startDate: Date; readonly endDate: Date | undefined; } & CoMap
Project) { const
type Project = { readonly name: string; readonly startDate: Date; readonly endDate: Date | undefined; } & CoMap
const now: Date
now = newDate(); return
var Date: DateConstructor new () => Date (+4 overloads)
const now: Date
now >=project.
project: { readonly name: string; readonly startDate: Date; readonly endDate: Date | undefined; } & CoMap
startDate: Date
startDate && (!project.
project: { readonly name: string; readonly startDate: Date; readonly endDate: Date | undefined; } & CoMap
endDate: Date | undefined
endDate ||const now: Date
now <=project.
project: { readonly name: string; readonly startDate: Date; readonly endDate: Date | undefined; } & CoMap
endDate: Date
endDate); } export functionfunction formatProjectDuration(project: Project, format: "short" | "full"): string
formatProjectDuration(project:
project: { readonly name: string; readonly startDate: Date; readonly endDate: Date | undefined; } & CoMap
Project,
type Project = { readonly name: string; readonly startDate: Date; readonly endDate: Date | undefined; } & CoMap
format: "short" | "full"
format: "short" | "full") { constconst start: string
start =project.
project: { readonly name: string; readonly startDate: Date; readonly 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: { readonly name: string; readonly startDate: Date; readonly 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: { readonly name: string; readonly startDate: Date; readonly 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: { readonly name: string; readonly startDate: Date; readonly 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: { readonly name: string; readonly startDate: Date; readonly 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: { readonly name: string; readonly startDate: Date; readonly endDate: Date | undefined; } & CoMap
Project.
const Project: co.Map<{ name: z.z.ZodString; startDate: z.z.ZodDate; endDate: z.ZodOptional<z.z.ZodDate>; }, unknown, Account | Group>
create({
CoMapSchema<{ name: ZodString; startDate: ZodDate; endDate: ZodOptional<ZodDate>; }, unknown, Account | Group>.create(init: { name: string; startDate: Date; endDate?: Date | undefined; }, options?: { owner?: Group; unique?: CoValueUniqueness["uniqueness"]; } | Group): { ...; } & CoMap (+1 overload)
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 (+2 overloads)
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log(function isProjectActive(project: Project): boolean
isProjectActive(project)); // false
const project: { readonly name: string; readonly startDate: Date; readonly 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 (+2 overloads)
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log(function formatProjectDuration(project: Project, format: "short" | "full"): string
formatProjectDuration(project, "short")); // "3 days"
const project: { readonly name: string; readonly startDate: Date; readonly endDate: Date | undefined; } & CoMap
Uniqueness
CoMaps are typically created with a CoValue ID that acts as an opaque UUID, by which you can then load them. However, there are situations where it is preferable to load CoMaps using a custom identifier:
- The CoMaps have user-generated identifiers, such as a slug
- The CoMaps have identifiers referring to equivalent data in an external system
- The CoMaps have human-readable & application-specific identifiers
- If an application has CoValues used by every user, referring to it by a unique well-known name (eg,
"my-global-comap"
) can be more convenient than using a CoValue ID
- If an application has CoValues used by every user, referring to it by a unique well-known name (eg,
Consider a scenario where one wants to identify a CoMap using some unique identifier that isn't the Jazz CoValue ID:
// This will not work as `learning-jazz` is not a CoValue ID const
myTask = await
const myTask: ({ readonly text: string; } & CoMap) | null
Task.
const Task: co.Map<{ text: z.z.ZodString; }, unknown, Account | Group>
load("learning-jazz");
CoMapSchema<{ text: ZodString; }, unknown, Account | Group>.load<true>(id: string, options?: { resolve?: RefsToResolve<{ readonly text: string; } & CoMap, 10, []> | undefined; loadAs?: Account | AnonymousJazzAgent; skipRetry?: boolean; } | undefined): Promise<...>
To make it possible to use human-readable identifiers Jazz lets you to define a unique
property on CoMaps.
Then the CoValue ID is deterministically derived from the unique
property and the owner of the CoMap.
// Given the project owner, myTask will have always the same id const
learnJazzTask = await
const learnJazzTask: { readonly text: string; } & CoMap
Task.
const Task: co.Map<{ text: z.z.ZodString; }, unknown, Account | Group>
create({
CoMapSchema<{ text: ZodString; }, unknown, Account | Group>.create(init: { text: string; }, options?: { owner?: Group; unique?: CoValueUniqueness["uniqueness"]; } | Group): { readonly text: string; } & CoMap (+1 overload)
text: string
text: "Let's learn some Jazz!", }, {unique?: JsonValue | undefined
unique: "learning-jazz",owner?: Group | undefined
owner:project.
const project: { readonly name: string; readonly startDate: Date; readonly endDate: Date | undefined; } & CoMap
CoMap.$jazz: CoMapJazzApi<{ readonly name: string; readonly startDate: Date; readonly endDate: Date | undefined; } & CoMap>
Jazz methods for CoMaps are inside this property. This allows CoMaps to be used as plain objects while still having access to Jazz methods, and also doesn't limit which key names can be used inside CoMaps.$jazz.CoMapJazzApi<{ readonly name: string; readonly startDate: Date; readonly endDate: Date | undefined; } & CoMap>.owner: Group
owner, // Different owner, different id });
Now you can use CoMap.loadUnique
to easily load the CoMap using the human-readable identifier:
const
learnJazzTask = await
const learnJazzTask: ({ readonly text: string; } & CoMap) | null
Task.
const Task: co.Map<{ text: z.z.ZodString; }, unknown, Account | Group>
loadUnique( "learning-jazz",
CoMapSchema<{ text: ZodString; }, unknown, Account | Group>.loadUnique<true>(unique: CoValueUniqueness["uniqueness"], ownerID: string, options?: { resolve?: RefsToResolve<{ readonly text: string; } & CoMap, 10, []> | undefined; loadAs?: Account | AnonymousJazzAgent; } | undefined): Promise<...>
project.
const project: { readonly name: string; readonly startDate: Date; readonly endDate: Date | undefined; } & CoMap
CoMap.$jazz: CoMapJazzApi<{ readonly name: string; readonly startDate: Date; readonly endDate: Date | undefined; } & CoMap>
Jazz methods for CoMaps are inside this property. This allows CoMaps to be used as plain objects while still having access to Jazz methods, and also doesn't limit which key names can be used inside CoMaps.$jazz.CoMapJazzApi<{ readonly name: string; readonly startDate: Date; readonly endDate: Date | undefined; } & CoMap>.owner: Group
owner.Group.$jazz: GroupJazzApi<Group>
$jazz.GroupJazzApi<Group>.id: string
The ID of this `Group`id );
It's also possible to combine the create+load operation using CoMap.upsertUnique
:
const
learnJazzTask = await
const learnJazzTask: ({ readonly text: string; } & CoMap) | null
Task.
const Task: co.Map<{ text: z.z.ZodString; }, unknown, Account | Group>
upsertUnique( {
CoMapSchema<{ text: ZodString; }, unknown, Account | Group>.upsertUnique: <true>(options: { value: { text: string; }; unique: CoValueUniqueness["uniqueness"]; owner: Account | Group; resolve?: RefsToResolve<{ readonly text: string; } & CoMap, 10, []> | undefined; }) => Promise<...>
value: {
value: { text: string; }
text: string
text: "Let's learn some Jazz!", },unique: JsonValue
unique: "learning-jazz",owner: Account | Group
owner:project.
const project: { readonly name: string; readonly startDate: Date; readonly endDate: Date | undefined; } & CoMap
CoMap.$jazz: CoMapJazzApi<{ readonly name: string; readonly startDate: Date; readonly endDate: Date | undefined; } & CoMap>
Jazz methods for CoMaps are inside this property. This allows CoMaps to be used as plain objects while still having access to Jazz methods, and also doesn't limit which key names can be used inside CoMaps.$jazz.CoMapJazzApi<{ readonly name: string; readonly startDate: Date; readonly endDate: Date | undefined; } & CoMap>.owner: Group
owner, } );
Caveats:
-
The
unique
parameter acts as an immutable identifier - i.e. the sameunique
parameter in the sameGroup
will always refer to the same CoValue.- To make dynamic renaming possible, you can create an indirection where a stable CoMap identified by a specific value of
unique
is simply a pointer to another CoMap with a normal, dynamic CoValue ID. This pointer can then be updated as desired by users with the corresponding permissions.
- To make dynamic renaming possible, you can create an indirection where a stable CoMap identified by a specific value of
-
This way of introducing identifiers allows for very fast lookup of individual CoMaps by identifier, but it doesn't let you enumerate all the CoMaps identified this way within a
Group
. If you also need enumeration, consider using a globalco.record()
that maps from identifier to a CoMap, which you then do lookups in (this requires at least a shallow load of the entireco.record()
, but this should be fast for up to 10s of 1000s of entries)