@weft-js/start 0.1.0 → 0.1.2
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/README.md +36 -3
- package/crate/Cargo.lock +1347 -0
- package/crate/Cargo.toml +72 -0
- package/crate/rust-toolchain.toml +3 -0
- package/crate/src/build/emit.rs +103 -0
- package/crate/src/build.rs +367 -0
- package/crate/src/ctx.rs +186 -0
- package/crate/src/envelope.rs +163 -0
- package/crate/src/functions.rs +173 -0
- package/crate/src/lib.rs +118 -0
- package/crate/src/native.rs +212 -0
- package/crate/src/otel.rs +62 -0
- package/crate/src/server.rs +203 -0
- package/package.json +46 -37
- package/src/app.mjs +51 -10
- package/src/asset-receipt.d.mts +13 -0
- package/src/asset-receipt.mjs +61 -0
- package/src/auth/better-auth.ts +215 -17
- package/src/auth/drizzle.ts +1 -1
- package/src/auth.ts +21 -6
- package/src/boot.ts +19 -8
- package/src/bus/postgres.ts +32 -19
- package/src/bus.ts +7 -4
- package/src/{capture.mjs → capture.ts} +34 -1
- package/src/changes-sql.d.mts +8 -2
- package/src/changes-sql.mjs +26 -10
- package/src/changes.ts +165 -66
- package/src/channel/connections.ts +216 -0
- package/src/channel/socket.ts +396 -0
- package/src/channel-fake.ts +19 -4
- package/src/channel.ts +124 -490
- package/src/client.tsx +9 -2
- package/src/contract/describe.mjs +231 -0
- package/src/contract.mjs +146 -155
- package/src/data/cache.ts +354 -0
- package/src/data/definitions.ts +94 -0
- package/src/data/draft.ts +104 -0
- package/src/data/foundation.ts +95 -0
- package/src/data/live-api.ts +22 -0
- package/src/data/mutation.ts +138 -0
- package/src/data/primitives.ts +135 -0
- package/src/data/query.ts +106 -0
- package/src/data/transport.ts +121 -0
- package/src/data/types.ts +303 -0
- package/src/data.ts +69 -894
- package/src/db/bun-sqlite.d.ts +4 -1
- package/src/db/changes-mysql.ts +61 -21
- package/src/db/changes-postgres.ts +197 -118
- package/src/db/changes-sqlite.ts +131 -64
- package/src/db/core.ts +38 -13
- package/src/db/index.ts +20 -4
- package/src/db/mysql.ts +22 -6
- package/src/db/postgres.ts +49 -13
- package/src/db/sqlite.ts +53 -27
- package/src/derive.d.mts +4 -1
- package/src/derive.mjs +136 -39
- package/src/dev-runtime.mjs +8 -4
- package/src/exec/errors.ts +57 -0
- package/src/exec/landing.ts +66 -0
- package/src/exec/scope.ts +174 -0
- package/src/exec/touch.ts +80 -0
- package/src/exec/validation.ts +176 -0
- package/src/exec.ts +191 -341
- package/src/extension.ts +8 -2
- package/src/first-paint.ts +26 -9
- package/src/form/value.ts +15 -0
- package/src/form.tsx +132 -43
- package/src/hub/channels.ts +102 -0
- package/src/hub/connections.ts +147 -0
- package/src/hub/delivery.ts +62 -0
- package/src/hub/feed.ts +168 -0
- package/src/hub/presence.ts +64 -0
- package/src/hub/route.ts +158 -0
- package/src/hub/subscriptions.ts +212 -0
- package/src/hub/topic.ts +98 -0
- package/src/hub/tracked.ts +12 -0
- package/src/hub/types.ts +190 -0
- package/src/hub/view.ts +147 -0
- package/src/hub.ts +132 -758
- package/src/index.ts +84 -10
- package/src/inline.mjs +356 -0
- package/src/inspect/compare.mjs +162 -0
- package/src/inspect/routes.mjs +154 -0
- package/src/inspect/samples.mjs +59 -0
- package/src/inspect/url.mjs +2 -0
- package/src/inspect.mjs +146 -187
- package/src/link/election.ts +178 -0
- package/src/link/lines.ts +27 -0
- package/src/link.ts +97 -168
- package/src/list-drizzle.ts +104 -18
- package/src/list.ts +127 -36
- package/src/listen.ts +32 -6
- package/src/live/api.ts +18 -0
- package/src/live/backoff.ts +6 -0
- package/src/live/frames.ts +42 -0
- package/src/live/read.ts +77 -0
- package/src/live/sub-types.ts +24 -0
- package/src/live/transport.ts +86 -0
- package/src/live/types.ts +52 -0
- package/src/live-fake.ts +20 -5
- package/src/live-target.mjs +113 -35
- package/src/live.ts +116 -225
- package/src/media.ts +8 -1
- package/src/migrations.mjs +14 -6
- package/src/mux.ts +161 -64
- package/src/og/DefaultCard.tsx +40 -9
- package/src/og/fonts.mjs +16 -8
- package/src/og/images.mjs +43 -22
- package/src/og/index.mjs +119 -43
- package/src/og/native.mjs +11 -4
- package/src/range.mjs +15 -3
- package/src/router.ts +56 -6
- package/src/routes.mjs +73 -22
- package/src/run/admission.ts +80 -0
- package/src/run/boot.tsx +115 -0
- package/src/run/cache-presentation.ts +67 -0
- package/src/run/cache.ts +254 -0
- package/src/run/helpers.ts +197 -0
- package/src/run/listeners.ts +155 -0
- package/src/run/navigation.ts +382 -0
- package/src/run/page-skeleton.tsx +35 -0
- package/src/run/prefetch.ts +128 -0
- package/src/run/retain-view.ts +18 -0
- package/src/run/shape-preparation.ts +97 -0
- package/src/run/shape-styles.ts +72 -0
- package/src/run/source-context/pin.ts +124 -0
- package/src/run/source-context.ts +311 -0
- package/src/run.tsx +50 -174
- package/src/rust/common.ts +163 -0
- package/src/rust/native.ts +282 -0
- package/src/rust.ts +103 -229
- package/src/server/card.tsx +65 -0
- package/src/server/compression.ts +115 -0
- package/src/server/core.ts +70 -0
- package/src/server/dispatch.ts +70 -0
- package/src/server/form.ts +85 -0
- package/src/server/functions.ts +129 -0
- package/src/server/handler.ts +39 -0
- package/src/server/head.ts +96 -0
- package/src/server/media.ts +60 -0
- package/src/server/navigation-cache.ts +80 -0
- package/src/server/navigation.tsx +85 -0
- package/src/server/page-cold.ts +95 -0
- package/src/server/page-stream.tsx +181 -0
- package/src/server/page-types.ts +51 -0
- package/src/server/page-warm.ts +130 -0
- package/src/server/page.ts +69 -0
- package/src/server/payload.ts +89 -0
- package/src/server/prerender.ts +60 -0
- package/src/server/preview.tsx +67 -0
- package/src/server/render.ts +167 -0
- package/src/server/request.ts +98 -0
- package/src/server/resolver.ts +228 -0
- package/src/server/routes.ts +150 -0
- package/src/server/services.ts +69 -0
- package/src/server/text.ts +29 -0
- package/src/server/types.ts +160 -0
- package/src/server.tsx +82 -964
- package/src/shape.mjs +30 -6
- package/src/share/display.ts +50 -0
- package/src/share/network.ts +119 -0
- package/src/share/receive.ts +70 -0
- package/src/share/state.ts +30 -0
- package/src/share/tab-queue.ts +28 -0
- package/src/share-server.ts +128 -43
- package/src/share.ts +103 -209
- package/src/sheets.mjs +30 -18
- package/src/sidecar/build.mjs +170 -0
- package/src/sidecar/cargo.mjs +164 -0
- package/src/sidecar/common.mjs +17 -0
- package/src/sidecar/dev-build.mjs +91 -0
- package/src/sidecar/dev-watch.mjs +88 -0
- package/src/sidecar/manifest.mjs +19 -0
- package/src/sidecar/process.mjs +58 -0
- package/src/sidecar/refresh.mjs +19 -0
- package/src/sidecar.mjs +75 -416
- package/src/socket/check.ts +50 -0
- package/src/socket/deliver.ts +16 -0
- package/src/socket/publish.ts +56 -0
- package/src/socket/rooms.ts +41 -0
- package/src/socket/route.ts +79 -0
- package/src/socket/shared.ts +26 -0
- package/src/socket/types.ts +48 -0
- package/src/socket.ts +205 -194
- package/src/stream.ts +10 -1
- package/src/verify/app.ts +39 -0
- package/src/verify/cleanup.ts +27 -0
- package/src/verify/database.ts +11 -0
- package/src/verify/live.ts +63 -0
- package/src/verify/notify.ts +35 -0
- package/src/verify/skip.ts +19 -0
- package/src/verify/support.ts +149 -0
- package/src/verify/timing.ts +15 -0
- package/src/verify/trigger.ts +14 -0
- package/src/verify.ts +77 -176
- package/src/view.tsx +96 -11
- package/src/vite/asset-bytes.mjs +95 -0
- package/src/vite/assets.mjs +117 -0
- package/src/vite/config-hooks.mjs +166 -0
- package/src/vite/dev-hooks.mjs +103 -0
- package/src/vite/dev.mjs +102 -0
- package/src/vite/entries.mjs +145 -0
- package/src/vite/module-hooks.mjs +288 -0
- package/src/vite/route-modules.mjs +289 -0
- package/src/vite/server-functions.mjs +222 -0
- package/src/vite/shape-entry-css.mjs +187 -0
- package/src/vite/shape-override.mjs +226 -0
- package/src/vite/shared.mjs +51 -0
- package/src/vite/sheets.mjs +92 -0
- package/src/vite/state.mjs +89 -0
- package/src/vite/syntax.mjs +139 -0
- package/src/vite.mjs +152 -853
- 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
|
+
}
|