@wervt/nuxt 0.3.9 → 0.3.11

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 (68) hide show
  1. package/dist/brand.d.ts +16 -3
  2. package/dist/brand.js +22 -6
  3. package/dist/module.d.ts +3 -14
  4. package/dist/module.js +24 -59
  5. package/dist/provision.d.ts +1 -77
  6. package/dist/provision.js +1 -238
  7. package/dist/runtime/app/plugins/analytics.client.js +2 -2
  8. package/dist/runtime/app/plugins/feedback.client.js +11 -22
  9. package/dist/runtime/app/plugins/user.client.js +2 -7
  10. package/dist/runtime/app/plugins/webmcp.client.js +4 -41
  11. package/dist/runtime/app/snapshot.d.ts +2 -15
  12. package/dist/runtime/app/snapshot.js +1 -63
  13. package/dist/runtime/app/useShape.js +1 -1
  14. package/dist/runtime/app/useWervtFeedback.d.ts +1 -13
  15. package/dist/runtime/app/useWervtFeedback.js +3 -11
  16. package/dist/runtime/app/useWervtTrack.js +4 -8
  17. package/dist/runtime/app/useWervtUser.d.ts +3 -2
  18. package/dist/runtime/app/useWervtUser.js +3 -2
  19. package/dist/runtime/email/components.d.ts +18 -0
  20. package/dist/runtime/email/components.js +15 -4
  21. package/dist/runtime/email/invite.d.ts +13 -0
  22. package/dist/runtime/email/invite.js +30 -14
  23. package/dist/runtime/i18n.d.ts +3 -0
  24. package/dist/runtime/i18n.js +3 -0
  25. package/dist/runtime/server/access.d.ts +3 -22
  26. package/dist/runtime/server/access.js +7 -24
  27. package/dist/runtime/server/anthropic.d.ts +2 -7
  28. package/dist/runtime/server/anthropic.js +6 -37
  29. package/dist/runtime/server/db.d.ts +2 -17
  30. package/dist/runtime/server/db.js +1 -35
  31. package/dist/runtime/server/errors.d.ts +6 -0
  32. package/dist/runtime/server/errors.js +21 -0
  33. package/dist/runtime/server/identity.d.ts +3 -9
  34. package/dist/runtime/server/identity.js +4 -23
  35. package/dist/runtime/server/index.d.ts +1 -1
  36. package/dist/runtime/server/index.js +1 -1
  37. package/dist/runtime/server/mail.js +11 -29
  38. package/dist/runtime/server/mcp-claims.d.ts +2 -3
  39. package/dist/runtime/server/mcp-claims.js +1 -17
  40. package/dist/runtime/server/seed.d.ts +2 -16
  41. package/dist/runtime/server/seed.js +1 -26
  42. package/dist/runtime/server/shape.d.ts +2 -2
  43. package/dist/runtime/server/shape.js +5 -45
  44. package/dist/runtime/server/task-handlers.d.ts +1 -5
  45. package/dist/runtime/server/task-handlers.js +3 -9
  46. package/dist/runtime/server/task-run-nitro2.d.ts +1 -5
  47. package/dist/runtime/server/task-run-nitro3.d.ts +1 -5
  48. package/dist/runtime/server/tasks.d.ts +2 -32
  49. package/dist/runtime/server/tasks.js +1 -9
  50. package/dist/runtime/server/user-route.d.ts +1 -3
  51. package/dist/runtime/server/user.js +8 -11
  52. package/dist/runtime/shared/agent-token.d.ts +2 -7
  53. package/dist/runtime/shared/agent-token.js +1 -16
  54. package/dist/runtime/shared/i18n.d.ts +28 -0
  55. package/dist/runtime/shared/i18n.js +54 -0
  56. package/dist/runtime/shared/messages.d.ts +15 -0
  57. package/dist/runtime/shared/messages.js +33 -0
  58. package/dist/runtime/shared/shape.d.ts +2 -17
  59. package/dist/runtime/shared/shape.js +1 -15
  60. package/dist/runtime/shared/user.d.ts +3 -19
  61. package/dist/runtime/shared/user.js +1 -9
  62. package/package.json +9 -1
  63. package/dist/dev.d.ts +0 -31
  64. package/dist/dev.js +0 -158
  65. package/dist/jsdoc-imports.js +0 -25
  66. package/dist/seed.js +0 -51
  67. package/dist/storage.d.ts +0 -72
  68. package/dist/storage.js +0 -362
