webspresso 0.0.104 → 0.0.106

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.
Files changed (233) hide show
  1. package/.agents/AGENTS.md +21 -2
  2. package/.agents/ARCHITECTURE.md +23 -0
  3. package/.agents/CACHE.md +147 -0
  4. package/.agents/CLI.md +220 -8
  5. package/.agents/EARLY_HINTS.md +120 -0
  6. package/.agents/FLAGS.md +118 -0
  7. package/.agents/HEALTH.md +118 -0
  8. package/.agents/LIMITS.md +109 -0
  9. package/.agents/LOGGING.md +93 -0
  10. package/.agents/METRICS.md +127 -0
  11. package/.agents/MIGRATION_JOBS.md +95 -0
  12. package/.agents/ORM.md +155 -1
  13. package/.agents/PLUGINS.md +38 -3
  14. package/.agents/QUEUE.md +42 -0
  15. package/.agents/RATE_LIMIT.md +147 -0
  16. package/.agents/RESILIENCE.md +235 -0
  17. package/.agents/SCALE_OUT.md +109 -0
  18. package/.agents/SECRETS.md +117 -0
  19. package/AGENTS.md +16 -0
  20. package/README.md +2 -2
  21. package/bin/commands/add-aws.js +396 -0
  22. package/bin/commands/add-ci.js +523 -0
  23. package/bin/commands/add-cloudflare.js +240 -0
  24. package/bin/commands/add-cloudrun.js +381 -0
  25. package/bin/commands/add-docker.js +6 -6
  26. package/bin/commands/add-migration-job.js +396 -0
  27. package/bin/commands/assets-sync.js +209 -0
  28. package/bin/commands/client-generate.js +52 -0
  29. package/bin/commands/console.js +53 -0
  30. package/bin/commands/db-migrate.js +47 -2
  31. package/bin/commands/db-studio.js +84 -0
  32. package/bin/commands/deploy-check.js +242 -13
  33. package/bin/commands/dev.js +1 -1
  34. package/bin/commands/export.js +87 -0
  35. package/bin/commands/new.js +5 -5
  36. package/bin/commands/openapi.js +1 -1
  37. package/bin/commands/secrets-pull.js +208 -0
  38. package/bin/commands/start.js +1 -1
  39. package/bin/utils/db.js +7 -1
  40. package/bin/utils/project.js +2 -2
  41. package/bin/webspresso.js +22 -0
  42. package/core/app.js +421 -0
  43. package/core/auth/authenticate.js +93 -0
  44. package/core/auth/hash.js +5 -4
  45. package/core/auth/index.js +34 -4
  46. package/core/auth/jwt.js +5 -54
  47. package/core/auth/magic-link.js +4 -5
  48. package/core/auth/manager.js +53 -10
  49. package/core/auth/middleware.js +16 -68
  50. package/core/auth/oauth.js +8 -17
  51. package/core/auth/redis-session.js +1008 -0
  52. package/core/auth/sessions.js +156 -0
  53. package/core/auth/totp.js +2 -1
  54. package/core/cache/byte-size.js +123 -0
  55. package/core/cache/cdn.js +296 -0
  56. package/core/cache/index.js +33 -0
  57. package/core/cache/lru.js +477 -0
  58. package/core/cache/tagged-lru.js +354 -0
  59. package/core/commands/registry.js +6 -20
  60. package/core/console/commands.js +148 -0
  61. package/core/console/context.js +260 -0
  62. package/core/console/eval.js +70 -0
  63. package/core/console/index.js +22 -0
  64. package/core/console/repl.js +102 -0
  65. package/core/early-hints/index.js +267 -0
  66. package/core/errors/domain.js +49 -0
  67. package/core/errors/http.js +31 -0
  68. package/core/errors/index.js +10 -0
  69. package/core/errors/normalize.js +88 -2
  70. package/core/events/discovery.js +6 -24
  71. package/core/events/dispatcher.js +9 -7
  72. package/core/events/signal.js +7 -0
  73. package/core/export/discovery.js +191 -0
  74. package/core/export/exporter.js +277 -0
  75. package/core/export/index.js +24 -0
  76. package/core/flags/evaluator.js +252 -0
  77. package/core/flags/hasher.js +37 -0
  78. package/core/flags/index.js +21 -0
  79. package/core/flags/manager.js +263 -0
  80. package/core/flags/store.js +126 -0
  81. package/core/health/index.js +41 -0
  82. package/core/health/middleware.js +213 -0
  83. package/core/health/normalize.js +140 -0
  84. package/core/health/probes.js +203 -0
  85. package/core/http/adapter.js +74 -0
  86. package/core/http/express-adapter.js +46 -0
  87. package/core/http/hono-adapter.js +531 -0
  88. package/core/http/index.js +41 -0
  89. package/core/http/pipeline.js +485 -0
  90. package/core/index.js +336 -0
  91. package/core/limits/index.js +34 -0
  92. package/core/limits/middleware.js +325 -0
  93. package/core/limits/normalize.js +222 -0
  94. package/core/logger/formatters.js +264 -0
  95. package/core/logger/index.js +260 -0
  96. package/core/logger/trace.js +137 -0
  97. package/core/metrics/collectors.js +125 -0
  98. package/core/metrics/index.js +45 -0
  99. package/core/metrics/middleware.js +230 -0
  100. package/core/metrics/registry.js +366 -0
  101. package/core/orm/batch.js +70 -0
  102. package/core/orm/cache/fingerprint.js +2 -2
  103. package/core/orm/cache/memory-provider.js +27 -110
  104. package/core/orm/d1.js +265 -0
  105. package/core/orm/index.js +176 -13
  106. package/core/orm/json-fields.js +22 -8
  107. package/core/orm/migrate-cloud.js +194 -0
  108. package/core/orm/model.js +10 -0
  109. package/core/orm/pool.js +97 -0
  110. package/core/orm/query-builder.js +72 -9
  111. package/core/orm/repository.js +318 -19
  112. package/core/orm/schema-helpers.js +8 -10
  113. package/core/orm/scopes.js +321 -1
  114. package/core/orm/transaction.js +10 -2
  115. package/core/orm/utils.js +4 -86
  116. package/core/queue/adapters/cloudflare.js +131 -0
  117. package/core/queue/adapters/database.js +4 -8
  118. package/core/queue/adapters/pubsub.js +400 -0
  119. package/core/queue/adapters/sqs.js +419 -0
  120. package/core/queue/adapters/wait-until.js +77 -0
  121. package/core/queue/index.js +8 -0
  122. package/core/queue/manager.js +66 -41
  123. package/core/rate-limit/index.js +22 -0
  124. package/core/rate-limit/limiter.js +219 -0
  125. package/core/rate-limit/stores/memory.js +129 -0
  126. package/core/rate-limit/stores/redis.js +151 -0
  127. package/core/rate-limit/utils.js +112 -0
  128. package/core/resilience/circuit-breaker.js +422 -0
  129. package/core/resilience/errors.js +63 -0
  130. package/core/resilience/index.js +53 -0
  131. package/core/resilience/registry.js +97 -0
  132. package/core/resilience/retry.js +203 -0
  133. package/core/schema-cache.js +9 -2
  134. package/core/secrets/index.js +113 -0
  135. package/core/secrets/manager.js +308 -0
  136. package/core/secrets/providers/aws.js +288 -0
  137. package/core/secrets/providers/env.js +44 -0
  138. package/core/secrets/providers/gcp.js +307 -0
  139. package/core/secrets/providers/memory.js +76 -0
  140. package/core/shutdown/http-adapter.js +4 -1
  141. package/core/shutdown/shutdown-manager.js +129 -19
  142. package/core/stream/index.js +19 -0
  143. package/core/stream/text-stream.js +326 -0
  144. package/core/studio/index.js +43 -0
  145. package/core/studio/inspector.js +258 -0
  146. package/core/studio/server.js +325 -0
  147. package/core/studio/ui.js +681 -0
  148. package/core/utils/array.js +12 -0
  149. package/core/utils/base64url.js +8 -0
  150. package/core/utils/crypto.js +118 -0
  151. package/core/utils/ensure-leading-slash.js +6 -0
  152. package/core/utils/escape-regexp.js +7 -0
  153. package/core/utils/fetch.js +6 -5
  154. package/core/utils/fs.js +144 -0
  155. package/core/utils/hmac.js +6 -0
  156. package/core/utils/json.js +98 -0
  157. package/core/utils/object.js +106 -0
  158. package/core/utils/random.js +9 -0
  159. package/core/utils/regex.js +77 -0
  160. package/core/utils/sha256.js +6 -0
  161. package/core/utils/string.js +87 -0
  162. package/core/utils/strip-trailing-slash.js +6 -0
  163. package/core/utils/time.js +96 -0
  164. package/core/utils/timeout.js +348 -0
  165. package/core/utils/url.js +106 -0
  166. package/core/utils/walk.js +7 -0
  167. package/index.d.ts +1549 -2
  168. package/index.js +272 -2
  169. package/package.json +3 -2
  170. package/plugins/admin-panel/core/api-extensions.js +2 -5
  171. package/plugins/admin-panel/index.js +13 -6
  172. package/plugins/admin-panel/modules/menu.js +1 -1
  173. package/plugins/api-key/crypto.js +3 -3
  174. package/plugins/api-key/index.js +3 -2
  175. package/plugins/api-key/scopes.js +5 -7
  176. package/plugins/audit-log/api-handlers.js +6 -15
  177. package/plugins/audit-log/index.js +3 -1
  178. package/plugins/audit-log/parse.js +3 -3
  179. package/plugins/basic-auth/index.js +3 -2
  180. package/plugins/cors.js +3 -2
  181. package/plugins/csrf/index.js +9 -12
  182. package/plugins/csv/index.js +8 -5
  183. package/plugins/dashboard/app.js +2 -2
  184. package/plugins/dashboard/index.js +5 -5
  185. package/plugins/data-exchange/record-selection.js +2 -5
  186. package/plugins/email/api-handlers.js +5 -18
  187. package/plugins/email/auth-bridge.js +3 -2
  188. package/plugins/email/index.js +11 -9
  189. package/plugins/health-check.js +2 -1
  190. package/plugins/index.js +5 -0
  191. package/plugins/lazy.js +5 -6
  192. package/plugins/mcp/discovery.js +18 -17
  193. package/plugins/polar/src/api.js +2 -1
  194. package/plugins/polar/src/webhooks.js +5 -6
  195. package/plugins/queue/index.js +18 -2
  196. package/plugins/rate-limit/index.js +3 -1
  197. package/plugins/redirect/index.js +16 -9
  198. package/plugins/rls/index.js +170 -0
  199. package/plugins/seo-checker/index.js +2 -2
  200. package/plugins/site-analytics/index.js +3 -1
  201. package/plugins/site-analytics/tracking.js +2 -2
  202. package/plugins/sitemap.js +3 -2
  203. package/plugins/swagger.js +12 -5
  204. package/plugins/upload/gcs-provider.js +148 -0
  205. package/plugins/upload/index.js +136 -77
  206. package/plugins/upload/orphan-pruner.js +4 -3
  207. package/plugins/upload/s3-provider.js +257 -0
  208. package/plugins/webhooks/crypto.js +6 -11
  209. package/plugins/webhooks/dispatcher.js +8 -8
  210. package/plugins/webhooks/index.js +33 -23
  211. package/plugins/webhooks/matcher.js +7 -11
  212. package/plugins/webhooks/sender.js +8 -11
  213. package/plugins/xlsx/index.js +7 -5
  214. package/src/api/api-loader.js +12 -95
  215. package/src/api/define-api.js +1 -0
  216. package/src/app-context.js +41 -5
  217. package/src/client-runtime/constants.js +10 -0
  218. package/src/client-runtime/mount.js +2 -3
  219. package/src/dev/banner.js +6 -2
  220. package/src/dev/client-gen.js +458 -0
  221. package/src/discovery/scan-routes.js +5 -2
  222. package/src/file-router.js +3 -3
  223. package/src/helpers.js +212 -24
  224. package/src/modules/module-discovery.js +1 -9
  225. package/src/pages/page-loader.js +13 -154
  226. package/src/plugin-manager.js +99 -18
  227. package/src/server.js +335 -133
  228. package/src/services/discovery.js +19 -40
  229. package/src/services/executor.js +95 -25
  230. package/src/services/memoize.js +3 -25
  231. package/src/services/registry.js +15 -1
  232. package/src/services/validator.js +38 -20
  233. package/src/template/precompile.js +8 -19
