@flightdev/core 0.6.7
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 +541 -0
- package/dist/actions/index.d.ts +743 -0
- package/dist/actions/index.js +3 -0
- package/dist/actions/index.js.map +1 -0
- package/dist/adapters/index.d.ts +502 -0
- package/dist/adapters/index.js +3 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/cache/index.d.ts +191 -0
- package/dist/cache/index.js +3 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/chunk-62HISNA3.js +354 -0
- package/dist/chunk-62HISNA3.js.map +1 -0
- package/dist/chunk-63LWTEDQ.js +341 -0
- package/dist/chunk-63LWTEDQ.js.map +1 -0
- package/dist/chunk-63SCEXD7.js +3 -0
- package/dist/chunk-63SCEXD7.js.map +1 -0
- package/dist/chunk-72MYOTUB.js +667 -0
- package/dist/chunk-72MYOTUB.js.map +1 -0
- package/dist/chunk-7CNW24MQ.js +257 -0
- package/dist/chunk-7CNW24MQ.js.map +1 -0
- package/dist/chunk-7WIEAUJT.js +300 -0
- package/dist/chunk-7WIEAUJT.js.map +1 -0
- package/dist/chunk-7ZZF4ULK.js +259 -0
- package/dist/chunk-7ZZF4ULK.js.map +1 -0
- package/dist/chunk-AE3JTS73.js +222 -0
- package/dist/chunk-AE3JTS73.js.map +1 -0
- package/dist/chunk-AP5NLUSB.js +258 -0
- package/dist/chunk-AP5NLUSB.js.map +1 -0
- package/dist/chunk-C37YQQI7.js +221 -0
- package/dist/chunk-C37YQQI7.js.map +1 -0
- package/dist/chunk-DCLVXFVH.js +225 -0
- package/dist/chunk-DCLVXFVH.js.map +1 -0
- package/dist/chunk-DZMWWDFD.js +223 -0
- package/dist/chunk-DZMWWDFD.js.map +1 -0
- package/dist/chunk-GCQZ4FHI.js +245 -0
- package/dist/chunk-GCQZ4FHI.js.map +1 -0
- package/dist/chunk-IPP44XY6.js +47 -0
- package/dist/chunk-IPP44XY6.js.map +1 -0
- package/dist/chunk-IW7FTQQX.js +267 -0
- package/dist/chunk-IW7FTQQX.js.map +1 -0
- package/dist/chunk-JX4YSCBH.js +428 -0
- package/dist/chunk-JX4YSCBH.js.map +1 -0
- package/dist/chunk-KX6UYWWR.js +229 -0
- package/dist/chunk-KX6UYWWR.js.map +1 -0
- package/dist/chunk-LWVETFJV.js +46 -0
- package/dist/chunk-LWVETFJV.js.map +1 -0
- package/dist/chunk-MCL2MCA2.js +285 -0
- package/dist/chunk-MCL2MCA2.js.map +1 -0
- package/dist/chunk-MZXCF35B.js +205 -0
- package/dist/chunk-MZXCF35B.js.map +1 -0
- package/dist/chunk-NCGPUFWV.js +96 -0
- package/dist/chunk-NCGPUFWV.js.map +1 -0
- package/dist/chunk-OEJMIE2Q.js +351 -0
- package/dist/chunk-OEJMIE2Q.js.map +1 -0
- package/dist/chunk-OYF2OAKS.js +394 -0
- package/dist/chunk-OYF2OAKS.js.map +1 -0
- package/dist/chunk-P6S43FYZ.js +316 -0
- package/dist/chunk-P6S43FYZ.js.map +1 -0
- package/dist/chunk-PL37KFRJ.js +3 -0
- package/dist/chunk-PL37KFRJ.js.map +1 -0
- package/dist/chunk-Q7BS5QC5.js +197 -0
- package/dist/chunk-Q7BS5QC5.js.map +1 -0
- package/dist/chunk-SDYPG3JD.js +288 -0
- package/dist/chunk-SDYPG3JD.js.map +1 -0
- package/dist/chunk-SUG56SZO.js +256 -0
- package/dist/chunk-SUG56SZO.js.map +1 -0
- package/dist/chunk-UVH5XJRP.js +164 -0
- package/dist/chunk-UVH5XJRP.js.map +1 -0
- package/dist/chunk-WZIJKCL3.js +282 -0
- package/dist/chunk-WZIJKCL3.js.map +1 -0
- package/dist/chunk-Y22AMGTM.js +3 -0
- package/dist/chunk-Y22AMGTM.js.map +1 -0
- package/dist/chunk-Z7G23XWU.js +200 -0
- package/dist/chunk-Z7G23XWU.js.map +1 -0
- package/dist/chunk-ZJU5M4IB.js +125 -0
- package/dist/chunk-ZJU5M4IB.js.map +1 -0
- package/dist/chunk-ZVC3ZWLM.js +52 -0
- package/dist/chunk-ZVC3ZWLM.js.map +1 -0
- package/dist/chunk-ZZZML7Y3.js +310 -0
- package/dist/chunk-ZZZML7Y3.js.map +1 -0
- package/dist/client.d.ts +25 -0
- package/dist/client.js +16 -0
- package/dist/client.js.map +1 -0
- package/dist/config/index.d.ts +170 -0
- package/dist/config/index.js +3 -0
- package/dist/config/index.js.map +1 -0
- package/dist/errors/index.d.ts +267 -0
- package/dist/errors/index.js +4 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/file-router/index.d.ts +184 -0
- package/dist/file-router/index.js +3 -0
- package/dist/file-router/index.js.map +1 -0
- package/dist/file-router/streaming-hints.d.ts +129 -0
- package/dist/file-router/streaming-hints.js +3 -0
- package/dist/file-router/streaming-hints.js.map +1 -0
- package/dist/handlers/index.d.ts +59 -0
- package/dist/handlers/index.js +3 -0
- package/dist/handlers/index.js.map +1 -0
- package/dist/index.d.ts +588 -0
- package/dist/index.js +886 -0
- package/dist/index.js.map +1 -0
- package/dist/islands/index.d.ts +234 -0
- package/dist/islands/index.js +3 -0
- package/dist/islands/index.js.map +1 -0
- package/dist/middleware/index.d.ts +305 -0
- package/dist/middleware/index.js +3 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/react/index.d.ts +73 -0
- package/dist/react/index.js +52 -0
- package/dist/react/index.js.map +1 -0
- package/dist/render/index.d.ts +131 -0
- package/dist/render/index.js +3 -0
- package/dist/render/index.js.map +1 -0
- package/dist/router/index.d.ts +65 -0
- package/dist/router/index.js +3 -0
- package/dist/router/index.js.map +1 -0
- package/dist/rsc/adapters/index.d.ts +8 -0
- package/dist/rsc/adapters/index.js +7 -0
- package/dist/rsc/adapters/index.js.map +1 -0
- package/dist/rsc/adapters/preact.d.ts +97 -0
- package/dist/rsc/adapters/preact.js +3 -0
- package/dist/rsc/adapters/preact.js.map +1 -0
- package/dist/rsc/adapters/react.d.ts +82 -0
- package/dist/rsc/adapters/react.js +3 -0
- package/dist/rsc/adapters/react.js.map +1 -0
- package/dist/rsc/adapters/solid.d.ts +84 -0
- package/dist/rsc/adapters/solid.js +3 -0
- package/dist/rsc/adapters/solid.js.map +1 -0
- package/dist/rsc/adapters/vue.d.ts +80 -0
- package/dist/rsc/adapters/vue.js +3 -0
- package/dist/rsc/adapters/vue.js.map +1 -0
- package/dist/rsc/boundaries.d.ts +182 -0
- package/dist/rsc/boundaries.js +3 -0
- package/dist/rsc/boundaries.js.map +1 -0
- package/dist/rsc/context.d.ts +201 -0
- package/dist/rsc/context.js +3 -0
- package/dist/rsc/context.js.map +1 -0
- package/dist/rsc/index.d.ts +232 -0
- package/dist/rsc/index.js +15 -0
- package/dist/rsc/index.js.map +1 -0
- package/dist/rsc/legacy.d.ts +155 -0
- package/dist/rsc/legacy.js +3 -0
- package/dist/rsc/legacy.js.map +1 -0
- package/dist/rsc/payload.d.ts +262 -0
- package/dist/rsc/payload.js +3 -0
- package/dist/rsc/payload.js.map +1 -0
- package/dist/rsc/plugins/esbuild.d.ts +124 -0
- package/dist/rsc/plugins/esbuild.js +4 -0
- package/dist/rsc/plugins/esbuild.js.map +1 -0
- package/dist/rsc/plugins/index.d.ts +4 -0
- package/dist/rsc/plugins/index.js +6 -0
- package/dist/rsc/plugins/index.js.map +1 -0
- package/dist/rsc/plugins/rollup.d.ts +103 -0
- package/dist/rsc/plugins/rollup.js +4 -0
- package/dist/rsc/plugins/rollup.js.map +1 -0
- package/dist/rsc/renderer.d.ts +162 -0
- package/dist/rsc/renderer.js +5 -0
- package/dist/rsc/renderer.js.map +1 -0
- package/dist/rsc/stream.d.ts +129 -0
- package/dist/rsc/stream.js +3 -0
- package/dist/rsc/stream.js.map +1 -0
- package/dist/rsc/vite-plugin.d.ts +78 -0
- package/dist/rsc/vite-plugin.js +4 -0
- package/dist/rsc/vite-plugin.js.map +1 -0
- package/dist/server/index.d.ts +135 -0
- package/dist/server/index.js +6 -0
- package/dist/server/index.js.map +1 -0
- package/dist/streaming/adapters/index.d.ts +223 -0
- package/dist/streaming/adapters/index.js +3 -0
- package/dist/streaming/adapters/index.js.map +1 -0
- package/dist/streaming/conditional.d.ts +130 -0
- package/dist/streaming/conditional.js +3 -0
- package/dist/streaming/conditional.js.map +1 -0
- package/dist/streaming/index.d.ts +177 -0
- package/dist/streaming/index.js +3 -0
- package/dist/streaming/index.js.map +1 -0
- package/dist/streaming/observability.d.ts +201 -0
- package/dist/streaming/observability.js +4 -0
- package/dist/streaming/observability.js.map +1 -0
- package/dist/streaming/priority.d.ts +103 -0
- package/dist/streaming/priority.js +3 -0
- package/dist/streaming/priority.js.map +1 -0
- package/dist/utils/index.d.ts +42 -0
- package/dist/utils/index.js +4 -0
- package/dist/utils/index.js.map +1 -0
- package/package.json +228 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
// src/rsc/legacy.ts
|
|
2
|
+
async function executeServerComponent(component, props, context) {
|
|
3
|
+
try {
|
|
4
|
+
const result = await component(props, context);
|
|
5
|
+
return result;
|
|
6
|
+
} catch (error) {
|
|
7
|
+
console.error("[Flight] Server component error:", error);
|
|
8
|
+
throw error;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
function createRenderContext(request, params = {}) {
|
|
12
|
+
const url = new URL(request.url);
|
|
13
|
+
const cookies = /* @__PURE__ */ new Map();
|
|
14
|
+
const cookieHeader = request.headers.get("cookie") || "";
|
|
15
|
+
cookieHeader.split(";").forEach((cookie) => {
|
|
16
|
+
const [key, value] = cookie.trim().split("=");
|
|
17
|
+
if (key && value) cookies.set(key, value);
|
|
18
|
+
});
|
|
19
|
+
return {
|
|
20
|
+
request,
|
|
21
|
+
params,
|
|
22
|
+
searchParams: url.searchParams,
|
|
23
|
+
headers: request.headers,
|
|
24
|
+
cookies
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
var fetchCache = /* @__PURE__ */ new Map();
|
|
28
|
+
async function serverFetch(url, options = {}) {
|
|
29
|
+
const { revalidate = 60, ...fetchOptions } = options;
|
|
30
|
+
const cacheKey = `${url}:${JSON.stringify(fetchOptions)}`;
|
|
31
|
+
const cached = fetchCache.get(cacheKey);
|
|
32
|
+
if (cached) {
|
|
33
|
+
const age = Date.now() - cached.timestamp;
|
|
34
|
+
if (revalidate === false || age < revalidate * 1e3) {
|
|
35
|
+
return cached.data;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const response = await fetch(url, fetchOptions);
|
|
39
|
+
if (!response.ok) {
|
|
40
|
+
throw new Error(`Fetch failed: ${response.status} ${response.statusText}`);
|
|
41
|
+
}
|
|
42
|
+
const data = await response.json();
|
|
43
|
+
if (revalidate !== false) {
|
|
44
|
+
fetchCache.set(cacheKey, {
|
|
45
|
+
data,
|
|
46
|
+
timestamp: Date.now()
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
return data;
|
|
50
|
+
}
|
|
51
|
+
function revalidateTag(tag) {
|
|
52
|
+
console.log(`[Flight] Revalidating tag: ${tag}`);
|
|
53
|
+
}
|
|
54
|
+
function revalidatePath(path) {
|
|
55
|
+
console.log(`[Flight] Revalidating path: ${path}`);
|
|
56
|
+
}
|
|
57
|
+
function serializeProps(props) {
|
|
58
|
+
function preProcess(value) {
|
|
59
|
+
if (value instanceof Date) {
|
|
60
|
+
return { __type: "Date", value: value.toISOString() };
|
|
61
|
+
}
|
|
62
|
+
if (value instanceof Map) {
|
|
63
|
+
return { __type: "Map", value: Array.from(value.entries()).map(([k, v]) => [preProcess(k), preProcess(v)]) };
|
|
64
|
+
}
|
|
65
|
+
if (value instanceof Set) {
|
|
66
|
+
return { __type: "Set", value: Array.from(value).map(preProcess) };
|
|
67
|
+
}
|
|
68
|
+
if (typeof value === "bigint") {
|
|
69
|
+
return { __type: "BigInt", value: value.toString() };
|
|
70
|
+
}
|
|
71
|
+
if (typeof value === "function") {
|
|
72
|
+
return void 0;
|
|
73
|
+
}
|
|
74
|
+
if (Array.isArray(value)) {
|
|
75
|
+
return value.map(preProcess);
|
|
76
|
+
}
|
|
77
|
+
if (value && typeof value === "object") {
|
|
78
|
+
const result = {};
|
|
79
|
+
for (const [k, v] of Object.entries(value)) {
|
|
80
|
+
result[k] = preProcess(v);
|
|
81
|
+
}
|
|
82
|
+
return result;
|
|
83
|
+
}
|
|
84
|
+
return value;
|
|
85
|
+
}
|
|
86
|
+
return JSON.stringify(preProcess(props));
|
|
87
|
+
}
|
|
88
|
+
function deserializeProps(serialized) {
|
|
89
|
+
return JSON.parse(serialized, (_key, value) => {
|
|
90
|
+
if (value && typeof value === "object" && "__type" in value) {
|
|
91
|
+
switch (value.__type) {
|
|
92
|
+
case "Date":
|
|
93
|
+
return new Date(value.value);
|
|
94
|
+
case "Map":
|
|
95
|
+
return new Map(value.value);
|
|
96
|
+
case "Set":
|
|
97
|
+
return new Set(value.value);
|
|
98
|
+
case "BigInt":
|
|
99
|
+
return BigInt(value.value);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return value;
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
function createClientBoundary(componentId, props, fallback) {
|
|
106
|
+
const serializedProps = serializeProps(props);
|
|
107
|
+
return `
|
|
108
|
+
<!--flight-client:${componentId}-->
|
|
109
|
+
<div data-flight-component="${componentId}" data-flight-props='${serializedProps.replace(/'/g, "'")}'>
|
|
110
|
+
${fallback || "<div>Loading...</div>"}
|
|
111
|
+
</div>
|
|
112
|
+
<!--/flight-client-->
|
|
113
|
+
<script type="module">
|
|
114
|
+
(async function() {
|
|
115
|
+
const component = await import('/_flight/components/${componentId}.js');
|
|
116
|
+
const props = JSON.parse('${serializedProps.replace(/'/g, "\\'")}');
|
|
117
|
+
const container = document.querySelector('[data-flight-component="${componentId}"]');
|
|
118
|
+
if (container && component.default) {
|
|
119
|
+
if (typeof component.hydrate === 'function') {
|
|
120
|
+
component.hydrate(container, props);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
})();
|
|
124
|
+
</script>`;
|
|
125
|
+
}
|
|
126
|
+
function createAsyncComponent(fetcher, renderer) {
|
|
127
|
+
const component = async (props, context) => {
|
|
128
|
+
const data = await fetcher(props, context);
|
|
129
|
+
return renderer(data, props);
|
|
130
|
+
};
|
|
131
|
+
component.__flight_server = true;
|
|
132
|
+
return component;
|
|
133
|
+
}
|
|
134
|
+
function composeComponents(...components) {
|
|
135
|
+
return async () => {
|
|
136
|
+
const results = await Promise.all(
|
|
137
|
+
components.map(async (comp) => await comp())
|
|
138
|
+
);
|
|
139
|
+
return results.join("");
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
function withAsyncErrorBoundary(component, options) {
|
|
143
|
+
const { fallback, onError, rethrowNavigation = true } = options;
|
|
144
|
+
return async (props, context) => {
|
|
145
|
+
try {
|
|
146
|
+
return await component(props, context);
|
|
147
|
+
} catch (error) {
|
|
148
|
+
if (rethrowNavigation) {
|
|
149
|
+
if (isNotFoundError(error)) {
|
|
150
|
+
throw error;
|
|
151
|
+
}
|
|
152
|
+
if (isRedirectError(error)) {
|
|
153
|
+
throw error;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (onError) {
|
|
157
|
+
try {
|
|
158
|
+
onError(error, props, context);
|
|
159
|
+
} catch (callbackError) {
|
|
160
|
+
console.error("[Flight] Error in onError callback:", callbackError);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
console.error("[Flight] Async component error:", error);
|
|
164
|
+
return fallback(error, props);
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
function withErrorBoundary(component, errorFallback) {
|
|
169
|
+
return async (props, context) => {
|
|
170
|
+
try {
|
|
171
|
+
return await component(props, context);
|
|
172
|
+
} catch (error) {
|
|
173
|
+
console.error("[Flight] Server component error:", error);
|
|
174
|
+
return errorFallback(error);
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
function notFound() {
|
|
179
|
+
const error = new Error("Not Found");
|
|
180
|
+
error.__flight_not_found = true;
|
|
181
|
+
throw error;
|
|
182
|
+
}
|
|
183
|
+
function isNotFoundError(error) {
|
|
184
|
+
return error instanceof Error && error.__flight_not_found === true;
|
|
185
|
+
}
|
|
186
|
+
function redirect(url, type = "replace") {
|
|
187
|
+
const error = new Error(`Redirect: ${url}`);
|
|
188
|
+
error.__flight_redirect = { url, type };
|
|
189
|
+
throw error;
|
|
190
|
+
}
|
|
191
|
+
function isRedirectError(error) {
|
|
192
|
+
if (error instanceof Error && error.__flight_redirect) {
|
|
193
|
+
return error.__flight_redirect;
|
|
194
|
+
}
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export { composeComponents, createAsyncComponent, createClientBoundary, createRenderContext, deserializeProps, executeServerComponent, isNotFoundError, isRedirectError, notFound, redirect, revalidatePath, revalidateTag, serializeProps, serverFetch, withAsyncErrorBoundary, withErrorBoundary };
|
|
199
|
+
//# sourceMappingURL=chunk-Z7G23XWU.js.map
|
|
200
|
+
//# sourceMappingURL=chunk-Z7G23XWU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/rsc/legacy.ts"],"names":[],"mappings":"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","file":"chunk-Z7G23XWU.js","sourcesContent":["/**\r\n * @flightdev/core - Legacy RSC Support\r\n * \r\n * Backward compatibility module for the original RSC implementation.\r\n * Use the new API from './index.js' for new features.\r\n * \r\n * @deprecated Use the new RSC API instead\r\n * @module @flightdev/core/rsc/legacy\r\n */\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\n/**\r\n * Component rendering context\r\n * @deprecated Use ServerContext from './context.js' instead\r\n */\r\nexport interface RenderContext {\r\n /** Current request */\r\n request: Request;\r\n /** Route params */\r\n params: Record<string, string>;\r\n /** Search params */\r\n searchParams: URLSearchParams;\r\n /** Request headers */\r\n headers: Headers;\r\n /** Cookies */\r\n cookies: Map<string, string>;\r\n}\r\n\r\n/**\r\n * Server component definition\r\n * @deprecated Use ServerComponentFn from './index.js' instead\r\n */\r\nexport interface ServerComponent<P = unknown> {\r\n (props: P, context: RenderContext): Promise<string> | string;\r\n /** Mark as server component */\r\n __flight_server?: true;\r\n /** Dependencies for hydration */\r\n __flight_deps?: string[];\r\n}\r\n\r\n/**\r\n * Client component definition\r\n * @deprecated Use ClientReference from './boundaries.js' instead\r\n */\r\nexport interface ClientComponent<P = unknown> {\r\n (props: P): unknown;\r\n /** Mark as client component */\r\n __flight_client?: true;\r\n /** Client bundle path */\r\n __flight_bundle?: string;\r\n}\r\n\r\n/**\r\n * Component type detection\r\n */\r\nexport type ComponentType = 'server' | 'client' | 'hybrid';\r\n\r\n// ============================================================================\r\n// Server Component Execution\r\n// ============================================================================\r\n\r\n/**\r\n * Execute an async server component\r\n * @deprecated Use the new rendering pipeline instead\r\n */\r\nexport async function executeServerComponent<P>(\r\n component: ServerComponent<P>,\r\n props: P,\r\n context: RenderContext\r\n): Promise<string> {\r\n try {\r\n const result = await component(props, context);\r\n return result;\r\n } catch (error) {\r\n console.error('[Flight] Server component error:', error);\r\n throw error;\r\n }\r\n}\r\n\r\n/**\r\n * Create a render context from a Request\r\n * @deprecated Use createServerContext from './context.js' instead\r\n */\r\nexport function createRenderContext(request: Request, params: Record<string, string> = {}): RenderContext {\r\n const url = new URL(request.url);\r\n\r\n // Parse cookies\r\n const cookies = new Map<string, string>();\r\n const cookieHeader = request.headers.get('cookie') || '';\r\n cookieHeader.split(';').forEach(cookie => {\r\n const [key, value] = cookie.trim().split('=');\r\n if (key && value) cookies.set(key, value);\r\n });\r\n\r\n return {\r\n request,\r\n params,\r\n searchParams: url.searchParams,\r\n headers: request.headers,\r\n cookies,\r\n };\r\n}\r\n\r\n// ============================================================================\r\n// Data Fetching Helpers\r\n// ============================================================================\r\n\r\n/**\r\n * Cache for server-side fetch requests\r\n */\r\nconst fetchCache = new Map<string, { data: unknown; timestamp: number }>();\r\n\r\n/**\r\n * Server-side fetch with automatic caching\r\n */\r\nexport async function serverFetch<T>(\r\n url: string,\r\n options: RequestInit & {\r\n revalidate?: number | false;\r\n tags?: string[];\r\n } = {}\r\n): Promise<T> {\r\n const { revalidate = 60, ...fetchOptions } = options;\r\n const cacheKey = `${url}:${JSON.stringify(fetchOptions)}`;\r\n\r\n // Check cache\r\n const cached = fetchCache.get(cacheKey);\r\n if (cached) {\r\n const age = Date.now() - cached.timestamp;\r\n if (revalidate === false || age < (revalidate * 1000)) {\r\n return cached.data as T;\r\n }\r\n }\r\n\r\n // Fetch fresh data\r\n const response = await fetch(url, fetchOptions);\r\n\r\n if (!response.ok) {\r\n throw new Error(`Fetch failed: ${response.status} ${response.statusText}`);\r\n }\r\n\r\n const data = await response.json();\r\n\r\n // Cache the result\r\n if (revalidate !== false) {\r\n fetchCache.set(cacheKey, {\r\n data,\r\n timestamp: Date.now(),\r\n });\r\n }\r\n\r\n return data as T;\r\n}\r\n\r\n/**\r\n * Invalidate cache by tag\r\n */\r\nexport function revalidateTag(tag: string): void {\r\n console.log(`[Flight] Revalidating tag: ${tag}`);\r\n}\r\n\r\n/**\r\n * Invalidate cache by path\r\n */\r\nexport function revalidatePath(path: string): void {\r\n console.log(`[Flight] Revalidating path: ${path}`);\r\n}\r\n\r\n// ============================================================================\r\n// Component Serialization\r\n// ============================================================================\r\n\r\n/**\r\n * Serialize props for transmission to client\r\n * @deprecated Use serialize from './payload.js' instead\r\n */\r\nexport function serializeProps(props: unknown): string {\r\n function preProcess(value: unknown): unknown {\r\n if (value instanceof Date) {\r\n return { __type: 'Date', value: value.toISOString() };\r\n }\r\n if (value instanceof Map) {\r\n return { __type: 'Map', value: Array.from(value.entries()).map(([k, v]) => [preProcess(k), preProcess(v)]) };\r\n }\r\n if (value instanceof Set) {\r\n return { __type: 'Set', value: Array.from(value).map(preProcess) };\r\n }\r\n if (typeof value === 'bigint') {\r\n return { __type: 'BigInt', value: value.toString() };\r\n }\r\n if (typeof value === 'function') {\r\n return undefined;\r\n }\r\n if (Array.isArray(value)) {\r\n return value.map(preProcess);\r\n }\r\n if (value && typeof value === 'object') {\r\n const result: Record<string, unknown> = {};\r\n for (const [k, v] of Object.entries(value)) {\r\n result[k] = preProcess(v);\r\n }\r\n return result;\r\n }\r\n return value;\r\n }\r\n\r\n return JSON.stringify(preProcess(props));\r\n}\r\n\r\n/**\r\n * Deserialize props on client\r\n * @deprecated Use deserialize from './payload.js' instead\r\n */\r\nexport function deserializeProps<T>(serialized: string): T {\r\n return JSON.parse(serialized, (_key, value) => {\r\n if (value && typeof value === 'object' && '__type' in value) {\r\n switch (value.__type) {\r\n case 'Date':\r\n return new Date(value.value);\r\n case 'Map':\r\n return new Map(value.value);\r\n case 'Set':\r\n return new Set(value.value);\r\n case 'BigInt':\r\n return BigInt(value.value);\r\n }\r\n }\r\n return value;\r\n });\r\n}\r\n\r\n// ============================================================================\r\n// Client Component Boundary\r\n// ============================================================================\r\n\r\n/**\r\n * Create a client boundary placeholder\r\n * @deprecated Use clientRef from './payload.js' instead\r\n */\r\nexport function createClientBoundary(\r\n componentId: string,\r\n props: unknown,\r\n fallback?: string\r\n): string {\r\n const serializedProps = serializeProps(props);\r\n\r\n return `\r\n<!--flight-client:${componentId}-->\r\n<div data-flight-component=\"${componentId}\" data-flight-props='${serializedProps.replace(/'/g, \"'\")}'>\r\n ${fallback || '<div>Loading...</div>'}\r\n</div>\r\n<!--/flight-client-->\r\n<script type=\"module\">\r\n(async function() {\r\n const component = await import('/_flight/components/${componentId}.js');\r\n const props = JSON.parse('${serializedProps.replace(/'/g, \"\\\\'\")}');\r\n const container = document.querySelector('[data-flight-component=\"${componentId}\"]');\r\n if (container && component.default) {\r\n if (typeof component.hydrate === 'function') {\r\n component.hydrate(container, props);\r\n }\r\n }\r\n})();\r\n</script>`;\r\n}\r\n\r\n// ============================================================================\r\n// Async Component Helpers\r\n// ============================================================================\r\n\r\n/**\r\n * Helper to create an async server component\r\n * @deprecated Use async Server Components directly\r\n */\r\nexport function createAsyncComponent<P, T>(\r\n fetcher: (props: P, context: RenderContext) => Promise<T>,\r\n renderer: (data: T, props: P) => string\r\n): ServerComponent<P> {\r\n const component: ServerComponent<P> = async (props, context) => {\r\n const data = await fetcher(props, context);\r\n return renderer(data, props);\r\n };\r\n component.__flight_server = true;\r\n return component;\r\n}\r\n\r\n/**\r\n * Compose multiple server components\r\n * @deprecated Use Promise.all with async components\r\n */\r\nexport function composeComponents(\r\n ...components: Array<() => Promise<string> | string>\r\n): () => Promise<string> {\r\n return async () => {\r\n const results = await Promise.all(\r\n components.map(async (comp) => await comp())\r\n );\r\n return results.join('');\r\n };\r\n}\r\n\r\n// ============================================================================\r\n// Error Boundary for Server Components\r\n// ============================================================================\r\n\r\n/**\r\n * Options for async error boundary\r\n */\r\nexport interface AsyncErrorBoundaryOptions<P, R> {\r\n /** Fallback to render on error */\r\n fallback: (error: Error, props: P) => R;\r\n /** Optional callback when error occurs */\r\n onError?: (error: Error, props: P, context: RenderContext) => void;\r\n /** Whether to rethrow certain errors (e.g., redirects, not found) */\r\n rethrowNavigation?: boolean;\r\n}\r\n\r\n/**\r\n * Wrap an async server component with comprehensive error handling.\r\n *\r\n * @example\r\n * ```typescript\r\n * const SafeUserPage = withAsyncErrorBoundary(UserPage, {\r\n * fallback: (error, props) => `<div>Error loading user ${props.id}</div>`,\r\n * onError: (error, props) => console.error(`Failed to load user ${props.id}:`, error),\r\n * rethrowNavigation: true,\r\n * });\r\n * ```\r\n */\r\nexport function withAsyncErrorBoundary<P, R = string>(\r\n component: (props: P, context: RenderContext) => Promise<R> | R,\r\n options: AsyncErrorBoundaryOptions<P, R>\r\n): (props: P, context: RenderContext) => Promise<R> {\r\n const { fallback, onError, rethrowNavigation = true } = options;\r\n\r\n return async (props: P, context: RenderContext): Promise<R> => {\r\n try {\r\n return await component(props, context);\r\n } catch (error) {\r\n // Rethrow navigation errors if configured\r\n if (rethrowNavigation) {\r\n if (isNotFoundError(error)) {\r\n throw error;\r\n }\r\n if (isRedirectError(error)) {\r\n throw error;\r\n }\r\n }\r\n\r\n // Call error callback if provided\r\n if (onError) {\r\n try {\r\n onError(error as Error, props, context);\r\n } catch (callbackError) {\r\n console.error('[Flight] Error in onError callback:', callbackError);\r\n }\r\n }\r\n\r\n // Log error\r\n console.error('[Flight] Async component error:', error);\r\n\r\n // Return fallback\r\n return fallback(error as Error, props);\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * Wrap a server component with error handling\r\n * @deprecated Use withAsyncErrorBoundary instead\r\n */\r\nexport function withErrorBoundary<P>(\r\n component: ServerComponent<P>,\r\n errorFallback: (error: Error) => string\r\n): ServerComponent<P> {\r\n return async (props: P, context: RenderContext) => {\r\n try {\r\n return await component(props, context);\r\n } catch (error) {\r\n console.error('[Flight] Server component error:', error);\r\n return errorFallback(error as Error);\r\n }\r\n };\r\n}\r\n\r\n// ============================================================================\r\n// Not Found / Redirect Helpers\r\n// ============================================================================\r\n\r\n/**\r\n * Throw a not found error\r\n * @deprecated Use notFound from './context.js' instead\r\n */\r\nexport function notFound(): never {\r\n const error = new Error('Not Found');\r\n (error as Error & { __flight_not_found: boolean }).__flight_not_found = true;\r\n throw error;\r\n}\r\n\r\n/**\r\n * Check if error is not found\r\n * @deprecated Use isNotFoundError from './context.js' instead\r\n */\r\nexport function isNotFoundError(error: unknown): boolean {\r\n return error instanceof Error && (error as Error & { __flight_not_found?: boolean }).__flight_not_found === true;\r\n}\r\n\r\n/**\r\n * Server-side redirect\r\n * @deprecated Use redirect from './context.js' instead\r\n */\r\nexport function redirect(url: string, type: 'replace' | 'push' = 'replace'): never {\r\n const error = new Error(`Redirect: ${url}`);\r\n (error as Error & { __flight_redirect: { url: string; type: string } }).__flight_redirect = { url, type };\r\n throw error;\r\n}\r\n\r\n/**\r\n * Check if error is redirect\r\n * @deprecated Use isRedirectError from './context.js' instead\r\n */\r\nexport function isRedirectError(error: unknown): { url: string; type: string } | null {\r\n if (error instanceof Error && (error as Error & { __flight_redirect?: unknown }).__flight_redirect) {\r\n return (error as Error & { __flight_redirect: { url: string; type: string } }).__flight_redirect;\r\n }\r\n return null;\r\n}\r\n"]}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// src/file-router/streaming-hints.ts
|
|
2
|
+
var DEFAULT_STREAMING_HINTS = {
|
|
3
|
+
enabled: true,
|
|
4
|
+
timeout: 1e4,
|
|
5
|
+
// 10 seconds
|
|
6
|
+
priority: "normal",
|
|
7
|
+
boundaries: [],
|
|
8
|
+
forceStatic: false,
|
|
9
|
+
cache: {
|
|
10
|
+
ttl: void 0,
|
|
11
|
+
swr: void 0,
|
|
12
|
+
tags: []
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
async function resolveStreamingConfig(module, params, request, routePath = "/") {
|
|
16
|
+
if (typeof module.getStreamingConfig === "function") {
|
|
17
|
+
try {
|
|
18
|
+
const dynamicConfig = await module.getStreamingConfig(params, request);
|
|
19
|
+
return {
|
|
20
|
+
...DEFAULT_STREAMING_HINTS,
|
|
21
|
+
...dynamicConfig,
|
|
22
|
+
source: "dynamic",
|
|
23
|
+
routePath
|
|
24
|
+
};
|
|
25
|
+
} catch (error) {
|
|
26
|
+
console.warn(`[Flight] Error in getStreamingConfig for ${routePath}:`, error);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (module.streaming) {
|
|
30
|
+
return {
|
|
31
|
+
...DEFAULT_STREAMING_HINTS,
|
|
32
|
+
...module.streaming,
|
|
33
|
+
source: "static",
|
|
34
|
+
routePath
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
...DEFAULT_STREAMING_HINTS,
|
|
39
|
+
source: "default",
|
|
40
|
+
routePath
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
async function loadRouteWithStreaming(filePath, moduleLoader) {
|
|
44
|
+
let module;
|
|
45
|
+
if (moduleLoader) {
|
|
46
|
+
module = await moduleLoader(filePath);
|
|
47
|
+
} else {
|
|
48
|
+
const { pathToFileURL } = await import('url');
|
|
49
|
+
const fileUrl = pathToFileURL(filePath).href;
|
|
50
|
+
module = await import(fileUrl);
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
module,
|
|
54
|
+
hasStreamingConfig: "streaming" in module,
|
|
55
|
+
hasGetStreamingConfig: typeof module.getStreamingConfig === "function"
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function shouldStream(config, request) {
|
|
59
|
+
if (config.forceStatic) {
|
|
60
|
+
return { stream: false, reason: "forceStatic enabled" };
|
|
61
|
+
}
|
|
62
|
+
if (config.enabled === false) {
|
|
63
|
+
return { stream: false, reason: "streaming disabled in config" };
|
|
64
|
+
}
|
|
65
|
+
if (request) {
|
|
66
|
+
if (request.headers.get("x-no-stream") === "true") {
|
|
67
|
+
return { stream: false, reason: "x-no-stream header" };
|
|
68
|
+
}
|
|
69
|
+
if (config.priority === "low") {
|
|
70
|
+
const ect = request.headers.get("ect");
|
|
71
|
+
if (ect && ["slow-2g", "2g"].includes(ect)) {
|
|
72
|
+
return { stream: false, reason: "low priority + slow connection" };
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return { stream: true, reason: "streaming enabled" };
|
|
77
|
+
}
|
|
78
|
+
function createStreamingController(timeout) {
|
|
79
|
+
const controller = new AbortController();
|
|
80
|
+
const timeoutId = setTimeout(() => {
|
|
81
|
+
controller.abort(new Error(`Streaming timeout after ${timeout}ms`));
|
|
82
|
+
}, timeout);
|
|
83
|
+
return {
|
|
84
|
+
controller,
|
|
85
|
+
signal: controller.signal,
|
|
86
|
+
cleanup: () => clearTimeout(timeoutId)
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function generateCacheKey(routePath, params, _config) {
|
|
90
|
+
const paramString = Object.entries(params).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${k}=${v}`).join("&");
|
|
91
|
+
return `streaming:${routePath}:${paramString}`;
|
|
92
|
+
}
|
|
93
|
+
function getStreamingCacheHeaders(config) {
|
|
94
|
+
if (!config.cache) return {};
|
|
95
|
+
const headers = {};
|
|
96
|
+
if (config.cache.ttl) {
|
|
97
|
+
if (config.cache.swr) {
|
|
98
|
+
headers["Cache-Control"] = `s-maxage=${config.cache.ttl}, stale-while-revalidate=${config.cache.swr}`;
|
|
99
|
+
} else {
|
|
100
|
+
headers["Cache-Control"] = `s-maxage=${config.cache.ttl}`;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (config.cache.tags && config.cache.tags.length > 0) {
|
|
104
|
+
headers["X-Cache-Tags"] = config.cache.tags.join(",");
|
|
105
|
+
}
|
|
106
|
+
return headers;
|
|
107
|
+
}
|
|
108
|
+
function hasStreamingConfig(module) {
|
|
109
|
+
if (!module || typeof module !== "object") return false;
|
|
110
|
+
return "streaming" in module || "getStreamingConfig" in module;
|
|
111
|
+
}
|
|
112
|
+
function isValidStreamingHints(obj) {
|
|
113
|
+
if (!obj || typeof obj !== "object") return false;
|
|
114
|
+
const hints = obj;
|
|
115
|
+
if (hints.enabled !== void 0 && typeof hints.enabled !== "boolean") return false;
|
|
116
|
+
if (hints.timeout !== void 0 && typeof hints.timeout !== "number") return false;
|
|
117
|
+
if (hints.priority !== void 0 && !["high", "normal", "low"].includes(hints.priority)) return false;
|
|
118
|
+
if (hints.boundaries !== void 0 && !Array.isArray(hints.boundaries)) return false;
|
|
119
|
+
if (hints.forceStatic !== void 0 && typeof hints.forceStatic !== "boolean") return false;
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export { DEFAULT_STREAMING_HINTS, createStreamingController, generateCacheKey, getStreamingCacheHeaders, hasStreamingConfig, isValidStreamingHints, loadRouteWithStreaming, resolveStreamingConfig, shouldStream };
|
|
124
|
+
//# sourceMappingURL=chunk-ZJU5M4IB.js.map
|
|
125
|
+
//# sourceMappingURL=chunk-ZJU5M4IB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/file-router/streaming-hints.ts"],"names":[],"mappings":"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","file":"chunk-ZJU5M4IB.js","sourcesContent":["/**\r\n * @flightdev/core - File Router Streaming Hints\r\n * \r\n * Per-route streaming configuration through exports.\r\n * The user defines streaming behavior at the route level.\r\n * \r\n * @example\r\n * ```typescript\r\n * // src/routes/products/[id].page.tsx\r\n * \r\n * // Static export for streaming hints\r\n * export const streaming = {\r\n * enabled: true,\r\n * timeout: 5000,\r\n * priority: 'high',\r\n * };\r\n * \r\n * // Or dynamic function based on params\r\n * export function getStreamingConfig(params: { id: string }) {\r\n * return {\r\n * enabled: params.id !== 'preview',\r\n * timeout: 3000,\r\n * };\r\n * }\r\n * \r\n * export default function ProductPage({ params }) {\r\n * return <Product id={params.id} />;\r\n * }\r\n * ```\r\n */\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\n/**\r\n * Static streaming configuration export\r\n */\r\nexport interface StreamingHints {\r\n /** Whether streaming is enabled for this route (default: true) */\r\n enabled?: boolean;\r\n /** Timeout before aborting streaming (ms) */\r\n timeout?: number;\r\n /** Priority hint for streaming scheduler */\r\n priority?: 'high' | 'normal' | 'low';\r\n /** Expected suspense boundary IDs */\r\n boundaries?: string[];\r\n /** Force static rendering (no streaming) */\r\n forceStatic?: boolean;\r\n /** Cache the static version */\r\n cache?: {\r\n /** Time to cache in seconds */\r\n ttl?: number;\r\n /** Stale-while-revalidate time in seconds */\r\n swr?: number;\r\n /** Cache tags for invalidation */\r\n tags?: string[];\r\n };\r\n}\r\n\r\n/**\r\n * Dynamic streaming configuration function\r\n */\r\nexport type GetStreamingConfig<TParams = Record<string, string>> = (\r\n params: TParams,\r\n request?: Request\r\n) => StreamingHints | Promise<StreamingHints>;\r\n\r\n/**\r\n * Route module with streaming exports\r\n */\r\nexport interface StreamingRouteModule {\r\n /** Default component/handler */\r\n default: unknown;\r\n /** Static streaming configuration */\r\n streaming?: StreamingHints;\r\n /** Dynamic streaming configuration function */\r\n getStreamingConfig?: GetStreamingConfig;\r\n /** Other route exports (metadata, generateStaticParams, etc.) */\r\n [key: string]: unknown;\r\n}\r\n\r\n/**\r\n * Resolved streaming configuration for a request\r\n */\r\nexport interface ResolvedStreamingConfig extends StreamingHints {\r\n /** Source of the configuration */\r\n source: 'static' | 'dynamic' | 'default';\r\n /** Route path */\r\n routePath: string;\r\n}\r\n\r\n// ============================================================================\r\n// Default Configuration\r\n// ============================================================================\r\n\r\n/**\r\n * Default streaming hints when none are specified\r\n */\r\nexport const DEFAULT_STREAMING_HINTS: Required<StreamingHints> = {\r\n enabled: true,\r\n timeout: 10000, // 10 seconds\r\n priority: 'normal',\r\n boundaries: [],\r\n forceStatic: false,\r\n cache: {\r\n ttl: undefined as unknown as number,\r\n swr: undefined as unknown as number,\r\n tags: [],\r\n },\r\n};\r\n\r\n// ============================================================================\r\n// Configuration Resolution\r\n// ============================================================================\r\n\r\n/**\r\n * Resolve streaming configuration for a route request\r\n */\r\nexport async function resolveStreamingConfig(\r\n module: StreamingRouteModule,\r\n params: Record<string, string>,\r\n request?: Request,\r\n routePath: string = '/'\r\n): Promise<ResolvedStreamingConfig> {\r\n // Check for dynamic config first (takes precedence)\r\n if (typeof module.getStreamingConfig === 'function') {\r\n try {\r\n const dynamicConfig = await module.getStreamingConfig(params, request);\r\n return {\r\n ...DEFAULT_STREAMING_HINTS,\r\n ...dynamicConfig,\r\n source: 'dynamic',\r\n routePath,\r\n };\r\n } catch (error) {\r\n console.warn(`[Flight] Error in getStreamingConfig for ${routePath}:`, error);\r\n // Fall through to static config\r\n }\r\n }\r\n\r\n // Check for static config\r\n if (module.streaming) {\r\n return {\r\n ...DEFAULT_STREAMING_HINTS,\r\n ...module.streaming,\r\n source: 'static',\r\n routePath,\r\n };\r\n }\r\n\r\n // Return defaults\r\n return {\r\n ...DEFAULT_STREAMING_HINTS,\r\n source: 'default',\r\n routePath,\r\n };\r\n}\r\n\r\n// ============================================================================\r\n// Route Loader Enhancement\r\n// ============================================================================\r\n\r\n/**\r\n * Load route module and extract streaming configuration\r\n */\r\nexport async function loadRouteWithStreaming(\r\n filePath: string,\r\n moduleLoader?: (path: string) => Promise<StreamingRouteModule>\r\n): Promise<{\r\n module: StreamingRouteModule;\r\n hasStreamingConfig: boolean;\r\n hasGetStreamingConfig: boolean;\r\n}> {\r\n // Use custom loader or native import\r\n let module: StreamingRouteModule;\r\n if (moduleLoader) {\r\n module = await moduleLoader(filePath);\r\n } else {\r\n const { pathToFileURL } = await import('node:url');\r\n const fileUrl = pathToFileURL(filePath).href;\r\n module = await import(fileUrl);\r\n }\r\n\r\n return {\r\n module,\r\n hasStreamingConfig: 'streaming' in module,\r\n hasGetStreamingConfig: typeof module.getStreamingConfig === 'function',\r\n };\r\n}\r\n\r\n// ============================================================================\r\n// Streaming Decision Helper\r\n// ============================================================================\r\n\r\n/**\r\n * Determine if streaming should be used based on config and request\r\n */\r\nexport function shouldStream(\r\n config: ResolvedStreamingConfig,\r\n request?: Request\r\n): { stream: boolean; reason: string } {\r\n // Force static always wins\r\n if (config.forceStatic) {\r\n return { stream: false, reason: 'forceStatic enabled' };\r\n }\r\n\r\n // Check enabled flag\r\n if (config.enabled === false) {\r\n return { stream: false, reason: 'streaming disabled in config' };\r\n }\r\n\r\n // Check for bot user agents (optional, user can also use conditional.ts)\r\n if (request) {\r\n // userAgent could be used here for bot detection if needed\r\n\r\n // Check for explicit no-stream header\r\n if (request.headers.get('x-no-stream') === 'true') {\r\n return { stream: false, reason: 'x-no-stream header' };\r\n }\r\n\r\n // Check for low priority and slow connection\r\n if (config.priority === 'low') {\r\n const ect = request.headers.get('ect');\r\n if (ect && ['slow-2g', '2g'].includes(ect)) {\r\n return { stream: false, reason: 'low priority + slow connection' };\r\n }\r\n }\r\n }\r\n\r\n return { stream: true, reason: 'streaming enabled' };\r\n}\r\n\r\n// ============================================================================\r\n// Timeout Controller\r\n// ============================================================================\r\n\r\n/**\r\n * Create an abort controller with timeout\r\n */\r\nexport function createStreamingController(\r\n timeout: number\r\n): {\r\n controller: AbortController;\r\n signal: AbortSignal;\r\n cleanup: () => void;\r\n} {\r\n const controller = new AbortController();\r\n const timeoutId = setTimeout(() => {\r\n controller.abort(new Error(`Streaming timeout after ${timeout}ms`));\r\n }, timeout);\r\n\r\n return {\r\n controller,\r\n signal: controller.signal,\r\n cleanup: () => clearTimeout(timeoutId),\r\n };\r\n}\r\n\r\n// ============================================================================\r\n// Caching Integration\r\n// ============================================================================\r\n\r\n/**\r\n * Generate cache key for a streaming route\r\n */\r\nexport function generateCacheKey(\r\n routePath: string,\r\n params: Record<string, string>,\r\n _config: StreamingHints\r\n): string {\r\n const paramString = Object.entries(params)\r\n .sort(([a], [b]) => a.localeCompare(b))\r\n .map(([k, v]) => `${k}=${v}`)\r\n .join('&');\r\n\r\n return `streaming:${routePath}:${paramString}`;\r\n}\r\n\r\n/**\r\n * Cache headers for static streaming fallback\r\n */\r\nexport function getStreamingCacheHeaders(config: StreamingHints): Record<string, string> {\r\n if (!config.cache) return {};\r\n\r\n const headers: Record<string, string> = {};\r\n\r\n if (config.cache.ttl) {\r\n if (config.cache.swr) {\r\n headers['Cache-Control'] = `s-maxage=${config.cache.ttl}, stale-while-revalidate=${config.cache.swr}`;\r\n } else {\r\n headers['Cache-Control'] = `s-maxage=${config.cache.ttl}`;\r\n }\r\n }\r\n\r\n if (config.cache.tags && config.cache.tags.length > 0) {\r\n headers['X-Cache-Tags'] = config.cache.tags.join(',');\r\n }\r\n\r\n return headers;\r\n}\r\n\r\n// ============================================================================\r\n// Type Guards\r\n// ============================================================================\r\n\r\n/**\r\n * Check if a module has streaming configuration\r\n */\r\nexport function hasStreamingConfig(module: unknown): module is StreamingRouteModule {\r\n if (!module || typeof module !== 'object') return false;\r\n return 'streaming' in module || 'getStreamingConfig' in module;\r\n}\r\n\r\n/**\r\n * Validate streaming hints object\r\n */\r\nexport function isValidStreamingHints(obj: unknown): obj is StreamingHints {\r\n if (!obj || typeof obj !== 'object') return false;\r\n const hints = obj as StreamingHints;\r\n\r\n if (hints.enabled !== undefined && typeof hints.enabled !== 'boolean') return false;\r\n if (hints.timeout !== undefined && typeof hints.timeout !== 'number') return false;\r\n if (hints.priority !== undefined && !['high', 'normal', 'low'].includes(hints.priority)) return false;\r\n if (hints.boundaries !== undefined && !Array.isArray(hints.boundaries)) return false;\r\n if (hints.forceStatic !== undefined && typeof hints.forceStatic !== 'boolean') return false;\r\n\r\n return true;\r\n}\r\n"]}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// src/render/index.ts
|
|
2
|
+
function requiresServer(mode) {
|
|
3
|
+
return mode === "ssr" || mode === "isr";
|
|
4
|
+
}
|
|
5
|
+
function canBeStatic(mode) {
|
|
6
|
+
return mode === "ssg" || mode === "isr";
|
|
7
|
+
}
|
|
8
|
+
function isClientOnly(mode) {
|
|
9
|
+
return mode === "csr";
|
|
10
|
+
}
|
|
11
|
+
function createHTMLShell(options) {
|
|
12
|
+
const { head = "", body, scripts = [], styles = [], htmlAttributes = {}, bodyAttributes = {} } = options;
|
|
13
|
+
const htmlAttrs = Object.entries(htmlAttributes).map(([key, value]) => `${key}="${escapeHtml(value)}"`).join(" ");
|
|
14
|
+
const bodyAttrs = Object.entries(bodyAttributes).map(([key, value]) => `${key}="${escapeHtml(value)}"`).join(" ");
|
|
15
|
+
const styleLinks = styles.map((href) => `<link rel="stylesheet" href="${escapeHtml(href)}">`).join("\n ");
|
|
16
|
+
const scriptTags = scripts.map((src) => `<script type="module" src="${escapeHtml(src)}"></script>`).join("\n ");
|
|
17
|
+
return `<!DOCTYPE html>
|
|
18
|
+
<html ${htmlAttrs}>
|
|
19
|
+
<head>
|
|
20
|
+
<meta charset="UTF-8">
|
|
21
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
22
|
+
${styleLinks}
|
|
23
|
+
${head}
|
|
24
|
+
</head>
|
|
25
|
+
<body ${bodyAttrs}>
|
|
26
|
+
${body}
|
|
27
|
+
${scriptTags}
|
|
28
|
+
</body>
|
|
29
|
+
</html>`;
|
|
30
|
+
}
|
|
31
|
+
function escapeHtml(str) {
|
|
32
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
33
|
+
}
|
|
34
|
+
function createCSRShell(options) {
|
|
35
|
+
const { title = "Flight App", entryScript, rootId = "app" } = options;
|
|
36
|
+
const html = createHTMLShell({
|
|
37
|
+
head: `<title>${escapeHtml(title)}</title>`,
|
|
38
|
+
body: `<div id="${rootId}"></div>`,
|
|
39
|
+
scripts: [entryScript]
|
|
40
|
+
});
|
|
41
|
+
return {
|
|
42
|
+
html,
|
|
43
|
+
status: 200,
|
|
44
|
+
headers: {
|
|
45
|
+
"Content-Type": "text/html; charset=utf-8"
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export { canBeStatic, createCSRShell, createHTMLShell, escapeHtml, isClientOnly, requiresServer };
|
|
51
|
+
//# sourceMappingURL=chunk-ZVC3ZWLM.js.map
|
|
52
|
+
//# sourceMappingURL=chunk-ZVC3ZWLM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/render/index.ts"],"names":[],"mappings":";AA0EO,SAAS,eAAe,IAAA,EAA2B;AACtD,EAAA,OAAO,IAAA,KAAS,SAAS,IAAA,KAAS,KAAA;AACtC;AAKO,SAAS,YAAY,IAAA,EAA2B;AACnD,EAAA,OAAO,IAAA,KAAS,SAAS,IAAA,KAAS,KAAA;AACtC;AAKO,SAAS,aAAa,IAAA,EAA2B;AACpD,EAAA,OAAO,IAAA,KAAS,KAAA;AACpB;AAkDO,SAAS,gBAAgB,OAAA,EAOrB;AACP,EAAA,MAAM,EAAE,IAAA,GAAO,EAAA,EAAI,IAAA,EAAM,OAAA,GAAU,EAAC,EAAG,MAAA,GAAS,EAAC,EAAG,iBAAiB,EAAC,EAAG,cAAA,GAAiB,IAAG,GAAI,OAAA;AAEjG,EAAA,MAAM,SAAA,GAAY,OAAO,OAAA,CAAQ,cAAc,EAC1C,GAAA,CAAI,CAAC,CAAC,GAAA,EAAK,KAAK,MAAM,CAAA,EAAG,GAAG,KAAK,UAAA,CAAW,KAAK,CAAC,CAAA,CAAA,CAAG,CAAA,CACrD,KAAK,GAAG,CAAA;AAEb,EAAA,MAAM,SAAA,GAAY,OAAO,OAAA,CAAQ,cAAc,EAC1C,GAAA,CAAI,CAAC,CAAC,GAAA,EAAK,KAAK,MAAM,CAAA,EAAG,GAAG,KAAK,UAAA,CAAW,KAAK,CAAC,CAAA,CAAA,CAAG,CAAA,CACrD,KAAK,GAAG,CAAA;AAEb,EAAA,MAAM,UAAA,GAAa,MAAA,CACd,GAAA,CAAI,CAAA,IAAA,KAAQ,CAAA,6BAAA,EAAgC,UAAA,CAAW,IAAI,CAAC,CAAA,EAAA,CAAI,CAAA,CAChE,IAAA,CAAK,QAAQ,CAAA;AAElB,EAAA,MAAM,UAAA,GAAa,OAAA,CACd,GAAA,CAAI,CAAA,GAAA,KAAO,CAAA,2BAAA,EAA8B,UAAA,CAAW,GAAG,CAAC,CAAA,WAAA,CAAa,CAAA,CACrE,IAAA,CAAK,QAAQ,CAAA;AAElB,EAAA,OAAO,CAAA;AAAA,MAAA,EACH,SAAS,CAAA;AAAA;AAAA;AAAA;AAAA,IAAA,EAIX,UAAU;AAAA,IAAA,EACV,IAAI;AAAA;AAAA,QAAA,EAEA,SAAS,CAAA;AAAA,IAAA,EACb,IAAI;AAAA,IAAA,EACJ,UAAU;AAAA;AAAA,OAAA,CAAA;AAGhB;AAKO,SAAS,WAAW,GAAA,EAAqB;AAC5C,EAAA,OAAO,IACF,OAAA,CAAQ,IAAA,EAAM,OAAO,CAAA,CACrB,OAAA,CAAQ,MAAM,MAAM,CAAA,CACpB,QAAQ,IAAA,EAAM,MAAM,EACpB,OAAA,CAAQ,IAAA,EAAM,QAAQ,CAAA,CACtB,OAAA,CAAQ,MAAM,OAAO,CAAA;AAC9B;AAKO,SAAS,eAAe,OAAA,EAId;AACb,EAAA,MAAM,EAAE,KAAA,GAAQ,YAAA,EAAc,WAAA,EAAa,MAAA,GAAS,OAAM,GAAI,OAAA;AAE9D,EAAA,MAAM,OAAO,eAAA,CAAgB;AAAA,IACzB,IAAA,EAAM,CAAA,OAAA,EAAU,UAAA,CAAW,KAAK,CAAC,CAAA,QAAA,CAAA;AAAA,IACjC,IAAA,EAAM,YAAY,MAAM,CAAA,QAAA,CAAA;AAAA,IACxB,OAAA,EAAS,CAAC,WAAW;AAAA,GACxB,CAAA;AAED,EAAA,OAAO;AAAA,IACH,IAAA;AAAA,IACA,MAAA,EAAQ,GAAA;AAAA,IACR,OAAA,EAAS;AAAA,MACL,cAAA,EAAgB;AAAA;AACpB,GACJ;AACJ","file":"chunk-ZVC3ZWLM.js","sourcesContent":["/**\r\n * Flight Render Engine - Universal rendering primitives\r\n * \r\n * Supports SSR, SSG, CSR, and ISR - the user chooses per route.\r\n */\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\n/** Rendering modes supported by Flight */\r\nexport type RenderMode =\r\n | 'ssr' // Server-Side Rendering\r\n | 'ssg' // Static Site Generation\r\n | 'csr' // Client-Side Rendering\r\n | 'isr'; // Incremental Static Regeneration\r\n\r\n/** Context passed to render functions */\r\nexport interface RenderContext {\r\n /** The URL being rendered */\r\n url: URL;\r\n /** Route parameters */\r\n params: Record<string, string | string[]>;\r\n /** Request headers (available in SSR/ISR) */\r\n headers?: Headers;\r\n /** Cookies (available in SSR/ISR) */\r\n cookies?: Record<string, string>;\r\n /** Request method */\r\n method?: string;\r\n /** Request body (for POST/PUT/etc) */\r\n body?: unknown;\r\n /** Custom context data */\r\n locals?: Record<string, unknown>;\r\n}\r\n\r\n/** Result of a render operation */\r\nexport interface RenderResult {\r\n /** HTML content */\r\n html: string;\r\n /** HTTP status code */\r\n status: number;\r\n /** Response headers */\r\n headers: Record<string, string>;\r\n /** Head elements to inject */\r\n head?: {\r\n title?: string;\r\n meta?: Array<{ name?: string; property?: string; content: string }>;\r\n links?: Array<{ rel: string; href: string;[key: string]: string }>;\r\n scripts?: Array<{ src?: string; content?: string; type?: string }>;\r\n styles?: Array<{ href?: string; content?: string }>;\r\n };\r\n}\r\n\r\n/** Configuration for SSG/ISR */\r\nexport interface StaticRenderConfig {\r\n /** Paths to pre-render */\r\n paths: string[] | (() => Promise<string[]>);\r\n /** Fallback behavior for non-pre-rendered paths */\r\n fallback?: 'blocking' | 'prerender' | false;\r\n}\r\n\r\n/** Configuration for ISR */\r\nexport interface ISRConfig extends StaticRenderConfig {\r\n /** Revalidation time in seconds */\r\n revalidate: number;\r\n}\r\n\r\n// ============================================================================\r\n// Render Mode Helpers\r\n// ============================================================================\r\n\r\n/**\r\n * Check if a render mode requires server runtime\r\n */\r\nexport function requiresServer(mode: RenderMode): boolean {\r\n return mode === 'ssr' || mode === 'isr';\r\n}\r\n\r\n/**\r\n * Check if a render mode can be statically generated\r\n */\r\nexport function canBeStatic(mode: RenderMode): boolean {\r\n return mode === 'ssg' || mode === 'isr';\r\n}\r\n\r\n/**\r\n * Check if a render mode is purely client-side\r\n */\r\nexport function isClientOnly(mode: RenderMode): boolean {\r\n return mode === 'csr';\r\n}\r\n\r\n// ============================================================================\r\n// Abstract Renderer Interface\r\n// ============================================================================\r\n\r\n/**\r\n * UI Framework Integration Interface\r\n * \r\n * Each UI framework (React, Vue, Svelte, etc.) implements this interface\r\n * to integrate with Flight's render engine.\r\n */\r\nexport interface UIFrameworkAdapter<Component = unknown> {\r\n /** Framework name */\r\n name: string;\r\n\r\n /** \r\n * Render a component to HTML string (for SSR/SSG)\r\n */\r\n renderToString(\r\n component: Component,\r\n context: RenderContext\r\n ): Promise<RenderResult>;\r\n\r\n /**\r\n * Render a component to a stream (for streaming SSR)\r\n */\r\n renderToStream?(\r\n component: Component,\r\n context: RenderContext\r\n ): Promise<ReadableStream<Uint8Array>>;\r\n\r\n /**\r\n * Generate client hydration script\r\n */\r\n getHydrationScript?(component: Component): string;\r\n\r\n /**\r\n * File extensions this adapter handles\r\n */\r\n extensions: string[];\r\n}\r\n\r\n// ============================================================================\r\n// Default HTML Render Helpers\r\n// ============================================================================\r\n\r\n/**\r\n * Create a minimal HTML shell\r\n */\r\nexport function createHTMLShell(options: {\r\n head?: string;\r\n body: string;\r\n scripts?: string[];\r\n styles?: string[];\r\n htmlAttributes?: Record<string, string>;\r\n bodyAttributes?: Record<string, string>;\r\n}): string {\r\n const { head = '', body, scripts = [], styles = [], htmlAttributes = {}, bodyAttributes = {} } = options;\r\n\r\n const htmlAttrs = Object.entries(htmlAttributes)\r\n .map(([key, value]) => `${key}=\"${escapeHtml(value)}\"`)\r\n .join(' ');\r\n\r\n const bodyAttrs = Object.entries(bodyAttributes)\r\n .map(([key, value]) => `${key}=\"${escapeHtml(value)}\"`)\r\n .join(' ');\r\n\r\n const styleLinks = styles\r\n .map(href => `<link rel=\"stylesheet\" href=\"${escapeHtml(href)}\">`)\r\n .join('\\n ');\r\n\r\n const scriptTags = scripts\r\n .map(src => `<script type=\"module\" src=\"${escapeHtml(src)}\"></script>`)\r\n .join('\\n ');\r\n\r\n return `<!DOCTYPE html>\r\n<html ${htmlAttrs}>\r\n <head>\r\n <meta charset=\"UTF-8\">\r\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n ${styleLinks}\r\n ${head}\r\n </head>\r\n <body ${bodyAttrs}>\r\n ${body}\r\n ${scriptTags}\r\n </body>\r\n</html>`;\r\n}\r\n\r\n/**\r\n * Escape HTML special characters\r\n */\r\nexport function escapeHtml(str: string): string {\r\n return str\r\n .replace(/&/g, '&')\r\n .replace(/</g, '<')\r\n .replace(/>/g, '>')\r\n .replace(/\"/g, '"')\r\n .replace(/'/g, ''');\r\n}\r\n\r\n/**\r\n * Create a CSR shell (minimal HTML that loads client bundle)\r\n */\r\nexport function createCSRShell(options: {\r\n title?: string;\r\n entryScript: string;\r\n rootId?: string;\r\n}): RenderResult {\r\n const { title = 'Flight App', entryScript, rootId = 'app' } = options;\r\n\r\n const html = createHTMLShell({\r\n head: `<title>${escapeHtml(title)}</title>`,\r\n body: `<div id=\"${rootId}\"></div>`,\r\n scripts: [entryScript],\r\n });\r\n\r\n return {\r\n html,\r\n status: 200,\r\n headers: {\r\n 'Content-Type': 'text/html; charset=utf-8',\r\n },\r\n };\r\n}\r\n"]}
|