import * as Schema from 'effect/Schema'; import type * as Types from 'effect/Types'; import { type EncodedReference } from '@dxos/echo-protocol'; import { DXN, type EntityId, type URI } from '@dxos/keys'; import type * as Database from './Database'; import type * as EntityModule from './Entity'; import * as internal from './internal'; import * as typeInternal from './internal/Type'; import type * as RelationModule from './Relation'; /** * Structural base shared by the three sibling type-entity interfaces * ({@link Obj}, {@link Relation}, {@link Type}). NOT exported — callers * should constrain on {@link AnyEntity} when they want "any of the three" * and on the specific kind interface otherwise. */ interface BaseTypeEntity { /** * Entity-kind brand of the type-entity value itself — always `EntityKind.Type`. * The kind of instance the type *describes* lives on `[SchemaKindId]` * (Object / Relation / Type). Lets `Obj.isObject` / `Relation.isRelation` * reject type entities by a single `[KindId]` check. */ readonly [internal.KindId]: internal.EntityKind.Type; /** * Object id. Like all ECHO entities, type entities always carry an id — * stamped at construction for in-memory (static) declarations and assigned by * the database once persisted. The id does NOT determine the entity's URI: * static types resolve to their typename DXN, persisted types to `echo:/` * (see `getTypeURIFromSpecifier`). */ readonly id: EntityId; readonly name?: string; readonly jsonSchema: internal.JsonSchemaType; readonly [InstancePhantomId]?: A; } /** * TypeScript type for an ECHO object type — a `Type.Type` entity. * * `T` is the instance type produced by `Obj.make(Foo, props)`. `Fields` is * retained as a structural hint (the runtime value still carries `.fields`), * but consumers should derive instance/encoded types via `Type.InstanceType`. * * **Not a `Schema.Schema`.** `Foo.ast` / `Schema.Schema.Type` / * `Schema.extend(Foo)` no longer typecheck — extract the Effect Schema via * `Type.getSchema(Foo)` first, or derive instance types via * `Type.InstanceType`. * * @example * ```ts * const Person = Schema.Struct({ * name: Schema.String, * }).pipe(Type.makeObject(DXN.make('com.example.type.person', '0.1.0'))); * * type Person = Type.InstanceType; * ``` */ export interface Obj extends BaseTypeEntity> { /** Schema-kind brand (object). */ readonly [internal.SchemaKindId]: internal.EntityKind.Object; /** Source Effect Schema — used internally by `Type.getSchema(self)`. */ readonly [internal.StaticTypeSchemaSlot]: Schema.Schema.AnyNoContext; /** * The fields defined in the original struct schema. * Allows accessing field definitions for introspection. */ readonly fields: Fields; } /** * Type that represents any ECHO object type — a `Type.Type` entity branded * with the object entity kind, i.e. what `Type.makeObject(dxn)` produces. */ export type AnyObj = Obj; /** * Factory function to create an ECHO object type. * * Returns a `Type.Type` entity — a live, in-memory `TypeSchema` instance, * NOT a `Schema.Schema`. Use `Type.InstanceType` for the instance * type and `Type.getSchema(Foo)` to obtain the underlying Effect Schema. * * The entity's id defaults to `EntityId.deterministic(typename, version)` so * constructing a type never reaches `crypto.getRandomValues()` — required for * Cloudflare workerd, which forbids RNG calls in global (module-evaluation) * scope. Pass `{ id }` to override (e.g. with `EntityId.random()` from a * request handler). * * @example * ```ts * const Person = Schema.Struct({ * name: Schema.String, * }).pipe(Type.makeObject(DXN.make('com.example.type.person', '0.1.0'))); * ``` */ export declare const makeObject: { (dxn: DXN.DXN, options?: { id?: EntityId; }): (self: Self) => Obj>; }; /** * ECHO meta-schema entity — stores `{ name?, typename, version, jsonSchema }`. * Type-kind sibling of `Type.makeObject(...)` / `Type.makeRelation(...)` outputs. * Stored types live under this entity; filter via `Filter.type(Type.Type)`. */ export declare const Type: Type; /** * Common props shared by the type-kind factories. Typename and version are * optional — drafts omit typename and default version to {@link DRAFT_VERSION}. */ type MakeTypeProps = { jsonSchema: internal.JsonSchemaType; typename?: string; version?: string; name?: string; id?: EntityId; }; /** * Construct a new object-kind type entity from raw metadata — for cases where * an Effect Schema isn't available (e.g. JSON-Schema arriving over the network * or from a UI editor). Parallel to {@link makeObject} but takes pre-built * `jsonSchema` instead of piping through an Effect schema. * * The returned entity is in-memory; persist it with `db.addType(entity)`. */ export declare const makeObjectFromJsonSchema: (props: MakeTypeProps) => Type; /** * Construct a new relation-kind type entity from raw metadata. Parallel to * {@link makeRelation} but takes pre-built `jsonSchema` instead of piping * through an Effect schema. `source` / `target` accept either a static * `Type.Obj` entity or the well-known `Obj.Unknown` schema. * * The returned entity is in-memory; persist it with `db.addType(entity)`. */ export declare const makeRelationFromJsonSchema: (props: MakeTypeProps & { source: AnyObj | internal.UnknownTypeSchema; target: AnyObj | internal.UnknownTypeSchema; }) => Type; /** * TypeScript type for an ECHO relation type — a `Type.Type` entity. * * `T` is the instance-property type produced by `Relation.make(...)` (excluding * source/target endpoints). `Source` and `Target` are the endpoint types. * * **Not a `Schema.Schema`.** See {@link Obj}'s note. */ export interface Relation extends BaseTypeEntity & T & EntityModule.OfKind> { /** Schema-kind brand (relation). */ readonly [internal.SchemaKindId]: internal.EntityKind.Relation; /** Source Effect Schema — used internally by `Type.getSchema(self)`. */ readonly [internal.StaticTypeSchemaSlot]: Schema.Schema.AnyNoContext; /** * The fields defined in the original struct schema. * Allows accessing field definitions for introspection. */ readonly fields: Fields; } /** * Type that represents any ECHO relation type — a `Type.Type` entity branded * with the relation entity kind, i.e. what `Type.makeRelation(...)` produces. */ export type AnyRelation = Relation; /** * Factory function to create an ECHO relation schema. * Adds relation metadata annotations to an Effect schema. * * @example * ```ts * const WorksFor = Schema.Struct({ * role: Schema.String, * }).pipe(Type.makeRelation({ * dxn: DXN.make('com.example.type.worksFor', '0.1.0'), * source: Person, * target: Company, * })); * ``` */ export declare const makeRelation: { (opts: { dxn: DXN.DXN; source: Obj | internal.UnknownTypeSchema; target: Obj | internal.UnknownTypeSchema; /** * Override the entity id. Defaults to `EntityId.deterministic(typename, version)`; * see `Type.makeObject` for the workerd motivation. */ id?: EntityId; }): (self: Self) => Relation, SourceInstance & EntityModule.OfKind, TargetInstance & EntityModule.OfKind>; }; /** * Type that represents any ECHO type-kind entity — a `Type.Type` meta-schema * value (static `Type.Type` or a persisted draft from `db.addType(...)`). * Mirrors {@link AnyObj} / {@link AnyRelation} for the third sibling kind. */ export type AnyType = Type; /** * Any ECHO type-entity — one of the three sibling kinds: object-kind, relation-kind, * or type-kind (the meta-schema). APIs that want "any ECHO type" use this union; * the underlying Effect Schema is retrieved via `Type.getSchema`. */ export type AnyEntity = AnyObj | AnyRelation | AnyType; /** * Type guard: narrows a `Type.AnyEntity` to an object-kind entity. Checks * ENTITIES, not instances — use `Obj.isObject` for instances. Raw * `Schema.Schema` values (including the branded `Obj.Unknown` companion) * are intentionally not accepted; inspect their `TypeAnnotation` directly. */ export declare const isObject: (entity: AnyEntity) => entity is AnyObj; /** * Type guard: narrows a `Type.AnyEntity` to a relation-kind entity. Checks * ENTITIES, not instances — use `Relation.isRelation` for instances. */ export declare const isRelation: (entity: AnyEntity) => entity is AnyRelation; /** * Type guard: narrows a `Type.AnyEntity` to the type-kind meta-schema * (e.g. `Type.Type`). Mirrors {@link isObject} / {@link isRelation}. */ export declare const isTypeKind: (entity: AnyEntity) => entity is Type; /** * Narrow a `Type.AnyEntity` (e.g. one returned from `schemaRegistry.query(...)`) * to `AnyObj`, throwing if it describes a relation or the type-kind * meta-schema. Use at call sites that need to pass the value to `Obj.make`, * `Filter.type`, or other object-only APIs. */ export declare const assertObject: (entity: AnyEntity) => AnyObj; /** Narrow a `Type.AnyEntity` to `AnyRelation`, throwing otherwise. */ export declare const expectRelation: (entity: AnyEntity) => AnyRelation; /** Narrow a `Type.AnyEntity` to the `Type.Type` meta-schema, throwing otherwise. */ export declare const expectTypeKind: (entity: AnyEntity) => Type; /** * Type that represents any Ref schema (with unknown target type). * This is a schema type, not an instance type. */ export type AnyRef = Schema.Schema, EncodedReference>; /** * Returns the URI identifying a type entity. Always defined. * * - Static `Type.Obj` / `Type.Relation` → typename DXN (e.g. `dxn:com.example.type.person:0.1.0`). * - Persisted `Type.Type` instance (has `id`) → local `EID` (`echo:/`). * - In-memory `Type.Type` draft (has `id`, no typename) → local `EID`. * * Only accepts `Type.AnyEntity` entities. Raw `Schema.Schema` values and the * branded `Obj.Unknown` / `Relation.Unknown` schemas are intentionally not * supported — use `internal.getSchemaURI` or the schema's typename annotation * directly when working at the schema level. */ export declare const getURI: (input: AnyEntity) => URI.URI; /** * @returns The typename. Example: `com.example.type.person`. * * Persisted `Type.Type` entities carry typename in `EntityMeta.key` (the * canonical registry-provenance field); unnamed drafts fall back to the * entity's object id so the helper always returns a string. Any `dxn:` or * `echo:/` prefix is stripped — typename is a bare identifier, not a URI. */ export declare const getTypename: (input: AnyEntity) => string; /** * Gets the version. * @example 0.1.0 * @example 0.1.0- (in-database, versioned by automerge heads) * * The registry-provenance semver lives in `EntityMeta.version`; unversioned * drafts default to {@link DRAFT_VERSION} (`'0.0.0'`). In-database entities are * additionally versioned by their automerge heads, which are exposed as the * semver pre-release tag (`-`). In-memory declarations have no * heads and surface the bare semver. Read the registry semver alone via * `Type.getMeta(input).version`. */ export declare const getVersion: (input: AnyEntity) => string; /** * Type predicate: true iff the value is any type-kind ECHO entity — a static * `Type.Obj` / `Type.Relation` produced by `Type.makeObject` / `Type.makeRelation`, a * static meta `Type.Type`, or a persisted `Type.Type` returned by the database. * * All three branches stamp `[KindId] = Type`, so this is a single brand check. * Use {@link isObject} / {@link isRelation} / {@link isTypeKind} * when you need to discriminate further; use {@link getDatabase} when you mean * "is this a db-attached type" (vs. an in-memory declaration). */ export declare const isType: (value: unknown) => value is AnyEntity; /** * Get the database the type entity belongs to, or `undefined` if it is an * in-memory declaration (`Type.makeObject` / `Type.makeRelation` result) not * yet attached to a database. Mirrors `Obj.getDatabase` / `Relation.getDatabase`. * * Database attachment is the canonical discriminator between in-memory and * in-database type entities — both are live reactive `TypeSchema` instances and * are otherwise indistinguishable. */ export declare const getDatabase: (input: AnyEntity) => Database.Database | undefined; /** * Mutable meta type returned by `Type.getMeta` inside a `Type.update` callback. * Mirrors `Obj.Meta` / `Relation.Meta` — `Type.Type` is an Entity like its * siblings, so its meta is the same `EntityMeta` record: * `{ keys, tags?, key?, version? }`. * * `key` / `version` here are the canonical registry-provenance pair * (typename + semver) on persisted Type.Type entities; they are absent on * unnamed drafts. Use {@link getTypename} / {@link getVersion} when you want * a non-`undefined` value with id / {@link DRAFT_VERSION} fallbacks. */ export type Meta = internal.Meta; /** * Deeply read-only version of {@link Meta}. * Prevents mutation at all nesting levels (e.g., `meta.keys.push()` is a TS error). */ export type ReadonlyMeta = internal.ReadonlyMeta; /** * Returns the entity's `EntityMeta`. Same semantics as `Obj.getMeta` / * `Relation.getMeta` — `Type.Type` is an Entity and carries the canonical * `EntityMeta` directly. Returns mutable meta when passed a mutable type * (inside a `Type.update` callback), read-only meta otherwise. * * For persisted Type entities, `meta.key` holds the typename and * `meta.version` holds the semver. Use {@link getTypename} / {@link getVersion} * if you want the helpers' id / {@link DRAFT_VERSION} fallbacks for drafts. * * Both persisted and in-memory type entities (`Type.makeObject` / * `Type.makeRelation` results) carry their `EntityMeta` via `[MetaId]`, so the * lookup is uniform. */ export declare function getMeta(entity: internal.Mutable): Meta; export declare function getMeta(entity: Mutable): Meta; export declare function getMeta(entity: AnyEntity): ReadonlyMeta; /** * Get the display label of a type entity. * Reads the field(s) nominated by the type's {@link LabelAnnotation} (e.g. `name` on persisted schemas). * Returns `undefined` if no label field is populated. */ export declare const getLabel: (entity: AnyEntity, options?: internal.GetLabelOptions) => string | undefined; /** * String key used to phantom-carry the instance type produced by a `Type.Type`. * Used by `Type.InstanceType` to recover the schema instance type * since `Type.makeObject(dxn)` does not return a `Schema.Schema`. * * Re-exported from the internal types layer so both `Type.ts` and internal * helpers (`makeObject`, `createObject`) reference the same phantom key. */ export declare const InstancePhantomId: "~@dxos/echo/Type.Instance"; export type InstancePhantomId = internal.InstancePhantomId; /** * Sibling of {@link Obj} / {@link Relation} for the third ECHO entity kind: * **type-kind** entities (meta-schemas). The singleton {@link Type} const is * the canonical example — it describes stored type definitions themselves. * * Not a `Schema.Schema`. Use `Type.getSchema(value)` to obtain the underlying * Effect Schema and `Type.update(value, draft => ...)` to mutate. * * `A` is the instance-type phantom — what `Obj.make(value, ...)` would produce. * Merged with the `Type` const value via TypeScript declaration merging. */ export interface Type extends BaseTypeEntity> { /** Schema-kind brand (type — the meta-schema kind). */ readonly [internal.SchemaKindId]: internal.EntityKind.Type; /** Source Effect Schema — used internally by `Type.getSchema(self)`. */ readonly [internal.StaticTypeSchemaSlot]: Schema.Schema.AnyNoContext; } /** * Instance type produced by a Type entity. * * Accepts ONLY {@link AnyEntity} inputs — `Type.Obj`, `Type.Relation`, or * `Type.Type`. Raw Effect `Schema.Schema` values are rejected: for those, use * `Schema.Schema.Type` directly. This separation keeps the type * system honest about which values represent ECHO entities versus plain * Effect schemas. * * Dispatches on the entity kind: * - `Relation` → `Endpoints & Props & OfKind` * - `Obj` → `A & OfKind` * - `Type` → `A & OfKind` */ export type InstanceType = T extends Relation ? RelationModule.Endpoints & Props & EntityModule.OfKind : T extends Obj ? A & EntityModule.OfKind : T extends Type ? A & EntityModule.OfKind : never; /** * Returns the Effect Schema for a type entity. * * - For static `Type.Obj` / `Type.Relation` entities the source Effect Schema is * read from a hidden slot — these overloads preserve the instance type. * - For `Type.Type` entities (the meta-schema kind) the schema is rebuilt from * `type.jsonSchema`; the instance type isn't statically knowable so the wide * `AnyEntity` overload widens to `Schema.Schema.AnyNoContext`. * * Always call this when you need to interact with the Effect Schema API * (e.g. before passing to Effect.Schema functions). For ECHO-side APIs * (`Obj.make`, `Filter.type`, `Ref`) pass the type entity directly. * * Only accepts `Type.AnyEntity` — raw `Schema.Schema` values can be used * directly without unwrapping. */ export declare function getSchema(type: T): Schema.Schema>; export declare function getSchema(type: T): Schema.Schema>; export declare function getSchema(type: AnyEntity): Schema.Schema.AnyNoContext; /** * Mutable view of a `Type.Type` — the shape passed to the `Type.update` callback. * Outside `Type.update`, `Type.Type` fields are read-only (both at the type level * and at runtime — direct assignment throws). Use this to constrain mutation to * the change context, analogous to `Obj.update(obj, (draft) => ...)`. * * NOTE: `typename` and `version` are intentionally absent — they live in * `EntityMeta` (`key` / `version` — the canonical registry-provenance pair). * Read them via {@link getTypename} / {@link getVersion} / {@link getMeta}; * `typename` is treated as immutable on persisted entities. * * Unlike `Obj.update` — whose mutable view is inferred as `Mutable` over the * whole instance type because every data field is editable — a `Type.Type` * exposes only `name` and `jsonSchema` for mutation. The rest of its shape * (`id`, the `[KindId]` / `[SchemaKindId]` brands, and `typename` / `version` * in meta) is immutable, so this view is declared explicitly rather than * derived from `InstanceType`. */ export interface Mutable { name?: string; jsonSchema: Types.DeepMutable; } /** * Perform mutations on a `Type.Type` within a change context. * * The callback receives a {@link Mutable} view of the type — direct mutation of * a `Type.Type` outside `Type.update` throws at runtime, mirroring `Obj.update`. * Delegates to the same automerge-transaction primitive `Obj.update(obj, cb)` uses. */ export declare const update: (type: AnyEntity, callback: (mutable: Mutable) => void) => void; /** * Add fields to a persisted type's schema. * @throws if the type is not persisted. */ export declare const addFields: (type: AnyEntity, fields: Schema.Struct.Fields) => void; /** * Replace existing fields on a persisted type's schema. * @throws if the type is not persisted. */ export declare const updateFields: (type: AnyEntity, fields: Schema.Struct.Fields) => void; /** * Rename a field on a persisted type's schema. * @throws if the type is not persisted. */ export declare const updateFieldPropertyName: (type: Type, { before, after }: { before: PropertyKey; after: PropertyKey; }) => void; /** * Remove fields from a persisted type's schema. * @throws if the type is not persisted. */ export declare const removeFields: (type: AnyEntity, fieldNames: string[]) => void; export {}; //# sourceMappingURL=Type.d.ts.map