@aklinker1/zero-factory 0.1.1
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/LICENSE +21 -0
- package/README.md +254 -0
- package/dist/factories.d.ts +53 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +68 -0
- package/dist/sequences.d.ts +57 -0
- package/dist/utils.d.ts +14 -0
- package/package.json +31 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Aaron
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
<h1 align="center">@aklinker1/zero-factory</h1>
|
|
2
|
+
|
|
3
|
+
<p align="center">Zero dependency object factory generator for testing.</p>
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { createFactory, createSequence } from "@aklinker1/zero-factory";
|
|
7
|
+
|
|
8
|
+
const userFactory = createFactory<User>({
|
|
9
|
+
id: createSequence("user-"),
|
|
10
|
+
username: "example-username",
|
|
11
|
+
email: () => "example-email-" + Math.random(),
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
userFactory({ id: "test", username: "user" });
|
|
15
|
+
// => {
|
|
16
|
+
// id: "test",
|
|
17
|
+
// username: "user",
|
|
18
|
+
// email: "example-email-0.20088082049103195"
|
|
19
|
+
// }
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
npm i @aklinker1/zero-factory
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**Features:**
|
|
27
|
+
|
|
28
|
+
- ✅ Type-safe
|
|
29
|
+
- ✨ Deeply merge overrides with default values
|
|
30
|
+
- 🔢 Sequence generator for IDs
|
|
31
|
+
- 🎨 "traits" - define multiple variants of default values
|
|
32
|
+
|
|
33
|
+
**Not Supported:**
|
|
34
|
+
|
|
35
|
+
- **Class instances**: Only objects can be created. Factories will not create class instances.
|
|
36
|
+
- **Randomized data**: There are already several mature libraries for generating randomized testing data (`@ngneat/falso`, `faker-js`, `chance`, `casual`, etc).
|
|
37
|
+
|
|
38
|
+
## Usage
|
|
39
|
+
|
|
40
|
+
### Factories
|
|
41
|
+
|
|
42
|
+
Use `createFactory` to build an object factory. Object factories are simple functions that returns an object:
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
const userFactory = createFactory<User>({
|
|
46
|
+
id: "user-id",
|
|
47
|
+
username: "username",
|
|
48
|
+
email: "example@gmail.com",
|
|
49
|
+
preferences: {
|
|
50
|
+
receiveMarketingEmails: true,
|
|
51
|
+
receiveSecurityEmails: true,
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
// typeof userFactory = (overrides?: DeepPartial<User>) => User
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Then, to get an object conforming to the `User` type, just call the factory as a function:
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
const user = userFactory();
|
|
61
|
+
// => {
|
|
62
|
+
// id: "user-id",
|
|
63
|
+
// username: "username",
|
|
64
|
+
// email: "example@gmail.com",
|
|
65
|
+
// preferences: {
|
|
66
|
+
// receiveMarketingEmails: true,
|
|
67
|
+
// receiveSecurityEmails: true,
|
|
68
|
+
// }
|
|
69
|
+
// }
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
You can also override specific properties at any level:
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
const user = userFactory({
|
|
76
|
+
username: "overridden",
|
|
77
|
+
preferences: {
|
|
78
|
+
receiveMarketingEmails: false,
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
// => {
|
|
82
|
+
// id: "user-id",
|
|
83
|
+
// username: "overridden",
|
|
84
|
+
// email: "example@gmail.com",
|
|
85
|
+
// preferences: {
|
|
86
|
+
// receiveMarketingEmails: false,
|
|
87
|
+
// receiveSecurityEmails: true,
|
|
88
|
+
// }
|
|
89
|
+
// }
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
#### Traits
|
|
93
|
+
|
|
94
|
+
If there are common variants or "traits" of an object you want to be able to generate, use `factory.trait(...)`:
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
const userFactory = createFactory({
|
|
98
|
+
// same as above
|
|
99
|
+
}).trait("noEmails", {
|
|
100
|
+
preferences: {
|
|
101
|
+
receiveMarketingEmails: false,
|
|
102
|
+
receiveSecurityEmails: false,
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Then, to generate an object using this trait, the trait is a function defined on the object factory:
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
const user = userFactory.noEmails();
|
|
111
|
+
// => {
|
|
112
|
+
// id: "user-id",
|
|
113
|
+
// username: "username",
|
|
114
|
+
// email: "example@gmail.com",
|
|
115
|
+
// preferences: {
|
|
116
|
+
// receiveMarketingEmails: false,
|
|
117
|
+
// receiveSecurityEmails: false,
|
|
118
|
+
// }
|
|
119
|
+
// }
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
When using a trait and overriding specific properties, the trait's default values are applied before the overrides:
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
const user = userFactory.noEmails({ username: "overridden" });
|
|
126
|
+
// => {
|
|
127
|
+
// id: "user-id",
|
|
128
|
+
// username: "overridden",
|
|
129
|
+
// email: "example@gmail.com",
|
|
130
|
+
// preferences: {
|
|
131
|
+
// receiveMarketingEmails: false,
|
|
132
|
+
// receiveSecurityEmails: false,
|
|
133
|
+
// }
|
|
134
|
+
// }
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Function Defaults
|
|
138
|
+
|
|
139
|
+
In addition to static values, you can pass a function as a value:
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
const userFactory = createFactory({
|
|
143
|
+
email: () => `example.${Math.floor(Math.random() * 1000)}@gmail.com`,
|
|
144
|
+
// ...
|
|
145
|
+
});
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Every time the factory is called, this will call the function and, in this case, generate a different `email` each time:
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
userFactory(); // { email: "example.424@gmail.com", ... }
|
|
152
|
+
userFactory(); // { email: "example.133@gmail.com", ... }
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
This is where [fake data generators](https://www.npmjs.com/search?q=fake%20data) and [sequences](#sequences) come in clutch:
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
import { createFactory, createSequence } from "@aklinker1/zero-factory";
|
|
159
|
+
import {
|
|
160
|
+
randEmail, // () => string
|
|
161
|
+
randUsername, // () => string
|
|
162
|
+
randBoolean, // () => boolean
|
|
163
|
+
} from "@ngneat/falso";
|
|
164
|
+
|
|
165
|
+
const userFactory = createFactory({
|
|
166
|
+
id: createSequence("user-"),
|
|
167
|
+
username: randUsername,
|
|
168
|
+
email: randEmail,
|
|
169
|
+
preferences: {
|
|
170
|
+
receiveMarketingEmails: randBoolean,
|
|
171
|
+
receiveSecurityEmails: randBoolean,
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Sequences
|
|
177
|
+
|
|
178
|
+
For values like IDs, it can be useful to generate them incrementally instead of using randomized values. Use the `createSequence` function to do this:
|
|
179
|
+
|
|
180
|
+
```ts
|
|
181
|
+
const userIdSequence = createSequence((i) => `user-${i}`);
|
|
182
|
+
|
|
183
|
+
userIdSequence(); // "user-0"
|
|
184
|
+
userIdSequence(); // "user-1"
|
|
185
|
+
userIdSequence(); // "user-2"
|
|
186
|
+
// ...
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
The argument `i` is a number starting at 0 that gets incremented by 1 each time the sequence is called. The return value can be anything (string, boolean, object, integer, etc).
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
const intSequence = createSequence((i) => i + 1);
|
|
193
|
+
intSequence(); // 1
|
|
194
|
+
intSequence(); // 2
|
|
195
|
+
intSequence(); // 3
|
|
196
|
+
// ...
|
|
197
|
+
|
|
198
|
+
const boolSequence = createSequence((i) => i % 2 === 0);
|
|
199
|
+
boolSequence(); // true
|
|
200
|
+
boolSequence(); // false
|
|
201
|
+
boolSequence(); // true
|
|
202
|
+
// ...
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
However, the most common types of return values are integers and strings. For both, there is a shorthand:
|
|
206
|
+
|
|
207
|
+
```ts
|
|
208
|
+
const intSequence = createSequence();
|
|
209
|
+
intSequence(); // 0
|
|
210
|
+
intSequence(); // 1
|
|
211
|
+
intSequence(); // 2
|
|
212
|
+
// ...
|
|
213
|
+
|
|
214
|
+
const strSequence = createSequence("prefix-");
|
|
215
|
+
intSequence(); // "prefix-0"
|
|
216
|
+
intSequence(); // "prefix-1"
|
|
217
|
+
intSequence(); // "prefix-2"
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## Future Features?
|
|
223
|
+
|
|
224
|
+
May or may not implement these.
|
|
225
|
+
|
|
226
|
+
- Generate multiple items:
|
|
227
|
+
```ts
|
|
228
|
+
userFactory.many(4, { username: "override" });
|
|
229
|
+
// [
|
|
230
|
+
// { id: "user-0", username: "override", ... },
|
|
231
|
+
// { id: "user-1", username: "override", ... },
|
|
232
|
+
// { id: "user-2", username: "override", ... },
|
|
233
|
+
// { id: "user-3", username: "override", ... },
|
|
234
|
+
// ]
|
|
235
|
+
```
|
|
236
|
+
- Associations:
|
|
237
|
+
|
|
238
|
+
```ts
|
|
239
|
+
const userIdSequence = createSequence("user-")
|
|
240
|
+
const userFactory = createFactory<User>({
|
|
241
|
+
id: userIdSequence,
|
|
242
|
+
// ...
|
|
243
|
+
})
|
|
244
|
+
const postFactory = createFactory<Post>({
|
|
245
|
+
id: createSequence("post-"),
|
|
246
|
+
userId: userIdSequence,
|
|
247
|
+
})
|
|
248
|
+
.associate("user", (user) => ({
|
|
249
|
+
userId: user.id
|
|
250
|
+
}))
|
|
251
|
+
|
|
252
|
+
const user = userFactory(); // { id: "user-0", ... }
|
|
253
|
+
const postFactory.with({ user })(/* optional overrides */) // { id: "post-0", userId: "user-0", ... }
|
|
254
|
+
```
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { type DeepPartial, type FactoryDefaults } from "./utils";
|
|
2
|
+
/**
|
|
3
|
+
* A factory is a:
|
|
4
|
+
* - Function that, when called, returns a new object as defined by the default values.
|
|
5
|
+
* - Object containing any `.traitName(...)` functions that, when called, return a new object applying the trait's defaults over the factory's base defaults.
|
|
6
|
+
* - Object containing immutable modifier functions (`trait`) that, when called, returns a new factory with more ways of generating objects.
|
|
7
|
+
*/
|
|
8
|
+
export type Factory<TObject extends Record<string, any>, TTraits extends string | undefined = undefined> = FactoryFn<TObject> & TraitFactoryFns<TObject, TTraits extends string ? TTraits : never> & FactoryModifiers<TObject, TTraits>;
|
|
9
|
+
/**
|
|
10
|
+
* Function that takes in overrides and returns a new object.
|
|
11
|
+
*/
|
|
12
|
+
export type FactoryFn<TObject> = {
|
|
13
|
+
(overrides?: DeepPartial<TObject>): TObject;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Map of factory functions for traits.
|
|
17
|
+
*/
|
|
18
|
+
export type TraitFactoryFns<TObject, TTraits extends string> = {
|
|
19
|
+
[name in TTraits]: FactoryFn<TObject>;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Functions that modify the factory's type.
|
|
23
|
+
*/
|
|
24
|
+
export type FactoryModifiers<TObject extends Record<string, any>, TTraits extends string | undefined> = {
|
|
25
|
+
/**
|
|
26
|
+
* Add a trait or variant to the factory, allowing developers to create the
|
|
27
|
+
* object with multiple sets of default values.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```ts
|
|
31
|
+
* const userFactory = createFactory<User>({
|
|
32
|
+
* // ...
|
|
33
|
+
* subscribedToEmails: true,
|
|
34
|
+
* })
|
|
35
|
+
* .trait("unsubscribed", { subscribedToEmails: false })
|
|
36
|
+
*
|
|
37
|
+
* userFactory() // { subscribedToEmails: true }
|
|
38
|
+
* userFactory.unsubscribed() // { subscribedToEmails: false }
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* @param name The name of the trait to add
|
|
42
|
+
* @param traitDefaults Default values to apply over the factory's base
|
|
43
|
+
* default values. These can still be overridden when
|
|
44
|
+
* calling the factory function.
|
|
45
|
+
*/
|
|
46
|
+
trait<T2 extends string>(name: T2, traitDefaults: DeepPartial<FactoryDefaults<TObject>>): Factory<TObject, AddTrait<TTraits, T2>>;
|
|
47
|
+
};
|
|
48
|
+
export type AddTrait<T1 extends string | undefined, T2 extends string> = T1 extends string ? T1 | T2 : T2;
|
|
49
|
+
/**
|
|
50
|
+
* Create a function that returns objects of the specified type.
|
|
51
|
+
* @param defaults The default values for the returned object. Each property can be a value or function that return a value.
|
|
52
|
+
*/
|
|
53
|
+
export declare function createFactory<T extends Record<string, any>>(defaults: FactoryDefaults<T>): Factory<T>;
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// src/sequences.ts
|
|
2
|
+
function createSequence(arg) {
|
|
3
|
+
if (!arg)
|
|
4
|
+
return createSequence((i2) => i2);
|
|
5
|
+
if (typeof arg === "string")
|
|
6
|
+
return createSequence((i2) => `${arg}${i2}`);
|
|
7
|
+
let i = 0;
|
|
8
|
+
return () => arg(i++);
|
|
9
|
+
}
|
|
10
|
+
// src/utils.ts
|
|
11
|
+
function deepMerge(base, overrides) {
|
|
12
|
+
if (!isMergeable(overrides))
|
|
13
|
+
return applyOverride(base, overrides);
|
|
14
|
+
return Object.fromEntries(Object.keys({ ...base, ...overrides }).map((key) => [
|
|
15
|
+
key,
|
|
16
|
+
deepMerge(base[key], overrides[key])
|
|
17
|
+
]));
|
|
18
|
+
}
|
|
19
|
+
function applyOverride(base, override) {
|
|
20
|
+
return override === undefined ? base : override;
|
|
21
|
+
}
|
|
22
|
+
function isMergeable(val) {
|
|
23
|
+
return val != null && typeof val === "object" && !Array.isArray(val);
|
|
24
|
+
}
|
|
25
|
+
function resolveDefaults(val) {
|
|
26
|
+
const result = {};
|
|
27
|
+
for (const key in val) {
|
|
28
|
+
if (Object.prototype.hasOwnProperty.call(val, key)) {
|
|
29
|
+
const defaultValue = val[key];
|
|
30
|
+
if (typeof defaultValue === "function") {
|
|
31
|
+
result[key] = defaultValue();
|
|
32
|
+
} else if (isMergeable(defaultValue)) {
|
|
33
|
+
result[key] = resolveDefaults(defaultValue);
|
|
34
|
+
} else {
|
|
35
|
+
result[key] = defaultValue;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// src/factories.ts
|
|
43
|
+
function createFactory(defaults) {
|
|
44
|
+
return createFactoryInternal(defaults, {});
|
|
45
|
+
}
|
|
46
|
+
function createFactoryInternal(defaults, traits) {
|
|
47
|
+
console.log("Created factory:", { defaults, traits });
|
|
48
|
+
return Object.assign((overrides) => generateObject(defaults, overrides), {
|
|
49
|
+
trait: (name, traitDefaults) => createFactoryInternal(defaults, {
|
|
50
|
+
...traits,
|
|
51
|
+
[name]: deepMerge(defaults, traitDefaults)
|
|
52
|
+
}),
|
|
53
|
+
...Object.fromEntries(Object.entries(traits).map(([name, traitDefaults]) => {
|
|
54
|
+
return [
|
|
55
|
+
name,
|
|
56
|
+
(overrides) => generateObject(traitDefaults, overrides)
|
|
57
|
+
];
|
|
58
|
+
}))
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
function generateObject(defaults, overrides) {
|
|
62
|
+
const resolvedDefaults = resolveDefaults(defaults);
|
|
63
|
+
return deepMerge(resolvedDefaults, overrides);
|
|
64
|
+
}
|
|
65
|
+
export {
|
|
66
|
+
createSequence,
|
|
67
|
+
createFactory
|
|
68
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A simple function that returns a value based on how many times the function
|
|
3
|
+
* has been called.
|
|
4
|
+
*/
|
|
5
|
+
export type Sequence<T> = () => T;
|
|
6
|
+
/**
|
|
7
|
+
* Defines what sequence values look like.
|
|
8
|
+
*/
|
|
9
|
+
export type SequenceDefinition<T> = (i: number) => T;
|
|
10
|
+
/**
|
|
11
|
+
* Shorthand for creating a sequence of incrementing integers staring at 0.
|
|
12
|
+
*
|
|
13
|
+
* Same as `createSequence((i) => i)`.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* const seq = createSequence();
|
|
18
|
+
* seq(); // 0
|
|
19
|
+
* seq(); // 1
|
|
20
|
+
* seq(); // 2
|
|
21
|
+
* // ...
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare function createSequence(): Sequence<number>;
|
|
25
|
+
/**
|
|
26
|
+
* Shorthand for creating a sequence of incrementing strings staring at 0.
|
|
27
|
+
*
|
|
28
|
+
* Same as `createSequence((i) => `${prefix}${i})`.
|
|
29
|
+
*
|
|
30
|
+
* @param prefix The string to put in front of the incrementing integer.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```ts
|
|
34
|
+
* const seq = createSequence("user-");
|
|
35
|
+
* seq(); // "user-0"
|
|
36
|
+
* seq(); // "user-1"
|
|
37
|
+
* seq(); // "user-2"
|
|
38
|
+
* // ...
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export declare function createSequence(prefix: string): Sequence<string>;
|
|
42
|
+
/**
|
|
43
|
+
* Use a custom function to generate the sequence values.
|
|
44
|
+
*
|
|
45
|
+
* @param fn Callback called each time the sequence needs to generate a value.
|
|
46
|
+
* The first argument, `i`, starts at 0.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```ts
|
|
50
|
+
* const seq = createSequence((i) => `example-${i * 2}`)
|
|
51
|
+
* seq(); // "example-0"
|
|
52
|
+
* seq(); // "example-2"
|
|
53
|
+
* seq(); // "example-4"
|
|
54
|
+
* // ...
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export declare function createSequence<T>(fn: SequenceDefinition<T>): Sequence<T>;
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deeply make objects partial, but ignoring arrays.
|
|
3
|
+
*/
|
|
4
|
+
export type DeepPartial<T> = T extends Array<any> ? T : T extends Record<string, any> ? {
|
|
5
|
+
[key in keyof T]?: DeepPartial<T[key]>;
|
|
6
|
+
} : T;
|
|
7
|
+
/**
|
|
8
|
+
* Deep merge objects, not arrays. Only override values with `null`, `undefined` does not override the base value.
|
|
9
|
+
*/
|
|
10
|
+
export declare function deepMerge<T>(base: T, overrides: DeepPartial<T>): T;
|
|
11
|
+
export type FactoryDefaults<T> = T extends Array<any> ? T | (() => T) : T extends Record<string, any> ? {
|
|
12
|
+
[key in keyof T]: FactoryDefaults<T[key]>;
|
|
13
|
+
} : T extends Function ? never : T | (() => T);
|
|
14
|
+
export declare function resolveDefaults<T extends Record<string, any>>(val: FactoryDefaults<T>): T;
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aklinker1/zero-factory",
|
|
3
|
+
"description": "Zero dependency object factory generator for testing",
|
|
4
|
+
"version": "0.1.1",
|
|
5
|
+
"packageManager": "bun@1.2.20",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"test": "bun test",
|
|
19
|
+
"test:watch": "bun test --watch",
|
|
20
|
+
"build": "bun run build.ts"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@aklinker1/check": "^2.1.0",
|
|
24
|
+
"@types/bun": "latest",
|
|
25
|
+
"jsr": "^0.13.5",
|
|
26
|
+
"oxlint": "^1.11.2",
|
|
27
|
+
"prettier": "^3.6.2",
|
|
28
|
+
"publint": "^0.3.12",
|
|
29
|
+
"typescript": "^5"
|
|
30
|
+
}
|
|
31
|
+
}
|