@slopware/sloppy-darwin-x64 0.1.0-alpha.0
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/LICENSE +201 -0
- package/README.md +5 -0
- package/bin/sloppy +0 -0
- package/bin/sloppyc +0 -0
- package/docs/KNOWN_LIMITATIONS.md +16 -0
- package/docs/LICENSES.md +6 -0
- package/docs/NOTICE.md +8 -0
- package/examples/README.md +140 -0
- package/examples/auth-api/README.md +20 -0
- package/examples/auth-api/app.js +61 -0
- package/examples/auth-api/appsettings.json +7 -0
- package/examples/auth-api/sloppy.json +5 -0
- package/examples/cache-basic/README.md +9 -0
- package/examples/cache-basic/app.js +32 -0
- package/examples/cache-hybrid-postgres/README.md +10 -0
- package/examples/cache-hybrid-postgres/app.js +27 -0
- package/examples/cache-output-api/README.md +10 -0
- package/examples/cache-output-api/app.js +35 -0
- package/examples/codec-base64-hex/README.md +14 -0
- package/examples/codec-base64-hex/app.js +15 -0
- package/examples/codec-checksums/README.md +15 -0
- package/examples/codec-checksums/app.js +8 -0
- package/examples/codec-compression/README.md +13 -0
- package/examples/codec-compression/app.js +9 -0
- package/examples/codec-streaming-compression/README.md +19 -0
- package/examples/codec-streaming-compression/app.js +16 -0
- package/examples/codec-text-binary/README.md +16 -0
- package/examples/codec-text-binary/app.js +17 -0
- package/examples/compiler-hello/README.md +71 -0
- package/examples/compiler-hello/app.js +7 -0
- package/examples/compiler-hello/expected/app.js +8 -0
- package/examples/compiler-hello/expected/app.js.map +53 -0
- package/examples/compiler-hello/expected/app.plan.json +229 -0
- package/examples/compiler-hello/expected/routes.slrt +0 -0
- package/examples/config-basic/README.md +13 -0
- package/examples/config-basic/app.js +13 -0
- package/examples/config-basic/appsettings.json +7 -0
- package/examples/config-secrets-redaction/README.md +9 -0
- package/examples/config-secrets-redaction/app.js +9 -0
- package/examples/config-secrets-redaction/appsettings.json +5 -0
- package/examples/config-strict-mode/README.md +7 -0
- package/examples/config-strict-mode/app.js +10 -0
- package/examples/config-strict-mode/appsettings.json +7 -0
- package/examples/configured-api/README.md +38 -0
- package/examples/configured-api/app.js +12 -0
- package/examples/configured-api/appsettings.Development.json +5 -0
- package/examples/configured-api/appsettings.json +6 -0
- package/examples/configured-api/sloppy.json +5 -0
- package/examples/core-config-secrets/README.md +10 -0
- package/examples/core-config-secrets/app.js +15 -0
- package/examples/core-fs-time-codec/README.md +9 -0
- package/examples/core-fs-time-codec/app.js +8 -0
- package/examples/core-network-time-codec/README.md +11 -0
- package/examples/core-network-time-codec/app.js +20 -0
- package/examples/core-policy-audit/README.md +7 -0
- package/examples/core-policy-audit/app.js +22 -0
- package/examples/core-process-time-codec/README.md +8 -0
- package/examples/core-process-time-codec/app.js +28 -0
- package/examples/core-worker-time/README.md +8 -0
- package/examples/core-worker-time/app.js +17 -0
- package/examples/crypto-hash-hmac/README.md +17 -0
- package/examples/crypto-hash-hmac/app.js +29 -0
- package/examples/crypto-password/README.md +21 -0
- package/examples/crypto-password/app.js +12 -0
- package/examples/crypto-random-token/README.md +16 -0
- package/examples/crypto-random-token/app.js +12 -0
- package/examples/crypto-secret-constant-time/README.md +21 -0
- package/examples/crypto-secret-constant-time/app.js +15 -0
- package/examples/data-foundation/README.md +39 -0
- package/examples/data-foundation/app.js +63 -0
- package/examples/dependency-graph/README.md +19 -0
- package/examples/dependency-graph/fixtures/graph-helper/index.js +3 -0
- package/examples/dependency-graph/fixtures/graph-helper/package.json +6 -0
- package/examples/dependency-graph/package.json +7 -0
- package/examples/dependency-graph/public/message.txt +1 -0
- package/examples/dependency-graph/sloppy.json +9 -0
- package/examples/dependency-graph/src/main.ts +8 -0
- package/examples/dogfood/README.md +23 -0
- package/examples/dogfood/dogfood.json +136 -0
- package/examples/dynamic-module-include/README.md +20 -0
- package/examples/dynamic-module-include/public/readme.txt +1 -0
- package/examples/dynamic-module-include/sloppy.json +12 -0
- package/examples/dynamic-module-include/src/main.ts +6 -0
- package/examples/dynamic-module-include/src/plugins/alpha.js +3 -0
- package/examples/dynamic-module-include/src/plugins/beta.js +3 -0
- package/examples/ergonomics/README.md +42 -0
- package/examples/ergonomics/app.js +38 -0
- package/examples/framework-controller/README.md +12 -0
- package/examples/framework-controller/app.js +31 -0
- package/examples/framework-di-services/README.md +17 -0
- package/examples/framework-di-services/app.ts +40 -0
- package/examples/framework-explicit-binding/README.md +12 -0
- package/examples/framework-explicit-binding/app.ts +34 -0
- package/examples/framework-hello/README.md +16 -0
- package/examples/framework-hello/app.ts +16 -0
- package/examples/framework-postgres-crud/README.md +73 -0
- package/examples/framework-postgres-crud/app.ts +64 -0
- package/examples/framework-sqlite-crud/README.md +52 -0
- package/examples/framework-sqlite-crud/app.ts +90 -0
- package/examples/framework-sqlite-crud/appsettings.json +11 -0
- package/examples/framework-sqlserver-crud/README.md +73 -0
- package/examples/framework-sqlserver-crud/app.ts +64 -0
- package/examples/framework-validation-errors/README.md +12 -0
- package/examples/framework-validation-errors/app.ts +16 -0
- package/examples/fs-basic/README.md +24 -0
- package/examples/fs-basic/app.js +12 -0
- package/examples/fs-roots-policy/README.md +14 -0
- package/examples/fs-roots-policy/app.js +4 -0
- package/examples/fs-streams/README.md +18 -0
- package/examples/fs-streams/app.js +11 -0
- package/examples/fs-watch/README.md +19 -0
- package/examples/fs-watch/app.js +11 -0
- package/examples/hello/README.md +63 -0
- package/examples/hello/app.js +19 -0
- package/examples/hello-minimal/README.md +51 -0
- package/examples/hello-minimal/sloppy.json +5 -0
- package/examples/hello-minimal/src/main.ts +9 -0
- package/examples/http-client-basic/README.md +11 -0
- package/examples/http-client-basic/app.js +46 -0
- package/examples/http-client-generated/README.md +22 -0
- package/examples/http-client-generated/openapi.json +45 -0
- package/examples/http-client-resilience/README.md +4 -0
- package/examples/http-client-resilience/app.js +38 -0
- package/examples/http-client-runtime-loopback/README.md +24 -0
- package/examples/http-client-testhost/README.md +4 -0
- package/examples/http-client-testhost/app.js +27 -0
- package/examples/http-client-testhost-package-mock/README.md +26 -0
- package/examples/http-client-typed/README.md +5 -0
- package/examples/http-client-typed/app.js +33 -0
- package/examples/modules-api/README.md +30 -0
- package/examples/modules-api/app.js +9 -0
- package/examples/modules-api/modules/routes.js +16 -0
- package/examples/modules-api/sloppy.json +5 -0
- package/examples/modules-basic/README.md +32 -0
- package/examples/modules-basic/app.js +41 -0
- package/examples/net-deadline-cancel/README.md +13 -0
- package/examples/net-deadline-cancel/app.js +34 -0
- package/examples/net-local-ipc/README.md +12 -0
- package/examples/net-local-ipc/app.js +46 -0
- package/examples/net-policy-strict/README.md +12 -0
- package/examples/net-policy-strict/app.js +34 -0
- package/examples/net-tcp-client/README.md +10 -0
- package/examples/net-tcp-client/app.js +23 -0
- package/examples/net-tcp-echo/README.md +11 -0
- package/examples/net-tcp-echo/app.js +45 -0
- package/examples/net-tcp-server/README.md +10 -0
- package/examples/net-tcp-server/app.js +28 -0
- package/examples/node-compat-path-events/README.md +15 -0
- package/examples/node-compat-path-events/sloppy.json +6 -0
- package/examples/node-compat-path-events/src/main.ts +15 -0
- package/examples/ops-compiler/README.md +9 -0
- package/examples/ops-compiler/app.js +26 -0
- package/examples/ops-health-metrics-management/README.md +14 -0
- package/examples/ops-health-metrics-management/app.js +24 -0
- package/examples/orm-basic/README.md +17 -0
- package/examples/orm-basic/app.js +82 -0
- package/examples/orm-cursor-export/README.md +16 -0
- package/examples/orm-cursor-export/app.js +28 -0
- package/examples/orm-migrations/README.md +14 -0
- package/examples/orm-migrations/migrations/.gitkeep +1 -0
- package/examples/orm-migrations/sloppy.json +9 -0
- package/examples/orm-migrations/src/app.ts +34 -0
- package/examples/orm-relations-includes/README.md +10 -0
- package/examples/orm-relations-includes/app.js +47 -0
- package/examples/orm-testservices/README.md +37 -0
- package/examples/orm-testservices/test.mjs +32 -0
- package/examples/os-runtime-api/README.md +11 -0
- package/examples/os-runtime-api/app.js +44 -0
- package/examples/package-zod-like/README.md +28 -0
- package/examples/package-zod-like/fixtures/zod-like/index.js +48 -0
- package/examples/package-zod-like/fixtures/zod-like/package.json +12 -0
- package/examples/package-zod-like/package.json +7 -0
- package/examples/package-zod-like/sloppy.json +6 -0
- package/examples/package-zod-like/src/main.ts +16 -0
- package/examples/postgres-basic/README.md +31 -0
- package/examples/postgres-basic/app.js +50 -0
- package/examples/prealpha-control-plane/README.md +50 -0
- package/examples/prealpha-control-plane/appsettings.Development.json +11 -0
- package/examples/prealpha-control-plane/appsettings.json +15 -0
- package/examples/prealpha-control-plane/sloppy.json +5 -0
- package/examples/prealpha-control-plane/src/db/schema.js +7 -0
- package/examples/prealpha-control-plane/src/db/seed.js +6 -0
- package/examples/prealpha-control-plane/src/main.js +21 -0
- package/examples/prealpha-control-plane/src/routes/apps.js +34 -0
- package/examples/prealpha-control-plane/src/routes/builds.js +25 -0
- package/examples/prealpha-control-plane/src/routes/deployments.js +19 -0
- package/examples/prealpha-control-plane/src/routes/diagnostics.js +11 -0
- package/examples/prealpha-control-plane/src/routes/health.js +27 -0
- package/examples/prealpha-control-plane/src/routes/projects.js +38 -0
- package/examples/prealpha-control-plane/src/services/diagnosticsSink.js +11 -0
- package/examples/prealpha-control-plane/src/services/repositories.js +9 -0
- package/examples/prealpha-control-plane/src/validation/schemas.js +6 -0
- package/examples/program-fs-process/README.md +31 -0
- package/examples/program-fs-process/sloppy.json +9 -0
- package/examples/program-fs-process/src/main.ts +27 -0
- package/examples/program-hello/README.md +32 -0
- package/examples/program-hello/main.ts +8 -0
- package/examples/program-hello/message.ts +1 -0
- package/examples/program-hello/sloppy.json +5 -0
- package/examples/rate-limit-auth/README.md +3 -0
- package/examples/rate-limit-auth/app.js +14 -0
- package/examples/rate-limit-basic/README.md +3 -0
- package/examples/rate-limit-basic/app.js +13 -0
- package/examples/rate-limit-redis/README.md +5 -0
- package/examples/rate-limit-redis/app.js +20 -0
- package/examples/rate-limit-testhost/README.md +4 -0
- package/examples/rate-limit-testhost/app.js +13 -0
- package/examples/rate-limit-websocket/README.md +3 -0
- package/examples/rate-limit-websocket/app.js +16 -0
- package/examples/realtime-auth/README.md +8 -0
- package/examples/realtime-auth/app.js +25 -0
- package/examples/realtime-auth/test.mjs +43 -0
- package/examples/realtime-chat/README.md +8 -0
- package/examples/realtime-chat/app.js +32 -0
- package/examples/realtime-chat/test.mjs +52 -0
- package/examples/realtime-dashboard/README.md +20 -0
- package/examples/realtime-dashboard/app.js +37 -0
- package/examples/realtime-presence/README.md +8 -0
- package/examples/realtime-presence/app.js +32 -0
- package/examples/realtime-presence/test.mjs +50 -0
- package/examples/realtime-testhost/README.md +8 -0
- package/examples/realtime-testhost/test.mjs +31 -0
- package/examples/redis-basic/README.md +17 -0
- package/examples/redis-basic/app.js +39 -0
- package/examples/redis-cache/README.md +14 -0
- package/examples/redis-cache/app.js +36 -0
- package/examples/redis-locks/README.md +13 -0
- package/examples/redis-locks/app.js +49 -0
- package/examples/request-context/README.md +32 -0
- package/examples/request-context/app.js +15 -0
- package/examples/sqlite-basic/README.md +52 -0
- package/examples/sqlite-basic/app.js +56 -0
- package/examples/sqlserver-basic/README.md +36 -0
- package/examples/sqlserver-basic/app.js +59 -0
- package/examples/static-files-basic/README.md +11 -0
- package/examples/static-files-basic/app.js +12 -0
- package/examples/static-files-basic/public/app.js +1 -0
- package/examples/static-files-basic/public/site.css +3 -0
- package/examples/static-files-package/README.md +12 -0
- package/examples/static-files-package/app.js +10 -0
- package/examples/static-files-package/public/index.html +2 -0
- package/examples/static-files-precompressed/README.md +12 -0
- package/examples/static-files-precompressed/app.js +11 -0
- package/examples/static-files-precompressed/public/app.js +1 -0
- package/examples/static-files-precompressed/public/app.js.br +0 -0
- package/examples/static-files-precompressed/public/app.js.gz +0 -0
- package/examples/static-files-spa/README.md +12 -0
- package/examples/static-files-spa/app.js +16 -0
- package/examples/static-files-spa/dist/assets/app.js +1 -0
- package/examples/static-files-spa/dist/index.html +4 -0
- package/examples/static-files-testhost/README.md +8 -0
- package/examples/static-files-testhost/app.js +13 -0
- package/examples/static-files-testhost/public/app.js +1 -0
- package/examples/static-files-testhost/public/app.js.gz +0 -0
- package/examples/static-files-testhost/test.mjs +38 -0
- package/examples/testhost-basic/README.md +26 -0
- package/examples/testhost-db/README.md +31 -0
- package/examples/testservices-postgres/README.md +68 -0
- package/examples/testservices-redis/README.md +71 -0
- package/examples/testservices-sqlserver/README.md +75 -0
- package/examples/time-basic/README.md +18 -0
- package/examples/time-basic/app.js +12 -0
- package/examples/time-deadline-cancellation/README.md +11 -0
- package/examples/time-deadline-cancellation/app.js +27 -0
- package/examples/time-fake-clock/README.md +14 -0
- package/examples/time-fake-clock/app.js +25 -0
- package/examples/time-interval-schedule/README.md +13 -0
- package/examples/time-interval-schedule/app.js +60 -0
- package/examples/users-api-sqlite/README.md +74 -0
- package/examples/users-api-sqlite/app.js +11 -0
- package/examples/users-api-sqlite/appsettings.Development.json +11 -0
- package/examples/users-api-sqlite/appsettings.json +11 -0
- package/examples/users-api-sqlite/modules/users.js +40 -0
- package/examples/users-api-sqlite/sloppy.json +5 -0
- package/examples/validation-errors/README.md +36 -0
- package/examples/validation-errors/app.js +14 -0
- package/examples/validation-errors/invalid-user.http +6 -0
- package/examples/validation-errors/sloppy.json +5 -0
- package/examples/web-dynamic-routes/README.md +17 -0
- package/examples/web-dynamic-routes/app.ts +27 -0
- package/examples/webhooks-basic/README.md +11 -0
- package/examples/webhooks-basic/app.js +48 -0
- package/examples/websocket-auth/README.md +8 -0
- package/examples/websocket-auth/app.js +16 -0
- package/examples/websocket-echo/README.md +9 -0
- package/examples/websocket-echo/app.js +36 -0
- package/examples/websocket-json-schema/README.md +5 -0
- package/examples/websocket-json-schema/app.js +25 -0
- package/examples/websocket-testhost/README.md +11 -0
- package/examples/websocket-testhost/test.mjs +49 -0
- package/examples/workers-background-service/README.md +7 -0
- package/examples/workers-background-service/app.js +16 -0
- package/examples/workers-js-isolate/README.md +8 -0
- package/examples/workers-js-isolate/app.js +19 -0
- package/examples/workers-js-isolate/workers/parser.ts +11 -0
- package/examples/workers-shutdown/README.md +6 -0
- package/examples/workers-shutdown/app.js +26 -0
- package/examples/workers-workerpool/README.md +6 -0
- package/examples/workers-workerpool/app.js +23 -0
- package/examples/workers-workqueue/README.md +8 -0
- package/examples/workers-workqueue/app.js +24 -0
- package/manifest.json +59 -0
- package/package.json +31 -0
- package/stdlib/sloppy/README.md +177 -0
- package/stdlib/sloppy/app.js +2142 -0
- package/stdlib/sloppy/auth.js +1813 -0
- package/stdlib/sloppy/bootstrap.manifest.json +83 -0
- package/stdlib/sloppy/cache.js +1542 -0
- package/stdlib/sloppy/codec.js +1153 -0
- package/stdlib/sloppy/config.js +61 -0
- package/stdlib/sloppy/crypto.js +312 -0
- package/stdlib/sloppy/data.js +2945 -0
- package/stdlib/sloppy/ffi.js +185 -0
- package/stdlib/sloppy/fs.js +795 -0
- package/stdlib/sloppy/health.js +603 -0
- package/stdlib/sloppy/http.js +1595 -0
- package/stdlib/sloppy/index.js +59 -0
- package/stdlib/sloppy/internal/bytes.js +31 -0
- package/stdlib/sloppy/internal/capabilities.js +155 -0
- package/stdlib/sloppy/internal/config.js +640 -0
- package/stdlib/sloppy/internal/disposable.js +31 -0
- package/stdlib/sloppy/internal/headers.js +63 -0
- package/stdlib/sloppy/internal/intrinsics.js +2 -0
- package/stdlib/sloppy/internal/json.js +20 -0
- package/stdlib/sloppy/internal/logging.js +278 -0
- package/stdlib/sloppy/internal/modules.js +405 -0
- package/stdlib/sloppy/internal/redaction.js +87 -0
- package/stdlib/sloppy/internal/routes.js +2279 -0
- package/stdlib/sloppy/internal/runtime-classic.js +19837 -0
- package/stdlib/sloppy/internal/services.js +690 -0
- package/stdlib/sloppy/internal/shared.js +32 -0
- package/stdlib/sloppy/internal/testhost-diagnostics.js +88 -0
- package/stdlib/sloppy/internal/testhost-http-server.js +238 -0
- package/stdlib/sloppy/internal/testhost-http.js +118 -0
- package/stdlib/sloppy/internal/testhost-loopback.js +50 -0
- package/stdlib/sloppy/internal/testservices-docker.js +154 -0
- package/stdlib/sloppy/internal/validation.js +117 -0
- package/stdlib/sloppy/metrics.js +427 -0
- package/stdlib/sloppy/net.js +5208 -0
- package/stdlib/sloppy/node/assert/strict.js +39 -0
- package/stdlib/sloppy/node/assert.js +228 -0
- package/stdlib/sloppy/node/buffer.js +247 -0
- package/stdlib/sloppy/node/console.js +33 -0
- package/stdlib/sloppy/node/constants.js +9 -0
- package/stdlib/sloppy/node/crypto.js +89 -0
- package/stdlib/sloppy/node/diagnostics_channel.js +41 -0
- package/stdlib/sloppy/node/events.js +113 -0
- package/stdlib/sloppy/node/fs/promises.js +27 -0
- package/stdlib/sloppy/node/fs.js +280 -0
- package/stdlib/sloppy/node/http.js +11 -0
- package/stdlib/sloppy/node/https.js +11 -0
- package/stdlib/sloppy/node/module.js +40 -0
- package/stdlib/sloppy/node/os.js +22 -0
- package/stdlib/sloppy/node/path.js +78 -0
- package/stdlib/sloppy/node/perf_hooks.js +12 -0
- package/stdlib/sloppy/node/process.js +129 -0
- package/stdlib/sloppy/node/querystring.js +21 -0
- package/stdlib/sloppy/node/stream/promises.js +3 -0
- package/stdlib/sloppy/node/stream.js +132 -0
- package/stdlib/sloppy/node/string_decoder.js +23 -0
- package/stdlib/sloppy/node/timers.js +26 -0
- package/stdlib/sloppy/node/tty.js +18 -0
- package/stdlib/sloppy/node/url.js +17 -0
- package/stdlib/sloppy/node/util.js +95 -0
- package/stdlib/sloppy/node/zlib.js +72 -0
- package/stdlib/sloppy/orm.js +2188 -0
- package/stdlib/sloppy/os.js +580 -0
- package/stdlib/sloppy/problem-details.js +29 -0
- package/stdlib/sloppy/providers/sqlite.js +26 -0
- package/stdlib/sloppy/rate-limit.js +856 -0
- package/stdlib/sloppy/realtime.js +1508 -0
- package/stdlib/sloppy/redis.js +1272 -0
- package/stdlib/sloppy/request-id.js +184 -0
- package/stdlib/sloppy/request-logging.js +101 -0
- package/stdlib/sloppy/results.js +933 -0
- package/stdlib/sloppy/schema.js +546 -0
- package/stdlib/sloppy/testing.js +4081 -0
- package/stdlib/sloppy/testservices.js +1041 -0
- package/stdlib/sloppy/time.js +894 -0
- package/stdlib/sloppy/webhooks.js +1330 -0
- package/stdlib/sloppy/workers.js +986 -0
- package/templates/api/README.md +82 -0
- package/templates/api/appsettings.Development.json +14 -0
- package/templates/api/appsettings.json +13 -0
- package/templates/api/data/.gitkeep +1 -0
- package/templates/api/gitignore +4 -0
- package/templates/api/migrations/0001_create_users.sql +1 -0
- package/templates/api/package.json +16 -0
- package/templates/api/public/hello.txt +1 -0
- package/templates/api/sloppy.json +14 -0
- package/templates/api/src/config.ts +1 -0
- package/templates/api/src/db/migrate.ts +14 -0
- package/templates/api/src/db/schema.ts +4 -0
- package/templates/api/src/db/usersRepository.ts +23 -0
- package/templates/api/src/main.ts +18 -0
- package/templates/api/src/models/user.ts +7 -0
- package/templates/api/src/routes/health.ts +20 -0
- package/templates/api/src/routes/users.ts +40 -0
- package/templates/api/src/services/usersService.ts +21 -0
- package/templates/api/tsconfig.json +15 -0
- package/templates/cli/README.md +16 -0
- package/templates/cli/gitignore +2 -0
- package/templates/cli/package.json +13 -0
- package/templates/cli/sloppy.json +6 -0
- package/templates/cli/src/commands/echo.ts +9 -0
- package/templates/cli/src/commands/inspect.ts +20 -0
- package/templates/cli/src/main.ts +50 -0
- package/templates/cli/tsconfig.json +15 -0
- package/templates/minimal-api/README.md +14 -0
- package/templates/minimal-api/gitignore +3 -0
- package/templates/minimal-api/package.json +14 -0
- package/templates/minimal-api/sloppy.json +5 -0
- package/templates/minimal-api/src/main.ts +9 -0
- package/templates/minimal-api/tsconfig.json +15 -0
- package/templates/node-compat/README.md +40 -0
- package/templates/node-compat/gitignore +2 -0
- package/templates/node-compat/package.json +11 -0
- package/templates/node-compat/sloppy.json +6 -0
- package/templates/node-compat/src/main.ts +40 -0
- package/templates/package-api/README.md +44 -0
- package/templates/package-api/fixtures/validator-lite/index.js +7 -0
- package/templates/package-api/fixtures/validator-lite/package.json +6 -0
- package/templates/package-api/gitignore +3 -0
- package/templates/package-api/package.json +17 -0
- package/templates/package-api/sloppy.json +5 -0
- package/templates/package-api/src/main.ts +10 -0
- package/templates/package-api/src/routes/health.ts +5 -0
- package/templates/package-api/src/routes/users.ts +12 -0
- package/templates/package-api/tsconfig.json +15 -0
- package/templates/program/README.md +12 -0
- package/templates/program/gitignore +1 -0
- package/templates/program/package.json +10 -0
- package/templates/program/sloppy.json +6 -0
- package/templates/program/src/main.ts +9 -0
|
@@ -0,0 +1,933 @@
|
|
|
1
|
+
import { isPlainObject } from "./internal/validation.js";
|
|
2
|
+
import { Text } from "./codec.js";
|
|
3
|
+
import { copyUint8Array } from "./internal/bytes.js";
|
|
4
|
+
|
|
5
|
+
const TEXT_CONTENT_TYPE = "text/plain; charset=utf-8";
|
|
6
|
+
const JSON_CONTENT_TYPE = "application/json; charset=utf-8";
|
|
7
|
+
const HTML_CONTENT_TYPE = "text/html; charset=utf-8";
|
|
8
|
+
const BYTES_CONTENT_TYPE = "application/octet-stream";
|
|
9
|
+
const PROBLEM_CONTENT_TYPE = "application/problem+json; charset=utf-8";
|
|
10
|
+
const STREAM_CONTENT_TYPE = "application/octet-stream";
|
|
11
|
+
const STREAM_MAX_CHUNK_BYTES = 65536;
|
|
12
|
+
const STREAM_MAX_TOTAL_BYTES = 131072;
|
|
13
|
+
const FAST_RESULT_KIND = "__sloppyFastResult";
|
|
14
|
+
const FAST_JSON_TEXT = "__sloppyJsonText";
|
|
15
|
+
const RAW_JSON_BODY = "__sloppyRawJsonBody";
|
|
16
|
+
const FAST_TEXT_OK = 1;
|
|
17
|
+
const FAST_NO_CONTENT = 2;
|
|
18
|
+
const FAST_JSON = 3;
|
|
19
|
+
const FAST_CREATED = 4;
|
|
20
|
+
const FAST_JSON_MAX_LENGTH = 256;
|
|
21
|
+
const DEFAULT_JSON_OPTIONS = Object.freeze({
|
|
22
|
+
casing: "preserve",
|
|
23
|
+
includeNulls: true,
|
|
24
|
+
dateFormat: "iso8601",
|
|
25
|
+
bigint: "string",
|
|
26
|
+
bytes: "base64",
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
function resolveStatus(options) {
|
|
30
|
+
const status = options?.status ?? 200;
|
|
31
|
+
|
|
32
|
+
if (!Number.isInteger(status) || status < 100 || status > 999) {
|
|
33
|
+
throw new TypeError("Sloppy Results status must be an integer from 100 to 999.");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return status;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function normalizeJsonOptions(options = undefined) {
|
|
40
|
+
if (options === undefined) {
|
|
41
|
+
return DEFAULT_JSON_OPTIONS;
|
|
42
|
+
}
|
|
43
|
+
if (!isPlainObject(options)) {
|
|
44
|
+
throw new TypeError("Sloppy JSON options must be a plain object.");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const normalized = {
|
|
48
|
+
casing: options.casing !== undefined
|
|
49
|
+
? options.casing
|
|
50
|
+
: DEFAULT_JSON_OPTIONS.casing,
|
|
51
|
+
includeNulls: options.includeNulls !== undefined
|
|
52
|
+
? options.includeNulls
|
|
53
|
+
: DEFAULT_JSON_OPTIONS.includeNulls,
|
|
54
|
+
dateFormat: options.dateFormat !== undefined
|
|
55
|
+
? options.dateFormat
|
|
56
|
+
: DEFAULT_JSON_OPTIONS.dateFormat,
|
|
57
|
+
bigint: options.bigint !== undefined
|
|
58
|
+
? options.bigint
|
|
59
|
+
: DEFAULT_JSON_OPTIONS.bigint,
|
|
60
|
+
bytes: options.bytes !== undefined
|
|
61
|
+
? options.bytes
|
|
62
|
+
: DEFAULT_JSON_OPTIONS.bytes,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
if (normalized.casing !== "preserve" && normalized.casing !== "camelCase") {
|
|
66
|
+
throw new TypeError("Sloppy JSON casing must be preserve or camelCase.");
|
|
67
|
+
}
|
|
68
|
+
if (typeof normalized.includeNulls !== "boolean") {
|
|
69
|
+
throw new TypeError("Sloppy JSON includeNulls must be a boolean.");
|
|
70
|
+
}
|
|
71
|
+
if (normalized.dateFormat !== "iso8601") {
|
|
72
|
+
throw new TypeError("Sloppy JSON dateFormat currently supports iso8601.");
|
|
73
|
+
}
|
|
74
|
+
if (normalized.bigint !== "string" && normalized.bigint !== "error") {
|
|
75
|
+
throw new TypeError("Sloppy JSON bigint must be string or error.");
|
|
76
|
+
}
|
|
77
|
+
if (normalized.bytes !== "base64" && normalized.bytes !== "array") {
|
|
78
|
+
throw new TypeError("Sloppy JSON bytes must be base64 or array.");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return Object.freeze(normalized);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function jsonKey(key, options) {
|
|
85
|
+
if (options.casing !== "camelCase") {
|
|
86
|
+
return key;
|
|
87
|
+
}
|
|
88
|
+
return key.replace(/[-_]+([A-Za-z0-9])/gu, (_, ch) => ch.toUpperCase());
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function base64Encode(bytes) {
|
|
92
|
+
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
93
|
+
let output = "";
|
|
94
|
+
for (let index = 0; index < bytes.byteLength; index += 3) {
|
|
95
|
+
const a = bytes[index];
|
|
96
|
+
const b = index + 1 < bytes.byteLength ? bytes[index + 1] : 0;
|
|
97
|
+
const c = index + 2 < bytes.byteLength ? bytes[index + 2] : 0;
|
|
98
|
+
const combined = (a << 16) | (b << 8) | c;
|
|
99
|
+
output += alphabet[(combined >> 18) & 63];
|
|
100
|
+
output += alphabet[(combined >> 12) & 63];
|
|
101
|
+
output += index + 1 < bytes.byteLength ? alphabet[(combined >> 6) & 63] : "=";
|
|
102
|
+
output += index + 2 < bytes.byteLength ? alphabet[combined & 63] : "=";
|
|
103
|
+
}
|
|
104
|
+
return output;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function bytesView(value) {
|
|
108
|
+
return copyUint8Array(value);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function defineJsonProperty(output, key, value) {
|
|
112
|
+
Object.defineProperty(output, key, {
|
|
113
|
+
value,
|
|
114
|
+
enumerable: true,
|
|
115
|
+
configurable: true,
|
|
116
|
+
writable: true,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function normalizeErrorObject(error, options, seen) {
|
|
121
|
+
const output = {};
|
|
122
|
+
defineJsonProperty(output, "name", typeof error.name === "string" && error.name.length > 0 ? error.name : "Error");
|
|
123
|
+
defineJsonProperty(output, "message", String(error.message ?? ""));
|
|
124
|
+
for (const [key, nested] of Object.entries(error)) {
|
|
125
|
+
if (key === "name" || key === "message" || key === "stack") {
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
const value = normalizeJsonValue(nested, options, seen, key);
|
|
129
|
+
if (value !== undefined && (value !== null || options.includeNulls)) {
|
|
130
|
+
defineJsonProperty(output, jsonKey(key, options), value);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return Object.freeze(output);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function normalizeJsonValue(value, options, seen, path) {
|
|
137
|
+
if (value === undefined) {
|
|
138
|
+
return undefined;
|
|
139
|
+
}
|
|
140
|
+
if (typeof value === "function" || typeof value === "symbol") {
|
|
141
|
+
throw new TypeError(`Sloppy JSON cannot serialize ${typeof value} at ${path}.`);
|
|
142
|
+
}
|
|
143
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") {
|
|
144
|
+
return value;
|
|
145
|
+
}
|
|
146
|
+
if (typeof value === "number") {
|
|
147
|
+
if (!Number.isFinite(value)) {
|
|
148
|
+
throw new TypeError(`Sloppy JSON numbers must be finite at ${path}.`);
|
|
149
|
+
}
|
|
150
|
+
return value;
|
|
151
|
+
}
|
|
152
|
+
if (typeof value === "bigint") {
|
|
153
|
+
if (options.bigint === "error") {
|
|
154
|
+
throw new TypeError(`Sloppy JSON cannot serialize BigInt at ${path}.`);
|
|
155
|
+
}
|
|
156
|
+
return value.toString();
|
|
157
|
+
}
|
|
158
|
+
if (value instanceof Date) {
|
|
159
|
+
const timestamp = value.getTime();
|
|
160
|
+
if (!Number.isFinite(timestamp)) {
|
|
161
|
+
throw new TypeError(`Sloppy JSON cannot serialize invalid Date at ${path}.`);
|
|
162
|
+
}
|
|
163
|
+
return value.toISOString();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const bytes = bytesView(value);
|
|
167
|
+
if (bytes !== undefined) {
|
|
168
|
+
return options.bytes === "array" ? Object.freeze(Array.from(bytes)) : base64Encode(bytes);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (value !== null && typeof value === "object") {
|
|
172
|
+
if (seen.has(value)) {
|
|
173
|
+
throw new TypeError(`Sloppy JSON cannot serialize circular reference at ${path}.`);
|
|
174
|
+
}
|
|
175
|
+
seen.add(value);
|
|
176
|
+
try {
|
|
177
|
+
if (Array.isArray(value)) {
|
|
178
|
+
return Object.freeze(value.map((item, index) => {
|
|
179
|
+
const itemValue = normalizeJsonValue(item, options, seen, `${path}[${index}]`);
|
|
180
|
+
return itemValue === undefined ? null : itemValue;
|
|
181
|
+
}));
|
|
182
|
+
}
|
|
183
|
+
if (value instanceof Error) {
|
|
184
|
+
return normalizeErrorObject(value, options, seen);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const output = {};
|
|
188
|
+
for (const [key, nested] of Object.entries(value)) {
|
|
189
|
+
const normalized = normalizeJsonValue(nested, options, seen, `${path}.${key}`);
|
|
190
|
+
if (normalized === undefined || (normalized === null && !options.includeNulls)) {
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
defineJsonProperty(output, jsonKey(key, options), normalized);
|
|
194
|
+
}
|
|
195
|
+
return Object.freeze(output);
|
|
196
|
+
} finally {
|
|
197
|
+
seen.delete(value);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
throw new TypeError(`Sloppy JSON cannot serialize ${typeof value} at ${path}.`);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function normalizeJsonBody(value, options = undefined) {
|
|
205
|
+
if (value === undefined) {
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
return normalizeJsonValue(value, normalizeJsonOptions(options), new Set(), "$");
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function normalizeJsonDescriptorBody(value, options = undefined) {
|
|
212
|
+
return value === undefined ? undefined : normalizeJsonBody(value, options);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function serializeJson(value, options = undefined) {
|
|
216
|
+
const normalized = normalizeJsonBody(value, options);
|
|
217
|
+
return JSON.stringify(normalized);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function isHeaderNameChar(value) {
|
|
221
|
+
return (value >= "A" && value <= "Z") ||
|
|
222
|
+
(value >= "a" && value <= "z") ||
|
|
223
|
+
(value >= "0" && value <= "9") ||
|
|
224
|
+
value === "!" ||
|
|
225
|
+
value === "#" ||
|
|
226
|
+
value === "$" ||
|
|
227
|
+
value === "%" ||
|
|
228
|
+
value === "&" ||
|
|
229
|
+
value === "'" ||
|
|
230
|
+
value === "*" ||
|
|
231
|
+
value === "+" ||
|
|
232
|
+
value === "-" ||
|
|
233
|
+
value === "." ||
|
|
234
|
+
value === "^" ||
|
|
235
|
+
value === "_" ||
|
|
236
|
+
value === "`" ||
|
|
237
|
+
value === "|" ||
|
|
238
|
+
value === "~";
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function isHeaderNameSafe(name) {
|
|
242
|
+
if (name.length === 0) {
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
for (const ch of name) {
|
|
247
|
+
if (!isHeaderNameChar(ch)) {
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return true;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function headerNameEqualsIgnoreCase(name, expected) {
|
|
256
|
+
if (name.length !== expected.length) {
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
for (let index = 0; index < expected.length; index += 1) {
|
|
261
|
+
let actual = name.charCodeAt(index);
|
|
262
|
+
let wanted = expected.charCodeAt(index);
|
|
263
|
+
if (actual >= 0x41 && actual <= 0x5A) {
|
|
264
|
+
actual += 0x20;
|
|
265
|
+
}
|
|
266
|
+
if (wanted >= 0x41 && wanted <= 0x5A) {
|
|
267
|
+
wanted += 0x20;
|
|
268
|
+
}
|
|
269
|
+
if (actual !== wanted) {
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return true;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function isManagedResponseHeader(name) {
|
|
277
|
+
switch (name.length) {
|
|
278
|
+
case "connection".length:
|
|
279
|
+
return headerNameEqualsIgnoreCase(name, "connection") ||
|
|
280
|
+
headerNameEqualsIgnoreCase(name, "keep-alive");
|
|
281
|
+
case "content-type".length:
|
|
282
|
+
return headerNameEqualsIgnoreCase(name, "content-type");
|
|
283
|
+
case "content-length".length:
|
|
284
|
+
return headerNameEqualsIgnoreCase(name, "content-length");
|
|
285
|
+
case "transfer-encoding".length:
|
|
286
|
+
return headerNameEqualsIgnoreCase(name, "transfer-encoding");
|
|
287
|
+
default:
|
|
288
|
+
return false;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function isHeaderValueSafe(value) {
|
|
293
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
294
|
+
const code = value.charCodeAt(index);
|
|
295
|
+
if ((code < 0x20 && code !== 0x09) || code === 0x7F) {
|
|
296
|
+
return false;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return true;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function assertHeaderValueSafe(value, label) {
|
|
304
|
+
if (typeof value !== "string" || !isHeaderValueSafe(value)) {
|
|
305
|
+
throw new TypeError(`Sloppy Results ${label} must be a safe HTTP header value.`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function resultStableHash(value) {
|
|
310
|
+
const text = String(value);
|
|
311
|
+
let hash = 0x811c9dc5;
|
|
312
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
313
|
+
hash ^= text.charCodeAt(index) & 0xff;
|
|
314
|
+
hash = Math.imul(hash, 0x01000193) >>> 0;
|
|
315
|
+
}
|
|
316
|
+
return `fnv1a32:${hash.toString(16).padStart(8, "0")}`;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function mergeVaryHeader(existing, value) {
|
|
320
|
+
if (existing === undefined || existing.length === 0) {
|
|
321
|
+
return value;
|
|
322
|
+
}
|
|
323
|
+
const tokens = existing.split(",").map((token) => token.trim().toLowerCase());
|
|
324
|
+
return tokens.includes(value.toLowerCase()) ? existing : `${existing}, ${value}`;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function assertCookieAttributeValueSafe(value, label) {
|
|
328
|
+
assertHeaderValueSafe(value, label);
|
|
329
|
+
if (value.includes(";")) {
|
|
330
|
+
throw new TypeError(`Sloppy Results ${label} must not contain ';'.`);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function assertCookieValueSafe(value, label) {
|
|
335
|
+
if (typeof value !== "string" || /[\x00-\x20\x7F;,]/u.test(value)) {
|
|
336
|
+
throw new TypeError(`Sloppy Results ${label} must be a safe Set-Cookie value.`);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function copyHeaders(options) {
|
|
341
|
+
const headers = options?.headers;
|
|
342
|
+
|
|
343
|
+
if (headers === undefined) {
|
|
344
|
+
return undefined;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (!isPlainObject(headers)) {
|
|
348
|
+
throw new TypeError("Sloppy Results headers must be a plain object when provided.");
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const copied = {};
|
|
352
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
353
|
+
if (!isHeaderNameSafe(name) || isManagedResponseHeader(name)) {
|
|
354
|
+
throw new TypeError("Sloppy Results headers must use safe unmanaged HTTP header names.");
|
|
355
|
+
}
|
|
356
|
+
assertHeaderValueSafe(value, `header '${name}'`);
|
|
357
|
+
Object.defineProperty(copied, name, {
|
|
358
|
+
value,
|
|
359
|
+
enumerable: true,
|
|
360
|
+
writable: true,
|
|
361
|
+
configurable: true,
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
return Object.freeze(copied);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function copySetCookies(options) {
|
|
369
|
+
const setCookies = options?.setCookies;
|
|
370
|
+
|
|
371
|
+
if (setCookies === undefined) {
|
|
372
|
+
return undefined;
|
|
373
|
+
}
|
|
374
|
+
if (!Array.isArray(setCookies)) {
|
|
375
|
+
throw new TypeError("Sloppy Results setCookies must be an array when provided.");
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
return Object.freeze(setCookies.map((value) => {
|
|
379
|
+
if (typeof value !== "string") {
|
|
380
|
+
throw new TypeError("Sloppy Results setCookies entries must be strings.");
|
|
381
|
+
}
|
|
382
|
+
assertHeaderValueSafe(value, "Set-Cookie");
|
|
383
|
+
return value;
|
|
384
|
+
}));
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function copyBytes(value) {
|
|
388
|
+
const bytes = copyUint8Array(value);
|
|
389
|
+
if (bytes === undefined) {
|
|
390
|
+
throw new TypeError("Sloppy Results.bytes body must be binary data or a typed array view.");
|
|
391
|
+
}
|
|
392
|
+
return bytes;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function resolveContentType(options, defaultContentType) {
|
|
396
|
+
const contentType = options?.contentType ?? defaultContentType;
|
|
397
|
+
|
|
398
|
+
if (typeof contentType !== "string" || contentType.length === 0) {
|
|
399
|
+
throw new TypeError("Sloppy Results contentType must be a non-empty string.");
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (/[\x00-\x1F\x7F]/.test(contentType)) {
|
|
403
|
+
throw new TypeError("Sloppy Results contentType must not contain control characters.");
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
return contentType;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function maybeFastJsonText(body) {
|
|
410
|
+
if (body !== null && typeof body === "object") {
|
|
411
|
+
return undefined;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
let jsonText;
|
|
415
|
+
|
|
416
|
+
try {
|
|
417
|
+
jsonText = serializeJson(body);
|
|
418
|
+
} catch {
|
|
419
|
+
return undefined;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
return typeof jsonText === "string" && jsonText.length <= FAST_JSON_MAX_LENGTH
|
|
423
|
+
? jsonText
|
|
424
|
+
: undefined;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
function createResult(kind, body, contentType, options, extra, fast, rawJsonBody) {
|
|
428
|
+
const setCookies = copySetCookies(options);
|
|
429
|
+
const descriptor = {
|
|
430
|
+
__sloppyResult: true,
|
|
431
|
+
kind,
|
|
432
|
+
status: resolveStatus(options),
|
|
433
|
+
contentType,
|
|
434
|
+
headers: copyHeaders(options),
|
|
435
|
+
...extra,
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
if (setCookies !== undefined) {
|
|
439
|
+
descriptor.setCookies = setCookies;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
if (body !== undefined) {
|
|
443
|
+
descriptor.body = body;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
if (fast !== undefined) {
|
|
447
|
+
Object.defineProperty(descriptor, FAST_RESULT_KIND, {
|
|
448
|
+
value: fast.kind,
|
|
449
|
+
});
|
|
450
|
+
if (fast.jsonText !== undefined) {
|
|
451
|
+
Object.defineProperty(descriptor, FAST_JSON_TEXT, {
|
|
452
|
+
value: fast.jsonText,
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
if (rawJsonBody !== undefined) {
|
|
458
|
+
Object.defineProperty(descriptor, RAW_JSON_BODY, {
|
|
459
|
+
value: rawJsonBody,
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
if (options?.json !== undefined) {
|
|
463
|
+
Object.defineProperty(descriptor, "json", {
|
|
464
|
+
value: normalizeJsonOptions(options.json),
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
Object.defineProperty(descriptor, "cookie", {
|
|
469
|
+
value(name, value, cookieOptions) {
|
|
470
|
+
return withCookie(descriptor, name, value, cookieOptions);
|
|
471
|
+
},
|
|
472
|
+
});
|
|
473
|
+
Object.defineProperty(descriptor, "cacheControl", {
|
|
474
|
+
value(value) {
|
|
475
|
+
return withCacheControl(descriptor, value);
|
|
476
|
+
},
|
|
477
|
+
});
|
|
478
|
+
Object.defineProperty(descriptor, "cacheHeaders", {
|
|
479
|
+
value(cacheOptions) {
|
|
480
|
+
return withCacheHeaders(descriptor, cacheOptions);
|
|
481
|
+
},
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
return Object.freeze(descriptor);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
function encodeCookieValue(value) {
|
|
488
|
+
try {
|
|
489
|
+
return encodeURIComponent(String(value));
|
|
490
|
+
} catch {
|
|
491
|
+
throw new TypeError("Sloppy Results cookie value must be safe.");
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
function normalizeSameSite(value) {
|
|
496
|
+
if (value === undefined) {
|
|
497
|
+
return undefined;
|
|
498
|
+
}
|
|
499
|
+
if (typeof value !== "string") {
|
|
500
|
+
throw new TypeError("Sloppy Results cookie sameSite must be lax, strict, or none.");
|
|
501
|
+
}
|
|
502
|
+
const lowered = value.toLowerCase();
|
|
503
|
+
if (lowered === "lax") {
|
|
504
|
+
return "Lax";
|
|
505
|
+
}
|
|
506
|
+
if (lowered === "strict") {
|
|
507
|
+
return "Strict";
|
|
508
|
+
}
|
|
509
|
+
if (lowered === "none") {
|
|
510
|
+
return "None";
|
|
511
|
+
}
|
|
512
|
+
throw new TypeError("Sloppy Results cookie sameSite must be lax, strict, or none.");
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
function buildSetCookie(name, value, options = undefined) {
|
|
516
|
+
if (typeof name !== "string" || !isHeaderNameSafe(name)) {
|
|
517
|
+
throw new TypeError("Sloppy Results cookie name must be a safe HTTP token.");
|
|
518
|
+
}
|
|
519
|
+
if (options !== undefined && !isPlainObject(options)) {
|
|
520
|
+
throw new TypeError("Sloppy Results cookie options must be a plain object.");
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
const encodedValue = encodeCookieValue(value);
|
|
524
|
+
assertCookieValueSafe(encodedValue, "cookie value");
|
|
525
|
+
const parts = [`${name}=${encodedValue}`];
|
|
526
|
+
|
|
527
|
+
if (options?.path !== undefined) {
|
|
528
|
+
assertCookieAttributeValueSafe(options.path, "cookie path");
|
|
529
|
+
parts.push(`Path=${options.path}`);
|
|
530
|
+
}
|
|
531
|
+
if (options?.domain !== undefined) {
|
|
532
|
+
assertCookieAttributeValueSafe(options.domain, "cookie domain");
|
|
533
|
+
parts.push(`Domain=${options.domain}`);
|
|
534
|
+
}
|
|
535
|
+
const maxAgeSeconds = options?.maxAgeSeconds ?? options?.maxAge;
|
|
536
|
+
if (maxAgeSeconds !== undefined) {
|
|
537
|
+
if (!Number.isInteger(maxAgeSeconds)) {
|
|
538
|
+
throw new TypeError("Sloppy Results cookie maxAgeSeconds must be an integer.");
|
|
539
|
+
}
|
|
540
|
+
parts.push(`Max-Age=${maxAgeSeconds}`);
|
|
541
|
+
}
|
|
542
|
+
if (options?.expires !== undefined) {
|
|
543
|
+
const expires = options.expires instanceof Date ? options.expires.toUTCString() : String(options.expires);
|
|
544
|
+
assertCookieAttributeValueSafe(expires, "cookie expires");
|
|
545
|
+
parts.push(`Expires=${expires}`);
|
|
546
|
+
}
|
|
547
|
+
const sameSite = normalizeSameSite(options?.sameSite);
|
|
548
|
+
if (sameSite !== undefined) {
|
|
549
|
+
parts.push(`SameSite=${sameSite}`);
|
|
550
|
+
}
|
|
551
|
+
if (options?.httpOnly === true) {
|
|
552
|
+
parts.push("HttpOnly");
|
|
553
|
+
}
|
|
554
|
+
if (options?.secure === true) {
|
|
555
|
+
parts.push("Secure");
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
return parts.join("; ");
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
function withCookie(descriptor, name, value, options) {
|
|
562
|
+
const existing = Array.isArray(descriptor.setCookies) ? descriptor.setCookies : [];
|
|
563
|
+
const setCookies = Object.freeze([...existing, buildSetCookie(name, value, options)]);
|
|
564
|
+
const extra = {};
|
|
565
|
+
if (descriptor.location !== undefined) {
|
|
566
|
+
extra.location = descriptor.location;
|
|
567
|
+
}
|
|
568
|
+
if (descriptor.chunks !== undefined) {
|
|
569
|
+
extra.chunks = Object.freeze([...descriptor.chunks]);
|
|
570
|
+
}
|
|
571
|
+
const rawJsonBody = Object.prototype.hasOwnProperty.call(descriptor, RAW_JSON_BODY)
|
|
572
|
+
? descriptor[RAW_JSON_BODY]
|
|
573
|
+
: undefined;
|
|
574
|
+
return createResult(
|
|
575
|
+
descriptor.kind,
|
|
576
|
+
descriptor.body,
|
|
577
|
+
descriptor.contentType,
|
|
578
|
+
{
|
|
579
|
+
status: descriptor.status,
|
|
580
|
+
headers: descriptor.headers,
|
|
581
|
+
json: descriptor.json,
|
|
582
|
+
setCookies,
|
|
583
|
+
},
|
|
584
|
+
Object.keys(extra).length === 0 ? undefined : extra,
|
|
585
|
+
undefined,
|
|
586
|
+
rawJsonBody,
|
|
587
|
+
);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
function descriptorExtra(descriptor) {
|
|
591
|
+
const extra = {};
|
|
592
|
+
if (descriptor.location !== undefined) {
|
|
593
|
+
extra.location = descriptor.location;
|
|
594
|
+
}
|
|
595
|
+
if (descriptor.chunks !== undefined) {
|
|
596
|
+
extra.chunks = Object.freeze([...descriptor.chunks]);
|
|
597
|
+
}
|
|
598
|
+
return Object.keys(extra).length === 0 ? undefined : extra;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
function cloneResult(descriptor, options) {
|
|
602
|
+
const rawJsonBody = Object.prototype.hasOwnProperty.call(descriptor, RAW_JSON_BODY)
|
|
603
|
+
? descriptor[RAW_JSON_BODY]
|
|
604
|
+
: undefined;
|
|
605
|
+
return createResult(
|
|
606
|
+
descriptor.kind,
|
|
607
|
+
descriptor.body,
|
|
608
|
+
descriptor.contentType,
|
|
609
|
+
options,
|
|
610
|
+
descriptorExtra(descriptor),
|
|
611
|
+
undefined,
|
|
612
|
+
rawJsonBody,
|
|
613
|
+
);
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
function withResultHeaders(descriptor, headers) {
|
|
617
|
+
return cloneResult(descriptor, {
|
|
618
|
+
status: descriptor.status,
|
|
619
|
+
headers: {
|
|
620
|
+
...(isPlainObject(descriptor.headers) ? descriptor.headers : {}),
|
|
621
|
+
...headers,
|
|
622
|
+
},
|
|
623
|
+
json: descriptor.json,
|
|
624
|
+
setCookies: descriptor.setCookies,
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
function withCacheControl(descriptor, value) {
|
|
629
|
+
assertHeaderValueSafe(value, "Cache-Control");
|
|
630
|
+
if (value.length === 0) {
|
|
631
|
+
throw new TypeError("Sloppy Results Cache-Control must be non-empty.");
|
|
632
|
+
}
|
|
633
|
+
return withResultHeaders(descriptor, { "Cache-Control": value });
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
function normalizeCacheHeadersOptions(options) {
|
|
637
|
+
if (!isPlainObject(options)) {
|
|
638
|
+
throw new TypeError("Sloppy Results cacheHeaders options must be a plain object.");
|
|
639
|
+
}
|
|
640
|
+
if (options.cacheControl !== undefined) {
|
|
641
|
+
assertHeaderValueSafe(options.cacheControl, "Cache-Control");
|
|
642
|
+
if (options.cacheControl.length === 0) {
|
|
643
|
+
throw new TypeError("Sloppy Results Cache-Control must be non-empty.");
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
if (options.vary !== undefined && !Array.isArray(options.vary)) {
|
|
647
|
+
throw new TypeError("Sloppy Results cacheHeaders vary must be an array.");
|
|
648
|
+
}
|
|
649
|
+
return options;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
function withCacheHeaders(descriptor, options) {
|
|
653
|
+
const normalized = normalizeCacheHeadersOptions(options);
|
|
654
|
+
const headers = {};
|
|
655
|
+
if (normalized.cacheControl !== undefined) {
|
|
656
|
+
headers["Cache-Control"] = normalized.cacheControl;
|
|
657
|
+
}
|
|
658
|
+
if (normalized.vary !== undefined) {
|
|
659
|
+
for (const value of normalized.vary) {
|
|
660
|
+
assertHeaderValueSafe(value, "Vary");
|
|
661
|
+
headers.Vary = mergeVaryHeader(headers.Vary ?? descriptor.headers?.Vary ?? descriptor.headers?.vary, value);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
if (normalized.lastModified !== undefined) {
|
|
665
|
+
const date = normalized.lastModified instanceof Date ? normalized.lastModified : new Date(normalized.lastModified);
|
|
666
|
+
if (!Number.isFinite(date.getTime())) {
|
|
667
|
+
throw new TypeError("Sloppy Results cacheHeaders lastModified must be a valid Date or timestamp.");
|
|
668
|
+
}
|
|
669
|
+
headers["Last-Modified"] = date.toUTCString();
|
|
670
|
+
}
|
|
671
|
+
if (normalized.etag !== undefined) {
|
|
672
|
+
if (normalized.etag === true) {
|
|
673
|
+
headers.ETag = `"${resultStableHash(serializeJson(descriptor.body ?? ""))}"`;
|
|
674
|
+
} else {
|
|
675
|
+
assertHeaderValueSafe(normalized.etag, "ETag");
|
|
676
|
+
headers.ETag = normalized.etag;
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
return withResultHeaders(descriptor, headers);
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
const TEXT_OK_RESULT = createResult("text", "ok", TEXT_CONTENT_TYPE, undefined, undefined, {
|
|
683
|
+
kind: FAST_TEXT_OK,
|
|
684
|
+
});
|
|
685
|
+
const NO_CONTENT_RESULT = createResult("empty", undefined, undefined, { status: 204 }, undefined, {
|
|
686
|
+
kind: FAST_NO_CONTENT,
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
function normalizeProblem(problemOrMessage, status) {
|
|
690
|
+
if (problemOrMessage === undefined) {
|
|
691
|
+
return Object.freeze({
|
|
692
|
+
title: "Sloppy problem",
|
|
693
|
+
status,
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
if (typeof problemOrMessage === "string") {
|
|
698
|
+
return Object.freeze({
|
|
699
|
+
title: problemOrMessage,
|
|
700
|
+
status,
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
if (problemOrMessage === null || typeof problemOrMessage !== "object" || Array.isArray(problemOrMessage)) {
|
|
705
|
+
throw new TypeError("Sloppy Results.problem value must be a string or plain problem object.");
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
return Object.freeze({
|
|
709
|
+
status,
|
|
710
|
+
...problemOrMessage,
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
function text(body, options) {
|
|
715
|
+
const value = String(body);
|
|
716
|
+
if (options === undefined && value === "ok") {
|
|
717
|
+
return TEXT_OK_RESULT;
|
|
718
|
+
}
|
|
719
|
+
return createResult("text", value, TEXT_CONTENT_TYPE, options);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
function json(value, options) {
|
|
723
|
+
const body = normalizeJsonDescriptorBody(value, options?.json);
|
|
724
|
+
const jsonText = options === undefined ? maybeFastJsonText(value) : undefined;
|
|
725
|
+
return createResult(
|
|
726
|
+
"json",
|
|
727
|
+
body,
|
|
728
|
+
JSON_CONTENT_TYPE,
|
|
729
|
+
options,
|
|
730
|
+
undefined,
|
|
731
|
+
jsonText === undefined ? undefined : { kind: FAST_JSON, jsonText },
|
|
732
|
+
value,
|
|
733
|
+
);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
function html(body, options) {
|
|
737
|
+
return createResult("html", String(body), HTML_CONTENT_TYPE, options);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
function bytes(body, options) {
|
|
741
|
+
return createResult("bytes", copyBytes(body), resolveContentType(options, BYTES_CONTENT_TYPE), options);
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
async function stream(callback, options) {
|
|
745
|
+
if (typeof callback !== "function") {
|
|
746
|
+
throw new TypeError("Sloppy Results.stream callback must be a function.");
|
|
747
|
+
}
|
|
748
|
+
const chunks = [];
|
|
749
|
+
let totalBytes = 0;
|
|
750
|
+
let closed = false;
|
|
751
|
+
function appendChunk(chunk) {
|
|
752
|
+
if (chunk.byteLength > STREAM_MAX_CHUNK_BYTES) {
|
|
753
|
+
throw new TypeError("Sloppy Results.stream chunk exceeds the bounded stream limit.");
|
|
754
|
+
}
|
|
755
|
+
if (totalBytes + chunk.byteLength > STREAM_MAX_TOTAL_BYTES) {
|
|
756
|
+
throw new TypeError("Sloppy Results.stream body exceeds the bounded stream limit.");
|
|
757
|
+
}
|
|
758
|
+
totalBytes += chunk.byteLength;
|
|
759
|
+
chunks.push(chunk);
|
|
760
|
+
}
|
|
761
|
+
const writer = Object.freeze({
|
|
762
|
+
writeText(text) {
|
|
763
|
+
if (closed) {
|
|
764
|
+
throw new TypeError("Sloppy stream writer is closed.");
|
|
765
|
+
}
|
|
766
|
+
appendChunk(Text.utf8.encode(String(text)));
|
|
767
|
+
},
|
|
768
|
+
writeBytes(bytes) {
|
|
769
|
+
if (closed) {
|
|
770
|
+
throw new TypeError("Sloppy stream writer is closed.");
|
|
771
|
+
}
|
|
772
|
+
appendChunk(copyBytes(bytes));
|
|
773
|
+
},
|
|
774
|
+
close() {
|
|
775
|
+
closed = true;
|
|
776
|
+
},
|
|
777
|
+
});
|
|
778
|
+
try {
|
|
779
|
+
await callback(writer);
|
|
780
|
+
} finally {
|
|
781
|
+
closed = true;
|
|
782
|
+
}
|
|
783
|
+
return createResult(
|
|
784
|
+
"stream",
|
|
785
|
+
Object.freeze(chunks),
|
|
786
|
+
resolveContentType(options, STREAM_CONTENT_TYPE),
|
|
787
|
+
options,
|
|
788
|
+
{ chunks: Object.freeze(chunks) },
|
|
789
|
+
);
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
function ok(value, options) {
|
|
793
|
+
const body = normalizeJsonDescriptorBody(value, options?.json);
|
|
794
|
+
const jsonText = options === undefined ? maybeFastJsonText(value) : undefined;
|
|
795
|
+
return createResult(
|
|
796
|
+
"json",
|
|
797
|
+
body,
|
|
798
|
+
JSON_CONTENT_TYPE,
|
|
799
|
+
options,
|
|
800
|
+
undefined,
|
|
801
|
+
jsonText === undefined ? undefined : { kind: FAST_JSON, jsonText },
|
|
802
|
+
value,
|
|
803
|
+
);
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
function created(location, value, options) {
|
|
807
|
+
if (typeof location !== "string" || location.length === 0) {
|
|
808
|
+
throw new TypeError("Sloppy Results.created location must be a non-empty string.");
|
|
809
|
+
}
|
|
810
|
+
assertHeaderValueSafe(location, "created location");
|
|
811
|
+
|
|
812
|
+
const mergedOptions = { status: 201, ...options };
|
|
813
|
+
const body = normalizeJsonDescriptorBody(value, options?.json);
|
|
814
|
+
const jsonText = options === undefined ? maybeFastJsonText(value) : undefined;
|
|
815
|
+
|
|
816
|
+
return createResult(
|
|
817
|
+
"json",
|
|
818
|
+
body,
|
|
819
|
+
JSON_CONTENT_TYPE,
|
|
820
|
+
mergedOptions,
|
|
821
|
+
{ location },
|
|
822
|
+
jsonText === undefined ? undefined : { kind: FAST_CREATED, jsonText },
|
|
823
|
+
value,
|
|
824
|
+
);
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
function accepted(value, options) {
|
|
828
|
+
return createResult(
|
|
829
|
+
"json",
|
|
830
|
+
normalizeJsonDescriptorBody(value, options?.json),
|
|
831
|
+
JSON_CONTENT_TYPE,
|
|
832
|
+
{ status: 202, ...options },
|
|
833
|
+
undefined,
|
|
834
|
+
undefined,
|
|
835
|
+
value,
|
|
836
|
+
);
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
function noContent() {
|
|
840
|
+
return NO_CONTENT_RESULT;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
function notFound(valueOrProblem, options) {
|
|
844
|
+
return createResult(
|
|
845
|
+
"json",
|
|
846
|
+
normalizeJsonDescriptorBody(valueOrProblem, options?.json),
|
|
847
|
+
JSON_CONTENT_TYPE,
|
|
848
|
+
{ status: 404, ...options },
|
|
849
|
+
undefined,
|
|
850
|
+
undefined,
|
|
851
|
+
valueOrProblem,
|
|
852
|
+
);
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
function badRequest(valueOrProblem, options) {
|
|
856
|
+
return createResult(
|
|
857
|
+
"json",
|
|
858
|
+
normalizeJsonDescriptorBody(valueOrProblem, options?.json),
|
|
859
|
+
JSON_CONTENT_TYPE,
|
|
860
|
+
{ status: 400, ...options },
|
|
861
|
+
undefined,
|
|
862
|
+
undefined,
|
|
863
|
+
valueOrProblem,
|
|
864
|
+
);
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
function unauthorized(valueOrProblem, options) {
|
|
868
|
+
return createResult(
|
|
869
|
+
"json",
|
|
870
|
+
normalizeJsonDescriptorBody(valueOrProblem, options?.json),
|
|
871
|
+
JSON_CONTENT_TYPE,
|
|
872
|
+
{ status: 401, ...options },
|
|
873
|
+
undefined,
|
|
874
|
+
undefined,
|
|
875
|
+
valueOrProblem,
|
|
876
|
+
);
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
function status(statusCode, value, options) {
|
|
880
|
+
if (value === undefined) {
|
|
881
|
+
return createResult("empty", undefined, undefined, { ...options, status: statusCode });
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
return createResult(
|
|
885
|
+
"json",
|
|
886
|
+
normalizeJsonDescriptorBody(value, options?.json),
|
|
887
|
+
JSON_CONTENT_TYPE,
|
|
888
|
+
{ ...options, status: statusCode },
|
|
889
|
+
undefined,
|
|
890
|
+
undefined,
|
|
891
|
+
value,
|
|
892
|
+
);
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
function problem(problemOrMessage, options) {
|
|
896
|
+
const status = resolveStatus({ status: 500, ...options });
|
|
897
|
+
const problemBody = normalizeProblem(problemOrMessage, status);
|
|
898
|
+
const body = normalizeJsonDescriptorBody(problemBody, options?.json);
|
|
899
|
+
return createResult(
|
|
900
|
+
"problem",
|
|
901
|
+
body,
|
|
902
|
+
PROBLEM_CONTENT_TYPE,
|
|
903
|
+
{ ...options, status },
|
|
904
|
+
undefined,
|
|
905
|
+
undefined,
|
|
906
|
+
problemBody,
|
|
907
|
+
);
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
export const Results = Object.freeze({
|
|
911
|
+
ok,
|
|
912
|
+
created,
|
|
913
|
+
accepted,
|
|
914
|
+
noContent,
|
|
915
|
+
notFound,
|
|
916
|
+
badRequest,
|
|
917
|
+
unauthorized,
|
|
918
|
+
status,
|
|
919
|
+
problem,
|
|
920
|
+
text,
|
|
921
|
+
json,
|
|
922
|
+
html,
|
|
923
|
+
bytes,
|
|
924
|
+
stream,
|
|
925
|
+
});
|
|
926
|
+
|
|
927
|
+
export {
|
|
928
|
+
DEFAULT_JSON_OPTIONS,
|
|
929
|
+
normalizeJsonBody,
|
|
930
|
+
normalizeJsonOptions,
|
|
931
|
+
RAW_JSON_BODY,
|
|
932
|
+
serializeJson,
|
|
933
|
+
};
|