@jarenjs/studio 0.83.3 → 0.84.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/README.md +21 -6
  2. package/contracts/data.contract.json +243 -0
  3. package/dist/types/component/data/actions.d.ts +234 -0
  4. package/dist/types/component/data/index.d.ts +9 -0
  5. package/dist/types/component/data/mount.d.ts +87 -0
  6. package/dist/types/component/data/project-widget.d.ts +27 -0
  7. package/dist/types/component/data/viewmodel.d.ts +85 -0
  8. package/dist/types/component/data/views.d.ts +148 -0
  9. package/dist/types/component/document.d.ts +82 -0
  10. package/dist/types/component/flow/actions.d.ts +708 -0
  11. package/dist/types/component/flow/index.d.ts +7 -0
  12. package/dist/types/component/flow/mount.d.ts +66 -0
  13. package/dist/types/component/flow/project-widget.d.ts +8 -0
  14. package/dist/types/component/flow/runtime.d.ts +57 -0
  15. package/dist/types/component/flow/views.d.ts +242 -0
  16. package/dist/types/component/host.d.ts +2 -2
  17. package/dist/types/component/index.d.ts +21 -15
  18. package/dist/types/component/mount.d.ts +51 -0
  19. package/dist/types/component/project-actions.d.ts +302 -0
  20. package/dist/types/component/project-controller.d.ts +81 -0
  21. package/dist/types/component/project-state.d.ts +1 -0
  22. package/dist/types/component/project.d.ts +327 -0
  23. package/dist/types/component/shared/host-widget.d.ts +24 -0
  24. package/dist/types/component/shared/memo.d.ts +12 -0
  25. package/dist/types/component/shared/nodes.d.ts +92 -0
  26. package/dist/types/component/shared/schema-options.d.ts +28 -0
  27. package/dist/types/component/shared/studio-kit.d.ts +78 -0
  28. package/dist/types/component/shared/ui.d.ts +136 -0
  29. package/dist/types/component/view.d.ts +6 -6
  30. package/dist/types/data/boot-stages.d.ts +97 -0
  31. package/dist/types/data/browser-worker.d.ts +22 -0
  32. package/dist/types/data/contract.d.ts +9 -0
  33. package/dist/types/data/editor.d.ts +81 -0
  34. package/dist/types/data/handlers.d.ts +130 -0
  35. package/dist/types/data/host.d.ts +8 -0
  36. package/dist/types/data/project-worker.d.ts +8 -0
  37. package/dist/types/data/runtime.d.ts +46 -0
  38. package/dist/types/data/state.d.ts +33 -0
  39. package/dist/types/data/storage.d.ts +26 -0
  40. package/dist/types/data/transport.d.ts +69 -0
  41. package/dist/types/flow-document.d.ts +19 -0
  42. package/dist/types/flow-editor.d.ts +88 -0
  43. package/docs/EDITORS.md +158 -0
  44. package/docs/PROJECT-FORMAT.md +1 -11
  45. package/package.json +32 -15
  46. package/src/component/data/actions.js +138 -0
  47. package/src/component/data/index.js +10 -0
  48. package/src/component/data/mount.js +63 -0
  49. package/src/component/data/project-widget.js +164 -0
  50. package/src/component/data/viewmodel.js +185 -0
  51. package/src/component/data/views.js +241 -0
  52. package/src/component/document.js +344 -0
  53. package/src/component/flow/actions.js +331 -0
  54. package/src/component/flow/index.js +8 -0
  55. package/src/component/flow/mount.js +53 -0
  56. package/src/component/flow/project-widget.js +44 -0
  57. package/src/component/flow/runtime.js +481 -0
  58. package/src/component/flow/views.js +196 -0
  59. package/src/component/host.js +2 -2
  60. package/src/component/index.js +19 -9
  61. package/src/component/mount.js +43 -0
  62. package/src/component/project-actions.js +189 -0
  63. package/src/component/project-controller.js +248 -0
  64. package/src/component/project-state.js +30 -0
  65. package/src/component/project.js +308 -0
  66. package/src/component/shared/host-widget.js +35 -0
  67. package/src/component/shared/memo.js +28 -0
  68. package/src/component/shared/nodes.js +94 -0
  69. package/src/component/shared/schema-options.js +30 -0
  70. package/src/component/shared/studio-kit.js +59 -0
  71. package/src/component/shared/ui.js +134 -0
  72. package/src/data/boot-stages.js +202 -0
  73. package/src/data/browser-worker.js +247 -0
  74. package/src/data/contract.js +7 -0
  75. package/src/data/editor.js +95 -0
  76. package/src/data/handlers.js +349 -0
  77. package/src/data/host.js +8 -0
  78. package/src/data/project-worker.js +26 -0
  79. package/src/data/runtime.js +462 -0
  80. package/src/data/state.js +46 -0
  81. package/src/data/storage.js +61 -0
  82. package/src/data/transport.js +215 -0
  83. package/src/flow-document.js +24 -0
  84. package/src/flow-editor.js +98 -0
  85. package/styles/data.css +53 -0
  86. package/styles/editor.css +150 -0
  87. package/styles/flow.css +99 -0
  88. package/styles/studio.css +1 -0
  89. package/dist/types/author.d.ts +0 -27
  90. package/src/author.js +0 -55
