@ultimat3/render 11.3.0 → 12.0.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/CLAUDE.md +1 -0
- package/package.json +5 -5
- package/src/route.ts +18 -4
- package/src/type-pins.tsx +27 -1
package/CLAUDE.md
CHANGED
|
@@ -40,6 +40,7 @@ for the mirror-image reason, keeping `render → pwa` refused. Never `cli` (upwa
|
|
|
40
40
|
| Type claims | `type-pins.tsx`, never a `.test.ts` — `tsconfig.json` excludes tests, so `tsc` never reads one. `.tsx` since 1.2.0: the island-as-JSX claim is only decidable by writing the JSX an author writes, checked against the same `solid-js` `JSX.Element` a page is. |
|
|
41
41
|
| Descriptor `meta` / `load` | always `(x) => Promise<…>`. Authors may declare either sync; consumers never branch. |
|
|
42
42
|
| Descriptor `budget` | always an object, `{}` when undeclared. Its *fields* stay optional — `budget.js === undefined` is the site/ hydration failure. |
|
|
43
|
+
| `RouteBudget` keys | **`js` and `lcp`, and every key is PROJECTED** (`As of 2026-08-24`). `css`, `cls` and `tbt` were declared on the contract, flattened away by `registerRoute` — the descriptor carries `budgetJs` + `budgetLcp` and nothing else — and read by NOTHING: `x verify`'s `budgets` step, `x.manifest.json`, `x routes` and the dev dashboard all read those two. So `budget: { cls: 0.1 }` was accepted, normalised, stored and ignored while the gate that exists to enforce budgets reported green — the shape `jobs.driver` and `realtime.tier` had one declaration surface over. **Deleted, not wired**, for `PwaConfig.installPrompt`'s reason: the measuring half does not exist either. `type-pins.tsx` derives the allowed set from `RouteDescriptor`'s own `budget*` keys, so a key added here without a projection is a build error; `scripts/declaration-readers.ts` is the rule that finds the class across every declaration. **Breaking**: three keys are gone from `RouteBudget`. |
|
|
43
44
|
| No `describe()` on a route | `describeRoutes()` is the one route list. A per-route projection would be a second one. |
|
|
44
45
|
| Mode invariants | `modes.ts` only. Never inline a mode check in a render-\* file. **Four modes, and every one renders the route's component** — `spa` did not, and `renderSpa` never read `entry.component`, so a `spa` route served `<div id="x-root"></div>` in every app that ever declared one. A gated page is `ssr`; a body that belongs in the browser is an `island({ src })`. A fifth mode has to name the function that renders it. |
|
|
45
46
|
| Island declaration | `island({ src })` — a **specifier**, never an import. That is the whole boundary: a string has no scope to close over and no edge for a bundler or `checkSurfaceBoundary` to follow, so a `static` page's graph cannot grow the island's dependencies. Never add an overload that takes a component. |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ultimat3/render",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "12.0.0",
|
|
4
4
|
"description": "The route primitive and the five render modes: static, isr, ssr, stream, spa.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -36,10 +36,10 @@
|
|
|
36
36
|
"test": "bun test"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@ultimat3/cache": "
|
|
40
|
-
"@ultimat3/core": "
|
|
41
|
-
"@ultimat3/i18n": "
|
|
42
|
-
"@ultimat3/seo": "
|
|
39
|
+
"@ultimat3/cache": "12.0.0",
|
|
40
|
+
"@ultimat3/core": "12.0.0",
|
|
41
|
+
"@ultimat3/i18n": "12.0.0",
|
|
42
|
+
"@ultimat3/seo": "12.0.0",
|
|
43
43
|
"sass": "1.102.0"
|
|
44
44
|
}
|
|
45
45
|
}
|
package/src/route.ts
CHANGED
|
@@ -42,14 +42,28 @@ export interface RevalidateConfig {
|
|
|
42
42
|
readonly ttl?: string | number;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
/**
|
|
46
|
+
* What a route promises to stay under. **Two keys, and every one of them is PROJECTED** — a key
|
|
47
|
+
* here that `RouteDescriptor` does not carry is a number an author writes and nothing can ever
|
|
48
|
+
* compare against, which `type-pins.tsx` now makes a build error.
|
|
49
|
+
*
|
|
50
|
+
* `css`, `cls` and `tbt` were three such keys and are gone in 12.0.0. `registerRoute` flattens a
|
|
51
|
+
* budget to `budgetJs` + `budgetLcp`, and every reader downstream — `x verify`'s `budgets` step,
|
|
52
|
+
* `x.manifest.json`, `x routes`, the dev dashboard — reads only those two, so `budget: { cls: 0.1 }`
|
|
53
|
+
* was accepted, normalised, stored and ignored while the gate that exists to enforce budgets
|
|
54
|
+
* reported green. Deleted rather than wired for `PwaConfig.installPrompt`'s reason: the measuring
|
|
55
|
+
* half does not exist either, and a knob that fails silently in the permissive direction is the
|
|
56
|
+
* framework's most repeated defect.
|
|
57
|
+
*/
|
|
45
58
|
export interface RouteBudget {
|
|
46
59
|
/** `'40kb'` — measured from the real bundle graph, not the source size. */
|
|
47
60
|
readonly js?: string;
|
|
48
|
-
|
|
49
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Milliseconds. PUBLISHED, not enforced: it reaches `x.manifest.json` and `x routes`, and no
|
|
63
|
+
* step of the gate compares it — nothing in the framework produces an `lcp` measurement, which
|
|
64
|
+
* `packages/cli/src/templates/route.ts` states at the one place an author would write one.
|
|
65
|
+
*/
|
|
50
66
|
readonly lcp?: number;
|
|
51
|
-
readonly cls?: number;
|
|
52
|
-
readonly tbt?: number;
|
|
53
67
|
}
|
|
54
68
|
|
|
55
69
|
/**
|
package/src/type-pins.tsx
CHANGED
|
@@ -11,7 +11,14 @@
|
|
|
11
11
|
import type { IslandComponent } from './island';
|
|
12
12
|
import { island } from './island';
|
|
13
13
|
import type { JsonValue } from './island-props';
|
|
14
|
-
import type {
|
|
14
|
+
import type { RouteDescriptor } from './registry';
|
|
15
|
+
import type {
|
|
16
|
+
LoadRequirement,
|
|
17
|
+
RouteBudget,
|
|
18
|
+
RouteContext,
|
|
19
|
+
RouteData,
|
|
20
|
+
RouteDefinition,
|
|
21
|
+
} from './route';
|
|
15
22
|
|
|
16
23
|
/** Fails to compile when `T` is anything but `true`. The whole mechanism. */
|
|
17
24
|
type Assert<T extends true> = T;
|
|
@@ -99,3 +106,22 @@ export function _IslandIsAJsxComponent(): unknown {
|
|
|
99
106
|
</ContactSales>
|
|
100
107
|
);
|
|
101
108
|
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Every key a route may declare a BUDGET for is a key the descriptor projects.
|
|
112
|
+
*
|
|
113
|
+
* Both sides are derived, so neither can move alone: `budgetJs`/`budgetLcp` on `RouteDescriptor`
|
|
114
|
+
* give `'js' | 'lcp'`, and anything `RouteBudget` declares beyond that fails this line by name.
|
|
115
|
+
* `budget.css`, `budget.cls` and `budget.tbt` were declared on the contract for four majors, were
|
|
116
|
+
* flattened away by `registerRoute`, and reached no reader anywhere — so an author writing
|
|
117
|
+
* `budget: { cls: 0.1 }` was ignored in silence while `x verify`'s `budgets` step, the one thing
|
|
118
|
+
* that exists to enforce a budget, reported green. `scripts/declaration-readers.ts` is the rule
|
|
119
|
+
* that finds the class; this is the build error that keeps THIS declaration honest.
|
|
120
|
+
*/
|
|
121
|
+
type BudgetKeyOf<K> = K extends `budget${infer Rest}` ? Uncapitalize<Rest> : never;
|
|
122
|
+
|
|
123
|
+
type UnprojectedBudgetKey = Exclude<keyof RouteBudget, BudgetKeyOf<keyof RouteDescriptor>>;
|
|
124
|
+
|
|
125
|
+
export type _EveryBudgetKeyIsProjected = Assert<
|
|
126
|
+
[UnprojectedBudgetKey] extends [never] ? true : false
|
|
127
|
+
>;
|