@@ -1,49 +1,12 @@
1
1
  import { defineNuxtPlugin, onNuxtReady, useRuntimeConfig } from "#app";
2
+ import { hasWebMcp, registerTools } from "@wervt/client";
2
3
  // src/runtime/app/plugins/webmcp.client.ts
3
- function pageModelContext() {
4
- const context = document.modelContext ?? navigator.modelContext;
5
- return typeof context?.registerTool === "function" ? context : void 0;
6
- }
7
4
  var webmcp_client_default = defineNuxtPlugin({
8
5
  name: "wervt:webmcp",
9
6
  setup() {
10
- const context = pageModelContext();
11
- if (!context) return;
12
- const base = useRuntimeConfig().app.baseURL.replace(/\/$/, "");
13
- async function execute(name, input) {
14
- const res = await fetch(`${base}/_wervt/tools/${encodeURIComponent(name)}`, {
15
- method: "POST",
16
- headers: { "content-type": "application/json" },
17
- body: JSON.stringify(input ?? {})
18
- });
19
- const body = await res.json().catch(() => void 0);
20
- if (res.ok) return body?.result ?? null;
21
- const issues = body?.issues ?? body?.data;
22
- const message = (typeof body?.error === "string" ? body.error : body?.message) ?? `${res.status} ${res.statusText}`;
23
- throw new Error(issues ? `${message} ${JSON.stringify(issues)}` : message);
24
- }
25
- onNuxtReady(async () => {
26
- let tools;
27
- try {
28
- const res = await fetch(`${base}/_wervt/tools`);
29
- if (!res.ok) return;
30
- tools = await res.json();
31
- } catch {
32
- return;
33
- }
34
- for (const tool of tools) try {
35
- const result = context.registerTool({
36
- name: tool.name,
37
- description: tool.description,
38
- inputSchema: tool.inputSchema,
39
- ...tool.readOnly ? { annotations: { readOnlyHint: true } } : {},
40
- execute: (input) => execute(tool.name, input)
41
- });
42
- if (result instanceof Promise) result.catch((e) => console.warn(`wervt: could not register tool ${tool.name}`, e));
43
- } catch (e) {
44
- console.warn(`wervt: could not register tool ${tool.name}`, e);
45
- }
46
- });
7
+ if (!hasWebMcp()) return;
8
+ const base = useRuntimeConfig().app.baseURL;
9
+ onNuxtReady(() => registerTools({ base }));
47
10
  }
48
11
  });
49
12
  export { webmcp_client_default as default };
@@ -1,15 +1,2 @@
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;
1
+ import { ShapeSnapshot, applyMessages, loadSnapshot } from "@wervt/client/shape";
2
+ export { type ShapeSnapshot, applyMessages, loadSnapshot };
@@ -1,63 +1 @@
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
+ export { applyMessages, loadSnapshot } from "@wervt/client/shape";
@@ -1,5 +1,5 @@
1
- import { shapeParser } from "../shared/shape.js";
2
1
  import { applyMessages, loadSnapshot } from "./snapshot.js";
2
+ import { shapeParser } from "../shared/shape.js";
3
3
  import { onNuxtReady, useAsyncData, useRequestEvent, useRequestFetch } from "#app";
4
4
  import { getCurrentScope, onScopeDispose, ref, shallowRef } from "vue";
5
5
  import { ShapeStream } from "@electric-sql/client";
