@weft-js/start 0.1.1 → 0.1.3

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 (214) hide show
  1. package/README.md +1278 -12
  2. package/crate/Cargo.lock +1347 -0
  3. package/crate/Cargo.toml +72 -0
  4. package/crate/README.md +322 -0
  5. package/crate/rust-toolchain.toml +3 -0
  6. package/crate/src/build/emit.rs +103 -0
  7. package/crate/src/build.rs +367 -0
  8. package/crate/src/ctx.rs +186 -0
  9. package/crate/src/envelope.rs +163 -0
  10. package/crate/src/functions.rs +173 -0
  11. package/crate/src/lib.rs +118 -0
  12. package/crate/src/native.rs +212 -0
  13. package/crate/src/otel.rs +62 -0
  14. package/crate/src/server.rs +203 -0
  15. package/package.json +46 -37
  16. package/src/app.mjs +38 -8
  17. package/src/asset-receipt.d.mts +13 -0
  18. package/src/asset-receipt.mjs +61 -0
  19. package/src/auth/better-auth.ts +215 -17
  20. package/src/auth/drizzle.ts +1 -1
  21. package/src/auth.ts +21 -6
  22. package/src/boot.ts +19 -8
  23. package/src/bus/postgres.ts +32 -19
  24. package/src/bus.ts +7 -4
  25. package/src/{capture.mjs → capture.ts} +34 -1
  26. package/src/changes-sql.d.mts +8 -2
  27. package/src/changes-sql.mjs +26 -10
  28. package/src/changes.ts +165 -66
  29. package/src/channel/connections.ts +216 -0
  30. package/src/channel/socket.ts +396 -0
  31. package/src/channel-fake.ts +19 -4
  32. package/src/channel.ts +120 -492
  33. package/src/client.tsx +9 -2
  34. package/src/contract/describe.mjs +231 -0
  35. package/src/contract.mjs +144 -157
  36. package/src/data/cache.ts +354 -0
  37. package/src/data/definitions.ts +94 -0
  38. package/src/data/draft.ts +104 -0
  39. package/src/data/foundation.ts +95 -0
  40. package/src/data/live-api.ts +22 -0
  41. package/src/data/mutation.ts +138 -0
  42. package/src/data/primitives.ts +135 -0
  43. package/src/data/query.ts +106 -0
  44. package/src/data/transport.ts +121 -0
  45. package/src/data/types.ts +303 -0
  46. package/src/data.ts +69 -894
  47. package/src/db/bun-sqlite.d.ts +4 -1
  48. package/src/db/changes-mysql.ts +61 -21
  49. package/src/db/changes-postgres.ts +197 -118
  50. package/src/db/changes-sqlite.ts +131 -64
  51. package/src/db/core.ts +38 -13
  52. package/src/db/index.ts +20 -4
  53. package/src/db/mysql.ts +22 -6
  54. package/src/db/postgres.ts +49 -13
  55. package/src/db/sqlite.ts +53 -27
  56. package/src/derive.d.mts +4 -1
  57. package/src/derive.mjs +136 -39
  58. package/src/dev-runtime.mjs +8 -4
  59. package/src/exec/errors.ts +57 -0
  60. package/src/exec/landing.ts +66 -0
  61. package/src/exec/scope.ts +174 -0
  62. package/src/exec/touch.ts +80 -0
  63. package/src/exec/validation.ts +176 -0
  64. package/src/exec.ts +191 -341
  65. package/src/extension.ts +8 -2
  66. package/src/first-paint.ts +26 -9
  67. package/src/form/value.ts +15 -0
  68. package/src/form.tsx +132 -43
  69. package/src/hub/channels.ts +102 -0
  70. package/src/hub/connections.ts +147 -0
  71. package/src/hub/delivery.ts +62 -0
  72. package/src/hub/feed.ts +168 -0
  73. package/src/hub/presence.ts +64 -0
  74. package/src/hub/route.ts +158 -0
  75. package/src/hub/subscriptions.ts +212 -0
  76. package/src/hub/topic.ts +98 -0
  77. package/src/hub/tracked.ts +12 -0
  78. package/src/hub/types.ts +190 -0
  79. package/src/hub/view.ts +147 -0
  80. package/src/hub.ts +132 -758
  81. package/src/index.ts +84 -10
  82. package/src/inline.mjs +356 -0
  83. package/src/inspect/compare.mjs +162 -0
  84. package/src/inspect/routes.mjs +154 -0
  85. package/src/inspect/samples.mjs +59 -0
  86. package/src/inspect/url.mjs +2 -0
  87. package/src/inspect.mjs +146 -187
  88. package/src/link/election.ts +178 -0
  89. package/src/link/lines.ts +27 -0
  90. package/src/link.ts +90 -166
  91. package/src/list-drizzle.ts +104 -18
  92. package/src/list.ts +127 -36
  93. package/src/listen.ts +32 -6
  94. package/src/live/api.ts +18 -0
  95. package/src/live/backoff.ts +6 -0
  96. package/src/live/frames.ts +42 -0
  97. package/src/live/read.ts +77 -0
  98. package/src/live/sub-types.ts +24 -0
  99. package/src/live/transport.ts +86 -0
  100. package/src/live/types.ts +52 -0
  101. package/src/live-fake.ts +20 -5
  102. package/src/live-target.mjs +112 -37
  103. package/src/live.ts +116 -225
  104. package/src/media.ts +8 -1
  105. package/src/migrations.mjs +14 -6
  106. package/src/mux.ts +161 -64
  107. package/src/og/DefaultCard.tsx +40 -9
  108. package/src/og/fonts.mjs +16 -8
  109. package/src/og/images.mjs +43 -22
  110. package/src/og/index.mjs +119 -43
  111. package/src/og/native.mjs +11 -4
  112. package/src/range.mjs +15 -3
  113. package/src/router.ts +56 -6
  114. package/src/routes.mjs +73 -22
  115. package/src/run/admission.ts +80 -0
  116. package/src/run/boot.tsx +115 -0
  117. package/src/run/cache-presentation.ts +67 -0
  118. package/src/run/cache.ts +254 -0
  119. package/src/run/helpers.ts +197 -0
  120. package/src/run/listeners.ts +155 -0
  121. package/src/run/navigation.ts +382 -0
  122. package/src/run/page-skeleton.tsx +35 -0
  123. package/src/run/prefetch.ts +128 -0
  124. package/src/run/retain-view.ts +18 -0
  125. package/src/run/shape-preparation.ts +97 -0
  126. package/src/run/shape-styles.ts +72 -0
  127. package/src/run/source-context/pin.ts +124 -0
  128. package/src/run/source-context.ts +311 -0
  129. package/src/run.tsx +50 -178
  130. package/src/rust/common.ts +163 -0
  131. package/src/rust/native.ts +282 -0
  132. package/src/rust.ts +103 -229
  133. package/src/server/card.tsx +65 -0
  134. package/src/server/compression.ts +115 -0
  135. package/src/server/core.ts +70 -0
  136. package/src/server/dispatch.ts +70 -0
  137. package/src/server/form.ts +85 -0
  138. package/src/server/functions.ts +129 -0
  139. package/src/server/handler.ts +39 -0
  140. package/src/server/head.ts +96 -0
  141. package/src/server/media.ts +60 -0
  142. package/src/server/navigation-cache.ts +80 -0
  143. package/src/server/navigation.tsx +85 -0
  144. package/src/server/page-cold.ts +95 -0
  145. package/src/server/page-stream.tsx +181 -0
  146. package/src/server/page-types.ts +51 -0
  147. package/src/server/page-warm.ts +130 -0
  148. package/src/server/page.ts +69 -0
  149. package/src/server/payload.ts +89 -0
  150. package/src/server/prerender.ts +60 -0
  151. package/src/server/preview.tsx +67 -0
  152. package/src/server/render.ts +167 -0
  153. package/src/server/request.ts +98 -0
  154. package/src/server/resolver.ts +228 -0
  155. package/src/server/routes.ts +150 -0
  156. package/src/server/services.ts +69 -0
  157. package/src/server/text.ts +29 -0
  158. package/src/server/types.ts +160 -0
  159. package/src/server.tsx +82 -971
  160. package/src/shape.mjs +30 -6
  161. package/src/share/display.ts +50 -0
  162. package/src/share/network.ts +119 -0
  163. package/src/share/receive.ts +70 -0
  164. package/src/share/state.ts +30 -0
  165. package/src/share/tab-queue.ts +28 -0
  166. package/src/share-server.ts +128 -43
  167. package/src/share.ts +103 -209
  168. package/src/sheets.mjs +30 -18
  169. package/src/sidecar/build.mjs +170 -0
  170. package/src/sidecar/cargo.mjs +164 -0
  171. package/src/sidecar/common.mjs +17 -0
  172. package/src/sidecar/dev-build.mjs +91 -0
  173. package/src/sidecar/dev-watch.mjs +88 -0
  174. package/src/sidecar/manifest.mjs +19 -0
  175. package/src/sidecar/process.mjs +58 -0
  176. package/src/sidecar/refresh.mjs +19 -0
  177. package/src/sidecar.mjs +75 -416
  178. package/src/socket/check.ts +50 -0
  179. package/src/socket/deliver.ts +16 -0
  180. package/src/socket/publish.ts +56 -0
  181. package/src/socket/rooms.ts +41 -0
  182. package/src/socket/route.ts +79 -0
  183. package/src/socket/shared.ts +26 -0
  184. package/src/socket/types.ts +48 -0
  185. package/src/socket.ts +205 -194
  186. package/src/stream.ts +10 -1
  187. package/src/verify/app.ts +39 -0
  188. package/src/verify/cleanup.ts +27 -0
  189. package/src/verify/database.ts +11 -0
  190. package/src/verify/live.ts +63 -0
  191. package/src/verify/notify.ts +35 -0
  192. package/src/verify/skip.ts +19 -0
  193. package/src/verify/support.ts +149 -0
  194. package/src/verify/timing.ts +15 -0
  195. package/src/verify/trigger.ts +14 -0
  196. package/src/verify.ts +77 -176
  197. package/src/view.tsx +96 -11
  198. package/src/vite/asset-bytes.mjs +95 -0
  199. package/src/vite/assets.mjs +117 -0
  200. package/src/vite/config-hooks.mjs +166 -0
  201. package/src/vite/dev-hooks.mjs +103 -0
  202. package/src/vite/dev.mjs +102 -0
  203. package/src/vite/entries.mjs +145 -0
  204. package/src/vite/module-hooks.mjs +288 -0
  205. package/src/vite/route-modules.mjs +289 -0
  206. package/src/vite/server-functions.mjs +222 -0
  207. package/src/vite/shape-entry-css.mjs +187 -0
  208. package/src/vite/shape-override.mjs +226 -0
  209. package/src/vite/shared.mjs +51 -0
  210. package/src/vite/sheets.mjs +92 -0
  211. package/src/vite/state.mjs +89 -0
  212. package/src/vite/syntax.mjs +139 -0
  213. package/src/vite.mjs +152 -853
  214. package/src/capture.d.mts +0 -1
