import { type CoValueUniqueness, type JsonValue, type RawCoMap } from "cojson"; import { AnonymousJazzAgent, AsLoaded, LoadedAndRequired, CoFieldInit, CoValue, CoValueClass, Group, ID, Settled, PartialOnUndefined, RefIfCoValue, RefsToResolve, RefsToResolveStrict, Resolved, Schema, Simplify, SubscribeListenerOptions, TypeSym, BranchDefinition, Account, CoValueBase, CoValueJazzApi, inspect, CoreCoMapSchema, CoValueCreateOptionsInternal, CoValueCursor, LoadCoValueCursorOption } from "../internal.js"; import { type LocalValidationMode } from "../implementation/zodSchema/validationSettings.js"; export type CoMapEdit = { value?: V; ref?: RefIfCoValue; by: Account | null; madeAt: Date; key?: string; }; export type LastAndAllCoMapEdits = CoMapEdit & { all: CoMapEdit[]; }; export type CoMapEdits = { [Key in CoKeys]?: LastAndAllCoMapEdits; }; /** * CoMaps are collaborative versions of plain objects, mapping string-like keys to values. * * @categoryDescription Declaration * Declare your own CoMap schemas by subclassing `CoMap` and assigning field schemas with `co`. * * Optional refs can be declared with `co.optional(...)`. * * ```ts * import { co, z } from "jazz-tools"; * * const Pet = co.map({ name: z.string() }); * const Person = co.map({ * name: z.string(), * age: z.number(), * pet: Pet, * car: co.optional(Pet), * }); * ``` * * @categoryDescription Content * You can access properties you declare on a `CoMap` (using `co`) as if they were normal properties on a plain object, using dot notation, `Object.keys()`, etc. * * ```ts * person.name; * person["age"]; * person.age = 42; * person.pet?.name; * Object.keys(person); * // => ["name", "age", "pet"] * ``` * * @category CoValues * */ export declare class CoMap extends CoValueBase implements CoValue { /** @category Type Helpers */ [TypeSym]: "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; static coValueSchema?: CoreCoMapSchema; /** @internal */ constructor(options: { fromRaw: RawCoMap; } | undefined); /** * Create a new CoMap with the given initial values and owner. * * The owner (a Group or Account) determines access rights to the CoMap. * * The CoMap will immediately be persisted and synced to connected peers. * * @example * ```ts * const person = Person.create({ * name: "Alice", * age: 42, * pet: cat, * }, { owner: friendGroup }); * ``` * * @category Creation * * @deprecated Use `co.map(...).create`. **/ static create(this: CoValueClass, init: Simplify>, options?: CoValueCreateOptionsInternal): M; /** * Return a JSON representation of the `CoMap` * @category Content */ toJSON(_key?: string, processedValues?: ID[]): any; [inspect](): any; /** * @internal */ static _createCoMap(instance: M, schema: CoreCoMapSchema, init: Simplify>, options?: CoValueCreateOptionsInternal): M; /** * Create a new `RawCoMap` from an initialization object * @internal */ static rawFromInit(instance: M, init: Simplify> | undefined, owner: Group, firstComesWins: boolean, uniqueness?: CoValueUniqueness, validationMode?: LocalValidationMode): RawCoMap<{ [key: string]: JsonValue | undefined; }, import("cojson").JsonObject | null>; /** * Load a `CoMap` with a given ID, as a given account. * * `depth` specifies which (if any) fields that reference other CoValues to load as well before resolving. * The `DeeplyLoaded` return type guarantees that corresponding referenced CoValues are loaded to the specified depth. * * You can pass `[]` or `{}` for shallowly loading only this CoMap, or `{ fieldA: depthA, fieldB: depthB }` for recursively loading referenced CoValues. * * Check out the `load` methods on `CoMap`/`CoList`/`CoFeed`/`Group`/`Account` to see which depth structures are valid to nest. * * @example * ```ts * const person = await Person.load( * "co_zdsMhHtfG6VNKt7RqPUPvUtN2Ax", * { pet: {} } * ); * ``` * * @category Subscription & Loading * * @deprecated Use `co.map(...).load` instead. */ static load = true>(this: CoValueClass, id: ID, options?: { resolve?: RefsToResolveStrict; loadAs?: Account | AnonymousJazzAgent; skipRetry?: boolean; }): Promise>>; /** * Load and subscribe to a `CoMap` with a given ID, as a given account. * * Automatically also subscribes to updates to all referenced/nested CoValues as soon as they are accessed in the listener. * * `depth` specifies which (if any) fields that reference other CoValues to load as well before calling `listener` for the first time. * The `DeeplyLoaded` return type guarantees that corresponding referenced CoValues are loaded to the specified depth. * * You can pass `[]` or `{}` for shallowly loading only this CoMap, or `{ fieldA: depthA, fieldB: depthB }` for recursively loading referenced CoValues. * * Check out the `load` methods on `CoMap`/`CoList`/`CoFeed`/`Group`/`Account` to see which depth structures are valid to nest. * * Returns an unsubscribe function that you should call when you no longer need updates. * * Also see the `useCoState` hook to reactively subscribe to a CoValue in a React component. * * @example * ```ts * const unsub = Person.subscribe( * "co_zdsMhHtfG6VNKt7RqPUPvUtN2Ax", * { pet: {} }, * (person) => console.log(person) * ); * ``` * * @category Subscription & Loading * * @deprecated Use `co.map(...).subscribe` instead. */ static subscribe = true>(this: CoValueClass, id: ID, listener: (value: Resolved, unsubscribe: () => void) => void): () => void; static subscribe = true>(this: CoValueClass, id: ID, options: SubscribeListenerOptions, listener: (value: Resolved, unsubscribe: () => void) => void): () => void; /** @deprecated Use `CoMap.upsertUnique` and `CoMap.loadUnique` instead. */ static findUnique(this: CoValueClass, unique: CoValueUniqueness["uniqueness"], ownerID: ID | ID, as?: Account | Group | AnonymousJazzAgent): `co_z${string}`; /** * Get an existing unique CoMap or create a new one if it doesn't exist. * * Unlike `upsertUnique`, this method does NOT update existing values with the provided value. * The provided value is only used when creating a new CoMap. * * @example * ```ts * const settings = await UserSettings.getOrCreateUnique({ * value: { theme: "dark", language: "en" }, * unique: "user-settings", * owner: me, * }); * ``` * * @param options The options for creating or loading the CoMap. * @returns Either an existing CoMap (unchanged), or a new initialised CoMap if none exists. * @category Subscription & Loading */ static getOrCreateUnique = true>(this: CoValueClass, options: { value: Simplify>; unique: CoValueUniqueness["uniqueness"]; owner: Account | Group; resolve?: RefsToResolveStrict; }): Promise>>; /** * Given some data, updates an existing CoMap or initialises a new one if none exists. * * Note: This method respects resolve options, and thus can return a not-loaded value if the references cannot be resolved. * * @example * ```ts * const activeEvent = await Event.upsertUnique( * sourceData.identifier, * workspace.id, * { * title: sourceData.title, * identifier: sourceData.identifier, * external_id: sourceData._id, * }, * workspace * ); * ``` * * @param options The options for creating or loading the CoMap. This includes the intended state of the CoMap, its unique identifier, its owner, and the references to resolve. * @returns Either an existing & modified CoMap, or a new initialised CoMap if none exists. * @category Subscription & Loading * * @deprecated Use `getOrCreateUnique` instead. Note: getOrCreateUnique does not update existing values. * If you need to update, use getOrCreateUnique followed by direct property assignment. */ static upsertUnique = true>(this: CoValueClass, options: { value: Simplify>; unique: CoValueUniqueness["uniqueness"]; owner: Account | Group; resolve?: RefsToResolveStrict; validation?: LocalValidationMode; }): Promise>>; /** * Loads a CoMap by its unique identifier and owner's ID. * @param unique The unique identifier of the CoMap to load. * @param ownerID The ID of the owner of the CoMap. * @param options Additional options for loading the CoMap. * @returns The loaded CoMap, or an not-loaded value if unavailable. * * @category Subscription & Loading */ static loadUnique = true>(this: CoValueClass, unique: CoValueUniqueness["uniqueness"], ownerID: ID | ID, options?: { resolve?: RefsToResolveStrict; loadAs?: Account | AnonymousJazzAgent; }): Promise>>; } /** * Contains CoMap Jazz methods that are part of the {@link CoMap.$jazz`} property. */ declare class CoMapJazzApi extends CoValueJazzApi { private coMap; private getRaw; private coMapSchema; private descriptorCache; constructor(coMap: M, getRaw: () => RawCoMap, coMapSchema: CoreCoMapSchema); get owner(): Group; private getPropertySchema; /** * Check if a key is defined in the CoMap. * * This check does not load the referenced value or validate permissions. * * @param key The key to check * @returns True if the key is defined, false otherwise * @category Content */ has(key: CoKeys): boolean; /** * Set a value on the CoMap * * @param key The key to set * @param value The value to set * @param options Optional options for setting the value * * @category Content */ set>(key: K, value: CoFieldInit, options?: { validation?: LocalValidationMode; }): 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. * * @param key The key to delete * * @category Content */ delete(key: OptionalCoKeys | (string extends keyof M ? string : never)): void; /** * Modify the `CoMap` to match another map. * * The new values are assigned to the CoMap, overwriting existing values * when the property already exists. * * @param newValues - The new values to apply to the CoMap. For collaborative values, * both CoValues and JSON values are supported. * @param options Optional options for applying the diff. * @param options.validation The validation mode to use. Defaults to "strict". * @returns The modified CoMap. * * @category Content */ applyDiff(newValues: Partial>, options?: { validation?: LocalValidationMode; }): M; /** * Given an already loaded `CoMap`, ensure that the specified fields are loaded to the specified depth. * * Works like `CoMap.load()`, but you don't need to pass the ID or the account to load as again. * * @category Subscription & Loading */ ensureLoaded>(this: CoMapJazzApi, options: { resolve: RefsToResolveStrict; unstable_branch?: BranchDefinition; cursor?: LoadCoValueCursorOption; }): Promise>; /** * Given an already loaded `CoMap`, subscribe to updates to the `CoMap` and ensure that the specified fields are loaded to the specified depth. * * Works like `CoMap.subscribe()`, but you don't need to pass the ID or the account to load as again. * * Returns an unsubscribe function that you should call when you no longer need updates. * * @category Subscription & Loading **/ subscribe = true>(this: CoMapJazzApi, listener: (value: Resolved, unsubscribe: () => void) => void): () => void; subscribe = true>(this: CoMapJazzApi, options: { resolve?: RefsToResolveStrict; unstable_branch?: BranchDefinition; cursor?: CoValueCursor; }, listener: (value: Resolved, unsubscribe: () => void) => void): () => void; /** * Wait for the `CoMap` to be uploaded to the other peers. * * @category Subscription & Loading */ waitForSync(options?: { timeout?: number; }): Promise; /** * Get the descriptor for a given key * @internal */ getDescriptor(key: string): Schema | undefined; /** * If property `prop` is a `coField.ref(...)`, you can use `coMap.$jazz.refs.prop` to access * the `Ref` instead of the potentially loaded/null value. * * This allows you to always get the ID or load the value manually. * * @example * ```ts * person.$jazz.refs.pet.id; // => ID * person.$jazz.refs.pet.value; * // => Animal | null * const pet = await person.$jazz.refs.pet.load(); * ``` * * @category Content **/ get refs(): Simplify<{ [Key in CoKeys as LoadedAndRequired extends CoValue ? Key : never]?: RefIfCoValue; } & { [Key in CoKeys as AsLoaded extends CoValue ? Key : never]: RefIfCoValue; }>; /** * Get the edits made to the CoMap. * * @category Collaboration */ getEdits(): CoMapEdits; /** @internal */ get raw(): RawCoMap<{ [key: string]: JsonValue | undefined; }, import("cojson").JsonObject | null>; } export type CoKeys = Exclude; /** * Extract keys of properties that are required */ export type RequiredCoKeys = { [K in CoKeys]: undefined extends Map[K] ? never : K; }[CoKeys]; /** * Extract keys of properties that can be undefined */ export type OptionalCoKeys = { [K in CoKeys]: undefined extends Map[K] ? K : never; }[CoKeys]; /** * Force required ref fields to be non nullable * * Considering that: * - Optional refs are typed as coField | null | undefined> * - Required refs are typed as coField | null> * * This type works in two steps: * - Remove the null from both types * - Then we check if the input type accepts undefined, if positive we put the null union back * * So the optional refs stays unchanged while we safely remove the null union * from required refs * * This way required refs can be marked as required in the CoMapInit while * staying a nullable property for value access. * * Example: * * const map = MyCoMap.create({ * requiredRef: NestedMap.create({}) // null is not valid here * }) * * map.requiredRef // this value is still nullable */ type ForceRequiredRef = V extends InstanceType | null ? NonNullable : V extends InstanceType | undefined ? V | null : V; export type CoMapInit_DEPRECATED = PartialOnUndefined<{ [Key in CoKeys]: ForceRequiredRef; }>; export type CoMapInit = { [K in RequiredCoKeys]: CoFieldInit; } & { [K in OptionalCoKeys]?: CoFieldInit | undefined; }; export {}; //# sourceMappingURL=coMap.d.ts.map