@statorjs/stator 2.10.0-next.1 → 2.11.0-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +7 -2
- package/src/cli/commands/dev.ts +3 -0
- package/src/cli/commands/start.ts +3 -0
- package/src/compiler/lower.ts +41 -0
- package/src/compiler/virtual-code.ts +1 -1
- package/src/components/images.ts +260 -0
- package/src/components/index.ts +10 -0
- package/src/config.ts +64 -0
- package/src/index.ts +7 -0
- package/src/server/api-route.ts +5 -2
- package/src/server/config-compat.ts +4 -0
- package/src/server/context.ts +13 -4
- package/src/server/create-app.ts +32 -0
- package/src/server/dev-native.ts +3 -0
- package/src/server/dev-vite.ts +3 -0
- package/src/server/dev.ts +14 -0
- package/src/server/http.ts +299 -91
- package/src/server/images.ts +426 -0
- package/src/server/index.ts +7 -0
- package/src/server/query-route.ts +68 -3
- package/src/server/render-context.ts +9 -0
- package/src/server/render.ts +3 -0
- package/src/server/route-discovery.ts +36 -5
- package/src/server/routing.ts +8 -0
- package/src/server/session.ts +58 -11
- package/src/template/html-attributes.ts +10 -0
package/src/server/routing.ts
CHANGED
|
@@ -198,6 +198,7 @@ export function defineApiRoute<TReads extends ReadonlyArray<AnyMachineDef>>(
|
|
|
198
198
|
__isStatorQueryRoute: true,
|
|
199
199
|
reads: config.reads ? [...config.reads] : [],
|
|
200
200
|
handler: config.handler,
|
|
201
|
+
revision: config.revision,
|
|
201
202
|
}
|
|
202
203
|
}
|
|
203
204
|
return {
|
|
@@ -248,6 +249,7 @@ export interface QueryRouteDefinition {
|
|
|
248
249
|
request: RouteRequest,
|
|
249
250
|
helpers: QueryRouteHelpers,
|
|
250
251
|
) => Promise<QueryRouteResult> | QueryRouteResult
|
|
252
|
+
revision?: () => string | number | Promise<string | number>
|
|
251
253
|
}
|
|
252
254
|
|
|
253
255
|
export interface DefineQueryRouteConfig<TReads extends ReadonlyArray<AnyMachineDef> = readonly []> {
|
|
@@ -259,6 +261,12 @@ export interface DefineQueryRouteConfig<TReads extends ReadonlyArray<AnyMachineD
|
|
|
259
261
|
request: RouteRequest,
|
|
260
262
|
helpers: QueryRouteHelpers<TReads>,
|
|
261
263
|
) => Promise<QueryRouteResult> | QueryRouteResult
|
|
264
|
+
/** The revision ledger: a CHEAP fingerprint of everything the body depends
|
|
265
|
+
* on (e.g. `() => String(maxUpdatedAt())` — one indexed SELECT). When
|
|
266
|
+
* present, the ETag derives from it and a matching `If-None-Match`
|
|
267
|
+
* answers 304 WITHOUT invoking the handler — a polling crawler costs the
|
|
268
|
+
* fingerprint, not the render. Must change whenever the body could. */
|
|
269
|
+
revision?: () => string | number | Promise<string | number>
|
|
262
270
|
}
|
|
263
271
|
|
|
264
272
|
export function isStatorQueryRoute(v: unknown): v is QueryRouteDefinition {
|
package/src/server/session.ts
CHANGED
|
@@ -23,9 +23,18 @@ export interface SessionState {
|
|
|
23
23
|
claimsDirty: boolean
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
/** Layer-3 provability tracking: did this request USE the session plane
|
|
27
|
+
* (establish/dispatch/session-read) or READ claims? Either makes the
|
|
28
|
+
* response unprovable as anonymous-identical, so no derived Cache-Control. */
|
|
29
|
+
export interface SessionUse {
|
|
30
|
+
used: boolean
|
|
31
|
+
claimsRead: boolean
|
|
32
|
+
}
|
|
33
|
+
|
|
26
34
|
declare module 'hono' {
|
|
27
35
|
interface ContextVariableMap {
|
|
28
36
|
statorSession: SessionState
|
|
37
|
+
statorSessionUse: SessionUse
|
|
29
38
|
}
|
|
30
39
|
}
|
|
31
40
|
|
|
@@ -34,6 +43,40 @@ export function getSessionState(c: Context): SessionState | undefined {
|
|
|
34
43
|
return c.get('statorSession')
|
|
35
44
|
}
|
|
36
45
|
|
|
46
|
+
/** The request's session-use flags (created on first access). */
|
|
47
|
+
export function sessionUse(c: Context): SessionUse {
|
|
48
|
+
let use = c.get('statorSessionUse')
|
|
49
|
+
if (!use) {
|
|
50
|
+
use = { used: false, claimsRead: false }
|
|
51
|
+
c.set('statorSessionUse', use)
|
|
52
|
+
}
|
|
53
|
+
return use
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** The session id this request WOULD have — established state or the arriving
|
|
57
|
+
* cookie — without creating anything. Null for a first-contact visitor. */
|
|
58
|
+
export function peekSessionId(c: Context): string | null {
|
|
59
|
+
return getSessionState(c)?.sid ?? getCookie(c, SESSION_COOKIE) ?? null
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Resume an EXISTING session from the arriving cookie (populate the shared
|
|
63
|
+
* state, no cookie write) — or return null for first contact. Resuming is
|
|
64
|
+
* not establishment: laziness governs CREATION only. */
|
|
65
|
+
export function resumeSession(c: Context): SessionState | null {
|
|
66
|
+
const established = getSessionState(c)
|
|
67
|
+
if (established) return established
|
|
68
|
+
const cookieSid = getCookie(c, SESSION_COOKIE)
|
|
69
|
+
if (cookieSid === undefined) return null
|
|
70
|
+
const state: SessionState = {
|
|
71
|
+
sid: cookieSid,
|
|
72
|
+
isNew: false,
|
|
73
|
+
claims: undefined,
|
|
74
|
+
claimsDirty: false,
|
|
75
|
+
}
|
|
76
|
+
c.set('statorSession', state)
|
|
77
|
+
return state
|
|
78
|
+
}
|
|
79
|
+
|
|
37
80
|
/** Secure cookie flag — set when running behind HTTPS. Enabled by
|
|
38
81
|
* NODE_ENV=production; can be overridden via STATOR_SECURE_COOKIE. */
|
|
39
82
|
function shouldUseSecureCookie(): boolean {
|
|
@@ -65,22 +108,26 @@ export function setSessionCookie(c: Context, sessionId: string): void {
|
|
|
65
108
|
})
|
|
66
109
|
}
|
|
67
110
|
|
|
111
|
+
/**
|
|
112
|
+
* ESTABLISH the session — the lazy-creation trigger (dispatches, session-
|
|
113
|
+
* machine reads, SSE connects, explicit session ops call this; anonymous
|
|
114
|
+
* page/data GETs never do). Idempotent per request: the first caller
|
|
115
|
+
* establishes, everyone after reads the same state. A first-contact visitor
|
|
116
|
+
* gets a fresh sid + Set-Cookie HERE and nowhere earlier — the whole
|
|
117
|
+
* cacheable-read-path layer 1.
|
|
118
|
+
*/
|
|
68
119
|
export function getOrCreateSessionId(c: Context): {
|
|
69
120
|
sessionId: string
|
|
70
121
|
isNew: boolean
|
|
71
122
|
} {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (established) return { sessionId: established.sid, isNew: established.isNew }
|
|
76
|
-
|
|
77
|
-
const cookieSid = getCookie(c, SESSION_COOKIE)
|
|
78
|
-
const sessionId = cookieSid ?? randomUUID()
|
|
79
|
-
const isNew = cookieSid === undefined
|
|
80
|
-
if (isNew) setSessionCookie(c, sessionId)
|
|
123
|
+
sessionUse(c).used = true
|
|
124
|
+
const resumed = resumeSession(c)
|
|
125
|
+
if (resumed) return { sessionId: resumed.sid, isNew: resumed.isNew }
|
|
81
126
|
|
|
82
|
-
|
|
83
|
-
|
|
127
|
+
const sessionId = randomUUID()
|
|
128
|
+
setSessionCookie(c, sessionId)
|
|
129
|
+
c.set('statorSession', { sid: sessionId, isNew: true, claims: undefined, claimsDirty: false })
|
|
130
|
+
return { sessionId, isNew: true }
|
|
84
131
|
}
|
|
85
132
|
|
|
86
133
|
/**
|
|
@@ -94,6 +94,11 @@ export interface ElementSpecificAttributes {
|
|
|
94
94
|
autofocus?: boolean
|
|
95
95
|
accept?: string
|
|
96
96
|
multiple?: boolean
|
|
97
|
+
/** Associates this element with a `<form>` elsewhere in the document by
|
|
98
|
+
* id — the standard way to submit an element that can't live inside
|
|
99
|
+
* the form's own DOM subtree (e.g. a per-row control alongside a
|
|
100
|
+
* page-wide bulk-action form). `button` already carries this. */
|
|
101
|
+
form?: string
|
|
97
102
|
}
|
|
98
103
|
label: { for?: string }
|
|
99
104
|
form: {
|
|
@@ -101,6 +106,11 @@ export interface ElementSpecificAttributes {
|
|
|
101
106
|
method?: 'get' | 'post' | 'GET' | 'POST' | 'dialog'
|
|
102
107
|
enctype?: 'application/x-www-form-urlencoded' | 'multipart/form-data' | 'text/plain'
|
|
103
108
|
novalidate?: boolean
|
|
109
|
+
/** Native inline event-handler attribute — a literal JS source string
|
|
110
|
+
* (`"return confirm(...)"`), distinct from the `on:submit={...}`
|
|
111
|
+
* Stator directive. Useful for a plain, no-hydration `confirm()`
|
|
112
|
+
* guard that doesn't otherwise need an island. */
|
|
113
|
+
onsubmit?: string
|
|
104
114
|
}
|
|
105
115
|
select: {
|
|
106
116
|
name?: string
|