d1s 0.2.6 → 0.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (247) hide show
  1. package/README.md +3 -1
  2. package/dist/.next/.next/BUILD_ID +1 -1
  3. package/dist/.next/.next/app-build-manifest.json +34 -27
  4. package/dist/.next/.next/app-path-routes-manifest.json +1 -1
  5. package/dist/.next/.next/build-manifest.json +2 -2
  6. package/dist/.next/.next/cache/.rscinfo +1 -1
  7. package/dist/.next/.next/cache/.tsbuildinfo +1 -1
  8. package/dist/.next/.next/cache/webpack/client-development/0.pack.gz +0 -0
  9. package/dist/.next/.next/cache/webpack/client-development/1.pack.gz +0 -0
  10. package/dist/.next/.next/cache/webpack/client-development/2.pack.gz +0 -0
  11. package/dist/.next/.next/cache/webpack/client-development/3.pack.gz +0 -0
  12. package/dist/.next/.next/cache/webpack/client-development/4.pack.gz +0 -0
  13. package/dist/.next/.next/cache/webpack/client-development/index.pack.gz +0 -0
  14. package/dist/.next/.next/cache/webpack/client-development/index.pack.gz.old +0 -0
  15. package/dist/.next/.next/cache/webpack/client-production/0.pack +0 -0
  16. package/dist/.next/.next/cache/webpack/client-production/1.pack +0 -0
  17. package/dist/.next/.next/cache/webpack/client-production/10.pack +0 -0
  18. package/dist/.next/.next/cache/webpack/client-production/11.pack +0 -0
  19. package/dist/.next/.next/cache/webpack/client-production/12.pack +0 -0
  20. package/dist/.next/.next/cache/webpack/client-production/13.pack +0 -0
  21. package/dist/.next/.next/cache/webpack/client-production/14.pack +0 -0
  22. package/dist/.next/.next/cache/webpack/client-production/2.pack +0 -0
  23. package/dist/.next/.next/cache/webpack/client-production/3.pack +0 -0
  24. package/dist/.next/.next/cache/webpack/client-production/4.pack +0 -0
  25. package/dist/.next/.next/cache/webpack/client-production/5.pack +0 -0
  26. package/dist/.next/.next/cache/webpack/client-production/6.pack +0 -0
  27. package/dist/.next/.next/cache/webpack/client-production/7.pack +0 -0
  28. package/dist/.next/.next/cache/webpack/client-production/8.pack +0 -0
  29. package/dist/.next/.next/cache/webpack/client-production/9.pack +0 -0
  30. package/dist/.next/.next/cache/webpack/client-production/index.pack +0 -0
  31. package/dist/.next/.next/cache/webpack/client-production/index.pack.old +0 -0
  32. package/dist/.next/.next/cache/webpack/edge-server-production/index.pack +0 -0
  33. package/dist/.next/.next/cache/webpack/server-development/0.pack.gz +0 -0
  34. package/dist/.next/.next/cache/webpack/server-development/1.pack.gz +0 -0
  35. package/dist/.next/.next/cache/webpack/server-development/2.pack.gz +0 -0
  36. package/dist/.next/.next/cache/webpack/server-development/3.pack.gz +0 -0
  37. package/dist/.next/.next/cache/webpack/server-development/4.pack.gz +0 -0
  38. package/dist/.next/.next/cache/webpack/server-development/index.pack.gz +0 -0
  39. package/dist/.next/.next/cache/webpack/server-development/index.pack.gz.old +0 -0
  40. package/dist/.next/.next/cache/webpack/server-production/0.pack +0 -0
  41. package/dist/.next/.next/cache/webpack/server-production/1.pack +0 -0
  42. package/dist/.next/.next/cache/webpack/server-production/10.pack +0 -0
  43. package/dist/.next/.next/cache/webpack/server-production/11.pack +0 -0
  44. package/dist/.next/.next/cache/webpack/server-production/12.pack +0 -0
  45. package/dist/.next/.next/cache/webpack/server-production/13.pack +0 -0
  46. package/dist/.next/.next/cache/webpack/server-production/2.pack +0 -0
  47. package/dist/.next/.next/cache/webpack/server-production/3.pack +0 -0
  48. package/dist/.next/.next/cache/webpack/server-production/4.pack +0 -0
  49. package/dist/.next/.next/cache/webpack/server-production/5.pack +0 -0
  50. package/dist/.next/.next/cache/webpack/server-production/6.pack +0 -0
  51. package/dist/.next/.next/cache/webpack/server-production/7.pack +0 -0
  52. package/dist/.next/.next/cache/webpack/server-production/8.pack +0 -0
  53. package/dist/.next/.next/cache/webpack/server-production/9.pack +0 -0
  54. package/dist/.next/.next/cache/webpack/server-production/index.pack +0 -0
  55. package/dist/.next/.next/cache/webpack/server-production/index.pack.old +0 -0
  56. package/dist/.next/.next/next-minimal-server.js.nft.json +1 -1
  57. package/dist/.next/.next/next-server.js.nft.json +1 -1
  58. package/dist/.next/.next/prerender-manifest.json +1 -1
  59. package/dist/.next/.next/server/app/_not-found/page.js +1 -1
  60. package/dist/.next/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  61. package/dist/.next/.next/server/app/_not-found.html +1 -1
  62. package/dist/.next/.next/server/app/_not-found.rsc +2 -2
  63. package/dist/.next/.next/server/app/api/cloudflare/route.js +1 -1
  64. package/dist/.next/.next/server/app/api/cloudflare/route.js.nft.json +1 -1
  65. package/dist/.next/.next/server/app/api/cloudflare/route_client-reference-manifest.js +1 -1
  66. package/dist/.next/.next/server/app/api/databases/route.js +1 -1
  67. package/dist/.next/.next/server/app/api/databases/route.js.nft.json +1 -1
  68. package/dist/.next/.next/server/app/api/databases/route_client-reference-manifest.js +1 -1
  69. package/dist/.next/.next/server/app/api/migrations/sync/route.js +3 -3
  70. package/dist/.next/.next/server/app/api/migrations/sync/route.js.nft.json +1 -1
  71. package/dist/.next/.next/server/app/api/migrations/sync/route_client-reference-manifest.js +1 -1
  72. package/dist/.next/.next/server/app/api/migrations/sync-files/route.js +1 -1
  73. package/dist/.next/.next/server/app/api/migrations/sync-files/route.js.nft.json +1 -1
  74. package/dist/.next/.next/server/app/api/migrations/sync-files/route_client-reference-manifest.js +1 -1
  75. package/dist/.next/.next/server/app/api/pull-remote/confirm/route.js +1 -1
  76. package/dist/.next/.next/server/app/api/pull-remote/confirm/route.js.nft.json +1 -1
  77. package/dist/.next/.next/server/app/api/pull-remote/confirm/route_client-reference-manifest.js +1 -1
  78. package/dist/.next/.next/server/app/api/pull-remote/preview/route.js +1 -1
  79. package/dist/.next/.next/server/app/api/pull-remote/preview/route.js.nft.json +1 -1
  80. package/dist/.next/.next/server/app/api/pull-remote/preview/route_client-reference-manifest.js +1 -1
  81. package/dist/.next/.next/server/app/api/push-remote/confirm/route.js +2 -2
  82. package/dist/.next/.next/server/app/api/push-remote/confirm/route.js.nft.json +1 -1
  83. package/dist/.next/.next/server/app/api/push-remote/confirm/route_client-reference-manifest.js +1 -1
  84. package/dist/.next/.next/server/app/api/push-remote/preview/route.js +2 -2
  85. package/dist/.next/.next/server/app/api/push-remote/preview/route.js.nft.json +1 -1
  86. package/dist/.next/.next/server/app/api/push-remote/preview/route_client-reference-manifest.js +1 -1
  87. package/dist/.next/.next/server/app/api/query/route.js +2 -2
  88. package/dist/.next/.next/server/app/api/query/route.js.nft.json +1 -1
  89. package/dist/.next/.next/server/app/api/query/route_client-reference-manifest.js +1 -1
  90. package/dist/.next/.next/server/app/api/tables/[name]/route.js +4 -4
  91. package/dist/.next/.next/server/app/api/tables/[name]/route.js.nft.json +1 -1
  92. package/dist/.next/.next/server/app/api/tables/[name]/route_client-reference-manifest.js +1 -1
  93. package/dist/.next/.next/server/app/api/tables/route.js +2 -2
  94. package/dist/.next/.next/server/app/api/tables/route.js.nft.json +1 -1
  95. package/dist/.next/.next/server/app/api/tables/route_client-reference-manifest.js +1 -1
  96. package/dist/.next/.next/server/app/api/update/route.js +5 -0
  97. package/dist/.next/.next/server/app/api/update/route.js.nft.json +1 -0
  98. package/dist/.next/.next/server/app/api/update/route_client-reference-manifest.js +1 -0
  99. package/dist/.next/.next/server/app/index.html +3 -3
  100. package/dist/.next/.next/server/app/index.rsc +3 -3
  101. package/dist/.next/.next/server/app/page.js +14 -12
  102. package/dist/.next/.next/server/app/page_client-reference-manifest.js +1 -1
  103. package/dist/.next/.next/server/app-paths-manifest.json +4 -3
  104. package/dist/.next/.next/server/chunks/336.js +1 -1
  105. package/dist/.next/.next/server/chunks/391.js +23 -0
  106. package/dist/.next/.next/server/chunks/452.js +1 -0
  107. package/dist/.next/.next/server/chunks/627.js +3 -3
  108. package/dist/.next/.next/server/chunks/638.js +3 -3
  109. package/dist/.next/.next/server/chunks/801.js +55 -0
  110. package/dist/.next/.next/server/chunks/992.js +3 -3
  111. package/dist/.next/.next/server/pages/404.html +1 -1
  112. package/dist/.next/.next/server/pages/500.html +1 -1
  113. package/dist/.next/.next/server/pages/_app.js +1 -1
  114. package/dist/.next/.next/server/pages/_document.js +1 -1
  115. package/dist/.next/.next/server/pages/_error.js +1 -1
  116. package/dist/.next/.next/server/pages-manifest.json +1 -1
  117. package/dist/.next/.next/server/server-reference-manifest.json +1 -1
  118. package/dist/.next/.next/static/JrwgdSDcipr0Zh7k19Cj_/_buildManifest.js +1 -0
  119. package/dist/.next/.next/static/chunks/958-98887928efebfdf7.js +1 -0
  120. package/dist/.next/.next/static/chunks/app/api/cloudflare/route-5133155a604f2719.js +1 -0
  121. package/dist/.next/.next/static/chunks/app/api/databases/route-63a31080b565794e.js +1 -0
  122. package/dist/.next/.next/static/chunks/app/api/migrations/sync/route-d97667565b4a59b6.js +1 -0
  123. package/dist/.next/.next/static/chunks/app/api/migrations/sync-files/route-7089bcbfeb6b5260.js +1 -0
  124. package/dist/.next/.next/static/chunks/app/api/pull-remote/confirm/route-9143e6e4c3f05162.js +1 -0
  125. package/dist/.next/.next/static/chunks/app/api/pull-remote/preview/route-c963d143a51aed66.js +1 -0
  126. package/dist/.next/.next/static/chunks/app/api/push-remote/confirm/route-072132c0a6cb3df3.js +1 -0
  127. package/dist/.next/.next/static/chunks/app/api/push-remote/preview/route-ca6bf35aa72661ee.js +1 -0
  128. package/dist/.next/.next/static/chunks/app/api/query/route-1466f6b7ffd8b6c0.js +1 -0
  129. package/dist/.next/.next/static/chunks/app/api/tables/[name]/route-898d3f85fc8544c4.js +1 -0
  130. package/dist/.next/.next/static/chunks/app/api/tables/route-91650359fc3735cb.js +1 -0
  131. package/dist/.next/.next/static/chunks/app/api/update/route-504d663df9124f97.js +1 -0
  132. package/dist/.next/.next/static/chunks/app/page-2c28a255a4ecd8d4.js +1 -0
  133. package/dist/.next/.next/static/chunks/app/page-3db7feef57019055.js +1 -0
  134. package/dist/.next/.next/static/css/4957328384ed5c42.css +3 -0
  135. package/dist/.next/.next/static/gc8l1G6KXOeLLOM7VJip6/_buildManifest.js +1 -0
  136. package/dist/.next/.next/static/kYsJoni86CJWDuTkc1xU6/_buildManifest.js +1 -0
  137. package/dist/.next/.next/static/kYsJoni86CJWDuTkc1xU6/_ssgManifest.js +1 -0
  138. package/dist/.next/.next/static/scC5qwQC9H7rR1groM0Q-/_ssgManifest.js +1 -0
  139. package/dist/.next/.next/static/tEHOI_-t-iqfJClNzuo9m/_buildManifest.js +1 -0
  140. package/dist/.next/.next/static/tEHOI_-t-iqfJClNzuo9m/_ssgManifest.js +1 -0
  141. package/dist/.next/.next/static/vin2VQs5pM_dH2e2_I7AN/_buildManifest.js +1 -0
  142. package/dist/.next/.next/static/vin2VQs5pM_dH2e2_I7AN/_ssgManifest.js +1 -0
  143. package/dist/.next/.next/static/wHKwQhaxin5_i06GFVDHb/_buildManifest.js +1 -0
  144. package/dist/.next/.next/static/wHKwQhaxin5_i06GFVDHb/_ssgManifest.js +1 -0
  145. package/dist/.next/.next/static/xW4MspD8XibrhXDi_q2IO/_ssgManifest.js +1 -0
  146. package/dist/.next/.next/trace +3 -3
  147. package/dist/.next/.next/types/app/api/update/route.ts +347 -0
  148. package/dist/.next/BUILD_ID +1 -1
  149. package/dist/.next/app-build-manifest.json +17 -17
  150. package/dist/.next/app-path-routes-manifest.json +1 -1
  151. package/dist/.next/build-manifest.json +2 -2
  152. package/dist/.next/cache/.rscinfo +1 -1
  153. package/dist/.next/cache/.tsbuildinfo +1 -1
  154. package/dist/.next/cache/webpack/client-production/0.pack +0 -0
  155. package/dist/.next/cache/webpack/client-production/index.pack +0 -0
  156. package/dist/.next/cache/webpack/edge-server-production/index.pack +0 -0
  157. package/dist/.next/cache/webpack/server-production/0.pack +0 -0
  158. package/dist/.next/cache/webpack/server-production/index.pack +0 -0
  159. package/dist/.next/next-minimal-server.js.nft.json +1 -1
  160. package/dist/.next/next-server.js.nft.json +1 -1
  161. package/dist/.next/prerender-manifest.json +1 -1
  162. package/dist/.next/server/app/_not-found.html +1 -1
  163. package/dist/.next/server/app/_not-found.rsc +1 -1
  164. package/dist/.next/server/app/api/cloudflare/route.js +1 -1
  165. package/dist/.next/server/app/api/databases/route.js +1 -1
  166. package/dist/.next/server/app/api/migrations/sync/route.js +3 -3
  167. package/dist/.next/server/app/api/migrations/sync/route.js.nft.json +1 -1
  168. package/dist/.next/server/app/api/migrations/sync-files/route.js +1 -1
  169. package/dist/.next/server/app/api/migrations/sync-files/route.js.nft.json +1 -1
  170. package/dist/.next/server/app/api/pull-remote/confirm/route.js +1 -1
  171. package/dist/.next/server/app/api/pull-remote/confirm/route.js.nft.json +1 -1
  172. package/dist/.next/server/app/api/pull-remote/preview/route.js +1 -1
  173. package/dist/.next/server/app/api/pull-remote/preview/route.js.nft.json +1 -1
  174. package/dist/.next/server/app/api/push-remote/confirm/route.js +2 -2
  175. package/dist/.next/server/app/api/push-remote/confirm/route.js.nft.json +1 -1
  176. package/dist/.next/server/app/api/push-remote/preview/route.js +2 -2
  177. package/dist/.next/server/app/api/push-remote/preview/route.js.nft.json +1 -1
  178. package/dist/.next/server/app/api/query/route.js +2 -2
  179. package/dist/.next/server/app/api/query/route.js.nft.json +1 -1
  180. package/dist/.next/server/app/api/tables/[name]/route.js +4 -4
  181. package/dist/.next/server/app/api/tables/route.js +2 -2
  182. package/dist/.next/server/app/index.html +1 -1
  183. package/dist/.next/server/app/index.rsc +1 -1
  184. package/dist/.next/server/app-paths-manifest.json +6 -6
  185. package/dist/.next/server/chunks/801.js +55 -0
  186. package/dist/.next/server/pages/404.html +1 -1
  187. package/dist/.next/server/pages/500.html +1 -1
  188. package/dist/.next/server/server-reference-manifest.json +1 -1
  189. package/dist/.next/static/ZwNOdZJ0LOtE0AJMNWwzY/_buildManifest.js +1 -0
  190. package/dist/.next/static/ZwNOdZJ0LOtE0AJMNWwzY/_ssgManifest.js +1 -0
  191. package/dist/.next/trace +14 -14
  192. package/dist/app/api/cloudflare/route.js +5 -1
  193. package/dist/app/api/databases/route.js +3 -4
  194. package/dist/app/api/migrations/sync/route.js +5 -3
  195. package/dist/app/api/pull-remote/confirm/route.js +24 -23
  196. package/dist/app/api/pull-remote/preview/route.js +4 -3
  197. package/dist/app/api/push-remote/preview/route.js +3 -2
  198. package/dist/app/api/query/route.js +11 -3
  199. package/dist/app/api/tables/[name]/route.js +3 -2
  200. package/dist/app/api/tables/route.js +6 -2
  201. package/dist/app/app/api/cloudflare/route.js +5 -1
  202. package/dist/app/app/api/databases/route.js +3 -4
  203. package/dist/app/app/api/migrations/sync/route.js +5 -3
  204. package/dist/app/app/api/pull-remote/confirm/route.js +24 -23
  205. package/dist/app/app/api/pull-remote/preview/route.js +4 -3
  206. package/dist/app/app/api/push-remote/preview/route.js +3 -2
  207. package/dist/app/app/api/query/route.js +11 -3
  208. package/dist/app/app/api/tables/[name]/route.js +3 -2
  209. package/dist/app/app/api/tables/route.js +6 -2
  210. package/dist/app/app/api/update/route.js +11 -0
  211. package/dist/app/app/page.jsx +7 -1
  212. package/dist/d1-studio.js +94 -58
  213. package/dist/lib/database-queue.js +34 -30
  214. package/dist/lib/find-wrangler-config.js +14 -8
  215. package/dist/lib/logger.js +11 -2
  216. package/dist/lib/migrations.js +4 -4
  217. package/dist/lib/schema-operations.js +4 -1
  218. package/dist/lib/sql-operations.js +8 -1
  219. package/package.json +3 -1
  220. package/scripts/studio.js +94 -58
  221. package/scripts/test.js +280 -82
  222. package/src/app/api/cloudflare/route.js +5 -1
  223. package/src/app/api/databases/route.js +3 -4
  224. package/src/app/api/migrations/sync/route.js +5 -3
  225. package/src/app/api/pull-remote/confirm/route.js +24 -23
  226. package/src/app/api/pull-remote/preview/route.js +4 -3
  227. package/src/app/api/push-remote/preview/route.js +3 -2
  228. package/src/app/api/query/route.js +11 -3
  229. package/src/app/api/tables/[name]/route.js +3 -2
  230. package/src/app/api/tables/route.js +6 -2
  231. package/src/app/api/update/route.js +11 -0
  232. package/src/app/page.jsx +7 -1
  233. package/src/components/update-banner.jsx +64 -0
  234. package/src/lib/database-queue.js +34 -30
  235. package/src/lib/find-wrangler-config.js +14 -8
  236. package/src/lib/logger.js +11 -2
  237. package/src/lib/migrations.js +4 -4
  238. package/src/lib/schema-operations.js +4 -1
  239. package/src/lib/sql-operations.js +8 -1
  240. package/dist/.next/.next/server/chunks/603.js +0 -55
  241. package/dist/.next/server/chunks/603.js +0 -55
  242. /package/dist/.next/.next/static/{HsZ8OanS5v86DqC9rErO- → IWRJZhjf8U8mjzz4h0VNE}/_buildManifest.js +0 -0
  243. /package/dist/.next/.next/static/{HsZ8OanS5v86DqC9rErO- → IWRJZhjf8U8mjzz4h0VNE}/_ssgManifest.js +0 -0
  244. /package/dist/.next/.next/static/{q6WmQmyONe88bMN0_jrC2 → JrwgdSDcipr0Zh7k19Cj_}/_ssgManifest.js +0 -0
  245. /package/dist/.next/{static/Tk1fnz_CXLavm23ZEeChG → .next/static/gc8l1G6KXOeLLOM7VJip6}/_ssgManifest.js +0 -0
  246. /package/dist/.next/.next/static/{q6WmQmyONe88bMN0_jrC2 → scC5qwQC9H7rR1groM0Q-}/_buildManifest.js +0 -0
  247. /package/dist/.next/{static/Tk1fnz_CXLavm23ZEeChG → .next/static/xW4MspD8XibrhXDi_q2IO}/_buildManifest.js +0 -0
