@taujs/react 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/dist/index.d.ts +76 -9
- package/dist/index.js +85 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,3 +11,7 @@
|
|
|
11
11
|
React Renderer: CSR, SSR, Streaming SSR
|
|
12
12
|
|
|
13
13
|
https://taujs.dev/
|
|
14
|
+
|
|
15
|
+
A lightweight, production-ready React SSR library with streaming capabilities, built for modern TypeScript applications. Designed as part of the taujs (τjs) ecosystem but fully standalone and runtime-agnostic.
|
|
16
|
+
|
|
17
|
+
https://taujs.dev/renderers/react/
|
package/dist/index.d.ts
CHANGED
|
@@ -22,10 +22,10 @@ type UILogger = {
|
|
|
22
22
|
error: (...args: unknown[]) => void;
|
|
23
23
|
};
|
|
24
24
|
type ServerLogs = {
|
|
25
|
-
info: (
|
|
26
|
-
warn: (
|
|
27
|
-
error: (
|
|
28
|
-
debug?: (category: string,
|
|
25
|
+
info: (meta?: unknown, message?: string) => void;
|
|
26
|
+
warn: (meta?: unknown, message?: string) => void;
|
|
27
|
+
error: (meta?: unknown, message?: string) => void;
|
|
28
|
+
debug?: (category: string, meta?: unknown, message?: string) => void;
|
|
29
29
|
child?: (ctx: Record<string, unknown>) => ServerLogs;
|
|
30
30
|
isDebugEnabled?: (category: string) => boolean;
|
|
31
31
|
};
|
|
@@ -56,6 +56,10 @@ type StreamOptions = {
|
|
|
56
56
|
/** Whether to use cork/uncork for batched writes (default: true) */
|
|
57
57
|
useCork?: boolean;
|
|
58
58
|
};
|
|
59
|
+
type HeadContext<T extends Record<string, unknown> = Record<string, unknown>> = {
|
|
60
|
+
data: T;
|
|
61
|
+
meta: Record<string, unknown>;
|
|
62
|
+
};
|
|
59
63
|
type SSRResult = {
|
|
60
64
|
headContent: string;
|
|
61
65
|
appHtml: string;
|
|
@@ -68,10 +72,7 @@ declare function createRenderer<T extends Record<string, unknown>>({ appComponen
|
|
|
68
72
|
appComponent: (props: {
|
|
69
73
|
location: string;
|
|
70
74
|
}) => React.ReactElement;
|
|
71
|
-
headContent: (ctx:
|
|
72
|
-
data: T;
|
|
73
|
-
meta: Record<string, unknown>;
|
|
74
|
-
}) => string;
|
|
75
|
+
headContent: (ctx: HeadContext<T>) => string;
|
|
75
76
|
enableDebug?: boolean;
|
|
76
77
|
logger?: LoggerLike;
|
|
77
78
|
streamOptions?: StreamOptions;
|
|
@@ -85,4 +86,70 @@ declare function createRenderer<T extends Record<string, unknown>>({ appComponen
|
|
|
85
86
|
};
|
|
86
87
|
};
|
|
87
88
|
|
|
88
|
-
|
|
89
|
+
/**
|
|
90
|
+
* τjs Client Data Bridge
|
|
91
|
+
*
|
|
92
|
+
* Provides framework-agnostic primitives for accessing route data:
|
|
93
|
+
* - SSR hydration (window.__INITIAL_DATA__)
|
|
94
|
+
* - Client-side fetch (/__taujs/data endpoint)
|
|
95
|
+
*
|
|
96
|
+
* This is a transport layer only. For data orchestration (caching, refetch, etc.),
|
|
97
|
+
* use TanStack Query or similar.
|
|
98
|
+
*/
|
|
99
|
+
type RouteData = Record<string, unknown>;
|
|
100
|
+
/**
|
|
101
|
+
* Error thrown when fetchRouteData receives a non-2xx response.
|
|
102
|
+
* Contains structured error information from the server.
|
|
103
|
+
*/
|
|
104
|
+
declare class RouteDataError extends Error {
|
|
105
|
+
readonly status: number;
|
|
106
|
+
readonly statusText: string;
|
|
107
|
+
readonly code?: string;
|
|
108
|
+
readonly body?: unknown;
|
|
109
|
+
constructor(message: string, opts: {
|
|
110
|
+
status: number;
|
|
111
|
+
statusText: string;
|
|
112
|
+
code?: string;
|
|
113
|
+
body?: unknown;
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Read SSR boot data from window.__INITIAL_DATA__ exactly once.
|
|
118
|
+
* Subsequent calls return null (forces client-side fetch).
|
|
119
|
+
*
|
|
120
|
+
* Returns null on server (typeof window === 'undefined').
|
|
121
|
+
*/
|
|
122
|
+
declare function readInitialDataOnce<T extends RouteData = RouteData>(): T | null;
|
|
123
|
+
/**
|
|
124
|
+
* Fetch route data from the τjs data endpoint.
|
|
125
|
+
*
|
|
126
|
+
* Calls: GET /__taujs/data?url=<pathname>
|
|
127
|
+
* Returns: { data: T }
|
|
128
|
+
*
|
|
129
|
+
* Throws RouteDataError on non-2xx responses with structured error info.
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* const data = await fetchRouteData('/app/dashboard');
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* try {
|
|
136
|
+
* const data = await fetchRouteData('/app/dashboard');
|
|
137
|
+
* } catch (err) {
|
|
138
|
+
* if (err instanceof RouteDataError && err.status === 404) {
|
|
139
|
+
* // Handle not found
|
|
140
|
+
* }
|
|
141
|
+
* }
|
|
142
|
+
*/
|
|
143
|
+
declare function fetchRouteData<T extends RouteData = RouteData>(pathname: string, init?: RequestInit): Promise<T>;
|
|
144
|
+
/**
|
|
145
|
+
* Get the current browser path (pathname + search).
|
|
146
|
+
* Does not include hash.
|
|
147
|
+
*
|
|
148
|
+
* Returns null on server (typeof window === 'undefined').
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* const path = getCurrentPath(); // "/app/dashboard?tab=overview"
|
|
152
|
+
*/
|
|
153
|
+
declare function getCurrentPath(): string | null;
|
|
154
|
+
|
|
155
|
+
export { type HeadContext, type HydrateAppOptions, type RenderCallbacks, type RouteData, RouteDataError, type SSRStore, SSRStoreProvider, type ServerLogs, type StreamOptions, createRenderer, createSSRStore, fetchRouteData, getCurrentPath, hydrateApp, readInitialDataOnce, useSSRStore };
|
package/dist/index.js
CHANGED
|
@@ -90,10 +90,16 @@ var splitMsgAndMeta = (args) => {
|
|
|
90
90
|
};
|
|
91
91
|
function createUILogger(logger, opts = {}) {
|
|
92
92
|
const { debugCategory = "ssr", context, preferDebug = false, enableDebug = false } = opts;
|
|
93
|
-
if (!enableDebug)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
93
|
+
if (!enableDebug) {
|
|
94
|
+
return {
|
|
95
|
+
log: () => {
|
|
96
|
+
},
|
|
97
|
+
warn: () => {
|
|
98
|
+
},
|
|
99
|
+
error: () => {
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
}
|
|
97
103
|
const looksServer = !!logger && ("info" in logger || "debug" in logger || "child" in logger || "isDebugEnabled" in logger);
|
|
98
104
|
if (looksServer) {
|
|
99
105
|
let s = logger;
|
|
@@ -103,11 +109,11 @@ function createUILogger(logger, opts = {}) {
|
|
|
103
109
|
} catch {
|
|
104
110
|
}
|
|
105
111
|
}
|
|
106
|
-
const info = s.info ? s.info
|
|
107
|
-
const warn = s.warn ? s.warn
|
|
108
|
-
const error = s.error ? s.error
|
|
109
|
-
const debug = s.debug ? s.debug
|
|
110
|
-
const isDebugEnabled = s.isDebugEnabled ? s.isDebugEnabled
|
|
112
|
+
const info = s.info ? (msg, meta) => s.info(meta, msg) : (msg, meta) => meta ? console.log(msg, meta) : console.log(msg);
|
|
113
|
+
const warn = s.warn ? (msg, meta) => s.warn(meta, msg) : (msg, meta) => meta ? console.warn(msg, meta) : console.warn(msg);
|
|
114
|
+
const error = s.error ? (msg, meta) => s.error(meta, msg) : (msg, meta) => meta ? console.error(msg, meta) : console.error(msg);
|
|
115
|
+
const debug = s.debug ? (category, msg, meta) => s.debug(category, meta, msg) : void 0;
|
|
116
|
+
const isDebugEnabled = s.isDebugEnabled ? (category) => s.isDebugEnabled(category) : void 0;
|
|
111
117
|
return {
|
|
112
118
|
log: (...args) => {
|
|
113
119
|
const { msg, meta } = splitMsgAndMeta(args);
|
|
@@ -151,10 +157,13 @@ function hydrateApp({
|
|
|
151
157
|
onSuccess
|
|
152
158
|
}) {
|
|
153
159
|
const { log, warn, error } = createUILogger(logger, { debugCategory: "ssr", context: { scope: "react-hydration" }, enableDebug });
|
|
154
|
-
const mountCSR = (rootEl) => {
|
|
160
|
+
const mountCSR = (rootEl, initialData) => {
|
|
155
161
|
rootEl.innerHTML = "";
|
|
162
|
+
const store = createSSRStore(initialData);
|
|
156
163
|
const root = createRoot(rootEl);
|
|
157
|
-
root.render(
|
|
164
|
+
root.render(
|
|
165
|
+
/* @__PURE__ */ jsx2(React2.StrictMode, { children: /* @__PURE__ */ jsx2(SSRStoreProvider, { store, children: appComponent }) })
|
|
166
|
+
);
|
|
158
167
|
};
|
|
159
168
|
const startHydration = (rootEl, initialData) => {
|
|
160
169
|
if (enableDebug) log("Hydration started");
|
|
@@ -187,8 +196,9 @@ function hydrateApp({
|
|
|
187
196
|
}
|
|
188
197
|
const data = window[dataKey];
|
|
189
198
|
if (data === void 0) {
|
|
199
|
+
const data2 = {};
|
|
190
200
|
if (enableDebug) warn(`No initial SSR data at window["${dataKey}"]. Mounting CSR.`);
|
|
191
|
-
mountCSR(rootEl);
|
|
201
|
+
mountCSR(rootEl, data2);
|
|
192
202
|
return;
|
|
193
203
|
}
|
|
194
204
|
startHydration(rootEl, data);
|
|
@@ -559,10 +569,73 @@ function createRenderer({
|
|
|
559
569
|
};
|
|
560
570
|
return { renderSSR, renderStream };
|
|
561
571
|
}
|
|
572
|
+
|
|
573
|
+
// src/RouteData.ts
|
|
574
|
+
var RouteDataError = class _RouteDataError extends Error {
|
|
575
|
+
status;
|
|
576
|
+
statusText;
|
|
577
|
+
code;
|
|
578
|
+
body;
|
|
579
|
+
constructor(message, opts) {
|
|
580
|
+
super(message);
|
|
581
|
+
this.name = "RouteDataError";
|
|
582
|
+
this.status = opts.status;
|
|
583
|
+
this.statusText = opts.statusText;
|
|
584
|
+
this.code = opts.code;
|
|
585
|
+
this.body = opts.body;
|
|
586
|
+
Object.setPrototypeOf(this, _RouteDataError.prototype);
|
|
587
|
+
}
|
|
588
|
+
};
|
|
589
|
+
var INITIAL_DATA_KEY = "__INITIAL_DATA__";
|
|
590
|
+
function readInitialDataOnce() {
|
|
591
|
+
if (typeof window === "undefined") return null;
|
|
592
|
+
const w = window;
|
|
593
|
+
const data = w[INITIAL_DATA_KEY];
|
|
594
|
+
if (!data) return null;
|
|
595
|
+
delete w[INITIAL_DATA_KEY];
|
|
596
|
+
return data;
|
|
597
|
+
}
|
|
598
|
+
async function fetchRouteData(pathname, init) {
|
|
599
|
+
if (!pathname) {
|
|
600
|
+
throw new Error("fetchRouteData: pathname is required");
|
|
601
|
+
}
|
|
602
|
+
const url = `/__taujs/data?url=${encodeURIComponent(pathname)}`;
|
|
603
|
+
const res = await fetch(url, {
|
|
604
|
+
credentials: "include",
|
|
605
|
+
...init
|
|
606
|
+
});
|
|
607
|
+
if (!res.ok) {
|
|
608
|
+
let body2;
|
|
609
|
+
try {
|
|
610
|
+
body2 = await res.json();
|
|
611
|
+
} catch {
|
|
612
|
+
const text = await res.text().catch(() => "");
|
|
613
|
+
body2 = { error: text };
|
|
614
|
+
}
|
|
615
|
+
const json = body2;
|
|
616
|
+
throw new RouteDataError(json.error ?? `Request failed: ${res.status}`, {
|
|
617
|
+
status: res.status,
|
|
618
|
+
statusText: json.statusText ?? res.statusText,
|
|
619
|
+
code: json.code,
|
|
620
|
+
body: body2
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
const body = await res.json();
|
|
624
|
+
return body.data ?? {};
|
|
625
|
+
}
|
|
626
|
+
function getCurrentPath() {
|
|
627
|
+
if (typeof window === "undefined") return null;
|
|
628
|
+
const { pathname, search } = window.location;
|
|
629
|
+
return `${pathname}${search}`;
|
|
630
|
+
}
|
|
562
631
|
export {
|
|
632
|
+
RouteDataError,
|
|
563
633
|
SSRStoreProvider,
|
|
564
634
|
createRenderer,
|
|
565
635
|
createSSRStore,
|
|
636
|
+
fetchRouteData,
|
|
637
|
+
getCurrentPath,
|
|
566
638
|
hydrateApp,
|
|
639
|
+
readInitialDataOnce,
|
|
567
640
|
useSSRStore
|
|
568
641
|
};
|