@lightupai/polaris 0.0.57 → 0.0.58
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Makefile +21 -2
- package/README.md +22 -0
- package/bun.lock +146 -0
- package/docker/Dockerfile +3 -0
- package/docs/audits/perf-audit-2026-06-18-local.json +22 -0
- package/docs/audits/perf-audit-2026-06-18.json +22 -0
- package/docs/audits/seo-audit-2026-06-18.json +189 -0
- package/docs/lighthouse-baseline-2026-06-18.md +42 -0
- package/package.json +4 -2
- package/scripts/perf-audit.ts +212 -0
- package/scripts/seo-audit.ts +154 -0
- package/src/web/app.ts +9 -0
- package/src/web/layout.ts +1 -12
- package/src/web/styles/input.css +16 -0
package/Makefile
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
.PHONY: dev dev-up dev-down api web daemon bridge test clean prod
|
|
1
|
+
.PHONY: dev dev-up dev-down api web daemon bridge test perf seo css css-watch clean prod
|
|
2
2
|
|
|
3
3
|
# Load .env if it exists
|
|
4
4
|
ifneq (,$(wildcard .env))
|
|
@@ -6,8 +6,15 @@ ifneq (,$(wildcard .env))
|
|
|
6
6
|
export
|
|
7
7
|
endif
|
|
8
8
|
|
|
9
|
+
# Build purged Tailwind CSS
|
|
10
|
+
css:
|
|
11
|
+
@npx bun x tailwindcss -i src/web/styles/input.css -o src/web/styles/output.css --minify
|
|
12
|
+
|
|
13
|
+
css-watch:
|
|
14
|
+
@npx bun x tailwindcss -i src/web/styles/input.css -o src/web/styles/output.css --watch
|
|
15
|
+
|
|
9
16
|
# Start everything for local development
|
|
10
|
-
dev: dev-up api web daemon bridge
|
|
17
|
+
dev: dev-up css api web daemon bridge
|
|
11
18
|
|
|
12
19
|
# Postgres
|
|
13
20
|
dev-up:
|
|
@@ -62,6 +69,18 @@ prod:
|
|
|
62
69
|
test:
|
|
63
70
|
npx bun test
|
|
64
71
|
|
|
72
|
+
# Lighthouse performance audit against production and local
|
|
73
|
+
perf:
|
|
74
|
+
@prod_failed=0; \
|
|
75
|
+
npx bun run scripts/perf-audit.ts https://app.withpolaris.ai || prod_failed=1; \
|
|
76
|
+
npx bun run scripts/perf-audit.ts local || exit 1; \
|
|
77
|
+
if [ "$$prod_failed" = "1" ]; then exit 1; fi
|
|
78
|
+
|
|
79
|
+
# DataForSEO on-page SEO audit against production
|
|
80
|
+
SEO_URL ?= https://app.withpolaris.ai
|
|
81
|
+
seo:
|
|
82
|
+
@npx bun run scripts/seo-audit.ts $(SEO_URL)
|
|
83
|
+
|
|
65
84
|
# Stop all background processes, tunnels, and Postgres
|
|
66
85
|
clean:
|
|
67
86
|
@lsof -ti :4321 | xargs kill -9 2>/dev/null || true
|
package/README.md
CHANGED
|
@@ -202,6 +202,28 @@ tests/ Test suite (bun test)
|
|
|
202
202
|
- [ ] Update available indicator — daemon periodically checks npm for newer version, caches the result. Status line shows "update available" when stale. `polaris update` command installs the latest version and rewrites skill/hooks.
|
|
203
203
|
- [ ] Slack channel name collision — if a channel name was previously deleted, Slack reserves it. Bridge should handle `name_taken` by trying a prefix/suffix (e.g., `p-project-name`)
|
|
204
204
|
|
|
205
|
+
## Testing
|
|
206
|
+
|
|
207
|
+
```sh
|
|
208
|
+
# Unit tests (uses polaris_test database)
|
|
209
|
+
make test
|
|
210
|
+
|
|
211
|
+
# Lighthouse performance audit against production
|
|
212
|
+
# Runs mobile + desktop, checks budgets (score >= 90, FCP <= 1.8s, LCP <= 2.5s)
|
|
213
|
+
# Saves results to docs/audits/perf-audit-YYYY-MM-DD.json
|
|
214
|
+
make perf
|
|
215
|
+
|
|
216
|
+
# DataForSEO on-page SEO audit against production
|
|
217
|
+
# Checks meta tags, headings, social tags, content rate, technical SEO
|
|
218
|
+
# Saves results to docs/audits/seo-audit-YYYY-MM-DD.json
|
|
219
|
+
# Requires DataForSEO API credentials (see scripts/seo-audit.ts)
|
|
220
|
+
make seo
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
All three targets exit non-zero on failure. `make perf` and `make seo` run against the live production site (`app.withpolaris.ai`) by default. Override with `make perf PERF_URL=http://localhost:3000` or `make seo SEO_URL=http://localhost:3000`.
|
|
224
|
+
|
|
225
|
+
Audit results are saved as JSON in `docs/audits/` for historical tracking.
|
|
226
|
+
|
|
205
227
|
## Development
|
|
206
228
|
|
|
207
229
|
Services run as background processes. Logs go to `/tmp/polaris-*.log`. The Makefile's `clean` target kills all processes and stops Postgres.
|
package/bun.lock
CHANGED
|
@@ -15,13 +15,25 @@
|
|
|
15
15
|
"zod": "^3.23.0",
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
|
+
"@tailwindcss/cli": "^4.3.1",
|
|
18
19
|
"@types/bun": "latest",
|
|
20
|
+
"tailwindcss": "^4.3.1",
|
|
19
21
|
},
|
|
20
22
|
},
|
|
21
23
|
},
|
|
22
24
|
"packages": {
|
|
23
25
|
"@hono/node-server": ["@hono/node-server@1.19.14", "", { "peerDependencies": { "hono": "^4" } }, "sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw=="],
|
|
24
26
|
|
|
27
|
+
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
|
|
28
|
+
|
|
29
|
+
"@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
|
|
30
|
+
|
|
31
|
+
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
|
|
32
|
+
|
|
33
|
+
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
|
|
34
|
+
|
|
35
|
+
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
|
36
|
+
|
|
25
37
|
"@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.29.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1", "zod": "^3.25 || ^4.0" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ=="],
|
|
26
38
|
|
|
27
39
|
"@oslojs/asn1": ["@oslojs/asn1@1.0.0", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="],
|
|
@@ -34,6 +46,34 @@
|
|
|
34
46
|
|
|
35
47
|
"@oslojs/jwt": ["@oslojs/jwt@0.2.0", "", { "dependencies": { "@oslojs/encoding": "0.4.1" } }, "sha512-bLE7BtHrURedCn4Mco3ma9L4Y1GR2SMBuIvjWr7rmQ4/W/4Jy70TIAgZ+0nIlk0xHz1vNP8x8DCns45Sb2XRbg=="],
|
|
36
48
|
|
|
49
|
+
"@parcel/watcher": ["@parcel/watcher@2.5.1", "", { "dependencies": { "detect-libc": "^1.0.3", "is-glob": "^4.0.3", "micromatch": "^4.0.5", "node-addon-api": "^7.0.0" }, "optionalDependencies": { "@parcel/watcher-android-arm64": "2.5.1", "@parcel/watcher-darwin-arm64": "2.5.1", "@parcel/watcher-darwin-x64": "2.5.1", "@parcel/watcher-freebsd-x64": "2.5.1", "@parcel/watcher-linux-arm-glibc": "2.5.1", "@parcel/watcher-linux-arm-musl": "2.5.1", "@parcel/watcher-linux-arm64-glibc": "2.5.1", "@parcel/watcher-linux-arm64-musl": "2.5.1", "@parcel/watcher-linux-x64-glibc": "2.5.1", "@parcel/watcher-linux-x64-musl": "2.5.1", "@parcel/watcher-win32-arm64": "2.5.1", "@parcel/watcher-win32-ia32": "2.5.1", "@parcel/watcher-win32-x64": "2.5.1" } }, "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg=="],
|
|
50
|
+
|
|
51
|
+
"@parcel/watcher-android-arm64": ["@parcel/watcher-android-arm64@2.5.1", "", { "os": "android", "cpu": "arm64" }, "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA=="],
|
|
52
|
+
|
|
53
|
+
"@parcel/watcher-darwin-arm64": ["@parcel/watcher-darwin-arm64@2.5.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw=="],
|
|
54
|
+
|
|
55
|
+
"@parcel/watcher-darwin-x64": ["@parcel/watcher-darwin-x64@2.5.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg=="],
|
|
56
|
+
|
|
57
|
+
"@parcel/watcher-freebsd-x64": ["@parcel/watcher-freebsd-x64@2.5.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ=="],
|
|
58
|
+
|
|
59
|
+
"@parcel/watcher-linux-arm-glibc": ["@parcel/watcher-linux-arm-glibc@2.5.1", "", { "os": "linux", "cpu": "arm" }, "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA=="],
|
|
60
|
+
|
|
61
|
+
"@parcel/watcher-linux-arm-musl": ["@parcel/watcher-linux-arm-musl@2.5.1", "", { "os": "linux", "cpu": "arm" }, "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q=="],
|
|
62
|
+
|
|
63
|
+
"@parcel/watcher-linux-arm64-glibc": ["@parcel/watcher-linux-arm64-glibc@2.5.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w=="],
|
|
64
|
+
|
|
65
|
+
"@parcel/watcher-linux-arm64-musl": ["@parcel/watcher-linux-arm64-musl@2.5.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg=="],
|
|
66
|
+
|
|
67
|
+
"@parcel/watcher-linux-x64-glibc": ["@parcel/watcher-linux-x64-glibc@2.5.1", "", { "os": "linux", "cpu": "x64" }, "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A=="],
|
|
68
|
+
|
|
69
|
+
"@parcel/watcher-linux-x64-musl": ["@parcel/watcher-linux-x64-musl@2.5.1", "", { "os": "linux", "cpu": "x64" }, "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg=="],
|
|
70
|
+
|
|
71
|
+
"@parcel/watcher-win32-arm64": ["@parcel/watcher-win32-arm64@2.5.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw=="],
|
|
72
|
+
|
|
73
|
+
"@parcel/watcher-win32-ia32": ["@parcel/watcher-win32-ia32@2.5.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ=="],
|
|
74
|
+
|
|
75
|
+
"@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.5.1", "", { "os": "win32", "cpu": "x64" }, "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA=="],
|
|
76
|
+
|
|
37
77
|
"@slack/logger": ["@slack/logger@4.0.1", "", { "dependencies": { "@types/node": ">=18" } }, "sha512-6cmdPrV/RYfd2U0mDGiMK8S7OJqpCTm7enMLRR3edccsPX8j7zXTLnaEF4fhxxJJTAIOil6+qZrnUPTuaLvwrQ=="],
|
|
38
78
|
|
|
39
79
|
"@slack/socket-mode": ["@slack/socket-mode@2.0.7", "", { "dependencies": { "@slack/logger": "^4.0.1", "@slack/web-api": "^7.15.0", "@types/node": ">=18", "@types/ws": "^8", "eventemitter3": "^5", "ws": "^8" } }, "sha512-qYy07je71WnEHgRwmw12DlAnZLi5HXmdlI2WUzUK2LH/rYXQpP6uEg462S5CwfE8FoCKUdIigHtYnOOfzZH1lQ=="],
|
|
@@ -42,6 +82,36 @@
|
|
|
42
82
|
|
|
43
83
|
"@slack/web-api": ["@slack/web-api@7.16.0", "", { "dependencies": { "@slack/logger": "^4.0.1", "@slack/types": "^2.21.0", "@types/node": ">=18", "@types/retry": "0.12.0", "axios": "^1.16.0", "eventemitter3": "^5.0.1", "form-data": "^4.0.4", "is-electron": "2.2.2", "is-stream": "^2", "p-queue": "^6", "p-retry": "^4", "retry": "^0.13.1" } }, "sha512-68SAV77uuGKuhyyaRytX8UijVnqSLsTSKslGXw17cjQYXn+jtNl7gbaEjHgC5x2rhCuFdahBrEC2VCLppbzReg=="],
|
|
44
84
|
|
|
85
|
+
"@tailwindcss/cli": ["@tailwindcss/cli@4.3.1", "", { "dependencies": { "@parcel/watcher": "2.5.1", "@tailwindcss/node": "4.3.1", "@tailwindcss/oxide": "4.3.1", "enhanced-resolve": "5.21.6", "mri": "^1.2.0", "picocolors": "^1.1.1", "tailwindcss": "4.3.1" }, "bin": { "tailwindcss": "dist/index.mjs" } }, "sha512-ZWPy20rF+TBfTImxDMG3Wr75Y3RpaPlo9lc+oJbInlMyjT+XPkTVKVIL5RZ7JirXuIahcfHoLNFRmDorKi+JQQ=="],
|
|
86
|
+
|
|
87
|
+
"@tailwindcss/node": ["@tailwindcss/node@4.3.1", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "5.21.6", "jiti": "^2.7.0", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.3.1" } }, "sha512-6NDaqRoAMSXD1mr/RXu0HBvNE9a2n5tHPsxu9XHLws8o4Twes5rBM2205SUUiJ9goAtadrN6xTGX0UDEwp/N4A=="],
|
|
88
|
+
|
|
89
|
+
"@tailwindcss/oxide": ["@tailwindcss/oxide@4.3.1", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.3.1", "@tailwindcss/oxide-darwin-arm64": "4.3.1", "@tailwindcss/oxide-darwin-x64": "4.3.1", "@tailwindcss/oxide-freebsd-x64": "4.3.1", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.1", "@tailwindcss/oxide-linux-arm64-gnu": "4.3.1", "@tailwindcss/oxide-linux-arm64-musl": "4.3.1", "@tailwindcss/oxide-linux-x64-gnu": "4.3.1", "@tailwindcss/oxide-linux-x64-musl": "4.3.1", "@tailwindcss/oxide-wasm32-wasi": "4.3.1", "@tailwindcss/oxide-win32-arm64-msvc": "4.3.1", "@tailwindcss/oxide-win32-x64-msvc": "4.3.1" } }, "sha512-yVPyo8RNkabVr3O2EhHEE0Rewu7YKzc1DhIqfL46LKveFrmu9XbDazNOJY7/GRuvw1h6u3utWnR29H/p5JPlgA=="],
|
|
90
|
+
|
|
91
|
+
"@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.3.1", "", { "os": "android", "cpu": "arm64" }, "sha512-SVlyf61g374l5cHyg8x9kf5xmLcOaxvOTsbsqDnSsDJaKOEFZ7GCvi84VAVGpxojYOs1+3K6M0UjXfqPU8vmOQ=="],
|
|
92
|
+
|
|
93
|
+
"@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.3.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-hVnWLwv+e/l7c4WKyVtHVrIPvYdqWHjRB3MDIqARynzFtnQg85kmQEFCbV9Ja0VVx4xXTIiDWY60Y7iz/iNoDA=="],
|
|
94
|
+
|
|
95
|
+
"@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.3.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-Cf7abu0WVgbhU7ANgPUnSAvm7nCvMweusHb8FnaHlLfv/Caq4GYaEZg7ZImzzmjx4lIAfuS8q+eLIS7A7IzxIg=="],
|
|
96
|
+
|
|
97
|
+
"@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.3.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-ZZqzX2Y+GXtXXfqSfpJhDm60OoZfvLHLCgm+J7NVqgHHJjG/m9ugZI77RwTsVd4fnBJuCFP6Ae6kTJb71UdS8g=="],
|
|
98
|
+
|
|
99
|
+
"@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.3.1", "", { "os": "linux", "cpu": "arm" }, "sha512-/Ah/xik0LaMYfv9DZ0S/t4pBlBNYOcqtRwusjgovHkvT8ixueWCLyJjsaF5kQIckjb4IT8Q6K6p/iPmZMixYgg=="],
|
|
100
|
+
|
|
101
|
+
"@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.3.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-gqdFoVJlw444GvpnheZLHmvTzSxI/cOUUh2KSNejQjTcYkW062SVD+En0rUgD+QV91bz1XGIGtt1HJd48xUGbQ=="],
|
|
102
|
+
|
|
103
|
+
"@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.3.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-Bwv9KwOvE0VKa86xPFif9b9c3Y1NxOV1P0gLti/IYaWEsQYZXDlxfGEtA8mdDZ7SG3wyNXAWYT5SIn3giL57oA=="],
|
|
104
|
+
|
|
105
|
+
"@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.3.1", "", { "os": "linux", "cpu": "x64" }, "sha512-Ymi8O8T15HYQdOUWUtTI6ldN0neHP85FC+Qz32xTcZ7iJXtem/x8ITev0o1e9e5rkqj4lONZfTRLvkmin1+tKg=="],
|
|
106
|
+
|
|
107
|
+
"@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.3.1", "", { "os": "linux", "cpu": "x64" }, "sha512-M+P/91qJ6uILLw4k2G93GMDRAXj61SMvFQYt39AqvUqYgExXpLL5aepfns7sj4HiAQeolirQF9E0lzRvdf4zPQ=="],
|
|
108
|
+
|
|
109
|
+
"@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.3.1", "", { "dependencies": { "@emnapi/core": "^1.10.0", "@emnapi/runtime": "^1.10.0", "@emnapi/wasi-threads": "^1.2.1", "@napi-rs/wasm-runtime": "^1.1.4", "@tybys/wasm-util": "^0.10.2", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-zsM8uOeqvVGHsAXsJxsT28ttosFahLJKCLOTUBqRAtKnVgGSRitds9T432QiT8b77Yga7JIBkulIRRlJPtYhRA=="],
|
|
110
|
+
|
|
111
|
+
"@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.3.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-aiNvSq9BsVk8V513lDKlrCFAgf8qBMPZTpgEhInL+NwQqs97mYmupVMrPrgBBSL8Pv/0zXu9MrMF9rMun1ZeNg=="],
|
|
112
|
+
|
|
113
|
+
"@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.3.1", "", { "os": "win32", "cpu": "x64" }, "sha512-xDEyu1rg290472FEGaKHnzyDyh5QH+AlWvsU5hMoMtPpzmKlRI0jaYKCgSHDYtaQWZOYbMaduSyCwFwY4n1HmA=="],
|
|
114
|
+
|
|
45
115
|
"@types/bun": ["@types/bun@1.3.14", "", { "dependencies": { "bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="],
|
|
46
116
|
|
|
47
117
|
"@types/node": ["@types/node@25.9.1", "", { "dependencies": { "undici-types": ">=7.24.0 <7.24.7" } }, "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg=="],
|
|
@@ -66,6 +136,8 @@
|
|
|
66
136
|
|
|
67
137
|
"body-parser": ["body-parser@2.2.2", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="],
|
|
68
138
|
|
|
139
|
+
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
|
|
140
|
+
|
|
69
141
|
"bun-types": ["bun-types@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="],
|
|
70
142
|
|
|
71
143
|
"bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
|
|
@@ -94,12 +166,16 @@
|
|
|
94
166
|
|
|
95
167
|
"depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="],
|
|
96
168
|
|
|
169
|
+
"detect-libc": ["detect-libc@1.0.3", "", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="],
|
|
170
|
+
|
|
97
171
|
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
|
|
98
172
|
|
|
99
173
|
"ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
|
|
100
174
|
|
|
101
175
|
"encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="],
|
|
102
176
|
|
|
177
|
+
"enhanced-resolve": ["enhanced-resolve@5.21.6", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ=="],
|
|
178
|
+
|
|
103
179
|
"es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
|
|
104
180
|
|
|
105
181
|
"es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
|
|
@@ -126,6 +202,8 @@
|
|
|
126
202
|
|
|
127
203
|
"fast-uri": ["fast-uri@3.1.2", "", {}, "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ=="],
|
|
128
204
|
|
|
205
|
+
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
|
|
206
|
+
|
|
129
207
|
"finalhandler": ["finalhandler@2.1.1", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="],
|
|
130
208
|
|
|
131
209
|
"follow-redirects": ["follow-redirects@1.16.0", "", {}, "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw=="],
|
|
@@ -144,6 +222,8 @@
|
|
|
144
222
|
|
|
145
223
|
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
|
|
146
224
|
|
|
225
|
+
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
|
|
226
|
+
|
|
147
227
|
"has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
|
|
148
228
|
|
|
149
229
|
"has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
|
|
@@ -166,32 +246,72 @@
|
|
|
166
246
|
|
|
167
247
|
"is-electron": ["is-electron@2.2.2", "", {}, "sha512-FO/Rhvz5tuw4MCWkpMzHFKWD2LsfHzIb7i6MdPYZ/KW7AlxawyLkqdy+jPZP1WubqEADE3O4FUENlJHDfQASRg=="],
|
|
168
248
|
|
|
249
|
+
"is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
|
|
250
|
+
|
|
251
|
+
"is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
|
|
252
|
+
|
|
253
|
+
"is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
|
|
254
|
+
|
|
169
255
|
"is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="],
|
|
170
256
|
|
|
171
257
|
"is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
|
|
172
258
|
|
|
173
259
|
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
|
174
260
|
|
|
261
|
+
"jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="],
|
|
262
|
+
|
|
175
263
|
"jose": ["jose@6.2.3", "", {}, "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw=="],
|
|
176
264
|
|
|
177
265
|
"json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
|
|
178
266
|
|
|
179
267
|
"json-schema-typed": ["json-schema-typed@8.0.2", "", {}, "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA=="],
|
|
180
268
|
|
|
269
|
+
"lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="],
|
|
270
|
+
|
|
271
|
+
"lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="],
|
|
272
|
+
|
|
273
|
+
"lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="],
|
|
274
|
+
|
|
275
|
+
"lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="],
|
|
276
|
+
|
|
277
|
+
"lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.32.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig=="],
|
|
278
|
+
|
|
279
|
+
"lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.32.0", "", { "os": "linux", "cpu": "arm" }, "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw=="],
|
|
280
|
+
|
|
281
|
+
"lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ=="],
|
|
282
|
+
|
|
283
|
+
"lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg=="],
|
|
284
|
+
|
|
285
|
+
"lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA=="],
|
|
286
|
+
|
|
287
|
+
"lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg=="],
|
|
288
|
+
|
|
289
|
+
"lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.32.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw=="],
|
|
290
|
+
|
|
291
|
+
"lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="],
|
|
292
|
+
|
|
293
|
+
"magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
|
|
294
|
+
|
|
181
295
|
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
|
|
182
296
|
|
|
183
297
|
"media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="],
|
|
184
298
|
|
|
185
299
|
"merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="],
|
|
186
300
|
|
|
301
|
+
"micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
|
|
302
|
+
|
|
187
303
|
"mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
|
|
188
304
|
|
|
189
305
|
"mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="],
|
|
190
306
|
|
|
307
|
+
"mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="],
|
|
308
|
+
|
|
191
309
|
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
|
192
310
|
|
|
193
311
|
"negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
|
|
194
312
|
|
|
313
|
+
"node-addon-api": ["node-addon-api@7.1.1", "", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="],
|
|
314
|
+
|
|
195
315
|
"object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
|
|
196
316
|
|
|
197
317
|
"object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="],
|
|
@@ -214,6 +334,10 @@
|
|
|
214
334
|
|
|
215
335
|
"path-to-regexp": ["path-to-regexp@8.4.2", "", {}, "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA=="],
|
|
216
336
|
|
|
337
|
+
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
|
338
|
+
|
|
339
|
+
"picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
|
|
340
|
+
|
|
217
341
|
"pkce-challenge": ["pkce-challenge@5.0.1", "", {}, "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ=="],
|
|
218
342
|
|
|
219
343
|
"postgres": ["postgres@3.4.9", "", {}, "sha512-GD3qdB0x1z9xgFI6cdRD6xu2Sp2WCOEoe3mtnyB5Ee0XrrL5Pe+e4CCnJrRMnL1zYtRDZmQQVbvOttLnKDLnaw=="],
|
|
@@ -254,8 +378,16 @@
|
|
|
254
378
|
|
|
255
379
|
"side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="],
|
|
256
380
|
|
|
381
|
+
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
|
382
|
+
|
|
257
383
|
"statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="],
|
|
258
384
|
|
|
385
|
+
"tailwindcss": ["tailwindcss@4.3.1", "", {}, "sha512-hk+TB1m+K8CYNrP6rjQaq/Y+4Zylwpa87mLYBKCunwnnQ9p+fHb7kmSfGqyEJoxF/O6CDyABWVFEafNSYKll+Q=="],
|
|
386
|
+
|
|
387
|
+
"tapable": ["tapable@2.3.3", "", {}, "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A=="],
|
|
388
|
+
|
|
389
|
+
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
|
|
390
|
+
|
|
259
391
|
"toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
|
|
260
392
|
|
|
261
393
|
"type-is": ["type-is@2.1.0", "", { "dependencies": { "content-type": "^2.0.0", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA=="],
|
|
@@ -278,8 +410,22 @@
|
|
|
278
410
|
|
|
279
411
|
"@oslojs/jwt/@oslojs/encoding": ["@oslojs/encoding@0.4.1", "", {}, "sha512-hkjo6MuIK/kQR5CrGNdAPZhS01ZCXuWDRJ187zh6qqF2+yMHZpD9fAYpX8q2bOO6Ryhl3XpCT6kUX76N8hhm4Q=="],
|
|
280
412
|
|
|
413
|
+
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.11.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" }, "bundled": true }, "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ=="],
|
|
414
|
+
|
|
415
|
+
"@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.11.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw=="],
|
|
416
|
+
|
|
417
|
+
"@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA=="],
|
|
418
|
+
|
|
419
|
+
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.5", "", { "dependencies": { "@tybys/wasm-util": "^0.10.2" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" }, "bundled": true }, "sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q=="],
|
|
420
|
+
|
|
421
|
+
"@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg=="],
|
|
422
|
+
|
|
423
|
+
"@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
|
424
|
+
|
|
281
425
|
"form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
|
282
426
|
|
|
427
|
+
"lightningcss/detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
|
428
|
+
|
|
283
429
|
"p-queue/eventemitter3": ["eventemitter3@4.0.7", "", {}, "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="],
|
|
284
430
|
|
|
285
431
|
"type-is/content-type": ["content-type@2.0.0", "", {}, "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ=="],
|
package/docker/Dockerfile
CHANGED
|
@@ -9,5 +9,8 @@ RUN bun install --frozen-lockfile
|
|
|
9
9
|
COPY src/ ./src/
|
|
10
10
|
COPY docker/bridge-entrypoint.sh ./docker/
|
|
11
11
|
|
|
12
|
+
# Build Tailwind CSS
|
|
13
|
+
RUN bunx tailwindcss -i src/web/styles/input.css -o src/web/styles/output.css --minify
|
|
14
|
+
|
|
12
15
|
# Default: API server
|
|
13
16
|
CMD ["bun", "run", "src/service/server.ts"]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"url": "http://localhost:3000",
|
|
3
|
+
"timestamp": "2026-06-18T18:13:42.438Z",
|
|
4
|
+
"mobile": {
|
|
5
|
+
"score": 100,
|
|
6
|
+
"fcp": 1052.0306,
|
|
7
|
+
"lcp": 1277.0306,
|
|
8
|
+
"tbt": 0,
|
|
9
|
+
"cls": 0,
|
|
10
|
+
"si": 1052.0306,
|
|
11
|
+
"weight": 77990
|
|
12
|
+
},
|
|
13
|
+
"desktop": {
|
|
14
|
+
"score": 100,
|
|
15
|
+
"fcp": 281.5984,
|
|
16
|
+
"lcp": 321.5984,
|
|
17
|
+
"tbt": 0,
|
|
18
|
+
"cls": 0,
|
|
19
|
+
"si": 281.5984,
|
|
20
|
+
"weight": 77990
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"url": "https://app.withpolaris.ai",
|
|
3
|
+
"timestamp": "2026-06-18T17:52:26.077Z",
|
|
4
|
+
"mobile": {
|
|
5
|
+
"score": 84,
|
|
6
|
+
"fcp": 3411.0824999999995,
|
|
7
|
+
"lcp": 3411.0824999999995,
|
|
8
|
+
"tbt": 0,
|
|
9
|
+
"cls": 0,
|
|
10
|
+
"si": 3411.0824999999995,
|
|
11
|
+
"weight": 179565
|
|
12
|
+
},
|
|
13
|
+
"desktop": {
|
|
14
|
+
"score": 91,
|
|
15
|
+
"fcp": 1405.5195,
|
|
16
|
+
"lcp": 1405.5195,
|
|
17
|
+
"tbt": 0,
|
|
18
|
+
"cls": 0,
|
|
19
|
+
"si": 1405.5195,
|
|
20
|
+
"weight": 179543
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
{
|
|
2
|
+
"resource_type": "html",
|
|
3
|
+
"status_code": 200,
|
|
4
|
+
"location": null,
|
|
5
|
+
"url": "https://app.withpolaris.ai/",
|
|
6
|
+
"meta": {
|
|
7
|
+
"title": "Polaris — It's like Gong for Claude Code sessions",
|
|
8
|
+
"charset": 65001,
|
|
9
|
+
"follow": true,
|
|
10
|
+
"generator": null,
|
|
11
|
+
"htags": {
|
|
12
|
+
"h1": [
|
|
13
|
+
"Meet Polaris.\nIt's like Gong for Claude Code sessions."
|
|
14
|
+
],
|
|
15
|
+
"h2": [
|
|
16
|
+
"One channel, every session",
|
|
17
|
+
"What you can do with Polaris",
|
|
18
|
+
"Why Polaris",
|
|
19
|
+
"The vision",
|
|
20
|
+
"Pricing",
|
|
21
|
+
"How it works",
|
|
22
|
+
"Ready to try it?"
|
|
23
|
+
],
|
|
24
|
+
"h3": [
|
|
25
|
+
"Start streaming your session",
|
|
26
|
+
"Pull a teammate into your session",
|
|
27
|
+
"Catch up on a teammate's session",
|
|
28
|
+
"Redirect an agent from Slack",
|
|
29
|
+
"Attach session context to a PR",
|
|
30
|
+
"Search past sessions",
|
|
31
|
+
"AI sessions are invisible",
|
|
32
|
+
"Knowledge evaporates",
|
|
33
|
+
"Feedback comes too late",
|
|
34
|
+
"Multiple agents, zero shared context",
|
|
35
|
+
"Learning on the Shop Floor",
|
|
36
|
+
"Free",
|
|
37
|
+
"Pro",
|
|
38
|
+
"Team",
|
|
39
|
+
"Enterprise",
|
|
40
|
+
"Connect",
|
|
41
|
+
"Install",
|
|
42
|
+
"Collaborate"
|
|
43
|
+
]
|
|
44
|
+
},
|
|
45
|
+
"description": "Capture every AI coding session. Stream prompts, responses, and tool calls to Slack in real time. Collaborate across agents. Nothing is lost.",
|
|
46
|
+
"favicon": null,
|
|
47
|
+
"meta_keywords": null,
|
|
48
|
+
"canonical": "https://app.withpolaris.ai/",
|
|
49
|
+
"internal_links_count": 7,
|
|
50
|
+
"external_links_count": 2,
|
|
51
|
+
"inbound_links_count": 0,
|
|
52
|
+
"images_count": 0,
|
|
53
|
+
"images_size": 0,
|
|
54
|
+
"scripts_count": 1,
|
|
55
|
+
"scripts_size": 0,
|
|
56
|
+
"stylesheets_count": 0,
|
|
57
|
+
"stylesheets_size": 0,
|
|
58
|
+
"title_length": 49,
|
|
59
|
+
"description_length": 141,
|
|
60
|
+
"render_blocking_scripts_count": 1,
|
|
61
|
+
"render_blocking_stylesheets_count": 0,
|
|
62
|
+
"cumulative_layout_shift": 0,
|
|
63
|
+
"meta_title": null,
|
|
64
|
+
"content": {
|
|
65
|
+
"plain_text_size": 5565,
|
|
66
|
+
"plain_text_rate": 0.0824627695043343,
|
|
67
|
+
"plain_text_word_count": 909,
|
|
68
|
+
"automated_readability_index": 8.19262715768782,
|
|
69
|
+
"coleman_liau_readability_index": 11.131434977578476,
|
|
70
|
+
"dale_chall_readability_index": 8.925914282204067,
|
|
71
|
+
"flesch_kincaid_readability_index": 55.14824037103017,
|
|
72
|
+
"smog_readability_index": 11.235764001479073,
|
|
73
|
+
"description_to_content_consistency": 1,
|
|
74
|
+
"title_to_content_consistency": 1,
|
|
75
|
+
"meta_keywords_to_content_consistency": null
|
|
76
|
+
},
|
|
77
|
+
"deprecated_tags": null,
|
|
78
|
+
"duplicate_meta_tags": null,
|
|
79
|
+
"spell": null,
|
|
80
|
+
"social_media_tags": {
|
|
81
|
+
"og:type": "website",
|
|
82
|
+
"og:title": "Polaris — It's like Gong for Claude Code sessions",
|
|
83
|
+
"og:description": "Capture every AI coding session. Stream prompts, responses, and tool calls to Slack in real time. Collaborate across agents. Nothing is lost.",
|
|
84
|
+
"og:image": "https://app.withpolaris.ai/og-image.png",
|
|
85
|
+
"og:url": "https://app.withpolaris.ai",
|
|
86
|
+
"twitter:card": "summary_large_image",
|
|
87
|
+
"twitter:title": "Polaris — It's like Gong for Claude Code sessions",
|
|
88
|
+
"twitter:description": "Capture every AI coding session. Stream prompts, responses, and tool calls to Slack in real time. Collaborate across agents. Nothing is lost.",
|
|
89
|
+
"twitter:image": "https://app.withpolaris.ai/og-image.png"
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
"page_timing": {
|
|
93
|
+
"time_to_interactive": 966,
|
|
94
|
+
"dom_complete": 966,
|
|
95
|
+
"largest_contentful_paint": 0,
|
|
96
|
+
"first_input_delay": 0,
|
|
97
|
+
"connection_time": 28,
|
|
98
|
+
"time_to_secure_connection": 16,
|
|
99
|
+
"request_sent_time": 0,
|
|
100
|
+
"waiting_time": 18,
|
|
101
|
+
"download_time": 9,
|
|
102
|
+
"duration_time": 967,
|
|
103
|
+
"fetch_start": 0,
|
|
104
|
+
"fetch_end": 967
|
|
105
|
+
},
|
|
106
|
+
"onpage_score": 100,
|
|
107
|
+
"total_dom_size": 67534,
|
|
108
|
+
"custom_js_response": null,
|
|
109
|
+
"custom_js_client_exception": null,
|
|
110
|
+
"resource_errors": {
|
|
111
|
+
"errors": null,
|
|
112
|
+
"warnings": null
|
|
113
|
+
},
|
|
114
|
+
"broken_resources": false,
|
|
115
|
+
"broken_links": false,
|
|
116
|
+
"duplicate_title": false,
|
|
117
|
+
"duplicate_description": false,
|
|
118
|
+
"duplicate_content": false,
|
|
119
|
+
"click_depth": 0,
|
|
120
|
+
"size": 67534,
|
|
121
|
+
"encoded_size": 51810,
|
|
122
|
+
"total_transfer_size": 51810,
|
|
123
|
+
"fetch_time": "2026-06-18 17:46:26 +00:00",
|
|
124
|
+
"cache_control": {
|
|
125
|
+
"cachable": false,
|
|
126
|
+
"ttl": 0
|
|
127
|
+
},
|
|
128
|
+
"checks": {
|
|
129
|
+
"no_content_encoding": true,
|
|
130
|
+
"high_loading_time": false,
|
|
131
|
+
"from_sitemap": false,
|
|
132
|
+
"is_redirect": false,
|
|
133
|
+
"is_4xx_code": false,
|
|
134
|
+
"is_5xx_code": false,
|
|
135
|
+
"is_broken": false,
|
|
136
|
+
"is_www": false,
|
|
137
|
+
"is_https": true,
|
|
138
|
+
"is_http": false,
|
|
139
|
+
"high_waiting_time": false,
|
|
140
|
+
"has_micromarkup": false,
|
|
141
|
+
"has_micromarkup_errors": false,
|
|
142
|
+
"no_doctype": false,
|
|
143
|
+
"has_html_doctype": true,
|
|
144
|
+
"canonical": true,
|
|
145
|
+
"no_encoding_meta_tag": false,
|
|
146
|
+
"no_h1_tag": false,
|
|
147
|
+
"https_to_http_links": false,
|
|
148
|
+
"size_greater_than_3mb": false,
|
|
149
|
+
"meta_charset_consistency": true,
|
|
150
|
+
"has_meta_refresh_redirect": false,
|
|
151
|
+
"has_render_blocking_resources": true,
|
|
152
|
+
"low_content_rate": true,
|
|
153
|
+
"high_content_rate": false,
|
|
154
|
+
"low_character_count": false,
|
|
155
|
+
"high_character_count": false,
|
|
156
|
+
"small_page_size": false,
|
|
157
|
+
"large_page_size": false,
|
|
158
|
+
"low_readability_rate": false,
|
|
159
|
+
"irrelevant_description": false,
|
|
160
|
+
"irrelevant_title": false,
|
|
161
|
+
"irrelevant_meta_keywords": false,
|
|
162
|
+
"title_too_long": false,
|
|
163
|
+
"has_meta_title": false,
|
|
164
|
+
"title_too_short": false,
|
|
165
|
+
"deprecated_html_tags": false,
|
|
166
|
+
"duplicate_meta_tags": false,
|
|
167
|
+
"duplicate_title_tag": false,
|
|
168
|
+
"no_image_alt": false,
|
|
169
|
+
"no_image_title": false,
|
|
170
|
+
"no_description": false,
|
|
171
|
+
"no_title": false,
|
|
172
|
+
"no_favicon": true,
|
|
173
|
+
"seo_friendly_url": true,
|
|
174
|
+
"flash": false,
|
|
175
|
+
"frame": false,
|
|
176
|
+
"lorem_ipsum": false,
|
|
177
|
+
"seo_friendly_url_characters_check": true,
|
|
178
|
+
"seo_friendly_url_dynamic_check": true,
|
|
179
|
+
"seo_friendly_url_keywords_check": true,
|
|
180
|
+
"seo_friendly_url_relative_length_check": true
|
|
181
|
+
},
|
|
182
|
+
"content_encoding": null,
|
|
183
|
+
"media_type": "text/html",
|
|
184
|
+
"server": null,
|
|
185
|
+
"is_resource": false,
|
|
186
|
+
"url_length": 27,
|
|
187
|
+
"relative_url_length": 1,
|
|
188
|
+
"last_modified": null
|
|
189
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Lighthouse Baseline — 2026-06-18
|
|
2
|
+
|
|
3
|
+
Captured before self-hosting Tailwind CSS (currently loaded via CDN).
|
|
4
|
+
|
|
5
|
+
## Mobile Performance
|
|
6
|
+
|
|
7
|
+
| Metric | Value | Score |
|
|
8
|
+
|--------------------------|-------|--------------|
|
|
9
|
+
| **Performance** | — | **85/100** |
|
|
10
|
+
| First Contentful Paint | 3.3s | 0.39 (poor) |
|
|
11
|
+
| Largest Contentful Paint | 3.3s | 0.69 (needs work) |
|
|
12
|
+
| Total Blocking Time | 0ms | 1.0 (perfect) |
|
|
13
|
+
| Cumulative Layout Shift | 0 | 1.0 (perfect) |
|
|
14
|
+
| Speed Index | 3.6s | 0.87 (good) |
|
|
15
|
+
|
|
16
|
+
## Desktop Performance
|
|
17
|
+
|
|
18
|
+
| Metric | Value | Score |
|
|
19
|
+
|--------------------------|-------|--------------|
|
|
20
|
+
| **Performance** | — | **90/100** |
|
|
21
|
+
| First Contentful Paint | 1.4s | 0.61 (needs work) |
|
|
22
|
+
| Largest Contentful Paint | 1.4s | 0.83 (good) |
|
|
23
|
+
| Total Blocking Time | 0ms | 1.0 (perfect) |
|
|
24
|
+
| Cumulative Layout Shift | 0 | 1.0 (perfect) |
|
|
25
|
+
| Speed Index | 1.4s | 0.86 (good) |
|
|
26
|
+
|
|
27
|
+
## Page Weight Breakdown
|
|
28
|
+
|
|
29
|
+
| Resource | Size | % of Total |
|
|
30
|
+
|-------------------------------|-----------|------------|
|
|
31
|
+
| `cdn.tailwindcss.com/3.4.17` | 127 KB | 72% |
|
|
32
|
+
| HTML document | 52 KB | 28% |
|
|
33
|
+
| **Total** | **175 KB**| 100% |
|
|
34
|
+
|
|
35
|
+
Total network requests: 5
|
|
36
|
+
|
|
37
|
+
## Key Finding
|
|
38
|
+
|
|
39
|
+
The Tailwind CDN script (127 KB) is the single largest resource and the
|
|
40
|
+
direct cause of the 3.3s FCP — the browser cannot paint until the script
|
|
41
|
+
downloads and executes. Switching to purged, self-hosted CSS should reduce
|
|
42
|
+
this to ~10 KB of static CSS.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lightupai/polaris",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.58",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"polaris": "bin/polaris",
|
|
@@ -20,6 +20,8 @@
|
|
|
20
20
|
"zod": "^3.23.0"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@
|
|
23
|
+
"@tailwindcss/cli": "^4.3.1",
|
|
24
|
+
"@types/bun": "latest",
|
|
25
|
+
"tailwindcss": "^4.3.1"
|
|
24
26
|
}
|
|
25
27
|
}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lighthouse performance audit.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* bun run scripts/perf-audit.ts [url]
|
|
6
|
+
*
|
|
7
|
+
* If url is http://localhost:*, builds CSS, starts the local web server,
|
|
8
|
+
* runs the audit, then stops the server.
|
|
9
|
+
*
|
|
10
|
+
* Runs mobile + desktop Lighthouse audits, prints a formatted report,
|
|
11
|
+
* checks performance budgets, and saves raw JSON to docs/audits/.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { mkdirSync, writeFileSync } from "fs";
|
|
15
|
+
import { execSync } from "child_process";
|
|
16
|
+
import { join } from "path";
|
|
17
|
+
|
|
18
|
+
let url = process.argv[2] ?? "https://app.withpolaris.ai";
|
|
19
|
+
const isLocal = url === "local";
|
|
20
|
+
let localServerPid: number | undefined;
|
|
21
|
+
|
|
22
|
+
// Find a free port by binding to :0 and releasing
|
|
23
|
+
async function freePort(): Promise<number> {
|
|
24
|
+
const server = Bun.serve({ port: 0, fetch: () => new Response() });
|
|
25
|
+
const port = server.port;
|
|
26
|
+
server.stop(true);
|
|
27
|
+
return port;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (isLocal) {
|
|
31
|
+
// Build CSS and start local web server on an ephemeral port
|
|
32
|
+
console.log("");
|
|
33
|
+
console.log(" Building CSS and starting local server ...");
|
|
34
|
+
execSync("npx bun x tailwindcss -i src/web/styles/input.css -o src/web/styles/output.css --minify", { stdio: "pipe" });
|
|
35
|
+
|
|
36
|
+
const port = await freePort();
|
|
37
|
+
url = `http://localhost:${port}`;
|
|
38
|
+
|
|
39
|
+
const proc = Bun.spawn(["bun", "run", "src/web/serve.ts"], {
|
|
40
|
+
env: { ...process.env, WEB_PORT: String(port), DATABASE_URL: process.env.DATABASE_URL ?? "postgres://polaris:polaris@localhost:5432/polaris" },
|
|
41
|
+
stdout: "pipe",
|
|
42
|
+
stderr: "pipe",
|
|
43
|
+
});
|
|
44
|
+
localServerPid = proc.pid;
|
|
45
|
+
|
|
46
|
+
// Wait for server to be ready
|
|
47
|
+
for (let i = 0; i < 30; i++) {
|
|
48
|
+
try {
|
|
49
|
+
await fetch(url);
|
|
50
|
+
break;
|
|
51
|
+
} catch {
|
|
52
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
console.log("");
|
|
58
|
+
console.log(` Running Lighthouse against ${url} ...`);
|
|
59
|
+
|
|
60
|
+
// --- Run Lighthouse ---
|
|
61
|
+
|
|
62
|
+
const tmpMobile = "/tmp/lighthouse-mobile.json";
|
|
63
|
+
const tmpDesktop = "/tmp/lighthouse-desktop.json";
|
|
64
|
+
|
|
65
|
+
execSync(
|
|
66
|
+
`npx --yes lighthouse ${url} --only-categories=performance --output=json --output-path=${tmpMobile} --chrome-flags="--headless --no-sandbox" 2>/dev/null`,
|
|
67
|
+
{ stdio: "pipe" },
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
execSync(
|
|
71
|
+
`npx lighthouse ${url} --only-categories=performance --preset=desktop --output=json --output-path=${tmpDesktop} --chrome-flags="--headless --no-sandbox" 2>/dev/null`,
|
|
72
|
+
{ stdio: "pipe" },
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
const m = JSON.parse(require("fs").readFileSync(tmpMobile, "utf-8"));
|
|
76
|
+
const d = JSON.parse(require("fs").readFileSync(tmpDesktop, "utf-8"));
|
|
77
|
+
|
|
78
|
+
// --- Extract metrics ---
|
|
79
|
+
|
|
80
|
+
function metric(report: any, key: string) {
|
|
81
|
+
return report.audits[key];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const metrics = [
|
|
85
|
+
{ key: "first-contentful-paint", label: "First Contentful Paint", unit: "s", divisor: 1000 },
|
|
86
|
+
{ key: "largest-contentful-paint", label: "Largest Contentful Paint", unit: "s", divisor: 1000 },
|
|
87
|
+
{ key: "total-blocking-time", label: "Total Blocking Time", unit: "ms", divisor: 1 },
|
|
88
|
+
{ key: "cumulative-layout-shift", label: "Cumulative Layout Shift", unit: "", divisor: 1 },
|
|
89
|
+
{ key: "speed-index", label: "Speed Index", unit: "s", divisor: 1000 },
|
|
90
|
+
{ key: "total-byte-weight", label: "Page weight", unit: "KB", divisor: 1024 },
|
|
91
|
+
];
|
|
92
|
+
|
|
93
|
+
const ms = m.categories.performance.score * 100;
|
|
94
|
+
const ds = d.categories.performance.score * 100;
|
|
95
|
+
|
|
96
|
+
// --- Save raw JSON ---
|
|
97
|
+
|
|
98
|
+
const now = new Date();
|
|
99
|
+
const stamp = now.toISOString().slice(0, 10);
|
|
100
|
+
const auditDir = join(import.meta.dir, "../docs/audits");
|
|
101
|
+
mkdirSync(auditDir, { recursive: true });
|
|
102
|
+
|
|
103
|
+
const summary = {
|
|
104
|
+
url,
|
|
105
|
+
timestamp: now.toISOString(),
|
|
106
|
+
mobile: {
|
|
107
|
+
score: ms,
|
|
108
|
+
fcp: metric(m, "first-contentful-paint").numericValue,
|
|
109
|
+
lcp: metric(m, "largest-contentful-paint").numericValue,
|
|
110
|
+
tbt: metric(m, "total-blocking-time").numericValue,
|
|
111
|
+
cls: metric(m, "cumulative-layout-shift").numericValue,
|
|
112
|
+
si: metric(m, "speed-index").numericValue,
|
|
113
|
+
weight: metric(m, "total-byte-weight").numericValue,
|
|
114
|
+
},
|
|
115
|
+
desktop: {
|
|
116
|
+
score: ds,
|
|
117
|
+
fcp: metric(d, "first-contentful-paint").numericValue,
|
|
118
|
+
lcp: metric(d, "largest-contentful-paint").numericValue,
|
|
119
|
+
tbt: metric(d, "total-blocking-time").numericValue,
|
|
120
|
+
cls: metric(d, "cumulative-layout-shift").numericValue,
|
|
121
|
+
si: metric(d, "speed-index").numericValue,
|
|
122
|
+
weight: metric(d, "total-byte-weight").numericValue,
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const suffix = isLocal ? "-local" : "";
|
|
127
|
+
const jsonPath = join(auditDir, `perf-audit-${stamp}${suffix}.json`);
|
|
128
|
+
writeFileSync(jsonPath, JSON.stringify(summary, null, 2) + "\n");
|
|
129
|
+
|
|
130
|
+
// --- Report ---
|
|
131
|
+
|
|
132
|
+
console.log("");
|
|
133
|
+
console.log(" Lighthouse Performance Audit");
|
|
134
|
+
console.log("");
|
|
135
|
+
console.log(" Metric Mobile Desktop Budget");
|
|
136
|
+
console.log(" ────────────────────────────────────────────────────────");
|
|
137
|
+
|
|
138
|
+
function fmt(val: number, unit: string, divisor: number): string {
|
|
139
|
+
if (unit === "KB") return Math.round(val / divisor) + " KB";
|
|
140
|
+
if (unit === "ms") return Math.round(val / divisor) + "ms";
|
|
141
|
+
if (unit === "") return (val / divisor).toFixed(3);
|
|
142
|
+
return (val / divisor).toFixed(1) + unit;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
console.log(
|
|
146
|
+
" Performance score "
|
|
147
|
+
+ String(ms).padStart(6) + " "
|
|
148
|
+
+ String(ds).padStart(6) + " >= 90"
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
for (const { key, label, unit, divisor } of metrics) {
|
|
152
|
+
const mv = metric(m, key).numericValue;
|
|
153
|
+
const dv = metric(d, key).numericValue;
|
|
154
|
+
const mStr = fmt(mv, unit, divisor).padStart(unit === "KB" ? 6 : 5);
|
|
155
|
+
const dStr = fmt(dv, unit, divisor).padStart(unit === "KB" ? 6 : 5);
|
|
156
|
+
|
|
157
|
+
let budget = "";
|
|
158
|
+
if (key === "first-contentful-paint") budget = "<= 1.8s";
|
|
159
|
+
if (key === "largest-contentful-paint") budget = "<= 2.5s";
|
|
160
|
+
if (key === "total-blocking-time") budget = "<= 200ms";
|
|
161
|
+
if (key === "cumulative-layout-shift") budget = "<= 0.100";
|
|
162
|
+
if (key === "speed-index") budget = "<= 3.4s";
|
|
163
|
+
|
|
164
|
+
const padLabel = (label + " ").padEnd(26);
|
|
165
|
+
console.log(` ${padLabel}${mStr} ${dStr} ${budget}`);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// --- Budget checks ---
|
|
169
|
+
|
|
170
|
+
type CheckResult = { label: string; pass: boolean; detail: string };
|
|
171
|
+
const results: CheckResult[] = [];
|
|
172
|
+
|
|
173
|
+
function check(label: string, pass: boolean, detail: string) {
|
|
174
|
+
results.push({ label, pass, detail });
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
check("Mobile score", ms >= 90, `${ms} (want >= 90)`);
|
|
178
|
+
check("Desktop score", ds >= 90, `${ds} (want >= 90)`);
|
|
179
|
+
check("Mobile FCP", summary.mobile.fcp <= 1800, `${(summary.mobile.fcp / 1000).toFixed(1)}s (want <= 1.8s)`);
|
|
180
|
+
check("Desktop FCP", summary.desktop.fcp <= 1800, `${(summary.desktop.fcp / 1000).toFixed(1)}s (want <= 1.8s)`);
|
|
181
|
+
check("Mobile LCP", summary.mobile.lcp <= 2500, `${(summary.mobile.lcp / 1000).toFixed(1)}s (want <= 2.5s)`);
|
|
182
|
+
check("Desktop LCP", summary.desktop.lcp <= 2500, `${(summary.desktop.lcp / 1000).toFixed(1)}s (want <= 2.5s)`);
|
|
183
|
+
check("Mobile TBT", summary.mobile.tbt <= 200, `${Math.round(summary.mobile.tbt)}ms (want <= 200ms)`);
|
|
184
|
+
check("Desktop TBT", summary.desktop.tbt <= 200, `${Math.round(summary.desktop.tbt)}ms (want <= 200ms)`);
|
|
185
|
+
check("Mobile CLS", summary.mobile.cls <= 0.1, `${summary.mobile.cls.toFixed(3)} (want <= 0.100)`);
|
|
186
|
+
check("Desktop CLS", summary.desktop.cls <= 0.1, `${summary.desktop.cls.toFixed(3)} (want <= 0.100)`);
|
|
187
|
+
|
|
188
|
+
const passCount = results.filter((r) => r.pass).length;
|
|
189
|
+
const failCount = results.filter((r) => !r.pass).length;
|
|
190
|
+
|
|
191
|
+
console.log("");
|
|
192
|
+
console.log(" Check Result Detail");
|
|
193
|
+
console.log(" ─────────────────────────────────────────────────────────");
|
|
194
|
+
|
|
195
|
+
for (const r of results) {
|
|
196
|
+
const icon = r.pass ? "PASS" : "FAIL";
|
|
197
|
+
console.log(` ${r.label.padEnd(24)} ${icon.padEnd(6)} ${r.detail}`);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
console.log("");
|
|
201
|
+
console.log(` ${passCount} passed, ${failCount} failed`);
|
|
202
|
+
console.log(` Report saved to docs/audits/perf-audit-${stamp}${suffix}.json`);
|
|
203
|
+
console.log("");
|
|
204
|
+
|
|
205
|
+
// Cleanup
|
|
206
|
+
try { require("fs").unlinkSync(tmpMobile); } catch {}
|
|
207
|
+
try { require("fs").unlinkSync(tmpDesktop); } catch {}
|
|
208
|
+
if (localServerPid) {
|
|
209
|
+
try { process.kill(localServerPid); } catch {}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (failCount > 0) process.exit(1);
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DataForSEO on-page SEO audit.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* DATAFORSEO_AUTH=<base64> bun run scripts/seo-audit.ts [url]
|
|
6
|
+
*
|
|
7
|
+
* Reads credentials from DATAFORSEO_AUTH env var, or falls back to
|
|
8
|
+
* ~/workspace/mbhome/keys/lightup-dataforseo-api-key-manub.
|
|
9
|
+
*
|
|
10
|
+
* Prints a formatted report and saves raw JSON to docs/audits/.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
14
|
+
import { homedir } from "os";
|
|
15
|
+
import { join } from "path";
|
|
16
|
+
|
|
17
|
+
const url = process.argv[2] ?? "https://app.withpolaris.ai";
|
|
18
|
+
|
|
19
|
+
// --- Credentials ---
|
|
20
|
+
|
|
21
|
+
function getAuth(): string {
|
|
22
|
+
if (process.env.DATAFORSEO_AUTH) return process.env.DATAFORSEO_AUTH;
|
|
23
|
+
const keyFile = join(homedir(), "workspace/mbhome/keys/lightup-dataforseo-api-key-manub");
|
|
24
|
+
try {
|
|
25
|
+
const content = readFileSync(keyFile, "utf-8");
|
|
26
|
+
const match = content.match(/api password base64:\s*(.+)/);
|
|
27
|
+
if (match) return match[1].trim();
|
|
28
|
+
} catch {}
|
|
29
|
+
console.error("Error: Set DATAFORSEO_AUTH or ensure key file exists at ~/workspace/mbhome/keys/lightup-dataforseo-api-key-manub");
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const auth = getAuth();
|
|
34
|
+
|
|
35
|
+
// --- API call ---
|
|
36
|
+
|
|
37
|
+
const res = await fetch("https://api.dataforseo.com/v3/on_page/instant_pages", {
|
|
38
|
+
method: "POST",
|
|
39
|
+
headers: {
|
|
40
|
+
Authorization: `Basic ${auth}`,
|
|
41
|
+
"Content-Type": "application/json",
|
|
42
|
+
},
|
|
43
|
+
body: JSON.stringify([{ url, enable_javascript: true }]),
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
if (!res.ok) {
|
|
47
|
+
console.error(`API error: ${res.status} ${res.statusText}`);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const data = await res.json();
|
|
52
|
+
const task = data.tasks?.[0];
|
|
53
|
+
|
|
54
|
+
if (!task || task.status_code !== 20000) {
|
|
55
|
+
console.error("API returned an error:", task?.status_message ?? "unknown");
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const page = task.result?.[0]?.items?.[0];
|
|
60
|
+
if (!page) {
|
|
61
|
+
console.error("No page data returned");
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// --- Save raw JSON ---
|
|
66
|
+
|
|
67
|
+
const now = new Date();
|
|
68
|
+
const stamp = now.toISOString().slice(0, 10);
|
|
69
|
+
const auditDir = join(import.meta.dir, "../docs/audits");
|
|
70
|
+
mkdirSync(auditDir, { recursive: true });
|
|
71
|
+
const jsonPath = join(auditDir, `seo-audit-${stamp}.json`);
|
|
72
|
+
writeFileSync(jsonPath, JSON.stringify(page, null, 2) + "\n");
|
|
73
|
+
|
|
74
|
+
// --- Report ---
|
|
75
|
+
|
|
76
|
+
const meta = page.meta ?? {};
|
|
77
|
+
const checks = page.checks ?? {};
|
|
78
|
+
const timing = page.page_timing ?? {};
|
|
79
|
+
const content = meta.content ?? {};
|
|
80
|
+
const social = meta.social_media_tags ?? {};
|
|
81
|
+
const htags = meta.htags ?? {};
|
|
82
|
+
|
|
83
|
+
type CheckResult = { label: string; pass: boolean; detail: string };
|
|
84
|
+
const results: CheckResult[] = [];
|
|
85
|
+
|
|
86
|
+
function check(label: string, pass: boolean, detail: string) {
|
|
87
|
+
results.push({ label, pass, detail });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Meta
|
|
91
|
+
check("Title", !!meta.title && meta.title_length >= 30 && meta.title_length <= 60,
|
|
92
|
+
meta.title ? `${meta.title_length} chars` : "missing");
|
|
93
|
+
check("Description", !!meta.description && meta.description_length >= 70 && meta.description_length <= 160,
|
|
94
|
+
meta.description ? `${meta.description_length} chars` : "missing");
|
|
95
|
+
check("Canonical", !!meta.canonical, meta.canonical ?? "missing");
|
|
96
|
+
check("HTTPS", !!checks.is_https, checks.is_https ? "yes" : "no");
|
|
97
|
+
check("Doctype", !!checks.has_html_doctype, checks.has_html_doctype ? "yes" : "no");
|
|
98
|
+
check("Charset", !!checks.meta_charset_consistency, checks.meta_charset_consistency ? "consistent" : "inconsistent");
|
|
99
|
+
|
|
100
|
+
// Headings
|
|
101
|
+
const h1Count = htags.h1?.length ?? 0;
|
|
102
|
+
check("Single H1", h1Count === 1, `${h1Count} h1 tag(s)`);
|
|
103
|
+
check("Heading hierarchy", (htags.h2?.length ?? 0) > 0, `h1:${h1Count} h2:${htags.h2?.length ?? 0} h3:${htags.h3?.length ?? 0}`);
|
|
104
|
+
|
|
105
|
+
// Social
|
|
106
|
+
check("OG tags", !!social["og:title"] && !!social["og:description"] && !!social["og:image"],
|
|
107
|
+
social["og:title"] ? "title + desc + image" : "incomplete");
|
|
108
|
+
check("Twitter card", !!social["twitter:card"],
|
|
109
|
+
social["twitter:card"] ?? "missing");
|
|
110
|
+
|
|
111
|
+
// Content
|
|
112
|
+
check("Content rate", !checks.low_content_rate,
|
|
113
|
+
`${(content.plain_text_rate * 100).toFixed(1)}% (want >= 10%)`);
|
|
114
|
+
check("Title/content match", content.title_to_content_consistency >= 0.8,
|
|
115
|
+
content.title_to_content_consistency?.toFixed(2) ?? "n/a");
|
|
116
|
+
check("Desc/content match", content.description_to_content_consistency >= 0.8,
|
|
117
|
+
content.description_to_content_consistency?.toFixed(2) ?? "n/a");
|
|
118
|
+
|
|
119
|
+
// Technical
|
|
120
|
+
check("Favicon", !checks.no_favicon, checks.no_favicon ? "missing" : "present");
|
|
121
|
+
check("No render-blocking", !checks.has_render_blocking_resources,
|
|
122
|
+
checks.has_render_blocking_resources ? `${meta.render_blocking_scripts_count} script(s)` : "clean");
|
|
123
|
+
check("Content encoding", !checks.no_content_encoding,
|
|
124
|
+
checks.no_content_encoding ? "no gzip/brotli" : "enabled");
|
|
125
|
+
check("SEO-friendly URL", !!checks.seo_friendly_url, checks.seo_friendly_url ? "yes" : "no");
|
|
126
|
+
check("Image alt text", !checks.no_image_alt, checks.no_image_alt ? "missing on some images" : "present");
|
|
127
|
+
check("No broken links", !checks.is_broken, checks.is_broken ? "broken" : "ok");
|
|
128
|
+
check("Page size", !checks.large_page_size, checks.large_page_size ? ">3MB" : `${Math.round(page.encoded_size / 1024)} KB`);
|
|
129
|
+
|
|
130
|
+
// Print report
|
|
131
|
+
console.log("");
|
|
132
|
+
console.log(` DataForSEO On-Page Audit — ${url}`);
|
|
133
|
+
console.log(` On-Page Score: ${page.onpage_score}/100`);
|
|
134
|
+
console.log(` Page size: ${Math.round(page.encoded_size / 1024)} KB (${Math.round(page.total_dom_size / 1024)} KB DOM)`);
|
|
135
|
+
console.log(` Words: ${content.plain_text_word_count} Readability: ${content.flesch_kincaid_readability_index?.toFixed(0)}/100 Flesch-Kincaid`);
|
|
136
|
+
console.log("");
|
|
137
|
+
console.log(" Check Result Detail");
|
|
138
|
+
console.log(" ─────────────────────────────────────────────────────────");
|
|
139
|
+
|
|
140
|
+
const passCount = results.filter((r) => r.pass).length;
|
|
141
|
+
const failCount = results.filter((r) => !r.pass).length;
|
|
142
|
+
|
|
143
|
+
for (const r of results) {
|
|
144
|
+
const icon = r.pass ? "PASS" : "FAIL";
|
|
145
|
+
console.log(` ${r.label.padEnd(24)} ${icon.padEnd(6)} ${r.detail}`);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
console.log("");
|
|
149
|
+
console.log(` ${passCount} passed, ${failCount} failed`);
|
|
150
|
+
console.log(` Report saved to docs/audits/seo-audit-${stamp}.json`);
|
|
151
|
+
console.log("");
|
|
152
|
+
|
|
153
|
+
// Exit non-zero if any checks failed
|
|
154
|
+
if (failCount > 0) process.exit(1);
|
package/src/web/app.ts
CHANGED
|
@@ -140,6 +140,15 @@ export function createApp(sql: Sql) {
|
|
|
140
140
|
// Start hourly signup rollup
|
|
141
141
|
startSignupRollup(sql);
|
|
142
142
|
|
|
143
|
+
// --- Static assets ---
|
|
144
|
+
|
|
145
|
+
app.get("/styles.css", async (c) => {
|
|
146
|
+
const file = Bun.file(new URL("./styles/output.css", import.meta.url).pathname);
|
|
147
|
+
return new Response(await file.arrayBuffer(), {
|
|
148
|
+
headers: { "Content-Type": "text/css", "Cache-Control": "public, max-age=31536000, immutable" },
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
143
152
|
// --- SEO ---
|
|
144
153
|
|
|
145
154
|
app.get("/og-image.png", async (c) => {
|
package/src/web/layout.ts
CHANGED
|
@@ -37,18 +37,7 @@ export function layout(body: string, title = "Polaris", seo?: SeoOpts): Response
|
|
|
37
37
|
<meta name="twitter:title" content="${pageTitle}">
|
|
38
38
|
<meta name="twitter:description" content="${description}">
|
|
39
39
|
<meta name="twitter:image" content="${ogImage}">
|
|
40
|
-
<
|
|
41
|
-
<script>
|
|
42
|
-
tailwind.config = {
|
|
43
|
-
theme: {
|
|
44
|
-
extend: {
|
|
45
|
-
colors: {
|
|
46
|
-
polaris: { 50: '#f0f4ff', 100: '#dbe4ff', 200: '#bac8ff', 300: '#91a7ff', 400: '#748ffc', 500: '#5c7cfa', 600: '#4c6ef5', 700: '#4263eb', 800: '#3b5bdb', 900: '#364fc7' }
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
</script>
|
|
40
|
+
<link rel="stylesheet" href="/styles.css">
|
|
52
41
|
</head>
|
|
53
42
|
<body class="bg-gray-50 text-gray-900 antialiased"><div class="overflow-x-hidden max-w-[100vw]">${body}</div>
|
|
54
43
|
<script>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
@import "tailwindcss";
|
|
2
|
+
|
|
3
|
+
@source "../*.ts";
|
|
4
|
+
|
|
5
|
+
@theme {
|
|
6
|
+
--color-polaris-50: #f0f4ff;
|
|
7
|
+
--color-polaris-100: #dbe4ff;
|
|
8
|
+
--color-polaris-200: #bac8ff;
|
|
9
|
+
--color-polaris-300: #91a7ff;
|
|
10
|
+
--color-polaris-400: #748ffc;
|
|
11
|
+
--color-polaris-500: #5c7cfa;
|
|
12
|
+
--color-polaris-600: #4c6ef5;
|
|
13
|
+
--color-polaris-700: #4263eb;
|
|
14
|
+
--color-polaris-800: #3b5bdb;
|
|
15
|
+
--color-polaris-900: #364fc7;
|
|
16
|
+
}
|