@c9up/ream 0.2.4 → 0.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/README.md +3 -9
  2. package/dist/Application.d.ts +85 -5
  3. package/dist/Application.d.ts.map +1 -1
  4. package/dist/Application.js +171 -21
  5. package/dist/Application.js.map +1 -1
  6. package/dist/ErrorBoundary.d.ts +1 -7
  7. package/dist/ErrorBoundary.d.ts.map +1 -1
  8. package/dist/ErrorBoundary.js +31 -31
  9. package/dist/ErrorBoundary.js.map +1 -1
  10. package/dist/HttpKernel.js +2 -2
  11. package/dist/HttpKernel.js.map +1 -1
  12. package/dist/Ignitor.d.ts +18 -49
  13. package/dist/Ignitor.d.ts.map +1 -1
  14. package/dist/Ignitor.js +211 -173
  15. package/dist/Ignitor.js.map +1 -1
  16. package/dist/env/Env.d.ts +6 -0
  17. package/dist/env/Env.d.ts.map +1 -1
  18. package/dist/env/Env.js +6 -0
  19. package/dist/env/Env.js.map +1 -1
  20. package/dist/events/Emitter.d.ts +0 -6
  21. package/dist/events/Emitter.d.ts.map +1 -1
  22. package/dist/events/Emitter.js +42 -42
  23. package/dist/events/Emitter.js.map +1 -1
  24. package/dist/events/native.d.ts +13 -11
  25. package/dist/events/native.d.ts.map +1 -1
  26. package/dist/events/native.js.map +1 -1
  27. package/dist/helpers/compose.d.ts +30 -0
  28. package/dist/helpers/compose.d.ts.map +1 -0
  29. package/dist/helpers/compose.js +27 -0
  30. package/dist/helpers/compose.js.map +1 -0
  31. package/dist/helpers/index.d.ts +1 -0
  32. package/dist/helpers/index.d.ts.map +1 -1
  33. package/dist/helpers/index.js +1 -0
  34. package/dist/helpers/index.js.map +1 -1
  35. package/dist/helpers/is.d.ts +69 -16
  36. package/dist/helpers/is.d.ts.map +1 -1
  37. package/dist/helpers/is.js +79 -16
  38. package/dist/helpers/is.js.map +1 -1
  39. package/dist/index.d.ts +4 -3
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +2 -3
  42. package/dist/index.js.map +1 -1
  43. package/dist/middleware/Pipeline.d.ts +9 -3
  44. package/dist/middleware/Pipeline.d.ts.map +1 -1
  45. package/dist/middleware/Pipeline.js +10 -4
  46. package/dist/middleware/Pipeline.js.map +1 -1
  47. package/dist/native/generated.d.ts +243 -0
  48. package/dist/native/generated.d.ts.map +1 -0
  49. package/dist/native/generated.js +7 -0
  50. package/dist/native/generated.js.map +1 -0
  51. package/dist/rpc/RpcRouter.js +2 -2
  52. package/dist/rpc/RpcRouter.js.map +1 -1
  53. package/dist/services/app.d.ts +6 -0
  54. package/dist/services/app.d.ts.map +1 -1
  55. package/dist/services/app.js +8 -0
  56. package/dist/services/app.js.map +1 -1
  57. package/dist/services/config.d.ts +15 -0
  58. package/dist/services/config.d.ts.map +1 -0
  59. package/dist/services/config.js +18 -0
  60. package/dist/services/config.js.map +1 -0
  61. package/dist/services/encryption.d.ts +23 -0
  62. package/dist/services/encryption.d.ts.map +1 -0
  63. package/dist/services/encryption.js +33 -0
  64. package/dist/services/encryption.js.map +1 -0
  65. package/dist/services/urlBuilder.d.ts +21 -0
  66. package/dist/services/urlBuilder.d.ts.map +1 -0
  67. package/dist/services/urlBuilder.js +34 -0
  68. package/dist/services/urlBuilder.js.map +1 -0
  69. package/dist/session/drivers/CookieDriver.d.ts +1 -1
  70. package/dist/session/drivers/CookieDriver.d.ts.map +1 -1
  71. package/dist/session/drivers/CookieDriver.js +4 -4
  72. package/dist/session/drivers/CookieDriver.js.map +1 -1
  73. package/dist/testing/ApiResponse.d.ts +44 -44
  74. package/dist/testing/ApiResponse.d.ts.map +1 -1
  75. package/dist/testing/ApiResponse.js +48 -48
  76. package/dist/testing/ApiResponse.js.map +1 -1
  77. package/dist/testing/RequestBuilder.d.ts +39 -39
  78. package/dist/testing/RequestBuilder.d.ts.map +1 -1
  79. package/dist/testing/RequestBuilder.js +49 -49
  80. package/dist/testing/RequestBuilder.js.map +1 -1
  81. package/dist/testing/TestClient.d.ts +5 -5
  82. package/dist/testing/TestClient.d.ts.map +1 -1
  83. package/dist/testing/TestClient.js +7 -7
  84. package/dist/testing/TestClient.js.map +1 -1
  85. package/events.win32-x64-msvc.node +0 -0
  86. package/index.win32-x64-msvc.node +0 -0
  87. package/package.json +15 -2
  88. package/scheduler.win32-x64-msvc.node +0 -0
  89. package/scripts/build-napi-types.mjs +68 -0
  90. package/scripts/generate-napi-types.mjs +171 -0
  91. package/src/Application.ts +221 -21
  92. package/src/ErrorBoundary.ts +31 -31
  93. package/src/HttpKernel.ts +2 -2
  94. package/src/Ignitor.ts +228 -176
  95. package/src/env/Env.ts +6 -0
  96. package/src/events/Emitter.ts +42 -42
  97. package/src/events/native.ts +21 -14
  98. package/src/helpers/compose.ts +84 -0
  99. package/src/helpers/index.ts +1 -0
  100. package/src/helpers/is.ts +87 -16
  101. package/src/index.ts +3 -3
  102. package/src/middleware/Pipeline.ts +10 -4
  103. package/src/native/generated.ts +291 -0
  104. package/src/rpc/RpcRouter.ts +2 -2
  105. package/src/services/app.ts +9 -0
  106. package/src/services/config.ts +24 -0
  107. package/src/services/encryption.ts +42 -0
  108. package/src/services/urlBuilder.ts +41 -0
  109. package/src/session/drivers/CookieDriver.ts +4 -4
  110. package/src/testing/ApiResponse.ts +52 -52
  111. package/src/testing/RequestBuilder.ts +55 -55
  112. package/src/testing/TestClient.ts +8 -8
