Compare commits

..

No commits in common. "master" and "v0.1.7" have entirely different histories.

16 changed files with 1519 additions and 19831 deletions

152
.eslintrc.json Normal file
View File

@ -0,0 +1,152 @@
{
"env": {
"es6": true,
"node": true
},
"extends": "eslint:recommended",
"globals": {
"Atomics": "readable",
"BigInt": "readable",
"BigInt64Array": "readable",
"BigUint64Array": "readable",
"queueMicrotask": "readable",
"SharedArrayBuffer": "readable",
"TextEncoder": "readable",
"TextDecoder": "readable"
},
"overrides": [
{
"files": ["*.mjs"],
"parserOptions": {
"sourceType": "module"
}
},
{
"files": ["*.cjs"],
"parserOptions": {
"sourceType": "script"
}
},
{
"files": [
"test/{,**/}*.{mjs,cjs,js}"
],
"env": {
"mocha": true
},
"globals": {
"register": "readable"
},
"rules": {
"max-len": "off",
"prefer-arrow-callback": "off"
}
}
],
"parser": "babel-eslint",
"parserOptions": {
"ecmaVersion": 10,
"ecmaFeatures": {
"globalReturn": true
},
"requireConfigFile": false,
"sourceType": "script"
},
"root": true,
"rules": {
"array-bracket-spacing": ["error", "never"],
"arrow-parens": ["error", "as-needed", {
"requireForBlockBody": true
}],
"arrow-spacing": "error",
"block-spacing": ["error", "always"],
"brace-style": ["error", "1tbs"],
"camelcase": ["error", {
"properties": "never"
}],
"comma-dangle": ["error", "never"],
"consistent-return": "error",
"eol-last": ["error", "always"],
"eqeqeq": ["error", "always", {
"null": "ignore"
}],
"func-name-matching": "error",
"indent": ["off", 2, {
"ArrayExpression": "off",
"SwitchCase": 1,
"CallExpression": {
"arguments": "off"
},
"FunctionDeclaration": {
"parameters": "off"
},
"FunctionExpression": {
"parameters": "off"
},
"MemberExpression": "off",
"ObjectExpression": "off",
"ImportDeclaration": "off"
}],
"handle-callback-err": "off",
"linebreak-style": ["error", "unix"],
"max-len": ["error", {
"code": 80,
"ignorePattern": "function \\w+\\(",
"ignoreUrls": true
}],
"max-statements-per-line": ["error", {
"max": 1
}],
"new-cap": ["error", {
"newIsCap": true,
"capIsNew": false
}],
"new-parens": "error",
"no-buffer-constructor": "error",
"no-console": "off",
"no-extra-semi": "off",
"no-fallthrough": "off",
"no-func-assign": "off",
"no-implicit-coercion": "error",
"no-multi-assign": "error",
"no-multiple-empty-lines": ["error", {
"max": 1
}],
"no-nested-ternary": "error",
"no-param-reassign": "off",
"no-return-assign": "error",
"no-return-await": "off",
"no-shadow-restricted-names": "error",
"no-tabs": "error",
"no-trailing-spaces": "error",
"no-unused-vars": ["error", {
"vars": "all",
"args": "none",
"ignoreRestSiblings": false
}],
"no-use-before-define": ["error", {
"functions": false,
"classes": false
}],
"no-useless-escape": "off",
"no-var": "error",
"nonblock-statement-body-position": ["error", "below"],
"padded-blocks": ["error", "never"],
"prefer-arrow-callback": "error",
"prefer-const": ["error", {
"destructuring": "all",
"ignoreReadBeforeAssign": true
}],
"prefer-template": "off",
"quotes": ["error", "single"],
"semi": ["error", "always"],
"spaced-comment": ["error", "always", {
"exceptions": ["!"]
}],
"space-before-blocks": "error",
"strict": "error",
"unicode-bom": ["error", "never"],
"valid-jsdoc": "error",
"wrap-iife": ["error", "inside"]
}
}

