mirror your GitHub repos to tangled.org automatically
1

Configure Feed

Select the types of activity you want to include in your feed.

feat: add db-backed job queue with cron-driven worker

+484 -51
+14
nuxt.config.ts
··· 14 14 githubAppId: '', 15 15 githubAppPrivateKey: '', 16 16 githubWebhookSecret: '', 17 + cronSecret: '', 18 + workerBudgetMs: '', 17 19 }, 18 20 typescript: { 19 21 nodeTsConfig: { ··· 30 32 }, 31 33 experimental: { 32 34 typedPages: true, 35 + }, 36 + nitro: { 37 + vercel: { 38 + config: { 39 + crons: [ 40 + { 41 + path: '/api/jobs/run', 42 + schedule: '* * * * *' 43 + }, 44 + ] 45 + }, 46 + } 33 47 }, 34 48 compatibilityDate: '2024-04-03', 35 49 // Ensure that any HTML validation errors are treated as build errors
+1
package.json
··· 46 46 "vue-router": "^5.0.6" 47 47 }, 48 48 "devDependencies": { 49 + "@electric-sql/pglite": "^0.4.5", 49 50 "@nuxt/test-utils": "4.0.3", 50 51 "@octokit/webhooks-types": "^7.6.1", 51 52 "@playwright/test": "1.59.1",
+54 -44
pnpm-lock.yaml
··· 17 17 version: 1.1.0 18 18 '@nuxt/fonts': 19 19 specifier: ^0.14.0 20 - version: 0.14.0(db0@0.3.4(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0)))(ioredis@5.10.1)(magicast@0.5.2)(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)) 20 + version: 0.14.0(db0@0.3.4(@electric-sql/pglite@0.4.5)(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.5)(@neondatabase/serverless@1.1.0)))(ioredis@5.10.1)(magicast@0.5.2)(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)) 21 21 '@nuxt/image': 22 22 specifier: ^2.0.0 23 - version: 2.0.0(db0@0.3.4(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0)))(ioredis@5.10.1)(magicast@0.5.2)(srvx@0.11.15) 23 + version: 2.0.0(db0@0.3.4(@electric-sql/pglite@0.4.5)(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.5)(@neondatabase/serverless@1.1.0)))(ioredis@5.10.1)(magicast@0.5.2)(srvx@0.11.15) 24 24 '@nuxt/scripts': 25 25 specifier: ^1.0.6 26 - version: 1.0.6(@unhead/vue@2.1.13(vue@3.5.33(typescript@6.0.3)))(db0@0.3.4(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0)))(ioredis@5.10.1)(magicast@0.5.2)(typescript@6.0.3)(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.33(typescript@6.0.3)) 26 + version: 1.0.6(@unhead/vue@2.1.13(vue@3.5.33(typescript@6.0.3)))(db0@0.3.4(@electric-sql/pglite@0.4.5)(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.5)(@neondatabase/serverless@1.1.0)))(ioredis@5.10.1)(magicast@0.5.2)(typescript@6.0.3)(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.33(typescript@6.0.3)) 27 27 '@nuxtjs/html-validator': 28 28 specifier: ^2.1.0 29 29 version: 2.1.0(@voidzero-dev/vite-plus-test@0.1.20)(magicast@0.5.2) ··· 32 32 version: 6.0.0 33 33 drizzle-orm: 34 34 specifier: ^0.45.2 35 - version: 0.45.2(@neondatabase/serverless@1.1.0) 35 + version: 0.45.2(@electric-sql/pglite@0.4.5)(@neondatabase/serverless@1.1.0) 36 36 nuxt: 37 37 specifier: ^4.4.4 38 - version: 4.4.4(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.6.0)(@vue/compiler-sfc@3.5.33)(cac@6.7.14)(db0@0.3.4(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0)))(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0))(esbuild@0.28.0)(eslint@10.3.0(jiti@2.6.1))(ioredis@5.10.1)(magicast@0.5.2)(optionator@0.9.4)(oxlint@1.61.0(oxlint-tsgolint@0.22.0))(rolldown@1.0.0-rc.18)(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-rc.18)(rollup@4.60.2))(rollup@4.60.2)(srvx@0.11.15)(terser@5.46.2)(tsx@4.21.0)(typescript@6.0.3)(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(vue-tsc@3.2.7(typescript@6.0.3))(yaml@2.8.4) 38 + version: 4.4.4(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@electric-sql/pglite@0.4.5)(@parcel/watcher@2.5.6)(@types/node@25.6.0)(@vue/compiler-sfc@3.5.33)(cac@6.7.14)(db0@0.3.4(@electric-sql/pglite@0.4.5)(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.5)(@neondatabase/serverless@1.1.0)))(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.5)(@neondatabase/serverless@1.1.0))(esbuild@0.28.0)(eslint@10.3.0(jiti@2.6.1))(ioredis@5.10.1)(magicast@0.5.2)(optionator@0.9.4)(oxlint@1.61.0(oxlint-tsgolint@0.22.0))(rolldown@1.0.0-rc.18)(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-rc.18)(rollup@4.60.2))(rollup@4.60.2)(srvx@0.11.15)(terser@5.46.2)(tsx@4.21.0)(typescript@6.0.3)(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(vue-tsc@3.2.7(typescript@6.0.3))(yaml@2.8.4) 39 39 nuxt-og-image: 40 40 specifier: ^6.4.11 41 - version: 6.4.11(ea4a24142c8db751653c3d93ffc3a1c4) 41 + version: 6.4.11(73738ea48627a6be1d31778aaae04f8e) 42 42 rolldown: 43 43 specifier: ^1.0.0-rc.18 44 44 version: 1.0.0-rc.18 ··· 52 52 specifier: ^5.0.6 53 53 version: 5.0.6(@vue/compiler-sfc@3.5.33)(vue@3.5.33(typescript@6.0.3)) 54 54 devDependencies: 55 + '@electric-sql/pglite': 56 + specifier: ^0.4.5 57 + version: 0.4.5 55 58 '@nuxt/test-utils': 56 59 specifier: 4.0.3 57 60 version: 4.0.3(@playwright/test@1.59.1)(@voidzero-dev/vite-plus-test@0.1.20)(@vue/test-utils@2.4.10(@vue/compiler-dom@3.5.33)(@vue/server-renderer@3.5.33(vue@3.5.33(typescript@6.0.3)))(vue@3.5.33(typescript@6.0.3)))(crossws@0.4.5(srvx@0.11.15))(happy-dom@20.9.0)(magicast@0.5.2)(playwright-core@1.59.1)(typescript@6.0.3)(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)) ··· 274 277 275 278 '@dxup/unimport@0.1.2': 276 279 resolution: {integrity: sha512-/B8YJGPzaYq1NbsQmwgP8EZqg40NpTw4ZB3suuI0TplbxKHeK94jeaawLmVhCv+YwUnOpiWEz9U6SeThku/8JQ==} 280 + 281 + '@electric-sql/pglite@0.4.5': 282 + resolution: {integrity: sha512-aGG2zGEyZzGWKy8P+9ZoNUV0jxt1+hgbeTf+bVAYyxVZZLXg3/9aFlfLxb08AYZVAfAkQlQIysmWjhc5hwDG8g==} 277 283 278 284 '@emnapi/core@1.10.0': 279 285 resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} ··· 5870 5876 5871 5877 '@dxup/unimport@0.1.2': {} 5872 5878 5879 + '@electric-sql/pglite@0.4.5': {} 5880 + 5873 5881 '@emnapi/core@1.10.0': 5874 5882 dependencies: 5875 5883 '@emnapi/wasi-threads': 1.2.1 ··· 6543 6551 - utf-8-validate 6544 6552 - vue 6545 6553 6546 - '@nuxt/fonts@0.14.0(db0@0.3.4(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0)))(ioredis@5.10.1)(magicast@0.5.2)(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))': 6554 + '@nuxt/fonts@0.14.0(db0@0.3.4(@electric-sql/pglite@0.4.5)(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.5)(@neondatabase/serverless@1.1.0)))(ioredis@5.10.1)(magicast@0.5.2)(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))': 6547 6555 dependencies: 6548 6556 '@nuxt/devtools-kit': 3.2.4(magicast@0.5.2)(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)) 6549 6557 '@nuxt/kit': 4.4.4(magicast@0.5.2) 6550 6558 consola: 3.4.2 6551 6559 defu: 6.1.7 6552 - fontless: 0.2.1(db0@0.3.4(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0)))(ioredis@5.10.1)(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)) 6560 + fontless: 0.2.1(db0@0.3.4(@electric-sql/pglite@0.4.5)(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.5)(@neondatabase/serverless@1.1.0)))(ioredis@5.10.1)(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)) 6553 6561 h3: 1.15.11 6554 6562 magic-regexp: 0.10.0 6555 6563 ofetch: 1.5.1 ··· 6559 6567 ufo: 1.6.4 6560 6568 unifont: 0.7.4 6561 6569 unplugin: 3.0.0 6562 - unstorage: 1.17.5(db0@0.3.4(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0)))(ioredis@5.10.1) 6570 + unstorage: 1.17.5(db0@0.3.4(@electric-sql/pglite@0.4.5)(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.5)(@neondatabase/serverless@1.1.0)))(ioredis@5.10.1) 6563 6571 transitivePeerDependencies: 6564 6572 - '@azure/app-configuration' 6565 6573 - '@azure/cosmos' ··· 6583 6591 - uploadthing 6584 6592 - vite 6585 6593 6586 - '@nuxt/image@2.0.0(db0@0.3.4(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0)))(ioredis@5.10.1)(magicast@0.5.2)(srvx@0.11.15)': 6594 + '@nuxt/image@2.0.0(db0@0.3.4(@electric-sql/pglite@0.4.5)(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.5)(@neondatabase/serverless@1.1.0)))(ioredis@5.10.1)(magicast@0.5.2)(srvx@0.11.15)': 6587 6595 dependencies: 6588 6596 '@nuxt/kit': 4.4.4(magicast@0.5.2) 6589 6597 consola: 3.4.2 ··· 6596 6604 std-env: 3.10.0 6597 6605 ufo: 1.6.4 6598 6606 optionalDependencies: 6599 - ipx: 3.1.1(db0@0.3.4(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0)))(ioredis@5.10.1)(srvx@0.11.15) 6607 + ipx: 3.1.1(db0@0.3.4(@electric-sql/pglite@0.4.5)(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.5)(@neondatabase/serverless@1.1.0)))(ioredis@5.10.1)(srvx@0.11.15) 6600 6608 transitivePeerDependencies: 6601 6609 - '@azure/app-configuration' 6602 6610 - '@azure/cosmos' ··· 6671 6679 transitivePeerDependencies: 6672 6680 - magicast 6673 6681 6674 - '@nuxt/nitro-server@4.4.4(@babel/core@7.29.0)(db0@0.3.4(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0)))(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0))(ioredis@5.10.1)(magicast@0.5.2)(nuxt@4.4.4(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.6.0)(@vue/compiler-sfc@3.5.33)(cac@6.7.14)(db0@0.3.4(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0)))(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0))(esbuild@0.28.0)(eslint@10.3.0(jiti@2.6.1))(ioredis@5.10.1)(magicast@0.5.2)(optionator@0.9.4)(oxlint@1.61.0(oxlint-tsgolint@0.22.0))(rolldown@1.0.0-rc.18)(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-rc.18)(rollup@4.60.2))(rollup@4.60.2)(srvx@0.11.15)(terser@5.46.2)(tsx@4.21.0)(typescript@6.0.3)(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(vue-tsc@3.2.7(typescript@6.0.3))(yaml@2.8.4))(oxc-parser@0.128.0)(rolldown@1.0.0-rc.18)(srvx@0.11.15)(typescript@6.0.3)': 6682 + '@nuxt/nitro-server@4.4.4(c703237518f96a01201357fd255c3b67)': 6675 6683 dependencies: 6676 6684 '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) 6677 6685 '@nuxt/devalue': 2.0.2 ··· 6689 6697 impound: 1.1.5 6690 6698 klona: 2.0.6 6691 6699 mocked-exports: 0.1.1 6692 - nitropack: 2.13.4(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0))(oxc-parser@0.128.0)(rolldown@1.0.0-rc.18)(srvx@0.11.15) 6693 - nuxt: 4.4.4(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.6.0)(@vue/compiler-sfc@3.5.33)(cac@6.7.14)(db0@0.3.4(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0)))(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0))(esbuild@0.28.0)(eslint@10.3.0(jiti@2.6.1))(ioredis@5.10.1)(magicast@0.5.2)(optionator@0.9.4)(oxlint@1.61.0(oxlint-tsgolint@0.22.0))(rolldown@1.0.0-rc.18)(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-rc.18)(rollup@4.60.2))(rollup@4.60.2)(srvx@0.11.15)(terser@5.46.2)(tsx@4.21.0)(typescript@6.0.3)(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(vue-tsc@3.2.7(typescript@6.0.3))(yaml@2.8.4) 6700 + nitropack: 2.13.4(@electric-sql/pglite@0.4.5)(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.5)(@neondatabase/serverless@1.1.0))(oxc-parser@0.128.0)(rolldown@1.0.0-rc.18)(srvx@0.11.15) 6701 + nuxt: 4.4.4(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@electric-sql/pglite@0.4.5)(@parcel/watcher@2.5.6)(@types/node@25.6.0)(@vue/compiler-sfc@3.5.33)(cac@6.7.14)(db0@0.3.4(@electric-sql/pglite@0.4.5)(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.5)(@neondatabase/serverless@1.1.0)))(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.5)(@neondatabase/serverless@1.1.0))(esbuild@0.28.0)(eslint@10.3.0(jiti@2.6.1))(ioredis@5.10.1)(magicast@0.5.2)(optionator@0.9.4)(oxlint@1.61.0(oxlint-tsgolint@0.22.0))(rolldown@1.0.0-rc.18)(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-rc.18)(rollup@4.60.2))(rollup@4.60.2)(srvx@0.11.15)(terser@5.46.2)(tsx@4.21.0)(typescript@6.0.3)(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(vue-tsc@3.2.7(typescript@6.0.3))(yaml@2.8.4) 6694 6702 nypm: 0.6.6 6695 6703 ohash: 2.0.11 6696 6704 pathe: 2.0.3 ··· 6698 6706 std-env: 4.1.0 6699 6707 ufo: 1.6.4 6700 6708 unctx: 2.5.0 6701 - unstorage: 1.17.5(db0@0.3.4(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0)))(ioredis@5.10.1) 6709 + unstorage: 1.17.5(db0@0.3.4(@electric-sql/pglite@0.4.5)(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.5)(@neondatabase/serverless@1.1.0)))(ioredis@5.10.1) 6702 6710 vue: 3.5.33(typescript@6.0.3) 6703 6711 vue-bundle-renderer: 2.2.0 6704 6712 vue-devtools-stub: 0.1.0 ··· 6749 6757 pkg-types: 2.3.1 6750 6758 std-env: 4.1.0 6751 6759 6752 - '@nuxt/scripts@1.0.6(@unhead/vue@2.1.13(vue@3.5.33(typescript@6.0.3)))(db0@0.3.4(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0)))(ioredis@5.10.1)(magicast@0.5.2)(typescript@6.0.3)(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.33(typescript@6.0.3))': 6760 + '@nuxt/scripts@1.0.6(@unhead/vue@2.1.13(vue@3.5.33(typescript@6.0.3)))(db0@0.3.4(@electric-sql/pglite@0.4.5)(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.5)(@neondatabase/serverless@1.1.0)))(ioredis@5.10.1)(magicast@0.5.2)(typescript@6.0.3)(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.33(typescript@6.0.3))': 6753 6761 dependencies: 6754 6762 '@nuxt/devtools-kit': 3.2.4(magicast@0.5.2)(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)) 6755 6763 '@nuxt/kit': 4.4.4(magicast@0.5.2) ··· 6771 6779 ufo: 1.6.4 6772 6780 ultrahtml: 1.6.0 6773 6781 unplugin: 3.0.0 6774 - unstorage: 1.17.5(db0@0.3.4(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0)))(ioredis@5.10.1) 6782 + unstorage: 1.17.5(db0@0.3.4(@electric-sql/pglite@0.4.5)(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.5)(@neondatabase/serverless@1.1.0)))(ioredis@5.10.1) 6775 6783 valibot: 1.3.1(typescript@6.0.3) 6776 6784 transitivePeerDependencies: 6777 6785 - '@azure/app-configuration' ··· 6850 6858 - typescript 6851 6859 - vite 6852 6860 6853 - '@nuxt/vite-builder@4.4.4(28264294504fc5bb34607a4fbd2f89d6)': 6861 + '@nuxt/vite-builder@4.4.4(60540df5947dbcc3d30c3e97abb55c90)': 6854 6862 dependencies: 6855 6863 '@nuxt/kit': 4.4.4(magicast@0.5.2) 6856 6864 '@rollup/plugin-replace': 6.0.3(rollup@4.60.2) ··· 6868 6876 magic-string: 0.30.21 6869 6877 mlly: 1.8.2 6870 6878 mocked-exports: 0.1.1 6871 - nuxt: 4.4.4(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.6.0)(@vue/compiler-sfc@3.5.33)(cac@6.7.14)(db0@0.3.4(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0)))(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0))(esbuild@0.28.0)(eslint@10.3.0(jiti@2.6.1))(ioredis@5.10.1)(magicast@0.5.2)(optionator@0.9.4)(oxlint@1.61.0(oxlint-tsgolint@0.22.0))(rolldown@1.0.0-rc.18)(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-rc.18)(rollup@4.60.2))(rollup@4.60.2)(srvx@0.11.15)(terser@5.46.2)(tsx@4.21.0)(typescript@6.0.3)(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(vue-tsc@3.2.7(typescript@6.0.3))(yaml@2.8.4) 6879 + nuxt: 4.4.4(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@electric-sql/pglite@0.4.5)(@parcel/watcher@2.5.6)(@types/node@25.6.0)(@vue/compiler-sfc@3.5.33)(cac@6.7.14)(db0@0.3.4(@electric-sql/pglite@0.4.5)(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.5)(@neondatabase/serverless@1.1.0)))(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.5)(@neondatabase/serverless@1.1.0))(esbuild@0.28.0)(eslint@10.3.0(jiti@2.6.1))(ioredis@5.10.1)(magicast@0.5.2)(optionator@0.9.4)(oxlint@1.61.0(oxlint-tsgolint@0.22.0))(rolldown@1.0.0-rc.18)(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-rc.18)(rollup@4.60.2))(rollup@4.60.2)(srvx@0.11.15)(terser@5.46.2)(tsx@4.21.0)(typescript@6.0.3)(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(vue-tsc@3.2.7(typescript@6.0.3))(yaml@2.8.4) 6872 6880 nypm: 0.6.6 6873 6881 pathe: 2.0.3 6874 6882 pkg-types: 2.3.1 ··· 8403 8411 8404 8412 culori@4.0.2: {} 8405 8413 8406 - db0@0.3.4(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0)): 8414 + db0@0.3.4(@electric-sql/pglite@0.4.5)(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.5)(@neondatabase/serverless@1.1.0)): 8407 8415 optionalDependencies: 8408 - drizzle-orm: 0.45.2(@neondatabase/serverless@1.1.0) 8416 + '@electric-sql/pglite': 0.4.5 8417 + drizzle-orm: 0.45.2(@electric-sql/pglite@0.4.5)(@neondatabase/serverless@1.1.0) 8409 8418 8410 8419 debug@4.4.3: 8411 8420 dependencies: ··· 8469 8478 esbuild: 0.25.12 8470 8479 tsx: 4.21.0 8471 8480 8472 - drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0): 8481 + drizzle-orm@0.45.2(@electric-sql/pglite@0.4.5)(@neondatabase/serverless@1.1.0): 8473 8482 optionalDependencies: 8483 + '@electric-sql/pglite': 0.4.5 8474 8484 '@neondatabase/serverless': 1.1.0 8475 8485 8476 8486 duplexer@0.1.2: {} ··· 8822 8832 dependencies: 8823 8833 tiny-inflate: 1.0.3 8824 8834 8825 - fontless@0.2.1(db0@0.3.4(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0)))(ioredis@5.10.1)(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)): 8835 + fontless@0.2.1(db0@0.3.4(@electric-sql/pglite@0.4.5)(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.5)(@neondatabase/serverless@1.1.0)))(ioredis@5.10.1)(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)): 8826 8836 dependencies: 8827 8837 consola: 3.4.2 8828 8838 css-tree: 3.2.1 ··· 8836 8846 pathe: 2.0.3 8837 8847 ufo: 1.6.4 8838 8848 unifont: 0.7.4 8839 - unstorage: 1.17.5(db0@0.3.4(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0)))(ioredis@5.10.1) 8849 + unstorage: 1.17.5(db0@0.3.4(@electric-sql/pglite@0.4.5)(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.5)(@neondatabase/serverless@1.1.0)))(ioredis@5.10.1) 8840 8850 optionalDependencies: 8841 8851 vite: 7.3.2(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4) 8842 8852 transitivePeerDependencies: ··· 9054 9064 transitivePeerDependencies: 9055 9065 - supports-color 9056 9066 9057 - ipx@3.1.1(db0@0.3.4(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0)))(ioredis@5.10.1)(srvx@0.11.15): 9067 + ipx@3.1.1(db0@0.3.4(@electric-sql/pglite@0.4.5)(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.5)(@neondatabase/serverless@1.1.0)))(ioredis@5.10.1)(srvx@0.11.15): 9058 9068 dependencies: 9059 9069 '@fastify/accept-negotiator': 2.0.1 9060 9070 citty: 0.1.6 ··· 9070 9080 sharp: 0.34.5 9071 9081 svgo: 4.0.1 9072 9082 ufo: 1.6.4 9073 - unstorage: 1.17.5(db0@0.3.4(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0)))(ioredis@5.10.1) 9083 + unstorage: 1.17.5(db0@0.3.4(@electric-sql/pglite@0.4.5)(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.5)(@neondatabase/serverless@1.1.0)))(ioredis@5.10.1) 9074 9084 xss: 1.0.15 9075 9085 transitivePeerDependencies: 9076 9086 - '@azure/app-configuration' ··· 9432 9442 9433 9443 natural-compare@1.4.0: {} 9434 9444 9435 - nitropack@2.13.4(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0))(oxc-parser@0.128.0)(rolldown@1.0.0-rc.18)(srvx@0.11.15): 9445 + nitropack@2.13.4(@electric-sql/pglite@0.4.5)(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.5)(@neondatabase/serverless@1.1.0))(oxc-parser@0.128.0)(rolldown@1.0.0-rc.18)(srvx@0.11.15): 9436 9446 dependencies: 9437 9447 '@cloudflare/kv-asset-handler': 0.4.2 9438 9448 '@rollup/plugin-alias': 6.0.0(rollup@4.60.2) ··· 9453 9463 cookie-es: 2.0.1 9454 9464 croner: 10.0.1 9455 9465 crossws: 0.3.5 9456 - db0: 0.3.4(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0)) 9466 + db0: 0.3.4(@electric-sql/pglite@0.4.5)(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.5)(@neondatabase/serverless@1.1.0)) 9457 9467 defu: 6.1.7 9458 9468 destr: 2.0.5 9459 9469 dot-prop: 10.1.0 ··· 9499 9509 unenv: 2.0.0-rc.24 9500 9510 unimport: 6.2.0(oxc-parser@0.128.0) 9501 9511 unplugin-utils: 0.3.1 9502 - unstorage: 1.17.5(db0@0.3.4(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0)))(ioredis@5.10.1) 9512 + unstorage: 1.17.5(db0@0.3.4(@electric-sql/pglite@0.4.5)(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.5)(@neondatabase/serverless@1.1.0)))(ioredis@5.10.1) 9503 9513 untyped: 2.0.0 9504 9514 unwasm: 0.5.3 9505 9515 youch: 4.1.1 ··· 9576 9586 dependencies: 9577 9587 boolbase: 1.0.0 9578 9588 9579 - nuxt-og-image@6.4.11(ea4a24142c8db751653c3d93ffc3a1c4): 9589 + nuxt-og-image@6.4.11(73738ea48627a6be1d31778aaae04f8e): 9580 9590 dependencies: 9581 9591 '@clack/prompts': 1.3.0 9582 9592 '@nuxt/kit': 4.4.4(magicast@0.5.2) ··· 9592 9602 magic-string: 0.30.21 9593 9603 magicast: 0.5.2 9594 9604 mocked-exports: 0.1.1 9595 - nuxt-site-config: 4.0.8(@nuxt/schema@4.4.4)(magicast@0.5.2)(nuxt@4.4.4(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.6.0)(@vue/compiler-sfc@3.5.33)(cac@6.7.14)(db0@0.3.4(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0)))(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0))(esbuild@0.28.0)(eslint@10.3.0(jiti@2.6.1))(ioredis@5.10.1)(magicast@0.5.2)(optionator@0.9.4)(oxlint@1.61.0(oxlint-tsgolint@0.22.0))(rolldown@1.0.0-rc.18)(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-rc.18)(rollup@4.60.2))(rollup@4.60.2)(srvx@0.11.15)(terser@5.46.2)(tsx@4.21.0)(typescript@6.0.3)(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(vue-tsc@3.2.7(typescript@6.0.3))(yaml@2.8.4))(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.33(typescript@6.0.3)) 9596 - nuxtseo-shared: 5.1.3(524f84012e9d72beb906744b155b32ec) 9605 + nuxt-site-config: 4.0.8(63185d4d07eff04c3532e23607595514) 9606 + nuxtseo-shared: 5.1.3(722a2202af61d2736787650a24b9e3d3) 9597 9607 nypm: 0.6.6 9598 9608 ofetch: 1.5.1 9599 9609 ohash: 2.0.11 ··· 9609 9619 ufo: 1.6.4 9610 9620 ultrahtml: 1.6.0 9611 9621 unplugin: 3.0.0 9612 - unstorage: 1.17.5(db0@0.3.4(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0)))(ioredis@5.10.1) 9622 + unstorage: 1.17.5(db0@0.3.4(@electric-sql/pglite@0.4.5)(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.5)(@neondatabase/serverless@1.1.0)))(ioredis@5.10.1) 9613 9623 optionalDependencies: 9614 9624 '@resvg/resvg-js': 2.6.2 9615 9625 '@resvg/resvg-wasm': 2.6.2 9616 - fontless: 0.2.1(db0@0.3.4(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0)))(ioredis@5.10.1)(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)) 9626 + fontless: 0.2.1(db0@0.3.4(@electric-sql/pglite@0.4.5)(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.5)(@neondatabase/serverless@1.1.0)))(ioredis@5.10.1)(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)) 9617 9627 playwright-core: 1.59.1 9618 9628 sharp: 0.34.5 9619 9629 unifont: 0.7.4 ··· 9635 9645 - magicast 9636 9646 - vue 9637 9647 9638 - nuxt-site-config@4.0.8(@nuxt/schema@4.4.4)(magicast@0.5.2)(nuxt@4.4.4(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.6.0)(@vue/compiler-sfc@3.5.33)(cac@6.7.14)(db0@0.3.4(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0)))(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0))(esbuild@0.28.0)(eslint@10.3.0(jiti@2.6.1))(ioredis@5.10.1)(magicast@0.5.2)(optionator@0.9.4)(oxlint@1.61.0(oxlint-tsgolint@0.22.0))(rolldown@1.0.0-rc.18)(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-rc.18)(rollup@4.60.2))(rollup@4.60.2)(srvx@0.11.15)(terser@5.46.2)(tsx@4.21.0)(typescript@6.0.3)(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(vue-tsc@3.2.7(typescript@6.0.3))(yaml@2.8.4))(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.33(typescript@6.0.3)): 9648 + nuxt-site-config@4.0.8(63185d4d07eff04c3532e23607595514): 9639 9649 dependencies: 9640 9650 '@nuxt/kit': 4.4.4(magicast@0.5.2) 9641 9651 h3: 1.15.11 9642 9652 nuxt-site-config-kit: 4.0.8(magicast@0.5.2)(vue@3.5.33(typescript@6.0.3)) 9643 - nuxtseo-shared: 5.1.3(524f84012e9d72beb906744b155b32ec) 9653 + nuxtseo-shared: 5.1.3(722a2202af61d2736787650a24b9e3d3) 9644 9654 pathe: 2.0.3 9645 9655 pkg-types: 2.3.1 9646 9656 site-config-stack: 4.0.8(vue@3.5.33(typescript@6.0.3)) ··· 9653 9663 - vue 9654 9664 - zod 9655 9665 9656 - nuxt@4.4.4(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.6.0)(@vue/compiler-sfc@3.5.33)(cac@6.7.14)(db0@0.3.4(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0)))(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0))(esbuild@0.28.0)(eslint@10.3.0(jiti@2.6.1))(ioredis@5.10.1)(magicast@0.5.2)(optionator@0.9.4)(oxlint@1.61.0(oxlint-tsgolint@0.22.0))(rolldown@1.0.0-rc.18)(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-rc.18)(rollup@4.60.2))(rollup@4.60.2)(srvx@0.11.15)(terser@5.46.2)(tsx@4.21.0)(typescript@6.0.3)(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(vue-tsc@3.2.7(typescript@6.0.3))(yaml@2.8.4): 9666 + nuxt@4.4.4(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@electric-sql/pglite@0.4.5)(@parcel/watcher@2.5.6)(@types/node@25.6.0)(@vue/compiler-sfc@3.5.33)(cac@6.7.14)(db0@0.3.4(@electric-sql/pglite@0.4.5)(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.5)(@neondatabase/serverless@1.1.0)))(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.5)(@neondatabase/serverless@1.1.0))(esbuild@0.28.0)(eslint@10.3.0(jiti@2.6.1))(ioredis@5.10.1)(magicast@0.5.2)(optionator@0.9.4)(oxlint@1.61.0(oxlint-tsgolint@0.22.0))(rolldown@1.0.0-rc.18)(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-rc.18)(rollup@4.60.2))(rollup@4.60.2)(srvx@0.11.15)(terser@5.46.2)(tsx@4.21.0)(typescript@6.0.3)(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(vue-tsc@3.2.7(typescript@6.0.3))(yaml@2.8.4): 9657 9667 dependencies: 9658 9668 '@dxup/nuxt': 0.4.1(magicast@0.5.2)(typescript@6.0.3) 9659 9669 '@nuxt/cli': 3.35.1(@nuxt/schema@4.4.4)(cac@6.7.14)(magicast@0.5.2) 9660 9670 '@nuxt/devtools': 3.2.4(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.33(typescript@6.0.3)) 9661 9671 '@nuxt/kit': 4.4.4(magicast@0.5.2) 9662 - '@nuxt/nitro-server': 4.4.4(@babel/core@7.29.0)(db0@0.3.4(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0)))(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0))(ioredis@5.10.1)(magicast@0.5.2)(nuxt@4.4.4(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.6.0)(@vue/compiler-sfc@3.5.33)(cac@6.7.14)(db0@0.3.4(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0)))(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0))(esbuild@0.28.0)(eslint@10.3.0(jiti@2.6.1))(ioredis@5.10.1)(magicast@0.5.2)(optionator@0.9.4)(oxlint@1.61.0(oxlint-tsgolint@0.22.0))(rolldown@1.0.0-rc.18)(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-rc.18)(rollup@4.60.2))(rollup@4.60.2)(srvx@0.11.15)(terser@5.46.2)(tsx@4.21.0)(typescript@6.0.3)(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(vue-tsc@3.2.7(typescript@6.0.3))(yaml@2.8.4))(oxc-parser@0.128.0)(rolldown@1.0.0-rc.18)(srvx@0.11.15)(typescript@6.0.3) 9672 + '@nuxt/nitro-server': 4.4.4(c703237518f96a01201357fd255c3b67) 9663 9673 '@nuxt/schema': 4.4.4 9664 9674 '@nuxt/telemetry': 2.8.0(@nuxt/kit@4.4.4(magicast@0.5.2)) 9665 - '@nuxt/vite-builder': 4.4.4(28264294504fc5bb34607a4fbd2f89d6) 9675 + '@nuxt/vite-builder': 4.4.4(60540df5947dbcc3d30c3e97abb55c90) 9666 9676 '@unhead/vue': 2.1.13(vue@3.5.33(typescript@6.0.3)) 9667 9677 '@vue/shared': 3.5.33 9668 9678 chokidar: 5.0.0 ··· 9788 9798 - xml2js 9789 9799 - yaml 9790 9800 9791 - nuxtseo-shared@5.1.3(524f84012e9d72beb906744b155b32ec): 9801 + nuxtseo-shared@5.1.3(722a2202af61d2736787650a24b9e3d3): 9792 9802 dependencies: 9793 9803 '@clack/prompts': 1.3.0 9794 9804 '@nuxt/devtools-kit': 4.0.0-alpha.3(magicast@0.5.2)(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4)) ··· 9797 9807 birpc: 4.0.0 9798 9808 consola: 3.4.2 9799 9809 defu: 6.1.7 9800 - nuxt: 4.4.4(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.6.0)(@vue/compiler-sfc@3.5.33)(cac@6.7.14)(db0@0.3.4(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0)))(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0))(esbuild@0.28.0)(eslint@10.3.0(jiti@2.6.1))(ioredis@5.10.1)(magicast@0.5.2)(optionator@0.9.4)(oxlint@1.61.0(oxlint-tsgolint@0.22.0))(rolldown@1.0.0-rc.18)(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-rc.18)(rollup@4.60.2))(rollup@4.60.2)(srvx@0.11.15)(terser@5.46.2)(tsx@4.21.0)(typescript@6.0.3)(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(vue-tsc@3.2.7(typescript@6.0.3))(yaml@2.8.4) 9810 + nuxt: 4.4.4(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@electric-sql/pglite@0.4.5)(@parcel/watcher@2.5.6)(@types/node@25.6.0)(@vue/compiler-sfc@3.5.33)(cac@6.7.14)(db0@0.3.4(@electric-sql/pglite@0.4.5)(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.5)(@neondatabase/serverless@1.1.0)))(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.5)(@neondatabase/serverless@1.1.0))(esbuild@0.28.0)(eslint@10.3.0(jiti@2.6.1))(ioredis@5.10.1)(magicast@0.5.2)(optionator@0.9.4)(oxlint@1.61.0(oxlint-tsgolint@0.22.0))(rolldown@1.0.0-rc.18)(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-rc.18)(rollup@4.60.2))(rollup@4.60.2)(srvx@0.11.15)(terser@5.46.2)(tsx@4.21.0)(typescript@6.0.3)(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(vue-tsc@3.2.7(typescript@6.0.3))(yaml@2.8.4) 9801 9811 ofetch: 1.5.1 9802 9812 pathe: 2.0.3 9803 9813 pkg-types: 2.3.1 ··· 9807 9817 ufo: 1.6.4 9808 9818 vue: 3.5.33(typescript@6.0.3) 9809 9819 optionalDependencies: 9810 - nuxt-site-config: 4.0.8(@nuxt/schema@4.4.4)(magicast@0.5.2)(nuxt@4.4.4(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.6.0)(@vue/compiler-sfc@3.5.33)(cac@6.7.14)(db0@0.3.4(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0)))(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0))(esbuild@0.28.0)(eslint@10.3.0(jiti@2.6.1))(ioredis@5.10.1)(magicast@0.5.2)(optionator@0.9.4)(oxlint@1.61.0(oxlint-tsgolint@0.22.0))(rolldown@1.0.0-rc.18)(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-rc.18)(rollup@4.60.2))(rollup@4.60.2)(srvx@0.11.15)(terser@5.46.2)(tsx@4.21.0)(typescript@6.0.3)(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(vue-tsc@3.2.7(typescript@6.0.3))(yaml@2.8.4))(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.33(typescript@6.0.3)) 9820 + nuxt-site-config: 4.0.8(63185d4d07eff04c3532e23607595514) 9811 9821 transitivePeerDependencies: 9812 9822 - magicast 9813 9823 - vite ··· 10775 10785 escape-string-regexp: 5.0.0 10776 10786 ufo: 1.6.4 10777 10787 10778 - unstorage@1.17.5(db0@0.3.4(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0)))(ioredis@5.10.1): 10788 + unstorage@1.17.5(db0@0.3.4(@electric-sql/pglite@0.4.5)(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.5)(@neondatabase/serverless@1.1.0)))(ioredis@5.10.1): 10779 10789 dependencies: 10780 10790 anymatch: 3.1.3 10781 10791 chokidar: 5.0.0 ··· 10786 10796 ofetch: 1.5.1 10787 10797 ufo: 1.6.4 10788 10798 optionalDependencies: 10789 - db0: 0.3.4(drizzle-orm@0.45.2(@neondatabase/serverless@1.1.0)) 10799 + db0: 0.3.4(@electric-sql/pglite@0.4.5)(drizzle-orm@0.45.2(@electric-sql/pglite@0.4.5)(@neondatabase/serverless@1.1.0)) 10790 10800 ioredis: 5.10.1 10791 10801 10792 10802 untun@0.1.3:
+71 -3
server/api/github/webhook.post.ts
··· 1 - import type { InstallationEvent } from '@octokit/webhooks-types' 1 + import type { 2 + CreateEvent, 3 + DeleteEvent, 4 + InstallationEvent, 5 + InstallationRepositoriesEvent, 6 + PushEvent, 7 + RepositoryEvent, 8 + } from '@octokit/webhooks-types' 2 9 import { verify } from '@octokit/webhooks-methods' 3 10 import { sql } from 'drizzle-orm' 4 11 import { installation, webhookEvent } from '~~/server/db/schema' 12 + import { enqueue } from '~~/server/utils/queue' 5 13 6 14 const RECOGNISED_EVENTS = new Set([ 7 15 'push', ··· 95 103 } 96 104 } 97 105 98 - // TODO(commit 7): enqueue a job for recognised event types. 106 + // Enqueue work for events we care about. Envelope shape is the minimum the 107 + // handler needs to re-fetch fresh data via the GitHub API; we never persist 108 + // the raw webhook body. See PLAN.md "Deferred / follow-ups". 99 109 if (RECOGNISED_EVENTS.has(eventName)) { 100 - // Will become: await enqueue({ kind: eventName, payload: <envelope> }) 110 + await enqueueForEvent(event, eventName, deliveryId) 101 111 } 102 112 103 113 return { ok: true, deliveryId } 104 114 }) 115 + 116 + async function enqueueForEvent(event: Parameters<typeof readBody>[0], eventName: string, deliveryId: string) { 117 + if (eventName === 'push') { 118 + const body = await readBody<PushEvent>(event) 119 + if (!body.installation) return 120 + await enqueue('github.push', { 121 + deliveryId, 122 + installationId: body.installation.id, 123 + githubRepoId: body.repository.id, 124 + ref: body.ref, 125 + before: body.before, 126 + after: body.after, 127 + }) 128 + } 129 + else if (eventName === 'create') { 130 + const body = await readBody<CreateEvent>(event) 131 + if (!body.installation) return 132 + await enqueue('github.create', { 133 + deliveryId, 134 + installationId: body.installation.id, 135 + githubRepoId: body.repository.id, 136 + refType: body.ref_type, 137 + ref: body.ref, 138 + }) 139 + } 140 + else if (eventName === 'delete') { 141 + const body = await readBody<DeleteEvent>(event) 142 + if (!body.installation) return 143 + await enqueue('github.delete', { 144 + deliveryId, 145 + installationId: body.installation.id, 146 + githubRepoId: body.repository.id, 147 + refType: body.ref_type, 148 + ref: body.ref, 149 + }) 150 + } 151 + else if (eventName === 'repository') { 152 + const body = await readBody<RepositoryEvent>(event) 153 + if (!body.installation) return 154 + await enqueue('github.repository', { 155 + deliveryId, 156 + installationId: body.installation.id, 157 + githubRepoId: body.repository.id, 158 + action: body.action, 159 + }) 160 + } 161 + else if (eventName === 'installation_repositories') { 162 + const body = await readBody<InstallationRepositoriesEvent>(event) 163 + await enqueue('github.installation_repositories', { 164 + deliveryId, 165 + installationId: body.installation.id, 166 + action: body.action, 167 + addedRepoIds: 'repositories_added' in body ? body.repositories_added.map(r => r.id) : [], 168 + removedRepoIds: 'repositories_removed' in body ? body.repositories_removed.map(r => r.id) : [], 169 + }) 170 + } 171 + // 'installation' is handled inline above for bookkeeping; no job enqueued. 172 + }
+54
server/api/jobs/run.post.ts
··· 1 + import crypto from 'node:crypto' 2 + import { dispatch } from '~~/server/utils/job-handlers' 3 + import { claim, complete, fail } from '~~/server/utils/queue' 4 + 5 + const LEASE_MS = 5 * 60_000 // 5 min — generous for a sync job 6 + const DEFAULT_BUDGET_MS = 25_000 // leave headroom under Vercel's 10s default; pro tiers can override 7 + 8 + export default defineEventHandler(async event => { 9 + const config = useRuntimeConfig() 10 + const cronSecret = config.cronSecret 11 + if (!cronSecret) { 12 + throw createError({ statusCode: 500, statusMessage: 'cron secret not configured' }) 13 + } 14 + 15 + const auth = getRequestHeader(event, 'authorization') 16 + if (auth !== `Bearer ${cronSecret}`) { 17 + throw createError({ statusCode: 401, statusMessage: 'unauthorized' }) 18 + } 19 + 20 + const workerId = `${process.env.VERCEL_DEPLOYMENT_ID ?? 'local'}:${crypto.randomUUID()}` 21 + const budgetMs = Number(config.workerBudgetMs) || DEFAULT_BUDGET_MS 22 + const deadline = Date.now() + budgetMs 23 + 24 + let processed = 0 25 + let drained = false 26 + 27 + // Sequential by design: each iteration claims one job, runs it, records the 28 + // outcome. We don't parallelise because each Vercel invocation is a single 29 + // small worker; concurrency comes from cron firing multiple invocations. 30 + // eslint-disable-next-line no-await-in-loop 31 + while (Date.now() < deadline) { 32 + // eslint-disable-next-line no-await-in-loop 33 + const job = await claim(workerId, LEASE_MS) 34 + if (!job) { 35 + drained = true 36 + break 37 + } 38 + 39 + try { 40 + // eslint-disable-next-line no-await-in-loop 41 + await dispatch(job) 42 + // eslint-disable-next-line no-await-in-loop 43 + await complete(job.id) 44 + } 45 + catch (err) { 46 + // eslint-disable-next-line no-await-in-loop 47 + await fail(job.id, job.attempts, err) 48 + } 49 + 50 + processed++ 51 + } 52 + 53 + return { ok: true, processed, drained, workerId } 54 + })
+19 -4
server/utils/db.ts
··· 1 1 import { neon } from '@neondatabase/serverless' 2 - import { drizzle } from 'drizzle-orm/neon-http' 2 + import { drizzle as drizzleNeon } from 'drizzle-orm/neon-http' 3 3 import * as schema from '../db/schema' 4 4 5 - let _db: ReturnType<typeof drizzle<typeof schema>> | undefined 5 + export type Db = ReturnType<typeof drizzleNeon<typeof schema>> 6 + 7 + let _db: Db | undefined 8 + 9 + /** 10 + * Override the DB returned by `useDb()`. Tests inject a PGlite-backed Drizzle 11 + * instance with the same schema. Production code never calls this. 12 + */ 13 + export function setDb(db: Db) { 14 + _db = db 15 + } 16 + 17 + /** Clear the cached DB so the next `useDb()` reconstructs from runtime config. */ 18 + export function clearDb() { 19 + _db = undefined 20 + } 6 21 7 - export function useDb() { 22 + export function useDb(): Db { 8 23 if (_db) return _db 9 24 const { databaseUrl } = useRuntimeConfig() 10 25 if (!databaseUrl) { 11 26 throw new Error('NUXT_DATABASE_URL is not set') 12 27 } 13 28 const client = neon(databaseUrl) 14 - _db = drizzle(client, { schema }) 29 + _db = drizzleNeon(client, { schema }) 15 30 return _db 16 31 } 17 32
+30
server/utils/job-handlers.ts
··· 1 + import type { JobEnvelope } from './queue' 2 + 3 + /** 4 + * Map of job kind → handler. Handlers are filled in by later commits: 5 + * - 'github.push' → commit 11 (sync push events) 6 + * - 'github.create' / 'github.delete' → commit 12 (branch/tag ref ops) 7 + * - 'github.repository' → commit 13/14 (description, lifecycle) 8 + * - 'tangled.create-repo' → commit 10 (initial enrolment) 9 + * - 'atproto.publish-pubkey' → commit 9 (publish ssh public key) 10 + * 11 + * For now the dispatcher knows the recognised kinds but routes them all to a 12 + * noop. An unknown kind throws so it surfaces as a job failure rather than 13 + * silent acknowledgement. 14 + */ 15 + const KNOWN_KINDS = new Set([ 16 + 'github.push', 17 + 'github.create', 18 + 'github.delete', 19 + 'github.repository', 20 + 'github.installation_repositories', 21 + 'tangled.create-repo', 22 + 'atproto.publish-pubkey', 23 + ]) 24 + 25 + export async function dispatch(envelope: JobEnvelope): Promise<void> { 26 + if (!KNOWN_KINDS.has(envelope.kind)) { 27 + throw new Error(`unknown job kind: ${envelope.kind}`) 28 + } 29 + // No-op until handlers land in later commits. 30 + }
+113
server/utils/queue.ts
··· 1 + import { sql } from 'drizzle-orm' 2 + import { job } from '../db/schema' 3 + import { useDb } from './db' 4 + 5 + export interface JobEnvelope { 6 + id: number 7 + kind: string 8 + payload: unknown 9 + attempts: number 10 + } 11 + 12 + export interface EnqueueOptions { 13 + /** Run no earlier than this time. Default: now. */ 14 + runAfter?: Date 15 + } 16 + 17 + /** 18 + * Push a job onto the queue. `payload` must be a JSON-serialisable object — keep 19 + * it small (an envelope of identifiers, not a webhook body); see PLAN.md. 20 + */ 21 + export async function enqueue(kind: string, payload: object, opts: EnqueueOptions = {}) { 22 + const db = useDb() 23 + const [row] = await db.insert(job).values({ 24 + kind, 25 + payload, 26 + runAfter: opts.runAfter ?? new Date(), 27 + }).returning({ id: job.id }) 28 + return row 29 + } 30 + 31 + /** 32 + * Atomically claim the oldest runnable job. Uses 33 + * `UPDATE ... WHERE id = (SELECT ... FOR UPDATE SKIP LOCKED LIMIT 1)` 34 + * so multiple workers don't race for the same row. 35 + * 36 + * Returns null when the queue is empty (or all rows are locked / not yet due). 37 + */ 38 + export async function claim(workerId: string, leaseMs: number): Promise<JobEnvelope | null> { 39 + const db = useDb() 40 + const leaseUntil = new Date(Date.now() + leaseMs) 41 + 42 + // Two conditions for a job to be claimable: 43 + // 1. status='queued' AND run_after <= now() 44 + // 2. status='running' AND locked_until < now() (lease expired) 45 + const result = await db.execute(sql` 46 + UPDATE ${job} 47 + SET 48 + status = 'running', 49 + locked_by = ${workerId}, 50 + locked_until = ${leaseUntil.toISOString()}, 51 + attempts = ${job.attempts} + 1, 52 + updated_at = now() 53 + WHERE ${job.id} = ( 54 + SELECT ${job.id} FROM ${job} 55 + WHERE 56 + (${job.status} = 'queued' AND ${job.runAfter} <= now()) 57 + OR 58 + (${job.status} = 'running' AND ${job.lockedUntil} < now()) 59 + ORDER BY ${job.runAfter} ASC 60 + FOR UPDATE SKIP LOCKED 61 + LIMIT 1 62 + ) 63 + RETURNING id, kind, payload, attempts 64 + `) 65 + 66 + // drizzle's neon-http execute returns rows on `.rows`; pglite's returns directly. 67 + // Normalise. 68 + const rows = (Array.isArray(result) ? result : (result as { rows?: unknown[] }).rows) ?? [] 69 + if (rows.length === 0) return null 70 + return rows[0] as JobEnvelope 71 + } 72 + 73 + /** Mark a job as completed. */ 74 + export async function complete(id: number) { 75 + const db = useDb() 76 + await db.update(job) 77 + .set({ status: 'done', lockedBy: null, lockedUntil: null, lastError: null, updatedAt: new Date() }) 78 + .where(sql`${job.id} = ${id}`) 79 + } 80 + 81 + /** 82 + * Record a failure. Re-queues with exponential backoff until `maxAttempts`, 83 + * after which the job is marked `failed` and stays put for inspection. 84 + */ 85 + export async function fail(id: number, attempts: number, err: unknown, maxAttempts = 5) { 86 + const db = useDb() 87 + const message = err instanceof Error ? err.message : String(err) 88 + 89 + if (attempts >= maxAttempts) { 90 + await db.update(job) 91 + .set({ status: 'failed', lastError: message, lockedBy: null, lockedUntil: null, updatedAt: new Date() }) 92 + .where(sql`${job.id} = ${id}`) 93 + return 94 + } 95 + 96 + // Exponential backoff with ±20% jitter: base*2^attempts, capped. 97 + const base = 30_000 // 30s 98 + const cap = 60 * 60_000 // 1h 99 + const backoff = Math.min(base * (2 ** (attempts - 1)), cap) 100 + const jitter = backoff * (0.8 + Math.random() * 0.4) 101 + const runAfter = new Date(Date.now() + jitter) 102 + 103 + await db.update(job) 104 + .set({ 105 + status: 'queued', 106 + lockedBy: null, 107 + lockedUntil: null, 108 + runAfter, 109 + lastError: message, 110 + updatedAt: new Date(), 111 + }) 112 + .where(sql`${job.id} = ${id}`) 113 + }
+98
test/unit/queue.spec.ts
··· 1 + import { sql } from 'drizzle-orm' 2 + import { afterEach, beforeEach, describe, expect, it } from 'vitest' 3 + import { job } from '../../server/db/schema' 4 + import { clearDb, setDb, useDb } from '../../server/utils/db' 5 + import { claim, complete, enqueue, fail } from '../../server/utils/queue' 6 + import { createTestDb } from '../utils/db' 7 + 8 + describe('queue', () => { 9 + beforeEach(async () => { 10 + setDb(await createTestDb()) 11 + }) 12 + 13 + afterEach(() => { 14 + clearDb() 15 + }) 16 + 17 + it('enqueues and claims a job', async () => { 18 + const queued = await enqueue('github.push', { foo: 'bar' }) 19 + expect(queued.id).toBeGreaterThan(0) 20 + 21 + const claimed = await claim('worker-1', 60_000) 22 + expect(claimed?.kind).toBe('github.push') 23 + expect(claimed?.payload).toEqual({ foo: 'bar' }) 24 + expect(claimed?.attempts).toBe(1) 25 + }) 26 + 27 + it('returns null when the queue is empty', async () => { 28 + const claimed = await claim('worker-1', 60_000) 29 + expect(claimed).toBeNull() 30 + }) 31 + 32 + it('does not claim the same job twice', async () => { 33 + await enqueue('github.push', { foo: 'bar' }) 34 + 35 + const a = await claim('worker-1', 60_000) 36 + const b = await claim('worker-2', 60_000) 37 + 38 + expect(a).not.toBeNull() 39 + expect(b).toBeNull() 40 + }) 41 + 42 + it('respects run_after', async () => { 43 + const future = new Date(Date.now() + 60_000) 44 + await enqueue('github.push', {}, { runAfter: future }) 45 + 46 + const claimed = await claim('worker-1', 60_000) 47 + expect(claimed).toBeNull() 48 + }) 49 + 50 + it('reclaims a job whose lease has expired', async () => { 51 + await enqueue('github.push', {}) 52 + 53 + const first = await claim('worker-1', 60_000) 54 + expect(first).not.toBeNull() 55 + 56 + // Force the lease into the past. 57 + const db = useDb() 58 + await db.execute(sql`UPDATE ${job} SET locked_until = now() - interval '1 minute'`) 59 + 60 + const second = await claim('worker-2', 60_000) 61 + expect(second?.id).toBe(first!.id) 62 + expect(second?.attempts).toBe(2) // attempts increments on each claim 63 + }) 64 + 65 + it('marks a job as done on complete', async () => { 66 + const queued = await enqueue('github.push', {}) 67 + const claimed = await claim('worker-1', 60_000) 68 + await complete(claimed!.id) 69 + 70 + const db = useDb() 71 + const rows = await db.select().from(job).where(sql`${job.id} = ${queued.id}`) 72 + expect(rows[0]?.status).toBe('done') 73 + expect(rows[0]?.lockedBy).toBeNull() 74 + }) 75 + 76 + it('re-queues with backoff on fail before maxAttempts', async () => { 77 + await enqueue('github.push', {}) 78 + const claimed = await claim('worker-1', 60_000) 79 + await fail(claimed!.id, claimed!.attempts, new Error('boom')) 80 + 81 + const db = useDb() 82 + const rows = await db.select().from(job).where(sql`${job.id} = ${claimed!.id}`) 83 + expect(rows[0]?.status).toBe('queued') 84 + expect(rows[0]?.lastError).toBe('boom') 85 + expect(new Date(rows[0]?.runAfter ?? 0).getTime()).toBeGreaterThan(Date.now()) 86 + }) 87 + 88 + it('marks failed once attempts >= maxAttempts', async () => { 89 + await enqueue('github.push', {}) 90 + const claimed = await claim('worker-1', 60_000) 91 + await fail(claimed!.id, 5, new Error('terminal'), 5) 92 + 93 + const db = useDb() 94 + const rows = await db.select().from(job).where(sql`${job.id} = ${claimed!.id}`) 95 + expect(rows[0]?.status).toBe('failed') 96 + expect(rows[0]?.lastError).toBe('terminal') 97 + }) 98 + })
+30
test/utils/db.ts
··· 1 + import { readFileSync } from 'node:fs' 2 + import { fileURLToPath } from 'node:url' 3 + import { PGlite } from '@electric-sql/pglite' 4 + import { drizzle } from 'drizzle-orm/pglite' 5 + import * as schema from '../../server/db/schema' 6 + import type { Db } from '../../server/utils/db' 7 + 8 + const migrationPath = fileURLToPath( 9 + new URL('../../server/db/migrations/0000_initial.sql', import.meta.url), 10 + ) 11 + 12 + /** 13 + * Create a fresh in-memory Postgres (PGlite) for a single test, apply our 14 + * schema, and return a Drizzle instance. Each call returns an isolated DB. 15 + */ 16 + export async function createTestDb(): Promise<Db> { 17 + const pg = new PGlite() 18 + const sql = readFileSync(migrationPath, 'utf8') 19 + 20 + // The drizzle-generated migration file uses `--> statement-breakpoint` between 21 + // statements; PGlite's exec accepts the whole thing if we strip those markers. 22 + // Sequential by design — DDL ordering matters. 23 + for (const statement of sql.split('--> statement-breakpoint')) { 24 + const trimmed = statement.trim() 25 + // eslint-disable-next-line no-await-in-loop 26 + if (trimmed) await pg.exec(trimmed) 27 + } 28 + 29 + return drizzle(pg, { schema }) as unknown as Db 30 + }