@lark-base-open/base-database-app-sdk 0.0.1-alpha.0 → 0.0.1-alpha.1
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 +13 -1
- package/capabilities.json +77 -38
- package/dist/chunk-5XIFZ4AH.js +1 -0
- package/dist/chunk-ZKBZAK4L.js +1 -0
- package/dist/codegen.cjs +15 -0
- package/dist/codegen.d.cts +43 -0
- package/dist/codegen.d.ts +43 -0
- package/dist/codegen.js +15 -0
- package/dist/components.d.ts +1 -0
- package/dist/components.js +1 -1
- package/dist/page.d.ts +2 -1
- package/dist/page.js +1 -1
- package/dist/server.d.cts +1 -0
- package/dist/server.d.ts +1 -0
- package/dist/shared.cjs +1 -1
- package/dist/shared.d.cts +6 -68
- package/dist/shared.d.ts +6 -68
- package/dist/shared.js +1 -1
- package/dist/testing.d.ts +2 -1
- package/dist/testing.js +1 -1
- package/docs/capabilities.md +3 -20
- package/docs/codegen.md +28 -0
- package/docs/compat.md +1 -1
- package/docs/components.md +1 -1
- package/docs/exports.md +8 -2
- package/docs/page.md +1 -1
- package/docs/testing.md +14 -14
- package/docs/upgrade.md +2 -2
- package/examples/codegen.ts +13 -0
- package/package.json +21 -7
- package/dist/chunk-HEMWSWXI.js +0 -1
- package/dist/chunk-YDAVA3BB.js +0 -1
package/dist/shared.d.cts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { CAPABILITY_CATALOG } from '@lark-base-open/base-site-channel/capabilities';
|
|
2
|
+
export { CAPABILITY_CATALOG } from '@lark-base-open/base-site-channel/capabilities';
|
|
3
|
+
|
|
1
4
|
interface SDKErrorOptions {
|
|
2
5
|
wireCode?: number;
|
|
3
6
|
retryable?: boolean;
|
|
@@ -72,79 +75,14 @@ interface CapabilityContext {
|
|
|
72
75
|
retryable: boolean;
|
|
73
76
|
}>>;
|
|
74
77
|
}
|
|
75
|
-
|
|
76
|
-
readonly "runtime.context": {
|
|
77
|
-
readonly kind: "critical";
|
|
78
|
-
readonly hostBaseline84afb07: true;
|
|
79
|
-
};
|
|
80
|
-
readonly "http.request": {
|
|
81
|
-
readonly kind: "critical";
|
|
82
|
-
readonly hostBaseline84afb07: true;
|
|
83
|
-
};
|
|
84
|
-
readonly "http.binary": {
|
|
85
|
-
readonly kind: "critical";
|
|
86
|
-
readonly hostBaseline84afb07: true;
|
|
87
|
-
};
|
|
88
|
-
readonly "http.cancel": {
|
|
89
|
-
readonly kind: "optional";
|
|
90
|
-
readonly hostBaseline84afb07: true;
|
|
91
|
-
};
|
|
92
|
-
readonly "function.invoke": {
|
|
93
|
-
readonly kind: "critical";
|
|
94
|
-
readonly hostBaseline84afb07: true;
|
|
95
|
-
};
|
|
96
|
-
readonly "runtime.user": {
|
|
97
|
-
readonly kind: "optional";
|
|
98
|
-
readonly hostBaseline84afb07: true;
|
|
99
|
-
};
|
|
100
|
-
readonly "runtime.logout": {
|
|
101
|
-
readonly kind: "optional";
|
|
102
|
-
readonly hostBaseline84afb07: true;
|
|
103
|
-
};
|
|
104
|
-
readonly "runtime.requestEdit": {
|
|
105
|
-
readonly kind: "optional";
|
|
106
|
-
readonly hostBaseline84afb07: true;
|
|
107
|
-
};
|
|
108
|
-
readonly "attachment.upload": {
|
|
109
|
-
readonly kind: "critical";
|
|
110
|
-
readonly hostBaseline84afb07: true;
|
|
111
|
-
};
|
|
112
|
-
readonly "attachment.cancel": {
|
|
113
|
-
readonly kind: "optional";
|
|
114
|
-
readonly hostBaseline84afb07: true;
|
|
115
|
-
};
|
|
116
|
-
readonly "chatbot.stream": {
|
|
117
|
-
readonly kind: "critical";
|
|
118
|
-
readonly hostBaseline84afb07: true;
|
|
119
|
-
};
|
|
120
|
-
readonly "ai.function.stream": {
|
|
121
|
-
readonly kind: "critical";
|
|
122
|
-
readonly hostBaseline84afb07: true;
|
|
123
|
-
};
|
|
124
|
-
readonly "runtime.lifecycle": {
|
|
125
|
-
readonly kind: "optional";
|
|
126
|
-
readonly hostBaseline84afb07: true;
|
|
127
|
-
};
|
|
128
|
-
readonly "runtime.mode": {
|
|
129
|
-
readonly kind: "optional";
|
|
130
|
-
readonly hostBaseline84afb07: true;
|
|
131
|
-
};
|
|
132
|
-
readonly "attachment.progress": {
|
|
133
|
-
readonly kind: "optional";
|
|
134
|
-
readonly hostBaseline84afb07: true;
|
|
135
|
-
};
|
|
136
|
-
readonly "link.open": {
|
|
137
|
-
readonly kind: "optional";
|
|
138
|
-
readonly hostBaseline84afb07: true;
|
|
139
|
-
};
|
|
140
|
-
};
|
|
78
|
+
|
|
141
79
|
type CapabilityName = keyof typeof CAPABILITY_CATALOG;
|
|
142
80
|
interface CapabilityQueryResult {
|
|
143
81
|
name: string;
|
|
144
82
|
requiredVersion: number;
|
|
145
83
|
version: number | null;
|
|
146
84
|
supported: boolean;
|
|
147
|
-
source: "advertised" | "host-baseline
|
|
85
|
+
source: "advertised" | "host-baseline";
|
|
148
86
|
availability: "available" | "unknown" | CapabilityUnavailableReason | "unsupported";
|
|
149
87
|
}
|
|
150
88
|
declare const DiagnosticCode: {
|
|
@@ -174,4 +112,4 @@ declare class CapabilityUnavailableError extends SDKError {
|
|
|
174
112
|
/** 不解析 message 猜测原因;可恢复不代表写入可安全重试。 */
|
|
175
113
|
declare function classifyRuntimeError(error: unknown): RuntimeDiagnostic;
|
|
176
114
|
|
|
177
|
-
export {
|
|
115
|
+
export { CapabilityContext, CapabilityDescriptor, CapabilityMap, CapabilityName, CapabilityQueryResult, CapabilityUnavailableError, CapabilityUnavailableReason, DiagnosticCategory, DiagnosticCode, FunctionEnvelope, JsonValue, RuntimeCapabilities, RuntimeDiagnostic, SDKError, SDKErrorOptions, WriteResult, classifyRuntimeError };
|
package/dist/shared.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { CAPABILITY_CATALOG } from '@lark-base-open/base-site-channel/capabilities';
|
|
2
|
+
export { CAPABILITY_CATALOG } from '@lark-base-open/base-site-channel/capabilities';
|
|
3
|
+
|
|
1
4
|
interface SDKErrorOptions {
|
|
2
5
|
wireCode?: number;
|
|
3
6
|
retryable?: boolean;
|
|
@@ -72,79 +75,14 @@ interface CapabilityContext {
|
|
|
72
75
|
retryable: boolean;
|
|
73
76
|
}>>;
|
|
74
77
|
}
|
|
75
|
-
|
|
76
|
-
readonly "runtime.context": {
|
|
77
|
-
readonly kind: "critical";
|
|
78
|
-
readonly hostBaseline84afb07: true;
|
|
79
|
-
};
|
|
80
|
-
readonly "http.request": {
|
|
81
|
-
readonly kind: "critical";
|
|
82
|
-
readonly hostBaseline84afb07: true;
|
|
83
|
-
};
|
|
84
|
-
readonly "http.binary": {
|
|
85
|
-
readonly kind: "critical";
|
|
86
|
-
readonly hostBaseline84afb07: true;
|
|
87
|
-
};
|
|
88
|
-
readonly "http.cancel": {
|
|
89
|
-
readonly kind: "optional";
|
|
90
|
-
readonly hostBaseline84afb07: true;
|
|
91
|
-
};
|
|
92
|
-
readonly "function.invoke": {
|
|
93
|
-
readonly kind: "critical";
|
|
94
|
-
readonly hostBaseline84afb07: true;
|
|
95
|
-
};
|
|
96
|
-
readonly "runtime.user": {
|
|
97
|
-
readonly kind: "optional";
|
|
98
|
-
readonly hostBaseline84afb07: true;
|
|
99
|
-
};
|
|
100
|
-
readonly "runtime.logout": {
|
|
101
|
-
readonly kind: "optional";
|
|
102
|
-
readonly hostBaseline84afb07: true;
|
|
103
|
-
};
|
|
104
|
-
readonly "runtime.requestEdit": {
|
|
105
|
-
readonly kind: "optional";
|
|
106
|
-
readonly hostBaseline84afb07: true;
|
|
107
|
-
};
|
|
108
|
-
readonly "attachment.upload": {
|
|
109
|
-
readonly kind: "critical";
|
|
110
|
-
readonly hostBaseline84afb07: true;
|
|
111
|
-
};
|
|
112
|
-
readonly "attachment.cancel": {
|
|
113
|
-
readonly kind: "optional";
|
|
114
|
-
readonly hostBaseline84afb07: true;
|
|
115
|
-
};
|
|
116
|
-
readonly "chatbot.stream": {
|
|
117
|
-
readonly kind: "critical";
|
|
118
|
-
readonly hostBaseline84afb07: true;
|
|
119
|
-
};
|
|
120
|
-
readonly "ai.function.stream": {
|
|
121
|
-
readonly kind: "critical";
|
|
122
|
-
readonly hostBaseline84afb07: true;
|
|
123
|
-
};
|
|
124
|
-
readonly "runtime.lifecycle": {
|
|
125
|
-
readonly kind: "optional";
|
|
126
|
-
readonly hostBaseline84afb07: true;
|
|
127
|
-
};
|
|
128
|
-
readonly "runtime.mode": {
|
|
129
|
-
readonly kind: "optional";
|
|
130
|
-
readonly hostBaseline84afb07: true;
|
|
131
|
-
};
|
|
132
|
-
readonly "attachment.progress": {
|
|
133
|
-
readonly kind: "optional";
|
|
134
|
-
readonly hostBaseline84afb07: true;
|
|
135
|
-
};
|
|
136
|
-
readonly "link.open": {
|
|
137
|
-
readonly kind: "optional";
|
|
138
|
-
readonly hostBaseline84afb07: true;
|
|
139
|
-
};
|
|
140
|
-
};
|
|
78
|
+
|
|
141
79
|
type CapabilityName = keyof typeof CAPABILITY_CATALOG;
|
|
142
80
|
interface CapabilityQueryResult {
|
|
143
81
|
name: string;
|
|
144
82
|
requiredVersion: number;
|
|
145
83
|
version: number | null;
|
|
146
84
|
supported: boolean;
|
|
147
|
-
source: "advertised" | "host-baseline
|
|
85
|
+
source: "advertised" | "host-baseline";
|
|
148
86
|
availability: "available" | "unknown" | CapabilityUnavailableReason | "unsupported";
|
|
149
87
|
}
|
|
150
88
|
declare const DiagnosticCode: {
|
|
@@ -174,4 +112,4 @@ declare class CapabilityUnavailableError extends SDKError {
|
|
|
174
112
|
/** 不解析 message 猜测原因;可恢复不代表写入可安全重试。 */
|
|
175
113
|
declare function classifyRuntimeError(error: unknown): RuntimeDiagnostic;
|
|
176
114
|
|
|
177
|
-
export {
|
|
115
|
+
export { CapabilityContext, CapabilityDescriptor, CapabilityMap, CapabilityName, CapabilityQueryResult, CapabilityUnavailableError, CapabilityUnavailableReason, DiagnosticCategory, DiagnosticCode, FunctionEnvelope, JsonValue, RuntimeCapabilities, RuntimeDiagnostic, SDKError, SDKErrorOptions, WriteResult, classifyRuntimeError };
|
package/dist/shared.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as b,b as c,c as d,d as e}from"./chunk-
|
|
1
|
+
import{a as b,b as c,c as d,d as e}from"./chunk-ZKBZAK4L.js";import{a}from"./chunk-3JX3FIBG.js";export{b as CAPABILITY_CATALOG,d as CapabilityUnavailableError,c as DiagnosticCode,a as SDKError,e as classifyRuntimeError};
|
package/dist/testing.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { RuntimeChannel } from '@lark-base-open/base-site-channel/runtime';
|
|
|
3
3
|
import { S as SiteRuntimeContext } from './capabilities-56e403e7.js';
|
|
4
4
|
import { RuntimeCapabilities } from './shared.js';
|
|
5
5
|
import { HttpHandler } from 'msw';
|
|
6
|
+
import '@lark-base-open/base-site-channel/capabilities';
|
|
6
7
|
|
|
7
8
|
interface SiteHttpBridgeRoute {
|
|
8
9
|
method: string;
|
|
@@ -38,7 +39,7 @@ interface MockHandlerContext {
|
|
|
38
39
|
}
|
|
39
40
|
interface MockHostOptions {
|
|
40
41
|
context?: Partial<SiteRuntimeContext>;
|
|
41
|
-
/** undefined
|
|
42
|
+
/** undefined 模拟未提供能力字段的宿主;{} 模拟明确无能力的新宿主。 */
|
|
42
43
|
capabilities?: RuntimeCapabilities;
|
|
43
44
|
bindings?: readonly {
|
|
44
45
|
baseToken: string;
|
package/dist/testing.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Host as me,ChannelCallbackError as P}from"@lark-base-open/base-site-channel";import{SiteRuntimeApi as s,SiteRuntimeEvent as C,SiteRuntimeFunction as D,isValidSiteFunctionName as he}from"@lark-base-open/base-site-channel/runtime-contract";import{ChannelCallbackError as K}from"@lark-base-open/base-site-channel";import{SiteRuntimeErrorCode as b}from"@lark-base-open/base-site-channel/runtime-contract";var X=3e4,Z=6e4,Y=4;var J=32,Q=127,ee=new Set(["DELETE","GET","PATCH","POST","PUT"]),te=new Set(["authorization","connection","content-length","cookie","host","origin","proxy-authorization","referer","transfer-encoding"]);var ne=/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/,E=(e,n)=>{throw new K(e,n)},oe=e=>{let n=e;for(let r=0;r<Y;r+=1){let a;try{a=decodeURIComponent(n)}catch{return E(b.InvalidHttpUrl,"\u8BF7\u6C42\u8DEF\u5F84\u7F16\u7801\u65E0\u6548")}if(a===n)return n;n=a}return n},B=(e,n)=>{if(typeof e!="string"||!e.startsWith("/")||e.startsWith("//"))return E(b.InvalidHttpUrl,"\u8BF7\u6C42\u8DEF\u5F84\u5FC5\u987B\u4EE5\u5355\u4E2A / \u5F00\u5934");if(e.includes("\\")||e.includes("#"))return E(b.InvalidHttpUrl,"\u8BF7\u6C42\u8DEF\u5F84\u5305\u542B\u975E\u6CD5\u5B57\u7B26");let r=oe(e),a=r.split("?")[0]??"";if(r.startsWith("//")||r.includes("\\")||Array.from(r).some(f=>{let m=f.charCodeAt(0);return m<J||m===Q})||a.split("/").some(f=>f===".."))return E(b.InvalidHttpUrl,"\u8BF7\u6C42\u8DEF\u5F84\u4E0D\u5141\u8BB8\u8DE8\u76EE\u5F55\u6216\u8DE8\u57DF");let c=new URL(e,n);return c.origin!==n?E(b.InvalidHttpUrl,"\u8BF7\u6C42\u8DEF\u5F84\u4E0D\u5141\u8BB8\u8DE8\u57DF"):`${c.pathname}${c.search}`},ie=e=>{if(e===void 0)return{};if(e===null||typeof e!="object"||Array.isArray(e))return E(b.InvalidHttpHeader,"\u8BF7\u6C42\u5934\u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u952E\u503C\u5BF9\u8C61");let n={};for(let[r,a]of Object.entries(e)){let c=r.toLowerCase();if(typeof a!="string"||!ne.test(r)||te.has(c)||c.startsWith("sec-")||c.startsWith("x-forwarded-")||c.startsWith("rpc-"))return E(b.InvalidHttpHeader,`\u8BF7\u6C42\u5934 ${r} \u4E0D\u5141\u8BB8\u900F\u4F20`);n[r]=a}return n},F=e=>{let n=e?.method?.toUpperCase()??"GET";if(!ee.has(n))return E(b.InvalidHttpUrl,"\u8BF7\u6C42\u65B9\u6CD5\u4E0D\u53D7\u652F\u6301");if(e?.body!==void 0&&typeof e.body!="string")return E(b.UnsupportedHttpBody,"\u8BF7\u6C42\u4F53\u4EC5\u652F\u6301\u5B57\u7B26\u4E32");if((n==="GET"||n==="HEAD")&&e?.body!==void 0)return E(b.UnsupportedHttpBody,"GET \u8BF7\u6C42\u4E0D\u652F\u6301\u8BF7\u6C42\u4F53");let r=e?.timeoutMs??X;if(typeof r!="number"||!Number.isFinite(r)||r<=0)return E(b.InvalidHttpUrl,"\u8BF7\u6C42\u8D85\u65F6\u65F6\u95F4\u65E0\u6548");let a=Math.min(Math.max(r,1),Z);return{body:e?.body,credentials:"same-origin",headers:ie(e?.headers),method:n,timeoutMs:a}};var re=/^:[A-Za-z][A-Za-z0-9_]*$/;var v={routes:[{method:"GET",path:"/space/api/box/stream/download/v2/cover/:fileToken"},{method:"GET",path:"/space/api/bitable/site/v1/:siteToken/invoke_get"},{method:"POST",path:"/space/api/bitable/site/v1/:siteToken/invoke_function/:functionId"},{method:"POST",path:"/space/api/bitable/site/v1/:siteToken/ai_functions/:functionKey/tasks"},{method:"GET",path:"/space/api/bitable/site/v1/:siteToken/ai_functions/:functionKey/tasks/:taskId"},{method:"POST",path:"/space/api/bitable/site/v1/:siteToken/bases/:baseToken/tables/:tableId/records/batch_create"},{method:"POST",path:"/space/api/bitable/site/v1/:siteToken/bases/:baseToken/tables/:tableId/records/batch_update"},{method:"POST",path:"/space/api/bitable/site/v1/:siteToken/bases/:baseToken/tables/:tableId/records/batch_delete"},{method:"POST",path:"/space/api/box/file/temporary_download_url"},{method:"POST",path:"/space/api/mention/recommend.v2"},{method:"GET",path:"/space/api/user"},{method:"POST",path:"/space/api/bitable/chatbot/chat"},{method:"GET",path:"/space/api/bitable/chatbot/poll"},{method:"POST",path:"/space/api/bitable/chatbot/:chatbotId/history/list"},{method:"POST",path:"/space/api/bitable/chatbot/chatbot_session/reset"},{method:"GET",path:"/space/api/bitable/chatbot/:chatbotId/run_log/session"},{method:"POST",path:"/space/api/bitable/chatbot/:chatbotId/run_log/stop"}]},q=e=>e.length>1&&e.endsWith("/")?e.slice(0,-1):e;var se=(e,n)=>{let r=q(e).split("/"),a=q(n).split("/");return r.length===a.length&&a.every((c,f)=>{let m=r[f];return re.test(c)?!!m:c===m})},z=(e,n,r)=>{let a=e.split("?")[0]??"";return r.some(c=>c.method===n&&se(a,c.path))};import{ChannelCallbackError as j}from"@lark-base-open/base-site-channel";import{SiteRuntimeErrorCode as ae}from"@lark-base-open/base-site-channel/runtime-contract";var G="/space/api/box/stream/download/v2/cover/",ce="/space/api/box/file/temporary_download_url",le=new Set(["120","360","720","1280"]),de=/^[A-Za-z0-9_-]{1,256}$/,w=e=>typeof e=="string"&&de.test(e),y=()=>{throw new j(ae.InvalidAttachmentTarget,"\u9644\u4EF6\u76EE\u6807\u4E0D\u5C5E\u4E8E\u5F53\u524D Site \u6216\u9644\u4EF6\u5750\u6807\u65E0\u6548")},$=(e,n,r)=>{let a=e.pathname.startsWith(G),c=e.pathname.replace(/\/+$/,"")===ce;if(!a&&!c)return()=>{};try{let f=c&&typeof r?.body=="string"?JSON.parse(r.body):void 0,m=a?e.pathname.slice(G.length):f?.file_token;if(a&&(e.searchParams.getAll("extra").length!==1||!["width","height"].every(o=>e.searchParams.getAll(o).length===1&&le.has(e.searchParams.get(o)??""))))return y();let R=JSON.parse(a?e.searchParams.get("extra")??"null":f?.extra??"null"),p=R?.bitablePerm;if(!n||!w(m)||!w(p?.tableId))return y();let _=Object.entries(p.attachments??{});if(_.length!==1||!w(_[0][0]))return y();let A=Object.entries(_[0][1]);if(A.length!==1||!w(A[0][0]))return y();let I=A[0][1];if(!Array.isArray(I)||I.length!==1||I[0]!==m)return y();let k=n.getSiteToken(),H=n.getSiteEnv?.()??"prod";if(!k||p.siteToken!==void 0&&p.baseAppToken!==void 0||(p.siteToken??p.baseAppToken)!==k||p.siteEnv!==void 0&&p.siteEnv!==H||c&&(r?.method!=="POST"||e.searchParams.has("extra"))||Object.keys(R).some(o=>o!=="bitablePerm"))return y();let O=JSON.stringify({bitablePerm:{tableId:p.tableId,attachments:p.attachments,siteToken:k,siteEnv:H}});a?e.searchParams.set("extra",O):r&&(r.body=JSON.stringify({file_token:m,extra:O}));let t=()=>{(k!==n.getSiteToken()||H!==(n.getSiteEnv?.()??"prod")||!n.isBoundTable(p.tableId))&&y()};return t(),t}catch(f){if(f instanceof j)throw f;return y()}};import{http as pe,HttpResponse as ue}from"msw";function fe(e){return e.map(n=>pe[n.method??"get"](n.url,()=>ue.json(n.body??null,{status:n.status??200,headers:n.headers})))}var u=(e,n=e)=>{throw new P(e,n)},Te=new Set(["cache-control","content-language","content-type","etag","last-modified","x-request-id","x-tt-logid","x-tt-trace-id"]);function Oe(e={}){let n={protocolVersion:"site.v1",colorScheme:"light",locale:"zh-CN",mode:"reading",env:"preview",siteToken:"mock-site",...e.context,...e.capabilities===void 0?{}:{capabilities:e.capabilities}},r=e.origin??"https://host.example",a=[],c=[],f=new Map,m=new Map,R=new Set,p=new Set,_=!1,A=0,I=async(t,o)=>{_||(c.push({event:t,payload:o}),await Promise.all([...[...f.get(t)??[]].map(l=>l(o)),...[...p].map(l=>l.emitEvent(t,o))]))},k=(t,o)=>(e.bindings??[]).some(l=>l.baseToken===t&&l.tableIds.includes(o)),H=async(t,o)=>{if(_&&u("CHANNEL_DESTROYED"),Object.values(s).includes(t)||u("10004"),a.push({api:t,params:o}),t!==s.RuntimeContext&&n.capabilities!==void 0){let h={[s.HttpRequestCancel]:"http.cancel",[s.AttachmentUploadCancel]:"attachment.cancel",[s.StreamOpen]:"chatbot.stream",[s.StreamClose]:"chatbot.stream",[s.AIFunctionStreamOpen]:"ai.function.stream",[s.AIFunctionStreamClose]:"ai.function.stream"}[t]??t.replace("site.v1.",""),d=Object.prototype.hasOwnProperty.call(n.capabilities,h)?n.capabilities[h]?.version:void 0;(!Number.isInteger(d)||(d??0)<1)&&u("10004")}let l,T=()=>{},g,S;if(t===s.RuntimeContext)l=structuredClone(n);else if(t===s.OpenLink){let i;try{if(typeof o?.url!="string"||!o.url.trim())throw 0;i=new URL(o.url)}catch{return u("INVALID_LINK_URL")}["http:","https:","mailto:","tel:"].includes(i.protocol)||u("INVALID_LINK_URL"),["http:","https:"].includes(i.protocol)&&e.secureLinkAvailable===!1&&u("LINK_CHECK_UNAVAILABLE"),l={opened:!0}}else if(t===s.HttpRequest){(!o||typeof o!="object")&&u("INVALID_HTTP_URL");let i=o,h=B(i.url,r);S=F(i.init),z(h,S.method??"GET",e.allowedRoutes??v.routes)||u("INVALID_HTTP_URL"),["text","arrayBuffer"].includes(i.init?.responseType??"text")||u("HTTP_RESPONSE_TYPE_UNSUPPORTED");let d=i.requestId??`legacy-${++A}`;(typeof d!="string"||!d||d.length>256||m.has(`http:${d}`))&&u("HTTP_REQUEST_FAILED"),g=`http:${d}`,T=$(new URL(h,r),{getSiteToken:()=>n.siteToken,getSiteEnv:()=>n.env??"prod",isBoundTable:U=>(e.bindings??[]).some(V=>V.tableIds.includes(U))},S),l={body:i.init?.responseType==="arrayBuffer"?new ArrayBuffer(0):"{}",...i.init?.responseType==="arrayBuffer"?{bodyType:"arrayBuffer"}:{},headers:{},status:200,statusText:"OK"}}else if(t===s.AttachmentUpload){let i=o,h=typeof i?.baseToken=="string"?i.baseToken.trim():"",d=typeof i?.tableId=="string"?i.tableId.trim():"";(!h||!d||typeof File>"u"||!(i.file instanceof File)||i.file.size<=0||i.file.size>2*1024*1024*1024||!k(h,d))&&u("INVALID_ATTACHMENT_TARGET"),n.siteToken?.trim()||u("ATTACHMENT_UPLOAD_FAILED"),g=`upload:${i.uploadId??++A}`,l={fileToken:"mock-file",name:i.file.name,size:i.file.size,mimeType:i.file.type}}else if(t===s.HttpRequestCancel||t===s.AttachmentUploadCancel){let i=t===s.HttpRequestCancel?`http:${o?.requestId}`:`upload:${o?.uploadId}`,h=m.get(i);h?.controller.abort(),h?.reject(new P(t===s.HttpRequestCancel?"HTTP_REQUEST_CANCELED":"ATTACHMENT_UPLOAD_CANCELED","\u6D4B\u8BD5\u8C03\u7528\u5DF2\u53D6\u6D88"))}else if(t===s.FunctionInvoke){let i=o?.functionName;(typeof i!="string"||!he(i))&&u("INVALID_FUNCTION_NAME"),i===D.GetCurrentUser?l={name:"\u6D4B\u8BD5\u7528\u6237",avatarUrl:""}:[D.Logout,D.RequestEdit].includes(i)||(!e.functions||!Object.prototype.hasOwnProperty.call(e.functions,i))&&u("FUNCTION_NOT_ALLOWED")}else t===s.AIFunctionStreamOpen||t===s.StreamOpen?(o?.subscriptionId||u("STREAM_UNAVAILABLE"),t===s.AIFunctionStreamOpen&&(!n.siteToken||!o.functionKey)&&u("STREAM_UNAVAILABLE")):(t===s.AIFunctionStreamClose||t===s.StreamClose)&&R.delete(o?.subscriptionId);let L=e.failures?.[t];L&&u(L.code,L.message);let x=new AbortController,M,N=()=>{},W=new Promise((i,h)=>{N=h});g??=`call:${++A}`,m.set(g,{controller:x,reject:N}),S&&(M=setTimeout(()=>{x.abort(),N(new P("REQUEST_TIMEOUT","HTTP \u8BF7\u6C42\u8D85\u65F6"))},S.timeoutMs));try{T();let i=e.handlers?.[t],h=t===s.FunctionInvoke?e.functions?.[o.functionName]:void 0,d=await Promise.race([Promise.resolve().then(()=>i?i(o,{signal:x.signal,emit:I}):h?h(o.params):l),W]);return T(),(t===s.AIFunctionStreamOpen||t===s.StreamOpen)&&R.add(o.subscriptionId),t===s.HttpRequest&&d&&typeof d=="object"&&"headers"in d?{...d,headers:Object.fromEntries(Object.entries(d.headers).filter(([U])=>Te.has(U.toLowerCase())))}:d}finally{clearTimeout(M),g&&m.delete(g)}};return{channel:{invokeApi:H,emitEvent:async(t,o)=>{c.push({event:t,payload:o})},subscribeEvent:(t,o)=>{let l=f.get(t)??new Set;return l.add(o),f.set(t,l),()=>{l.delete(o)}}},context:n,calls:a,events:c,emit:I,mount(t,o,l=new URL(o).origin){let T=new me(t,()=>{},`mock-${++A}`,[l],{sandbox:"allow-scripts allow-same-origin allow-downloads"},{enableBinaryFetch:!1});for(let g of Object.values(s))T.registerApi(g,S=>H(g,S));for(let g of[C.LifecycleReady,C.LifecycleLoadFailed])T.subscribeEvent(g,S=>{c.push({event:g,payload:S})});return p.add(T),T.setClientUrl(o),()=>{T.destroy(),p.delete(T)}},emitStreamMessage(t,o,l,T=!1){return R.has(t)?I(T?C.AIFunctionStreamMessage:C.StreamMessage,{subscriptionId:t,data:o,seq:l}):Promise.resolve()},dispose(){_=!0;for(let t of m.values())t.controller.abort(),t.reject(new P("CHANNEL_DESTROYED","\u6D4B\u8BD5\u5BBF\u4E3B\u5DF2\u9500\u6BC1"));m.clear(),f.clear(),R.clear();for(let t of p)t.destroy();p.clear()}}}export{v as DEFAULT_SITE_HTTP_BRIDGE_ALLOWLIST,fe as createHttpHandlers,Oe as createMockHost};
|
|
1
|
+
import{Host as me,ChannelCallbackError as P}from"@lark-base-open/base-site-channel";import{SiteRuntimeApi as s,SiteRuntimeEvent as C,SiteRuntimeFunction as D,isValidSiteFunctionName as he}from"@lark-base-open/base-site-channel/runtime-contract";import{ChannelCallbackError as K}from"@lark-base-open/base-site-channel";import{SiteRuntimeErrorCode as g}from"@lark-base-open/base-site-channel/runtime-contract";var X=3e4,Z=6e4,Y=4;var J=32,Q=127,ee=new Set(["DELETE","GET","PATCH","POST","PUT"]),te=new Set(["authorization","connection","content-length","cookie","host","origin","proxy-authorization","referer","transfer-encoding"]);var ne=/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/,E=(e,n)=>{throw new K(e,n)},oe=e=>{let n=e;for(let r=0;r<Y;r+=1){let a;try{a=decodeURIComponent(n)}catch{return E(g.InvalidHttpUrl,"\u8BF7\u6C42\u8DEF\u5F84\u7F16\u7801\u65E0\u6548")}if(a===n)return n;n=a}return n},B=(e,n)=>{if(typeof e!="string"||!e.startsWith("/")||e.startsWith("//"))return E(g.InvalidHttpUrl,"\u8BF7\u6C42\u8DEF\u5F84\u5FC5\u987B\u4EE5\u5355\u4E2A / \u5F00\u5934");if(e.includes("\\")||e.includes("#"))return E(g.InvalidHttpUrl,"\u8BF7\u6C42\u8DEF\u5F84\u5305\u542B\u975E\u6CD5\u5B57\u7B26");let r=oe(e),a=r.split("?")[0]??"";if(r.startsWith("//")||r.includes("\\")||Array.from(r).some(f=>{let m=f.charCodeAt(0);return m<J||m===Q})||a.split("/").some(f=>f===".."))return E(g.InvalidHttpUrl,"\u8BF7\u6C42\u8DEF\u5F84\u4E0D\u5141\u8BB8\u8DE8\u76EE\u5F55\u6216\u8DE8\u57DF");let c=new URL(e,n);return c.origin!==n?E(g.InvalidHttpUrl,"\u8BF7\u6C42\u8DEF\u5F84\u4E0D\u5141\u8BB8\u8DE8\u57DF"):`${c.pathname}${c.search}`},ie=e=>{if(e===void 0)return{};if(e===null||typeof e!="object"||Array.isArray(e))return E(g.InvalidHttpHeader,"\u8BF7\u6C42\u5934\u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u952E\u503C\u5BF9\u8C61");let n={};for(let[r,a]of Object.entries(e)){let c=r.toLowerCase();if(typeof a!="string"||!ne.test(r)||te.has(c)||c.startsWith("sec-")||c.startsWith("x-forwarded-")||c.startsWith("rpc-"))return E(g.InvalidHttpHeader,`\u8BF7\u6C42\u5934 ${r} \u4E0D\u5141\u8BB8\u900F\u4F20`);n[r]=a}return n},F=e=>{let n=e?.method?.toUpperCase()??"GET";if(!ee.has(n))return E(g.InvalidHttpUrl,"\u8BF7\u6C42\u65B9\u6CD5\u4E0D\u53D7\u652F\u6301");if(e?.body!==void 0&&typeof e.body!="string")return E(g.UnsupportedHttpBody,"\u8BF7\u6C42\u4F53\u4EC5\u652F\u6301\u5B57\u7B26\u4E32");if((n==="GET"||n==="HEAD")&&e?.body!==void 0)return E(g.UnsupportedHttpBody,"GET \u8BF7\u6C42\u4E0D\u652F\u6301\u8BF7\u6C42\u4F53");let r=e?.timeoutMs??X;if(typeof r!="number"||!Number.isFinite(r)||r<=0)return E(g.InvalidHttpUrl,"\u8BF7\u6C42\u8D85\u65F6\u65F6\u95F4\u65E0\u6548");let a=Math.min(Math.max(r,1),Z);return{body:e?.body,credentials:"same-origin",headers:ie(e?.headers),method:n,timeoutMs:a}};var re=/^:[A-Za-z][A-Za-z0-9_]*$/;var v={routes:[{method:"GET",path:"/space/api/box/stream/download/v2/cover/:fileToken"},{method:"GET",path:"/space/api/bitable/site/v1/:siteToken/invoke_get"},{method:"POST",path:"/space/api/bitable/site/v1/:siteToken/functions/:key/invoke"},{method:"GET",path:"/space/api/bitable/site/v1/:siteToken/functions/:key/tasks/:taskId"},{method:"POST",path:"/space/api/bitable/site/v1/:siteToken/invoke_function/:functionId"},{method:"POST",path:"/space/api/bitable/site/v1/:siteToken/ai_functions/:functionKey/tasks"},{method:"GET",path:"/space/api/bitable/site/v1/:siteToken/ai_functions/:functionKey/tasks/:taskId"},{method:"POST",path:"/space/api/bitable/site/v1/:siteToken/bases/:baseToken/tables/:tableId/records/batch_create"},{method:"POST",path:"/space/api/bitable/site/v1/:siteToken/bases/:baseToken/tables/:tableId/records/batch_update"},{method:"POST",path:"/space/api/bitable/site/v1/:siteToken/bases/:baseToken/tables/:tableId/records/batch_delete"},{method:"POST",path:"/space/api/box/file/temporary_download_url"},{method:"POST",path:"/space/api/mention/recommend.v2"},{method:"GET",path:"/space/api/user"},{method:"POST",path:"/space/api/bitable/chatbot/chat"},{method:"GET",path:"/space/api/bitable/chatbot/poll"},{method:"POST",path:"/space/api/bitable/chatbot/:chatbotId/history/list"},{method:"POST",path:"/space/api/bitable/chatbot/chatbot_session/reset"},{method:"GET",path:"/space/api/bitable/chatbot/:chatbotId/run_log/session"},{method:"POST",path:"/space/api/bitable/chatbot/:chatbotId/run_log/stop"}]},q=e=>e.length>1&&e.endsWith("/")?e.slice(0,-1):e;var se=(e,n)=>{let r=q(e).split("/"),a=q(n).split("/");return r.length===a.length&&a.every((c,f)=>{let m=r[f];return re.test(c)?!!m:c===m})},z=(e,n,r)=>{let a=e.split("?")[0]??"";return r.some(c=>c.method===n&&se(a,c.path))};import{ChannelCallbackError as j}from"@lark-base-open/base-site-channel";import{SiteRuntimeErrorCode as ae}from"@lark-base-open/base-site-channel/runtime-contract";var G="/space/api/box/stream/download/v2/cover/",ce="/space/api/box/file/temporary_download_url",le=new Set(["120","360","720","1280"]),de=/^[A-Za-z0-9_-]{1,256}$/,w=e=>typeof e=="string"&&de.test(e),y=()=>{throw new j(ae.InvalidAttachmentTarget,"\u9644\u4EF6\u76EE\u6807\u4E0D\u5C5E\u4E8E\u5F53\u524D Site \u6216\u9644\u4EF6\u5750\u6807\u65E0\u6548")},$=(e,n,r)=>{let a=e.pathname.startsWith(G),c=e.pathname.replace(/\/+$/,"")===ce;if(!a&&!c)return()=>{};try{let f=c&&typeof r?.body=="string"?JSON.parse(r.body):void 0,m=a?e.pathname.slice(G.length):f?.file_token;if(a&&(e.searchParams.getAll("extra").length!==1||!["width","height"].every(o=>e.searchParams.getAll(o).length===1&&le.has(e.searchParams.get(o)??""))))return y();let R=JSON.parse(a?e.searchParams.get("extra")??"null":f?.extra??"null"),p=R?.bitablePerm;if(!n||!w(m)||!w(p?.tableId))return y();let _=Object.entries(p.attachments??{});if(_.length!==1||!w(_[0][0]))return y();let A=Object.entries(_[0][1]);if(A.length!==1||!w(A[0][0]))return y();let k=A[0][1];if(!Array.isArray(k)||k.length!==1||k[0]!==m)return y();let I=n.getSiteToken(),H=n.getSiteEnv?.()??"prod";if(!I||p.siteToken!==void 0&&p.baseAppToken!==void 0||(p.siteToken??p.baseAppToken)!==I||p.siteEnv!==void 0&&p.siteEnv!==H||c&&(r?.method!=="POST"||e.searchParams.has("extra"))||Object.keys(R).some(o=>o!=="bitablePerm"))return y();let O=JSON.stringify({bitablePerm:{tableId:p.tableId,attachments:p.attachments,siteToken:I,siteEnv:H}});a?e.searchParams.set("extra",O):r&&(r.body=JSON.stringify({file_token:m,extra:O}));let t=()=>{(I!==n.getSiteToken()||H!==(n.getSiteEnv?.()??"prod")||!n.isBoundTable(p.tableId))&&y()};return t(),t}catch(f){if(f instanceof j)throw f;return y()}};import{http as pe,HttpResponse as ue}from"msw";function fe(e){return e.map(n=>pe[n.method??"get"](n.url,()=>ue.json(n.body??null,{status:n.status??200,headers:n.headers})))}var u=(e,n=e)=>{throw new P(e,n)},Te=new Set(["cache-control","content-language","content-type","etag","last-modified","x-request-id","x-tt-logid","x-tt-trace-id"]);function Oe(e={}){let n={protocolVersion:"site.v1",colorScheme:"light",locale:"zh-CN",mode:"reading",env:"preview",siteToken:"mock-site",...e.context,...e.capabilities===void 0?{}:{capabilities:e.capabilities}},r=e.origin??"https://host.example",a=[],c=[],f=new Map,m=new Map,R=new Set,p=new Set,_=!1,A=0,k=async(t,o)=>{_||(c.push({event:t,payload:o}),await Promise.all([...[...f.get(t)??[]].map(l=>l(o)),...[...p].map(l=>l.emitEvent(t,o))]))},I=(t,o)=>(e.bindings??[]).some(l=>l.baseToken===t&&l.tableIds.includes(o)),H=async(t,o)=>{if(_&&u("CHANNEL_DESTROYED"),Object.values(s).includes(t)||u("10004"),a.push({api:t,params:o}),t!==s.RuntimeContext&&n.capabilities!==void 0){let h={[s.HttpRequestCancel]:"http.cancel",[s.AttachmentUploadCancel]:"attachment.cancel",[s.StreamOpen]:"chatbot.stream",[s.StreamClose]:"chatbot.stream",[s.AIFunctionStreamOpen]:"ai.function.stream",[s.AIFunctionStreamClose]:"ai.function.stream"}[t]??t.replace("site.v1.",""),d=Object.prototype.hasOwnProperty.call(n.capabilities,h)?n.capabilities[h]?.version:void 0;(!Number.isInteger(d)||(d??0)<1)&&u("10004")}let l,T=()=>{},b,S;if(t===s.RuntimeContext)l=structuredClone(n);else if(t===s.OpenLink){let i;try{if(typeof o?.url!="string"||!o.url.trim())throw 0;i=new URL(o.url)}catch{return u("INVALID_LINK_URL")}["http:","https:","mailto:","tel:"].includes(i.protocol)||u("INVALID_LINK_URL"),["http:","https:"].includes(i.protocol)&&e.secureLinkAvailable===!1&&u("LINK_CHECK_UNAVAILABLE"),l={opened:!0}}else if(t===s.HttpRequest){(!o||typeof o!="object")&&u("INVALID_HTTP_URL");let i=o,h=B(i.url,r);S=F(i.init),z(h,S.method??"GET",e.allowedRoutes??v.routes)||u("INVALID_HTTP_URL"),["text","arrayBuffer"].includes(i.init?.responseType??"text")||u("HTTP_RESPONSE_TYPE_UNSUPPORTED");let d=i.requestId??`legacy-${++A}`;(typeof d!="string"||!d||d.length>256||m.has(`http:${d}`))&&u("HTTP_REQUEST_FAILED"),b=`http:${d}`,T=$(new URL(h,r),{getSiteToken:()=>n.siteToken,getSiteEnv:()=>n.env??"prod",isBoundTable:U=>(e.bindings??[]).some(V=>V.tableIds.includes(U))},S),l={body:i.init?.responseType==="arrayBuffer"?new ArrayBuffer(0):"{}",...i.init?.responseType==="arrayBuffer"?{bodyType:"arrayBuffer"}:{},headers:{},status:200,statusText:"OK"}}else if(t===s.AttachmentUpload){let i=o,h=typeof i?.baseToken=="string"?i.baseToken.trim():"",d=typeof i?.tableId=="string"?i.tableId.trim():"";(!h||!d||typeof File>"u"||!(i.file instanceof File)||i.file.size<=0||i.file.size>2*1024*1024*1024||!I(h,d))&&u("INVALID_ATTACHMENT_TARGET"),n.siteToken?.trim()||u("ATTACHMENT_UPLOAD_FAILED"),b=`upload:${i.uploadId??++A}`,l={fileToken:"mock-file",name:i.file.name,size:i.file.size,mimeType:i.file.type}}else if(t===s.HttpRequestCancel||t===s.AttachmentUploadCancel){let i=t===s.HttpRequestCancel?`http:${o?.requestId}`:`upload:${o?.uploadId}`,h=m.get(i);h?.controller.abort(),h?.reject(new P(t===s.HttpRequestCancel?"HTTP_REQUEST_CANCELED":"ATTACHMENT_UPLOAD_CANCELED","\u6D4B\u8BD5\u8C03\u7528\u5DF2\u53D6\u6D88"))}else if(t===s.FunctionInvoke){let i=o?.functionName;(typeof i!="string"||!he(i))&&u("INVALID_FUNCTION_NAME"),i===D.GetCurrentUser?l={name:"\u6D4B\u8BD5\u7528\u6237",avatarUrl:""}:[D.Logout,D.RequestEdit].includes(i)||(!e.functions||!Object.prototype.hasOwnProperty.call(e.functions,i))&&u("FUNCTION_NOT_ALLOWED")}else t===s.AIFunctionStreamOpen||t===s.StreamOpen?(o?.subscriptionId||u("STREAM_UNAVAILABLE"),t===s.AIFunctionStreamOpen&&(!n.siteToken||!o.functionKey)&&u("STREAM_UNAVAILABLE")):(t===s.AIFunctionStreamClose||t===s.StreamClose)&&R.delete(o?.subscriptionId);let L=e.failures?.[t];L&&u(L.code,L.message);let x=new AbortController,M,N=()=>{},W=new Promise((i,h)=>{N=h});b??=`call:${++A}`,m.set(b,{controller:x,reject:N}),S&&(M=setTimeout(()=>{x.abort(),N(new P("REQUEST_TIMEOUT","HTTP \u8BF7\u6C42\u8D85\u65F6"))},S.timeoutMs));try{T();let i=e.handlers?.[t],h=t===s.FunctionInvoke?e.functions?.[o.functionName]:void 0,d=await Promise.race([Promise.resolve().then(()=>i?i(o,{signal:x.signal,emit:k}):h?h(o.params):l),W]);return T(),(t===s.AIFunctionStreamOpen||t===s.StreamOpen)&&R.add(o.subscriptionId),t===s.HttpRequest&&d&&typeof d=="object"&&"headers"in d?{...d,headers:Object.fromEntries(Object.entries(d.headers).filter(([U])=>Te.has(U.toLowerCase())))}:d}finally{clearTimeout(M),b&&m.delete(b)}};return{channel:{invokeApi:H,emitEvent:async(t,o)=>{c.push({event:t,payload:o})},subscribeEvent:(t,o)=>{let l=f.get(t)??new Set;return l.add(o),f.set(t,l),()=>{l.delete(o)}}},context:n,calls:a,events:c,emit:k,mount(t,o,l=new URL(o).origin){let T=new me(t,()=>{},`mock-${++A}`,[l],{sandbox:"allow-scripts allow-same-origin allow-downloads"},{enableBinaryFetch:!1});for(let b of Object.values(s))T.registerApi(b,S=>H(b,S));for(let b of[C.LifecycleReady,C.LifecycleLoadFailed])T.subscribeEvent(b,S=>{c.push({event:b,payload:S})});return p.add(T),T.setClientUrl(o),()=>{T.destroy(),p.delete(T)}},emitStreamMessage(t,o,l,T=!1){return R.has(t)?k(T?C.AIFunctionStreamMessage:C.StreamMessage,{subscriptionId:t,data:o,seq:l}):Promise.resolve()},dispose(){_=!0;for(let t of m.values())t.controller.abort(),t.reject(new P("CHANNEL_DESTROYED","\u6D4B\u8BD5\u5BBF\u4E3B\u5DF2\u9500\u6BC1"));m.clear(),f.clear(),R.clear();for(let t of p)t.destroy();p.clear()}}}export{v as DEFAULT_SITE_HTTP_BRIDGE_ALLOWLIST,fe as createHttpHandlers,Oe as createMockHost};
|
package/docs/capabilities.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# 能力与诊断
|
|
2
2
|
|
|
3
|
-
宿主字段形状为 `capabilities: { [name: string]: { version: number } }`。只有缺字段才使用下面的 `host-baseline
|
|
3
|
+
宿主字段形状为 `capabilities: { [name: string]: { version: number } }`。只有缺字段才使用下面的 `host-baseline` 宿主基线;显式空表不回退。null、数组或非正安全整数版本抛 CAPABILITY_CONTEXT_INVALID;未知能力和过低版本不支持。能力声明不代表用户有权限,也不保证依赖服务当前可用。
|
|
4
4
|
|
|
5
|
-
| 能力 | 分类 | 缺字段时 host-baseline
|
|
5
|
+
| 能力 | 分类 | 缺字段时 host-baseline(版本 1) |
|
|
6
6
|
|---|---|---|
|
|
7
7
|
| runtime.context | 关键 | 是 |
|
|
8
8
|
| http.request | 关键 | 是 |
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
| attachment.progress | 可选 | 是 |
|
|
22
22
|
| link.open | 可选 | 是 |
|
|
23
23
|
|
|
24
|
-
宿主集中部署;缺少 capabilities
|
|
24
|
+
宿主集中部署;缺少 capabilities 字段仅代表能力协商尚未上线,不代表缺少外链或进度能力。基线以包内 capabilities.json 的能力清单为准,由宿主注册与事件实现的回归测试验证。服务端 ctx.records 不属于页面能力协商;未知能力不在基线。[`queryCapability`](exports.md#page) 的 source 此时为 `host-baseline`;显式能力表为 `advertised`。
|
|
25
25
|
`readCapabilities(context)`、`hasCapability(context,name,minVersion)` 是纯查询;[`getCapabilities`](exports.md#page) 为标准查询入口,[`getRuntimeCapabilities`](exports.md#page) 是别名;[`queryCapability`](exports.md#page) 返回 supported/version/source/availability,未明确健康状态时 availability 为 unknown;`getRuntimeCapabilities(runtime?)` 每次读宿主,不跨应用、环境或用户缓存。[`requireCapability`](exports.md#page) 查询失败或缺失时抛错。所有公开业务入口、兼容 helper 与组件通过同一调用边界:等待配对 → 自动能力预检 → 发送。显式缺失能力时本地抛结构化能力错误,不发送业务请求;RuntimeContext 是协商引导请求,不对自身递归预检。显式 [`requireCapability`](exports.md#page) 仍可用于界面判断,宿主是最终授权方。
|
|
26
26
|
|
|
27
27
|
<!-- example: capabilities.ts -->
|
|
@@ -40,20 +40,3 @@ export async function inspectHost() {
|
|
|
40
40
|
输出含 source/code/upstreamCode/category/retryable/message/redaction;不附原始异常、请求参数、URL、身份、token。应用、环境、制品版本与 requestId/logid 由持有可信上下文的诊断汇总端补齐,本模块不伪造关联信息。
|
|
41
41
|
|
|
42
42
|
结构化清单位于包内 capabilities.json,测试保证它与共享 [`CAPABILITY_CATALOG`](exports.md#page) 一致。无缓存实现每次读取当前 channel 上下文,避免跨 mount/用户/应用泄漏旧状态。基线包含 link.open 与 attachment.progress;支持进度事件不代表每次上传都必定出现回调或保证服务当前可用。
|
|
43
|
-
|
|
44
|
-
## 基线来源
|
|
45
|
-
|
|
46
|
-
以下均引用 base-site-web 84afb07 的 `apps/site-web/src/view/preview/` 下文件;固定源码保存在 test/fixtures/host-84afb07。
|
|
47
|
-
|
|
48
|
-
| 能力 | 源码证据 |
|
|
49
|
-
|---|---|
|
|
50
|
-
| link.open / attachment.upload / attachment.cancel | runtime-host.ts:175、176、178 |
|
|
51
|
-
| http.request / http.cancel / function.invoke / runtime.context | runtime-host.ts:179、180、181、182 |
|
|
52
|
-
| chatbot.stream 的 open / close | chatbot-stream-broker.ts:201、204 |
|
|
53
|
-
| ai.function.stream 的 open / close | ai-function-broker.ts:476、479 |
|
|
54
|
-
| attachment.progress | runtime-host.ts:81–86 接入上传进度回调;attachment-proxy.ts:367 派发进度 |
|
|
55
|
-
| http.binary | http-proxy.ts:212–215、230–245 支持 arrayBuffer |
|
|
56
|
-
| runtime.user / runtime.[`logout`](exports.md#page) / runtime.[`requestEdit`](exports.md#page) | function-proxy.ts:145–152 的运行时 Function 路由 |
|
|
57
|
-
| runtime.lifecycle / runtime.mode | runtime-host.ts:196–203 订阅生命周期;162–165 派发 mode |
|
|
58
|
-
|
|
59
|
-
上述注册合计 11 个 site.v1 API,测试从固定源码提取注册列表并与基线逐项核对。无字段时安装 guard;显式非空表缺 link.open 或显式空表时均不安装,且不回退到基线。
|
package/docs/codegen.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Page 客户端代码生成
|
|
2
|
+
|
|
3
|
+
`@lark-base-open/base-database-app-sdk/codegen` 是 Node.js 工具入口,不导入浏览器运行时。首个支持版本为 0.0.1-alpha.1;调用方必须安装含此入口的 SDK,不回退旧模板。
|
|
4
|
+
|
|
5
|
+
<!-- example: codegen.ts -->
|
|
6
|
+
```ts
|
|
7
|
+
import { generateFunctionClients, type FunctionContractData } from '@lark-base-open/base-database-app-sdk/codegen';
|
|
8
|
+
const data: FunctionContractData = {
|
|
9
|
+
contractFormatVersion: 1,
|
|
10
|
+
layout: 'workspace',
|
|
11
|
+
schemaHash: 'sha256:application-contract-hash',
|
|
12
|
+
functions: [{ functionId: 'hello', functionType: 2, output: { type: 'string' } }],
|
|
13
|
+
inputs: [{ functionId: 'hello', inputSchema: { type: 'object', properties: {}, additionalProperties: false } }],
|
|
14
|
+
client: true,
|
|
15
|
+
augmentation: 'none',
|
|
16
|
+
};
|
|
17
|
+
const files = await generateFunctionClients(data);
|
|
18
|
+
// [{path:'page/src/generated/function-inputs.ts',content:...},
|
|
19
|
+
// {path:'page/src/generated/functions.ts',content:...}]
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
此入口只接收提取后的 JSON 数据并返回 `{path, content}[]`,不读工程、不写文件、不加载函数源码。函数契约、inputSchema、SQL/Mutation 命名参数转换及 parameterOptions 由 CLI 提取;SDK 负责所有 SDK 工程的 Page 生成模板。`functions` 的 output 仅用于 FaaS(functionType 2),SQL/Mutation 返回类型保持 unknown。
|
|
23
|
+
|
|
24
|
+
格式类型 `FunctionContractData`、`FunctionInputContract`、`ClientContract` 与等价校验函数 `validateContractData` 同入口导出。`SUPPORTED_CONTRACT_FORMAT_VERSIONS` 当前为 `[1]`。未知格式版本抛 `CodegenError`,其 `code=CONTRACT_FORMAT_UNSUPPORTED`、`toJSON()` 返回结构化错误;非法数据为 `CONTRACT_DATA_INVALID`。不接受外部 Schema 引用;沿用单 Schema 32KiB、256 节点和16层限制。
|
|
25
|
+
|
|
26
|
+
`client=false` 仅输出输入映射;`augmentation=compat|client` 生成相应 FunctionInputMap 增强文件,none 不生成增强。`previousAugmentation` 若提供,必须为已知的未修改生成文件,否则报 `CONTRACT_INPUT_MAP_CONFLICT`。输出保留历史 CLI 的文件文本和生成注释,便于逐字节对比。
|
|
27
|
+
|
|
28
|
+
CLI 在与契约提取相同的受限子进程边界执行安装在工程内的 codegen:环境清空、只读临时包快照、禁子进程/写文件、时间和内存限制;父进程校验相对路径、文件大小与目标软链后写入,check 比较文件内容。此机制不是面向任意恶意原生程序的通用沙箱。
|
package/docs/compat.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 老模板兼容
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
兼容基准为 classic 模板的 `page/src/lib/database-runtime.ts`;源码版本与摘要保存在仓库测试夹具中。page 保留其全部 36 个值/类型导出;测试夹具保存原始文件与 SHA256。兼容长期维护,暂不设截止日期。
|
|
4
4
|
|
|
5
5
|
数据库与附件实现位于 page/database.ts,AI helper 位于 page/ai-compat.ts。替换 import 后,Function 调用仍返回原 envelope;HTTP 失败仍抛错;Mutation batch 接受 1–200 个命名参数对象,与统一 Function 路由一致;旧位置数组必须按已发布的冻结声明人工迁移,不能直接替换 import 后发送。[`resolveDatabaseContext`](exports.md#page) 保持宿主优先、路径兜底和原会话缓存语义。媒体任务保留原 helper 的宽容归一化及错误名/码,流式 destroy 仍取消。
|
|
6
6
|
|
package/docs/components.md
CHANGED
|
@@ -14,7 +14,7 @@ Panel 的 slots.empty/loading/option/footer 均为 ComponentType;option 收到
|
|
|
14
14
|
|
|
15
15
|
CSS 只作用 `.base-app-platform`,位于 `@layer base-app`。可覆盖 `--base-app-color-bg/fg/muted/accent/danger/border`、`--base-app-radius`、`--base-app-font-family/font-size`、`--base-app-z-popover/z-floating` 和 `--base-app-shadow`。Portal 保留 Provider 的主题,支持 light/dark、文档方向与 reduced-motion。不依赖用户工程的 Tailwind 编译。
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
组件语义与 classic 模板组件保持兼容;源码版本与摘要保存在仓库测试目录中。旧成员推荐接口、返回归一化、zh/en/ja 文案和权限语义保留;交互骨架与主题 CSS 重写。旧 ui-language 测试中的业务 App 和 PageState 属模板而非 SDK,迁移保留该用例中全部 SDK 断言。
|
|
18
18
|
|
|
19
19
|
体积由 scripts/measure-size.mjs 对生产 ESM bundle 测 gzip,包含 channel/cmdk/Radix,排除 React/react-dom/TanStack Query;测试阈值在 test/size-budget.json,为本次实测值 +10%。CSS 单独计量。浏览器夹具与 mock host 证明组件行为,不证明 BOE 权限或真实业务请求。
|
|
20
20
|
|
package/docs/exports.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# 公开导出清单
|
|
2
2
|
|
|
3
|
-
由 package.json exports 指向的实际 dist ESM 与 TypeScript 声明生成;在 SDK 源码仓运行 npm run docs:exports 更新,npm test 校验。server/shared 另提供 CJS;CSS 入口为 /page/styles.css。没有根入口或源码通配入口。
|
|
3
|
+
由 package.json exports 指向的实际 dist ESM 与 TypeScript 声明生成;在 SDK 源码仓运行 npm run docs:exports 更新,npm test 校验。server/shared/codegen 另提供 CJS;CSS 入口为 /page/styles.css。没有根入口或源码通配入口。
|
|
4
4
|
|
|
5
5
|
## page
|
|
6
6
|
|
|
7
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
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`。
|
|
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`、`SiteAIFunctionArguments`、`SiteAIFunctionError`、`SiteAIFunctionOptions`、`SiteAIFunctionParams`、`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`、`SiteFunctionValidationIssue`、`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
10
|
|
|
11
11
|
## server
|
|
12
12
|
|
|
@@ -37,3 +37,9 @@
|
|
|
37
37
|
值:`baseAppDevelopmentPairingPlugin`。
|
|
38
38
|
|
|
39
39
|
类型与值完整清单:`baseAppDevelopmentPairingPlugin`。
|
|
40
|
+
|
|
41
|
+
## codegen
|
|
42
|
+
|
|
43
|
+
值:`CodegenError`、`SUPPORTED_CONTRACT_FORMAT_VERSIONS`、`generateFunctionClients`、`validateContractData`。
|
|
44
|
+
|
|
45
|
+
类型与值完整清单:`ClientContract`、`CodegenError`、`FunctionContractData`、`FunctionInputContract`、`GeneratedFile`、`SUPPORTED_CONTRACT_FORMAT_VERSIONS`、`generateFunctionClients`、`validateContractData`。
|
package/docs/page.md
CHANGED
|
@@ -44,7 +44,7 @@ export function onSupportedLinkClick(url: string) {
|
|
|
44
44
|
```
|
|
45
45
|
|
|
46
46
|
[`openLink`](exports.md#page) 支持 http/https/mailto/tel;宿主拒绝 javascript/data/file 等协议。HTTP(S) 必须经过宿主 seclink,服务不可用时失败关闭;opened 只代表宿主已提交导航,不保证弹窗可见。
|
|
47
|
-
`installLinkGuard()` 同步返回 cleanup,内部异步查询能力;查询前、查询失败或显式能力表缺 link.open 时不拦截;无能力字段采用 host-baseline
|
|
47
|
+
`installLinkGuard()` 同步返回 cleanup,内部异步查询能力;查询前、查询失败或显式能力表缺 link.open 时不拦截;无能力字段采用 host-baseline 并安装拦截。onDiagnostic 收到脱敏提示,默认 console.warn。应在页面启动时安装,而不是点击后才查询。支持能力时沿用 channel guard:捕获外链、_blank 与中键;download/hash/普通同源导航不接管。cleanup 也阻止迟到查询安装。
|
|
48
48
|
不要将 guard 当作任意 HTML 的安全过滤器;尤其不允许把不可信字符串直接设为 href。浏览器原生行为仍受 iframe sandbox 限制。
|
|
49
49
|
|
|
50
50
|
## 附件字节上传
|
package/docs/testing.md
CHANGED
|
@@ -14,7 +14,7 @@ export async function testUser() {
|
|
|
14
14
|
}
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
`createMockHost({context,capabilities,bindings,allowedRoutes,secureLinkAvailable,functions,handlers,failures})`:context 提供模拟 locale/env/siteToken;capabilities
|
|
17
|
+
`createMockHost({context,capabilities,bindings,allowedRoutes,secureLinkAvailable,functions,handlers,failures})`:context 提供模拟 locale/env/siteToken;capabilities 缺失模拟未提供能力字段的宿主,空表显式关闭能力;bindings 默认空,拒绝全部上传;allowedRoutes 默认固定宿主白名单。
|
|
18
18
|
11 个 site.v1 API 都有默认响应,业务函数只有显式登记才允许;`handlers` 可替换下游结果,但不能绕过前置拒绝规则。`failures` 为指定 API 注入错误码。HTTP handler 返回 wire response(body/headers/status/statusText),runtime client 转为 Response。
|
|
19
19
|
`calls/events` 仅保留测试夹具数据;dispose 清理监听、流、挂起请求和浏览器宿主。HTTP/上传支持定点取消;可通过 emit 注入进度、mode、AI error/done,通过 emitStreamMessage 注入带 seq 的流消息。默认不模拟真实上传耗时、AI 内容、并发排队、seclink 重定向或 backend 身份。
|
|
20
20
|
`mount(container,url,allowedOrigin?)` 在真实浏览器中使用 channel 的 Host 创建 iframe;默认 origin 来自 url,sandbox 保留脚本/同源/下载限制,不允许弹窗(不模拟表单和模态框)。返回卸载函数。origin/source 错误由真实 channel 丢弃,不虚构业务错误回包;直接 channel 对象是内存调用,不声称校验浏览器 origin。
|
|
@@ -34,21 +34,21 @@ MSW 工厂仅服务真正的 HTTP 请求,如 FaaS 下游或页面直接请求
|
|
|
34
34
|
|
|
35
35
|
## 拒绝规则来源与逐条对照
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
拒绝规则与宿主纯校验实现逐条对照;源码快照、版本来源及摘要仅保存在仓库测试目录。隔离测试仅替换日志、平台配置、身份注入与下游传输,不替换拒绝规则。
|
|
38
38
|
|
|
39
|
-
| 规则 |
|
|
39
|
+
| 规则 | 原文件 | 测试 |
|
|
40
40
|
|---|---|---|
|
|
41
|
-
| origin 白名单,绑定 sourceWindow | apps/site-web/src/view/preview/runtime-host.ts
|
|
42
|
-
| http/https/mailto/tel 之外链接,空 URL、相对 URL | apps/site-web/src/view/preview/open-link.ts
|
|
43
|
-
| http(s) seclink 不可用 | apps/site-web/src/view/preview/open-link.ts
|
|
44
|
-
| 单斜杠、跨域、反斜杠、fragment、编码、穿越、控制字符 | apps/site-web/src/view/preview/http-proxy.ts
|
|
45
|
-
| Header 类型、名称、禁止清单及前缀 | apps/site-web/src/view/preview/http-proxy.ts
|
|
46
|
-
| method、body、GET body、timeout | apps/site-web/src/view/preview/http-proxy.ts
|
|
47
|
-
| 默认路由白名单/方法 | apps/site-web/src/view/preview/http-bridge-allowlist.ts
|
|
48
|
-
| responseType、空/重复/过长 requestId | apps/site-web/src/view/preview/http-proxy.ts
|
|
49
|
-
| 上传 File、非空、2 GiB、Base/表成对绑定 | apps/site-web/src/view/preview/attachment-proxy.ts
|
|
50
|
-
| 封面/下载 file/table/field/record、应用、环境、单文件对应 | apps/site-web/src/view/preview/http-attachment-context.ts
|
|
51
|
-
| Function 格式与发布清单 | apps/site-web/src/view/preview/function-proxy.ts
|
|
41
|
+
| origin 白名单,绑定 sourceWindow | apps/site-web/src/view/preview/runtime-host.ts;channel src/host.ts:255 | test/browser/run.mjs:真实 Host 接受正确请求,错误 origin/source 不调用 handler |
|
|
42
|
+
| http/https/mailto/tel 之外链接,空 URL、相对 URL | apps/site-web/src/view/preview/open-link.ts | rules.test.ts 链接矩阵,javascript/data/file 逐例 |
|
|
43
|
+
| http(s) seclink 不可用 | apps/site-web/src/view/preview/open-link.ts | rules.test.ts seclink 不可用 |
|
|
44
|
+
| 单斜杠、跨域、反斜杠、fragment、编码、穿越、控制字符 | apps/site-web/src/view/preview/http-proxy.ts | rules.test.ts HTTP 路径矩阵 |
|
|
45
|
+
| Header 类型、名称、禁止清单及前缀 | apps/site-web/src/view/preview/http-proxy.ts | rules.test.ts 每个受限头单独用例 |
|
|
46
|
+
| method、body、GET body、timeout | apps/site-web/src/view/preview/http-proxy.ts | rules.test.ts HTTP init 矩阵 |
|
|
47
|
+
| 默认路由白名单/方法 | apps/site-web/src/view/preview/http-bridge-allowlist.ts | rules.test.ts 非白名单与合法读取;默认表逐条等价测试 |
|
|
48
|
+
| responseType、空/重复/过长 requestId | apps/site-web/src/view/preview/http-proxy.ts | rules.test.ts 与 runtime.test.ts |
|
|
49
|
+
| 上传 File、非空、2 GiB、Base/表成对绑定 | apps/site-web/src/view/preview/attachment-proxy.ts | rules.test.ts 附件矩阵 |
|
|
50
|
+
| 封面/下载 file/table/field/record、应用、环境、单文件对应 | apps/site-web/src/view/preview/http-attachment-context.ts | rules.test.ts 下载坐标矩阵及封面缺坐标拒绝 |
|
|
51
|
+
| Function 格式与发布清单 | apps/site-web/src/view/preview/function-proxy.ts | runtime.test.ts 函数拒绝 |
|
|
52
52
|
|
|
53
53
|
实现采用原宿主的纯校验片段(HTTP normalize、白名单匹配、附件上下文);仅移除平台配置读取并导出测试需要的函数,不复制 channel 实现。宿主版本变化时需更新快照、规则和矩阵一起审查。生产鉴权仍由真正的宿主和服务端执行,模拟成功不证明真实权限。
|
|
54
54
|
|
package/docs/upgrade.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# 迁移到 SDK 0.0.1-alpha.
|
|
1
|
+
# 迁移到 SDK 0.0.1-alpha.1
|
|
2
2
|
|
|
3
3
|
先核对工程 AGENTS.md 中的模板版本、实际安装 SDK 和 CLI 随包 Skill。SDK 未发布期间仅在本地验收副本使用经审核 tarball;正式模板不写 file:/tarball 依赖,也不伪造 registry lock。老模板持续兼容并维护安全与事实更正,未设截止日期。
|
|
4
4
|
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
6. 选择保留旧启动入口或采用 [startBaseApp](start-base-app.md)。采用后移除重复的 createRoot、guard、ready/load-failed 和默认悬浮按钮;保留业务 Provider、样式及路由。仅迁移 import 不要求更换布局。
|
|
13
13
|
7. 运行类型检查及 `base-database-cli check --json`;按 Skill 的验收和发布流程验证真实宿主中的链接、附件、成员选择与写入结果。验证失败时恢复源码与锁文件,不能把 mock 成功当作真实权限证明。
|
|
14
14
|
|
|
15
|
-
guard 仍同步返回 cleanup,安装前异步查询能力。缺 capabilities 字段采用 host-baseline
|
|
15
|
+
guard 仍同步返回 cleanup,安装前异步查询能力。缺 capabilities 字段采用 host-baseline;显式空表或缺 link.open 时不拦截。只保留一处 guard;查询期间原生行为仍受 iframe sandbox 约束。测试替身只在测试中导入。
|
|
16
16
|
|
|
17
17
|
## 模板 1 → 2:手动迁移
|
|
18
18
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { generateFunctionClients, type FunctionContractData } from '@lark-base-open/base-database-app-sdk/codegen';
|
|
2
|
+
const data: FunctionContractData = {
|
|
3
|
+
contractFormatVersion: 1,
|
|
4
|
+
layout: 'workspace',
|
|
5
|
+
schemaHash: 'sha256:application-contract-hash',
|
|
6
|
+
functions: [{ functionId: 'hello', functionType: 2, output: { type: 'string' } }],
|
|
7
|
+
inputs: [{ functionId: 'hello', inputSchema: { type: 'object', properties: {}, additionalProperties: false } }],
|
|
8
|
+
client: true,
|
|
9
|
+
augmentation: 'none',
|
|
10
|
+
};
|
|
11
|
+
const files = await generateFunctionClients(data);
|
|
12
|
+
// [{path:'page/src/generated/function-inputs.ts',content:...},
|
|
13
|
+
// {path:'page/src/generated/functions.ts',content:...}]
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lark-base-open/base-database-app-sdk",
|
|
3
|
-
"version": "0.0.1-alpha.
|
|
4
|
-
"description": "Base Database
|
|
3
|
+
"version": "0.0.1-alpha.1",
|
|
4
|
+
"description": "Base Database 页面与服务端 SDK、平台组件及测试工具",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": [
|
|
7
7
|
"**/*.css"
|
|
@@ -44,7 +44,17 @@
|
|
|
44
44
|
"types": "./dist/vite.d.ts",
|
|
45
45
|
"import": "./dist/vite.js"
|
|
46
46
|
},
|
|
47
|
-
"./development-pairing.json": "./development-pairing.json"
|
|
47
|
+
"./development-pairing.json": "./development-pairing.json",
|
|
48
|
+
"./codegen": {
|
|
49
|
+
"import": {
|
|
50
|
+
"types": "./dist/codegen.d.ts",
|
|
51
|
+
"default": "./dist/codegen.js"
|
|
52
|
+
},
|
|
53
|
+
"require": {
|
|
54
|
+
"types": "./dist/codegen.d.cts",
|
|
55
|
+
"default": "./dist/codegen.cjs"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
48
58
|
},
|
|
49
59
|
"files": [
|
|
50
60
|
"dist",
|
|
@@ -56,18 +66,19 @@
|
|
|
56
66
|
"development-pairing.json"
|
|
57
67
|
],
|
|
58
68
|
"scripts": {
|
|
59
|
-
"build": "tsup && tsup --config tsup.cjs.config.ts && cp src/page/theme.css dist/theme.css",
|
|
60
|
-
"test": "npm run build && node --import tsx --test test/*.test.ts && npm run test:types && npm run test:components",
|
|
69
|
+
"build": "node scripts/sync-capabilities.mjs && tsup && tsup --config tsup.cjs.config.ts && cp src/page/theme.css dist/theme.css",
|
|
70
|
+
"test": "node scripts/sync-capabilities.mjs --check && npm run build && node --import tsx --test test/*.test.ts && npm run test:types && npm run test:components",
|
|
61
71
|
"test:types": "tsc -p tsconfig.examples.json && tsc -p tsconfig.server.json && tsc -p tsconfig.cjs.json",
|
|
62
72
|
"test:browser": "node test/browser/verify.mjs && node test/browser/ui-verify.mjs",
|
|
63
73
|
"test:components": "vitest run",
|
|
64
74
|
"docs:exports": "node scripts/doc-exports.mjs"
|
|
65
75
|
},
|
|
66
76
|
"dependencies": {
|
|
67
|
-
"@lark-base-open/base-site-channel": "0.1.
|
|
77
|
+
"@lark-base-open/base-site-channel": "0.1.6",
|
|
68
78
|
"@radix-ui/react-popover": "1.2.0",
|
|
69
79
|
"@standard-schema/spec": "1.0.0",
|
|
70
|
-
"cmdk": "1.1.1"
|
|
80
|
+
"cmdk": "1.1.1",
|
|
81
|
+
"json-schema-to-typescript": "16.0.0"
|
|
71
82
|
},
|
|
72
83
|
"peerDependencies": {
|
|
73
84
|
"@tanstack/react-query": "5.104.1",
|
|
@@ -101,6 +112,9 @@
|
|
|
101
112
|
],
|
|
102
113
|
"shared": [
|
|
103
114
|
"dist/shared.d.cts"
|
|
115
|
+
],
|
|
116
|
+
"codegen": [
|
|
117
|
+
"dist/codegen.d.cts"
|
|
104
118
|
]
|
|
105
119
|
}
|
|
106
120
|
}
|