@svgrid/enterprise 3.1.1 → 3.1.2
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/SvSheet.svelte +7 -1
- package/dist/cdn/svgrid-enterprise.svelte-external.js +10534 -10332
- package/dist/designer/assets/{index-DkhD2kIe.js → index-V_cMXdxt.js} +311 -311
- package/dist/designer/assets/validate-YxSrBF1H.js +1 -0
- package/dist/designer/index.html +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/node/studio.js +16 -4
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +1 -0
- package/dist/server/server-row-model.d.ts +9 -0
- package/dist/server/server-row-model.js +120 -3
- package/dist/server/worker-data-source.d.ts +62 -0
- package/dist/server/worker-data-source.js +133 -0
- package/dist/studio/emit-project.js +8 -3
- package/dist/sveltekit/in-memory.d.ts +21 -1
- package/dist/sveltekit/in-memory.js +153 -105
- package/dist/sveltekit/index.d.ts +1 -1
- package/dist/version.d.ts +14 -1
- package/dist/version.js +14 -1
- package/dist/wc/sv-sheet-element.js +14 -9
- package/dist/wc/{validate-BqDthkNr.js → validate-JAuMsk6Z.js} +36 -36
- package/dist/worker/columnar.d.ts +42 -0
- package/dist/worker/columnar.js +410 -0
- package/dist/worker/index.d.ts +12 -0
- package/dist/worker/index.js +12 -0
- package/dist/worker/infer-schema.d.ts +21 -0
- package/dist/worker/infer-schema.js +34 -0
- package/dist/worker/protocol.d.ts +73 -0
- package/dist/worker/protocol.js +14 -0
- package/dist/worker/serve.d.ts +44 -0
- package/dist/worker/serve.js +100 -0
- package/package.json +7 -3
- package/dist/designer/assets/validate-C3B8QKsM.js +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{n as e}from"./server-block-cache-CXJKkJpy.js";var t=10,n=1e3,r=`https://svgrid.com/docs/getting-started/3-data-and-columns/`;function i(e,t=[]){for(let n of e)n.columns?.length?i(n.columns,t):t.push(n);return t}function a(a){let s=[],c=i(a.columns??[]),l=[],u=a.data??[],d=Math.min(u.length,n);for(let n=0;n<d&&l.length<t;n+=1){let t=u[n];e(t)||l.push(t)}if(l.length&&!a.serverGroup){let e=new Set;for(let t of l)if(t&&typeof t==`object`)for(let n of Object.keys(t))e.add(n);for(let t of c){let n=t.field;if(!n||t.fieldFn||e.has(n))continue;let i=o(n,[...e]);s.push(`[svgrid] Column field "${n}" does not exist on your row data, so that column renders empty.${i?` Did you mean "${i}"?`:``} Available keys: ${[...e].slice(0,12).join(`, `)}. See ${r}`)}}let f=new Map;for(let e of c){let t=e.id??e.field;t&&f.set(t,(f.get(t)??0)+1)}for(let[e,t]of f)t>1&&s.push(`[svgrid] ${t} columns share the id "${e}". Column state (sorting, filtering, pinning, width) is keyed by id, so they will act as one. Give each an explicit \`id\`. See ${r}`);let p=a.pageable===void 0&&a.showPagination===void 0;a.pageSize!==void 0&&p&&s.push("[svgrid] `pageSize` is set but pagination was never turned on, so it has no effect and every row renders. Add `pageable` to switch the pager on."),!(a.sortable===!0||a.features?.rowSortingFeature)&&c.some(e=>e.sortable===!0)&&s.push("[svgrid] A column sets `sortable: true`, but sorting is not enabled on the grid, so its header does nothing. Add `sortable` to <SvGrid> (it registers the sorting feature for you).");let m=new Set;for(let e of c){let t=e.id??e.field;t&&m.add(t)}for(let e of a.groupBy??[]){if(m.has(e))continue;let t=o(e,[...m]);s.push(`[svgrid] \`groupBy\` refers to column "${e}", which does not exist, so that grouping level is ignored.${t?` Did you mean "${t}"?`:``} Column ids: ${[...m].slice(0,12).join(`, `)}`)}let h=a.treeData?.column;if(h&&!m.has(h)&&s.push(`[svgrid] \`treeData.column\` refers to column "${h}", which does not exist, so the expander falls back to the first visible column.`),l.length&&a.treeData){let e=new Set;for(let t of l)if(t&&typeof t==`object`)for(let n of Object.keys(t))e.add(n);let t=a.treeData.parentField;if(t&&!e.has(t)){let n=o(t,[...e]);s.push(`[svgrid] \`treeData.parentField\` is "${t}", which is not on your row data, so every row becomes a root and no hierarchy appears.${n?` Did you mean "${n}"?`:``}`)}let n=a.treeData.idField;n&&!e.has(n)&&s.push(`[svgrid] \`treeData.idField\` is "${n}", which is not on your row data, so parent lookups cannot match and the tree stays flat.`)}return a.gantt&&a.treeData&&s.push("[svgrid] `gantt` and `treeData` are both set. The Gantt builds its own work-breakdown tree from `gantt.parentField` and needs every descendant row, but `treeData` hides collapsed children from the view, so summary bars will under-report. Remove `treeData` and set `gantt.parentField` instead."),(a.initialColumnPinning?.left?.length??0)+(a.initialColumnPinning?.right?.length??0)>0&&a.columnVirtualization!==!1&&s.push("[svgrid] `initialColumnPinning` is set while column virtualization is on (its default), so the pinned columns will not stick - the virtualizer recycles column nodes. Add `columnVirtualization={false}`."),a.externalPagination===!0&&a.rowCount===void 0&&s.push("[svgrid] `externalPagination` is on but `rowCount` is not set, so the pager cannot know how many pages exist. Pass the server total as `rowCount`."),a.externalSort===!0&&!a.onSortingChange&&s.push("[svgrid] `externalSort` is on but there is no `onSortingChange` handler, so clicking a header changes nothing - the grid stopped sorting and nobody is listening. Add `onSortingChange` and re-fetch in your handler."),a.externalFilter===!0&&!a.onFiltersChange&&s.push("[svgrid] `externalFilter` is on but there is no `onFiltersChange` handler, so filtering the grid changes nothing. Add `onFiltersChange` and re-fetch in your handler."),s}function o(e,t){let n,r=1/0;for(let i of t){let t=s(e.toLowerCase(),i.toLowerCase());t<r&&(r=t,n=i)}let i=Math.min(3,Math.floor(e.length/3)+1);return r<=i?n:void 0}function s(e,t){if(e===t)return 0;let n=Array(t.length+1);for(let e=0;e<=t.length;e++)n[e]=e;for(let r=1;r<=e.length;r++){let i=n[0];n[0]=r;for(let a=1;a<=t.length;a++){let o=Math.min(n[a]+1,n[a-1]+1,i+(e[r-1]===t[a-1]?0:1));i=n[a],n[a]=o}}return n[t.length]}export{a as validateGridConfig};
|
package/dist/designer/index.html
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>SvGrid Studio - Designer</title>
|
|
7
|
-
<script type="module" crossorigin src="./assets/index-
|
|
7
|
+
<script type="module" crossorigin src="./assets/index-V_cMXdxt.js"></script>
|
|
8
8
|
<link rel="modulepreload" crossorigin href="./assets/rolldown-runtime-Dd_uD5pT.js">
|
|
9
9
|
<link rel="modulepreload" crossorigin href="./assets/disclose-version-CIm4S9xS.js">
|
|
10
10
|
<link rel="modulepreload" crossorigin href="./assets/cell-formatting-BWrW_O0n.js">
|
package/dist/index.d.ts
CHANGED
|
@@ -122,8 +122,8 @@ export { dismissUnlicensedNudge } from './watermark.js';
|
|
|
122
122
|
export { showUpgradePrompt, dismissUpgradePrompt, type EnterpriseFeatureLabel, } from './upgrade-prompt.js';
|
|
123
123
|
export { createPivotModel, pivotAggregators, filterCollapsedPivotRows, type PivotAggregator, type PivotAggregatorId, type PivotConfig, type PivotResult, type PivotRow, type PivotRowKind, type PivotValueConfig, } from './pivot.js';
|
|
124
124
|
export { titleCase, resolveIdField, schemaToColumns, schemaToFormFields, isFieldHidden, validateField, applyComputed, validateEntity, type EntitySchema, type EntityField, type EntityFieldType, type EntityHooks, type FormFieldDescriptor, type FormLayout, type FormSection, type StudioEditorType, type StandardSchemaV1, type StandardSchemaResult, type ValidationOp, type ValidationRuleSpec, } from './schema.js';
|
|
125
|
-
export { planQuery, coerce, createInMemoryDataSource, createKitDataSource, createKitHandlers, createSqlDataSource, planToSql, BUILTIN_AGGREGATORS, type QueryPlan, type PlanPredicate, type PlanOp, type PlanAggregate, type PlanOptions, type KitDataSourceOptions, type KitHandlerOptions, type KitScope, type KitHandlers, type KitMessage, type SqlDialect, type SqlPlan, type SqlExecutor, type SqlDataSourceConfig, type WritableDataSource, } from './sveltekit/index.js';
|
|
126
|
-
export { createServerRowModel, serverRowModelRows, serverRowModelNav, serverGroupText, defaultServerGroupMessages, resolveServerGroupMessages, type ServerGroupMessages, GRAND_TOTAL_ROW_ID, GROUP_TOTAL_ROW_ID_PREFIX, type ServerRowModel, type ServerRowModelOptions, type ServerRowModelState, type ServerRowModelGridRow, type ServerRowModelDisplayRow, type ServerLevelParams, type ServerLevelState, type RefreshOptions, type ServerTransaction, type ServerTransactionResult, type ServerTransactionStatus, type ServerRowModelPaginationOptions, type ServerRowModelPagination, createServerSelectionModel, buildPivotResultColumns, type PivotResultColumnOptions, adaptCallbackDatasource, toCallbackDatasource, fromCallbackRequest, toCallbackRequest, fromCallbackFilterModel, toCallbackFilterModel, fromCallbackSelectionState, toCallbackSelectionState, isCallbackSelectionState, type CallbackSelectionState, type CallbackGroupSelectionState, type SelectionStateMapping, type CallbackServerRequest, type CallbackColumnFilter, type CallbackGetRowsParams, type CallbackDatasource, type ServerSelectionModel, type ServerSelectionModelOptions, type ServerSelectionState, type ServerGroupSelectionNode, type ServerSelectionGroupMode, createServerGroupModel, serverGroupRows, serverGroupNav, enableServerRowModel, SvGroupCell, SvRowGroupPanel, type ServerGroupController, type ServerGroupControllerOptions, type ServerGroupState, type ServerGroupGridRow, } from './server/index.js';
|
|
125
|
+
export { planQuery, coerce, createInMemoryDataSource, createKitDataSource, createKitHandlers, createSqlDataSource, planToSql, BUILTIN_AGGREGATORS, type QueryPlan, type PlanPredicate, type PlanOp, type PlanAggregate, type PlanOptions, type KitDataSourceOptions, type KitHandlerOptions, type KitScope, type KitHandlers, type KitMessage, type SqlDialect, type SqlPlan, type SqlExecutor, type SqlDataSourceConfig, type WritableDataSource, type InMemoryDataSourceOptions, } from './sveltekit/index.js';
|
|
126
|
+
export { createServerRowModel, serverRowModelRows, serverRowModelNav, serverGroupText, defaultServerGroupMessages, resolveServerGroupMessages, type ServerGroupMessages, GRAND_TOTAL_ROW_ID, GROUP_TOTAL_ROW_ID_PREFIX, type ServerRowModel, type ServerRowModelOptions, type ServerRowModelState, type ServerRowModelGridRow, type ServerRowModelDisplayRow, type ServerLevelParams, type ServerLevelState, type RefreshOptions, type ServerTransaction, type ServerTransactionResult, type ServerTransactionStatus, type ServerRowModelPaginationOptions, type ServerRowModelPagination, createServerSelectionModel, buildPivotResultColumns, type PivotResultColumnOptions, adaptCallbackDatasource, toCallbackDatasource, fromCallbackRequest, toCallbackRequest, fromCallbackFilterModel, toCallbackFilterModel, fromCallbackSelectionState, toCallbackSelectionState, isCallbackSelectionState, type CallbackSelectionState, type CallbackGroupSelectionState, type SelectionStateMapping, type CallbackServerRequest, type CallbackColumnFilter, type CallbackGetRowsParams, type CallbackDatasource, type ServerSelectionModel, type ServerSelectionModelOptions, type ServerSelectionState, type ServerGroupSelectionNode, type ServerSelectionGroupMode, createServerGroupModel, serverGroupRows, serverGroupNav, createWorkerDataSource, type WorkerDataSource, type WorkerDataSourceOptions, type WorkerLike, enableServerRowModel, SvGroupCell, SvRowGroupPanel, type ServerGroupController, type ServerGroupControllerOptions, type ServerGroupState, type ServerGroupGridRow, } from './server/index.js';
|
|
127
127
|
export { createSupabaseDataSource, createRestDataSource, offsetLimitAdapter, dummyJsonAdapter, jsonServerAdapter, introspectSupabaseTable, listSupabaseTables, createSupabaseRealtime, createRelationLookup, createSupabaseAuth, aggregateRows, chartFieldsFromSchema, normalizeFilters, buildEntitySchema, pickLabelField, linkRelationLabels, type SupabaseDataSourceConfig, type SupabaseClientLike, type RestDataSourceConfig, type RestAdapter, type OffsetLimitOptions, type IntrospectSupabaseOptions, type IntrospectedColumn, type ColumnReference, type NormalizedFilter, type NormalizedPredicate, type SupabaseRealtimeConfig, type SupabaseRealtimeClientLike, type SupabaseRealtimeHandle, type RealtimeChannelLike, type RealtimeChange, type RelationLookup, type RelationLookupConfig, type RelationOption, type SupabaseAuthConfig, type SupabaseAuthController, type SupabaseAuthClientLike, type AuthState, type AuthUser, withEntityRules, withRelationLabels, reduceValue, kpiSeries, sparklinePoints, seriesDelta, formatKpiValue, dashboardFromSchema, type AggregateReduce, type AggregateRequest, type AggregateBucket, type AggregateSource, type DashboardSpec, type DashboardWidget, type KpiWidget, type ChartWidget, type RelationLabelConfig, } from './sources/index.js';
|
|
128
128
|
export { buildInitialValues, controlKind, editMode, evalValidationRules, fieldState, sectionVisible, hasFieldConditions, rowId, stripHiddenValues, toSubmitValues, validateAll, visibleFormFields, type EditMode, type ControlKind, type FieldState, } from './edit-panel.js';
|
|
129
129
|
export { default as SvGridEditPanel } from './SvGridEditPanel.svelte';
|
package/dist/index.js
CHANGED
|
@@ -151,7 +151,7 @@ export { titleCase, resolveIdField, schemaToColumns, schemaToFormFields, isField
|
|
|
151
151
|
export { planQuery, coerce, createInMemoryDataSource, createKitDataSource, createKitHandlers, createSqlDataSource, planToSql, BUILTIN_AGGREGATORS, } from './sveltekit/index.js';
|
|
152
152
|
// Server-Side Row Model: lazy server-side grouping and tree data over the
|
|
153
153
|
// free grid's datasource contract. Also available as '@svgrid/enterprise/server'.
|
|
154
|
-
export { createServerRowModel, serverRowModelRows, serverRowModelNav, serverGroupText, defaultServerGroupMessages, resolveServerGroupMessages, GRAND_TOTAL_ROW_ID, GROUP_TOTAL_ROW_ID_PREFIX, createServerSelectionModel, buildPivotResultColumns, adaptCallbackDatasource, toCallbackDatasource, fromCallbackRequest, toCallbackRequest, fromCallbackFilterModel, toCallbackFilterModel, fromCallbackSelectionState, toCallbackSelectionState, isCallbackSelectionState, createServerGroupModel, serverGroupRows, serverGroupNav, enableServerRowModel, SvGroupCell, SvRowGroupPanel, } from './server/index.js';
|
|
154
|
+
export { createServerRowModel, serverRowModelRows, serverRowModelNav, serverGroupText, defaultServerGroupMessages, resolveServerGroupMessages, GRAND_TOTAL_ROW_ID, GROUP_TOTAL_ROW_ID_PREFIX, createServerSelectionModel, buildPivotResultColumns, adaptCallbackDatasource, toCallbackDatasource, fromCallbackRequest, toCallbackRequest, fromCallbackFilterModel, toCallbackFilterModel, fromCallbackSelectionState, toCallbackSelectionState, isCallbackSelectionState, createServerGroupModel, serverGroupRows, serverGroupNav, createWorkerDataSource, enableServerRowModel, SvGroupCell, SvRowGroupPanel, } from './server/index.js';
|
|
155
155
|
export { createSupabaseDataSource, createRestDataSource, offsetLimitAdapter, dummyJsonAdapter, jsonServerAdapter, introspectSupabaseTable, listSupabaseTables, createSupabaseRealtime, createRelationLookup, createSupabaseAuth, aggregateRows, chartFieldsFromSchema, normalizeFilters, buildEntitySchema, pickLabelField, linkRelationLabels, withEntityRules, withRelationLabels, reduceValue, kpiSeries, sparklinePoints, seriesDelta, formatKpiValue, dashboardFromSchema, } from './sources/index.js';
|
|
156
156
|
export { buildInitialValues, controlKind, editMode, evalValidationRules, fieldState, sectionVisible, hasFieldConditions, rowId, stripHiddenValues, toSubmitValues, validateAll, visibleFormFields, } from './edit-panel.js';
|
|
157
157
|
export { default as SvGridEditPanel } from './SvGridEditPanel.svelte';
|
package/dist/node/studio.js
CHANGED
|
@@ -16045,7 +16045,20 @@ function hasFieldConditions(schema) {
|
|
|
16045
16045
|
*
|
|
16046
16046
|
* `version.test.ts` asserts this matches `package.json`, so the two cannot drift.
|
|
16047
16047
|
*/
|
|
16048
|
-
var SVGRID_VERSION = "3.1.
|
|
16048
|
+
var SVGRID_VERSION = "3.1.2";
|
|
16049
|
+
/**
|
|
16050
|
+
* The version of `@svgrid/grid`, which releases on its own line: enterprise is
|
|
16051
|
+
* on 3.1.x while the grid is on 3.0.x.
|
|
16052
|
+
*
|
|
16053
|
+
* A scaffolded app depends on BOTH packages, and pinning the grid to this
|
|
16054
|
+
* package's version asked npm for `@svgrid/grid@^3.1.1`, which has never been
|
|
16055
|
+
* published. Every `svgrid-studio init` therefore produced an app whose very
|
|
16056
|
+
* next step, `npm install`, failed with ERESOLVE. Found 2026-10-05 by
|
|
16057
|
+
* scaffolding one and running the install.
|
|
16058
|
+
*
|
|
16059
|
+
* `version.test.ts` asserts this matches packages/grid/package.json.
|
|
16060
|
+
*/
|
|
16061
|
+
var GRID_VERSION = "3.0.8";
|
|
16049
16062
|
//#endregion
|
|
16050
16063
|
//#region src/studio/emit-project.ts
|
|
16051
16064
|
var has = (blocks, kind) => blocks.some((b) => b.config.kind === kind);
|
|
@@ -20902,10 +20915,9 @@ function svelteConfig(plan) {
|
|
|
20902
20915
|
/** The runtime npm deps the generated source imports, keyed by package -> range.
|
|
20903
20916
|
* One source of truth for package.json AND the fragment-mode dependency report. */
|
|
20904
20917
|
function runtimeDeps(project, allSource) {
|
|
20905
|
-
const svgrid = `^${SVGRID_VERSION}`;
|
|
20906
20918
|
const dependencies = {
|
|
20907
|
-
"@svgrid/grid":
|
|
20908
|
-
"@svgrid/enterprise":
|
|
20919
|
+
"@svgrid/grid": `^${GRID_VERSION}`,
|
|
20920
|
+
"@svgrid/enterprise": `^${SVGRID_VERSION}`
|
|
20909
20921
|
};
|
|
20910
20922
|
if (allSource.includes("from '@supabase/supabase-js'")) dependencies["@supabase/supabase-js"] = "^2.45.0";
|
|
20911
20923
|
if (allSource.includes("import pg from 'pg'")) dependencies["pg"] = "^8.11.0";
|
package/dist/server/index.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export { adaptCallbackDatasource, toCallbackDatasource, fromCallbackRequest, toC
|
|
|
16
16
|
export { createServerGroupModel, serverGroupRows, serverGroupNav, type ServerGroupController, type ServerGroupControllerOptions, type ServerGroupState, type ServerGroupGridRow, } from './server-group-model.js';
|
|
17
17
|
export { createServerSelectionModel, type ServerSelectionModel, type ServerSelectionModelOptions, type ServerSelectionState, type ServerGroupSelectionNode, type ServerSelectionGroupMode, } from './server-selection.js';
|
|
18
18
|
export { buildPivotResultColumns, type PivotResultColumnOptions, } from './server-pivot.js';
|
|
19
|
+
export { createWorkerDataSource, type WorkerDataSource, type WorkerDataSourceOptions, type WorkerLike, } from './worker-data-source.js';
|
|
19
20
|
export { enableServerRowModel } from './enable.js';
|
|
20
21
|
export { defaultServerGroupMessages, resolveServerGroupMessages, type ServerGroupMessages, } from './messages.js';
|
|
21
22
|
export { default as SvGroupCell } from './SvGroupCell.svelte';
|
package/dist/server/index.js
CHANGED
|
@@ -18,6 +18,7 @@ export { adaptCallbackDatasource, toCallbackDatasource, fromCallbackRequest, toC
|
|
|
18
18
|
export { createServerGroupModel, serverGroupRows, serverGroupNav, } from './server-group-model.js';
|
|
19
19
|
export { createServerSelectionModel, } from './server-selection.js';
|
|
20
20
|
export { buildPivotResultColumns, } from './server-pivot.js';
|
|
21
|
+
export { createWorkerDataSource, } from './worker-data-source.js';
|
|
21
22
|
export { enableServerRowModel } from './enable.js';
|
|
22
23
|
export { defaultServerGroupMessages, resolveServerGroupMessages, } from './messages.js';
|
|
23
24
|
export { default as SvGroupCell } from './SvGroupCell.svelte';
|
|
@@ -169,6 +169,15 @@ export type ServerRowModelOptions<TData> = {
|
|
|
169
169
|
* grid's pinned rows so it stays in view.
|
|
170
170
|
*/
|
|
171
171
|
grandTotalRow?: 'top' | 'bottom' | 'pinnedTop' | 'pinnedBottom';
|
|
172
|
+
/**
|
|
173
|
+
* Milliseconds to keep the rows on screen after a sort, a filter or a
|
|
174
|
+
* regroup while the new answer loads. If its first block lands in that
|
|
175
|
+
* time, the grid goes straight from the old rows to the new ones: no
|
|
176
|
+
* frame of skeletons between them, and one update instead of two. A fast
|
|
177
|
+
* source (a worker, a local database) never shows a skeleton; a slow one
|
|
178
|
+
* shows them once the time is up. Default 0: skeletons at once.
|
|
179
|
+
*/
|
|
180
|
+
keepRowsWhileLoading?: number;
|
|
172
181
|
/** Drop a group's cached children when it is collapsed. Default false. */
|
|
173
182
|
purgeClosedGroups?: boolean;
|
|
174
183
|
/**
|
|
@@ -368,10 +368,53 @@ export function createServerRowModel(source, options) {
|
|
|
368
368
|
}
|
|
369
369
|
}
|
|
370
370
|
}
|
|
371
|
-
|
|
371
|
+
const leafRuns = new WeakMap();
|
|
372
|
+
function leafRun(store, rows) {
|
|
373
|
+
if (openDetails.size)
|
|
374
|
+
return null;
|
|
375
|
+
const prev = leafRuns.get(store);
|
|
376
|
+
if (prev && prev.rows === rows)
|
|
377
|
+
return prev;
|
|
378
|
+
const n = rows.length;
|
|
379
|
+
// Same length (a block landed, a row was patched): update the kept
|
|
380
|
+
// arrays in place. The flatten only ever copies out of them.
|
|
381
|
+
const inPlace = !!prev && prev.rows.length === n;
|
|
382
|
+
const display = inPlace ? prev.display : new Array(n);
|
|
383
|
+
const grid = inPlace ? prev.grid : new Array(n);
|
|
384
|
+
const old = prev?.rows;
|
|
385
|
+
const oldLength = old ? old.length : 0;
|
|
386
|
+
for (let i = 0; i < n; i += 1) {
|
|
387
|
+
const child = rows[i];
|
|
388
|
+
if (i < oldLength && old[i] === child) {
|
|
389
|
+
if (!inPlace) {
|
|
390
|
+
display[i] = prev.display[i];
|
|
391
|
+
grid[i] = prev.grid[i];
|
|
392
|
+
}
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
const ph = rowPlaceholderState(child);
|
|
396
|
+
if (ph) {
|
|
397
|
+
const d = ph === 'failed' ? FAILED_ROW : LOADING_ROW;
|
|
398
|
+
display[i] = d;
|
|
399
|
+
grid[i] = toGridRow(d);
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
402
|
+
if (child.kind !== 'leaf') {
|
|
403
|
+
// Written in place up to here, so the kept run is no longer whole.
|
|
404
|
+
leafRuns.delete(store);
|
|
405
|
+
return null;
|
|
406
|
+
}
|
|
407
|
+
const d = { kind: 'leaf', id: child.id, level: store.level, route: store.route, data: child.data };
|
|
408
|
+
display[i] = d;
|
|
409
|
+
grid[i] = toGridRow(d, child.data);
|
|
410
|
+
}
|
|
411
|
+
const run = { rows, display, grid };
|
|
412
|
+
leafRuns.set(store, run);
|
|
413
|
+
return run;
|
|
414
|
+
}
|
|
372
415
|
function flatten() {
|
|
373
|
-
|
|
374
|
-
|
|
416
|
+
let display = [];
|
|
417
|
+
let grid = [];
|
|
375
418
|
segments = [];
|
|
376
419
|
const pushRow = (d, data) => {
|
|
377
420
|
display.push(d);
|
|
@@ -392,6 +435,28 @@ export function createServerRowModel(source, options) {
|
|
|
392
435
|
};
|
|
393
436
|
const walk = (store, from = 0, to = Number.POSITIVE_INFINITY) => {
|
|
394
437
|
const rows = store.cache.rows();
|
|
438
|
+
const run = store.loadMore ? null : leafRun(store, rows);
|
|
439
|
+
if (run) {
|
|
440
|
+
// A level of leaves maps one to one: copy the range out of the run.
|
|
441
|
+
const end = Math.min(rows.length, to);
|
|
442
|
+
if (end > from) {
|
|
443
|
+
segments.push({ store, displayStart: display.length, storeStart: from, length: end - from });
|
|
444
|
+
const { display: d, grid: g } = run;
|
|
445
|
+
if (display.length === 0) {
|
|
446
|
+
// The common flat case: the whole list is this run. A slice is a
|
|
447
|
+
// block copy; a push per row was the rest of the flatten's cost.
|
|
448
|
+
display = d.slice(from, end);
|
|
449
|
+
grid = g.slice(from, end);
|
|
450
|
+
}
|
|
451
|
+
else {
|
|
452
|
+
for (let i = from; i < end; i += 1) {
|
|
453
|
+
display.push(d[i]);
|
|
454
|
+
grid.push(g[i]);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
395
460
|
const end = Math.min(rows.length, to, store.loadMore ? loadedPrefix(rows) : Number.POSITIVE_INFINITY);
|
|
396
461
|
let segStart = from;
|
|
397
462
|
let dispStart = display.length;
|
|
@@ -606,6 +671,34 @@ export function createServerRowModel(source, options) {
|
|
|
606
671
|
pagination: pagination(),
|
|
607
672
|
};
|
|
608
673
|
}
|
|
674
|
+
// `keepRowsWhileLoading`: after a sort, a filter or a regroup, the rows on
|
|
675
|
+
// screen stay until the new answer's first block lands or the time runs
|
|
676
|
+
// out. `holdUntil` is that deadline, 0 when nothing is held.
|
|
677
|
+
let holdUntil = 0;
|
|
678
|
+
let holdTimer = null;
|
|
679
|
+
let held = false;
|
|
680
|
+
function holdRows() {
|
|
681
|
+
const ms = options.keepRowsWhileLoading ?? 0;
|
|
682
|
+
if (ms > 0 && current.displayRows.length > 0)
|
|
683
|
+
holdUntil = Date.now() + ms;
|
|
684
|
+
}
|
|
685
|
+
/** Whether the rows on screen should stay a little longer. */
|
|
686
|
+
function holding() {
|
|
687
|
+
if (!holdUntil)
|
|
688
|
+
return false;
|
|
689
|
+
const root = stores.get(routeKey([]));
|
|
690
|
+
// A failed block ends the hold too: its Retry is the answer to show.
|
|
691
|
+
const rootAnswered = !!root && root.cache.getCacheState().some((b) => b.status !== 'loading');
|
|
692
|
+
let pending = false;
|
|
693
|
+
for (const s of stores.values())
|
|
694
|
+
if (s.refreshPending)
|
|
695
|
+
pending = true;
|
|
696
|
+
if (Date.now() >= holdUntil || rootError || (rootAnswered && !pending)) {
|
|
697
|
+
holdUntil = 0;
|
|
698
|
+
return false;
|
|
699
|
+
}
|
|
700
|
+
return true;
|
|
701
|
+
}
|
|
609
702
|
function scheduleEmit() {
|
|
610
703
|
if (disposed || emitScheduled)
|
|
611
704
|
return;
|
|
@@ -614,7 +707,25 @@ export function createServerRowModel(source, options) {
|
|
|
614
707
|
emitScheduled = false;
|
|
615
708
|
if (disposed)
|
|
616
709
|
return;
|
|
710
|
+
if (holding()) {
|
|
711
|
+
held = true;
|
|
712
|
+
holdTimer ?? (holdTimer = setTimeout(() => {
|
|
713
|
+
holdTimer = null;
|
|
714
|
+
scheduleEmit();
|
|
715
|
+
}, Math.max(0, holdUntil - Date.now())));
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
if (holdTimer) {
|
|
719
|
+
clearTimeout(holdTimer);
|
|
720
|
+
holdTimer = null;
|
|
721
|
+
}
|
|
617
722
|
current = buildState();
|
|
723
|
+
// While rows were held the grid kept reporting positions in the old
|
|
724
|
+
// list, mapped through the old segments; map them through the new.
|
|
725
|
+
if (held) {
|
|
726
|
+
held = false;
|
|
727
|
+
applyViewport();
|
|
728
|
+
}
|
|
618
729
|
options.onChange?.(current);
|
|
619
730
|
for (const notify of subscribers)
|
|
620
731
|
notify();
|
|
@@ -749,6 +860,7 @@ export function createServerRowModel(source, options) {
|
|
|
749
860
|
}
|
|
750
861
|
function setSort(next) {
|
|
751
862
|
sortModel = next;
|
|
863
|
+
holdRows();
|
|
752
864
|
for (const store of storesAffectedBySort(next)) {
|
|
753
865
|
if (options.clientSideSort && sortLocally(store))
|
|
754
866
|
continue;
|
|
@@ -759,6 +871,7 @@ export function createServerRowModel(source, options) {
|
|
|
759
871
|
}
|
|
760
872
|
function setFilter(next) {
|
|
761
873
|
const previous = filterModel;
|
|
874
|
+
holdRows();
|
|
762
875
|
filterModel = Array.isArray(next.columns)
|
|
763
876
|
? { global: next.global, columns: toServerFilterColumns(next) }
|
|
764
877
|
: next;
|
|
@@ -1212,6 +1325,7 @@ export function createServerRowModel(source, options) {
|
|
|
1212
1325
|
expanded.clear();
|
|
1213
1326
|
seenGroups.clear();
|
|
1214
1327
|
}
|
|
1328
|
+
holdRows();
|
|
1215
1329
|
rebuild();
|
|
1216
1330
|
},
|
|
1217
1331
|
toggleGroup(row) {
|
|
@@ -1439,6 +1553,9 @@ export function createServerRowModel(source, options) {
|
|
|
1439
1553
|
if (asyncTimer)
|
|
1440
1554
|
clearTimeout(asyncTimer);
|
|
1441
1555
|
asyncTimer = null;
|
|
1556
|
+
if (holdTimer)
|
|
1557
|
+
clearTimeout(holdTimer);
|
|
1558
|
+
holdTimer = null;
|
|
1442
1559
|
asyncQueue.length = 0;
|
|
1443
1560
|
for (const s of stores.values())
|
|
1444
1561
|
s.cache.dispose();
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A `ServerDataSource` whose rows live in a Web Worker.
|
|
3
|
+
*
|
|
4
|
+
* Sorting, filtering, grouping and aggregating a few hundred thousand rows
|
|
5
|
+
* takes long enough on the main thread that scrolling and typing stop while
|
|
6
|
+
* it runs. This moves that work into a worker and lets the grid treat the
|
|
7
|
+
* worker as a server: `createServerRowModel` (or the free
|
|
8
|
+
* `createServerDataSource`) asks for one block of rows at a time, the worker
|
|
9
|
+
* answers with just that block, and the full array never crosses back.
|
|
10
|
+
*
|
|
11
|
+
* Inside the worker the requests run through `createInMemoryDataSource`, the
|
|
12
|
+
* reference implementation of the datasource contract, so the answers are the
|
|
13
|
+
* ones every other backend is tested against.
|
|
14
|
+
*
|
|
15
|
+
* ```ts
|
|
16
|
+
* // grid.worker.ts
|
|
17
|
+
* import { serveWorkerDataSource } from '@svgrid/enterprise/worker'
|
|
18
|
+
* serveWorkerDataSource({ rows: () => fetch('/orders.json').then((r) => r.json()) })
|
|
19
|
+
*
|
|
20
|
+
* // App.svelte
|
|
21
|
+
* const worker = new Worker(new URL('./grid.worker.ts', import.meta.url), { type: 'module' })
|
|
22
|
+
* const ctl = createServerRowModel(createWorkerDataSource(worker), { groupBy: ['region'] })
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* What it cannot do: run your functions. A worker receives data, not code,
|
|
26
|
+
* so a column's `valueGetter`, `comparator` or custom filter function never
|
|
27
|
+
* reaches it. Sorting and filtering go by field value, as with any server.
|
|
28
|
+
*/
|
|
29
|
+
import type { RowData } from '@svgrid/grid';
|
|
30
|
+
import type { ServerDataSource } from '@svgrid/grid/server';
|
|
31
|
+
import type { EntitySchema } from '../schema.js';
|
|
32
|
+
import type { AggregateBucket, AggregateRequest } from '../sources/aggregate.js';
|
|
33
|
+
/** What `createWorkerDataSource` needs from a `Worker`. A `MessagePort` fits too. */
|
|
34
|
+
export type WorkerLike = {
|
|
35
|
+
postMessage(message: unknown): void;
|
|
36
|
+
addEventListener(type: string, listener: (event: MessageEvent) => void): void;
|
|
37
|
+
removeEventListener(type: string, listener: (event: MessageEvent) => void): void;
|
|
38
|
+
terminate?(): void;
|
|
39
|
+
};
|
|
40
|
+
export type WorkerDataSourceOptions<TData extends RowData> = {
|
|
41
|
+
/**
|
|
42
|
+
* Rows to copy into the worker. Copying is structured cloning on the main
|
|
43
|
+
* thread, so for a large dataset prefer loading it inside the worker
|
|
44
|
+
* (`serveWorkerDataSource({ rows })`) and leave this out.
|
|
45
|
+
*/
|
|
46
|
+
rows?: ReadonlyArray<TData>;
|
|
47
|
+
/** Sent with `rows`. Default: the worker's own, or one read off the rows. */
|
|
48
|
+
schema?: EntitySchema<TData>;
|
|
49
|
+
/** The row id field, when the schema is inferred. */
|
|
50
|
+
idField?: string;
|
|
51
|
+
};
|
|
52
|
+
export type WorkerDataSource<TData extends RowData> = Required<Pick<ServerDataSource<TData>, 'getRows' | 'createRow' | 'updateRow' | 'deleteRow' | 'updateWhere'>> & {
|
|
53
|
+
/** Group and reduce in the worker, for `SvSchemaChart` and dashboards. */
|
|
54
|
+
getAggregate(request: AggregateRequest): Promise<AggregateBucket[]>;
|
|
55
|
+
/** Settles once the worker holds rows: with the row count, or the loader's error. */
|
|
56
|
+
readonly ready: Promise<number>;
|
|
57
|
+
/** Replace the worker's rows. Resolves with the new count. */
|
|
58
|
+
setRows(rows: ReadonlyArray<TData>, schema?: EntitySchema<TData>): Promise<number>;
|
|
59
|
+
/** Reject what is pending and terminate the worker. The row model's `dispose()` calls it. */
|
|
60
|
+
destroy(): void;
|
|
61
|
+
};
|
|
62
|
+
export declare function createWorkerDataSource<TData extends RowData>(worker: WorkerLike, options?: WorkerDataSourceOptions<TData>): WorkerDataSource<TData>;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { CHANNEL, fromWorkerError, isChannelMessage, } from '../worker/protocol.js';
|
|
2
|
+
import { nudgeServerRowModel } from './enable.js';
|
|
3
|
+
function abortReason(signal) {
|
|
4
|
+
return signal.reason ?? new DOMException('The request was aborted.', 'AbortError');
|
|
5
|
+
}
|
|
6
|
+
export function createWorkerDataSource(worker, options = {}) {
|
|
7
|
+
nudgeServerRowModel();
|
|
8
|
+
let nextId = 1;
|
|
9
|
+
let destroyed = false;
|
|
10
|
+
// Set once the worker cannot answer any more: it failed to start, its
|
|
11
|
+
// loader threw, or `destroy()` ran. Every later call rejects with it.
|
|
12
|
+
let failure = null;
|
|
13
|
+
const pending = new Map();
|
|
14
|
+
let resolveReady;
|
|
15
|
+
let rejectReady;
|
|
16
|
+
const ready = new Promise((resolve, reject) => {
|
|
17
|
+
resolveReady = resolve;
|
|
18
|
+
rejectReady = reject;
|
|
19
|
+
});
|
|
20
|
+
// A source nobody awaited `ready` on must not raise an unhandled rejection.
|
|
21
|
+
ready.catch(() => { });
|
|
22
|
+
function failAll(error) {
|
|
23
|
+
failure ?? (failure = error);
|
|
24
|
+
rejectReady(error);
|
|
25
|
+
for (const p of pending.values())
|
|
26
|
+
p.reject(error);
|
|
27
|
+
pending.clear();
|
|
28
|
+
}
|
|
29
|
+
function onMessage(event) {
|
|
30
|
+
const message = event.data;
|
|
31
|
+
if (!isChannelMessage(message))
|
|
32
|
+
return;
|
|
33
|
+
switch (message.kind) {
|
|
34
|
+
case 'ready':
|
|
35
|
+
resolveReady(message.rowCount);
|
|
36
|
+
break;
|
|
37
|
+
case 'failed':
|
|
38
|
+
failAll(fromWorkerError(message.error));
|
|
39
|
+
break;
|
|
40
|
+
case 'result': {
|
|
41
|
+
const p = pending.get(message.id);
|
|
42
|
+
if (!p)
|
|
43
|
+
return; // cancelled, or answered after destroy
|
|
44
|
+
pending.delete(message.id);
|
|
45
|
+
if (message.ok)
|
|
46
|
+
p.resolve(message.value);
|
|
47
|
+
else
|
|
48
|
+
p.reject(fromWorkerError(message.error));
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// The worker script failed to load or threw at the top level. Without this
|
|
54
|
+
// every request would wait forever on a worker that is not there.
|
|
55
|
+
function onError(event) {
|
|
56
|
+
const message = event.message || 'The data source worker failed to start.';
|
|
57
|
+
failAll(new Error(message));
|
|
58
|
+
}
|
|
59
|
+
worker.addEventListener('message', onMessage);
|
|
60
|
+
worker.addEventListener('error', onError);
|
|
61
|
+
worker.start?.();
|
|
62
|
+
function send(message) {
|
|
63
|
+
worker.postMessage(message);
|
|
64
|
+
}
|
|
65
|
+
function request(message, signal) {
|
|
66
|
+
if (failure)
|
|
67
|
+
return Promise.reject(failure);
|
|
68
|
+
if (signal?.aborted)
|
|
69
|
+
return Promise.reject(abortReason(signal));
|
|
70
|
+
const id = nextId++;
|
|
71
|
+
return new Promise((resolve, reject) => {
|
|
72
|
+
const onAbort = () => {
|
|
73
|
+
if (!pending.delete(id))
|
|
74
|
+
return;
|
|
75
|
+
send({ channel: CHANNEL, kind: 'cancel', id });
|
|
76
|
+
reject(abortReason(signal));
|
|
77
|
+
};
|
|
78
|
+
pending.set(id, {
|
|
79
|
+
resolve: (value) => {
|
|
80
|
+
signal?.removeEventListener('abort', onAbort);
|
|
81
|
+
resolve(value);
|
|
82
|
+
},
|
|
83
|
+
reject: (error) => {
|
|
84
|
+
signal?.removeEventListener('abort', onAbort);
|
|
85
|
+
reject(error);
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
89
|
+
send(message(id));
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
function call(method, args, signal) {
|
|
93
|
+
return request((id) => ({ channel: CHANNEL, kind: 'call', id, method, args }), signal);
|
|
94
|
+
}
|
|
95
|
+
function load(rows, schema) {
|
|
96
|
+
return request((id) => ({
|
|
97
|
+
channel: CHANNEL,
|
|
98
|
+
kind: 'load',
|
|
99
|
+
id,
|
|
100
|
+
rows,
|
|
101
|
+
...(schema ? { schema: schema } : {}),
|
|
102
|
+
...(options.idField ? { idField: options.idField } : {}),
|
|
103
|
+
}));
|
|
104
|
+
}
|
|
105
|
+
if (options.rows)
|
|
106
|
+
void load(options.rows, options.schema).catch(failAll);
|
|
107
|
+
return {
|
|
108
|
+
getRows(req) {
|
|
109
|
+
// The signal cannot be cloned, and `parentRow` / `context` are app
|
|
110
|
+
// objects the in-memory source never reads; sending them would only
|
|
111
|
+
// risk a DataCloneError.
|
|
112
|
+
const { signal, parentRow: _parentRow, context: _context, ...plain } = req;
|
|
113
|
+
return call('getRows', [plain], signal);
|
|
114
|
+
},
|
|
115
|
+
createRow: (input) => call('createRow', [input]),
|
|
116
|
+
updateRow: (id, patch) => call('updateRow', [id, patch]),
|
|
117
|
+
deleteRow: (id) => call('deleteRow', [id]),
|
|
118
|
+
updateWhere: (filterModel, patch, selection) => call('updateWhere', [filterModel, patch, selection]),
|
|
119
|
+
getAggregate: (req) => call('getAggregate', [req]),
|
|
120
|
+
ready,
|
|
121
|
+
setRows: (rows, schema) => load(rows, schema ?? options.schema),
|
|
122
|
+
destroy() {
|
|
123
|
+
if (destroyed)
|
|
124
|
+
return;
|
|
125
|
+
destroyed = true;
|
|
126
|
+
failure = new Error('createWorkerDataSource: the source was destroyed');
|
|
127
|
+
failAll(failure);
|
|
128
|
+
worker.removeEventListener('message', onMessage);
|
|
129
|
+
worker.removeEventListener('error', onError);
|
|
130
|
+
worker.terminate?.();
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
@@ -3,7 +3,7 @@ import { blockColumns, blockStyleCss, blockClassName, sanitizeClassName, compone
|
|
|
3
3
|
import { uiComponentSpec, gridApiSettableProps, STANDARD_UI_EVENTS } from './ui-components.js';
|
|
4
4
|
import { isDarkTheme, themeTokenCss } from './themes.js';
|
|
5
5
|
import { hasFieldConditions } from '../edit-panel.js';
|
|
6
|
-
import { SVGRID_VERSION } from '../version.js';
|
|
6
|
+
import { SVGRID_VERSION, GRID_VERSION } from '../version.js';
|
|
7
7
|
import { emitEntityModules, homeFile, layoutFile, lookupVar, namesFor, prepareEntities, relationDisplayFields, sqlDdlFiles } from './emit-schema.js';
|
|
8
8
|
const has = (blocks, kind) => blocks.some((b) => b.config.kind === kind);
|
|
9
9
|
/** Per-Tabs-block active-tab state var + a stable tab id. */
|
|
@@ -5231,8 +5231,13 @@ export function runtimeDeps(project, allSource) {
|
|
|
5231
5231
|
// APIs of the SvGrid it was written by, so the app must install a runtime at
|
|
5232
5232
|
// least that new. `latest` also made a scaffold non-reproducible - an app could
|
|
5233
5233
|
// resolve a different runtime tomorrow, or break on an unrelated publish.
|
|
5234
|
-
|
|
5235
|
-
|
|
5234
|
+
// The two packages release on separate lines, so they need separate ranges.
|
|
5235
|
+
// Using this package's version for BOTH asked for `@svgrid/grid@^3.1.1`,
|
|
5236
|
+
// which does not exist, and `npm install` failed on every scaffolded app.
|
|
5237
|
+
const dependencies = {
|
|
5238
|
+
'@svgrid/grid': `^${GRID_VERSION}`,
|
|
5239
|
+
'@svgrid/enterprise': `^${SVGRID_VERSION}`,
|
|
5240
|
+
};
|
|
5236
5241
|
if (allSource.includes("from '@supabase/supabase-js'"))
|
|
5237
5242
|
dependencies['@supabase/supabase-js'] = '^2.45.0';
|
|
5238
5243
|
if (allSource.includes("import pg from 'pg'"))
|
|
@@ -7,8 +7,28 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import type { RowData, ServerDataSource } from '@svgrid/grid';
|
|
9
9
|
import { type EntitySchema } from '../schema.js';
|
|
10
|
+
import { type PlanPredicate, type QueryPlan } from './query-plan.js';
|
|
10
11
|
import type { WritableDataSource } from './types.js';
|
|
11
12
|
import { type AggregateSource } from '../sources/aggregate.js';
|
|
12
|
-
export declare function
|
|
13
|
+
export declare function asString(v: unknown): string;
|
|
14
|
+
export declare function matchesPredicate(row: RowData, p: PlanPredicate): boolean;
|
|
15
|
+
export declare function sortRows<T extends RowData>(rows: T[], orderBy: QueryPlan['orderBy']): T[];
|
|
16
|
+
export type InMemoryDataSourceOptions = {
|
|
17
|
+
/**
|
|
18
|
+
* Keep the last few filtered, sorted (and grouped) results and answer a
|
|
19
|
+
* request that differs only in its row range by slicing one, instead of
|
|
20
|
+
* filtering and sorting the whole array again. Scrolling a block-loading
|
|
21
|
+
* row model asks for the same query once per block, so on a large array
|
|
22
|
+
* this is the difference between one sort and one sort per block.
|
|
23
|
+
*
|
|
24
|
+
* Off by default because a cached result does not see a row object that
|
|
25
|
+
* was changed in place, only changes made through this source's own
|
|
26
|
+
* `createRow` / `updateRow` / `deleteRow` / `updateWhere`, which drop the
|
|
27
|
+
* cache. `createWorkerDataSource` turns it on: there the rows live in the
|
|
28
|
+
* worker and nothing else can reach them.
|
|
29
|
+
*/
|
|
30
|
+
cacheResults?: boolean;
|
|
31
|
+
};
|
|
32
|
+
export declare function createInMemoryDataSource<TData extends RowData>(initial: ReadonlyArray<TData>, schema: EntitySchema<TData>, options?: InMemoryDataSourceOptions): WritableDataSource<TData> & Required<Pick<ServerDataSource<TData>, 'updateWhere'>> & AggregateSource & {
|
|
13
33
|
rows(): ReadonlyArray<TData>;
|
|
14
34
|
};
|