@tbox.cn/app-toolkit 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/{chunk-VV3ERZXS.js → chunk-KPD3LUH4.js} +1 -1
- package/dist/contracts-resolver-E4SECBOG.js +1 -0
- package/dist/index.d.ts +157 -32
- package/dist/index.js +7 -7
- package/package.json +1 -1
- package/src/assembly/provider-catalog.ts +6 -1
- package/src/assembly/walk-manifests.ts +2 -1
- package/src/core/contracts-resolver.ts +3 -2
- package/src/core/credential-format.ts +4 -2
- package/src/core/errors.ts +9 -3
- package/src/dto.ts +81 -11
- package/src/factory.ts +84 -13
- package/src/index.ts +13 -4
- package/src/integrations/domain-binding.ts +81 -0
- package/src/integrations/predicate.ts +5 -5
- package/src/integrations/read.ts +23 -3
- package/src/integrations/write.ts +78 -23
- package/src/views/app.ts +7 -5
- package/src/views/context.ts +3 -6
- package/src/views/credential-types.ts +28 -0
- package/src/views/credentials.ts +57 -0
- package/src/views/integration-schemas.ts +53 -0
- package/src/views/modules.ts +15 -13
- package/src/views/providers.ts +18 -9
- package/src/views/service-detail.ts +3 -4
- package/src/views/service-resolutions.ts +31 -25
- package/tests/credentials-view.test.ts +152 -0
- package/tests/demo-app.ts +14 -1
- package/tests/naming-alignment.test.ts +8 -5
- package/tests/vendor-schema-keywords.test.ts +101 -0
- package/tests/views.test.ts +414 -10
- package/tests/write-core.test.ts +81 -4
- package/dist/contracts-resolver-QM4FBQQV.js +0 -1
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import{a as
|
|
2
|
-
`,"utf8"),a}import{mkdirSync as Tn,renameSync as Nn,writeFileSync as Pn}from"fs";import{dirname as Dn}from"path";function $(e,n){Ue(Dn(e));let r=`${e}.tmp-${process.pid}-${Date.now()}`;Pn(r,n,"utf8"),Nn(r,e)}function Ue(e){Tn(e,{recursive:!0})}function ue(e){if(!e||typeof e!="object")return!1;let n=Object.keys(e),r=new Set(["provider","credentialType","configSchema"]);if(!n.every(a=>r.has(a)))return!1;let t=e;return typeof t.provider=="string"&&t.provider.length>0&&typeof t.credentialType=="string"&&t.credentialType.length>0&&(t.configSchema===void 0||typeof t.configSchema=="string")}function Je(e,n,r){return e==="*"||r.has(e)?null:{severity:"error",code:"INSTANCE_KEY_NOT_REGISTERED",instance:e,message:`integrations: ${n} 键 ${e} 不在 malls 白名单内(${[...r].join(", ")})`}}function pe(e,n){let r=new Set(n),t=[],a=(i,o)=>{let g=Je(i,o,r);g&&t.push(g)};for(let i of Object.keys(e.configByInstance??{}))a(i,"configByInstance");for(let i of Object.keys(e.credentialRefByInstance??{}))a(i,"credentialRefByInstance");for(let[i,o]of Object.entries(e.services??{}))for(let g of Object.keys(o.instances??{}))a(g,`services.${i}.instances`);return t}function X(e){let n=[],r=e.contracts,{integrations:t,suppliedProviders:a,demandedServices:i,supplies:o,malls:g,credentialSchemaMissing:d,vendorConfigKeys:p,vendorConfigSchemaMissing:m,credentialFileMissing:v,credentialFileType:f,realmSwitchHints:u,moduleResourceNeeds:k}=e,s=g?new Set(g):void 0;for(let c of u??[])n.push({severity:"warning",code:"REALM_SWITCH_HINT",message:c});let l=new Set(Object.keys(a).map(c=>c.slice(0,c.indexOf("\0"))).filter(c=>c.length>0)),h=(c,S,b,I)=>{if(typeof b?.provider=="string"&&typeof b.implementation=="string"&&b.provider&&b.implementation)return a[Z(b.provider,b.implementation)]?.credentialType;if(typeof S.provider=="string"&&typeof S.implementation=="string")return a[Z(S.provider,S.implementation)]?.credentialType;let A=S.provider?.credentialType;if(typeof A=="string")return A;if(!o)return;let x=I??Object.keys(S.instances??{})[0]??"",E=r.resolveEffectiveService(t,c,x,o);if(!(E.status!=="ok"||typeof E.effective.provider!="string"))return a[Z(E.effective.provider,E.effective.implementation)]?.credentialType};if(v||f)for(let[c,S]of Object.entries(t.services))for(let[b,I]of Object.entries(S.instances??{})){if(I.enabled===!1)continue;let A=I.credentialRef??S.credentialRef??t.credentialRefByInstance?.[b]??t.credentialRef;if(typeof A!="string"||!A.startsWith("secret://"))continue;let x=A.slice(9);v?.[x]===!0&&n.push({severity:"error",code:"CREDENTIAL_FILE_MISSING",service:c,instance:b,message:`integrations: 凭据文件缺失 ${x}(${c}@${b} effective ref=${A};档内 config/credentials/ 无此文件)`});let E=f?.[x];if(E){let N=h(c,S,I,b);N&&E!==N&&n.push({severity:"error",code:"CREDENTIAL_TYPE_MISMATCH",service:c,instance:b,message:`integrations: 凭据文件 ${x}.json type=${E} 与绑定 credentialType ${N} 不一致(${c}@${b}——transport 创建期将按 type 选策略)`})}}let C=(c,S)=>{if(!s)return;let b=Je(c,S,s);b&&n.push(b)};for(let[c]of Object.entries(t.configByInstance??{}))C(c,"configByInstance");for(let[c]of Object.entries(t.credentialRefByInstance??{}))C(c,"credentialRefByInstance");let j=Object.entries(t.services).filter(([,c])=>c.instances==null||c.instances["*"]!=null).map(([c])=>c);if(j.length>0){let c=Object.entries(t.services).filter(([,S])=>S.instances!=null&&S.instances["*"]==null).map(([S])=>S);c.length>0&&n.push({severity:"warning",code:"WILDCARD_ALIGNMENT",message:`integrations: 激活形态不对称(通配:${j.join(", ")};显式键集:${c.join(", ")})——动态 mallId 将在显式键集槽上 INSTANCE_NOT_FOUND(provider 业务限制表达位),确认是否齐步`})}let R=r.normalizeInstances({instances:t.instances,defaultInstanceId:t.defaultInstanceId});R.instances.length>=2&&R.defaultInstanceId===null&&n.push({severity:"warning",code:"MULTI_INSTANCE_NO_DEFAULT",message:`integrations: ${R.instances.length} 个部署实例(${R.instances.map(c=>c.id).join(", ")})均无 defaultInstanceId——无 scope 参数访问将落全局会话(默认商圈不注入)`});for(let[c,S]of Object.entries(t.services)){if(i[c]||n.push({severity:"warning",code:"SLOT_NOT_DECLARED",service:c,message:`integrations: 服务槽 ${c} 无消费方声明(拼写错误或需求方未安装)——若为拆分前旧槽位词汇(member.main/parking.main/promotion.read/shopping-guide.main),参照 cookbook·app-config 槽位迁移配方展开为新槽`}),typeof S.provider=="string"?typeof S.implementation=="string"&&!a[Z(S.provider,S.implementation)]&&n.push({severity:"error",code:"INSTANCE_SUPPLY_UNMATCHED",service:c,message:`integrations: provider ${S.provider}/${S.implementation} 未命中已装供给(${c} 槽级)——装对应 provider 包或改绑`}):S.provider!==void 0&&!ue(S.provider)&&n.push({severity:"error",code:"INLINE_PROVIDER_INVALID",service:c,message:`integrations: 内联 provider 超受控形状(${c} 槽级;允许键 provider/credentialType/configSchema)`}),d){let I=h(c,S);I&&d[I]===!0&&n.push({severity:"error",code:"SCHEMA_FILE_MISSING",service:c,message:`integrations: credentialType ${I} 的 schema 引用不存在(${c};manifest 声明指向缺失文件)`})}typeof S.provider=="object"&&typeof S.implementation!="string"&&n.push({severity:"error",code:"IMPL_WITHOUT_PROVIDER",service:c,message:`integrations: 内联厂商声明必须同时提供 implementation(${c} 槽级)`});for(let[I,A]of Object.entries(S.instances??{})){C(I,`services.${c}.instances`);let x=A.provider!==void 0,E=A.implementation!==void 0;if(E&&!x&&n.push({severity:"error",code:"IMPL_WITHOUT_PROVIDER",service:c,instance:I,message:`integrations: 实例级 implementation 需伴随 provider(${c}@${I}——impl-only 禁止,provider 是绑定组头)`}),x&&E){let N=A.provider,V=A.implementation;typeof N=="string"&&typeof V=="string"?a[Z(N,V)]||n.push({severity:"error",code:"INSTANCE_SUPPLY_UNMATCHED",service:c,instance:I,message:`integrations: 实例级 provider 覆盖 ${N}/${V} 未命中已装供给(${c}@${I})`}):ue(N)||n.push({severity:"error",code:"INLINE_PROVIDER_INVALID",service:c,instance:I,message:`integrations: 实例级内联 provider 超受控形状(${c}@${I})`})}}}for(let[c,S]of[["root",t.provider],...Object.entries(t.domains??{}).map(([b,I])=>[`domains.${b}`,I?.provider])])typeof S!="string"||S.length===0||l.has(S)||n.push({severity:"error",code:"VENDOR_NOT_INSTALLED",module:c.startsWith("domains.")?c.slice(8):void 0,message:`integrations: ${c} provider ${S} 未命中已装供给——装对应 provider 包或 provider use 改绑`});if(o)for(let[c,S]of Object.entries(t.services)){let b=S.instances?Object.keys(S.instances):[""];for(let I of b){let A=r.resolveEffectiveService(t,c,I,o);A.status==="binding-unresolved"&&n.push({severity:"error",code:"BINDING_UNRESOLVED",service:c,instance:I||void 0,message:`integrations: ${A.message}`})}}for(let[c,S]of Object.entries(t.services))S.implementation!==void 0&&S.provider===void 0&&n.push({severity:"error",code:"IMPL_WITHOUT_PROVIDER",service:c,message:`integrations: 槽级 implementation 需伴随 provider(${c}——impl-only 禁止,provider 是绑定组头)`});let w=new Set([...Object.keys(t.services).map(c=>r.moduleDomainOf(c)),...Object.keys(i).map(c=>r.moduleDomainOf(c))]);for(let c of Object.keys(t.domains??{}))w.has(c)||n.push({severity:"warning",code:"DOMAIN_KEY_UNMATCHED",module:c,message:`integrations: domains.${c} 无对应槽位或需求声明(域前缀拼写错误?——键 = 槽位 id 点分前缀)`});if(p){let c=new Set,S=!1,b=I=>{typeof I!="string"||I.length===0||(p[I]&&c.add(I),!p[I]&&!m?.[I]&&(S=!0),m?.[I]&&(S=!0))};b(t.provider);for(let I of Object.values(t.domains??{}))b(I?.provider);for(let I of Object.values(t.services)){let A=I.provider;b(typeof I.provider=="string"?I.provider:A?.provider)}if(c.size>0&&!S){let I=new Set;for(let x of c)for(let E of p[x]??[])I.add(E);let A=(x,E,N)=>{for(let[V]of Object.entries(x??{}))(N??I).has(V)||n.push({severity:"error",code:"DOMAIN_CONFIG_INVALID",message:`integrations: ${E} 键 ${V} 不在引用厂商(${[...c].join("/")})任何槽 schema properties 内(死配置)`})};A(t.config,"root.config");for(let[x,E]of Object.entries(t.configByInstance??{}))A(E,`root.configByInstance[${x}]`);for(let[x,E]of Object.entries(t.domains??{})){if(!E?.config)continue;let N=typeof E.provider=="string"&&E.provider?E.provider:typeof t.provider=="string"?t.provider:void 0;if(N===void 0){A(E.config,`domains.${x}.config`);continue}let V=p[N];!V||m?.[N]||A(E.config,`domains.${x}.config`,new Set(V))}}}for(let[c,S]of Object.entries(i)){let b=t.services[c];if(!b){n.push({severity:"warning",code:"SLOT_NOT_CONFIGURED",service:c,message:`integrations: 服务槽 ${c}(需求方 ${S.owners.join(", ")})未配置${S.optional?"(可选槽)":"(必需槽——模块将走降级链)"}`});continue}let I=Object.entries(b.instances??{}).filter(([,A])=>A.enabled===!1).map(([A])=>A);I.length>0&&n.push({severity:"warning",code:"INSTANCE_DISABLED",service:c,instance:I.join(", "),message:`integrations: ${c} 实例禁用(${I.join(", ")})`})}for(let[c,S]of Object.entries(t.modules??{})){let b=k?.[c];k&&!(c in k)&&n.push({severity:"warning",code:"RESOURCE_NOT_DECLARED",module:c,message:`integrations: modules.${c} 未命中已装模块(键 = install id——检查 .tbox/app.json entry.id / packages/ 目录名)`});for(let[I,A]of Object.entries(S?.resources??{})){let x=A?.type;(typeof x!="string"||!r.RESOURCE_TYPE_IDS.includes(x))&&n.push({severity:"error",code:"RESOURCE_TYPE_UNKNOWN",module:c,message:`integrations: modules.${c}.resources.${I} type 非法(${String(x)};词汇表 = ${r.RESOURCE_TYPE_IDS.join("/")})`}),b&&b.size>0&&!b.has(I)&&n.push({severity:"warning",code:"RESOURCE_NOT_DECLARED",module:c,message:`integrations: modules.${c}.resources.${I} 未被模块 manifest contributes.resources 声明`})}}return n}function Z(e,n){return`${e}\0${n}`}import{existsSync as fe,readFileSync as Vn,readdirSync as $n}from"fs";import{join as ee}from"path";function Ze(e){return e.replaceAll("-","_").toUpperCase()}function Ye(e){if(!fe(e))return null;try{let n=JSON.parse(Vn(e,"utf8"));return n&&typeof n=="object"?n:null}catch{return null}}function _n(e){let n=new Set,r=e.properties;if(r&&typeof r=="object"&&!Array.isArray(r))for(let t of Object.keys(r))n.add(t);return n}function ge(e,n,r,t=process.env){let a={};for(let[R,w]of Object.entries(n.catalog.providers))for(let[c,S]of Object.entries(w))a[`${R}\0${c}`]={services:[...S.services],owner:S.owner,credentialType:S.credentialType};let i={};for(let[R,w]of Object.entries(n.catalog.services))i[R]={optional:w.optional,owners:[...w.owners]};let o={},g={},d={},p={};for(let R of n.modules){p[R.id]=new Set((R.descriptor.contributes.resources??[]).map(w=>w.id));for(let w of R.descriptor.contributes.providers?.slots??[]){if(w.configSchema){let c=Ye(ee(R.dir,w.configSchema));if(c){let S=o[w.provider]??=new Set;for(let b of _n(c))S.add(b)}else g[w.provider]=!0}w.credentialSchema&&!fe(ee(R.dir,w.credentialSchema))&&(d[w.credentialType]=!0)}}let m={},v=new Set,f=ee(e,"config","credentials");if(fe(f))for(let R of $n(f)){if(!R.endsWith(".json"))continue;let w=R.slice(0,-5);v.add(w);let c=Ye(ee(f,R));c&&typeof c.type=="string"&&(m[w]=c.type)}let u=r,k=[],s=R=>{typeof R=="string"&&R.startsWith("secret://")&&k.push(R)};for(let R of Object.values(u.services??{}))if(R&&typeof R=="object"){s(R.credentialRef);for(let w of Object.values(R.instances??{}))w&&typeof w=="object"&&s(w.credentialRef)}for(let R of Object.values(u.credentialRefByInstance??{}))s(R);s(u.credentialRef);let l={};for(let R of k){let w=R.slice(9);t[Ze(w)]===void 0&&(v.has(w)||(l[w]=!0))}let h=[];for(let[R,w]of Object.entries(u.services??{})){let c=w&&typeof w=="object"&&typeof w.provider=="string"?w.provider:void 0;for(let[S,b]of Object.entries((w&&typeof w=="object"?w.instances:void 0)??{})){let I=b&&typeof b=="object"&&typeof b.provider=="string"?b.provider:void 0;c&&I&&I!==c&&h.push(`integrations: ${R}@${S} 实现覆盖跨厂商(${c} → ${I})——凭据域切换,确认该 mall 对应凭据在场`)}}let j=((Array.isArray(u.instances)?u.instances:void 0)??[]).map(R=>R&&typeof R=="object"&&typeof R.id=="string"?R.id:void 0).filter(R=>typeof R=="string");return{suppliedProviders:a,demandedServices:i,credentialFileMissing:l,credentialFileType:m,credentialSchemaMissing:d,vendorConfigKeys:o,vendorConfigSchemaMissing:g,realmSwitchHints:h,moduleResourceNeeds:p,...j.length>0?{malls:j}:{}}}import{existsSync as D,readdirSync as ve,readFileSync as B}from"fs";import{join as T}from"path";import{existsSync as Fn,readFileSync as He,mkdirSync as Ln,writeFileSync as Wn}from"fs";import Ge from"path";function ne(e){return Ge.join(e,".tbox","app.json")}function qe(e){let n=ne(e);if(!Fn(n))return null;try{let r=JSON.parse(He(n,"utf8")),t=r?.templateVersion;return typeof t!="string"||t.trim().length===0?null:{templateVersion:t.trim(),npmModules:Array.isArray(r?.npmModules)?r.npmModules:[]}}catch{return null}}function Bn(e,n){let r=ne(e);Ln(Ge.dirname(r),{recursive:!0}),Wn(r,`${JSON.stringify(n,null,2)}
|
|
3
|
-
`,"utf8")}function me(e){let n=qe(e);if(n===null)return null;let r=ne(e);try{let t=JSON.parse(He(r,"utf8"));return{...n,miniappContainers:Array.isArray(t?.miniappContainers)?t.miniappContainers:[]}}catch{return{...n,miniappContainers:[]}}}function zn(e,n){let r=e.npmModules.filter(t=>t.id!==n.id);return{...e,npmModules:[...r,n]}}function Kn(e,n){return{...e,npmModules:e.npmModules.filter(r=>r.id!==n)}}function Un(e,n){let r=(e.miniappContainers??[]).filter(t=>t.id!==n.id);return{...e,miniappContainers:[...r,n]}}function Jn(e,n,r){return{...e,npmModules:e.npmModules.map(t=>t.id===n?{...t,...r}:t)}}function Qe(e){return/^[a-z0-9]+(-[a-z0-9]+)*$/.test(e)&&!e.includes("..")}function Yn(e,n){if(!Qe(e))throw new Error(`非法模块 id "${e}"(来自 ${n}):仅允许小写字母/数字/中划线`)}function te(e,n){let r=t=>{try{let a=JSON.parse(t),i=J(a);if(i.ok)return{kind:"strict",name:i.value.name,version:i.value.version,descriptor:i.value,contributes:i.value.contributes};if(a&&typeof a=="object"&&"contributes"in a){let o=a;return{kind:"loose",...typeof o.name=="string"?{name:o.name}:{},contributes:{providers:o.contributes?.providers,services:o.contributes?.services}}}return null}catch{return null}};if(n)return n.read(e,r)??null;if(!D(e))return null;try{return r(B(e,"utf8"))}catch{return null}}function Zn(e){let n=T(e,".tbox","app.json");if(!D(n))return[];try{let r=JSON.parse(B(n,"utf8"));return Array.isArray(r.npmModules)?r.npmModules.filter(t=>!!t&&typeof t=="object"&&typeof t.id=="string"&&typeof t.package=="string"):[]}catch{return[]}}function Hn(e){let n=T(e,".tbox-dev.local"),r=T(n,"dev-manifest.json");if(!D(r))return[];try{let t=JSON.parse(B(r,"utf8")),a=[];for(let[i,o]of Object.entries(t.redirects??{}))typeof o!="string"||!i.startsWith("@tbox.cn/app-")||a.push({pkg:i,dir:T(n,o)});return a}catch{return[]}}function Gn(e,n){let r=[],t=[e];for(let i of["apps","packages"]){let o=T(e,i);if(D(o))try{for(let g of ve(o,{withFileTypes:!0}))g.isDirectory()&&t.push(T(o,g.name))}catch{}}let a=new Set;for(let i of t){let o=T(i,"node_modules","@tbox.cn");if(!D(o))continue;let g=[];try{g=ve(o)}catch{continue}for(let d of g){let p=T(o,d);if(a.has(p))continue;a.add(p);let m=T(p,"service-slots.json");if(!D(m))continue;let v=u=>{try{let k=JSON.parse(u);if(Array.isArray(k.slots))return k.slots.filter(s=>typeof s=="string")}catch{}return null},f=n?n.read(m,v):v(B(m,"utf8"));f!=null&&r.push({dir:p,slots:f})}}return r}function P(e,n){let r=[],t={providers:{},services:{},credentialTypes:{}},a={},i=[],o=new Set,g=new Map,d=(f,u,k)=>{for(let s of f){let l=t.providers[s.provider]??={},h=l[s.implementation];h?h.services.includes(s.service)||h.services.push(s.service):(l[s.implementation]={provider:s.provider,implementation:s.implementation,credentialType:s.credentialType,owner:u,...s.local?{local:!0}:{},...s.configSchema?{configSchema:s.configSchema}:{},...s.credentialSchema?{credentialSchema:s.credentialSchema}:{},services:[s.service]},t.credentialTypes[s.credentialType]??={owner:u,...s.credentialSchema?{credentialSchema:s.credentialSchema}:{}});let C=`${s.provider}\0${s.service}`;a[C]||(a[C]={...s.configSchema?{configSchema:s.configSchema}:{},...s.credentialSchema?{credentialSchema:s.credentialSchema}:{},packageDir:k})}},p=(f,u)=>{let k=f.optional!==!1,s=f.defaults??[],l=t.services[f.service];if(l){l.optional=l.optional&&k;let j=new Set(l.defaults.map(R=>`${R.provider}\0${R.implementation}`));for(let R of s){let w=`${R.provider}\0${R.implementation}`;j.has(w)||(l.defaults.push(R),j.add(w))}l.owners.includes(u)||l.owners.push(u);return}t.services[f.service]={optional:k,defaults:[...s],...f.title?{title:f.title}:{},...f.description?{description:f.description}:{},owners:[u]};let h=f.service.includes(".")?f.service.slice(0,f.service.indexOf(".")):f.service,C=g.get(h);C===void 0?g.set(h,u):C!==u&&i.push({severity:"warning",code:"DOMAIN_KEY_CONFLICT",module:u,message:`域键 ${h} 被多模块声明(${C} 与 ${u})——domains 换算歧义(P13)`})};for(let f of Zn(e)){if(o.has(f.id))continue;let u=T(e,"packages",f.id),k=T(e,"node_modules",...f.package.split("/")),s=te(T(u,"tbox.module.json"),n)??te(T(k,"tbox.module.json"),n);if(!s)continue;o.add(f.id);let l=D(T(u,"tbox.module.json"))?u:k,h=s.contributes;s.kind==="strict"&&r.push({id:f.id,dir:l,pkg:f.mode==="sdk"?f.package:`@app/${f.id}`,descriptor:s.descriptor});let C=h.providers?.slots??[];C.length&&d(C,f.package,l);let j=h.services??[];if(j.length)for(let R of j)p(R,f.package)}for(let{pkg:f,dir:u}of Hn(e)){let k=te(T(u,"tbox.module.json"),n);if(!k?.name||o.has(k.name))continue;o.add(k.name);let s=k.contributes.providers?.slots??[];s.length&&d(s,f,u);let l=k.contributes.services??[];if(l.length)for(let h of l)p(h,f)}let m=T(e,"packages");if(D(m)){let f=[];try{f=ve(m,{withFileTypes:!0}).filter(u=>u.isDirectory()).map(u=>u.name)}catch{}for(let u of f){if(o.has(u))continue;let k=T(m,u),s=te(T(k,"tbox.module.json"),n);if(!s)continue;let l=s.name??u;if(o.has(l))continue;o.add(l);let h=s.contributes;s.kind==="strict"&&r.push({id:l,dir:k,pkg:`@app/${l}`,descriptor:s.descriptor});let C=h.providers?.slots??[];C.length&&d(C,`@app/${l}`,k);let j=h.services??[];if(j.length)for(let R of j)p(R,`@app/${l}`)}}let v=new Set;for(let f of Gn(e,n))for(let u of f.slots)v.add(u);return{modules:r,catalog:t,serviceSchemas:a,vocabulary:[...v].sort(),domainKeyIssues:i}}function Xe(e,n,r){let t=T(e,"src",n,"index.ts");if(!D(t))return!1;try{let a=B(t,"utf8");return new RegExp(`export\\s+const\\s+${r}\\b`).test(a)}catch{return!1}}function qn(e){let n=T(e,"src","server","index.ts");if(!D(n))return"";try{return B(n,"utf8")}catch{return""}}function en(e){return e.match(/declaration\s*:\s*\{[\s\S]*?moduleId\s*:\s*['"]([^'"]+)['"]/)?.[1]}function Qn(e){let n=[],r=P(e),t=me(e),a=new Map;for(let i of t?.npmModules??[])a.set(i.id,i.mode);for(let i of r.modules){let o=a.get(i.id)??"local",g=Xe(i.dir,"server","serverModule"),d=i.descriptor.contributes;n.push({id:i.id,pkg:o==="sdk"?i.pkg:`@app/${i.id}`,mode:o,hasServerEntry:g,hasClientEntry:Xe(i.dir,"client","clientModule"),hasCards:(d.cards?.length??0)>0,...g?{moduleId:en(qn(i.dir))}:{}})}return n.sort((i,o)=>i.id.localeCompare(o.id))}import{existsSync as Jt,readFileSync as Yt}from"fs";import{join as Ht,resolve as Gt}from"path";function ye(e){return P(e).catalog}function nn(e){let n=P(e).catalog,r={};for(let[i,o]of Object.entries(n.providers)){r[i]={};for(let[g,d]of Object.entries(o))r[i][g]={provider:d.provider,implementation:d.implementation,credentialType:d.credentialType,owner:d.owner,...d.local?{local:!0}:{},...d.configSchema?{configSchema:d.configSchema}:{},...d.credentialSchema?{credentialSchema:d.credentialSchema}:{},services:[...d.services]}}let t={};for(let[i,o]of Object.entries(n.services))t[i]={optional:o.optional,defaults:o.defaults.map(g=>({...g})),owners:[...o.owners]};let a={};for(let[i,o]of Object.entries(n.credentialTypes))a[i]={...o};return{providers:r,services:t,credentialTypes:a}}import{existsSync as Xn,readFileSync as et}from"fs";import{join as tn}from"path";var nt=tn("config","integrations.json");function z(e,n){let r=tn(e,nt),t=a=>JSON.parse(a);if(n)return n.read(r,t)??null;if(!Xn(r))return null;try{return t(et(r,"utf8"))}catch{return null}}async function rn(e,n){let r=await M(e),t=z(e,n);return t===null?{config:null,strict:!1,contracts:r}:r.strictValidation&&r.module.parseIntegrationsConfig?{config:r.module.parseIntegrationsConfig(t),strict:!0,contracts:r}:{config:t,strict:!1,contracts:r}}import{existsSync as ln,readFileSync as dn}from"fs";import{join as K,resolve as ot}from"path";import st from"ajv";var H=["provider","config","configByInstance","credentialRef","credentialRefByInstance","instances","defaultInstanceId"];import{readdirSync as tt,existsSync as re,readFileSync as rt}from"fs";import{join as ie}from"path";function on(e,n,r){let t=[];if(n){let a=W({provider:e});t.push({stem:a,ref:`secret://${a}`,input:n})}for(let[a,i]of Object.entries(r??{})){let o=W({provider:e,instanceId:a});t.push({instanceId:a,stem:o,ref:`secret://${o}`,input:i})}return t}function sn(e,n,r,t,a){let i=[];if(t){let o=W({provider:e,service:n});i.push({stem:o,ref:`secret://${o}`,input:t})}for(let[o,g]of Object.entries(a??{})){let d=W({provider:e,instanceId:o,domain:r});i.push({instanceId:o,stem:d,ref:`secret://${d}`,input:g})}return i}function Re(e,n,r){return n.map(t=>({stem:t.stem,ref:t.ref,overwrote:r?re(ie(e,_(t.stem))):de(e,t.stem,t.input)}))}function it(e,n){return re(ie(e,_(n)))}function Se(e,n){if(typeof e=="string"){e.startsWith("secret://")&&n.add(e.slice(9));return}if(Array.isArray(e)){for(let r of e)Se(r,n);return}if(e&&typeof e=="object")for(let r of Object.values(e))Se(r,n)}function we(e){let n=ie(e,"config","credentials"),r=new Set,t=ie(e,"config","integrations.json");if(re(t))try{Se(JSON.parse(rt(t,"utf8")),r)}catch{}let a=[];if(!re(n))return a;let i=[];try{i=tt(n).filter(o=>o.endsWith(".json")).map(o=>o.slice(0,-5))}catch{return a}for(let o of i.sort()){if(r.has(o))continue;let g=Y(e,o);a.push({severity:"warning",code:"ORPHANED_CREDENTIAL",message:`config/credentials/${o}.json 不再被引用(凭据文件保留,可手动清理)`})}return a}var O=class extends Error{code;httpStatus;fields;constructor(n,r,t,a){super(t),this.name="AppToolkitError",this.code=n,this.httpStatus=r,this.fields=a}};function Ie(e){return e.version===null?"contracts 不可解析(未安装)——检查应用依赖安装后重试":e.strictValidation?e.notes[0]??"contracts workspace 源码态未构建——执行 pnpm --filter @tbox.cn/app-contracts build 后重试":`contracts ${e.version} 无严格校验面(需 ≥0.9)——升级应用 @tbox.cn/app-contracts 依赖后可用`}var L=K("config","integrations.json");function U(e,n){if(e===n)return!0;if(Array.isArray(e)&&Array.isArray(n))return e.length!==n.length?!1:e.every((r,t)=>U(r,n[t]));if(e&&n&&typeof e=="object"&&typeof n=="object"){let r=Object.keys(e),t=Object.keys(n);return r.length!==t.length?!1:r.every(a=>U(e[a],n[a]))}return!1}function ae(e,n){if(typeof e!="string"&&e&&typeof e=="object")for(let[r,t]of Object.entries(e)){if((r==="provider"||r==="instanceId")&&typeof t=="string"&&!F.test(t))throw new O("VALIDATION_FAILED",400,`${r} 名含非法字符(需 /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/)`,[`${n}.${r}`]);ae(t,`${n}.${r}`)}}function oe(e,n,r){if(!(!e||typeof e!="object"||Array.isArray(e))){for(let t of Object.keys(e))if(!(r&&(t==="*"||t==="**"))&&!F.test(t))throw new O("VALIDATION_FAILED",400,`${n} 键 ${t} 含非法字符(需 /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/)——进凭据 stem 派生,防路径逃逸`,[n])}}function an(e,n,r){if(r==="root"){let t=e.instances;Array.isArray(t)&&t.forEach((a,i)=>{let o=a&&typeof a=="object"?a.id:void 0;if(typeof o=="string"&&!F.test(o))throw new O("VALIDATION_FAILED",400,`instances[${i}].id 含非法字符(需 /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/)`,[`root.instances[${i}].id`])}),oe(e.configByInstance,"root.configByInstance",!1),oe(e.credentialRefByInstance,"root.credentialRefByInstance",!1)}else oe(e.instances,"services.instances",!0);oe(n.credentialsByInstance,"credentialsByInstance",!1)}function se(e,n){let r=K(e,L),t=a=>JSON.parse(a);if(n)return n.read(r,t)??null;if(!ln(r))return null;try{return t(dn(r,"utf8"))}catch{return null}}function he(e){let n=(e.instances??[]).map(r=>r.id);return n.length===0?[]:pe(e,n)}function at(e,n,r,t){let{catalog:a}=P(e,t),i=a.providers[n];if(!i)return;let o=new Set;for(let g of Object.values(i))(r===void 0||g.services.includes(r))&&o.add(g.credentialType);return o.size===1?[...o][0]:void 0}function cn(e,n,r,t,a,i){let o=a??at(e,n,r,i);if(o!==void 0&&o!=="-"&&t.type!==o)throw new O("VALIDATION_FAILED",400,`凭据 type=${t.type} 与绑定 credentialType ${o} 不一致`,["credentials.type"]);if(r===void 0)return;let{serviceSchemas:g}=P(e,i),d=g[`${n}\0${r}`];if(!d?.credentialSchema)return;let p=ot(d.packageDir,d.credentialSchema);if(ln(p))try{let v=new st({allErrors:!0}).compile(JSON.parse(dn(p,"utf8")));if(!v({type:t.type,...t.values})){let f=(v.errors??[]).map(u=>`credentials.values.${String(u.instancePath||u.params?.missingProperty||"").replace(/^\//,"")}`);throw new O("VALIDATION_FAILED",400,"凭据字段校验失败(ajv 字段级)",f)}}catch(m){if(m instanceof O)throw m}}function ke(e,n={}){let{onApplied:r,cache:t}=n;function a(d,p,m,v,f){let u=!v&&d!==null;u&&($(K(e,L),`${JSON.stringify(d,null,2)}
|
|
4
|
-
`),t?.invalidate([
|
|
5
|
-
`),t?.invalidate([
|
|
1
|
+
import{a as me,b as T,c as ve}from"./chunk-KPD3LUH4.js";import{z as h}from"zod";var L=/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/,Dn=h.object({cardType:h.string(),schemaVersion:h.number().optional()}),He=h.object({route:h.string(),component:h.string()}),Ze=h.object({route:h.string(),label:h.string().optional(),icon:h.string().optional(),order:h.number().optional()}),Ge=h.object({id:h.string(),range:h.string().optional(),required:h.boolean().default(!0)}),qe=h.object({service:h.string(),provider:h.string().regex(L,"provider 名含非法字符(需 /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/)"),implementation:h.string(),credentialType:h.string(),local:h.boolean().optional(),configSchema:h.string().optional(),credentialSchema:h.string().optional()}),Qe=h.object({service:h.string(),optional:h.boolean().default(!0),defaults:h.array(h.object({provider:h.string().regex(L,"provider 名含非法字符(需 /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/)"),implementation:h.string()})).default([]),title:h.string().optional(),description:h.string().optional()}),Xe=h.object({id:h.string(),type:h.string()}),en=h.object({schemaVersion:h.number().default(1),name:h.string(),version:h.string().default("0.0.0"),kind:h.enum(["platform","business","third-party"]).default("business"),risk:h.object({level:h.enum(["high","medium","low"]).default("low"),writeBoundary:h.enum(["sdk-enforced","source"]).default("source")}).default({}),distribution:h.object({defaultMode:h.enum(["hybrid","sdk","codegen","local"]).default("codegen")}).default({}),contributes:h.object({handlers:h.array(h.string()).default([]),tools:h.array(h.string()).default([]),cards:h.array(Dn).default([]),routes:h.array(h.string()).default([]),pages:h.array(He).default([]),tabs:h.array(Ze).default([]),providers:h.object({slots:h.array(qe).default([])}).default({}),services:h.array(Qe).default([]),resources:h.array(Xe).default([])}).default({}),dependencies:h.object({modules:h.array(Ge).default([])}).default({}),env:h.array(h.string()).default([])}).strict();function G(e){let n=en.safeParse(e);return n.success?{ok:!0,value:n.data}:{ok:!1,errors:n.error.issues.map(r=>`${r.path.join(".")||"(root)"}: ${r.message}`)}}var Vn=/\$\$?\{([A-Z][A-Z0-9_]*)\}/g;function $n(e){return JSON.stringify(e).slice(1,-1)}function _n(e,n=process.env){let r=[],t=e.replace(Vn,(s,i)=>{if(s.startsWith("$$"))return s.slice(1);let o=n[i];return o===void 0?(r.push(i),s):$n(o)});return r.length>0?{missing:r}:{text:t,missing:[]}}import{mkdirSync as Fn,existsSync as nn,readFileSync as Ln,writeFileSync as Wn}from"fs";import{dirname as Bn,join as tn}from"path";function re(e){let n={};for(let[r,t]of Object.entries(e)){if(typeof t!="string"){n[r]=null;continue}n[r]=t.length<=4?"****":`${t.slice(0,2)}****`}return n}var A=class extends Error{code;httpStatus;fields;constructor(n,r,t,s){super(t),this.name="AppToolkitError",this.code=n,this.httpStatus=r,this.fields=s}};function ye(e){return e.notes.length>0?e.notes[0]:e.version===null?"contracts 不可解析(未安装)——检查应用依赖安装后重试":e.strictValidation?"contracts workspace 源码态未构建——执行 pnpm --filter @tbox.cn/app-contracts build 后重试":`contracts ${e.version} 无严格校验面(需 ≥0.9.0)——升级应用 @tbox.cn/app-contracts 依赖后可用`}function ie(e,n){if(n!==void 0&&(n.includes("/")||n.includes("\\")||n.includes("..")))throw new A("VALIDATION_FAILED",400,`凭据 stem 派生输入含路径敌对字符(${e})——拒绝派生`)}function B(e){if(ie("provider",e.provider),ie("service",e.service),ie("instanceId",e.instanceId),ie("domain",e.domain),e.instanceId!==void 0){let n=e.domain??e.service??"global";return`${e.provider}-${e.instanceId}-${n}`}return e.service!==void 0?`${e.provider}-${e.service}`:`${e.provider}-global`}function Kn(e){return`secret://${e}`}function F(e){return`config/credentials/${e}.json`}function q(e,n){let r=tn(e,F(n));if(!nn(r))return null;try{let t=JSON.parse(Ln(r,"utf8"));if(typeof t.type!="string")return null;let{type:s,...i}=t,o={};for(let[m,p]of Object.entries(i))typeof p=="string"&&(o[m]=p);return{type:s,values:o}}catch{return null}}function Se(e,n,r){let t=tn(e,F(n)),s=nn(t);return Fn(Bn(t),{recursive:!0}),Wn(t,`${JSON.stringify({type:r.type,...r.values},null,2)}
|
|
2
|
+
`,"utf8"),s}import{mkdirSync as zn,renameSync as Un,writeFileSync as Jn}from"fs";import{dirname as Yn}from"path";function $(e,n){rn(Yn(e));let r=`${e}.tmp-${process.pid}-${Date.now()}`;Jn(r,n,"utf8"),Un(r,e)}function rn(e){zn(e,{recursive:!0})}function K(e){let n=e.indexOf(".");return n===-1?e:e.slice(0,n)}function _(e,n){let r=e?.domains?.[n]?.provider;if(typeof r=="string"&&r.length>0)return{provider:r,layer:"module"};let t=e?.provider;return typeof t=="string"&&t.length>0?{provider:t,layer:"app"}:{provider:null,layer:null}}function z(e,n){let r=[...new Set((n.descriptor.contributes.services??[]).map(i=>K(i.service)))].sort();if(r.length===0)return{writable:!1,reason:"zero-domain",domainKeys:[]};if(r.length>1)return{writable:!1,reason:"multi-domain",domainKeys:r};let t=r[0],s=new Set;for(let[i,o]of Object.entries(e.catalog.services))if(K(i)===t)for(let m of o.owners)s.add(m);return s.size>1?{writable:!1,reason:"shared-key",domainKeys:[t],domainKey:t,coOwners:[...s].sort()}:{writable:!0,domainKeys:[t],domainKey:t}}function on(e,n){let r=e.find(t=>t.pkg===n||`@app/${t.id}`===n);return r?r.id:n.startsWith("@app/")?n.slice(5):n}function sn(e,n){return[...new Set(Object.values(e.providers[n]??{}).flatMap(r=>r.services))].sort()}function we(e){if(!e||typeof e!="object")return!1;let n=Object.keys(e),r=new Set(["provider","credentialType","configSchema"]);if(!n.every(s=>r.has(s)))return!1;let t=e;return typeof t.provider=="string"&&t.provider.length>0&&typeof t.credentialType=="string"&&t.credentialType.length>0&&(t.configSchema===void 0||typeof t.configSchema=="string")}function an(e,n,r){return e==="*"||r.has(e)?null:{severity:"error",code:"INSTANCE_KEY_NOT_REGISTERED",instance:e,message:`integrations: ${n} 键 ${e} 不在 malls 白名单内(${[...r].join(", ")})`}}function he(e,n){let r=new Set(n),t=[],s=(i,o)=>{let m=an(i,o,r);m&&t.push(m)};for(let i of Object.keys(e.configByInstance??{}))s(i,"configByInstance");for(let i of Object.keys(e.credentialRefByInstance??{}))s(i,"credentialRefByInstance");for(let[i,o]of Object.entries(e.services??{}))for(let m of Object.keys(o.instances??{}))s(m,`services.${i}.instances`);return t}function oe(e){let n=[],r=e.contracts,{integrations:t,suppliedProviders:s,demandedServices:i,supplies:o,malls:m,credentialSchemaMissing:p,vendorConfigKeys:u,vendorConfigSchemaMissing:g,credentialFileMissing:w,credentialFileType:d,realmSwitchHints:v,moduleResourceNeeds:I}=e,a=m?new Set(m):void 0;for(let c of v??[])n.push({severity:"warning",code:"REALM_SWITCH_HINT",message:c});let l=new Set(Object.keys(s).map(c=>c.slice(0,c.indexOf("\0"))).filter(c=>c.length>0)),f=(c,S,C,R)=>{if(typeof C?.provider=="string"&&typeof C.implementation=="string"&&C.provider&&C.implementation)return s[Q(C.provider,C.implementation)]?.credentialType;if(typeof S.provider=="string"&&typeof S.implementation=="string")return s[Q(S.provider,S.implementation)]?.credentialType;let x=S.provider?.credentialType;if(typeof x=="string")return x;if(!o)return;let M=R??Object.keys(S.instances??{})[0]??"",O=r.resolveEffectiveService(t,c,M,o);if(!(O.status!=="ok"||typeof O.effective.provider!="string"))return s[Q(O.effective.provider,O.effective.implementation)]?.credentialType};if(w||d)for(let[c,S]of Object.entries(t.services))for(let[C,R]of Object.entries(S.instances??{})){if(R.enabled===!1)continue;let x=R.credentialRef??S.credentialRef??t.credentialRefByInstance?.[C]??t.credentialRef;if(typeof x!="string"||!x.startsWith("secret://"))continue;let M=x.slice(9);w?.[M]===!0&&n.push({severity:"error",code:"CREDENTIAL_FILE_MISSING",service:c,instance:C,message:`integrations: 凭据文件缺失 ${M}(${c}@${C} effective ref=${x};档内 config/credentials/ 无此文件)`});let O=d?.[M];if(O){let N=f(c,S,R,C);N&&O!==N&&n.push({severity:"error",code:"CREDENTIAL_TYPE_MISMATCH",service:c,instance:C,message:`integrations: 凭据文件 ${M}.json type=${O} 与绑定 credentialType ${N} 不一致(${c}@${C}——transport 创建期将按 type 选策略)`})}}let b=(c,S)=>{if(!a)return;let C=an(c,S,a);C&&n.push(C)};for(let[c]of Object.entries(t.configByInstance??{}))b(c,"configByInstance");for(let[c]of Object.entries(t.credentialRefByInstance??{}))b(c,"credentialRefByInstance");let E=Object.entries(t.services).filter(([,c])=>c.instances==null||c.instances["*"]!=null).map(([c])=>c);if(E.length>0){let c=Object.entries(t.services).filter(([,S])=>S.instances!=null&&S.instances["*"]==null).map(([S])=>S);c.length>0&&n.push({severity:"warning",code:"WILDCARD_ALIGNMENT",message:`integrations: 激活形态不对称(通配:${E.join(", ")};显式键集:${c.join(", ")})——动态 mallId 将在显式键集槽上 INSTANCE_NOT_FOUND(provider 业务限制表达位),确认是否齐步`})}let y=r.normalizeInstances({instances:t.instances,defaultInstanceId:t.defaultInstanceId});y.instances.length>=2&&y.defaultInstanceId===null&&n.push({severity:"warning",code:"MULTI_INSTANCE_NO_DEFAULT",message:`integrations: ${y.instances.length} 个部署实例(${y.instances.map(c=>c.id).join(", ")})均无 defaultInstanceId——无 scope 参数访问将落全局会话(默认商圈不注入)`});for(let[c,S]of Object.entries(t.services)){if(i[c]||n.push({severity:"warning",code:"SLOT_NOT_DECLARED",service:c,message:`integrations: 服务槽 ${c} 无消费方声明(拼写错误或需求方未安装)——若为拆分前旧槽位词汇(member.main/parking.main/promotion.read/shopping-guide.main),参照 cookbook·app-config 槽位迁移配方展开为新槽`}),typeof S.provider=="string"?typeof S.implementation=="string"&&!s[Q(S.provider,S.implementation)]&&n.push({severity:"error",code:"INSTANCE_SUPPLY_UNMATCHED",service:c,message:`integrations: provider ${S.provider}/${S.implementation} 未命中已装供给(${c} 槽级)——装对应 provider 包或改绑`}):S.provider!==void 0&&!we(S.provider)&&n.push({severity:"error",code:"INLINE_PROVIDER_INVALID",service:c,message:`integrations: 内联 provider 超受控形状(${c} 槽级;允许键 provider/credentialType/configSchema)`}),p){let R=f(c,S);R&&p[R]===!0&&n.push({severity:"error",code:"SCHEMA_FILE_MISSING",service:c,message:`integrations: credentialType ${R} 的 schema 引用不存在(${c};manifest 声明指向缺失文件)`})}typeof S.provider=="object"&&typeof S.implementation!="string"&&n.push({severity:"error",code:"IMPL_WITHOUT_PROVIDER",service:c,message:`integrations: 内联厂商声明必须同时提供 implementation(${c} 槽级)`});for(let[R,x]of Object.entries(S.instances??{})){b(R,`services.${c}.instances`);let M=x.provider!==void 0,O=x.implementation!==void 0;if(O&&!M&&n.push({severity:"error",code:"IMPL_WITHOUT_PROVIDER",service:c,instance:R,message:`integrations: 实例级 implementation 需伴随 provider(${c}@${R}——impl-only 禁止,provider 是绑定组头)`}),M&&O){let N=x.provider,D=x.implementation;typeof N=="string"&&typeof D=="string"?s[Q(N,D)]||n.push({severity:"error",code:"INSTANCE_SUPPLY_UNMATCHED",service:c,instance:R,message:`integrations: 实例级 provider 覆盖 ${N}/${D} 未命中已装供给(${c}@${R})`}):we(N)||n.push({severity:"error",code:"INLINE_PROVIDER_INVALID",service:c,instance:R,message:`integrations: 实例级内联 provider 超受控形状(${c}@${R})`})}}}for(let[c,S]of[["root",t.provider],...Object.entries(t.domains??{}).map(([C,R])=>[`domains.${C}`,R?.provider])])typeof S!="string"||S.length===0||l.has(S)||n.push({severity:"error",code:"VENDOR_NOT_INSTALLED",module:c.startsWith("domains.")?c.slice(8):void 0,message:`integrations: ${c} provider ${S} 未命中已装供给——装对应 provider 包或 provider use 改绑`});if(o)for(let[c,S]of Object.entries(t.services)){let C=S.instances?Object.keys(S.instances):[""];for(let R of C){let x=r.resolveEffectiveService(t,c,R,o);x.status==="binding-unresolved"&&n.push({severity:"error",code:"BINDING_UNRESOLVED",service:c,instance:R||void 0,message:`integrations: ${x.message}`})}}for(let[c,S]of Object.entries(t.services))S.implementation!==void 0&&S.provider===void 0&&n.push({severity:"error",code:"IMPL_WITHOUT_PROVIDER",service:c,message:`integrations: 槽级 implementation 需伴随 provider(${c}——impl-only 禁止,provider 是绑定组头)`});let k=new Set([...Object.keys(t.services).map(c=>r.moduleDomainOf(c)),...Object.keys(i).map(c=>r.moduleDomainOf(c))]);for(let c of Object.keys(t.domains??{}))k.has(c)||n.push({severity:"warning",code:"DOMAIN_KEY_UNMATCHED",module:c,message:`integrations: domains.${c} 无对应槽位或需求声明(域前缀拼写错误?——键 = 槽位 id 点分前缀)`});if(u){let c=new Set,S=!1,C=R=>{typeof R!="string"||R.length===0||(u[R]&&c.add(R),!u[R]&&!g?.[R]&&(S=!0),g?.[R]&&(S=!0))};C(t.provider);for(let R of Object.values(t.domains??{}))C(R?.provider);for(let R of Object.values(t.services)){let x=R.provider;C(typeof R.provider=="string"?R.provider:x?.provider)}if(c.size>0&&!S){let R=new Set;for(let M of c)for(let O of u[M]??[])R.add(O);let x=(M,O,N)=>{for(let[D]of Object.entries(M??{}))(N??R).has(D)||n.push({severity:"error",code:"DOMAIN_CONFIG_INVALID",message:`integrations: ${O} 键 ${D} 不在引用厂商(${[...c].join("/")})任何槽 schema properties 内(死配置)`})};x(t.config,"root.config");for(let[M,O]of Object.entries(t.configByInstance??{}))x(O,`root.configByInstance[${M}]`);for(let[M,O]of Object.entries(t.domains??{})){if(!O?.config)continue;let N=_(t,M).provider??void 0;if(N===void 0){x(O.config,`domains.${M}.config`);continue}let D=u[N];!D||g?.[N]||x(O.config,`domains.${M}.config`,new Set(D))}}}for(let[c,S]of Object.entries(i)){let C=t.services[c];if(!C){n.push({severity:"warning",code:"SLOT_NOT_CONFIGURED",service:c,message:`integrations: 服务槽 ${c}(需求方 ${S.owners.join(", ")})未配置${S.optional?"(可选槽)":"(必需槽——模块将走降级链)"}`});continue}let R=Object.entries(C.instances??{}).filter(([,x])=>x.enabled===!1).map(([x])=>x);R.length>0&&n.push({severity:"warning",code:"INSTANCE_DISABLED",service:c,instance:R.join(", "),message:`integrations: ${c} 实例禁用(${R.join(", ")})`})}for(let[c,S]of Object.entries(t.modules??{})){let C=I?.[c];I&&!(c in I)&&n.push({severity:"warning",code:"RESOURCE_NOT_DECLARED",module:c,message:`integrations: modules.${c} 未命中已装模块(键 = install id——检查 .tbox/app.json entry.id / packages/ 目录名)`});for(let[R,x]of Object.entries(S?.resources??{})){let M=x?.type;(typeof M!="string"||!r.RESOURCE_TYPE_IDS.includes(M))&&n.push({severity:"error",code:"RESOURCE_TYPE_UNKNOWN",module:c,message:`integrations: modules.${c}.resources.${R} type 非法(${String(M)};词汇表 = ${r.RESOURCE_TYPE_IDS.join("/")})`}),C&&C.size>0&&!C.has(R)&&n.push({severity:"warning",code:"RESOURCE_NOT_DECLARED",module:c,message:`integrations: modules.${c}.resources.${R} 未被模块 manifest contributes.resources 声明`})}}return n}function Q(e,n){return`${e}\0${n}`}import{existsSync as Ie,readFileSync as Hn,readdirSync as Zn}from"fs";import{join as se}from"path";function ln(e){return e.replaceAll("-","_").toUpperCase()}function cn(e){if(!Ie(e))return null;try{let n=JSON.parse(Hn(e,"utf8"));return n&&typeof n=="object"?n:null}catch{return null}}function Gn(e){let n=new Set,r=e.properties;if(r&&typeof r=="object"&&!Array.isArray(r))for(let t of Object.keys(r))n.add(t);return n}function Re(e,n,r,t=process.env){let s={};for(let[y,k]of Object.entries(n.catalog.providers))for(let[c,S]of Object.entries(k))s[`${y}\0${c}`]={services:[...S.services],owner:S.owner,credentialType:S.credentialType};let i={};for(let[y,k]of Object.entries(n.catalog.services))i[y]={optional:k.optional,owners:[...k.owners]};let o={},m={},p={},u={};for(let y of n.modules){u[y.id]=new Set((y.descriptor.contributes.resources??[]).map(k=>k.id));for(let k of y.descriptor.contributes.providers?.slots??[]){if(k.configSchema){let c=cn(se(y.dir,k.configSchema));if(c){let S=o[k.provider]??=new Set;for(let C of Gn(c))S.add(C)}else m[k.provider]=!0}k.credentialSchema&&!Ie(se(y.dir,k.credentialSchema))&&(p[k.credentialType]=!0)}}let g={},w=new Set,d=se(e,"config","credentials");if(Ie(d))for(let y of Zn(d)){if(!y.endsWith(".json"))continue;let k=y.slice(0,-5);w.add(k);let c=cn(se(d,y));c&&typeof c.type=="string"&&(g[k]=c.type)}let v=r,I=[],a=y=>{typeof y=="string"&&y.startsWith("secret://")&&I.push(y)};for(let y of Object.values(v.services??{}))if(y&&typeof y=="object"){a(y.credentialRef);for(let k of Object.values(y.instances??{}))k&&typeof k=="object"&&a(k.credentialRef)}for(let y of Object.values(v.credentialRefByInstance??{}))a(y);a(v.credentialRef);let l={};for(let y of I){let k=y.slice(9);t[ln(k)]===void 0&&(w.has(k)||(l[k]=!0))}let f=[];for(let[y,k]of Object.entries(v.services??{})){let c=k&&typeof k=="object"&&typeof k.provider=="string"?k.provider:void 0;for(let[S,C]of Object.entries((k&&typeof k=="object"?k.instances:void 0)??{})){let R=C&&typeof C=="object"&&typeof C.provider=="string"?C.provider:void 0;c&&R&&R!==c&&f.push(`integrations: ${y}@${S} 实现覆盖跨厂商(${c} → ${R})——凭据域切换,确认该 mall 对应凭据在场`)}}let E=((Array.isArray(v.instances)?v.instances:void 0)??[]).map(y=>y&&typeof y=="object"&&typeof y.id=="string"?y.id:void 0).filter(y=>typeof y=="string");return{suppliedProviders:s,demandedServices:i,credentialFileMissing:l,credentialFileType:g,credentialSchemaMissing:p,vendorConfigKeys:o,vendorConfigSchemaMissing:m,realmSwitchHints:f,moduleResourceNeeds:u,...E.length>0?{malls:E}:{}}}import{existsSync as V,readdirSync as be,readFileSync as U}from"fs";import{join as j}from"path";import{existsSync as qn,readFileSync as dn,mkdirSync as Qn,writeFileSync as Xn}from"fs";import un from"path";function ae(e){return un.join(e,".tbox","app.json")}function pn(e){let n=ae(e);if(!qn(n))return null;try{let r=JSON.parse(dn(n,"utf8")),t=r?.templateVersion;return typeof t!="string"||t.trim().length===0?null:{templateVersion:t.trim(),npmModules:Array.isArray(r?.npmModules)?r.npmModules:[]}}catch{return null}}function et(e,n){let r=ae(e);Qn(un.dirname(r),{recursive:!0}),Xn(r,`${JSON.stringify(n,null,2)}
|
|
3
|
+
`,"utf8")}function ke(e){let n=pn(e);if(n===null)return null;let r=ae(e);try{let t=JSON.parse(dn(r,"utf8"));return{...n,miniappContainers:Array.isArray(t?.miniappContainers)?t.miniappContainers:[]}}catch{return{...n,miniappContainers:[]}}}function nt(e,n){let r=e.npmModules.filter(t=>t.id!==n.id);return{...e,npmModules:[...r,n]}}function tt(e,n){return{...e,npmModules:e.npmModules.filter(r=>r.id!==n)}}function rt(e,n){let r=(e.miniappContainers??[]).filter(t=>t.id!==n.id);return{...e,miniappContainers:[...r,n]}}function it(e,n,r){return{...e,npmModules:e.npmModules.map(t=>t.id===n?{...t,...r}:t)}}function fn(e){return/^[a-z0-9]+(-[a-z0-9]+)*$/.test(e)&&!e.includes("..")}function ot(e,n){if(!fn(e))throw new Error(`非法模块 id "${e}"(来自 ${n}):仅允许小写字母/数字/中划线`)}function ce(e,n){let r=t=>{try{let s=JSON.parse(t),i=G(s);if(i.ok)return{kind:"strict",name:i.value.name,version:i.value.version,descriptor:i.value,contributes:i.value.contributes};if(s&&typeof s=="object"&&"contributes"in s){let o=s;return{kind:"loose",...typeof o.name=="string"?{name:o.name}:{},contributes:{providers:o.contributes?.providers,services:o.contributes?.services}}}return null}catch{return null}};if(n)return n.read(e,r)??null;if(!V(e))return null;try{return r(U(e,"utf8"))}catch{return null}}function st(e){let n=j(e,".tbox","app.json");if(!V(n))return[];try{let r=JSON.parse(U(n,"utf8"));return Array.isArray(r.npmModules)?r.npmModules.filter(t=>!!t&&typeof t=="object"&&typeof t.id=="string"&&typeof t.package=="string"):[]}catch{return[]}}function at(e){let n=j(e,".tbox-dev.local"),r=j(n,"dev-manifest.json");if(!V(r))return[];try{let t=JSON.parse(U(r,"utf8")),s=[];for(let[i,o]of Object.entries(t.redirects??{}))typeof o!="string"||!i.startsWith("@tbox.cn/app-")||s.push({pkg:i,dir:j(n,o)});return s}catch{return[]}}function ct(e,n){let r=[],t=[e];for(let i of["apps","packages"]){let o=j(e,i);if(V(o))try{for(let m of be(o,{withFileTypes:!0}))m.isDirectory()&&t.push(j(o,m.name))}catch{}}let s=new Set;for(let i of t){let o=j(i,"node_modules","@tbox.cn");if(!V(o))continue;let m=[];try{m=be(o)}catch{continue}for(let p of m){let u=j(o,p);if(s.has(u))continue;s.add(u);let g=j(u,"service-slots.json");if(!V(g))continue;let w=v=>{try{let I=JSON.parse(v);if(Array.isArray(I.slots))return I.slots.filter(a=>typeof a=="string")}catch{}return null},d=n?n.read(g,w):w(U(g,"utf8"));d!=null&&r.push({dir:u,slots:d})}}return r}function P(e,n){let r=[],t={providers:{},services:{},credentialTypes:{}},s={},i=[],o=new Set,m=new Map,p=(d,v,I)=>{for(let a of d){let l=t.providers[a.provider]??={},f=l[a.implementation];f?f.services.includes(a.service)||f.services.push(a.service):(l[a.implementation]={provider:a.provider,implementation:a.implementation,credentialType:a.credentialType,owner:v,...a.local?{local:!0}:{},...a.configSchema?{configSchema:a.configSchema}:{},...a.credentialSchema?{credentialSchema:a.credentialSchema}:{},services:[a.service]},t.credentialTypes[a.credentialType]??={owner:v,packageDir:I,...a.credentialSchema?{credentialSchema:a.credentialSchema}:{}});let b=`${a.provider}\0${a.service}`;s[b]||(s[b]={...a.configSchema?{configSchema:a.configSchema}:{},...a.credentialSchema?{credentialSchema:a.credentialSchema}:{},packageDir:I})}},u=(d,v)=>{let I=d.optional!==!1,a=d.defaults??[],l=t.services[d.service];if(l){l.optional=l.optional&&I;let E=new Set(l.defaults.map(y=>`${y.provider}\0${y.implementation}`));for(let y of a){let k=`${y.provider}\0${y.implementation}`;E.has(k)||(l.defaults.push(y),E.add(k))}l.owners.includes(v)||l.owners.push(v);return}t.services[d.service]={optional:I,defaults:[...a],...d.title?{title:d.title}:{},...d.description?{description:d.description}:{},owners:[v]};let f=d.service.includes(".")?d.service.slice(0,d.service.indexOf(".")):d.service,b=m.get(f);b===void 0?m.set(f,v):b!==v&&i.push({severity:"warning",code:"DOMAIN_KEY_CONFLICT",module:v,message:`域键 ${f} 被多模块声明(${b} 与 ${v})——domains 换算歧义(P13)`})};for(let d of st(e)){if(o.has(d.id))continue;let v=j(e,"packages",d.id),I=j(e,"node_modules",...d.package.split("/")),a=ce(j(v,"tbox.module.json"),n)??ce(j(I,"tbox.module.json"),n);if(!a)continue;o.add(d.id);let l=V(j(v,"tbox.module.json"))?v:I,f=a.contributes;a.kind==="strict"&&r.push({id:d.id,dir:l,pkg:d.mode==="sdk"?d.package:`@app/${d.id}`,descriptor:a.descriptor});let b=f.providers?.slots??[];b.length&&p(b,d.package,l);let E=f.services??[];if(E.length)for(let y of E)u(y,d.package)}for(let{pkg:d,dir:v}of at(e)){let I=ce(j(v,"tbox.module.json"),n);if(!I?.name||o.has(I.name))continue;o.add(I.name);let a=I.contributes.providers?.slots??[];a.length&&p(a,d,v);let l=I.contributes.services??[];if(l.length)for(let f of l)u(f,d)}let g=j(e,"packages");if(V(g)){let d=[];try{d=be(g,{withFileTypes:!0}).filter(v=>v.isDirectory()).map(v=>v.name)}catch{}for(let v of d){if(o.has(v))continue;let I=j(g,v),a=ce(j(I,"tbox.module.json"),n);if(!a)continue;let l=a.name??v;if(o.has(l))continue;o.add(l);let f=a.contributes;a.kind==="strict"&&r.push({id:l,dir:I,pkg:`@app/${l}`,descriptor:a.descriptor});let b=f.providers?.slots??[];b.length&&p(b,`@app/${l}`,I);let E=f.services??[];if(E.length)for(let y of E)u(y,`@app/${l}`)}}let w=new Set;for(let d of ct(e,n))for(let v of d.slots)w.add(v);return{modules:r,catalog:t,serviceSchemas:s,vocabulary:[...w].sort(),domainKeyIssues:i}}function gn(e,n,r){let t=j(e,"src",n,"index.ts");if(!V(t))return!1;try{let s=U(t,"utf8");return new RegExp(`export\\s+const\\s+${r}\\b`).test(s)}catch{return!1}}function lt(e){let n=j(e,"src","server","index.ts");if(!V(n))return"";try{return U(n,"utf8")}catch{return""}}function mn(e){return e.match(/declaration\s*:\s*\{[\s\S]*?moduleId\s*:\s*['"]([^'"]+)['"]/)?.[1]}function dt(e){let n=[],r=P(e),t=ke(e),s=new Map;for(let i of t?.npmModules??[])s.set(i.id,i.mode);for(let i of r.modules){let o=s.get(i.id)??"local",m=gn(i.dir,"server","serverModule"),p=i.descriptor.contributes;n.push({id:i.id,pkg:o==="sdk"?i.pkg:`@app/${i.id}`,mode:o,hasServerEntry:m,hasClientEntry:gn(i.dir,"client","clientModule"),hasCards:(p.cards?.length??0)>0,...m?{moduleId:mn(lt(i.dir))}:{}})}return n.sort((i,o)=>i.id.localeCompare(o.id))}import{existsSync as ar,readFileSync as cr}from"fs";import{join as dr,resolve as ur}from"path";function Ce(e){return P(e).catalog}function vn(e){let n=P(e).catalog,r={};for(let[i,o]of Object.entries(n.providers)){r[i]={};for(let[m,p]of Object.entries(o))r[i][m]={provider:p.provider,implementation:p.implementation,credentialType:p.credentialType,owner:p.owner,...p.local?{local:!0}:{},...p.configSchema?{configSchema:p.configSchema}:{},...p.credentialSchema?{credentialSchema:p.credentialSchema}:{},services:[...p.services]}}let t={};for(let[i,o]of Object.entries(n.services))t[i]={optional:o.optional,defaults:o.defaults.map(m=>({...m})),owners:[...o.owners]};let s={};for(let[i,o]of Object.entries(n.credentialTypes))s[i]={owner:o.owner,...o.credentialSchema?{credentialSchema:o.credentialSchema}:{}};return{providers:r,services:t,credentialTypes:s}}import{existsSync as ut,readFileSync as pt}from"fs";import{join as yn}from"path";var ft=yn("config","integrations.json");function J(e,n){let r=yn(e,ft),t=s=>JSON.parse(s);if(n)return n.read(r,t)??null;if(!ut(r))return null;try{return t(pt(r,"utf8"))}catch{return null}}async function Sn(e,n){let r=await T(e),t=J(e,n);if(t===null)return{config:null,strict:!1,contracts:r};if(r.strictValidation&&r.module.parseIntegrationsConfig)try{return{config:r.module.parseIntegrationsConfig(t),strict:!0,contracts:r}}catch(s){if(s instanceof Error&&Array.isArray(s.issues)){let i=s.issues.filter(o=>Array.isArray(o.path)).map(o=>o.path.map(String).join(".")).filter(o=>o.length>0);throw new A("VALIDATION_FAILED",400,"integrations.json 校验失败",i.length>0?i:void 0)}throw new A("VALIDATION_FAILED",400,s instanceof Error?s.message:String(s))}return{config:t,strict:!1,contracts:r}}function X(e,n,r){return e.includes(r)||n.includes(r)}import{existsSync as kn,readFileSync as bn}from"fs";import{join as Y,resolve as yt}from"path";import St from"ajv";var ee=["provider","config","configByInstance","credentialRef","credentialRefByInstance","instances","defaultInstanceId"];import{readdirSync as gt,existsSync as le,readFileSync as mt}from"fs";import{join as de}from"path";function wn(e,n,r){let t=[];if(n){let s=B({provider:e});t.push({stem:s,ref:`secret://${s}`,input:n})}for(let[s,i]of Object.entries(r??{})){let o=B({provider:e,instanceId:s});t.push({instanceId:s,stem:o,ref:`secret://${o}`,input:i})}return t}function hn(e,n,r,t,s){let i=[];if(t){let o=B({provider:e,service:n});i.push({stem:o,ref:`secret://${o}`,input:t})}for(let[o,m]of Object.entries(s??{})){let p=B({provider:e,instanceId:o,domain:r});i.push({instanceId:o,stem:p,ref:`secret://${p}`,input:m})}return i}function Ee(e,n,r){return n.map(t=>({stem:t.stem,ref:t.ref,overwrote:r?le(de(e,F(t.stem))):Se(e,t.stem,t.input)}))}function vt(e,n){return le(de(e,F(n)))}function Ae(e,n){if(typeof e=="string"){e.startsWith("secret://")&&n.add(e.slice(9));return}if(Array.isArray(e)){for(let r of e)Ae(r,n);return}if(e&&typeof e=="object")for(let r of Object.values(e))Ae(r,n)}function xe(e){let n=de(e,"config","credentials"),r=new Set,t=de(e,"config","integrations.json");if(le(t))try{Ae(JSON.parse(mt(t,"utf8")),r)}catch{}let s=[];if(!le(n))return s;let i=[];try{i=gt(n).filter(o=>o.endsWith(".json")).map(o=>o.slice(0,-5))}catch{return s}for(let o of i.sort()){if(r.has(o))continue;let m=q(e,o);s.push({severity:"warning",code:"ORPHANED_CREDENTIAL",message:`config/credentials/${o}.json 不再被引用(凭据文件保留,可手动清理)`})}return s}var W=Y("config","integrations.json");function H(e,n){if(e===n)return!0;if(Array.isArray(e)&&Array.isArray(n))return e.length!==n.length?!1:e.every((r,t)=>H(r,n[t]));if(e&&n&&typeof e=="object"&&typeof n=="object"){let r=Object.keys(e),t=Object.keys(n);return r.length!==t.length?!1:r.every(s=>H(e[s],n[s]))}return!1}function fe(e,n){if(typeof e!="string"&&e&&typeof e=="object")for(let[r,t]of Object.entries(e)){if((r==="provider"||r==="instanceId")&&typeof t=="string"&&!L.test(t))throw new A("VALIDATION_FAILED",400,`${r} 名含非法字符(需 /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/)`,[`${n}.${r}`]);fe(t,`${n}.${r}`)}}function ue(e,n,r){if(!(!e||typeof e!="object"||Array.isArray(e))){for(let t of Object.keys(e))if(!(r&&(t==="*"||t==="**"))&&!L.test(t))throw new A("VALIDATION_FAILED",400,`${n} 键 ${t} 含非法字符(需 /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/)——进凭据 stem 派生,防路径逃逸`,[n])}}function In(e,n,r){if(r==="root"){let t=e.instances;Array.isArray(t)&&t.forEach((s,i)=>{let o=s&&typeof s=="object"?s.id:void 0;if(typeof o=="string"&&!L.test(o))throw new A("VALIDATION_FAILED",400,`instances[${i}].id 含非法字符(需 /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/)`,[`root.instances[${i}].id`])}),ue(e.configByInstance,"root.configByInstance",!1),ue(e.credentialRefByInstance,"root.credentialRefByInstance",!1)}else ue(e.instances,"services.instances",!0);ue(n.credentialsByInstance,"credentialsByInstance",!1)}function pe(e,n){let r=Y(e,W),t=s=>JSON.parse(s);if(n)return n.read(r,t)??null;if(!kn(r))return null;try{return t(bn(r,"utf8"))}catch{return null}}function Oe(e){let n=(e.instances??[]).map(r=>r.id);return n.length===0?[]:he(e,n)}function wt(e,n,r,t){let{catalog:s}=P(e,t),i=s.providers[n];if(!i)return;let o=new Set;for(let m of Object.values(i))(r===void 0||m.services.includes(r))&&o.add(m.credentialType);return o.size===1?[...o][0]:void 0}function Rn(e,n,r,t,s,i){if(typeof t.type!="string"||t.type.length===0)throw new A("VALIDATION_FAILED",400,"凭据 type 缺失或为空字符串",["credentials.type"]);let o=s??wt(e,n,r,i);if(o!==void 0&&o!=="-"&&t.type!==o)throw new A("VALIDATION_FAILED",400,`凭据 type=${t.type} 与绑定 credentialType ${o} 不一致`,["credentials.type"]);if(r===void 0)return;let{serviceSchemas:m}=P(e,i),p=m[`${n}\0${r}`];if(!p?.credentialSchema)return;let u=yt(p.packageDir,p.credentialSchema);if(kn(u))try{let w=new St({allErrors:!0}).compile(JSON.parse(bn(u,"utf8")));if(!w({type:t.type,...t.values})){let d=(w.errors??[]).map(v=>`credentials.values.${String(v.instancePath||v.params?.missingProperty||"").replace(/^\//,"")}`);throw new A("VALIDATION_FAILED",400,"凭据字段校验失败(ajv 字段级)",d)}}catch(g){if(g instanceof A)throw g}}function Te(e,n={}){let{onApplied:r,cache:t}=n;function s(u,g,w,d,v){let I=!d&&u!==null;I&&($(Y(e,W),`${JSON.stringify(u,null,2)}
|
|
4
|
+
`),t?.invalidate([Y(e,W)]));let a=I||v,l=a&&r!==void 0;l&&r?.();let f=[];return I&&f.push(W),v&&f.push(...g.map(b=>`config/credentials/${b.stem}.json`)),{written:a,restartScheduled:l,files:f,credentials:g,issues:w}}let i;async function o(){i=await T(e)}function m(u,g){return u.filter(w=>Array.isArray(w.path)).map(w=>{let d=w.path.map(String).join(".");return g!==""&&d.startsWith(g)&&d.length>g.length?d.slice(g.length):d}).filter(w=>w.length>0)}function p(u,g,w){let d=i;if(!d||!d.strictValidation||!d.module.parseIntegrationsConfig)throw new A("CONTRACTS_NOT_RESOLVED",503,"contracts 严格校验面不可用(需 ≥0.9)——写路径门控失败(工厂边缘断言应先行拦截)");try{d.module.parseIntegrationsConfig(u)}catch(v){throw v instanceof Error&&Array.isArray(v.issues)?new A("VALIDATION_FAILED",400,"节点 schema 校验失败",m(v.issues,w)):new A("VALIDATION_FAILED",400,v instanceof Error?v.message:String(v))}}return{warmup:o,applyApp(u,g={}){fe(u,"root"),In(u,g,"root");let w=[],d=pe(e,t),v=d??{services:{}};d===null&&w.push({severity:"warning",code:"SKELETON_MATERIALIZED",message:"config/integrations.json 缺席——已物化骨架(写时自动初始化;R13 语义翻转提示)"});let I={...v};for(let c of ee)c in u||delete I[c];for(let[c,S]of Object.entries(u))I[c]=S;let a=typeof I.provider=="string"?I.provider:"app",l=wn(a,g.credentials,g.credentialsByInstance);for(let c of l)Rn(e,a,void 0,c.input,void 0,t);let f=l.find(c=>c.instanceId===void 0);if(g.credentials&&f&&(I.credentialRef=f.ref),g.credentialsByInstance){let c={...I.credentialRefByInstance??{}};for(let S of l)S.instanceId!==void 0&&(c[S.instanceId]=S.ref);I.credentialRefByInstance=c}let b=g.credentials!==void 0||g.credentialsByInstance!==void 0,E=H(d,I);if(E&&!b)return{written:!1,restartScheduled:!1,files:[],credentials:[],issues:w};p(I,w,"");let y=g.dryRun===!0,k=Ee(e,l,y);return w.push(...Oe(I)),s(E?null:I,k,w,g.dryRun,!y&&l.length>0)},applyModule(u,g,w={}){fe(g,"root");let{modules:d,catalog:v}=P(e,t),I=d.find(c=>c.id===u);if(!I)throw new A("MODULE_NOT_FOUND",404,`模块 ${u} 非已装模块`);let a=z({modules:d,catalog:v},I);if(!a.writable)throw a.reason==="multi-domain"?new A("VALIDATION_FAILED",400,`模块 ${u} 跨 ${a.domainKeys.length} 个域(${a.domainKeys.join("、")})——模块级统一配置仅支持单域模块,请按服务级配置(编辑各服务行)`):a.reason==="zero-domain"?new A("VALIDATION_FAILED",400,`模块 ${u} 无服务需求声明,无域配置位`):new A("VALIDATION_FAILED",400,`域键 ${a.domainKey} 由 ${a.coOwners.map(c=>on(d,c)).join("、")} 共用——写通道拒绝歧义操作,请按服务级配置`);let l=a.domainKey,f=[],b=pe(e,t);if(b===null&&Object.keys(g).length===0)return{written:!1,restartScheduled:!1,files:[],credentials:[],issues:f};b===null&&f.push({severity:"warning",code:"SKELETON_MATERIALIZED",message:"config/integrations.json 缺席——已物化骨架(写时自动初始化;R13 语义翻转提示)"});let E=b??{services:{}},y={...E.domains??{}},k={...E,domains:y};return Object.keys(g).length===0?delete y[l]:y[l]={...g},H(b,k)?{written:!1,restartScheduled:!1,files:[],credentials:[],issues:f}:(p(k,f,`domains.${l}.`),f.push(...Oe(k)),s(k,[],f,w.dryRun,!1))},applyService(u,g,w={}){fe(g,"root"),In(g,w,"slot");let d=[],v=pe(e,t);if(!X(P(e,t).vocabulary,Object.keys(v?.services??{}),u))throw new A("SERVICE_NOT_FOUND",404,`服务 ${u} 不在服务词汇(已装契约包 ∪ 已配置)`);v===null&&d.push({severity:"warning",code:"SKELETON_MATERIALIZED",message:"config/integrations.json 缺席——已物化骨架(写时自动初始化;R13 语义翻转提示)"});let I=v??{services:{}},a={...I.services??{}},l={...I,services:a};a[u]={...g};let f=g.provider,b=f!==null&&typeof f=="object"?f:void 0,E=typeof f=="string"?f:typeof b?.provider=="string"?b.provider:"app",y=u.includes(".")?u.slice(0,u.indexOf(".")):u,k=hn(E,u,y,w.credentials,w.credentialsByInstance);for(let O of k)Rn(e,E,u,O.input,typeof b?.credentialType=="string"?b.credentialType:void 0,t);let c=a[u],S=k.find(O=>O.instanceId===void 0);if(w.credentials&&S&&(c.credentialRef=S.ref),w.credentialsByInstance){let O={...c.instances??{}};for(let N of k){if(N.instanceId===void 0)continue;let D={...O[N.instanceId]??{}};D.credentialRef=N.ref,O[N.instanceId]=D}c.instances=O}let C=w.credentials!==void 0||w.credentialsByInstance!==void 0,R=H(v,l);if(R&&!C)return{written:!1,restartScheduled:!1,files:[],credentials:[],issues:d};p(l,d,`services.${u}.`);let x=w.dryRun===!0,M=Ee(e,k,x);return d.push(...Oe(l)),s(R?null:l,M,d,w.dryRun,!x&&k.length>0)},deleteService(u){let g=[],w=pe(e,t);if(!X(P(e,t).vocabulary,Object.keys(w?.services??{}),u))throw new A("SERVICE_NOT_FOUND",404,`服务 ${u} 不在服务词汇(已装契约包 ∪ 已配置)`);let d={...w?.services??{}};if(!(u in d))return{written:!1,restartScheduled:!1,files:[],credentials:[],issues:g};delete d[u];let v={...w??{services:{}},services:d};$(Y(e,W),`${JSON.stringify(v,null,2)}
|
|
5
|
+
`),t?.invalidate([Y(e,W)]),g.push(...xe(e));let I=r!==void 0;return I&&r?.(),{written:!0,restartScheduled:I,files:[W],credentials:[],issues:g}}}}import{existsSync as ht}from"fs";import{join as It}from"path";function Me(e){let n=It(e,"config","integrations.json");if(ht(n))return{generated:!1,reason:"in-place",services:[]};let r=[];for(let i of P(e).modules)for(let o of i.descriptor.contributes.providers?.slots??[])o.local===!0&&r.push({service:o.service,provider:o.provider,implementation:o.implementation});if(r.length===0)return{generated:!1,reason:"no-local-slots",services:[]};let t={},s=new Set(r.map(i=>i.provider));if(s.size===1){let[i]=s;for(let o of r)t[o.service]={};$(n,`${JSON.stringify({provider:i,services:t},null,2)}
|
|
6
6
|
`)}else{for(let i of r)t[i.service]={provider:i.provider,implementation:i.implementation};$(n,`${JSON.stringify({services:t},null,2)}
|
|
7
|
-
`)}return{generated:!0,reason:"generated",services:r.map(i=>i.service).sort()}}function G(e){let n=e.indexOf(".");return n===-1?e:e.slice(0,n)}function un(e,n){let r=P(e.appDir,e.cache),t=z(e.appDir,e.cache);return{appDir:e.appDir,walk:r,catalog:r.catalog,integrations:t,contracts:n}}function pn(e){let n=new Set;for(let r of e.modules){for(let t of r.descriptor.contributes.services??[])n.add(t.service);for(let t of r.descriptor.contributes.providers?.slots??[])n.add(t.service)}return n}function fn(e,n,r){let t=e.providers[n];return t?Object.values(t).some(a=>a.services.includes(r)):!1}function gn(e,n){let r=[];for(let[t,a]of Object.entries(e.providers))Object.values(a).some(i=>i.services.includes(n))&&r.push(t);return r.sort()}function Ce(e){return{modules:e.walk.modules.map(r=>({id:r.id,name:r.descriptor.name,title:r.descriptor.name,description:""})).sort((r,t)=>r.id.localeCompare(t.id))}}function mn(e,n){let r=new Set(e.map(t=>G(t.service)));return r.size===1?[...r][0]:n}function Ae(e,n){let r=e.walk.modules.find(g=>g.id===n);if(!r)throw new O("MODULE_NOT_FOUND",404,`模块 ${n} 非已装模块`);let t=(r.descriptor.contributes.services??[]).map(g=>({service:g.service,...g.title?{title:g.title}:{},...g.description?{description:g.description}:{},required:g.optional!==!1})),a=e.integrations?.domains??{},i=mn(t,n);return{integration:a[i]??null,services:t}}function xe(e){let n=pn(e.walk),r=e.walk.vocabulary.filter(i=>!n.has(i)),t=e.integrations,a=null;if(t){let i={};for(let o of H)o in t&&(i[o]=t[o]);a=i}return{services:r,integration:a}}function Ee(e,n){let r=e.integrations?.services??{},t=e.catalog.services[n],a=e.walk.vocabulary.includes(n),i=n in r;if(!a&&!i)throw new O("SERVICE_NOT_FOUND",404,`服务 ${n} 不在服务词汇(已装契约包 ∪ 已配置)`);let o=r[n]??null;return{service:n,...t?.title?{title:t.title}:{title:n},...t?.description?{description:t.description}:{},required:t?!t.optional:!1,defaults:t?t.defaults.map(g=>({...g})):[],integration:o,suppliedBy:gn(e.catalog,n)}}import{existsSync as Rn,readFileSync as wn,realpathSync as vn}from"fs";import{isAbsolute as dt,join as ut,relative as pt,resolve as yn,sep as ft}from"path";function Me(e){let n=[];for(let[r,t]of Object.entries(e.catalog.providers)){let a=Object.values(t);a.length!==0&&n.push({provider:r,local:a.every(i=>i.local===!0),owner:a[0].owner})}return{providers:n.sort((r,t)=>r.provider.localeCompare(t.provider))}}function Oe(e,n){let r=e.catalog.providers[n];if(!r)throw new O("PROVIDER_NOT_FOUND",404,`厂商 ${n} 不 ∈ catalog(内联声明厂商无包级档案——身份与表单经组合端点暴露)`);let t={};for(let i of Object.values(r))for(let o of i.services)(t[o]??={implementations:[]}).implementations.push({implementation:i.implementation,credentialType:i.credentialType});let a=Object.values(r);return{provider:n,local:a.every(i=>i.local===!0),owner:a[0].owner,services:t}}function je(e,n,r){let t=e.catalog.providers[n],a=e.integrations?.services??{},i=[];if(t)for(let v of Object.values(t))v.services.includes(r)&&i.push({implementation:v.implementation,credentialType:v.credentialType});let o=a[r]?.provider,g=o&&typeof o=="object"&&o.provider===n?o:void 0;if(i.length===0&&!g)throw t?new O("SUPPLY_NOT_FOUND",404,`厂商 ${n} 未供给服务 ${r}(AI 帮我实现流:PUT 显式绑定后实施)`):new O("PROVIDER_NOT_FOUND",404,`厂商 ${n} 完全未知(不 ∈ catalog 且无任何内联声明)`);i.length===0&&g&&i.push({implementation:a[r]?.implementation??`${n}-inline@1`,credentialType:g.credentialType});let d=null,p=null,m=P(e.appDir).serviceSchemas[`${n}\0${r}`];if(m)d=Sn(e.appDir,m.packageDir,m.configSchema),p=Sn(e.appDir,m.packageDir,m.credentialSchema);else if(g){let v=o?.configSchema;typeof v=="string"&&(d=gt(e.appDir,v))}return{provider:n,service:r,implementations:i,integrationSchemas:{configSchema:d,credentialSchema:p}}}function Sn(e,n,r){if(!r)return null;let t=ut(n,r);if(!Rn(t))return null;try{return JSON.parse(wn(t,"utf8"))}catch{return null}}function gt(e,n){let r=yn(e,"config","schemas"),t=yn(e,n),a=pt(r,t);if(a===""||a.startsWith("..")||dt(a))throw new O("VALIDATION_FAILED",400,"configSchema 引用必须落在 config/schemas/ 内(禁路径逃逸)",["provider.configSchema"]);if(!Rn(t))return null;let i,o;try{i=vn(r),o=vn(t)}catch{return null}if(o!==i&&!o.startsWith(i+ft))throw new O("VALIDATION_FAILED",400,"configSchema 引用经 symlink 逃逸出 config/schemas/(realpath 复核拒绝)",["provider.configSchema"]);try{return JSON.parse(wn(t,"utf8"))}catch{return null}}function In(e){return{version:e.version,evaluation:e.evaluation,strictValidation:e.strictValidation,notes:[...e.notes]}}function mt(e,n,r){let t=e.integrations,a=G(n),i=t?.services?.[n],o=i?.instances?.[r]??i?.instances?.["*"];return o&&o.provider!==void 0?"instance":i?.provider!==void 0?"service":t?.domains?.[a]?.provider!==void 0?"module":"app"}function vt(e,n){if(n===void 0||!n.startsWith("secret://"))return;let r=n.slice(9),t=Y(e,r);if(t)return{type:t.type,ref:n,file:_(r),masked:q(t.values)}}function hn(e,n,r,t,a){let i=n.resolveEffectiveService(e.integrations,r,t,a);if(i.status==="ok"&&i.effective){let o=i.effective.provider,g=typeof o=="string"?o:String(o?.provider??"");return{status:"ok",provider:g,implementation:i.effective.implementation,effective:{provider:g,implementation:i.effective.implementation,...i.effective.config!==void 0?{config:{...i.effective.config}}:{},...i.effective.credentialRef!==void 0?{credentialRef:i.effective.credentialRef}:{},layer:mt(e,r,t)}}}return i.status==="binding-unresolved"?{status:"binding-unresolved",statusMessage:i.message}:{status:i.status}}function Te(e){let n=e.contracts.module;if(!n.resolveEffectiveService||!n.normalizeInstances)throw new Error("contracts 求值面缺席(工厂门控应先行拦截)");return n}function Ne(e,n){let r=Te(n),{integrations:t,catalog:a}=n,i=r.buildSupplyLookup(a),o=t,d=r.normalizeInstances({instances:o?.instances,defaultInstanceId:o?.defaultInstanceId}).defaultInstanceId??"*",p=hn(n,r,e,d,i),m=n.integrations?.services?.[e]?.provider,v=p.provider??(typeof m=="string"?m:typeof m=="object"&&m?String(m.provider):void 0),f=new Set(Object.keys(n.catalog.providers)),u=v!==void 0&&f.has(v)?yt(n.catalog,v,e):void 0,k=a.services[e],s=k!==void 0?!k.optional:void 0,l=k?.owners[0];return{status:p.status,...p.statusMessage?{statusMessage:p.statusMessage}:{},...v!==void 0?{provider:v}:{},...u!==void 0?{supplied:u}:{},...l!==void 0?{module:l}:{},...s!==void 0?{required:s}:{}}}function yt(e,n,r){return fn(e,n,r)}function Pe(e,n,r){let t=new Set([...e.walk.vocabulary,...Object.keys(e.catalog.services)]),a=e.integrations?.services??{};for(let o of Object.keys(a))t.add(o);let i={};for(let o of[...t].sort())r&&!r.includes(o)||(i[o]=Ne(o,e));return{contracts:In(e.contracts),resolutions:i,issues:n}}function De(e,n,r){let t=Te(n),a=t.buildSupplyLookup(n.catalog),i=n.integrations,g=t.normalizeInstances({instances:i?.instances,defaultInstanceId:i?.defaultInstanceId}).instances.map(s=>s.id),d=n.integrations?.services?.[e],p=d?.instances?Object.keys(d.instances):void 0,m;g.length>0?(m=[...g],p?.includes("*")&&m.push("*")):p!==void 0&&p.length>0?m=p.includes("*")?["*"]:p:m=[];let v=m.map(s=>{let l=hn(n,t,e,s,a),h=vt(n.appDir,l.effective?.credentialRef);return{instanceId:s,status:l.status,...l.statusMessage?{statusMessage:l.statusMessage}:{},...l.effective?{effective:l.effective}:{},...h?{credential:h}:{}}}),f=St(n,e),k={...Ne(e,n),instances:v,inheritance:f};return{contracts:In(n.contracts),resolution:k,issues:r.filter(s=>s.service===e)}}function St(e,n){let r=e.integrations,t=G(n),a=r?.services?.[n],i=e.integrations,g=Te(e).normalizeInstances({instances:i?.instances,defaultInstanceId:i?.defaultInstanceId}).defaultInstanceId??"*",d=a?.instances?.[g]??a?.instances?.["*"];return[{layer:"app",...r?.provider?{provider:r.provider}:{}},{layer:"module",...r?.domains?.[t]?.provider?{provider:r.domains[t].provider}:{}},{layer:"service",...typeof a?.provider=="string"?{provider:a.provider}:{},...a?.implementation?{implementation:a.implementation}:{}},{layer:"instance",...d?.provider?{provider:d.provider}:{},...d?.implementation?{implementation:d.implementation}:{}}]}import Rt from"path";function wt(e,n={}){let r=ce(),t=Promise.resolve(),a={appDir:e,cache:r};function i(s){if(!s.strictValidation)throw new O("CONTRACTS_NOT_RESOLVED",503,Ie(s))}function o(s){return un(a,s)}async function g(){let s=await M(e),l=o(s),h=l.integrations;if(h===null)return[];let C={resolveEffectiveService:d(s.module.resolveEffectiveService,"resolveEffectiveService"),buildSupplyLookup:d(s.module.buildSupplyLookup,"buildSupplyLookup"),moduleDomainOf:d(s.module.moduleDomainOf,"moduleDomainOf"),normalizeInstances:d(s.module.normalizeInstances,"normalizeInstances"),RESOURCE_TYPE_IDS:s.module.RESOURCE_TYPE_IDS??[]},j=ge(e,l.walk,h,process.env);return X({contracts:C,integrations:h,supplies:C.buildSupplyLookup(l.catalog),...j})}function d(s,l){if(typeof s!="function")throw new O("CONTRACTS_NOT_RESOLVED",503,`contracts 求值面 ${l} 缺席(形态异常)`);return s}function p(s){let l=t.then(s,s);return t=l.catch(()=>{}),l}let m={async loadModules(){let s=await M(e);return Ce(o(s))},async loadModule(s){let l=await M(e);return Ae(o(l),s)},async loadApp(){let s=await M(e);return xe(o(s))},async loadService(s){let l=await M(e);return Ee(o(l),s)},async loadServiceResolutions(s){let l=await M(e);i(l);let h=await g();return Pe(o(l),h,s)},async loadServiceResolution(s){let l=await M(e);i(l);let h=await g();return De(s,o(l),h)},async loadProviders(){let s=await M(e);return Me(o(s))},async loadProvider(s){let l=await M(e);return Oe(o(l),s)},async loadProviderService(s,l){let h=await M(e);return je(o(h),s,l)}},v=ke(e,{onApplied:n.onApplied,cache:r}),f={async writeAppIntegration(s,l){await v.warmup();let h=await M(e);return i(h),p(()=>v.applyApp(s,l))},async writeModuleIntegration(s,l,h){await v.warmup();let C=await M(e);return i(C),p(()=>v.applyModule(s,l,h))},async writeServiceIntegration(s,l,h){await v.warmup();let C=await M(e);return i(C),p(()=>v.applyService(s,l,h))},async deleteServiceIntegration(s){await v.warmup();let l=await M(e);return i(l),p(()=>v.deleteService(s))}},u={invalidate(){r.invalidate(),le(e)},async resolveContracts(){return M(e)},walkManifests(){return k(e)},parseModuleDescriptor(s){return J(s)},loadProviderCatalogRaw(){return ye(e)},async readIntegrationsConfig(){return z(e,r)},async writeIntegrationsConfig(s){await v.warmup();let l=await M(e);return i(l),p(()=>It(e,s,r))},async ensureMockBindings(){await v.warmup();let s=await M(e);return i(s),p(()=>be(e))},async evaluateIntegrationServices(s){await v.warmup();let l=await M(e);i(l);let h={resolveEffectiveService:d(l.module.resolveEffectiveService,"resolveEffectiveService"),buildSupplyLookup:d(l.module.buildSupplyLookup,"buildSupplyLookup"),moduleDomainOf:d(l.module.moduleDomainOf,"moduleDomainOf"),normalizeInstances:d(l.module.normalizeInstances,"normalizeInstances"),RESOURCE_TYPE_IDS:l.module.RESOURCE_TYPE_IDS??[]};return X({...s,contracts:h})}};function k(s){return P(s,r)}return{...m,...f,...u}}function It(e,n,r){let t=Rt.join(e,"config","integrations.json");$(t,`${JSON.stringify(n,null,2)}
|
|
8
|
-
`),r.invalidate([t])}export{
|
|
7
|
+
`)}return{generated:!0,reason:"generated",services:r.map(i=>i.service).sort()}}function je(e){return{modules:e.walk.modules.map(r=>({id:r.id,name:r.descriptor.name,title:r.descriptor.name,description:""})).sort((r,t)=>r.id.localeCompare(t.id))}}function Ne(e,n){let r=e.walk.modules.find(p=>p.id===n);if(!r)throw new A("MODULE_NOT_FOUND",404,`模块 ${n} 非已装模块`);let t=(r.descriptor.contributes.services??[]).map(p=>({service:p.service,...p.title?{title:p.title}:{},...p.description?{description:p.description}:{},required:p.optional===!1})),s=z(e.walk,r),i=e.integrations?.domains??{},o=s.writable?i[s.domainKey]??null:null,m=s.domainKeys.map(p=>{let u=_(e.integrations,p);return{domain:p,effective:u.provider,layer:u.layer}});return{domainKeys:[...s.domainKeys],integration:o,providerBindings:m,configEditable:s.writable,services:t}}function Cn(e,n){let r=P(e.appDir,e.cache),t=J(e.appDir,e.cache);return{appDir:e.appDir,walk:r,catalog:r.catalog,integrations:t,contracts:n}}function An(e){let n=new Set;for(let r of e.modules){for(let t of r.descriptor.contributes.services??[])n.add(t.service);for(let t of r.descriptor.contributes.providers?.slots??[])n.add(t.service)}return n}function ge(e,n,r){let t=e.providers[n];return t?Object.values(t).some(s=>s.services.includes(r)):!1}function En(e,n){let r=[];for(let[t,s]of Object.entries(e.providers))Object.values(s).some(i=>i.services.includes(n))&&r.push(t);return r.sort()}function Pe(e){let n=An(e.walk),r=e.walk.vocabulary.filter(i=>!n.has(i)).map(i=>({service:i,required:!1})),t=e.integrations,s=null;if(t){let i={};for(let o of ee)o in t&&(i[o]=t[o]);s=i}return{services:r,integration:s}}function De(e,n){let r=e.integrations?.services??{},t=e.catalog.services[n];if(!X(e.walk.vocabulary,Object.keys(r),n))throw new A("SERVICE_NOT_FOUND",404,`服务 ${n} 不在服务词汇(已装契约包 ∪ 已配置)`);let s=r[n]??null;return{service:n,...t?.title?{title:t.title}:{title:n},...t?.description?{description:t.description}:{},required:t?!t.optional:!1,defaults:t?t.defaults.map(i=>({...i})):[],integration:s,suppliedBy:En(e.catalog,n)}}import{existsSync as Tn,readFileSync as Mn,realpathSync as xn}from"fs";import{isAbsolute as Rt,join as kt,relative as bt,resolve as On,sep as Ct}from"path";function Ve(e){let n=[];for(let[r,t]of Object.entries(e.catalog.providers)){let s=Object.values(t);s.length!==0&&n.push({provider:r,local:s.every(i=>i.local===!0),owner:s[0].owner})}return{providers:n.sort((r,t)=>r.provider.localeCompare(t.provider))}}function $e(e,n,r){let t=e.catalog.providers[n];if(!t)throw new A("PROVIDER_NOT_FOUND",404,`厂商 ${n} 不 ∈ catalog(内联声明厂商无包级档案——身份与表单经组合端点暴露)`);let s={};for(let o of Object.values(t))for(let m of o.services)(s[m]??={implementations:[]}).implementations.push({implementation:o.implementation,credentialType:o.credentialType});if(r?.has("integrationSchemas"))for(let[o,m]of Object.entries(s)){let p=e.walk.serviceSchemas[`${n}\0${o}`];m.integrationSchemas={configSchema:p?Z(e.appDir,p.packageDir,p.configSchema):null}}let i=Object.values(t);return{provider:n,local:i.every(o=>o.local===!0),owner:i[0].owner,services:s}}function _e(e,n,r){let t=e.catalog.providers[n],s=e.integrations?.services??{},i=[];if(t)for(let g of Object.values(t))g.services.includes(r)&&i.push({implementation:g.implementation,credentialType:g.credentialType});let o=s[r]?.provider,m=o&&typeof o=="object"&&o.provider===n?o:void 0;if(i.length===0&&!m)throw t?new A("SUPPLY_NOT_FOUND",404,`厂商 ${n} 未供给服务 ${r}(AI 帮我实现流:PUT 显式绑定后实施)`):new A("PROVIDER_NOT_FOUND",404,`厂商 ${n} 完全未知(不 ∈ catalog 且无任何内联声明)`);i.length===0&&m&&i.push({implementation:s[r]?.implementation??`${n}-inline@1`,credentialType:m.credentialType});let p=null,u=e.walk.serviceSchemas[`${n}\0${r}`];if(u)p=Z(e.appDir,u.packageDir,u.configSchema);else if(m){let g=o?.configSchema;typeof g=="string"&&(p=At(e.appDir,g))}return{provider:n,service:r,implementations:i,integrationSchemas:{configSchema:p}}}function Z(e,n,r){if(!r)return null;let t=kt(n,r);if(!Tn(t))return null;try{return JSON.parse(Mn(t,"utf8"))}catch{return null}}function At(e,n){let r=On(e,"config","schemas"),t=On(e,n),s=bt(r,t);if(s===""||s.startsWith("..")||Rt(s))throw new A("VALIDATION_FAILED",400,"configSchema 引用必须落在 config/schemas/ 内(禁路径逃逸)",["provider.configSchema"]);if(!Tn(t))return null;let i,o;try{i=xn(r),o=xn(t)}catch{return null}if(o!==i&&!o.startsWith(i+Ct))throw new A("VALIDATION_FAILED",400,"configSchema 引用经 symlink 逃逸出 config/schemas/(realpath 复核拒绝)",["provider.configSchema"]);try{return JSON.parse(Mn(t,"utf8"))}catch{return null}}function jn(e){return{version:e.version,evaluation:e.evaluation,strictValidation:e.strictValidation,notes:[...e.notes]}}function Nn(e,n,r){let t=e.integrations,s=K(n),i=t?.services?.[n],o=i?.instances?.[r]??i?.instances?.["*"];return o&&o.provider!==void 0?"instance":i?.provider!==void 0?"service":_(t,s).layer==="module"?"module":typeof t?.provider=="string"&&t.provider.length>0?"app":null}function Pn(e,n,r,t,s){let i=n.resolveEffectiveService(e.integrations,r,t,s);if(i.status==="ok"&&i.effective){let o=i.effective.provider,m=typeof o=="string"?o:String(o?.provider??"");return{status:"ok",provider:m,implementation:i.effective.implementation,effective:{provider:m,implementation:i.effective.implementation,...i.effective.config!==void 0?{config:{...i.effective.config}}:{},...i.effective.credentialRef!==void 0?{credentialRef:i.effective.credentialRef}:{},layer:Nn(e,r,t)??"app"}}}return i.status==="binding-unresolved"?{status:"binding-unresolved",statusMessage:i.message}:{status:i.status}}function Fe(e){let n=e.contracts.module;if(!n.resolveEffectiveService||!n.normalizeInstances)throw new A("CONTRACTS_NOT_RESOLVED",503,"contracts 求值面缺席(契约包形态异常)——重新安装或升级应用 @tbox.cn/app-contracts 后重试");return n}function Le(e,n){let r=Fe(n),{integrations:t,catalog:s}=n,i=r.buildSupplyLookup(s),o=t,p=r.normalizeInstances({instances:o?.instances,defaultInstanceId:o?.defaultInstanceId}).defaultInstanceId??"*",u=Pn(n,r,e,p,i),g=Nn(n,e,p),w=n.integrations?.services?.[e]?.provider,d=u.provider??(typeof w=="string"?w:typeof w=="object"&&w?String(w.provider):void 0),v=new Set(Object.keys(n.catalog.providers)),I=d!==void 0&&v.has(d)?Et(n.catalog,d,e):void 0,a=s.services[e],l=a!==void 0?!a.optional:void 0,f=a?.owners[0];return{status:u.status,...u.statusMessage?{statusMessage:u.statusMessage}:{},...d!==void 0?{provider:d}:{},...d!==void 0&&g!==null?{layer:g}:{},...I!==void 0?{supplied:I}:{},...f!==void 0?{module:f}:{},...l!==void 0?{required:l}:{}}}function Et(e,n,r){return ge(e,n,r)}function ne(e,n,r){let t=new Set([...e.walk.vocabulary,...Object.keys(e.catalog.services)]),s=e.integrations?.services??{};for(let o of Object.keys(s))t.add(o);let i={};for(let o of[...t].sort())r&&!r.includes(o)||(i[o]=Le(o,e));return{contracts:jn(e.contracts),resolutions:i,issues:n}}function We(e,n,r){let t=Fe(n),s=t.buildSupplyLookup(n.catalog),i=n.integrations,m=t.normalizeInstances({instances:i?.instances,defaultInstanceId:i?.defaultInstanceId}).instances.map(a=>a.id),p=n.integrations?.services?.[e],u=p?.instances?Object.keys(p.instances):void 0,g;m.length>0?(g=[...m],u?.includes("*")&&g.push("*")):u!==void 0&&u.length>0?g=u.includes("*")?["*"]:u:g=[];let w=g.map(a=>{let l=Pn(n,t,e,a,s);return{instanceId:a,status:l.status,...l.statusMessage?{statusMessage:l.statusMessage}:{},...l.effective?{effective:l.effective}:{}}}),d=xt(n,e),I={...Le(e,n),instances:w,inheritance:d};return{contracts:jn(n.contracts),resolution:I,issues:r.filter(a=>a.service===e)}}function xt(e,n){let r=e.integrations,t=K(n),s=r?.services?.[n],i=e.integrations,m=Fe(e).normalizeInstances({instances:i?.instances,defaultInstanceId:i?.defaultInstanceId}).defaultInstanceId??"*",p=s?.instances?.[m]??s?.instances?.["*"],u=_(r,t);return[{layer:"app",...r?.provider?{provider:r.provider}:{}},{layer:"module",...u.layer==="module"&&u.provider?{provider:u.provider}:{}},{layer:"service",...typeof s?.provider=="string"?{provider:s.provider}:{},...s?.implementation?{implementation:s.implementation}:{}},{layer:"instance",...p?.provider?{provider:p.provider}:{},...p?.implementation?{implementation:p.implementation}:{}}]}function Be(e,n,r){if(n===null)return null;let t={};for(let s of r){let i=e.walk.serviceSchemas[`${n}\0${s}`];t[s]={configSchema:i?Z(e.appDir,i.packageDir,i.configSchema):null}}return{provider:n,services:t}}function Ke(e){let n=e.integrations?.provider,r=typeof n=="string"&&n.length>0?n:null;return r===null?null:Be(e,r,sn(e.catalog,r))}function ze(e,n){let r=e.walk.modules.find(o=>o.id===n);if(!r)return null;let t=z(e.walk,r);if(!t.writable)return null;let s=_(e.integrations,t.domainKey).provider;if(s===null)return null;let i=(r.descriptor.contributes.services??[]).map(o=>o.service);return Be(e,s,i.filter(o=>ge(e.catalog,s,o)))}function te(e,n){if(n===void 0||!n.startsWith("secret://"))return;let r=n.slice(9),t=q(e,r);if(t)return{type:t.type,ref:n,file:F(r),masked:re(t.values)}}function Ue(e){let n=e.integrations,r={credential:te(e.appDir,typeof n?.credentialRef=="string"?n.credentialRef:void 0)??null,byInstance:{}};for(let{id:s}of n?.instances??[]){let i=n?.credentialRefByInstance?.[s];i!==void 0&&(r.byInstance[s]=te(e.appDir,i)??null)}let t={};for(let[s,i]of Object.entries(n?.services??{})){let o={credential:te(e.appDir,i.credentialRef)??null,byInstance:{}};for(let[m,p]of Object.entries(i.instances??{}))o.byInstance[m]=te(e.appDir,p.credentialRef)??null;t[s]=o}return{app:r,services:t}}function Je(e,n){return{types:(n===void 0?Object.keys(e.catalog.credentialTypes):[...new Set(Object.values(e.catalog.providers[n]??{}).map(t=>t.credentialType))]).sort().map(t=>{let s=e.catalog.credentialTypes[t];return{credentialType:t,owner:s?.owner??"",credentialSchema:s?Z(e.appDir,s.packageDir,s.credentialSchema):null}})}}import Ot from"path";var Tt={app:["serviceResolutions","integrationSchemas"],module:["serviceResolutions","integrationSchemas"],provider:["integrationSchemas"]};function Ye(e,n){if(!n||n.length===0)return new Set;let r=Tt[e],t=[...new Set(n)].filter(s=>!r.includes(s));if(t.length>0)throw new A("VALIDATION_FAILED",400,`include 含未知或不适用于本端点的键:${t.join("、")}(白名单:${r.join("、")})`,["include"]);return new Set(n)}function Mt(e,n={}){let r=me(),t=Promise.resolve(),s={appDir:e,cache:r};function i(a){if(!a.strictValidation)throw new A("CONTRACTS_NOT_RESOLVED",503,ye(a))}function o(a){return Cn(s,a)}async function m(){let a=await T(e),l=o(a),f=l.integrations;if(f===null)return[];let b={resolveEffectiveService:p(a.module.resolveEffectiveService,"resolveEffectiveService"),buildSupplyLookup:p(a.module.buildSupplyLookup,"buildSupplyLookup"),moduleDomainOf:p(a.module.moduleDomainOf,"moduleDomainOf"),normalizeInstances:p(a.module.normalizeInstances,"normalizeInstances"),RESOURCE_TYPE_IDS:a.module.RESOURCE_TYPE_IDS??[]},E=Re(e,l.walk,f,process.env);return oe({contracts:b,integrations:f,supplies:b.buildSupplyLookup(l.catalog),...E})}function p(a,l){if(typeof a!="function")throw new A("CONTRACTS_NOT_RESOLVED",503,`contracts 求值面 ${l} 缺席(形态异常)`);return a}function u(a){let l=t.then(a,a);return t=l.catch(()=>{}),l}let g={async loadModules(){let a=await T(e);return je(o(a))},async loadModule(a,l){let f=Ye("module",l?.include),b=await T(e),E=o(b),y=Ne(E,a);return f.has("serviceResolutions")&&(y.serviceResolutions=b.evaluation&&b.strictValidation?await m().then(k=>ne(E,k,y.services.map(c=>c.service))):null),f.has("integrationSchemas")&&(y.integrationSchemas=ze(E,a)),y},async loadApp(a){let l=Ye("app",a?.include),f=await T(e),b=o(f),E=Pe(b);return l.has("serviceResolutions")&&(E.serviceResolutions=f.evaluation&&f.strictValidation?await m().then(y=>ne(b,y)):null),l.has("integrationSchemas")&&(E.integrationSchemas=Ke(b)),E},async loadService(a){let l=await T(e);return De(o(l),a)},async loadServiceResolutions(a){let l=await T(e);i(l);let f=await m();return ne(o(l),f,a)},async loadServiceResolution(a){let l=await T(e);i(l);let f=await m();return We(a,o(l),f)},async loadProviders(){let a=await T(e);return Ve(o(a))},async loadProvider(a,l){let f=Ye("provider",l?.include),b=await T(e);return $e(o(b),a,f)},async loadProviderService(a,l){let f=await T(e);return _e(o(f),a,l)},async loadCredentials(){let a=await T(e);return Ue(o(a))},async loadCredentialTypes(a){let l=await T(e);return Je(o(l),a)}},w=Te(e,{onApplied:n.onApplied,cache:r}),d={async writeAppIntegration(a,l){await w.warmup();let f=await T(e);return i(f),u(()=>w.applyApp(a,l))},async writeModuleIntegration(a,l,f){await w.warmup();let b=await T(e);return i(b),u(()=>w.applyModule(a,l,f))},async writeServiceIntegration(a,l,f){await w.warmup();let b=await T(e);return i(b),u(()=>w.applyService(a,l,f))},async deleteServiceIntegration(a){await w.warmup();let l=await T(e);return i(l),u(()=>w.deleteService(a))}},v={invalidate(){r.invalidate(),ve(e)},async resolveContracts(){return T(e)},walkManifests(){return I(e)},parseModuleDescriptor(a){return G(a)},loadProviderCatalogRaw(){return Ce(e)},async readIntegrationsConfig(){return J(e,r)},async writeIntegrationsConfig(a){await w.warmup();let l=await T(e);return i(l),u(()=>jt(e,a,r))},async ensureMockBindings(){await w.warmup();let a=await T(e);return i(a),u(()=>Me(e))},async evaluateIntegrationServices(a){await w.warmup();let l=await T(e);i(l);let f={resolveEffectiveService:p(l.module.resolveEffectiveService,"resolveEffectiveService"),buildSupplyLookup:p(l.module.buildSupplyLookup,"buildSupplyLookup"),moduleDomainOf:p(l.module.moduleDomainOf,"moduleDomainOf"),normalizeInstances:p(l.module.normalizeInstances,"normalizeInstances"),RESOURCE_TYPE_IDS:l.module.RESOURCE_TYPE_IDS??[]};return oe({...a,contracts:f})}};function I(a){return P(a,r)}return{...g,...d,...v}}function jt(e,n,r){let t=Ot.join(e,"config","integrations.json");$(t,`${JSON.stringify(n,null,2)}
|
|
8
|
+
`),r.invalidate([t])}export{ee as APP_INTEGRATION_NODE_KEYS,A as AppToolkitError,L as SAFE_NAME_PATTERN,ot as assertValidModuleId,Re as buildPredicateAssembly,he as checkInstanceKeys,dt as collectDeclaredModules,ye as contractsNotResolvedMessage,Mt as createAppToolkit,me as createFileCache,Te as createWriteCore,vt as credentialArtifactExists,H as deepEqual,B as deriveStem,te as echoForRef,rn as ensureDir,Me as ensureMockBindings,oe as evaluateIntegrationServices,_n as expandEnvVars,mn as extractModuleIdFromSource,ve as invalidateContractsResolver,we as isControlledInlineProvider,fn as isValidModuleId,Ke as loadAppIntegrationSchemas,Pe as loadAppView,Je as loadCredentialTypesView,Ue as loadCredentialsView,Ne as loadModuleDetailView,ze as loadModuleIntegrationSchemas,je as loadModulesView,vn as loadProviderCatalog,Ce as loadProviderCatalogRaw,$e as loadProviderDetailView,_e as loadProviderServiceDetailView,Ve as loadProvidersView,De as loadServiceDetailView,We as loadServiceResolutionDetail,ne as loadServiceResolutionsView,ae as manifestPath,re as maskCredentialValues,Ge as moduleDependencySchema,en as moduleDescriptorSchema,He as pageContribSchema,G as parseModuleDescriptor,qe as providerSlotContribSchema,pn as readAppManifest,ke as readAppManifestFull,q as readCredentialFile,J as readIntegrationsConfig,Sn as readIntegrationsStrict,xe as reconcileOrphanCredentials,ln as refToEnvKey,tt as removeNpmModule,T as resolveContractsForApp,T as resolveContractsForAppExport,Be as resolveLayerIntegrationSchemas,Le as resolveServiceSummary,Xe as resourceNeedContribSchema,Qe as serviceNeedContribSchema,F as stemToFilePath,Kn as stemToRef,Ze as tabContribSchema,it as updateNpmModule,rt as upsertMiniappContainer,nt as upsertNpmModule,P as walkManifests,et as writeAppManifest,$ as writeAtomic,Se as writeCredentialFile};
|
package/package.json
CHANGED
|
@@ -71,7 +71,12 @@ export function loadProviderCatalog(appDir: string): BridgeProviderCatalog {
|
|
|
71
71
|
}
|
|
72
72
|
const credentialTypes: BridgeProviderCatalog['credentialTypes'] = {};
|
|
73
73
|
for (const [ct, entry] of Object.entries(rich.credentialTypes)) {
|
|
74
|
-
|
|
74
|
+
// 桥源形态冻结(v4.4 注意):rich 条目新增的 packageDir(凭据结构清单解析位)不进桥——
|
|
75
|
+
// 显式投影保桥面 = contracts TIER0 形状(owner/credentialSchema),不随富形态漂移
|
|
76
|
+
credentialTypes[ct] = {
|
|
77
|
+
owner: entry.owner,
|
|
78
|
+
...(entry.credentialSchema ? { credentialSchema: entry.credentialSchema } : {}),
|
|
79
|
+
};
|
|
75
80
|
}
|
|
76
81
|
return { providers, services, credentialTypes };
|
|
77
82
|
}
|
|
@@ -74,7 +74,7 @@ export interface WalkCatalog {
|
|
|
74
74
|
owners: string[];
|
|
75
75
|
}
|
|
76
76
|
>;
|
|
77
|
-
credentialTypes: Record<string, { owner: string; credentialSchema?: string }>;
|
|
77
|
+
credentialTypes: Record<string, { owner: string; packageDir: string; credentialSchema?: string }>;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
/** loose 允许的 contributes 局部结构(本地声明——解跨模块类型身份耦合,TS2719 防线) */
|
|
@@ -252,6 +252,7 @@ export function walkManifests(appDir: string, cache?: FileCache): WalkManifestsR
|
|
|
252
252
|
};
|
|
253
253
|
catalog.credentialTypes[slot.credentialType] ??= {
|
|
254
254
|
owner,
|
|
255
|
+
packageDir,
|
|
255
256
|
...(slot.credentialSchema ? { credentialSchema: slot.credentialSchema } : {}),
|
|
256
257
|
};
|
|
257
258
|
}
|
|
@@ -244,9 +244,10 @@ export function resolveContractsForApp(appDir: string): Promise<ResolvedContract
|
|
|
244
244
|
const resolved = await probeCandidate(candidate, notes);
|
|
245
245
|
if (resolved) {
|
|
246
246
|
if (resolved.version !== null && !resolved.strictValidation) {
|
|
247
|
-
// 注:push 进 resolved.notes(probeCandidate 返回体自带 notes 字段——调用侧 notes 仅供 null
|
|
247
|
+
// 注:push 进 resolved.notes(probeCandidate 返回体自带 notes 字段——调用侧 notes 仅供 null 路径聚合);
|
|
248
|
+
// 文案对齐 api.md §5 信封(P-4 notes 优先后该 note 即实际 message)
|
|
248
249
|
resolved.notes.push(
|
|
249
|
-
`contracts ${resolved.version} 无严格校验面(需 ≥0.9)——升级应用 @tbox.cn/app-contracts 依赖后可用`,
|
|
250
|
+
`contracts ${resolved.version} 无严格校验面(需 ≥0.9.0)——升级应用 @tbox.cn/app-contracts 依赖后可用`,
|
|
250
251
|
);
|
|
251
252
|
}
|
|
252
253
|
// 实际命中候选覆写(多候选走位可觉察——后续命中判定以命中候选为基准;F9)
|
|
@@ -16,6 +16,7 @@ import { dirname, join } from 'node:path';
|
|
|
16
16
|
* 第一道为 descriptor/节点 zod 的字符集 pattern(module-schema SAFE_NAME_PATTERN)。
|
|
17
17
|
*/
|
|
18
18
|
import { maskCredentialValues } from './credential-mask.js';
|
|
19
|
+
import { AppToolkitError } from './errors.js';
|
|
19
20
|
|
|
20
21
|
export { maskCredentialValues };
|
|
21
22
|
|
|
@@ -35,8 +36,9 @@ export interface StemInput {
|
|
|
35
36
|
function assertSafeSegment(kind: string, value: string | undefined): void {
|
|
36
37
|
if (value === undefined) return;
|
|
37
38
|
if (value.includes('/') || value.includes('\\') || value.includes('..')) {
|
|
38
|
-
//
|
|
39
|
-
|
|
39
|
+
// 纵深防御断言(A1:裸 Error → AppToolkitError 400——服务名是路径参数不在节点 zod 覆盖面,
|
|
40
|
+
// 本断言原是该输入面的唯一拦截点;裸 Error 穿透壳层落 500 违 api.md §5)
|
|
41
|
+
throw new AppToolkitError('VALIDATION_FAILED', 400, `凭据 stem 派生输入含路径敌对字符(${kind})——拒绝派生`);
|
|
40
42
|
}
|
|
41
43
|
}
|
|
42
44
|
|
package/src/core/errors.ts
CHANGED
|
@@ -33,14 +33,20 @@ export class AppToolkitError extends Error {
|
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
/** CONTRACTS_NOT_RESOLVED
|
|
36
|
+
/** CONTRACTS_NOT_RESOLVED message(N1 构建指引——api.md 错误信封示例)。
|
|
37
|
+
* P-4:notes 优先——resolver 在全部不可解析路径均已 push 携带操作指引的 notes(导入失败/
|
|
38
|
+
* 产物缺失/未安装/<0.9 升级);现行 version===null 分支会把 notes 内细分指引吞成通用文案。
|
|
39
|
+
* 三分支保留为兜底(防御性——正常路径 notes 恒非空)。 */
|
|
37
40
|
export function contractsNotResolvedMessage(contracts: { version: string | null; strictValidation: boolean; notes: readonly string[] }): string {
|
|
41
|
+
if (contracts.notes.length > 0) {
|
|
42
|
+
return contracts.notes[0];
|
|
43
|
+
}
|
|
38
44
|
if (contracts.version === null) {
|
|
39
45
|
return 'contracts 不可解析(未安装)——检查应用依赖安装后重试';
|
|
40
46
|
}
|
|
41
47
|
if (!contracts.strictValidation) {
|
|
42
|
-
return `contracts ${contracts.version} 无严格校验面(需 ≥0.9)——升级应用 @tbox.cn/app-contracts 依赖后可用`;
|
|
48
|
+
return `contracts ${contracts.version} 无严格校验面(需 ≥0.9.0)——升级应用 @tbox.cn/app-contracts 依赖后可用`;
|
|
43
49
|
}
|
|
44
50
|
// 版本达标但解析态异常(notes 携带细分——导入拒绝/产物缺失)
|
|
45
|
-
return
|
|
51
|
+
return 'contracts workspace 源码态未构建——执行 pnpm --filter @tbox.cn/app-contracts build 后重试';
|
|
46
52
|
}
|
package/src/dto.ts
CHANGED
|
@@ -34,13 +34,13 @@ export interface ModuleDeclaration {
|
|
|
34
34
|
description: string;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
/** 服务需求声明(模块 demand
|
|
37
|
+
/** 服务需求声明(模块 demand 单元 / 平台服务条目——v4.4 D29 双端点同型;defaults 唯一落位 ServiceDetail) */
|
|
38
38
|
export interface ServiceDemand {
|
|
39
39
|
service: string;
|
|
40
40
|
/** manifest demand meta;缺席 UI 回退 service id */
|
|
41
41
|
title?: string;
|
|
42
42
|
description?: string;
|
|
43
|
-
/** demand optional
|
|
43
|
+
/** demand optional === false(多模块 OR 聚合见 resolution echo;平台服务条目恒 false) */
|
|
44
44
|
required: boolean;
|
|
45
45
|
}
|
|
46
46
|
|
|
@@ -48,18 +48,51 @@ export interface ServiceDemand {
|
|
|
48
48
|
|
|
49
49
|
/** GET /modules/:module(模块静态一站式 · PUT 读对偶) */
|
|
50
50
|
export interface ModuleDetailView {
|
|
51
|
-
/**
|
|
51
|
+
/** v4.4 D34:demand 服务首段去重(排序;1..N;零域 = []) */
|
|
52
|
+
domainKeys: string[];
|
|
53
|
+
/** domains[换算键] 节点原值;未配置 = null;v4.4 D34:仅单域(writable)时在场——多域/零域/共享键无单键落点 */
|
|
52
54
|
integration: ModuleIntegrationNode | null;
|
|
55
|
+
/** v4.4 D34:每域一条厂商绑定解析(单域 = 单元素;零域 = [];resolveDomainBinding 单源) */
|
|
56
|
+
providerBindings: ProviderBinding[];
|
|
57
|
+
/** v4.4 D34:单域 && 域键无跨模块共享(moduleDomainWritable 单源——读标志与写守卫共用) */
|
|
58
|
+
configEditable: boolean;
|
|
53
59
|
/** 本模块 demand(卡片展开 + 编辑器静态上下文自足) */
|
|
54
60
|
services: ServiceDemand[];
|
|
61
|
+
/** v4.4 D31:include=serviceResolutions 内嵌(demand 服务过滤 ≡ 批量 ?service= 语义;<0.9/形态异常 → null——永不 503) */
|
|
62
|
+
serviceResolutions?: ServiceResolutionsView | null;
|
|
63
|
+
/** v4.4 D32:include=integrationSchemas 层级富化(多域/零域/共享键/未绑定 → null) */
|
|
64
|
+
integrationSchemas?: LayerIntegrationSchemas | null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** 模块域厂商绑定解析(v4.4 D34——静态文件事实;规则 ≡ 谓词 #7 域级,resolveDomainBinding 单源) */
|
|
68
|
+
export interface ProviderBinding {
|
|
69
|
+
/** 域键(= domainKeys 成员;多域模块每域一条) */
|
|
70
|
+
domain: string;
|
|
71
|
+
/** domains[domain].provider ?? root.provider(双缺 = null) */
|
|
72
|
+
effective: string | null;
|
|
73
|
+
/** 生效来源层(双缺 = null) */
|
|
74
|
+
layer: 'module' | 'app' | null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** 层级 config schema 计算(v4.4 D32——静态轴富化,零 contracts) */
|
|
78
|
+
export interface LayerIntegrationSchemas {
|
|
79
|
+
/** 层生效厂商(resolveDomainBinding 单源) */
|
|
80
|
+
provider: string;
|
|
81
|
+
/** 模块级 = 本模块 demand ∩ 该厂商供给;应用级 = 供给足迹全量(configSchema = raw draft 2020-12 原文;供给在场未声明 = null) */
|
|
82
|
+
services: Record<string, { configSchema: object | null }>;
|
|
55
83
|
}
|
|
56
84
|
|
|
57
85
|
/** GET /app(应用静态视图:平台服务 + root 原值) */
|
|
58
86
|
export interface AppView {
|
|
59
|
-
/**
|
|
60
|
-
|
|
87
|
+
/** 平台服务(v4.4 D29:服务词汇 − 模块占用——与模块 demand 同型 ServiceDemand[];
|
|
88
|
+
* title/description 缺席 = UI 回退 service id;required 恒 false——平台服务无 demand 源) */
|
|
89
|
+
services: ServiceDemand[];
|
|
61
90
|
/** root 节点原值;文件缺席 = null */
|
|
62
91
|
integration: AppIntegrationNode | null;
|
|
92
|
+
/** v4.4 D31:include=serviceResolutions 内嵌(全量键集 ≡ 批量无参;<0.9/形态异常 → null——永不 503) */
|
|
93
|
+
serviceResolutions?: ServiceResolutionsView | null;
|
|
94
|
+
/** v4.4 D32:include=integrationSchemas 层级富化(root.provider 缺席 → null) */
|
|
95
|
+
integrationSchemas?: LayerIntegrationSchemas | null;
|
|
63
96
|
}
|
|
64
97
|
|
|
65
98
|
/** GET /services/:service(服务静态单体) */
|
|
@@ -91,6 +124,9 @@ export interface ServiceResolutionEntry {
|
|
|
91
124
|
statusMessage?: string;
|
|
92
125
|
/** 最特异声明层厂商名(跨层事实) */
|
|
93
126
|
provider?: string;
|
|
127
|
+
/** v4.4:provider 来源声明层(默认实例口径,与 status 同轴);恒 = provider 字段来源层
|
|
128
|
+
* (ok/binding-unresolved 取声明层;未配置双缺席);≡ 单体 inheritance 链逆序首个有 provider 的层 */
|
|
129
|
+
layer?: BindingLayer;
|
|
94
130
|
/** provider 在场时在场:(provider, service) ∈ 供给关系 */
|
|
95
131
|
supplied?: boolean;
|
|
96
132
|
/** owner 模块 install id;平台服务/孤儿缺席 */
|
|
@@ -194,8 +230,7 @@ export interface ResolvedInstance {
|
|
|
194
230
|
statusMessage?: string;
|
|
195
231
|
/** 该实例合并结果 */
|
|
196
232
|
effective?: EffectiveBinding;
|
|
197
|
-
|
|
198
|
-
credential?: CredentialEcho;
|
|
233
|
+
// v4.4 D33:credential(掩码回显)删净——凭据状态归 GET /credentials(求值路径零凭据文件 I/O)
|
|
199
234
|
}
|
|
200
235
|
|
|
201
236
|
// ===== 供给轴(静态)=====
|
|
@@ -222,7 +257,11 @@ export interface ProviderDetail {
|
|
|
222
257
|
* 多元素 = 升级窗口,UI 出 impl 选择器,PUT 显式钉版) */
|
|
223
258
|
services: Record<
|
|
224
259
|
string,
|
|
225
|
-
{
|
|
260
|
+
{
|
|
261
|
+
implementations: Array<{ implementation: string; credentialType: string }>;
|
|
262
|
+
/** v4.4 F5:include=integrationSchemas 切换态批量(纯 config——凭据结构归 /credential-types) */
|
|
263
|
+
integrationSchemas?: { configSchema: object | null };
|
|
264
|
+
}
|
|
226
265
|
>;
|
|
227
266
|
}
|
|
228
267
|
|
|
@@ -230,16 +269,47 @@ export interface ProviderDetail {
|
|
|
230
269
|
export interface ProviderServiceDetail {
|
|
231
270
|
provider: string;
|
|
232
271
|
service: string;
|
|
233
|
-
/** ★ 恒数组(通常 1
|
|
272
|
+
/** ★ 恒数组(通常 1);credentialType 保留(/credential-types 寻址键) */
|
|
234
273
|
implementations: Array<{ implementation: string; credentialType: string }>;
|
|
235
|
-
/** 厂商集成该服务的表单 schema(integration
|
|
274
|
+
/** 厂商集成该服务的表单 schema(integration 域专属)——v4.4 D33 纯 config(credentialSchema 删净) */
|
|
236
275
|
integrationSchemas: {
|
|
237
276
|
/** raw draft 2020-12 原文零转换;供给在场未声明 = null(mock 合法);解析自默认 impl */
|
|
238
277
|
configSchema: object | null;
|
|
239
|
-
credentialSchema: object | null;
|
|
240
278
|
};
|
|
241
279
|
}
|
|
242
280
|
|
|
281
|
+
// ===== 凭据域(v4.4 D33 独立端点)=====
|
|
282
|
+
|
|
283
|
+
/** GET /credentials:绑定位全量掩码回显(应用状态轴;GET 永无真值) */
|
|
284
|
+
export interface CredentialsView {
|
|
285
|
+
/** root 位;byInstance 键集 = 注册表 ids ∩ 有 ref(无 ref 的注册实例不出现;未注册 ref 跳过不虚报) */
|
|
286
|
+
app: CredentialBindingEcho;
|
|
287
|
+
/** 键集 = 已配置服务;byInstance 键集 = 节点 instances map 全键(含 '*')——键域不对称 = 文件语义忠实镜像 */
|
|
288
|
+
services: Record<string, CredentialBindingEcho>;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/** 一个形状两处(镜像文件层「标量 + ByInstance」模式) */
|
|
292
|
+
export interface CredentialBindingEcho {
|
|
293
|
+
/** 本位 credentialRef(root.credentialRef / services[s].credentialRef);ref 缺席/文件缺失 → null */
|
|
294
|
+
credential: CredentialEcho | null;
|
|
295
|
+
/** 覆盖位(credentialRefByInstance / instances[k].credentialRef) */
|
|
296
|
+
byInstance: Record<string, CredentialEcho | null>;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/** GET /credential-types[?provider=]:凭据结构清单(供给轴静态;?provider= 过滤语义 ≡ ?service=) */
|
|
300
|
+
export interface CredentialTypesView {
|
|
301
|
+
types: CredentialTypeEntry[];
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
export interface CredentialTypeEntry {
|
|
305
|
+
/** '-' = 无凭据语义(mock/local——UI 判 '-' 不渲染凭据表单) */
|
|
306
|
+
credentialType: string;
|
|
307
|
+
/** 声明来源包名 */
|
|
308
|
+
owner: string;
|
|
309
|
+
/** raw draft 2020-12 原文;未声明 = null(ExtraFields 自由键值同轨) */
|
|
310
|
+
credentialSchema: object | null;
|
|
311
|
+
}
|
|
312
|
+
|
|
243
313
|
// ===== 通用 =====
|
|
244
314
|
|
|
245
315
|
export interface EffectiveBinding {
|