@svadmin/core 0.49.1 → 0.52.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 (97) hide show
  1. package/README.md +32 -0
  2. package/package.json +21 -4
  3. package/src/access-control-contract.ts +250 -0
  4. package/src/adapters/casbin.ts +20 -14
  5. package/src/adapters/casl.ts +22 -19
  6. package/src/admin-tool.ts +7 -7
  7. package/src/audit-contract.ts +105 -0
  8. package/src/audit.ts +88 -128
  9. package/src/auth-hooks.svelte.ts +593 -261
  10. package/src/auth-mutation-contract.ts +143 -0
  11. package/src/auth-query-contract.ts +97 -0
  12. package/src/captured-mutation.svelte.ts +45 -0
  13. package/src/chatProvider.svelte.ts +1 -1
  14. package/src/command-contract.ts +195 -0
  15. package/src/context.svelte.ts +217 -205
  16. package/src/contract-context.svelte.ts +37 -0
  17. package/src/contract-form.svelte.ts +566 -0
  18. package/src/create-contract.ts +56 -0
  19. package/src/create-hooks.svelte.ts +347 -0
  20. package/src/create-many-contract.ts +90 -0
  21. package/src/create-many-hooks.svelte.ts +396 -0
  22. package/src/data-transfer.svelte.ts +1 -215
  23. package/src/defined-options.ts +50 -0
  24. package/src/delete-contract.ts +50 -0
  25. package/src/delete-hooks.svelte.ts +403 -0
  26. package/src/delete-many-contract.ts +60 -0
  27. package/src/delete-many-hooks.svelte.ts +402 -0
  28. package/src/enterprise.ts +4 -4
  29. package/src/export-format.ts +16 -14
  30. package/src/export-hooks.svelte.ts +191 -0
  31. package/src/feature-gate-contract.ts +53 -0
  32. package/src/hook-utils.svelte.ts +53 -105
  33. package/src/hooks.svelte.ts +19 -774
  34. package/src/http.ts +88 -87
  35. package/src/i18n.svelte.ts +56 -2
  36. package/src/import-contract.ts +106 -0
  37. package/src/import-hooks.svelte.ts +310 -0
  38. package/src/index.ts +77 -28
  39. package/src/inferencer-graphql.ts +7 -6
  40. package/src/infinite-query.svelte.ts +228 -0
  41. package/src/invalidation-contract.ts +26 -0
  42. package/src/live-sse.ts +91 -127
  43. package/src/live-subscription.svelte.ts +130 -0
  44. package/src/live-transport.ts +127 -0
  45. package/src/live-websocket.ts +85 -118
  46. package/src/live.svelte.ts +66 -68
  47. package/src/notification.svelte.ts +17 -15
  48. package/src/permission-catalog.ts +121 -0
  49. package/src/permission-hints-contract.ts +48 -0
  50. package/src/permissions.svelte.ts +112 -59
  51. package/src/plain-data.ts +61 -0
  52. package/src/provider-bundle.ts +85 -79
  53. package/src/provider-response.ts +35 -0
  54. package/src/query-hooks.svelte.ts +241 -286
  55. package/src/query-invalidation.ts +172 -0
  56. package/src/query-keys.ts +221 -110
  57. package/src/query-session.svelte.ts +216 -0
  58. package/src/query-snapshot.ts +144 -0
  59. package/src/reactive-projection.ts +53 -0
  60. package/src/record-decoder.ts +63 -0
  61. package/src/resource-binding.svelte.ts +19 -0
  62. package/src/resource-contract.ts +595 -0
  63. package/src/resource-mutation-types.ts +30 -0
  64. package/src/resource-schemas.ts +371 -0
  65. package/src/resource-transport-config.ts +156 -0
  66. package/src/route-parsing.ts +11 -11
  67. package/src/router-provider.ts +64 -59
  68. package/src/router.ts +8 -3
  69. package/src/routing-hooks.svelte.ts +90 -62
  70. package/src/schema-validation.ts +92 -0
  71. package/src/schema.ts +13 -0
  72. package/src/select-options.ts +95 -0
  73. package/src/select-query.svelte.ts +191 -0
  74. package/src/session-query.svelte.ts +88 -0
  75. package/src/strict-command-hooks.svelte.ts +333 -0
  76. package/src/strict-hooks.svelte.ts +198 -0
  77. package/src/table-hooks.svelte.ts +180 -174
  78. package/src/table-state.svelte.ts +117 -117
  79. package/src/task-contract.ts +87 -0
  80. package/src/task-hooks.svelte.ts +193 -149
  81. package/src/task-provider.ts +115 -0
  82. package/src/task-subscription.ts +132 -0
  83. package/src/theme.svelte.ts +155 -154
  84. package/src/toast.svelte.ts +38 -37
  85. package/src/types.ts +55 -103
  86. package/src/unsafe.ts +7 -0
  87. package/src/update-contract.ts +65 -0
  88. package/src/update-hooks.svelte.ts +347 -0
  89. package/src/update-many-contract.ts +79 -0
  90. package/src/update-many-hooks.svelte.ts +373 -0
  91. package/src/url-sync.ts +11 -10
  92. package/src/useCan.ts +96 -31
  93. package/src/useParsed.svelte.ts +18 -14
  94. package/src/useStepsForm.svelte.ts +123 -63
  95. package/src/utility-hooks.svelte.ts +101 -158
  96. package/src/form-hooks.svelte.ts +0 -889
  97. package/src/mutation-hooks.svelte.ts +0 -561
