@_tc/template-core 0.3.0 → 0.3.2

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.
@@ -27,6 +27,7 @@ This workspace is the TemplateCore npm package repository (`@_tc/template-core`)
27
27
  3. Generate the smallest working slice first: config, entry, controller, router, model.
28
28
  4. Keep keys stable. Array merge and project overrides depend on `key`.
29
29
  5. Prefer CommonJS `.js` for generated consumer examples unless the existing consumer app already uses TypeScript.
30
+ 6. If the existing consumer app declares `"type": "module"`, do not generate `.js` backend convention files with `module.exports` under that scope. Use `.ts` convention files, or add a local `package.json` with `{ "type": "commonjs" }` under `app/`, `config/`, or `models/` before generating CommonJS `.js` files.
30
31
 
31
32
  ## TemplateCore Conventions
32
33
 
package/AGENT_README.md CHANGED
@@ -124,6 +124,20 @@ models/**/*.(js|ts) -> 内置 project service 读取的项目模
124
124
  ssr/apps/**/*.entry.(js|jsx|ts|tsx) -> SSR 页面入口,可选
125
125
  ```
126
126
 
127
+ ### 3.1 后端约定文件的模块格式
128
+
129
+ `@_tc/template-core` 包本身提供 ESM/CJS 入口,使用方项目可以用 `import` 引入包。但 `config/`、`app/`、`models/` 里的后端约定文件由 loader 扫描真实文件路径后同步加载,当前按 `require()` 语义处理,并只扫描 `.js` / `.ts`。
130
+
131
+ 后端约定文件需要满足 CommonJS 加载语义:
132
+
133
+ - 普通 CommonJS 项目可以使用 `.js` + `module.exports`。
134
+ - TypeScript 项目可以使用 `.ts` + `export default`,loader 会把 `.ts` 按 CommonJS 方式转译后加载。
135
+ - 如果业务项目声明了 `"type": "module"`,不要把 `.js` 后端约定文件写成原生 ESM 作为稳定用法,也不要在该作用域下使用 `.js` + `module.exports`。
136
+ - `"type": "module"` 项目如需保留 `.js` 后端约定文件,请在 `app/`、`config/` 或 `models/` 下增加局部 `package.json`:`{ "type": "commonjs" }`。
137
+ - `.mjs`、顶层 `await`、依赖原生 ESM 语义的后端约定文件当前不作为支持约定。
138
+
139
+ 推荐分层:启动脚本、构建脚本和前端代码可以使用 ESM;`config/`、`app/`、`models/` 里的后端约定文件保持 CommonJS 加载语义。
140
+
127
141
  常用后端类型:
128
142
 
129
143
  | 类型 | 使用场景 |
@@ -524,7 +538,7 @@ await buildSSR({ baseDir, output: 'run' })
524
538
  await watchSSRFiles({ baseDir, output: 'run' })
525
539
  ```
526
540
 
527
- SSR 入口约定为 `ssr/apps/{page}/{page}.entry.tsx`,客户端 hydration 工厂从 `@_tc/template-core/ssr/createSSREntry` 引入。页面可导出静态字符串或函数式 `pageTitle` / `pageDescription`,函数会在 `getServerProps()` 后执行,入参包含 `params`、`query`、`path`、`app`、`ctx` 和 `props`。组件级流式异步数据用 `@_tc/template-core/ssr/components` 的 `StreamingRender`;底层 Suspense Promise hook 从 `@_tc/template-core/ssr/hooks` 引入。`buildSSR()` 会输出私有 `{ssrPrivateRoot}/ssr-server/{page}.mjs`、私有 `{ssrPrivateRoot}/ssr-manifest/manifest.json`,SSR hydration 入口并入 FE browser build,页面默认通过 `/fessr/:page` 访问,浏览器静态资源走普通 `/dist/assets/*`;`/dist/ssr-server/*` 和 `/dist/.vite/*` 会被拦截为 404。成功 SSR 会用 React streaming bootstrap 动态 import 客户端入口,带 `Suspense` 边界的 shell 可以先启动 hydration,pending 内容继续 stream。入口文件会被 server/client 两次构建,顶层代码必须同时兼容 Node 和浏览器。`getServerProps()` 运行在 Node 侧,不要直接复用依赖 `window` / `localStorage` 的前端 API 层;服务端请求逻辑拆到 `*.server.ts` 并在 `getServerProps()` 内动态导入。`StreamingRender` 的 `getData()` 返回值会写入 HTML,不能包含 token、密钥等敏感信息;客户端找不到注入数据时也可能执行 `getData()`,需要兼容浏览器 fallback。
541
+ SSR 入口约定为 `ssr/apps/{page}/{page}.entry.tsx`,客户端 hydration 工厂从 `@_tc/template-core/ssr/createSSREntry` 引入。页面可导出静态字符串或函数式 `pageTitle` / `pageDescription`,函数会在 `getServerProps()` 后执行,入参包含 `params`、`query`、`path`、`app`、`ctx` 和 `props`。组件级流式异步数据用 `@_tc/template-core/ssr/components` 的 `StreamingRender`;底层 Suspense Promise hook 从 `@_tc/template-core/ssr/hooks` 引入。`buildSSR()` 会输出私有 `{ssrPrivateRoot}/ssr-server/{page}.mjs`、私有 `{ssrPrivateRoot}/ssr-manifest/manifest.json`,SSR hydration 入口并入 FE browser build,页面默认通过 `/fessr/:page` 访问,浏览器静态资源走普通 `/dist/assets/*`;`/dist/ssr-server/*` 和 `/dist/.vite/*` 会被拦截为 404。成功 SSR 会用 React streaming bootstrap 动态 import 客户端入口,带 `Suspense` 边界的 shell 可以先启动 hydration,pending 内容继续 stream。入口文件会被 server/client 两次构建,顶层代码必须同时兼容 Node 和浏览器。`getServerProps()` 运行在 Node 侧,不要直接复用依赖 `window` / `localStorage` 的前端 API 层;服务端请求逻辑拆到 `*.server.ts` 并在 `getServerProps()` 内动态导入。`StreamingRender` 的 `getData()` 返回值会 JSON 序列化后以 base64 写入 HTML;base64 可逆,不等于加密,不能包含 token、密钥等敏感信息;客户端找不到注入数据时也可能执行 `getData()`,需要兼容浏览器 fallback。
528
542
 
