talizen 0.2.11 → 0.2.13

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 CHANGED
@@ -216,6 +216,10 @@ export function list(input: { offset?: number }, ctx: TalizenFuncContext) {
216
216
 
217
217
  `ctx.db`, `ctx.cache`, `ctx.auth`, `ctx.request`, and `ctx.cookies` are injected by the Talizen Func runtime. `talizen/func-runtime` is a type-only authoring module; do not import runtime values from it.
218
218
 
219
+ `ctx.request` exposes Fetch-style one-shot body readers. Use `await ctx.request.text()` when a webhook signature must be verified against the exact request bytes, `await ctx.request.json()` for parsed JSON, or `await ctx.request.arrayBuffer()` for binary input. Reading the body sets `ctx.request.bodyUsed`; a second read rejects. `ctx.response.status(code)` sets the actual HTTP response status (100-599), including statuses returned when a Func throws after setting the status. The runtime also provides `TextEncoder` and the HMAC SHA-256 subset of `crypto.subtle` (`importKey`, `sign`, and `verify`) for webhook verification.
220
+
221
+ Func HTTP responses use the HTTP status code rather than a top-level `ok` field. Successful responses contain `{ result: ... }`; failed responses contain `{ error: ... }`. `invoke()` unwraps `result` for callers.
222
+
219
223
  ## Package Layout
220
224
 
221
225
  - `talizen/core`: shared runtime config, request helpers, and base data types.
package/core.d.ts CHANGED
@@ -17,6 +17,8 @@ export interface TalizenClientConfig {
17
17
  headers?: HeadersInit;
18
18
  fetch?: typeof fetch;
19
19
  onFileUploadProcess?: TalizenFileUploadProcessCallback;
20
+ i18n?: Partial<TalizenLocaleRuntime>;
21
+ messages?: Record<string, unknown>;
20
22
  }
21
23
  export interface TalizenRequestOptions extends TalizenClientConfig {
22
24
  signal?: AbortSignal;
@@ -25,8 +27,14 @@ export interface TalizenErrorBody {
25
27
  code?: number | string;
26
28
  message?: string;
27
29
  }
30
+ export interface TalizenLocaleRuntime {
31
+ locale: string;
32
+ locales: string[];
33
+ defaultLocale: string;
34
+ }
28
35
  export declare function setTalizenConfig(config: TalizenClientConfig): void;
29
36
  export declare function getTalizenConfig(): TalizenClientConfig;
37
+ export declare function subscribeTalizenConfig(listener: () => void): () => void;
30
38
  declare global {
31
39
  var TalizenConfig: TalizenClientConfig | undefined;
32
40
  }
package/core.js CHANGED
@@ -1,13 +1,21 @@
1
1
  let talizenConfig = {};
2
+ const talizenConfigListeners = new Set();
2
3
  export function setTalizenConfig(config) {
3
4
  talizenConfig = {
4
5
  ...talizenConfig,
5
6
  ...config,
6
7
  };
8
+ talizenConfigListeners.forEach(listener => listener());
7
9
  }
8
10
  export function getTalizenConfig() {
9
11
  return talizenConfig;
10
12
  }
13
+ export function subscribeTalizenConfig(listener) {
14
+ talizenConfigListeners.add(listener);
15
+ return () => {
16
+ talizenConfigListeners.delete(listener);
17
+ };
18
+ }
11
19
  export function resolveTalizenConfig(config) {
12
20
  // globalThis is the global object in the browser and node.js
13
21
  const globalTalizenConfig = typeof globalThis !== "undefined" ? globalThis.TalizenConfig : {};
package/func-runtime.d.ts CHANGED
@@ -74,6 +74,13 @@ export interface FuncRequestRuntime {
74
74
  path: string;
75
75
  headers: FuncReadonlyStringMap;
76
76
  cookies: FuncReadonlyStringMap;
77
+ readonly bodyUsed: boolean;
78
+ text(): Promise<string>;
79
+ json<T = unknown>(): Promise<T>;
80
+ arrayBuffer(): Promise<ArrayBuffer>;
81
+ }
82
+ export interface FuncResponseRuntime {
83
+ status(code: number): void;
77
84
  }
78
85
  export interface FuncCookieSetOptions {
79
86
  path?: string;
@@ -96,6 +103,7 @@ export interface TalizenFuncContext {
96
103
  trace_id: string;
97
104
  extra?: Record<string, unknown>;
98
105
  request: FuncRequestRuntime;
106
+ response: FuncResponseRuntime;
99
107
  db: FuncDbRuntime;
100
108
  auth: FuncAuthRuntime;
101
109
  cache: FuncCacheRuntime;
package/func.d.ts CHANGED
@@ -4,7 +4,6 @@ export interface FuncLogEntry {
4
4
  text: string;
5
5
  }
6
6
  export interface FuncRunResponse<T = unknown> {
7
- ok: boolean;
8
7
  result?: T;
9
8
  logs?: FuncLogEntry[];
10
9
  error?: string;
@@ -13,7 +12,8 @@ export declare class TalizenFuncError extends Error {
13
12
  readonly key: string;
14
13
  readonly method: string;
15
14
  readonly logs: FuncLogEntry[];
16
- constructor(key: string, method: string, message: string, logs?: FuncLogEntry[]);
15
+ readonly status: number;
16
+ constructor(key: string, method: string, message: string, logs?: FuncLogEntry[], status?: number);
17
17
  }
18
18
  export declare function invoke<T = unknown>(name: string, input?: unknown, options?: RunFuncOptions): Promise<T>;
19
19
  export interface RunFuncOptions extends TalizenRequestOptions {
package/func.js CHANGED
@@ -1,14 +1,16 @@
1
- import { requestJson, } from "./core.js";
1
+ import { requestJson, TalizenHttpError, } from "./core.js";
2
2
  export class TalizenFuncError extends Error {
3
3
  key;
4
4
  method;
5
5
  logs;
6
- constructor(key, method, message, logs) {
6
+ status;
7
+ constructor(key, method, message, logs, status = 200) {
7
8
  super(message);
8
9
  this.name = "TalizenFuncError";
9
10
  this.key = key;
10
11
  this.method = method;
11
12
  this.logs = logs ?? [];
13
+ this.status = status;
12
14
  }
13
15
  }
14
16
  export async function invoke(name, input, options) {
@@ -23,15 +25,42 @@ export async function runFunc(key, input, options) {
23
25
  const method = normalizeFuncMethod(options?.method);
24
26
  const timeoutMS = normalizeTimeoutMS(options?.timeoutMS ?? options?.timeoutMs);
25
27
  const path = `/func/${encodeFuncKey(normalizedKey)}${method === "main" ? "" : `.${encodeURIComponent(method)}`}${timeoutMS ? `?timeout_ms=${timeoutMS}` : ""}`;
26
- const response = await requestJson(path, {
27
- method: "POST",
28
- body: JSON.stringify(input ?? {}),
29
- }, options);
30
- if (!response.ok) {
28
+ let response;
29
+ try {
30
+ response = await requestJson(path, {
31
+ method: "POST",
32
+ body: JSON.stringify(input ?? {}),
33
+ }, options);
34
+ }
35
+ catch (error) {
36
+ if (error instanceof TalizenHttpError) {
37
+ throw new TalizenFuncError(normalizedKey, method, funcHttpErrorMessage(error), undefined, error.status);
38
+ }
39
+ throw error;
40
+ }
41
+ if (response.error) {
31
42
  throw new TalizenFuncError(normalizedKey, method, response.error || "Talizen func failed.", response.logs);
32
43
  }
33
44
  return response.result;
34
45
  }
46
+ function funcHttpErrorMessage(error) {
47
+ try {
48
+ const payload = JSON.parse(error.body);
49
+ if (typeof payload.error === "string") {
50
+ return payload.error;
51
+ }
52
+ if (payload.error && typeof payload.error === "object") {
53
+ const message = payload.error.message;
54
+ if (typeof message === "string") {
55
+ return message;
56
+ }
57
+ }
58
+ }
59
+ catch {
60
+ // Fall through to the HTTP error body.
61
+ }
62
+ return error.body || error.message || "Talizen func failed.";
63
+ }
35
64
  export function parseInvokeName(name) {
36
65
  const normalized = normalizeFuncKey(name);
37
66
  const lastSlash = normalized.lastIndexOf("/");
package/i18n.d.ts CHANGED
@@ -1,16 +1,10 @@
1
1
  import * as React from "react";
2
+ import { type TalizenLocaleRuntime } from "./core.js";
2
3
  /**
3
- * 当前语言运行时信息。由 Talizen 渲染引擎注入到 `globalThis.__TALIZEN_I18N__`
4
- * (SSR 与浏览器端注入同一份,避免 hydration 不一致)。站点未开启多语言时为空。
4
+ * 当前语言运行时信息。由 Talizen 渲染引擎写入 TalizenConfig.i18n
5
+ * (旧版 globalThis.__TALIZEN_I18N__ 仍作为兼容 fallback)。站点未开启多语言时为空。
5
6
  */
6
- export interface LocaleRuntime {
7
- /** 当前生效语言;站点未配置多语言时为 ""。 */
8
- locale: string;
9
- /** 站点配置的全部语言。 */
10
- locales: string[];
11
- /** 默认语言(其 URL 无前缀)。 */
12
- defaultLocale: string;
13
- }
7
+ export type LocaleRuntime = TalizenLocaleRuntime;
14
8
  declare global {
15
9
  var __TALIZEN_I18N__: Partial<LocaleRuntime> | undefined;
16
10
  }
package/i18n.js CHANGED
@@ -1,6 +1,24 @@
1
1
  import * as React from "react";
2
- function readLocaleRuntime() {
3
- const d = (typeof globalThis !== "undefined" ? globalThis.__TALIZEN_I18N__ : undefined) ?? {};
2
+ import { getTalizenConfig, subscribeTalizenConfig } from "./core.js";
3
+ let lastSnapshot = null;
4
+ let lastSnapshotI18n;
5
+ let lastSnapshotMessages;
6
+ function readI18nRuntimeConfig() {
7
+ const config = getTalizenConfig();
8
+ const fallbackI18n = typeof globalThis !== "undefined" ? globalThis.__TALIZEN_I18N__ : undefined;
9
+ const fallbackMessages = typeof globalThis !== "undefined" ? globalThis.__TALIZEN_MESSAGES__ : undefined;
10
+ const i18n = config.i18n ?? fallbackI18n;
11
+ const messages = config.messages ?? fallbackMessages ?? {};
12
+ if (lastSnapshot && lastSnapshotI18n === i18n && lastSnapshotMessages === messages) {
13
+ return lastSnapshot;
14
+ }
15
+ lastSnapshotI18n = i18n;
16
+ lastSnapshotMessages = messages;
17
+ lastSnapshot = { i18n, messages };
18
+ return lastSnapshot;
19
+ }
20
+ function normalizeLocaleRuntime(value) {
21
+ const d = value ?? {};
4
22
  const locales = Array.isArray(d.locales) ? d.locales.filter((l) => typeof l === "string") : [];
5
23
  return {
6
24
  locale: typeof d.locale === "string" ? d.locale : "",
@@ -8,6 +26,9 @@ function readLocaleRuntime() {
8
26
  defaultLocale: typeof d.defaultLocale === "string" ? d.defaultLocale : (locales[0] ?? ""),
9
27
  };
10
28
  }
29
+ function readLocaleRuntime() {
30
+ return normalizeLocaleRuntime(readI18nRuntimeConfig().i18n);
31
+ }
11
32
  /**
12
33
  * 读取当前语言运行时信息(当前语言 / 全部语言 / 默认语言)。SSR 与客户端返回一致。
13
34
  * 站点未开启多语言时 `locale === ""`。
@@ -17,7 +38,8 @@ function readLocaleRuntime() {
17
38
  * ```
18
39
  */
19
40
  export function useLocale() {
20
- return readLocaleRuntime();
41
+ const snapshot = React.useSyncExternalStore(subscribeTalizenConfig, readI18nRuntimeConfig, readI18nRuntimeConfig);
42
+ return normalizeLocaleRuntime(snapshot.i18n);
21
43
  }
22
44
  /**
23
45
  * 读取当前语言运行时信息——与 useLocale() 完全等价,但命名为 get*(**不是 hook**),
@@ -67,6 +89,7 @@ export const LOCALE_COOKIE = "CREGHT_LOCALE";
67
89
  */
68
90
  export function Link(props) {
69
91
  const { href, locale, onClick, ...rest } = props;
92
+ useLocale();
70
93
  const finalHref = localizedPath(href, locale);
71
94
  const handleClick = (event) => {
72
95
  if (locale && typeof document !== "undefined") {
@@ -77,7 +100,7 @@ export function Link(props) {
77
100
  return React.createElement("a", { ...rest, href: finalHref, onClick: handleClick });
78
101
  }
79
102
  function readMessages() {
80
- return (typeof globalThis !== "undefined" ? globalThis.__TALIZEN_MESSAGES__ : undefined) ?? {};
103
+ return readI18nRuntimeConfig().messages;
81
104
  }
82
105
  function lookupMessage(obj, path) {
83
106
  let cur = obj;
@@ -99,8 +122,8 @@ function lookupMessage(obj, path) {
99
122
  *
100
123
  * UI chrome 用 useTranslations;文章等内容用 CMS 字段级 _i18n(见 listContents/getContent)。
101
124
  */
102
- function buildTranslator(namespace) {
103
- const messages = readMessages();
125
+ function buildTranslator(namespace, sourceMessages) {
126
+ const messages = sourceMessages ?? readMessages();
104
127
  const scoped = namespace ? lookupMessage(messages, namespace) : messages;
105
128
  const base = (scoped && typeof scoped === "object" ? scoped : {});
106
129
  return (key, vars) => {
@@ -112,7 +135,8 @@ function buildTranslator(namespace) {
112
135
  };
113
136
  }
114
137
  export function useTranslations(namespace) {
115
- return buildTranslator(namespace);
138
+ const snapshot = React.useSyncExternalStore(subscribeTalizenConfig, readI18nRuntimeConfig, readI18nRuntimeConfig);
139
+ return React.useMemo(() => buildTranslator(namespace, snapshot.messages), [namespace, snapshot]);
116
140
  }
117
141
  /**
118
142
  * 读取 UI 文案翻译器——与 useTranslations() 等价,但命名为 get*(**不是 hook**),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "talizen",
3
- "version": "0.2.11",
3
+ "version": "0.2.13",
4
4
  "description": "Talizen frontend SDK types for cms, form and core.",
5
5
  "type": "module",
6
6
  "license": "MIT",