@statorjs/stator 1.6.1 → 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@statorjs/stator",
3
- "version": "1.6.1",
3
+ "version": "1.7.1",
4
4
  "description": "Server-canonical web framework: business logic in composable state machines, UI as a thin renderer binding machine outputs to DOM positions. Ships TypeScript source (Vite/tsx-native by design).",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -88,7 +88,7 @@
88
88
  "pino-pretty": "^13.1.3",
89
89
  "vite": "^6.0.0",
90
90
  "vitest": "^2.1.0",
91
- "@statorjs/stator": "1.6.1"
91
+ "@statorjs/stator": "1.7.1"
92
92
  },
93
93
  "scripts": {
94
94
  "typecheck": "tsc --noEmit",
@@ -6,7 +6,12 @@ import { scopedLogger } from './logger.ts'
6
6
  import type { MachineStore } from './machine-store.ts'
7
7
  import type { DiscoveredRoute } from './route-discovery.ts'
8
8
  import { buildRouteRequest } from './route-request.ts'
9
- import type { QueryRouteDefinition, QueryRouteResult, RouteContext } from './routing.ts'
9
+ import type {
10
+ QueryRouteDefinition,
11
+ QueryRouteHelpers,
12
+ QueryRouteResult,
13
+ RouteContext,
14
+ } from './routing.ts'
10
15
  import { getOrCreateSessionId } from './session.ts'
11
16
  import { withSessionLock } from './session-lock.ts'
12
17
  import { SessionRuntime } from './session-runtime.ts'
@@ -74,7 +79,11 @@ export async function runQueryRoute(
74
79
 
75
80
  let result: QueryRouteResult
76
81
  try {
77
- result = await route.handler(request, { machines })
82
+ // The map is built dynamically by machine name; the typed ReadsMap
83
+ // guarantee is for the user's handler, recovered at their call site.
84
+ result = await route.handler(request, {
85
+ machines: machines as QueryRouteHelpers['machines'],
86
+ })
78
87
  } catch (err) {
79
88
  queryLog.error({ err: String(err), path: c.req.path }, 'data route handler threw')
80
89
  return c.text('Internal Server Error', 500)
@@ -1,6 +1,6 @@
1
1
  import type { HtmlFragment } from '../template/types.ts'
2
2
  import type { WireEnvelope } from '../wire/index.ts'
3
- import type { AnyMachineDef, EventOf } from './define-machine.ts'
3
+ import type { AnyMachineDef, EventOf, ReadsMap } from './define-machine.ts'
4
4
 
5
5
  /** Machine context passed to a route's render function. Keyed by machine name. */
6
6
  export type RouteContext = Record<string, unknown>
@@ -170,7 +170,7 @@ export interface DefineApiRouteConfig<TReads extends ReadonlyArray<AnyMachineDef
170
170
  /** `method: 'GET'` discriminates: with it, the handler is a read-only data
171
171
  * route typed with `{ machines }`; without it, a command route typed with
172
172
  * `{ dispatch, rotateSession }` — exactly as before. */
173
- export function defineApiRoute<TReads extends ReadonlyArray<AnyMachineDef>>(
173
+ export function defineApiRoute<TReads extends ReadonlyArray<AnyMachineDef> = readonly []>(
174
174
  config: DefineQueryRouteConfig<TReads>,
175
175
  ): QueryRouteDefinition
176
176
  export function defineApiRoute<TReads extends ReadonlyArray<AnyMachineDef>>(
@@ -209,10 +209,15 @@ export function isStatorApiRoute(v: unknown): v is ApiRouteDefinition {
209
209
  * there is no `dispatch` here, and that structural absence is what makes
210
210
  * handler reads safe — a handler that cannot dispatch has nothing to
211
211
  * interleave with effect completions or other sessions' commits. */
212
- export interface QueryRouteHelpers {
212
+ export interface QueryRouteHelpers<
213
+ TReads extends ReadonlyArray<AnyMachineDef> = ReadonlyArray<AnyMachineDef>,
214
+ > {
213
215
  /** Read proxies keyed by machine name — the same shape a page's render
214
- * context uses (selector + context reads; no send). */
215
- machines: RouteContext
216
+ * context uses (selector + context reads; no send). Typed off the route's
217
+ * `reads:` tuple (`ReadsMap`), so `machines.SitesMachine` is a real proxy
218
+ * and a mistyped name is a compile error rather than `unknown`. The default
219
+ * keeps the erased runtime definition loose. */
220
+ machines: ReadsMap<TReads>
216
221
  }
217
222
 
218
223
  /** What a data GET handler may return: a raw `Response` (passed through
@@ -231,14 +236,14 @@ export interface QueryRouteDefinition {
231
236
  ) => Promise<QueryRouteResult> | QueryRouteResult
232
237
  }
233
238
 
234
- export interface DefineQueryRouteConfig<TReads extends ReadonlyArray<AnyMachineDef>> {
239
+ export interface DefineQueryRouteConfig<TReads extends ReadonlyArray<AnyMachineDef> = readonly []> {
235
240
  /** The capability discriminant: `'GET'` declares a read-only data route.
236
241
  * Discovery cross-checks it against the export name. */
237
242
  method: 'GET'
238
243
  reads?: TReads
239
244
  handler: (
240
245
  request: RouteRequest,
241
- helpers: QueryRouteHelpers,
246
+ helpers: QueryRouteHelpers<TReads>,
242
247
  ) => Promise<QueryRouteResult> | QueryRouteResult
243
248
  }
244
249