@happyvertical/smrt-app-shell 0.60.17 → 0.61.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/AGENTS.md +51 -9
- package/README.md +378 -23
- package/agents/draft-ui.md +74 -0
- package/agents/engine.md +81 -0
- package/agents/shared-pieces.md +127 -0
- package/dist/engine/app-runtime.d.ts +7 -0
- package/dist/engine/app-runtime.js +30 -3
- package/dist/engine/commands.d.ts +23 -0
- package/dist/engine/commands.js +29 -0
- package/dist/engine/draft.d.ts +31 -0
- package/dist/engine/draft.js +88 -0
- package/dist/engine/host.d.ts +175 -1
- package/dist/engine/host.js +501 -9
- package/dist/engine/index.d.ts +4 -2
- package/dist/engine/index.js +2 -1
- package/dist/engine/publish.d.ts +100 -0
- package/dist/engine/publish.js +218 -0
- package/dist/engine/store.d.ts +8 -0
- package/dist/engine/store.js +8 -0
- package/dist/engine/worker-protocol.d.ts +1 -1
- package/dist/engine/worker-protocol.js +1 -0
- package/dist/lib/objects/App.d.ts +42 -1
- package/dist/lib/objects/App.js +72 -4
- package/dist/lib/objects/AppDraft.d.ts +31 -0
- package/dist/lib/objects/AppDraft.js +73 -0
- package/dist/lib/objects/AppDraftVersion.d.ts +25 -0
- package/dist/lib/objects/AppDraftVersion.js +68 -0
- package/dist/lib/objects/Approval.d.ts +4 -4
- package/dist/lib/objects/Approval.js +3 -3
- package/dist/lib/objects/index.d.ts +2 -0
- package/dist/lib/objects/index.js +2 -0
- package/dist/lib/server/approval-store.d.ts +6 -0
- package/dist/lib/server/approval-store.js +102 -0
- package/dist/lib/server/approvals.d.ts +15 -0
- package/dist/lib/server/approvals.js +62 -43
- package/dist/lib/server/builder.d.ts +18 -10
- package/dist/lib/server/builder.js +46 -18
- package/dist/lib/server/drafts.d.ts +100 -0
- package/dist/lib/server/drafts.js +324 -0
- package/dist/lib/server/events.d.ts +14 -0
- package/dist/lib/server/events.js +3 -0
- package/dist/lib/server/host.d.ts +44 -2
- package/dist/lib/server/host.js +155 -11
- package/dist/lib/server/results.d.ts +6 -0
- package/dist/lib/server/results.js +8 -0
- package/dist/lib/server/target.d.ts +24 -0
- package/dist/lib/server/target.js +64 -0
- package/dist/lib/server/version-store.d.ts +40 -1
- package/dist/lib/server/version-store.js +144 -32
- package/dist/manifest.json +699 -6
- package/dist/smrt-knowledge.okf.md +19 -2
- package/package.json +19 -15
package/dist/engine/host.d.ts
CHANGED
|
@@ -1,12 +1,96 @@
|
|
|
1
1
|
import { EventEmitter } from 'node:events';
|
|
2
2
|
import { type Command, type ExecuteOptions, type Plan, type Template } from './commands.ts';
|
|
3
|
-
import { type AppDefinition } from './definition.ts';
|
|
3
|
+
import { type AppDefinition, type Step } from './definition.ts';
|
|
4
|
+
import { type DraftMarker } from './draft.ts';
|
|
5
|
+
import { type DefinitionDiff, type DraftChain, type PublishMethod } from './publish.ts';
|
|
4
6
|
import type { VersionStore } from './store.ts';
|
|
5
7
|
type Row = Record<string, unknown>;
|
|
6
8
|
export interface HostOptions {
|
|
7
9
|
store: VersionStore;
|
|
8
10
|
/** The worker entry. Defaults to `./app-worker.ts` or `.js` next to this module. */
|
|
9
11
|
workerUrl?: URL | string;
|
|
12
|
+
/**
|
|
13
|
+
* Start frozen: every structural command is refused (see
|
|
14
|
+
* {@link AppHost.setFrozen}). Records, reports, the app's own tools and REST
|
|
15
|
+
* routes, and `exportTemplate` keep working.
|
|
16
|
+
*/
|
|
17
|
+
frozen?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Approve risky commands, `_delete` tools and REST `DELETE` without being
|
|
20
|
+
* asked. For a DRAFT host only: `open` refuses it unless `dataDir` is a draft
|
|
21
|
+
* directory made by `forkDraft` (it holds the engine's marker, naming a
|
|
22
|
+
* different live directory). It is read once, here, into a private field:
|
|
23
|
+
* there is no setter, so it cannot be turned on later or for the live host.
|
|
24
|
+
*/
|
|
25
|
+
autoApprove?: boolean;
|
|
26
|
+
}
|
|
27
|
+
export interface ForkDraftOptions {
|
|
28
|
+
/**
|
|
29
|
+
* The new draft's version store, EMPTY. `forkDraft` seeds it with a copy of
|
|
30
|
+
* the live current version (the draft's BASE record, same version number)
|
|
31
|
+
* and points `current` at it; every draft version after that has a higher
|
|
32
|
+
* number. The base record is not a draft version.
|
|
33
|
+
*/
|
|
34
|
+
store: VersionStore;
|
|
35
|
+
}
|
|
36
|
+
/** Where a publish is, for `PublishOptions.onStage` (tests inject failures there). */
|
|
37
|
+
export type PublishStage = 'copied' | 'rehearsed' | 'snapshotted' | 'journaled' | 'retired' | 'moved-aside' | 'swapped' | 'respawned' | 'committed';
|
|
38
|
+
export interface PublishVersionPreview {
|
|
39
|
+
/** The number this version gets in live. */
|
|
40
|
+
version: number;
|
|
41
|
+
/** Its number in the draft. */
|
|
42
|
+
draftVersion: number;
|
|
43
|
+
author: string;
|
|
44
|
+
summary: string;
|
|
45
|
+
steps: Step[];
|
|
46
|
+
/** The SQL the rehearsal ran for it. */
|
|
47
|
+
statements: string[];
|
|
48
|
+
}
|
|
49
|
+
/** What a publish would do, from a rehearsal on a fresh copy of the current live data. */
|
|
50
|
+
export interface PublishPreview {
|
|
51
|
+
/** Pass this as `PublishOptions.expect`; publish refuses if the draft's chain no longer matches. */
|
|
52
|
+
digest: string;
|
|
53
|
+
risky: true;
|
|
54
|
+
fromVersion: number;
|
|
55
|
+
toVersion: number;
|
|
56
|
+
versions: PublishVersionPreview[];
|
|
57
|
+
/** Live's current definition against the draft's final one. */
|
|
58
|
+
definition: DefinitionDiff;
|
|
59
|
+
/** Archived values and tables the changes create (what undo restores from). */
|
|
60
|
+
archives: {
|
|
61
|
+
values: number;
|
|
62
|
+
tables: number;
|
|
63
|
+
};
|
|
64
|
+
/** EVERY method of the final definition, in full, marked added/changed/unchanged. */
|
|
65
|
+
methods: PublishMethod[];
|
|
66
|
+
}
|
|
67
|
+
export interface PublishOptions {
|
|
68
|
+
author: string;
|
|
69
|
+
/** Required: a publish is always risky. Only the shell's approval sets it. */
|
|
70
|
+
approved?: boolean;
|
|
71
|
+
/** The `digest` of the preview the owner saw. */
|
|
72
|
+
expect: string;
|
|
73
|
+
/** Tags the history summaries: "[draft: <name>] <summary>". */
|
|
74
|
+
draftName?: string;
|
|
75
|
+
/**
|
|
76
|
+
* Called after each stage; throwing aborts and rolls back as if that stage
|
|
77
|
+
* had failed. For tests. After `committed` the publish is final: errors are ignored.
|
|
78
|
+
*/
|
|
79
|
+
onStage?: (stage: PublishStage) => void | Promise<void>;
|
|
80
|
+
}
|
|
81
|
+
export interface PublishResult {
|
|
82
|
+
/** The version live is on now. */
|
|
83
|
+
version: number;
|
|
84
|
+
/** The versions the publish added, oldest first. */
|
|
85
|
+
versions: number[];
|
|
86
|
+
summary: string;
|
|
87
|
+
/** The retained copy of the data as it was before (snapshots/…-migrate-prepublish.db). */
|
|
88
|
+
snapshot: string;
|
|
89
|
+
archives: {
|
|
90
|
+
values: number;
|
|
91
|
+
tables: number;
|
|
92
|
+
};
|
|
93
|
+
appRestartMs: number;
|
|
10
94
|
}
|
|
11
95
|
export interface ChangeResult {
|
|
12
96
|
version: number;
|
|
@@ -53,6 +137,7 @@ export interface VersionSummary {
|
|
|
53
137
|
current: boolean;
|
|
54
138
|
}
|
|
55
139
|
export declare class AppHost extends EventEmitter {
|
|
140
|
+
#private;
|
|
56
141
|
def: AppDefinition;
|
|
57
142
|
version: number;
|
|
58
143
|
readonly dataDir: string;
|
|
@@ -61,6 +146,14 @@ export declare class AppHost extends EventEmitter {
|
|
|
61
146
|
private workerUrl;
|
|
62
147
|
private app;
|
|
63
148
|
private closed;
|
|
149
|
+
private frozenNow;
|
|
150
|
+
/** What boot did about an interrupted publish: `rolled-back` put the old data back. */
|
|
151
|
+
recovery: 'none' | 'rolled-forward' | 'rolled-back';
|
|
152
|
+
/** Set when this host is a draft copy (its directory holds the draft marker). */
|
|
153
|
+
readonly draft: DraftMarker | null;
|
|
154
|
+
/** Record/tool/REST calls currently inside the worker (see {@link drainInflight}). */
|
|
155
|
+
private inflight;
|
|
156
|
+
private idleWaiters;
|
|
64
157
|
/** Changes are applied one at a time. Work inside the queue must not wait on it. */
|
|
65
158
|
private queue;
|
|
66
159
|
private constructor();
|
|
@@ -84,6 +177,15 @@ export declare class AppHost extends EventEmitter {
|
|
|
84
177
|
private applyChange;
|
|
85
178
|
/** What a command would do, without changing anything. */
|
|
86
179
|
plan(cmd: Command): Promise<Plan>;
|
|
180
|
+
/** True while structural commands are refused. */
|
|
181
|
+
get frozen(): boolean;
|
|
182
|
+
/**
|
|
183
|
+
* Freeze or thaw the host. Frozen, every command in `STRUCTURAL_COMMANDS`
|
|
184
|
+
* (and `plan` of it) is refused with code `refused`; nothing about records
|
|
185
|
+
* changes. The shell freezes the live host while a draft is open.
|
|
186
|
+
*/
|
|
187
|
+
setFrozen(frozen: boolean): void;
|
|
188
|
+
private refuseIfFrozen;
|
|
87
189
|
/**
|
|
88
190
|
* Apply a command. A risky one throws {@link ApprovalRequiredError} unless
|
|
89
191
|
* `approved` is true, and then nothing has changed.
|
|
@@ -98,6 +200,13 @@ export declare class AppHost extends EventEmitter {
|
|
|
98
200
|
private analyse;
|
|
99
201
|
private assertValid;
|
|
100
202
|
private analyseUndo;
|
|
203
|
+
/**
|
|
204
|
+
* Undo back to `target`: the same step as `undo`, repeated newest first, as
|
|
205
|
+
* one command with one decision. Each step is its own consistent state (its
|
|
206
|
+
* own swap and store write), so a failure part-way leaves the app at the
|
|
207
|
+
* version it had reached, and says so.
|
|
208
|
+
*/
|
|
209
|
+
private analyseUndoTo;
|
|
101
210
|
/** The model and field stored in `table.column` under `def`. */
|
|
102
211
|
private locate;
|
|
103
212
|
/** The chain from the current version back to the root, newest first. */
|
|
@@ -108,6 +217,71 @@ export declare class AppHost extends EventEmitter {
|
|
|
108
217
|
/** Talk to the app. Callers inside the change queue use this; others use onApp. */
|
|
109
218
|
private callApp;
|
|
110
219
|
private onApp;
|
|
220
|
+
/**
|
|
221
|
+
* Wait for the calls already inside the worker to finish. Used inside the
|
|
222
|
+
* change queue, where no new call can start (see {@link onApp}), before a
|
|
223
|
+
* consistent copy of the data is taken.
|
|
224
|
+
*/
|
|
225
|
+
private drainInflight;
|
|
226
|
+
/**
|
|
227
|
+
* Start a draft: copy the live data into `draftDir` (a directory that must
|
|
228
|
+
* not exist yet; this call creates it) and return a second host over the
|
|
229
|
+
* copy, with `autoApprove` on. The copy is a `VACUUM INTO` taken in the
|
|
230
|
+
* change queue with no call in flight, so it is one consistent state; live is
|
|
231
|
+
* only read. If anything fails, the directory this call created is removed.
|
|
232
|
+
* Only an ordinary (live) host can fork. Live may keep serving records while
|
|
233
|
+
* the draft is open; the caller freezes live structure with `setFrozen`.
|
|
234
|
+
*/
|
|
235
|
+
forkDraft(draftDir: string, opts: ForkDraftOptions): Promise<AppHost>;
|
|
236
|
+
/**
|
|
237
|
+
* Throw this draft away: stop its app and delete its directory (only a
|
|
238
|
+
* directory holding the draft marker is ever deleted). The caller discards
|
|
239
|
+
* the draft's version store.
|
|
240
|
+
*/
|
|
241
|
+
discard(): Promise<void>;
|
|
242
|
+
/**
|
|
243
|
+
* This draft's versions since its base, oldest first, with the base record.
|
|
244
|
+
* What a publish takes from the draft.
|
|
245
|
+
*/
|
|
246
|
+
draftChain(): Promise<DraftChain>;
|
|
247
|
+
/** Checks shared by preview and publish; runs inside the change queue. */
|
|
248
|
+
private publishInputs;
|
|
249
|
+
/** The version numbers a publish of `chain` would give: after everything live's store holds. */
|
|
250
|
+
private nextNumbers;
|
|
251
|
+
/**
|
|
252
|
+
* Replay the draft's versions, in order, onto the copy of live in `dir`: one
|
|
253
|
+
* short worker per version, with that version's stored definition and steps
|
|
254
|
+
* and the number it will have in live (archives are keyed by it). Ends by
|
|
255
|
+
* writing the result as one standalone file, `dir/final/app.db`. Touches
|
|
256
|
+
* nothing but `dir`.
|
|
257
|
+
*/
|
|
258
|
+
private replay;
|
|
259
|
+
/**
|
|
260
|
+
* What publishing `draft` would do: the draft's versions replayed on a fresh
|
|
261
|
+
* copy of the CURRENT live data (the live app is not touched and only waits
|
|
262
|
+
* while the copy is taken). `digest` pins the answer for `publishDraft`.
|
|
263
|
+
*/
|
|
264
|
+
previewPublish(draft: AppHost): Promise<PublishPreview>;
|
|
265
|
+
/**
|
|
266
|
+
* Publish `draft` into live. Always needs `approved: true` and the `expect`
|
|
267
|
+
* digest of a preview; refuses if the draft's chain, or the app it started
|
|
268
|
+
* from, is not what that preview was of.
|
|
269
|
+
*
|
|
270
|
+
* It rehearses on a fresh copy of the current live data (so records added
|
|
271
|
+
* since the fork are kept and draft records are not), then swaps the file:
|
|
272
|
+
* the old data is first copied to snapshots/<ts>-0-migrate-prepublish.db,
|
|
273
|
+
* live's files are renamed aside and the rehearsed file renamed into place.
|
|
274
|
+
* The versions land in live as N ordinary versions (numbers after live's
|
|
275
|
+
* highest, summaries tagged with `draftName`), so `undo` / `undoTo` work.
|
|
276
|
+
* Any failure before the store is written puts the old files back (live's
|
|
277
|
+
* `app.db` is byte-for-byte what it was) and leaves the version unchanged.
|
|
278
|
+
* Live records wait (not fail) while this runs. The caller discards the draft.
|
|
279
|
+
*/
|
|
280
|
+
publishDraft(draft: AppHost, opts: PublishOptions): Promise<PublishResult>;
|
|
281
|
+
private persistVersions;
|
|
282
|
+
private doPublish;
|
|
283
|
+
/** Undo a failed publish: put live back exactly as it was and start it again. */
|
|
284
|
+
private abortPublish;
|
|
111
285
|
list(model: string, limit?: number): Promise<Row[]>;
|
|
112
286
|
get(model: string, id: string): Promise<Row>;
|
|
113
287
|
create(model: string, data: Row): Promise<Row>;
|