@drizzle-adapter/d1 1.0.18 → 2.0.0

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 (48) hide show
  1. package/.wrangler/state/v3/d1/miniflare-D1DatabaseObject/6e148680931b1bfcb993e0463aced3b0a6d6cf50af24dea2dcc2c3b2e9ae9e3b.sqlite-shm +0 -0
  2. package/.wrangler/state/v3/d1/miniflare-D1DatabaseObject/6e148680931b1bfcb993e0463aced3b0a6d6cf50af24dea2dcc2c3b2e9ae9e3b.sqlite-wal +0 -0
  3. package/README.md +22 -12
  4. package/dist/Adapter/D1DrizzleAdapter.d.ts +4 -4
  5. package/dist/Adapter/D1DrizzleAdapter.js +3 -3
  6. package/dist/Adapter/D1DrizzleConnection.d.ts +2 -2
  7. package/dist/Adapter/D1DrizzleConnection.js +13 -9
  8. package/dist/Adapter/D1DrizzleMigrator.d.ts +1 -1
  9. package/dist/Error/D1TransactionUnsupportedError.d.ts +10 -0
  10. package/dist/Error/D1TransactionUnsupportedError.js +13 -0
  11. package/dist/Interface/D1DrizzleClientInterface.d.ts +2 -1
  12. package/dist/index.d.ts +8 -5
  13. package/dist/index.js +10 -6
  14. package/dist/migrator.d.ts +1 -1
  15. package/dist/migrator.js +3 -3
  16. package/eslint.config.mjs +1 -1
  17. package/package.json +29 -27
  18. package/src/Adapter/D1DrizzleAdapter.ts +6 -6
  19. package/src/Adapter/D1DrizzleConnection.ts +23 -15
  20. package/src/Adapter/D1DrizzleMigrator.ts +16 -8
  21. package/src/Error/D1TransactionUnsupportedError.ts +17 -0
  22. package/src/Factory/D1DrizzleDataTypesFactory.ts +7 -2
  23. package/src/Factory/D1DrizzleMigratorFactory.ts +4 -1
  24. package/src/Interface/D1DrizzleClientInterface.ts +2 -1
  25. package/src/index.ts +22 -10
  26. package/src/migrator.ts +9 -6
  27. package/tests/Adapter/D1DrizzleAdapter.test.ts +2 -2
  28. package/tests/Adapter/D1DrizzleConnection.test.ts +11 -1
  29. package/tests/Adapter/D1DrizzleMigrator.test.ts +1 -1
  30. package/tests/Error/D1TransactionUnsupportedError.test.ts +12 -0
  31. package/tests/Factory/D1DrizzleAdapterFactory.test.ts +3 -3
  32. package/tests/Integration/D1Atomicity.miniflare.test.ts +77 -0
  33. package/tsconfig.json +1 -6
  34. package/dist/Adapter/D1DrizzleDataTypes.d.ts +0 -7
  35. package/dist/Adapter/D1DrizzleDataTypes.js +0 -7
  36. package/dist/Adapter/index.d.ts +0 -2
  37. package/dist/Adapter/index.js +0 -2
  38. package/dist/Factory/index.d.ts +0 -2
  39. package/dist/Factory/index.js +0 -2
  40. package/dist/Interface/index.d.ts +0 -1
  41. package/dist/Interface/index.js +0 -1
  42. package/dist/Type/index.d.ts +0 -1
  43. package/dist/Type/index.js +0 -1
  44. package/src/Adapter/D1DrizzleDataTypes.ts +0 -7
  45. package/src/Adapter/index.ts +0 -2
  46. package/src/Factory/index.ts +0 -2
  47. package/src/Interface/index.ts +0 -1
  48. package/src/Type/index.ts +0 -1
package/README.md CHANGED
@@ -60,33 +60,43 @@ await client.run(sql`SELECT * FROM users`);
60
60
  ```
61
61
 
62
62
  ### Transaction Compatibility
63
- **Important**: Cloudflare D1 does not support real SQL transactions. This adapter provides a compatibility layer that allows transaction-based code to run without modifications, but **without transaction guarantees**:
63
+ **Breaking in 2.0.0**: Cloudflare D1 has no interactive transactions. `transaction()` now throws `D1TransactionUnsupportedError` instead of running the callback without atomicity. The callback is never invoked.
64
64
 
65
65
  ```typescript
66
- // This code will run without errors, but operations are NOT atomic
67
- await client.transaction(async (tx) => {
68
- await tx.insert(users).values({ name: 'John' });
69
- await tx.update(posts).set({ published: 1 });
70
- // If an error occurs here, previous operations will NOT be rolled back
71
- });
66
+ import { D1TransactionUnsupportedError } from '@drizzle-adapter/d1';
67
+
68
+ try {
69
+ await client.transaction(async (tx) => { /* never runs */ });
70
+ } catch (caughtError) {
71
+ if (caughtError instanceof D1TransactionUnsupportedError) {
72
+ // use db.batch() instead
73
+ }
74
+ }
72
75
  ```
73
76
 
74
- **For atomic operations**, use D1's batch API directly:
77
+ **For atomic operations**, use `db.batch()`. It runs as one implicit SQL transaction (all-or-nothing):
75
78
 
76
79
  ```typescript
77
- // This provides atomic execution - all succeed or all fail
80
+ import { sql } from 'drizzle-orm';
81
+
78
82
  const results = await client.batch([
79
- client.insert(users).values({ name: 'John' }).prepare(),
80
- client.update(posts).set({ published: 1 }).prepare()
83
+ client.insert(users).values({ name: 'John' }),
84
+ client.run(sql`UPDATE posts SET published = 1 WHERE id = ${postId}`)
81
85
  ]);
