Compare commits

...

4 Commits

Author SHA1 Message Date
61fafd2c8f 5.1.4
Some checks failed
Default (tags) / security (push) Successful in 34s
Default (tags) / test (push) Failing after 4m4s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-08-15 12:26:50 +00:00
33a5b6b11c fix(AppData, dev dependencies, settings): Improve boolean conversion in AppData, update @types/node dependency, and add local settings file. 2025-08-15 12:26:50 +00:00
c74ded8d5d 5.1.3
Some checks failed
Default (tags) / security (push) Successful in 41s
Default (tags) / test (push) Failing after 4m3s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-08-15 12:12:26 +00:00
62e61168a0 fix(appdata): Fix iteration over overwriteObject in AppData and update configuration for dependency and path handling 2025-08-15 12:12:26 +00:00
25 changed files with 5827 additions and 2946 deletions

View File

@@ -6,8 +6,8 @@ on:
- '**'
env:
IMAGE: registry.gitlab.com/hosttoday/ht-docker-node:npmci
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@gitea.lossless.digital/${{gitea.repository}}.git
IMAGE: code.foss.global/host.today/ht-docker-node:npmci
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@/${{gitea.repository}}.git
NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}}
NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}
@@ -26,7 +26,7 @@ jobs:
- name: Install pnpm and npmci
run: |
pnpm install -g pnpm
pnpm install -g @shipzone/npmci
pnpm install -g @ship.zone/npmci
- name: Run npm prepare
run: npmci npm prepare

View File

@@ -6,8 +6,8 @@ on:
- '*'
env:
IMAGE: registry.gitlab.com/hosttoday/ht-docker-node:npmci
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@gitea.lossless.digital/${{gitea.repository}}.git
IMAGE: code.foss.global/host.today/ht-docker-node:npmci
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@/${{gitea.repository}}.git
NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}}
NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}
@@ -26,7 +26,7 @@ jobs:
- name: Prepare
run: |
pnpm install -g pnpm
pnpm install -g @shipzone/npmci
pnpm install -g @ship.zone/npmci
npmci npm prepare
- name: Audit production dependencies
@@ -54,7 +54,7 @@ jobs:
- name: Prepare
run: |
pnpm install -g pnpm
pnpm install -g @shipzone/npmci
pnpm install -g @ship.zone/npmci
npmci npm prepare
- name: Test stable
@@ -82,7 +82,7 @@ jobs:
- name: Prepare
run: |
pnpm install -g pnpm
pnpm install -g @shipzone/npmci
pnpm install -g @ship.zone/npmci
npmci npm prepare
- name: Release
@@ -104,7 +104,7 @@ jobs:
- name: Prepare
run: |
pnpm install -g pnpm
pnpm install -g @shipzone/npmci
pnpm install -g @ship.zone/npmci
npmci npm prepare
- name: Code quality

3
.gitignore vendored
View File

@@ -3,7 +3,6 @@
# artifacts
coverage/
public/
pages/
# installs
node_modules/
@@ -17,4 +16,4 @@ node_modules/
dist/
dist_*/
# custom
#------# custom

Binary file not shown.

View File

@@ -0,0 +1,32 @@
# 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

View File

@@ -0,0 +1,30 @@
# 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

View File

@@ -0,0 +1,31 @@
# 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

68
.serena/project.yml Normal file
View File

@@ -0,0 +1,68 @@
# 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"

View File

@@ -1,57 +1,83 @@
# Changelog
## 2025-08-15 - 5.1.4 - fix(AppData, dev dependencies, settings)
Improve boolean conversion in AppData, update @types/node dependency, and add local settings file.
- Fixed env var boolean conversion to properly handle non-string values in AppData.
- Updated @types/node from ^20.14.5 to ^22 in package.json.
- Added .claude/settings.local.json to configure project permissions locally.
## 2025-08-15 - 5.1.3 - fix(appdata)
Fix iteration over overwriteObject in AppData and update configuration for dependency and path handling
- Replaced incorrect looping constructs in the AppData class to properly iterate over overwriteObject keys
- Improved environment variable mapping and file path resolution in multiple TS modules
- Updated dependency versions and adjusted git workflow configurations
- Enhanced project configuration including TS config and build script adjustments
## 2024-11-06 - 5.1.2 - fix(appdata)
Fix iteration over overwriteObject in AppData class
- Corrected the for loop from in to of inside the AppData class for iterating over overwriteObject keys.
## 2024-11-05 - 5.1.1 - fix(AppData)
Fix issue with overwrite object handling in AppData class
- Corrected logic to handle cases when overwriteObject is undefined.
## 2024-11-05 - 5.1.0 - feat(appdata)
Add support for overwriting keys using the overwriteObject option in AppData
- Introduced the overwriteObject option in IAppDataOptions to allow overwriting specific keys in the AppData class.
## 2024-06-19 - 5.0.17 - 5.0.23 - Core Updates
Routine maintenance and updates to the core components.
- Multiple core updates and fixes improving stability
## 2024-06-12 - 5.0.13 - 5.0.16 - Core Updates
Maintenance focus on core systems with enhancements and problem resolutions.
- Enhancements and updates in the core functionality
## 2024-05-29 - 5.0.13 - Documentation Update
Descriptive improvements aligned with current features.
- Updated core description for better clarity in documentation
## 2024-04-01 - 5.0.10 - Configuration Update
Improved configuration management for build processes.
- Updated `npmextra.json` to reflect changes in git repository management
## 2024-02-12 - 5.0.0 - 5.0.9 - Major Core Enhancements
A series of critical updates with resolved issues in the core components.
- Introduction of new core features
- Several core system updates
## 2024-02-12 - 4.0.16 - Major Version Transition
Migration to the new major version with impactful changes.
- BREAKING CHANGE: Significant updates requiring attention for smooth transition
## 2023-08-24 - 3.0.9 - 4.0.16 - Organization Updates
Formatted updates with attention to organizational standards and practice.
- SWITCH to a new organizational scheme
## 2023-07-11 - 3.0.9 - Organizational Enhancement
Shifts aligning with contemporary structuring and logistics.
- Strategic realignment with organizational principles

