genomic 0.0.1 → 4.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.
@@ -0,0 +1 @@
1
+ export * from './types';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Built-in date/time resolvers.
3
+ */
4
+ export const dateResolvers = {
5
+ 'date.year': () => new Date().getFullYear().toString(),
6
+ 'date.month': () => (new Date().getMonth() + 1).toString().padStart(2, '0'),
7
+ 'date.day': () => new Date().getDate().toString().padStart(2, '0'),
8
+ 'date.now': () => new Date().toISOString(),
9
+ 'date.iso': () => new Date().toISOString().split('T')[0], // YYYY-MM-DD
10
+ 'date.timestamp': () => Date.now().toString(),
11
+ };
@@ -0,0 +1,26 @@
1
+ import { execSync } from 'child_process';
2
+ /**
3
+ * Retrieves a git config value.
4
+ * @param key The git config key (e.g., 'user.name', 'user.email')
5
+ * @returns The config value as a string, or undefined if not found or error occurs
6
+ */
7
+ export function getGitConfig(key) {
8
+ try {
9
+ const result = execSync(`git config --global ${key}`, {
10
+ encoding: 'utf8',
11
+ stdio: ['pipe', 'pipe', 'ignore'] // Suppress stderr
12
+ });
13
+ const trimmed = result.trim();
14
+ return trimmed || undefined; // Treat empty string as undefined
15
+ }
16
+ catch {
17
+ return undefined;
18
+ }
19
+ }
20
+ /**
21
+ * Built-in git configuration resolvers.
22
+ */
23
+ export const gitResolvers = {
24
+ 'git.user.name': () => getGitConfig('user.name'),
25
+ 'git.user.email': () => getGitConfig('user.email'),
26
+ };
@@ -0,0 +1,103 @@
1
+ import { gitResolvers } from './git';
2
+ import { dateResolvers } from './date';
3
+ import { npmResolvers } from './npm';
4
+ import { workspaceResolvers } from './workspace';
5
+ /**
6
+ * A registry for managing default value resolvers.
7
+ * Allows registration of custom resolvers and provides resolution logic.
8
+ */
9
+ export class DefaultResolverRegistry {
10
+ resolvers;
11
+ constructor(initialResolvers = {}) {
12
+ this.resolvers = { ...initialResolvers };
13
+ }
14
+ /**
15
+ * Register a custom resolver.
16
+ * @param key The resolver key (e.g., 'git.user.name')
17
+ * @param resolver The resolver function
18
+ */
19
+ register(key, resolver) {
20
+ this.resolvers[key] = resolver;
21
+ }
22
+ /**
23
+ * Unregister a resolver.
24
+ * @param key The resolver key to remove
25
+ */
26
+ unregister(key) {
27
+ delete this.resolvers[key];
28
+ }
29
+ /**
30
+ * Resolve a key to its value.
31
+ * Returns undefined if the resolver doesn't exist or if it throws an error.
32
+ * @param key The resolver key
33
+ * @returns The resolved value or undefined
34
+ */
35
+ async resolve(key) {
36
+ const resolver = this.resolvers[key];
37
+ if (!resolver) {
38
+ return undefined;
39
+ }
40
+ try {
41
+ const result = await Promise.resolve(resolver());
42
+ // Treat empty strings as undefined
43
+ return result === '' ? undefined : result;
44
+ }
45
+ catch (error) {
46
+ // Silent failure - log only in debug mode
47
+ if (process.env.DEBUG === 'genomic') {
48
+ console.error(`[genomic] Resolver '${key}' failed:`, error);
49
+ }
50
+ return undefined;
51
+ }
52
+ }
53
+ /**
54
+ * Check if a resolver exists for the given key.
55
+ * @param key The resolver key
56
+ * @returns True if the resolver exists
57
+ */
58
+ has(key) {
59
+ return key in this.resolvers;
60
+ }
61
+ /**
62
+ * Get all registered resolver keys.
63
+ * @returns Array of resolver keys
64
+ */
65
+ keys() {
66
+ return Object.keys(this.resolvers);
67
+ }
68
+ /**
69
+ * Create a copy of this registry with all current resolvers.
70
+ * @returns A new DefaultResolverRegistry instance
71
+ */
72
+ clone() {
73
+ return new DefaultResolverRegistry({ ...this.resolvers });
74
+ }
75
+ }
76
+ /**
77
+ * Global resolver registry instance with built-in resolvers.
78
+ * This is the default registry used by Prompter unless a custom one is provided.
79
+ */
80
+ export const globalResolverRegistry = new DefaultResolverRegistry({
81
+ ...gitResolvers,
82
+ ...dateResolvers,
83
+ ...npmResolvers,
84
+ ...workspaceResolvers,
85
+ });
86
+ /**
87
+ * Convenience function to register a resolver on the global registry.
88
+ * @param key The resolver key
89
+ * @param resolver The resolver function
90
+ */
91
+ export function registerDefaultResolver(key, resolver) {
92
+ globalResolverRegistry.register(key, resolver);
93
+ }
94
+ /**
95
+ * Convenience function to resolve a key using the global registry.
96
+ * @param key The resolver key
97
+ * @returns The resolved value or undefined
98
+ */
99
+ export function resolveDefault(key) {
100
+ return globalResolverRegistry.resolve(key);
101
+ }
102
+ export { getGitConfig } from './git';
103
+ export { getNpmWhoami } from './npm';
@@ -0,0 +1,24 @@
1
+ import { execSync } from 'child_process';
2
+ /**
3
+ * Retrieves the currently logged in npm user.
4
+ * @returns The npm username, or undefined if not logged in or error occurs
5
+ */
6
+ export function getNpmWhoami() {
7
+ try {
8
+ const result = execSync('npm whoami', {
9
+ encoding: 'utf8',
10
+ stdio: ['pipe', 'pipe', 'ignore'] // Suppress stderr
11
+ });
12
+ const trimmed = result.trim();
13
+ return trimmed || undefined; // Treat empty string as undefined
14
+ }
15
+ catch {
16
+ return undefined;
17
+ }
18
+ }
19
+ /**
20
+ * Built-in npm resolvers.
21
+ */
22
+ export const npmResolvers = {
23
+ 'npm.whoami': () => getNpmWhoami(),
24
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,141 @@
1
+ import { findAndRequirePackageJson } from 'find-and-require-package-json';
2
+ /**
3
+ * Find and read the nearest package.json starting from cwd.
4
+ * Returns undefined if not found (instead of throwing).
5
+ */
6
+ function findPackageJsonFromCwd() {
7
+ try {
8
+ return findAndRequirePackageJson(process.cwd());
9
+ }
10
+ catch {
11
+ return undefined;
12
+ }
13
+ }
14
+ /**
15
+ * Parse a GitHub URL and extract organization and repo name.
16
+ * Handles various formats:
17
+ * - https://github.com/org/repo
18
+ * - https://github.com/org/repo.git
19
+ * - git@github.com:org/repo.git
20
+ * - git://github.com/org/repo.git
21
+ */
22
+ function parseGitHubUrl(url) {
23
+ if (!url) {
24
+ return {};
25
+ }
26
+ // Handle git@github.com:org/repo.git format
27
+ const sshMatch = url.match(/git@github\.com:([^/]+)\/([^/.]+)(?:\.git)?/);
28
+ if (sshMatch) {
29
+ return { organization: sshMatch[1], name: sshMatch[2] };
30
+ }
31
+ // Handle https://github.com/org/repo or git://github.com/org/repo formats
32
+ const httpsMatch = url.match(/(?:https?|git):\/\/github\.com\/([^/]+)\/([^/.]+)(?:\.git)?/);
33
+ if (httpsMatch) {
34
+ return { organization: httpsMatch[1], name: httpsMatch[2] };
35
+ }
36
+ return {};
37
+ }
38
+ /**
39
+ * Get the repository URL from package.json.
40
+ * Handles both string and object formats.
41
+ */
42
+ function getRepositoryUrl(pkg) {
43
+ if (!pkg.repository) {
44
+ return undefined;
45
+ }
46
+ if (typeof pkg.repository === 'string') {
47
+ return pkg.repository;
48
+ }
49
+ return pkg.repository.url;
50
+ }
51
+ /**
52
+ * Parse author field which can be a string or object.
53
+ * String format: "Name <email> (url)" where email and url are optional
54
+ */
55
+ function parseAuthor(author) {
56
+ if (!author) {
57
+ return {};
58
+ }
59
+ if (typeof author === 'object') {
60
+ return { name: author.name, email: author.email };
61
+ }
62
+ // Parse string format: "Name <email> (url)"
63
+ const nameMatch = author.match(/^([^<(]+)/);
64
+ const emailMatch = author.match(/<([^>]+)>/);
65
+ return {
66
+ name: nameMatch ? nameMatch[1].trim() : undefined,
67
+ email: emailMatch ? emailMatch[1] : undefined,
68
+ };
69
+ }
70
+ /**
71
+ * Built-in workspace resolvers.
72
+ * These resolve values from the nearest package.json in the current working directory.
73
+ */
74
+ export const workspaceResolvers = {
75
+ 'workspace.name': () => {
76
+ const pkg = findPackageJsonFromCwd();
77
+ if (!pkg)
78
+ return undefined;
79
+ const url = getRepositoryUrl(pkg);
80
+ // Prefer repo slug when repository is set; fall back to package name
81
+ if (url) {
82
+ const parsed = parseGitHubUrl(url);
83
+ if (parsed.name)
84
+ return parsed.name;
85
+ }
86
+ return pkg.name;
87
+ },
88
+ 'workspace.repo.name': () => {
89
+ const pkg = findPackageJsonFromCwd();
90
+ if (!pkg)
91
+ return undefined;
92
+ const url = getRepositoryUrl(pkg);
93
+ if (!url)
94
+ return undefined;
95
+ return parseGitHubUrl(url).name;
96
+ },
97
+ 'workspace.repo.organization': () => {
98
+ const pkg = findPackageJsonFromCwd();
99
+ if (!pkg)
100
+ return undefined;
101
+ const url = getRepositoryUrl(pkg);
102
+ if (!url)
103
+ return undefined;
104
+ return parseGitHubUrl(url).organization;
105
+ },
106
+ // Alias for repo.organization for template readability
107
+ 'workspace.organization.name': () => {
108
+ const pkg = findPackageJsonFromCwd();
109
+ if (!pkg)
110
+ return undefined;
111
+ const url = getRepositoryUrl(pkg);
112
+ if (!url)
113
+ return undefined;
114
+ return parseGitHubUrl(url).organization;
115
+ },
116
+ 'workspace.license': () => {
117
+ const pkg = findPackageJsonFromCwd();
118
+ return pkg?.license;
119
+ },
120
+ 'workspace.author': () => {
121
+ const pkg = findPackageJsonFromCwd();
122
+ if (!pkg)
123
+ return undefined;
124
+ const parsed = parseAuthor(pkg.author);
125
+ return parsed.name;
126
+ },
127
+ 'workspace.author.name': () => {
128
+ const pkg = findPackageJsonFromCwd();
129
+ if (!pkg)
130
+ return undefined;
131
+ const parsed = parseAuthor(pkg.author);
132
+ return parsed.name;
133
+ },
134
+ 'workspace.author.email': () => {
135
+ const pkg = findPackageJsonFromCwd();
136
+ if (!pkg)
137
+ return undefined;
138
+ const parsed = parseAuthor(pkg.author);
139
+ return parsed.email;
140
+ },
141
+ };
package/esm/utils.js ADDED
@@ -0,0 +1,12 @@
1
+ import { green, blue } from 'yanse';
2
+ import { findAndRequirePackageJson } from 'find-and-require-package-json';
3
+ // Function to display the version information
4
+ export function displayVersion() {
5
+ const pkg = findAndRequirePackageJson(__dirname);
6
+ console.log(green(`Name: ${pkg.name}`));
7
+ console.log(blue(`Version: ${pkg.version}`));
8
+ }
9
+ export function getVersion() {
10
+ const pkg = findAndRequirePackageJson(__dirname);
11
+ return pkg.version;
12
+ }
package/index.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ export * from './commander';
2
+ export * from './prompt';
3
+ export * from './question';
4
+ export * from './resolvers';
package/index.js ADDED
@@ -0,0 +1,20 @@
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./commander"), exports);
18
+ __exportStar(require("./prompt"), exports);
19
+ __exportStar(require("./question"), exports);
20
+ __exportStar(require("./resolvers"), exports);
package/keypress.d.ts ADDED
@@ -0,0 +1,45 @@
1
+ import { Readable } from 'stream';
2
+ type KeyHandler = () => void;
3
+ interface ProcessWrapper {
4
+ exit: (code?: number) => never;
5
+ }
6
+ export declare const KEY_CODES: {
7
+ UP_ARROW: string;
8
+ DOWN_ARROW: string;
9
+ RIGHT_ARROW: string;
10
+ LEFT_ARROW: string;
11
+ ENTER: string;
12
+ SPACE: string;
13
+ CTRL_C: string;
14
+ BACKSPACE: string;
15
+ BACKSPACE_LEGACY: string;
16
+ };
17
+ /**
18
+ * Handles keyboard input for interactive prompts.
19
+ *
20
+ * **Important**: Only one TerminalKeypress instance should be actively listening
21
+ * on a given input stream at a time. If you need multiple Prompter instances,
22
+ * call `close()` on the first instance before using the second, or reuse a single
23
+ * instance for all prompts.
24
+ *
25
+ * Multiple instances sharing the same input stream (e.g., process.stdin) will
26
+ * each receive all keypresses, which can cause duplicate or unexpected behavior.
27
+ */
28
+ export declare class TerminalKeypress {
29
+ private listeners;
30
+ private active;
31
+ private noTty;
32
+ private input;
33
+ private proc;
34
+ private dataHandler;
35
+ constructor(noTty?: boolean, input?: Readable, proc?: ProcessWrapper);
36
+ isTTY(): boolean;
37
+ private setupListeners;
38
+ on(key: string, callback: KeyHandler): void;
39
+ off(key: string, callback: KeyHandler): void;
40
+ clearHandlers(): void;
41
+ pause(): void;
42
+ resume(): void;
43
+ destroy(): void;
44
+ }
45
+ export {};
package/keypress.js ADDED
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TerminalKeypress = exports.KEY_CODES = void 0;
4
+ const defaultProcessWrapper = {
5
+ exit: (code) => process.exit(code)
6
+ };
7
+ exports.KEY_CODES = {
8
+ UP_ARROW: '\u001b[A',
9
+ DOWN_ARROW: '\u001b[B',
10
+ RIGHT_ARROW: '\u001b[C',
11
+ LEFT_ARROW: '\u001b[D',
12
+ ENTER: '\r',
13
+ SPACE: ' ',
14
+ CTRL_C: '\u0003',
15
+ BACKSPACE: '\x7f', // Commonly used BACKSPACE key in Unix-like systems
16
+ BACKSPACE_LEGACY: '\x08' // For compatibility with some systems
17
+ };
18
+ /**
19
+ * Handles keyboard input for interactive prompts.
20
+ *
21
+ * **Important**: Only one TerminalKeypress instance should be actively listening
22
+ * on a given input stream at a time. If you need multiple Prompter instances,
23
+ * call `close()` on the first instance before using the second, or reuse a single
24
+ * instance for all prompts.
25
+ *
26
+ * Multiple instances sharing the same input stream (e.g., process.stdin) will
27
+ * each receive all keypresses, which can cause duplicate or unexpected behavior.
28
+ */
29
+ class TerminalKeypress {
30
+ listeners = {};
31
+ active = true;
32
+ noTty;
33
+ input;
34
+ proc;
35
+ dataHandler = null;
36
+ constructor(noTty = false, input = process.stdin, proc = defaultProcessWrapper) {
37
+ this.noTty = noTty;
38
+ this.input = input;
39
+ this.proc = proc;
40
+ if (this.isTTY()) {
41
+ this.input.resume();
42
+ this.input.setEncoding('utf8');
43
+ }
44
+ this.setupListeners();
45
+ }
46
+ isTTY() {
47
+ return !this.noTty;
48
+ }
49
+ setupListeners() {
50
+ this.dataHandler = (key) => {
51
+ if (!this.active)
52
+ return;
53
+ const handlers = this.listeners[key];
54
+ handlers?.forEach(handler => handler());
55
+ if (key === exports.KEY_CODES.CTRL_C) {
56
+ this.proc.exit(0);
57
+ }
58
+ };
59
+ this.input.on('data', this.dataHandler);
60
+ }
61
+ on(key, callback) {
62
+ if (!this.listeners[key]) {
63
+ this.listeners[key] = [];
64
+ }
65
+ this.listeners[key].push(callback);
66
+ }
67
+ off(key, callback) {
68
+ if (this.listeners[key]) {
69
+ const index = this.listeners[key].indexOf(callback);
70
+ if (index !== -1) {
71
+ this.listeners[key].splice(index, 1);
72
+ }
73
+ }
74
+ }
75
+ clearHandlers() {
76
+ this.listeners = {};
77
+ }
78
+ pause() {
79
+ this.active = false;
80
+ this.clearHandlers();
81
+ }
82
+ resume() {
83
+ this.active = true;
84
+ if (this.isTTY() && typeof this.input.setRawMode === 'function') {
85
+ this.input.setRawMode(true);
86
+ }
87
+ }
88
+ destroy() {
89
+ if (typeof this.input.setRawMode === 'function') {
90
+ this.input.setRawMode(false);
91
+ }
92
+ this.input.pause();
93
+ if (this.dataHandler) {
94
+ this.input.removeListener('data', this.dataHandler);
95
+ this.dataHandler = null;
96
+ }
97
+ }
98
+ }
99
+ exports.TerminalKeypress = TerminalKeypress;
package/package.json CHANGED
@@ -1,77 +1,45 @@
1
1
  {
2
2
  "name": "genomic",
3
- "version": "0.0.1",
4
- "description": "asdf",
5
- "author": "Dan Lynch <pyramation@gmail.com>",
6
- "homepage": "https://github.com/asdf/genome#readme",
7
- "license": "SEE LICENSE IN LICENSE",
8
- "main": "main/index.js",
9
- "typings": "types/index.d.ts",
10
- "directories": {
11
- "lib": "src",
12
- "test": "__tests__"
13
- },
14
- "files": [
15
- "types",
16
- "main"
17
- ],
18
- "scripts": {
19
- "build": "cross-env BABEL_ENV=production babel src --out-dir main --delete-dir-on-start --extensions \".tsx,.ts,.js\"",
20
- "build:ts": "tsc --project ./tsconfig.json",
21
- "prepare": "npm run build",
22
- "dev": "cross-env NODE_ENV=development babel-node src/index --extensions \".tsx,.ts,.js\"",
23
- "watch": "cross-env NODE_ENV=development babel-watch src/index --extensions \".tsx,.ts,.js\"",
24
- "lint": "eslint .",
25
- "format": "eslint --fix .",
26
- "test": "jest",
27
- "test:watch": "jest --watch",
28
- "test:debug": "node --inspect node_modules/.bin/jest --runInBand"
29
- },
3
+ "version": "4.0.0",
4
+ "author": "Constructive <developers@constructive.io>",
5
+ "description": "TypeScript-first library for building beautiful CLI interfaces with interactive prompts",
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",
30
11
  "publishConfig": {
31
- "access": "public"
12
+ "access": "public",
13
+ "directory": "dist"
32
14
  },
33
15
  "repository": {
34
16
  "type": "git",
35
- "url": "https://github.com/asdf/genome"
17
+ "url": "https://github.com/constructive-io/dev-utils"
36
18
  },
37
- "keywords": [],
38
19
  "bugs": {
39
- "url": "https://github.com/asdf/genome/issues"
20
+ "url": "https://github.com/constructive-io/dev-utils/issues"
40
21
  },
41
- "jest": {
42
- "testPathIgnorePatterns": [
43
- "main/",
44
- "module/",
45
- "types/"
46
- ]
22
+ "scripts": {
23
+ "copy": "makage assets",
24
+ "clean": "makage clean",
25
+ "prepublishOnly": "npm run build",
26
+ "build": "makage build",
27
+ "dev": "ts-node dev/index",
28
+ "test": "jest",
29
+ "test:watch": "jest --watch"
30
+ },
31
+ "dependencies": {
32
+ "deepmerge": "^4.3.1",
33
+ "find-and-require-package-json": "0.8.2",
34
+ "js-yaml": "^4.1.0",
35
+ "minimist": "^1.2.8",
36
+ "yanse": "0.1.8"
47
37
  },
38
+ "keywords": [],
48
39
  "devDependencies": {
49
- "@babel/cli": "7.18.10",
50
- "@babel/core": "7.19.1",
51
- "@babel/eslint-parser": "^7.19.1",
52
- "@babel/node": "^7.19.1",
53
- "@babel/plugin-proposal-class-properties": "7.18.6",
54
- "@babel/plugin-proposal-export-default-from": "7.18.10",
55
- "@babel/plugin-proposal-object-rest-spread": "7.18.9",
56
- "@babel/plugin-transform-runtime": "7.19.1",
57
- "@babel/preset-env": "7.19.1",
58
- "@babel/preset-typescript": "^7.18.6",
59
- "@types/jest": "^29.0.2",
60
- "babel-core": "7.0.0-bridge.0",
61
- "babel-jest": "29.0.3",
62
- "babel-watch": "^7.0.0",
63
- "cross-env": "^7.0.2",
64
- "eslint": "8.23.1",
65
- "eslint-config-prettier": "^8.5.0",
66
- "eslint-plugin-prettier": "^4.2.1",
67
- "jest": "^29.0.3",
68
- "jest-in-case": "^1.0.2",
69
- "prettier": "^2.7.1",
70
- "regenerator-runtime": "^0.13.7",
71
- "ts-jest": "^29.0.1",
72
- "typescript": "^4.8.3"
40
+ "@types/minimist": "^1.2.5",
41
+ "clean-ansi": "0.1.5",
42
+ "makage": "0.1.8"
73
43
  },
74
- "dependencies": {
75
- "@babel/runtime": "^7.19.0"
76
- }
44
+ "gitHead": "59317e6ae9ba291aebdee3098e456d4ef4e1a5dc"
77
45
  }