@svadmin/ui 0.88.1 → 0.92.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.
- package/dist/app.css +4 -0
- package/dist/app.theme.css +4 -0
- package/dist/component-registry.svelte.d.ts +2 -2
- package/dist/components/AboutSettings.svelte +23 -4
- package/dist/components/AdminApp.svelte +6 -4
- package/dist/components/AdminApp.svelte.d.ts +3 -3
- package/dist/components/ApiSettings.svelte +43 -27
- package/dist/components/ApprovalActionCard.svelte +8 -9
- package/dist/components/ApprovalCenter.svelte +24 -5
- package/dist/components/AuditLogViewer.svelte +2 -2
- package/dist/components/AutoComplete.svelte +610 -0
- package/dist/components/AutoComplete.svelte.d.ts +25 -0
- package/dist/components/AutoForm.svelte +265 -18
- package/dist/components/AutoForm.svelte.d.ts +19 -2
- package/dist/components/AutoTable.svelte +141 -36
- package/dist/components/AutoTable.svelte.d.ts +8 -0
- package/dist/components/BatchActionBar.svelte +42 -14
- package/dist/components/BatchActionBar.svelte.d.ts +5 -0
- package/dist/components/BoundRecordDetailDrawer.svelte +5 -3
- package/dist/components/CancelTaskButton.svelte +14 -1
- package/dist/components/Cascader.svelte +412 -53
- package/dist/components/Cascader.svelte.d.ts +7 -1
- package/dist/components/ColorPicker.svelte +1 -1
- package/dist/components/ColorPicker.svelte.d.ts +1 -1
- package/dist/components/ComboboxField.svelte +270 -34
- package/dist/components/ComboboxField.svelte.d.ts +4 -0
- package/dist/components/CommandPalette.svelte +4 -4
- package/dist/components/DashboardView.svelte +73 -7
- package/dist/components/DashboardView.svelte.d.ts +3 -1
- package/dist/components/DateRangeInput.svelte +8 -1
- package/dist/components/DetailWorkspaceAsyncSection.svelte +107 -0
- package/dist/components/DetailWorkspaceAsyncSection.svelte.d.ts +36 -0
- package/dist/components/DetailWorkspaceSections.svelte +233 -0
- package/dist/components/DetailWorkspaceSections.svelte.d.ts +9 -0
- package/dist/components/DrawerForm.svelte +78 -9
- package/dist/components/DrawerForm.svelte.d.ts +3 -3
- package/dist/components/ErrorBoundary.svelte +1 -1
- package/dist/components/ErrorComponent.svelte +19 -6
- package/dist/components/ErrorComponent.svelte.d.ts +1 -0
- package/dist/components/ErrorSummary.svelte +9 -4
- package/dist/components/FieldRenderer.svelte +203 -90
- package/dist/components/FieldRenderer.svelte.d.ts +16 -1
- package/dist/components/FileUpload.svelte +116 -51
- package/dist/components/FileUpload.svelte.d.ts +7 -27
- package/dist/components/FileUploadField.svelte +170 -0
- package/dist/components/FileUploadField.svelte.d.ts +21 -0
- package/dist/components/FilterBuilder.svelte +13 -7
- package/dist/components/FilterCollectionInput.svelte +9 -7
- package/dist/components/FilterCollectionInput.svelte.d.ts +3 -0
- package/dist/components/ForgotPasswordPage.svelte +1 -1
- package/dist/components/Header.svelte +1 -1
- package/dist/components/ImportWizard.svelte +197 -28
- package/dist/components/ImportWizard.svelte.d.ts +5 -1
- package/dist/components/InferencerPanel.svelte +4 -2
- package/dist/components/InfiniteList.svelte +52 -9
- package/dist/components/JsonSchemaForm.svelte +237 -42
- package/dist/components/JsonSchemaForm.svelte.d.ts +10 -3
- package/dist/components/KanbanBoard.svelte +43 -7
- package/dist/components/KanbanBoard.svelte.d.ts +2 -2
- package/dist/components/Layout.svelte +1 -1
- package/dist/components/LoginPage.svelte +1 -1
- package/dist/components/MetricStrip.svelte +8 -3
- package/dist/components/MetricStrip.svelte.d.ts +1 -0
- package/dist/components/ModalForm.svelte +85 -9
- package/dist/components/ModalForm.svelte.d.ts +3 -3
- package/dist/components/MoneyInput.svelte +5 -3
- package/dist/components/MoneyInput.svelte.d.ts +4 -2
- package/dist/components/MultiTabKeepAlive.svelte +502 -43
- package/dist/components/MultiTabKeepAlive.svelte.d.ts +40 -6
- package/dist/components/NotificationsSettings.svelte +10 -10
- package/dist/components/NumberInput.svelte +5 -3
- package/dist/components/NumberInput.svelte.d.ts +4 -2
- package/dist/components/NumericInput.svelte +277 -25
- package/dist/components/NumericInput.svelte.d.ts +3 -1
- package/dist/components/PercentInput.svelte +5 -3
- package/dist/components/PercentInput.svelte.d.ts +4 -2
- package/dist/components/PermissionMatrix.svelte +44 -8
- package/dist/components/PermissionMatrix.svelte.d.ts +1 -0
- package/dist/components/ProfilePage.svelte +38 -13
- package/dist/components/RadioGroup.svelte +203 -4
- package/dist/components/RadioGroup.svelte.d.ts +1 -1
- package/dist/components/Rate.svelte +3 -3
- package/dist/components/Rate.svelte.d.ts +2 -2
- package/dist/components/RecordDetailDrawer.svelte +1 -1
- package/dist/components/RecordDetailDrawer.svelte.d.ts +1 -1
- package/dist/components/RegisterPage.svelte +1 -1
- package/dist/components/RelationPicker.svelte +39 -11
- package/dist/components/ResourceAccessGuard.svelte +7 -2
- package/dist/components/RetryTaskButton.svelte +14 -1
- package/dist/components/RolesSettings.svelte +74 -28
- package/dist/components/SecuritySettings.svelte +66 -51
- package/dist/components/SegmentedControl.svelte +4 -2
- package/dist/components/SegmentedControl.svelte.d.ts +1 -1
- package/dist/components/Sidebar.svelte +1 -1
- package/dist/components/StepsForm.svelte +62 -10
- package/dist/components/StepsForm.svelte.d.ts +4 -2
- package/dist/components/SvadminLogo.svelte +1 -0
- package/dist/components/SvarDataGrid.svelte +42 -24
- package/dist/components/SvarDataGrid.svelte.d.ts +10 -10
- package/dist/components/SvarResourceRead.svelte +2 -0
- package/dist/components/SvarResourceTableView.svelte +4 -2
- package/dist/components/TagsInput.svelte +3 -3
- package/dist/components/TagsInput.svelte.d.ts +2 -2
- package/dist/components/TaskDetails.svelte +22 -16
- package/dist/components/TaskDetails.svelte.d.ts +1 -1
- package/dist/components/TaskList.svelte +57 -15
- package/dist/components/TaskQueueDrawer.svelte +54 -34
- package/dist/components/Toast.svelte +13 -2
- package/dist/components/Transfer.svelte +67 -5
- package/dist/components/Transfer.svelte.d.ts +4 -0
- package/dist/components/TreeSelect.svelte +337 -77
- package/dist/components/TreeSelect.svelte.d.ts +4 -0
- package/dist/components/TreeTable.svelte +6 -1
- package/dist/components/TwoFactorAuthPage.svelte +8 -4
- package/dist/components/UpdatePasswordPage.svelte +1 -1
- package/dist/components/VersionDiffViewer.svelte +9 -7
- package/dist/components/account/CompanyProfilePage.svelte +13 -12
- package/dist/components/account/MemberDirectory.svelte +14 -10
- package/dist/components/account/SettingsEnterprisePage.svelte +18 -18
- package/dist/components/content/AuditTimeline.svelte +8 -6
- package/dist/components/content/DataState.svelte +1 -1
- package/dist/components/content/FileList.svelte +44 -13
- package/dist/components/content/FileList.svelte.d.ts +4 -2
- package/dist/components/content/FileListRow.svelte +87 -0
- package/dist/components/content/FileListRow.svelte.d.ts +11 -0
- package/dist/components/content/ImportDropzone.svelte +109 -12
- package/dist/components/content/ImportDropzone.svelte.d.ts +1 -0
- package/dist/components/content/IntegrationCard.svelte +3 -3
- package/dist/components/content/IntegrationCard.svelte.d.ts +2 -0
- package/dist/components/content/MediaThumbnail.svelte +6 -2
- package/dist/components/detail-workspace.d.ts +60 -0
- package/dist/components/detail-workspace.js +3 -0
- package/dist/components/enterprise/json-schema-form.d.ts +1 -1
- package/dist/components/enterprise/recursive-schema-form.d.ts +15 -1
- package/dist/components/enterprise/recursive-schema-form.js +288 -14
- package/dist/components/enterprise/schema-form-references.d.ts +14 -0
- package/dist/components/enterprise/schema-form-references.js +316 -0
- package/dist/components/fields/ArrayField.svelte +178 -14
- package/dist/components/fields/ArrayField.svelte.d.ts +17 -3
- package/dist/components/fields/array-field-metadata.d.ts +4 -0
- package/dist/components/fields/array-field-metadata.js +36 -0
- package/dist/components/file-upload-reference.d.ts +11 -0
- package/dist/components/file-upload-reference.js +50 -0
- package/dist/components/file-upload-types.d.ts +32 -0
- package/dist/components/file-upload-types.js +1 -0
- package/dist/components/form-contract.upload-type-fixture.svelte +108 -0
- package/dist/components/form-contract.upload-type-fixture.svelte.d.ts +18 -0
- package/dist/components/json-schema-form-state.d.ts +19 -0
- package/dist/components/list-deletion.svelte.js +6 -1
- package/dist/components/list-state.svelte.d.ts +12 -1
- package/dist/components/list-state.svelte.js +56 -16
- package/dist/components/numeric-format.d.ts +44 -0
- package/dist/components/numeric-format.js +198 -0
- package/dist/components/record-detail.svelte.js +2 -0
- package/dist/components/saved-list-views.d.ts +9 -2
- package/dist/components/saved-list-views.js +42 -11
- package/dist/components/saved-list-views.svelte.d.ts +3 -0
- package/dist/components/saved-list-views.svelte.js +56 -7
- package/dist/components/task-action.svelte.d.ts +1 -0
- package/dist/components/task-action.svelte.js +55 -3
- package/dist/components/task-utils.d.ts +1 -0
- package/dist/components/task-utils.js +18 -0
- package/dist/components/ui/input/input.svelte +1 -1
- package/dist/components/ui/input/input.svelte.d.ts +1 -1
- package/dist/components/ui/sheet/sheet.svelte +4 -1
- package/dist/components/version-history.d.ts +14 -0
- package/dist/components/version-history.js +32 -0
- package/dist/components/workspace-tab-state.d.ts +78 -0
- package/dist/components/workspace-tab-state.js +587 -0
- package/dist/index.d.ts +13 -1
- package/dist/index.js +4 -0
- package/package.json +5 -5
|
@@ -0,0 +1,587 @@
|
|
|
1
|
+
const SNAPSHOT_VERSION = 1;
|
|
2
|
+
const DEFAULT_KEY_PREFIX = 'svadmin:workspace-tab-state';
|
|
3
|
+
const ANONYMOUS_USER_ID = 'anonymous';
|
|
4
|
+
const DEFAULT_MAX_DRAFT_BYTES = 65536;
|
|
5
|
+
const MAX_DRAFT_DEPTH = 32;
|
|
6
|
+
const MAX_DRAFT_NODES = 10000;
|
|
7
|
+
const FORBIDDEN_DRAFT_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
|
|
8
|
+
const EMPTY_STATE = {
|
|
9
|
+
query: '',
|
|
10
|
+
scrollTop: 0,
|
|
11
|
+
scrollLeft: 0,
|
|
12
|
+
draft: false,
|
|
13
|
+
};
|
|
14
|
+
// 同一 storage 对象上的关闭标记需跨 adapter 实例共享,不能只依赖各自的内存
|
|
15
|
+
// tombstone。持久化的关闭信封负责刷新/新运行时;这个 WeakMap 负责同一运行时
|
|
16
|
+
// 即使 storage 写入也失败时的最小安全兜底。
|
|
17
|
+
const sharedClearTombstones = new WeakMap();
|
|
18
|
+
function sharedTombstonesFor(storage) {
|
|
19
|
+
let tombstones = sharedClearTombstones.get(storage);
|
|
20
|
+
if (!tombstones) {
|
|
21
|
+
tombstones = new Map();
|
|
22
|
+
sharedClearTombstones.set(storage, tombstones);
|
|
23
|
+
}
|
|
24
|
+
return tombstones;
|
|
25
|
+
}
|
|
26
|
+
function normalizeQuery(value, fallback) {
|
|
27
|
+
if (typeof value !== 'string')
|
|
28
|
+
return fallback;
|
|
29
|
+
const withoutPrefix = value.startsWith('?') ? value.slice(1) : value;
|
|
30
|
+
return withoutPrefix;
|
|
31
|
+
}
|
|
32
|
+
function normalizeScroll(value, fallback) {
|
|
33
|
+
if (typeof value !== 'number' || !Number.isFinite(value))
|
|
34
|
+
return fallback;
|
|
35
|
+
return Math.max(0, Math.floor(value));
|
|
36
|
+
}
|
|
37
|
+
function normalizeDraft(value, fallback) {
|
|
38
|
+
return typeof value === 'boolean' ? value : fallback;
|
|
39
|
+
}
|
|
40
|
+
function isValidScroll(value) {
|
|
41
|
+
return typeof value === 'number' && Number.isFinite(value) && value >= 0;
|
|
42
|
+
}
|
|
43
|
+
function normalizeState(state, fallback = EMPTY_STATE) {
|
|
44
|
+
return {
|
|
45
|
+
query: normalizeQuery(state?.query, fallback.query),
|
|
46
|
+
scrollTop: normalizeScroll(state?.scrollTop, fallback.scrollTop),
|
|
47
|
+
scrollLeft: normalizeScroll(state?.scrollLeft, fallback.scrollLeft),
|
|
48
|
+
draft: normalizeDraft(state?.draft, fallback.draft),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function cloneState(state) {
|
|
52
|
+
const cloned = { ...state };
|
|
53
|
+
if (state.draftData !== undefined) {
|
|
54
|
+
cloned.draftData = cloneDraftData(state.draftData);
|
|
55
|
+
}
|
|
56
|
+
return cloned;
|
|
57
|
+
}
|
|
58
|
+
function isPlainObject(value) {
|
|
59
|
+
const prototype = Object.getPrototypeOf(value);
|
|
60
|
+
return prototype === Object.prototype || prototype === null;
|
|
61
|
+
}
|
|
62
|
+
function isPlainDraftObject(value) {
|
|
63
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value) && isPlainObject(value);
|
|
64
|
+
}
|
|
65
|
+
function validateDraftJsonValue(value, budget, depth) {
|
|
66
|
+
budget.nodes += 1;
|
|
67
|
+
if (budget.nodes > MAX_DRAFT_NODES)
|
|
68
|
+
return false;
|
|
69
|
+
if (depth > MAX_DRAFT_DEPTH)
|
|
70
|
+
return false;
|
|
71
|
+
if (value === null)
|
|
72
|
+
return true;
|
|
73
|
+
switch (typeof value) {
|
|
74
|
+
case 'boolean':
|
|
75
|
+
case 'string':
|
|
76
|
+
return true;
|
|
77
|
+
case 'number':
|
|
78
|
+
return Number.isFinite(value);
|
|
79
|
+
case 'object':
|
|
80
|
+
break;
|
|
81
|
+
default:
|
|
82
|
+
// undefined、函数、Symbol、BigInt 都不是纯 JSON 值。
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
const objectValue = value;
|
|
86
|
+
const stack = budget.stack;
|
|
87
|
+
if (objectValue instanceof Date ||
|
|
88
|
+
objectValue instanceof Map ||
|
|
89
|
+
objectValue instanceof Set ||
|
|
90
|
+
(typeof File !== 'undefined' && objectValue instanceof File) ||
|
|
91
|
+
(typeof Blob !== 'undefined' && objectValue instanceof Blob)) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
if (stack.has(objectValue))
|
|
95
|
+
return false;
|
|
96
|
+
stack.add(objectValue);
|
|
97
|
+
try {
|
|
98
|
+
if (Array.isArray(objectValue)) {
|
|
99
|
+
if (Object.getOwnPropertySymbols(objectValue).length > 0)
|
|
100
|
+
return false;
|
|
101
|
+
const names = Object.getOwnPropertyNames(objectValue);
|
|
102
|
+
for (const name of names) {
|
|
103
|
+
if (name === 'length')
|
|
104
|
+
continue;
|
|
105
|
+
// 稀疏数组、非索引属性、访问器或不可枚举元素都不属于纯 JSON 数组。
|
|
106
|
+
if (!/^(0|[1-9]\d*)$/.test(name))
|
|
107
|
+
return false;
|
|
108
|
+
const index = Number(name);
|
|
109
|
+
if (!Number.isInteger(index) || index < 0 || index >= objectValue.length)
|
|
110
|
+
return false;
|
|
111
|
+
const descriptor = Object.getOwnPropertyDescriptor(objectValue, name);
|
|
112
|
+
if (!descriptor ||
|
|
113
|
+
typeof descriptor.get === 'function' ||
|
|
114
|
+
typeof descriptor.set === 'function' ||
|
|
115
|
+
descriptor.enumerable !== true) {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
if (!validateDraftJsonValue(objectValue[index], budget, depth + 1)) {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
for (let index = 0; index < objectValue.length; index += 1) {
|
|
123
|
+
if (!Object.prototype.hasOwnProperty.call(objectValue, String(index)))
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
if (!isPlainObject(objectValue))
|
|
129
|
+
return false;
|
|
130
|
+
if (Object.getOwnPropertySymbols(objectValue).length > 0)
|
|
131
|
+
return false;
|
|
132
|
+
for (const key of Object.getOwnPropertyNames(objectValue)) {
|
|
133
|
+
if (FORBIDDEN_DRAFT_KEYS.has(key))
|
|
134
|
+
return false;
|
|
135
|
+
const descriptor = Object.getOwnPropertyDescriptor(objectValue, key);
|
|
136
|
+
if (!descriptor ||
|
|
137
|
+
typeof descriptor.get === 'function' ||
|
|
138
|
+
typeof descriptor.set === 'function' ||
|
|
139
|
+
descriptor.enumerable !== true) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
if (!validateDraftJsonValue(objectValue[key], budget, depth + 1)) {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
finally {
|
|
149
|
+
stack.delete(objectValue);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
function isValidDraftJsonObject(value) {
|
|
153
|
+
return (isPlainDraftObject(value) &&
|
|
154
|
+
validateDraftJsonValue(value, { stack: new WeakSet(), nodes: 0 }, 0));
|
|
155
|
+
}
|
|
156
|
+
function cloneDraftJsonValue(value) {
|
|
157
|
+
if (Array.isArray(value))
|
|
158
|
+
return value.map(cloneDraftJsonValue);
|
|
159
|
+
if (value !== null && typeof value === 'object') {
|
|
160
|
+
const cloned = {};
|
|
161
|
+
for (const key of Object.keys(value)) {
|
|
162
|
+
const child = value[key];
|
|
163
|
+
if (child !== undefined)
|
|
164
|
+
cloned[key] = cloneDraftJsonValue(child);
|
|
165
|
+
}
|
|
166
|
+
return cloned;
|
|
167
|
+
}
|
|
168
|
+
return value;
|
|
169
|
+
}
|
|
170
|
+
function cloneDraftData(draftData) {
|
|
171
|
+
return cloneDraftJsonValue(draftData);
|
|
172
|
+
}
|
|
173
|
+
function draftDataByteLength(draftData) {
|
|
174
|
+
return new TextEncoder().encode(JSON.stringify(draftData)).length;
|
|
175
|
+
}
|
|
176
|
+
function filterDraftData(draftData, fields) {
|
|
177
|
+
const filtered = {};
|
|
178
|
+
for (const field of fields) {
|
|
179
|
+
if (!Object.prototype.hasOwnProperty.call(draftData, field))
|
|
180
|
+
continue;
|
|
181
|
+
filtered[field] = cloneDraftJsonValue(draftData[field]);
|
|
182
|
+
}
|
|
183
|
+
return filtered;
|
|
184
|
+
}
|
|
185
|
+
function normalizeMaxDraftBytes(value) {
|
|
186
|
+
if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0) {
|
|
187
|
+
return DEFAULT_MAX_DRAFT_BYTES;
|
|
188
|
+
}
|
|
189
|
+
return Math.floor(value);
|
|
190
|
+
}
|
|
191
|
+
export function createWorkspaceTabStateAdapter(options) {
|
|
192
|
+
const tenantId = options.tenantId;
|
|
193
|
+
const storage = options.storage ?? null;
|
|
194
|
+
const keyPrefix = options.keyPrefix ?? DEFAULT_KEY_PREFIX;
|
|
195
|
+
const tenantScoped = typeof tenantId === 'string' && tenantId.trim().length > 0;
|
|
196
|
+
const rawUserId = typeof options.userId === 'string' ? options.userId : '';
|
|
197
|
+
// 显式身份(包括字面量 anonymous)与未登录 fallback 是两种作用域,不能共用
|
|
198
|
+
// 存储键,也不能让显式身份回退读取旧的无 userId 视图键。身份本身保留原始
|
|
199
|
+
// 字符串,只用 trim 判断是否为空,避免把 `x` 与 ` x ` 合并成同一用户。
|
|
200
|
+
const explicitIdentity = rawUserId.trim().length > 0;
|
|
201
|
+
const configuredUserId = explicitIdentity ? rawUserId : undefined;
|
|
202
|
+
const effectiveUserId = configuredUserId ?? ANONYMOUS_USER_ID;
|
|
203
|
+
// 复制调用方白名单,防止适配器创建后外部 push/splice 改变保护范围。
|
|
204
|
+
const draftFields = options.draftFields === undefined ? undefined : [...options.draftFields];
|
|
205
|
+
const draftAllowlistInvalid = draftFields !== undefined &&
|
|
206
|
+
draftFields.some((field) => typeof field !== 'string' || FORBIDDEN_DRAFT_KEYS.has(field));
|
|
207
|
+
const maxDraftBytes = normalizeMaxDraftBytes(options.maxDraftBytes);
|
|
208
|
+
const draftDataEnabled = configuredUserId !== undefined &&
|
|
209
|
+
tenantScoped &&
|
|
210
|
+
draftFields !== undefined &&
|
|
211
|
+
storage !== null &&
|
|
212
|
+
!draftAllowlistInvalid;
|
|
213
|
+
const memory = new Map();
|
|
214
|
+
const tombstones = new Set();
|
|
215
|
+
// 清理失败后,本实例显式 capture/markDraft 产生的新内存快照可以优先于同一
|
|
216
|
+
// 个共享关闭标记;只有在对应 storage 写入并回读确认成功后,才退休该跨实例阻断。
|
|
217
|
+
// 记录标记身份而不是布尔值,后续新的 clear 会替换标记,旧的内存优先权随之失效。
|
|
218
|
+
const memoryOverrides = new Map();
|
|
219
|
+
const storageTombstones = storage ? sharedTombstonesFor(storage) : null;
|
|
220
|
+
let disposed = false;
|
|
221
|
+
let lastDraftDataIssue;
|
|
222
|
+
function memoryKey(tabId) {
|
|
223
|
+
return `${encodeURIComponent(tenantId)}::${encodeURIComponent(tabId)}`;
|
|
224
|
+
}
|
|
225
|
+
function storageKeys(tabId) {
|
|
226
|
+
const prefixes = `${keyPrefix}:v${SNAPSHOT_VERSION}:${encodeURIComponent(tenantId)}`;
|
|
227
|
+
// 显式身份统一放进 `user:` 命名空间,避免任何字面量(包括 anonymous、
|
|
228
|
+
// 含分隔符或空白的 id)与未登录 fallback 的键发生碰撞;显式身份只写自己
|
|
229
|
+
// 的键,不参与旧键回退。
|
|
230
|
+
const primary = explicitIdentity
|
|
231
|
+
? `${prefixes}:user:${encodeURIComponent(configuredUserId ?? '')}:${encodeURIComponent(tabId)}`
|
|
232
|
+
: `${prefixes}:${encodeURIComponent(effectiveUserId)}:${encodeURIComponent(tabId)}`;
|
|
233
|
+
if (explicitIdentity)
|
|
234
|
+
return [primary];
|
|
235
|
+
// 旧版本的无 userId 快照使用 tenant + tab 两级键;保留只读回退,写入仍走
|
|
236
|
+
// 包含 user 的新键,避免新草稿数据落回旧键。
|
|
237
|
+
const legacy = `${prefixes}:${encodeURIComponent(tabId)}`;
|
|
238
|
+
return [primary, legacy];
|
|
239
|
+
}
|
|
240
|
+
function barrierState(tabId) {
|
|
241
|
+
const key = memoryKey(tabId);
|
|
242
|
+
const keys = storageKeys(tabId);
|
|
243
|
+
const barriers = keys
|
|
244
|
+
.map((storageKey) => storageTombstones?.get(storageKey))
|
|
245
|
+
.filter((barrier) => barrier !== undefined);
|
|
246
|
+
const overrides = memoryOverrides.get(key);
|
|
247
|
+
const overridden = barriers.length > 0 && barriers.every((barrier) => overrides?.has(barrier) === true);
|
|
248
|
+
return {
|
|
249
|
+
key,
|
|
250
|
+
keys,
|
|
251
|
+
barriers,
|
|
252
|
+
sharedBlocked: barriers.length > 0,
|
|
253
|
+
overridden,
|
|
254
|
+
blocked: tombstones.has(key) || (barriers.length > 0 && !overridden),
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
function readEnvelope(raw, tab) {
|
|
258
|
+
try {
|
|
259
|
+
const value = JSON.parse(raw);
|
|
260
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
261
|
+
return 'invalid';
|
|
262
|
+
const record = value;
|
|
263
|
+
// 关闭标记不是快照:无论旧快照是否仍残留,都不能再恢复已关闭标签。
|
|
264
|
+
if (record['closed'] === true)
|
|
265
|
+
return 'closed';
|
|
266
|
+
// 显式身份必须精确匹配 envelope.userId,缺失也不能按匿名视图接受;
|
|
267
|
+
// 未登录 fallback 仅接受自身匿名信封或旧版无 userId 视图信封。
|
|
268
|
+
const envelopeUserId = record['userId'];
|
|
269
|
+
const userIdValid = explicitIdentity
|
|
270
|
+
? envelopeUserId === configuredUserId
|
|
271
|
+
: envelopeUserId === undefined || envelopeUserId === ANONYMOUS_USER_ID;
|
|
272
|
+
// 持久化读取不做宽容归一化:任何字段错误都按无快照处理,尤其不能把
|
|
273
|
+
// `draft: "true"` 洗成 false 而静默丢失草稿保护。
|
|
274
|
+
if (record['version'] !== SNAPSHOT_VERSION ||
|
|
275
|
+
record['tenantId'] !== tenantId ||
|
|
276
|
+
record['tabId'] !== tab.id ||
|
|
277
|
+
record['path'] !== tab.path ||
|
|
278
|
+
!userIdValid ||
|
|
279
|
+
typeof record['query'] !== 'string' ||
|
|
280
|
+
!isValidScroll(record['scrollTop']) ||
|
|
281
|
+
!isValidScroll(record['scrollLeft']) ||
|
|
282
|
+
typeof record['draft'] !== 'boolean') {
|
|
283
|
+
return 'invalid';
|
|
284
|
+
}
|
|
285
|
+
const state = {
|
|
286
|
+
query: record['query'],
|
|
287
|
+
scrollTop: record['scrollTop'],
|
|
288
|
+
scrollLeft: record['scrollLeft'],
|
|
289
|
+
draft: record['draft'],
|
|
290
|
+
};
|
|
291
|
+
if (Object.prototype.hasOwnProperty.call(record, 'draftData')) {
|
|
292
|
+
const persistedDraft = record['draftData'];
|
|
293
|
+
// 已持久化的草稿必须整体合法;非法载荷让整条快照失效,不能只丢弃草稿
|
|
294
|
+
// 后继续恢复 query/scroll,否则会掩盖损坏或被篡改的数据。
|
|
295
|
+
if (!isValidDraftJsonObject(persistedDraft)) {
|
|
296
|
+
lastDraftDataIssue = 'invalid';
|
|
297
|
+
return 'invalid';
|
|
298
|
+
}
|
|
299
|
+
// 恢复时对过滤前的完整载荷再次执行字节上限校验,避免只校验白名单
|
|
300
|
+
// 子集而让被篡改/膨胀的信封通过。
|
|
301
|
+
if (draftDataByteLength(persistedDraft) > maxDraftBytes) {
|
|
302
|
+
lastDraftDataIssue = 'too-large';
|
|
303
|
+
return 'invalid';
|
|
304
|
+
}
|
|
305
|
+
// 危险白名单即使已经持久化也拒绝恢复,不能让过滤阶段把危险键
|
|
306
|
+
// 写进结果对象。
|
|
307
|
+
if (draftAllowlistInvalid) {
|
|
308
|
+
lastDraftDataIssue = 'invalid';
|
|
309
|
+
return 'invalid';
|
|
310
|
+
}
|
|
311
|
+
if (draftDataEnabled && draftFields !== undefined && draftFields.length > 0) {
|
|
312
|
+
state.draftData = filterDraftData(persistedDraft, draftFields);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return state;
|
|
316
|
+
}
|
|
317
|
+
catch {
|
|
318
|
+
// 损坏、被篡改或 storage 不可用时按未保存处理,不阻断标签切换。
|
|
319
|
+
return 'invalid';
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
function readPersisted(tab) {
|
|
323
|
+
if (!storage)
|
|
324
|
+
return undefined;
|
|
325
|
+
for (const storageKey of storageKeys(tab.id)) {
|
|
326
|
+
let raw;
|
|
327
|
+
try {
|
|
328
|
+
raw = storage.getItem(storageKey);
|
|
329
|
+
}
|
|
330
|
+
catch {
|
|
331
|
+
return undefined;
|
|
332
|
+
}
|
|
333
|
+
if (raw === null)
|
|
334
|
+
continue;
|
|
335
|
+
const result = readEnvelope(raw, tab);
|
|
336
|
+
// 新键存在但损坏/关闭时不能回退旧键,否则清理或损坏的标识会被旧快照绕过。
|
|
337
|
+
if (result === 'invalid' || result === 'closed')
|
|
338
|
+
return undefined;
|
|
339
|
+
return result;
|
|
340
|
+
}
|
|
341
|
+
return undefined;
|
|
342
|
+
}
|
|
343
|
+
function writePersisted(tab, state) {
|
|
344
|
+
if (!storage)
|
|
345
|
+
return true;
|
|
346
|
+
try {
|
|
347
|
+
const [key] = storageKeys(tab.id);
|
|
348
|
+
if (key === undefined)
|
|
349
|
+
return false;
|
|
350
|
+
const serialized = JSON.stringify({
|
|
351
|
+
version: SNAPSHOT_VERSION,
|
|
352
|
+
tenantId,
|
|
353
|
+
userId: effectiveUserId,
|
|
354
|
+
tabId: tab.id,
|
|
355
|
+
path: tab.path,
|
|
356
|
+
...state,
|
|
357
|
+
});
|
|
358
|
+
storage.setItem(key, serialized);
|
|
359
|
+
// setItem 不抛错不等于真的写入:必须回读确认,避免静默 no-op 让旧快照复活。
|
|
360
|
+
return storage.getItem(key) === serialized;
|
|
361
|
+
}
|
|
362
|
+
catch {
|
|
363
|
+
// 配额或隐私模式失败时保留内存快照,调用方仍可在当前会话恢复。
|
|
364
|
+
return false;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
function currentEntry(tab) {
|
|
368
|
+
const barrier = barrierState(tab.id);
|
|
369
|
+
if (tombstones.has(barrier.key))
|
|
370
|
+
return undefined;
|
|
371
|
+
const cached = memory.get(barrier.key);
|
|
372
|
+
// 该 id 已有内存快照时,以内存为准:path 不匹配就视为未保存,绝不回读
|
|
373
|
+
// 持久化旧路径来覆盖内存中的新快照(例如持久化写失败后的会话)。
|
|
374
|
+
if (cached) {
|
|
375
|
+
if (cached.path !== tab.path)
|
|
376
|
+
return undefined;
|
|
377
|
+
// 其它实例清理失败留下的共享阻断仍优先,除非本实例已显式接管新内存。
|
|
378
|
+
if (barrier.sharedBlocked && !barrier.overridden)
|
|
379
|
+
return undefined;
|
|
380
|
+
return cached;
|
|
381
|
+
}
|
|
382
|
+
if (barrier.sharedBlocked)
|
|
383
|
+
return undefined;
|
|
384
|
+
const persisted = readPersisted(tab);
|
|
385
|
+
return persisted ? { path: tab.path, state: persisted } : undefined;
|
|
386
|
+
}
|
|
387
|
+
function settlePersistence(barrier, persisted) {
|
|
388
|
+
if (persisted) {
|
|
389
|
+
// 只有新快照确实写入并回读一致,才能退休共享关闭标记。
|
|
390
|
+
for (const storageKey of barrier.keys)
|
|
391
|
+
storageTombstones?.delete(storageKey);
|
|
392
|
+
memoryOverrides.delete(barrier.key);
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
if (barrier.sharedBlocked) {
|
|
396
|
+
// 持久化未确认:本地新内存可用,但跨实例阻断继续保留。
|
|
397
|
+
const overrides = memoryOverrides.get(barrier.key) ?? new Set();
|
|
398
|
+
for (const sharedBarrier of barrier.barriers)
|
|
399
|
+
overrides.add(sharedBarrier);
|
|
400
|
+
memoryOverrides.set(barrier.key, overrides);
|
|
401
|
+
}
|
|
402
|
+
else {
|
|
403
|
+
memoryOverrides.delete(barrier.key);
|
|
404
|
+
}
|
|
405
|
+
// 草稿能力一旦启用,任何持久化失败都可能丢失草稿或清除结果,必须让调用方
|
|
406
|
+
// 通过诊断看到;不能只在本次状态仍带 draftData 时才报告。
|
|
407
|
+
if (draftDataEnabled)
|
|
408
|
+
lastDraftDataIssue = 'storage-failed';
|
|
409
|
+
}
|
|
410
|
+
function resolveDraftDataInput(input, previous) {
|
|
411
|
+
const hasDraftDataProperty = input !== undefined &&
|
|
412
|
+
Object.prototype.hasOwnProperty.call(input, 'draftData');
|
|
413
|
+
// 显式传入 draftData: undefined 表示清除;完全省略才沿用旧草稿。
|
|
414
|
+
if (hasDraftDataProperty && input.draftData === undefined)
|
|
415
|
+
return {};
|
|
416
|
+
if (!hasDraftDataProperty) {
|
|
417
|
+
return draftDataEnabled && previous?.draftData !== undefined
|
|
418
|
+
? { draftData: cloneDraftData(previous.draftData) }
|
|
419
|
+
: {};
|
|
420
|
+
}
|
|
421
|
+
if (draftAllowlistInvalid)
|
|
422
|
+
return { issue: 'invalid' };
|
|
423
|
+
if (!draftDataEnabled)
|
|
424
|
+
return { issue: 'disabled' };
|
|
425
|
+
if (draftFields === undefined || draftFields.length === 0) {
|
|
426
|
+
return { issue: 'empty-allowlist' };
|
|
427
|
+
}
|
|
428
|
+
if (draftFields.some((field) => FORBIDDEN_DRAFT_KEYS.has(field))) {
|
|
429
|
+
return { issue: 'invalid' };
|
|
430
|
+
}
|
|
431
|
+
const source = input.draftData;
|
|
432
|
+
if (!isPlainDraftObject(source))
|
|
433
|
+
return { issue: 'invalid' };
|
|
434
|
+
const filtered = {};
|
|
435
|
+
// 整个候选草稿共享一份节点/深度预算,不能按字段各自重置。
|
|
436
|
+
// 根对象本身也占用预算,字段值从 depth 1 开始,与 restore 读取持久化载荷
|
|
437
|
+
// 时的深度/节点计算保持一致,避免 capture 比 restore 多放行一层。
|
|
438
|
+
const budget = { stack: new WeakSet(), nodes: 1 };
|
|
439
|
+
for (const field of draftFields) {
|
|
440
|
+
const descriptor = Object.getOwnPropertyDescriptor(source, field);
|
|
441
|
+
// 白名单外字段直接丢弃;白名单内字段必须存在普通数据属性且值可序列化。
|
|
442
|
+
if (!descriptor)
|
|
443
|
+
continue;
|
|
444
|
+
if (typeof descriptor.get === 'function' ||
|
|
445
|
+
typeof descriptor.set === 'function' ||
|
|
446
|
+
descriptor.enumerable !== true) {
|
|
447
|
+
return { issue: 'invalid' };
|
|
448
|
+
}
|
|
449
|
+
if (!validateDraftJsonValue(descriptor.value, budget, 1)) {
|
|
450
|
+
return { issue: 'invalid' };
|
|
451
|
+
}
|
|
452
|
+
filtered[field] = cloneDraftJsonValue(descriptor.value);
|
|
453
|
+
}
|
|
454
|
+
if (draftDataByteLength(filtered) > maxDraftBytes)
|
|
455
|
+
return { issue: 'too-large' };
|
|
456
|
+
return { draftData: filtered };
|
|
457
|
+
}
|
|
458
|
+
return {
|
|
459
|
+
capture(tab, state) {
|
|
460
|
+
const barrier = barrierState(tab.id);
|
|
461
|
+
const previous = disposed
|
|
462
|
+
? EMPTY_STATE
|
|
463
|
+
: barrier.blocked
|
|
464
|
+
? EMPTY_STATE
|
|
465
|
+
: currentEntry(tab)?.state;
|
|
466
|
+
const next = normalizeState(state, previous ?? EMPTY_STATE);
|
|
467
|
+
const draftResult = resolveDraftDataInput(state, previous);
|
|
468
|
+
lastDraftDataIssue = draftResult.issue;
|
|
469
|
+
// 保存/关闭草稿(draft:false)时同步清除草稿载荷,避免旧内容在后续
|
|
470
|
+
// capture 时复活;draftData: undefined 的显式清除路径已由 resolve 处理。
|
|
471
|
+
const explicitlyClean = state !== undefined &&
|
|
472
|
+
Object.prototype.hasOwnProperty.call(state, 'draft') &&
|
|
473
|
+
state.draft === false;
|
|
474
|
+
if (!explicitlyClean && draftResult.draftData !== undefined) {
|
|
475
|
+
next.draftData = draftResult.draftData;
|
|
476
|
+
}
|
|
477
|
+
if (disposed)
|
|
478
|
+
return cloneState(next);
|
|
479
|
+
// 重新 capture 表示标签被重新打开/接管,本实例旧 tombstone 不再阻止新快照。
|
|
480
|
+
tombstones.delete(barrier.key);
|
|
481
|
+
memory.set(barrier.key, { path: tab.path, state: next });
|
|
482
|
+
const persisted = writePersisted(tab, next);
|
|
483
|
+
settlePersistence(barrier, persisted);
|
|
484
|
+
return cloneState(next);
|
|
485
|
+
},
|
|
486
|
+
restore(tab) {
|
|
487
|
+
if (disposed)
|
|
488
|
+
return undefined;
|
|
489
|
+
lastDraftDataIssue = undefined;
|
|
490
|
+
const entry = currentEntry(tab);
|
|
491
|
+
if (!entry)
|
|
492
|
+
return undefined;
|
|
493
|
+
memory.set(memoryKey(tab.id), entry);
|
|
494
|
+
return cloneState(entry.state);
|
|
495
|
+
},
|
|
496
|
+
markDraft(tab, draft) {
|
|
497
|
+
if (disposed)
|
|
498
|
+
return;
|
|
499
|
+
const barrier = barrierState(tab.id);
|
|
500
|
+
const previous = barrier.blocked ? EMPTY_STATE : (currentEntry(tab)?.state ?? EMPTY_STATE);
|
|
501
|
+
const next = normalizeState({ ...previous, draft });
|
|
502
|
+
if (draft && previous.draftData !== undefined) {
|
|
503
|
+
next.draftData = cloneDraftData(previous.draftData);
|
|
504
|
+
}
|
|
505
|
+
lastDraftDataIssue = undefined;
|
|
506
|
+
tombstones.delete(barrier.key);
|
|
507
|
+
memory.set(barrier.key, { path: tab.path, state: next });
|
|
508
|
+
const persisted = writePersisted(tab, next);
|
|
509
|
+
settlePersistence(barrier, persisted);
|
|
510
|
+
},
|
|
511
|
+
hasDraft(tab) {
|
|
512
|
+
if (disposed)
|
|
513
|
+
return false;
|
|
514
|
+
return currentEntry(tab)?.state.draft ?? false;
|
|
515
|
+
},
|
|
516
|
+
clear(tab) {
|
|
517
|
+
if (disposed)
|
|
518
|
+
return { memoryCleared: false, storage: 'not-configured' };
|
|
519
|
+
const key = memoryKey(tab.id);
|
|
520
|
+
const persistedKeys = storageKeys(tab.id);
|
|
521
|
+
memory.delete(key);
|
|
522
|
+
memoryOverrides.delete(key);
|
|
523
|
+
// 先立 tombstone,即使 removeItem 抛错,同一实例也不能从 storage 回读旧标签。
|
|
524
|
+
tombstones.add(key);
|
|
525
|
+
if (!storage)
|
|
526
|
+
return { memoryCleared: true, storage: 'not-configured' };
|
|
527
|
+
// 同一运行时的其它 adapter 使用同一 storage 对象时也立即失效;capture
|
|
528
|
+
// 重新打开同 id/path 时会移除该标记并重新接管。
|
|
529
|
+
const barrier = {};
|
|
530
|
+
for (const persistedKey of persistedKeys)
|
|
531
|
+
storageTombstones?.set(persistedKey, barrier);
|
|
532
|
+
let allRemoved = true;
|
|
533
|
+
for (const persistedKey of persistedKeys) {
|
|
534
|
+
let exists;
|
|
535
|
+
try {
|
|
536
|
+
exists = storage.getItem(persistedKey) !== null;
|
|
537
|
+
}
|
|
538
|
+
catch {
|
|
539
|
+
// 读取失败时仍尝试删除,不能因为无法确认存在就跳过清理。
|
|
540
|
+
exists = true;
|
|
541
|
+
}
|
|
542
|
+
if (!exists)
|
|
543
|
+
continue;
|
|
544
|
+
let removed = false;
|
|
545
|
+
try {
|
|
546
|
+
storage.removeItem(persistedKey);
|
|
547
|
+
// removeItem 不抛错不等于真的删除;仍能读到旧值时必须按失败处理,
|
|
548
|
+
// 不能让新实例恢复已关闭标签。
|
|
549
|
+
removed = storage.getItem(persistedKey) === null;
|
|
550
|
+
}
|
|
551
|
+
catch {
|
|
552
|
+
// removeItem 抛错时 removed 保持 false,按失败处理。
|
|
553
|
+
}
|
|
554
|
+
if (!removed)
|
|
555
|
+
allRemoved = false;
|
|
556
|
+
}
|
|
557
|
+
if (allRemoved)
|
|
558
|
+
return { memoryCleared: true, storage: 'cleared' };
|
|
559
|
+
for (const persistedKey of persistedKeys) {
|
|
560
|
+
try {
|
|
561
|
+
// 删除失败或未生效时写入关闭信封,供刷新后的新实例识别,而不是回读旧快照。
|
|
562
|
+
storage.setItem(persistedKey, JSON.stringify({
|
|
563
|
+
version: SNAPSHOT_VERSION,
|
|
564
|
+
tenantId,
|
|
565
|
+
tabId: tab.id,
|
|
566
|
+
closed: true,
|
|
567
|
+
}));
|
|
568
|
+
}
|
|
569
|
+
catch {
|
|
570
|
+
// 关闭标记也写不进去时仍返回 failed;同一运行时共享 tombstone 继续阻止
|
|
571
|
+
// 同 storage 对象的新 adapter。跨刷新风险由调用方按 failed 处理。
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
return { memoryCleared: true, storage: 'failed' };
|
|
575
|
+
},
|
|
576
|
+
dispose() {
|
|
577
|
+
disposed = true;
|
|
578
|
+
memory.clear();
|
|
579
|
+
tombstones.clear();
|
|
580
|
+
memoryOverrides.clear();
|
|
581
|
+
lastDraftDataIssue = undefined;
|
|
582
|
+
},
|
|
583
|
+
getLastDraftDataIssue() {
|
|
584
|
+
return lastDraftDataIssue;
|
|
585
|
+
},
|
|
586
|
+
};
|
|
587
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -20,6 +20,8 @@ export type { PivotCache } from '@svadmin/core/pivot';
|
|
|
20
20
|
export { decodeSavedListViewMutationResult, decodeSavedListViewRemoveResult, decodeSavedListViewAccess, } from './components/saved-list-views.js';
|
|
21
21
|
export type { SavedListView, SavedListViewState, SavedListViewProvider, SavedListViewMutation, SavedListViewMutationSuccess, SavedListViewMutationConflict, SavedListViewRemoveSuccess, ListPreferenceScope, SavedListViewAccess, SavedListViewAccessMode, SavedListViewSubject, } from './components/saved-list-views.js';
|
|
22
22
|
export type { BatchSelection } from './components/table-contract.js';
|
|
23
|
+
export { cloneListFilter, cloneListStateSnapshot } from './components/list-state.svelte.js';
|
|
24
|
+
export type { ListStateSnapshot } from './components/list-state.svelte.js';
|
|
23
25
|
export { default as AutoForm } from './components/AutoForm.svelte';
|
|
24
26
|
export { default as ShowPage } from './components/ShowPage.svelte';
|
|
25
27
|
export { default as ConfirmDialog } from './components/ConfirmDialog.svelte';
|
|
@@ -93,6 +95,7 @@ export { default as ColumnSettings } from './components/ColumnSettings.svelte';
|
|
|
93
95
|
export type { ColumnItem } from './components/ColumnSettings.svelte';
|
|
94
96
|
export { default as UnsavedChangesPrompt } from './components/UnsavedChangesPrompt.svelte';
|
|
95
97
|
export { default as ImportWizard } from './components/ImportWizard.svelte';
|
|
98
|
+
export type { ImportPreviewFailure, ImportPreviewResult } from '@svadmin/core';
|
|
96
99
|
export { default as ColumnHeaderFilter } from './components/ColumnHeaderFilter.svelte';
|
|
97
100
|
export { default as TreeTable } from './components/TreeTable.svelte';
|
|
98
101
|
export type { TreeTableColumn } from './components/TreeTable.svelte';
|
|
@@ -111,6 +114,7 @@ export { default as DrawerForm } from './components/DrawerForm.svelte';
|
|
|
111
114
|
export { default as TableSummary } from './components/TableSummary.svelte';
|
|
112
115
|
export type { TableSummaryColumn, AggregationType } from './components/TableSummary.svelte';
|
|
113
116
|
export { default as VersionDiffViewer } from './components/VersionDiffViewer.svelte';
|
|
117
|
+
export type { DetailWorkspaceSectionsConfig, DetailWorkspaceSection, DetailWorkspaceProvider, DetailWorkspaceScope, DetailWorkspaceVersionData } from './components/detail-workspace.js';
|
|
114
118
|
export { default as VirtualTable } from './components/VirtualTable.svelte';
|
|
115
119
|
export type { VirtualTableColumn } from './components/VirtualTable.svelte';
|
|
116
120
|
export { default as EditableTable } from './components/EditableTable.svelte';
|
|
@@ -129,6 +133,9 @@ export type { PresenceUser } from './components/PresenceAvatarGroup.svelte';
|
|
|
129
133
|
export { default as PrintableBill } from './components/PrintableBill.svelte';
|
|
130
134
|
export type { BillItem } from './components/PrintableBill.svelte';
|
|
131
135
|
export { default as JsonSchemaForm } from './components/JsonSchemaForm.svelte';
|
|
136
|
+
export type { JsonSchemaFormSchema } from './components/json-schema-form-state.js';
|
|
137
|
+
export type { SchemaFormReferenceDocuments } from './components/enterprise/schema-form-references.js';
|
|
138
|
+
export type { SchemaFormIssue, SchemaIssueCode } from './components/enterprise/json-schema-form.js';
|
|
132
139
|
export { default as MentionsInput } from './components/MentionsInput.svelte';
|
|
133
140
|
export type { MentionOption } from './components/MentionsInput.svelte';
|
|
134
141
|
export { default as KanbanBoard } from './components/KanbanBoard.svelte';
|
|
@@ -136,7 +143,9 @@ export type { KanbanCard, KanbanColumn } from './components/KanbanBoard.svelte';
|
|
|
136
143
|
export { default as PivotTable } from './components/PivotTable.svelte';
|
|
137
144
|
export type { AggregationFn } from './components/PivotTable.svelte';
|
|
138
145
|
export { default as MultiTabKeepAlive } from './components/MultiTabKeepAlive.svelte';
|
|
139
|
-
export type { WorkspaceTab } from './components/MultiTabKeepAlive.svelte';
|
|
146
|
+
export type { WorkspaceTab, WorkspaceTabLimitReachedDetail, WorkspaceTabOpenRejectReason, WorkspaceTabOpenResult, WorkspaceTabOpenStatus, } from './components/MultiTabKeepAlive.svelte';
|
|
147
|
+
export { createWorkspaceTabStateAdapter } from './components/workspace-tab-state.js';
|
|
148
|
+
export type { WorkspaceDraftDataIssue, WorkspaceDraftJsonObject, WorkspaceDraftJsonValue, WorkspaceTabClearResult, WorkspaceTabIdentity, WorkspaceTabRef, WorkspaceTabStateAdapter, WorkspaceTabStateAdapterOptions, WorkspaceTabStateStorage, WorkspaceTabViewState, } from './components/workspace-tab-state.js';
|
|
140
149
|
export { default as GanttChart } from './components/GanttChart.svelte';
|
|
141
150
|
export type { GanttTask } from './components/GanttChart.svelte';
|
|
142
151
|
export { default as CanvasAnnotation } from './components/CanvasAnnotation.svelte';
|
|
@@ -152,11 +161,14 @@ export { default as OfflineSyncBanner } from './components/OfflineSyncBanner.sve
|
|
|
152
161
|
export type { PendingMutation } from './components/OfflineSyncBanner.svelte';
|
|
153
162
|
export { default as InfiniteList } from './components/InfiniteList.svelte';
|
|
154
163
|
export { default as ComboboxField } from './components/ComboboxField.svelte';
|
|
164
|
+
export { default as AutoComplete } from './components/AutoComplete.svelte';
|
|
155
165
|
export { default as RelationPicker } from './components/RelationPicker.svelte';
|
|
156
166
|
export type { RelationPickerOption } from './components/RelationPicker.svelte';
|
|
157
167
|
export { default as PasswordInput } from './components/PasswordInput.svelte';
|
|
158
168
|
export { default as NumericInput } from './components/NumericInput.svelte';
|
|
159
169
|
export { default as NumberInput } from './components/NumberInput.svelte';
|
|
170
|
+
export { NUMERIC_INPUT_LOCALES, isNumericInputLocale } from './components/numeric-format.js';
|
|
171
|
+
export type { NumericInputLocale, NumericParseResult } from './components/numeric-format.js';
|
|
160
172
|
export { default as MoneyInput } from './components/MoneyInput.svelte';
|
|
161
173
|
export { default as PercentInput } from './components/PercentInput.svelte';
|
|
162
174
|
export { default as DateTimeInput } from './components/DateTimeInput.svelte';
|
package/dist/index.js
CHANGED
|
@@ -15,6 +15,7 @@ export { default as ColorPicker } from './components/ColorPicker.svelte';
|
|
|
15
15
|
export { default as Rate } from './components/Rate.svelte';
|
|
16
16
|
export { default as PivotExportButton } from './components/PivotExportButton.svelte';
|
|
17
17
|
export { decodeSavedListViewMutationResult, decodeSavedListViewRemoveResult, decodeSavedListViewAccess, } from './components/saved-list-views.js';
|
|
18
|
+
export { cloneListFilter, cloneListStateSnapshot } from './components/list-state.svelte.js';
|
|
18
19
|
export { default as AutoForm } from './components/AutoForm.svelte';
|
|
19
20
|
export { default as ShowPage } from './components/ShowPage.svelte';
|
|
20
21
|
export { default as ConfirmDialog } from './components/ConfirmDialog.svelte';
|
|
@@ -105,6 +106,7 @@ export { default as MentionsInput } from './components/MentionsInput.svelte';
|
|
|
105
106
|
export { default as KanbanBoard } from './components/KanbanBoard.svelte';
|
|
106
107
|
export { default as PivotTable } from './components/PivotTable.svelte';
|
|
107
108
|
export { default as MultiTabKeepAlive } from './components/MultiTabKeepAlive.svelte';
|
|
109
|
+
export { createWorkspaceTabStateAdapter } from './components/workspace-tab-state.js';
|
|
108
110
|
export { default as GanttChart } from './components/GanttChart.svelte';
|
|
109
111
|
export { default as CanvasAnnotation } from './components/CanvasAnnotation.svelte';
|
|
110
112
|
export { default as SignaturePad } from './components/SignaturePad.svelte';
|
|
@@ -114,10 +116,12 @@ export { default as DecisionTable } from './components/DecisionTable.svelte';
|
|
|
114
116
|
export { default as OfflineSyncBanner } from './components/OfflineSyncBanner.svelte';
|
|
115
117
|
export { default as InfiniteList } from './components/InfiniteList.svelte';
|
|
116
118
|
export { default as ComboboxField } from './components/ComboboxField.svelte';
|
|
119
|
+
export { default as AutoComplete } from './components/AutoComplete.svelte';
|
|
117
120
|
export { default as RelationPicker } from './components/RelationPicker.svelte';
|
|
118
121
|
export { default as PasswordInput } from './components/PasswordInput.svelte';
|
|
119
122
|
export { default as NumericInput } from './components/NumericInput.svelte';
|
|
120
123
|
export { default as NumberInput } from './components/NumberInput.svelte';
|
|
124
|
+
export { NUMERIC_INPUT_LOCALES, isNumericInputLocale } from './components/numeric-format.js';
|
|
121
125
|
export { default as MoneyInput } from './components/MoneyInput.svelte';
|
|
122
126
|
export { default as PercentInput } from './components/PercentInput.svelte';
|
|
123
127
|
export { default as DateTimeInput } from './components/DateTimeInput.svelte';
|