@drizzle-adapter/d1 1.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 (51) hide show
  1. package/.wrangler/state/v3/d1/miniflare-D1DatabaseObject/6e148680931b1bfcb993e0463aced3b0a6d6cf50af24dea2dcc2c3b2e9ae9e3b.sqlite +0 -0
  2. package/.wrangler/state/v3/d1/miniflare-D1DatabaseObject/6e148680931b1bfcb993e0463aced3b0a6d6cf50af24dea2dcc2c3b2e9ae9e3b.sqlite-shm +0 -0
  3. package/.wrangler/state/v3/d1/miniflare-D1DatabaseObject/6e148680931b1bfcb993e0463aced3b0a6d6cf50af24dea2dcc2c3b2e9ae9e3b.sqlite-wal +0 -0
  4. package/LICENSE +674 -0
  5. package/README.md +260 -0
  6. package/dist/Adapter/D1DrizzleAdapter.d.ts +32 -0
  7. package/dist/Adapter/D1DrizzleAdapter.js +41 -0
  8. package/dist/Adapter/D1DrizzleConnection.d.ts +29 -0
  9. package/dist/Adapter/D1DrizzleConnection.js +39 -0
  10. package/dist/Adapter/D1DrizzleDataTypes.d.ts +3 -0
  11. package/dist/Adapter/D1DrizzleDataTypes.js +3 -0
  12. package/dist/Adapter/index.d.ts +2 -0
  13. package/dist/Adapter/index.js +2 -0
  14. package/dist/Factory/D1DrizzleAdapterFactory.d.ts +12 -0
  15. package/dist/Factory/D1DrizzleAdapterFactory.js +12 -0
  16. package/dist/Factory/index.d.ts +1 -0
  17. package/dist/Factory/index.js +1 -0
  18. package/dist/Interface/D1DrizzleClientInterface.d.ts +13 -0
  19. package/dist/Interface/D1DrizzleClientInterface.js +1 -0
  20. package/dist/Interface/index.d.ts +1 -0
  21. package/dist/Interface/index.js +1 -0
  22. package/dist/Type/Types.d.ts +5 -0
  23. package/dist/Type/Types.js +1 -0
  24. package/dist/Type/index.d.ts +1 -0
  25. package/dist/Type/index.js +1 -0
  26. package/dist/index.d.ts +6 -0
  27. package/dist/index.js +7 -0
  28. package/eslint.config.mjs +6 -0
  29. package/package.json +34 -0
  30. package/src/Adapter/D1DrizzleAdapter.ts +50 -0
  31. package/src/Adapter/D1DrizzleConnection.ts +53 -0
  32. package/src/Adapter/D1DrizzleDataTypes.ts +3 -0
  33. package/src/Adapter/index.ts +2 -0
  34. package/src/Factory/D1DrizzleAdapterFactory.ts +17 -0
  35. package/src/Factory/index.ts +1 -0
  36. package/src/Interface/D1DrizzleClientInterface.ts +15 -0
  37. package/src/Interface/index.ts +1 -0
  38. package/src/Type/Types.ts +7 -0
  39. package/src/Type/index.ts +1 -0
  40. package/src/index.ts +8 -0
  41. package/tests/Adapter/D1DrizzleAdapter.test.ts +36 -0
  42. package/tests/Adapter/D1DrizzleConnection.test.ts +40 -0
  43. package/tests/Factory/D1DrizzleAdapterFactory.test.ts +41 -0
  44. package/tests/Integration/D1DrizzleAdapter.integration.test.ts +101 -0
  45. package/tests/Integration/D1DrizzleMigrations.integration.test.ts +100 -0
  46. package/tests/test-runner.ts +51 -0
  47. package/tests/worker.ts +334 -0
  48. package/tsconfig.eslint.json +4 -0
  49. package/tsconfig.json +38 -0
  50. package/vitest.config.ts +15 -0
  51. package/wrangler.toml +8 -0
