Skip to content

Commit 82d825a

Browse files
committed
[middleware] WillSetTableCallback
1 parent 9f60040 commit 82d825a

8 files changed

Lines changed: 488 additions & 13 deletions

File tree

‎src/@types/middleware/docs.js‎

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,25 @@
3232
* @since v8.0.0
3333
*/
3434
/// WillSetCellCallback
35+
/**
36+
* The WillSetTableCallback type describes a function that is called before a
37+
* Table is set in the Store.
38+
*
39+
* The callback receives the table Id and the Table object that is about to be
40+
* set. It can return the Table (possibly transformed) to allow the write, or
41+
* `undefined` to prevent the Table from being set.
42+
*
43+
* Multiple WillSetTableCallback functions can be registered and they will be
44+
* called sequentially, the Table being updated successively. If any callback
45+
* returns `undefined`, the chain short-circuits and the Table will not be set.
46+
* @param tableId The Id of the Table being set.
47+
* @param table The Table object about to be set.
48+
* @returns The Table to use (possibly transformed), or `undefined` to prevent
49+
* the write.
50+
* @category Callback
51+
* @since v8.0.0
52+
*/
53+
/// WillSetTableCallback
3554
/**
3655
* The WillSetRowCallback type describes a function that is called before a Row
3756
* is set in the Store.
@@ -259,6 +278,72 @@
259278
* @since v8.0.0
260279
*/
261280
/// Middleware.addWillSetCellCallback
281+
/**
282+
* The addWillSetTableCallback method registers a WillSetTableCallback that
283+
* will be called before any Table is set in the Store.
284+
*
285+
* The callback can transform the Table or return `undefined` to prevent the
286+
* write. Multiple callbacks can be registered and they are called
287+
* sequentially, each receiving the (possibly transformed) table from the
288+
* previous callback.
289+
* @param callback The WillSetTableCallback to register.
290+
* @returns A reference to the Middleware object, for chaining.
291+
* @example
292+
* This example registers a callback that upper-cases string Cell values in
293+
* rows set to the 'pets' table.
294+
*
295+
* ```js
296+
* import {createMiddleware, createStore} from 'tinybase';
297+
*
298+
* const store = createStore();
299+
* const middleware = createMiddleware(store);
300+
*
301+
* middleware.addWillSetTableCallback((tableId, table) =>
302+
* tableId === 'pets'
303+
* ? Object.fromEntries(
304+
* Object.entries(table).map(([rowId, row]) => [
305+
* rowId,
306+
* Object.fromEntries(
307+
* Object.entries(row).map(([k, v]) => [
308+
* k,
309+
* typeof v === 'string' ? v.toUpperCase() : v,
310+
* ]),
311+
* ),
312+
* ]),
313+
* )
314+
* : table,
315+
* );
316+
*
317+
* store.setTable('pets', {fido: {species: 'dog'}, felix: {species: 'cat'}});
318+
* console.log(store.getTable('pets'));
319+
* // -> {fido: {species: 'DOG'}, felix: {species: 'CAT'}}
320+
*
321+
* middleware.destroy();
322+
* ```
323+
* @example
324+
* This example registers a callback that prevents writes to the 'species'
325+
* table.
326+
*
327+
* ```js
328+
* import {createMiddleware, createStore} from 'tinybase';
329+
*
330+
* const store = createStore();
331+
* const middleware = createMiddleware(store);
332+
*
333+
* middleware.addWillSetTableCallback((tableId, table) =>
334+
* tableId === 'species' ? undefined : table,
335+
* );
336+
*
337+
* store.setTable('species', {dog: {legs: 4, sound: 'woof'}});
338+
* console.log(store.getTables());
339+
* // -> {}
340+
*
341+
* middleware.destroy();
342+
* ```
343+
* @category Configuration
344+
* @since v8.0.0
345+
*/
346+
/// Middleware.addWillSetTableCallback
262347
/**
263348
* The addWillSetRowCallback method registers a WillSetRowCallback that will
264349
* be called before any Row is set in the Store.

‎src/@types/middleware/index.d.ts‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type {
55
CellOrUndefined,
66
Row,
77
Store,
8+
Table,
89
Value,
910
ValueOrUndefined,
1011
Values,
@@ -18,6 +19,12 @@ export type WillSetCellCallback = (
1819
cell: Cell,
1920
) => CellOrUndefined;
2021

22+
/// WillSetTableCallback
23+
export type WillSetTableCallback = (
24+
tableId: Id,
25+
table: Table,
26+
) => Table | undefined;
27+
2128
/// WillSetRowCallback
2229
export type WillSetRowCallback = (
2330
tableId: Id,
@@ -58,6 +65,9 @@ export interface Middleware {
5865
/// Middleware.addWillSetCellCallback
5966
addWillSetCellCallback(callback: WillSetCellCallback): Middleware;
6067

68+
/// Middleware.addWillSetTableCallback
69+
addWillSetTableCallback(callback: WillSetTableCallback): Middleware;
70+
6171
/// Middleware.addWillSetRowCallback
6272
addWillSetRowCallback(callback: WillSetRowCallback): Middleware;
6373

‎src/@types/middleware/with-schemas/index.d.ts‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import type {
1212
OptionalValuesSchema,
1313
Row,
1414
Store,
15+
Table,
1516
Value,
1617
Values,
1718
} from '../../store/with-schemas/index.d.ts';
@@ -37,6 +38,18 @@ export type WillSetCellCallback<
3738
...params: Params | [tableId: never, rowId: never, cellId: never, cell: never]
3839
) => Params[3] | undefined;
3940

41+
/// WillSetTableCallback
42+
export type WillSetTableCallback<
43+
Schema extends OptionalTablesSchema,
44+
Params extends any[] = TableIdFromSchema<Schema> extends infer TableId
45+
? TableId extends TableIdFromSchema<Schema>
46+
? [tableId: TableId, table: Table<Schema, TableId>]
47+
: never
48+
: never,
49+
> = (
50+
...params: Params | [tableId: never, table: never]
51+
) => Params[1] | undefined;
52+
4053
/// WillSetRowCallback
4154
export type WillSetRowCallback<
4255
Schema extends OptionalTablesSchema,
@@ -115,6 +128,11 @@ export interface Middleware<in out Schemas extends OptionalSchemas> {
115128
callback: WillSetCellCallback<Schemas[0]>,
116129
): Middleware<Schemas>;
117130

131+
/// Middleware.addWillSetTableCallback
132+
addWillSetTableCallback(
133+
callback: WillSetTableCallback<Schemas[0]>,
134+
): Middleware<Schemas>;
135+
118136
/// Middleware.addWillSetRowCallback
119137
addWillSetRowCallback(
120138
callback: WillSetRowCallback<Schemas[0]>,

‎src/middleware/index.ts‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type {
77
WillDelValuesCallback,
88
WillSetCellCallback,
99
WillSetRowCallback,
10+
WillSetTableCallback,
1011
WillSetValueCallback,
1112
WillSetValuesCallback,
1213
createMiddleware as createMiddlewareDecl,
@@ -16,6 +17,7 @@ import type {
1617
CellOrUndefined,
1718
Row,
1819
Store,
20+
Table,
1921
Value,
2022
ValueOrUndefined,
2123
Values,
@@ -28,6 +30,7 @@ import {isUndefined} from '../common/other.ts';
2830
export const createMiddleware = getCreateFunction(
2931
(store: Store): Middleware => {
3032
const willSetCellCallbacks: WillSetCellCallback[] = [];
33+
const willSetTableCallbacks: WillSetTableCallback[] = [];
3134
const willSetRowCallbacks: WillSetRowCallback[] = [];
3235
const willSetValueCallbacks: WillSetValueCallback[] = [];
3336
const willSetValuesCallbacks: WillSetValuesCallback[] = [];
@@ -61,6 +64,19 @@ export const createMiddleware = getCreateFunction(
6164
row as Row | undefined,
6265
);
6366

67+
const willSetTable = (
68+
tableId: Id,
69+
table: Table,
70+
): Table | undefined =>
71+
arrayReduce(
72+
willSetTableCallbacks,
73+
(current, callback) =>
74+
isUndefined(current)
75+
? current
76+
: callback(tableId, current as Table),
77+
table as Table | undefined,
78+
);
79+
6480
const willSetValue = (valueId: Id, value: Value): ValueOrUndefined =>
6581
arrayReduce(
6682
willSetValueCallbacks,
@@ -100,6 +116,7 @@ export const createMiddleware = getCreateFunction(
100116
willSetValues,
101117
willDelRow,
102118
willDelValues,
119+
willSetTable,
103120
);
104121

105122
const getStore = (): Store => store;
@@ -116,6 +133,11 @@ export const createMiddleware = getCreateFunction(
116133
const addWillSetRowCallback = (callback: WillSetRowCallback): Middleware =>
117134
fluent(() => arrayPush(willSetRowCallbacks, callback));
118135

136+
const addWillSetTableCallback = (
137+
callback: WillSetTableCallback,
138+
): Middleware =>
139+
fluent(() => arrayPush(willSetTableCallbacks, callback));
140+
119141
const addWillSetValueCallback = (
120142
callback: WillSetValueCallback,
121143
): Middleware => fluent(() => arrayPush(willSetValueCallbacks, callback));
@@ -145,6 +167,7 @@ export const createMiddleware = getCreateFunction(
145167
const middleware: Middleware = objFreeze({
146168
getStore,
147169
addWillSetCellCallback,
170+
addWillSetTableCallback,
148171
addWillSetRowCallback,
149172
addWillSetValueCallback,
150173
addWillSetValuesCallback,

‎src/store/index.ts‎

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ export const createStore: typeof createStoreDecl = (): Store => {
176176
willSetValues?: (values: Values) => Values | undefined,
177177
willDelRow?: (tableId: Id, rowId: Id) => boolean,
178178
willDelValues?: () => boolean,
179+
willSetTable?: (tableId: Id, table: Table) => Table | undefined,
179180
] = [];
180181
let internalListeners: [
181182
preStartTransaction?: () => void,
@@ -471,18 +472,34 @@ export const createStore: typeof createStoreDecl = (): Store => {
471472
(_tables, tableId) => delValidTable(tableId),
472473
);
473474

474-
const setValidTable = (tableId: Id, table: Table): TableMap =>
475-
mapMatch(
476-
mapEnsure(tablesMap, tableId, () => {
477-
tableIdsChanged(tableId, 1);
478-
mapSet(tablePoolFunctions, tableId, getPoolFunctions());
479-
mapSet(tableCellIds, tableId, mapNew());
480-
return mapNew();
481-
}),
482-
table,
483-
(tableMap, rowId, row) => setValidRow(tableId, tableMap, rowId, row),
484-
(tableMap, rowId) => delValidRow(tableId, tableMap, rowId),
485-
);
475+
const setValidTable = (
476+
tableId: Id,
477+
table: Table,
478+
forceDel?: boolean,
479+
): TableMap =>
480+
ifNotUndefined(
481+
forceDel
482+
? table
483+
: ifNotUndefined(
484+
internalWillSets[8],
485+
(willSetTable) => willSetTable(tableId, table),
486+
() => table,
487+
),
488+
(validTable) =>
489+
mapMatch(
490+
mapEnsure(tablesMap, tableId, () => {
491+
tableIdsChanged(tableId, 1);
492+
mapSet(tablePoolFunctions, tableId, getPoolFunctions());
493+
mapSet(tableCellIds, tableId, mapNew());
494+
return mapNew();
495+
}),
496+
validTable,
497+
(tableMap, rowId, row) =>
498+
setValidRow(tableId, tableMap, rowId, row),
499+
(tableMap, rowId) =>
500+
delValidRow(tableId, tableMap, rowId),
501+
),
502+
) as TableMap;
486503

487504
const setValidRow = (
488505
tableId: Id,
@@ -612,7 +629,8 @@ export const createStore: typeof createStoreDecl = (): Store => {
612629
const getOrCreateTable = (tableId: Id) =>
613630
mapGet(tablesMap, tableId) ?? setValidTable(tableId, {});
614631

615-
const delValidTable = (tableId: Id): TableMap => setValidTable(tableId, {});
632+
const delValidTable = (tableId: Id): TableMap =>
633+
setValidTable(tableId, {}, true);
616634

617635
const delValidRow = (tableId: Id, tableMap: TableMap, rowId: Id): void => {
618636
if (internalWillSets[6]?.(tableId, rowId) ?? true) {
@@ -1783,6 +1801,7 @@ export const createStore: typeof createStoreDecl = (): Store => {
17831801
willSetValues: (values: Values) => Values | undefined,
17841802
willDelRow: (tableId: Id, rowId: Id) => boolean,
17851803
willDelValues: () => boolean,
1804+
willSetTable: (tableId: Id, table: Table) => Table | undefined,
17861805
) =>
17871806
(internalWillSets = [
17881807
willSetCell,
@@ -1793,6 +1812,7 @@ export const createStore: typeof createStoreDecl = (): Store => {
17931812
willSetValues,
17941813
willDelRow,
17951814
willDelValues,
1815+
willSetTable,
17961816
]);
17971817

17981818
const setInternalListeners = (

0 commit comments

Comments
 (0)