@@ -1,14 +1,3 @@
1
- interface KontorWidget {
2
- open(): void;
3
- close(): void;
4
- setContext(values: Record<string, unknown>): void;
5
- }
6
- declare global {
7
- interface Window {
8
- kontor?: KontorWidget;
9
- }
10
- }
11
- export declare const pendingFeedbackContext: Record<string, unknown>;
12
1
  /**
13
2
  * Controls the feedback widget (`wervt: { feedback: { key } }`): open it from your own button,
14
3
  * or add context to every report, e.g. what the user was looking at.
@@ -19,5 +8,4 @@ export declare function useWervtFeedback(): {
19
8
  open: () => void | undefined;
20
9
  close: () => void | undefined;
21
10
  setContext(values: Record<string, unknown>): void;
22
- };
23
- export {};
11
+ };
@@ -1,5 +1,5 @@
1
+ import { feedback } from "@wervt/client";
1
2
  // src/runtime/app/useWervtFeedback.ts
2
- const pendingFeedbackContext = {};
3
3
  /**
4
4
  * Controls the feedback widget (`wervt: { feedback: { key } }`): open it from your own button,
5
5
  * or add context to every report, e.g. what the user was looking at.
@@ -7,14 +7,6 @@ const pendingFeedbackContext = {};
7
7
  * @example useWervtFeedback().setContext({ room: room.id })
8
8
  */
9
9
  function useWervtFeedback() {
10
- const widget = () => import.meta.client ? window.kontor : void 0;
11
- return {
12
- open: () => widget()?.open(),
13
- close: () => widget()?.close(),
14
- setContext(values) {
15
- Object.assign(pendingFeedbackContext, values);
16
- widget()?.setContext(values);
17
- }
18
- };
10
+ return feedback;
19
11
  }
20
- export { pendingFeedbackContext, useWervtFeedback };
12
+ export { useWervtFeedback };
@@ -1,4 +1,5 @@
1
1
  import { useRuntimeConfig } from "#app";
2
+ import { track } from "@wervt/client";
2
3
  // src/runtime/app/useWervtTrack.ts
3
4
  /**
4
5
  * Custom events for wervt's analytics, e.g. `track('signup')` or `track('click', url)`. They
@@ -9,15 +10,10 @@ import { useRuntimeConfig } from "#app";
9
10
  function useWervtTrack() {
10
11
  const config = useRuntimeConfig();
11
12
  const enabled = config.public.wervt?.analytics;
12
- const base = config.app.baseURL.replace(/\/$/, "");
13
+ const base = config.app.baseURL;
13
14
  return (name, label) => {
14
- if (!enabled || import.meta.server || typeof navigator === "undefined") return;
15
- const body = JSON.stringify({
16
- path: location.pathname,
17
- name,
18
- label
19
- });
20
- navigator.sendBeacon?.(`${base}/_wervt/view`, new Blob([body], { type: "application/json" }));
15
+ if (!enabled || import.meta.server) return;
16
+ track(name, label, { base });
21
17
  };
22
18
  }
23
19
  export { useWervtTrack };
@@ -1,4 +1,5 @@
1
1
  import { WervtUser } from "../shared/user.js";
2
+ import { loginUrl, logoutUrl } from "@wervt/client";
2
3
  /**
3
4
  * The signed-in wervt user, or `null`. Read during SSR and kept in the payload; apps without
4
5
  * SSR load it from `/_wervt/user` before they render. Public apps get one when the visitor
@@ -6,6 +7,6 @@ import { WervtUser } from "../shared/user.js";
6
7
  */
7
8
  export declare function useWervtUser(): import("vue").Ref<WervtUser | null, WervtUser | null>;
8
9
  /** Sign in with wervt, then come back to `redirect` (a path in this app). */
9
- export declare const wervtLoginUrl: (redirect?: string) => string;
10
+ export declare const wervtLoginUrl: typeof loginUrl;
10
11
  /** Sign out of wervt (all apps), then come back to `redirect`. */