529
543
  Node/backend 构建:
530
544
 
package/CHANGE.md CHANGED
@@ -13,6 +13,7 @@ TemplateCore v0.3 的首个版本,核心是正式引入 SSR V1。这个版本
13
13
  - SSR / CSR HTML shell 会注入 `window._renderMode` 和 `window._isSSR`,Dash 共用路由守卫可按渲染模式分别处理跳转。
14
14
  - 新增 SSR document shell 与错误 fallback,非法页面、bundle 缺失、render / getServerProps 异常都会返回统一 HTML 错误页。
15
15
  - SSR 成功渲染支持 shell 到达后提前启动客户端入口;带 `Suspense` 边界的 pending 内容可继续流式补齐。
16
+ - `StreamingRender` 组件级流式数据注入改为 JSON 序列化后 base64 写入 script,避免 HTML 中直接暴露明文 JSON。
16
17
 
17
18
  ## 构建与产物
18
19
 
package/README.md CHANGED
@@ -184,7 +184,7 @@ SSR 运行时会通过 React streaming bootstrap 动态 import 客户端入口
184
184
 
185
185
  `pageTitle` / `pageDescription` 可以导出字符串,也可以导出同步或异步函数。函数会在 `getServerProps()` 之后执行,入参包含 `params`、`query`、`path`、`app`、`ctx` 和 `props`。
186
186
 
187
- 组件级流式异步数据可以使用 `StreamingRender`。它会在服务端 Suspense 中等待 `getData()`,把结果随 HTML 片段用 JSON script 注入,客户端 hydration 时优先复用这份数据。
187
+ 组件级流式异步数据可以使用 `StreamingRender`。它会在服务端 Suspense 中等待 `getData()`,把结果 JSON 序列化后转成 base64,再随 HTML 片段写入 script;客户端 hydration 时会先解码并复用这份数据。
188
188
 
189
189
  ```tsx
190
190
  import { StreamingRender } from '@_tc/template-core/ssr/components'
@@ -202,7 +202,7 @@ function OverviewPanel() {
202
202
  }
203
203
  ```
204
204
 
205
- `keyName` 需要在页面内稳定且唯一;`getData()` 的返回值必须可 JSON 序列化,并且会进入 HTML,不能包含 token、密钥等敏感信息。客户端找不到对应注入数据时也可能执行 `getData()`,因此数据函数需要兼容浏览器 fallback,或在函数内部显式区分服务端和客户端逻辑。底层 Suspense Promise hook 可从 `@_tc/template-core/ssr/hooks` 或 `@_tc/template-core/ssr/hooks/useSuspensePromise` 引入。
205
+ `keyName` 需要在页面内稳定且唯一;`getData()` 的返回值必须可 JSON 序列化。注入内容会以 base64 形式进入 HTML,避免明文 JSON,但 base64 可逆,不等于加密,不能包含 token、密钥等敏感信息。客户端找不到对应注入数据时也可能执行 `getData()`,因此数据函数需要兼容浏览器 fallback,或在函数内部显式区分服务端和客户端逻辑。底层 Suspense Promise hook 可从 `@_tc/template-core/ssr/hooks` 或 `@_tc/template-core/ssr/hooks/useSuspensePromise` 引入。
206
206
 
207
207
  启动时构建 SSR 双产物:
208
208
 
@@ -656,6 +656,20 @@ config/config.{env}.(js|ts) -> app.config
656
656
  models/**/*.(js|ts) -> 内置 project service 按需读取的项目模型配置