View File

@@ -39,4 +39,4 @@
"tsdoc": {
"legal": "\n## License and Legal Information\n\nThis repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository. \n\n**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.\n\n### Trademarks\n\nThis project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.\n\n### Company Information\n\nTask Venture Capital GmbH \nRegistered at District court Bremen HRB 35230 HB, Germany\n\nFor any legal inquiries or if you require further information, please contact us via email at hello@task.vc.\n\nBy using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.\n"
}
}
}

View File

@@ -1,12 +1,12 @@
{
"name": "@push.rocks/npmextra",
"version": "5.1.2",
"version": "5.1.4",
"private": false,
"description": "A utility to enhance npm with additional configuration, tool management capabilities, and a key-value store for project setups.",
"main": "dist_ts/index.js",
"typings": "dist_ts/index.d.ts",
"scripts": {
"test": "(tstest test/ --web)",
"test": "(tstest test/ --verbose --logfile --timeout 60)",
"build": "(tsbuild --web --allowimplicitany)",
"buildDocs": "tsdoc"
},
@@ -17,26 +17,25 @@
"author": "Lossless GmbH",
"license": "MIT",
"bugs": {
"url": "https://gitlab.com/pushrocks/npmextra/issues"
"url": "https://code.foss.global/push.rocks/npmextra/issues"
},
"homepage": "https://code.foss.global/push.rocks/npmextra",
"homepage": "https://code.foss.global/push.rocks/npmextra#readme",
"dependencies": {
"@push.rocks/qenv": "^6.0.5",
"@push.rocks/smartfile": "^11.0.20",
"@push.rocks/qenv": "^6.1.2",
"@push.rocks/smartfile": "^11.2.5",
"@push.rocks/smartjson": "^5.0.20",
"@push.rocks/smartlog": "^3.0.7",
"@push.rocks/smartpath": "^5.0.18",
"@push.rocks/smartpromise": "^4.0.2",
"@push.rocks/smartrx": "^3.0.7",
"@push.rocks/smartlog": "^3.1.8",
"@push.rocks/smartpath": "^6.0.0",
"@push.rocks/smartpromise": "^4.2.3",
"@push.rocks/smartrx": "^3.0.10",
"@push.rocks/taskbuffer": "^3.1.7",
"@tsclass/tsclass": "^4.0.59"
"@tsclass/tsclass": "^9.2.0"
},
"devDependencies": {
"@git.zone/tsbuild": "^2.1.80",
"@git.zone/tsrun": "^1.2.44",
"@git.zone/tstest": "^1.0.90",
"@push.rocks/tapbundle": "^5.0.23",
"@types/node": "^20.14.5"
"@git.zone/tsbuild": "^2.6.4",
"@git.zone/tsrun": "^1.3.3",
"@git.zone/tstest": "^2.3.2",
"@types/node": "^22"
},
"files": [
"ts/**/*",
@@ -70,5 +69,9 @@
"smart file handling",
"workflow improvement",
"persistent storage"
]
],
"packageManager": "pnpm@10.11.0+sha512.6540583f41cc5f628eb3d9773ecee802f4f9ef9923cc45b69890fb47991d4b092964694ec3a4f738a420c918a333062c8b925d312f42e4f0c263eb603551f977",
"pnpm": {
"overrides": {}
}
}

7745
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

4
pnpm-workspace.yaml Normal file
View File

@@ -0,0 +1,4 @@
onlyBuiltDependencies:
- esbuild
- mongodb-memory-server
- puppeteer

604
readme.md
View File

