d1s 0.2.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/README.md +7 -0
- package/dist/.next/.next/BUILD_ID +1 -0
- package/dist/.next/.next/app-build-manifest.json +109 -0
- package/dist/.next/.next/app-path-routes-manifest.json +1 -0
- package/dist/.next/.next/build-manifest.json +33 -0
- package/dist/.next/.next/cache/.rscinfo +1 -0
- package/dist/.next/.next/cache/.tsbuildinfo +1 -0
- package/dist/.next/.next/cache/webpack/client-production/0.pack +0 -0
- package/dist/.next/.next/cache/webpack/client-production/1.pack +0 -0
- package/dist/.next/.next/cache/webpack/client-production/2.pack +0 -0
- package/dist/.next/.next/cache/webpack/client-production/index.pack +0 -0
- package/dist/.next/.next/cache/webpack/client-production/index.pack.old +0 -0
- package/dist/.next/.next/cache/webpack/edge-server-production/0.pack +0 -0
- package/dist/.next/.next/cache/webpack/edge-server-production/index.pack +0 -0
- package/dist/.next/.next/cache/webpack/server-production/0.pack +0 -0
- package/dist/.next/.next/cache/webpack/server-production/1.pack +0 -0
- package/dist/.next/.next/cache/webpack/server-production/index.pack +0 -0
- package/dist/.next/.next/cache/webpack/server-production/index.pack.old +0 -0
- package/dist/.next/.next/diagnostics/build-diagnostics.json +6 -0
- package/dist/.next/.next/diagnostics/framework.json +1 -0
- package/dist/.next/.next/export-marker.json +1 -0
- package/dist/.next/.next/images-manifest.json +1 -0
- package/dist/.next/.next/next-minimal-server.js.nft.json +1 -0
- package/dist/.next/.next/next-server.js.nft.json +1 -0
- package/dist/.next/.next/package.json +1 -0
- package/dist/.next/.next/prerender-manifest.json +1 -0
- package/dist/.next/.next/react-loadable-manifest.json +1 -0
- package/dist/.next/.next/required-server-files.json +1 -0
- package/dist/.next/.next/routes-manifest.json +1 -0
- package/dist/.next/.next/server/app/_not-found/page.js +1 -0
- package/dist/.next/.next/server/app/_not-found/page.js.nft.json +1 -0
- package/dist/.next/.next/server/app/_not-found/page_client-reference-manifest.js +1 -0
- package/dist/.next/.next/server/app/_not-found.html +1 -0
- package/dist/.next/.next/server/app/_not-found.meta +8 -0
- package/dist/.next/.next/server/app/_not-found.rsc +16 -0
- package/dist/.next/.next/server/app/api/cloudflare/route.js +1 -0
- package/dist/.next/.next/server/app/api/cloudflare/route.js.nft.json +1 -0
- package/dist/.next/.next/server/app/api/cloudflare/route_client-reference-manifest.js +1 -0
- package/dist/.next/.next/server/app/api/databases/route.js +1 -0
- package/dist/.next/.next/server/app/api/databases/route.js.nft.json +1 -0
- package/dist/.next/.next/server/app/api/databases/route_client-reference-manifest.js +1 -0
- package/dist/.next/.next/server/app/api/migrations/sync/route.js +12 -0
- package/dist/.next/.next/server/app/api/migrations/sync/route.js.nft.json +1 -0
- package/dist/.next/.next/server/app/api/migrations/sync/route_client-reference-manifest.js +1 -0
- package/dist/.next/.next/server/app/api/migrations/sync-files/route.js +1 -0
- package/dist/.next/.next/server/app/api/migrations/sync-files/route.js.nft.json +1 -0
- package/dist/.next/.next/server/app/api/migrations/sync-files/route_client-reference-manifest.js +1 -0
- package/dist/.next/.next/server/app/api/pull-remote/confirm/route.js +1 -0
- package/dist/.next/.next/server/app/api/pull-remote/confirm/route.js.nft.json +1 -0
- package/dist/.next/.next/server/app/api/pull-remote/confirm/route_client-reference-manifest.js +1 -0
- package/dist/.next/.next/server/app/api/pull-remote/preview/route.js +1 -0
- package/dist/.next/.next/server/app/api/pull-remote/preview/route.js.nft.json +1 -0
- package/dist/.next/.next/server/app/api/pull-remote/preview/route_client-reference-manifest.js +1 -0
- package/dist/.next/.next/server/app/api/push-remote/confirm/route.js +14 -0
- package/dist/.next/.next/server/app/api/push-remote/confirm/route.js.nft.json +1 -0
- package/dist/.next/.next/server/app/api/push-remote/confirm/route_client-reference-manifest.js +1 -0
- package/dist/.next/.next/server/app/api/push-remote/preview/route.js +14 -0
- package/dist/.next/.next/server/app/api/push-remote/preview/route.js.nft.json +1 -0
- package/dist/.next/.next/server/app/api/push-remote/preview/route_client-reference-manifest.js +1 -0
- package/dist/.next/.next/server/app/api/query/route.js +8 -0
- package/dist/.next/.next/server/app/api/query/route.js.nft.json +1 -0
- package/dist/.next/.next/server/app/api/query/route_client-reference-manifest.js +1 -0
- package/dist/.next/.next/server/app/api/tables/[name]/route.js +5 -0
- package/dist/.next/.next/server/app/api/tables/[name]/route.js.nft.json +1 -0
- package/dist/.next/.next/server/app/api/tables/[name]/route_client-reference-manifest.js +1 -0
- package/dist/.next/.next/server/app/api/tables/route.js +7 -0
- package/dist/.next/.next/server/app/api/tables/route.js.nft.json +1 -0
- package/dist/.next/.next/server/app/api/tables/route_client-reference-manifest.js +1 -0
- package/dist/.next/.next/server/app/index.html +3 -0
- package/dist/.next/.next/server/app/index.meta +7 -0
- package/dist/.next/.next/server/app/index.rsc +20 -0
- package/dist/.next/.next/server/app/page.js +58 -0
- package/dist/.next/.next/server/app/page.js.nft.json +1 -0
- package/dist/.next/.next/server/app/page_client-reference-manifest.js +1 -0
- package/dist/.next/.next/server/app-paths-manifest.json +15 -0
- package/dist/.next/.next/server/chunks/336.js +1 -0
- package/dist/.next/.next/server/chunks/627.js +6 -0
- package/dist/.next/.next/server/chunks/638.js +13 -0
- package/dist/.next/.next/server/chunks/801.js +55 -0
- package/dist/.next/.next/server/chunks/973.js +2 -0
- package/dist/.next/.next/server/chunks/992.js +3 -0
- package/dist/.next/.next/server/functions-config-manifest.json +1 -0
- package/dist/.next/.next/server/interception-route-rewrite-manifest.js +1 -0
- package/dist/.next/.next/server/middleware-build-manifest.js +1 -0
- package/dist/.next/.next/server/middleware-manifest.json +6 -0
- package/dist/.next/.next/server/middleware-react-loadable-manifest.js +1 -0
- package/dist/.next/.next/server/next-font-manifest.js +1 -0
- package/dist/.next/.next/server/next-font-manifest.json +1 -0
- package/dist/.next/.next/server/pages/404.html +1 -0
- package/dist/.next/.next/server/pages/500.html +1 -0
- package/dist/.next/.next/server/pages/_app.js +1 -0
- package/dist/.next/.next/server/pages/_app.js.nft.json +1 -0
- package/dist/.next/.next/server/pages/_document.js +1 -0
- package/dist/.next/.next/server/pages/_document.js.nft.json +1 -0
- package/dist/.next/.next/server/pages/_error.js +1 -0
- package/dist/.next/.next/server/pages/_error.js.nft.json +1 -0
- package/dist/.next/.next/server/pages-manifest.json +1 -0
- package/dist/.next/.next/server/server-reference-manifest.js +1 -0
- package/dist/.next/.next/server/server-reference-manifest.json +1 -0
- package/dist/.next/.next/server/webpack-runtime.js +1 -0
- package/dist/.next/.next/static/ODMSSwVqLoDcBgbFHUjr8/_buildManifest.js +1 -0
- package/dist/.next/.next/static/ODMSSwVqLoDcBgbFHUjr8/_ssgManifest.js +1 -0
- package/dist/.next/.next/static/chunks/422-6fa3c14fc64ad67b.js +1 -0
- package/dist/.next/.next/static/chunks/4bd1b696-8c908eb707f85a27.js +1 -0
- package/dist/.next/.next/static/chunks/517-3b698a5d3da06ff7.js +2 -0
- package/dist/.next/.next/static/chunks/692-ede418e1d28a1394.js +1 -0
- package/dist/.next/.next/static/chunks/735-c9144179c6bd7d06.js +1 -0
- package/dist/.next/.next/static/chunks/92-c4466e0f1ca8a3a8.js +1 -0
- package/dist/.next/.next/static/chunks/app/_not-found/page-b786c3dd79ad15a5.js +1 -0
- package/dist/.next/.next/static/chunks/app/api/cloudflare/route-3458d78022185d7f.js +1 -0
- package/dist/.next/.next/static/chunks/app/api/databases/route-64255424877a9f72.js +1 -0
- package/dist/.next/.next/static/chunks/app/api/migrations/sync/route-03766b3afcf043ba.js +1 -0
- package/dist/.next/.next/static/chunks/app/api/migrations/sync-files/route-02fa291c8000dadf.js +1 -0
- package/dist/.next/.next/static/chunks/app/api/pull-remote/confirm/route-7e05bb8920127abe.js +1 -0
- package/dist/.next/.next/static/chunks/app/api/pull-remote/preview/route-0fa15f783e8a306c.js +1 -0
- package/dist/.next/.next/static/chunks/app/api/push-remote/confirm/route-96691f1880e4ed14.js +1 -0
- package/dist/.next/.next/static/chunks/app/api/push-remote/preview/route-a798679c67374552.js +1 -0
- package/dist/.next/.next/static/chunks/app/api/query/route-e90d0030a5083e20.js +1 -0
- package/dist/.next/.next/static/chunks/app/api/tables/[name]/route-1efe63e73b480e58.js +1 -0
- package/dist/.next/.next/static/chunks/app/api/tables/route-3772b4f480c4b4a4.js +1 -0
- package/dist/.next/.next/static/chunks/app/layout-8360fab69b848283.js +1 -0
- package/dist/.next/.next/static/chunks/app/page-75081ac7a88cb706.js +1 -0
- package/dist/.next/.next/static/chunks/framework-d29117d969504448.js +1 -0
- package/dist/.next/.next/static/chunks/main-8d0ab17774ffa031.js +1 -0
- package/dist/.next/.next/static/chunks/main-app-d36cdfb402b40f13.js +1 -0
- package/dist/.next/.next/static/chunks/pages/_app-d23763e3e6c904ff.js +1 -0
- package/dist/.next/.next/static/chunks/pages/_error-9b7125ad1a1e68fa.js +1 -0
- package/dist/.next/.next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
- package/dist/.next/.next/static/chunks/webpack-c76ac1e586b2802a.js +1 -0
- package/dist/.next/.next/static/css/c51a7ef325e30c16.css +3 -0
- package/dist/.next/.next/static/media/569ce4b8f30dc480-s.p.woff2 +0 -0
- package/dist/.next/.next/static/media/747892c23ea88013-s.woff2 +0 -0
- package/dist/.next/.next/static/media/93f479601ee12b01-s.p.woff2 +0 -0
- package/dist/.next/.next/static/media/ba015fad6dcf6784-s.woff2 +0 -0
- package/dist/.next/.next/trace +3 -0
- package/dist/.next/.next/types/app/api/cloudflare/route.ts +347 -0
- package/dist/.next/.next/types/app/api/databases/route.ts +347 -0
- package/dist/.next/.next/types/app/api/migrations/sync/route.ts +347 -0
- package/dist/.next/.next/types/app/api/migrations/sync-files/route.ts +347 -0
- package/dist/.next/.next/types/app/api/pull-remote/confirm/route.ts +347 -0
- package/dist/.next/.next/types/app/api/pull-remote/preview/route.ts +347 -0
- package/dist/.next/.next/types/app/api/push-remote/confirm/route.ts +347 -0
- package/dist/.next/.next/types/app/api/push-remote/preview/route.ts +347 -0
- package/dist/.next/.next/types/app/api/query/route.ts +347 -0
- package/dist/.next/.next/types/app/api/tables/[name]/route.ts +347 -0
- package/dist/.next/.next/types/app/api/tables/route.ts +347 -0
- package/dist/.next/.next/types/app/page.ts +84 -0
- package/dist/.next/.next/types/cache-life.d.ts +141 -0
- package/dist/.next/.next/types/package.json +1 -0
- package/dist/.next/BUILD_ID +1 -0
- package/dist/.next/app-build-manifest.json +109 -0
- package/dist/.next/app-path-routes-manifest.json +1 -0
- package/dist/.next/build-manifest.json +33 -0
- package/dist/.next/cache/.rscinfo +1 -0
- package/dist/.next/cache/.tsbuildinfo +1 -0
- package/dist/.next/cache/webpack/client-production/0.pack +0 -0
- package/dist/.next/cache/webpack/client-production/index.pack +0 -0
- package/dist/.next/cache/webpack/edge-server-production/0.pack +0 -0
- package/dist/.next/cache/webpack/edge-server-production/index.pack +0 -0
- package/dist/.next/cache/webpack/server-production/0.pack +0 -0
- package/dist/.next/cache/webpack/server-production/index.pack +0 -0
- package/dist/.next/diagnostics/build-diagnostics.json +6 -0
- package/dist/.next/diagnostics/framework.json +1 -0
- package/dist/.next/export-marker.json +1 -0
- package/dist/.next/images-manifest.json +1 -0
- package/dist/.next/next-minimal-server.js.nft.json +1 -0
- package/dist/.next/next-server.js.nft.json +1 -0
- package/dist/.next/package.json +1 -0
- package/dist/.next/prerender-manifest.json +1 -0
- package/dist/.next/react-loadable-manifest.json +1 -0
- package/dist/.next/required-server-files.json +1 -0
- package/dist/.next/routes-manifest.json +1 -0
- package/dist/.next/server/app/_not-found/page.js +1 -0
- package/dist/.next/server/app/_not-found/page.js.nft.json +1 -0
- package/dist/.next/server/app/_not-found/page_client-reference-manifest.js +1 -0
- package/dist/.next/server/app/_not-found.html +1 -0
- package/dist/.next/server/app/_not-found.meta +8 -0
- package/dist/.next/server/app/_not-found.rsc +16 -0
- package/dist/.next/server/app/api/cloudflare/route.js +1 -0
- package/dist/.next/server/app/api/cloudflare/route.js.nft.json +1 -0
- package/dist/.next/server/app/api/cloudflare/route_client-reference-manifest.js +1 -0
- package/dist/.next/server/app/api/databases/route.js +1 -0
- package/dist/.next/server/app/api/databases/route.js.nft.json +1 -0
- package/dist/.next/server/app/api/databases/route_client-reference-manifest.js +1 -0
- package/dist/.next/server/app/api/migrations/sync/route.js +12 -0
- package/dist/.next/server/app/api/migrations/sync/route.js.nft.json +1 -0
- package/dist/.next/server/app/api/migrations/sync/route_client-reference-manifest.js +1 -0
- package/dist/.next/server/app/api/migrations/sync-files/route.js +1 -0
- package/dist/.next/server/app/api/migrations/sync-files/route.js.nft.json +1 -0
- package/dist/.next/server/app/api/migrations/sync-files/route_client-reference-manifest.js +1 -0
- package/dist/.next/server/app/api/pull-remote/confirm/route.js +1 -0
- package/dist/.next/server/app/api/pull-remote/confirm/route.js.nft.json +1 -0
- package/dist/.next/server/app/api/pull-remote/confirm/route_client-reference-manifest.js +1 -0
- package/dist/.next/server/app/api/pull-remote/preview/route.js +1 -0
- package/dist/.next/server/app/api/pull-remote/preview/route.js.nft.json +1 -0
- package/dist/.next/server/app/api/pull-remote/preview/route_client-reference-manifest.js +1 -0
- package/dist/.next/server/app/api/push-remote/confirm/route.js +14 -0
- package/dist/.next/server/app/api/push-remote/confirm/route.js.nft.json +1 -0
- package/dist/.next/server/app/api/push-remote/confirm/route_client-reference-manifest.js +1 -0
- package/dist/.next/server/app/api/push-remote/preview/route.js +14 -0
- package/dist/.next/server/app/api/push-remote/preview/route.js.nft.json +1 -0
- package/dist/.next/server/app/api/push-remote/preview/route_client-reference-manifest.js +1 -0
- package/dist/.next/server/app/api/query/route.js +8 -0
- package/dist/.next/server/app/api/query/route.js.nft.json +1 -0
- package/dist/.next/server/app/api/query/route_client-reference-manifest.js +1 -0
- package/dist/.next/server/app/api/tables/[name]/route.js +5 -0
- package/dist/.next/server/app/api/tables/[name]/route.js.nft.json +1 -0
- package/dist/.next/server/app/api/tables/[name]/route_client-reference-manifest.js +1 -0
- package/dist/.next/server/app/api/tables/route.js +7 -0
- package/dist/.next/server/app/api/tables/route.js.nft.json +1 -0
- package/dist/.next/server/app/api/tables/route_client-reference-manifest.js +1 -0
- package/dist/.next/server/app/index.html +3 -0
- package/dist/.next/server/app/index.meta +7 -0
- package/dist/.next/server/app/index.rsc +20 -0
- package/dist/.next/server/app/page.js +58 -0
- package/dist/.next/server/app/page.js.nft.json +1 -0
- package/dist/.next/server/app/page_client-reference-manifest.js +1 -0
- package/dist/.next/server/app-paths-manifest.json +15 -0
- package/dist/.next/server/chunks/336.js +1 -0
- package/dist/.next/server/chunks/627.js +6 -0
- package/dist/.next/server/chunks/638.js +13 -0
- package/dist/.next/server/chunks/801.js +55 -0
- package/dist/.next/server/chunks/973.js +2 -0
- package/dist/.next/server/chunks/992.js +3 -0
- package/dist/.next/server/functions-config-manifest.json +1 -0
- package/dist/.next/server/interception-route-rewrite-manifest.js +1 -0
- package/dist/.next/server/middleware-build-manifest.js +1 -0
- package/dist/.next/server/middleware-manifest.json +6 -0
- package/dist/.next/server/middleware-react-loadable-manifest.js +1 -0
- package/dist/.next/server/next-font-manifest.js +1 -0
- package/dist/.next/server/next-font-manifest.json +1 -0
- package/dist/.next/server/pages/404.html +1 -0
- package/dist/.next/server/pages/500.html +1 -0
- package/dist/.next/server/pages/_app.js +1 -0
- package/dist/.next/server/pages/_app.js.nft.json +1 -0
- package/dist/.next/server/pages/_document.js +1 -0
- package/dist/.next/server/pages/_document.js.nft.json +1 -0
- package/dist/.next/server/pages/_error.js +1 -0
- package/dist/.next/server/pages/_error.js.nft.json +1 -0
- package/dist/.next/server/pages-manifest.json +1 -0
- package/dist/.next/server/server-reference-manifest.js +1 -0
- package/dist/.next/server/server-reference-manifest.json +1 -0
- package/dist/.next/server/webpack-runtime.js +1 -0
- package/dist/.next/static/OmPVuV0d1lrcjSBQzxkdH/_buildManifest.js +1 -0
- package/dist/.next/static/OmPVuV0d1lrcjSBQzxkdH/_ssgManifest.js +1 -0
- package/dist/.next/static/chunks/422-6fa3c14fc64ad67b.js +1 -0
- package/dist/.next/static/chunks/4bd1b696-8c908eb707f85a27.js +1 -0
- package/dist/.next/static/chunks/517-3b698a5d3da06ff7.js +2 -0
- package/dist/.next/static/chunks/692-ede418e1d28a1394.js +1 -0
- package/dist/.next/static/chunks/735-c9144179c6bd7d06.js +1 -0
- package/dist/.next/static/chunks/92-c4466e0f1ca8a3a8.js +1 -0
- package/dist/.next/static/chunks/app/_not-found/page-b786c3dd79ad15a5.js +1 -0
- package/dist/.next/static/chunks/app/api/cloudflare/route-3458d78022185d7f.js +1 -0
- package/dist/.next/static/chunks/app/api/databases/route-64255424877a9f72.js +1 -0
- package/dist/.next/static/chunks/app/api/migrations/sync/route-03766b3afcf043ba.js +1 -0
- package/dist/.next/static/chunks/app/api/migrations/sync-files/route-02fa291c8000dadf.js +1 -0
- package/dist/.next/static/chunks/app/api/pull-remote/confirm/route-7e05bb8920127abe.js +1 -0
- package/dist/.next/static/chunks/app/api/pull-remote/preview/route-0fa15f783e8a306c.js +1 -0
- package/dist/.next/static/chunks/app/api/push-remote/confirm/route-96691f1880e4ed14.js +1 -0
- package/dist/.next/static/chunks/app/api/push-remote/preview/route-a798679c67374552.js +1 -0
- package/dist/.next/static/chunks/app/api/query/route-e90d0030a5083e20.js +1 -0
- package/dist/.next/static/chunks/app/api/tables/[name]/route-1efe63e73b480e58.js +1 -0
- package/dist/.next/static/chunks/app/api/tables/route-3772b4f480c4b4a4.js +1 -0
- package/dist/.next/static/chunks/app/layout-8360fab69b848283.js +1 -0
- package/dist/.next/static/chunks/app/page-75081ac7a88cb706.js +1 -0
- package/dist/.next/static/chunks/framework-d29117d969504448.js +1 -0
- package/dist/.next/static/chunks/main-8d0ab17774ffa031.js +1 -0
- package/dist/.next/static/chunks/main-app-d36cdfb402b40f13.js +1 -0
- package/dist/.next/static/chunks/pages/_app-d23763e3e6c904ff.js +1 -0
- package/dist/.next/static/chunks/pages/_error-9b7125ad1a1e68fa.js +1 -0
- package/dist/.next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
- package/dist/.next/static/chunks/webpack-c76ac1e586b2802a.js +1 -0
- package/dist/.next/static/css/c51a7ef325e30c16.css +3 -0
- package/dist/.next/static/media/569ce4b8f30dc480-s.p.woff2 +0 -0
- package/dist/.next/static/media/747892c23ea88013-s.woff2 +0 -0
- package/dist/.next/static/media/93f479601ee12b01-s.p.woff2 +0 -0
- package/dist/.next/static/media/ba015fad6dcf6784-s.woff2 +0 -0
- package/dist/.next/trace +13 -0
- package/dist/.next/types/app/api/cloudflare/route.ts +347 -0
- package/dist/.next/types/app/api/databases/route.ts +347 -0
- package/dist/.next/types/app/api/migrations/sync/route.ts +347 -0
- package/dist/.next/types/app/api/migrations/sync-files/route.ts +347 -0
- package/dist/.next/types/app/api/pull-remote/confirm/route.ts +347 -0
- package/dist/.next/types/app/api/pull-remote/preview/route.ts +347 -0
- package/dist/.next/types/app/api/push-remote/confirm/route.ts +347 -0
- package/dist/.next/types/app/api/push-remote/preview/route.ts +347 -0
- package/dist/.next/types/app/api/query/route.ts +347 -0
- package/dist/.next/types/app/api/tables/[name]/route.ts +347 -0
- package/dist/.next/types/app/api/tables/route.ts +347 -0
- package/dist/.next/types/app/page.ts +84 -0
- package/dist/.next/types/cache-life.d.ts +141 -0
- package/dist/.next/types/package.json +1 -0
- package/dist/app/api/cloudflare/route.js +146 -0
- package/dist/app/api/databases/route.js +72 -0
- package/dist/app/api/migrations/sync/route.js +172 -0
- package/dist/app/api/migrations/sync-files/route.js +172 -0
- package/dist/app/api/pull-remote/confirm/route.js +277 -0
- package/dist/app/api/pull-remote/preview/route.js +143 -0
- package/dist/app/api/push-remote/confirm/route.js +196 -0
- package/dist/app/api/push-remote/preview/route.js +178 -0
- package/dist/app/api/query/route.js +160 -0
- package/dist/app/api/tables/[name]/route.js +173 -0
- package/dist/app/api/tables/route.js +174 -0
- package/dist/app/app/api/cloudflare/route.js +146 -0
- package/dist/app/app/api/databases/route.js +72 -0
- package/dist/app/app/api/migrations/sync/route.js +172 -0
- package/dist/app/app/api/migrations/sync-files/route.js +172 -0
- package/dist/app/app/api/pull-remote/confirm/route.js +277 -0
- package/dist/app/app/api/pull-remote/preview/route.js +143 -0
- package/dist/app/app/api/push-remote/confirm/route.js +196 -0
- package/dist/app/app/api/push-remote/preview/route.js +178 -0
- package/dist/app/app/api/query/route.js +160 -0
- package/dist/app/app/api/tables/[name]/route.js +173 -0
- package/dist/app/app/api/tables/route.js +174 -0
- package/dist/app/app/globals.css +93 -0
- package/dist/app/app/layout.jsx +35 -0
- package/dist/app/app/page.jsx +37 -0
- package/dist/app/globals.css +93 -0
- package/dist/app/layout.jsx +35 -0
- package/dist/app/page.jsx +37 -0
- package/dist/d1-studio.js +344 -0
- package/dist/lib/database-queue.js +33 -0
- package/dist/lib/error.js +20 -0
- package/dist/lib/find-wrangler-config.js +67 -0
- package/dist/lib/license.js +99 -0
- package/dist/lib/migrations.js +415 -0
- package/dist/lib/parse-sql-error.js +200 -0
- package/dist/lib/request-deduplication.js +24 -0
- package/dist/lib/schema-operations.js +103 -0
- package/dist/lib/sql-error-types.js +14 -0
- package/dist/lib/sql-operations.js +557 -0
- package/dist/lib/utils.js +6 -0
- package/next.config.mjs +18 -0
- package/package.json +97 -0
- package/postcss.config.mjs +8 -0
- package/scripts/studio.js +344 -0
- package/scripts/test.js +168 -0
- package/src/app/api/cloudflare/route.js +146 -0
- package/src/app/api/databases/route.js +72 -0
- package/src/app/api/migrations/sync/route.js +172 -0
- package/src/app/api/migrations/sync-files/route.js +172 -0
- package/src/app/api/pull-remote/confirm/route.js +277 -0
- package/src/app/api/pull-remote/preview/route.js +143 -0
- package/src/app/api/push-remote/confirm/route.js +196 -0
- package/src/app/api/push-remote/preview/route.js +178 -0
- package/src/app/api/query/route.js +160 -0
- package/src/app/api/tables/[name]/route.js +173 -0
- package/src/app/api/tables/route.js +174 -0
- package/src/app/globals.css +93 -0
- package/src/app/layout.jsx +35 -0
- package/src/app/page.jsx +37 -0
- package/src/components/cloudflare-credentials-modal.jsx +94 -0
- package/src/components/column-selector.jsx +133 -0
- package/src/components/column-sorting.jsx +65 -0
- package/src/components/delete-records-dialog.jsx +64 -0
- package/src/components/download-button.jsx +205 -0
- package/src/components/edit-error-alert.jsx +30 -0
- package/src/components/editable-cell.jsx +163 -0
- package/src/components/editable-row.jsx +169 -0
- package/src/components/environment-handler.jsx +278 -0
- package/src/components/environment-select.jsx +59 -0
- package/src/components/pull-preview-toast.jsx +188 -0
- package/src/components/push-error-alert.jsx +72 -0
- package/src/components/push-preview-toast.jsx +183 -0
- package/src/components/query-results.jsx +112 -0
- package/src/components/refresh-button.jsx +61 -0
- package/src/components/resize-columns.jsx +101 -0
- package/src/components/sidebar.jsx +236 -0
- package/src/components/sql-editor.jsx +411 -0
- package/src/components/sql-error-display.jsx +167 -0
- package/src/components/sync-migrations-button.jsx +87 -0
- package/src/components/table-error-dialog.jsx +65 -0
- package/src/components/table-toolbar.jsx +285 -0
- package/src/components/table-view.jsx +843 -0
- package/src/components/ui/alert.jsx +47 -0
- package/src/components/ui/button.jsx +48 -0
- package/src/components/ui/card.jsx +50 -0
- package/src/components/ui/checkbox.jsx +24 -0
- package/src/components/ui/collapsible.jsx +11 -0
- package/src/components/ui/dialog.jsx +96 -0
- package/src/components/ui/dropdown-menu.jsx +158 -0
- package/src/components/ui/input.jsx +19 -0
- package/src/components/ui/scroll-area.jsx +40 -0
- package/src/components/ui/select.jsx +121 -0
- package/src/components/ui/separator.jsx +25 -0
- package/src/components/ui/sheet.jsx +109 -0
- package/src/components/ui/sidebar.jsx +620 -0
- package/src/components/ui/skeleton.jsx +14 -0
- package/src/components/ui/table.jsx +86 -0
- package/src/components/ui/tabs.jsx +43 -0
- package/src/components/ui/toast.jsx +86 -0
- package/src/components/ui/toaster.jsx +35 -0
- package/src/components/ui/tooltip.jsx +28 -0
- package/src/context/database-context.js +510 -0
- package/src/hooks/use-mobile.jsx +19 -0
- package/src/hooks/use-sql-error.ts +23 -0
- package/src/hooks/use-toast.js +155 -0
- package/src/lib/database-queue.js +33 -0
- package/src/lib/error.js +20 -0
- package/src/lib/find-wrangler-config.js +67 -0
- package/src/lib/license.js +99 -0
- package/src/lib/migrations.js +415 -0
- package/src/lib/parse-sql-error.js +200 -0
- package/src/lib/request-deduplication.js +24 -0
- package/src/lib/schema-operations.js +103 -0
- package/src/lib/sql-error-types.js +14 -0
- package/src/lib/sql-operations.js +557 -0
- package/src/lib/utils.js +6 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// utils/database-queue.js
|
|
2
|
+
class DatabaseQueue {
|
|
3
|
+
constructor() {
|
|
4
|
+
this.queue = [];
|
|
5
|
+
this.isProcessing = false;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
async add(operation) {
|
|
9
|
+
return new Promise((resolve, reject) => {
|
|
10
|
+
this.queue.push({ operation, resolve, reject });
|
|
11
|
+
this.processNext();
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async processNext() {
|
|
16
|
+
if (this.isProcessing || this.queue.length === 0) return;
|
|
17
|
+
|
|
18
|
+
this.isProcessing = true;
|
|
19
|
+
const { operation, resolve, reject } = this.queue.shift();
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
const result = await operation();
|
|
23
|
+
resolve(result);
|
|
24
|
+
} catch (error) {
|
|
25
|
+
reject(error);
|
|
26
|
+
} finally {
|
|
27
|
+
this.isProcessing = false;
|
|
28
|
+
this.processNext();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const dbQueue = new DatabaseQueue();
|
package/src/lib/error.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// lib/errors.js
|
|
2
|
+
export class LicenseError extends Error {
|
|
3
|
+
constructor(message, type = 'VALIDATION_ERROR') {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = 'LicenseError';
|
|
6
|
+
this.type = type;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const ErrorTypes = {
|
|
11
|
+
LICENSE_NOT_FOUND: 'LICENSE_NOT_FOUND',
|
|
12
|
+
EMAIL_MISMATCH: 'EMAIL_MISMATCH',
|
|
13
|
+
INACTIVE_LICENSE: 'INACTIVE_LICENSE',
|
|
14
|
+
INACTIVE_SUBSCRIPTION: 'INACTIVE_SUBSCRIPTION',
|
|
15
|
+
EXPIRED_SUBSCRIPTION: 'EXPIRED_SUBSCRIPTION',
|
|
16
|
+
MAX_ACTIVATIONS: 'MAX_ACTIVATIONS',
|
|
17
|
+
CONNECTION_ERROR: 'CONNECTION_ERROR',
|
|
18
|
+
VALIDATION_ERROR: 'VALIDATION_ERROR',
|
|
19
|
+
SAVE_ERROR: 'SAVE_ERROR'
|
|
20
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// lib/find-wrangler-config.js
|
|
2
|
+
import { readFile } from 'fs/promises';
|
|
3
|
+
import { findUp } from 'find-up';
|
|
4
|
+
import { parse as parseToml } from '@iarna/toml';
|
|
5
|
+
import stripJsonComments from 'strip-json-comments';
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Finds and parses the Wrangler configuration file.
|
|
10
|
+
*
|
|
11
|
+
* @param {string} userProjectPath - The path where to look for the config file
|
|
12
|
+
* @returns {Promise<{config: object, projectPath: string}|null>}
|
|
13
|
+
*/
|
|
14
|
+
export async function findWranglerConfig(userProjectPath) {
|
|
15
|
+
try {
|
|
16
|
+
if (!userProjectPath || typeof userProjectPath !== 'string') {
|
|
17
|
+
throw new Error('Project path is required and must be a string');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
console.log(chalk.blue('Looking for wrangler configuration in:'), userProjectPath);
|
|
21
|
+
|
|
22
|
+
// Define the possible configuration file names in priority order.
|
|
23
|
+
const possibleFiles = ['wrangler.toml', 'wrangler.json', 'wrangler.jsonc'];
|
|
24
|
+
let configPath = null;
|
|
25
|
+
let configFileUsed = null;
|
|
26
|
+
|
|
27
|
+
// Search for any of the possible config files
|
|
28
|
+
for (const file of possibleFiles) {
|
|
29
|
+
configPath = await findUp(file, { cwd: userProjectPath });
|
|
30
|
+
if (configPath) {
|
|
31
|
+
configFileUsed = file;
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!configPath) {
|
|
37
|
+
console.log(chalk.yellow('No Wrangler configuration file found in:'), userProjectPath);
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
console.log(chalk.green(`Found ${configFileUsed} at:`), configPath);
|
|
42
|
+
|
|
43
|
+
// Read the configuration file
|
|
44
|
+
let configContent = await readFile(configPath, 'utf-8');
|
|
45
|
+
let config;
|
|
46
|
+
|
|
47
|
+
// Parse based on file type
|
|
48
|
+
if (configFileUsed.endsWith('.toml')) {
|
|
49
|
+
config = parseToml(configContent);
|
|
50
|
+
} else {
|
|
51
|
+
// If it's JSONC, strip comments before parsing
|
|
52
|
+
if (configFileUsed.endsWith('.jsonc')) {
|
|
53
|
+
configContent = stripJsonComments(configContent);
|
|
54
|
+
}
|
|
55
|
+
config = JSON.parse(configContent);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
config,
|
|
60
|
+
projectPath: userProjectPath
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
} catch (error) {
|
|
64
|
+
console.error(chalk.red('Error reading Wrangler configuration:'), error.message);
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// lib/license.js
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { readFile, writeFile, mkdir } from 'node:fs/promises';
|
|
5
|
+
import { createHash } from 'crypto';
|
|
6
|
+
import { LicenseError } from './errors.js';
|
|
7
|
+
|
|
8
|
+
export async function generateHardwareId() {
|
|
9
|
+
const osInfo = {
|
|
10
|
+
platform: process.platform,
|
|
11
|
+
arch: process.arch,
|
|
12
|
+
cpus: os.cpus().length,
|
|
13
|
+
hostname: os.hostname(),
|
|
14
|
+
username: os.userInfo().username
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const systemString = JSON.stringify(osInfo);
|
|
18
|
+
return createHash('sha256').update(systemString).digest('hex');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function checkLicenseFile() {
|
|
22
|
+
const licenseDir = join(os.homedir(), '.d1studio');
|
|
23
|
+
const licensePath = join(licenseDir, 'license.json');
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
const licenseData = JSON.parse(await readFile(licensePath, 'utf8'));
|
|
27
|
+
const hardwareId = await generateHardwareId();
|
|
28
|
+
|
|
29
|
+
if (licenseData.hardwareId !== hardwareId) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return licenseData;
|
|
34
|
+
} catch (error) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function saveLicenseFile(licenseData) {
|
|
40
|
+
const licenseDir = join(os.homedir(), '.d1studio');
|
|
41
|
+
const licensePath = join(licenseDir, 'license.json');
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
await mkdir(licenseDir, { recursive: true });
|
|
45
|
+
await writeFile(licensePath, JSON.stringify(licenseData, null, 2));
|
|
46
|
+
} catch (error) {
|
|
47
|
+
throw new LicenseError('Failed to save license information', 'SAVE_ERROR');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function validateLicenseRemote(licenseKey, email, hardwareId) {
|
|
52
|
+
const baseUrl = process.env.NODE_ENV === 'development'
|
|
53
|
+
? 'http://localhost:4000'
|
|
54
|
+
: 'https://d1-studio.com';
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
const response = await fetch(`${baseUrl}/api/validate-license`, {
|
|
58
|
+
method: 'POST',
|
|
59
|
+
headers: {
|
|
60
|
+
'Content-Type': 'application/json',
|
|
61
|
+
},
|
|
62
|
+
body: JSON.stringify({
|
|
63
|
+
licenseKey,
|
|
64
|
+
email,
|
|
65
|
+
hardwareId
|
|
66
|
+
})
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const result = await response.json();
|
|
70
|
+
|
|
71
|
+
if (!response.ok) {
|
|
72
|
+
throw new LicenseError(
|
|
73
|
+
result.message || 'License validation failed',
|
|
74
|
+
result.type || 'VALIDATION_ERROR'
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return result;
|
|
79
|
+
|
|
80
|
+
} catch (error) {
|
|
81
|
+
if (error instanceof LicenseError) {
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (error.name === 'TypeError' && error.message.includes('fetch')) {
|
|
86
|
+
throw new LicenseError(
|
|
87
|
+
process.env.NODE_ENV === 'development'
|
|
88
|
+
? 'Unable to connect to local license validation service. Make sure the development server is running.'
|
|
89
|
+
: 'Unable to connect to license server. Please check your internet connection.',
|
|
90
|
+
'CONNECTION_ERROR'
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
throw new LicenseError(
|
|
95
|
+
'An unexpected error occurred during license validation',
|
|
96
|
+
'VALIDATION_ERROR'
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
// /lib/migrations.js
|
|
2
|
+
|
|
3
|
+
import { readFile } from 'fs/promises';
|
|
4
|
+
import { promisify } from 'util';
|
|
5
|
+
import { exec } from 'child_process';
|
|
6
|
+
import { createHash } from 'crypto';
|
|
7
|
+
import { mkdir, writeFile } from 'fs/promises';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import {
|
|
10
|
+
executeSQL,
|
|
11
|
+
ErrorTypes,
|
|
12
|
+
formatMigrationContent,
|
|
13
|
+
parseStatement,
|
|
14
|
+
validateStatement,
|
|
15
|
+
Operations,
|
|
16
|
+
tableExists
|
|
17
|
+
} from './sql-operations.js';
|
|
18
|
+
import { analyzeDependencies } from '@/lib/schema-operations';
|
|
19
|
+
|
|
20
|
+
const execAsync = promisify(exec);
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Initializes the migrations table.
|
|
24
|
+
* For remote environments: table with (id, name, applied_at).
|
|
25
|
+
* For local environments: table with (id, name, checksum, applied_at),
|
|
26
|
+
* where checksum is optional.
|
|
27
|
+
*
|
|
28
|
+
* If reset is true (e.g. during a pull operation), the table is cleared.
|
|
29
|
+
*
|
|
30
|
+
* @param {string} projectDir
|
|
31
|
+
* @param {string} databaseName
|
|
32
|
+
* @param {('local'|'remote')} environment
|
|
33
|
+
* @param {boolean} [reset=false] - If true, clear existing records.
|
|
34
|
+
*/
|
|
35
|
+
export async function initMigrationsTable(projectDir, databaseName, environment = 'local', reset = false) {
|
|
36
|
+
const flag = environment === 'remote' ? '--remote' : '--local';
|
|
37
|
+
// For remote, keep original D1 schema
|
|
38
|
+
const createTableSQL = environment === 'remote'
|
|
39
|
+
? `
|
|
40
|
+
CREATE TABLE IF NOT EXISTS d1_migrations (
|
|
41
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
42
|
+
name TEXT NOT NULL UNIQUE,
|
|
43
|
+
applied_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
44
|
+
);
|
|
45
|
+
`
|
|
46
|
+
: `
|
|
47
|
+
CREATE TABLE IF NOT EXISTS d1_migrations (
|
|
48
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
49
|
+
name TEXT NOT NULL UNIQUE,
|
|
50
|
+
checksum TEXT NULL,
|
|
51
|
+
applied_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
52
|
+
);
|
|
53
|
+
`;
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
// Create the table if it doesn't exist
|
|
57
|
+
await execAsync(
|
|
58
|
+
`wrangler d1 execute ${databaseName} ${flag} --command="${createTableSQL.replace(/"/g, '\\"')}"`,
|
|
59
|
+
{ cwd: projectDir }
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
if (reset) {
|
|
63
|
+
const deleteSQL = `DELETE FROM d1_migrations;`;
|
|
64
|
+
await execAsync(
|
|
65
|
+
`wrangler d1 execute ${databaseName} ${flag} --command="${deleteSQL.replace(/"/g, '\\"')}"`,
|
|
66
|
+
{ cwd: projectDir }
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
} catch (error) {
|
|
70
|
+
console.error(`Failed to initialize migrations table in ${environment}:`, error);
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Generate a migration file name using a stable naming convention.
|
|
77
|
+
* Format: YYYYMMDDHHMMSS_operation_tablename[optional-description].sql
|
|
78
|
+
*
|
|
79
|
+
* @param {Object} stmt - The SQL statement object containing type, tableName and optional description
|
|
80
|
+
* @returns {string} The formatted migration filename
|
|
81
|
+
*/
|
|
82
|
+
function getMigrationFileName(stmt) {
|
|
83
|
+
const timestamp = new Date().toISOString().replace(/[^0-9]/g, '').slice(0, -3);
|
|
84
|
+
const operation = stmt.type.toLowerCase().replace(/_/g, '-');
|
|
85
|
+
|
|
86
|
+
// Map for non-index operations.
|
|
87
|
+
const operationMap = {
|
|
88
|
+
'create-table': 'create-table',
|
|
89
|
+
'alter-table': 'alter-table',
|
|
90
|
+
'drop-table': 'drop-table',
|
|
91
|
+
'drop-index': 'drop-index',
|
|
92
|
+
'create-view': 'create-view',
|
|
93
|
+
'drop-view': 'drop-view',
|
|
94
|
+
'add-foreign-key': 'add-foreign-key',
|
|
95
|
+
'drop-foreign-key': 'drop-foreign-key',
|
|
96
|
+
'add-constraint': 'add-constraint',
|
|
97
|
+
'drop-constraint': 'drop-constraint'
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
if (stmt.type === 'CREATE_INDEX') {
|
|
101
|
+
// For index migrations, simply include the operation and the description.
|
|
102
|
+
const desc = stmt.description.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-_]/g, '');
|
|
103
|
+
return `${timestamp}_create-index_${desc}.sql`;
|
|
104
|
+
} else {
|
|
105
|
+
let fileName = `${timestamp}_${operationMap[operation] || operation}_${stmt.tableName}`;
|
|
106
|
+
// Append description for alter migrations if available.
|
|
107
|
+
if (stmt.type === 'ALTER_TABLE' && stmt.description) {
|
|
108
|
+
fileName += `_${stmt.description}`;
|
|
109
|
+
}
|
|
110
|
+
fileName = fileName.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-_]/g, '');
|
|
111
|
+
return fileName + '.sql';
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Validates a migration file by reading, parsing, and validating its SQL.
|
|
117
|
+
*
|
|
118
|
+
* @param {string} filePath - Full path to the migration file.
|
|
119
|
+
* @returns {object} Object with `isValid` flag and either the parsed statement or errors.
|
|
120
|
+
*/
|
|
121
|
+
export async function validateMigrationFile(filePath) {
|
|
122
|
+
const content = await readFile(filePath, 'utf8');
|
|
123
|
+
try {
|
|
124
|
+
const statement = parseStatement(content);
|
|
125
|
+
validateStatement(statement);
|
|
126
|
+
return { isValid: true, statement };
|
|
127
|
+
} catch (error) {
|
|
128
|
+
return { isValid: false, errors: error };
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Get remote migrations
|
|
133
|
+
export async function getRemoteMigrations(projectDir, databaseName, env) {
|
|
134
|
+
try {
|
|
135
|
+
const result = await executeSQL(
|
|
136
|
+
projectDir,
|
|
137
|
+
databaseName,
|
|
138
|
+
'SELECT name, applied_at FROM d1_migrations ORDER BY applied_at;',
|
|
139
|
+
env,
|
|
140
|
+
true
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
if (typeof result === 'string') {
|
|
144
|
+
const jsonStart = result.indexOf('[');
|
|
145
|
+
if (jsonStart !== -1) {
|
|
146
|
+
const jsonStr = result.substring(jsonStart);
|
|
147
|
+
const parsed = JSON.parse(jsonStr);
|
|
148
|
+
return parsed[0]?.results || [];
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return [];
|
|
152
|
+
} catch (error) {
|
|
153
|
+
throw {
|
|
154
|
+
type: ErrorTypes.EXECUTION_ERROR,
|
|
155
|
+
message: 'Failed to get remote migrations',
|
|
156
|
+
details: {
|
|
157
|
+
error: error.message,
|
|
158
|
+
suggestions: [
|
|
159
|
+
'Check remote database permissions',
|
|
160
|
+
'Verify credentials and connection'
|
|
161
|
+
]
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Records (or tracks) a migration.
|
|
169
|
+
* For remote, skips checksum.
|
|
170
|
+
*
|
|
171
|
+
* @param {string} projectDir
|
|
172
|
+
* @param {string} databaseName
|
|
173
|
+
* @param {Object} migrationData { name, checksum }
|
|
174
|
+
* @param {('local'|'remote')} environment
|
|
175
|
+
*/
|
|
176
|
+
// Update tracking function to handle both cases
|
|
177
|
+
export async function trackMigration(projectDir, databaseName, { name, checksum }, environment = 'local') {
|
|
178
|
+
const flag = environment === 'remote' ? '--remote' : '--local';
|
|
179
|
+
|
|
180
|
+
// For Studio-originated migrations (with checksum), check if we need to add the column
|
|
181
|
+
if (environment === 'local' && checksum) {
|
|
182
|
+
try {
|
|
183
|
+
// Check if checksum column exists
|
|
184
|
+
const hasChecksumColumn = await execAsync(
|
|
185
|
+
`wrangler d1 execute ${databaseName} ${flag} --command="SELECT checksum FROM d1_migrations LIMIT 1;"`,
|
|
186
|
+
{ cwd: projectDir }
|
|
187
|
+
).catch(() => false);
|
|
188
|
+
|
|
189
|
+
// If column doesn't exist, alter table to add it
|
|
190
|
+
if (!hasChecksumColumn) {
|
|
191
|
+
await execAsync(
|
|
192
|
+
`wrangler d1 execute ${databaseName} ${flag} --command="ALTER TABLE d1_migrations ADD COLUMN checksum TEXT;"`,
|
|
193
|
+
{ cwd: projectDir }
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
} catch (error) {
|
|
197
|
+
// Ignore errors - the column might already exist
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// For Studio migrations (with checksum)
|
|
202
|
+
const insertSQL = environment === 'remote' || !checksum
|
|
203
|
+
? `
|
|
204
|
+
INSERT OR REPLACE INTO d1_migrations (name, applied_at)
|
|
205
|
+
VALUES ('${name}', CURRENT_TIMESTAMP);
|
|
206
|
+
`
|
|
207
|
+
: `
|
|
208
|
+
INSERT OR REPLACE INTO d1_migrations (name, checksum, applied_at)
|
|
209
|
+
VALUES ('${name}', '${checksum}', CURRENT_TIMESTAMP);
|
|
210
|
+
`;
|
|
211
|
+
|
|
212
|
+
try {
|
|
213
|
+
await execAsync(
|
|
214
|
+
`wrangler d1 execute ${databaseName} ${flag} --command="${insertSQL.replace(/"/g, '\\"')}"`,
|
|
215
|
+
{ cwd: projectDir }
|
|
216
|
+
);
|
|
217
|
+
} catch (error) {
|
|
218
|
+
console.error('Failed to track migration:', error);
|
|
219
|
+
throw error;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Records a migration (used during push).
|
|
225
|
+
* For remote, checksum is omitted.
|
|
226
|
+
*
|
|
227
|
+
* @param {string} projectDir
|
|
228
|
+
* @param {string} databaseName
|
|
229
|
+
* @param {Object} migrationData { name, checksum }
|
|
230
|
+
* @param {object} env
|
|
231
|
+
* @param {boolean} remote
|
|
232
|
+
*/
|
|
233
|
+
export async function recordMigration(projectDir, databaseName, { name, checksum }, env = {}, remote = false) {
|
|
234
|
+
// For CLI migrations or remote, don't include checksum
|
|
235
|
+
const sql = remote || !checksum
|
|
236
|
+
? `
|
|
237
|
+
INSERT OR REPLACE INTO d1_migrations (name, applied_at)
|
|
238
|
+
VALUES ('${name}', CURRENT_TIMESTAMP);
|
|
239
|
+
`
|
|
240
|
+
: `
|
|
241
|
+
INSERT OR REPLACE INTO d1_migrations (name, checksum, applied_at)
|
|
242
|
+
VALUES ('${name}', '${checksum}', CURRENT_TIMESTAMP);
|
|
243
|
+
`;
|
|
244
|
+
|
|
245
|
+
try {
|
|
246
|
+
await executeSQL(projectDir, databaseName, sql, env, remote);
|
|
247
|
+
} catch (error) {
|
|
248
|
+
throw {
|
|
249
|
+
type: ErrorTypes.EXECUTION_ERROR,
|
|
250
|
+
message: `Failed to record migration ${name}`,
|
|
251
|
+
details: {
|
|
252
|
+
file: name,
|
|
253
|
+
error: error.message,
|
|
254
|
+
suggestions: ['Check if migration is already recorded']
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Compares local and remote migrations.
|
|
262
|
+
*
|
|
263
|
+
* @param {string} projectDir
|
|
264
|
+
* @param {string} databaseName
|
|
265
|
+
* @returns {Promise<Object>} { applied: Array, count: Number }
|
|
266
|
+
*/
|
|
267
|
+
export async function compareMigrations(projectDir, databaseName) {
|
|
268
|
+
try {
|
|
269
|
+
const migrations = await getRemoteMigrations(projectDir, databaseName, {});
|
|
270
|
+
const migrationMap = new Map(migrations.map(m => [m.name, m]));
|
|
271
|
+
return {
|
|
272
|
+
applied: Array.from(migrationMap.keys()),
|
|
273
|
+
count: migrationMap.size
|
|
274
|
+
};
|
|
275
|
+
} catch (error) {
|
|
276
|
+
console.error('Failed to compare migrations:', error);
|
|
277
|
+
throw error;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Generates a SHA256 checksum for migration content.
|
|
283
|
+
*
|
|
284
|
+
* @param {string} content
|
|
285
|
+
* @returns {string}
|
|
286
|
+
*/
|
|
287
|
+
export function createChecksum(content) {
|
|
288
|
+
return createHash('sha256').update(content).digest('hex');
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Processes schema changes (DDL) and creates migration files.
|
|
293
|
+
* Uses a stable naming convention:
|
|
294
|
+
* YYYYMMDDHHMMSS_operation_tablename[optional-description].sql
|
|
295
|
+
*
|
|
296
|
+
* Executes the SQL, writes the migration file, and tracks it.
|
|
297
|
+
* If any part fails, the process aborts (all-or-nothing).
|
|
298
|
+
*
|
|
299
|
+
* This updated version uses analyzeDependencies to order the changes.
|
|
300
|
+
*
|
|
301
|
+
* @param {string} projectDir
|
|
302
|
+
* @param {string} databaseName
|
|
303
|
+
* @param {Array<Object>} statements - Parsed schema change statements.
|
|
304
|
+
* Each statement is expected to have at least a `tableName` (if applicable) and `sql` property.
|
|
305
|
+
* @returns {Promise<Array>} Array of migration records.
|
|
306
|
+
*/
|
|
307
|
+
export async function handleSchemaChange(projectDir, databaseName, statements) {
|
|
308
|
+
try {
|
|
309
|
+
const migrationsDir = path.join(projectDir, 'migrations');
|
|
310
|
+
await mkdir(migrationsDir, { recursive: true });
|
|
311
|
+
const results = [];
|
|
312
|
+
|
|
313
|
+
// Partition statements into schema changes and data modifications.
|
|
314
|
+
const schemaChanges = [];
|
|
315
|
+
const dataModifications = [];
|
|
316
|
+
for (const stmt of statements) {
|
|
317
|
+
if (stmt.type === Operations.DATA_MODIFICATION) {
|
|
318
|
+
dataModifications.push(stmt);
|
|
319
|
+
} else {
|
|
320
|
+
schemaChanges.push(stmt);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Group schema change statements by table name.
|
|
325
|
+
const groupedStatements = new Map();
|
|
326
|
+
const NO_TABLE_KEY = '__no_table__';
|
|
327
|
+
for (const stmt of schemaChanges) {
|
|
328
|
+
const key = stmt.tableName ? stmt.tableName.toLowerCase() : NO_TABLE_KEY;
|
|
329
|
+
if (!groupedStatements.has(key)) {
|
|
330
|
+
groupedStatements.set(key, []);
|
|
331
|
+
}
|
|
332
|
+
groupedStatements.get(key).push(stmt);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// For those with a table name, get an ordered list using analyzeDependencies.
|
|
336
|
+
const tableKeys = [...groupedStatements.keys()].filter(key => key !== NO_TABLE_KEY);
|
|
337
|
+
let orderedTables = [];
|
|
338
|
+
if (tableKeys.length > 0) {
|
|
339
|
+
const sampleStatements = tableKeys.map(key => groupedStatements.get(key)[0]);
|
|
340
|
+
orderedTables = analyzeDependencies(sampleStatements);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Process schema changes (create migration files).
|
|
344
|
+
for (const tableName of orderedTables) {
|
|
345
|
+
const stmts = groupedStatements.get(tableName) || [];
|
|
346
|
+
for (const stmt of stmts) {
|
|
347
|
+
try {
|
|
348
|
+
// For DROP TABLE statements, check if the table exists.
|
|
349
|
+
if (stmt.type === Operations.DROP_TABLE) {
|
|
350
|
+
const exists = await tableExists(projectDir, databaseName, {}, stmt.tableName, false);
|
|
351
|
+
if (!exists) {
|
|
352
|
+
throw new Error(`no such table: ${stmt.tableName}`);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
// Execute the schema change SQL.
|
|
356
|
+
const result = await executeSQL(projectDir, databaseName, stmt.sql);
|
|
357
|
+
// Check the output for syntax error messages.
|
|
358
|
+
if (
|
|
359
|
+
result &&
|
|
360
|
+
(result.toLowerCase().includes('syntax error') ||
|
|
361
|
+
result.toLowerCase().includes('sqlite_error'))
|
|
362
|
+
) {
|
|
363
|
+
throw new Error(result);
|
|
364
|
+
}
|
|
365
|
+
// Generate a migration file.
|
|
366
|
+
const migrationFileName = getMigrationFileName(stmt);
|
|
367
|
+
const migrationPath = path.join(migrationsDir, migrationFileName);
|
|
368
|
+
const formattedSQL = formatMigrationContent(stmt.sql);
|
|
369
|
+
await writeFile(migrationPath, formattedSQL, 'utf8');
|
|
370
|
+
// Compute checksum and track the migration.
|
|
371
|
+
const checksum = createChecksum(stmt.sql);
|
|
372
|
+
await trackMigration(projectDir, databaseName, { name: migrationFileName, checksum }, 'local');
|
|
373
|
+
results.push({ type: stmt.type, table: stmt.tableName, migration: migrationFileName, checksum });
|
|
374
|
+
} catch (error) {
|
|
375
|
+
throw error;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
groupedStatements.delete(tableName);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Process any remaining schema changes with no table name.
|
|
382
|
+
if (groupedStatements.has(NO_TABLE_KEY)) {
|
|
383
|
+
for (const stmt of groupedStatements.get(NO_TABLE_KEY)) {
|
|
384
|
+
try {
|
|
385
|
+
await executeSQL(projectDir, databaseName, stmt.sql);
|
|
386
|
+
const migrationFileName = getMigrationFileName(stmt);
|
|
387
|
+
const migrationPath = path.join(migrationsDir, migrationFileName);
|
|
388
|
+
const formattedSQL = formatMigrationContent(stmt.sql);
|
|
389
|
+
await writeFile(migrationPath, formattedSQL, 'utf8');
|
|
390
|
+
const checksum = createChecksum(stmt.sql);
|
|
391
|
+
await trackMigration(projectDir, databaseName, { name: migrationFileName, checksum }, 'local');
|
|
392
|
+
results.push({ type: stmt.type, table: stmt.tableName || null, migration: migrationFileName, checksum });
|
|
393
|
+
} catch (error) {
|
|
394
|
+
throw error;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Now process data modification statements (execute them, but don't create migration files).
|
|
400
|
+
for (const stmt of dataModifications) {
|
|
401
|
+
try {
|
|
402
|
+
await executeSQL(projectDir, databaseName, stmt.sql);
|
|
403
|
+
// Record the success of the data modification execution.
|
|
404
|
+
results.push({ type: stmt.type, table: stmt.tableName || null, executed: true });
|
|
405
|
+
} catch (error) {
|
|
406
|
+
throw error;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
return results;
|
|
411
|
+
} catch (error) {
|
|
412
|
+
throw error;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|