657
657
  ```
658
658
 
659
+ ### 后端约定文件的模块格式
660
+
661
+ TemplateCore 包本身提供 ESM/CJS 入口,业务项目可以用 `import` 引入包。但上面这些后端约定文件是由 loader 扫描真实文件路径后同步加载的,当前按 `require()` 语义处理,并只扫描 `.js` / `.ts`。
662
+
663
+ 因此后端约定文件需要满足 CommonJS 加载语义:
664
+
665
+ - 普通 CommonJS 项目可以使用 `.js` + `module.exports`。
666
+ - TypeScript 项目可以使用 `.ts` + `export default`,loader 会把 `.ts` 按 CommonJS 方式转译后加载。
667
+ - 如果业务项目声明了 `"type": "module"`,不要把 `.js` 后端约定文件写成原生 ESM 作为稳定用法,也不要在该作用域下使用 `.js` + `module.exports`。
668
+ - `"type": "module"` 项目如需保留 `.js` 后端约定文件,请在 `app/`、`config/` 或 `models/` 下增加局部 `package.json`:`{ "type": "commonjs" }`。
669
+ - `.mjs`、顶层 `await`、依赖原生 ESM 语义的后端约定文件当前不作为支持约定。
670
+
671
+ 推荐分层:启动脚本、构建脚本和前端代码可以使用 ESM;`config/`、`app/`、`models/` 里的后端约定文件保持 CommonJS 加载语义。
672
+
659
673
  内置扩展:
660
674
 
661
675
  - `app.extends.$fetch`:Node 侧基于 `fetch` 的 axios 风格请求实例,支持 `get/post/put/patch/delete` 和 `create(config)`。
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./i18n/default.js`),t=require(`./i18n/en-US.js`),n=require(`./i18n/locales.js`),r=require(`./i18n/index.js`),i=require(`./guards/index.js`),a=require(`./array/index.js`),o=require(`./cache/LRUCache.js`),s=require(`./http/index.js`),c=require(`./log/index.js`),l=require(`./number/index.js`),u=require(`./object/filterEmpty.js`),d=require(`./object/index.js`),f=require(`./rafTimer.js`),p=require(`./ssr/hydration.js`),m=require(`./ssr/index.js`),h=require(`./string/index.js`);exports.ANSI_RESET=c.ANSI_RESET,exports.FetchAxios=s.FetchAxios,exports.LRUCache=o.LRUCache,exports.SSR_DATA_KEY=p.SSR_DATA_KEY,exports.ansiColors=c.ansiColors,exports.axios=s.axios,exports.capitalize=h.capitalize,exports.chunk=a.chunk,exports.clamp=l.clamp,exports.clearHydrationData=p.clearHydrationData,exports.clearRafTimer=f.clearRafTimer,exports.clientOnly=m.clientOnly,exports.colorLog=c.colorLog,exports.colorize=c.colorize,exports.compact=a.compact,exports.createHydrationData=p.createHydrationData,exports.createInstance=s.createInstance,exports.defaultEnglishResources=t.defaultEnglishResources,exports.defaultLanguage=n.defaultLanguage,exports.defaultLanguageResources=e.defaultLanguageResources,exports.filterEmpty=u.filterEmpty,exports.filtereEmpty=u.filtereEmpty,exports.getI18nPathValue=r.getI18nPathValue,exports.getLanguage=r.getLanguage,exports.groupBy=a.groupBy,exports.i18n=r.i18n,exports.i18nStore=r.i18nStore,exports.interpolateI18nMessage=r.interpolateI18nMessage,exports.isBlank=h.isBlank,exports.isBoolean=i.isBoolean,exports.isBrowser=m.isBrowser,exports.isFunction=i.isFunction,exports.isNil=i.isNil,exports.isNonNullable=i.isNonNullable,exports.isNumber=i.isNumber,exports.isPlainI18nDictionary=r.isPlainI18nDictionary,exports.isPlainObject=i.isPlainObject,exports.isServer=m.isServer,exports.isString=i.isString,exports.joinColorized=c.joinColorized,exports.joinStr=h.joinStr,exports.kebabCase=h.kebabCase,exports.languageLocalKey=n.languageLocalKey,exports.logColor=c.logColor,exports.logColorized=c.logColorized,exports.logGreen=c.logGreen,exports.logJoinColorized=c.logJoinColorized,exports.logPink=c.logPink,exports.logRed=c.logRed,exports.logWhite=c.logWhite,exports.logYellow=c.logYellow,exports.mapValues=d.mapValues,exports.mergeI18nDictionary=r.mergeI18nDictionary,exports.mergeI18nResources=r.mergeI18nResources,exports.omit=d.omit,exports.peekHydrationData=p.peekHydrationData,exports.pick=d.pick,exports.rafClearInterval=f.rafClearInterval,exports.rafClearTimeout=f.rafClearTimeout,exports.rafSetInterval=f.rafSetInterval,exports.rafSetTimeout=f.rafSetTimeout,exports.readHydrationData=p.readHydrationData,exports.safeDocument=m.safeDocument,exports.safeLocalStorage=m.safeLocalStorage,exports.safeSetLocalStorage=m.safeSetLocalStorage,exports.safeWindow=m.safeWindow,exports.serializeHydrationData=p.serializeHydrationData,exports.serializeHydrationDataScriptContent=p.serializeHydrationDataScriptContent,exports.t=r.t,exports.toArray=a.toArray,exports.toFiniteNumber=l.toFiniteNumber,exports.translate=r.translate,exports.translations=n.translations,exports.uniqueBy=a.uniqueBy;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./i18n/default.js`),t=require(`./i18n/en-US.js`),n=require(`./i18n/locales.js`),r=require(`./i18n/index.js`),i=require(`./guards/index.js`),a=require(`./array/index.js`),o=require(`./cache/LRUCache.js`),s=require(`./http/index.js`),c=require(`./log/index.js`),l=require(`./number/index.js`),u=require(`./object/filterEmpty.js`),d=require(`./object/index.js`),f=require(`./rafTimer.js`),p=require(`./ssr/hydration.js`),m=require(`./ssr/index.js`),h=require(`./string/index.js`);exports.ANSI_RESET=c.ANSI_RESET,exports.FetchAxios=s.FetchAxios,exports.LRUCache=o.LRUCache,exports.SSR_DATA_KEY=p.SSR_DATA_KEY,exports.ansiColors=c.ansiColors,exports.axios=s.axios,exports.capitalize=h.capitalize,exports.chunk=a.chunk,exports.clamp=l.clamp,exports.clearHydrationData=p.clearHydrationData,exports.clearRafTimer=f.clearRafTimer,exports.clientOnly=m.clientOnly,exports.colorLog=c.colorLog,exports.colorize=c.colorize,exports.compact=a.compact,exports.createHydrationData=p.createHydrationData,exports.createInstance=s.createInstance,exports.decodeBase64ToUtf8=p.decodeBase64ToUtf8,exports.defaultEnglishResources=t.defaultEnglishResources,exports.defaultLanguage=n.defaultLanguage,exports.defaultLanguageResources=e.defaultLanguageResources,exports.encodeUtf8ToBase64=p.encodeUtf8ToBase64,exports.filterEmpty=u.filterEmpty,exports.filtereEmpty=u.filtereEmpty,exports.getI18nPathValue=r.getI18nPathValue,exports.getLanguage=r.getLanguage,exports.groupBy=a.groupBy,exports.i18n=r.i18n,exports.i18nStore=r.i18nStore,exports.interpolateI18nMessage=r.interpolateI18nMessage,exports.isBlank=h.isBlank,exports.isBoolean=i.isBoolean,exports.isBrowser=m.isBrowser,exports.isFunction=i.isFunction,exports.isNil=i.isNil,exports.isNonNullable=i.isNonNullable,exports.isNumber=i.isNumber,exports.isPlainI18nDictionary=r.isPlainI18nDictionary,exports.isPlainObject=i.isPlainObject,exports.isServer=m.isServer,exports.isString=i.isString,exports.joinColorized=c.joinColorized,exports.joinStr=h.joinStr,exports.kebabCase=h.kebabCase,exports.languageLocalKey=n.languageLocalKey,exports.logColor=c.logColor,exports.logColorized=c.logColorized,exports.logGreen=c.logGreen,exports.logJoinColorized=c.logJoinColorized,exports.logPink=c.logPink,exports.logRed=c.logRed,exports.logWhite=c.logWhite,exports.logYellow=c.logYellow,exports.mapValues=d.mapValues,exports.mergeI18nDictionary=r.mergeI18nDictionary,exports.mergeI18nResources=r.mergeI18nResources,exports.omit=d.omit,exports.peekHydrationData=p.peekHydrationData,exports.pick=d.pick,exports.rafClearInterval=f.rafClearInterval,exports.rafClearTimeout=f.rafClearTimeout,exports.rafSetInterval=f.rafSetInterval,exports.rafSetTimeout=f.rafSetTimeout,exports.readHydrationData=p.readHydrationData,exports.safeDocument=m.safeDocument,exports.safeLocalStorage=m.safeLocalStorage,exports.safeSetLocalStorage=m.safeSetLocalStorage,exports.safeWindow=m.safeWindow,exports.serializeHydrationData=p.serializeHydrationData,exports.serializeHydrationDataScriptContent=p.serializeHydrationDataScriptContent,exports.t=r.t,exports.toArray=a.toArray,exports.toFiniteNumber=l.toFiniteNumber,exports.translate=r.translate,exports.translations=n.translations,exports.uniqueBy=a.uniqueBy;
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=`__SSR_DATA__`;function t(){return globalThis.Buffer}function n(e){let n=t();if(n)return n.from(e,`utf-8`).toString(`base64`);let r=new TextEncoder().encode(e),i=``;return r.forEach(e=>{i+=String.fromCharCode(e)}),btoa(i)}function r(e){let n=t();if(n)return n.from(e,`base64`).toString(`utf-8`);let r=atob(e),i=Uint8Array.from(r,e=>e.charCodeAt(0));return new TextDecoder().decode(i)}function i(e){return typeof e==`string`?JSON.parse(r(e)):e}function a(t){let r=n(JSON.stringify(t));return`window.${e}=${JSON.stringify(r)}`}function o(e){return`<script>${a(e)}<\/script>`}function s(){if(typeof window>`u`)return null;let t=window[e];if(!t)return null;let n=i(t);return window[e]=n,n}function c(){typeof window>`u`||delete window[e]}function l(){let e=s();return e?(c(),e):null}function u(e,t,n){return{props:e,state:n??{},meta:{...t,renderedAt:Date.now()}}}exports.SSR_DATA_KEY=e,exports.clearHydrationData=c,exports.createHydrationData=u,exports.peekHydrationData=s,exports.readHydrationData=l,exports.serializeHydrationData=o,exports.serializeHydrationDataScriptContent=a;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=`__SSR_DATA__`;function t(){return globalThis.Buffer}function n(e){let n=t();if(n)return n.from(e,`utf-8`).toString(`base64`);let r=new TextEncoder().encode(e),i=``;return r.forEach(e=>{i+=String.fromCharCode(e)}),btoa(i)}function r(e){let n=t();if(n)return n.from(e,`base64`).toString(`utf-8`);let r=atob(e),i=Uint8Array.from(r,e=>e.charCodeAt(0));return new TextDecoder().decode(i)}function i(e){return typeof e==`string`?JSON.parse(r(e)):e}function a(t){let r=n(JSON.stringify(t));return`window.${e}=${JSON.stringify(r)}`}function o(e){return`<script>${a(e)}<\/script>`}function s(){if(typeof window>`u`)return null;let t=window[e];if(!t)return null;let n=i(t);return window[e]=n,n}function c(){typeof window>`u`||delete window[e]}function l(){let e=s();return e?(c(),e):null}function u(e,t,n){return{props:e,state:n??{},meta:{...t,renderedAt:Date.now()}}}exports.SSR_DATA_KEY=e,exports.clearHydrationData=c,exports.createHydrationData=u,exports.decodeBase64ToUtf8=r,exports.encodeUtf8ToBase64=n,exports.peekHydrationData=s,exports.readHydrationData=l,exports.serializeHydrationData=o,exports.serializeHydrationDataScriptContent=a;
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./hydration.js`);var t=typeof window<`u`&&typeof document<`u`,n=!t;function r(e,t){return n?t:e()}function i(){if(!n)return window}function a(){if(!n)return document}function o(e,t=``){return n?t:localStorage.getItem(e)??t}function s(e,t){n||localStorage.setItem(e,t)}exports.SSR_DATA_KEY=e.SSR_DATA_KEY,exports.clearHydrationData=e.clearHydrationData,exports.clientOnly=r,exports.createHydrationData=e.createHydrationData,exports.isBrowser=t,exports.isServer=n,exports.peekHydrationData=e.peekHydrationData,exports.readHydrationData=e.readHydrationData,exports.safeDocument=a,exports.safeLocalStorage=o,exports.safeSetLocalStorage=s,exports.safeWindow=i,exports.serializeHydrationData=e.serializeHydrationData,exports.serializeHydrationDataScriptContent=e.serializeHydrationDataScriptContent;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./hydration.js`);var t=typeof window<`u`&&typeof document<`u`,n=!t;function r(e,t){return n?t:e()}function i(){if(!n)return window}function a(){if(!n)return document}function o(e,t=``){return n?t:localStorage.getItem(e)??t}function s(e,t){n||localStorage.setItem(e,t)}exports.SSR_DATA_KEY=e.SSR_DATA_KEY,exports.clearHydrationData=e.clearHydrationData,exports.clientOnly=r,exports.createHydrationData=e.createHydrationData,exports.decodeBase64ToUtf8=e.decodeBase64ToUtf8,exports.encodeUtf8ToBase64=e.encodeUtf8ToBase64,exports.isBrowser=t,exports.isServer=n,exports.peekHydrationData=e.peekHydrationData,exports.readHydrationData=e.readHydrationData,exports.safeDocument=a,exports.safeLocalStorage=o,exports.safeSetLocalStorage=s,exports.safeWindow=i,exports.serializeHydrationData=e.serializeHydrationData,exports.serializeHydrationDataScriptContent=e.serializeHydrationDataScriptContent;
@@ -11,7 +11,7 @@ import { clamp as W, toFiniteNumber as G } from "./number/index.js";
11
11
  import { filterEmpty as K, filtereEmpty as q } from "./object/filterEmpty.js";
12
12
  import { mapValues as J, omit as Y, pick as X } from "./object/index.js";
13
13
  import { clearRafTimer as Z, rafClearInterval as Q, rafClearTimeout as $, rafSetInterval as ee, rafSetTimeout as te } from "./rafTimer.js";
14
- import { SSR_DATA_KEY as ne, clearHydrationData as re, createHydrationData as ie, peekHydrationData as ae, readHydrationData as oe, serializeHydrationData as se, serializeHydrationDataScriptContent as ce } from "./ssr/hydration.js";
15
- import { clientOnly as le, isBrowser as ue, isServer as de, safeDocument as fe, safeLocalStorage as pe, safeSetLocalStorage as me, safeWindow as he } from "./ssr/index.js";
16
- import { capitalize as ge, isBlank as _e, joinStr as ve, kebabCase as ye } from "./string/index.js";
17
- export { j as ANSI_RESET, O as FetchAxios, D as LRUCache, ne as SSR_DATA_KEY, M as ansiColors, k as axios, ge as capitalize, S as chunk, W as clamp, re as clearHydrationData, Z as clearRafTimer, le as clientOnly, N as colorLog, P as colorize, C as compact, ie as createHydrationData, A as createInstance, t as defaultEnglishResources, n as defaultLanguage, e as defaultLanguageResources, K as filterEmpty, q as filtereEmpty, a as getI18nPathValue, o as getLanguage, w as groupBy, s as i18n, c as i18nStore, l as interpolateI18nMessage, _e as isBlank, h as isBoolean, ue as isBrowser, g as isFunction, _ as isNil, v as isNonNullable, y as isNumber, u as isPlainI18nDictionary, b as isPlainObject, de as isServer, x as isString, F as joinColorized, ve as joinStr, ye as kebabCase, r as languageLocalKey, I as logColor, L as logColorized, R as logGreen, z as logJoinColorized, B as logPink, V as logRed, H as logWhite, U as logYellow, J as mapValues, d as mergeI18nDictionary, f as mergeI18nResources, Y as omit, ae as peekHydrationData, X as pick, Q as rafClearInterval, $ as rafClearTimeout, ee as rafSetInterval, te as rafSetTimeout, oe as readHydrationData, fe as safeDocument, pe as safeLocalStorage, me as safeSetLocalStorage, he as safeWindow, se as serializeHydrationData, ce as serializeHydrationDataScriptContent, p as t, T as toArray, G as toFiniteNumber, m as translate, i as translations, E as uniqueBy };
14
+ import { SSR_DATA_KEY as ne, clearHydrationData as re, createHydrationData as ie, decodeBase64ToUtf8 as ae, encodeUtf8ToBase64 as oe, peekHydrationData as se, readHydrationData as ce, serializeHydrationData as le, serializeHydrationDataScriptContent as ue } from "./ssr/hydration.js";
15
+ import { clientOnly as de, isBrowser as fe, isServer as pe, safeDocument as me, safeLocalStorage as he, safeSetLocalStorage as ge, safeWindow as _e } from "./ssr/index.js";
16
+ import { capitalize as ve, isBlank as ye, joinStr as be, kebabCase as xe } from "./string/index.js";
17
+ export { j as ANSI_RESET, O as FetchAxios, D as LRUCache, ne as SSR_DATA_KEY, M as ansiColors, k as axios, ve as capitalize, S as chunk, W as clamp, re as clearHydrationData, Z as clearRafTimer, de as clientOnly, N as colorLog, P as colorize, C as compact, ie as createHydrationData, A as createInstance, ae as decodeBase64ToUtf8, t as defaultEnglishResources, n as defaultLanguage, e as defaultLanguageResources, oe as encodeUtf8ToBase64, K as filterEmpty, q as filtereEmpty, a as getI18nPathValue, o as getLanguage, w as groupBy, s as i18n, c as i18nStore, l as interpolateI18nMessage, ye as isBlank, h as isBoolean, fe as isBrowser, g as isFunction, _ as isNil, v as isNonNullable, y as isNumber, u as isPlainI18nDictionary, b as isPlainObject, pe as isServer, x as isString, F as joinColorized, be as joinStr, xe as kebabCase, r as languageLocalKey, I as logColor, L as logColorized, R as logGreen, z as logJoinColorized, B as logPink, V as logRed, H as logWhite, U as logYellow, J as mapValues, d as mergeI18nDictionary, f as mergeI18nResources, Y as omit, se as peekHydrationData, X as pick, Q as rafClearInterval, $ as rafClearTimeout, ee as rafSetInterval, te as rafSetTimeout, ce as readHydrationData, me as safeDocument, he as safeLocalStorage, ge as safeSetLocalStorage, _e as safeWindow, le as serializeHydrationData, ue as serializeHydrationDataScriptContent, p as t, T as toArray, G as toFiniteNumber, m as translate, i as translations, E as uniqueBy };
@@ -52,4 +52,4 @@ function u(e, t, n) {
52
52
  };
53
53
  }
54
54
  //#endregion
55
- export { e as SSR_DATA_KEY, c as clearHydrationData, u as createHydrationData, s as peekHydrationData, l as readHydrationData, o as serializeHydrationData, a as serializeHydrationDataScriptContent };
55
+ export { e as SSR_DATA_KEY, c as clearHydrationData, u as createHydrationData, r as decodeBase64ToUtf8, n as encodeUtf8ToBase64, s as peekHydrationData, l as readHydrationData, o as serializeHydrationData, a as serializeHydrationDataScriptContent };
@@ -1,20 +1,20 @@
1
- import { SSR_DATA_KEY as e, clearHydrationData as t, createHydrationData as n, peekHydrationData as r, readHydrationData as i, serializeHydrationData as a, serializeHydrationDataScriptContent as o } from "./hydration.js";
1
+ import { SSR_DATA_KEY as e, clearHydrationData as t, createHydrationData as n, decodeBase64ToUtf8 as r, encodeUtf8ToBase64 as i, peekHydrationData as a, readHydrationData as o, serializeHydrationData as s, serializeHydrationDataScriptContent as c } from "./hydration.js";
2
2
  //#region packages/common/ssr/index.ts
3
- var s = typeof window < "u" && typeof document < "u", c = !s;
4
- function l(e, t) {
5
- return c ? t : e();
3
+ var l = typeof window < "u" && typeof document < "u", u = !l;
4
+ function d(e, t) {
5
+ return u ? t : e();
6
6
  }
7
- function u() {
8
- if (!c) return window;
7
+ function f() {
8
+ if (!u) return window;
9
9
  }
10
- function d() {
11
- if (!c) return document;
10
+ function p() {
11
+ if (!u) return document;
12
12
  }
13
- function f(e, t = "") {
14
- return c ? t : localStorage.getItem(e) ?? t;
13
+ function m(e, t = "") {
14
+ return u ? t : localStorage.getItem(e) ?? t;
15
15
  }
16
- function p(e, t) {
17
- c || localStorage.setItem(e, t);
16
+ function h(e, t) {
17
+ u || localStorage.setItem(e, t);
18
18
  }
19
19
  //#endregion
20
- export { e as SSR_DATA_KEY, t as clearHydrationData, l as clientOnly, n as createHydrationData, s as isBrowser, c as isServer, r as peekHydrationData, i as readHydrationData, d as safeDocument, f as safeLocalStorage, p as safeSetLocalStorage, u as safeWindow, a as serializeHydrationData, o as serializeHydrationDataScriptContent };
20
+ export { e as SSR_DATA_KEY, t as clearHydrationData, d as clientOnly, n as createHydrationData, r as decodeBase64ToUtf8, i as encodeUtf8ToBase64, l as isBrowser, u as isServer, a as peekHydrationData, o as readHydrationData, p as safeDocument, m as safeLocalStorage, h as safeSetLocalStorage, f as safeWindow, s as serializeHydrationData, c as serializeHydrationDataScriptContent };
@@ -1,5 +1,5 @@
1
1
  import type { MergeType, MOmit } from "../../../../typings/type";
2
- import { FetchInfo } from "../../../../models/types/data/fetchInfo";
2
+ import type { FetchInfo } from "../../../../models/types/data/fetchInfo";
3
3
  import { SelectProps } from "../../../../packages/react/ui/index";
4
4
  export interface AsyncSelectProps extends MergeType<[MOmit<FetchInfo, 'fetchKey'>, MOmit<SelectProps, 'options'>]> {
5
5
  }
@@ -76,8 +76,9 @@ var PopForm = memo((props) => {
76
76
  /**
77
77
  * 编辑弹窗需要 获取一下数据
78
78
  */
79
- if (props.comName === "editForm" && "fetchKey" in config) {
80
- const { fetchKey } = config;
79
+ if (props.comName === "editForm") {
80
+ const fetchKey = config?.fetchKey;
81
+ if (!fetchKey) return;
81
82
  const params = { [fetchKey]: props.data[fetchKey] };
82
83
  const { url = api, method = "get" } = config?.fetchConfig ?? {};
83
84
  setLoading(true);
@@ -11,7 +11,7 @@ import { clamp, toFiniteNumber } from "./number/index.js";
11
11
  import { filterEmpty, filtereEmpty } from "./object/filterEmpty.js";
12
12
  import { mapValues, omit, pick } from "./object/index.js";
13
13
  import { clearRafTimer, rafClearInterval, rafClearTimeout, rafSetInterval, rafSetTimeout } from "./rafTimer.js";
14
- import { SSR_DATA_KEY, clearHydrationData, createHydrationData, peekHydrationData, readHydrationData, serializeHydrationData, serializeHydrationDataScriptContent } from "./ssr/hydration.js";
14
+ import { SSR_DATA_KEY, clearHydrationData, createHydrationData, decodeBase64ToUtf8, encodeUtf8ToBase64, peekHydrationData, readHydrationData, serializeHydrationData, serializeHydrationDataScriptContent } from "./ssr/hydration.js";
15
15
  import { clientOnly, isBrowser, isServer, safeDocument, safeLocalStorage, safeSetLocalStorage, safeWindow } from "./ssr/index.js";
16
16
  import { capitalize, isBlank, joinStr, kebabCase } from "./string/index.js";
17
- export { ANSI_RESET, FetchAxios, LRUCache, SSR_DATA_KEY, ansiColors, axios, capitalize, chunk, clamp, clearHydrationData, clearRafTimer, clientOnly, colorLog, colorize, compact, createHydrationData, createInstance, defaultEnglishResources, defaultLanguage, defaultLanguageResources, filterEmpty, filtereEmpty, getI18nPathValue, getLanguage, groupBy, i18n, i18nStore, interpolateI18nMessage, isBlank, isBoolean, isBrowser, isFunction, isNil, isNonNullable, isNumber, isPlainI18nDictionary, isPlainObject, isServer, isString, joinColorized, joinStr, kebabCase, languageLocalKey, logColor, logColorized, logGreen, logJoinColorized, logPink, logRed, logWhite, logYellow, mapValues, mergeI18nDictionary, mergeI18nResources, omit, peekHydrationData, pick, rafClearInterval, rafClearTimeout, rafSetInterval, rafSetTimeout, readHydrationData, safeDocument, safeLocalStorage, safeSetLocalStorage, safeWindow, serializeHydrationData, serializeHydrationDataScriptContent, t, toArray, toFiniteNumber, translate, translations, uniqueBy };
17
+ export { ANSI_RESET, FetchAxios, LRUCache, SSR_DATA_KEY, ansiColors, axios, capitalize, chunk, clamp, clearHydrationData, clearRafTimer, clientOnly, colorLog, colorize, compact, createHydrationData, createInstance, decodeBase64ToUtf8, defaultEnglishResources, defaultLanguage, defaultLanguageResources, encodeUtf8ToBase64, filterEmpty, filtereEmpty, getI18nPathValue, getLanguage, groupBy, i18n, i18nStore, interpolateI18nMessage, isBlank, isBoolean, isBrowser, isFunction, isNil, isNonNullable, isNumber, isPlainI18nDictionary, isPlainObject, isServer, isString, joinColorized, joinStr, kebabCase, languageLocalKey, logColor, logColorized, logGreen, logJoinColorized, logPink, logRed, logWhite, logYellow, mapValues, mergeI18nDictionary, mergeI18nResources, omit, peekHydrationData, pick, rafClearInterval, rafClearTimeout, rafSetInterval, rafSetTimeout, readHydrationData, safeDocument, safeLocalStorage, safeSetLocalStorage, safeWindow, serializeHydrationData, serializeHydrationDataScriptContent, t, toArray, toFiniteNumber, translate, translations, uniqueBy };
@@ -15,6 +15,8 @@ declare global {
15
15
  [SSR_DATA_KEY]?: SSRHydrationData | string;
16
16
  }
17
17
  }
18
+ export declare function encodeUtf8ToBase64(value: string): string;
19
+ export declare function decodeBase64ToUtf8(value: string): string;
18
20
  export declare function serializeHydrationDataScriptContent(data: SSRHydrationData): string;
19
21
  export declare function serializeHydrationData(data: SSRHydrationData): string;
20
22
  export declare function peekHydrationData<Props = Record<string, unknown>, State = Record<string, unknown>>(): SSRHydrationData<Props, State> | null;
@@ -85,4 +85,4 @@ function createHydrationData(props, meta, state) {
85
85
  };
86
86
  }
87
87
  //#endregion
88
- export { SSR_DATA_KEY, clearHydrationData, createHydrationData, peekHydrationData, readHydrationData, serializeHydrationData, serializeHydrationDataScriptContent };
88
+ export { SSR_DATA_KEY, clearHydrationData, createHydrationData, decodeBase64ToUtf8, encodeUtf8ToBase64, peekHydrationData, readHydrationData, serializeHydrationData, serializeHydrationDataScriptContent };
@@ -1,4 +1,4 @@
1
- import { SSR_DATA_KEY, clearHydrationData, createHydrationData, peekHydrationData, readHydrationData, serializeHydrationData, serializeHydrationDataScriptContent } from "./hydration.js";
1
+ import { SSR_DATA_KEY, clearHydrationData, createHydrationData, decodeBase64ToUtf8, encodeUtf8ToBase64, peekHydrationData, readHydrationData, serializeHydrationData, serializeHydrationDataScriptContent } from "./hydration.js";
2
2
  //#region packages/common/ssr/index.ts
3
3
  /**
4
4
  * SSR 环境检测与守卫工具
@@ -49,4 +49,4 @@ function safeSetLocalStorage(key, value) {
49
49
  localStorage.setItem(key, value);
50
50
  }
51
51
  //#endregion
52
- export { SSR_DATA_KEY, clearHydrationData, clientOnly, createHydrationData, isBrowser, isServer, peekHydrationData, readHydrationData, safeDocument, safeLocalStorage, safeSetLocalStorage, safeWindow, serializeHydrationData, serializeHydrationDataScriptContent };
52
+ export { SSR_DATA_KEY, clearHydrationData, clientOnly, createHydrationData, decodeBase64ToUtf8, encodeUtf8ToBase64, isBrowser, isServer, peekHydrationData, readHydrationData, safeDocument, safeLocalStorage, safeSetLocalStorage, safeWindow, serializeHydrationData, serializeHydrationDataScriptContent };
@@ -28,10 +28,10 @@ var RenderCom = (props) => {
28
28
  /**
29
29
  * 组件级流式异步数据渲染组件。
30
30
  *
31
- * - 服务端通过 Suspense 等待 getData(),并把结果注入到 HTML script 中。
31
+ * - 服务端通过 Suspense 等待 getData(),并把结果 JSON 序列化后以 base64 注入到 HTML script 中。
32
32
  * - 客户端 hydration 优先读取服务端注入数据,读取失败时才会走 getData() fallback。
33
33
  * - keyName 需要同请求内稳定;多请求数据可能不同时,应拼入非敏感的请求维度。
34
- * - getData() 返回值会进入 HTML,不能包含 token、密钥、未脱敏个人信息等敏感数据。
34
+ * - getData() 返回值会以 base64 进入 HTML;base64 可逆,不能包含 token、密钥、未脱敏个人信息等敏感数据。
35
35
  */
36
36
  var StreamingRender = (props) => {
37
37
  const { fallback, name, ...ext } = props;
@@ -1,3 +1,4 @@
1
+ import { decodeBase64ToUtf8, encodeUtf8ToBase64 } from "../../../packages/common/ssr/hydration.js";
1
2
  import { useEffect, useState } from "react";
2
3
  import { jsx } from "react/jsx-runtime";
3
4
  //#region ssr/components/StreamingRender/StreamingScript.tsx
@@ -14,11 +15,11 @@ var StreamingRenderGetDataScript = (props) => {
14
15
  const timer = setTimeout(() => setHidden(true), 3e3);
15
16
  return () => clearTimeout(timer);
16
17
  }, []);
17
- const json = JSON.stringify(data);
18
+ const encodedData = encodeUtf8ToBase64(JSON.stringify(data) ?? "null");
18
19
  return !hidden && /* @__PURE__ */ jsx("script", {
19
20
  type: "application/json",
20
21
  id: getStreamingRenderGetDataScriptId(keyName),
21
- dangerouslySetInnerHTML: { __html: (json ?? "null").replace(/</g, "\\u003c") }
22
+ dangerouslySetInnerHTML: { __html: JSON.stringify(encodedData) }
22
23
  });
23
24
  };
24
25
  var clientDataCache = /* @__PURE__ */ new Map();
@@ -43,7 +44,12 @@ var readClientStreamData = (key) => {
43
44
  data: null
44
45
  };
45
46
  const dataStr = sDom.textContent;
46
- scriptData = JSON.parse(dataStr);
47
+ if (!dataStr) return {
48
+ found: false,
49
+ data: null
50
+ };
51
+ const encodedData = JSON.parse(dataStr);
52
+ scriptData = JSON.parse(decodeBase64ToUtf8(encodedData));
47
53
  found = true;
48
54
  clientDataCache.set(key, scriptData);
49
55
  } catch (error) {
@@ -1,5 +1,5 @@
1
1
  import type { MergeType, MOmit } from "../../../../../types/typings/type";
2
- import { FetchInfo } from "@model/data/fetchInfo";
2
+ import type { FetchInfo } from "@model/data/fetchInfo";
3
3
  import { SelectProps } from "../../../../../fe/packages/react/ui/index";
4
4
  export interface AsyncSelectProps extends MergeType<[MOmit<FetchInfo, 'fetchKey'>, MOmit<SelectProps, 'options'>]> {
5
5
  }
@@ -15,6 +15,8 @@ declare global {
15
15
  [SSR_DATA_KEY]?: SSRHydrationData | string;
16
16
  }
17
17
  }
18
+ export declare function encodeUtf8ToBase64(value: string): string;
19
+ export declare function decodeBase64ToUtf8(value: string): string;
18
20
  export declare function serializeHydrationDataScriptContent(data: SSRHydrationData): string;
19
21
  export declare function serializeHydrationData(data: SSRHydrationData): string;
20
22
  export declare function peekHydrationData<Props = Record<string, unknown>, State = Record<string, unknown>>(): SSRHydrationData<Props, State> | null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@_tc/template-core",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "A full-stack TypeScript admin framework package powered by Koa, React, and Vite",
5
5
  "types": "./types/index.d.ts",
6
6
  "exports": {
@@ -42,6 +42,8 @@ declare global {
42
42
  [SSR_DATA_KEY]?: SSRHydrationData | string;
43
43
  }
44
44
  }
45
+ export declare function encodeUtf8ToBase64(value: string): string;
46
+ export declare function decodeBase64ToUtf8(value: string): string;
45
47
  /**
46
48
  * 服务端:将注水数据序列化为 inline script 内容。
47
49
  * 数据内容以 base64 字符串注入,避免在 HTML 中明文暴露 JSON。