@wervt/nuxt 0.3.3 → 0.3.5

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 (122) hide show
  1. package/dist/dev.d.ts +1 -3
  2. package/dist/dev.js +33 -11
  3. package/dist/env-types.js +1 -2
  4. package/dist/icons.js +1 -2
  5. package/dist/loading.js +30 -0
  6. package/dist/module.d.ts +0 -2
  7. package/dist/module.js +3 -2
  8. package/dist/node_modules/.pnpm/nitro@3.0.260903-beta/node_modules/nitro/dist/runtime/internal/task.js +1 -2
  9. package/dist/og-image.js +1 -2
  10. package/dist/preview.d.ts +1 -3
  11. package/dist/preview.js +1 -2
  12. package/dist/provision.d.ts +12 -2
  13. package/dist/provision.js +35 -21
  14. package/dist/route-tools.js +1 -2
  15. package/dist/runtime/app/plugins/analytics.client.d.ts +0 -2
  16. package/dist/runtime/app/plugins/analytics.client.js +1 -2
  17. package/dist/runtime/app/plugins/feedback.client.d.ts +0 -2
  18. package/dist/runtime/app/plugins/feedback.client.js +1 -2
  19. package/dist/runtime/app/plugins/update.client.d.ts +0 -2
  20. package/dist/runtime/app/plugins/update.client.js +1 -2
  21. package/dist/runtime/app/plugins/user.client.d.ts +0 -2
  22. package/dist/runtime/app/plugins/user.client.js +1 -2
  23. package/dist/runtime/app/plugins/webmcp.client.d.ts +0 -2
  24. package/dist/runtime/app/plugins/webmcp.client.js +1 -2
  25. package/dist/runtime/app/preview-error.d.ts +26 -0
  26. package/dist/runtime/app/preview-error.js +69 -0
  27. package/dist/runtime/app/snapshot.d.ts +1 -3
  28. package/dist/runtime/app/snapshot.js +1 -2
  29. package/dist/runtime/app/useShape.d.ts +1 -3
  30. package/dist/runtime/app/useShape.js +3 -4
  31. package/dist/runtime/app/useWervtFeedback.d.ts +0 -2
  32. package/dist/runtime/app/useWervtFeedback.js +1 -2
  33. package/dist/runtime/app/useWervtTrack.d.ts +1 -3
  34. package/dist/runtime/app/useWervtTrack.js +1 -2
  35. package/dist/runtime/app/useWervtUpdate.d.ts +1 -3
  36. package/dist/runtime/app/useWervtUpdate.js +2 -3
  37. package/dist/runtime/app/useWervtUser.d.ts +1 -3
  38. package/dist/runtime/app/useWervtUser.js +1 -2
  39. package/dist/runtime/email/components.d.ts +1 -3
  40. package/dist/runtime/email/components.js +1 -2
  41. package/dist/runtime/email/invite.d.ts +1 -3
  42. package/dist/runtime/email/invite.js +1 -2
  43. package/dist/runtime/email/render.d.ts +1 -3
  44. package/dist/runtime/email/render.js +1 -2
  45. package/dist/runtime/server/access.d.ts +1 -3
  46. package/dist/runtime/server/access.js +1 -2
  47. package/dist/runtime/server/agent-token-plugin.d.ts +0 -2
  48. package/dist/runtime/server/agent-token-plugin.js +1 -2
  49. package/dist/runtime/server/anthropic.d.ts +1 -3
  50. package/dist/runtime/server/anthropic.js +1 -2
  51. package/dist/runtime/server/boot.d.ts +0 -2
  52. package/dist/runtime/server/boot.js +1 -2
  53. package/dist/runtime/server/db.d.ts +1 -3
  54. package/dist/runtime/server/db.js +1 -2
  55. package/dist/runtime/server/dev-auth.d.ts +0 -2
  56. package/dist/runtime/server/dev-auth.js +1 -2
  57. package/dist/runtime/server/email-preview.d.ts +0 -2
  58. package/dist/runtime/server/email-preview.js +1 -2
  59. package/dist/runtime/server/identity.d.ts +1 -3
  60. package/dist/runtime/server/identity.js +1 -2
  61. package/dist/runtime/server/logger.d.ts +0 -2
  62. package/dist/runtime/server/logger.js +1 -2
  63. package/dist/runtime/server/mail.d.ts +1 -3
  64. package/dist/runtime/server/mail.js +1 -2
  65. package/dist/runtime/server/mcp-auth-server.d.ts +0 -2
  66. package/dist/runtime/server/mcp-auth-server.js +1 -2
  67. package/dist/runtime/server/mcp-auth.d.ts +1 -3
  68. package/dist/runtime/server/mcp-auth.js +1 -2
  69. package/dist/runtime/server/mcp-claims.d.ts +1 -3
  70. package/dist/runtime/server/mcp-claims.js +1 -2
  71. package/dist/runtime/server/mcp-metadata.d.ts +0 -2
  72. package/dist/runtime/server/mcp-metadata.js +1 -2
  73. package/dist/runtime/server/mcp.d.ts +0 -2
  74. package/dist/runtime/server/mcp.js +1 -2
  75. package/dist/runtime/server/preview-errors-plugin.d.ts +13 -0
  76. package/dist/runtime/server/preview-errors-plugin.js +19 -0
  77. package/dist/runtime/server/seed.d.ts +1 -3
  78. package/dist/runtime/server/seed.js +1 -2
  79. package/dist/runtime/server/shape.d.ts +1 -3
  80. package/dist/runtime/server/shape.js +1 -2
  81. package/dist/runtime/server/storage-plugin.d.ts +1 -3
  82. package/dist/runtime/server/storage-plugin.js +1 -2
  83. package/dist/runtime/server/task-handlers.d.ts +1 -3
  84. package/dist/runtime/server/task-handlers.js +1 -2
  85. package/dist/runtime/server/task-list-nitro2.d.ts +0 -2
  86. package/dist/runtime/server/task-list-nitro2.js +1 -2
  87. package/dist/runtime/server/task-list-nitro3.d.ts +0 -2
  88. package/dist/runtime/server/task-list-nitro3.js +1 -2
  89. package/dist/runtime/server/task-run-nitro2.d.ts +0 -2
  90. package/dist/runtime/server/task-run-nitro2.js +1 -2
  91. package/dist/runtime/server/task-run-nitro3.d.ts +0 -2
  92. package/dist/runtime/server/task-run-nitro3.js +1 -2
  93. package/dist/runtime/server/tasks.d.ts +1 -3
  94. package/dist/runtime/server/tasks.js +1 -2
  95. package/dist/runtime/server/tool-handlers.d.ts +1 -3
  96. package/dist/runtime/server/tool-handlers.js +1 -2
  97. package/dist/runtime/server/tool-list.js +1 -2
  98. package/dist/runtime/server/tool-run.js +1 -2
  99. package/dist/runtime/server/tools.d.ts +0 -2
  100. package/dist/runtime/server/tools.js +1 -2
  101. package/dist/runtime/server/user-route.d.ts +0 -2
  102. package/dist/runtime/server/user-route.js +1 -2
  103. package/dist/runtime/server/user.d.ts +0 -2
  104. package/dist/runtime/server/user.js +1 -2
  105. package/dist/runtime/server/validate.d.ts +0 -2
  106. package/dist/runtime/server/validate.js +1 -2
  107. package/dist/runtime/shared/agent-token.d.ts +1 -3
  108. package/dist/runtime/shared/agent-token.js +1 -2
  109. package/dist/runtime/shared/mark.d.ts +1 -0
  110. package/dist/runtime/shared/mark.js +3 -0
  111. package/dist/runtime/shared/preview-errors.d.ts +10 -0
  112. package/dist/runtime/shared/preview-errors.js +14 -0
  113. package/dist/runtime/shared/shape.d.ts +0 -2
  114. package/dist/runtime/shared/shape.js +1 -2
  115. package/dist/runtime/shared/user.d.ts +1 -3
  116. package/dist/runtime/shared/user.js +1 -2
  117. package/dist/seed.js +1 -2
  118. package/dist/shapes.js +1 -2
  119. package/dist/storage.d.ts +2 -3
  120. package/dist/storage.js +7 -2
  121. package/dist/studio-preview.js +38 -0
  122. package/package.json +6 -6