@@ -0,0 +1,291 @@
1
+ // GENERATED FROM THE RUST — do not edit.
2
+ //
3
+ // Produced by scripts/generate-napi-types.mjs from napi-derive's type-def
4
+ // output. Editing this file by hand puts it back where it started: a
5
+ // description that can disagree with the code it describes.
6
+
7
+ /**
8
+ * Typed response object crossing the TS→NAPI→Rust boundary.
9
+ *
10
+ * Using `#[napi(object)]` (instead of `String` carrying JSON) means napi-rs
11
+ * walks the JS object tree directly into Rust fields — zero JSON parse overhead.
12
+ *
13
+ * When `stream_id` is set, `body` is ignored and the hyper response is fed
14
+ * from the matching entry in the shared `StreamRegistry`. The handler must
15
+ * have called [`HyperServer::register_stream`] with that id BEFORE
16
+ * returning, otherwise the response collapses to a 500 (E_STREAM_UNKNOWN).
17
+ */
18
+
19
+ export interface NapiResponse {
20
+ status: number
21
+ headers: Record<string, string>
22
+ body: string
23
+ streamId?: string
24
+ }
25
+
26
+ /**
27
+ * Configuration for the wire-level shield filter. Mirrors the Rust
28
+ * `ShieldConfig` shape; the JS layer constructs it from `ShieldMiddleware`
29
+ * options at boot and hands it off to `HyperServer.configureShield`.
30
+ */
31
+
32
+ export interface NapiShieldConfig {
33
+ pathTraversal: boolean
34
+ paramPollution: boolean
35
+ }
36
+
37
+ /**
38
+ * Configuration for the wire-level rate limiter. `windowSecs` is the size
39
+ * of the fixed window in seconds; `max` is the request budget per key
40
+ * (resolved client IP) per window.
41
+ */
42
+
43
+ export interface NapiRateLimitConfig {
44
+ max: number
45
+ windowSecs: number
46
+ }
47
+
48
+ /**
49
+ * NAPI-exposed event bus.
50
+ * Uses shared Tokio runtime (no per-instance runtime).
51
+ */
52
+
53
+ export declare class EventBus {
54
+ constructor(requestHandlerTimeoutMs?: number | undefined | null)
55
+ /** Emit an event (async — does NOT block Node.js thread). */
56
+ emit(name: string, data: string): Promise<string>
57
+ /**
58
+ * Subscribe to events matching a pattern. Callback receives event JSON string.
59
+ * Returns subscription ID.
60
+ *
61
+ * NOTE: This is `pub fn` (sync) — napi-rs cannot make `async fn` capture
62
+ * `JsFunction` because JsFunction isn't Send. The block_on here runs on the
63
+ * JS thread (no async context above), so it doesn't deadlock the runtime.
64
+ */
65
+ subscribe(pattern: string, callback: (eventJson: string) => void): number
66
+ /** Unsubscribe by subscription ID. */
67
+ unsubscribe(subId: number): Promise<void>
68
+ /** Register a request handler. */
69
+ onRequest(
70
+ name: string,
71
+ callback: (eventJson: string, reply: (response: string) => void) => void,
72
+ ): void
73
+ /** Send a request and get a response (async with timeout). */
74
+ request(name: string, data: string, timeoutMs?: number | undefined | null): Promise<string>
75
+ /** Check if a pattern matches an event name (wildcard matching via Rust). */
76
+ matchesWildcard(pattern: string, eventName: string): boolean
77
+ /** Get subscription count. */
78
+ subscriptionCount(): Promise<number>
79
+ }
80
+
81
+ /** NAPI-exposed Hyper HTTP server. */
82
+
83
+ export declare class HyperServer {
84
+ /**
85
+ * `host` is the bind address: an IPv4 literal (`0.0.0.0` to accept from
86
+ * every interface) or the `localhost` alias. Omitted → loopback only,
87
+ * the safe default for a dev machine.
88
+ */
89
+ constructor(port?: number | undefined | null, host?: string | undefined | null)
90
+ /** Register the request handler. Callback receives JSON request string, must return JSON response string. */
91
+ onRequest(callback: (request: string) => Promise<string> | string): void
92
+ /**
93
+ * Configure the trusted-proxy CIDR list used by `request.ip` resolution.
94
+ * Must be called BEFORE `listen()`. Empty list = legacy permissive mode
95
+ * (any peer can populate `X-Forwarded-For`).
96
+ */
97
+ configureTrustedProxies(cidrs: Array<string>): void
98
+ /**
99
+ * Install (or replace) the wire-level rate limiter. Pass `None` to
100
+ * disable. Must be called BEFORE `listen()`.
101
+ */
102
+ configureRateLimit(config?: NapiRateLimitConfig | undefined | null): void
103
+ /**
104
+ * Configure the wire-level security shield. Must be called BEFORE
105
+ * `listen()` — the configuration is captured into the security filter
106
+ * when the server starts. Calling after `listen()` has no effect.
107
+ */
108
+ configureShield(config: NapiShieldConfig): void
109
+ /** Start the HTTP server. */
110
+ listen(): Promise<void>
111
+ /** Get the actual bound port. */
112
+ port(): Promise<number>
113
+ /**
114
+ * Shut down the server and release the handles that keep the host's event
115
+ * loop alive.
116
+ *
117
+ * `listen()` registers the JS callback as a `ThreadsafeFunction` (which
118
+ * holds a libuv ref so `ream start` stays alive). Three `Arc` clones of the
119
+ * request handler capture it: this struct's `self.handler`, the inner
120
+ * `ReamServer.handler`, and the accept-loop task's clone. ALL three must
121
+ * drop for napi-rs to release the tsfn on `Drop` (refcount → 0). The old
122
+ * `close()` only signalled shutdown, leaving every clone alive — so an
123
+ * in-process host (test harness) leaked the handle and never drained.
124
+ */
125
+ close(): Promise<void>
126
+ /**
127
+ * Reserve a slot in the stream registry. Returns `false` if the id
128
+ * already exists (caller picked a colliding UUID). Always returns
129
+ * before `listen()` need have run.
130
+ */
131
+ registerStream(streamId: string): Promise<boolean>
132
+ /**
133
+ * Push a chunk onto a registered stream. Returns `false` when the
134
+ * stream has already finished (receiver dropped — client gone or
135
+ * `closeStream` called) so the caller can bail out of its push
136
+ * loop. Backpressure is handled silently: a full buffer drops the
137
+ * frame rather than awaiting — SSE is fire-and-forget by design.
138
+ */
139
+ writeStream(streamId: string, chunk: string): Promise<boolean>
140
+ /**
141
+ * Push a BINARY chunk onto a registered stream, waiting for room.
142
+ *
143
+ * The counterpart of [`write_stream`](Self::write_stream) for a body the
144
+ * client reassembles — a file, an export, an archive. Two differences,
145
+ * both required there and both wrong for SSE:
146
+ *
147
+ * * it takes bytes, not a `String`, so a payload that is not valid UTF-8
148
+ * survives the crossing intact;
149
+ * * it AWAITS when the channel is full instead of dropping the frame, so
150
+ * a slow client slows the producer rather than silently receiving a
151
+ * truncated file.
152
+ *
153
+ * Returns `false` when the receiver is gone (client disconnected or the
154
+ * stream was closed), so the caller can stop reading its source.
155
+ */
156
+ writeStreamBytes(streamId: string, chunk: Uint8Array): Promise<boolean>
157
+ /**
158
+ * Close a stream from the server side. The matching response body
159
+ * finishes cleanly (hyper writes the final chunk + `0
160
+
161
+ ` for
162
+ * HTTP/1.1 chunked encoding). Idempotent.
163
+ */
164
+ closeStream(streamId: string): Promise<boolean>
165
+ /**
166
+ * Install a one-shot callback fired the moment the matching stream
167
+ * receiver is dropped — i.e. the client disconnected or
168
+ * `closeStream` was called. Used by the JS SDK to drop subscription
169
+ * bookkeeping when an SSE viewer leaves.
170
+ *
171
+ * The callback runs on the libuv loop via the threadsafe-function
172
+ * machinery, so JS code in the callback uses regular await /
173
+ * reentrant container calls without crossing-thread surprises.
174
+ */
175
+ onStreamDisconnect(streamId: string, callback: (streamId: string) => void): void
176
+ }
177
+
178
+ /**
179
+ * Scheduler instance exposed to TypeScript.
180
+ *
181
+ * Usage (from TS):
182
+ * ```ignore
183
+ * const scheduler = new RustScheduler();
184
+ * scheduler.register('cleanup', '0 *\/5 * * *', (payload) => { ... });
185
+ * scheduler.start();
186
+ * // later
187
+ * scheduler.stop();
188
+ * ```
189
+ */
190
+
191
+ export declare class RustScheduler {
192
+ constructor()
193
+ /**
194
+ * Register a task. `cronExpr` is a standard 5-field cron expression
195
+ * evaluated in UTC.
196
+ *
197
+ * Throws with `DUPLICATE_TASK` if `name` already exists, or
198
+ * `INVALID_CRON` if the expression is malformed.
199
+ */
200
+ register(
201
+ name: string,
202
+ cronExpr: string,
203
+ callback: (invocation: { taskName: string; scheduledForMs: number }) => void,
204
+ ): void
205
+ /** Remove a task. Idempotent — unknown names are not an error. */
206
+ unregister(name: string): void
207
+ /** Launch the tick loop on the shared Tokio runtime. Idempotent. */
208
+ start(): void
209
+ /** Cancel the tick loop. Safe to call even if not running. */
210
+ stop(): void
211
+ /** Return the next fire time in ms epoch, or `null` if the task is unknown. */
212
+ nextRun(name: string): number | null
213
+ }
214
+
215
+ /**
216
+ * Sign a JWT payload with HMAC-SHA256 (Rust-native).
217
+ * Returns the complete JWT token string.
218
+ */
219
+
220
+ export declare function jwtSign(payload: string, secret: string): string
221
+
222
+ /**
223
+ * Verify a JWT token and return the decoded payload JSON string.
224
+ * Validates signature (constant-time), exp, and nbf claims.
225
+ */
226
+
227
+ export declare function jwtVerify(token: string, secret: string): string
228
+
229
+ /**
230
+ * Hash a password with Argon2id (Rust-native).
231
+ * Returns the PHC-formatted hash string.
232
+ */
233
+
234
+ export declare function argon2Hash(password: string): string
235
+
236
+ /** Verify a password against an Argon2id hash. */
237
+
238
+ export declare function argon2Verify(password: string, hash: string): boolean
239
+
240
+ /**
241
+ * Hash a password with bcrypt (Rust-native).
242
+ * `rounds` defaults to 12 when not supplied — matches the previous
243
+ * warden-engine default and the OWASP-recommended cost floor enforced
244
+ * by `sigil-engine`.
245
+ */
246
+
247
+ export declare function bcryptHash(password: string, rounds?: number | undefined | null): string
248
+
249
+ /** Verify a password against a bcrypt hash. */
250
+
251
+ export declare function bcryptVerify(password: string, hash: string): boolean
252
+
253
+ /** Constant-time string comparison. */
254
+
255
+ export declare function constantTimeEq(a: string, b: string): boolean
256
+
257
+ /** HMAC-SHA256 sign. Returns base64url signature. */
258
+
259
+ export declare function hmacSign(data: string, secret: string): string
260
+
261
+ /** HMAC-SHA256 verify (constant-time). */
262
+
263
+ export declare function hmacVerify(data: string, signature: string, secret: string): boolean
264
+
265
+ /** Generate cryptographically secure random bytes as base64url. */
266
+
267
+ export declare function randomBytesBase64(len: number): string
268
+
269
+ /** Generate cryptographically secure random bytes as hex. */
270
+
271
+ export declare function randomHex(len: number): string
272
+
273
+ /**
274
+ * Parse a GraphQL query using the Rust graphql-parser crate.
275
+ * Returns JSON: { operationType, operationName, fields: [...], errors: [...] }
276
+ * Invalid queries are rejected here — they never reach the TypeScript resolver layer.
277
+ */
278
+
279
+ export declare function graphqlParse(query: string): string
280
+
281
+ /** Validate a GraphQL query string. Returns JSON array of error strings (empty = valid). */
282
+
283
+ export declare function graphqlValidate(query: string): string
284
+
285
+ /**
286
+ * Extract argument scalar types from a GraphQL SDL schema.
287
+ * Returns JSON: `{ "Type.field": { "argName": "ScalarType" } }` for argument
288
+ * coercion in the TypeScript engine.
289
+ */
290
+
291
+ export declare function graphqlSchemaArgTypes(sdl: string): string
@@ -20,7 +20,7 @@ import {
20
20
  } from '@c9up/comet'
