Workspace API
Epicenter workspace definitions, table and KV access, isomorphic action registries, row child docs, runtime connection, daemon mount composition, and lower-level Y.Doc primitives share one model.
Notebook model:
defineWorkspace = pure app contract: id, name, tables, kv, actions, child-doc declarations
connect(...) = browser/local runtime: storage, optional relay sync, child-doc openers, wipe
mount(...) = daemon runtime: Yjs log, cloud sync, materializers, daemon actions
createWorkspace = low-level root Y.Doc bundle for internals, tests, and older ports
table.docs(...) = row child-doc declaration; callers open through tables.X.docs.field.open(rowId)
Reference Repositories
- Yjs: CRDT framework used by the workspace data layer
- Yjs Protocols: sync, awareness, and protocol helpers used around collaboration
Upstream Grounding
When workspace behavior depends on Yjs transactions, shared types, update encoding, document lifecycle, or conflict semantics, use source-backed grounding before relying on memory. If DeepWiki MCP is available, ask a narrow question against yjs/yjs; for collaboration sync or awareness protocol behavior, ask against yjs/y-protocols. If DeepWiki is unavailable or the repo is not indexed, use upstream source or official docs directly. Treat DeepWiki as orientation, then verify decisive details against local workspace code, installed types, tests, or official docs before changing code.
Skip DeepWiki for Epicenter schema, action, migration, and attachment conventions already documented below.
Related Skills
yjs: Yjs CRDT patterns and shared typessvelte: reactive wrappers such asfromTableandfromKv, plus commit-on-blur workspace inputsattach-primitive: the full contract and invariants everyattach*function must followtypebox: TypeBox primitives used byfield.*,defineKv, and action input schemas
Core Rules
- Workspace action
defineQuery/defineMutationfactories are not Whispering$lib/rpcadapters fromwellcrafted/query. Do not apply workspace action input-schema rules to Whispering RPC modules. _vis library-managed. Never declare it as a column, never set it on a write, never read it off a row. Single-version tables drop the versioning surface entirely; multi-version tables expose it only inside themigratefunction as({ value, version }).- Columns are TypeBox schemas. Prefer the
field.*builders from@epicenter/field(field.string,field.number,field.boolean,field.select,field.json,field.datetime) plus the standalonenullablewrapper from@epicenter/workspacefor the emptiness axis (an IANA timezone is justfield.string<IanaTimeZone>(), no bespoke builder); rawType.X()is allowed and theFlatJsonTSchemaconstraint enforces SQLite-mappable shapes either way. - Derive row types with
InferTableRow<typeof tableDefinition>in the same module that defines the table. Consumers import the type from the workspace definition module. - Do not re-derive row types from runtime table methods or relay them through state files.
- KV stores use
defineKv(schema, defaultValue)wheredefaultValueis a factory() => Static<S>. Prefer one scalar per dot-namespaced key unless the value is a true atomic object. - Every table
idand string foreign key uses a branded type plus a co-located generator. The brand lives as a pure type alias (type X = string & Brand<'X'>); the generator usesgenerateId<X>(). Call sites use the generator, never a direct cast. - Export an app's shared model as
defineWorkspace({ id, name, tables, kv, actions }). The workspace file is the sync contract: schema, branded IDs, KV defaults, isomorphic actions, andtable.docs(...)child-doc layouts. - Put isomorphic actions in the
actions: ({ tables, kv, ydoc }) => defineActions({ ... })callback. Runtime-specific actions live inconnect(..., compose)ormount(..., compose), where browser, Node, Tauri, extension, materializer, or daemon APIs are in scope. - Use
workspaceDefinition.connect(null)for local-only browser storage andworkspaceDefinition.connect(connection)for principal-scoped storage plus relay sync. Connected tables expose row child-doc openers attables.X.docs.field.open(rowId). - Use
workspaceDefinition.mount(...)for daemon composition: Yjs-log persistence, cloud sync, materializers, and daemon-exposed actions. - Treat
createWorkspace({ id, tables, kv })as the lower-level root constructor for internals, tests, and older ports. It exposes{ ydoc, tables, kv, actions, [Symbol.dispose] }; app-facing code normally starts fromdefineWorkspace. - Do not import or compute child-doc guids with
docGuid. The public contract istables.X.docs.field.guid(rowId)andtables.X.docs.field.open(rowId). - Local action calls see the handler shape directly. Adapter calls use
invokeAction, which validates input, wraps raw values inOk, preserves existingResults, and catches throws asErr(cause). Read the action return reference before changing handler failure behavior. - Every action method inside the workspace action object should have JSDoc that adds developer-facing value beyond the short
descriptionfield. - Keep workspace schema and isomorphic actions runtime-neutral. If an action file is extracted, it must stay isomorphic too. Keep runtime singletons and auth subscriptions in
client.ts,browser.ts,tauri.ts, or app-specific openers. - Compose lower-level attachments inline when you are below
defineWorkspace.connect(...). Avoid wrapper helpers that hide ordering unless the abstraction owns a real invariant. - For one-off scripts, prefer materialized files or SQLite, or call daemon actions through the daemon client. Do not reintroduce a local
connectWorkspace()shortcut unless that API exists in the current package surface.
Reference Map
- Schema definition patterns:
defineTable,defineKv, row type inference, KV scalar design, and branded IDs. - Actions, layout, and attachments:
defineWorkspaceactions, JSDoc, runtime action composition, file layout, attachment ordering, and script/daemon boundaries. - Deriving action input schemas: use
tables.X.schemaandschema.properties.Xto composedefineQuery/defineMutationinput schemas inline. No helper layer. - Action return shapes: direct calls vs adapter
invokeActionreturn contracts and error normalization. - Table, KV, CRUD, and observation: table/KV read, write, observe, and derived-state details.
- Table migrations: migration rules and version evolution examples.
- Primitive API: lower-level primitive contracts and composition details.