@taujs/server 0.0.7 → 0.0.8
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 +9 -165
- package/dist/data.d.ts +37 -0
- package/dist/data.js +142 -0
- package/dist/{SSRServer.d.ts → index.d.ts} +3 -2
- package/dist/{SSRServer.js → index.js} +1 -1
- package/package.json +18 -33
- package/dist/SSRDataStore.d.ts +0 -15
- package/dist/SSRDataStore.js +0 -58
- package/dist/SSRRender.d.ts +0 -18
- package/dist/SSRRender.js +0 -31
package/README.md
CHANGED
|
@@ -38,122 +38,27 @@ Integrated ViteDevServer HMR + Vite Runtime API run alongside tsx (TS eXecute) p
|
|
|
38
38
|
|
|
39
39
|
### Fastify
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
import { SSRServer } from '@taujs/server;
|
|
43
|
-
|
|
44
|
-
void (await fastify.register(SSRServer, {
|
|
45
|
-
clientEntryClient: 'entry-client',
|
|
46
|
-
clientEntryServer: 'entry-server',
|
|
47
|
-
clientHtmlTemplate: 'index.html',
|
|
48
|
-
clientRoot: path.resolve(__dirname, '../client'),
|
|
49
|
-
routes,
|
|
50
|
-
serviceRegistry,
|
|
51
|
-
}));
|
|
52
|
-
```
|
|
41
|
+
https://github.com/aoede3/taujs/blob/main/src/server/index.ts
|
|
53
42
|
|
|
54
43
|
Not utilising taujs [ τjs ] template? Add in your own `alias` object for your own particular setup e.g. `alias: { object }`
|
|
55
44
|
|
|
56
45
|
### React 'entry-client.tsx'
|
|
57
46
|
|
|
58
|
-
|
|
59
|
-
import React from 'react';
|
|
60
|
-
import { hydrateRoot } from 'react-dom/client';
|
|
61
|
-
import { createSSRStore, SSRStoreProvider } from '@taujs/server/data-store';
|
|
62
|
-
|
|
63
|
-
import AppBootstrap from './AppBootstrap';
|
|
64
|
-
|
|
65
|
-
const bootstrap = () => {
|
|
66
|
-
const initialDataPromise = Promise.resolve(window.__INITIAL_DATA__);
|
|
67
|
-
const store = createSSRStore(initialDataPromise);
|
|
68
|
-
|
|
69
|
-
hydrateRoot(
|
|
70
|
-
document.getElementById('root') as HTMLElement,
|
|
71
|
-
<SSRStoreProvider store={store}>
|
|
72
|
-
<AppBootstrap />
|
|
73
|
-
</SSRStoreProvider>,
|
|
74
|
-
);
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
if (document.readyState !== 'loading') {
|
|
78
|
-
bootstrap();
|
|
79
|
-
} else {
|
|
80
|
-
document.addEventListener('DOMContentLoaded', () => {
|
|
81
|
-
bootstrap();
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
```
|
|
47
|
+
https://github.com/aoede3/taujs/blob/main/src/client/entry-client.tsx
|
|
86
48
|
|
|
87
49
|
### React 'entry-server.tsx'
|
|
88
50
|
|
|
89
51
|
Extended pipe object with callbacks to @taujs/server enabling additional manipulation of HEAD content from client code
|
|
90
52
|
|
|
91
|
-
|
|
92
|
-
import { ServerResponse } from 'node:http';
|
|
93
|
-
|
|
94
|
-
import React from 'react';
|
|
95
|
-
import { createSSRStore, SSRStoreProvider } from '@taujs/server/data-store';
|
|
96
|
-
import { createStreamRenderer } from '@taujs/server/render';
|
|
97
|
-
|
|
98
|
-
import AppBootstrap from '@client/AppBootstrap';
|
|
99
|
-
|
|
100
|
-
import type { RenderCallbacks } from '@taujs/server';
|
|
101
|
-
|
|
102
|
-
export const streamRender = (
|
|
103
|
-
serverResponse: ServerResponse,
|
|
104
|
-
{ onHead, onFinish, onError }: RenderCallbacks,
|
|
105
|
-
initialDataPromise: Promise<Record<string, unknown>>,
|
|
106
|
-
bootstrapModules: string,
|
|
107
|
-
) => {
|
|
108
|
-
const store = createSSRStore(initialDataPromise);
|
|
109
|
-
|
|
110
|
-
const headContent = `
|
|
111
|
-
<meta name="description" content="taujs [ τjs ]">
|
|
112
|
-
<link rel="icon" type="image/svg+xml" href="/taujs.svg" />
|
|
113
|
-
<title>taujs [ τjs ]</title>
|
|
114
|
-
`;
|
|
115
|
-
|
|
116
|
-
createStreamRenderer(
|
|
117
|
-
serverResponse,
|
|
118
|
-
{ onHead, onFinish, onError },
|
|
119
|
-
{
|
|
120
|
-
appElement: (
|
|
121
|
-
<SSRStoreProvider store={store}>
|
|
122
|
-
<AppBootstrap />
|
|
123
|
-
</SSRStoreProvider>
|
|
124
|
-
),
|
|
125
|
-
bootstrapModules,
|
|
126
|
-
getStoreSnapshot: store.getSnapshot,
|
|
127
|
-
headContent,
|
|
128
|
-
},
|
|
129
|
-
);
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
```
|
|
53
|
+
https://github.com/aoede3/taujs/blob/main/src/client/entry-server.tsx
|
|
133
54
|
|
|
134
55
|
### index.html
|
|
135
56
|
|
|
136
|
-
|
|
137
|
-
<!DOCTYPE html>
|
|
138
|
-
<html lang="en">
|
|
139
|
-
<head>
|
|
140
|
-
<meta charset="UTF-8" />
|
|
141
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
142
|
-
<!--ssr-head-->
|
|
143
|
-
</head>
|
|
144
|
-
<body>
|
|
145
|
-
<main id="root"><!--ssr-html--></main>
|
|
146
|
-
</body>
|
|
147
|
-
</html>
|
|
148
|
-
```
|
|
57
|
+
https://github.com/aoede3/taujs/blob/main/src/client/index.html
|
|
149
58
|
|
|
150
59
|
### client.d.ts
|
|
151
60
|
|
|
152
|
-
|
|
153
|
-
interface Window {
|
|
154
|
-
__INITIAL_DATA__: Record<string, unknown>;
|
|
155
|
-
}
|
|
156
|
-
```
|
|
61
|
+
https://github.com/aoede3/taujs/blob/main/src/client/client.d.ts
|
|
157
62
|
|
|
158
63
|
### Routes
|
|
159
64
|
|
|
@@ -172,73 +77,12 @@ In supporting Option 2. there is a registry of services. More detail in 'Service
|
|
|
172
77
|
|
|
173
78
|
Each routes 'path' is a simple URL regex as per below examples.
|
|
174
79
|
|
|
175
|
-
|
|
176
|
-
import type { Route, RouteParams } from '@taujs/server';
|
|
177
|
-
|
|
178
|
-
export const routes: Route<RouteParams>[] = [
|
|
179
|
-
{
|
|
180
|
-
path: '/',
|
|
181
|
-
attributes: {
|
|
182
|
-
fetch: async () => {
|
|
183
|
-
return {
|
|
184
|
-
url: 'http://localhost:5173/api/initial',
|
|
185
|
-
options: {
|
|
186
|
-
method: 'GET',
|
|
187
|
-
},
|
|
188
|
-
};
|
|
189
|
-
},
|
|
190
|
-
},
|
|
191
|
-
},
|
|
192
|
-
{
|
|
193
|
-
path: '/:id',
|
|
194
|
-
attributes: {
|
|
195
|
-
fetch: async (params: RouteParams) => {
|
|
196
|
-
return {
|
|
197
|
-
url: `http://localhost:5173/api/initial/${params.id}`,
|
|
198
|
-
options: {
|
|
199
|
-
method: 'GET',
|
|
200
|
-
},
|
|
201
|
-
};
|
|
202
|
-
},
|
|
203
|
-
},
|
|
204
|
-
},
|
|
205
|
-
{
|
|
206
|
-
path: '/:id/:another',
|
|
207
|
-
attributes: {
|
|
208
|
-
fetch: async (params: RouteParams) => {
|
|
209
|
-
return {
|
|
210
|
-
options: { params },
|
|
211
|
-
serviceMethod: 'exampleMethod',
|
|
212
|
-
serviceName: 'ServiceExample',
|
|
213
|
-
};
|
|
214
|
-
},
|
|
215
|
-
},
|
|
216
|
-
},
|
|
217
|
-
];
|
|
218
|
-
```
|
|
80
|
+
https://github.com/aoede3/taujs/blob/main/src/shared/routes/Routes.ts
|
|
219
81
|
|
|
220
82
|
### Service Registry
|
|
221
83
|
|
|
222
84
|
In supporting internal calls via τjs a registry of available services and methods provides the linkage to your own architectural setup and developmental patterns
|
|
223
85
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
import type { ServiceRegistry } from '@taujs/server';
|
|
228
|
-
|
|
229
|
-
export const serviceRegistry: ServiceRegistry = {
|
|
230
|
-
ServiceExample,
|
|
231
|
-
};
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
```
|
|
235
|
-
export const ServiceExample = {
|
|
236
|
-
async exampleMethod(params: Record<string, unknown>): Promise<Record<string, unknown>> {
|
|
237
|
-
return new Promise((resolve) => {
|
|
238
|
-
setTimeout(() => {
|
|
239
|
-
resolve({ hello: `world internal service call response with id: ${params.id} and another: ${params.another}` });
|
|
240
|
-
}, 5500);
|
|
241
|
-
});
|
|
242
|
-
},
|
|
243
|
-
};
|
|
244
|
-
```
|
|
86
|
+
https://github.com/aoede3/taujs/blob/main/src/server/services/ServiceRegistry.ts
|
|
87
|
+
|
|
88
|
+
https://github.com/aoede3/taujs/blob/main/src/server/services/ServiceExample.ts
|
package/dist/data.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ServerResponse } from 'node:http';
|
|
3
|
+
|
|
4
|
+
type SSRStore<T> = {
|
|
5
|
+
getSnapshot: () => T;
|
|
6
|
+
getServerSnapshot: () => T;
|
|
7
|
+
setData: (newData: T) => void;
|
|
8
|
+
subscribe: (callback: () => void) => () => void;
|
|
9
|
+
};
|
|
10
|
+
declare const createSSRStore: <T>(initialDataPromise: Promise<T>) => SSRStore<T>;
|
|
11
|
+
declare const SSRStoreProvider: React.FC<React.PropsWithChildren<{
|
|
12
|
+
store: SSRStore<Record<string, unknown>>;
|
|
13
|
+
}>>;
|
|
14
|
+
declare const useSSRStore: <T>() => SSRStore<T>;
|
|
15
|
+
|
|
16
|
+
type HydrateAppOptions = {
|
|
17
|
+
appComponent: React.ReactElement;
|
|
18
|
+
initialDataKey?: keyof Window;
|
|
19
|
+
rootElementId?: string;
|
|
20
|
+
debug?: boolean;
|
|
21
|
+
};
|
|
22
|
+
declare const hydrateApp: ({ appComponent, initialDataKey, rootElementId, debug }: HydrateAppOptions) => void;
|
|
23
|
+
|
|
24
|
+
type StreamRenderOptions = {
|
|
25
|
+
appComponent: React.ReactElement;
|
|
26
|
+
initialDataPromise: Promise<Record<string, unknown>>;
|
|
27
|
+
bootstrapModules: string;
|
|
28
|
+
headContent: string;
|
|
29
|
+
};
|
|
30
|
+
type RenderCallbacks = {
|
|
31
|
+
onHead: (headContent: string) => void;
|
|
32
|
+
onFinish: (initialDataResolved: unknown) => void;
|
|
33
|
+
onError: (error: unknown) => void;
|
|
34
|
+
};
|
|
35
|
+
declare const createStreamRenderer: (serverResponse: ServerResponse, { onHead, onFinish, onError }: RenderCallbacks, { appComponent, initialDataPromise, bootstrapModules, headContent }: StreamRenderOptions) => void;
|
|
36
|
+
|
|
37
|
+
export { type SSRStore, SSRStoreProvider, createSSRStore, createStreamRenderer, hydrateApp, useSSRStore };
|
package/dist/data.js
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// src/SSRDataStore.tsx
|
|
2
|
+
import { createContext, useContext, useSyncExternalStore } from "react";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
var createSSRStore = (initialDataPromise) => {
|
|
5
|
+
let currentData;
|
|
6
|
+
let status = "pending";
|
|
7
|
+
const subscribers = /* @__PURE__ */ new Set();
|
|
8
|
+
let resolvePromise = null;
|
|
9
|
+
const serverDataPromise = new Promise((resolve) => resolvePromise = resolve);
|
|
10
|
+
initialDataPromise.then((data) => {
|
|
11
|
+
currentData = data;
|
|
12
|
+
status = "success";
|
|
13
|
+
subscribers.forEach((callback) => callback());
|
|
14
|
+
if (resolvePromise) resolvePromise();
|
|
15
|
+
}).catch((error) => {
|
|
16
|
+
console.error("Failed to load initial data:", error);
|
|
17
|
+
status = "error";
|
|
18
|
+
});
|
|
19
|
+
const setData = (newData) => {
|
|
20
|
+
currentData = newData;
|
|
21
|
+
status = "success";
|
|
22
|
+
subscribers.forEach((callback) => callback());
|
|
23
|
+
if (resolvePromise) resolvePromise();
|
|
24
|
+
};
|
|
25
|
+
const subscribe = (callback) => {
|
|
26
|
+
subscribers.add(callback);
|
|
27
|
+
return () => subscribers.delete(callback);
|
|
28
|
+
};
|
|
29
|
+
const getSnapshot = () => {
|
|
30
|
+
if (status === "pending") {
|
|
31
|
+
throw initialDataPromise;
|
|
32
|
+
} else if (status === "error") {
|
|
33
|
+
throw new Error("An error occurred while fetching the data.");
|
|
34
|
+
}
|
|
35
|
+
return currentData;
|
|
36
|
+
};
|
|
37
|
+
const getServerSnapshot = () => {
|
|
38
|
+
if (status === "pending") {
|
|
39
|
+
throw serverDataPromise;
|
|
40
|
+
} else if (status === "error") {
|
|
41
|
+
throw new Error("Data is not available on the server.");
|
|
42
|
+
}
|
|
43
|
+
return currentData;
|
|
44
|
+
};
|
|
45
|
+
return { getSnapshot, getServerSnapshot, setData, subscribe };
|
|
46
|
+
};
|
|
47
|
+
var SSRStoreContext = createContext(null);
|
|
48
|
+
var SSRStoreProvider = ({ store, children }) => /* @__PURE__ */ jsx(SSRStoreContext.Provider, { value: store, children });
|
|
49
|
+
var useSSRStore = () => {
|
|
50
|
+
const store = useContext(SSRStoreContext);
|
|
51
|
+
if (!store) throw new Error("useSSRStore must be used within a SSRStoreProvider");
|
|
52
|
+
return useSyncExternalStore(store.subscribe, store.getSnapshot, store.getServerSnapshot);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// src/SSRHydration.tsx
|
|
56
|
+
import React2 from "react";
|
|
57
|
+
import { hydrateRoot } from "react-dom/client";
|
|
58
|
+
|
|
59
|
+
// src/utils/Logger.ts
|
|
60
|
+
var createLogger = (debug) => ({
|
|
61
|
+
log: (...args) => {
|
|
62
|
+
if (debug) console.log(...args);
|
|
63
|
+
},
|
|
64
|
+
warn: (...args) => {
|
|
65
|
+
if (debug) console.warn(...args);
|
|
66
|
+
},
|
|
67
|
+
error: (...args) => {
|
|
68
|
+
if (debug) console.error(...args);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// src/SSRHydration.tsx
|
|
73
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
74
|
+
var hydrateApp = ({ appComponent, initialDataKey = "__INITIAL_DATA__", rootElementId = "root", debug = false }) => {
|
|
75
|
+
const { log, warn, error } = createLogger(debug);
|
|
76
|
+
const bootstrap = () => {
|
|
77
|
+
log("Hydration started");
|
|
78
|
+
const rootElement = document.getElementById(rootElementId);
|
|
79
|
+
if (!rootElement) {
|
|
80
|
+
error(`Root element with id "${rootElementId}" not found.`);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const initialData = window[initialDataKey];
|
|
84
|
+
if (!initialData) {
|
|
85
|
+
warn(`Initial data key "${initialDataKey}" is undefined on window.`);
|
|
86
|
+
} else {
|
|
87
|
+
log("Initial data loaded:", initialData);
|
|
88
|
+
}
|
|
89
|
+
const initialDataPromise = Promise.resolve(initialData);
|
|
90
|
+
const store = createSSRStore(initialDataPromise);
|
|
91
|
+
log("Store created:", store);
|
|
92
|
+
hydrateRoot(
|
|
93
|
+
rootElement,
|
|
94
|
+
/* @__PURE__ */ jsx2(React2.StrictMode, { children: /* @__PURE__ */ jsx2(SSRStoreProvider, { store, children: appComponent }) })
|
|
95
|
+
);
|
|
96
|
+
log("Hydration completed");
|
|
97
|
+
};
|
|
98
|
+
if (document.readyState !== "loading") {
|
|
99
|
+
bootstrap();
|
|
100
|
+
} else {
|
|
101
|
+
document.addEventListener("DOMContentLoaded", bootstrap);
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
// src/SSRRender.tsx
|
|
106
|
+
import { Writable } from "node:stream";
|
|
107
|
+
import "react";
|
|
108
|
+
import { renderToPipeableStream } from "react-dom/server";
|
|
109
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
110
|
+
var createStreamRenderer = (serverResponse, { onHead, onFinish, onError }, { appComponent, initialDataPromise, bootstrapModules, headContent }) => {
|
|
111
|
+
const store = createSSRStore(initialDataPromise);
|
|
112
|
+
const appElement = /* @__PURE__ */ jsx3(SSRStoreProvider, { store, children: appComponent });
|
|
113
|
+
const { pipe } = renderToPipeableStream(appElement, {
|
|
114
|
+
bootstrapModules: [bootstrapModules],
|
|
115
|
+
onShellReady() {
|
|
116
|
+
onHead(headContent);
|
|
117
|
+
pipe(
|
|
118
|
+
new Writable({
|
|
119
|
+
write(chunk, _encoding, callback) {
|
|
120
|
+
serverResponse.write(chunk, callback);
|
|
121
|
+
},
|
|
122
|
+
final(callback) {
|
|
123
|
+
onFinish(store.getSnapshot());
|
|
124
|
+
callback();
|
|
125
|
+
}
|
|
126
|
+
})
|
|
127
|
+
);
|
|
128
|
+
},
|
|
129
|
+
onAllReady() {
|
|
130
|
+
},
|
|
131
|
+
onError(error) {
|
|
132
|
+
onError(error);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
};
|
|
136
|
+
export {
|
|
137
|
+
SSRStoreProvider,
|
|
138
|
+
createSSRStore,
|
|
139
|
+
createStreamRenderer,
|
|
140
|
+
hydrateApp,
|
|
141
|
+
useSSRStore
|
|
142
|
+
};
|
|
@@ -40,8 +40,9 @@ type Manifest = {
|
|
|
40
40
|
assets?: string[];
|
|
41
41
|
};
|
|
42
42
|
};
|
|
43
|
+
type StreamRender = (serverResponse: ServerResponse, callbacks: RenderCallbacks, initialDataPromise: Promise<Record<string, unknown>>, bootstrapModules: string) => void;
|
|
43
44
|
type RenderModule = {
|
|
44
|
-
streamRender:
|
|
45
|
+
streamRender: StreamRender;
|
|
45
46
|
};
|
|
46
47
|
type RouteAttributes<Params = {}> = {
|
|
47
48
|
fetch: (params?: Params, options?: RequestInit & {
|
|
@@ -66,4 +67,4 @@ interface InitialRouteParams extends Record<string, unknown> {
|
|
|
66
67
|
type RouteParams = InitialRouteParams & Record<string, unknown>;
|
|
67
68
|
type RoutePathsAndAttributes<Params = {}> = Omit<Route<Params>, 'element'>;
|
|
68
69
|
|
|
69
|
-
export { type FetchConfig, type InitialRouteParams, type Manifest, type RenderCallbacks, type RenderModule, type Route, type RouteAttributes, type RouteParams, type RoutePathsAndAttributes, SSRServer, type SSRServerOptions, type ServiceRegistry };
|
|
70
|
+
export { type FetchConfig, type InitialRouteParams, type Manifest, type RenderCallbacks, type RenderModule, type Route, type RouteAttributes, type RouteParams, type RoutePathsAndAttributes, SSRServer, type SSRServerOptions, type ServiceRegistry, type StreamRender };
|
|
@@ -121,7 +121,7 @@ import { readFile } from "node:fs/promises";
|
|
|
121
121
|
import path from "node:path";
|
|
122
122
|
import { createViteRuntime } from "vite";
|
|
123
123
|
|
|
124
|
-
// src/utils/
|
|
124
|
+
// src/utils/Utils.ts
|
|
125
125
|
import { fileURLToPath } from "node:url";
|
|
126
126
|
import { dirname, join } from "node:path";
|
|
127
127
|
import { match } from "path-to-regexp";
|
package/package.json
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
{
|
|
2
|
+
"name": "@taujs/server",
|
|
3
|
+
"version": "0.0.8",
|
|
4
|
+
"description": "taujs | τjs",
|
|
2
5
|
"author": "Aoede <taujs@aoede.uk.net> (https://www.aoede.uk.net)",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"homepage": "https://github.com/aoede3/taujs-server",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/aoede3/taujs-server.git"
|
|
11
|
+
},
|
|
3
12
|
"bugs": {
|
|
4
13
|
"url": "https://github.com/aoede3/taujs-server/issues"
|
|
5
14
|
},
|
|
6
|
-
"description": "taujs | τjs",
|
|
7
|
-
"homepage": "https://github.com/aoede3/taujs-server",
|
|
8
15
|
"keywords": [
|
|
9
16
|
"fastify",
|
|
10
17
|
"typescript",
|
|
@@ -14,44 +21,23 @@
|
|
|
14
21
|
"react",
|
|
15
22
|
"ssr"
|
|
16
23
|
],
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"type": "git",
|
|
21
|
-
"url": "git+https://github.com/aoede3/taujs-server.git"
|
|
22
|
-
},
|
|
23
|
-
"version": "0.0.7",
|
|
24
|
+
"type": "module",
|
|
25
|
+
"main": "./dist/index.js",
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
24
27
|
"exports": {
|
|
25
28
|
".": {
|
|
26
|
-
"import": "./dist/
|
|
27
|
-
"types": "./dist/
|
|
28
|
-
},
|
|
29
|
-
"./data-store": {
|
|
30
|
-
"import": "./dist/SSRDataStore.js",
|
|
31
|
-
"types": "./dist/SSRDataStore.d.ts"
|
|
29
|
+
"import": "./dist/index.js",
|
|
30
|
+
"types": "./dist/index.d.ts"
|
|
32
31
|
},
|
|
33
|
-
"./
|
|
34
|
-
"import": "./dist/
|
|
35
|
-
"types": "./dist/
|
|
32
|
+
"./data": {
|
|
33
|
+
"import": "./dist/data.js",
|
|
34
|
+
"types": "./dist/data.d.ts"
|
|
36
35
|
},
|
|
37
36
|
"./package.json": "./package.json"
|
|
38
37
|
},
|
|
39
38
|
"files": [
|
|
40
39
|
"dist"
|
|
41
40
|
],
|
|
42
|
-
"main": "./dist/SSRServer.js",
|
|
43
|
-
"types": "./dist/SSRServer.d.ts",
|
|
44
|
-
"typesVersions": {
|
|
45
|
-
"*": {
|
|
46
|
-
"data-store": [
|
|
47
|
-
"./dist/SSRDataStore.d.ts"
|
|
48
|
-
],
|
|
49
|
-
"render": [
|
|
50
|
-
"./dist/SSRRender.d.ts"
|
|
51
|
-
]
|
|
52
|
-
}
|
|
53
|
-
},
|
|
54
|
-
"type": "module",
|
|
55
41
|
"dependencies": {
|
|
56
42
|
"@fastify/static": "^7.0.4",
|
|
57
43
|
"path-to-regexp": "^8.1.0"
|
|
@@ -76,7 +62,6 @@
|
|
|
76
62
|
"vitest": "^2.0.5"
|
|
77
63
|
},
|
|
78
64
|
"peerDependencies": {
|
|
79
|
-
"@vitejs/plugin-react": "^4.3.1",
|
|
80
65
|
"fastify": "^5.0.0",
|
|
81
66
|
"react": "^18.3.1",
|
|
82
67
|
"react-dom": "^18.3.1",
|
|
@@ -85,7 +70,7 @@
|
|
|
85
70
|
},
|
|
86
71
|
"scripts": {
|
|
87
72
|
"build": "tsup",
|
|
88
|
-
"ci": "npm run build && npm run check-format && npm run
|
|
73
|
+
"ci": "npm run build && npm run check-format && npm run lint",
|
|
89
74
|
"lint": "tsc",
|
|
90
75
|
"test": "vitest run",
|
|
91
76
|
"coverage": "vitest run --coverage",
|
package/dist/SSRDataStore.d.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
|
|
3
|
-
type SSRStore<T> = {
|
|
4
|
-
getSnapshot: () => T;
|
|
5
|
-
getServerSnapshot: () => T;
|
|
6
|
-
setData: (newData: T) => void;
|
|
7
|
-
subscribe: (callback: () => void) => () => void;
|
|
8
|
-
};
|
|
9
|
-
declare const createSSRStore: <T>(initialDataPromise: Promise<T>) => SSRStore<T>;
|
|
10
|
-
declare const SSRStoreProvider: React.FC<React.PropsWithChildren<{
|
|
11
|
-
store: SSRStore<Record<string, unknown>>;
|
|
12
|
-
}>>;
|
|
13
|
-
declare const useSSRStore: <T>() => SSRStore<T>;
|
|
14
|
-
|
|
15
|
-
export { type SSRStore, SSRStoreProvider, createSSRStore, useSSRStore };
|
package/dist/SSRDataStore.js
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
// src/SSRDataStore.tsx
|
|
2
|
-
import { createContext, useContext, useSyncExternalStore } from "react";
|
|
3
|
-
import { jsx } from "react/jsx-runtime";
|
|
4
|
-
var createSSRStore = (initialDataPromise) => {
|
|
5
|
-
let currentData;
|
|
6
|
-
let status = "pending";
|
|
7
|
-
const subscribers = /* @__PURE__ */ new Set();
|
|
8
|
-
let resolvePromise = null;
|
|
9
|
-
const serverDataPromise = new Promise((resolve) => resolvePromise = resolve);
|
|
10
|
-
initialDataPromise.then((data) => {
|
|
11
|
-
currentData = data;
|
|
12
|
-
status = "success";
|
|
13
|
-
subscribers.forEach((callback) => callback());
|
|
14
|
-
if (resolvePromise) resolvePromise();
|
|
15
|
-
}).catch((error) => {
|
|
16
|
-
console.error("Failed to load initial data:", error);
|
|
17
|
-
status = "error";
|
|
18
|
-
});
|
|
19
|
-
const setData = (newData) => {
|
|
20
|
-
currentData = newData;
|
|
21
|
-
status = "success";
|
|
22
|
-
subscribers.forEach((callback) => callback());
|
|
23
|
-
if (resolvePromise) resolvePromise();
|
|
24
|
-
};
|
|
25
|
-
const subscribe = (callback) => {
|
|
26
|
-
subscribers.add(callback);
|
|
27
|
-
return () => subscribers.delete(callback);
|
|
28
|
-
};
|
|
29
|
-
const getSnapshot = () => {
|
|
30
|
-
if (status === "pending") {
|
|
31
|
-
throw initialDataPromise;
|
|
32
|
-
} else if (status === "error") {
|
|
33
|
-
throw new Error("An error occurred while fetching the data.");
|
|
34
|
-
}
|
|
35
|
-
return currentData;
|
|
36
|
-
};
|
|
37
|
-
const getServerSnapshot = () => {
|
|
38
|
-
if (status === "pending") {
|
|
39
|
-
throw serverDataPromise;
|
|
40
|
-
} else if (status === "error") {
|
|
41
|
-
throw new Error("Data is not available on the server.");
|
|
42
|
-
}
|
|
43
|
-
return currentData;
|
|
44
|
-
};
|
|
45
|
-
return { getSnapshot, getServerSnapshot, setData, subscribe };
|
|
46
|
-
};
|
|
47
|
-
var SSRStoreContext = createContext(null);
|
|
48
|
-
var SSRStoreProvider = ({ store, children }) => /* @__PURE__ */ jsx(SSRStoreContext.Provider, { value: store, children });
|
|
49
|
-
var useSSRStore = () => {
|
|
50
|
-
const store = useContext(SSRStoreContext);
|
|
51
|
-
if (!store) throw new Error("useSSRStore must be used within a SSRStoreProvider");
|
|
52
|
-
return useSyncExternalStore(store.subscribe, store.getSnapshot, store.getServerSnapshot);
|
|
53
|
-
};
|
|
54
|
-
export {
|
|
55
|
-
SSRStoreProvider,
|
|
56
|
-
createSSRStore,
|
|
57
|
-
useSSRStore
|
|
58
|
-
};
|
package/dist/SSRRender.d.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { ServerResponse } from 'node:http';
|
|
2
|
-
import React from 'react';
|
|
3
|
-
|
|
4
|
-
type RenderCallbacks = {
|
|
5
|
-
onHead: (headContent: string) => void;
|
|
6
|
-
onFinish: (initialDataResolved: unknown) => void;
|
|
7
|
-
onError: (error: unknown) => void;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
type StreamRender = {
|
|
11
|
-
appElement: React.JSX.Element;
|
|
12
|
-
bootstrapModules: string;
|
|
13
|
-
headContent: string;
|
|
14
|
-
getStoreSnapshot: () => unknown;
|
|
15
|
-
};
|
|
16
|
-
declare const createStreamRenderer: (serverResponse: ServerResponse, { onHead, onFinish, onError }: RenderCallbacks, { appElement, bootstrapModules, headContent, getStoreSnapshot }: StreamRender) => void;
|
|
17
|
-
|
|
18
|
-
export { createStreamRenderer };
|
package/dist/SSRRender.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
// src/SSRRender.ts
|
|
2
|
-
import { Writable } from "node:stream";
|
|
3
|
-
import "react";
|
|
4
|
-
import { renderToPipeableStream } from "react-dom/server";
|
|
5
|
-
var createStreamRenderer = (serverResponse, { onHead, onFinish, onError }, { appElement, bootstrapModules, headContent, getStoreSnapshot }) => {
|
|
6
|
-
const { pipe } = renderToPipeableStream(appElement, {
|
|
7
|
-
bootstrapModules: [bootstrapModules],
|
|
8
|
-
onShellReady() {
|
|
9
|
-
onHead(headContent);
|
|
10
|
-
pipe(
|
|
11
|
-
new Writable({
|
|
12
|
-
write(chunk, _encoding, callback) {
|
|
13
|
-
serverResponse.write(chunk, callback);
|
|
14
|
-
},
|
|
15
|
-
final(callback) {
|
|
16
|
-
onFinish(getStoreSnapshot());
|
|
17
|
-
callback();
|
|
18
|
-
}
|
|
19
|
-
})
|
|
20
|
-
);
|
|
21
|
-
},
|
|
22
|
-
onAllReady() {
|
|
23
|
-
},
|
|
24
|
-
onError(error) {
|
|
25
|
-
onError(error);
|
|
26
|
-
}
|
|
27
|
-
});
|
|
28
|
-
};
|
|
29
|
-
export {
|
|
30
|
-
createStreamRenderer
|
|
31
|
-
};
|