@dxos/plugin-sheet 0.6.12-main.15a606f → 0.6.12-main.568932b
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/lib/browser/{SheetContainer-V4GCCZTX.mjs → SheetContainer-T2QWJOFD.mjs} +14 -13
- package/dist/lib/browser/SheetContainer-T2QWJOFD.mjs.map +7 -0
- package/dist/lib/browser/{chunk-T3NJFTD4.mjs → chunk-5ZMVZYGB.mjs} +18 -6
- package/dist/lib/browser/{chunk-T3NJFTD4.mjs.map → chunk-5ZMVZYGB.mjs.map} +3 -3
- package/dist/lib/browser/{chunk-6ZMQVB4Z.mjs → chunk-GSV5QNLD.mjs} +220 -177
- package/dist/lib/browser/chunk-GSV5QNLD.mjs.map +7 -0
- package/dist/lib/browser/{chunk-U2JHW3L6.mjs → chunk-ZL2V5UJR.mjs} +206 -53
- package/dist/lib/browser/chunk-ZL2V5UJR.mjs.map +7 -0
- package/dist/lib/browser/graph-M4IQ76QX.mjs +33 -0
- package/dist/lib/browser/index.mjs +45 -21
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/types.mjs +1 -1
- package/dist/lib/node/{SheetContainer-3ZY7MPWJ.cjs → SheetContainer-PV5ET4UJ.cjs} +21 -20
- package/dist/lib/node/SheetContainer-PV5ET4UJ.cjs.map +7 -0
- package/dist/lib/node/{chunk-OTTD7FBK.cjs → chunk-2K53Z2TU.cjs} +224 -70
- package/dist/lib/node/chunk-2K53Z2TU.cjs.map +7 -0
- package/dist/lib/node/{chunk-DD6FIXWC.cjs → chunk-5XPK2V4A.cjs} +222 -175
- package/dist/lib/node/chunk-5XPK2V4A.cjs.map +7 -0
- package/dist/lib/node/{chunk-Q3HBHPRL.cjs → chunk-STAVQ2JE.cjs} +21 -9
- package/dist/lib/node/{chunk-Q3HBHPRL.cjs.map → chunk-STAVQ2JE.cjs.map} +3 -3
- package/dist/lib/node/graph-Q3N2X26H.cjs +55 -0
- package/dist/lib/node/graph-Q3N2X26H.cjs.map +7 -0
- package/dist/lib/node/index.cjs +51 -30
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/types.cjs +8 -8
- package/dist/lib/node/types.cjs.map +1 -1
- package/dist/lib/node-esm/{SheetContainer-PXSJX6XK.mjs → SheetContainer-FOZD2WLT.mjs} +14 -13
- package/dist/lib/node-esm/SheetContainer-FOZD2WLT.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-BMNA27EX.mjs → chunk-2HAM45RC.mjs} +18 -6
- package/dist/lib/node-esm/{chunk-BMNA27EX.mjs.map → chunk-2HAM45RC.mjs.map} +3 -3
- package/dist/lib/node-esm/{chunk-D6KU5MI7.mjs → chunk-5WPZCXNS.mjs} +220 -177
- package/dist/lib/node-esm/chunk-5WPZCXNS.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-7HVSOTGA.mjs → chunk-QEUCIHIN.mjs} +206 -53
- package/dist/lib/node-esm/chunk-QEUCIHIN.mjs.map +7 -0
- package/dist/lib/node-esm/graph-SMPUMOV2.mjs +34 -0
- package/dist/lib/node-esm/index.mjs +45 -21
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/types.mjs +1 -1
- package/dist/types/src/SheetPlugin.d.ts.map +1 -1
- package/dist/types/src/components/CellEditor/CellEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/CellEditor/extension.d.ts.map +1 -1
- package/dist/types/src/components/GridSheet/GridSheet.d.ts +3 -3
- package/dist/types/src/components/GridSheet/GridSheet.d.ts.map +1 -1
- package/dist/types/src/components/GridSheet/GridSheet.stories.d.ts +1 -1
- package/dist/types/src/components/GridSheet/GridSheet.stories.d.ts.map +1 -1
- package/dist/types/src/components/GridSheet/util.d.ts +12 -2
- package/dist/types/src/components/GridSheet/util.d.ts.map +1 -1
- package/dist/types/src/components/Sheet/Sheet.d.ts.map +1 -1
- package/dist/types/src/components/Sheet/Sheet.stories.d.ts.map +1 -1
- package/dist/types/src/components/Sheet/sheet-context.d.ts +3 -3
- package/dist/types/src/components/Sheet/sheet-context.d.ts.map +1 -1
- package/dist/types/src/components/SheetContainer.d.ts +1 -1
- package/dist/types/src/components/SheetContainer.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +1 -1
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/defs/types.d.ts.map +1 -1
- package/dist/types/src/defs/util.d.ts +1 -1
- package/dist/types/src/defs/util.d.ts.map +1 -1
- package/dist/types/src/extensions/compute.d.ts +3 -2
- package/dist/types/src/extensions/compute.d.ts.map +1 -1
- package/dist/types/src/extensions/compute.stories.d.ts.map +1 -1
- package/dist/types/src/graph/compute-graph-registry.d.ts +34 -0
- package/dist/types/src/graph/compute-graph-registry.d.ts.map +1 -0
- package/dist/types/src/graph/compute-graph.d.ts +17 -34
- package/dist/types/src/graph/compute-graph.d.ts.map +1 -1
- package/dist/types/src/graph/compute-graph.stories.d.ts.map +1 -1
- package/dist/types/src/graph/compute-graph.test.d.ts +2 -0
- package/dist/types/src/graph/compute-graph.test.d.ts.map +1 -0
- package/dist/types/src/graph/compute-node.d.ts +9 -2
- package/dist/types/src/graph/compute-node.d.ts.map +1 -1
- package/dist/types/src/graph/{async-function.d.ts → functions/async-function.d.ts} +13 -4
- package/dist/types/src/graph/functions/async-function.d.ts.map +1 -0
- package/dist/types/src/graph/functions/edge-function.d.ts +21 -0
- package/dist/types/src/graph/functions/edge-function.d.ts.map +1 -0
- package/dist/types/src/graph/functions/function-defs.d.ts.map +1 -0
- package/dist/types/src/graph/functions/index.d.ts +4 -0
- package/dist/types/src/graph/functions/index.d.ts.map +1 -0
- package/dist/types/src/graph/index.d.ts +2 -1
- package/dist/types/src/graph/index.d.ts.map +1 -1
- package/dist/types/src/graph/testing/index.d.ts +3 -0
- package/dist/types/src/graph/testing/index.d.ts.map +1 -0
- package/dist/types/src/graph/testing/test-builder.d.ts +15 -0
- package/dist/types/src/graph/testing/test-builder.d.ts.map +1 -0
- package/dist/types/src/graph/testing/test-plugin.d.ts +36 -0
- package/dist/types/src/graph/testing/test-plugin.d.ts.map +1 -0
- package/dist/types/src/hooks/useComputeGraph.d.ts.map +1 -1
- package/dist/types/src/hooks/useSheetModel.d.ts +2 -2
- package/dist/types/src/hooks/useSheetModel.d.ts.map +1 -1
- package/dist/types/src/model/sheet-model.d.ts +3 -3
- package/dist/types/src/model/sheet-model.d.ts.map +1 -1
- package/dist/types/src/model/sheet-model.test.d.ts +2 -0
- package/dist/types/src/model/sheet-model.test.d.ts.map +1 -0
- package/dist/types/src/testing/testing.d.ts +4 -5
- package/dist/types/src/testing/testing.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +4 -3
- package/dist/types/src/types.d.ts.map +1 -1
- package/package.json +40 -39
- package/src/SheetPlugin.tsx +19 -15
- package/src/components/CellEditor/CellEditor.stories.tsx +2 -3
- package/src/components/CellEditor/extension.test.ts +0 -1
- package/src/components/CellEditor/extension.ts +4 -3
- package/src/components/GridSheet/GridSheet.stories.tsx +5 -4
- package/src/components/GridSheet/GridSheet.tsx +27 -9
- package/src/components/GridSheet/util.ts +93 -34
- package/src/components/Sheet/Sheet.stories.tsx +21 -20
- package/src/components/Sheet/Sheet.tsx +30 -14
- package/src/components/Sheet/sheet-context.tsx +4 -4
- package/src/components/SheetContainer.tsx +13 -15
- package/src/defs/types.ts +1 -0
- package/src/defs/util.ts +19 -3
- package/src/extensions/compute.stories.tsx +20 -20
- package/src/extensions/compute.ts +91 -42
- package/src/graph/compute-graph-registry.ts +90 -0
- package/src/graph/compute-graph.stories.tsx +4 -3
- package/src/graph/compute-graph.test.ts +87 -0
- package/src/graph/compute-graph.ts +73 -121
- package/src/graph/compute-node.ts +17 -5
- package/src/graph/{async-function.ts → functions/async-function.ts} +23 -15
- package/src/graph/{edge-function.ts → functions/edge-function.ts} +14 -13
- package/src/graph/functions/index.ts +7 -0
- package/src/graph/hyperformula.test.ts +1 -2
- package/src/graph/index.ts +2 -1
- package/src/graph/testing/index.ts +6 -0
- package/src/graph/testing/test-builder.ts +54 -0
- package/src/graph/{custom-function.ts → testing/test-plugin.ts} +43 -9
- package/src/hooks/hooks.stories.tsx +3 -3
- package/src/hooks/useComputeGraph.ts +9 -1
- package/src/hooks/useSheetModel.ts +4 -7
- package/src/model/sheet-model.test.ts +57 -0
- package/src/model/sheet-model.ts +47 -30
- package/src/testing/testing.tsx +17 -15
- package/src/types.ts +7 -7
- package/dist/lib/browser/SheetContainer-V4GCCZTX.mjs.map +0 -7
- package/dist/lib/browser/chunk-6ZMQVB4Z.mjs.map +0 -7
- package/dist/lib/browser/chunk-U2JHW3L6.mjs.map +0 -7
- package/dist/lib/browser/graph-T27BOBOV.mjs +0 -21
- package/dist/lib/node/SheetContainer-3ZY7MPWJ.cjs.map +0 -7
- package/dist/lib/node/chunk-DD6FIXWC.cjs.map +0 -7
- package/dist/lib/node/chunk-OTTD7FBK.cjs.map +0 -7
- package/dist/lib/node/graph-SPKGX7W4.cjs +0 -43
- package/dist/lib/node/graph-SPKGX7W4.cjs.map +0 -7
- package/dist/lib/node-esm/SheetContainer-PXSJX6XK.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-7HVSOTGA.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-D6KU5MI7.mjs.map +0 -7
- package/dist/lib/node-esm/graph-U67IO4UC.mjs +0 -22
- package/dist/types/src/graph/async-function.d.ts.map +0 -1
- package/dist/types/src/graph/compute-graph.browser.test.d.ts +0 -2
- package/dist/types/src/graph/compute-graph.browser.test.d.ts.map +0 -1
- package/dist/types/src/graph/custom-function.d.ts +0 -21
- package/dist/types/src/graph/custom-function.d.ts.map +0 -1
- package/dist/types/src/graph/edge-function.d.ts +0 -20
- package/dist/types/src/graph/edge-function.d.ts.map +0 -1
- package/dist/types/src/graph/function-defs.d.ts.map +0 -1
- package/src/graph/compute-graph.browser.test.ts +0 -104
- /package/dist/lib/browser/{graph-T27BOBOV.mjs.map → graph-M4IQ76QX.mjs.map} +0 -0
- /package/dist/lib/node-esm/{graph-U67IO4UC.mjs.map → graph-SMPUMOV2.mjs.map} +0 -0
- /package/dist/types/src/graph/{function-defs.d.ts → functions/function-defs.d.ts} +0 -0
- /package/src/graph/{function-defs.ts → functions/function-defs.ts} +0 -0
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import { type SimpleCellAddress } from 'hyperformula/typings/Cell';
|
|
5
6
|
import { type InterpreterState } from 'hyperformula/typings/interpreter/InterpreterState';
|
|
6
7
|
import { type InterpreterValue } from 'hyperformula/typings/interpreter/InterpreterValue';
|
|
7
8
|
import { type ProcedureAst } from 'hyperformula/typings/parser';
|
|
@@ -13,28 +14,33 @@ import { log } from '@dxos/log';
|
|
|
13
14
|
|
|
14
15
|
import { CellError, ErrorType, EmptyValue, FunctionPlugin, type HyperFormula } from '#hyperformula';
|
|
15
16
|
|
|
16
|
-
// TODO(burdon): API gateways
|
|
17
|
+
// TODO(burdon): Create API gateways:
|
|
17
18
|
// https://publicapis.io
|
|
18
19
|
// https://api-ninjas.com/api/cryptoprice
|
|
19
20
|
// https://developers.google.com/apis-explorer
|
|
20
21
|
// https://publicapis.io/coin-desk-api
|
|
21
22
|
|
|
22
|
-
// TODO(burdon): Create wrapper.
|
|
23
23
|
export type AsyncFunction = (...args: any) => Promise<InterpreterValue>;
|
|
24
24
|
|
|
25
|
+
export type FunctionUpdateEvent = {
|
|
26
|
+
name: string;
|
|
27
|
+
cell: SimpleCellAddress;
|
|
28
|
+
};
|
|
29
|
+
|
|
25
30
|
export type FunctionOptions = {
|
|
26
31
|
ttl?: number;
|
|
27
32
|
};
|
|
28
33
|
|
|
29
34
|
export type FunctionContextOptions = {
|
|
30
35
|
defaultTtl: number;
|
|
31
|
-
|
|
36
|
+
debounceDelay: number;
|
|
32
37
|
remoteFunctionUrl: string;
|
|
38
|
+
onUpdate?: (update: FunctionUpdateEvent) => void;
|
|
33
39
|
};
|
|
34
40
|
|
|
35
41
|
export const defaultFunctionContextOptions: FunctionContextOptions = {
|
|
36
42
|
defaultTtl: 5_000,
|
|
37
|
-
|
|
43
|
+
debounceDelay: 200,
|
|
38
44
|
remoteFunctionUrl: 'https://edge.dxos.workers.dev/functions', // TODO(burdon): Config.
|
|
39
45
|
};
|
|
40
46
|
|
|
@@ -66,24 +72,23 @@ export class FunctionContext {
|
|
|
66
72
|
private _invocations: Record<string, number> = {};
|
|
67
73
|
|
|
68
74
|
private readonly _options: FunctionContextOptions;
|
|
69
|
-
|
|
75
|
+
|
|
76
|
+
// Debounced update handler.
|
|
77
|
+
private readonly _onUpdate: (update: FunctionUpdateEvent) => void;
|
|
70
78
|
|
|
71
79
|
constructor(
|
|
72
80
|
private readonly _hf: HyperFormula,
|
|
73
81
|
private readonly _space: Space | undefined,
|
|
74
|
-
onUpdate: (context: FunctionContext) => void,
|
|
75
82
|
_options?: Partial<FunctionContextOptions>,
|
|
76
83
|
) {
|
|
77
84
|
this._options = defaultsDeep(_options ?? {}, defaultFunctionContextOptions);
|
|
78
|
-
this._onUpdate = debounce(() => {
|
|
79
|
-
// TODO(burdon): Better way to trigger recalculation?
|
|
80
|
-
// NOTE: rebuildAndRecalculate resets the undo history.
|
|
85
|
+
this._onUpdate = debounce((update) => {
|
|
86
|
+
// TODO(burdon): Better way to trigger recalculation? (NOTE: rebuildAndRecalculate resets the undo history.)
|
|
81
87
|
this._hf.resumeEvaluation();
|
|
82
|
-
onUpdate(
|
|
83
|
-
}, this._options.
|
|
88
|
+
this._options.onUpdate?.(update);
|
|
89
|
+
}, this._options.debounceDelay);
|
|
84
90
|
}
|
|
85
91
|
|
|
86
|
-
// TODO(burdon): Remove?
|
|
87
92
|
get space() {
|
|
88
93
|
return this._space;
|
|
89
94
|
}
|
|
@@ -136,7 +141,7 @@ export class FunctionContext {
|
|
|
136
141
|
const value = await cb(...args);
|
|
137
142
|
this._cache.set(invocationKey, { value, ts: Date.now() });
|
|
138
143
|
log('set', { cell, value });
|
|
139
|
-
this._onUpdate();
|
|
144
|
+
this._onUpdate({ name, cell });
|
|
140
145
|
} catch (err) {
|
|
141
146
|
// TODO(burdon): Show error to user.
|
|
142
147
|
log.warn('failed', { cell, err });
|
|
@@ -155,12 +160,15 @@ export class FunctionContext {
|
|
|
155
160
|
/**
|
|
156
161
|
* Base class for async functions.
|
|
157
162
|
*/
|
|
158
|
-
export class
|
|
163
|
+
export class AsyncFunctionPlugin extends FunctionPlugin {
|
|
159
164
|
get context() {
|
|
160
165
|
return this.config.context as FunctionContext;
|
|
161
166
|
}
|
|
162
167
|
|
|
163
|
-
|
|
168
|
+
/**
|
|
169
|
+
* Immediately returns cached value then runs the async function.
|
|
170
|
+
*/
|
|
171
|
+
protected runAsyncFunction(ast: ProcedureAst, state: InterpreterState, cb: AsyncFunction, options?: FunctionOptions) {
|
|
164
172
|
const { procedureName } = ast;
|
|
165
173
|
const metadata = this.metadata(procedureName);
|
|
166
174
|
return this.runFunction(ast.args, state, metadata, (...args: any) => {
|
|
@@ -13,17 +13,19 @@ import { FunctionType } from '@dxos/plugin-script/types';
|
|
|
13
13
|
import { nonNullable } from '@dxos/util';
|
|
14
14
|
|
|
15
15
|
import { CellError, ErrorType, FunctionArgumentType } from '#hyperformula';
|
|
16
|
-
import { type AsyncFunction,
|
|
16
|
+
import { type AsyncFunction, AsyncFunctionPlugin } from './async-function';
|
|
17
17
|
|
|
18
|
-
const
|
|
18
|
+
export const EDGE_FUNCTION_NAME = 'DX';
|
|
19
|
+
|
|
20
|
+
const FUNCTION_TTL = 10_000;
|
|
19
21
|
|
|
20
22
|
/**
|
|
21
|
-
* A hyperformula function plugin for calling EDGE functions.
|
|
23
|
+
* A hyperformula function plugin for calling remote (EDGE) functions.
|
|
22
24
|
*
|
|
23
25
|
* https://hyperformula.handsontable.com/guide/custom-functions.html#add-a-simple-custom-function
|
|
24
26
|
*/
|
|
25
|
-
export class EdgeFunctionPlugin extends
|
|
26
|
-
|
|
27
|
+
export class EdgeFunctionPlugin extends AsyncFunctionPlugin {
|
|
28
|
+
dx(ast: ProcedureAst, state: InterpreterState) {
|
|
27
29
|
const handler =
|
|
28
30
|
(subscribe = false): AsyncFunction =>
|
|
29
31
|
async (binding: string, ...args: any) => {
|
|
@@ -47,7 +49,7 @@ export class EdgeFunctionPlugin extends FunctionPluginAsync {
|
|
|
47
49
|
|
|
48
50
|
// TODO(wittjosiah): `ttl` should be 0 to force a recalculation when a new version is deployed.
|
|
49
51
|
// This needs a ttl to prevent a binding change from causing the function not to be found.
|
|
50
|
-
this.runAsyncFunction(ast, state, handler(false), { ttl:
|
|
52
|
+
this.runAsyncFunction(ast, state, handler(false), { ttl: FUNCTION_TTL });
|
|
51
53
|
});
|
|
52
54
|
|
|
53
55
|
this.context.createSubscription(ast.procedureName, unsubscribe);
|
|
@@ -63,19 +65,18 @@ export class EdgeFunctionPlugin extends FunctionPluginAsync {
|
|
|
63
65
|
return await result.text();
|
|
64
66
|
};
|
|
65
67
|
|
|
66
|
-
return this.runAsyncFunction(ast, state, handler(true), { ttl:
|
|
68
|
+
return this.runAsyncFunction(ast, state, handler(true), { ttl: FUNCTION_TTL });
|
|
67
69
|
}
|
|
68
70
|
}
|
|
69
71
|
|
|
70
72
|
EdgeFunctionPlugin.implementedFunctions = {
|
|
71
|
-
|
|
72
|
-
method: '
|
|
73
|
+
[EDGE_FUNCTION_NAME]: {
|
|
74
|
+
method: 'dx',
|
|
73
75
|
parameters: [
|
|
74
76
|
// Binding
|
|
75
77
|
{ argumentType: FunctionArgumentType.STRING },
|
|
76
78
|
|
|
77
|
-
// Remote function arguments (currently supporting up to
|
|
78
|
-
{ argumentType: FunctionArgumentType.ANY, optionalArg: true },
|
|
79
|
+
// Remote function arguments (currently supporting up to 8).
|
|
79
80
|
{ argumentType: FunctionArgumentType.ANY, optionalArg: true },
|
|
80
81
|
{ argumentType: FunctionArgumentType.ANY, optionalArg: true },
|
|
81
82
|
{ argumentType: FunctionArgumentType.ANY, optionalArg: true },
|
|
@@ -91,9 +92,9 @@ EdgeFunctionPlugin.implementedFunctions = {
|
|
|
91
92
|
|
|
92
93
|
export const EdgeFunctionPluginTranslations = {
|
|
93
94
|
enGB: {
|
|
94
|
-
|
|
95
|
+
[EDGE_FUNCTION_NAME]: 'Remote function',
|
|
95
96
|
},
|
|
96
97
|
enUS: {
|
|
97
|
-
|
|
98
|
+
[EDGE_FUNCTION_NAME]: 'Remote function',
|
|
98
99
|
},
|
|
99
100
|
};
|
|
@@ -7,8 +7,7 @@ import { describe, test, expect } from 'vitest';
|
|
|
7
7
|
import { HyperFormula } from '#hyperformula';
|
|
8
8
|
|
|
9
9
|
describe('hyperformula', () => {
|
|
10
|
-
test('sanity', async () => {
|
|
11
|
-
// TODO(burdon): Throws "Cannot convert undefined or null to object" in vitest (without browser).
|
|
10
|
+
test('sanity test', async () => {
|
|
12
11
|
const hf = HyperFormula.buildEmpty({ licenseKey: 'gpl-v3' });
|
|
13
12
|
expect(hf).to.exist;
|
|
14
13
|
});
|
package/src/graph/index.ts
CHANGED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Client, type ClientOptions } from '@dxos/client';
|
|
6
|
+
import { type Context, Resource } from '@dxos/context';
|
|
7
|
+
import { invariant } from '@dxos/invariant';
|
|
8
|
+
|
|
9
|
+
import { type ComputeGraphOptions, ComputeGraphRegistry } from '../compute-graph-registry';
|
|
10
|
+
|
|
11
|
+
export type TestBuilderOptions = ClientOptions & ComputeGraphOptions;
|
|
12
|
+
|
|
13
|
+
// TODO(burdon): Reconcile with @dxos/client/testing.
|
|
14
|
+
export class TestBuilder extends Resource {
|
|
15
|
+
private _client?: Client;
|
|
16
|
+
private _registry?: ComputeGraphRegistry;
|
|
17
|
+
|
|
18
|
+
constructor(private readonly _options: TestBuilderOptions = {}) {
|
|
19
|
+
super();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
get ctx(): Context {
|
|
23
|
+
return this._ctx;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
get client(): Client {
|
|
27
|
+
invariant(this._client);
|
|
28
|
+
return this._client;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
get registry(): ComputeGraphRegistry {
|
|
32
|
+
invariant(this._registry);
|
|
33
|
+
return this._registry;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
override async _open() {
|
|
37
|
+
const client = new Client(this._options);
|
|
38
|
+
await client.initialize();
|
|
39
|
+
await client.halo.createIdentity();
|
|
40
|
+
this._client = client;
|
|
41
|
+
this._ctx.onDispose(async () => {
|
|
42
|
+
await client.destroy();
|
|
43
|
+
this._client = undefined;
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const registry = new ComputeGraphRegistry(this._options);
|
|
47
|
+
await registry.open();
|
|
48
|
+
this._registry = registry;
|
|
49
|
+
this._ctx.onDispose(async () => {
|
|
50
|
+
await registry.close();
|
|
51
|
+
this._registry = undefined;
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -8,14 +8,30 @@ import { type ProcedureAst } from 'hyperformula/typings/parser';
|
|
|
8
8
|
import { getDeep } from '@dxos/util';
|
|
9
9
|
|
|
10
10
|
import { FunctionArgumentType } from '#hyperformula';
|
|
11
|
-
import { type
|
|
12
|
-
import {
|
|
11
|
+
import { type ComputeGraphPlugin } from '../compute-graph-registry';
|
|
12
|
+
import { type AsyncFunction, AsyncFunctionPlugin } from '../functions';
|
|
13
|
+
import { parseNumberString } from '../util';
|
|
13
14
|
|
|
14
15
|
/**
|
|
16
|
+
* Testing functions run locally (not run via EDGE).
|
|
15
17
|
* https://hyperformula.handsontable.com/guide/custom-functions.html#add-a-simple-custom-function
|
|
16
18
|
*/
|
|
17
|
-
export class
|
|
19
|
+
export class TestPlugin extends AsyncFunctionPlugin {
|
|
20
|
+
/**
|
|
21
|
+
* Simple local function returns input value.
|
|
22
|
+
*/
|
|
18
23
|
test(ast: ProcedureAst, state: InterpreterState) {
|
|
24
|
+
const handler: AsyncFunction = async (_value) => {
|
|
25
|
+
return _value;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
return this.runAsyncFunction(ast, state, handler);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Simple local function returns random number.
|
|
33
|
+
*/
|
|
34
|
+
random(ast: ProcedureAst, state: InterpreterState) {
|
|
19
35
|
const handler: AsyncFunction = async () => {
|
|
20
36
|
return Math.random();
|
|
21
37
|
};
|
|
@@ -23,6 +39,9 @@ export class CustomPlugin extends FunctionPluginAsync {
|
|
|
23
39
|
return this.runAsyncFunction(ast, state, handler);
|
|
24
40
|
}
|
|
25
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Async HTTP function.
|
|
44
|
+
*/
|
|
26
45
|
crypto(ast: ProcedureAst, state: InterpreterState) {
|
|
27
46
|
const handler: AsyncFunction = async (_currency) => {
|
|
28
47
|
const currency = (_currency || 'USD').toUpperCase();
|
|
@@ -40,9 +59,15 @@ export class CustomPlugin extends FunctionPluginAsync {
|
|
|
40
59
|
}
|
|
41
60
|
}
|
|
42
61
|
|
|
43
|
-
|
|
62
|
+
TestPlugin.implementedFunctions = {
|
|
44
63
|
TEST: {
|
|
45
64
|
method: 'test',
|
|
65
|
+
parameters: [{ argumentType: FunctionArgumentType.NUMBER, optionalArg: false }],
|
|
66
|
+
isVolatile: true,
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
RANDOM: {
|
|
70
|
+
method: 'random',
|
|
46
71
|
parameters: [],
|
|
47
72
|
isVolatile: true,
|
|
48
73
|
},
|
|
@@ -54,13 +79,22 @@ CustomPlugin.implementedFunctions = {
|
|
|
54
79
|
},
|
|
55
80
|
};
|
|
56
81
|
|
|
57
|
-
export const
|
|
82
|
+
export const TestPluginTranslations = {
|
|
58
83
|
enGB: {
|
|
59
|
-
TEST: '
|
|
60
|
-
|
|
84
|
+
TEST: 'Returns input value',
|
|
85
|
+
RANDOM: 'Random number',
|
|
86
|
+
CRYPTO: 'Crypto token value',
|
|
61
87
|
},
|
|
62
88
|
enUS: {
|
|
63
|
-
TEST: '
|
|
64
|
-
|
|
89
|
+
TEST: 'Returns input value',
|
|
90
|
+
RANDOM: 'Random number',
|
|
91
|
+
CRYPTO: 'Crypto token value',
|
|
65
92
|
},
|
|
66
93
|
};
|
|
94
|
+
|
|
95
|
+
export const testFunctionPlugins: ComputeGraphPlugin[] = [
|
|
96
|
+
{
|
|
97
|
+
plugin: TestPlugin,
|
|
98
|
+
translations: TestPluginTranslations,
|
|
99
|
+
},
|
|
100
|
+
];
|
|
@@ -14,14 +14,14 @@ import { withTheme } from '@dxos/storybook-utils';
|
|
|
14
14
|
import { ComputeGraphContextProvider } from '../components';
|
|
15
15
|
import { createSheet } from '../defs';
|
|
16
16
|
import { useComputeGraph, useSheetModel } from '../hooks';
|
|
17
|
-
import {
|
|
17
|
+
import { withComputeGraphDecorator } from '../testing';
|
|
18
18
|
import { SheetType } from '../types';
|
|
19
19
|
|
|
20
20
|
const Story = () => {
|
|
21
21
|
const space = useSpace();
|
|
22
22
|
const graph = useComputeGraph(space);
|
|
23
23
|
const [sheet, setSheet] = useState<SheetType>();
|
|
24
|
-
const model = useSheetModel(
|
|
24
|
+
const model = useSheetModel(graph, sheet);
|
|
25
25
|
useEffect(() => {
|
|
26
26
|
if (space) {
|
|
27
27
|
const sheet = space.db.add(createSheet());
|
|
@@ -41,7 +41,7 @@ export default {
|
|
|
41
41
|
component: ComputeGraphContextProvider,
|
|
42
42
|
decorators: [
|
|
43
43
|
withClientProvider({ types: [SheetType], createIdentity: true, createSpace: true }),
|
|
44
|
-
|
|
44
|
+
withComputeGraphDecorator(),
|
|
45
45
|
withTheme,
|
|
46
46
|
],
|
|
47
47
|
render: (args: any) => <Story {...args} />,
|
|
@@ -16,5 +16,13 @@ import { type ComputeGraph } from '../graph';
|
|
|
16
16
|
*/
|
|
17
17
|
export const useComputeGraph = (space?: Space): ComputeGraph | undefined => {
|
|
18
18
|
const { registry } = useContext(ComputeGraphContext) ?? raise(new Error('Missing ComputeGraphContext'));
|
|
19
|
-
|
|
19
|
+
const [graph] = useAsyncState(async () => {
|
|
20
|
+
if (space) {
|
|
21
|
+
const graph = registry.getOrCreateGraph(space);
|
|
22
|
+
await graph.open();
|
|
23
|
+
return graph;
|
|
24
|
+
}
|
|
25
|
+
}, [space, registry]);
|
|
26
|
+
|
|
27
|
+
return graph;
|
|
20
28
|
};
|
|
@@ -4,9 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { useEffect, useState } from 'react';
|
|
6
6
|
|
|
7
|
-
import { type
|
|
8
|
-
|
|
9
|
-
import { useComputeGraph } from './useComputeGraph';
|
|
7
|
+
import { type ComputeGraph } from '../graph';
|
|
10
8
|
import { SheetModel } from '../model';
|
|
11
9
|
import { type SheetType } from '../types';
|
|
12
10
|
|
|
@@ -15,14 +13,13 @@ export type UseSheetModelOptions = {
|
|
|
15
13
|
};
|
|
16
14
|
|
|
17
15
|
export const useSheetModel = (
|
|
18
|
-
|
|
16
|
+
graph?: ComputeGraph,
|
|
19
17
|
sheet?: SheetType,
|
|
20
18
|
{ readonly }: UseSheetModelOptions = {},
|
|
21
19
|
): SheetModel | undefined => {
|
|
22
|
-
const graph = useComputeGraph(space);
|
|
23
20
|
const [model, setModel] = useState<SheetModel>();
|
|
24
21
|
useEffect(() => {
|
|
25
|
-
if (!
|
|
22
|
+
if (!graph || !sheet) {
|
|
26
23
|
return;
|
|
27
24
|
}
|
|
28
25
|
|
|
@@ -37,7 +34,7 @@ export const useSheetModel = (
|
|
|
37
34
|
clearTimeout(t);
|
|
38
35
|
void model?.close();
|
|
39
36
|
};
|
|
40
|
-
}, [
|
|
37
|
+
}, [graph, sheet, readonly]);
|
|
41
38
|
|
|
42
39
|
return model;
|
|
43
40
|
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { afterEach, beforeEach, describe, expect, test } from 'vitest';
|
|
6
|
+
|
|
7
|
+
import { Trigger } from '@dxos/async';
|
|
8
|
+
import { FunctionType } from '@dxos/plugin-script/types';
|
|
9
|
+
|
|
10
|
+
import { SheetModel } from './sheet-model';
|
|
11
|
+
import { addressFromA1Notation, createSheet } from '../defs';
|
|
12
|
+
import { TestBuilder, testFunctionPlugins } from '../graph/testing';
|
|
13
|
+
import { type CellScalarValue } from '../types';
|
|
14
|
+
|
|
15
|
+
describe('SheetModel', () => {
|
|
16
|
+
let testBuilder: TestBuilder;
|
|
17
|
+
beforeEach(async () => {
|
|
18
|
+
testBuilder = new TestBuilder({ types: [FunctionType], plugins: testFunctionPlugins });
|
|
19
|
+
await testBuilder.open();
|
|
20
|
+
});
|
|
21
|
+
afterEach(async () => {
|
|
22
|
+
await testBuilder.close();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('async function', async () => {
|
|
26
|
+
const space = await testBuilder.client.spaces.create();
|
|
27
|
+
const graph = testBuilder.registry.createGraph(space);
|
|
28
|
+
await graph.open();
|
|
29
|
+
|
|
30
|
+
// TODO(burdon): Create via factory.
|
|
31
|
+
const sheet = createSheet({ rows: 5, columns: 5 });
|
|
32
|
+
const model = new SheetModel(graph, sheet);
|
|
33
|
+
await model.open();
|
|
34
|
+
testBuilder.ctx.onDispose(() => model.close());
|
|
35
|
+
|
|
36
|
+
// Trigger waits for function invocation.
|
|
37
|
+
const trigger = new Trigger<CellScalarValue>();
|
|
38
|
+
model.setValue(addressFromA1Notation('A1'), '=TEST(100)');
|
|
39
|
+
model.update.once((update) => {
|
|
40
|
+
const { type } = update;
|
|
41
|
+
if (type === 'valuesUpdated') {
|
|
42
|
+
const value = model.getValue(addressFromA1Notation('A1'));
|
|
43
|
+
trigger.wake(value);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Initial value will be null.
|
|
48
|
+
const v1 = model.getValue(addressFromA1Notation('A1'));
|
|
49
|
+
expect(v1).to.be.null;
|
|
50
|
+
expect(graph.context.info.invocations.TEST).not.to.exist;
|
|
51
|
+
|
|
52
|
+
// Wait until async update triggered.
|
|
53
|
+
const v2 = await trigger.wait();
|
|
54
|
+
expect(v2).to.eq(100);
|
|
55
|
+
expect(graph.context.info.invocations.TEST).to.eq(1);
|
|
56
|
+
});
|
|
57
|
+
});
|