package/.agents/AGENTS.md CHANGED
@@ -18,6 +18,13 @@ When tasked with a feature or bugfix, **immediately consult the corresponding to
18
18
  | **Authentication (Session / JWT / Guard)** | [`.agents/AUTH.md`](AUTH.md) | `core/auth/` |
19
19
  | **Background Jobs & Worker Queue** | [`.agents/QUEUE.md`](QUEUE.md) | `jobs/` |
20
20
  | **Decoupled Events & Signals** | [`.agents/EVENTS.md`](EVENTS.md) | `core/events/`, `listeners/` |
21
+ | **Fault Tolerance / Circuit Breaker / Retry** | [`.agents/RESILIENCE.md`](RESILIENCE.md) | `core/resilience/` |
22
+ | **Stateless Scale-Out (Redis Sessions & DB Pool)** | [`.agents/SCALE_OUT.md`](SCALE_OUT.md) | `core/auth/redis-session.js`, `core/orm/pool.js` |
23
+ | **Cloud Secrets (GCP & AWS Secret Manager)** | [`.agents/SECRETS.md`](SECRETS.md) | `core/secrets/`, `bin/commands/secrets-pull.js` |
24
+ | **Cloud Metrics & Distributed Tracing** | [`.agents/METRICS.md`](METRICS.md) | `core/metrics/`, `/metrics` |
25
+ | **Cloud Migration Jobs (Zero-Downtime DB Deploy)** | [`.agents/MIGRATION_JOBS.md`](MIGRATION_JOBS.md) | `bin/commands/add-migration-job.js`, `core/orm/migrate-cloud.js` |
26
+ | **Rate Limiting Engine (Zero-Dependency & Redis)** | [`.agents/RATE_LIMIT.md`](RATE_LIMIT.md) | `core/rate-limit/` |
27
+ | **Feature Flags & Canary Splitter (`core/flags`)** | [`.agents/FLAGS.md`](FLAGS.md) | `core/flags/` |
21
28
  | **API Signatures & Types Verification** | [`index.d.ts`](../index.d.ts) | **Canonical Ground Truth** |
