@lunarhue/expo-wa-sqlite 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/.claude/settings.local.json +9 -0
- package/.lovely-docs.yaml +11 -0
- package/LICENSE +21 -0
- package/demo/next.config.mjs +17 -0
- package/demo/package.json +26 -0
- package/demo/scripts/copy-wasm.mjs +14 -0
- package/demo/src/app/layout.tsx +16 -0
- package/demo/src/app/page.tsx +16 -0
- package/demo/src/app/todos.tsx +118 -0
- package/demo/src/db/schema.ts +7 -0
- package/demo/tsconfig.json +18 -0
- package/lovely-docs/drizzle-orm/arktype.md +113 -0
- package/lovely-docs/drizzle-orm/batch-api.md +35 -0
- package/lovely-docs/drizzle-orm/cache.md +145 -0
- package/lovely-docs/drizzle-orm/check-migrations.md +52 -0
- package/lovely-docs/drizzle-orm/column_types/mysql-column-types.md +76 -0
- package/lovely-docs/drizzle-orm/column_types/postgresql_column_types.md +314 -0
- package/lovely-docs/drizzle-orm/column_types/singlestore-column-types.md +171 -0
- package/lovely-docs/drizzle-orm/column_types/sqlite_column_types.md +132 -0
- package/lovely-docs/drizzle-orm/column_types.md +76 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/case-insensitive-unique-email.md +113 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/conditional-filters-in-query.md +69 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/count-rows.md +76 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/cursor-based-pagination.md +142 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/d1_http_api_configuration.md +49 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/decrementing-a-value.md +36 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/empty-array-default-value.md +43 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/full-text-search-with-generated-columns.md +73 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/gel-auth-extension.md +89 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/include-or-exclude-columns.md +51 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/incrementing-a-value.md +36 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/limit-offset-pagination.md +104 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/mysql-local-setup.md +55 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/point-datatype-psql.md +79 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/postgis-geometry-point.md +115 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/postgresql-full-text-search.md +150 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/postgresql-local-setup.md +55 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/seeding-with-option.md +69 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/seeding-with-partially-exposed-schema.md +60 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/select-parent-rows-with-at-least-one-related-child-row.md +74 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/timestamp-default-value.md +93 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/toggling-a-boolean-field.md +20 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/update-many-with-different-values.md +50 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/upsert.md +169 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/vector-similarity-search.md +79 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes.md +787 -0
- package/lovely-docs/drizzle-orm/connect-aws-data-api-pg.md +47 -0
- package/lovely-docs/drizzle-orm/connect-bun-sql.md +35 -0
- package/lovely-docs/drizzle-orm/connect-bun-sqlite.md +42 -0
- package/lovely-docs/drizzle-orm/connect-cloudflare-d1.md +61 -0
- package/lovely-docs/drizzle-orm/connect-cloudflare-do.md +86 -0
- package/lovely-docs/drizzle-orm/connect-neon.md +72 -0
- package/lovely-docs/drizzle-orm/connect-nile.md +84 -0
- package/lovely-docs/drizzle-orm/connect-pglite.md +39 -0
- package/lovely-docs/drizzle-orm/connect-planetscale.md +37 -0
- package/lovely-docs/drizzle-orm/connect-prisma-postgres.md +48 -0
- package/lovely-docs/drizzle-orm/connect-sqlite-cloud.md +29 -0
- package/lovely-docs/drizzle-orm/connect-supabase.md +45 -0
- package/lovely-docs/drizzle-orm/connect-tidb-serverless.md +35 -0
- package/lovely-docs/drizzle-orm/connect-turso-database.md +28 -0
- package/lovely-docs/drizzle-orm/connect-turso.md +60 -0
- package/lovely-docs/drizzle-orm/connect-vercel-postgres.md +37 -0
- package/lovely-docs/drizzle-orm/connect-xata.md +29 -0
- package/lovely-docs/drizzle-orm/custom-migrations.md +40 -0
- package/lovely-docs/drizzle-orm/custom-types.md +137 -0
- package/lovely-docs/drizzle-orm/database-connection-overview.md +89 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/bun-sql-existing.md +59 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/bun-sqlite-existing.md +48 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/bun-sqlite-new.md +34 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/d1-new.md +74 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/expo-sqlite-setup.md +169 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/gel-existing-project.md +81 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/get-started-bun-sql.md +29 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/getting_started_with_gel.md +85 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/mysql-existing-project.md +32 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/mysql-setup.md +43 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/neon-existing-project.md +39 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/neon-setup.md +45 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/nile-existing-project.md +66 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/nile-setup.md +59 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/op-sqlite_setup_guide.md +135 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/pglite-existing-project.md +67 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/pglite-setup.md +33 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/planetscale-existing-project.md +70 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/planetscale-setup.md +46 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/postgresql-existing-project.md +57 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/postgresql-setup.md +44 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/singlestore-existing-project.md +22 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/singlestore-setup.md +37 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/sqlite-cloud-existing-project.md +52 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/sqlite-cloud-setup.md +53 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/sqlite-durable-objects-setup.md +163 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/sqlite-existing-project.md +36 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/sqlite-new.md +28 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/supabase-existing-project.md +32 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/tidb-existing-project.md +25 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/tidb-serverless-setup.md +41 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/turso-database-existing.md +61 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/turso-database-setup.md +60 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/turso-existing.md +74 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/turso-new.md +78 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/vercel-postgres-existing-project.md +71 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/vercel-postgres-setup.md +46 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/xata-existing-project.md +32 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/xata-new.md +30 -0
- package/lovely-docs/drizzle-orm/database_setup_guides.md +144 -0
- package/lovely-docs/drizzle-orm/delete.md +57 -0
- package/lovely-docs/drizzle-orm/drizzle-config-file.md +252 -0
- package/lovely-docs/drizzle-orm/drizzle-kit-up.md +43 -0
- package/lovely-docs/drizzle-orm/dynamic-query-building.md +68 -0
- package/lovely-docs/drizzle-orm/eslint-plugin.md +76 -0
- package/lovely-docs/drizzle-orm/expo-sqlite.md +101 -0
- package/lovely-docs/drizzle-orm/export.md +88 -0
- package/lovely-docs/drizzle-orm/faq.md +28 -0
- package/lovely-docs/drizzle-orm/filter-and-conditional-operators.md +169 -0
- package/lovely-docs/drizzle-orm/gel-setup.md +37 -0
- package/lovely-docs/drizzle-orm/generate.md +119 -0
- package/lovely-docs/drizzle-orm/generated-columns.md +128 -0
- package/lovely-docs/drizzle-orm/getting_started/database_integrations/drizzle-with-turso.md +159 -0
- package/lovely-docs/drizzle-orm/getting_started/database_integrations/drizzle_with_nile_database.md +195 -0
- package/lovely-docs/drizzle-orm/getting_started/database_integrations/neon_postgres_integration.md +157 -0
- package/lovely-docs/drizzle-orm/getting_started/database_integrations/supabase_integration.md +150 -0
- package/lovely-docs/drizzle-orm/getting_started/database_integrations/vercel-postgres-setup.md +152 -0
- package/lovely-docs/drizzle-orm/getting_started/database_integrations/xata_integration.md +143 -0
- package/lovely-docs/drizzle-orm/getting_started/database_integrations.md +117 -0
- package/lovely-docs/drizzle-orm/getting_started/edge_functions_integration/drizzle_with_vercel_edge_functions.md +220 -0
- package/lovely-docs/drizzle-orm/getting_started/edge_functions_integration/netlify_edge_functions_with_neon_postgres.md +120 -0
- package/lovely-docs/drizzle-orm/getting_started/edge_functions_integration/netlify_edge_functions_with_supabase.md +94 -0
- package/lovely-docs/drizzle-orm/getting_started/edge_functions_integration/supabase_edge_functions_integration.md +116 -0
- package/lovely-docs/drizzle-orm/getting_started/edge_functions_integration.md +63 -0
- package/lovely-docs/drizzle-orm/getting_started/todo_app_with_neon_postgres.md +323 -0
- package/lovely-docs/drizzle-orm/getting_started.md +443 -0
- package/lovely-docs/drizzle-orm/graphql.md +107 -0
- package/lovely-docs/drizzle-orm/http-proxy-driver.md +138 -0
- package/lovely-docs/drizzle-orm/indexes-constraints.md +135 -0
- package/lovely-docs/drizzle-orm/insert.mdx.md +118 -0
- package/lovely-docs/drizzle-orm/joins.md +145 -0
- package/lovely-docs/drizzle-orm/kit-overview.md +81 -0
- package/lovely-docs/drizzle-orm/migrate.md +54 -0
- package/lovely-docs/drizzle-orm/migration_guides/migrate-from-sequelize.md +335 -0
- package/lovely-docs/drizzle-orm/migration_guides/migrate-from-typeorm.md +317 -0
- package/lovely-docs/drizzle-orm/migration_guides/migrate_from_prisma_to_drizzle.md +258 -0
- package/lovely-docs/drizzle-orm/migration_guides.md +201 -0
- package/lovely-docs/drizzle-orm/migrations.md +50 -0
- package/lovely-docs/drizzle-orm/mysql-setup.md +51 -0
- package/lovely-docs/drizzle-orm/op-sqlite-setup.md +80 -0
- package/lovely-docs/drizzle-orm/overview.md +69 -0
- package/lovely-docs/drizzle-orm/postgresql-setup.md +71 -0
- package/lovely-docs/drizzle-orm/postgresql_extensions.md +93 -0
- package/lovely-docs/drizzle-orm/prepared-statements.md +77 -0
- package/lovely-docs/drizzle-orm/prisma-extension.md +46 -0
- package/lovely-docs/drizzle-orm/pull.md +134 -0
- package/lovely-docs/drizzle-orm/push.md +129 -0
- package/lovely-docs/drizzle-orm/queries-and-crud.md +72 -0
- package/lovely-docs/drizzle-orm/quick-start.md +63 -0
- package/lovely-docs/drizzle-orm/react-native-sqlite-setup.md +1 -0
- package/lovely-docs/drizzle-orm/read-replicas.md +66 -0
- package/lovely-docs/drizzle-orm/relational-queries.md +271 -0
- package/lovely-docs/drizzle-orm/relations.md +194 -0
- package/lovely-docs/drizzle-orm/release_notes/live-queries.md +27 -0
- package/lovely-docs/drizzle-orm/release_notes/pglite_driver_support.md +14 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.11.0_release.md +139 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.16.2_release_notes.md +86 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.23.2_release.md +5 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.27.2_-_unique_constraints_support.md +66 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.28.0_release_notes.md +80 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.28.1_release.md +7 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.28.2_release_notes.md +18 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.28.3_release_notes.md +48 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.28.4_release.md +8 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.28.5_release_notes.md +7 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.28.6_release_notes.md +54 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.29.0_release_notes.md +143 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.29.1_release_notes.md +72 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.29.2_release_notes.md +95 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.29.3_release.md +7 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.29.4_release_notes.md +40 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.29.5_release_notes.md +69 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.30.0_release_notes.md +31 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.30.10_release.md +18 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.30.1_release_notes.md +16 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.30.2_release_notes.md +7 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.30.3_release_notes.md +8 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.30.5_release_notes.md +20 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.30.7_release.md +5 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.30.8_release_notes.md +36 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.30.9_release.md +29 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.31.0_release_notes.md +186 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.31.2_tidb_cloud_serverless_support.md +16 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.31.3_release.md +19 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.31.4_release.md +1 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.32.0_release_notes.md +136 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.32.1_release_notes.md +15 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.32.2_release.md +13 -0
- package/lovely-docs/drizzle-orm/release_notes/xata-http-driver-support.md +27 -0
- package/lovely-docs/drizzle-orm/release_notes.md +25 -0
- package/lovely-docs/drizzle-orm/rls.mdx.md +385 -0
- package/lovely-docs/drizzle-orm/schema-declaration.md +239 -0
- package/lovely-docs/drizzle-orm/schemas.md +63 -0
- package/lovely-docs/drizzle-orm/seed-generators.md +220 -0
- package/lovely-docs/drizzle-orm/seed-limitations.md +3 -0
- package/lovely-docs/drizzle-orm/seed-overview.md +155 -0
- package/lovely-docs/drizzle-orm/seed-versioning.md +85 -0
- package/lovely-docs/drizzle-orm/select.md +411 -0
- package/lovely-docs/drizzle-orm/sequences.md +62 -0
- package/lovely-docs/drizzle-orm/serverless-performance.md +21 -0
- package/lovely-docs/drizzle-orm/set-operations.md +127 -0
- package/lovely-docs/drizzle-orm/singlestore-setup.md +57 -0
- package/lovely-docs/drizzle-orm/sql-template.md +127 -0
- package/lovely-docs/drizzle-orm/sqlite-setup.md +65 -0
- package/lovely-docs/drizzle-orm/studio.md +47 -0
- package/lovely-docs/drizzle-orm/transactions.md +83 -0
- package/lovely-docs/drizzle-orm/type-helpers-and-utilities.md +160 -0
- package/lovely-docs/drizzle-orm/typebox.md +110 -0
- package/lovely-docs/drizzle-orm/update.md +79 -0
- package/lovely-docs/drizzle-orm/upgrade-to-0.21.0.md +40 -0
- package/lovely-docs/drizzle-orm/valibot.md +115 -0
- package/lovely-docs/drizzle-orm/views.md +135 -0
- package/lovely-docs/drizzle-orm/why-drizzle.md +66 -0
- package/lovely-docs/drizzle-orm/zod.md +113 -0
- package/lovely-docs/drizzle-orm.md +60 -0
- package/package.json +24 -0
- package/src/index.ts +186 -0
- package/tsconfig.json +14 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
source:
|
|
2
|
+
type: git
|
|
3
|
+
repo: https://github.com/xl0/lovely-docs
|
|
4
|
+
branch: master
|
|
5
|
+
gitCacheDir: /Users/wesleybaldwin/Library/Caches/lovely-docs/github.com/xl0/lovely-docs
|
|
6
|
+
installed:
|
|
7
|
+
- drizzle-orm
|
|
8
|
+
installs: digest
|
|
9
|
+
summaries: true
|
|
10
|
+
llms_map: false
|
|
11
|
+
installDir: lovely-docs
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Roy T. Hashimoto, LunarHUE LLC
|
|
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.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** @type {import('next').NextConfig} */
|
|
2
|
+
const nextConfig = {
|
|
3
|
+
// Transpile workspace packages that ship TypeScript source
|
|
4
|
+
transpilePackages: ['@lunarhue/expo-wa-sqlite'],
|
|
5
|
+
|
|
6
|
+
webpack(config) {
|
|
7
|
+
// wa-sqlite-async.mjs is an Emscripten module — tell webpack not to
|
|
8
|
+
// parse it so it can fetch the WASM binary at runtime via locateFile.
|
|
9
|
+
config.module.rules.push({
|
|
10
|
+
test: /wa-sqlite-async\.mjs$/,
|
|
11
|
+
type: 'javascript/auto',
|
|
12
|
+
});
|
|
13
|
+
return config;
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export default nextConfig;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lunarhue/expo-wa-sqlite-demo",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "node scripts/copy-wasm.mjs && next dev",
|
|
8
|
+
"build": "node scripts/copy-wasm.mjs && next build",
|
|
9
|
+
"start": "next start"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@lunarhue/expo-wa-sqlite": "workspace:*",
|
|
13
|
+
"@lunarhue/wa-sqlite": "workspace:*",
|
|
14
|
+
"@lunarhue/wa-sqlite-wasm": "workspace:*",
|
|
15
|
+
"drizzle-orm": "^0.43.1",
|
|
16
|
+
"next": "^15.2.3",
|
|
17
|
+
"react": "^19.0.0",
|
|
18
|
+
"react-dom": "^19.0.0"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/node": "^22.0.0",
|
|
22
|
+
"@types/react": "^19.0.0",
|
|
23
|
+
"@types/react-dom": "^19.0.0",
|
|
24
|
+
"typescript": "^5.3.3"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Copies the wa-sqlite-async WASM binary into /public so Next.js can serve it.
|
|
3
|
+
import { copyFile } from 'node:fs/promises';
|
|
4
|
+
import { createRequire } from 'node:module';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
|
|
8
|
+
const require = createRequire(import.meta.url);
|
|
9
|
+
const wasmPkg = path.dirname(require.resolve('@lunarhue/wa-sqlite-wasm/wa-sqlite-async.mjs'));
|
|
10
|
+
const src = path.join(wasmPkg, 'wa-sqlite-async.wasm');
|
|
11
|
+
const dest = fileURLToPath(new URL('../public/wa-sqlite-async.wasm', import.meta.url));
|
|
12
|
+
|
|
13
|
+
await copyFile(src, dest);
|
|
14
|
+
console.log('Copied wa-sqlite-async.wasm → public/');
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Metadata } from 'next';
|
|
2
|
+
|
|
3
|
+
export const metadata: Metadata = {
|
|
4
|
+
title: 'wa-sqlite + Drizzle Demo',
|
|
5
|
+
description: 'IDBBatchAtomicVFS + drizzle-orm in the browser',
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
9
|
+
return (
|
|
10
|
+
<html lang="en">
|
|
11
|
+
<body style={{ fontFamily: 'system-ui, sans-serif', maxWidth: 640, margin: '3rem auto', padding: '0 1rem' }}>
|
|
12
|
+
{children}
|
|
13
|
+
</body>
|
|
14
|
+
</html>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Suspense } from 'react';
|
|
2
|
+
import Todos from './todos';
|
|
3
|
+
|
|
4
|
+
export default function Page() {
|
|
5
|
+
return (
|
|
6
|
+
<>
|
|
7
|
+
<h1>wa-sqlite + Drizzle</h1>
|
|
8
|
+
<p style={{ color: '#555' }}>
|
|
9
|
+
Persisted in IndexedDB via <code>IDBBatchAtomicVFS</code>. Reload the page — your todos survive.
|
|
10
|
+
</p>
|
|
11
|
+
<Suspense fallback={<p>Initialising database…</p>}>
|
|
12
|
+
<Todos />
|
|
13
|
+
</Suspense>
|
|
14
|
+
</>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { eq } from 'drizzle-orm';
|
|
4
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
5
|
+
import { useWaSQLiteDB } from '@lunarhue/expo-wa-sqlite';
|
|
6
|
+
import { todos } from '@/db/schema';
|
|
7
|
+
|
|
8
|
+
// The Emscripten module factory — imported at module scope so the singleton
|
|
9
|
+
// in useWaSQLiteDB only ever sees one reference to it.
|
|
10
|
+
// We use a dynamic import so Next.js doesn't try to SSR this module.
|
|
11
|
+
const moduleFactoryPromise =
|
|
12
|
+
typeof window !== 'undefined'
|
|
13
|
+
? import('@lunarhue/wa-sqlite-wasm/wa-sqlite-async.mjs').then((m) => m.default)
|
|
14
|
+
: Promise.resolve(null as any);
|
|
15
|
+
|
|
16
|
+
type Todo = typeof todos.$inferSelect;
|
|
17
|
+
|
|
18
|
+
export default function Todos() {
|
|
19
|
+
const [moduleFactory, setModuleFactory] = useState<((...args: any[]) => Promise<any>) | null>(null);
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
moduleFactoryPromise.then(setModuleFactory);
|
|
23
|
+
}, []);
|
|
24
|
+
|
|
25
|
+
if (!moduleFactory) return <p>Loading WASM…</p>;
|
|
26
|
+
|
|
27
|
+
return <TodoApp moduleFactory={moduleFactory} />;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function TodoApp({ moduleFactory }: { moduleFactory: (...args: any[]) => Promise<any> }) {
|
|
31
|
+
const { db, isReady, error } = useWaSQLiteDB({
|
|
32
|
+
dbName: 'todos-demo',
|
|
33
|
+
moduleFactory,
|
|
34
|
+
// The WASM binary is copied to /public by the dev/build script.
|
|
35
|
+
wasmUrl: '/wa-sqlite-async.wasm',
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const [items, setItems] = useState<Todo[]>([]);
|
|
39
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
40
|
+
|
|
41
|
+
const refresh = useCallback(async () => {
|
|
42
|
+
if (!db) return;
|
|
43
|
+
setItems(await db.select().from(todos).orderBy(todos.id));
|
|
44
|
+
}, [db]);
|
|
45
|
+
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
if (isReady) refresh();
|
|
48
|
+
}, [isReady, refresh]);
|
|
49
|
+
|
|
50
|
+
const addTodo = async (e: React.FormEvent) => {
|
|
51
|
+
e.preventDefault();
|
|
52
|
+
const text = inputRef.current?.value.trim();
|
|
53
|
+
if (!text || !db) return;
|
|
54
|
+
await db.insert(todos).values({ text, done: false });
|
|
55
|
+
inputRef.current!.value = '';
|
|
56
|
+
refresh();
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const toggleTodo = async (todo: Todo) => {
|
|
60
|
+
if (!db) return;
|
|
61
|
+
await db.update(todos).set({ done: !todo.done }).where(eq(todos.id, todo.id));
|
|
62
|
+
refresh();
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const deleteTodo = async (id: number) => {
|
|
66
|
+
if (!db) return;
|
|
67
|
+
await db.delete(todos).where(eq(todos.id, id));
|
|
68
|
+
refresh();
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
if (error) {
|
|
72
|
+
return <p style={{ color: 'red' }}>Error: {error.message}</p>;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (!isReady) {
|
|
76
|
+
return <p>Opening database…</p>;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<div>
|
|
81
|
+
<form onSubmit={addTodo} style={{ display: 'flex', gap: 8, marginBottom: 16 }}>
|
|
82
|
+
<input
|
|
83
|
+
ref={inputRef}
|
|
84
|
+
placeholder="What needs doing?"
|
|
85
|
+
style={{ flex: 1, padding: '6px 10px', fontSize: 16 }}
|
|
86
|
+
/>
|
|
87
|
+
<button type="submit" style={{ padding: '6px 16px' }}>Add</button>
|
|
88
|
+
</form>
|
|
89
|
+
|
|
90
|
+
{items.length === 0 && <p style={{ color: '#888' }}>No todos yet.</p>}
|
|
91
|
+
|
|
92
|
+
<ul style={{ listStyle: 'none', padding: 0 }}>
|
|
93
|
+
{items.map((todo) => (
|
|
94
|
+
<li
|
|
95
|
+
key={todo.id}
|
|
96
|
+
style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 }}
|
|
97
|
+
>
|
|
98
|
+
<input
|
|
99
|
+
type="checkbox"
|
|
100
|
+
checked={todo.done}
|
|
101
|
+
onChange={() => toggleTodo(todo)}
|
|
102
|
+
/>
|
|
103
|
+
<span style={{ flex: 1, textDecoration: todo.done ? 'line-through' : 'none', color: todo.done ? '#aaa' : 'inherit' }}>
|
|
104
|
+
{todo.text}
|
|
105
|
+
</span>
|
|
106
|
+
<button onClick={() => deleteTodo(todo.id)} style={{ color: 'red', background: 'none', border: 'none', cursor: 'pointer' }}>
|
|
107
|
+
✕
|
|
108
|
+
</button>
|
|
109
|
+
</li>
|
|
110
|
+
))}
|
|
111
|
+
</ul>
|
|
112
|
+
|
|
113
|
+
<p style={{ marginTop: 24, fontSize: 12, color: '#aaa' }}>
|
|
114
|
+
Storage: IndexedDB · VFS: IDBBatchAtomicVFS · ORM: drizzle-orm
|
|
115
|
+
</p>
|
|
116
|
+
</div>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
5
|
+
"module": "ESNext",
|
|
6
|
+
"moduleResolution": "bundler",
|
|
7
|
+
"jsx": "preserve",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"incremental": true,
|
|
11
|
+
"plugins": [{ "name": "next" }],
|
|
12
|
+
"paths": {
|
|
13
|
+
"@/*": ["./src/*"]
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
17
|
+
"exclude": ["node_modules"]
|
|
18
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
## drizzle-arktype Plugin
|
|
2
|
+
|
|
3
|
+
Plugin for Drizzle ORM that generates Arktype schemas from Drizzle ORM schemas. Requires Drizzle ORM v0.36.0+, Arktype v2.0.0+.
|
|
4
|
+
|
|
5
|
+
### Select Schema
|
|
6
|
+
Validates data queried from the database (API responses).
|
|
7
|
+
|
|
8
|
+
```ts
|
|
9
|
+
import { pgTable, text, integer } from 'drizzle-orm/pg-core';
|
|
10
|
+
import { createSelectSchema } from 'drizzle-arktype';
|
|
11
|
+
|
|
12
|
+
const users = pgTable('users', {
|
|
13
|
+
id: integer().generatedAlwaysAsIdentity().primaryKey(),
|
|
14
|
+
name: text().notNull(),
|
|
15
|
+
age: integer().notNull()
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const userSelectSchema = createSelectSchema(users);
|
|
19
|
+
const rows = await db.select().from(users).limit(1);
|
|
20
|
+
const parsed = userSelectSchema(rows[0]); // validates all fields are present
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Supports views and enums:
|
|
24
|
+
```ts
|
|
25
|
+
const roles = pgEnum('roles', ['admin', 'basic']);
|
|
26
|
+
const rolesSchema = createSelectSchema(roles);
|
|
27
|
+
|
|
28
|
+
const usersView = pgView('users_view').as((qb) => qb.select().from(users).where(gt(users.age, 18)));
|
|
29
|
+
const usersViewSchema = createSelectSchema(usersView);
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Insert Schema
|
|
33
|
+
Validates data to be inserted into the database (API requests).
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
const userInsertSchema = createInsertSchema(users);
|
|
37
|
+
const user = { name: 'Jane', age: 30 };
|
|
38
|
+
const parsed = userInsertSchema(user); // validates required fields
|
|
39
|
+
await db.insert(users).values(parsed);
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Update Schema
|
|
43
|
+
Validates data to be updated in the database (API requests). Generated columns cannot be updated.
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
const userUpdateSchema = createUpdateSchema(users);
|
|
47
|
+
const user = { age: 35 };
|
|
48
|
+
const parsed = userUpdateSchema(user); // all fields optional, generated columns rejected
|
|
49
|
+
await db.update(users).set(parsed).where(eq(users.name, 'Jane'));
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Refinements
|
|
53
|
+
Each create schema function accepts optional refinements parameter to extend, modify, or overwrite field schemas:
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
const userSelectSchema = createSelectSchema(users, {
|
|
57
|
+
name: (schema) => pipe(schema, maxLength(20)), // extends schema
|
|
58
|
+
bio: (schema) => pipe(schema, maxLength(1000)), // extends before nullability
|
|
59
|
+
preferences: object({ theme: string() }) // overwrites field including nullability
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Data Type Reference
|
|
64
|
+
|
|
65
|
+
Boolean: `pg.boolean()`, `mysql.boolean()`, `sqlite.integer({ mode: 'boolean' })` → `type.boolean`
|
|
66
|
+
|
|
67
|
+
Date: `pg.date({ mode: 'date' })`, `pg.timestamp({ mode: 'date' })`, `mysql.date({ mode: 'date' })`, `mysql.datetime({ mode: 'date' })`, `mysql.timestamp({ mode: 'date' })`, `sqlite.integer({ mode: 'timestamp' })`, `sqlite.integer({ mode: 'timestamp_ms' })` → `type.Date`
|
|
68
|
+
|
|
69
|
+
String: `pg.date({ mode: 'string' })`, `pg.timestamp({ mode: 'string' })`, `pg.cidr()`, `pg.inet()`, `pg.interval()`, `pg.macaddr()`, `pg.macaddr8()`, `pg.numeric()`, `pg.text()`, `pg.sparsevec()`, `pg.time()`, `mysql.binary()`, `mysql.date({ mode: 'string' })`, `mysql.datetime({ mode: 'string' })`, `mysql.decimal()`, `mysql.time()`, `mysql.timestamp({ mode: 'string' })`, `mysql.varbinary()`, `sqlite.numeric()`, `sqlite.text({ mode: 'text' })` → `type.string`
|
|
70
|
+
|
|
71
|
+
Bit: `pg.bit({ dimensions: ... })` → `type(/^[01]{${column.dimensions}}$/)`
|
|
72
|
+
|
|
73
|
+
UUID: `pg.uuid()` → `type(/^[\da-f]{8}(?:-[\da-f]{4}){3}-[\da-f]{12}$/iu)`
|
|
74
|
+
|
|
75
|
+
Char: `pg.char({ length: ... })`, `mysql.char({ length: ... })` → `type.string.exactlyLength(length)`
|
|
76
|
+
|
|
77
|
+
Varchar: `pg.varchar({ length: ... })`, `mysql.varchar({ length: ... })`, `sqlite.text({ mode: 'text', length: ... })` → `type.string.atMostLength(length)`
|
|
78
|
+
|
|
79
|
+
MySQL text variants: `mysql.tinytext()` → `type.string.atMostLength(255)`, `mysql.text()` → `type.string.atMostLength(65_535)`, `mysql.mediumtext()` → `type.string.atMostLength(16_777_215)`, `mysql.longtext()` → `type.string.atMostLength(4_294_967_295)`
|
|
80
|
+
|
|
81
|
+
Enum: `pg.text({ enum: ... })`, `pg.char({ enum: ... })`, `pg.varchar({ enum: ... })`, `mysql.tinytext({ enum: ... })`, `mysql.mediumtext({ enum: ... })`, `mysql.text({ enum: ... })`, `mysql.longtext({ enum: ... })`, `mysql.char({ enum: ... })`, `mysql.varchar({ enum: ... })`, `mysql.mysqlEnum(...)`, `sqlite.text({ mode: 'text', enum: ... })` → `type.enumerated(...enum)`
|
|
82
|
+
|
|
83
|
+
Integer types with ranges:
|
|
84
|
+
- `mysql.tinyint()` → `type.keywords.number.integer.atLeast(-128).atMost(127)`
|
|
85
|
+
- `mysql.tinyint({ unsigned: true })` → `type.keywords.number.integer.atLeast(0).atMost(255)`
|
|
86
|
+
- `pg.smallint()`, `pg.smallserial()`, `mysql.smallint()` → `type.keywords.number.integer.atLeast(-32_768).atMost(32_767)`
|
|
87
|
+
- `mysql.smallint({ unsigned: true })` → `type.keywords.number.integer.atLeast(0).atMost(65_535)`
|
|
88
|
+
- `pg.real()`, `mysql.float()` → `type.number.atLeast(-8_388_608).atMost(8_388_607)`
|
|
89
|
+
- `mysql.mediumint()` → `type.keywords.number.integer.atLeast(-8_388_608).atMost(8_388_607)`
|
|
90
|
+
- `mysql.float({ unsigned: true })` → `type.number.atLeast(0).atMost(16_777_215)`
|
|
91
|
+
- `mysql.mediumint({ unsigned: true })` → `type.keywords.number.integer.atLeast(0).atMost(16_777_215)`
|
|
92
|
+
- `pg.integer()`, `pg.serial()`, `mysql.int()` → `type.keywords.number.integer.atLeast(-2_147_483_648).atMost(2_147_483_647)`
|
|
93
|
+
- `mysql.int({ unsigned: true })` → `type.keywords.number.integer.atLeast(0).atMost(4_294_967_295)`
|
|
94
|
+
- `pg.doublePrecision()`, `mysql.double()`, `mysql.real()`, `sqlite.real()` → `type.number.atLeast(-140_737_488_355_328).atMost(140_737_488_355_327)`
|
|
95
|
+
- `mysql.double({ unsigned: true })` → `type.number.atLeast(0).atMost(281_474_976_710_655)`
|
|
96
|
+
- `pg.bigint({ mode: 'number' })`, `pg.bigserial({ mode: 'number' })`, `mysql.bigint({ mode: 'number' })`, `mysql.bigserial({ mode: 'number' })`, `sqlite.integer({ mode: 'number' })` → `type.keywords.number.integer.atLeast(-9_007_199_254_740_991).atMost(9_007_199_254_740_991)`
|
|
97
|
+
- `mysql.serial()` → `type.keywords.number.integer.atLeast(0).atMost(9_007_199_254_740_991)`
|
|
98
|
+
- `pg.bigint({ mode: 'bigint' })`, `pg.bigserial({ mode: 'bigint' })`, `mysql.bigint({ mode: 'bigint' })`, `sqlite.blob({ mode: 'bigint' })` → `type.bigint.narrow(...)` with 64-bit limits
|
|
99
|
+
- `mysql.bigint({ mode: 'bigint', unsigned: true })` → `type.bigint.narrow(...)` with unsigned 64-bit limits
|
|
100
|
+
|
|
101
|
+
Year: `mysql.year()` → `type.keywords.number.integer.atLeast(1_901).atMost(2_155)`
|
|
102
|
+
|
|
103
|
+
Geometry point: `pg.geometry({ type: 'point', mode: 'tuple' })`, `pg.point({ mode: 'tuple' })` → `type([type.number, type.number])` or `pg.geometry({ type: 'point', mode: 'xy' })`, `pg.point({ mode: 'xy' })` → `type({ x: type.number, y: type.number })`
|
|
104
|
+
|
|
105
|
+
Vectors: `pg.halfvec({ dimensions: ... })`, `pg.vector({ dimensions: ... })` → `type.number.array().exactlyLength(dimensions)`
|
|
106
|
+
|
|
107
|
+
Line: `pg.line({ mode: 'abc' })` → `type({ a: type.number, b: type.number, c: type.number })` or `pg.line({ mode: 'tuple' })` → `type([type.number, type.number, type.number])`
|
|
108
|
+
|
|
109
|
+
JSON: `pg.json()`, `pg.jsonb()`, `mysql.json()`, `sqlite.blob({ mode: 'json' })`, `sqlite.text({ mode: 'json' })` → `type('string | number | boolean | null').or(type('unknown.any[] | Record<string, unknown.any>'))`
|
|
110
|
+
|
|
111
|
+
Buffer: `sqlite.blob({ mode: 'buffer' })` → `type.instanceOf(Buffer)`
|
|
112
|
+
|
|
113
|
+
Arrays: `pg.dataType().array(...)` → `baseDataTypeSchema.array().exactlyLength(size)`
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
## Batch API
|
|
2
|
+
|
|
3
|
+
Execute multiple SQL statements in a single database call with implicit transaction handling.
|
|
4
|
+
|
|
5
|
+
### Overview
|
|
6
|
+
|
|
7
|
+
Batch APIs are supported for LibSQL, Neon, and D1 drivers. A batch executes one or more SQL statements in order within an implicit transaction. If all statements succeed, the transaction commits. If any statement fails, the entire transaction rolls back.
|
|
8
|
+
|
|
9
|
+
**LibSQL**: Batch is an implicit transaction controlled by the backend.
|
|
10
|
+
|
|
11
|
+
**D1**: Batching reduces network latency by sending multiple statements in one call. Operates in auto-commit mode. Statements execute sequentially and non-concurrently. If any statement fails, the sequence aborts and rolls back.
|
|
12
|
+
|
|
13
|
+
### Usage
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
const batchResponse: BatchResponse = await db.batch([
|
|
17
|
+
db.insert(usersTable).values({ id: 1, name: 'John' }).returning({ id: usersTable.id }),
|
|
18
|
+
db.update(usersTable).set({ name: 'Dan' }).where(eq(usersTable.id, 1)),
|
|
19
|
+
db.query.usersTable.findMany({}),
|
|
20
|
+
db.select().from(usersTable).where(eq(usersTable.id, 1)),
|
|
21
|
+
db.select({ id: usersTable.id, invitedBy: usersTable.invitedBy }).from(usersTable),
|
|
22
|
+
]);
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The response is a tuple where each element corresponds to the result of each statement in the batch. Return types vary by driver:
|
|
26
|
+
- **libSQL**: `ResultSet` for mutations
|
|
27
|
+
- **Neon**: `NeonHttpQueryResult` for mutations
|
|
28
|
+
- **D1**: `D1Result` for mutations
|
|
29
|
+
|
|
30
|
+
### Supported Builders
|
|
31
|
+
|
|
32
|
+
All query builders can be used in `db.batch()`:
|
|
33
|
+
- `db.all()`, `db.get()`, `db.values()`, `db.run()`, `db.execute()`
|
|
34
|
+
- `db.query.<table>.findMany()`, `db.query.<table>.findFirst()`
|
|
35
|
+
- `db.select()`, `db.update()`, `db.delete()`, `db.insert()`
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
## Overview
|
|
2
|
+
Drizzle sends every query to the database by default with no automatic caching. Caching is opt-in using an `explicit` strategy (`global: false`) by default, or can be enabled globally with `global: true`.
|
|
3
|
+
|
|
4
|
+
## Upstash Integration
|
|
5
|
+
|
|
6
|
+
Quick setup with automatic environment variable configuration:
|
|
7
|
+
```ts
|
|
8
|
+
import { upstashCache } from "drizzle-orm/cache/upstash";
|
|
9
|
+
const db = drizzle(process.env.DB_URL!, { cache: upstashCache() });
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
With explicit credentials and options:
|
|
13
|
+
```ts
|
|
14
|
+
const db = drizzle(process.env.DB_URL!, {
|
|
15
|
+
cache: upstashCache({
|
|
16
|
+
url: '<UPSTASH_URL>',
|
|
17
|
+
token: '<UPSTASH_TOKEN>',
|
|
18
|
+
global: true, // cache all queries by default
|
|
19
|
+
config: { ex: 60 } // 60 second TTL
|
|
20
|
+
})
|
|
21
|
+
});
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Cache Config
|
|
25
|
+
```ts
|
|
26
|
+
type CacheConfig = {
|
|
27
|
+
ex?: number; // expiration in seconds
|
|
28
|
+
hexOptions?: "NX" | "nx" | "XX" | "xx" | "GT" | "gt" | "LT" | "lt"; // HEXPIRE options
|
|
29
|
+
};
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Usage Examples
|
|
33
|
+
|
|
34
|
+
**With `global: false` (default, opt-in):**
|
|
35
|
+
```ts
|
|
36
|
+
const db = drizzle(process.env.DB_URL!, { cache: upstashCache({ url: "", token: "" }) });
|
|
37
|
+
|
|
38
|
+
// Won't use cache
|
|
39
|
+
const res = await db.select().from(users);
|
|
40
|
+
|
|
41
|
+
// Use cache with .$withCache()
|
|
42
|
+
const res = await db.select().from(users).$withCache();
|
|
43
|
+
|
|
44
|
+
// Options for .$withCache()
|
|
45
|
+
.$withCache({ config: { ex: 60 } }) // custom TTL
|
|
46
|
+
.$withCache({ tag: 'custom_key' }) // custom cache key
|
|
47
|
+
.$withCache({ autoInvalidate: false }) // disable auto-invalidation for eventual consistency
|
|
48
|
+
|
|
49
|
+
// Mutations still trigger cache invalidation
|
|
50
|
+
await db.insert(users).value({ email: "test@example.com" });
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**With `global: true`:**
|
|
54
|
+
```ts
|
|
55
|
+
const db = drizzle(process.env.DB_URL!, { cache: upstashCache({ url: "", token: "", global: true }) });
|
|
56
|
+
|
|
57
|
+
// All queries use cache by default
|
|
58
|
+
const res = await db.select().from(users);
|
|
59
|
+
|
|
60
|
+
// Disable cache for specific query
|
|
61
|
+
const res = await db.select().from(users).$withCache(false);
|
|
62
|
+
|
|
63
|
+
// Manual invalidation
|
|
64
|
+
await db.$cache.invalidate({ tables: users });
|
|
65
|
+
await db.$cache.invalidate({ tables: [users, posts] });
|
|
66
|
+
await db.$cache.invalidate({ tables: "usersTable" });
|
|
67
|
+
await db.$cache.invalidate({ tags: "custom_key" });
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Custom Cache Implementation
|
|
71
|
+
|
|
72
|
+
Extend the `Cache` class with `strategy()`, `get()`, `put()`, and `onMutate()` methods:
|
|
73
|
+
```ts
|
|
74
|
+
import Keyv from "keyv";
|
|
75
|
+
|
|
76
|
+
export class TestGlobalCache extends Cache {
|
|
77
|
+
private globalTtl: number = 1000;
|
|
78
|
+
private usedTablesPerKey: Record<string, string[]> = {};
|
|
79
|
+
|
|
80
|
+
constructor(private kv: Keyv = new Keyv()) {
|
|
81
|
+
super();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
override strategy(): "explicit" | "all" {
|
|
85
|
+
return "all"; // or "explicit"
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
override async get(key: string): Promise<any[] | undefined> {
|
|
89
|
+
return (await this.kv.get(key)) ?? undefined;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
override async put(
|
|
93
|
+
key: string,
|
|
94
|
+
response: any,
|
|
95
|
+
tables: string[],
|
|
96
|
+
config?: CacheConfig,
|
|
97
|
+
): Promise<void> {
|
|
98
|
+
const ttl = config?.px ?? (config?.ex ? config.ex * 1000 : this.globalTtl);
|
|
99
|
+
await this.kv.set(key, response, ttl);
|
|
100
|
+
for (const table of tables) {
|
|
101
|
+
const keys = this.usedTablesPerKey[table];
|
|
102
|
+
if (keys === undefined) {
|
|
103
|
+
this.usedTablesPerKey[table] = [key];
|
|
104
|
+
} else {
|
|
105
|
+
keys.push(key);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
override async onMutate(params: {
|
|
111
|
+
tags: string | string[];
|
|
112
|
+
tables: string | string[] | Table<any> | Table<any>[];
|
|
113
|
+
}): Promise<void> {
|
|
114
|
+
const tagsArray = Array.isArray(params.tags) ? params.tags : params.tags ? [params.tags] : [];
|
|
115
|
+
const tablesArray = Array.isArray(params.tables) ? params.tables : params.tables ? [params.tables] : [];
|
|
116
|
+
const keysToDelete = new Set<string>();
|
|
117
|
+
|
|
118
|
+
for (const table of tablesArray) {
|
|
119
|
+
const tableName = is(table, Table) ? getTableName(table) : (table as string);
|
|
120
|
+
const keys = this.usedTablesPerKey[tableName] ?? [];
|
|
121
|
+
for (const key of keys) keysToDelete.add(key);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
for (const tag of tagsArray) {
|
|
125
|
+
await this.kv.delete(tag);
|
|
126
|
+
}
|
|
127
|
+
for (const key of keysToDelete) {
|
|
128
|
+
await this.kv.delete(key);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const db = drizzle(process.env.DB_URL!, { cache: new TestGlobalCache() });
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Limitations
|
|
137
|
+
|
|
138
|
+
**Not supported:**
|
|
139
|
+
- Raw queries: `db.execute(sql\`select 1\`)`
|
|
140
|
+
- Batch operations in d1 and libsql
|
|
141
|
+
- Transactions
|
|
142
|
+
- Relational queries: `db.query.users.findMany()`
|
|
143
|
+
- better-sqlite3, Durable Objects, expo sqlite
|
|
144
|
+
- AWS Data API drivers
|
|
145
|
+
- Views
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
## Purpose
|
|
2
|
+
`drizzle-kit check` validates consistency of generated SQL migrations history. Essential for teams with multiple developers working on different branches altering the database schema.
|
|
3
|
+
|
|
4
|
+
## Configuration
|
|
5
|
+
Requires `dialect` and database connection credentials via `drizzle.config.ts` or CLI options.
|
|
6
|
+
|
|
7
|
+
**With config file:**
|
|
8
|
+
```ts
|
|
9
|
+
// drizzle.config.ts
|
|
10
|
+
import { defineConfig } from "drizzle-kit";
|
|
11
|
+
export default defineConfig({
|
|
12
|
+
dialect: "postgresql",
|
|
13
|
+
});
|
|
14
|
+
```
|
|
15
|
+
```shell
|
|
16
|
+
npx drizzle-kit check
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**As CLI options:**
|
|
20
|
+
```shell
|
|
21
|
+
npx drizzle-kit check --dialect=postgresql
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Multiple Configuration Files
|
|
25
|
+
Support for multiple config files in one project for different database stages:
|
|
26
|
+
```plaintext
|
|
27
|
+
📦 <project root>
|
|
28
|
+
├ 📂 drizzle
|
|
29
|
+
├ 📂 src
|
|
30
|
+
├ 📜 .env
|
|
31
|
+
├ 📜 drizzle-dev.config.ts
|
|
32
|
+
├ 📜 drizzle-prod.config.ts
|
|
33
|
+
├ 📜 package.json
|
|
34
|
+
└ 📜 tsconfig.json
|
|
35
|
+
```
|
|
36
|
+
```shell
|
|
37
|
+
npx drizzle-kit check --config=drizzle-dev.config.ts
|
|
38
|
+
npx drizzle-kit check --config=drizzle-prod.config.ts
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## CLI Options
|
|
42
|
+
| Option | Required | Description |
|
|
43
|
+
|--------|----------|-------------|
|
|
44
|
+
| `dialect` | yes | Database dialect: `postgresql`, `mysql`, or `sqlite` |
|
|
45
|
+
| `out` | no | Migrations folder, default: `./drizzle` |
|
|
46
|
+
| `config` | no | Config file path, default: `drizzle.config.ts` |
|
|
47
|
+
|
|
48
|
+
**Examples:**
|
|
49
|
+
```shell
|
|
50
|
+
npx drizzle-kit check --dialect=postgresql
|
|
51
|
+
npx drizzle-kit check --dialect=postgresql --out=./migrations-folder
|
|
52
|
+
```
|