rename package from @push.rocks/npmextra to @push.rocks/smartconfig
- Rename all source files from npmextra.* to simpler names (classes.appdata.ts, etc.) - Rename Npmextra class to Smartconfig - Config file changed from npmextra.json to smartconfig.json - KV store path changed from ~/.npmextra/kv to ~/.smartconfig/kv - Update all imports, tests, and metadata
This commit is contained in:
Binary file not shown.
@@ -1,32 +0,0 @@
|
||||
# Coding Standards for npmextra
|
||||
|
||||
## Naming Conventions
|
||||
- **Interfaces**: Prefix with `I` (e.g., `IAppDataOptions`, `ITestOptions`)
|
||||
- **Types**: Prefix with `T` (e.g., `TKeyValueStore`)
|
||||
- **Filenames**: Always lowercase (e.g., `npmextra.classes.appdata.ts`)
|
||||
- **Module structure**: `npmextra.<type>.<name>.ts` pattern
|
||||
|
||||
## Import/Export Patterns
|
||||
- Use ES module syntax (`import`/`export`)
|
||||
- Import all dependencies through `npmextra.plugins.ts`
|
||||
- Reference with full path: `plugins.moduleName.className()`
|
||||
- Export all public APIs through `index.ts`
|
||||
|
||||
## TypeScript Patterns
|
||||
- Use generic types for flexibility (`<T = any>`)
|
||||
- Leverage TypeScript utility types from `@tsclass/tsclass`
|
||||
- Use async/await for asynchronous operations
|
||||
- Use Promises with smartpromise utilities
|
||||
|
||||
## Testing Standards
|
||||
- Import expect from `@git.zone/tstest/tapbundle`
|
||||
- Test files end with `export default tap.start()`
|
||||
- Use descriptive test names with `tap.test()`
|
||||
- Test file naming: `test.*.ts` pattern
|
||||
|
||||
## Code Quality
|
||||
- Make focused, goal-oriented changes
|
||||
- Preserve necessary complexity
|
||||
- Remove redundancy carefully
|
||||
- Keep async patterns where they add value
|
||||
- No comments unless explicitly requested
|
||||
@@ -1,30 +0,0 @@
|
||||
# npmextra Project Overview
|
||||
|
||||
## Purpose
|
||||
npmextra is a utility library that enhances npm with additional configuration and tool management capabilities. It provides a key-value store for project setups and centralized configuration management through npmextra.json files.
|
||||
|
||||
## Tech Stack
|
||||
- TypeScript (ES modules)
|
||||
- Node.js
|
||||
- Dependencies:
|
||||
- @push.rocks/qenv - Environment variable management
|
||||
- @push.rocks/smartfile - File system operations
|
||||
- @push.rocks/smartjson - JSON handling
|
||||
- @push.rocks/smartlog - Logging
|
||||
- @push.rocks/smartpath - Path utilities
|
||||
- @push.rocks/smartpromise - Promise utilities
|
||||
- @push.rocks/smartrx - Reactive programming
|
||||
- @push.rocks/taskbuffer - Task management
|
||||
- @tsclass/tsclass - TypeScript utilities
|
||||
|
||||
## Main Components
|
||||
1. **Npmextra** - Main class for managing npmextra.json configurations
|
||||
2. **KeyValueStore** - Persistent key-value storage system
|
||||
3. **AppData** - Advanced data management with environment variable mapping
|
||||
|
||||
## Project Structure
|
||||
- `ts/` - TypeScript source files
|
||||
- `test/` - Test files using @git.zone/tstest
|
||||
- `dist_ts/` - Compiled JavaScript output
|
||||
- `npmextra.json` - Project configuration
|
||||
- `package.json` - Node.js package configuration
|
||||
@@ -1,31 +0,0 @@
|
||||
# Suggested Commands for npmextra Development
|
||||
|
||||
## Build Commands
|
||||
- `pnpm run build` - Build the TypeScript project (uses tsbuild)
|
||||
- `pnpm test` - Run tests using tstest
|
||||
|
||||
## Development Commands
|
||||
- `pnpm install` - Install dependencies
|
||||
- `pnpm install --save-dev <package>` - Add development dependencies
|
||||
- `tsx <script>` - Run TypeScript files directly (tsx is globally available)
|
||||
|
||||
## Git Commands
|
||||
- `git status` - Check current changes
|
||||
- `git add .` - Stage changes
|
||||
- `git commit -m "message"` - Commit changes (only when explicitly requested)
|
||||
- `git mv` - Move/rename files to preserve history
|
||||
|
||||
## Testing
|
||||
- `pnpm test` - Run all tests
|
||||
- `tstest test/test.some.ts --verbose` - Run specific test file
|
||||
- Tests use @git.zone/tstest framework with tap-based structure
|
||||
|
||||
## Type Checking
|
||||
- `pnpm run build` - Type check and build the project
|
||||
- `tsbuild check test/**/* --skiplibcheck` - Type check test files only
|
||||
|
||||
## Directory Structure
|
||||
- Source code in `ts/` directory
|
||||
- Tests in `test/` directory
|
||||
- Built output in `dist_ts/` directory
|
||||
- Temporary/debug files in `.nogit/` directory
|
||||
@@ -1,68 +0,0 @@
|
||||
# language of the project (csharp, python, rust, java, typescript, go, cpp, or ruby)
|
||||
# * For C, use cpp
|
||||
# * For JavaScript, use typescript
|
||||
# Special requirements:
|
||||
# * csharp: Requires the presence of a .sln file in the project folder.
|
||||
language: typescript
|
||||
|
||||
# whether to use the project's gitignore file to ignore files
|
||||
# Added on 2025-04-07
|
||||
ignore_all_files_in_gitignore: true
|
||||
# list of additional paths to ignore
|
||||
# same syntax as gitignore, so you can use * and **
|
||||
# Was previously called `ignored_dirs`, please update your config if you are using that.
|
||||
# Added (renamed) on 2025-04-07
|
||||
ignored_paths: []
|
||||
|
||||
# whether the project is in read-only mode
|
||||
# If set to true, all editing tools will be disabled and attempts to use them will result in an error
|
||||
# Added on 2025-04-18
|
||||
read_only: false
|
||||
|
||||
|
||||
# list of tool names to exclude. We recommend not excluding any tools, see the readme for more details.
|
||||
# Below is the complete list of tools for convenience.
|
||||
# To make sure you have the latest list of tools, and to view their descriptions,
|
||||
# execute `uv run scripts/print_tool_overview.py`.
|
||||
#
|
||||
# * `activate_project`: Activates a project by name.
|
||||
# * `check_onboarding_performed`: Checks whether project onboarding was already performed.
|
||||
# * `create_text_file`: Creates/overwrites a file in the project directory.
|
||||
# * `delete_lines`: Deletes a range of lines within a file.
|
||||
# * `delete_memory`: Deletes a memory from Serena's project-specific memory store.
|
||||
# * `execute_shell_command`: Executes a shell command.
|
||||
# * `find_referencing_code_snippets`: Finds code snippets in which the symbol at the given location is referenced.
|
||||
# * `find_referencing_symbols`: Finds symbols that reference the symbol at the given location (optionally filtered by type).
|
||||
# * `find_symbol`: Performs a global (or local) search for symbols with/containing a given name/substring (optionally filtered by type).
|
||||
# * `get_current_config`: Prints the current configuration of the agent, including the active and available projects, tools, contexts, and modes.
|
||||
# * `get_symbols_overview`: Gets an overview of the top-level symbols defined in a given file.
|
||||
# * `initial_instructions`: Gets the initial instructions for the current project.
|
||||
# Should only be used in settings where the system prompt cannot be set,
|
||||
# e.g. in clients you have no control over, like Claude Desktop.
|
||||
# * `insert_after_symbol`: Inserts content after the end of the definition of a given symbol.
|
||||
# * `insert_at_line`: Inserts content at a given line in a file.
|
||||
# * `insert_before_symbol`: Inserts content before the beginning of the definition of a given symbol.
|
||||
# * `list_dir`: Lists files and directories in the given directory (optionally with recursion).
|
||||
# * `list_memories`: Lists memories in Serena's project-specific memory store.
|
||||
# * `onboarding`: Performs onboarding (identifying the project structure and essential tasks, e.g. for testing or building).
|
||||
# * `prepare_for_new_conversation`: Provides instructions for preparing for a new conversation (in order to continue with the necessary context).
|
||||
# * `read_file`: Reads a file within the project directory.
|
||||
# * `read_memory`: Reads the memory with the given name from Serena's project-specific memory store.
|
||||
# * `remove_project`: Removes a project from the Serena configuration.
|
||||
# * `replace_lines`: Replaces a range of lines within a file with new content.
|
||||
# * `replace_symbol_body`: Replaces the full definition of a symbol.
|
||||
# * `restart_language_server`: Restarts the language server, may be necessary when edits not through Serena happen.
|
||||
# * `search_for_pattern`: Performs a search for a pattern in the project.
|
||||
# * `summarize_changes`: Provides instructions for summarizing the changes made to the codebase.
|
||||
# * `switch_modes`: Activates modes by providing a list of their names
|
||||
# * `think_about_collected_information`: Thinking tool for pondering the completeness of collected information.
|
||||
# * `think_about_task_adherence`: Thinking tool for determining whether the agent is still on track with the current task.
|
||||
# * `think_about_whether_you_are_done`: Thinking tool for determining whether the task is truly completed.
|
||||
# * `write_memory`: Writes a named memory (for future reference) to Serena's project-specific memory store.
|
||||
excluded_tools: []
|
||||
|
||||
# initial prompt for the project. It will always be given to the LLM upon activating the project
|
||||
# (contrary to the memories, which are loaded on demand).
|
||||
initial_prompt: ""
|
||||
|
||||
project_name: "npmextra"
|
||||
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"json.schemas": [
|
||||
{
|
||||
"fileMatch": ["/npmextra.json"],
|
||||
"fileMatch": ["/smartconfig.json"],
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
12
package.json
12
package.json
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "@push.rocks/npmextra",
|
||||
"name": "@push.rocks/smartconfig",
|
||||
"version": "5.3.3",
|
||||
"private": false,
|
||||
"description": "A utility to enhance npm with additional configuration, tool management capabilities, and a key-value store for project setups.",
|
||||
"description": "A comprehensive configuration management library providing key-value storage, environment variable mapping, and tool configuration.",
|
||||
"main": "dist_ts/index.js",
|
||||
"typings": "dist_ts/index.d.ts",
|
||||
"scripts": {
|
||||
@@ -12,14 +12,14 @@
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://code.foss.global/push.rocks/npmextra.git"
|
||||
"url": "https://code.foss.global/push.rocks/smartconfig.git"
|
||||
},
|
||||
"author": "Lossless GmbH",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://code.foss.global/push.rocks/npmextra/issues"
|
||||
"url": "https://code.foss.global/push.rocks/smartconfig/issues"
|
||||
},
|
||||
"homepage": "https://code.foss.global/push.rocks/npmextra#readme",
|
||||
"homepage": "https://code.foss.global/push.rocks/smartconfig#readme",
|
||||
"dependencies": {
|
||||
"@push.rocks/qenv": "^6.1.3",
|
||||
"@push.rocks/smartfile": "^11.2.5",
|
||||
@@ -46,7 +46,7 @@
|
||||
"dist_ts_web/**/*",
|
||||
"assets/**/*",
|
||||
"cli.js",
|
||||
"npmextra.json",
|
||||
"smartconfig.json",
|
||||
"readme.md"
|
||||
],
|
||||
"browserslist": [
|
||||
|
||||
50
readme.md
50
readme.md
@@ -1,4 +1,4 @@
|
||||
# @push.rocks/npmextra 🚀
|
||||
# @push.rocks/smartconfig 🚀
|
||||
|
||||
**Supercharge your npm projects with powerful configuration management, tool orchestration, and persistent key-value storage.**
|
||||
|
||||
@@ -6,19 +6,19 @@
|
||||
|
||||
```bash
|
||||
# Using npm
|
||||
npm install @push.rocks/npmextra --save
|
||||
npm install @push.rocks/smartconfig --save
|
||||
|
||||
# Using pnpm (recommended)
|
||||
pnpm add @push.rocks/npmextra
|
||||
pnpm add @push.rocks/smartconfig
|
||||
```
|
||||
|
||||
## Overview 🎯
|
||||
|
||||
`@push.rocks/npmextra` is your Swiss Army knife for npm project configuration. It eliminates configuration sprawl by centralizing tool settings, providing intelligent key-value storage, and offering powerful environment variable mapping with automatic type conversions.
|
||||
`@push.rocks/smartconfig` is your Swiss Army knife for npm project configuration. It eliminates configuration sprawl by centralizing tool settings, providing intelligent key-value storage, and offering powerful environment variable mapping with automatic type conversions.
|
||||
|
||||
### Why npmextra?
|
||||
### Why smartconfig?
|
||||
|
||||
- **🎛️ Centralized Configuration**: Manage all your tool configs in one `npmextra.json` file
|
||||
- **🎛️ Centralized Configuration**: Manage all your tool configs in one `smartconfig.json` file
|
||||
- **💾 Persistent Storage**: Smart key-value store with multiple storage strategies
|
||||
- **🔐 Environment Mapping**: Sophisticated env var handling with automatic type conversion
|
||||
- **🏗️ TypeScript First**: Full type safety and IntelliSense support
|
||||
@@ -27,28 +27,28 @@ pnpm add @push.rocks/npmextra
|
||||
|
||||
## Core Concepts 🏗️
|
||||
|
||||
### 1. Npmextra Configuration Management
|
||||
### 1. Smartconfig Configuration Management
|
||||
|
||||
Stop scattering configuration across dozens of files. Centralize everything in `npmextra.json`:
|
||||
Stop scattering configuration across dozens of files. Centralize everything in `smartconfig.json`:
|
||||
|
||||
```typescript
|
||||
import { Npmextra } from '@push.rocks/npmextra';
|
||||
import { Smartconfig } from '@push.rocks/smartconfig';
|
||||
|
||||
// Initialize with current directory
|
||||
const npmextra = new Npmextra();
|
||||
const smartconfig = new Smartconfig();
|
||||
|
||||
// Or specify a custom path
|
||||
const npmextra = new Npmextra('/path/to/project');
|
||||
const smartconfig = new Smartconfig('/path/to/project');
|
||||
|
||||
// Get merged configuration for any tool
|
||||
const eslintConfig = npmextra.dataFor<EslintConfig>('eslint', {
|
||||
// Default values if not in npmextra.json
|
||||
const eslintConfig = smartconfig.dataFor<EslintConfig>('eslint', {
|
||||
// Default values if not in smartconfig.json
|
||||
extends: 'standard',
|
||||
rules: {}
|
||||
});
|
||||
```
|
||||
|
||||
**npmextra.json example:**
|
||||
**smartconfig.json example:**
|
||||
```json
|
||||
{
|
||||
"eslint": {
|
||||
@@ -69,7 +69,7 @@ const eslintConfig = npmextra.dataFor<EslintConfig>('eslint', {
|
||||
A flexible key-value store that persists data between script executions:
|
||||
|
||||
```typescript
|
||||
import { KeyValueStore } from '@push.rocks/npmextra';
|
||||
import { KeyValueStore } from '@push.rocks/smartconfig';
|
||||
|
||||
interface UserSettings {
|
||||
username: string;
|
||||
@@ -115,10 +115,10 @@ await kvStore.waitForKeysPresent(['apiKey']);
|
||||
|
||||
### 3. AppData - Advanced Environment Management 🌟
|
||||
|
||||
The crown jewel of npmextra - sophisticated environment variable mapping with automatic type conversion:
|
||||
The crown jewel of smartconfig - sophisticated environment variable mapping with automatic type conversion:
|
||||
|
||||
```typescript
|
||||
import { AppData } from '@push.rocks/npmextra';
|
||||
import { AppData } from '@push.rocks/smartconfig';
|
||||
|
||||
interface AppConfig {
|
||||
apiUrl: string;
|
||||
@@ -264,7 +264,7 @@ AppData intelligently handles boolean conversions:
|
||||
AppData provides convenient static methods for directly accessing and converting environment variables without creating an instance:
|
||||
|
||||
```typescript
|
||||
import { AppData } from '@push.rocks/npmextra';
|
||||
import { AppData } from '@push.rocks/smartconfig';
|
||||
|
||||
// Get environment variable as boolean
|
||||
const isEnabled = await AppData.valueAsBoolean('FEATURE_ENABLED');
|
||||
@@ -375,10 +375,10 @@ const missingKeys = await appData.logMissingKeys();
|
||||
|
||||
## Real-World Example 🌍
|
||||
|
||||
Here's a complete example of a CLI tool using npmextra:
|
||||
Here's a complete example of a CLI tool using smartconfig:
|
||||
|
||||
```typescript
|
||||
import { Npmextra, AppData, KeyValueStore } from '@push.rocks/npmextra';
|
||||
import { Smartconfig, AppData, KeyValueStore } from '@push.rocks/smartconfig';
|
||||
|
||||
interface CliConfig {
|
||||
githubToken: string;
|
||||
@@ -391,14 +391,14 @@ interface CliConfig {
|
||||
}
|
||||
|
||||
class MyCLI {
|
||||
private npmextra: Npmextra;
|
||||
private smartconfig: Smartconfig;
|
||||
private appData: AppData<CliConfig>;
|
||||
private cache: KeyValueStore<{[key: string]: any}>;
|
||||
|
||||
async initialize() {
|
||||
// Load tool configuration
|
||||
this.npmextra = new Npmextra();
|
||||
const config = this.npmextra.dataFor<any>('mycli', {
|
||||
this.smartconfig = new Smartconfig();
|
||||
const config = this.smartconfig.dataFor<any>('mycli', {
|
||||
defaultModel: 'gpt-3'
|
||||
});
|
||||
|
||||
@@ -450,10 +450,10 @@ cli.run();
|
||||
|
||||
## API Reference 📚
|
||||
|
||||
### Npmextra Class
|
||||
### Smartconfig Class
|
||||
|
||||
```typescript
|
||||
new Npmextra(cwdArg?: string)
|
||||
new Smartconfig(cwdArg?: string)
|
||||
```
|
||||
- `cwdArg`: Optional working directory path
|
||||
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
"module": {
|
||||
"githost": "code.foss.global",
|
||||
"gitscope": "push.rocks",
|
||||
"gitrepo": "npmextra",
|
||||
"description": "A utility to enhance npm with additional configuration, tool management capabilities, and a key-value store for project setups.",
|
||||
"npmPackagename": "@push.rocks/npmextra",
|
||||
"gitrepo": "smartconfig",
|
||||
"description": "A comprehensive configuration management library providing key-value storage, environment variable mapping, and tool configuration.",
|
||||
"npmPackagename": "@push.rocks/smartconfig",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"npm",
|
||||
@@ -1,7 +1,7 @@
|
||||
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
||||
|
||||
// module to test
|
||||
import * as npmextra from '../ts/index.js';
|
||||
import * as smartconfig from '../ts/index.js';
|
||||
|
||||
interface ITestOptions {
|
||||
hi: string;
|
||||
@@ -11,10 +11,10 @@ interface ITestOptions {
|
||||
};
|
||||
}
|
||||
|
||||
let testAppdata: npmextra.AppData<ITestOptions>;
|
||||
let testAppdata: smartconfig.AppData<ITestOptions>;
|
||||
|
||||
tap.test('should create a valid AppData', async () => {
|
||||
testAppdata = new npmextra.AppData<ITestOptions>({
|
||||
testAppdata = new smartconfig.AppData<ITestOptions>({
|
||||
envMapping: {
|
||||
deep: {
|
||||
deep1: '',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
||||
import * as npmextra from '../ts/index.js';
|
||||
import * as smartconfig from '../ts/index.js';
|
||||
|
||||
// Test environment variable with boolean false value
|
||||
tap.test('should handle boolean false values in nested objects correctly', async () => {
|
||||
@@ -11,7 +11,7 @@ tap.test('should handle boolean false values in nested objects correctly', async
|
||||
process.env['S3_ACCESSSECRET'] = 'test-secret';
|
||||
|
||||
// Create AppData with nested object structure similar to CloudlyConfig
|
||||
const appData = await npmextra.AppData.createAndInit({
|
||||
const appData = await smartconfig.AppData.createAndInit({
|
||||
ephemeral: true, // Use in-memory storage for testing
|
||||
envMapping: {
|
||||
s3Descriptor: {
|
||||
@@ -40,7 +40,7 @@ tap.test('should handle boolean false values in nested objects correctly', async
|
||||
expect(s3Descriptor.region).toEqual('us-east-1');
|
||||
expect(s3Descriptor.accessKey).toEqual('test-key');
|
||||
expect(s3Descriptor.accessSecret).toEqual('test-secret');
|
||||
|
||||
|
||||
// Critical test: useSsl should be false, not undefined
|
||||
expect(s3Descriptor.useSsl).toEqual(false);
|
||||
expect(typeof s3Descriptor.useSsl).toEqual('boolean');
|
||||
@@ -68,8 +68,8 @@ tap.test('should handle various boolean representations correctly', async () =>
|
||||
|
||||
for (const testCase of testCases) {
|
||||
process.env['TEST_BOOL'] = testCase.env;
|
||||
|
||||
const appData = await npmextra.AppData.createAndInit({
|
||||
|
||||
const appData = await smartconfig.AppData.createAndInit({
|
||||
ephemeral: true,
|
||||
envMapping: {
|
||||
testBool: 'boolean:TEST_BOOL'
|
||||
@@ -86,7 +86,7 @@ tap.test('should handle various boolean representations correctly', async () =>
|
||||
|
||||
tap.test('should handle hardcoded boolean false values', async () => {
|
||||
// Test with hardcoded boolean false value
|
||||
const appData = await npmextra.AppData.createAndInit({
|
||||
const appData = await smartconfig.AppData.createAndInit({
|
||||
ephemeral: true,
|
||||
envMapping: {
|
||||
boolValue: 'hard_boolean:false'
|
||||
@@ -108,8 +108,8 @@ tap.test('should handle hardcoded boolean false values', async () => {
|
||||
tap.test('should not filter out other falsy values', async () => {
|
||||
process.env['ZERO_VALUE'] = '0';
|
||||
process.env['EMPTY_STRING'] = ''; // This should be preserved as empty string
|
||||
|
||||
const appData = await npmextra.AppData.createAndInit({
|
||||
|
||||
const appData = await smartconfig.AppData.createAndInit({
|
||||
ephemeral: true,
|
||||
envMapping: {
|
||||
nested: {
|
||||
@@ -136,4 +136,4 @@ tap.test('should not filter out other falsy values', async () => {
|
||||
expect(nested.hardcodedZero).toEqual('0');
|
||||
});
|
||||
|
||||
export default tap.start();
|
||||
export default tap.start();
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
||||
|
||||
import * as npmextra from '../ts/index.js';
|
||||
import * as smartconfig from '../ts/index.js';
|
||||
|
||||
let myKeyValueStore: npmextra.KeyValueStore<any>;
|
||||
let myKeyValueStore: smartconfig.KeyValueStore<any>;
|
||||
|
||||
tap.test('should create a keyValueStore', async () => {
|
||||
myKeyValueStore = new npmextra.KeyValueStore<any>({
|
||||
myKeyValueStore = new smartconfig.KeyValueStore<any>({
|
||||
typeArg: 'custom',
|
||||
identityArg: 'test',
|
||||
customPath: 'test/somekv.json',
|
||||
});
|
||||
expect(myKeyValueStore).toBeInstanceOf(npmextra.KeyValueStore);
|
||||
expect(myKeyValueStore).toBeInstanceOf(smartconfig.KeyValueStore);
|
||||
});
|
||||
|
||||
tap.test('should reset the keyValueStore', async () => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
||||
import * as npmextra from '../ts/index.js';
|
||||
import * as smartconfig from '../ts/index.js';
|
||||
|
||||
// Test that sensitive values are properly redacted in logs
|
||||
tap.test('should redact sensitive values in console output', async () => {
|
||||
@@ -19,7 +19,7 @@ tap.test('should redact sensitive values in console output', async () => {
|
||||
process.env['DEBUG_MODE'] = 'true';
|
||||
|
||||
// Create AppData with sensitive environment mappings
|
||||
const appData = await npmextra.AppData.createAndInit({
|
||||
const appData = await smartconfig.AppData.createAndInit({
|
||||
ephemeral: true,
|
||||
envMapping: {
|
||||
apiKey: 'API_KEY',
|
||||
@@ -39,22 +39,22 @@ tap.test('should redact sensitive values in console output', async () => {
|
||||
|
||||
// Check that sensitive values were redacted in logs
|
||||
const combinedOutput = logOutput.join('\n');
|
||||
|
||||
|
||||
// API_KEY should be redacted
|
||||
expect(combinedOutput).toContain('sup...[26 chars]');
|
||||
expect(combinedOutput).not.toContain('super-secret-api-key-12345');
|
||||
|
||||
|
||||
// DATABASE_PASSWORD should be redacted
|
||||
expect(combinedOutput).toContain('myP...[13 chars]');
|
||||
expect(combinedOutput).not.toContain('myP@ssw0rd123');
|
||||
|
||||
|
||||
// AUTH_TOKEN should be redacted (JWT tokens starting with eyJ)
|
||||
expect(combinedOutput).toContain('eyJ...[');
|
||||
expect(combinedOutput).not.toContain('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9');
|
||||
|
||||
|
||||
// PUBLIC_URL should not be redacted (not sensitive)
|
||||
expect(combinedOutput).toContain('https://example.com');
|
||||
|
||||
|
||||
// DEBUG_MODE should not be redacted (not sensitive)
|
||||
expect(combinedOutput).toContain('true');
|
||||
|
||||
@@ -63,16 +63,16 @@ tap.test('should redact sensitive values in console output', async () => {
|
||||
const apiKey = await kvStore.readKey('apiKey');
|
||||
const dbPassword = await kvStore.readKey('dbPassword');
|
||||
const publicUrl = await kvStore.readKey('publicUrl');
|
||||
|
||||
|
||||
// Actual values should be stored correctly
|
||||
expect(apiKey).toEqual('super-secret-api-key-12345');
|
||||
expect(dbPassword).toEqual('myP@ssw0rd123');
|
||||
expect(publicUrl).toEqual('https://example.com');
|
||||
|
||||
|
||||
} finally {
|
||||
// Restore console.log in case of test failure
|
||||
console.log = originalLog;
|
||||
|
||||
|
||||
// Clean up environment variables
|
||||
delete process.env['API_KEY'];
|
||||
delete process.env['DATABASE_PASSWORD'];
|
||||
@@ -82,4 +82,4 @@ tap.test('should redact sensitive values in console output', async () => {
|
||||
}
|
||||
});
|
||||
|
||||
export default tap.start();
|
||||
export default tap.start();
|
||||
|
||||
18
test/test.ts
18
test/test.ts
@@ -1,31 +1,31 @@
|
||||
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
||||
|
||||
// module to test
|
||||
import * as npmextra from '../ts/index.js';
|
||||
import * as smartconfig from '../ts/index.js';
|
||||
|
||||
let testNpmextra: npmextra.Npmextra;
|
||||
let testSmartconfig: smartconfig.Smartconfig;
|
||||
|
||||
tap.test('should create a new Npmtextra instance', async () => {
|
||||
testNpmextra = new npmextra.Npmextra('./test/');
|
||||
expect(testNpmextra).toBeInstanceOf(npmextra.Npmextra);
|
||||
tap.test('should create a new Smartconfig instance', async () => {
|
||||
testSmartconfig = new smartconfig.Smartconfig('./test/');
|
||||
expect(testSmartconfig).toBeInstanceOf(smartconfig.Smartconfig);
|
||||
});
|
||||
|
||||
tap.test('should state wether a npmextra.json exists', async () => {
|
||||
tap.test('should state wether a smartconfig.json exists', async () => {
|
||||
// tslint:disable-next-line:no-unused-expression
|
||||
expect(testNpmextra.npmextraJsonExists).toBeTrue();
|
||||
expect(testSmartconfig.smartconfigJsonExists).toBeTrue();
|
||||
});
|
||||
|
||||
tap.test(
|
||||
'should pass through default value, if not overriden by config from file',
|
||||
async () => {
|
||||
let testData = testNpmextra.dataFor('testTool', { someKey2: 'someValue2' });
|
||||
let testData = testSmartconfig.dataFor('testTool', { someKey2: 'someValue2' });
|
||||
console.log(testData);
|
||||
expect(testData).toHaveProperty('someKey2');
|
||||
},
|
||||
);
|
||||
|
||||
tap.test('should read a config file', async () => {
|
||||
let testData = testNpmextra.dataFor<any>('testTool', {
|
||||
let testData = testSmartconfig.dataFor<any>('testTool', {
|
||||
someKey2: 'someValue2',
|
||||
});
|
||||
expect(testData).toHaveProperty('someKey2');
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* autocreated commitinfo by @push.rocks/commitinfo
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@push.rocks/npmextra',
|
||||
name: '@push.rocks/smartconfig',
|
||||
version: '5.3.3',
|
||||
description: 'A utility to enhance npm with additional configuration, tool management capabilities, and a key-value store for project setups.'
|
||||
description: 'A comprehensive configuration management library providing key-value storage, environment variable mapping, and tool configuration.'
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as plugins from './npmextra.plugins.js';
|
||||
import { KeyValueStore } from './npmextra.classes.keyvaluestore.js';
|
||||
import * as plugins from './plugins.js';
|
||||
import { KeyValueStore } from './classes.keyvaluestore.js';
|
||||
|
||||
// ============================================================================
|
||||
// Singleton Qenv Provider
|
||||
@@ -25,24 +25,24 @@ function getQenv(): plugins.qenv.Qenv {
|
||||
function redactSensitiveValue(key: string, value: unknown): string {
|
||||
// List of patterns that indicate sensitive data
|
||||
const sensitivePatterns = [
|
||||
/secret/i, /token/i, /key/i, /password/i, /pass/i,
|
||||
/secret/i, /token/i, /key/i, /password/i, /pass/i,
|
||||
/api/i, /credential/i, /auth/i, /private/i, /jwt/i,
|
||||
/cert/i, /signature/i, /bearer/i
|
||||
];
|
||||
|
||||
|
||||
// Check if key contains sensitive pattern
|
||||
const isSensitive = sensitivePatterns.some(pattern => pattern.test(key));
|
||||
|
||||
|
||||
if (isSensitive) {
|
||||
if (typeof value === 'string') {
|
||||
// Show first 3 chars and length for debugging
|
||||
return value.length > 3
|
||||
return value.length > 3
|
||||
? `${value.substring(0, 3)}...[${value.length} chars]`
|
||||
: '[redacted]';
|
||||
}
|
||||
return '[redacted]';
|
||||
}
|
||||
|
||||
|
||||
// Check if value looks like a JWT token or base64 secret
|
||||
if (typeof value === 'string') {
|
||||
// JWT tokens start with eyJ
|
||||
@@ -54,7 +54,7 @@ function redactSensitiveValue(key: string, value: unknown): string {
|
||||
return `${value.substring(0, 50)}...[${value.length} chars total]`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return JSON.stringify(value);
|
||||
}
|
||||
|
||||
@@ -67,28 +67,28 @@ function toBoolean(value: unknown): boolean {
|
||||
console.log(` 🔹 toBoolean: value is already boolean: ${value}`);
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
// Handle null/undefined
|
||||
if (value == null) {
|
||||
console.log(` 🔹 toBoolean: value is null/undefined, returning false`);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Handle string representations
|
||||
const s = String(value).toLowerCase().trim();
|
||||
|
||||
|
||||
// True values: "true", "1", "yes", "y", "on"
|
||||
if (['true', '1', 'yes', 'y', 'on'].includes(s)) {
|
||||
console.log(` 🔹 toBoolean: converting "${value}" to true`);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// False values: "false", "0", "no", "n", "off"
|
||||
if (['false', '0', 'no', 'n', 'off'].includes(s)) {
|
||||
console.log(` 🔹 toBoolean: converting "${value}" to false`);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Default: non-empty string = true, empty = false
|
||||
const result = s.length > 0;
|
||||
console.log(` 🔹 toBoolean: defaulting "${value}" to ${result}`);
|
||||
@@ -133,7 +133,7 @@ function toString(value: unknown): string | undefined {
|
||||
type Transform = 'boolean' | 'json' | 'base64' | 'number';
|
||||
|
||||
type MappingSpec = {
|
||||
source:
|
||||
source:
|
||||
| { type: 'env'; key: string }
|
||||
| { type: 'hard'; value: string };
|
||||
transforms: Transform[];
|
||||
@@ -153,7 +153,7 @@ const transformRegistry: Record<string, (v: unknown) => unknown> = {
|
||||
function parseMappingSpec(input: string): MappingSpec {
|
||||
const transforms: Transform[] = [];
|
||||
let remaining = input;
|
||||
|
||||
|
||||
// Check for hardcoded prefixes with type conversion
|
||||
if (remaining.startsWith('hard_boolean:')) {
|
||||
return {
|
||||
@@ -161,21 +161,21 @@ function parseMappingSpec(input: string): MappingSpec {
|
||||
transforms: ['boolean']
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
if (remaining.startsWith('hard_json:')) {
|
||||
return {
|
||||
source: { type: 'hard', value: remaining.slice(10) },
|
||||
transforms: ['json']
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
if (remaining.startsWith('hard_base64:')) {
|
||||
return {
|
||||
source: { type: 'hard', value: remaining.slice(12) },
|
||||
transforms: ['base64']
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// Check for generic hard: prefix
|
||||
if (remaining.startsWith('hard:')) {
|
||||
remaining = remaining.slice(5);
|
||||
@@ -192,7 +192,7 @@ function parseMappingSpec(input: string): MappingSpec {
|
||||
transforms
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// Check for env var prefixes
|
||||
if (remaining.startsWith('boolean:')) {
|
||||
transforms.push('boolean');
|
||||
@@ -204,7 +204,7 @@ function parseMappingSpec(input: string): MappingSpec {
|
||||
transforms.push('base64');
|
||||
remaining = remaining.slice(7);
|
||||
}
|
||||
|
||||
|
||||
// Check for legacy suffixes on env vars
|
||||
if (remaining.endsWith('_JSON')) {
|
||||
transforms.push('json');
|
||||
@@ -213,7 +213,7 @@ function parseMappingSpec(input: string): MappingSpec {
|
||||
transforms.push('base64');
|
||||
remaining = remaining.slice(0, -7);
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
source: { type: 'env', key: remaining },
|
||||
transforms
|
||||
@@ -253,19 +253,19 @@ function applyTransforms(value: unknown, transforms: Transform[]): unknown {
|
||||
async function processMappingValue(mappingString: string): Promise<unknown> {
|
||||
const spec = parseMappingSpec(mappingString);
|
||||
const keyName = spec.source.type === 'env' ? spec.source.key : 'hardcoded';
|
||||
|
||||
|
||||
console.log(` 🔍 Processing mapping: "${mappingString}"`);
|
||||
console.log(` Source: ${spec.source.type === 'env' ? `env:${spec.source.key}` : `hard:${spec.source.value}`}`);
|
||||
console.log(` Transforms: ${spec.transforms.length > 0 ? spec.transforms.join(', ') : 'none'}`);
|
||||
|
||||
|
||||
const rawValue = await resolveSource(spec.source);
|
||||
console.log(` Raw value: ${redactSensitiveValue(keyName, rawValue)} (type: ${typeof rawValue})`);
|
||||
|
||||
|
||||
if (rawValue === undefined || rawValue === null) {
|
||||
console.log(` ⚠️ Raw value is undefined/null, returning undefined`);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
const result = applyTransforms(rawValue, spec.transforms);
|
||||
console.log(` Final value: ${redactSensitiveValue(keyName, result)} (type: ${typeof result})`);
|
||||
return result;
|
||||
@@ -280,12 +280,12 @@ async function evaluateMappingValue(mappingValue: any): Promise<any> {
|
||||
console.log(` 📌 Value is null, returning null`);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// Handle strings (mapping specs)
|
||||
if (typeof mappingValue === 'string') {
|
||||
return processMappingValue(mappingValue);
|
||||
}
|
||||
|
||||
|
||||
// Handle objects (but not arrays or null)
|
||||
if (mappingValue && typeof mappingValue === 'object' && !Array.isArray(mappingValue)) {
|
||||
console.log(` 📂 Processing nested object with ${Object.keys(mappingValue).length} keys`);
|
||||
@@ -304,7 +304,7 @@ async function evaluateMappingValue(mappingValue: any): Promise<any> {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// For any other type (numbers, booleans, etc.), return as-is
|
||||
// Note: We don't have key context here, so we'll just indicate the type
|
||||
console.log(` 📎 Returning value as-is: [value] (type: ${typeof mappingValue})`);
|
||||
@@ -322,7 +322,7 @@ export interface IAppDataOptions<T = any> {
|
||||
* Whether keys should be persisted on disk or not
|
||||
*/
|
||||
ephemeral?: boolean;
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated Use 'ephemeral' instead
|
||||
*/
|
||||
@@ -415,13 +415,13 @@ export class AppData<T = any> {
|
||||
*/
|
||||
private async init() {
|
||||
console.log('🚀 Initializing AppData...');
|
||||
|
||||
|
||||
// Handle backward compatibility for typo
|
||||
const isEphemeral = this.options.ephemeral ?? this.options.ephermal ?? false;
|
||||
if (this.options.ephermal && !this.options.ephemeral) {
|
||||
console.warn('⚠️ Option "ephermal" is deprecated, use "ephemeral" instead.');
|
||||
}
|
||||
|
||||
|
||||
if (this.options.dirPath) {
|
||||
console.log(` 📁 Using custom directory: ${this.options.dirPath}`);
|
||||
} else if (isEphemeral) {
|
||||
@@ -456,17 +456,17 @@ export class AppData<T = any> {
|
||||
console.log(`📦 Processing envMapping for AppData...`);
|
||||
const totalKeys = Object.keys(this.options.envMapping).length;
|
||||
let processedCount = 0;
|
||||
|
||||
|
||||
// Process each top-level key in envMapping
|
||||
for (const key in this.options.envMapping) {
|
||||
try {
|
||||
const mappingSpec = this.options.envMapping[key];
|
||||
const specType = mappingSpec === null ? 'null' :
|
||||
typeof mappingSpec === 'string' ? mappingSpec :
|
||||
typeof mappingSpec === 'object' ? 'nested object' :
|
||||
const specType = mappingSpec === null ? 'null' :
|
||||
typeof mappingSpec === 'string' ? mappingSpec :
|
||||
typeof mappingSpec === 'object' ? 'nested object' :
|
||||
typeof mappingSpec;
|
||||
console.log(` → Processing key "${key}" with spec: ${specType}`);
|
||||
|
||||
|
||||
const evaluated = await evaluateMappingValue(mappingSpec);
|
||||
// Important: Don't skip false, 0, empty string, or null values!
|
||||
// Only skip if explicitly undefined
|
||||
@@ -474,10 +474,10 @@ export class AppData<T = any> {
|
||||
await this.kvStore.writeKey(key as keyof T, evaluated);
|
||||
processedCount++;
|
||||
const valueType = evaluated === null ? 'null' :
|
||||
Array.isArray(evaluated) ? 'array' :
|
||||
Array.isArray(evaluated) ? 'array' :
|
||||
typeof evaluated;
|
||||
const valuePreview = evaluated === null ? 'null' :
|
||||
typeof evaluated === 'object' ?
|
||||
typeof evaluated === 'object' ?
|
||||
(Array.isArray(evaluated) ? `[${evaluated.length} items]` : `{${Object.keys(evaluated).length} keys}`) :
|
||||
redactSensitiveValue(key, evaluated);
|
||||
console.log(` ✅ Successfully processed key "${key}" = ${valuePreview} (type: ${valueType})`);
|
||||
@@ -488,7 +488,7 @@ export class AppData<T = any> {
|
||||
console.error(` ❌ Failed to evaluate envMapping for key "${key}":`, err);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
console.log(`📊 EnvMapping complete: ${processedCount}/${totalKeys} keys successfully processed`);
|
||||
}
|
||||
|
||||
@@ -496,18 +496,18 @@ export class AppData<T = any> {
|
||||
if (this.options.overwriteObject) {
|
||||
const overwriteKeys = Object.keys(this.options.overwriteObject);
|
||||
console.log(`🔄 Applying overwriteObject with ${overwriteKeys.length} key(s)...`);
|
||||
|
||||
|
||||
for (const key of overwriteKeys) {
|
||||
const value = this.options.overwriteObject[key];
|
||||
const valueType = Array.isArray(value) ? 'array' : typeof value;
|
||||
console.log(` 🔧 Overwriting key "${key}" with ${valueType} value`);
|
||||
|
||||
|
||||
await this.kvStore.writeKey(
|
||||
key as keyof T,
|
||||
value,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
console.log(`✅ OverwriteObject complete: ${overwriteKeys.length} key(s) overwritten`);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as plugins from './npmextra.plugins.js';
|
||||
import * as paths from './npmextra.paths.js';
|
||||
import * as plugins from './plugins.js';
|
||||
import * as paths from './paths.js';
|
||||
|
||||
import { Task } from '@push.rocks/taskbuffer';
|
||||
|
||||
79
ts/classes.smartconfig.ts
Normal file
79
ts/classes.smartconfig.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import * as plugins from './plugins.js';
|
||||
import * as paths from './paths.js';
|
||||
|
||||
/**
|
||||
* Smartconfig class allows easy configuration of tools
|
||||
*/
|
||||
export class Smartconfig {
|
||||
cwd: string;
|
||||
lookupPath: string;
|
||||
smartconfigJsonExists: boolean;
|
||||
smartconfigJsonData: any;
|
||||
|
||||
/**
|
||||
* creates instance of Smartconfig
|
||||
*/
|
||||
constructor(cwdArg?: string) {
|
||||
if (cwdArg) {
|
||||
this.cwd = cwdArg;
|
||||
} else {
|
||||
this.cwd = paths.cwd;
|
||||
}
|
||||
this.checkLookupPath();
|
||||
this.checkSmartconfigJsonExists();
|
||||
this.checkSmartconfigJsonData();
|
||||
}
|
||||
|
||||
/**
|
||||
* merges the supplied options with the ones from smartconfig.json
|
||||
*/
|
||||
dataFor<IToolConfig>(
|
||||
toolnameArg: string,
|
||||
defaultOptionsArg: any,
|
||||
): IToolConfig {
|
||||
let smartconfigToolOptions;
|
||||
if (this.smartconfigJsonData[toolnameArg]) {
|
||||
smartconfigToolOptions = this.smartconfigJsonData[toolnameArg];
|
||||
} else {
|
||||
smartconfigToolOptions = {};
|
||||
}
|
||||
let mergedOptions = {
|
||||
...defaultOptionsArg,
|
||||
...smartconfigToolOptions,
|
||||
};
|
||||
return mergedOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* checks if the JSON exists
|
||||
*/
|
||||
private checkSmartconfigJsonExists() {
|
||||
this.smartconfigJsonExists = plugins.smartfile.fs.fileExistsSync(
|
||||
this.lookupPath,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* gets lookupPath
|
||||
*/
|
||||
private checkLookupPath() {
|
||||
if (this.cwd) {
|
||||
this.lookupPath = plugins.path.join(this.cwd, 'smartconfig.json');
|
||||
} else {
|
||||
this.lookupPath = paths.configFile;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get smartconfigJsonData
|
||||
*/
|
||||
private checkSmartconfigJsonData() {
|
||||
if (this.smartconfigJsonExists) {
|
||||
this.smartconfigJsonData = plugins.smartfile.fs.toObjectSync(
|
||||
this.lookupPath,
|
||||
);
|
||||
} else {
|
||||
this.smartconfigJsonData = {};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,3 @@
|
||||
export * from './npmextra.classes.appdata.js';
|
||||
export * from './npmextra.classes.keyvaluestore.js';
|
||||
export * from './npmextra.classes.npmextra.js';
|
||||
export * from './classes.appdata.js';
|
||||
export * from './classes.keyvaluestore.js';
|
||||
export * from './classes.smartconfig.js';
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
import * as plugins from './npmextra.plugins.js';
|
||||
import * as paths from './npmextra.paths.js';
|
||||
|
||||
/**
|
||||
* Npmextra class allows easy configuration of tools
|
||||
*/
|
||||
export class Npmextra {
|
||||
cwd: string;
|
||||
lookupPath: string;
|
||||
npmextraJsonExists: boolean;
|
||||
npmextraJsonData: any;
|
||||
|
||||
/**
|
||||
* creates instance of Npmextra
|
||||
*/
|
||||
constructor(cwdArg?: string) {
|
||||
if (cwdArg) {
|
||||
this.cwd = cwdArg;
|
||||
} else {
|
||||
this.cwd = paths.cwd;
|
||||
}
|
||||
this.checkLookupPath();
|
||||
this.checkNpmextraJsonExists();
|
||||
this.checkNpmextraJsonData();
|
||||
}
|
||||
|
||||
/**
|
||||
* merges the supplied options with the ones from npmextra.json
|
||||
*/
|
||||
dataFor<IToolConfig>(
|
||||
toolnameArg: string,
|
||||
defaultOptionsArg: any,
|
||||
): IToolConfig {
|
||||
let npmextraToolOptions;
|
||||
if (this.npmextraJsonData[toolnameArg]) {
|
||||
npmextraToolOptions = this.npmextraJsonData[toolnameArg];
|
||||
} else {
|
||||
npmextraToolOptions = {};
|
||||
}
|
||||
let mergedOptions = {
|
||||
...defaultOptionsArg,
|
||||
...npmextraToolOptions,
|
||||
};
|
||||
return mergedOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* checks if the JSON exists
|
||||
*/
|
||||
private checkNpmextraJsonExists() {
|
||||
this.npmextraJsonExists = plugins.smartfile.fs.fileExistsSync(
|
||||
this.lookupPath,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* gets lookupPath
|
||||
*/
|
||||
private checkLookupPath() {
|
||||
if (this.cwd) {
|
||||
this.lookupPath = plugins.path.join(this.cwd, 'npmextra.json');
|
||||
} else {
|
||||
this.lookupPath = paths.configFile;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get npmextraJsonData
|
||||
*/
|
||||
private checkNpmextraJsonData() {
|
||||
if (this.npmextraJsonExists) {
|
||||
this.npmextraJsonData = plugins.smartfile.fs.toObjectSync(
|
||||
this.lookupPath,
|
||||
);
|
||||
} else {
|
||||
this.npmextraJsonData = {};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as plugins from './npmextra.plugins.js';
|
||||
import * as plugins from './plugins.js';
|
||||
|
||||
// directories
|
||||
export let cwd = process.cwd();
|
||||
@@ -16,7 +16,7 @@ export let home = plugins.smartpath.get.home();
|
||||
/**
|
||||
* keyValue base path
|
||||
*/
|
||||
export let kvUserHomeDirBase = plugins.path.join(home, '.npmextra/kv');
|
||||
export let kvUserHomeDirBase = plugins.path.join(home, '.smartconfig/kv');
|
||||
|
||||
// files
|
||||
export let configFile = plugins.path.join(cwd, 'npmextra.json');
|
||||
export let configFile = plugins.path.join(cwd, 'smartconfig.json');
|
||||
Reference in New Issue
Block a user