@ubean/pages 0.1.6 → 0.1.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/dist/index.d.ts CHANGED
@@ -29,15 +29,31 @@ interface PageRenderContext {
29
29
  * preventing `availableLocales` hydration mismatches. */
30
30
  availableLocales?: LocaleMetaInfo[];
31
31
  }
32
- type PageRenderFn = (pageObj: PageObject, shellHtml: string, assetTags: PageAssetTags, renderContext?: PageRenderContext) => string | Promise<string>;
32
+ /**
33
+ * 渲染结果。
34
+ *
35
+ * - `string`:仅 HTML(向后兼容,无 SSR state)
36
+ * - `{ html, state? }`:HTML + 可选的 SSR state(由 `defineApp.serializeState` 产生,
37
+ * 会被序列化到 `__UBEAN_STATE__` script 标签中,客户端通过 `getInitialState()` 读取)
38
+ *
39
+ * `state` 用于支持 Pinia / vue-query 等需要跨 SSR 边界传递状态的库。
40
+ */
41
+ type PageRenderResult = string | {
42
+ html: string;
43
+ state?: Record<string, unknown>;
44
+ };
45
+ type PageRenderFn = (pageObj: PageObject, shellHtml: string, assetTags: PageAssetTags, renderContext?: PageRenderContext) => PageRenderResult | Promise<PageRenderResult>;
33
46
  interface PageRenderer {
34
47
  render: PageRenderFn;
35
48
  preambleScript?: string;
36
49
  }
37
50
  declare const PAGE_DATA_ID = "__UBEAN_PAGE_DATA__";
38
51
  declare const LOCALE_DATA_ID = "__UBEAN_LOCALE__";
52
+ declare const STATE_DATA_ID = "__UBEAN_STATE__";
39
53
  declare const PAGE_REQUEST_HEADER = "x-ubeanpages";
40
54
  declare const SSR_CONTENT_MARKER = "<!--UBEAN_SSR_CONTENT-->";