@@ -0,0 +1,17 @@
1
+ import type { TypeD1DatabaseConfig } from '../Type/Types';
2
+ import { D1DrizzleAdapter } from '../Adapter/D1DrizzleAdapter';
3
+ import type { DrizzleAdapterFactoryInterface } from '@drizzle-adapter/core';
4
+
5
+ export class D1DrizzleAdapterFactory implements DrizzleAdapterFactoryInterface
6
+ {
7
+ /**
8
+ * Returns a adapter.
9
+ *
10
+ * @param {TypeD1DatabaseConfig} databaseConfig
11
+ * @returns {Promise<D1DrizzleAdapter>}
12
+ */
13
+ public async create(databaseConfig: TypeD1DatabaseConfig): Promise<D1DrizzleAdapter>
14
+ {
15
+ return new D1DrizzleAdapter(databaseConfig.DATABASE);
16
+ }
17
+ }
@@ -0,0 +1 @@
1
+ export * from './D1DrizzleAdapterFactory';
@@ -0,0 +1,15 @@
1
+ import type { AnyD1Database } from 'drizzle-orm/d1';
2
+ import type { DrizzleClientInterface } from '@drizzle-adapter/core';
3
+
4
+ export interface D1DrizzleClientInterface extends DrizzleClientInterface
5
+ {
6
+ execute(...args: any[]): ReturnType<AnyD1Database['run']>;
7
+ $with(...args: any[]): ReturnType<AnyD1Database['$with']>;
8
+ $count(...args: any[]): ReturnType<AnyD1Database['$count']>;
9
+ with(...args: any[]): ReturnType<AnyD1Database['with']>;
10
+ select(...args: any[]): ReturnType<AnyD1Database['select']>;
11
+ selectDistinct(...args: any[]): ReturnType<AnyD1Database['selectDistinct']>;
12
+ update(...args: any[]): ReturnType<AnyD1Database['update']>;
13
+ insert(...args: any[]): ReturnType<AnyD1Database['insert']>;
14
+ transaction(...args: any[]): ReturnType<AnyD1Database['transaction']>;
15
+ }
@@ -0,0 +1 @@
1
+ export * from './D1DrizzleClientInterface';
@@ -0,0 +1,7 @@
1
+ import type { D1Database } from '@cloudflare/workers-types';
2
+ import type { TypeDrizzleDatabaseConfig } from '@drizzle-adapter/core';
3
+
4
+ export interface TypeD1DatabaseConfig extends TypeDrizzleDatabaseConfig
5
+ {
6
+ readonly DATABASE: D1Database;
7
+ }
@@ -0,0 +1 @@
1
+ export * from './Types';
package/src/index.ts ADDED
@@ -0,0 +1,8 @@
1
+ export * from './Adapter';
2
+ export * from './Factory';
3
+ export * from './Interface';
4
+ export * from './Type';
5
+
6
+ // default export so that core factory can use it directly
7
+ import { D1DrizzleAdapterFactory } from './Factory';
8
+ export default D1DrizzleAdapterFactory;
@@ -0,0 +1,36 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { D1DrizzleAdapter } from '../../src/Adapter/D1DrizzleAdapter';
3
+ import { D1DrizzleConnection } from '../../src/Adapter/D1DrizzleConnection';
4
+ import { SqliteCoreDrizzleDataTypes } from '@drizzle-adapter/sqlite-core';
5
+ import type { D1Database } from '@cloudflare/workers-types';
6
+
7
+ describe('D1DrizzleAdapter', () => {
8
+ const mockD1Database: D1Database = {
9
+ prepare: vi.fn(),
10
+ batch: vi.fn(),
11
+ exec: vi.fn(),
12
+ dump: vi.fn()
13
+ };
14
+
15
+ it('should create an instance with a D1 database', () => {
16
+ const adapter = new D1DrizzleAdapter(mockD1Database);
17
+ expect(adapter).toBeInstanceOf(D1DrizzleAdapter);
18
+ });
19
+
20
+ it('should return sqlite as dialect', () => {
21
+ const adapter = new D1DrizzleAdapter(mockD1Database);
22
+ expect(adapter.getDialect()).toBe('sqlite');
23
+ });
24
+
25
+ it('should return SqliteCoreDrizzleDataTypes instance', () => {
26
+ const adapter = new D1DrizzleAdapter(mockD1Database);
27
+ const dataTypes = adapter.getDataTypes();
28
+ expect(dataTypes).toBeInstanceOf(SqliteCoreDrizzleDataTypes);
29
+ });
30
+
31
+ it('should return D1DrizzleConnection instance with the provided database', () => {
32
+ const adapter = new D1DrizzleAdapter(mockD1Database);
33
+ const connection = adapter.getConnection();
34
+ expect(connection).toBeInstanceOf(D1DrizzleConnection);
35
+ });
36
+ });
@@ -0,0 +1,40 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { D1DrizzleConnection } from '../../src/Adapter/D1DrizzleConnection';
3
+ import { drizzle } from 'drizzle-orm/d1';
4
+ import type { D1Database } from '@cloudflare/workers-types';
5
+
6
+ // Mock drizzle-orm/d1
7
+ vi.mock('drizzle-orm/d1', () => ({
8
+ drizzle: vi.fn().mockReturnValue({ mockedClient: true })
9
+ }));
10
+
11
+ describe('D1DrizzleConnection', () => {
12
+ const mockD1Database: D1Database = {
13
+ prepare: vi.fn(),
14
+ batch: vi.fn(),
15
+ exec: vi.fn(),
16
+ dump: vi.fn()
17
+ };
18
+
19
+ it('should create an instance with a D1 database and initialize drizzle', () => {
20
+ const connection = new D1DrizzleConnection(mockD1Database);
21
+ expect(connection).toBeInstanceOf(D1DrizzleConnection);
22
+ expect(drizzle).toHaveBeenCalledWith(mockD1Database);
23
+ });
24
+
25
+ it('should not perform any operation on connect (serverless)', async () => {
26
+ const connection = new D1DrizzleConnection(mockD1Database);
27
+ await expect(connection.connect()).resolves.toBeUndefined();
28
+ });
29
+
30
+ it('should not perform any operation on disconnect (serverless)', async () => {
31
+ const connection = new D1DrizzleConnection(mockD1Database);
32
+ await expect(connection.disconnect()).resolves.toBeUndefined();
33
+ });
34
+
35
+ it('should return the query client', async () => {
36
+ const connection = new D1DrizzleConnection(mockD1Database);
37
+ const client = await connection.getClient();
38
+ expect(client).toEqual({ mockedClient: true });
39
+ });
40
+ });
@@ -0,0 +1,41 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { D1DrizzleAdapterFactory } from '../../src/Factory';
3
+ import { D1DrizzleAdapter } from '../../src/Adapter';
4
+ import { drizzle } from 'drizzle-orm/d1';
5
+ import type { D1Database } from '@cloudflare/workers-types';
6
+ import type { TypeD1DatabaseConfig } from '../../src/Type';
7
+
8
+ // Mock drizzle-orm/d1
9
+ vi.mock('drizzle-orm/d1', () => ({
10
+ drizzle: vi.fn().mockReturnValue({ mockedQueryBuilder: true })
11
+ }));
12
+
13
+ describe('D1DrizzleAdapterFactory', () => {
14
+ const mockD1Database: D1Database = {
15
+ prepare: vi.fn(),
16
+ batch: vi.fn(),
17
+ exec: vi.fn(),
18
+ dump: vi.fn()
19
+ };
20
+
21
+ const mockDatabaseConfig: TypeD1DatabaseConfig = {
22
+ DATABASE_DRIVER: 'd1',
23
+ DATABASE_URL: mockD1Database
24
+ };
25
+
26
+ beforeEach(() => {
27
+ vi.clearAllMocks();
28
+ });
29
+
30
+ it('should be instantiable', () => {
31
+ const factory = new D1DrizzleAdapterFactory();
32
+ expect(factory).toBeInstanceOf(D1DrizzleAdapterFactory);
33
+ });
34
+
35
+ it('should create a D1DrizzleAdapter instance', async () => {
36
+ const factory = new D1DrizzleAdapterFactory();
37
+ const adapter = await factory.create(mockDatabaseConfig);
38
+
39
+ expect(adapter).toBeInstanceOf(D1DrizzleAdapter);
40
+ });
41
+ });
@@ -0,0 +1,101 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { getWorker, isReady, getWorkerUrl } from '../test-runner';
3
+
4
+ interface User {
5
+ id: number;
6
+ name: string;
7
+ email: string;
8
+ }
9
+
10
+ interface CreateRecordResponse {
11
+ success: boolean;
12
+ result: User;
13
+ }
14
+
15
+ interface UpdateRecordResponse {
16
+ success: boolean;
17
+ result: User;
18
+ }
19
+
20
+ interface SelectRecordsResponse {
21
+ success: boolean;
22
+ allUsers: User[];
23
+ specificUser: User;
24
+ }
25
+
26
+ describe('D1DrizzleAdapter Integration Tests', () => {
27
+ it('should create a new record', async () => {
28
+ if (!isReady()) {
29
+ console.log('Skipping test: worker not ready');
30
+ return;
31
+ }
32
+
33
+ const worker = getWorker();
34
+ const workerUrl = getWorkerUrl();
35
+ if (!worker || !workerUrl) {
36
+ throw new Error('Worker not available');
37
+ }
38
+
39
+ const response = await worker.fetch(`${workerUrl}/test/create-record`);
40
+ if (!response.ok) {
41
+ const error = await response.text();
42
+ console.error('Error response:', error);
43
+ }
44
+ expect(response.ok).toBe(true);
45
+
46
+ const data = await response.json() as CreateRecordResponse;
47
+ expect(data.success).toBe(true);
48
+ expect(data.result.name).toBe('John Doe');
49
+ expect(data.result.email).toBe('john@example.com');
50
+ });
51
+
52
+ it('should update an existing record', async () => {
53
+ if (!isReady()) {
54
+ console.log('Skipping test: worker not ready');
55
+ return;
56
+ }
57
+
58
+ const worker = getWorker();
59
+ const workerUrl = getWorkerUrl();
60
+ if (!worker || !workerUrl) {
61
+ throw new Error('Worker not available');
62
+ }
63
+
64
+ const response = await worker.fetch(`${workerUrl}/test/update-record`);
65
+ if (!response.ok) {
66
+ const error = await response.text();
67
+ console.error('Error response:', error);
68
+ }
69
+ expect(response.ok).toBe(true);
70
+
71
+ const data = await response.json() as UpdateRecordResponse;
72
+ expect(data.success).toBe(true);
73
+ expect(data.result.name).toBe('Jane Smith');
74
+ expect(data.result.email).toBe('jane@example.com');
75
+ });
76
+
77
+ it('should select records', async () => {
78
+ if (!isReady()) {
79
+ console.log('Skipping test: worker not ready');
80
+ return;
81
+ }
82
+
83
+ const worker = getWorker();
84
+ const workerUrl = getWorkerUrl();
85
+ if (!worker || !workerUrl) {
86
+ throw new Error('Worker not available');
87
+ }
88
+
89
+ const response = await worker.fetch(`${workerUrl}/test/select-records`);
90
+ if (!response.ok) {
91
+ const error = await response.text();
92
+ console.error('Error response:', error);
93
+ }
94
+ expect(response.ok).toBe(true);
95
+
96
+ const data = await response.json() as SelectRecordsResponse;
97
+ expect(data.success).toBe(true);
98
+ expect(data.allUsers.length).toBeGreaterThanOrEqual(2);
99
+ expect(data.specificUser.name).toBe('User 1');
100
+ });
101
+ });
@@ -0,0 +1,100 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { getWorker, isReady, getWorkerUrl } from '../test-runner';
3
+
4
+ interface EventAction {
5
+ id: number;
6
+ createdAt: number;
7
+ updatedAt: number;
8
+ action: string;
9
+ }
10
+
11
+ interface MigrationsResponse {
12
+ success: boolean;
13
+ initial: EventAction;
14
+ updated: EventAction;
15
+ }
16
+
17
+ interface ConstraintsResponse {
18
+ success: boolean;
19
+ }
20
+
21
+ interface TextTypeResponse {
22
+ success: boolean;
23
+ result: EventAction;
24
+ }
25
+
26
+ describe('D1Drizzle Migrations Integration Tests', () => {
27
+ it('should create a table with migrations', async () => {
28
+ if (!isReady()) {
29
+ console.log('Skipping test: worker not ready');
30
+ return;
31
+ }
32
+
33
+ const worker = getWorker();
34
+ const workerUrl = getWorkerUrl();
35
+ if (!worker || !workerUrl) {
36
+ throw new Error('Worker not available');
37
+ }
38
+
39
+ const response = await worker.fetch(`${workerUrl}/test/migrations`);
40
+ if (!response.ok) {
41
+ const error = await response.text();
42
+ console.error('Error response:', error);
43
+ }
44
+ expect(response.ok).toBe(true);
45
+
46
+ const data = await response.json() as MigrationsResponse;
47
+ expect(data.success).toBe(true);
48
+ expect(data.initial.action).toBe('TEST_ACTION');
49
+ expect(data.updated.action).toBe('UPDATED_ACTION');
50
+ expect(data.updated.updatedAt).toBeGreaterThan(data.initial.updatedAt);
51
+ });
52
+
53
+ it('should enforce NOT NULL constraints', async () => {
54
+ if (!isReady()) {
55
+ console.log('Skipping test: worker not ready');
56
+ return;
57
+ }
58
+
59
+ const worker = getWorker();
60
+ const workerUrl = getWorkerUrl();
61
+ if (!worker || !workerUrl) {
62
+ throw new Error('Worker not available');
63
+ }
64
+
65
+ const response = await worker.fetch(`${workerUrl}/test/constraints`);
66
+ if (!response.ok) {
67
+ const error = await response.text();
68
+ console.error('Error response:', error);
69
+ }
70
+ expect(response.ok).toBe(true);
71
+
72
+ const data = await response.json() as ConstraintsResponse;
73
+ expect(data.success).toBe(true);
74
+ });
75
+
76
+ it('should handle TEXT data type properly', async () => {
77
+ if (!isReady()) {
78
+ console.log('Skipping test: worker not ready');
79
+ return;
80
+ }
81
+
82
+ const worker = getWorker();
83
+ const workerUrl = getWorkerUrl();
84
+ if (!worker || !workerUrl) {
85
+ throw new Error('Worker not available');
86
+ }
87
+
88
+ const response = await worker.fetch(`${workerUrl}/test/text-type`);
89
+ if (!response.ok) {
90
+ const error = await response.text();
91
+ console.error('Error response:', error);
92
+ }
93
+ expect(response.ok).toBe(true);
94
+
95
+ const data = await response.json() as TextTypeResponse;
96
+ expect(data.success).toBe(true);
97
+ expect(data.result.action.length).toBe(1000);
98
+ expect(data.result.action).toBe('A'.repeat(1000));
99
+ });
100
+ });
@@ -0,0 +1,51 @@
1
+ import { unstable_dev } from 'wrangler';
2
+ import type { UnstableDevWorker } from 'wrangler';
3
+ import { beforeAll, afterAll } from 'vitest';
4
+
5
+ let worker: UnstableDevWorker | null = null;
6
+ let isWorkerReady = false;
7
+ let workerUrl: string | null = null;
8
+
9
+ beforeAll(async () => {
10
+ try {
11
+ // Start worker with D1 binding
12
+ worker = await unstable_dev('tests/worker.ts', {
13
+ experimental: { disableExperimentalWarning: true },
14
+ local: true
15
+ });
16
+
17
+ // Store worker URL
18
+ workerUrl = `http://${worker.address}:${worker.port}`;
19
+
20
+ // Test if worker is ready
21
+ const response = await worker.fetch('/__d1');
22
+ if (response.ok) {
23
+ isWorkerReady = true;
24
+ console.log('Worker ready');
25
+ }
26
+ } catch (error) {
27
+ console.error('Failed to start worker:', error);
28
+ }
29
+ });
30
+
31
+ afterAll(async () => {
32
+ if (worker) {
33
+ await worker.stop();
34
+ console.log('Worker stopped');
35
+ }
36
+ });
37
+
38
+ // Export function to get worker status
39
+ export function isReady(): boolean {
40
+ return isWorkerReady;
41
+ }
42
+
43
+ // Export function to get worker
44
+ export function getWorker(): UnstableDevWorker | null {
45
+ return worker;
46
+ }
47
+
48
+ // Export function to get worker URL
49
+ export function getWorkerUrl(): string | null {
50
+ return workerUrl;
51
+ }