@@ -1,182 +1,187 @@
1
- import { useParsed } from './useParsed.svelte';
2
- import { useList } from './query-hooks.svelte';
3
- import { readURLState, writeURLState } from './url-sync';
4
- import { captureAdminContext } from './context.svelte';
1
+ import { definedOptions } from './defined-options';
2
+ import { createListQuery } from './query-hooks.svelte';
3
+ import { decodeBaseRecord, type RecordDecoder } from './record-decoder';
4
+ import { readURLState,writeURLState } from './url-sync';
5
+ import { captureContractContext } from './contract-context.svelte';
5
6
  import { getAdminOptions } from './options.svelte';
6
- import type { Pagination, Sort, Filter, BaseRecord, HttpError, KnownResources, GetListResult } from './types';
7
- import type { UseListOptions, MaybeGetter } from './query-hooks.svelte';
7
+ import type { Pagination,Sort,Filter,BaseRecord,HttpError,KnownResources } from './types';
8
+ import type { UseListOptions,MaybeGetter } from './query-hooks.svelte';
8
9
 
9
- export type FilterSetMode = 'merge' | 'replace';
10
+ export type FilterSetMode='merge'|'replace';
10
11
 
11
- export interface UseTableOptions<TQueryFnData extends BaseRecord = BaseRecord, TError = HttpError, _TSearchVariables = Record<string, unknown>> extends Omit<UseListOptions<TQueryFnData, TError>, 'pagination' | 'sorters' | 'filters'> {
12
+ export interface UseTableOptions<TQueryFnData extends BaseRecord=BaseRecord,TError=HttpError,_TSearchVariables=Record<string,unknown>> extends Omit<UseListOptions<TQueryFnData,TError>,'pagination'|'sorters'|'filters'> {
12
13
  resource?: KnownResources;
13
14
  pagination?: Pagination;
14
15
  sorters?: {
15
16
  initial?: Sort[];
16
17
  permanent?: Sort[];
17
- mode?: 'off' | 'server';
18
+ mode?: 'off'|'server';
18
19
  };
19
20
  filters?: {
20
21
  initial?: Filter[];
21
22
  permanent?: Filter[];
22
- mode?: 'off' | 'server';
23
+ mode?: 'off'|'server';
23
24
  defaultBehavior?: FilterSetMode;
24
25
  };
25
26
  syncWithLocation?: boolean;
26
27
  }
27
28
 
28
- export function useTable<
29
- TQueryFnData extends BaseRecord = BaseRecord,
30
- TError = HttpError,
31
- TSearchVariables = Record<string, unknown>
32
- >(optionsOrGetter: MaybeGetter<UseTableOptions<TQueryFnData, TError, TSearchVariables>> = {}) {
33
- const adminContext = captureAdminContext();
34
- const getOptions = () => typeof optionsOrGetter === 'function' ? optionsOrGetter() : optionsOrGetter;
35
- const parsed = useParsed();
36
-
29
+ export function useTable(optionsOrGetter: MaybeGetter<UseTableOptions>={}) {
30
+ return createTableQuery(optionsOrGetter, () => decodeBaseRecord);
31
+ }
32
+
33
+ export function createTableQuery<TQueryFnData extends BaseRecord>(
34
+ optionsOrGetter: MaybeGetter<UseTableOptions>, getDecoder: () => RecordDecoder<TQueryFnData>,
35
+ ) {
36
+ const adminContext=captureContractContext(optionsOrGetter);
37
+ const getOptions=() => typeof optionsOrGetter==='function'? optionsOrGetter():optionsOrGetter;
38
+
37
39
  // Use a local derived to compute these since they are used to initialize state
38
40
  // Even if options change, init values are only used once, which makes sense for initial state
39
- const initialOpts = getOptions();
40
- const syncWithLocation = initialOpts.syncWithLocation ?? getAdminOptions().syncWithLocation;
41
-
42
- const paginationMode = $derived(getOptions().pagination?.mode ?? 'server');
43
- const sortersMode = $derived(getOptions().sorters?.mode ?? 'server');
44
- const filtersMode = $derived(getOptions().filters?.mode ?? 'server');
45
- const filterDefaultBehavior = $derived(getOptions().filters?.defaultBehavior ?? 'replace');
41
+ const initialOpts=getOptions();
42
+ const syncWithLocation=initialOpts.syncWithLocation??getAdminOptions().syncWithLocation;
46
43
 
47
- const permanentSorters = $derived(getOptions().sorters?.permanent ?? []);
48
- const permanentFilters = $derived(getOptions().filters?.permanent ?? []);
44
+ const paginationMode=$derived(getOptions().pagination?.mode??'server');
45
+ const sortersMode=$derived(getOptions().sorters?.mode??'server');
46
+ const filtersMode=$derived(getOptions().filters?.mode??'server');
47
+ const filterDefaultBehavior=$derived(getOptions().filters?.defaultBehavior??'replace');
49
48
 
50
- let initPagination = initialOpts.pagination ?? { current: 1, pageSize: 10 };
51
- let initSorters = initialOpts.sorters?.initial ?? [];
52
- let initFilters = initialOpts.filters?.initial ?? [];
49
+ const permanentSorters=$derived(getOptions().sorters?.permanent??[]);
50
+ const permanentFilters=$derived(getOptions().filters?.permanent??[]);
53
51
 
54
- if (syncWithLocation && typeof window !== 'undefined') {
55
- const urlState = readURLState(adminContext);
56
- if (urlState.page || urlState.pageSize) {
57
- initPagination = {
58
- current: urlState.page ?? initPagination.current,
59
- pageSize: urlState.pageSize ?? initPagination.pageSize,
60
- };
52
+ let initPagination=initialOpts.pagination??{ current: 1,pageSize: 10 };
53
+ let initSorters=initialOpts.sorters?.initial??[];
54
+ let initFilters=initialOpts.filters?.initial??[];
55
+
56
+ if(syncWithLocation&&typeof window!=='undefined') {
57
+ const urlState=readURLState(adminContext);
58
+ if(urlState.page||urlState.pageSize) {
59
+ initPagination=definedOptions({
60
+ current: urlState.page??initPagination.current,
61
+ pageSize: urlState.pageSize??initPagination.pageSize,
62
+ });
61
63
  }
62
- if (urlState.sortField) {
63
- initSorters = [{ field: urlState.sortField, order: (urlState.sortOrder as 'asc' | 'desc') ?? 'asc' }];
64
+ if(urlState.sortField) {
65
+ initSorters=[{ field: urlState.sortField,order: urlState.sortOrder??'asc' }];
64
66
  }
65
- if (urlState.filters) {
66
- initFilters = urlState.filters;
67
+ if(urlState.filters) {
68
+ initFilters=urlState.filters;
67
69
  }
68
70
  }
69
71
 
70
- let pagination = $state<Pagination>(initPagination);
71
- let currentSorters = $state<Sort[]>(initSorters);
72
- let currentFilters = $state<Filter[]>(initFilters);
72
+ let pagination=$state<Pagination>(initPagination);
73
+ let currentSorters=$state<Sort[]>(initSorters);
74
+ let currentFilters=$state<Filter[]>(initFilters);
73
75
 
74
- const effectiveSorters = $derived([...currentSorters, ...permanentSorters]);
75
- const effectiveFilters = $derived([...currentFilters, ...permanentFilters]);
76
+ const effectiveSorters=$derived([...currentSorters,...permanentSorters]);
77
+ const effectiveFilters=$derived([...currentFilters,...permanentFilters]);
76
78
 
77
- const querySorters = $derived(sortersMode === 'server' ? effectiveSorters : []);
78
- const queryFilters = $derived(filtersMode === 'server' ? effectiveFilters : []);
79
- const queryPagination = $derived(paginationMode === 'server'
79
+ const querySorters=$derived(sortersMode==='server'? effectiveSorters:[]);
80
+ const queryFilters=$derived(filtersMode==='server'? effectiveFilters:[]);
81
+ const queryPagination=$derived(paginationMode==='server'
80
82
  ? pagination
81
- : { current: 1, pageSize: 999999, mode: paginationMode as 'client' | 'off' });
83
+ :{ current: 1,pageSize: 999999,mode: paginationMode });
82
84
 
83
- const tableQueryInfo = useList<TQueryFnData, TError>(() => {
84
- const currentOpts = getOptions();
85
- const { meta, syncWithLocation: _swl, dataProviderName, pagination: _p, sorters: _s, filters: _f, ...restOptions } = currentOpts;
85
+ const tableQueryInfo=createListQuery(() => {
86
+ const currentOpts=getOptions();
87
+ const { meta,syncWithLocation: _swl,dataProviderName,pagination: _p,sorters: _s,filters: _f,...restOptions }=currentOpts;
86
88
  return {
87
- resource: currentOpts.resource ?? parsed.resource ?? '',
88
89
  pagination: queryPagination,
89
90
  sorters: querySorters,
90
91
  filters: queryFilters,
91
- meta,
92
- dataProviderName,
92
+ ...definedOptions({
93
+ meta,
94
+ dataProviderName,
95
+ }),
93
96
  ...restOptions
94
97
  };
95
- });
98
+ }, getDecoder);
96
99
 
97
- const query = tableQueryInfo;
98
- const overtime = tableQueryInfo.overtime;
100
+ const query=tableQueryInfo;
101
+ const overtime=tableQueryInfo.overtime;
99
102
 
100
- function setSorters(newSorters: Sort[]) { currentSorters = newSorters; pagination = { ...pagination, current: 1 }; }
101
- function setFilters(newFilters: Filter[], mode?: FilterSetMode) {
102
- const behavior = mode ?? filterDefaultBehavior;
103
- if (behavior === 'merge') {
104
- const merged = [...currentFilters];
105
- for (const nf of newFilters) {
106
- if (!('field' in nf)) { merged.push(nf); continue; }
107
- const idx = merged.findIndex(f => 'field' in f && f.field === nf.field && f.operator === nf.operator);
108
- if (idx >= 0) {
109
- if (nf.value === undefined || nf.value === null || nf.value === '') merged.splice(idx, 1);
110
- else merged[idx] = nf;
111
- } else if (nf.value !== undefined && nf.value !== null && nf.value !== '') {
103
+ function setSorters(newSorters: Sort[]) { currentSorters=newSorters; pagination={ ...pagination,current: 1 }; }
104
+ function setFilters(newFilters: Filter[],mode?: FilterSetMode) {
105
+ const behavior=mode??filterDefaultBehavior;
106
+ if(behavior==='merge') {
107
+ const merged=[...currentFilters];
108
+ for(const nf of newFilters) {
109
+ if(!('field' in nf)) { merged.push(nf); continue; }
110
+ const idx=merged.findIndex(f => 'field' in f&&f.field===nf.field&&f.operator===nf.operator);
111
+ if(idx>=0) {
112
+ if(nf.value===undefined||nf.value===null||nf.value==='') merged.splice(idx,1);
113
+ else merged[idx]=nf;
114
+ } else if(nf.value!==undefined&&nf.value!==null&&nf.value!=='') {
112
115
  merged.push(nf);
113
116
  }
114
117
  }
115
- currentFilters = merged;
118
+ currentFilters=merged;
116
119
  } else {
117
- currentFilters = newFilters;
120
+ currentFilters=newFilters;
118
121
  }
119
- pagination = { ...pagination, current: 1 };
122
+ pagination={ ...pagination,current: 1 };
120
123
  }
121
124
 
122
- function setPage(page: number) { pagination = { ...pagination, current: page }; }
123
- function setPageSize(size: number) { pagination = { ...pagination, pageSize: size, current: 1 }; }
125
+ function setPage(page: number) { pagination={ ...pagination,current: page }; }
126
+ function setPageSize(size: number) { pagination={ ...pagination,pageSize: size,current: 1 }; }
124
127
 
125
- if (syncWithLocation && typeof window !== 'undefined') {
128
+ if(syncWithLocation&&typeof window!=='undefined') {
126
129
  $effect(() => {
127
130
  writeURLState({
128
131
  page: pagination.current,
129
132
  pageSize: pagination.pageSize,
130
133
  sortField: currentSorters[0]?.field,
131
134
  sortOrder: currentSorters[0]?.order,
132
- filters: currentFilters,
133
- }, adminContext);
135
+ ...definedOptions({
136
+ filters: currentFilters
137
+ })
138
+ },adminContext);
134
139
  });
135
140
 
136
141
  $effect(() => {
137
- const handler = () => {
138
- const urlState = readURLState(adminContext);
139
- const newPage = urlState.page ?? 1;
140
- if (newPage !== pagination.current) {
141
- pagination = { ...pagination, current: newPage };
142
+ const handler=() => {
143
+ const urlState=readURLState(adminContext);
144
+ const newPage=urlState.page??1;
145
+ if(newPage!==pagination.current) {
146
+ pagination={ ...pagination,current: newPage };
142
147
  }
143
- const newPageSize = urlState.pageSize ?? getOptions().pagination?.pageSize ?? getAdminOptions().defaultPageSize ?? 10;
144
- if (newPageSize !== pagination.pageSize) {
145
- pagination = { ...pagination, pageSize: newPageSize };
148
+ const newPageSize=urlState.pageSize??getOptions().pagination?.pageSize??getAdminOptions().defaultPageSize??10;
149
+ if(newPageSize!==pagination.pageSize) {
150
+ pagination={ ...pagination,pageSize: newPageSize };
146
151
  }
147
- if (urlState.sortField) {
148
- const newSorter: Sort = { field: urlState.sortField, order: (urlState.sortOrder as 'asc' | 'desc') ?? 'asc' };
149
- if (JSON.stringify([newSorter]) !== JSON.stringify(currentSorters)) {
150
- currentSorters = [newSorter];
152
+ if(urlState.sortField) {
153
+ const newSorter: Sort={ field: urlState.sortField,order: urlState.sortOrder??'asc' };
154
+ if(JSON.stringify([newSorter])!==JSON.stringify(currentSorters)) {
155
+ currentSorters=[newSorter];
151
156
  }
152
- } else if (currentSorters.length > 0) {
153
- currentSorters = [];
157
+ } else if(currentSorters.length>0) {
158
+ currentSorters=[];
154
159
  }
155
- if (urlState.filters && JSON.stringify(urlState.filters) !== JSON.stringify(currentFilters)) {
156
- currentFilters = urlState.filters;
157
- } else if (!urlState.filters && currentFilters.length > 0) {
158
- currentFilters = [];
160
+ if(urlState.filters&&JSON.stringify(urlState.filters)!==JSON.stringify(currentFilters)) {
161
+ currentFilters=urlState.filters;
162
+ } else if(!urlState.filters&&currentFilters.length>0) {
163
+ currentFilters=[];
159
164
  }
160
165
  };
161
- window.addEventListener('popstate', handler);
162
- window.addEventListener('hashchange', handler);
166
+ window.addEventListener('popstate',handler);
167
+ window.addEventListener('hashchange',handler);
163
168
  return () => {
164
- window.removeEventListener('popstate', handler);
165
- window.removeEventListener('hashchange', handler);
169
+ window.removeEventListener('popstate',handler);
170
+ window.removeEventListener('hashchange',handler);
166
171
  };
167
172
  });
168
173
  }
169
174
 
170
175
  function createLinkForSyncWithLocation(): string {
171
- const params = new URLSearchParams();
172
- if (pagination.current) params.set('page', String(pagination.current));
173
- if (pagination.pageSize) params.set('pageSize', String(pagination.pageSize));
174
- if (currentSorters[0]) {
175
- params.set('sort', currentSorters[0].field);
176
- params.set('order', currentSorters[0].order);
176
+ const params=new URLSearchParams();
177
+ if(pagination.current) params.set('page',String(pagination.current));
178
+ if(pagination.pageSize) params.set('pageSize',String(pagination.pageSize));
179
+ if(currentSorters[0]) {
180
+ params.set('sort',currentSorters[0].field);
181
+ params.set('order',currentSorters[0].order);
177
182
  }
178
- if (currentFilters.length > 0) {
179
- params.set('filters', JSON.stringify(currentFilters));
183
+ if(currentFilters.length>0) {
184
+ params.set('filters',JSON.stringify(currentFilters));
180
185
  }
181
186
  return `?${params.toString()}`;
182
187
  }
@@ -187,91 +192,92 @@ export function useTable<
187
192
  get pagination() { return pagination; },
188
193
  get sorters() { return currentSorters; },
189
194
  get filters() { return currentFilters; },
190
- setSorters, setFilters, setPage, setPageSize,
195
+ setSorters,setFilters,setPage,setPageSize,
191
196
  createLinkForSyncWithLocation,
192
197
  get totalPages() {
193
- // ts-expect-error reactive access
194
- const total = (query.data as GetListResult<TQueryFnData> | undefined)?.total ?? 0;
195
- return Math.ceil(total / (pagination.pageSize ?? 10));
198
+ const total=query.data?.total??0;
199
+ return Math.ceil(total/(pagination.pageSize??10));
196
200
  },
197
- get current() { return pagination.current ?? 1; },
198
- get pageSize() { return pagination.pageSize ?? 10; },
199
- get pageCount() { return Math.ceil(((query.data as GetListResult<TQueryFnData> | undefined)?.total ?? 0) / (pagination.pageSize ?? 10)); },
201
+ get current() { return pagination.current??1; },
202
+ get pageSize() { return pagination.pageSize??10; },
203
+ get pageCount() { return Math.ceil((query.data?.total??0)/(pagination.pageSize??10)); },
200
204
  get clientData(): TQueryFnData[] {
201
- if (paginationMode === 'server') return [];
202
- const allData = ((query.data as GetListResult<TQueryFnData> | undefined)?.data ?? []) as TQueryFnData[];
203
- let sorted = [...allData];
204
- const activeSorters = sortersMode === 'off' ? effectiveSorters : [];
205
- if (activeSorters.length > 0) {
206
- sorted.sort((a, b) => {
207
- for (const { field, order } of activeSorters) {
208
- const va = (a as Record<string, unknown>)[field];
209
- const vb = (b as Record<string, unknown>)[field];
210
- if (va == null && vb != null) return 1;
211
- if (vb == null && va != null) return -1;
212
- if (va == null && vb == null) continue;
213
-
214
- // eslint-disable-next-line no-useless-assignment
215
- let cmp = 0;
216
- if (typeof va === 'number' && typeof vb === 'number') cmp = va - vb;
217
- else if (va instanceof Date && vb instanceof Date) cmp = va.getTime() - vb.getTime();
218
- else if (typeof va === 'string' && typeof vb === 'string' && !isNaN(Number(va)) && !isNaN(Number(vb))) cmp = Number(va) - Number(vb);
219
- else cmp = String(va).localeCompare(String(vb));
220
-
221
- if (cmp !== 0) return order === 'desc' ? -cmp : cmp;
205
+ if(paginationMode==='server') return [];
206
+ const allData=query.data?.data??[];
207
+ let sorted=[...allData];
208
+ const activeSorters=sortersMode==='off'? effectiveSorters:[];
209
+ if(activeSorters.length>0) {
210
+ sorted.sort((a,b) => {
211
+ for(const { field,order } of activeSorters) {
212
+ const va=a[field];
213
+ const vb=b[field];
214
+ if(va==null&&vb!=null) return 1;
215
+ if(vb==null&&va!=null) return -1;
216
+ if(va==null&&vb==null) continue;
217
+
218
+ let cmp: number;
219
+ if(typeof va==='number'&&typeof vb==='number') cmp=va-vb;
220
+ else if(va instanceof Date&&vb instanceof Date) cmp=va.getTime()-vb.getTime();
221
+ else if(typeof va==='string'&&typeof vb==='string'&&!isNaN(Number(va))&&!isNaN(Number(vb))) cmp=Number(va)-Number(vb);
222
+ else cmp=String(va).localeCompare(String(vb));
223
+
224
+ if(cmp!==0) return order==='desc'? -cmp:cmp;
222
225
  }
223
226
  return 0;
224
227
  });
225
228
  }
226
- const activeFilters = filtersMode === 'off' ? effectiveFilters : [];
227
- if (activeFilters.length > 0) {
228
- function matchesFilter(item: Record<string, unknown>, f: Filter): boolean {
229
- if (!('field' in f)) {
230
- if (f.operator === 'and') return f.value.every(sub => matchesFilter(item, sub));
231
- if (f.operator === 'or') return f.value.length === 0 || f.value.some(sub => matchesFilter(item, sub));
229
+ const activeFilters=filtersMode==='off'? effectiveFilters:[];
230
+ if(activeFilters.length>0) {
231
+ function matchesFilter(item: Record<string,unknown>,f: Filter): boolean {
232
+ if(!('field' in f)) {
233
+ if(f.operator==='and') return f.value.every(sub => matchesFilter(item,sub));
234
+ if(f.operator==='or') return f.value.length===0||f.value.some(sub => matchesFilter(item,sub));
232
235
  return true;
233
236
  }
234
- const val = item[f.field];
235
- const fv = f.value;
236
- switch (f.operator) {
237
- case 'null': return val == null;
238
- case 'nnull': return val != null;
237
+ const val=item[f.field];
238
+ const fv=f.value;
239
+ switch(f.operator) {
240
+ case 'null': return val==null;
241
+ case 'nnull': return val!=null;
239
242
  default: break;
240
243
  }
241
- if (fv == null || fv === '') return true;
242
- switch (f.operator) {
243
- case 'eq': return val == fv;
244
- case 'ne': return val != fv;
245
- case 'lt': return typeof val === 'number' && typeof fv === 'number' && val < fv;
246
- case 'gt': return typeof val === 'number' && typeof fv === 'number' && val > fv;
247
- case 'lte': return typeof val === 'number' && typeof fv === 'number' && val <= fv;
248
- case 'gte': return typeof val === 'number' && typeof fv === 'number' && val >= fv;
249
- case 'in': return !Array.isArray(fv) || fv.length === 0 || fv.includes(val);
250
- case 'nin': return !Array.isArray(fv) || fv.length === 0 || !fv.includes(val);
251
- case 'contains': return typeof val === 'string' && typeof fv === 'string' && val.includes(fv);
252
- case 'ncontains': return typeof val === 'string' && typeof fv === 'string' && !val.includes(fv);
253
- case 'startswith': return typeof val === 'string' && typeof fv === 'string' && val.startsWith(fv);
254
- case 'endswith': return typeof val === 'string' && typeof fv === 'string' && val.endsWith(fv);
244
+ if(fv==null||fv==='') return true;
245
+ switch(f.operator) {
246
+ case 'eq': return val==fv;
247
+ case 'ne': return val!=fv;
248
+ case 'lt': return typeof val==='number'&&typeof fv==='number'&&val<fv;
249
+ case 'gt': return typeof val==='number'&&typeof fv==='number'&&val>fv;
250
+ case 'lte': return typeof val==='number'&&typeof fv==='number'&&val<=fv;
251
+ case 'gte': return typeof val==='number'&&typeof fv==='number'&&val>=fv;
252
+ case 'in': return !Array.isArray(fv)||fv.length===0||fv.includes(val);
253
+ case 'nin': return !Array.isArray(fv)||fv.length===0||!fv.includes(val);
254
+ case 'contains': return typeof val==='string'&&typeof fv==='string'&&val.includes(fv);
255
+ case 'ncontains': return typeof val==='string'&&typeof fv==='string'&&!val.includes(fv);
256
+ case 'startswith': return typeof val==='string'&&typeof fv==='string'&&val.startsWith(fv);
257
+ case 'endswith': return typeof val==='string'&&typeof fv==='string'&&val.endsWith(fv);
255
258
  case 'between': {
256
- if (!Array.isArray(fv) || fv.length !== 2) return true;
257
- return typeof val === 'number' && val >= (fv[0] as number) && val <= (fv[1] as number);
259
+ if(!Array.isArray(fv)||fv.length!==2) return true;
260
+ const [min,max]: unknown[]=fv;
261
+ return typeof val==='number'&&typeof min==='number'&&typeof max==='number'
262
+ &&val>=min&&val<=max;
258
263
  }
259
264
  case 'nbetween': {
260
- if (!Array.isArray(fv) || fv.length !== 2) return true;
261
- return typeof val === 'number' && (val < (fv[0] as number) || val > (fv[1] as number));
265
+ if(!Array.isArray(fv)||fv.length!==2) return true;
266
+ const [min,max]: unknown[]=fv;
267
+ return typeof val==='number'&&typeof min==='number'&&typeof max==='number'
268
+ &&(val<min||val>max);
262
269
  }
263
270
  default: return true;
264
271
  }
265
272
  }
266
- sorted = sorted.filter((item) => {
267
- const rec = item as Record<string, unknown>;
268
- return activeFilters.every(f => matchesFilter(rec, f));
273
+ sorted=sorted.filter((item) => {
274
+ return activeFilters.every(f => matchesFilter(item,f));
269
275
  });
270
276
  }
271
- if (paginationMode === 'off') return sorted;
272
- const start = ((pagination.current ?? 1) - 1) * (pagination.pageSize ?? 10);
273
- const end = start + (pagination.pageSize ?? 10);
274
- return sorted.slice(start, end);
277
+ if(paginationMode==='off') return sorted;
278
+ const start=((pagination.current??1)-1)*(pagination.pageSize??10);
279
+ const end=start+(pagination.pageSize??10);
280
+ return sorted.slice(start,end);
275
281
  },
276
282
  };
277
283
  }