@tbox.cn/app-toolkit 0.2.0 → 0.4.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/dist/index.d.ts +181 -33
- package/dist/index.js +8 -7
- package/package.json +2 -2
- package/src/assembly/app-manifest.ts +6 -0
- package/src/assembly/walk-manifests.ts +95 -28
- package/src/core/env-expansion.ts +14 -12
- package/src/core/module-schema.ts +3 -0
- package/src/dto.ts +59 -4
- package/src/factory.ts +58 -16
- package/src/index.ts +8 -2
- package/src/integrations/credentials.ts +1 -1
- package/src/integrations/evaluate-app.ts +41 -0
- package/src/integrations/predicate.ts +2 -1
- package/src/integrations/prune-bindings.ts +186 -0
- package/src/integrations/write.ts +15 -11
- package/src/views/app.ts +6 -2
- package/src/views/context.ts +20 -0
- package/src/views/integration-schemas.ts +197 -24
- package/src/views/modules.ts +42 -13
- package/src/views/providers.ts +39 -21
- package/src/views/service-detail.ts +5 -3
- package/tests/demo-app.ts +27 -3
- package/tests/dev-manifest-catalog.test.ts +81 -1
- package/tests/file-cache.test.ts +3 -1
- package/tests/module-schema.test.ts +15 -0
- package/tests/vendor-schema-keywords.test.ts +12 -0
- package/tests/views.test.ts +1098 -74
- package/tests/write-core.test.ts +26 -4
package/dist/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import{a as
|
|
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
|
-
`),
|
|
5
|
-
`),t?.invalidate([
|
|
1
|
+
import{a as ke,b as j,c as be}from"./chunk-KPD3LUH4.js";import{z as R}from"zod";var K=/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/,qn=R.object({cardType:R.string(),schemaVersion:R.number().optional()}),tn=R.object({route:R.string(),component:R.string()}),rn=R.object({route:R.string(),label:R.string().optional(),icon:R.string().optional(),order:R.number().optional()}),on=R.object({id:R.string(),range:R.string().optional(),required:R.boolean().default(!0)}),sn=R.object({service:R.string(),provider:R.string().regex(K,"provider 名含非法字符(需 /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/)"),implementation:R.string(),credentialType:R.string(),local:R.boolean().optional(),configSchema:R.string().optional(),credentialSchema:R.string().optional()}),an=R.object({service:R.string(),optional:R.boolean().default(!0),defaults:R.array(R.object({provider:R.string().regex(K,"provider 名含非法字符(需 /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/)"),implementation:R.string()})).default([]),title:R.string().optional(),description:R.string().optional()}),cn=R.object({id:R.string(),type:R.string()}),ln=R.object({schemaVersion:R.number().default(1),name:R.string(),title:R.string().optional(),description:R.string().optional(),version:R.string().default("0.0.0"),kind:R.enum(["platform","business","third-party"]).default("business"),risk:R.object({level:R.enum(["high","medium","low"]).default("low"),writeBoundary:R.enum(["sdk-enforced","source"]).default("source")}).default({}),distribution:R.object({defaultMode:R.enum(["hybrid","sdk","codegen","local"]).default("codegen")}).default({}),contributes:R.object({handlers:R.array(R.string()).default([]),tools:R.array(R.string()).default([]),cards:R.array(qn).default([]),routes:R.array(R.string()).default([]),pages:R.array(tn).default([]),tabs:R.array(rn).default([]),providers:R.object({slots:R.array(sn).default([])}).default({}),services:R.array(an).default([]),resources:R.array(cn).default([])}).default({}),dependencies:R.object({modules:R.array(on).default([])}).default({}),env:R.array(R.string()).default([])}).strict();function ee(e){let n=ln.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 Gn=/\$\$?\{([A-Z][A-Z0-9_]*)(?::-([^}]*))?\}/g;function dn(e){return JSON.stringify(e).slice(1,-1)}function Ce(e,n=process.env){let r=[],t=e.replace(Gn,(o,i,s)=>{if(o.startsWith("$$"))return o.slice(1);let l=n[i];return l!==void 0?dn(l):s!==void 0?dn(s):(r.push(i),o)});return r.length>0?{missing:r}:{text:t,missing:[]}}import{mkdirSync as Qn,existsSync as un,readFileSync as Xn,writeFileSync as et}from"fs";import{dirname as nt,join as pn}from"path";function le(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,o){super(t),this.name="AppToolkitError",this.code=n,this.httpStatus=r,this.fields=o}};function Ae(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 de(e,n){if(n!==void 0&&(n.includes("/")||n.includes("\\")||n.includes("..")))throw new A("VALIDATION_FAILED",400,`凭据 stem 派生输入含路径敌对字符(${e})——拒绝派生`)}function Y(e){if(de("provider",e.provider),de("service",e.service),de("instanceId",e.instanceId),de("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 tt(e){return`secret://${e}`}function F(e){return`config/credentials/${e}.json`}function ne(e,n){let r=pn(e,F(n));if(!un(r))return null;try{let t=JSON.parse(Xn(r,"utf8"));if(typeof t.type!="string")return null;let{type:o,...i}=t,s={};for(let[l,d]of Object.entries(i))typeof d=="string"&&(s[l]=d);return{type:o,values:s}}catch{return null}}function xe(e,n,r){let t=pn(e,F(n)),o=un(t);return Qn(nt(t),{recursive:!0}),et(t,`${JSON.stringify({type:r.type,...r.values},null,2)}
|
|
2
|
+
`,"utf8"),o}import{mkdirSync as rt,renameSync as it,writeFileSync as ot}from"fs";import{dirname as st}from"path";function $(e,n){fn(st(e));let r=`${e}.tmp-${process.pid}-${Date.now()}`;ot(r,n,"utf8"),it(r,e)}function fn(e){rt(e,{recursive:!0})}function _(e){let n=e.indexOf(".");return n===-1?e:e.slice(0,n)}function L(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 H(e,n){let r=[...new Set((n.descriptor.contributes.services??[]).map(i=>_(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],o=new Set;for(let[i,s]of Object.entries(e.catalog.services))if(_(i)===t)for(let l of s.owners)o.add(l);return o.size>1?{writable:!1,reason:"shared-key",domainKeys:[t],domainKey:t,coOwners:[...o].sort()}:{writable:!0,domainKeys:[t],domainKey:t}}function gn(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 mn(e,n){return[...new Set(Object.values(e.providers[n]??{}).flatMap(r=>r.services))].sort()}function z(e){if(!e||typeof e!="object")return!1;let n=Object.keys(e),r=new Set(["provider","credentialType","configSchema"]);if(!n.every(o=>r.has(o)))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 vn(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 Ee(e,n){let r=new Set(n),t=[],o=(i,s)=>{let l=vn(i,s,r);l&&t.push(l)};for(let i of Object.keys(e.configByInstance??{}))o(i,"configByInstance");for(let i of Object.keys(e.credentialRefByInstance??{}))o(i,"credentialRefByInstance");for(let[i,s]of Object.entries(e.services??{}))for(let l of Object.keys(s.instances??{}))o(l,`services.${i}.instances`);return t}function Z(e){let n=[],r=e.contracts,{integrations:t,suppliedProviders:o,demandedServices:i,supplies:s,malls:l,credentialSchemaMissing:d,vendorConfigKeys:f,vendorConfigSchemaMissing:S,credentialFileMissing:p,credentialFileType:w,realmSwitchHints:C,moduleResourceNeeds:g}=e,c=l?new Set(l):void 0;for(let a of C??[])n.push({severity:"warning",code:"REALM_SWITCH_HINT",message:a});let u=new Set(Object.keys(o).map(a=>a.slice(0,a.indexOf("\0"))).filter(a=>a.length>0)),m=(a,h,x,k)=>{if(typeof x?.provider=="string"&&typeof x.implementation=="string"&&x.provider&&x.implementation)return o[te(x.provider,x.implementation)]?.credentialType;if(typeof h.provider=="string"&&typeof h.implementation=="string")return o[te(h.provider,h.implementation)]?.credentialType;let E=h.provider?.credentialType;if(typeof E=="string")return E;if(!s)return;let M=k??Object.keys(h.instances??{})[0]??"",O=r.resolveEffectiveService(t,a,M,s);if(!(O.status!=="ok"||typeof O.effective.provider!="string"))return o[te(O.effective.provider,O.effective.implementation)]?.credentialType};if(p||w)for(let[a,h]of Object.entries(t.services))for(let[x,k]of Object.entries(h.instances??{})){if(k.enabled===!1)continue;let E=k.credentialRef??h.credentialRef??t.credentialRefByInstance?.[x]??t.credentialRef;if(typeof E!="string"||!E.startsWith("secret://"))continue;let M=E.slice(9);p?.[M]===!0&&n.push({severity:"error",code:"CREDENTIAL_FILE_MISSING",service:a,instance:x,message:`integrations: 凭据文件缺失 ${M}(${a}@${x} effective ref=${E};档内 config/credentials/ 无此文件)`});let O=w?.[M];if(O){let N=m(a,h,k,x);N&&O!==N&&n.push({severity:"error",code:"CREDENTIAL_TYPE_MISMATCH",service:a,instance:x,message:`integrations: 凭据文件 ${M}.json type=${O} 与绑定 credentialType ${N} 不一致(${a}@${x}——transport 创建期将按 type 选策略)`})}}let v=(a,h)=>{if(!c)return;let x=vn(a,h,c);x&&n.push(x)};for(let[a]of Object.entries(t.configByInstance??{}))v(a,"configByInstance");for(let[a]of Object.entries(t.credentialRefByInstance??{}))v(a,"credentialRefByInstance");let b=Object.entries(t.services).filter(([,a])=>a.instances==null||a.instances["*"]!=null).map(([a])=>a);if(b.length>0){let a=Object.entries(t.services).filter(([,h])=>h.instances!=null&&h.instances["*"]==null).map(([h])=>h);a.length>0&&n.push({severity:"warning",code:"WILDCARD_ALIGNMENT",message:`integrations: 激活形态不对称(通配:${b.join(", ")};显式键集:${a.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(a=>a.id).join(", ")})均无 defaultInstanceId——无 scope 参数访问将落全局会话(默认商圈不注入)`});for(let[a,h]of Object.entries(t.services)){if(i[a]||n.push({severity:"warning",code:"SLOT_NOT_DECLARED",service:a,message:`integrations: 服务槽 ${a} 无消费方声明(拼写错误或需求方未安装)——若为拆分前旧槽位词汇(member.main/parking.main/promotion.read/shopping-guide.main),参照 cookbook·app-config 槽位迁移配方展开为新槽`}),typeof h.provider=="string"?typeof h.implementation=="string"&&!o[te(h.provider,h.implementation)]&&n.push({severity:"error",code:"INSTANCE_SUPPLY_UNMATCHED",service:a,message:`integrations: provider ${h.provider}/${h.implementation} 未命中已装供给(${a} 槽级)——装对应 provider 包或改绑`}):h.provider!==void 0&&!z(h.provider)&&n.push({severity:"error",code:"INLINE_PROVIDER_INVALID",service:a,message:`integrations: 内联 provider 超受控形状(${a} 槽级;允许键 provider/credentialType/configSchema)`}),d){let k=m(a,h);k&&d[k]===!0&&n.push({severity:"error",code:"SCHEMA_FILE_MISSING",service:a,message:`integrations: credentialType ${k} 的 schema 引用不存在(${a};manifest 声明指向缺失文件)`})}typeof h.provider=="object"&&typeof h.implementation!="string"&&n.push({severity:"error",code:"IMPL_WITHOUT_PROVIDER",service:a,message:`integrations: 内联厂商声明必须同时提供 implementation(${a} 槽级)`});for(let[k,E]of Object.entries(h.instances??{})){v(k,`services.${a}.instances`);let M=E.provider!==void 0,O=E.implementation!==void 0;if(O&&!M&&n.push({severity:"error",code:"IMPL_WITHOUT_PROVIDER",service:a,instance:k,message:`integrations: 实例级 implementation 需伴随 provider(${a}@${k}——impl-only 禁止,provider 是绑定组头)`}),M&&O){let N=E.provider,V=E.implementation;typeof N=="string"&&typeof V=="string"?o[te(N,V)]||n.push({severity:"error",code:"INSTANCE_SUPPLY_UNMATCHED",service:a,instance:k,message:`integrations: 实例级 provider 覆盖 ${N}/${V} 未命中已装供给(${a}@${k})`}):z(N)||n.push({severity:"error",code:"INLINE_PROVIDER_INVALID",service:a,instance:k,message:`integrations: 实例级内联 provider 超受控形状(${a}@${k})`})}}}for(let[a,h]of[["root",t.provider],...Object.entries(t.domains??{}).map(([x,k])=>[`domains.${x}`,k?.provider])])typeof h!="string"||h.length===0||u.has(h)||n.push({severity:"error",code:"VENDOR_NOT_INSTALLED",module:a.startsWith("domains.")?a.slice(8):void 0,message:`integrations: ${a} provider ${h} 未命中已装供给——装对应 provider 包或 provider use 改绑`});if(s)for(let[a,h]of Object.entries(t.services)){let x=h.instances?Object.keys(h.instances):[""];for(let k of x){let E=r.resolveEffectiveService(t,a,k,s);E.status==="binding-unresolved"&&n.push({severity:"error",code:"BINDING_UNRESOLVED",service:a,instance:k||void 0,message:`integrations: ${E.message}`})}}for(let[a,h]of Object.entries(t.services))h.implementation!==void 0&&h.provider===void 0&&n.push({severity:"error",code:"IMPL_WITHOUT_PROVIDER",service:a,message:`integrations: 槽级 implementation 需伴随 provider(${a}——impl-only 禁止,provider 是绑定组头)`});let I=new Set([...Object.keys(t.services).map(a=>r.moduleDomainOf(a)),...Object.keys(i).map(a=>r.moduleDomainOf(a))]);for(let a of Object.keys(t.domains??{}))I.has(a)||n.push({severity:"warning",code:"DOMAIN_KEY_UNMATCHED",module:a,message:`integrations: domains.${a} 无对应槽位或需求声明(域前缀拼写错误?——键 = 槽位 id 点分前缀)`});if(f){let a=new Set,h=!1,x=k=>{typeof k!="string"||k.length===0||(f[k]&&a.add(k),!f[k]&&!S?.[k]&&(h=!0),S?.[k]&&(h=!0))};x(t.provider);for(let k of Object.values(t.domains??{}))x(k?.provider);for(let k of Object.values(t.services)){let E=k.provider;x(typeof k.provider=="string"?k.provider:E?.provider)}if(a.size>0&&!h){let k=new Set;for(let M of a)for(let O of f[M]??[])k.add(O);let E=(M,O,N)=>{for(let[V]of Object.entries(M??{}))(N??k).has(V)||n.push({severity:"error",code:"DOMAIN_CONFIG_INVALID",message:`integrations: ${O} 键 ${V} 不在引用厂商(${[...a].join("/")})任何槽 schema properties 内(死配置)`})};E(t.config,"root.config");for(let[M,O]of Object.entries(t.configByInstance??{}))E(O,`root.configByInstance[${M}]`);for(let[M,O]of Object.entries(t.domains??{})){if(!O?.config)continue;let N=L(t,M).provider??void 0;if(N===void 0){E(O.config,`domains.${M}.config`);continue}let V=f[N];!V||S?.[N]||E(O.config,`domains.${M}.config`,new Set(V))}}}for(let[a,h]of Object.entries(i)){let x=t.services[a];if(!x){n.push({severity:"warning",code:"SLOT_NOT_CONFIGURED",service:a,message:`integrations: 服务槽 ${a}(需求方 ${h.owners.join(", ")})未配置${h.optional?"(可选槽)":"(必需槽——模块将走降级链)"}`});continue}let k=Object.entries(x.instances??{}).filter(([,E])=>E.enabled===!1).map(([E])=>E);k.length>0&&n.push({severity:"warning",code:"INSTANCE_DISABLED",service:a,instance:k.join(", "),message:`integrations: ${a} 实例禁用(${k.join(", ")})`})}for(let[a,h]of Object.entries(t.modules??{})){let x=g?.[a];g&&!(a in g)&&n.push({severity:"warning",code:"RESOURCE_NOT_DECLARED",module:a,message:`integrations: modules.${a} 未命中已装模块(键 = install id——检查 .tbox/app.json entry.id / packages/ 目录名)`});for(let[k,E]of Object.entries(h?.resources??{})){let M=E?.type;(typeof M!="string"||!r.RESOURCE_TYPE_IDS.includes(M))&&n.push({severity:"error",code:"RESOURCE_TYPE_UNKNOWN",module:a,message:`integrations: modules.${a}.resources.${k} type 非法(${String(M)};词汇表 = ${r.RESOURCE_TYPE_IDS.join("/")})`}),x&&x.size>0&&!x.has(k)&&n.push({severity:"warning",code:"RESOURCE_NOT_DECLARED",module:a,message:`integrations: modules.${a}.resources.${k} 未被模块 manifest contributes.resources 声明`})}}return n}function te(e,n){return`${e}\0${n}`}import{existsSync as Oe,readFileSync as at,readdirSync as ct}from"fs";import{join as ue}from"path";function Sn(e){return e.replaceAll("-","_").toUpperCase()}function yn(e){if(!Oe(e))return null;try{let n=JSON.parse(at(e,"utf8"));return n&&typeof n=="object"?n:null}catch{return null}}function lt(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 o={};for(let[y,I]of Object.entries(n.catalog.providers))for(let[a,h]of Object.entries(I))o[`${y}\0${a}`]={services:[...h.services],owner:h.owner,credentialType:h.credentialType};let i={};for(let[y,I]of Object.entries(n.catalog.services))i[y]={optional:I.optional,owners:[...I.owners]};let s={},l={},d={},f={};for(let y of n.modules){f[y.id]=new Set((y.descriptor.contributes.resources??[]).map(I=>I.id));for(let I of y.descriptor.contributes.providers?.slots??[]){if(I.configSchema){let a=yn(ue(y.dir,I.configSchema));if(a){let h=s[I.provider]??=new Set;for(let x of lt(a))h.add(x)}else l[I.provider]=!0}I.credentialSchema&&!Oe(ue(y.dir,I.credentialSchema))&&(d[I.credentialType]=!0)}}let S={},p=new Set,w=ue(e,"config","credentials");if(Oe(w))for(let y of ct(w)){if(!y.endsWith(".json"))continue;let I=y.slice(0,-5);p.add(I);let a=yn(ue(w,y));a&&typeof a.type=="string"&&(S[I]=a.type)}let C=r,g=[],c=y=>{typeof y=="string"&&y.startsWith("secret://")&&g.push(y)};for(let y of Object.values(C.services??{}))if(y&&typeof y=="object"){c(y.credentialRef);for(let I of Object.values(y.instances??{}))I&&typeof I=="object"&&c(I.credentialRef)}for(let y of Object.values(C.credentialRefByInstance??{}))c(y);c(C.credentialRef);let u={};for(let y of g){let I=y.slice(9);t[Sn(I)]===void 0&&(p.has(I)||(u[I]=!0))}let m=[];for(let[y,I]of Object.entries(C.services??{})){let a=I&&typeof I=="object"&&typeof I.provider=="string"?I.provider:void 0;for(let[h,x]of Object.entries((I&&typeof I=="object"?I.instances:void 0)??{})){let k=x&&typeof x=="object"&&typeof x.provider=="string"?x.provider:void 0;a&&k&&k!==a&&m.push(`integrations: ${y}@${h} 实现覆盖跨厂商(${a} → ${k})——凭据域切换,确认该 mall 对应凭据在场`)}}let b=((Array.isArray(C.instances)?C.instances:void 0)??[]).map(y=>y&&typeof y=="object"&&typeof y.id=="string"?y.id:void 0).filter(y=>typeof y=="string");return{suppliedProviders:o,demandedServices:i,credentialFileMissing:u,credentialFileType:S,credentialSchemaMissing:d,vendorConfigKeys:s,vendorConfigSchemaMissing:l,realmSwitchHints:m,moduleResourceNeeds:f,...b.length>0?{malls:b}:{}}}import{existsSync as D,readdirSync as Me,readFileSync as q}from"fs";import{join as T}from"path";import{existsSync as dt,readFileSync as wn,mkdirSync as ut,writeFileSync as pt}from"fs";import hn from"path";function pe(e){return hn.join(e,".tbox","app.json")}function Rn(e){let n=pe(e);if(!dt(n))return null;try{let r=JSON.parse(wn(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 ft(e,n){let r=pe(e);ut(hn.dirname(r),{recursive:!0}),pt(r,`${JSON.stringify(n,null,2)}
|
|
3
|
+
`,"utf8")}function je(e){let n=Rn(e);if(n===null)return null;let r=pe(e);try{let t=JSON.parse(wn(r,"utf8"));return{...n,miniappContainers:Array.isArray(t?.miniappContainers)?t.miniappContainers:[]}}catch{return{...n,miniappContainers:[]}}}function gt(e,n){let r=e.npmModules.filter(t=>t.id!==n.id);return{...e,npmModules:[...r,n]}}function mt(e,n){return{...e,npmModules:e.npmModules.filter(r=>r.id!==n)}}function vt(e,n){let r=(e.miniappContainers??[]).filter(t=>t.id!==n.id);return{...e,miniappContainers:[...r,n]}}function yt(e,n,r){return{...e,npmModules:e.npmModules.map(t=>t.id===n?{...t,...r}:t)}}function In(e){return/^[a-z0-9]+(-[a-z0-9]+)*$/.test(e)&&!e.includes("..")}function St(e,n){if(!In(e))throw new Error(`非法模块 id "${e}"(来自 ${n}):仅允许小写字母/数字/中划线`)}function kn(e){try{let n=JSON.parse(e);if(!Array.isArray(n.slots))return null;let r=n.slots.filter(o=>typeof o=="string"),t={};if(n.meta&&typeof n.meta=="object"&&!Array.isArray(n.meta))for(let[o,i]of Object.entries(n.meta)){if(!i||typeof i!="object"||Array.isArray(i))continue;let{title:s,description:l}=i,d={};typeof s=="string"&&s.length>0&&(d.title=s),typeof l=="string"&&l.length>0&&(d.description=l),(d.title!==void 0||d.description!==void 0)&&(t[o]=d)}return{slots:r,meta:t}}catch{return null}}function fe(e,n){let r=t=>{try{let o=JSON.parse(t),i=ee(o);if(i.ok)return{kind:"strict",name:i.value.name,version:i.value.version,descriptor:i.value,contributes:i.value.contributes};if(o&&typeof o=="object"&&"contributes"in o){let s=o;return{kind:"loose",...typeof s.name=="string"?{name:s.name}:{},contributes:{providers:s.contributes?.providers,services:s.contributes?.services}}}return null}catch{return null}};if(n)return n.read(e,r)??null;if(!D(e))return null;try{return r(q(e,"utf8"))}catch{return null}}function wt(e){let n=T(e,".tbox","app.json");if(!D(n))return[];try{let r=JSON.parse(q(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 ht(e){let n=T(e,".tbox-dev.local"),r=T(n,"dev-manifest.json");if(!D(r))return[];try{let t=JSON.parse(q(r,"utf8")),o=[];for(let[i,s]of Object.entries(t.redirects??{}))typeof s!="string"||!i.startsWith("@tbox.cn/app-")||o.push({pkg:i,dir:T(n,s)});return o}catch{return[]}}function Rt(e,n){let r=[],t=[e];for(let s of["apps","packages"]){let l=T(e,s);if(D(l))try{for(let d of Me(l,{withFileTypes:!0}))d.isDirectory()&&t.push(T(l,d.name))}catch{}}let o=new Set,i=(s,l)=>{let d=n?n.read(l,kn):kn(q(l,"utf8"));d!=null&&r.push({dir:s,slots:d.slots,meta:d.meta})};for(let s of t){let l=T(s,"node_modules","@tbox.cn");if(!D(l))continue;let d=[];try{d=Me(l)}catch{continue}for(let f of d){let S=T(l,f);if(o.has(S))continue;o.add(S);let p=T(S,"service-slots.json"),w=T(S,"src","service-slots.json");D(p)?i(S,p):D(w)&&i(S,w)}}for(let s of t)for(let l of[T(s,"service-slots.json"),T(s,"src","service-slots.json")])D(l)&&i(s,l);return r}function P(e,n){let r=[],t={providers:{},services:{},credentialTypes:{}},o={},i=[],s=new Set,l=new Map,d=(g,c,u,m)=>{for(let v of g){let b=t.providers[v.provider]??={},y=b[v.implementation];y?y.services.includes(v.service)||y.services.push(v.service):(b[v.implementation]={provider:v.provider,implementation:v.implementation,credentialType:v.credentialType,owner:c,...m?.title?{title:m.title}:{},...m?.description?{description:m.description}:{},...v.local?{local:!0}:{},...v.configSchema?{configSchema:v.configSchema}:{},...v.credentialSchema?{credentialSchema:v.credentialSchema}:{},services:[v.service]},t.credentialTypes[v.credentialType]??={owner:c,packageDir:u,...v.credentialSchema?{credentialSchema:v.credentialSchema}:{}});let I=`${v.provider}\0${v.service}`;o[I]||(o[I]={...v.configSchema?{configSchema:v.configSchema}:{},...v.credentialSchema?{credentialSchema:v.credentialSchema}:{},packageDir:u})}},f=g=>g.kind==="strict"?{title:g.descriptor.title,description:g.descriptor.description}:void 0,S=(g,c)=>{let u=g.optional!==!1,m=g.defaults??[],v=t.services[g.service];if(v){v.optional=v.optional&&u;let I=new Set(v.defaults.map(a=>`${a.provider}\0${a.implementation}`));for(let a of m){let h=`${a.provider}\0${a.implementation}`;I.has(h)||(v.defaults.push(a),I.add(h))}v.owners.includes(c)||v.owners.push(c);return}t.services[g.service]={optional:u,defaults:[...m],...g.title?{title:g.title}:{},...g.description?{description:g.description}:{},owners:[c]};let b=g.service.includes(".")?g.service.slice(0,g.service.indexOf(".")):g.service,y=l.get(b);y===void 0?l.set(b,c):y!==c&&i.push({severity:"warning",code:"DOMAIN_KEY_CONFLICT",module:c,message:`域键 ${b} 被多模块声明(${y} 与 ${c})——domains 换算歧义(P13)`})};for(let g of wt(e)){if(s.has(g.id))continue;let c=T(e,"packages",g.id),u=T(e,"node_modules",...g.package.split("/")),m=fe(T(c,"tbox.module.json"),n)??fe(T(u,"tbox.module.json"),n);if(!m)continue;s.add(g.id);let v=D(T(c,"tbox.module.json"))?c:u,b=m.contributes;m.kind==="strict"&&r.push({id:g.id,dir:v,pkg:g.mode==="sdk"?g.package:`@app/${g.id}`,descriptor:m.descriptor});let y=b.providers?.slots??[];y.length&&d(y,g.package,v,f(m));let I=b.services??[];if(I.length)for(let a of I)S(a,g.package)}for(let{pkg:g,dir:c}of ht(e)){let u=fe(T(c,"tbox.module.json"),n);if(!u?.name||s.has(u.name))continue;s.add(u.name);let m=u.contributes.providers?.slots??[];m.length&&d(m,g,c,f(u));let v=u.contributes.services??[];if(v.length)for(let b of v)S(b,g)}let p=T(e,"packages");if(D(p)){let g=[];try{g=Me(p,{withFileTypes:!0}).filter(c=>c.isDirectory()).map(c=>c.name)}catch{}for(let c of g){if(s.has(c))continue;let u=T(p,c),m=fe(T(u,"tbox.module.json"),n);if(!m)continue;let v=m.name??c;if(s.has(v))continue;s.add(v);let b=m.contributes;m.kind==="strict"&&r.push({id:v,dir:u,pkg:`@app/${v}`,descriptor:m.descriptor});let y=b.providers?.slots??[];y.length&&d(y,`@app/${v}`,u,f(m));let I=b.services??[];if(I.length)for(let a of I)S(a,`@app/${v}`)}}let w=new Set,C={};for(let g of Rt(e,n)){for(let c of g.slots)w.add(c);for(let[c,u]of Object.entries(g.meta))C[c]===void 0&&(C[c]=u)}return{modules:r,catalog:t,serviceSchemas:o,vocabulary:[...w].sort(),slotMeta:C,domainKeyIssues:i}}function bn(e,n,r){let t=T(e,"src",n,"index.ts");if(!D(t))return!1;try{let o=q(t,"utf8");return new RegExp(`export\\s+const\\s+${r}\\b`).test(o)}catch{return!1}}function It(e){let n=T(e,"src","server","index.ts");if(!D(n))return"";try{return q(n,"utf8")}catch{return""}}function Cn(e){return e.match(/declaration\s*:\s*\{[\s\S]*?moduleId\s*:\s*['"]([^'"]+)['"]/)?.[1]}function kt(e){let n=[],r=P(e),t=je(e),o=new Map;for(let i of t?.npmModules??[])o.set(i.id,i.mode);for(let i of r.modules){let s=o.get(i.id)??"local",l=bn(i.dir,"server","serverModule"),d=i.descriptor.contributes;n.push({id:i.id,pkg:s==="sdk"?i.pkg:`@app/${i.id}`,mode:s,hasServerEntry:l,hasClientEntry:bn(i.dir,"client","clientModule"),hasCards:(d.cards?.length??0)>0,...l?{moduleId:Cn(It(i.dir))}:{}})}return n.sort((i,s)=>i.id.localeCompare(s.id))}import{existsSync as Mr,readFileSync as Tr}from"fs";import{join as Nr,resolve as Vr}from"path";function Te(e){return P(e).catalog}function ge(e){let n=P(e).catalog,r={};for(let[i,s]of Object.entries(n.providers)){r[i]={};for(let[l,d]of Object.entries(s))r[i][l]={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,s]of Object.entries(n.services))t[i]={optional:s.optional,defaults:s.defaults.map(l=>({...l})),owners:[...s.owners]};let o={};for(let[i,s]of Object.entries(n.credentialTypes))o[i]={owner:s.owner,...s.credentialSchema?{credentialSchema:s.credentialSchema}:{}};return{providers:r,services:t,credentialTypes:o}}async function Pe(e,n,r,t=process.env){let o=r.module,i=P(e),s=o.buildSupplyLookup(ge(e)),l=re(e,i,n,t);return Z({contracts:o,integrations:n,supplies:s,...l})}import{existsSync as jt,rmSync as Mt}from"fs";import{join as Tt}from"path";import{existsSync as bt,readFileSync as Ct}from"fs";import{join as An}from"path";var At=An("config","integrations.json");function W(e,n){let r=An(e,At),t=o=>JSON.parse(o);if(n)return n.read(r,t)??null;if(!bt(r))return null;try{return t(Ct(r,"utf8"))}catch{return null}}async function xn(e,n){let r=await j(e),t=W(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(o){if(o instanceof Error&&Array.isArray(o.issues)){let i=o.issues.filter(s=>Array.isArray(s.path)).map(s=>s.path.map(String).join(".")).filter(s=>s.length>0);throw new A("VALIDATION_FAILED",400,"integrations.json 校验失败",i.length>0?i:void 0)}throw new A("VALIDATION_FAILED",400,o instanceof Error?o.message:String(o))}return{config:t,strict:!1,contracts:r}}function ie(e,n,r){return e.includes(r)||n.includes(r)}import{readdirSync as xt,existsSync as me,readFileSync as Et}from"fs";import{join as ve}from"path";function En(e,n,r){let t=[];if(n){let o=Y({provider:e});t.push({stem:o,ref:`secret://${o}`,input:n})}for(let[o,i]of Object.entries(r??{})){let s=Y({provider:e,instanceId:o});t.push({instanceId:o,stem:s,ref:`secret://${s}`,input:i})}return t}function On(e,n,r,t,o){let i=[];if(t){let s=Y({provider:e,service:n});i.push({stem:s,ref:`secret://${s}`,input:t})}for(let[s,l]of Object.entries(o??{})){let d=Y({provider:e,instanceId:s,domain:r});i.push({instanceId:s,stem:d,ref:`secret://${d}`,input:l})}return i}function Ve(e,n,r){return n.map(t=>({stem:t.stem,ref:t.ref,overwrote:r?me(ve(e,F(t.stem))):xe(e,t.stem,t.input)}))}function Ot(e,n){return me(ve(e,F(n)))}function Ne(e,n){if(typeof e=="string"){e.startsWith("secret://")&&n.add(e.slice(9));return}if(Array.isArray(e)){for(let r of e)Ne(r,n);return}if(e&&typeof e=="object")for(let r of Object.values(e))Ne(r,n)}function oe(e){let n=ve(e,"config","credentials"),r=new Set,t=ve(e,"config","integrations.json");if(me(t))try{Ne(JSON.parse(Et(t,"utf8")),r)}catch{}let o=[];if(!me(n))return o;let i=[];try{i=xt(n).filter(s=>s.endsWith(".json")).map(s=>s.slice(0,-5))}catch{return o}for(let s of i.sort()){if(r.has(s))continue;let l=ne(e,s);o.push({severity:"warning",code:"ORPHANED_CREDENTIAL",message:`${F(s)} 不再被引用(凭据文件保留,可手动清理)`})}return o}function ye(e){if(typeof e=="string")return e.length>0?e:void 0;if(e&&typeof e=="object"&&z(e)){let n=e.provider;return n.length>0?n:void 0}}async function Pt(e){let n=Tt(e,"config","integrations.json"),r=W(e);if(r===null)return{removed:[],orphanCredentials:[],skipped:"not-configured"};let t=await j(e);if(!t.strictValidation)return{removed:[],orphanCredentials:[],skipped:"predicate-unavailable"};let o;try{let p=Ce(JSON.stringify(r),process.env);if(p.missing.length>0||p.text===void 0)return{removed:[],orphanCredentials:[],skipped:"predicate-unavailable"};o=JSON.parse(p.text)}catch{return{removed:[],orphanCredentials:[],skipped:"predicate-unavailable"}}let i;try{i=await Pe(e,o,t,process.env)}catch{return{removed:[],orphanCredentials:[],skipped:"predicate-unavailable"}}let s=new Set(Object.keys(P(e).catalog.providers)),l=structuredClone(r);l.services??={};let d=[];for(let p of i)if(p.severity==="error"){if(p.code==="VENDOR_NOT_INSTALLED")if(p.module){let w=l.domains?.[p.module];w?.provider&&(d.push(`domains.${p.module}.provider=${w.provider}`),delete w.provider,Object.keys(w).length===0&&delete l.domains[p.module])}else l.provider&&(d.push(`root.provider=${l.provider}`),delete l.provider);else if(p.code==="BINDING_UNRESOLVED"&&p.service){let w=l.services[p.service];if(!w||[p.instance?ye(w.instances?.[p.instance]?.provider):void 0,ye(w.provider),ye(l.domains?.[_(p.service)]?.provider),ye(l.provider)].filter(g=>typeof g=="string").some(g=>s.has(g)))continue;if(p.instance){let g=w.instances;if(!g||!(p.instance in g))continue;delete g[p.instance],d.push(`services.${p.service}.instances.${p.instance}`),Object.keys(g).length===0&&delete w.instances,Object.keys(w).length===0&&delete l.services[p.service]}else delete l.services[p.service],d.push(`services.${p.service}`)}}if(Object.keys(l.services).length===0&&delete l.services,d.length===0)return{removed:d,orphanCredentials:[]};let f=!l.provider&&Object.keys(l.domains??{}).length===0&&Object.keys(l.services??{}).length===0&&Object.keys(l.config??{}).length===0&&Object.keys(l.configByInstance??{}).length===0&&!l.credentialRef&&Object.keys(l.credentialRefByInstance??{}).length===0&&(l.instances??[]).length===0&&l.defaultInstanceId==null&&Object.keys(l.modules??{}).length===0;if(!f&&(l.services??={},typeof t.module.parseIntegrationsConfig=="function"))try{t.module.parseIntegrationsConfig(l)}catch{return{removed:[],orphanCredentials:[],skipped:"validation-failed"}}let S=[];return f?jt(n)&&Mt(n,{force:!0}):$(n,`${JSON.stringify(l,null,2)}
|
|
4
|
+
`),S=oe(e).map(p=>p.message),{removed:d,orphanCredentials:S}}import{existsSync as Tn,readFileSync as Pn}from"fs";import{join as G,resolve as Nt}from"path";import Vt from"ajv";var se=["provider","config","configByInstance","credentialRef","credentialRefByInstance","instances","defaultInstanceId"];var U=G("config","integrations.json");function Q(e,n){if(e===n)return!0;if(Array.isArray(e)&&Array.isArray(n))return e.length!==n.length?!1:e.every((r,t)=>Q(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(o=>Q(e[o],n[o]))}return!1}function he(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"&&!K.test(t))throw new A("VALIDATION_FAILED",400,`${r} 名含非法字符(需 /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/)`,[`${n}.${r}`]);he(t,`${n}.${r}`)}}function Se(e,n,r){if(!(!e||typeof e!="object"||Array.isArray(e))){for(let t of Object.keys(e))if(!(r&&(t==="*"||t==="**"))&&!K.test(t))throw new A("VALIDATION_FAILED",400,`${n} 键 ${t} 含非法字符(需 /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/)——进凭据 stem 派生,防路径逃逸`,[n])}}function jn(e,n,r){if(r==="root"){let t=e.instances;Array.isArray(t)&&t.forEach((o,i)=>{let s=o&&typeof o=="object"?o.id:void 0;if(typeof s=="string"&&!K.test(s))throw new A("VALIDATION_FAILED",400,`instances[${i}].id 含非法字符(需 /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/)`,[`root.instances[${i}].id`])}),Se(e.configByInstance,"root.configByInstance",!1),Se(e.credentialRefByInstance,"root.credentialRefByInstance",!1)}else Se(e.instances,"services.instances",!0);Se(n.credentialsByInstance,"credentialsByInstance",!1)}function we(e,n){let r=G(e,U),t=o=>JSON.parse(o);if(n)return n.read(r,t)??null;if(!Tn(r))return null;try{return t(Pn(r,"utf8"))}catch{return null}}function De(e){let n=(e.instances??[]).map(r=>r.id);return n.length===0?[]:Ee(e,n)}function Dt(e,n,r,t){let{catalog:o}=P(e,t),i=o.providers[n];if(!i)return;let s=new Set;for(let l of Object.values(i))(r===void 0||l.services.includes(r))&&s.add(l.credentialType);return s.size===1?[...s][0]:void 0}function Mn(e,n,r,t,o,i){if(typeof t.type!="string"||t.type.length===0)throw new A("VALIDATION_FAILED",400,"凭据 type 缺失或为空字符串",["credentials.type"]);let s=o??Dt(e,n,r,i);if(s!==void 0&&s!=="-"&&t.type!==s)throw new A("VALIDATION_FAILED",400,`凭据 type=${t.type} 与绑定 credentialType ${s} 不一致`,["credentials.type"]);if(r===void 0)return;let{serviceSchemas:l}=P(e,i),d=l[`${n}\0${r}`];if(!d?.credentialSchema)return;let f=Nt(d.packageDir,d.credentialSchema);if(Tn(f))try{let p=new Vt({allErrors:!0}).compile(JSON.parse(Pn(f,"utf8")));if(!p({type:t.type,...t.values})){let w=(p.errors??[]).map(C=>`credentials.values.${String(C.instancePath||C.params?.missingProperty||"").replace(/^\//,"")}`);throw new A("VALIDATION_FAILED",400,"凭据字段校验失败(ajv 字段级)",w)}}catch(S){if(S instanceof A)throw S}}function Fe(e,n={}){let{onApplied:r,cache:t}=n;function o(f,S,p,w){let C=f!==null,g=S.length>0,c=!w&&C,u=!w&&g;c&&($(G(e,U),`${JSON.stringify(f,null,2)}
|
|
5
|
+
`),t?.invalidate([G(e,U)]));let m=c||u,v=m&&r!==void 0;v&&r?.();let b=[];return C&&b.push(U),g&&b.push(...S.map(y=>F(y.stem))),{written:m,restartScheduled:v,files:b,credentials:S,issues:p}}let i;async function s(){i=await j(e)}function l(f,S){return f.filter(p=>Array.isArray(p.path)).map(p=>{let w=p.path.map(String).join(".");return S!==""&&w.startsWith(S)&&w.length>S.length?w.slice(S.length):w}).filter(p=>p.length>0)}function d(f,S,p){let w=i;if(!w||!w.strictValidation||!w.module.parseIntegrationsConfig)throw new A("CONTRACTS_NOT_RESOLVED",503,"contracts 严格校验面不可用(需 ≥0.9)——写路径门控失败(工厂边缘断言应先行拦截)");try{w.module.parseIntegrationsConfig(f)}catch(C){throw C instanceof Error&&Array.isArray(C.issues)?new A("VALIDATION_FAILED",400,"节点 schema 校验失败",l(C.issues,p)):new A("VALIDATION_FAILED",400,C instanceof Error?C.message:String(C))}}return{warmup:s,applyApp(f,S={}){he(f,"root"),jn(f,S,"root");let p=[],w=we(e,t),C=w??{services:{}};w===null&&p.push({severity:"warning",code:"SKELETON_MATERIALIZED",message:"config/integrations.json 缺席——已物化骨架(写时自动初始化;R13 语义翻转提示)"});let g={...C};for(let a of se)a in f||delete g[a];for(let[a,h]of Object.entries(f))g[a]=h;let c=typeof g.provider=="string"?g.provider:"app",u=En(c,S.credentials,S.credentialsByInstance);for(let a of u)Mn(e,c,void 0,a.input,void 0,t);let m=u.find(a=>a.instanceId===void 0);if(S.credentials&&m&&(g.credentialRef=m.ref),S.credentialsByInstance){let a={...g.credentialRefByInstance??{}};for(let h of u)h.instanceId!==void 0&&(a[h.instanceId]=h.ref);g.credentialRefByInstance=a}let v=S.credentials!==void 0||S.credentialsByInstance!==void 0,b=Q(w,g);if(b&&!v)return{written:!1,restartScheduled:!1,files:[],credentials:[],issues:p};d(g,p,"");let y=S.dryRun===!0,I=Ve(e,u,y);return p.push(...De(g)),o(b?null:g,I,p,S.dryRun)},applyModule(f,S,p={}){he(S,"root");let{modules:w,catalog:C}=P(e,t),g=w.find(a=>a.id===f);if(!g)throw new A("MODULE_NOT_FOUND",404,`模块 ${f} 非已装模块`);let c=H({modules:w,catalog:C},g);if(!c.writable)throw c.reason==="multi-domain"?new A("VALIDATION_FAILED",400,`模块 ${f} 跨 ${c.domainKeys.length} 个域(${c.domainKeys.join("、")})——模块级统一配置仅支持单域模块,请按服务级配置(编辑各服务行)`):c.reason==="zero-domain"?new A("VALIDATION_FAILED",400,`模块 ${f} 无服务需求声明,无域配置位`):new A("VALIDATION_FAILED",400,`域键 ${c.domainKey} 由 ${c.coOwners.map(a=>gn(w,a)).join("、")} 共用——写通道拒绝歧义操作,请按服务级配置`);let u=c.domainKey,m=[],v=we(e,t);if(v===null&&Object.keys(S).length===0)return{written:!1,restartScheduled:!1,files:[],credentials:[],issues:m};v===null&&m.push({severity:"warning",code:"SKELETON_MATERIALIZED",message:"config/integrations.json 缺席——已物化骨架(写时自动初始化;R13 语义翻转提示)"});let b=v??{services:{}},y={...b.domains??{}},I={...b,domains:y};return Object.keys(S).length===0?delete y[u]:y[u]={...S},Q(v,I)?{written:!1,restartScheduled:!1,files:[],credentials:[],issues:m}:(d(I,m,`domains.${u}.`),m.push(...De(I)),o(I,[],m,p.dryRun))},applyService(f,S,p={}){he(S,"root"),jn(S,p,"slot");let w=[],C=we(e,t);if(!ie(P(e,t).vocabulary,Object.keys(C?.services??{}),f))throw new A("SERVICE_NOT_FOUND",404,`服务 ${f} 不在服务词汇(已装契约包 ∪ 已配置)`);C===null&&w.push({severity:"warning",code:"SKELETON_MATERIALIZED",message:"config/integrations.json 缺席——已物化骨架(写时自动初始化;R13 语义翻转提示)"});let g=C??{services:{}},c={...g.services??{}},u={...g,services:c};c[f]={...S};let m=S.provider,v=m!==null&&typeof m=="object"?m:void 0,b=typeof m=="string"?m:typeof v?.provider=="string"?v.provider:"app",y=f.includes(".")?f.slice(0,f.indexOf(".")):f,I=On(b,f,y,p.credentials,p.credentialsByInstance);for(let O of I)Mn(e,b,f,O.input,typeof v?.credentialType=="string"?v.credentialType:void 0,t);let a=c[f],h=I.find(O=>O.instanceId===void 0);if(p.credentials&&h&&(a.credentialRef=h.ref),p.credentialsByInstance){let O={...a.instances??{}};for(let N of I){if(N.instanceId===void 0)continue;let V={...O[N.instanceId]??{}};V.credentialRef=N.ref,O[N.instanceId]=V}a.instances=O}let x=p.credentials!==void 0||p.credentialsByInstance!==void 0,k=Q(C,u);if(k&&!x)return{written:!1,restartScheduled:!1,files:[],credentials:[],issues:w};d(u,w,`services.${f}.`);let E=p.dryRun===!0,M=Ve(e,I,E);return w.push(...De(u)),o(k?null:u,M,w,p.dryRun)},deleteService(f){let S=[],p=we(e,t);if(!ie(P(e,t).vocabulary,Object.keys(p?.services??{}),f))throw new A("SERVICE_NOT_FOUND",404,`服务 ${f} 不在服务词汇(已装契约包 ∪ 已配置)`);let w={...p?.services??{}};if(!(f in w))return{written:!1,restartScheduled:!1,files:[],credentials:[],issues:S};delete w[f];let C={...p??{services:{}},services:w};$(G(e,U),`${JSON.stringify(C,null,2)}
|
|
6
|
+
`),t?.invalidate([G(e,U)]),S.push(...oe(e));let g=r!==void 0;return g&&r?.(),{written:!0,restartScheduled:g,files:[U],credentials:[],issues:S}}}}import{existsSync as Ft}from"fs";import{join as $t}from"path";function $e(e){let n=$t(e,"config","integrations.json");if(Ft(n))return{generated:!1,reason:"in-place",services:[]};let r=[];for(let i of P(e).modules)for(let s of i.descriptor.contributes.providers?.slots??[])s.local===!0&&r.push({service:s.service,provider:s.provider,implementation:s.implementation});if(r.length===0)return{generated:!1,reason:"no-local-slots",services:[]};let t={},o=new Set(r.map(i=>i.provider));if(o.size===1){let[i]=o;for(let s of r)t[s.service]={};$(n,`${JSON.stringify({provider:i,services:t},null,2)}
|
|
6
7
|
`)}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 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{
|
|
8
|
+
`)}return{generated:!0,reason:"generated",services:r.map(i=>i.service).sort()}}function Nn(e,n){let r=P(e.appDir,e.cache),t=W(e.appDir,e.cache);return{appDir:e.appDir,walk:r,catalog:r.catalog,integrations:t,contracts:n}}function Vn(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 Re(e,n,r){let t=e.providers[n];return t?Object.values(t).some(o=>o.services.includes(r)):!1}function Dn(e,n){let r=[];for(let[t,o]of Object.entries(e.providers))Object.values(o).some(i=>i.services.includes(n))&&r.push(t);return r.sort()}function B(e,n){let r=e.slotMeta[n],t=e.catalog.services[n],o=r?.title??t?.title??n,i=r?.description??t?.description;return{title:o,...i!==void 0?{description:i}:{}}}function Fn(e){return e.replace(/^@tbox\.cn\/app-/,"").replace(/^@[^/]+\//,"")}function _t(e){return Fn(e.id).startsWith("provider-")||Fn(e.pkg).startsWith("provider-")}function _e(e){return{modules:e.walk.modules.filter(r=>!_t(r)).map(r=>({id:r.id,name:r.descriptor.name,title:r.descriptor.title??r.descriptor.name,description:r.descriptor.description??""})).sort((r,t)=>r.id.localeCompare(t.id))}}function Le(e,n){let r=e.walk.modules.find(d=>d.id===n);if(!r)throw new A("MODULE_NOT_FOUND",404,`模块 ${n} 非已装模块`);let t=(r.descriptor.contributes.services??[]).map(d=>{let f=B(e.walk,d.service);return{service:d.service,title:f.title,...f.description!==void 0?{description:f.description}:{},required:d.optional===!1}}),o=H(e.walk,r),i=e.integrations?.domains??{},s=o.writable?i[o.domainKey]??null:null,l=o.domainKeys.map(d=>{let f=L(e.integrations,d);return{domain:d,effective:f.provider,layer:f.layer}});return{title:r.descriptor.title??r.descriptor.name,description:r.descriptor.description??"",domainKeys:[...o.domainKeys],integration:s,providerBindings:l,configEditable:o.writable,services:t}}function We(e){let n=Vn(e.walk),r=e.walk.vocabulary.filter(i=>!n.has(i)).map(i=>{let s=B(e.walk,i);return{service:i,title:s.title,...s.description!==void 0?{description:s.description}:{},required:!1}}),t=e.integrations,o=null;if(t){let i={};for(let s of se)s in t&&(i[s]=t[s]);o=i}return{services:r,integration:o}}function Be(e,n){let r=e.integrations?.services??{},t=e.catalog.services[n];if(!ie(e.walk.vocabulary,Object.keys(r),n))throw new A("SERVICE_NOT_FOUND",404,`服务 ${n} 不在服务词汇(已装契约包 ∪ 已配置)`);let o=r[n]??null,i=B(e.walk,n);return{service:n,title:i.title,...i.description!==void 0?{description:i.description}:{},required:t?!t.optional:!1,defaults:t?t.defaults.map(s=>({...s})):[],integration:o,suppliedBy:Dn(e.catalog,n)}}import{existsSync as Ln,readFileSync as Wn,realpathSync as $n}from"fs";import{isAbsolute as Lt,join as Wt,relative as Bt,resolve as _n,sep as Kt}from"path";function Ke(e){let n=[];for(let[r,t]of Object.entries(e.catalog.providers)){let o=Object.values(t);o.length!==0&&n.push({provider:r,local:o.every(i=>i.local===!0),owner:o[0].owner,...o[0].title?{title:o[0].title}:{},...o[0].description?{description:o[0].description}:{}})}return{providers:n.sort((r,t)=>r.provider.localeCompare(t.provider))}}function ze(e,n,r){let t=e.catalog.providers[n];if(!t)throw new A("PROVIDER_NOT_FOUND",404,`厂商 ${n} 不 ∈ catalog(内联声明厂商无包级档案——身份与表单经组合端点暴露)`);let o={};for(let s of Object.values(t))for(let l of s.services)(o[l]??={implementations:[]}).implementations.push({implementation:s.implementation,credentialType:s.credentialType});for(let[s,l]of Object.entries(o)){let d=B(e.walk,s);l.title=d.title,d.description!==void 0&&(l.description=d.description)}if(r?.has("integrationSchemas"))for(let[s,l]of Object.entries(o))l.integrationSchemas={configSchema:J(e,n,s)};let i=Object.values(t);return{provider:n,local:i.every(s=>s.local===!0),owner:i[0].owner,...i[0].title?{title:i[0].title}:{},...i[0].description?{description:i[0].description}:{},services:o}}function Ue(e,n,r){let t=e.catalog.providers[n],o=e.integrations?.services??{},i=[];if(t)for(let f of Object.values(t))f.services.includes(r)&&i.push({implementation:f.implementation,credentialType:f.credentialType});let s=o[r]?.provider,l=s&&typeof s=="object"&&s.provider===n?s:void 0;if(i.length===0&&!l)throw t?new A("SUPPLY_NOT_FOUND",404,`厂商 ${n} 未供给服务 ${r}(AI 帮我实现流:PUT 显式绑定后实施)`):new A("PROVIDER_NOT_FOUND",404,`厂商 ${n} 完全未知(不 ∈ catalog 且无任何内联声明)`);i.length===0&&l&&i.push({implementation:o[r]?.implementation??`${n}-inline@1`,credentialType:l.credentialType});let d=J(e,n,r,l?s?.configSchema:void 0);return{provider:n,service:r,implementations:i,integrationSchemas:{configSchema:d}}}function J(e,n,r,t){let o=e.walk.serviceSchemas[`${n}\0${r}`];return o?Je(e.appDir,o.packageDir,o.configSchema):typeof t=="string"?zt(e.appDir,t):null}function Je(e,n,r){if(!r)return null;let t=Wt(n,r);if(!Ln(t))return null;try{return JSON.parse(Wn(t,"utf8"))}catch{return null}}function zt(e,n){let r=_n(e,"config","schemas"),t=_n(e,n),o=Bt(r,t);if(o===""||o.startsWith("..")||Lt(o))throw new A("VALIDATION_FAILED",400,"configSchema 引用必须落在 config/schemas/ 内(禁路径逃逸)",["provider.configSchema"]);if(!Ln(t))return null;let i,s;try{i=$n(r),s=$n(t)}catch{return null}if(s!==i&&!s.startsWith(i+Kt))throw new A("VALIDATION_FAILED",400,"configSchema 引用经 symlink 逃逸出 config/schemas/(realpath 复核拒绝)",["provider.configSchema"]);try{return JSON.parse(Wn(t,"utf8"))}catch{return null}}function Bn(e){return{version:e.version,evaluation:e.evaluation,strictValidation:e.strictValidation,notes:[...e.notes]}}function Kn(e,n,r){let t=e.integrations,o=_(n),i=t?.services?.[n],s=i?.instances?.[r]??i?.instances?.["*"];return s&&s.provider!==void 0?"instance":i?.provider!==void 0?"service":L(t,o).layer==="module"?"module":typeof t?.provider=="string"&&t.provider.length>0?"app":null}function zn(e,n,r,t,o){let i=n.resolveEffectiveService(e.integrations,r,t,o);if(i.status==="ok"&&i.effective){let s=i.effective.provider,l=typeof s=="string"?s:String(s?.provider??"");return{status:"ok",provider:l,implementation:i.effective.implementation,effective:{provider:l,implementation:i.effective.implementation,...i.effective.config!==void 0?{config:{...i.effective.config}}:{},...i.effective.credentialRef!==void 0?{credentialRef:i.effective.credentialRef}:{},layer:Kn(e,r,t)??"app"}}}return i.status==="binding-unresolved"?{status:"binding-unresolved",statusMessage:i.message}:{status:i.status}}function Ye(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 He(e,n){let r=Ye(n),{integrations:t,catalog:o}=n,i=r.buildSupplyLookup(o),s=t,d=r.normalizeInstances({instances:s?.instances,defaultInstanceId:s?.defaultInstanceId}).defaultInstanceId??"*",f=zn(n,r,e,d,i),S=Kn(n,e,d),p=n.integrations?.services?.[e]?.provider,w=f.provider??(typeof p=="string"?p:typeof p=="object"&&p?String(p.provider):void 0),C=new Set(Object.keys(n.catalog.providers)),g=w!==void 0&&C.has(w)?Ut(n.catalog,w,e):void 0,c=o.services[e],u=c!==void 0?!c.optional:void 0,m=c?.owners[0];return{status:f.status,...f.statusMessage?{statusMessage:f.statusMessage}:{},...w!==void 0?{provider:w}:{},...w!==void 0&&S!==null?{layer:S}:{},...g!==void 0?{supplied:g}:{},...m!==void 0?{module:m}:{},...u!==void 0?{required:u}:{}}}function Ut(e,n,r){return Re(e,n,r)}function ae(e,n,r){let t=new Set([...e.walk.vocabulary,...Object.keys(e.catalog.services)]),o=e.integrations?.services??{};for(let s of Object.keys(o))t.add(s);let i={};for(let s of[...t].sort())r&&!r.includes(s)||(i[s]=He(s,e));return{contracts:Bn(e.contracts),resolutions:i,issues:n}}function Ze(e,n,r){let t=Ye(n),o=t.buildSupplyLookup(n.catalog),i=n.integrations,l=t.normalizeInstances({instances:i?.instances,defaultInstanceId:i?.defaultInstanceId}).instances.map(c=>c.id),d=n.integrations?.services?.[e],f=d?.instances?Object.keys(d.instances):void 0,S;l.length>0?(S=[...l],f?.includes("*")&&S.push("*")):f!==void 0&&f.length>0?S=f.includes("*")?["*"]:f:S=[];let p=S.map(c=>{let u=zn(n,t,e,c,o);return{instanceId:c,status:u.status,...u.statusMessage?{statusMessage:u.statusMessage}:{},...u.effective?{effective:u.effective}:{}}}),w=Jt(n,e),g={...He(e,n),instances:p,inheritance:w};return{contracts:Bn(n.contracts),resolution:g,issues:r.filter(c=>c.service===e)}}function Jt(e,n){let r=e.integrations,t=_(n),o=r?.services?.[n],i=e.integrations,l=Ye(e).normalizeInstances({instances:i?.instances,defaultInstanceId:i?.defaultInstanceId}).defaultInstanceId??"*",d=o?.instances?.[l]??o?.instances?.["*"],f=L(r,t);return[{layer:"app",...r?.provider?{provider:r.provider}:{}},{layer:"module",...f.layer==="module"&&f.provider?{provider:f.provider}:{}},{layer:"service",...typeof o?.provider=="string"?{provider:o.provider}:{},...o?.implementation?{implementation:o.implementation}:{}},{layer:"instance",...d?.provider?{provider:d.provider}:{},...d?.implementation?{implementation:d.implementation}:{}}]}function qe(e,n,r){if(n===null)return null;let t={};for(let o of r)t[o]={configSchema:J(e,n,o)};return{provider:n,services:t}}function X(e){return e===null||typeof e!="object"?JSON.stringify(e??null):Array.isArray(e)?`[${e.map(X).join(",")}]`:`{${Object.keys(e).sort().map(r=>`${JSON.stringify(r)}:${X(e[r])}`).join(",")}}`}var Yt="\0no-enum";function Ht(e){let n=e.enum;return Array.isArray(n)?`[${[...new Set(n.map(X))].sort().join(",")}]`:Yt}function Zt(e){if(!e||typeof e!="object"||Array.isArray(e))return null;let n=e.type;return n===void 0?null:X(n)}function qt(e){let n=e.map(t=>Zt(t.subschema));if(n.some(t=>t===null)||new Set(n).size>1)return null;if(n[0]==='"object"'){let t=X(e[0].subschema);return e.every(o=>X(o.subschema)===t)?e[0].subschema:null}return new Set(e.map(t=>Ht(t.subschema))).size===1?e[0].subschema:null}function Un(e,n){let r=[],t=!0,o=new Map;for(let[s,l]of Object.entries(e.services)){let d=l.configSchema;if(d===null){r.push(s);continue}if(!d||typeof d!="object"||Array.isArray(d))continue;let f=d;f.additionalProperties===!1&&(t=!1);let S=f.properties;if(!S||typeof S!="object"||Array.isArray(S))continue;let p=Array.isArray(f.required)?f.required:[];for(let w of Object.keys(S)){let C=o.get(w)??[];C.push({service:s,subschema:S[w],required:p.includes(w)}),o.set(w,C)}}let i=[];for(let[s,l]of o)i.push({key:s,schema:qt(l),sources:l.map(({service:d,required:f})=>({service:d,required:f}))});return{provider:e.provider,schemaless:r,unsupplied:[...n?.unsupplied??[]],additionalPropertiesAllowed:t,fields:i}}function Jn(e,n,r,t){let o=typeof t=="string"&&t.length>0?t:void 0;if(e==="app"){if(o!==void 0)return o;let l=n.integrations?.provider;return typeof l=="string"&&l.length>0?l:null}let i=n.walk.modules.find(l=>l.id===r);if(!i)return null;let s=H(n.walk,i);return s.writable?o!==void 0?o:L(n.integrations,s.domainKey).provider:null}var Yn={integrationSchemas:null,integrationFieldRegistry:null};function Ge(e,n){let r=Jn("app",e,void 0,n);if(r===null)return Yn;let t=qe(e,r,mn(e.catalog,r));return{integrationSchemas:t,integrationFieldRegistry:Un(t)}}function Qe(e,n,r){let t=Jn("module",e,n,r),o=e.walk.modules.find(d=>d.id===n);if(t===null||!o)return Yn;let i=(o.descriptor.contributes.services??[]).map(d=>d.service),s=d=>Re(e.catalog,t,d),l=qe(e,t,i.filter(s));return{integrationSchemas:l,integrationFieldRegistry:Un(l,{unsupplied:[...new Set(i.filter(d=>!s(d)))]})}}function Gt(e){return Ge(e).integrationSchemas}function Qt(e,n){return Qe(e,n).integrationSchemas}function Xe(e,n){let r=e.integrations?.services?.[n]?.provider;if(r===void 0||r===""){let t=L(e.integrations,_(n));return t.provider===null?null:{provider:t.provider,configSchema:J(e,t.provider,n)}}if(typeof r=="string")return{provider:r,configSchema:J(e,r,n)};if(z(r)){let t=r;return{provider:t.provider,configSchema:J(e,t.provider,n,t.configSchema)}}return null}function ce(e,n){if(n===void 0||!n.startsWith("secret://"))return;let r=n.slice(9),t=ne(e,r);if(t)return{type:t.type,ref:n,file:F(r),masked:le(t.values)}}function en(e){let n=e.integrations,r={credential:ce(e.appDir,typeof n?.credentialRef=="string"?n.credentialRef:void 0)??null,byInstance:{}};for(let{id:o}of n?.instances??[]){let i=n?.credentialRefByInstance?.[o];i!==void 0&&(r.byInstance[o]=ce(e.appDir,i)??null)}let t={};for(let[o,i]of Object.entries(n?.services??{})){let s={credential:ce(e.appDir,i.credentialRef)??null,byInstance:{}};for(let[l,d]of Object.entries(i.instances??{}))s.byInstance[l]=ce(e.appDir,d.credentialRef)??null;t[o]=s}return{app:r,services:t}}function nn(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 o=e.catalog.credentialTypes[t];return{credentialType:t,owner:o?.owner??"",credentialSchema:o?Je(e.appDir,o.packageDir,o.credentialSchema):null}})}}import Xt from"path";var er={app:["serviceResolutions","integrationSchemas","integrationFieldRegistry"],module:["serviceResolutions","integrationSchemas","integrationFieldRegistry"],provider:["integrationSchemas"],service:["integrationSchemas"]};function Ie(e,n){if(!n||n.length===0)return new Set;let r=er[e],t=[...new Set(n)].filter(o=>!r.includes(o));if(t.length>0)throw new A("VALIDATION_FAILED",400,`include 含未知或不适用于本端点的键:${t.join("、")}(白名单:${r.join("、")})`,["include"]);return new Set(n)}var Hn=["integrationSchemas","integrationFieldRegistry"];function Zn(e,n,r){if(!(typeof n!="string"||n.length===0)&&!Hn.some(t=>r.has(t)))throw new A("VALIDATION_FAILED",400,`参数 ${e} 需与表单 include 键(${Hn.join("、")})同现——轴参仅重定向表单层双投影`,[e])}function nr(e,n={}){let r=ke(),t=Promise.resolve(),o={appDir:e,cache:r};function i(c){if(!c.strictValidation)throw new A("CONTRACTS_NOT_RESOLVED",503,Ae(c))}function s(c){return Nn(o,c)}async function l(){let c=await j(e),u=s(c),m=u.integrations;if(m===null)return[];let v={resolveEffectiveService:d(c.module.resolveEffectiveService,"resolveEffectiveService"),buildSupplyLookup:d(c.module.buildSupplyLookup,"buildSupplyLookup"),moduleDomainOf:d(c.module.moduleDomainOf,"moduleDomainOf"),normalizeInstances:d(c.module.normalizeInstances,"normalizeInstances"),RESOURCE_TYPE_IDS:c.module.RESOURCE_TYPE_IDS??[]},b=re(e,u.walk,m,process.env);return Z({contracts:v,integrations:m,supplies:v.buildSupplyLookup(u.catalog),...b})}function d(c,u){if(typeof c!="function")throw new A("CONTRACTS_NOT_RESOLVED",503,`contracts 求值面 ${u} 缺席(形态异常)`);return c}function f(c){let u=t.then(c,c);return t=u.catch(()=>{}),u}let S={async loadModules(){let c=await j(e);return _e(s(c))},async loadModule(c,u){let m=Ie("module",u?.include);Zn("moduleProvider",u?.moduleProvider,m);let v=await j(e),b=s(v),y=Le(b,c);if(m.has("serviceResolutions")&&(y.serviceResolutions=v.evaluation&&v.strictValidation?await l().then(I=>ae(b,I,y.services.map(a=>a.service))):null),m.has("integrationSchemas")||m.has("integrationFieldRegistry")){let I=Qe(b,c,u?.moduleProvider);m.has("integrationSchemas")&&(y.integrationSchemas=I.integrationSchemas),m.has("integrationFieldRegistry")&&(y.integrationFieldRegistry=I.integrationFieldRegistry)}return y},async loadApp(c){let u=Ie("app",c?.include);Zn("appProvider",c?.appProvider,u);let m=await j(e),v=s(m),b=We(v);if(u.has("serviceResolutions")&&(b.serviceResolutions=m.evaluation&&m.strictValidation?await l().then(y=>ae(v,y)):null),u.has("integrationSchemas")||u.has("integrationFieldRegistry")){let y=Ge(v,c?.appProvider);u.has("integrationSchemas")&&(b.integrationSchemas=y.integrationSchemas),u.has("integrationFieldRegistry")&&(b.integrationFieldRegistry=y.integrationFieldRegistry)}return b},async loadService(c,u){let m=Ie("service",u?.include),v=await j(e),b=s(v),y=Be(b,c);return m.has("integrationSchemas")&&(y.integrationSchemas=Xe(b,c)),y},async loadServiceResolutions(c){let u=await j(e);i(u);let m=await l();return ae(s(u),m,c)},async loadServiceResolution(c){let u=await j(e);i(u);let m=await l();return Ze(c,s(u),m)},async loadProviders(){let c=await j(e);return Ke(s(c))},async loadProvider(c,u){let m=Ie("provider",u?.include),v=await j(e);return ze(s(v),c,m)},async loadProviderService(c,u){let m=await j(e);return Ue(s(m),c,u)},async loadCredentials(){let c=await j(e);return en(s(c))},async loadCredentialTypes(c){let u=await j(e);return nn(s(u),c)}},p=Fe(e,{onApplied:n.onApplied,cache:r}),w={async writeAppIntegration(c,u){await p.warmup();let m=await j(e);return i(m),f(()=>p.applyApp(c,u))},async writeModuleIntegration(c,u,m){await p.warmup();let v=await j(e);return i(v),f(()=>p.applyModule(c,u,m))},async writeServiceIntegration(c,u,m){await p.warmup();let v=await j(e);return i(v),f(()=>p.applyService(c,u,m))},async deleteServiceIntegration(c){await p.warmup();let u=await j(e);return i(u),f(()=>p.deleteService(c))}},C={invalidate(){r.invalidate(),be(e)},async resolveContracts(){return j(e)},walkManifests(){return g(e)},parseModuleDescriptor(c){return ee(c)},loadProviderCatalogRaw(){return Te(e)},async readIntegrationsConfig(){return W(e,r)},async writeIntegrationsConfig(c){await p.warmup();let u=await j(e);return i(u),f(()=>tr(e,c,r))},async ensureMockBindings(){await p.warmup();let c=await j(e);return i(c),f(()=>$e(e))},async evaluateIntegrationServices(c){await p.warmup();let u=await j(e);i(u);let m={resolveEffectiveService:d(u.module.resolveEffectiveService,"resolveEffectiveService"),buildSupplyLookup:d(u.module.buildSupplyLookup,"buildSupplyLookup"),moduleDomainOf:d(u.module.moduleDomainOf,"moduleDomainOf"),normalizeInstances:d(u.module.normalizeInstances,"normalizeInstances"),RESOURCE_TYPE_IDS:u.module.RESOURCE_TYPE_IDS??[]};return Z({...c,contracts:m})}};function g(c){return P(c,r)}return{...S,...w,...C}}function tr(e,n,r){let t=Xt.join(e,"config","integrations.json");$(t,`${JSON.stringify(n,null,2)}
|
|
9
|
+
`),r.invalidate([t])}export{se as APP_INTEGRATION_NODE_KEYS,A as AppToolkitError,K as SAFE_NAME_PATTERN,St as assertValidModuleId,re as buildPredicateAssembly,Ee as checkInstanceKeys,kt as collectDeclaredModules,Ae as contractsNotResolvedMessage,nr as createAppToolkit,ke as createFileCache,Fe as createWriteCore,Ot as credentialArtifactExists,Q as deepEqual,Y as deriveStem,ce as echoForRef,fn as ensureDir,$e as ensureMockBindings,Pe as evaluateAppIntegrationIssues,Z as evaluateIntegrationServices,Ce as expandEnvVars,Cn as extractModuleIdFromSource,be as invalidateContractsResolver,z as isControlledInlineProvider,In as isValidModuleId,Gt as loadAppIntegrationSchemas,We as loadAppView,nn as loadCredentialTypesView,en as loadCredentialsView,Le as loadModuleDetailView,Qt as loadModuleIntegrationSchemas,_e as loadModulesView,ge as loadProviderCatalog,Te as loadProviderCatalogRaw,ze as loadProviderDetailView,Ue as loadProviderServiceDetailView,Ke as loadProvidersView,Be as loadServiceDetailView,Xe as loadServiceIntegrationSchemas,Ze as loadServiceResolutionDetail,ae as loadServiceResolutionsView,pe as manifestPath,le as maskCredentialValues,on as moduleDependencySchema,ln as moduleDescriptorSchema,tn as pageContribSchema,ee as parseModuleDescriptor,sn as providerSlotContribSchema,Pt as pruneIntegrationBindings,Rn as readAppManifest,je as readAppManifestFull,ne as readCredentialFile,W as readIntegrationsConfig,xn as readIntegrationsStrict,oe as reconcileOrphanCredentials,Sn as refToEnvKey,mt as removeNpmModule,j as resolveContractsForApp,j as resolveContractsForAppExport,qe as resolveLayerIntegrationSchemas,He as resolveServiceSummary,cn as resourceNeedContribSchema,an as serviceNeedContribSchema,F as stemToFilePath,tt as stemToRef,rn as tabContribSchema,yt as updateNpmModule,vt as upsertMiniappContainer,gt as upsertNpmModule,P as walkManifests,ft as writeAppManifest,$ as writeAtomic,xe as writeCredentialFile};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tbox.cn/app-toolkit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "tbox 应用工具面(运行面/工具面对仗:app-sdk 给运行的应用用,app-toolkit 给操作应用的工具用)——模块解析、集成配置求值/校验/写入内核,contracts 经应用侧动态解析(沙箱与目标应用版本解耦)。",
|
|
6
6
|
"exports": {
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"zod": "3.25.76"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
|
-
"@tbox.cn/app-contracts": "0.
|
|
21
|
+
"@tbox.cn/app-contracts": "0.11.0",
|
|
22
22
|
"typescript": "5.9.3",
|
|
23
23
|
"tsup": "8.5.1",
|
|
24
24
|
"vitest": "4.1.10",
|
|
@@ -20,6 +20,12 @@ export interface NpmModuleEntry {
|
|
|
20
20
|
mode: ModuleMode;
|
|
21
21
|
/** 3-way merge 基线(codegen 模式) */
|
|
22
22
|
baseVersion?: string;
|
|
23
|
+
/**
|
|
24
|
+
* 条目类别(单一模板统一化 W2-4):'ui-kit' = 壳 UI 组件包——create 全量继承后
|
|
25
|
+
* 不再承担保留判据(模板 manifest = 出厂装配真源);现职 = update 三方合并锚 +
|
|
26
|
+
* sync-agent-ui-kit 副本 identity 标记(由该脚本派生维护)。
|
|
27
|
+
*/
|
|
28
|
+
kind?: 'ui-kit';
|
|
23
29
|
}
|
|
24
30
|
|
|
25
31
|
export interface AppManifest {
|
|
@@ -15,9 +15,15 @@ import { readAppManifestFull, type NpmModuleEntry, type AppManifestFull } from '
|
|
|
15
15
|
* 3. serviceSchemas:per-service schema 复合键表——键 `${provider}\u0000${service}`(H2,同一服务
|
|
16
16
|
* 多厂商供给各自 schema 零碰撞);多 impl 同 (provider, service) 异 schema → 声明序首个
|
|
17
17
|
* 4. vocabulary:所有携带 service-slots.json 的已装契约包该文件 `.slots` 键并集(fs 直读、
|
|
18
|
-
* `$comment`
|
|
19
|
-
*
|
|
20
|
-
*
|
|
18
|
+
* `$comment` 忽略;探测三跳 = ① <候选>/node_modules/@tbox.cn/<pkg>/service-slots.json(包根)
|
|
19
|
+
* ② 同包 src/service-slots.json(发布包 src 附带 / 模板 shim 链接)
|
|
20
|
+
* ③ 候选自身 {service-slots.json, src/service-slots.json}(local/codegen 全量副本——
|
|
21
|
+
* mall 生成应用主形态,不依赖 install);零缓存枚举,与 manifest 探测同机制;
|
|
22
|
+
* 多行业 contracts-* 天然扩展 F8);缺席 → [](退化 demanded ∪ configured)
|
|
23
|
+
* 5. slotMeta:词汇 meta 节(per-service {title,description} 展示元数据——消费端兜底链
|
|
24
|
+
* 词汇 > demand > id,views/context resolveServiceDisplay 单源);多包发现序首声明胜出
|
|
25
|
+
* (与 catalog addSlots 同语义);contracts-mall 词汇锁保证 mall 域全量覆盖;缺席 → {}
|
|
26
|
+
* 6. domainKeyIssues:跨模块域键冲突 issue(两模块派生同一 domains 键——P13)
|
|
21
27
|
*
|
|
22
28
|
* **约束(D26;FX-3/F7 边界)**:目录枚举零缓存——readdir / node_modules 候选探测 / `.tbox/app.json` 读取 /
|
|
23
29
|
* dev-manifest 读取每次执行(枚举输入保持 fresh——agent 装卸模块下一笔即见);manifest/service-slots
|
|
@@ -35,10 +41,48 @@ export interface WalkManifestsResult {
|
|
|
35
41
|
serviceSchemas: Record<string, { configSchema?: string; credentialSchema?: string; packageDir: string }>;
|
|
36
42
|
/** 服务词汇 = 所有携带 service-slots.json 的已装契约包 `.slots` 键并集(缺席 → []) */
|
|
37
43
|
vocabulary: string[];
|
|
44
|
+
/** 词汇 meta 节:per-service {title,description} 展示元数据(多包首声明胜出;缺席 → {}) */
|
|
45
|
+
slotMeta: Record<string, SlotMeta>;
|
|
38
46
|
/** 跨模块域键冲突(P13——两模块的 service 集派生同一 domains 键) */
|
|
39
47
|
domainKeyIssues: Array<{ severity: 'warning'; code: 'DOMAIN_KEY_CONFLICT'; message: string; module?: string }>;
|
|
40
48
|
}
|
|
41
49
|
|
|
50
|
+
/** 槽位展示元数据(service-slots.json meta 节元素——宽松提取,字段皆可选) */
|
|
51
|
+
export interface SlotMeta {
|
|
52
|
+
title?: string;
|
|
53
|
+
description?: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** 词汇文件单次解析产物(slots 与 meta 同源同读——防双读漂移) */
|
|
57
|
+
interface SlotsFile {
|
|
58
|
+
slots: string[];
|
|
59
|
+
meta: Record<string, SlotMeta>;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** 词汇文件解析(宽松:坏文件/缺 slots 数组 → null;meta 非对象条目/无有效字段跳过) */
|
|
63
|
+
function parseSlotsFile(raw: string): SlotsFile | null {
|
|
64
|
+
try {
|
|
65
|
+
const parsed = JSON.parse(raw) as { slots?: unknown; meta?: unknown };
|
|
66
|
+
if (!Array.isArray(parsed.slots)) return null;
|
|
67
|
+
const slots = parsed.slots.filter((s): s is string => typeof s === 'string');
|
|
68
|
+
const meta: Record<string, SlotMeta> = {};
|
|
69
|
+
if (parsed.meta && typeof parsed.meta === 'object' && !Array.isArray(parsed.meta)) {
|
|
70
|
+
for (const [svc, entry] of Object.entries(parsed.meta as Record<string, unknown>)) {
|
|
71
|
+
if (!entry || typeof entry !== 'object' || Array.isArray(entry)) continue;
|
|
72
|
+
const { title, description } = entry as { title?: unknown; description?: unknown };
|
|
73
|
+
const m: SlotMeta = {};
|
|
74
|
+
if (typeof title === 'string' && title.length > 0) m.title = title;
|
|
75
|
+
if (typeof description === 'string' && description.length > 0) m.description = description;
|
|
76
|
+
if (m.title !== undefined || m.description !== undefined) meta[svc] = m;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return { slots, meta };
|
|
80
|
+
} catch {
|
|
81
|
+
/* 宽松:坏文件跳过 */
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
42
86
|
export interface WalkModuleInfo {
|
|
43
87
|
/** install id(packages/ 目录名 / npmModules entry.id) */
|
|
44
88
|
id: string;
|
|
@@ -49,12 +93,17 @@ export interface WalkModuleInfo {
|
|
|
49
93
|
descriptor: ModuleDescriptor;
|
|
50
94
|
}
|
|
51
95
|
|
|
52
|
-
/** 富形态供给条目:per-service {implementation, credentialType} 恒数组保真(H1
|
|
96
|
+
/** 富形态供给条目:per-service {implementation, credentialType} 恒数组保真(H1);
|
|
97
|
+
* title/description = 厂商展示元数据(strict manifest descriptor 透传——多包同厂商
|
|
98
|
+
* 首声明胜出,与 owner 同语义;B1 walk 层传播) */
|
|
53
99
|
export interface WalkProviderEntry {
|
|
54
100
|
provider: string;
|
|
55
101
|
implementation: string;
|
|
56
102
|
credentialType: string;
|
|
57
103
|
owner: string;
|
|
104
|
+
/** 厂商展示名(manifest 模块 meta——多包首声明;缺席 = undefined) */
|
|
105
|
+
title?: string;
|
|
106
|
+
description?: string;
|
|
58
107
|
local?: boolean;
|
|
59
108
|
configSchema?: string;
|
|
60
109
|
credentialSchema?: string;
|
|
@@ -174,10 +223,15 @@ function readDevManifestRedirects(appDir: string): DevManifestRedirect[] {
|
|
|
174
223
|
}
|
|
175
224
|
}
|
|
176
225
|
|
|
177
|
-
/**
|
|
178
|
-
*
|
|
179
|
-
|
|
180
|
-
|
|
226
|
+
/** 契约包发现链(探测三跳——词汇源真实布局全覆盖,B0):候选集(appDir、apps/*、packages/*)×
|
|
227
|
+
* ① node_modules/@tbox.cn/<pkg>/service-slots.json(包根——夹具/旧布局)
|
|
228
|
+
* ② 同包 src/service-slots.json(发布包 src 随 files 附带 / 模板 shim 链接到仓库源 src/)
|
|
229
|
+
* ③ 候选自身 {service-slots.json, src/service-slots.json}(local/codegen 全量副本——mall 生成应用
|
|
230
|
+
* 主形态,stage rewriteExportsToSrc 展开后永驻 packages/*,不依赖 install)
|
|
231
|
+
* 同包双在场(异常态)root 优先。slots 与 meta 单次解析同产(目录枚举零缓存,F8;
|
|
232
|
+
* FX-3:文件**内容**经 file-cache)。 */
|
|
233
|
+
function discoverContractPackages(appDir: string, cache?: FileCache): Array<{ dir: string; slots: string[]; meta: Record<string, SlotMeta> }> {
|
|
234
|
+
const found: Array<{ dir: string; slots: string[]; meta: Record<string, SlotMeta> }> = [];
|
|
181
235
|
const candidates: Array<string> = [appDir];
|
|
182
236
|
for (const sub of ['apps', 'packages']) {
|
|
183
237
|
const parent = join(appDir, sub);
|
|
@@ -191,7 +245,12 @@ function discoverContractPackages(appDir: string, cache?: FileCache): Array<{ di
|
|
|
191
245
|
}
|
|
192
246
|
}
|
|
193
247
|
const seenRoots = new Set<string>();
|
|
248
|
+
const addDiscovered = (dir: string, file: string): void => {
|
|
249
|
+
const parsed = cache ? cache.read(file, parseSlotsFile) : parseSlotsFile(readFileSync(file, 'utf8'));
|
|
250
|
+
if (parsed !== null && parsed !== undefined) found.push({ dir, slots: parsed.slots, meta: parsed.meta });
|
|
251
|
+
};
|
|
194
252
|
for (const dir of candidates) {
|
|
253
|
+
// 跳 1/2:node_modules 位(包根 → src——显式安装真源优先)
|
|
195
254
|
const scopeDir = join(dir, 'node_modules', '@tbox.cn');
|
|
196
255
|
if (!existsSync(scopeDir)) continue;
|
|
197
256
|
let names: string[] = [];
|
|
@@ -204,19 +263,16 @@ function discoverContractPackages(appDir: string, cache?: FileCache): Array<{ di
|
|
|
204
263
|
const pkgDir = join(scopeDir, name);
|
|
205
264
|
if (seenRoots.has(pkgDir)) continue;
|
|
206
265
|
seenRoots.add(pkgDir);
|
|
207
|
-
const
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
};
|
|
218
|
-
const slots = cache ? cache.read(slotsFile, parseSlots) : parseSlots(readFileSync(slotsFile, 'utf8'));
|
|
219
|
-
if (slots !== null && slots !== undefined) found.push({ dir: pkgDir, slots });
|
|
266
|
+
const rootFile = join(pkgDir, 'service-slots.json');
|
|
267
|
+
const srcFile = join(pkgDir, 'src', 'service-slots.json');
|
|
268
|
+
if (existsSync(rootFile)) addDiscovered(pkgDir, rootFile);
|
|
269
|
+
else if (existsSync(srcFile)) addDiscovered(pkgDir, srcFile);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
// 跳 3:候选自身(local/codegen 全量副本形态——后置于 nm 位:真源 > 副本,meta 首声明序随此)
|
|
273
|
+
for (const dir of candidates) {
|
|
274
|
+
for (const file of [join(dir, 'service-slots.json'), join(dir, 'src', 'service-slots.json')]) {
|
|
275
|
+
if (existsSync(file)) addDiscovered(dir, file);
|
|
220
276
|
}
|
|
221
277
|
}
|
|
222
278
|
return found;
|
|
@@ -232,7 +288,7 @@ export function walkManifests(appDir: string, cache?: FileCache): WalkManifestsR
|
|
|
232
288
|
/** 域键 → 首个声明模块(P13 冲突检测) */
|
|
233
289
|
const domainOwners = new Map<string, string>();
|
|
234
290
|
|
|
235
|
-
const addSlots = (slots: readonly ProviderSlotContrib[], owner: string, packageDir: string): void => {
|
|
291
|
+
const addSlots = (slots: readonly ProviderSlotContrib[], owner: string, packageDir: string, modMeta?: SlotMeta): void => {
|
|
236
292
|
for (const slot of slots) {
|
|
237
293
|
// 富形态:per-service {implementation, credentialType} 保真(同厂商多 impl 合法——声明序)
|
|
238
294
|
const vendorSlots = (catalog.providers[slot.provider] ??= {});
|
|
@@ -245,6 +301,8 @@ export function walkManifests(appDir: string, cache?: FileCache): WalkManifestsR
|
|
|
245
301
|
implementation: slot.implementation,
|
|
246
302
|
credentialType: slot.credentialType,
|
|
247
303
|
owner,
|
|
304
|
+
...(modMeta?.title ? { title: modMeta.title } : {}),
|
|
305
|
+
...(modMeta?.description ? { description: modMeta.description } : {}),
|
|
248
306
|
...(slot.local ? { local: true } : {}),
|
|
249
307
|
...(slot.configSchema ? { configSchema: slot.configSchema } : {}),
|
|
250
308
|
...(slot.credentialSchema ? { credentialSchema: slot.credentialSchema } : {}),
|
|
@@ -268,6 +326,10 @@ export function walkManifests(appDir: string, cache?: FileCache): WalkManifestsR
|
|
|
268
326
|
}
|
|
269
327
|
};
|
|
270
328
|
|
|
329
|
+
/** 厂商展示元数据(strict manifest descriptor 透传——loose 形态无 descriptor → undefined) */
|
|
330
|
+
const modMetaOf = (manifest: ReadManifestResult): SlotMeta | undefined =>
|
|
331
|
+
manifest.kind === 'strict' ? { title: manifest.descriptor.title, description: manifest.descriptor.description } : undefined;
|
|
332
|
+
|
|
271
333
|
const addServiceNeed = (
|
|
272
334
|
need: { service: string; optional?: boolean; defaults?: Array<{ provider: string; implementation: string }>; title?: string; description?: string },
|
|
273
335
|
owner: string,
|
|
@@ -324,7 +386,7 @@ export function walkManifests(appDir: string, cache?: FileCache): WalkManifestsR
|
|
|
324
386
|
modules.push({ id: entry.id, dir, pkg: entry.mode === 'sdk' ? entry.package : `@app/${entry.id}`, descriptor: manifest.descriptor });
|
|
325
387
|
}
|
|
326
388
|
const slots = contributes.providers?.slots ?? [];
|
|
327
|
-
if (slots.length) addSlots(slots, entry.package, dir);
|
|
389
|
+
if (slots.length) addSlots(slots, entry.package, dir, modMetaOf(manifest));
|
|
328
390
|
const needs = contributes.services ?? [];
|
|
329
391
|
if (needs.length) {
|
|
330
392
|
for (const need of needs) {
|
|
@@ -340,14 +402,14 @@ export function walkManifests(appDir: string, cache?: FileCache): WalkManifestsR
|
|
|
340
402
|
if (!manifest?.name || seen.has(manifest.name)) continue;
|
|
341
403
|
seen.add(manifest.name);
|
|
342
404
|
const slots = manifest.contributes.providers?.slots ?? [];
|
|
343
|
-
if (slots.length) addSlots(slots, pkg, dir);
|
|
405
|
+
if (slots.length) addSlots(slots, pkg, dir, modMetaOf(manifest));
|
|
344
406
|
const needs = manifest.contributes.services ?? [];
|
|
345
407
|
if (needs.length) {
|
|
346
408
|
for (const need of needs) addServiceNeed(need, pkg);
|
|
347
409
|
}
|
|
348
410
|
}
|
|
349
411
|
|
|
350
|
-
// 通道 2:packages/* 约定发现(本地模块——npmModules
|
|
412
|
+
// 通道 2:packages/* 约定发现(本地模块——npmModules 未登记形态,如手工放入的本地模块)
|
|
351
413
|
const packagesDir = join(appDir, 'packages');
|
|
352
414
|
if (existsSync(packagesDir)) {
|
|
353
415
|
let entries: string[] = [];
|
|
@@ -369,7 +431,7 @@ export function walkManifests(appDir: string, cache?: FileCache): WalkManifestsR
|
|
|
369
431
|
modules.push({ id: modName, dir, pkg: `@app/${modName}`, descriptor: manifest.descriptor });
|
|
370
432
|
}
|
|
371
433
|
const slots = contributes.providers?.slots ?? [];
|
|
372
|
-
if (slots.length) addSlots(slots, `@app/${modName}`, dir);
|
|
434
|
+
if (slots.length) addSlots(slots, `@app/${modName}`, dir, modMetaOf(manifest));
|
|
373
435
|
const needs = contributes.services ?? [];
|
|
374
436
|
if (needs.length) {
|
|
375
437
|
for (const need of needs) {
|
|
@@ -379,13 +441,18 @@ export function walkManifests(appDir: string, cache?: FileCache): WalkManifestsR
|
|
|
379
441
|
}
|
|
380
442
|
}
|
|
381
443
|
|
|
382
|
-
// 产物 4
|
|
444
|
+
// 产物 4/5:服务词汇 + 词汇 meta(契约包 service-slots.json 并集——零缓存枚举;
|
|
445
|
+
// meta 多包发现序首声明胜出,与 catalog addSlots 同语义)
|
|
383
446
|
const vocabulary = new Set<string>();
|
|
447
|
+
const slotMeta: Record<string, SlotMeta> = {};
|
|
384
448
|
for (const pkg of discoverContractPackages(appDir, cache)) {
|
|
385
449
|
for (const s of pkg.slots) vocabulary.add(s);
|
|
450
|
+
for (const [svc, m] of Object.entries(pkg.meta)) {
|
|
451
|
+
if (slotMeta[svc] === undefined) slotMeta[svc] = m;
|
|
452
|
+
}
|
|
386
453
|
}
|
|
387
454
|
|
|
388
|
-
return { modules, catalog, serviceSchemas, vocabulary: [...vocabulary].sort(), domainKeyIssues };
|
|
455
|
+
return { modules, catalog, serviceSchemas, vocabulary: [...vocabulary].sort(), slotMeta, domainKeyIssues };
|
|
389
456
|
}
|
|
390
457
|
|
|
391
458
|
// ── CLI collectDeclared 迁入(C5——声明集合 + 双端入口探测 + declaration.moduleId 提取)──
|