@brust/core 2.0.0-alpha.2 → 2.0.0-alpha.20
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 +66 -6
- package/native/index.d.ts +89 -4
- package/native/index.js +60 -54
- package/package.json +25 -9
- package/src/actions/client.ts +22 -5
- package/src/actions/load.ts +2 -1
- package/src/actions/prefix.ts +24 -2
- package/src/actions-bridge.ts +29 -11
- package/src/browser.ts +3 -1
- package/src/build/actions-only.ts +86 -0
- package/src/build/build-eval.ts +332 -0
- package/src/build/build-icons.ts +69 -0
- package/src/build/bundle.ts +462 -52
- package/src/build/cf-assets.ts +83 -0
- package/src/build/cf-core.ts +90 -0
- package/src/build/cf-entry.ts +76 -0
- package/src/build/cf-wrangler.ts +169 -0
- package/src/build/cloudflare.ts +320 -0
- package/src/build/compile.ts +114 -5
- package/src/build/config.ts +294 -11
- package/src/build/css.ts +275 -0
- package/src/build/esm-resolve.ts +170 -0
- package/src/build/features.ts +114 -0
- package/src/build/index.ts +264 -20
- package/src/build/manifest.ts +85 -7
- package/src/build/pkg-src.ts +514 -0
- package/src/build/recognised.ts +19 -0
- package/src/build/scan.ts +71 -20
- package/src/build/server-only.ts +39 -0
- package/src/build/ssg-render.ts +65 -0
- package/src/build/ssg.ts +146 -0
- package/src/build/static-worker.ts +91 -0
- package/src/build/static.ts +258 -0
- package/src/build/tailwind.ts +117 -0
- package/src/build/tsconfig-paths.ts +67 -0
- package/src/build-time.ts +10 -0
- package/src/cache-adapter/bridge.ts +91 -0
- package/src/cache-adapter/conformance.ts +226 -0
- package/src/cache-adapter/index.ts +10 -0
- package/src/cache-adapter/load.ts +144 -0
- package/src/cache-adapter/memory.ts +229 -0
- package/src/cache-adapter/runtime.ts +124 -0
- package/src/cache-adapter/types.ts +96 -0
- package/src/cache-adapter/workers.ts +118 -0
- package/src/cache.ts +4 -1
- package/src/cf.ts +19 -0
- package/src/cli.ts +71 -2
- package/src/config.ts +127 -25
- package/src/css-modules.d.ts +8 -0
- package/src/dev/bun.ts +371 -0
- package/src/dev/client.ts +92 -0
- package/src/dev/devtools-data.ts +23 -0
- package/src/dev/devtools.ts +356 -0
- package/src/dev/diag.ts +30 -0
- package/src/dev/proxy.ts +236 -0
- package/src/dev/tui.ts +144 -0
- package/src/dev/watch.ts +130 -0
- package/src/dev.ts +97 -0
- package/src/handlers.ts +282 -0
- package/src/index.ts +3 -1
- package/src/native.ts +38 -1
- package/src/routes.ts +20 -1
- package/src/run.ts +59 -12
- package/src/server-handlers.ts +32 -0
- package/src/shadcn/init.ts +513 -0
- package/src/shadcn/jsonc.ts +129 -0
- package/src/shadcn/registry.ts +149 -0
- package/src/worker.ts +127 -245
package/src/build/manifest.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import type { FlatRoute } from '../routes'
|
|
7
7
|
import type { Compiled, ComponentIR } from './compile'
|
|
8
8
|
import { BuildError } from './errors'
|
|
9
|
+
import { featuresOf } from './features'
|
|
9
10
|
|
|
10
11
|
export interface JobCache {
|
|
11
12
|
key: string | null
|
|
@@ -29,6 +30,12 @@ export interface ChildRecord {
|
|
|
29
30
|
id: string
|
|
30
31
|
instances: string
|
|
31
32
|
props: Record<string, string>
|
|
33
|
+
/** F105: constant props; part of the child's job keys. */
|
|
34
|
+
literals?: Record<string, unknown>
|
|
35
|
+
/** F105: jinja over `__props` (and `__row`/`__idx` per row) the server evaluates. */
|
|
36
|
+
exprs?: Record<string, string>
|
|
37
|
+
/** F142 R34.43: the list of a `per-row-literal` instance (a constant the compiler read). */
|
|
38
|
+
rows?: unknown[]
|
|
32
39
|
}
|
|
33
40
|
|
|
34
41
|
export interface ComponentRecord {
|
|
@@ -40,6 +47,8 @@ export interface ComponentRecord {
|
|
|
40
47
|
needs_worker: boolean
|
|
41
48
|
use_id_slots: number
|
|
42
49
|
stores: string[]
|
|
50
|
+
/** L33: the client runtime feature modules (`assets.features` keys) this component's output needs, in load order. */
|
|
51
|
+
features: string[]
|
|
43
52
|
}
|
|
44
53
|
|
|
45
54
|
export interface RouteRecord {
|
|
@@ -49,6 +58,8 @@ export interface RouteRecord {
|
|
|
49
58
|
loaders: string[]
|
|
50
59
|
cache: { ttl_seconds: number; prefix: string | null; bypass: true | string | null; tags: string[] } | null
|
|
51
60
|
catch_all: boolean
|
|
61
|
+
/** L19 (R19.3): `client/<file>.css` in cascade order; absent when the route has none. */
|
|
62
|
+
css?: string[]
|
|
52
63
|
}
|
|
53
64
|
|
|
54
65
|
/** `manifest.actions` (A3): `entry` is relative to dist; `maxBodyBytes`/`maxResponseBytes` are camelCase by contract. */
|
|
@@ -60,13 +71,66 @@ export interface ActionsRecord {
|
|
|
60
71
|
crossSite: 'reject' | 'allow'
|
|
61
72
|
}
|
|
62
73
|
|
|
74
|
+
/** The manifest format the Rust loader accepts (`MANIFEST_VERSION` in brust-core `manifest.rs`): the one place TS names it. */
|
|
75
|
+
export const MANIFEST_VERSION = 3 as const
|
|
76
|
+
|
|
63
77
|
export interface ManifestJson {
|
|
64
|
-
version:
|
|
78
|
+
version: typeof MANIFEST_VERSION
|
|
65
79
|
routes: RouteRecord[]
|
|
66
80
|
components: Record<string, ComponentRecord>
|
|
67
|
-
|
|
81
|
+
/** `runtime` is the core; `features` maps each feature module name to its file (L33). */
|
|
82
|
+
assets: { runtime: string; features: Record<string, string> }
|
|
68
83
|
jobs_module: string
|
|
69
84
|
actions: ActionsRecord | null
|
|
85
|
+
/** L16 R16.3: an actions-only build (no routes, no templates); everything but the prefix is a 404 problem+json. */
|
|
86
|
+
actions_only?: true
|
|
87
|
+
/** L16 R16.4b: a page-only build: no local actions app, `base_url` points at the deployment that has it. */
|
|
88
|
+
actions_remote?: { prefix: string }
|
|
89
|
+
/** `[cache]` (cache adapters); absent without one (the manifest is then byte-identical). */
|
|
90
|
+
cache?: CacheRecord
|
|
91
|
+
/** F165 (R40.1): `brust start` spawns its worker pool (`needsWorkers`). Read by `run.ts` only; an
|
|
92
|
+
* absent value (an older or actions-only manifest) means the pool is needed. */
|
|
93
|
+
needs_workers?: boolean
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** F165 (R40.1): whether any request can call a worker. Workers answer four kinds of call
|
|
97
|
+
* (worker.ts): `loader`, `jobs` (precompute and SSR), `fetch` (the local actions app) and `cache`
|
|
98
|
+
* (the adapter). Only the jobs a request can plan count (`reachedJobs`, F183). */
|
|
99
|
+
export function needsWorkers(m: Pick<ManifestJson, 'routes' | 'components' | 'actions' | 'cache'>): boolean {
|
|
100
|
+
const planned = [...reachedJobs(m).planned].some((id) => (m.components[id]?.jobs.length ?? 0) > 0)
|
|
101
|
+
return planned || m.routes.some((r) => r.loaders.length > 0) || m.actions !== null || m.cache !== undefined
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** F183: the jobs a request can call, as brust-core plans them (`collect_jobs`, pipeline.rs): every job of each
|
|
105
|
+
* route chain component and of each of its direct `children[]` records (`planned`; nothing deeper has a job,
|
|
106
|
+
* `linkNativeChunks`), and the `ssr` of each component one of those jobs targets (`ssr`; the worker runs the
|
|
107
|
+
* target's, handlers.ts). `jobs.js` leaves out every other entry: no request reaches it. */
|
|
108
|
+
export function reachedJobs(m: Pick<ManifestJson, 'routes' | 'components'>): { planned: Set<string>; ssr: Set<string> } {
|
|
109
|
+
const planned = new Set<string>()
|
|
110
|
+
for (const r of m.routes)
|
|
111
|
+
for (const id of r.chain) {
|
|
112
|
+
planned.add(id)
|
|
113
|
+
for (const ch of m.components[id]?.children ?? []) planned.add(ch.id)
|
|
114
|
+
}
|
|
115
|
+
const ssr = new Set<string>()
|
|
116
|
+
for (const id of planned) for (const j of m.components[id]?.jobs ?? []) if (j.kind === 'ssr' && j.target !== undefined) ssr.add(j.target)
|
|
117
|
+
return { planned, ssr }
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** `manifest.cache` / `CONFIG.cache`: the adapter the runtime loads (`cache-adapter/load.ts`). */
|
|
121
|
+
export interface CacheRecord {
|
|
122
|
+
adapter: string
|
|
123
|
+
/** `custom`: the bundled module, relative to dist. */
|
|
124
|
+
module?: string
|
|
125
|
+
broadcast?: string
|
|
126
|
+
options: Record<string, unknown>
|
|
127
|
+
broadcastOptions?: Record<string, unknown>
|
|
128
|
+
namespace: string
|
|
129
|
+
getTimeoutMs: number
|
|
130
|
+
maxBodyBytes: number
|
|
131
|
+
epochIntervalMs: number
|
|
132
|
+
/** 16 hex chars of sha256 over the build output (R7): every L2 key carries it. */
|
|
133
|
+
buildId: string
|
|
70
134
|
}
|
|
71
135
|
|
|
72
136
|
// biome-ignore lint/suspicious/noExplicitAny: IR JSON (externally tagged enums)
|
|
@@ -208,15 +272,23 @@ export function childRecords(c: Compiled, clientOnly: string[] = []): ChildRecor
|
|
|
208
272
|
if (inst.loops.length > 1)
|
|
209
273
|
throw new BuildError('nested-instance', `${ir.id}: child ${inst.child_id} sits in nested lists (M3)`)
|
|
210
274
|
const list = inst.loops[0]
|
|
211
|
-
|
|
275
|
+
const literal = list === null && Array.isArray(inst.rows_literal)
|
|
276
|
+
if (list === null && !literal)
|
|
212
277
|
throw new BuildError('instance-list-path', `${ir.id}: child ${inst.child_id} sits in a list that is not a props path`)
|
|
213
278
|
const props: Record<string, string> = {}
|
|
214
279
|
for (const [name, path] of Object.entries(inst.props)) {
|
|
215
280
|
if (path === null)
|
|
216
|
-
throw new BuildError(
|
|
281
|
+
throw new BuildError(
|
|
282
|
+
'instance-prop-path',
|
|
283
|
+
`${ir.id}: child ${inst.child_id} prop ${name} is not a props path, a JSON literal or a template expression over props; compute it in a loader, or wrap the child's derived value in cache()`,
|
|
284
|
+
)
|
|
217
285
|
props[name] = path
|
|
218
286
|
}
|
|
219
|
-
|
|
287
|
+
const rec: ChildRecord = { id: inst.child_id, instances: literal ? 'per-row-literal' : list === undefined ? 'static' : `per-row:${list}`, props }
|
|
288
|
+
if (inst.literals && Object.keys(inst.literals).length > 0) rec.literals = { ...inst.literals }
|
|
289
|
+
if (inst.exprs && Object.keys(inst.exprs).length > 0) rec.exprs = { ...inst.exprs }
|
|
290
|
+
if (literal) rec.rows = inst.rows_literal
|
|
291
|
+
return rec
|
|
220
292
|
})
|
|
221
293
|
for (const id of clientOnly) out.push({ id, instances: 'static', props: {} })
|
|
222
294
|
return out
|
|
@@ -243,7 +315,8 @@ export function writeManifest(opts: {
|
|
|
243
315
|
leaves: FlatRoute[]
|
|
244
316
|
routeComponent: Map<string, string>
|
|
245
317
|
compiled: Map<string, Compiled>
|
|
246
|
-
runtime:
|
|
318
|
+
/** The runtime files `buildRuntime` wrote: the core and the feature modules. */
|
|
319
|
+
runtime: { core: string; features: Record<string, string> }
|
|
247
320
|
/** component id → `client/<file>` (client chunk or react island chunk). */
|
|
248
321
|
chunks: Map<string, string>
|
|
249
322
|
actions?: ActionsRecord | null
|
|
@@ -262,7 +335,10 @@ export function writeManifest(opts: {
|
|
|
262
335
|
needs_worker: jobs.length > 0,
|
|
263
336
|
use_id_slots: c.ir.use_id_slots ?? 0,
|
|
264
337
|
stores: storesOf(compiled, c.id),
|
|
338
|
+
features: featuresOf(c.id, c.jinja, tierOf(c.ir) === 'react'),
|
|
265
339
|
}
|
|
340
|
+
for (const f of components[c.id]!.features)
|
|
341
|
+
if (!(f in opts.runtime.features)) throw new BuildError('runtime-feature-missing', `${c.id}: needs the client runtime feature ${f}, which the build did not ship`)
|
|
266
342
|
}
|
|
267
343
|
const routes: RouteRecord[] = opts.leaves.map((l) => {
|
|
268
344
|
// A node without a Component (a pure path/loader group) renders nothing: not a chain entry.
|
|
@@ -284,7 +360,7 @@ export function writeManifest(opts: {
|
|
|
284
360
|
}
|
|
285
361
|
})
|
|
286
362
|
linkNativeChunks(routes, compiled, components)
|
|
287
|
-
return { version:
|
|
363
|
+
return { version: MANIFEST_VERSION, routes, components, assets: { runtime: opts.runtime.core, features: opts.runtime.features }, jobs_module: 'jobs.js', actions: opts.actions ?? null }
|
|
288
364
|
}
|
|
289
365
|
|
|
290
366
|
/** S6 amendment (F66): the server's asset injection walks only each chain component's direct
|
|
@@ -297,6 +373,8 @@ function linkNativeChunks(routes: RouteRecord[], compiled: Map<string, Compiled>
|
|
|
297
373
|
const chainIds = new Set(routes.flatMap((r) => r.chain))
|
|
298
374
|
for (const root of chainIds) {
|
|
299
375
|
const rec = components[root]!
|
|
376
|
+
// A React root renders its whole subtree itself: whatever native chunks its IR lists are not served.
|
|
377
|
+
if (rec.tier === 'react') continue
|
|
300
378
|
const linked = new Set(rec.children.map((ch) => ch.id))
|
|
301
379
|
const seen = new Set([root])
|
|
302
380
|
const walk = (id: string) => {
|