@deox/drizzle-d1-utils 0.0.1

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/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-present Deo Kumar
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/dist/index.cjs ADDED
@@ -0,0 +1,160 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.src || new URL("main.js", document.baseURI).href;
2
+ var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
3
+ var _nodechild_process = require('node:child_process');
4
+ var _nodecrypto = require('node:crypto');
5
+ var _nodefs = require('node:fs');
6
+ var _nodemodule = require('node:module');
7
+ var _nodepath = require('node:path');
8
+ var _cli = require('wrangler/wrangler-dist/cli');
9
+ function durableObjectNamespaceIdFromName(uniqueKey, name) {
10
+ const key = _nodecrypto.createHash.call(void 0, "sha256").update(uniqueKey).digest();
11
+ const nameHmac = _nodecrypto.createHmac.call(void 0, "sha256", key).update(name).digest().subarray(0, 16);
12
+ const hmac = _nodecrypto.createHmac.call(void 0, "sha256", key).update(nameHmac).digest().subarray(0, 16);
13
+ return Buffer.concat([nameHmac, hmac]).toString("hex");
14
+ }
15
+ function getD1BindingInfo({
16
+ binding,
17
+ environment,
18
+ persistTo,
19
+ configPath: wranglerConfigPath
20
+ } = {}) {
21
+ const { d1_databases, configPath } = _cli.unstable_readConfig.call(void 0, { env: environment, config: wranglerConfigPath });
22
+ if (d1_databases.length === 0) {
23
+ throw new Error("No D1 binding exists in the config");
24
+ }
25
+ if (d1_databases.length > 1 && !binding) {
26
+ throw new Error("Argument 'binding' is required when more than one D1 bindings exist in the config");
27
+ }
28
+ let bindingConfig;
29
+ if (binding) {
30
+ bindingConfig = d1_databases.find((d1) => d1.binding === binding);
31
+ if (!bindingConfig) {
32
+ throw new Error(`Could not find D1 binding '${binding}' in config`);
33
+ }
34
+ } else {
35
+ bindingConfig = d1_databases[0];
36
+ }
37
+ if (!bindingConfig.database_id && !bindingConfig.preview_database_id) {
38
+ throw new Error(`Neither 'database_id' nor 'preview_database_id' is set for D1 binding '${bindingConfig.binding}'`);
39
+ }
40
+ const wranglerConfigDir = configPath ? _nodepath.dirname.call(void 0, configPath) : void 0;
41
+ const wranglerStateDir = _nullishCoalesce(persistTo, () => ( _nodepath.relative.call(void 0, ".", _nodepath.join.call(void 0, _nullishCoalesce(wranglerConfigDir, () => ( "")), ".wrangler/state/v3"))));
42
+ const [database, previewDatabase] = [bindingConfig.database_id, bindingConfig.preview_database_id].map((databaseId) => {
43
+ if (!databaseId) {
44
+ return null;
45
+ }
46
+ const uniqueKey = "miniflare-D1DatabaseObject";
47
+ const miniflarePath = `${wranglerStateDir}/d1/${uniqueKey}`;
48
+ const hash = durableObjectNamespaceIdFromName(uniqueKey, databaseId);
49
+ const filename = _nodepath.join.call(void 0, miniflarePath, `${hash}.sqlite`);
50
+ return {
51
+ id: databaseId,
52
+ filename,
53
+ exists: _nodefs.existsSync.call(void 0, filename)
54
+ };
55
+ });
56
+ return {
57
+ configPath,
58
+ binding: bindingConfig.binding,
59
+ databaseName: bindingConfig.database_name,
60
+ database,
61
+ previewDatabase
62
+ };
63
+ }
64
+ function drizzleD1Config(config, options = {}) {
65
+ if (options.remote) {
66
+ const requiredOptions = ["accountId", "apiToken"];
67
+ const missingOptions = requiredOptions.filter((name) => !options[name]);
68
+ if (missingOptions.length > 0) {
69
+ throw new Error(`Options ${requiredOptions.join(", ")} are required when using remote database. Missing: ${missingOptions.join(", ")}`);
70
+ }
71
+ }
72
+ const binding = getD1BindingInfo({
73
+ binding: options.binding,
74
+ environment: options.environment,
75
+ configPath: options.configPath,
76
+ persistTo: options.persistTo
77
+ });
78
+ const database = options.preview ? binding.previewDatabase : binding.database;
79
+ if (!database) {
80
+ throw new Error(`'${options.preview ? "preview_database_id" : "database_id"} is not set for D1 binding '${binding.binding}'`);
81
+ }
82
+ if (!options.remote && !database.exists) {
83
+ if (binding.databaseName) {
84
+ let bin;
85
+ try {
86
+ bin = _nodemodule.createRequire.call(void 0, importMetaUrl).resolve("wrangler/bin/wrangler");
87
+ } catch (_) {
88
+ }
89
+ if (bin) {
90
+ const args = ["d1", "execute", binding.databaseName, "--command='SELECT 1'", "--local"];
91
+ if (options.environment) {
92
+ args.push(`--env=${options.environment}`);
93
+ }
94
+ if (options.configPath) {
95
+ args.push(`--config=${options.configPath}`);
96
+ }
97
+ if (options.persistTo) {
98
+ args.push(`--persist-to=${options.persistTo}`);
99
+ }
100
+ const command = ["wrangler", ...args].join(" ");
101
+ console.log(`[!] SQLite file for D1 binding '${binding.binding}' does not exist. Trying to create one by executing the following command:`);
102
+ console.log(` ${command}`);
103
+ try {
104
+ _nodechild_process.execFileSync.call(void 0, bin, args, {
105
+ stdio: "ignore"
106
+ });
107
+ } catch (_) {
108
+ }
109
+ if (_nodefs.existsSync.call(void 0, database.filename)) {
110
+ database.exists = true;
111
+ console.log("[\u2713] Command success! SQLite file has been successfully created.");
112
+ } else {
113
+ console.log("[\xD7] Command failed! Unable to create SQLite file.");
114
+ process.exit(1);
115
+ }
116
+ }
117
+ }
118
+ if (!database.exists) {
119
+ throw new Error(`Could not find SQLite file for D1 binding '${binding.binding}'`);
120
+ }
121
+ }
122
+ console.log("----------------------------------------------------------");
123
+ console.log(" Using following D1 Database:");
124
+ console.log("----------------------------------------------------------");
125
+ console.log(` Binding : ${binding.binding}`);
126
+ console.log(` Database name : ${_nullishCoalesce(binding.databaseName, () => ( "(not set)"))}`);
127
+ console.log(` Database Id : ${database.id}`);
128
+ if (options.remote) {
129
+ console.log(" Mode : REMOTE");
130
+ console.log(" (using remote Cloudflare D1 database)");
131
+ } else {
132
+ console.log(" Mode : LOCAL");
133
+ console.log(" (using local SQLite database)");
134
+ console.log(` SQLite file : ${database.filename}`);
135
+ }
136
+ console.log("----------------------------------------------------------");
137
+ process.on("exit", () => {
138
+ console.log("");
139
+ });
140
+ return {
141
+ ...config,
142
+ dialect: "sqlite",
143
+ ...options.remote ? {
144
+ driver: "d1-http",
145
+ dbCredentials: {
146
+ databaseId: database.id,
147
+ accountId: options.accountId,
148
+ token: options.apiToken
149
+ }
150
+ } : {
151
+ dbCredentials: {
152
+ url: `file:${database.filename}`
153
+ }
154
+ }
155
+ };
156
+ }
157
+
158
+
159
+ exports.drizzleD1Config = drizzleD1Config;
160
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../node_modules/.pnpm/tsup@8.5.0_@microsoft+api-extractor@7.47.6_@types+node@24.5.2__postcss@8.4.38_tsx@4.20.5_typescript@5.9.2_yaml@2.4.2/node_modules/tsup/assets/cjs_shims.js","../src/index.ts","/home/runner/work/deox/deox/packages/drizzle-d1-utils/dist/index.cjs"],"names":[],"mappings":"AAKA,iLAAM,iBAAA,EAAmB,CAAA,EAAA,GACvB,OAAO,SAAA,IAAa,YAAA,EAChB,IAAI,GAAA,CAAI,CAAA,KAAA,EAAQ,UAAU,CAAA,CAAA;AAI8B;ACXjC;AACU;AACZ;AACG;AACU;AAEJ;AAEuC;AAUf,EAAA;AACqB,EAAA;AACA,EAAA;AAC1B,EAAA;AACvD;AAE0B;AACxB,EAAA;AACA,EAAA;AACA,EAAA;AACY,EAAA;AAMN;AAC+E,EAAA;AACtD,EAAA;AACuB,IAAA;AACtD,EAAA;AACyC,EAAA;AACvB,IAAA;AAClB,EAAA;AACI,EAAA;AACS,EAAA;AACqD,IAAA;AAC5C,IAAA;AACgD,MAAA;AACpE,IAAA;AACK,EAAA;AACyB,IAAA;AAChC,EAAA;AAEsE,EAAA;AACpD,IAAA;AAClB,EAAA;AAE6D,EAAA;AACqB,EAAA;AAEJ,EAAA;AAC3D,IAAA;AACR,MAAA;AACT,IAAA;AACkB,IAAA;AACuC,IAAA;AACU,IAAA;AACd,IAAA;AAC9C,IAAA;AACD,MAAA;AACJ,MAAA;AAC2B,MAAA;AAC7B,IAAA;AACD,EAAA;AAEM,EAAA;AACL,IAAA;AACuB,IAAA;AACK,IAAA;AAC5B,IAAA;AACA,IAAA;AACF,EAAA;AACF;AAasI;AAChH,EAAA;AAC0D,IAAA;AACN,IAAA;AACvC,IAAA;AACwB,MAAA;AACvD,IAAA;AACF,EAAA;AAEiC,EAAA;AACd,IAAA;AACI,IAAA;AACD,IAAA;AACD,IAAA;AACpB,EAAA;AAEoE,EAAA;AAEtD,EAAA;AAC8D,IAAA;AAC7E,EAAA;AAEyC,EAAA;AACb,IAAA;AACpB,MAAA;AACA,MAAA;AACkE,QAAA;AAC1D,MAAA;AAAC,MAAA;AAEJ,MAAA;AACgF,QAAA;AAC9D,QAAA;AACiB,UAAA;AAC1C,QAAA;AACwB,QAAA;AACoB,UAAA;AAC5C,QAAA;AACuB,QAAA;AACwB,UAAA;AAC/C,QAAA;AAE8C,QAAA;AACgB,QAAA;AAClC,QAAA;AAExB,QAAA;AACsB,UAAA;AACf,YAAA;AACR,UAAA;AACS,QAAA;AAAC,QAAA;AAEsB,QAAA;AACf,UAAA;AAEN,UAAA;AACP,QAAA;AAC0D,UAAA;AACjD,UAAA;AAChB,QAAA;AACF,MAAA;AACF,IAAA;AACsB,IAAA;AAC4D,MAAA;AAClF,IAAA;AACF,EAAA;AAEwE,EAAA;AAC5B,EAAA;AAC4B,EAAA;AACrB,EAAA;AACoB,EAAA;AACxB,EAAA;AAC3B,EAAA;AACqB,IAAA;AAC+B,IAAA;AACjE,EAAA;AACiC,IAAA;AACwB,IAAA;AACT,IAAA;AACvD,EAAA;AACwE,EAAA;AAE/C,EAAA;AACT,IAAA;AACf,EAAA;AAEM,EAAA;AACF,IAAA;AACM,IAAA;AAEL,IAAA;AACU,MAAA;AACO,MAAA;AACQ,QAAA;AACF,QAAA;AACJ,QAAA;AACjB,MAAA;AAEF,IAAA;AACiB,MAAA;AACiB,QAAA;AAChC,MAAA;AACF,IAAA;AACN,EAAA;AACF;AC1C0F;AACA;AACA","file":"/home/runner/work/deox/deox/packages/drizzle-d1-utils/dist/index.cjs","sourcesContent":["// Shim globals in cjs bundle\n// There's a weird bug that esbuild will always inject importMetaUrl\n// if we export it as `const importMetaUrl = ... __filename ...`\n// But using a function will not cause this issue\n\nconst getImportMetaUrl = () =>\n typeof document === 'undefined'\n ? new URL(`file:${__filename}`).href\n : (document.currentScript && document.currentScript.src) ||\n new URL('main.js', document.baseURI).href\n\nexport const importMetaUrl = /* @__PURE__ */ getImportMetaUrl()\n","import { execFileSync } from 'node:child_process';\nimport { createHash, createHmac } from 'node:crypto';\nimport { existsSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { dirname, join, relative } from 'node:path';\nimport type { Config } from 'drizzle-kit';\nimport { unstable_readConfig } from 'wrangler/wrangler-dist/cli';\n\nfunction durableObjectNamespaceIdFromName(uniqueKey: string, name: string) {\n /**\n * In v3.2, miniflare uses durable object to implement D1 and hashes the local sqlite filename.\n *\n * See the following for more context:\n * https://github.com/cloudflare/workers-sdk/issues/4548 (understand the hash of the local D1 filename)\n * https://github.com/cloudflare/miniflare/releases/tag/v3.20230918.0\n *\n * This function is copied from these links\n */\n const key = createHash('sha256').update(uniqueKey).digest();\n const nameHmac = createHmac('sha256', key).update(name).digest().subarray(0, 16);\n const hmac = createHmac('sha256', key).update(nameHmac).digest().subarray(0, 16);\n return Buffer.concat([nameHmac, hmac]).toString('hex');\n}\n\nfunction getD1BindingInfo({\n binding,\n environment,\n persistTo,\n configPath: wranglerConfigPath,\n}: {\n binding?: string;\n configPath?: string;\n persistTo?: string;\n environment?: string;\n} = {}) {\n const { d1_databases, configPath } = unstable_readConfig({ env: environment, config: wranglerConfigPath });\n if (d1_databases.length === 0) {\n throw new Error('No D1 binding exists in the config');\n }\n if (d1_databases.length > 1 && !binding) {\n throw new Error(\"Argument 'binding' is required when more than one D1 bindings exist in the config\");\n }\n let bindingConfig: (typeof d1_databases)[number] | undefined;\n if (binding) {\n bindingConfig = d1_databases.find((d1) => d1.binding === binding);\n if (!bindingConfig) {\n throw new Error(`Could not find D1 binding '${binding}' in config`);\n }\n } else {\n bindingConfig = d1_databases[0];\n }\n\n if (!bindingConfig.database_id && !bindingConfig.preview_database_id) {\n throw new Error(`Neither 'database_id' nor 'preview_database_id' is set for D1 binding '${bindingConfig.binding}'`);\n }\n\n const wranglerConfigDir = configPath ? dirname(configPath) : undefined;\n const wranglerStateDir = persistTo ?? relative('.', join(wranglerConfigDir ?? '', '.wrangler/state/v3'));\n\n const [database, previewDatabase] = [bindingConfig.database_id, bindingConfig.preview_database_id].map((databaseId) => {\n if (!databaseId) {\n return null;\n }\n const uniqueKey = 'miniflare-D1DatabaseObject';\n const miniflarePath = `${wranglerStateDir}/d1/${uniqueKey}`;\n const hash = durableObjectNamespaceIdFromName(uniqueKey, databaseId);\n const filename = join(miniflarePath, `${hash}.sqlite`);\n return {\n id: databaseId,\n filename,\n exists: existsSync(filename),\n };\n });\n\n return {\n configPath,\n binding: bindingConfig.binding,\n databaseName: bindingConfig.database_name,\n database,\n previewDatabase,\n };\n}\n\nexport interface DrizzleD1Options {\n configPath?: string;\n persistTo?: string;\n environment?: string;\n binding?: string;\n remote?: boolean;\n preview?: boolean;\n accountId?: string;\n apiToken?: string;\n}\n\nexport function drizzleD1Config(config: Omit<Config, 'dialect' | 'driver' | 'dbCredentials'>, options: DrizzleD1Options = {}): Config {\n if (options.remote) {\n const requiredOptions: (keyof DrizzleD1Options)[] = ['accountId', 'apiToken'];\n const missingOptions = requiredOptions.filter((name) => !options[name]);\n if (missingOptions.length > 0) {\n throw new Error(`Options ${requiredOptions.join(', ')} are required when using remote database. Missing: ${missingOptions.join(', ')}`);\n }\n }\n\n const binding = getD1BindingInfo({\n binding: options.binding,\n environment: options.environment,\n configPath: options.configPath,\n persistTo: options.persistTo,\n });\n\n const database = options.preview ? binding.previewDatabase : binding.database;\n\n if (!database) {\n throw new Error(`'${options.preview ? 'preview_database_id' : 'database_id'} is not set for D1 binding '${binding.binding}'`);\n }\n\n if (!options.remote && !database.exists) {\n if (binding.databaseName) {\n let bin: string | undefined;\n try {\n bin = createRequire(import.meta.url).resolve('wrangler/bin/wrangler');\n } catch (_) {}\n\n if (bin) {\n const args: string[] = ['d1', 'execute', binding.databaseName, \"--command='SELECT 1'\", '--local'];\n if (options.environment) {\n args.push(`--env=${options.environment}`);\n }\n if (options.configPath) {\n args.push(`--config=${options.configPath}`);\n }\n if (options.persistTo) {\n args.push(`--persist-to=${options.persistTo}`);\n }\n\n const command = ['wrangler', ...args].join(' ');\n console.log(`[!] SQLite file for D1 binding '${binding.binding}' does not exist. Trying to create one by executing the following command:`);\n console.log(` ${command}`);\n\n try {\n execFileSync(bin, args, {\n stdio: 'ignore',\n });\n } catch (_) {}\n\n if (existsSync(database.filename)) {\n database.exists = true;\n\n console.log('[✓] Command success! SQLite file has been successfully created.');\n } else {\n console.log('[×] Command failed! Unable to create SQLite file.');\n process.exit(1);\n }\n }\n }\n if (!database.exists) {\n throw new Error(`Could not find SQLite file for D1 binding '${binding.binding}'`);\n }\n }\n\n console.log('----------------------------------------------------------');\n console.log(' Using following D1 Database:');\n console.log('----------------------------------------------------------');\n console.log(` Binding : ${binding.binding}`);\n console.log(` Database name : ${binding.databaseName ?? '(not set)'}`);\n console.log(` Database Id : ${database.id}`);\n if (options.remote) {\n console.log(' Mode : REMOTE');\n console.log(' (using remote Cloudflare D1 database)');\n } else {\n console.log(' Mode : LOCAL');\n console.log(' (using local SQLite database)');\n console.log(` SQLite file : ${database.filename}`);\n }\n console.log('----------------------------------------------------------');\n\n process.on('exit', () => {\n console.log('');\n });\n\n return {\n ...config,\n dialect: 'sqlite',\n ...(options.remote\n ? {\n driver: 'd1-http',\n dbCredentials: {\n databaseId: database.id,\n accountId: options.accountId,\n token: options.apiToken,\n },\n }\n : {\n dbCredentials: {\n url: `file:${database.filename}`,\n },\n }),\n };\n}\n",null]}
@@ -0,0 +1,15 @@
1
+ import { Config } from 'drizzle-kit';
2
+
3
+ interface DrizzleD1Options {
4
+ configPath?: string;
5
+ persistTo?: string;
6
+ environment?: string;
7
+ binding?: string;
8
+ remote?: boolean;
9
+ preview?: boolean;
10
+ accountId?: string;
11
+ apiToken?: string;
12
+ }
13
+ declare function drizzleD1Config(config: Omit<Config, 'dialect' | 'driver' | 'dbCredentials'>, options?: DrizzleD1Options): Config;
14
+
15
+ export { type DrizzleD1Options, drizzleD1Config };
@@ -0,0 +1,15 @@
1
+ import { Config } from 'drizzle-kit';
2
+
3
+ interface DrizzleD1Options {
4
+ configPath?: string;
5
+ persistTo?: string;
6
+ environment?: string;
7
+ binding?: string;
8
+ remote?: boolean;
9
+ preview?: boolean;
10
+ accountId?: string;
11
+ apiToken?: string;
12
+ }
13
+ declare function drizzleD1Config(config: Omit<Config, 'dialect' | 'driver' | 'dbCredentials'>, options?: DrizzleD1Options): Config;
14
+
15
+ export { type DrizzleD1Options, drizzleD1Config };
package/dist/index.js ADDED
@@ -0,0 +1,158 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { createHash, createHmac } from "node:crypto";
3
+ import { existsSync } from "node:fs";
4
+ import { createRequire } from "node:module";
5
+ import { dirname, join, relative } from "node:path";
6
+ import { unstable_readConfig } from "wrangler/wrangler-dist/cli";
7
+ function durableObjectNamespaceIdFromName(uniqueKey, name) {
8
+ const key = createHash("sha256").update(uniqueKey).digest();
9
+ const nameHmac = createHmac("sha256", key).update(name).digest().subarray(0, 16);
10
+ const hmac = createHmac("sha256", key).update(nameHmac).digest().subarray(0, 16);
11
+ return Buffer.concat([nameHmac, hmac]).toString("hex");
12
+ }
13
+ function getD1BindingInfo({
14
+ binding,
15
+ environment,
16
+ persistTo,
17
+ configPath: wranglerConfigPath
18
+ } = {}) {
19
+ const { d1_databases, configPath } = unstable_readConfig({ env: environment, config: wranglerConfigPath });
20
+ if (d1_databases.length === 0) {
21
+ throw new Error("No D1 binding exists in the config");
22
+ }
23
+ if (d1_databases.length > 1 && !binding) {
24
+ throw new Error("Argument 'binding' is required when more than one D1 bindings exist in the config");
25
+ }
26
+ let bindingConfig;
27
+ if (binding) {
28
+ bindingConfig = d1_databases.find((d1) => d1.binding === binding);
29
+ if (!bindingConfig) {
30
+ throw new Error(`Could not find D1 binding '${binding}' in config`);
31
+ }
32
+ } else {
33
+ bindingConfig = d1_databases[0];
34
+ }
35
+ if (!bindingConfig.database_id && !bindingConfig.preview_database_id) {
36
+ throw new Error(`Neither 'database_id' nor 'preview_database_id' is set for D1 binding '${bindingConfig.binding}'`);
37
+ }
38
+ const wranglerConfigDir = configPath ? dirname(configPath) : void 0;
39
+ const wranglerStateDir = persistTo ?? relative(".", join(wranglerConfigDir ?? "", ".wrangler/state/v3"));
40
+ const [database, previewDatabase] = [bindingConfig.database_id, bindingConfig.preview_database_id].map((databaseId) => {
41
+ if (!databaseId) {
42
+ return null;
43
+ }
44
+ const uniqueKey = "miniflare-D1DatabaseObject";
45
+ const miniflarePath = `${wranglerStateDir}/d1/${uniqueKey}`;
46
+ const hash = durableObjectNamespaceIdFromName(uniqueKey, databaseId);
47
+ const filename = join(miniflarePath, `${hash}.sqlite`);
48
+ return {
49
+ id: databaseId,
50
+ filename,
51
+ exists: existsSync(filename)
52
+ };
53
+ });
54
+ return {
55
+ configPath,
56
+ binding: bindingConfig.binding,
57
+ databaseName: bindingConfig.database_name,
58
+ database,
59
+ previewDatabase
60
+ };
61
+ }
62
+ function drizzleD1Config(config, options = {}) {
63
+ if (options.remote) {
64
+ const requiredOptions = ["accountId", "apiToken"];
65
+ const missingOptions = requiredOptions.filter((name) => !options[name]);
66
+ if (missingOptions.length > 0) {
67
+ throw new Error(`Options ${requiredOptions.join(", ")} are required when using remote database. Missing: ${missingOptions.join(", ")}`);
68
+ }
69
+ }
70
+ const binding = getD1BindingInfo({
71
+ binding: options.binding,
72
+ environment: options.environment,
73
+ configPath: options.configPath,
74
+ persistTo: options.persistTo
75
+ });
76
+ const database = options.preview ? binding.previewDatabase : binding.database;
77
+ if (!database) {
78
+ throw new Error(`'${options.preview ? "preview_database_id" : "database_id"} is not set for D1 binding '${binding.binding}'`);
79
+ }
80
+ if (!options.remote && !database.exists) {
81
+ if (binding.databaseName) {
82
+ let bin;
83
+ try {
84
+ bin = createRequire(import.meta.url).resolve("wrangler/bin/wrangler");
85
+ } catch (_) {
86
+ }
87
+ if (bin) {
88
+ const args = ["d1", "execute", binding.databaseName, "--command='SELECT 1'", "--local"];
89
+ if (options.environment) {
90
+ args.push(`--env=${options.environment}`);
91
+ }
92
+ if (options.configPath) {
93
+ args.push(`--config=${options.configPath}`);
94
+ }
95
+ if (options.persistTo) {
96
+ args.push(`--persist-to=${options.persistTo}`);
97
+ }
98
+ const command = ["wrangler", ...args].join(" ");
99
+ console.log(`[!] SQLite file for D1 binding '${binding.binding}' does not exist. Trying to create one by executing the following command:`);
100
+ console.log(` ${command}`);
101
+ try {
102
+ execFileSync(bin, args, {
103
+ stdio: "ignore"
104
+ });
105
+ } catch (_) {
106
+ }
107
+ if (existsSync(database.filename)) {
108
+ database.exists = true;
109
+ console.log("[\u2713] Command success! SQLite file has been successfully created.");
110
+ } else {
111
+ console.log("[\xD7] Command failed! Unable to create SQLite file.");
112
+ process.exit(1);
113
+ }
114
+ }
115
+ }
116
+ if (!database.exists) {
117
+ throw new Error(`Could not find SQLite file for D1 binding '${binding.binding}'`);
118
+ }
119
+ }
120
+ console.log("----------------------------------------------------------");
121
+ console.log(" Using following D1 Database:");
122
+ console.log("----------------------------------------------------------");
123
+ console.log(` Binding : ${binding.binding}`);
124
+ console.log(` Database name : ${binding.databaseName ?? "(not set)"}`);
125
+ console.log(` Database Id : ${database.id}`);
126
+ if (options.remote) {
127
+ console.log(" Mode : REMOTE");
128
+ console.log(" (using remote Cloudflare D1 database)");
129
+ } else {
130
+ console.log(" Mode : LOCAL");
131
+ console.log(" (using local SQLite database)");
132
+ console.log(` SQLite file : ${database.filename}`);
133
+ }
134
+ console.log("----------------------------------------------------------");
135
+ process.on("exit", () => {
136
+ console.log("");
137
+ });
138
+ return {
139
+ ...config,
140
+ dialect: "sqlite",
141
+ ...options.remote ? {
142
+ driver: "d1-http",
143
+ dbCredentials: {
144
+ databaseId: database.id,
145
+ accountId: options.accountId,
146
+ token: options.apiToken
147
+ }
148
+ } : {
149
+ dbCredentials: {
150
+ url: `file:${database.filename}`
151
+ }
152
+ }
153
+ };
154
+ }
155
+ export {
156
+ drizzleD1Config
157
+ };
158
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { execFileSync } from 'node:child_process';\nimport { createHash, createHmac } from 'node:crypto';\nimport { existsSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { dirname, join, relative } from 'node:path';\nimport type { Config } from 'drizzle-kit';\nimport { unstable_readConfig } from 'wrangler/wrangler-dist/cli';\n\nfunction durableObjectNamespaceIdFromName(uniqueKey: string, name: string) {\n /**\n * In v3.2, miniflare uses durable object to implement D1 and hashes the local sqlite filename.\n *\n * See the following for more context:\n * https://github.com/cloudflare/workers-sdk/issues/4548 (understand the hash of the local D1 filename)\n * https://github.com/cloudflare/miniflare/releases/tag/v3.20230918.0\n *\n * This function is copied from these links\n */\n const key = createHash('sha256').update(uniqueKey).digest();\n const nameHmac = createHmac('sha256', key).update(name).digest().subarray(0, 16);\n const hmac = createHmac('sha256', key).update(nameHmac).digest().subarray(0, 16);\n return Buffer.concat([nameHmac, hmac]).toString('hex');\n}\n\nfunction getD1BindingInfo({\n binding,\n environment,\n persistTo,\n configPath: wranglerConfigPath,\n}: {\n binding?: string;\n configPath?: string;\n persistTo?: string;\n environment?: string;\n} = {}) {\n const { d1_databases, configPath } = unstable_readConfig({ env: environment, config: wranglerConfigPath });\n if (d1_databases.length === 0) {\n throw new Error('No D1 binding exists in the config');\n }\n if (d1_databases.length > 1 && !binding) {\n throw new Error(\"Argument 'binding' is required when more than one D1 bindings exist in the config\");\n }\n let bindingConfig: (typeof d1_databases)[number] | undefined;\n if (binding) {\n bindingConfig = d1_databases.find((d1) => d1.binding === binding);\n if (!bindingConfig) {\n throw new Error(`Could not find D1 binding '${binding}' in config`);\n }\n } else {\n bindingConfig = d1_databases[0];\n }\n\n if (!bindingConfig.database_id && !bindingConfig.preview_database_id) {\n throw new Error(`Neither 'database_id' nor 'preview_database_id' is set for D1 binding '${bindingConfig.binding}'`);\n }\n\n const wranglerConfigDir = configPath ? dirname(configPath) : undefined;\n const wranglerStateDir = persistTo ?? relative('.', join(wranglerConfigDir ?? '', '.wrangler/state/v3'));\n\n const [database, previewDatabase] = [bindingConfig.database_id, bindingConfig.preview_database_id].map((databaseId) => {\n if (!databaseId) {\n return null;\n }\n const uniqueKey = 'miniflare-D1DatabaseObject';\n const miniflarePath = `${wranglerStateDir}/d1/${uniqueKey}`;\n const hash = durableObjectNamespaceIdFromName(uniqueKey, databaseId);\n const filename = join(miniflarePath, `${hash}.sqlite`);\n return {\n id: databaseId,\n filename,\n exists: existsSync(filename),\n };\n });\n\n return {\n configPath,\n binding: bindingConfig.binding,\n databaseName: bindingConfig.database_name,\n database,\n previewDatabase,\n };\n}\n\nexport interface DrizzleD1Options {\n configPath?: string;\n persistTo?: string;\n environment?: string;\n binding?: string;\n remote?: boolean;\n preview?: boolean;\n accountId?: string;\n apiToken?: string;\n}\n\nexport function drizzleD1Config(config: Omit<Config, 'dialect' | 'driver' | 'dbCredentials'>, options: DrizzleD1Options = {}): Config {\n if (options.remote) {\n const requiredOptions: (keyof DrizzleD1Options)[] = ['accountId', 'apiToken'];\n const missingOptions = requiredOptions.filter((name) => !options[name]);\n if (missingOptions.length > 0) {\n throw new Error(`Options ${requiredOptions.join(', ')} are required when using remote database. Missing: ${missingOptions.join(', ')}`);\n }\n }\n\n const binding = getD1BindingInfo({\n binding: options.binding,\n environment: options.environment,\n configPath: options.configPath,\n persistTo: options.persistTo,\n });\n\n const database = options.preview ? binding.previewDatabase : binding.database;\n\n if (!database) {\n throw new Error(`'${options.preview ? 'preview_database_id' : 'database_id'} is not set for D1 binding '${binding.binding}'`);\n }\n\n if (!options.remote && !database.exists) {\n if (binding.databaseName) {\n let bin: string | undefined;\n try {\n bin = createRequire(import.meta.url).resolve('wrangler/bin/wrangler');\n } catch (_) {}\n\n if (bin) {\n const args: string[] = ['d1', 'execute', binding.databaseName, \"--command='SELECT 1'\", '--local'];\n if (options.environment) {\n args.push(`--env=${options.environment}`);\n }\n if (options.configPath) {\n args.push(`--config=${options.configPath}`);\n }\n if (options.persistTo) {\n args.push(`--persist-to=${options.persistTo}`);\n }\n\n const command = ['wrangler', ...args].join(' ');\n console.log(`[!] SQLite file for D1 binding '${binding.binding}' does not exist. Trying to create one by executing the following command:`);\n console.log(` ${command}`);\n\n try {\n execFileSync(bin, args, {\n stdio: 'ignore',\n });\n } catch (_) {}\n\n if (existsSync(database.filename)) {\n database.exists = true;\n\n console.log('[✓] Command success! SQLite file has been successfully created.');\n } else {\n console.log('[×] Command failed! Unable to create SQLite file.');\n process.exit(1);\n }\n }\n }\n if (!database.exists) {\n throw new Error(`Could not find SQLite file for D1 binding '${binding.binding}'`);\n }\n }\n\n console.log('----------------------------------------------------------');\n console.log(' Using following D1 Database:');\n console.log('----------------------------------------------------------');\n console.log(` Binding : ${binding.binding}`);\n console.log(` Database name : ${binding.databaseName ?? '(not set)'}`);\n console.log(` Database Id : ${database.id}`);\n if (options.remote) {\n console.log(' Mode : REMOTE');\n console.log(' (using remote Cloudflare D1 database)');\n } else {\n console.log(' Mode : LOCAL');\n console.log(' (using local SQLite database)');\n console.log(` SQLite file : ${database.filename}`);\n }\n console.log('----------------------------------------------------------');\n\n process.on('exit', () => {\n console.log('');\n });\n\n return {\n ...config,\n dialect: 'sqlite',\n ...(options.remote\n ? {\n driver: 'd1-http',\n dbCredentials: {\n databaseId: database.id,\n accountId: options.accountId,\n token: options.apiToken,\n },\n }\n : {\n dbCredentials: {\n url: `file:${database.filename}`,\n },\n }),\n };\n}\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@deox/drizzle-d1-utils",
3
+ "version": "0.0.1",
4
+ "private": false,
5
+ "description": "Drizzle D1 utils",
6
+ "keywords": [
7
+ "drizzle-d1",
8
+ "cloudflare-d1"
9
+ ],
10
+ "license": "MIT",
11
+ "author": {
12
+ "name": "Deo Kumar",
13
+ "url": "https://github.com/kumardeo"
14
+ },
15
+ "homepage": "https://github.com/kumardeo/deox/tree/main/packages/drizzle-d1-utils#readme",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "https://github.com/kumardeo/deox",
19
+ "directory": "packages/drizzle-d1-utils"
20
+ },
21
+ "bugs": {
22
+ "url": "https://github.com/kumardeo/deox/issues"
23
+ },
24
+ "files": [
25
+ "dist"
26
+ ],
27
+ "type": "module",
28
+ "main": "./dist/index.cjs",
29
+ "module": "./dist/index.js",
30
+ "types": "./dist/index.d.ts",
31
+ "exports": {
32
+ "./package.json": "./package.json",
33
+ ".": {
34
+ "import": {
35
+ "types": "./dist/index.d.ts",
36
+ "default": "./dist/index.js"
37
+ },
38
+ "require": {
39
+ "types": "./dist/index.d.cts",
40
+ "default": "./dist/index.cjs"
41
+ }
42
+ }
43
+ },
44
+ "devDependencies": {
45
+ "drizzle-kit": "^0.31.7",
46
+ "tsup": "^8.5.0",
47
+ "wrangler": "^4.39.0"
48
+ },
49
+ "peerDependencies": {
50
+ "wrangler": "^4.0.0"
51
+ },
52
+ "scripts": {
53
+ "check:types": "tsc --noEmit",
54
+ "build": "tsup",
55
+ "clean": "rimraf dist"
56
+ }
57
+ }