@c9up/ream 0.2.3 → 0.2.5

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 (71) hide show
  1. package/dist/Application.d.ts +85 -5
  2. package/dist/Application.d.ts.map +1 -1
  3. package/dist/Application.js +171 -21
  4. package/dist/Application.js.map +1 -1
  5. package/dist/ErrorBoundary.d.ts +1 -7
  6. package/dist/ErrorBoundary.d.ts.map +1 -1
  7. package/dist/ErrorBoundary.js +31 -31
  8. package/dist/ErrorBoundary.js.map +1 -1
  9. package/dist/Ignitor.d.ts +15 -46
  10. package/dist/Ignitor.d.ts.map +1 -1
  11. package/dist/Ignitor.js +178 -173
  12. package/dist/Ignitor.js.map +1 -1
  13. package/dist/env/Env.d.ts +6 -0
  14. package/dist/env/Env.d.ts.map +1 -1
  15. package/dist/env/Env.js +6 -0
  16. package/dist/env/Env.js.map +1 -1
  17. package/dist/events/Emitter.d.ts +2 -6
  18. package/dist/events/Emitter.d.ts.map +1 -1
  19. package/dist/events/Emitter.js +45 -42
  20. package/dist/events/Emitter.js.map +1 -1
  21. package/dist/events/native.d.ts +13 -11
  22. package/dist/events/native.d.ts.map +1 -1
  23. package/dist/events/native.js.map +1 -1
  24. package/dist/http/HttpContext.d.ts +1 -1
  25. package/dist/http/HttpContext.d.ts.map +1 -1
  26. package/dist/http/Request.js +0 -13
  27. package/dist/http/Request.js.map +1 -1
  28. package/dist/index.d.ts +3 -2
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +1 -2
  31. package/dist/index.js.map +1 -1
  32. package/dist/native/generated.d.ts +243 -0
  33. package/dist/native/generated.d.ts.map +1 -0
  34. package/dist/native/generated.js +7 -0
  35. package/dist/native/generated.js.map +1 -0
  36. package/dist/session/drivers/CookieDriver.d.ts +1 -1
  37. package/dist/session/drivers/CookieDriver.d.ts.map +1 -1
  38. package/dist/session/drivers/CookieDriver.js +4 -4
  39. package/dist/session/drivers/CookieDriver.js.map +1 -1
  40. package/dist/types/index.d.ts +88 -5
  41. package/dist/types/index.d.ts.map +1 -1
  42. package/dist/types/index.js +8 -3
  43. package/dist/types/index.js.map +1 -1
  44. package/events.win32-x64-msvc.node +0 -0
  45. package/index.win32-x64-msvc.node +0 -0
  46. package/package.json +3 -2
  47. package/scheduler.win32-x64-msvc.node +0 -0
  48. package/scripts/build-napi-types.mjs +68 -0
  49. package/scripts/generate-napi-types.mjs +171 -0
  50. package/src/Application.ts +221 -21
  51. package/src/ErrorBoundary.ts +31 -31
  52. package/src/Ignitor.ts +185 -172
  53. package/src/env/Env.ts +6 -0
  54. package/src/events/Emitter.ts +45 -42
  55. package/src/events/native.ts +21 -14
  56. package/src/http/HttpContext.ts +1 -1
  57. package/src/http/Request.ts +0 -13
  58. package/src/index.ts +2 -2
  59. package/src/native/generated.ts +291 -0
  60. package/src/session/drivers/CookieDriver.ts +4 -4
  61. package/src/types/index.ts +92 -5
  62. package/dist/types/authenticators.d.ts +0 -24
  63. package/dist/types/authenticators.d.ts.map +0 -1
  64. package/dist/types/authenticators.js +0 -2
  65. package/dist/types/authenticators.js.map +0 -1
  66. package/dist/types/container.d.ts +0 -64
  67. package/dist/types/container.d.ts.map +0 -1
  68. package/dist/types/container.js +0 -23
  69. package/dist/types/container.js.map +0 -1
  70. package/src/types/authenticators.ts +0 -24
  71. package/src/types/container.ts +0 -66
