movehat 0.0.9-alpha.0 → 0.0.10-alpha.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/README.md +1 -1
- package/dist/cli.js +16 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/test-move.d.ts.map +1 -1
- package/dist/commands/test-move.js +10 -40
- package/dist/commands/test-move.js.map +1 -1
- package/dist/commands/test.d.ts.map +1 -1
- package/dist/commands/test.js +36 -51
- package/dist/commands/test.js.map +1 -1
- package/dist/commands/update.d.ts +5 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +121 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/helpers/move-tests.d.ts +13 -0
- package/dist/helpers/move-tests.d.ts.map +1 -0
- package/dist/helpers/move-tests.js +54 -0
- package/dist/helpers/move-tests.js.map +1 -0
- package/dist/helpers/npm-registry.d.ts +19 -0
- package/dist/helpers/npm-registry.d.ts.map +1 -0
- package/dist/helpers/npm-registry.js +47 -0
- package/dist/helpers/npm-registry.js.map +1 -0
- package/dist/helpers/semver-utils.d.ts +7 -0
- package/dist/helpers/semver-utils.d.ts.map +1 -0
- package/dist/helpers/semver-utils.js +47 -0
- package/dist/helpers/semver-utils.js.map +1 -0
- package/dist/helpers/version-check.d.ts +6 -0
- package/dist/helpers/version-check.d.ts.map +1 -0
- package/dist/helpers/version-check.js +85 -0
- package/dist/helpers/version-check.js.map +1 -0
- package/dist/templates/README.md +1 -1
- package/dist/templates/move/Move.toml +3 -2
- package/package.json +1 -1
- package/src/cli.ts +19 -2
- package/src/commands/test-move.ts +10 -45
- package/src/commands/test.ts +37 -56
- package/src/commands/update.ts +148 -0
- package/src/helpers/move-tests.ts +68 -0
- package/src/helpers/npm-registry.ts +71 -0
- package/src/helpers/semver-utils.ts +53 -0
- package/src/helpers/version-check.ts +97 -0
- package/src/templates/README.md +1 -1
- package/src/templates/move/Move.toml +3 -2
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
export interface NpmRegistryResponse {
|
|
2
|
+
"dist-tags": {
|
|
3
|
+
latest: string;
|
|
4
|
+
[tag: string]: string;
|
|
5
|
+
};
|
|
6
|
+
versions: Record<string, unknown>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface FetchOptions {
|
|
10
|
+
timeout?: number; // Timeout in milliseconds
|
|
11
|
+
throwOnError?: boolean; // If true, throw errors; if false, return null on error
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Fetch latest version from npm registry
|
|
16
|
+
* @param packageName - The npm package name
|
|
17
|
+
* @param options - Fetch options (timeout, error handling)
|
|
18
|
+
* @returns Latest version string, or null if failed and throwOnError is false
|
|
19
|
+
*/
|
|
20
|
+
export async function fetchLatestVersion(
|
|
21
|
+
packageName: string,
|
|
22
|
+
options: FetchOptions = {}
|
|
23
|
+
): Promise<string | null> {
|
|
24
|
+
const { timeout = 0, throwOnError = false } = options;
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
const controller = timeout > 0 ? new AbortController() : undefined;
|
|
28
|
+
const timeoutId = timeout > 0 && controller
|
|
29
|
+
? setTimeout(() => controller.abort(), timeout)
|
|
30
|
+
: undefined;
|
|
31
|
+
|
|
32
|
+
const response = await fetch(`https://registry.npmjs.org/${packageName}`, {
|
|
33
|
+
signal: controller?.signal,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
37
|
+
|
|
38
|
+
if (!response.ok) {
|
|
39
|
+
const error = new Error(`Failed to fetch package info: ${response.statusText}`);
|
|
40
|
+
if (throwOnError) throw error;
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const data = (await response.json()) as NpmRegistryResponse;
|
|
45
|
+
|
|
46
|
+
// Defensive validation of registry response structure
|
|
47
|
+
if (
|
|
48
|
+
!data ||
|
|
49
|
+
typeof data !== "object" ||
|
|
50
|
+
!data["dist-tags"] ||
|
|
51
|
+
typeof data["dist-tags"] !== "object" ||
|
|
52
|
+
typeof data["dist-tags"].latest !== "string"
|
|
53
|
+
) {
|
|
54
|
+
const error = new Error(
|
|
55
|
+
`Invalid npm registry response: missing or malformed "dist-tags.latest" field`
|
|
56
|
+
);
|
|
57
|
+
if (throwOnError) throw error;
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return data["dist-tags"].latest;
|
|
62
|
+
} catch (error) {
|
|
63
|
+
if (throwOnError) {
|
|
64
|
+
throw new Error(
|
|
65
|
+
`Failed to check for updates: ${error instanceof Error ? error.message : String(error)}`
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
// Silently fail - don't interrupt user's workflow
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compare two semver versions
|
|
3
|
+
* Returns true if newVersion > currentVersion
|
|
4
|
+
* Handles variable-length versions (1.2, 1.2.3, 1.2.3.4, etc.)
|
|
5
|
+
*/
|
|
6
|
+
export function isNewerVersion(currentVersion: string, newVersion: string): boolean {
|
|
7
|
+
// Remove any pre-release tags (e.g., -alpha.0, -beta.1)
|
|
8
|
+
const cleanCurrent = currentVersion.split("-")[0];
|
|
9
|
+
const cleanNew = newVersion.split("-")[0];
|
|
10
|
+
|
|
11
|
+
// Split and validate numeric parts
|
|
12
|
+
const currentParts = cleanCurrent.split(".").map((part) => {
|
|
13
|
+
const num = Number(part);
|
|
14
|
+
if (isNaN(num) || !part.trim()) {
|
|
15
|
+
throw new Error(`Invalid version format: ${currentVersion}`);
|
|
16
|
+
}
|
|
17
|
+
return num;
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const newerParts = cleanNew.split(".").map((part) => {
|
|
21
|
+
const num = Number(part);
|
|
22
|
+
if (isNaN(num) || !part.trim()) {
|
|
23
|
+
throw new Error(`Invalid version format: ${newVersion}`);
|
|
24
|
+
}
|
|
25
|
+
return num;
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// Compare up to the maximum length, treating missing parts as 0
|
|
29
|
+
const maxLength = Math.max(currentParts.length, newerParts.length);
|
|
30
|
+
|
|
31
|
+
for (let i = 0; i < maxLength; i++) {
|
|
32
|
+
const currentPart = currentParts[i] || 0;
|
|
33
|
+
const newerPart = newerParts[i] || 0;
|
|
34
|
+
|
|
35
|
+
if (newerPart > currentPart) return true;
|
|
36
|
+
if (newerPart < currentPart) return false;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// If base versions are equal, check pre-release tags
|
|
40
|
+
// A version with no pre-release tag is considered newer than one with a tag
|
|
41
|
+
const currentHasPrerelease = currentVersion.includes("-");
|
|
42
|
+
const newHasPrerelease = newVersion.includes("-");
|
|
43
|
+
|
|
44
|
+
if (!currentHasPrerelease && newHasPrerelease) {
|
|
45
|
+
return false; // Current stable is newer than new pre-release
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (currentHasPrerelease && !newHasPrerelease) {
|
|
49
|
+
return true; // New stable is newer than current pre-release
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { homedir } from "os";
|
|
4
|
+
import { isNewerVersion } from "./semver-utils.js";
|
|
5
|
+
import { fetchLatestVersion } from "./npm-registry.js";
|
|
6
|
+
|
|
7
|
+
interface VersionCache {
|
|
8
|
+
lastChecked: number;
|
|
9
|
+
latestVersion: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const CACHE_DURATION = 24 * 60 * 60 * 1000; // 24 hours in milliseconds
|
|
13
|
+
const CACHE_DIR = join(homedir(), ".movehat");
|
|
14
|
+
const CACHE_FILE = join(CACHE_DIR, "version-cache.json");
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Read version from cache
|
|
18
|
+
*/
|
|
19
|
+
function readCache(): VersionCache | null {
|
|
20
|
+
try {
|
|
21
|
+
if (!existsSync(CACHE_FILE)) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const cacheContent = readFileSync(CACHE_FILE, "utf-8");
|
|
26
|
+
return JSON.parse(cacheContent) as VersionCache;
|
|
27
|
+
} catch (error) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Write version to cache
|
|
34
|
+
*/
|
|
35
|
+
function writeCache(latestVersion: string): void {
|
|
36
|
+
try {
|
|
37
|
+
if (!existsSync(CACHE_DIR)) {
|
|
38
|
+
mkdirSync(CACHE_DIR, { recursive: true });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const cache: VersionCache = {
|
|
42
|
+
lastChecked: Date.now(),
|
|
43
|
+
latestVersion,
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
writeFileSync(CACHE_FILE, JSON.stringify(cache, null, 2));
|
|
47
|
+
} catch (error) {
|
|
48
|
+
// Silently fail - don't interrupt user's workflow
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Check if a newer version is available and notify the user
|
|
54
|
+
* This runs synchronously using cache, and updates cache in background
|
|
55
|
+
*/
|
|
56
|
+
export function checkForUpdates(currentVersion: string, packageName: string): void {
|
|
57
|
+
try {
|
|
58
|
+
const cache = readCache();
|
|
59
|
+
let shouldNotify = false;
|
|
60
|
+
|
|
61
|
+
// Check cache synchronously for immediate notification
|
|
62
|
+
if (cache && isNewerVersion(currentVersion, cache.latestVersion)) {
|
|
63
|
+
shouldNotify = true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Display notification immediately if cache says there's an update
|
|
67
|
+
if (shouldNotify && cache) {
|
|
68
|
+
const updateMessage =
|
|
69
|
+
"\n" +
|
|
70
|
+
"┌" + "─".repeat(60) + "┐\n" +
|
|
71
|
+
`│ Update available: ${currentVersion} → ${cache.latestVersion}`.padEnd(61) + "│\n" +
|
|
72
|
+
`│ Run: movehat update`.padEnd(61) + "│\n" +
|
|
73
|
+
"└" + "─".repeat(60) + "┘\n";
|
|
74
|
+
|
|
75
|
+
console.error(updateMessage);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Update cache in background if needed (doesn't block)
|
|
79
|
+
if (!cache || Date.now() - cache.lastChecked > CACHE_DURATION) {
|
|
80
|
+
setImmediate(async () => {
|
|
81
|
+
try {
|
|
82
|
+
const latestVersion = await fetchLatestVersion(packageName, {
|
|
83
|
+
timeout: 2000,
|
|
84
|
+
throwOnError: false,
|
|
85
|
+
});
|
|
86
|
+
if (latestVersion) {
|
|
87
|
+
writeCache(latestVersion);
|
|
88
|
+
}
|
|
89
|
+
} catch (error) {
|
|
90
|
+
// Silently fail
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
} catch (error) {
|
|
95
|
+
// Silently fail - never interrupt user's workflow
|
|
96
|
+
}
|
|
97
|
+
}
|
package/src/templates/README.md
CHANGED
|
@@ -7,8 +7,9 @@ authors = []
|
|
|
7
7
|
counter = "_"
|
|
8
8
|
|
|
9
9
|
[dev-addresses]
|
|
10
|
-
# Dev addresses
|
|
11
|
-
#
|
|
10
|
+
# Dev addresses for testing (used with movement move test --dev)
|
|
11
|
+
# These addresses are temporary and used for local development/testing only
|
|
12
|
+
counter = "0xcafe"
|
|
12
13
|
|
|
13
14
|
[dependencies.AptosFramework]
|
|
14
15
|
git = "https://github.com/movementlabsxyz/aptos-core.git"
|