@bezoar/nuxt 0.0.0-stage → 0.0.2

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.
@@ -0,0 +1,31 @@
1
+ /**
2
+ * The virtual modules the module adds to the server build: the build of the
3
+ * app, and its service. A virtual module is bundled in dev and in production
4
+ * alike, so the plugin of the runtime, which imports both, is too.
5
+ */
6
+ export declare const VIRTUAL: {
7
+ readonly build: "#bezoar/build";
8
+ readonly service: "#bezoar/service";
9
+ };
10
+ /**
11
+ * The declaration of the service, in the build directory. It declares the
12
+ * virtual service module, so the server is typed by the build.
13
+ */
14
+ export declare const DECLARATION = "types/bezoar.d.ts";
15
+ /**
16
+ * The modules of a kit build the module adopts.
17
+ */
18
+ export declare const ENTRIES: readonly ["index", "queries", "migrations", "seeds"];
19
+ /**
20
+ * The export of each module of a kit build, re-exported by the virtual
21
+ * build. Named, not `export *`: the modules are external to the server
22
+ * bundle in dev, and a bundler cannot tell which of several external modules
23
+ * a star re-export provides a name from.
24
+ */
25
+ export declare const EXPORTS: Record<(typeof ENTRIES)[number], string>;
26
+ /**
27
+ * The drivers the module can connect with. `pg` connects to a server with
28
+ * the connection string of the runtime config. `pglite` runs Postgres in the
29
+ * process, on the data directory of the runtime config, or in memory.
30
+ */
31
+ export declare const DRIVERS: readonly ["pg", "pglite"];
@@ -0,0 +1,62 @@
1
+ import { DRIVERS } from "./_chunks/constant.js";
2
+ /**
3
+ * A driver the module can connect with.
4
+ */
5
+ export type Driver = (typeof DRIVERS)[number];
6
+ /**
7
+ * The module's configuration.
8
+ *
9
+ * ```ts
10
+ * export default defineNuxtConfig({
11
+ * modules: ["@bezoar/nuxt"],
12
+ * bezoar: { build: "@acme/database", migrate: true },
13
+ * });
14
+ * ```
15
+ */
16
+ export interface NuxtBezoarConfig {
17
+ /**
18
+ * A kit build: the output directory `bezoar build` wrote. A path,
19
+ * relative to the project root or absolute, to the directory or its
20
+ * `index.mjs`, or a package whose main entry is that `index.mjs`.
21
+ */
22
+ build: string;
23
+ /**
24
+ * The driver. `pg` connects to a Postgres server with the connection
25
+ * string of the runtime config. `pglite` runs Postgres in the process.
26
+ * Defaults to `pg`.
27
+ */
28
+ driver?: Driver;
29
+ /**
30
+ * Whether the server applies the migrations of the build when it starts.
31
+ * Defaults to `false`.
32
+ */
33
+ migrate?: boolean;
34
+ /**
35
+ * Whether the server runs the seeds of the build when it starts, after
36
+ * the migrations. Defaults to `false`.
37
+ */
38
+ seed?: boolean;
39
+ }
40
+ /**
41
+ * The runtime config of the module, under `runtimeConfig.bezoar`. An
42
+ * environment variable sets each member: `NUXT_BEZOAR_URL` sets `url`.
43
+ */
44
+ export interface BezoarRuntimeConfig {
45
+ /** The connection string, for the `pg` driver. */
46
+ url: string;
47
+ /** The data directory, for the `pglite` driver. Empty for memory. */
48
+ dataDir: string;
49
+ /** The driver. */
50
+ driver: Driver;
51
+ /** Whether the server applies the migrations when it starts. */
52
+ migrate: boolean;
53
+ /** Whether the server runs the seeds when it starts. */
54
+ seed: boolean;
55
+ }
56
+ /**
57
+ * Identity helper that types a Nuxt bezoar configuration.
58
+ *
59
+ * @param config - The Nuxt bezoar configuration.
60
+ * @returns The same config.
61
+ */
62
+ export declare const defineBezoarConfig: (config: NuxtBezoarConfig) => NuxtBezoarConfig;
@@ -0,0 +1,2 @@
1
+ const defineBezoarConfig = (config) => config;
2
+ export { defineBezoarConfig };
@@ -0,0 +1,2 @@
1
+ import { DECLARATION, DRIVERS, ENTRIES, EXPORTS, VIRTUAL } from "./_chunks/constant.js";
2
+ export { DECLARATION, DRIVERS, ENTRIES, EXPORTS, VIRTUAL };
@@ -0,0 +1,19 @@
1
+ const VIRTUAL = {
2
+ build: "#bezoar/build",
3
+ service: "#bezoar/service"
4
+ };
5
+ const DECLARATION = "types/bezoar.d.ts";
6
+ const ENTRIES = [
7
+ "index",
8
+ "queries",
9
+ "migrations",
10
+ "seeds"
11
+ ];
12
+ const EXPORTS = {
13
+ index: "contract",
14
+ queries: "queries",
15
+ migrations: "migrations",
16
+ seeds: "seeds"
17
+ };
18
+ const DRIVERS = ["pg", "pglite"];
19
+ export { DECLARATION, DRIVERS, ENTRIES, EXPORTS, VIRTUAL };
@@ -0,0 +1,22 @@
1
+ import { NuxtBezoarConfig } from "./config.js";
2
+ /**
3
+ * Nuxt module for bezoar.
4
+ *
5
+ * The module takes a kit build, the output of `bezoar build` in a
6
+ * database package, and adds two virtual modules to the server build: the
7
+ * build of the app, which re-exports the modules of the kit build, and the
8
+ * service of the app, over the contract and the SQL of that build. A
9
+ * declaration in the build directory types the service by the contract. The
10
+ * module provides `useBezoar()` to the server from the virtual service: a
11
+ * service over the contract of the build, typed by it, connected through
12
+ * the driver and the connection string of the runtime config. The plugin of
13
+ * the runtime makes the service when the server starts, applies the
14
+ * migrations and runs the seeds of the virtual build when the config asks,
15
+ * and closes the connection when the server stops.
16
+ *
17
+ * The modules are virtual, not written, so Nitro bundles them in dev as in
18
+ * production. Nitro loads a written file through Node in dev, where neither
19
+ * `#imports` nor a virtual module exists.
20
+ */
21
+ declare const _default: import("@nuxt/schema").NuxtModule<NuxtBezoarConfig, NuxtBezoarConfig, false>;
22
+ export { _default as default };
@@ -0,0 +1,108 @@
1
+ import { DECLARATION, ENTRIES, EXPORTS, VIRTUAL } from "./constant.js";
2
+ import { dirname, isAbsolute, join, resolve } from "node:path";
3
+ import { addServerImports, addServerPlugin, addServerTemplate, addTypeTemplate, createResolver, defineNuxtModule, directoryToURL, tryResolveModule } from "@nuxt/kit";
4
+ import { record } from "objectively";
5
+ import { existsSync, statSync } from "node:fs";
6
+ import { fileURLToPath } from "node:url";
7
+ const locate = async (build, root) => {
8
+ let path;
9
+ if (build.startsWith(".") || isAbsolute(build)) path = resolve(root, build);
10
+ else {
11
+ const resolved = await tryResolveModule(build, directoryToURL(root));
12
+ path = resolved?.startsWith("file:") ? fileURLToPath(resolved) : resolved;
13
+ }
14
+ if (path === void 0 || !existsSync(path)) throw new Error(`bezoar: \`build\` ${JSON.stringify(build)} could not be found from ${root}`);
15
+ const directory = statSync(path).isFile() ? dirname(path) : path;
16
+ for (const entry of ENTRIES) if (!existsSync(join(directory, `${entry}.mjs`))) throw new Error(`bezoar: ${directory} is not a kit build; it has no ${entry}.mjs (run \`bezoar build\` there first)`);
17
+ return directory;
18
+ };
19
+ const from = (directory, file) => JSON.stringify(join(directory, file));
20
+ const build = ({ build: directory }) => [...ENTRIES.map((entry) => `export { ${EXPORTS[entry]} } from ${from(directory, `${entry}.mjs`)};`), ""].join("\n");
21
+ const service = ({ runtime }) => [
22
+ `import { contract, queries } from ${JSON.stringify(VIRTUAL.build)};`,
23
+ `import { defineService } from ${from(runtime, "service.js")};`,
24
+ "",
25
+ "export const { useBezoar, closeBezoar } = defineService(contract, queries);",
26
+ ""
27
+ ].join("\n");
28
+ const declaration = ({ build, runtime }) => {
29
+ const contract = `typeof import(${from(build, "index.mjs")}).contract`;
30
+ const service = `import(${from(runtime, "service.js")})`;
31
+ return [
32
+ `declare module ${JSON.stringify(VIRTUAL.service)} {`,
33
+ ` export type AppBezoarContract = ${contract};`,
34
+ ` export const useBezoar: ${service}.Defined<AppBezoarContract>["useBezoar"];`,
35
+ ` export const closeBezoar: ${service}.Defined<AppBezoarContract>["closeBezoar"];`,
36
+ " export type AppBezoar = ReturnType<typeof useBezoar>;",
37
+ ` export type AppBezoarQuery = ${service}.Query<AppBezoarContract>;`,
38
+ ` export type AppBezoarArguments<Q extends AppBezoarQuery> = ${service}.Arguments<AppBezoarContract, Q>;`,
39
+ ` export type AppBezoarRow<Q extends AppBezoarQuery> = ${service}.Row<AppBezoarContract, Q>;`,
40
+ "}",
41
+ ""
42
+ ].join("\n");
43
+ };
44
+ var module_default = defineNuxtModule({
45
+ meta: {
46
+ name: "bezoar",
47
+ configKey: "bezoar"
48
+ },
49
+ setup: async (options, nuxt) => {
50
+ if (typeof options.build !== "string" || options.build === "") throw new Error("bezoar: `build` must name a kit build, as a package or a path");
51
+ const resolver = createResolver(import.meta.url);
52
+ const sources = {
53
+ build: await locate(options.build, nuxt.options.rootDir),
54
+ runtime: dirname(await resolver.resolvePath("./runtime/server/service"))
55
+ };
56
+ nuxt.options.build.transpile.push(sources.runtime);
57
+ addServerTemplate({
58
+ filename: VIRTUAL.build,
59
+ getContents: () => build(sources)
60
+ });
61
+ addServerTemplate({
62
+ filename: VIRTUAL.service,
63
+ getContents: () => service(sources)
64
+ });
65
+ addTypeTemplate({
66
+ filename: DECLARATION,
67
+ getContents: () => declaration(sources)
68
+ }, {
69
+ nitro: true,
70
+ nuxt: true
71
+ });
72
+ const defaults = {
73
+ url: "",
74
+ dataDir: "",
75
+ driver: options.driver ?? "pg",
76
+ migrate: options.migrate ?? false,
77
+ seed: options.seed ?? false
78
+ };
79
+ const given = nuxt.options.runtimeConfig.bezoar;
80
+ nuxt.options.runtimeConfig.bezoar = {
81
+ ...defaults,
82
+ ...record(given) ? given : {}
83
+ };
84
+ addServerPlugin(await resolver.resolvePath("./runtime/server/plugin"));
85
+ addServerImports([
86
+ {
87
+ from: VIRTUAL.service,
88
+ name: "useBezoar"
89
+ },
90
+ {
91
+ from: VIRTUAL.service,
92
+ name: "closeBezoar"
93
+ },
94
+ ...[
95
+ "AppBezoar",
96
+ "AppBezoarContract",
97
+ "AppBezoarQuery",
98
+ "AppBezoarArguments",
99
+ "AppBezoarRow"
100
+ ].map((name) => ({
101
+ from: VIRTUAL.service,
102
+ name,
103
+ type: true
104
+ }))
105
+ ]);
106
+ }
107
+ });
108
+ export { module_default as default };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * The plugin of the app. Nitro resolves a plugin by path, so the plugin is a
3
+ * file of the runtime; what differs by app, the service and the build, comes
4
+ * from the virtual modules the module adds to the server build.
5
+ */
6
+ declare const _default: (nitroApp: import("#imports").NitroApp) => Promise<void>;
7
+ export { _default as default };
@@ -0,0 +1,10 @@
1
+ import { definePlugin } from "./startup.js";
2
+ import { migrations, seeds } from "#bezoar/build";
3
+ import { closeBezoar, useBezoar } from "#bezoar/service";
4
+ var plugin_default = definePlugin({
5
+ useBezoar,
6
+ closeBezoar,
7
+ migrations,
8
+ seeds
9
+ });
10
+ export { plugin_default as default };
@@ -0,0 +1,23 @@
1
+ import { Arguments, Bezoar, Bundle, Definition, Query, Row } from "bezoar";
2
+ /**
3
+ * The service of an app: `useBezoar` for the service, `closeBezoar`
4
+ * to close its connection.
5
+ */
6
+ export interface Defined<D extends Definition> {
7
+ useBezoar: () => Bezoar<D>;
8
+ closeBezoar: () => Promise<void>;
9
+ }
10
+ /**
11
+ * Defines the service of an app over a contract and its SQL. The written
12
+ * service of the build directory calls this with the modules of the build.
13
+ *
14
+ * `useBezoar` connects on its first call, with the driver of the runtime
15
+ * config, and makes the service. Every call after returns the same service.
16
+ * `closeBezoar` closes the connection. The next `useBezoar` connects
17
+ * again.
18
+ *
19
+ * @param contract - The contract of the build.
20
+ * @param queries - The SQL of the build.
21
+ */
22
+ export declare const defineService: <const D extends Definition>(contract: D, queries: Bundle<D>) => Defined<D>;
23
+ export type { Arguments, Query, Row };
@@ -0,0 +1,34 @@
1
+ import { useRuntimeConfig } from "#imports";
2
+ import { pg } from "@bezoar/pg";
3
+ import { pglite } from "@bezoar/pglite";
4
+ import { makeBezoar } from "bezoar";
5
+ const connect = (config) => {
6
+ if (config.driver === "pglite") return pglite(config.dataDir === "" ? {} : config.dataDir);
7
+ if (config.url === "") throw new Error("bezoar: no connection string; set NUXT_BEZOAR_URL, or runtimeConfig.bezoar.url");
8
+ return pg(config.url);
9
+ };
10
+ const defineService = (contract, queries) => {
11
+ let connection;
12
+ let service;
13
+ const useBezoar = () => {
14
+ if (service === void 0) {
15
+ connection = connect(useRuntimeConfig().bezoar);
16
+ service = makeBezoar(contract, {
17
+ executor: connection,
18
+ queries
19
+ });
20
+ }
21
+ return service;
22
+ };
23
+ const closeBezoar = async () => {
24
+ const open = connection;
25
+ connection = void 0;
26
+ service = void 0;
27
+ await open?.close();
28
+ };
29
+ return {
30
+ useBezoar,
31
+ closeBezoar
32
+ };
33
+ };
34
+ export { defineService };
@@ -0,0 +1,21 @@
1
+ import { Defined } from "./service.js";
2
+ import { Definition, Migration, Seed } from "bezoar";
3
+ /**
4
+ * What the plugin of an app runs with: its service, and the migrations and
5
+ * the seeds of its build.
6
+ */
7
+ export interface Startup<D extends Definition> extends Defined<D> {
8
+ migrations: readonly Migration[];
9
+ seeds: readonly Seed[];
10
+ }
11
+ /**
12
+ * Defines the plugin of an app. The plugin of the runtime calls this with
13
+ * the virtual service and the virtual build of the app.
14
+ *
15
+ * The plugin makes the service when the server starts. When the runtime
16
+ * config asks, it applies the migrations of the build and runs its seeds.
17
+ * It closes the connection when the server stops.
18
+ *
19
+ * @param startup - The service, the migrations, and the seeds.
20
+ */
21
+ export declare const definePlugin: <D extends Definition>(startup: Startup<D>) => (nitroApp: import("#imports").NitroApp) => Promise<void>;
@@ -0,0 +1,9 @@
1
+ import { defineNitroPlugin, useRuntimeConfig } from "#imports";
2
+ const definePlugin = (startup) => defineNitroPlugin(async (nitroApp) => {
3
+ const { migrate, seed } = useRuntimeConfig().bezoar;
4
+ const db = startup.useBezoar();
5
+ if (migrate) await db.migrate(startup.migrations);
6
+ if (seed) await db.seed(startup.seeds);
7
+ nitroApp.hooks.hook("close", startup.closeBezoar);
8
+ });
9
+ export { definePlugin };
@@ -0,0 +1,6 @@
1
+ import { BezoarRuntimeConfig } from "../../config.js";
2
+ declare module "nuxt/schema" {
3
+ interface RuntimeConfig {
4
+ bezoar: BezoarRuntimeConfig;
5
+ }
6
+ }
@@ -0,0 +1 @@
1
+ export {};
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Alexander Thorwaldson
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,89 @@
1
- # Temporary Holding Version
1
+ # @bezoar/nuxt
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Nuxt module for bezoar. The module takes a kit build and gives the server
4
+ a typed database service.
5
+
6
+ ## Install
7
+
8
+ ```sh
9
+ pnpm add @bezoar/nuxt
10
+ ```
11
+
12
+ ## Setup
13
+
14
+ ```ts
15
+ // nuxt.config.ts
16
+ export default defineNuxtConfig({
17
+ modules: ["@bezoar/nuxt"],
18
+ bezoar: { build: "@acme/database", migrate: true },
19
+ });
20
+ ```
21
+
22
+ `build` names a kit build: a package whose main entry is the `index.mjs`
23
+ that `bezoar build` wrote, or a path to that directory. The module adds
24
+ two virtual modules to the server build: `#bezoar/build`, which
25
+ re-exports the modules of the kit build, and `#bezoar/service`, the
26
+ service over its contract and its SQL. It writes a declaration of the
27
+ service into the build directory. `useBezoar()` and its types come from
28
+ the virtual service, so they are typed by the build. The modules are virtual
29
+ so that Nitro bundles them in dev as in production; the server plugin of
30
+ the module imports both.
31
+
32
+ ## Connection
33
+
34
+ The module reads `runtimeConfig.bezoar`. An environment variable sets
35
+ each member.
36
+
37
+ | Member | Variable | Meaning |
38
+ | --------- | ---------------------- | ------------------------------------------- |
39
+ | `url` | `NUXT_BEZOAR_URL` | The connection string, for the `pg` driver |
40
+ | `dataDir` | `NUXT_BEZOAR_DATA_DIR` | The data directory, for the `pglite` driver |
41
+ | `driver` | `NUXT_BEZOAR_DRIVER` | `pg` or `pglite`. Defaults to `pg` |
42
+ | `migrate` | `NUXT_BEZOAR_MIGRATE` | Apply the migrations when the server starts |
43
+ | `seed` | `NUXT_BEZOAR_SEED` | Run the seeds when the server starts |
44
+
45
+ `driver`, `migrate`, and `seed` can also be set in the module options. A
46
+ value in the runtime config wins.
47
+
48
+ The `pg` driver connects to a Postgres server and needs `url`. The `pglite`
49
+ driver runs Postgres in the process, on `dataDir`, or in memory when it is
50
+ empty. A server with the `pg` driver and no `url` fails to start.
51
+
52
+ ## In a server route
53
+
54
+ ```ts
55
+ export default defineEventHandler(async (event) => {
56
+ const db = useBezoar();
57
+ const { day } = await getValidatedQuery(
58
+ event,
59
+ db.schema.validators.queries["events.list"].arguments.parse,
60
+ );
61
+ return db.events.list({ day });
62
+ });
63
+ ```
64
+
65
+ `useBezoar()` is auto-imported on the server. It returns the same service
66
+ on every call, typed by the contract of the build, so `db.events.list`
67
+ completes, and its arguments and rows check. `AppBezoar`,
68
+ `AppBezoarContract`, `AppBezoarQuery`, `AppBezoarArguments<Q>`, and
69
+ `AppBezoarRow<Q>` are the types, also auto-imported.
70
+
71
+ ## Startup and shutdown
72
+
73
+ A server plugin makes the service when the server starts. With `migrate`,
74
+ it applies the migrations of the build first, through `migrate` of
75
+ `@bezoar/core`, under an advisory lock, so instances that start together
76
+ apply each migration once. With `seed`, it runs the seeds after. When the
77
+ server stops, the plugin closes the connection. `closeBezoar()` closes it
78
+ by hand.
79
+
80
+ ## Runtime
81
+
82
+ The `pg` driver needs a Node runtime, which the Node server presets of Nuxt
83
+ and the functions of Vercel are.
84
+
85
+ ## Related
86
+
87
+ - [`bezoar`](../../packages/bezoar): the runtime.
88
+ - [`@bezoar/kit`](../../packages/kit): builds the contract the module
89
+ adopts.
package/package.json CHANGED
@@ -1,6 +1,51 @@
1
1
  {
2
2
  "name": "@bezoar/nuxt",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.0.2",
4
+ "description": "Nuxt module for bezoar: a typed database service for server routes, from a kit build",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/zoobzio/bezoar.git",
9
+ "directory": "integrations/nuxt"
10
+ },
11
+ "type": "module",
12
+ "main": "./.dist/module.js",
13
+ "types": "./.dist/module.d.ts",
14
+ "exports": {
15
+ ".": {
16
+ "types": "./.dist/module.d.ts",
17
+ "import": "./.dist/module.js"
18
+ },
19
+ "./config": {
20
+ "types": "./.dist/config.d.ts",
21
+ "import": "./.dist/config.js"
22
+ },
23
+ "./constant": {
24
+ "types": "./.dist/constant.d.ts",
25
+ "import": "./.dist/constant.js"
26
+ }
27
+ },
28
+ "files": [
29
+ ".dist"
30
+ ],
31
+ "dependencies": {
32
+ "@nuxt/kit": "^4.5.0",
33
+ "objectively": "^0.3.0",
34
+ "@bezoar/pg": "0.0.2",
35
+ "@bezoar/pglite": "0.0.2",
36
+ "bezoar": "0.0.2"
37
+ },
38
+ "devDependencies": {
39
+ "@nuxt/nitro-server": "^4.5.0",
40
+ "@nuxt/schema": "^4.5.0",
41
+ "@types/node": "^25.9.9",
42
+ "nuxt": "^4.5.0"
43
+ },
44
+ "scripts": {
45
+ "build": "obuild",
46
+ "stub": "obuild --stub",
47
+ "lint": "oxlint",
48
+ "typecheck": "tsc --noEmit",
49
+ "test": "vitest run"
50
+ }
6
51
  }