@jarenjs/studio 0.83.3 → 0.85.0
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.
- package/README.md +21 -6
- package/contracts/data.contract.json +243 -0
- package/dist/types/component/data/actions.d.ts +234 -0
- package/dist/types/component/data/index.d.ts +9 -0
- package/dist/types/component/data/mount.d.ts +87 -0
- package/dist/types/component/data/project-widget.d.ts +27 -0
- package/dist/types/component/data/viewmodel.d.ts +85 -0
- package/dist/types/component/data/views.d.ts +148 -0
- package/dist/types/component/document.d.ts +82 -0
- package/dist/types/component/flow/actions.d.ts +708 -0
- package/dist/types/component/flow/index.d.ts +7 -0
- package/dist/types/component/flow/mount.d.ts +66 -0
- package/dist/types/component/flow/project-widget.d.ts +8 -0
- package/dist/types/component/flow/runtime.d.ts +57 -0
- package/dist/types/component/flow/views.d.ts +242 -0
- package/dist/types/component/host.d.ts +2 -2
- package/dist/types/component/index.d.ts +21 -15
- package/dist/types/component/mount.d.ts +51 -0
- package/dist/types/component/project-actions.d.ts +302 -0
- package/dist/types/component/project-controller.d.ts +81 -0
- package/dist/types/component/project-state.d.ts +1 -0
- package/dist/types/component/project.d.ts +327 -0
- package/dist/types/component/shared/host-widget.d.ts +24 -0
- package/dist/types/component/shared/memo.d.ts +12 -0
- package/dist/types/component/shared/nodes.d.ts +92 -0
- package/dist/types/component/shared/schema-options.d.ts +28 -0
- package/dist/types/component/shared/studio-kit.d.ts +78 -0
- package/dist/types/component/shared/ui.d.ts +136 -0
- package/dist/types/component/view.d.ts +6 -6
- package/dist/types/data/boot-stages.d.ts +97 -0
- package/dist/types/data/browser-worker.d.ts +22 -0
- package/dist/types/data/contract.d.ts +9 -0
- package/dist/types/data/editor.d.ts +81 -0
- package/dist/types/data/handlers.d.ts +130 -0
- package/dist/types/data/host.d.ts +8 -0
- package/dist/types/data/project-worker.d.ts +8 -0
- package/dist/types/data/runtime.d.ts +46 -0
- package/dist/types/data/state.d.ts +33 -0
- package/dist/types/data/storage.d.ts +26 -0
- package/dist/types/data/transport.d.ts +69 -0
- package/dist/types/flow-document.d.ts +19 -0
- package/dist/types/flow-editor.d.ts +88 -0
- package/docs/EDITORS.md +158 -0
- package/docs/PROJECT-FORMAT.md +1 -11
- package/package.json +32 -15
- package/src/component/data/actions.js +138 -0
- package/src/component/data/index.js +10 -0
- package/src/component/data/mount.js +63 -0
- package/src/component/data/project-widget.js +164 -0
- package/src/component/data/viewmodel.js +185 -0
- package/src/component/data/views.js +241 -0
- package/src/component/document.js +344 -0
- package/src/component/flow/actions.js +331 -0
- package/src/component/flow/index.js +8 -0
- package/src/component/flow/mount.js +53 -0
- package/src/component/flow/project-widget.js +44 -0
- package/src/component/flow/runtime.js +481 -0
- package/src/component/flow/views.js +196 -0
- package/src/component/host.js +2 -2
- package/src/component/index.js +19 -9
- package/src/component/mount.js +43 -0
- package/src/component/project-actions.js +189 -0
- package/src/component/project-controller.js +248 -0
- package/src/component/project-state.js +30 -0
- package/src/component/project.js +308 -0
- package/src/component/shared/host-widget.js +35 -0
- package/src/component/shared/memo.js +28 -0
- package/src/component/shared/nodes.js +94 -0
- package/src/component/shared/schema-options.js +30 -0
- package/src/component/shared/studio-kit.js +59 -0
- package/src/component/shared/ui.js +134 -0
- package/src/data/boot-stages.js +202 -0
- package/src/data/browser-worker.js +247 -0
- package/src/data/contract.js +7 -0
- package/src/data/editor.js +95 -0
- package/src/data/handlers.js +349 -0
- package/src/data/host.js +8 -0
- package/src/data/project-worker.js +26 -0
- package/src/data/runtime.js +462 -0
- package/src/data/state.js +46 -0
- package/src/data/storage.js +61 -0
- package/src/data/transport.js +215 -0
- package/src/flow-document.js +24 -0
- package/src/flow-editor.js +98 -0
- package/styles/data.css +53 -0
- package/styles/editor.css +150 -0
- package/styles/flow.css +99 -0
- package/styles/studio.css +1 -0
- package/dist/types/author.d.ts +0 -27
- 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,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
|
+
};
|
package/docs/EDITORS.md
ADDED
|
@@ -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.
|
package/docs/PROJECT-FORMAT.md
CHANGED
|
@@ -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
|