@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
package/dist/storage.js CHANGED
@@ -1,7 +1,76 @@
1
1
  import { storageCredentials } from "./provision.js";
2
2
  import { AwsClient } from "aws4fetch";
3
- //#region src/storage.ts
3
+ // src/storage.ts
4
4
  var StorageError = class extends Error {};
5
+ /**
6
+ * ablage marks bytes uploaded straight from browsers (`x-amz-meta-ablage-direct-upload`). Its
7
+ * metadata object (`<group>/meta/<id>`) is only written once `completeUpload()` has checked them,
8
+ * so marked bytes without one belong to an upload that was never completed.
9
+ */
10
+ const DIRECT_UPLOAD_MARKER = "x-amz-meta-ablage-direct-upload";
11
+ /**
12
+ * How old abandoned direct-upload bytes must be before the sweep deletes them. The browser calls
13
+ * `completeUpload()` right after its `PUT` finishes, so a day leaves any upload in flight alone;
14
+ * it matches the lifecycle rule for multipart uploads.
15
+ */
16
+ const ABANDONED_UPLOAD_GRACE_MS = 864e5;
17
+ /** ablage's S3 layout: `<group>/data/<id>` holds the bytes, `<group>/meta/<id>` the metadata. */
18
+ function metaKeyOf(key) {
19
+ const parts = key.split("/");
20
+ if (parts.length < 3 || parts.at(-2) !== "data" || !parts.at(-1)) return void 0;
21
+ return [
22
+ ...parts.slice(0, -2),
23
+ "meta",
24
+ parts.at(-1)
25
+ ].join("/");
26
+ }
27
+ const unescapeXml = (text) => text.replace(/&(amp|lt|gt|quot|apos|#x?[0-9a-f]+);/gi, (_, entity) => {
28
+ const named = {
29
+ amp: "&",
30
+ lt: "<",
31
+ gt: ">",
32
+ quot: "\"",
33
+ apos: "'"
34
+ };
35
+ if (entity in named) return named[entity];
36
+ const hex = entity[1] === "x" || entity[1] === "X";
37
+ return String.fromCodePoint(parseInt(entity.slice(hex ? 2 : 1), hex ? 16 : 10));
38
+ });
39
+ /**
40
+ * Objects of a ListObjectsV2 page, its folders (listed with a delimiter), and the token for the
41
+ * next page.
42
+ */
43
+ function parseObjectList(xml) {
44
+ const objects = [...xml.matchAll(/<Contents>([\s\S]*?)<\/Contents>/g)].map(([, entry]) => ({
45
+ key: unescapeXml(/<Key>([^<]*)<\/Key>/.exec(entry)?.[1] ?? ""),
46
+ lastModified: Date.parse(/<LastModified>([^<]*)<\/LastModified>/.exec(entry)?.[1] ?? ""),
47
+ size: Number(/<Size>([^<]*)<\/Size>/.exec(entry)?.[1] ?? 0)
48
+ }));
49
+ const prefixes = [...xml.matchAll(/<CommonPrefixes>([\s\S]*?)<\/CommonPrefixes>/g)].map(([, entry]) => unescapeXml(/<Prefix>([^<]*)<\/Prefix>/.exec(entry)?.[1] ?? ""));
50
+ const next = /<IsTruncated>true<\/IsTruncated>/.test(xml) ? /<NextContinuationToken>([^<]*)<\/NextContinuationToken>/.exec(xml)?.[1] : void 0;
51
+ return {
52
+ objects,
53
+ prefixes,
54
+ next: next === void 0 ? void 0 : unescapeXml(next)
55
+ };
56
+ }
57
+ /**
58
+ * ablage keeps a file as bytes and metadata, which only make sense together: the metadata key
59
+ * for bytes, the bytes for metadata, undefined for anything else.
60
+ */
61
+ function companionKeyOf(key) {
62
+ const meta = metaKeyOf(key);
63
+ if (meta !== void 0) return meta;
64
+ const parts = key.split("/");
65
+ if (parts.length < 3 || parts.at(-2) !== "meta" || !parts.at(-1)) return void 0;
66
+ return [
67
+ ...parts.slice(0, -2),
68
+ "data",
69
+ parts.at(-1)
70
+ ].join("/");
71
+ }
72
+ /** S3's limit for object keys, in bytes. */
73
+ const MAX_KEY_BYTES = 1024;
5
74
  function createStorage(options) {
6
75
  async function admin(path, body) {
7
76
  const res = await fetch(`${options.adminUrl}/v2/${path}`, {
@@ -52,8 +121,9 @@ function createStorage(options) {
52
121
  /**
53
122
  * Creates the app's bucket and key if they're missing, lets the app's origin upload and
54
123
  * download from browsers (CORS), and cleans up abandoned uploads. Safe to run on every deploy.
124
+ * With `quota`, sets the bucket's size limit too; without, leaves it as it is.
55
125
  */
56
- async function provision(app, appOrigin) {
126
+ async function provision(app, appOrigin, quota) {
57
127
  const { creds, client, base } = await s3(app);
58
128
  const bucket = await exists(`GetBucketInfo?globalAlias=${creds.bucket}`) ?? await admin("CreateBucket", { globalAlias: creds.bucket });
59
129
  if (!await exists(`GetKeyInfo?id=${creds.accessKeyId}`)) await admin("ImportKey", {
@@ -70,6 +140,7 @@ function createStorage(options) {
70
140
  owner: true
71
141
  }
72
142
  });
143
+ if (quota) await applyQuota(bucket.id, quota);
73
144
  const cors = `<CORSConfiguration><CORSRule>
74
145
  <AllowedOrigin>${appOrigin}</AllowedOrigin>
75
146
  <AllowedMethod>GET</AllowedMethod><AllowedMethod>HEAD</AllowedMethod>
@@ -92,6 +163,20 @@ function createStorage(options) {
92
163
  if (!rules.ok) throw new StorageError(`setting the lifecycle for ${creds.bucket}: ${await rules.text()}`);
93
164
  return { bucket: creds.bucket };
94
165
  }
166
+ function applyQuota(bucketId, quota) {
167
+ return admin(`UpdateBucket?id=${bucketId}`, { quotas: {
168
+ maxSize: quota.maxBytes,
169
+ maxObjects: null
170
+ } });
171
+ }
172
+ /** Changes the size limit of an existing bucket. False when the app has no bucket yet. */
173
+ async function setQuota(app, quota) {
174
+ const { creds } = await s3(app);
175
+ const bucket = await exists(`GetBucketInfo?globalAlias=${creds.bucket}`);
176
+ if (!bucket) return false;
177
+ await applyQuota(bucket.id, quota);
178
+ return true;
179
+ }
95
180
  async function usage(app) {
96
181
  const { creds } = await s3(app);
97
182
  const info = await exists(`GetBucketInfo?globalAlias=${creds.bucket}`);
@@ -100,9 +185,53 @@ function createStorage(options) {
100
185
  bucket: creds.bucket,
101
186
  objects: info.objects,
102
187
  bytes: info.bytes,
103
- unfinishedUploads: info.unfinishedMultipartUploads
188
+ unfinishedUploads: info.unfinishedMultipartUploads,
189
+ maxBytes: info.quotas?.maxSize ?? null
104
190
  };
105
191
  }
192
+ /**
193
+ * Deletes the bytes of direct uploads that were never completed: marked by ablage, older than
194
+ * `graceMs` and without a metadata object. ablage never lists them, but they count against the
195
+ * bucket quota. Safe to run any time and as often as wanted.
196
+ */
197
+ async function sweepAbandonedUploads(app, { graceMs = ABANDONED_UPLOAD_GRACE_MS, now = Date.now() } = {}) {
198
+ const { creds, client, base } = await s3(app);
199
+ if (!await exists(`GetBucketInfo?globalAlias=${creds.bucket}`)) return { deleted: [] };
200
+ const url = (key) => `${base}/${key.split("/").map(encodeURIComponent).join("/")}`;
201
+ const candidates = /* @__PURE__ */ new Map();
202
+ let token;
203
+ do {
204
+ const query = `list-type=2&max-keys=1000${token ? `&continuation-token=${encodeURIComponent(token)}` : ""}`;
205
+ const res = await client.fetch(`${base}?${query}`);
206
+ if (!res.ok) throw new StorageError(`listing ${creds.bucket}: ${await res.text()}`);
207
+ const page = parseObjectList(await res.text());
208
+ for (const object of page.objects) {
209
+ candidates.delete(object.key);
210
+ const metaKey = metaKeyOf(object.key);
211
+ if (metaKey !== void 0 && now - object.lastModified >= graceMs) candidates.set(metaKey, object.key);
212
+ }
213
+ token = page.next;
214
+ } while (token);
215
+ const deleted = [];
216
+ for (const [metaKey, key] of candidates) {
217
+ const head = await client.fetch(url(key), { method: "HEAD" });
218
+ if (head.status === 404) continue;
219
+ if (!head.ok) throw new StorageError(`checking ${key} in ${creds.bucket}: ${head.status}`);
220
+ if (!head.headers.get(DIRECT_UPLOAD_MARKER)) continue;
221
+ const meta = await client.fetch(url(metaKey), { method: "HEAD" });
222
+ if (meta.ok) continue;
223
+ if (meta.status !== 404) throw new StorageError(`checking ${metaKey} in ${creds.bucket}: ${meta.status}`);
224
+ const res = await client.fetch(url(key), { method: "DELETE" });
225
+ if (!res.ok && res.status !== 404) throw new StorageError(`deleting ${key} in ${creds.bucket}: ${await res.text()}`);
226
+ deleted.push(key);
227
+ }
228
+ return { deleted };
229
+ }
230
+ /** Whether the app's bucket exists, e.g. left over from a deleted app that kept its data. */
231
+ async function hasBucket(app) {
232
+ const creds = await storageCredentials(app, options.secret);
233
+ return Boolean(await exists(`GetBucketInfo?globalAlias=${creds.bucket}`));
234
+ }
106
235
  /** Deletes every object and unfinished upload, then the bucket. Only with "drop data". */
107
236
  async function drop(app) {
108
237
  const { creds, client, base } = await s3(app);
@@ -133,12 +262,102 @@ function createStorage(options) {
133
262
  await admin(`DeleteBucket?id=${String(bucket.id)}`, {});
134
263
  }
135
264
  }
265
+ const objectUrl = (base, key) => `${base}/${key.split("/").map(encodeURIComponent).join("/")}`;
266
+ /**
267
+ * One page of the bucket, one folder level at a time like a file browser: the files directly
268
+ * in `prefix` and the folders below it. Undefined when the app has no bucket.
269
+ */
270
+ async function list(app, { prefix = "", token, limit = 200 } = {}) {
271
+ const { creds, client, base } = await s3(app);
272
+ const query = new URLSearchParams({
273
+ "list-type": "2",
274
+ delimiter: "/",
275
+ prefix,
276
+ "max-keys": String(Math.min(Math.max(limit, 1), 1e3))
277
+ });
278
+ if (token) query.set("continuation-token", token);
279
+ const res = await client.fetch(`${base}?${query.toString()}`);
280
+ if (res.status === 404) return void 0;
281
+ if (!res.ok) throw new StorageError(`listing ${creds.bucket}: ${await res.text()}`);
282
+ const page = parseObjectList(await res.text());
283
+ return {
284
+ prefix,
285
+ folders: page.prefixes,
286
+ files: page.objects.map((object) => ({
287
+ key: object.key,
288
+ size: object.size,
289
+ lastModified: new Date(object.lastModified).toISOString()
290
+ })),
291
+ next: page.next
292
+ };
293
+ }
294
+ /** Size, type and metadata of one object; undefined when it doesn't exist. */
295
+ async function describe(app, key) {
296
+ const { creds, client, base } = await s3(app);
297
+ const res = await client.fetch(objectUrl(base, key), { method: "HEAD" });
298
+ if (res.status === 404) return void 0;
299
+ if (!res.ok) throw new StorageError(`reading ${key} in ${creds.bucket}: ${res.status}`);
300
+ const metadata = {};
301
+ for (const [name, value] of res.headers) if (name.startsWith("x-amz-meta-")) metadata[name.slice(11)] = value;
302
+ const modified = res.headers.get("last-modified");
303
+ return {
304
+ key,
305
+ size: Number(res.headers.get("content-length") ?? 0),
306
+ lastModified: modified ? new Date(modified).toISOString() : "",
307
+ contentType: res.headers.get("content-type"),
308
+ metadata
309
+ };
310
+ }
311
+ /**
312
+ * A presigned download link on `publicUrl`, the S3 API as browsers reach it, so files go from
313
+ * Garage straight to the browser. Always an attachment, never shown inline: the file is the
314
+ * app's users' content and may well be HTML.
315
+ */
316
+ async function downloadUrl(app, key, { publicUrl, expiresIn = 300 }) {
317
+ const { creds, client } = await s3(app);
318
+ const url = new URL(objectUrl(`${publicUrl.replace(/\/$/, "")}/${creds.bucket}`, key));
319
+ const name = key.split("/").at(-1) || "download";
320
+ const ascii = name.replace(/[^\x20-\x7e]|["\\]/g, "_");
321
+ url.searchParams.set("response-content-disposition", `attachment; filename="${ascii}"; filename*=UTF-8''${encodeURIComponent(name)}`);
322
+ url.searchParams.set("X-Amz-Expires", String(expiresIn));
323
+ return (await client.sign(url.toString(), {
324
+ method: "GET",
325
+ aws: { signQuery: true }
326
+ })).url;
327
+ }
328
+ /**
329
+ * Deletes one object, and its ablage companion (see companionKeyOf) so ablage never lists a
330
+ * file whose bytes are gone. Undefined when the object doesn't exist.
331
+ */
332
+ async function remove(app, key) {
333
+ const { creds, client, base } = await s3(app);
334
+ const present = async (k) => {
335
+ const res = await client.fetch(objectUrl(base, k), { method: "HEAD" });
336
+ if (res.status === 404) return false;
337
+ if (!res.ok) throw new StorageError(`checking ${k} in ${creds.bucket}: ${res.status}`);
338
+ return true;
339
+ };
340
+ if (!await present(key)) return void 0;
341
+ const companion = companionKeyOf(key);
342
+ const keys = companion !== void 0 && await present(companion) ? [key, companion] : [key];
343
+ for (const k of keys) {
344
+ const res = await client.fetch(objectUrl(base, k), { method: "DELETE" });
345
+ if (!res.ok && res.status !== 404) throw new StorageError(`deleting ${k} in ${creds.bucket}: ${await res.text()}`);
346
+ }
347
+ return { deleted: keys };
348
+ }
136
349
  return {
137
350
  ensureLayout,
138
351
  provision,
352
+ setQuota,
139
353
  usage,
354
+ list,
355
+ describe,
356
+ downloadUrl,
357
+ remove,
358
+ sweepAbandonedUploads,
359
+ hasBucket,
140
360
  drop
141
361
  };
142
362
  }
143
- //#endregion
144
- export { StorageError, createStorage };
363
+ export { ABANDONED_UPLOAD_GRACE_MS, MAX_KEY_BYTES, StorageError, companionKeyOf, createStorage, metaKeyOf, parseObjectList };
@@ -0,0 +1,34 @@
1
+ import { previewLoadingTemplate } from "./loading.js";
2
+ import { recordPreviewError, trimStack } from "./runtime/shared/preview-errors.js";
3
+ import { addServerPlugin, createResolver } from "nuxt/kit";
4
+ import { join } from "node:path";
5
+ // src/studio-preview.ts
6
+ /** One error per line: what the app raised in the browser or on the server. */
7
+ const PREVIEW_ERRORS = ".data/preview-errors.jsonl";
8
+ function setupStudioPreview(nuxt) {
9
+ const devtools = nuxt.options.devtools;
10
+ nuxt.options.devtools = {
11
+ ...typeof devtools === "object" ? devtools : {},
12
+ enabled: false
13
+ };
14
+ nuxt.options.devServer.loadingTemplate = previewLoadingTemplate;
15
+ nuxt.options.app.head.style = [...nuxt.options.app.head.style ?? [], { innerHTML: "nuxt-error-overlay,[data-nuxt-dev-error]{display:none!important}" }];
16
+ const resolver = createResolver(import.meta.url);
17
+ nuxt.hook("app:resolve", (app) => {
18
+ if (!app.errorComponent || app.errorComponent.includes("nuxt-error-page")) app.errorComponent = resolver.resolve("./runtime/app/preview-error");
19
+ });
20
+ const file = join(nuxt.options.rootDir, PREVIEW_ERRORS);
21
+ process.env.WERVT_PREVIEW_ERRORS = file;
22
+ addServerPlugin(resolver.resolve("./runtime/server/preview-errors-plugin"));
23
+ nuxt.hook("vite:serverCreated", (server) => {
24
+ server.environments?.client?.hot.on("nuxt:dev:client-error", (data) => {
25
+ recordPreviewError(file, {
26
+ at: Date.now(),
27
+ source: "browser",
28
+ message: `${data.name ?? "Error"}: ${data.message ?? ""}`,
29
+ stack: trimStack(data.stack)
30
+ });
31
+ });
32
+ });
33
+ }
34
+ export { PREVIEW_ERRORS, setupStudioPreview };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wervt/nuxt",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "description": "Nuxt module for apps hosted on wervt: database, live data, MCP and runtime setup.",
5
5
  "keywords": [
6
6
  "drizzle",
@@ -37,6 +37,10 @@
37
37
  "types": "./dist/runtime/email/index.d.ts",
38
38
  "default": "./dist/runtime/email/index.js"
39
39
  },
40
+ "./anthropic": {
41
+ "types": "./dist/runtime/server/anthropic.d.ts",
42
+ "default": "./dist/runtime/server/anthropic.js"
43
+ },
40
44
  "./provision": {
41
45
  "types": "./dist/provision.d.ts",
42
46
  "default": "./dist/provision.js"
@@ -46,27 +50,32 @@
46
50
  "@electric-sql/client": "^1.5.28",
47
51
  "@valibot/to-json-schema": "^1.8.0",
48
52
  "aws4fetch": "^1.0.20",
49
- "evlog": "^2.30.1",
53
+ "evlog": "^2.34.0",
50
54
  "h3-mcp": "0.2.0",
51
- "html-to-text": "^9.0.5",
52
- "jose": "^6.2.12",
55
+ "html-to-text": "^10.0.1",
56
+ "jose": "^6.2.13",
53
57
  "postgres": "^3.4.9",
54
58
  "valibot": "^1.5.0"
55
59
  },
56
60
  "devDependencies": {
61
+ "@anthropic-ai/sdk": "^1.3.0",
57
62
  "@types/html-to-text": "^9.0.4",
58
- "drizzle-orm": "1.0.0-rc.4",
59
- "h3": "2.0.2",
63
+ "drizzle-orm": "1.0.0-rc.5",
64
+ "h3": "2.0.8",
60
65
  "nitro": "3.0.260903-beta",
61
66
  "nuxt": "npm:nuxt-nightly@5x",
62
- "vue": "^3.5.43"
67
+ "vue": "^3.5.44"
63
68
  },
64
69
  "peerDependencies": {
65
- "drizzle-orm": "1.0.0-rc.4",
70
+ "@anthropic-ai/sdk": ">=0.97.1",
71
+ "drizzle-orm": ">=1.0.0-rc.5 <2",
66
72
  "h3": "^2.0.1",
67
73
  "nuxt": ">=4.2"
68
74
  },
69
75
  "peerDependenciesMeta": {
76
+ "@anthropic-ai/sdk": {
77
+ "optional": true
78
+ },
70
79
  "h3": {
71
80
  "optional": true
72
81
  }