@dsh-workbench/shared 1.1.1 → 1.1.3

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/src/i18n/zh.ts CHANGED
@@ -255,6 +255,7 @@ export const zh = {
255
255
  'panels.market.add.failureInstalled':
256
256
  '它已经装在这份工作台里了。同一个包不能就地覆盖,得先卸载再装;这次卸载也没成功。',
257
257
  'panels.market.add.failureGeneric': '安装过程中出错,原因见安装详情',
258
+ 'panels.market.add.failureCredentials': '这个仓库要凭证:本机没有它的 git 凭据。先在终端里 clone 一次、配好凭据助手,或改用 SSH 地址,再装一次。',
258
259
  'panels.market.add.approveTitle': '需要你允许安装脚本',
259
260
  'panels.market.add.approveCaution': '只允许你信得过的包:安装脚本会以你的权限在这台机器上运行。',
260
261
  'panels.market.add.approveRun': '允许并继续安装',
@@ -296,10 +297,16 @@ export const zh = {
296
297
  'panels.discover.addingSource': '正在添加…',
297
298
  'panels.discover.addFirstSource': '添加索引源',
298
299
  'panels.discover.removeSource': '移除',
299
- 'panels.discover.removeSourceNamed': '移除 {url}',
300
- 'panels.discover.useSource': '使用',
301
- 'panels.discover.useSourceNamed': '使用 {label}',
302
- 'panels.discover.sourceCurrent': '当前使用',
300
+ 'panels.discover.removeTitle': '移除这个索引源?',
301
+ 'panels.discover.removeBody': '会把「{label}」从这份列表里移除;仓库本身不会被删,之后还能再添加。',
302
+ 'panels.discover.sourceActions': '「{label}」的操作',
303
+ 'panels.discover.sourceStateWritable': '可发布',
304
+ 'panels.discover.sourceStateReadOnly': '仅读取模块列表',
305
+ 'panels.discover.sourceEnable': '设为可发布',
306
+ 'panels.discover.sourceDisable': '设为只读',
307
+ 'panels.discover.sourceWorking': '设置中…',
308
+ 'panels.discover.sourceEnabled': '已把「{label}」设为可发布',
309
+ 'panels.discover.sourceReadOnly': '已把「{label}」设为只读',
303
310
  'panels.discover.refresh': '刷新',
304
311
  'panels.discover.search': '搜索模块',
305
312
  'panels.discover.clearSearch': '清除搜索',
@@ -321,12 +328,13 @@ export const zh = {
321
328
  'panels.discover.noSourceHint': '添加一个模块市场索引(http 地址或 git 仓库),即可在这里浏览和安装其中的模块',
322
329
  'panels.discover.sourcesFailed': '无法读取索引源',
323
330
  'panels.discover.unreadable': '无法读取此索引',
331
+ 'panels.discover.sourceAuth': '这个索引源读不了:本机没有它的 git 凭据。配好凭据(或在源里改用 SSH 地址),再点右上角的刷新。',
324
332
  'panels.discover.invalid': '此索引的格式无效',
325
333
  'panels.discover.emptyRepo': '这个仓库还没有任何提交',
326
334
  'panels.discover.missingIndex': '仓库里还没有索引文件 {path}',
327
335
  'panels.discover.initTitle': '把这个仓库初始化成模块市场索引?',
328
336
  'panels.discover.initCta': '初始化成市场索引',
329
- 'panels.discover.initWhat': '会在 {branch} 分支写 market.json 与 README.md,提交一次并推上去;之后从这个工作台发布的模块会把打包好的文件提交到 packages/<unit>/。仓库的写权限归它的所有者。',
337
+ 'panels.discover.initWhat': '会在 {branch} 分支写 market.json 与 README.md,提交一次并推上去;之后从这个工作台发布的模块会把打包好的文件提交到按包名分的 packages/<包名>/。仓库的写权限归它的所有者。',
330
338
  'panels.discover.initBranch': '分支',
331
339
  'panels.discover.initBranchHint': '索引和打包文件都发到这个分支;空仓库会新建它。',
332
340
  'panels.discover.initBranchBad': '分支名只能用字母、数字、点、下划线、斜杠或短横线',
@@ -344,8 +352,23 @@ export const zh = {
344
352
  'panels.discover.badShellDetail': '此版本与当前的工作台 {current} 不兼容,它要求 {peer}',
345
353
  'panels.discover.unverified': '工作台未验证',
346
354
  'panels.discover.installing': '正在安装…',
355
+ 'panels.discover.installingNamed': '正在安装「{name}」…',
356
+ 'panels.discover.cancelInstall': '取消安装',
357
+ 'panels.discover.cancelling': '正在取消…',
358
+ 'panels.discover.installCancelled': '已取消安装:{name}',
347
359
  'panels.discover.installedDone': '装好了:{name}',
348
360
  'panels.discover.failed': '操作失败:{reason}',
361
+ 'panels.discover.transportTitle': '从哪读这个仓库',
362
+ 'panels.discover.transportSsh': 'SSH',
363
+ 'panels.discover.transportHttps': 'HTTPS',
364
+ 'panels.discover.transportOk': '能读',
365
+ 'panels.discover.transportAuth': '要凭证',
366
+ 'panels.discover.transportUnreachable': '连不上',
367
+ 'panels.discover.transportUnknown': '没探成',
368
+ 'panels.discover.transportChecking': '正在探测…',
369
+ 'panels.discover.transportTest': '试这个地址',
370
+ 'panels.discover.transportPlaceholder': '或贴一个别的地址(公司镜像、别名)',
371
+ 'panels.discover.transportNeedsAuth': '这个仓库要凭证:先在终端里把它 clone 一次,或配好 SSH key / 凭据助手;配好后再点安装。',
349
372
  'panels.discover.expand': '展开版本',
350
373
  'panels.discover.collapse': '收起版本',
351
374
  'panels.discover.versionCount': '{n} 个版本',
@@ -642,6 +665,13 @@ export const zh = {
642
665
  'approval.publish.version': '版本',
643
666
  'approval.publish.author': '作者',
644
667
  'approval.publish.authorMissing': '未署名',
668
+ 'approval.publish.owner': '认领人',
669
+ 'approval.publish.ownerUnowned': '原来没署名',
670
+ 'approval.publish.ownerTaken': '已有主人:{owner}',
671
+ 'approval.publish.unit': '模块 id',
672
+ 'approval.publish.unitTaken': '已被 {entry} 占用',
673
+ 'approval.publish.path': '产物目录',
674
+ 'approval.publish.pathTaken': '已被 {entry} 占用',
645
675
  'approval.publish.registry': '源',
646
676
  'approval.publish.repo': '仓库',
647
677
  'approval.publish.branch': '分支',
@@ -652,6 +682,7 @@ export const zh = {
652
682
  'approval.publish.twoCommits': '打包文件 + market.json 两次提交,一次推送',
653
683
  'approval.publish.reading': '读取中…',
654
684
  'approval.publish.changelog': '{version} 的更新日志',
685
+ 'approval.publish.changelogPending': '{version} 这一版会写进更新日志,随包发布',
655
686
  'approval.publish.changelogMissing': '这一版还没有更新日志({version}),发布会被拦下',
656
687
  'approval.publish.targetAuto': '按配置默认',
657
688
  'approval.publish.targetTeam': '团队源',
@@ -660,6 +691,15 @@ export const zh = {
660
691
  'approval.publish.targetGit': 'git 仓库',
661
692
  'approval.publish.targetMarket': '模块市场索引',
662
693
  'approval.publish.targetLocal': '留在本机',
694
+ 'approval.market.source': '索引源',
695
+ 'approval.market.url': '地址',
696
+ 'approval.market.action': '这次操作',
697
+ 'approval.market.policy': '当前状态',
698
+ 'approval.market.enable': '设为可发布',
699
+ 'approval.market.disable': '改回只读',
700
+ 'approval.market.writable': '可发布',
701
+ 'approval.market.readOnly': '仅读取模块列表',
702
+ 'approval.market.reading': '正在读取…',
663
703
  'markdown.copy': '复制',
664
704
  'markdown.copied': '已复制',
665
705
  'markdown.footnotes': '脚注',
package/src/index.ts CHANGED
@@ -66,6 +66,7 @@ export * from './i18n/settings.js';
66
66
  export * from './i18n/zh.js';
67
67
  export * from './lib/blocker-action.js';
68
68
  export * from './lib/market-index.js';
69
+ export * from './lib/git-address.js';
69
70
  export * from './lib/board-fact.js';
70
71
  export * from './lib/busy.js';
71
72
  export * from './lib/dsh-client.js';
@@ -0,0 +1,63 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { gitAddressOf, gitAddressText, gitTransportCandidates } from './git-address.js';
3
+
4
+ describe('git 地址的两种读法', () => {
5
+ it('https 原样留着,另一条推成 ssh://', () => {
6
+ expect(gitTransportCandidates('https://e.coding.net/org/repo.git')).toEqual([
7
+ { kind: 'https', url: 'https://e.coding.net/org/repo.git' },
8
+ { kind: 'ssh', url: 'ssh://git@e.coding.net/org/repo.git' },
9
+ ]);
10
+ });
11
+
12
+ it('scp 写法当 ssh,并规范成 ssh://(pnpm 只认这个)', () => {
13
+ expect(gitTransportCandidates('git@e.coding.net:org/repo.git')).toEqual([
14
+ { kind: 'ssh', url: 'ssh://git@e.coding.net/org/repo.git' },
15
+ { kind: 'https', url: 'https://e.coding.net/org/repo.git' },
16
+ ]);
17
+ });
18
+
19
+ it('git+ 前缀先剥掉,别带进 spec', () => {
20
+ expect(gitTransportCandidates('git+https://host/org/repo.git')[0]).toEqual({
21
+ kind: 'https',
22
+ url: 'https://host/org/repo.git',
23
+ });
24
+ });
25
+
26
+ it('端口只留在它本来那一种上,另一种回到默认端口', () => {
27
+ expect(gitTransportCandidates('ssh://git@host:2222/org/repo.git')).toEqual([
28
+ { kind: 'ssh', url: 'ssh://git@host:2222/org/repo.git' },
29
+ { kind: 'https', url: 'https://host/org/repo.git' },
30
+ ]);
31
+ expect(gitTransportCandidates('https://host:8443/org/repo.git')).toEqual([
32
+ { kind: 'https', url: 'https://host:8443/org/repo.git' },
33
+ { kind: 'ssh', url: 'ssh://git@host/org/repo.git' },
34
+ ]);
35
+ });
36
+
37
+ it('http 的仓库留着 http,别替发布者升到 https', () => {
38
+ expect(gitTransportCandidates('http://host/org/repo.git')[0]).toEqual({
39
+ kind: 'https',
40
+ url: 'http://host/org/repo.git',
41
+ });
42
+ });
43
+
44
+ it('拆得开要拆对,拆不开给 undefined', () => {
45
+ expect(gitAddressOf('https://host/a/b.git')).toEqual({ kind: 'https', host: 'host', path: 'a/b.git' });
46
+ expect(gitAddressOf('ssh://git@host:2222/a/b.git')).toEqual({
47
+ kind: 'ssh',
48
+ host: 'host',
49
+ path: 'a/b.git',
50
+ user: 'git',
51
+ port: 2222,
52
+ });
53
+ expect(gitAddressOf('git@host:a/b.git')).toEqual({ kind: 'ssh', host: 'host', path: 'a/b.git', user: 'git' });
54
+ expect(gitAddressOf('https://host/')).toBeUndefined();
55
+ expect(gitAddressOf('ftp://host/a.git')).toBeUndefined();
56
+ expect(gitAddressOf('')).toBeUndefined();
57
+ });
58
+
59
+ it('拼回去是规范写法,用户名缺省补 git', () => {
60
+ expect(gitAddressText({ kind: 'ssh', host: 'h', path: 'a/b.git' })).toBe('ssh://git@h/a/b.git');
61
+ expect(gitAddressText({ kind: 'https', host: 'h', path: 'a/b.git', port: 8443 })).toBe('https://h:8443/a/b.git');
62
+ });
63
+ });
@@ -0,0 +1,92 @@
1
+ export interface GitAddress {
2
+ readonly kind: 'https' | 'ssh';
3
+ readonly host: string;
4
+ readonly path: string;
5
+ readonly user?: string;
6
+ readonly port?: number;
7
+ }
8
+
9
+ export interface GitTransportCandidate {
10
+ readonly kind: 'https' | 'ssh';
11
+ readonly url: string;
12
+ }
13
+
14
+ const SCP = /^(?:([A-Za-z0-9._-]+)@)?([A-Za-z0-9.-]+):(?!\/)(\S+)$/;
15
+
16
+ function bare(url: string): string {
17
+ return url.trim().replace(/^git\+/, '');
18
+ }
19
+
20
+ function pathOf(pathname: string): string {
21
+ return pathname.replace(/^\/+/, '').replace(/\/+$/, '');
22
+ }
23
+
24
+ /**
25
+ * 把一个 git 地址拆成「哪台主机 + 哪个路径」,不管发布者当初用的是 https、ssh 还是
26
+ * scp 那种 `git@host:path` 写法;认不出来给 undefined。
27
+ */
28
+ export function gitAddressOf(url: string): GitAddress | undefined {
29
+ const text = bare(url);
30
+ if (text === '') return undefined;
31
+ if (text.includes('://')) {
32
+ let parsed: URL;
33
+ try {
34
+ parsed = new URL(text);
35
+ } catch {
36
+ return undefined;
37
+ }
38
+ if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:' && parsed.protocol !== 'ssh:') {
39
+ return undefined;
40
+ }
41
+ const path = pathOf(parsed.pathname);
42
+ if (path === '') return undefined;
43
+ const port = parsed.port === '' ? undefined : Number(parsed.port);
44
+ const shared = {
45
+ host: parsed.hostname,
46
+ path,
47
+ ...(port === undefined ? {} : { port }),
48
+ };
49
+ if (parsed.protocol !== 'ssh:') return { kind: 'https', ...shared };
50
+ return { kind: 'ssh', ...shared, ...(parsed.username === '' ? {} : { user: parsed.username }) };
51
+ }
52
+ const scp = SCP.exec(text);
53
+ if (scp === null) return undefined;
54
+ const path = pathOf(scp[3] ?? '');
55
+ if (path === '') return undefined;
56
+ return {
57
+ kind: 'ssh',
58
+ host: scp[2] ?? '',
59
+ path,
60
+ ...(scp[1] === undefined ? {} : { user: scp[1] }),
61
+ };
62
+ }
63
+
64
+ /** 一份地址的规范写法:ssh 一律成 `ssh://user@host/path`(pnpm 认这个),https 原样。 */
65
+ export function gitAddressText(address: GitAddress): string {
66
+ const port = address.port === undefined ? '' : `:${String(address.port)}`;
67
+ if (address.kind === 'ssh') {
68
+ return `ssh://${address.user === undefined || address.user === '' ? 'git' : address.user}@${address.host}${port}/${address.path}`;
69
+ }
70
+ return `https://${address.host}${port}/${address.path}`;
71
+ }
72
+
73
+ /**
74
+ * 同一个仓库的两种读法,发布者用的那种排前面。ssh 那条一律规范成 `ssh://` 形式,
75
+ * 因为 `git@host:path` 拼进安装 spec 时 pnpm 不认;https 那条保留发布者的原样(含 http)。
76
+ */
77
+ export function gitTransportCandidates(url: string): readonly GitTransportCandidate[] {
78
+ const address = gitAddressOf(url);
79
+ if (address === undefined) {
80
+ const only = bare(url);
81
+ return only === '' ? [] : [{ kind: only.startsWith('ssh') ? 'ssh' : 'https', url: only }];
82
+ }
83
+ const primary: GitTransportCandidate = {
84
+ kind: address.kind,
85
+ url: address.kind === 'ssh' ? gitAddressText(address) : bare(url),
86
+ };
87
+ const other: GitTransportCandidate =
88
+ address.kind === 'https'
89
+ ? { kind: 'ssh', url: gitAddressText({ kind: 'ssh', host: address.host, path: address.path }) }
90
+ : { kind: 'https', url: gitAddressText({ kind: 'https', host: address.host, path: address.path }) };
91
+ return other.url === primary.url ? [primary] : [primary, other];
92
+ }