@czap/astro 0.1.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/LICENSE +21 -0
- package/README.md +19 -0
- package/dist/Satellite.d.ts +42 -0
- package/dist/Satellite.d.ts.map +1 -0
- package/dist/Satellite.js +55 -0
- package/dist/Satellite.js.map +1 -0
- package/dist/client-directives/gpu.d.ts +3 -0
- package/dist/client-directives/gpu.d.ts.map +1 -0
- package/dist/client-directives/gpu.js +5 -0
- package/dist/client-directives/gpu.js.map +1 -0
- package/dist/client-directives/llm.d.ts +3 -0
- package/dist/client-directives/llm.d.ts.map +1 -0
- package/dist/client-directives/llm.js +5 -0
- package/dist/client-directives/llm.js.map +1 -0
- package/dist/client-directives/satellite.d.ts +3 -0
- package/dist/client-directives/satellite.d.ts.map +1 -0
- package/dist/client-directives/satellite.js +5 -0
- package/dist/client-directives/satellite.js.map +1 -0
- package/dist/client-directives/stream.d.ts +3 -0
- package/dist/client-directives/stream.d.ts.map +1 -0
- package/dist/client-directives/stream.js +5 -0
- package/dist/client-directives/stream.js.map +1 -0
- package/dist/client-directives/wasm.d.ts +3 -0
- package/dist/client-directives/wasm.d.ts.map +1 -0
- package/dist/client-directives/wasm.js +6 -0
- package/dist/client-directives/wasm.js.map +1 -0
- package/dist/client-directives/worker.d.ts +3 -0
- package/dist/client-directives/worker.d.ts.map +1 -0
- package/dist/client-directives/worker.js +5 -0
- package/dist/client-directives/worker.js.map +1 -0
- package/dist/detect-upgrade.d.ts +16 -0
- package/dist/detect-upgrade.d.ts.map +1 -0
- package/dist/detect-upgrade.js +105 -0
- package/dist/detect-upgrade.js.map +1 -0
- package/dist/headers.d.ts +45 -0
- package/dist/headers.d.ts.map +1 -0
- package/dist/headers.js +64 -0
- package/dist/headers.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/integration.d.ts +76 -0
- package/dist/integration.d.ts.map +1 -0
- package/dist/integration.js +240 -0
- package/dist/integration.js.map +1 -0
- package/dist/middleware.d.ts +69 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +75 -0
- package/dist/middleware.js.map +1 -0
- package/dist/quantize.d.ts +50 -0
- package/dist/quantize.d.ts.map +1 -0
- package/dist/quantize.js +122 -0
- package/dist/quantize.js.map +1 -0
- package/dist/runtime/boundary.d.ts +123 -0
- package/dist/runtime/boundary.d.ts.map +1 -0
- package/dist/runtime/boundary.js +164 -0
- package/dist/runtime/boundary.js.map +1 -0
- package/dist/runtime/globals.d.ts +32 -0
- package/dist/runtime/globals.d.ts.map +1 -0
- package/dist/runtime/globals.js +45 -0
- package/dist/runtime/globals.js.map +1 -0
- package/dist/runtime/gpu.d.ts +15 -0
- package/dist/runtime/gpu.d.ts.map +1 -0
- package/dist/runtime/gpu.js +266 -0
- package/dist/runtime/gpu.js.map +1 -0
- package/dist/runtime/index.d.ts +7 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +5 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/llm-receipt-tracker.d.ts +21 -0
- package/dist/runtime/llm-receipt-tracker.d.ts.map +1 -0
- package/dist/runtime/llm-receipt-tracker.js +60 -0
- package/dist/runtime/llm-receipt-tracker.js.map +1 -0
- package/dist/runtime/llm-render-pipeline.d.ts +89 -0
- package/dist/runtime/llm-render-pipeline.d.ts.map +1 -0
- package/dist/runtime/llm-render-pipeline.js +241 -0
- package/dist/runtime/llm-render-pipeline.js.map +1 -0
- package/dist/runtime/llm-session.d.ts +126 -0
- package/dist/runtime/llm-session.d.ts.map +1 -0
- package/dist/runtime/llm-session.js +385 -0
- package/dist/runtime/llm-session.js.map +1 -0
- package/dist/runtime/llm.d.ts +16 -0
- package/dist/runtime/llm.d.ts.map +1 -0
- package/dist/runtime/llm.js +273 -0
- package/dist/runtime/llm.js.map +1 -0
- package/dist/runtime/policy.d.ts +100 -0
- package/dist/runtime/policy.d.ts.map +1 -0
- package/dist/runtime/policy.js +147 -0
- package/dist/runtime/policy.js.map +1 -0
- package/dist/runtime/receipt-chain.d.ts +22 -0
- package/dist/runtime/receipt-chain.d.ts.map +1 -0
- package/dist/runtime/receipt-chain.js +80 -0
- package/dist/runtime/receipt-chain.js.map +1 -0
- package/dist/runtime/runtime-session.d.ts +34 -0
- package/dist/runtime/runtime-session.d.ts.map +1 -0
- package/dist/runtime/runtime-session.js +102 -0
- package/dist/runtime/runtime-session.js.map +1 -0
- package/dist/runtime/satellite.d.ts +13 -0
- package/dist/runtime/satellite.d.ts.map +1 -0
- package/dist/runtime/satellite.js +59 -0
- package/dist/runtime/satellite.js.map +1 -0
- package/dist/runtime/slots.d.ts +34 -0
- package/dist/runtime/slots.d.ts.map +1 -0
- package/dist/runtime/slots.js +108 -0
- package/dist/runtime/slots.js.map +1 -0
- package/dist/runtime/stream-session.d.ts +47 -0
- package/dist/runtime/stream-session.d.ts.map +1 -0
- package/dist/runtime/stream-session.js +82 -0
- package/dist/runtime/stream-session.js.map +1 -0
- package/dist/runtime/stream.d.ts +9 -0
- package/dist/runtime/stream.d.ts.map +1 -0
- package/dist/runtime/stream.js +308 -0
- package/dist/runtime/stream.js.map +1 -0
- package/dist/runtime/url-policy.d.ts +28 -0
- package/dist/runtime/url-policy.d.ts.map +1 -0
- package/dist/runtime/url-policy.js +87 -0
- package/dist/runtime/url-policy.js.map +1 -0
- package/dist/runtime/wasm.d.ts +20 -0
- package/dist/runtime/wasm.d.ts.map +1 -0
- package/dist/runtime/wasm.js +70 -0
- package/dist/runtime/wasm.js.map +1 -0
- package/dist/runtime/worker.d.ts +11 -0
- package/dist/runtime/worker.d.ts.map +1 -0
- package/dist/runtime/worker.js +249 -0
- package/dist/runtime/worker.js.map +1 -0
- package/package.json +106 -0
- package/src/Satellite.astro +39 -0
- package/src/Satellite.ts +84 -0
- package/src/client-directives/gpu.ts +5 -0
- package/src/client-directives/llm.ts +5 -0
- package/src/client-directives/satellite.ts +5 -0
- package/src/client-directives/stream.ts +5 -0
- package/src/client-directives/wasm.ts +6 -0
- package/src/client-directives/worker.ts +5 -0
- package/src/detect-upgrade.ts +105 -0
- package/src/headers.ts +84 -0
- package/src/index.ts +30 -0
- package/src/integration.ts +309 -0
- package/src/middleware.ts +133 -0
- package/src/quantize.ts +173 -0
- package/src/runtime/boundary.ts +263 -0
- package/src/runtime/globals.ts +57 -0
- package/src/runtime/gpu.ts +291 -0
- package/src/runtime/index.ts +12 -0
- package/src/runtime/llm-receipt-tracker.ts +88 -0
- package/src/runtime/llm-render-pipeline.ts +366 -0
- package/src/runtime/llm-session.ts +548 -0
- package/src/runtime/llm.ts +344 -0
- package/src/runtime/policy.ts +229 -0
- package/src/runtime/receipt-chain.ts +106 -0
- package/src/runtime/runtime-session.ts +139 -0
- package/src/runtime/satellite.ts +80 -0
- package/src/runtime/slots.ts +136 -0
- package/src/runtime/stream-session.ts +125 -0
- package/src/runtime/stream.ts +407 -0
- package/src/runtime/url-policy.ts +107 -0
- package/src/runtime/wasm.ts +85 -0
- package/src/runtime/worker.ts +307 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Astro 6 `AstroIntegration` for czap.
|
|
3
|
+
*
|
|
4
|
+
* Registers the `@czap/vite` plugin, injects the detect/boot scripts,
|
|
5
|
+
* registers every client directive (`client:satellite`,
|
|
6
|
+
* `client:stream`, `client:llm`, `client:worker`, `client:gpu`,
|
|
7
|
+
* `client:wasm`) that the host opts into, and turns on Astro's
|
|
8
|
+
* `serverIslands` experimental flag when requested.
|
|
9
|
+
*
|
|
10
|
+
* @module
|
|
11
|
+
*/
|
|
12
|
+
import type { AstroIntegration } from 'astro';
|
|
13
|
+
import type { PluginConfig } from '@czap/vite';
|
|
14
|
+
import type { RuntimeEndpointPolicy } from '@czap/web';
|
|
15
|
+
import { type RuntimeHtmlPolicy } from './runtime/policy.js';
|
|
16
|
+
/**
|
|
17
|
+
* Options passed to {@link integration} from `astro.config.mjs`. Every
|
|
18
|
+
* field is optional; omitted features fall back to conservative
|
|
19
|
+
* defaults (detect enabled, stream/llm/gpu enabled, workers/wasm/server
|
|
20
|
+
* islands opt-in).
|
|
21
|
+
*/
|
|
22
|
+
export interface IntegrationConfig {
|
|
23
|
+
/** Overrides passed through to `@czap/vite`'s plugin. */
|
|
24
|
+
readonly vite?: PluginConfig;
|
|
25
|
+
/** Enable the inline detect script (default `true`). */
|
|
26
|
+
readonly detect?: boolean;
|
|
27
|
+
/** Turn on Astro's experimental server-islands flag (default `false`). */
|
|
28
|
+
readonly serverIslands?: boolean;
|
|
29
|
+
/** WASM runtime configuration. */
|
|
30
|
+
readonly wasm?: {
|
|
31
|
+
readonly enabled?: boolean;
|
|
32
|
+
readonly path?: string;
|
|
33
|
+
};
|
|
34
|
+
/** GPU runtime configuration. */
|
|
35
|
+
readonly gpu?: {
|
|
36
|
+
readonly enabled?: boolean;
|
|
37
|
+
readonly preferWebGPU?: boolean;
|
|
38
|
+
};
|
|
39
|
+
/** Off-thread worker runtime configuration. */
|
|
40
|
+
readonly workers?: {
|
|
41
|
+
readonly enabled?: boolean;
|
|
42
|
+
};
|
|
43
|
+
/** SSE streaming runtime configuration. */
|
|
44
|
+
readonly stream?: {
|
|
45
|
+
readonly enabled?: boolean;
|
|
46
|
+
};
|
|
47
|
+
/** LLM streaming runtime configuration. */
|
|
48
|
+
readonly llm?: {
|
|
49
|
+
readonly enabled?: boolean;
|
|
50
|
+
};
|
|
51
|
+
/** Security policies applied to runtime fetch/HTML boundaries. */
|
|
52
|
+
readonly security?: {
|
|
53
|
+
readonly endpointPolicy?: RuntimeEndpointPolicy;
|
|
54
|
+
readonly htmlPolicy?: RuntimeHtmlPolicy;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Build the czap `AstroIntegration`.
|
|
59
|
+
*
|
|
60
|
+
* Plug the returned object into `astro.config.mjs`'s `integrations`
|
|
61
|
+
* array. The integration wires Astro's `astro:config:setup`,
|
|
62
|
+
* `astro:config:done`, `astro:server:setup`, and `astro:build:done`
|
|
63
|
+
* hooks.
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```ts
|
|
67
|
+
* // astro.config.mjs
|
|
68
|
+
* import { integration as czap } from '@czap/astro';
|
|
69
|
+
*
|
|
70
|
+
* const config = defineConfig({
|
|
71
|
+
* integrations: [czap({ detect: true, workers: { enabled: true } })],
|
|
72
|
+
* });
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
export declare function integration(config?: IntegrationConfig): AstroIntegration;
|
|
76
|
+
//# sourceMappingURL=integration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integration.d.ts","sourceRoot":"","sources":["../src/integration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC;AAE9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AACvD,OAAO,EAEL,KAAK,iBAAiB,EAEvB,MAAM,qBAAqB,CAAC;AAM7B;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,yDAAyD;IACzD,QAAQ,CAAC,IAAI,CAAC,EAAE,YAAY,CAAC;IAC7B,wDAAwD;IACxD,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,0EAA0E;IAC1E,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;IACjC,kCAAkC;IAClC,QAAQ,CAAC,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACvE,iCAAiC;IACjC,QAAQ,CAAC,GAAG,CAAC,EAAE;QAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAC/E,+CAA+C;IAC/C,QAAQ,CAAC,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAClD,2CAA2C;IAC3C,QAAQ,CAAC,MAAM,CAAC,EAAE;QAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IACjD,2CAA2C;IAC3C,QAAQ,CAAC,GAAG,CAAC,EAAE;QAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAC9C,kEAAkE;IAClE,QAAQ,CAAC,QAAQ,CAAC,EAAE;QAClB,QAAQ,CAAC,cAAc,CAAC,EAAE,qBAAqB,CAAC;QAChD,QAAQ,CAAC,UAAU,CAAC,EAAE,iBAAiB,CAAC;KACzC,CAAC;CACH;AAmGD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,WAAW,CAAC,MAAM,CAAC,EAAE,iBAAiB,GAAG,gBAAgB,CAuIxE"}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Astro 6 `AstroIntegration` for czap.
|
|
3
|
+
*
|
|
4
|
+
* Registers the `@czap/vite` plugin, injects the detect/boot scripts,
|
|
5
|
+
* registers every client directive (`client:satellite`,
|
|
6
|
+
* `client:stream`, `client:llm`, `client:worker`, `client:gpu`,
|
|
7
|
+
* `client:wasm`) that the host opts into, and turns on Astro's
|
|
8
|
+
* `serverIslands` experimental flag when requested.
|
|
9
|
+
*
|
|
10
|
+
* @module
|
|
11
|
+
*/
|
|
12
|
+
import { plugin } from '@czap/vite';
|
|
13
|
+
import { DETECT_UPGRADE_SCRIPT } from './detect-upgrade.js';
|
|
14
|
+
import { getCzapHeaderEntries } from './headers.js';
|
|
15
|
+
import { normalizeRuntimeSecurityPolicy, } from './runtime/policy.js';
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// Detect Script
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
/**
|
|
20
|
+
* Inline script that runs device detection on page load and stores
|
|
21
|
+
* the result as CSS custom properties on the <html> element and
|
|
22
|
+
* as a global for runtime access.
|
|
23
|
+
*/
|
|
24
|
+
const DETECT_INLINE_SCRIPT = `
|
|
25
|
+
(function(){
|
|
26
|
+
function writeDetectState(next) {
|
|
27
|
+
var safe = Object.freeze(Object.assign({}, next));
|
|
28
|
+
try {
|
|
29
|
+
Object.defineProperty(window, '__CZAP_DETECT__', {
|
|
30
|
+
value: safe,
|
|
31
|
+
configurable: true,
|
|
32
|
+
enumerable: false,
|
|
33
|
+
writable: false
|
|
34
|
+
});
|
|
35
|
+
} catch (_) {
|
|
36
|
+
try {
|
|
37
|
+
window.__CZAP_DETECT__ = safe;
|
|
38
|
+
} catch (_) {}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
var h = document.documentElement;
|
|
44
|
+
var w = window.innerWidth || 0;
|
|
45
|
+
var cores = navigator.hardwareConcurrency || 2;
|
|
46
|
+
var mem = navigator.deviceMemory || 4;
|
|
47
|
+
var touch = ('ontouchstart' in window) || (navigator.maxTouchPoints > 0);
|
|
48
|
+
var motion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
49
|
+
var dark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
50
|
+
var dpr = window.devicePixelRatio || 1;
|
|
51
|
+
|
|
52
|
+
h.style.setProperty('--czap-vw', w + 'px');
|
|
53
|
+
h.style.setProperty('--czap-cores', String(cores));
|
|
54
|
+
h.style.setProperty('--czap-dpr', String(dpr));
|
|
55
|
+
h.setAttribute('data-czap-touch', String(touch));
|
|
56
|
+
h.setAttribute('data-czap-motion', motion ? 'reduce' : 'no-preference');
|
|
57
|
+
h.setAttribute('data-czap-scheme', dark ? 'dark' : 'light');
|
|
58
|
+
|
|
59
|
+
// Provisional tier -- conservative, no GPU probe available inline.
|
|
60
|
+
// Full detect package overrides on hydration via data-czap-tier attribute.
|
|
61
|
+
var tier = 'reactive';
|
|
62
|
+
if (motion) tier = 'static';
|
|
63
|
+
else if (cores <= 2 || mem <= 2) tier = 'styled';
|
|
64
|
+
h.setAttribute('data-czap-tier', tier);
|
|
65
|
+
h.setAttribute('data-czap-tier-provisional', 'true');
|
|
66
|
+
|
|
67
|
+
writeDetectState({
|
|
68
|
+
tier: tier,
|
|
69
|
+
provisional: true
|
|
70
|
+
});
|
|
71
|
+
} catch(e) {}
|
|
72
|
+
})();
|
|
73
|
+
`.trim();
|
|
74
|
+
function serializeInlineRuntimePolicy(policy) {
|
|
75
|
+
return JSON.stringify(policy).replace(/</g, '\\u003c').replace(/>/g, '\\u003e').replace(/&/g, '\\u0026');
|
|
76
|
+
}
|
|
77
|
+
function runtimeBootstrapScript(policy) {
|
|
78
|
+
return `
|
|
79
|
+
import { bootstrapSlots, configureRuntimePolicy, installSwapReinit } from '@czap/astro/runtime';
|
|
80
|
+
|
|
81
|
+
configureRuntimePolicy(${serializeInlineRuntimePolicy(policy)});
|
|
82
|
+
bootstrapSlots();
|
|
83
|
+
installSwapReinit();
|
|
84
|
+
`.trim();
|
|
85
|
+
}
|
|
86
|
+
const WASM_RUNTIME_SCRIPT = `
|
|
87
|
+
import { wasmUrl } from 'virtual:czap/wasm-url';
|
|
88
|
+
import { configureWasmRuntime } from '@czap/astro/runtime';
|
|
89
|
+
|
|
90
|
+
configureWasmRuntime(wasmUrl);
|
|
91
|
+
`.trim();
|
|
92
|
+
/**
|
|
93
|
+
* Build an `updateConfig` payload that toggles a single experimental flag
|
|
94
|
+
* not yet present in Astro's declared `experimental` shape.
|
|
95
|
+
*
|
|
96
|
+
* The `experimental` field on `AstroConfig` is strictly keyed, so adding
|
|
97
|
+
* an unknown flag requires a widening bridge. Containing that bridge
|
|
98
|
+
* here keeps the cast off individual call sites.
|
|
99
|
+
*/
|
|
100
|
+
function withExperimentalFlag(flag, value) {
|
|
101
|
+
return { experimental: { [flag]: value } };
|
|
102
|
+
}
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
// Integration
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
/**
|
|
107
|
+
* Build the czap `AstroIntegration`.
|
|
108
|
+
*
|
|
109
|
+
* Plug the returned object into `astro.config.mjs`'s `integrations`
|
|
110
|
+
* array. The integration wires Astro's `astro:config:setup`,
|
|
111
|
+
* `astro:config:done`, `astro:server:setup`, and `astro:build:done`
|
|
112
|
+
* hooks.
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* ```ts
|
|
116
|
+
* // astro.config.mjs
|
|
117
|
+
* import { integration as czap } from '@czap/astro';
|
|
118
|
+
*
|
|
119
|
+
* const config = defineConfig({
|
|
120
|
+
* integrations: [czap({ detect: true, workers: { enabled: true } })],
|
|
121
|
+
* });
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
124
|
+
export function integration(config) {
|
|
125
|
+
const detectEnabled = config?.detect !== false;
|
|
126
|
+
const serverIslandsEnabled = config?.serverIslands === true;
|
|
127
|
+
const workersEnabled = config?.workers?.enabled === true;
|
|
128
|
+
const gpuEnabled = config?.gpu?.enabled !== false;
|
|
129
|
+
const streamEnabled = config?.stream?.enabled !== false;
|
|
130
|
+
const llmEnabled = config?.llm?.enabled !== false;
|
|
131
|
+
const wasmEnabled = config?.wasm?.enabled === true;
|
|
132
|
+
const runtimePolicy = normalizeRuntimeSecurityPolicy({
|
|
133
|
+
endpointPolicy: config?.security?.endpointPolicy,
|
|
134
|
+
htmlPolicy: config?.security?.htmlPolicy,
|
|
135
|
+
});
|
|
136
|
+
return {
|
|
137
|
+
name: '@czap/astro',
|
|
138
|
+
hooks: {
|
|
139
|
+
'astro:config:setup': ({ updateConfig, addClientDirective, injectScript, logger }) => {
|
|
140
|
+
logger.info('Setting up @czap integration');
|
|
141
|
+
// Astro may carry a different Vite type graph than @czap/vite. The plugin
|
|
142
|
+
// runtime contract is still compatible, so the host integration owns the
|
|
143
|
+
// version bridge here instead of leaking duplicate plugin shapes downstream.
|
|
144
|
+
const astroViteConfig = {
|
|
145
|
+
plugins: [
|
|
146
|
+
plugin({
|
|
147
|
+
...(config?.vite ?? {}),
|
|
148
|
+
...(wasmEnabled ? { wasm: { enabled: true, path: config?.wasm?.path } } : {}),
|
|
149
|
+
}),
|
|
150
|
+
],
|
|
151
|
+
};
|
|
152
|
+
updateConfig({
|
|
153
|
+
vite: astroViteConfig,
|
|
154
|
+
});
|
|
155
|
+
// Register client directives
|
|
156
|
+
addClientDirective({
|
|
157
|
+
name: 'satellite',
|
|
158
|
+
entrypoint: '@czap/astro/client-directives/satellite',
|
|
159
|
+
});
|
|
160
|
+
logger.info('Registered satellite client directive');
|
|
161
|
+
if (streamEnabled) {
|
|
162
|
+
addClientDirective({
|
|
163
|
+
name: 'stream',
|
|
164
|
+
entrypoint: '@czap/astro/client-directives/stream',
|
|
165
|
+
});
|
|
166
|
+
logger.info('Registered stream client directive');
|
|
167
|
+
}
|
|
168
|
+
if (llmEnabled) {
|
|
169
|
+
addClientDirective({
|
|
170
|
+
name: 'llm',
|
|
171
|
+
entrypoint: '@czap/astro/client-directives/llm',
|
|
172
|
+
});
|
|
173
|
+
logger.info('Registered llm client directive');
|
|
174
|
+
}
|
|
175
|
+
if (workersEnabled) {
|
|
176
|
+
addClientDirective({
|
|
177
|
+
name: 'worker',
|
|
178
|
+
entrypoint: '@czap/astro/client-directives/worker',
|
|
179
|
+
});
|
|
180
|
+
logger.info('Registered worker client directive');
|
|
181
|
+
}
|
|
182
|
+
if (gpuEnabled) {
|
|
183
|
+
addClientDirective({
|
|
184
|
+
name: 'gpu',
|
|
185
|
+
entrypoint: '@czap/astro/client-directives/gpu',
|
|
186
|
+
});
|
|
187
|
+
logger.info('Registered gpu client directive');
|
|
188
|
+
}
|
|
189
|
+
if (wasmEnabled) {
|
|
190
|
+
addClientDirective({
|
|
191
|
+
name: 'wasm',
|
|
192
|
+
entrypoint: '@czap/astro/client-directives/wasm',
|
|
193
|
+
});
|
|
194
|
+
logger.info('Registered wasm client directive');
|
|
195
|
+
}
|
|
196
|
+
// Inject detect script for client-side capability detection
|
|
197
|
+
if (detectEnabled) {
|
|
198
|
+
injectScript('head-inline', DETECT_INLINE_SCRIPT);
|
|
199
|
+
logger.info('Injected detect script');
|
|
200
|
+
// Inject GPU probe upgrade (deferred, non-blocking)
|
|
201
|
+
if (gpuEnabled) {
|
|
202
|
+
injectScript('page', DETECT_UPGRADE_SCRIPT);
|
|
203
|
+
logger.info('Injected GPU probe upgrade');
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
injectScript('page', runtimeBootstrapScript(runtimePolicy));
|
|
207
|
+
if (wasmEnabled) {
|
|
208
|
+
injectScript('page', WASM_RUNTIME_SCRIPT);
|
|
209
|
+
logger.info('Injected wasm runtime bootstrap');
|
|
210
|
+
}
|
|
211
|
+
// Configure server islands if enabled.
|
|
212
|
+
// `serverIslands` is not in Astro 6's declared experimental keys yet;
|
|
213
|
+
// enablement is delegated via a named bridge so only the one call site
|
|
214
|
+
// opts out of the declared-config shape.
|
|
215
|
+
if (serverIslandsEnabled) {
|
|
216
|
+
updateConfig(withExperimentalFlag('serverIslands', true));
|
|
217
|
+
logger.info('Enabled server islands');
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
'astro:config:done': ({ config: astroConfig, logger }) => {
|
|
221
|
+
logger.info(`@czap configured for ${astroConfig.output} output`);
|
|
222
|
+
},
|
|
223
|
+
'astro:server:setup': ({ server, logger }) => {
|
|
224
|
+
logger.info('@czap dev server middleware active');
|
|
225
|
+
if (detectEnabled || workersEnabled) {
|
|
226
|
+
server.middlewares.use((_req, res, next) => {
|
|
227
|
+
for (const [header, value] of getCzapHeaderEntries({ detectEnabled, workersEnabled })) {
|
|
228
|
+
res.setHeader(header, value);
|
|
229
|
+
}
|
|
230
|
+
next();
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
'astro:build:done': ({ logger }) => {
|
|
235
|
+
logger.info('@czap build integration complete');
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
//# sourceMappingURL=integration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integration.js","sourceRoot":"","sources":["../src/integration.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Edge middleware -- Client Hints parsing, tier detection, response headers.
|
|
3
|
+
*
|
|
4
|
+
* Framework-agnostic handler compatible with Astro middleware,
|
|
5
|
+
* Cloudflare Workers, and Express/Vite dev server.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import type { CompiledOutputs, EdgeHostAdapterConfig, ThemeCompileResult } from '@czap/edge';
|
|
10
|
+
import type { ExtendedDeviceCapabilities } from '@czap/detect';
|
|
11
|
+
/**
|
|
12
|
+
* Shape of `context.locals.czap` injected by {@link czapMiddleware}.
|
|
13
|
+
* Astro components (and downstream middleware) read this to drive
|
|
14
|
+
* adaptive rendering decisions.
|
|
15
|
+
*/
|
|
16
|
+
export interface CzapLocals {
|
|
17
|
+
/** Resolved tiers (capability, motion, design). */
|
|
18
|
+
readonly tier: {
|
|
19
|
+
readonly cap: string;
|
|
20
|
+
readonly motion: string;
|
|
21
|
+
readonly design: string;
|
|
22
|
+
};
|
|
23
|
+
/** Parsed device capabilities. */
|
|
24
|
+
readonly capabilities: ExtendedDeviceCapabilities;
|
|
25
|
+
/** Edge-host resolution result, present when an edge adapter is configured. */
|
|
26
|
+
readonly edge?: {
|
|
27
|
+
readonly theme?: ThemeCompileResult;
|
|
28
|
+
readonly compiledOutputs?: CompiledOutputs;
|
|
29
|
+
readonly htmlAttributes: string;
|
|
30
|
+
readonly cacheStatus: 'disabled' | 'hit' | 'miss';
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Options accepted by {@link czapMiddleware}.
|
|
35
|
+
*
|
|
36
|
+
* Omit `edge` to run in pure Client-Hints mode. Pass `edge` when you
|
|
37
|
+
* have an `@czap/edge` host adapter (KV cache, theme compilation).
|
|
38
|
+
*/
|
|
39
|
+
export interface CzapMiddlewareConfig {
|
|
40
|
+
/** Edge host adapter configuration (KV cache, theme compilation). */
|
|
41
|
+
readonly edge?: EdgeHostAdapterConfig;
|
|
42
|
+
/** Whether to include the Client Hints request headers (default `true`). */
|
|
43
|
+
readonly detect?: boolean;
|
|
44
|
+
/** Whether to emit COOP/COEP headers for worker features. */
|
|
45
|
+
readonly workers?: {
|
|
46
|
+
readonly enabled?: boolean;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
interface MiddlewareContext {
|
|
50
|
+
readonly request: Request;
|
|
51
|
+
locals: Record<string, unknown>;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Create the czap edge middleware.
|
|
55
|
+
*
|
|
56
|
+
* Parses Client Hints from request headers, computes tier detection,
|
|
57
|
+
* injects results into `context.locals.czap`, and sets Client Hints
|
|
58
|
+
* response headers (`Accept-CH`, `Critical-CH`).
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```ts
|
|
62
|
+
* // Astro middleware (src/middleware.ts)
|
|
63
|
+
* import { czapMiddleware } from '@czap/astro';
|
|
64
|
+
* export const onRequest = czapMiddleware();
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export declare function czapMiddleware(config?: CzapMiddlewareConfig): (context: MiddlewareContext, next: () => Promise<Response>) => Promise<Response>;
|
|
68
|
+
export {};
|
|
69
|
+
//# sourceMappingURL=middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAC7F,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAC;AAO/D;;;;GAIG;AACH,MAAM,WAAW,UAAU;IACzB,mDAAmD;IACnD,QAAQ,CAAC,IAAI,EAAE;QACb,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;KACzB,CAAC;IACF,kCAAkC;IAClC,QAAQ,CAAC,YAAY,EAAE,0BAA0B,CAAC;IAClD,+EAA+E;IAC/E,QAAQ,CAAC,IAAI,CAAC,EAAE;QACd,QAAQ,CAAC,KAAK,CAAC,EAAE,kBAAkB,CAAC;QACpC,QAAQ,CAAC,eAAe,CAAC,EAAE,eAAe,CAAC;QAC3C,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;QAChC,QAAQ,CAAC,WAAW,EAAE,UAAU,GAAG,KAAK,GAAG,MAAM,CAAC;KACnD,CAAC;CACH;AAED;;;;;GAKG;AACH,MAAM,WAAW,oBAAoB;IACnC,qEAAqE;IACrE,QAAQ,CAAC,IAAI,CAAC,EAAE,qBAAqB,CAAC;IACtC,4EAA4E;IAC5E,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,6DAA6D;IAC7D,QAAQ,CAAC,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CACnD;AAED,UAAU,iBAAiB;IACzB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAMD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAC5B,MAAM,CAAC,EAAE,oBAAoB,GAC5B,CAAC,OAAO,EAAE,iBAAiB,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,QAAQ,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,CAmDlF"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Edge middleware -- Client Hints parsing, tier detection, response headers.
|
|
3
|
+
*
|
|
4
|
+
* Framework-agnostic handler compatible with Astro middleware,
|
|
5
|
+
* Cloudflare Workers, and Express/Vite dev server.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import { ClientHints, createEdgeHostAdapter, EdgeTier } from '@czap/edge';
|
|
10
|
+
import { applyCzapHeaders } from './headers.js';
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
// Middleware
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
/**
|
|
15
|
+
* Create the czap edge middleware.
|
|
16
|
+
*
|
|
17
|
+
* Parses Client Hints from request headers, computes tier detection,
|
|
18
|
+
* injects results into `context.locals.czap`, and sets Client Hints
|
|
19
|
+
* response headers (`Accept-CH`, `Critical-CH`).
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* // Astro middleware (src/middleware.ts)
|
|
24
|
+
* import { czapMiddleware } from '@czap/astro';
|
|
25
|
+
* export const onRequest = czapMiddleware();
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export function czapMiddleware(config) {
|
|
29
|
+
const edgeConfig = config?.edge;
|
|
30
|
+
let edgeAdapter = null;
|
|
31
|
+
if (edgeConfig) {
|
|
32
|
+
edgeAdapter = createEdgeHostAdapter(edgeConfig);
|
|
33
|
+
}
|
|
34
|
+
const detectEnabled = config?.detect !== false;
|
|
35
|
+
const workersEnabled = config?.workers?.enabled === true;
|
|
36
|
+
return async (context, next) => {
|
|
37
|
+
const edgeResolution = edgeAdapter ? await edgeAdapter.resolve(context.request.headers) : null;
|
|
38
|
+
const capabilities = edgeResolution?.capabilities ?? ClientHints.parseClientHints(context.request.headers);
|
|
39
|
+
const tier = edgeResolution?.tier ?? EdgeTier.detectTier(context.request.headers);
|
|
40
|
+
// Inject into locals for component access
|
|
41
|
+
context.locals.czap = {
|
|
42
|
+
tier: {
|
|
43
|
+
cap: tier.capLevel,
|
|
44
|
+
motion: tier.motionTier,
|
|
45
|
+
design: tier.designTier,
|
|
46
|
+
},
|
|
47
|
+
capabilities,
|
|
48
|
+
...(edgeResolution
|
|
49
|
+
? {
|
|
50
|
+
edge: {
|
|
51
|
+
theme: edgeResolution.theme,
|
|
52
|
+
compiledOutputs: edgeResolution.compiledOutputs,
|
|
53
|
+
htmlAttributes: edgeResolution.htmlAttributes,
|
|
54
|
+
cacheStatus: edgeResolution.cacheStatus,
|
|
55
|
+
},
|
|
56
|
+
}
|
|
57
|
+
: {}),
|
|
58
|
+
};
|
|
59
|
+
// Continue to the route handler
|
|
60
|
+
const response = await next();
|
|
61
|
+
// Add Client Hints request headers to the response
|
|
62
|
+
const headers = applyCzapHeaders(new Headers(response.headers), {
|
|
63
|
+
detectEnabled,
|
|
64
|
+
workersEnabled,
|
|
65
|
+
acceptCH: edgeResolution?.responseHeaders.acceptCH ?? ClientHints.acceptCHHeader(),
|
|
66
|
+
criticalCH: edgeResolution?.responseHeaders.criticalCH ?? ClientHints.criticalCHHeader(),
|
|
67
|
+
});
|
|
68
|
+
return new Response(response.body, {
|
|
69
|
+
status: response.status,
|
|
70
|
+
statusText: response.statusText,
|
|
71
|
+
headers,
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=middleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,WAAW,EAAE,qBAAqB,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAG1E,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAiDhD,8EAA8E;AAC9E,aAAa;AACb,8EAA8E;AAE9E;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,cAAc,CAC5B,MAA6B;IAE7B,MAAM,UAAU,GAAG,MAAM,EAAE,IAAI,CAAC;IAChC,IAAI,WAAW,GAAoD,IAAI,CAAC;IACxE,IAAI,UAAU,EAAE,CAAC;QACf,WAAW,GAAG,qBAAqB,CAAC,UAAU,CAAC,CAAC;IAClD,CAAC;IACD,MAAM,aAAa,GAAG,MAAM,EAAE,MAAM,KAAK,KAAK,CAAC;IAC/C,MAAM,cAAc,GAAG,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAEzD,OAAO,KAAK,EAAE,OAA0B,EAAE,IAA6B,EAAqB,EAAE;QAC5F,MAAM,cAAc,GAAG,WAAW,CAAC,CAAC,CAAC,MAAM,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC/F,MAAM,YAAY,GAAG,cAAc,EAAE,YAAY,IAAI,WAAW,CAAC,gBAAgB,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC3G,MAAM,IAAI,GAAG,cAAc,EAAE,IAAI,IAAI,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAElF,0CAA0C;QAC1C,OAAO,CAAC,MAAM,CAAC,IAAI,GAAG;YACpB,IAAI,EAAE;gBACJ,GAAG,EAAE,IAAI,CAAC,QAAQ;gBAClB,MAAM,EAAE,IAAI,CAAC,UAAU;gBACvB,MAAM,EAAE,IAAI,CAAC,UAAU;aACxB;YACD,YAAY;YACZ,GAAG,CAAC,cAAc;gBAChB,CAAC,CAAC;oBACE,IAAI,EAAE;wBACJ,KAAK,EAAE,cAAc,CAAC,KAAK;wBAC3B,eAAe,EAAE,cAAc,CAAC,eAAe;wBAC/C,cAAc,EAAE,cAAc,CAAC,cAAc;wBAC7C,WAAW,EAAE,cAAc,CAAC,WAAW;qBACxC;iBACF;gBACH,CAAC,CAAC,EAAE,CAAC;SACa,CAAC;QAEvB,gCAAgC;QAChC,MAAM,QAAQ,GAAG,MAAM,IAAI,EAAE,CAAC;QAE9B,mDAAmD;QACnD,MAAM,OAAO,GAAG,gBAAgB,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE;YAC9D,aAAa;YACb,cAAc;YACd,QAAQ,EAAE,cAAc,EAAE,eAAe,CAAC,QAAQ,IAAI,WAAW,CAAC,cAAc,EAAE;YAClF,UAAU,EAAE,cAAc,EAAE,eAAe,CAAC,UAAU,IAAI,WAAW,CAAC,gBAAgB,EAAE;SACzF,CAAC,CAAC;QAEH,OAAO,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE;YACjC,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,UAAU,EAAE,QAAQ,CAAC,UAAU;YAC/B,OAAO;SACR,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Quantize component helpers -- server-side initial state resolution.
|
|
3
|
+
*
|
|
4
|
+
* Maps {@link ServerIslandContext} (user agent, client hints, detected
|
|
5
|
+
* tier) to the best initial boundary state for SSR and server islands.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import type { Boundary, CapLevel, Quantizer } from '@czap/core';
|
|
10
|
+
/**
|
|
11
|
+
* Server-only context that {@link resolveInitialState} consumes. Astro
|
|
12
|
+
* builds this from the incoming request (user agent + Client Hints)
|
|
13
|
+
* and the tier detected by the edge middleware.
|
|
14
|
+
*/
|
|
15
|
+
export interface ServerIslandContext {
|
|
16
|
+
/** Raw `User-Agent` header. */
|
|
17
|
+
readonly userAgent: string;
|
|
18
|
+
/** Flat Client Hints header map. */
|
|
19
|
+
readonly clientHints: Record<string, string>;
|
|
20
|
+
/** Tier detected by `@czap/edge`. */
|
|
21
|
+
readonly detectedTier: CapLevel;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Props accepted by the `Quantize` Astro component and by
|
|
25
|
+
* {@link resolveInitialState}.
|
|
26
|
+
*/
|
|
27
|
+
export interface QuantizeProps<B extends Boundary.Shape = Boundary.Shape> {
|
|
28
|
+
/** Boundary to quantize. */
|
|
29
|
+
readonly boundary: B;
|
|
30
|
+
/** Optional explicit quantizer definition. */
|
|
31
|
+
readonly quantizer?: Quantizer<B>;
|
|
32
|
+
/** Explicit initial state (skips resolution). */
|
|
33
|
+
readonly initialState?: string;
|
|
34
|
+
/** Final fallback if resolution fails. */
|
|
35
|
+
readonly fallback?: string;
|
|
36
|
+
/** Extra CSS class names. */
|
|
37
|
+
readonly class?: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Resolve the initial boundary state for server-side rendering.
|
|
41
|
+
*
|
|
42
|
+
* Priority:
|
|
43
|
+
* 1. Use viewport width from client hints if available
|
|
44
|
+
* 2. Estimate viewport from user agent
|
|
45
|
+
* 3. Fall back to tier-based synthetic value
|
|
46
|
+
*
|
|
47
|
+
* Evaluates the boundary thresholds to find the matching state.
|
|
48
|
+
*/
|
|
49
|
+
export declare function resolveInitialState<B extends Boundary.Shape>(boundary: B, context: ServerIslandContext): string;
|
|
50
|
+
//# sourceMappingURL=quantize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quantize.d.ts","sourceRoot":"","sources":["../src/quantize.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAOhE;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,+BAA+B;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,oCAAoC;IACpC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7C,qCAAqC;IACrC,QAAQ,CAAC,YAAY,EAAE,QAAQ,CAAC;CACjC;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC,SAAS,QAAQ,CAAC,KAAK,GAAG,QAAQ,CAAC,KAAK;IACtE,4BAA4B;IAC5B,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;IACrB,8CAA8C;IAC9C,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;IAClC,iDAAiD;IACjD,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,0CAA0C;IAC1C,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,6BAA6B;IAC7B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AA6ED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,QAAQ,CAAC,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,mBAAmB,GAAG,MAAM,CAwC/G"}
|
package/dist/quantize.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Quantize component helpers -- server-side initial state resolution.
|
|
3
|
+
*
|
|
4
|
+
* Maps {@link ServerIslandContext} (user agent, client hints, detected
|
|
5
|
+
* tier) to the best initial boundary state for SSR and server islands.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import { VIEWPORT } from '@czap/core';
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Client Hint Parsing
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
/**
|
|
14
|
+
* Parse a viewport width from client hints.
|
|
15
|
+
* Supports Sec-CH-Viewport-Width and Sec-CH-Width headers.
|
|
16
|
+
*/
|
|
17
|
+
function parseViewportWidth(clientHints) {
|
|
18
|
+
const raw = clientHints['sec-ch-viewport-width'] ??
|
|
19
|
+
clientHints['Sec-CH-Viewport-Width'] ??
|
|
20
|
+
clientHints['sec-ch-width'] ??
|
|
21
|
+
clientHints['Sec-CH-Width'];
|
|
22
|
+
if (raw === undefined)
|
|
23
|
+
return undefined;
|
|
24
|
+
const parsed = parseInt(raw, 10);
|
|
25
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Parse prefers-reduced-motion from client hints.
|
|
29
|
+
*/
|
|
30
|
+
function parsePrefersReducedMotion(clientHints) {
|
|
31
|
+
const raw = clientHints['sec-ch-prefers-reduced-motion'] ?? clientHints['Sec-CH-Prefers-Reduced-Motion'];
|
|
32
|
+
if (raw === undefined)
|
|
33
|
+
return undefined;
|
|
34
|
+
return raw === 'reduce';
|
|
35
|
+
}
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
// User Agent Heuristics
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
/**
|
|
40
|
+
* Estimate a viewport width from user agent string for common device classes.
|
|
41
|
+
*/
|
|
42
|
+
function estimateViewportFromUA(ua) {
|
|
43
|
+
const lower = ua.toLowerCase();
|
|
44
|
+
if (lower.includes('mobile') || lower.includes('android') || lower.includes('iphone')) {
|
|
45
|
+
return VIEWPORT.mobile;
|
|
46
|
+
}
|
|
47
|
+
if (lower.includes('tablet') || lower.includes('ipad')) {
|
|
48
|
+
return VIEWPORT.tablet;
|
|
49
|
+
}
|
|
50
|
+
return VIEWPORT.desktop;
|
|
51
|
+
}
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
// Tier-Based Heuristic
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
const TIER_ORDINALS = {
|
|
56
|
+
static: 0,
|
|
57
|
+
styled: 1,
|
|
58
|
+
reactive: 2,
|
|
59
|
+
animated: 3,
|
|
60
|
+
gpu: 4,
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Map a CapLevel tier to a synthetic viewport-like value for boundary evaluation.
|
|
64
|
+
* This bridges between the capability tier system and viewport-based boundaries.
|
|
65
|
+
*/
|
|
66
|
+
function syntheticValueFromTier(tier) {
|
|
67
|
+
const ord = TIER_ORDINALS[tier];
|
|
68
|
+
// Map tier ordinal to viewport-like breakpoints: 320, 640, 960, 1280, 1920
|
|
69
|
+
return 320 + ord * 320;
|
|
70
|
+
}
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
// Public API
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
/**
|
|
75
|
+
* Resolve the initial boundary state for server-side rendering.
|
|
76
|
+
*
|
|
77
|
+
* Priority:
|
|
78
|
+
* 1. Use viewport width from client hints if available
|
|
79
|
+
* 2. Estimate viewport from user agent
|
|
80
|
+
* 3. Fall back to tier-based synthetic value
|
|
81
|
+
*
|
|
82
|
+
* Evaluates the boundary thresholds to find the matching state.
|
|
83
|
+
*/
|
|
84
|
+
export function resolveInitialState(boundary, context) {
|
|
85
|
+
const stateNames = boundary.states;
|
|
86
|
+
const thresholds = boundary.thresholds;
|
|
87
|
+
if (stateNames.length === 0)
|
|
88
|
+
return '';
|
|
89
|
+
if (stateNames.length === 1)
|
|
90
|
+
return stateNames[0];
|
|
91
|
+
// Determine the signal value to evaluate against the boundary
|
|
92
|
+
let value;
|
|
93
|
+
// Check client hints first (most accurate)
|
|
94
|
+
const hintWidth = parseViewportWidth(context.clientHints);
|
|
95
|
+
const reducedMotion = parsePrefersReducedMotion(context.clientHints);
|
|
96
|
+
if (hintWidth !== undefined) {
|
|
97
|
+
value = hintWidth;
|
|
98
|
+
}
|
|
99
|
+
else if (context.userAgent) {
|
|
100
|
+
value = estimateViewportFromUA(context.userAgent);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
value = syntheticValueFromTier(context.detectedTier);
|
|
104
|
+
}
|
|
105
|
+
// If reduced motion is detected and the tier suggests limited capability,
|
|
106
|
+
// bias toward the lowest state
|
|
107
|
+
if (reducedMotion === true && TIER_ORDINALS[context.detectedTier] <= 1) {
|
|
108
|
+
return stateNames[0];
|
|
109
|
+
}
|
|
110
|
+
// Evaluate against boundary thresholds to find the matching state.
|
|
111
|
+
// thresholds[i] is the lower bound for state[i].
|
|
112
|
+
// Walk backwards from the highest threshold to find the first match.
|
|
113
|
+
for (let i = stateNames.length - 1; i >= 0; i--) {
|
|
114
|
+
const threshold = thresholds[i];
|
|
115
|
+
if (threshold !== undefined && value >= threshold) {
|
|
116
|
+
return stateNames[i];
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// Fallback to first state
|
|
120
|
+
return stateNames[0];
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=quantize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quantize.js","sourceRoot":"","sources":["../src/quantize.ts"],"names":[],"mappings":"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"}
|