@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.
Files changed (161) hide show
  1. package/dist/lib/browser/{SheetContainer-V4GCCZTX.mjs → SheetContainer-T2QWJOFD.mjs} +14 -13
  2. package/dist/lib/browser/SheetContainer-T2QWJOFD.mjs.map +7 -0
  3. package/dist/lib/browser/{chunk-T3NJFTD4.mjs → chunk-5ZMVZYGB.mjs} +18 -6
  4. package/dist/lib/browser/{chunk-T3NJFTD4.mjs.map → chunk-5ZMVZYGB.mjs.map} +3 -3
  5. package/dist/lib/browser/{chunk-6ZMQVB4Z.mjs → chunk-GSV5QNLD.mjs} +220 -177
  6. package/dist/lib/browser/chunk-GSV5QNLD.mjs.map +7 -0
  7. package/dist/lib/browser/{chunk-U2JHW3L6.mjs → chunk-ZL2V5UJR.mjs} +206 -53
  8. package/dist/lib/browser/chunk-ZL2V5UJR.mjs.map +7 -0
  9. package/dist/lib/browser/graph-M4IQ76QX.mjs +33 -0
  10. package/dist/lib/browser/index.mjs +45 -21
  11. package/dist/lib/browser/index.mjs.map +4 -4
  12. package/dist/lib/browser/meta.json +1 -1
  13. package/dist/lib/browser/types.mjs +1 -1
  14. package/dist/lib/node/{SheetContainer-3ZY7MPWJ.cjs → SheetContainer-PV5ET4UJ.cjs} +21 -20
  15. package/dist/lib/node/SheetContainer-PV5ET4UJ.cjs.map +7 -0
  16. package/dist/lib/node/{chunk-OTTD7FBK.cjs → chunk-2K53Z2TU.cjs} +224 -70
  17. package/dist/lib/node/chunk-2K53Z2TU.cjs.map +7 -0
  18. package/dist/lib/node/{chunk-DD6FIXWC.cjs → chunk-5XPK2V4A.cjs} +222 -175
  19. package/dist/lib/node/chunk-5XPK2V4A.cjs.map +7 -0
  20. package/dist/lib/node/{chunk-Q3HBHPRL.cjs → chunk-STAVQ2JE.cjs} +21 -9
  21. package/dist/lib/node/{chunk-Q3HBHPRL.cjs.map → chunk-STAVQ2JE.cjs.map} +3 -3
  22. package/dist/lib/node/graph-Q3N2X26H.cjs +55 -0
  23. package/dist/lib/node/graph-Q3N2X26H.cjs.map +7 -0
  24. package/dist/lib/node/index.cjs +51 -30
  25. package/dist/lib/node/index.cjs.map +4 -4
  26. package/dist/lib/node/meta.json +1 -1
  27. package/dist/lib/node/types.cjs +8 -8
  28. package/dist/lib/node/types.cjs.map +1 -1
  29. package/dist/lib/node-esm/{SheetContainer-PXSJX6XK.mjs → SheetContainer-FOZD2WLT.mjs} +14 -13
  30. package/dist/lib/node-esm/SheetContainer-FOZD2WLT.mjs.map +7 -0
  31. package/dist/lib/node-esm/{chunk-BMNA27EX.mjs → chunk-2HAM45RC.mjs} +18 -6
  32. package/dist/lib/node-esm/{chunk-BMNA27EX.mjs.map → chunk-2HAM45RC.mjs.map} +3 -3
  33. package/dist/lib/node-esm/{chunk-D6KU5MI7.mjs → chunk-5WPZCXNS.mjs} +220 -177
  34. package/dist/lib/node-esm/chunk-5WPZCXNS.mjs.map +7 -0
  35. package/dist/lib/node-esm/{chunk-7HVSOTGA.mjs → chunk-QEUCIHIN.mjs} +206 -53
  36. package/dist/lib/node-esm/chunk-QEUCIHIN.mjs.map +7 -0
  37. package/dist/lib/node-esm/graph-SMPUMOV2.mjs +34 -0
  38. package/dist/lib/node-esm/index.mjs +45 -21
  39. package/dist/lib/node-esm/index.mjs.map +4 -4
  40. package/dist/lib/node-esm/meta.json +1 -1
  41. package/dist/lib/node-esm/types.mjs +1 -1
  42. package/dist/types/src/SheetPlugin.d.ts.map +1 -1
  43. package/dist/types/src/components/CellEditor/CellEditor.stories.d.ts.map +1 -1
  44. package/dist/types/src/components/CellEditor/extension.d.ts.map +1 -1
  45. package/dist/types/src/components/GridSheet/GridSheet.d.ts +3 -3
  46. package/dist/types/src/components/GridSheet/GridSheet.d.ts.map +1 -1
  47. package/dist/types/src/components/GridSheet/GridSheet.stories.d.ts +1 -1
  48. package/dist/types/src/components/GridSheet/GridSheet.stories.d.ts.map +1 -1
  49. package/dist/types/src/components/GridSheet/util.d.ts +12 -2
  50. package/dist/types/src/components/GridSheet/util.d.ts.map +1 -1
  51. package/dist/types/src/components/Sheet/Sheet.d.ts.map +1 -1
  52. package/dist/types/src/components/Sheet/Sheet.stories.d.ts.map +1 -1
  53. package/dist/types/src/components/Sheet/sheet-context.d.ts +3 -3
  54. package/dist/types/src/components/Sheet/sheet-context.d.ts.map +1 -1
  55. package/dist/types/src/components/SheetContainer.d.ts +1 -1
  56. package/dist/types/src/components/SheetContainer.d.ts.map +1 -1
  57. package/dist/types/src/components/index.d.ts +1 -1
  58. package/dist/types/src/components/index.d.ts.map +1 -1
  59. package/dist/types/src/defs/types.d.ts.map +1 -1
  60. package/dist/types/src/defs/util.d.ts +1 -1
  61. package/dist/types/src/defs/util.d.ts.map +1 -1
  62. package/dist/types/src/extensions/compute.d.ts +3 -2
  63. package/dist/types/src/extensions/compute.d.ts.map +1 -1
  64. package/dist/types/src/extensions/compute.stories.d.ts.map +1 -1
  65. package/dist/types/src/graph/compute-graph-registry.d.ts +34 -0
  66. package/dist/types/src/graph/compute-graph-registry.d.ts.map +1 -0
  67. package/dist/types/src/graph/compute-graph.d.ts +17 -34
  68. package/dist/types/src/graph/compute-graph.d.ts.map +1 -1
  69. package/dist/types/src/graph/compute-graph.stories.d.ts.map +1 -1
  70. package/dist/types/src/graph/compute-graph.test.d.ts +2 -0
  71. package/dist/types/src/graph/compute-graph.test.d.ts.map +1 -0
  72. package/dist/types/src/graph/compute-node.d.ts +9 -2
  73. package/dist/types/src/graph/compute-node.d.ts.map +1 -1
  74. package/dist/types/src/graph/{async-function.d.ts → functions/async-function.d.ts} +13 -4
  75. package/dist/types/src/graph/functions/async-function.d.ts.map +1 -0
  76. package/dist/types/src/graph/functions/edge-function.d.ts +21 -0
  77. package/dist/types/src/graph/functions/edge-function.d.ts.map +1 -0
  78. package/dist/types/src/graph/functions/function-defs.d.ts.map +1 -0
  79. package/dist/types/src/graph/functions/index.d.ts +4 -0
  80. package/dist/types/src/graph/functions/index.d.ts.map +1 -0
  81. package/dist/types/src/graph/index.d.ts +2 -1
  82. package/dist/types/src/graph/index.d.ts.map +1 -1
  83. package/dist/types/src/graph/testing/index.d.ts +3 -0
  84. package/dist/types/src/graph/testing/index.d.ts.map +1 -0
  85. package/dist/types/src/graph/testing/test-builder.d.ts +15 -0
  86. package/dist/types/src/graph/testing/test-builder.d.ts.map +1 -0
  87. package/dist/types/src/graph/testing/test-plugin.d.ts +36 -0
  88. package/dist/types/src/graph/testing/test-plugin.d.ts.map +1 -0
  89. package/dist/types/src/hooks/useComputeGraph.d.ts.map +1 -1
  90. package/dist/types/src/hooks/useSheetModel.d.ts +2 -2
  91. package/dist/types/src/hooks/useSheetModel.d.ts.map +1 -1
  92. package/dist/types/src/model/sheet-model.d.ts +3 -3
  93. package/dist/types/src/model/sheet-model.d.ts.map +1 -1
  94. package/dist/types/src/model/sheet-model.test.d.ts +2 -0
  95. package/dist/types/src/model/sheet-model.test.d.ts.map +1 -0
  96. package/dist/types/src/testing/testing.d.ts +4 -5
  97. package/dist/types/src/testing/testing.d.ts.map +1 -1
  98. package/dist/types/src/types.d.ts +4 -3
  99. package/dist/types/src/types.d.ts.map +1 -1
  100. package/package.json +40 -39
  101. package/src/SheetPlugin.tsx +19 -15
  102. package/src/components/CellEditor/CellEditor.stories.tsx +2 -3
  103. package/src/components/CellEditor/extension.test.ts +0 -1
  104. package/src/components/CellEditor/extension.ts +4 -3
  105. package/src/components/GridSheet/GridSheet.stories.tsx +5 -4
  106. package/src/components/GridSheet/GridSheet.tsx +27 -9
  107. package/src/components/GridSheet/util.ts +93 -34
  108. package/src/components/Sheet/Sheet.stories.tsx +21 -20
  109. package/src/components/Sheet/Sheet.tsx +30 -14
  110. package/src/components/Sheet/sheet-context.tsx +4 -4
  111. package/src/components/SheetContainer.tsx +13 -15
  112. package/src/defs/types.ts +1 -0
  113. package/src/defs/util.ts +19 -3
  114. package/src/extensions/compute.stories.tsx +20 -20
  115. package/src/extensions/compute.ts +91 -42
  116. package/src/graph/compute-graph-registry.ts +90 -0
  117. package/src/graph/compute-graph.stories.tsx +4 -3
  118. package/src/graph/compute-graph.test.ts +87 -0
  119. package/src/graph/compute-graph.ts +73 -121
  120. package/src/graph/compute-node.ts +17 -5
  121. package/src/graph/{async-function.ts → functions/async-function.ts} +23 -15
  122. package/src/graph/{edge-function.ts → functions/edge-function.ts} +14 -13
  123. package/src/graph/functions/index.ts +7 -0
  124. package/src/graph/hyperformula.test.ts +1 -2
  125. package/src/graph/index.ts +2 -1
  126. package/src/graph/testing/index.ts +6 -0
  127. package/src/graph/testing/test-builder.ts +54 -0
  128. package/src/graph/{custom-function.ts → testing/test-plugin.ts} +43 -9
  129. package/src/hooks/hooks.stories.tsx +3 -3
  130. package/src/hooks/useComputeGraph.ts +9 -1
  131. package/src/hooks/useSheetModel.ts +4 -7
  132. package/src/model/sheet-model.test.ts +57 -0
  133. package/src/model/sheet-model.ts +47 -30
  134. package/src/testing/testing.tsx +17 -15
  135. package/src/types.ts +7 -7
  136. package/dist/lib/browser/SheetContainer-V4GCCZTX.mjs.map +0 -7
  137. package/dist/lib/browser/chunk-6ZMQVB4Z.mjs.map +0 -7
  138. package/dist/lib/browser/chunk-U2JHW3L6.mjs.map +0 -7
  139. package/dist/lib/browser/graph-T27BOBOV.mjs +0 -21
  140. package/dist/lib/node/SheetContainer-3ZY7MPWJ.cjs.map +0 -7
  141. package/dist/lib/node/chunk-DD6FIXWC.cjs.map +0 -7
  142. package/dist/lib/node/chunk-OTTD7FBK.cjs.map +0 -7
  143. package/dist/lib/node/graph-SPKGX7W4.cjs +0 -43
  144. package/dist/lib/node/graph-SPKGX7W4.cjs.map +0 -7
  145. package/dist/lib/node-esm/SheetContainer-PXSJX6XK.mjs.map +0 -7
  146. package/dist/lib/node-esm/chunk-7HVSOTGA.mjs.map +0 -7
  147. package/dist/lib/node-esm/chunk-D6KU5MI7.mjs.map +0 -7
  148. package/dist/lib/node-esm/graph-U67IO4UC.mjs +0 -22
  149. package/dist/types/src/graph/async-function.d.ts.map +0 -1
  150. package/dist/types/src/graph/compute-graph.browser.test.d.ts +0 -2
  151. package/dist/types/src/graph/compute-graph.browser.test.d.ts.map +0 -1
  152. package/dist/types/src/graph/custom-function.d.ts +0 -21
  153. package/dist/types/src/graph/custom-function.d.ts.map +0 -1
  154. package/dist/types/src/graph/edge-function.d.ts +0 -20
  155. package/dist/types/src/graph/edge-function.d.ts.map +0 -1
  156. package/dist/types/src/graph/function-defs.d.ts.map +0 -1
  157. package/src/graph/compute-graph.browser.test.ts +0 -104
  158. /package/dist/lib/browser/{graph-T27BOBOV.mjs.map → graph-M4IQ76QX.mjs.map} +0 -0
  159. /package/dist/lib/node-esm/{graph-U67IO4UC.mjs.map → graph-SMPUMOV2.mjs.map} +0 -0
  160. /package/dist/types/src/graph/{function-defs.d.ts → functions/function-defs.d.ts} +0 -0
  161. /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