@@ -12,6 +12,7 @@ import {
12
12
  } from '@/lib/sql-operations';
13
13
  import { getCurrentSchema, analyzeDependencies } from '@/lib/schema-operations';
14
14
  import { initMigrationsTable, recordMigration, getRemoteMigrations } from '@/lib/migrations.js';
15
+ import logger from '@/lib/logger';
15
16
 
16
17
  /**
17
18
  * Applies the pull migrations by:
@@ -52,33 +53,33 @@ async function applyPullMigrations(projectDir, accountId, apiToken, databaseName
52
53
  await Promise.all(stagedFilesList.map(file => unlink(path.join(stagingDir, file))));
53
54
 
54
55
  // --- STEP 1: Fetch remote migration metadata ---
55
- // console.log('Fetching remote migration metadata...');
56
+ logger.debug('Fetching remote migration metadata...');
56
57
  const remoteMigrations = await getRemoteMigrations(projectDir, databaseName, env);
57
- // console.log('Remote migration metadata:', remoteMigrations);
58
+ logger.debug('Remote migration metadata:', remoteMigrations);
58
59
 
59
60
  // --- STEP 2: For each remote migration, fetch its file content ---
60
- // console.log('Fetching migration file content from remote...');
61
+ logger.debug('Fetching migration file content from remote...');
61
62
  const stagedFiles = new Map();
62
63
  for (const migration of remoteMigrations) {
63
64
  try {
64
65
  const migrationFileName = migration.name; // Use the remote migration's name.
65
66
  const content = await getMigrationFileContent(projectDir, databaseName, migrationFileName, env);
66
67
  stagedFiles.set(migrationFileName, content);
67
- // console.log(`Staged migration file: ${migrationFileName}`);
68
+ logger.debug(`Staged migration file: ${migrationFileName}`);
68
69
  await writeFile(path.join(stagingDir, migrationFileName), content, 'utf8');
69
70
  } catch (fileError) {
70
- console.error(`Error fetching content for migration ${migration.name}:`, fileError);
71
+ logger.error(`Error fetching content for migration ${migration.name}:`, fileError);
71
72
  throw new Error(`Failed to fetch migration file content for ${migration.name}`);
72
73
  }
73
74
  }
74
75
 
75
76
  // --- STEP 3: Fetch local schema for destructive update ---
76
- // console.log('Fetching local schema for destructive update...');
77
+ logger.debug('Fetching local schema for destructive update...');
77
78
  const currentSchema = await getCurrentSchema(projectDir, databaseName, env, false);
78
79
  const tableSchema = currentSchema.filter(obj =>
79
80
  obj.type === 'table' && obj.name && obj.name.toLowerCase() !== 'if'
80
81
  );
81
- // console.log('Local table schema:', tableSchema);
82
+ logger.debug('Local table schema:', tableSchema);
82
83
  const dropOrder = tableSchema.length > 0
83
84
  ? analyzeDependencies(
84
85
  tableSchema.map(obj => ({
@@ -88,18 +89,18 @@ async function applyPullMigrations(projectDir, accountId, apiToken, databaseName
88
89
  }))
89
90
  )
90
91
  : [];
91
- // console.log('Computed drop order:', dropOrder);
92
+ logger.debug('Computed drop order:', dropOrder);
92
93
 
93
94
  // --- STEP 4: Destructive update (all-or-nothing) ---
94
- // console.log('Beginning destructive update...');
95
+ logger.debug('Beginning destructive update...');
95
96
  try {
96
- // console.log('Disabling foreign keys...');
97
+ logger.debug('Disabling foreign keys...');
97
98
  await executeSQL(projectDir, databaseName, 'PRAGMA foreign_keys=OFF;', env, false);
98
99
 
99
100
  // Drop triggers.
100
101
  const triggers = currentSchema.filter(obj => obj.type === 'trigger');
101
102
  for (const trigger of triggers) {
102
- // console.log(`Dropping trigger: ${trigger.name}`);
103
+ logger.debug(`Dropping trigger: ${trigger.name}`);
103
104
  await executeSQL(
104
105
  projectDir,
105
106
  databaseName,
@@ -111,7 +112,7 @@ async function applyPullMigrations(projectDir, accountId, apiToken, databaseName
111
112
  // Drop indexes.
112
113
  const indexes = currentSchema.filter(obj => obj.type === 'index');
113
114
  for (const index of indexes) {
114
- // console.log(`Dropping index: ${index.name}`);
115
+ logger.debug(`Dropping index: ${index.name}`);
115
116
  await executeSQL(
116
117
  projectDir,
117
118
  databaseName,
@@ -122,7 +123,7 @@ async function applyPullMigrations(projectDir, accountId, apiToken, databaseName
122
123
  }
123
124
  // Drop tables in reverse dependency order.
124
125
  for (const tableName of dropOrder.reverse()) {
125
- // console.log(`Dropping table: ${tableName}`);
126
+ logger.debug(`Dropping table: ${tableName}`);
126
127
  await executeSQL(
127
128
  projectDir,
128
129
  databaseName,
@@ -131,7 +132,7 @@ async function applyPullMigrations(projectDir, accountId, apiToken, databaseName
131
132
  false
132
133
  );
133
134
  }
134
- // console.log('Re-enabling foreign keys...');
135
+ logger.debug('Re-enabling foreign keys...');
135
136
  await executeSQL(projectDir, databaseName, 'PRAGMA foreign_keys=ON;', env, false);
136
137
  } catch (dropError) {
137
138
  throw new Error(`Error dropping existing objects: ${dropError.message}`);
@@ -139,7 +140,7 @@ async function applyPullMigrations(projectDir, accountId, apiToken, databaseName
139
140
 
140
141
  // --- STEP 5: Reset the local migrations table ---
141
142
  try {
142
- // console.log('Resetting local migrations table...');
143
+ logger.debug('Resetting local migrations table...');
143
144
  await initMigrationsTable(projectDir, databaseName, 'local', true);
144
145
  } catch (initError) {
145
146
  throw new Error(`Error resetting migrations table: ${initError.message}`);
@@ -152,7 +153,7 @@ async function applyPullMigrations(projectDir, accountId, apiToken, databaseName
152
153
  try {
153
154
  for (const [filename, content] of stagedFiles.entries()) {
154
155
  currentFilename = filename;
155
- // console.log(`Applying migration: ${filename}`);
156
+ logger.debug(`Applying migration: ${filename}`);
156
157
  // Write the migration file to the main migrations folder.
157
158
  await writeFile(path.join(migrationsDir, filename), content, 'utf8');
158
159
 
@@ -162,7 +163,7 @@ async function applyPullMigrations(projectDir, accountId, apiToken, databaseName
162
163
  .update(content)
163
164
  .digest('hex');
164
165
 
165
- // console.log(`Recording migration: ${filename} with checksum ${checksum}`);
166
+ logger.debug(`Recording migration: ${filename} with checksum ${checksum}`);
166
167
  await recordMigration(
167
168
  projectDir,
168
169
  databaseName,
@@ -181,26 +182,26 @@ async function applyPullMigrations(projectDir, accountId, apiToken, databaseName
181
182
  }
182
183
 
183
184
  // --- STEP 7: Replace main migrations folder with staged files ---
184
- // console.log('Replacing main migrations folder with staged files...');
185
+ logger.debug('Replacing main migrations folder with staged files...');
185
186
  const mainFiles = await readdir(migrationsDir).catch(() => []);
186
187
  if (mainFiles.length) {
187
188
  for (const file of mainFiles) {
188
- // console.log(`Deleting local migration file: ${file}`);
189
+ logger.debug(`Deleting local migration file: ${file}`);
189
190
  await unlink(path.join(migrationsDir, file));
190
191
  }
191
192
  }
192
193
  for (const [filename, content] of stagedFiles.entries()) {
193
- // console.log(`Copying staged file ${filename} to main migrations folder`);
194
+ logger.debug(`Copying staged file ${filename} to main migrations folder`);
194
195
  await writeFile(path.join(migrationsDir, filename), content, 'utf8');
195
196
  }
196
197
 
197
- // console.log('Pull migration applied successfully.');
198
+ logger.debug('Pull migration applied successfully.');
198
199
  return {
199
200
  appliedMigrations: Array.from(stagedFiles.keys()),
200
201
  details: appliedPreview
201
202
  };
202
203
  } catch (error) {
203
- console.error('Pull migration error:', error);
204
+ logger.error('Pull migration error:', error);
204
205
  throw error;
205
206
  }
206
207
  }
@@ -251,7 +252,7 @@ export async function POST(req) {
251
252
  details: result
252
253
  });
253
254
  } catch (error) {
254
- console.error('Pull remote confirm error:', error);
255
+ logger.error('Pull remote confirm error:', error);
255
256
  return NextResponse.json({
256
257
  success: false,
257
258
  error: {
@@ -2,9 +2,10 @@
2
2
 
3
3
  import { NextResponse } from 'next/server';
4
4
  import { findWranglerConfig } from '@/lib/find-wrangler-config.js';
5
- import { ErrorTypes,} from '@/lib/sql-operations';
5
+ import { ErrorTypes } from '@/lib/sql-operations';
6
6
  import { getCurrentSchema, analyzeDependencies } from '@/lib/schema-operations';
7
7
  import { getRemoteMigrations } from '@/lib/migrations.js';
8
+ import logger from '@/lib/logger';
8
9
 
9
10
  /**
10
11
  * Generates a preview for pulling remote migrations.
@@ -64,7 +65,7 @@ async function generatePullPreview(projectDir, accountId, apiToken, databaseName
64
65
 
65
66
  return preview;
66
67
  } catch (error) {
67
- console.error('Pull preview generation error:', error);
68
+ logger.error('Pull preview generation error:', error);
68
69
  throw error;
69
70
  }
70
71
  }
@@ -123,7 +124,7 @@ export async function POST(req) {
123
124
  details: preview
124
125
  });
125
126
  } catch (error) {
126
- console.error('Pull remote preview error:', error);
127
+ logger.error('Pull remote preview error:', error);
127
128
  return NextResponse.json({
128
129
  success: false,
129
130
  error: {
@@ -6,6 +6,7 @@ import path from 'path';
6
6
  import { findWranglerConfig } from '@/lib/find-wrangler-config.js';
7
7
  import { executeSQL, ErrorTypes } from '@/lib/sql-operations';
8
8
  import { validateMigrationFile } from '@/lib/migrations.js';
9
+ import logger from '@/lib/logger';
9
10
 
10
11
  /**
11
12
  * Pushes migration preview to remote.
@@ -100,7 +101,7 @@ async function pushToRemote(projectDir, accountId, apiToken, databaseName, previ
100
101
  }
101
102
  }
102
103
 
103
- // console.log("Push Preview Result (server):", results);
104
+ logger.debug("Push Preview Result (server):", results);
104
105
  return { success: true, preview: true, applied: results.applied.length, skipped: results.skipped.length, details: results };
105
106
  } catch (error) {
106
107
  throw {
@@ -157,7 +158,7 @@ export async function POST(req) {
157
158
  details: result
158
159
  });
159
160
  } catch (error) {
160
- console.error('Push to remote preview error:', error);
161
+ logger.error('Push to remote preview error:', error);
161
162
  return NextResponse.json({
162
163
  success: false,
163
164
  error: {
@@ -11,15 +11,19 @@ import {
11
11
  import { handleSchemaChange } from '@/lib/migrations';
12
12
  import { isSchemaChange } from '@/lib/sql-operations';
13
13
  import { parseSQLError } from '@/lib/parse-sql-error';
14
+ import logger from '@/lib/logger';
14
15
 
15
16
  /**
16
17
  * API POST handler to execute SQL queries against the configured D1 database.
17
18
  */