@@ -0,0 +1,150 @@
1
+ import type { AsyncLocalStorage } from 'node:async_hooks'
2
+ import { BaseRootRoute, BaseRoute, isNotFound, type AnyRoute } from '@tanstack/router-core'
3
+ import { isRedirect, type Redirect } from '../data'
4
+ import * as exec from '../exec'
5
+ import type { Scope } from '../exec'
6
+ import type { Manifest, RouteModule } from './types'
7
+
8
+ /**
9
+ * What each router-core loader gets as `context`: the request scope, where it leaves tags and redirects (router-core
10
+ * copies the context object, so those go in shared containers), and the matched page's catch-all name.
11
+ */
12
+ export interface LoadContext {
13
+ request: Request
14
+ scope: Scope
15
+ tags: Map<string, Set<string>>
16
+ out: { redirect?: Redirect; chain?: Promise<unknown> }
17
+ splat?: string | undefined
18
+ }
19
+
20
+ interface TreeNode {
21
+ route: AnyRoute
22
+ children: AnyRoute[]
23
+ dir: string
24
+ }
25
+
26
+ const makeLoader =
27
+ (modules: Record<string, RouteModule>, als: AsyncLocalStorage<Scope>) =>
28
+ (file: string, splat?: string) =>
29
+ async ({
30
+ params,
31
+ context,
32
+ abortController,
33
+ }: {
34
+ params: Record<string, unknown>
35
+ context: LoadContext
36
+ abortController: AbortController
37
+ }) => {
38
+ const load = modules[file]?.load
39
+ if (!load) return undefined
40
+ // Parent first (router-core starts them together): a layout's `load` is a guard, so a child never runs for a
41
+ // visitor its parent redirects (research 28 §3).
42
+ const out = context.out
43
+ const before = out.chain
44
+ let done!: () => void
45
+ out.chain = new Promise<void>((ok) => {
46
+ done = ok
47
+ })
48
+ await before
49
+ if (out.redirect) return (done(), undefined)
50
+ // Each loader runs in its own frame of the request scope, which records the tags of the queries it read.
51
+ const f = exec.frame(context.scope)
52
+ context.tags.set(file, f.tags!)
53
+ try {
54
+ const ctx = await f.ctx()
55
+ const v = await als.run(f, () =>
56
+ load({
57
+ params: cleanParams(params, splat ?? context.splat),
58
+ request: context.request,
59
+ signal: abortController.signal,
60
+ ctx,
61
+ }),
62
+ )
63
+ // oxlint-disable-next-line typescript/only-throw-error -- router-core's own not-found signal, passed on as it came
64
+ if (isNotFound(v)) throw v
65
+ return v
66
+ } catch (e) {
67
+ if (!isRedirect(e)) throw e
68
+ out.redirect ??= e
69
+ return undefined
70
+ } finally {
71
+ done()
72
+ }
73
+ }
74
+
75
+ /** The router-core tree for the manifest: layouts are path prefixes, routes hang under their innermost layout. */
76
+ export function routeTree(
77
+ manifest: Manifest,
78
+ modules: Record<string, RouteModule>,
79
+ als: AsyncLocalStorage<Scope>,
80
+ ) {
81
+ const loader = makeLoader(modules, als)
82
+ const rootLayout = manifest.layouts.find((l) => l.dir === '')
83
+ const root = new BaseRootRoute({
84
+ ...(rootLayout && { loader: loader(rootLayout.file), weft: rootLayout }),
85
+ } as never)
86
+ const top: TreeNode = { route: root, children: [], dir: '' }
87
+ const nodes = new Map<string, TreeNode>()
88
+ const at = (id: string | undefined) => (id ? (nodes.get(id) ?? top) : top)
89
+ for (const l of manifest.layouts) {
90
+ if (l.dir === '') {
91
+ nodes.set(l.id, top)
92
+ continue
93
+ }
94
+ const parent = at(l.parent)
95
+ const route = new BaseRoute({
96
+ getParentRoute: () => parent.route,
97
+ path: toPath(l.dir, parent.dir),
98
+ loader: loader(l.file),
99
+ weft: l,
100
+ } as never)
101
+ parent.children.push(route)
102
+ nodes.set(l.id, { route, children: [], dir: l.dir })
103
+ }
104
+ const splats = new Set(manifest.routes.filter((r) => r.splat).map((r) => r.pattern))
105
+ for (const r of manifest.routes) {
106
+ if (r.kind !== 'page') continue
107
+ const parent = at(r.layouts.at(-1))
108
+ const rel = r.pattern
109
+ .slice(1)
110
+ .split('/')
111
+ .slice(parent.dir ? parent.dir.split('/').length : 0)
112
+ .join('/')
113
+ // A splat beside it (`docs/[...slug]` next to `docs/index`) also matches `/docs`, and router-core ranks the
114
+ // splat first unless the page is its index route, written with the slash.
115
+ const index = rel && splats.has(`${r.pattern}/$`) ? `${rel}/` : rel
116
+ parent.children.push(
117
+ new BaseRoute({
118
+ getParentRoute: () => parent.route,
119
+ path: index || '/',
120
+ loader: loader(r.file, r.splat),
121
+ weft: r,
122
+ } as never),
123
+ )
124
+ }
125
+ // Innermost layouts first, so each route's children are attached before its parent takes it.
126
+ for (const n of [...new Set(nodes.values())]
127
+ .filter((x) => x !== top)
128
+ .sort((a, b) => b.dir.length - a.dir.length))
129
+ if (n.children.length) n.route.addChildren(n.children)
130
+ return root.addChildren(top.children)
131
+ }
132
+
133
+ /** A layout dir relative to its parent layout's, as a router-core path: `users/[id]` → `users/$id`. */
134
+ function toPath(dir: string, parentDir: string) {
135
+ const rel = dir.split('/').slice(parentDir ? parentDir.split('/').length : 0)
136
+ return rel
137
+ .map((s) => s.replace(/^\[\.\.\.(\w+)\]$/, '$').replace(/^\[(\w+)\]$/, '$$$1'))
138
+ .join('/')
139
+ }
140
+
141
+ /** router-core's params without its splat aliases; a splat is under its file name (`[...rest]` → `rest`). */
142
+ export function cleanParams(
143
+ p: Record<string, unknown> = {},
144
+ splat?: string,
145
+ ): Record<string, string> {
146
+ const out: Record<string, string> = {}
147
+ for (const k in p) if (k !== '*' && k !== '_splat') out[k] = String(p[k])
148
+ if (splat && p._splat !== undefined) out[splat] = p._splat as string
149
+ return out
150
+ }
@@ -0,0 +1,69 @@
1
+ import { runtime } from '../data'
2
+ import * as exec from '../exec'
3
+ import type { MediaServer } from '../exec'
4
+ import { configure } from '../hub'
5
+ import type { ServerExtension } from '../extension'
6
+ import type { HandlerOptions } from './types'
7
+
8
+ /** Handler-local extensions, media provider, and boot state. */
9
+ export function makeServices(options: HandlerOptions) {
10
+ let provider: Promise<MediaServer> | undefined
11
+ const mediaServer = () => (provider ??= options.media?.())
12
+ const scopeOptions = {
13
+ context: options.context,
14
+ store: options.store,
15
+ dev: options.dev,
16
+ onDiagnostic: options.onDiagnostic,
17
+ media: mediaServer,
18
+ extensions: undefined as ServerExtension[] | undefined,
19
+ }
20
+ let checked = !options.dev
21
+ const authBase = (options.auth?.basePath ?? '/api/auth').replace(/\/$/, '')
22
+ const extensions = new Map<string, ServerExtension>()
23
+ let loading: Promise<void> | undefined
24
+ const loadExtensions = () =>
25
+ (loading ??= (async () => {
26
+ for (const e of options.extensions ?? []) extensions.set(e.key, await e.load())
27
+ scopeOptions.extensions = [...extensions.values()]
28
+ })())
29
+ let applied = -1
30
+ configure({
31
+ ...options.realtime,
32
+ build: options.build,
33
+ dev: options.dev,
34
+ store: options.store,
35
+ required: options.liveRequired,
36
+ onDiagnostic: options.onDiagnostic,
37
+ })
38
+
39
+ /**
40
+ * Before the first request (and in dev, when `drizzle/` grows): the auth secret, and the list each database applies
41
+ * before its first statement (db/core.ts), so a page with no queries doesn't need one. A client whose statements can't
42
+ * be held is migrated here; if that fails the request goes on, and Q016 is what using the database then gives.
43
+ */
44
+ async function boot() {
45
+ const list = options.migrations?.() ?? []
46
+ if (applied !== list.length) {
47
+ options.auth?.$weft?.check()
48
+ for (const e of extensions.values()) e.check?.(options.dev)
49
+ applied = list.length
50
+ const report = (e: unknown) =>
51
+ options.onDiagnostic?.({
52
+ code: 'Q016',
53
+ severity: 'error',
54
+ message: `Q016: ${String((e as Error | null)?.message ?? e)}`,
55
+ })
56
+ runtime.migrations = { list, report }
57
+ }
58
+ if (runtime.dbs?.size)
59
+ await Promise.all([...runtime.dbs].map((ready) => Promise.resolve(ready()).catch(() => {})))
60
+ }
61
+
62
+ const check = () => {
63
+ if (!checked) {
64
+ checked = true
65
+ exec.checkTags((d) => options.onDiagnostic?.(d))
66
+ }
67
+ }
68
+ return { mediaServer, scopeOptions, authBase, extensions, loadExtensions, boot, check }
69
+ }
@@ -0,0 +1,29 @@
1
+ import { esc } from '@weft-js/core/markup'
2
+ export { esc } from '@weft-js/core/markup'
3
+
4
+ export const json = (v: unknown) => JSON.stringify(v ?? null).replace(/</g, '\\u003c')
5
+ /**
6
+ * A redirect's body. A server's 3xx is followed by its `location`; a prerendered one is a file on a static host, and this
7
+ * is what that file says: go now, and the address to index. No stylesheet, so no view transition starts on a page
8
+ * that never paints (Chrome aborts it, and logs an InvalidStateError).
9
+ */
10
+ export const moved = (href: string) =>
11
+ `<!doctype html><html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Moved</title><meta name="robots" content="noindex"><link rel="canonical" href="${esc(href)}"><meta http-equiv="refresh" content="0; url=${esc(href)}"><a href="${esc(href)}">Moved here</a>`
12
+ export const thenable = (v: unknown): v is PromiseLike<unknown> =>
13
+ typeof (v as Partial<PromiseLike<unknown>> | null | undefined)?.then === 'function'
14
+
15
+ /** `/` → `index`, `/users/42` → `users/42`: the card path under `/_weft/og/`. */
16
+ export const cardPath = (path: string) =>
17
+ path.replace(/[?#].*/, '').replace(/^\/+|\/+$/g, '') || 'index'
18
+
19
+ /** FNV-1a, twice (different seeds), in base 36: a stable, sync content hash for `?v=`. */
20
+ export function hash(s: string): string {
21
+ let a = 0x811c9dc5,
22
+ b = 0x01000193 ^ 0x5bd1e995
23
+ for (let i = 0; i < s.length; i++) {
24
+ const c = s.charCodeAt(i)
25
+ a = Math.imul(a ^ c, 0x01000193)
26
+ b = Math.imul(b ^ c, 0x5bd1e995)
27
+ }
28
+ return ((a >>> 0).toString(36) + (b >>> 0).toString(36)).slice(0, 10)
29
+ }
@@ -0,0 +1,160 @@
1
+ import type { Site, Merged, Meta as MetaObject } from '@weft-js/core/ssr'
2
+ import type { AssetReceipt, Component, Match } from '../router'
3
+ import type { AuthProvider } from '../auth'
4
+ import type { ExtensionEntry } from '../extension'
5
+ import type { Context, Redirect } from '../data'
6
+ import type { MediaServer, Store } from '../exec'
7
+ import type { RealtimeOptions } from '../hub'
8
+ import type { Pieces } from '../first-paint'
9
+
10
+ /** A page route from the file tree. */
11
+ export interface RouteEntry {
12
+ id: string
13
+ file: string
14
+ layouts: string[]
15
+ pattern: string
16
+ params: string[]
17
+ splat?: string
18
+ card?: string
19
+ kind: 'page' | '404' | 'error'
20
+ }
21
+ /** A layout from the file tree. */
22
+ export interface LayoutEntry {
23
+ id: string
24
+ file: string
25
+ dir: string
26
+ parent?: string
27
+ }
28
+ /** The routes and layouts `weft build` found. */
29
+ export interface Manifest {
30
+ dir: string
31
+ routes: RouteEntry[]
32
+ layouts: LayoutEntry[]
33
+ defaultCard?: string
34
+ }
35
+
36
+ /** What a route file exports. */
37
+ export interface RouteModule {
38
+ default?: Component
39
+ load?: (args: {
40
+ params: Record<string, string>
41
+ request: Request
42
+ signal: AbortSignal
43
+ ctx: Context
44
+ }) => unknown
45
+ meta?:
46
+ | MetaObject
47
+ | ((args: {
48
+ params: Record<string, string>
49
+ data: unknown
50
+ }) => MetaObject | Promise<MetaObject>)
51
+ prerender?:
52
+ | true
53
+ | Record<string, string>[]
54
+ | (() => Record<string, string>[] | Promise<Record<string, string>[]>)
55
+ size?: { width: number; height: number }
56
+ }
57
+
58
+ /** A font for generated cards. */
59
+ export interface Font {
60
+ name: string
61
+ data: Uint8Array
62
+ weight?: number
63
+ style?: 'normal' | 'italic'
64
+ }
65
+
66
+ /** `createHandler`: the app as the build describes it, and how to serve it. */
67
+ export interface HandlerOptions {
68
+ manifest: Manifest
69
+ /** Route, layout and card modules by manifest file. */
70
+ modules: Record<string, RouteModule>
71
+ site?: Site
72
+ /**
73
+ * The client entry and what to preload (Nitro's `?assets=client`), plus the font build's preloads (`weft:fonts/preloads`).
74
+ * `pieces`: the build's first-paint table. Without it, `inline` and Weft's purge (`critical`) are what `pageStyles` uses.
75
+ */
76
+ assets?: {
77
+ entry?: string
78
+ js?: { href: string }[]
79
+ css?: { href: string }[]
80
+ fonts?: { href: string; type: string }[]
81
+ pieces?: Pieces & {
82
+ shared?: { href: string; has: string[] }
83
+ routes: { href: string; has: string[] }[]
84
+ }
85
+ inline?: Record<string, string>
86
+ critical?: {
87
+ parse: (css: string) => unknown[]
88
+ purge: (sheets: string[], found: { has: (word: string) => boolean }) => string[]
89
+ }
90
+ }
91
+ /** Changes with every build: part of each card's `?v=` hash. */
92
+ build?: string
93
+ /** Build-matched emitted asset receipt; null until the client bundle is available. */
94
+ routeAssetBytes?: AssetReceipt | null
95
+ dev?: boolean
96
+ fonts?: Font[]
97
+ /** Route and layout ids with no client code (PLAN §11): the server sends their HTML. */
98
+ statics?: string[]
99
+ /** Client chunks by level or island id: the page's own are preloaded, so they don't wait for the entry to ask. */
100
+ chunks?: Record<string, { js?: { href: string }[]; css?: { href: string }[] }>
101
+ /** Some island can wait, so every page carries the capture script: a navigation can arrive at one that does. */
102
+ lazy?: boolean
103
+ /** Stream timeout for Boundaries (ms). */
104
+ timeout?: number
105
+ /** How long (ms) a page waits for its Boundaries' data before streaming it: what lands sooner goes in the page. 0 streams at once. */
106
+ hold?: number
107
+ /** The app's context for `load` and server functions (`src/context.server.ts`'s default export). */
108
+ context?: (args: { request: Request }) => object | Promise<object>
109
+ /** The shared cache for `cache: { life }` queries (memory by default). */
110
+ store?: Store
111
+ /** `src/auth.server.ts`'s default export, any provider: mounted at its base path, checked on the first request. */
112
+ auth?: Pick<AuthProvider, 'handler' | 'basePath' | '$weft'>
113
+ /** `server.extension` providers with an app file (`virtual:weft/extensions`), loaded on the first request. */
114
+ extensions?: ExtensionEntry[]
115
+ /** The app's migrations (`drizzle/`); dev reads them each request, so `weft db generate` needs no restart. */
116
+ migrations?: () => { tag: string; sql: string }[]
117
+ /** The `media.server` provider (`virtual:weft/media`), loaded on first use. */
118
+ media?: () => Promise<MediaServer>
119
+ /** `src/realtime.server.ts`'s default export: the bus, origins and limits. */
120
+ realtime?: RealtimeOptions
121
+ /** `false`: never build URLs on the request's origin (`weft routes --meta`, whose requests aren't real). */
122
+ requestOrigin?: false
123
+ /** Queries marked `live: { required: true }` (`scanRequired`, at build). */
124
+ liveRequired?: string[]
125
+ /** Dev and tests: diagnostics found while serving (H005–H008, Q002–Q010). */
126
+ onDiagnostic?: (d: { code: string; severity: string; message: string; route?: string }) => void
127
+ }
128
+
129
+ /** One matched route level, with its module and the data before serialising. */
130
+ export interface Level extends Match {
131
+ mod?: RouteModule | undefined
132
+ raw: unknown
133
+ }
134
+
135
+ /** Where the words on a page came from, and its image: what `weft routes --meta` and `weft check` report. */
136
+ export interface MetaSource {
137
+ title: 'route' | 'layout' | 'auto' | 'config' | 'none'
138
+ description: 'route' | 'layout' | 'auto' | 'config' | 'none'
139
+ image: 'explicit' | 'card' | 'auto' | 'none'
140
+ }
141
+
142
+ /** What a path comes to: the status, the route and its levels. */
143
+ export interface Resolved {
144
+ status: number
145
+ path: string
146
+ levels: Level[]
147
+ head: Merged
148
+ /** The page route, when one matched. */
149
+ route?: RouteEntry | undefined
150
+ error?: unknown
151
+ /** A loader called `redirect()`: nothing renders. */
152
+ redirect?: Redirect
153
+ /** Headers supplied by a router-core redirect. */
154
+ redirectHeaders?: Headers
155
+ /** The metas the head is merged from, until `complete` has finished it. */
156
+ metas?: MetaObject[] | undefined
157
+ source?: MetaSource
158
+ /** The origin URLs are built on, when one is known. */
159
+ origin?: string | undefined
160
+ }