@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,5 +1,4 @@
1
1
  import { Component } from "vue";
2
- //#region src/runtime/email/render.d.ts
3
2
  export interface RenderedEmail {
4
3
  subject: string;
5
4
  html: string;
@@ -13,5 +12,4 @@ export type EmailProps<T> = T extends (new () => {
13
12
  * Renders an email template (a component with `<WervtEmail>` at its root) to HTML, a plain-text
14
13
  * part and its subject.
15
14
  */
16
- export declare function renderEmail<T extends Component>(template: T, props?: EmailProps<T>): Promise<RenderedEmail>;
17
- //#endregion
15
+ export declare function renderEmail<T extends Component>(template: T, props?: EmailProps<T>): Promise<RenderedEmail>;
@@ -2,7 +2,7 @@ import { SUBJECT_KEY } from "./components.js";
2
2
  import { createSSRApp } from "vue";
3
3
  import { convert } from "html-to-text";
4
4
  import { renderToString } from "vue/server-renderer";
5
- //#region src/runtime/email/render.ts
5
+ // src/runtime/email/render.ts
6
6
  const DOCTYPE = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">";
7
7
  /**
8
8
  * Renders an email template (a component with `<WervtEmail>` at its root) to HTML, a plain-text
@@ -64,5 +64,4 @@ function toText(html) {
64
64
  ]
65
65
  }).trim();
66
66
  }
67
- //#endregion
68
67
  export { renderEmail };
@@ -1,5 +1,4 @@
1
1
  import { RequestEvent } from "nuxt/server";
2
- //#region src/runtime/server/access.d.ts
3
2
  export interface WervtInvite {
4
3
  email: string;
5
4
  /** Who invited them. */
@@ -24,11 +23,11 @@ export interface WervtAccess {
24
23
  /**
25
24
  * Invites on an invite-only app (`wervt access <app> --invite`), as the user of this request: the
26
25
  * runtime vouches for them, and only someone with access can invite. Everyone with access may
27
- * invite; restrict it in the route if the app has its own roles.
26
+ * invite, or only the owner, members and admins with `wervt access <app> --invite-by owners`;
27
+ * restrict it further in the route if the app has its own roles.
28
28
  *
29
- * Throws with the platform's status: 401 without a signed-in user, 403 when they have no access,
29
+ * Throws with the platform's status: 401 without a signed-in user, 403 when they may not invite,
30
30
  * 409 when the app isn't invite-only, 429 past the limits (25 invites per person and day, 500 per
31
31
  * app).
32
32
  */
33
- export declare function useWervtAccess(event: RequestEvent): WervtAccess;
34
- //#endregion
33
+ export declare function useWervtAccess(event: RequestEvent): WervtAccess;
@@ -1,13 +1,14 @@
1
1
  import { DEV_LOGIN_COOKIE, devUser } from "../shared/user.js";
2
2
  import { createError, getCookie } from "nuxt/server";
3
- //#region src/runtime/server/access.ts
3
+ // src/runtime/server/access.ts
4
4
  const devInvites = [];
5
5
  /**
6
6
  * Invites on an invite-only app (`wervt access <app> --invite`), as the user of this request: the
7
7
  * runtime vouches for them, and only someone with access can invite. Everyone with access may
8
- * invite; restrict it in the route if the app has its own roles.
8
+ * invite, or only the owner, members and admins with `wervt access <app> --invite-by owners`;
9
+ * restrict it further in the route if the app has its own roles.
9
10
  *
10
- * Throws with the platform's status: 401 without a signed-in user, 403 when they have no access,
11
+ * Throws with the platform's status: 401 without a signed-in user, 403 when they may not invite,
11
12
  * 409 when the app isn't invite-only, 429 past the limits (25 invites per person and day, 500 per
12
13
  * app).
13
14
  */
@@ -83,5 +84,4 @@ function devAccess(event) {
83
84
  }
84
85
  };
85
86
  }
86
- //#endregion
87
87
  export { useWervtAccess };
@@ -0,0 +1,11 @@
1
+ interface EnrichContext {
2
+ event: Record<string, unknown>;
3
+ headers?: Record<string, string>;
4
+ }
5
+ interface NitroAppHooks {
6
+ hooks: {
7
+ hook(name: 'evlog:enrich', handler: (ctx: EnrichContext) => void): unknown;
8
+ };
9
+ }
10
+ export default function wervtAgentToken(nitroApp: NitroAppHooks): void;
11
+ export {};
@@ -0,0 +1,9 @@
1
+ import { parseAgentToken } from "../shared/agent-token.js";
2
+ // src/runtime/server/agent-token-plugin.ts
3
+ function wervtAgentToken(nitroApp) {
4
+ nitroApp.hooks.hook("evlog:enrich", (ctx) => {
5
+ const agentToken = parseAgentToken(ctx.headers?.["x-wervt-agent-token"]);
6
+ if (agentToken) ctx.event.agentToken = agentToken;
7
+ });
8
+ }
9
+ export { wervtAgentToken as default };
@@ -0,0 +1,19 @@
1
+ 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
+ }
9
+ /**
10
+ * An Anthropic client that authenticates as this app through its wervt identity: no API key, only
11
+ * IDs from the Anthropic Console in the app's env (`ANTHROPIC_FEDERATION_RULE_ID`,
12
+ * `ANTHROPIC_ORGANIZATION_ID`, optionally `ANTHROPIC_SERVICE_ACCOUNT_ID` and
13
+ * `ANTHROPIC_WORKSPACE_ID`). The SDK trades an identity token for an Anthropic token and renews it
14
+ * before it runs out.
15
+ *
16
+ * Needs `wervt identity enable <app>` and a federation rule for the app's subject
17
+ * (`wervt identity status <app>`). In `nuxt dev` it uses `options.devApiKey` instead.
18
+ */
19
+ export declare function useAnthropic(options?: UseAnthropicOptions): Anthropic;
@@ -0,0 +1,52 @@
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";
5
+ // src/runtime/server/anthropic.ts
6
+ const BASE_URL = "https://api.anthropic.com";
7
+ let client;
8
+ /**
9
+ * An Anthropic client that authenticates as this app through its wervt identity: no API key, only
10
+ * IDs from the Anthropic Console in the app's env (`ANTHROPIC_FEDERATION_RULE_ID`,
11
+ * `ANTHROPIC_ORGANIZATION_ID`, optionally `ANTHROPIC_SERVICE_ACCOUNT_ID` and
12
+ * `ANTHROPIC_WORKSPACE_ID`). The SDK trades an identity token for an Anthropic token and renews it
13
+ * before it runs out.
14
+ *
15
+ * Needs `wervt identity enable <app>` and a federation rule for the app's subject
16
+ * (`wervt identity status <app>`). In `nuxt dev` it uses `options.devApiKey` instead.
17
+ */
18
+ 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 });
29
+ }
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
+ }
52
+ export { useAnthropic };
@@ -1,4 +1,3 @@
1
- //#region src/runtime/server/boot.d.ts
2
1
  declare const _default: import("nuxt/server").EventHandler<{
3
2
  error: string;
4
3
  ok?: undefined;
@@ -6,5 +5,4 @@ declare const _default: import("nuxt/server").EventHandler<{
6
5
  error?: undefined;
7
6
  ok: boolean;
8
7
  }>;
9
- //#endregion
10
8
  export { _default as default };
@@ -1,5 +1,5 @@
1
1
  import { defineEventHandler } from "nuxt/server";
2
- //#region src/runtime/server/boot.ts
2
+ // src/runtime/server/boot.ts
3
3
  var boot_default = defineEventHandler((event) => {
4
4
  event.res.headers.set("cache-control", "no-store");
5
5
  if (event.req.headers.get("x-wervt-task") !== "1") {
@@ -8,5 +8,4 @@ var boot_default = defineEventHandler((event) => {
8
8
  }
9
9
  return { ok: true };
10
10
  });
11
- //#endregion
12
11
  export { boot_default as default };
@@ -1,6 +1,5 @@
1
1
  import { PostgresJsDatabase } from "drizzle-orm/postgres-js";
2
2
  import { AnyRelations, EmptyRelations } from "drizzle-orm";
3
- //#region src/runtime/server/db.d.ts
4
3
  /**
5
4
  * Drizzle client for the app's database. In production the wervt runtime injects
6
5
  * DATABASE_URL for the app's own role, whose search_path is its own schema, so
@@ -15,5 +14,4 @@ import { AnyRelations, EmptyRelations } from "drizzle-orm";
15
14
  */
16
15
  export declare function useDb<TRelations extends AnyRelations = EmptyRelations>(options?: {
17
16
  relations?: TRelations;
18
- }): PostgresJsDatabase<TRelations>;
19
- //#endregion
17
+ }): PostgresJsDatabase<TRelations>;
@@ -1,6 +1,6 @@
1
1
  import { drizzle } from "drizzle-orm/postgres-js";
2
2
  import postgres from "postgres";
3
- //#region src/runtime/server/db.ts
3
+ // src/runtime/server/db.ts
4
4
  let client;
5
5
  const clients = /* @__PURE__ */ new Map();
6
6
  /**
@@ -32,5 +32,4 @@ function useDb(options) {
32
32
  }
33
33
  return db;
34
34
  }
35
- //#endregion
36
35
  export { useDb };
@@ -1,4 +1,2 @@
1
- //#region src/runtime/server/dev-auth.d.ts
2
1
  declare const _default: import("nuxt/server").EventHandler<string>;
3
- //#endregion
4
2
  export { _default as default };
@@ -1,6 +1,6 @@
1
1
  import { DEV_LOGIN_COOKIE } from "../shared/user.js";
2
2
  import { defineEventHandler, deleteCookie, getQuery, sendRedirect, setCookie } from "nuxt/server";
3
- //#region src/runtime/server/dev-auth.ts
3
+ // src/runtime/server/dev-auth.ts
4
4
  var dev_auth_default = defineEventHandler((event) => {
5
5
  const { redirect } = getQuery(event);
6
6
  const target = typeof redirect === "string" && redirect.startsWith("/") ? redirect : "/";
@@ -11,5 +11,4 @@ var dev_auth_default = defineEventHandler((event) => {
11
11
  else deleteCookie(event, DEV_LOGIN_COOKIE, { path: "/" });
12
12
  return sendRedirect(event, target);
13
13
  });
14
- //#endregion
15
14
  export { dev_auth_default as default };
@@ -1,4 +1,2 @@
1
- //#region src/runtime/server/email-preview.d.ts
2
1
  declare const _default: import("nuxt/server").EventHandler<Promise<Response>>;
3
- //#endregion
4
2
  export { _default as default };
@@ -1,7 +1,7 @@
1
1
  import { renderEmail } from "../email/render.js";
2
2
  import { createError, defineEventHandler, getQuery } from "nuxt/server";
3
3
  import { templates } from "#wervt/emails";
4
- //#region src/runtime/server/email-preview.ts
4
+ // src/runtime/server/email-preview.ts
5
5
  const list = templates;
6
6
  const page = (body) => new Response(`<!doctype html><meta charset="utf-8"><title>Emails</title><body style="font:15px/1.6 system-ui;margin:40px">${body}</body>`, { headers: { "content-type": "text/html; charset=utf-8" } });
7
7
  const escape = (value) => value.replace(/[&<>"]/g, (c) => ({
@@ -26,5 +26,4 @@ var email_preview_default = defineEventHandler(async (event) => {
26
26
  if ("text" in getQuery(event)) return new Response(`Subject: ${mail.subject}\n\n${mail.text}\n`, { headers: { "content-type": "text/plain; charset=utf-8" } });
27
27
  return new Response(mail.html, { headers: { "content-type": "text/html; charset=utf-8" } });
28
28
  });
29
- //#endregion
30
29
  export { email_preview_default as default };
@@ -0,0 +1,18 @@
1
+ export interface WorkloadIdentity {
2
+ /**
3
+ * A fresh JWT that says "I am this wervt app", for `audience` (a provider on wervt's list, e.g.
4
+ * `https://api.anthropic.com`). Valid for 5 minutes and single-use: trade it for a provider
5
+ * token right away. Each call signs a new one; providers' SDKs cache what they get for it.
6
+ */
7
+ token(audience: string): Promise<string>;
8
+ }
9
+ /**
10
+ * The app's workload identity: tokens signed by wervt's issuer, so the app can authenticate to an
11
+ * AI provider (Anthropic's Workload Identity Federation) without an API key. Its owner turns it
12
+ * on with `wervt identity enable <app>`; `useAnthropic()` (`@wervt/nuxt/anthropic`) uses it.
13
+ *
14
+ * Throws with the platform's status: 403 when the app has no identity, 400 for an audience wervt
15
+ * doesn't sign for, 429 past 10 tokens a minute, 503 when the issuer is off. In `nuxt dev` there
16
+ * is no issuer, so it always throws.
17
+ */
18
+ export declare function useWorkloadIdentity(): WorkloadIdentity;
@@ -0,0 +1,36 @@
1
+ import { createError } from "nuxt/server";
2
+ // src/runtime/server/identity.ts
3
+ /**
4
+ * The app's workload identity: tokens signed by wervt's issuer, so the app can authenticate to an
5
+ * AI provider (Anthropic's Workload Identity Federation) without an API key. Its owner turns it
6
+ * on with `wervt identity enable <app>`; `useAnthropic()` (`@wervt/nuxt/anthropic`) uses it.
7
+ *
8
+ * Throws with the platform's status: 403 when the app has no identity, 400 for an audience wervt
9
+ * doesn't sign for, 429 past 10 tokens a minute, 503 when the issuer is off. In `nuxt dev` there
10
+ * is no issuer, so it always throws.
11
+ */
12
+ function useWorkloadIdentity() {
13
+ return { async token(audience) {
14
+ const runtime = process.env.WERVT_RUNTIME_URL;
15
+ if (!runtime) throw createError({
16
+ status: 503,
17
+ message: "Workload identity only exists on wervt, not in `nuxt dev`."
18
+ });
19
+ const res = await fetch(new URL("/_wervt/identity-token", runtime), {
20
+ method: "POST",
21
+ headers: {
22
+ "content-type": "application/json",
23
+ "x-wervt-app": process.env.WERVT_APP ?? "",
24
+ "x-wervt-token": process.env.WERVT_TOKEN ?? ""
25
+ },
26
+ body: JSON.stringify({ audience })
27
+ });
28
+ const body = await res.json().catch(() => ({}));
29
+ if (!res.ok || !body.token) throw createError({
30
+ status: res.status,
31
+ message: body.error ?? `identity token failed (${res.status})`
32
+ });
33
+ return body.token;
34
+ } };
35
+ }
36
+ export { useWorkloadIdentity };
@@ -1,7 +1,8 @@
1
- import { WervtUser } from "../shared/user.js";
1
+ import { WervtRole, WervtUser } from "../shared/user.js";
2
2
  import { EmailProps, RenderedEmail, renderEmail } from "../email/render.js";
