@svadmin/ui 0.72.0 → 0.73.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/README.md +40 -25
- package/THIRD_PARTY_NOTICES.md +52 -0
- package/dist/app.css +7694 -5117
- package/dist/app.theme.css +7645 -5119
- package/dist/class-properties.generated.d.ts +1 -0
- package/dist/class-properties.generated.js +2 -0
- package/dist/classnames.d.ts +8 -0
- package/dist/classnames.js +26 -0
- package/dist/components/AutoForm.svelte +20 -6
- package/dist/components/AutoForm.svelte.d.ts +2 -0
- package/dist/components/AutoTable.svelte +249 -9
- package/dist/components/AutoTable.svelte.d.ts +5 -0
- package/dist/components/BoundRecordDetailDrawer.svelte +8 -2
- package/dist/components/BoundRecordDetailDrawer.svelte.d.ts +2 -0
- package/dist/components/ComboboxField.svelte +31 -9
- package/dist/components/ComboboxField.svelte.d.ts +3 -2
- package/dist/components/CreatePage.svelte +4 -0
- package/dist/components/CreatePage.svelte.d.ts +2 -0
- package/dist/components/DateRangeInput.svelte +75 -0
- package/dist/components/DateRangeInput.svelte.d.ts +25 -0
- package/dist/components/DateTimeInput.svelte +65 -0
- package/dist/components/DateTimeInput.svelte.d.ts +19 -0
- package/dist/components/DevTools.svelte +2 -2
- package/dist/components/EditPage.svelte +4 -0
- package/dist/components/EditPage.svelte.d.ts +2 -0
- package/dist/components/FieldRenderer.svelte +90 -18
- package/dist/components/FileUpload.svelte +250 -0
- package/dist/components/FileUpload.svelte.d.ts +32 -0
- package/dist/components/FilterBuilder.svelte +236 -279
- package/dist/components/FilterBuilder.svelte.d.ts +3 -0
- package/dist/components/ImportWizard.svelte +181 -94
- package/dist/components/JsonSchemaForm.svelte +255 -99
- package/dist/components/JsonSchemaForm.svelte.d.ts +12 -1
- package/dist/components/Layout.svelte +1 -2
- package/dist/components/LazyTaskQueueDrawer.svelte.d.ts +1 -1
- package/dist/components/ListPage.svelte +4 -0
- package/dist/components/ListPage.svelte.d.ts +2 -0
- package/dist/components/LoginPage.svelte +1 -1
- package/dist/components/MoneyInput.svelte +25 -0
- package/dist/components/MoneyInput.svelte.d.ts +20 -0
- package/dist/components/NumberInput.svelte +25 -0
- package/dist/components/NumberInput.svelte.d.ts +19 -0
- package/dist/components/NumericInput.svelte +110 -0
- package/dist/components/NumericInput.svelte.d.ts +23 -0
- package/dist/components/PercentInput.svelte +25 -0
- package/dist/components/PercentInput.svelte.d.ts +20 -0
- package/dist/components/QuickEditDrawer.svelte +4 -1
- package/dist/components/QuickEditDrawer.svelte.d.ts +2 -0
- package/dist/components/RecordDetailDrawer.svelte +4 -2
- package/dist/components/RecordDetailDrawer.svelte.d.ts +2 -0
- package/dist/components/ResourceOperationsPage.svelte +25 -19
- package/dist/components/ResourceOperationsPage.svelte.d.ts +5 -1
- package/dist/components/ShowInferencer.svelte +4 -2
- package/dist/components/ShowInferencer.svelte.d.ts +2 -0
- package/dist/components/ShowPage.svelte +7 -3
- package/dist/components/ShowPage.svelte.d.ts +2 -0
- package/dist/components/Sidebar.svelte +5 -5
- package/dist/components/SpreadsheetView.svelte +81 -286
- package/dist/components/StepsForm.svelte +5 -5
- package/dist/components/SvarAutoTable.svelte +39 -0
- package/dist/components/SvarAutoTable.svelte.d.ts +13 -0
- package/dist/components/SvarDataGrid.svelte +362 -0
- package/dist/components/SvarDataGrid.svelte.d.ts +57 -0
- package/dist/components/SvarGridCell.svelte +10 -0
- package/dist/components/SvarGridCell.svelte.d.ts +7 -0
- package/dist/components/SvarResourceRead.svelte +50 -0
- package/dist/components/SvarResourceRead.svelte.d.ts +23 -0
- package/dist/components/SvarResourceTable.svelte +63 -0
- package/dist/components/SvarResourceTable.svelte.d.ts +42 -0
- package/dist/components/SvarResourceTableView.svelte +364 -0
- package/dist/components/SvarResourceTableView.svelte.d.ts +4 -0
- package/dist/components/TaskQueueDrawer.svelte +72 -16
- package/dist/components/TreeSelect.svelte +208 -28
- package/dist/components/TreeSelect.svelte.d.ts +3 -1
- package/dist/components/auto-table-grid.d.ts +26 -0
- package/dist/components/auto-table-grid.js +1 -0
- package/dist/components/content/ContentPageHeader.svelte +12 -9
- package/dist/components/content/ContentPageShell.svelte +6 -9
- package/dist/components/content/FileBrowser.svelte +35 -0
- package/dist/components/content/FileBrowser.svelte.d.ts +14 -0
- package/dist/components/content/MetricBlock.svelte +10 -13
- package/dist/components/content/NetworkTable.svelte +3 -3
- package/dist/components/content/NetworkTable.svelte.d.ts +1 -0
- package/dist/components/content/PageToolbar.svelte +6 -10
- package/dist/components/content/SectionHeader.svelte +8 -13
- package/dist/components/content/SettingsFieldRow.svelte +9 -23
- package/dist/components/content/SettingsGroup.svelte +11 -27
- package/dist/components/content/StatusBadge.svelte +3 -8
- package/dist/components/content/WorkspaceLayout.svelte +8 -12
- package/dist/components/enterprise/filter-tree.d.ts +48 -0
- package/dist/components/enterprise/filter-tree.js +224 -0
- package/dist/components/enterprise/json-schema-form.d.ts +39 -0
- package/dist/components/enterprise/json-schema-form.js +195 -0
- package/dist/components/enterprise/recursive-schema-form.d.ts +30 -0
- package/dist/components/enterprise/recursive-schema-form.js +196 -0
- package/dist/components/enterprise/spreadsheet-formula.d.ts +12 -0
- package/dist/components/enterprise/spreadsheet-formula.js +245 -0
- package/dist/components/fieldComponentMap.js +5 -0
- package/dist/components/fields/DateField.svelte +4 -3
- package/dist/components/fields/date-field-value.d.ts +2 -0
- package/dist/components/fields/date-field-value.js +13 -0
- package/dist/components/json-schema-form-state.d.ts +37 -0
- package/dist/components/json-schema-form-state.js +168 -0
- package/dist/components/record-detail.svelte.d.ts +3 -0
- package/dist/components/record-detail.svelte.js +10 -4
- package/dist/components/svar-grid-cells.d.ts +14 -0
- package/dist/components/svar-grid-cells.js +2 -0
- package/dist/components/svar-grid-contract.d.ts +80 -0
- package/dist/components/svar-grid-contract.js +159 -0
- package/dist/components/svar-grid-interactions.d.ts +54 -0
- package/dist/components/svar-grid-interactions.js +174 -0
- package/dist/components/svar-grid-loading.d.ts +40 -0
- package/dist/components/svar-grid-loading.js +180 -0
- package/dist/components/svar-grid-operations.d.ts +37 -0
- package/dist/components/svar-grid-operations.js +186 -0
- package/dist/components/svar-grid-panes.d.ts +34 -0
- package/dist/components/svar-grid-panes.js +66 -0
- package/dist/components/svar-grid-resource-loading.d.ts +18 -0
- package/dist/components/svar-grid-resource-loading.js +68 -0
- package/dist/components/svar-grid-resource-session.svelte.d.ts +29 -0
- package/dist/components/svar-grid-resource-session.svelte.js +61 -0
- package/dist/components/ui/badge/badge-variants.js +3 -1
- package/dist/components/ui/button/button-variants.js +5 -2
- package/dist/components/ui/input/input.svelte +1 -4
- package/dist/components/ui/textarea/textarea.svelte +2 -1
- package/dist/components.css +3079 -0
- package/dist/design-contract.d.ts +13 -0
- package/dist/design-contract.js +11 -0
- package/dist/design-system.d.ts +15 -0
- package/dist/design-system.js +7 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +9 -0
- package/dist/page-chrome.css +12 -49
- package/dist/recipes.d.ts +4 -0
- package/dist/recipes.js +4 -0
- package/dist/rendering/index.d.ts +87 -0
- package/dist/rendering/index.js +117 -0
- package/dist/styled-system/css/conditions.js +36 -0
- package/dist/styled-system/css/css.d.ts +22 -0
- package/dist/styled-system/css/css.js +45 -0
- package/dist/styled-system/css/cva.d.ts +6 -0
- package/dist/styled-system/css/cva.js +87 -0
- package/dist/styled-system/css/cx.d.ts +5 -0
- package/dist/styled-system/css/cx.js +15 -0
- package/dist/styled-system/css/index.d.ts +5 -0
- package/dist/styled-system/css/index.js +4 -0
- package/dist/styled-system/css/sva.d.ts +4 -0
- package/dist/styled-system/css/sva.js +46 -0
- package/dist/styled-system/helpers.js +316 -0
- package/dist/styled-system/patterns/aspect-ratio.d.ts +20 -0
- package/dist/styled-system/patterns/aspect-ratio.js +38 -0
- package/dist/styled-system/patterns/bleed.d.ts +21 -0
- package/dist/styled-system/patterns/bleed.js +24 -0
- package/dist/styled-system/patterns/box.d.ts +20 -0
- package/dist/styled-system/patterns/box.js +15 -0
- package/dist/styled-system/patterns/center.d.ts +20 -0
- package/dist/styled-system/patterns/center.js +21 -0
- package/dist/styled-system/patterns/circle.d.ts +20 -0
- package/dist/styled-system/patterns/circle.js +25 -0
- package/dist/styled-system/patterns/container.d.ts +20 -0
- package/dist/styled-system/patterns/container.js +21 -0
- package/dist/styled-system/patterns/cq.d.ts +21 -0
- package/dist/styled-system/patterns/cq.js +21 -0
- package/dist/styled-system/patterns/divider.d.ts +22 -0
- package/dist/styled-system/patterns/divider.js +25 -0
- package/dist/styled-system/patterns/flex.d.ts +26 -0
- package/dist/styled-system/patterns/flex.js +26 -0
- package/dist/styled-system/patterns/float.d.ts +23 -0
- package/dist/styled-system/patterns/float.js +52 -0
- package/dist/styled-system/patterns/grid-item.d.ts +25 -0
- package/dist/styled-system/patterns/grid-item.js +25 -0
- package/dist/styled-system/patterns/grid.d.ts +24 -0
- package/dist/styled-system/patterns/grid.js +27 -0
- package/dist/styled-system/patterns/hstack.d.ts +21 -0
- package/dist/styled-system/patterns/hstack.js +24 -0
- package/dist/styled-system/patterns/index.d.ts +21 -0
- package/dist/styled-system/patterns/index.js +20 -0
- package/dist/styled-system/patterns/link-overlay.d.ts +20 -0
- package/dist/styled-system/patterns/link-overlay.js +24 -0
- package/dist/styled-system/patterns/spacer.d.ts +20 -0
- package/dist/styled-system/patterns/spacer.js +25 -0
- package/dist/styled-system/patterns/square.d.ts +20 -0
- package/dist/styled-system/patterns/square.js +24 -0
- package/dist/styled-system/patterns/stack.d.ts +23 -0
- package/dist/styled-system/patterns/stack.js +24 -0
- package/dist/styled-system/patterns/visually-hidden.d.ts +20 -0
- package/dist/styled-system/patterns/visually-hidden.js +18 -0
- package/dist/styled-system/patterns/vstack.d.ts +21 -0
- package/dist/styled-system/patterns/vstack.js +24 -0
- package/dist/styled-system/patterns/wrap.d.ts +24 -0
- package/dist/styled-system/patterns/wrap.js +25 -0
- package/dist/styled-system/recipes/content-header.d.ts +31 -0
- package/dist/styled-system/recipes/content-header.js +65 -0
- package/dist/styled-system/recipes/content-page.d.ts +34 -0
- package/dist/styled-system/recipes/content-page.js +43 -0
- package/dist/styled-system/recipes/create-recipe.js +82 -0
- package/dist/styled-system/recipes/index.d.ts +17 -0
- package/dist/styled-system/recipes/index.js +16 -0
- package/dist/styled-system/recipes/metric-block.d.ts +34 -0
- package/dist/styled-system/recipes/metric-block.js +76 -0
- package/dist/styled-system/recipes/product-list.d.ts +31 -0
- package/dist/styled-system/recipes/product-list.js +73 -0
- package/dist/styled-system/recipes/product-section.d.ts +31 -0
- package/dist/styled-system/recipes/product-section.js +49 -0
- package/dist/styled-system/recipes/product-settings-row.d.ts +34 -0
- package/dist/styled-system/recipes/product-settings-row.js +58 -0
- package/dist/styled-system/recipes/product-settings.d.ts +31 -0
- package/dist/styled-system/recipes/product-settings.js +57 -0
- package/dist/styled-system/recipes/product-status.d.ts +34 -0
- package/dist/styled-system/recipes/product-status.js +45 -0
- package/dist/styled-system/recipes/product-toolbar.d.ts +31 -0
- package/dist/styled-system/recipes/product-toolbar.js +41 -0
- package/dist/styled-system/recipes/product-workspace.d.ts +34 -0
- package/dist/styled-system/recipes/product-workspace.js +58 -0
- package/dist/styled-system/recipes/surface-metric.d.ts +38 -0
- package/dist/styled-system/recipes/surface-metric.js +63 -0
- package/dist/styled-system/recipes/surface-table.d.ts +34 -0
- package/dist/styled-system/recipes/surface-table.js +62 -0
- package/dist/styled-system/recipes/ui-badge.d.ts +31 -0
- package/dist/styled-system/recipes/ui-badge.js +38 -0
- package/dist/styled-system/recipes/ui-button.d.ts +32 -0
- package/dist/styled-system/recipes/ui-button.js +43 -0
- package/dist/styled-system/recipes/ui-input.d.ts +31 -0
- package/dist/styled-system/recipes/ui-input.js +49 -0
- package/dist/styled-system/recipes/ui-textarea.d.ts +31 -0
- package/dist/styled-system/recipes/ui-textarea.js +24 -0
- package/dist/styled-system/styles.css +1257 -0
- package/dist/styled-system/tokens/index.d.ts +9 -0
- package/dist/styled-system/tokens/index.js +268 -0
- package/dist/styled-system/tokens/tokens.d.ts +36 -0
- package/dist/styled-system/types/composition.d.ts +227 -0
- package/dist/styled-system/types/conditions.d.ts +236 -0
- package/dist/styled-system/types/csstype.d.ts +22570 -0
- package/dist/styled-system/types/index.d.ts +6 -0
- package/dist/styled-system/types/parts.d.ts +8 -0
- package/dist/styled-system/types/pattern.d.ts +78 -0
- package/dist/styled-system/types/prop-type.d.ts +223 -0
- package/dist/styled-system/types/recipe.d.ts +181 -0
- package/dist/styled-system/types/selectors.d.ts +59 -0
- package/dist/styled-system/types/static-css.d.ts +56 -0
- package/dist/styled-system/types/style-props.d.ts +8088 -0
- package/dist/styled-system/types/system-types.d.ts +151 -0
- package/dist/styles/aliases.css +49 -0
- package/dist/styles/compatibility.epilogue.css +384 -0
- package/dist/styles/compatibility.prelude.css +5513 -0
- package/dist/styles/conditional-states.css +28 -0
- package/dist/styles/conditional-utilities.css +22 -0
- package/dist/styles/recipes.css +1262 -0
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +1 -1
- package/package.json +31 -13
- package/styles-compatibility.json +12 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type SchemaFormModel, type SchemaFormIssue } from './json-schema-form.js';
|
|
2
|
+
type Node = {
|
|
3
|
+
kind: 'object';
|
|
4
|
+
properties: Record<string, Node>;
|
|
5
|
+
required: Set<string>;
|
|
6
|
+
additional: boolean;
|
|
7
|
+
nullable: boolean;
|
|
8
|
+
} | {
|
|
9
|
+
kind: 'array';
|
|
10
|
+
item: Node;
|
|
11
|
+
min: number | undefined;
|
|
12
|
+
max: number | undefined;
|
|
13
|
+
nullable: boolean;
|
|
14
|
+
} | {
|
|
15
|
+
kind: 'scalar';
|
|
16
|
+
model: SchemaFormModel;
|
|
17
|
+
};
|
|
18
|
+
export interface RecursiveSchemaFormModel {
|
|
19
|
+
node: Node | undefined;
|
|
20
|
+
issues: SchemaFormIssue[];
|
|
21
|
+
}
|
|
22
|
+
export declare const schemaFormPointer: (path: readonly string[]) => string;
|
|
23
|
+
/** Compile bounded objects/arrays, delegating scalar assertions to the reviewed leaf validator.
|
|
24
|
+
* Unsupported assertions fail closed; this is deliberately not a complete JSON Schema engine.
|
|
25
|
+
* TypeBox's symbol annotations are not JSON assertion keywords and are not executed.
|
|
26
|
+
*/
|
|
27
|
+
export declare function readRecursiveSchemaForm(schema: unknown): RecursiveSchemaFormModel;
|
|
28
|
+
/** Validate a detached JSON payload without defaults, coercion, cleaning or partial acceptance. */
|
|
29
|
+
export declare function validateRecursiveSchemaForm(model: RecursiveSchemaFormModel, value: unknown): SchemaFormIssue[];
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { readSchemaForm, validateSchemaForm } from './json-schema-form.js';
|
|
2
|
+
import { SCHEMA_FORM_LIMITS, schemaFormSnapshot } from '../json-schema-form-state.js';
|
|
3
|
+
const annotationKeys = ['$schema', '$id', '$comment', 'title', 'description', 'type', 'default', 'readOnly', 'writeOnly', 'deprecated', 'examples'];
|
|
4
|
+
const objectKeys = new Set([...annotationKeys, 'properties', 'required', 'additionalProperties']);
|
|
5
|
+
const arrayKeys = new Set([...annotationKeys, 'items', 'minItems', 'maxItems']);
|
|
6
|
+
const unsafe = new Set(['__proto__', 'prototype', 'constructor']);
|
|
7
|
+
export const schemaFormPointer = (path) => path.map(key => '/' + key.replaceAll('~', '~0').replaceAll('/', '~1')).join('');
|
|
8
|
+
function record(value) {
|
|
9
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
10
|
+
&& (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
|
|
11
|
+
}
|
|
12
|
+
function dataRecord(value) {
|
|
13
|
+
return record(value) && Object.keys(value).every(key => {
|
|
14
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
15
|
+
return descriptor !== undefined && 'value' in descriptor;
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
/** Compile bounded objects/arrays, delegating scalar assertions to the reviewed leaf validator.
|
|
19
|
+
* Unsupported assertions fail closed; this is deliberately not a complete JSON Schema engine.
|
|
20
|
+
* TypeBox's symbol annotations are not JSON assertion keywords and are not executed.
|
|
21
|
+
*/
|
|
22
|
+
export function readRecursiveSchemaForm(schema) {
|
|
23
|
+
const issues = [];
|
|
24
|
+
const active = new Set();
|
|
25
|
+
let visited = 0;
|
|
26
|
+
const issue = (path, code) => {
|
|
27
|
+
if (issues.length < 128)
|
|
28
|
+
issues.push({ path, code });
|
|
29
|
+
};
|
|
30
|
+
function read(raw, path, depth, root = false) {
|
|
31
|
+
if (++visited > SCHEMA_FORM_LIMITS.nodes || depth > SCHEMA_FORM_LIMITS.depth || !dataRecord(raw) || active.has(raw)) {
|
|
32
|
+
issue(path, 'invalid-schema');
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
active.add(raw);
|
|
36
|
+
try {
|
|
37
|
+
let type = raw['type'];
|
|
38
|
+
let nullable = false;
|
|
39
|
+
if (Array.isArray(type)) {
|
|
40
|
+
const concrete = type.filter(value => value !== 'null');
|
|
41
|
+
if (type.length !== 2 || concrete.length !== 1 || !type.includes('null')) {
|
|
42
|
+
issue(path, 'unsupported-schema');
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
nullable = true;
|
|
46
|
+
type = concrete[0];
|
|
47
|
+
}
|
|
48
|
+
if (type === undefined && (root || Object.hasOwn(raw, 'properties')))
|
|
49
|
+
type = 'object';
|
|
50
|
+
if (root && type !== 'object') {
|
|
51
|
+
issue(path, 'unsupported-schema');
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
for (const key of ['readOnly', 'writeOnly', 'deprecated']) {
|
|
55
|
+
if (raw[key] !== undefined && typeof raw[key] !== 'boolean')
|
|
56
|
+
issue(path + '/' + key, 'invalid-schema');
|
|
57
|
+
}
|
|
58
|
+
if (Object.hasOwn(raw, 'default')) {
|
|
59
|
+
try {
|
|
60
|
+
schemaFormSnapshot({ value: raw['default'] });
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
issue(path + '/default', 'invalid-schema');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (type !== 'object' && type !== 'array') {
|
|
67
|
+
const model = readSchemaForm({ type: 'object', properties: { value: raw } });
|
|
68
|
+
for (const failure of model.issues)
|
|
69
|
+
issue(path + failure.path.replace(/^\/value(?=\/|$)/u, ''), failure.code);
|
|
70
|
+
return { kind: 'scalar', model };
|
|
71
|
+
}
|
|
72
|
+
const allowed = type === 'object' ? objectKeys : arrayKeys;
|
|
73
|
+
for (const key of Object.keys(raw))
|
|
74
|
+
if (!allowed.has(key))
|
|
75
|
+
issue(path + '/' + key, 'unsupported-schema');
|
|
76
|
+
if (type === 'object') {
|
|
77
|
+
const properties = raw['properties'] ?? {};
|
|
78
|
+
if (!dataRecord(properties) || Object.keys(properties).length > 256 || raw['properties'] === null) {
|
|
79
|
+
issue(path + '/properties', 'invalid-schema');
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const keys = raw['required'] ?? [];
|
|
83
|
+
if (!Array.isArray(keys) || keys.length > 256 || raw['required'] === null
|
|
84
|
+
|| new Set(keys).size !== keys.length || keys.some(key => typeof key !== 'string' || !Object.hasOwn(properties, key))) {
|
|
85
|
+
issue(path + '/required', 'invalid-schema');
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (raw['additionalProperties'] !== undefined && typeof raw['additionalProperties'] !== 'boolean') {
|
|
89
|
+
issue(path + '/additionalProperties', 'unsupported-schema');
|
|
90
|
+
}
|
|
91
|
+
const result = Object.create(null);
|
|
92
|
+
for (const [key, child] of Object.entries(properties)) {
|
|
93
|
+
const childPath = path + schemaFormPointer([key]);
|
|
94
|
+
if (unsafe.has(key)) {
|
|
95
|
+
issue(childPath, 'invalid-schema');
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const node = read(child, childPath, depth + 1);
|
|
99
|
+
if (node)
|
|
100
|
+
result[key] = node;
|
|
101
|
+
}
|
|
102
|
+
return { kind: 'object', properties: result, required: new Set(keys), additional: raw['additionalProperties'] !== false, nullable };
|
|
103
|
+
}
|
|
104
|
+
const item = read(raw['items'], path + '/items', depth + 1);
|
|
105
|
+
const definition = raw;
|
|
106
|
+
function bound(key) {
|
|
107
|
+
const value = definition[key];
|
|
108
|
+
if (value === undefined)
|
|
109
|
+
return;
|
|
110
|
+
if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < 0) {
|
|
111
|
+
issue(path + '/' + key, 'invalid-schema');
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
return value;
|
|
115
|
+
}
|
|
116
|
+
const min = bound('minItems'), max = bound('maxItems');
|
|
117
|
+
if (min !== undefined && max !== undefined && min > max)
|
|
118
|
+
issue(path, 'invalid-schema');
|
|
119
|
+
return item ? { kind: 'array', item, min, max, nullable } : undefined;
|
|
120
|
+
}
|
|
121
|
+
finally {
|
|
122
|
+
active.delete(raw);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
let node;
|
|
126
|
+
try {
|
|
127
|
+
node = read(schema, '', 0, true);
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
issue('', 'invalid-schema');
|
|
131
|
+
}
|
|
132
|
+
return { node, issues };
|
|
133
|
+
}
|
|
134
|
+
/** Validate a detached JSON payload without defaults, coercion, cleaning or partial acceptance. */
|
|
135
|
+
export function validateRecursiveSchemaForm(model, value) {
|
|
136
|
+
if (model.issues.length || !model.node)
|
|
137
|
+
return model.issues.length ? [...model.issues] : [{ path: '', code: 'invalid-schema' }];
|
|
138
|
+
const issues = [];
|
|
139
|
+
const issue = (path, code) => {
|
|
140
|
+
if (issues.length < 128)
|
|
141
|
+
issues.push({ path, code });
|
|
142
|
+
};
|
|
143
|
+
let payload;
|
|
144
|
+
try {
|
|
145
|
+
payload = schemaFormSnapshot(value);
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
return [{ path: '', code: 'invalid-value' }];
|
|
149
|
+
}
|
|
150
|
+
let visited = 0;
|
|
151
|
+
function visit(node, current, path, required, depth) {
|
|
152
|
+
if (++visited > SCHEMA_FORM_LIMITS.nodes || depth > SCHEMA_FORM_LIMITS.depth) {
|
|
153
|
+
issue(path, 'invalid-value');
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
if (current === undefined) {
|
|
157
|
+
if (required)
|
|
158
|
+
issue(path, 'required');
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (node.kind === 'scalar') {
|
|
162
|
+
for (const failure of validateSchemaForm(node.model, { value: current }))
|
|
163
|
+
issue(path + failure.path.replace(/^\/value(?=\/|$)/u, ''), failure.code);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (current === null && node.nullable)
|
|
167
|
+
return;
|
|
168
|
+
if (node.kind === 'object') {
|
|
169
|
+
if (!record(current)) {
|
|
170
|
+
issue(path, 'type');
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
for (const [key, child] of Object.entries(node.properties)) {
|
|
174
|
+
visit(child, Object.hasOwn(current, key) ? current[key] : undefined, path + schemaFormPointer([key]), node.required.has(key), depth + 1);
|
|
175
|
+
}
|
|
176
|
+
if (!node.additional)
|
|
177
|
+
for (const key of Object.keys(current)) {
|
|
178
|
+
if (!Object.hasOwn(node.properties, key))
|
|
179
|
+
issue(path + schemaFormPointer([key]), 'additionalProperties');
|
|
180
|
+
}
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
if (!Array.isArray(current)) {
|
|
184
|
+
issue(path, 'type');
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
if (node.min !== undefined && current.length < node.min)
|
|
188
|
+
issue(path, 'minItems');
|
|
189
|
+
if (node.max !== undefined && current.length > node.max)
|
|
190
|
+
issue(path, 'maxItems');
|
|
191
|
+
for (const [index, item] of current.entries())
|
|
192
|
+
visit(node.item, item, path + '/' + index, true, depth + 1);
|
|
193
|
+
}
|
|
194
|
+
visit(model.node, payload, '', true, 0);
|
|
195
|
+
return issues;
|
|
196
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** 有界、无动态代码执行的轻量公式解析器;不是完整 Excel 公式引擎。 */
|
|
2
|
+
export declare const FORMULA_LIMITS: Readonly<{
|
|
3
|
+
length: 2048;
|
|
4
|
+
tokens: 1024;
|
|
5
|
+
depth: 64;
|
|
6
|
+
work: 10000;
|
|
7
|
+
range: 10000;
|
|
8
|
+
}>;
|
|
9
|
+
export type FormulaErrorCode = '#VALUE!' | '#REF!' | '#NAME?' | '#DIV/0!' | '#NUM!' | '#CYCLE!' | '#LIMIT!';
|
|
10
|
+
export declare function evaluateFormula(raw: string, cells: Readonly<Record<string, string>>): number | string;
|
|
11
|
+
/** 文本导出不能把危险前缀当成外部电子表格公式;调用者仍需完成 CSV 引号转义。 */
|
|
12
|
+
export declare function safeSpreadsheetCsvText(value: string): string;
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/** 有界、无动态代码执行的轻量公式解析器;不是完整 Excel 公式引擎。 */
|
|
2
|
+
export const FORMULA_LIMITS = Object.freeze({ length: 2048, tokens: 1024, depth: 64, work: 10000, range: 10000 });
|
|
3
|
+
class FormulaError extends Error {
|
|
4
|
+
code;
|
|
5
|
+
constructor(code) { super(code); this.code = code; }
|
|
6
|
+
}
|
|
7
|
+
function fail(code) { throw new FormulaError(code); }
|
|
8
|
+
function finite(value) { return Number.isFinite(value) ? value : fail('#NUM!'); }
|
|
9
|
+
function numeric(value) {
|
|
10
|
+
if (value === null)
|
|
11
|
+
return 0;
|
|
12
|
+
return typeof value === 'number' ? value : fail('#VALUE!');
|
|
13
|
+
}
|
|
14
|
+
const decimal = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i;
|
|
15
|
+
function address(key) {
|
|
16
|
+
const match = /^\$?([A-Z]+)\$?(\d+)$/i.exec(key);
|
|
17
|
+
if (!match?.[1] || !match[2])
|
|
18
|
+
return fail('#REF!');
|
|
19
|
+
let column = 0;
|
|
20
|
+
for (const char of match[1].toUpperCase()) {
|
|
21
|
+
column = column * 26 + char.charCodeAt(0) - 64;
|
|
22
|
+
if (column > 16384)
|
|
23
|
+
return fail('#REF!');
|
|
24
|
+
}
|
|
25
|
+
const row = Number(match[2]);
|
|
26
|
+
if (row < 1 || row > 1048576 || !Number.isSafeInteger(row))
|
|
27
|
+
return fail('#REF!');
|
|
28
|
+
return { column, row };
|
|
29
|
+
}
|
|
30
|
+
function columnName(column) {
|
|
31
|
+
let name = '';
|
|
32
|
+
while (column > 0) {
|
|
33
|
+
column--;
|
|
34
|
+
name = String.fromCharCode(65 + column % 26) + name;
|
|
35
|
+
column = Math.floor(column / 26);
|
|
36
|
+
}
|
|
37
|
+
return name;
|
|
38
|
+
}
|
|
39
|
+
class Evaluation {
|
|
40
|
+
work = 0;
|
|
41
|
+
depth = 0;
|
|
42
|
+
active = new Set();
|
|
43
|
+
cache = new Map();
|
|
44
|
+
cells;
|
|
45
|
+
constructor(cells) { this.cells = cells; }
|
|
46
|
+
tick() { if (++this.work > FORMULA_LIMITS.work)
|
|
47
|
+
fail('#LIMIT!'); }
|
|
48
|
+
bounded(fn) {
|
|
49
|
+
if (++this.depth > FORMULA_LIMITS.depth) {
|
|
50
|
+
this.depth--;
|
|
51
|
+
return fail('#LIMIT!');
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
return fn();
|
|
55
|
+
}
|
|
56
|
+
finally {
|
|
57
|
+
this.depth--;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
cell(key) {
|
|
61
|
+
this.tick();
|
|
62
|
+
const parsed = address(key);
|
|
63
|
+
const normalized = `${columnName(parsed.column)}${parsed.row}`;
|
|
64
|
+
if (this.active.has(normalized))
|
|
65
|
+
return fail('#CYCLE!');
|
|
66
|
+
if (this.cache.has(normalized))
|
|
67
|
+
return this.cache.get(normalized) ?? null;
|
|
68
|
+
const raw = Object.hasOwn(this.cells, normalized) ? this.cells[normalized] : '';
|
|
69
|
+
if (typeof raw !== 'string')
|
|
70
|
+
return fail('#VALUE!');
|
|
71
|
+
this.active.add(normalized);
|
|
72
|
+
try {
|
|
73
|
+
const trimmed = raw.trim();
|
|
74
|
+
const value = raw.startsWith('=')
|
|
75
|
+
? new Parser(raw.slice(1), this).parse()
|
|
76
|
+
: trimmed === '' ? null : decimal.test(trimmed) ? finite(Number(trimmed)) : raw;
|
|
77
|
+
this.cache.set(normalized, value);
|
|
78
|
+
return value;
|
|
79
|
+
}
|
|
80
|
+
finally {
|
|
81
|
+
this.active.delete(normalized);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
range(start, end) {
|
|
85
|
+
const a = address(start);
|
|
86
|
+
const b = address(end);
|
|
87
|
+
const left = Math.min(a.column, b.column), right = Math.max(a.column, b.column);
|
|
88
|
+
const top = Math.min(a.row, b.row), bottom = Math.max(a.row, b.row);
|
|
89
|
+
if ((right - left + 1) * (bottom - top + 1) > FORMULA_LIMITS.range)
|
|
90
|
+
return fail('#LIMIT!');
|
|
91
|
+
const values = [];
|
|
92
|
+
for (let row = top; row <= bottom; row++) {
|
|
93
|
+
for (let column = left; column <= right; column++)
|
|
94
|
+
values.push(this.cell(`${columnName(column)}${row}`));
|
|
95
|
+
}
|
|
96
|
+
return values;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
class Parser {
|
|
100
|
+
tokens = [];
|
|
101
|
+
position = 0;
|
|
102
|
+
context;
|
|
103
|
+
constructor(source, context) {
|
|
104
|
+
this.context = context;
|
|
105
|
+
if (source.length + 1 > FORMULA_LIMITS.length)
|
|
106
|
+
fail('#LIMIT!');
|
|
107
|
+
let rest = source;
|
|
108
|
+
while (rest.length) {
|
|
109
|
+
rest = rest.trimStart();
|
|
110
|
+
if (!rest)
|
|
111
|
+
break;
|
|
112
|
+
context.tick();
|
|
113
|
+
if (this.tokens.length >= FORMULA_LIMITS.tokens)
|
|
114
|
+
fail('#LIMIT!');
|
|
115
|
+
const number = /^(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?/i.exec(rest)?.[0];
|
|
116
|
+
const cell = /^\$?[A-Z]+\$?\d+/i.exec(rest)?.[0];
|
|
117
|
+
const name = /^[A-Z_][A-Z_0-9]*/i.exec(rest)?.[0];
|
|
118
|
+
let token;
|
|
119
|
+
if (number)
|
|
120
|
+
token = { kind: 'number', text: number };
|
|
121
|
+
else if (cell)
|
|
122
|
+
token = { kind: 'cell', text: cell.toUpperCase() };
|
|
123
|
+
else if (name)
|
|
124
|
+
token = { kind: 'name', text: name.toUpperCase() };
|
|
125
|
+
else if (/^[+\-*/(),:]/.test(rest))
|
|
126
|
+
token = { kind: 'symbol', text: rest[0] ?? '' };
|
|
127
|
+
else
|
|
128
|
+
return fail('#VALUE!');
|
|
129
|
+
this.tokens.push(token);
|
|
130
|
+
rest = rest.slice(token.text.length);
|
|
131
|
+
}
|
|
132
|
+
this.tokens.push({ kind: 'end', text: '' });
|
|
133
|
+
}
|
|
134
|
+
peek() { return this.tokens[this.position] ?? { kind: 'end', text: '' }; }
|
|
135
|
+
take() { this.context.tick(); const token = this.peek(); this.position++; return token; }
|
|
136
|
+
expect(text) { if (this.take().text !== text)
|
|
137
|
+
fail('#VALUE!'); }
|
|
138
|
+
parse() {
|
|
139
|
+
return this.context.bounded(() => {
|
|
140
|
+
const result = this.expression();
|
|
141
|
+
if (this.peek().kind !== 'end')
|
|
142
|
+
return fail('#VALUE!');
|
|
143
|
+
return result;
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
expression() {
|
|
147
|
+
let value = this.product();
|
|
148
|
+
while (this.peek().text === '+' || this.peek().text === '-') {
|
|
149
|
+
const op = this.take().text;
|
|
150
|
+
const right = numeric(this.product()), left = numeric(value);
|
|
151
|
+
value = finite(op === '+' ? left + right : left - right);
|
|
152
|
+
}
|
|
153
|
+
return value;
|
|
154
|
+
}
|
|
155
|
+
product() {
|
|
156
|
+
let value = this.unary();
|
|
157
|
+
while (this.peek().text === '*' || this.peek().text === '/') {
|
|
158
|
+
const op = this.take().text;
|
|
159
|
+
const right = numeric(this.unary()), left = numeric(value);
|
|
160
|
+
if (op === '/' && right === 0)
|
|
161
|
+
return fail('#DIV/0!');
|
|
162
|
+
value = finite(op === '*' ? left * right : left / right);
|
|
163
|
+
}
|
|
164
|
+
return value;
|
|
165
|
+
}
|
|
166
|
+
unary() {
|
|
167
|
+
if (this.peek().text === '+' || this.peek().text === '-') {
|
|
168
|
+
const negative = this.take().text === '-';
|
|
169
|
+
return this.context.bounded(() => finite(numeric(this.unary()) * (negative ? -1 : 1)));
|
|
170
|
+
}
|
|
171
|
+
return this.primary();
|
|
172
|
+
}
|
|
173
|
+
primary() {
|
|
174
|
+
const token = this.take();
|
|
175
|
+
if (token.kind === 'number')
|
|
176
|
+
return finite(Number(token.text));
|
|
177
|
+
if (token.kind === 'cell')
|
|
178
|
+
return this.context.cell(token.text);
|
|
179
|
+
if (token.text === '(')
|
|
180
|
+
return this.context.bounded(() => {
|
|
181
|
+
const value = this.expression();
|
|
182
|
+
this.expect(')');
|
|
183
|
+
return value;
|
|
184
|
+
});
|
|
185
|
+
if (token.kind === 'name')
|
|
186
|
+
return this.context.bounded(() => this.aggregate(token.text));
|
|
187
|
+
return fail('#VALUE!');
|
|
188
|
+
}
|
|
189
|
+
aggregate(name) {
|
|
190
|
+
if (!['SUM', 'AVG', 'AVERAGE', 'COUNT', 'MIN', 'MAX'].includes(name))
|
|
191
|
+
return fail('#NAME?');
|
|
192
|
+
this.expect('(');
|
|
193
|
+
const values = [];
|
|
194
|
+
if (this.peek().text !== ')') {
|
|
195
|
+
while (true) {
|
|
196
|
+
if (this.peek().kind === 'cell' && this.tokens[this.position + 1]?.text === ':') {
|
|
197
|
+
const start = this.take().text;
|
|
198
|
+
this.expect(':');
|
|
199
|
+
const end = this.take();
|
|
200
|
+
if (end.kind !== 'cell')
|
|
201
|
+
return fail('#REF!');
|
|
202
|
+
for (const value of this.context.range(start, end.text))
|
|
203
|
+
if (typeof value === 'number')
|
|
204
|
+
values.push(value);
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
const value = this.expression();
|
|
208
|
+
if (typeof value === 'number')
|
|
209
|
+
values.push(value);
|
|
210
|
+
}
|
|
211
|
+
if (this.peek().text !== ',')
|
|
212
|
+
break;
|
|
213
|
+
this.take();
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
this.expect(')');
|
|
217
|
+
if (name === 'COUNT')
|
|
218
|
+
return values.length;
|
|
219
|
+
if (name === 'MIN' || name === 'MAX') {
|
|
220
|
+
if (!values.length)
|
|
221
|
+
return 0;
|
|
222
|
+
return values.reduce((a, b) => name === 'MIN' ? Math.min(a, b) : Math.max(a, b));
|
|
223
|
+
}
|
|
224
|
+
const sum = finite(values.reduce((a, b) => finite(a + b), 0));
|
|
225
|
+
if (name === 'SUM')
|
|
226
|
+
return sum;
|
|
227
|
+
return values.length ? finite(sum / values.length) : fail('#DIV/0!');
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
export function evaluateFormula(raw, cells) {
|
|
231
|
+
if (!raw.startsWith('='))
|
|
232
|
+
return raw;
|
|
233
|
+
try {
|
|
234
|
+
return new Parser(raw.slice(1), new Evaluation(cells)).parse() ?? 0;
|
|
235
|
+
}
|
|
236
|
+
catch (error) {
|
|
237
|
+
if (error instanceof FormulaError)
|
|
238
|
+
return error.code;
|
|
239
|
+
throw error;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
/** 文本导出不能把危险前缀当成外部电子表格公式;调用者仍需完成 CSV 引号转义。 */
|
|
243
|
+
export function safeSpreadsheetCsvText(value) {
|
|
244
|
+
return /^[\s\uFEFF]*[=+\-@]/u.test(value) || /^[\t\r\n]/.test(value) ? `'${value}` : value;
|
|
245
|
+
}
|
|
@@ -70,6 +70,11 @@ export const builtinDisplayComponents = Object.freeze({
|
|
|
70
70
|
images: validatedDisplay(props(nullable(Type.Array(Type.String()))), ImagesField),
|
|
71
71
|
tags: validatedDisplay(props(nullable(Type.Union([Type.String(), Type.Array(Type.String())]))), TagField),
|
|
72
72
|
date: validatedDisplay(props(date), DateField),
|
|
73
|
+
datetime: validatedDisplay(Type.Object({
|
|
74
|
+
value: date, format: Type.Literal('datetime'),
|
|
75
|
+
}, { additionalProperties: false }), DateField, input => ({ value: input.value, format: 'datetime' })),
|
|
76
|
+
// 原生 time 值没有日期或时区,不能交给 Date 解析。
|
|
77
|
+
time: textDisplay,
|
|
73
78
|
daterange: validatedDisplay(props(nullable(Type.Union([
|
|
74
79
|
Type.Tuple([date, date]),
|
|
75
80
|
Type.Object({ start: dateProperty, end: dateProperty, from: dateProperty, to: dateProperty }, { additionalProperties: false }),
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
</script>
|
|
4
4
|
|
|
5
5
|
<script lang="ts">
|
|
6
|
+
import { dateFieldValue } from './date-field-value.js';
|
|
6
7
|
import { cn } from '../../utils.js';
|
|
7
8
|
|
|
8
9
|
interface Props {
|
|
@@ -45,7 +46,7 @@
|
|
|
45
46
|
|
|
46
47
|
const formatted = $derived.by(() => {
|
|
47
48
|
if (value == null || value === '') return nullLabel;
|
|
48
|
-
const d = value
|
|
49
|
+
const d = dateFieldValue(value, format === 'time');
|
|
49
50
|
if (isNaN(d.getTime())) return nullLabel;
|
|
50
51
|
|
|
51
52
|
if (format === 'iso') return d.toISOString();
|
|
@@ -68,8 +69,8 @@
|
|
|
68
69
|
|
|
69
70
|
const fullIsoTitle = $derived.by(() => {
|
|
70
71
|
if (value == null || value === '') return undefined;
|
|
71
|
-
const d = value
|
|
72
|
-
return isNaN(d.getTime()) ? undefined : d.toISOString();
|
|
72
|
+
const d = dateFieldValue(value, format === 'time');
|
|
73
|
+
return isNaN(d.getTime()) ? undefined : format === 'time' && typeof value === 'string' && /^\d{2}:/u.test(value) ? value : d.toISOString();
|
|
73
74
|
});
|
|
74
75
|
</script>
|
|
75
76
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** 原生 time 输入没有日期/时区;使用本地锚点格式化,不能按 ISO 时间戳解析。 */
|
|
2
|
+
export function dateFieldValue(value, timeOnly) {
|
|
3
|
+
if (timeOnly && typeof value === 'string') {
|
|
4
|
+
const match = /^(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{1,3}))?)?$/u.exec(value);
|
|
5
|
+
if (match) {
|
|
6
|
+
const hour = Number(match[1]), minute = Number(match[2]), second = Number(match[3] ?? 0);
|
|
7
|
+
if (hour > 23 || minute > 59 || second > 59)
|
|
8
|
+
return new Date(NaN);
|
|
9
|
+
return new Date(1970, 0, 1, hour, minute, second, Number((match[4] ?? '').padEnd(3, '0')));
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return value instanceof Date ? value : new Date(value);
|
|
13
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/** 嵌套表单的草稿与传输语义;不是替代服务端的 JSON Schema 校验器。 */
|
|
2
|
+
export interface JsonSchemaFormSchema {
|
|
3
|
+
type?: string | string[];
|
|
4
|
+
const?: string | number | boolean | null;
|
|
5
|
+
readOnly?: boolean;
|
|
6
|
+
minimum?: number;
|
|
7
|
+
maximum?: number;
|
|
8
|
+
exclusiveMinimum?: number;
|
|
9
|
+
exclusiveMaximum?: number;
|
|
10
|
+
multipleOf?: number;
|
|
11
|
+
minLength?: number;
|
|
12
|
+
maxLength?: number;
|
|
13
|
+
minItems?: number;
|
|
14
|
+
maxItems?: number;
|
|
15
|
+
additionalProperties?: boolean;
|
|
16
|
+
title?: string;
|
|
17
|
+
description?: string;
|
|
18
|
+
properties?: Record<string, JsonSchemaFormSchema>;
|
|
19
|
+
items?: JsonSchemaFormSchema;
|
|
20
|
+
enum?: Array<string | number | boolean | null>;
|
|
21
|
+
default?: unknown;
|
|
22
|
+
required?: string[];
|
|
23
|
+
}
|
|
24
|
+
export declare const SCHEMA_FORM_LIMITS: Readonly<{
|
|
25
|
+
depth: 32;
|
|
26
|
+
nodes: 10000;
|
|
27
|
+
characters: 1048576;
|
|
28
|
+
}>;
|
|
29
|
+
/** Svelte 代理也通过普通数据属性读取;回调永远收不到活跃草稿的引用。 */
|
|
30
|
+
export declare function schemaFormSnapshot(value: unknown): Record<string, unknown>;
|
|
31
|
+
/** 默认值只填“缺失的键”;显式 undefined/null/false/0/空串都表示调用方的决定。 */
|
|
32
|
+
export declare function initializeSchemaForm(schema: JsonSchemaFormSchema, value: Record<string, unknown>): Record<string, unknown>;
|
|
33
|
+
export declare function schemaFormArrayItem(schema: JsonSchemaFormSchema): unknown;
|
|
34
|
+
export declare function writeSchemaFormPath(value: Record<string, unknown>, path: readonly string[], nextValue: unknown): Record<string, unknown>;
|
|
35
|
+
/** option.value 是索引,不是 String(value):1 与 "1"、null 与 "null" 必须可区分。 */
|
|
36
|
+
export declare function schemaFormEnumIndex(choices: readonly unknown[], value: unknown): string;
|
|
37
|
+
export declare function schemaFormEnumValue(choices: readonly unknown[], index: string): unknown;
|