weifuwu 0.33.11 → 0.34.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 +89 -87
- package/dist/client/app.d.ts +11 -27
- package/dist/client/index.d.ts +23 -233
- package/dist/client/index.js +601 -896
- package/dist/client/middleware/api.d.ts +2 -0
- package/dist/client/middleware/auth.d.ts +9 -60
- package/dist/client/middleware/ws.d.ts +4 -10
- package/dist/client/render.d.ts +17 -0
- package/dist/client/router.d.ts +4 -53
- package/dist/client/types.d.ts +51 -92
- package/dist/client/vnode.d.ts +38 -0
- package/dist/core/router.d.ts +22 -4
- package/dist/index.d.ts +3 -0
- package/dist/index.js +218 -56
- package/dist/middleware/static.d.ts +8 -3
- package/dist/postgres/client.d.ts +1 -1
- package/dist/postgres/index.d.ts +0 -1
- package/dist/postgres/types.d.ts +5 -1
- package/dist/redis/client.d.ts +1 -1
- package/dist/request.d.ts +23 -0
- package/dist/response.d.ts +46 -0
- package/dist/types.d.ts +29 -0
- package/package.json +2 -3
- package/dist/client/form.d.ts +0 -111
- package/dist/client/jsx-runtime.d.ts +0 -481
- package/dist/client/jsx-runtime.js +0 -1073
- package/dist/client/lazy.d.ts +0 -50
- package/dist/client/resource.d.ts +0 -50
- package/dist/client/signal.d.ts +0 -65
- package/dist/postgres/module.d.ts +0 -12
package/dist/client/index.js
CHANGED
|
@@ -1,809 +1,604 @@
|
|
|
1
|
-
// src/client/
|
|
2
|
-
var
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
#listeners = /* @__PURE__ */ new Set();
|
|
9
|
-
constructor(value) {
|
|
10
|
-
this.#value = value;
|
|
11
|
-
}
|
|
12
|
-
get value() {
|
|
13
|
-
if (currentEffect) {
|
|
14
|
-
this.#listeners.add(currentEffect);
|
|
15
|
-
currentDeps?.add(this);
|
|
16
|
-
}
|
|
17
|
-
return this.#value;
|
|
18
|
-
}
|
|
19
|
-
set value(v) {
|
|
20
|
-
if (v !== this.#value) {
|
|
21
|
-
this.#value = v;
|
|
22
|
-
this.#notify();
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
/** @internal 移除监听器(由 effect dispose 调用) */
|
|
26
|
-
_removeListener(fn) {
|
|
27
|
-
this.#listeners.delete(fn);
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* 可变更新 — 原地修改信号值并触发通知。
|
|
31
|
-
*
|
|
32
|
-
* 适用于数组/对象等引用类型:无需创建新引用即可触发更新。
|
|
33
|
-
*
|
|
34
|
-
* ```ts
|
|
35
|
-
* const items = signal([1, 2, 3])
|
|
36
|
-
* items.mutate(arr => arr.push(4)) // 数组原地修改
|
|
37
|
-
* // items.value === [1, 2, 3, 4]
|
|
38
|
-
* ```
|
|
39
|
-
*/
|
|
40
|
-
mutate(fn) {
|
|
41
|
-
fn(this.#value);
|
|
42
|
-
this.#notify();
|
|
43
|
-
}
|
|
44
|
-
/** 通知所有监听器 — 批量模式时积攒,否则立即执行 */
|
|
45
|
-
#notify() {
|
|
46
|
-
if (_batchDepth > 0) {
|
|
47
|
-
for (const fn of this.#listeners) _pendingBatch.add(fn);
|
|
48
|
-
} else {
|
|
49
|
-
const fns = [...this.#listeners];
|
|
50
|
-
for (const fn of fns) fn();
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
function signal(initial) {
|
|
55
|
-
return new Signal(initial);
|
|
56
|
-
}
|
|
57
|
-
function isSignal(value) {
|
|
58
|
-
return value instanceof Signal;
|
|
59
|
-
}
|
|
60
|
-
function effect(fn) {
|
|
61
|
-
const deps = /* @__PURE__ */ new Set();
|
|
62
|
-
const run = () => {
|
|
63
|
-
for (const dep of deps) dep._removeListener(run);
|
|
64
|
-
deps.clear();
|
|
65
|
-
const prevEffect = currentEffect;
|
|
66
|
-
const prevDeps = currentDeps;
|
|
67
|
-
currentEffect = run;
|
|
68
|
-
currentDeps = deps;
|
|
69
|
-
try {
|
|
70
|
-
fn();
|
|
71
|
-
} finally {
|
|
72
|
-
currentEffect = prevEffect;
|
|
73
|
-
currentDeps = prevDeps;
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
run();
|
|
77
|
-
return () => {
|
|
78
|
-
for (const dep of deps) dep._removeListener(run);
|
|
79
|
-
deps.clear();
|
|
1
|
+
// src/client/vnode.ts
|
|
2
|
+
var Fragment = /* @__PURE__ */ Symbol("Fragment");
|
|
3
|
+
function jsx(type, props, key) {
|
|
4
|
+
return {
|
|
5
|
+
type,
|
|
6
|
+
props: normalizeProps(props),
|
|
7
|
+
key: key ?? void 0
|
|
80
8
|
};
|
|
81
9
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
s.value = fn();
|
|
86
|
-
});
|
|
87
|
-
return s;
|
|
88
|
-
}
|
|
89
|
-
function batch(fn) {
|
|
90
|
-
_batchDepth++;
|
|
91
|
-
try {
|
|
92
|
-
fn();
|
|
93
|
-
} finally {
|
|
94
|
-
_batchDepth--;
|
|
95
|
-
if (_batchDepth === 0 && _pendingBatch.size > 0) {
|
|
96
|
-
const fns = [..._pendingBatch];
|
|
97
|
-
_pendingBatch.clear();
|
|
98
|
-
for (const fn2 of fns) fn2();
|
|
99
|
-
}
|
|
100
|
-
}
|
|
10
|
+
var jsxs = jsx;
|
|
11
|
+
function jsxDEV(type, props, key) {
|
|
12
|
+
return jsx(type, props, key);
|
|
101
13
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
} finally {
|
|
110
|
-
currentEffect = prevEffect;
|
|
111
|
-
currentDeps = prevDeps;
|
|
14
|
+
var h = jsx;
|
|
15
|
+
function normalizeProps(props) {
|
|
16
|
+
if (!props) return {};
|
|
17
|
+
const result = {};
|
|
18
|
+
for (const key of Object.keys(props)) {
|
|
19
|
+
if (key === "key") continue;
|
|
20
|
+
result[key] = props[key];
|
|
112
21
|
}
|
|
22
|
+
return result;
|
|
113
23
|
}
|
|
114
24
|
|
|
115
|
-
// src/client/
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
if (
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
if (
|
|
25
|
+
// src/client/render.ts
|
|
26
|
+
function render(input, ctx) {
|
|
27
|
+
return renderValue(input, ctx);
|
|
28
|
+
}
|
|
29
|
+
function renderValue(v, ctx) {
|
|
30
|
+
if (v == null || typeof v === "boolean") return document.createTextNode("");
|
|
31
|
+
if (typeof v === "string" || typeof v === "number") return document.createTextNode(String(v));
|
|
32
|
+
if (Array.isArray(v)) return renderArray(v, ctx);
|
|
33
|
+
const vnode = v;
|
|
34
|
+
if (vnode.type === Fragment) {
|
|
35
|
+
const frag = document.createDocumentFragment();
|
|
36
|
+
forEach(vnode.props?.children, (child) => frag.appendChild(renderValue(child, ctx)));
|
|
37
|
+
return frag;
|
|
38
|
+
}
|
|
39
|
+
if (typeof vnode.type === "function") {
|
|
40
|
+
return renderComponent(vnode.type, vnode.props, vnode, ctx);
|
|
41
|
+
}
|
|
42
|
+
const el = document.createElement(vnode.type);
|
|
43
|
+
vnode.el = el;
|
|
44
|
+
for (const [key, value] of Object.entries(vnode.props ?? {})) {
|
|
45
|
+
if (key === "children" || key === "key" || key === "ref") continue;
|
|
46
|
+
setProp(el, key, value);
|
|
47
|
+
}
|
|
48
|
+
const flatChildren = flattenChildren(vnode.props?.children);
|
|
49
|
+
for (const child of flatChildren) {
|
|
50
|
+
el.appendChild(renderValue(child, ctx));
|
|
51
|
+
}
|
|
52
|
+
if (vnode.props?.ref) {
|
|
53
|
+
queueMicrotask(() => vnode.props.ref(el));
|
|
54
|
+
}
|
|
55
|
+
return el;
|
|
125
56
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
} else if (!now && entry.mounted) {
|
|
152
|
-
entry.mounted = false;
|
|
153
|
-
for (const fn of entry.disposeFns) fn();
|
|
154
|
-
entry.disposeFns = [];
|
|
155
|
-
entry.mountFns = [];
|
|
156
|
-
obs.disconnect();
|
|
157
|
-
_entries.delete(el);
|
|
158
|
-
}
|
|
159
|
-
});
|
|
160
|
-
obs.observe(document.body, { childList: true, subtree: true });
|
|
161
|
-
entry.observer = obs;
|
|
162
|
-
return entry;
|
|
57
|
+
function renderComponent(Comp, props, vnode, ctx) {
|
|
58
|
+
const prev$ = vnode._$;
|
|
59
|
+
if (!prev$) vnode._$ = {};
|
|
60
|
+
ctx.ui = ctx.ui ?? {};
|
|
61
|
+
ctx.ui.ready = !!prev$;
|
|
62
|
+
const childVNode = Comp(props, ctx);
|
|
63
|
+
if (childVNode == null) {
|
|
64
|
+
vnode._child = null;
|
|
65
|
+
return document.createTextNode("");
|
|
66
|
+
}
|
|
67
|
+
vnode._child = childVNode;
|
|
68
|
+
return renderValue(childVNode, ctx);
|
|
69
|
+
}
|
|
70
|
+
function renderArray(arr, ctx) {
|
|
71
|
+
const frag = document.createDocumentFragment();
|
|
72
|
+
for (const item of arr) frag.appendChild(renderValue(item, ctx));
|
|
73
|
+
return frag;
|
|
74
|
+
}
|
|
75
|
+
function forEach(children, fn) {
|
|
76
|
+
if (children == null) return;
|
|
77
|
+
if (Array.isArray(children)) {
|
|
78
|
+
children.forEach(fn);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
fn(children);
|
|
163
82
|
}
|
|
164
|
-
function
|
|
165
|
-
|
|
166
|
-
if (
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
83
|
+
function flattenChildren(children) {
|
|
84
|
+
if (children == null) return [];
|
|
85
|
+
if (!Array.isArray(children)) return [children];
|
|
86
|
+
const result = [];
|
|
87
|
+
for (const child of children) {
|
|
88
|
+
if (Array.isArray(child)) {
|
|
89
|
+
result.push(...child);
|
|
90
|
+
} else {
|
|
91
|
+
result.push(child);
|
|
170
92
|
}
|
|
171
|
-
} else {
|
|
172
|
-
entry.mountFns.push(fn);
|
|
173
93
|
}
|
|
174
|
-
|
|
175
|
-
function wrap(tagName, setup) {
|
|
176
|
-
return (props, ctx) => {
|
|
177
|
-
const el = document.createElement(tagName);
|
|
178
|
-
_onMountElement(el, () => setup(el, props, ctx));
|
|
179
|
-
return el;
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
function _trackEffect(el, dispose) {
|
|
183
|
-
const entry = _ensure(el);
|
|
184
|
-
entry.disposeFns.push(dispose);
|
|
94
|
+
return result;
|
|
185
95
|
}
|
|
186
96
|
function setProp(el, key, value) {
|
|
187
97
|
if (key === "class" || key === "className") {
|
|
188
|
-
|
|
189
|
-
_trackEffect(el, effect(() => {
|
|
190
|
-
el.className = String(value.value);
|
|
191
|
-
}));
|
|
192
|
-
} else {
|
|
193
|
-
el.className = String(value ?? "");
|
|
194
|
-
}
|
|
98
|
+
el.className = String(value ?? "");
|
|
195
99
|
} else if (key === "style" && typeof value === "object" && value !== null) {
|
|
196
100
|
Object.assign(el.style, value);
|
|
197
101
|
} else if (key.startsWith("on") && typeof value === "function") {
|
|
198
102
|
el.addEventListener(key.slice(2).toLowerCase(), value);
|
|
199
|
-
} else if (key === "
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
if (key === "value" || key === "checked") {
|
|
205
|
-
el[key] = v;
|
|
206
|
-
} else if (v == null || v === false) {
|
|
207
|
-
el.removeAttribute(key);
|
|
208
|
-
} else if (v === true) {
|
|
209
|
-
el.setAttribute(key, "");
|
|
210
|
-
} else {
|
|
211
|
-
el.setAttribute(key, String(v));
|
|
212
|
-
}
|
|
213
|
-
}));
|
|
103
|
+
} else if (key === "value" && (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement)) {
|
|
104
|
+
;
|
|
105
|
+
el.value = String(value ?? "");
|
|
106
|
+
} else if (value === true) {
|
|
107
|
+
el.setAttribute(key, "");
|
|
214
108
|
} else if (value != null && value !== false) {
|
|
215
|
-
|
|
216
|
-
else if (key === "value" || key === "checked") el[key] = value;
|
|
217
|
-
else el.setAttribute(key, String(value));
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
function _closestElement(node) {
|
|
221
|
-
if (node instanceof Element) return node;
|
|
222
|
-
if (node instanceof DocumentFragment) {
|
|
223
|
-
return null;
|
|
224
|
-
}
|
|
225
|
-
let parent = node.parentNode;
|
|
226
|
-
while (parent) {
|
|
227
|
-
if (parent instanceof Element) return parent;
|
|
228
|
-
parent = parent.parentNode;
|
|
109
|
+
el.setAttribute(key, String(value));
|
|
229
110
|
}
|
|
230
|
-
return null;
|
|
231
111
|
}
|
|
232
|
-
function
|
|
233
|
-
if (
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
if (child instanceof Node) {
|
|
239
|
-
parent.appendChild(child);
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
242
|
-
if (isSignal(child)) {
|
|
243
|
-
const text = document.createTextNode("");
|
|
244
|
-
const parentEl = parent instanceof Element ? parent : _closestElement(parent);
|
|
245
|
-
if (parentEl) {
|
|
246
|
-
_trackEffect(parentEl, effect(() => {
|
|
247
|
-
text.textContent = String(child.value);
|
|
248
|
-
}));
|
|
112
|
+
function patchValue(parent, oldNode, oldInput, newInput, ctx) {
|
|
113
|
+
if (oldInput == null) {
|
|
114
|
+
if (newInput == null) return null;
|
|
115
|
+
const node = renderValue(newInput, ctx);
|
|
116
|
+
if (oldNode && oldNode.parentNode) {
|
|
117
|
+
oldNode.parentNode.insertBefore(node, oldNode);
|
|
249
118
|
} else {
|
|
250
|
-
|
|
251
|
-
anchor.style.display = "contents";
|
|
252
|
-
_trackEffect(anchor, effect(() => {
|
|
253
|
-
text.textContent = String(child.value);
|
|
254
|
-
}));
|
|
255
|
-
anchor.appendChild(text);
|
|
256
|
-
parent.appendChild(anchor);
|
|
257
|
-
return;
|
|
119
|
+
parent.appendChild(node);
|
|
258
120
|
}
|
|
259
|
-
|
|
260
|
-
return;
|
|
121
|
+
return node;
|
|
261
122
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
123
|
+
if (newInput == null) {
|
|
124
|
+
if (oldNode) {
|
|
125
|
+
callRef(oldInput, null);
|
|
126
|
+
oldNode.remove();
|
|
127
|
+
}
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
const oldType = typeOf(oldInput);
|
|
131
|
+
const newType = typeOf(newInput);
|
|
132
|
+
if (oldType !== newType) {
|
|
133
|
+
callRef(oldInput, null);
|
|
134
|
+
const node = renderValue(newInput, ctx);
|
|
135
|
+
if (oldNode?.parentNode) {
|
|
136
|
+
oldNode.parentNode.replaceChild(node, oldNode);
|
|
137
|
+
}
|
|
138
|
+
return node;
|
|
139
|
+
}
|
|
140
|
+
if (newType === "text") {
|
|
141
|
+
if (oldNode && oldNode.textContent !== String(newInput)) {
|
|
142
|
+
oldNode.textContent = String(newInput);
|
|
143
|
+
}
|
|
144
|
+
return oldNode;
|
|
145
|
+
}
|
|
146
|
+
const newV = newInput;
|
|
147
|
+
const oldV = oldInput;
|
|
148
|
+
if (typeof newV.type === "function") {
|
|
149
|
+
const comp = newV.type;
|
|
150
|
+
if (oldV._$) newV._$ = oldV._$;
|
|
151
|
+
ctx.ui = ctx.ui ?? {};
|
|
152
|
+
ctx.ui.ready = !!newV._$;
|
|
153
|
+
const childOld = oldV._child != null ? oldV._child : comp(oldV.props, ctx);
|
|
154
|
+
ctx.ui.ready = !!newV._$;
|
|
155
|
+
const childNew = comp(newV.props, ctx);
|
|
156
|
+
newV._child = childNew;
|
|
157
|
+
return patchValue(parent, oldNode, childOld, childNew, ctx);
|
|
158
|
+
}
|
|
159
|
+
if (newV.type === Fragment) {
|
|
160
|
+
patchChildren(parent, oldV, newV, ctx);
|
|
161
|
+
return oldNode;
|
|
162
|
+
}
|
|
163
|
+
if (typeof newV.type === "string") {
|
|
164
|
+
if (oldNode && oldNode.nodeType === 1) {
|
|
165
|
+
patchProps(oldNode, oldV.props, newV.props);
|
|
166
|
+
patchChildren(oldNode, oldV, newV, ctx);
|
|
167
|
+
} else if (oldNode) {
|
|
168
|
+
callRef(oldInput, null);
|
|
169
|
+
const node = renderValue(newInput, ctx);
|
|
170
|
+
oldNode.parentNode?.replaceChild(node, oldNode);
|
|
171
|
+
return node;
|
|
172
|
+
}
|
|
173
|
+
return oldNode;
|
|
174
|
+
}
|
|
175
|
+
if (Array.isArray(newInput)) {
|
|
176
|
+
const oldArr = Array.isArray(oldInput) ? oldInput : [];
|
|
177
|
+
patchSimpleChildren(parent, oldArr, newInput, ctx);
|
|
178
|
+
return oldNode;
|
|
179
|
+
}
|
|
180
|
+
return oldNode;
|
|
181
|
+
}
|
|
182
|
+
function typeOf(input) {
|
|
183
|
+
if (input == null || typeof input === "boolean") return "null";
|
|
184
|
+
if (typeof input === "string" || typeof input === "number") return "text";
|
|
185
|
+
if (Array.isArray(input)) return "array";
|
|
186
|
+
const v = input;
|
|
187
|
+
if (typeof v.type === "function") return `fn:${v.type.name || "anon"}`;
|
|
188
|
+
if (v.type === Fragment) return "fragment";
|
|
189
|
+
if (typeof v.type === "string") return "tag:" + v.type;
|
|
190
|
+
return "unknown";
|
|
191
|
+
}
|
|
192
|
+
function patchProps(el, oldProps, newProps) {
|
|
193
|
+
const oldKeys = oldProps ? Object.keys(oldProps).filter((k) => k !== "children" && k !== "key" && k !== "ref") : [];
|
|
194
|
+
const newKeys = newProps ? Object.keys(newProps).filter((k) => k !== "children" && k !== "key" && k !== "ref") : [];
|
|
195
|
+
for (const key of oldKeys) {
|
|
196
|
+
if (!newKeys.includes(key)) {
|
|
197
|
+
if (key === "value" && (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement)) {
|
|
198
|
+
;
|
|
199
|
+
el.value = "";
|
|
200
|
+
} else {
|
|
201
|
+
el.removeAttribute(key === "className" ? "class" : key);
|
|
296
202
|
}
|
|
297
|
-
_pendingMountQueue = prevMount;
|
|
298
|
-
_pendingCleanupQueue = prevCleanup;
|
|
299
203
|
}
|
|
300
|
-
return result;
|
|
301
204
|
}
|
|
302
|
-
const
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
205
|
+
for (const key of newKeys) {
|
|
206
|
+
const oldVal = oldProps?.[key];
|
|
207
|
+
const newVal = newProps?.[key];
|
|
208
|
+
if (newVal !== oldVal) {
|
|
209
|
+
if (key === "class" || key === "className") {
|
|
210
|
+
el.className = String(newVal ?? "");
|
|
211
|
+
} else if (key === "style" && typeof newVal === "object") {
|
|
212
|
+
Object.assign(el.style, newVal);
|
|
213
|
+
} else if (key.startsWith("on") && typeof newVal === "function") {
|
|
214
|
+
const eventName = key.slice(2).toLowerCase();
|
|
215
|
+
if (typeof oldVal === "function") el.removeEventListener(eventName, oldVal);
|
|
216
|
+
el.addEventListener(eventName, newVal);
|
|
217
|
+
} else if (key === "value" && (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement)) {
|
|
218
|
+
;
|
|
219
|
+
el.value = String(newVal ?? "");
|
|
220
|
+
} else if (newVal === true) {
|
|
221
|
+
el.setAttribute(key, "");
|
|
222
|
+
} else if (newVal != null && newVal !== false) {
|
|
223
|
+
el.setAttribute(key, String(newVal));
|
|
224
|
+
} else {
|
|
225
|
+
if (key === "value" && (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement)) {
|
|
226
|
+
;
|
|
227
|
+
el.value = "";
|
|
228
|
+
} else {
|
|
229
|
+
el.removeAttribute(key);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
306
232
|
}
|
|
307
233
|
}
|
|
308
|
-
const childList = children.length > 0 ? children : props?.children != null ? [props.children] : [];
|
|
309
|
-
for (const child of childList) appendChild(el, child);
|
|
310
|
-
return el;
|
|
311
|
-
}
|
|
312
|
-
function jsxs(type, props, ...children) {
|
|
313
|
-
return jsx(type, props, ...children);
|
|
314
234
|
}
|
|
315
|
-
function
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
function Fragment(props, ..._rest) {
|
|
319
|
-
const childList = props?.children != null ? Array.isArray(props.children) ? props.children : [props.children] : [];
|
|
320
|
-
const el = document.createElement("div");
|
|
321
|
-
el.style.display = "contents";
|
|
322
|
-
for (const child of childList) appendChild(el, child);
|
|
323
|
-
return el;
|
|
235
|
+
function getKey(input) {
|
|
236
|
+
if (input == null || typeof input !== "object") return void 0;
|
|
237
|
+
return input.key;
|
|
324
238
|
}
|
|
325
|
-
function
|
|
326
|
-
const
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
return children();
|
|
334
|
-
} catch (e) {
|
|
335
|
-
const err = e;
|
|
336
|
-
if (onError) onError(err);
|
|
337
|
-
return fallback(err);
|
|
239
|
+
function patchChildren(parent, oldVNode, newVNode, ctx) {
|
|
240
|
+
const oldChildren = normalize(oldVNode.props?.children);
|
|
241
|
+
const newChildren = normalize(newVNode.props?.children);
|
|
242
|
+
const hasKey = newChildren.some((c) => getKey(c) !== void 0);
|
|
243
|
+
if (hasKey) {
|
|
244
|
+
patchKeyedChildren(parent, oldChildren, newChildren, ctx);
|
|
245
|
+
} else {
|
|
246
|
+
patchSimpleChildren(parent, oldChildren, newChildren, ctx);
|
|
338
247
|
}
|
|
339
248
|
}
|
|
340
|
-
function
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
function Show({ when, children, fallback }) {
|
|
350
|
-
const el = document.createElement("div");
|
|
351
|
-
el.style.display = "contents";
|
|
352
|
-
function render(show) {
|
|
353
|
-
while (el.lastChild) el.removeChild(el.lastChild);
|
|
354
|
-
if (show && children != null) {
|
|
355
|
-
el.appendChild(toNode(children));
|
|
356
|
-
} else if (!show && fallback != null) {
|
|
357
|
-
el.appendChild(toNode(fallback));
|
|
249
|
+
function normalize(children) {
|
|
250
|
+
if (children == null) return [];
|
|
251
|
+
if (!Array.isArray(children)) return [children];
|
|
252
|
+
const result = [];
|
|
253
|
+
for (const child of children) {
|
|
254
|
+
if (Array.isArray(child)) {
|
|
255
|
+
result.push(...child);
|
|
256
|
+
} else {
|
|
257
|
+
result.push(child);
|
|
358
258
|
}
|
|
359
259
|
}
|
|
360
|
-
|
|
361
|
-
const dispose = effect(() => render(Boolean(when.value)));
|
|
362
|
-
_trackEffect(el, dispose);
|
|
363
|
-
} else {
|
|
364
|
-
render(Boolean(when));
|
|
365
|
-
}
|
|
366
|
-
return el;
|
|
260
|
+
return result;
|
|
367
261
|
}
|
|
368
|
-
function
|
|
369
|
-
const
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
if (
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
el.appendChild(children(list[i], i));
|
|
262
|
+
function patchSimpleChildren(parent, oldChildren, newChildren, ctx) {
|
|
263
|
+
const max = Math.max(oldChildren.length, newChildren.length);
|
|
264
|
+
for (let i = 0; i < max; i++) {
|
|
265
|
+
const oldChild = oldChildren[i];
|
|
266
|
+
const newChild = newChildren[i];
|
|
267
|
+
const existingNode = parent.childNodes[i] || null;
|
|
268
|
+
if (oldChild === void 0 && newChild !== void 0) {
|
|
269
|
+
const node = renderValue(newChild, ctx);
|
|
270
|
+
parent.appendChild(node);
|
|
271
|
+
} else if (newChild === void 0) {
|
|
272
|
+
if (existingNode) {
|
|
273
|
+
callRef(oldChild, null);
|
|
274
|
+
existingNode.remove();
|
|
382
275
|
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
const oldNodes = Array.from(el.children).filter((n) => n instanceof Element);
|
|
386
|
-
const oldKeyMap = /* @__PURE__ */ new Map();
|
|
387
|
-
for (const node of oldNodes) {
|
|
388
|
-
const k = node.getAttribute("data-key");
|
|
389
|
-
if (k !== null) oldKeyMap.set(k, node);
|
|
276
|
+
} else {
|
|
277
|
+
patchValue(parent, existingNode, oldChild, newChild, ctx);
|
|
390
278
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
function patchKeyedChildren(parent, oldChildren, newChildren, ctx) {
|
|
282
|
+
const oldKeyMap = /* @__PURE__ */ new Map();
|
|
283
|
+
for (let i = 0; i < oldChildren.length; i++) {
|
|
284
|
+
const key = getKey(oldChildren[i]);
|
|
285
|
+
if (key !== void 0) {
|
|
286
|
+
oldKeyMap.set(key, { vnode: oldChildren[i], node: parent.childNodes[i] || null });
|
|
396
287
|
}
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
288
|
+
}
|
|
289
|
+
const newKeys = newChildren.map((c) => getKey(c));
|
|
290
|
+
for (const key of oldKeyMap.keys()) {
|
|
291
|
+
if (!newKeys.includes(key)) {
|
|
292
|
+
const entry = oldKeyMap.get(key);
|
|
293
|
+
callRef(entry.vnode, null);
|
|
294
|
+
entry.node?.remove();
|
|
295
|
+
oldKeyMap.delete(key);
|
|
403
296
|
}
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
const node = children(newItems[i], i);
|
|
413
|
-
if (node instanceof Element) {
|
|
414
|
-
node.setAttribute("data-key", key);
|
|
415
|
-
}
|
|
416
|
-
el.insertBefore(node, insertBefore);
|
|
417
|
-
insertBefore = node;
|
|
297
|
+
}
|
|
298
|
+
for (let i = oldChildren.length - 1; i >= 0; i--) {
|
|
299
|
+
const key = getKey(oldChildren[i]);
|
|
300
|
+
if (key === void 0) {
|
|
301
|
+
const node = parent.childNodes[i];
|
|
302
|
+
if (node) {
|
|
303
|
+
callRef(oldChildren[i], null);
|
|
304
|
+
node.remove();
|
|
418
305
|
}
|
|
419
306
|
}
|
|
420
307
|
}
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
308
|
+
let insertBefore = parent.firstChild;
|
|
309
|
+
for (let i = newChildren.length - 1; i >= 0; i--) {
|
|
310
|
+
const key = newKeys[i];
|
|
311
|
+
const newChild = newChildren[i];
|
|
312
|
+
const oldEntry = key !== void 0 ? oldKeyMap.get(key) : void 0;
|
|
313
|
+
if (oldEntry && oldEntry.node) {
|
|
314
|
+
parent.insertBefore(oldEntry.node, insertBefore);
|
|
315
|
+
insertBefore = oldEntry.node;
|
|
316
|
+
patchValue(parent, oldEntry.node, oldEntry.vnode, newChild, ctx);
|
|
317
|
+
} else {
|
|
318
|
+
const node = renderValue(newChild, ctx);
|
|
319
|
+
parent.insertBefore(node, insertBefore);
|
|
320
|
+
insertBefore = node;
|
|
321
|
+
}
|
|
426
322
|
}
|
|
427
|
-
return el;
|
|
428
323
|
}
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
}
|
|
437
|
-
function extendCtx(ctx, fields) {
|
|
438
|
-
return Object.assign(Object.create(ctx), fields);
|
|
324
|
+
function callRef(input, el) {
|
|
325
|
+
if (input == null || typeof input !== "object") return;
|
|
326
|
+
const vnode = input;
|
|
327
|
+
if (typeof vnode.props?.ref === "function") {
|
|
328
|
+
vnode.props.ref(el);
|
|
329
|
+
}
|
|
330
|
+
forEach(vnode.props?.children, (child) => callRef(child, el));
|
|
439
331
|
}
|
|
440
332
|
|
|
441
333
|
// src/client/app.ts
|
|
442
334
|
function createApp() {
|
|
443
335
|
const middlewares = [];
|
|
444
|
-
|
|
445
|
-
let
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
hash: window.location.hash,
|
|
451
|
-
component: null,
|
|
452
|
-
data: {},
|
|
453
|
-
loading: false
|
|
454
|
-
},
|
|
455
|
-
app: {
|
|
456
|
-
navigate(path) {
|
|
457
|
-
window.history.pushState({}, "", path);
|
|
458
|
-
ctx.route.path = path;
|
|
459
|
-
ctx.route.query = Object.fromEntries(new URLSearchParams(window.location.search));
|
|
460
|
-
ctx.route.hash = window.location.hash;
|
|
461
|
-
const Ctor = window.CustomEvent || CustomEvent;
|
|
462
|
-
window.dispatchEvent(new Ctor("wefu:navigate", { detail: { path } }));
|
|
463
|
-
}
|
|
464
|
-
},
|
|
465
|
-
provide(key, value) {
|
|
466
|
-
provides.set(key, value);
|
|
467
|
-
},
|
|
468
|
-
inject(key) {
|
|
469
|
-
return provides.get(key) ?? null;
|
|
470
|
-
},
|
|
471
|
-
ws: null
|
|
472
|
-
};
|
|
473
|
-
return {
|
|
336
|
+
let ctx = {};
|
|
337
|
+
let container = null;
|
|
338
|
+
let rootComponent = null;
|
|
339
|
+
let oldVNode = null;
|
|
340
|
+
let rendered = false;
|
|
341
|
+
const app = {
|
|
474
342
|
get ctx() {
|
|
475
343
|
return ctx;
|
|
476
344
|
},
|
|
477
345
|
use(mw) {
|
|
478
346
|
middlewares.push(mw);
|
|
479
|
-
return
|
|
347
|
+
return app;
|
|
480
348
|
},
|
|
481
349
|
async mount(rootSelector, RootComponent) {
|
|
350
|
+
rootComponent = RootComponent;
|
|
482
351
|
for (const mw of middlewares) {
|
|
483
352
|
ctx = await mw(ctx);
|
|
484
353
|
}
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
354
|
+
const el = typeof rootSelector === "string" ? document.querySelector(rootSelector) : rootSelector;
|
|
355
|
+
if (!el) throw new Error(`mount target not found: ${rootSelector}`);
|
|
356
|
+
container = el;
|
|
357
|
+
container.innerHTML = "";
|
|
358
|
+
let _dirty = false;
|
|
359
|
+
const $target = {};
|
|
360
|
+
ctx.ui = {
|
|
361
|
+
render: () => {
|
|
362
|
+
if (!container || !rootComponent || !oldVNode) return;
|
|
363
|
+
_dirty = false;
|
|
364
|
+
ctx._rvDepth = 0;
|
|
365
|
+
const newVNode = wrapComponent(rootComponent, ctx);
|
|
366
|
+
const oldNode = container.firstChild;
|
|
367
|
+
if (oldNode) {
|
|
368
|
+
patchValue(container, oldNode, oldVNode, newVNode, ctx);
|
|
369
|
+
}
|
|
370
|
+
oldVNode = newVNode;
|
|
371
|
+
},
|
|
372
|
+
/** 显式标记脏(用于嵌套突变如 push、arr[i].x +=) */
|
|
373
|
+
dirty: () => {
|
|
374
|
+
if (_dirty) return;
|
|
375
|
+
_dirty = true;
|
|
376
|
+
queueMicrotask(() => {
|
|
377
|
+
if (!_dirty) return;
|
|
378
|
+
_dirty = false;
|
|
379
|
+
ctx.ui.render();
|
|
380
|
+
});
|
|
381
|
+
},
|
|
382
|
+
$: new Proxy($target, {
|
|
383
|
+
set(_target, key, val) {
|
|
384
|
+
_target[key] = val;
|
|
385
|
+
if (!_dirty) {
|
|
386
|
+
_dirty = true;
|
|
387
|
+
queueMicrotask(() => {
|
|
388
|
+
if (!_dirty) return;
|
|
389
|
+
_dirty = false;
|
|
390
|
+
ctx.ui.render();
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
return true;
|
|
394
|
+
}
|
|
395
|
+
}),
|
|
396
|
+
ready: false
|
|
397
|
+
};
|
|
398
|
+
oldVNode = wrapComponent(RootComponent, ctx);
|
|
399
|
+
const node = render(oldVNode, ctx);
|
|
400
|
+
if (node instanceof Node) container.appendChild(node);
|
|
401
|
+
rendered = true;
|
|
489
402
|
},
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
return;
|
|
495
|
-
}
|
|
496
|
-
const mergedProps = props ?? window.__WFUI_PROPS__ ?? {};
|
|
497
|
-
setCtx(ctx);
|
|
498
|
-
const vnode = jsx(Component, mergedProps);
|
|
499
|
-
root.appendChild(vnode);
|
|
500
|
-
setCtx(null);
|
|
403
|
+
destroy() {
|
|
404
|
+
if (container) container.innerHTML = "";
|
|
405
|
+
container = null;
|
|
406
|
+
ctx = {};
|
|
501
407
|
}
|
|
502
408
|
};
|
|
409
|
+
return app;
|
|
410
|
+
}
|
|
411
|
+
function wrapComponent(Comp, _ctx) {
|
|
412
|
+
return { type: Comp, props: {}, key: void 0 };
|
|
503
413
|
}
|
|
504
414
|
|
|
505
415
|
// src/client/router.ts
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
const isLeaf = (idx) => idx === fr.chain.length - 1;
|
|
516
|
-
const matchedChain = fr.chain.map((item, idx) => {
|
|
517
|
-
const params = {};
|
|
518
|
-
if (isLeaf(idx)) {
|
|
519
|
-
fr.keys.forEach((k, i) => {
|
|
520
|
-
params[k] = decodeURIComponent(result[i + 1]);
|
|
521
|
-
});
|
|
522
|
-
}
|
|
523
|
-
return { ...item, params };
|
|
524
|
-
});
|
|
525
|
-
return { chain: matchedChain, leafRoute: fr.leafRoute };
|
|
416
|
+
function flattenRoutes(routes, basePath = "", chain = []) {
|
|
417
|
+
const result = [];
|
|
418
|
+
for (const route of routes) {
|
|
419
|
+
const fullPath = joinPaths(basePath, route.path);
|
|
420
|
+
const { re, keys } = compilePath(fullPath);
|
|
421
|
+
const fullChain = [...chain, route];
|
|
422
|
+
result.push({ re, keys, def: route, chain: fullChain });
|
|
423
|
+
if (route.children) {
|
|
424
|
+
result.push(...flattenRoutes(route.children, fullPath, fullChain));
|
|
526
425
|
}
|
|
527
|
-
return null;
|
|
528
426
|
}
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
427
|
+
return result;
|
|
428
|
+
}
|
|
429
|
+
function joinPaths(a, b) {
|
|
430
|
+
if (!b || b === "/") return a || "/";
|
|
431
|
+
const left = a.endsWith("/") ? a.slice(0, -1) : a;
|
|
432
|
+
const right = b.startsWith("/") ? b : "/" + b;
|
|
433
|
+
return left + right;
|
|
434
|
+
}
|
|
435
|
+
function compilePath(path) {
|
|
436
|
+
const keys = [];
|
|
437
|
+
const reStr = path.replace(/:(\w+)/g, (_, key) => {
|
|
438
|
+
keys.push(key);
|
|
439
|
+
return "([^/]+)";
|
|
440
|
+
}).replace(/\*/g, ".*");
|
|
441
|
+
return { re: new RegExp(`^${reStr}$`), keys };
|
|
442
|
+
}
|
|
443
|
+
function matchRoute(path, routes) {
|
|
444
|
+
let best = null;
|
|
445
|
+
for (const fr of routes) {
|
|
446
|
+
if (path.match(fr.re)) {
|
|
447
|
+
if (!best || fr.chain.length > best.chain.length) best = fr;
|
|
541
448
|
}
|
|
542
|
-
const leaf = matched.chain[matched.chain.length - 1];
|
|
543
|
-
ctx2.route.params = { ...leaf.params };
|
|
544
|
-
ctx2.route.component = leaf.component ?? leaf.routeDef.component ?? null;
|
|
545
|
-
ctx2.route.title = matched.leafRoute.title;
|
|
546
|
-
ctx2.route.auth = matched.leafRoute.auth;
|
|
547
|
-
ctx2.route.transition = matched.leafRoute.transition ?? opts.transition;
|
|
548
|
-
if (matched.leafRoute.title) document.title = matched.leafRoute.title;
|
|
549
|
-
ctx2.route[CHAIN_KEY] = matched.chain;
|
|
550
|
-
ctx2.route[DEPTH_KEY] = 0;
|
|
551
449
|
}
|
|
552
|
-
return
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
const
|
|
560
|
-
const
|
|
561
|
-
if (
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
leafLoader(ctx).then((data) => {
|
|
565
|
-
ctx.route.data = data;
|
|
566
|
-
ctx.route.loading = false;
|
|
567
|
-
emit(ctx.route.path);
|
|
568
|
-
}).catch(() => {
|
|
569
|
-
ctx.route.data = {};
|
|
570
|
-
ctx.route.loading = false;
|
|
571
|
-
emit(ctx.route.path);
|
|
572
|
-
});
|
|
573
|
-
} else {
|
|
574
|
-
ctx.route.loading = false;
|
|
575
|
-
emit(ctx.route.path);
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
if (mode === "hash") {
|
|
579
|
-
ctx.app.navigate = (path) => {
|
|
580
|
-
window.location.hash = "#" + path;
|
|
581
|
-
};
|
|
582
|
-
window.addEventListener("hashchange", () => {
|
|
583
|
-
navigateAndLoad(window.location.hash.slice(1) || "/");
|
|
584
|
-
});
|
|
585
|
-
} else {
|
|
586
|
-
const scrollPositions = /* @__PURE__ */ new Map();
|
|
587
|
-
ctx.app.navigate = (path) => {
|
|
588
|
-
if (opts.scrollRestoration !== false) {
|
|
589
|
-
scrollPositions.set(window.location.pathname, window.scrollY);
|
|
450
|
+
return best;
|
|
451
|
+
}
|
|
452
|
+
function router(opts) {
|
|
453
|
+
const flatRoutes = flattenRoutes(opts.routes);
|
|
454
|
+
function resolve(path) {
|
|
455
|
+
const match = matchRoute(path, flatRoutes);
|
|
456
|
+
if (match) {
|
|
457
|
+
const params = {};
|
|
458
|
+
const m = path.match(match.re);
|
|
459
|
+
if (m) {
|
|
460
|
+
for (let i = 0; i < match.keys.length; i++) {
|
|
461
|
+
params[match.keys[i]] = decodeURIComponent(m[i + 1]);
|
|
590
462
|
}
|
|
591
|
-
|
|
592
|
-
|
|
463
|
+
}
|
|
464
|
+
return {
|
|
465
|
+
path: match.def.path,
|
|
466
|
+
params,
|
|
467
|
+
query: Object.fromEntries(new URLSearchParams(window.location.search)),
|
|
468
|
+
chain: match.chain
|
|
593
469
|
};
|
|
594
|
-
window.addEventListener("popstate", () => {
|
|
595
|
-
if (opts.scrollRestoration !== false) {
|
|
596
|
-
const savedY = scrollPositions.get(window.location.pathname);
|
|
597
|
-
if (savedY !== void 0) {
|
|
598
|
-
requestAnimationFrame(() => window.scrollTo(0, savedY));
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
navigateAndLoad(window.location.pathname + window.location.search);
|
|
602
|
-
});
|
|
603
470
|
}
|
|
604
|
-
|
|
605
|
-
|
|
471
|
+
return { path, params: {}, query: {}, chain: [] };
|
|
472
|
+
}
|
|
473
|
+
return (ctx) => {
|
|
474
|
+
const resolved = resolve(window.location.pathname);
|
|
475
|
+
ctx.route = resolved;
|
|
476
|
+
if (!ctx.app) ctx.app = {};
|
|
477
|
+
ctx.app.navigate = (path) => {
|
|
478
|
+
window.history.pushState({}, "", path);
|
|
479
|
+
const resolved2 = resolve(path);
|
|
480
|
+
ctx.route = resolved2;
|
|
481
|
+
ctx.ui?.render();
|
|
482
|
+
};
|
|
483
|
+
window.addEventListener("popstate", () => {
|
|
484
|
+
const resolved2 = resolve(window.location.pathname);
|
|
485
|
+
ctx.route = resolved2;
|
|
486
|
+
ctx.ui?.render();
|
|
487
|
+
});
|
|
606
488
|
return ctx;
|
|
607
489
|
};
|
|
608
490
|
}
|
|
609
|
-
function flattenRoutes(routes, parentPath = "") {
|
|
610
|
-
const result = [];
|
|
611
|
-
for (const route of routes) {
|
|
612
|
-
const fullPath = parentPath + route.path;
|
|
613
|
-
const parts = fullPath.split("/").filter(Boolean);
|
|
614
|
-
const keys = [];
|
|
615
|
-
const reStr = "^/" + parts.map((p) => {
|
|
616
|
-
if (p.startsWith(":")) {
|
|
617
|
-
keys.push(p.slice(1));
|
|
618
|
-
return "([^/]+)";
|
|
619
|
-
}
|
|
620
|
-
return p;
|
|
621
|
-
}).join("/") + "$";
|
|
622
|
-
if (route.children && route.children.length > 0) {
|
|
623
|
-
for (const child of route.children) {
|
|
624
|
-
const childPath = fullPath + child.path;
|
|
625
|
-
const childParts = childPath.split("/").filter(Boolean);
|
|
626
|
-
const childKeys = [];
|
|
627
|
-
const childReStr = "^/" + childParts.map((p) => {
|
|
628
|
-
if (p.startsWith(":")) {
|
|
629
|
-
childKeys.push(p.slice(1));
|
|
630
|
-
return "([^/]+)";
|
|
631
|
-
}
|
|
632
|
-
return p;
|
|
633
|
-
}).join("/") + "$";
|
|
634
|
-
const chain = [
|
|
635
|
-
{ layout: route.layout, params: {}, routeDef: route, depth: 0 },
|
|
636
|
-
{ component: child.component, params: {}, routeDef: child, depth: 1 }
|
|
637
|
-
];
|
|
638
|
-
result.push({
|
|
639
|
-
re: new RegExp(childReStr),
|
|
640
|
-
keys: childKeys,
|
|
641
|
-
chain,
|
|
642
|
-
leafRoute: child
|
|
643
|
-
});
|
|
644
|
-
}
|
|
645
|
-
} else {
|
|
646
|
-
const chain = [
|
|
647
|
-
{ component: route.component, params: {}, routeDef: route, depth: 0 }
|
|
648
|
-
];
|
|
649
|
-
result.push({
|
|
650
|
-
re: new RegExp(reStr),
|
|
651
|
-
keys,
|
|
652
|
-
chain,
|
|
653
|
-
leafRoute: route
|
|
654
|
-
});
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
return result;
|
|
658
|
-
}
|
|
659
491
|
function RouteView(_props, ctx) {
|
|
660
|
-
const
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
const depth = myDepth;
|
|
671
|
-
if (!chain || depth >= chain.length) {
|
|
672
|
-
if (el.children.length > 0) el.textContent = "";
|
|
673
|
-
cachedComp = null;
|
|
674
|
-
return;
|
|
675
|
-
}
|
|
676
|
-
const item = chain[depth];
|
|
677
|
-
const Comp = item.layout ?? item.component;
|
|
678
|
-
if (!Comp) return;
|
|
679
|
-
if (cachedComp === Comp) return;
|
|
680
|
-
cachedComp = Comp;
|
|
681
|
-
el.textContent = "";
|
|
682
|
-
setCtx(ctx);
|
|
683
|
-
const page = jsx(Comp, {});
|
|
684
|
-
el.appendChild(page);
|
|
685
|
-
setCtx(null);
|
|
686
|
-
}
|
|
687
|
-
render();
|
|
688
|
-
window.addEventListener("wefu:route", render);
|
|
689
|
-
onCleanup(() => window.removeEventListener("wefu:route", render));
|
|
690
|
-
return el;
|
|
492
|
+
const route = ctx.route;
|
|
493
|
+
if (!route?.chain?.length) return null;
|
|
494
|
+
const ctxAny = ctx;
|
|
495
|
+
const depth = ctxAny._rvDepth ?? 0;
|
|
496
|
+
if (depth >= route.chain.length) return null;
|
|
497
|
+
const def = route.chain[depth];
|
|
498
|
+
const Comp = def.layout ?? def.component;
|
|
499
|
+
if (!Comp) return null;
|
|
500
|
+
if (def.layout) ctxAny._rvDepth = depth + 1;
|
|
501
|
+
return { type: Comp, props: {}, key: void 0 };
|
|
691
502
|
}
|
|
692
503
|
|
|
693
|
-
// src/client/
|
|
694
|
-
function
|
|
695
|
-
|
|
696
|
-
let error = null;
|
|
697
|
-
let status = "pending";
|
|
698
|
-
let loadingPromise = null;
|
|
699
|
-
function startLoad() {
|
|
700
|
-
if (status !== "pending") return;
|
|
701
|
-
status = "loading";
|
|
702
|
-
loadingPromise = loader().then((mod) => {
|
|
703
|
-
loaded = "default" in mod ? mod.default : mod;
|
|
704
|
-
status = "loaded";
|
|
705
|
-
}).catch((e) => {
|
|
706
|
-
error = e;
|
|
707
|
-
status = "error";
|
|
708
|
-
});
|
|
709
|
-
}
|
|
710
|
-
return (props, ctx) => {
|
|
711
|
-
if (loaded) {
|
|
712
|
-
return loaded(props, ctx);
|
|
713
|
-
}
|
|
714
|
-
if (error) {
|
|
715
|
-
const ErrorComp = options?.errorFallback;
|
|
716
|
-
if (ErrorComp) return ErrorComp(props, ctx);
|
|
717
|
-
const el2 = document.createElement("div");
|
|
718
|
-
el2.textContent = `\u7EC4\u4EF6\u52A0\u8F7D\u5931\u8D25: ${error.message}`;
|
|
719
|
-
el2.style.cssText = "padding: 1rem; color: #ef4444;";
|
|
720
|
-
return el2;
|
|
721
|
-
}
|
|
722
|
-
if (status === "pending") {
|
|
723
|
-
startLoad();
|
|
724
|
-
}
|
|
725
|
-
if (status === "loading" && loadingPromise) {
|
|
726
|
-
loadingPromise.then(() => {
|
|
727
|
-
setTimeout(() => {
|
|
728
|
-
window.dispatchEvent(new CustomEvent("wefu:lazy-loaded"));
|
|
729
|
-
}, 0);
|
|
730
|
-
});
|
|
731
|
-
}
|
|
732
|
-
const Fallback = options?.fallback;
|
|
733
|
-
if (Fallback) return Fallback(props, ctx);
|
|
734
|
-
const el = document.createElement("div");
|
|
735
|
-
el.textContent = "\u52A0\u8F7D\u4E2D...";
|
|
736
|
-
el.style.cssText = "padding: 1rem; color: #6b7280;";
|
|
737
|
-
return el;
|
|
738
|
-
};
|
|
504
|
+
// src/client/types.ts
|
|
505
|
+
function extendCtx(ctx, fields) {
|
|
506
|
+
return Object.assign(Object.create(ctx), fields);
|
|
739
507
|
}
|
|
740
508
|
|
|
741
509
|
// src/client/middleware/ws.ts
|
|
742
|
-
function ws(
|
|
743
|
-
const wsUrl =
|
|
744
|
-
const reconnectInterval =
|
|
745
|
-
const maxReconnect =
|
|
510
|
+
function ws(options = {}) {
|
|
511
|
+
const wsUrl = options.url ?? "/ws";
|
|
512
|
+
const reconnectInterval = options.reconnectInterval ?? 3e3;
|
|
513
|
+
const maxReconnect = options.maxReconnect ?? 10;
|
|
514
|
+
const pingIntervalMs = options.pingInterval ?? 3e4;
|
|
515
|
+
const pingTimeoutMs = options.pingTimeout ?? 1e4;
|
|
746
516
|
return (ctx) => {
|
|
747
|
-
const isConnected = signal(false);
|
|
748
517
|
const messageHandlers = /* @__PURE__ */ new Set();
|
|
749
518
|
let socket = null;
|
|
750
|
-
let
|
|
519
|
+
let reconnectAttempts = 0;
|
|
751
520
|
let reconnectTimer = null;
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
521
|
+
let pingTimer = null;
|
|
522
|
+
let pingTimeoutTimer = null;
|
|
523
|
+
let destroyed = false;
|
|
524
|
+
const wsClient = {
|
|
525
|
+
isConnected: false,
|
|
526
|
+
onMessage: (fn) => {
|
|
527
|
+
messageHandlers.add(fn);
|
|
528
|
+
return () => {
|
|
529
|
+
messageHandlers.delete(fn);
|
|
530
|
+
};
|
|
531
|
+
},
|
|
532
|
+
send: (msg) => {
|
|
533
|
+
if (socket?.readyState === WebSocket.OPEN) {
|
|
534
|
+
socket.send(JSON.stringify(msg));
|
|
535
|
+
}
|
|
536
|
+
},
|
|
537
|
+
_connect: connect,
|
|
538
|
+
/** 断开连接并清理所有定时器 */
|
|
539
|
+
close: () => {
|
|
540
|
+
destroyed = true;
|
|
541
|
+
clearTimers();
|
|
542
|
+
socket?.close();
|
|
543
|
+
socket = null;
|
|
544
|
+
wsClient.isConnected = false;
|
|
545
|
+
}
|
|
546
|
+
};
|
|
547
|
+
async function connect() {
|
|
548
|
+
if (destroyed) return;
|
|
755
549
|
try {
|
|
756
|
-
socket = new WebSocket(
|
|
550
|
+
socket = new WebSocket(wsUrl);
|
|
551
|
+
socket.onopen = () => {
|
|
552
|
+
wsClient.isConnected = true;
|
|
553
|
+
reconnectAttempts = 0;
|
|
554
|
+
startPing();
|
|
555
|
+
};
|
|
556
|
+
socket.onmessage = (e) => {
|
|
557
|
+
try {
|
|
558
|
+
const data = JSON.parse(e.data);
|
|
559
|
+
for (const handler of messageHandlers) handler(data);
|
|
560
|
+
} catch {
|
|
561
|
+
}
|
|
562
|
+
resetPingTimeout();
|
|
563
|
+
};
|
|
564
|
+
socket.onclose = () => {
|
|
565
|
+
wsClient.isConnected = false;
|
|
566
|
+
clearTimers();
|
|
567
|
+
if (!destroyed && reconnectAttempts < maxReconnect) {
|
|
568
|
+
reconnectAttempts++;
|
|
569
|
+
reconnectTimer = setTimeout(connect, reconnectInterval * Math.min(reconnectAttempts, 5));
|
|
570
|
+
}
|
|
571
|
+
};
|
|
572
|
+
socket.onerror = () => {
|
|
573
|
+
socket?.close();
|
|
574
|
+
};
|
|
757
575
|
} catch {
|
|
758
|
-
|
|
759
|
-
|
|
576
|
+
if (!destroyed) {
|
|
577
|
+
reconnectTimer = setTimeout(connect, reconnectInterval);
|
|
578
|
+
}
|
|
760
579
|
}
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
const data = JSON.parse(event.data);
|
|
768
|
-
for (const h of messageHandlers) h(data);
|
|
769
|
-
} catch {
|
|
770
|
-
for (const h of messageHandlers) h(event.data);
|
|
580
|
+
}
|
|
581
|
+
function startPing() {
|
|
582
|
+
if (pingIntervalMs <= 0) return;
|
|
583
|
+
pingTimer = setInterval(() => {
|
|
584
|
+
if (socket?.readyState === WebSocket.OPEN) {
|
|
585
|
+
socket.send(JSON.stringify({ type: "ping" }));
|
|
771
586
|
}
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
scheduleReconnect();
|
|
777
|
-
};
|
|
778
|
-
socket.onerror = () => {
|
|
779
|
-
socket?.close();
|
|
780
|
-
};
|
|
587
|
+
pingTimeoutTimer = setTimeout(() => {
|
|
588
|
+
socket?.close();
|
|
589
|
+
}, pingTimeoutMs);
|
|
590
|
+
}, pingIntervalMs);
|
|
781
591
|
}
|
|
782
|
-
function
|
|
783
|
-
|
|
784
|
-
reconnectCount++;
|
|
785
|
-
reconnectTimer = setTimeout(connect, reconnectInterval * reconnectCount);
|
|
592
|
+
function resetPingTimeout() {
|
|
593
|
+
clearTimeout(pingTimeoutTimer);
|
|
786
594
|
}
|
|
787
|
-
function
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
console.warn("ws.send: WebSocket not open, readyState:", socket?.readyState);
|
|
792
|
-
}
|
|
595
|
+
function clearTimers() {
|
|
596
|
+
clearInterval(pingTimer);
|
|
597
|
+
clearTimeout(pingTimeoutTimer);
|
|
598
|
+
clearTimeout(reconnectTimer);
|
|
793
599
|
}
|
|
794
600
|
connect();
|
|
795
|
-
return extendCtx(ctx, {
|
|
796
|
-
ws: {
|
|
797
|
-
send,
|
|
798
|
-
onMessage: (handler) => {
|
|
799
|
-
messageHandlers.add(handler);
|
|
800
|
-
return () => messageHandlers.delete(handler);
|
|
801
|
-
},
|
|
802
|
-
get isConnected() {
|
|
803
|
-
return isConnected;
|
|
804
|
-
}
|
|
805
|
-
}
|
|
806
|
-
});
|
|
601
|
+
return extendCtx(ctx, { ws: wsClient });
|
|
807
602
|
};
|
|
808
603
|
}
|
|
809
604
|
|
|
@@ -815,13 +610,27 @@ function api(options) {
|
|
|
815
610
|
};
|
|
816
611
|
const onRequest = options?.onRequest;
|
|
817
612
|
const onResponse = options?.onResponse;
|
|
613
|
+
const timeoutMs = options?.timeout ?? 0;
|
|
818
614
|
async function request(method, url, body, reqOpts) {
|
|
819
615
|
const fullURL = opts.baseURL + url;
|
|
820
616
|
const init = {
|
|
821
617
|
method,
|
|
822
|
-
headers: { ...opts.headers, ...reqOpts?.headers }
|
|
823
|
-
signal: reqOpts?.signal
|
|
618
|
+
headers: { ...opts.headers, ...reqOpts?.headers }
|
|
824
619
|
};
|
|
620
|
+
const hasTimeout = timeoutMs > 0;
|
|
621
|
+
const hasUserSignal = !!reqOpts?.signal;
|
|
622
|
+
let abortController = null;
|
|
623
|
+
let timer = null;
|
|
624
|
+
if (hasTimeout || hasUserSignal) {
|
|
625
|
+
abortController = new AbortController();
|
|
626
|
+
init.signal = abortController.signal;
|
|
627
|
+
if (hasTimeout) {
|
|
628
|
+
timer = setTimeout(() => abortController.abort(new Error("Request timed out")), timeoutMs);
|
|
629
|
+
}
|
|
630
|
+
if (hasUserSignal) {
|
|
631
|
+
reqOpts.signal.addEventListener("abort", () => abortController.abort());
|
|
632
|
+
}
|
|
633
|
+
}
|
|
825
634
|
if (body !== void 0 && method !== "GET" && method !== "HEAD") {
|
|
826
635
|
init.body = JSON.stringify(body);
|
|
827
636
|
}
|
|
@@ -829,19 +638,27 @@ function api(options) {
|
|
|
829
638
|
if (onRequest) {
|
|
830
639
|
finalReq = onRequest(finalReq);
|
|
831
640
|
}
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
641
|
+
try {
|
|
642
|
+
const res = await fetch(finalReq.url, finalReq.init);
|
|
643
|
+
if (onResponse) {
|
|
644
|
+
return onResponse(res);
|
|
645
|
+
}
|
|
646
|
+
if (!res.ok) {
|
|
647
|
+
const text = await res.text().catch(() => "");
|
|
648
|
+
throw new ApiError(res.status, text || res.statusText);
|
|
649
|
+
}
|
|
650
|
+
const contentLength = res.headers.get("content-length");
|
|
651
|
+
if (res.status === 204 || contentLength === "0") {
|
|
652
|
+
return void 0;
|
|
653
|
+
}
|
|
654
|
+
const ct = (res.headers.get("content-type") || "").toLowerCase();
|
|
655
|
+
if (ct.includes("application/json") || ct.includes("json")) {
|
|
656
|
+
return res.json();
|
|
657
|
+
}
|
|
658
|
+
return res.text();
|
|
659
|
+
} finally {
|
|
660
|
+
if (timer) clearTimeout(timer);
|
|
843
661
|
}
|
|
844
|
-
return res.json();
|
|
845
662
|
}
|
|
846
663
|
return (ctx) => {
|
|
847
664
|
const client = {
|
|
@@ -851,8 +668,7 @@ function api(options) {
|
|
|
851
668
|
patch: (url, body, reqOpts) => request("PATCH", url, body, reqOpts),
|
|
852
669
|
delete: (url, reqOpts) => request("DELETE", url, void 0, reqOpts)
|
|
853
670
|
};
|
|
854
|
-
ctx
|
|
855
|
-
return ctx;
|
|
671
|
+
return extendCtx(ctx, { api: client });
|
|
856
672
|
};
|
|
857
673
|
}
|
|
858
674
|
var ApiError = class extends Error {
|
|
@@ -867,207 +683,96 @@ var ApiError = class extends Error {
|
|
|
867
683
|
};
|
|
868
684
|
|
|
869
685
|
// src/client/middleware/auth.ts
|
|
686
|
+
function decodeJWT(token) {
|
|
687
|
+
try {
|
|
688
|
+
const parts = token.split(".");
|
|
689
|
+
if (parts.length !== 3) return null;
|
|
690
|
+
const payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
691
|
+
return JSON.parse(atob(payload));
|
|
692
|
+
} catch {
|
|
693
|
+
return null;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
function isTokenExpired(token) {
|
|
697
|
+
const payload = decodeJWT(token);
|
|
698
|
+
if (!payload?.exp) return true;
|
|
699
|
+
return payload.exp * 1e3 - 3e4 < Date.now();
|
|
700
|
+
}
|
|
870
701
|
function auth(options) {
|
|
871
702
|
const storage = options?.storage ?? localStorage;
|
|
872
703
|
const tokenKey = options?.tokenKey ?? "weifuwu_token";
|
|
873
704
|
const userKey = options?.userKey ?? "weifuwu_user";
|
|
705
|
+
const refreshTokenKey = options?.refreshTokenKey ?? "weifuwu_refresh";
|
|
706
|
+
const refreshEndpoint = options?.refreshEndpoint ?? "/api/auth/refresh";
|
|
874
707
|
return (ctx) => {
|
|
875
708
|
const savedToken = storage.getItem(tokenKey);
|
|
876
709
|
const savedUserStr = storage.getItem(userKey);
|
|
877
|
-
const token = signal(savedToken);
|
|
878
|
-
const user = signal(
|
|
879
|
-
savedUserStr ? JSON.parse(savedUserStr) : null
|
|
880
|
-
);
|
|
881
|
-
const isLoggedIn = computed(() => token.value !== null);
|
|
882
|
-
const authorizationHeader = computed(() => token.value ? `Bearer ${token.value}` : null);
|
|
883
710
|
const authClient = {
|
|
884
|
-
token,
|
|
885
|
-
user,
|
|
886
|
-
isLoggedIn
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
711
|
+
token: savedToken,
|
|
712
|
+
user: savedUserStr ? JSON.parse(savedUserStr) : null,
|
|
713
|
+
get isLoggedIn() {
|
|
714
|
+
return this.token !== null;
|
|
715
|
+
},
|
|
716
|
+
login(newToken, newUser, refreshToken) {
|
|
717
|
+
authClient.token = newToken;
|
|
718
|
+
authClient.user = newUser;
|
|
891
719
|
storage.setItem(tokenKey, newToken);
|
|
892
720
|
storage.setItem(userKey, JSON.stringify(newUser));
|
|
721
|
+
if (refreshToken) storage.setItem(refreshTokenKey, refreshToken);
|
|
893
722
|
},
|
|
894
723
|
logout() {
|
|
895
|
-
token
|
|
896
|
-
user
|
|
724
|
+
authClient.token = null;
|
|
725
|
+
authClient.user = null;
|
|
897
726
|
storage.removeItem(tokenKey);
|
|
898
727
|
storage.removeItem(userKey);
|
|
728
|
+
storage.removeItem(refreshTokenKey);
|
|
899
729
|
},
|
|
900
730
|
setUser(newUser) {
|
|
901
|
-
user
|
|
731
|
+
authClient.user = newUser;
|
|
902
732
|
storage.setItem(userKey, JSON.stringify(newUser));
|
|
903
|
-
}
|
|
904
|
-
};
|
|
905
|
-
ctx.auth = authClient;
|
|
906
|
-
return ctx;
|
|
907
|
-
};
|
|
908
|
-
}
|
|
909
|
-
|
|
910
|
-
// src/client/resource.ts
|
|
911
|
-
function createResource(fetcher, options) {
|
|
912
|
-
const data = signal(options?.initialValue);
|
|
913
|
-
const loading = signal(true);
|
|
914
|
-
const error = signal(void 0);
|
|
915
|
-
let fetchId = 0;
|
|
916
|
-
async function load() {
|
|
917
|
-
const id = ++fetchId;
|
|
918
|
-
loading.value = true;
|
|
919
|
-
error.value = void 0;
|
|
920
|
-
try {
|
|
921
|
-
const result = await fetcher();
|
|
922
|
-
if (id === fetchId) {
|
|
923
|
-
data.value = result;
|
|
924
|
-
loading.value = false;
|
|
925
|
-
}
|
|
926
|
-
} catch (e) {
|
|
927
|
-
if (id === fetchId) {
|
|
928
|
-
error.value = e instanceof Error ? e : new Error(String(e));
|
|
929
|
-
loading.value = false;
|
|
930
|
-
}
|
|
931
|
-
}
|
|
932
|
-
}
|
|
933
|
-
load();
|
|
934
|
-
const state = {
|
|
935
|
-
data,
|
|
936
|
-
loading,
|
|
937
|
-
error,
|
|
938
|
-
refetch: () => {
|
|
939
|
-
load();
|
|
940
|
-
}
|
|
941
|
-
};
|
|
942
|
-
return [data, state];
|
|
943
|
-
}
|
|
944
|
-
|
|
945
|
-
// src/client/form.ts
|
|
946
|
-
function useForm(options) {
|
|
947
|
-
const values = signal({ ...options.initial });
|
|
948
|
-
const errors = signal({});
|
|
949
|
-
const submitting = signal(false);
|
|
950
|
-
const touched = signal({});
|
|
951
|
-
function getValidators(name) {
|
|
952
|
-
const rules = options.validate?.[name];
|
|
953
|
-
if (!rules) return [];
|
|
954
|
-
return Array.isArray(rules) ? rules : [rules];
|
|
955
|
-
}
|
|
956
|
-
function validateField(name, value) {
|
|
957
|
-
const validators = getValidators(name);
|
|
958
|
-
for (const v of validators) {
|
|
959
|
-
const err = v(value);
|
|
960
|
-
if (err !== null) return err;
|
|
961
|
-
}
|
|
962
|
-
return null;
|
|
963
|
-
}
|
|
964
|
-
function validateAllFields() {
|
|
965
|
-
const newErrors = {};
|
|
966
|
-
let hasError = false;
|
|
967
|
-
for (const key of Object.keys(values.value)) {
|
|
968
|
-
const err = validateField(key, values.value[key]);
|
|
969
|
-
newErrors[key] = err;
|
|
970
|
-
if (err !== null) hasError = true;
|
|
971
|
-
}
|
|
972
|
-
errors.value = newErrors;
|
|
973
|
-
return !hasError;
|
|
974
|
-
}
|
|
975
|
-
function handleSubmit(e) {
|
|
976
|
-
e.preventDefault();
|
|
977
|
-
if (submitting.value) return;
|
|
978
|
-
const allTouched = {};
|
|
979
|
-
for (const key of Object.keys(values.value)) {
|
|
980
|
-
allTouched[key] = true;
|
|
981
|
-
}
|
|
982
|
-
touched.value = allTouched;
|
|
983
|
-
if (!validateAllFields()) return;
|
|
984
|
-
if (options.onSubmit) {
|
|
985
|
-
submitting.value = true;
|
|
986
|
-
const result = options.onSubmit({ ...values.value });
|
|
987
|
-
if (result instanceof Promise) {
|
|
988
|
-
result.finally(() => {
|
|
989
|
-
submitting.value = false;
|
|
990
|
-
});
|
|
991
|
-
} else {
|
|
992
|
-
submitting.value = false;
|
|
993
|
-
}
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
function field(name) {
|
|
997
|
-
const fieldError = {
|
|
998
|
-
get value() {
|
|
999
|
-
return errors.value[name];
|
|
1000
|
-
}
|
|
1001
|
-
};
|
|
1002
|
-
return {
|
|
1003
|
-
get value() {
|
|
1004
|
-
return String(values.value[name] ?? "");
|
|
1005
733
|
},
|
|
1006
|
-
|
|
1007
|
-
const
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
734
|
+
async refresh() {
|
|
735
|
+
const rt = storage.getItem(refreshTokenKey);
|
|
736
|
+
if (!rt) return false;
|
|
737
|
+
try {
|
|
738
|
+
const res = await fetch(refreshEndpoint, {
|
|
739
|
+
method: "POST",
|
|
740
|
+
headers: { "Content-Type": "application/json" },
|
|
741
|
+
body: JSON.stringify({ refreshToken: rt })
|
|
742
|
+
});
|
|
743
|
+
if (!res.ok) {
|
|
744
|
+
authClient.logout();
|
|
745
|
+
return false;
|
|
746
|
+
}
|
|
747
|
+
const data = await res.json();
|
|
748
|
+
authClient.token = data.token;
|
|
749
|
+
storage.setItem(tokenKey, data.token);
|
|
750
|
+
if (data.refreshToken) storage.setItem(refreshTokenKey, data.refreshToken);
|
|
751
|
+
return true;
|
|
752
|
+
} catch {
|
|
753
|
+
return false;
|
|
1014
754
|
}
|
|
1015
|
-
},
|
|
1016
|
-
get error() {
|
|
1017
|
-
return fieldError;
|
|
1018
755
|
}
|
|
1019
756
|
};
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
values.value = { ...options.initial };
|
|
1026
|
-
errors.value = {};
|
|
1027
|
-
touched.value = {};
|
|
1028
|
-
submitting.value = false;
|
|
1029
|
-
}
|
|
1030
|
-
return {
|
|
1031
|
-
values,
|
|
1032
|
-
errors,
|
|
1033
|
-
submitting,
|
|
1034
|
-
touched,
|
|
1035
|
-
handleSubmit,
|
|
1036
|
-
field,
|
|
1037
|
-
setValue,
|
|
1038
|
-
reset,
|
|
1039
|
-
validateAll: validateAllFields
|
|
757
|
+
if (savedToken && isTokenExpired(savedToken)) {
|
|
758
|
+
authClient.refresh().catch(() => {
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
return extendCtx(ctx, { auth: authClient });
|
|
1040
762
|
};
|
|
1041
763
|
}
|
|
1042
764
|
export {
|
|
1043
765
|
ApiError,
|
|
1044
|
-
ErrorBoundary,
|
|
1045
|
-
For,
|
|
1046
766
|
Fragment,
|
|
1047
767
|
RouteView,
|
|
1048
|
-
Show,
|
|
1049
768
|
api,
|
|
1050
769
|
auth,
|
|
1051
|
-
batch,
|
|
1052
|
-
computed,
|
|
1053
770
|
createApp,
|
|
1054
|
-
createContext,
|
|
1055
|
-
createPortal,
|
|
1056
|
-
createResource,
|
|
1057
|
-
domMount,
|
|
1058
|
-
effect,
|
|
1059
771
|
extendCtx,
|
|
1060
|
-
|
|
772
|
+
h,
|
|
1061
773
|
jsx,
|
|
1062
774
|
jsxDEV,
|
|
1063
775
|
jsxs,
|
|
1064
|
-
lazy,
|
|
1065
|
-
onCleanup,
|
|
1066
|
-
onMount,
|
|
1067
776
|
router,
|
|
1068
|
-
signal,
|
|
1069
|
-
untrack,
|
|
1070
|
-
useForm,
|
|
1071
|
-
wrap,
|
|
1072
777
|
ws
|
|
1073
778
|
};
|