struct-fakerator 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,36 @@
1
+ # 假資料結構產生器
2
+
3
+ ## 用途
4
+
5
+ 使用 faker js 時是單一純值不能產生結構,需要自己手動組合結構,此專案利用撰寫設定檔的方式來產生一個特定的假資料函數,減少手動組合結構的麻煩。
6
+
7
+ ```ts
8
+ const test = {
9
+ type: 'obj',
10
+ content: {
11
+ name: {
12
+ type: 'value',
13
+ generateFn: () => 'hello',
14
+ },
15
+ list: {
16
+ type: 'arr',
17
+ len: 5,
18
+ item: {
19
+ type: 'value',
20
+ generateFn: () => 10,
21
+ },
22
+ },
23
+ },
24
+ };
25
+
26
+ const generateFn = createGeneratorByType(test);
27
+
28
+ console.log(generateFn());
29
+
30
+ /*
31
+ {
32
+ name: "hello"
33
+ list: [10, 10, 10, 10, 10,]
34
+ }
35
+ */
36
+ ```
package/biome.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "$schema": "https://biomejs.dev/schemas/1.7.1/schema.json",
3
+ "organizeImports": {
4
+ "enabled": true
5
+ },
6
+ "linter": {
7
+ "enabled": true,
8
+ "rules": {
9
+ "recommended": true
10
+ }
11
+ },
12
+ "formatter": {
13
+ "indentStyle": "space"
14
+ },
15
+ "javascript": {
16
+ "formatter": {
17
+ "quoteStyle": "double"
18
+ }
19
+ }
20
+ }
package/jsconfig.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "es6",
4
+ "module": "CommonJS",
5
+ "baseUrl": ".",
6
+ "paths": {
7
+ "*": [
8
+ "node_modules/*"
9
+ ]
10
+ },
11
+ "checkJs": true,
12
+ },
13
+ "include": [
14
+ "./**/*"
15
+ ],
16
+ "exclude": [
17
+ "node_modules",
18
+ "**/node_modules/*"
19
+ ]
20
+ }
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "struct-fakerator",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "type": "module",
7
+ "scripts": {
8
+ "start": "node src/index.js",
9
+ "test": "vitest"
10
+ },
11
+ "keywords": [],
12
+ "author": "FizzyElt",
13
+ "license": "ISC",
14
+ "devDependencies": {
15
+ "@biomejs/biome": "^1.7.1",
16
+ "@faker-js/faker": "^8.4.1",
17
+ "vitest": "^1.5.2"
18
+ },
19
+ "dependencies": {
20
+ "zod": "^3.23.6"
21
+ },
22
+ "publishConfig": {
23
+ "access": "public"
24
+ }
25
+ }
@@ -0,0 +1,45 @@
1
+ import { z } from "zod";
2
+
3
+ export const valueConfigScheme = z.object({
4
+ type: z.string().regex(/^value$/, { message: "invalid type string" }),
5
+ generateFn: z.function(),
6
+ });
7
+
8
+ export const selectionConfigScheme = z.object({
9
+ type: z.string().regex(/^select$/, { message: "invalid type string" }),
10
+ items: z.any().array().nonempty({ message: "items can not be empty" }),
11
+ });
12
+
13
+ export const arrayConfigScheme = z.object({
14
+ type: z.string().regex(/^arr$/, { message: "invalid type string" }),
15
+ item: z.object({}),
16
+ len: z.number().nonnegative(),
17
+ });
18
+
19
+ export const tupleConfigScheme = z.object({
20
+ type: z.string().regex(/^tuple$/, { message: "invalid type string" }),
21
+ configItems: z.any().array(),
22
+ });
23
+
24
+ export const objConfigScheme = z.object({
25
+ type: z.string().regex(/^obj$/, { message: "invalid type string" }),
26
+ content: z.object({}),
27
+ });
28
+
29
+ export const boundedSeriesScheme = z
30
+ .object({
31
+ type: z
32
+ .string()
33
+ .regex(/^bounded_series$/, { message: "invalid type string" }),
34
+ upperLimit: z.number().nonnegative(),
35
+ lowerLimit: z.number().nonnegative(),
36
+ createInitValue: z.function().args().returns(z.number()),
37
+ count: z.number().nonnegative(),
38
+ })
39
+ .refine(({ upperLimit, lowerLimit }) => upperLimit >= lowerLimit, {
40
+ message: "lowerLimit can not greater then upperLimit",
41
+ })
42
+ .refine(({ createInitValue }) => typeof createInitValue() === "number", {
43
+ message: "createInitValue is not return number",
44
+ path: ["createInitValue"],
45
+ });
@@ -0,0 +1,109 @@
1
+ import { faker } from "@faker-js/faker";
2
+ import {
3
+ valueConfigScheme,
4
+ arrayConfigScheme,
5
+ selectionConfigScheme,
6
+ tupleConfigScheme,
7
+ objConfigScheme,
8
+ boundedSeriesScheme,
9
+ } from "./config_scheme";
10
+
11
+ /**
12
+ * value
13
+ * @param {function} generateFn - The function used to generate the value.
14
+ * @return {ValueConfig} The configuration object with the type "value" and the provided generate function.
15
+ */
16
+ export const createValueConfig = (generateFn) => {
17
+ const config = {
18
+ type: "value",
19
+ generateFn,
20
+ };
21
+
22
+ valueConfigScheme.parse(config);
23
+
24
+ return config;
25
+ };
26
+
27
+ /**
28
+ * selection
29
+ * @param {Array} items - The array of items to choose from.
30
+ * @return {SelectionConfig} The configuration object with the type "select" and the provided items.
31
+ */
32
+ export const createSelectionConfig = (items) => {
33
+ const config = { type: "select", items };
34
+
35
+ selectionConfigScheme.parse(config);
36
+
37
+ return config;
38
+ };
39
+
40
+ /**
41
+ * object
42
+ * @param {object} content
43
+ * @return {ObjectConfig}
44
+ */
45
+ export const createObjectConfig = (content) => {
46
+ const config = { type: "obj", content };
47
+
48
+ objConfigScheme.parse(config);
49
+
50
+ return config;
51
+ };
52
+
53
+ /**
54
+ * array
55
+ * @param {object} item
56
+ * @param {number} len
57
+ * @return {ArrayConfig}
58
+ */
59
+ export const createArrayConfig = (item, len) => {
60
+ const config = { type: "arr", item, len };
61
+
62
+ arrayConfigScheme.parse(config);
63
+
64
+ return config;
65
+ };
66
+
67
+ /**
68
+ * tuple
69
+ * @param {Array} configItems
70
+ * @return {TupleConfig}
71
+ */
72
+ export const createTupleConfig = (configItems) => {
73
+ const config = {
74
+ type: "tuple",
75
+ configItems,
76
+ };
77
+
78
+ tupleConfigScheme.parse(config);
79
+
80
+ return config;
81
+ };
82
+
83
+ /**
84
+ * bounded series
85
+ * @param {{ upperLimit: number, lowerLimit: number, createInitValue: () => number, count: number }} config
86
+ * @return {BoundedSeriesConfig}
87
+ */
88
+ export const createBoundedSeriesConfig = (config) => {
89
+ const newConfig = {
90
+ type: "bounded_series",
91
+ ...config,
92
+ };
93
+
94
+ boundedSeriesScheme.parse(newConfig);
95
+
96
+ return newConfig;
97
+ };
98
+
99
+ // int value
100
+ export const createIntValueConfig = (option) =>
101
+ createValueConfig(() => faker.number.int(option));
102
+
103
+ // float value
104
+ export const createFloatValueConfig = (option) =>
105
+ createValueConfig(() => faker.number.float(option));
106
+
107
+ // email value
108
+ export const createEmailValueConfig = (option) =>
109
+ createValueConfig(() => faker.internet.email(option));
@@ -0,0 +1,72 @@
1
+ import { expect, expectTypeOf, test } from "vitest";
2
+
3
+ import {
4
+ createValueConfig,
5
+ createSelectionConfig,
6
+ createArrayConfig,
7
+ createTupleConfig,
8
+ createObjectConfig,
9
+ createBoundedSeriesConfig,
10
+ } from "./create_config";
11
+ import { createGeneratorByType } from "./create_generator_fn";
12
+
13
+ test("createValueConfig", () => {
14
+ const valueConfig = createValueConfig(() => 44);
15
+
16
+ expect(valueConfig.type).toBe("value");
17
+ expect(valueConfig.generateFn).toBeTypeOf("function");
18
+ });
19
+
20
+ test("createSelectionConfig", () => {
21
+ const options = [1, 2, 3, 4];
22
+ const selectionConfig = createSelectionConfig([1, 2, 3, 4]);
23
+
24
+ expect(selectionConfig.type).toBe("select");
25
+ expect(selectionConfig.items).toEqual(options);
26
+ });
27
+
28
+ test("createArrayConfig", () => {
29
+ const valueConfig = createValueConfig(() => 44);
30
+ const arrConfig = createArrayConfig(valueConfig, 20);
31
+
32
+ expect(arrConfig.type).toBe("arr");
33
+ expect(arrConfig.len).toBe(20);
34
+ expect(arrConfig.item).toEqual(valueConfig);
35
+ });
36
+
37
+ test("createTupleConfig", () => {
38
+ const value1Config = createValueConfig(() => 123);
39
+ const value2Config = createValueConfig(() => "hello");
40
+
41
+ const tupleConfig = createTupleConfig([value1Config, value2Config]);
42
+
43
+ expect(tupleConfig.type).toBe("tuple");
44
+ expect(tupleConfig.configItems).toEqual([value1Config, value2Config]);
45
+ });
46
+
47
+ test("createObjConfig", () => {
48
+ const value1Config = createValueConfig(() => 32);
49
+ const value2Config = createValueConfig(() => "frank");
50
+
51
+ const objConfig = createObjectConfig({
52
+ name: value2Config,
53
+ age: value1Config,
54
+ });
55
+
56
+ expect(objConfig.type).toBe("obj");
57
+ expect(objConfig.content).toEqual({ name: value2Config, age: value1Config });
58
+ });
59
+
60
+ test("createBoundedSeriesConfig", () => {
61
+ const boundedSeriesConfig = createBoundedSeriesConfig({
62
+ count: 1,
63
+ upperLimit: 1.2,
64
+ lowerLimit: 1.0,
65
+ createInitValue: () => 40,
66
+ });
67
+
68
+ expect(boundedSeriesConfig.type).toBe("bounded_series");
69
+ expect(boundedSeriesConfig.upperLimit).toBe(1.2);
70
+ expect(boundedSeriesConfig.lowerLimit).toBe(1.0);
71
+ expect(boundedSeriesConfig.createInitValue).toBeTypeOf("function");
72
+ });
@@ -0,0 +1,139 @@
1
+ import { faker } from "@faker-js/faker";
2
+
3
+ import {
4
+ valueConfigScheme,
5
+ selectionConfigScheme,
6
+ arrayConfigScheme,
7
+ objConfigScheme,
8
+ boundedSeriesScheme,
9
+ tupleConfigScheme,
10
+ } from "./config_scheme";
11
+
12
+ /**
13
+ * value
14
+ * @param {ValueConfig} config
15
+ * @param {number=} level
16
+ * @return {function}
17
+ */
18
+ export const createValueGenerator = (config, level = 0) => {
19
+ valueConfigScheme.parse(config);
20
+
21
+ return config.generateFn;
22
+ };
23
+
24
+ /**
25
+ * selection
26
+ * @param {SelectionConfig} config
27
+ * @param {number=} level
28
+ * @return {function} The configuration object with the type "select" and the provided items.
29
+ */
30
+ export const createSelectionGenerator = (config, level = 0) => {
31
+ selectionConfigScheme.parse(config);
32
+
33
+ const { items } = config;
34
+
35
+ return () => items[faker.number.int(items.length - 1)];
36
+ };
37
+
38
+ /**
39
+ * object
40
+ * @param {ObjectConfig} config
41
+ * @param {number=} level
42
+ * @return {() => object}
43
+ */
44
+ export const createObjectGenerator = (config, level = 0) => {
45
+ objConfigScheme.parse(config);
46
+
47
+ const keyWithFns = Object.entries(config.content).map(([key, subConfig]) => [
48
+ key,
49
+ createGeneratorByType(subConfig, level + 1),
50
+ ]);
51
+
52
+ return () => {
53
+ const result = {};
54
+ for (const [key, generateFn] of keyWithFns) {
55
+ result[key] = generateFn();
56
+ }
57
+ return result;
58
+ };
59
+ };
60
+
61
+ /**
62
+ * array
63
+ * @param {ArrayConfig} config
64
+ * @param {number=} level
65
+ * @return {() => Array}
66
+ */
67
+ export const createArrayGenerator = (config, level = 0) => {
68
+ arrayConfigScheme.parse(config);
69
+
70
+ const itemGeneratorFn = createGeneratorByType(config.item, level + 1);
71
+
72
+ return () => Array.from({ length: config.len ?? 0 }, itemGeneratorFn);
73
+ };
74
+
75
+ /**
76
+ * tuple
77
+ * @param {TupleConfig} config
78
+ * @param {number=} level
79
+ * @return {() => Array}
80
+ */
81
+ export const createTupleGenerator = (config, level = 0) => {
82
+ tupleConfigScheme.parse(config);
83
+
84
+ const itemsFns = config.configItems.map(createGeneratorByType);
85
+
86
+ return () => itemsFns.map((generateFn) => generateFn());
87
+ };
88
+
89
+ /**
90
+ * bounded series
91
+ * @param {BoundedSeriesConfig} config
92
+ * @param {number} level
93
+ * @return {() => Array<number>}
94
+ */
95
+ export const createBoundedSeriesGenerator = (config, level = 0) => {
96
+ boundedSeriesScheme.parse(config);
97
+
98
+ const { upperLimit, lowerLimit, createInitValue, count } = config;
99
+
100
+ return () => {
101
+ let value = createInitValue();
102
+
103
+ const boundedSeries = [];
104
+
105
+ for (let i = 0; i < count; i++) {
106
+ value = faker.number.float({ max: upperLimit, min: lowerLimit }) * value;
107
+ boundedSeries.push(value);
108
+ }
109
+
110
+ return boundedSeries;
111
+ };
112
+ };
113
+
114
+ /**
115
+ *
116
+ * @param {ValueConfig | SelectionConfig | ArrayConfig | ObjectConfig | TupleConfig | BoundedSeriesConfig} config
117
+ * @param {number=} level
118
+ * @return {function}
119
+ */
120
+ export const createGeneratorByType = (config, level = 0) => {
121
+ switch (config.type) {
122
+ case "obj":
123
+ return createObjectGenerator(config, level);
124
+ case "arr":
125
+ return createArrayGenerator(config, level);
126
+ case "select":
127
+ return createSelectionGenerator(config, level);
128
+ case "tuple":
129
+ return createTupleGenerator(config, level);
130
+ case "value":
131
+ return createValueGenerator(config, level);
132
+ case "bounded_series":
133
+ return createBoundedSeriesGenerator(config, level);
134
+ default:
135
+ throw Error(
136
+ `level ${level} config type "${config.type}" is not supported`,
137
+ );
138
+ }
139
+ };
@@ -0,0 +1,132 @@
1
+ import { describe, test, expect } from "vitest";
2
+ import { ZodError } from "zod";
3
+ import {
4
+ createValueGenerator,
5
+ createSelectionGenerator,
6
+ createArrayGenerator,
7
+ createTupleGenerator,
8
+ createObjectGenerator,
9
+ createBoundedSeriesGenerator,
10
+ } from "./create_generator_fn";
11
+
12
+ describe("createValueGenerator", () => {
13
+ test("normal", () => {
14
+ const value = createValueGenerator({
15
+ type: "value",
16
+ generateFn: () => 50,
17
+ })();
18
+
19
+ expect(value).toBe(50);
20
+
21
+ const value2 = createValueGenerator({
22
+ type: "value",
23
+ generateFn: () => ({ age: 100, name: "hello" }),
24
+ })();
25
+
26
+ expect(value2).toEqual({ age: 100, name: "hello" });
27
+ });
28
+ });
29
+
30
+ describe("createSelectionGenerator", () => {
31
+ test("normal", () => {
32
+ const value = createSelectionGenerator({
33
+ type: "select",
34
+ items: [1],
35
+ })();
36
+
37
+ expect(value).toBe(1);
38
+
39
+ const value2 = createSelectionGenerator({
40
+ type: "select",
41
+ items: [30, 30, 30, 30],
42
+ })();
43
+
44
+ expect(value2).toBe(30);
45
+ });
46
+ });
47
+
48
+ describe("createArrayGenerator", () => {
49
+ test("normal", () => {
50
+ const list = createArrayGenerator({
51
+ type: "arr",
52
+ len: 5,
53
+ item: { type: "value", generateFn: () => ({ age: 42 }) },
54
+ })();
55
+
56
+ expect(list).toEqual([
57
+ { age: 42 },
58
+ { age: 42 },
59
+ { age: 42 },
60
+ { age: 42 },
61
+ { age: 42 },
62
+ ]);
63
+ });
64
+ });
65
+
66
+ describe("createTupleGenerator", () => {
67
+ test("normal", () => {
68
+ const tuple = createTupleGenerator({
69
+ type: "tuple",
70
+ configItems: [
71
+ { type: "value", generateFn: () => 225 },
72
+ { type: "value", generateFn: () => "hello world" },
73
+ ],
74
+ })();
75
+
76
+ expect(tuple.length).toBe(2);
77
+ const [num, str] = tuple;
78
+ expect(num).toBe(225);
79
+ expect(str).toBe("hello world");
80
+ });
81
+ });
82
+
83
+ describe("createObjectGenerator", () => {
84
+ test("normal", () => {
85
+ const obj = createObjectGenerator({
86
+ type: "obj",
87
+ content: {
88
+ name: { type: "value", generateFn: () => "John" },
89
+ age: { type: "value", generateFn: () => 50 },
90
+ location: { type: "value", generateFn: () => "Taiwan" },
91
+ },
92
+ })();
93
+
94
+ expect(obj).toEqual({ name: "John", age: 50, location: "Taiwan" });
95
+ });
96
+ });
97
+
98
+ describe("createBoundedSeriesGenerator", () => {
99
+ test("normal", () => {
100
+ const upperLimit = 1.1;
101
+ const lowerLimit = 0.9;
102
+ const initValue = 100;
103
+ const count = 100;
104
+
105
+ const list = createBoundedSeriesGenerator({
106
+ type: "bounded_series",
107
+ upperLimit,
108
+ lowerLimit,
109
+ createInitValue: () => initValue,
110
+ count,
111
+ })();
112
+
113
+ for (let i = 0; i < count; i++) {
114
+ const value = list[i];
115
+ if (i === 0) {
116
+ const ratio = value / initValue;
117
+
118
+ expect(ratio).toBeLessThanOrEqual(upperLimit);
119
+ expect(ratio).toBeGreaterThanOrEqual(lowerLimit);
120
+
121
+ continue;
122
+ }
123
+
124
+ const prevValue = list[i - 1];
125
+
126
+ const ratio = value / prevValue;
127
+
128
+ expect(ratio).toBeLessThanOrEqual(upperLimit);
129
+ expect(ratio).toBeGreaterThanOrEqual(lowerLimit);
130
+ }
131
+ });
132
+ });
package/src/type.js ADDED
@@ -0,0 +1,23 @@
1
+ /**
2
+ * @typedef {{ type: 'value', generateFn: Function}} ValueConfig
3
+ */
4
+
5
+ /**
6
+ * @typedef {{ type: 'select', items: Array }} SelectionConfig
7
+ */
8
+
9
+ /**
10
+ * @typedef {{ type: 'arr', item: Object, len: number }} ArrayConfig
11
+ */
12
+
13
+ /**
14
+ * @typedef {{ type: 'tuple', configItems: Array }} TupleConfig
15
+ */
16
+
17
+ /**
18
+ * @typedef {{ type: 'obj', content: Object }} ObjectConfig
19
+ */
20
+
21
+ /**
22
+ * @typedef {{ type: 'bounded_series', upperLimit: number, lowerLimit: number, createInitValue: function, count: number }} BoundedSeriesConfig
23
+ */