feat: pulseflare package — CF Workers + D1 runtime (refs #5)
CI / test (push) Has been cancelled

This commit is contained in:
2026-04-18 07:51:25 +00:00
parent ad25161a8f
commit 272aa9728a
9 changed files with 865 additions and 0 deletions
+193
View File
@@ -60,6 +60,17 @@
"@uncaged/pulse": ">=0.1.0",
},
},
"packages/pulseflare": {
"name": "@uncaged/pulseflare",
"version": "0.1.0",
"dependencies": {
"@uncaged/pulse": "workspace:*",
},
"devDependencies": {
"@cloudflare/workers-types": "^4.20260418.1",
"wrangler": "^4.83.0",
},
},
"packages/upulse": {
"name": "@uncaged/upulse",
"version": "0.1.0",
@@ -96,6 +107,144 @@
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.4.11", "", { "os": "win32", "cpu": "x64" }, "sha512-A8D3JM/00C2KQgUV3oj8Ba15EHEYwebAGCy5Sf9GAjr5Y3+kJIYOiESoqRDeuRZueuMdCsbLZIUqmPhpYXJE9A=="],
"@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.4.2", "", {}, "sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ=="],
"@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.16.0", "", { "peerDependencies": { "unenv": "2.0.0-rc.24", "workerd": "1.20260301.1 || ~1.20260302.1 || ~1.20260303.1 || ~1.20260304.1 || >1.20260305.0 <2.0.0-0" }, "optionalPeers": ["workerd"] }, "sha512-8ovsRpwzPoEqPUzoErAYVv8l3FMZNeBVQfJTvtzP4AgLSRGZISRfuChFxHWUQd3n6cnrwkuTGxT+2cGo8EsyYg=="],
"@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20260415.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-dsxaKsQm3LnPGNPEdsRv09QN3Y4DqCw7kX5j6noKqbAtro2jTr95sVlYM1jUxZ5FkOl1f7SXgaKKB9t5H5Nkbg=="],
"@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20260415.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-+JgSgVA49KyKteHRA1SnonE4Zn5Ei5zdAp5FQMxFmXI8qulZw4Hl7safXxRyK4i9sTO8gl7TFOKO5Q64VPvSDQ=="],
"@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20260415.1", "", { "os": "linux", "cpu": "x64" }, "sha512-tU+9pwsqCy8afOVlGtiWrWQc/fedQK4SRm4KPIAt+zOiQWDxWASm6YGBUJis5c648WN80yz47qnmdDi8DQNOcA=="],
"@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20260415.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-bR9uITnV19r5NQ14xnypi2xHXu2iQvfYV8cVgx0JouFUmWwTEEAwFVojDdssGq93VHX9hr/pi2IRUZeegbYBog=="],
"@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20260415.1", "", { "os": "win32", "cpu": "x64" }, "sha512-4NuMLlerI0Ijua3Ir8HXQ+qyNvCUDEG5gDco5Om+sAiK6rnWiz+aGoSlbB8W16yW9QAgzCstbmXLiVknUBflfQ=="],
"@cloudflare/workers-types": ["@cloudflare/workers-types@4.20260418.1", "", {}, "sha512-bywXb2XmeSqrLCQYipcupLneqx015YhhNWz2v9b9iatpe8Cg551vP7ZuD5S2a6GfBka0dDnO70kIBiBvFglcrg=="],
"@cspotcode/source-map-support": ["@cspotcode/source-map-support@0.8.1", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.9" } }, "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw=="],
"@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="],
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.3", "", { "os": "aix", "cpu": "ppc64" }, "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg=="],
"@esbuild/android-arm": ["@esbuild/android-arm@0.27.3", "", { "os": "android", "cpu": "arm" }, "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA=="],
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.3", "", { "os": "android", "cpu": "arm64" }, "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg=="],
"@esbuild/android-x64": ["@esbuild/android-x64@0.27.3", "", { "os": "android", "cpu": "x64" }, "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ=="],
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg=="],
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg=="],
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.3", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w=="],
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA=="],
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.3", "", { "os": "linux", "cpu": "arm" }, "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw=="],
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg=="],
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.3", "", { "os": "linux", "cpu": "ia32" }, "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg=="],
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA=="],
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw=="],
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA=="],
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ=="],
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw=="],
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.3", "", { "os": "linux", "cpu": "x64" }, "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA=="],
"@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.3", "", { "os": "none", "cpu": "arm64" }, "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA=="],
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.3", "", { "os": "none", "cpu": "x64" }, "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA=="],
"@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.3", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw=="],
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.3", "", { "os": "openbsd", "cpu": "x64" }, "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ=="],
"@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.3", "", { "os": "none", "cpu": "arm64" }, "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g=="],
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.3", "", { "os": "sunos", "cpu": "x64" }, "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA=="],
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA=="],
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q=="],
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.3", "", { "os": "win32", "cpu": "x64" }, "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA=="],
"@img/colour": ["@img/colour@1.1.0", "", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="],
"@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="],
"@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="],
"@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="],
"@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="],
"@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A=="],
"@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw=="],
"@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.2.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA=="],
"@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.2.4", "", { "os": "linux", "cpu": "none" }, "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA=="],
"@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ=="],
"@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw=="],
"@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw=="],
"@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg=="],
"@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.4" }, "os": "linux", "cpu": "arm" }, "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw=="],
"@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg=="],
"@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.4" }, "os": "linux", "cpu": "ppc64" }, "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA=="],
"@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.2.4" }, "os": "linux", "cpu": "none" }, "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw=="],
"@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.4" }, "os": "linux", "cpu": "s390x" }, "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg=="],
"@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ=="],
"@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg=="],
"@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q=="],
"@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.5", "", { "dependencies": { "@emnapi/runtime": "^1.7.0" }, "cpu": "none" }, "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw=="],
"@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g=="],
"@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg=="],
"@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="],
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.9", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" } }, "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ=="],
"@poppinss/colors": ["@poppinss/colors@4.1.6", "", { "dependencies": { "kleur": "^4.1.5" } }, "sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg=="],
"@poppinss/dumper": ["@poppinss/dumper@0.6.5", "", { "dependencies": { "@poppinss/colors": "^4.1.5", "@sindresorhus/is": "^7.0.2", "supports-color": "^10.0.0" } }, "sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw=="],
"@poppinss/exception": ["@poppinss/exception@1.2.3", "", {}, "sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw=="],
"@sindresorhus/is": ["@sindresorhus/is@7.2.0", "", {}, "sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw=="],
"@speed-highlight/core": ["@speed-highlight/core@1.2.15", "", {}, "sha512-BMq1K3DsElxDWawkX6eLg9+CKJrTVGCBAWVuHXVUV2u0s2711qiChLSId6ikYPfxhdYocLNt3wWwSvDiTvFabw=="],
"@types/node": ["@types/node@25.6.0", "", { "dependencies": { "undici-types": "~7.19.0" } }, "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ=="],
"@uncaged/pulse": ["@uncaged/pulse@workspace:packages/pulse"],
@@ -106,18 +255,62 @@
"@uncaged/pulse-openclaw": ["@uncaged/pulse-openclaw@workspace:packages/pulse-openclaw"],
"@uncaged/pulseflare": ["@uncaged/pulseflare@workspace:packages/pulseflare"],
"@uncaged/upulse": ["@uncaged/upulse@workspace:packages/upulse"],
"blake3-wasm": ["blake3-wasm@2.1.5", "", {}, "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g=="],
"bun-types": ["bun-types@1.3.12", "", { "dependencies": { "@types/node": "*" } }, "sha512-HqOLj5PoFajAQciOMRiIZGNoKxDJSr6qigAttOX40vJuSp6DN/CxWp9s3C1Xwm4oH7ybueITwiaOcWXoYVoRkA=="],
"commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="],
"cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="],
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
"error-stack-parser-es": ["error-stack-parser-es@1.0.5", "", {}, "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA=="],
"esbuild": ["esbuild@0.27.3", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.3", "@esbuild/android-arm": "0.27.3", "@esbuild/android-arm64": "0.27.3", "@esbuild/android-x64": "0.27.3", "@esbuild/darwin-arm64": "0.27.3", "@esbuild/darwin-x64": "0.27.3", "@esbuild/freebsd-arm64": "0.27.3", "@esbuild/freebsd-x64": "0.27.3", "@esbuild/linux-arm": "0.27.3", "@esbuild/linux-arm64": "0.27.3", "@esbuild/linux-ia32": "0.27.3", "@esbuild/linux-loong64": "0.27.3", "@esbuild/linux-mips64el": "0.27.3", "@esbuild/linux-ppc64": "0.27.3", "@esbuild/linux-riscv64": "0.27.3", "@esbuild/linux-s390x": "0.27.3", "@esbuild/linux-x64": "0.27.3", "@esbuild/netbsd-arm64": "0.27.3", "@esbuild/netbsd-x64": "0.27.3", "@esbuild/openbsd-arm64": "0.27.3", "@esbuild/openbsd-x64": "0.27.3", "@esbuild/openharmony-arm64": "0.27.3", "@esbuild/sunos-x64": "0.27.3", "@esbuild/win32-arm64": "0.27.3", "@esbuild/win32-ia32": "0.27.3", "@esbuild/win32-x64": "0.27.3" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg=="],
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
"jsonata": ["jsonata@2.1.0", "", {}, "sha512-OCzaRMK8HobtX8fp37uIVmL8CY1IGc/a6gLsDqz3quExFR09/U78HUzWYr7T31UEB6+Eu0/8dkVD5fFDOl9a8w=="],
"kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
"miniflare": ["miniflare@4.20260415.0", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "sharp": "^0.34.5", "undici": "7.24.8", "workerd": "1.20260415.1", "ws": "8.18.0", "youch": "4.1.0-beta.10" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-JoExRWN4YBI2luA5BoSMFEgi8rQWXUGzo3mtE+58VXCLV3jj/Xnk5Yeqs/IXWz8Es5GJIaq6BtsixDvAxXSIng=="],
"path-to-regexp": ["path-to-regexp@6.3.0", "", {}, "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ=="],
"pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
"semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
"sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.5", "@img/sharp-darwin-x64": "0.34.5", "@img/sharp-libvips-darwin-arm64": "1.2.4", "@img/sharp-libvips-darwin-x64": "1.2.4", "@img/sharp-libvips-linux-arm": "1.2.4", "@img/sharp-libvips-linux-arm64": "1.2.4", "@img/sharp-libvips-linux-ppc64": "1.2.4", "@img/sharp-libvips-linux-riscv64": "1.2.4", "@img/sharp-libvips-linux-s390x": "1.2.4", "@img/sharp-libvips-linux-x64": "1.2.4", "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", "@img/sharp-libvips-linuxmusl-x64": "1.2.4", "@img/sharp-linux-arm": "0.34.5", "@img/sharp-linux-arm64": "0.34.5", "@img/sharp-linux-ppc64": "0.34.5", "@img/sharp-linux-riscv64": "0.34.5", "@img/sharp-linux-s390x": "0.34.5", "@img/sharp-linux-x64": "0.34.5", "@img/sharp-linuxmusl-arm64": "0.34.5", "@img/sharp-linuxmusl-x64": "0.34.5", "@img/sharp-wasm32": "0.34.5", "@img/sharp-win32-arm64": "0.34.5", "@img/sharp-win32-ia32": "0.34.5", "@img/sharp-win32-x64": "0.34.5" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="],
"supports-color": ["supports-color@10.2.2", "", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="],
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"typescript": ["typescript@6.0.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ=="],
"undici": ["undici@7.24.8", "", {}, "sha512-6KQ/+QxK49Z/p3HO6E5ZCZWNnCasyZLa5ExaVYyvPxUwKtbCPMKELJOqh7EqOle0t9cH/7d2TaaTRRa6Nhs4YQ=="],
"undici-types": ["undici-types@7.19.2", "", {}, "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg=="],
"unenv": ["unenv@2.0.0-rc.24", "", { "dependencies": { "pathe": "^2.0.3" } }, "sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw=="],
"workerd": ["workerd@1.20260415.1", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20260415.1", "@cloudflare/workerd-darwin-arm64": "1.20260415.1", "@cloudflare/workerd-linux-64": "1.20260415.1", "@cloudflare/workerd-linux-arm64": "1.20260415.1", "@cloudflare/workerd-windows-64": "1.20260415.1" }, "bin": { "workerd": "bin/workerd" } }, "sha512-phyPjRnx+mQDfkhN9ENPioL1L0SdhYs4S0YmJK/xF9Oga+ykNfdSy1MHnsOj8yqnOV96zcVQMx32dJ0r3pq0jQ=="],
"wrangler": ["wrangler@4.83.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.2", "@cloudflare/unenv-preset": "2.16.0", "blake3-wasm": "2.1.5", "esbuild": "0.27.3", "miniflare": "4.20260415.0", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", "workerd": "1.20260415.1" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20260415.1" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-gw5g3LCiuAqVWxaoKY6+quE0HzAUEFb/FV3oAlNkE1ttd4XP3FiV91XDkkzUCcdqxS4WjhQvPhIDBNdhEi8P0A=="],
"ws": ["ws@8.18.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="],
"youch": ["youch@4.1.0-beta.10", "", { "dependencies": { "@poppinss/colors": "^4.1.5", "@poppinss/dumper": "^0.6.4", "@speed-highlight/core": "^1.2.7", "cookie": "^1.0.2", "youch-core": "^0.3.3" } }, "sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ=="],
"youch-core": ["youch-core@0.3.3", "", { "dependencies": { "@poppinss/exception": "^1.2.2", "error-stack-parser-es": "^1.0.5" } }, "sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA=="],
"zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="],
}
}
+17
View File
@@ -0,0 +1,17 @@
{
"name": "@uncaged/pulseflare",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "wrangler dev",
"deploy": "wrangler deploy",
"migrate": "wrangler d1 migrations apply pulseflare-db"
},
"dependencies": {
"@uncaged/pulse": "workspace:*"
},
"devDependencies": {
"@cloudflare/workers-types": "^4.20260418.1",
"wrangler": "^4.83.0"
}
}
+138
View File
@@ -0,0 +1,138 @@
import type { D1Database, DurableObject, DurableObjectState } from '@cloudflare/workers-types';
import { D1PulseStore } from './store-d1.js';
import { createSigilExecutor } from './executor-sigil.js';
export interface PulseTickEnv {
DB: D1Database;
SIGIL_URL: string;
SIGIL_TOKEN?: string;
TICK_INTERVAL_MS?: string;
}
export class PulseTick implements DurableObject {
private tickIntervalMs: number;
private store: D1PulseStore;
private sigilExecutor: ReturnType<typeof createSigilExecutor>;
constructor(private state: DurableObjectState, private env: PulseTickEnv) {
this.tickIntervalMs = parseInt(env.TICK_INTERVAL_MS ?? '30000'); // Default 30s
this.store = new D1PulseStore(env.DB);
this.sigilExecutor = createSigilExecutor(
env.SIGIL_URL,
env.SIGIL_TOKEN ?? 'default-token'
);
}
async fetch(request: Request): Promise<Response> {
const url = new URL(request.url);
if (request.method === 'POST' && url.pathname === '/tick') {
// Manual tick trigger
await this.performTick();
return new Response(JSON.stringify({ status: 'tick performed' }), {
headers: { 'Content-Type': 'application/json' }
});
}
if (request.method === 'GET' && url.pathname === '/status') {
const status = await this.getStatus();
return new Response(JSON.stringify(status), {
headers: { 'Content-Type': 'application/json' }
});
}
if (request.method === 'POST' && url.pathname === '/start') {
await this.startTicking();
return new Response(JSON.stringify({ status: 'ticking started' }), {
headers: { 'Content-Type': 'application/json' }
});
}
return new Response('Not found', { status: 404 });
}
async alarm(): Promise<void> {
try {
await this.performTick();
// Schedule next tick
const nextTickTime = Date.now() + this.tickIntervalMs;
await this.state.storage.setAlarm(nextTickTime);
} catch (error) {
console.error('Error during tick:', error);
// Still schedule next tick even if this one failed
const nextTickTime = Date.now() + this.tickIntervalMs;
await this.state.storage.setAlarm(nextTickTime);
}
}
private async startTicking(): Promise<void> {
// Cancel any existing alarm
await this.state.storage.deleteAlarm();
// Set first alarm
const firstTickTime = Date.now() + this.tickIntervalMs;
await this.state.storage.setAlarm(firstTickTime);
await this.state.storage.put('ticking', true);
}
private async performTick(): Promise<void> {
console.log('Performing tick at', new Date().toISOString());
// 1. Read latest state from D1
const hasEvents = await this.store.hasEvents();
const recentEvents = await this.store.getRecent(10);
console.log('Has events:', hasEvents, 'Recent events:', recentEvents.length);
// 2. Run Pulse tick logic (placeholder for now)
// This is where we would:
// - Load rule definitions
// - Process events through rules
// - Generate effects
// - Execute effects through Sigil
// Placeholder: just log the tick
const tickEvent = {
occurredAt: Date.now(),
kind: 'pulse.tick',
meta: JSON.stringify({
eventsCount: recentEvents.length,
tickedAt: new Date().toISOString()
})
};
await this.store.appendEvent(tickEvent);
// Example effect execution (commented out for now)
/*
try {
const result = await this.sigilExecutor.invoke('example-capability', {
message: 'Tick performed',
timestamp: Date.now()
});
console.log('Effect result:', result);
} catch (error) {
console.error('Error executing effect:', error);
}
*/
}
private async getStatus(): Promise<any> {
const isTicking = await this.state.storage.get<boolean>('ticking') ?? false;
const currentAlarm = await this.state.storage.getAlarm();
const hasEvents = await this.store.hasEvents();
const recentEvents = await this.store.getRecent(5);
return {
isTicking,
nextAlarmAt: currentAlarm ? new Date(currentAlarm).toISOString() : null,
tickIntervalMs: this.tickIntervalMs,
hasEvents,
recentEventsCount: recentEvents.length,
lastEvents: recentEvents.slice(0, 3)
};
}
}
+20
View File
@@ -0,0 +1,20 @@
export interface SigilExecutor {
invoke(capability: string, input: unknown): Promise<{ status: number; payload: unknown }>;
}
export function createSigilExecutor(sigilUrl: string, token: string): SigilExecutor {
return {
async invoke(capability, input) {
const res = await fetch(`${sigilUrl}/_api/invoke`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
},
body: JSON.stringify({ capability, params: input }),
});
const data = await res.json();
return { status: res.status, payload: data };
}
};
}
+92
View File
@@ -0,0 +1,92 @@
import type { D1Database, DurableObjectNamespace } from '@cloudflare/workers-types';
import { D1PulseStore } from './store-d1.js';
import { PulseTick } from './durable-tick.js';
export { PulseTick };
export interface Env {
DB: D1Database;
PULSE_TICK: DurableObjectNamespace;
SIGIL_URL: string;
SIGIL_TOKEN?: string;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const url = new URL(request.url);
const path = url.pathname;
// Health check
if (request.method === 'GET' && path === '/health') {
return new Response(JSON.stringify({
status: 'healthy',
timestamp: new Date().toISOString(),
service: 'pulseflare'
}), {
headers: { 'Content-Type': 'application/json' }
});
}
// Manual tick trigger
if (request.method === 'POST' && path === '/tick') {
const durableObjectId = env.PULSE_TICK.idFromName('default');
const durableObject = env.PULSE_TICK.get(durableObjectId);
return await durableObject.fetch(new Request(request.url.replace(path, '/tick'), {
method: 'POST'
}));
}
// Get events
if (request.method === 'GET' && path === '/events') {
const store = new D1PulseStore(env.DB);
const limit = parseInt(url.searchParams.get('limit') ?? '20');
const events = await store.getRecent(limit);
return new Response(JSON.stringify({
events,
count: events.length,
timestamp: new Date().toISOString()
}), {
headers: { 'Content-Type': 'application/json' }
});
}
// Get status (from Durable Object)
if (request.method === 'GET' && path === '/status') {
const durableObjectId = env.PULSE_TICK.idFromName('default');
const durableObject = env.PULSE_TICK.get(durableObjectId);
return await durableObject.fetch(new Request(request.url.replace(path, '/status'), {
method: 'GET'
}));
}
// Start ticking
if (request.method === 'POST' && path === '/start') {
const durableObjectId = env.PULSE_TICK.idFromName('default');
const durableObject = env.PULSE_TICK.get(durableObjectId);
return await durableObject.fetch(new Request(request.url.replace(path, '/start'), {
method: 'POST'
}));
}
// Default 404
return new Response(JSON.stringify({
error: 'Not found',
path,
method: request.method,
available_endpoints: [
'GET /health',
'POST /tick',
'GET /events',
'GET /status',
'POST /start'
]
}), {
status: 404,
headers: { 'Content-Type': 'application/json' }
});
},
};
+56
View File
@@ -0,0 +1,56 @@
-- Events table
CREATE TABLE IF NOT EXISTS events (
id INTEGER PRIMARY KEY AUTOINCREMENT,
occurred_at INTEGER NOT NULL,
kind TEXT NOT NULL,
key TEXT,
hash TEXT,
code_rev TEXT,
meta TEXT,
object_id INTEGER REFERENCES objects(id)
);
CREATE INDEX IF NOT EXISTS idx_occurred ON events(occurred_at);
CREATE INDEX IF NOT EXISTS idx_kind_key ON events(kind, key, occurred_at);
CREATE INDEX IF NOT EXISTS idx_code_rev ON events(code_rev, occurred_at);
-- Objects table
CREATE TABLE IF NOT EXISTS objects (
id INTEGER PRIMARY KEY AUTOINCREMENT,
object_type TEXT NOT NULL,
external_id TEXT,
created_at INTEGER NOT NULL,
code_rev TEXT NOT NULL,
UNIQUE(object_type, external_id)
);
-- Projections table (from projection-engine.js)
CREATE TABLE IF NOT EXISTS projections (
name TEXT PRIMARY KEY,
last_event_id INTEGER NOT NULL DEFAULT 0
);
-- Defs tables (from defs.js)
CREATE TABLE IF NOT EXISTS event_defs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
code_hash TEXT NOT NULL UNIQUE,
data TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS rule_defs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
code_hash TEXT NOT NULL UNIQUE,
data TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS executor_defs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
code_hash TEXT NOT NULL UNIQUE,
data TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS projection_defs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
code_hash TEXT NOT NULL UNIQUE,
data TEXT NOT NULL
);
+312
View File
@@ -0,0 +1,312 @@
import type { D1Database } from '@cloudflare/workers-types';
import type { EventRecord, ObjectInstance, PulseStore } from '@uncaged/pulse';
/**
* D1 implementation of PulseStore interface
* Adapts from bun:sqlite to Cloudflare D1
*/
export class D1PulseStore implements PulseStore {
constructor(private db: D1Database) {}
async appendEvent(event: Omit<EventRecord, 'id'>): Promise<EventRecord> {
const result = await this.db
.prepare(`
INSERT INTO events (occurred_at, kind, key, hash, code_rev, meta, object_id)
VALUES (?, ?, ?, ?, ?, ?, ?)
`)
.bind(
event.occurredAt,
event.kind,
event.key ?? null,
event.hash ?? null,
event.codeRev ?? null,
event.meta ?? null,
event.objectId ?? null,
)
.run();
const id = result.meta.last_row_id as number;
return { id, ...event };
}
async appendEvents(events: Omit<EventRecord, 'id'>[]): Promise<EventRecord[]> {
const statements = events.map(event =>
this.db
.prepare(`
INSERT INTO events (occurred_at, kind, key, hash, code_rev, meta, object_id)
VALUES (?, ?, ?, ?, ?, ?, ?)
`)
.bind(
event.occurredAt,
event.kind,
event.key ?? null,
event.hash ?? null,
event.codeRev ?? null,
event.meta ?? null,
event.objectId ?? null,
)
);
const results = await this.db.batch(statements);
return events.map((event, index) => ({
id: results[index].meta.last_row_id as number,
...event
}));
}
async createObject(opts: { objectType: string; externalId?: string; codeRev: string }): Promise<number> {
const now = Date.now();
const extId = opts.externalId ?? null;
// Idempotent: if (objectType, externalId) already exists, return existing id
if (extId !== null) {
const existing = await this.db
.prepare('SELECT id FROM objects WHERE object_type = ? AND external_id = ?')
.bind(opts.objectType, extId)
.first<{ id: number }>();
if (existing) return existing.id;
}
const result = await this.db
.prepare('INSERT INTO objects (object_type, external_id, created_at, code_rev) VALUES (?, ?, ?, ?)')
.bind(opts.objectType, extId, now, opts.codeRev)
.run();
return result.meta.last_row_id as number;
}
async getObjectInstance(id: number): Promise<ObjectInstance | null> {
const row = await this.db
.prepare('SELECT * FROM objects WHERE id = ?')
.bind(id)
.first<any>();
if (!row) return null;
return {
id: row.id,
objectType: row.object_type,
externalId: row.external_id,
createdAt: row.created_at,
codeRev: row.code_rev,
};
}
async queryObjectsByType(objectType: string): Promise<ObjectInstance[]> {
const { results } = await this.db
.prepare('SELECT * FROM objects WHERE object_type = ?')
.bind(objectType)
.all<any>();
return results.map(row => ({
id: row.id,
objectType: row.object_type,
externalId: row.external_id,
createdAt: row.created_at,
codeRev: row.code_rev,
}));
}
async getLatest(kind: string, key?: string): Promise<EventRecord | null> {
const row = await this.db
.prepare(`
SELECT * FROM events
WHERE kind = ? AND (key = ? OR ? IS NULL)
ORDER BY occurred_at DESC, id DESC
LIMIT 1
`)
.bind(kind, key ?? null, key ?? null)
.first<any>();
return row ? this.rowToEventRecord(row) : null;
}
async getLatestWhere(opts: { kind: string; key?: string; codeRev?: string }): Promise<EventRecord | null> {
const conditions: string[] = ['kind = ?'];
const params: (string | number | null)[] = [opts.kind];
if (opts.key !== undefined) {
conditions.push('key = ?');
params.push(opts.key);
}
if (opts.codeRev !== undefined) {
conditions.push('code_rev = ?');
params.push(opts.codeRev);
}
const sql = `SELECT * FROM events WHERE ${conditions.join(' AND ')} ORDER BY occurred_at DESC, id DESC LIMIT 1`;
let stmt = this.db.prepare(sql);
for (const param of params) {
stmt = stmt.bind(param);
}
const row = await stmt.first<any>();
return row ? this.rowToEventRecord(row) : null;
}
async getRecent(limit: number = 20): Promise<EventRecord[]> {
const { results } = await this.db
.prepare(`SELECT * FROM events ORDER BY occurred_at DESC, id DESC LIMIT ?`)
.bind(limit)
.all<any>();
return results.map(row => this.rowToEventRecord(row));
}
async queryByKind(
kind: string,
opts?: {
key?: string;
since?: number;
codeRev?: string;
limit?: number;
},
): Promise<EventRecord[]> {
const conditions: string[] = ['kind = ?'];
const params: (string | number | null)[] = [kind];
if (opts?.key !== undefined) {
conditions.push('key = ?');
params.push(opts.key);
}
if (opts?.since !== undefined) {
conditions.push('occurred_at >= ?');
params.push(opts.since);
}
if (opts?.codeRev !== undefined) {
conditions.push('code_rev = ?');
params.push(opts.codeRev);
}
let sql = `SELECT * FROM events WHERE ${conditions.join(' AND ')} ORDER BY occurred_at DESC, id DESC`;
if (opts?.limit !== undefined) {
sql += ' LIMIT ?';
params.push(opts.limit);
}
let stmt = this.db.prepare(sql);
for (const param of params) {
stmt = stmt.bind(param);
}
const { results } = await stmt.all<any>();
return results.map(row => this.rowToEventRecord(row));
}
async getAfter(
afterId: number,
opts?: {
kind?: string;
key?: string;
codeRev?: string;
},
): Promise<EventRecord[]> {
const conditions: string[] = ['id > ?'];
const params: (string | number | null)[] = [afterId];
if (opts?.kind !== undefined) {
conditions.push('kind = ?');
params.push(opts.kind);
}
if (opts?.key !== undefined) {
conditions.push('key = ?');
params.push(opts.key);
}
if (opts?.codeRev !== undefined) {
conditions.push('code_rev = ?');
params.push(opts.codeRev);
}
const sql = `SELECT * FROM events WHERE ${conditions.join(' AND ')} ORDER BY id ASC`;
let stmt = this.db.prepare(sql);
for (const param of params) {
stmt = stmt.bind(param);
}
const { results } = await stmt.all<any>();
return results.map(row => this.rowToEventRecord(row));
}
async hasEvents(): Promise<boolean> {
const result = await this.db
.prepare('SELECT 1 FROM events LIMIT 1')
.first();
return result !== null;
}
async putObject(data: unknown): Promise<string> {
// For simplicity, use a basic hash function
const hash = await this.hashObject(data);
// Store in D1 as a simple key-value table (could be added to schema later)
// For now, just return the hash - actual CAS implementation would need object storage
return hash;
}
async getObject(hash: string): Promise<unknown | null> {
// Placeholder - would need object storage or D1 table for CAS
return null;
}
async close(): Promise<void> {
// D1 doesn't need explicit closing
}
async archiveEvents(olderThan: number): Promise<number> {
const result = await this.db
.prepare('DELETE FROM events WHERE occurred_at < ?')
.bind(olderThan)
.run();
return result.meta.changes;
}
async downsampleEvents(
kind: string,
key: string,
intervalMs: number,
olderThan: number,
): Promise<number> {
const safeInterval = Math.floor(Math.abs(intervalMs));
if (safeInterval <= 0) return 0;
const result = await this.db
.prepare(`
DELETE FROM events WHERE kind = ? AND key = ? AND occurred_at < ? AND id NOT IN (
SELECT id FROM (
SELECT id, ROW_NUMBER() OVER (
PARTITION BY (occurred_at / ${safeInterval}) ORDER BY occurred_at DESC
) as rn FROM events WHERE kind = ? AND key = ? AND occurred_at < ?
) WHERE rn = 1
)
`)
.bind(kind, key, olderThan, kind, key, olderThan)
.run();
return result.meta.changes;
}
private rowToEventRecord(row: any): EventRecord {
const rec: EventRecord = {
id: row.id,
occurredAt: row.occurred_at,
kind: row.kind,
};
if (row.key != null) rec.key = row.key;
if (row.hash != null) rec.hash = row.hash;
if (row.code_rev != null) rec.codeRev = row.code_rev;
if (row.meta != null) rec.meta = row.meta;
if (row.object_id != null) rec.objectId = row.object_id;
return rec;
}
private async hashObject(data: unknown): Promise<string> {
const str = JSON.stringify(data);
const msgUint8 = new TextEncoder().encode(str);
const hashBuffer = await crypto.subtle.digest('SHA-256', msgUint8);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('').slice(0, 32);
}
}
+17
View File
@@ -0,0 +1,17 @@
{
"compilerOptions": {
"target": "ES2020",
"lib": ["ES2020", "WebWorker"],
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"types": ["@cloudflare/workers-types"]
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
+20
View File
@@ -0,0 +1,20 @@
name = "pulseflare"
main = "src/index.ts"
compatibility_date = "2024-09-25"
[[d1_databases]]
binding = "DB"
database_name = "pulseflare-db"
database_id = "placeholder-fill-later"
[durable_objects]
bindings = [
{ name = "PULSE_TICK", class_name = "PulseTick" }
]
[[migrations]]
tag = "v1"
new_classes = ["PulseTick"]
[vars]
SIGIL_URL = "https://sigil.shazhou.workers.dev"