11
- export declare const wervtLogoutUrl: (redirect?: string) => string;
12
+ export declare const wervtLogoutUrl: typeof logoutUrl;
@@ -1,5 +1,6 @@
1
1
  import { DEV_LOGIN_COOKIE, devUser, parseWervtUser } from "../shared/user.js";
2
2
  import { useCookie, useRequestHeaders, useState } from "#app";
3
+ import { loginUrl, logoutUrl } from "@wervt/client";
3
4
  // src/runtime/app/useWervtUser.ts
4
5
  /**
5
6
  * The signed-in wervt user, or `null`. Read during SSR and kept in the payload; apps without
@@ -11,7 +12,7 @@ function useWervtUser() {
11
12
  return useState("wervt-user", () => import.meta.server ? parseWervtUser(useRequestHeaders(["x-wervt-user"])["x-wervt-user"]) ?? devUser(String(devLogin?.value) === "1") ?? null : null);
12
13
  }
13
14
  /** Sign in with wervt, then come back to `redirect` (a path in this app). */
14
- const wervtLoginUrl = (redirect = "/") => `/_wervt/login?redirect=${encodeURIComponent(redirect)}`;
15
+ const wervtLoginUrl = loginUrl;
15
16
  /** Sign out of wervt (all apps), then come back to `redirect`. */
16
- const wervtLogoutUrl = (redirect = "/") => `/_wervt/logout?redirect=${encodeURIComponent(redirect)}`;
17
+ const wervtLogoutUrl = logoutUrl;
17
18
  export { useWervtUser, wervtLoginUrl, wervtLogoutUrl };
@@ -1,3 +1,4 @@
1
+ import { Locale } from "../shared/i18n.js";
1
2
  import { InjectionKey, PropType } from "vue";
2
3
  /** The template's subject, handed to renderEmail() while rendering. */
3
4
  export declare const SUBJECT_KEY: InjectionKey<(subject: string) => void>;
@@ -84,6 +85,14 @@ export declare const WervtEmail: import("vue").DefineComponent<import("vue").Ext
84
85
  type: PropType<EmailBrand | null>;
85
86
  default: null;
86
87
  };
88
+ /**
89
+ * The language of the frame's own texts ("Sent by …") and the document. English unless set;
90
+ * the template's own texts are up to the template.
91
+ */
92
+ lang: {
93
+ type: PropType<Locale>;
94
+ default: string;
95
+ };
87
96
  }>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
88
97
  [key: string]: any;
89
98
  }>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
@@ -117,12 +126,21 @@ export declare const WervtEmail: import("vue").DefineComponent<import("vue").Ext
117
126
  type: PropType<EmailBrand | null>;
118
127
  default: null;
119
128
  };
