@heswell/vuu-data-engine-local 2.0.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +167 -25
  2. package/index.js +3771 -3318
  3. package/legacy.js +8 -0
  4. package/package.json +12 -8
  5. package/src/EngineDataSource.d.ts +13 -3
  6. package/src/ModuleDataSource.d.ts +3 -3
  7. package/src/UpdateGenerator.d.ts +12 -0
  8. package/src/basket/BasketModule.d.ts +12 -2
  9. package/src/basket/index.d.ts +1 -1
  10. package/src/core/engine/LocalEngine.d.ts +62 -0
  11. package/src/core/filter/PermissionFilter.d.ts +2 -2
  12. package/src/core/module/VuuModule.d.ts +68 -1
  13. package/src/core/module/edit-hooks.d.ts +135 -0
  14. package/src/core/table/TableContainer.d.ts +35 -11
  15. package/src/core/table/defaultTableContainer.d.ts +6 -0
  16. package/src/data-utils.d.ts +7 -0
  17. package/src/demo-modules.d.ts +18 -0
  18. package/src/index.d.ts +7 -2
  19. package/src/legacy.d.ts +11 -0
  20. package/src/local-datasource-provider/LocalDatasourceProvider.d.ts +18 -7
  21. package/src/local-datasource-provider/createDataSourceProvider.d.ts +12 -0
  22. package/src/module-admin/ModuleAdminModule.d.ts +0 -1
  23. package/src/module-admin/index.d.ts +1 -1
  24. package/src/notifications/NotificationModule.d.ts +1 -0
  25. package/src/notifications/index.d.ts +0 -1
  26. package/src/simul/SimulModule.d.ts +9 -1
  27. package/src/simul/index.d.ts +1 -1
  28. package/src/simul/reference-data/instrument-prices.d.ts +2 -2
  29. package/src/simul/reference-data/instruments-extended.d.ts +1 -1
  30. package/src/simul/reference-data/instruments.d.ts +2 -3
  31. package/src/simul/reference-data/orders.d.ts +2 -3
  32. package/src/simul/reference-data/parent-child-orders.d.ts +15 -4
  33. package/src/simul/reference-data/prices.d.ts +18 -6
  34. package/src/test/TestModule.d.ts +2 -3
  35. package/src/user-admin/UserAdminModule.d.ts +0 -1
  36. package/src/user-admin/index.d.ts +1 -1
  37. package/src/basket/reference-data/prices.d.ts +0 -24
  38. package/src/core/module/ModuleContainer.d.ts +0 -15
  39. package/src/notifications/simulated/notificationsModule.d.ts +0 -5
package/legacy.js ADDED
@@ -0,0 +1,8 @@
1
+ import { getDefaultEngine, UserAdminModule, SimulatedNotificationsModule, registerDemoModules, ModuleAdminModule } from "./index.js";
2
+ const engine = getDefaultEngine();
3
+ const { simulModule: simulModule, basketModule: basketModule, testModule: testModule } = registerDemoModules(engine);
4
+ const moduleAdminModule = engine.register(new ModuleAdminModule());
5
+ const userAdminModule = engine.register(new UserAdminModule());
6
+ const notificationsModule = engine.register(SimulatedNotificationsModule());
7
+ export { AllowAllPermissionFilter, ArrayProxy, BaseUpdateGenerator, BasketModule, DUPLICATE_KEY, DefaultColumnGenerator, DefaultRowGenerator, DenyAllPermissionFilter, DismissNotificationRpcHandler, EngineDataSource, LocalDataSourceProvider, LocalEngine, MODULE_ADMIN_INITIAL_SNAPSHOT, MODULE_ADMIN_MODULE_NAME, MODULE_ADMIN_TABLE_SCHEMAS, ModuleAdminModule, ModuleDataSource, NotificationModule, NotificationsSchema, PermissionFilter, SAVE_FAILED, SAVE_NOT_PERMITTED, STALE_UPDATE, SimulModule, SimulatedNotificationsModule, SimulatedNotificationsProvider, Table, TableContainer, TestModule, TickingArrayDataSource, USER_ADMIN_INITIAL_SNAPSHOT, UserAdminModule, VUU_SYSTEM_COLUMNS, VuuModule, basketSchemas, buildDataColumnMap, buildDataColumnMapFromSchema, createDataSourceProvider, createJoinTable, createLocalEngine, createLocalVuuServer, createTable, defaultApplySessionSave, defaultCanEditSource, defaultGenerators, defaultGetStaleColumns, defaultIsEditableColumn, ensureVuuModule, formatMessage, getAllSchemas, getColumnAndRowGenerator, getCurrentUser, getDefaultEngine, getSchema, isBasketTable, isSimulTable, isSystemColumn, makeSuggestions, populateArray, projectModuleAdminSnapshot, projectUserAdminSnapshot, reconcileModuleAdminTables, reconcileUserAdminTables, registerDemoModules, resetDefaultEngine, schemas, setCurrentUser, simulSchemas, tableContainer, testSchemas, toColumn, toRange } from "./index.js";
8
+ export { basketModule, moduleAdminModule, notificationsModule, simulModule, testModule, userAdminModule };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heswell/vuu-data-engine-local",
3
- "version": "2.0.1",
3
+ "version": "3.0.0",
4
4
  "description": "In-browser Vuu server (VuuModule, tables, EngineDataSource) built on @heswell/vuu-table and @heswell/vuu-viewport",