@@ -1,295 +1,463 @@
# @push.rocks/npmextra
A utility to enhance npm with additional configuration, tool management capabilities, and a key-value store for project setups.
# @push.rocks/npmextra 🚀
## Install
To install `@push.rocks/npmextra`, use the following npm command:
**Supercharge your npm projects with powerful configuration management, tool orchestration, and persistent key-value storage.**
## Install 📦
```bash
# Using npm
npm install @push.rocks/npmextra --save
# Using pnpm (recommended)
pnpm add @push.rocks/npmextra
```
This package is available on [npm](https://www.npmjs.com/package/@push.rocks/npmextra) and can be installed into your project as a dependency to enhance npm with additional configuration and tool management capabilities.
## Overview 🎯
## Usage
`@push.rocks/npmextra` is designed to supplement npm functionalities with enhanced configuration and tool management. It facilitates the management of project configurations and tool setups in a consolidated manner, enabling a smoother workflow and maintenance process. Below are detailed use cases and examples implemented with ESM syntax and TypeScript.
`@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.
### Initial Setup and Configuration
To start using `npmextra` in your project, first include it with an import statement:
### Why npmextra?
- **🎛️ Centralized Configuration**: Manage all your tool configs in one `npmextra.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
- **⚡ Zero Config**: Works out of the box with sensible defaults
- **🔄 Reactive**: Built-in change detection and observables
## Core Concepts 🏗️
### 1. Npmextra Configuration Management
Stop scattering configuration across dozens of files. Centralize everything in `npmextra.json`:
```typescript
import { Npmextra } from '@push.rocks/npmextra';
// Initialize with current directory
const npmextra = new Npmextra();
// Or specify a custom path
const npmextra = new Npmextra('/path/to/project');
// Get merged configuration for any tool
const eslintConfig = npmextra.dataFor<EslintConfig>('eslint', {
// Default values if not in npmextra.json
extends: 'standard',
rules: {}
});
```
Instantiate the `Npmextra` class optionally with a custom path to your project's working directory. If no path is provided, the current working directory (`process.cwd()`) is used.
```typescript
const npmExtraInstance = new Npmextra('/path/to/your/project');
```
### Managing Tool Configurations with `npmextra.json`
`@push.rocks/npmextra` excels in unifying tool configurations through a single `npmextra.json` file. Instead of scattering configurations across multiple files, `npmextra` enables you to define tool-specific settings within this centralized configuration file, which can then be accessed programmatically.
#### Creating and Utilizing `npmextra.json`
Create a `npmextra.json` in your project root with the following structure:
**npmextra.json example:**
```json
{
"toolname": {
"setting1": "value1",
"setting2": "value2"
"eslint": {
"extends": "@company/eslint-config",
"rules": {
"no-console": "warn"
}
},
"prettier": {
"semi": false,
"singleQuote": true
}
}
```
For example, to configure a hypothetical tool named `toolname`, define its settings as shown above.
### 2. KeyValueStore - Persistent Data Storage
#### Accessing Configuration in Your Project
With the configuration defined, you can easily access these settings in your TypeScript code as follows:
```typescript
// Import the npmextra module
import { Npmextra } from '@push.rocks/npmextra';
// Create an instance pointing at the current directory
const npmExtraInstance = new Npmextra();
// Retrieve the configuration for 'toolname', merging defaults with any found in npmextra.json
const toolConfig = npmExtraInstance.dataFor<{ setting1: string, setting2: string }>('toolname', {
setting1: 'defaultValue1',
setting2: 'defaultValue2'
});
// toolConfig now contains the merged settings from npmextra.json and provided defaults
console.log(toolConfig);
```
### Key-Value Store Management
`@push.rocks/npmextra` also includes a Key-Value Store (KeyValueStore) functionality enabling persistent storage of key-value pairs between script executions.
#### Setting Up KeyValueStore
To utilize the KeyValueStore, create an instance specifying its scope (e.g., 'userHomeDir') and a unique identity for your application or tool:
A flexible key-value store that persists data between script executions:
```typescript
import { KeyValueStore } from '@push.rocks/npmextra';
const kvStore = new KeyValueStore<'userHomeDir'>({
typeArg: 'userHomeDir',
identityArg: 'myUniqueAppName'
interface UserSettings {
username: string;
apiKey: string;
preferences: {
theme: 'light' | 'dark';
notifications: boolean;
};
}
// Different storage strategies
const kvStore = new KeyValueStore<UserSettings>({
typeArg: 'userHomeDir', // Store in user's home directory
identityArg: 'myApp',
mandatoryKeys: ['username', 'apiKey']
});
```
You can then use the `writeKey`, `readKey`, `writeAll`, and `readAll` methods to manage your store respectively.
// Write operations
await kvStore.writeKey('username', 'john_doe');
await kvStore.writeKey('preferences', {
theme: 'dark',
notifications: true
});
#### Example: Storing and Retrieving Data
```typescript
// Write a single key-value pair
await kvStore.writeKey('username', 'johnDoe');
// Read a single key
// Read operations
const username = await kvStore.readKey('username');
console.log(username); // Outputs: johnDoe
// Write multiple key-value pairs
await kvStore.writeAll({
email: 'john@example.com',
isAdmin: true
});
// Read all key-value pairs
const allData = await kvStore.readAll();
console.log(allData); // Outputs the entire store's contents
```
### Advanced Key-Value Store Management
In addition to basic read/write operations, `npmextra`s `KeyValueStore` supports advanced scenarios like mandatory keys and custom file paths.
#### Example: Mandatory Keys and Custom Paths
Consider a scenario where your application requires specific keys to be present in the KeyValueStore. You can define mandatory keys and use a custom path for your store like this:
```typescript
import { KeyValueStore } from '@push.rocks/npmextra';
interface CustomData {
key1: string;
key2: number;
key3?: boolean;
}
const kvStore = new KeyValueStore<CustomData>({
typeArg: 'custom',
identityArg: 'customApp',
customPath: '/custom/path/to/store.json',
mandatoryKeys: ['key1', 'key2']
});
// Ensure all mandatory keys are present
// Check for missing mandatory keys
const missingKeys = await kvStore.getMissingMandatoryKeys();
if (missingKeys.length) {
console.log(`Missing mandatory keys: ${missingKeys.join(', ')}`);
if (missingKeys.length > 0) {
console.log('Missing required configuration:', missingKeys);
}
// Use the KeyValueStore
await kvStore.writeKey('key1', 'value1');
await kvStore.writeKey('key2', 123);
const key1Value = await kvStore.readKey('key1');
const allData = await kvStore.readAll();
console.log(key1Value); // Outputs: value1
console.log(allData); // Outputs: { key1: 'value1', key2: 123 }
// Wait for keys to be present
await kvStore.waitForKeysPresent(['apiKey']);
```
### Combining AppData and KeyValueStore
**Storage Types:**
- `userHomeDir`: Store in user's home directory
- `custom`: Specify your own path
- `ephemeral`: In-memory only (perfect for testing)
The `AppData` class extends the functionality of `KeyValueStore` by integrating environmental variables, specifying additional configurations, and providing a more structured approach to data management.
### 3. AppData - Advanced Environment Management 🌟
#### Example: AppData Usage
The crown jewel of npmextra - sophisticated environment variable mapping with automatic type conversion:
```typescript
import { AppData } from '@push.rocks/npmextra';
interface AppSettings {
settingA: string;
settingB: number;
nestedSetting: {
innerSetting: boolean;
}
interface AppConfig {
apiUrl: string;
apiKey: string;
port: number;
features: {
analytics: boolean;
payment: boolean;
};
cache: {
ttl: number;
redis: {
host: string;
password: string;
};
};
}
const appDataInstance = await AppData.createAndInit<AppSettings>({
dirPath: '/custom/path/to/appdata',
requiredKeys: ['settingA', 'settingB'],
const appData = await AppData.createAndInit<AppConfig>({
dirPath: '/app/config', // Optional: defaults to smart path selection
requiredKeys: ['apiKey', 'apiUrl'],
envMapping: {
settingA: 'MY_ENV_A',
settingB: 'hard:42',
nestedSetting: {
innerSetting: 'MY_ENV_INNER'
apiUrl: 'API_URL', // Simple mapping
apiKey: 'hard:development-key-123', // Hardcoded value
port: 'hard:3000', // Hardcoded number
features: {
analytics: 'boolean:ENABLE_ANALYTICS', // Force boolean conversion
payment: 'hard_boolean:true' // Hardcoded boolean
},
cache: {
ttl: 'json:CACHE_CONFIG', // Parse JSON from env var
redis: {
host: 'REDIS_HOST',
password: 'base64:REDIS_PASSWORD_B64' // Decode base64
}
}
},
overwriteObject: {
// Force these values regardless of env vars
port: 8080
}
});
const store = await appData.getKvStore();
const apiUrl = await store.readKey('apiUrl');
```
## AppData Special Cases & Conversions 🎯
### Environment Variable Prefixes
AppData supports sophisticated type conversion through prefixes:
| Prefix | Description | Example | Result |
|--------|-------------|---------|--------|
| `hard:` | Hardcoded value | `hard:myvalue` | `"myvalue"` |
| `hard_boolean:` | Hardcoded boolean | `hard_boolean:true` | `true` |
| `hard_json:` | Hardcoded JSON | `hard_json:{"key":"value"}` | `{key: "value"}` |
| `hard_base64:` | Hardcoded base64 | `hard_base64:SGVsbG8=` | `"Hello"` |
| `boolean:` | Env var as boolean | `boolean:FEATURE_FLAG` | `true/false` |
| `json:` | Parse env var as JSON | `json:CONFIG_JSON` | Parsed object |
| `base64:` | Decode env var from base64 | `base64:SECRET_B64` | Decoded string |
### Automatic Suffix Detection
Variables ending with certain suffixes get automatic conversion:
```typescript
{
envMapping: {
// Automatically parsed as JSON if MY_CONFIG_JSON="{"enabled":true}"
config: 'MY_CONFIG_JSON',
// Automatically decoded from base64 if SECRET_KEY_BASE64="SGVsbG8="
secret: 'SECRET_KEY_BASE64'
}
}
```
### Complex Examples
```typescript
const appData = await AppData.createAndInit({
envMapping: {
// Simple environment variable
apiUrl: 'API_URL',
// Hardcoded values with type conversion
debugMode: 'hard_boolean:false',
maxRetries: 'hard:5',
defaultConfig: 'hard_json:{"timeout":30,"retries":3}',
// Environment variables with conversion
features: 'json:FEATURE_FLAGS', // Expects: {"feature1":true,"feature2":false}
isProduction: 'boolean:IS_PROD', // Expects: "true" or "false"
apiSecret: 'base64:API_SECRET', // Expects: base64 encoded string
// Nested structures
database: {
host: 'DB_HOST',
port: 'hard:5432',
credentials: {
user: 'DB_USER',
password: 'base64:DB_PASSWORD_ENCODED',
ssl: 'boolean:DB_USE_SSL'
}
}
},
// Override any env mappings
overwriteObject: {
debugMode: true, // Force debug mode regardless of env
database: {
host: 'localhost' // Force localhost for development
}
}
});
const appDataKvStore = await appDataInstance.getKvStore();
// Writing values
await appDataKvStore.writeKey('settingA', 'exampleValue');
await appDataKvStore.writeKey('settingB', 100);
await appDataKvStore.writeKey('nestedSetting', { innerSetting: true });
// Reading values
const settingA = await appDataKvStore.readKey('settingA');
const allSettings = await appDataKvStore.readAll();
console.log(settingA); // Outputs: 'exampleValue'
console.log(allSettings); // Outputs: { settingA: 'exampleValue', settingB: 100, nestedSetting: { innerSetting: true } }
```
### Error Handling and Debugging
### Boolean Conversion Rules
Proper error handling ensures your integrations with `npmextra` are robust and stable. Below are some strategies for error handling and debugging potential issues.
AppData intelligently handles boolean conversions:
#### Example: Error Handling in KeyValueStore
1. **String "true"/"false"**: Converted to boolean
2. **With `boolean:` prefix**: Any env var value is converted (`"true"``true`, anything else → `false`)
3. **With `hard_boolean:` prefix**: Hardcoded boolean value
4. **Regular env vars**: Strings remain strings unless prefixed
```typescript
import { KeyValueStore } from '@push.rocks/npmextra';
const kvStore = new KeyValueStore('userHomeDir', 'errorHandlingApp');
try {
await kvStore.writeKey('importantKey', 'importantValue');
const value = await kvStore.readKey('importantKey');
console.log(value); // Outputs: importantValue
} catch (error) {
console.error('Error managing key-value store:', error);
// Environment: FEATURE_A="true", FEATURE_B="yes", FEATURE_C="1"
{
envMapping: {
featureA: 'FEATURE_A', // Result: "true" (string)
featureB: 'boolean:FEATURE_B', // Result: false (only "true" → true)
featureC: 'boolean:FEATURE_C', // Result: false (only "true" → true)
featureD: 'hard_boolean:true' // Result: true (hardcoded)
}
}
```
#### Debugging Configuration Issues in `npmextra.json`
## Advanced Patterns 🎨
To debug configuration issues, you can utilize conditional logging and checks:
### Reactive Configuration
Subscribe to configuration changes:
```typescript
import { Npmextra } from '@push.rocks/npmextra';
const npmExtraInstance = new Npmextra();
const toolConfig = npmExtraInstance.dataFor('toolname', {
configKey1: 'defaultValue1',
configKey2: 'defaultValue2'
const kvStore = new KeyValueStore<Config>({
typeArg: 'custom',
identityArg: 'myApp'
});
if (!toolConfig.configKey1) {
console.error('configKey1 is missing in npmextra.json');
// Subscribe to changes
kvStore.changeSubject.subscribe((newData) => {
console.log('Configuration changed:', newData);
});
// Changes trigger notifications
await kvStore.writeKey('theme', 'dark');
```
### Testing with Ephemeral Storage
Perfect for unit tests - no file system pollution:
```typescript
const testStore = new KeyValueStore<TestData>({
typeArg: 'ephemeral',
identityArg: 'test'
});
// All operations work normally, but nothing persists to disk
await testStore.writeKey('testKey', 'testValue');
```
### Smart Path Resolution
AppData automatically selects the best storage location:
1. Checks for `/app/data` (containerized environments)
2. Falls back to `/data` (alternate container path)
3. Uses `.nogit/appdata` (local development)
```typescript
// Automatic path selection
const appData = await AppData.createAndInit({
// No dirPath specified - smart detection
requiredKeys: ['apiKey']
});
// Or force ephemeral for testing
const testData = await AppData.createAndInit({
ephemeral: true, // No disk persistence
requiredKeys: ['testKey']
});
```
### Waiting for Configuration
Block until required configuration is available:
```typescript
const appData = await AppData.createAndInit<Config>({
requiredKeys: ['apiKey', 'apiUrl']
});
// Wait for specific key
const apiKey = await appData.waitForAndGetKey('apiKey');
// Check missing keys
const missingKeys = await appData.logMissingKeys();
// Logs: "The following mandatory keys are missing in the appdata:
// -> apiKey,
// -> apiUrl"
```
## Real-World Example 🌍
Here's a complete example of a CLI tool using npmextra:
```typescript
import { Npmextra, AppData, KeyValueStore } from '@push.rocks/npmextra';
interface CliConfig {
githubToken: string;
openaiKey: string;
model: 'gpt-3' | 'gpt-4';
cache: {
enabled: boolean;
ttl: number;
};
}
console.log(toolConfig);
class MyCLI {
private npmextra: Npmextra;
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', {
defaultModel: 'gpt-3'
});
// Setup app data with env mapping
this.appData = await AppData.createAndInit<CliConfig>({
requiredKeys: ['githubToken', 'openaiKey'],
envMapping: {
githubToken: 'GITHUB_TOKEN',
openaiKey: 'base64:OPENAI_KEY_ENCODED',
model: 'hard:gpt-4',
cache: {
enabled: 'boolean:ENABLE_CACHE',
ttl: 'hard:3600'
}
}
});
// Initialize cache
this.cache = new KeyValueStore({
typeArg: 'userHomeDir',
identityArg: 'mycli-cache'
});
// Check for missing configuration
const missingKeys = await this.appData.logMissingKeys();
if (missingKeys.length > 0) {
console.error('Please configure the missing keys');
process.exit(1);
}
}
async run() {
await this.initialize();
const config = await this.appData.getKvStore();
const settings = await config.readAll();
console.log(`Using model: ${settings.model}`);
console.log(`Cache enabled: ${settings.cache.enabled}`);
// Use the configuration...
}
}
// Run the CLI
const cli = new MyCLI();
cli.run();
```
### Integration Tests
## API Reference 📚
Writing tests ensures that your integration with `npmextra` works as expected. Below are examples of integration tests for both `Npmextra` and `KeyValueStore`.
#### Example: Testing `Npmextra` Class
### Npmextra Class
```typescript
import { expect, tap } from '@push.rocks/tapbundle';
import { Npmextra } from '@push.rocks/npmextra';
let npmExtraInstance: Npmextra;
tap.test('should create an instance of Npmextra', async () => {
npmExtraInstance = new Npmextra();
expect(npmExtraInstance).toBeInstanceOf(Npmextra);
});
tap.test('should load configuration from npmextra.json', async () => {
const config = npmExtraInstance.dataFor('toolname', {
defaultKey1: 'defaultValue1',
});
expect(config).toHaveProperty('defaultKey1');
});
tap.start();
new Npmextra(cwdArg?: string)
```
- `cwdArg`: Optional working directory path
#### Example: Testing `KeyValueStore` Class
**Methods:**
- `dataFor<T>(toolName: string, defaultOptions: T): T` - Get merged configuration
### KeyValueStore Class
```typescript
import { expect, tap } from '@push.rocks/tapbundle';
import { KeyValueStore } from '@push.rocks/npmextra';
let kvStore: KeyValueStore<{ key1: string, key2: number }>;
tap.test('should create a KeyValueStore instance', async () => {
kvStore = new KeyValueStore({
typeArg: 'userHomeDir',
identityArg: 'testApp'
});
expect(kvStore).toBeInstanceOf(KeyValueStore);
});
tap.test('should write and read back a value', async () => {
await kvStore.writeKey('key1', 'value1');
const result = await kvStore.readKey('key1');
expect(result).toEqual('value1');
});
tap.test('should write and read back multiple values', async () => {
await kvStore.writeAll({ key1: 'updatedValue1', key2: 2 });
const result = await kvStore.readAll();
expect(result).toEqual({ key1: 'updatedValue1', key2: 2 });
});
tap.start();
new KeyValueStore<T>(options: {
typeArg: 'custom' | 'userHomeDir' | 'ephemeral';
identityArg: string;
customPath?: string;
mandatoryKeys?: Array<keyof T>;
})
```
### Summary
**Methods:**
- `readKey(key: string): Promise<T>` - Read single value
- `writeKey(key: string, value: T): Promise<void>` - Write single value
- `readAll(): Promise<T>` - Read all values
- `writeAll(data: T): Promise<void>` - Write all values
- `deleteKey(key: string): Promise<void>` - Delete a key
- `getMissingMandatoryKeys(): Promise<string[]>` - Check missing required keys
- `waitForKeysPresent(keys: string[]): Promise<void>` - Wait for keys
By centralizing configuration management and offering a versatile key-value store, `@push.rocks/npmextra` significantly simplifies the setup and management of tools and settings in modern JavaScript and TypeScript projects. Whether you're managing project-wide configurations or need persistent storage for key-value pairs, `npmextra` provides an efficient and streamlined solution. Leveraging these robust features will ensure your project is well-configured and maintainable.
### AppData Class
```typescript
await AppData.createAndInit<T>(options: {
dirPath?: string;
requiredKeys?: Array<keyof T>;
ephemeral?: boolean;
envMapping?: PartialDeep<T>;
overwriteObject?: PartialDeep<T>;
})
```
**Methods:**
- `getKvStore(): Promise<KeyValueStore<T>>` - Get underlying store
- `logMissingKeys(): Promise<Array<keyof T>>` - Log and return missing keys
- `waitForAndGetKey<K>(key: K): Promise<T[K]>` - Wait for and retrieve key
## License and Legal Information
@@ -308,4 +476,4 @@ Registered at District court Bremen HRB 35230 HB, Germany
For any legal inquiries or if you require further information, please contact us via email at hello@task.vc.
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.

View File

@@ -1,5 +1,5 @@
{
"testTool":{
"testValue":2
}
}
"testTool": {
"testValue": 2
}
}