129
+ /**
130
+ * The language of the frame's own texts ("Sent by …") and the document. English unless set;
131
+ * the template's own texts are up to the template.
132
+ */
133
+ lang: {
134
+ type: PropType<Locale>;
135
+ default: string;
136
+ };
120
137
  }>> & Readonly<{}>, {
121
138
  app: string;
122
139
  preview: string;
123
140
  footer: string;
124
141
  reason: string;
125
142
  brand: EmailBrand | null;
143
+ lang: "de" | "en";
126
144
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
127
145
  export declare const EmailHeading: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
128
146
  /** A short label above the heading, in the landing page's mono eyebrow style. */
@@ -1,3 +1,5 @@
1
+ import { isLocale, translator } from "../shared/i18n.js";
2
+ import { mailMessages } from "../shared/messages.js";
1
3
  import { wervtInstance } from "../shared/instance.js";
2
4
  import { defineComponent, h, inject } from "vue";
3
5
  // src/runtime/email/components.ts
@@ -102,11 +104,19 @@ const WervtEmail = defineComponent({
102
104
  brand: {
103
105
  type: Object,
104
106
  default: null
107
+ },
108
+ /**
109
+ * The language of the frame's own texts ("Sent by …") and the document. English unless set;
110
+ * the template's own texts are up to the template.
111
+ */
112
+ lang: {
113
+ type: String,
114
+ default: "en"
105
115
  }
106
116
  },
107
117
  setup(props, { slots }) {
108
118
  inject(SUBJECT_KEY, void 0)?.(props.subject);
109
- return () => h("html", { lang: "en" }, [h("head", [
119
+ return () => h("html", { lang: isLocale(props.lang) ? props.lang : "en" }, [h("head", [
110
120
  h("meta", { charset: "utf-8" }),
111
121
  h("meta", {
112
122
  name: "viewport",
@@ -304,7 +314,8 @@ function accentLine(color) {
304
314
  }
305
315
  function footer(props) {
306
316
  const { brand } = props;
307
- const sentBy = props.footer || (brand ? `Sent by ${brand.name}` : props.app === "wervt" ? "Sent by wervt" : `Sent by ${props.app} via wervt`);
317
+ const say = translator(mailMessages, isLocale(props.lang) ? props.lang : "en");
318
+ const sentBy = props.footer || (brand ? say("sentBy", { app: brand.name }) : props.app === "wervt" ? say("sentBy", { app: "wervt" }) : say("sentByVia", { app: props.app }));
308
319
  const tile = brand ? mailIcon(brand) : `${t.assets}/mark-tile.png`;
309
320
  const small = {
310
321
  fontFamily: t.font,
@@ -367,7 +378,7 @@ function footer(props) {
367
378
  href: instance.site,
368
379
  class: "wv-muted",
369
380
  style: link
370
- }, "Secured by wervt")] : [h("a", {
381
+ }, say("securedBy"))] : [h("a", {
371
382
  href: instance.site,
372
383
  class: "wv-muted",
373
384
  style: link
@@ -375,7 +386,7 @@ function footer(props) {
375
386
  href: `${instance.docs}/getting-started/introduction`,
376
387
  class: "wv-muted",
377
388
  style: link
378
- }, "Docs")] : []])
389
+ }, say("docs"))] : []])
379
390
  ])])]);
380
391
  }
381
392
  function separator() {
@@ -1,3 +1,5 @@
1
+ import { Locale } from "../shared/i18n.js";
2
+ import { PropType } from "vue";
1
3
  /**
2
4
  * An invitation to an app: who invited, where to go, and that signing in with this address is
3
5
  * all it takes. Send it with `sendMail({ to, template: InviteEmail, props: { by, app, url } })`.
@@ -33,6 +35,11 @@ export declare const InviteEmail: import("vue").DefineComponent<import("vue").Ex
33
35
  type: StringConstructor;
34
36
  default: string;
35
37
  };
38
+ /** The language of the invitation's own texts; `message`, `action` and `note` stay as given. */
39
+ lang: {
40
+ type: PropType<Locale>;
41
+ default: string;
42
+ };
36
43
  }>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
37
44
  [key: string]: any;
38
45
  }>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
@@ -66,8 +73,14 @@ export declare const InviteEmail: import("vue").DefineComponent<import("vue").Ex
66
73
  type: StringConstructor;
67
74
  default: string;
68
75
  };
