@tbox.cn/app-toolkit 0.4.0 → 0.6.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.
Files changed (40) hide show
  1. package/dist/chunk-WUULQDOV.js +1 -0
  2. package/dist/contracts-resolver-PS2UH2G6.js +1 -0
  3. package/dist/index.d.ts +374 -150
  4. package/dist/index.js +9 -9
  5. package/package.json +2 -2
  6. package/src/assembly/provider-catalog.ts +1 -1
  7. package/src/assembly/resolve-key.ts +75 -0
  8. package/src/assembly/walk-manifests.ts +138 -85
  9. package/src/core/contracts-expected.ts +2 -2
  10. package/src/core/contracts-resolver.ts +12 -1
  11. package/src/core/module-schema.ts +90 -14
  12. package/src/dto.ts +80 -33
  13. package/src/factory.ts +78 -26
  14. package/src/index.ts +16 -7
  15. package/src/integrations/mock-bindings.ts +1 -1
  16. package/src/integrations/module-binding.ts +60 -0
  17. package/src/integrations/predicate-io.ts +26 -5
  18. package/src/integrations/predicate.ts +97 -57
  19. package/src/integrations/prune-bindings.ts +33 -14
  20. package/src/integrations/write.ts +47 -27
  21. package/src/views/app.ts +4 -4
  22. package/src/views/context.ts +15 -14
  23. package/src/views/integration-schemas.ts +76 -18
  24. package/src/views/modules.ts +93 -25
  25. package/src/views/providers.ts +5 -5
  26. package/src/views/service-detail.ts +1 -1
  27. package/src/views/service-resolutions.ts +14 -13
  28. package/tests/credentials-view.test.ts +2 -2
  29. package/tests/demo-app.ts +11 -6
  30. package/tests/dev-manifest-catalog.test.ts +14 -14
  31. package/tests/file-cache.test.ts +5 -5
  32. package/tests/module-resources.test.ts +148 -0
  33. package/tests/module-schema.test.ts +120 -20
  34. package/tests/resolve-key.test.ts +154 -0
  35. package/tests/vendor-schema-keywords.test.ts +74 -0
  36. package/tests/views.test.ts +585 -247
  37. package/tests/write-core.test.ts +117 -0
  38. package/dist/chunk-KPD3LUH4.js +0 -1
  39. package/dist/contracts-resolver-E4SECBOG.js +0 -1
  40. package/src/integrations/domain-binding.ts +0 -81
package/src/dto.ts CHANGED
@@ -3,6 +3,11 @@
3
3
  * agtcodingbox / tbox-ai-coding 经 `import type` 消费(L2 DTO 单源——API 层零类型镜像)。
4
4
  * 词汇七概念:app / module / service / instance / provider / integration / resolution。
5
5
  * 弃用词汇清单见 api.md §3 尾(component/slot/scope/configMode/…——防复活)。
6
+ *
7
+ * **v7 词汇统一破例声明(一次性收敛,此后恢复 additive-only)**:标识/展示名分轴
8
+ * (id = 标识、name = 展示名、description = 描述——对齐实例轴 {id, name}),
9
+ * title 键全量退役(与 manifest name→id/title→name 同一变更);`.title` 消费方
10
+ * (外部仓 import type / 运行时读)需同窗迁移。
6
11
  */
7
12
 
8
13
  // ===== 词汇基线 =====