3
3
  import { WervtAccess, WervtInvite, useWervtAccess } from "./access.js";
4
4
  import { useDb } from "./db.js";
5
+ import { WorkloadIdentity, useWorkloadIdentity } from "./identity.js";
5
6
  import { createEvlogError, log, useLogger } from "./logger.js";
6
7
  import { SendMailOptions, sendMail } from "./mail.js";
7
8
  import { McpTokenContext } from "./mcp-auth.js";
@@ -11,5 +12,5 @@ import { ShapeOptions, ShapeRow, proxyShape } from "./shape.js";
11
12
  import { Task, TaskEvent, TaskResult, defineTask } from "./tasks.js";
12
13
  import { SafeResult, useSafeValidatedBody, useSafeValidatedParams, useSafeValidatedQuery, useValidatedBody, useValidatedParams, useValidatedQuery, vh } from "./validate.js";
13
14
  import { Tool, ToolRoute, defineTool, defineToolRoute } from "./tools.js";
14
- import { useWervtUser } from "./user.js";
15
- export { type EmailProps, type McpTokenContext, type RenderedEmail, type SafeResult, type SendMailOptions, type ShapeOptions, type ShapeResponse, type ShapeRow, type Task, type TaskEvent, type TaskResult, type Tool, type ToolRoute, type WervtAccess, type WervtInvite, type WervtUser, createEvlogError, defineTask, defineTool, defineToolRoute, log, proxyShape, renderEmail, seedTable, sendMail, useDb, useLogger, useSafeValidatedBody, useSafeValidatedParams, useSafeValidatedQuery, useValidatedBody, useValidatedParams, useValidatedQuery, useWervtAccess, useWervtUser, vh };
15
+ import { requireWervtRole, useWervtUser } from "./user.js";
16
+ export { type EmailProps, type McpTokenContext, type RenderedEmail, type SafeResult, type SendMailOptions, type ShapeOptions, type ShapeResponse, type ShapeRow, type Task, type TaskEvent, type TaskResult, type Tool, type ToolRoute, type WervtAccess, type WervtInvite, type WervtRole, type WervtUser, type WorkloadIdentity, createEvlogError, defineTask, defineTool, defineToolRoute, log, proxyShape, renderEmail, requireWervtRole, seedTable, sendMail, useDb, useLogger, useSafeValidatedBody, useSafeValidatedParams, useSafeValidatedQuery, useValidatedBody, useValidatedParams, useValidatedQuery, useWervtAccess, useWervtUser, useWorkloadIdentity, vh };
@@ -1,6 +1,7 @@
1
+ import { renderEmail } from "../email/render.js";
1
2
  import { useWervtAccess } from "./access.js";
