@revojs/vue 0.2.64 → 0.3.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/dist/index.d.ts +17 -0
- package/dist/index.js +154 -27
- package/dist/routes/[...]/get.js +3 -1
- package/package.json +5 -5
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type FetchOptions } from "@revojs/server";
|
|
2
2
|
import { type RouterContext, type Scope } from "revojs";
|
|
3
|
+
import type { StandardSchemaV1 } from "@standard-schema/spec";
|
|
3
4
|
import type { Awaitable } from "revojs/shared";
|
|
4
5
|
import { type Component, type ComputedRef, type MaybeRefOrGetter, type Ref, type ShallowRef, type WatchSource, type WritableComputedRef } from "vue";
|
|
5
6
|
export type AsyncStatus = "idle" | "pending" | "success" | "error";
|
|
@@ -78,9 +79,24 @@ export declare function useRouter(): {
|
|
|
78
79
|
};
|
|
79
80
|
export declare function useState<T>(name: MaybeRefOrGetter<string>): Ref<T | undefined>;
|
|
80
81
|
export declare function useState<T>(name: MaybeRefOrGetter<string>, value: T): Ref<T>;
|
|
82
|
+
export declare function useStateVersion(scope: Scope, name: string): Ref<number, number>;
|
|
83
|
+
export declare function writeState<T>(scope: Scope, name: string, value: T): void;
|
|
81
84
|
export declare function useAsync<T, TError = Error>(name: MaybeRefOrGetter<string>, invoke: (context: AsyncContext) => Awaitable<T>, options?: AsyncOptions<T, TError>): Promise<AsyncResult<T, TError>> & AsyncResult<T, TError>;
|
|
82
85
|
export declare function useFetch<T, TError = Error>(input: MaybeRefOrGetter<string>, options?: FetchOptions & AsyncOptions<T, TError>): Promise<AsyncResult<T, TError>> & AsyncResult<T, TError>;
|
|
83
86
|
export declare function refreshAsync(input?: string | Array<string>): Promise<void[] | undefined>;
|
|
87
|
+
export declare function useAction<TInput extends Array<unknown>, TResult>(invoke: (...input: TInput) => Promise<TResult>): {
|
|
88
|
+
isLoading: Ref<boolean, boolean>;
|
|
89
|
+
error: ShallowRef<unknown, unknown>;
|
|
90
|
+
execute: (...input: TInput) => Promise<TResult>;
|
|
91
|
+
};
|
|
92
|
+
export declare function useForm<TSchema extends StandardSchemaV1>(schema: TSchema, defaults: StandardSchemaV1.InferInput<TSchema>, invoke: (value: StandardSchemaV1.InferOutput<TSchema>) => Promise<void>): {
|
|
93
|
+
form: Ref<StandardSchemaV1.InferInput<TSchema>, StandardSchemaV1.InferInput<TSchema>>;
|
|
94
|
+
issues: ShallowRef<readonly StandardSchemaV1.Issue[] | undefined, readonly StandardSchemaV1.Issue[] | undefined>;
|
|
95
|
+
isLoading: Ref<boolean, boolean>;
|
|
96
|
+
error: ShallowRef<unknown, unknown>;
|
|
97
|
+
execute: () => Promise<void>;
|
|
98
|
+
reset: () => void;
|
|
99
|
+
};
|
|
84
100
|
export declare function useWebSocket(url: MaybeRefOrGetter<string>, options?: WebSocketOptions): {
|
|
85
101
|
status: Ref<WebSocketStatus, WebSocketStatus>;
|
|
86
102
|
send: (data: string | Blob | BufferSource) => boolean;
|
|
@@ -92,4 +108,5 @@ export declare const NAVIGATE_HOOK: import("revojs").Descriptor<import("revojs")
|
|
|
92
108
|
export declare const ASYNC_CONTEXT: import("revojs").Descriptor<AsyncRegistry>;
|
|
93
109
|
export declare const REFRESH_ASYNC_HOOK: import("revojs").Descriptor<import("revojs").Hook<void | string[], Promise<void>>>;
|
|
94
110
|
export declare const CLIENT_ERROR_CONTEXT: import("revojs").Descriptor<ShallowRef<Error>>;
|
|
111
|
+
export declare const STATE_VERSION_CONTEXT: import("revojs").Descriptor<Map<string, Ref<number, number>>>;
|
|
95
112
|
export declare const CLIENT_ROUTER_CONTEXT: import("revojs").Descriptor<ShallowRef<RouterContext<Component>>>;
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import pages from "#pages";
|
|
2
2
|
import { isClient, isServer, isServerSideRendering } from "#variables";
|
|
3
3
|
import { fetch, getState, sendRedirect, setState, useUrl } from "@revojs/server";
|
|
4
|
-
import { Router, defineContext, defineHook, dispatchHook, getContext, registerHook, setContext } from "revojs";
|
|
5
|
-
import { Suspense, computed, createApp, createCommentVNode, createSSRApp, customRef, defineAsyncComponent, defineComponent, getCurrentInstance, getCurrentScope, h, inject, onErrorCaptured, onMounted, onScopeDispose, onServerPrefetch, ref, shallowRef, toValue, warn, watch } from "vue";
|
|
4
|
+
import { Router, defineContext, defineHook, dispatchHook, getContext, registerHook, setContext, useRouteResolverContext } from "revojs";
|
|
5
|
+
import { Suspense, computed, createApp, createCommentVNode, createSSRApp, customRef, defineAsyncComponent, defineComponent, getCurrentInstance, getCurrentScope, h, inject, onErrorCaptured, onMounted, onScopeDispose, onServerPrefetch, ref, shallowRef, toRaw, toValue, warn, watch } from "vue";
|
|
6
6
|
//#region src/index.ts
|
|
7
7
|
var AsyncEntry = class {
|
|
8
8
|
scope;
|
|
@@ -24,7 +24,7 @@ var AsyncEntry = class {
|
|
|
24
24
|
}
|
|
25
25
|
write(value) {
|
|
26
26
|
this.data.value = value;
|
|
27
|
-
|
|
27
|
+
writeState(this.scope, this.key, value);
|
|
28
28
|
}
|
|
29
29
|
cancel(reason) {
|
|
30
30
|
const controller = this.controller;
|
|
@@ -86,8 +86,13 @@ function useRouter() {
|
|
|
86
86
|
const navigate = (route) => {
|
|
87
87
|
if (isClient) {
|
|
88
88
|
const url = new URL(route, globalThis.location.href);
|
|
89
|
+
const pathname = globalThis.location.pathname;
|
|
89
90
|
if (url.href !== globalThis.location.href) globalThis.history.pushState(globalThis.history.state, "", url);
|
|
90
|
-
|
|
91
|
+
(async () => {
|
|
92
|
+
await dispatchHook(scope, NAVIGATE_HOOK);
|
|
93
|
+
if (url.hash) globalThis.document.getElementById(decodeURIComponent(url.hash.slice(1)))?.scrollIntoView();
|
|
94
|
+
else if (url.pathname !== pathname) globalThis.scrollTo(0, 0);
|
|
95
|
+
})();
|
|
91
96
|
}
|
|
92
97
|
if (isServer) throw sendRedirect(scope, route);
|
|
93
98
|
};
|
|
@@ -108,14 +113,29 @@ function useState(name, value) {
|
|
|
108
113
|
return customRef((track, trigger) => ({
|
|
109
114
|
get() {
|
|
110
115
|
track();
|
|
116
|
+
useStateVersion(scope, toValue(name)).value;
|
|
111
117
|
return getState(scope, toValue(name)) ?? value;
|
|
112
118
|
},
|
|
113
119
|
set(update) {
|
|
114
|
-
|
|
120
|
+
writeState(scope, toValue(name), update);
|
|
115
121
|
trigger();
|
|
116
122
|
}
|
|
117
123
|
}));
|
|
118
124
|
}
|
|
125
|
+
function useStateVersion(scope, name) {
|
|
126
|
+
let versions = getContext(scope, STATE_VERSION_CONTEXT);
|
|
127
|
+
if (!Object.hasOwn(scope.context, STATE_VERSION_CONTEXT)) versions = setContext(scope, STATE_VERSION_CONTEXT, /* @__PURE__ */ new Map());
|
|
128
|
+
let version = versions.get(name);
|
|
129
|
+
if (version === void 0) {
|
|
130
|
+
version = ref(0);
|
|
131
|
+
versions.set(name, version);
|
|
132
|
+
}
|
|
133
|
+
return version;
|
|
134
|
+
}
|
|
135
|
+
function writeState(scope, name, value) {
|
|
136
|
+
setState(scope, name, value);
|
|
137
|
+
useStateVersion(scope, name).value += 1;
|
|
138
|
+
}
|
|
119
139
|
function useAsync(name, invoke, options) {
|
|
120
140
|
const scope = useScope();
|
|
121
141
|
const registry = useAsyncContext(scope);
|
|
@@ -150,7 +170,6 @@ function useAsync(name, invoke, options) {
|
|
|
150
170
|
}
|
|
151
171
|
} catch (error) {
|
|
152
172
|
if (entry.controller === controller) {
|
|
153
|
-
entry.write(void 0);
|
|
154
173
|
entry.error.value = error;
|
|
155
174
|
entry.status.value = "error";
|
|
156
175
|
await options?.catch?.(error);
|
|
@@ -233,6 +252,60 @@ async function refreshAsync(input) {
|
|
|
233
252
|
const scope = useScope();
|
|
234
253
|
if (isClient) return await dispatchHook(scope, REFRESH_ASYNC_HOOK, input === void 0 ? void 0 : Array.isArray(input) ? input : [input]);
|
|
235
254
|
}
|
|
255
|
+
function useAction(invoke) {
|
|
256
|
+
const isLoading = ref(false);
|
|
257
|
+
const error = shallowRef();
|
|
258
|
+
let running;
|
|
259
|
+
const run = async (...input) => {
|
|
260
|
+
isLoading.value = true;
|
|
261
|
+
error.value = void 0;
|
|
262
|
+
try {
|
|
263
|
+
return await invoke(...input);
|
|
264
|
+
} catch (cause) {
|
|
265
|
+
error.value = cause;
|
|
266
|
+
throw cause;
|
|
267
|
+
} finally {
|
|
268
|
+
isLoading.value = false;
|
|
269
|
+
running = void 0;
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
const execute = async (...input) => {
|
|
273
|
+
running ??= run(...input);
|
|
274
|
+
return await running;
|
|
275
|
+
};
|
|
276
|
+
return {
|
|
277
|
+
isLoading,
|
|
278
|
+
error,
|
|
279
|
+
execute
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
function useForm(schema, defaults, invoke) {
|
|
283
|
+
const form = ref(structuredClone(defaults));
|
|
284
|
+
const issues = shallowRef();
|
|
285
|
+
const action = useAction(invoke);
|
|
286
|
+
const execute = async () => {
|
|
287
|
+
if (action.isLoading.value) return;
|
|
288
|
+
const result = await schema["~standard"].validate(toRaw(form.value));
|
|
289
|
+
if (result.issues) {
|
|
290
|
+
issues.value = result.issues;
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
issues.value = void 0;
|
|
294
|
+
await action.execute(result.value);
|
|
295
|
+
};
|
|
296
|
+
const reset = () => {
|
|
297
|
+
form.value = structuredClone(defaults);
|
|
298
|
+
issues.value = void 0;
|
|
299
|
+
};
|
|
300
|
+
return {
|
|
301
|
+
form,
|
|
302
|
+
issues,
|
|
303
|
+
isLoading: action.isLoading,
|
|
304
|
+
error: action.error,
|
|
305
|
+
execute,
|
|
306
|
+
reset
|
|
307
|
+
};
|
|
308
|
+
}
|
|
236
309
|
function useWebSocket(url, options = {}) {
|
|
237
310
|
const status = ref("closed");
|
|
238
311
|
const minDelay = options.minDelay ?? 1e3;
|
|
@@ -324,19 +397,44 @@ async function createVueApp(scope) {
|
|
|
324
397
|
const router = new Router();
|
|
325
398
|
const error = shallowRef();
|
|
326
399
|
const context = shallowRef();
|
|
400
|
+
const loaders = /* @__PURE__ */ new Map();
|
|
327
401
|
for (const entries of Object.values(pages)) for (const [path, resolve] of Object.entries(entries)) {
|
|
328
402
|
const segments = path.split("/");
|
|
329
403
|
for (const attribute of segments.pop()?.split(".") ?? []) {
|
|
330
404
|
if (attribute === "page") continue;
|
|
331
405
|
segments.push(attribute);
|
|
332
406
|
}
|
|
333
|
-
|
|
407
|
+
const component = defineAsyncComponent(resolve);
|
|
408
|
+
loaders.set(component, resolve);
|
|
409
|
+
router.use(segments, component);
|
|
334
410
|
}
|
|
335
411
|
const registry = new AsyncRegistry(isClient && isServerSideRendering);
|
|
412
|
+
let navigation = 0;
|
|
336
413
|
const resolve = async () => {
|
|
414
|
+
navigation += 1;
|
|
415
|
+
const current = navigation;
|
|
337
416
|
const path = useUrl(scope).pathname.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
338
417
|
const segments = path === "" ? [] : path.split("/");
|
|
339
|
-
|
|
418
|
+
const resolved = await router.resolve(scope, segments);
|
|
419
|
+
if (current === navigation) {
|
|
420
|
+
error.value = void 0;
|
|
421
|
+
context.value = resolved;
|
|
422
|
+
}
|
|
423
|
+
};
|
|
424
|
+
const preload = async (href) => {
|
|
425
|
+
const path = new URL(href, globalThis.location.href).pathname.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
426
|
+
const segments = path === "" ? [] : path.split("/");
|
|
427
|
+
const resolver = useRouteResolverContext(scope);
|
|
428
|
+
let resolved = segments;
|
|
429
|
+
if (typeof resolver === "function") resolved = await resolver(segments);
|
|
430
|
+
if (resolved === false) return;
|
|
431
|
+
const { match, hooks } = router.match(resolved);
|
|
432
|
+
const chunks = new Array();
|
|
433
|
+
for (const component of [...hooks, match]) {
|
|
434
|
+
const load = component ? loaders.get(component) : void 0;
|
|
435
|
+
if (load) chunks.push(load());
|
|
436
|
+
}
|
|
437
|
+
await Promise.allSettled(chunks);
|
|
340
438
|
};
|
|
341
439
|
const navigate = async () => {
|
|
342
440
|
registry.isHydrating = false;
|
|
@@ -347,7 +445,13 @@ async function createVueApp(scope) {
|
|
|
347
445
|
setContext(scope, CLIENT_ERROR_CONTEXT, error);
|
|
348
446
|
setContext(scope, CLIENT_ROUTER_CONTEXT, context);
|
|
349
447
|
await resolve();
|
|
350
|
-
const app = (isServerSideRendering ? createSSRApp(h(Main, {
|
|
448
|
+
const app = (isServerSideRendering ? createSSRApp(h(Main, {
|
|
449
|
+
popstate: navigate,
|
|
450
|
+
preload
|
|
451
|
+
})) : createApp(h(Main, {
|
|
452
|
+
popstate: navigate,
|
|
453
|
+
preload
|
|
454
|
+
}))).provide("REVOJS_SCOPE", scope);
|
|
351
455
|
app.config.warnHandler = (message, instance, trace) => {
|
|
352
456
|
if (message.includes("Invalid vnode type when creating vnode") || message.includes("Component <Anonymous> is missing template or render function")) return;
|
|
353
457
|
warn(message, instance, trace);
|
|
@@ -357,11 +461,17 @@ async function createVueApp(scope) {
|
|
|
357
461
|
}
|
|
358
462
|
var Main = defineComponent({
|
|
359
463
|
name: "Main",
|
|
360
|
-
props: {
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
464
|
+
props: {
|
|
465
|
+
popstate: {
|
|
466
|
+
type: Function,
|
|
467
|
+
required: true
|
|
468
|
+
},
|
|
469
|
+
preload: {
|
|
470
|
+
type: Function,
|
|
471
|
+
required: true
|
|
472
|
+
}
|
|
473
|
+
},
|
|
474
|
+
setup({ popstate, preload }) {
|
|
365
475
|
const scope = useScope();
|
|
366
476
|
const error = useError(scope);
|
|
367
477
|
const context = useClientRouterContext(scope);
|
|
@@ -378,24 +488,40 @@ var Main = defineComponent({
|
|
|
378
488
|
event.preventDefault();
|
|
379
489
|
navigate(url.pathname + url.search + url.hash);
|
|
380
490
|
};
|
|
491
|
+
const preloaded = /* @__PURE__ */ new Set();
|
|
492
|
+
const preloadLink = (anchor) => {
|
|
493
|
+
const url = new URL(anchor.href);
|
|
494
|
+
if (url.origin !== globalThis.location.origin || anchor.hasAttribute("download") || anchor.relList.contains("external") || preloaded.has(url.pathname)) return;
|
|
495
|
+
preloaded.add(url.pathname);
|
|
496
|
+
preload(url.pathname);
|
|
497
|
+
};
|
|
498
|
+
const preloadVisible = () => {
|
|
499
|
+
for (const anchor of globalThis.document.querySelectorAll("a[href]")) if (anchor instanceof HTMLAnchorElement) preloadLink(anchor);
|
|
500
|
+
};
|
|
501
|
+
const schedulePreload = () => {
|
|
502
|
+
if ("requestIdleCallback" in globalThis) {
|
|
503
|
+
requestIdleCallback(preloadVisible);
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
setTimeout(preloadVisible, 1e3);
|
|
507
|
+
};
|
|
508
|
+
const hover = (event) => {
|
|
509
|
+
const anchor = event.target instanceof Element ? event.target.closest("a[href]") : null;
|
|
510
|
+
if (anchor instanceof HTMLAnchorElement) preloadLink(anchor);
|
|
511
|
+
};
|
|
381
512
|
globalThis.addEventListener("popstate", popstate);
|
|
382
513
|
globalThis.document.addEventListener("click", click);
|
|
514
|
+
globalThis.document.addEventListener("pointerover", hover);
|
|
515
|
+
globalThis.document.addEventListener("focusin", hover);
|
|
516
|
+
const unregister = registerHook(scope, NAVIGATE_HOOK, schedulePreload);
|
|
383
517
|
onScopeDispose(() => {
|
|
384
518
|
globalThis.removeEventListener("popstate", popstate);
|
|
385
519
|
globalThis.document.removeEventListener("click", click);
|
|
520
|
+
globalThis.document.removeEventListener("pointerover", hover);
|
|
521
|
+
globalThis.document.removeEventListener("focusin", hover);
|
|
522
|
+
unregister();
|
|
386
523
|
});
|
|
387
|
-
onMounted(
|
|
388
|
-
const preload = async () => {
|
|
389
|
-
const chunks = new Array();
|
|
390
|
-
for (const entries of Object.values(pages)) for (const load of Object.values(entries)) chunks.push(load());
|
|
391
|
-
await Promise.allSettled(chunks);
|
|
392
|
-
};
|
|
393
|
-
if ("requestIdleCallback" in globalThis) {
|
|
394
|
-
requestIdleCallback(() => void preload());
|
|
395
|
-
return;
|
|
396
|
-
}
|
|
397
|
-
setTimeout(() => void preload(), 1e3);
|
|
398
|
-
});
|
|
524
|
+
onMounted(schedulePreload);
|
|
399
525
|
}
|
|
400
526
|
onErrorCaptured((value) => {
|
|
401
527
|
error.value = value;
|
|
@@ -408,6 +534,7 @@ var NAVIGATE_HOOK = defineHook("NAVIGATE_HOOK");
|
|
|
408
534
|
var ASYNC_CONTEXT = defineContext("ASYNC_CONTEXT");
|
|
409
535
|
var REFRESH_ASYNC_HOOK = defineHook("REFRESH_ASYNC_HOOK");
|
|
410
536
|
var CLIENT_ERROR_CONTEXT = defineContext("CLIENT_ERROR_CONTEXT");
|
|
537
|
+
var STATE_VERSION_CONTEXT = defineContext("STATE_VERSION_CONTEXT");
|
|
411
538
|
var CLIENT_ROUTER_CONTEXT = defineContext("CLIENT_ROUTER_CONTEXT");
|
|
412
539
|
//#endregion
|
|
413
|
-
export { ASYNC_CONTEXT, AsyncEntry, AsyncRegistry, CLIENT_ERROR_CONTEXT, CLIENT_ROUTER_CONTEXT, NAVIGATE_HOOK, REFRESH_ASYNC_HOOK, createVueApp, isRouterNavigation, refreshAsync, useAsync, useAsyncContext, useClientRouterContext, useError, useFetch, useRouter, useScope, useState, useWebSocket };
|
|
540
|
+
export { ASYNC_CONTEXT, AsyncEntry, AsyncRegistry, CLIENT_ERROR_CONTEXT, CLIENT_ROUTER_CONTEXT, NAVIGATE_HOOK, REFRESH_ASYNC_HOOK, STATE_VERSION_CONTEXT, createVueApp, isRouterNavigation, refreshAsync, useAction, useAsync, useAsyncContext, useClientRouterContext, useError, useFetch, useForm, useRouter, useScope, useState, useStateVersion, useWebSocket, writeState };
|
package/dist/routes/[...]/get.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createVueApp, useError } from "../../index.js";
|
|
2
2
|
import { isServerSideRendering } from "#variables";
|
|
3
|
-
import { defineRoute, sendOk, useServer } from "@revojs/server";
|
|
3
|
+
import { FetchError, defineRoute, sendOk, useServer } from "@revojs/server";
|
|
4
4
|
import client from "#client";
|
|
5
5
|
import manifest from "#client/manifest";
|
|
6
6
|
import { renderToString } from "vue/server-renderer";
|
|
@@ -16,9 +16,11 @@ var get_default = defineRoute({ async fetch(scope) {
|
|
|
16
16
|
const html = await renderToString(app, context);
|
|
17
17
|
content = content.replace("<!-- APP -->", () => html);
|
|
18
18
|
} catch (error) {
|
|
19
|
+
if (captured.value instanceof FetchError) throw captured.value.response;
|
|
19
20
|
throw captured.value ?? error;
|
|
20
21
|
}
|
|
21
22
|
if (captured.value instanceof Response) throw captured.value;
|
|
23
|
+
if (captured.value instanceof FetchError) throw captured.value.response;
|
|
22
24
|
let body = "";
|
|
23
25
|
let container = "";
|
|
24
26
|
for (const [target, teleport] of Object.entries(context.teleports ?? {})) if (target.toLowerCase() === "body") body += teleport;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@revojs/vue",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -32,14 +32,14 @@
|
|
|
32
32
|
"watch": "vite build --watch"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@revojs/client": "
|
|
36
|
-
"@revojs/server": "
|
|
35
|
+
"@revojs/client": "0.3.0",
|
|
36
|
+
"@revojs/server": "0.3.0",
|
|
37
37
|
"@standard-schema/spec": "^1.1.0",
|
|
38
38
|
"@vitejs/plugin-vue": "^6.0.9",
|
|
39
|
-
"revojs": "
|
|
39
|
+
"revojs": "0.3.0",
|
|
40
40
|
"vue": "^3.5.43"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
|
-
"@revojs/tsconfig": "
|
|
43
|
+
"@revojs/tsconfig": "0.3.0"
|
|
44
44
|
}
|
|
45
45
|
}
|