97 lines
6.5 KiB
Markdown
97 lines
6.5 KiB
Markdown
# Headless Canvas: portable ESM preview
|
|
|
|
Build from the Endoleap checkout with `npm run build:headless`. The generated
|
|
`.headless-dist` directory is a private package; nothing is published to a registry.
|
|
Copy it elsewhere, or use `npm pack --offline --ignore-scripts` inside that directory.
|
|
|
|
Public exports:
|
|
|
|
| Import | Responsibility |
|
|
| --- | --- |
|
|
| `@endoleap/headless-canvas` or `/core` | Document, registry, capabilities, transactions, history, sessions, geometry and queries |
|
|
| `/runtime` | Project/Workspace/Host, storage and service port types, recovery, scoped loader and compute/display pools |
|
|
| `/service` | Foundry protocol codecs and transport adapters; credentials remain in the host |
|
|
| `/browser` | Explicit IndexedDB/Web Locks and module Worker adapters |
|
|
|
|
The portable entries have no React, Next, React Flow or other runtime package
|
|
dependency. They use standard platform APIs such as AbortController, Blob, URL,
|
|
TextEncoder and Web Crypto. The sample runs on Node 22+. Browser adapters require
|
|
their browser APIs; importing `/runtime` does not import the browser adapter.
|
|
|
|
Run `node consumer.mjs` inside the generated package. It uses a custom counter
|
|
component, rejects an atomic invalid transaction, commits edits, undoes/redoes,
|
|
cancels a gesture preview, deep-copies through the runtime, and reopens the engine.
|
|
The storage and protocol-2 transport are in-memory examples. Reopening demonstrates
|
|
the codec and host contract, not cross-process crash durability or a deployed server.
|
|
|
|
`npm run test:headless:package` builds, packs/unpacks into an isolated directory,
|
|
blocks framework imports and browser-only globals, runs the sample, checks Node
|
|
TypeScript consumers without the DOM library, and exercises compiled browser adapters
|
|
and module Worker URLs in isolated Chromium. The browser checks load the emitted JS
|
|
directly, without TypeScript stripping or a Next bundler.
|
|
|
|
This preview does not yet package the React renderer/bindings. Their legacy
|
|
projection DTO types still require an explicit adapter boundary. No registry
|
|
publication, release support promise, server deployment or full-goal completion is
|
|
implied by this local package.
|
|
|
|
Experimental partial-model runtime: pass a validated prefix plus `loading` in
|
|
`EngineOptions`. Version 1 uses recovery format 7; version 2 carries snapshot-bound direct canvas
|
|
membership counts and uses format 8. Both keep the scheduler held. Version 3
|
|
uses format 9 and retains `runtime.loadingRead` separately from the moving ACK base.
|
|
Loaded non-reference data fields and node geometry can be edited and undone; creation,
|
|
deletions, ownership/reference changes, uploads, imports and AI admission wait.
|
|
`engine.loading.accept(document, versions, state)` extends the captured prefix without
|
|
replacing local edits. `engine.loading.complete(document, versions, {sourceToken})`
|
|
validates the full capture and releases scheduling only after its cut is durable.
|
|
Use `{fresh:true}` only for an explicitly fresh complete read; conflicts require the
|
|
existing reviewed recovery flow. `engine.loading.resume()` coalesces a fresh complete
|
|
read through the project's persistence adapter and persists completion before resolving.
|
|
`workspace.resumeLoading(projectId)` delegates to that owner and reports current-identity
|
|
failures. Closing the project aborts the read and saves the recoverable prefix; detaching
|
|
a view does not close its engine.
|
|
|
|
Endoleap's page now opens the same project engine for a validated prefix, allows the
|
|
restricted field/geometry edits above, coalesces incoming prefixes, and preserves its
|
|
textarea, history and viewport when the full model arrives. App navigation cancels the
|
|
view's stream and asks the retained project owner to complete through a fresh read.
|
|
Reload recovers the durable prefix when that project is opened again. This is not a
|
|
persistent HTTP cursor or a workspace-wide startup scan. Version-3 scoped ACK/chunk
|
|
coordination is described below; broader structural permissions remain unfinished.
|
|
|
|
Scope-aware queries are obtained from `engine.editor.getQueries()` and share the
|
|
same publication boundary as the root. `lookupElement(id)` returns `present`,
|
|
`absent` (complete project), or `unloaded`; a missing ID in a partial model is not
|
|
proof of absence. `membersResult(canvasId)` adds a completeness flag for direct
|
|
members; `childCanvasesResult` covers published topology, while `incidentResult`
|
|
requires the complete project because another owner may contain an incoming edge.
|
|
Legacy array-returning methods enumerate loaded records only. Hit-query completeness
|
|
also includes scope coverage. Derived indexes and coverage views are not persisted.
|
|
|
|
An optional `canvasElementCounts` field in the version-1 stream header allows new
|
|
clients to certify individual scopes. Missing metadata retains legacy conservative
|
|
coverage. Counts include nodes and connections; they must cover exactly the declared
|
|
active canvases, sum to the total, and never be exceeded by received records. A scope
|
|
is ready only after its full count has entered the dependency-closed published prefix.
|
|
Version-2 loading permits `connection.path` edits and history in a certified owner
|
|
scope; endpoint, ownership, creation and deletion edits still wait. Scope completion never replaces terminal/EOF validation for the whole project.
|
|
Versions 1/2 continue to hold network scheduling. In version 3, mutable operations
|
|
wait in the scheduler's derived admission set until every participant scope is
|
|
complete and its root is durable; unrelated certified scopes can send meanwhile.
|
|
The normal frozen-body/key, writer claims and pre-dispatch durability barrier apply.
|
|
|
|
`runtime.loadingRead` retains the original source document and version vector. Incoming
|
|
prefixes and same-source completion validate against that capture; scopes with advanced
|
|
ACK versions retain their newer acknowledged records, then pending intents replay.
|
|
`Workspace.open(document, versions, {sourceToken})` completes that original stream,
|
|
even when its captured version is now older than a scoped ACK. An unrelated fresh
|
|
read waits for outstanding receipts and uses a scheduling hold while capturing and
|
|
persisting its base; edits during the read remain local and are replayed.
|
|
|
|
Recovery format 9 encodes both baselines and original frozen requests. Unknown results
|
|
still query/retry the same body/key after reopening. Partial-source completion clears
|
|
the retained read baseline and forces a full checkpoint without older partial journal
|
|
dependencies. Creation, deletion, owner/endpoint/reference changes, uploads and AI
|
|
remain gated during partial loading; broader structural scope admission and real
|
|
Foundry deployment/integration remain unfinished.
|