@svadmin/lite 0.3.6 → 0.3.10
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 +19 -13
- package/dist/components/LiteAlert.svelte +19 -0
- package/dist/components/LiteAlert.svelte.d.ts +11 -0
- package/dist/components/LiteArrayField.svelte +60 -0
- package/dist/components/LiteArrayField.svelte.d.ts +17 -0
- package/dist/components/LiteArrayItem.svelte +146 -0
- package/dist/components/LiteArrayItem.svelte.d.ts +13 -0
- package/dist/components/LiteAuditLog.svelte +104 -0
- package/dist/components/LiteAuditLog.svelte.d.ts +23 -0
- package/dist/components/LiteBreadcrumbs.svelte +39 -0
- package/dist/components/LiteBreadcrumbs.svelte.d.ts +16 -0
- package/dist/components/LiteChatDialog.svelte +101 -0
- package/dist/components/LiteChatDialog.svelte.d.ts +20 -0
- package/dist/components/LiteConfirmDialog.svelte +60 -0
- package/dist/components/LiteConfirmDialog.svelte.d.ts +22 -0
- package/dist/components/LiteEmptyState.svelte +39 -0
- package/dist/components/LiteEmptyState.svelte.d.ts +16 -0
- package/dist/components/LiteForm.svelte +165 -0
- package/dist/components/LiteForm.svelte.d.ts +26 -0
- package/dist/components/LiteLayout.svelte +147 -0
- package/dist/components/LiteLayout.svelte.d.ts +19 -0
- package/dist/components/LiteLogin.svelte +41 -0
- package/dist/components/LiteLogin.svelte.d.ts +8 -0
- package/dist/components/LitePagination.svelte +73 -0
- package/dist/components/LitePagination.svelte.d.ts +11 -0
- package/dist/components/LitePermissionMatrix.svelte +147 -0
- package/dist/components/LitePermissionMatrix.svelte.d.ts +32 -0
- package/dist/components/LiteSearch.svelte +41 -0
- package/dist/components/LiteSearch.svelte.d.ts +9 -0
- package/dist/components/LiteShow.svelte +61 -0
- package/dist/components/LiteShow.svelte.d.ts +14 -0
- package/dist/components/LiteShowField.svelte +62 -0
- package/dist/components/LiteShowField.svelte.d.ts +12 -0
- package/dist/components/LiteStatsCard.svelte +70 -0
- package/dist/components/LiteStatsCard.svelte.d.ts +24 -0
- package/dist/components/LiteTable.svelte +132 -0
- package/dist/components/LiteTable.svelte.d.ts +19 -0
- package/dist/components/LiteTabs.svelte +57 -0
- package/dist/components/LiteTabs.svelte.d.ts +16 -0
- package/dist/components/advanced/LiteAutoSaveIndicator.svelte +26 -0
- package/dist/components/advanced/LiteAutoSaveIndicator.svelte.d.ts +12 -0
- package/dist/components/advanced/LiteDraggableHeader.svelte +33 -0
- package/dist/components/advanced/LiteDraggableHeader.svelte.d.ts +15 -0
- package/dist/components/advanced/LiteDrawerForm.svelte +42 -0
- package/dist/components/advanced/LiteDrawerForm.svelte.d.ts +16 -0
- package/dist/components/advanced/LiteInlineEdit.svelte +32 -0
- package/dist/components/advanced/LiteInlineEdit.svelte.d.ts +15 -0
- package/dist/components/advanced/LiteModalForm.svelte +44 -0
- package/dist/components/advanced/LiteModalForm.svelte.d.ts +17 -0
- package/dist/components/advanced/LiteToast.svelte +34 -0
- package/dist/components/advanced/LiteToast.svelte.d.ts +14 -0
- package/dist/components/advanced/LiteUndoableNotification.svelte +25 -0
- package/dist/components/advanced/LiteUndoableNotification.svelte.d.ts +13 -0
- package/dist/components/advanced/LiteVirtualTable.svelte +44 -0
- package/dist/components/advanced/LiteVirtualTable.svelte.d.ts +20 -0
- package/dist/components/advanced/index.js +8 -0
- package/dist/components/buttons/LiteCloneButton.svelte +33 -0
- package/dist/components/buttons/LiteCloneButton.svelte.d.ts +11 -0
- package/dist/components/buttons/LiteCreateButton.svelte +31 -0
- package/dist/components/buttons/LiteCreateButton.svelte.d.ts +10 -0
- package/dist/components/buttons/LiteDeleteButton.svelte +57 -0
- package/dist/components/buttons/LiteDeleteButton.svelte.d.ts +12 -0
- package/dist/components/buttons/LiteEditButton.svelte +33 -0
- package/dist/components/buttons/LiteEditButton.svelte.d.ts +11 -0
- package/dist/components/buttons/LiteExportButton.svelte +31 -0
- package/dist/components/buttons/LiteExportButton.svelte.d.ts +10 -0
- package/dist/components/buttons/LiteImportButton.svelte +50 -0
- package/dist/components/buttons/LiteImportButton.svelte.d.ts +10 -0
- package/dist/components/buttons/LiteListButton.svelte +31 -0
- package/dist/components/buttons/LiteListButton.svelte.d.ts +10 -0
- package/dist/components/buttons/LiteRefreshButton.svelte +34 -0
- package/dist/components/buttons/LiteRefreshButton.svelte.d.ts +8 -0
- package/dist/components/buttons/LiteSaveButton.svelte +27 -0
- package/dist/components/buttons/LiteSaveButton.svelte.d.ts +8 -0
- package/dist/components/buttons/LiteShowButton.svelte +33 -0
- package/dist/components/buttons/LiteShowButton.svelte.d.ts +11 -0
- package/dist/components/buttons/index.js +10 -0
- package/dist/components/fields/LiteBooleanField.svelte +42 -0
- package/dist/components/fields/LiteBooleanField.svelte.d.ts +10 -0
- package/dist/components/fields/LiteDateField.svelte +51 -0
- package/dist/components/fields/LiteDateField.svelte.d.ts +10 -0
- package/dist/components/fields/LiteEmailField.svelte +41 -0
- package/dist/components/fields/LiteEmailField.svelte.d.ts +10 -0
- package/dist/components/fields/LiteFileField.svelte +59 -0
- package/dist/components/fields/LiteFileField.svelte.d.ts +10 -0
- package/dist/components/fields/LiteImageField.svelte +57 -0
- package/dist/components/fields/LiteImageField.svelte.d.ts +10 -0
- package/dist/components/fields/LiteJsonField.svelte +43 -0
- package/dist/components/fields/LiteJsonField.svelte.d.ts +10 -0
- package/dist/components/fields/LiteMarkdownField.svelte +33 -0
- package/dist/components/fields/LiteMarkdownField.svelte.d.ts +10 -0
- package/dist/components/fields/LiteMultiSelectField.svelte +57 -0
- package/dist/components/fields/LiteMultiSelectField.svelte.d.ts +10 -0
- package/dist/components/fields/LiteNumberField.svelte +35 -0
- package/dist/components/fields/LiteNumberField.svelte.d.ts +10 -0
- package/dist/components/fields/LiteRelationField.svelte +67 -0
- package/dist/components/fields/LiteRelationField.svelte.d.ts +10 -0
- package/dist/components/fields/LiteRichTextField.svelte +44 -0
- package/dist/components/fields/LiteRichTextField.svelte.d.ts +10 -0
- package/dist/components/fields/LiteSelectField.svelte +47 -0
- package/dist/components/fields/LiteSelectField.svelte.d.ts +10 -0
- package/dist/components/fields/LiteTagField.svelte +44 -0
- package/dist/components/fields/LiteTagField.svelte.d.ts +10 -0
- package/dist/components/fields/LiteTextField.svelte +35 -0
- package/dist/components/fields/LiteTextField.svelte.d.ts +10 -0
- package/dist/components/fields/LiteUrlField.svelte +52 -0
- package/dist/components/fields/LiteUrlField.svelte.d.ts +10 -0
- package/dist/components/fields/index.js +15 -0
- package/dist/components/layout/LiteAuthenticated.svelte +23 -0
- package/dist/components/layout/LiteAuthenticated.svelte.d.ts +10 -0
- package/dist/components/layout/LiteCanAccess.svelte +27 -0
- package/dist/components/layout/LiteCanAccess.svelte.d.ts +10 -0
- package/dist/components/layout/LiteCatchAllNavigate.svelte +20 -0
- package/dist/components/layout/LiteCatchAllNavigate.svelte.d.ts +7 -0
- package/dist/components/layout/LiteErrorBoundary.svelte +20 -0
- package/dist/components/layout/LiteErrorBoundary.svelte.d.ts +13 -0
- package/dist/components/layout/LiteErrorComponent.svelte +37 -0
- package/dist/components/layout/LiteErrorComponent.svelte.d.ts +9 -0
- package/dist/components/layout/LiteHeader.svelte +19 -0
- package/dist/components/layout/LiteHeader.svelte.d.ts +8 -0
- package/dist/components/layout/LiteNavigateToResource.svelte +25 -0
- package/dist/components/layout/LiteNavigateToResource.svelte.d.ts +7 -0
- package/dist/components/layout/LiteSidebar.svelte +93 -0
- package/dist/components/layout/LiteSidebar.svelte.d.ts +12 -0
- package/dist/components/layout/index.js +8 -0
- package/dist/components/pages/LiteCreatePage.svelte +39 -0
- package/dist/components/pages/LiteCreatePage.svelte.d.ts +10 -0
- package/dist/components/pages/LiteEditPage.svelte +54 -0
- package/dist/components/pages/LiteEditPage.svelte.d.ts +12 -0
- package/dist/components/pages/LiteForgotPasswordPage.svelte +60 -0
- package/dist/components/pages/LiteForgotPasswordPage.svelte.d.ts +11 -0
- package/dist/components/pages/LiteListPage.svelte +81 -0
- package/dist/components/pages/LiteListPage.svelte.d.ts +20 -0
- package/dist/components/pages/LiteProfilePage.svelte +61 -0
- package/dist/components/pages/LiteProfilePage.svelte.d.ts +14 -0
- package/dist/components/pages/LiteRegisterPage.svelte +63 -0
- package/dist/components/pages/LiteRegisterPage.svelte.d.ts +10 -0
- package/dist/components/pages/LiteShowPage.svelte +61 -0
- package/dist/components/pages/LiteShowPage.svelte.d.ts +11 -0
- package/dist/components/pages/LiteUpdatePasswordPage.svelte +51 -0
- package/dist/components/pages/LiteUpdatePasswordPage.svelte.d.ts +9 -0
- package/dist/components/pages/index.js +8 -0
- package/dist/components/widgets/LiteAnomalyBadge.svelte +40 -0
- package/dist/components/widgets/LiteAnomalyBadge.svelte.d.ts +15 -0
- package/dist/components/widgets/LiteBarChart.svelte +45 -0
- package/dist/components/widgets/LiteBarChart.svelte.d.ts +17 -0
- package/dist/components/widgets/LiteInsightCard.svelte +31 -0
- package/dist/components/widgets/LiteInsightCard.svelte.d.ts +13 -0
- package/dist/components/widgets/LiteLineChart.svelte +48 -0
- package/dist/components/widgets/LiteLineChart.svelte.d.ts +17 -0
- package/dist/components/widgets/LitePieChart.svelte +44 -0
- package/dist/components/widgets/LitePieChart.svelte.d.ts +17 -0
- package/dist/components/widgets/index.js +5 -0
- package/dist/enhance.js +112 -0
- package/dist/index.d.ts +27 -0
- package/{src/index.ts → dist/index.js} +3 -26
- package/dist/lite.css +817 -0
- package/dist/schema-generator.d.ts +29 -0
- package/dist/schema-generator.js +226 -0
- package/dist/security.d.ts +14 -0
- package/dist/security.js +59 -0
- package/dist/server-adapter.d.ts +111 -0
- package/dist/server-adapter.js +422 -0
- package/package.json +55 -18
- package/src/schema-generator.ts +0 -147
- package/src/server-adapter.ts +0 -307
- /package/{src/components/advanced/index.ts → dist/components/advanced/index.d.ts} +0 -0
- /package/{src/components/buttons/index.ts → dist/components/buttons/index.d.ts} +0 -0
- /package/{src/components/fields/index.ts → dist/components/fields/index.d.ts} +0 -0
- /package/{src/components/layout/index.ts → dist/components/layout/index.d.ts} +0 -0
- /package/{src/components/pages/index.ts → dist/components/pages/index.d.ts} +0 -0
- /package/{src/components/widgets/index.ts → dist/components/widgets/index.d.ts} +0 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @svadmin/lite — Schema Generator
|
|
3
|
+
*
|
|
4
|
+
* Auto-generates Zod schemas from @svadmin/core FieldDefinitions.
|
|
5
|
+
* Used with sveltekit-superforms for server-side form validation.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
import type { FieldDefinition, ResourceDefinition } from '@svadmin/core';
|
|
9
|
+
/**
|
|
10
|
+
* Generate a Zod object schema from a ResourceDefinition's fields.
|
|
11
|
+
* Only includes fields that are relevant for form rendering.
|
|
12
|
+
*
|
|
13
|
+
* @param mode - 'create' | 'edit' to filter fields by showInCreate / showInEdit
|
|
14
|
+
*/
|
|
15
|
+
export declare function fieldsToZodSchema(fields: FieldDefinition[], mode?: 'create' | 'edit'): z.ZodObject<Record<string, z.ZodTypeAny>>;
|
|
16
|
+
/**
|
|
17
|
+
* Generate a Zod schema from a ResourceDefinition.
|
|
18
|
+
* Convenience wrapper around fieldsToZodSchema.
|
|
19
|
+
*/
|
|
20
|
+
export declare function resourceToZodSchema(resource: ResourceDefinition, mode?: 'create' | 'edit'): z.ZodObject<Record<string, z.ZodTypeAny>>;
|
|
21
|
+
/**
|
|
22
|
+
* Determine a conservative HTML input type for server-rendered forms.
|
|
23
|
+
* Text fallbacks avoid inconsistent native validation and date widgets.
|
|
24
|
+
*/
|
|
25
|
+
export declare function fieldToInputType(field: FieldDefinition): string;
|
|
26
|
+
/**
|
|
27
|
+
* Generate an input placeholder with a portable format hint.
|
|
28
|
+
*/
|
|
29
|
+
export declare function fieldToPlaceholder(field: FieldDefinition): string;
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @svadmin/lite — Schema Generator
|
|
3
|
+
*
|
|
4
|
+
* Auto-generates Zod schemas from @svadmin/core FieldDefinitions.
|
|
5
|
+
* Used with sveltekit-superforms for server-side form validation.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
function isNativeFile(value) {
|
|
9
|
+
return typeof File !== 'undefined' && value instanceof File;
|
|
10
|
+
}
|
|
11
|
+
function isNonEmptyNativeFile(value) {
|
|
12
|
+
return isNativeFile(value) && value.size > 0 && value.name !== '';
|
|
13
|
+
}
|
|
14
|
+
function hasRequiredValue(value) {
|
|
15
|
+
if (value === undefined || value === null)
|
|
16
|
+
return false;
|
|
17
|
+
if (typeof value === 'string')
|
|
18
|
+
return value.trim().length > 0;
|
|
19
|
+
if (Array.isArray(value))
|
|
20
|
+
return value.length > 0;
|
|
21
|
+
if (isNativeFile(value))
|
|
22
|
+
return isNonEmptyNativeFile(value);
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
function numberFieldToZod(field) {
|
|
26
|
+
const numberSchema = z.coerce.number({ message: `${field.label} must be a number` });
|
|
27
|
+
const targetSchema = field.required ? numberSchema : numberSchema.optional();
|
|
28
|
+
return z.preprocess((value) => {
|
|
29
|
+
if (value === undefined || value === null)
|
|
30
|
+
return undefined;
|
|
31
|
+
if (typeof value === 'string' && value.trim() === '')
|
|
32
|
+
return undefined;
|
|
33
|
+
return value;
|
|
34
|
+
}, targetSchema);
|
|
35
|
+
}
|
|
36
|
+
function singleFileFieldToZod(field, options) {
|
|
37
|
+
const fileSchema = z.custom(isNonEmptyNativeFile, {
|
|
38
|
+
message: `${field.label} must be a non-empty file`,
|
|
39
|
+
});
|
|
40
|
+
const referenceSchema = z.string().trim().min(1, `${field.label} must reference an existing file`);
|
|
41
|
+
const uploadSchema = options.allowReference ? z.union([fileSchema, referenceSchema]) : fileSchema;
|
|
42
|
+
const targetSchema = options.required ? uploadSchema : uploadSchema.optional();
|
|
43
|
+
return z.preprocess((value) => {
|
|
44
|
+
if (value === undefined || value === null || value === '')
|
|
45
|
+
return undefined;
|
|
46
|
+
if (isNativeFile(value) && !isNonEmptyNativeFile(value))
|
|
47
|
+
return undefined;
|
|
48
|
+
return value;
|
|
49
|
+
}, targetSchema);
|
|
50
|
+
}
|
|
51
|
+
function multipleFilesFieldToZod(field, options) {
|
|
52
|
+
const fileSchema = z.custom(isNonEmptyNativeFile, {
|
|
53
|
+
message: `${field.label} contains an empty or invalid file`,
|
|
54
|
+
});
|
|
55
|
+
const filesSchema = z.array(fileSchema).min(1, `${field.label} must contain at least one file`);
|
|
56
|
+
const referencesSchema = z.array(z.string().trim().min(1, `${field.label} contains an empty file reference`)).min(1, `${field.label} must contain at least one file`);
|
|
57
|
+
const uploadSchema = options.allowReference ? z.union([filesSchema, referencesSchema]) : filesSchema;
|
|
58
|
+
const targetSchema = options.required ? uploadSchema : uploadSchema.optional();
|
|
59
|
+
return z.preprocess((value) => {
|
|
60
|
+
if (value === undefined || value === null || value === '')
|
|
61
|
+
return undefined;
|
|
62
|
+
if (!Array.isArray(value))
|
|
63
|
+
return value;
|
|
64
|
+
const invalidEntries = value.filter((entry) => !isNativeFile(entry) && typeof entry !== 'string');
|
|
65
|
+
if (invalidEntries.length > 0)
|
|
66
|
+
return value;
|
|
67
|
+
const uploadedFiles = value.filter(isNonEmptyNativeFile);
|
|
68
|
+
if (uploadedFiles.length > 0)
|
|
69
|
+
return uploadedFiles;
|
|
70
|
+
const references = value.filter((entry) => typeof entry === 'string' && entry.trim().length > 0);
|
|
71
|
+
return references.length > 0 ? references : undefined;
|
|
72
|
+
}, targetSchema);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Convert a single FieldDefinition to its corresponding Zod type.
|
|
76
|
+
*/
|
|
77
|
+
function fieldToZod(field, mode, withinArray = false) {
|
|
78
|
+
let schema;
|
|
79
|
+
switch (field.type) {
|
|
80
|
+
case 'number':
|
|
81
|
+
return numberFieldToZod(field);
|
|
82
|
+
case 'boolean':
|
|
83
|
+
schema = z.coerce.boolean();
|
|
84
|
+
break;
|
|
85
|
+
case 'email':
|
|
86
|
+
schema = z.string().email(`${field.label} must be a valid email`);
|
|
87
|
+
break;
|
|
88
|
+
case 'url':
|
|
89
|
+
schema = z.string().url(`${field.label} must be a valid URL`);
|
|
90
|
+
break;
|
|
91
|
+
case 'date':
|
|
92
|
+
schema = z.string().refine((v) => !v || !isNaN(Date.parse(v)), { message: `${field.label} must be a valid date` });
|
|
93
|
+
break;
|
|
94
|
+
case 'select':
|
|
95
|
+
if (field.options?.length) {
|
|
96
|
+
schema = z.enum(field.options.map((o) => String(o.value)), { message: `${field.label} must be one of the options` });
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
schema = z.string();
|
|
100
|
+
}
|
|
101
|
+
break;
|
|
102
|
+
case 'multiselect':
|
|
103
|
+
schema = z.array(z.string()).default([]);
|
|
104
|
+
break;
|
|
105
|
+
case 'array': {
|
|
106
|
+
const shape = {};
|
|
107
|
+
for (const subField of field.subFields ?? []) {
|
|
108
|
+
shape[subField.key] = fieldToZod(subField, mode, true);
|
|
109
|
+
}
|
|
110
|
+
const arraySchema = z.array(z.object(shape));
|
|
111
|
+
return field.required
|
|
112
|
+
? arraySchema.min(1, `${field.label} must contain at least one item`)
|
|
113
|
+
: arraySchema.optional().or(z.literal(''));
|
|
114
|
+
}
|
|
115
|
+
case 'tags':
|
|
116
|
+
schema = z.union([
|
|
117
|
+
z.array(z.string()),
|
|
118
|
+
z.string().transform((value) => value ? value.split(',').map((tag) => tag.trim()).filter(Boolean) : []),
|
|
119
|
+
]);
|
|
120
|
+
break;
|
|
121
|
+
case 'textarea':
|
|
122
|
+
case 'richtext':
|
|
123
|
+
schema = z.string().max(50000, `${field.label} is too long`);
|
|
124
|
+
break;
|
|
125
|
+
case 'json':
|
|
126
|
+
schema = z.unknown().transform((value, context) => {
|
|
127
|
+
if (typeof value !== 'string')
|
|
128
|
+
return value;
|
|
129
|
+
try {
|
|
130
|
+
return JSON.parse(value);
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
context.addIssue({ code: 'custom', message: `${field.label} must be valid JSON` });
|
|
134
|
+
return z.NEVER;
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
break;
|
|
138
|
+
case 'phone':
|
|
139
|
+
schema = z.string().regex(/^[+\d\s()-]*$/, `${field.label} must be a valid phone number`);
|
|
140
|
+
break;
|
|
141
|
+
case 'file':
|
|
142
|
+
case 'image':
|
|
143
|
+
return singleFileFieldToZod(field, {
|
|
144
|
+
required: field.required === true && (mode === 'create' || withinArray),
|
|
145
|
+
allowReference: mode === 'edit' && withinArray,
|
|
146
|
+
});
|
|
147
|
+
case 'images':
|
|
148
|
+
return multipleFilesFieldToZod(field, {
|
|
149
|
+
required: field.required === true && (mode === 'create' || withinArray),
|
|
150
|
+
allowReference: mode === 'edit' && withinArray,
|
|
151
|
+
});
|
|
152
|
+
default:
|
|
153
|
+
schema = z.string();
|
|
154
|
+
}
|
|
155
|
+
// Enforce meaningful values for required fields, including nested array rows.
|
|
156
|
+
if (field.required) {
|
|
157
|
+
schema = schema.refine(hasRequiredValue, { message: `${field.label} is required` });
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
schema = schema.optional().or(z.literal(''));
|
|
161
|
+
}
|
|
162
|
+
return schema;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Generate a Zod object schema from a ResourceDefinition's fields.
|
|
166
|
+
* Only includes fields that are relevant for form rendering.
|
|
167
|
+
*
|
|
168
|
+
* @param mode - 'create' | 'edit' to filter fields by showInCreate / showInEdit
|
|
169
|
+
*/
|
|
170
|
+
export function fieldsToZodSchema(fields, mode = 'create') {
|
|
171
|
+
const shape = {};
|
|
172
|
+
for (const field of fields) {
|
|
173
|
+
// Skip fields not shown in forms
|
|
174
|
+
if (field.showInForm === false)
|
|
175
|
+
continue;
|
|
176
|
+
if (mode === 'create' && field.showInCreate === false)
|
|
177
|
+
continue;
|
|
178
|
+
if (mode === 'edit' && field.showInEdit === false)
|
|
179
|
+
continue;
|
|
180
|
+
shape[field.key] = fieldToZod(field, mode);
|
|
181
|
+
}
|
|
182
|
+
return z.object(shape);
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Generate a Zod schema from a ResourceDefinition.
|
|
186
|
+
* Convenience wrapper around fieldsToZodSchema.
|
|
187
|
+
*/
|
|
188
|
+
export function resourceToZodSchema(resource, mode = 'create') {
|
|
189
|
+
return fieldsToZodSchema(resource.fields, mode);
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Determine a conservative HTML input type for server-rendered forms.
|
|
193
|
+
* Text fallbacks avoid inconsistent native validation and date widgets.
|
|
194
|
+
*/
|
|
195
|
+
export function fieldToInputType(field) {
|
|
196
|
+
switch (field.type) {
|
|
197
|
+
case 'number': return 'text';
|
|
198
|
+
case 'email': return 'text';
|
|
199
|
+
case 'url': return 'text';
|
|
200
|
+
case 'phone': return 'tel';
|
|
201
|
+
case 'boolean': return 'checkbox';
|
|
202
|
+
case 'date': return 'text';
|
|
203
|
+
case 'textarea':
|
|
204
|
+
case 'richtext': return 'textarea';
|
|
205
|
+
case 'select':
|
|
206
|
+
case 'multiselect': return 'select';
|
|
207
|
+
case 'array': return 'text';
|
|
208
|
+
case 'image':
|
|
209
|
+
case 'images':
|
|
210
|
+
case 'file': return 'file';
|
|
211
|
+
default: return 'text';
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Generate an input placeholder with a portable format hint.
|
|
216
|
+
*/
|
|
217
|
+
export function fieldToPlaceholder(field) {
|
|
218
|
+
switch (field.type) {
|
|
219
|
+
case 'date': return 'YYYY-MM-DD';
|
|
220
|
+
case 'email': return 'user@example.com';
|
|
221
|
+
case 'url': return 'https://example.com';
|
|
222
|
+
case 'phone': return '+1 (555) 000-0000';
|
|
223
|
+
case 'number': return '0';
|
|
224
|
+
default: return '';
|
|
225
|
+
}
|
|
226
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert untrusted display values to text. Callers must render the result with
|
|
3
|
+
* normal Svelte interpolation so the framework escapes markup characters.
|
|
4
|
+
*/
|
|
5
|
+
export declare function toSafeText(value: unknown, fallback?: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* Convert an untrusted value into a navigation-safe href.
|
|
8
|
+
*
|
|
9
|
+
* Relative references are preserved. Absolute references are limited to
|
|
10
|
+
* http(s), mailto, tel, and browser-created http(s) blob URLs. Any control or
|
|
11
|
+
* invisible character fails closed before scheme parsing so browsers cannot
|
|
12
|
+
* normalize an executable protocol after validation.
|
|
13
|
+
*/
|
|
14
|
+
export declare function toSafeHref(value: unknown): string | undefined;
|
package/dist/security.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert untrusted display values to text. Callers must render the result with
|
|
3
|
+
* normal Svelte interpolation so the framework escapes markup characters.
|
|
4
|
+
*/
|
|
5
|
+
export function toSafeText(value, fallback = '') {
|
|
6
|
+
return value === null || value === undefined ? fallback : String(value);
|
|
7
|
+
}
|
|
8
|
+
const URI_SCHEME = /^([a-z][a-z\d+.-]*):/iu;
|
|
9
|
+
const SAFE_HREF_SCHEMES = new Set(['http', 'https', 'mailto', 'tel']);
|
|
10
|
+
function hasUnsafeHrefCharacters(value) {
|
|
11
|
+
for (const character of value) {
|
|
12
|
+
const codePoint = character.codePointAt(0) ?? 0;
|
|
13
|
+
if (codePoint <= 0x1f
|
|
14
|
+
|| (codePoint >= 0x7f && codePoint <= 0x9f)
|
|
15
|
+
|| (codePoint >= 0x200b && codePoint <= 0x200f)
|
|
16
|
+
|| (codePoint >= 0x2028 && codePoint <= 0x202e)
|
|
17
|
+
|| codePoint === 0x2060
|
|
18
|
+
|| codePoint === 0xfeff) {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Convert an untrusted value into a navigation-safe href.
|
|
26
|
+
*
|
|
27
|
+
* Relative references are preserved. Absolute references are limited to
|
|
28
|
+
* http(s), mailto, tel, and browser-created http(s) blob URLs. Any control or
|
|
29
|
+
* invisible character fails closed before scheme parsing so browsers cannot
|
|
30
|
+
* normalize an executable protocol after validation.
|
|
31
|
+
*/
|
|
32
|
+
export function toSafeHref(value) {
|
|
33
|
+
if (value === null || value === undefined)
|
|
34
|
+
return undefined;
|
|
35
|
+
const href = String(value).trim();
|
|
36
|
+
if (!href || hasUnsafeHrefCharacters(href))
|
|
37
|
+
return undefined;
|
|
38
|
+
const scheme = URI_SCHEME.exec(href)?.[1]?.toLowerCase();
|
|
39
|
+
if (!scheme)
|
|
40
|
+
return href;
|
|
41
|
+
if (scheme === 'blob') {
|
|
42
|
+
try {
|
|
43
|
+
const blobUrl = new URL(href);
|
|
44
|
+
const innerProtocol = new URL(blobUrl.pathname).protocol;
|
|
45
|
+
return innerProtocol === 'http:' || innerProtocol === 'https:' ? href : undefined;
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (!SAFE_HREF_SCHEMES.has(scheme))
|
|
52
|
+
return undefined;
|
|
53
|
+
try {
|
|
54
|
+
return new URL(href).protocol === `${scheme}:` ? href : undefined;
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @svadmin/lite — Server Adapter
|
|
3
|
+
*
|
|
4
|
+
* Bridges @svadmin/core DataProvider into SvelteKit server loaders and form actions.
|
|
5
|
+
* All data fetching happens on the server; optional client-side enhancement is
|
|
6
|
+
* limited to interaction affordances such as dynamic array rows.
|
|
7
|
+
*/
|
|
8
|
+
import type { DataProvider, AuthProvider, ResourceDefinition } from '@svadmin/core';
|
|
9
|
+
import { type RequestEvent } from '@sveltejs/kit';
|
|
10
|
+
export interface ListLoaderResult {
|
|
11
|
+
records: Record<string, unknown>[];
|
|
12
|
+
total: number;
|
|
13
|
+
page: number;
|
|
14
|
+
pageSize: number;
|
|
15
|
+
totalPages: number;
|
|
16
|
+
sort?: string;
|
|
17
|
+
order?: 'asc' | 'desc';
|
|
18
|
+
search?: string;
|
|
19
|
+
resource: ResourceDefinition;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Creates a SvelteKit `load` function that fetches a resource list
|
|
23
|
+
* via the DataProvider. All state is driven by URL search params.
|
|
24
|
+
*/
|
|
25
|
+
export declare function createListLoader(dp: DataProvider, resource: ResourceDefinition): ({ url }: {
|
|
26
|
+
url: URL;
|
|
27
|
+
}) => Promise<ListLoaderResult>;
|
|
28
|
+
export declare function createDetailLoader(dp: DataProvider, resource: ResourceDefinition): ({ params }: {
|
|
29
|
+
params: {
|
|
30
|
+
id: string;
|
|
31
|
+
};
|
|
32
|
+
}) => Promise<{
|
|
33
|
+
record: Record<string, unknown>;
|
|
34
|
+
resource: ResourceDefinition;
|
|
35
|
+
}>;
|
|
36
|
+
/**
|
|
37
|
+
* Creates SvelteKit form actions for create / update / delete.
|
|
38
|
+
* Works with standard `<form method="POST">` submissions — no JS needed.
|
|
39
|
+
*/
|
|
40
|
+
export declare function createCrudActions(dp: DataProvider, resource: ResourceDefinition): {
|
|
41
|
+
create: ({ request }: RequestEvent) => Promise<{
|
|
42
|
+
success: false;
|
|
43
|
+
error: string;
|
|
44
|
+
values: Record<string, unknown>;
|
|
45
|
+
errors: Record<string, string[]>;
|
|
46
|
+
} | {
|
|
47
|
+
success: boolean;
|
|
48
|
+
id: unknown;
|
|
49
|
+
error?: undefined;
|
|
50
|
+
values?: undefined;
|
|
51
|
+
} | {
|
|
52
|
+
success: boolean;
|
|
53
|
+
error: string;
|
|
54
|
+
values: Record<string, unknown>;
|
|
55
|
+
id?: undefined;
|
|
56
|
+
}>;
|
|
57
|
+
update: ({ request }: RequestEvent) => Promise<{
|
|
58
|
+
success: false;
|
|
59
|
+
error: string;
|
|
60
|
+
values: Record<string, unknown>;
|
|
61
|
+
errors: Record<string, string[]>;
|
|
62
|
+
} | {
|
|
63
|
+
success: boolean;
|
|
64
|
+
error?: undefined;
|
|
65
|
+
values?: undefined;
|
|
66
|
+
} | {
|
|
67
|
+
success: boolean;
|
|
68
|
+
error: string;
|
|
69
|
+
values: Record<string, unknown>;
|
|
70
|
+
}>;
|
|
71
|
+
delete: ({ request }: RequestEvent) => Promise<{
|
|
72
|
+
success: boolean;
|
|
73
|
+
error?: undefined;
|
|
74
|
+
} | {
|
|
75
|
+
success: boolean;
|
|
76
|
+
error: string;
|
|
77
|
+
}>;
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Creates a SvelteKit server hook that checks auth via AuthProvider
|
|
81
|
+
* and redirects unauthenticated users to a login page.
|
|
82
|
+
*/
|
|
83
|
+
export declare function createAuthGuard(authProvider: AuthProvider, loginPath?: string): ({ event, resolve }: {
|
|
84
|
+
event: RequestEvent;
|
|
85
|
+
resolve: (event: RequestEvent) => Promise<Response>;
|
|
86
|
+
}) => Promise<Response>;
|
|
87
|
+
/**
|
|
88
|
+
* Creates login/logout form actions using AuthProvider.
|
|
89
|
+
*/
|
|
90
|
+
export declare function createAuthActions(authProvider: AuthProvider): {
|
|
91
|
+
login: ({ request, cookies }: RequestEvent) => Promise<{
|
|
92
|
+
success: boolean;
|
|
93
|
+
error: string;
|
|
94
|
+
}>;
|
|
95
|
+
logout: ({ cookies }: RequestEvent) => Promise<{
|
|
96
|
+
success: boolean;
|
|
97
|
+
}>;
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* Detects IE11 user agents for applications that maintain a dedicated fallback.
|
|
101
|
+
* This helper does not imply that Svelte 5 or the consuming app supports IE11.
|
|
102
|
+
*/
|
|
103
|
+
export declare function isLegacyBrowser(userAgent: string): boolean;
|
|
104
|
+
/**
|
|
105
|
+
* Creates an opt-in SvelteKit hook that redirects detected IE11 user agents.
|
|
106
|
+
* Consumers remain responsible for their own transpilation and browser support.
|
|
107
|
+
*/
|
|
108
|
+
export declare function createLegacyRedirectHook(litePrefix?: string): ({ event, resolve }: {
|
|
109
|
+
event: RequestEvent;
|
|
110
|
+
resolve: (event: RequestEvent) => Promise<Response>;
|
|
111
|
+
}) => Promise<Response>;
|