@@ -0,0 +1,130 @@
1
+ /**
2
+ * @file The data studio's handler table, apart from the browser that
3
+ * hosts it.
4
+ *
5
+ * `db-worker.js` is the OWNER context: it loads the SQLite wasm build,
6
+ * claims the OPFS access-handle pool and serves two transports. None of
7
+ * that is testable outside a browser, and none of it is where the
8
+ * studio's behaviour lives — so the behaviour is here, over an injected
9
+ * host, and the worker supplies the real one. The same table then runs
10
+ * under a Node driver in the test suite, which is how the store rules
11
+ * below are held to anything at all.
12
+ *
13
+ * What the table owns, and the worker deliberately does not:
14
+ *
15
+ * - **The live registry.** Every `data.live` registration is held in a
16
+ * set, and `data.lives` reports its SIZE. A count kept as a number
17
+ * beside the registrations drifts the moment a reopen zeroes it while
18
+ * subscriptions are still held — under-reporting forever, and going
19
+ * negative on the next release, at which point the count fails its own
20
+ * output schema and freezes at its last good value. A frozen count
21
+ * reads exactly like a healthy one, which is why this is a set.
22
+ * - **Reopening releases.** Closing the store releases every registration
23
+ * it fed, so the count comes back to zero because the subscriptions
24
+ * ended, not because someone assigned zero.
25
+ * - **A refused migration does not advance the model.** `state.model`
26
+ * moves only when `migrate()` returned; a store reopened after a
27
+ * refusal is reopened on the model it still has, and the refusal
28
+ * crosses as the declared `db` failure.
29
+ * - **Peers are told.** Any reopen — open, recreate, migrate — drops the
30
+ * live registrations of every tab, so the worker announces it and the
31
+ * boundaries resubscribe. Silence would leave every live pane showing
32
+ * its last rows forever while looking live.
33
+ * - **Recreating is owner-only.** `reset` unlinks the database the owner
34
+ * holds; a client tab asking for it is refused rather than served, so
35
+ * the control that is honest in the owning tab cannot be destructive
36
+ * from another one.
37
+ * - **The oracle's store is throwaway.** `data.oracle` opens a fresh
38
+ * in-memory store on the model it is handed, answers one query over
39
+ * the documents it is handed (with its plan, when asked), and closes
40
+ * it — the store this context holds, its registrations and its peers
41
+ * are untouched. That is what
42
+ * lets the committed spatial corpus hold SQLite-in-wasm to the
43
+ * JavaScript engine from any tab, including one with no OPFS at all.
44
+ */
45
+ export type DataHost = {
46
+ /**
47
+ * - Decide the topology and prepare
48
+ * this context; resolves to the `data.init` answer.
49
+ */
50
+ init: () => Promise<any>;
51
+ /**
52
+ * - A fresh driver over a fresh handle.
53
+ */
54
+ makeDriver: () => any;
55
+ /**
56
+ * - A driver over a throwaway
57
+ * in-memory handle: the oracle's store, opened and closed per call,
58
+ * never the database this context holds.
59
+ */
60
+ makeScratchDriver: () => any;
61
+ /**
62
+ * - The database path for this context.
63
+ */
64
+ path: () => string;
65
+ /**
66
+ * - The VFS this context settled on.
67
+ */
68
+ vfs: () => string;
69
+ /**
70
+ * - Whether a migration can be applied
71
+ * here at all; a memory store recreates instead.
72
+ */
73
+ durable: () => boolean;
74
+ /**
75
+ * - Drop the persistent database.
76
+ */
77
+ unlink: () => void;
78
+ /**
79
+ * - Tell every peer the store
80
+ * was reopened under them.
81
+ */
82
+ announce: (notice: any) => void;
83
+ /**
84
+ * - The operator registry the store mounts.
85
+ */
86
+ operators: any;
87
+ };
88
+ /**
89
+ * The host the table runs over: everything that differs between a
90
+ * browser worker holding an OPFS pool and a test holding a file.
91
+ * @typedef {Object} DataHost
92
+ * @property {() => Promise<any>} init - Decide the topology and prepare
93
+ * this context; resolves to the `data.init` answer.
94
+ * @property {() => any} makeDriver - A fresh driver over a fresh handle.
95
+ * @property {() => any} makeScratchDriver - A driver over a throwaway
96
+ * in-memory handle: the oracle's store, opened and closed per call,
97
+ * never the database this context holds.
98
+ * @property {() => string} path - The database path for this context.
99
+ * @property {() => string} vfs - The VFS this context settled on.
100
+ * @property {() => boolean} durable - Whether a migration can be applied
101
+ * here at all; a memory store recreates instead.
102
+ * @property {() => void} unlink - Drop the persistent database.
103
+ * @property {(notice: any) => void} announce - Tell every peer the store
104
+ * was reopened under them.
105
+ * @property {any} operators - The operator registry the store mounts.
106
+ */
107
+ /**
108
+ * The details of the declared `db` failure: the store's own coded refusal
109
+ * and its message.
110
+ *
111
+ * The contract carries string codes, including numeric DOMException
112
+ * codes converted to strings. The host's message survives unchanged.
113
+ * @param {unknown} error
114
+ * @returns {{ code: string | null, message: string }}
115
+ */
116
+ export declare function wireError(error: unknown): {
117
+ code: string | null;
118
+ message: string;
119
+ };
120
+ /**
121
+ * The studio's handler table over an injected host.
122
+ * @param {DataHost} host
123
+ * @returns {{ handlers: Record<string, any>, clientHandlers: Record<string, any>, state: any, dispose: () => Promise<void> }}
124
+ */
125
+ export declare function createDataHandlers(host: DataHost): {
126
+ handlers: Record<string, any>;
127
+ clientHandlers: Record<string, any>;
128
+ state: any;
129
+ dispose: () => Promise<void>;
130
+ };
@@ -0,0 +1,8 @@
1
+ export { createDataHandlers, wireError } from './handlers.js';
2
+ export { dataContract, dataContractDocument } from './contract.js';
3
+ export { BOOT_STAGES, BOOT_ERROR_CODE, DEFAULT_BOOT_BUDGETS, DataBootError, bootFailure, resolveBootBudgets, createStageRunner } from './boot-stages.js';
4
+ export { selectBrowserStorage, discoverStorageOwner } from './storage.js';
5
+ export type DataHost = import('./handlers.js').DataHost;
6
+ /** @typedef {import('./handlers.js').DataHost} DataHost */
7
+ export { createBrowserDataWorker } from './browser-worker.js';
8
+ export { createProjectDataWorker } from './project-worker.js';
@@ -0,0 +1,8 @@
1
+ /** @param {{ initialize: () => Promise<any>, scope: any, operators?: any }} env */
2
+ export declare function createProjectDataWorker(env: {
3
+ initialize: () => Promise<any>;
4
+ scope: any;
5
+ operators?: any;
6
+ }): {
7
+ dispose(): Promise<void>;
8
+ };
@@ -0,0 +1,46 @@
1
+ /**
2
+ * The runtime: effects plus the exported view model.
3
+ * @param {any} env - explicit seed documents, transport, lifecycle and optional worked examples.
4
+ */
5
+ export declare function createDataRuntime(env?: any): {
6
+ effects: {
7
+ 'data-boot': (_props: any, dispatch: any) => Promise<void>;
8
+ 'data-retry': (_props: any, dispatch: any) => Promise<void>;
9
+ 'data-resume': (_props: any, dispatch: any) => Promise<void>;
10
+ 'data-open': (props: any, dispatch: any) => Promise<{
11
+ ok: boolean;
12
+ error: string | undefined;
13
+ }> | Promise<{
14
+ ok: boolean;
15
+ model: any;
16
+ } | {
17
+ ok: boolean;
18
+ error: string;
19
+ code: any;
20
+ }> | undefined;
21
+ 'data-insert': (props: any, dispatch: any) => void;
22
+ 'data-delete': (props: any, dispatch: any) => void;
23
+ 'data-run': (props: any, dispatch: any) => Promise<{
24
+ ok: boolean;
25
+ error: string | undefined;
26
+ }> | Promise<{
27
+ ok: boolean;
28
+ result: any;
29
+ explain: {
30
+ sql: any;
31
+ params: any;
32
+ indexes: any;
33
+ residual: any;
34
+ };
35
+ } | {
36
+ ok: boolean;
37
+ error: string;
38
+ code: any;
39
+ }>;
40
+ 'data-oracle': (_props: any, dispatch: any) => void;
41
+ 'data-trip': (props: any, dispatch: any) => void;
42
+ 'data-migrate': (_props: any, dispatch: any) => Promise<any> | undefined;
43
+ };
44
+ ownerSub: (_props: any, dispatch: any) => (() => void) | undefined;
45
+ dispose: () => void;
46
+ };
@@ -0,0 +1,33 @@
1
+ /** Isolated Data state; the boot operation seeds the editable documents. */
2
+ export declare function createDataState(): {
3
+ status: string;
4
+ boot: null;
5
+ topology: string;
6
+ vfs: string;
7
+ version: string;
8
+ capture: string;
9
+ operators: never[];
10
+ pushableOperators: never[];
11
+ refusal: null;
12
+ modelText: string;
13
+ queryText: string;
14
+ collection: string;
15
+ keyPointer: string;
16
+ rows: never[];
17
+ results: never[];
18
+ explain: null;
19
+ live: {
20
+ rows: never[];
21
+ seq: null;
22
+ regs: null;
23
+ };
24
+ insertDraft: string;
25
+ migration: null;
26
+ oracle: null;
27
+ trip: {
28
+ csv: string;
29
+ report: null;
30
+ };
31
+ error: null;
32
+ mobilePane: string;
33
+ };
@@ -0,0 +1,26 @@
1
+ /** The browser storage ladder reports every observed fallback. */
2
+ /** @param {{ isolated: boolean, sharedArrayBuffer: boolean,
3
+ * sab: () => Promise<any>, sah: () => Promise<any>, indexedDB: () => Promise<any> }} probes
4
+ * @returns {Promise<any>}
5
+ */
6
+ export declare function selectBrowserStorage(probes: {
7
+ isolated: boolean;
8
+ sharedArrayBuffer: boolean;
9
+ sab: () => Promise<any>;
10
+ sah: () => Promise<any>;
11
+ indexedDB: () => Promise<any>;
12
+ }): Promise<any>;
13
+ /** Discover a peer after storage acquisition failed without a Web Lock.
14
+ * Silence says only that no peer answered in time. When OPFS reported a
15
+ * held handle, it cannot authorize a private memory fallback.
16
+ * @param {{ held?: boolean }} selected
17
+ * @param {(ms: number) => Promise<false | { vfs: string }>} ping
18
+ * @returns {Promise<false | { vfs: string }>}
19
+ */
20
+ export declare function discoverStorageOwner(selected: {
21
+ held?: boolean;
22
+ }, ping: (ms: number) => Promise<false | {
23
+ vfs: string;
24
+ }>): Promise<false | {
25
+ vfs: string;
26
+ }>;
@@ -0,0 +1,69 @@
1
+ export type TransportDeps = {
2
+ /**
3
+ * - a fresh owner worker
4
+ */
5
+ spawnWorker: () => any;
6
+ /**
7
+ * - a port client over a channel
8
+ */
9
+ openClient?: (channel: any) => any;
10
+ /**
11
+ * - the shared tab channel
12
+ */
13
+ openChannel: () => any;
14
+ /**
15
+ * - per-stage budgets
16
+ */
17
+ budgets?: Readonly<Record<string, number>>;
18
+ setTimer?: (fn: () => void, ms: number) => any;
19
+ clearTimer?: (handle: any) => void;
20
+ };
21
+ /**
22
+ * The browser-shaped dependencies of a transport: how a worker is
23
+ * spawned, how a port client is opened over a channel, how the shared
24
+ * channel is opened, and the stage budgets. The page supplies the
25
+ * platform's; a Node test supplies fakes and millisecond budgets.
26
+ * @typedef {Object} TransportDeps
27
+ * @property {() => any} spawnWorker - a fresh owner worker
28
+ * @property {(channel: any) => any} [openClient] - a port client over a channel
29
+ * @property {() => any} openChannel - the shared tab channel
30
+ * @property {Readonly<Record<string, number>>} [budgets] - per-stage budgets
31
+ * @property {(fn: () => void, ms: number) => any} [setTimer]
32
+ * @property {(handle: any) => void} [clearTimer]
33
+ */
34
+ /**
35
+ * The transport: a contract PORT client over the own worker (owner) or
36
+ * the shared channel (client). `request` unwraps the binding's D6
37
+ * outcome into the value-or-throw shape the effects consume — a
38
+ * declared `db` failure surfaces the store's own code and message from
39
+ * its details; `subscribe` is the client's stream half, passed through.
40
+ *
41
+ * `boot()` is the closed protocol of `lib/boot-stages.js`: the worker
42
+ * starting (its `ready` frame, or its `error` event), then the worker's
43
+ * own `data.init` under the three stages it announces as it passes them
44
+ * (`sqlite-init`, `vfs-acquire`, `topology`) — each bounded from here,
45
+ * so a stage the worker never finishes fails under its own name and
46
+ * never masquerades as an OPFS absence. `bounded('store-open', …)` is
47
+ * the fifth stage, run by the boot effect around the first open.
48
+ * `close()` releases everything this transport created — the client,
49
+ * the worker, the shared channel, the listeners — exactly once, so a
50
+ * failed boot leaves nothing behind and a retry starts clean.
51
+ * @param {TransportDeps} [deps]
52
+ */
53
+ export declare function createTransport(deps?: TransportDeps): {
54
+ boot: () => Promise<any>;
55
+ bounded: (name: string, work: (advance: (next: string) => void) => any) => Promise<any>;
56
+ request: (op: any, args: any) => Promise<any>;
57
+ subscribe: (input: any, callbacks: any) => any;
58
+ close: () => void;
59
+ /** The stage the last boot reached (for a failure outside a run). */
60
+ stage: () => string;
61
+ /** @param {(notice: any) => void} cb */
62
+ notices: (cb: (notice: any) => void) => void;
63
+ /** @param {(fault: { message: string }) => void} cb - a worker that
64
+ * fails after the boot settled */
65
+ faults: (cb: (fault: {
66
+ message: string;
67
+ }) => void) => void;
68
+ settled: () => void;
69
+ };
@@ -0,0 +1,19 @@
1
+ export declare const validateDagDoc: import("@jarenjs/validate").CompiledCollector;
2
+ /** @param {'fsm'|'dag'} kind @param {any} document */
3
+ export declare function validateFlowDocument(kind: 'fsm' | 'dag', document: any): {
4
+ valid: boolean;
5
+ kind: string;
6
+ total: number;
7
+ errors: Array<{
8
+ code: string | null;
9
+ message: string;
10
+ docPath?: string;
11
+ }>;
12
+ } | {
13
+ valid: boolean;
14
+ errors: {
15
+ code: any;
16
+ message: any;
17
+ docPath: any;
18
+ }[];
19
+ };
@@ -0,0 +1,88 @@
1
+ export type FlowSnapshot = {
2
+ kind: 'fsm' | 'dag' | null;
3
+ document: any;
4
+ revision: string;
5
+ result: any;
6
+ };
7
+ export type EditorReceipt = {
8
+ ok: boolean;
9
+ document?: any;
10
+ revision?: string;
11
+ conflict?: boolean;
12
+ error?: string;
13
+ errors?: any[];
14
+ valid?: boolean;
15
+ total?: number;
16
+ code?: string;
17
+ output?: any;
18
+ started?: boolean;
19
+ };
20
+ import { applyJSONPatch } from '@jarenjs/json/patch';
21
+ /** @param {{ kind?: 'fsm'|'dag'|null, document?: any, input?: any }} [options] */
22
+ export declare function createFlowState(options?: {
23
+ kind?: 'fsm' | 'dag' | null;
24
+ document?: any;
25
+ input?: any;
26
+ }): {
27
+ kind: "dag" | "fsm" | null;
28
+ doc: any;
29
+ selection: null;
30
+ connect: null;
31
+ tab: string;
32
+ parseError: null;
33
+ history: {
34
+ past: never[];
35
+ future: never[];
36
+ };
37
+ run: null;
38
+ runContext: any;
39
+ dagInput: string;
40
+ revision: number;
41
+ mobilePane: string;
42
+ };
43
+ /** @param {{ getApp: () => any, runtime: any }} env */
44
+ export declare function createFlowController(env: {
45
+ getApp: () => any;
46
+ runtime: any;
47
+ }): {
48
+ read: () => FlowSnapshot;
49
+ validate: (candidate: any, kind?: 'fsm' | 'dag') => {
50
+ valid: boolean;
51
+ kind: string;
52
+ total: number;
53
+ errors: Array<{
54
+ code: string | null;
55
+ message: string;
56
+ docPath?: string;
57
+ }>;
58
+ } | {
59
+ valid: boolean;
60
+ errors: {
61
+ code: any;
62
+ message: any;
63
+ docPath: any;
64
+ }[];
65
+ };
66
+ replace: (candidate: any, options?: {
67
+ expectedRevision?: string;
68
+ kind?: 'fsm' | 'dag';
69
+ }) => Promise<EditorReceipt>;
70
+ apply: (patch: Parameters<typeof applyJSONPatch>[1], options?: {
71
+ expectedRevision?: string;
72
+ kind?: 'fsm' | 'dag';
73
+ }) => Promise<EditorReceipt>;
74
+ run: (options?: {
75
+ input?: any;
76
+ event?: string;
77
+ }) => Promise<EditorReceipt>;
78
+ subscribe: (listener: (snapshot: FlowSnapshot) => void) => () => void;
79
+ attach: () => void;
80
+ effects: {
81
+ 'flow-accepted': (id: any) => void;
82
+ 'flow-refused': (id: any) => void;
83
+ 'flow-editor-run': ((props: any, dispatch: any) => Promise<void>) & {
84
+ dispose: () => void;
85
+ };
86
+ };
87
+ dispose: () => void;
88
+ };
@@ -0,0 +1,158 @@
1
+ # Shared editors and host services
2
+
3
+ Studio publishes the complete project, Flow and Data editors used by the Jaren
4
+ website. Their actions, views, controllers and resource lifetimes have one
5
+ implementation. A host supplies its own page, templates and execution services.
6
+ The root `@jarenjs/studio` entry remains a headless project engine.
7
+
8
+ | Public entry | What the host receives |
9
+ | --- | --- |
10
+ | `@jarenjs/studio/component` | Project mount, document/render host, project host, actions, controller and view pieces |
11
+ | `@jarenjs/studio/flow` | Flow mount, actions, diagram/text/inspector projections, controller, machine/DAG runtime and project-file widget |
12
+ | `@jarenjs/studio/data` | Data mount, actions, projections, controller, bounded transport and project-model worker ownership |
13
+ | `@jarenjs/studio/data/host` | Compiled operation contract, handler table, boot stages, storage selection and injected browser/project worker hosts |
14
+ | `@jarenjs/studio/contracts/data.contract.json` | The unchanged `jaren-data-studio` operation document |
15
+ | `@jarenjs/studio/styles/studio.css` | Project chrome, shared controls and embedded Flow styles |
16
+ | `@jarenjs/studio/styles/flow.css` | Standalone Flow chrome and shared controls |
17
+ | `@jarenjs/studio/styles/data.css` | Standalone Data chrome and shared controls |
18
+ | `@jarenjs/studio/styles/editor.css` | Scoped common controls and generated-form chrome |
19
+
20
+ The installed-host fixture in `test/consumer/studio-installed.js` composes all
21
+ three editors using public imports. The packed consumer gate runs it with real
22
+ Node and Bun SQLite drivers and bundles a separate browser host with private
23
+ SQLite initialization. It imports no website source.
24
+
25
+ ## Operations and revisions
26
+
27
+ Every mount returns `read`, `validate`, `replace`, `apply`, `run`, `subscribe` and
28
+ `dispose`. Data additionally returns `ready`, which resolves to an actual boot
29
+ success or failure. The app that implements each editor stays private.
30
+
31
+ `read()` returns an isolated document and a string `revision`. Revisions describe
32
+ the current document, rather than a counter incremented by unrelated rendering.
33
+ `subscribe(listener)` observes document changes and returns an unsubscribe
34
+ function. Disposing twice is safe; disposal ends subscriptions, pending work and
35
+ owned runtimes. A disposed editor refuses publication and execution.
36
+
37
+ `validate(candidate)` uses the existing document validators and returns a
38
+ verdict and errors. `replace(candidate, { expectedRevision })` validates and then
39
+ checks the observed revision again inside the same queued transition used by
40
+ manual edits. A stale revision returns `{ ok: false, conflict: true }`. Invalid or
41
+ stale candidates preserve documents, results and typing buffers.
42
+ `apply(patch, { expectedRevision })` applies an RFC 6902 patch to the read document
43
+ and follows that same replacement path. Keep the returned candidate and errors
44
+ when publication is refused; obtain a fresh revision before trying again.
45
+
46
+ Project documents include the project name, files, active file and layout.
47
+ Uncommitted typing remains a separate buffer: an external file replacement keeps
48
+ that draft and the existing conflict UI. `run(name?)` selects the requested file
49
+ and uses the same host runners and result actions as the controls. It returns
50
+ settled transform, flow and model results, refusing stale results after edits.
51
+ An interactive app file acknowledges its requested stage restart. The injected
52
+ `projectData.execute(project, name)` owns model-query execution.
53
+
54
+ Flow documents are the `jaren-fsm` or `jaren-dag` value. Replacement retains an
55
+ undo entry, invalidates the previous run and clears structural selection. The
56
+ kind stays fixed while a document is open; templates open a new document.
57
+ `run({ input, event? })` uses the injected task registry or the mounted machine.
58
+ A DAG returns its settled output or failure. `event` sends a machine event
59
+ through the same sandbox used by the run controls.
60
+
61
+ Data documents contain `{ model, query }`. `read()` also returns `buffers`
62
+ (`modelText` and `queryText`), results and the query plan. Its revision includes
63
+ both raw buffers, so even an unfinished JSON edit prevents a stale replacement.
64
+ Replacing the document edits those buffers. It does not recreate a database.
65
+ `run({ operation: 'query', externals })` executes and explains against the open
66
+ store. `run({ operation: 'open' })` explicitly recreates the model through the
67
+ existing owner-only operation. A client or memory topology retains the same
68
+ recreation refusal as the manual UI. Data accepts JSON row input by default;
69
+ a host can inject its existing `createRow(text)` convenience function.
70
+
71
+ ## Project and Flow hosts
72
+
73
+ `createStudioDocumentHost({ markdown, diagram, templates? })` validates and audits
74
+ app documents and provides the form/chart/Markdown/diagram render widgets. The
75
+ host supplies its Markdown and diagram rendering policy.
76
+ `createProjectHost({ loadDocument, runQuery, runJslt, runValidation, operators? })`
77
+ provides the stage, splitter and project engine integration.
78
+
79
+ Pass that host to `mountStudioEditor(node, { project, host, ... })`. Optional
80
+ services include template lookup, template cards, downloads, offline export,
81
+ scheduling and a `projectData` runtime. The mount includes the shared embedded
82
+ Flow widget; a supplied project Data runtime contributes its model/query widget.
83
+ `createProjectDataRuntime({ createWorker })` owns one worker per committed model
84
+ and seed identity. Switching files releases the view subscription, while model
85
+ changes, deletion, replacement and disposal retire the owning worker.
86
+
87
+ `mountFlowEditor(node, { kind, document, input?, tasks?, template?, templates? })`
88
+ uses the same diagram, inspector, text round-trip policy, history and run runtime
89
+ as the project-file widget. Task functions are explicit host services. Document
90
+ machine effects are recorded by the sandbox, rather than acquiring host effects.
91
+ Both mounts accept scheduling and error-reporting services.
92
+
93
+ ## Data transport, initialization and identity
94
+
95
+ `mountDataEditor(node, { model, query, transport, ... })` accepts a factory that
96
+ creates a fresh transport. `createTransport({ spawnWorker, openChannel,
97
+ openClient?, budgets?, setTimer?, clearTimer? })` implements the operation port,
98
+ five bounded boot stages, owner-to-client handoff, live stream and cleanup.
99
+ Its default port client uses the exported Data contract. It chooses no worker
100
+ URL or channel name. Failed boots release their resources; retries start fresh.
101
+ Leaving a composed route releases its transport; returning resumes without
102
+ re-seeding the text buffers. Lifecycle listeners are removed on disposal.
103
+
104
+ The browser host imports its chosen SQLite initializer and calls
105
+ `createBrowserDataWorker({ initialize, scope, createChannel, identity, operators? })`.
106
+ The published code does not import that initializer. `initialize()` returns the
107
+ SQLite module accepted by the existing `@jarenjs/db/wasm` adapters.
108
+ The `identity` record explicitly names **all five** storage resources:
109
+ `channel`, `pool`, `database`, `snapshots` and `lock`. Use the same identity only
110
+ for tabs intentionally sharing a store. Independent applications and datasets
111
+ must supply distinct identities. The host factory proves durable VFS candidates
112
+ with a write/close/reopen probe and retains visible fallback/refusal reporting.
113
+ Its disposal pauses an acquired pool without deleting stored files.
114
+
115
+ `createProjectDataWorker({ initialize, scope, operators? })` serves the same
116
+ contract over an isolated in-memory store. It acquires no shared database,
117
+ channel or storage identity. The public handler constructor remains
118
+ `createDataHandlers(DataHost)`, so Node, Bun and other driver hosts can serve the
119
+ operation contract directly without a browser or a SQLite WASM initializer.
120
+
121
+ The Jaren website keeps its existing `jaren-data-studio` channel,
122
+ `jaren-data` pool, `/jaren-data-studio.db` database,
123
+ `jaren-data-studio-snapshots` snapshots and `jaren-data-studio-owner` lock.
124
+ Its private app owns `@sqlite.org/sqlite-wasm` and Vite asset handling. Another
125
+ private app must explicitly provide and declare that bootstrap dependency;
126
+ Studio's published dependency graph contains only Jaren packages.
127
+
128
+ Optional `corpus`, `trip`, `migration` and `createRow` services carry host examples.
129
+ The corresponding controls appear when configured. Their data and preview
130
+ functions remain host-owned. A corpus report names the configured `executor`
131
+ (default `sqlite`); a host using the browser adapter can name `sqlite-wasm`.
132
+
133
+ ## Composing and styling
134
+
135
+ Existing app hosts can merge the exported actions, view rules and viewmodels
136
+ and attach the corresponding controller to their app lifecycle. They use the
137
+ same component implementations as the mount helpers. Their private app handles
138
+ routing; external callers receive only the controller's document operations.
139
+
140
+ Import the styles explicitly. Flow and Data selectors are scoped to each
141
+ editor; project selectors retain the existing `jstudio`/`js-*` vocabulary.
142
+ Controls inherit the host's color, font, spacing and motion tokens and provide
143
+ standalone fallbacks. The host owns page gutters, theme tokens, worker assets
144
+ and the available viewport height. The established 1024/760 breakpoints,
145
+ 44px phone controls and reduced-motion behavior travel with the editors.
146
+
147
+ A mounted Data editor also exposes `setActive(boolean)`. Await
148
+ `setActive(false)` when a host route leaves to release its worker, live query
149
+ and ownership without losing incomplete model/query text. `setActive(true)`
150
+ reopens the last accepted model, preserves the raw editing buffers and resolves
151
+ when boot settles; `ready` always refers to that activation. Repeated activation
152
+ is idempotent, inactive runs return a refusal, and `dispose()` is terminal.
153
+
154
+ Data model, query and round-trip text buffers publish every input event. Live
155
+ store updates cannot replace uncommitted typing; model recreation and query
156
+ execution still require their explicit operations.
157
+
158
+ Project snapshots expose both a semantic document `revision` for candidate publication and a numeric `stageRevision` for the mounted app. `run(name)` explicitly restarts an interactive app; `run(name, { restart: false })` uses normal commit behavior, preserving the stage for state-only edits and rebuilding it after structural changes.
@@ -183,17 +183,7 @@ and `describe` report file problems as diagnostics.
183
183
  v0.1 is the headless **engine** — parse, per-file validate, assemble,
184
184
  classify — plus the IDE **component** that mounts it: the file rail, the
185
185
  debounced editor with its typing buffer, the run stage, the three layout
186
- modes with a drag splitter, the phone pane switcher, save/load/share, and
187
- an assistant that can list, read, write and run a project's files.
188
-
189
- The creation menu and assistant share the engine's ten-kind vocabulary and
190
- valid starter files. `@jarenjs/studio/author` uses one authoring profile per
191
- file, then the full file validation gate with bounded repair rounds. The
192
- website's `jaren_project_author` tool rejects stale publication if the project
193
- changed during generation, returning the candidate for recovery. Generic
194
- JSON/schema/contract files and imported destinations use broad JSON profiles;
195
- their acceptance still depends on the full local validation gate. The exact
196
- `jaren_project_write` tool remains available for supplied text.
186
+ modes with a drag splitter, the phone pane switcher, save/load/share, and complete file validation and export. The creation menu shares the engine’s ten-kind vocabulary and valid starter files. External authoring integrations publish through the same revision-checked public editor controllers.
197
187
 
198
188
  The stage displays the nested app's latest boot/runtime failure and clears
199
189
  it on restart. Remaining constraints live in