21
21
  import type { HttpContext } from '../http/HttpContext.js'
22
22
  import type { MiddlewareRegistry, RuntimeValidator } from '../middleware/Pipeline.js'
23
- import { compose, runValidator } from '../middleware/Pipeline.js'
23
+ import { composeMiddleware, runValidator } from '../middleware/Pipeline.js'
24
24
 
25
25
  export type RpcHandler = (ctx: HttpContext, params: unknown) => Promise<unknown> | unknown
26
26
 
@@ -252,7 +252,7 @@ export class RpcRouter {
252
252
  }
253
253
  return mw
254
254
  })
255
- await compose(fns)(ctx, async () => {})
255
+ await composeMiddleware(fns)(ctx, async () => {})
256
256
  }
257
257
 
258
258
  async #processOne(ctx: HttpContext, request: unknown): Promise<unknown> {
@@ -26,6 +26,15 @@ export function clearApp(app: Application): void {
26
26
  if (instance === app) instance = undefined
27
27
  }
28
28
 
29
+ /**
30
+ * @internal Get the app instance directly. Used by the accessors that stand in
31
+ * for something the app OWNS (`services/config` is `app.config`), so they read
32
+ * through here instead of keeping a second copy that a re-boot could desync.
33
+ */
34
+ export function getApp(): Application | undefined {
35
+ return instance
36
+ }
37
+
29
38
  const app: Application = createServiceProxy<Application>(
30
39
  () => instance,
31
40
  'Application accessed before initialization. ' +
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Config store singleton service.
3
+ *
4
+ * Usage:
5
+ * import config from '@c9up/ream/services/config'
6
+ * const port = config.get<number>('app.port', 3333)
7
+ *
8
+ * Unlike the other accessors this one keeps no instance of its own: the store
9
+ * IS `app.config`, so it reads through the app locator. One source of truth
10
+ * means the two can never point at different stores after a re-boot.
11
+ */
12
+
13
+ import type { ConfigStore } from '../ConfigLoader.js'
14
+ import { getApp } from './app.js'
15
+ import { createServiceProxy } from './createServiceProxy.js'
16
+
17
+ const config: ConfigStore = createServiceProxy<ConfigStore>(
18
+ () => getApp()?.config,
19
+ 'Config accessed before initialization. ' +
20
+ 'The store is built during the boot phase — read it from inside a provider, ' +
21
+ 'a preload, or a request, not at module top level.',
22
+ )
23
+
24
+ export default config
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Cookie/value signing singleton service.
3
+ *
4
+ * Usage:
5
+ * import encryption from '@c9up/ream/services/encryption'
6
+ * const signed = encryption.sign('payload')
7
+ *
8
+ * Registered only when `APP_KEY` is set — there is nothing to sign with
9
+ * otherwise — so the not-ready message names that rather than the boot phase.
10
+ */
11
+
12
+ import type { CookieSigner } from '../security/CookieSigner.js'
13
+ import { createServiceProxy } from './createServiceProxy.js'
14
+
15
+ let instance: CookieSigner | undefined
16
+
17
+ /** @internal Set the signer (called by Ignitor once APP_KEY is validated). */
18
+ export function setEncryption(signer: CookieSigner): void {
19
+ instance = signer
20
+ }
21
+
22
+ /**
23
+ * @internal Unset the locator IF it still points at `signer` (called by
24
+ * Ignitor.stop()). Ownership-guarded — see services/app.ts.
25
+ */
26
+ export function clearEncryption(signer: CookieSigner): void {
27
+ if (instance === signer) instance = undefined
28
+ }
29
+
30
+ /** @internal Get the signer directly. */
31
+ export function getEncryption(): CookieSigner | undefined {
32
+ return instance
33
+ }
34
+
35
+ const encryption: CookieSigner = createServiceProxy<CookieSigner>(
36
+ () => instance,
37
+ '[E_MISSING_APP_KEY] Encryption accessed before it was registered. ' +
38
+ 'It exists only when APP_KEY is set (>= 16 characters) — check the ' +
39
+ 'environment before reaching for it, or you are reading it before boot.',
40
+ )
41
+
42
+ export default encryption
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Named-route URL helpers, as free functions.
3
+ *
4
+ * Usage:
5
+ * import { urlFor, signedUrlFor } from '@c9up/ream/services/urlBuilder'
6
+ * const href = urlFor('users.show', { id: '42' })
7
+ * const link = signedUrlFor('email.verify', { id: '42' }, { expiresIn: '1h' })
8
+ *
9
+ * Both delegate to the router — the same code `router.urlFor` /
10
+ * `router.makeSignedUrl` run — so a route renamed in one place cannot mean two
11
+ * things. They exist as functions rather than as another proxy object because
12
+ * that is all there is to the URL builder: two calls, no state.
13
+ *
14
+ * `signedUrlFor` needs an APP_KEY-backed signer and says so if there is none.
15
+ */
16
+
17
+ import type { SignedUrlOptions } from '../router/Router.js'
18
+ import { getRouter } from './router.js'
19
+
20
+ const NOT_READY =
21
+ 'URL builder used before initialization. ' +
22
+ 'Routes are registered during the boot phase — build URLs from inside a ' +
23
+ 'controller, a view, or a preload, not at module top level.'
24
+
25
+ /** Generate a URL from a named route, filling `:param` placeholders. */
26
+ export function urlFor(name: string, params?: Record<string, string>): string {
27
+ const router = getRouter()
28
+ if (!router) throw new Error(NOT_READY)
29
+ return router.urlFor(name, params)
30
+ }
31
+
32
+ /** Generate a tamper-proof signed URL for a named route. */
33
+ export function signedUrlFor(
34
+ name: string,
35
+ params?: Record<string, string>,
36
+ options?: SignedUrlOptions,
37
+ ): string {
38
+ const router = getRouter()
39
+ if (!router) throw new Error(NOT_READY)
40
+ return router.makeSignedUrl(name, params, options)
41
+ }
@@ -7,7 +7,7 @@ import type { SessionDriver } from '../Session.js'
7
7
  * Requires a secret for encryption.
8
8
  */
9
9
  export class CookieDriver implements SessionDriver {
10
- private signer: CookieSigner
10
+ #signer: CookieSigner
11
11
 
12
12
  constructor(secret: string) {
13
13
  if (!secret) {
@@ -15,12 +15,12 @@ export class CookieDriver implements SessionDriver {
15
15
  'CookieDriver requires a secret for session encryption. Set session.secret in your config.',
16
16
  )
17
17
  }
18
- this.signer = new CookieSigner(secret)
18
+ this.#signer = new CookieSigner(secret)
19
19
  }
20
20
 
21
21
  async read(sessionId: string): Promise<Record<string, unknown> | null> {
22
22
  if (!sessionId) return null
23
- const decrypted = this.signer.decrypt(sessionId)
23
+ const decrypted = this.#signer.decrypt(sessionId)
24
24
  if (!decrypted) return null
25
25
  try {
26
26
  return JSON.parse(decrypted)
@@ -43,6 +43,6 @@ export class CookieDriver implements SessionDriver {
43
43
 
44
44
  /** Encode session data for cookie storage (encrypted). */
45
45
  encode(data: Record<string, unknown>): string {
46
- return this.signer.encrypt(JSON.stringify(data))
46
+ return this.#signer.encrypt(JSON.stringify(data))
47
47
  }
48
48
  }