View File

@ -1,13 +0,0 @@
name: Build/Publish
on:
push:
branches:
- master
- develop
- develop-*
jobs:
main:
uses: lumeweb/github-node-deploy-workflow/.github/workflows/main.yml@master
secrets: inherit

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
build/
node_modules/
npm-debug.log

17
.npmignore Normal file
View File

@ -0,0 +1,17 @@
.babel*
.bmocharc*
.bpkgignore
.editorconfig
.eslint*
.git*
.mocharc*
.yarnignore
bench/
build/
docs/
node_modules/
npm-debug.log
package-lock.json
test/
webpack.*.js
yarn.lock

View File

@ -1,6 +0,0 @@
{
"preset": [
"presetter-preset-hybrid",
"@lumeweb/node-library-preset"
]
}

View File

@ -1,35 +0,0 @@
## [0.1.15](https://git.lumeweb.com/LumeWeb/relay-cfg/compare/v0.1.14...v0.1.15) (2023-07-07)
### Bug Fixes
* need to use set, not objectPath directly ([82f603c](https://git.lumeweb.com/LumeWeb/relay-cfg/commit/82f603c00a44f6bf82da8b26acb4e37ad2df33ae))
## [0.1.14](https://git.lumeweb.com/LumeWeb/relay-cfg/compare/v0.1.13...v0.1.14) (2023-07-07)
### Bug Fixes
* use regex ([4475f12](https://git.lumeweb.com/LumeWeb/relay-cfg/commit/4475f122f12ccd7ecbaf055eb8ffc2596d4c71aa))
## [0.1.13](https://git.lumeweb.com/LumeWeb/relay-cfg/compare/v0.1.12...v0.1.13) (2023-07-07)
### Bug Fixes
* bad for loop ([ecb2990](https://git.lumeweb.com/LumeWeb/relay-cfg/commit/ecb29905726324165548f06ac505d0edfa3c4028))
## [0.1.12](https://git.lumeweb.com/LumeWeb/relay-cfg/compare/v0.1.11...v0.1.12) (2023-07-07)
## [0.1.11](https://git.lumeweb.com/LumeWeb/relay-cfg/compare/v0.1.10...v0.1.11) (2023-07-07)
## [0.1.10](https://git.lumeweb.com/LumeWeb/relay-cfg/compare/v0.1.9...v0.1.10) (2023-07-07)
## [0.1.9](https://git.lumeweb.com/LumeWeb/relay-cfg/compare/v0.1.8...v0.1.9) (2023-07-04)
## [0.1.8](https://git.lumeweb.com/LumeWeb/relay-cfg/compare/v0.1.7...v0.1.8) (2023-07-04)
### Bug Fixes
* fix or disable errors in eslint ([1b5a7c0](https://git.lumeweb.com/LumeWeb/relay-cfg/commit/1b5a7c0605e2dcda3f379c0b61baccf0a3dfc257))

View File

@ -1,6 +1,6 @@
This software is licensed under the MIT License. This software is licensed under the MIT License.
Copyright (c) 2017, Christopher Jeffrey (https://github.com/chjj), 2022 Hammer Technologies LLC Copyright (c) 2017, Christopher Jeffrey (https://github.com/chjj)
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -1 +1,55 @@
# relay-cfg # bcfg
Config parser (used for bcoin).
## Usage
``` js
const Config = require('bcfg');
// Will consider ~/.my-module the prefix directory.
const config = new Config('my-module', {
alias: {
'n': 'network'
}
});
// Inject some custom options first.
config.inject({
some: 'user',
options: 'here'
});
config.load({
// Parse URL hash
hash: true,
// Parse querystring
query: true,
// Parse environment
env: true,
// Parse args
argv: true
});
// Will parse ~/.my-module/my-config.conf (throws on FS error).
config.open('my-config.conf');
// These will cast types and throw on incorrect type.
console.log(config.str('username'));
console.log(config.str('password'));
console.log(config.uint('userid'));
console.log(config.float('percent'));
console.log(config.bool('initialize'));
```
## Contribution and License Agreement
If you contribute code to this project, you are implicitly allowing your code
to be distributed under the MIT license. You are also implicitly verifying that
all code is your original work. `</legalese>`
## License
- Copyright (c) 2017, Christopher Jeffrey (MIT License).
See LICENSE for more info.

9
lib/bcfg.js Normal file
View File

@ -0,0 +1,9 @@
/*!
* bcfg.js - configuration parsing for bcoin
* Copyright (c) 2016-2017, Christopher Jeffrey (MIT License).
* https://github.com/bcoin-org/bcoin
*/
'use strict';
module.exports = require('./config');

1204
lib/config.js Normal file

File diff suppressed because it is too large Load Diff

3
lib/fs-browser.js Normal file
View File

@ -0,0 +1,3 @@
'use strict';
exports.unsupported = true;

3
lib/fs.js Normal file
View File

@ -0,0 +1,3 @@
'use strict';
module.exports = require('fs');

19139
npm-shrinkwrap.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,47 +1,33 @@
{ {
"name": "@lumeweb/relay-cfg", "name": "bcfg",
"version": "0.1.15", "version": "0.1.7",
"main": "lib/index.js", "description": "Config parser for bcoin",
"license": "MIT", "keywords": [
"type": "module", "conf",
"types": "lib/index.d.ts", "config"
"module": "lib/index.mjs",
"exports": {
".": {
"require": "./lib/index.js",
"import": "./lib/index.mjs"
},
"./package.json": "./package.json"
},
"authors": [
"Christopher Jeffrey <chjjeffrey@gmail.com>",
"Hammer Technologies LLC <contact@lumeweb.com>"
], ],
"repository": { "license": "MIT",
"type": "git", "repository": "git://github.com/bcoin-org/bcfg.git",
"url": "gitea@git.lumeweb.com:LumeWeb/relay-cfg.git" "homepage": "https://github.com/bcoin-org/bcfg",
"bugs": {
"url": "https://github.com/bcoin-org/bcfg/issues"
}, },
"author": "Christopher Jeffrey <chjjeffrey@gmail.com>",
"main": "./lib/bcfg.js",
"scripts": { "scripts": {
"prepare": "presetter bootstrap", "lint": "eslint lib/ || exit 0",
"build": "run build", "test": "bmocha --reporter spec test/*-test.js"
"semantic-release": "semantic-release" },
"dependencies": {
"bsert": "~0.0.10"
}, },
"devDependencies": { "devDependencies": {
"@lumeweb/node-library-preset": "^0.2.7", "bmocha": "^2.1.0"
"presetter": "*",
"presetter-preset-hybrid": "^4.0.1"
}, },
"readme": "ERROR: No README data found!", "engines": {
"dependencies": { "node": ">=8.0.0"
"arg": "^5.0.2",
"bsert": "^0.0.12",
"deep-to-flat-object": "^1.0.1",
"object-path": "^0.11.8"
}, },
"files": [ "browser": {
"lib" "./lib/fs": "./lib/fs-browser.js"
],
"publishConfig": {
"access": "public"
} }
} }

View File

@ -1,591 +0,0 @@
/* eslint-disable eslint-comments/disable-enable-pair */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/restrict-template-expressions */
/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable sonarjs/no-duplicate-string */
/* eslint-disable no-param-reassign */
/* eslint-disable @typescript-eslint/member-ordering */
/* eslint-disable sonarjs/cognitive-complexity */
/* eslint-disable @typescript-eslint/explicit-function-return-type */
/* eslint-disable jsdoc/require-jsdoc */
/* !
* config.js - configuration parsing for bcoin
* Copyright (c) 2016-2017, Christopher Jeffrey (MIT License).
* https://github.com/bcoin-org/bcoin
*/
"use strict";
import fs from "fs";
import Path from "path";
import arg from "arg";
import assert from "bsert";
import deepToFlatObject from "deep-to-flat-object";
import objectPath from "object-path";
/**
* Config Parser
*/
export class Config {
private module: string;
private data = {};
private configProperty;
constructor(module: string, configProperty: string) {
assert(typeof module === "string");
assert(module.length > 0);
this.module = module;
this.configProperty = configProperty;
}
public inject(options: object) {
for (const key of Object.keys(options)) {
const value = options[key];
// eslint-disable-next-line default-case
switch (key) {
case "env":
case "argv":
case "config":
continue;
}
this.set(key, value);
}
}
public load() {
const args = arg({}, { permissive: true });
this.parseArg(args);
}
public openDir(dir: string) {
assert(fs.existsSync(dir), `Directory ${dir} does not exist`);
let files = fs
.readdirSync(dir)
.filter((item) => item.endsWith(".json"))
.map((item) => Path.join(dir, item));
files.forEach(this.open.bind(this));
}
public open(file: string) {
let json;
try {
json = fs.readFileSync(file, "utf8");
json = JSON.parse(json);
} catch (e) {
if (e.code === "ENOENT") {
return;
}
throw new Error(`Error parsing file ${file}: ${e.message}`);
}
assert(typeof json === "object", `Config file ${file} must be an object`);
const settings = deepToFlatObject(json);
for (const key of Object.keys(settings)) {
const value = settings[key];
this.set(key, value);
}
}
public save(file: string, data: object): void {
assert(typeof data === "object");
assert(!Array.isArray(data));
const configDir = this.str(this.configProperty) as string;
const fullPath = Path.join(configDir, `${file}.json`);
if (!fs.existsSync(configDir)) {
fs.mkdirSync(configDir, { recursive: true });
}
fs.writeFileSync(fullPath, JSON.stringify(data));
this.open(fullPath);
}
public savePath(file: string, path: string): void {
this.save(file, this.get(path));
}
public set(key: string, value: any) {
assert(typeof key === "string", "Key must be a string.");
if (value === null) {
return;
}
key = this.normalize(key);
objectPath.set(this.data, key, value);
}
public has(key: string) {
assert(typeof key === "string", "Key must be a string.");
key = key.replace(/-/g, "");
key = key.toLowerCase();
return objectPath.has(this.data, key);
}
private normalize(key: string, env = false): string {
assert(typeof key === "string", "Key must be a string.");
if (env) {
key = key.replace(/__/g, ".");
key = key.replace(/_/g, "");
} else {
key = key.replace(/-/g, "");
}
key = key.toLowerCase();
return key;
}
public get(key: string, fallback = null) {
if (Array.isArray(key)) {
const keys = key;
for (const key of keys) {
const value = this.get(key);
if (value !== null) {
return value;
}
}
return fallback;
}
assert(typeof key === "string", "Key must be a string.");
key = this.normalize(key);
return objectPath.get(this.data, key, fallback);
}
public typeOf(key: string) {
const value = this.get(key);
if (value === null) {
return "null";
}
return typeof value;
}
public str(key: string, fallback = null) {
const value = this.get(key);
if (value === null) {
return fallback;
}
if (typeof value !== "string") {
throw new Error(`${fmt(key)} must be a string.`);
}
return value;
}
public int(key, fallback = null) {
const value = this.get(key);
if (value === null) {
return fallback;
}
if (typeof value !== "string") {
if (typeof value !== "number") {
throw new Error(`${fmt(key)} must be an int.`);
}
if (!Number.isSafeInteger(value)) {
throw new Error(`${fmt(key)} must be an int.`);
}
return value;
}
if (!/^-?\d+$/.test(value)) {
throw new Error(`${fmt(key)} must be an int.`);
}
const num = parseInt(value, 10);
if (!Number.isSafeInteger(num)) {
throw new Error(`${fmt(key)} must be an int.`);
}
return num;
}
public uint(key, fallback = null) {
const value = this.int(key);
if (value === null) {
return fallback;
}
if (value < 0) {
throw new Error(`${fmt(key)} must be a uint.`);
}
return value;
}
public float(key, fallback = null) {
const value = this.get(key);
if (value === null) {
return fallback;
}
if (typeof value !== "string") {
if (typeof value !== "number") {
throw new Error(`${fmt(key)} must be a float.`);
}
if (!isFinite(value)) {
throw new Error(`${fmt(key)} must be a float.`);
}
return value;
}
if (!/^-?\d*(?:\.\d*)?$/.test(value)) {
throw new Error(`${fmt(key)} must be a float.`);
}
if (!/\d/.test(value)) {
throw new Error(`${fmt(key)} must be a float.`);
}
const num = parseFloat(value);
if (!isFinite(num)) {
throw new Error(`${fmt(key)} must be a float.`);
}
return num;
}
public ufloat(key, fallback = null) {
const value = this.float(key);
if (value === null) {
return fallback;
}
if (value < 0) {
throw new Error(`${fmt(key)} must be a positive float.`);
}
return value;
}
public fixed(key, exp, fallback = null) {
const value = this.float(key);
if (value === null) {
return fallback;
}
try {
return fromFloat(value, exp || 0);
} catch (e) {
throw new Error(`${fmt(key)} must be a fixed number.`);
}
}
public ufixed(key, exp, fallback = null) {
const value = this.fixed(key, exp);
if (value === null) {
return fallback;
}
if (value < 0) {
throw new Error(`${fmt(key)} must be a positive fixed number.`);
}
return value;
}
public bool(key, fallback = null) {
const value = this.get(key);
if (value === null) {
return fallback;
}
// Bitcoin Core compat.
if (typeof value === "number") {
if (value === 1) {
return true;
}
if (value === 0) {
return false;
}
}
if (typeof value !== "string") {
if (typeof value !== "boolean") {
throw new Error(`${fmt(key)} must be a boolean.`);
}
return value;
}
if (value === "true" || value === "1") {
return true;
}
if (value === "false" || value === "0") {
return false;
}
throw new Error(`${fmt(key)} must be a boolean.`);
}
public buf(key: string, fallback = null, enc: BufferEncoding = "hex") {
const value = this.get(key);
if (value === null) {
return fallback;
}
if (typeof value !== "string") {
if (!Buffer.isBuffer(value)) {
throw new Error(`${fmt(key)} must be a buffer.`);
}
return value;
}
const data = Buffer.from(value, enc);
if (data.length !== Buffer.byteLength(value, enc)) {
throw new Error(`${fmt(key)} must be a ${enc} string.`);
}
return data;
}
public array(key: string, fallback = null) {
const value = this.get(key);
if (value === null) {
return fallback;
}
if (typeof value !== "string") {
if (!Array.isArray(value)) {
throw new Error(`${fmt(key)} must be an array.`);
}
return value;
}
const parts = value.trim().split(/\s*,\s*/);
const result: string[] = [];
for (const part of parts) {
if (part.length === 0) {
continue;
}
result.push(part);
}
return result;
}
public obj(key: string, fallback = null) {
const value = this.get(key);
if (value === null) {
return fallback;
}
if (typeof value !== "object" || Array.isArray(value)) {
throw new Error(`${fmt(key)} must be an object.`);
}
return value;
}
public func(key: string, fallback = null) {
const value = this.get(key);
if (value === null) {
return fallback;
}
if (typeof value !== "function") {
throw new Error(`${fmt(key)} must be a function.`);
}
return value;
}
public mb(key: string, fallback = null) {
const value = this.uint(key);
if (value === null) {
return fallback;
}
// eslint-disable-next-line @typescript-eslint/no-magic-numbers
return value * 1024 * 1024;
}
public parseArg(args: arg.Result<any>) {
const argPairs = args._.reduce((prev: any, item: any) => {
const parts = item.split("=");
const key = parts[0].replace(/-/g, "");
prev[key] = parts[1];
return prev;
}, {});
// eslint-disable-next-line @typescript-eslint/no-for-in-array
for (const key in argPairs) {
this.set(key, argPairs[key]);
}
}
public parseEnv(env?: object) {
let prefix = this.module;
prefix = prefix.toUpperCase();
prefix = prefix.replace(/-/g, "_");
prefix += "_";
if (!env || typeof env !== "object") {
env = process.env;
}
assert(typeof env === "object");
for (let key of Object.keys(env)) {
const value = env[key];
assert(typeof value === "string");
if (key.indexOf(prefix) !== 0) {
continue;
}
if (!isUpperKey(key)) {
continue;
}
key = key.substring(prefix.length);
key = this.normalize(key, true);
if (value.length === 0) {
continue;
}
objectPath.set(this.data, key);
}
}
}
/*
* Helpers
*/
function fmt(key: string[] | string | number) {
if (Array.isArray(key)) {
key = key[0];
}
if (typeof key === "number") {
return `Argument #${key}`;
}
return key;
}
function isAlpha(str: string) {
return /^[a-z0-9_-]+$/i.test(str);
}
function isKey(key: string) {
return /^[a-zA-Z0-9]+$/.test(key);
}
function isUpperKey(key: string) {
if (!isKey(key)) {
return false;
}
return !/[a-z]/.test(key);
}
function fromFloat(num: number, exp: number) {
assert(typeof num === "number" && isFinite(num));
assert(Number.isSafeInteger(exp));
let str = num.toFixed(exp);
let sign = 1;
if (str.length > 0 && str[0] === "-") {
str = str.substring(1);
sign = -1;
}
let hi: number | string = str;
let lo: number | string = "0";
const index = str.indexOf(".");
if (index !== -1) {
hi = str.substring(0, index);
lo = str.substring(index + 1);
}
hi = hi.replace(/^0+/, "");
lo = lo.replace(/0+$/, "");
assert(hi.length <= 16 - exp, "Fixed number string exceeds 2^53-1.");
assert(lo.length <= exp, "Too many decimal places in fixed number string.");
if (hi.length === 0) {
hi = "0";
}
while (lo.length < exp) {
lo += "0";
}
if (lo.length === 0) {
lo = "0";
}
assert(
/^\d+$/.test(hi) && /^\d+$/.test(lo),
"Non-numeric characters in fixed number string.",
);
hi = parseInt(hi, 10);
lo = parseInt(lo, 10);
const mult = Math.pow(10, exp);
const maxLo = Number.MAX_SAFE_INTEGER % mult;
const maxHi = (Number.MAX_SAFE_INTEGER - maxLo) / mult;
assert(
hi < maxHi || (hi === maxHi && lo <= maxLo),
"Fixed number string exceeds 2^53-1.",
);
return sign * (hi * mult + lo);
}

41
test/bcfg-test.js Normal file
View File

@ -0,0 +1,41 @@
'use strict';
const assert = require('bsert');
const Config = require('../lib/config');
describe('bcfg', function() {
it('should filter options', () => {
const options = {
testString: 'hello',
childTestString: 'goodbye'
};
const parent = new Config('bcfg');
parent.inject(options);
parent.load(options);
assert.strictEqual(parent.str('test-string'), 'hello');
const child = parent.filter('child');
assert.strictEqual(child.str('test-string'), 'goodbye');
});
it('should filter argv', () => {
const parent = new Config('bcfg');
// process.argv
parent.parseArg([
'node',
'bcfg',
'--test-string=hello',
'--child-test-string=goodbye'
]);
assert.strictEqual(parent.str('test-string'), 'hello');
const child = parent.filter('child');
assert.strictEqual(child.str('test-string'), 'goodbye');
});
});