codiedev 0.7.8 → 0.7.10
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/cli.js +0 -0
- package/dist/connect.js +69 -14
- package/dist/hook.js +0 -0
- package/dist/mcp.js +0 -0
- package/dist/secret.d.ts +1 -0
- package/dist/secret.js +14 -0
- package/package.json +3 -3
- package/dist/__tests__/connectFlow.test.d.ts +0 -1
- package/dist/__tests__/connectFlow.test.js +0 -33
- package/dist/__tests__/detection.test.d.ts +0 -1
- package/dist/__tests__/detection.test.js +0 -18
- package/dist/__tests__/prUrl.test.d.ts +0 -1
- package/dist/__tests__/prUrl.test.js +0 -50
- package/dist/__tests__/repoResolver.test.d.ts +0 -1
- package/dist/__tests__/repoResolver.test.js +0 -72
- package/dist/__tests__/scope.test.d.ts +0 -1
- package/dist/__tests__/scope.test.js +0 -24
- package/dist/__tests__/versionCheck.test.d.ts +0 -1
- package/dist/__tests__/versionCheck.test.js +0 -85
package/dist/cli.js
CHANGED
|
File without changes
|
package/dist/connect.js
CHANGED
|
@@ -42,11 +42,73 @@ const connectFlow_1 = require("./connectFlow");
|
|
|
42
42
|
const version_1 = require("./version");
|
|
43
43
|
const upgradeNudge_1 = require("./upgradeNudge");
|
|
44
44
|
const BACKEND_URL = process.env.CODIEDEV_URL || "https://judicious-falcon-861.convex.site";
|
|
45
|
-
|
|
45
|
+
/**
|
|
46
|
+
* Reads a line from stdin without echoing the typed/pasted characters —
|
|
47
|
+
* for tokens, passwords, anything that shouldn't end up in terminal
|
|
48
|
+
* scrollback or screenshots. Echoes `*` per character so the user gets
|
|
49
|
+
* visual feedback that input is being received.
|
|
50
|
+
*
|
|
51
|
+
* Falls back to the regular `prompt()` if stdin isn't a TTY (e.g., piped
|
|
52
|
+
* input in CI). Without a TTY there's no echo to suppress anyway, and
|
|
53
|
+
* raw mode would error.
|
|
54
|
+
*/
|
|
55
|
+
function promptHidden(question) {
|
|
46
56
|
return new Promise((resolve) => {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
57
|
+
const stdin = process.stdin;
|
|
58
|
+
const stdout = process.stdout;
|
|
59
|
+
if (!stdin.isTTY) {
|
|
60
|
+
// CI / piped input — no terminal to mute. Fall back to readline.
|
|
61
|
+
const rl = readline.createInterface({ input: stdin, output: stdout });
|
|
62
|
+
rl.question(question, (answer) => {
|
|
63
|
+
rl.close();
|
|
64
|
+
resolve(answer.trim());
|
|
65
|
+
});
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
stdout.write(question);
|
|
69
|
+
let buf = "";
|
|
70
|
+
const cleanup = () => {
|
|
71
|
+
stdin.removeListener("data", onData);
|
|
72
|
+
stdin.setRawMode(false);
|
|
73
|
+
stdin.pause();
|
|
74
|
+
};
|
|
75
|
+
const onData = (chunk) => {
|
|
76
|
+
const key = chunk.toString("utf8");
|
|
77
|
+
// Iterate so a single `chunk` containing a multi-char paste still
|
|
78
|
+
// masks each character individually.
|
|
79
|
+
for (const ch of key) {
|
|
80
|
+
// Ctrl+C — abort like a normal terminal interrupt.
|
|
81
|
+
if (ch === "") {
|
|
82
|
+
cleanup();
|
|
83
|
+
stdout.write("\n");
|
|
84
|
+
process.exit(130);
|
|
85
|
+
}
|
|
86
|
+
// Enter — finalize.
|
|
87
|
+
if (ch === "\r" || ch === "\n") {
|
|
88
|
+
cleanup();
|
|
89
|
+
stdout.write("\n");
|
|
90
|
+
resolve(buf.trim());
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
// Backspace / DEL — erase one masked char.
|
|
94
|
+
if (ch === "" || ch === "\b") {
|
|
95
|
+
if (buf.length > 0) {
|
|
96
|
+
buf = buf.slice(0, -1);
|
|
97
|
+
stdout.write("\b \b");
|
|
98
|
+
}
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
// Ignore other control bytes (escape sequences from arrow keys, etc.)
|
|
102
|
+
if (ch.charCodeAt(0) < 0x20)
|
|
103
|
+
continue;
|
|
104
|
+
buf += ch;
|
|
105
|
+
stdout.write("*");
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
stdin.setRawMode(true);
|
|
109
|
+
stdin.resume();
|
|
110
|
+
stdin.setEncoding("utf8");
|
|
111
|
+
stdin.on("data", onData);
|
|
50
112
|
});
|
|
51
113
|
}
|
|
52
114
|
function postJson(url, body) {
|
|
@@ -109,16 +171,9 @@ async function runConnect(args = []) {
|
|
|
109
171
|
console.log(`Reusing API token from ~/.codiedev/config.json (run with --force to use a new one).\n`);
|
|
110
172
|
}
|
|
111
173
|
else {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
});
|
|
116
|
-
try {
|
|
117
|
-
token = await prompt(rl, "Enter your CodieDev API token: ");
|
|
118
|
-
}
|
|
119
|
-
finally {
|
|
120
|
-
rl.close();
|
|
121
|
-
}
|
|
174
|
+
// Hidden prompt — masks the token with `*` as it's typed/pasted so it
|
|
175
|
+
// doesn't end up in terminal scrollback or screenshots.
|
|
176
|
+
token = await promptHidden("Enter your CodieDev API token: ");
|
|
122
177
|
if (!token) {
|
|
123
178
|
console.error("Error: API token is required.");
|
|
124
179
|
process.exit(1);
|
package/dist/hook.js
CHANGED
|
File without changes
|
package/dist/mcp.js
CHANGED
|
File without changes
|
package/dist/secret.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function maskToken(token: string | null | undefined): string;
|
package/dist/secret.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Display helpers for credential-shaped strings. Use anywhere a token /
|
|
3
|
+
// secret might end up in user-visible output (logs, success lines, error
|
|
4
|
+
// messages) so a stray `console.log` can't leak the full value.
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.maskToken = maskToken;
|
|
7
|
+
const PREFIX_LEN = 12;
|
|
8
|
+
function maskToken(token) {
|
|
9
|
+
if (typeof token !== "string" || token.length === 0)
|
|
10
|
+
return "";
|
|
11
|
+
if (token.length <= PREFIX_LEN)
|
|
12
|
+
return token;
|
|
13
|
+
return `${token.slice(0, PREFIX_LEN)}...`;
|
|
14
|
+
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codiedev",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.10",
|
|
4
4
|
"description": "Connect Claude Code, Codex, Cursor, or VS Code Copilot to CodieDev for org-wide session capture and artifact collaboration",
|
|
5
5
|
"bin": {
|
|
6
|
-
"codiedev": "
|
|
7
|
-
"codiedev-hook": "
|
|
6
|
+
"codiedev": "dist/cli.js",
|
|
7
|
+
"codiedev-hook": "dist/hook.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"build": "tsc",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const vitest_1 = require("vitest");
|
|
4
|
-
const connectFlow_1 = require("../connectFlow");
|
|
5
|
-
const SAVED = {
|
|
6
|
-
token: "cdv_savedToken_xxxx",
|
|
7
|
-
companyId: "co_1",
|
|
8
|
-
companyName: "Sonifi",
|
|
9
|
-
repos: [],
|
|
10
|
-
lastSynced: new Date().toISOString(),
|
|
11
|
-
backendUrl: "https://example.test",
|
|
12
|
-
};
|
|
13
|
-
(0, vitest_1.describe)("resolveConnectToken", () => {
|
|
14
|
-
(0, vitest_1.it)("uses saved token when present and force=false", () => {
|
|
15
|
-
const res = (0, connectFlow_1.resolveConnectToken)({ savedConfig: SAVED, force: false });
|
|
16
|
-
(0, vitest_1.expect)(res).toEqual({ source: "reused", token: SAVED.token });
|
|
17
|
-
});
|
|
18
|
-
(0, vitest_1.it)("requires a prompt when no config exists", () => {
|
|
19
|
-
const res = (0, connectFlow_1.resolveConnectToken)({ savedConfig: null, force: false });
|
|
20
|
-
(0, vitest_1.expect)(res).toEqual({ source: "prompt-required" });
|
|
21
|
-
});
|
|
22
|
-
(0, vitest_1.it)("requires a prompt when --force is set, even if a saved token exists", () => {
|
|
23
|
-
const res = (0, connectFlow_1.resolveConnectToken)({ savedConfig: SAVED, force: true });
|
|
24
|
-
(0, vitest_1.expect)(res).toEqual({ source: "prompt-required" });
|
|
25
|
-
});
|
|
26
|
-
(0, vitest_1.it)("requires a prompt when saved config has empty token", () => {
|
|
27
|
-
const res = (0, connectFlow_1.resolveConnectToken)({
|
|
28
|
-
savedConfig: { ...SAVED, token: "" },
|
|
29
|
-
force: false,
|
|
30
|
-
});
|
|
31
|
-
(0, vitest_1.expect)(res).toEqual({ source: "prompt-required" });
|
|
32
|
-
});
|
|
33
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const vitest_1 = require("vitest");
|
|
4
|
-
const detection_1 = require("../detection");
|
|
5
|
-
(0, vitest_1.describe)("detectClaudeCode", () => {
|
|
6
|
-
(0, vitest_1.it)("returns false when neither ~/.claude nor `claude` binary present", () => {
|
|
7
|
-
(0, vitest_1.expect)((0, detection_1.detectClaudeCode)({ dirExists: false, binOnPath: false })).toBe(false);
|
|
8
|
-
});
|
|
9
|
-
(0, vitest_1.it)("returns true when only ~/.claude exists", () => {
|
|
10
|
-
(0, vitest_1.expect)((0, detection_1.detectClaudeCode)({ dirExists: true, binOnPath: false })).toBe(true);
|
|
11
|
-
});
|
|
12
|
-
(0, vitest_1.it)("returns true when only `claude` is on PATH (CC installed but never launched)", () => {
|
|
13
|
-
(0, vitest_1.expect)((0, detection_1.detectClaudeCode)({ dirExists: false, binOnPath: true })).toBe(true);
|
|
14
|
-
});
|
|
15
|
-
(0, vitest_1.it)("returns true when both are present", () => {
|
|
16
|
-
(0, vitest_1.expect)((0, detection_1.detectClaudeCode)({ dirExists: true, binOnPath: true })).toBe(true);
|
|
17
|
-
});
|
|
18
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const vitest_1 = require("vitest");
|
|
4
|
-
const prUrl_1 = require("../prUrl");
|
|
5
|
-
(0, vitest_1.describe)("parsePrOrMrUrl", () => {
|
|
6
|
-
(0, vitest_1.it)("parses a GitHub PR url", () => {
|
|
7
|
-
(0, vitest_1.expect)((0, prUrl_1.parsePrOrMrUrl)("https://github.com/foo/bar/pull/123")).toEqual({
|
|
8
|
-
provider: "github",
|
|
9
|
-
host: "github.com",
|
|
10
|
-
owner: "foo",
|
|
11
|
-
repo: "bar",
|
|
12
|
-
number: 123,
|
|
13
|
-
});
|
|
14
|
-
});
|
|
15
|
-
(0, vitest_1.it)("parses a GitLab.com MR url", () => {
|
|
16
|
-
(0, vitest_1.expect)((0, prUrl_1.parsePrOrMrUrl)("https://gitlab.com/sonifi/platform/-/merge_requests/42")).toEqual({
|
|
17
|
-
provider: "gitlab",
|
|
18
|
-
host: "gitlab.com",
|
|
19
|
-
owner: "sonifi",
|
|
20
|
-
repo: "platform",
|
|
21
|
-
number: 42,
|
|
22
|
-
});
|
|
23
|
-
});
|
|
24
|
-
(0, vitest_1.it)("parses a self-hosted GitLab MR url", () => {
|
|
25
|
-
(0, vitest_1.expect)((0, prUrl_1.parsePrOrMrUrl)("https://git.sonifi.com/internal/platform/-/merge_requests/7")).toEqual({
|
|
26
|
-
provider: "gitlab",
|
|
27
|
-
host: "git.sonifi.com",
|
|
28
|
-
owner: "internal",
|
|
29
|
-
repo: "platform",
|
|
30
|
-
number: 7,
|
|
31
|
-
});
|
|
32
|
-
});
|
|
33
|
-
(0, vitest_1.it)("parses a GitLab MR url with a nested group path", () => {
|
|
34
|
-
(0, vitest_1.expect)((0, prUrl_1.parsePrOrMrUrl)("https://gitlab.com/sonifi/team-a/platform/-/merge_requests/9")).toEqual({
|
|
35
|
-
provider: "gitlab",
|
|
36
|
-
host: "gitlab.com",
|
|
37
|
-
owner: "sonifi/team-a",
|
|
38
|
-
repo: "platform",
|
|
39
|
-
number: 9,
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
(0, vitest_1.it)("returns null for unrecognized URLs", () => {
|
|
43
|
-
(0, vitest_1.expect)((0, prUrl_1.parsePrOrMrUrl)("https://example.com/foo")).toBe(null);
|
|
44
|
-
(0, vitest_1.expect)((0, prUrl_1.parsePrOrMrUrl)("not a url")).toBe(null);
|
|
45
|
-
(0, vitest_1.expect)((0, prUrl_1.parsePrOrMrUrl)("")).toBe(null);
|
|
46
|
-
});
|
|
47
|
-
(0, vitest_1.it)("returns null when the number segment isn't an integer", () => {
|
|
48
|
-
(0, vitest_1.expect)((0, prUrl_1.parsePrOrMrUrl)("https://github.com/foo/bar/pull/abc")).toBe(null);
|
|
49
|
-
});
|
|
50
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const vitest_1 = require("vitest");
|
|
4
|
-
const repoResolver_1 = require("../repoResolver");
|
|
5
|
-
const ghRepo = { _id: "rep_gh1", owner: "Signal-and-Code", name: "vm-demo", gitProvider: "github" };
|
|
6
|
-
const glRepo = { _id: "rep_gl1", owner: "sonifi", name: "platform", gitProvider: "gitlab" };
|
|
7
|
-
(0, vitest_1.describe)("resolveRepoForCapture", () => {
|
|
8
|
-
(0, vitest_1.it)("returns the provided repoId when it matches a current repo", () => {
|
|
9
|
-
const res = (0, repoResolver_1.resolveRepoForCapture)({
|
|
10
|
-
remoteUrl: "https://github.com/Signal-and-Code/vm-demo.git",
|
|
11
|
-
providedRepoId: "rep_gh1",
|
|
12
|
-
companyRepos: [ghRepo],
|
|
13
|
-
});
|
|
14
|
-
(0, vitest_1.expect)(res).toEqual({ repoId: "rep_gh1", source: "provided" });
|
|
15
|
-
});
|
|
16
|
-
(0, vitest_1.it)("falls back to remoteUrl resolution when providedRepoId is stale", () => {
|
|
17
|
-
const res = (0, repoResolver_1.resolveRepoForCapture)({
|
|
18
|
-
remoteUrl: "https://github.com/Signal-and-Code/vm-demo.git",
|
|
19
|
-
providedRepoId: "rep_deleted",
|
|
20
|
-
companyRepos: [ghRepo],
|
|
21
|
-
});
|
|
22
|
-
(0, vitest_1.expect)(res).toEqual({ repoId: "rep_gh1", source: "resolved" });
|
|
23
|
-
});
|
|
24
|
-
(0, vitest_1.it)("resolves from remoteUrl when no providedRepoId is given (HTTPS form)", () => {
|
|
25
|
-
const res = (0, repoResolver_1.resolveRepoForCapture)({
|
|
26
|
-
remoteUrl: "https://github.com/Signal-and-Code/vm-demo.git",
|
|
27
|
-
providedRepoId: null,
|
|
28
|
-
companyRepos: [ghRepo],
|
|
29
|
-
});
|
|
30
|
-
(0, vitest_1.expect)(res).toEqual({ repoId: "rep_gh1", source: "resolved" });
|
|
31
|
-
});
|
|
32
|
-
(0, vitest_1.it)("resolves from remoteUrl when no providedRepoId is given (SSH form)", () => {
|
|
33
|
-
const res = (0, repoResolver_1.resolveRepoForCapture)({
|
|
34
|
-
remoteUrl: "git@github.com:Signal-and-Code/vm-demo.git",
|
|
35
|
-
providedRepoId: null,
|
|
36
|
-
companyRepos: [ghRepo],
|
|
37
|
-
});
|
|
38
|
-
(0, vitest_1.expect)(res).toEqual({ repoId: "rep_gh1", source: "resolved" });
|
|
39
|
-
});
|
|
40
|
-
(0, vitest_1.it)("resolves a GitLab remote against a gitlab-provider repo", () => {
|
|
41
|
-
const res = (0, repoResolver_1.resolveRepoForCapture)({
|
|
42
|
-
remoteUrl: "https://gitlab.com/sonifi/platform.git",
|
|
43
|
-
providedRepoId: null,
|
|
44
|
-
companyRepos: [ghRepo, glRepo],
|
|
45
|
-
});
|
|
46
|
-
(0, vitest_1.expect)(res).toEqual({ repoId: "rep_gl1", source: "resolved" });
|
|
47
|
-
});
|
|
48
|
-
(0, vitest_1.it)("resolves SSH-form GitLab URL", () => {
|
|
49
|
-
const res = (0, repoResolver_1.resolveRepoForCapture)({
|
|
50
|
-
remoteUrl: "git@gitlab.com:sonifi/platform.git",
|
|
51
|
-
providedRepoId: null,
|
|
52
|
-
companyRepos: [glRepo],
|
|
53
|
-
});
|
|
54
|
-
(0, vitest_1.expect)(res).toEqual({ repoId: "rep_gl1", source: "resolved" });
|
|
55
|
-
});
|
|
56
|
-
(0, vitest_1.it)("returns unmatched when remoteUrl matches no repo (don't drop the session — let backend decide)", () => {
|
|
57
|
-
const res = (0, repoResolver_1.resolveRepoForCapture)({
|
|
58
|
-
remoteUrl: "https://github.com/some/other-repo.git",
|
|
59
|
-
providedRepoId: null,
|
|
60
|
-
companyRepos: [ghRepo, glRepo],
|
|
61
|
-
});
|
|
62
|
-
(0, vitest_1.expect)(res).toEqual({ repoId: null, source: "unmatched" });
|
|
63
|
-
});
|
|
64
|
-
(0, vitest_1.it)("matches case-insensitively on owner/name (GitHub is case-insensitive on org/repo names)", () => {
|
|
65
|
-
const res = (0, repoResolver_1.resolveRepoForCapture)({
|
|
66
|
-
remoteUrl: "https://github.com/SIGNAL-AND-CODE/vm-demo.git",
|
|
67
|
-
providedRepoId: null,
|
|
68
|
-
companyRepos: [ghRepo],
|
|
69
|
-
});
|
|
70
|
-
(0, vitest_1.expect)(res).toEqual({ repoId: "rep_gh1", source: "resolved" });
|
|
71
|
-
});
|
|
72
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const vitest_1 = require("vitest");
|
|
4
|
-
const scope_1 = require("../scope");
|
|
5
|
-
(0, vitest_1.describe)("formatScope", () => {
|
|
6
|
-
(0, vitest_1.it)("formats matched-repo case", () => {
|
|
7
|
-
(0, vitest_1.expect)((0, scope_1.formatScope)({
|
|
8
|
-
workspaceName: "Sonifi",
|
|
9
|
-
matchedRepo: { fullName: "Signal-and-Code/codiedev-ea" },
|
|
10
|
-
})).toBe("→ Sonifi (matched Signal-and-Code/codiedev-ea)");
|
|
11
|
-
});
|
|
12
|
-
(0, vitest_1.it)("formats no-match case", () => {
|
|
13
|
-
(0, vitest_1.expect)((0, scope_1.formatScope)({ workspaceName: "Sonifi", matchedRepo: null })).toBe("→ Sonifi (default profile, no repo match)");
|
|
14
|
-
});
|
|
15
|
-
(0, vitest_1.it)("handles unknown workspace gracefully", () => {
|
|
16
|
-
(0, vitest_1.expect)((0, scope_1.formatScope)({ workspaceName: "", matchedRepo: null })).toBe("→ unknown workspace");
|
|
17
|
-
});
|
|
18
|
-
(0, vitest_1.it)("handles unknown workspace with matched repo", () => {
|
|
19
|
-
(0, vitest_1.expect)((0, scope_1.formatScope)({
|
|
20
|
-
workspaceName: "",
|
|
21
|
-
matchedRepo: { fullName: "Foo/Bar" },
|
|
22
|
-
})).toBe("→ unknown workspace");
|
|
23
|
-
});
|
|
24
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const vitest_1 = require("vitest");
|
|
4
|
-
const versionCheck_1 = require("../versionCheck");
|
|
5
|
-
(0, vitest_1.describe)("parseSemver", () => {
|
|
6
|
-
(0, vitest_1.it)("parses a normal version", () => {
|
|
7
|
-
(0, vitest_1.expect)((0, versionCheck_1.parseSemver)("0.7.7")).toEqual({ major: 0, minor: 7, patch: 7 });
|
|
8
|
-
});
|
|
9
|
-
(0, vitest_1.it)("strips a leading v", () => {
|
|
10
|
-
(0, vitest_1.expect)((0, versionCheck_1.parseSemver)("v1.2.3")).toEqual({ major: 1, minor: 2, patch: 3 });
|
|
11
|
-
});
|
|
12
|
-
(0, vitest_1.it)("ignores prerelease/build suffixes", () => {
|
|
13
|
-
(0, vitest_1.expect)((0, versionCheck_1.parseSemver)("1.2.3-beta.1+build.99")).toEqual({
|
|
14
|
-
major: 1,
|
|
15
|
-
minor: 2,
|
|
16
|
-
patch: 3,
|
|
17
|
-
});
|
|
18
|
-
});
|
|
19
|
-
(0, vitest_1.it)("returns null for malformed input", () => {
|
|
20
|
-
(0, vitest_1.expect)((0, versionCheck_1.parseSemver)("")).toBe(null);
|
|
21
|
-
(0, vitest_1.expect)((0, versionCheck_1.parseSemver)("not.a.version")).toBe(null);
|
|
22
|
-
(0, vitest_1.expect)((0, versionCheck_1.parseSemver)("1.2")).toBe(null);
|
|
23
|
-
});
|
|
24
|
-
});
|
|
25
|
-
(0, vitest_1.describe)("compareSemver", () => {
|
|
26
|
-
(0, vitest_1.it)("returns -1 when current is older (patch)", () => {
|
|
27
|
-
(0, vitest_1.expect)((0, versionCheck_1.compareSemver)("0.7.6", "0.7.7")).toBe(-1);
|
|
28
|
-
});
|
|
29
|
-
(0, vitest_1.it)("returns -1 when current is older (minor)", () => {
|
|
30
|
-
(0, vitest_1.expect)((0, versionCheck_1.compareSemver)("0.6.99", "0.7.0")).toBe(-1);
|
|
31
|
-
});
|
|
32
|
-
(0, vitest_1.it)("returns -1 when current is older (major)", () => {
|
|
33
|
-
(0, vitest_1.expect)((0, versionCheck_1.compareSemver)("0.99.99", "1.0.0")).toBe(-1);
|
|
34
|
-
});
|
|
35
|
-
(0, vitest_1.it)("returns 0 when equal", () => {
|
|
36
|
-
(0, vitest_1.expect)((0, versionCheck_1.compareSemver)("0.7.7", "0.7.7")).toBe(0);
|
|
37
|
-
});
|
|
38
|
-
(0, vitest_1.it)("returns 1 when current is ahead of latest (e.g. local dev build)", () => {
|
|
39
|
-
(0, vitest_1.expect)((0, versionCheck_1.compareSemver)("0.8.0", "0.7.7")).toBe(1);
|
|
40
|
-
});
|
|
41
|
-
(0, vitest_1.it)("treats malformed inputs as equal — never warn on garbage", () => {
|
|
42
|
-
(0, vitest_1.expect)((0, versionCheck_1.compareSemver)("garbage", "0.7.7")).toBe(0);
|
|
43
|
-
(0, vitest_1.expect)((0, versionCheck_1.compareSemver)("0.7.7", "garbage")).toBe(0);
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
(0, vitest_1.describe)("shouldShowUpdateWarning", () => {
|
|
47
|
-
const now = 1_777_777_777_000;
|
|
48
|
-
const dayMs = 24 * 60 * 60 * 1000;
|
|
49
|
-
(0, vitest_1.it)("warns when current is older AND cache is fresh", () => {
|
|
50
|
-
(0, vitest_1.expect)((0, versionCheck_1.shouldShowUpdateWarning)({
|
|
51
|
-
current: "0.7.6",
|
|
52
|
-
latest: "0.7.7",
|
|
53
|
-
lastChecked: now - 1000,
|
|
54
|
-
nowMs: now,
|
|
55
|
-
cacheTtlMs: dayMs,
|
|
56
|
-
})).toBe(true);
|
|
57
|
-
});
|
|
58
|
-
(0, vitest_1.it)("does not warn when current matches latest", () => {
|
|
59
|
-
(0, vitest_1.expect)((0, versionCheck_1.shouldShowUpdateWarning)({
|
|
60
|
-
current: "0.7.7",
|
|
61
|
-
latest: "0.7.7",
|
|
62
|
-
lastChecked: now,
|
|
63
|
-
nowMs: now,
|
|
64
|
-
cacheTtlMs: dayMs,
|
|
65
|
-
})).toBe(false);
|
|
66
|
-
});
|
|
67
|
-
(0, vitest_1.it)("does not warn when latest is unknown (no fetch yet)", () => {
|
|
68
|
-
(0, vitest_1.expect)((0, versionCheck_1.shouldShowUpdateWarning)({
|
|
69
|
-
current: "0.7.6",
|
|
70
|
-
latest: null,
|
|
71
|
-
lastChecked: 0,
|
|
72
|
-
nowMs: now,
|
|
73
|
-
cacheTtlMs: dayMs,
|
|
74
|
-
})).toBe(false);
|
|
75
|
-
});
|
|
76
|
-
(0, vitest_1.it)("does not warn when current is ahead of latest (dev build)", () => {
|
|
77
|
-
(0, vitest_1.expect)((0, versionCheck_1.shouldShowUpdateWarning)({
|
|
78
|
-
current: "0.8.0",
|
|
79
|
-
latest: "0.7.7",
|
|
80
|
-
lastChecked: now,
|
|
81
|
-
nowMs: now,
|
|
82
|
-
cacheTtlMs: dayMs,
|
|
83
|
-
})).toBe(false);
|
|
84
|
-
});
|
|
85
|
-
});
|