- recalculationDelay: number;
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
- recalculationDelay: 200,
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
- private readonly _onUpdate: () => void;
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(this);
83
- }, this._options.recalculationDelay);
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 FunctionPluginAsync extends FunctionPlugin {
163
+ export class AsyncFunctionPlugin extends FunctionPlugin {
159
164
  get context() {
160
165
  return this.config.context as FunctionContext;
161
166
  }
162
167
 
163
- runAsyncFunction(ast: ProcedureAst, state: InterpreterState, cb: AsyncFunction, options?: FunctionOptions) {
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, FunctionPluginAsync } from './async-function';
16
+ import { type AsyncFunction, AsyncFunctionPlugin } from './async-function';
17
17
 
18
- const EDGE_FUNCTION_TTL = 10_000;
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 FunctionPluginAsync {
26
- edge(ast: ProcedureAst, state: InterpreterState) {
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: EDGE_FUNCTION_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: EDGE_FUNCTION_TTL });
68
+ return this.runAsyncFunction(ast, state, handler(true), { ttl: FUNCTION_TTL });
67
69
  }
68
70
  }
69
71
 
70
72
  EdgeFunctionPlugin.implementedFunctions = {
71
- EDGE: {
72
- method: 'edge',
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 9).
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
- EDGE: 'EDGE',
95
+ [EDGE_FUNCTION_NAME]: 'Remote function',
95
96
  },