3
+ import { useWorkloadIdentity } from "./identity.js";
2
4
  import { useDb } from "./db.js";
3
- import { renderEmail } from "../email/render.js";
4
5
  import { createEvlogError, log, useLogger } from "./logger.js";
5
6
  import { sendMail } from "./mail.js";
6
7
  import { seedTable } from "./seed.js";
@@ -8,5 +9,5 @@ import { proxyShape } from "./shape.js";
8
9
  import { defineTask } from "./tasks.js";
9
10
  import { useSafeValidatedBody, useSafeValidatedParams, useSafeValidatedQuery, useValidatedBody, useValidatedParams, useValidatedQuery, vh } from "./validate.js";
10
11
  import { defineTool, defineToolRoute } from "./tools.js";
11
- import { useWervtUser } from "./user.js";
12
- export { createEvlogError, defineTask, defineTool, defineToolRoute, log, proxyShape, renderEmail, seedTable, sendMail, useDb, useLogger, useSafeValidatedBody, useSafeValidatedParams, useSafeValidatedQuery, useValidatedBody, useValidatedParams, useValidatedQuery, useWervtAccess, useWervtUser, vh };
12
+ import { requireWervtRole, useWervtUser } from "./user.js";
13
+ export { createEvlogError, defineTask, defineTool, defineToolRoute, log, proxyShape, renderEmail, requireWervtRole, seedTable, sendMail, useDb, useLogger, useSafeValidatedBody, useSafeValidatedParams, useSafeValidatedQuery, useValidatedBody, useValidatedParams, useValidatedQuery, useWervtAccess, useWervtUser, useWorkloadIdentity, vh };
@@ -1,7 +1,5 @@
1
1
  import { RequestEvent } from "nuxt/server";
