@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.
Files changed (128) hide show
  1. package/README.md +2 -0
  2. package/dist/dev.d.ts +4 -4
  3. package/dist/dev.js +33 -11
  4. package/dist/env-types.js +13 -0
  5. package/dist/icons.js +30 -0
  6. package/dist/loading.js +30 -0
  7. package/dist/module.d.ts +0 -2
  8. package/dist/module.js +42 -2
  9. package/dist/node_modules/.pnpm/nitro@3.0.260903-beta/node_modules/nitro/dist/runtime/internal/task.js +1 -2
  10. package/dist/node_modules/.pnpm/nitro@3.0.260903-beta/node_modules/nitro/dist/runtime/task.js +1 -2
  11. package/dist/og-image.js +38 -0
  12. package/dist/preview.d.ts +1 -3
  13. package/dist/preview.js +1 -2
  14. package/dist/provision.d.ts +31 -4
  15. package/dist/provision.js +82 -21
  16. package/dist/route-tools.js +1 -2
  17. package/dist/runtime/app/plugins/analytics.client.d.ts +0 -2
  18. package/dist/runtime/app/plugins/analytics.client.js +1 -2
  19. package/dist/runtime/app/plugins/feedback.client.d.ts +0 -2
  20. package/dist/runtime/app/plugins/feedback.client.js +1 -2
  21. package/dist/runtime/app/plugins/update.client.d.ts +0 -2
  22. package/dist/runtime/app/plugins/update.client.js +1 -2
  23. package/dist/runtime/app/plugins/user.client.d.ts +0 -2
  24. package/dist/runtime/app/plugins/user.client.js +1 -2
  25. package/dist/runtime/app/plugins/webmcp.client.d.ts +0 -2
  26. package/dist/runtime/app/plugins/webmcp.client.js +1 -2
  27. package/dist/runtime/app/preview-error.d.ts +26 -0
  28. package/dist/runtime/app/preview-error.js +69 -0
  29. package/dist/runtime/app/snapshot.d.ts +15 -0
  30. package/dist/runtime/app/snapshot.js +63 -0
  31. package/dist/runtime/app/useShape.d.ts +8 -3
  32. package/dist/runtime/app/useShape.js +10 -55
  33. package/dist/runtime/app/useWervtFeedback.d.ts +1 -2
  34. package/dist/runtime/app/useWervtFeedback.js +1 -2
  35. package/dist/runtime/app/useWervtTrack.d.ts +1 -3
  36. package/dist/runtime/app/useWervtTrack.js +1 -2
  37. package/dist/runtime/app/useWervtUpdate.d.ts +1 -3
  38. package/dist/runtime/app/useWervtUpdate.js +2 -3
  39. package/dist/runtime/app/useWervtUser.d.ts +1 -3
  40. package/dist/runtime/app/useWervtUser.js +1 -2
  41. package/dist/runtime/email/components.d.ts +1 -3
  42. package/dist/runtime/email/components.js +1 -2
  43. package/dist/runtime/email/index.d.ts +2 -1
  44. package/dist/runtime/email/index.js +2 -1
  45. package/dist/runtime/email/invite.d.ts +73 -0
  46. package/dist/runtime/email/invite.js +59 -0
  47. package/dist/runtime/email/render.d.ts +1 -3
  48. package/dist/runtime/email/render.js +1 -2
  49. package/dist/runtime/server/access.d.ts +4 -5
  50. package/dist/runtime/server/access.js +4 -4
  51. package/dist/runtime/server/agent-token-plugin.d.ts +11 -0
  52. package/dist/runtime/server/agent-token-plugin.js +9 -0
  53. package/dist/runtime/server/anthropic.d.ts +19 -0
  54. package/dist/runtime/server/anthropic.js +52 -0
  55. package/dist/runtime/server/boot.d.ts +0 -2
  56. package/dist/runtime/server/boot.js +1 -2
  57. package/dist/runtime/server/db.d.ts +1 -3
  58. package/dist/runtime/server/db.js +1 -2
  59. package/dist/runtime/server/dev-auth.d.ts +0 -2
  60. package/dist/runtime/server/dev-auth.js +1 -2
  61. package/dist/runtime/server/email-preview.d.ts +0 -2
  62. package/dist/runtime/server/email-preview.js +1 -2
  63. package/dist/runtime/server/identity.d.ts +18 -0
  64. package/dist/runtime/server/identity.js +36 -0
  65. package/dist/runtime/server/index.d.ts +4 -3
  66. package/dist/runtime/server/index.js +4 -3
  67. package/dist/runtime/server/logger.d.ts +0 -2
  68. package/dist/runtime/server/logger.js +1 -2
  69. package/dist/runtime/server/mail.d.ts +1 -3
  70. package/dist/runtime/server/mail.js +1 -2
  71. package/dist/runtime/server/mcp-auth-server.d.ts +0 -2
  72. package/dist/runtime/server/mcp-auth-server.js +1 -2
  73. package/dist/runtime/server/mcp-auth.d.ts +1 -3
  74. package/dist/runtime/server/mcp-auth.js +1 -2
  75. package/dist/runtime/server/mcp-claims.d.ts +1 -3
  76. package/dist/runtime/server/mcp-claims.js +1 -2
  77. package/dist/runtime/server/mcp-metadata.d.ts +0 -2
  78. package/dist/runtime/server/mcp-metadata.js +1 -2
  79. package/dist/runtime/server/mcp.d.ts +0 -2
  80. package/dist/runtime/server/mcp.js +1 -2
  81. package/dist/runtime/server/preview-errors-plugin.d.ts +13 -0
  82. package/dist/runtime/server/preview-errors-plugin.js +19 -0
  83. package/dist/runtime/server/seed.d.ts +1 -3
  84. package/dist/runtime/server/seed.js +1 -2
  85. package/dist/runtime/server/shape.d.ts +1 -3
  86. package/dist/runtime/server/shape.js +1 -2
  87. package/dist/runtime/server/storage-plugin.d.ts +1 -3
  88. package/dist/runtime/server/storage-plugin.js +1 -2
  89. package/dist/runtime/server/task-handlers.d.ts +2 -3
  90. package/dist/runtime/server/task-handlers.js +1 -2
  91. package/dist/runtime/server/task-list-nitro2.d.ts +0 -2
  92. package/dist/runtime/server/task-list-nitro2.js +1 -2
  93. package/dist/runtime/server/task-list-nitro3.d.ts +0 -2
  94. package/dist/runtime/server/task-list-nitro3.js +1 -2
  95. package/dist/runtime/server/task-run-nitro2.d.ts +0 -2
  96. package/dist/runtime/server/task-run-nitro2.js +1 -2
  97. package/dist/runtime/server/task-run-nitro3.d.ts +0 -2
  98. package/dist/runtime/server/task-run-nitro3.js +1 -2
  99. package/dist/runtime/server/tasks.d.ts +1 -3
  100. package/dist/runtime/server/tasks.js +1 -2
  101. package/dist/runtime/server/tool-handlers.d.ts +1 -3
  102. package/dist/runtime/server/tool-handlers.js +1 -2
  103. package/dist/runtime/server/tool-list.js +1 -2
  104. package/dist/runtime/server/tool-run.js +1 -2
  105. package/dist/runtime/server/tools.d.ts +1 -2
  106. package/dist/runtime/server/tools.js +1 -2
  107. package/dist/runtime/server/user-route.d.ts +0 -2
  108. package/dist/runtime/server/user-route.js +1 -2
  109. package/dist/runtime/server/user.d.ts +9 -5
  110. package/dist/runtime/server/user.js +29 -5
  111. package/dist/runtime/server/validate.d.ts +1 -2
  112. package/dist/runtime/server/validate.js +1 -2
  113. package/dist/runtime/shared/agent-token.d.ts +7 -0
  114. package/dist/runtime/shared/agent-token.js +16 -0
  115. package/dist/runtime/shared/mark.d.ts +1 -0
  116. package/dist/runtime/shared/mark.js +3 -0
  117. package/dist/runtime/shared/preview-errors.d.ts +10 -0
  118. package/dist/runtime/shared/preview-errors.js +14 -0
  119. package/dist/runtime/shared/shape.d.ts +1 -2
  120. package/dist/runtime/shared/shape.js +1 -2
  121. package/dist/runtime/shared/user.d.ts +9 -3
  122. package/dist/runtime/shared/user.js +5 -3
  123. package/dist/seed.js +1 -2
  124. package/dist/shapes.js +1 -2
  125. package/dist/storage.d.ts +50 -4
  126. package/dist/storage.js +224 -5
  127. package/dist/studio-preview.js +34 -0
  128. 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