22
29
 
23
30
  ---
@@ -49,13 +56,25 @@ This `.agents/` customization root is organized into modular topic guides:
49
56
  10. **[Realtime Layer & Adapters](.agents/REALTIME.md)** — Framework-agnostic realtime layer (`core/realtime`), generic WebSocket, SSE, and Socket.IO adapters, subscription identity, backoff reconnect, and auth lifecycle.
50
57
  11. **[Email Plugin Guide](.agents/EMAIL.md)** — MJML template compilation, Nodemailer transport, DB delivery logs, Auth Email bridge, and Admin UI.
51
58
  12. **[Admin Panel Customization](.agents/ADMIN.md)** — Extending Mithril.js Admin Panel SPA (Custom pages `component.js`, custom field renderers, widgets, single & bulk actions).
52
- 13. **[CLI Tooling & Commands](CLI.md)** — `webspresso dev`, `build`, `doctor`, `db:migrate`, `db:seed`, `polar:migrate`, `favicon:generate`, `add:tailwind`, `add:docker`, `deploy:check`, `agents:init`.
59
+ 13. **[CLI Tooling & Commands](CLI.md)** — `webspresso dev`, `build`, `doctor`, `db:migrate`, `db:seed`, `polar:migrate`, `favicon:generate`, `add:tailwind`, `add:docker`, `add:cloudrun`, `add:aws`, `add:cloudflare`, `deploy:check`, `agents:init`.
53
60
  14. **[Development & Testing Workflow](.agents/WORKFLOW.md)** — CLI commands, Vitest unit/integration testing, Playwright E2E tests, TypeScript type checking, and zero regression policy.
54
61
  15. **[Coding Standards & Conventions](.agents/CONVENTIONS.md)** — Zero external dependencies policy, strict function signature & schema verification, clean component separation, and log traceback inspection rules.
55
- 16. **[Background Job Queue](.agents/QUEUE.md)** — In-Memory, Knex Database, and Distributed Redis queue adapters, job auto-discovery (`jobs/`), retries, and graceful draining.
62
+ 16. **[Background Job Queue](.agents/QUEUE.md)** — In-Memory, Knex Database, Distributed Redis, AWS SQS, and Google Cloud Pub/Sub queue adapters, job auto-discovery (`jobs/`), retries, and graceful draining.
56
63
  17. **[SSR Streaming & Chunked Transfer](.agents/STREAMING.md)** — Native HTTP chunked streaming (`res.renderStream()`), deferred data loader slots (`defer: { key: promise }`), and TTFB optimization.
57
64
  18. **Polar billing production checklist** — [docs/guides/polar-production-checklist.md](../docs/guides/polar-production-checklist.md) (env, webhooks, rate limits, external tier hooks, CSP). Plugin reference: [PLUGINS.md § polarPlugin](PLUGINS.md).
58
65
  19. **[Decoupled Events & Signals Architecture](.agents/EVENTS.md)** — Zero-dependency enterprise event dispatcher (`core/events`), sync/async/parallel/serial/waterfall dispatch, wildcard listeners (`order.*`), Django-style `Signal` and sender filtering, queue bridging, and listener auto-discovery.
66
+ 20. **[HTTP 103 Early Hints](.agents/EARLY_HINTS.md)** — Zero-dependency RFC 8288 / RFC 8297 Early Hints dispatcher (`core/early-hints`), preloading route CSS/JS and fonts before SSR loader execution, `createApp({ earlyHints })`, and `res.earlyHints(links)` / `ctx.earlyHints(links)` helpers.
67
+ 21. **[Payload Limits & DoS Protection](.agents/LIMITS.md)** — Zero-dependency request body limits and DoS protection (`core/limits`), `createApp({ limits })`, automatic `PayloadTooLargeError` (HTTP 413) mapping, route overrides (`limits.routes`), streaming bypass, and native HMAC webhook `req.rawBody` capture.
68
+ 22. **[Cloud & Kubernetes Health Probes](.agents/HEALTH.md)** — Zero-dependency RFC-compliant liveness (`/healthz`), readiness (`/readyz`), and combined diagnostic (`/health`) probes (`core/health`), event loop lag telemetry, database/queue health monitors, automatic HTTP 503 draining on graceful shutdown, and `createApp({ healthCheck: true })`.
69
+ 23. **[LRU & Tagged Cache Engine](.agents/CACHE.md)** — Zero-dependency, memory-bounded in-memory LRU cache (`core/cache`), byte budget constraints (`maxBytes` / `maxMemoryMb`), TTL expiration, stampede protection (`getOrSet`), leak-free tag invalidation (`TaggedLruCache`), ORM query cache provider, and `createApp({ cache })`.
70
+ 24. **[Enterprise Resilience & Circuit Breaker](.agents/RESILIENCE.md)** — Zero-dependency fault tolerance engine (`core/resilience`), three-state `CircuitBreaker` (`CLOSED`, `OPEN`, `HALF_OPEN`), rolling window failure rate, per-call execution timeout, fallback handlers, and exponential backoff retry engine (`retry`, `withRetry`) with randomized jitter and transient error detection.
71
+ 25. **[Structured Cloud Logging & Distributed Tracing](.agents/LOGGING.md)** — Zero-dependency cloud logger (`core/logger`), GCP Cloud Logging, AWS CloudWatch, automatic trace propagation (`x-cloud-trace-context`, `x-amzn-trace-id`, `traceparent`), sensitive key redaction, and `req.logger` child context.
72
+ 26. **[Stateless Scale-Out & Database Pool Management](.agents/SCALE_OUT.md)** — Distributed Redis session store (`createRedisSessionStore`), Redis session tracker (`createRedisSessionTracker`), zero-dependency RESP client, serverless DB connection pool optimizer (`normalizeServerlessPoolConfig`), and scale-out preflight audits (`deploy:check`).
73
+ 27. **[Cloud Secrets Management](.agents/SECRETS.md)** — Zero-dependency Google Cloud Secret Manager & AWS Secrets Manager/SSM integration (`core/secrets`), in-memory caching with TTL, dot-notation JSON subkeys, process.env hydration, and `webspresso secrets:pull`.
74
+ 28. **[Cloud Metrics & Distributed Tracing](.agents/METRICS.md)** — Zero-dependency Prometheus exposition format (`/metrics`), JSON telemetry (`/metrics.json`), HTTP latency histograms, Node.js event loop/memory/pool/cache collectors, and W3C `traceparent` & `x-trace-id` propagation (`core/metrics`).
75
+ 29. **[Cloud Migration Jobs & Zero-Downtime Database Deploy](.agents/MIGRATION_JOBS.md)** — Dedicated pre-deploy migration jobs for Cloud Run, AWS ECS, and Kubernetes (`webspresso add:migration-job`), connection retries, and stale lock auto-healing (`webspresso db:migrate --safe`).
76
+ 30. **[Native Zero-Dependency Rate Limiting Engine](.agents/RATE_LIMIT.md)** — Framework-agnostic rate limiter (`core/rate-limit`), sliding window in-memory & Redis stores, RFC 6585 standard headers (`RateLimit-*`, `Retry-After`), Express & Hono dual support, and `rateLimit({ window, limit })`.
77
+ 31. **[Feature Flags & Canary Traffic Splitter](.agents/FLAGS.md)** — Zero-dependency feature flag and canary rollout engine (`core/flags`), deterministic FNV-1a bucketing, targeting rules (users, roles, email domains), A/B variants, SSR template helpers (`fsy.hasFeature`), and declarative API guards (`defineApi({ flag })`).
59
78
 