2
2
  import { createEvlogError, log } from "evlog";
3
- //#region src/runtime/server/logger.d.ts
4
3
  /** The request's wide event: `useLogger(event).set({ user: { id } })` adds fields to it. */
5
4
  export declare function useLogger<T extends object = Record<string, unknown>>(event: RequestEvent, service?: string): import("evlog").AuditableLogger<T>;
6
- //#endregion
7
5
  export { createEvlogError, log };
@@ -1,8 +1,7 @@
1
1
  import { createEvlogError, log, useLogger as useLogger$1 } from "evlog";
2
- //#region src/runtime/server/logger.ts
2
+ // src/runtime/server/logger.ts
3
3
  /** The request's wide event: `useLogger(event).set({ user: { id } })` adds fields to it. */
4
4
  function useLogger(event, service) {
5
5
  return useLogger$1(event, service);
6
6
  }
7
- //#endregion
8
7
  export { createEvlogError, log, useLogger };
@@ -1,6 +1,5 @@
1
1
  import { EmailProps } from "../email/render.js";
2
2
  import { Component } from "vue";
3
- //#region src/runtime/server/mail.d.ts
4
3
  export interface SendMailOptions<T extends Component> {
5
4
  /** One recipient. */
6
5
  to: string;
@@ -19,5 +18,4 @@ export interface SendMailOptions<T extends Component> {
19
18
  */
20
19
  export declare function sendMail<T extends Component>(options: SendMailOptions<T>): Promise<{
21
20
  id: string;
22
- }>;
23
- //#endregion
21
+ }>;
@@ -1,6 +1,6 @@
1
1
  import { renderEmail } from "../email/render.js";
2
2
  import { createError } from "nuxt/server";
3
- //#region src/runtime/server/mail.ts
3
+ // src/runtime/server/mail.ts
4
4
  /**
5
5
  * Renders a template and sends it through wervt. Throws on failure, with the status the platform
6
6
  * answered: 400 (invalid mail), 429 (the app's or the recipient's hourly limit), 502/503.
@@ -39,5 +39,4 @@ async function sendMail(options) {
39
39
  });
40
40
  return { id: body.id ?? "" };
41
41
  }
42
- //#endregion
43
42
  export { sendMail };
@@ -1,4 +1,2 @@
1
- //#region src/runtime/server/mcp-auth-server.d.ts
2
1
  declare const _default: import("nuxt/server").EventHandler<Promise<Response>>;
3
- //#endregion
4
2
  export { _default as default };
@@ -1,6 +1,6 @@
1
1
  import { CORS, fetchAuth, mcpEndpoint } from "./mcp-auth.js";
2
2
  import { defineEventHandler } from "nuxt/server";
3
- //#region src/runtime/server/mcp-auth-server.ts
3
+ // src/runtime/server/mcp-auth-server.ts
4
4
  var mcp_auth_server_default = defineEventHandler(async (event) => {
5
5
  const endpoint = mcpEndpoint(event);
6
6
  if (event.req.method === "OPTIONS") return new Response(null, {
@@ -17,5 +17,4 @@ var mcp_auth_server_default = defineEventHandler(async (event) => {
17
17
  });
18
18
  return Response.json(await res.json(), { headers: CORS });
19
19
  });
20
- //#endregion
21
20
  export { mcp_auth_server_default as default };
@@ -1,6 +1,5 @@
1
1
  import { WervtUser } from "../shared/user.js";
2
2
  import { RequestEvent } from "nuxt/server";
3
- //#region src/runtime/server/mcp-auth.d.ts
4
3
  /** What `wervt:mcp:token` handlers get: set `user` to accept the token. */
5
4
  export interface McpTokenContext {
6
5
  token: string;
@@ -57,5 +56,4 @@ export declare function verifyAccessToken(token: string, endpoint: McpEndpoint):
57
56
  /** The user a bearer token on `/mcp` stands for: a wervt access token, or the app's own. */
58
57
  export declare function authenticateMcp(token: string, event: RequestEvent, endpoint: McpEndpoint): Promise<WervtUser | undefined>;
59
58
  /** Makes `useWervtUser(event)` return the token's user while a tool runs over MCP. */
60
- export declare function setMcpUser(event: RequestEvent, user: WervtUser): void;
61
- //#endregion
59
+ export declare function setMcpUser(event: RequestEvent, user: WervtUser): void;
@@ -1,7 +1,7 @@
1
1
  import { userFromClaims } from "./mcp-claims.js";
2
2
  import { getRequestURL, useServerHooks } from "nuxt/server";
3
3
  import { createRemoteJWKSet, customFetch, errors, jwtVerify } from "jose";
4
- //#region src/runtime/server/mcp-auth.ts
4
+ // src/runtime/server/mcp-auth.ts
5
5
  const trimSlash = (url) => url.replace(/\/+$/, "");
6
6
  /**
7
7
  * Where this app's `/mcp` and wervt's login live, from the runtime's `WERVT_APP_URL` and
@@ -95,5 +95,4 @@ async function authenticateMcp(token, event, endpoint) {
95
95
  function setMcpUser(event, user) {
96
96
  event.context.wervtMcpUser = user;
97
97
  }
98
- //#endregion
99
98
  export { CORS, authenticateMcp, fetchAuth, mcpEndpoint, protectedResourceMetadata, setMcpUser, verifyAccessToken };
@@ -1,5 +1,3 @@
1
1
  import { WervtUser } from "../shared/user.js";
2
- //#region src/runtime/server/mcp-claims.d.ts
3
2
  /** The wervt user an access token's claims name, or undefined when they're incomplete. */
4
- export declare function userFromClaims(claims: Record<string, unknown>): WervtUser | undefined;
5
- //#endregion
3
+ export declare function userFromClaims(claims: Record<string, unknown>): WervtUser | undefined;
@@ -1,4 +1,4 @@
1
- //#region src/runtime/server/mcp-claims.ts
1
+ // src/runtime/server/mcp-claims.ts
2
2
  /** The wervt user an access token's claims name, or undefined when they're incomplete. */
3
3
  function userFromClaims(claims) {
4
4
  const { sub, email, name, picture, accounts } = claims;
@@ -14,5 +14,4 @@ function userFromClaims(claims) {
14
14
  function isAccounts(value) {
15
15
  return typeof value === "object" && value !== null && !Array.isArray(value) && Object.values(value).every((id) => typeof id === "string");
16
16
  }
17
- //#endregion
18
17
  export { userFromClaims };
@@ -1,4 +1,2 @@
1
- //#region src/runtime/server/mcp-metadata.d.ts
2
1
  declare const _default: import("nuxt/server").EventHandler<Response>;
3
- //#endregion
4
2
  export { _default as default };
@@ -1,6 +1,6 @@
1
1
  import { CORS, mcpEndpoint, protectedResourceMetadata } from "./mcp-auth.js";
2
2
  import { defineEventHandler, getRequestURL } from "nuxt/server";
3
- //#region src/runtime/server/mcp-metadata.ts
3
+ // src/runtime/server/mcp-metadata.ts
4
4
  var mcp_metadata_default = defineEventHandler((event) => {
5
5
  const endpoint = mcpEndpoint(event);
6
6
  if (event.req.method === "OPTIONS") return new Response(null, {
@@ -11,5 +11,4 @@ var mcp_metadata_default = defineEventHandler((event) => {
11
11
  const mcp = getRequestURL(event).pathname.endsWith("/mcp");
12
12
  return Response.json(protectedResourceMetadata(endpoint, mcp ? endpoint.resource : endpoint.app), { headers: CORS });
13
13
  });
14
- //#endregion
15
14
  export { mcp_metadata_default as default };
@@ -1,4 +1,2 @@
1
- //#region src/runtime/server/mcp.d.ts
2
1
  declare const _default: import("nuxt/server").EventHandler<Promise<unknown>>;
3
- //#endregion
4
2
  export { _default as default };
@@ -6,7 +6,7 @@ import { allTools, toolInputSchema } from "./tool-handlers.js";
6
6
  import { defineEventHandler } from "nuxt/server";
7
7
  import { server } from "#wervt/mcp";
8
8
  import { defineMcpHandler } from "h3-mcp";
9
- //#region src/runtime/server/mcp.ts
9
+ // src/runtime/server/mcp.ts
10
10
  const MAX_BODY = 4194304;
11
11
  const text = (value) => [{
12
12
  type: "text",
@@ -104,5 +104,4 @@ var mcp_default = defineEventHandler(async (event) => {
104
104
  });
105
105
  return (await handler)(event);
106
106
  });
107
- //#endregion
108
107
  export { mcp_default as default };
@@ -0,0 +1,13 @@
1
+ interface ErrorContext {
2
+ event?: {
3
+ req?: Request;
4
+ path?: string;
5
+ };
6
+ }
7
+ interface NitroAppHooks {
8
+ hooks: {
9
+ hook(name: 'error', handler: (error: unknown, context: ErrorContext) => void): unknown;
10
+ };
11
+ }
12
+ export default function wervtPreviewErrors(nitroApp: NitroAppHooks): void;
13
+ export {};
@@ -0,0 +1,19 @@
1
+ import { recordPreviewError, trimStack } from "../shared/preview-errors.js";
2
+ // src/runtime/server/preview-errors-plugin.ts
3
+ function wervtPreviewErrors(nitroApp) {
4
+ const file = process.env.WERVT_PREVIEW_ERRORS;
5
+ if (!file) return;
6
+ nitroApp.hooks.hook("error", (error, { event }) => {
7
+ const status = error ?? {};
8
+ if ((status.status ?? status.statusCode ?? 500) < 500) return;
9
+ const err = error instanceof Error ? error : new Error(String(error));
10
+ recordPreviewError(file, {
11
+ at: Date.now(),
12
+ source: "server",
13
+ message: `${err.name}: ${err.message}`,
14
+ stack: trimStack(err.stack),
15
+ url: event?.req ? new URL(event.req.url).pathname : event?.path
16
+ });
17
+ });
18
+ }
19
+ export { wervtPreviewErrors as default };
@@ -1,6 +1,5 @@
1
1
  import { InferInsertModel } from "drizzle-orm";
2
2
  import { PgTable } from "drizzle-orm/pg-core";
3
- //#region src/runtime/server/seed.d.ts
4
3
  /**
5
4
  * Inserts `rows` into `table` only while it is empty, so a seed task can run again without
6
5
  * duplicating data or touching what users created. Returns how many rows it inserted.
@@ -14,5 +13,4 @@ import { PgTable } from "drizzle-orm/pg-core";
14
13
  * },
15
14
  * })
16
15
  */
17
- export declare function seedTable<T extends PgTable>(table: T, rows: InferInsertModel<T>[]): Promise<number>;
18
- //#endregion
16
+ export declare function seedTable<T extends PgTable>(table: T, rows: InferInsertModel<T>[]): Promise<number>;
@@ -1,6 +1,6 @@
1
1
  import { useDb } from "./db.js";
2
2
  import { sql } from "drizzle-orm";
3
- //#region src/runtime/server/seed.ts
3
+ // src/runtime/server/seed.ts
4
4
  /**
5
5
  * Inserts `rows` into `table` only while it is empty, so a seed task can run again without
6
6
  * duplicating data or touching what users created. Returns how many rows it inserted.
@@ -23,5 +23,4 @@ async function seedTable(table, rows) {
23
23
  await db.insert(target).values(rows);
24
24
  return rows.length;
25
25
  }
26
- //#endregion
27
26
  export { seedTable };
@@ -1,7 +1,6 @@
1
1
  import { ShapeResponse } from "../shared/shape.js";
2
2
  import { RequestEvent } from "nuxt/server";
3
3
  import { InferSelectModel, Table } from "drizzle-orm";
4
- //#region src/runtime/server/shape.d.ts
5
4
  export interface ShapeOptions<T extends Table | string = Table | string> {
6
5
  /**
7
6
  * The Drizzle table (`todos` from `server/db/schema`), or its name without schema. With the
@@ -22,5 +21,4 @@ export type ShapeRow<T extends Table | string> = T extends Table ? InferSelectMo
22
21
  * In production the request goes through the wervt runtime gateway, which pins the
23
22
  * table to this app's schema. In dev it goes to Electric directly (ELECTRIC_URL).
24
23
  */
25
- export declare function proxyShape<T extends Table | string>(event: RequestEvent, { table: tableOrName, where, params, columns }: ShapeOptions<T>): Promise<ShapeResponse<ShapeRow<T>>>;
26
- //#endregion
24
+ export declare function proxyShape<T extends Table | string>(event: RequestEvent, { table: tableOrName, where, params, columns }: ShapeOptions<T>): Promise<ShapeResponse<ShapeRow<T>>>;
@@ -1,5 +1,5 @@
1
1
  import { getTableName, isTable } from "drizzle-orm";
2
- //#region src/runtime/server/shape.ts
2
+ // src/runtime/server/shape.ts
3
3
  const CLIENT_PARAMS = [
4
4
  "offset",
5
5
  "handle",
@@ -53,5 +53,4 @@ function devTable(table) {
53
53
  function quoteIdentifier(name) {
54
54
  return /^[a-z_][a-z0-9_]*$/.test(name) ? name : `"${name.replaceAll("\"", "\"\"")}"`;
55
55
  }
56
- //#endregion
57
56
  export { proxyShape };
@@ -1,3 +1 @@
1
- //#region src/runtime/server/storage-plugin.d.ts
2
- export default function wervtStorage(): void;
3
- //#endregion
1
+ export default function wervtStorage(): void;