@solcreek/adapter-creek 0.2.4 → 0.2.6
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.
package/dist/bundler.js
CHANGED
|
@@ -711,6 +711,13 @@ export async function bundleForWorkers(opts) {
|
|
|
711
711
|
// throws \`sharp is not a function\`. Aliasing to a shim whose default
|
|
712
712
|
// is undefined makes \`@vercel/og\` fall back to its resvg.wasm path.
|
|
713
713
|
"sharp": path.join(adapterDir, "src", "shims", "sharp.js"),
|
|
714
|
+
// better-sqlite3 is a native module that can't run on workerd. The DB
|
|
715
|
+
// driver swap (Drizzle/Prisma) ignores the local client and uses env.DB,
|
|
716
|
+
// so stub better-sqlite3 to keep its native .node out of the bundle.
|
|
717
|
+
// Aliased here (esbuild) because Next bundles its JS inline — the
|
|
718
|
+
// webpack-layer alias doesn't catch the already-resolved native require.
|
|
719
|
+
"better-sqlite3": path.join(adapterDir, "src", "shims", "better-sqlite3-stub.js"),
|
|
720
|
+
"node:better-sqlite3": path.join(adapterDir, "src", "shims", "better-sqlite3-stub.js"),
|
|
714
721
|
// Some packages intentionally leave unreachable dynamic imports such as
|
|
715
722
|
// `if (Math.random() < 0) import("fail")` in their ESM entry. Turbopack
|
|
716
723
|
// externalizes the package and never resolves that branch, but wrangler
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,23 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
3
4
|
import { copyFileSync, existsSync } from "node:fs";
|
|
4
5
|
import { applyBaseModifyConfig } from "@solcreek/adapter-next-core";
|
|
5
6
|
import { handleBuild } from "./build.js";
|
|
7
|
+
// Build-time DB driver swaps: in the Workers build only, redirect a local
|
|
8
|
+
// SQLite driver import to a Creek shim that backs the same ORM with the
|
|
9
|
+
// request's D1 binding (env.DB, resolved lazily). Local dev is untouched —
|
|
10
|
+
// the adapter only runs under the Creek build. Aliases are harmless when the
|
|
11
|
+
// module isn't imported, so they apply unconditionally.
|
|
12
|
+
const SHIMS_DIR = path.join(path.dirname(fileURLToPath(import.meta.url)), "..", "src", "shims");
|
|
13
|
+
const DB_DRIVER_ALIASES = {
|
|
14
|
+
// Drizzle: `drizzle-orm/better-sqlite3` → D1-backed drizzle. No WASM.
|
|
15
|
+
"drizzle-orm/better-sqlite3$": path.join(SHIMS_DIR, "drizzle-better-sqlite3.js"),
|
|
16
|
+
// The native better-sqlite3 client the user passes to drizzle() — stubbed,
|
|
17
|
+
// since the swap ignores it and uses env.DB. Keeps the native .node out of
|
|
18
|
+
// the Workers bundle.
|
|
19
|
+
"better-sqlite3$": path.join(SHIMS_DIR, "better-sqlite3-stub.js"),
|
|
20
|
+
};
|
|
6
21
|
// Path to the cache handler shipped by @solcreek/adapter-next-core, resolved
|
|
7
22
|
// from THIS module's location — not from the consumer project. The adapter is
|
|
8
23
|
// not always installed in the project's own node_modules: the Creek CLI
|
|
@@ -77,6 +92,16 @@ const adapter = {
|
|
|
77
92
|
...(baseConfig.experimental ?? {}),
|
|
78
93
|
maxPostponedStateSize: "20mb",
|
|
79
94
|
},
|
|
95
|
+
// Swap local SQLite ORM drivers → D1-backed shims (Workers build only).
|
|
96
|
+
// Compose with any webpack fn the base config already set.
|
|
97
|
+
webpack(webpackConfig, webpackCtx) {
|
|
98
|
+
const cfg = typeof baseConfig.webpack === "function"
|
|
99
|
+
? baseConfig.webpack(webpackConfig, webpackCtx)
|
|
100
|
+
: webpackConfig;
|
|
101
|
+
cfg.resolve = cfg.resolve ?? {};
|
|
102
|
+
cfg.resolve.alias = { ...(cfg.resolve.alias ?? {}), ...DB_DRIVER_ALIASES };
|
|
103
|
+
return cfg;
|
|
104
|
+
},
|
|
80
105
|
};
|
|
81
106
|
},
|
|
82
107
|
async onBuildComplete(ctx) {
|
package/dist/worker-entry.js
CHANGED
|
@@ -1973,6 +1973,14 @@ function __creekCurrentEnv() {
|
|
|
1973
1973
|
}
|
|
1974
1974
|
}
|
|
1975
1975
|
|
|
1976
|
+
// Expose the current request's Cloudflare env (incl. bindings like DB) to
|
|
1977
|
+
// bundled user code and the build-time DB driver-swap shims (Drizzle/Prisma),
|
|
1978
|
+
// which resolve env.DB lazily at query time. AsyncLocalStorage-backed, so it
|
|
1979
|
+
// is request-safe under concurrency.
|
|
1980
|
+
if (typeof globalThis !== "undefined") {
|
|
1981
|
+
globalThis.__creekEnv = __creekCurrentEnv;
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1976
1984
|
// Group tags into {shardKey: tags[]} so we hit each DO instance at most
|
|
1977
1985
|
// once per operation (avoids fan-out + 2x/3x RPC cost for entries that
|
|
1978
1986
|
// carry many tags).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solcreek/adapter-creek",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.6",
|
|
4
4
|
"description": "Next.js deployment adapter for Creek (Cloudflare Workers)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@next/routing": "16.2.3",
|
|
48
|
+
"@node-rs/xxhash": "^1.7.6",
|
|
48
49
|
"@solcreek/adapter-core": "^0.2.0",
|
|
49
50
|
"@solcreek/adapter-next-core": "^0.1.1",
|
|
50
51
|
"sql.js": "^1.14.1",
|
|
@@ -54,7 +55,6 @@
|
|
|
54
55
|
"next": ">=16.2.3"
|
|
55
56
|
},
|
|
56
57
|
"devDependencies": {
|
|
57
|
-
"@node-rs/xxhash": "^1.7.6",
|
|
58
58
|
"@types/node": "^22.13.10",
|
|
59
59
|
"miniflare": "^4.20260410.0",
|
|
60
60
|
"next": "16.2.3",
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Creek: better-sqlite3 is a native module that cannot run on Cloudflare
|
|
2
|
+
// Workers. The Drizzle/Prisma driver-swap shims ignore the local client and
|
|
3
|
+
// use the D1 binding instead, so the actual better-sqlite3 instance is never
|
|
4
|
+
// used on Workers — stub it to a harmless no-op so the bundle builds. Local
|
|
5
|
+
// dev keeps the real better-sqlite3.
|
|
6
|
+
class Database {
|
|
7
|
+
constructor() {}
|
|
8
|
+
prepare() { throw new Error("[creek] better-sqlite3 is not available on Workers; queries run on D1 via the driver swap."); }
|
|
9
|
+
exec() {}
|
|
10
|
+
close() {}
|
|
11
|
+
pragma() {}
|
|
12
|
+
}
|
|
13
|
+
export default Database;
|
|
14
|
+
export { Database };
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
import adapter from "../index.js";
|
|
6
|
+
|
|
7
|
+
// The drizzle shim imports `drizzle-orm/d1`, which is resolved from the user's
|
|
8
|
+
// project at build time (drizzle-orm is not an adapter-creek dependency). Mock
|
|
9
|
+
// it so the swap logic can be unit-tested in isolation: the mock drizzle()
|
|
10
|
+
// just records the client + config it was handed.
|
|
11
|
+
vi.mock("drizzle-orm/d1", () => ({
|
|
12
|
+
drizzle: (client: unknown, config: unknown) => ({ __d1Client: client, __config: config }),
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
const SHIMS_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
|
|
17
|
+
afterEach(() => {
|
|
18
|
+
delete (globalThis as { __creekEnv?: unknown }).__creekEnv;
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe("DB driver-swap aliases (modifyConfig)", () => {
|
|
22
|
+
it("aliases the local SQLite drivers to Creek shims in the Workers build", () => {
|
|
23
|
+
const config = adapter.modifyConfig?.(
|
|
24
|
+
{},
|
|
25
|
+
{ phase: "phase-production-build" } as never,
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
// The adapter exposes webpack(cfg, ctx); applying it surfaces the aliases.
|
|
29
|
+
const webpack = (config as { webpack?: (c: unknown, x: unknown) => any }).webpack;
|
|
30
|
+
expect(typeof webpack).toBe("function");
|
|
31
|
+
|
|
32
|
+
const built = webpack!({ resolve: {} }, {});
|
|
33
|
+
const alias = built.resolve.alias as Record<string, string>;
|
|
34
|
+
|
|
35
|
+
expect(alias["drizzle-orm/better-sqlite3$"]).toBe(
|
|
36
|
+
path.join(SHIMS_DIR, "drizzle-better-sqlite3.js"),
|
|
37
|
+
);
|
|
38
|
+
expect(alias["better-sqlite3$"]).toBe(
|
|
39
|
+
path.join(SHIMS_DIR, "better-sqlite3-stub.js"),
|
|
40
|
+
);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("preserves a base webpack fn while adding the DB aliases", () => {
|
|
44
|
+
// modifyConfig composes its webpack on top of whatever the base set; verify
|
|
45
|
+
// a pre-existing alias survives alongside the swap aliases.
|
|
46
|
+
const config = adapter.modifyConfig?.(
|
|
47
|
+
{},
|
|
48
|
+
{ phase: "phase-production-build" } as never,
|
|
49
|
+
);
|
|
50
|
+
const webpack = (config as { webpack?: (c: unknown, x: unknown) => any }).webpack;
|
|
51
|
+
const built = webpack!({ resolve: { alias: { "pre-existing": "/x" } } }, {});
|
|
52
|
+
const alias = built.resolve.alias as Record<string, string>;
|
|
53
|
+
expect(alias["pre-existing"]).toBe("/x");
|
|
54
|
+
expect(alias["drizzle-orm/better-sqlite3$"]).toContain("drizzle-better-sqlite3.js");
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("does not add the aliases outside the production build phase", () => {
|
|
58
|
+
const config = adapter.modifyConfig?.({}, { phase: "phase-development-server" } as never);
|
|
59
|
+
// Dev passthrough should not install the Workers-only webpack swap.
|
|
60
|
+
expect((config as { webpack?: unknown }).webpack).toBeUndefined();
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe("drizzle-better-sqlite3 shim", () => {
|
|
65
|
+
it("backs drizzle with env.DB (resolved lazily) and ignores the local client", async () => {
|
|
66
|
+
const { drizzle } = await import("./drizzle-better-sqlite3.js");
|
|
67
|
+
|
|
68
|
+
const fakeD1 = { __isD1: true, prepare: () => "stmt" };
|
|
69
|
+
(globalThis as { __creekEnv?: () => unknown }).__creekEnv = () => ({ DB: fakeD1 });
|
|
70
|
+
|
|
71
|
+
const localClient = { native: true };
|
|
72
|
+
const db = drizzle(localClient as never, { schema: {} }) as {
|
|
73
|
+
__d1Client: Record<string, unknown>;
|
|
74
|
+
__config: unknown;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// The user's local better-sqlite3 client is ignored; config flows through.
|
|
78
|
+
expect(db.__config).toEqual({ schema: {} });
|
|
79
|
+
// The D1 client is a lazy proxy that resolves env.DB per property access.
|
|
80
|
+
expect((db.__d1Client as { __isD1: boolean }).__isD1).toBe(true);
|
|
81
|
+
expect(typeof db.__d1Client.prepare).toBe("function");
|
|
82
|
+
expect((db.__d1Client.prepare as () => string)()).toBe("stmt");
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("throws a helpful error when env.DB is unavailable", async () => {
|
|
86
|
+
const { drizzle } = await import("./drizzle-better-sqlite3.js");
|
|
87
|
+
(globalThis as { __creekEnv?: () => unknown }).__creekEnv = () => ({});
|
|
88
|
+
const db = drizzle({} as never, {}) as { __d1Client: Record<string, unknown> };
|
|
89
|
+
expect(() => db.__d1Client.prepare).toThrow(/D1 binding `env\.DB` is unavailable/);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("resolves env per access (request-scoped), not once at construction", async () => {
|
|
93
|
+
const { drizzle } = await import("./drizzle-better-sqlite3.js");
|
|
94
|
+
let current: { DB?: unknown } = {};
|
|
95
|
+
(globalThis as { __creekEnv?: () => unknown }).__creekEnv = () => current;
|
|
96
|
+
const db = drizzle({} as never, {}) as { __d1Client: Record<string, unknown> };
|
|
97
|
+
|
|
98
|
+
// No binding yet → throws.
|
|
99
|
+
expect(() => db.__d1Client.prepare).toThrow();
|
|
100
|
+
// Binding appears on a later request → resolves without rebuilding.
|
|
101
|
+
current = { DB: { tag: "req-2", prepare: () => "ok" } };
|
|
102
|
+
expect((db.__d1Client.prepare as () => string)()).toBe("ok");
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
describe("better-sqlite3 stub", () => {
|
|
107
|
+
it("constructs but refuses queries (swap uses D1 instead)", async () => {
|
|
108
|
+
const { default: Database } = await import("./better-sqlite3-stub.js");
|
|
109
|
+
const dbInst = new Database();
|
|
110
|
+
expect(dbInst).toBeInstanceOf(Database);
|
|
111
|
+
expect(() => dbInst.prepare()).toThrow(/not available on Workers/);
|
|
112
|
+
// Lifecycle no-ops don't throw.
|
|
113
|
+
expect(() => dbInst.exec()).not.toThrow();
|
|
114
|
+
expect(() => dbInst.close()).not.toThrow();
|
|
115
|
+
});
|
|
116
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Creek build-time swap for `drizzle-orm/better-sqlite3` → Cloudflare D1.
|
|
2
|
+
//
|
|
3
|
+
// Only active in the Creek/Workers build (aliased by adapter-creek's
|
|
4
|
+
// modifyConfig); local dev keeps the real better-sqlite3 driver. The user's
|
|
5
|
+
// `drizzle(new Database(...), config)` call is preserved verbatim — we ignore
|
|
6
|
+
// the local client and back the same Drizzle instance with the request's D1
|
|
7
|
+
// binding instead, resolved lazily so it works at module scope (env.DB is
|
|
8
|
+
// only available per-request via AsyncLocalStorage).
|
|
9
|
+
import { drizzle as drizzleD1 } from "drizzle-orm/d1";
|
|
10
|
+
|
|
11
|
+
const lazyD1 = new Proxy(
|
|
12
|
+
{},
|
|
13
|
+
{
|
|
14
|
+
get(_t, prop) {
|
|
15
|
+
const env = (globalThis.__creekEnv && globalThis.__creekEnv()) || {};
|
|
16
|
+
const db = env.DB;
|
|
17
|
+
if (!db) {
|
|
18
|
+
throw new Error(
|
|
19
|
+
"[creek] D1 binding `env.DB` is unavailable. Add `database = true` under [resources] in creek.toml.",
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
const v = db[prop];
|
|
23
|
+
return typeof v === "function" ? v.bind(db) : v;
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
export function drizzle(_client, config) {
|
|
29
|
+
// _client is the user's better-sqlite3 instance — ignored on Workers.
|
|
30
|
+
return drizzleD1(lazyD1, config);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export default drizzle;
|