60
79
  ---
61
80
 
@@ -51,6 +51,29 @@
51
51
  - **HTTP Response Compression (`core/compression`)**:
52
52
  - Zero-dependency streaming zlib compression configured via `createApp({ server: { compression: true } })`.
53
53
  - Content negotiation for Brotli (`br`), Gzip (`gzip`), Deflate (`deflate`), threshold filtering (1 KB), and `res.compress(false)` route opt-out.
54
+ - **Configurable Payload Size Limits & DoS Protection (`core/limits`)**:
55
+ - Zero-dependency request body limits configured via `createApp({ limits: { json: '1mb', urlencoded: '1mb', raw: false, text: false, parameterLimit: 1000 } })` or shorthand string/number (`limits: '5mb'`). Also accepted under `createApp({ server: { limits } })`.
56
+ - **HTTP 413 & 400 Error Mapping**: Payload limit violations and excessive form parameters automatically produce `PayloadTooLargeError` (HTTP 413) with `{ limit, length, expected }` metadata; malformed JSON bodies produce `BadRequestError` (HTTP 400) with `INVALID_JSON_PAYLOAD`.
57
+ - **Raw Body Capture for Webhooks**: `captureRawBody: true | string[] | Function` captures incoming request payload `Buffer` on `req.rawBody` for zero-overhead HMAC cryptographic verification (Polar, Stripe, GitHub).
58
+ - **Route Overrides & Exclusions**: `limits.routes` supports per-route limit adjustments (e.g. `routes: { '/api/upload': { json: '25mb' } }`), while `limits.exclude` completely bypasses body parsing for raw socket/streaming endpoints.
59
+ - **Cloud & Kubernetes Health Probes (`core/health`, `/healthz`, `/readyz`, `/health`)**:
60
+ - RFC-compliant cloud-native endpoints enabled via `createApp({ healthCheck: true })` or fine-grained `{ path: '/healthz', readyPath: '/readyz', combinedPath: '/health', db: true, queue: true, eventLoopLag: true }`.
61
+ - **Liveness Probe (`/healthz`)**: Non-blocking probe reporting basic process health, memory usage (`rss`, `heapUsed`, `heapTotal`, `external`), and system uptime.
62
+ - **Readiness Probe (`/readyz`)**: Deep dependency inspection checking database ping (`knex.raw('SELECT 1')`), worker queue status, custom user checks, and `ShutdownManager` draining state (immediately returning HTTP 503 so upstream load balancers detach the instance).
63
+ - **Diagnostic Health (`/health`)**: Full operational dashboard with event loop lag percentiles (`perf_hooks.monitorEventLoopDelay`), dependency breakdown, and latency metrics.
64
+ - **In-Memory LRU & Tagged Cache Engine (`core/cache`)**:
65
+ - Zero-dependency O(1) Least-Recently-Used caching engine configured via `createApp({ cache: { maxEntries, maxMemoryMb, defaultTtl } })` or `cache: false`.
66
+ - **Memory Budget & Leak Prevention**: Enforces byte boundaries (`maxBytes` / `maxMemoryMb`) via `approximateByteSize` to prevent process OOM errors, combined with passive and active TTL pruning (`pruneExpired()`).
67
+ - **Bidirectional Tag Invalidation (`TaggedLruCache`)**: Supports model and query tagging (`invalidateTag`, `invalidateTags`). When items are evicted via LRU, all associated tag sets are automatically and symmetrically cleaned up, eliminating dangling Set memory leaks.
68
+ - **Stampede / Thundering Herd Protection**: `getOrSet(key, asyncFactory, options)` deduplicates concurrent in-flight requests for cold keys so expensive factories execute only once.
69
+ - **Framework Integration**: Directly powers `core/orm/cache/memory-provider.js` (ORM query caching) and `core/schema-cache.js` (Zod schema cache), and is automatically exposed on `req.cache`, `req.context.cache`, and `app.cache`.
70
+ - **Enterprise Resilience, Circuit Breaker & Retry Engine (`core/resilience`)**:
71
+ - Zero-dependency fault tolerance and cascading failure prevention system built natively on `events` and `perf_hooks`.
72
+ - **Three-State Circuit Breaker (`CircuitBreaker`)**: Standard `CLOSED`, `OPEN`, and `HALF_OPEN` states. Trips to `OPEN` when consecutive failures reach `failureThreshold` or failure rate percentage exceeds `failureRateThreshold` in a rolling sample window (`rollingWindow`).
73
+ - **Fail-Fast & Graceful Fallbacks**: Fast-fails calls with `CircuitBreakerOpenError` (HTTP 503) without executing downstream code when open, or seamlessly executes fallback handlers (`fallback: (err, ...args) => ...`).
74
+ - **Trial Recovery Probing**: Automatically transitions to `HALF_OPEN` after `resetTimeout` and tests downstream health using trial probes (`successThreshold`).
75
+ - **Exponential Backoff Retry with Full Jitter (`retry`, `withRetry`)**: Automatically retries transient network errors (`ECONNRESET`, `ETIMEDOUT`, 429 Too Many Requests, and 5xx errors) with randomized full jitter to prevent thundering herd spikes while avoiding retrying 4xx client errors.
76
+ - **Services Layer Integration**: Declarative `defineService({ circuitBreaker, retry })` seamlessly wraps service execution. Also accessible on `app.breakers`, `app.getBreaker()`, `req.breaker()`, `req.retry()`, `ctx.breaker()`, and `ctx.retry()`.
54
77
  - **Custom Error & 404 Pages (`errorPages`)**:
55
78
  - Configured via `createApp({ errorPages: { notFound, serverError, timeout } })`.
56
79
  - **404 Not Found (`notFound`)**: Template path (`'404.njk'`) or handler `(req, res, ctx) => ...`.
@@ -0,0 +1,147 @@
1
+ # High-Performance In-Memory LRU & Tagged Cache Engine (`core/cache`)
2
+
3
+ Webspresso includes a zero-dependency, memory-bounded, leak-free in-memory cache engine featuring O(1) Least-Recently-Used (LRU) eviction, byte budget constraints (`maxBytes` / `maxMemoryMb`), TTL expiration, stampede protection (thundering herd prevention), and O(1) bidirectional tag-based invalidation.
4
+
5
+ ---
6
+
7
+ ## 🎯 Key Capabilities & Design Guarantees
8
+
9
+ 1. **True O(1) LRU Eviction**: Built on V8's native insertion-order Map mechanics with zero extra memory allocation for linked-list node wrappers.
10
+ 2. **Dual Capacity Boundaries**:
11
+ - **`maxEntries`**: Restricts the maximum number of items stored.
12
+ - **`maxBytes` / `maxMemoryMb`**: Enforces strict memory budgets using `approximateByteSize` to prevent process Out-Of-Memory (OOM) crashes under heavy or large payload loads.
13
+ 3. **Symmetrical Tag Cleanup (Zero Leaks)**: When an item is evicted via LRU, expired via TTL, or deleted, all associated tag index sets are automatically pruned. Empty tag sets are deleted immediately, preventing the classic "dangling Set memory leak".
14
+ 4. **Stampede / Thundering Herd Protection**: `getOrSet(key, asyncFactory)` coalesces concurrent requests for the same missing key, invoking the expensive calculation or DB query exactly once.
15
+ 5. **Universal Integration**:
16
+ - **ORM Query Cache**: Powers `core/orm/cache/memory-provider.js` with true LRU and memory budgets.
17
+ - **Zod Schema Cache**: Powers `core/schema-cache.js` with bounded 2,000 schemas capacity.
18
+ - **Application & Context**: Automatically mounted on `app.cache`, `req.cache`, and `req.context.cache` unless `cache: false` is configured.
19
+
20
+ ---
21
+
22
+ ## 🚀 Quick Usage
23
+
24
+ ### 1. Standalone `LruCache`
25
+ ```javascript
26
+ const { createLruCache } = require('webspresso');
27
+
28
+ const cache = createLruCache({
29
+ maxEntries: 1000,
30
+ maxMemoryMb: 50, // 50 MB byte budget
31
+ defaultTtl: '15m', // 15 minutes default TTL
32
+ clone: false, // Set true if cached objects must not be mutated
33
+ });
34
+
35
+ cache.set('user:42', { name: 'Alice', role: 'admin' });
36
+
37
+ const user = cache.get('user:42'); // Refreshes LRU position
38
+ const peekUser = cache.peek('user:42'); // Reads without refreshing LRU position
39
+ ```
40
+
41
+ ### 2. Tagged Invalidation (`TaggedLruCache`)
42
+ ```javascript
43
+ const { createTaggedLruCache } = require('webspresso');
44
+
45
+ const cache = createTaggedLruCache({
46
+ maxEntries: 5000,
47
+ maxMemoryMb: 100,
48
+ });
49
+
50
+ // Tag items by model and primary key
51
+ cache.set('query:users:active', usersList, { tags: ['model:User', 'q:active'] });
52
+ cache.set('user:1:profile', userProfile, { tags: ['model:User', 'pk:1'] });
53
+
54
+ // Invalidate single primary key
55
+ cache.invalidateTag('pk:1');
56
+
57
+ // Invalidate all queries related to the User model
58
+ cache.invalidateTags(['model:User']);
59
+ ```
60
+
61
+ ### 3. Stampede / Thundering Herd Protection (`getOrSet`)
62
+ ```javascript
63
+ // 50 concurrent requests for 'expensive:report' will execute the factory only once:
64
+ const report = await cache.getOrSet('expensive:report', async (key) => {
65
+ return await generateHeavyReport();
66
+ }, { ttl: '1h' });
67
+ ```
68
+
69
+ ---
70
+
71
+ ## ⚙️ Application Configuration (`createApp`)
72
+
73
+ Configure the process-wide cache directly when bootstrapping Webspresso:
74
+
75
+ ```javascript
76
+ const { createApp } = require('webspresso');
77
+
78
+ const app = createApp({
79
+ pagesDir: './pages',
80
+ cache: {
81
+ maxEntries: 5000,
82
+ maxMemoryMb: 64, // 64 MB budget
83
+ defaultTtl: '10m',
84
+ },
85
+ });
86
+ ```
87
+
88
+ To disable the app cache completely:
89
+ ```javascript
90
+ const app = createApp({
91
+ pagesDir: './pages',
92
+ cache: false,
93
+ });
94
+ ```
95
+
96
+ ---
97
+
98
+ ## 🌐 Context & Route Usage
99
+
100
+ Inside page loaders, services, or API route handlers:
101
+
102
+ ```javascript
103
+ // In API routes (pages/api/stats.get.js)
104
+ module.exports = defineApi({
105
+ handler: async (req, res) => {
106
+ const stats = await req.cache.getOrSet('site:stats', async () => {
107
+ return await req.db.getRepository('Order').getSummaryStats();
108
+ }, { ttl: '5m', tags: ['stats', 'model:Order'] });
109
+
110
+ return res.json(stats);
111
+ },
112
+ });
113
+ ```
114
+
115
+ ```javascript
116
+ // From any background job or script using app-context
117
+ const { getCache } = require('webspresso');
118
+
119
+ const cache = getCache();
120
+ if (cache) {
121
+ cache.invalidateTag('stats');
122
+ }
123
+ ```
124
+
125
+ ---
126
+
127
+ ## 📊 Telemetry & Observability Metrics
128
+
129
+ Access real-time cache performance and memory metrics:
130
+
131
+ ```javascript
132
+ const stats = app.cache.getStats();
133
+ console.log(stats);
134
+ // {
135
+ // entries: 42,
136
+ // tags: 5,
137
+ // currentBytes: 124850,
138
+ // maxBytes: 67108864,
139
+ // maxEntries: 5000,
140
+ // hits: 1540,
141
+ // misses: 23,
142
+ // hitRate: 0.985,
143
+ // sets: 65,
144
+ // evictions: 0,
145
+ // expirations: 12
146
+ // }
147
+ ```
package/.agents/CLI.md CHANGED
@@ -313,7 +313,7 @@ Generate production-ready, security-hardened, multi-stage `Dockerfile`, `.docker
313
313
  npx webspresso add:docker
314
314
 
315
315
  # Customize Node.js version, database service, or container port
316
- npx webspresso add:docker --node 22-alpine --db postgres --redis --port 3000
316
+ npx webspresso add:docker --node 22-alpine --db postgres --redis --port 7331
317
317
 
318
318
  # Overwrite existing files
319
319
  npx webspresso add:docker --force
@@ -332,12 +332,17 @@ npx webspresso add:docker --force
332
332
 
333
333
  ## 17. Production Deployment Preflight Check (`deploy:check` / `check:deploy`)
334
334
 
335
- Run comprehensive pre-flight verification before deploying to Fly.io, Railway, Render, VPS, or Kubernetes:
335
+ Run comprehensive pre-flight verification before deploying to Google Cloud Run, AWS (App Runner / ECS), Cloudflare Workers, Fly.io, Railway, Render, VPS, or Kubernetes:
336
336
 
337
337
  ```bash
