@genomic/utils 2.0.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 +109 -0
- package/argv.d.ts +13 -0
- package/argv.js +16 -0
- package/cli-error.d.ts +14 -0
- package/cli-error.js +33 -0
- package/esm/argv.d.ts +13 -0
- package/esm/argv.js +12 -0
- package/esm/cli-error.d.ts +14 -0
- package/esm/cli-error.js +29 -0
- package/esm/index.d.ts +8 -0
- package/esm/index.js +8 -0
- package/esm/package-json.d.ts +18 -0
- package/esm/package-json.js +20 -0
- package/esm/update-check.d.ts +18 -0
- package/esm/update-check.js +76 -0
- package/index.d.ts +8 -0
- package/index.js +17 -0
- package/package-json.d.ts +18 -0
- package/package-json.js +26 -0
- package/package.json +41 -0
- package/update-check.d.ts +18 -0
- package/update-check.js +113 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Constructive <developers@constructive.io>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# @genomic/utils
|
|
2
|
+
|
|
3
|
+
CLI lifecycle utilities for building command-line applications.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @genomic/utils
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
### extractFirst
|
|
14
|
+
|
|
15
|
+
Extracts the first positional argument from argv for command routing:
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { extractFirst } from '@genomic/utils';
|
|
19
|
+
|
|
20
|
+
const { first, newArgv } = extractFirst(argv);
|
|
21
|
+
// first = 'init' (the subcommand)
|
|
22
|
+
// newArgv = remaining arguments with first positional removed
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### cliExitWithError
|
|
26
|
+
|
|
27
|
+
Exits the CLI with an error message and optional cleanup:
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { cliExitWithError } from '@genomic/utils';
|
|
31
|
+
|
|
32
|
+
await cliExitWithError(error, {
|
|
33
|
+
beforeExit: async () => {
|
|
34
|
+
await closeConnections();
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### checkForUpdates
|
|
40
|
+
|
|
41
|
+
Checks for package updates with caching:
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
import { checkForUpdates } from '@genomic/utils';
|
|
45
|
+
|
|
46
|
+
const result = await checkForUpdates({
|
|
47
|
+
pkgName: '@my/cli',
|
|
48
|
+
pkgVersion: '1.0.0',
|
|
49
|
+
registryBaseUrl: 'https://registry.npmjs.org'
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
if (result.hasUpdate) {
|
|
53
|
+
console.log(result.message);
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### getSelfPackageJson
|
|
58
|
+
|
|
59
|
+
Gets the package.json for the current package:
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
import { getSelfPackageJson, getSelfVersion, getSelfName } from '@genomic/utils';
|
|
63
|
+
|
|
64
|
+
const pkg = getSelfPackageJson(__dirname);
|
|
65
|
+
const version = getSelfVersion(__dirname);
|
|
66
|
+
const name = getSelfName(__dirname);
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## License
|
|
70
|
+
|
|
71
|
+
MIT
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Development
|
|
76
|
+
|
|
77
|
+
### Setup
|
|
78
|
+
|
|
79
|
+
1. Clone the repository:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
git clone https://github.com/constructive-io/dev-utils.git
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
2. Install dependencies:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
cd dev-utils
|
|
89
|
+
pnpm install
|
|
90
|
+
pnpm build
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
3. Test the package of interest:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
cd packages/<packagename>
|
|
97
|
+
pnpm test:watch
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Credits
|
|
101
|
+
|
|
102
|
+
Built for developers, with developers.
|
|
103
|
+
👉 https://launchql.com | https://hyperweb.io
|
|
104
|
+
|
|
105
|
+
## Disclaimer
|
|
106
|
+
|
|
107
|
+
AS DESCRIBED IN THE LICENSES, THE SOFTWARE IS PROVIDED "AS IS", AT YOUR OWN RISK, AND WITHOUT WARRANTIES OF ANY KIND.
|
|
108
|
+
|
|
109
|
+
No developer or entity involved in creating this software will be liable for any claims or damages whatsoever associated with your use, inability to use, or your interaction with other users of the code, including any direct, indirect, incidental, special, exemplary, punitive or consequential damages, or loss of profits, cryptocurrencies, tokens, or anything else of value.
|
package/argv.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ParsedArgs } from 'minimist';
|
|
2
|
+
/**
|
|
3
|
+
* Extracts the first positional argument from argv and returns it along with the remaining argv.
|
|
4
|
+
* This is useful for command routing in CLI applications where the first argument is a subcommand.
|
|
5
|
+
*/
|
|
6
|
+
export declare const extractFirst: (argv: Partial<ParsedArgs>) => {
|
|
7
|
+
first: string;
|
|
8
|
+
newArgv: {
|
|
9
|
+
_: string[];
|
|
10
|
+
"--"?: string[] | undefined;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
export type { ParsedArgs } from 'minimist';
|
package/argv.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractFirst = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Extracts the first positional argument from argv and returns it along with the remaining argv.
|
|
6
|
+
* This is useful for command routing in CLI applications where the first argument is a subcommand.
|
|
7
|
+
*/
|
|
8
|
+
const extractFirst = (argv) => {
|
|
9
|
+
const first = argv._?.[0];
|
|
10
|
+
const newArgv = {
|
|
11
|
+
...argv,
|
|
12
|
+
_: argv._?.slice(1) ?? []
|
|
13
|
+
};
|
|
14
|
+
return { first, newArgv };
|
|
15
|
+
};
|
|
16
|
+
exports.extractFirst = extractFirst;
|
package/cli-error.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface CliExitOptions {
|
|
2
|
+
context?: Record<string, any>;
|
|
3
|
+
beforeExit?: () => Promise<void>;
|
|
4
|
+
logger?: {
|
|
5
|
+
error: (message: string, ...args: any[]) => void;
|
|
6
|
+
debug: (message: string, ...args: any[]) => void;
|
|
7
|
+
warn: (message: string, ...args: any[]) => void;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Exits the CLI with an error message and optional cleanup.
|
|
12
|
+
* Supports a beforeExit hook for cleanup operations (e.g., closing database connections).
|
|
13
|
+
*/
|
|
14
|
+
export declare const cliExitWithError: (error: Error | string, options?: CliExitOptions) => Promise<never>;
|
package/cli-error.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.cliExitWithError = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Exits the CLI with an error message and optional cleanup.
|
|
6
|
+
* Supports a beforeExit hook for cleanup operations (e.g., closing database connections).
|
|
7
|
+
*/
|
|
8
|
+
const cliExitWithError = async (error, options = {}) => {
|
|
9
|
+
const { context, beforeExit, logger = console } = options;
|
|
10
|
+
if (error instanceof Error) {
|
|
11
|
+
logger.error(`Error: ${error.message}`);
|
|
12
|
+
if (context) {
|
|
13
|
+
logger.debug('Context:', context);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
else if (typeof error === 'string') {
|
|
17
|
+
logger.error(`Error: ${error}`);
|
|
18
|
+
if (context) {
|
|
19
|
+
logger.debug('Context:', context);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
if (beforeExit) {
|
|
23
|
+
try {
|
|
24
|
+
await beforeExit();
|
|
25
|
+
logger.debug('Cleanup completed');
|
|
26
|
+
}
|
|
27
|
+
catch (cleanupError) {
|
|
28
|
+
logger.warn('Failed to complete cleanup:', cleanupError);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
process.exit(1);
|
|
32
|
+
};
|
|
33
|
+
exports.cliExitWithError = cliExitWithError;
|
package/esm/argv.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ParsedArgs } from 'minimist';
|
|
2
|
+
/**
|
|
3
|
+
* Extracts the first positional argument from argv and returns it along with the remaining argv.
|
|
4
|
+
* This is useful for command routing in CLI applications where the first argument is a subcommand.
|
|
5
|
+
*/
|
|
6
|
+
export declare const extractFirst: (argv: Partial<ParsedArgs>) => {
|
|
7
|
+
first: string;
|
|
8
|
+
newArgv: {
|
|
9
|
+
_: string[];
|
|
10
|
+
"--"?: string[] | undefined;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
export type { ParsedArgs } from 'minimist';
|
package/esm/argv.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extracts the first positional argument from argv and returns it along with the remaining argv.
|
|
3
|
+
* This is useful for command routing in CLI applications where the first argument is a subcommand.
|
|
4
|
+
*/
|
|
5
|
+
export const extractFirst = (argv) => {
|
|
6
|
+
const first = argv._?.[0];
|
|
7
|
+
const newArgv = {
|
|
8
|
+
...argv,
|
|
9
|
+
_: argv._?.slice(1) ?? []
|
|
10
|
+
};
|
|
11
|
+
return { first, newArgv };
|
|
12
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface CliExitOptions {
|
|
2
|
+
context?: Record<string, any>;
|
|
3
|
+
beforeExit?: () => Promise<void>;
|
|
4
|
+
logger?: {
|
|
5
|
+
error: (message: string, ...args: any[]) => void;
|
|
6
|
+
debug: (message: string, ...args: any[]) => void;
|
|
7
|
+
warn: (message: string, ...args: any[]) => void;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Exits the CLI with an error message and optional cleanup.
|
|
12
|
+
* Supports a beforeExit hook for cleanup operations (e.g., closing database connections).
|
|
13
|
+
*/
|
|
14
|
+
export declare const cliExitWithError: (error: Error | string, options?: CliExitOptions) => Promise<never>;
|
package/esm/cli-error.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exits the CLI with an error message and optional cleanup.
|
|
3
|
+
* Supports a beforeExit hook for cleanup operations (e.g., closing database connections).
|
|
4
|
+
*/
|
|
5
|
+
export const cliExitWithError = async (error, options = {}) => {
|
|
6
|
+
const { context, beforeExit, logger = console } = options;
|
|
7
|
+
if (error instanceof Error) {
|
|
8
|
+
logger.error(`Error: ${error.message}`);
|
|
9
|
+
if (context) {
|
|
10
|
+
logger.debug('Context:', context);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
else if (typeof error === 'string') {
|
|
14
|
+
logger.error(`Error: ${error}`);
|
|
15
|
+
if (context) {
|
|
16
|
+
logger.debug('Context:', context);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
if (beforeExit) {
|
|
20
|
+
try {
|
|
21
|
+
await beforeExit();
|
|
22
|
+
logger.debug('Cleanup completed');
|
|
23
|
+
}
|
|
24
|
+
catch (cleanupError) {
|
|
25
|
+
logger.warn('Failed to complete cleanup:', cleanupError);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
process.exit(1);
|
|
29
|
+
};
|
package/esm/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { extractFirst } from './argv';
|
|
2
|
+
export type { ParsedArgs } from './argv';
|
|
3
|
+
export { cliExitWithError } from './cli-error';
|
|
4
|
+
export type { CliExitOptions } from './cli-error';
|
|
5
|
+
export { checkForUpdates } from './update-check';
|
|
6
|
+
export type { UpdateCheckOptions, UpdateCheckResult } from './update-check';
|
|
7
|
+
export { getSelfPackageJson, getSelfVersion, getSelfName } from './package-json';
|
|
8
|
+
export type { PackageJsonInfo } from './package-json';
|
package/esm/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Argv utilities
|
|
2
|
+
export { extractFirst } from './argv';
|
|
3
|
+
// CLI error handling
|
|
4
|
+
export { cliExitWithError } from './cli-error';
|
|
5
|
+
// Update checking
|
|
6
|
+
export { checkForUpdates } from './update-check';
|
|
7
|
+
// Package.json utilities
|
|
8
|
+
export { getSelfPackageJson, getSelfVersion, getSelfName } from './package-json';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface PackageJsonInfo {
|
|
2
|
+
name: string;
|
|
3
|
+
version: string;
|
|
4
|
+
[key: string]: any;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Gets the package.json for the current package by searching up from the given directory.
|
|
8
|
+
* This is useful for CLIs to get their own version information.
|
|
9
|
+
*/
|
|
10
|
+
export declare const getSelfPackageJson: (dirname: string) => PackageJsonInfo;
|
|
11
|
+
/**
|
|
12
|
+
* Gets the version from the package.json for the current package.
|
|
13
|
+
*/
|
|
14
|
+
export declare const getSelfVersion: (dirname: string) => string;
|
|
15
|
+
/**
|
|
16
|
+
* Gets the name from the package.json for the current package.
|
|
17
|
+
*/
|
|
18
|
+
export declare const getSelfName: (dirname: string) => string;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { findAndRequirePackageJson } from 'find-and-require-package-json';
|
|
2
|
+
/**
|
|
3
|
+
* Gets the package.json for the current package by searching up from the given directory.
|
|
4
|
+
* This is useful for CLIs to get their own version information.
|
|
5
|
+
*/
|
|
6
|
+
export const getSelfPackageJson = (dirname) => {
|
|
7
|
+
return findAndRequirePackageJson(dirname);
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Gets the version from the package.json for the current package.
|
|
11
|
+
*/
|
|
12
|
+
export const getSelfVersion = (dirname) => {
|
|
13
|
+
return getSelfPackageJson(dirname).version;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Gets the name from the package.json for the current package.
|
|
17
|
+
*/
|
|
18
|
+
export const getSelfName = (dirname) => {
|
|
19
|
+
return getSelfPackageJson(dirname).name;
|
|
20
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface UpdateCheckOptions {
|
|
2
|
+
pkgName: string;
|
|
3
|
+
pkgVersion: string;
|
|
4
|
+
registryBaseUrl?: string;
|
|
5
|
+
toolName?: string;
|
|
6
|
+
ttl?: number;
|
|
7
|
+
}
|
|
8
|
+
export interface UpdateCheckResult {
|
|
9
|
+
hasUpdate: boolean;
|
|
10
|
+
currentVersion: string;
|
|
11
|
+
latestVersion: string | null;
|
|
12
|
+
message: string | null;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Checks for updates to a package and caches the result.
|
|
16
|
+
* Uses appstash for configuration storage.
|
|
17
|
+
*/
|
|
18
|
+
export declare const checkForUpdates: (options: UpdateCheckOptions) => Promise<UpdateCheckResult>;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { appstash } from 'appstash';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
const DEFAULT_TTL = 24 * 60 * 60 * 1000; // 24 hours
|
|
5
|
+
const DEFAULT_REGISTRY = 'https://registry.npmjs.org';
|
|
6
|
+
/**
|
|
7
|
+
* Checks for updates to a package and caches the result.
|
|
8
|
+
* Uses appstash for configuration storage.
|
|
9
|
+
*/
|
|
10
|
+
export const checkForUpdates = async (options) => {
|
|
11
|
+
const { pkgName, pkgVersion, registryBaseUrl = DEFAULT_REGISTRY, toolName = pkgName, ttl = DEFAULT_TTL } = options;
|
|
12
|
+
const dirs = appstash(toolName);
|
|
13
|
+
const cacheFile = path.join(dirs.cache, 'update-check.json');
|
|
14
|
+
// Check cache first
|
|
15
|
+
try {
|
|
16
|
+
if (fs.existsSync(cacheFile)) {
|
|
17
|
+
const cached = JSON.parse(fs.readFileSync(cacheFile, 'utf-8'));
|
|
18
|
+
if (Date.now() - cached.timestamp < ttl) {
|
|
19
|
+
return {
|
|
20
|
+
hasUpdate: cached.latestVersion !== pkgVersion && cached.latestVersion > pkgVersion,
|
|
21
|
+
currentVersion: pkgVersion,
|
|
22
|
+
latestVersion: cached.latestVersion,
|
|
23
|
+
message: cached.latestVersion > pkgVersion
|
|
24
|
+
? `Update available: ${pkgVersion} -> ${cached.latestVersion}`
|
|
25
|
+
: null
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// Cache read failed, continue to fetch
|
|
32
|
+
}
|
|
33
|
+
// Fetch latest version from registry
|
|
34
|
+
try {
|
|
35
|
+
const response = await fetch(`${registryBaseUrl}/${pkgName}/latest`);
|
|
36
|
+
if (!response.ok) {
|
|
37
|
+
return {
|
|
38
|
+
hasUpdate: false,
|
|
39
|
+
currentVersion: pkgVersion,
|
|
40
|
+
latestVersion: null,
|
|
41
|
+
message: null
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
const data = await response.json();
|
|
45
|
+
const latestVersion = data.version;
|
|
46
|
+
// Cache the result
|
|
47
|
+
try {
|
|
48
|
+
if (!fs.existsSync(dirs.cache)) {
|
|
49
|
+
fs.mkdirSync(dirs.cache, { recursive: true });
|
|
50
|
+
}
|
|
51
|
+
fs.writeFileSync(cacheFile, JSON.stringify({
|
|
52
|
+
latestVersion,
|
|
53
|
+
timestamp: Date.now()
|
|
54
|
+
}));
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// Cache write failed, continue anyway
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
hasUpdate: latestVersion !== pkgVersion && latestVersion > pkgVersion,
|
|
61
|
+
currentVersion: pkgVersion,
|
|
62
|
+
latestVersion,
|
|
63
|
+
message: latestVersion > pkgVersion
|
|
64
|
+
? `Update available: ${pkgVersion} -> ${latestVersion}`
|
|
65
|
+
: null
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return {
|
|
70
|
+
hasUpdate: false,
|
|
71
|
+
currentVersion: pkgVersion,
|
|
72
|
+
latestVersion: null,
|
|
73
|
+
message: null
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
};
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { extractFirst } from './argv';
|
|
2
|
+
export type { ParsedArgs } from './argv';
|
|
3
|
+
export { cliExitWithError } from './cli-error';
|
|
4
|
+
export type { CliExitOptions } from './cli-error';
|
|
5
|
+
export { checkForUpdates } from './update-check';
|
|
6
|
+
export type { UpdateCheckOptions, UpdateCheckResult } from './update-check';
|
|
7
|
+
export { getSelfPackageJson, getSelfVersion, getSelfName } from './package-json';
|
|
8
|
+
export type { PackageJsonInfo } from './package-json';
|
package/index.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getSelfName = exports.getSelfVersion = exports.getSelfPackageJson = exports.checkForUpdates = exports.cliExitWithError = exports.extractFirst = void 0;
|
|
4
|
+
// Argv utilities
|
|
5
|
+
var argv_1 = require("./argv");
|
|
6
|
+
Object.defineProperty(exports, "extractFirst", { enumerable: true, get: function () { return argv_1.extractFirst; } });
|
|
7
|
+
// CLI error handling
|
|
8
|
+
var cli_error_1 = require("./cli-error");
|
|
9
|
+
Object.defineProperty(exports, "cliExitWithError", { enumerable: true, get: function () { return cli_error_1.cliExitWithError; } });
|
|
10
|
+
// Update checking
|
|
11
|
+
var update_check_1 = require("./update-check");
|
|
12
|
+
Object.defineProperty(exports, "checkForUpdates", { enumerable: true, get: function () { return update_check_1.checkForUpdates; } });
|
|
13
|
+
// Package.json utilities
|
|
14
|
+
var package_json_1 = require("./package-json");
|
|
15
|
+
Object.defineProperty(exports, "getSelfPackageJson", { enumerable: true, get: function () { return package_json_1.getSelfPackageJson; } });
|
|
16
|
+
Object.defineProperty(exports, "getSelfVersion", { enumerable: true, get: function () { return package_json_1.getSelfVersion; } });
|
|
17
|
+
Object.defineProperty(exports, "getSelfName", { enumerable: true, get: function () { return package_json_1.getSelfName; } });
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface PackageJsonInfo {
|
|
2
|
+
name: string;
|
|
3
|
+
version: string;
|
|
4
|
+
[key: string]: any;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Gets the package.json for the current package by searching up from the given directory.
|
|
8
|
+
* This is useful for CLIs to get their own version information.
|
|
9
|
+
*/
|
|
10
|
+
export declare const getSelfPackageJson: (dirname: string) => PackageJsonInfo;
|
|
11
|
+
/**
|
|
12
|
+
* Gets the version from the package.json for the current package.
|
|
13
|
+
*/
|
|
14
|
+
export declare const getSelfVersion: (dirname: string) => string;
|
|
15
|
+
/**
|
|
16
|
+
* Gets the name from the package.json for the current package.
|
|
17
|
+
*/
|
|
18
|
+
export declare const getSelfName: (dirname: string) => string;
|
package/package-json.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getSelfName = exports.getSelfVersion = exports.getSelfPackageJson = void 0;
|
|
4
|
+
const find_and_require_package_json_1 = require("find-and-require-package-json");
|
|
5
|
+
/**
|
|
6
|
+
* Gets the package.json for the current package by searching up from the given directory.
|
|
7
|
+
* This is useful for CLIs to get their own version information.
|
|
8
|
+
*/
|
|
9
|
+
const getSelfPackageJson = (dirname) => {
|
|
10
|
+
return (0, find_and_require_package_json_1.findAndRequirePackageJson)(dirname);
|
|
11
|
+
};
|
|
12
|
+
exports.getSelfPackageJson = getSelfPackageJson;
|
|
13
|
+
/**
|
|
14
|
+
* Gets the version from the package.json for the current package.
|
|
15
|
+
*/
|
|
16
|
+
const getSelfVersion = (dirname) => {
|
|
17
|
+
return (0, exports.getSelfPackageJson)(dirname).version;
|
|
18
|
+
};
|
|
19
|
+
exports.getSelfVersion = getSelfVersion;
|
|
20
|
+
/**
|
|
21
|
+
* Gets the name from the package.json for the current package.
|
|
22
|
+
*/
|
|
23
|
+
const getSelfName = (dirname) => {
|
|
24
|
+
return (0, exports.getSelfPackageJson)(dirname).name;
|
|
25
|
+
};
|
|
26
|
+
exports.getSelfName = getSelfName;
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@genomic/utils",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"author": "Constructive <developers@constructive.io>",
|
|
5
|
+
"description": "CLI lifecycle utilities for building command-line applications",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"module": "esm/index.js",
|
|
8
|
+
"types": "index.d.ts",
|
|
9
|
+
"homepage": "https://github.com/constructive-io/dev-utils",
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"publishConfig": {
|
|
12
|
+
"access": "public",
|
|
13
|
+
"directory": "dist"
|
|
14
|
+
},
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "https://github.com/constructive-io/dev-utils"
|
|
18
|
+
},
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://github.com/constructive-io/dev-utils/issues"
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"copy": "makage assets",
|
|
24
|
+
"clean": "makage clean",
|
|
25
|
+
"prepublishOnly": "npm run build",
|
|
26
|
+
"build": "makage build",
|
|
27
|
+
"test": "jest --passWithNoTests",
|
|
28
|
+
"test:watch": "jest --watch"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"appstash": "0.2.6",
|
|
32
|
+
"find-and-require-package-json": "0.8.2",
|
|
33
|
+
"minimist": "^1.2.8"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/minimist": "^1.2.5",
|
|
37
|
+
"makage": "0.1.8"
|
|
38
|
+
},
|
|
39
|
+
"keywords": [],
|
|
40
|
+
"gitHead": "59317e6ae9ba291aebdee3098e456d4ef4e1a5dc"
|
|
41
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface UpdateCheckOptions {
|
|
2
|
+
pkgName: string;
|
|
3
|
+
pkgVersion: string;
|
|
4
|
+
registryBaseUrl?: string;
|
|
5
|
+
toolName?: string;
|
|
6
|
+
ttl?: number;
|
|
7
|
+
}
|
|
8
|
+
export interface UpdateCheckResult {
|
|
9
|
+
hasUpdate: boolean;
|
|
10
|
+
currentVersion: string;
|
|
11
|
+
latestVersion: string | null;
|
|
12
|
+
message: string | null;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Checks for updates to a package and caches the result.
|
|
16
|
+
* Uses appstash for configuration storage.
|
|
17
|
+
*/
|
|
18
|
+
export declare const checkForUpdates: (options: UpdateCheckOptions) => Promise<UpdateCheckResult>;
|
package/update-check.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.checkForUpdates = void 0;
|
|
37
|
+
const appstash_1 = require("appstash");
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
const DEFAULT_TTL = 24 * 60 * 60 * 1000; // 24 hours
|
|
41
|
+
const DEFAULT_REGISTRY = 'https://registry.npmjs.org';
|
|
42
|
+
/**
|
|
43
|
+
* Checks for updates to a package and caches the result.
|
|
44
|
+
* Uses appstash for configuration storage.
|
|
45
|
+
*/
|
|
46
|
+
const checkForUpdates = async (options) => {
|
|
47
|
+
const { pkgName, pkgVersion, registryBaseUrl = DEFAULT_REGISTRY, toolName = pkgName, ttl = DEFAULT_TTL } = options;
|
|
48
|
+
const dirs = (0, appstash_1.appstash)(toolName);
|
|
49
|
+
const cacheFile = path.join(dirs.cache, 'update-check.json');
|
|
50
|
+
// Check cache first
|
|
51
|
+
try {
|
|
52
|
+
if (fs.existsSync(cacheFile)) {
|
|
53
|
+
const cached = JSON.parse(fs.readFileSync(cacheFile, 'utf-8'));
|
|
54
|
+
if (Date.now() - cached.timestamp < ttl) {
|
|
55
|
+
return {
|
|
56
|
+
hasUpdate: cached.latestVersion !== pkgVersion && cached.latestVersion > pkgVersion,
|
|
57
|
+
currentVersion: pkgVersion,
|
|
58
|
+
latestVersion: cached.latestVersion,
|
|
59
|
+
message: cached.latestVersion > pkgVersion
|
|
60
|
+
? `Update available: ${pkgVersion} -> ${cached.latestVersion}`
|
|
61
|
+
: null
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// Cache read failed, continue to fetch
|
|
68
|
+
}
|
|
69
|
+
// Fetch latest version from registry
|
|
70
|
+
try {
|
|
71
|
+
const response = await fetch(`${registryBaseUrl}/${pkgName}/latest`);
|
|
72
|
+
if (!response.ok) {
|
|
73
|
+
return {
|
|
74
|
+
hasUpdate: false,
|
|
75
|
+
currentVersion: pkgVersion,
|
|
76
|
+
latestVersion: null,
|
|
77
|
+
message: null
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
const data = await response.json();
|
|
81
|
+
const latestVersion = data.version;
|
|
82
|
+
// Cache the result
|
|
83
|
+
try {
|
|
84
|
+
if (!fs.existsSync(dirs.cache)) {
|
|
85
|
+
fs.mkdirSync(dirs.cache, { recursive: true });
|
|
86
|
+
}
|
|
87
|
+
fs.writeFileSync(cacheFile, JSON.stringify({
|
|
88
|
+
latestVersion,
|
|
89
|
+
timestamp: Date.now()
|
|
90
|
+
}));
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
// Cache write failed, continue anyway
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
hasUpdate: latestVersion !== pkgVersion && latestVersion > pkgVersion,
|
|
97
|
+
currentVersion: pkgVersion,
|
|
98
|
+
latestVersion,
|
|
99
|
+
message: latestVersion > pkgVersion
|
|
100
|
+
? `Update available: ${pkgVersion} -> ${latestVersion}`
|
|
101
|
+
: null
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return {
|
|
106
|
+
hasUpdate: false,
|
|
107
|
+
currentVersion: pkgVersion,
|
|
108
|
+
latestVersion: null,
|
|
109
|
+
message: null
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
exports.checkForUpdates = checkForUpdates;
|