View File

@@ -1,5 +1,4 @@
import { expect, tap } from '@push.rocks/tapbundle';
import path = require('path');
import { expect, tap } from '@git.zone/tstest/tapbundle';
// module to test
import * as npmextra from '../ts/index.js';

View File

@@ -1,4 +1,4 @@
import { expect, tap } from '@push.rocks/tapbundle';
import { expect, tap } from '@git.zone/tstest/tapbundle';
import * as npmextra from '../ts/index.js';
@@ -29,4 +29,4 @@ tap.test('expect to add an object to the kv Store', async () => {
await expect(await myKeyValueStore.readKey('myKey')).toEqual('myValue');
});
tap.start();
export default tap.start();

View File

@@ -1,5 +1,4 @@
import { expect, tap } from '@push.rocks/tapbundle';
import path = require('path');
import { expect, tap } from '@git.zone/tstest/tapbundle';
// module to test
import * as npmextra from '../ts/index.js';
@@ -16,11 +15,14 @@ tap.test('should state wether a npmextra.json exists', async () => {
expect(testNpmextra.npmextraJsonExists).toBeTrue();
});
tap.test('should pass through default value, if not overriden by config from file', async () => {
let testData = testNpmextra.dataFor('testTool', { someKey2: 'someValue2' });
console.log(testData);
expect(testData).toHaveProperty('someKey2');
});
tap.test(
'should pass through default value, if not overriden by config from file',
async () => {
let testData = testNpmextra.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', {

View File

@@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@push.rocks/npmextra',
version: '5.1.2',
version: '5.1.4',
description: 'A utility to enhance npm with additional configuration, tool management capabilities, and a key-value store for project setups.'
}

View File

@@ -26,7 +26,7 @@ export class AppData<T = any> {
* @returns
*/
public static async createAndInit<T = any>(
optionsArg: IAppDataOptions<T> = {}
optionsArg: IAppDataOptions<T> = {},
): Promise<AppData<T>> {
const appData = new AppData<T>(optionsArg);
await appData.readyDeferred.promise;
@@ -76,14 +76,14 @@ export class AppData<T = any> {
if (this.options.envMapping) {
const qenvInstance = new plugins.qenv.Qenv(
process.cwd(),
plugins.path.join(process.cwd(), '.nogit')
plugins.path.join(process.cwd(), '.nogit'),
);
// Recursive function to handle nested objects, now includes key parameter
const processEnvMapping = async (
key: keyof T,
mappingValue: any,
parentKey: keyof T | '' = ''
parentKey: keyof T | '' = '',
): Promise<any> => {
if (typeof mappingValue === 'string') {
let envValue: string | boolean | T[keyof T];
@@ -97,43 +97,57 @@ export class AppData<T = any> {
convert = 'boolean';
break;
case mappingValue.startsWith('hard_json:'):
envValue = JSON.parse(mappingValue.replace('hard_json:', '')) as T[keyof T];
envValue = JSON.parse(
mappingValue.replace('hard_json:', ''),
) as T[keyof T];
convert = 'json';
break;
case mappingValue.startsWith('hard_base64:'):
envValue = Buffer.from(
mappingValue.replace('hard_base64:', ''),
'base64'
'base64',
).toString() as T[keyof T];
convert = 'base64';
break;
case mappingValue.startsWith('boolean:'):
envValue = (await qenvInstance.getEnvVarOnDemand(
mappingValue.replace('boolean:', '')
mappingValue.replace('boolean:', ''),
)) as T[keyof T];
convert = 'boolean';
break;
case mappingValue.startsWith('json:'):
envValue = (await qenvInstance.getEnvVarOnDemand(
mappingValue.replace('json:', '')
mappingValue.replace('json:', ''),
)) as T[keyof T];
convert = 'json';
break;
case mappingValue.startsWith('base64:'):
envValue = (await qenvInstance.getEnvVarOnDemand(
mappingValue.replace('base64:', '')
mappingValue.replace('base64:', ''),
)) as T[keyof T];
convert = 'base64';
break;
default:
envValue = (await qenvInstance.getEnvVarOnDemand(mappingValue)) as T[keyof T];
envValue = (await qenvInstance.getEnvVarOnDemand(
mappingValue,
)) as T[keyof T];
break;
}
// lets format the env value
if (envValue) {
if (typeof envValue === 'string' && convert === 'boolean') {
envValue = envValue === 'true';
if (envValue !== undefined && envValue !== null) {
if (convert === 'boolean') {
// Handle both string and boolean inputs
if (typeof envValue === 'boolean') {
// Already boolean, keep as-is
envValue = envValue;
} else if (typeof envValue === 'string') {
// Convert string to boolean
envValue = envValue === 'true';
} else {
// Any other type, convert to boolean
envValue = Boolean(envValue);
}
}
if (
typeof envValue === 'string' &&
@@ -160,7 +174,11 @@ export class AppData<T = any> {
for (const innerKey in mappingValue) {
const nestedValue = mappingValue[innerKey];
// For nested objects, call recursively but do not immediately write to kvStore
const nestedResult = await processEnvMapping(innerKey as keyof T, nestedValue, key);
const nestedResult = await processEnvMapping(
innerKey as keyof T,
nestedValue,
key,
);
resultObject[innerKey as keyof T] = nestedResult;
}
if (parentKey === '') {
@@ -179,8 +197,13 @@ export class AppData<T = any> {
if (this.options.overwriteObject) {
for (const key of Object.keys(this.options.overwriteObject)) {
console.log(`-> heads up: overwriting key ${key} from options.overwriteObject`);
await this.kvStore.writeKey(key as keyof T, this.options.overwriteObject[key]);
console.log(
`-> heads up: overwriting key ${key} from options.overwriteObject`,
);
await this.kvStore.writeKey(
key as keyof T,
this.options.overwriteObject[key],
);
}
}
}
@@ -202,8 +225,8 @@ export class AppData<T = any> {
if (missingMandatoryKeys.length > 0) {
console.log(
`The following mandatory keys are missing in the appdata:\n -> ${missingMandatoryKeys.join(
',\n -> '
)}`
',\n -> ',
)}`,
);
} else {
console.log('All mandatory keys are present in the appdata');
@@ -211,7 +234,9 @@ export class AppData<T = any> {
return missingMandatoryKeys;
}
public async waitForAndGetKey<K extends keyof T>(keyArg: K): Promise<T[K] | undefined> {
public async waitForAndGetKey<K extends keyof T>(
keyArg: K,
): Promise<T[K] | undefined> {
await this.readyDeferred.promise;
await this.kvStore.waitForKeysPresent([keyArg]);
return this.kvStore.readKey(keyArg);

View File

@@ -39,7 +39,7 @@ export class KeyValueStore<T = any> {
this.deletedObject = {};
await plugins.smartfile.memory.toFs(
plugins.smartjson.stringifyPretty(this.dataObject),
this.filePath
this.filePath,
);
}
const newStateString = plugins.smartjson.stringify(this.dataObject);
@@ -62,10 +62,16 @@ export class KeyValueStore<T = any> {
}
if (this.customPath) {
// Use custom path if provided
const absolutePath = plugins.smartpath.transform.makeAbsolute(this.customPath, paths.cwd);
const absolutePath = plugins.smartpath.transform.makeAbsolute(
this.customPath,
paths.cwd,
);
this.filePath = absolutePath;
if (plugins.smartfile.fs.isDirectorySync(this.filePath)) {
this.filePath = plugins.path.join(this.filePath, this.identity + '.json');
this.filePath = plugins.path.join(
this.filePath,
this.identity + '.json',
);
}
plugins.smartfile.fs.ensureFileSync(this.filePath, '{}');
return;
@@ -129,7 +135,10 @@ export class KeyValueStore<T = any> {
/**
* writes a specific key to the keyValueStore
*/
public async writeKey<K extends keyof T>(keyArg: K, valueArg: T[K]): Promise<void> {
public async writeKey<K extends keyof T>(
keyArg: K,
valueArg: T[K],
): Promise<void> {
await this.writeAll({
[keyArg]: valueArg,
} as unknown as Partial<T>);
@@ -174,22 +183,28 @@ export class KeyValueStore<T = any> {
}
private setMandatoryKeys(keys: Array<keyof T>) {
keys.forEach(key => this.mandatoryKeys.add(key));
keys.forEach((key) => this.mandatoryKeys.add(key));
}
public async getMissingMandatoryKeys(): Promise<Array<keyof T>> {
await this.readAll();
return Array.from(this.mandatoryKeys).filter(key => !(key in this.dataObject));
return Array.from(this.mandatoryKeys).filter(
(key) => !(key in this.dataObject),
);
}
public async waitForKeysPresent<K extends keyof T>(keysArg: K[]): Promise<void> {
const missingKeys = keysArg.filter(keyArg => !this.dataObject[keyArg]);
public async waitForKeysPresent<K extends keyof T>(
keysArg: K[],
): Promise<void> {
const missingKeys = keysArg.filter((keyArg) => !this.dataObject[keyArg]);
if (missingKeys.length === 0) {
return;
}
return new Promise<void>((resolve, reject) => {
const subscription = this.changeSubject.subscribe(() => {
const missingKeys = keysArg.filter(keyArg => !this.dataObject[keyArg]);
const missingKeys = keysArg.filter(
(keyArg) => !this.dataObject[keyArg],
);
if (missingKeys.length === 0) {
subscription.unsubscribe();
resolve();
@@ -198,8 +213,10 @@ export class KeyValueStore<T = any> {
});
}
public async waitForAndGetKey<K extends keyof T>(keyArg: K): Promise<T[K] | undefined> {
public async waitForAndGetKey<K extends keyof T>(
keyArg: K,
): Promise<T[K] | undefined> {
await this.waitForKeysPresent([keyArg]);
return this.readKey(keyArg);
}
}
}

View File

@@ -27,7 +27,10 @@ export class Npmextra {
/**
* merges the supplied options with the ones from npmextra.json
*/
dataFor<IToolConfig>(toolnameArg: string, defaultOptionsArg: any): IToolConfig {
dataFor<IToolConfig>(
toolnameArg: string,
defaultOptionsArg: any,
): IToolConfig {
let npmextraToolOptions;
if (this.npmextraJsonData[toolnameArg]) {
npmextraToolOptions = this.npmextraJsonData[toolnameArg];
@@ -45,7 +48,9 @@ export class Npmextra {
* checks if the JSON exists
*/
private checkNpmextraJsonExists() {
this.npmextraJsonExists = plugins.smartfile.fs.fileExistsSync(this.lookupPath);
this.npmextraJsonExists = plugins.smartfile.fs.fileExistsSync(
this.lookupPath,
);
}
/**
@@ -64,7 +69,9 @@ export class Npmextra {
*/
private checkNpmextraJsonData() {
if (this.npmextraJsonExists) {
this.npmextraJsonData = plugins.smartfile.fs.toObjectSync(this.lookupPath);
this.npmextraJsonData = plugins.smartfile.fs.toObjectSync(
this.lookupPath,
);
} else {
this.npmextraJsonData = {};
}

View File

@@ -4,7 +4,7 @@ import * as plugins from './npmextra.plugins.js';
export let cwd = process.cwd();
export let packageDir = plugins.path.join(
plugins.smartpath.get.dirnameFromImportMetaUrl(import.meta.url),
'../'
'../',
);
// ----------------------

View File

@@ -1,8 +1,6 @@
import * as tsclass from '@tsclass/tsclass';
export {
tsclass
}
export { tsclass };
import * as qenv from '@push.rocks/qenv';
import * as smartlog from '@push.rocks/smartlog';
@@ -14,4 +12,14 @@ import * as smartpromise from '@push.rocks/smartpromise';
import * as smartrx from '@push.rocks/smartrx';
import * as taskbuffer from '@push.rocks/taskbuffer';
export { qenv, smartlog, path, smartfile, smartjson, smartpath, smartpromise, smartrx, taskbuffer };
export {
qenv,
smartlog,
path,
smartfile,
smartjson,
smartpath,
smartpromise,
smartrx,
taskbuffer,
};

View File

@@ -1,14 +1,15 @@
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"useDefineForClassFields": false,
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"esModuleInterop": true,
"verbatimModuleSyntax": true
"verbatimModuleSyntax": true,
"baseUrl": ".",
"paths": {}
},
"exclude": [
"dist_*/**/*.d.ts"
]
"exclude": ["dist_*/**/*.d.ts"]
}