logisheets-core 1.1.0 → 1.3.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/index.d.ts +4 -0
- package/dist/craft/index.js +4 -0
- package/dist/craft/manifest.d.ts +10 -0
- package/dist/craft/manifest.js +1 -0
- package/dist/craft/rpc.d.ts +17 -0
- package/dist/craft/rpc.js +8 -0
- package/dist/craft/runtime.d.ts +46 -0
- package/dist/craft/runtime.js +1 -0
- package/dist/craft/state.d.ts +5 -0
- package/dist/craft/state.js +48 -0
- package/dist/craft-interactions/index.js +4 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/ops/index.d.ts +19 -2
- package/dist/ops/index.js +38 -1
- package/dist/utils/uuid.js +12 -2
- package/package.json +2 -2
- package/dist/selection/index.d.ts +0 -1
- package/dist/selection/index.js +0 -1
- package/dist/selection/model.d.ts +0 -10
- package/dist/selection/model.js +0 -38
- package/dist/strings/common_length.d.ts +0 -4
- package/dist/strings/common_length.js +0 -28
- package/dist/structured/index.d.ts +0 -41
- package/dist/structured/index.js +0 -50
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface JsonRpcRequest {
|
|
2
|
+
jsonrpc: '2.0';
|
|
3
|
+
id?: string | number | null;
|
|
4
|
+
method: string;
|
|
5
|
+
params?: unknown;
|
|
6
|
+
}
|
|
7
|
+
export interface JsonRpcError {
|
|
8
|
+
code: number;
|
|
9
|
+
message: string;
|
|
10
|
+
data?: unknown;
|
|
11
|
+
}
|
|
12
|
+
export interface JsonRpcResponse {
|
|
13
|
+
jsonrpc: '2.0';
|
|
14
|
+
id: string | number | null;
|
|
15
|
+
result?: unknown;
|
|
16
|
+
error?: JsonRpcError;
|
|
17
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// JSON-RPC 2.0 wire types shared across the craft contract.
|
|
2
|
+
//
|
|
3
|
+
// These describe the envelope a craft's runtime sees in `onRequest` /
|
|
4
|
+
// `onResponse` (see ./runtime.ts). They live in logisheets-core — the shared,
|
|
5
|
+
// host-neutral layer — so both the craft interface here and the Node host's
|
|
6
|
+
// RpcServer (logisheets-runtime) can reference one definition. The host owns
|
|
7
|
+
// the transport (HTTP, dispatch, error mapping); these are only the shapes.
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { Result } from 'logisheets-web';
|
|
2
|
+
import type { Violation } from '../validation/index.js';
|
|
3
|
+
import type { JsonRpcRequest, JsonRpcResponse } from './rpc.js';
|
|
4
|
+
/**
|
|
5
|
+
* A craft's persisted state. It is always a JSON object (the craft serializes
|
|
6
|
+
* it to a string for storage in AppData, and the host treats that string as
|
|
7
|
+
* opaque). Craft authors narrow this by supplying their own type argument to
|
|
8
|
+
* {@link CraftRuntime}.
|
|
9
|
+
*/
|
|
10
|
+
export type CraftState = Record<string, unknown>;
|
|
11
|
+
/**
|
|
12
|
+
* A lifecycle hook may run engine operations, which are async on every host.
|
|
13
|
+
* So every hook may return its {@link Result} directly or a promise of it; the
|
|
14
|
+
* host always awaits before inspecting the value.
|
|
15
|
+
*/
|
|
16
|
+
export type MaybePromise<T> = T | Promise<T>;
|
|
17
|
+
/**
|
|
18
|
+
* The headless logic of a craft, reconstructed by a host from a workbook's
|
|
19
|
+
* persisted craft state.
|
|
20
|
+
*
|
|
21
|
+
* `S` is the craft's own state shape (defaults to a generic JSON object,
|
|
22
|
+
* {@link CraftState}). `W` is the host's workbook handle — logisheets-core is
|
|
23
|
+
* host-neutral and does not know the concrete type, so each host binds it: the
|
|
24
|
+
* Node runtime to its `Workbook`, the browser to its craft workbook wrapper.
|
|
25
|
+
*
|
|
26
|
+
* The hooks fire around a single JSON-RPC exchange in this order:
|
|
27
|
+
*
|
|
28
|
+
* onLoad once, when the workbook is opened — rehydrate from state
|
|
29
|
+
* onRequest inputs of an incoming request are about to be applied
|
|
30
|
+
* onValidate inputs are now in place; check them BEFORE a response is read
|
|
31
|
+
* onResponse the response has been produced and is about to be returned
|
|
32
|
+
*/
|
|
33
|
+
export interface CraftRuntime<S extends CraftState = CraftState, W = unknown> {
|
|
34
|
+
onLoad: (s: S, wb: W) => MaybePromise<Result<void>>;
|
|
35
|
+
onRequest: (req: JsonRpcRequest, s: S, wb: W) => MaybePromise<Result<void>>;
|
|
36
|
+
/**
|
|
37
|
+
* Called once an RPC request's inputs have been written into the workbook
|
|
38
|
+
* but BEFORE its response is read back — the gateway's validation
|
|
39
|
+
* checkpoint. Returns the cells that fail their rules (empty when all
|
|
40
|
+
* pass). A host that gets a non-empty result should reject the request and
|
|
41
|
+
* roll the inputs back rather than return a response computed from invalid
|
|
42
|
+
* input. Optional: a craft with no validation needs simply omits it.
|
|
43
|
+
*/
|
|
44
|
+
onValidate?: (s: S, wb: W) => MaybePromise<Result<readonly Violation[]>>;
|
|
45
|
+
onResponse: (resp: JsonRpcResponse, s: S, wb: W) => MaybePromise<Result<void>>;
|
|
46
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function setCraftState(craftId: string, json: string): void;
|
|
2
|
+
export declare function getCraftState(craftId: string): string | undefined;
|
|
3
|
+
export declare function clearCraftState(craftId: string): void;
|
|
4
|
+
export declare function getPersistentCraftStates(): Record<string, string>;
|
|
5
|
+
export declare function loadPersistentCraftStates(data: unknown): void;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Host-held store of opaque per-craft JSON state.
|
|
2
|
+
//
|
|
3
|
+
// A craft (running in its iframe) pushes its own serialized state here via the
|
|
4
|
+
// injected `setCraftState(json)` API, keyed by the craft's id (its iframe src
|
|
5
|
+
// path — stable across sessions). On file save the host folds the whole store
|
|
6
|
+
// into the AppData envelope under `craftStates`; on load it is rehydrated, so a
|
|
7
|
+
// craft can read its previous state back via `getCraftState()` the next time
|
|
8
|
+
// its iframe mounts.
|
|
9
|
+
//
|
|
10
|
+
// The host treats each entry as an opaque string and never parses it — the
|
|
11
|
+
// craft owns its own schema. This state lives entirely outside the engine's
|
|
12
|
+
// undo/redo Status (it rides AppData, a side channel on the workbook), so
|
|
13
|
+
// writing it never pollutes edit history.
|
|
14
|
+
const craftStates = new Map();
|
|
15
|
+
// Push a craft's serialized state. Called from the iframe-injected
|
|
16
|
+
// `setCraftState`; `craftId` is captured per-iframe by the host.
|
|
17
|
+
export function setCraftState(craftId, json) {
|
|
18
|
+
if (!craftId)
|
|
19
|
+
return;
|
|
20
|
+
craftStates.set(craftId, json);
|
|
21
|
+
}
|
|
22
|
+
// Read a craft's last-known state (e.g. the one rehydrated from the loaded
|
|
23
|
+
// workbook). Returns undefined if the craft never stored anything.
|
|
24
|
+
export function getCraftState(craftId) {
|
|
25
|
+
return craftStates.get(craftId);
|
|
26
|
+
}
|
|
27
|
+
export function clearCraftState(craftId) {
|
|
28
|
+
craftStates.delete(craftId);
|
|
29
|
+
}
|
|
30
|
+
// Snapshot every craft's state for persistence into the AppData envelope.
|
|
31
|
+
export function getPersistentCraftStates() {
|
|
32
|
+
const out = {};
|
|
33
|
+
for (const [craftId, json] of craftStates)
|
|
34
|
+
out[craftId] = json;
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
37
|
+
// Rehydrate the store from a previously-persisted snapshot. Replaces the
|
|
38
|
+
// current contents so a freshly-loaded workbook doesn't inherit stale state
|
|
39
|
+
// from a prior one.
|
|
40
|
+
export function loadPersistentCraftStates(data) {
|
|
41
|
+
craftStates.clear();
|
|
42
|
+
if (!data || typeof data !== 'object')
|
|
43
|
+
return;
|
|
44
|
+
for (const [craftId, json] of Object.entries(data)) {
|
|
45
|
+
if (typeof json === 'string')
|
|
46
|
+
craftStates.set(craftId, json);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -362,9 +362,10 @@ export function getPersistentInteractions() {
|
|
|
362
362
|
return out;
|
|
363
363
|
}
|
|
364
364
|
export function loadPersistentInteractions(data) {
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
365
|
+
// Always clear first, then repopulate from whatever the workbook carried.
|
|
366
|
+
// Passing undefined/null (a workbook with no interaction state) therefore
|
|
367
|
+
// resets to empty rather than leaving the previous workbook's state behind.
|
|
368
|
+
const d = (data && typeof data === 'object' ? data : {});
|
|
368
369
|
// Radio
|
|
369
370
|
radioBindings.clear();
|
|
370
371
|
if (Array.isArray(d.radioBindings)) {
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export * from './port.js';
|
|
|
2
2
|
export * from './ops/index.js';
|
|
3
3
|
export * from './format/index.js';
|
|
4
4
|
export * from './craft-interactions/index.js';
|
|
5
|
+
export * from './craft/index.js';
|
|
5
6
|
export * from './validation/index.js';
|
|
6
7
|
export * from './field/index.js';
|
|
7
8
|
export * from './value/index.js';
|
package/dist/index.js
CHANGED
|
@@ -7,6 +7,7 @@ export * from './port.js';
|
|
|
7
7
|
export * from './ops/index.js';
|
|
8
8
|
export * from './format/index.js';
|
|
9
9
|
export * from './craft-interactions/index.js';
|
|
10
|
+
export * from './craft/index.js';
|
|
10
11
|
export * from './validation/index.js';
|
|
11
12
|
export * from './field/index.js';
|
|
12
13
|
export * from './value/index.js';
|
package/dist/ops/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Payload, ActionEffect, SelectedData, Alignment, StPatternType, Value } from 'logisheets-web';
|
|
1
|
+
import type { Payload, ActionEffect, SelectedData, Alignment, StPatternType, Value, SheetCellId } from 'logisheets-web';
|
|
2
2
|
import type { Client } from '../port.js';
|
|
3
3
|
import { type ValidationRule, type Violation } from '../validation/index.js';
|
|
4
4
|
import { type FieldColumn } from '../field/index.js';
|
|
@@ -136,7 +136,24 @@ export declare class WorkbookOps {
|
|
|
136
136
|
* Lifted verbatim out of the browser's ValidationCell component so the
|
|
137
137
|
* Node runtime gets the same operation.
|
|
138
138
|
*/
|
|
139
|
-
setValidationRule(sheetIdx: number, row: number, col: number, formula: string): Promise<
|
|
139
|
+
setValidationRule(sheetIdx: number, row: number, col: number, formula: string): Promise<SheetCellId>;
|
|
140
|
+
/**
|
|
141
|
+
* Read a set of *installed* validation shadows by their ids and interpret
|
|
142
|
+
* each — the read half of {@link setValidationRule}.
|
|
143
|
+
*
|
|
144
|
+
* The engine evaluates each shadow reactively (with `#PLACEHOLDER` bound to
|
|
145
|
+
* its target cell), so a caller that cached the shadow ids at install time
|
|
146
|
+
* gets the up-to-date verdicts here in a single batch read, with no
|
|
147
|
+
* coordinate resolution. `rule` is carried only into the returned
|
|
148
|
+
* {@link Violation} for reporting; the evaluation is entirely the engine's.
|
|
149
|
+
*
|
|
150
|
+
* Returns one {@link Violation} per failing cell (passing cells and empty
|
|
151
|
+
* shadows contribute nothing), in input order.
|
|
152
|
+
*/
|
|
153
|
+
checkValidationShadows(entries: readonly {
|
|
154
|
+
shadow: SheetCellId;
|
|
155
|
+
rule: ValidationRule;
|
|
156
|
+
}[]): Promise<Violation[]>;
|
|
140
157
|
/**
|
|
141
158
|
* Evaluate an Excel formula (no leading `=`) in a sheet and return its
|
|
142
159
|
* Value. Parks the formula in a throwaway ephemeral cell, reads the result
|
package/dist/ops/index.js
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
// UI side effects (refocus, toasts, event buses, closing dialogs) stay in the
|
|
18
18
|
// host — only the engine-facing operation lives here.
|
|
19
19
|
import { makeTransaction } from '../transaction/index.js';
|
|
20
|
-
import { checkValidations as checkValidationsPure, } from '../validation/index.js';
|
|
20
|
+
import { checkValidations as checkValidationsPure, interpretValidation, } from '../validation/index.js';
|
|
21
21
|
import { checkFieldConstraints as checkFieldConstraintsPure, } from '../field/index.js';
|
|
22
22
|
import { generateFontPayload, generateAlgnmentPayload, generateWrapTextPayload, generateNumFmtPayload, generatePatternFillPayload, generateBorderPayloads, } from '../format/index.js';
|
|
23
23
|
function isErrorMessage(v) {
|
|
@@ -269,6 +269,43 @@ export class WorkbookOps {
|
|
|
269
269
|
},
|
|
270
270
|
},
|
|
271
271
|
], false);
|
|
272
|
+
// Return the shadow's stable id so callers can cache it and later read
|
|
273
|
+
// the verdict back by id (see {@link checkValidationShadows}) without
|
|
274
|
+
// re-resolving the cell's coordinates.
|
|
275
|
+
return shadow;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Read a set of *installed* validation shadows by their ids and interpret
|
|
279
|
+
* each — the read half of {@link setValidationRule}.
|
|
280
|
+
*
|
|
281
|
+
* The engine evaluates each shadow reactively (with `#PLACEHOLDER` bound to
|
|
282
|
+
* its target cell), so a caller that cached the shadow ids at install time
|
|
283
|
+
* gets the up-to-date verdicts here in a single batch read, with no
|
|
284
|
+
* coordinate resolution. `rule` is carried only into the returned
|
|
285
|
+
* {@link Violation} for reporting; the evaluation is entirely the engine's.
|
|
286
|
+
*
|
|
287
|
+
* Returns one {@link Violation} per failing cell (passing cells and empty
|
|
288
|
+
* shadows contribute nothing), in input order.
|
|
289
|
+
*/
|
|
290
|
+
async checkValidationShadows(entries) {
|
|
291
|
+
if (entries.length === 0)
|
|
292
|
+
return [];
|
|
293
|
+
const infos = await this.client.batchGetCellInfoById({
|
|
294
|
+
ids: entries.map((e) => e.shadow),
|
|
295
|
+
});
|
|
296
|
+
if (isErrorMessage(infos)) {
|
|
297
|
+
throw new Error('Failed to read shadow cells: ' + infos.msg);
|
|
298
|
+
}
|
|
299
|
+
const out = [];
|
|
300
|
+
for (let i = 0; i < entries.length; i++) {
|
|
301
|
+
const info = infos[i];
|
|
302
|
+
if (!info)
|
|
303
|
+
continue;
|
|
304
|
+
const violation = interpretValidation(entries[i].rule, info.value);
|
|
305
|
+
if (violation)
|
|
306
|
+
out.push(violation);
|
|
307
|
+
}
|
|
308
|
+
return out;
|
|
272
309
|
}
|
|
273
310
|
/**
|
|
274
311
|
* Evaluate an Excel formula (no leading `=`) in a sheet and return its
|
package/dist/utils/uuid.js
CHANGED
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
export function simpleUuid() {
|
|
2
|
-
return '10000000-1000-4000-8000-100000000000'.replace(/[018]/g, (c) => (+c ^
|
|
3
|
-
|
|
2
|
+
return '10000000-1000-4000-8000-100000000000'.replace(/[018]/g, (c) => (+c ^ (randomByte() & (15 >> (+c / 4)))).toString(16));
|
|
3
|
+
}
|
|
4
|
+
// `crypto` isn't a typed global here (this package targets ES2020 with no DOM
|
|
5
|
+
// lib, and it must run on both the browser and Node). Reach it through
|
|
6
|
+
// `globalThis` when available (browsers, Node >= 19) and fall back to
|
|
7
|
+
// Math.random otherwise — these ids aren't security-sensitive.
|
|
8
|
+
function randomByte() {
|
|
9
|
+
const webcrypto = globalThis.crypto;
|
|
10
|
+
if (webcrypto?.getRandomValues) {
|
|
11
|
+
return webcrypto.getRandomValues(new Uint8Array(1))[0];
|
|
12
|
+
}
|
|
13
|
+
return Math.floor(Math.random() * 256);
|
|
4
14
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "logisheets-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "UI-free LogiSheets logic — runs in the browser or on Node. Depends on logisheets-web only for types; the engine Client is injected by the host.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"author": "Jeremy He",
|
|
45
45
|
"license": "MIT",
|
|
46
46
|
"peerDependencies": {
|
|
47
|
-
"logisheets-web": "^1.
|
|
47
|
+
"logisheets-web": "^1.3.0"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"logisheets-web": "workspace:*",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './model.js';
|
package/dist/selection/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './model.js';
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export declare class SelectionModel<T> {
|
|
2
|
-
#private;
|
|
3
|
-
readonly multi: boolean;
|
|
4
|
-
readonly data: readonly T[];
|
|
5
|
-
constructor(multi?: boolean, data?: readonly T[]);
|
|
6
|
-
get selected(): readonly T[];
|
|
7
|
-
toggle(data: T): void;
|
|
8
|
-
select(data: T): void;
|
|
9
|
-
deSelect(data: T): void;
|
|
10
|
-
}
|
package/dist/selection/model.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
2
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
3
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
4
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
5
|
-
};
|
|
6
|
-
var _SelectionModel_selected;
|
|
7
|
-
export class SelectionModel {
|
|
8
|
-
constructor(multi = false, data = []) {
|
|
9
|
-
this.multi = multi;
|
|
10
|
-
this.data = data;
|
|
11
|
-
_SelectionModel_selected.set(this, new Map());
|
|
12
|
-
data.forEach((d) => {
|
|
13
|
-
__classPrivateFieldGet(this, _SelectionModel_selected, "f").set(d, true);
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
get selected() {
|
|
17
|
-
const selecteds = [];
|
|
18
|
-
__classPrivateFieldGet(this, _SelectionModel_selected, "f").forEach((checked, d) => {
|
|
19
|
-
if (!checked)
|
|
20
|
-
return;
|
|
21
|
-
selecteds.push(d);
|
|
22
|
-
});
|
|
23
|
-
return selecteds;
|
|
24
|
-
}
|
|
25
|
-
toggle(data) {
|
|
26
|
-
if (__classPrivateFieldGet(this, _SelectionModel_selected, "f").has(data))
|
|
27
|
-
__classPrivateFieldGet(this, _SelectionModel_selected, "f").set(data, true);
|
|
28
|
-
else
|
|
29
|
-
__classPrivateFieldGet(this, _SelectionModel_selected, "f").set(data, false);
|
|
30
|
-
}
|
|
31
|
-
select(data) {
|
|
32
|
-
__classPrivateFieldGet(this, _SelectionModel_selected, "f").set(data, true);
|
|
33
|
-
}
|
|
34
|
-
deSelect(data) {
|
|
35
|
-
__classPrivateFieldGet(this, _SelectionModel_selected, "f").set(data, false);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
_SelectionModel_selected = new WeakMap();
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
export declare function commonSuffix(a: string, b: string): string;
|
|
2
|
-
export declare function commonSuffixLength(a: string, b: string): number;
|
|
3
|
-
export declare function commonPrefix(a: string, b: string): string;
|
|
4
|
-
export declare function commonPrefixLength(a: string, b: string): number;
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
export function commonSuffix(a, b) {
|
|
2
|
-
const strs = [];
|
|
3
|
-
const aLastIndex = a.length - 1;
|
|
4
|
-
const bLastIndex = b.length - 1;
|
|
5
|
-
const len = Math.min(a.length, b.length);
|
|
6
|
-
for (let i = 0; i < len; i += 1) {
|
|
7
|
-
if (a.charCodeAt(aLastIndex - i) !== b.charCodeAt(bLastIndex - i))
|
|
8
|
-
break;
|
|
9
|
-
strs.push(a[i]);
|
|
10
|
-
}
|
|
11
|
-
return strs.join('');
|
|
12
|
-
}
|
|
13
|
-
export function commonSuffixLength(a, b) {
|
|
14
|
-
return commonSuffix(a, b).length;
|
|
15
|
-
}
|
|
16
|
-
export function commonPrefix(a, b) {
|
|
17
|
-
const strs = [];
|
|
18
|
-
const len = Math.min(a.length, b.length);
|
|
19
|
-
for (let i = 0; i < len; i += 1) {
|
|
20
|
-
if (a.charCodeAt(i) !== b.charCodeAt(i))
|
|
21
|
-
break;
|
|
22
|
-
strs.push(a[i]);
|
|
23
|
-
}
|
|
24
|
-
return strs.join('');
|
|
25
|
-
}
|
|
26
|
-
export function commonPrefixLength(a, b) {
|
|
27
|
-
return commonPrefix(a, b).length;
|
|
28
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import type { Value } from 'logisheets-web';
|
|
2
|
-
/** The slice of the engine that structured-data ops actually need. */
|
|
3
|
-
export interface EnginePort {
|
|
4
|
-
/** Apply a cell-input transaction. Returns the engine's status object. */
|
|
5
|
-
handleTransaction(tx: GatewayTransaction): {
|
|
6
|
-
status: {
|
|
7
|
-
type: string;
|
|
8
|
-
};
|
|
9
|
-
};
|
|
10
|
-
/** Read a single cell's evaluated value. */
|
|
11
|
-
getValue(sheetIdx: number, row: number, col: number): Value;
|
|
12
|
-
}
|
|
13
|
-
export interface GatewayTransaction {
|
|
14
|
-
payloads: ReadonlyArray<{
|
|
15
|
-
type: 'cellInput';
|
|
16
|
-
value: {
|
|
17
|
-
sheetIdx: number;
|
|
18
|
-
row: number;
|
|
19
|
-
col: number;
|
|
20
|
-
content: string;
|
|
21
|
-
};
|
|
22
|
-
}>;
|
|
23
|
-
undoable: boolean;
|
|
24
|
-
temp: boolean;
|
|
25
|
-
}
|
|
26
|
-
export interface WriteTarget {
|
|
27
|
-
sheetIdx: number;
|
|
28
|
-
startRow: number;
|
|
29
|
-
startCol: number;
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Import: write `records` (rows of string cells) into the sheet, anchored at
|
|
33
|
-
* (startRow, startCol). One transaction for the whole block. Returns true on
|
|
34
|
-
* an `ok` engine status.
|
|
35
|
-
*/
|
|
36
|
-
export declare function writeRecords(port: EnginePort, target: WriteTarget, records: ReadonlyArray<ReadonlyArray<string>>, undoable?: boolean): boolean;
|
|
37
|
-
/**
|
|
38
|
-
* Export: read a `rows` x `cols` rectangle of evaluated values back out as a
|
|
39
|
-
* 2-D array, anchored at (startRow, startCol).
|
|
40
|
-
*/
|
|
41
|
-
export declare function readRecords(port: EnginePort, target: WriteTarget, rows: number, cols: number): Value[][];
|
package/dist/structured/index.js
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
// Structured-data primitives — the engine-facing core of data-gateway.
|
|
2
|
-
//
|
|
3
|
-
// These functions are the import/export building blocks: write a 2-D block of
|
|
4
|
-
// records into the workbook, and read a rectangle of records back out. They
|
|
5
|
-
// are PURE LOGIC: they never import a concrete engine. They talk to whatever
|
|
6
|
-
// engine the host injects through the minimal `EnginePort` seam below.
|
|
7
|
-
//
|
|
8
|
-
// - browser: wrap logisheets-engine's worker-backed client as an EnginePort
|
|
9
|
-
// - node: wrap logisheets' synchronous handle()/Workbook as an EnginePort
|
|
10
|
-
//
|
|
11
|
-
// The payload/value shapes are imported as TYPES from logisheets-web, so this
|
|
12
|
-
// file still carries no runtime dependency.
|
|
13
|
-
/**
|
|
14
|
-
* Import: write `records` (rows of string cells) into the sheet, anchored at
|
|
15
|
-
* (startRow, startCol). One transaction for the whole block. Returns true on
|
|
16
|
-
* an `ok` engine status.
|
|
17
|
-
*/
|
|
18
|
-
export function writeRecords(port, target, records, undoable = true) {
|
|
19
|
-
const payloads = [];
|
|
20
|
-
records.forEach((row, r) => {
|
|
21
|
-
row.forEach((content, c) => {
|
|
22
|
-
payloads.push({
|
|
23
|
-
type: 'cellInput',
|
|
24
|
-
value: {
|
|
25
|
-
sheetIdx: target.sheetIdx,
|
|
26
|
-
row: target.startRow + r,
|
|
27
|
-
col: target.startCol + c,
|
|
28
|
-
content,
|
|
29
|
-
},
|
|
30
|
-
});
|
|
31
|
-
});
|
|
32
|
-
});
|
|
33
|
-
const effect = port.handleTransaction({ payloads, undoable, temp: false });
|
|
34
|
-
return effect.status.type === 'ok';
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Export: read a `rows` x `cols` rectangle of evaluated values back out as a
|
|
38
|
-
* 2-D array, anchored at (startRow, startCol).
|
|
39
|
-
*/
|
|
40
|
-
export function readRecords(port, target, rows, cols) {
|
|
41
|
-
const out = [];
|
|
42
|
-
for (let r = 0; r < rows; r++) {
|
|
43
|
-
const line = [];
|
|
44
|
-
for (let c = 0; c < cols; c++) {
|
|
45
|
-
line.push(port.getValue(target.sheetIdx, target.startRow + r, target.startCol + c));
|
|
46
|
-
}
|
|
47
|
-
out.push(line);
|
|
48
|
-
}
|
|
49
|
-
return out;
|
|
50
|
-
}
|