55
+ /** Shell 中 state script 的占位符,render 后由 `insertStateContent` 替换为实际 JSON */
56
+ declare const STATE_MARKER = "<!--UBEAN_STATE-->";
41
57
  declare function isPagesRequest(c: {
42
58
  req: {
43
59
  header: (k: string) => string | undefined;
@@ -48,6 +64,16 @@ declare function serializePageData(pageObj: PageObject): string;
48
64
  declare function pageJsonResponse(pageObj: PageObject, headers?: Record<string, string>): Response;
49
65
  declare function buildPageShell(pageObj: PageObject, assetTags: PageAssetTags, preambleScript?: string, appId?: string, renderContext?: PageRenderContext): string;
50
66
  declare function insertSsrContent(shell: string, appHtml: string): string;
67
+ /**
68
+ * 将序列化后的 state JSON 注入 shell 中的 `__UBEAN_STATE__` script 标签。
69
+ *
70
+ * 在 `renderPage` 中于 `renderer.render()` 之后调用:renderer 返回 `{ html, state }`
71
+ * 时,把 `state` 用 `safeJsonStringify` 转义后替换 `STATE_MARKER` 占位符。
72
+ *
73
+ * 若 `stateJson` 为空字符串或 `null`,将占位符替换为空字符串(产出空 script 体,
74
+ * 客户端 `getInitialState()` 会返回 `null`)。
75
+ */
76
+ declare function insertStateContent(shell: string, stateJson: string | null): string;
51
77
  declare function buildClientOnlyShell(pageObj: PageObject, assetTags: PageAssetTags, preambleScript?: string, appId?: string, renderContext?: PageRenderContext): string;
52
78
  declare function renderPage(pageObj: PageObject, assetTags: PageAssetTags, renderer: PageRenderer | null, appId?: string, renderContext?: PageRenderContext): Promise<string>;
53
79
  //#endregion
@@ -110,4 +136,4 @@ interface StreamHelper {
110
136
  declare function createStreamResponse(init?: ResponseInit, onStart?: (stream: StreamHelper) => void | Promise<void>): Response;
111
137
  declare function createSseStream(onStart?: (stream: StreamHelper) => void | Promise<void>): Response;
112
138
  //#endregion
113
- export { type DataCacheEntry, type DataKey, type DataResult, type DependencyDeclaration, type InternalFetchOptions, LOCALE_DATA_ID, type LocaleMetaInfo, PAGE_DATA_ID, PAGE_REQUEST_HEADER, type PageAssetTags, type PageHead, type PageHeadMeta, type PageObject, type PageRenderContext, type PageRenderFn, type PageRenderer, SSR_CONTENT_MARKER, type StreamHelper, type UseDataOptions, buildClientOnlyShell, buildPageShell, clearDataCache, createInternalFetch, createSseStream, createStreamResponse, declareDependencies, defineDataKey, getInvalidatedKeysForAction, hasData, insertSsrContent, invalidateAll, invalidateData, isPagesRequest, pageJsonResponse, renderPage, safeJsonStringify, serializePageData, useData, withDependencies };
139
+ export { type DataCacheEntry, type DataKey, type DataResult, type DependencyDeclaration, type InternalFetchOptions, LOCALE_DATA_ID, type LocaleMetaInfo, PAGE_DATA_ID, PAGE_REQUEST_HEADER, type PageAssetTags, type PageHead, type PageHeadMeta, type PageObject, type PageRenderContext, type PageRenderFn, type PageRenderResult, type PageRenderer, SSR_CONTENT_MARKER, STATE_DATA_ID, STATE_MARKER, type StreamHelper, type UseDataOptions, buildClientOnlyShell, buildPageShell, clearDataCache, createInternalFetch, createSseStream, createStreamResponse, declareDependencies, defineDataKey, getInvalidatedKeysForAction, hasData, insertSsrContent, insertStateContent, invalidateAll, invalidateData, isPagesRequest, pageJsonResponse, renderPage, safeJsonStringify, serializePageData, useData, withDependencies };
package/dist/index.js CHANGED
@@ -1,8 +1,11 @@
1
1
  //#region src/protocol.ts
2
2
  const PAGE_DATA_ID = "__UBEAN_PAGE_DATA__";
3
3
  const LOCALE_DATA_ID = "__UBEAN_LOCALE__";
4
+ const STATE_DATA_ID = "__UBEAN_STATE__";
4
5
  const PAGE_REQUEST_HEADER = "x-ubeanpages";
5
6
  const SSR_CONTENT_MARKER = "<!--UBEAN_SSR_CONTENT-->";
7
+ /** Shell 中 state script 的占位符,render 后由 `insertStateContent` 替换为实际 JSON */
8
+ const STATE_MARKER = "<!--UBEAN_STATE-->";
6
9
  function isPagesRequest(c) {
7
10
  return c.req.header(PAGE_REQUEST_HEADER) === "true";
8
11
  }
@@ -83,6 +86,7 @@ function buildPageShell(pageObj, assetTags, preambleScript = "", appId = "app",
83
86
  <body${finalBodyAttrs ? ` ${finalBodyAttrs}` : ""}>
84
87
  ${localeScript}
85
88
  <script id="${PAGE_DATA_ID}" type="application/json">${pageData}<\/script>
89
+ <script id="${STATE_DATA_ID}" type="application/json">${STATE_MARKER}<\/script>
86
90
  <div id="${appId}">${SSR_CONTENT_MARKER}</div>
87
91
  ${preambleScript}
88
92
  ${bodyTags}
@@ -92,6 +96,18 @@ function buildPageShell(pageObj, assetTags, preambleScript = "", appId = "app",
92
96
  function insertSsrContent(shell, appHtml) {
93
97
  return shell.replace(SSR_CONTENT_MARKER, appHtml);
94
98
  }
99
+ /**
100
+ * 将序列化后的 state JSON 注入 shell 中的 `__UBEAN_STATE__` script 标签。
101
+ *
102
+ * 在 `renderPage` 中于 `renderer.render()` 之后调用:renderer 返回 `{ html, state }`
103
+ * 时,把 `state` 用 `safeJsonStringify` 转义后替换 `STATE_MARKER` 占位符。
104
+ *
105
+ * 若 `stateJson` 为空字符串或 `null`,将占位符替换为空字符串(产出空 script 体,
106
+ * 客户端 `getInitialState()` 会返回 `null`)。
107
+ */
108
+ function insertStateContent(shell, stateJson) {
109
+ return shell.replace(STATE_MARKER, stateJson ?? "");
110
+ }
95
111
  function buildClientOnlyShell(pageObj, assetTags, preambleScript = "", appId = "app", renderContext) {
96
112
  const pageData = serializePageData(pageObj);
97
113
  const css = assetTags.css ?? "";
@@ -123,6 +139,7 @@ function buildClientOnlyShell(pageObj, assetTags, preambleScript = "", appId = "
123
139
  <body${finalBodyAttrs ? ` ${finalBodyAttrs}` : ""}>
124
140
  ${localeScript}
125
141
  <script id="${PAGE_DATA_ID}" type="application/json">${pageData}<\/script>
142
+ <script id="${STATE_DATA_ID}" type="application/json"><\/script>
126
143
  <div id="${appId}" data-ubean-ssr="false"></div>
127
144
  ${bodyTags}
128
145
  </body>
@@ -131,9 +148,17 @@ function buildClientOnlyShell(pageObj, assetTags, preambleScript = "", appId = "
131
148
  async function renderPage(pageObj, assetTags, renderer, appId = "app", renderContext) {
132
149
  if (!renderer) return buildClientOnlyShell(pageObj, assetTags, "", appId, renderContext);
133
150
  const shell = buildPageShell(pageObj, assetTags, renderer.preambleScript ?? "", appId, renderContext);
134
- const appHtml = await renderer.render(pageObj, shell, assetTags, renderContext);
135
- if (typeof appHtml === "string" && !shell.includes(appHtml) && !appHtml.includes("<html")) return insertSsrContent(shell, appHtml);
136
- return appHtml;
151
+ const result = await renderer.render(pageObj, shell, assetTags, renderContext);
152
+ if (typeof result === "string") {
153
+ if (!shell.includes(result) && !result.includes("<html")) return insertSsrContent(shell, result);
154
+ return result;
155
+ }
156
+ const { html, state } = result;
157
+ let finalHtml = html;
158
+ if (!shell.includes(html) && !html.includes("<html")) finalHtml = insertSsrContent(shell, html);
159
+ if (state && Object.keys(state).length > 0) finalHtml = insertStateContent(finalHtml, safeJsonStringify(state));
160
+ else finalHtml = insertStateContent(finalHtml, "");
161
+ return finalHtml;
137
162
  }
138
163
  //#endregion
139
164
  //#region src/data.ts
@@ -372,4 +397,4 @@ function createSseStream(onStart) {
372
397
  } });
373
398
  }
374
399
  //#endregion
375
- export { LOCALE_DATA_ID, PAGE_DATA_ID, PAGE_REQUEST_HEADER, SSR_CONTENT_MARKER, buildClientOnlyShell, buildPageShell, clearDataCache, createInternalFetch, createSseStream, createStreamResponse, declareDependencies, defineDataKey, getInvalidatedKeysForAction, hasData, insertSsrContent, invalidateAll, invalidateData, isPagesRequest, pageJsonResponse, renderPage, safeJsonStringify, serializePageData, useData, withDependencies };
400
+ export { LOCALE_DATA_ID, PAGE_DATA_ID, PAGE_REQUEST_HEADER, SSR_CONTENT_MARKER, STATE_DATA_ID, STATE_MARKER, buildClientOnlyShell, buildPageShell, clearDataCache, createInternalFetch, createSseStream, createStreamResponse, declareDependencies, defineDataKey, getInvalidatedKeysForAction, hasData, insertSsrContent, insertStateContent, invalidateAll, invalidateData, isPagesRequest, pageJsonResponse, renderPage, safeJsonStringify, serializePageData, useData, withDependencies };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ubean/pages",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "Page data protocol and isomorphic data layer for ubean (PageObject, useData, invalidateData)",
5
5
  "files": [
6
6
  "dist"
@@ -16,11 +16,11 @@
16
16
  }
17
17
  },
18
18
  "dependencies": {
19
- "@ubean/types": "0.1.6"
19
+ "@ubean/types": "0.1.8"
20
20
  },
21
21
  "devDependencies": {
22
- "@types/node": "^26.1.1",
23
- "typescript": "6.0.3",
22
+ "@types/node": "^26.1.2",
23
+ "typescript": "7.0.2",
24
24
  "vite-plus": "0.2.6"
25
25
  },
26
26
  "scripts": {