@lark-base-open/base-database-app-sdk 0.0.0-stage → 0.0.1-alpha.0
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/LICENSE +6 -0
- package/README.md +30 -2
- package/capabilities.json +75 -0
- package/development-pairing.json +11 -0
- package/dist/capabilities-56e403e7.d.ts +22 -0
- package/dist/chunk-3JX3FIBG.js +1 -0
- package/dist/chunk-HEMWSWXI.js +1 -0
- package/dist/chunk-OHPYKFCM.js +1 -0
- package/dist/chunk-YDAVA3BB.js +1 -0
- package/dist/components.d.ts +5 -0
- package/dist/components.js +1 -0
- package/dist/index-64557073.d.ts +306 -0
- package/dist/page.d.ts +137 -0
- package/dist/page.js +1 -0
- package/dist/server.cjs +1 -0
- package/dist/server.d.cts +78 -0
- package/dist/server.d.ts +78 -0
- package/dist/server.js +1 -0
- package/dist/shared.cjs +1 -0
- package/dist/shared.d.cts +177 -0
- package/dist/shared.d.ts +177 -0
- package/dist/shared.js +1 -0
- package/dist/testing.d.ts +73 -0
- package/dist/testing.js +1 -0
- package/dist/theme.css +168 -0
- package/dist/vite.d.ts +15 -0
- package/dist/vite.js +3 -0
- package/docs/README.md +16 -0
- package/docs/capabilities.md +59 -0
- package/docs/compat.md +36 -0
- package/docs/components.md +34 -0
- package/docs/exports.md +39 -0
- package/docs/page.md +101 -0
- package/docs/server.md +92 -0
- package/docs/shared.md +21 -0
- package/docs/start-base-app.md +47 -0
- package/docs/testing.md +57 -0
- package/docs/upgrade.md +44 -0
- package/examples/capabilities.ts +7 -0
- package/examples/compat.ts +21 -0
- package/examples/components.tsx +11 -0
- package/examples/contract-types.ts +41 -0
- package/examples/development-context.ts +17 -0
- package/examples/http.ts +12 -0
- package/examples/link.ts +10 -0
- package/examples/mock.ts +8 -0
- package/examples/msw.ts +5 -0
- package/examples/page.ts +6 -0
- package/examples/server.ts +57 -0
- package/examples/shared.ts +10 -0
- package/examples/start-base-app.tsx +8 -0
- package/examples/upgrade.ts +7 -0
- package/examples/upload.ts +18 -0
- package/examples/vite.ts +2 -0
- package/package.json +105 -4
package/docs/exports.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# 公开导出清单
|
|
2
|
+
|
|
3
|
+
由 package.json exports 指向的实际 dist ESM 与 TypeScript 声明生成;在 SDK 源码仓运行 npm run docs:exports 更新,npm test 校验。server/shared 另提供 CJS;CSS 入口为 /page/styles.css。没有根入口或源码通配入口。
|
|
4
|
+
|
|
5
|
+
## page
|
|
6
|
+
|
|
7
|
+
值:`AttachmentImage`、`BaseAppProvider`、`CAPABILITY_CATALOG`、`CapabilityUnavailableError`、`DatabaseUserMenu`、`DiagnosticCode`、`FloatingButton`、`SDKError`、`SITE_RUNTIME_PROTOCOL_VERSION`、`SiteChatbotStreamAdapter`、`SiteChatbotStreamError`、`SiteRuntimeApi`、`SiteRuntimeClient`、`SiteRuntimeErrorCode`、`SiteRuntimeEvent`、`SiteRuntimeFunction`、`SiteRuntimeInvocationError`、`UiLanguageContext`、`UserSelectorEditor`、`UserSelectorPanel`、`buildAttachmentCoverUrl`、`buildDatabaseInvokeFunctionPath`、`buildDatabaseInvokeGetPath`、`callSiteAIFunction`、`classifyRuntimeError`、`createSiteAIFunctionTask`、`createSiteRuntimeClient`、`createSiteRuntimeFetch`、`fetch`、`fetchAttachmentCover`、`fetchAttachmentDownloadUrl`、`getCapabilities`、`getCurrentUser`、`getDatabaseContext`、`getRuntimeCapabilities`、`getSiteAIFunctionTask`、`getUiLanguage`、`getUiLocale`、`hasCapability`、`hasRunnableMediaTask`、`initializeUiLanguage`、`installLinkGuard`、`invokeApi`、`invokeDatabaseFunction`、`invokeDatabaseMutationBatch`、`invokeHttp`、`invokeSiteAIFunctionStream`、`isValidSiteFunctionName`、`logout`、`normalizeSiteAIMediaTask`、`openAttachmentDownload`、`openLink`、`queryCapability`、`readCapabilities`、`readSiteAITextDelta`、`requestEdit`、`requireCapability`、`resolveDatabaseContext`、`selectUiLanguage`、`siteRuntime`、`startBaseApp`、`uploadAttachment`、`useBaseApp`、`useOptionalBaseApp`、`useUiLanguage`。
|
|
8
|
+
|
|
9
|
+
类型与值完整清单:`AttachmentImage`、`BaseAppContextValue`、`BaseAppHandle`、`BaseAppProvider`、`CAPABILITY_CATALOG`、`CallSiteAIFunctionParams`、`CapabilityContext`、`CapabilityDescriptor`、`CapabilityMap`、`CapabilityName`、`CapabilityQueryResult`、`CapabilityUnavailableError`、`CapabilityUnavailableReason`、`DatabaseAttachmentRef`、`DatabaseAudience`、`DatabaseContext`、`DatabaseEnv`、`DatabaseFunctionInputMap`、`DatabaseFunctionInvocation`、`DatabaseFunctionResponse`、`DatabaseUserMenu`、`DatabaseUserMenuProps`、`DevelopmentPairChallenge`、`DevelopmentPairProof`、`DiagnosticCategory`、`DiagnosticCode`、`FloatingButton`、`FloatingButtonProps`、`FunctionEnvelope`、`FunctionInputMap`、`FunctionMap`、`InvokeSiteAIFunctionStreamParams`、`JsonValue`、`LinkGuardOptions`、`RuntimeCapabilities`、`RuntimeChannel`、`RuntimeContextReader`、`RuntimeDiagnostic`、`SDKError`、`SDKErrorOptions`、`SITE_RUNTIME_PROTOCOL_VERSION`、`SiteAIAudioContentPart`、`SiteAICapabilityType`、`SiteAIFileContentPart`、`SiteAIFunctionError`、`SiteAIFunctionOptions`、`SiteAIFunctionResult`、`SiteAIFunctionStreamCloseRequest`、`SiteAIFunctionStreamDone`、`SiteAIFunctionStreamError`、`SiteAIFunctionStreamHandle`、`SiteAIFunctionStreamMessage`、`SiteAIFunctionStreamOpenRequest`、`SiteAIFunctionVariables`、`SiteAIImageAsset`、`SiteAIImageContentPart`、`SiteAIImageGenerationInput`、`SiteAIImageTask`、`SiteAIMediaTask`、`SiteAIMediaTaskBase`、`SiteAIMessage`、`SiteAIMessageContentPart`、`SiteAIStreamDelta`、`SiteAIStreamMessageParam`、`SiteAITaskStatus`、`SiteAITextContentPart`、`SiteAITextDelta`、`SiteAITextMessageParam`、`SiteAIUserMessageParam`、`SiteAIVideoAsset`、`SiteAIVideoContentPart`、`SiteAIVideoGenerationInput`、`SiteAIVideoTask`、`SiteAttachmentTarget`、`SiteAttachmentUploadCancelRequest`、`SiteAttachmentUploadOptions`、`SiteAttachmentUploadPayload`、`SiteAttachmentUploadProgress`、`SiteAttachmentUploadRequest`、`SiteAttachmentUploadResult`、`SiteChatbotStreamAdapter`、`SiteChatbotStreamCloseRequest`、`SiteChatbotStreamError`、`SiteChatbotStreamMessage`、`SiteChatbotStreamOpenRequest`、`SiteFunctionDefinition`、`SiteFunctionInvokeRequest`、`SiteHttpCancelRequest`、`SiteHttpMethod`、`SiteHttpRequest`、`SiteHttpRequestInit`、`SiteHttpRequestOptions`、`SiteHttpResponse`、`SiteHttpResponseType`、`SiteOpenLinkRequest`、`SiteOpenLinkResult`、`SiteProjectEnv`、`SiteRuntimeApi`、`SiteRuntimeClient`、`SiteRuntimeContext`、`SiteRuntimeError`、`SiteRuntimeErrorCode`、`SiteRuntimeEvent`、`SiteRuntimeFunction`、`SiteRuntimeInvocationError`、`SiteRuntimeLoadFailedEvent`、`SiteRuntimeModeChangedEvent`、`SiteRuntimeReadyEvent`、`SiteRuntimeUserInfo`、`StartBaseAppOptions`、`UiLanguage`、`UiLanguageContext`、`User`、`UserSelectorAction`、`UserSelectorEditor`、`UserSelectorEditorProps`、`UserSelectorEditorValue`、`UserSelectorMode`、`UserSelectorPanel`、`UserSelectorPanelProps`、`UserSelectorSearchAction`、`UserSelectorValue`、`WriteResult`、`buildAttachmentCoverUrl`、`buildDatabaseInvokeFunctionPath`、`buildDatabaseInvokeGetPath`、`callSiteAIFunction`、`classifyRuntimeError`、`createSiteAIFunctionTask`、`createSiteRuntimeClient`、`createSiteRuntimeFetch`、`fetch`、`fetchAttachmentCover`、`fetchAttachmentDownloadUrl`、`getCapabilities`、`getCurrentUser`、`getDatabaseContext`、`getRuntimeCapabilities`、`getSiteAIFunctionTask`、`getUiLanguage`、`getUiLocale`、`hasCapability`、`hasRunnableMediaTask`、`initializeUiLanguage`、`installLinkGuard`、`invokeApi`、`invokeDatabaseFunction`、`invokeDatabaseMutationBatch`、`invokeHttp`、`invokeSiteAIFunctionStream`、`isValidSiteFunctionName`、`logout`、`normalizeSiteAIMediaTask`、`openAttachmentDownload`、`openLink`、`queryCapability`、`readCapabilities`、`readSiteAITextDelta`、`requestEdit`、`requireCapability`、`resolveDatabaseContext`、`selectUiLanguage`、`siteRuntime`、`startBaseApp`、`uploadAttachment`、`useBaseApp`、`useOptionalBaseApp`、`useUiLanguage`。
|
|
10
|
+
|
|
11
|
+
## server
|
|
12
|
+
|
|
13
|
+
值:`SDKError`、`bindFunctionRuntime`、`createServerContext`、`defineFunction`、`normalizeRecordsResult`。
|
|
14
|
+
|
|
15
|
+
类型与值完整清单:`AttachmentInput`、`Attachments`、`DefineFunctionOptions`、`DefinedFunction`、`DiagnosticCategory`、`Fields`、`FunctionEnvelope`、`FunctionResult`、`JsonValue`、`LegacyFunctionHandler`、`RawFunctionContext`、`RecordsClient`、`RecordsOperation`、`RecordsResultRequest`、`RuntimeBuildProfile`、`RuntimeDiagnostic`、`SDKError`、`SDKErrorOptions`、`ServerContext`、`Target`、`WriteResult`、`bindFunctionRuntime`、`createServerContext`、`defineFunction`、`normalizeRecordsResult`。
|
|
16
|
+
|
|
17
|
+
## shared
|
|
18
|
+
|
|
19
|
+
值:`CAPABILITY_CATALOG`、`CapabilityUnavailableError`、`DiagnosticCode`、`SDKError`、`classifyRuntimeError`。
|
|
20
|
+
|
|
21
|
+
类型与值完整清单:`CAPABILITY_CATALOG`、`CapabilityContext`、`CapabilityDescriptor`、`CapabilityMap`、`CapabilityName`、`CapabilityQueryResult`、`CapabilityUnavailableError`、`CapabilityUnavailableReason`、`DiagnosticCategory`、`DiagnosticCode`、`FunctionEnvelope`、`JsonValue`、`RuntimeCapabilities`、`RuntimeDiagnostic`、`SDKError`、`SDKErrorOptions`、`WriteResult`、`classifyRuntimeError`。
|
|
22
|
+
|
|
23
|
+
## testing
|
|
24
|
+
|
|
25
|
+
值:`DEFAULT_SITE_HTTP_BRIDGE_ALLOWLIST`、`createHttpHandlers`、`createMockHost`。
|
|
26
|
+
|
|
27
|
+
类型与值完整清单:`DEFAULT_SITE_HTTP_BRIDGE_ALLOWLIST`、`HttpFixture`、`MockApi`、`MockCall`、`MockHandlerContext`、`MockHostOptions`、`createHttpHandlers`、`createMockHost`。
|
|
28
|
+
|
|
29
|
+
## components
|
|
30
|
+
|
|
31
|
+
值:`AttachmentImage`、`BaseAppProvider`、`DatabaseUserMenu`、`FloatingButton`、`UiLanguageContext`、`UserSelectorEditor`、`UserSelectorPanel`、`getUiLanguage`、`getUiLocale`、`initializeUiLanguage`、`selectUiLanguage`、`useBaseApp`、`useOptionalBaseApp`、`useUiLanguage`。
|
|
32
|
+
|
|
33
|
+
类型与值完整清单:`AttachmentImage`、`BaseAppContextValue`、`BaseAppProvider`、`DatabaseAudience`、`DatabaseUserMenu`、`DatabaseUserMenuProps`、`FloatingButton`、`FloatingButtonProps`、`UiLanguage`、`UiLanguageContext`、`User`、`UserSelectorAction`、`UserSelectorEditor`、`UserSelectorEditorProps`、`UserSelectorEditorValue`、`UserSelectorMode`、`UserSelectorPanel`、`UserSelectorPanelProps`、`UserSelectorSearchAction`、`UserSelectorValue`、`getUiLanguage`、`getUiLocale`、`initializeUiLanguage`、`selectUiLanguage`、`useBaseApp`、`useOptionalBaseApp`、`useUiLanguage`。
|
|
34
|
+
|
|
35
|
+
## vite
|
|
36
|
+
|
|
37
|
+
值:`baseAppDevelopmentPairingPlugin`。
|
|
38
|
+
|
|
39
|
+
类型与值完整清单:`baseAppDevelopmentPairingPlugin`。
|
package/docs/page.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# 页面运行时
|
|
2
|
+
|
|
3
|
+
从 `@lark-base-open/base-database-app-sdk/page` 导入。协议通信复用 channel/runtime;SDK 另提供能力感知链接拦截、老模板数据库/AI 兼容适配、启动管理与平台组件。[`siteRuntime`](exports.md#page) 是默认单例;单测可把模拟 channel 交给 [`createSiteRuntimeClient`](exports.md#page)。
|
|
4
|
+
|
|
5
|
+
生命周期:首屏挂载后调用 `siteRuntime.ready()`,启动失败调用 `reportLoadFailed(code)`;不要等业务数据加载完成才上报 ready。`getRuntimeContext()` 返回 locale/colorScheme/mode/protocolVersion,可选 env/siteToken/capabilities;缺 env/token 的旧宿主由 SDK 的 getDatabaseContext/resolveDatabaseContext 保留路径兜底。
|
|
6
|
+
[`getCurrentUser`](exports.md#page)、[`logout`](exports.md#page)、[`requestEdit`](exports.md#page)、`siteRuntime.subscribeModeChanged` 保持 channel 返回语义。
|
|
7
|
+
|
|
8
|
+
## HTTP 代理
|
|
9
|
+
|
|
10
|
+
<!-- example: http.ts -->
|
|
11
|
+
```ts
|
|
12
|
+
import { invokeHttp, fetch as hostFetch } from '@lark-base-open/base-database-app-sdk/page';
|
|
13
|
+
export async function loadUser(signal: AbortSignal) {
|
|
14
|
+
const response = await invokeHttp({ url: '/space/api/user', signal });
|
|
15
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
16
|
+
const data: unknown = await response.json();
|
|
17
|
+
return data;
|
|
18
|
+
}
|
|
19
|
+
export async function loadWithFetch() {
|
|
20
|
+
const response = await hostFetch('/space/api/user');
|
|
21
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
22
|
+
return response.json() as Promise<unknown>;
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
业务 HTTP 经宿主代理,只接受单个 `/` 开头的相对地址和宿主配置白名单;默认清单见 testing.md。绝对地址、跨源、目录穿越、反斜杠、fragment、控制字符被拒绝。
|
|
27
|
+
方法仅 DELETE/GET/PATCH/POST/PUT;body 只支持字符串,GET 不能有 body。禁止透传 Authorization、Connection、Content-Length、Cookie、Host、Origin、Proxy-Authorization、Referer、Transfer-Encoding,以及 sec-/x-forwarded-/rpc- 前缀请求头。身份由宿主注入。
|
|
28
|
+
4xx/5xx 正常返回 Response,必须自行检查 ok。文本与 arrayBuffer 使用明确的 responseType;旧宿主不支持二进制时失败,不降级为文本或直连。超时默认 30 秒、最高 60 秒;取消不证明远端写入已撤销,写入失败不得盲目重试。
|
|
29
|
+
|
|
30
|
+
## 外链
|
|
31
|
+
|
|
32
|
+
<!-- example: link.ts -->
|
|
33
|
+
```ts
|
|
34
|
+
import { siteRuntime, hasCapability, openLink } from '@lark-base-open/base-database-app-sdk/page';
|
|
35
|
+
import type { SiteRuntimeContext } from '@lark-base-open/base-database-app-sdk/page';
|
|
36
|
+
export async function canOpenLink() {
|
|
37
|
+
const context: SiteRuntimeContext = await siteRuntime.getRuntimeContext();
|
|
38
|
+
return hasCapability(context, 'link.open');
|
|
39
|
+
}
|
|
40
|
+
// 在点击之前查询能力。用户点击时立即调用,避免因等待查询失去用户激活。
|
|
41
|
+
export function onSupportedLinkClick(url: string) {
|
|
42
|
+
return openLink(url);
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
[`openLink`](exports.md#page) 支持 http/https/mailto/tel;宿主拒绝 javascript/data/file 等协议。HTTP(S) 必须经过宿主 seclink,服务不可用时失败关闭;opened 只代表宿主已提交导航,不保证弹窗可见。
|
|
47
|
+
`installLinkGuard()` 同步返回 cleanup,内部异步查询能力;查询前、查询失败或显式能力表缺 link.open 时不拦截;无能力字段采用 host-baseline-84afb07 并安装拦截。onDiagnostic 收到脱敏提示,默认 console.warn。应在页面启动时安装,而不是点击后才查询。支持能力时沿用 channel guard:捕获外链、_blank 与中键;download/hash/普通同源导航不接管。cleanup 也阻止迟到查询安装。
|
|
48
|
+
不要将 guard 当作任意 HTML 的安全过滤器;尤其不允许把不可信字符串直接设为 href。浏览器原生行为仍受 iframe sandbox 限制。
|
|
49
|
+
|
|
50
|
+
## 附件字节上传
|
|
51
|
+
|
|
52
|
+
<!-- example: upload.ts -->
|
|
53
|
+
```ts
|
|
54
|
+
import { requireCapability, siteRuntime } from '@lark-base-open/base-database-app-sdk/page';
|
|
55
|
+
export async function upload(baseToken: string, tableId: string, file: File) {
|
|
56
|
+
await requireCapability('attachment.upload');
|
|
57
|
+
let dimensions: { image_width: number; image_height: number } | undefined;
|
|
58
|
+
if (!file.type) throw new Error('无法识别文件类型');
|
|
59
|
+
if (file.type.startsWith('image/')) {
|
|
60
|
+
const url = URL.createObjectURL(file);
|
|
61
|
+
try {
|
|
62
|
+
const image = new Image(); image.src = url; await image.decode();
|
|
63
|
+
if (image.naturalWidth <= 0 || image.naturalHeight <= 0) throw new Error('无法读取图片尺寸');
|
|
64
|
+
dimensions = { image_width: image.naturalWidth, image_height: image.naturalHeight };
|
|
65
|
+
} finally { URL.revokeObjectURL(url); }
|
|
66
|
+
}
|
|
67
|
+
const result = await siteRuntime.uploadAttachment({ baseToken, tableId, file,
|
|
68
|
+
onProgress: ({ loaded, total }) => console.info(`${loaded}/${total}`) });
|
|
69
|
+
return { file_token: result.fileToken, ...dimensions };
|
|
70
|
+
// 下一步经业务 FaaS 挂载;上传成功不代表单元格写入成功。
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
baseToken/tableId 来自 Catalog,必须成对属于应用绑定;不传 fieldId。只接受非空 File,上限 2 GiB,不接受 Blob;普通 HTTP bridge 不上传文件。返回 fileToken 是资源标识,name/size/mimeType 是客户端信息;持久化权威值以服务端为准。临时下载 URL 不持久化或写入日志。
|
|
75
|
+
进度以回调 total 为分母,100% 不等于完成。首条进度之后连续 120 秒无新进度会报告停滞,未收到首条进度可能仍在排队;超时不是上传未发生的证明。先保留 token 并核对结果,不自动重传。
|
|
76
|
+
上传不会自动挂载到单元格。图片宽高应从原始 File 解码后透传给业务 FaaS;移除附件引用不等于删除云盘文件。展示使用 /page 或 /components 的 [`AttachmentImage`](exports.md#page);[`fetchAttachmentCover`](exports.md#page) 返回 Blob,[`fetchAttachmentDownloadUrl`](exports.md#page) 返回临时链接,参数及完整附件坐标见 [兼容 API](compat.md) 与 [组件](components.md)。写入由业务 FaaS 调用 /server 的 ctx.records,见 [服务端 SDK](server.md);页面不导入 server。附件业务规则见 随 CLI 安装的 lark-base-database Skill 中的「Skill 文件传输」(references/file-transfer.md)。
|
|
77
|
+
|
|
78
|
+
## 其他兼容导出
|
|
79
|
+
|
|
80
|
+
[`invokeApi`](exports.md#page) 使用 [`FunctionMap`](exports.md#page) 定义的 function 点号路径,不是 Database Function ID。
|
|
81
|
+
[`fetch`](exports.md#page)/[`invokeHttp`](exports.md#page) 返回 Response。[`callSiteAIFunction`](exports.md#page)、任务与流式 API、[`SiteChatbotStreamAdapter`](exports.md#page) 保持兼容导出;已有返回联合类型和事件语义不变。
|
|
82
|
+
[`invokeDatabaseFunction`](exports.md#page)、[`invokeDatabaseMutationBatch`](exports.md#page)、[`resolveDatabaseContext`](exports.md#page) 已由 /page 提供,保持老模板语义,见 [兼容 API](compat.md)。[`invokeApi`](exports.md#page) 的 function 点号路径不能替代 Database Function ID。
|
|
83
|
+
|
|
84
|
+
SDK 文档说明本版本 API、组件、兼容语义与可编译示例。业务规则、服务端契约、Mutation 结果、CLI 命令与发布流程,请查阅 [@lark-base-open/base-database-cli](https://www.npmjs.com/package/@lark-base-open/base-database-cli) 随包安装的 lark-base-database Skill 及其 references。升级 CLI 时以该版本随包 Skill 为准。
|
|
85
|
+
|
|
86
|
+
## 本机开发上下文
|
|
87
|
+
|
|
88
|
+
独立路由为 `https://<宿主>/baseapp/dev/<siteToken>/<port>`,端口仅限 43100–43109,
|
|
89
|
+
本地 origin 固定为 `http://127.0.0.1:<port>`,加载根路径 `/`。使用 CLI 输出的带配对令牌片段链接打开。
|
|
90
|
+
宿主检查编辑权限并完成挑战配对,通过后直接进入应用,常驻提示本机地址、preview 环境与写入范围;不再提供应用内确认页。
|
|
91
|
+
浏览器原生“本地网络访问”授权是访问 loopback 的前置条件:若浏览器提示,需允许当前宿主访问本机网络;此前已拒绝时到该站点的浏览器权限设置恢复。
|
|
92
|
+
这项浏览器授权独立于应用内配对,不能由 SDK 代替用户授予。
|
|
93
|
+
无令牌或非编辑者直接打开线上版本;复制地址栏链接给别人会自动打开线上版本。普通入口不加载 loopback。
|
|
94
|
+
页面隐藏不销毁,聚焦与30秒轮询复查权限;离开、本地登出或检测到权限失效即回收;当前标签刷新复用 sessionStorage 令牌,但重新挑战配对。不调用服务端开发会话接口。
|
|
95
|
+
|
|
96
|
+
`SiteRuntimeContext.development` 可选,结构为
|
|
97
|
+
`{ localOrigin: string, projectEnv: 'preview', artifactVersion: string | null }`。
|
|
98
|
+
本机页面没有平台发布制品版本时 `artifactVersion` 为 null;上下文不携带凭据。
|
|
99
|
+
Function 仍通过宿主代理访问远端 preview,不能以该字段获取权限。
|
|
100
|
+
main.tsx 在开发和生产都直接导入配置;Vite 插件仅提供服务端配对接口,不生成启动虚拟模块。
|
|
101
|
+
入口是否可用由宿主环境 CSP 策略决定;普通宿主 CSP 不因开发模式而放宽。
|
package/docs/server.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# 服务端 SDK
|
|
2
|
+
|
|
3
|
+
`server` 支持 Node ES2022 的 ESM 和 CJS,无 React、DOM 和 channel 运行依赖。类型消费者需要 Node 类型;Standard Schema 使用精确版本 `@standard-schema/spec@1.0.0` 的类型接口,不引入验证库运行时。Schema 可同步或异步,不要求实现 JSON Schema 转换。契约生成由 CLI 负责。
|
|
4
|
+
|
|
5
|
+
`defineFunction({input, output, handler})` 保持 `(rawParams, context)` ABI。输入转换结果交给 handler,输出转换结果经过无损 JSON 检查,再返回 `{code:0,msg:'success',data}`。业务对象里的 code/data/msg 都是业务数据。void 业务请返回 null。拒绝 undefined、BigInt、非有限数、Date、循环、getter、Symbol、稀疏数组、方法和 toJSON;先由 Schema 转换成 JSON 值可通过。
|
|
6
|
+
|
|
7
|
+
输入 issues 映射 400;输出 issues、校验器异常、非 JSON 输出及普通异常映射 500。[`SDKError`](exports.md#server) 可设置有限非零整数 wireCode,SDK_TIMEOUT 默认 504、SDK_CANCELLED 默认 499。safeMessage 必须是开发者提供的安全文案;不要传原始 error.message。结构化诊断仅记录码、字段路径、retryable、outcome,不记录校验消息或被拒值。网关可能脱敏 500,不能承诺客户端看到 SDK 字符串码。
|
|
8
|
+
|
|
9
|
+
旧 faas-entry 对错误返回仍会构造 `{code,msg,data:{code,msg}}`;先判断外层 code,错误 data 不是输出契约。运行时实现不需要升级。跨 server/shared、ESM/CJS 的 [`SDKError`](exports.md#server) 用同版本 Symbol 标识识别。
|
|
10
|
+
|
|
11
|
+
<!-- example: server.ts -->
|
|
12
|
+
```ts
|
|
13
|
+
import { z } from "zod";
|
|
14
|
+
import {
|
|
15
|
+
defineFunction,
|
|
16
|
+
bindFunctionRuntime,
|
|
17
|
+
} from "@lark-base-open/base-database-app-sdk/server";
|
|
18
|
+
// 实际工程将 input/output 放独立 contract.ts,CLI 只提取 Schema 依赖图。
|
|
19
|
+
const input = z.object({ title: z.string().min(1) });
|
|
20
|
+
const ignored = z.object({
|
|
21
|
+
id: z.string(),
|
|
22
|
+
name: z.string(),
|
|
23
|
+
reason: z.string(),
|
|
24
|
+
});
|
|
25
|
+
const output = z.discriminatedUnion("status", [
|
|
26
|
+
z.object({
|
|
27
|
+
status: z.literal("success"),
|
|
28
|
+
appliedIds: z.array(z.string()),
|
|
29
|
+
notFoundIds: z.tuple([]),
|
|
30
|
+
ignoredFields: z.tuple([]),
|
|
31
|
+
attachments: z.json().optional(),
|
|
32
|
+
noop: z.boolean().optional(),
|
|
33
|
+
}),
|
|
34
|
+
z.object({
|
|
35
|
+
status: z.literal("partial"),
|
|
36
|
+
appliedIds: z.array(z.string()),
|
|
37
|
+
notFoundIds: z.array(z.string()),
|
|
38
|
+
ignoredFields: z.array(ignored),
|
|
39
|
+
attachments: z.json().optional(),
|
|
40
|
+
}),
|
|
41
|
+
z.object({
|
|
42
|
+
status: z.literal("failed"),
|
|
43
|
+
code: z.string(),
|
|
44
|
+
upstreamCode: z.number().optional(),
|
|
45
|
+
retryable: z.literal(false),
|
|
46
|
+
appliedIds: z.tuple([]),
|
|
47
|
+
}),
|
|
48
|
+
z.object({
|
|
49
|
+
status: z.literal("unknown"),
|
|
50
|
+
code: z.string(),
|
|
51
|
+
retryable: z.literal(false),
|
|
52
|
+
requestedIds: z.array(z.string()).optional(),
|
|
53
|
+
logid: z.string().optional(),
|
|
54
|
+
}),
|
|
55
|
+
]);
|
|
56
|
+
const handler = defineFunction({
|
|
57
|
+
input,
|
|
58
|
+
output,
|
|
59
|
+
handler: (params, ctx) =>
|
|
60
|
+
ctx.records.create(
|
|
61
|
+
{ baseToken: "baseFromCatalog", tableId: "tblFromCatalog" },
|
|
62
|
+
[{ title: params.title }],
|
|
63
|
+
),
|
|
64
|
+
});
|
|
65
|
+
// 此绑定入口由 CLI 生成;业务 handler 不选择区域、不持有身份凭据。
|
|
66
|
+
export default bindFunctionRuntime(handler, {
|
|
67
|
+
platformEnv: "online",
|
|
68
|
+
brand: "feishu",
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
[`bindFunctionRuntime`](exports.md#server) 为每次调用复制 context 并添加私有非凭据 profile,不改全局或原 context。CLI 为模板 1/2 的 defineFunction 入口注入 profile 和 runtime 绑定;模板 1 从当前已验证账号品牌与工程 runtimeProfile 生成,模板 2 使用 generated/runtime-profile.ts。未绑定 profile 时 records 返回 SDK_RUNTIME_PROFILE_MISSING;不支持的环境/品牌返回 SDK_RUNTIME_PROFILE_UNSUPPORTED。online 支持 feishu/lark,pre、boe 仅支持 feishu。域名只是原 runtime 的路由标记,真实回调地址和身份仍由平台控制;SDK 不发身份头、不猜域、不跨区重试。
|
|
73
|
+
|
|
74
|
+
| records 操作 | 参数与限制 |
|
|
75
|
+
| --- | --- |
|
|
76
|
+
| create | [`Target`](exports.md#server) + 1–200 个字段对象;返回 ID 必须逐条对应 |
|
|
77
|
+
| update | [`Target`](exports.md#server) + record ID 到 patch 的映射,1–200 条;applied/notFound 必须完整、不重叠 |
|
|
78
|
+
| delete | [`Target`](exports.md#server) + 1–200 个不同 record ID;applied 为回显减 notFound |
|
|
79
|
+
| appendAttachments | record ID → 字段名或 ID → file_token/可选图片尺寸数组 |
|
|
80
|
+
| removeAttachments | 同样的映射,数组项只允许 file_token |
|
|
81
|
+
|
|
82
|
+
[`Target`](exports.md#server) 为 catalog 的 baseToken/tableId。record ID 校验 `^rec[A-Za-z0-9]{1,32}$`。附件最多 50 个 token,空字段数组和空记录映射被移除,全空本地 no-op。响应按 data.attachments 的字段 ID 读取,不以请求中的字段名猜测。
|
|
83
|
+
|
|
84
|
+
统一 [`WriteResult`](exports.md#server):success 含 appliedIds;partial 保留 appliedIds、notFoundIds 和 ignoredFields;本地校验/profile/发送前取消返回 failed;HTTP 401/403/404 及明确权限/不存在业务码(91403、1254302、1254043、1254044)返回 failed 并保留 upstreamCode;其余已发请求后的网络错误、超时、取消、业务错误或不完整回执返回 unknown。HTTP 200 也必须检查 body.code。SDK 不会盲目把下游错误码当作“确定未写入”。不拆批、不自动重试;取消不撤销写入。unknown 后应核对结果,附件保留已上传 token。仅实际响应含 logid 头才保留,记录回调通常没有该头。
|
|
85
|
+
|
|
86
|
+
[兼容 API](compat.md) · [公开导出](exports.md)
|
|
87
|
+
|
|
88
|
+
示例与测试使用精确固定的 Zod 4.2.1(devDependency),验证真实 Standard Schema 的转换、refine 与异步校验;SDK 的 server 产物不捆绑 Zod。消费此示例的工程需自行固定安装 Zod。
|
|
89
|
+
|
|
90
|
+
SDK 的 ctx.records 方法和 [`WriteResult`](exports.md#server) 是本包 API;底层记录 payload、CellValue、Mutation 与服务端业务规范以 Skill 为唯一正文来源。
|
|
91
|
+
|
|
92
|
+
SDK 文档说明本版本 API、组件、兼容语义与可编译示例。业务规则、服务端契约、Mutation 结果、CLI 命令与发布流程,请查阅 [@lark-base-open/base-database-cli](https://www.npmjs.com/package/@lark-base-open/base-database-cli) 随包安装的 lark-base-database Skill 及其 references。升级 CLI 时以该版本随包 Skill 为准。
|
package/docs/shared.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# 共享类型与诊断
|
|
2
|
+
|
|
3
|
+
/shared 同时提供 ESM/CJS,不依赖 React、DOM 或 channel。提供 [`SDKError`](exports.md#shared)、[`SDKErrorOptions`](exports.md#shared)、[`FunctionEnvelope`](exports.md#shared)、[`WriteResult`](exports.md#shared)、[`JsonValue`](exports.md#shared),以及能力目录、能力类型和 [`classifyRuntimeError`](exports.md#shared);精确名单见 [导出清单](exports.md#shared)。
|
|
4
|
+
|
|
5
|
+
[`FunctionEnvelope`](exports.md#shared) 是调用包裹类型;[`WriteResult`](exports.md#shared) 的 success/partial/failed/unknown 表示记录写入结果,见 [server](server.md)。[`JsonValue`](exports.md#shared) 描述 JSON 数据;类型断言不能替代 [`defineFunction`](exports.md#server) 的运行时校验。[`classifyRuntimeError`](exports.md#shared) 只按稳定错误码分类,不推断原始错误消息,也不证明写入是否发生。
|
|
6
|
+
|
|
7
|
+
SDK 文档说明本版本 API、组件、兼容语义与可编译示例。业务规则、服务端契约、Mutation 结果、CLI 命令与发布流程,请查阅 [@lark-base-open/base-database-cli](https://www.npmjs.com/package/@lark-base-open/base-database-cli) 随包安装的 lark-base-database Skill 及其 references。升级 CLI 时以该版本随包 Skill 为准。
|
|
8
|
+
|
|
9
|
+
<!-- example: shared.ts -->
|
|
10
|
+
```ts
|
|
11
|
+
import { classifyRuntimeError, type WriteResult } from '@lark-base-open/base-database-app-sdk/shared';
|
|
12
|
+
export function writeMessage(result: WriteResult): string {
|
|
13
|
+
switch (result.status) {
|
|
14
|
+
case 'success': return `已写入 ${result.appliedIds.length} 条`;
|
|
15
|
+
case 'partial': return '部分完成,请检查缺失记录和忽略字段';
|
|
16
|
+
case 'failed': return '请求未发送,请修正输入或运行环境';
|
|
17
|
+
case 'unknown': return '结果待核对,请勿直接重试';
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export const describeError = classifyRuntimeError;
|
|
21
|
+
```
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# 启动应用
|
|
2
|
+
|
|
3
|
+
从 `/page` 导入 [`startBaseApp`](exports.md#page),传入 `app: App`(React ComponentType),通过 `/page/styles.css` 加载组件样式。SDK 不认识业务 App 的路径,也不会在 Node 侧执行业务代码。返回 `{ ready, dispose }`;ready 是首次 React effect 提交后的宿主通知 Promise。调用者应处理 ready 拒绝,并在热替换或移除页面时 dispose。
|
|
4
|
+
|
|
5
|
+
选项与默认值:root 为 `#root`(可传选择器或 HTMLElement);strictMode、linkGuard、reportLifecycle、platformUI 均为 true。theme 为 host,取宿主 colorScheme;locale 为 host,取宿主语言,不支持的语言映射 en。locale 可显式设 zh/en/ja,theme 可设 light/dark。providers 接收 ReactNode 并返回包裹后的 ReactNode;slots.loading 与 slots.error 接受 React 组件,error 只收到安全 code。onDiagnostic 接收 SDK 的脱敏 [`RuntimeDiagnostic`](exports.md#page)。
|
|
6
|
+
|
|
7
|
+
顺序:解析 root、发起 channel 上下文请求、能力感知链接拦截、Provider/错误边界/App、首次 effect 后 ready。StrictMode 不重复上报。启动错误报 APP_BOOT_FAILED;ready 之后的业务错误只交 onDiagnostic。root 缺失使 ready 拒绝,重复挂载同一 root 同步抛 APP_ALREADY_STARTED。dispose 幂等,卸载 React、退订 mode、清理链接拦截和 UI 缓存;ready 前 dispose 使 ready 拒绝 APP_DISPOSED。共享 channel 连接不销毁,以免影响同页面其他调用者。
|
|
8
|
+
|
|
9
|
+
linkGuard:false 完全不安装拦截;能力不支持时 SDK 同样保持原生行为。reportLifecycle:false 不发 ready/load-failed,但 ready Promise 仍表达本地挂载结果。platformUI:false 隐藏默认悬浮按钮,不改变权限或宿主开发标识。关闭选项用于迁移中明确选择行为。
|
|
10
|
+
|
|
11
|
+
开发与生产统一由 main.tsx 直接导入 base-app.config.ts 并调用 startBaseApp(options),不使用虚拟入口。CLI 自动安装 `/vite` 的配对服务端中间件;HMR 不重配对,整页或 iframe 重载才配对。
|
|
12
|
+
|
|
13
|
+
运行示例与构建类型检查位于包内 examples;SDK 尚未发布时只使用本批 tarball 验证,不代表 registry 已可安装。
|
|
14
|
+
|
|
15
|
+
<!-- example: start-base-app.tsx -->
|
|
16
|
+
```tsx
|
|
17
|
+
import { startBaseApp } from '@lark-base-open/base-database-app-sdk/page';
|
|
18
|
+
import '@lark-base-open/base-database-app-sdk/page/styles.css';
|
|
19
|
+
function App() { return <main>订单工作台</main>; }
|
|
20
|
+
export function mountApp() {
|
|
21
|
+
const app = startBaseApp({ app: App, locale: 'zh', theme: 'host' });
|
|
22
|
+
void app.ready.catch(() => console.warn('应用启动失败'));
|
|
23
|
+
return () => app.dispose(); // 卸载或 HMR 时调用。
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
<!-- example: vite.ts -->
|
|
28
|
+
```ts
|
|
29
|
+
import { baseAppDevelopmentPairingPlugin } from '@lark-base-open/base-database-app-sdk/vite';
|
|
30
|
+
export const plugins = [baseAppDevelopmentPairingPlugin()];
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## 本地开发配对(FE1C)
|
|
34
|
+
|
|
35
|
+
使用 CLI 输出的 `/baseapp/dev/<siteToken>/<port>#t=<令牌>` 打开;宿主读取后立刻移除片段并按站点、端口存当前标签 sessionStorage。复制地址栏链接给别人会自动打开线上版本。无令牌或非编辑者直接降级,有令牌但未连接/配对失败保持错误页。普通线上页面不启用此协议。
|
|
36
|
+
|
|
37
|
+
访问本机服务前,浏览器可能要求当前宿主的原生“本地网络访问”授权;需在浏览器提示或站点权限设置中允许。它是浏览器安全前置,不是应用内确认页,也不由 SDK 自动授予;配对成功后应用直接进入。
|
|
38
|
+
|
|
39
|
+
`development-pairing.json` 是 CLI、宿主和 SDK 的协议常量唯一来源;其他仓的镜像必须逐字段校验。CLI 每项目生成 32 字节随机数的 base64url 令牌,持久化并仅通过 `BASE_APP_DEV_PAIRING_TOKEN` 环境变量交给 Vite。`baseAppDevelopmentPairingPlugin()` 必须排在中间件链最前,令牌不进入客户端产物、日志或 NDJSON。
|
|
40
|
+
|
|
41
|
+
插件在实际监听端口的 `http://127.0.0.1:43100` 至 `43109` 提供 `POST /__base_app/dev-pair`,请求体为 `{challenge}`(32 字节随机数的 base64url)。要求 Host 精确匹配监听地址;Origin 精确等于本机 origin,或无 Origin 时 Sec-Fetch-Site 为 same-origin。显式外域/null Origin 即使带 same-origin 也拒绝。响应无 CORS 头、禁止缓存;服务端按监听端口确定 origin,不读取请求体 origin。
|
|
42
|
+
|
|
43
|
+
返回 `{proof}`,proof 为 `HMAC-SHA256(UTF8(令牌字符串), UTF8("base-app-dev-pair.v1\n" + localOrigin + "\n" + challenge))` 的 base64url。令牌不先 base64url 解码;宿主 WebCrypto 必须采用相同 key 字节。每次 channel 握手宿主生成新挑战,通过 `site.v1.development.pair` 调用 iframe,iframe 同源 fetch 后返回 proof;宿主常量时间验证通过,广播 `site.v1.development.paired` 的 `{challenge}`。SDK 启动和 runtime Promise 方法等待匹配的 accepted,宿主仍负责在配对前拒绝所有业务调用(不能依赖客户端 gate 作为安全边界)。旧 SDK 不支持配对时宿主失败关闭并要求升级。
|
|
44
|
+
|
|
45
|
+
Vite HMR 不关闭 gate,不重新配对;仅整页或 iframe 重载重新配对。普通页面与旧 channel 宿主/夹具组合保留原行为。
|
|
46
|
+
|
|
47
|
+
平台 UI 使用 @tanstack/react-query,BaseAppProvider 管理缓存生命周期和应用/环境/身份隔离;它是组件的必需 peer。React/ReactDOM peer 接受 ^19.2.0,业务可按锁文件使用兼容补丁版本。server/shared 没有 React 运行时导入。
|
package/docs/testing.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# 测试替身
|
|
2
|
+
|
|
3
|
+
安装可选 peer msw@2.11.6 后从 SDK/testing 导入。单元/组件测试使用 [`createMockHost`](exports.md#testing)().channel;不需要手工 mock channel 模块。替身无真实身份、无后端或 seclink 服务,不作为开发主路径或真实授权证明。开发时应使用真实宿主会话。
|
|
4
|
+
|
|
5
|
+
<!-- example: mock.ts -->
|
|
6
|
+
```ts
|
|
7
|
+
import { createMockHost } from '@lark-base-open/base-database-app-sdk/testing';
|
|
8
|
+
import { createSiteRuntimeClient } from '@lark-base-open/base-database-app-sdk/page';
|
|
9
|
+
export async function testUser() {
|
|
10
|
+
const host = createMockHost({ capabilities: { 'function.invoke': { version: 1 } } });
|
|
11
|
+
const runtime = createSiteRuntimeClient(host.channel);
|
|
12
|
+
try { return await runtime.getCurrentUser(); }
|
|
13
|
+
finally { host.dispose(); }
|
|
14
|
+
}
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
`createMockHost({context,capabilities,bindings,allowedRoutes,secureLinkAvailable,functions,handlers,failures})`:context 提供模拟 locale/env/siteToken;capabilities 缺失模拟 84afb07 宿主,空表显式关闭能力;bindings 默认空,拒绝全部上传;allowedRoutes 默认固定宿主白名单。
|
|
18
|
+
11 个 site.v1 API 都有默认响应,业务函数只有显式登记才允许;`handlers` 可替换下游结果,但不能绕过前置拒绝规则。`failures` 为指定 API 注入错误码。HTTP handler 返回 wire response(body/headers/status/statusText),runtime client 转为 Response。
|
|
19
|
+
`calls/events` 仅保留测试夹具数据;dispose 清理监听、流、挂起请求和浏览器宿主。HTTP/上传支持定点取消;可通过 emit 注入进度、mode、AI error/done,通过 emitStreamMessage 注入带 seq 的流消息。默认不模拟真实上传耗时、AI 内容、并发排队、seclink 重定向或 backend 身份。
|
|
20
|
+
`mount(container,url,allowedOrigin?)` 在真实浏览器中使用 channel 的 Host 创建 iframe;默认 origin 来自 url,sandbox 保留脚本/同源/下载限制,不允许弹窗(不模拟表单和模态框)。返回卸载函数。origin/source 错误由真实 channel 丢弃,不虚构业务错误回包;直接 channel 对象是内存调用,不声称校验浏览器 origin。
|
|
21
|
+
|
|
22
|
+
## HTTP 层 MSW
|
|
23
|
+
|
|
24
|
+
<!-- example: msw.ts -->
|
|
25
|
+
```ts
|
|
26
|
+
import { createHttpHandlers } from '@lark-base-open/base-database-app-sdk/testing';
|
|
27
|
+
export const handlers = createHttpHandlers([
|
|
28
|
+
{ method: 'post', url: 'https://service.example/items', status: 201, body: { id: 'item-1' } },
|
|
29
|
+
]);
|
|
30
|
+
// 交给 MSW setupServer(...handlers) 或 setupWorker(...handlers)。
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
MSW 工厂仅服务真正的 HTTP 请求,如 FaaS 下游或页面直接请求。宿主 postMessage 调用不经过网络,需上述 mock host。测试入口及 MSW 不会被 page/server/shared 导入。
|
|
34
|
+
|
|
35
|
+
## 拒绝规则来源与逐条对照
|
|
36
|
+
|
|
37
|
+
固定来源:base-site-web `84afb07a7a4c05c09f7791cb67c4fe82bac23df8`,2026-10-09。下表路径均相对该仓根,行号按原源码。源码只读快照存于 test/fixtures/host-84afb07,provenance.json 锁定 SHA256。test/host-fixture.ts 在隔离 VM 中执行原源码,仅替换日志、平台配置、身份注入与下游传输;拒绝规则不替换。
|
|
38
|
+
|
|
39
|
+
| 规则 | 原文件:行 | 测试 |
|
|
40
|
+
|---|---|---|
|
|
41
|
+
| origin 白名单,绑定 sourceWindow | apps/site-web/src/view/preview/runtime-host.ts:106;channel src/host.ts:255(935e447) | test/browser/run.mjs:真实 Host 接受正确请求,错误 origin/source 不调用 handler |
|
|
42
|
+
| http/https/mailto/tel 之外链接,空 URL、相对 URL | apps/site-web/src/view/preview/open-link.ts:17 | rules.test.ts 链接矩阵,javascript/data/file 逐例 |
|
|
43
|
+
| http(s) seclink 不可用 | apps/site-web/src/view/preview/open-link.ts:35 | rules.test.ts seclink 不可用 |
|
|
44
|
+
| 单斜杠、跨域、反斜杠、fragment、编码、穿越、控制字符 | apps/site-web/src/view/preview/http-proxy.ts:81 | rules.test.ts HTTP 路径矩阵 |
|
|
45
|
+
| Header 类型、名称、禁止清单及前缀 | apps/site-web/src/view/preview/http-proxy.ts:108 | rules.test.ts 每个受限头单独用例 |
|
|
46
|
+
| method、body、GET body、timeout | apps/site-web/src/view/preview/http-proxy.ts:133 | rules.test.ts HTTP init 矩阵 |
|
|
47
|
+
| 默认路由白名单/方法 | apps/site-web/src/view/preview/http-bridge-allowlist.ts:24、134 | rules.test.ts 非白名单与合法读取;默认表逐条等价测试 |
|
|
48
|
+
| responseType、空/重复/过长 requestId | apps/site-web/src/view/preview/http-proxy.ts:212 | rules.test.ts 与 runtime.test.ts |
|
|
49
|
+
| 上传 File、非空、2 GiB、Base/表成对绑定 | apps/site-web/src/view/preview/attachment-proxy.ts:51、312 | rules.test.ts 附件矩阵 |
|
|
50
|
+
| 封面/下载 file/table/field/record、应用、环境、单文件对应 | apps/site-web/src/view/preview/http-attachment-context.ts:24–106 | rules.test.ts 下载坐标矩阵及封面缺坐标拒绝 |
|
|
51
|
+
| Function 格式与发布清单 | apps/site-web/src/view/preview/function-proxy.ts:40–55 | runtime.test.ts 函数拒绝 |
|
|
52
|
+
|
|
53
|
+
实现采用原宿主的纯校验片段(HTTP normalize、白名单匹配、附件上下文);仅移除平台配置读取并导出测试需要的函数,不复制 channel 实现。宿主版本变化时需更新快照、规则和矩阵一起审查。生产鉴权仍由真正的宿主和服务端执行,模拟成功不证明真实权限。
|
|
54
|
+
|
|
55
|
+
## 验证入口
|
|
56
|
+
|
|
57
|
+
npm test 执行构建、node:test、入口依赖扫描和 examples 的 TypeScript 编译;server/shared 还在无 DOM、带 Node types 的配置下检查。test/browser/run.mjs 为本地真实 Chrome 页面夹具服务,使用 agent-browser 独立会话访问并采集结果;浏览器检查需显式执行,未宣称已自动接入仓外 CI。
|
package/docs/upgrade.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# 迁移到 SDK 0.0.1-alpha.0
|
|
2
|
+
|
|
3
|
+
先核对工程 AGENTS.md 中的模板版本、实际安装 SDK 和 CLI 随包 Skill。SDK 未发布期间仅在本地验收副本使用经审核 tarball;正式模板不写 file:/tarball 依赖,也不伪造 registry lock。老模板持续兼容并维护安全与事实更正,未设截止日期。
|
|
4
|
+
|
|
5
|
+
## 老模板 database-runtime.ts → SDK(仍可保持模板 1)
|
|
6
|
+
|
|
7
|
+
1. 保存可回滚的源码和锁文件,核对 page/src/lib/database-runtime.ts 是否有业务定制。显式选择并安装 SDK 到 Page 工程,按其 peerDependencies 对齐 React/react-dom 与 TanStack Query;MSW 仅测试入口需要。codemod 不选择版本、不安装依赖。
|
|
8
|
+
2. 在项目根运行 `base-database-cli codemod database-runtime .` 查看默认 dry-run 的 changes/conflicts。仅受支持原模板指纹可自动迁移;定制 helper、未知引用或类型冲突须先人工审查,不覆盖定制逻辑。
|
|
9
|
+
3. 审查后运行 `base-database-cli codemod database-runtime . --apply`。存在冲突时不应用;检查输出 applied 和文件 diff。该操作迁移 helper 引用并适配输入类型声明,不搬目录、不升级模板版本、不迁移组件或启动入口。再次 dry-run 应无 changes。
|
|
10
|
+
4. 对使用 [`defineFunction`](exports.md#server) 的 FaaS,把 input/output 放独立 contract.ts,在项目根运行 `base-database-cli contracts generate .`,检查生成契约、输入扩充和页面客户端。生成失败或 runtime-only 警告须处理;Database Function 输出仍为 unknown。生成器属于 CLI,SDK 没有 createFunctionClient 导出,消费工程生成的客户端。
|
|
11
|
+
5. 手工检查其余 channel/runtime 导入与 [`FunctionMap`](exports.md#page) 扩充:channel 的 [`FunctionMap`](exports.md#page) 声明目标保持原样;[`DatabaseFunctionInputMap`](exports.md#page) 的新扩充目标为 SDK/page。兼容调用仍返回 envelope,HTTP 失败仍抛错,不二次解包。详见 [兼容说明](compat.md)。
|
|
12
|
+
6. 选择保留旧启动入口或采用 [startBaseApp](start-base-app.md)。采用后移除重复的 createRoot、guard、ready/load-failed 和默认悬浮按钮;保留业务 Provider、样式及路由。仅迁移 import 不要求更换布局。
|
|
13
|
+
7. 运行类型检查及 `base-database-cli check --json`;按 Skill 的验收和发布流程验证真实宿主中的链接、附件、成员选择与写入结果。验证失败时恢复源码与锁文件,不能把 mock 成功当作真实权限证明。
|
|
14
|
+
|
|
15
|
+
guard 仍同步返回 cleanup,安装前异步查询能力。缺 capabilities 字段采用 host-baseline-84afb07;显式空表或缺 link.open 时不拦截。只保留一处 guard;查询期间原生行为仍受 iframe sandbox 约束。测试替身只在测试中导入。
|
|
16
|
+
|
|
17
|
+
## 模板 1 → 2:手动迁移
|
|
18
|
+
|
|
19
|
+
codemod 不做模板结构升级;应在独立副本逐项迁移。模板 2 当前非默认,公开 SDK 安装/根锁及远端流水线尚待发布和验收,本地构建通过不代表可远端交付。
|
|
20
|
+
|
|
21
|
+
- 将 server-functions/functions 移到 server/functions;保留 Function ID、meta.json 和业务 handler,database-functions、mutations、Catalog 与应用绑定保持对应关系。移除旧布局前核对搬迁完整性,避免同时留下 server 与 server-functions。
|
|
22
|
+
- 根 package.json 使用 private、type:module 和 workspaces:[page,server];两成员保留各自依赖声明,统一到唯一根 package-lock.json。合并依赖版本后重新生成根锁,不拼接两份旧锁,不保留成员锁或嵌套 workspace。正式锁须在依赖公开可安装后生成。
|
|
23
|
+
- base-database.jsonc 标明 template:{name:"database-starter",version:2}。不能只改版本字段而保留旧目录;CLI 会拒绝混杂布局或锁冲突。
|
|
24
|
+
- 根 base-app.config.ts 绑定业务 App、Provider 与启动选项;page/src/main.tsx 从 SDK/page 导入 [`startBaseApp`](exports.md#page) 并传入 options,同时加载 SDK/page/styles.css 和业务 CSS。生产入口直接导入,不依赖仅 serve 可用的 virtual:base-app。
|
|
25
|
+
- 将复制的平台组件导入改为 SDK/page 或 SDK/components;核对 props、插槽、语言和样式覆盖,保留业务组件。平台权限仍由宿主判断。
|
|
26
|
+
- server 源码采用 ESM,TypeScript 配置 noEmit;由 CLI build-server 做类型检查并输出兼容运行时的 CJS,不把 tsc emit 当作打包。不要手写 runtime profile 或凭据,绑定由 CLI 生成。
|
|
27
|
+
- 再运行 contracts generate、Page 构建、build-server 和 check;依 Skill 做 Preview 与真实浏览器验收。遇到 REMOTE_TEMPLATE_2_BUILD_UNVERIFIED 保留未验收状态,不以本地成功消除警告。
|
|
28
|
+
|
|
29
|
+
SDK 文档说明本版本 API、组件、兼容语义与可编译示例。业务规则、服务端契约、Mutation 结果、CLI 命令与发布流程,请查阅 [@lark-base-open/base-database-cli](https://www.npmjs.com/package/@lark-base-open/base-database-cli) 随包安装的 lark-base-database Skill 及其 references。升级 CLI 时以该版本随包 Skill 为准。
|
|
30
|
+
|
|
31
|
+
老工程参考:随 CLI 安装的 lark-base-database Skill 中的「页面」(references/legacy-development.md)、随 CLI 安装的 lark-base-database Skill 中的「AI」(references/legacy-ai-function.md)、随 CLI 安装的 lark-base-database Skill 中的「附件」(references/legacy-file-transfer.md)。命令选项与发布步骤以对应 CLI 随包 Skill 为准,此处仅标明迁移中需要调用的命令。
|
|
32
|
+
|
|
33
|
+
保留旧启动入口的最小示例(采用 [`startBaseApp`](exports.md#page) 时不要重复调用):
|
|
34
|
+
|
|
35
|
+
<!-- example: upgrade.ts -->
|
|
36
|
+
```ts
|
|
37
|
+
// 模板 1 可先迁移 helper 导入;切换启动入口与模板 2 是独立的手动步骤。
|
|
38
|
+
import { installLinkGuard, siteRuntime, classifyRuntimeError } from '@lark-base-open/base-database-app-sdk/page';
|
|
39
|
+
export function initializeRuntime() {
|
|
40
|
+
const uninstall = installLinkGuard();
|
|
41
|
+
void siteRuntime.ready().catch(error => console.warn(classifyRuntimeError(error)));
|
|
42
|
+
return uninstall;
|
|
43
|
+
}
|
|
44
|
+
```
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { getRuntimeCapabilities, requireCapability, classifyRuntimeError } from '@lark-base-open/base-database-app-sdk/page';
|
|
2
|
+
export async function inspectHost() {
|
|
3
|
+
const capabilities = await getRuntimeCapabilities();
|
|
4
|
+
try { await requireCapability('function.invoke'); }
|
|
5
|
+
catch (error) { return { capabilities, diagnostic: classifyRuntimeError(error) }; }
|
|
6
|
+
return { capabilities };
|
|
7
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {
|
|
2
|
+
invokeDatabaseFunction,
|
|
3
|
+
invokeDatabaseMutationBatch,
|
|
4
|
+
type DatabaseFunctionResponse,
|
|
5
|
+
} from "@lark-base-open/base-database-app-sdk/page";
|
|
6
|
+
// CLI 生成到工程文件;空 map 不接受任意 string。
|
|
7
|
+
declare module "@lark-base-open/base-database-app-sdk/page" {
|
|
8
|
+
interface DatabaseFunctionInputMap {
|
|
9
|
+
list_orders: { status: string };
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export async function loadOrders(): Promise<DatabaseFunctionResponse> {
|
|
13
|
+
return invokeDatabaseFunction("list_orders", { status: "open" });
|
|
14
|
+
}
|
|
15
|
+
export async function writeOrders() {
|
|
16
|
+
const result = await invokeDatabaseMutationBatch("update_order", [
|
|
17
|
+
{ recordId: "rec1", status: "done" },
|
|
18
|
+
]);
|
|
19
|
+
// 兼容 API 保留 envelope,不能把 code/data/msg 再次解包。
|
|
20
|
+
return result;
|
|
21
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { AttachmentImage, BaseAppProvider, DatabaseUserMenu, UserSelectorEditor, type UserSelectorEditorValue } from '@lark-base-open/base-database-app-sdk/components';
|
|
3
|
+
import { siteRuntime, type DatabaseAttachmentRef } from '@lark-base-open/base-database-app-sdk/page';
|
|
4
|
+
export function Details({ attachment }: { attachment: DatabaseAttachmentRef }) {
|
|
5
|
+
const [value, setValue] = useState<UserSelectorEditorValue>();
|
|
6
|
+
return <BaseAppProvider runtime={siteRuntime} locale="zh" theme="light">
|
|
7
|
+
<DatabaseUserMenu audience="enterprise" />
|
|
8
|
+
<UserSelectorEditor value={value} onChange={setValue} />
|
|
9
|
+
<AttachmentImage attachment={attachment} alt="订单附件" size={360} />
|
|
10
|
+
</BaseAppProvider>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { StandardSchemaV1 } from "@standard-schema/spec";
|
|
2
|
+
import {
|
|
3
|
+
defineFunction,
|
|
4
|
+
type DefinedFunction,
|
|
5
|
+
} from "@lark-base-open/base-database-app-sdk/server";
|
|
6
|
+
import { invokeDatabaseFunction } from "@lark-base-open/base-database-app-sdk/page";
|
|
7
|
+
const transform: StandardSchemaV1<string, number> = {
|
|
8
|
+
"~standard": {
|
|
9
|
+
version: 1,
|
|
10
|
+
vendor: "type-test",
|
|
11
|
+
validate: (value) => ({ value: Number(value) }),
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
const output: StandardSchemaV1<number, string> = {
|
|
15
|
+
"~standard": {
|
|
16
|
+
version: 1,
|
|
17
|
+
vendor: "type-test",
|
|
18
|
+
validate: (value) => ({ value: String(value) }),
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
const fn = defineFunction({
|
|
22
|
+
input: transform,
|
|
23
|
+
output,
|
|
24
|
+
handler: (value) => value + 1,
|
|
25
|
+
});
|
|
26
|
+
const typed: DefinedFunction<typeof transform, typeof output> = fn;
|
|
27
|
+
void typed;
|
|
28
|
+
defineFunction({
|
|
29
|
+
input: transform,
|
|
30
|
+
output,
|
|
31
|
+
// @ts-expect-error handler 的参数是转换后的 number
|
|
32
|
+
handler: (value: string) => value.length,
|
|
33
|
+
});
|
|
34
|
+
// @ts-expect-error handler 返回必须满足 output 的输入 number
|
|
35
|
+
defineFunction({ input: transform, output, handler: () => "bad" });
|
|
36
|
+
export function rejectedCalls() {
|
|
37
|
+
// @ts-expect-error 生成 map 未声明此 ID
|
|
38
|
+
void invokeDatabaseFunction("missing", []);
|
|
39
|
+
// @ts-expect-error 统一 SQL 命名参数不能传位置数组
|
|
40
|
+
void invokeDatabaseFunction("list_orders", ["open"]);
|
|
41
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { SiteRuntimeContext } from "@lark-base-open/base-database-app-sdk/page";
|
|
2
|
+
export function localAddress(context: SiteRuntimeContext): string | undefined {
|
|
3
|
+
const development = context.development;
|
|
4
|
+
if (!development) return undefined;
|
|
5
|
+
const env: "preview" = development.projectEnv;
|
|
6
|
+
const version: string | null = development.artifactVersion;
|
|
7
|
+
// @ts-expect-error 轻量上下文没有服务端会话 ID。
|
|
8
|
+
development.sessionId;
|
|
9
|
+
// @ts-expect-error 轻量上下文没有服务端租约。
|
|
10
|
+
development.expiresAt;
|
|
11
|
+
return `${development.localOrigin} (${env}, ${version ?? "local"})`;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
import { siteRuntime } from "@lark-base-open/base-database-app-sdk/page";
|
|
15
|
+
export async function readDevelopmentOrigin() {
|
|
16
|
+
return (await siteRuntime.getRuntimeContext()).development?.localOrigin;
|
|
17
|
+
}
|
package/examples/http.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { invokeHttp, fetch as hostFetch } from '@lark-base-open/base-database-app-sdk/page';
|
|
2
|
+
export async function loadUser(signal: AbortSignal) {
|
|
3
|
+
const response = await invokeHttp({ url: '/space/api/user', signal });
|
|
4
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
5
|
+
const data: unknown = await response.json();
|
|
6
|
+
return data;
|
|
7
|
+
}
|
|
8
|
+
export async function loadWithFetch() {
|
|
9
|
+
const response = await hostFetch('/space/api/user');
|
|
10
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
11
|
+
return response.json() as Promise<unknown>;
|
|
12
|
+
}
|
package/examples/link.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { siteRuntime, hasCapability, openLink } from '@lark-base-open/base-database-app-sdk/page';
|
|
2
|
+
import type { SiteRuntimeContext } from '@lark-base-open/base-database-app-sdk/page';
|
|
3
|
+
export async function canOpenLink() {
|
|
4
|
+
const context: SiteRuntimeContext = await siteRuntime.getRuntimeContext();
|
|
5
|
+
return hasCapability(context, 'link.open');
|
|
6
|
+
}
|
|
7
|
+
// 在点击之前查询能力。用户点击时立即调用,避免因等待查询失去用户激活。
|
|
8
|
+
export function onSupportedLinkClick(url: string) {
|
|
9
|
+
return openLink(url);
|
|
10
|
+
}
|
package/examples/mock.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { createMockHost } from '@lark-base-open/base-database-app-sdk/testing';
|
|
2
|
+
import { createSiteRuntimeClient } from '@lark-base-open/base-database-app-sdk/page';
|
|
3
|
+
export async function testUser() {
|
|
4
|
+
const host = createMockHost({ capabilities: { 'function.invoke': { version: 1 } } });
|
|
5
|
+
const runtime = createSiteRuntimeClient(host.channel);
|
|
6
|
+
try { return await runtime.getCurrentUser(); }
|
|
7
|
+
finally { host.dispose(); }
|
|
8
|
+
}
|
package/examples/msw.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { createHttpHandlers } from '@lark-base-open/base-database-app-sdk/testing';
|
|
2
|
+
export const handlers = createHttpHandlers([
|
|
3
|
+
{ method: 'post', url: 'https://service.example/items', status: 201, body: { id: 'item-1' } },
|
|
4
|
+
]);
|
|
5
|
+
// 交给 MSW setupServer(...handlers) 或 setupWorker(...handlers)。
|
package/examples/page.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { siteRuntime, installLinkGuard, classifyRuntimeError } from '@lark-base-open/base-database-app-sdk/page';
|
|
2
|
+
export function mountPage() {
|
|
3
|
+
const cleanup = installLinkGuard({ onDiagnostic: diagnostic => console.warn(diagnostic) });
|
|
4
|
+
void siteRuntime.ready().catch(error => console.warn(classifyRuntimeError(error)));
|
|
5
|
+
return cleanup;
|
|
6
|
+
}
|