338
- # Run production preflight verification
338
+ # Run production preflight verification (auto-detects target platform)
339
339
  npx webspresso deploy:check
340
340
 
341
+ # Target a specific cloud platform explicitly (cloudrun, aws, cloudflare, docker)
342
+ npx webspresso deploy:check --platform cloudrun
343
+ npx webspresso deploy:check --platform aws
344
+ npx webspresso deploy:check -p cloudflare
345
+
341
346
  # Run against a specific environment (.env.staging or .env.production)
342
347
  npx webspresso deploy:check --env staging
343
348
 
@@ -349,11 +354,17 @@ npx webspresso deploy:check --json
349
354
  ```
350
355
 
351
356
  ### Audited Verification Areas:
352
- 1. **Secrets & Cryptography**: Ensures `SESSION_SECRET` or `APP_KEY` is set, >= 32 characters, and not using weak placeholder defaults.
353
- 2. **Git & Leak Prevention**: Verifies `.env` and local database files are excluded in `.gitignore`.
354
- 3. **Container Hardening**: Ensures `Dockerfile` runs as a non-root `USER`, configures a `HEALTHCHECK`, and uses multi-stage caching.
355
- 4. **Database & Migrations**: Audits for pending unapplied migrations and warns against ephemeral SQLite storage.
356
- 5. **Server Optimizations**: Validates HTTP compression (`compression: true`), graceful shutdown, and health check endpoints.
357
+ 1. **Platform-Specific Cloud Preflight**:
358
+ - **Google Cloud Run**: Ephemeral SQLite/local upload warnings, SIGTERM timeout alignment (<= 10000ms), `.gcloudignore` secrets exclusion (`.env`), `cloudbuild.yaml` pipeline, and dynamic `PORT` binding.
359
+ - **AWS (App Runner / ECS Fargate)**: Ephemeral storage audits, `apprunner.yaml` / `ecs-task-definition.json` validity, and CloudWatch `awslogs` log driver configuration.
360
+ - **Cloudflare Workers**: `wrangler.toml` configuration and `nodejs_compat` compatibility flag verification.
361
+ - **Stateless Scale-Out**: Distributed Redis sessions and serverless connection pool limits.
362
+ 2. **Secrets & Cryptography**: Ensures `SESSION_SECRET` or `APP_KEY` is set, >= 32 characters, and not using weak placeholder defaults.
363
+ 3. **Git & Leak Prevention**: Verifies `.env` and local database files are excluded in `.gitignore`.
364
+ 4. **Container Hardening**: Ensures `Dockerfile` runs as a non-root `USER`, configures a `HEALTHCHECK`, and uses multi-stage caching.
365
+ 5. **Database & Migrations**: Audits for pending unapplied migrations and warns against ephemeral SQLite storage.
366
+ 6. **Server Optimizations**: Validates HTTP compression (`compression: true`), graceful shutdown, and health check endpoints.
367
+ 7. **Actionable Remediations**: Emits precise, single-command remediation steps for every warning and error detected.
357
368
 
358
369
  ---
359
370
 
@@ -435,3 +446,204 @@ npx webspresso make:mail Welcome
435
446
  # Outputs: emails/WelcomeEmail.js
436
447
  ```
437
448
 
