@lark-base-open/base-database-app-sdk 0.0.0-stage → 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/LICENSE +6 -0
- package/README.md +42 -2
- package/capabilities.json +114 -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-5XIFZ4AH.js +1 -0
- package/dist/chunk-OHPYKFCM.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 +6 -0
- package/dist/components.js +1 -0
- package/dist/index-64557073.d.ts +306 -0
- package/dist/page.d.ts +138 -0
- package/dist/page.js +1 -0
- package/dist/server.cjs +1 -0
- package/dist/server.d.cts +79 -0
- package/dist/server.d.ts +79 -0
- package/dist/server.js +1 -0
- package/dist/shared.cjs +1 -0
- package/dist/shared.d.cts +115 -0
- package/dist/shared.d.ts +115 -0
- package/dist/shared.js +1 -0
- package/dist/testing.d.ts +74 -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 +42 -0
- package/docs/codegen.md +28 -0
- package/docs/compat.md +36 -0
- package/docs/components.md +34 -0
- package/docs/exports.md +45 -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/codegen.ts +13 -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 +119 -4
package/dist/server.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as y}from"./chunk-3JX3FIBG.js";var d=t=>typeof t=="object"&&t!==null&&!Array.isArray(t),w=t=>typeof t=="string"&&/^rec[A-Za-z0-9]{1,32}$/.test(t);function O(t){let a=new Set,i=e=>{if(!(e===null||typeof e=="string"||typeof e=="boolean"||typeof e=="number"&&Number.isFinite(e))){if(typeof e!="object"||!e||a.has(e)||Array.isArray(e)&&Object.getPrototypeOf(e)!==Array.prototype||!Array.isArray(e)&&Object.getPrototypeOf(e)!==Object.prototype&&Object.getPrototypeOf(e)!==null||Array.isArray(e)&&(Object.keys(e).length!==e.length||Object.keys(e).some((r,n)=>r!==String(n))))throw 0;a.add(e);for(let r of Reflect.ownKeys(e)){if(Array.isArray(e)&&r==="length")continue;if(typeof r!="string")throw 0;let n=Object.getOwnPropertyDescriptor(e,r);if(!n.enumerable||!("value"in n))throw 0;i(n.value)}a.delete(e)}};try{i(t)}catch{throw new y("SDK_OUTPUT_NOT_JSON","\u8F93\u51FA\u5FC5\u987B\u662F\u65E0\u635F JSON \u503C")}}function x(t,a,i){return{status:"unknown",code:t,retryable:!1,...a?{requestedIds:[...a]}:{},...i?{logid:i}:{}}}var P=t=>typeof t=="number"&&[401,403,404,91403,1254302,1254043,1254044].includes(t),I=t=>({status:"failed",code:"SDK_RECORDS_REJECTED",upstreamCode:t,retryable:!1,appliedIds:[]}),F=t=>Array.isArray(t)&&t.every(w)&&new Set(t).size===t.length;function _(t,a){let i=()=>x("SDK_RECORDS_RESULT_UNKNOWN",a.requestedIds);if(d(t)&&P(t.code))return I(t.code);if(!d(t)||t.code!==0||!d(t.data))return i();let e=t.data,r=e.ignored_fields??[];if(!Array.isArray(r)||!r.every(s=>d(s)&&["id","name","reason"].every(o=>typeof s[o]=="string")))return i();let n=e.record_not_found??[];if(!F(n))return i();let c=a.requestedIds??[],u,m;if(a.operation==="appendAttachments"||a.operation==="removeAttachments"){if(!d(e.attachments))return i();try{O(e.attachments)}catch{return i()}for(let[s,o]of Object.entries(e.attachments)){if(!w(s)||!c.includes(s)||!d(o)||!Object.keys(o).length)return i();for(let[p,f]of Object.entries(o))if(!/^fld[A-Za-z0-9]+$/.test(p)||!Array.isArray(f)||!f.every(g=>d(g)&&typeof g.file_token=="string"&&g.file_token.length>0))return i()}if(u=Object.keys(e.attachments),n.some(s=>!c.includes(s)||u.includes(s))||!r.length&&c.some(s=>!u.includes(s)&&!n.includes(s)))return i();m=e.attachments}else{if(!F(e.record_id_list))return i();let s=e.record_id_list;if(a.operation==="create"){if(s.length!==a.count||Object.hasOwn(e,"record_not_found"))return i();u=s}else if(a.operation==="update"){if(s.some(o=>!c.includes(o)||n.includes(o))||n.some(o=>!c.includes(o))||s.length+n.length!==c.length)return i();u=s}else{if(s.length!==c.length||s.some((o,p)=>o!==c[p])||n.some(o=>!c.includes(o)))return i();u=s.filter(o=>!n.includes(o))}}let l=m===void 0?{}:{attachments:m};return n.length||r.length?{status:"partial",appliedIds:u,notFoundIds:n,ignoredFields:r,...l}:{status:"success",appliedIds:u,notFoundIds:[],ignoredFields:[],...l}}var C={create:"records/batch_create",update:"records/batch_update",delete:"records/batch_delete",appendAttachments:"append_attachments",removeAttachments:"remove_attachments"},S=t=>({status:"failed",code:t,retryable:!1,appliedIds:[]});function j(t){if(t?.platformEnv==="online")return t.brand==="feishu"?"https://open.feishu.cn":t.brand==="lark"?"https://open.larksuite.com":void 0;if(t?.brand==="feishu"){if(t.platformEnv==="pre")return"https://open.feishu-pre.cn";if(t.platformEnv==="boe")return"https://open.feishu-boe.cn"}}function E(t,a){let i=async(e,r,n,c)=>{if(!a)return S("SDK_RUNTIME_PROFILE_MISSING");let u=j(a);if(!u)return S("SDK_RUNTIME_PROFILE_UNSUPPORTED");if(c?.signal?.aborted)return S("SDK_CANCELLED");let m,l,s;try{if(!d(r)||typeof r.baseToken!="string"||typeof r.tableId!="string"||!/^[A-Za-z0-9]+$/.test(r.baseToken)||!/^tbl[A-Za-z0-9]+$/.test(r.tableId))throw 0;if(O(n),e==="create"){if(!Array.isArray(n)||!n.every(d))throw 0;s=n.length,m={create_records:n}}else if(e==="update"){if(!d(n)||!Object.values(n).every(d))throw 0;l=Object.keys(n),s=l.length,m={update_records:n}}else if(e==="delete"){if(!Array.isArray(n)||!n.every(w))throw 0;l=n,s=n.length,m={record_id_list:n}}else{if(!d(n))throw 0;let o=Object.create(null),p=0;for(let[f,g]of Object.entries(n)){if(!w(f)||!d(g))throw 0;for(let[A,b]of Object.entries(g)){if(!A.trim()||!Array.isArray(b))throw 0;for(let h of b){if(!d(h)||typeof h.file_token!="string"||!h.file_token.trim()||Object.keys(h).some(R=>!["file_token",...e==="appendAttachments"?["image_width","image_height"]:[]].includes(R)))throw 0;for(let R of["image_width","image_height"])if(h[R]!==void 0&&(typeof h[R]!="number"||!Number.isInteger(h[R])||h[R]<=0))throw 0}p+=b.length,b.length&&(o[f]??=Object.create(null),o[f][A]=b)}}if(p>50)throw 0;if(!p)return{status:"success",appliedIds:[],notFoundIds:[],ignoredFields:[],noop:!0};l=Object.keys(o),m={attachments:o}}if(s!==void 0&&(s<1||s>200)||l&&(!l.every(w)||new Set(l).size!==l.length))throw 0}catch{return S("SDK_RECORDS_INPUT_INVALID")}try{let o=await t.fetch(`${u}/open-apis/bitable/v1/apps/${encodeURIComponent(r.baseToken)}/tables/${encodeURIComponent(r.tableId)}/${C[e]}`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(m),signal:c?.signal}),p=o.headers.get("x-tt-logid")??void 0;if([401,403,404].includes(o.status))return I(o.status);if(!o.ok)return x("SDK_RECORDS_HTTP_ERROR",l,p);let f=_(await o.json(),{operation:e,requestedIds:l,count:s});return f.status==="unknown"&&p?{...f,logid:p}:f}catch(o){let p=o instanceof Error?o.name:"";return x(p==="AbortError"?"SDK_CANCELLED":p==="TimeoutError"?"SDK_TIMEOUT":"SDK_RECORDS_RESULT_UNKNOWN",l)}};return Object.fromEntries(Object.keys(C).map(e=>[e,(r,n,c)=>i(e,r,n,c)]))}var T=Symbol("base-database-runtime-profile");function N(t,a){let i=Object.freeze({platformEnv:a.platformEnv,brand:a.brand});return function(e,r){let n=Object.defineProperty({...r},T,{value:i});return t.call(this,e,n)}}function k(t){return{...t,records:E(t,t[T])}}async function D(t,a,i){let e;try{e=await t["~standard"].validate(a)}catch{throw new y("SDK_VALIDATOR_FAILED","Schema \u6821\u9A8C\u5668\u6267\u884C\u5931\u8D25")}if(e.issues){let r=e.issues.map(n=>(n.path??[]).map(c=>{let u=typeof c=="object"?c.key:c;return typeof u=="number"||typeof u=="string"?u:"[symbol]"}));throw new y(`SDK_${i}_INVALID`,i==="INPUT"?"\u8F93\u5165\u4E0D\u7B26\u5408\u5951\u7EA6":"\u8F93\u51FA\u4E0D\u7B26\u5408\u5951\u7EA6",{paths:r})}return e.value}function K(t){return async(a,i)=>{try{let e=await D(t.input,a,"INPUT"),r=await t.handler(e,k(i)),n=await D(t.output,r,"OUTPUT");return O(n),{code:0,msg:"success",data:n}}catch(e){let r=e instanceof y?e:new y("SDK_HANDLER_FAILED","\u51FD\u6570\u6267\u884C\u5931\u8D25");return console.error(JSON.stringify({source:"sdk",code:r.code,paths:r.paths,retryable:r.retryable,outcome:r.outcome})),{code:r.wireCode,msg:r.safeMessage}}}}export{y as SDKError,N as bindFunctionRuntime,k as createServerContext,K as defineFunction,_ as normalizeRecordsResult};
|
package/dist/shared.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var p=Object.defineProperty;var b=Object.getOwnPropertyDescriptor;var c=Object.getOwnPropertyNames;var m=Object.prototype.hasOwnProperty;var C=(t,r)=>{for(var e in r)p(t,e,{get:r[e],enumerable:!0})},E=(t,r,e,n)=>{if(r&&typeof r=="object"||typeof r=="function")for(let o of c(r))!m.call(t,o)&&o!==e&&p(t,o,{get:()=>r[o],enumerable:!(n=b(r,o))||n.enumerable});return t};var A=t=>E(p({},"__esModule",{value:!0}),t);var T={};C(T,{CAPABILITY_CATALOG:()=>y.CAPABILITY_CATALOG,CapabilityUnavailableError:()=>d,DiagnosticCode:()=>a,SDKError:()=>i,classifyRuntimeError:()=>I});module.exports=A(T);var y=require("@lark-base-open/base-site-channel/capabilities");var u=Symbol.for("@lark-base-open/base-database-app-sdk/SDKError/v1"),i=class extends Error{constructor(e,n,o,l){super(n);this.code=e;this.name="SDKError",this.safeMessage=n;let s=typeof o=="object"?o:{};this.upstreamCode=typeof o=="string"?o:void 0,this.wireCode=Number.isSafeInteger(s.wireCode)&&s.wireCode!==0?s.wireCode:e==="SDK_TIMEOUT"?504:e==="SDK_CANCELLED"?499:e==="SDK_INPUT_INVALID"?400:500,this.retryable=s.retryable??l??!1,this.outcome=s.outcome??"failed",this.paths=s.paths??[]}[u]=!0;static[Symbol.hasInstance](e){return this!==i?Function.prototype[Symbol.hasInstance].call(this,e):e!==null&&typeof e=="object"&&e[u]===!0}wireCode;safeMessage;retryable;outcome;paths;upstreamCode};var a={HostUnsupported:"CAPABILITY_HOST_UNSUPPORTED",PermissionDenied:"CAPABILITY_PERMISSION_DENIED",RegionUnavailable:"CAPABILITY_REGION_UNAVAILABLE",TemporarilyUnavailable:"CAPABILITY_TEMPORARILY_UNAVAILABLE",ContextInvalid:"CAPABILITY_CONTEXT_INVALID",UnavailableUnknown:"CAPABILITY_UNAVAILABLE_UNKNOWN"},d=class extends i{constructor(e,n){super(a.HostUnsupported,`\u5F53\u524D\u5BBF\u4E3B\u4E0D\u652F\u6301 ${e} v${n}`);this.capability=e;this.minimumVersion=n;this.name="CapabilityUnavailableError"}};function I(t){let r=t&&typeof t=="object"?t:{},e=typeof r.code=="string"||typeof r.code=="number"?String(r.code):"UNKNOWN",n=a.UnavailableUnknown,o="unknown";[a.HostUnsupported,"10004","PROTOCOL_MISMATCH","ATTACHMENT_UNAVAILABLE","HTTP_RESPONSE_TYPE_UNSUPPORTED"].includes(e)?(n=a.HostUnsupported,o="host-unsupported"):[a.PermissionDenied,"403","PERMISSION_DENIED","FORBIDDEN","FUNCTION_NOT_ALLOWED"].includes(e)?(n=a.PermissionDenied,o="permission-denied"):[a.RegionUnavailable,"REGION_UNAVAILABLE","REGION_NOT_SUPPORTED"].includes(e)?(n=a.RegionUnavailable,o="region-unavailable"):[a.TemporarilyUnavailable,"REQUEST_TIMEOUT","10003","10005","HTTP_REQUEST_FAILED","FUNCTION_REQUEST_FAILED","STREAM_DISCONNECTED","STREAM_HEALTH_TIMEOUT","ATTACHMENT_UPLOAD_STALLED"].includes(e)?(n=a.TemporarilyUnavailable,o="temporary"):e===a.ContextInvalid&&(n=a.ContextInvalid,o="protocol");let l={"host-unsupported":"\u5F53\u524D\u5BBF\u4E3B\u4E0D\u652F\u6301\u6B64\u80FD\u529B","permission-denied":"\u5F53\u524D\u8EAB\u4EFD\u6216\u53D1\u5E03\u6E05\u5355\u4E0D\u5141\u8BB8\u6B64\u64CD\u4F5C","region-unavailable":"\u5BBF\u4E3B\u660E\u786E\u5F53\u524D\u533A\u57DF\u4E0D\u53EF\u7528",temporary:"\u8C03\u7528\u6682\u65F6\u5931\u8D25\uFF0C\u8BF7\u6838\u5BF9\u7ED3\u679C\u540E\u51B3\u5B9A\u662F\u5426\u91CD\u8BD5",protocol:"\u5BBF\u4E3B\u80FD\u529B\u5B57\u6BB5\u65E0\u6548",unknown:"\u8C03\u7528\u5931\u8D25\uFF0C\u539F\u56E0\u672A\u660E\u786E"};return{source:"sdk",code:n,upstreamCode:r.upstreamCode??e,category:o,retryable:o==="temporary"&&r.retryable!==!1,message:l[o],redaction:"\u539F\u59CB\u9519\u8BEF\u6D88\u606F\u548C\u4E1A\u52A1\u53C2\u6570\u5DF2\u7701\u7565"}}0&&(module.exports={CAPABILITY_CATALOG,CapabilityUnavailableError,DiagnosticCode,SDKError,classifyRuntimeError});
|
|
@@ -0,0 +1,115 @@
|
|
|
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
|
+
|
|
4
|
+
interface SDKErrorOptions {
|
|
5
|
+
wireCode?: number;
|
|
6
|
+
retryable?: boolean;
|
|
7
|
+
outcome?: "failed" | "unknown";
|
|
8
|
+
paths?: readonly (readonly (string | number)[])[];
|
|
9
|
+
}
|
|
10
|
+
/** message 必须是开发者提供的安全文案,不能包含原始错误或参数。 */
|
|
11
|
+
declare const errorBrand: unique symbol;
|
|
12
|
+
declare class SDKError extends Error {
|
|
13
|
+
readonly code: string;
|
|
14
|
+
private readonly [errorBrand];
|
|
15
|
+
static [Symbol.hasInstance](value: unknown): boolean;
|
|
16
|
+
readonly wireCode: number;
|
|
17
|
+
readonly safeMessage: string;
|
|
18
|
+
readonly retryable: boolean;
|
|
19
|
+
readonly outcome: "failed" | "unknown";
|
|
20
|
+
readonly paths: readonly (readonly (string | number)[])[];
|
|
21
|
+
readonly upstreamCode?: string;
|
|
22
|
+
constructor(code: string, message: string, options?: SDKErrorOptions | string, legacyRetryable?: boolean);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
type JsonValue = null | boolean | number | string | JsonValue[] | {
|
|
26
|
+
[key: string]: JsonValue;
|
|
27
|
+
};
|
|
28
|
+
interface FunctionEnvelope<T = unknown> {
|
|
29
|
+
code: number;
|
|
30
|
+
msg: string;
|
|
31
|
+
data?: T;
|
|
32
|
+
}
|
|
33
|
+
type IgnoredField = {
|
|
34
|
+
id: string;
|
|
35
|
+
name: string;
|
|
36
|
+
reason: string;
|
|
37
|
+
};
|
|
38
|
+
type WriteResult = {
|
|
39
|
+
status: "success";
|
|
40
|
+
appliedIds: string[];
|
|
41
|
+
notFoundIds: [];
|
|
42
|
+
ignoredFields: [];
|
|
43
|
+
attachments?: JsonValue;
|
|
44
|
+
noop?: boolean;
|
|
45
|
+
} | {
|
|
46
|
+
status: "partial";
|
|
47
|
+
appliedIds: string[];
|
|
48
|
+
notFoundIds: string[];
|
|
49
|
+
ignoredFields: IgnoredField[];
|
|
50
|
+
attachments?: JsonValue;
|
|
51
|
+
} | {
|
|
52
|
+
status: "failed";
|
|
53
|
+
code: string;
|
|
54
|
+
upstreamCode?: number;
|
|
55
|
+
retryable: false;
|
|
56
|
+
appliedIds: [];
|
|
57
|
+
} | {
|
|
58
|
+
status: "unknown";
|
|
59
|
+
code: string;
|
|
60
|
+
retryable: false;
|
|
61
|
+
requestedIds?: string[];
|
|
62
|
+
logid?: string;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
type CapabilityDescriptor = Readonly<{
|
|
66
|
+
version: number;
|
|
67
|
+
}>;
|
|
68
|
+
type CapabilityMap = Readonly<Record<string, CapabilityDescriptor>>;
|
|
69
|
+
type RuntimeCapabilities = CapabilityMap;
|
|
70
|
+
type CapabilityUnavailableReason = "permission-denied" | "region-unavailable" | "temporarily-unavailable";
|
|
71
|
+
interface CapabilityContext {
|
|
72
|
+
capabilities?: CapabilityMap;
|
|
73
|
+
capabilityUnavailable?: Readonly<Record<string, {
|
|
74
|
+
reason: CapabilityUnavailableReason;
|
|
75
|
+
retryable: boolean;
|
|
76
|
+
}>>;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
type CapabilityName = keyof typeof CAPABILITY_CATALOG;
|
|
80
|
+
interface CapabilityQueryResult {
|
|
81
|
+
name: string;
|
|
82
|
+
requiredVersion: number;
|
|
83
|
+
version: number | null;
|
|
84
|
+
supported: boolean;
|
|
85
|
+
source: "advertised" | "host-baseline";
|
|
86
|
+
availability: "available" | "unknown" | CapabilityUnavailableReason | "unsupported";
|
|
87
|
+
}
|
|
88
|
+
declare const DiagnosticCode: {
|
|
89
|
+
readonly HostUnsupported: "CAPABILITY_HOST_UNSUPPORTED";
|
|
90
|
+
readonly PermissionDenied: "CAPABILITY_PERMISSION_DENIED";
|
|
91
|
+
readonly RegionUnavailable: "CAPABILITY_REGION_UNAVAILABLE";
|
|
92
|
+
readonly TemporarilyUnavailable: "CAPABILITY_TEMPORARILY_UNAVAILABLE";
|
|
93
|
+
readonly ContextInvalid: "CAPABILITY_CONTEXT_INVALID";
|
|
94
|
+
readonly UnavailableUnknown: "CAPABILITY_UNAVAILABLE_UNKNOWN";
|
|
95
|
+
};
|
|
96
|
+
type DiagnosticCategory = "host-unsupported" | "permission-denied" | "region-unavailable" | "temporary" | "protocol" | "unknown";
|
|
97
|
+
interface RuntimeDiagnostic {
|
|
98
|
+
source: "sdk";
|
|
99
|
+
code: string;
|
|
100
|
+
upstreamCode?: string;
|
|
101
|
+
category: DiagnosticCategory;
|
|
102
|
+
retryable: boolean;
|
|
103
|
+
message: string;
|
|
104
|
+
redaction: "原始错误消息和业务参数已省略";
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
declare class CapabilityUnavailableError extends SDKError {
|
|
108
|
+
readonly capability: string;
|
|
109
|
+
readonly minimumVersion: number;
|
|
110
|
+
constructor(capability: string, minimumVersion: number);
|
|
111
|
+
}
|
|
112
|
+
/** 不解析 message 猜测原因;可恢复不代表写入可安全重试。 */
|
|
113
|
+
declare function classifyRuntimeError(error: unknown): RuntimeDiagnostic;
|
|
114
|
+
|
|
115
|
+
export { CapabilityContext, CapabilityDescriptor, CapabilityMap, CapabilityName, CapabilityQueryResult, CapabilityUnavailableError, CapabilityUnavailableReason, DiagnosticCategory, DiagnosticCode, FunctionEnvelope, JsonValue, RuntimeCapabilities, RuntimeDiagnostic, SDKError, SDKErrorOptions, WriteResult, classifyRuntimeError };
|
package/dist/shared.d.ts
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
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
|
+
|
|
4
|
+
interface SDKErrorOptions {
|
|
5
|
+
wireCode?: number;
|
|
6
|
+
retryable?: boolean;
|
|
7
|
+
outcome?: "failed" | "unknown";
|
|
8
|
+
paths?: readonly (readonly (string | number)[])[];
|
|
9
|
+
}
|
|
10
|
+
/** message 必须是开发者提供的安全文案,不能包含原始错误或参数。 */
|
|
11
|
+
declare const errorBrand: unique symbol;
|
|
12
|
+
declare class SDKError extends Error {
|
|
13
|
+
readonly code: string;
|
|
14
|
+
private readonly [errorBrand];
|
|
15
|
+
static [Symbol.hasInstance](value: unknown): boolean;
|
|
16
|
+
readonly wireCode: number;
|
|
17
|
+
readonly safeMessage: string;
|
|
18
|
+
readonly retryable: boolean;
|
|
19
|
+
readonly outcome: "failed" | "unknown";
|
|
20
|
+
readonly paths: readonly (readonly (string | number)[])[];
|
|
21
|
+
readonly upstreamCode?: string;
|
|
22
|
+
constructor(code: string, message: string, options?: SDKErrorOptions | string, legacyRetryable?: boolean);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
type JsonValue = null | boolean | number | string | JsonValue[] | {
|
|
26
|
+
[key: string]: JsonValue;
|
|
27
|
+
};
|
|
28
|
+
interface FunctionEnvelope<T = unknown> {
|
|
29
|
+
code: number;
|
|
30
|
+
msg: string;
|
|
31
|
+
data?: T;
|
|
32
|
+
}
|
|
33
|
+
type IgnoredField = {
|
|
34
|
+
id: string;
|
|
35
|
+
name: string;
|
|
36
|
+
reason: string;
|
|
37
|
+
};
|
|
38
|
+
type WriteResult = {
|
|
39
|
+
status: "success";
|
|
40
|
+
appliedIds: string[];
|
|
41
|
+
notFoundIds: [];
|
|
42
|
+
ignoredFields: [];
|
|
43
|
+
attachments?: JsonValue;
|
|
44
|
+
noop?: boolean;
|
|
45
|
+
} | {
|
|
46
|
+
status: "partial";
|
|
47
|
+
appliedIds: string[];
|
|
48
|
+
notFoundIds: string[];
|
|
49
|
+
ignoredFields: IgnoredField[];
|
|
50
|
+
attachments?: JsonValue;
|
|
51
|
+
} | {
|
|
52
|
+
status: "failed";
|
|
53
|
+
code: string;
|
|
54
|
+
upstreamCode?: number;
|
|
55
|
+
retryable: false;
|
|
56
|
+
appliedIds: [];
|
|
57
|
+
} | {
|
|
58
|
+
status: "unknown";
|
|
59
|
+
code: string;
|
|
60
|
+
retryable: false;
|
|
61
|
+
requestedIds?: string[];
|
|
62
|
+
logid?: string;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
type CapabilityDescriptor = Readonly<{
|
|
66
|
+
version: number;
|
|
67
|
+
}>;
|
|
68
|
+
type CapabilityMap = Readonly<Record<string, CapabilityDescriptor>>;
|
|
69
|
+
type RuntimeCapabilities = CapabilityMap;
|
|
70
|
+
type CapabilityUnavailableReason = "permission-denied" | "region-unavailable" | "temporarily-unavailable";
|
|
71
|
+
interface CapabilityContext {
|
|
72
|
+
capabilities?: CapabilityMap;
|
|
73
|
+
capabilityUnavailable?: Readonly<Record<string, {
|
|
74
|
+
reason: CapabilityUnavailableReason;
|
|
75
|
+
retryable: boolean;
|
|
76
|
+
}>>;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
type CapabilityName = keyof typeof CAPABILITY_CATALOG;
|
|
80
|
+
interface CapabilityQueryResult {
|
|
81
|
+
name: string;
|
|
82
|
+
requiredVersion: number;
|
|
83
|
+
version: number | null;
|
|
84
|
+
supported: boolean;
|
|
85
|
+
source: "advertised" | "host-baseline";
|
|
86
|
+
availability: "available" | "unknown" | CapabilityUnavailableReason | "unsupported";
|
|
87
|
+
}
|
|
88
|
+
declare const DiagnosticCode: {
|
|
89
|
+
readonly HostUnsupported: "CAPABILITY_HOST_UNSUPPORTED";
|
|
90
|
+
readonly PermissionDenied: "CAPABILITY_PERMISSION_DENIED";
|
|
91
|
+
readonly RegionUnavailable: "CAPABILITY_REGION_UNAVAILABLE";
|
|
92
|
+
readonly TemporarilyUnavailable: "CAPABILITY_TEMPORARILY_UNAVAILABLE";
|
|
93
|
+
readonly ContextInvalid: "CAPABILITY_CONTEXT_INVALID";
|
|
94
|
+
readonly UnavailableUnknown: "CAPABILITY_UNAVAILABLE_UNKNOWN";
|
|
95
|
+
};
|
|
96
|
+
type DiagnosticCategory = "host-unsupported" | "permission-denied" | "region-unavailable" | "temporary" | "protocol" | "unknown";
|
|
97
|
+
interface RuntimeDiagnostic {
|
|
98
|
+
source: "sdk";
|
|
99
|
+
code: string;
|
|
100
|
+
upstreamCode?: string;
|
|
101
|
+
category: DiagnosticCategory;
|
|
102
|
+
retryable: boolean;
|
|
103
|
+
message: string;
|
|
104
|
+
redaction: "原始错误消息和业务参数已省略";
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
declare class CapabilityUnavailableError extends SDKError {
|
|
108
|
+
readonly capability: string;
|
|
109
|
+
readonly minimumVersion: number;
|
|
110
|
+
constructor(capability: string, minimumVersion: number);
|
|
111
|
+
}
|
|
112
|
+
/** 不解析 message 猜测原因;可恢复不代表写入可安全重试。 */
|
|
113
|
+
declare function classifyRuntimeError(error: unknown): RuntimeDiagnostic;
|
|
114
|
+
|
|
115
|
+
export { CapabilityContext, CapabilityDescriptor, CapabilityMap, CapabilityName, CapabilityQueryResult, CapabilityUnavailableError, CapabilityUnavailableReason, DiagnosticCategory, DiagnosticCode, FunctionEnvelope, JsonValue, RuntimeCapabilities, RuntimeDiagnostic, SDKError, SDKErrorOptions, WriteResult, classifyRuntimeError };
|
package/dist/shared.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
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};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { SiteRuntimeApi } from '@lark-base-open/base-site-channel/runtime-contract';
|
|
2
|
+
import { RuntimeChannel } from '@lark-base-open/base-site-channel/runtime';
|
|
3
|
+
import { S as SiteRuntimeContext } from './capabilities-56e403e7.js';
|
|
4
|
+
import { RuntimeCapabilities } from './shared.js';
|
|
5
|
+
import { HttpHandler } from 'msw';
|
|
6
|
+
import '@lark-base-open/base-site-channel/capabilities';
|
|
7
|
+
|
|
8
|
+
interface SiteHttpBridgeRoute {
|
|
9
|
+
method: string;
|
|
10
|
+
path: string;
|
|
11
|
+
}
|
|
12
|
+
interface SiteHttpBridgeAllowlistConfig {
|
|
13
|
+
routes: SiteHttpBridgeRoute[];
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Runtime 当前使用的全部父页面 HTTP bridge 路由。AppSettings 未配置或配置非法时回退到这里,
|
|
17
|
+
* 避免把 bridge 重新退化为任意同源请求代理。
|
|
18
|
+
*/
|
|
19
|
+
declare const DEFAULT_SITE_HTTP_BRIDGE_ALLOWLIST: SiteHttpBridgeAllowlistConfig;
|
|
20
|
+
|
|
21
|
+
interface HttpFixture {
|
|
22
|
+
method?: "get" | "post" | "put" | "patch" | "delete";
|
|
23
|
+
url: string;
|
|
24
|
+
status?: number;
|
|
25
|
+
body?: unknown;
|
|
26
|
+
headers?: Record<string, string>;
|
|
27
|
+
}
|
|
28
|
+
/** 仅替换真正的 HTTP;channel 的 postMessage 必须使用 createMockHost。 */
|
|
29
|
+
declare function createHttpHandlers(fixtures: readonly HttpFixture[]): HttpHandler[];
|
|
30
|
+
|
|
31
|
+
type MockApi = (typeof SiteRuntimeApi)[keyof typeof SiteRuntimeApi];
|
|
32
|
+
interface MockCall {
|
|
33
|
+
api: string;
|
|
34
|
+
params: unknown;
|
|
35
|
+
}
|
|
36
|
+
interface MockHandlerContext {
|
|
37
|
+
signal: AbortSignal;
|
|
38
|
+
emit: (event: string, payload: unknown) => Promise<void>;
|
|
39
|
+
}
|
|
40
|
+
interface MockHostOptions {
|
|
41
|
+
context?: Partial<SiteRuntimeContext>;
|
|
42
|
+
/** undefined 模拟未提供能力字段的宿主;{} 模拟明确无能力的新宿主。 */
|
|
43
|
+
capabilities?: RuntimeCapabilities;
|
|
44
|
+
bindings?: readonly {
|
|
45
|
+
baseToken: string;
|
|
46
|
+
tableIds: readonly string[];
|
|
47
|
+
}[];
|
|
48
|
+
allowedRoutes?: readonly SiteHttpBridgeRoute[];
|
|
49
|
+
origin?: string;
|
|
50
|
+
secureLinkAvailable?: boolean;
|
|
51
|
+
functions?: Record<string, (params: unknown) => unknown | Promise<unknown>>;
|
|
52
|
+
handlers?: Partial<Record<MockApi, (params: unknown, context: MockHandlerContext) => unknown | Promise<unknown>>>;
|
|
53
|
+
failures?: Partial<Record<MockApi, {
|
|
54
|
+
code: string;
|
|
55
|
+
message?: string;
|
|
56
|
+
}>>;
|
|
57
|
+
}
|
|
58
|
+
/** 无网络、无真实身份。校验始终先于配置的失败和 handler。 */
|
|
59
|
+
declare function createMockHost(options?: MockHostOptions): {
|
|
60
|
+
channel: RuntimeChannel;
|
|
61
|
+
context: SiteRuntimeContext;
|
|
62
|
+
calls: MockCall[];
|
|
63
|
+
events: {
|
|
64
|
+
event: string;
|
|
65
|
+
payload: unknown;
|
|
66
|
+
}[];
|
|
67
|
+
emit: (event: string, payload: unknown) => Promise<void>;
|
|
68
|
+
/** 真实 postMessage 传输使用 channel Host 的 origin/source 校验,非法消息无回包。 */
|
|
69
|
+
mount(container: HTMLElement, url: string, allowedOrigin?: string): () => void;
|
|
70
|
+
emitStreamMessage(subscriptionId: string, data: unknown, seq: number, ai?: boolean): Promise<void>;
|
|
71
|
+
dispose(): void;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export { DEFAULT_SITE_HTTP_BRIDGE_ALLOWLIST, HttpFixture, MockApi, MockCall, MockHandlerContext, MockHostOptions, createHttpHandlers, createMockHost };
|
package/dist/testing.js
ADDED
|
@@ -0,0 +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 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/dist/theme.css
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
@layer base-app {
|
|
2
|
+
.base-app-platform {
|
|
3
|
+
--base-app-color-bg: #fff;
|
|
4
|
+
--base-app-color-fg: #1f2329;
|
|
5
|
+
--base-app-color-muted: #646a73;
|
|
6
|
+
--base-app-color-accent: #1456f0;
|
|
7
|
+
--base-app-color-danger: #c6262e;
|
|
8
|
+
--base-app-color-border: #dee0e3;
|
|
9
|
+
--base-app-radius: 8px;
|
|
10
|
+
--base-app-font-family: system-ui, sans-serif;
|
|
11
|
+
--base-app-font-size: 14px;
|
|
12
|
+
--base-app-z-popover: 1000;
|
|
13
|
+
--base-app-z-floating: 900;
|
|
14
|
+
--base-app-shadow: 0 4px 20px #0002;
|
|
15
|
+
color: var(--base-app-color-fg);
|
|
16
|
+
font: var(--base-app-font-size) / 1.5 var(--base-app-font-family);
|
|
17
|
+
box-sizing: border-box;
|
|
18
|
+
}
|
|
19
|
+
.base-app-platform[data-theme="dark"],
|
|
20
|
+
[data-theme="dark"] .base-app-platform {
|
|
21
|
+
--base-app-color-bg: #22252b;
|
|
22
|
+
--base-app-color-fg: #f4f5f7;
|
|
23
|
+
--base-app-color-muted: #b3b7c0;
|
|
24
|
+
--base-app-color-border: #515660;
|
|
25
|
+
--base-app-color-accent: #7da6ff;
|
|
26
|
+
}
|
|
27
|
+
@media (prefers-color-scheme: dark) {
|
|
28
|
+
.base-app-platform[data-theme="host"] {
|
|
29
|
+
--base-app-color-bg: #22252b;
|
|
30
|
+
--base-app-color-fg: #f4f5f7;
|
|
31
|
+
--base-app-color-muted: #b3b7c0;
|
|
32
|
+
--base-app-color-border: #515660;
|
|
33
|
+
--base-app-color-accent: #7da6ff;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
.base-app-platform button {
|
|
37
|
+
font: inherit;
|
|
38
|
+
color: inherit;
|
|
39
|
+
background: var(--base-app-color-bg);
|
|
40
|
+
border: 1px solid var(--base-app-color-border);
|
|
41
|
+
border-radius: var(--base-app-radius);
|
|
42
|
+
padding: 6px 10px;
|
|
43
|
+
cursor: pointer;
|
|
44
|
+
}
|
|
45
|
+
.base-app-platform button:focus-visible,
|
|
46
|
+
.base-app-platform input:focus-visible {
|
|
47
|
+
outline: 2px solid var(--base-app-color-accent);
|
|
48
|
+
outline-offset: 2px;
|
|
49
|
+
}
|
|
50
|
+
.base-app-popover {
|
|
51
|
+
background: var(--base-app-color-bg);
|
|
52
|
+
border: 1px solid var(--base-app-color-border);
|
|
53
|
+
border-radius: var(--base-app-radius);
|
|
54
|
+
box-shadow: var(--base-app-shadow);
|
|
55
|
+
padding: 8px;
|
|
56
|
+
width: 280px;
|
|
57
|
+
max-width: calc(100vw - 16px);
|
|
58
|
+
max-height: var(--radix-popover-content-available-height);
|
|
59
|
+
overflow: auto;
|
|
60
|
+
z-index: var(--base-app-z-popover);
|
|
61
|
+
}
|
|
62
|
+
.base-app-editor {
|
|
63
|
+
display: flex;
|
|
64
|
+
gap: 4px;
|
|
65
|
+
align-items: center;
|
|
66
|
+
}
|
|
67
|
+
.base-app-editor > button:first-child {
|
|
68
|
+
flex: 1;
|
|
69
|
+
text-align: start;
|
|
70
|
+
}
|
|
71
|
+
.base-app-editor span + span {
|
|
72
|
+
margin-inline-start: 8px;
|
|
73
|
+
}
|
|
74
|
+
.base-app-selector {
|
|
75
|
+
background: var(--base-app-color-bg);
|
|
76
|
+
min-width: 0;
|
|
77
|
+
}
|
|
78
|
+
.base-app-selector [cmdk-input] {
|
|
79
|
+
box-sizing: border-box;
|
|
80
|
+
width: 100%;
|
|
81
|
+
padding: 8px;
|
|
82
|
+
border: 1px solid var(--base-app-color-border);
|
|
83
|
+
border-radius: var(--base-app-radius);
|
|
84
|
+
background: var(--base-app-color-bg);
|
|
85
|
+
color: inherit;
|
|
86
|
+
font: inherit;
|
|
87
|
+
}
|
|
88
|
+
.base-app-selector [cmdk-list] {
|
|
89
|
+
max-height: 240px;
|
|
90
|
+
overflow: auto;
|
|
91
|
+
margin-top: 6px;
|
|
92
|
+
}
|
|
93
|
+
.base-app-selector [cmdk-item] {
|
|
94
|
+
display: flex;
|
|
95
|
+
align-items: center;
|
|
96
|
+
gap: 8px;
|
|
97
|
+
padding: 8px;
|
|
98
|
+
border-radius: var(--base-app-radius);
|
|
99
|
+
cursor: pointer;
|
|
100
|
+
}
|
|
101
|
+
.base-app-selector [cmdk-item][data-selected="true"] {
|
|
102
|
+
background: color-mix(
|
|
103
|
+
in srgb,
|
|
104
|
+
var(--base-app-color-accent) 16%,
|
|
105
|
+
transparent
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
.base-app-selector small {
|
|
109
|
+
color: var(--base-app-color-muted);
|
|
110
|
+
}
|
|
111
|
+
.base-app-selector [role="status"] {
|
|
112
|
+
padding: 12px;
|
|
113
|
+
color: var(--base-app-color-muted);
|
|
114
|
+
}
|
|
115
|
+
.base-app-floating {
|
|
116
|
+
position: fixed;
|
|
117
|
+
bottom: 24px;
|
|
118
|
+
right: 24px;
|
|
119
|
+
z-index: var(--base-app-z-floating);
|
|
120
|
+
display: flex;
|
|
121
|
+
gap: 4px;
|
|
122
|
+
}
|
|
123
|
+
.base-app-floating[data-position="bottom-left"] {
|
|
124
|
+
left: 24px;
|
|
125
|
+
right: auto;
|
|
126
|
+
}
|
|
127
|
+
.base-app-floating [role="tooltip"],
|
|
128
|
+
.base-app-floating [role="alert"] {
|
|
129
|
+
position: absolute;
|
|
130
|
+
bottom: 100%;
|
|
131
|
+
width: 220px;
|
|
132
|
+
background: var(--base-app-color-bg);
|
|
133
|
+
padding: 8px;
|
|
134
|
+
border-radius: var(--base-app-radius);
|
|
135
|
+
box-shadow: var(--base-app-shadow);
|
|
136
|
+
}
|
|
137
|
+
.base-app-notice {
|
|
138
|
+
position: fixed;
|
|
139
|
+
top: 20px;
|
|
140
|
+
left: 50%;
|
|
141
|
+
transform: translateX(-50%);
|
|
142
|
+
background: var(--base-app-color-bg);
|
|
143
|
+
padding: 12px;
|
|
144
|
+
z-index: var(--base-app-z-floating);
|
|
145
|
+
}
|
|
146
|
+
.base-app-user-menu {
|
|
147
|
+
display: inline-flex;
|
|
148
|
+
align-items: center;
|
|
149
|
+
gap: 8px;
|
|
150
|
+
}
|
|
151
|
+
.base-app-user-menu img {
|
|
152
|
+
width: 28px;
|
|
153
|
+
height: 28px;
|
|
154
|
+
border-radius: 50%;
|
|
155
|
+
}
|
|
156
|
+
.base-app-attachment img {
|
|
157
|
+
max-width: 100%;
|
|
158
|
+
}
|
|
159
|
+
.base-app-platform [role="alert"] {
|
|
160
|
+
color: var(--base-app-color-danger);
|
|
161
|
+
}
|
|
162
|
+
@media (prefers-reduced-motion: reduce) {
|
|
163
|
+
.base-app-platform * {
|
|
164
|
+
animation: none !important;
|
|
165
|
+
transition: none !important;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
package/dist/vite.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Server, IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
+
|
|
3
|
+
/** Only server-side environment receives the token. Never inject it into Vite defines. */
|
|
4
|
+
declare function baseAppDevelopmentPairingPlugin(): {
|
|
5
|
+
name: string;
|
|
6
|
+
apply: "serve";
|
|
7
|
+
configureServer(server: {
|
|
8
|
+
httpServer: Server | null;
|
|
9
|
+
middlewares: {
|
|
10
|
+
use(handler: (req: IncomingMessage, res: ServerResponse, next: () => void) => void): void;
|
|
11
|
+
};
|
|
12
|
+
}): void;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export { baseAppDevelopmentPairingPlugin };
|
package/dist/vite.js
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{a as r}from"./chunk-OHPYKFCM.js";import{createHmac as h}from"crypto";function I(){let i=process.env[r.tokenEnv];return{name:"base-app-development-pairing",apply:"serve",configureServer(c){if(i){if(!/^[A-Za-z0-9_-]{43}$/.test(i))throw new Error("Invalid development pairing token");c.middlewares.use((e,o,m)=>{if(e.url?.split("?")[0]!==r.endpoint)return m();let n=(t,f)=>{for(let g of o.getHeaderNames())g.toLowerCase().startsWith("access-control-")&&o.removeHeader(g);o.setHeader("Cache-Control","no-store"),o.setHeader("Content-Type","application/json"),o.statusCode=t,o.end(JSON.stringify(f))},a=c.httpServer?.address(),s=a&&typeof a!="string"?a.port:0,l=`http://${r.host}:${s}`,u=e.headers.origin===void 0?e.headers["sec-fetch-site"]==="same-origin":e.headers.origin===l;if(s<r.portMin||s>r.portMax||e.headers.host!==`${r.host}:${s}`||!u)return n(403,{error:"PAIRING_ORIGIN_REJECTED"});if(e.method!=="POST")return n(405,{error:"PAIRING_METHOD_REJECTED"});let p="",d=!1;e.on("data",t=>{d||(p+=t.toString(),p.length>1024&&(d=!0,n(413,{error:"PAIRING_BODY_TOO_LARGE"})))}),e.on("end",()=>{if(!d)try{let{challenge:t}=JSON.parse(p);if(typeof t!="string"||!/^[A-Za-z0-9_-]{43}$/.test(t))return n(400,{error:"PAIRING_CHALLENGE_INVALID"});let f=h("sha256",i).update(`${r.proofPrefix}
|
|
2
|
+
${l}
|
|
3
|
+
${t}`).digest("base64url");n(200,{proof:f})}catch{n(400,{error:"PAIRING_CHALLENGE_INVALID"})}})})}}}}export{I as baseAppDevelopmentPairingPlugin};
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# SDK 文档索引
|
|
2
|
+
|
|
3
|
+
本版本提供页面与服务端 SDK、平台组件、启动管理及测试替身。没有根入口;以 [完整导出清单](exports.md) 和已安装包的 dist 声明为准。
|
|
4
|
+
|
|
5
|
+
- [page:页面运行时、附件与兼容调用](page.md)
|
|
6
|
+
- [server:defineFunction、ctx.records 与结果语义](server.md)
|
|
7
|
+
- [shared:通用类型与诊断](shared.md)
|
|
8
|
+
- [testing:模拟宿主与 MSW](testing.md)
|
|
9
|
+
- [components:平台组件、主题与插槽](components.md)
|
|
10
|
+
- [start-base-app:启动、清理与 Vite 开发插件](start-base-app.md)
|
|
11
|
+
- [capabilities:能力协商与诊断](capabilities.md)
|
|
12
|
+
- [compat:老模板兼容](compat.md)
|
|
13
|
+
- [upgrade:codemod 与模板 1→2 迁移](upgrade.md)
|
|
14
|
+
- [可编译示例](../examples)
|
|
15
|
+
|
|
16
|
+
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 为准。
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# 能力与诊断
|
|
2
|
+
|
|
3
|
+
宿主字段形状为 `capabilities: { [name: string]: { version: number } }`。只有缺字段才使用下面的 `host-baseline` 宿主基线;显式空表不回退。null、数组或非正安全整数版本抛 CAPABILITY_CONTEXT_INVALID;未知能力和过低版本不支持。能力声明不代表用户有权限,也不保证依赖服务当前可用。
|
|
4
|
+
|
|
5
|
+
| 能力 | 分类 | 缺字段时 host-baseline(版本 1) |
|
|
6
|
+
|---|---|---|
|
|
7
|
+
| runtime.context | 关键 | 是 |
|
|
8
|
+
| http.request | 关键 | 是 |
|
|
9
|
+
| http.binary | 关键 | 是 |
|
|
10
|
+
| http.cancel | 可选 | 是 |
|
|
11
|
+
| function.invoke | 关键 | 是 |
|
|
12
|
+
| runtime.user | 可选 | 是 |
|
|
13
|
+
| runtime.[`logout`](exports.md#page) | 可选 | 是 |
|
|
14
|
+
| runtime.[`requestEdit`](exports.md#page) | 可选 | 是 |
|
|
15
|
+
| attachment.upload | 关键 | 是 |
|
|
16
|
+
| attachment.cancel | 可选 | 是 |
|
|
17
|
+
| chatbot.stream | 关键 | 是 |
|
|
18
|
+
| ai.function.stream | 关键 | 是 |
|
|
19
|
+
| runtime.lifecycle | 可选 | 是 |
|
|
20
|
+
| runtime.mode | 可选 | 是 |
|
|
21
|
+
| attachment.progress | 可选 | 是 |
|
|
22
|
+
| link.open | 可选 | 是 |
|
|
23
|
+
|
|
24
|
+
宿主集中部署;缺少 capabilities 字段仅代表能力协商尚未上线,不代表缺少外链或进度能力。基线以包内 capabilities.json 的能力清单为准,由宿主注册与事件实现的回归测试验证。服务端 ctx.records 不属于页面能力协商;未知能力不在基线。[`queryCapability`](exports.md#page) 的 source 此时为 `host-baseline`;显式能力表为 `advertised`。
|
|
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
|
+
|
|
27
|
+
<!-- example: capabilities.ts -->
|
|
28
|
+
```ts
|
|
29
|
+
import { getRuntimeCapabilities, requireCapability, classifyRuntimeError } from '@lark-base-open/base-database-app-sdk/page';
|
|
30
|
+
export async function inspectHost() {
|
|
31
|
+
const capabilities = await getRuntimeCapabilities();
|
|
32
|
+
try { await requireCapability('function.invoke'); }
|
|
33
|
+
catch (error) { return { capabilities, diagnostic: classifyRuntimeError(error) }; }
|
|
34
|
+
return { capabilities };
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
诊断分类:host-unsupported(CAPABILITY_HOST_UNSUPPORTED)、permission-denied(CAPABILITY_PERMISSION_DENIED)、region-unavailable(CAPABILITY_REGION_UNAVAILABLE)、temporary(CAPABILITY_TEMPORARILY_UNAVAILABLE)、protocol(CAPABILITY_CONTEXT_INVALID)、unknown(CAPABILITY_UNAVAILABLE_UNKNOWN)。
|
|
39
|
+
分类只用稳定错误码,不解析含业务内容的 message。LINK_CHECK_UNAVAILABLE 表示服务不可用,分类为 unknown,不能据此断言地域政策。宿主可用 companion 字段 capabilityUnavailable 明确 permission-denied/region-unavailable/temporarily-unavailable;[`requireCapability`](exports.md#page) 会根据该原因拒绝。unknown 不猜测也不自动重试;retryable 提示不保证非幂等写入重试安全。
|
|
40
|
+
输出含 source/code/upstreamCode/category/retryable/message/redaction;不附原始异常、请求参数、URL、身份、token。应用、环境、制品版本与 requestId/logid 由持有可信上下文的诊断汇总端补齐,本模块不伪造关联信息。
|
|
41
|
+
|
|
42
|
+
结构化清单位于包内 capabilities.json,测试保证它与共享 [`CAPABILITY_CATALOG`](exports.md#page) 一致。无缓存实现每次读取当前 channel 上下文,避免跨 mount/用户/应用泄漏旧状态。基线包含 link.open 与 attachment.progress;支持进度事件不代表每次上传都必定出现回调或保证服务当前可用。
|
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 比较文件内容。此机制不是面向任意恶意原生程序的通用沙箱。
|