@vertz/ui 0.2.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 +1138 -0
- package/dist/css/public.d.ts +157 -0
- package/dist/css/public.js +18 -0
- package/dist/form/public.d.ts +111 -0
- package/dist/form/public.js +11 -0
- package/dist/index.d.ts +700 -0
- package/dist/index.js +261 -0
- package/dist/internals.d.ts +414 -0
- package/dist/internals.js +268 -0
- package/dist/jsx-runtime/index.d.ts +40 -0
- package/dist/jsx-runtime/index.js +50 -0
- package/dist/query/public.d.ts +58 -0
- package/dist/query/public.js +7 -0
- package/dist/router/public.d.ts +214 -0
- package/dist/router/public.js +21 -0
- package/dist/shared/chunk-bp3v6s9j.js +62 -0
- package/dist/shared/chunk-d8h2eh8d.js +141 -0
- package/dist/shared/chunk-f1ynwam4.js +872 -0
- package/dist/shared/chunk-j8vzvne3.js +153 -0
- package/dist/shared/chunk-pgymxpn1.js +308 -0
- package/dist/shared/chunk-tsdpgmks.js +98 -0
- package/dist/shared/chunk-xd9d7q5p.js +115 -0
- package/dist/shared/chunk-zbbvx05f.js +202 -0
- package/dist/test/index.d.ts +268 -0
- package/dist/test/index.js +236 -0
- package/package.json +76 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
// src/router/matcher.ts
|
|
2
|
+
function splitPath(path) {
|
|
3
|
+
const trimmed = path.replace(/^\/+|\/+$/g, "");
|
|
4
|
+
if (trimmed === "")
|
|
5
|
+
return [];
|
|
6
|
+
return trimmed.split("/");
|
|
7
|
+
}
|
|
8
|
+
function matchPath(pattern, path) {
|
|
9
|
+
const patternSegments = splitPath(pattern);
|
|
10
|
+
const pathSegments = splitPath(path);
|
|
11
|
+
const params = {};
|
|
12
|
+
for (let i = 0;i < patternSegments.length; i++) {
|
|
13
|
+
const seg = patternSegments[i];
|
|
14
|
+
if (seg === "*") {
|
|
15
|
+
params["*"] = pathSegments.slice(i).join("/");
|
|
16
|
+
return { params, path };
|
|
17
|
+
}
|
|
18
|
+
if (i >= pathSegments.length) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
const pathSeg = pathSegments[i];
|
|
22
|
+
if (seg.startsWith(":")) {
|
|
23
|
+
if (pathSeg === "")
|
|
24
|
+
return null;
|
|
25
|
+
params[seg.slice(1)] = pathSeg;
|
|
26
|
+
} else {
|
|
27
|
+
if (seg !== pathSeg)
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (pathSegments.length > patternSegments.length) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
return { params, path };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// src/router/define-routes.ts
|
|
38
|
+
function defineRoutes(map) {
|
|
39
|
+
const routes = [];
|
|
40
|
+
for (const [pattern, config] of Object.entries(map)) {
|
|
41
|
+
const compiled = {
|
|
42
|
+
component: config.component,
|
|
43
|
+
errorComponent: config.errorComponent,
|
|
44
|
+
loader: config.loader,
|
|
45
|
+
pattern,
|
|
46
|
+
searchParams: config.searchParams
|
|
47
|
+
};
|
|
48
|
+
if (config.children) {
|
|
49
|
+
compiled.children = defineRoutes(config.children);
|
|
50
|
+
}
|
|
51
|
+
routes.push(compiled);
|
|
52
|
+
}
|
|
53
|
+
return routes;
|
|
54
|
+
}
|
|
55
|
+
function matchRoute(routes, url) {
|
|
56
|
+
const [pathname, queryString] = splitUrl(url);
|
|
57
|
+
const searchParams = new URLSearchParams(queryString);
|
|
58
|
+
const matched = [];
|
|
59
|
+
const allParams = {};
|
|
60
|
+
const leaf = matchRouteRecursive(routes, pathname, matched, allParams);
|
|
61
|
+
if (!leaf)
|
|
62
|
+
return null;
|
|
63
|
+
let search = {};
|
|
64
|
+
for (const m of matched) {
|
|
65
|
+
if (m.route.searchParams) {
|
|
66
|
+
const raw = {};
|
|
67
|
+
for (const [key, value] of searchParams.entries()) {
|
|
68
|
+
raw[key] = value;
|
|
69
|
+
}
|
|
70
|
+
search = m.route.searchParams.parse(raw);
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
matched,
|
|
76
|
+
params: allParams,
|
|
77
|
+
route: leaf,
|
|
78
|
+
search,
|
|
79
|
+
searchParams
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function splitUrl(url) {
|
|
83
|
+
const qIndex = url.indexOf("?");
|
|
84
|
+
if (qIndex === -1)
|
|
85
|
+
return [url, ""];
|
|
86
|
+
return [url.slice(0, qIndex), url.slice(qIndex + 1)];
|
|
87
|
+
}
|
|
88
|
+
function matchRouteRecursive(routes, pathname, matched, allParams) {
|
|
89
|
+
for (const route of routes) {
|
|
90
|
+
if (route.children && route.children.length > 0) {
|
|
91
|
+
const prefixResult = matchPrefix(route.pattern, pathname);
|
|
92
|
+
if (prefixResult) {
|
|
93
|
+
matched.push({ params: prefixResult.params, route });
|
|
94
|
+
Object.assign(allParams, prefixResult.params);
|
|
95
|
+
const childResult = matchRouteRecursive(route.children, prefixResult.remaining, matched, allParams);
|
|
96
|
+
if (childResult)
|
|
97
|
+
return childResult;
|
|
98
|
+
matched.pop();
|
|
99
|
+
for (const key of Object.keys(prefixResult.params)) {
|
|
100
|
+
delete allParams[key];
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
} else {
|
|
104
|
+
const result = matchPath(route.pattern, pathname);
|
|
105
|
+
if (result) {
|
|
106
|
+
matched.push({ params: result.params, route });
|
|
107
|
+
Object.assign(allParams, result.params);
|
|
108
|
+
return route;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
function matchPrefix(pattern, pathname) {
|
|
115
|
+
const patternSegments = splitSegments(pattern);
|
|
116
|
+
const pathSegments = splitSegments(pathname);
|
|
117
|
+
if (patternSegments.length > pathSegments.length)
|
|
118
|
+
return null;
|
|
119
|
+
const params = {};
|
|
120
|
+
for (let i = 0;i < patternSegments.length; i++) {
|
|
121
|
+
const pSeg = patternSegments[i];
|
|
122
|
+
const uSeg = pathSegments[i];
|
|
123
|
+
if (pSeg.startsWith(":")) {
|
|
124
|
+
if (uSeg === "")
|
|
125
|
+
return null;
|
|
126
|
+
params[pSeg.slice(1)] = uSeg;
|
|
127
|
+
} else if (pSeg !== uSeg) {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
const remainingSegments = pathSegments.slice(patternSegments.length);
|
|
132
|
+
const remaining = `/${remainingSegments.join("/")}`;
|
|
133
|
+
return { params, remaining };
|
|
134
|
+
}
|
|
135
|
+
function splitSegments(path) {
|
|
136
|
+
const trimmed = path.replace(/^\/+|\/+$/g, "");
|
|
137
|
+
if (trimmed === "")
|
|
138
|
+
return [];
|
|
139
|
+
return trimmed.split("/");
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// src/router/loader.ts
|
|
143
|
+
async function executeLoaders(matched, params, signal) {
|
|
144
|
+
const loaderSignal = signal ?? new AbortController().signal;
|
|
145
|
+
const promises = matched.map((m) => {
|
|
146
|
+
if (!m.route.loader)
|
|
147
|
+
return Promise.resolve(undefined);
|
|
148
|
+
return Promise.resolve(m.route.loader({ params, signal: loaderSignal }));
|
|
149
|
+
});
|
|
150
|
+
return Promise.all(promises);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export { matchPath, defineRoutes, matchRoute, executeLoaders };
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
// src/component/context.ts
|
|
2
|
+
var currentScope = null;
|
|
3
|
+
function asKey(ctx) {
|
|
4
|
+
return ctx;
|
|
5
|
+
}
|
|
6
|
+
function createContext(defaultValue) {
|
|
7
|
+
const ctx = {
|
|
8
|
+
Provider(value, fn) {
|
|
9
|
+
const parentScope = currentScope;
|
|
10
|
+
const scope = parentScope ? new Map(parentScope) : new Map;
|
|
11
|
+
scope.set(asKey(ctx), value);
|
|
12
|
+
ctx._stack.push(value);
|
|
13
|
+
const prevScope = currentScope;
|
|
14
|
+
currentScope = scope;
|
|
15
|
+
try {
|
|
16
|
+
fn();
|
|
17
|
+
} finally {
|
|
18
|
+
ctx._stack.pop();
|
|
19
|
+
currentScope = prevScope;
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
_default: defaultValue,
|
|
23
|
+
_stack: []
|
|
24
|
+
};
|
|
25
|
+
return ctx;
|
|
26
|
+
}
|
|
27
|
+
function useContext(ctx) {
|
|
28
|
+
if (ctx._stack.length > 0) {
|
|
29
|
+
return ctx._stack[ctx._stack.length - 1];
|
|
30
|
+
}
|
|
31
|
+
const key = asKey(ctx);
|
|
32
|
+
if (currentScope?.has(key)) {
|
|
33
|
+
return currentScope.get(key);
|
|
34
|
+
}
|
|
35
|
+
return ctx._default;
|
|
36
|
+
}
|
|
37
|
+
function getContextScope() {
|
|
38
|
+
return currentScope;
|
|
39
|
+
}
|
|
40
|
+
function setContextScope(scope) {
|
|
41
|
+
const prev = currentScope;
|
|
42
|
+
currentScope = scope;
|
|
43
|
+
return prev;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// src/runtime/disposal.ts
|
|
47
|
+
class DisposalScopeError extends Error {
|
|
48
|
+
constructor() {
|
|
49
|
+
super("onCleanup() must be called within a disposal scope (e.g., inside effect(), watch(), onMount(), or a pushScope()/popScope() block). " + "Called outside a scope, the cleanup callback would be silently discarded.");
|
|
50
|
+
this.name = "DisposalScopeError";
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
var cleanupStack = [];
|
|
54
|
+
function onCleanup(fn) {
|
|
55
|
+
const current = cleanupStack[cleanupStack.length - 1];
|
|
56
|
+
if (!current) {
|
|
57
|
+
throw new DisposalScopeError;
|
|
58
|
+
}
|
|
59
|
+
current.push(fn);
|
|
60
|
+
}
|
|
61
|
+
function _tryOnCleanup(fn) {
|
|
62
|
+
const current = cleanupStack[cleanupStack.length - 1];
|
|
63
|
+
if (current) {
|
|
64
|
+
current.push(fn);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function pushScope() {
|
|
68
|
+
const scope = [];
|
|
69
|
+
cleanupStack.push(scope);
|
|
70
|
+
return scope;
|
|
71
|
+
}
|
|
72
|
+
function popScope() {
|
|
73
|
+
cleanupStack.pop();
|
|
74
|
+
}
|
|
75
|
+
function runCleanups(cleanups) {
|
|
76
|
+
for (let i = cleanups.length - 1;i >= 0; i--) {
|
|
77
|
+
cleanups[i]?.();
|
|
78
|
+
}
|
|
79
|
+
cleanups.length = 0;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// src/runtime/scheduler.ts
|
|
83
|
+
var batchDepth = 0;
|
|
84
|
+
var pendingEffects = new Map;
|
|
85
|
+
function scheduleNotify(subscriber) {
|
|
86
|
+
if (!subscriber._isEffect) {
|
|
87
|
+
subscriber._notify();
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
if (batchDepth > 0) {
|
|
91
|
+
pendingEffects.set(subscriber._id, subscriber);
|
|
92
|
+
} else {
|
|
93
|
+
subscriber._notify();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function flush() {
|
|
97
|
+
while (pendingEffects.size > 0) {
|
|
98
|
+
const queue = [...pendingEffects.values()];
|
|
99
|
+
pendingEffects.clear();
|
|
100
|
+
for (const sub of queue) {
|
|
101
|
+
sub._notify();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function batch(fn) {
|
|
106
|
+
batchDepth++;
|
|
107
|
+
try {
|
|
108
|
+
fn();
|
|
109
|
+
} finally {
|
|
110
|
+
batchDepth--;
|
|
111
|
+
if (batchDepth === 0) {
|
|
112
|
+
flush();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// src/runtime/tracking.ts
|
|
118
|
+
var currentSubscriber = null;
|
|
119
|
+
var readValueCallback = null;
|
|
120
|
+
function getSubscriber() {
|
|
121
|
+
return currentSubscriber;
|
|
122
|
+
}
|
|
123
|
+
function setSubscriber(sub) {
|
|
124
|
+
const prev = currentSubscriber;
|
|
125
|
+
currentSubscriber = sub;
|
|
126
|
+
return prev;
|
|
127
|
+
}
|
|
128
|
+
function getReadValueCallback() {
|
|
129
|
+
return readValueCallback;
|
|
130
|
+
}
|
|
131
|
+
function setReadValueCallback(cb) {
|
|
132
|
+
const prev = readValueCallback;
|
|
133
|
+
readValueCallback = cb;
|
|
134
|
+
return prev;
|
|
135
|
+
}
|
|
136
|
+
function untrack(fn) {
|
|
137
|
+
const prev = currentSubscriber;
|
|
138
|
+
currentSubscriber = null;
|
|
139
|
+
try {
|
|
140
|
+
return fn();
|
|
141
|
+
} finally {
|
|
142
|
+
currentSubscriber = prev;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// src/runtime/signal.ts
|
|
147
|
+
var nextId = 0;
|
|
148
|
+
|
|
149
|
+
class SignalImpl {
|
|
150
|
+
_value;
|
|
151
|
+
_subscribers = new Set;
|
|
152
|
+
constructor(initial) {
|
|
153
|
+
this._value = initial;
|
|
154
|
+
}
|
|
155
|
+
get value() {
|
|
156
|
+
const sub = getSubscriber();
|
|
157
|
+
if (sub) {
|
|
158
|
+
this._subscribers.add(sub);
|
|
159
|
+
sub._addSource(this);
|
|
160
|
+
const cb = getReadValueCallback();
|
|
161
|
+
if (cb)
|
|
162
|
+
cb(this._value);
|
|
163
|
+
}
|
|
164
|
+
return this._value;
|
|
165
|
+
}
|
|
166
|
+
set value(newValue) {
|
|
167
|
+
if (Object.is(this._value, newValue)) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
this._value = newValue;
|
|
171
|
+
this._notifySubscribers();
|
|
172
|
+
}
|
|
173
|
+
peek() {
|
|
174
|
+
return this._value;
|
|
175
|
+
}
|
|
176
|
+
notify() {
|
|
177
|
+
this._notifySubscribers();
|
|
178
|
+
}
|
|
179
|
+
_notifySubscribers() {
|
|
180
|
+
batch(() => {
|
|
181
|
+
for (const sub of this._subscribers) {
|
|
182
|
+
scheduleNotify(sub);
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
function signal(initial) {
|
|
188
|
+
return new SignalImpl(initial);
|
|
189
|
+
}
|
|
190
|
+
class ComputedImpl {
|
|
191
|
+
_id;
|
|
192
|
+
_isEffect = false;
|
|
193
|
+
_fn;
|
|
194
|
+
_cachedValue;
|
|
195
|
+
_state = 1 /* Dirty */;
|
|
196
|
+
_subscribers = new Set;
|
|
197
|
+
_sources = new Set;
|
|
198
|
+
constructor(fn) {
|
|
199
|
+
this._id = nextId++;
|
|
200
|
+
this._fn = fn;
|
|
201
|
+
}
|
|
202
|
+
get value() {
|
|
203
|
+
const sub = getSubscriber();
|
|
204
|
+
if (sub) {
|
|
205
|
+
this._subscribers.add(sub);
|
|
206
|
+
sub._addSource(this);
|
|
207
|
+
}
|
|
208
|
+
if (this._state !== 0 /* Clean */) {
|
|
209
|
+
this._compute();
|
|
210
|
+
}
|
|
211
|
+
return this._cachedValue;
|
|
212
|
+
}
|
|
213
|
+
peek() {
|
|
214
|
+
if (this._state !== 0 /* Clean */) {
|
|
215
|
+
this._compute();
|
|
216
|
+
}
|
|
217
|
+
return this._cachedValue;
|
|
218
|
+
}
|
|
219
|
+
_addSource(source) {
|
|
220
|
+
this._sources.add(source);
|
|
221
|
+
}
|
|
222
|
+
_compute() {
|
|
223
|
+
this._state = 2 /* Computing */;
|
|
224
|
+
this._clearSources();
|
|
225
|
+
const prev = setSubscriber(this);
|
|
226
|
+
try {
|
|
227
|
+
const newValue = this._fn();
|
|
228
|
+
if (!Object.is(this._cachedValue, newValue)) {
|
|
229
|
+
this._cachedValue = newValue;
|
|
230
|
+
}
|
|
231
|
+
} finally {
|
|
232
|
+
setSubscriber(prev);
|
|
233
|
+
this._state = 0 /* Clean */;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
_clearSources() {
|
|
237
|
+
for (const source of this._sources) {
|
|
238
|
+
source._subscribers.delete(this);
|
|
239
|
+
}
|
|
240
|
+
this._sources.clear();
|
|
241
|
+
}
|
|
242
|
+
_notify() {
|
|
243
|
+
if (this._state === 1 /* Dirty */) {
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
this._state = 1 /* Dirty */;
|
|
247
|
+
for (const sub of this._subscribers) {
|
|
248
|
+
scheduleNotify(sub);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
function computed(fn) {
|
|
253
|
+
return new ComputedImpl(fn);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
class EffectImpl {
|
|
257
|
+
_id;
|
|
258
|
+
_isEffect = true;
|
|
259
|
+
_fn;
|
|
260
|
+
_disposed = false;
|
|
261
|
+
_sources = new Set;
|
|
262
|
+
_contextScope;
|
|
263
|
+
constructor(fn) {
|
|
264
|
+
this._id = nextId++;
|
|
265
|
+
this._fn = fn;
|
|
266
|
+
this._contextScope = getContextScope();
|
|
267
|
+
}
|
|
268
|
+
_addSource(source) {
|
|
269
|
+
this._sources.add(source);
|
|
270
|
+
}
|
|
271
|
+
_notify() {
|
|
272
|
+
if (this._disposed) {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
this._run();
|
|
276
|
+
}
|
|
277
|
+
_run() {
|
|
278
|
+
this._clearSources();
|
|
279
|
+
const prev = setSubscriber(this);
|
|
280
|
+
const prevCtx = setContextScope(this._contextScope);
|
|
281
|
+
try {
|
|
282
|
+
this._fn();
|
|
283
|
+
} finally {
|
|
284
|
+
setContextScope(prevCtx);
|
|
285
|
+
setSubscriber(prev);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
_clearSources() {
|
|
289
|
+
for (const source of this._sources) {
|
|
290
|
+
source._subscribers.delete(this);
|
|
291
|
+
}
|
|
292
|
+
this._sources.clear();
|
|
293
|
+
}
|
|
294
|
+
_dispose() {
|
|
295
|
+
this._disposed = true;
|
|
296
|
+
this._clearSources();
|
|
297
|
+
this._contextScope = null;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
function effect(fn) {
|
|
301
|
+
const eff = new EffectImpl(fn);
|
|
302
|
+
eff._run();
|
|
303
|
+
const dispose = () => eff._dispose();
|
|
304
|
+
_tryOnCleanup(dispose);
|
|
305
|
+
return dispose;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export { createContext, useContext, DisposalScopeError, onCleanup, _tryOnCleanup, pushScope, popScope, runCleanups, batch, setReadValueCallback, untrack, signal, computed, effect };
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import {
|
|
2
|
+
signal
|
|
3
|
+
} from "./chunk-pgymxpn1.js";
|
|
4
|
+
|
|
5
|
+
// src/form/form-data.ts
|
|
6
|
+
function formDataToObject(formData, options) {
|
|
7
|
+
const result = {};
|
|
8
|
+
const coerce = options?.coerce ?? false;
|
|
9
|
+
for (const [key, value] of formData.entries()) {
|
|
10
|
+
if (typeof value !== "string") {
|
|
11
|
+
continue;
|
|
12
|
+
}
|
|
13
|
+
result[key] = coerce ? coerceValue(value) : value;
|
|
14
|
+
}
|
|
15
|
+
return result;
|
|
16
|
+
}
|
|
17
|
+
function coerceValue(value) {
|
|
18
|
+
if (value === "true")
|
|
19
|
+
return true;
|
|
20
|
+
if (value === "false")
|
|
21
|
+
return false;
|
|
22
|
+
if (value !== "" && !Number.isNaN(Number(value))) {
|
|
23
|
+
return Number(value);
|
|
24
|
+
}
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// src/form/validation.ts
|
|
29
|
+
function validate(schema, data) {
|
|
30
|
+
try {
|
|
31
|
+
const parsed = schema.parse(data);
|
|
32
|
+
return { success: true, data: parsed, errors: {} };
|
|
33
|
+
} catch (err) {
|
|
34
|
+
if (err instanceof Error) {
|
|
35
|
+
const fieldErrors = err.fieldErrors;
|
|
36
|
+
if (fieldErrors && Object.keys(fieldErrors).length > 0) {
|
|
37
|
+
return { success: false, data: undefined, errors: fieldErrors };
|
|
38
|
+
}
|
|
39
|
+
return { success: false, data: undefined, errors: { _form: err.message } };
|
|
40
|
+
}
|
|
41
|
+
return { success: false, data: undefined, errors: { _form: "Validation failed" } };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// src/form/form.ts
|
|
46
|
+
function form(sdkMethod, options) {
|
|
47
|
+
const submitting = signal(false);
|
|
48
|
+
const errors = signal({});
|
|
49
|
+
return {
|
|
50
|
+
attrs() {
|
|
51
|
+
return {
|
|
52
|
+
action: sdkMethod.url,
|
|
53
|
+
method: sdkMethod.method
|
|
54
|
+
};
|
|
55
|
+
},
|
|
56
|
+
submitting,
|
|
57
|
+
handleSubmit(callbacks) {
|
|
58
|
+
return async (formDataOrEvent) => {
|
|
59
|
+
let formData;
|
|
60
|
+
if (formDataOrEvent instanceof Event) {
|
|
61
|
+
formDataOrEvent.preventDefault();
|
|
62
|
+
const target = formDataOrEvent.target;
|
|
63
|
+
formData = new FormData(target);
|
|
64
|
+
} else {
|
|
65
|
+
formData = formDataOrEvent;
|
|
66
|
+
}
|
|
67
|
+
const data = formDataToObject(formData);
|
|
68
|
+
const result = validate(options.schema, data);
|
|
69
|
+
if (!result.success) {
|
|
70
|
+
errors.value = result.errors;
|
|
71
|
+
callbacks?.onError?.(result.errors);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
errors.value = {};
|
|
75
|
+
submitting.value = true;
|
|
76
|
+
let response;
|
|
77
|
+
try {
|
|
78
|
+
response = await sdkMethod(result.data);
|
|
79
|
+
} catch (err) {
|
|
80
|
+
submitting.value = false;
|
|
81
|
+
const message = err instanceof Error ? err.message : "Submission failed";
|
|
82
|
+
const serverErrors = { _form: message };
|
|
83
|
+
errors.value = serverErrors;
|
|
84
|
+
callbacks?.onError?.(serverErrors);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
submitting.value = false;
|
|
88
|
+
callbacks?.onSuccess?.(response);
|
|
89
|
+
};
|
|
90
|
+
},
|
|
91
|
+
error(field) {
|
|
92
|
+
const currentErrors = errors.value;
|
|
93
|
+
return currentErrors[field];
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export { formDataToObject, validate, form };
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import {
|
|
2
|
+
executeLoaders,
|
|
3
|
+
matchRoute
|
|
4
|
+
} from "./chunk-j8vzvne3.js";
|
|
5
|
+
import {
|
|
6
|
+
signal
|
|
7
|
+
} from "./chunk-pgymxpn1.js";
|
|
8
|
+
|
|
9
|
+
// src/router/navigate.ts
|
|
10
|
+
function createRouter(routes, initialUrl) {
|
|
11
|
+
const isSSR = typeof window === "undefined" || typeof globalThis.__SSR_URL__ !== "undefined";
|
|
12
|
+
let url;
|
|
13
|
+
if (initialUrl) {
|
|
14
|
+
url = initialUrl;
|
|
15
|
+
} else if (isSSR) {
|
|
16
|
+
url = globalThis.__SSR_URL__ || "/";
|
|
17
|
+
} else {
|
|
18
|
+
url = window.location.pathname + window.location.search;
|
|
19
|
+
}
|
|
20
|
+
const initialMatch = matchRoute(routes, url);
|
|
21
|
+
const current = signal(initialMatch);
|
|
22
|
+
const loaderData = signal([]);
|
|
23
|
+
const loaderError = signal(null);
|
|
24
|
+
const searchParams = signal(initialMatch?.search ?? {});
|
|
25
|
+
let navigationGen = 0;
|
|
26
|
+
let currentAbort = null;
|
|
27
|
+
if (initialMatch) {
|
|
28
|
+
const gen = ++navigationGen;
|
|
29
|
+
const abort = new AbortController;
|
|
30
|
+
currentAbort = abort;
|
|
31
|
+
runLoaders(initialMatch, gen, abort.signal).catch(() => {});
|
|
32
|
+
}
|
|
33
|
+
async function runLoaders(match, gen, abortSignal) {
|
|
34
|
+
try {
|
|
35
|
+
loaderError.value = null;
|
|
36
|
+
const results = await executeLoaders(match.matched, match.params, abortSignal);
|
|
37
|
+
if (gen === navigationGen) {
|
|
38
|
+
loaderData.value = results;
|
|
39
|
+
}
|
|
40
|
+
} catch (err) {
|
|
41
|
+
if (gen === navigationGen) {
|
|
42
|
+
loaderError.value = err instanceof Error ? err : new Error(String(err));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async function applyNavigation(url2) {
|
|
47
|
+
if (currentAbort) {
|
|
48
|
+
currentAbort.abort();
|
|
49
|
+
}
|
|
50
|
+
const gen = ++navigationGen;
|
|
51
|
+
const abort = new AbortController;
|
|
52
|
+
currentAbort = abort;
|
|
53
|
+
const match = matchRoute(routes, url2);
|
|
54
|
+
current.value = match;
|
|
55
|
+
if (match) {
|
|
56
|
+
searchParams.value = match.search;
|
|
57
|
+
await runLoaders(match, gen, abort.signal);
|
|
58
|
+
} else {
|
|
59
|
+
searchParams.value = {};
|
|
60
|
+
if (gen === navigationGen) {
|
|
61
|
+
loaderData.value = [];
|
|
62
|
+
loaderError.value = null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async function navigate(url2, options) {
|
|
67
|
+
if (!isSSR) {
|
|
68
|
+
if (options?.replace) {
|
|
69
|
+
window.history.replaceState(null, "", url2);
|
|
70
|
+
} else {
|
|
71
|
+
window.history.pushState(null, "", url2);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
await applyNavigation(url2);
|
|
75
|
+
}
|
|
76
|
+
async function revalidate() {
|
|
77
|
+
const match = current.value;
|
|
78
|
+
if (match) {
|
|
79
|
+
if (currentAbort) {
|
|
80
|
+
currentAbort.abort();
|
|
81
|
+
}
|
|
82
|
+
const gen = ++navigationGen;
|
|
83
|
+
const abort = new AbortController;
|
|
84
|
+
currentAbort = abort;
|
|
85
|
+
await runLoaders(match, gen, abort.signal);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
let onPopState = null;
|
|
89
|
+
if (!isSSR) {
|
|
90
|
+
onPopState = () => {
|
|
91
|
+
const url2 = window.location.pathname + window.location.search;
|
|
92
|
+
applyNavigation(url2).catch(() => {});
|
|
93
|
+
};
|
|
94
|
+
window.addEventListener("popstate", onPopState);
|
|
95
|
+
}
|
|
96
|
+
function dispose() {
|
|
97
|
+
if (onPopState) {
|
|
98
|
+
window.removeEventListener("popstate", onPopState);
|
|
99
|
+
}
|
|
100
|
+
if (currentAbort) {
|
|
101
|
+
currentAbort.abort();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
current,
|
|
106
|
+
dispose,
|
|
107
|
+
loaderData,
|
|
108
|
+
loaderError,
|
|
109
|
+
navigate,
|
|
110
|
+
revalidate,
|
|
111
|
+
searchParams
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export { createRouter };
|