talizen 0.2.2 → 0.2.4
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 +33 -0
- package/auth.d.ts +32 -0
- package/auth.js +35 -0
- package/cms.js +5 -3
- package/func.d.ts +26 -0
- package/func.js +69 -0
- package/i18n.d.ts +24 -8
- package/i18n.js +35 -1
- package/index.d.ts +2 -0
- package/index.js +2 -0
- package/package.json +9 -1
package/README.md
CHANGED
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
Talizen's frontend SDK package. It provides a small runtime client and shared types for:
|
|
4
4
|
|
|
5
5
|
- `talizen/core`
|
|
6
|
+
- `talizen/auth`
|
|
6
7
|
- `talizen/cms`
|
|
7
8
|
- `talizen/form`
|
|
9
|
+
- `talizen/func`
|
|
8
10
|
|
|
9
11
|
The package is designed to hold the platform-level APIs that frontend projects use directly, while project-specific CMS and form schema types can still be generated separately per project.
|
|
10
12
|
|
|
@@ -106,11 +108,42 @@ When a `File` object appears in the payload, `submitForm()` will:
|
|
|
106
108
|
3. Replace the original `File` value with the returned `file_url`
|
|
107
109
|
4. Submit the final payload to `/form/:key/submit`
|
|
108
110
|
|
|
111
|
+
### Login users
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
import { currentUser, login, logout, register } from "talizen/auth";
|
|
115
|
+
|
|
116
|
+
await register({ email: "hi@talizen.com", password: "secret", name: "Alice" });
|
|
117
|
+
await login({ email: "hi@talizen.com", password: "secret" });
|
|
118
|
+
|
|
119
|
+
const user = await currentUser();
|
|
120
|
+
await logout();
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Invoke a custom function
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
import { invoke } from "talizen/func";
|
|
127
|
+
|
|
128
|
+
const result = await invoke<{ success: boolean }>("user/auth.login", {
|
|
129
|
+
email: "hi@talizen.com",
|
|
130
|
+
password: "secret",
|
|
131
|
+
});
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
`invoke("<fileKey>.<method>", input)` calls the method exported by the script file. If `.method` is omitted, Talizen calls `main`:
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
await invoke("user/auth", { email: "hi@talizen.com" });
|
|
138
|
+
```
|
|
139
|
+
|
|
109
140
|
## Package Layout
|
|
110
141
|
|
|
111
142
|
- `talizen/core`: shared runtime config, request helpers, and base data types.
|
|
143
|
+
- `talizen/auth`: project user register, login, logout, and current user helpers.
|
|
112
144
|
- `talizen/cms`: CMS content types and content query APIs.
|
|
113
145
|
- `talizen/form`: form submission helpers and related types.
|
|
146
|
+
- `talizen/func`: custom function invocation helpers.
|
|
114
147
|
|
|
115
148
|
## Publish
|
|
116
149
|
|
package/auth.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type TalizenRequestOptions } from "./core.js";
|
|
2
|
+
export interface AuthUser {
|
|
3
|
+
id: string;
|
|
4
|
+
email?: string;
|
|
5
|
+
phone?: string;
|
|
6
|
+
name?: string;
|
|
7
|
+
avatar?: string;
|
|
8
|
+
status?: string;
|
|
9
|
+
profile?: unknown;
|
|
10
|
+
email_verified_at?: string | null;
|
|
11
|
+
last_login_at?: string | null;
|
|
12
|
+
created_at?: string;
|
|
13
|
+
updated_at?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface AuthPasswordInput {
|
|
16
|
+
email: string;
|
|
17
|
+
password: string;
|
|
18
|
+
}
|
|
19
|
+
export interface AuthRegisterInput extends AuthPasswordInput {
|
|
20
|
+
phone?: string;
|
|
21
|
+
name?: string;
|
|
22
|
+
avatar?: string;
|
|
23
|
+
profile?: unknown;
|
|
24
|
+
}
|
|
25
|
+
export declare class TalizenAuthError extends Error {
|
|
26
|
+
constructor(message: string);
|
|
27
|
+
}
|
|
28
|
+
export declare function register(input: AuthRegisterInput, options?: TalizenRequestOptions): Promise<AuthUser>;
|
|
29
|
+
export declare function login(input: AuthPasswordInput, options?: TalizenRequestOptions): Promise<AuthUser>;
|
|
30
|
+
export declare function logout(options?: TalizenRequestOptions): Promise<void>;
|
|
31
|
+
export declare function currentUser(options?: TalizenRequestOptions): Promise<AuthUser | null>;
|
|
32
|
+
export declare function requireUser(options?: TalizenRequestOptions): Promise<AuthUser>;
|
package/auth.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { requestJson, } from "./core.js";
|
|
2
|
+
export class TalizenAuthError extends Error {
|
|
3
|
+
constructor(message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = "TalizenAuthError";
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export async function register(input, options) {
|
|
9
|
+
return requestJson("/auth/register", {
|
|
10
|
+
method: "POST",
|
|
11
|
+
body: JSON.stringify(input),
|
|
12
|
+
}, options);
|
|
13
|
+
}
|
|
14
|
+
export async function login(input, options) {
|
|
15
|
+
return requestJson("/auth/login", {
|
|
16
|
+
method: "POST",
|
|
17
|
+
body: JSON.stringify(input),
|
|
18
|
+
}, options);
|
|
19
|
+
}
|
|
20
|
+
export async function logout(options) {
|
|
21
|
+
await requestJson("/auth/logout", {
|
|
22
|
+
method: "POST",
|
|
23
|
+
body: JSON.stringify({}),
|
|
24
|
+
}, options);
|
|
25
|
+
}
|
|
26
|
+
export async function currentUser(options) {
|
|
27
|
+
return requestJson("/auth/me", { method: "GET" }, options);
|
|
28
|
+
}
|
|
29
|
+
export async function requireUser(options) {
|
|
30
|
+
const user = await currentUser(options);
|
|
31
|
+
if (!user) {
|
|
32
|
+
throw new TalizenAuthError("Login required.");
|
|
33
|
+
}
|
|
34
|
+
return user;
|
|
35
|
+
}
|
package/cms.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { requestJson } from "./core.js";
|
|
2
|
-
import {
|
|
2
|
+
import { getLocale } from "./i18n.js";
|
|
3
3
|
// 字段级本地化解码:把 body._i18n[当前语言] 覆盖到同级字段并删除 _i18n。
|
|
4
|
-
//
|
|
4
|
+
// 无论 SSR(getServerSideProps 取数)、客户端还是编辑器预览,都在此处按当前语言解码——
|
|
5
|
+
// 渲染引擎不再解码 CMS,只透传原始 body(含 _i18n)。用 getLocale()(非 hook),
|
|
6
|
+
// 因此在 getServerSideProps 里调用也合法。当前语言由引擎注入的 __TALIZEN_I18N__ 提供。
|
|
5
7
|
function decodeBodyLocalization(body) {
|
|
6
8
|
if (!body || typeof body !== "object" || !("_i18n" in body))
|
|
7
9
|
return body;
|
|
8
10
|
const { _i18n, ...base } = body;
|
|
9
|
-
const locale =
|
|
11
|
+
const locale = getLocale().locale;
|
|
10
12
|
const over = locale && _i18n ? _i18n[locale] : undefined;
|
|
11
13
|
return over && typeof over === "object" ? { ...base, ...over } : base;
|
|
12
14
|
}
|
package/func.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type TalizenRequestOptions } from "./core.js";
|
|
2
|
+
export interface FuncLogEntry {
|
|
3
|
+
level: string;
|
|
4
|
+
text: string;
|
|
5
|
+
}
|
|
6
|
+
export interface FuncRunResponse<T = unknown> {
|
|
7
|
+
ok: boolean;
|
|
8
|
+
result?: T;
|
|
9
|
+
logs?: FuncLogEntry[];
|
|
10
|
+
error?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare class TalizenFuncError extends Error {
|
|
13
|
+
readonly key: string;
|
|
14
|
+
readonly method: string;
|
|
15
|
+
readonly logs: FuncLogEntry[];
|
|
16
|
+
constructor(key: string, method: string, message: string, logs?: FuncLogEntry[]);
|
|
17
|
+
}
|
|
18
|
+
export declare function invoke<T = unknown>(name: string, input?: unknown, options?: TalizenRequestOptions): Promise<T>;
|
|
19
|
+
export interface RunFuncOptions extends TalizenRequestOptions {
|
|
20
|
+
method?: string;
|
|
21
|
+
}
|
|
22
|
+
export declare function runFunc<T = unknown>(key: string, input?: unknown, options?: RunFuncOptions): Promise<T>;
|
|
23
|
+
export declare function parseInvokeName(name: string): {
|
|
24
|
+
key: string;
|
|
25
|
+
method: string;
|
|
26
|
+
};
|
package/func.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { requestJson, } from "./core.js";
|
|
2
|
+
export class TalizenFuncError extends Error {
|
|
3
|
+
key;
|
|
4
|
+
method;
|
|
5
|
+
logs;
|
|
6
|
+
constructor(key, method, message, logs) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = "TalizenFuncError";
|
|
9
|
+
this.key = key;
|
|
10
|
+
this.method = method;
|
|
11
|
+
this.logs = logs ?? [];
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export async function invoke(name, input, options) {
|
|
15
|
+
const target = parseInvokeName(name);
|
|
16
|
+
return runFunc(target.key, input, {
|
|
17
|
+
...options,
|
|
18
|
+
method: target.method,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
export async function runFunc(key, input, options) {
|
|
22
|
+
const normalizedKey = normalizeFuncKey(key);
|
|
23
|
+
const method = normalizeFuncMethod(options?.method);
|
|
24
|
+
const response = await requestJson(`/func/${encodeFuncKey(normalizedKey)}${method === "main" ? "" : `.${encodeURIComponent(method)}`}`, {
|
|
25
|
+
method: "POST",
|
|
26
|
+
body: JSON.stringify(input ?? {}),
|
|
27
|
+
}, options);
|
|
28
|
+
if (!response.ok) {
|
|
29
|
+
throw new TalizenFuncError(normalizedKey, method, response.error || "Talizen func failed.", response.logs);
|
|
30
|
+
}
|
|
31
|
+
return response.result;
|
|
32
|
+
}
|
|
33
|
+
export function parseInvokeName(name) {
|
|
34
|
+
const normalized = normalizeFuncKey(name);
|
|
35
|
+
const lastSlash = normalized.lastIndexOf("/");
|
|
36
|
+
const lastDot = normalized.lastIndexOf(".");
|
|
37
|
+
if (lastDot <= lastSlash) {
|
|
38
|
+
return { key: normalized, method: "main" };
|
|
39
|
+
}
|
|
40
|
+
const key = normalized.slice(0, lastDot);
|
|
41
|
+
const method = normalized.slice(lastDot + 1);
|
|
42
|
+
return {
|
|
43
|
+
key: normalizeFuncKey(key),
|
|
44
|
+
method: normalizeFuncMethod(method),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function normalizeFuncKey(key) {
|
|
48
|
+
const normalized = key.trim().replace(/^\/+|\/+$/g, "");
|
|
49
|
+
if (normalized === "") {
|
|
50
|
+
throw new Error("Talizen func key is required.");
|
|
51
|
+
}
|
|
52
|
+
if (normalized.includes("..") || normalized.includes("\0")) {
|
|
53
|
+
throw new Error(`Invalid Talizen func key: ${key}`);
|
|
54
|
+
}
|
|
55
|
+
return normalized;
|
|
56
|
+
}
|
|
57
|
+
function normalizeFuncMethod(method) {
|
|
58
|
+
const normalized = (method ?? "main").trim();
|
|
59
|
+
if (normalized === "") {
|
|
60
|
+
return "main";
|
|
61
|
+
}
|
|
62
|
+
if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(normalized)) {
|
|
63
|
+
throw new Error(`Invalid Talizen func method: ${method}`);
|
|
64
|
+
}
|
|
65
|
+
return normalized;
|
|
66
|
+
}
|
|
67
|
+
function encodeFuncKey(key) {
|
|
68
|
+
return key.split("/").map(encodeURIComponent).join("/");
|
|
69
|
+
}
|
package/i18n.d.ts
CHANGED
|
@@ -23,6 +23,20 @@ declare global {
|
|
|
23
23
|
* ```
|
|
24
24
|
*/
|
|
25
25
|
export declare function useLocale(): LocaleRuntime;
|
|
26
|
+
/**
|
|
27
|
+
* 读取当前语言运行时信息——与 useLocale() 完全等价,但命名为 get*(**不是 hook**),
|
|
28
|
+
* 用于 getServerSideProps / generateMetadata 等**服务端取数场景**(对齐 next-intl 的 getLocale)。
|
|
29
|
+
* 组件渲染里用 useLocale()。SSR 与客户端返回一致。
|
|
30
|
+
*
|
|
31
|
+
* ```ts
|
|
32
|
+
* export async function getServerSideProps(ctx) {
|
|
33
|
+
* const { locale } = getLocale() // ✅ 服务端取数用 get*
|
|
34
|
+
* const posts = await listContents("blog") // 内部按当前 locale 自动解码 CMS
|
|
35
|
+
* return { props: { posts } }
|
|
36
|
+
* }
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export declare function getLocale(): LocaleRuntime;
|
|
26
40
|
/**
|
|
27
41
|
* 给站内路径加语言前缀(默认语言不加前缀)。不传 `locale` 时使用当前语言。
|
|
28
42
|
* 站外链接(协议 / 协议相对 URL)与页内锚点(`#...`)原样返回。
|
|
@@ -63,15 +77,17 @@ declare global {
|
|
|
63
77
|
}
|
|
64
78
|
/** 翻译函数:按 key 取文案(支持点路径),`{var}` 插值;缺失时返回 key 本身。 */
|
|
65
79
|
export type Translator = (key: string, vars?: Record<string, unknown>) => string;
|
|
80
|
+
export declare function useTranslations(namespace?: string): Translator;
|
|
66
81
|
/**
|
|
67
|
-
* 读取 UI
|
|
82
|
+
* 读取 UI 文案翻译器——与 useTranslations() 等价,但命名为 get*(**不是 hook**),
|
|
83
|
+
* 用于 getServerSideProps / generateMetadata 等**服务端取数场景**(对齐 next-intl 的 getTranslations)。
|
|
84
|
+
* 组件渲染里用 useTranslations()。
|
|
68
85
|
*
|
|
69
|
-
* ```
|
|
70
|
-
*
|
|
71
|
-
* t("
|
|
72
|
-
* t("
|
|
86
|
+
* ```ts
|
|
87
|
+
* export async function generateMetadata() {
|
|
88
|
+
* const t = getTranslations("seo")
|
|
89
|
+
* return { title: t("home_title") }
|
|
90
|
+
* }
|
|
73
91
|
* ```
|
|
74
|
-
*
|
|
75
|
-
* UI chrome 用 useTranslations;文章等内容用 CMS 字段级 _i18n(见 listContents/getContent)。
|
|
76
92
|
*/
|
|
77
|
-
export declare function
|
|
93
|
+
export declare function getTranslations(namespace?: string): Translator;
|
package/i18n.js
CHANGED
|
@@ -19,6 +19,22 @@ function readLocaleRuntime() {
|
|
|
19
19
|
export function useLocale() {
|
|
20
20
|
return readLocaleRuntime();
|
|
21
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* 读取当前语言运行时信息——与 useLocale() 完全等价,但命名为 get*(**不是 hook**),
|
|
24
|
+
* 用于 getServerSideProps / generateMetadata 等**服务端取数场景**(对齐 next-intl 的 getLocale)。
|
|
25
|
+
* 组件渲染里用 useLocale()。SSR 与客户端返回一致。
|
|
26
|
+
*
|
|
27
|
+
* ```ts
|
|
28
|
+
* export async function getServerSideProps(ctx) {
|
|
29
|
+
* const { locale } = getLocale() // ✅ 服务端取数用 get*
|
|
30
|
+
* const posts = await listContents("blog") // 内部按当前 locale 自动解码 CMS
|
|
31
|
+
* return { props: { posts } }
|
|
32
|
+
* }
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export function getLocale() {
|
|
36
|
+
return readLocaleRuntime();
|
|
37
|
+
}
|
|
22
38
|
/**
|
|
23
39
|
* 给站内路径加语言前缀(默认语言不加前缀)。不传 `locale` 时使用当前语言。
|
|
24
40
|
* 站外链接(协议 / 协议相对 URL)与页内锚点(`#...`)原样返回。
|
|
@@ -83,7 +99,7 @@ function lookupMessage(obj, path) {
|
|
|
83
99
|
*
|
|
84
100
|
* UI chrome 用 useTranslations;文章等内容用 CMS 字段级 _i18n(见 listContents/getContent)。
|
|
85
101
|
*/
|
|
86
|
-
|
|
102
|
+
function buildTranslator(namespace) {
|
|
87
103
|
const messages = readMessages();
|
|
88
104
|
const scoped = namespace ? lookupMessage(messages, namespace) : messages;
|
|
89
105
|
const base = (scoped && typeof scoped === "object" ? scoped : {});
|
|
@@ -95,3 +111,21 @@ export function useTranslations(namespace) {
|
|
|
95
111
|
return text.replace(/\{(\w+)\}/g, (_, k) => (k in vars ? String(vars[k]) : `{${k}}`));
|
|
96
112
|
};
|
|
97
113
|
}
|
|
114
|
+
export function useTranslations(namespace) {
|
|
115
|
+
return buildTranslator(namespace);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* 读取 UI 文案翻译器——与 useTranslations() 等价,但命名为 get*(**不是 hook**),
|
|
119
|
+
* 用于 getServerSideProps / generateMetadata 等**服务端取数场景**(对齐 next-intl 的 getTranslations)。
|
|
120
|
+
* 组件渲染里用 useTranslations()。
|
|
121
|
+
*
|
|
122
|
+
* ```ts
|
|
123
|
+
* export async function generateMetadata() {
|
|
124
|
+
* const t = getTranslations("seo")
|
|
125
|
+
* return { title: t("home_title") }
|
|
126
|
+
* }
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
export function getTranslations(namespace) {
|
|
130
|
+
return buildTranslator(namespace);
|
|
131
|
+
}
|
package/index.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
export * from "./core.js";
|
|
2
|
+
export * from "./auth.js";
|
|
2
3
|
export * from "./cms.js";
|
|
3
4
|
export * from "./captcha-ui.js";
|
|
4
5
|
export * from "./form.js";
|
|
6
|
+
export * from "./func.js";
|
|
5
7
|
export * from "./i18n.js";
|
|
6
8
|
type OneOrMany<T> = T | Array<T>;
|
|
7
9
|
export interface MetadataTitle {
|
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "talizen",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "Talizen frontend SDK types for cms, form and core.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -20,6 +20,10 @@
|
|
|
20
20
|
"types": "./core.d.ts",
|
|
21
21
|
"import": "./core.js"
|
|
22
22
|
},
|
|
23
|
+
"./auth": {
|
|
24
|
+
"types": "./auth.d.ts",
|
|
25
|
+
"import": "./auth.js"
|
|
26
|
+
},
|
|
23
27
|
"./cms": {
|
|
24
28
|
"types": "./cms.d.ts",
|
|
25
29
|
"import": "./cms.js"
|
|
@@ -32,6 +36,10 @@
|
|
|
32
36
|
"types": "./form.d.ts",
|
|
33
37
|
"import": "./form.js"
|
|
34
38
|
},
|
|
39
|
+
"./func": {
|
|
40
|
+
"types": "./func.d.ts",
|
|
41
|
+
"import": "./func.js"
|
|
42
|
+
},
|
|
35
43
|
"./i18n": {
|
|
36
44
|
"types": "./i18n.d.ts",
|
|
37
45
|
"import": "./i18n.js"
|