diff --git a/CHANGELOG.md b/CHANGELOG.md
index 428f035..2494997 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,10 +2,50 @@
## [Unreleased][unreleased]
-
+- Support latest:21 node version
+- Removed parser (maybe temporary)
+- TypeScript .d.ts support
+- Schema field with multiple type variants now works only with special type union
+- Modular mechanism (internal rework): **How it works**
+
+ ```js
+ const schema = new Schema();
+ schema.register((schema, options, plan) => {}); //? Register new module
+ ```
+
+ By default registrated modules are:
+
+ - Metatest module (Adds tests for prototypes)
+ - Metatype module (Typescript parser)
+ - Handyman (Quality of life module)
+
+ But you also remove default modules:
+
+ ```js
+ Schema.modules.delete('metatest');
+ ```
+
+- New shorthands for:
+ - enum example: ['winter', 'spring', 'summer, 'autumn']
+ - tuple example: ['string', 'number']
+ - object example: { a: string, b: number }
+ - string example: 'string'
+ - schema example: 'MyGlobalSchema'
+ - schema#2 example: new Schema('string')
+- Removed preprocessor mechanism
+- Schemas now can be part of plan
+- Performance improvements (by removing unnecessary modules)
+- Lightweight inheritance
+- Removed type JSON (temporary)
+- Prototype chaining
+- Partial testing
+- New prototypes:
+ - Tuple
+ - Record
+ - Schema
+ - Union
## [0.3.0][] - 2023-10-19
diff --git a/README.md b/README.md
index 1dd23a0..1e8fc7b 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,52 @@
Copyright © 2023 Astrohelm contributors.
This library is MIT licensed.
diff --git a/index.js b/index.js
index dc03568..94eea91 100644
--- a/index.js
+++ b/index.js
@@ -1,3 +1,35 @@
'use strict';
-module.exports = require('./lib/schema');
+const Forge = require('./lib/forge');
+const createError = require('./lib/error');
+Schema.modules = require('./modules');
+module.exports = Schema;
+
+const MODULE_ERROR = 'Module already exists: ';
+function Schema(plan, options = {}) {
+ const { modules = Schema.modules, errorFormat, prototypes } = options;
+ const [SchemaError, warnings] = [createError({ format: errorFormat }), []];
+ [this.tools, this.modules] = [{ Error: SchemaError, build, warn }, new Map()];
+ const forge = new Forge(this, prototypes);
+ this.child = (a = plan, b = options) => new Schema(a, b === options ? b : { ...b, ...options });
+ this.register = (name, module) => {
+ if (this.modules.has(module)) warn({ cause: MODULE_ERROR, plan: this.modules, sample: module });
+ module(this, options, plan), this.modules.set(name);
+ return this;
+ };
+
+ [this.forge, this.warnings] = [forge, warnings];
+ for (const [name, plugin] of modules.entries()) this.register(name, plugin);
+ return Object.assign(this, this.tools.build(plan));
+
+ function build(plan) {
+ const Type = forge.get(plan.$type);
+ if (Type) return new Type(plan);
+ throw new Error('Building error: recieved wrong plan:\n' + JSON.stringify(plan));
+ }
+
+ function warn(options) {
+ const warn = new SchemaError({ path: 'BUILD', ...options });
+ return warnings.push(warn), warn;
+ }
+}
diff --git a/lib/builder/index.js b/lib/builder/index.js
deleted file mode 100644
index 674c5e8..0000000
--- a/lib/builder/index.js
+++ /dev/null
@@ -1,21 +0,0 @@
-'use strict';
-
-const preprocess = require('./preprocess');
-
-//? [Preprocess] Purpose: find, join and build prototype by plan
-module.exports = (types, tools, plan) => {
- const prototypes = preprocess(types, tools, plan);
- if (prototypes.length === 1) return prototypes[0];
- if (!prototypes.length) return { test: () => [], ts: () => 'unknown', debug: 'Check warnings' };
- const { condition: conditionName = 'anyof' } = plan;
-
- return {
- prototypes,
- type: () => 'object',
- test: (sample, path = 'root') => {
- const createError = cause => new tools.Error({ cause, path, sample });
- const condition = tools.getCondition(conditionName, prototypes.length - 1);
- return tools.runCondition(condition, { path, sample, createError, prototypes });
- },
- };
-};
diff --git a/lib/builder/preprocess.js b/lib/builder/preprocess.js
deleted file mode 100644
index fa6756d..0000000
--- a/lib/builder/preprocess.js
+++ /dev/null
@@ -1,30 +0,0 @@
-'use strict';
-
-const path = 'PREPROCESS';
-const { string: astropack } = require('astropack');
-const { typeOf, isShorthand } = require('../schema/tools');
-const ERR_SHORTHAND = 'Shorthand usage with non-scalar schema';
-const ERR_MISSING_PROTO = 'Missing prototype';
-const ERR_MISSING_SCHEMA = 'Missing or wrong schema at namespace';
-
-module.exports = ({ types, namespace, rules }, tools, schema) => {
- const [prototypes, { warn }] = [[], tools];
- const signal = (cause, sample, sampleType) => warn({ sample, sampleType, path, cause });
- for (const type of typeOf(schema)) {
- if (astropack.case.isFirstUpper(type)) {
- const prototype = namespace?.get(type);
- if (prototype && prototype.test) prototypes.push(prototype);
- else signal(ERR_MISSING_SCHEMA, namespace, type);
- continue;
- }
- const Type = types.get(type);
- if (!Type || ((Type.kind !== 'scalar' || type === 'enum') && isShorthand(schema))) {
- if (!Type) signal(ERR_MISSING_PROTO, schema, type);
- else signal(ERR_SHORTHAND, schema, type);
- continue;
- }
- const prototype = new Type(schema, tools, rules);
- prototypes.push(prototype);
- }
- return prototypes;
-};
diff --git a/lib/schema/error.js b/lib/error.js
similarity index 79%
rename from lib/schema/error.js
rename to lib/error.js
index 7a7df1b..7caa4c6 100644
--- a/lib/schema/error.js
+++ b/lib/error.js
@@ -3,9 +3,9 @@
const define = (ctx, name, value) => Object.defineProperty(ctx, name, { enumerable: false, value });
const defaultFormat = dict => `[${dict.path}] => ${dict.cause}`;
-module.exports = (options = {}) => {
- const { format = defaultFormat } = options;
- return function SchemaError({ path, sample, plan, cause, sampleType }) {
+module.exports = options => {
+ const format = options?.format ?? defaultFormat;
+ return function SchemaError({ path = 'unknown', sample, plan, cause, sampleType }) {
[this.count, this.path, this.cause] = [0, path, ''];
if (sample) [this.sample, this.sampleType] = [sample, sampleType ?? typeof sample];
if (cause) this.cause = (++this.count, cause);
@@ -13,7 +13,6 @@ module.exports = (options = {}) => {
this.message = format(this);
define(this, 'toString', () => this.message);
define(this, 'toJSON', () => ({ sample, path, cause: this.cause, count: this.count }));
- define(this, 'message', format(this));
define(this, 'add', cause => {
this.cause = this.cause ? `${this.cause}, ${cause}` : cause;
this.message = (++this.count, format(this));
diff --git a/lib/forge.js b/lib/forge.js
new file mode 100644
index 0000000..276d471
--- /dev/null
+++ b/lib/forge.js
@@ -0,0 +1,48 @@
+'use strict';
+
+const { utils } = require('astropack');
+const core = require('./proto');
+
+module.exports = function Forge(schema, custom = {}) {
+ const [chains, wrappers] = [new Map(), { before: [], after: [] }];
+ const { before, after } = wrappers;
+ this.has = name => chains.has(name);
+ this.attach = (name, ...prototypes) => {
+ const protos = prototypes.map(unifyProto);
+ if (name in wrappers) return void wrappers[name].push(...protos);
+ const chain = chains.get(name) ?? [];
+ chain.push(...protos), chains.set(name, chain);
+ return void 0;
+ };
+
+ this.get = name => {
+ const chain = chains.get(name);
+ if (!chain) return null;
+ return function ForgePrototype(plan) {
+ const meta = plan.$meta;
+ if (plan.$id) this.$id = plan.$id;
+ [this.$required, this.$type, this.$plan] = [plan.$required ?? true, name, plan];
+ if (meta && typeof meta === 'object' && !Array.isArray(meta)) Object.assign(this, meta);
+ [...before, ...chain, ...after].forEach(proto => proto.call(this, plan, schema.tools));
+ if (!this.$kind) this.$kind = 'unknown';
+ };
+ };
+
+ for (const [name, proto] of [...entries(core), ...entries(custom)]) this.attach(name, proto);
+ return Object.freeze(this);
+};
+
+function unifyProto(Proto) {
+ const type = utils.isFunction(Proto);
+ if (type === 'function' || type === 'arrow') return Proto;
+ return function Prototype(plan, tools) {
+ if (type === 'class') Object.assign(this, new Proto(plan, tools));
+ if (typeof Proto.construct !== 'function') Object.assign(this, Proto);
+ else Object.assign(this, Proto.construct(plan, tools));
+ };
+}
+
+function entries(protos) {
+ if (Array.isArray(protos) && protos[0]?.length === 2) return protos;
+ return protos?.constructor.name === 'Map' ? protos.entries() : Object.entries(protos);
+}
diff --git a/lib/parser/index.js b/lib/parser/index.js
deleted file mode 100644
index e25ebe0..0000000
--- a/lib/parser/index.js
+++ /dev/null
@@ -1,19 +0,0 @@
-'use strict';
-
-const SKIP = ['undefined', 'function', 'symbol'];
-const ParseStorage = require('./store');
-
-const createParser = store => {
- const parser = sample => {
- const type = Array.isArray(sample) ? 'array' : typeof sample;
- if (SKIP.includes(type) || (type === 'object' && !sample)) return '?unknown';
- for (const [, parse] of store[type]) {
- const result = parse(sample, parser);
- if (result) return result;
- }
- return '?unknown';
- };
- return parser;
-};
-
-module.exports = { createParser, ParseStorage };
diff --git a/lib/parser/store.js b/lib/parser/store.js
deleted file mode 100644
index 5fdb037..0000000
--- a/lib/parser/store.js
+++ /dev/null
@@ -1,39 +0,0 @@
-'use strict';
-
-const STANDARD = ['string', 'number', 'bigint', 'boolean', 'object', 'array'];
-function TypedParseStorage(name, prototype) {
- const handlers = new Map();
- this.toJSON = () => handlers;
- this.toString = () => JSON.stringify(handlers);
- this[Symbol.iterator] = function* () {
- yield* handlers.entries();
- yield [name, prototype];
- };
-
- return new Proxy(this, {
- ownKeys: () => handlers.keys(),
- has: (_, prop) => handlers.has(prop),
- set: (_, prop, value) => handlers.set(prop, value),
- deleteProperty: (_, prop) => handlers.delete(prop),
- get: (target, prop) => {
- if (prop === Symbol.iterator) return this[Symbol.iterator].bind(target);
- return handlers.get(prop);
- },
- });
-}
-
-module.exports = function ParseStorage(argTypes) {
- const types = new Map([...argTypes]);
- const store = STANDARD.reduce((acc, name) => {
- acc[name] = new TypedParseStorage(name, types.get(name).parse);
- types.delete(name);
- return acc;
- }, {});
-
- for (const [name, Type] of types.entries()) {
- if (!Type.parse) continue;
- for (const type of Type.parse.targets) store[type][name] = Type.parse;
- }
-
- return types.clear(), Object.freeze(Object.assign(this, store));
-};
diff --git a/lib/proto.js b/lib/proto.js
new file mode 100644
index 0000000..b9cda3d
--- /dev/null
+++ b/lib/proto.js
@@ -0,0 +1,65 @@
+'use strict';
+
+const create = type => ({ $kind: type });
+module.exports = new Map(
+ Object.entries({
+ unknown: create('unknown'),
+ boolean: create('scalar'),
+ string: create('scalar'),
+ number: create('scalar'),
+ bigint: create('scalar'),
+ any: create('any'),
+ schema: Schema,
+ union: Union,
+ array: List,
+ tuple: List,
+ set: List,
+ record: Struct,
+ object: Struct,
+ map: Struct,
+ enum: Enum,
+ }),
+);
+
+const ENUM_WARN = 'Recieved incorrect enumerable';
+function Enum(plan, { warn }) {
+ this.$kind = 'enum';
+ const filter = el => typeof el === 'string' || typeof el === 'number';
+ this.$enum = Array.isArray(plan.enum) ? [...new Set(plan.enum)].filter(filter) : [];
+ const isFiltered = this.$enum.length !== plan.enum?.length;
+ isFiltered && warn({ cause: ENUM_WARN, plan, sample: plan.enum });
+}
+
+const ITEMS_ERROR = 'Plan items are invalid or empty';
+function List(plan, { warn, build }) {
+ this.$kind = 'struct';
+ const isArray = Array.isArray(plan.items);
+ this.$isTuple = this.$type === 'tuple' || isArray;
+ this.$items = (isArray ? plan.items : [plan.items]).map(build);
+ !this.$items.length && warn({ plan, cause: ITEMS_ERROR, sample: plan.items });
+}
+
+const PLANS_ERROR = 'Revievd plan without properties';
+function Struct(plan, { build, warn }) {
+ [this.$kind, this.$properties, this.$patterns] = ['struct', new Map(), new Map()];
+ this.$isRecord = this.$type === 'record' || plan.isRecord;
+ this.$requires = [];
+ !plan.properties && warn({ plan, sample: plan.properties, cause: PLANS_ERROR });
+ for (const [key, value] of Object.entries(plan.properties ?? {})) {
+ const builded = build(value);
+ builded.$required && this.$requires.push(key);
+ (value.isPattern ? this.$patterns : this.$properties).set(key, builded);
+ }
+}
+
+function Union(plan, { build }) {
+ this.$kind = 'union';
+ this.$condition = plan.condition ?? 'anyof';
+ this.$types = (Array.isArray(plan.types) ? plan.types : [plan]).map(build);
+}
+
+function Schema(plan) {
+ Object.assign(this, plan.schema);
+ if (plan.$id) this.$id = plan.$id;
+ this.$required = plan.$required;
+}
diff --git a/lib/proto/arrays/index.js b/lib/proto/arrays/index.js
deleted file mode 100644
index 9a163e0..0000000
--- a/lib/proto/arrays/index.js
+++ /dev/null
@@ -1,22 +0,0 @@
-'use strict';
-
-const astropack = require('astropack');
-const struct = require('./struct');
-
-const parse = (sample, parse) => {
- if (!Array.isArray(sample)) return null;
- const items = sample.reduce((acc, sample) => {
- const plan = parse(sample);
- for (const saved of acc) if (astropack.utils.equals(saved, plan)) return acc;
- return acc.push(plan), acc;
- }, []);
- if (items.length === 0) return { type: 'array', items: ['unknown'] };
- if (items.length === 1) return { type: 'array', items: [items[0]] };
- return { type: 'array', items };
-};
-
-module.exports = {
- array: struct(value => Array.isArray(value)),
- set: struct(value => value?.constructor?.name === 'Set'),
-};
-module.exports.array.parse = Object.assign(parse, { targets: ['object'] });
diff --git a/lib/proto/arrays/struct.js b/lib/proto/arrays/struct.js
deleted file mode 100644
index a43a3cd..0000000
--- a/lib/proto/arrays/struct.js
+++ /dev/null
@@ -1,31 +0,0 @@
-'use strict';
-
-const ERR_MISS = 'Data type misconfiguration, expected: ';
-const ERR_EMPTY = 'Empty list received, but required';
-module.exports = isInstance => ({
- meta: { kind: 'struct', origin: 'default' },
- construct(plan, tools) {
- const { Error, build, getCondition } = tools;
- this.required = plan.required ?? true;
- this.items = Array.isArray(plan.items) ? plan.items.map(v => build(v)) : [build(plan.items)];
- this.condition = plan.condition ?? 'anyof';
-
- this.ts = () => 'object';
- this.test = (sample, path) => {
- const createError = cause => new Error({ path, sample, plan, cause });
- if (!isInstance(sample)) return [createError(ERR_MISS + this.type)];
- const entries = [...sample];
- if (!entries.length && this.required) return [createError(ERR_EMPTY)];
- const errors = [];
- for (let i = 0; i < entries.length; ++i) {
- const condition = getCondition(this.condition, this.items.length - 1);
- const suboption = { path: `${path}[${i}]`, sample: entries[i] };
- const createError = cause => new tools.Error({ cause, plan, ...suboption });
- const option = { createError, prototypes: this.items, ...suboption };
- const result = tools.runCondition(condition, option);
- if (result.length) errors.push(...result);
- }
- return errors;
- };
- },
-});
diff --git a/lib/proto/constructor/index.js b/lib/proto/constructor/index.js
deleted file mode 100644
index f5230fa..0000000
--- a/lib/proto/constructor/index.js
+++ /dev/null
@@ -1,46 +0,0 @@
-'use strict';
-
-const defaultTest = () => [];
-const INVALID = 'Invalid prototype, missing construct method';
-const TEST_FAIL = `Didn't pass test`;
-const RULE_FAIL = `Didn't pass rule: `;
-const RULE_NOT_EXIST = `Missing rule: `;
-const { parseResult: parse, setDefault } = require('./utils');
-
-//? [Pre-preprocess] Purpose: Prototype wrapper and tester
-module.exports = (name, proto, defaultMeta) => {
- if (!proto?.construct || typeof proto.construct !== 'function') throw new Error(INVALID);
- const meta = { type: name };
- function Type(plan, tools, defaultRules) {
- Object.assign(this, meta);
- if (plan.meta) Object.assign(this, plan.meta);
- proto.construct.call(this, plan, tools), setDefault(this, plan, tools);
-
- const [test, rules] = [this.test ?? defaultTest, plan.rules ?? []];
- this.test = (sample, path = 'root') => {
- const options = { sample, path, tools, plan };
- const errors = parse(test(sample, path), options, TEST_FAIL);
- if (rules.length === 0) return errors;
- if (rules.length === 0 || (!this.required && sample === undefined)) return errors;
- for (let i = 0; i < rules.length; ++i) {
- let rule = rules[i];
- if (typeof rule === 'string') {
- const name = rule;
- rule = defaultRules?.get(rule);
- if (!rule) {
- errors.push(new tools.Error({ cause: RULE_NOT_EXIST + name, sample, path, plan }));
- continue;
- }
- }
- const result = parse(rule(sample), options, RULE_FAIL + i);
- if (result.length > 0) errors.push(...result);
- }
- return errors;
- };
- return Object.freeze(this);
- }
- if (proto.meta) Object.assign(meta, proto.meta);
- if (defaultMeta) Object.assign(meta, defaultMeta);
- if (proto.parse && Array.isArray(proto.parse.targets)) Type.parse = proto.parse;
- return Object.assign(Type, meta);
-};
diff --git a/lib/proto/constructor/utils.js b/lib/proto/constructor/utils.js
deleted file mode 100644
index f560f8e..0000000
--- a/lib/proto/constructor/utils.js
+++ /dev/null
@@ -1,23 +0,0 @@
-'use strict';
-
-const parseResult = (res, options, cause) => {
- const { sample, path, plan, tools } = options;
- if (typeof res === 'object' && Array.isArray(res)) {
- if (!res.length || res[0] instanceof tools.Error) return res;
- return res.map(v => parseResult(v, options)).flat(2);
- }
- if (typeof res === 'boolean' && !res) return [new tools.Error({ sample, path, plan, cause })];
- if (typeof res === 'string') return [new tools.Error({ sample, path, plan, cause: res })];
- if (res instanceof tools.Error) return [res];
- return [];
-};
-
-const setDefault = (ctx, plan, tools) => {
- if (typeof plan.preprocess === 'function') ctx.preprocess = plan.preprocess;
- if (typeof plan.postprocess === 'function') ctx.postprocess = plan.postprocess;
- if (!('required' in ctx)) ctx.required = tools.isRequired(plan);
- if (!ctx.origin) ctx.origin = 'custom';
- if (!ctx.type) ctx.type = 'unknown';
-};
-
-module.exports = { parseResult, setDefault };
diff --git a/lib/proto/exotic.js b/lib/proto/exotic.js
deleted file mode 100644
index f4721c4..0000000
--- a/lib/proto/exotic.js
+++ /dev/null
@@ -1,33 +0,0 @@
-'use strict';
-
-const ERR_REQUIRED = 'Value is required';
-const ERR_MISS = 'Not of expected type: object';
-const META = { kind: 'scalar', origin: 'default' };
-const any = {
- meta: META,
- construct(plan, tools) {
- const { isRequired, Error } = tools;
- this.required = isRequired(plan);
- this.ts = () => this.type;
- this.test = (sample, path) => {
- if (!(!this.required && sample === undefined)) return [];
- return [new Error({ path, sample, plan, cause: ERR_REQUIRED })];
- };
- },
-};
-
-const json = {
- meta: { kind: 'struct', origin: 'default' },
- construct(plan, tools) {
- const { isRequired, Error } = tools;
- this.required = isRequired(plan);
- this.ts = () => 'object';
- this.test = (sample, path) => {
- if (typeof sample === 'object' && sample) return [];
- if (!this.required && sample === undefined) return [];
- return [new Error({ path, sample, plan, cause: ERR_MISS })];
- };
- },
-};
-
-module.exports = { any, unknown: any, json };
diff --git a/lib/proto/index.js b/lib/proto/index.js
deleted file mode 100644
index e7efeb5..0000000
--- a/lib/proto/index.js
+++ /dev/null
@@ -1,15 +0,0 @@
-'use strict';
-
-const createType = require('./constructor');
-const prototypes = Object.entries({
- ...require('./scalars'),
- ...require('./objects/index.js'),
- ...require('./arrays'),
- ...require('./exotic'),
-});
-
-module.exports = {
- createType,
- defaultPrototypes: new Map(prototypes),
- defaultTypes: new Map(prototypes.map(([k, v]) => [k, createType(k, v)])),
-};
diff --git a/lib/proto/objects/index.js b/lib/proto/objects/index.js
deleted file mode 100644
index f077d58..0000000
--- a/lib/proto/objects/index.js
+++ /dev/null
@@ -1,18 +0,0 @@
-'use strict';
-
-const struct = require('./struct');
-
-module.exports = {
- object: struct({
- isInstance: v => typeof v === 'object',
- getKeys: v => Object.keys(v),
- getValue: (k, target) => target[k],
- setValue: (k, v, target) => (target[k] = v),
- }),
- map: struct({
- isInstance: v => v?.constructor?.name === 'Map',
- getKeys: v => [...v.keys()],
- getValue: (k, target) => target.get(k),
- setValue: (k, v, target) => target.set(k, v),
- }),
-};
diff --git a/lib/proto/objects/struct.js b/lib/proto/objects/struct.js
deleted file mode 100644
index e2117f7..0000000
--- a/lib/proto/objects/struct.js
+++ /dev/null
@@ -1,49 +0,0 @@
-'use strict';
-
-const { process, search, parser, buildProps } = require('./utils');
-const ERR_MISS = 'Data type misconfiguration, expected: ';
-const ERR_EMPTY = 'Empty object received, but required';
-const ERR_REQUIRED = `Properties and value required`;
-
-module.exports = protoTools => ({
- meta: { kind: 'struct', origin: 'default' },
- parse: Object.assign(parser(protoTools), { targets: ['object'] }),
- construct(plan, tools) {
- const { Error } = tools;
- const allowExotic = plan.allowExotic ?? false;
- const requires = buildProps(this, plan, tools);
-
- this.required = plan.required ?? true;
- this.ts = () => 'object';
- this.test = (sample, path) => {
- const createError = cause => new Error({ path, sample, plan, cause });
- if (!this.required && sample === undefined) return [];
- if (this.required && sample === undefined) return [createError(ERR_REQUIRED)];
- if (!protoTools.isInstance(sample)) return [createError(ERR_MISS + this.type)];
- const keys = protoTools.getKeys(sample);
- if (!keys.length && this.required) return [createError(ERR_EMPTY)];
- const [errors, test] = [[], process(protoTools.getValue, protoTools.setValue, sample)];
- for (const prop of keys) {
- const createError = cause => new Error({ path: `${path}.${prop}`, sample, plan, cause });
- const [prototype, schemaProp] = search(this, prop);
- if (!prototype) {
- if (!allowExotic) errors.push(createError(`Exotic property`));
- continue;
- }
- requires.delete(schemaProp ?? prop);
- const result = test(prototype, prop, `${path}.${prop}`);
- if (result.length) errors.push(...result);
- }
- if (requires.size) {
- for (const prop of requires.keys()) {
- const prototype = this.properties[prop] ?? this.patternProperties[prop];
- if (!prototype.preprocess) continue;
- const result = (requires.delete(prop), test(prototype, prop, `${path}.${prop}`));
- if (result.length) errors.push(...result);
- }
- requires.size && errors.push(createError(`Missing "${[...requires.keys()].join()}"`));
- }
- return errors;
- };
- },
-});
diff --git a/lib/proto/objects/utils.js b/lib/proto/objects/utils.js
deleted file mode 100644
index b18bc12..0000000
--- a/lib/proto/objects/utils.js
+++ /dev/null
@@ -1,49 +0,0 @@
-'use strict';
-
-const parser = tools => {
- const parse = (sample, parse) => {
- if (!tools.isInstance(sample) || !sample) return null;
- const properties = tools.getKeys(sample).reduce((acc, key) => {
- acc[key] = parse(tools.getValue(key, sample), parse);
- return acc;
- }, {});
- return { type: 'object', properties };
- };
- return parse;
-};
-
-const search = (schema, prop) => {
- if (!schema.properties) return [null, null];
- if (prop in schema.properties) return [schema.properties[prop], null];
- if (!schema.patternProperties) return [null, null];
- const entries = Object.entries(schema.patternProperties);
- if (typeof prop !== 'string') prop = String(prop);
- for (const [pattern, value] of entries) if (prop.match(pattern)) return [value, pattern];
- return [null, null];
-};
-
-const process = (get, set, target) => (proto, prop, path) => {
- const { preprocess, postprocess } = proto;
- if (preprocess) set(prop, preprocess(get(prop, target)), target);
- const result = proto.test(get(prop, target), path);
- if (postprocess) set(prop, postprocess(get(prop, target), result), target);
- return result;
-};
-
-const buildProps = (ctx, plan, tools) => {
- const requires = new Map();
- const builded = { properties: {}, patternProperties: {} };
- for (const propType in builded) {
- if (!plan[propType]) continue;
- const entries = Object.entries(plan[propType]);
- for (const [key, value] of entries) {
- builded[propType][key] = tools.build(value);
- const required = tools.isRequired(value);
- if (required) requires.set(key);
- }
- ctx[propType] = Object.assign({}, builded[propType]);
- }
- return requires;
-};
-
-module.exports = { process, parser, search, buildProps };
diff --git a/lib/proto/scalars.js b/lib/proto/scalars.js
deleted file mode 100644
index 75985ab..0000000
--- a/lib/proto/scalars.js
+++ /dev/null
@@ -1,41 +0,0 @@
-'use strict';
-
-const ACCEPTED = ['string', 'boolean', 'number', 'bigint'];
-const META = { kind: 'scalar', origin: 'default' };
-const ERR_ENUM = `Enum doesn't contain this value, enum: `;
-const ERR_MISS = 'Type missconfiguration, expected type: ';
-
-const scalar = type => ({
- meta: META,
- parse: Object.assign(() => type, { targets: [type] }),
- construct(plan, { isRequired, Error }) {
- this.required = isRequired(plan);
- this.ts = () => type;
- this.test = (sample, path) => {
- if (typeof sample === type) return [];
- if (sample === undefined && !this.required) return [];
- return [new Error({ path, sample, plan, cause: ERR_MISS + type })];
- };
- },
-});
-
-const enumerable = {
- meta: META,
- construct(plan, { isRequired, Error }) {
- this.required = isRequired(plan);
- this.enum = plan.enum?.filter(el => ACCEPTED.includes(typeof el));
- this.ts = () => 'unknown';
- this.test = (sample, path) => {
- if (this.enum.includes(sample)) return [];
- return new Error({ path, sample, plan, cause: ERR_ENUM + this.enum.join(', ') });
- };
- },
-};
-
-module.exports = {
- string: scalar('string'),
- bigint: scalar('bigint'),
- number: scalar('number'),
- boolean: scalar('boolean'),
- enum: enumerable,
-};
diff --git a/lib/schema/index.js b/lib/schema/index.js
deleted file mode 100644
index c9a7525..0000000
--- a/lib/schema/index.js
+++ /dev/null
@@ -1,43 +0,0 @@
-'use strict';
-
-const [build, createError] = [require('../builder'), require('./error')];
-const { defaultTypes, defaultPrototypes, createType } = require('../proto');
-const tooling = { Error: createError(), ...require('./tools') };
-const { ParseStorage, createParser } = require('../parser');
-
-const path = 'PREPROCESS';
-const MISS = 'Parameter type misconfiguration: ';
-function Schema(plan, params = {}) {
- if (plan instanceof Schema) return plan.child(null, params); //? Just updating parameters
- const { errorFormat, types: custom, namespace = new Map(), rules = new Map() } = params;
- const tools = { ...tooling, build: null, warn: null };
- const types = new Map([...defaultTypes]); //? Copy default types links
- tools.build = build.bind(null, { types, namespace, rules }, tools);
- tools.warn = options => {
- const err = new tooling.Error(options);
- return this.warnings.push(err), err;
- };
-
- if (errorFormat) tools.Error = createError({ format: errorFormat });
- if (custom && custom instanceof Map) {
- const entries = custom.entries();
- for (const [name, proto] of entries) {
- const prototype = defaultPrototypes.get(name) ?? proto;
- types.set(name, createType(name, prototype, proto.meta));
- }
- }
-
- this.warnings = [];
- if (!(rules instanceof Map)) tools.warn({ cause: MISS + 'Rules not a Map', path });
- if (!(namespace instanceof Map)) tools.warn({ cause: MISS + 'Namespace not a Map', path });
- Object.freeze(tools);
- Object.defineProperty(this, 'meta', { get: tools.exportMeta.bind(null, this) });
- this.parse = createParser(new ParseStorage(types));
- this.child = (p, o) => new Schema(p || plan, o ? { ...params, ...o } : params);
- if (plan) Object.assign(this, tools.build(plan)); //? Gathering test, ts methods and metadata
- return Object.freeze(this);
-}
-
-Schema.parse = createParser(new ParseStorage(defaultTypes));
-Schema.from = (plan, params) => new Schema(plan, params);
-module.exports = Schema;
diff --git a/lib/schema/tools.js b/lib/schema/tools.js
deleted file mode 100644
index 0aa433e..0000000
--- a/lib/schema/tools.js
+++ /dev/null
@@ -1,66 +0,0 @@
-'use strict';
-
-const getCondition = (name, max) => {
- let flag = false;
- if (name === 'allof') {
- return (result, i) => {
- if (result.length > 0) return ['break', 'Item did not pass one of schema'];
- if (i === max) return ['skip'];
- return ['continue'];
- };
- }
- if (name === 'oneof') {
- return (result, i) => {
- if (flag && result.length === 0) return ['break', 'Item passed more than one schema'];
- if (result.length === 0) flag = true;
- if (!flag && i === max) return ['break', 'Item did not pass all schemas'];
- if (flag && i === max) return ['skip'];
- return ['continue'];
- };
- }
- return (result, i) => {
- if (result.length !== 0 && i >= max) return ['break', 'Item did not pass all schemas'];
- if (result.length === 0) return ['skip'];
- return ['continue'];
- };
-};
-
-const runCondition = (handler, { path, sample, createError, prototypes }) => {
- const errors = [];
- for (let i = 0; i < prototypes.length; ++i) {
- const result = prototypes[i].test(sample, path);
- const [toDo, cause] = handler(result, i);
- if (cause) {
- if (result.length) errors.push(...result);
- else errors.push(createError(cause));
- }
- if (toDo === 'skip' || toDo === 'break') break;
- if (toDo === 'continue') continue;
- }
- return errors;
-};
-
-const isShorthand = schema => typeof schema === 'string';
-const isRequired = s => (isShorthand(s) ? !s.startsWith('?') : s.required ?? true);
-const typeOf = s => {
- if (isShorthand(s)) return [!s.startsWith('?') ? s : s.substring(1)];
- if (Array.isArray(s)) return s;
- if (Array.isArray(s.type)) return s.type;
- return [s.type];
-};
-
-const exportMeta = schema => {
- const [entries, meta] = [Object.entries(schema), new Map()];
- for (const [k, v] of entries) {
- if (typeof v === 'function' || k === 'warnings') continue;
- if (typeof v === 'object') {
- if (Array.isArray(v)) meta.set(k, v.map(exportMeta));
- else meta.set(k, exportMeta(v));
- continue;
- }
- meta.set(k, v);
- }
- return meta;
-};
-
-module.exports = { getCondition, isRequired, typeOf, isShorthand, exportMeta, runCondition };
diff --git a/modules/handyman/index.js b/modules/handyman/index.js
new file mode 100644
index 0000000..b1e36e3
--- /dev/null
+++ b/modules/handyman/index.js
@@ -0,0 +1,34 @@
+'use strict';
+
+const RepairKit = require('./repairkit');
+const MISSING_MODULE = 'Missing plugin: handyman';
+const MODULES_ERROR = 'Recieved sub schema with different modules, may be incompatible: ';
+const entries = v => (v?.constructor?.name === 'Map' ? v.entries() : Object.entries(v));
+
+module.exports = (schema, options) => {
+ const namespace = options.namespace ? new Map(entries(options.namespace)) : new Map();
+ const [{ tools, modules }, { build, warn }] = [schema, schema.tools];
+ const repair = new RepairKit(schema, namespace);
+ const unnamedSchemas = new Set();
+ schema.pull = name => {
+ const schema = namespace.get(name);
+ if (schema) return schema;
+ for (const [, schema] of [...namespace.entries(), ...unnamedSchemas.entries()]) {
+ if (!schema.pull) throw new Error(MISSING_MODULE);
+ const found = schema.pull(name);
+ if (found) return found;
+ }
+ return null;
+ };
+
+ tools.build = plan => {
+ const fixed = repair(plan);
+ const builded = build(fixed);
+ const { $id } = builded;
+ if (fixed.$type !== 'schema') return builded;
+ const [a, b] = [[...modules.keys()], [...builded.modules.keys()]];
+ !a.every(key => b.includes(key)) && warn({ cause: MODULES_ERROR + $id, plan, sample: b });
+ if (!$id) return unnamedSchemas.add(builded), builded;
+ return namespace.set($id, builded), builded;
+ };
+};
diff --git a/modules/handyman/repairkit.js b/modules/handyman/repairkit.js
new file mode 100644
index 0000000..2a3c227
--- /dev/null
+++ b/modules/handyman/repairkit.js
@@ -0,0 +1,61 @@
+'use strict';
+
+const { string: astropack } = require('astropack');
+const unknown = { $type: 'unknown', $required: false };
+const SCHEMA_NOT_FOUND = 'Schema not found: ';
+const TYPE_NOT_FOUND = 'Recieved unknown type: ';
+const ARRAY_TYPE_NOT_FOUND = 'Cant parse type of recieved array: ';
+module.exports = function RepairKit(schema, namespace) {
+ const shorthands = { string, object, array };
+ const { tools, forge, child } = schema;
+ return repair;
+
+ function repair(plan, warn = tools.warn) {
+ const type = Array.isArray(plan) ? 'array' : typeof plan;
+ return shorthands[type]?.(plan, warn) ?? unknown;
+ }
+
+ function string(plan, warn) {
+ const $required = plan[0] !== '?';
+ const $type = $required ? plan : plan.slice(1);
+ if (!astropack.case.isFirstUpper(plan)) {
+ if (forge.has($type)) return { $type, $required };
+ warn({ cause: TYPE_NOT_FOUND + $type, plan, sample: $type });
+ return unknown;
+ }
+ const schema = namespace.get($type); //? Schema wrapper #1
+ if (schema) return { $type: 'schema', schema, $id: $type, $required };
+ warn({ cause: SCHEMA_NOT_FOUND + $type, plan, sample: $type });
+ return unknown;
+ }
+
+ function array(plan, warn) {
+ let isArray = true;
+ const stub = () => (isArray = false);
+ for (let i = 0; i < plan.length && isArray; ++i) {
+ const fixed = repair(plan[i], stub);
+ isArray = isArray && forge.has(fixed.$type);
+ if (!isArray) break;
+ }
+ if (isArray) return { $type: 'array', items: plan, $required: true };
+ const isEnum = plan.every(item => typeof item === 'string' || typeof item === 'number');
+ if (isEnum) return { $type: 'enum', enum: plan, $required: true };
+ warn({ cause: ARRAY_TYPE_NOT_FOUND, sample: plan, plan });
+ return unknown;
+ }
+
+ function object(plan, warn) {
+ const { $required = true, $id, ...fields } = plan; //? Schema wrapper #2
+ if (plan.constructor.name === 'Schema') return { $type: 'schema', schema: plan, $required };
+ if (!plan || plan.constructor.name !== 'Object') return unknown;
+ if ($id) return { $type: 'schema', $id, schema: child(fields), $required };
+ if ('$type' in plan) {
+ if (forge.has(plan.$type)) return '$required' in plan ? plan : { ...plan, $required };
+ warn({ cause: TYPE_NOT_FOUND + plan.$type, sample: plan.$type, plan });
+ return unknown;
+ }
+ const result = { $type: 'object', properties: { ...fields }, $required };
+ if (plan.$meta) result.$meta = plan.$meta;
+ return result;
+ }
+};
diff --git a/modules/index.js b/modules/index.js
new file mode 100644
index 0000000..83778c6
--- /dev/null
+++ b/modules/index.js
@@ -0,0 +1,9 @@
+'use strict';
+
+module.exports = new Map(
+ Object.entries({
+ handyman: require('./handyman'),
+ metatype: require('./types'),
+ metatest: require('./test'),
+ }),
+);
diff --git a/modules/test/index.js b/modules/test/index.js
new file mode 100644
index 0000000..0ffa671
--- /dev/null
+++ b/modules/test/index.js
@@ -0,0 +1,45 @@
+'use strict';
+
+const { objectEntries, unifyResult } = require('./utils');
+const prototypes = require('./tests');
+
+const DID_NOT_PASSED = 'Test failed: ';
+const RULE_NOT_EXIST = `Missing rule: `;
+const REQUIRE_SAMPLE = 'Recieved empty sample';
+module.exports = (schema, options) => {
+ schema.forge.attach('after', TestWrapper);
+ for (const [name, proto] of prototypes.entries()) schema.forge.attach(name, proto);
+ const schemaRules = options.rules ? new Map(objectEntries('any', options.rules)) : new Map();
+ const Error = schema.tools.Error;
+ schema.test = (sample, path = 'root', isPartial = false) => {
+ const err = (def, cause = def) => new Error({ cause, path, plan: schema.$plan, sample });
+ const result = schema.test(sample, path, isPartial);
+ const flat = unifyResult(result, err.bind(null, DID_NOT_PASSED));
+ flat.valid = flat.length === 0;
+ return flat;
+ };
+
+ function TestWrapper(plan) {
+ if (plan.$type === 'schema') return this.test.bind(this);
+ const planRules = plan?.$rules;
+ const rules = Array.isArray(planRules) ? planRules : [planRules];
+ const tests = rules.filter(test => typeof test === 'string' || typeof test === 'function');
+ typeof this.test === 'function' && tests.unshift(this.test.bind(this));
+ this.test = (sample, path, isPartial) => {
+ if (sample === undefined || sample === null) return !this.$required ? [] : [REQUIRE_SAMPLE];
+ const err = (def, cause = def) => new Error({ cause, path, plan, sample });
+ const errors = [];
+ for (let i = 0; i < tests.length; i++) {
+ let [rule, name] = [tests[i], i - 1 < 0 ? 'Prototype test' : 'Rule №' + i];
+ if (typeof rule === 'string') [rule, name] = [schemaRules.get(rule), rule];
+ if (rule) {
+ const result = rule(sample, path, isPartial);
+ errors.push(...unifyResult(result, err.bind(null, DID_NOT_PASSED + name)));
+ continue;
+ }
+ errors.push(err(RULE_NOT_EXIST + name));
+ }
+ return errors;
+ };
+ }
+};
diff --git a/modules/test/tests.js b/modules/test/tests.js
new file mode 100644
index 0000000..e978dcb
--- /dev/null
+++ b/modules/test/tests.js
@@ -0,0 +1,99 @@
+'use strict';
+
+module.exports = new Map(
+ Object.entries({
+ union: Union,
+ set: Iterable,
+ array: Iterable,
+ tuple: Iterable,
+ boolean: Scalar,
+ string: Scalar,
+ number: Scalar,
+ bigint: Scalar,
+ object: Struct,
+ record: Struct,
+ map: Struct,
+ enum: Enum,
+ }),
+);
+
+const { unionHandler, instanceOfArray, objectEntries } = require('./utils');
+
+const WRONG_TYPE = 'Type misconfiguration, expected type: ';
+function Scalar() {
+ this.test = sample => {
+ if (typeof sample === this.$type) return null;
+ return WRONG_TYPE + this.$type;
+ };
+}
+
+const NOT_AT_ENUM = `Enum doesn't contain this value, enum: `;
+function Enum() {
+ this.test = sample => {
+ if (this.$enum.includes(sample)) return null;
+ return NOT_AT_ENUM + this.$enum.join(', ');
+ };
+}
+
+const EMPTY_ERROR = 'Empty data recieved';
+const TUPLE_ERROR = 'Recieved items length does not match expected length';
+const INCOR_ERROR = 'Data type misconfiguration, expected: ';
+function Iterable() {
+ this.test = (sample, path, isPartial) => {
+ if (!instanceOfArray(this.$type, sample)) return [INCOR_ERROR + this.$type];
+ const entries = [...sample];
+ if (!entries.length && this.$required) return [EMPTY_ERROR];
+ if (this.$isTuple && entries.length !== this.$items.length) return [TUPLE_ERROR];
+ const errors = [];
+ for (let i = 0; i < entries.length; i++) {
+ const test = this.$isTuple ? this.$items[i].test : this.$items[0].test;
+ const result = test(entries[i], `${path}[${i}]`, isPartial);
+ result.length && errors.push(...result);
+ }
+ return errors;
+ };
+}
+
+const EXOTC_ERROR = 'Exotic propertie: ';
+const RELIC_ERROR = 'Missing properties: ';
+function Struct() {
+ const pull = prop => {
+ const patterns = this.$patterns.entries();
+ const temp = this.$properties.get(prop);
+ if (temp) return [prop, temp];
+ if (typeof prop !== 'string') prop = String(prop);
+ for (const [pattern, value] of patterns) if (prop.match(pattern)) return [pattern, value];
+ return [null, null];
+ };
+
+ this.test = (sample, path, isPartial) => {
+ const requires = this.$requires.reduce((acc, prop) => acc.set(prop), new Map());
+ const entries = objectEntries(this.$type, sample);
+ if (!entries) return [INCOR_ERROR + this.$type];
+ if (!entries.length && this.$required) return [EMPTY_ERROR];
+ const errors = [];
+ for (const [prop, sample] of entries) {
+ const [key, prototype] = pull(prop);
+ if (!key && this.$isRecord) errors.push(EXOTC_ERROR);
+ if (!key) continue;
+ const result = prototype.test(sample, `${path}.${prop}`, isPartial);
+ requires.delete(key), result.length && errors.push(...result);
+ }
+ !isPartial && requires.size && errors.push(RELIC_ERROR + [...requires.keys()].join(', '));
+ return errors;
+ };
+}
+
+function Union() {
+ this.test = (sample, path, isPartial) => {
+ const [errors, handler] = [[], unionHandler(this.$condition, this.$types.length - 1)];
+ for (let i = 0; i < this.$types.length; ++i) {
+ const result = this.$types[i].test(sample, path, isPartial);
+ const [message, deepErrors] = handler(result, i);
+ if (message === 'ok') return [];
+ if (deepErrors && deepErrors.length > 0) errors.push(...deepErrors);
+ if (message !== 'continue') return errors.push(message), errors;
+ }
+ return errors;
+ };
+}
diff --git a/modules/test/utils.js b/modules/test/utils.js
new file mode 100644
index 0000000..b040146
--- /dev/null
+++ b/modules/test/utils.js
@@ -0,0 +1,57 @@
+'use strict';
+
+module.exports = { unionHandler, instanceOfArray, objectEntries, unifyResult };
+
+function objectEntries(t, s) {
+ if ((t === 'map' || t === 'any') && s?.constructor?.name === 'Map') return s.entries();
+ if (typeof s === 'object') return Object.entries(s);
+ return null;
+}
+
+function instanceOfArray(type, sample) {
+ if (type === 'array') return Array.isArray(sample);
+ return sample?.constructor?.name === 'Set';
+}
+
+function unifyResult(result, createError) {
+ const flatResult = [];
+ const unify = v => {
+ if (typeof v === 'boolean' && !v) return void flatResult.push(createError());
+ if (!v) return void 0;
+ if (Array.isArray(v)) return void v.forEach(unify);
+ if (typeof v === 'string') return void flatResult.push(createError(v));
+ if (typeof v === 'object') return void flatResult.push(v);
+ return void 0;
+ };
+ return unify(result), flatResult;
+}
+
+function unionHandler(name, max) {
+ let flag = false;
+ if (name === 'allof') {
+ return (result, i) => {
+ if (result.length > 0) return ['Item did not pass one of schema', result];
+ if (i === max) return ['ok'];
+ return ['continue'];
+ };
+ }
+ const errors = [];
+ if (name === 'oneof') {
+ return (result, i) => {
+ if (flag && result.length === 0) return ['Item passed more than one schema'];
+ if (result.length === 0) flag = true;
+ else if (!flag) errors.push(...result);
+ if (!flag && i === max) return ['Item did not pass all schemas', errors];
+ if (flag && i === max) return ['ok'];
+ return ['continue'];
+ };
+ }
+ return (result, i) => {
+ if (result.length !== 0) {
+ if (i >= max) return ['Item did not pass all schemas', errors];
+ errors.push(...result);
+ }
+ if (result.length === 0) return ['ok'];
+ return ['continue'];
+ };
+}
diff --git a/modules/types/index.js b/modules/types/index.js
new file mode 100644
index 0000000..787fa93
--- /dev/null
+++ b/modules/types/index.js
@@ -0,0 +1,11 @@
+'use strict';
+
+const types = require('./types');
+
+module.exports = schema => {
+ function TypescriptWrapper() {
+ this.toTypescript = () => 'unknown';
+ }
+ for (const [name, proto] of types.entries()) schema.forge.attach(name, proto);
+ schema.forge.attach('before', TypescriptWrapper);
+};
diff --git a/modules/types/types.js b/modules/types/types.js
new file mode 100644
index 0000000..891e704
--- /dev/null
+++ b/modules/types/types.js
@@ -0,0 +1,54 @@
+'use strict';
+
+const create = type => ({ toTypescript: () => type });
+module.exports = new Map(
+ Object.entries({
+ unknown: create('unknown'),
+ boolean: create('boolean'),
+ string: create('string'),
+ number: create('number'),
+ bigint: create('bigint'),
+ any: create('any'),
+ enum: Enumerable,
+ union: Union,
+ array: Iterable,
+ tuple: Iterable,
+ set: Iterable,
+ object: Struct,
+ record: Struct,
+ map: Struct,
+ }),
+);
+
+function Enumerable() {
+ this.toTypescript = () => `(${this.$enum.join(' | ')})`;
+}
+
+function Iterable() {
+ this.toTypescipt = () => {
+ const builded = this.$items.map(item => item.toTypescript());
+ if (this.$type === 'set') return `Set<${builded.join(' | ')}>`;
+ if (this.$isTuple) return `[${builded.join(', ')}]`;
+ return `(${builded.join(' | ')})[]`;
+ };
+}
+
+function Struct() {
+ const patterns = this.$patterns.entries();
+ this.toTypescript = () => {
+ let result = '{ ';
+ for (const [key, value] in this.$properties.entries()) {
+ // eslint-disable-next-line quotes, no-extra-parens
+ const sep = key.includes('`') ? (key.includes('"') ? "'" : '"') : '`';
+ result += `${sep + key + sep}${value.$required ? '?' : ''}: ${value.toTypescript()}, `;
+ }
+ if (!patterns.length) return result + '}';
+ const types = patterns.map((_, value) => value.toTypescript());
+ return result + `[key: string]?: ${types.join(' | ')}, }`;
+ };
+}
+
+function Union() {
+ const sep = this.$condition === 'allof' ? ' & ' : ' | ';
+ this.toTypescript = () => this.$types.map(type => type.toTypescript()).join(sep);
+}
diff --git a/package-lock.json b/package-lock.json
index a8ceb99..621b3be 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,17 +9,17 @@
"version": "0.4.0",
"license": "MIT",
"dependencies": {
- "astropack": "^0.4.1"
+ "astropack": "^0.4.2"
},
"devDependencies": {
- "@types/node": "^20.5.3",
- "eslint": "^8.40.0",
+ "@types/node": "^20.8.10",
+ "eslint": "^8.52.0",
"eslint-config-astrohelm": "^1.1.1",
"eslint-config-prettier": "^9.0.0",
- "eslint-plugin-import": "^2.27.5",
- "eslint-plugin-prettier": "^5.0.0",
- "prettier": "^3.0.2",
- "typescript": "^5.0.2"
+ "eslint-plugin-import": "^2.29.0",
+ "eslint-plugin-prettier": "^5.0.1",
+ "prettier": "^3.0.3",
+ "typescript": "^5.2.2"
},
"engines": {
"node": "18 || 19 || 20"
@@ -82,21 +82,21 @@
}
},
"node_modules/@eslint/js": {
- "version": "8.47.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.47.0.tgz",
- "integrity": "sha512-P6omY1zv5MItm93kLM8s2vr1HICJH8v0dvddDhysbIuZ+vcjOHg5Zbkf1mTkcmi2JA9oBG2anOkRnW8WJTS8Og==",
+ "version": "8.52.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.52.0.tgz",
+ "integrity": "sha512-mjZVbpaeMZludF2fsWLD0Z9gCref1Tk4i9+wddjRvpUNqqcndPkBD09N/Mapey0b3jaXbLm2kICwFv2E64QinA==",
"dev": true,
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/@humanwhocodes/config-array": {
- "version": "0.11.10",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz",
- "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==",
+ "version": "0.11.13",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz",
+ "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==",
"dev": true,
"dependencies": {
- "@humanwhocodes/object-schema": "^1.2.1",
+ "@humanwhocodes/object-schema": "^2.0.1",
"debug": "^4.1.1",
"minimatch": "^3.0.5"
},
@@ -118,9 +118,9 @@
}
},
"node_modules/@humanwhocodes/object-schema": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
- "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz",
+ "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==",
"dev": true
},
"node_modules/@nodelib/fs.scandir": {
@@ -185,9 +185,18 @@
"dev": true
},
"node_modules/@types/node": {
- "version": "20.5.3",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.3.tgz",
- "integrity": "sha512-ITI7rbWczR8a/S6qjAW7DMqxqFMjjTo61qZVWJ1ubPvbIQsL5D/TvwjYEalM8Kthpe3hTzOGrF2TGbAu2uyqeA==",
+ "version": "20.8.10",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.10.tgz",
+ "integrity": "sha512-TlgT8JntpcbmKUFzjhsyhGfP2fsiz1Mv56im6enJ905xG1DAYesxJaeSbGqQmAw8OWPdhyJGhGSQGKRNJ45u9w==",
+ "dev": true,
+ "dependencies": {
+ "undici-types": "~5.26.4"
+ }
+ },
+ "node_modules/@ungap/structured-clone": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
+ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
"dev": true
},
"node_modules/acorn": {
@@ -271,15 +280,15 @@
}
},
"node_modules/array-includes": {
- "version": "3.1.6",
- "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz",
- "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==",
+ "version": "3.1.7",
+ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz",
+ "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==",
"dev": true,
"dependencies": {
"call-bind": "^1.0.2",
- "define-properties": "^1.1.4",
- "es-abstract": "^1.20.4",
- "get-intrinsic": "^1.1.3",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1",
+ "get-intrinsic": "^1.2.1",
"is-string": "^1.0.7"
},
"engines": {
@@ -290,16 +299,16 @@
}
},
"node_modules/array.prototype.findlastindex": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.2.tgz",
- "integrity": "sha512-tb5thFFlUcp7NdNF6/MpDk/1r/4awWG1FIz3YqDf+/zJSTezBb+/5WViH41obXULHVpDzoiCLpJ/ZO9YbJMsdw==",
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz",
+ "integrity": "sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==",
"dev": true,
"dependencies": {
"call-bind": "^1.0.2",
- "define-properties": "^1.1.4",
- "es-abstract": "^1.20.4",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1",
"es-shim-unscopables": "^1.0.0",
- "get-intrinsic": "^1.1.3"
+ "get-intrinsic": "^1.2.1"
},
"engines": {
"node": ">= 0.4"
@@ -309,14 +318,14 @@
}
},
"node_modules/array.prototype.flat": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz",
- "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==",
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz",
+ "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==",
"dev": true,
"dependencies": {
"call-bind": "^1.0.2",
- "define-properties": "^1.1.4",
- "es-abstract": "^1.20.4",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1",
"es-shim-unscopables": "^1.0.0"
},
"engines": {
@@ -327,14 +336,14 @@
}
},
"node_modules/array.prototype.flatmap": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz",
- "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==",
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz",
+ "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==",
"dev": true,
"dependencies": {
"call-bind": "^1.0.2",
- "define-properties": "^1.1.4",
- "es-abstract": "^1.20.4",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1",
"es-shim-unscopables": "^1.0.0"
},
"engines": {
@@ -345,14 +354,15 @@
}
},
"node_modules/arraybuffer.prototype.slice": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.1.tgz",
- "integrity": "sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw==",
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz",
+ "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==",
"dev": true,
"dependencies": {
"array-buffer-byte-length": "^1.0.0",
"call-bind": "^1.0.2",
"define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1",
"get-intrinsic": "^1.2.1",
"is-array-buffer": "^3.0.2",
"is-shared-array-buffer": "^1.0.2"
@@ -365,9 +375,9 @@
}
},
"node_modules/astropack": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/astropack/-/astropack-0.4.1.tgz",
- "integrity": "sha512-QjCh7ogS1rUz26NS9DCI6ofGMLjewFfE57Wd7ZuZaru9SNqeDyqS8eSjpwGddLfi3nM0uvhuz+bNA5PlSKZ2QA==",
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/astropack/-/astropack-0.4.2.tgz",
+ "integrity": "sha512-KT4+RAMEWzeqoCKHGoG9CzskomwTZnWQITpdbRrvWQ/dpaqlElcCkKpIXwThmGVoTkfMe2fpJfO5fzx1Jsic6g==",
"engines": {
"node": "18 || 19 || 20"
}
@@ -449,13 +459,14 @@
}
},
"node_modules/call-bind": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
- "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz",
+ "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==",
"dev": true,
"dependencies": {
- "function-bind": "^1.1.1",
- "get-intrinsic": "^1.0.2"
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.1",
+ "set-function-length": "^1.1.1"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -581,6 +592,20 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/define-data-property": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz",
+ "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==",
+ "dev": true,
+ "dependencies": {
+ "get-intrinsic": "^1.2.1",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/define-lazy-prop": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz",
@@ -594,11 +619,12 @@
}
},
"node_modules/define-properties": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz",
- "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==",
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
+ "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
"dev": true,
"dependencies": {
+ "define-data-property": "^1.0.1",
"has-property-descriptors": "^1.0.0",
"object-keys": "^1.1.1"
},
@@ -622,26 +648,26 @@
}
},
"node_modules/es-abstract": {
- "version": "1.22.1",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.1.tgz",
- "integrity": "sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==",
+ "version": "1.22.3",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz",
+ "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==",
"dev": true,
"dependencies": {
"array-buffer-byte-length": "^1.0.0",
- "arraybuffer.prototype.slice": "^1.0.1",
+ "arraybuffer.prototype.slice": "^1.0.2",
"available-typed-arrays": "^1.0.5",
- "call-bind": "^1.0.2",
+ "call-bind": "^1.0.5",
"es-set-tostringtag": "^2.0.1",
"es-to-primitive": "^1.2.1",
- "function.prototype.name": "^1.1.5",
- "get-intrinsic": "^1.2.1",
+ "function.prototype.name": "^1.1.6",
+ "get-intrinsic": "^1.2.2",
"get-symbol-description": "^1.0.0",
"globalthis": "^1.0.3",
"gopd": "^1.0.1",
- "has": "^1.0.3",
"has-property-descriptors": "^1.0.0",
"has-proto": "^1.0.1",
"has-symbols": "^1.0.3",
+ "hasown": "^2.0.0",
"internal-slot": "^1.0.5",
"is-array-buffer": "^3.0.2",
"is-callable": "^1.2.7",
@@ -649,23 +675,23 @@
"is-regex": "^1.1.4",
"is-shared-array-buffer": "^1.0.2",
"is-string": "^1.0.7",
- "is-typed-array": "^1.1.10",
+ "is-typed-array": "^1.1.12",
"is-weakref": "^1.0.2",
- "object-inspect": "^1.12.3",
+ "object-inspect": "^1.13.1",
"object-keys": "^1.1.1",
"object.assign": "^4.1.4",
- "regexp.prototype.flags": "^1.5.0",
- "safe-array-concat": "^1.0.0",
+ "regexp.prototype.flags": "^1.5.1",
+ "safe-array-concat": "^1.0.1",
"safe-regex-test": "^1.0.0",
- "string.prototype.trim": "^1.2.7",
- "string.prototype.trimend": "^1.0.6",
- "string.prototype.trimstart": "^1.0.6",
+ "string.prototype.trim": "^1.2.8",
+ "string.prototype.trimend": "^1.0.7",
+ "string.prototype.trimstart": "^1.0.7",
"typed-array-buffer": "^1.0.0",
"typed-array-byte-length": "^1.0.0",
"typed-array-byte-offset": "^1.0.0",
"typed-array-length": "^1.0.4",
"unbox-primitive": "^1.0.2",
- "which-typed-array": "^1.1.10"
+ "which-typed-array": "^1.1.13"
},
"engines": {
"node": ">= 0.4"
@@ -675,26 +701,26 @@
}
},
"node_modules/es-set-tostringtag": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz",
- "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==",
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz",
+ "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==",
"dev": true,
"dependencies": {
- "get-intrinsic": "^1.1.3",
- "has": "^1.0.3",
- "has-tostringtag": "^1.0.0"
+ "get-intrinsic": "^1.2.2",
+ "has-tostringtag": "^1.0.0",
+ "hasown": "^2.0.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-shim-unscopables": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz",
- "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==",
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz",
+ "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==",
"dev": true,
"dependencies": {
- "has": "^1.0.3"
+ "hasown": "^2.0.0"
}
},
"node_modules/es-to-primitive": {
@@ -727,18 +753,19 @@
}
},
"node_modules/eslint": {
- "version": "8.47.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.47.0.tgz",
- "integrity": "sha512-spUQWrdPt+pRVP1TTJLmfRNJJHHZryFmptzcafwSvHsceV81djHOdnEeDmkdotZyLNjDhrOasNK8nikkoG1O8Q==",
+ "version": "8.52.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.52.0.tgz",
+ "integrity": "sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1",
"@eslint/eslintrc": "^2.1.2",
- "@eslint/js": "^8.47.0",
- "@humanwhocodes/config-array": "^0.11.10",
+ "@eslint/js": "8.52.0",
+ "@humanwhocodes/config-array": "^0.11.13",
"@humanwhocodes/module-importer": "^1.0.1",
"@nodelib/fs.walk": "^1.2.8",
+ "@ungap/structured-clone": "^1.2.0",
"ajv": "^6.12.4",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.2",
@@ -852,26 +879,26 @@
}
},
"node_modules/eslint-plugin-import": {
- "version": "2.28.1",
- "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.28.1.tgz",
- "integrity": "sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A==",
+ "version": "2.29.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.0.tgz",
+ "integrity": "sha512-QPOO5NO6Odv5lpoTkddtutccQjysJuFxoPS7fAHO+9m9udNHvTCPSAMW9zGAYj8lAIdr40I8yPCdUYrncXtrwg==",
"dev": true,
"dependencies": {
- "array-includes": "^3.1.6",
- "array.prototype.findlastindex": "^1.2.2",
- "array.prototype.flat": "^1.3.1",
- "array.prototype.flatmap": "^1.3.1",
+ "array-includes": "^3.1.7",
+ "array.prototype.findlastindex": "^1.2.3",
+ "array.prototype.flat": "^1.3.2",
+ "array.prototype.flatmap": "^1.3.2",
"debug": "^3.2.7",
"doctrine": "^2.1.0",
- "eslint-import-resolver-node": "^0.3.7",
+ "eslint-import-resolver-node": "^0.3.9",
"eslint-module-utils": "^2.8.0",
- "has": "^1.0.3",
- "is-core-module": "^2.13.0",
+ "hasown": "^2.0.0",
+ "is-core-module": "^2.13.1",
"is-glob": "^4.0.3",
"minimatch": "^3.1.2",
- "object.fromentries": "^2.0.6",
- "object.groupby": "^1.0.0",
- "object.values": "^1.1.6",
+ "object.fromentries": "^2.0.7",
+ "object.groupby": "^1.0.1",
+ "object.values": "^1.1.7",
"semver": "^6.3.1",
"tsconfig-paths": "^3.14.2"
},
@@ -904,9 +931,9 @@
}
},
"node_modules/eslint-plugin-prettier": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.0.tgz",
- "integrity": "sha512-AgaZCVuYDXHUGxj/ZGu1u8H8CYgDY3iG6w5kUFw4AzMVXzB7VvbKgYR4nATIN+OvUrghMbiDLeimVjVY5ilq3w==",
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.1.tgz",
+ "integrity": "sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==",
"dev": true,
"dependencies": {
"prettier-linter-helpers": "^1.0.0",
@@ -1178,21 +1205,24 @@
"dev": true
},
"node_modules/function-bind": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
- "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
- "dev": true
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
},
"node_modules/function.prototype.name": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz",
- "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==",
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz",
+ "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==",
"dev": true,
"dependencies": {
"call-bind": "^1.0.2",
- "define-properties": "^1.1.3",
- "es-abstract": "^1.19.0",
- "functions-have-names": "^1.2.2"
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1",
+ "functions-have-names": "^1.2.3"
},
"engines": {
"node": ">= 0.4"
@@ -1211,15 +1241,15 @@
}
},
"node_modules/get-intrinsic": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
- "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==",
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz",
+ "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==",
"dev": true,
"dependencies": {
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
+ "function-bind": "^1.1.2",
"has-proto": "^1.0.1",
- "has-symbols": "^1.0.3"
+ "has-symbols": "^1.0.3",
+ "hasown": "^2.0.0"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -1333,18 +1363,6 @@
"integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
"dev": true
},
- "node_modules/has": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
- "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
- "dev": true,
- "dependencies": {
- "function-bind": "^1.1.1"
- },
- "engines": {
- "node": ">= 0.4.0"
- }
- },
"node_modules/has-bigints": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
@@ -1364,12 +1382,12 @@
}
},
"node_modules/has-property-descriptors": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz",
- "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==",
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz",
+ "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==",
"dev": true,
"dependencies": {
- "get-intrinsic": "^1.1.1"
+ "get-intrinsic": "^1.2.2"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -1414,6 +1432,18 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/hasown": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz",
+ "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==",
+ "dev": true,
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/human-signals": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz",
@@ -1474,13 +1504,13 @@
"dev": true
},
"node_modules/internal-slot": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz",
- "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==",
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz",
+ "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==",
"dev": true,
"dependencies": {
- "get-intrinsic": "^1.2.0",
- "has": "^1.0.3",
+ "get-intrinsic": "^1.2.2",
+ "hasown": "^2.0.0",
"side-channel": "^1.0.4"
},
"engines": {
@@ -1542,12 +1572,12 @@
}
},
"node_modules/is-core-module": {
- "version": "2.13.0",
- "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz",
- "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==",
+ "version": "2.13.1",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz",
+ "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==",
"dev": true,
"dependencies": {
- "has": "^1.0.3"
+ "hasown": "^2.0.0"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -1974,9 +2004,9 @@
}
},
"node_modules/object-inspect": {
- "version": "1.12.3",
- "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
- "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==",
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
+ "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==",
"dev": true,
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -2010,14 +2040,14 @@
}
},
"node_modules/object.fromentries": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz",
- "integrity": "sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==",
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz",
+ "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==",
"dev": true,
"dependencies": {
"call-bind": "^1.0.2",
- "define-properties": "^1.1.4",
- "es-abstract": "^1.20.4"
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1"
},
"engines": {
"node": ">= 0.4"
@@ -2027,26 +2057,26 @@
}
},
"node_modules/object.groupby": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.0.tgz",
- "integrity": "sha512-70MWG6NfRH9GnbZOikuhPPYzpUpof9iW2J9E4dW7FXTqPNb6rllE6u39SKwwiNh8lCwX3DDb5OgcKGiEBrTTyw==",
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.1.tgz",
+ "integrity": "sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==",
"dev": true,
"dependencies": {
"call-bind": "^1.0.2",
"define-properties": "^1.2.0",
- "es-abstract": "^1.21.2",
+ "es-abstract": "^1.22.1",
"get-intrinsic": "^1.2.1"
}
},
"node_modules/object.values": {
- "version": "1.1.6",
- "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz",
- "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==",
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz",
+ "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==",
"dev": true,
"dependencies": {
"call-bind": "^1.0.2",
- "define-properties": "^1.1.4",
- "es-abstract": "^1.20.4"
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1"
},
"engines": {
"node": ">= 0.4"
@@ -2217,9 +2247,9 @@
}
},
"node_modules/prettier": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.2.tgz",
- "integrity": "sha512-o2YR9qtniXvwEZlOKbveKfDQVyqxbEIWn48Z8m3ZJjBjcCmUy3xZGIv+7AkaeuaTr6yPXJjwv07ZWlsWbEy1rQ==",
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz",
+ "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==",
"dev": true,
"bin": {
"prettier": "bin/prettier.cjs"
@@ -2273,14 +2303,14 @@
]
},
"node_modules/regexp.prototype.flags": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz",
- "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==",
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz",
+ "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==",
"dev": true,
"dependencies": {
"call-bind": "^1.0.2",
"define-properties": "^1.2.0",
- "functions-have-names": "^1.2.3"
+ "set-function-name": "^2.0.0"
},
"engines": {
"node": ">= 0.4"
@@ -2468,13 +2498,13 @@
}
},
"node_modules/safe-array-concat": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.0.tgz",
- "integrity": "sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==",
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz",
+ "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==",
"dev": true,
"dependencies": {
"call-bind": "^1.0.2",
- "get-intrinsic": "^1.2.0",
+ "get-intrinsic": "^1.2.1",
"has-symbols": "^1.0.3",
"isarray": "^2.0.5"
},
@@ -2508,6 +2538,35 @@
"semver": "bin/semver.js"
}
},
+ "node_modules/set-function-length": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz",
+ "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==",
+ "dev": true,
+ "dependencies": {
+ "define-data-property": "^1.1.1",
+ "get-intrinsic": "^1.2.1",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/set-function-name": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz",
+ "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==",
+ "dev": true,
+ "dependencies": {
+ "define-data-property": "^1.0.1",
+ "functions-have-names": "^1.2.3",
+ "has-property-descriptors": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@@ -2550,14 +2609,14 @@
"dev": true
},
"node_modules/string.prototype.trim": {
- "version": "1.2.7",
- "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz",
- "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==",
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz",
+ "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==",
"dev": true,
"dependencies": {
"call-bind": "^1.0.2",
- "define-properties": "^1.1.4",
- "es-abstract": "^1.20.4"
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1"
},
"engines": {
"node": ">= 0.4"
@@ -2567,28 +2626,28 @@
}
},
"node_modules/string.prototype.trimend": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz",
- "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==",
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz",
+ "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==",
"dev": true,
"dependencies": {
"call-bind": "^1.0.2",
- "define-properties": "^1.1.4",
- "es-abstract": "^1.20.4"
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/string.prototype.trimstart": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz",
- "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==",
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz",
+ "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==",
"dev": true,
"dependencies": {
"call-bind": "^1.0.2",
- "define-properties": "^1.1.4",
- "es-abstract": "^1.20.4"
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -2817,9 +2876,9 @@
}
},
"node_modules/typescript": {
- "version": "5.1.6",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz",
- "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==",
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
+ "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
@@ -2844,6 +2903,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/undici-types": {
+ "version": "5.26.5",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
+ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
+ "dev": true
+ },
"node_modules/untildify": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz",
@@ -2894,13 +2959,13 @@
}
},
"node_modules/which-typed-array": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz",
- "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==",
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz",
+ "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==",
"dev": true,
"dependencies": {
"available-typed-arrays": "^1.0.5",
- "call-bind": "^1.0.2",
+ "call-bind": "^1.0.4",
"for-each": "^0.3.3",
"gopd": "^1.0.1",
"has-tostringtag": "^1.0.0"
diff --git a/package.json b/package.json
index 0513cce..f9e2f7c 100644
--- a/package.json
+++ b/package.json
@@ -21,14 +21,17 @@
"runtime-verification",
"zero-dependencies",
"type-generator",
- "astrohelm"
+ "astrohelm",
+ "lightweight",
+ "modules",
+ "modular"
],
"main": "index.js",
"types": "types/index.d.ts",
"packageManager": "npm@9.6.4",
"readmeFilename": "README.md",
"engines": {
- "node": "18 || 19 || 20"
+ "node": ">= 18"
},
"browser": {},
"files": ["/lib", "/types"],
@@ -47,16 +50,16 @@
"email": "sashapop101@gmail.com"
},
"devDependencies": {
- "@types/node": "^20.5.3",
- "eslint": "^8.40.0",
+ "@types/node": "^20.8.10",
+ "eslint": "^8.52.0",
"eslint-config-astrohelm": "^1.1.1",
"eslint-config-prettier": "^9.0.0",
- "eslint-plugin-import": "^2.27.5",
- "eslint-plugin-prettier": "^5.0.0",
- "prettier": "^3.0.2",
- "typescript": "^5.0.2"
+ "eslint-plugin-import": "^2.29.0",
+ "eslint-plugin-prettier": "^5.0.1",
+ "prettier": "^3.0.3",
+ "typescript": "^5.2.2"
},
"dependencies": {
- "astropack": "^0.4.1"
+ "astropack": "^0.4.2"
}
}
diff --git a/tests/basic.test.js b/tests/basic.test.js
index ff64ee6..1faebd9 100644
--- a/tests/basic.test.js
+++ b/tests/basic.test.js
@@ -3,86 +3,60 @@
const [test, assert] = [require('node:test'), require('node:assert')];
const Schema = require('..');
-test('Schema with errors & warnings', () => {
- const plan = {
- type: 'object',
- properties: {
- a: ['number'],
- b: { type: 'set', items: ['array'], condition: 'anyof' },
- c: { type: 'object', properties: { z: 'string' } },
- z: 'string',
- z2: '?string',
- season: { type: 'enum', enum: ['winter', 'spring', 'autumn', 'summer'] },
- room: { type: 'enum', enum: [1, 2, 3, 4] },
- room2: 'enum', //? +1 [Warning] Shorthand enum (enum is scalar)
- },
- patternProperties: {
- '^[a-z]+': 'string',
+test('Schema without errors & warnings', () => {
+ const userSchema = new Schema({
+ $id: 'UserSchema',
+ phone: { $type: 'union', types: ['number', 'string'] }, //? anyof tyupe
+ name: { $type: 'set', items: ['string', '?string'] }, //? set tuple
+ mask: { $type: 'array', items: 'string' }, //? array
+ ip: {
+ $id: 'IpSchema',
+ $type: 'array',
+ $required: false,
+ $rules: [ip => ip[0] === '192'], //? custom rules
+ items: { $type: 'union', types: ['string', '?number'], condition: 'oneof', $required: false },
},
- };
- const sample = {
- a: 'test', //? +1 [Error] Type missmatch
- b: new Set(['a', 'b', 'c']), //? +1 [Warning] Shorthand non-scalar
- c: { z: 'string', a: true }, //? +1 [Error] Exotic
- hello: 'world',
- season: 'today', //? +1 [Error] Not at enum
- room: 5, //? +1 [Error] Not at enum
- 123: 'test', //? +2 [Error] Exoitic, Missing field "z"
- };
- const schema = new Schema(plan);
- assert.strictEqual(schema.warnings.length, 2);
- const { cause, message, path } = schema.warnings[0];
- assert.strictEqual(path, 'PREPROCESS');
- assert.strictEqual(cause, 'Shorthand usage with non-scalar schema');
- assert.strictEqual(message, '[PREPROCESS] => Shorthand usage with non-scalar schema');
- const errors = schema.test(sample);
- assert.strictEqual(errors.length, 6);
-});
+ type: ['elite', 'member', 'guest'], //? enum
+ adress: 'string',
+ secondAdress: '?string',
+ associations: new Schema({
+ $id: 'UserID',
+ '[a-z]+Id': { $type: 'number', isPattern: true },
+ }),
+ options: { notifications: 'boolean', lvls: ['number', 'string'] },
+ });
-test('Schema without errors & warnings', () => {
- const plan = {
- type: 'map',
- preprocess: v => ({ ...v, test: 123 }), //? This process wont work
- properties: {
- a: ['number', 'string'], //? anyof
- b: { type: 'set', items: ['?string', 'any', 'unknown'], condition: 'allof' },
- c: {
- type: 'object',
- properties: {
- z: 'string',
- //? Required shorthand don't work at array items
- d: { type: 'array', items: ['?number', '?string'], condition: 'oneof' },
- },
- },
- z: 'string',
- z2: '?string', //? not required
- z3: { type: 'string', required: false },
- z4: { type: 'string', preprocess: () => 'Required' }, //? Default value
- z5: {
- type: 'array',
- items: [{ type: 'number', postprocess: v => v * 2 }], //? This process wont work
- preprocess: () => [1, 2, 3, 4],
- },
- season: { type: 'enum', enum: ['winter', 'spring', 'autumn', 'summer'] },
- users: { type: 'array', items: 'string' },
+ const sample = [
+ {
+ phone: '7(***)...',
+ ip: ['192', 168, '1', null],
+ type: 'elite',
+ mask: ['255', '255', '255', '0'],
+ name: new Set(['Alexander', null]),
+ options: { notifications: true, lvls: [2, '["admin", "user"]'] },
+ associations: { userId: 1, recordId: 1, dbId: 1 },
+ adress: 'Pushkin street',
},
- patternProperties: {
- '^[a-z]+': { type: 'string', postprocess: v => v + ' !' },
+ {
+ phone: 79999999999,
+ type: 'guest',
+ mask: ['255', '255', '255', '0'],
+ name: new Set(['Alexander', 'Ivanov']),
+ options: { notifications: false, lvls: [2, '["admin", "user"]'] },
+ associations: { userId: 2, recordId: 2, dbId: 2 },
+ adress: 'Pushkin street',
},
- };
- const sample = new Map(
- Object.entries({
- a: 'test',
- b: new Set(['a', 'b', 'c']),
- c: { z: 'string', d: [1, 'test'] },
- hello: 'world',
- z: 'test',
- season: 'winter',
- users: ['sashapop10', 'expertrix', 'alexander'],
- }),
- );
- const schema = new Schema(plan);
- assert.strictEqual(schema.warnings.length, 0);
- const errors = schema.test(sample);
- assert.strictEqual(errors.length, 0);
+ ];
+
+ const systemSchema = new Schema({ $type: 'array', items: userSchema });
+ const ipSchema = systemSchema.pull('IpSchema');
+ const usSchema = systemSchema.pull('UserSchema');
+ const UserID = systemSchema.pull('UserID');
+ assert.strictEqual(systemSchema.warnings.length, 0);
+ assert.strictEqual(userSchema.warnings.length, 0);
+ assert.strictEqual(UserID.test(sample[0].associations).valid, true);
+ assert.strictEqual(ipSchema.test(sample[0].ip).valid, true);
+ assert.strictEqual(usSchema.test(sample[0]).valid, true);
+ assert.strictEqual(userSchema.test(sample[0]).valid, true);
+ assert.strictEqual(systemSchema.test(sample).valid, true);
});
diff --git a/tests/namespace.test.js b/tests/namespace.test.js
index a5f3272..7eb011b 100644
--- a/tests/namespace.test.js
+++ b/tests/namespace.test.js
@@ -4,41 +4,10 @@ const [test, assert] = [require('node:test'), require('node:assert')];
const Schema = require('..');
test('Schema with namespace', () => {
- const plan = {
- type: 'object',
- allowExotic: true,
- properties: {
- a: ['number', 'string'], //? anyof
- b: { type: 'set', items: ['?string', 'any', 'unknown'], condition: 'allof' },
- c: {
- type: 'object',
- properties: {
- z: 'string',
- //? Required shorthand don't work at array items
- d: { type: 'array', items: ['?string', '?number'], condition: 'oneof' },
- },
- },
- z: 'string',
- z2: '?string', //? not required
- z3: { type: 'string', required: false },
- },
- patternProperties: {
- '^[a-z]+': 'string',
- },
- };
- const sample = {
- a: 'test',
- b: new Set(['a', 'b', 'c']),
- c: { z: 'string', d: [1, 'test'] },
- hello: 'world',
- z: 'test',
- 123: 'test',
- };
- const schemaA = new Schema(plan);
- const namespace = new Map();
- namespace.set('IntegratedSchema', schemaA);
- const schemaB = new Schema({ type: 'IntegratedSchema' }, { namespace });
- assert.strictEqual(schemaB.warnings.length + schemaB.warnings.length, 0);
- const errors = schemaB.test(sample);
- assert.strictEqual(errors.length, 0);
+ const namespace = { User: new Schema('string') };
+ const schema = new Schema(['User', 'User'], { namespace });
+ const sample = ['Alexander', 'Ivanov'];
+
+ assert.strictEqual(namespace.User.warnings.length + schema.warnings.length, 0);
+ assert.strictEqual(schema.test(sample).length, 0);
});
diff --git a/tests/parser.test.js b/tests/parser.test.js
deleted file mode 100644
index 42fe9ed..0000000
--- a/tests/parser.test.js
+++ /dev/null
@@ -1,78 +0,0 @@
-'use strict';
-
-const test = require('node:test');
-const assert = require('node:assert');
-const Schema = require('..');
-
-test('Generate schema from sample', () => {
- const sample = {
- lines: ['Pushkin streen', 'Kalatushkin home', 8],
- zip: '123103',
- city: 'Moscow',
- country: 'Russia',
- };
- const expected = {
- type: 'object',
- properties: {
- lines: { type: 'array', items: ['string', 'number'] },
- zip: 'string',
- city: 'string',
- country: 'string',
- },
- };
- const plan = Schema.parse(sample);
- assert.deepStrictEqual(plan, expected);
-});
-
-test('Generate schema from array of objects', () => {
- const sample = [
- {
- country: 'Russia',
- lines: ['Pushkin street', 'Kalatushkin home', 8],
- zip: '123103',
- city: 'Moscow',
- },
- {
- lines: ['Ivanov street', 25],
- city: 'Moscow',
- zip: '123103',
- country: 'Russia',
- },
- {
- lines: ['Brodway street'],
- zip: '123103',
- city: 'New York',
- phone: '+1 111...',
- country: 'USA',
- },
- ];
- const expected = {
- type: 'array',
- items: [
- {
- type: 'object',
- properties: {
- lines: { type: 'array', items: ['string', 'number'] },
- zip: 'string',
- city: 'string',
- country: 'string',
- },
- },
- {
- type: 'object',
- properties: {
- lines: { type: 'array', items: ['string'] },
- zip: 'string',
- city: 'string',
- phone: 'string',
- country: 'string',
- },
- },
- ],
- };
- const plan = Schema.parse(sample);
- const schema = new Schema(plan);
- const errors = schema.test(sample);
- assert.deepStrictEqual(plan, expected);
- assert.strictEqual(errors.length + schema.warnings.length, 0);
-});
diff --git a/tests/rules.test.js b/tests/rules.test.js
index a6f1e7f..519f7cf 100644
--- a/tests/rules.test.js
+++ b/tests/rules.test.js
@@ -6,10 +6,10 @@ const Schema = require('..');
test('Rules', () => {
const rule1 = sample => sample?.length > 5;
const rule2 = sample => sample?.length < 100;
- const plan = { type: 'string', rules: [rule1, rule2] };
+ const plan = { $type: 'string', $rules: [rule1, rule2] };
const schema = new Schema(plan);
assert.strictEqual(schema.warnings.length, 0);
- assert.strictEqual(schema.test().length, 3); //? Required + two rules
+ assert.strictEqual(schema.test().length, 1); //? Required
assert.strictEqual(schema.test('Test').length, 1); //? One rule
assert.strictEqual(schema.test('Hello world').length, 0);
});
@@ -19,11 +19,10 @@ test('String type of rules', () => {
const mailRule = sample => sample?.includes('@');
const isGmail = sample => sample?.includes('gmail.com');
const rules = new Map().set('length', lengthRule).set('mail', mailRule);
- const plan = { type: 'string', rules: ['length', 'mail', isGmail], required: false };
+ const plan = { $type: 'string', $rules: ['length', 'mail', isGmail], $required: false };
const schema = new Schema(plan, { rules });
assert.strictEqual(schema.warnings.length, 0);
- assert.strictEqual(schema.test().length, 0);
- console.log(schema.test('Alexander Ivanov'));
- assert.strictEqual(schema.test('Alexander Ivanov').length, 2); //? Not a mail
+ assert.strictEqual(schema.test().length, 0); //? Not required
+ assert.strictEqual(schema.test('Alexander Ivanov').length, 2); //? Not a mail / gmail
assert.strictEqual(schema.test('somemail@gmail.com').length, 0);
});
diff --git a/tests/types.test.js b/tests/types.test.js
index 923f51b..8cd400a 100644
--- a/tests/types.test.js
+++ b/tests/types.test.js
@@ -3,46 +3,36 @@
const [test, assert] = [require('node:test'), require('node:assert')];
const Schema = require('..');
-test('Custom types', () => {
- const types = new Map();
+test('Custom prototypes', () => {
+ function MyDate() {
+ this.$kind = 'scalar';
+ this.test = sample => {
+ if (!isNaN(new Date(sample))) return null;
+ return 'Invalid sample type';
+ };
+ }
- const datePrototype = {
- meta: { kind: 'scalar', type: 'date' },
- construct(plan, { isRequired, Error }) {
- this.required = isRequired(plan);
- this.test = (sample, path) => {
- if (!this.required && sample === undefined) return [];
- if (this.required && !sample) {
- return [new Error({ plan, sample, path, cause: 'Value is required' })];
- }
- if (typeof sample !== 'object' && typeof sample !== 'string') {
- return [new Error({ plan, sample, path, cause: 'Invalid sample type' })];
- }
- if (isNaN(new Date(sample))) {
- return [new Error({ plan, sample, path, cause: 'Invalid sample type' })];
- }
- return [];
- };
- },
- };
- types.set('myDate', datePrototype);
- const plan = '?myDate';
- const schema = new Schema(plan, { types });
+ const schema = new Schema('?date', { prototypes: { date: MyDate } });
assert.strictEqual(schema.warnings.length, 0);
assert.strictEqual(schema.test().length, 0);
assert.strictEqual(schema.test(new Date()).length, 0);
assert.strictEqual(schema.test(new Date('Invalid param')).length, 1);
});
-test('Custom types with meta replacement for old ones', () => {
- const types = new Map();
- types.set('string', { meta: { newMeta: 'This String is awsome' } });
- types.set('number', { meta: { newMeta: 'This Number is awsome' } });
- const stringSchema = new Schema('string', { types });
- const numberSchema = new Schema('number', { types });
+test('Custom prototypes with meta replacement for old ones', () => {
+ const prototypes = new Map();
+ prototypes.set('string', { about: 'This String is awsome' });
+ prototypes.set('number', { about: 'This Number is awsome' });
+ const prototypeOnject = Object.fromEntries(prototypes.entries());
+ const stringSchema = new Schema('string', { prototypes });
+ const numberSchema = new Schema(
+ { $type: 'number', $meta: { desc: 'age' } },
+ { prototypes: prototypeOnject },
+ );
assert.strictEqual(stringSchema.warnings.length + numberSchema.warnings.length, 0);
assert.strictEqual(numberSchema.test(1).length, 0);
assert.strictEqual(stringSchema.test('test').length, 0);
- assert.strictEqual(stringSchema.newMeta, 'This String is awsome');
- assert.strictEqual(numberSchema.newMeta, 'This Number is awsome');
+ assert.strictEqual(stringSchema.about, 'This String is awsome');
+ assert.strictEqual(numberSchema.about, 'This Number is awsome');
+ assert.strictEqual(numberSchema.desc, 'age');
});
diff --git a/types/index.d.ts b/types/index.d.ts
index 42bc6c1..a3d9d2c 100644
--- a/types/index.d.ts
+++ b/types/index.d.ts
@@ -1,54 +1,81 @@
-//TODO: Root link, root path, wrap value in object to update it (possible to do calculated fields) / Or remove that processing
-//TODO: String rules, pass realization through Schema constructor, rename rules -> validate, possisble to pass only one method / string
-//TODO: String for preprocess, postprocess
-//TODO: Tuple, Date
-//TODO: Custom prototype inheritance
-//TODO: Plans join ?
-
-//TODO: #1 Typescript code generation
-//TODO: #2 Types & README
+class SchemaError {
+ constructor(options: { pattern: (dict: { [key: string]: string }) => string }) {}
+ plan: unknown;
+ message: string;
+ path: string;
+ sample: unknown;
+ sampleType: string;
+ cause: string;
+}
-type Condition = 'allof' | 'anyof' | 'oneof';
-type TypeField = { type: Type | Type[]; condition?: Condition };
-type Rule = (sample: unknown, tools: Tools) => SchemaError[];
-type Type = string | TypeObject;
+type Rule = 'string' | ((sample: string, path?: string, partial?: boolean) => unknown);
+interface PlanCore {
+ $meta?: { [key: string]: unknown };
+ $rules?: Array