76
+ /** The language of the invitation's own texts; `message`, `action` and `note` stay as given. */
77
+ lang: {
78
+ type: PropType<Locale>;
79
+ default: string;
80
+ };
69
81
  }>> & Readonly<{}>, {
70
82
  message: string;
71
83
  action: string;
72
84
  note: string;
85
+ lang: "de" | "en";
73
86
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
@@ -1,3 +1,5 @@
1
+ import { isLocale, translator } from "../shared/i18n.js";
2
+ import { mailMessages } from "../shared/messages.js";
1
3
  import { EmailButton, EmailHeading, EmailMuted, EmailText, WervtEmail } from "./components.js";
2
4
  import { defineComponent, h } from "vue";
3
5
  // src/runtime/email/invite.ts
@@ -37,23 +39,37 @@ const InviteEmail = defineComponent({
37
39
  note: {
38
40
  type: String,
39
41
  default: ""
42
+ },
43
+ /** The language of the invitation's own texts; `message`, `action` and `note` stay as given. */
44
+ lang: {
45
+ type: String,
46
+ default: "en"
40
47
  }
41
48
  },
42
49
  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
- ]);
50
+ return () => {
51
+ const lang = isLocale(props.lang) ? props.lang : "en";
52
+ const t = translator(mailMessages, lang);
53
+ const who = {
54
+ by: props.by,
55
+ app: props.app
56
+ };
57
+ return h(WervtEmail, {
58
+ subject: t("inviteSubject", who),
59
+ app: props.app,
60
+ preview: props.message || t("invitePreview", who),
61
+ reason: t("inviteReason", who),
62
+ lang
63
+ }, () => [
64
+ h(EmailHeading, { eyebrow: t("inviteEyebrow") }, () => t("inviteHeading", who)),
65
+ h(EmailText, () => [t("inviteText", who), props.message].filter(Boolean).join(" ")),
66
+ h(EmailButton, {
67
+ href: props.url,
68
+ color: "neutral"
69
+ }, () => props.action || t("inviteAction", who)),
70
+ h(EmailMuted, () => props.note || t("inviteNote"))
71
+ ]);
72
+ };
57
73
  }
58
74
  });
59
75
  export { InviteEmail };
@@ -0,0 +1,3 @@
1
+ import { Catalog, DEFAULT_LOCALE, LOCALES, Locale, Messages, Translate, catalogProblems, defineMessages, isLocale, negotiateLocale, translator } from "./shared/i18n.js";
2
+ import { mailMessages } from "./shared/messages.js";
3
+ export { Catalog, DEFAULT_LOCALE, LOCALES, Locale, Messages, Translate, catalogProblems, defineMessages, isLocale, mailMessages, negotiateLocale, translator };
@@ -0,0 +1,3 @@
1
+ import { DEFAULT_LOCALE, LOCALES, catalogProblems, defineMessages, isLocale, negotiateLocale, translator } from "./shared/i18n.js";
2
+ import { mailMessages } from "./shared/messages.js";
3
+ export { DEFAULT_LOCALE, LOCALES, catalogProblems, defineMessages, isLocale, mailMessages, negotiateLocale, translator };
@@ -1,25 +1,5 @@
1
+ import { WervtAccess, WervtAccess as WervtAccess$1, WervtInvite } from "@wervt/core";
1
2
  import { RequestEvent } from "nuxt/server";
2
- export interface WervtInvite {
3
- email: string;
4
- /** Who invited them. */
5
- by: string;
6
- /** ISO time. */
7
- at: string;
8
- }
9
- export interface WervtAccess {
10
- /** Lets `email` into the app. `invited` is false when they could already get in. */
11
- invite(email: string): Promise<{
12
- email: string;
13
- invited: boolean;
14
- }>;
15
- /** Takes back an invite (not the owner's allow list). */
16
- revoke(email: string): Promise<{
17
- email: string;
18
- revoked: boolean;
19
- }>;
20
- /** Everyone the app invited, with who invited them and when. */
21
- list(): Promise<WervtInvite[]>;
22
- }
23
3
  /**
24
4
  * Invites on an invite-only app (`wervt access <app> --invite`), as the user of this request: the
25
5
  * runtime vouches for them, and only someone with access can invite. Everyone with access may
@@ -30,4 +10,5 @@ export interface WervtAccess {
30
10
  * 409 when the app isn't invite-only, 429 past the limits (25 invites per person and day, 500 per
31
11
  * app).
32
12
  */
33
- export declare function useWervtAccess(event: RequestEvent): WervtAccess;
13
+ export declare function useWervtAccess(event: RequestEvent): WervtAccess$1;
14
+ export type { WervtAccess, WervtInvite };
@@ -1,4 +1,6 @@
1
1
  import { DEV_LOGIN_COOKIE, devUser } from "../shared/user.js";
