dsh-keys-manager 0.0.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +203 -16
- package/README.zh-CN.md +125 -0
- package/bundle/cordis.patch.yml +5 -0
- package/icon.svg +12 -0
- package/lib/client/format.d.ts +8 -0
- package/lib/client/index.d.ts +3 -0
- package/lib/client/locales.d.ts +4 -0
- package/lib/client/runtime.d.ts +21 -0
- package/lib/client/store.d.ts +4 -0
- package/lib/client/styles.d.ts +2 -0
- package/lib/client/types.d.ts +130 -0
- package/lib/client/ui.d.ts +18 -0
- package/lib/client.js +2106 -0
- package/lib/core.d.ts +115 -0
- package/lib/core.js +282 -0
- package/lib/core.js.map +1 -0
- package/lib/domain.d.ts +246 -0
- package/lib/domain.js +9 -0
- package/lib/domain.js.map +1 -0
- package/lib/host-types.d.ts +240 -0
- package/lib/host-types.js +2 -0
- package/lib/host-types.js.map +1 -0
- package/lib/index.d.ts +860 -0
- package/lib/index.js +4157 -0
- package/lib/index.js.map +1 -0
- package/lib/providers.d.ts +28 -0
- package/lib/providers.js +1308 -0
- package/lib/providers.js.map +1 -0
- package/lib/store.d.ts +62 -0
- package/lib/store.js +432 -0
- package/lib/store.js.map +1 -0
- package/locale/en.json +25 -0
- package/locale/zh.json +25 -0
- package/package.json +79 -14
- package/scripts/build-client.mjs +53 -0
- package/scripts/prepare-release.mjs +243 -0
- package/scripts/verify-dist.mjs +30 -0
- package/index.js +0 -30
package/package.json
CHANGED
|
@@ -1,27 +1,92 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-keys-manager",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
"main": "index.js",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Multi-key management for DSH providers: sticky per-session round-robin, retry failover, cooldown recovery, and searchable extra providers.",
|
|
7
6
|
"repository": {
|
|
8
7
|
"type": "git",
|
|
9
|
-
"url": "
|
|
10
|
-
},
|
|
11
|
-
"bugs": {
|
|
12
|
-
"url": "https://github.com/btspoony/dsh-keys-manager/issues"
|
|
8
|
+
"url": "https://github.com/btspoony/dsh-keys-manager"
|
|
13
9
|
},
|
|
14
|
-
"homepage": "https://github.com/btspoony/dsh-keys-manager#readme",
|
|
15
10
|
"keywords": [
|
|
16
11
|
"dsh",
|
|
17
12
|
"dsh-plugin",
|
|
18
13
|
"keys",
|
|
19
14
|
"provider",
|
|
20
|
-
"llm"
|
|
21
|
-
|
|
15
|
+
"llm"
|
|
16
|
+
],
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"main": "./lib/index.js",
|
|
19
|
+
"types": "./lib/index.d.ts",
|
|
20
|
+
"icon": "./icon.svg",
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./lib/index.d.ts",
|
|
24
|
+
"default": "./lib/index.js"
|
|
25
|
+
},
|
|
26
|
+
"./client": {
|
|
27
|
+
"types": "./lib/client/index.d.ts",
|
|
28
|
+
"default": "./lib/client.js"
|
|
29
|
+
},
|
|
30
|
+
"./bundle/cordis.patch.yml": "./bundle/cordis.patch.yml",
|
|
31
|
+
"./locale/*.json": "./locale/*.json",
|
|
32
|
+
"./icon": "./icon.svg",
|
|
33
|
+
"./package.json": "./package.json"
|
|
34
|
+
},
|
|
35
|
+
"files": [
|
|
36
|
+
"lib",
|
|
37
|
+
"bundle",
|
|
38
|
+
"locale",
|
|
39
|
+
"icon.svg",
|
|
40
|
+
"scripts"
|
|
22
41
|
],
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
|
|
42
|
+
"dsh": {
|
|
43
|
+
"bundle": {
|
|
44
|
+
"patch": "./bundle/cordis.patch.yml"
|
|
45
|
+
},
|
|
46
|
+
"client": {
|
|
47
|
+
"platform": "web",
|
|
48
|
+
"inject": [
|
|
49
|
+
"@deepseek-ai/dsh-client-ui-plugin-manager",
|
|
50
|
+
"@deepseek-ai/dsh-client-ui-settings",
|
|
51
|
+
"@deepseek-ai/dsh-client-ui-settings-models",
|
|
52
|
+
"@deepseek-ai/dsh-client-locale",
|
|
53
|
+
"@deepseek-ai/dsh-client-connection"
|
|
54
|
+
]
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"scripts": {
|
|
58
|
+
"typecheck": "tsc --noEmit && tsc -p tsconfig.client.json --noEmit && tsc -p tsconfig.spec.json --noEmit",
|
|
59
|
+
"build": "tsc -p tsconfig.build.json && tsc -p tsconfig.client.json && node scripts/build-client.mjs && node scripts/verify-dist.mjs",
|
|
60
|
+
"build:client": "tsc -p tsconfig.client.json && node scripts/build-client.mjs",
|
|
61
|
+
"test": "vitest run",
|
|
62
|
+
"release:prepare": "node scripts/prepare-release.mjs",
|
|
63
|
+
"prepare": "pnpm run build",
|
|
64
|
+
"prepack": "pnpm run build"
|
|
65
|
+
},
|
|
66
|
+
"engines": {
|
|
67
|
+
"node": "^22.19 || >=24"
|
|
68
|
+
},
|
|
69
|
+
"peerDependencies": {
|
|
70
|
+
"@deepseek-ai/cordis": "^4.0.5",
|
|
71
|
+
"@deepseek-ai/dsh-client-connection": "^0.2.1-rc.2",
|
|
72
|
+
"@deepseek-ai/dsh-client-locale": "^0.2.1-rc.2",
|
|
73
|
+
"@deepseek-ai/dsh-client-ui-plugin-manager": "^0.2.1-rc.2",
|
|
74
|
+
"@deepseek-ai/dsh-client-ui-settings": "^0.2.1-rc.2",
|
|
75
|
+
"@deepseek-ai/dsh-client-ui-settings-models": "^0.2.1-rc.2",
|
|
76
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.2.1-rc.2",
|
|
77
|
+
"@deepseek-ai/dsh-credentials": "^0.2.1-rc.2",
|
|
78
|
+
"@deepseek-ai/dsh-llm": "^0.2.1-rc.2",
|
|
79
|
+
"@deepseek-ai/dsh-settings": "^0.2.1-rc.2",
|
|
80
|
+
"@deepseek-ai/dsh-typert-registry": "^0.2.1-rc.2",
|
|
81
|
+
"react": "^18.2.0"
|
|
82
|
+
},
|
|
83
|
+
"devDependencies": {
|
|
84
|
+
"@deepseek-ai/cordis": "^4.0.5",
|
|
85
|
+
"@types/node": "^22.20.0",
|
|
86
|
+
"@types/react": "~18.3.31",
|
|
87
|
+
"esbuild": "^0.28.2",
|
|
88
|
+
"react": "^18.3.1",
|
|
89
|
+
"typescript": "^5.9.3",
|
|
90
|
+
"vitest": "^4.1.8"
|
|
26
91
|
}
|
|
27
92
|
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bundle the browser half into the module-loader artifact the DSH Web client
|
|
3
|
+
* executes.
|
|
4
|
+
*
|
|
5
|
+
* The source is a normal ESM TypeScript entry; esbuild emits CommonJS and the
|
|
6
|
+
* banner/footer wrap it in the loader's closure factory. React stays external
|
|
7
|
+
* because the browser module table provides it. Host and DSH packages must
|
|
8
|
+
* never appear as runtime imports: the Client half is rendered inside the
|
|
9
|
+
* harness, so only React is loaded from the module table.
|
|
10
|
+
*/
|
|
11
|
+
import { build } from 'esbuild'
|
|
12
|
+
import { readFile } from 'node:fs/promises'
|
|
13
|
+
|
|
14
|
+
const ID = 'dsh-keys-manager'
|
|
15
|
+
const OUT = 'lib/client.js'
|
|
16
|
+
|
|
17
|
+
const result = await build({
|
|
18
|
+
entryPoints: ['src/client/index.ts'],
|
|
19
|
+
outfile: OUT,
|
|
20
|
+
bundle: true,
|
|
21
|
+
format: 'cjs',
|
|
22
|
+
platform: 'browser',
|
|
23
|
+
target: 'es2020',
|
|
24
|
+
external: ['react', 'react/jsx-runtime'],
|
|
25
|
+
sourcemap: false,
|
|
26
|
+
logLevel: 'warning',
|
|
27
|
+
banner: {
|
|
28
|
+
js: [
|
|
29
|
+
'/* Keys Manager Client bundle; generated by scripts/build-client.mjs. */',
|
|
30
|
+
`window.__ModuleLoader__.load({ id: ${JSON.stringify(ID)}, factory: (require) => {`,
|
|
31
|
+
'var module = { exports: {} }; var exports = module.exports;',
|
|
32
|
+
].join('\n'),
|
|
33
|
+
},
|
|
34
|
+
footer: {
|
|
35
|
+
js: 'return module.exports; } });',
|
|
36
|
+
},
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
if (result.errors.length > 0) {
|
|
40
|
+
throw new Error(`client bundle build failed:\n${result.errors.map(error => error.text).join('\n')}`)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const text = await readFile(OUT, 'utf8')
|
|
44
|
+
if (!text.includes(`window.__ModuleLoader__.load({ id: ${JSON.stringify(ID)}`)) {
|
|
45
|
+
throw new Error('client bundle is missing the module-loader handoff')
|
|
46
|
+
}
|
|
47
|
+
if (text.includes('import.meta')) {
|
|
48
|
+
throw new Error('client bundle must not contain import.meta; the loader executes it as a classic script')
|
|
49
|
+
}
|
|
50
|
+
if (/require\(["']@deepseek-ai\//.test(text)) {
|
|
51
|
+
throw new Error('client bundle must not require @deepseek-ai packages at runtime')
|
|
52
|
+
}
|
|
53
|
+
console.log(`built ${OUT} (${Buffer.byteLength(text)} bytes)`)
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Release-prep version helper (plan dsh-keys-manager-ci-release, task 2 + 4):
|
|
4
|
+
* resolves the target version — explicit `X.Y.Z` / `X.Y.Z-prerelease`
|
|
5
|
+
* argument (e.g. 0.1.1-alpha.1) or an auto patch bump from package.json —
|
|
6
|
+
* refuses already-released versions (existing `v<version>` git tag), aborts
|
|
7
|
+
* when there are no commits since the previous release tag (nothing to
|
|
8
|
+
* release), bumps package.json, inserts the `## [<version>]` section into
|
|
9
|
+
* CHANGELOG.md (from the same git-log notes the workflows use; keeps
|
|
10
|
+
* existing sections, no-op when already present), and prints
|
|
11
|
+
* `VERSION=<version>` for the workflow to capture. Node built-ins only, no
|
|
12
|
+
* new dependencies.
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* node scripts/prepare-release.mjs # auto patch bump
|
|
16
|
+
* node scripts/prepare-release.mjs 0.2.0 # explicit version
|
|
17
|
+
* node scripts/prepare-release.mjs 0.1.1-alpha.1 # explicit prerelease
|
|
18
|
+
*
|
|
19
|
+
* Exit codes: 0 = bumped and printed VERSION; 1 = rejected (unparseable
|
|
20
|
+
* version, existing tag, no commits since the previous release tag,
|
|
21
|
+
* unparseable current package.json version, or CHANGELOG.md write error).
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { execFileSync } from 'node:child_process'
|
|
25
|
+
import { readFileSync, writeFileSync } from 'node:fs'
|
|
26
|
+
import { dirname, join } from 'node:path'
|
|
27
|
+
import { fileURLToPath } from 'node:url'
|
|
28
|
+
|
|
29
|
+
const REPO_ROOT = join(dirname(fileURLToPath(import.meta.url)), '..')
|
|
30
|
+
const PKG_PATH = join(REPO_ROOT, 'package.json')
|
|
31
|
+
const CHANGELOG_PATH = join(REPO_ROOT, 'CHANGELOG.md')
|
|
32
|
+
const CHANGELOG_HEADER =
|
|
33
|
+
'# Changelog\n\nAll notable changes to dsh-keys-manager are documented here. Generated from git log by the release-prep workflow.\n'
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Parse X.Y.Z or X.Y.Z[-prerelease] (numeric parts; optional prerelease
|
|
37
|
+
* suffix); null when not parseable. The suffix character set matches the
|
|
38
|
+
* workflows' guard regex `[0-9A-Za-z.-]+` (keep-simple: no leading-zero or
|
|
39
|
+
* empty-identifier rules), with one tightening: the suffix MUST contain at
|
|
40
|
+
* least one digit, so `1.2.3-alpha.1` / `1.2.3-rc.2` parse but `1.2.3-alpha`
|
|
41
|
+
* (no numeric identifier) and `1.2.3-` (empty suffix) are rejected. Returns
|
|
42
|
+
* { major, minor, patch, prerelease? } — prerelease is the raw suffix text
|
|
43
|
+
* (e.g. 'alpha.1') when present, undefined for formal versions.
|
|
44
|
+
*/
|
|
45
|
+
function parseVersion(value) {
|
|
46
|
+
const match = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]*[0-9][0-9A-Za-z.-]*))?$/.exec(value)
|
|
47
|
+
if (!match) return null
|
|
48
|
+
return {
|
|
49
|
+
major: Number(match[1]),
|
|
50
|
+
minor: Number(match[2]),
|
|
51
|
+
patch: Number(match[3]),
|
|
52
|
+
prerelease: match[4] === undefined ? undefined : match[4],
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const pkg = JSON.parse(readFileSync(PKG_PATH, 'utf8'))
|
|
57
|
+
|
|
58
|
+
let target
|
|
59
|
+
if (process.argv[2] !== undefined) {
|
|
60
|
+
target = parseVersion(process.argv[2])
|
|
61
|
+
if (target === null) {
|
|
62
|
+
console.error(`Error: invalid version "${process.argv[2]}". Expected X.Y.Z or X.Y.Z-prerelease (e.g. 0.2.0, 0.1.1-alpha.1).`)
|
|
63
|
+
process.exit(1)
|
|
64
|
+
}
|
|
65
|
+
} else {
|
|
66
|
+
// Auto patch bump (mirrors sibling dsh-llm-fallbacks autoBumpPatch): parse
|
|
67
|
+
// the current version as X.Y.Z or X.Y.Z-pre, then:
|
|
68
|
+
// - formal version X.Y.Z -> X.Y.(Z+1) (0.1.2 -> 0.1.3)
|
|
69
|
+
// - prerelease with numeric tail -> bump only the tail, staying on the
|
|
70
|
+
// prerelease line (0.1.3-alpha.3 -> 0.1.3-alpha.4)
|
|
71
|
+
// - prerelease with non-numeric tail (e.g. 0.1.0-alpha) -> cannot auto
|
|
72
|
+
// bump; require an explicit version argument.
|
|
73
|
+
// The looser regex is deliberate: parseVersion (explicit args) requires a
|
|
74
|
+
// digit in the suffix, so it cannot tell "alpha" from a malformed version;
|
|
75
|
+
// this path needs the raw suffix to reject non-numeric tails with the
|
|
76
|
+
// explicit-version hint.
|
|
77
|
+
const m = /^(\d+)\.(\d+)\.(\d+)(?:-(.+))?$/.exec(pkg.version)
|
|
78
|
+
if (!m) {
|
|
79
|
+
console.error(`Error: cannot parse current package.json version "${pkg.version}".`)
|
|
80
|
+
process.exit(1)
|
|
81
|
+
}
|
|
82
|
+
const [, major, minor, patch, pre] = m
|
|
83
|
+
if (pre === undefined) {
|
|
84
|
+
target = { major: Number(major), minor: Number(minor), patch: Number(patch) + 1 }
|
|
85
|
+
} else {
|
|
86
|
+
const tail = pre.split('.').at(-1)
|
|
87
|
+
if (tail === undefined || !/^\d+$/.test(tail)) {
|
|
88
|
+
console.error(
|
|
89
|
+
`Error: cannot auto-bump "${pkg.version}": prerelease tail "${pre}" is not numeric. ` +
|
|
90
|
+
'Pass an explicit version instead (e.g. `node scripts/prepare-release.mjs 0.1.1-alpha.1`).',
|
|
91
|
+
)
|
|
92
|
+
process.exit(1)
|
|
93
|
+
}
|
|
94
|
+
const prefix = pre.slice(0, pre.length - tail.length) // "alpha." for "alpha.1"
|
|
95
|
+
target = {
|
|
96
|
+
major: Number(major),
|
|
97
|
+
minor: Number(minor),
|
|
98
|
+
patch: Number(patch),
|
|
99
|
+
prerelease: `${prefix}${Number(tail) + 1}`,
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const version =
|
|
105
|
+
target.prerelease === undefined
|
|
106
|
+
? `${target.major}.${target.minor}.${target.patch}`
|
|
107
|
+
: `${target.major}.${target.minor}.${target.patch}-${target.prerelease}`
|
|
108
|
+
|
|
109
|
+
// Validate the npm dist-tag derived by the release workflow. A prerelease
|
|
110
|
+
// identifier like "latest" would move the stable tag, and a numeric one like
|
|
111
|
+
// "1" is not a legal npm tag. Reject both before the release PR is opened.
|
|
112
|
+
if (target.prerelease !== undefined) {
|
|
113
|
+
const distTag = target.prerelease.split('.')[0]
|
|
114
|
+
if (distTag === 'latest' || !/^[a-z][a-z0-9-]*$/.test(distTag)) {
|
|
115
|
+
console.error(
|
|
116
|
+
`Error: prerelease "${target.prerelease}" maps to invalid npm dist-tag "${distTag}". ` +
|
|
117
|
+
'Use a prerelease such as alpha.1, beta.1, or rc.1.',
|
|
118
|
+
)
|
|
119
|
+
process.exit(1)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Reject already-released versions: `git rev-parse v<version>` succeeds iff
|
|
124
|
+
// the tag exists. Works unchanged for prerelease tags (v0.1.1-alpha.1 is a
|
|
125
|
+
// plain tag name). Run from REPO_ROOT so the check is cwd-independent.
|
|
126
|
+
try {
|
|
127
|
+
execFileSync('git', ['rev-parse', `v${version}`], { cwd: REPO_ROOT, stdio: 'ignore' })
|
|
128
|
+
console.error(`Error: tag v${version} already exists — this version is already released.`)
|
|
129
|
+
process.exit(1)
|
|
130
|
+
} catch {
|
|
131
|
+
// Tag absent → OK to prepare.
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Fail fast on a no-change release: an empty notes range means no commits
|
|
135
|
+
// since the previous release tag — prepare-release must not bump the version
|
|
136
|
+
// or touch CHANGELOG.md for an empty release (no empty release PR). First-
|
|
137
|
+
// ever releases (no ancestor tag yet) span the full first-parent history,
|
|
138
|
+
// which is non-empty by construction, so they still pass.
|
|
139
|
+
const { prevTag, notes } = resolveReleaseNotes()
|
|
140
|
+
if (notes === '') {
|
|
141
|
+
console.error(
|
|
142
|
+
`Error: no commits since previous release tag ${prevTag === '' ? '<first commit>' : prevTag} — nothing to release. Aborting.`,
|
|
143
|
+
)
|
|
144
|
+
process.exit(1)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
pkg.version = version
|
|
148
|
+
// Preserve repo formatting: 2-space indent + trailing newline. The version
|
|
149
|
+
// line must be the only diff (verified by `git diff package.json` in the
|
|
150
|
+
// dry-run validation).
|
|
151
|
+
writeFileSync(PKG_PATH, `${JSON.stringify(pkg, null, 2)}\n`, 'utf8')
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Resolve the previous release tag (nearest ancestor tag of HEAD; empty
|
|
155
|
+
* when none exists yet) and the first-parent commit subjects since that
|
|
156
|
+
* tag (full first-parent history when no tag exists). Same git-log snippet
|
|
157
|
+
* the workflows use for the PR body — the CHANGELOG section and the PR body
|
|
158
|
+
* stay consistent. Do NOT use "v$VERSION^": the version string is not a
|
|
159
|
+
* git ref — the bump commit is not tagged yet.
|
|
160
|
+
*/
|
|
161
|
+
function resolveReleaseNotes() {
|
|
162
|
+
let prevTag = ''
|
|
163
|
+
try {
|
|
164
|
+
prevTag = execFileSync('git', ['describe', '--tags', '--abbrev=0', 'HEAD'], {
|
|
165
|
+
cwd: REPO_ROOT,
|
|
166
|
+
encoding: 'utf8',
|
|
167
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
168
|
+
}).trim()
|
|
169
|
+
} catch {
|
|
170
|
+
// No ancestor tag yet → full history.
|
|
171
|
+
}
|
|
172
|
+
const range = prevTag === '' ? 'HEAD' : `${prevTag}..HEAD`
|
|
173
|
+
const notes = execFileSync('git', ['log', '--oneline', '--first-parent', range], {
|
|
174
|
+
cwd: REPO_ROOT,
|
|
175
|
+
encoding: 'utf8',
|
|
176
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
177
|
+
}).trim()
|
|
178
|
+
return { prevTag, notes }
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Insert a `## [<version>] - <YYYY-MM-DD>` section under the `# Changelog`
|
|
183
|
+
* header, above the existing sections, from the pre-collected git-log notes
|
|
184
|
+
* (guaranteed non-empty by the no-change guard, so no fallback text is ever
|
|
185
|
+
* written). Preserves all existing sections; no-op when the section for this
|
|
186
|
+
* version already exists (idempotent re-runs must not duplicate it); write
|
|
187
|
+
* failures exit 1.
|
|
188
|
+
*/
|
|
189
|
+
function updateChangelog(version, notes) {
|
|
190
|
+
let content
|
|
191
|
+
try {
|
|
192
|
+
content = readFileSync(CHANGELOG_PATH, 'utf8')
|
|
193
|
+
} catch {
|
|
194
|
+
content = CHANGELOG_HEADER
|
|
195
|
+
}
|
|
196
|
+
// A 0-byte or whitespace-only CHANGELOG.md (e.g. manual truncation) is
|
|
197
|
+
// treated as missing: such a file must still bootstrap with the standard
|
|
198
|
+
// header, otherwise the new section would land at the top with leading
|
|
199
|
+
// blank lines and no `# Changelog` header.
|
|
200
|
+
if (content.trim() === '') {
|
|
201
|
+
content = CHANGELOG_HEADER
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (content.includes(`## [${version}]`)) {
|
|
205
|
+
return
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const sectionHeader = `## [${version}] - ${new Date().toISOString().slice(0, 10)}`
|
|
209
|
+
const sectionBody = notes
|
|
210
|
+
.split('\n')
|
|
211
|
+
.map((line) => `- ${line}`)
|
|
212
|
+
.join('\n')
|
|
213
|
+
// The section's own trailing `\n` plus the inserted `\n` below form the
|
|
214
|
+
// blank-line separator between the new section and the existing sections.
|
|
215
|
+
const section = `${sectionHeader}\n\n${sectionBody}\n`
|
|
216
|
+
|
|
217
|
+
// Insert on the first `## [` section header via string slice: everything
|
|
218
|
+
// at and below the insertion point — including the file's exact trailing
|
|
219
|
+
// newline — is preserved byte-for-byte. (A split('\n')/splice/join('\n')
|
|
220
|
+
// round-trip would re-normalize line breaks and could alter the tail, e.g.
|
|
221
|
+
// appending a trailing newline that was not in the source file.)
|
|
222
|
+
const firstSectionMatch = /^## \[/m.exec(content)
|
|
223
|
+
const firstSectionIdx = firstSectionMatch === null ? -1 : firstSectionMatch.index
|
|
224
|
+
let next
|
|
225
|
+
if (firstSectionIdx === -1) {
|
|
226
|
+
// No sections yet — append the new section after the header block,
|
|
227
|
+
// keeping the file's own trailing-newline state.
|
|
228
|
+
next = content.endsWith('\n') ? `${content}\n${section}` : `${content}\n\n${section}`
|
|
229
|
+
} else {
|
|
230
|
+
next = `${content.slice(0, firstSectionIdx)}${section}\n${content.slice(firstSectionIdx)}`
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
try {
|
|
234
|
+
writeFileSync(CHANGELOG_PATH, next, 'utf8')
|
|
235
|
+
} catch (error) {
|
|
236
|
+
console.error(`Error: cannot write ${CHANGELOG_PATH}: ${error.message}`)
|
|
237
|
+
process.exit(1)
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
updateChangelog(version, notes)
|
|
242
|
+
|
|
243
|
+
console.log(`VERSION=${version}`)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Assert the built package contains every path the bundle manifest and the
|
|
3
|
+
* Web module loader resolve at runtime.
|
|
4
|
+
*/
|
|
5
|
+
import { access, readFile } from 'node:fs/promises'
|
|
6
|
+
|
|
7
|
+
const REQUIRED = [
|
|
8
|
+
'lib/index.js',
|
|
9
|
+
'lib/index.d.ts',
|
|
10
|
+
'lib/client.js',
|
|
11
|
+
'lib/client/index.d.ts',
|
|
12
|
+
'bundle/cordis.patch.yml',
|
|
13
|
+
'locale/en.json',
|
|
14
|
+
'locale/zh.json',
|
|
15
|
+
'icon.svg',
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
for (const path of REQUIRED) {
|
|
19
|
+
try {
|
|
20
|
+
await access(path)
|
|
21
|
+
} catch {
|
|
22
|
+
throw new Error(`missing built artifact: ${path}`)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const client = await readFile('lib/client.js', 'utf8')
|
|
27
|
+
if (!client.includes('dsh-keys-manager')) throw new Error('lib/client.js does not identify the bundle')
|
|
28
|
+
if (!client.includes('__ModuleLoader__')) throw new Error('lib/client.js is not a module-loader bundle')
|
|
29
|
+
|
|
30
|
+
console.log(`verified ${REQUIRED.length} artifacts`)
|
package/index.js
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
// Placeholder stub for the `dsh-keys-manager` package name.
|
|
2
|
-
// The real package ships from https://github.com/btspoony/dsh-keys-manager
|
|
3
|
-
// via GitHub Actions OIDC trusted publishing (.github/workflows/release.yml).
|
|
4
|
-
//
|
|
5
|
-
// 0.0.0 is permanently reserved as the placeholder version: npm never allows
|
|
6
|
-
// republishing the same version, so every real release must use a version
|
|
7
|
-
// greater than 0.0.0.
|
|
8
|
-
//
|
|
9
|
-
// Load-time behavior: warn only, so bundler static analysis and accidental
|
|
10
|
-
// transitive installs never crash at require/import time. Call-time behavior:
|
|
11
|
-
// the exported function throws, so actual usage of this stub fails loudly
|
|
12
|
-
// with an actionable message instead of being silently broken.
|
|
13
|
-
|
|
14
|
-
'use strict';
|
|
15
|
-
|
|
16
|
-
const MESSAGE =
|
|
17
|
-
'dsh-keys-manager@0.0.0 is a placeholder package. Install a real version (> 0.0.0) from https://github.com/btspoony/dsh-keys-manager';
|
|
18
|
-
|
|
19
|
-
// Warn once at load time except in production builds (set
|
|
20
|
-
// DSH_KEYS_MANAGER_WARN=1 to force the warning there too).
|
|
21
|
-
if (process.env.NODE_ENV !== 'production' || process.env.DSH_KEYS_MANAGER_WARN === '1') {
|
|
22
|
-
console.warn(`[dsh-keys-manager] ${MESSAGE}`);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
module.exports = function dshKeysManagerPlaceholder() {
|
|
26
|
-
throw new Error(MESSAGE);
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
// Machine-readable marker so tooling can detect the stub without importing it.
|
|
30
|
-
module.exports.isPlaceholder = true;
|