18
19
  export async function POST(req) {
20
+ logger.logRequest('POST', '/api/query');
21
+
19
22
  try {
20
23
  // Ensure that the project directory is set.
21
24
  const projectDir = process.env.D1_PROJECT_DIR;
22
25
  if (!projectDir) {
26
+ logger.error('Project directory not configured');
23
27
  return NextResponse.json(
24
28
  { error: 'Project directory not configured' },
25
29
  { status: 500 }
@@ -28,7 +32,10 @@ export async function POST(req) {
28
32
 
29
33
  // Extract the SQL query from the request body.
30
34
  const { query } = await req.json();
31
- // console.log('Received query:', query);
35
+
36
+ if (logger.shouldLog('debug')) {
37
+ logger.debug('Received query:', query);
38
+ }
32
39
 
33
40
  if (!query?.trim()) {
34
41
  return NextResponse.json(
@@ -40,6 +47,7 @@ export async function POST(req) {
40
47
  // Retrieve the Wrangler configuration using the centralized helper.
41
48
  const wranglerResult = await findWranglerConfig(projectDir);
42
49
  if (!wranglerResult || !wranglerResult.config) {
50
+ logger.error('No Wrangler configuration file found');
43
51
  return NextResponse.json(
44
52
  { success: false, error: { message: 'No Wrangler configuration file found' } },
45
53
  { status: 404 }
@@ -94,7 +102,7 @@ export async function POST(req) {
94
102
  // Process schema changes and capture migrations.
95
103
  // The handleSchemaChange helper is responsible for executing the DDL statements.
96
104
  const migrations = await handleSchemaChange(projectDir, databaseName, statements);
97
- console.log('Schema changes processed:', migrations);
105
+ // console.log('Schema changes processed:', migrations);
98
106
 
99
107
  result = {
100
108
  success: true,
@@ -127,7 +135,7 @@ export async function POST(req) {
127
135
  return NextResponse.json(result);
128
136
 
129
137
  } catch (error) {
130
- console.error('Query execution error:', error);
138
+ logger.logError(error, 'query-route');
131
139
  const parsedError = parseSQLError(error);
132
140
 
133
141
  const statusCode =
@@ -5,6 +5,7 @@ import { promisify } from 'util';
5
5
  import { exec } from 'child_process';
6
6
  import { findWranglerConfig } from '@/lib/find-wrangler-config.js';
7
7
  import { dbQueue } from '@/lib/database-queue';
8
+ import logger from '@/lib/logger';
8
9
 
9
10
  const execAsync = promisify(exec);
10
11
 
@@ -35,7 +36,7 @@ async function executeD1Query(command, projectDir, databaseName) {
35
36
  const result = JSON.parse(stdout);
36
37
  return result[0]?.results || [];
37
38
  } catch (error) {
38
- console.error('Query execution error:', error);
39
+ logger.error('Query execution error:', error);
39
40
  throw error;
40
41
  } finally {
41
42
  isProcessing = false;
@@ -145,7 +146,7 @@ export async function GET(request, context) {
145
146
  });
146
147
 
147
148
  } catch (error) {
148
- console.error('Error handling request:', error);
149
+ logger.error('Error handling request:', error);
149
150
  return NextResponse.json({
150
151
  success: false,
151
152
  error: {
@@ -4,6 +4,7 @@ import { NextResponse } from 'next/server';
4
4
  import { promisify } from 'util';
5
5
  import { exec } from 'child_process';
6
6
  import { findWranglerConfig } from '@/lib/find-wrangler-config.js';
7
+ import logger from '@/lib/logger';
7
8
 
8
9
  const execAsync = promisify(exec);
9
10
 
@@ -75,11 +76,13 @@ async function retryWithTimeout(fn, retries = 3, timeout = 1000) {
75
76
  * It uses the Wrangler configuration (via findWranglerConfig) to determine the D1 database.
76
77
  */
77
78
  export async function GET() {
79
+ logger.logRequest('GET', '/api/tables');
80
+
78
81
  try {
79
82
  const projectDir = process.env.D1_PROJECT_DIR;
80
83
 
81
84
  if (!projectDir) {
82
- console.error('D1_PROJECT_DIR environment variable is not set');
85
+ logger.error('D1_PROJECT_DIR environment variable is not set');
83
86
  return NextResponse.json(
84
87
  { error: 'Project directory not configured' },
85
88
  { status: 500 }
@@ -89,6 +92,7 @@ export async function GET() {
89
92
  // Use the centralized helper to get Wrangler configuration
90
93
  const wranglerResult = await findWranglerConfig(projectDir);
91
94
  if (!wranglerResult || !wranglerResult.config) {
95
+ logger.error('No Wrangler configuration file found');
92
96
  return NextResponse.json(
93
97
  { error: 'No Wrangler configuration file found in project directory' },
94
98
  { status: 404 }
@@ -138,7 +142,7 @@ export async function GET() {
138
142
  });
139
143
 
140
144
  } catch (error) {
141
- console.error('Error fetching tables:', error);
145
+ logger.logError(error, 'tables-route');
142
146
 
143
147
  // Enhanced error response for troubleshooting
144
148
  const errorResponse = {
@@ -0,0 +1,11 @@
1
+ // app/api/update/route.js
2
+ import { NextResponse } from 'next/server';
3
+ import updateNotifier from 'update-notifier';
4
+ import pkg from '../../../../package.json' assert { type: 'json' };
5
+
6
+ export async function GET() {
7
+ // Check for updates (you might want to cache this result to avoid too many requests)
8
+ const notifier = updateNotifier({ pkg, updateCheckInterval: 1000 * 60 * 60 * 24 });
9
+ const update = notifier.update;
10
+ return NextResponse.json({ update });
11
+ }
@@ -1,10 +1,12 @@
1
1
  // app/page.js
2
2
  'use client'
3
3
 
4
+ import { useState } from 'react';
4
5
  import { AppSidebar } from '../components/sidebar';
5
6
  import { SQLEditor } from '../components/sql-editor';
6
7
  import { TableView } from '../components/table-view';
7
- import { useState } from 'react';
8
+ import UpdateBanner from '@/components/update-banner';
9
+
8
10
 
9
11
  export default function StudioPage() {
10
12
  const [currentView, setCurrentView] = useState('sql-editor');
@@ -17,12 +19,16 @@ export default function StudioPage() {
17
19
 
18
20
  return (
19
21
  <div className="min-h-screen bg-background">
22
+ {/* notification banner at the top of the page */}
23
+ <UpdateBanner />
24
+ {/* sidebar */}
20
25
  <AppSidebar
21
26
  className="w-64 border-r"
22
27
  currentView={currentView}
23
28
  onNavigate={setCurrentView}
24
29
  onTableSelect={handleTableSelect}
25
30
  />
31
+ {/* main content */}
26
32
  <main className="ml-64 h-screen">
27
33
  {currentView === 'sql-editor' && <SQLEditor />}
28
34
  {currentView === 'table-view' && selectedTable && (
package/dist/d1-studio.js CHANGED
@@ -12,8 +12,13 @@ import { createServer } from 'http';
12
12
  import next from 'next';
13
13
  import { findWranglerConfig } from '../src/lib/find-wrangler-config.js';
14
14
  import logger from '../src/lib/logger.js';
15
+ import updateNotifier from 'update-notifier';
16
+ import pkg from '../package.json' assert { type: 'json' };
15
17
 
16
- // Compute the absolute path of this file and its directory.
18
+ const notifier = updateNotifier({ pkg, updateCheckInterval: 1000 * 60 * 60 * 24 });
19
+ notifier.notify({ defer: false });
20
+
21
+ // Compute the absolute path of this file and its directory
17
22
  const __filename = fileURLToPath(import.meta.url);
18
23
  const __dirname = dirname(__filename);
19
24
 
@@ -26,6 +31,70 @@ class LicenseError extends Error {
26
31
  }
27
32
  }
28
33
 
34
+ class StudioLicenseManager {
35
+ constructor() {
36
+ this.licenseDir = join(homedir(), '.d1studio');
37
+ this.licensePath = join(this.licenseDir, 'license.json');
38
+ }
39
+
40
+ async checkLicenseFile() {
41
+ try {
42
+ const licenseData = JSON.parse(await readFile(this.licensePath, 'utf8'));
43
+
44
+ // If no end_date or subscription_status, the license is valid (perpetual license)
45
+ if (!licenseData.end_date || !licenseData.subscription_status) {
46
+ return {
47
+ licenseData,
48
+ isValid: true,
49
+ isExpiringSoon: false
50
+ };
51
+ }
52
+
53
+ // Check subscription status first
54
+ if (licenseData.subscription_status !== 'active') {
55
+ return {
56
+ licenseData,
57
+ isValid: false,
58
+ isExpiringSoon: false
59
+ };
60
+ }
61
+
62
+ const endDate = new Date(licenseData.end_date);
63
+ const now = new Date();
64
+
65
+ // Check if subscription has ended
66
+ if (endDate < now) {
67
+ return {
68
+ licenseData,
69
+ isValid: false,
70
+ isExpiringSoon: false
71
+ };
72
+ }
73
+
74
+ // Check if subscription ends in next 7 days
75
+ const sevenDaysFromNow = new Date(now.getTime() + (7 * 24 * 60 * 60 * 1000));
76
+ const isExpiringSoon = endDate <= sevenDaysFromNow;
77
+
78
+ return {
79
+ licenseData,
80
+ isValid: true,
81
+ isExpiringSoon
82
+ };
83
+ } catch (error) {
84
+ return null;
85
+ }
86
+ }
87
+
88
+ async saveLicenseFile(licenseData) {
89
+ try {
90
+ await mkdir(this.licenseDir, { recursive: true });
91
+ await writeFile(this.licensePath, JSON.stringify(licenseData, null, 2));
92
+ } catch (error) {
93
+ throw new LicenseError('Failed to save license information', 'SAVE_ERROR');
94
+ }
95
+ }
96
+ }
97
+
29
98
  async function generateHardwareId() {
30
99
  const osInfo = {
31
100
  platform: process.platform,
@@ -60,13 +129,6 @@ async function validateLicenseRemote(licenseKey, email, hardwareId) {
60
129
  );
61
130
  }
62
131
 
63
- if (result.end_date && new Date(result.end_date) < new Date()) {
64
- throw new LicenseError(
65
- 'Your subscription has expired. Please renew your license to continue using D1 Studio.',
66
- 'SUBSCRIPTION_EXPIRED'
67
- );
68
- }
69
-
70
132
  return result;
71
133
  } catch (error) {
72
134
  if (error instanceof LicenseError) {
@@ -87,35 +149,6 @@ async function validateLicenseRemote(licenseKey, email, hardwareId) {
87
149
  }
88
150
  }
89
151
 
90
- async function checkLicenseFile() {
91
- const licenseDir = join(homedir(), '.d1studio');
92
- const licensePath = join(licenseDir, 'license.json');
93
-
94
- try {
95
- const licenseData = JSON.parse(await readFile(licensePath, 'utf8'));
96
- const hardwareId = await generateHardwareId();
97
-
98
- if (licenseData.hardwareId !== hardwareId) {
99
- return null;
100
- }
101
- return licenseData;
102
- } catch (error) {
103
- return null;
104
- }
105
- }
106
-
107
- async function saveLicenseFile(licenseData) {
108
- const licenseDir = join(homedir(), '.d1studio');
109
- const licensePath = join(licenseDir, 'license.json');
110
-
111
- try {
112
- await mkdir(licenseDir, { recursive: true });
113
- await writeFile(licensePath, JSON.stringify(licenseData, null, 2));
114
- } catch (error) {
115
- throw new LicenseError('Failed to save license information', 'SAVE_ERROR');
116
- }
117
- }
118
-
119
152
  async function startServer(port, projectDirectory) {
120
153
  return new Promise((resolvePromise, rejectPromise) => {
121
154
  // Determine Next.js app directory based on environment:
@@ -170,20 +203,14 @@ async function startStudio() {
170
203
  try {
171
204
  logger.info(chalk.green('Initializing D1 Studio...'));
172
205
 
173
- // Validate license
174
- let licenseData = await checkLicenseFile();
175
- logger.debug(chalk.blue(`License data found: ${JSON.stringify(licenseData)}`));
176
-
177
- const TWELVE_HOURS_MS = 43200000; // 12 hours
178
- let shouldRevalidate = true;
179
- if (licenseData) {
180
- const lastCheck = new Date(licenseData.lastValidatedAt || licenseData.activatedAt);
181
- if (Date.now() - lastCheck.getTime() < TWELVE_HOURS_MS) {
182
- shouldRevalidate = false;
183
- }
184
- }
206
+ const licenseManager = new StudioLicenseManager();
185
207
 
186
- if (!licenseData || shouldRevalidate) {
208
+ // Check existing license
209
+ const licenseCheck = await licenseManager.checkLicenseFile();
210
+
211
+ // Handle license validation
212
+ if (!licenseCheck || !licenseCheck.isValid) {
213
+ // Prompt for license information if needed
187
214
  const { default: inquirer } = await import('inquirer');
188
215
  logger.info(chalk.yellow('\nPlease enter your D1 Studio license information:'));
189
216
 
@@ -205,9 +232,8 @@ async function startStudio() {
205
232
  },
206
233
  ]);
207
234
 
208
- logger.debug(chalk.blue(`Entered license information: ${JSON.stringify(answers)}`));
209
-
210
235
  const hardwareId = await generateHardwareId();
236
+
211
237
  try {
212
238
  const validation = await validateLicenseRemote(
213
239
  answers.licenseKey,
@@ -215,19 +241,29 @@ async function startStudio() {
215
241
  hardwareId
216
242
  );
217
243
 
218
- licenseData = {
244
+ // Save the complete validation response
245
+ await licenseManager.saveLicenseFile({
219
246
  licenseKey: answers.licenseKey,
220
247
  email: answers.email,
221
- activatedAt: new Date().toISOString(),
222
- endDate: validation.end_date || null
223
- };
248
+ hardwareId,
249
+ end_date: validation.end_date,
250
+ subscription_status: validation.subscription_status,
251
+ type: validation.licenseType,
252
+ activatedAt: new Date().toISOString()
253
+ });
224
254
 
225
- await saveLicenseFile(licenseData);
226
255
  logger.info(chalk.green('\nLicense activated successfully!'));
227
256
  } catch (error) {
228
257
  logger.error(chalk.red(`\nLicense validation failed: ${error.message}`));
229
258
  process.exit(1);
230
259
  }
260
+ } else if (licenseCheck.isExpiringSoon) {
261
+ // Show warning about expiring subscription
262
+ const endDate = new Date(licenseCheck.licenseData.end_date).toLocaleDateString();
263
+ logger.warn(chalk.yellow(`
264
+ ⚠️ Your subscription will expire on ${endDate}
265
+ Please renew your subscription at https://d1-studio.com to continue using D1 Studio.
266
+ `));
231
267
  }
232
268
 
233
269
  // Get wrangler configuration
@@ -317,4 +353,4 @@ process.on('SIGTERM', () => {
317
353
  startStudio().catch((error) => {
318
354
  logger.error(chalk.red(`\nFailed to start D1 Studio: ${error}`));
319
355
  process.exit(1);
320
- });
356
+ });
@@ -1,33 +1,37 @@
1
1
  // utils/database-queue.js
2
+
3
+ import logger from '@/lib/logger.js';
4
+
2
5
  class DatabaseQueue {
3
- constructor() {
4
- this.queue = [];
5
- this.isProcessing = false;
6
- }
6
+ constructor() {
7
+ this.queue = [];
8
+ this.isProcessing = false;
9
+ }
10
+
11
+ async add(operation) {
12
+ return new Promise((resolve, reject) => {
13
+ this.queue.push({ operation, resolve, reject });
14
+ this.processNext();
15
+ });
16
+ }
17
+
18
+ async processNext() {
19
+ if (this.isProcessing || this.queue.length === 0) return;
20
+
21
+ this.isProcessing = true;
22
+ const { operation, resolve, reject } = this.queue.shift();
23
+
24
+ try {
25
+ const result = await operation();
26
+ resolve(result);
27
+ } catch (error) {
28
+ logger.error('Error processing database queue operation:', error);
29
+ reject(error);
30
+ } finally {
31
+ this.isProcessing = false;
32
+ this.processNext();
33
+ }
34
+ }
35
+ }
7
36
 
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();
37
+ export const dbQueue = new DatabaseQueue();