2
+ import { withH3Errors } from "./errors.js";
3
+ import { appAccess, runtimeUrl } from "@wervt/core";
2
4
  import { createError, getCookie } from "nuxt/server";
3
5
  // src/runtime/server/access.ts
4
6
  const devInvites = [];
@@ -13,31 +15,12 @@ const devInvites = [];
13
15
  * app).
14
16
  */
15
17
  function useWervtAccess(event) {
16
- const runtime = process.env.WERVT_RUNTIME_URL;
17
- if (!runtime) return devAccess(event);
18
- async function call(method, body) {
19
- const res = await fetch(new URL("/_wervt/access", runtime), {
20
- method,
21
- headers: {
22
- "content-type": "application/json",
23
- "x-wervt-app": process.env.WERVT_APP ?? "",
24
- "x-wervt-token": process.env.WERVT_TOKEN ?? "",
25
- "x-wervt-user": event.req.headers.get("x-wervt-user") ?? "",
26
- "x-wervt-user-proof": event.req.headers.get("x-wervt-user-proof") ?? ""
27
- },
28
- body: body && JSON.stringify(body)
29
- });
30
- const data = await res.json().catch(() => ({}));
31
- if (!res.ok) throw createError({
32
- status: res.status,
33
- message: data.error ?? `access ${res.status}`
34
- });
35
- return data;
36
- }
18
+ if (!runtimeUrl()) return devAccess(event);
19
+ const access = appAccess(event.req);
37
20
  return {
38
- invite: (email) => call("POST", { email }),
39
- revoke: (email) => call("DELETE", { email }),
40
- list: async () => (await call("GET")).invited
21
+ invite: (email) => withH3Errors(access.invite(email)),
22
+ revoke: (email) => withH3Errors(access.revoke(email)),
23
+ list: () => withH3Errors(access.list())
41
24
  };
42
25
  }
