@gblikas/querykit 0.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 (118) hide show
  1. package/.cursor/BUGBOT.md +21 -0
  2. package/.cursor/rules/01-project-structure.mdc +77 -0
  3. package/.cursor/rules/02-typescript-standards.mdc +105 -0
  4. package/.cursor/rules/03-testing-standards.mdc +78 -0
  5. package/.cursor/rules/04-query-language.mdc +79 -0
  6. package/.cursor/rules/05-solid-principles.mdc +118 -0
  7. package/.cursor/rules/liqe-readme-docs.mdc +438 -0
  8. package/.devcontainer/devcontainer.json +25 -0
  9. package/.eslintignore +1 -0
  10. package/.eslintrc.js +39 -0
  11. package/.github/dependabot.yml +12 -0
  12. package/.github/workflows/ci.yml +114 -0
  13. package/.github/workflows/publish.yml +61 -0
  14. package/.husky/pre-commit +30 -0
  15. package/.prettierrc +10 -0
  16. package/CONTRIBUTING.md +187 -0
  17. package/LICENSE +674 -0
  18. package/README.md +237 -0
  19. package/dist/adapters/drizzle/index.d.ts +122 -0
  20. package/dist/adapters/drizzle/index.js +166 -0
  21. package/dist/adapters/index.d.ts +7 -0
  22. package/dist/adapters/index.js +25 -0
  23. package/dist/adapters/types.d.ts +60 -0
  24. package/dist/adapters/types.js +8 -0
  25. package/dist/index.d.ts +75 -0
  26. package/dist/index.js +118 -0
  27. package/dist/parser/index.d.ts +2 -0
  28. package/dist/parser/index.js +18 -0
  29. package/dist/parser/parser.d.ts +51 -0
  30. package/dist/parser/parser.js +201 -0
  31. package/dist/parser/types.d.ts +68 -0
  32. package/dist/parser/types.js +5 -0
  33. package/dist/query/builder.d.ts +61 -0
  34. package/dist/query/builder.js +188 -0
  35. package/dist/query/index.d.ts +2 -0
  36. package/dist/query/index.js +18 -0
  37. package/dist/query/types.d.ts +79 -0
  38. package/dist/query/types.js +2 -0
  39. package/dist/security/index.d.ts +2 -0
  40. package/dist/security/index.js +18 -0
  41. package/dist/security/types.d.ts +181 -0
  42. package/dist/security/types.js +43 -0
  43. package/dist/security/validator.d.ts +191 -0
  44. package/dist/security/validator.js +344 -0
  45. package/dist/translators/drizzle/index.d.ts +73 -0
  46. package/dist/translators/drizzle/index.js +260 -0
  47. package/dist/translators/index.d.ts +8 -0
  48. package/dist/translators/index.js +27 -0
  49. package/dist/translators/sql/index.d.ts +108 -0
  50. package/dist/translators/sql/index.js +252 -0
  51. package/dist/translators/types.d.ts +39 -0
  52. package/dist/translators/types.js +8 -0
  53. package/examples/qk-next/README.md +35 -0
  54. package/examples/qk-next/app/favicon.ico +0 -0
  55. package/examples/qk-next/app/globals.css +122 -0
  56. package/examples/qk-next/app/layout.tsx +121 -0
  57. package/examples/qk-next/app/page.tsx +813 -0
  58. package/examples/qk-next/app/providers.tsx +80 -0
  59. package/examples/qk-next/components/aurora-background.tsx +12 -0
  60. package/examples/qk-next/components/github-stars.tsx +51 -0
  61. package/examples/qk-next/components/mode-toggle.tsx +27 -0
  62. package/examples/qk-next/components/reactbits/blocks/Backgrounds/Aurora/Aurora.tsx +217 -0
  63. package/examples/qk-next/components/reactbits/blocks/Backgrounds/LightRays/LightRays.tsx +474 -0
  64. package/examples/qk-next/components/theme-provider.tsx +11 -0
  65. package/examples/qk-next/components/ui/card.tsx +92 -0
  66. package/examples/qk-next/components/ui/command.tsx +184 -0
  67. package/examples/qk-next/components/ui/dialog.tsx +143 -0
  68. package/examples/qk-next/components/ui/drawer.tsx +135 -0
  69. package/examples/qk-next/components/ui/hover-card.tsx +44 -0
  70. package/examples/qk-next/components/ui/icons.tsx +148 -0
  71. package/examples/qk-next/components/ui/sonner.tsx +26 -0
  72. package/examples/qk-next/components/ui/table.tsx +117 -0
  73. package/examples/qk-next/components.json +21 -0
  74. package/examples/qk-next/eslint.config.mjs +21 -0
  75. package/examples/qk-next/jsrepo.json +13 -0
  76. package/examples/qk-next/lib/utils.ts +6 -0
  77. package/examples/qk-next/next.config.ts +8 -0
  78. package/examples/qk-next/package.json +48 -0
  79. package/examples/qk-next/pnpm-lock.yaml +5558 -0
  80. package/examples/qk-next/postcss.config.mjs +5 -0
  81. package/examples/qk-next/public/file.svg +1 -0
  82. package/examples/qk-next/public/globe.svg +1 -0
  83. package/examples/qk-next/public/next.svg +1 -0
  84. package/examples/qk-next/public/vercel.svg +1 -0
  85. package/examples/qk-next/public/window.svg +1 -0
  86. package/examples/qk-next/tsconfig.json +42 -0
  87. package/examples/qk-next/types/sonner.d.ts +3 -0
  88. package/jest.config.js +26 -0
  89. package/package.json +51 -0
  90. package/src/adapters/drizzle/drizzle-adapter.test.ts +115 -0
  91. package/src/adapters/drizzle/index.ts +299 -0
  92. package/src/adapters/index.ts +11 -0
  93. package/src/adapters/types.ts +72 -0
  94. package/src/index.ts +194 -0
  95. package/src/integration.test.ts +202 -0
  96. package/src/parser/index.ts +2 -0
  97. package/src/parser/parser.test.ts +1056 -0
  98. package/src/parser/parser.ts +268 -0
  99. package/src/parser/types.ts +97 -0
  100. package/src/query/builder.test.ts +272 -0
  101. package/src/query/builder.ts +274 -0
  102. package/src/query/index.ts +2 -0
  103. package/src/query/types.ts +107 -0
  104. package/src/security/index.ts +2 -0
  105. package/src/security/types.ts +210 -0
  106. package/src/security/validator.test.ts +459 -0
  107. package/src/security/validator.ts +395 -0
  108. package/src/security.test.ts +366 -0
  109. package/src/translators/drizzle/drizzle-translator.test.ts +128 -0
  110. package/src/translators/drizzle/index.test.ts +45 -0
  111. package/src/translators/drizzle/index.ts +346 -0
  112. package/src/translators/index.ts +14 -0
  113. package/src/translators/sql/index.test.ts +45 -0
  114. package/src/translators/sql/index.ts +331 -0
  115. package/src/translators/sql/sql-translator.test.ts +419 -0
  116. package/src/translators/types.ts +44 -0
  117. package/src/types/sonner.d.ts +3 -0
  118. package/tsconfig.json +34 -0