449
+ ---
450
+
451
+ ## 21. Google Cloud Run Deployment (`add:cloudrun` / `cloudrun`)
452
+
453
+ Generate production-ready Google Cloud Run configuration with Google Cloud Build CI/CD pipeline, `.gcloudignore`, and one-command deployment script:
454
+
455
+ ```bash
456
+ # Generate cloudbuild.yaml, .gcloudignore, deploy-cloudrun.sh, and Dockerfile
457
+ npx webspresso add:cloudrun
458
+
459
+ # Customize region, container resources, scaling limits, and Cloud SQL
460
+ npx webspresso add:cloudrun --region europe-west1 --memory 1Gi --cpu 2 --min-instances 1 --max-instances 20 --cloud-sql my-project:europe-west1:my-db
461
+
462
+ # Overwrite existing files
463
+ npx webspresso add:cloudrun --force
464
+ ```
465
+
466
+ ### Generated Artifacts:
467
+ - **`cloudbuild.yaml`**: Multi-stage Docker image build, Google Artifact Registry push, and zero-downtime serverless Cloud Run deployment with Gen2 execution environment.
468
+ - **`.gcloudignore`**: Prevents `.env`, SQLite files, node_modules, and test directories from entering the Cloud Build upload context.
469
+ - **`deploy-cloudrun.sh`**: Executable bash script for direct zero-friction deployment (`chmod +x`).
470
+ - **`package.json` Scripts**: Adds `gcp:deploy` and `gcp:build`.
471
+
472
+ ---
473
+
474
+ ## 22. AWS App Runner & ECS Fargate Deployment (`add:aws` / `aws`)
475
+
476
+ Generate production-ready AWS deployment configuration supporting both AWS App Runner and Amazon ECS (Fargate):
477
+
478
+ ```bash
479
+ # Generate apprunner.yaml, ecs-task-definition.json, deploy-aws.sh, and Dockerfile
480
+ npx webspresso add:aws
481
+
482
+ # Customize region, CPU, and memory allocations
483
+ npx webspresso add:aws --region us-east-1 --cpu "1 vCPU" --memory "2 GB"
484
+
485
+ # Overwrite existing files
486
+ npx webspresso add:aws --force
487
+ ```
488
+
489
+ ### Generated Artifacts:
490
+ - **`apprunner.yaml`**: AWS App Runner configuration with build commands (`npm ci --omit=dev`), start command, and port network mapping.
491
+ - **`ecs-task-definition.json`**: Production ECS Fargate task definition with `awsvpc` network mode, container resource constraints, CloudWatch `awslogs`, and native `/healthz` health checks.
492
+ - **`deploy-aws.sh`**: Automated deployment shell script that verifies AWS credentials, creates Amazon ECR repositories, builds and pushes Docker images, and updates services.
493
+ - **`package.json` Scripts**: Adds `aws:deploy`.
494
+
495
+ ---
496
+
497
+ ## 23. Cloudflare Workers Deployment (`add:cloudflare` / `cloudflare`)
498
+
499
+ Generate serverless edge deployment files for Cloudflare Workers with D1, KV, and Queues support:
500
+
501
+ ```bash
502
+ # Generate wrangler.toml and worker.js
503
+ npx webspresso add:cloudflare
504
+
505
+ # Include Cloudflare D1, KV, and Queues
506
+ npx webspresso add:cloudflare --d1 --kv --queue
507
+ ```
508
+
509
+ ---
510
+
511
+ ## 24. Cloud Static Asset Sync (`assets:sync` / `assets-sync`)
512
+
513
+ Upload and synchronize compiled static assets (`public/assets/`, `dist/`, fonts, images) to S3, Cloudflare R2, or Google Cloud Storage with immutable CDN cache headers:
514
+
515
+ ```bash
516
+ # Preview files to be uploaded without transmitting data
517
+ npx webspresso assets:sync --dry-run
518
+
519
+ # Sync assets to an AWS S3 bucket
520
+ npx webspresso assets:sync --bucket my-cdn-bucket --provider s3
521
+
522
+ # Sync assets to Google Cloud Storage
523
+ npx webspresso assets:sync --bucket my-gcs-bucket --provider gcs --prefix assets
524
+
525
+ # Override directory or custom Cache-Control header
526
+ npx webspresso assets:sync --dir public --cache-control "public, max-age=604800"
527
+ ```
528
+
529
+ ---
530
+
531
+ ## 25. Cloud Secrets Synchronization (`secrets:pull` / `pull:secrets`)
532
+
533
+ Pull cloud secrets from Google Cloud Secret Manager or AWS Secrets Manager into a local `.env.local` file for secure local development:
534
+
535
+ ```bash
536
+ # Auto-detect cloud platform and pull expected secrets
537
+ npx webspresso secrets:pull
538
+
539
+ # Explicitly pull specific secrets from Google Cloud Secret Manager
540
+ npx webspresso secrets:pull --platform cloudrun -s SESSION_SECRET DATABASE_URL
541
+
542
+ # Explicitly pull from AWS Secrets Manager
543
+ npx webspresso secrets:pull --platform aws -s SESSION_SECRET DATABASE_URL
544
+
545
+ # Unpack a JSON secret bundle to stdout (dry run)
546
+ npx webspresso secrets:pull --bundle prod/app/config --dry-run
547
+
548
+ # Output secrets in JSON format
549
+ npx webspresso secrets:pull -s API_KEY DB_PASS --format json -o secrets.json
550
+ ```
551
+
552
+ ---
553
+
554
+ ## 26. Zero-Secret CI/CD Pipeline Generator (`add:ci` / `add:workflow`)
555
+
556
+ Scaffold automated, keyless GitHub Actions workflows (Google WIF, AWS IAM OIDC, Cloudflare) and GitLab CI pipelines with integrated test suites and `webspresso deploy:check --strict` gating:
557
+
558
+ ```bash
559
+ # Auto-detect cloud platform and scaffold GitHub Actions workflows
560
+ npx webspresso add:ci
561
+
562
+ # Explicitly target Google Cloud Run (Workload Identity Federation)
563
+ npx webspresso add:ci --platform cloudrun
564
+
565
+ # Explicitly target AWS (IAM OIDC Role & Amazon ECR)
566
+ npx webspresso add:ci --platform aws
567
+
568
+ # Explicitly target Cloudflare Workers
569
+ npx webspresso add:ci --platform cloudflare
570
+
571
+ # Scaffold all cloud workflows alongside a GitLab CI pipeline (.gitlab-ci.yml)
572
+ npx webspresso add:ci --platform all --gitlab
573
+ ```
574
+
575
+ ---
576
+
577
+ ## 27. Type-Safe Frontend Client Generator (`client:generate` / `client:gen`)
578
+
579
+ Generate an end-to-end typed TypeScript HTTP client from your Webspresso API routes and Zod schemas for React, Next.js, Vite, Vue, Svelte, or mobile apps:
580
+
581
+ ```bash
582
+ # Generate client into src/client.ts (or .webspresso/client.ts)
583
+ npx webspresso client:generate
584
+
585
+ # Specify custom output path and base URL
586
+ npx webspresso client:generate --out src/api-client.ts --base-url https://api.myapp.com
587
+ ```
588
+
589
+ ---
590
+
591
+ ## 28. DB Studio Live Telemetry & SQL Runner (`db:studio`)
592
+
593
+ Launch an interactive database inspector, SQL query runner, and real-time system monitoring dashboard:
594
+
595
+ ```bash
596
+ # Launch DB Studio on default port 4567
597
+ npx webspresso db:studio
598
+
599
+ # Custom port without opening default browser
600
+ npx webspresso db:studio --port 5000 --no-open
601
+ ```
602
+ - Schema inspection & CRUD operations for all database tables.
603
+ - Real-time SSE Live Telemetry streaming Node.js memory metrics (RSS, Heap), Knex connection pool utilization, background queues, and circuit breaker trip states.
604
+ - Interactive Circuit Breaker manual reset (`POST /_studio/api/breakers/:name/reset`).
605
+
606
+ ---
607
+
608
+ ## 29. Interactive REPL Console (`console` / `c`)
609
+
610
+ Launch an interactive Node.js REPL session with auto-injected framework context (database, repositories, services, queues, events, feature flags, and cache):
611
+
612
+ ```bash
613
+ # Start interactive console
614
+ npx webspresso console
615
+ # Alias:
616
+ npx webspresso c
617
+
618
+ # Evaluate a single expression and print output (with top-level await support)
619
+ npx webspresso console -e "await Note.find({ status: 'published' })"
620
+
621
+ # Target specific environment or config file
622
+ npx webspresso console -E production -c config/db.js
623
+
624
+ # Disable command history persistence or banner
625
+ npx webspresso console --no-history --no-banner
626
+ ```
627
+
628
+ ### Context & Shortcuts Injected:
629
+ - `db`: Knex / ORM database instance.
630
+ - `<ModelName>` (e.g. `Note`, `User`): Direct repository shortcuts (`await Note.find()`, `await Note.findById(1)`).
631
+ - `models`: Dictionary of all loaded model repositories (`models.Note`).
632
+ - `services`: ServiceRegistry instance.
633
+ - `service(name, input)`: Direct helper to invoke domain services (`await service('notes.create', payload)`).
634
+ - `queue`: Background QueueDispatcher (`await queue.dispatch('email.send', data)`).
635
+ - `events`: Decoupled EventDispatcher (`events.emit('order.created', order)`).
636
+ - `flags`: FeatureFlagManager (`flags.isEnabled('new_checkout', { user })`).
637
+ - `cache`: TaggedLruCache instance (`await cache.get('key')`).
638
+ - `mailer`: Framework mailer service.
639
+ - `reload()`: Hot-reloads all models and services from filesystem without exiting the REPL.
640
+ - `help()`: Displays interactive help menu with all available objects and commands.
641
+
642
+ ### Custom REPL Dot-Commands:
643
+ - `.models`: List all loaded models, table names, and available repository methods.
644
+ - `.services`: List all registered business services and descriptions.
645
+ - `.routes`: Scan and list all API endpoints and SSR pages.
646
+ - `.flags`: List active feature flags and targeting rules.
647
+ - `.reload`: Hot-reload models and services from filesystem.
648
+ - `.exit`: Exit the console session (`Ctrl+D`).
649
+
@@ -0,0 +1,120 @@
1
+ # HTTP 103 Early Hints & Critical Asset Preloading
2
+
3
+ Webspresso features built-in, zero-dependency support for **HTTP 103 Early Hints** (RFC 8288 / RFC 8297).
4
+ Early Hints allow Webspresso to send informational HTTP headers (containing CSS, JavaScript, and font preload instructions) to the client **before** server-side rendering (`load()`, database queries, external API calls) finishes, allowing the browser to download critical page assets in parallel.
5
+
6
+ ---
7
+
8
+ ## 🚀 How It Works
9
+
10
+ 1. The client sends an HTTP request (`GET /products/123`).
11
+ 2. Webspresso resolves the matching route and its companion CSS/JS asset candidates via `AssetManager`.
12
+ 3. If Early Hints are enabled, Webspresso immediately writes an `HTTP/1.1 103 Early Hints` response with `Link: </css/products.css>; rel=preload; as=style, </js/products.js>; rel=preload; as=script`.
13
+ 4. While the browser starts downloading stylesheets and scripts, Webspresso runs the page's async `load()` function and Nunjucks rendering.
14
+ 5. Once the HTML is ready, Webspresso returns `HTTP/1.1 200 OK` with the final document.
15
+ 6. The browser can immediately render without waiting for secondary asset network round-trips (reducing TTFB, FCP and LCP).
16
+
17
+ ---
18
+
19
+ ## 🛠️ Configuration
20
+
21
+ ### Global Configuration (`createApp`)
22
+
23
+ Enable automatic route asset preloading globally:
24
+
25
+ ```javascript
26
+ const { createApp } = require('webspresso');
27
+
28
+ const app = createApp({
29
+ earlyHints: true, // Automatically preloads route CSS & JS
30
+ // Or with custom default links:
31
+ // earlyHints: {
32
+ // auto: true,
33
+ // links: ['/css/global.css', '/fonts/inter.woff2'],
34
+ // }
35
+ });
36
+ ```
37
+
38
+ ### Route / Page Configuration (`definePage`)
39
+
40
+ Configure Early Hints per-page inside your page module:
41
+
42
+ ```javascript
43
+ const { definePage } = require('webspresso');
44
+
45
+ module.exports = definePage({
46
+ // Preload route assets automatically
47
+ earlyHints: true,
48
+
49
+ // Or specify exact links:
50
+ // earlyHints: [
51
+ // '/css/special.css',
52
+ // { url: '/fonts/inter.woff2', as: 'font', crossOrigin: 'anonymous' }
53
+ // ],
54
+
55
+ load: async (ctx) => {
56
+ // You can also dispatch hints manually during load:
57
+ ctx.earlyHints(['/extra-chunk.js']);
58
+
59
+ const product = await ctx.db.getRepository('Product').findById(ctx.params.id);
60
+ return { product };
61
+ },
62
+ });
63
+ ```
64
+
65
+ ### In Route Handlers and API Middleware
66
+
67
+ `res.earlyHints(links, [callback])` is attached to `res` on every request:
68
+
69
+ ```javascript
70
+ app.get('/dashboard', (req, res) => {
71
+ // Send 103 Early Hints
72
+ res.earlyHints([
73
+ '/css/dashboard.css',
74
+ '/js/charts.js',
75
+ ]);
76
+
77
+ // Run async queries...
78
+ setTimeout(() => {
79
+ res.send('<h1>Dashboard</h1>');
80
+ }, 200);
81
+ });
82
+ ```
83
+
84
+ ---
85
+
86
+ ## 📋 Link Options & Deduction
87
+
88
+ Webspresso automatically deduces `as` and `crossorigin` attributes based on URL file extensions:
89
+
90
+ | Extension | Inferred `as` | Browser CORS Requirement |
91
+ | :--- | :--- | :--- |
92
+ | `.css` | `style` | - |
93
+ | `.js`, `.mjs`, `.cjs` | `script` | - |
94
+ | `.woff2`, `.woff`, `.ttf` | `font` | Auto-adds `crossorigin` |
95
+ | `.png`, `.jpg`, `.webp`, `.svg` | `image` | - |
96
+ | `.json` | `fetch` | - |
97
+
98
+ You can also pass full descriptor objects:
99
+
100
+ ```javascript
101
+ res.earlyHints([
102
+ {
103
+ url: '/fonts/inter.woff2',
104
+ as: 'font',
105
+ crossOrigin: 'anonymous',
106
+ fetchPriority: 'high',
107
+ },
108
+ {
109
+ url: 'https://cdn.example.com',
110
+ rel: 'preconnect',
111
+ },
112
+ ]);
113
+ ```
114
+
115
+ ---
116
+
117
+ ## 🛡️ Resilience & Fallbacks
118
+
119
+ - If the client or proxy does not support HTTP 103, or if running in an environment without `res.writeEarlyHints` (HTTP/1.0, test mocks), `sendEarlyHints` returns `false` safely without throwing.
120
+ - Calling `earlyHints` after response headers have been sent (`res.headersSent === true`) is a safe no-op.