@@ -30,17 +35,19 @@ export interface ModulesView {
30
35
  export interface ModuleDeclaration {
31
36
  /** install id(packages/ 目录名 / .tbox/app.json entry.id)——恒业务模块(provider 家族不在本清单) */
32
37
  id: string;
33
- /** 包名 */
38
+ /** 展示名(v7 词汇统一:manifest name ?? id——恒有值) */
34
39
  name: string;
35
- title: string;
36
40
  description: string;
41
+ /** 隐藏模块(additive——manifest hidden === true 时携带;默认清单已过滤,本字段仅供
42
+ * ?includeHidden=true 可见时徽标呈现) */
43
+ hidden?: boolean;
37
44
  }
38
45
 
39
46
  /** 服务需求声明(模块 demand 单元 / 平台服务条目——v4.4 D29 双端点同型;defaults 唯一落位 ServiceDetail) */
40
47
  export interface ServiceDemand {
41
48
  service: string;
42
- /** manifest demand meta;缺席 UI 回退 service id */
43
- title?: string;
49
+ /** manifest demand meta(v7 title→name);缺席 UI 回退 service id */
50
+ name?: string;
44
51
  description?: string;
45
52
  /** demand optional === false(多模块 OR 聚合见 resolution echo;平台服务条目恒 false) */
46
53
  required: boolean;
@@ -50,44 +57,50 @@ export interface ServiceDemand {
50
57
 
51
58
  /** GET /modules/:module(模块静态一站式 · PUT 读对偶) */
52
59
  export interface ModuleDetailView {
53
- /** B1 additive:模块 meta 展示名(manifest title;视图层兜底 name——恒有值) */
54
- title: string;
60
+ /** 模块展示名(v7 词汇统一:manifest name ?? id——恒有值) */
61
+ name: string;
55
62
  /** B1 additive:模块 meta 描述(manifest description;兜底 '') */
56
63
  description: string;
57
- /** v4.4 D34:demand 服务首段去重(排序;1..N;零域 = []) */
58
- domainKeys: string[];
59
- /** domains[换算键] 节点原值;未配置 = null;v4.4 D34:仅单域(writable)时在场——多域/零域/共享键无单键落点 */
64
+ /** 隐藏模块(additive——manifest hidden === true 时携带;详情恒 200 不受清单过滤影响,
65
+ * 本字段仅供面板徽标呈现) */
66
+ hidden?: boolean;
67
+ /** v6 所有权模型:模块归属服务(declare.owns 展平,排序;零归属 = [])——
68
+ * v5 domainKeys 退役(破坏性更名;面板 presence-detection 双读义务见 api.md §3) */
69
+ ownedServices: string[];
70
+ /** modules[install id] 集成位原值(provider/config;resources 不投影——见 resources 字段);未配置 = null */
60
71
  integration: ModuleIntegrationNode | null;
61
- /** v4.4 D34:每域一条厂商绑定解析(单域 = 单元素;零域 = [];resolveDomainBinding 单源) */
62
- providerBindings: ProviderBinding[];
63
- /** v4.4 D34:单域 && 域键无跨模块共享(moduleDomainWritable 单源——读标志与写守卫共用) */
72
+ /** 模块资源只读镜像(N2 读半边——写侧 resources 恒保全;含 ref 台账展开态 resolvedDatasetId);
73
+ * 无模块键/无资源节 = null */
74
+ resources?: ModuleResourcesEcho | null;
75
+ /** v6 单数化(v5 providerBindings[] 退役):模块级厂商绑定解析(resolveModuleBinding 单源;
76
+ * 单值作用于全部 ownedServices) */
77
+ binding: ModuleBinding | null;
78
+ /** v6:owns.length > 0(moduleOwnershipWritable 单源——读标志与写守卫共用) */
64
79
  configEditable: boolean;
65
- /** 本模块 demand(卡片展开 + 编辑器静态上下文自足) */
80
+ /** 本模块 demand(declare.consumes;卡片展开 + 编辑器静态上下文自足) */
66
81
  services: ServiceDemand[];
67
82
  /** v4.4 D31:include=serviceResolutions 内嵌(demand 服务过滤 ≡ 批量 ?service= 语义;<0.9/形态异常 → null——永不 503) */
68
83
  serviceResolutions?: ServiceResolutionsView | null;
69
- /** v4.4 D32:include=integrationSchemas 层级富化(多域/零域/共享键/未绑定 → null) */
84
+ /** v4.4 D32:include=integrationSchemas 层级富化(零归属/未绑定 → null) */
70
85
  integrationSchemas?: LayerIntegrationSchemas | null;
71
86
  /** v4.6 D36:include=integrationFieldRegistry 集成字段注册表(null 语义镜像 integrationSchemas;
72
87
  * 未知厂商/零足迹 → 非 null 空集 + unsupplied 回填——D37 轴参假想计算同此) */
73
88
  integrationFieldRegistry?: IntegrationFieldRegistryView | null;
74
89
  }
75
90
 
76
- /** 模块域厂商绑定解析(v4.4 D34——静态文件事实;规则 ≡ 谓词 #7 域级,resolveDomainBinding 单源) */
77
- export interface ProviderBinding {
78
- /** 域键(= domainKeys 成员;多域模块每域一条) */
79
- domain: string;
80
- /** domains[domain].provider ?? root.provider(双缺 = null) */
81
- effective: string | null;
91
+ /** 模块级厂商绑定解析(v6——静态文件事实;规则 ≡ 谓词 #7 模块层,resolveModuleBinding 单源) */
92
+ export interface ModuleBinding {
93
+ /** modules[install id].provider ?? root.provider(双缺 = null) */
94
+ provider: string | null;
82
95
  /** 生效来源层(双缺 = null) */
83
96
  layer: 'module' | 'app' | null;
84
97
  }
85
98
 
86
99
  /** 层级 config schema 计算(v4.4 D32——静态轴富化,零 contracts) */
87
100
  export interface LayerIntegrationSchemas {
88
- /** 层生效厂商(resolveDomainBinding 单源) */
101
+ /** 层生效厂商(resolveModuleBinding 单源) */
89
102
  provider: string;
90
- /** 模块级 = 本模块 demand ∩ 该厂商供给;应用级 = 供给足迹全量(configSchema = raw draft 2020-12 原文;供给在场未声明 = null) */
103
+ /** 模块级 = 本模块 ownedServices ∩ 该厂商供给;应用级 = 供给足迹全量(configSchema = raw draft 2020-12 原文;供给在场未声明 = null) */
91
104
  services: Record<string, { configSchema: object | null }>;
92
105
  }
93
106
 
@@ -102,13 +115,16 @@ export interface IntegrationFieldRegistryView {
102
115
  unsupplied: string[];
103
116
  /** 作用域内全部非 null schema 的 additionalProperties 均 ≠ false(聚合;空真) */
104
117
  additionalPropertiesAllowed: boolean;
105
- /** 逐 key 合并产物(首声明插入序 = scope 遍历序:app 字母序 / module demand 序) */
118
+ /** 逐 key 合并产物(首声明插入序 = scope 遍历序:app 字母序 / module ownedServices 序) */
106
119
  fields: Array<{
107
120
  key: string;
108
121
  /** null = 类型不可调和(R3/R2 异构/R1 enum 不等——冲突折叠,客户端 RawJson 保真) */
109
122
  schema: object | null;
110
123
  /** required 聚合 = sources.some(s => s.required);conflict 字段照常聚合 */
111
124
  sources: Array<{ service: string; required: boolean }>;
125
+ /** G 批:x-tbox-global 聚合旗标(sources.some——任一来源声明即 true;app/module 轴表单
126
+ * 仅渲染 true 字段,非全局键落 ExtraFields;service 轴全量透传) */
127
+ global: boolean;
112
128
  }>;
113
129
  }
114
130
 
@@ -139,12 +155,18 @@ export interface ServiceDetail extends ServiceDemand {
139
155
  /** v4.7 D38:include=integrationSchemas 生效绑定富化(三态:null 全链未绑定 /
140
156
  * 绑定无可计算 schema / 含 schema;malformed 节点 → null——不放行域级联) */
141
157
  integrationSchemas?: ServiceIntegrationSchemas | null;
158
+ /** G 批:include=integrationFieldRegistry 服务轴注册表(loadServiceFieldRegistry——
159
+ * 四分支链推导复用 D38;fields = 该 schema 全部字段带 global 旗标;null 语义镜像 D38。
160
+ * v5.1 D39:serviceProvider 轴参 = 假想重定向——仅表单投影按 X 计算,带非空轴参时
161
+ * registry 恒非 null(豁免链③不镜像实际态 null);同构 ≡ PUT 改写 provider 后实读) */
162
+ integrationFieldRegistry?: IntegrationFieldRegistryView | null;
142
163
  }
143
164
 
144
165
  /** 服务级生效绑定 schema(v4.7 D38 静态轴富化——零 contracts,永不 503;实例层无条件排除:
145
166
  * node 级口径 ≠ resolution 默认实例口径,api.md §4.4 注) */
146
167
  export interface ServiceIntegrationSchemas {
147
- /** 生效厂商:显式(含内联声明名)?? domains[首段].provider ?? root.provider */
168
+ /** 生效厂商:显式(含内联声明名)?? modules[ownerOf].provider ?? root.provider
169
+ * v5.1 D39:serviceProvider 轴参在场时 = 假想短路值(非链解析产物) */
148
170
  provider: string;
149
171
  /** catalog 复合键(多 impl 声明序首个)> 内联 ref;miss(未知厂商/未供给/内联缺 ref)→ null(富化非门控) */
150
172
  configSchema: object | null;
@@ -211,8 +233,8 @@ export interface AppIntegrationNode {
211
233
  }
212
234
 
213
235
  /** AppIntegrationNode 键集单源(runtime const——write applyApp 全量替换白名单
214
- * (缺席根键删除;services/domains/modules 域不在其中)+ views/app 投影(GET 预填 ≡ PUT body,
215
- * D21/L1——文件含 services/domains 但 root 节点投影只含本键集)) */
236
+ * (缺席根键删除;services/modules 域不在其中;v6 domains 键退役)+ views/app 投影
237
+ * (GET 预填 ≡ PUT body,D21/L1——文件含 services/modules 但 root 节点投影只含本键集)) */
216
238
  export const APP_INTEGRATION_NODE_KEYS = [
217
239
  'provider',
218
240
  'config',
@@ -223,12 +245,37 @@ export const APP_INTEGRATION_NODE_KEYS = [
223
245
  'defaultInstanceId',
224
246
  ] as const;
225
247
 
226
- /** 模块域节点(恒字符串 provider——域层无凭据位,契约事实) */
248
+ /** 模块集成位节点(v6——provider 绑定默认 + config 级联层;resources 不投影不写——
249
+ * 集成位编辑恒保全资源节,N2) */
227
250
  export interface ModuleIntegrationNode {
228
251
  provider?: string;
229
252
  config?: Record<string, unknown>;
230
253
  }
231
254
 
255
+ /** ModuleIntegrationNode 可写键集单源(v6——write applyModule 全量替换白名单
256
+ * (键集外 400;resources 恒随既有节点保全)+ views/modules 投影(GET ≡ PUT,三同构)) */
257
+ export const MODULE_INTEGRATION_NODE_KEYS = ['provider', 'config'] as const;
258
+
259
+ /** 模块资源回显(只读投影——N2 读半边:写侧仍仅 provider/config,resources 恒保全)。
260
+ * 原值文件镜像(L1)+ ref 台账展开态:`ref` 在场且同目录 knowledge.json 命中时
261
+ * `resolvedDatasetId` = 台账 datasetId(镜像 SDK resolveKnowledge 展开语义),未命中/台账缺失 = null。
262
+ * 路径锚定边界:视图恒读 `<appDir>/config/`(与 toolkit readIntegrationsConfig 同源——
263
+ * `TBOX_INTEGRATIONS_FILE` 为 SDK 运行时逃生舱,视图不跟随,既有分叉)。 */
264
+ export interface ModuleResourceEcho {
265
+ type: 'knowledge';
266
+ /** 文件原值直填 datasetId(与 ref 互斥使用——并存时 SDK 侧 datasetId 优先) */
267
+ datasetId?: string;
268
+ /** 文件原值 ref(台账引用——knowledge.json knowledge_bases 键) */
269
+ ref?: string;
270
+ /** 展开态:datasetId 直填时 = 该值;ref 命中台账 = 台账 datasetId;ref 未命中/台账缺失 = null */
271
+ resolvedDatasetId: string | null;
272
+ topK?: number;
273
+ scoreThreshold?: number;
274
+ }
275
+
276
+ /** modules[install id].resources 只读镜像(键 = 资源 id;无模块键/无资源 = null) */
277
+ export type ModuleResourcesEcho = Record<string, ModuleResourceEcho>;
278
+
232
279
  /** services[s] 整节点 */
233
280
  export interface ServiceIntegrationNode {
234
281
  /** 对象 = 自定义 API(仅槽级,P2) */
@@ -291,8 +338,8 @@ export interface ProviderEntry {
291
338
  local: boolean;
292
339
  /** 供给声明来源包名 */
293
340
  owner: string;
294
- /** B1 additive:厂商展示名(manifest 模块 meta——多包首声明;缺席 = undefined) */
295
- title?: string;
341
+ /** 厂商展示名(v7 title→name:manifest name——多包首声明;缺席 = undefined) */
342
+ name?: string;
296
343
  description?: string;
297
344
  }
298
345
 
@@ -301,8 +348,8 @@ export interface ProviderDetail {
301
348
  provider: string;
302
349
  local: boolean;
303
350
  owner: string;
304
- /** B1 additive:厂商展示名(同 ProviderEntry——首声明透传) */
305
- title?: string;
351
+ /** 厂商展示名(同 ProviderEntry——首声明透传) */
352
+ name?: string;
306
353
  description?: string;
307
354
  /** per (provider, service) 保真,同厂商异构合法;★ implementations 恒数组(H1——
308
355
  * 多元素 = 升级窗口,UI 出 impl 选择器,PUT 显式钉版) */
@@ -310,8 +357,8 @@ export interface ProviderDetail {
310
357
  string,
311
358
  {
312
359
  implementations: Array<{ implementation: string; credentialType: string }>;
313
- /** B1 additive:服务展示名(resolveServiceDisplay——词汇 > demand > id;恒发) */
314
- title?: string;
360
+ /** 服务展示名(resolveServiceDisplay——词汇 > demand > id;恒发) */
361
+ name?: string;
315
362
  description?: string;
316
363
  /** v4.4 F5:include=integrationSchemas 切换态批量(纯 config——凭据结构归 /credential-types) */
317
364
  integrationSchemas?: { configSchema: object | null };
package/src/factory.ts CHANGED
@@ -19,9 +19,10 @@ import { loadModulesView, loadModuleDetailView } from './views/modules.js';
19
19
  import { loadAppView } from './views/app.js';
20
20
  import { loadServiceDetailView } from './views/service-detail.js';
21
21
  import { loadServiceResolutionsView, loadServiceResolutionDetail } from './views/service-resolutions.js';
22
- import { loadAppFormViews, loadModuleFormViews, loadServiceIntegrationSchemas } from './views/integration-schemas.js';
22
+ import { loadAppFormViews, loadModuleFormViews, loadServiceIntegrationSchemas, loadServiceFieldRegistry } from './views/integration-schemas.js';
23
23
  import { loadCredentialsView } from './views/credentials.js';
24
24
  import { loadCredentialTypesView } from './views/credential-types.js';
25
+ import { resolveModuleKey, resolveServiceKey } from './assembly/resolve-key.js';
25
26
  import type { WalkManifestsResult } from './assembly/walk-manifests.js';
26
27
  import type {
27
28
  AppIntegrationNode,
@@ -44,7 +45,8 @@ import type {
44
45
  * createAppToolkit(C4 工厂;v4.4——24 方法 = 11 读(含 credentials/credential-types 双端点)
45
46
  * + 4 写 + 9 迁移面;loadApp/loadModule/loadProvider 增 include 选项;v4.6——loadApp/loadModule
46
47
  * += 轴向假想绑定选项(appProvider/moduleProvider,D37);表单双投影经 formViews 单点装配;
47
- * v4.7——loadService 增 include 选项(服务级生效绑定 schema 单体))。
48
+ * v4.7——loadService 增 include 选项(服务级生效绑定 schema 单体);v5.1 D39——loadService
49
+ * += serviceProvider 轴参(服务轴向假想绑定,三轴收口))。
48
50
  * AppToolkit = AppToolkitRead & AppToolkitWrite & AppToolkitInternals——
49
51
  * Read+Write = HTTP 直通面 additive 冻结(L1 端点↔方法 1:1,L3 命名镜像:Integration=节点级 /
50
52
  * Config=文件级);Internals = CLI/doctor/桥消费(自由演进)。
@@ -67,12 +69,13 @@ export interface AppToolkitOptions {
67
69
 
68
70
  /** include 协议白名单(v4.4 D30/P15——键名 = 产出字段名 1:1;per 端点;注册表 additive-only;
69
71
  * v4.6 D36 += integrationFieldRegistry:app/module 三键,provider 轴不加——YAGNI;
70
- * v4.7 D38 += service 键——服务级生效绑定 schema) */
72
+ * v4.7 D38 += service 键——服务级生效绑定 schema
73
+ * G 批 += service 轴 integrationFieldRegistry——服务轴注册表(x-tbox-global 旗标全量透传)) */
71
74
  const INCLUDE_WHITELIST = {
72
75
  app: ['serviceResolutions', 'integrationSchemas', 'integrationFieldRegistry'],
73
76
  module: ['serviceResolutions', 'integrationSchemas', 'integrationFieldRegistry'],
74
77
  provider: ['integrationSchemas'],
75
- service: ['integrationSchemas'],
78
+ service: ['integrationSchemas', 'integrationFieldRegistry'],
76
79
  } as const;
77
80
 
78
81
  /** include 校验(P15 单点在 toolkit 方法内——壳层仅解析可重复参数,零校验纪律不变):
@@ -96,9 +99,10 @@ function validateInclude(kind: keyof typeof INCLUDE_WHITELIST, include?: string[
96
99
  const FORM_INCLUDE_KEYS = ['integrationSchemas', 'integrationFieldRegistry'] as const;
97
100
 
98
101
  /** v4.6 D37 轴参严检(400 先于 404——镜像输入校验先行):非空轴参需与表单 include 键同现;
99
- * 缺席/空串 ≡ 按实际配置解析(I3——不触发严检);错轴参数由壳层按名读取,天然静默忽略。 */
102
+ * 缺席/空串 ≡ 按实际配置解析(I3——不触发严检);错轴参数由壳层按名读取,天然静默忽略。
103
+ * v5.1 D39:联合类型 += serviceProvider(service 轴同律)。 */
100
104
  function assertAxisProviderUsable(
101
- paramName: 'appProvider' | 'moduleProvider',
105
+ paramName: 'appProvider' | 'moduleProvider' | 'serviceProvider',
102
106
  value: string | undefined,
103
107
  include: ReadonlySet<string>,
104
108
  ): void {
@@ -113,14 +117,20 @@ function assertAxisProviderUsable(
113
117
  }
114
118
  }
115
119
 
116
- /** ── HTTP 直通面(11 读;L1 镜像律:方法名 = load + URL 资源段;query → 选项字段 L4)── */
120
+ /** ── HTTP 直通面(11 读;L1 镜像律:方法名 = load + URL 资源段;query → 选项字段 L4)──
121
+ * **双键契约(v7 词汇统一)**:资源路径参数双键寻址(id 优先、唯一展示名兜底——resolve-key
122
+ * 单点;多命中 404 带候选);query 过滤参数(loadServiceResolutions services?)恒 id-only。 */
117
123
  export interface AppToolkitRead {
118
- loadModules(): Promise<ModulesView>;
119
- /** v4.6 D37:moduleProvider = 轴向假想绑定(缺席/空串 ≡ 按实际配置解析;需与表单 include 键同现) */
124
+ /** GET /modules:hidden 模块默认过滤(includeHidden = true 可见——对齐 ?dryRun 布尔参数先例) */
125
+ loadModules(o?: { includeHidden?: boolean }): Promise<ModulesView>;
126
+ /** v4.6 D37:moduleProvider = 轴向假想绑定(缺席/空串 ≡ 按实际配置解析;需与表单 include 键同现)。
127
+ * moduleId = 双键(id 优先、唯一展示名兜底)。 */
120
128
  loadModule(moduleId: string, o?: { include?: string[]; moduleProvider?: string }): Promise<ModuleDetailView>;
121
129
  /** v4.6 D37:appProvider = 轴向假想绑定(缺席/空串 ≡ 按实际配置解析;需与表单 include 键同现) */
122
130
  loadApp(o?: { include?: string[]; appProvider?: string }): Promise<AppView>;
123
- loadService(service: string, o?: { include?: string[] }): Promise<ServiceDetail>;
131
+ /** v5.1 D39:serviceProvider = 服务轴向假想绑定(缺席/空串 按实际配置解析;需与表单 include
132
+ * 键同现)——假想重定向仅表单双投影,integration/suppliedBy/defaults 恒实际态(I2) */
133
+ loadService(service: string, o?: { include?: string[]; serviceProvider?: string }): Promise<ServiceDetail>;
124
134
  loadServiceResolutions(services?: string[]): Promise<ServiceResolutionsView>;
125
135
  loadServiceResolution(service: string): Promise<ServiceResolutionDetail>;
126
136
  loadProviders(): Promise<ProvidersView>;
@@ -132,7 +142,8 @@ export interface AppToolkitRead {
132
142
  loadCredentialTypes(provider?: string): Promise<CredentialTypesView>;
133
143
  }
134
144
 
135
- /** ── HTTP 直通面(4 写;body ≡ 文件节点全量替换;DELETE 无 dryRun/无 options)── */
145
+ /** ── HTTP 直通面(4 写;body ≡ 文件节点全量替换;DELETE 无 dryRun/无 options)──
146
+ * 双键契约(v7):路径键双键寻址(写侧 enqueue 闭包内解析——落盘键恒真实 id)。 */
136
147
  export interface AppToolkitWrite {
137
148
  writeAppIntegration(
138
149
  node: AppIntegrationNode,
@@ -174,11 +185,20 @@ export function createAppToolkit(appDir: string, options: AppToolkitOptions = {}
174
185
 
175
186
  const viewCtx = { appDir, cache };
176
187
 
177
- /** 门控断言(policy at edge)——strictValidation false → 503(message 三分支 N1) */
188
+ /** 门控断言(policy at edge)——strictValidation false → 503(message 三分支 N1);
189
+ * v6 标记门(P1):ownershipBindingV6 false → 503 第四分支(v5 contracts + v6 toolkit 偏斜
190
+ * ——防静默忽略 ownerOf;门控范围 = 求值 + 写 + migrate 全体,本函数为唯一调用点)。 */
178
191
  function assertContractsUsable(contracts: ResolvedContracts): void {
179
192
  if (!contracts.strictValidation) {
180
193
  throw new AppToolkitError('CONTRACTS_NOT_RESOLVED', 503, contractsNotResolvedMessage(contracts));
181
194
  }
195
+ if (!contracts.ownershipBindingV6) {
196
+ throw new AppToolkitError(
197
+ 'CONTRACTS_NOT_RESOLVED',
198
+ 503,
199
+ `contracts ${contracts.version ?? '(版本未知)'} 不支持 v6 modules 绑定形状(OWNERSHIP_BINDING_V6 标记缺席)——升级应用 @tbox.cn/app-contracts 依赖后可用`,
200
+ );
201
+ }
182
202
  }
183
203
 
184
204
  /** 快照(同步——调用方已 await resolveContracts) */
@@ -186,6 +206,12 @@ export function createAppToolkit(appDir: string, options: AppToolkitOptions = {}
186
206
  return loadViewSnapshotSync(viewCtx, contracts);
187
207
  }
188
208
 
209
+ /** 服务双键解析的已配置键集(integrations services 节键——宽松形态防御式提取) */
210
+ function configuredServiceKeys(snap: ReturnType<typeof snapshot>): string[] {
211
+ const services = (snap.integrations as { services?: Record<string, unknown> } | null)?.services;
212
+ return services && typeof services === 'object' ? Object.keys(services) : [];
213
+ }
214
+
189
215
  /** 谓词全量 issues(求值轴/写后复查共用——contracts 注入 + buildPredicateAssembly 单源装配,FX-2b:
190
216
  * 与 CLI doctor 规则同装配面——「面板 issues ≡ doctor issues」机制保证) */
191
217
  async function predicateIssues(): Promise<IntegrationServiceIssue[]> {
@@ -196,7 +222,6 @@ export function createAppToolkit(appDir: string, options: AppToolkitOptions = {}
196
222
  const predicateContracts: PredicateContracts = {
197
223
  resolveEffectiveService: assertFn(contracts.module.resolveEffectiveService, 'resolveEffectiveService'),
198
224
  buildSupplyLookup: assertFn(contracts.module.buildSupplyLookup, 'buildSupplyLookup'),
199
- moduleDomainOf: assertFn(contracts.module.moduleDomainOf, 'moduleDomainOf'),
200
225
  normalizeInstances: assertFn(contracts.module.normalizeInstances, 'normalizeInstances'),
201
226
  RESOURCE_TYPE_IDS: (contracts.module.RESOURCE_TYPE_IDS ?? []) as readonly string[],
202
227
  };
@@ -222,9 +247,9 @@ export function createAppToolkit(appDir: string, options: AppToolkitOptions = {}
222
247
  }
223
248
 
224
249
  const read = {
225
- async loadModules() {
250
+ async loadModules(o?: { includeHidden?: boolean }) {
226
251
  const contracts = await resolveContractsForApp(appDir);
227
- return loadModulesView(snapshot(contracts));
252
+ return loadModulesView(snapshot(contracts), o);
228
253
  },
229
254
  // v4.4 D31:门控与内嵌分离——静态方法零门控分支;include=serviceResolutions 按
230
255
  // evaluation && strictValidation 双条件装配(<0.9/形态异常 → null,永不 503——技术方案 §2.4:
@@ -234,7 +259,9 @@ export function createAppToolkit(appDir: string, options: AppToolkitOptions = {}
234
259
  assertAxisProviderUsable('moduleProvider', o?.moduleProvider, include); // ①' D37 轴参严检(先于 404)
235
260
  const contracts = await resolveContractsForApp(appDir);
236
261
  const snap = snapshot(contracts);
237
- const view = loadModuleDetailView(snap, moduleId); // MODULE_NOT_FOUND
262
+ // 双键寻址(v7 词汇统一):路径参数 id 优先、唯一展示名兜底 真实 id(404 面)
263
+ const mod = resolveModuleKey(snap.walk, moduleId);
264
+ const view = loadModuleDetailView(snap, mod.id);
238
265
  if (include.has('serviceResolutions')) {
239
266
  view.serviceResolutions = contracts.evaluation && contracts.strictValidation
240
267
  ? await predicateIssues().then((issues) =>
@@ -243,7 +270,7 @@ export function createAppToolkit(appDir: string, options: AppToolkitOptions = {}
243
270
  }
244
271
  if (include.has('integrationSchemas') || include.has('integrationFieldRegistry')) {
245
272
  // v4.6 D37:表单层双投影同源一次计算(双取单算 + I1——假想重定向天然同步)
246
- const form = loadModuleFormViews(snap, moduleId, o?.moduleProvider);
273
+ const form = loadModuleFormViews(snap, mod.id, o?.moduleProvider);
247
274
  if (include.has('integrationSchemas')) view.integrationSchemas = form.integrationSchemas;
248
275
  if (include.has('integrationFieldRegistry')) view.integrationFieldRegistry = form.integrationFieldRegistry;
249
276
  }
@@ -268,13 +295,22 @@ export function createAppToolkit(appDir: string, options: AppToolkitOptions = {}
268
295
  return view;
269
296
  },
270
297
  // v4.7 D38:include 镜像 loadModule 三段式——① 输入校验 400 先于 404 ② 视图 ③ 静态富化(永不 503)
271
- async loadService(service: string, o?: { include?: string[] }): Promise<ServiceDetail> {
298
+ // G 批:service += integrationFieldRegistry(loadServiceFieldRegistry——D38 四分支链复用)
299
+ // v5.1 D39:loadService += serviceProvider 轴参(三段式序对齐 loadModule——①' 严检先于 resolveContracts,
300
+ // 即先于 404 也先于 503;I1 双投影同源一次计算经单源链透传)
301
+ async loadService(service: string, o?: { include?: string[]; serviceProvider?: string }): Promise<ServiceDetail> {
272
302
  const include = validateInclude('service', o?.include);
303
+ assertAxisProviderUsable('serviceProvider', o?.serviceProvider, include); // ①' D39 轴参严检(先于 404)
273
304
  const contracts = await resolveContractsForApp(appDir);
274
305
  const snap = snapshot(contracts); // 单快照——detail 与 include 装配共享
275
- const view = loadServiceDetailView(snap, service);
306
+ // 双键寻址(v7):id 优先、唯一展示名兜底 真实 service id(404 面)
307
+ const serviceId = resolveServiceKey(snap.walk, configuredServiceKeys(snap), service);
308
+ const view = loadServiceDetailView(snap, serviceId);
276
309
  if (include.has('integrationSchemas')) {
277
- view.integrationSchemas = loadServiceIntegrationSchemas(snap, service);
310
+ view.integrationSchemas = loadServiceIntegrationSchemas(snap, serviceId, o?.serviceProvider);
311
+ }
312
+ if (include.has('integrationFieldRegistry')) {
313
+ view.integrationFieldRegistry = loadServiceFieldRegistry(snap, serviceId, o?.serviceProvider);
278
314
  }
279
315
  return view;
280
316
  },
@@ -287,8 +323,10 @@ export function createAppToolkit(appDir: string, options: AppToolkitOptions = {}
287
323
  async loadServiceResolution(service: string) {
288
324
  const contracts = await resolveContractsForApp(appDir);
289
325
  assertContractsUsable(contracts);
326
+ const snap = snapshot(contracts);
327
+ const serviceId = resolveServiceKey(snap.walk, configuredServiceKeys(snap), service);
290
328
  const issues = await predicateIssues();
291
- return loadServiceResolutionDetail(service, snapshot(contracts), issues);
329
+ return loadServiceResolutionDetail(serviceId, snap, issues);
292
330
  },
293
331
  async loadProviders() {
294
332
  const contracts = await resolveContractsForApp(appDir);
@@ -301,7 +339,10 @@ export function createAppToolkit(appDir: string, options: AppToolkitOptions = {}
301
339
  },
302
340
  async loadProviderService(provider: string, service: string) {
303
341
  const contracts = await resolveContractsForApp(appDir);
304
- return loadProviderServiceDetailView(snapshot(contracts), provider, service);
342
+ const snap = snapshot(contracts);
343
+ // 服务轴双键寻址(provider 轴不兜底——厂商键即词汇)
344
+ const serviceId = resolveServiceKey(snap.walk, configuredServiceKeys(snap), service);
345
+ return loadProviderServiceDetailView(snap, provider, serviceId);
305
346
  },
306
347
  // v4.4 D33:凭据域双端点(应用状态轴 / 供给轴静态——零门控零求值,永不 503)
307
348
  async loadCredentials(): Promise<CredentialsView> {
@@ -330,19 +371,31 @@ export function createAppToolkit(appDir: string, options: AppToolkitOptions = {}
330
371
  await core.warmup();
331
372
  const contracts = await resolveContractsForApp(appDir);
332
373
  assertContractsUsable(contracts);
333
- return enqueue(() => core.applyModule(moduleId, node as Record<string, unknown>, o));
374
+ // enqueue 闭包内双键解析(写时新鲜度)——落盘键恒真实 id
375
+ return enqueue(() => {
376
+ const mod = resolveModuleKey(snapshot(contracts).walk, moduleId);
377
+ return core.applyModule(mod.id, node as Record<string, unknown>, o);
378
+ });
334
379
  },
335
380
  async writeServiceIntegration(service: string, node: ServiceIntegrationNode, o?: WriteOptions) {
336
381
  await core.warmup();
337
382
  const contracts = await resolveContractsForApp(appDir);
338
383
  assertContractsUsable(contracts);
339
- return enqueue(() => core.applyService(service, node as Record<string, unknown>, o));
384
+ return enqueue(() => {
385
+ const snap = snapshot(contracts);
386
+ const serviceId = resolveServiceKey(snap.walk, configuredServiceKeys(snap), service);
387
+ return core.applyService(serviceId, node as Record<string, unknown>, o);
388
+ });
340
389
  },
341
390
  async deleteServiceIntegration(service: string) {
342
391
  await core.warmup();
343
392
  const contracts = await resolveContractsForApp(appDir);
344
393
  assertContractsUsable(contracts);
345
- return enqueue(() => core.deleteService(service));
394
+ return enqueue(() => {
395
+ const snap = snapshot(contracts);
396
+ const serviceId = resolveServiceKey(snap.walk, configuredServiceKeys(snap), service);
397
+ return core.deleteService(serviceId);
398
+ });
346
399
  },
347
400
  };
348
401
 
@@ -385,7 +438,6 @@ export function createAppToolkit(appDir: string, options: AppToolkitOptions = {}
385
438
  const predicateContracts: PredicateContracts = {
386
439
  resolveEffectiveService: assertFn(contracts.module.resolveEffectiveService, 'resolveEffectiveService'),
387
440
  buildSupplyLookup: assertFn(contracts.module.buildSupplyLookup, 'buildSupplyLookup'),
388
- moduleDomainOf: assertFn(contracts.module.moduleDomainOf, 'moduleDomainOf'),
389
441
  normalizeInstances: assertFn(contracts.module.normalizeInstances, 'normalizeInstances'),
390
442
  RESOURCE_TYPE_IDS: (contracts.module.RESOURCE_TYPE_IDS ?? []) as readonly string[],
391
443
  };
package/src/index.ts CHANGED
@@ -10,14 +10,16 @@ export {
10
10
  tabContribSchema,
11
11
  moduleDependencySchema,
12
12
  providerSlotContribSchema,
13
- serviceNeedContribSchema,
13
+ serviceConsumeContribSchema,
14
+ serviceOwnContribSchema,
14
15
  resourceNeedContribSchema,
15
16
  } from './core/module-schema.js';
16
17
  export type {
17
18
  ModuleDescriptor,
18
19
  CardContrib,
19
20
  ProviderSlotContrib,
20
- ServiceNeedContrib,
21
+ ServiceConsumeContrib,
22
+ ServiceOwnContrib,
21
23
  ResourceNeedContrib,
22
24
  ParseModuleResult,
23
25
  } from './core/module-schema.js';
@@ -63,13 +65,15 @@ export type { PruneResult } from './integrations/prune-bindings.js';
63
65
 
64
66
  // ── assembly 层(目录知识——walkManifests 四产物 + catalog 双形态 + 组合单体装配)──
65
67
  export { walkManifests } from './assembly/walk-manifests.js';
66
- export type { WalkManifestsResult, WalkModuleInfo, WalkCatalog, WalkProviderEntry } from './assembly/walk-manifests.js';
68
+ // 双键寻址(v7 词汇统一——factory 七方法入口单点消费的解析 helper;纯参数式零 IO)
69
+ export { resolveModuleKey, resolveServiceKey } from './assembly/resolve-key.js';
70
+ export type { WalkManifestsResult, WalkModuleInfo, WalkCatalog, WalkProviderEntry, DeclarationIssue, SlotMeta } from './assembly/walk-manifests.js';
67
71
  export { readAppManifest, writeAppManifest, manifestPath } from './assembly/app-manifest.js';
68
72
  export type { AppManifest, NpmModuleEntry, ModuleMode } from './assembly/app-manifest.js';
69
73
  export { loadProviderCatalog, loadProviderCatalogRaw } from './assembly/provider-catalog.js';
70
74
  export type { BridgeProviderCatalog, ProviderServiceSchemas, ProviderServiceSchemasResult } from './assembly/provider-catalog.js';
71
75
 
72
- // ── integrations 层(领域——读/写核心/凭据双工件/mock 绑定)──
76
+ // ── integrations 层(领域——读/写核心/凭据双工件/mock 绑定;v6 模块绑定单源)──
73
77
  export { readIntegrationsConfig, readIntegrationsStrict } from './integrations/read.js';
74
78
  export type { ReadIntegrationsResult } from './integrations/read.js';
75
79
  export { createWriteCore, deepEqual } from './integrations/write.js';
@@ -78,6 +82,9 @@ export { ensureMockBindings } from './integrations/mock-bindings.js';
78
82
  export type { MockBindingsResult } from './integrations/mock-bindings.js';
79
83
  export { reconcileOrphanCredentials, credentialArtifactExists } from './integrations/credentials.js';
80
84
  export type { AppliedCredential, CredentialInput, OrphanIssue, PlannedCredential } from './integrations/credentials.js';
85
+ // v6 所有权模型单源(module-binding——resolveModuleBinding/写守卫/归属表直通)
86
+ export { buildOwnershipMap, moduleOwnershipWritable, resolveModuleBinding, providerFootprint } from './integrations/module-binding.js';
87
+ export type { ModuleOwnershipWritability } from './integrations/module-binding.js';
81
88
 
82
89
  // ── 错误面(领域 6 码——AppToolkitError;name 字面量约定见 errors.ts)──
83
90
  export { AppToolkitError, contractsNotResolvedMessage } from './core/errors.js';
@@ -89,7 +96,7 @@ export { loadAppView } from './views/app.js';
89
96
  export { loadServiceDetailView } from './views/service-detail.js';
90
97
  export { loadProvidersView, loadProviderDetailView, loadProviderServiceDetailView } from './views/providers.js';
91
98
  export { loadServiceResolutionsView, loadServiceResolutionDetail, resolveServiceSummary } from './views/service-resolutions.js';
92
- export { loadAppIntegrationSchemas, loadModuleIntegrationSchemas, loadServiceIntegrationSchemas, resolveLayerIntegrationSchemas } from './views/integration-schemas.js';
99
+ export { loadAppIntegrationSchemas, loadModuleIntegrationSchemas, loadServiceIntegrationSchemas, loadServiceFieldRegistry, resolveLayerIntegrationSchemas, formProviderOf, buildIntegrationFieldRegistry } from './views/integration-schemas.js';
93
100
  export { echoForRef, loadCredentialsView } from './views/credentials.js';
94
101
  export { loadCredentialTypesView } from './views/credential-types.js';
95
102
  export type { ViewContext, ViewSnapshot } from './views/context.js';
@@ -106,10 +113,10 @@ export type {
106
113
  ModuleDeclaration,
107
114
  ServiceDemand,
108
115
  ModuleDetailView,
116
+ ModuleBinding,
109
117
  AppView,
110
118
  ServiceDetail,
111
119
  ServiceIntegrationSchemas,
112
- ProviderBinding,
113
120
  LayerIntegrationSchemas,
114
121
  IntegrationFieldRegistryView,
115
122
  CredentialsView,
@@ -122,6 +129,8 @@ export type {
122
129
  ServiceResolutionDetail,
123
130
  AppIntegrationNode,
124
131
  ModuleIntegrationNode,
132
+ ModuleResourceEcho,
133
+ ModuleResourcesEcho,
125
134
  ServiceIntegrationNode,
126
135
  ServiceInstanceNode,
127
136
  InlineVendorDeclaration,
@@ -136,7 +145,7 @@ export type {
136
145
  ContractsResolution,
137
146
  IntegrationIssue,
138
147
  } from './dto.js';
139
- export { APP_INTEGRATION_NODE_KEYS } from './dto.js';
148
+ export { APP_INTEGRATION_NODE_KEYS, MODULE_INTEGRATION_NODE_KEYS } from './dto.js';
140
149
  export {
141
150
  readAppManifestFull,
142
151
  upsertNpmModule,
@@ -29,7 +29,7 @@ export function ensureMockBindings(appDir: string): MockBindingsResult {
29
29
  }
30
30
  const slots: Array<{ service: string; provider: string; implementation: string }> = [];
31
31
  for (const mod of walkManifests(appDir).modules) {
32
- for (const slot of mod.descriptor.contributes.providers?.slots ?? []) {
32
+ for (const slot of mod.descriptor.declare.providers?.slots ?? []) {
33
33
  if (slot.local !== true) continue;
34
34
  slots.push({ service: slot.service, provider: slot.provider, implementation: slot.implementation });
35
35
  }
@@ -0,0 +1,60 @@
1
+ import { walkManifests, type WalkCatalog, type WalkManifestsResult, type WalkModuleInfo } from '../assembly/walk-manifests.js';
2
+
3
+ /**
4
+ * 模块级联绑定单源(integrations v6 所有权模型——原 domain-binding.ts v4.4 F1/F3 重写)。
5
+ * integrations 层落位依据:谓词(同层)/ 写核(同层)/ views(下层可向上 import)三向消费
6
+ * 的唯一公共下层;import 前缀 guard 覆盖。
7
+ *
8
+ * 知识锚(module-tab note 单源锚表,v6 形态):
9
+ * 模块级联(值 + 层) = resolveModuleBinding —— 谓词 #7(per-module 生效厂商 ≡
10
+ * `modules[m].provider ?? root.provider`)/ deriveBindingLayer 模块级段 /
11
+ * buildInheritance module 段 / binding(单数)
12
+ * 归属表 = walk.ownership(单源聚合产物;buildOwnershipMap 桥直通——
13
+ * registry 显式参 / 谓词 / 写守卫 / views 四消费方零第二实现)
14
+ * 来源层四层推导 = deriveBindingLayer(views/service-resolutions,模块级段改调本文件)
15
+ * 写通道可用性 = moduleOwnershipWritable —— configEditable 读标志 / applyModule 400 守卫
16
+ * 共用(owns>0 唯一判据——多域归属合法化、共享键/多域判据随 D34 退役)
17
+ */
18
+
19
+ /** 归属表构建(walk 单源直通——sdk 桥 re-export 消费;appDir 直入对齐 loadProviderCatalog
20
+ * 桥工效——模板装配第六行:`ownerOf: buildOwnershipMap(appDir)`)。 */
21
+ export function buildOwnershipMap(appDir: string): Record<string, string> {
22
+ return walkManifests(appDir).ownership;
23
+ }
24
+
25
+ /** 模块级联绑定解析(值 + 来源层):provider 取非空字符串(模块级优先 → root 兜底;空串 = 缺席)。
26
+ * layer:modules[moduleId].provider 声明 → 'module';root 兜底 → 'app';双缺 → null(binding 双缺诚实态)。 */
27
+ export function resolveModuleBinding(
28
+ config: Record<string, unknown> | null,
29
+ moduleId: string,
30
+ ): { provider: string | null; layer: 'module' | 'app' | null } {
31
+ const moduleProvider = (config?.modules as Record<string, { provider?: unknown }> | undefined)?.[moduleId]?.provider;
32
+ if (typeof moduleProvider === 'string' && moduleProvider.length > 0) {
33
+ return { provider: moduleProvider, layer: 'module' };
34
+ }
35
+ const rootProvider = config?.provider;
36
+ if (typeof rootProvider === 'string' && rootProvider.length > 0) {
37
+ return { provider: rootProvider, layer: 'app' };
38
+ }
39
+ return { provider: null, layer: null };
40
+ }
41
+
42
+ /** 模块集成位可写性(v6 判别联合——configEditable 读标志与 applyModule 写守卫共用单源):
43
+ * owns>0 唯一判据(D34 多域/shared-key 判据随 v6 退役——服务归属唯一真源 = declare.owns)。 */
44
+ export type ModuleOwnershipWritability =
45
+ | { writable: true; ownedServices: string[] }
46
+ | { writable: false; reason: 'zero-ownership'; ownedServices: [] };
47
+
48
+ export function moduleOwnershipWritable(
49
+ _walk: Pick<WalkManifestsResult, 'modules' | 'catalog' | 'ownership'>,
50
+ mod: WalkModuleInfo,
51
+ ): ModuleOwnershipWritability {
52
+ const ownedServices = (mod.descriptor.declare.owns ?? []).map((o) => o.service).sort();
53
+ if (ownedServices.length === 0) return { writable: false, reason: 'zero-ownership', ownedServices: [] };
54
+ return { writable: true, ownedServices };
55
+ }
56
+
57
+ /** 供给足迹(该厂商全部供给服务并集——应用级 integrationSchemas 作用域,D32) */
58
+ export function providerFootprint(catalog: WalkCatalog, provider: string): string[] {
59
+ return [...new Set(Object.values(catalog.providers[provider] ?? {}).flatMap((e) => e.services))].sort();
60
+ }