package/src/env/Env.ts CHANGED
@@ -68,6 +68,12 @@ export class Env<Values extends Record<string, unknown>> {
68
68
 
69
69
  readonly #values: Values
70
70
 
71
+ /**
72
+ * Private on purpose: an `Env` is only ever built by {@link create}, which is
73
+ * what validates. `private` and not `#` because a private CONSTRUCTOR has no
74
+ * native form — this is the one place the keyword carries a meaning `#`
75
+ * cannot express.
76
+ */
71
77
  private constructor(values: Values) {
72
78
  this.#values = values
73
79
  }
@@ -66,10 +66,10 @@ export type UnsubscribeFunction = () => void
66
66
  export type AsyncUnsubscribeFunction = () => Promise<void>
67
67
 
68
68
  export class Emitter {
69
- private bus: EventBus
70
- private resolver?: EmitterResolver
71
- private classListeners: Map<EventConstructor, Listener[]> = new Map()
72
- private stringListeners: Map<string, ListenerFn[]> = new Map()
69
+ #bus: EventBus
70
+ #resolver?: EmitterResolver
71
+ #classListeners: Map<EventConstructor, Listener[]> = new Map()
72
+ #stringListeners: Map<string, ListenerFn[]> = new Map()
73
73
  /** Set while faking; every emission lands here instead of running. */
74
74
  #buffer?: EventsBuffer
75
75
  /** Which events are faked. `undefined` means all of them. */
@@ -78,8 +78,8 @@ export class Emitter {
78
78
  readonly #anySubscriptions = new Map<(eventName: string, data: unknown) => void, Set<number>>()
79
79
 
80
80
  constructor(bus: EventBus, resolver?: EmitterResolver) {
81
- this.bus = bus
82
- this.resolver = resolver
81
+ this.#bus = bus
82
+ this.#resolver = resolver
83
83
  }
84
84
 
85
85
  // ─── Class-based events ───────────────────────────────────
@@ -102,18 +102,18 @@ export class Emitter {
102
102
  on<T>(event: string, listener: ListenerFn<T>): UnsubscribeFunction
103
103
  on(event: EventConstructor | string, listener: Listener): UnsubscribeFunction {
104
104
  if (typeof event === 'string') {
105
- const list = this.stringListeners.get(event) ?? []
105
+ const list = this.#stringListeners.get(event) ?? []
106
106
  list.push(listener as ListenerFn)
107
- this.stringListeners.set(event, list)
107
+ this.#stringListeners.set(event, list)
108
108
  return () => {
109
109
  this.off(event, listener as ListenerFn)
110
110
  }
111
111
  }
112
- const list = this.classListeners.get(event) ?? []
112
+ const list = this.#classListeners.get(event) ?? []
113
113
  list.push(listener)
114
- this.classListeners.set(event, list)
114
+ this.#classListeners.set(event, list)
115
115
  return () => {
116
- const current = this.classListeners.get(event)
116
+ const current = this.#classListeners.get(event)
117
117
  if (!current) return
118
118
  const index = current.indexOf(listener)
119
119
  if (index !== -1) current.splice(index, 1)
@@ -159,11 +159,11 @@ export class Emitter {
159
159
 
160
160
  /** Remove one listener from an event (AdonisJS `off`). */
161
161
  off(event: string, listener: ListenerFn): void {
162
- const list = this.stringListeners.get(event)
162
+ const list = this.#stringListeners.get(event)
163
163
  if (!list) return
164
164
  const index = list.indexOf(listener)
165
165
  if (index !== -1) list.splice(index, 1)
166
- if (list.length === 0) this.stringListeners.delete(event)
166
+ if (list.length === 0) this.#stringListeners.delete(event)
167
167
  }
168
168
 
169
169
  /** Alias of {@link off} (AdonisJS `clearListener`). */
@@ -173,13 +173,13 @@ export class Emitter {
173
173
 
174
174
  /** Drop every listener of one event (AdonisJS `clearListeners`). */
175
175
  clearListeners(event: string): void {
176
- this.stringListeners.delete(event)
176
+ this.#stringListeners.delete(event)
177
177
  }
178
178
 
179
179
  /** Drop every listener of every event (AdonisJS `clearAllListeners`). */
180
180
  clearAllListeners(): void {
181
- this.stringListeners.clear()
182
- this.classListeners.clear()
181
+ this.#stringListeners.clear()
182
+ this.#classListeners.clear()
183
183
  }
184
184
 
185
185
  /**
@@ -191,17 +191,17 @@ export class Emitter {
191
191
  */
192
192
  get eventsListeners(): Map<EventConstructor | string, Listener[]> {
193
193
  const all = new Map<EventConstructor | string, Listener[]>()
194
- for (const [event, listeners] of this.classListeners) all.set(event, [...listeners])
195
- for (const [event, listeners] of this.stringListeners) all.set(event, [...listeners])
194
+ for (const [event, listeners] of this.#classListeners) all.set(event, [...listeners])
195
+ for (const [event, listeners] of this.#stringListeners) all.set(event, [...listeners])
196
196
  return all
197
197
  }
198
198
 
199
199
  /** How many listeners an event has, or all of them (AdonisJS `listenerCount`). */
200
200
  listenerCount(event?: string): number {
201
- if (event !== undefined) return this.stringListeners.get(event)?.length ?? 0
201
+ if (event !== undefined) return this.#stringListeners.get(event)?.length ?? 0
202
202
  let total = 0
203
- for (const list of this.stringListeners.values()) total += list.length
204
- for (const list of this.classListeners.values()) total += list.length
203
+ for (const list of this.#stringListeners.values()) total += list.length
204
+ for (const list of this.#classListeners.values()) total += list.length
205
205
  return total
206
206
  }
207
207
 
@@ -216,7 +216,7 @@ export class Emitter {
216
216
  */
217
217
  async #dispatchListeners(event: string, data: unknown, serial: boolean): Promise<void> {
218
218
  const listeners = [
219
- ...(this.stringListeners.get(event) ?? []),
219
+ ...(this.#stringListeners.get(event) ?? []),
220
220
  ...this.#wildcardListenersFor(event),
221
221
  ]
222
222
  if (listeners.length === 0) return
@@ -224,7 +224,7 @@ export class Emitter {
224
224
  try {
225
225
  await fn(data)
226
226
  } catch (err) {
227
- this.emitError(event, err)
227
+ this.#emitError(event, err)
228
228
  }
229
229
  }
230
230
  if (serial) {
@@ -281,17 +281,17 @@ export class Emitter {
281
281
  try {
282
282
  payload = JSON.stringify(this.#wrapForBus(data))
283
283
  } catch (err) {
284
- this.emitError(event, err)
284
+ this.#emitError(event, err)
285
285
  return
286
286
  }
287
- void this.bus.emit(event, payload).catch((err: unknown) => {
288
- this.emitError(event, err)
287
+ void this.#bus.emit(event, payload).catch((err: unknown) => {
288
+ this.#emitError(event, err)
289
289
  })
290
290
  }
291
291
 
292
292
  /** Emit an error event for listener failures. */
293
- private emitError(event: string, error: unknown): void {
294
- const errorListeners = this.stringListeners.get('emitter:error')
293
+ #emitError(event: string, error: unknown): void {
294
+ const errorListeners = this.#stringListeners.get('emitter:error')
295
295
  if (errorListeners && errorListeners.length > 0) {
296
296
  for (const fn of errorListeners) {
297
297
  try {
@@ -314,7 +314,7 @@ export class Emitter {
314
314
  // Faked by the class, as AdonisJS does — `fake([TaskDeclared])` catches
315
315
  // `new TaskDeclared(...).emit()`.
316
316
  if (this.#capture(EventClass, event)) return
317
- const listeners = this.classListeners.get(EventClass) ?? []
317
+ const listeners = this.#classListeners.get(EventClass) ?? []
318
318
  const name = (EventClass as { eventName?: string }).eventName ?? classToEventName(EventClass)
319
319
 
320
320
  // Per-listener error isolation — mirrors the string `emit()` contract.
@@ -326,22 +326,22 @@ export class Emitter {
326
326
  try {
327
327
  if (isListenerClass(listener)) {
328
328
  // Listener class — resolve via container for @inject() support
329
- const instance = this.resolver
330
- ? await this.resolver.make(listener as ListenerConstructor<T>)
329
+ const instance = this.#resolver
330
+ ? await this.#resolver.make(listener as ListenerConstructor<T>)
331
331
  : new (listener as ListenerConstructor<T>)()
332
332
  await instance.handle(event)
333
333
  } else {
334
334
  await (listener as ListenerFn<T>)(event)
335
335
  }
336
336
  } catch (err) {
337
- this.emitError(name, err)
337
+ this.#emitError(name, err)
338
338
  }
339
339
  }
340
340
 
341
341
  // Also push through EventBus — same correlation-envelope wrapping as
342
342
  // the string-event path so distributed tracing covers class events too.
343
343
  // Reached unconditionally: a listener failure above no longer skips it.
344
- await this.bus.emit(name, JSON.stringify(this.#wrapForBus(event)))
344
+ await this.#bus.emit(name, JSON.stringify(this.#wrapForBus(event)))
345
345
  }
346
346
 
347
347
  // ─── Faking (AdonisJS `fake` / `restore`) ─────────────────
@@ -436,7 +436,7 @@ export class Emitter {
436
436
  pattern: string,
437
437
  listener: (eventName: string, data: unknown) => void,
438
438
  ): Promise<number> {
439
- return this.bus.subscribe(pattern, (eventJson: string) => {
439
+ return this.#bus.subscribe(pattern, (eventJson: string) => {
440
440
  try {
441
441
  const event = JSON.parse(eventJson)
442
442
  // The bus delivers a Rust `Event` envelope whose `correlationId`
@@ -471,7 +471,7 @@ export class Emitter {
471
471
  */
472
472
  async offAny(listenerOrId: number | ((eventName: string, data: unknown) => void)): Promise<this> {
473
473
  if (typeof listenerOrId === 'number') {
474
- await this.bus.unsubscribe(listenerOrId)
474
+ await this.#bus.unsubscribe(listenerOrId)
475
475
  for (const ids of this.#anySubscriptions.values()) ids.delete(listenerOrId)
476
476
  return this
477
477
  }
@@ -479,13 +479,13 @@ export class Emitter {
479
479
  const ids = this.#anySubscriptions.get(listenerOrId)
480
480
  if (!ids) return this
481
481
  this.#anySubscriptions.delete(listenerOrId)
482
- await Promise.all([...ids].map((id) => this.bus.unsubscribe(id)))
482
+ await Promise.all([...ids].map((id) => this.#bus.unsubscribe(id)))
483
483
  return this
484
484
  }
485
485
 
486
486
  /** Check if a pattern matches an event name (wildcard matching via Rust). */
487
487
  matchesPattern(pattern: string, eventName: string): boolean {
488
- return this.bus.matchesWildcard(pattern, eventName)
488
+ return this.#bus.matchesWildcard(pattern, eventName)
489
489
  }
490
490
 
491
491
  // ─── Request / Reply ──────────────────────────────────────
@@ -499,7 +499,7 @@ export class Emitter {
499
499
  // Same correlation injection as `emit()` so a `setCorrelationId`
500
500
  // preceding a request actually reaches the responder via the
501
501
  // top-level `correlationId` field on the parsed payload.
502
- const result = await this.bus.request(name, JSON.stringify(this.#wrapForBus(data)), timeoutMs)
502
+ const result = await this.#bus.request(name, JSON.stringify(this.#wrapForBus(data)), timeoutMs)
503
503
  try {
504
504
  return JSON.parse(result) as T
505
505
  } catch {
@@ -519,7 +519,7 @@ export class Emitter {
519
519
  name: string,
520
520
  handler: (eventJson: string, reply: (response: string) => void) => void,
521
521
  ): void {
522
- this.bus.onRequest(name, handler)
522
+ this.#bus.onRequest(name, handler)
523
523
  }
524
524
 
525
525
  // ─── Correlation context ──────────────────────────────────
@@ -565,13 +565,13 @@ export class Emitter {
565
565
  // AdonisJS makes the argument optional: no argument asks whether ANY event
566
566
  // has a listener at all.
567
567
  if (event === undefined) return this.listenerCount() > 0
568
- if (typeof event === 'string') return (this.stringListeners.get(event)?.length ?? 0) > 0
569
- return (this.classListeners.get(event)?.length ?? 0) > 0
568
+ if (typeof event === 'string') return (this.#stringListeners.get(event)?.length ?? 0) > 0
569
+ return (this.#classListeners.get(event)?.length ?? 0) > 0
570
570
  }
571
571
 
572
572
  /** Get the number of Rust-side subscriptions. */
573
573
  async subscriptionCount(): Promise<number> {
574
- return this.bus.subscriptionCount()
574
+ return this.#bus.subscriptionCount()
575
575
  }
576
576
  }
577
577
 
@@ -593,7 +593,10 @@ export class BaseEvent {
593
593
  /**
594
594
  * Subclasses declare their own payload parameters; this signature only has
595
595
  * to be wide enough for `dispatch` to forward them (AdonisJS does the same).
596
+ * It widens `ConstructorParameters<typeof BaseEvent>` from `[]` to a rest
597
+ * parameter, which is what lets `dispatch` call `new this(...args)`.
596
598
  */
599
+ // biome-ignore lint/complexity/noUselessConstructor: it widens ConstructorParameters for dispatch — removing it fails to compile
597
600
  constructor(..._args: unknown[]) {}
598
601
 
599
602
  static eventName?: string
@@ -10,22 +10,28 @@
10
10
 
11
11
  import { loadNapi } from '../helpers/napi-loader.js'
12
12
 
13
- /** Rust-backed bus surface (mirrors the napi `#[napi] EventBus` exports). */
14
- export interface EventBus {
15
- emit(name: string, data: string): Promise<string>
16
- subscribe(pattern: string, callback: (eventJson: string) => void): number
17
- unsubscribe(subscriptionId: number): Promise<void>
18
- onRequest(
19
- name: string,
20
- callback: (eventJson: string, reply: (response: string) => void) => void,
21
- ): void
22
- request(name: string, data: string, timeoutMs?: number): Promise<string>
23
- matchesWildcard(pattern: string, eventName: string): boolean
24
- subscriptionCount(): Promise<number>
25
- }
13
+ /**
14
+ * Rust-backed bus surface.
15
+ *
16
+ * Re-exported from the generated declarations rather than restated here: the
17
+ * shape is derived from the `#[napi]` items themselves, so it cannot drift
18
+ * from the Rust. It did before — making `Bus::on_request` synchronous changed
19
+ * the Rust with nothing on this side to notice.
20
+ *
21
+ * Run `pnpm build:napi-types` after touching a `#[napi]` signature.
22
+ */
23
+ import type { EventBus as NativeEventBus } from '../native/generated.js'
26
24
 
25
+ /**
26
+ * What the `events` binary exports.
27
+ *
28
+ * The instance shape comes from the generated declarations — derived from the
29
+ * `#[napi]` items, so it cannot drift from the Rust without the generated file
30
+ * changing. Only the constructor is named here, because the binary is a module
31
+ * object rather than a class the loader can see.
32
+ */
27
33
  interface EventsNative {
28
- EventBus: new (requestHandlerTimeoutMs?: number) => EventBus
34
+ EventBus: new (requestHandlerTimeoutMs?: number) => NativeEventBus
29
35
  }
30
36
 
31
37
  const native = loadNapi<EventsNative>({
@@ -35,3 +41,4 @@ const native = loadNapi<EventsNative>({
35
41
  })
36
42
 
37
43
  export const EventBus = native.EventBus
44
+ export type EventBus = NativeEventBus
@@ -14,7 +14,7 @@ import type { MatchedParams } from '../router/Router.js'
14
14
  import type { CookieSigner } from '../security/CookieSigner.js'
15
15
  import type { SignedUrl } from '../security/SignedUrl.js'
16
16
  import type { Session } from '../session/Session.js'
17
- import type { Authenticators } from '../types/authenticators.js'
17
+ import type { Authenticators } from '../types/index.js'
18
18
  import { Macroable } from '../utils/Macroable.js'
19
19
  import type { RouteUrlResolver } from './RedirectBuilder.js'
20
20
  import { RedirectBuilder } from './RedirectBuilder.js'
@@ -1004,19 +1004,6 @@ function isIpLiteral(host: string): boolean {
1004
1004
  return /^(\d{1,3}\.){3}\d{1,3}$/.test(host)
1005
1005
  }
1006
1006
 
1007
- /**
1008
- * Decode a percent-encoded segment without throwing on malformed input.
1009
- * Browsers happily send `q=%E0%A4%A` (truncated UTF-8 sequences) — refusing
1010
- * the whole request would let any client knock the route handler offline.
1011
- */
1012
- function safeDecode(value: string): string {
1013
- try {
1014
- return decodeURIComponent(value)
1015
- } catch {
1016
- return value
1017
- }
1018
- }
1019
-
1020
1007
  interface AcceptEntry {
1021
1008
  value: string
1022
1009
  q: number
package/src/index.ts CHANGED
@@ -8,6 +8,7 @@ import 'reflect-metadata'
8
8
 
9
9
  // ─── Core ───────────────────────────────────────────────────
10
10
 
11
+ export type { AppEnvironment, ApplicationMode, ApplicationState } from './Application.js'
11
12
  export { Application } from './Application.js'
12
13
  export { ConfigStore, configProvider, defineModuleConfig, env } from './ConfigLoader.js'
13
14
  export { Container } from './container/Container.js'
@@ -242,7 +243,6 @@ export type {
242
243
  FlagMetaData,
243
244
  } from './console/types.js'
244
245
  export type {
245
- AppEnvironment,
246
246
  HyperServerLike,
247
247
  IgnitorConfig,
248
248
  ReamrcConfig,
@@ -371,4 +371,4 @@ export {
371
371
  StatsTracker,
372
372
  } from './scheduler/index.js'
373
373
 
374
- export const VERSION = '0.2.3'
374
+ export const VERSION = '0.2.5'
@@ -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