@almadar/shell-node 0.0.0-stage → 1.0.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.
@@ -0,0 +1,28 @@
1
+ import { EffectHandlers } from '@almadar/shell-runtime';
2
+
3
+ /**
4
+ * Program host handlers — server-side only.
5
+ *
6
+ * Backs the language trio's effects (behavior/catalog|describe|source,
7
+ * program/read|print|eval) with the one host implementation in
8
+ * `host/program`, which runs the installed `orb` from the
9
+ * project directory. Its workspaces are `std-workspace` run by the kernel
10
+ * (`kernelWorkspaceHost`). Wired by OrbitalServerRuntime
11
+ * (lazy import, merged under user handlers).
12
+ *
13
+ * NOT exported from the main index.ts because it spawns processes.
14
+ *
15
+ * @packageDocumentation
16
+ */
17
+
18
+ interface ProgramHostConfig {
19
+ /** Project root `orb` resolves behavior packages from. */
20
+ cwd: string;
21
+ /** Explicit `orb` binary; defaults to the host's resolution order. */
22
+ orbBin?: string;
23
+ }
24
+ type ProgramHandlers = Pick<EffectHandlers, 'programEffect'>;
25
+ declare function createProgramHandlers(host: ProgramHostConfig): ProgramHandlers;
26
+
27
+ export { createProgramHandlers };
28
+ export type { ProgramHandlers, ProgramHostConfig };
@@ -0,0 +1,6 @@
1
+ export { createProgramHandlers } from './chunk-7QG7JGKQ.js';
2
+ import './chunk-YLFK5OJN.js';
3
+ import './chunk-VQVAHUAX.js';
4
+ import './chunk-EEYGL7AB.js';
5
+ import './chunk-7QE32QQR.js';
6
+ import './chunk-BWMLDDCL.js';
@@ -0,0 +1,301 @@
1
+ import { CredentialVaultModule, CredentialVault, CredentialRowsPort, OsAnchors, OsGrantPort, OsHostPort, TokenEndpointPost, KernelModule, NativeMemoryPersistenceAdapter, ServiceCallContext, StepRequest, PlayedStep, ProgramValidationReport, ProgramEffectName, ProgramEffectOutcome } from '@almadar/shell-runtime';
2
+ import { JsonValue, KernelMemoryStoreExports, OrbitalSchema, EvaluatedProgram, ProgramValidationIssue, CatalogScope, CatalogEntry, DependencyGraph, TraitValue, TraitDescription, DescribeTarget, BehaviorDescription } from '@almadar/core';
3
+ export { ClientEffectTuple, ClientNavigateBackTuple, ClientNavigateTuple, ClientRenderUITuple, EventRequest, EventResponse, TransitionRejection } from '@almadar/core';
4
+ import { RequestOptions } from 'node:https';
5
+ import { IncomingMessage, ClientRequest } from 'node:http';
6
+ import { OsGrantsReport, ProveReport, StepRefusal } from '@almadar/core/kernel-wire';
7
+ import { LayoutStrategy } from '@almadar/core/builders';
8
+ export { K as KernelProgram, a as KernelProgramOptions, b as KernelWorkspaceBucket, c as KernelWorkspaceHostOptions, P as ProgramRun, W as WorkspaceHost, d as WorkspacePort, e as emitPayload, i as installWorkspaceHost, f as installedWorkspaceHost, k as kernelWorkspaceHost, r as resolveProgramFile, u as uninstallWorkspaceHost, w as workspaceBucketFromEnv, g as workspaceOwnerSegment, h as workspacesRoot } from './kernel-workspace-host-cT9JGJur.js';
9
+ export { HostDispatchItem, LiveBroadcastItem, LoaderConfig, OrbitalServerRuntime, OrbitalServerRuntimeConfig, PushItem, RegisterOptions, RegisteredOrbital, RuntimeOrbital, RuntimeOrbitalSchema, UnresolvedProgramError } from './OrbitalServerRuntime.js';
10
+ export { ProgramHandlers, ProgramHostConfig, createProgramHandlers } from './createProgramHandlers.js';
11
+
12
+ /** Install the Node ports `@almadar/shell-runtime` leaves open: per-tenant credential scopes. Idempotent. */
13
+ declare function installNodeHostPorts(): void;
14
+ interface NodeCredentialVaultOptions {
15
+ /** The kernel module that carries the vault class (`@almadar/kernel`, initialised). */
16
+ kernel: CredentialVaultModule;
17
+ /** Defaults to the process env, which the native host reads the same keys and file from. */
18
+ env?: Record<string, string | undefined>;
19
+ /** Defaults to the working directory, where the native host looks for `.almadar/dev-credentials.json`. */
20
+ cwd?: string;
21
+ }
22
+ /**
23
+ * The durable credential vault of a Node host, as the native host opens it: the key in `ALMADAR_CREDENTIAL_MASTER_KEY`
24
+ * (and `..._PREVIOUS` in a rotation window), the rows in the encrypted file `ALMADAR_CREDENTIALS_FILE` names, else
25
+ * `.almadar/dev-credentials.json`, so one file serves both hosts. With no master key set the host has no store and this
26
+ * answers `null`; a key that is not 64 hex digits, or a row the kernel cannot read, rejects. The vault is installed as the
27
+ * process-wide credential store.
28
+ */
29
+ declare function installNodeCredentialVault(options: NodeCredentialVaultOptions): Promise<CredentialVault | null>;
30
+
31
+ declare const DEFAULT_CREDENTIALS_FILE = ".almadar/dev-credentials.json";
32
+ /** `ALMADAR_CREDENTIALS_FILE`, else `.almadar/dev-credentials.json` under the working directory, as the native host resolves it. */
33
+ declare function credentialsFilePath(env?: Record<string, string | undefined>, cwd?: string): string;
34
+ /**
35
+ * The kernel's credential rows kept in a JSON file, the layout the native host writes: an array of rows each carrying an
36
+ * `id`, written whole through a temporary file and a rename. A missing file is no rows; a file that cannot be read, is not
37
+ * JSON, is not an array of rows or holds a row without an id is a failure. Operations here run one after another, so the
38
+ * conditional delete is atomic against every other port over the same file in this process; a second process is not
39
+ * covered, as with the native host.
40
+ */
41
+ declare function fileCredentialRows(path: string): CredentialRowsPort;
42
+
43
+ /**
44
+ * A development host's credential-store master key: read from `<dir>/dev-credential-master-key`,
45
+ * generated there (owner-only) on first use. Production must hold its key in the platform secret
46
+ * store, so this refuses under NODE_ENV=production.
47
+ */
48
+ declare function loadOrCreateDevMasterKey(dir: string): string;
49
+
50
+ /** One program instance's `os` host: its own handle tables, closed by `close`. */
51
+ interface NodeOsHostOptions {
52
+ /** Where `~/` and `./` and the declared grant anchors are placed; absent, the process's own (`OsAnchors::of_process`). */
53
+ anchors?: OsAnchors;
54
+ }
55
+ /** The process's home, working directory, executable directory and environment, read once (`OsAnchors::of_process`). */
56
+ declare function processAnchors(): OsAnchors;
57
+ declare function createNodeOsHost(grants: OsGrantPort, options?: NodeOsHostOptions): OsHostPort;
58
+
59
+ type KvErrorKind = 'denied' | 'invalid' | 'io' | 'timeout' | 'unsupported' | 'closed';
60
+ type KvResult<T> = {
61
+ ok: true;
62
+ value: T;
63
+ } | {
64
+ ok: false;
65
+ error: string;
66
+ kind: KvErrorKind;
67
+ };
68
+ interface KvNamespace {
69
+ app: string;
70
+ locale: string;
71
+ store: string;
72
+ }
73
+ interface KvScanEntry {
74
+ key: string;
75
+ value: JsonValue;
76
+ }
77
+ type KvBatchOp = {
78
+ kind: 'put';
79
+ key: string;
80
+ value: JsonValue;
81
+ } | {
82
+ kind: 'delete';
83
+ key: string;
84
+ };
85
+ declare class KvFailure extends Error {
86
+ readonly kind: KvErrorKind;
87
+ constructor(kind: KvErrorKind, message: string);
88
+ }
89
+ /** A namespace's whole content, key order = UTF-8 byte order. Backends load and commit it whole. */
90
+ type KvTable = Map<string, JsonValue>;
91
+ interface KvBackend {
92
+ load(ns: KvNamespace): Promise<KvTable>;
93
+ /** Replace the namespace content atomically: a failure leaves the previous content. */
94
+ commit(ns: KvNamespace, table: KvTable): Promise<void>;
95
+ }
96
+ declare function memoryKvBackend(): KvBackend;
97
+ declare function directoryKvBackend(stateDir: string): KvBackend;
98
+ interface KvHost {
99
+ get(ns: KvNamespace, key: string): Promise<KvResult<{
100
+ found: boolean;
101
+ value?: JsonValue;
102
+ }>>;
103
+ put(ns: KvNamespace, key: string, value: JsonValue): Promise<KvResult<{
104
+ storedKey: string;
105
+ }>>;
106
+ delete(ns: KvNamespace, key: string): Promise<KvResult<{
107
+ existed: boolean;
108
+ }>>;
109
+ scan(ns: KvNamespace, prefix: string, limit?: number): Promise<KvResult<{
110
+ entries: KvScanEntry[];
111
+ }>>;
112
+ batch(ns: KvNamespace, ops: readonly KvBatchOp[]): Promise<KvResult<{
113
+ applied: number;
114
+ }>>;
115
+ }
116
+ declare function createKvHost(backend: KvBackend): KvHost;
117
+
118
+ interface ResolvedAddress {
119
+ address: string;
120
+ family: number;
121
+ }
122
+ interface NodeTokenPostOptions {
123
+ /** The kernel's egress rule over the program's declared origins. */
124
+ egressAllows(url: string): boolean;
125
+ /** The kernel's public-address rule. */
126
+ isPublicAddress(ip: string): boolean;
127
+ request?: (options: RequestOptions, onResponse: (response: IncomingMessage) => void) => ClientRequest;
128
+ resolve?: (host: string) => Promise<ResolvedAddress[]>;
129
+ }
130
+ declare class TokenPostRefusal extends Error {
131
+ constructor(message: string);
132
+ }
133
+ declare function createNodeTokenPost(options: NodeTokenPostOptions): TokenEndpointPost;
134
+
135
+ /** The whole server kernel as Node loads it: the shared module plus the server-only exports the client build omits. */
136
+ interface LoadedKernel extends KernelModule, KernelMemoryStoreExports {
137
+ initSync(input: {
138
+ module: Uint8Array;
139
+ }): object;
140
+ }
141
+ /** A row store the kernel itself holds (its `MemoryStore`), as a host keeps rows with no database. */
142
+ declare function nativeMemoryOf(kernel: KernelModule): NativeMemoryPersistenceAdapter;
143
+ interface LoadKernelOptions {
144
+ /** Where the installed `@almadar/core` and `@almadar/std` are found, node-style. Required: the caller names its own root. */
145
+ languageFrom: string;
146
+ }
147
+ /**
148
+ * The kernel of the `@almadar/kernel` package this host installs, loaded once per process with the language data
149
+ * the packages under `languageFrom` declare. A host with no package installed rejects, never falls back.
150
+ */
151
+ declare function loadInstalledKernel(languageFrom: string): Promise<LoadedKernel>;
152
+ /**
153
+ * Loads the kernel from `kernelDir` (the one with `pkg/`) and installs the language data the installed packages declare
154
+ * (the operator table that routes `program/*` and the other host effects), as `orb` reads it. The first install in a
155
+ * process wins.
156
+ */
157
+ declare function loadKernelModule(kernelDir: string, options: LoadKernelOptions): Promise<LoadedKernel>;
158
+ /**
159
+ * Where the native kernel add-on is: `ALMADAR_KERNEL_NODE`, else `kernel.node` in the `@almadar/orb-<platform>-<arch>`
160
+ * package this platform installs. Rejects when neither exists, so a host can fall back to the wasm kernel by choice.
161
+ */
162
+ declare function resolveNativeAddon(from: string): string;
163
+ /**
164
+ * Loads the native kernel add-on (the same API as the wasm module, run as machine code) and installs the language data
165
+ * the installed packages declare. The first install in a process wins.
166
+ */
167
+ declare function loadNativeKernelModule(addonPath: string, options: LoadKernelOptions): Promise<LoadedKernel>;
168
+
169
+ /**
170
+ * The host side of the language trio's server effects (`behavior/catalog`,
171
+ * `behavior/describe`, `behavior/source`, `behavior/resolve`, `behavior/dependencies`, `program/read|print|eval`): a thin
172
+ * caller that runs the installed `orb` from the project directory and parses its
173
+ * JSON. All behavior resolution, reflection, printing and validation is `orb`'s;
174
+ * nothing here re-implements it. Both execution paths (the interpreter's effect
175
+ * executor and the compiled server host) call this module.
176
+ *
177
+ * @packageDocumentation
178
+ */
179
+
180
+ /** Where the host runs `orb`: the project (workspace) root, so installed behavior packages resolve. */
181
+ interface ProgramHostOptions {
182
+ readonly cwd: string;
183
+ /** Explicit `orb` binary; defaults to {@link resolveOrbBinary}. */
184
+ readonly orbBin?: string;
185
+ }
186
+ /** `behavior/catalog`: installed behaviors under registry folders; `traits: false` omits each entry's traits (`--no-traits`). */
187
+ declare function behaviorCatalog(scope: CatalogScope, opts: ProgramHostOptions, options?: {
188
+ readonly traits?: boolean;
189
+ }): Promise<CatalogEntry[]>;
190
+ /** {@link behaviorCatalog} for a caller that cannot await. */
191
+ declare function behaviorCatalogSync(scope: CatalogScope, opts: ProgramHostOptions): CatalogEntry[];
192
+ /** `behavior/describe`: a trait value's knobs and events; an orbital value's or a whole behavior's orbitals. */
193
+ declare function behaviorDescribe(value: TraitValue, opts: ProgramHostOptions): Promise<TraitDescription>;
194
+ declare function behaviorDescribe(value: DescribeTarget, opts: ProgramHostOptions): Promise<TraitDescription | BehaviorDescription>;
195
+ /** `behavior/source`: the behavior a value belongs to, as a program (`.orb` IR). */
196
+ declare function behaviorSource(value: Pick<TraitValue, 'behavior'>, opts: ProgramHostOptions): Promise<OrbitalSchema>;
197
+ /** `program/read`: `.lolo` text → a program (`.orb` IR). */
198
+ declare function programRead(text: string, opts: ProgramHostOptions): Promise<OrbitalSchema>;
199
+ /** `program/print`: a program (`.orb` IR) → `.lolo` text. */
200
+ declare function programPrint(program: OrbitalSchema, opts: ProgramHostOptions): Promise<string>;
201
+ /** What `program/os-grants` returns: the static `os/` grant check's typed findings. */
202
+ type ProgramOsGrantsReport = OsGrantsReport;
203
+ /** What `program/prove` returns: every spec proposition the program cannot be shown to keep. */
204
+ type ProgramProveReport = ProveReport;
205
+ /** Who runs a program effect: the signed-in caller, as `call-service` receives it. */
206
+ type ProgramCallContext = Pick<ServiceCallContext, 'principal' | 'role'>;
207
+ /**
208
+ * A workspace-backed specifier (`./<workspaces root>/<owner>/…`) is readable
209
+ * only by its owner; every other specifier is unaffected. Returns the refusal,
210
+ * or undefined when the caller may read it.
211
+ */
212
+ declare function workspaceAccessError(behavior: string, opts: ProgramHostOptions, context: ProgramCallContext | undefined): string | undefined;
213
+ /** Options for {@link programEval}. */
214
+ interface ProgramEvalOptions extends ProgramHostOptions {
215
+ /** Write into this workspace (opened as `context`'s caller) and snapshot it; without one, into `<cwd>/orbitals`. */
216
+ readonly workspace?: {
217
+ readonly id: string;
218
+ readonly context: ProgramCallContext;
219
+ };
220
+ /** A prior version of the program whose ids the re-evaluation keeps. */
221
+ readonly prior?: OrbitalSchema;
222
+ /** The program is a finished app (a generation concludes here): `orb validate --final`. */
223
+ readonly final?: boolean;
224
+ }
225
+ type ProgramEvalResult = {
226
+ readonly ok: true;
227
+ readonly result: EvaluatedProgram;
228
+ } | {
229
+ readonly ok: false;
230
+ readonly errors: readonly ProgramValidationIssue[];
231
+ };
232
+ /**
233
+ * `program/validate`: the same stamp-and-validate call as `program/eval`, at the same
234
+ * 0-error / 0-warning bar, in a temp directory that is removed after. Nothing is written
235
+ * to the workspace or the project.
236
+ */
237
+ declare function programValidate(program: OrbitalSchema, opts: Pick<ProgramEvalOptions, 'cwd' | 'prior' | 'final'> & ProgramHostOptions): Promise<ProgramValidationReport>;
238
+ /**
239
+ * `program/prove`: the kernel prover's findings on a program, asked of `orb prove` (the prover is the kernel's, so the
240
+ * node host carries no copy of it). A program value is written to a temp file and resolved like any file; `path` names
241
+ * the file directly.
242
+ */
243
+ declare function programProve(target: {
244
+ readonly program: OrbitalSchema;
245
+ } | {
246
+ readonly path: string;
247
+ }, opts: ProgramHostOptions): Promise<ProgramProveReport>;
248
+ /**
249
+ * `program/os-grants`: the kernel's static `os/` grant findings on a program, asked of `orb os-grants` (the check is the
250
+ * kernel's, so the node host carries no copy of it). `final` checks the finished program, where a config-fed target is a
251
+ * literal. A program value is written to a temp file and resolved like any file; `path` names the file directly.
252
+ */
253
+ declare function programOsGrants(target: {
254
+ readonly program: OrbitalSchema;
255
+ } | {
256
+ readonly path: string;
257
+ }, opts: ProgramHostOptions & {
258
+ readonly final?: boolean;
259
+ }): Promise<ProgramOsGrantsReport>;
260
+ /**
261
+ * `program/eval`: validate through {@link stampAndValidate}, and on success write the program to `orbitals/<name>.orb` in its workspace (or `cwd`)
262
+ * and return its traits as values. Failures come back as data.
263
+ */
264
+ declare function programEval(program: OrbitalSchema, opts: ProgramEvalOptions): Promise<ProgramEvalResult>;
265
+ /** Options for {@link programCompose}. */
266
+ interface ProgramComposeOptions {
267
+ readonly appName: string;
268
+ readonly layout?: LayoutStrategy;
269
+ /** The app's theme (a built-in theme name), set on the composed program. */
270
+ readonly theme?: string;
271
+ }
272
+ /**
273
+ * `program/compose`: assemble programs, in the order given, into one app
274
+ * program. The composer is `orb compose` (the Rust composer, `program/compose`
275
+ * of the kernel's program host): the programs are written to a scratch folder
276
+ * and handed to it, and its JSON is the answer. Pages, layout, wiring, the app
277
+ * nav and every registry read are `orb`'s.
278
+ */
279
+ declare function programCompose(programs: readonly OrbitalSchema[], options: ProgramComposeOptions, opts: ProgramHostOptions): Promise<OrbitalSchema>;
280
+ /** A program file resolved by `orb` (every import flattened, every composed trait inlined): what a host registers. */
281
+ declare function programResolve(path: string, opts: ProgramHostOptions): Promise<OrbitalSchema>;
282
+ /** An unresolved program resolved by `orb` from the project root `opts.cwd`, so its relative imports resolve there. */
283
+ declare function programResolveSchema(program: OrbitalSchema, opts: ProgramHostOptions): Promise<OrbitalSchema>;
284
+ /** `behavior/resolve`: the behavior a value belongs to, resolved by `orb` — every import and composed trait inlined, as the runtime runs it. */
285
+ declare function behaviorResolve(value: Pick<TraitValue, 'behavior'>, opts: ProgramHostOptions): Promise<OrbitalSchema | {
286
+ error: string;
287
+ }>;
288
+ /** `behavior/dependencies`: a program's dependency graph — each orbital's imports, then every installed behavior they reach, with its layer and its own imports. */
289
+ declare function behaviorDependencies(program: OrbitalSchema, opts: ProgramHostOptions): Promise<DependencyGraph>;
290
+ declare function programStep(program: OrbitalSchema, requests: readonly StepRequest[], opts: ProgramHostOptions, live?: boolean): Promise<PlayedStep[] | StepRefusal>;
291
+ type EffectArg = JsonValue;
292
+ /**
293
+ * Parse one program effect's positional arguments (the trailing emit config
294
+ * already removed) and run it. Both execution paths call this — the
295
+ * interpreter's effect executor and the compiled server handlers — so argument
296
+ * validation has one owner. Failures come back as data, never thrown.
297
+ */
298
+ declare function runProgramEffect(op: ProgramEffectName, args: readonly EffectArg[], opts: ProgramHostOptions, context?: ProgramCallContext): Promise<ProgramEffectOutcome>;
299
+
300
+ export { DEFAULT_CREDENTIALS_FILE, KvFailure, TokenPostRefusal, behaviorCatalog, behaviorCatalogSync, behaviorDependencies, behaviorDescribe, behaviorResolve, behaviorSource, createKvHost, createNodeOsHost, createNodeTokenPost, credentialsFilePath, directoryKvBackend, fileCredentialRows, installNodeCredentialVault, installNodeHostPorts, loadInstalledKernel, loadKernelModule, loadNativeKernelModule, loadOrCreateDevMasterKey, memoryKvBackend, nativeMemoryOf, processAnchors, programCompose, programEval, programOsGrants, programPrint, programProve, programRead, programResolve, programResolveSchema, programStep, programValidate, resolveNativeAddon, runProgramEffect, workspaceAccessError };
301
+ export type { KvBackend, KvBatchOp, KvErrorKind, KvHost, KvNamespace, KvResult, LoadKernelOptions, LoadedKernel, NodeCredentialVaultOptions, NodeOsHostOptions, NodeTokenPostOptions, ProgramCallContext, ProgramComposeOptions, ProgramEvalOptions, ProgramEvalResult, ProgramHostOptions, ProgramOsGrantsReport, ProgramProveReport, ResolvedAddress };
package/dist/index.js ADDED
@@ -0,0 +1,217 @@
1
+ export { OrbitalServerRuntime, UnresolvedProgramError } from './chunk-3PNZYEZQ.js';
2
+ export { createProgramHandlers } from './chunk-7QG7JGKQ.js';
3
+ export { KernelProgram, createNodeOsHost, emitPayload, kernelWorkspaceHost, processAnchors, resolveProgramFile, workspaceBucketFromEnv, workspacesRoot } from './chunk-YLFK5OJN.js';
4
+ export { DEFAULT_CREDENTIALS_FILE, TokenPostRefusal, createNodeTokenPost, credentialsFilePath, fileCredentialRows, installNodeCredentialVault, installNodeHostPorts } from './chunk-VQVAHUAX.js';
5
+ export { loadInstalledKernel, loadKernelModule, loadNativeKernelModule, nativeMemoryOf, resolveNativeAddon } from './chunk-EEYGL7AB.js';
6
+ export { behaviorCatalog, behaviorCatalogSync, behaviorDependencies, behaviorDescribe, behaviorResolve, behaviorSource, programCompose, programEval, programOsGrants, programPrint, programProve, programRead, programResolve, programResolveSchema, programStep, programValidate, runProgramEffect, workspaceAccessError } from './chunk-7QE32QQR.js';
7
+ export { installWorkspaceHost, installedWorkspaceHost, uninstallWorkspaceHost, workspaceOwnerSegment } from './chunk-BWMLDDCL.js';
8
+ import { randomBytes } from 'crypto';
9
+ import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'fs';
10
+ import { join } from 'path';
11
+ import { mkdir, open, rename, rm, readFile } from 'fs/promises';
12
+
13
+ var KEY_FILE = "dev-credential-master-key";
14
+ var HEX_KEY = /^[0-9a-f]{64}$/;
15
+ function loadOrCreateDevMasterKey(dir) {
16
+ if (process.env.NODE_ENV === "production") {
17
+ throw new Error("A generated dev master key is not permitted when NODE_ENV=production \u2014 set ALMADAR_CREDENTIAL_MASTER_KEY");
18
+ }
19
+ const path = join(dir, KEY_FILE);
20
+ if (existsSync(path)) {
21
+ const key2 = readFileSync(path, "utf8").trim();
22
+ if (!HEX_KEY.test(key2)) throw new Error(`${path} is malformed (expected 64 hex characters)`);
23
+ return key2;
24
+ }
25
+ mkdirSync(dir, { recursive: true });
26
+ const key = randomBytes(32).toString("hex");
27
+ writeFileSync(path, `${key}
28
+ `, { mode: 384, flag: "wx" });
29
+ return key;
30
+ }
31
+ var KvFailure = class extends Error {
32
+ constructor(kind, message) {
33
+ super(message);
34
+ this.kind = kind;
35
+ }
36
+ kind;
37
+ };
38
+ var LONE_SURROGATE = /[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]/;
39
+ var SEGMENT = /^[A-Za-z0-9._-]+$/;
40
+ function namespaceId(ns) {
41
+ return `kv:${ns.app}:${ns.locale}:${ns.store}`;
42
+ }
43
+ function checkNamespace(ns) {
44
+ for (const [what, seg] of [["app", ns.app], ["locale", ns.locale], ["store", ns.store]]) {
45
+ if (!SEGMENT.test(seg) || seg === "." || seg === "..") throw new KvFailure("invalid", `kv ${what} name ${JSON.stringify(seg)} is not [A-Za-z0-9._-]+`);
46
+ }
47
+ }
48
+ function checkString(what, s) {
49
+ if (typeof s !== "string") throw new KvFailure("invalid", `kv ${what} must be a string`);
50
+ if (LONE_SURROGATE.test(s)) throw new KvFailure("invalid", `kv ${what} is not valid UTF-8`);
51
+ return s;
52
+ }
53
+ function checkJson(value, path) {
54
+ if (value === null || typeof value === "string" || typeof value === "boolean") return value;
55
+ if (typeof value === "number") {
56
+ if (!Number.isFinite(value)) throw new KvFailure("invalid", `kv value at ${path} is not a finite number`);
57
+ return value;
58
+ }
59
+ if (Array.isArray(value)) return value.map((item, i) => checkJson(item, `${path}[${i}]`));
60
+ if (typeof value === "object" && Object.getPrototypeOf(value) === Object.prototype) {
61
+ const out = {};
62
+ for (const [k, v] of Object.entries(value)) Object.defineProperty(out, k, { value: checkJson(v, `${path}.${k}`), enumerable: true, writable: true, configurable: true });
63
+ return out;
64
+ }
65
+ throw new KvFailure("invalid", `kv value at ${path} is not JSON`);
66
+ }
67
+ function byteCompare(a, b) {
68
+ return Buffer.compare(Buffer.from(a, "utf8"), Buffer.from(b, "utf8"));
69
+ }
70
+ function serialize(table) {
71
+ const keys = [...table.keys()].sort(byteCompare);
72
+ return `{${keys.map((k) => `${JSON.stringify(k)}:${JSON.stringify(table.get(k))}`).join(",")}}`;
73
+ }
74
+ function parseTable(text) {
75
+ const parsed = JSON.parse(text);
76
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) throw new KvFailure("io", "kv store file is not a JSON object");
77
+ const table = /* @__PURE__ */ new Map();
78
+ for (const [k, v] of Object.entries(parsed)) table.set(k, checkJson(v, k));
79
+ return table;
80
+ }
81
+ function memoryKvBackend() {
82
+ const tables = /* @__PURE__ */ new Map();
83
+ return {
84
+ async load(ns) {
85
+ return new Map(tables.get(namespaceId(ns)) ?? []);
86
+ },
87
+ async commit(ns, table) {
88
+ tables.set(namespaceId(ns), new Map(table));
89
+ }
90
+ };
91
+ }
92
+ function isErrno(e, code) {
93
+ return typeof e === "object" && e !== null && "code" in e && e.code === code;
94
+ }
95
+ function directoryKvBackend(stateDir) {
96
+ const fileOf = (ns) => {
97
+ const dir = join(stateDir, "kv", ns.app, ns.locale);
98
+ return { dir, file: join(dir, `${ns.store}.json`) };
99
+ };
100
+ return {
101
+ async load(ns) {
102
+ const { file } = fileOf(ns);
103
+ let text;
104
+ try {
105
+ text = await readFile(file, "utf8");
106
+ } catch (e) {
107
+ if (isErrno(e, "ENOENT")) return /* @__PURE__ */ new Map();
108
+ throw new KvFailure("io", `kv read failed: ${e instanceof Error ? e.message : String(e)}`);
109
+ }
110
+ try {
111
+ return parseTable(text);
112
+ } catch (e) {
113
+ if (e instanceof KvFailure) throw e;
114
+ throw new KvFailure("io", `kv store file is corrupt: ${e instanceof Error ? e.message : String(e)}`);
115
+ }
116
+ },
117
+ async commit(ns, table) {
118
+ const { dir, file } = fileOf(ns);
119
+ const tmp = `${file}.tmp-${randomBytes(6).toString("hex")}`;
120
+ try {
121
+ await mkdir(dir, { recursive: true });
122
+ const handle = await open(tmp, "w");
123
+ try {
124
+ await handle.writeFile(serialize(table), "utf8");
125
+ await handle.sync();
126
+ } finally {
127
+ await handle.close();
128
+ }
129
+ await rename(tmp, file);
130
+ } catch (e) {
131
+ await rm(tmp, { force: true }).catch(() => void 0);
132
+ throw new KvFailure("io", `kv write failed: ${e instanceof Error ? e.message : String(e)}`);
133
+ }
134
+ }
135
+ };
136
+ }
137
+ function createKvHost(backend) {
138
+ const tails = /* @__PURE__ */ new Map();
139
+ const serialized = (ns, run) => {
140
+ const id = namespaceId(ns);
141
+ const next = (tails.get(id) ?? Promise.resolve()).then(run, run);
142
+ tails.set(id, next.catch(() => void 0));
143
+ return next;
144
+ };
145
+ const guard = async (run) => {
146
+ try {
147
+ return { ok: true, value: await run() };
148
+ } catch (e) {
149
+ if (e instanceof KvFailure) return { ok: false, error: e.message, kind: e.kind };
150
+ return { ok: false, error: e instanceof Error ? e.message : String(e), kind: "io" };
151
+ }
152
+ };
153
+ return {
154
+ get: (ns, key) => guard(async () => {
155
+ checkNamespace(ns);
156
+ checkString("key", key);
157
+ const table = await backend.load(ns);
158
+ const value = table.get(key);
159
+ return value === void 0 ? { found: false } : { found: true, value };
160
+ }),
161
+ put: (ns, key, value) => guard(() => {
162
+ checkNamespace(ns);
163
+ checkString("key", key);
164
+ const checked = checkJson(value, "value");
165
+ return serialized(ns, async () => {
166
+ const table = await backend.load(ns);
167
+ table.set(key, checked);
168
+ await backend.commit(ns, table);
169
+ return { storedKey: key };
170
+ });
171
+ }),
172
+ delete: (ns, key) => guard(() => {
173
+ checkNamespace(ns);
174
+ checkString("key", key);
175
+ return serialized(ns, async () => {
176
+ const table = await backend.load(ns);
177
+ const existed = table.delete(key);
178
+ if (existed) await backend.commit(ns, table);
179
+ return { existed };
180
+ });
181
+ }),
182
+ scan: (ns, prefix, limit) => guard(async () => {
183
+ checkNamespace(ns);
184
+ checkString("prefix", prefix);
185
+ if (limit !== void 0 && (!Number.isInteger(limit) || limit < 0)) throw new KvFailure("invalid", "kv scan limit must be a non-negative integer");
186
+ const table = await backend.load(ns);
187
+ const wanted = Buffer.from(prefix, "utf8");
188
+ const hits = [...table.keys()].filter((k) => Buffer.from(k, "utf8").subarray(0, wanted.length).equals(wanted)).sort(byteCompare);
189
+ const entries = [];
190
+ for (const key of limit === void 0 ? hits : hits.slice(0, limit)) {
191
+ const value = table.get(key);
192
+ if (value !== void 0) entries.push({ key, value });
193
+ }
194
+ return { entries };
195
+ }),
196
+ batch: (ns, ops) => guard(() => {
197
+ checkNamespace(ns);
198
+ const checked = ops.map((op, i) => {
199
+ const key = checkString(`batch op ${i} key`, op.key);
200
+ if (op.kind === "put") return { kind: "put", key, value: checkJson(op.value, `ops[${i}].value`) };
201
+ if (op.kind === "delete") return { kind: "delete", key };
202
+ throw new KvFailure("invalid", `kv batch op ${i} has unknown kind`);
203
+ });
204
+ return serialized(ns, async () => {
205
+ const table = await backend.load(ns);
206
+ for (const op of checked) {
207
+ if (op.kind === "put") table.set(op.key, op.value);
208
+ else table.delete(op.key);
209
+ }
210
+ await backend.commit(ns, table);
211
+ return { applied: checked.length };
212
+ });
213
+ })
214
+ };
215
+ }
216
+
217
+ export { KvFailure, createKvHost, directoryKvBackend, loadOrCreateDevMasterKey, memoryKvBackend };
@@ -0,0 +1 @@
1
+ export { loadInstalledKernel, loadKernelModule, loadNativeKernelModule, nativeMemoryOf, resolveNativeAddon } from './chunk-EEYGL7AB.js';