43
26
  function devAccess(event) {
@@ -1,11 +1,6 @@
1
+ import { AnthropicOptions } from "@wervt/core/anthropic";
1
2
  import Anthropic from "@anthropic-ai/sdk";
2
- export interface UseAnthropicOptions {
3
- /**
4
- * The key for `nuxt dev`, where there is no workload identity. Default: `ANTHROPIC_API_KEY`.
5
- * Never used on wervt.
6
- */
7
- devApiKey?: string;
8
- }
3
+ export type UseAnthropicOptions = AnthropicOptions;
9
4
  /**
10
5
  * An Anthropic client that authenticates as this app through its wervt identity: no API key, only
11
6
  * IDs from the Anthropic Console in the app's env (`ANTHROPIC_FEDERATION_RULE_ID`,
@@ -1,10 +1,6 @@
1
- import { useWorkloadIdentity } from "./identity.js";
2
- import { createError } from "nuxt/server";
3
- import Anthropic from "@anthropic-ai/sdk";
4
- import { oidcFederationProvider } from "@anthropic-ai/sdk/lib/credentials/oidc-federation";
1
+ import { toH3Error } from "./errors.js";
2
+ import { getAnthropic } from "@wervt/core/anthropic";
5
3
  // src/runtime/server/anthropic.ts
6
- const BASE_URL = "https://api.anthropic.com";
7
- let client;
8
4
  /**
9
5
  * An Anthropic client that authenticates as this app through its wervt identity: no API key, only
10
6
  * IDs from the Anthropic Console in the app's env (`ANTHROPIC_FEDERATION_RULE_ID`,
@@ -16,37 +12,10 @@ let client;
16
12
  * (`wervt identity status <app>`). In `nuxt dev` it uses `options.devApiKey` instead.
17
13
  */
18
14
  function useAnthropic(options = {}) {
19
- return client ??= create(options);
20
- }
21
- function create(options) {
22
- if (!process.env.WERVT_RUNTIME_URL) {
23
- const apiKey = options.devApiKey ?? process.env.ANTHROPIC_API_KEY;
24
- if (!apiKey) throw createError({
25
- status: 503,
26
- message: "useAnthropic() in `nuxt dev` needs an API key (ANTHROPIC_API_KEY or devApiKey)."
27
- });
28
- return new Anthropic({ apiKey });
15
+ try {
16
+ return getAnthropic(options);
17
+ } catch (e) {
18
+ throw toH3Error(e);
29
19
  }
30
- const federationRuleId = process.env.ANTHROPIC_FEDERATION_RULE_ID;
31
- const organizationId = process.env.ANTHROPIC_ORGANIZATION_ID;
32
- if (!federationRuleId || !organizationId) throw createError({
33
- status: 503,
34
- message: "useAnthropic() needs ANTHROPIC_FEDERATION_RULE_ID and ANTHROPIC_ORGANIZATION_ID (wervt env <app> --set …)."
35
- });
36
- const identity = useWorkloadIdentity();
37
- return new Anthropic({
38
- apiKey: null,
39
- authToken: null,
40
- baseURL: BASE_URL,
41
- credentials: oidcFederationProvider({
42
- identityTokenProvider: () => identity.token(BASE_URL),
43
- federationRuleId,
44
- organizationId,
45
- serviceAccountId: process.env.ANTHROPIC_SERVICE_ACCOUNT_ID || void 0,
46
- workspaceId: process.env.ANTHROPIC_WORKSPACE_ID || void 0,
47
- baseURL: BASE_URL,
48
- fetch: (...args) => fetch(...args)
49
- })
50
- });
51
20
  }
52
21
  export { useAnthropic };
@@ -1,17 +1,2 @@
1
- import { PostgresJsDatabase } from "drizzle-orm/postgres-js";
2
- import { AnyRelations, EmptyRelations } from "drizzle-orm";
3
- /**
4
- * Drizzle client for the app's database. In production the wervt runtime injects
5
- * DATABASE_URL for the app's own role, whose search_path is its own schema, so
6
- * tables are declared without a schema.
7
- *
8
- * Pass the app's relations (from `defineRelations`) to use `db.query.*`. All clients
9
- * share one connection pool.
10
- *
11
- * @example
12
- * const db = useDb({ relations })
13
- * const room = await db.query.rooms.findFirst({ where: { id }, with: { players: true } })
14
- */
15
- export declare function useDb<TRelations extends AnyRelations = EmptyRelations>(options?: {
16
- relations?: TRelations;
17
- }): PostgresJsDatabase<TRelations>;
1
+ import { useDb } from "@wervt/core/db";
2
+ export { useDb };
@@ -1,35 +1 @@
1
- import { drizzle } from "drizzle-orm/postgres-js";
2
- import postgres from "postgres";
3
- // src/runtime/server/db.ts
4
- let client;
5
- const clients = /* @__PURE__ */ new Map();
6
- /**
7
- * Drizzle client for the app's database. In production the wervt runtime injects
8
- * DATABASE_URL for the app's own role, whose search_path is its own schema, so
9
- * tables are declared without a schema.
10
- *
11
- * Pass the app's relations (from `defineRelations`) to use `db.query.*`. All clients
12
- * share one connection pool.
13
- *
14
- * @example
15
- * const db = useDb({ relations })
16
- * const room = await db.query.rooms.findFirst({ where: { id }, with: { players: true } })
17
- */
18
- function useDb(options) {
19
- if (!process.env.DATABASE_URL) throw new Error("DATABASE_URL is not set");
20
- client ??= postgres(process.env.DATABASE_URL, {
21
- max: 2,
22
- idle_timeout: 20
23
- });
24
- const relations = options?.relations;
25
- let db = clients.get(relations);
26
- if (!db) {
27
- db = drizzle({
28
- client,
29
- relations
30
- });
31
- clients.set(relations, db);
32
- }
33
- return db;
34
- }
35
- export { useDb };
1
+ export { useDb } from "@wervt/core/db";