5
5
  "main": "./index.js",
6
6
  "module": "./index.js",
@@ -9,6 +9,10 @@
9
9
  ".": {
10
10
  "types": "./src/index.d.ts",
11
11
  "import": "./index.js"
12
+ },
13
+ "./legacy": {
14
+ "types": "./src/legacy.d.ts",
15
+ "import": "./legacy.js"
12
16
  }
13
17
  },
14
18
  "type": "module",
@@ -20,20 +24,20 @@
20
24
  "dependencies": {
21
25
  "@heswell/module-admin": "^2.0.1",
22
26
  "@heswell/user-admin": "^1.0.2",
23
- "@heswell/vuu-table": "^1.0.1",
24
- "@heswell/vuu-viewport": "^1.1.3",
25
- "@vuu-ui/vuu-data-editing": "4.0.0-alpha.20",
26
- "@vuu-ui/vuu-data-local": "4.0.0-alpha.20",
27
- "@vuu-ui/vuu-filter-parser": "4.0.0-alpha.20",
28
- "@vuu-ui/vuu-utils": "4.0.0-alpha.20"
27
+ "@heswell/vuu-table": "^1.0.2",
28
+ "@heswell/vuu-viewport": "^1.1.4"
29
29
  },
30
30
  "peerDependencies": {
31
31
  "@vuu-ui/core": "^4.0.0-alpha.20",
32
32
  "@vuu-ui/vuu-data-types": "^4.0.0-alpha.20",
33
+ "@vuu-ui/vuu-filter-parser": "^4.0.0-alpha.20",
33
34
  "@vuu-ui/vuu-filter-types": "^4.0.0-alpha.20",
34
35
  "@vuu-ui/vuu-protocol-types": "^4.0.0-alpha.20",
35
36
  "@vuu-ui/vuu-table-types": "^4.0.0-alpha.20",
37
+ "@vuu-ui/vuu-utils": "^4.0.0-alpha.20",
36
38
  "react": "^19.2.3"
37
39
  },
38
- "sideEffects": false
40
+ "sideEffects": [
41
+ "./legacy.js"
42
+ ]
39
43
  }
@@ -1,9 +1,18 @@
1
1
  import { type DataEngine, type LinkFilter, type RowPredicate, type ViewportEngine } from "@heswell/vuu-viewport";
2
2
  import type { DataSourceBase, DataSourceConstructorProps, DataSourceEvents, DataSourceFilter, DataSourceRowWithBigint, DataSourceStatus, DataSourceSubscribeCallback, DataSourceSubscribeProps, TableSchema, WithBaseFilter, WithFullConfig } from "@vuu-ui/vuu-data-types";
3
3
  import type { Filter } from "@vuu-ui/vuu-filter-types";
4
- import type { LinkDescriptorWithLabel, SelectRequest, VuuAggregation, VuuGroupBy, VuuMenu, VuuRowDataItemType, VuuRpcMenuRequest, VuuRpcMenuResponse, VuuRpcServiceRequest, RpcResultError, RpcResultSuccess, VuuSort } from "@vuu-ui/vuu-protocol-types";
4
+ import type { LinkDescriptorWithLabel, SelectRequest, VuuAggregation, VuuGroupBy, VuuMenu, VuuRange, VuuRowDataItemType, VuuRpcMenuRequest, VuuRpcMenuResponse, VuuRpcServiceRequest, RpcResultError, RpcResultSuccess, VuuSort } from "@vuu-ui/vuu-protocol-types";
5
5
  import type { ColumnDescriptor } from "@vuu-ui/vuu-table-types";
6
6
  import { type ColumnMap, EventEmitter, Range } from "@vuu-ui/vuu-utils";
7
+ /**
8
+ * Accept either a vuu-utils `Range` or a plain `{from, to}` (VuuRange),
9
+ * which is converted to a `Range`.
10
+ */
11
+ /** `range` may be a plain `{from, to}`. */
12
+ export type EngineDataSourceSubscribeProps = Omit<DataSourceSubscribeProps, "range"> & {
13
+ range?: Range | VuuRange;
14
+ };
15
+ export declare const toRange: (range: VuuRange | Range) => Range;
7
16
  import { Table } from "./Table";
