@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,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @flightdev/core - Conditional Streaming
|
|
3
|
+
*
|
|
4
|
+
* Smart streaming decisions based on request context.
|
|
5
|
+
* Bots get full HTML, users get streaming - YOU control the logic.
|
|
6
|
+
*
|
|
7
|
+
* Best Practices 2026:
|
|
8
|
+
* - SEO-friendly: Crawlers receive complete HTML
|
|
9
|
+
* - Performance: Users get progressive streaming
|
|
10
|
+
* - Flexibility: Custom conditions for any use case
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* import { streamIf } from '@flightdev/core/streaming';
|
|
15
|
+
*
|
|
16
|
+
* const response = await streamIf({
|
|
17
|
+
* request,
|
|
18
|
+
* condition: (req) => !isBot(req), // YOUR logic
|
|
19
|
+
* stream: () => createStreamingSSR({ ... }),
|
|
20
|
+
* static: () => renderToString(<App />),
|
|
21
|
+
* });
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* Condition function to determine if streaming should be used
|
|
26
|
+
*/
|
|
27
|
+
type StreamingCondition = (request: Request) => boolean | Promise<boolean>;
|
|
28
|
+
/**
|
|
29
|
+
* Built-in condition types
|
|
30
|
+
*/
|
|
31
|
+
type BuiltInCondition = 'always-stream' | 'never-stream' | 'no-bots' | 'fast-network' | 'modern-browser';
|
|
32
|
+
/**
|
|
33
|
+
* Configuration for conditional streaming
|
|
34
|
+
*/
|
|
35
|
+
interface StreamIfConfig<T = Response> {
|
|
36
|
+
/** The incoming request */
|
|
37
|
+
request: Request;
|
|
38
|
+
/** Condition to check (function or built-in) */
|
|
39
|
+
condition: StreamingCondition | BuiltInCondition;
|
|
40
|
+
/** Factory for streaming response */
|
|
41
|
+
stream: () => Promise<T> | T;
|
|
42
|
+
/** Factory for static response */
|
|
43
|
+
static: () => Promise<T> | T;
|
|
44
|
+
/** Optional: Custom bot patterns */
|
|
45
|
+
botPatterns?: RegExp[];
|
|
46
|
+
/** Optional: Force streaming via query param (for testing) */
|
|
47
|
+
forceStreamParam?: string;
|
|
48
|
+
/** Optional: Force static via query param (for testing) */
|
|
49
|
+
forceStaticParam?: string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Result of condition evaluation
|
|
53
|
+
*/
|
|
54
|
+
interface StreamingDecision {
|
|
55
|
+
/** Whether streaming was used */
|
|
56
|
+
streaming: boolean;
|
|
57
|
+
/** Reason for the decision */
|
|
58
|
+
reason: string;
|
|
59
|
+
/** The response */
|
|
60
|
+
response: Response;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Default bot patterns for detection
|
|
64
|
+
*/
|
|
65
|
+
declare const DEFAULT_BOT_PATTERNS: RegExp[];
|
|
66
|
+
/**
|
|
67
|
+
* Check if a request is from a bot/crawler
|
|
68
|
+
*/
|
|
69
|
+
declare function isBot(request: Request, customPatterns?: RegExp[]): boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Check if request explicitly asks for no streaming
|
|
72
|
+
*/
|
|
73
|
+
declare function prefersNoStream(request: Request): boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Check if a connection is likely slow (based on headers)
|
|
76
|
+
*/
|
|
77
|
+
declare function isSlowConnection(request: Request): boolean;
|
|
78
|
+
/**
|
|
79
|
+
* Check if browser supports streaming well
|
|
80
|
+
*/
|
|
81
|
+
declare function supportsStreaming(request: Request): boolean;
|
|
82
|
+
/**
|
|
83
|
+
* Conditionally use streaming or static rendering based on request context
|
|
84
|
+
*/
|
|
85
|
+
declare function streamIf<T = Response>(config: StreamIfConfig<T>): Promise<{
|
|
86
|
+
result: T;
|
|
87
|
+
streaming: boolean;
|
|
88
|
+
reason: string;
|
|
89
|
+
}>;
|
|
90
|
+
/**
|
|
91
|
+
* Create a middleware-style conditional streamer
|
|
92
|
+
*/
|
|
93
|
+
declare function createConditionalStreamer<T = Response>(defaultCondition: StreamingCondition | BuiltInCondition, options?: {
|
|
94
|
+
botPatterns?: RegExp[];
|
|
95
|
+
forceStreamParam?: string;
|
|
96
|
+
forceStaticParam?: string;
|
|
97
|
+
}): (config: {
|
|
98
|
+
request: Request;
|
|
99
|
+
stream: () => Promise<T> | T;
|
|
100
|
+
static: () => Promise<T> | T;
|
|
101
|
+
condition?: StreamingCondition | BuiltInCondition;
|
|
102
|
+
}) => Promise<{
|
|
103
|
+
result: T;
|
|
104
|
+
streaming: boolean;
|
|
105
|
+
reason: string;
|
|
106
|
+
}>;
|
|
107
|
+
/**
|
|
108
|
+
* Add streaming decision headers to response (for debugging)
|
|
109
|
+
*/
|
|
110
|
+
declare function addStreamingHeaders(response: Response, decision: {
|
|
111
|
+
streaming: boolean;
|
|
112
|
+
reason: string;
|
|
113
|
+
}): Response;
|
|
114
|
+
/**
|
|
115
|
+
* Create a streaming-aware Response with appropriate headers
|
|
116
|
+
*/
|
|
117
|
+
declare function createStreamingResponse(stream: ReadableStream<Uint8Array>, options?: {
|
|
118
|
+
status?: number;
|
|
119
|
+
headers?: Record<string, string>;
|
|
120
|
+
isStreaming?: boolean;
|
|
121
|
+
}): Response;
|
|
122
|
+
/**
|
|
123
|
+
* Create a static HTML Response
|
|
124
|
+
*/
|
|
125
|
+
declare function createStaticResponse(html: string, options?: {
|
|
126
|
+
status?: number;
|
|
127
|
+
headers?: Record<string, string>;
|
|
128
|
+
}): Response;
|
|
129
|
+
|
|
130
|
+
export { type BuiltInCondition, DEFAULT_BOT_PATTERNS, type StreamIfConfig, type StreamingCondition, type StreamingDecision, addStreamingHeaders, createConditionalStreamer, createStaticResponse, createStreamingResponse, isBot, isSlowConnection, prefersNoStream, streamIf, supportsStreaming };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { DEFAULT_BOT_PATTERNS, addStreamingHeaders, createConditionalStreamer, createStaticResponse, createStreamingResponse, isBot, isSlowConnection, prefersNoStream, streamIf, supportsStreaming } from '../chunk-UVH5XJRP.js';
|
|
2
|
+
//# sourceMappingURL=conditional.js.map
|
|
3
|
+
//# sourceMappingURL=conditional.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"conditional.js"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @flightdev/core - Streaming SSR
|
|
3
|
+
*
|
|
4
|
+
* Full streaming server-side rendering implementation following React 18+/19 patterns.
|
|
5
|
+
* Supports both Node.js (renderToPipeableStream) and Edge (renderToReadableStream) environments.
|
|
6
|
+
*
|
|
7
|
+
* Best Practices 2025/2026:
|
|
8
|
+
* - Progressive HTML streaming with Suspense boundaries
|
|
9
|
+
* - Shell-first rendering for fast TTFB
|
|
10
|
+
* - Nested Suspense for granular loading states
|
|
11
|
+
* - Error boundaries for graceful degradation
|
|
12
|
+
* - Web Streams API for Edge runtime compatibility
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Streaming render options following React's conventions
|
|
16
|
+
*/
|
|
17
|
+
interface StreamingRenderOptions {
|
|
18
|
+
/** Bootstrap scripts to load on client */
|
|
19
|
+
bootstrapScripts?: string[];
|
|
20
|
+
/** Bootstrap ES modules */
|
|
21
|
+
bootstrapModules?: string[];
|
|
22
|
+
/** Inline script content */
|
|
23
|
+
bootstrapScriptContent?: string;
|
|
24
|
+
/** Prefix for React IDs (useId) */
|
|
25
|
+
identifierPrefix?: string;
|
|
26
|
+
/** Nonce for CSP */
|
|
27
|
+
nonce?: string;
|
|
28
|
+
/** Callback when shell is ready (main content before Suspense) */
|
|
29
|
+
onShellReady?: () => void;
|
|
30
|
+
/** Callback when shell errors */
|
|
31
|
+
onShellError?: (error: Error) => void;
|
|
32
|
+
/** Callback when all content is ready */
|
|
33
|
+
onAllReady?: () => void;
|
|
34
|
+
/** Callback for any error */
|
|
35
|
+
onError?: (error: Error) => void;
|
|
36
|
+
/** Timeout before aborting stream */
|
|
37
|
+
timeoutMs?: number;
|
|
38
|
+
/** Progressive hydration enabled */
|
|
39
|
+
progressiveHydration?: boolean;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Streaming render result
|
|
43
|
+
*/
|
|
44
|
+
interface StreamingRenderResult {
|
|
45
|
+
/** The readable stream to pipe to response */
|
|
46
|
+
stream: ReadableStream<Uint8Array>;
|
|
47
|
+
/** Abort the stream */
|
|
48
|
+
abort: () => void;
|
|
49
|
+
/** Promise that resolves when shell is ready */
|
|
50
|
+
shellReady: Promise<void>;
|
|
51
|
+
/** Promise that resolves when all content is ready */
|
|
52
|
+
allReady: Promise<void>;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Suspense boundary configuration
|
|
56
|
+
*/
|
|
57
|
+
interface SuspenseBoundaryConfig {
|
|
58
|
+
/** Unique ID for this boundary */
|
|
59
|
+
id: string;
|
|
60
|
+
/** Fallback HTML to show while loading */
|
|
61
|
+
fallback: string;
|
|
62
|
+
/** Content resolver promise */
|
|
63
|
+
contentPromise: Promise<string>;
|
|
64
|
+
/**
|
|
65
|
+
* IDs of boundaries that must resolve before this one.
|
|
66
|
+
* Enables dependency-aware streaming for complex data relationships.
|
|
67
|
+
* @example ['user'] - Wait for 'user' boundary before starting
|
|
68
|
+
*/
|
|
69
|
+
dependsOn?: string[];
|
|
70
|
+
/** Custom metadata for observability */
|
|
71
|
+
meta?: Record<string, unknown>;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Create a streaming SSR response using Web Streams API.
|
|
75
|
+
* Compatible with Edge Runtime (Cloudflare, Vercel Edge, Deno).
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```typescript
|
|
79
|
+
* const result = await createStreamingSSR({
|
|
80
|
+
* shell: '<html><body><div id="root">',
|
|
81
|
+
* shellEnd: '</div></body></html>',
|
|
82
|
+
* suspenseBoundaries: [
|
|
83
|
+
* {
|
|
84
|
+
* id: 'posts',
|
|
85
|
+
* fallback: '<div>Loading posts...</div>',
|
|
86
|
+
* contentPromise: fetchAndRenderPosts(),
|
|
87
|
+
* }
|
|
88
|
+
* ],
|
|
89
|
+
* bootstrapScripts: ['/client.js'],
|
|
90
|
+
* });
|
|
91
|
+
*
|
|
92
|
+
* return new Response(result.stream, {
|
|
93
|
+
* headers: { 'Content-Type': 'text/html' },
|
|
94
|
+
* });
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
declare function createStreamingSSR(config: {
|
|
98
|
+
/** Initial HTML shell (before suspense content) */
|
|
99
|
+
shell: string;
|
|
100
|
+
/** Closing HTML shell */
|
|
101
|
+
shellEnd: string;
|
|
102
|
+
/** Suspense boundaries with async content */
|
|
103
|
+
suspenseBoundaries?: SuspenseBoundaryConfig[];
|
|
104
|
+
/** Streaming options */
|
|
105
|
+
options?: StreamingRenderOptions;
|
|
106
|
+
}): Promise<StreamingRenderResult>;
|
|
107
|
+
/**
|
|
108
|
+
* Create a streaming HTML Response object
|
|
109
|
+
*/
|
|
110
|
+
declare function createStreamingResponse(stream: ReadableStream<Uint8Array>, options?: {
|
|
111
|
+
status?: number;
|
|
112
|
+
headers?: Record<string, string>;
|
|
113
|
+
}): Response;
|
|
114
|
+
/**
|
|
115
|
+
* Higher-level API: Render page with Suspense boundaries
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```typescript
|
|
119
|
+
* return renderWithStreaming({
|
|
120
|
+
* layout: ({ children }) => `
|
|
121
|
+
* <html>
|
|
122
|
+
* <head><title>My App</title></head>
|
|
123
|
+
* <body><div id="root">${children}</div></body>
|
|
124
|
+
* </html>
|
|
125
|
+
* `,
|
|
126
|
+
* page: async () => '<h1>Welcome</h1>',
|
|
127
|
+
* suspense: {
|
|
128
|
+
* posts: {
|
|
129
|
+
* fallback: '<div class="skeleton">Loading...</div>',
|
|
130
|
+
* content: fetchPosts().then(renderPosts),
|
|
131
|
+
* },
|
|
132
|
+
* },
|
|
133
|
+
* });
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
declare function renderWithStreaming(config: {
|
|
137
|
+
/** Layout wrapper */
|
|
138
|
+
layout: (props: {
|
|
139
|
+
children: string;
|
|
140
|
+
}) => string;
|
|
141
|
+
/** Main page content (sync part) */
|
|
142
|
+
page: () => string | Promise<string>;
|
|
143
|
+
/** Suspense boundaries keyed by ID */
|
|
144
|
+
suspense?: Record<string, {
|
|
145
|
+
fallback: string;
|
|
146
|
+
content: Promise<string>;
|
|
147
|
+
}>;
|
|
148
|
+
/** Bootstrap scripts */
|
|
149
|
+
bootstrapScripts?: string[];
|
|
150
|
+
/** Timeout in ms */
|
|
151
|
+
timeoutMs?: number;
|
|
152
|
+
}): Promise<Response>;
|
|
153
|
+
/**
|
|
154
|
+
* Create a lazy component that streams its content
|
|
155
|
+
*/
|
|
156
|
+
declare function createLazyContent<T>(fetcher: () => Promise<T>, renderer: (data: T) => string, fallback: string): {
|
|
157
|
+
fallback: string;
|
|
158
|
+
content: Promise<string>;
|
|
159
|
+
};
|
|
160
|
+
/**
|
|
161
|
+
* Parallel streaming: resolve multiple boundaries simultaneously
|
|
162
|
+
*/
|
|
163
|
+
declare function streamParallel(boundaries: Array<{
|
|
164
|
+
id: string;
|
|
165
|
+
fallback: string;
|
|
166
|
+
content: () => Promise<string>;
|
|
167
|
+
}>): Promise<SuspenseBoundaryConfig[]>;
|
|
168
|
+
/**
|
|
169
|
+
* Sequential streaming: resolve boundaries in order
|
|
170
|
+
*/
|
|
171
|
+
declare function streamSequential(boundaries: Array<{
|
|
172
|
+
id: string;
|
|
173
|
+
fallback: string;
|
|
174
|
+
content: () => Promise<string>;
|
|
175
|
+
}>): Promise<SuspenseBoundaryConfig[]>;
|
|
176
|
+
|
|
177
|
+
export { type StreamingRenderOptions, type StreamingRenderResult, type SuspenseBoundaryConfig, createLazyContent, createStreamingResponse, createStreamingSSR, renderWithStreaming, streamParallel, streamSequential };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { SuspenseBoundaryConfig, StreamingRenderOptions } from './index.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @flightdev/core - Streaming Observability
|
|
5
|
+
*
|
|
6
|
+
* Zero-telemetry metrics and instrumentation for streaming SSR.
|
|
7
|
+
* All data stays on YOUR infrastructure - never sent anywhere.
|
|
8
|
+
*
|
|
9
|
+
* Best Practices 2026:
|
|
10
|
+
* - Comprehensive timing metrics for each boundary
|
|
11
|
+
* - Hook-based observability for custom integrations
|
|
12
|
+
* - Performance insights without vendor lock-in
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* import { createInstrumentedStream } from '@flightdev/core/streaming';
|
|
17
|
+
*
|
|
18
|
+
* const { stream, metrics } = await createInstrumentedStream({
|
|
19
|
+
* shell: '<html>...',
|
|
20
|
+
* boundaries: [...],
|
|
21
|
+
* onMetrics: (m) => myAnalytics.track(m), // YOUR system
|
|
22
|
+
* });
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Timing information for a single boundary
|
|
28
|
+
*/
|
|
29
|
+
interface BoundaryTiming {
|
|
30
|
+
/** Boundary identifier */
|
|
31
|
+
id: string;
|
|
32
|
+
/** When the boundary started resolving (ms since stream start) */
|
|
33
|
+
startTime: number;
|
|
34
|
+
/** When the boundary finished resolving */
|
|
35
|
+
endTime: number;
|
|
36
|
+
/** Total duration in milliseconds */
|
|
37
|
+
duration: number;
|
|
38
|
+
/** Whether it resolved successfully */
|
|
39
|
+
success: boolean;
|
|
40
|
+
/** Size of content in bytes (if successful) */
|
|
41
|
+
contentSize?: number;
|
|
42
|
+
/** Error message if failed */
|
|
43
|
+
error?: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Overall streaming metrics
|
|
47
|
+
*/
|
|
48
|
+
interface StreamingMetrics {
|
|
49
|
+
/** Unique ID for this stream session */
|
|
50
|
+
streamId: string;
|
|
51
|
+
/** When streaming started */
|
|
52
|
+
startTime: number;
|
|
53
|
+
/** Time to shell ready (TTFB improvement) */
|
|
54
|
+
shellTime: number;
|
|
55
|
+
/** Time until all boundaries resolved */
|
|
56
|
+
totalStreamTime: number;
|
|
57
|
+
/** Individual boundary timings */
|
|
58
|
+
boundaries: BoundaryTiming[];
|
|
59
|
+
/** Number of boundaries that resolved successfully */
|
|
60
|
+
successCount: number;
|
|
61
|
+
/** Number of boundaries that failed */
|
|
62
|
+
errorCount: number;
|
|
63
|
+
/** Total bytes streamed */
|
|
64
|
+
totalBytes: number;
|
|
65
|
+
/** Strategy used (if priority streaming) */
|
|
66
|
+
strategy?: string;
|
|
67
|
+
/** Custom metadata you can add */
|
|
68
|
+
meta?: Record<string, unknown>;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Observability hooks for streaming events
|
|
72
|
+
*/
|
|
73
|
+
interface StreamingObserver {
|
|
74
|
+
/** Called when streaming starts */
|
|
75
|
+
onStreamStart?: (streamId: string) => void;
|
|
76
|
+
/** Called when shell is sent */
|
|
77
|
+
onShellReady?: (timing: {
|
|
78
|
+
streamId: string;
|
|
79
|
+
duration: number;
|
|
80
|
+
}) => void;
|
|
81
|
+
/** Called when a boundary starts resolving */
|
|
82
|
+
onBoundaryStart?: (info: {
|
|
83
|
+
streamId: string;
|
|
84
|
+
boundaryId: string;
|
|
85
|
+
startTime: number;
|
|
86
|
+
}) => void;
|
|
87
|
+
/** Called when a boundary finishes */
|
|
88
|
+
onBoundaryEnd?: (timing: BoundaryTiming & {
|
|
89
|
+
streamId: string;
|
|
90
|
+
}) => void;
|
|
91
|
+
/** Called when streaming completes */
|
|
92
|
+
onStreamEnd?: (metrics: StreamingMetrics) => void;
|
|
93
|
+
/** Called on any error */
|
|
94
|
+
onError?: (error: {
|
|
95
|
+
streamId: string;
|
|
96
|
+
boundaryId?: string;
|
|
97
|
+
error: Error;
|
|
98
|
+
}) => void;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Configuration for instrumented streaming
|
|
102
|
+
*/
|
|
103
|
+
interface InstrumentedStreamConfig {
|
|
104
|
+
/** Initial HTML shell */
|
|
105
|
+
shell: string;
|
|
106
|
+
/** Closing HTML */
|
|
107
|
+
shellEnd: string;
|
|
108
|
+
/** Suspense boundaries */
|
|
109
|
+
suspenseBoundaries: SuspenseBoundaryConfig[];
|
|
110
|
+
/** Streaming options */
|
|
111
|
+
options?: StreamingRenderOptions;
|
|
112
|
+
/** Observer hooks */
|
|
113
|
+
observer?: StreamingObserver;
|
|
114
|
+
/** Callback when all metrics are ready */
|
|
115
|
+
onMetrics?: (metrics: StreamingMetrics) => void;
|
|
116
|
+
/** Custom metadata to include in metrics */
|
|
117
|
+
meta?: Record<string, unknown>;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Result of instrumented streaming
|
|
121
|
+
*/
|
|
122
|
+
interface InstrumentedStreamResult {
|
|
123
|
+
/** The readable stream */
|
|
124
|
+
stream: ReadableStream<Uint8Array>;
|
|
125
|
+
/** Abort the stream */
|
|
126
|
+
abort: () => void;
|
|
127
|
+
/** Shell ready promise */
|
|
128
|
+
shellReady: Promise<void>;
|
|
129
|
+
/** All content ready promise */
|
|
130
|
+
allReady: Promise<void>;
|
|
131
|
+
/** Final metrics (resolves when stream completes) */
|
|
132
|
+
metrics: Promise<StreamingMetrics>;
|
|
133
|
+
/** Stream ID for correlation */
|
|
134
|
+
streamId: string;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Create an instrumented streaming SSR response with full observability
|
|
138
|
+
*/
|
|
139
|
+
declare function createInstrumentedStream(config: InstrumentedStreamConfig): Promise<InstrumentedStreamResult>;
|
|
140
|
+
/**
|
|
141
|
+
* Aggregate metrics from multiple streams for analysis
|
|
142
|
+
*/
|
|
143
|
+
declare class MetricsAggregator {
|
|
144
|
+
private metrics;
|
|
145
|
+
private maxSamples;
|
|
146
|
+
constructor(maxSamples?: number);
|
|
147
|
+
/**
|
|
148
|
+
* Add metrics from a stream
|
|
149
|
+
*/
|
|
150
|
+
add(metrics: StreamingMetrics): void;
|
|
151
|
+
/**
|
|
152
|
+
* Get average shell time
|
|
153
|
+
*/
|
|
154
|
+
getAverageShellTime(): number;
|
|
155
|
+
/**
|
|
156
|
+
* Get average total stream time
|
|
157
|
+
*/
|
|
158
|
+
getAverageTotalTime(): number;
|
|
159
|
+
/**
|
|
160
|
+
* Get slowest boundaries (by average duration)
|
|
161
|
+
*/
|
|
162
|
+
getSlowestBoundaries(limit?: number): {
|
|
163
|
+
id: string;
|
|
164
|
+
avgDuration: number;
|
|
165
|
+
errorRate: number;
|
|
166
|
+
}[];
|
|
167
|
+
/**
|
|
168
|
+
* Get overall error rate
|
|
169
|
+
*/
|
|
170
|
+
getErrorRate(): number;
|
|
171
|
+
/**
|
|
172
|
+
* Get percentiles for shell time
|
|
173
|
+
*/
|
|
174
|
+
getShellTimePercentiles(): {
|
|
175
|
+
p50: number;
|
|
176
|
+
p90: number;
|
|
177
|
+
p99: number;
|
|
178
|
+
};
|
|
179
|
+
/**
|
|
180
|
+
* Export all metrics for external analysis
|
|
181
|
+
*/
|
|
182
|
+
export(): StreamingMetrics[];
|
|
183
|
+
/**
|
|
184
|
+
* Clear all metrics
|
|
185
|
+
*/
|
|
186
|
+
clear(): void;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Create a streaming observer from a simple logger function
|
|
190
|
+
*/
|
|
191
|
+
declare function createLoggerObserver(log: (message: string, data?: Record<string, unknown>) => void): StreamingObserver;
|
|
192
|
+
/**
|
|
193
|
+
* Create observer that sends metrics to a custom endpoint (YOUR infrastructure)
|
|
194
|
+
*/
|
|
195
|
+
declare function createHttpObserver(endpoint: string, options?: {
|
|
196
|
+
headers?: Record<string, string>;
|
|
197
|
+
batchSize?: number;
|
|
198
|
+
flushInterval?: number;
|
|
199
|
+
}): StreamingObserver;
|
|
200
|
+
|
|
201
|
+
export { type BoundaryTiming, type InstrumentedStreamConfig, type InstrumentedStreamResult, MetricsAggregator, type StreamingMetrics, type StreamingObserver, createHttpObserver, createInstrumentedStream, createLoggerObserver };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"observability.js"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { StreamingRenderOptions, StreamingRenderResult } from './index.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @flightdev/core - Priority-Based Streaming SSR
|
|
5
|
+
*
|
|
6
|
+
* Out-of-order streaming with priority control, deadlines, and dependencies.
|
|
7
|
+
* Gives developers full control over streaming order and timing.
|
|
8
|
+
*
|
|
9
|
+
* Best Practices 2026:
|
|
10
|
+
* - Priority-based content delivery for optimal UX
|
|
11
|
+
* - Deadline-aware streaming to prevent slow boundaries from blocking
|
|
12
|
+
* - Dependency graphs for complex data relationships
|
|
13
|
+
* - Strategy-based streaming modes for different use cases
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* import { streamWithPriority } from '@flightdev/core/streaming';
|
|
18
|
+
*
|
|
19
|
+
* const result = await streamWithPriority({
|
|
20
|
+
* shell: '<html>...',
|
|
21
|
+
* shellEnd: '</html>',
|
|
22
|
+
* boundaries: [
|
|
23
|
+
* { id: 'hero', priority: 1, content: fetchHero() },
|
|
24
|
+
* { id: 'nav', priority: 2, content: fetchNav() },
|
|
25
|
+
* { id: 'comments', priority: 10, content: fetchComments() },
|
|
26
|
+
* ],
|
|
27
|
+
* strategy: 'priority-first',
|
|
28
|
+
* });
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Boundary with priority and advanced options
|
|
34
|
+
*/
|
|
35
|
+
interface PriorityBoundary {
|
|
36
|
+
/** Unique identifier for this boundary */
|
|
37
|
+
id: string;
|
|
38
|
+
/** Fallback HTML while loading */
|
|
39
|
+
fallback: string;
|
|
40
|
+
/** Content promise or factory function */
|
|
41
|
+
content: Promise<string> | (() => Promise<string>);
|
|
42
|
+
/** Priority level (lower number = higher priority, rendered first) */
|
|
43
|
+
priority: number;
|
|
44
|
+
/** Maximum time to wait before skipping (ms). Boundary shows fallback if exceeded. */
|
|
45
|
+
deadline?: number;
|
|
46
|
+
/** IDs of boundaries that must resolve before this one starts */
|
|
47
|
+
dependsOn?: string[];
|
|
48
|
+
/** Optional metadata for observability */
|
|
49
|
+
meta?: Record<string, unknown>;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Streaming strategy - how to order content delivery
|
|
53
|
+
*/
|
|
54
|
+
type StreamingStrategy = 'priority-first' | 'first-ready' | 'dependency-aware' | 'round-robin' | 'deadline-strict';
|
|
55
|
+
/**
|
|
56
|
+
* Result of a priority boundary resolution
|
|
57
|
+
*/
|
|
58
|
+
interface BoundaryResolution {
|
|
59
|
+
id: string;
|
|
60
|
+
content: string | null;
|
|
61
|
+
duration: number;
|
|
62
|
+
status: 'resolved' | 'timeout' | 'error' | 'skipped';
|
|
63
|
+
error?: Error;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Priority streaming configuration
|
|
67
|
+
*/
|
|
68
|
+
interface PriorityStreamConfig {
|
|
69
|
+
/** Initial HTML shell */
|
|
70
|
+
shell: string;
|
|
71
|
+
/** Closing HTML */
|
|
72
|
+
shellEnd: string;
|
|
73
|
+
/** Boundaries with priorities */
|
|
74
|
+
boundaries: PriorityBoundary[];
|
|
75
|
+
/** Streaming strategy */
|
|
76
|
+
strategy: StreamingStrategy;
|
|
77
|
+
/** Base streaming options */
|
|
78
|
+
options?: StreamingRenderOptions;
|
|
79
|
+
/** Callback when a boundary resolves */
|
|
80
|
+
onBoundaryResolved?: (resolution: BoundaryResolution) => void;
|
|
81
|
+
/** Global timeout for all boundaries */
|
|
82
|
+
globalTimeout?: number;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Priority streaming result with additional metrics
|
|
86
|
+
*/
|
|
87
|
+
interface PriorityStreamResult extends StreamingRenderResult {
|
|
88
|
+
/** Resolution details for each boundary */
|
|
89
|
+
resolutions: Promise<BoundaryResolution[]>;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Create a priority-ordered streaming SSR response
|
|
93
|
+
*/
|
|
94
|
+
declare function streamWithPriority(config: PriorityStreamConfig): Promise<PriorityStreamResult>;
|
|
95
|
+
/**
|
|
96
|
+
* Validate that boundaries don't have circular dependencies
|
|
97
|
+
*/
|
|
98
|
+
declare function validateDependencies(boundaries: PriorityBoundary[]): {
|
|
99
|
+
valid: boolean;
|
|
100
|
+
cycles?: string[][];
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export { type BoundaryResolution, type PriorityBoundary, type PriorityStreamConfig, type PriorityStreamResult, type StreamingStrategy, streamWithPriority, validateDependencies };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"priority.js"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @flightdev/core - Environment Utilities
|
|
3
|
+
*
|
|
4
|
+
* Environment detection utilities that work across Node.js, browsers, and edge runtimes.
|
|
5
|
+
* No hardcoded values - detects environment dynamically.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Check if running in production environment.
|
|
9
|
+
*
|
|
10
|
+
* Detection order:
|
|
11
|
+
* 1. Node.js process.env.NODE_ENV
|
|
12
|
+
* 2. Vite/modern bundlers import.meta.env.MODE or import.meta.env.PROD
|
|
13
|
+
* 3. Default: false (not production)
|
|
14
|
+
*/
|
|
15
|
+
declare function isProduction(): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Check if running in development environment.
|
|
18
|
+
*
|
|
19
|
+
* Detection order:
|
|
20
|
+
* 1. Node.js process.env.NODE_ENV
|
|
21
|
+
* 2. Vite/modern bundlers import.meta.env.MODE or import.meta.env.DEV
|
|
22
|
+
* 3. Default: true (assume development if unknown)
|
|
23
|
+
*/
|
|
24
|
+
declare function isDevelopment(): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Check if running in test environment.
|
|
27
|
+
*/
|
|
28
|
+
declare function isTest(): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Check if running on the server (not browser).
|
|
31
|
+
*/
|
|
32
|
+
declare function isServer(): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Check if running in the browser.
|
|
35
|
+
*/
|
|
36
|
+
declare function isBrowser(): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Get the current environment name.
|
|
39
|
+
*/
|
|
40
|
+
declare function getEnvironment(): 'production' | 'development' | 'test' | 'unknown';
|
|
41
|
+
|
|
42
|
+
export { getEnvironment, isBrowser, isDevelopment, isProduction, isServer, isTest };
|