// // Copyright 2025 DXOS.org // import * as Schema from 'effect/Schema'; // NOTE: String literals are used instead of unique symbols for both KindId and SchemaKindId. // Unique symbols cause TS4023 "cannot be named" errors when external packages // try to export types that reference this key (e.g., `export const Graph = ...`). // TypeScript cannot emit declaration files that reference unique symbols from // external modules. Using a string literal allows the type to be inlined in // declaration files, making the API portable across package boundaries. /** * String key used to identify the kind of an entity instance (object or relation). */ export const KindId = '~@dxos/echo/Kind' as const; export type KindId = typeof KindId; /** * String key used to identify the kind of a schema (object schema or relation schema). * Parallels KindId which identifies instance kinds. */ export const SchemaKindId = '~@dxos/echo/SchemaKind' as const; export type SchemaKindId = typeof SchemaKindId; /** * String key used to brand snapshot types. * Snapshots have SnapshotKindId instead of KindId, making them * distinguishable from reactive objects at the type level. */ export const SnapshotKindId = '~@dxos/echo/SnapshotKind' as const; export type SnapshotKindId = typeof SnapshotKindId; /** * Hidden slot on a static `Type.Type` entity that holds the source Effect * Schema. `Type.getSchema(...)` reads this for static types; persisted types * rebuild from `jsonSchema` instead. Stored as a string key for declaration * portability (see KindId comment above). */ export const StaticTypeSchemaSlot = '~@dxos/echo/Type.StaticSchema' as const; export type StaticTypeSchemaSlot = typeof StaticTypeSchemaSlot; /** * Read the hidden `StaticTypeSchemaSlot` off any value that may carry one. * Returns `undefined` for raw schemas (no slot) and non-object inputs. * Single point-of-cast for the slot lookup. */ export const getStaticTypeSchema = (value: unknown): Schema.Schema.AnyNoContext | undefined => { if (value == null || typeof value !== 'object') { return undefined; } return (value as { [StaticTypeSchemaSlot]?: Schema.Schema.AnyNoContext })[StaticTypeSchemaSlot]; }; /** * Read the `[SchemaKindId]` brand off a value. Returns `undefined` for raw * schemas (which don't carry the brand on their static type) and non-object * inputs. Single point-of-cast for the brand lookup. */ export const getSchemaKind = (value: unknown): EntityKind | undefined => { if (value == null || typeof value !== 'object') { return undefined; } return (value as { [SchemaKindId]?: EntityKind })[SchemaKindId]; }; /** * Read the `[KindId]` brand off a value. Returns `undefined` for raw schemas * and non-object inputs. Companion to {@link getSchemaKind}. */ export const getEntityKindBrand = (value: unknown): EntityKind | undefined => { if (value == null || typeof value !== 'object') { return undefined; } return (value as { [KindId]?: EntityKind })[KindId]; }; /** * Phantom string key on `Type` entities that carries the instance type `A`. * Lets internal helpers (`makeObject`, `createObject`, etc.) pattern-match the * instance type from an entity input without importing from the top-level * `Type` module. Mirrors `Type.InstancePhantomId` (declared in `Type.ts`). * * Stored as a string key so declarations remain portable across packages * (see KindId comment above). */ export const InstancePhantomId = '~@dxos/echo/Type.Instance' as const; export type InstancePhantomId = typeof InstancePhantomId; /** * Nominal brand carried by the well-known "any object" / "any relation" * schemas (`Obj.Unknown`, `Relation.Unknown`). The brand lets `Ref.Ref`, * `Filter.type`, and `Query.type` accept these schemas in addition to * `Type.Type` entities, without opening the door to arbitrary raw schemas. * * Stored as a string key so declarations remain portable across packages * (see KindId comment above). */ export const UnknownTypeSchemaBrandId = '~@dxos/echo/UnknownTypeSchemaBrand' as const; export type UnknownTypeSchemaBrandId = typeof UnknownTypeSchemaBrandId; /** * Schema-side companion to `Type.Type` entities for the "any object" / * "any relation" cases. Branded so `Ref.Ref` / `Filter.type` / `Query.type` * can pattern-match on it; arbitrary `Schema.Schema` values do not satisfy * this shape. */ export interface UnknownTypeSchema extends Schema.Schema { readonly [UnknownTypeSchemaBrandId]: K; } /** * Kinds of entities stored in ECHO: objects, relations, and types. */ export enum EntityKind { Object = 'object', Relation = 'relation', Type = 'type', } export const EntityKindSchema = Schema.Enums(EntityKind); /** * Typename for generic object references (Type.Obj / Ref.Ref(Obj.Unknown)). * Used when referencing any object without a specific schema. */ export const ANY_OBJECT_TYPENAME = 'org.dxos.schema.anyObject'; /** * Version for generic object references. */ export const ANY_OBJECT_VERSION = '0.0.0';