@wervt/nuxt 0.3.2 → 0.3.4
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 +2 -0
- package/dist/dev.d.ts +4 -4
- package/dist/dev.js +33 -11
- package/dist/env-types.js +13 -0
- package/dist/icons.js +30 -0
- package/dist/loading.js +30 -0
- package/dist/module.d.ts +0 -2
- package/dist/module.js +42 -2
- package/dist/node_modules/.pnpm/nitro@3.0.260903-beta/node_modules/nitro/dist/runtime/internal/task.js +1 -2
- package/dist/node_modules/.pnpm/nitro@3.0.260903-beta/node_modules/nitro/dist/runtime/task.js +1 -2
- package/dist/og-image.js +38 -0
- package/dist/preview.d.ts +1 -3
- package/dist/preview.js +1 -2
- package/dist/provision.d.ts +31 -4
- package/dist/provision.js +82 -21
- package/dist/route-tools.js +1 -2
- package/dist/runtime/app/plugins/analytics.client.d.ts +0 -2
- package/dist/runtime/app/plugins/analytics.client.js +1 -2
- package/dist/runtime/app/plugins/feedback.client.d.ts +0 -2
- package/dist/runtime/app/plugins/feedback.client.js +1 -2
- package/dist/runtime/app/plugins/update.client.d.ts +0 -2
- package/dist/runtime/app/plugins/update.client.js +1 -2
- package/dist/runtime/app/plugins/user.client.d.ts +0 -2
- package/dist/runtime/app/plugins/user.client.js +1 -2
- package/dist/runtime/app/plugins/webmcp.client.d.ts +0 -2
- package/dist/runtime/app/plugins/webmcp.client.js +1 -2
- package/dist/runtime/app/preview-error.d.ts +26 -0
- package/dist/runtime/app/preview-error.js +69 -0
- package/dist/runtime/app/snapshot.d.ts +15 -0
- package/dist/runtime/app/snapshot.js +63 -0
- package/dist/runtime/app/useShape.d.ts +8 -3
- package/dist/runtime/app/useShape.js +10 -55
- package/dist/runtime/app/useWervtFeedback.d.ts +1 -2
- package/dist/runtime/app/useWervtFeedback.js +1 -2
- package/dist/runtime/app/useWervtTrack.d.ts +1 -3
- package/dist/runtime/app/useWervtTrack.js +1 -2
- package/dist/runtime/app/useWervtUpdate.d.ts +1 -3
- package/dist/runtime/app/useWervtUpdate.js +2 -3
- package/dist/runtime/app/useWervtUser.d.ts +1 -3
- package/dist/runtime/app/useWervtUser.js +1 -2
- package/dist/runtime/email/components.d.ts +1 -3
- package/dist/runtime/email/components.js +1 -2
- package/dist/runtime/email/index.d.ts +2 -1
- package/dist/runtime/email/index.js +2 -1
- package/dist/runtime/email/invite.d.ts +73 -0
- package/dist/runtime/email/invite.js +59 -0
- package/dist/runtime/email/render.d.ts +1 -3
- package/dist/runtime/email/render.js +1 -2
- package/dist/runtime/server/access.d.ts +4 -5
- package/dist/runtime/server/access.js +4 -4
- package/dist/runtime/server/agent-token-plugin.d.ts +11 -0
- package/dist/runtime/server/agent-token-plugin.js +9 -0
- package/dist/runtime/server/anthropic.d.ts +19 -0
- package/dist/runtime/server/anthropic.js +52 -0
- package/dist/runtime/server/boot.d.ts +0 -2
- package/dist/runtime/server/boot.js +1 -2
- package/dist/runtime/server/db.d.ts +1 -3
- package/dist/runtime/server/db.js +1 -2
- package/dist/runtime/server/dev-auth.d.ts +0 -2
- package/dist/runtime/server/dev-auth.js +1 -2
- package/dist/runtime/server/email-preview.d.ts +0 -2
- package/dist/runtime/server/email-preview.js +1 -2
- package/dist/runtime/server/identity.d.ts +18 -0
- package/dist/runtime/server/identity.js +36 -0
- package/dist/runtime/server/index.d.ts +4 -3
- package/dist/runtime/server/index.js +4 -3
- package/dist/runtime/server/logger.d.ts +0 -2
- package/dist/runtime/server/logger.js +1 -2
- package/dist/runtime/server/mail.d.ts +1 -3
- package/dist/runtime/server/mail.js +1 -2
- package/dist/runtime/server/mcp-auth-server.d.ts +0 -2
- package/dist/runtime/server/mcp-auth-server.js +1 -2
- package/dist/runtime/server/mcp-auth.d.ts +1 -3
- package/dist/runtime/server/mcp-auth.js +1 -2
- package/dist/runtime/server/mcp-claims.d.ts +1 -3
- package/dist/runtime/server/mcp-claims.js +1 -2
- package/dist/runtime/server/mcp-metadata.d.ts +0 -2
- package/dist/runtime/server/mcp-metadata.js +1 -2
- package/dist/runtime/server/mcp.d.ts +0 -2
- package/dist/runtime/server/mcp.js +1 -2
- package/dist/runtime/server/preview-errors-plugin.d.ts +13 -0
- package/dist/runtime/server/preview-errors-plugin.js +19 -0
- package/dist/runtime/server/seed.d.ts +1 -3
- package/dist/runtime/server/seed.js +1 -2
- package/dist/runtime/server/shape.d.ts +1 -3
- package/dist/runtime/server/shape.js +1 -2
- package/dist/runtime/server/storage-plugin.d.ts +1 -3
- package/dist/runtime/server/storage-plugin.js +1 -2
- package/dist/runtime/server/task-handlers.d.ts +2 -3
- package/dist/runtime/server/task-handlers.js +1 -2
- package/dist/runtime/server/task-list-nitro2.d.ts +0 -2
- package/dist/runtime/server/task-list-nitro2.js +1 -2
- package/dist/runtime/server/task-list-nitro3.d.ts +0 -2
- package/dist/runtime/server/task-list-nitro3.js +1 -2
- package/dist/runtime/server/task-run-nitro2.d.ts +0 -2
- package/dist/runtime/server/task-run-nitro2.js +1 -2
- package/dist/runtime/server/task-run-nitro3.d.ts +0 -2
- package/dist/runtime/server/task-run-nitro3.js +1 -2
- package/dist/runtime/server/tasks.d.ts +1 -3
- package/dist/runtime/server/tasks.js +1 -2
- package/dist/runtime/server/tool-handlers.d.ts +1 -3
- package/dist/runtime/server/tool-handlers.js +1 -2
- package/dist/runtime/server/tool-list.js +1 -2
- package/dist/runtime/server/tool-run.js +1 -2
- package/dist/runtime/server/tools.d.ts +1 -2
- package/dist/runtime/server/tools.js +1 -2
- package/dist/runtime/server/user-route.d.ts +0 -2
- package/dist/runtime/server/user-route.js +1 -2
- package/dist/runtime/server/user.d.ts +9 -5
- package/dist/runtime/server/user.js +29 -5
- package/dist/runtime/server/validate.d.ts +1 -2
- package/dist/runtime/server/validate.js +1 -2
- package/dist/runtime/shared/agent-token.d.ts +7 -0
- package/dist/runtime/shared/agent-token.js +16 -0
- package/dist/runtime/shared/mark.d.ts +1 -0
- package/dist/runtime/shared/mark.js +3 -0
- package/dist/runtime/shared/preview-errors.d.ts +10 -0
- package/dist/runtime/shared/preview-errors.js +14 -0
- package/dist/runtime/shared/shape.d.ts +1 -2
- package/dist/runtime/shared/shape.js +1 -2
- package/dist/runtime/shared/user.d.ts +9 -3
- package/dist/runtime/shared/user.js +5 -3
- package/dist/seed.js +1 -2
- package/dist/shapes.js +1 -2
- package/dist/storage.d.ts +50 -4
- package/dist/storage.js +224 -5
- package/dist/studio-preview.js +34 -0
- package/package.json +17 -8
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useUpdateAvailable, useWervtUpdate } from "../useWervtUpdate.js";
|
|
2
2
|
import { defineNuxtPlugin, onNuxtReady, useRuntimeConfig } from "#app";
|
|
3
|
-
|
|
3
|
+
// src/runtime/app/plugins/update.client.ts
|
|
4
4
|
const INTERVAL_MS = 6e4;
|
|
5
5
|
/** Focus and visibility changes come in bursts; one check covers them. */
|
|
6
6
|
const MIN_GAP_MS = 1e4;
|
|
@@ -73,5 +73,4 @@ function showBanner(reload) {
|
|
|
73
73
|
bar.append(text, button("Reload", "background:#fafafa;color:#18181b", reload), close);
|
|
74
74
|
document.body.append(bar);
|
|
75
75
|
}
|
|
76
|
-
//#endregion
|
|
77
76
|
export { update_client_default as default };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useWervtUser } from "../useWervtUser.js";
|
|
2
2
|
import { defineNuxtPlugin } from "#app";
|
|
3
|
-
|
|
3
|
+
// src/runtime/app/plugins/user.client.ts
|
|
4
4
|
var user_client_default = defineNuxtPlugin({
|
|
5
5
|
name: "wervt:user",
|
|
6
6
|
async setup(nuxtApp) {
|
|
@@ -14,5 +14,4 @@ var user_client_default = defineNuxtPlugin({
|
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
});
|
|
17
|
-
//#endregion
|
|
18
17
|
export { user_client_default as default };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { defineNuxtPlugin, onNuxtReady, useRuntimeConfig } from "#app";
|
|
2
|
-
|
|
2
|
+
// src/runtime/app/plugins/webmcp.client.ts
|
|
3
3
|
function pageModelContext() {
|
|
4
4
|
const context = document.modelContext ?? navigator.modelContext;
|
|
5
5
|
return typeof context?.registerTool === "function" ? context : void 0;
|
|
@@ -46,5 +46,4 @@ var webmcp_client_default = defineNuxtPlugin({
|
|
|
46
46
|
});
|
|
47
47
|
}
|
|
48
48
|
});
|
|
49
|
-
//#endregion
|
|
50
49
|
export { webmcp_client_default as default };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { PropType } from "vue";
|
|
2
|
+
declare const _default: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
3
|
+
error: {
|
|
4
|
+
type: PropType<{
|
|
5
|
+
status?: number;
|
|
6
|
+
statusCode?: number;
|
|
7
|
+
}>;
|
|
8
|
+
default: () => {};
|
|
9
|
+
};
|
|
10
|
+
}>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
11
|
+
[key: string]: any;
|
|
12
|
+
}>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
|
13
|
+
error: {
|
|
14
|
+
type: PropType<{
|
|
15
|
+
status?: number;
|
|
16
|
+
statusCode?: number;
|
|
17
|
+
}>;
|
|
18
|
+
default: () => {};
|
|
19
|
+
};
|
|
20
|
+
}>> & Readonly<{}>, {
|
|
21
|
+
error: {
|
|
22
|
+
status?: number;
|
|
23
|
+
statusCode?: number;
|
|
24
|
+
};
|
|
25
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
26
|
+
export { _default as default };
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { WERVT_MARK } from "../shared/mark.js";
|
|
2
|
+
import { clearError, useRequestHeaders, useRequestURL, useState } from "#app";
|
|
3
|
+
import { defineComponent, h } from "vue";
|
|
4
|
+
// src/runtime/app/preview-error.ts
|
|
5
|
+
const TEXT = {
|
|
6
|
+
de: {
|
|
7
|
+
missing: "Diese Seite gibt es noch nicht",
|
|
8
|
+
building: "Die Startseite entsteht gerade",
|
|
9
|
+
broken: "Hier hakt gerade etwas",
|
|
10
|
+
hint: "Studio sieht den Fehler und kann ihn beheben.",
|
|
11
|
+
home: "Zur Startseite",
|
|
12
|
+
reload: "Neu laden"
|
|
13
|
+
},
|
|
14
|
+
en: {
|
|
15
|
+
missing: "This page doesn’t exist yet",
|
|
16
|
+
building: "The start page is being built",
|
|
17
|
+
broken: "Something isn’t working here",
|
|
18
|
+
hint: "Studio sees the error and can fix it.",
|
|
19
|
+
home: "Go to start",
|
|
20
|
+
reload: "Reload"
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
const STYLE = `
|
|
24
|
+
.wervt-preview-error { min-height: 100dvh; display: grid; place-content: center; justify-items: center;
|
|
25
|
+
gap: .75rem; padding: 2rem; text-align: center; font: 500 1rem/1.4 system-ui, -apple-system, sans-serif;
|
|
26
|
+
color: #0a0a0b; background: #fafaf9; --a: #2f5bff; }
|
|
27
|
+
.wervt-preview-error svg { width: 2.5rem; height: 2.5rem; margin-bottom: .5rem; }
|
|
28
|
+
.wervt-preview-error h1 { margin: 0; font-size: 1.25rem; font-weight: 600; }
|
|
29
|
+
.wervt-preview-error p { margin: 0; color: #6b6b70; }
|
|
30
|
+
.wervt-preview-error button { margin-top: .75rem; padding: .7rem 1.4rem; border: 0; border-radius: 999px;
|
|
31
|
+
background: #0a0a0b; color: #fafaf9; font: inherit; cursor: pointer; }
|
|
32
|
+
@media (prefers-color-scheme: dark) {
|
|
33
|
+
.wervt-preview-error { color: #fafaf9; background: #0a0a0b; --a: #5885ff; }
|
|
34
|
+
.wervt-preview-error p { color: #a1a1a6; }
|
|
35
|
+
.wervt-preview-error button { background: #fafaf9; color: #0a0a0b; }
|
|
36
|
+
}`;
|
|
37
|
+
var preview_error_default = defineComponent({
|
|
38
|
+
name: "WervtPreviewError",
|
|
39
|
+
props: { error: {
|
|
40
|
+
type: Object,
|
|
41
|
+
default: () => ({})
|
|
42
|
+
} },
|
|
43
|
+
setup(props) {
|
|
44
|
+
const lang = useState("wervt-preview-lang", () => {
|
|
45
|
+
return (import.meta.server ? useRequestHeaders(["accept-language"])["accept-language"] ?? "" : navigator.language).toLowerCase().startsWith("en") ? "en" : "de";
|
|
46
|
+
});
|
|
47
|
+
const path = useRequestURL().pathname;
|
|
48
|
+
return () => {
|
|
49
|
+
const text = TEXT[lang.value];
|
|
50
|
+
const notFound = (props.error.status ?? props.error.statusCode) === 404;
|
|
51
|
+
const building = notFound && path === "/";
|
|
52
|
+
const missing = notFound && !building;
|
|
53
|
+
return h("main", { class: "wervt-preview-error" }, [
|
|
54
|
+
h("style", STYLE),
|
|
55
|
+
h("span", {
|
|
56
|
+
innerHTML: WERVT_MARK,
|
|
57
|
+
"aria-hidden": "true"
|
|
58
|
+
}),
|
|
59
|
+
h("h1", building ? text.building : missing ? text.missing : text.broken),
|
|
60
|
+
notFound ? null : h("p", text.hint),
|
|
61
|
+
h("button", {
|
|
62
|
+
type: "button",
|
|
63
|
+
onClick: () => missing ? clearError({ redirect: "/" }) : window.location.reload()
|
|
64
|
+
}, missing ? text.home : text.reload)
|
|
65
|
+
]);
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
export { preview_error_default as default };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Message, Offset, Row } from "@electric-sql/client";
|
|
2
|
+
export interface ShapeSnapshot<T> {
|
|
3
|
+
rows: [string, T][];
|
|
4
|
+
offset: Offset;
|
|
5
|
+
handle?: string;
|
|
6
|
+
/**
|
|
7
|
+
* The route answered 401: the shape needs a signed-in user and there is none, e.g. an anonymous
|
|
8
|
+
* visitor of a public app, or the deploy's page check. No rows, and nothing to stream.
|
|
9
|
+
*/
|
|
10
|
+
unauthorized?: true;
|
|
11
|
+
}
|
|
12
|
+
/** The rows of the shape at `url` (absolute) as of now, without subscribing. */
|
|
13
|
+
export declare function loadSnapshot<T extends Row<unknown>>(url: string, fetchClient?: typeof fetch): Promise<ShapeSnapshot<T>>;
|
|
14
|
+
/** Applies Electric change messages to `rows`. Returns whether anything changed. */
|
|
15
|
+
export declare function applyMessages<T extends Row<unknown>>(rows: Map<string, T>, messages: Message<T>[]): boolean;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { shapeParser } from "../shared/shape.js";
|
|
2
|
+
import { ShapeStream, isChangeMessage, isControlMessage } from "@electric-sql/client";
|
|
3
|
+
// src/runtime/app/snapshot.ts
|
|
4
|
+
/** The rows of the shape at `url` (absolute) as of now, without subscribing. */
|
|
5
|
+
async function loadSnapshot(url, fetchClient) {
|
|
6
|
+
const rows = /* @__PURE__ */ new Map();
|
|
7
|
+
let fail = () => {};
|
|
8
|
+
const stream = new ShapeStream({
|
|
9
|
+
url,
|
|
10
|
+
subscribe: false,
|
|
11
|
+
fetchClient,
|
|
12
|
+
parser: shapeParser,
|
|
13
|
+
onError: (e) => fail(e)
|
|
14
|
+
});
|
|
15
|
+
try {
|
|
16
|
+
await new Promise((resolve, reject) => {
|
|
17
|
+
fail = reject;
|
|
18
|
+
stream.subscribe((messages) => {
|
|
19
|
+
applyMessages(rows, messages);
|
|
20
|
+
if (stream.isUpToDate) resolve();
|
|
21
|
+
}, (e) => reject(e));
|
|
22
|
+
});
|
|
23
|
+
} catch (e) {
|
|
24
|
+
if (e?.status === 401) return {
|
|
25
|
+
rows: [],
|
|
26
|
+
offset: "-1",
|
|
27
|
+
unauthorized: true
|
|
28
|
+
};
|
|
29
|
+
throw e;
|
|
30
|
+
} finally {
|
|
31
|
+
stream.unsubscribeAll();
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
rows: [...rows],
|
|
35
|
+
offset: stream.lastOffset,
|
|
36
|
+
handle: stream.shapeHandle
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/** Applies Electric change messages to `rows`. Returns whether anything changed. */
|
|
40
|
+
function applyMessages(rows, messages) {
|
|
41
|
+
let changed = false;
|
|
42
|
+
for (const message of messages) if (isChangeMessage(message)) {
|
|
43
|
+
const { key, value } = message;
|
|
44
|
+
switch (message.headers.operation) {
|
|
45
|
+
case "insert":
|
|
46
|
+
rows.set(key, value);
|
|
47
|
+
break;
|
|
48
|
+
case "update":
|
|
49
|
+
rows.set(key, {
|
|
50
|
+
...rows.get(key),
|
|
51
|
+
...value
|
|
52
|
+
});
|
|
53
|
+
break;
|
|
54
|
+
case "delete": rows.delete(key);
|
|
55
|
+
}
|
|
56
|
+
changed = true;
|
|
57
|
+
} else if (isControlMessage(message) && message.headers.control === "must-refetch") {
|
|
58
|
+
rows.clear();
|
|
59
|
+
changed = true;
|
|
60
|
+
}
|
|
61
|
+
return changed;
|
|
62
|
+
}
|
|
63
|
+
export { applyMessages, loadSnapshot };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { Row } from "@electric-sql/client";
|
|
2
1
|
import { Ref, ShallowRef } from "vue";
|
|
3
|
-
|
|
2
|
+
import { Row } from "@electric-sql/client";
|
|
4
3
|
declare global {
|
|
5
4
|
/**
|
|
6
5
|
* Shape routes and their rows, generated from `server/api/shapes/` (`.nuxt/types/wervt-shapes.d.ts`).
|
|
@@ -18,6 +17,12 @@ interface ShapeRefs<T> {
|
|
|
18
17
|
data: ShallowRef<T[]>;
|
|
19
18
|
error: Ref<unknown>;
|
|
20
19
|
isLive: Ref<boolean>;
|
|
20
|
+
/**
|
|
21
|
+
* The route refused the snapshot for lack of a signed-in user (401): `data` is empty because
|
|
22
|
+
* nobody may see it, not because there are no rows. Lets public apps show "sign in" instead
|
|
23
|
+
* of an empty state.
|
|
24
|
+
*/
|
|
25
|
+
unauthorized: Ref<boolean>;
|
|
21
26
|
}
|
|
22
27
|
/**
|
|
23
28
|
* Live rows of an Electric shape served by one of the app's shape routes
|
|
@@ -44,4 +49,4 @@ export declare function preloadShape<P extends keyof WervtShapeRoutes>(path: P,
|
|
|
44
49
|
export declare function preloadShape(path: string, options?: {
|
|
45
50
|
key?: string;
|
|
46
51
|
}): Promise<void>;
|
|
47
|
-
|
|
52
|
+
export {};
|
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
import { shapeParser } from "../shared/shape.js";
|
|
2
|
-
import {
|
|
3
|
-
import { getCurrentScope, onScopeDispose, ref, shallowRef } from "vue";
|
|
2
|
+
import { applyMessages, loadSnapshot } from "./snapshot.js";
|
|
4
3
|
import { onNuxtReady, useAsyncData, useRequestEvent, useRequestFetch } from "#app";
|
|
5
|
-
|
|
4
|
+
import { getCurrentScope, onScopeDispose, ref, shallowRef } from "vue";
|
|
5
|
+
import { ShapeStream } from "@electric-sql/client";
|
|
6
|
+
// src/runtime/app/useShape.ts
|
|
6
7
|
function useShape(path, options = {}) {
|
|
7
8
|
const key = options.key ?? `wervt-shape:${path}`;
|
|
8
9
|
const fetchClient = import.meta.server ? inProcessFetch() : void 0;
|
|
9
10
|
const asyncData = useAsyncData(key, () => {
|
|
10
11
|
const live = import.meta.client ? shapes.get(key) : void 0;
|
|
11
|
-
return live?.stream ? live.settled.then(() => snapshotOf(live)) : loadSnapshot(path, fetchClient);
|
|
12
|
+
return live?.stream ? live.settled.then(() => snapshotOf(live)) : loadSnapshot(absolute(path), fetchClient);
|
|
12
13
|
});
|
|
13
14
|
const shape = import.meta.client && options.live !== false ? acquire(key, path) : (import.meta.client ? shapes.get(key) : void 0) ?? createShape(path);
|
|
14
15
|
const result = {
|
|
15
16
|
data: shape.data,
|
|
16
17
|
error: shape.error,
|
|
17
|
-
isLive: shape.isLive
|
|
18
|
+
isLive: shape.isLive,
|
|
19
|
+
unauthorized: shape.unauthorized
|
|
18
20
|
};
|
|
19
21
|
const ready = Promise.resolve(asyncData).then(() => {
|
|
20
22
|
if (!shape.stream && !shape.seeded) {
|
|
@@ -22,6 +24,7 @@ function useShape(path, options = {}) {
|
|
|
22
24
|
shape.data.value = [...shape.rows.values()];
|
|
23
25
|
shape.offset = asyncData.data.value?.offset;
|
|
24
26
|
shape.handle = asyncData.data.value?.handle;
|
|
27
|
+
shape.unauthorized.value = asyncData.data.value?.unauthorized ?? false;
|
|
25
28
|
shape.seeded = true;
|
|
26
29
|
}
|
|
27
30
|
const error = asyncData.error.value ?? null;
|
|
@@ -34,7 +37,7 @@ function useShape(path, options = {}) {
|
|
|
34
37
|
if (import.meta.client && options.live !== false) {
|
|
35
38
|
onNuxtReady(async () => {
|
|
36
39
|
await ready;
|
|
37
|
-
start(shape);
|
|
40
|
+
if (!shape.unauthorized.value) start(shape);
|
|
38
41
|
});
|
|
39
42
|
if (getCurrentScope()) onScopeDispose(() => release(key, shape, KEEP_AFTER_UNMOUNT_MS));
|
|
40
43
|
}
|
|
@@ -63,6 +66,7 @@ function createShape(path) {
|
|
|
63
66
|
error: ref(null),
|
|
64
67
|
isLive: ref(false),
|
|
65
68
|
seeded: false,
|
|
69
|
+
unauthorized: ref(false),
|
|
66
70
|
settled,
|
|
67
71
|
settle,
|
|
68
72
|
users: 0
|
|
@@ -118,54 +122,6 @@ function snapshotOf(shape) {
|
|
|
118
122
|
handle: shape.stream?.shapeHandle ?? shape.handle
|
|
119
123
|
};
|
|
120
124
|
}
|
|
121
|
-
async function loadSnapshot(path, fetchClient) {
|
|
122
|
-
const rows = /* @__PURE__ */ new Map();
|
|
123
|
-
let fail = () => {};
|
|
124
|
-
const stream = new ShapeStream({
|
|
125
|
-
url: absolute(path),
|
|
126
|
-
subscribe: false,
|
|
127
|
-
fetchClient,
|
|
128
|
-
parser: shapeParser,
|
|
129
|
-
onError: (e) => fail(e)
|
|
130
|
-
});
|
|
131
|
-
await new Promise((resolve, reject) => {
|
|
132
|
-
fail = reject;
|
|
133
|
-
stream.subscribe((messages) => {
|
|
134
|
-
applyMessages(rows, messages);
|
|
135
|
-
if (stream.isUpToDate) resolve();
|
|
136
|
-
}, (e) => reject(e));
|
|
137
|
-
});
|
|
138
|
-
stream.unsubscribeAll();
|
|
139
|
-
return {
|
|
140
|
-
rows: [...rows],
|
|
141
|
-
offset: stream.lastOffset,
|
|
142
|
-
handle: stream.shapeHandle
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
/** Applies Electric change messages to `rows`. Returns whether anything changed. */
|
|
146
|
-
function applyMessages(rows, messages) {
|
|
147
|
-
let changed = false;
|
|
148
|
-
for (const message of messages) if (isChangeMessage(message)) {
|
|
149
|
-
const { key, value } = message;
|
|
150
|
-
switch (message.headers.operation) {
|
|
151
|
-
case "insert":
|
|
152
|
-
rows.set(key, value);
|
|
153
|
-
break;
|
|
154
|
-
case "update":
|
|
155
|
-
rows.set(key, {
|
|
156
|
-
...rows.get(key),
|
|
157
|
-
...value
|
|
158
|
-
});
|
|
159
|
-
break;
|
|
160
|
-
case "delete": rows.delete(key);
|
|
161
|
-
}
|
|
162
|
-
changed = true;
|
|
163
|
-
} else if (isControlMessage(message) && message.headers.control === "must-refetch") {
|
|
164
|
-
rows.clear();
|
|
165
|
-
changed = true;
|
|
166
|
-
}
|
|
167
|
-
return changed;
|
|
168
|
-
}
|
|
169
125
|
/**
|
|
170
126
|
* During SSR, calls the shape route in-process with the request's headers (cookies, auth).
|
|
171
127
|
* Nitro 2 (Nuxt 4.6) has `event.fetch`, which returns a Response; on Nitro 3 (Nuxt 5) the
|
|
@@ -198,5 +154,4 @@ function inProcessFetch() {
|
|
|
198
154
|
function absolute(path) {
|
|
199
155
|
return new URL(path, import.meta.client ? location.origin : "http://localhost").href;
|
|
200
156
|
}
|
|
201
|
-
//#endregion
|
|
202
157
|
export { preloadShape, useShape };
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//#region src/runtime/app/useWervtFeedback.d.ts
|
|
2
1
|
interface KontorWidget {
|
|
3
2
|
open(): void;
|
|
4
3
|
close(): void;
|
|
@@ -21,4 +20,4 @@ export declare function useWervtFeedback(): {
|
|
|
21
20
|
close: () => void | undefined;
|
|
22
21
|
setContext(values: Record<string, unknown>): void;
|
|
23
22
|
};
|
|
24
|
-
|
|
23
|
+
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/runtime/app/useWervtFeedback.ts
|
|
2
2
|
const pendingFeedbackContext = {};
|
|
3
3
|
/**
|
|
4
4
|
* Controls the feedback widget (`wervt: { feedback: { key } }`): open it from your own button,
|
|
@@ -17,5 +17,4 @@ function useWervtFeedback() {
|
|
|
17
17
|
}
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
|
-
//#endregion
|
|
21
20
|
export { pendingFeedbackContext, useWervtFeedback };
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
//#region src/runtime/app/useWervtTrack.d.ts
|
|
2
1
|
/**
|
|
3
2
|
* Custom events for wervt's analytics, e.g. `track('signup')` or `track('click', url)`. They
|
|
4
3
|
* show up with their counts and visitors in the dashboard and `wervt stats`. Like page views
|
|
5
4
|
* they are cookieless: only the event, its label and the current path are sent. Does nothing
|
|
6
5
|
* in `nuxt dev`, during SSR and with `wervt.analytics: false`.
|
|
7
6
|
*/
|
|
8
|
-
export declare function useWervtTrack(): (name: string, label?: string) => void;
|
|
9
|
-
//#endregion
|
|
7
|
+
export declare function useWervtTrack(): (name: string, label?: string) => void;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useRuntimeConfig } from "#app";
|
|
2
|
-
|
|
2
|
+
// src/runtime/app/useWervtTrack.ts
|
|
3
3
|
/**
|
|
4
4
|
* Custom events for wervt's analytics, e.g. `track('signup')` or `track('click', url)`. They
|
|
5
5
|
* show up with their counts and visitors in the dashboard and `wervt stats`. Like page views
|
|
@@ -20,5 +20,4 @@ function useWervtTrack() {
|
|
|
20
20
|
navigator.sendBeacon?.(`${base}/_wervt/view`, new Blob([body], { type: "application/json" }));
|
|
21
21
|
};
|
|
22
22
|
}
|
|
23
|
-
//#endregion
|
|
24
23
|
export { useWervtTrack };
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//#region src/runtime/app/useWervtUpdate.d.ts
|
|
2
1
|
/** State behind `useWervtUpdate()`, set by the update check (`plugins/update.client`). */
|
|
3
2
|
export declare const useUpdateAvailable: () => import("vue").Ref<boolean, boolean>;
|
|
4
3
|
/**
|
|
@@ -9,5 +8,4 @@ export declare const useUpdateAvailable: () => import("vue").Ref<boolean, boolea
|
|
|
9
8
|
export declare function useWervtUpdate(): {
|
|
10
9
|
available: Readonly<import("vue").Ref<boolean, boolean>>;
|
|
11
10
|
reload: () => void;
|
|
12
|
-
};
|
|
13
|
-
//#endregion
|
|
11
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { readonly } from "vue";
|
|
2
1
|
import { reloadNuxtApp, useState } from "#app";
|
|
3
|
-
|
|
2
|
+
import { readonly } from "vue";
|
|
3
|
+
// src/runtime/app/useWervtUpdate.ts
|
|
4
4
|
/** State behind `useWervtUpdate()`, set by the update check (`plugins/update.client`). */
|
|
5
5
|
const useUpdateAvailable = () => useState("wervt-update", () => false);
|
|
6
6
|
/**
|
|
@@ -17,5 +17,4 @@ function useWervtUpdate() {
|
|
|
17
17
|
})
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
|
-
//#endregion
|
|
21
20
|
export { useUpdateAvailable, useWervtUpdate };
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { WervtUser } from "../shared/user.js";
|
|
2
|
-
//#region src/runtime/app/useWervtUser.d.ts
|
|
3
2
|
/**
|
|
4
3
|
* The signed-in wervt user, or `null`. Read during SSR and kept in the payload; apps without
|
|
5
4
|
* SSR load it from `/_wervt/user` before they render. Public apps get one when the visitor
|
|
@@ -9,5 +8,4 @@ export declare function useWervtUser(): import("vue").Ref<WervtUser | null, Werv
|
|
|
9
8
|
/** Sign in with wervt, then come back to `redirect` (a path in this app). */
|
|
10
9
|
export declare const wervtLoginUrl: (redirect?: string) => string;
|
|
11
10
|
/** Sign out of wervt (all apps), then come back to `redirect`. */
|
|
12
|
-
export declare const wervtLogoutUrl: (redirect?: string) => string;
|
|
13
|
-
//#endregion
|
|
11
|
+
export declare const wervtLogoutUrl: (redirect?: string) => string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DEV_LOGIN_COOKIE, devUser, parseWervtUser } from "../shared/user.js";
|
|
2
2
|
import { useCookie, useRequestHeaders, useState } from "#app";
|
|
3
|
-
|
|
3
|
+
// src/runtime/app/useWervtUser.ts
|
|
4
4
|
/**
|
|
5
5
|
* The signed-in wervt user, or `null`. Read during SSR and kept in the payload; apps without
|
|
6
6
|
* SSR load it from `/_wervt/user` before they render. Public apps get one when the visitor
|
|
@@ -14,5 +14,4 @@ function useWervtUser() {
|
|
|
14
14
|
const wervtLoginUrl = (redirect = "/") => `/_wervt/login?redirect=${encodeURIComponent(redirect)}`;
|
|
15
15
|
/** Sign out of wervt (all apps), then come back to `redirect`. */
|
|
16
16
|
const wervtLogoutUrl = (redirect = "/") => `/_wervt/logout?redirect=${encodeURIComponent(redirect)}`;
|
|
17
|
-
//#endregion
|
|
18
17
|
export { useWervtUser, wervtLoginUrl, wervtLogoutUrl };
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { InjectionKey, PropType } from "vue";
|
|
2
|
-
//#region src/runtime/email/components.d.ts
|
|
3
2
|
/** The template's subject, handed to renderEmail() while rendering. */
|
|
4
3
|
export declare const SUBJECT_KEY: InjectionKey<(subject: string) => void>;
|
|
5
4
|
export declare const emailTheme: {
|
|
@@ -187,5 +186,4 @@ export declare const EmailCode: import("vue").DefineComponent<import("vue").Extr
|
|
|
187
186
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
188
187
|
export declare const EmailDivider: import("vue").DefineComponent<{}, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
189
188
|
[key: string]: any;
|
|
190
|
-
}>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
191
|
-
//#endregion
|
|
189
|
+
}>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { defineComponent, h, inject } from "vue";
|
|
2
|
-
|
|
2
|
+
// src/runtime/email/components.ts
|
|
3
3
|
/** The template's subject, handed to renderEmail() while rendering. */
|
|
4
4
|
const SUBJECT_KEY = Symbol("wervt-email-subject");
|
|
5
5
|
const emailTheme = {
|
|
@@ -598,5 +598,4 @@ function table(attrs, children) {
|
|
|
598
598
|
}
|
|
599
599
|
}, children);
|
|
600
600
|
}
|
|
601
|
-
//#endregion
|
|
602
601
|
export { EmailButton, EmailCode, EmailDivider, EmailHeading, EmailLink, EmailMuted, EmailText, SUBJECT_KEY, WervtEmail, emailTheme };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { EmailButton, EmailCode, EmailDivider, EmailHeading, EmailLink, EmailMuted, EmailText, WervtEmail, emailTheme } from "./components.js";
|
|
2
|
+
import { InviteEmail } from "./invite.js";
|
|
2
3
|
import { EmailProps, RenderedEmail, renderEmail } from "./render.js";
|
|
3
|
-
export { EmailButton, EmailCode, EmailDivider, EmailHeading, EmailLink, EmailMuted, type EmailProps, EmailText, type RenderedEmail, WervtEmail, emailTheme, renderEmail };
|
|
4
|
+
export { EmailButton, EmailCode, EmailDivider, EmailHeading, EmailLink, EmailMuted, type EmailProps, EmailText, InviteEmail, type RenderedEmail, WervtEmail, emailTheme, renderEmail };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { EmailButton, EmailCode, EmailDivider, EmailHeading, EmailLink, EmailMuted, EmailText, WervtEmail, emailTheme } from "./components.js";
|
|
2
|
+
import { InviteEmail } from "./invite.js";
|
|
2
3
|
import { renderEmail } from "./render.js";
|
|
3
|
-
export { EmailButton, EmailCode, EmailDivider, EmailHeading, EmailLink, EmailMuted, EmailText, WervtEmail, emailTheme, renderEmail };
|
|
4
|
+
export { EmailButton, EmailCode, EmailDivider, EmailHeading, EmailLink, EmailMuted, EmailText, InviteEmail, WervtEmail, emailTheme, renderEmail };
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An invitation to an app: who invited, where to go, and that signing in with this address is
|
|
3
|
+
* all it takes. Send it with `sendMail({ to, template: InviteEmail, props: { by, app, url } })`.
|
|
4
|
+
*/
|
|
5
|
+
export declare const InviteEmail: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
6
|
+
/** Who invited them, by name or email. */
|
|
7
|
+
by: {
|
|
8
|
+
type: StringConstructor;
|
|
9
|
+
required: true;
|
|
10
|
+
};
|
|
11
|
+
/** The app they're invited to, as people know it. */
|
|
12
|
+
app: {
|
|
13
|
+
type: StringConstructor;
|
|
14
|
+
required: true;
|
|
15
|
+
};
|
|
16
|
+
/** Where the button goes: the app, or a link that lets them in. */
|
|
17
|
+
url: {
|
|
18
|
+
type: StringConstructor;
|
|
19
|
+
required: true;
|
|
20
|
+
};
|
|
21
|
+
/** What the app is, in a sentence after "<by> invited you to <app>." */
|
|
22
|
+
message: {
|
|
23
|
+
type: StringConstructor;
|
|
24
|
+
default: string;
|
|
25
|
+
};
|
|
26
|
+
/** The button's label; "Open <app>" unless set. */
|
|
27
|
+
action: {
|
|
28
|
+
type: StringConstructor;
|
|
29
|
+
default: string;
|
|
30
|
+
};
|
|
31
|
+
/** A hint under the button; by default, to sign in with this address. */
|
|
32
|
+
note: {
|
|
33
|
+
type: StringConstructor;
|
|
34
|
+
default: string;
|
|
35
|
+
};
|
|
36
|
+
}>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
37
|
+
[key: string]: any;
|
|
38
|
+
}>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
|
39
|
+
/** Who invited them, by name or email. */
|
|
40
|
+
by: {
|
|
41
|
+
type: StringConstructor;
|
|
42
|
+
required: true;
|
|
43
|
+
};
|
|
44
|
+
/** The app they're invited to, as people know it. */
|
|
45
|
+
app: {
|
|
46
|
+
type: StringConstructor;
|
|
47
|
+
required: true;
|
|
48
|
+
};
|
|
49
|
+
/** Where the button goes: the app, or a link that lets them in. */
|
|
50
|
+
url: {
|
|
51
|
+
type: StringConstructor;
|
|
52
|
+
required: true;
|
|
53
|
+
};
|
|
54
|
+
/** What the app is, in a sentence after "<by> invited you to <app>." */
|
|
55
|
+
message: {
|
|
56
|
+
type: StringConstructor;
|
|
57
|
+
default: string;
|
|
58
|
+
};
|
|
59
|
+
/** The button's label; "Open <app>" unless set. */
|
|
60
|
+
action: {
|
|
61
|
+
type: StringConstructor;
|
|
62
|
+
default: string;
|
|
63
|
+
};
|
|
64
|
+
/** A hint under the button; by default, to sign in with this address. */
|
|
65
|
+
note: {
|
|
66
|
+
type: StringConstructor;
|
|
67
|
+
default: string;
|
|
68
|
+
};
|
|
69
|
+
}>> & Readonly<{}>, {
|
|
70
|
+
message: string;
|
|
71
|
+
action: string;
|
|
72
|
+
note: string;
|
|
73
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { EmailButton, EmailHeading, EmailMuted, EmailText, WervtEmail } from "./components.js";
|
|
2
|
+
import { defineComponent, h } from "vue";
|
|
3
|
+
// src/runtime/email/invite.ts
|
|
4
|
+
/**
|
|
5
|
+
* An invitation to an app: who invited, where to go, and that signing in with this address is
|
|
6
|
+
* all it takes. Send it with `sendMail({ to, template: InviteEmail, props: { by, app, url } })`.
|
|
7
|
+
*/
|
|
8
|
+
const InviteEmail = defineComponent({
|
|
9
|
+
name: "InviteEmail",
|
|
10
|
+
props: {
|
|
11
|
+
/** Who invited them, by name or email. */
|
|
12
|
+
by: {
|
|
13
|
+
type: String,
|
|
14
|
+
required: true
|
|
15
|
+
},
|
|
16
|
+
/** The app they're invited to, as people know it. */
|
|
17
|
+
app: {
|
|
18
|
+
type: String,
|
|
19
|
+
required: true
|
|
20
|
+
},
|
|
21
|
+
/** Where the button goes: the app, or a link that lets them in. */
|
|
22
|
+
url: {
|
|
23
|
+
type: String,
|
|
24
|
+
required: true
|
|
25
|
+
},
|
|
26
|
+
/** What the app is, in a sentence after "<by> invited you to <app>." */
|
|
27
|
+
message: {
|
|
28
|
+
type: String,
|
|
29
|
+
default: ""
|
|
30
|
+
},
|
|
31
|
+
/** The button's label; "Open <app>" unless set. */
|
|
32
|
+
action: {
|
|
33
|
+
type: String,
|
|
34
|
+
default: ""
|
|
35
|
+
},
|
|
36
|
+
/** A hint under the button; by default, to sign in with this address. */
|
|
37
|
+
note: {
|
|
38
|
+
type: String,
|
|
39
|
+
default: ""
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
setup(props) {
|
|
43
|
+
return () => h(WervtEmail, {
|
|
44
|
+
subject: `${props.by} invited you to ${props.app}`,
|
|
45
|
+
app: props.app,
|
|
46
|
+
preview: props.message || `Join ${props.app}`,
|
|
47
|
+
reason: `You received this email because ${props.by} invited this address.`
|
|
48
|
+
}, () => [
|
|
49
|
+
h(EmailHeading, { eyebrow: "Invitation" }, () => `You're invited to ${props.app}`),
|
|
50
|
+
h(EmailText, () => [`${props.by} invited you to ${props.app}.`, props.message].filter(Boolean).join(" ")),
|
|
51
|
+
h(EmailButton, {
|
|
52
|
+
href: props.url,
|
|
53
|
+
color: "neutral"
|
|
54
|
+
}, () => props.action || `Open ${props.app}`),
|
|
55
|
+
h(EmailMuted, () => props.note || "Sign in with this email address to get in.")
|
|
56
|
+
]);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
export { InviteEmail };
|