gorsee 0.2.4 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +283 -212
- package/dist-pkg/ai/artifact-lifecycle.d.ts +25 -0
- package/dist-pkg/ai/artifact-lifecycle.js +98 -0
- package/dist-pkg/ai/bridge.d.ts +26 -0
- package/dist-pkg/ai/bridge.js +82 -0
- package/dist-pkg/ai/bundle.d.ts +46 -0
- package/dist-pkg/ai/bundle.js +247 -0
- package/dist-pkg/ai/contracts.d.ts +1 -0
- package/dist-pkg/ai/contracts.js +1 -0
- package/dist-pkg/ai/ide.d.ts +37 -0
- package/dist-pkg/ai/ide.js +56 -0
- package/dist-pkg/ai/index.d.ts +85 -0
- package/dist-pkg/ai/index.js +267 -0
- package/dist-pkg/ai/json.d.ts +6 -0
- package/dist-pkg/ai/json.js +14 -0
- package/dist-pkg/ai/mcp.d.ts +28 -0
- package/dist-pkg/ai/mcp.js +173 -0
- package/dist-pkg/ai/session-pack.d.ts +28 -0
- package/dist-pkg/ai/session-pack.js +57 -0
- package/dist-pkg/ai/store.d.ts +67 -0
- package/dist-pkg/ai/store.js +174 -0
- package/dist-pkg/ai/summary.d.ts +57 -0
- package/dist-pkg/ai/summary.js +148 -0
- package/dist-pkg/ai/watch.d.ts +15 -0
- package/dist-pkg/ai/watch.js +66 -0
- package/dist-pkg/auth/action-tokens.d.ts +50 -0
- package/dist-pkg/auth/action-tokens.js +79 -0
- package/dist-pkg/auth/index.d.ts +70 -0
- package/dist-pkg/auth/index.js +199 -0
- package/dist-pkg/auth/redis-session-store.d.ts +7 -0
- package/dist-pkg/auth/redis-session-store.js +42 -0
- package/dist-pkg/auth/signing.d.ts +4 -0
- package/dist-pkg/auth/signing.js +33 -0
- package/dist-pkg/auth/sqlite-session-store.d.ts +12 -0
- package/dist-pkg/auth/sqlite-session-store.js +83 -0
- package/dist-pkg/auth/store-utils.d.ts +2 -0
- package/dist-pkg/auth/store-utils.js +20 -0
- package/dist-pkg/bin/gorsee.js +2 -0
- package/dist-pkg/build/backends/experimental-rolldown.d.ts +6 -0
- package/dist-pkg/build/backends/experimental-rolldown.js +9 -0
- package/dist-pkg/build/backends/register.d.ts +7 -0
- package/dist-pkg/build/backends/register.js +24 -0
- package/dist-pkg/build/backends/rolldown.d.ts +16 -0
- package/dist-pkg/build/backends/rolldown.js +176 -0
- package/dist-pkg/build/client-backend.d.ts +31 -0
- package/dist-pkg/build/client-backend.js +97 -0
- package/dist-pkg/build/client.d.ts +12 -0
- package/dist-pkg/build/client.js +93 -0
- package/dist-pkg/build/css-modules.d.ts +10 -0
- package/dist-pkg/build/css-modules.js +51 -0
- package/dist-pkg/build/devalue-parse.d.ts +1 -0
- package/dist-pkg/build/devalue-parse.js +1 -0
- package/dist-pkg/build/diagnostics.d.ts +20 -0
- package/dist-pkg/build/diagnostics.js +35 -0
- package/dist-pkg/build/fixtures.d.ts +7 -0
- package/dist-pkg/build/fixtures.js +60 -0
- package/dist-pkg/build/framework-imports.d.ts +4 -0
- package/dist-pkg/build/framework-imports.js +58 -0
- package/dist-pkg/build/init.d.ts +1 -0
- package/dist-pkg/build/init.js +6 -0
- package/dist-pkg/build/manifest.d.ts +3 -0
- package/dist-pkg/build/manifest.js +23 -0
- package/dist-pkg/build/parity.d.ts +32 -0
- package/dist-pkg/build/parity.js +71 -0
- package/dist-pkg/build/plugin.d.ts +11 -0
- package/dist-pkg/build/plugin.js +8 -0
- package/dist-pkg/build/readiness.d.ts +11 -0
- package/dist-pkg/build/readiness.js +18 -0
- package/dist-pkg/build/route-client-transform.d.ts +17 -0
- package/dist-pkg/build/route-client-transform.js +48 -0
- package/dist-pkg/build/route-metadata.d.ts +6 -0
- package/dist-pkg/build/route-metadata.js +8 -0
- package/dist-pkg/build/rpc-transform.d.ts +1 -0
- package/dist-pkg/build/rpc-transform.js +44 -0
- package/dist-pkg/build/server-bundle.d.ts +4 -0
- package/dist-pkg/build/server-bundle.js +261 -0
- package/dist-pkg/build/server-strip.d.ts +2 -0
- package/dist-pkg/build/server-strip.js +32 -0
- package/dist-pkg/build/ssg.d.ts +12 -0
- package/dist-pkg/build/ssg.js +36 -0
- package/dist-pkg/cli/bun-plugin.d.ts +2 -0
- package/dist-pkg/cli/bun-plugin.js +14 -0
- package/dist-pkg/cli/canonical-import-rewrite.d.ts +18 -0
- package/dist-pkg/cli/canonical-import-rewrite.js +94 -0
- package/dist-pkg/cli/canonical-imports.d.ts +8 -0
- package/dist-pkg/cli/canonical-imports.js +131 -0
- package/dist-pkg/cli/check-ast.d.ts +20 -0
- package/dist-pkg/cli/check-ast.js +126 -0
- package/dist-pkg/cli/cmd-ai.d.ts +4 -0
- package/dist-pkg/cli/cmd-ai.js +290 -0
- package/dist-pkg/cli/cmd-build.d.ts +12 -0
- package/dist-pkg/cli/cmd-build.js +198 -0
- package/dist-pkg/cli/cmd-check.d.ts +25 -0
- package/dist-pkg/cli/cmd-check.js +573 -0
- package/dist-pkg/cli/cmd-create.d.ts +6 -0
- package/dist-pkg/cli/cmd-create.js +600 -0
- package/dist-pkg/cli/cmd-deploy.d.ts +6 -0
- package/dist-pkg/cli/cmd-deploy.js +381 -0
- package/dist-pkg/cli/cmd-dev.d.ts +5 -0
- package/dist-pkg/cli/cmd-dev.js +5 -0
- package/dist-pkg/cli/cmd-docs.d.ts +50 -0
- package/dist-pkg/cli/cmd-docs.js +122 -0
- package/dist-pkg/cli/cmd-generate.d.ts +12 -0
- package/dist-pkg/cli/cmd-generate.js +320 -0
- package/dist-pkg/cli/cmd-migrate.d.ts +7 -0
- package/dist-pkg/cli/cmd-migrate.js +42 -0
- package/dist-pkg/cli/cmd-routes.d.ts +6 -0
- package/dist-pkg/cli/cmd-routes.js +24 -0
- package/dist-pkg/cli/cmd-start.d.ts +13 -0
- package/dist-pkg/cli/cmd-start.js +32 -0
- package/dist-pkg/cli/cmd-test.d.ts +20 -0
- package/dist-pkg/cli/cmd-test.js +103 -0
- package/dist-pkg/cli/cmd-typegen.d.ts +7 -0
- package/dist-pkg/cli/cmd-typegen.js +66 -0
- package/dist-pkg/cli/cmd-upgrade.d.ts +38 -0
- package/dist-pkg/cli/cmd-upgrade.js +232 -0
- package/dist-pkg/cli/context.d.ts +4 -0
- package/dist-pkg/cli/context.js +4 -0
- package/dist-pkg/cli/framework-md.d.ts +1 -0
- package/dist-pkg/cli/framework-md.js +391 -0
- package/dist-pkg/cli/index.d.ts +6 -0
- package/dist-pkg/cli/index.js +106 -0
- package/dist-pkg/cli/release-version.d.ts +2 -0
- package/dist-pkg/cli/release-version.js +33 -0
- package/dist-pkg/client.d.ts +16 -0
- package/dist-pkg/client.js +71 -0
- package/dist-pkg/compat.d.ts +1 -0
- package/dist-pkg/compat.js +1 -0
- package/dist-pkg/compiler/analysis-backend.d.ts +20 -0
- package/dist-pkg/compiler/analysis-backend.js +164 -0
- package/dist-pkg/compiler/backends/experimental-oxc.d.ts +6 -0
- package/dist-pkg/compiler/backends/experimental-oxc.js +9 -0
- package/dist-pkg/compiler/backends/oxc.d.ts +16 -0
- package/dist-pkg/compiler/backends/oxc.js +198 -0
- package/dist-pkg/compiler/backends/register.d.ts +7 -0
- package/dist-pkg/compiler/backends/register.js +22 -0
- package/dist-pkg/compiler/fixtures.d.ts +2 -0
- package/dist-pkg/compiler/fixtures.js +118 -0
- package/dist-pkg/compiler/init.d.ts +1 -0
- package/dist-pkg/compiler/init.js +6 -0
- package/dist-pkg/compiler/module-analysis.d.ts +13 -0
- package/dist-pkg/compiler/module-analysis.js +50 -0
- package/dist-pkg/compiler/parity.d.ts +35 -0
- package/dist-pkg/compiler/parity.js +89 -0
- package/dist-pkg/compiler/readiness.d.ts +11 -0
- package/dist-pkg/compiler/readiness.js +18 -0
- package/dist-pkg/compiler/route-facts.d.ts +34 -0
- package/dist-pkg/compiler/route-facts.js +75 -0
- package/dist-pkg/content/index.d.ts +27 -0
- package/dist-pkg/content/index.js +287 -0
- package/dist-pkg/db/index.d.ts +3 -0
- package/dist-pkg/db/index.js +6 -0
- package/dist-pkg/db/migrate.d.ts +7 -0
- package/dist-pkg/db/migrate.js +53 -0
- package/dist-pkg/db/postgres.d.ts +29 -0
- package/dist-pkg/db/postgres.js +59 -0
- package/dist-pkg/db/sqlite.d.ts +10 -0
- package/dist-pkg/db/sqlite.js +22 -0
- package/dist-pkg/deploy/cloudflare.d.ts +8 -0
- package/{src/deploy/cloudflare.ts → dist-pkg/deploy/cloudflare.js} +44 -28
- package/dist-pkg/deploy/conformance.d.ts +21 -0
- package/dist-pkg/deploy/conformance.js +98 -0
- package/dist-pkg/deploy/dockerfile.d.ts +3 -0
- package/{src/deploy/dockerfile.ts → dist-pkg/deploy/dockerfile.js} +8 -10
- package/dist-pkg/deploy/fly.d.ts +3 -0
- package/{src/deploy/fly.ts → dist-pkg/deploy/fly.js} +11 -12
- package/dist-pkg/deploy/index.d.ts +6 -0
- package/dist-pkg/deploy/index.js +26 -0
- package/dist-pkg/deploy/netlify.d.ts +2 -0
- package/{src/deploy/netlify.ts → dist-pkg/deploy/netlify.js} +17 -11
- package/dist-pkg/deploy/runtime.d.ts +2 -0
- package/dist-pkg/deploy/runtime.js +3 -0
- package/dist-pkg/deploy/vercel.d.ts +22 -0
- package/dist-pkg/deploy/vercel.js +53 -0
- package/dist-pkg/dev/error-overlay.d.ts +1 -0
- package/{src/dev/error-overlay.ts → dist-pkg/dev/error-overlay.js} +13 -23
- package/dist-pkg/dev/hmr.d.ts +30 -0
- package/dist-pkg/dev/hmr.js +86 -0
- package/dist-pkg/dev/partial-handler.d.ts +9 -0
- package/dist-pkg/dev/partial-handler.js +24 -0
- package/dist-pkg/dev/request-handler.d.ts +30 -0
- package/dist-pkg/dev/request-handler.js +67 -0
- package/dist-pkg/dev/watcher.d.ts +6 -0
- package/dist-pkg/dev/watcher.js +34 -0
- package/dist-pkg/dev.d.ts +11 -0
- package/dist-pkg/dev.js +268 -0
- package/dist-pkg/env/index.d.ts +3 -0
- package/dist-pkg/env/index.js +57 -0
- package/dist-pkg/errors/catalog.d.ts +9 -0
- package/{src/errors/catalog.ts → dist-pkg/errors/catalog.js} +8 -24
- package/dist-pkg/errors/formatter.d.ts +22 -0
- package/dist-pkg/errors/formatter.js +43 -0
- package/dist-pkg/errors/index.d.ts +2 -0
- package/dist-pkg/errors/index.js +2 -0
- package/dist-pkg/forms/action.d.ts +15 -0
- package/dist-pkg/forms/action.js +20 -0
- package/dist-pkg/forms/index.d.ts +4 -0
- package/dist-pkg/forms/index.js +12 -0
- package/dist-pkg/i18n/index.d.ts +61 -0
- package/dist-pkg/i18n/index.js +147 -0
- package/dist-pkg/index-client.d.ts +1 -0
- package/dist-pkg/index-client.js +1 -0
- package/dist-pkg/index.d.ts +32 -0
- package/dist-pkg/index.js +79 -0
- package/dist-pkg/jsx-runtime-client.d.ts +8 -0
- package/dist-pkg/jsx-runtime-client.js +1 -0
- package/dist-pkg/jsx-runtime.d.ts +13 -0
- package/dist-pkg/jsx-runtime.js +5 -0
- package/dist-pkg/jsx-types-html.d.ts +221 -0
- package/dist-pkg/jsx-types-html.js +0 -0
- package/dist-pkg/log/index.d.ts +8 -0
- package/dist-pkg/log/index.js +55 -0
- package/dist-pkg/plugins/drizzle.d.ts +16 -0
- package/dist-pkg/plugins/drizzle.js +53 -0
- package/dist-pkg/plugins/index.d.ts +62 -0
- package/dist-pkg/plugins/index.js +159 -0
- package/dist-pkg/plugins/lucia.d.ts +26 -0
- package/dist-pkg/plugins/lucia.js +63 -0
- package/dist-pkg/plugins/prisma.d.ts +14 -0
- package/dist-pkg/plugins/prisma.js +60 -0
- package/dist-pkg/plugins/resend.d.ts +21 -0
- package/dist-pkg/plugins/resend.js +45 -0
- package/dist-pkg/plugins/s3.d.ts +18 -0
- package/dist-pkg/plugins/s3.js +63 -0
- package/dist-pkg/plugins/stripe.d.ts +32 -0
- package/dist-pkg/plugins/stripe.js +69 -0
- package/dist-pkg/plugins/tailwind.d.ts +15 -0
- package/dist-pkg/plugins/tailwind.js +58 -0
- package/dist-pkg/prod.d.ts +21 -0
- package/dist-pkg/prod.js +347 -0
- package/dist-pkg/reactive/computed.d.ts +3 -0
- package/dist-pkg/reactive/computed.js +15 -0
- package/dist-pkg/reactive/data.d.ts +25 -0
- package/dist-pkg/reactive/data.js +56 -0
- package/dist-pkg/reactive/diagnostics.d.ts +100 -0
- package/dist-pkg/reactive/diagnostics.js +363 -0
- package/dist-pkg/reactive/effect.d.ts +3 -0
- package/dist-pkg/reactive/effect.js +6 -0
- package/dist-pkg/reactive/index.d.ts +9 -0
- package/dist-pkg/reactive/index.js +18 -0
- package/dist-pkg/reactive/live.d.ts +15 -0
- package/dist-pkg/reactive/live.js +70 -0
- package/dist-pkg/reactive/optimistic.d.ts +20 -0
- package/dist-pkg/reactive/optimistic.js +67 -0
- package/dist-pkg/reactive/resource.d.ts +25 -0
- package/dist-pkg/reactive/resource.js +153 -0
- package/dist-pkg/reactive/signal.d.ts +4 -0
- package/dist-pkg/reactive/signal.js +17 -0
- package/dist-pkg/reactive/store.d.ts +6 -0
- package/dist-pkg/reactive/store.js +19 -0
- package/dist-pkg/router/index.d.ts +2 -0
- package/dist-pkg/router/index.js +2 -0
- package/dist-pkg/router/matcher.d.ts +7 -0
- package/dist-pkg/router/matcher.js +40 -0
- package/dist-pkg/router/scanner.d.ts +14 -0
- package/dist-pkg/router/scanner.js +141 -0
- package/dist-pkg/routes/index.d.ts +1 -0
- package/dist-pkg/routes/index.js +10 -0
- package/dist-pkg/runtime/app-config.d.ts +26 -0
- package/dist-pkg/runtime/app-config.js +69 -0
- package/dist-pkg/runtime/client.d.ts +3 -0
- package/dist-pkg/runtime/client.js +42 -0
- package/dist-pkg/runtime/devtools.d.ts +64 -0
- package/dist-pkg/runtime/devtools.js +132 -0
- package/dist-pkg/runtime/error-boundary.d.ts +6 -0
- package/dist-pkg/runtime/error-boundary.js +17 -0
- package/dist-pkg/runtime/event-replay.d.ts +3 -0
- package/{src/runtime/event-replay.ts → dist-pkg/runtime/event-replay.js} +20 -22
- package/dist-pkg/runtime/event-source.d.ts +7 -0
- package/dist-pkg/runtime/event-source.js +24 -0
- package/dist-pkg/runtime/form.d.ts +24 -0
- package/dist-pkg/runtime/form.js +65 -0
- package/dist-pkg/runtime/head.d.ts +7 -0
- package/dist-pkg/runtime/head.js +90 -0
- package/dist-pkg/runtime/html-escape.d.ts +7 -0
- package/dist-pkg/runtime/html-escape.js +39 -0
- package/dist-pkg/runtime/hydration.d.ts +15 -0
- package/dist-pkg/runtime/hydration.js +103 -0
- package/dist-pkg/runtime/image.d.ts +49 -0
- package/dist-pkg/runtime/image.js +144 -0
- package/dist-pkg/runtime/index.d.ts +17 -0
- package/dist-pkg/runtime/index.js +59 -0
- package/dist-pkg/runtime/island-hydrator.d.ts +12 -0
- package/dist-pkg/runtime/island-hydrator.js +74 -0
- package/dist-pkg/runtime/island.d.ts +19 -0
- package/dist-pkg/runtime/island.js +36 -0
- package/dist-pkg/runtime/jsx-runtime.d.ts +12 -0
- package/dist-pkg/runtime/jsx-runtime.js +176 -0
- package/dist-pkg/runtime/link.d.ts +16 -0
- package/dist-pkg/runtime/link.js +47 -0
- package/dist-pkg/runtime/project.d.ts +37 -0
- package/dist-pkg/runtime/project.js +36 -0
- package/dist-pkg/runtime/renderable.d.ts +6 -0
- package/dist-pkg/runtime/renderable.js +0 -0
- package/dist-pkg/runtime/router.d.ts +47 -0
- package/dist-pkg/runtime/router.js +487 -0
- package/dist-pkg/runtime/server.d.ts +8 -0
- package/dist-pkg/runtime/server.js +71 -0
- package/dist-pkg/runtime/stream.d.ts +29 -0
- package/dist-pkg/runtime/stream.js +106 -0
- package/dist-pkg/runtime/suspense.d.ts +6 -0
- package/dist-pkg/runtime/suspense.js +16 -0
- package/dist-pkg/runtime/typed-routes.d.ts +24 -0
- package/dist-pkg/runtime/typed-routes.js +77 -0
- package/dist-pkg/runtime/validated-form.d.ts +40 -0
- package/dist-pkg/runtime/validated-form.js +120 -0
- package/dist-pkg/security/cors.d.ts +10 -0
- package/dist-pkg/security/cors.js +56 -0
- package/dist-pkg/security/csrf.d.ts +9 -0
- package/dist-pkg/security/csrf.js +53 -0
- package/dist-pkg/security/headers.d.ts +11 -0
- package/dist-pkg/security/headers.js +31 -0
- package/dist-pkg/security/index.d.ts +5 -0
- package/dist-pkg/security/index.js +5 -0
- package/dist-pkg/security/rate-limit.d.ts +10 -0
- package/dist-pkg/security/rate-limit.js +49 -0
- package/dist-pkg/security/redis-rate-limit.d.ts +14 -0
- package/dist-pkg/security/redis-rate-limit.js +23 -0
- package/dist-pkg/server/action.d.ts +21 -0
- package/dist-pkg/server/action.js +64 -0
- package/dist-pkg/server/cache-utils.d.ts +2 -0
- package/dist-pkg/server/cache-utils.js +26 -0
- package/dist-pkg/server/cache.d.ts +33 -0
- package/dist-pkg/server/cache.js +236 -0
- package/dist-pkg/server/compress.d.ts +2 -0
- package/dist-pkg/server/compress.js +77 -0
- package/dist-pkg/server/endpoint-execution.d.ts +28 -0
- package/dist-pkg/server/endpoint-execution.js +37 -0
- package/dist-pkg/server/etag.d.ts +3 -0
- package/dist-pkg/server/etag.js +18 -0
- package/dist-pkg/server/guard.d.ts +16 -0
- package/dist-pkg/server/guard.js +45 -0
- package/dist-pkg/server/html-shell.d.ts +12 -0
- package/dist-pkg/server/html-shell.js +52 -0
- package/dist-pkg/server/index.d.ts +36 -0
- package/dist-pkg/server/index.js +170 -0
- package/dist-pkg/server/jobs.d.ts +41 -0
- package/dist-pkg/server/jobs.js +83 -0
- package/dist-pkg/server/manifest.d.ts +19 -0
- package/dist-pkg/server/manifest.js +36 -0
- package/dist-pkg/server/middleware.d.ts +39 -0
- package/dist-pkg/server/middleware.js +127 -0
- package/dist-pkg/server/mime.d.ts +1 -0
- package/{src/server/mime.ts → dist-pkg/server/mime.js} +6 -17
- package/dist-pkg/server/not-found.d.ts +6 -0
- package/dist-pkg/server/not-found.js +27 -0
- package/dist-pkg/server/page-render.d.ts +29 -0
- package/dist-pkg/server/page-render.js +70 -0
- package/dist-pkg/server/partial-navigation.d.ts +4 -0
- package/dist-pkg/server/partial-navigation.js +16 -0
- package/dist-pkg/server/pipe.d.ts +9 -0
- package/dist-pkg/server/pipe.js +32 -0
- package/dist-pkg/server/redis-cache-store.d.ts +10 -0
- package/dist-pkg/server/redis-cache-store.js +73 -0
- package/dist-pkg/server/redis-client.d.ts +37 -0
- package/dist-pkg/server/redis-client.js +37 -0
- package/dist-pkg/server/request-policy.d.ts +55 -0
- package/dist-pkg/server/request-policy.js +216 -0
- package/dist-pkg/server/request-preflight.d.ts +23 -0
- package/dist-pkg/server/request-preflight.js +46 -0
- package/dist-pkg/server/request-security-policy.d.ts +17 -0
- package/dist-pkg/server/request-security-policy.js +34 -0
- package/dist-pkg/server/request-surface.d.ts +8 -0
- package/dist-pkg/server/request-surface.js +19 -0
- package/dist-pkg/server/route-request.d.ts +31 -0
- package/dist-pkg/server/route-request.js +112 -0
- package/dist-pkg/server/route-response.d.ts +22 -0
- package/dist-pkg/server/route-response.js +61 -0
- package/dist-pkg/server/rpc-hash.d.ts +2 -0
- package/dist-pkg/server/rpc-hash.js +7 -0
- package/dist-pkg/server/rpc-protocol.d.ts +22 -0
- package/dist-pkg/server/rpc-protocol.js +28 -0
- package/dist-pkg/server/rpc-utils.d.ts +2 -0
- package/dist-pkg/server/rpc-utils.js +26 -0
- package/dist-pkg/server/rpc.d.ts +20 -0
- package/dist-pkg/server/rpc.js +147 -0
- package/dist-pkg/server/runtime-dispatch.d.ts +19 -0
- package/dist-pkg/server/runtime-dispatch.js +67 -0
- package/dist-pkg/server/server-execution.d.ts +22 -0
- package/dist-pkg/server/server-execution.js +53 -0
- package/dist-pkg/server/sqlite-cache-store.d.ts +13 -0
- package/dist-pkg/server/sqlite-cache-store.js +88 -0
- package/dist-pkg/server/sse.d.ts +9 -0
- package/dist-pkg/server/sse.js +34 -0
- package/dist-pkg/server/static-file.d.ts +9 -0
- package/dist-pkg/server/static-file.js +43 -0
- package/dist-pkg/server/ws.d.ts +18 -0
- package/dist-pkg/server/ws.js +40 -0
- package/dist-pkg/server-entry.d.ts +19 -0
- package/dist-pkg/server-entry.js +93 -0
- package/dist-pkg/testing/index.d.ts +98 -0
- package/dist-pkg/testing/index.js +257 -0
- package/dist-pkg/types/index.d.ts +4 -0
- package/dist-pkg/types/index.js +4 -0
- package/dist-pkg/types/safe-html.d.ts +7 -0
- package/dist-pkg/types/safe-html.js +19 -0
- package/dist-pkg/types/safe-sql.d.ts +8 -0
- package/dist-pkg/types/safe-sql.js +14 -0
- package/dist-pkg/types/safe-url.d.ts +7 -0
- package/dist-pkg/types/safe-url.js +20 -0
- package/dist-pkg/types/user-input.d.ts +9 -0
- package/dist-pkg/types/user-input.js +3 -0
- package/dist-pkg/unsafe/index.d.ts +12 -0
- package/dist-pkg/unsafe/index.js +6 -0
- package/package.json +110 -45
- package/bin/gorsee.js +0 -2
- package/src/auth/index.ts +0 -178
- package/src/auth/redis-session-store.ts +0 -46
- package/src/auth/sqlite-session-store.ts +0 -98
- package/src/auth/store-utils.ts +0 -21
- package/src/build/client.ts +0 -139
- package/src/build/css-modules.ts +0 -69
- package/src/build/devalue-parse.ts +0 -2
- package/src/build/manifest.ts +0 -34
- package/src/build/route-metadata.ts +0 -12
- package/src/build/rpc-transform.ts +0 -62
- package/src/build/server-strip.ts +0 -87
- package/src/build/ssg.ts +0 -70
- package/src/cli/bun-plugin.ts +0 -58
- package/src/cli/cmd-build.ts +0 -153
- package/src/cli/cmd-check.ts +0 -239
- package/src/cli/cmd-create.ts +0 -328
- package/src/cli/cmd-deploy.ts +0 -149
- package/src/cli/cmd-dev.ts +0 -13
- package/src/cli/cmd-docs.ts +0 -152
- package/src/cli/cmd-generate.ts +0 -155
- package/src/cli/cmd-migrate.ts +0 -53
- package/src/cli/cmd-routes.ts +0 -36
- package/src/cli/cmd-start.ts +0 -30
- package/src/cli/cmd-test.ts +0 -129
- package/src/cli/cmd-typegen.ts +0 -93
- package/src/cli/cmd-upgrade.ts +0 -143
- package/src/cli/context.ts +0 -12
- package/src/cli/framework-md.ts +0 -223
- package/src/cli/index.ts +0 -107
- package/src/client.ts +0 -26
- package/src/db/index.ts +0 -2
- package/src/db/migrate.ts +0 -89
- package/src/db/sqlite.ts +0 -40
- package/src/deploy/index.ts +0 -31
- package/src/deploy/vercel.ts +0 -94
- package/src/dev/hmr.ts +0 -31
- package/src/dev/partial-handler.ts +0 -52
- package/src/dev/request-handler.ts +0 -127
- package/src/dev/watcher.ts +0 -48
- package/src/dev.ts +0 -208
- package/src/env/index.ts +0 -74
- package/src/errors/formatter.ts +0 -63
- package/src/errors/index.ts +0 -2
- package/src/i18n/index.ts +0 -72
- package/src/index-client.ts +0 -4
- package/src/index.ts +0 -43
- package/src/jsx-runtime-client.ts +0 -13
- package/src/jsx-runtime.ts +0 -20
- package/src/jsx-types-html.ts +0 -242
- package/src/log/index.ts +0 -44
- package/src/plugins/drizzle.ts +0 -84
- package/src/plugins/index.ts +0 -86
- package/src/plugins/lucia.ts +0 -111
- package/src/plugins/prisma.ts +0 -85
- package/src/plugins/resend.ts +0 -78
- package/src/plugins/s3.ts +0 -102
- package/src/plugins/stripe.ts +0 -133
- package/src/plugins/tailwind.ts +0 -92
- package/src/prod.ts +0 -252
- package/src/reactive/computed.ts +0 -7
- package/src/reactive/effect.ts +0 -7
- package/src/reactive/index.ts +0 -7
- package/src/reactive/live.ts +0 -97
- package/src/reactive/optimistic.ts +0 -83
- package/src/reactive/resource.ts +0 -138
- package/src/reactive/signal.ts +0 -20
- package/src/reactive/store.ts +0 -36
- package/src/router/index.ts +0 -2
- package/src/router/matcher.ts +0 -53
- package/src/router/scanner.ts +0 -206
- package/src/runtime/client.ts +0 -28
- package/src/runtime/error-boundary.ts +0 -35
- package/src/runtime/form.ts +0 -49
- package/src/runtime/head.ts +0 -113
- package/src/runtime/html-escape.ts +0 -30
- package/src/runtime/hydration.ts +0 -95
- package/src/runtime/image.ts +0 -48
- package/src/runtime/index.ts +0 -12
- package/src/runtime/island-hydrator.ts +0 -84
- package/src/runtime/island.ts +0 -88
- package/src/runtime/jsx-runtime.ts +0 -167
- package/src/runtime/link.ts +0 -45
- package/src/runtime/project.ts +0 -73
- package/src/runtime/router.ts +0 -224
- package/src/runtime/server.ts +0 -102
- package/src/runtime/stream.ts +0 -182
- package/src/runtime/suspense.ts +0 -37
- package/src/runtime/typed-routes.ts +0 -26
- package/src/runtime/validated-form.ts +0 -106
- package/src/security/cors.ts +0 -80
- package/src/security/csrf.ts +0 -85
- package/src/security/headers.ts +0 -50
- package/src/security/index.ts +0 -4
- package/src/security/rate-limit.ts +0 -80
- package/src/server/action.ts +0 -48
- package/src/server/cache-utils.ts +0 -23
- package/src/server/cache.ts +0 -125
- package/src/server/compress.ts +0 -60
- package/src/server/etag.ts +0 -23
- package/src/server/guard.ts +0 -69
- package/src/server/html-shell.ts +0 -69
- package/src/server/index.ts +0 -57
- package/src/server/manifest.ts +0 -36
- package/src/server/middleware.ts +0 -159
- package/src/server/not-found.ts +0 -35
- package/src/server/page-render.ts +0 -123
- package/src/server/pipe.ts +0 -46
- package/src/server/redis-cache-store.ts +0 -87
- package/src/server/redis-client.ts +0 -71
- package/src/server/request-preflight.ts +0 -45
- package/src/server/route-request.ts +0 -72
- package/src/server/rpc-hash.ts +0 -17
- package/src/server/rpc-utils.ts +0 -27
- package/src/server/rpc.ts +0 -177
- package/src/server/sqlite-cache-store.ts +0 -109
- package/src/server/sse.ts +0 -96
- package/src/server/static-file.ts +0 -63
- package/src/server/ws.ts +0 -56
- package/src/server-entry.ts +0 -36
- package/src/testing/index.ts +0 -74
- package/src/types/index.ts +0 -4
- package/src/types/safe-html.ts +0 -32
- package/src/types/safe-sql.ts +0 -28
- package/src/types/safe-url.ts +0 -40
- package/src/types/user-input.ts +0 -12
- package/src/unsafe/index.ts +0 -18
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { escapeHTML, escapeAttr, VOID_ELEMENTS, resolveValue } from "./html-escape.js";
|
|
2
|
+
export function streamJsx(type, props) {
|
|
3
|
+
return { type, props: props ?? {} };
|
|
4
|
+
}
|
|
5
|
+
export const streamJsxs = streamJsx;
|
|
6
|
+
export function StreamSuspense(props) {
|
|
7
|
+
const node = streamJsx("gorsee-suspense", {
|
|
8
|
+
fallback: props.fallback,
|
|
9
|
+
children: props.children
|
|
10
|
+
});
|
|
11
|
+
node.__gorsee_suspense = !0;
|
|
12
|
+
return node;
|
|
13
|
+
}
|
|
14
|
+
function renderAttrs(props) {
|
|
15
|
+
const parts = [];
|
|
16
|
+
for (const [key, rawValue] of Object.entries(props)) {
|
|
17
|
+
if (key === "children" || key === "ref" || key === "fallback" || key.startsWith("on:"))
|
|
18
|
+
continue;
|
|
19
|
+
const value = resolveValue(rawValue);
|
|
20
|
+
if (key === "className" || key === "class") {
|
|
21
|
+
if (value != null)
|
|
22
|
+
parts.push(` class="${escapeAttr(String(value))}"`);
|
|
23
|
+
} else if (typeof value === "boolean") {
|
|
24
|
+
if (value)
|
|
25
|
+
parts.push(` ${key}`);
|
|
26
|
+
} else if (value != null)
|
|
27
|
+
parts.push(` ${key}="${escapeAttr(String(value))}"`);
|
|
28
|
+
}
|
|
29
|
+
return parts.join("");
|
|
30
|
+
}
|
|
31
|
+
function renderShellNode(node, ctx) {
|
|
32
|
+
if (node == null || typeof node === "boolean")
|
|
33
|
+
return "";
|
|
34
|
+
if (Array.isArray(node))
|
|
35
|
+
return node.map((n) => renderShellNode(n, ctx)).join("");
|
|
36
|
+
if (typeof node === "object" && node !== null && "type" in node) {
|
|
37
|
+
const vnode = node;
|
|
38
|
+
if (vnode.__gorsee_suspense) {
|
|
39
|
+
const id = `s${ctx.nextId++}`, fallbackHtml = renderShellNode(vnode.props.fallback, ctx);
|
|
40
|
+
ctx.suspenseSlots.push({
|
|
41
|
+
id,
|
|
42
|
+
fallback: vnode.props.fallback,
|
|
43
|
+
children: vnode.props.children,
|
|
44
|
+
resolve: async () => {
|
|
45
|
+
const children = vnode.props.children;
|
|
46
|
+
if (typeof children === "function")
|
|
47
|
+
return await children();
|
|
48
|
+
return children;
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
return `<div data-g-suspense="${id}">${fallbackHtml}</div>`;
|
|
52
|
+
}
|
|
53
|
+
if (typeof vnode.type === "symbol")
|
|
54
|
+
return renderShellNode(vnode.props.children, ctx);
|
|
55
|
+
if (typeof vnode.type === "function") {
|
|
56
|
+
const result = vnode.type(vnode.props);
|
|
57
|
+
return renderShellNode(result, ctx);
|
|
58
|
+
}
|
|
59
|
+
const tag = vnode.type, attrs = renderAttrs(vnode.props);
|
|
60
|
+
if (VOID_ELEMENTS.has(tag))
|
|
61
|
+
return `<${tag}${attrs} />`;
|
|
62
|
+
const children = renderShellNode(vnode.props.children, ctx);
|
|
63
|
+
return `<${tag}${attrs}>${children}</${tag}>`;
|
|
64
|
+
}
|
|
65
|
+
const resolved = resolveValue(node);
|
|
66
|
+
if (resolved == null || typeof resolved === "boolean")
|
|
67
|
+
return "";
|
|
68
|
+
return escapeHTML(String(resolved));
|
|
69
|
+
}
|
|
70
|
+
export function buildStreamChunkScript(slotId, html) {
|
|
71
|
+
return [
|
|
72
|
+
`<template data-g-chunk="${slotId}">${html}</template>`,
|
|
73
|
+
"<script>",
|
|
74
|
+
"(function(){",
|
|
75
|
+
` var t=document.querySelector('[data-g-chunk="${slotId}"]');`,
|
|
76
|
+
` var s=document.querySelector('[data-g-suspense="${slotId}"]');`,
|
|
77
|
+
" if(t&&s){var f=t.content.cloneNode(true);s.replaceChildren(...Array.from(f.childNodes));t.remove();s.removeAttribute('data-g-suspense')}",
|
|
78
|
+
"})();",
|
|
79
|
+
"</script>"
|
|
80
|
+
].join("");
|
|
81
|
+
}
|
|
82
|
+
const defaultShell = (body) => `<!DOCTYPE html>
|
|
83
|
+
<html lang="en">
|
|
84
|
+
<head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Gorsee App</title></head>
|
|
85
|
+
<body><div id="app">${body}</div>`, STREAM_TAIL = "</body></html>";
|
|
86
|
+
export function renderToStream(root, options) {
|
|
87
|
+
const shell = options?.shell ?? defaultShell, encoder = new TextEncoder, ctx = { suspenseSlots: [], nextId: 0 };
|
|
88
|
+
return new ReadableStream({
|
|
89
|
+
async start(controller) {
|
|
90
|
+
const shellHtml = renderShellNode(root, ctx);
|
|
91
|
+
controller.enqueue(encoder.encode(shell(shellHtml)));
|
|
92
|
+
const promises = ctx.suspenseSlots.map(async (slot) => {
|
|
93
|
+
try {
|
|
94
|
+
const resolved = await slot.resolve(), html = renderShellNode(resolved, ctx), script = buildStreamChunkScript(slot.id, html);
|
|
95
|
+
controller.enqueue(encoder.encode(script));
|
|
96
|
+
} catch (err) {
|
|
97
|
+
const message = err instanceof Error ? err.message : String(err), errorHtml = `<div class="error">Error: ${escapeHTML(message)}</div>`, script = buildStreamChunkScript(slot.id, errorHtml);
|
|
98
|
+
controller.enqueue(encoder.encode(script));
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
await Promise.all(promises);
|
|
102
|
+
controller.enqueue(encoder.encode(STREAM_TAIL));
|
|
103
|
+
controller.close();
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createSignal } from "../reactive/signal.js";
|
|
2
|
+
export function Suspense(props) {
|
|
3
|
+
const children = props.children;
|
|
4
|
+
if (typeof children === "function") {
|
|
5
|
+
const asyncFn = children, [resolved, setResolved] = createSignal(null), [pending, setPending] = createSignal(!0);
|
|
6
|
+
asyncFn().then((result) => {
|
|
7
|
+
setResolved(result);
|
|
8
|
+
setPending(!1);
|
|
9
|
+
}).catch((err) => {
|
|
10
|
+
setResolved(err instanceof Error ? err.message : String(err));
|
|
11
|
+
setPending(!1);
|
|
12
|
+
});
|
|
13
|
+
return () => pending() ? props.fallback : resolved();
|
|
14
|
+
}
|
|
15
|
+
return children;
|
|
16
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type TypedRouteParams = Record<string, string | undefined>;
|
|
2
|
+
export type TypedRouteSearchValue = string | number | boolean | null | undefined | Array<string | number | boolean>;
|
|
3
|
+
export interface TypedRouteOptions {
|
|
4
|
+
params?: TypedRouteParams;
|
|
5
|
+
search?: Record<string, TypedRouteSearchValue>;
|
|
6
|
+
hash?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface TypedRouteDefinition {
|
|
9
|
+
path: string;
|
|
10
|
+
params(): string[];
|
|
11
|
+
build(options?: TypedRouteParams | TypedRouteOptions): string;
|
|
12
|
+
buildStrict(options: TypedRouteOptions): string;
|
|
13
|
+
navigate(options?: TypedRouteParams | TypedRouteOptions): Promise<void>;
|
|
14
|
+
prefetch(options?: TypedRouteParams | TypedRouteOptions): Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
export type TypedRouteTarget = string | TypedRouteDefinition;
|
|
17
|
+
export declare function extractRouteParamKeys(path: string): string[];
|
|
18
|
+
export declare function buildSearchParams(search?: Record<string, TypedRouteSearchValue>): string;
|
|
19
|
+
export declare function buildTypedPath(path: string, params?: TypedRouteParams, strict?: boolean): string;
|
|
20
|
+
export declare function typedLink(target: TypedRouteTarget, paramsOrOptions?: TypedRouteParams | TypedRouteOptions): string;
|
|
21
|
+
export declare function resolveTypedRouteTarget(target: TypedRouteTarget, paramsOrOptions?: TypedRouteParams | TypedRouteOptions): string;
|
|
22
|
+
export declare function createTypedRoute(path: string): TypedRouteDefinition;
|
|
23
|
+
export declare function typedNavigate(target: TypedRouteTarget, paramsOrOptions?: TypedRouteParams | TypedRouteOptions): Promise<void>;
|
|
24
|
+
export declare function typedPrefetch(target: TypedRouteTarget, paramsOrOptions?: TypedRouteParams | TypedRouteOptions): Promise<void>;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
function isTypedRouteOptions(value) {
|
|
2
|
+
return "params" in value || "search" in value || "hash" in value;
|
|
3
|
+
}
|
|
4
|
+
function isTypedRouteDefinition(value) {
|
|
5
|
+
return typeof value !== "string" && typeof value.build === "function" && typeof value.path === "string";
|
|
6
|
+
}
|
|
7
|
+
export function extractRouteParamKeys(path) {
|
|
8
|
+
return [...path.matchAll(/\[\.\.\.(\w+)\]|\[(\w+)\]/g)].map((match) => match[1] ?? match[2]).filter(Boolean);
|
|
9
|
+
}
|
|
10
|
+
export function buildSearchParams(search = {}) {
|
|
11
|
+
const params = new URLSearchParams;
|
|
12
|
+
for (const [key, value] of Object.entries(search)) {
|
|
13
|
+
if (value === void 0 || value === null)
|
|
14
|
+
continue;
|
|
15
|
+
if (Array.isArray(value)) {
|
|
16
|
+
for (const item of value)
|
|
17
|
+
params.append(key, String(item));
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
params.set(key, String(value));
|
|
21
|
+
}
|
|
22
|
+
const query = params.toString();
|
|
23
|
+
return query ? `?${query}` : "";
|
|
24
|
+
}
|
|
25
|
+
export function buildTypedPath(path, params = {}, strict = !1) {
|
|
26
|
+
return path.replace(/\[\.\.\.(\w+)\]/g, (_, key) => {
|
|
27
|
+
const value = params[key];
|
|
28
|
+
if (strict && !value)
|
|
29
|
+
throw Error(`Missing route param: ${key}`);
|
|
30
|
+
return value ?? "";
|
|
31
|
+
}).replace(/\[(\w+)\]/g, (_, key) => {
|
|
32
|
+
const value = params[key];
|
|
33
|
+
if (strict && !value)
|
|
34
|
+
throw Error(`Missing route param: ${key}`);
|
|
35
|
+
return encodeURIComponent(value ?? "");
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
export function typedLink(target, paramsOrOptions = {}) {
|
|
39
|
+
if (isTypedRouteDefinition(target))
|
|
40
|
+
return target.build(paramsOrOptions);
|
|
41
|
+
const options = isTypedRouteOptions(paramsOrOptions) ? paramsOrOptions : { params: paramsOrOptions }, pathname = buildTypedPath(target, options.params ?? {}), search = buildSearchParams(options.search), hash = options.hash ? `#${options.hash.replace(/^#/, "")}` : "";
|
|
42
|
+
return `${pathname}${search}${hash}`;
|
|
43
|
+
}
|
|
44
|
+
export function resolveTypedRouteTarget(target, paramsOrOptions = {}) {
|
|
45
|
+
return typedLink(target, paramsOrOptions);
|
|
46
|
+
}
|
|
47
|
+
export function createTypedRoute(path) {
|
|
48
|
+
return {
|
|
49
|
+
path,
|
|
50
|
+
params() {
|
|
51
|
+
return extractRouteParamKeys(path);
|
|
52
|
+
},
|
|
53
|
+
build(options = {}) {
|
|
54
|
+
return typedLink(path, options);
|
|
55
|
+
},
|
|
56
|
+
buildStrict(options) {
|
|
57
|
+
const pathname = buildTypedPath(path, options.params ?? {}, !0), search = buildSearchParams(options.search), hash = options.hash ? `#${options.hash.replace(/^#/, "")}` : "";
|
|
58
|
+
return `${pathname}${search}${hash}`;
|
|
59
|
+
},
|
|
60
|
+
navigate(options = {}) {
|
|
61
|
+
return typedNavigate(path, options);
|
|
62
|
+
},
|
|
63
|
+
prefetch(options = {}) {
|
|
64
|
+
return typedPrefetch(path, options);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
export function typedNavigate(target, paramsOrOptions = {}) {
|
|
69
|
+
const url = typedLink(target, paramsOrOptions);
|
|
70
|
+
return import("./router.js").then((m) => m.navigate(url));
|
|
71
|
+
}
|
|
72
|
+
export function typedPrefetch(target, paramsOrOptions = {}) {
|
|
73
|
+
const url = typedLink(target, paramsOrOptions);
|
|
74
|
+
return import("./router.js").then((m) => {
|
|
75
|
+
m.prefetch(url);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export type FormFieldKind = "string" | "number" | "boolean" | "json";
|
|
2
|
+
export interface FieldRule<T = unknown> {
|
|
3
|
+
required?: boolean;
|
|
4
|
+
minLength?: number;
|
|
5
|
+
maxLength?: number;
|
|
6
|
+
pattern?: RegExp;
|
|
7
|
+
min?: number;
|
|
8
|
+
max?: number;
|
|
9
|
+
custom?: (value: T, data: Record<string, unknown>) => string | null;
|
|
10
|
+
}
|
|
11
|
+
export interface FormField<TName extends string = string, TValue = string> {
|
|
12
|
+
name: TName;
|
|
13
|
+
rules: FieldRule<TValue>;
|
|
14
|
+
label?: string;
|
|
15
|
+
kind?: FormFieldKind;
|
|
16
|
+
coerce?: (value: string | undefined) => TValue;
|
|
17
|
+
}
|
|
18
|
+
export interface FormSchema<TData extends Record<string, unknown> = Record<string, unknown>> {
|
|
19
|
+
fields: FormField[];
|
|
20
|
+
formRules?: Array<(data: Partial<TData>) => ValidationError | null>;
|
|
21
|
+
}
|
|
22
|
+
export interface ValidationError {
|
|
23
|
+
field: string;
|
|
24
|
+
message: string;
|
|
25
|
+
}
|
|
26
|
+
export interface ValidationResult<T> {
|
|
27
|
+
valid: boolean;
|
|
28
|
+
data: T | null;
|
|
29
|
+
errors: ValidationError[];
|
|
30
|
+
fieldErrors: Record<string, string[]>;
|
|
31
|
+
formErrors: string[];
|
|
32
|
+
}
|
|
33
|
+
export interface ActionValidationResult<T> extends ValidationResult<T> {
|
|
34
|
+
values: Record<string, string>;
|
|
35
|
+
}
|
|
36
|
+
export declare function defineForm<TData extends Record<string, unknown> = Record<string, unknown>>(fields: FormField[], options?: Pick<FormSchema<TData>, "formRules">): FormSchema<TData>;
|
|
37
|
+
export declare function toFieldErrors(errors: ValidationError[]): Record<string, string[]>;
|
|
38
|
+
export declare function validateForm<T extends Record<string, unknown>>(data: Record<string, string>, schema: FormSchema<T>): ValidationResult<T>;
|
|
39
|
+
export declare function validateAction<T extends Record<string, unknown>>(request: Request, schema: FormSchema<T>): Promise<ActionValidationResult<T>>;
|
|
40
|
+
export declare function fieldAttrs(field: FormField): Record<string, unknown>;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
export function defineForm(fields, options = {}) {
|
|
2
|
+
return { fields, formRules: options.formRules ?? [] };
|
|
3
|
+
}
|
|
4
|
+
function coerceFieldValue(field, value) {
|
|
5
|
+
if (field.coerce)
|
|
6
|
+
return field.coerce(value);
|
|
7
|
+
const raw = value ?? "";
|
|
8
|
+
switch (field.kind ?? (field.rules.min !== void 0 || field.rules.max !== void 0 ? "number" : void 0)) {
|
|
9
|
+
case "number":
|
|
10
|
+
return raw === "" ? void 0 : Number(raw);
|
|
11
|
+
case "boolean":
|
|
12
|
+
return raw === "true" || raw === "on" || raw === "1";
|
|
13
|
+
case "json":
|
|
14
|
+
return raw === "" ? void 0 : JSON.parse(raw);
|
|
15
|
+
default:
|
|
16
|
+
return raw;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function validateField(rawValue, field, data) {
|
|
20
|
+
const { rules, label } = field, name = label ?? field.name, raw = rawValue ?? "";
|
|
21
|
+
if (rules.required && !raw.trim())
|
|
22
|
+
return { field: field.name, message: `${name} is required` };
|
|
23
|
+
if (!raw && !rules.required)
|
|
24
|
+
return null;
|
|
25
|
+
if (rules.minLength !== void 0 && raw.length < rules.minLength)
|
|
26
|
+
return { field: field.name, message: `${name} must be at least ${rules.minLength} characters` };
|
|
27
|
+
if (rules.maxLength !== void 0 && raw.length > rules.maxLength)
|
|
28
|
+
return { field: field.name, message: `${name} must be at most ${rules.maxLength} characters` };
|
|
29
|
+
if (rules.pattern && !rules.pattern.test(raw))
|
|
30
|
+
return { field: field.name, message: `${name} format is invalid` };
|
|
31
|
+
const coerced = coerceFieldValue(field, rawValue);
|
|
32
|
+
if (rules.min !== void 0 && typeof coerced === "number" && coerced < rules.min)
|
|
33
|
+
return { field: field.name, message: `${name} must be at least ${rules.min}` };
|
|
34
|
+
if (rules.max !== void 0 && typeof coerced === "number" && coerced > rules.max)
|
|
35
|
+
return { field: field.name, message: `${name} must be at most ${rules.max}` };
|
|
36
|
+
if (rules.custom) {
|
|
37
|
+
const message = rules.custom(coerced, data);
|
|
38
|
+
if (message)
|
|
39
|
+
return { field: field.name, message };
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
export function toFieldErrors(errors) {
|
|
44
|
+
const grouped = {};
|
|
45
|
+
for (const error of errors) {
|
|
46
|
+
const key = error.field || "$form";
|
|
47
|
+
grouped[key] ??= [];
|
|
48
|
+
grouped[key].push(error.message);
|
|
49
|
+
}
|
|
50
|
+
return grouped;
|
|
51
|
+
}
|
|
52
|
+
export function validateForm(data, schema) {
|
|
53
|
+
const errors = [], coercedData = {};
|
|
54
|
+
for (const field of schema.fields) {
|
|
55
|
+
const raw = data[field.name], error = validateField(raw, field, coercedData);
|
|
56
|
+
if (error) {
|
|
57
|
+
errors.push(error);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const coerced = coerceFieldValue(field, raw);
|
|
61
|
+
if (coerced !== void 0)
|
|
62
|
+
coercedData[field.name] = coerced;
|
|
63
|
+
else if (raw !== void 0 && field.kind === void 0 && field.rules.min === void 0 && field.rules.max === void 0)
|
|
64
|
+
coercedData[field.name] = raw;
|
|
65
|
+
}
|
|
66
|
+
if (errors.length === 0)
|
|
67
|
+
for (const rule of schema.formRules ?? []) {
|
|
68
|
+
const error = rule(coercedData);
|
|
69
|
+
if (error)
|
|
70
|
+
errors.push(error);
|
|
71
|
+
}
|
|
72
|
+
const fieldErrors = toFieldErrors(errors.filter((error) => error.field !== "$form")), formErrors = errors.filter((error) => error.field === "$form").map((error) => error.message);
|
|
73
|
+
return {
|
|
74
|
+
valid: errors.length === 0,
|
|
75
|
+
data: errors.length === 0 ? coercedData : null,
|
|
76
|
+
errors,
|
|
77
|
+
fieldErrors,
|
|
78
|
+
formErrors
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function normalizeInput(input) {
|
|
82
|
+
if (input instanceof FormData) {
|
|
83
|
+
const data = {};
|
|
84
|
+
input.forEach((value, key) => {
|
|
85
|
+
data[key] = String(value);
|
|
86
|
+
});
|
|
87
|
+
return data;
|
|
88
|
+
}
|
|
89
|
+
return input;
|
|
90
|
+
}
|
|
91
|
+
export async function validateAction(request, schema) {
|
|
92
|
+
const contentType = request.headers.get("content-type") ?? "";
|
|
93
|
+
let data;
|
|
94
|
+
if (contentType.includes("application/json")) {
|
|
95
|
+
const json = await request.json();
|
|
96
|
+
data = Object.fromEntries(Object.entries(json).map(([key, value]) => [key, String(value ?? "")]));
|
|
97
|
+
} else
|
|
98
|
+
data = normalizeInput(await request.formData());
|
|
99
|
+
return { ...validateForm(data, schema), values: data };
|
|
100
|
+
}
|
|
101
|
+
export function fieldAttrs(field) {
|
|
102
|
+
const attrs = { name: field.name };
|
|
103
|
+
if (field.rules.required)
|
|
104
|
+
attrs.required = !0;
|
|
105
|
+
if (field.rules.minLength !== void 0)
|
|
106
|
+
attrs.minlength = field.rules.minLength;
|
|
107
|
+
if (field.rules.maxLength !== void 0)
|
|
108
|
+
attrs.maxlength = field.rules.maxLength;
|
|
109
|
+
if (field.rules.pattern)
|
|
110
|
+
attrs.pattern = field.rules.pattern.source;
|
|
111
|
+
if (field.rules.min !== void 0)
|
|
112
|
+
attrs.min = field.rules.min;
|
|
113
|
+
if (field.rules.max !== void 0)
|
|
114
|
+
attrs.max = field.rules.max;
|
|
115
|
+
if (field.kind === "number" || field.rules.min !== void 0 || field.rules.max !== void 0)
|
|
116
|
+
attrs.inputmode = "numeric";
|
|
117
|
+
if (field.kind === "boolean")
|
|
118
|
+
attrs.value = "true";
|
|
119
|
+
return attrs;
|
|
120
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { MiddlewareFn } from "../server/middleware.js";
|
|
2
|
+
export interface CORSOptions {
|
|
3
|
+
origin?: string | string[] | ((origin: string) => boolean);
|
|
4
|
+
methods?: string[];
|
|
5
|
+
allowHeaders?: string[];
|
|
6
|
+
exposeHeaders?: string[];
|
|
7
|
+
credentials?: boolean;
|
|
8
|
+
maxAge?: number;
|
|
9
|
+
}
|
|
10
|
+
export declare function cors(options?: CORSOptions): MiddlewareFn;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
const DEFAULT_METHODS = ["GET", "HEAD", "PUT", "PATCH", "POST", "DELETE"], DEFAULT_HEADERS = ["Content-Type", "Authorization", "X-Requested-With"];
|
|
2
|
+
function isOriginAllowed(origin, allowed) {
|
|
3
|
+
if (!allowed)
|
|
4
|
+
return !1;
|
|
5
|
+
if (allowed === "*")
|
|
6
|
+
return !0;
|
|
7
|
+
if (typeof allowed === "string")
|
|
8
|
+
return origin === allowed;
|
|
9
|
+
if (Array.isArray(allowed))
|
|
10
|
+
return allowed.includes(origin);
|
|
11
|
+
if (typeof allowed === "function")
|
|
12
|
+
return allowed(origin);
|
|
13
|
+
return !1;
|
|
14
|
+
}
|
|
15
|
+
export function cors(options = {}) {
|
|
16
|
+
const {
|
|
17
|
+
origin = "*",
|
|
18
|
+
methods = DEFAULT_METHODS,
|
|
19
|
+
allowHeaders = DEFAULT_HEADERS,
|
|
20
|
+
exposeHeaders = [],
|
|
21
|
+
credentials = !1,
|
|
22
|
+
maxAge = 86400
|
|
23
|
+
} = options;
|
|
24
|
+
return async (ctx, next) => {
|
|
25
|
+
const requestOrigin = ctx.request.headers.get("origin") ?? "";
|
|
26
|
+
if (ctx.request.method === "OPTIONS") {
|
|
27
|
+
const headers = new Headers;
|
|
28
|
+
if (origin === "*" && !credentials)
|
|
29
|
+
headers.set("Access-Control-Allow-Origin", "*");
|
|
30
|
+
else if (isOriginAllowed(requestOrigin, origin)) {
|
|
31
|
+
headers.set("Access-Control-Allow-Origin", requestOrigin);
|
|
32
|
+
headers.set("Vary", "Origin");
|
|
33
|
+
}
|
|
34
|
+
headers.set("Access-Control-Allow-Methods", methods.join(", "));
|
|
35
|
+
headers.set("Access-Control-Allow-Headers", allowHeaders.join(", "));
|
|
36
|
+
if (exposeHeaders.length > 0)
|
|
37
|
+
headers.set("Access-Control-Expose-Headers", exposeHeaders.join(", "));
|
|
38
|
+
if (credentials)
|
|
39
|
+
headers.set("Access-Control-Allow-Credentials", "true");
|
|
40
|
+
headers.set("Access-Control-Max-Age", String(maxAge));
|
|
41
|
+
return new Response(null, { status: 204, headers });
|
|
42
|
+
}
|
|
43
|
+
const response = await next();
|
|
44
|
+
if (origin === "*" && !credentials)
|
|
45
|
+
response.headers.set("Access-Control-Allow-Origin", "*");
|
|
46
|
+
else if (isOriginAllowed(requestOrigin, origin)) {
|
|
47
|
+
response.headers.set("Access-Control-Allow-Origin", requestOrigin);
|
|
48
|
+
response.headers.append("Vary", "Origin");
|
|
49
|
+
}
|
|
50
|
+
if (credentials)
|
|
51
|
+
response.headers.set("Access-Control-Allow-Credentials", "true");
|
|
52
|
+
if (exposeHeaders.length > 0)
|
|
53
|
+
response.headers.set("Access-Control-Expose-Headers", exposeHeaders.join(", "));
|
|
54
|
+
return response;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { MiddlewareFn } from "../server/middleware.js";
|
|
2
|
+
export declare function generateCSRFToken(): string;
|
|
3
|
+
export declare function validateCSRFToken(request: Request, secret: string): Promise<boolean>;
|
|
4
|
+
export declare function csrfProtection(secret: string): Promise<{
|
|
5
|
+
token: string;
|
|
6
|
+
cookie: string;
|
|
7
|
+
headerName: string;
|
|
8
|
+
}>;
|
|
9
|
+
export declare function createCSRFMiddleware(secret: string): MiddlewareFn;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { timingSafeEqual } from "node:crypto";
|
|
2
|
+
const CSRF_COOKIE = "__gorsee_csrf", CSRF_HEADER = "x-gorsee-csrf", TOKEN_LENGTH = 32;
|
|
3
|
+
function randomBytes(length) {
|
|
4
|
+
const bytes = new Uint8Array(length);
|
|
5
|
+
crypto.getRandomValues(bytes);
|
|
6
|
+
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
7
|
+
}
|
|
8
|
+
async function hmacSign(token, secret) {
|
|
9
|
+
const key = await crypto.subtle.importKey("raw", new TextEncoder().encode(secret), { name: "HMAC", hash: "SHA-256" }, !1, ["sign"]), sig = await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(token));
|
|
10
|
+
return Array.from(new Uint8Array(sig), (b) => b.toString(16).padStart(2, "0")).join("");
|
|
11
|
+
}
|
|
12
|
+
export function generateCSRFToken() {
|
|
13
|
+
return randomBytes(TOKEN_LENGTH);
|
|
14
|
+
}
|
|
15
|
+
export async function validateCSRFToken(request, secret) {
|
|
16
|
+
if (["GET", "HEAD", "OPTIONS"].includes(request.method))
|
|
17
|
+
return !0;
|
|
18
|
+
const cookieHeader = request.headers.get("cookie") ?? "", headerToken = request.headers.get(CSRF_HEADER) ?? "";
|
|
19
|
+
let cookieToken = "";
|
|
20
|
+
for (const pair of cookieHeader.split(";")) {
|
|
21
|
+
const [key, ...rest] = pair.trim().split("=");
|
|
22
|
+
if (key === CSRF_COOKIE) {
|
|
23
|
+
cookieToken = rest.join("=");
|
|
24
|
+
break;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (!cookieToken || !headerToken)
|
|
28
|
+
return !1;
|
|
29
|
+
const [token, signature] = cookieToken.split(".");
|
|
30
|
+
if (!token || !signature)
|
|
31
|
+
return !1;
|
|
32
|
+
if (headerToken !== token)
|
|
33
|
+
return !1;
|
|
34
|
+
const expectedSig = await hmacSign(token, secret);
|
|
35
|
+
if (signature.length !== expectedSig.length)
|
|
36
|
+
return !1;
|
|
37
|
+
return timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSig));
|
|
38
|
+
}
|
|
39
|
+
export async function csrfProtection(secret) {
|
|
40
|
+
const token = generateCSRFToken(), signature = await hmacSign(token, secret), cookieValue = `${token}.${signature}`;
|
|
41
|
+
return {
|
|
42
|
+
token,
|
|
43
|
+
cookie: `${CSRF_COOKIE}=${cookieValue}; Path=/; SameSite=Lax; Secure`,
|
|
44
|
+
headerName: CSRF_HEADER
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export function createCSRFMiddleware(secret) {
|
|
48
|
+
return async (ctx, next) => {
|
|
49
|
+
if (!await validateCSRFToken(ctx.request, secret))
|
|
50
|
+
return new Response("Invalid CSRF token", { status: 403 });
|
|
51
|
+
return next();
|
|
52
|
+
};
|
|
53
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface SecurityConfig {
|
|
2
|
+
csp: boolean;
|
|
3
|
+
hsts: boolean;
|
|
4
|
+
csrf: boolean;
|
|
5
|
+
rateLimit: {
|
|
6
|
+
requests: number;
|
|
7
|
+
window: string;
|
|
8
|
+
} | false;
|
|
9
|
+
nonce?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function securityHeaders(config?: Partial<SecurityConfig>, nonce?: string): Record<string, string>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const DEFAULT_CONFIG = {
|
|
2
|
+
csp: !0,
|
|
3
|
+
hsts: !0,
|
|
4
|
+
csrf: !0,
|
|
5
|
+
rateLimit: { requests: 100, window: "1m" }
|
|
6
|
+
};
|
|
7
|
+
export function securityHeaders(config = {}, nonce) {
|
|
8
|
+
const cfg = { ...DEFAULT_CONFIG, ...config }, headers = {};
|
|
9
|
+
if (cfg.csp) {
|
|
10
|
+
const scriptSrc = nonce ? `'nonce-${nonce}'` : "'self'";
|
|
11
|
+
headers["Content-Security-Policy"] = [
|
|
12
|
+
"default-src 'self'",
|
|
13
|
+
`script-src ${scriptSrc}`,
|
|
14
|
+
"style-src 'self' 'unsafe-inline'",
|
|
15
|
+
"img-src 'self' data: https:",
|
|
16
|
+
"font-src 'self'",
|
|
17
|
+
"connect-src 'self' ws: wss:",
|
|
18
|
+
"frame-ancestors 'none'",
|
|
19
|
+
"base-uri 'self'",
|
|
20
|
+
"form-action 'self'"
|
|
21
|
+
].join("; ");
|
|
22
|
+
}
|
|
23
|
+
if (cfg.hsts)
|
|
24
|
+
headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains; preload";
|
|
25
|
+
headers["X-Content-Type-Options"] = "nosniff";
|
|
26
|
+
headers["X-Frame-Options"] = "DENY";
|
|
27
|
+
headers["Referrer-Policy"] = "strict-origin-when-cross-origin";
|
|
28
|
+
headers["X-XSS-Protection"] = "0";
|
|
29
|
+
headers["Permissions-Policy"] = "camera=(), microphone=(), geolocation=()";
|
|
30
|
+
return headers;
|
|
31
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { securityHeaders, type SecurityConfig } from "./headers.js";
|
|
2
|
+
export { csrfProtection, createCSRFMiddleware, generateCSRFToken, validateCSRFToken } from "./csrf.js";
|
|
3
|
+
export { createRateLimiter, parseRateLimitWindow, type RateLimiter } from "./rate-limit.js";
|
|
4
|
+
export { createRedisRateLimiter, type AsyncRateLimiter, type AsyncRateLimitResult } from "./redis-rate-limit.js";
|
|
5
|
+
export { cors, type CORSOptions } from "./cors.js";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { securityHeaders } from "./headers.js";
|
|
2
|
+
export { csrfProtection, createCSRFMiddleware, generateCSRFToken, validateCSRFToken } from "./csrf.js";
|
|
3
|
+
export { createRateLimiter, parseRateLimitWindow } from "./rate-limit.js";
|
|
4
|
+
export { createRedisRateLimiter } from "./redis-rate-limit.js";
|
|
5
|
+
export { cors } from "./cors.js";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface RateLimiter {
|
|
2
|
+
check(key: string): {
|
|
3
|
+
allowed: boolean;
|
|
4
|
+
remaining: number;
|
|
5
|
+
resetAt: number;
|
|
6
|
+
};
|
|
7
|
+
reset(key: string): void;
|
|
8
|
+
}
|
|
9
|
+
export declare function parseRateLimitWindow(window: string): number;
|
|
10
|
+
export declare function createRateLimiter(maxRequests: number, window: string): RateLimiter;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export function parseRateLimitWindow(window) {
|
|
2
|
+
const match = window.match(/^(\d+)(s|m|h)$/);
|
|
3
|
+
if (!match)
|
|
4
|
+
throw Error(`Invalid rate limit window: "${window}"`);
|
|
5
|
+
const value = Number(match[1]);
|
|
6
|
+
switch (match[2]) {
|
|
7
|
+
case "s":
|
|
8
|
+
return value * 1000;
|
|
9
|
+
case "m":
|
|
10
|
+
return value * 60000;
|
|
11
|
+
case "h":
|
|
12
|
+
return value * 3600000;
|
|
13
|
+
default:
|
|
14
|
+
throw Error(`Invalid unit: ${match[2]}`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export function createRateLimiter(maxRequests, window) {
|
|
18
|
+
const windowMs = parseRateLimitWindow(window), buckets = new Map, CLEANUP_INTERVAL = Math.max(windowMs * 2, 60000), cleanup = setInterval(() => {
|
|
19
|
+
const now = Date.now();
|
|
20
|
+
for (const [key, bucket] of buckets)
|
|
21
|
+
if (now - bucket.lastRefill > windowMs * 2)
|
|
22
|
+
buckets.delete(key);
|
|
23
|
+
}, CLEANUP_INTERVAL);
|
|
24
|
+
if (typeof cleanup === "object" && "unref" in cleanup)
|
|
25
|
+
cleanup.unref();
|
|
26
|
+
return {
|
|
27
|
+
check(key) {
|
|
28
|
+
const now = Date.now();
|
|
29
|
+
let bucket = buckets.get(key);
|
|
30
|
+
if (!bucket) {
|
|
31
|
+
bucket = { tokens: maxRequests, lastRefill: now };
|
|
32
|
+
buckets.set(key, bucket);
|
|
33
|
+
}
|
|
34
|
+
if (now - bucket.lastRefill >= windowMs) {
|
|
35
|
+
bucket.tokens = maxRequests;
|
|
36
|
+
bucket.lastRefill = now;
|
|
37
|
+
}
|
|
38
|
+
const resetAt = bucket.lastRefill + windowMs;
|
|
39
|
+
if (bucket.tokens > 0) {
|
|
40
|
+
bucket.tokens--;
|
|
41
|
+
return { allowed: !0, remaining: bucket.tokens, resetAt };
|
|
42
|
+
}
|
|
43
|
+
return { allowed: !1, remaining: 0, resetAt };
|
|
44
|
+
},
|
|
45
|
+
reset(key) {
|
|
46
|
+
buckets.delete(key);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type RedisLikeClient } from "../server/redis-client.js";
|
|
2
|
+
export interface AsyncRateLimitResult {
|
|
3
|
+
allowed: boolean;
|
|
4
|
+
remaining: number;
|
|
5
|
+
resetAt: number;
|
|
6
|
+
}
|
|
7
|
+
export interface AsyncRateLimiter {
|
|
8
|
+
check(key: string): Promise<AsyncRateLimitResult>;
|
|
9
|
+
reset(key: string): Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
export interface RedisRateLimiterOptions {
|
|
12
|
+
prefix?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function createRedisRateLimiter(client: RedisLikeClient, maxRequests: number, window: string, options?: RedisRateLimiterOptions): AsyncRateLimiter;
|