@dsh-workbench/shared 1.0.0 → 1.1.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/contract/protocol/bundle-install.d.ts +9 -0
- package/dist/contract/protocol/market-index.d.ts +1 -0
- package/dist/contract/protocol/market-index.js +1 -0
- package/dist/contract/unit-declaration.d.ts +11 -5
- package/dist/contract/unit-declaration.js +42 -12
- package/dist/contract/unit.d.ts +15 -1
- package/dist/contract/unit.js +8 -0
- package/dist/contract/workbench-contract.d.ts +1 -1
- package/dist/host/provision.d.ts +1 -1
- package/dist/host/provision.js +8 -2
- package/dist/host/supply-index.d.ts +4 -1
- package/dist/host/supply-index.js +18 -12
- package/dist/i18n/en.js +53 -31
- package/dist/i18n/index.d.ts +55 -33
- package/dist/i18n/zh.d.ts +54 -32
- package/dist/i18n/zh.js +54 -32
- package/dist/lib/dsh-client.d.ts +3 -1
- package/package.json +1 -1
- package/src/contract/__tests__/unit-declaration.test.ts +33 -1
- package/src/contract/protocol/bundle-install.ts +11 -0
- package/src/contract/protocol/market-index.ts +1 -0
- package/src/contract/unit-declaration.ts +52 -16
- package/src/contract/unit.ts +19 -1
- package/src/contract/workbench-contract.ts +3 -1
- package/src/host/__tests__/provision.test.ts +2 -0
- package/src/host/__tests__/supply-index.test.ts +42 -6
- package/src/host/provision.ts +6 -2
- package/src/host/supply-index.ts +40 -11
- package/src/i18n/en.ts +53 -31
- package/src/i18n/zh.ts +54 -32
- package/src/lib/dsh-client.ts +3 -1
- package/src/testing/__tests__/declaration.test.ts +6 -3
package/dist/i18n/zh.js
CHANGED
|
@@ -129,14 +129,14 @@ export const zh = {
|
|
|
129
129
|
'panels.market.update': '更新',
|
|
130
130
|
'panels.market.swapSlow': '还没换上:可能网慢或没装上。再点一次「更新」;一直这样就把 dsh 重启一遍。',
|
|
131
131
|
'panels.market.removeSlow': '还没卸掉:可能网慢或卸载没走完。等一下再看;一直这样就把 dsh 重启一遍。',
|
|
132
|
-
'panels.market.updateNoChange': '
|
|
132
|
+
'panels.market.updateNoChange': '更新失败:还是 {from};源上最新是 {latest},可以再点一次「更新」。',
|
|
133
133
|
'panels.market.updateUnknownVersion': '(问不到)',
|
|
134
|
-
'panels.market.updateGone': '
|
|
135
|
-
'panels.market.updateRestored': '
|
|
134
|
+
'panels.market.updateGone': '更新失败:更新后找不到这个模块。',
|
|
135
|
+
'panels.market.updateRestored': '更新失败:更新后找不到新版本,已把 {version} 装回去。',
|
|
136
136
|
'panels.market.updateBehind': '装上了 {installed},但源上最新是 {latest} —— 没更到最新,可以再点一次。',
|
|
137
|
-
'panels.market.selfUpdateFailed': '
|
|
137
|
+
'panels.market.selfUpdateFailed': '工作台更新失败:{reason}',
|
|
138
138
|
'panels.market.selfUpdateUnknown': '外部进程没留下原因(看 ~/.dsh/workbench/self-update.log)',
|
|
139
|
-
'panels.market.selfUpdateRolledBack': '
|
|
139
|
+
'panels.market.selfUpdateRolledBack': '工作台更新失败,已回滚到 {version}。',
|
|
140
140
|
'panels.market.uninstall': '卸载',
|
|
141
141
|
'panels.market.uninstallLabel': '卸载 {name}',
|
|
142
142
|
'panels.market.installing': '正在安装…',
|
|
@@ -158,9 +158,27 @@ export const zh = {
|
|
|
158
158
|
'panels.market.empty': '暂无可用模块',
|
|
159
159
|
'panels.market.sectionPlugins': '已安装 opencli 插件',
|
|
160
160
|
'panels.market.moduleOutdated': '有新版',
|
|
161
|
+
'panels.market.updateNeedsWorkbench': '新版本要先把工作台升级才能用',
|
|
162
|
+
'panels.market.updateIncompatible': '新版本和当前的工作台不匹配,暂时不能更新',
|
|
161
163
|
'panels.market.moduleRefused': '模块版本与工作台不匹配,暂时无法使用',
|
|
162
164
|
'panels.market.moduleRestartHint': '重启后生效',
|
|
163
165
|
'panels.market.pendingRestart': '重启后生效',
|
|
166
|
+
'panels.market.pendingFollowUp': '旧数据待迁移',
|
|
167
|
+
'panels.market.migrationHint': '旧会话和项目还没搬过来',
|
|
168
|
+
'panels.market.migrateAction': '迁移旧数据',
|
|
169
|
+
'panels.market.migrating': '正在迁移…',
|
|
170
|
+
'panels.market.migrateConfirmTitle': '迁移旧数据?',
|
|
171
|
+
'panels.market.migrateConfirmDetail': '先退出 dsh web / Desktop。迁移只补缺、不覆盖;旧目录保留。',
|
|
172
|
+
'panels.market.migrateConfirmAck': '我已经退出 dsh web / Desktop',
|
|
173
|
+
'panels.market.migrateConfirmYes': '开始迁移',
|
|
174
|
+
'panels.market.migrateDone': '迁移完成:补了 {added} 个项目;重启工作台和 dsh web 后出现。',
|
|
175
|
+
'panels.market.dismissFollowUp': '不再提醒',
|
|
176
|
+
'panels.market.dismissing': '正在收起…',
|
|
177
|
+
'panels.market.dismissFollowUpTitle': '不再提醒这件事?',
|
|
178
|
+
'panels.market.dismissFollowUpDetail': '提示会从模块市场收起,但不会迁移或删除旧数据。旧会话和项目仍留在旧目录,新版本里看不到;以后需要时仍可运行 dsh-workbench migrate。',
|
|
179
|
+
'panels.market.dismissFollowUpAck': '我知道旧数据不会自动出现',
|
|
180
|
+
'panels.market.dismissFollowUpYes': '不再提醒',
|
|
181
|
+
'panels.market.dismissed': '已不再提醒;旧数据仍在旧目录,需要时仍可运行 dsh-workbench migrate。',
|
|
164
182
|
'panels.market.workbenchName': '工作台',
|
|
165
183
|
'panels.market.pluginBroken': '这份插件装坏了:认不出它是什么。可以先卸载,再让 agent 按包名重装一次。',
|
|
166
184
|
'panels.market.restartNeeded': '新版装好了,重启一次才生效。',
|
|
@@ -244,7 +262,7 @@ export const zh = {
|
|
|
244
262
|
'panels.market.add.exampleTitle': '示例',
|
|
245
263
|
'panels.market.add.exampleFill': '填入示例',
|
|
246
264
|
'panels.market.add.registryLabel': '安装源',
|
|
247
|
-
'panels.market.add.registryProfile': '
|
|
265
|
+
'panels.market.add.registryProfile': '这份工作台配的源',
|
|
248
266
|
'panels.market.add.registryPnpm': 'pnpm 自己配的源',
|
|
249
267
|
'panels.market.add.check': '预检',
|
|
250
268
|
'panels.market.add.checking': '正在预检…',
|
|
@@ -265,12 +283,16 @@ export const zh = {
|
|
|
265
283
|
'panels.market.tabs': '模块市场的两个面',
|
|
266
284
|
'panels.tab.discover': '发现',
|
|
267
285
|
'panels.discover.loading': '正在读取索引',
|
|
286
|
+
'panels.discover.slowHint': '读取较慢:如有授权提示,先完成它,或换一个索引源。',
|
|
268
287
|
'panels.discover.lead': '从索引源浏览模块;装上就能用。',
|
|
269
288
|
'panels.discover.addSource': '添加',
|
|
270
289
|
'panels.discover.addingSource': '正在添加…',
|
|
271
290
|
'panels.discover.addFirstSource': '添加索引源',
|
|
272
291
|
'panels.discover.removeSource': '移除',
|
|
273
292
|
'panels.discover.removeSourceNamed': '移除 {url}',
|
|
293
|
+
'panels.discover.useSource': '使用',
|
|
294
|
+
'panels.discover.useSourceNamed': '使用 {label}',
|
|
295
|
+
'panels.discover.sourceCurrent': '当前使用',
|
|
274
296
|
'panels.discover.refresh': '刷新',
|
|
275
297
|
'panels.discover.search': '搜索模块',
|
|
276
298
|
'panels.discover.all': '全部',
|
|
@@ -282,8 +304,8 @@ export const zh = {
|
|
|
282
304
|
'panels.discover.sourcePlaceholder': 'https://example.com/index.json 或 git@git.example.com:wb/market.git',
|
|
283
305
|
'panels.discover.sourceRef': '分支',
|
|
284
306
|
'panels.discover.sourcePath': '索引文件',
|
|
285
|
-
'panels.discover.sourceLocal': '
|
|
286
|
-
'panels.discover.sourceLocalHint': '
|
|
307
|
+
'panels.discover.sourceLocal': '本地副本目录(可选)',
|
|
308
|
+
'panels.discover.sourceLocalHint': '不填就放到 ~/.dsh/workbench/market-index/。',
|
|
287
309
|
'panels.discover.save': '保存并刷新',
|
|
288
310
|
'panels.discover.cancel': '取消',
|
|
289
311
|
'panels.discover.retry': '重试',
|
|
@@ -296,9 +318,9 @@ export const zh = {
|
|
|
296
318
|
'panels.discover.missingIndex': '仓库里还没有索引文件 {path}',
|
|
297
319
|
'panels.discover.initTitle': '把这个仓库初始化成模块市场索引?',
|
|
298
320
|
'panels.discover.initCta': '初始化成市场索引',
|
|
299
|
-
'panels.discover.initWhat': '会在 {branch} 分支写 market.json 与 README.md
|
|
321
|
+
'panels.discover.initWhat': '会在 {branch} 分支写 market.json 与 README.md,提交一次并推上去;之后从这个工作台发布的模块会把打包好的文件提交到 packages/<unit>/。仓库的写权限归它的所有者。',
|
|
300
322
|
'panels.discover.initBranch': '分支',
|
|
301
|
-
'panels.discover.initBranchHint': '
|
|
323
|
+
'panels.discover.initBranchHint': '索引和打包文件都发到这个分支;空仓库会新建它。',
|
|
302
324
|
'panels.discover.initBranchBad': '分支名只能用字母、数字、点、下划线、斜杠或短横线',
|
|
303
325
|
'panels.discover.initOk': '初始化',
|
|
304
326
|
'panels.discover.initBusy': '正在初始化…',
|
|
@@ -402,11 +424,11 @@ export const zh = {
|
|
|
402
424
|
'panels.market.add.doneApplied': '装好了,已经生效。',
|
|
403
425
|
'panels.market.add.doneRestart': '装好了,重启一次才生效。',
|
|
404
426
|
'panels.market.add.restartWhy': '为什么这一版要重启:{reason}',
|
|
405
|
-
'panels.market.add.restartWhy.no-hmr': '
|
|
427
|
+
'panels.market.add.restartWhy.no-hmr': '这份配置只在启动时读一次,改了得重启',
|
|
406
428
|
'panels.market.add.restartWhy.package-moved': '某个已经装着的包换到了新的目录,或者这一下换掉了 dsh 自己提供的那份',
|
|
407
429
|
'panels.market.add.restartWhy.host-module': '这一下动到了 dsh 自己加载的模块',
|
|
408
|
-
'panels.market.add.restartWhy.unmanaged-plugin': '
|
|
409
|
-
'panels.market.add.restartWhy.module-comparison': 'dsh
|
|
430
|
+
'panels.market.add.restartWhy.unmanaged-plugin': '这一下动到了没登记过、正在运行的插件',
|
|
431
|
+
'panels.market.add.restartWhy.module-comparison': 'dsh 没法确认正在跑的插件就是这台机器上装好的那一份',
|
|
410
432
|
'panels.market.add.cancelled': '已取消。',
|
|
411
433
|
'panels.market.add.failed': '没成功:{reason}',
|
|
412
434
|
'panels.market.add.removed': '已经移除了。',
|
|
@@ -416,10 +438,10 @@ export const zh = {
|
|
|
416
438
|
'panels.market.add.removedRestart': '卸掉了,重启一次才彻底生效。',
|
|
417
439
|
'panels.market.add.remove': '移除它',
|
|
418
440
|
'panels.market.add.problem.invalidSpec': '这个写法不认:不能是空的,也不能以横杠开头。',
|
|
419
|
-
'panels.market.add.problem.alreadyInstalled': '
|
|
441
|
+
'panels.market.add.problem.alreadyInstalled': '这个包已经在这份工作台里了(要换版本请带上 @版本)。',
|
|
420
442
|
'panels.market.add.problem.notFound': '源上没有这个包。换个包名或版本再试。',
|
|
421
443
|
'panels.market.add.problem.notAPackage': '这个位置没有包。选一个包含 package.json 的目录。',
|
|
422
|
-
'panels.market.add.problem.notABundle': '它是一个 npm 包,但不是 dsh
|
|
444
|
+
'panels.market.add.problem.notABundle': '它是一个 npm 包,但不是 dsh 插件包(dsh 在里面找不到该加载什么)。',
|
|
423
445
|
'panels.market.add.problem.network': '源连不上。检查网络后重试。',
|
|
424
446
|
'panels.market.add.problem.unknown': '预检没通过(原因未知)。展开安装详情看原始输出,或重试。',
|
|
425
447
|
'panels.market.add.reason.invalidSpec': '写法不认',
|
|
@@ -427,7 +449,7 @@ export const zh = {
|
|
|
427
449
|
'panels.market.add.reason.notBundle': '它不是一个 dsh 插件包',
|
|
428
450
|
'panels.market.add.reason.notRemovable': '它是安装自带的组合包,不能卸',
|
|
429
451
|
'panels.market.add.reason.bundleInUse': '还有东西在用它,卸不掉',
|
|
430
|
-
'panels.market.add.reason.stopProfile': '
|
|
452
|
+
'panels.market.add.reason.stopProfile': '这份配置改了要重启,得先停掉工作台',
|
|
431
453
|
'panels.market.add.reason.staleApproval': '要允许的安装脚本已经变了,重新来一次',
|
|
432
454
|
'panels.market.add.reason.operationError': 'pnpm 执行出错(详情见安装输出)',
|
|
433
455
|
'panels.market.add.reason.buildBlocked': 'pnpm 拦下了安装脚本',
|
|
@@ -450,15 +472,15 @@ export const zh = {
|
|
|
450
472
|
'blocker.market.open': '去模块市场',
|
|
451
473
|
'blocker.login.title': '需要登录 {platform}',
|
|
452
474
|
'blocker.login.titleGeneric': '需要登录',
|
|
453
|
-
'blocker.login.hint': '
|
|
475
|
+
'blocker.login.hint': '工作台借你浏览器里的登录状态;登录一次就行,之后不用再登。',
|
|
454
476
|
'blocker.login.go': '去登录',
|
|
455
477
|
'blocker.login.manual': '在终端执行该插件的登录命令(见插件的文档),按提示完成登录后重试。',
|
|
456
478
|
'blocker.forbidden.title': '没有权限访问 {platform}',
|
|
457
479
|
'blocker.forbidden.titleGeneric': '没有权限',
|
|
458
480
|
'blocker.forbidden.hint': '账号已登录,但没有这个项目/仓库的权限;申请之后再试。',
|
|
459
481
|
'blocker.forbidden.open': '打开权限页',
|
|
460
|
-
'blocker.timeout.hint': '
|
|
461
|
-
'blocker.exec.hint': '确认 opencli
|
|
482
|
+
'blocker.timeout.hint': '后台浏览器没起来,重试一次通常就好。',
|
|
483
|
+
'blocker.exec.hint': '确认 opencli 已经装好、在终端里能直接运行(which opencli)。',
|
|
462
484
|
'blocker.unknown.title': '出了点问题',
|
|
463
485
|
'panels.reading': '正在读取面板配置…',
|
|
464
486
|
'panels.cardBroken': '这个模块起不来:{unit}({reason})',
|
|
@@ -472,7 +494,7 @@ export const zh = {
|
|
|
472
494
|
'env.elapsed': '已等待 {seconds} 秒',
|
|
473
495
|
'env.stepFailed.install': '装不上',
|
|
474
496
|
'env.stepFailed.path': '装了但还找不到',
|
|
475
|
-
'env.stepFailed.shell': '
|
|
497
|
+
'env.stepFailed.shell': '连不上命令行',
|
|
476
498
|
'env.toolchain.ok': '正常',
|
|
477
499
|
'env.toolchain.warn': '还没完全准备好',
|
|
478
500
|
'env.opencli.ok': '已就绪',
|
|
@@ -489,10 +511,10 @@ export const zh = {
|
|
|
489
511
|
'env.fix.title': '怎么解决',
|
|
490
512
|
'env.fix.why': '工作台用它自己那份后台工具(与你在终端里那套分开装,互不影响),这台机器上还没装好它。',
|
|
491
513
|
'env.fix.whyPath': '后台工具装上了,但当前环境还找不到它 —— 通常是 PATH 或终端会话的问题。',
|
|
492
|
-
'env.fix.whyShell': '
|
|
514
|
+
'env.fix.whyShell': '工作台拿不到这台机器的命令行,所以取不了数据 —— 这一步你装不了,需要有人处理后台环境。',
|
|
493
515
|
'env.fix.step': '在终端里运行下面这条命令,装好之后点「重试」:',
|
|
494
516
|
'env.fix.stepPath': '新开一个终端窗口(或确认 npm 的全局 bin 目录在 PATH 里),然后点「重试」。',
|
|
495
|
-
'env.fix.stepShell': '重启一次工作台(或 dsh
|
|
517
|
+
'env.fix.stepShell': '重启一次工作台(或 dsh);如果反复出现,说明工作台还是拿不到命令行。',
|
|
496
518
|
'env.fix.permission': '权限不够:在命令前加 sudo 重跑,或先把 npm 的全局目录配到你的用户目录。',
|
|
497
519
|
'env.fix.network': '网络不通:检查网络或代理设置后重试。',
|
|
498
520
|
'env.fix.details': '详细输出(排障用)',
|
|
@@ -521,9 +543,9 @@ export const zh = {
|
|
|
521
543
|
'env.warm.done': '已就绪({n} 个站点)',
|
|
522
544
|
'env.warm.short': '这次没提前连上 —— 不影响用,第一次取数会自己补',
|
|
523
545
|
'env.unit.failed': '这个模块起不来',
|
|
524
|
-
'env.unit.failedHint': '
|
|
525
|
-
'env.unit.unchecked': '
|
|
526
|
-
'env.unit.uncheckedHint': '
|
|
546
|
+
'env.unit.failedHint': '它自己启动时报了错,别的模块不受影响。',
|
|
547
|
+
'env.unit.unchecked': '检测不了:拿不到命令行',
|
|
548
|
+
'env.unit.uncheckedHint': '不是这个模块的问题,是工作台拿不到命令行 —— 重启一次工作台再试。',
|
|
527
549
|
'adapterSet.not-configured': '没配置适配器集合,用默认的。',
|
|
528
550
|
'adapterSet.no-profile': '找不到 profile,没法加载适配器集合;先用默认的。',
|
|
529
551
|
'adapterSet.malformed': '适配器集合要写包名(如 @acme/adapters-set);先用默认的。',
|
|
@@ -566,7 +588,7 @@ export const zh = {
|
|
|
566
588
|
'sidebarEdition.official': '官方',
|
|
567
589
|
'sidebarEdition.officialHint': '跟 dsh 官方一致、最稳:搜索、视图选项、显示更多、归档、添加工作区都齐,升级也不等我们适配。想省心就选它。',
|
|
568
590
|
'sidebarEdition.custom': '自定义',
|
|
569
|
-
'sidebarEdition.customHint': '
|
|
591
|
+
'sidebarEdition.customHint': '为多项目工作流做的:项目下拉一屏一个项目,会话按最近更新排,名字自动滚动、两侧渐隐,工坊 DIY 新会话。项目多时更顺手。',
|
|
570
592
|
'sidebarEdition.saving': '配置中…',
|
|
571
593
|
'sidebarEdition.reloadRequired': '已写入。刷新页面后生效(不用重启工作台)。',
|
|
572
594
|
'sidebarEdition.reload': '刷新页面',
|
|
@@ -595,25 +617,25 @@ export const zh = {
|
|
|
595
617
|
'record.act.halt': '停',
|
|
596
618
|
'approval.record.site': '站点',
|
|
597
619
|
'approval.record.what': '录制功能',
|
|
598
|
-
'approval.record.lands': '
|
|
620
|
+
'approval.record.lands': '保存到',
|
|
599
621
|
'approval.record.noUrl': '没给站点地址(这次会被当场拒)',
|
|
600
622
|
'approval.record.oneAction': '一次操作',
|
|
601
623
|
'approval.record.noSwitch': '录完不会替你切回来(左栏底部会亮一条状态)',
|
|
602
|
-
'approval.longTask.what': '
|
|
603
|
-
'approval.longTask.signal': '停了只是让工作台发出中断信号 ——
|
|
624
|
+
'approval.longTask.what': '要停的那次任务',
|
|
625
|
+
'approval.longTask.signal': '停了只是让工作台发出中断信号 —— 这次任务怎么收尾由模块自己负责',
|
|
604
626
|
'approval.publish.module': '模块',
|
|
605
627
|
'approval.publish.target': '目标',
|
|
606
628
|
'approval.publish.version': '版本',
|
|
607
629
|
'approval.publish.author': '作者',
|
|
608
|
-
'approval.publish.authorMissing': '
|
|
630
|
+
'approval.publish.authorMissing': '未署名',
|
|
609
631
|
'approval.publish.registry': '源',
|
|
610
632
|
'approval.publish.repo': '仓库',
|
|
611
633
|
'approval.publish.branch': '分支',
|
|
612
634
|
'approval.publish.defaultBranch': '默认分支',
|
|
613
|
-
'approval.publish.artifacts': '
|
|
635
|
+
'approval.publish.artifacts': '打包文件',
|
|
614
636
|
'approval.publish.index': '索引',
|
|
615
637
|
'approval.publish.commits': '提交',
|
|
616
|
-
'approval.publish.twoCommits': '
|
|
638
|
+
'approval.publish.twoCommits': '打包文件 + market.json 两次提交,一次推送',
|
|
617
639
|
'approval.publish.reading': '读取中…',
|
|
618
640
|
'approval.publish.changelog': '{version} 的更新日志',
|
|
619
641
|
'approval.publish.changelogMissing': '这一版还没有更新日志({version}),发布会被拦下',
|
package/dist/lib/dsh-client.d.ts
CHANGED
|
@@ -121,11 +121,13 @@ export interface DshSlots {
|
|
|
121
121
|
onMutate?(callback: (name: string) => void): () => void;
|
|
122
122
|
}
|
|
123
123
|
/**
|
|
124
|
-
* dsh
|
|
124
|
+
* dsh 的语言服务:按命名空间注册字典,再取回那个命名空间的文案函数;
|
|
125
|
+
* `resolveText` 按当前语言取一份字面量(字符串原样返回,映射按语言回退链取)。
|
|
125
126
|
*/
|
|
126
127
|
export interface DshLocale {
|
|
127
128
|
register(namespace: string, dictionaries: Record<string, Record<string, string>>): () => void;
|
|
128
129
|
bind(namespace: string): Translate;
|
|
130
|
+
resolveText?(text: string | Readonly<Record<string, string | undefined>>): string;
|
|
129
131
|
}
|
|
130
132
|
/**
|
|
131
133
|
* dsh 的布局服务:选面板、开关左栏、开合右栏;右栏宽度经可选的 panels 写。
|
package/package.json
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { unitTitleKey } from '../unit.js';
|
|
2
3
|
import { declarationProblemsOf, readDeclaration } from '../unit-declaration.js';
|
|
3
4
|
|
|
4
5
|
const declared = (tools: unknown) => ({
|
|
5
|
-
apiVersion:
|
|
6
|
+
apiVersion: 3,
|
|
6
7
|
unit: 'demo',
|
|
8
|
+
title: { zh: '演示', en: 'Demo' },
|
|
7
9
|
card: null,
|
|
8
10
|
tools,
|
|
9
11
|
host: { inject: [] },
|
|
@@ -50,3 +52,33 @@ describe('清单里的工具声明', () => {
|
|
|
50
52
|
expect(read.manifest?.tools).toEqual([{ name: 'workbench_demo_sync', writes: true }]);
|
|
51
53
|
});
|
|
52
54
|
});
|
|
55
|
+
|
|
56
|
+
describe('清单里的名字(v3 起名字在顶层,是字面量)', () => {
|
|
57
|
+
it('中英各一份,都有就过', () => {
|
|
58
|
+
expect(declarationProblemsOf('@t/demo', declared([]))).toEqual([]);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('少一门 / 空串 → 红,点名那一门', () => {
|
|
62
|
+
expect(declarationProblemsOf('@t/demo', { ...declared([]), title: { zh: '演示' } }).join('\n')).toMatch(
|
|
63
|
+
/title\.en/,
|
|
64
|
+
);
|
|
65
|
+
expect(
|
|
66
|
+
declarationProblemsOf('@t/demo', { ...declared([]), title: { zh: '演示', en: ' ' } }).join('\n'),
|
|
67
|
+
).toMatch(/title\.en 要是非空字符串/);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('★ v2 的老写法(card.titleKey)被点名,并给出迁移提示', () => {
|
|
71
|
+
const v2 = { ...declared([]), apiVersion: 2, card: { titleKey: 'demo.cardTitle', order: 7 } };
|
|
72
|
+
expect(declarationProblemsOf('@t/demo', v2).join('\n')).toMatch(/v3 把卡片标题挪成了顶层/);
|
|
73
|
+
const keyOnly = { ...declared([]), card: { titleKey: 'demo.cardTitle', order: 7 } };
|
|
74
|
+
expect(declarationProblemsOf('@t/demo', keyOnly).join('\n')).toMatch(/不认的键/);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('★ 清单里的名字键由外壳合成:模块不用写、也别想写', () => {
|
|
78
|
+
const read = readDeclaration('@t/demo', { ...declared([]), card: { order: 7 } });
|
|
79
|
+
|
|
80
|
+
expect(read.problems).toEqual([]);
|
|
81
|
+
expect(read.manifest?.title).toEqual({ zh: '演示', en: 'Demo' });
|
|
82
|
+
expect(read.manifest?.card).toEqual({ titleKey: unitTitleKey('demo'), order: 7 });
|
|
83
|
+
});
|
|
84
|
+
});
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { UnitTitle } from '../unit.js';
|
|
2
|
+
|
|
1
3
|
export const BUNDLE_REGISTRIES_ROUTE = 'bundle-registries';
|
|
2
4
|
|
|
3
5
|
export const BUNDLE_INSPECT_ROUTE = 'bundle-inspect';
|
|
@@ -174,11 +176,15 @@ export interface BundleCancellationPayload {
|
|
|
174
176
|
readonly cancellation: BundleCancellation | null;
|
|
175
177
|
}
|
|
176
178
|
|
|
179
|
+
export type ModuleUpdateBlock = 'needs-workbench' | 'incompatible';
|
|
180
|
+
|
|
177
181
|
export interface ModuleVersion {
|
|
178
182
|
readonly unit: string;
|
|
179
183
|
readonly pkg: string;
|
|
180
184
|
readonly installed: string;
|
|
185
|
+
readonly title?: UnitTitle;
|
|
181
186
|
readonly latest?: string;
|
|
187
|
+
readonly latestBlocked?: ModuleUpdateBlock;
|
|
182
188
|
readonly local?: boolean;
|
|
183
189
|
readonly refused?: readonly string[];
|
|
184
190
|
}
|
|
@@ -189,6 +195,11 @@ export interface WorkbenchVersion {
|
|
|
189
195
|
readonly latest?: string;
|
|
190
196
|
readonly running?: string;
|
|
191
197
|
readonly local?: boolean;
|
|
198
|
+
readonly followUp?: {
|
|
199
|
+
readonly id: string;
|
|
200
|
+
readonly kind: 'data-migration';
|
|
201
|
+
readonly hard: boolean;
|
|
202
|
+
};
|
|
192
203
|
}
|
|
193
204
|
|
|
194
205
|
export interface ModuleVersionsPayload {
|
|
@@ -4,6 +4,7 @@ export const MARKET_DISCOVER_ROUTE = 'panels/market-discover';
|
|
|
4
4
|
export const MARKET_DOC_ROUTE = 'panels/market-doc';
|
|
5
5
|
export const MARKET_PUBLISH_PREVIEW_ROUTE = 'panels/market-publish-preview';
|
|
6
6
|
export const MARKET_SOURCES_RESOURCE = 'market-sources';
|
|
7
|
+
export const MARKET_SOURCES_ROUTE = 'panels/market-sources';
|
|
7
8
|
export const MARKET_WORKBENCH_LABEL = 'workbench';
|
|
8
9
|
|
|
9
10
|
export type MarketSourceKind = 'http' | 'git';
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import type { UnitHostNeeds, UnitManifest } from '../contract/unit.js';
|
|
1
|
+
import type { UnitHostNeeds, UnitManifest, UnitTitle } from '../contract/unit.js';
|
|
2
|
+
import { unitTitleKey } from '../contract/unit.js';
|
|
2
3
|
import { needsProblemsOf, type Need } from './needs.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* 声明协议当前版本;模块清单的 dsh.workbench.apiVersion 要写这个整数。
|
|
6
7
|
*/
|
|
7
|
-
export const WORKBENCH_API_VERSION =
|
|
8
|
+
export const WORKBENCH_API_VERSION = 3;
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* 外壳愿意认的协议版本;不在表里的模块当场被拒,不猜语义。
|
|
@@ -12,10 +13,9 @@ export const WORKBENCH_API_VERSION = 2;
|
|
|
12
13
|
export const SUPPORTED_API_VERSIONS: readonly number[] = [WORKBENCH_API_VERSION];
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
|
-
*
|
|
16
|
+
* 清单里那半张卡片声明:只剩排序位;卡片默认显不显示不在这里(那是运行期面板配置的事)。
|
|
16
17
|
*/
|
|
17
18
|
export interface UnitCardDeclaration {
|
|
18
|
-
readonly titleKey: string;
|
|
19
19
|
readonly order: number;
|
|
20
20
|
}
|
|
21
21
|
|
|
@@ -29,11 +29,12 @@ export interface UnitToolDeclaration {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
|
-
* 模块清单里 dsh.workbench 那一段的完整形状(协议版本、单元 id
|
|
32
|
+
* 模块清单里 dsh.workbench 那一段的完整形状(协议版本、单元 id、名字、卡片、工具、宿主与环境需要)。
|
|
33
33
|
*/
|
|
34
34
|
export interface UnitDeclaration {
|
|
35
35
|
readonly apiVersion: number;
|
|
36
36
|
readonly unit: string;
|
|
37
|
+
readonly title: UnitTitle;
|
|
37
38
|
readonly card: UnitCardDeclaration | null;
|
|
38
39
|
readonly tools: readonly UnitToolDeclaration[];
|
|
39
40
|
readonly host: UnitHostNeeds;
|
|
@@ -46,6 +47,7 @@ export interface UnitDeclaration {
|
|
|
46
47
|
export const DECLARATION_KEYS: readonly string[] = [
|
|
47
48
|
'apiVersion',
|
|
48
49
|
'unit',
|
|
50
|
+
'title',
|
|
49
51
|
'card',
|
|
50
52
|
'tools',
|
|
51
53
|
'host',
|
|
@@ -58,12 +60,14 @@ export const DECLARATION_KEYS: readonly string[] = [
|
|
|
58
60
|
export const DECLARATION_REQUIRED_KEYS: readonly string[] = [
|
|
59
61
|
'apiVersion',
|
|
60
62
|
'unit',
|
|
63
|
+
'title',
|
|
61
64
|
'card',
|
|
62
65
|
'tools',
|
|
63
66
|
'host',
|
|
64
67
|
];
|
|
65
68
|
|
|
66
|
-
const CARD_KEYS = ['
|
|
69
|
+
const CARD_KEYS = ['order'];
|
|
70
|
+
const TITLE_KEYS = ['zh', 'en'];
|
|
67
71
|
const TOOL_KEYS = ['name', 'writes', 'key'];
|
|
68
72
|
|
|
69
73
|
const TOOL_KEY_SHAPE = /^[a-z][a-zA-Z0-9]*$/;
|
|
@@ -77,6 +81,17 @@ function isRecord(value: unknown): value is Readonly<Record<string, unknown>> {
|
|
|
77
81
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
78
82
|
}
|
|
79
83
|
|
|
84
|
+
/**
|
|
85
|
+
* 读一份声明里的协议版本;不是对象或 apiVersion 不是整数时给 undefined。
|
|
86
|
+
* @param raw 清单里 dsh.workbench 那段原始值。
|
|
87
|
+
* @returns 声明声明的协议版本,读不出来时 undefined。
|
|
88
|
+
*/
|
|
89
|
+
export function declarationApiVersionOf(raw: unknown): number | undefined {
|
|
90
|
+
if (!isRecord(raw)) return undefined;
|
|
91
|
+
const version = raw.apiVersion;
|
|
92
|
+
return Number.isInteger(version) ? (version as number) : undefined;
|
|
93
|
+
}
|
|
94
|
+
|
|
80
95
|
function outsideKeys(value: Readonly<Record<string, unknown>>, allowed: readonly string[]): string[] {
|
|
81
96
|
return Object.keys(value).filter((key) => !allowed.includes(key));
|
|
82
97
|
}
|
|
@@ -110,10 +125,9 @@ export function declarationProblemsOf(subject: string, raw: unknown): readonly s
|
|
|
110
125
|
}
|
|
111
126
|
if (!Number.isInteger(version) || !SUPPORTED_API_VERSIONS.includes(version as number)) {
|
|
112
127
|
const hint =
|
|
113
|
-
version ===
|
|
114
|
-
?
|
|
115
|
-
'
|
|
116
|
-
'再把 apiVersion 改成 2'
|
|
128
|
+
version === 2
|
|
129
|
+
? ' —— v3 把卡片标题挪成了顶层 `title`(`{ "zh": "…", "en": "…" }` 字面量):' +
|
|
130
|
+
'把 `card.titleKey` 删掉、在顶层写 title,再把 apiVersion 改成 3'
|
|
117
131
|
: '';
|
|
118
132
|
return [
|
|
119
133
|
`${subject}: dsh.workbench.apiVersion 是 ${JSON.stringify(version)},这里只认 ` +
|
|
@@ -140,6 +154,29 @@ export function declarationProblemsOf(subject: string, raw: unknown): readonly s
|
|
|
140
154
|
}
|
|
141
155
|
}
|
|
142
156
|
|
|
157
|
+
const title = raw.title;
|
|
158
|
+
if (title !== undefined) {
|
|
159
|
+
if (!isRecord(title)) {
|
|
160
|
+
problems.push(
|
|
161
|
+
`${subject}: dsh.workbench.title 要是对象(中英各一份),现在是 ${JSON.stringify(title)}`,
|
|
162
|
+
);
|
|
163
|
+
} else {
|
|
164
|
+
const extra = outsideKeys(title, TITLE_KEYS);
|
|
165
|
+
if (extra.length > 0) {
|
|
166
|
+
problems.push(
|
|
167
|
+
`${subject}: dsh.workbench.title 里有不认的键 ${listOf(extra)} —— title 只有 ${listOf(TITLE_KEYS)}`,
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
for (const language of TITLE_KEYS) {
|
|
171
|
+
if (text(title[language]) === '') {
|
|
172
|
+
problems.push(
|
|
173
|
+
`${subject}: dsh.workbench.title.${language} 要是非空字符串(市场和面板里显示的名字)`,
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
143
180
|
const card = raw.card;
|
|
144
181
|
if (card !== undefined && card !== null) {
|
|
145
182
|
if (!isRecord(card)) {
|
|
@@ -149,12 +186,9 @@ export function declarationProblemsOf(subject: string, raw: unknown): readonly s
|
|
|
149
186
|
if (extra.length > 0) {
|
|
150
187
|
problems.push(
|
|
151
188
|
`${subject}: dsh.workbench.card 里有不认的键 ${listOf(extra)} —— card 只有 ${listOf(CARD_KEYS)}` +
|
|
152
|
-
|
|
189
|
+
`(名字写在顶层的 title 里;卡片默认显不显示由运行期的面板配置决定,清单里没有这一位)`,
|
|
153
190
|
);
|
|
154
191
|
}
|
|
155
|
-
if (text(card.titleKey) === '') {
|
|
156
|
-
problems.push(`${subject}: dsh.workbench.card.titleKey 要是非空字符串(面板那一格的文案键)`);
|
|
157
|
-
}
|
|
158
192
|
if (typeof card.order !== 'number' || !Number.isFinite(card.order)) {
|
|
159
193
|
problems.push(`${subject}: dsh.workbench.card.order 要是数字,现在是 ${JSON.stringify(card.order)}`);
|
|
160
194
|
}
|
|
@@ -257,16 +291,18 @@ export function readDeclaration(subject: string, raw: unknown): DeclarationRead
|
|
|
257
291
|
const needsRead = needsProblemsOf(subject, declaration.needs);
|
|
258
292
|
if (needsRead.errors.length > 0) return { problems: needsRead.errors, warnings: needsRead.warnings };
|
|
259
293
|
const card = declaration.card;
|
|
294
|
+
const id = text(declaration.unit);
|
|
260
295
|
return {
|
|
261
296
|
declaration,
|
|
262
297
|
problems: [],
|
|
263
298
|
manifest: {
|
|
264
|
-
id
|
|
299
|
+
id,
|
|
300
|
+
title: { zh: text(declaration.title.zh), en: text(declaration.title.en) },
|
|
265
301
|
card:
|
|
266
302
|
card === null
|
|
267
303
|
? null
|
|
268
304
|
: {
|
|
269
|
-
titleKey:
|
|
305
|
+
titleKey: unitTitleKey(id),
|
|
270
306
|
order: card.order,
|
|
271
307
|
},
|
|
272
308
|
tools: declaration.tools.map((tool) => ({
|
package/src/contract/unit.ts
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
import type { ExtensionNeed, LibNeed, Need } from './needs.js';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* 模块的人类可读名字:中英各一份,写在 package.json 的 dsh.workbench.title 里。
|
|
5
|
+
*/
|
|
6
|
+
export interface UnitTitle {
|
|
7
|
+
readonly zh: string;
|
|
8
|
+
readonly en: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 外壳替模块名合成的词表键:模块自己不必写名字键,名字直接从清单里的 title 注册进来。
|
|
13
|
+
* @param unit 单元 id。
|
|
14
|
+
* @returns 该单元名字在词表里的键。
|
|
15
|
+
*/
|
|
16
|
+
export function unitTitleKey(unit: string): string {
|
|
17
|
+
return `dsh-workbench.unit.${unit}.title`;
|
|
18
|
+
}
|
|
19
|
+
|
|
3
20
|
/**
|
|
4
21
|
* 一张卡片在清单里的静态声明(文案键加排序位);默认显不显示由运行期面板配置定。
|
|
5
22
|
*/
|
|
@@ -33,10 +50,11 @@ export interface PluginRelease {
|
|
|
33
50
|
}
|
|
34
51
|
|
|
35
52
|
/**
|
|
36
|
-
* 装配期用的模块清单:单元 id
|
|
53
|
+
* 装配期用的模块清单:单元 id、名字、卡片、工具、宿主服务与环境需要,比声明少一层协议包装。
|
|
37
54
|
*/
|
|
38
55
|
export interface UnitManifest {
|
|
39
56
|
readonly id: string;
|
|
57
|
+
readonly title?: UnitTitle;
|
|
40
58
|
readonly card: UnitCard | null;
|
|
41
59
|
readonly tools: readonly UnitTool[];
|
|
42
60
|
readonly host: UnitHostNeeds;
|
|
@@ -2,12 +2,16 @@ import { describe, expect, it } from 'vitest';
|
|
|
2
2
|
import type { ExtensionNeed, LibNeed, SupplySource } from '../../contract/needs.js';
|
|
3
3
|
import { buildSupplyIndex, type SupplyDeclaration } from '../supply-index.js';
|
|
4
4
|
|
|
5
|
-
function npm(
|
|
5
|
+
function npm(
|
|
6
|
+
pkg?: string,
|
|
7
|
+
registryEnv = 'DSH_WB_REGISTRY',
|
|
8
|
+
registryDefault = 'https://registry.invalid/',
|
|
9
|
+
): SupplySource {
|
|
6
10
|
return {
|
|
7
11
|
kind: 'npm',
|
|
8
12
|
...(pkg === undefined ? {} : { package: pkg }),
|
|
9
13
|
registryEnv,
|
|
10
|
-
registryDefault
|
|
14
|
+
registryDefault,
|
|
11
15
|
};
|
|
12
16
|
}
|
|
13
17
|
|
|
@@ -77,23 +81,55 @@ describe('供给索引:一个包只有一个来源', () => {
|
|
|
77
81
|
expect(index.sourcesOf('extension', 'coding')).toEqual([npm('opencli-plugin-coding')]);
|
|
78
82
|
});
|
|
79
83
|
|
|
80
|
-
it('★★ 共用库两家用不同的 registryEnv
|
|
84
|
+
it('★★ 共用库两家用不同的 registryEnv、解析到同一个地址:收成一家,不算冲突', () => {
|
|
81
85
|
const index = buildSupplyIndex([
|
|
82
86
|
manifest('argocd', [lib('opencli-workflow', [npm(undefined, 'DSH_WORKBENCH_ARGOCD_REGISTRY')])]),
|
|
83
87
|
manifest('cmdb', [lib('opencli-workflow', [npm(undefined, 'DSH_WORKBENCH_CMDB_REGISTRY')])]),
|
|
84
88
|
]);
|
|
85
89
|
|
|
90
|
+
expect(index.conflicts).toEqual([]);
|
|
91
|
+
expect(index.entryOf('lib', 'opencli-workflow')?.conflicted).toBe(false);
|
|
92
|
+
expect(index.entryOf('lib', 'opencli-workflow')?.declarers).toEqual(['argocd', 'cmdb']);
|
|
93
|
+
expect(index.sourcesOf('lib', 'opencli-workflow')).toHaveLength(1);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('★★ registryEnv 名不同、其中一家被环境变量指到别处:判冲突', () => {
|
|
97
|
+
const index = buildSupplyIndex(
|
|
98
|
+
[
|
|
99
|
+
manifest('jenkins', [lib('opencli-workflow', [npm(undefined, 'DSH_WB_JENKINS_REGISTRY')])]),
|
|
100
|
+
manifest('argocd', [lib('opencli-workflow', [npm(undefined, 'DSH_WB_ARGOCD_REGISTRY')])]),
|
|
101
|
+
],
|
|
102
|
+
{ env: { DSH_WB_JENKINS_REGISTRY: 'https://mirror.invalid/' } },
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
expect(index.conflicts.map((item) => item.kind)).toEqual(['source-mismatch']);
|
|
106
|
+
expect(index.sourcesOf('lib', 'opencli-workflow')).toEqual([]);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('★★ 两家默认源就不同:判冲突,哪怕 registryEnv 同名', () => {
|
|
110
|
+
const index = buildSupplyIndex([
|
|
111
|
+
manifest('a', [lib('opencli-workflow', [npm(undefined, 'DSH_WB_REGISTRY', 'https://r-a.invalid/')])]),
|
|
112
|
+
manifest('b', [lib('opencli-workflow', [npm(undefined, 'DSH_WB_REGISTRY', 'https://r-b.invalid/')])]),
|
|
113
|
+
]);
|
|
114
|
+
|
|
86
115
|
expect(index.conflicts).toHaveLength(1);
|
|
87
|
-
expect(index.conflicts[0]?.kind).toBe('source-mismatch');
|
|
88
116
|
expect(index.entryOf('lib', 'opencli-workflow')?.conflicted).toBe(true);
|
|
89
|
-
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('★ 两家都没写默认源、registryEnv 又不同:解析不出地址,按名字判冲突', () => {
|
|
120
|
+
const index = buildSupplyIndex([
|
|
121
|
+
manifest('a', [lib('opencli-workflow', [{ kind: 'npm', registryEnv: 'DSH_WB_A_REGISTRY' }])]),
|
|
122
|
+
manifest('b', [lib('opencli-workflow', [{ kind: 'npm', registryEnv: 'DSH_WB_B_REGISTRY' }])]),
|
|
123
|
+
]);
|
|
124
|
+
|
|
125
|
+
expect(index.conflicts).toHaveLength(1);
|
|
90
126
|
});
|
|
91
127
|
|
|
92
128
|
it('★★ 三家三个源:同一个包只报一条冲突,declarers 合并齐', () => {
|
|
93
129
|
const index = buildSupplyIndex([
|
|
94
130
|
manifest('a', [lib('opencli-workflow', [npm(undefined, 'DSH_WB_A_REGISTRY')])]),
|
|
95
131
|
manifest('b', [lib('opencli-workflow', [npm(undefined, 'DSH_WB_B_REGISTRY')])]),
|
|
96
|
-
manifest('c', [lib('opencli-workflow', [npm(undefined, 'DSH_WB_C_REGISTRY')])]),
|
|
132
|
+
manifest('c', [lib('opencli-workflow', [npm(undefined, 'DSH_WB_C_REGISTRY', 'https://mirror.invalid/')])]),
|
|
97
133
|
]);
|
|
98
134
|
|
|
99
135
|
expect(index.conflicts).toHaveLength(1);
|