96
97
  enUS: {
97
- EDGE: 'EDGE',
98
+ [EDGE_FUNCTION_NAME]: 'Remote function',
98
99
  },
99
100
  };
@@ -0,0 +1,7 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ export * from './async-function';
6
+ export * from './edge-function';
7
+ export * from './function-defs';
@@ -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
  });
@@ -3,5 +3,6 @@
3
3
  //
4
4
 
5
5
  export * from './compute-graph';
6
+ export * from './compute-graph-registry';
6
7
  export * from './compute-node';
7
- export * from './function-defs';
8
+ export * from './functions';
@@ -0,0 +1,6 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ export * from './test-builder';
6
+ export * from './test-plugin';
@@ -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 AsyncFunction, FunctionPluginAsync } from './async-function';
12
- import { parseNumberString } from './util';
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 CustomPlugin extends FunctionPluginAsync {
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
- CustomPlugin.implementedFunctions = {
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 CustomPluginTranslations = {
82
+ export const TestPluginTranslations = {
58
83
  enGB: {
59
- TEST: 'TEST',
60
- CRYPTO: 'CRYPTO',
84
+ TEST: 'Returns input value',
85
+ RANDOM: 'Random number',
86
+ CRYPTO: 'Crypto token value',
61
87
  },
62
88
  enUS: {
63
- TEST: 'TEST',
64
- CRYPTO: 'CRYPTO',
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 { withGraphDecorator } from '../testing';
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(space, sheet);
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
- withGraphDecorator,
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
- return useAsyncState(async () => space && registry.getOrCreateGraph(space), [space, registry]);
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 Space } from '@dxos/react-client/echo';
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
- space?: Space,
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 (!space || !graph || !sheet) {
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
- }, [space, sheet, graph, readonly]);
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
+ });