json-response-gen
Version:
Repo for some utility functions to generate random data when you know the shape of data samples but cant get it
597 lines (583 loc) • 17.7 kB
JavaScript
;
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
ALPHA: () => ALPHA,
ALPHA_1: () => ALPHA_1,
ALPHA_NUMERIC: () => ALPHA_NUMERIC,
ALPHA_PHRASE: () => ALPHA_PHRASE,
Builder: () => Builder,
C: () => Custom,
Custom: () => Custom,
DAY_RANGE: () => DAY_RANGE,
DEFAULT_MOCKS_DIR: () => DEFAULT_MOCKS_DIR,
DateRange: () => DateRange,
EMAIL: () => EMAIL,
LOWER_ALPHA: () => LOWER_ALPHA,
LOWER_ALPHA_NUMERIC: () => LOWER_ALPHA_NUMERIC,
NUMERIC: () => NUMERIC,
Option: () => Option,
Regex: () => Regex,
Repetition: () => Repetition,
SELECTION_TYPES: () => SELECTION_TYPES,
Shared: () => Shared,
UPPER_ALPHA: () => UPPER_ALPHA,
UPPER_ALPHA_NUMERIC: () => UPPER_ALPHA_NUMERIC,
conditionalDatePrefix: () => conditionalDatePrefix,
get: () => get,
getDay: () => getDay,
handleObject: () => handleObject,
handleValue: () => handleValue,
hasTestFunc: () => hasTestFunc,
implementsOptions: () => implementsOptions,
isLeapYear: () => isLeapYear,
noop: () => noop,
prepDirs: () => prepDirs,
shouldGetFromExtraDetails: () => shouldGetFromExtraDetails,
toReverseLookupMap: () => toReverseLookupMap
});
module.exports = __toCommonJS(src_exports);
// src/utils/regexOptions.ts
var ALPHA_PHRASE = (wordCount) => {
return new Custom(() => {
let phrase = ALPHA_1(4, 10)?.build({});
for (let i = 1; i < wordCount; i++) {
const next = LOWER_ALPHA(4, 10).build({});
phrase += ` ${next}`;
}
return phrase;
});
};
var UPPER_ALPHA = (min, max = min) => new Regex(new RegExp(`^[A-Z]{${min},${max}}$`));
var LOWER_ALPHA = (min, max = min) => new Regex(new RegExp(`^[a-z]{${min},${max}}$`));
var ALPHA = (min, max = min) => new Regex(new RegExp(`^[a-zA-Z]{${min},${max}}$`));
var ALPHA_1 = (min, max = min) => {
if (max == 1) return new Regex(new RegExp(`^[A-Z]$`));
return new Regex(new RegExp(`^[A-Z][a-z]{${min - 1},${max - 1}}$`));
};
var LOWER_ALPHA_NUMERIC = (min, max = min) => new Regex(new RegExp(`^[a-z0-9]{${min},${max}}$`));
var UPPER_ALPHA_NUMERIC = (min, max = min) => new Regex(new RegExp(`^[A-Z0-9]{${min},${max}}$`));
var ALPHA_NUMERIC = (min, max = min) => new Regex(new RegExp(`^[a-zA-Z0-9]{${min},${max}}$`));
var NUMERIC = (min, max = min) => new Regex(new RegExp(`^[0-9]{${min},${max}}$`));
var EMAIL = (length = 9, domain) => {
let eCount;
let dCount;
if (!!domain) {
const dLen = domain.length;
eCount = Math.max(1, length - (dLen + 1));
dCount = 0;
} else {
const usableLen = Math.max(4, length - 5);
eCount = Math.ceil(Math.max(1, usableLen * 2 / 3));
dCount = usableLen - eCount;
}
const dom = domain || `${ALPHA_NUMERIC(dCount).build({})}.${LOWER_ALPHA(3).build({})}`;
return new Custom(() => `${ALPHA_NUMERIC(eCount).build({})}@${dom}`);
};
// src/options/DateRange.ts
var DateRange = class {
/**
* @param range +/- from current year
*/
constructor(range, maxToday = true) {
/**
* Quick alias for the build function
*/
this.b = this.build;
this.range = range;
this.maxToday = maxToday;
}
/**
* @returns date in MM/DD/YYY
*/
build(buildOptions, extraInfo) {
const month = Number(NUMERIC(2).build()) % 12 + 1;
const mult = [-1, 1][this.maxToday ? 0 : Math.floor(Math.random() * 2)];
const year = (/* @__PURE__ */ new Date()).getFullYear() + mult * Math.round(Math.random() * this.range);
const day = Math.floor(Math.random() * getDay(month, year)) + 1;
return `${conditionalDatePrefix(month)}/${conditionalDatePrefix(day)}/${year}`;
}
test(value) {
const reg = /^[0-9]{2}\/[0-9]{2}\/[0-9]{4}$/;
const year = value.slice(value.length - 4);
const withinRange = Number(year) >= (/* @__PURE__ */ new Date()).getFullYear() - this.range;
return reg.test(value) && withinRange;
}
};
// src/constants/constants.ts
var SELECTION_TYPES = Object.freeze({
IN_ORDER: "IN_ORDER",
RANDOM: "RANDOM"
});
var DAY_RANGE = Object.freeze({
1: 31,
2: 28,
3: 31,
4: 30,
5: 31,
6: 30,
7: 31,
8: 31,
9: 30,
10: 31,
11: 30,
12: 31,
LEAP: 29
});
var DEFAULT_MOCKS_DIR = "__jgen__/mocks";
// src/options/Option.ts
var Option = class {
/**
*
* @param array - Array of options to select, if filled with Regex it returns a random instance of the pattern
* @param selectionType - How to select the item, check out our SELECTION_TYPES
*/
constructor(array, options) {
/**
* Quick alias for the build function
*/
this.b = this.build;
this.array = array;
this.nextIdx = 0;
this.selectionType = options?.selectionType;
this.shouldSpread = options?.shouldSpread;
}
/**
* @returns an item from the given array depending on the selection type
*/
build(buildOptions, extraInfo) {
const selectionType = this.selectionType || buildOptions?.selectionType;
let idx;
if (selectionType === SELECTION_TYPES.RANDOM) {
idx = Math.floor(Math.random() * this.array.length);
} else {
if (this.nextIdx >= this.array.length) {
this.nextIdx = 0;
}
idx = this.nextIdx++;
}
const value = this.array[idx];
return handleValue(value, buildOptions);
}
test(value) {
const result = this.array.some((option) => {
if (hasTestFunc(option)) {
return option.test(value);
} else {
return option === value;
}
});
return result;
}
};
// src/options/Repetition.ts
var Repetition = class {
/**
*
* @param shape Object of the array element
* @param repetitions Amount of elements in the array
*/
constructor(shape, options) {
/**
* Quick alias for the build function
*/
this.b = this.build;
this.shape = shape;
this.options = options;
this.sharedOptions = !!options?.baseArrayPath;
}
/**
* @returns Array filled with randomly generated elements of described shape
*/
build(buildOptions, extraInfo) {
if (this.sharedOptions) {
return this.buildShared(buildOptions, extraInfo);
}
const repetitions = this.options?.repetitions || buildOptions?.repetitions;
return Array(repetitions).fill(0).map(() => handleValue(this.shape, buildOptions));
}
buildShared(buildOptions, extraInfo = {}) {
const { getFromGeneration } = buildOptions;
const { baseArrayPath, sharedKeysMap = {} } = this.options;
const [shouldGetFromExtra, newPath] = shouldGetFromExtraDetails(
baseArrayPath
);
let baseArray;
if (shouldGetFromExtra) {
const relativeValue = get(extraInfo, newPath, void 0);
if (Array.isArray(relativeValue)) {
baseArray = relativeValue;
} else {
return [];
}
} else {
baseArray = getFromGeneration?.(baseArrayPath);
}
if (!Array.isArray(baseArray)) return [];
const { toShare, toRename } = sharedKeysMap;
const reducedShare = toShare?.reduce((prev, currString) => {
return {
...prev,
[currString]: currString
};
}, {});
const reducedRename = toReverseLookupMap(toRename);
const reducedMap = {
...reducedShare,
...reducedRename
};
return baseArray.map((currObj) => {
const newGen = handleValue(this.shape, buildOptions, {
...extraInfo,
...currObj
});
if (Array.isArray(newGen)) {
return newGen;
}
if (!(newGen instanceof Object)) {
return newGen;
}
if (!(currObj instanceof Object)) {
return currObj;
}
const sharedGen = Object.keys(reducedMap).reduce((prev, currKey) => {
const oldKey = reducedMap[currKey];
const sharedValue = currObj[oldKey];
return {
...prev,
[currKey]: sharedValue
};
}, {});
return {
...newGen,
...sharedGen
};
});
}
/**
* TODO:
* what if:
* Object: should we iterate and verify each object? its for testing so probly
* Option: call its test function for generated value
* Repetition: call test function for generated value i guess?
* DateRange: call its test function for generated value
* Regex: call its test function for generated value
*/
test(value) {
const isLength = value.length === this.options?.repetitions;
return isLength;
}
};
// src/options/Regex.ts
var import_randexp = __toESM(require("randexp"));
var Regex = class {
constructor(pattern) {
/**
* Quick alias for the build function
*/
this.b = this.build;
this.pattern = pattern;
}
build(buildOptions, extraInfo) {
return new import_randexp.default(this.pattern).gen();
}
test(value) {
return this.pattern.test(value);
}
};
// src/options/Builder.ts
var import_fs = require("fs");
var import_kleur = __toESM(require("kleur"));
var defaultOptions = {
repetitions: 3,
selectionType: SELECTION_TYPES.IN_ORDER,
addSharedValue: noop,
getSharedValue: noop,
getFromGeneration: noop
};
var Builder = class {
/**
*
* @param path Path in which to save the files under
* @param {BuildProps} options Build options to configure the builder
*
*/
constructor(options) {
/**
* @param result Result which was built, probly only used internally
*/
this.addResult = (result) => {
const newResults = [...this.results, result];
this.results = newResults;
};
/**
* @returns Get result at index, defaults to 0, or first generated result
*/
this.getResult = (index = 0) => {
return this.results[index];
};
this.getResults = () => {
return this.results;
};
this.addSharedValue = (key, value) => {
this.valueMap.set(key, value);
};
this.getSharedValue = (key) => {
return this.valueMap.get(key);
};
/**
*
* @param key Key to get an item from the base generation, use '/' to get the root of the object, same as getResult()
* @returns
*/
this.getFromGeneration = (key, genNumber = 0) => {
if (key === "/") return this.results[genNumber];
return get(this.results[genNumber], key, void 0);
};
/**
* @param shape Shape of the object to build
* @returns Same Builder object with baseGeneration populated for future builds, saved if save option is enabled
*/
this.build = (shape, fileName) => {
const result = handleValue(shape, this.options);
this.addResult(result);
const shouldSave = !!fileName;
if (shouldSave) {
const filePath = prepDirs(fileName, this.options?.writeDir);
(0, import_fs.writeFile)(`${filePath}`, JSON.stringify(result), "utf8", noop);
console.log(import_kleur.default.yellow("Wrote file to: "), import_kleur.default.green(filePath));
}
return this;
};
/**
* Quick alias for the build function
*/
this.b = this.build;
this.valueMap = /* @__PURE__ */ new Map();
this.results = new Array();
this.options = {
...defaultOptions,
...options,
addSharedValue: this.addSharedValue,
getSharedValue: this.getSharedValue,
getFromGeneration: this.getFromGeneration
};
}
};
// src/options/Shared.ts
var Shared = class {
/**
* @param key Key is string to be used to save/access data
* @param options Object containing value to use/build if saving i.e. shape or any of our other utility classes, i.e. Option, Repetition, DateRange, Regex.
* Also if it returns an object, if it should spread that.
*/
constructor(key, options) {
/**
* Quick alias for the build function
*/
this.b = this.build;
this.key = key;
this.value = options?.value;
this.shouldSpread = options?.shouldSpread;
}
// Probably call a separate build for when it is a repetition being called back
build(buildOptions, extraInfo) {
const { addSharedValue, getSharedValue } = buildOptions;
const currentValue = getSharedValue?.(this.key);
if (!currentValue) {
const genValue = handleValue(this.value, buildOptions);
addSharedValue?.(this.key, genValue);
return genValue;
}
return currentValue;
}
// TODO: test somehow or we can remove the test thing
test(value) {
return true;
}
};
// src/options/Custom.ts
var Custom = class {
constructor(callback, shouldSpread = false) {
/**
* Quick alias for the build function
*/
this.b = this.build;
this.callback = callback;
this.shouldSpread = shouldSpread;
}
build(buildOptions, extraInfo) {
return this.callback();
}
test(value) {
return true;
}
};
// src/utils/utils.ts
var import_fs2 = require("fs");
var import_path = __toESM(require("path"));
function noop() {
}
function implementsOptions(object) {
return !!object?.build;
}
var hasTestFunc = (value) => {
return implementsOptions(value);
};
var isLeapYear = (year) => {
return year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;
};
var getDay = (month, year) => {
const dayKey = month === 2 && isLeapYear(year) ? "LEAP" : month;
return DAY_RANGE[dayKey];
};
var conditionalDatePrefix = (dateNumber, prefix = "0") => {
if (dateNumber < 10) {
return `${prefix}${dateNumber}`;
}
return `${dateNumber}`;
};
var toReverseLookupMap = (lookupMap) => {
if (!lookupMap) return {};
const reverseLookup = Object.keys(lookupMap).reduce((prevMap, currKey) => {
const stringArr = lookupMap[currKey];
let keyMap;
if (stringArr.length === 0) {
const lastKey = currKey.split(".").at(-1);
keyMap = { [lastKey]: lastKey };
} else {
keyMap = stringArr.reduce((prev, currValue) => {
return {
...prev,
[currValue]: currKey
};
}, {});
}
return {
...prevMap,
...keyMap
};
}, {});
return reverseLookup;
};
var shouldGetFromExtraDetails = (path2) => {
const pathArr = path2.split(".");
const idx = pathArr.findIndex((str) => str === "?");
if (idx === -1) return [false, path2];
return [true, pathArr.slice(idx + 1).join("")];
};
var prepDirs = (str, writeDir) => {
const dir = writeDir || DEFAULT_MOCKS_DIR;
const fullPath = import_path.default.join(dir, str);
const fullPath2 = fullPath.split("\\").join("/");
const dirPath = fullPath.split("\\").slice(0, -1).join("/");
if (!(0, import_fs2.existsSync)(dirPath)) {
(0, import_fs2.mkdirSync)(dirPath, { recursive: true });
}
return fullPath2;
};
// src/utils/generation.ts
var handleValue = (value, buildOptions, extraInfo) => {
if (implementsOptions(value)) {
return value.build(buildOptions, extraInfo);
} else if (Array.isArray(value)) {
return new Repetition(value[0]).build(buildOptions, extraInfo);
} else if (value instanceof Object) {
return handleObject(value, buildOptions, extraInfo);
}
return value;
};
var handleObject = (shape, buildOptions, extraInfo) => {
const keyArr = Object.keys(shape);
if (keyArr.length === 0) {
return {};
}
const result = keyArr.reduce((prev, currKey) => {
const currVal = shape[currKey];
const currResult = handleValue(currVal, buildOptions, extraInfo);
let _currResult;
if (currVal?.shouldSpread && currResult instanceof Object) {
_currResult = currResult;
} else {
_currResult = { [currKey]: currResult };
}
return {
...prev,
..._currResult
};
}, {});
return result;
};
// src/utils/selectors.ts
var get = (obj, path2, defaultValue) => {
if (!path2) return void 0;
const pathArray = Array.isArray(path2) ? path2 : path2.match(/([^.[\]])+/g);
const result = pathArray?.reduce(
(prevObj, key) => prevObj && prevObj[key],
obj
);
return result === void 0 ? defaultValue : result;
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
ALPHA,
ALPHA_1,
ALPHA_NUMERIC,
ALPHA_PHRASE,
Builder,
C,
Custom,
DAY_RANGE,
DEFAULT_MOCKS_DIR,
DateRange,
EMAIL,
LOWER_ALPHA,
LOWER_ALPHA_NUMERIC,
NUMERIC,
Option,
Regex,
Repetition,
SELECTION_TYPES,
Shared,
UPPER_ALPHA,
UPPER_ALPHA_NUMERIC,
conditionalDatePrefix,
get,
getDay,
handleObject,
handleValue,
hasTestFunc,
implementsOptions,
isLeapYear,
noop,
prepDirs,
shouldGetFromExtraDetails,
toReverseLookupMap
});