8
17
  export interface EngineDataSourceConstructorProps extends Omit<DataSourceConstructorProps, "bufferSize" | "table"> {
9
18
  /**
@@ -38,7 +47,7 @@ export declare class EngineDataSource extends EventEmitter<DataSourceEvents> imp
38
47
  sendSizeUpdateToClient(): void;
39
48
  /** Send every row in the current range, irrespective of what was sent before */
40
49
  sendRowsToClient(): void;
41
- subscribe({ viewport, columns, aggregations, baseFilterSpec, range, sort, groupBy, filterSpec, }: DataSourceSubscribeProps, callback: DataSourceSubscribeCallback): Promise<void>;
50
+ subscribe({ viewport, columns, aggregations, baseFilterSpec, range, sort, groupBy, filterSpec, }: EngineDataSourceSubscribeProps, callback: DataSourceSubscribeCallback): Promise<void>;
42
51
  unsubscribe(): void;
43
52
  suspend(): void;
44
53
  resume(callback?: DataSourceSubscribeCallback): void;
@@ -46,10 +55,11 @@ export declare class EngineDataSource extends EventEmitter<DataSourceEvents> imp
46
55
  enable(): void;
47
56
  get status(): DataSourceStatus;
48
57
  get range(): Range;
58
+ /** A plain `{from, to}` is also accepted at runtime. */
49
59
  set range(range: Range);
50
60
  get maxRangeEnd(): number;
51
61
  get pageSize(): number;
52
- protected setRange(range: Range, forceFullRefresh?: boolean): void;
62
+ protected setRange(vuuRange: Range | VuuRange, forceFullRefresh?: boolean): void;
53
63
  get config(): WithBaseFilter<WithFullConfig>;
54
64
  set config(config: WithBaseFilter<WithFullConfig>);
55
65
  get aggregations(): VuuAggregation[];
@@ -1,7 +1,7 @@
1
- import type { DataSourceBase, DataSource, DataSourceRowWithBigint, DataSourceSubscribeCallback, DataSourceSubscribeProps, DataSourceVisualLinkCreatedMessage, DeleteRowMode, CopyOption, EditSessionMode, SessionDataSourceOverrides, SessionType } from "@vuu-ui/vuu-data-types";
1
+ import type { DataSourceBase, DataSource, DataSourceRowWithBigint, DataSourceSubscribeCallback, DataSourceVisualLinkCreatedMessage, DeleteRowMode, CopyOption, EditSessionMode, SessionDataSourceOverrides, SessionType } from "@vuu-ui/vuu-data-types";
2
2
  import type { LinkDescriptorWithLabel, RpcResult, RpcResultError, RpcResultSuccess, VuuCreateVisualLink, VuuRemoveVisualLink, VuuRowDataItemType, VuuRpcMenuRequest, VuuRpcMenuResponse, VuuRpcServiceRequest } from "@vuu-ui/vuu-protocol-types";
3
3
  import type { IVuuModule, RpcMenuService, RpcService } from "./core/module/VuuModule";
4
- import { EngineDataSource, type EngineDataSourceConstructorProps } from "./EngineDataSource";
4
+ import { EngineDataSource, type EngineDataSourceConstructorProps, type EngineDataSourceSubscribeProps } from "./EngineDataSource";
5
5
  export type VisualLinkHandler = (message: VuuCreateVisualLink | VuuRemoveVisualLink) => Promise<DataSourceVisualLinkCreatedMessage | undefined>;
6
6
  export interface ModuleDataSourceConstructorProps extends EngineDataSourceConstructorProps {
7
7
  getVisualLinks?: (tableName: string) => LinkDescriptorWithLabel[] | undefined;
@@ -18,7 +18,7 @@ export interface ModuleDataSourceConstructorProps extends EngineDataSourceConstr
18
18
  export declare class ModuleDataSource extends EngineDataSource {
19
19
  #private;
20
20
  constructor({ getVisualLinks, rpcServices, rpcMenuServices, session, visualLink, visualLinkService, vuuModule, ...engineDataSourceProps }: ModuleDataSourceConstructorProps);
21
- subscribe(subscribeProps: DataSourceSubscribeProps, callback: DataSourceSubscribeCallback): Promise<void>;
21
+ subscribe(subscribeProps: EngineDataSourceSubscribeProps, callback: DataSourceSubscribeCallback): Promise<void>;
22
22
  set links(links: LinkDescriptorWithLabel[] | undefined);
23
23
  get links(): LinkDescriptorWithLabel[] | undefined;
24
24
  isSessionDataSourceOf(dataSource: DataSource): boolean;
@@ -7,10 +7,22 @@ export declare class BaseUpdateGenerator implements UpdateGenerator {
7
7
  private updating;
8
8
  private timer;
9
9
  private tickingColumns;
10
+ private enabled;
11
+ /**
12
+ * @param autoStart default true, start ticking as soon as a table and
13
+ * range are set. With false, ticking starts only when `start` is called.
14
+ */
10
15
  constructor(tickingColumns: {
11
16
  [key: string]: number;
17
+ }, { autoStart }?: {
18
+ autoStart?: boolean;
12
19
  });
13
20
  setRange(range: VuuRange): void;
21
+ get isUpdating(): boolean;
22
+ /** Start (or resume) ticking. */
23
+ start(): void;
24
+ /** Stop ticking, clearing any pending timer. */
25
+ stop(): void;
14
26
  setTable(table: Table): void;
15
27
  private startUpdating;
16
28
  private stopUpdating;
@@ -2,6 +2,15 @@ import type { VuuMenu } from "@vuu-ui/vuu-protocol-types";
2
2
  import { type RpcMenuService, type ServiceHandler, VuuModule } from "../core/module/VuuModule";
3
3
  import { type BasketsTableName } from "./basket-schemas";
4
4
  import type { Table } from "../Table";
5
+ import { type PricesTable } from "../simul/reference-data/prices";
6
+ export type BasketModuleOptions = {
7
+ /**
8
+ * The prices table joined with basket constituents, typically the SIMUL
9
+ * module's prices (`simulModule.prices`). If not provided, the module
10
+ * creates its own prices table.
11
+ */
12
+ prices?: PricesTable;
13
+ };
5
14
  export declare const createBasketTradingRow: (basketId: string, basketName: string, side?: string, status?: string) => (string | number)[];
6
15
  /**
7
16
  * This is an example of how we might extend the built-in VuuModule to
@@ -10,7 +19,9 @@ export declare const createBasketTradingRow: (basketId: string, basketName: stri
10
19
  */
11
20
  export declare class BasketModule extends VuuModule<BasketsTableName> {
12
21
  #private;
13
- constructor();
22
+ constructor({ prices }?: BasketModuleOptions);
23
+ protected beforeCreateDataSource(): void;
24
+ dispose(): void;
14
25
  get menus(): Record<BasketsTableName, VuuMenu | undefined>;
15
26
  get schemas(): Readonly<Record<BasketsTableName, Readonly<import("@vuu-ui/vuu-data-types").TableSchema>>>;
16
27
  get services(): {
@@ -52,4 +63,3 @@ export declare class BasketModule extends VuuModule<BasketsTableName> {
52
63
  private takeOffMarket;
53
64
  private addConstituent;
54
65
  }
55
- export declare const basketModule: BasketModule;
@@ -1,2 +1,2 @@
1
1
  export { type BasketsTableName, schemas as basketSchemas, isBasketTable, } from "./basket-schemas";
2
- export { basketModule } from "./BasketModule";
2
+ export { BasketModule, type BasketModuleOptions } from "./BasketModule";
@@ -0,0 +1,62 @@
1
+ import type { DataSourceConfig, SessionDataSourceOverrides, TableSchema } from "@vuu-ui/vuu-data-types";
2
+ import type { VuuTable } from "@vuu-ui/vuu-protocol-types";
3
+ import type { VuuModule } from "../module/VuuModule";
4
+ import { TableContainer } from "../table/TableContainer";
5
+ export type AnyVuuModule = VuuModule<any>;
6
+ export interface LocalEngineOptions {
7
+ /** Modules to register on creation. */
8
+ modules?: AnyVuuModule[];
9
+ }
10
+ /**
11
+ * An in-browser Vuu server: a set of VuuModules and the tables they publish,
12
+ * keyed by `{module, table}`. Engines are fully isolated from each other, so
13
+ * two engines (or two modules in one engine) can use the same table names.
14
+ *
15
+ * The engine has no React or DOM dependency. Use `createDataSourceProvider`
16
+ * or `createLocalVuuServer` to expose it to a React app.
17
+ */
18
+ export declare class LocalEngine {
19
+ #private;
20
+ readonly tableContainer: TableContainer;
21
+ constructor({ modules }?: LocalEngineOptions);
22
+ /**
23
+ * Register a module. A module already registered under the same name is
24
+ * replaced (and unregistered). Its tables are added to `tableContainer`,
25
+ * keyed by `{module: module.name, table}`.
26
+ */
27
+ register<M extends AnyVuuModule>(module: M): M;
28
+ /** Remove a module and its tables. The module is not disposed. */
29
+ unregister(moduleName: string): boolean;
30
+ hasModule(moduleName: string): boolean;
31
+ getModule<M extends AnyVuuModule = AnyVuuModule>(moduleName: string): M;
32
+ get modules(): AnyVuuModule[];
33
+ get moduleNames(): string[];
34
+ /** Every table published by the registered modules. */
35
+ getTableList(): VuuTable[];
36
+ getTableSchema({ module, table }: VuuTable): TableSchema | undefined;
37
+ createDataSource({ module, table }: VuuTable, viewport?: string, config?: DataSourceConfig & {
38
+ session?: SessionDataSourceOverrides;
39
+ }): import("@vuu-ui/vuu-data-types").DataSourceBase<import("@vuu-ui/vuu-data-types").DataSourceRowWithBigint>;
40
+ get disposed(): boolean;
41
+ /**
42
+ * Unregister every module and dispose those owned by this engine (that is,
43
+ * first registered here), stopping any timers they started.
44
+ */
45
+ dispose(): void;
46
+ }
47
+ /** Create an isolated local engine, optionally registering modules. */
48
+ export declare const createLocalEngine: (options?: LocalEngineOptions) => LocalEngine;
49
+ /**
50
+ * The shared engine behind the deprecated globals (`tableContainer`,
51
+ * `LocalDataSourceProvider`, `ensureVuuModule`) and the `/legacy` entry.
52
+ * Created on first use.
53
+ */
54
+ export declare const getDefaultEngine: () => LocalEngine;
55
+ /** Dispose the default engine; the next `getDefaultEngine()` creates a new one. */
56
+ export declare const resetDefaultEngine: () => void;
57
+ /**
58
+ * Register a module with the default engine, unless a module with that name
59
+ * is already registered there.
60
+ * @deprecated create an engine with `createLocalEngine({ modules })`.
61
+ */
62
+ export declare const ensureVuuModule: <T extends AnyVuuModule>(module: T) => T;
@@ -1,7 +1,7 @@
1
1
  import type { VuuRowDataItemType } from "@vuu-ui/vuu-protocol-types";
2
2
  import type { VuuModuleUser } from "../user/CurrentUser";
3
3
  import type { ColumnMap } from "@vuu-ui/vuu-utils";
4
- import type tableContainer from "../table/TableContainer";
4
+ import type { TableContainer } from "../table/TableContainer";
5
5
  export type TableRow = Array<bigint | VuuRowDataItemType>;
6
6
  export type RowPredicate = (row: TableRow) => boolean;
7
7
  /**
@@ -29,7 +29,7 @@ export interface PermissionViewport {
29
29
  id: string;
30
30
  user: VuuModuleUser;
31
31
  }
32
- export type TableContainer = typeof tableContainer;
32
+ export type { TableContainer };
33
33
  /**
34
34
  * Creates the PermissionFilter for a viewport. Configured per table on a VuuModule.
35
35
  */
@@ -1,9 +1,12 @@
1
1
  import type { DataSourceBase, DataSourceConfig, DataSourceRowWithBigint, DataSourceSubscribedMessage, DataSourceVisualLinkCreatedMessage, EditSessionMode, CopyOption, SessionDataSourceOverrides, SessionType, TableSchema } from "@vuu-ui/vuu-data-types";
2
2
  import type { VuuMenu, VuuLink, LinkDescriptorWithLabel, VuuRpcMenuResponse, VuuCreateVisualLink, VuuRemoveVisualLink, VuuRpcServiceRequest, RpcResultSuccess, RpcResultError, VuuRpcMenuRequest } from "@vuu-ui/vuu-protocol-types";
3
- import { Table } from "../../Table";
3
+ import { Table, type TableRow } from "../../Table";
4
4
  import { ModuleDataSource } from "../../ModuleDataSource";
5
5
  import { type PermissionFunction } from "../filter/PermissionFilter";
6
6
  import { type VuuModuleUser } from "../user/CurrentUser";
7
+ import { TableContainer } from "../table/TableContainer";
8
+ import type { LocalEngine } from "../engine/LocalEngine";
9
+ import { type CanSaveSessionResult, type CellEdit, type EditContext, type EditHooks, type PreparedSessionSave, type SessionRowChange, type SessionSaveResult } from "./edit-hooks";
7
10
  export interface IVuuModule<T extends string = string> {
8
11
  createDataSource: (tableName: T, viewport?: string, config?: DataSourceConfig & {
9
12
  session?: SessionDataSourceOverrides;
@@ -39,16 +42,67 @@ type Subscription = {
39
42
  };
40
43
  export declare abstract class VuuModule<T extends string = string> implements IVuuModule<T> {
41
44
  #private;
45
+ /**
46
+ * Modules are not registered anywhere on construction. Register them with
47
+ * a LocalEngine (`createLocalEngine({ modules })`).
48
+ */
42
49
  constructor(name: string, { sessionTableMessageColumn }?: VuuModuleOptions);
50
+ /** The engine this module was first registered with, if any. */
51
+ get engine(): LocalEngine | undefined;
52
+ /** @internal called by LocalEngine.register */
53
+ attachEngine(engine: LocalEngine): void;
54
+ /** @internal called by LocalEngine.unregister */
55
+ detachEngine(engine: LocalEngine): void;
56
+ /**
57
+ * Release resources (timers, providers). Called by `LocalEngine.dispose`
58
+ * for modules it owns. Subclasses that start timers should override,
59
+ * calling super.dispose().
60
+ */
61
+ dispose(): void;
43
62
  protected abstract menus?: Record<T, VuuMenu | undefined> | undefined;
44
63
  protected abstract schemas: Record<T, Readonly<TableSchema>>;
45
64
  protected abstract tables: Record<T, Table>;
46
65
  protected abstract menuServices?: Record<T, RpcMenuService[] | undefined> | undefined;
66
+ /**
67
+ * Per table services. A service with the same rpcName as a built-in
68
+ * service (e.g. endEditSession, editCell) replaces it for that table,
69
+ * including for edit sessions created from the table.
70
+ */
47
71
  protected abstract services?: Record<T, RpcService[] | undefined> | undefined;
72
+ /**
73
+ * Services available on every table of this module. Like table services,
74
+ * these replace built-in services with the same rpcName. Table services
75
+ * take precedence.
76
+ */
77
+ protected moduleServices?: RpcService[];
78
+ /**
79
+ * Per table edit hooks. These take precedence over the module-wide
80
+ * protected hook methods (isEditableColumn, validateCellEdit,
81
+ * getStaleColumns, canSaveSession, applySessionSave, canEditSource).
82
+ */
83
+ protected editHooks?: Partial<Record<T, EditHooks>>;
48
84
  protected abstract visualLinks?: Record<T, VuuLink[] | undefined>;
49
85
  protected get includeDefaultServices(): boolean;
86
+ /**
87
+ * Called on save, before changes are prepared. Rows given a message
88
+ * (vuuMsg) here are treated as handled and excluded from the save.
89
+ * @deprecated implement applySessionSave (per table via editHooks).
90
+ */
50
91
  protected beforeSessionSave(_sourceTable: Table, _sessionTable: Table): Promise<void>;
92
+ /** Called after a successful save, before the session is reset. */
51
93
  protected afterSessionSave(_sourceTable: Table, _sessionTable: Table): Promise<void>;
94
+ /** See {@link EditHooks.canEditSource}. Default true. */
95
+ protected canEditSource(_column: string, _ctx: EditContext): boolean;
96
+ /** See {@link EditHooks.isEditableColumn}. */
97
+ protected isEditableColumn(table: Table, column: string, _ctx: EditContext): boolean;
98
+ /** See {@link EditHooks.validateCellEdit}. */
99
+ protected validateCellEdit(_edit: CellEdit): string | undefined | void;
100
+ /** See {@link EditHooks.getStaleColumns}. */
101
+ protected getStaleColumns(sourceTable: Table, sourceRow: TableRow, change: SessionRowChange): string[];
102
+ /** See {@link EditHooks.canSaveSession}. Default true. */
103
+ protected canSaveSession(_prepared: PreparedSessionSave, _ctx: EditContext): CanSaveSessionResult | Promise<CanSaveSessionResult>;
104
+ /** See {@link EditHooks.applySessionSave}. */
105
+ protected applySessionSave(sessionTable: Table, prepared: PreparedSessionSave, _ctx: EditContext): SessionSaveResult | Promise<SessionSaveResult>;
52
106
  /**
53
107
  * Optional, per table. Restricts the rows visible to each viewport, based on
54
108
  * the viewport (and the user who owns it).
@@ -61,6 +115,14 @@ export declare abstract class VuuModule<T extends string = string> implements IV
61
115
  protected beforeCreateDataSource?(tableName: T): void;
62
116
  getTableSchema(tableName: string): Record<T, Readonly<TableSchema>>[T];
63
117
  getTableList(): string[];
118
+ /** The tables published by this module, keyed by table name. */
119
+ getTables(): Record<T, Table>;
120
+ getTable(tableName: T): Table;
121
+ /**
122
+ * Tables visible to permission functions: the engine's tables, or this
123
+ * module's own tables if it is not registered with an engine.
124
+ */
125
+ protected get tableContainer(): TableContainer;
64
126
  protected handlePostSubscribeActivities: ({ clientViewportId, tableSchema, }: DataSourceSubscribedMessage) => void;
65
127
  protected unregisterViewport: (viewportId: string) => void;
66
128
  get name(): string;
@@ -73,6 +135,11 @@ export declare abstract class VuuModule<T extends string = string> implements IV
73
135
  session?: SessionDataSourceOverrides;
74
136
  }) => DataSourceBase<DataSourceRowWithBigint>;
75
137
  private createPermissionPredicate;
138
+ /**
139
+ * The RPC services for a table: built-in services, overridden by
140
+ * moduleServices, overridden by table services (matched by rpcName).
141
+ * For a session table, the services of its source table.
142
+ */
76
143
  getServices(tableName: T): RpcService[];
77
144
  getMenuServices(tableName: T): RpcMenuService[];
78
145
  protected get sessionTableMap(): SessionTableMap;
@@ -0,0 +1,135 @@
1
+ import type { RpcResult, VuuColumnDataType, VuuRowDataItemType } from "@vuu-ui/vuu-protocol-types";
2
+ import type { Table, TableRow } from "../../Table";
3
+ import type { VuuModuleUser } from "../user/CurrentUser";
4
+ /**
5
+ * Engine-maintained columns. Not editable through editCell by default.
6
+ */
7
+ export declare const VUU_SYSTEM_COLUMNS: ReadonlySet<string>;
8
+ export declare const isSystemColumn: (column: string) => boolean;
9
+ /**
10
+ * endEditSession error when a source row changed after it was copied into
11
+ * the session. vuu-ui's VuuDataSource matches this string exactly (and
12
+ * throws StaleUpdateError), do not change it.
13
+ */
14
+ export declare const STALE_UPDATE = "stale update";
15
+ export declare const DUPLICATE_KEY = "duplicate key";
16
+ export declare const SAVE_NOT_PERMITTED = "endEditSession: save not permitted";
17
+ export declare const SAVE_FAILED = "endEditSession: save failed";
18
+ /**
19
+ * Passed to every edit hook.
20
+ */
21
+ export interface EditContext {
22
+ moduleName: string;
23
+ /** The source table name (never the session table name). */
24
+ tableName: string;
25
+ /** The viewport that made the request. */
26
+ viewportId: string;
27
+ /** The user that owns the viewport. */
28
+ user: VuuModuleUser;
29
+ /** The session table, when the request targets an edit session. */
30
+ sessionTable?: Table;
31
+ sessionTableName?: string;
32
+ }
33
+ export type SessionTableAction = "addRow" | "editCell" | "deleteRow";
34
+ export type SessionRowChange = {
35
+ action: SessionTableAction;
36
+ /**
37
+ * Changed source columns and their new values. For addRow, every source
38
+ * column. Empty for deleteRow.
39
+ */
40
+ cellUpdates: Record<string, VuuRowDataItemType>;
41
+ key: string;
42
+ /** The session row, projected onto the source table's columns. */
43
+ row: TableRow;
44
+ /** The session row, as stored in the session table. */
45
+ sessionRow: TableRow;
46
+ /**
47
+ * Copy of the source row taken when it was copied into the session (or
48
+ * when a change was undone). Used to detect concurrent source changes.
49
+ */
50
+ sourceSnapshot?: TableRow;
51
+ };
52
+ export type PreparedSessionChange = SessionRowChange & {
53
+ /** The current source row (absent for addRow). */
54
+ sourceRow?: TableRow;
55
+ };
56
+ export type PreparedSessionSave = {
57
+ sourceTable: Table;
58
+ sessionTable: Table;
59
+ changes: PreparedSessionChange[];
60
+ };
61
+ /** true permits the save, false or a string (the reason) refuses it. */
62
+ export type CanSaveSessionResult = boolean | string;
63
+ /**
64
+ * undefined, true or a SUCCESS_RESULT mean the save succeeded. false or an
65
+ * ERROR_RESULT (as does a thrown error) mean it failed; the session is left
66
+ * open so the user can retry or cancel.
67
+ */
68
+ export type SessionSaveResult = void | boolean | RpcResult;
69
+ /** A cell edit, passed to `validateCellEdit`. */
70
+ export type CellEdit = {
71
+ column: string;
72
+ ctx: EditContext;
73
+ dataType: VuuColumnDataType | undefined;
74
+ key: string;
75
+ /** the row after the edit */
76
+ newRow: TableRow;
77
+ /** the row before the edit */
78
+ row: TableRow;
79
+ /** the table being edited, the session table unless editing the source directly */
80
+ table: Table;
81
+ value: VuuRowDataItemType;
82
+ };
83
+ /**
84
+ * Edit and session-save hooks. Implement them as protected methods of a
85
+ * VuuModule (applying to every table), or per table via
86
+ * `VuuModule.editHooks`, which take precedence.
87
+ */
88
+ export interface EditHooks {
89
+ /**
90
+ * Opt in to editCell on a viewport that is NOT using a session table, i.e.
91
+ * edits written directly to the source table. Default true (unlike
92
+ * vuu-server) for backward compatibility.
93
+ */
94
+ canEditSource?: (column: string, ctx: EditContext) => boolean;
95
+ /**
96
+ * Whether a column may be edited through editCell. Default: any column
97
+ * except the key and the system columns.
98
+ */
99
+ isEditableColumn?: (table: Table, column: string, ctx: EditContext) => boolean;
100
+ /** Return an error message to refuse a cell edit. */
101
+ validateCellEdit?: (edit: CellEdit) => string | undefined | void;
102
+ /**
103
+ * Names of the source columns that changed since the row was copied into
104
+ * the session. A non-empty result makes the save fail with "stale update".
105
+ */
106
+ getStaleColumns?: (sourceTable: Table, sourceRow: TableRow, change: SessionRowChange) => string[];
107
+ /**
108
+ * Authorise a save, after changes are prepared (and found not stale),
109
+ * before applySessionSave. Return false or a reason to refuse.
110
+ */
111
+ canSaveSession?: (prepared: PreparedSessionSave, ctx: EditContext) => CanSaveSessionResult | Promise<CanSaveSessionResult>;
112
+ /**
113
+ * Apply prepared changes. Default {@link defaultApplySessionSave}, an
114
+ * atomic write to the source table. Override to route changes elsewhere
115
+ * (e.g. to an order service); call `defaultApplySessionSave` for any
116
+ * changes that should still be written to the source table.
117
+ */
118
+ applySessionSave?: (sessionTable: Table, prepared: PreparedSessionSave, ctx: EditContext) => SessionSaveResult | Promise<SessionSaveResult>;
119
+ }
120
+ export declare const defaultCanEditSource: () => boolean;
121
+ export declare const defaultIsEditableColumn: (table: Table, column: string) => boolean;
122
+ /**
123
+ * Compares the current source row with the snapshot taken when the row was
124
+ * copied into the session. For editCell, only the columns edited in the
125
+ * session are compared (other columns, e.g. ticking prices, are not written
126
+ * by the save so cannot conflict). For deleteRow, every column is compared.
127
+ * Without a snapshot (e.g. a row added to an empty session), nothing is
128
+ * reported as stale.
129
+ */
130
+ export declare const defaultGetStaleColumns: (sourceTable: Table, sourceRow: TableRow, { action, cellUpdates, sourceSnapshot }: SessionRowChange) => string[];
131
+ /**
132
+ * Writes prepared changes to the source table. Atomic: if any change
133
+ * throws, changes already applied are rolled back and the error rethrown.
134
+ */
135
+ export declare const defaultApplySessionSave: (_sessionTable: Table, { sourceTable, changes, }: Pick<PreparedSessionSave, "sourceTable" | "changes">) => SessionSaveResult;
@@ -3,19 +3,43 @@ import { Table } from "../../Table";
3
3
  import type { VuuRowDataItemType, VuuTable } from "@vuu-ui/vuu-protocol-types";
4
4
  import type { ColumnMap } from "@vuu-ui/vuu-utils";
5
5
  import type { UpdateGenerator } from "../../rowUpdates";
6
- declare class TableContainer {
6
+ /**
7
+ * Create a table. The table is not registered anywhere; it belongs to
8
+ * whichever VuuModule returns it from `tables`.
9
+ */
10
+ export declare const createTable: (schema: TableSchema, data?: Array<Array<VuuRowDataItemType | bigint>>, dataMap?: ColumnMap, updateGenerator?: UpdateGenerator) => Table;
11
+ /**
12
+ * Create a materialized (inner) join of two tables, backed by an engine
13
+ * JoinTable, maintained incrementally as either source changes.
14
+ */
15
+ export declare const createJoinTable: (joinTable: VuuTable, table1: Table, table2: Table, joinColumn: string) => Table;
16
+ /**
17
+ * Tables, keyed by `{module, table}`. Each LocalEngine owns one, populated
18
+ * from the tables of its registered modules. Tables created through
19
+ * `createTable` / `createJoinTable` are added to this container.
20
+ */
21
+ export declare class TableContainer {
7
22
  #private;
8
- private constructor();
9
- static get instance(): TableContainer;
10
23
  createTable: (schema: TableSchema, data?: Array<Array<VuuRowDataItemType | bigint>>, dataMap?: ColumnMap, updateGenerator?: UpdateGenerator) => Table;
11
24
  /**
12
- * Create a materialized (inner) join of two existing tables, backed by an
13
- * engine JoinTable, maintained incrementally as either source changes.
14
- * Both source tables must already have been created.
25
+ * Create a join of two tables already in this container. Pass full
26
+ * `{module, table}` references; a bare name is accepted only if unique.
15
27
  */
16
- createJoinTable(joinTable: VuuTable, { table: t1 }: VuuTable, { table: t2 }: VuuTable, joinColumn: string): Table;
17
- addTable(table: Table): void;
18
- getTable<T = Table>(tableName: string): NonNullable<T>;
28
+ createJoinTable(joinTable: VuuTable, table1: VuuTable | string, table2: VuuTable | string, joinColumn: string): Table;
29
+ /**
30
+ * Add a table, keyed by `as` if given, otherwise by its schema's
31
+ * `{module, table}`.
32
+ */
33
+ addTable(table: Table, as?: VuuTable): void;
34
+ removeTable(table: VuuTable): boolean;
35
+ hasTable(table: VuuTable): boolean;
36
+ /**
37
+ * Resolve a table by `{module, table}`. A bare table name is accepted for
38
+ * backward compatibility, but only resolves if exactly one module has a
39
+ * table with that name.
40
+ */
41
+ getTable(table: VuuTable | string): Table;
42
+ getTable<T>(table: VuuTable | string): T;
43
+ get tables(): Table[];
44
+ clear(): void;
19
45
  }
20
- declare const _default: TableContainer;
21
- export default _default;
@@ -0,0 +1,6 @@
1
+ import type { TableContainer } from "./TableContainer";
2
+ /**
3
+ * @deprecated The default engine's table container, resolved lazily on each
4
+ * access. Use `engine.tableContainer` of an engine you create.
5
+ */
6
+ export declare const tableContainer: TableContainer;
@@ -5,3 +5,10 @@ export declare const nextRandomDouble: PriceGenerator;
5
5
  export declare const initBidAsk: (priceMaxDelta: number, nextRandomDouble: PriceGenerator) => number[];
6
6
  export declare const generateNextBidAsk: (bid: number, ask: number, spreadMultipler: number, priceMaxDelta: number, nextRandomDouble: PriceGenerator) => number[];
7
7
  export declare const withNanoMs: (ts: number, nanos?: number) => bigint;
8
+ /**
9
+ * Copy rows, so tables created from shared reference data do not share
10
+ * (and mutate) the same row arrays.
11
+ */
12
+ export declare const cloneRows: <R extends unknown[]>(rows: readonly R[]) => R[];
13
+ /** Memoise a zero-arg factory. */
14
+ export declare const lazy: <V>(factory: () => V) => () => V;
@@ -0,0 +1,18 @@
1
+ import { BasketModule } from "./basket/BasketModule";
2
+ import { type LocalEngine } from "./core/engine/LocalEngine";
3
+ import { SimulModule } from "./simul/SimulModule";
4
+ import { TestModule } from "./test/TestModule";
5
+ export type DemoModules = {
6
+ simulModule: SimulModule;
7
+ basketModule: BasketModule;
8
+ testModule: TestModule;
9
+ };
10
+ /**
11
+ * Create new SIMUL, BASKET and TEST demo modules and register them with an
12
+ * engine (by default, the default engine used by LocalDataSourceProvider).
13
+ * BASKET shares SIMUL's prices table. Prices tick only once a dataSource is
14
+ * created; `engine.dispose()` stops all timers.
15
+ *
16
+ * Nothing is created or registered until this is called.
17
+ */
18
+ export declare const registerDemoModules: (engine?: LocalEngine) => DemoModules;
package/src/index.d.ts CHANGED
@@ -1,10 +1,15 @@
1
1
  export * from "./ArrayProxy";
2
2
  export * from "./core/filter/PermissionFilter";
3
3
  export * from "./core/module/VuuModule";
4
- export { ensureVuuModule } from "./core/module/ModuleContainer";
4
+ export * from "./core/module/edit-hooks";
5
+ export { type AnyVuuModule, createLocalEngine, ensureVuuModule, getDefaultEngine, LocalEngine, type LocalEngineOptions, resetDefaultEngine, } from "./core/engine/LocalEngine";
5
6
  export * from "./core/user/CurrentUser";
6
- export { default as tableContainer } from "./core/table/TableContainer";
7
+ export { createJoinTable, createTable, TableContainer, } from "./core/table/TableContainer";
8
+ export { tableContainer } from "./core/table/defaultTableContainer";
9
+ export { registerDemoModules, type DemoModules } from "./demo-modules";
10
+ export { BaseUpdateGenerator } from "./UpdateGenerator";
7
11
  export * from "./local-datasource-provider/LocalDatasourceProvider";
12
+ export { createDataSourceProvider } from "./local-datasource-provider/createDataSourceProvider";
8
13
  export * from "./makeSuggestions";
9
14
  export * from "./notifications";
10
15
  export * from "./schemas";
@@ -0,0 +1,11 @@
1
+ import { ModuleAdminModule } from "./module-admin/ModuleAdminModule";
2
+ import { UserAdminModule } from "./user-admin/UserAdminModule";
3
+ export * from "./index";
4
+ /** @deprecated */
5
+ export declare const simulModule: import("./simul").SimulModule, basketModule: import("./basket").BasketModule, testModule: import("./test").TestModule;
6
+ /** @deprecated */
7
+ export declare const moduleAdminModule: ModuleAdminModule;
8
+ /** @deprecated */
9
+ export declare const userAdminModule: UserAdminModule;
10
+ /** @deprecated */
11
+ export declare const notificationsModule: import("./notifications").NotificationModule;