- //#region src/runtime/app/plugins/update.client.ts
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,4 +1,2 @@
1
- //#region src/runtime/app/plugins/user.client.d.ts
2
1
  declare const _default: import("#app").Plugin<Record<string, unknown>> & import("#app").ObjectPlugin<Record<string, unknown>>;
3
- //#endregion
4
2
  export { _default as default };
@@ -1,6 +1,6 @@
1
1
  import { useWervtUser } from "../useWervtUser.js";
2
2
  import { defineNuxtPlugin } from "#app";
3
- //#region src/runtime/app/plugins/user.client.ts
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,4 +1,2 @@
1
- //#region src/runtime/app/plugins/webmcp.client.d.ts
2
1
  declare const _default: import("#app").Plugin<Record<string, unknown>> & import("#app").ObjectPlugin<Record<string, unknown>>;
3
- //#endregion
4
2
  export { _default as default };
@@ -1,5 +1,5 @@
1
1
  import { defineNuxtPlugin, onNuxtReady, useRuntimeConfig } from "#app";
2
- //#region src/runtime/app/plugins/webmcp.client.ts
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
- //#region src/runtime/app/useShape.d.ts
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
- //#endregion
52
+ export {};
@@ -1,20 +1,22 @@
1
1
  import { shapeParser } from "../shared/shape.js";
2
- import { ShapeStream, isChangeMessage, isControlMessage } from "@electric-sql/client";
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
- //#region src/runtime/app/useShape.ts
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
- //#endregion
23
+ export {};
@@ -1,4 +1,4 @@
1
- //#region src/runtime/app/useWervtFeedback.ts
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
- //#region src/runtime/app/useWervtTrack.ts
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
- //#region src/runtime/app/useWervtUpdate.ts
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
- //#region src/runtime/app/useWervtUser.ts
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
- //#region src/runtime/email/components.ts
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 };