package/dist/dev.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import { WervtRole } from "./runtime/shared/user.js";
2
2
  import "nuxt/schema";
3
- //#region src/dev.d.ts
4
3
  export interface DevOptions {
5
4
  /** Superuser URL of the local wervt Postgres. */
6
5
  adminUrl?: string;
@@ -29,5 +28,4 @@ export interface DevOptions {
29
28
  accounts?: Record<string, string>;
30
29
  role?: WervtRole;
31
30
  };
32
- }
33
- //#endregion
31
+ }
package/dist/dev.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import { createStorage } from "./storage.js";
2
- import { appDatabaseUrl, dbRole, provisionDatabase, storageCredentials } from "./provision.js";
2
+ import { appDatabaseUrl, dbRole, migrateAs, provisionDatabase, storageCredentials } from "./provision.js";
3
3
  import { SEED_TASK, runDevSeed } from "./seed.js";
4
4
  import { useLogger } from "nuxt/kit";
5
5
  import { existsSync, watch } from "node:fs";
6
6
  import { readFile } from "node:fs/promises";
7
7
  import { join } from "node:path";
8
- //#region src/dev.ts
8
+ // src/dev.ts
9
9
  const DEFAULTS = {
10
10
  adminUrl: "postgres://postgres:postgres@localhost:5432/wervt",
11
11
  dbSecret: "dev-secret",
@@ -22,6 +22,7 @@ const logger = useLogger("wervt");
22
22
  * points the dev server at the app's database and Electric.
23
23
  */
24
24
  async function setupDev(nuxt, app, options, features = {}) {
25
+ forgetOwnEnv(nuxt.options.rootDir);
25
26
  const config = {
26
27
  adminUrl: process.env.WERVT_PG_ADMIN_URL ?? options.adminUrl ?? DEFAULTS.adminUrl,
27
28
  dbSecret: process.env.WERVT_DB_SECRET ?? options.dbSecret ?? DEFAULTS.dbSecret,
@@ -43,13 +44,18 @@ async function setupDev(nuxt, app, options, features = {}) {
43
44
  secret: config.dbSecret,
44
45
  migrationsFolder
45
46
  };
47
+ const given = process.env.DATABASE_URL && process.env.WERVT_DB_SCHEMA ? {
48
+ url: process.env.DATABASE_URL,
49
+ schema: process.env.WERVT_DB_SCHEMA
50
+ } : void 0;
51
+ const database = given?.schema ?? dbRole(app);
46
52
  let serverUrl;
47
53
  let seedPending = false;
48
54
  const seed = async () => {
49
55
  if (!seedPending || !serverUrl) return;
50
56
  seedPending = false;
51
57
  try {
52
- if ((await runDevSeed(serverUrl)).status === "seeded") logger.success(`Seeded \`${dbRole(app)}\` (${SEED_TASK})`);
58
+ if ((await runDevSeed(serverUrl)).status === "seeded") logger.success(`Seeded \`${database}\` (${SEED_TASK})`);
53
59
  } catch (e) {
54
60
  logger.warn(`\`${SEED_TASK}\` failed:`, e instanceof Error ? e.message : e);
55
61
  }
@@ -60,21 +66,29 @@ async function setupDev(nuxt, app, options, features = {}) {
60
66
  });
61
67
  const migrate = async () => {
62
68
  try {
63
- const { fresh } = await provisionDatabase(provision);
64
- logger.success(`Database \`${dbRole(app)}\` is up to date`);
69
+ const { fresh } = given ? await migrateAs({
70
+ ...given,
71
+ migrationsFolder
72
+ }) : await provisionDatabase(provision);
73
+ logger.success(`Database \`${database}\` is up to date`);
65
74
  if (fresh && options.seed !== false) {
66
75
  seedPending = true;
67
76
  seed();
68
77
  }
69
78
  } catch (e) {
70
- logger.warn(`Could not migrate \`${dbRole(app)}\`. Is the wervt stack running (docker compose up -d)?`, e instanceof Error ? e.message : e);
79
+ logger.warn(given ? `Could not migrate \`${database}\`.` : `Could not migrate \`${database}\`. Is the wervt stack running (docker compose up -d)?`, e instanceof Error ? e.message : e);
71
80
  }
72
81
  };
73
82
  await migrate();
74
- process.env.DATABASE_URL ??= await appDatabaseUrl(provision);
75
- process.env.WERVT_DB_SCHEMA ??= dbRole(app);
76
- process.env.ELECTRIC_URL ??= config.electricUrl;
77
- process.env.ELECTRIC_SECRET ??= config.electricSecret;
83
+ const own = {
84
+ DATABASE_URL: await appDatabaseUrl(provision),
85
+ WERVT_DB_SCHEMA: dbRole(app),
86
+ ELECTRIC_URL: config.electricUrl,
87
+ ELECTRIC_SECRET: config.electricSecret
88
+ };
89
+ const set = Object.fromEntries(Object.entries(own).filter(([key]) => process.env[key] === void 0));
90
+ Object.assign(process.env, set);
91
+ process.env[OWN_ENV] = JSON.stringify(set);
78
92
  let timer;
79
93
  const watcher = watch(migrationsFolder, { recursive: true }, (_event, file) => {
80
94
  if (!file?.endsWith(".sql")) return;
@@ -86,6 +100,15 @@ async function setupDev(nuxt, app, options, features = {}) {
86
100
  });
87
101
  nuxt.hook("close", () => watcher.close());
88
102
  }
103
+ const OWN_ENV = "WERVT_DEV_OWN_ENV";
104
+ function forgetOwnEnv(rootDir) {
105
+ const own = process.env[OWN_ENV];
106
+ if (!own) return;
107
+ for (const [key, value] of Object.entries(JSON.parse(own))) if (process.env[key] === value) delete process.env[key];
108
+ delete process.env[OWN_ENV];
109
+ const file = join(rootDir, ".env");
110
+ if (existsSync(file)) process.loadEnvFile(file);
111
+ }
89
112
  /** The app's bucket in the local Garage, and WERVT_S3_* for ablage, like in production. */
90
113
  async function setupStorage(app, secret, options) {
91
114
  const s3Url = process.env.WERVT_S3_URL ?? options.s3Url ?? DEFAULTS.s3Url;
@@ -118,5 +141,4 @@ async function resolveAppName(rootDir, name) {
118
141
  }
119
142
  return name.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "");
120
143
  }
121
- //#endregion
122
144
  export { resolveAppName, setupDev };
package/dist/env-types.js CHANGED
@@ -1,4 +1,4 @@
1
- //#region src/env-types.ts
1
+ // src/env-types.ts
2
2
  /** `.nuxt/types/wervt-env.d.ts`: types for the `process.env` the isolate provides. */
3
3
  const PROCESS_ENV_TYPES = `declare namespace NodeJS {
4
4
  interface ProcessEnv {
@@ -10,5 +10,4 @@ const PROCESS_ENV_TYPES = `declare namespace NodeJS {
10
10
  }
11
11
  declare var process: NodeJS.Process
12
12
  `;
13
- //#endregion
14
13
  export { PROCESS_ENV_TYPES };
package/dist/icons.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { dependencyNames } from "./og-image.js";
2
- //#region src/icons.ts
2
+ // src/icons.ts
3
3
  const ICONIFY_JSON = "@iconify-json/";
4
4
  /** The whole Iconify set in one package; @nuxt/icon finds every collection in it. */
5
5
  const ICONIFY_ALL = "@iconify/json";
@@ -27,5 +27,4 @@ function iconWarning(sources, nuxtUi) {
27
27
  if (nuxtUi && !sources.collections.includes("lucide")) return "Nuxt UI uses Lucide icons, but `@iconify-json/lucide` is not installed, so they won't render on wervt (the isolate can't reach the Iconify API). Run `pnpm add -D @iconify-json/lucide`, plus `@iconify-json/<name>` for every other icon collection the app uses.";
28
28
  if (sources.collections.length === 0) return "No icon collection is installed, so icons won't render on wervt (the isolate can't reach the Iconify API). Install the collections the app uses, e.g. `pnpm add -D @iconify-json/lucide`.";
29
29
  }
30
- //#endregion
31
30
  export { iconDefaults, iconSources, iconWarning };
@@ -0,0 +1,30 @@
1
+ import { WERVT_MARK } from "./runtime/shared/mark.js";
2
+ // src/loading.ts
3
+ const previewLoadingTemplate = () => `<!doctype html>
4
+ <html lang="de">
5
+ <head>
6
+ <meta charset="utf-8">
7
+ <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
8
+ <title>Einen Moment …</title>
9
+ <style>
10
+ :root { color-scheme: light dark; --fg: #0a0a0b; --muted: #6b6b70; --bg: #fafaf9; --a: #2f5bff; }
11
+ @media (prefers-color-scheme: dark) { :root { --fg: #fafaf9; --muted: #a1a1a6; --bg: #0a0a0b; --a: #5885ff; } }
12
+ html, body { height: 100%; margin: 0; }
13
+ body { display: grid; place-content: center; justify-items: center; gap: 1rem; background: var(--bg);
14
+ color: var(--fg); font: 500 1rem/1.4 system-ui, -apple-system, sans-serif; text-align: center; }
15
+ svg { width: 3rem; height: 3rem; color: var(--fg); animation: pulse 1.6s ease-in-out infinite; }
16
+ p { margin: 0; color: var(--muted); }
17
+ @keyframes pulse { 50% { opacity: .45; transform: scale(.94); } }
18
+ @media (prefers-reduced-motion: reduce) { svg { animation: none; } }
19
+ </style>
20
+ </head>
21
+ <body>
22
+ ${WERVT_MARK}
23
+ <p id="m">Einen Moment, die App wird gerade aktualisiert …</p>
24
+ <script>
25
+ if (!navigator.language.toLowerCase().startsWith('de'))
26
+ document.getElementById('m').textContent = 'One moment, the app is being updated …'
27
+ <\/script>
28
+ </body>
29
+ </html>`;
30
+ export { previewLoadingTemplate };
package/dist/module.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import { DevOptions } from "./dev.js";
2
2
  import { PreviewOptions } from "./preview.js";
3
- //#region src/module.d.ts
4
3
  export interface ModuleOptions {
5
4
  /** App name on wervt. Defaults to the package.json name. */
6
5
  app?: string;
@@ -82,5 +81,4 @@ export interface TasksManifest {
82
81
  timezone?: string;
83
82
  }
84
83
  declare const _default: import("nuxt/schema").NuxtModule<ModuleOptions, ModuleOptions, false>;
85
- //#endregion
86
84
  export { _default as default };
package/dist/module.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { SEED_TASK, unscheduleSeed } from "./seed.js";
2
2
  import { resolveAppName, setupDev } from "./dev.js";
3
+ import { setupStudioPreview } from "./studio-preview.js";
3
4
  import { PROCESS_ENV_TYPES } from "./env-types.js";
4
5
  import { dependencyNames, ogImageDefaults, ogImageWarning } from "./og-image.js";
5
6
  import { iconDefaults, iconSources, iconWarning } from "./icons.js";
@@ -12,7 +13,7 @@ import { mkdir, readFile, readdir, writeFile } from "node:fs/promises";
12
13
  import { builtinModules, createRequire } from "node:module";
13
14
  import { dirname, extname, join, relative, resolve, sep } from "node:path";
14
15
  import evlog from "evlog/nuxt";
15
- //#region src/module.ts
16
+ // src/module.ts
16
17
  const CONTENT_DATABASE = {
17
18
  type: "postgres",
18
19
  url: "{{DATABASE_URL}}"
@@ -275,6 +276,7 @@ var module_default = defineNuxtModule({
275
276
  if (nuxt.options.dev && nitroMajor >= 3) {
276
277
  if (!hasPackage(nuxt.options.rootDir, "vue")) logger.warn("`vue` is not a dependency of this app, so `nuxt dev` fails with \"module is not defined\". Run `pnpm add vue`.");
277
278
  }
279
+ if (nuxt.options.dev && process.env.WERVT_PREVIEW) setupStudioPreview(nuxt);
278
280
  if (nuxt.options.dev && !nuxt.options._prepare) {
279
281
  const app = await resolveAppName(nuxt.options.rootDir, options.app);
280
282
  await setupDev(nuxt, app, options.dev ?? {}, { storage: useAblage });
@@ -535,5 +537,4 @@ function exceptRuntime(pattern) {
535
537
  if (pattern.source.startsWith("^")) return pattern;
536
538
  return new RegExp(`^(?![\\s\\S]*[\\\\/]@wervt[\\\\/]nuxt[\\\\/]src[\\\\/]runtime[\\\\/])[\\s\\S]*?(?:${pattern.source})`, pattern.flags);
537
539
  }
538
- //#endregion
539
540
  export { module_default as default };
@@ -1,6 +1,6 @@
1
1
  import { tasks } from "#nitro/virtual/tasks";
2
2
  import { HTTPError } from "h3";
3
- //#region ../../node_modules/.pnpm/nitro@3.0.260903-beta/node_modules/nitro/dist/runtime/internal/task.mjs
3
+ // ../../node_modules/.pnpm/nitro@3.0.260903-beta/node_modules/nitro/dist/runtime/internal/task.mjs
4
4
  const __runningTasks__ = {};
5
5
  async function runTask(name, { payload = {}, context = {} } = {}) {
6
6
  if (__runningTasks__[name]) return __runningTasks__[name];
@@ -25,5 +25,4 @@ async function runTask(name, { payload = {}, context = {} } = {}) {
25
25
  delete __runningTasks__[name];
26
26
  }
27
27
  }
28
- //#endregion
29
28
  export { runTask };
package/dist/og-image.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { readFile } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
- //#region src/og-image.ts
3
+ // src/og-image.ts
4
4
  /** The app's dependencies and devDependencies, by name. */
5
5
  async function dependencyNames(rootDir) {
6
6
  const pkg = JSON.parse(await readFile(join(rootDir, "package.json"), "utf8").catch(() => "{}"));
@@ -35,5 +35,4 @@ function ogImageWarning(dependencies) {
35
35
  if (missing.length === 0) return void 0;
36
36
  return `OG images render with WebAssembly on wervt (an isolate can't load native renderers), and nuxt-og-image's build also wants the native package, but ${missing.map((name) => `\`${name}\``).join(", ")} ${missing.length > 1 ? "are" : "is"} not installed. Run \`pnpm add ${missing.join(" ")}\`.`;
37
37
  }
38
- //#endregion
39
38
  export { dependencyNames, ogImageDefaults, ogImageWarning };
package/dist/preview.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- //#region src/preview.d.ts
2
1
  export interface PreviewOptions {
3
2
  /** Defaults to the app's `app.head.title`. */
4
3
  title?: string;
@@ -10,5 +9,4 @@ export interface PreviewOptions {
10
9
  icon?: string;
11
10
  /** Defaults to the app's `theme-color` meta tag. */
12
11
  themeColor?: string;
13
- }
14
- //#endregion
12
+ }
package/dist/preview.js CHANGED
@@ -1,4 +1,4 @@
1
- //#region src/preview.ts
1
+ // src/preview.ts
2
2
  /**
3
3
  * What the runtime shows. Without `preview`, only the app's name and icon: for some private apps
4
4
  * the title alone says too much, so the rest of the head is only used once the app opts in.
@@ -35,5 +35,4 @@ function localFiles(manifest) {
35
35
  if (!manifest) return [];
36
36
  return [manifest.image, manifest.icon].filter((path) => !!path && path.startsWith("/") && !path.startsWith("//"));
37
37
  }
38
- //#endregion
39
38
  export { localFiles, previewManifest };
@@ -1,5 +1,4 @@
1
1
  import { MAX_KEY_BYTES, ObjectDetails, ObjectListing, StorageError, StorageOptions, StorageQuota, StorageUsage, StoredObject, createStorage } from "./storage.js";
2
- //#region src/provision.d.ts
3
2
  export interface ProvisionOptions {
4
3
  /** App name, `[a-z0-9-]+`. */
5
4
  app: string;
@@ -49,6 +48,18 @@ export declare function appDatabaseUrl({ app, adminUrl, secret }: ProvisionOptio
49
48
  export declare function provisionDatabase(options: ProvisionOptions): Promise<{
50
49
  fresh: boolean;
51
50
  }>;
51
+ /**
52
+ * Runs the Drizzle migrations as the app's own role, into its schema. The role needs CREATE on
53
+ * the database while this runs (see `provisionDatabase`). `fresh` is true when no migration had
54
+ * been applied before.
55
+ */
56
+ export declare function migrateAs({ url, schema, migrationsFolder }: {
57
+ url: string;
58
+ schema: string;
59
+ migrationsFolder: string;
60
+ }): Promise<{
61
+ fresh: boolean;
62
+ }>;
52
63
  /** The role PgBouncer signs in with to look up app roles' password hashes (`auth_query`). */
53
64
  export declare const POOLER_ROLE = "pgbouncer";
54
65
  /**
@@ -63,5 +74,4 @@ export declare function setupPooler({ adminUrl, password }: {
63
74
  }): Promise<void>;
64
75
  /** Drops the app's schema (all its data) and role. */
65
76
  export declare function dropDatabase({ app, adminUrl }: Pick<ProvisionOptions, 'app' | 'adminUrl'>): Promise<void>;
66
- //#endregion
67
77
  export { MAX_KEY_BYTES, type ObjectDetails, type ObjectListing, StorageError, type StorageOptions, type StorageQuota, type StorageUsage, type StoredObject, createStorage };
package/dist/provision.js CHANGED
@@ -2,7 +2,7 @@ import { MAX_KEY_BYTES, StorageError, createStorage } from "./storage.js";
2
2
  import { drizzle } from "drizzle-orm/postgres-js";
3
3
  import { migrate } from "drizzle-orm/postgres-js/migrator";
4
4
  import postgres from "postgres";
5
- //#region src/provision.ts
5
+ // src/provision.ts
6
6
  const DEFAULT_ROLE_TIMEOUTS = {
7
7
  statement: "30s",
8
8
  idleInTransaction: "60s"
@@ -110,35 +110,50 @@ async function provisionDatabase(options) {
110
110
  await admin.unsafe(`grant connect on database "${database}" to "${role}"`);
111
111
  await admin.unsafe(`revoke all on schema public from public`);
112
112
  if (!migrationsFolder) return { fresh: false };
113
- const table = `"${role}"."__drizzle_migrations"`;
114
- const [state] = await admin`
115
- select to_regclass(${table}) is not null as exists`;
116
- let fresh = !state?.exists;
117
- if (!fresh) {
118
- const [row] = await admin.unsafe(`select exists(select 1 from ${table}) as applied`);
119
- fresh = !row?.applied;
120
- }
121
113
  await admin.unsafe(`grant create on database "${database}" to "${role}"`);
122
- const client = postgres(await appDatabaseUrl(options), {
123
- max: 1,
124
- onnotice: () => {}
125
- });
126
114
  try {
127
- await client.unsafe("set statement_timeout = 0; set idle_in_transaction_session_timeout = 0");
128
- await migrate(drizzle({ client }), {
129
- migrationsFolder,
130
- migrationsSchema: role
115
+ return await migrateAs({
116
+ url: await appDatabaseUrl(options),
117
+ schema: role,
118
+ migrationsFolder
131
119
  });
132
120
  } finally {
133
- await client.end();
134
121
  await admin.unsafe(`revoke create on database "${database}" from "${role}"`);
135
122
  }
136
- return { fresh };
137
123
  } finally {
138
124
  await admin.end();
139
125
  }
140
126
  }
141
127
  /**
128
+ * Runs the Drizzle migrations as the app's own role, into its schema. The role needs CREATE on
129
+ * the database while this runs (see `provisionDatabase`). `fresh` is true when no migration had
130
+ * been applied before.
131
+ */
132
+ async function migrateAs({ url, schema, migrationsFolder }) {
133
+ const client = postgres(url, {
134
+ max: 1,
135
+ onnotice: () => {}
136
+ });
137
+ try {
138
+ const table = `"${schema}"."__drizzle_migrations"`;
139
+ const [state] = await client`
140
+ select to_regclass(${table}) is not null as exists`;
141
+ let fresh = !state?.exists;
142
+ if (!fresh) {
143
+ const [row] = await client.unsafe(`select exists(select 1 from ${table}) as applied`);
144
+ fresh = !row?.applied;
145
+ }
146
+ await client.unsafe("set statement_timeout = 0; set idle_in_transaction_session_timeout = 0");
147
+ await migrate(drizzle({ client }), {
148
+ migrationsFolder,
149
+ migrationsSchema: schema
150
+ });
151
+ return { fresh };
152
+ } finally {
153
+ await client.end();
154
+ }
155
+ }
156
+ /**
142
157
  * Gives the role its schema and everything in it back. A dump restored with `--no-owner`
143
158
  * (Coolify's backups) leaves all of it owned by the restoring superuser.
144
159
  */
@@ -220,5 +235,4 @@ async function dropDatabase({ app, adminUrl }) {
220
235
  await admin.end();
221
236
  }
222
237
  }
223
- //#endregion
224
- export { DEFAULT_ROLE_TIMEOUTS, MAX_KEY_BYTES, POOLER_ROLE, StorageError, appDatabaseUrl, createStorage, dbPassword, dbRole, dropDatabase, hmac, parseDuration, provisionDatabase, setRoleTimeouts, setupPooler, storageCredentials };
238
+ export { DEFAULT_ROLE_TIMEOUTS, MAX_KEY_BYTES, POOLER_ROLE, StorageError, appDatabaseUrl, createStorage, dbPassword, dbRole, dropDatabase, hmac, migrateAs, parseDuration, provisionDatabase, setRoleTimeouts, setupPooler, storageCredentials };
@@ -1,6 +1,6 @@
1
1
  import { readFile, readdir } from "node:fs/promises";
2
2
  import { extname, join, sep } from "node:path";
3
- //#region src/route-tools.ts
3
+ // src/route-tools.ts
4
4
  const EXTENSIONS = /* @__PURE__ */ new Set([
5
5
  ".ts",
6
6
  ".js",
@@ -40,5 +40,4 @@ async function findRouteTools(dir, prefix = []) {
40
40
  }
41
41
  return found;
42
42
  }
43
- //#endregion
44
43
  export { findRouteTools, routeToolName };
@@ -1,4 +1,2 @@
1
- //#region src/runtime/app/plugins/analytics.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, useRouter, useRuntimeConfig } from "#app";
2
- //#region src/runtime/app/plugins/analytics.client.ts
2
+ // src/runtime/app/plugins/analytics.client.ts
3
3
  var analytics_client_default = defineNuxtPlugin({
4
4
  name: "wervt:analytics",
5
5
  setup() {
@@ -12,5 +12,4 @@ var analytics_client_default = defineNuxtPlugin({
12
12
  });
13
13
  }
14
14
  });
15
- //#endregion
16
15
  export { analytics_client_default as default };
@@ -1,4 +1,2 @@
1
- //#region src/runtime/app/plugins/feedback.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 { pendingFeedbackContext } from "../useWervtFeedback.js";
2
2
  import { defineNuxtPlugin, onNuxtReady, useRuntimeConfig } from "#app";
3
- //#region src/runtime/app/plugins/feedback.client.ts
3
+ // src/runtime/app/plugins/feedback.client.ts
4
4
  var feedback_client_default = defineNuxtPlugin({
5
5
  name: "wervt:feedback",
6
6
  setup() {
@@ -28,5 +28,4 @@ var feedback_client_default = defineNuxtPlugin({
28
28
  });
29
29
  }
30
30
  });
31
- //#endregion
32
31
  export { feedback_client_default as default };
@@ -1,4 +1,2 @@
1
- //#region src/runtime/app/plugins/update.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 { 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 };
@@ -1,5 +1,4 @@
1
1
  import { Message, Offset, Row } from "@electric-sql/client";
2
- //#region src/runtime/app/snapshot.d.ts
3
2
  export interface ShapeSnapshot<T> {
4
3
  rows: [string, T][];
5
4
  offset: Offset;
@@ -13,5 +12,4 @@ export interface ShapeSnapshot<T> {
13
12
  /** The rows of the shape at `url` (absolute) as of now, without subscribing. */
14
13
  export declare function loadSnapshot<T extends Row<unknown>>(url: string, fetchClient?: typeof fetch): Promise<ShapeSnapshot<T>>;
15
14
  /** Applies Electric change messages to `rows`. Returns whether anything changed. */
16
- export declare function applyMessages<T extends Row<unknown>>(rows: Map<string, T>, messages: Message<T>[]): boolean;
17
- //#endregion
15
+ export declare function applyMessages<T extends Row<unknown>>(rows: Map<string, T>, messages: Message<T>[]): boolean;
@@ -1,6 +1,6 @@
1
1
  import { shapeParser } from "../shared/shape.js";
2
2
  import { ShapeStream, isChangeMessage, isControlMessage } from "@electric-sql/client";
3
- //#region src/runtime/app/snapshot.ts
3
+ // src/runtime/app/snapshot.ts
4
4
  /** The rows of the shape at `url` (absolute) as of now, without subscribing. */
5
5
  async function loadSnapshot(url, fetchClient) {
6
6
  const rows = /* @__PURE__ */ new Map();
@@ -60,5 +60,4 @@ function applyMessages(rows, messages) {
60
60
  }
61
61
  return changed;
62
62
  }
63
- //#endregion
64
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`).
@@ -50,5 +49,4 @@ export declare function preloadShape<P extends keyof WervtShapeRoutes>(path: P,
50
49
  export declare function preloadShape(path: string, options?: {
51
50
  key?: string;
52
51
  }): Promise<void>;
53
- //#endregion
54
52
  export {};