@@ -0,0 +1,5 @@
1
+ const config = {
2
+ plugins: ["@tailwindcss/postcss"],
3
+ };
4
+
5
+ export default config;
@@ -0,0 +1 @@
1
+ <svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
@@ -0,0 +1 @@
1
+ <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
@@ -0,0 +1 @@
1
+ <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>
@@ -0,0 +1 @@
1
+ <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
@@ -0,0 +1,42 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2017",
4
+ "lib": [
5
+ "dom",
6
+ "dom.iterable",
7
+ "esnext"
8
+ ],
9
+ "allowJs": true,
10
+ "skipLibCheck": true,
11
+ "strict": true,
12
+ "noEmit": true,
13
+ "esModuleInterop": true,
14
+ "module": "esnext",
15
+ "moduleResolution": "bundler",
16
+ "resolveJsonModule": true,
17
+ "isolatedModules": true,
18
+ "jsx": "preserve",
19
+ "incremental": true,
20
+ "plugins": [
21
+ {
22
+ "name": "next"
23
+ }
24
+ ],
25
+ "paths": {
26
+ "@/*": [
27
+ "./*"
28
+ ],
29
+ },
30
+ "baseUrl": "."
31
+ },
32
+ "include": [
33
+ "next-env.d.ts",
34
+ "**/*.ts",
35
+ "**/*.tsx",
36
+ "types/**/*.d.ts",
37
+ ".next/types/**/*.ts"
38
+ ],
39
+ "exclude": [
40
+ "node_modules"
41
+ ]
42
+ }
@@ -0,0 +1,3 @@
1
+ declare module 'sonner' {
2
+ export function toast(message: string): void;
3
+ }
package/jest.config.js ADDED
@@ -0,0 +1,26 @@
1
+ /** @type {import('ts-jest').JestConfigWithTsJest} */
2
+ module.exports = {
3
+ preset: 'ts-jest',
4
+ testEnvironment: 'node',
5
+ roots: ['<rootDir>/src'],
6
+ testMatch: ['**/__tests__/**/*.ts?(x)', '**/?(*.)+(spec|test).ts?(x)'],
7
+ transform: {
8
+ '^.+\\.tsx?$': [
9
+ 'ts-jest',
10
+ {
11
+ diagnostics: {
12
+ ignoreCodes: [6133, 6196, 2322]
13
+ }
14
+ }
15
+ ]
16
+ },
17
+ verbose: true,
18
+ collectCoverage: true,
19
+ collectCoverageFrom: [
20
+ 'src/**/*.ts',
21
+ '!src/**/*.test.ts',
22
+ '!src/**/*.spec.ts',
23
+ '!src/**/index.ts',
24
+ '!src/types/**/*.ts'
25
+ ]
26
+ };
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@gblikas/querykit",
3
+ "version": "0.0.0",
4
+ "description": "A comprehensive query toolkit for TypeScript that simplifies building and executing data queries across different environments",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "lint-staged": {
8
+ "*.{ts,tsx}": [
9
+ "eslint --fix",
10
+ "prettier --write"
11
+ ]
12
+ },
13
+ "keywords": [
14
+ "query",
15
+ "filter",
16
+ "typescript",
17
+ "orm",
18
+ "database",
19
+ "drizzle"
20
+ ],
21
+ "author": "",
22
+ "license": "MIT",
23
+ "publishConfig": {
24
+ "access": "public"
25
+ },
26
+ "dependencies": {
27
+ "drizzle-orm": "^0.30.2",
28
+ "liqe": "^3.3.0"
29
+ },
30
+ "devDependencies": {
31
+ "@types/jest": "^29.5.12",
32
+ "@types/node": "^20.11.24",
33
+ "@typescript-eslint/eslint-plugin": "^7.1.0",
34
+ "@typescript-eslint/parser": "^7.1.0",
35
+ "eslint": "^8.57.0",
36
+ "eslint-config-prettier": "^9.1.0",
37
+ "husky": "^9.0.11",
38
+ "jest": "^29.7.0",
39
+ "lint-staged": "^15.5.1",
40
+ "prettier": "^3.2.5",
41
+ "ts-jest": "^29.1.2",
42
+ "typescript": "^5.3.3"
43
+ },
44
+ "scripts": {
45
+ "build": "tsc --outDir dist",
46
+ "test": "jest",
47
+ "lint": "eslint src --ext .ts",
48
+ "format": "prettier --write \"src/**/*.ts\"",
49
+ "lint-staged": "lint-staged"
50
+ }
51
+ }
@@ -0,0 +1,115 @@
1
+ import { DrizzleAdapter, IDrizzleDatabase } from './index';
2
+ import { QueryParser } from '../../parser';
3
+ import { QueryExpression } from '../../parser/types';
4
+ import { SQLWrapper, sql } from 'drizzle-orm';
5
+
6
+ // Create mocks
7
+ const mockWhere = jest.fn().mockReturnThis();
8
+ const mockOrderBy = jest.fn().mockReturnThis();
9
+ const mockLimit = jest.fn().mockReturnThis();
10
+ const mockOffset = jest.fn().mockReturnThis();
11
+ const mockFrom = jest.fn().mockReturnValue({
12
+ where: mockWhere,
13
+ orderBy: mockOrderBy,
14
+ limit: mockLimit,
15
+ offset: mockOffset,
16
+ then: <T>(callback: (value: unknown[]) => T) =>
17
+ Promise.resolve(callback([{ id: 1, title: 'Test Todo' }]))
18
+ });
19
+ const mockSelect = jest.fn().mockReturnValue({ from: mockFrom });
20
+
21
+ // Create a mock DB instance
22
+ const mockDb: IDrizzleDatabase = {
23
+ select: mockSelect
24
+ };
25
+
26
+ // Create mock schema with SQLWrapper fields
27
+ const mockSchema = {
28
+ todos: {
29
+ id: sql.raw('id') as unknown as SQLWrapper,
30
+ title: sql.raw('title') as unknown as SQLWrapper,
31
+ priority: sql.raw('priority') as unknown as SQLWrapper,
32
+ status: sql.raw('status') as unknown as SQLWrapper
33
+ }
34
+ };
35
+
36
+ describe('DrizzleAdapter', () => {
37
+ let adapter: DrizzleAdapter;
38
+ let parser: QueryParser;
39
+
40
+ beforeEach(() => {
41
+ // Reset mocks
42
+ jest.clearAllMocks();
43
+
44
+ // Create fresh instances
45
+ adapter = new DrizzleAdapter();
46
+ parser = new QueryParser();
47
+
48
+ // Initialize the adapter
49
+ adapter.initialize({
50
+ db: mockDb,
51
+ schema: mockSchema
52
+ });
53
+ });
54
+
55
+ describe('execute', () => {
56
+ it('should execute a simple query', async () => {
57
+ const expression = parser.parse('title:"Test Todo"');
58
+ const result = await adapter.execute('todos', expression);
59
+
60
+ expect(mockSelect).toHaveBeenCalled();
61
+ expect(mockFrom).toHaveBeenCalled();
62
+ expect(mockWhere).toHaveBeenCalled();
63
+ expect(result).toEqual([{ id: 1, title: 'Test Todo' }]);
64
+ });
65
+
66
+ it('should support ordering', async () => {
67
+ const expression = parser.parse('status:"active"');
68
+ await adapter.execute('todos', expression, {
69
+ orderBy: { title: 'asc' }
70
+ });
71
+
72
+ expect(mockOrderBy).toHaveBeenCalled();
73
+ });
74
+
75
+ it('should support limit', async () => {
76
+ const expression = parser.parse('status:"active"');
77
+ await adapter.execute('todos', expression, {
78
+ limit: 10
79
+ });
80
+
81
+ expect(mockLimit).toHaveBeenCalledWith(10);
82
+ });
83
+
84
+ it('should support offset', async () => {
85
+ const expression = parser.parse('status:"active"');
86
+ await adapter.execute('todos', expression, {
87
+ offset: 20
88
+ });
89
+
90
+ expect(mockOffset).toHaveBeenCalledWith(20);
91
+ });
92
+
93
+ it('should throw error if table not found', async () => {
94
+ const expression = parser.parse('status:"active"');
95
+
96
+ await expect(
97
+ adapter.execute('unknown_table', expression)
98
+ ).rejects.toThrow('Table unknown_table not found in schema');
99
+ });
100
+ });
101
+
102
+ describe('canExecute', () => {
103
+ it('should return true for valid expressions', () => {
104
+ const expression = parser.parse('status:"active"');
105
+ expect(adapter.canExecute(expression)).toBe(true);
106
+ });
107
+
108
+ it('should return false for invalid expressions', () => {
109
+ const invalidExpression = {
110
+ type: 'unsupported'
111
+ } as unknown as QueryExpression;
112
+ expect(adapter.canExecute(invalidExpression)).toBe(false);
113
+ });
114
+ });
115
+ });
@@ -0,0 +1,299 @@
1
+ /**
2
+ * Drizzle ORM Adapter for QueryKit
3
+ *
4
+ * This adapter connects QueryKit to Drizzle ORM for database queries.
5
+ */
6
+
7
+ import { DrizzleTranslator } from '../../translators/drizzle';
8
+ import { IAdapter, IAdapterOptions, IQueryExecutionOptions } from '../types';
9
+ import { QueryExpression } from '../../parser/types';
10
+ import { SQL, SQLWrapper, asc, desc, sql } from 'drizzle-orm';
11
+ import { createQueryKit, QueryKit } from '../../index';
12
+ /**
13
+ * Type for Drizzle ORM database instance
14
+ */
15
+ export interface IDrizzleDatabase {
16
+ select: () => { from: (table: unknown) => IDrizzleQueryBuilder };
17
+ }
18
+
19
+ /**
20
+ * Type for Drizzle query builder
21
+ */
22
+ export interface IDrizzleQueryBuilder {
23
+ where: (condition: SQL) => IDrizzleQueryBuilder;
24
+ orderBy: (...clauses: SQL[]) => IDrizzleQueryBuilder;
25
+ limit: (limit: number) => IDrizzleQueryBuilder;
26
+ offset: (offset: number) => IDrizzleQueryBuilder;
27
+ // This is already a Promise due to Drizzle's thenable implementation
28
+ [Symbol.toStringTag]: string;
29
+ then<TResult1 = unknown, TResult2 = never>(
30
+ onfulfilled?:
31
+ | ((value: unknown[]) => TResult1 | PromiseLike<TResult1>)
32
+ | null,
33
+ onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null
34
+ ): Promise<TResult1 | TResult2>;
35
+ }
36
+
37
+ /**
38
+ * Options specific to the Drizzle adapter
39
+ */
40
+ export interface IDrizzleAdapterOptions<
41
+ TSchema extends Record<string, unknown> = Record<string, unknown>
42
+ > extends IAdapterOptions {
43
+ /**
44
+ * The Drizzle ORM database instance
45
+ */
46
+ db: IDrizzleDatabase | unknown;
47
+
48
+ /**
49
+ * Schema information with Drizzle table definitions
50
+ */
51
+ schema: TSchema;
52
+
53
+ /**
54
+ * Whether to normalize field names (e.g., lowercase them)
55
+ */
56
+ normalizeFieldNames?: boolean;
57
+ }
58
+
59
+ /**
60
+ * Options for Drizzle query execution
61
+ */
62
+ export interface IDrizzleQueryExecutionOptions extends IQueryExecutionOptions {
63
+ /**
64
+ * Sort fields in the format: { field: 'asc' | 'desc' }
65
+ */
66
+ orderBy?: Record<string, 'asc' | 'desc'>;
67
+
68
+ /**
69
+ * Maximum number of records to return
70
+ */
71
+ limit?: number;
72
+
73
+ /**
74
+ * Number of records to skip
75
+ */
76
+ offset?: number;
77
+ }
78
+
79
+ /**
80
+ * Error thrown when adapter operations fail
81
+ */
82
+ export class DrizzleAdapterError extends Error {
83
+ constructor(message: string) {
84
+ super(message);
85
+ this.name = 'DrizzleAdapterError';
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Adapter for Drizzle ORM
91
+ */
92
+ export class DrizzleAdapter<
93
+ TSchema extends Record<string, unknown> = Record<string, unknown>
94
+ > implements IAdapter<IDrizzleAdapterOptions<TSchema>>
95
+ {
96
+ private db!: unknown;
97
+ private schema!: TSchema;
98
+ private translator!: DrizzleTranslator;
99
+ private initialized: boolean = false;
100
+
101
+ /**
102
+ * Optionally initialize via constructor for convenience
103
+ */
104
+ constructor(options?: IDrizzleAdapterOptions<TSchema>) {
105
+ if (options) {
106
+ this.initialize(options);
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Initialize the adapter with options
112
+ */
113
+ public initialize(options: IDrizzleAdapterOptions<TSchema>): void {
114
+ if (!options.db) {
115
+ throw new DrizzleAdapterError('Drizzle db instance is required');
116
+ }
117
+
118
+ if (!options.schema) {
119
+ throw new DrizzleAdapterError('Schema definition is required');
120
+ }
121
+
122
+ this.db = options.db;
123
+ this.schema = options.schema;
124
+ this.translator = new DrizzleTranslator({
125
+ normalizeFieldNames: options.normalizeFieldNames,
126
+ fieldMappings: options.fieldMappings,
127
+ schema: options.schema as unknown as Record<
128
+ string,
129
+ Record<string, unknown>
130
+ >
131
+ });
132
+
133
+ this.initialized = true;
134
+ }
135
+
136
+ /**
137
+ * Execute a QueryKit expression using Drizzle ORM
138
+ */
139
+ public async execute<TResult = unknown>(
140
+ tableName: string,
141
+ expression: QueryExpression,
142
+ options?: IDrizzleQueryExecutionOptions
143
+ ): Promise<TResult[]> {
144
+ this.ensureInitialized();
145
+
146
+ const table = this.getTable(tableName);
147
+
148
+ if (!table) {
149
+ throw new DrizzleAdapterError(`Table ${tableName} not found in schema`);
150
+ }
151
+
152
+ try {
153
+ // Start with a base query
154
+ let query = (this.db as IDrizzleDatabase).select().from(table);
155
+
156
+ // Add where condition if expression is provided
157
+ if (expression) {
158
+ const condition = this.translator.translate(expression);
159
+ query = query.where(condition);
160
+ }
161
+
162
+ // Add ordering if specified
163
+ if (options?.orderBy) {
164
+ const orderClauses: SQL[] = [];
165
+
166
+ Object.entries(options.orderBy).forEach(([field, direction]) => {
167
+ // Try to find the field in the schema
168
+ const schemaField = this.getSchemaField(tableName, field);
169
+
170
+ if (schemaField) {
171
+ // If field exists in schema, use it directly
172
+ orderClauses.push(
173
+ direction === 'asc' ? asc(schemaField) : desc(schemaField)
174
+ );
175
+ } else {
176
+ // Otherwise use raw SQL
177
+ orderClauses.push(
178
+ sql`${sql.identifier(field)} ${sql.raw(direction.toUpperCase())}`
179
+ );
180
+ }
181
+ });
182
+
183
+ if (orderClauses.length > 0) {
184
+ query = query.orderBy(...orderClauses);
185
+ }
186
+ }
187
+
188
+ // Add limit if specified
189
+ if (options?.limit !== undefined) {
190
+ query = query.limit(options.limit);
191
+ }
192
+
193
+ // Add offset if specified
194
+ if (options?.offset !== undefined) {
195
+ query = query.offset(options.offset);
196
+ }
197
+
198
+ // Execute the query
199
+ const result = await query;
200
+ return result as TResult[];
201
+ } catch (error) {
202
+ throw new DrizzleAdapterError(
203
+ `Failed to execute query: ${error instanceof Error ? error.message : String(error)}`
204
+ );
205
+ }
206
+ }
207
+
208
+ /**
209
+ * Check if an expression can be executed by this adapter
210
+ */
211
+ public canExecute(expression: QueryExpression): boolean {
212
+ try {
213
+ this.ensureInitialized();
214
+ return this.translator.canTranslate(expression);
215
+ } catch {
216
+ return false;
217
+ }
218
+ }
219
+
220
+ /**
221
+ * Get a table from the schema
222
+ */
223
+ private getTable(tableName: string): unknown {
224
+ return (this.schema as Record<string, unknown>)[tableName as string];
225
+ }
226
+
227
+ /**
228
+ * Get a field from the schema
229
+ */
230
+ private getSchemaField(
231
+ tableName: string,
232
+ fieldName: string
233
+ ): SQLWrapper | null {
234
+ const schemaAsColumns = this.schema as unknown as Record<
235
+ string,
236
+ Record<string, SQLWrapper>
237
+ >;
238
+ const table = schemaAsColumns[tableName];
239
+ if (table && fieldName in table) {
240
+ return table[fieldName];
241
+ }
242
+ return null;
243
+ }
244
+
245
+ /**
246
+ * Ensure the adapter is initialized
247
+ */
248
+ private ensureInitialized(): void {
249
+ if (!this.initialized) {
250
+ throw new DrizzleAdapterError('Adapter has not been initialized');
251
+ }
252
+ }
253
+ }
254
+
255
+ /**
256
+ * Convenience factory to create a pre-initialized Drizzle adapter
257
+ */
258
+ export function drizzleAdapter<TSchema extends Record<string, unknown>>(
259
+ options: IDrizzleAdapterOptions<TSchema>
260
+ ): DrizzleAdapter<TSchema> {
261
+ return new DrizzleAdapter<TSchema>(options);
262
+ }
263
+
264
+ // Helper types and factory for zero-cast DX with Drizzle tables
265
+ export type RowTypeFromDrizzleTable<TTable> = TTable extends {
266
+ $inferSelect: infer R;
267
+ }
268
+ ? R
269
+ : unknown;
270
+
271
+ export type RowMapFromDrizzleSchema<TSchema extends Record<string, unknown>> = {
272
+ [K in keyof TSchema]: RowTypeFromDrizzleTable<TSchema[K]>;
273
+ };
274
+
275
+ export function createDrizzleQueryKit<
276
+ TSchema extends Record<string, object>
277
+ >(args: {
278
+ db: unknown;
279
+ schema: TSchema;
280
+ normalizeFieldNames?: boolean;
281
+ fieldMappings?: Record<string, string>;
282
+ security?: import('../../security').ISecurityOptions;
283
+ }): QueryKit<TSchema, RowMapFromDrizzleSchema<TSchema>> {
284
+ const adapter = new DrizzleAdapter<TSchema>();
285
+ adapter.initialize({
286
+ db: args.db,
287
+ schema: args.schema,
288
+ normalizeFieldNames: args.normalizeFieldNames,
289
+ fieldMappings: args.fieldMappings
290
+ });
291
+
292
+ type RowMap = RowMapFromDrizzleSchema<TSchema>;
293
+
294
+ return createQueryKit<TSchema, RowMap>({
295
+ adapter,
296
+ schema: args.schema as unknown as TSchema,
297
+ security: args.security
298
+ });
299
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * QueryKit Adapters
3
+ *
4
+ * Exports all adapter implementations for different database systems.
5
+ */
6
+
7
+ // Export base types
8
+ export * from './types';
9
+
10
+ // Export Drizzle adapter
11
+ export * from './drizzle';
@@ -0,0 +1,72 @@
1
+ /**
2
+ * QueryKit Adapter Types
3
+ *
4
+ * These are the core interfaces for adapters, which connect QueryKit to
5
+ * external systems or libraries like Drizzle ORM.
6
+ */
7
+
8
+ import { QueryExpression } from '../parser/types';
9
+
10
+ /**
11
+ * Options for configuring an adapter
12
+ */
13
+ export interface IAdapterOptions {
14
+ /**
15
+ * Schema information for type safety and validation
16
+ */
17
+ schema?: Record<string, unknown>;
18
+
19
+ /**
20
+ * Field mappings from QueryKit fields to target database fields
21
+ */
22
+ fieldMappings?: Record<string, string>;
23
+ }
24
+
25
+ /**
26
+ * Options for a query execution
27
+ */
28
+ export interface IQueryExecutionOptions {
29
+ /**
30
+ * Optional transaction object
31
+ */
32
+ transaction?: unknown;
33
+
34
+ /**
35
+ * Additional parameters specific to the adapter
36
+ */
37
+ [key: string]: unknown;
38
+ }
39
+
40
+ /**
41
+ * Interface for a query adapter
42
+ */
43
+ export interface IAdapter<TOptions extends IAdapterOptions = IAdapterOptions> {
44
+ /**
45
+ * Initialize the adapter with options
46
+ *
47
+ * @param options Adapter-specific options
48
+ */
49
+ initialize(options: TOptions): void;
50
+
51
+ /**
52
+ * Execute a QueryKit expression and return results
53
+ *
54
+ * @param tableName The table/collection name to query
55
+ * @param expression The QueryKit expression to execute
56
+ * @param options Optional execution options
57
+ * @returns The query results
58
+ */
59
+ execute<T = unknown>(
60
+ tableName: string,
61
+ expression: QueryExpression,
62
+ options?: IQueryExecutionOptions
63
+ ): Promise<T[]>;
64
+
65
+ /**
66
+ * Check if an expression can be executed by this adapter
67
+ *
68
+ * @param expression The QueryKit expression to check
69
+ * @returns true if the expression can be executed, false otherwise
70
+ */
71
+ canExecute(expression: QueryExpression): boolean;
72
+ }