dalila 1.5.0 → 1.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/index.d.ts +0 -1
- package/dist/core/index.js +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/router/route-tables.d.ts +2 -2
- package/dist/router/router.js +1 -1
- package/dist/router/validation.d.ts +9 -0
- package/dist/router/validation.js +178 -0
- package/package.json +1 -5
- package/scripts/dev-server.cjs +0 -1
package/dist/core/index.d.ts
CHANGED
package/dist/core/index.js
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Scope } from '../core/scope.js';
|
|
2
|
-
import type { Rule } from '
|
|
2
|
+
import type { Rule } from './validation.js';
|
|
3
3
|
/** Options for programmatic navigation. */
|
|
4
4
|
export interface NavigateOptions {
|
|
5
5
|
replace?: boolean;
|
|
@@ -22,7 +22,7 @@ export type RouteQueryValues = Record<string, string | string[]>;
|
|
|
22
22
|
export type RouteParamValidationValue = RouteParamValue | undefined;
|
|
23
23
|
export type RouteQueryValidationSchema = Record<string, Array<Rule<RouteQueryValue, RouteQueryValues>>>;
|
|
24
24
|
export type RouteParamsValidationSchema = Record<string, Array<Rule<RouteParamValidationValue, RouteParamsValues>>>;
|
|
25
|
-
/** Schema for params/query validation (
|
|
25
|
+
/** Schema for params/query validation (router built-in rule format). */
|
|
26
26
|
export interface RouteValidationConfig {
|
|
27
27
|
query?: RouteQueryValidationSchema;
|
|
28
28
|
params?: RouteParamsValidationSchema;
|
package/dist/router/router.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { signal } from '../core/signal.js';
|
|
2
2
|
import { createScope, withScope, withScopeAsync } from '../core/scope.js';
|
|
3
|
-
import { normalizeRules, validateValue } from '
|
|
3
|
+
import { normalizeRules, validateValue } from './validation.js';
|
|
4
4
|
import { compileRoutes, findCompiledRouteStackResult, normalizePath } from './route-tables.js';
|
|
5
5
|
/** Singleton reference to the most recently created router. */
|
|
6
6
|
let currentRouter = null;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type RuleFn<TValue = unknown, TValues = Record<string, unknown>> = (value: TValue, values: TValues) => string | boolean | null | undefined;
|
|
2
|
+
export interface RuleConfig<TValue = unknown, TValues = Record<string, unknown>> {
|
|
3
|
+
rule: string | RuleFn<TValue, TValues>;
|
|
4
|
+
value?: unknown;
|
|
5
|
+
message?: string;
|
|
6
|
+
}
|
|
7
|
+
export type Rule<TValue = unknown, TValues = Record<string, unknown>> = string | RuleFn<TValue, TValues> | RuleConfig<TValue, TValues>;
|
|
8
|
+
export declare function normalizeRules<TValue = unknown, TValues = Record<string, unknown>>(rules: Array<Rule<TValue, TValues>>, _field?: string): Array<RuleFn<TValue, TValues>>;
|
|
9
|
+
export declare function validateValue<TValue = unknown, TValues = Record<string, unknown>>(value: TValue, values: TValues, validators: Array<RuleFn<TValue, TValues>>, _field?: string): string | null;
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
function hasValue(value) {
|
|
2
|
+
if (value === null || value === undefined)
|
|
3
|
+
return false;
|
|
4
|
+
if (typeof value === 'string')
|
|
5
|
+
return value.trim().length > 0;
|
|
6
|
+
if (Array.isArray(value))
|
|
7
|
+
return value.length > 0;
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
function toLength(value) {
|
|
11
|
+
if (typeof value === 'string' || Array.isArray(value))
|
|
12
|
+
return value.length;
|
|
13
|
+
if (typeof value === 'number')
|
|
14
|
+
return value;
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
function toNumericOrLength(value) {
|
|
18
|
+
if (typeof value === 'number') {
|
|
19
|
+
return Number.isFinite(value) ? value : null;
|
|
20
|
+
}
|
|
21
|
+
if (typeof value === 'string') {
|
|
22
|
+
const trimmed = value.trim();
|
|
23
|
+
if (trimmed.length === 0)
|
|
24
|
+
return 0;
|
|
25
|
+
const numeric = Number(trimmed);
|
|
26
|
+
if (Number.isFinite(numeric))
|
|
27
|
+
return numeric;
|
|
28
|
+
return value.length;
|
|
29
|
+
}
|
|
30
|
+
if (Array.isArray(value)) {
|
|
31
|
+
return value.length;
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
function toNumber(value) {
|
|
36
|
+
if (typeof value === 'number')
|
|
37
|
+
return Number.isFinite(value) ? value : null;
|
|
38
|
+
if (typeof value === 'string') {
|
|
39
|
+
const parsed = Number(value);
|
|
40
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
function parseRuleName(input) {
|
|
45
|
+
const separator = input.indexOf(':');
|
|
46
|
+
if (separator === -1) {
|
|
47
|
+
return { name: input.trim(), arg: undefined };
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
name: input.slice(0, separator).trim(),
|
|
51
|
+
arg: input.slice(separator + 1).trim()
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function normalizeInValues(value) {
|
|
55
|
+
if (Array.isArray(value))
|
|
56
|
+
return value.map(v => String(v));
|
|
57
|
+
if (typeof value === 'string')
|
|
58
|
+
return value.split(',').map(v => v.trim()).filter(Boolean);
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
function buildBuiltinRule(name, rawArg, message) {
|
|
62
|
+
const fail = (fallback) => message ?? fallback;
|
|
63
|
+
switch (name) {
|
|
64
|
+
case 'required':
|
|
65
|
+
return value => (hasValue(value) ? undefined : fail('This field is required.'));
|
|
66
|
+
case 'min': {
|
|
67
|
+
const min = toNumber(rawArg);
|
|
68
|
+
if (min === null)
|
|
69
|
+
return () => undefined;
|
|
70
|
+
return value => {
|
|
71
|
+
const size = toNumericOrLength(value);
|
|
72
|
+
if (size === null)
|
|
73
|
+
return undefined;
|
|
74
|
+
return size >= min ? undefined : fail(`Must be at least ${min}.`);
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
case 'max': {
|
|
78
|
+
const max = toNumber(rawArg);
|
|
79
|
+
if (max === null)
|
|
80
|
+
return () => undefined;
|
|
81
|
+
return value => {
|
|
82
|
+
const size = toNumericOrLength(value);
|
|
83
|
+
if (size === null)
|
|
84
|
+
return undefined;
|
|
85
|
+
return size <= max ? undefined : fail(`Must be at most ${max}.`);
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
case 'pattern': {
|
|
89
|
+
const pattern = typeof rawArg === 'string' ? rawArg : String(rawArg ?? '');
|
|
90
|
+
if (!pattern)
|
|
91
|
+
return () => undefined;
|
|
92
|
+
let regex;
|
|
93
|
+
try {
|
|
94
|
+
regex = new RegExp(pattern);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
return () => undefined;
|
|
98
|
+
}
|
|
99
|
+
return value => {
|
|
100
|
+
if (value === undefined || value === null)
|
|
101
|
+
return undefined;
|
|
102
|
+
return regex.test(String(value)) ? undefined : fail('Invalid format.');
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
case 'email': {
|
|
106
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
107
|
+
return value => {
|
|
108
|
+
if (value === undefined || value === null || value === '')
|
|
109
|
+
return undefined;
|
|
110
|
+
return emailRegex.test(String(value)) ? undefined : fail('Invalid email.');
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
case 'in': {
|
|
114
|
+
const allowed = normalizeInValues(rawArg);
|
|
115
|
+
if (allowed.length === 0)
|
|
116
|
+
return () => undefined;
|
|
117
|
+
return value => {
|
|
118
|
+
if (value === undefined || value === null)
|
|
119
|
+
return undefined;
|
|
120
|
+
return allowed.includes(String(value)) ? undefined : fail(`Must be one of: ${allowed.join(', ')}.`);
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
default:
|
|
124
|
+
return () => undefined;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
export function normalizeRules(rules, _field) {
|
|
128
|
+
const normalized = [];
|
|
129
|
+
for (const rule of rules) {
|
|
130
|
+
if (typeof rule === 'function') {
|
|
131
|
+
normalized.push(rule);
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (typeof rule === 'string') {
|
|
135
|
+
const { name, arg } = parseRuleName(rule);
|
|
136
|
+
if (!name)
|
|
137
|
+
continue;
|
|
138
|
+
normalized.push(buildBuiltinRule(name, arg));
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (!rule || typeof rule !== 'object') {
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
if (typeof rule.rule === 'function') {
|
|
145
|
+
const fn = rule.rule;
|
|
146
|
+
normalized.push((value, values) => {
|
|
147
|
+
const result = fn(value, values);
|
|
148
|
+
if (result === true || result === undefined || result === null)
|
|
149
|
+
return undefined;
|
|
150
|
+
if (result === false)
|
|
151
|
+
return rule.message ?? 'Invalid value.';
|
|
152
|
+
return typeof result === 'string' ? result : undefined;
|
|
153
|
+
});
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
if (typeof rule.rule === 'string') {
|
|
157
|
+
normalized.push(buildBuiltinRule(rule.rule, rule.value, rule.message));
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return normalized;
|
|
161
|
+
}
|
|
162
|
+
export function validateValue(value, values, validators, _field) {
|
|
163
|
+
for (const validator of validators) {
|
|
164
|
+
const result = validator(value, values);
|
|
165
|
+
if (result === true || result === undefined || result === null) {
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
if (result === false) {
|
|
169
|
+
return 'Invalid value.';
|
|
170
|
+
}
|
|
171
|
+
if (typeof result === 'string') {
|
|
172
|
+
const message = result.trim();
|
|
173
|
+
if (message)
|
|
174
|
+
return message;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return null;
|
|
178
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dalila",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.2",
|
|
4
4
|
"description": "DOM-first reactive framework based on signals",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -30,10 +30,6 @@
|
|
|
30
30
|
"types": "./dist/runtime/index.d.ts",
|
|
31
31
|
"default": "./dist/runtime/index.js"
|
|
32
32
|
},
|
|
33
|
-
"./form": {
|
|
34
|
-
"types": "./dist/form/index.d.ts",
|
|
35
|
-
"default": "./dist/form/index.js"
|
|
36
|
-
},
|
|
37
33
|
"./router": {
|
|
38
34
|
"types": "./dist/router/index.d.ts",
|
|
39
35
|
"default": "./dist/router/index.js"
|
package/scripts/dev-server.cjs
CHANGED
|
@@ -287,7 +287,6 @@ function injectBindings(html, requestPath) {
|
|
|
287
287
|
"dalila/core": "${dalilaPath}/core/index.js",
|
|
288
288
|
"dalila/context": "${dalilaPath}/context/index.js",
|
|
289
289
|
"dalila/context/raw": "${dalilaPath}/context/raw.js",
|
|
290
|
-
"dalila/form": "${dalilaPath}/form/index.js",
|
|
291
290
|
"dalila/runtime": "${dalilaPath}/runtime/index.js",
|
|
292
291
|
"dalila/router": "${dalilaPath}/router/index.js",
|
|
293
292
|
"@/": "/src/"
|