logisheets-runtime 1.4.0 → 1.6.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/dist/craft.d.ts +29 -6
- package/dist/craft.js +60 -9
- package/dist/enterprise.d.ts +10 -0
- package/dist/enterprise.js +25 -2
- package/package.json +3 -3
package/dist/craft.d.ts
CHANGED
|
@@ -78,15 +78,38 @@ export declare function validateLoadedCrafts(loaded: readonly LoadedCraft[], wb:
|
|
|
78
78
|
* contract as {@link applyCraftRequest}.
|
|
79
79
|
*/
|
|
80
80
|
export declare function applyCraftResponse(loaded: readonly LoadedCraft[], resp: JsonRpcResponse, wb: Workbook): Promise<string[]>;
|
|
81
|
+
/** Application code: a craft's `onValidate` rejected the request's inputs. */
|
|
82
|
+
export declare const RPC_VALIDATION_FAILED = 1001;
|
|
81
83
|
/**
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
84
|
+
* Run one JSON-RPC exchange against a workbook's loaded crafts and return the
|
|
85
|
+
* reply. This is the generic "run a craft's request/response" primitive — a
|
|
86
|
+
* host registers it as an RPC method (or task handler) and never re-implements
|
|
87
|
+
* the exchange. Composes the three hooks in order:
|
|
88
|
+
*
|
|
89
|
+
* 1. {@link applyCraftRequest} — write the request's inputs (`#INVALID_PARAMS` if a craft rejects one),
|
|
90
|
+
* 2. {@link validateLoadedCrafts} — validate them (`#VALIDATION_FAILED` + the violations if any fail),
|
|
91
|
+
* 3. {@link applyCraftResponse} — read the outputs into `result` (`#INTERNAL_ERROR` if a craft errors).
|
|
92
|
+
*
|
|
93
|
+
* The crafts define what the exchange does (via their onRequest/onResponse);
|
|
94
|
+
* this function is entirely craft-agnostic.
|
|
95
|
+
*/
|
|
96
|
+
export declare function runCraftExchange(loaded: readonly LoadedCraft[], wb: Workbook, req: JsonRpcRequest): Promise<JsonRpcResponse>;
|
|
97
|
+
/**
|
|
98
|
+
* An in-memory {@link CraftRegistry}: crafts are registered in-process with
|
|
99
|
+
* their already-imported runtime module, so `importRuntime` returns it directly
|
|
100
|
+
* — no network, bundler, or filesystem. This is the general-purpose registry
|
|
101
|
+
* for a host that knows its crafts up front (embedding, local dev, tests);
|
|
102
|
+
* production may instead back the registry with a real HTTP registry.
|
|
85
103
|
*/
|
|
86
|
-
export declare class
|
|
104
|
+
export declare class MemoryCraftRegistry implements CraftRegistry {
|
|
87
105
|
private readonly entries;
|
|
88
|
-
/**
|
|
89
|
-
|
|
106
|
+
/**
|
|
107
|
+
* Register a craft by id with its runtime module (a {@link CraftRuntime},
|
|
108
|
+
* or a module whose default export is one). `manifest` is optional
|
|
109
|
+
* metadata; the default carries a non-empty `rtJs` so {@link loadCrafts}
|
|
110
|
+
* imports the module instead of skipping the craft as runtime-less.
|
|
111
|
+
*/
|
|
112
|
+
add(craftId: string, module: unknown, manifest?: CraftManifest): this;
|
|
90
113
|
getManifest(craftId: string): Promise<CraftManifest | undefined>;
|
|
91
114
|
importRuntime(craftId: string): Promise<unknown | undefined>;
|
|
92
115
|
}
|
package/dist/craft.js
CHANGED
|
@@ -13,8 +13,9 @@
|
|
|
13
13
|
// imported module *is* the craft's {@link CraftRuntime},
|
|
14
14
|
// 4. call the runtime's `onLoad(state, workbook)` so it can rehydrate.
|
|
15
15
|
//
|
|
16
|
-
// The registry is a seam: production
|
|
17
|
-
//
|
|
16
|
+
// The registry is a seam: production may wire it to a real HTTP registry;
|
|
17
|
+
// embedding hosts, local dev, and tests use the in-process {@link
|
|
18
|
+
// MemoryCraftRegistry}.
|
|
18
19
|
/**
|
|
19
20
|
* The name of the AppData entry the host folds craft/block/interaction state
|
|
20
21
|
* into. Mirrors the envelope written by the browser host on save.
|
|
@@ -164,18 +165,68 @@ export async function applyCraftResponse(loaded, resp, wb) {
|
|
|
164
165
|
}
|
|
165
166
|
return errors;
|
|
166
167
|
}
|
|
168
|
+
// JSON-RPC 2.0 error codes (kept local so this pure-logic module doesn't pull
|
|
169
|
+
// in the HTTP server that also owns them).
|
|
170
|
+
const RPC_INVALID_PARAMS = -32602;
|
|
171
|
+
const RPC_INTERNAL_ERROR = -32603;
|
|
172
|
+
/** Application code: a craft's `onValidate` rejected the request's inputs. */
|
|
173
|
+
export const RPC_VALIDATION_FAILED = 1001;
|
|
174
|
+
function errorResponse(id, code, message, data) {
|
|
175
|
+
return {
|
|
176
|
+
jsonrpc: '2.0',
|
|
177
|
+
id,
|
|
178
|
+
error: data === undefined ? { code, message } : { code, message, data },
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Run one JSON-RPC exchange against a workbook's loaded crafts and return the
|
|
183
|
+
* reply. This is the generic "run a craft's request/response" primitive — a
|
|
184
|
+
* host registers it as an RPC method (or task handler) and never re-implements
|
|
185
|
+
* the exchange. Composes the three hooks in order:
|
|
186
|
+
*
|
|
187
|
+
* 1. {@link applyCraftRequest} — write the request's inputs (`#INVALID_PARAMS` if a craft rejects one),
|
|
188
|
+
* 2. {@link validateLoadedCrafts} — validate them (`#VALIDATION_FAILED` + the violations if any fail),
|
|
189
|
+
* 3. {@link applyCraftResponse} — read the outputs into `result` (`#INTERNAL_ERROR` if a craft errors).
|
|
190
|
+
*
|
|
191
|
+
* The crafts define what the exchange does (via their onRequest/onResponse);
|
|
192
|
+
* this function is entirely craft-agnostic.
|
|
193
|
+
*/
|
|
194
|
+
export async function runCraftExchange(loaded, wb, req) {
|
|
195
|
+
const id = req.id ?? null;
|
|
196
|
+
const reqErrors = await applyCraftRequest(loaded, req, wb);
|
|
197
|
+
if (reqErrors.length)
|
|
198
|
+
return errorResponse(id, RPC_INVALID_PARAMS, reqErrors.join('; '));
|
|
199
|
+
const violations = await validateLoadedCrafts(loaded, wb);
|
|
200
|
+
if (violations.length)
|
|
201
|
+
return errorResponse(id, RPC_VALIDATION_FAILED, 'validation failed', violations);
|
|
202
|
+
const resp = { jsonrpc: '2.0', id };
|
|
203
|
+
const respErrors = await applyCraftResponse(loaded, resp, wb);
|
|
204
|
+
if (respErrors.length)
|
|
205
|
+
return errorResponse(id, RPC_INTERNAL_ERROR, respErrors.join('; '));
|
|
206
|
+
return resp;
|
|
207
|
+
}
|
|
167
208
|
/**
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
209
|
+
* An in-memory {@link CraftRegistry}: crafts are registered in-process with
|
|
210
|
+
* their already-imported runtime module, so `importRuntime` returns it directly
|
|
211
|
+
* — no network, bundler, or filesystem. This is the general-purpose registry
|
|
212
|
+
* for a host that knows its crafts up front (embedding, local dev, tests);
|
|
213
|
+
* production may instead back the registry with a real HTTP registry.
|
|
171
214
|
*/
|
|
172
|
-
export class
|
|
215
|
+
export class MemoryCraftRegistry {
|
|
173
216
|
constructor() {
|
|
174
217
|
this.entries = new Map();
|
|
175
218
|
}
|
|
176
|
-
/**
|
|
177
|
-
|
|
178
|
-
|
|
219
|
+
/**
|
|
220
|
+
* Register a craft by id with its runtime module (a {@link CraftRuntime},
|
|
221
|
+
* or a module whose default export is one). `manifest` is optional
|
|
222
|
+
* metadata; the default carries a non-empty `rtJs` so {@link loadCrafts}
|
|
223
|
+
* imports the module instead of skipping the craft as runtime-less.
|
|
224
|
+
*/
|
|
225
|
+
add(craftId, module, manifest) {
|
|
226
|
+
this.entries.set(craftId, {
|
|
227
|
+
manifest: manifest ?? { rtJs: craftId, html: '' },
|
|
228
|
+
module,
|
|
229
|
+
});
|
|
179
230
|
return this;
|
|
180
231
|
}
|
|
181
232
|
getManifest(craftId) {
|
package/dist/enterprise.d.ts
CHANGED
|
@@ -51,6 +51,16 @@ export interface TaskContext {
|
|
|
51
51
|
}
|
|
52
52
|
/** A task handler runs one `rpcCall` against an ephemerally-loaded workbook. */
|
|
53
53
|
export type TaskHandler = (ctx: TaskContext) => unknown | Promise<unknown>;
|
|
54
|
+
/**
|
|
55
|
+
* The standard `compute` task handler: drive the workbook's loaded crafts
|
|
56
|
+
* through one JSON-RPC exchange (see {@link runCraftExchange}). Generic and
|
|
57
|
+
* craft-agnostic — the crafts loaded from the workbook's AppData define what
|
|
58
|
+
* "compute" does via their `onRequest`/`onResponse`; a runtime serving craft
|
|
59
|
+
* workbooks uses this instead of hand-writing the exchange. `ctx.params` is
|
|
60
|
+
* forwarded verbatim as the request params (e.g. `{inputs}`) and the JSON-RPC
|
|
61
|
+
* response envelope is returned.
|
|
62
|
+
*/
|
|
63
|
+
export declare const craftComputeHandler: TaskHandler;
|
|
54
64
|
export interface EnterpriseServerOptions {
|
|
55
65
|
runtime: SpreadsheetRuntime;
|
|
56
66
|
/** Registry to load crafts from. Set after registration via {@link setRegistry}. */
|
package/dist/enterprise.js
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
// depend on the merged data-gateway craft + engine cell ops (DATA_GATEWAY_CHANGES).
|
|
16
16
|
import { createServer, } from 'node:http';
|
|
17
17
|
import { SpreadsheetRuntime } from './index.js';
|
|
18
|
-
import { loadCrafts, } from './craft.js';
|
|
18
|
+
import { loadCrafts, runCraftExchange, } from './craft.js';
|
|
19
19
|
import { WorkbookWatcher } from './watcher.js';
|
|
20
20
|
export class ControlPlaneClient {
|
|
21
21
|
constructor(opts) {
|
|
@@ -102,6 +102,24 @@ export class HttpCraftRegistry {
|
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* The standard `compute` task handler: drive the workbook's loaded crafts
|
|
107
|
+
* through one JSON-RPC exchange (see {@link runCraftExchange}). Generic and
|
|
108
|
+
* craft-agnostic — the crafts loaded from the workbook's AppData define what
|
|
109
|
+
* "compute" does via their `onRequest`/`onResponse`; a runtime serving craft
|
|
110
|
+
* workbooks uses this instead of hand-writing the exchange. `ctx.params` is
|
|
111
|
+
* forwarded verbatim as the request params (e.g. `{inputs}`) and the JSON-RPC
|
|
112
|
+
* response envelope is returned.
|
|
113
|
+
*/
|
|
114
|
+
export const craftComputeHandler = (ctx) => {
|
|
115
|
+
const req = {
|
|
116
|
+
jsonrpc: '2.0',
|
|
117
|
+
id: 1,
|
|
118
|
+
method: 'compute',
|
|
119
|
+
params: ctx.params,
|
|
120
|
+
};
|
|
121
|
+
return runCraftExchange(ctx.crafts, ctx.workbook, req);
|
|
122
|
+
};
|
|
105
123
|
export class EnterpriseRuntimeServer {
|
|
106
124
|
constructor(opts) {
|
|
107
125
|
this.handlers = new Map();
|
|
@@ -110,6 +128,9 @@ export class EnterpriseRuntimeServer {
|
|
|
110
128
|
this.runtime = opts.runtime;
|
|
111
129
|
this.registry = opts.registry;
|
|
112
130
|
this.secret = opts.secret;
|
|
131
|
+
// Ship a generic `compute` by default so a craft-serving runtime needs
|
|
132
|
+
// no bespoke handler; anything in opts.taskHandlers can override it.
|
|
133
|
+
this.handlers.set('compute', craftComputeHandler);
|
|
113
134
|
for (const [name, h] of Object.entries(opts.taskHandlers ?? {}))
|
|
114
135
|
this.handlers.set(name, h);
|
|
115
136
|
}
|
|
@@ -211,7 +232,9 @@ export class EnterpriseRuntimeServer {
|
|
|
211
232
|
throw new Error(`unknown rpcCall: ${body.rpcCall}`);
|
|
212
233
|
const wb = await this.loadFromUrl(body.workbookUrl);
|
|
213
234
|
try {
|
|
214
|
-
const crafts = this.registry
|
|
235
|
+
const crafts = this.registry
|
|
236
|
+
? await loadCrafts(wb, this.registry)
|
|
237
|
+
: [];
|
|
215
238
|
return await handler({ workbook: wb, crafts, params: body.params });
|
|
216
239
|
}
|
|
217
240
|
finally {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "logisheets-runtime",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "Headless LogiSheets spreadsheet runtime for Node — logisheets-core wired to the Node WASM engine. The Node counterpart of the browser app.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
"author": "Jeremy He",
|
|
19
19
|
"license": "MIT",
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"logisheets": "^1.
|
|
22
|
-
"logisheets-core": "^1.
|
|
21
|
+
"logisheets": "^1.6.0",
|
|
22
|
+
"logisheets-core": "^1.6.0"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"typescript": "^5.5.0",
|