82
86
  ```
83
87
 
84
88
  ### Migration Considerations
85
- - **Transaction Compatibility**: Existing code using `db.transaction()` will work without modification
89
+ - **Transaction Compatibility**: Since 2.0.0, `db.transaction()` throws `D1TransactionUnsupportedError`; migrate to `db.batch()`
86
90
  - **No Rollback**: Failed operations cannot be automatically rolled back
87
91
  - **Batch Operations**: Use `db.batch()` for operations that require atomicity
88
92
  - **Error Handling**: Implement manual cleanup logic for critical operations
89
93
 
94
+ ### Releasing
95
+ ```bash
96
+ pnpm check && pnpm lint && pnpm test
97
+ pnpm --filter @drizzle-adapter/d1 publish --access public # run from the repo root
98
+ ```
99
+
90
100
  ## Usage
91
101
 
92
102
  ### Cloudflare Workers Setup
@@ -1,13 +1,13 @@
1
1
  import type { D1Database } from '@cloudflare/workers-types';
2
2
  import type { DrizzleAdapterInterface } from '@drizzle-adapter/core';
3
+ import { SqliteCoreDrizzleDataTypes } from '@drizzle-adapter/sqlite-core';
3
4
  import { D1DrizzleConnection } from './D1DrizzleConnection.js';
4
- import { D1DrizzleDataTypes } from './D1DrizzleDataTypes.js';
5
5
  /**
6
6
  * D1 adapter implementation for Drizzle ORM.
7
7
  * Provides D1-specific database operations and connection management.
8
8
  */
9
9
  export declare class D1DrizzleAdapter implements DrizzleAdapterInterface {
10
- private connection;
10
+ private readonly connection;
11
11
  /**
12
12
  * Constructor.
13
13
  *
@@ -23,9 +23,9 @@ export declare class D1DrizzleAdapter implements DrizzleAdapterInterface {
23
23
  /**
24
24
  * Returns the data types.
25
25
  *
26
- * @returns {D1DrizzleDataTypes} The D1 data types instance
26
+ * @returns {SqliteCoreDrizzleDataTypes} The SQLite core data types instance
27
27
  */
28
- getDataTypes(): D1DrizzleDataTypes;
28
+ getDataTypes(): SqliteCoreDrizzleDataTypes;
29
29
  /**
30
30
  * Returns the dialect.
31
31
  *
@@ -1,5 +1,5 @@
1
+ import { SqliteCoreDrizzleDataTypes } from '@drizzle-adapter/sqlite-core';
1
2
  import { D1DrizzleConnection } from './D1DrizzleConnection.js';
2
- import { D1DrizzleDataTypes } from './D1DrizzleDataTypes.js';
3
3
  /**
4
4
  * D1 adapter implementation for Drizzle ORM.
5
5
  * Provides D1-specific database operations and connection management.
@@ -25,10 +25,10 @@ export class D1DrizzleAdapter {
25
25
  /**
26
26
  * Returns the data types.
27
27
  *
28
- * @returns {D1DrizzleDataTypes} The D1 data types instance
28
+ * @returns {SqliteCoreDrizzleDataTypes} The SQLite core data types instance
29
29
  */
30
30
  getDataTypes() {
31
- return new D1DrizzleDataTypes();
31
+ return new SqliteCoreDrizzleDataTypes();
32
32
  }
33
33
  /**
34
34
  * Returns the dialect.
@@ -1,12 +1,12 @@
1
1
  import type { D1Database } from '@cloudflare/workers-types';
2
2
  import type { DrizzleConnectionInterface } from '@drizzle-adapter/core';
3
- import type { D1DrizzleClientInterface } from 'src/Interface';
3
+ import type { D1DrizzleClientInterface } from '../Interface/D1DrizzleClientInterface.js';
4
4
  /**
5
5
  * D1 connection implementation for Drizzle ORM.
6
6
  * Manages database connections and provides client access for D1 databases.
7
7
  */
8
8
  export declare class D1DrizzleConnection implements DrizzleConnectionInterface {
9
- private client;
9
+ private readonly client;
10
10
  /**
11
11
  * Constructor.
12
12
  *
@@ -1,4 +1,5 @@
1
1
  import { drizzle } from 'drizzle-orm/d1';
2
+ import { D1TransactionUnsupportedError } from '../Error/D1TransactionUnsupportedError.js';
2
3
  /**
3
4
  * D1 connection implementation for Drizzle ORM.
4
5
  * Manages database connections and provides client access for D1 databases.
@@ -13,15 +14,18 @@ export class D1DrizzleConnection {
13
14
  constructor(driver) {
14
15
  // We want to add the execute method as an alias for run
15
16
  const database = drizzle(driver);
16
- database.execute = database.run;
17
- // Override transaction method to provide transaction-like behavior
18
- // Since D1 doesn't support real transactions, we pass the original client
19
- // Users can implement their own batching if needed for atomicity
20
- database.transaction = async (callback) => {
21
- // For now, just pass the original client - no transaction isolation
22
- // This allows code to work without modifications while being explicit about the limitation
23
- return await callback(database);
24
- };
17
+ database.execute =
18
+ database.run;
19
+ /**
20
+ * Replaces drizzle's transaction. drizzle-orm d1/session.js transaction() issues raw `begin`, which D1 rejects (probe in 1TS-Monorepo#4023).
21
+ * The callback is never run; callers must use batch().
22
+ * @returns {Promise<never>} Always rejects with D1TransactionUnsupportedError.
23
+ */
24
+ database.transaction =
25
+ async () => {
26
+ // `return await` satisfies both require-await and the no-bare-throw lint rules
27
+ return await Promise.reject(new D1TransactionUnsupportedError());
28
+ };
25
29
  this.client = database;
26
30
  }
27
31
  /**
@@ -1,5 +1,5 @@
1
1
  import type { DrizzleMigratorInterface, TypeDrizzleMigratorConfig } from '@drizzle-adapter/core';
2
- import type { D1DrizzleClientInterface } from '../Interface/index.js';
2
+ import type { D1DrizzleClientInterface } from '../Interface/D1DrizzleClientInterface.js';
3
3
  /**
4
4
  * D1 migrator implementation for Drizzle ORM.
5
5
  * Handles database migrations for D1 databases.
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Thrown by transaction() on a D1 client. D1 has no interactive transactions,
3
+ * so the callback could not run atomically.
4
+ */
5
+ export declare class D1TransactionUnsupportedError extends Error {
6
+ /**
7
+ * Constructor.
8
+ */
9
+ constructor();
10
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Thrown by transaction() on a D1 client. D1 has no interactive transactions,
3
+ * so the callback could not run atomically.
4
+ */
5
+ export class D1TransactionUnsupportedError extends Error {
6
+ /**
7
+ * Constructor.
8
+ */
9
+ constructor() {
10
+ super('D1 has no interactive transactions, so transaction() would run your callback without atomicity. Use db.batch([...]) instead: it is atomic (one implicit SQL transaction, all-or-nothing). Wrap raw SQL as db.run(sql`...`) inside the batch.');
11
+ this.name = 'D1TransactionUnsupportedError';
12
+ }
13
+ }
@@ -13,5 +13,6 @@ export interface D1DrizzleClientInterface extends DrizzleClientInterface {
13
13
  selectDistinct(...args: any[]): ReturnType<AnyD1Database['selectDistinct']>;
14
14
  update(...args: any[]): ReturnType<AnyD1Database['update']>;
15
15
  insert(...args: any[]): ReturnType<AnyD1Database['insert']>;
16
- transaction(...args: any[]): ReturnType<AnyD1Database['transaction']>;
16
+ /** Always rejects with D1TransactionUnsupportedError — use batch(). */
17
+ transaction(...args: unknown[]): Promise<never>;
17
18
  }
package/dist/index.d.ts CHANGED
@@ -1,6 +1,9 @@
1
- export * from './Adapter/index.js';
2
- export * from './Factory/index.js';
3
- export * from './Interface/index.js';
4
- export * from './Type/index.js';
5
- import { D1DrizzleAdapterFactory } from './Factory/index.js';
1
+ import { D1DrizzleAdapterFactory } from './Factory/D1DrizzleAdapterFactory.js';
2
+ export { D1DrizzleAdapter } from './Adapter/D1DrizzleAdapter.js';
3
+ export { D1DrizzleConnection } from './Adapter/D1DrizzleConnection.js';
4
+ export { D1TransactionUnsupportedError } from './Error/D1TransactionUnsupportedError.js';
5
+ export { D1DrizzleAdapterFactory } from './Factory/D1DrizzleAdapterFactory.js';
6
+ export { D1DrizzleDataTypesFactory } from './Factory/D1DrizzleDataTypesFactory.js';
7
+ export * from './Interface/D1DrizzleClientInterface.js';
8
+ export * from './Type/Types.js';
6
9
  export default D1DrizzleAdapterFactory;
package/dist/index.js CHANGED
@@ -1,10 +1,14 @@
1
- export * from './Adapter/index.js';
2
- export * from './Factory/index.js';
3
- export * from './Interface/index.js';
4
- export * from './Type/index.js';
5
1
  // default export so that core factory can use it directly
6
- import { D1DrizzleAdapterFactory, D1DrizzleDataTypesFactory } from './Factory/index.js';
7
- export default D1DrizzleAdapterFactory;
8
2
  import { DrizzleAdapterFactoryRegistry, DrizzleDataTypesFactoryRegistry } from '@drizzle-adapter/core';
3
+ import { D1DrizzleAdapterFactory } from './Factory/D1DrizzleAdapterFactory.js';
4
+ import { D1DrizzleDataTypesFactory } from './Factory/D1DrizzleDataTypesFactory.js';
5
+ export { D1DrizzleAdapter } from './Adapter/D1DrizzleAdapter.js';
6
+ export { D1DrizzleConnection } from './Adapter/D1DrizzleConnection.js';
7
+ export { D1TransactionUnsupportedError } from './Error/D1TransactionUnsupportedError.js';
8
+ export { D1DrizzleAdapterFactory } from './Factory/D1DrizzleAdapterFactory.js';
9
+ export { D1DrizzleDataTypesFactory } from './Factory/D1DrizzleDataTypesFactory.js';
10
+ export * from './Interface/D1DrizzleClientInterface.js';
11
+ export * from './Type/Types.js';
12
+ export default D1DrizzleAdapterFactory;
9
13
  DrizzleAdapterFactoryRegistry.registerAdapterFactory('d1', D1DrizzleAdapterFactory);
10
14
  DrizzleDataTypesFactoryRegistry.registerDataTypesFactory('d1', D1DrizzleDataTypesFactory);
@@ -1,4 +1,4 @@
1
+ import { D1DrizzleMigratorFactory } from './Factory/D1DrizzleMigratorFactory.js';
1
2
  export * from './Adapter/D1DrizzleMigrator.js';
2
3
  export * from './Factory/D1DrizzleMigratorFactory.js';
3
- import { D1DrizzleMigratorFactory } from './Factory/D1DrizzleMigratorFactory.js';
4
4
  export default D1DrizzleMigratorFactory;
package/dist/migrator.js CHANGED
@@ -1,7 +1,7 @@
1
- export * from './Adapter/D1DrizzleMigrator.js';
2
- export * from './Factory/D1DrizzleMigratorFactory.js';
3
1
  // default export so that core factory can use it directly
2
+ import { DrizzleMigratorFactoryRegistry } from '@drizzle-adapter/core/migrator';
4
3
  import { D1DrizzleMigratorFactory } from './Factory/D1DrizzleMigratorFactory.js';
4
+ export * from './Adapter/D1DrizzleMigrator.js';
5
+ export * from './Factory/D1DrizzleMigratorFactory.js';
5
6
  export default D1DrizzleMigratorFactory;
6
- import { DrizzleMigratorFactoryRegistry } from '@drizzle-adapter/core/migrator';
7
7
  DrizzleMigratorFactoryRegistry.registerMigratorFactory('d1', D1DrizzleMigratorFactory);
package/eslint.config.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import eslintConfig from '@dmitryrechkin/eslint-standard/eslint.config.mjs';
1
+ import eslintConfig from '@dmitryrechkin/eslint-standard';
2
2
 
3
3
  export default eslintConfig({
4
4
  tsconfigPath: './tsconfig.eslint.json',
package/package.json CHANGED
@@ -1,38 +1,19 @@
1
1
  {
2
2
  "name": "@drizzle-adapter/d1",
3
3
  "type": "module",
4
- "version": "1.0.18",
4
+ "version": "2.0.0",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "exports": {
8
8
  ".": {
9
- "import": "./dist/index.js",
10
- "types": "./dist/index.d.ts"
9
+ "types": "./dist/index.d.ts",
10
+ "default": "./dist/index.js"
11
11
  },
12
12
  "./migrator": {
13
- "import": "./dist/migrator.js",
14
- "types": "./dist/migrator.d.ts"
13
+ "types": "./dist/migrator.d.ts",
14
+ "default": "./dist/migrator.js"
15
15
  }
16
16
  },
17
- "devDependencies": {
18
- "@cloudflare/workers-types": "^4.20250419.0",
19
- "@dmitryrechkin/eslint-standard": "^1.0.7",
20
- "dotenv": "^17.0.1",
21
- "eslint": "^9.30.1",
22
- "eslint-plugin-unused-imports": "^4.1.4",
23
- "shx": "^0.4.0",
24
- "tsc-esm-fix": "^3.1.2",
25
- "tsconfig-paths": "^4.2.0",
26
- "typescript": "^5.8.3",
27
- "vitest": "^3.2.4",
28
- "wrangler": "^3.114.11"
29
- },
30
- "dependencies": {
31
- "drizzle-orm": "^0.44.2",
32
- "@drizzle-adapter/core": "^1.0.14",
33
- "@drizzle-adapter/sqlite-core": "^1.0.14"
34
- },
35
- "license": "MIT",
36
17
  "scripts": {
37
18
  "lint": "eslint src/",
38
19
  "format": "eslint --fix src/",
@@ -40,6 +21,27 @@
40
21
  "check": "tsc --noEmit",
41
22
  "build": "shx rm -rf dist && tsc && tsc-esm-fix",
42
23
  "package:tgz": "pnpm pack",
43
- "package:publish": "pnpm publish --access public"
44
- }
45
- }
24
+ "package:publish": "pnpm publish --access public",
25
+ "prepublishOnly": "pnpm run build"
26
+ },
27
+ "devDependencies": {
28
+ "@cloudflare/workers-types": "^4.20260317.1",
29
+ "@dmitryrechkin/eslint-standard": "^1.5.13",
30
+ "dotenv": "^17.3.1",
31
+ "eslint": "^10.1.0",
32
+ "eslint-plugin-unused-imports": "^4.4.1",
33
+ "miniflare": "4.20260317.2",
34
+ "shx": "^0.4.0",
35
+ "tsc-esm-fix": "^3.1.2",
36
+ "tsconfig-paths": "^4.2.0",
37
+ "typescript": "^5.9.3",
38
+ "vitest": "^4.1.1",
39
+ "wrangler": "^4.77.0"
40
+ },
41
+ "dependencies": {
42
+ "@drizzle-adapter/core": "workspace:^",
43
+ "@drizzle-adapter/sqlite-core": "workspace:^",
44
+ "drizzle-orm": "^0.45.1"
45
+ },
46
+ "license": "MIT"
47
+ }
@@ -1,8 +1,8 @@
1
1
  import type { D1Database } from '@cloudflare/workers-types';
2
2
  import type { DrizzleAdapterInterface } from '@drizzle-adapter/core';
3
+ import { SqliteCoreDrizzleDataTypes } from '@drizzle-adapter/sqlite-core';
3
4
 
4
5
  import { D1DrizzleConnection } from './D1DrizzleConnection';
5
- import { D1DrizzleDataTypes } from './D1DrizzleDataTypes';
6
6
 
7
7
  /**
8
8
  * D1 adapter implementation for Drizzle ORM.
@@ -10,14 +10,14 @@ import { D1DrizzleDataTypes } from './D1DrizzleDataTypes';
10
10
  */
11
11
  export class D1DrizzleAdapter implements DrizzleAdapterInterface
12
12
  {
13
- private connection: D1DrizzleConnection;
13
+ private readonly connection: D1DrizzleConnection;
14
14
 
15
15
  /**
16
16
  * Constructor.
17
17
  *
18
18
  * @param {D1Database} driver - The D1 database driver instance
19
19
  */
20
- public constructor(driver: D1Database)
20
+ constructor(driver: D1Database)
21
21
  {
22
22
  this.connection = new D1DrizzleConnection(driver);
23
23
  }
@@ -35,11 +35,11 @@ export class D1DrizzleAdapter implements DrizzleAdapterInterface
35
35
  /**
36
36
  * Returns the data types.
37
37
  *
38
- * @returns {D1DrizzleDataTypes} The D1 data types instance
38
+ * @returns {SqliteCoreDrizzleDataTypes} The SQLite core data types instance
39
39
  */
40
- public getDataTypes(): D1DrizzleDataTypes
40
+ public getDataTypes(): SqliteCoreDrizzleDataTypes
41
41
  {
42
- return new D1DrizzleDataTypes();
42
+ return new SqliteCoreDrizzleDataTypes();
43
43
  }
44
44
 
45
45
  /**
@@ -1,7 +1,9 @@
1
1
  import type { D1Database } from '@cloudflare/workers-types';
2
2
  import type { DrizzleConnectionInterface } from '@drizzle-adapter/core';
3
3
  import { drizzle } from 'drizzle-orm/d1';
4
- import type { D1DrizzleClientInterface } from 'src/Interface';
4
+
5
+ import { D1TransactionUnsupportedError } from '../Error/D1TransactionUnsupportedError';
6
+ import type { D1DrizzleClientInterface } from '../Interface/D1DrizzleClientInterface';
5
7
 
6
8
  /**
7
9
  * D1 connection implementation for Drizzle ORM.
@@ -9,29 +11,35 @@ import type { D1DrizzleClientInterface } from 'src/Interface';
9
11
  */
10
12
  export class D1DrizzleConnection implements DrizzleConnectionInterface
11
13
  {
12
- private client: D1DrizzleClientInterface;
14
+ private readonly client: D1DrizzleClientInterface;
13
15
 
14
16
  /**
15
17
  * Constructor.
16
18
  *
17
19
  * @param {D1Database} driver - The D1 database driver instance
18
20
  */
19
- public constructor(driver: D1Database)
21
+ constructor(driver: D1Database)
20
22
  {
21
23
  // We want to add the execute method as an alias for run
22
- const database: ReturnType<typeof drizzle> | D1DrizzleClientInterface = drizzle(driver);
23
- (database as unknown as D1DrizzleClientInterface).execute = database.run;
24
+ const database: ReturnType<typeof drizzle> | D1DrizzleClientInterface =
25
+ drizzle(driver);
26
+
27
+ (database as unknown as D1DrizzleClientInterface).execute =
28
+ database.run;
24
29
 
25
- // Override transaction method to provide transaction-like behavior
26
- // Since D1 doesn't support real transactions, we pass the original client
27
- // Users can implement their own batching if needed for atomicity
28
- (database as unknown as D1DrizzleClientInterface).transaction = async <T>(
29
- callback: (tx: D1DrizzleClientInterface) => Promise<T>
30
- ): Promise<T> => {
31
- // For now, just pass the original client - no transaction isolation
32
- // This allows code to work without modifications while being explicit about the limitation
33
- return await callback(database as unknown as D1DrizzleClientInterface);
34
- };
30
+ /**
31
+ * Replaces drizzle's transaction. drizzle-orm d1/session.js transaction() issues raw `begin`, which D1 rejects (probe in 1TS-Monorepo#4023).
32
+ * The callback is never run; callers must use batch().
33
+ * @returns {Promise<never>} Always rejects with D1TransactionUnsupportedError.
34
+ */
35
+ (database as unknown as D1DrizzleClientInterface).transaction =
36
+ async (): Promise<never> =>
37
+ {
38
+ // `return await` satisfies both require-await and the no-bare-throw lint rules
39
+ return await Promise.reject(
40
+ new D1TransactionUnsupportedError()
41
+ );
42
+ };
35
43
 
36
44
  this.client = database as unknown as D1DrizzleClientInterface;
37
45
  }
@@ -1,7 +1,10 @@
1
- import type { DrizzleMigratorInterface, TypeDrizzleMigratorConfig } from '@drizzle-adapter/core';
1
+ import type {
2
+ DrizzleMigratorInterface,
3
+ TypeDrizzleMigratorConfig
4
+ } from '@drizzle-adapter/core';
2
5
  import { DrizzleD1Database } from 'drizzle-orm/d1/driver';
3
6
 
4
- import type { D1DrizzleClientInterface } from '../Interface';
7
+ import type { D1DrizzleClientInterface } from '../Interface/D1DrizzleClientInterface';
5
8
 
6
9
  /**
7
10
  * D1 migrator implementation for Drizzle ORM.
@@ -14,7 +17,8 @@ export class D1DrizzleMigrator implements DrizzleMigratorInterface
14
17
  *
15
18
  * @param {TypeDrizzleMigratorConfig} config The migrator configuration.
16
19
  */
17
- public constructor(private readonly config: TypeDrizzleMigratorConfig) {}
20
+ constructor(private readonly config: TypeDrizzleMigratorConfig)
21
+ {}
18
22
 
19
23
  /**
20
24
  * Migrate the database.
@@ -24,10 +28,14 @@ export class D1DrizzleMigrator implements DrizzleMigratorInterface
24
28
  public async migrate(client: D1DrizzleClientInterface): Promise<void>
25
29
  {
26
30
  const { migrate } = await import('drizzle-orm/d1/migrator');
27
- await migrate(client as unknown as DrizzleD1Database<Record<string, unknown>>, {
28
- migrationsFolder: this.config.DATABASE_MIGRATIONS_FOLDER,
29
- migrationsTable: this.config.DATABASE_MIGRATIONS_TABLE,
30
- migrationsSchema: this.config.DATABASE_MIGRATIONS_SCHEMA
31
- });
31
+
32
+ await migrate(
33
+ client as unknown as DrizzleD1Database<Record<string, unknown>>,
34
+ {
35
+ migrationsFolder: this.config.DATABASE_MIGRATIONS_FOLDER,
36
+ migrationsTable: this.config.DATABASE_MIGRATIONS_TABLE,
37
+ migrationsSchema: this.config.DATABASE_MIGRATIONS_SCHEMA
38
+ }
39
+ );
32
40
  }
33
41
  }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Thrown by transaction() on a D1 client. D1 has no interactive transactions,
3
+ * so the callback could not run atomically.
4
+ */
5
+ export class D1TransactionUnsupportedError extends Error
6
+ {
7
+ /**
8
+ * Constructor.
9
+ */
10
+ constructor()
11
+ {
12
+ super(
13
+ 'D1 has no interactive transactions, so transaction() would run your callback without atomicity. Use db.batch([...]) instead: it is atomic (one implicit SQL transaction, all-or-nothing). Wrap raw SQL as db.run(sql`...`) inside the batch.'
14
+ );
15
+ this.name = 'D1TransactionUnsupportedError';
16
+ }
17
+ }
@@ -1,4 +1,7 @@
1
- import type { DrizzleDataTypesFactoryInterface, TypeDrizzleDatabaseConfig } from '@drizzle-adapter/core';
1
+ import type {
2
+ DrizzleDataTypesFactoryInterface,
3
+ TypeDrizzleDatabaseConfig
4
+ } from '@drizzle-adapter/core';
2
5
  import { SqliteCoreDrizzleDataTypes } from '@drizzle-adapter/sqlite-core';
3
6
 
4
7
  /**
@@ -13,7 +16,9 @@ export class D1DrizzleDataTypesFactory implements DrizzleDataTypesFactoryInterfa
13
16
  * @param {TypeDrizzleDatabaseConfig} _config - Not used in this implementation
14
17
  * @returns {SqliteCoreDrizzleDataTypes} A new SQLite core data types instance
15
18
  */
16
- public create(_config: TypeDrizzleDatabaseConfig): SqliteCoreDrizzleDataTypes
19
+ public create(
20
+ _config: TypeDrizzleDatabaseConfig
21
+ ): SqliteCoreDrizzleDataTypes
17
22
  {
18
23
  return new SqliteCoreDrizzleDataTypes();
19
24
  }
@@ -1,4 +1,7 @@
1
- import type { DrizzleMigratorFactoryInterface, TypeDrizzleMigratorConfig } from '@drizzle-adapter/core';
1
+ import type {
2
+ DrizzleMigratorFactoryInterface,
3
+ TypeDrizzleMigratorConfig
4
+ } from '@drizzle-adapter/core';
2
5
 
3
6
  import { D1DrizzleMigrator } from '../migrator';
4
7
 
@@ -15,5 +15,6 @@ export interface D1DrizzleClientInterface extends DrizzleClientInterface
15
15
  selectDistinct(...args: any[]): ReturnType<AnyD1Database['selectDistinct']>;
16
16
  update(...args: any[]): ReturnType<AnyD1Database['update']>;
17
17
  insert(...args: any[]): ReturnType<AnyD1Database['insert']>;
18
- transaction(...args: any[]): ReturnType<AnyD1Database['transaction']>;
18
+ /** Always rejects with D1TransactionUnsupportedError — use batch(). */
19
+ transaction(...args: unknown[]): Promise<never>;
19
20
  }
package/src/index.ts CHANGED
@@ -1,13 +1,25 @@
1
- export * from './Adapter';
2
- export * from './Factory';
3
- export * from './Interface';
4
- export * from './Type';
5
-
6
1
  // default export so that core factory can use it directly
7
- import { D1DrizzleAdapterFactory, D1DrizzleDataTypesFactory } from './Factory';
8
- export default D1DrizzleAdapterFactory;
2
+ import {
3
+ DrizzleAdapterFactoryRegistry,
4
+ DrizzleDataTypesFactoryRegistry
5
+ } from '@drizzle-adapter/core';
9
6
 
10
- import { DrizzleAdapterFactoryRegistry, DrizzleDataTypesFactoryRegistry } from '@drizzle-adapter/core';
11
- DrizzleAdapterFactoryRegistry.registerAdapterFactory('d1', D1DrizzleAdapterFactory);
12
- DrizzleDataTypesFactoryRegistry.registerDataTypesFactory('d1', D1DrizzleDataTypesFactory);
7
+ import { D1DrizzleAdapterFactory } from './Factory/D1DrizzleAdapterFactory';
8
+ import { D1DrizzleDataTypesFactory } from './Factory/D1DrizzleDataTypesFactory';
13
9
 
10
+ export { D1DrizzleAdapter } from './Adapter/D1DrizzleAdapter';
11
+ export { D1DrizzleConnection } from './Adapter/D1DrizzleConnection';
12
+ export { D1TransactionUnsupportedError } from './Error/D1TransactionUnsupportedError';
13
+ export { D1DrizzleAdapterFactory } from './Factory/D1DrizzleAdapterFactory';
14
+ export { D1DrizzleDataTypesFactory } from './Factory/D1DrizzleDataTypesFactory';
15
+ export * from './Interface/D1DrizzleClientInterface';
16
+ export * from './Type/Types';
17
+ export default D1DrizzleAdapterFactory;
18
+ DrizzleAdapterFactoryRegistry.registerAdapterFactory(
19
+ 'd1',
20
+ D1DrizzleAdapterFactory
21
+ );
22
+ DrizzleDataTypesFactoryRegistry.registerDataTypesFactory(
23
+ 'd1',
24
+ D1DrizzleDataTypesFactory
25
+ );
package/src/migrator.ts CHANGED
@@ -1,9 +1,12 @@
1
- export * from './Adapter/D1DrizzleMigrator';
2
- export * from './Factory/D1DrizzleMigratorFactory';
3
-
4
1
  // default export so that core factory can use it directly
2
+ import { DrizzleMigratorFactoryRegistry } from '@drizzle-adapter/core/migrator';
3
+
5
4
  import { D1DrizzleMigratorFactory } from './Factory/D1DrizzleMigratorFactory';
6
- export default D1DrizzleMigratorFactory;
7
5
 
8
- import { DrizzleMigratorFactoryRegistry } from '@drizzle-adapter/core/migrator';
9
- DrizzleMigratorFactoryRegistry.registerMigratorFactory('d1', D1DrizzleMigratorFactory);
6
+ export * from './Adapter/D1DrizzleMigrator';
7
+ export * from './Factory/D1DrizzleMigratorFactory';
8
+ export default D1DrizzleMigratorFactory;
9
+ DrizzleMigratorFactoryRegistry.registerMigratorFactory(
10
+ 'd1',
11
+ D1DrizzleMigratorFactory
12
+ );
@@ -28,8 +28,8 @@ describe('D1DrizzleAdapter', () => {
28
28
  expect(dataTypes).toBeInstanceOf(SqliteCoreDrizzleDataTypes);
29
29
  });
30
30
 
31
- it('should return D1DrizzleConnection instance with the provided database', () => {
31
+ it('should return D1DrizzleConnection instance with the provided database', async () => {
32
32
  const adapter = new D1DrizzleAdapter(mockD1Database);
33
- expect(adapter.getConnection()).resolves.toBeInstanceOf(D1DrizzleConnection);
33
+ await expect(adapter.getConnection()).resolves.toBeInstanceOf(D1DrizzleConnection);
34
34
  });
35
35
  });
@@ -2,6 +2,7 @@ import { describe, it, expect, vi } from 'vitest';
2
2
  import { D1DrizzleConnection } from '../../src/Adapter/D1DrizzleConnection';
3
3
  import { drizzle } from 'drizzle-orm/d1';
4
4
  import type { D1Database } from '@cloudflare/workers-types';
5
+ import { D1TransactionUnsupportedError } from '../../src/Error/D1TransactionUnsupportedError';
5
6
 
6
7
  // Mock drizzle-orm/d1
7
8
  vi.mock('drizzle-orm/d1', () => ({
@@ -35,6 +36,15 @@ describe('D1DrizzleConnection', () => {
35
36
  it('should return the query client', async () => {
36
37
  const connection = new D1DrizzleConnection(mockD1Database);
37
38
  const client = connection.getClient();
38
- expect(client).toEqual({ mockedClient: true });
39
+ expect(client).toMatchObject({ mockedClient: true });
40
+ });
41
+
42
+ it('transaction() rejects with D1TransactionUnsupportedError and never runs the callback', async () => {
43
+ const connection = new D1DrizzleConnection(mockD1Database);
44
+ const callback = vi.fn();
45
+ const transactionPromise = (connection.getClient() as unknown as { transaction: (cb: typeof callback) => Promise<unknown> }).transaction(callback);
46
+ await expect(transactionPromise).rejects.toBeInstanceOf(D1TransactionUnsupportedError);
47
+ await expect(transactionPromise).rejects.toThrow('db.batch');
48
+ expect(callback).not.toHaveBeenCalled();
39
49
  });
40
50
  });
@@ -2,7 +2,7 @@ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
2
  import { D1DrizzleMigrator } from '../../src/Adapter/D1DrizzleMigrator';
3
3
  import * as migrator from 'drizzle-orm/d1/migrator';
4
4
  import type { TypeDrizzleMigratorConfig } from '@drizzle-adapter/core';
5
- import type { D1DrizzleClientInterface } from '../../src/Interface';
5
+ import type { D1DrizzleClientInterface } from '../../src';
6
6
 
7
7
  // Mock the migrate function from drizzle-orm/d1/migrator
8
8
  vi.mock('drizzle-orm/d1/migrator', () => ({
@@ -0,0 +1,12 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import * as packageEntry from '../../src/index';
3
+
4
+ describe('D1TransactionUnsupportedError', () => {
5
+ it('is exported from the package entry point', () => {
6
+ expect(packageEntry.D1TransactionUnsupportedError).toBeDefined();
7
+ const error = new packageEntry.D1TransactionUnsupportedError();
8
+ expect(error).toBeInstanceOf(Error);
9
+ expect(error.name).toBe('D1TransactionUnsupportedError');
10
+ expect(error.message).toContain('db.batch');
11
+ });
12
+ });
@@ -1,9 +1,9 @@
1
1
  import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- import { D1DrizzleAdapterFactory } from '../../src/Factory';
3
- import { D1DrizzleAdapter } from '../../src/Adapter';
2
+ import { D1DrizzleAdapterFactory } from '../../src';
3
+ import { D1DrizzleAdapter } from '../../src';
4
4
  import { drizzle } from 'drizzle-orm/d1';
5
5
  import type { D1Database } from '@cloudflare/workers-types';
6
- import type { TypeD1DatabaseConfig } from '../../src/Type';
6
+ import type { TypeD1DatabaseConfig } from '../../src';
7
7
 
8
8
  // Mock drizzle-orm/d1
9
9
  vi.mock('drizzle-orm/d1', () => ({
@@ -0,0 +1,77 @@
1
+ import type { D1Database } from '@cloudflare/workers-types';
2
+ import { sql } from 'drizzle-orm';
3
+ import type { DrizzleD1Database } from 'drizzle-orm/d1';
4
+ import { Miniflare } from 'miniflare';
5
+ import type { D1Database as MiniflareD1Database } from 'miniflare';
6
+ import { describe, it, expect, vi, beforeAll, afterAll } from 'vitest';
7
+ import { D1DrizzleConnection } from '../../src/Adapter/D1DrizzleConnection';
8
+ import { D1TransactionUnsupportedError } from '../../src/Error/D1TransactionUnsupportedError';
9
+
10
+ describe('D1 atomicity (real Miniflare D1)', () => {
11
+ let miniflare: Miniflare;
12
+ let miniflareDatabase: MiniflareD1Database;
13
+
14
+ beforeAll(async () => {
15
+ miniflare = new Miniflare({
16
+ modules: true,
17
+ script: 'export default {fetch(){return new Response("")}}',
18
+ d1Databases: ['DB']
19
+ });
20
+ miniflareDatabase = await miniflare.getD1Database('DB');
21
+ await miniflareDatabase.exec('CREATE TABLE probe (id INTEGER PRIMARY KEY, name TEXT UNIQUE)');
22
+ });
23
+
24
+ afterAll(async () => {
25
+ await miniflare.dispose();
26
+ });
27
+
28
+ // Real adapter client. batch() and run() exist on the drizzle D1 client at runtime
29
+ // but are not on D1DrizzleClientInterface, so the client is cast to DrizzleD1Database.
30
+ const buildAdapterClient = (): DrizzleD1Database =>
31
+ new D1DrizzleConnection(miniflareDatabase as unknown as D1Database).getClient() as unknown as DrizzleD1Database;
32
+
33
+ const countProbeRows = async (): Promise<number> => {
34
+ const result = await miniflareDatabase
35
+ .prepare('SELECT count(*) AS total FROM probe')
36
+ .first<{ total: number }>();
37
+ return Number(result?.total);
38
+ };
39
+
40
+ const clearProbeRows = async (): Promise<void> => {
41
+ await miniflareDatabase.exec('DELETE FROM probe');
42
+ };
43
+
44
+ it('transaction() rejects with D1TransactionUnsupportedError and never runs the callback', async () => {
45
+ await clearProbeRows();
46
+ const client = buildAdapterClient();
47
+ const callback = vi.fn(async () => 'ran');
48
+ await expect(
49
+ (client as unknown as { transaction: (work: typeof callback) => Promise<unknown> }).transaction(callback)
50
+ ).rejects.toBeInstanceOf(D1TransactionUnsupportedError);
51
+ expect(callback).not.toHaveBeenCalled();
52
+ expect(await countProbeRows()).toBe(0);
53
+ });
54
+
55
+ // drizzle-orm d1/session.js batch() forwards to native D1Database.batch, which is one implicit SQL transaction.
56
+ it('a batch with a UNIQUE violation is all-or-nothing', async () => {
57
+ await clearProbeRows();
58
+ const client = buildAdapterClient();
59
+ await expect(
60
+ client.batch([
61
+ client.run(sql`INSERT INTO probe (name) VALUES ('dup')`),
62
+ client.run(sql`INSERT INTO probe (name) VALUES ('dup')`)
63
+ ])
64
+ ).rejects.toThrow(/UNIQUE/);
65
+ expect(await countProbeRows()).toBe(0);
66
+ });
67
+
68
+ it('a batch of two distinct inserts commits both rows (control)', async () => {
69
+ await clearProbeRows();
70
+ const client = buildAdapterClient();
71
+ await client.batch([
72
+ client.run(sql`INSERT INTO probe (name) VALUES ('alpha')`),
73
+ client.run(sql`INSERT INTO probe (name) VALUES ('beta')`)
74
+ ]);
75
+ expect(await countProbeRows()).toBe(2);
76
+ });
77
+ });
package/tsconfig.json CHANGED
@@ -26,12 +26,7 @@
26
26
  // Some stricter flags (disabled by default)
27
27
  "noUnusedLocals": true,
28
28
  "noUnusedParameters": true,
29
- "noPropertyAccessFromIndexSignature": true,
30
-
31
- "baseUrl": ".",
32
- "paths": {
33
- "@drizzle-adapter/*": ["../*/dist"]
34
- }
29
+ "noPropertyAccessFromIndexSignature": true
35
30
  },
36
31
  "include": ["src/**/*.ts", "tests/worker.ts", "tests/worker.ts"],
37
32
  "exclude": ["node_modules", "dist", "tests"]
@@ -1,7 +0,0 @@
1
- import { SqliteCoreDrizzleDataTypes } from '@drizzle-adapter/sqlite-core';
2
- /**
3
- * D1 data types implementation for Drizzle ORM.
4
- * Extends the core SQLite data types with D1-specific functionality.
5
- */
6
- export declare class D1DrizzleDataTypes extends SqliteCoreDrizzleDataTypes {
7
- }
@@ -1,7 +0,0 @@
1
- import { SqliteCoreDrizzleDataTypes } from '@drizzle-adapter/sqlite-core';
2
- /**
3
- * D1 data types implementation for Drizzle ORM.
4
- * Extends the core SQLite data types with D1-specific functionality.
5
- */
6
- export class D1DrizzleDataTypes extends SqliteCoreDrizzleDataTypes {
7
- }
@@ -1,2 +0,0 @@
1
- export * from './D1DrizzleAdapter.js';
2
- export * from './D1DrizzleConnection.js';
@@ -1,2 +0,0 @@
1
- export * from './D1DrizzleAdapter.js';
2
- export * from './D1DrizzleConnection.js';
@@ -1,2 +0,0 @@
1
- export * from './D1DrizzleAdapterFactory.js';
2
- export * from './D1DrizzleDataTypesFactory.js';
@@ -1,2 +0,0 @@
1
- export * from './D1DrizzleAdapterFactory.js';
2
- export * from './D1DrizzleDataTypesFactory.js';
@@ -1 +0,0 @@
1
- export * from './D1DrizzleClientInterface.js';
@@ -1 +0,0 @@
1
- export * from './D1DrizzleClientInterface.js';
@@ -1 +0,0 @@
1
- export * from './Types.js';
@@ -1 +0,0 @@
1
- export * from './Types.js';
@@ -1,7 +0,0 @@
1
- import { SqliteCoreDrizzleDataTypes } from '@drizzle-adapter/sqlite-core';
2
-
3
- /**
4
- * D1 data types implementation for Drizzle ORM.
5
- * Extends the core SQLite data types with D1-specific functionality.
6
- */
7
- export class D1DrizzleDataTypes extends SqliteCoreDrizzleDataTypes {}
@@ -1,2 +0,0 @@
1
- export * from './D1DrizzleAdapter';
2
- export * from './D1DrizzleConnection';
@@ -1,2 +0,0 @@
1
- export * from './D1DrizzleAdapterFactory';
2
- export * from './D1DrizzleDataTypesFactory';
@@ -1 +0,0 @@
1
- export * from './D1DrizzleClientInterface';
package/src/Type/index.ts DELETED
@@ -1 +0,0 @@
1
- export * from './Types';