@jaypie/mcp 0.3.2 โ 0.4.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/dist/createMcpServer.d.ts +7 -1
- package/dist/index.js +26 -3135
- package/dist/index.js.map +1 -1
- package/dist/suite.d.ts +1 -0
- package/dist/suite.js +2442 -0
- package/dist/suite.js.map +1 -0
- package/package.json +8 -3
- package/release-notes/constructs/1.2.17.md +11 -0
- package/release-notes/fabric/0.1.2.md +11 -0
- package/release-notes/fabric/0.1.3.md +25 -0
- package/release-notes/fabric/0.1.4.md +42 -0
- package/release-notes/mcp/0.3.3.md +12 -0
- package/release-notes/mcp/0.3.4.md +36 -0
- package/release-notes/mcp/0.4.0.md +27 -0
- package/release-notes/testkit/1.2.15.md +23 -0
- package/skills/agents.md +25 -0
- package/skills/aws.md +107 -0
- package/skills/cdk.md +141 -0
- package/skills/cicd.md +152 -0
- package/skills/datadog.md +129 -0
- package/skills/debugging.md +148 -0
- package/skills/dns.md +134 -0
- package/skills/dynamodb.md +140 -0
- package/skills/errors.md +142 -0
- package/skills/fabric.md +191 -0
- package/skills/index.md +7 -0
- package/skills/jaypie.md +100 -0
- package/skills/legacy.md +97 -0
- package/skills/logs.md +160 -0
- package/skills/mocks.md +174 -0
- package/skills/models.md +195 -0
- package/skills/releasenotes.md +94 -0
- package/skills/secrets.md +155 -0
- package/skills/services.md +175 -0
- package/skills/style.md +190 -0
- package/skills/tests.md +209 -0
- package/skills/tools.md +127 -0
- package/skills/topics.md +116 -0
- package/skills/variables.md +146 -0
- package/skills/writing.md +153 -0
- package/prompts/Branch_Management.md +0 -34
- package/prompts/Development_Process.md +0 -89
- package/prompts/Jaypie_Agent_Rules.md +0 -110
- package/prompts/Jaypie_Auth0_Express_Mongoose.md +0 -736
- package/prompts/Jaypie_Browser_and_Frontend_Web_Packages.md +0 -18
- package/prompts/Jaypie_CDK_Constructs_and_Patterns.md +0 -430
- package/prompts/Jaypie_CICD_with_GitHub_Actions.md +0 -371
- package/prompts/Jaypie_Commander_CLI_Package.md +0 -166
- package/prompts/Jaypie_Core_Errors_and_Logging.md +0 -39
- package/prompts/Jaypie_DynamoDB_Package.md +0 -774
- package/prompts/Jaypie_Eslint_NPM_Package.md +0 -78
- package/prompts/Jaypie_Express_Package.md +0 -630
- package/prompts/Jaypie_Fabric_Commander.md +0 -411
- package/prompts/Jaypie_Fabric_LLM.md +0 -312
- package/prompts/Jaypie_Fabric_Lambda.md +0 -308
- package/prompts/Jaypie_Fabric_MCP.md +0 -316
- package/prompts/Jaypie_Fabric_Package.md +0 -513
- package/prompts/Jaypie_Fabricator.md +0 -617
- package/prompts/Jaypie_Ideal_Project_Structure.md +0 -78
- package/prompts/Jaypie_Init_CICD_with_GitHub_Actions.md +0 -1186
- package/prompts/Jaypie_Init_Express_on_Lambda.md +0 -115
- package/prompts/Jaypie_Init_Jaypie_CDK_Package.md +0 -35
- package/prompts/Jaypie_Init_Lambda_Package.md +0 -505
- package/prompts/Jaypie_Init_Monorepo_Project.md +0 -44
- package/prompts/Jaypie_Init_Project_Subpackage.md +0 -65
- package/prompts/Jaypie_Legacy_Patterns.md +0 -15
- package/prompts/Jaypie_Llm_Calls.md +0 -449
- package/prompts/Jaypie_Llm_Tools.md +0 -155
- package/prompts/Jaypie_MCP_Package.md +0 -281
- package/prompts/Jaypie_Mocks_and_Testkit.md +0 -137
- package/prompts/Jaypie_Repokit.md +0 -103
- package/prompts/Jaypie_Scrub.md +0 -177
- package/prompts/Jaypie_Streaming.md +0 -467
- package/prompts/Templates_CDK_Subpackage.md +0 -115
- package/prompts/Templates_Express_Subpackage.md +0 -187
- package/prompts/Templates_Project_Monorepo.md +0 -326
- package/prompts/Templates_Project_Subpackage.md +0 -93
- package/prompts/Write_Efficient_Prompt_Guides.md +0 -48
- package/prompts/Write_and_Maintain_Engaging_Readme.md +0 -67
|
@@ -1,617 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: coding style resource, helpful when stuck on lint errors
|
|
3
|
-
globs: packages/fabricator/**
|
|
4
|
-
version: 0.3.0
|
|
5
|
-
---
|
|
6
|
-
# Brief Guide to @jaypie/fabricator
|
|
7
|
-
|
|
8
|
-
## Overview
|
|
9
|
-
|
|
10
|
-
`@jaypie/fabricator` is a test data generation library built on top of `@faker-js/faker` that provides deterministic, seeded data generation with additional utilities for randomness and complex data patterns.
|
|
11
|
-
|
|
12
|
-
## Core Concepts
|
|
13
|
-
|
|
14
|
-
### 1. The Fabricator Class
|
|
15
|
-
|
|
16
|
-
The `Fabricator` class wraps faker.js and provides:
|
|
17
|
-
- **Deterministic seeding**: Pass a string, number, or UUID to get reproducible test data
|
|
18
|
-
- **Direct faker access**: All faker modules are proxied for convenience
|
|
19
|
-
- **Enhanced random number generation**: Built-in `random()` function with advanced options
|
|
20
|
-
- **Custom generators**: Specialized methods like `words()` and `generate.person()`
|
|
21
|
-
- **Unique identifiers**: Each fabricator has an `id` (UUID) and `name` property
|
|
22
|
-
- **Nested fabricators**: Support for hierarchical data generation with child fabricators
|
|
23
|
-
|
|
24
|
-
```typescript
|
|
25
|
-
import { fabricator } from "@jaypie/fabricator";
|
|
26
|
-
|
|
27
|
-
// Create unseeded (random) fabricator
|
|
28
|
-
const fab = fabricator();
|
|
29
|
-
|
|
30
|
-
// Create seeded (deterministic) fabricator
|
|
31
|
-
const seededFab = fabricator("my-seed");
|
|
32
|
-
const uuidFab = fabricator("550e8400-e29b-41d4-a716-446655440000");
|
|
33
|
-
|
|
34
|
-
// Access built-in properties
|
|
35
|
-
console.log(fab.id); // UUID
|
|
36
|
-
console.log(fab.name); // Generated name like "Happy Mountain"
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
**Options**:
|
|
40
|
-
```typescript
|
|
41
|
-
const fab = fabricator("my-seed", {
|
|
42
|
-
name: "Custom Name", // String or function
|
|
43
|
-
generator: {
|
|
44
|
-
name: ({ fabricator }) => fabricator.faker.person.firstName()
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
### 2. Deterministic Seeding
|
|
50
|
-
|
|
51
|
-
Any string or number can be used as a seed. The same seed always produces the same output:
|
|
52
|
-
|
|
53
|
-
```typescript
|
|
54
|
-
const fab1 = fabricator("test-seed");
|
|
55
|
-
const fab2 = fabricator("test-seed");
|
|
56
|
-
|
|
57
|
-
fab1.faker.person.firstName(); // "Jordan"
|
|
58
|
-
fab2.faker.person.firstName(); // "Jordan" (same!)
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
**UUID Seeding**: UUIDs are converted to numeric arrays using `uuidToBytes` for efficient seeding.
|
|
62
|
-
|
|
63
|
-
**Environment Variable**: Set `PROJECT_SEED` environment variable to provide a default seed for all unseeded fabricators:
|
|
64
|
-
```typescript
|
|
65
|
-
process.env.PROJECT_SEED = "my-project-seed";
|
|
66
|
-
const fab = fabricator(); // Uses PROJECT_SEED
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
### 3. Random Number Generation
|
|
70
|
-
|
|
71
|
-
The `random()` function provides advanced randomness with multiple distribution types:
|
|
72
|
-
|
|
73
|
-
**Basic Usage:**
|
|
74
|
-
```typescript
|
|
75
|
-
fab.random(); // 0-1 float
|
|
76
|
-
fab.random({ min: 1, max: 10 }); // 1-10 float
|
|
77
|
-
fab.random({ min: 1, max: 10, integer: true }); // 1-10 integer
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
**All Options:**
|
|
81
|
-
```typescript
|
|
82
|
-
fab.random({
|
|
83
|
-
min: 1, // Minimum value (default: 0)
|
|
84
|
-
max: 10, // Maximum value (default: 1)
|
|
85
|
-
integer: true, // Return integer instead of float (default: false)
|
|
86
|
-
mean: 5, // For normal distribution
|
|
87
|
-
stddev: 2, // For normal distribution
|
|
88
|
-
precision: 2, // Decimal places
|
|
89
|
-
currency: true, // Shorthand for precision: 2
|
|
90
|
-
start: 100, // Add offset to result
|
|
91
|
-
seed: "override" // Override fabricator's seed for this call only
|
|
92
|
-
});
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
**Smart Defaults:**
|
|
96
|
-
- If only `min` is set, `max` defaults to `min * 2`
|
|
97
|
-
- Normal distribution with `mean/stddev` can optionally be clamped by `min/max`
|
|
98
|
-
|
|
99
|
-
**Common Patterns:**
|
|
100
|
-
```typescript
|
|
101
|
-
// Currency values
|
|
102
|
-
fab.random({ min: 10, max: 1000, currency: true }); // e.g., 542.73
|
|
103
|
-
|
|
104
|
-
// Percentages
|
|
105
|
-
fab.random({ min: 0, max: 100, integer: true }); // e.g., 42
|
|
106
|
-
|
|
107
|
-
// Weighted random with normal distribution
|
|
108
|
-
fab.random({ mean: 50, stddev: 15, min: 0, max: 100 }); // Clusters around 50
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
### 4. Faker Integration
|
|
112
|
-
|
|
113
|
-
All faker modules are directly accessible through getters:
|
|
114
|
-
|
|
115
|
-
```typescript
|
|
116
|
-
fab.person.firstName();
|
|
117
|
-
fab.internet.email();
|
|
118
|
-
fab.location.city();
|
|
119
|
-
fab.company.name();
|
|
120
|
-
// ... all faker modules available
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
### 5. Custom Methods
|
|
124
|
-
|
|
125
|
-
#### `generate.words()`: Two-Word Combinations
|
|
126
|
-
Generates one of three patterns randomly:
|
|
127
|
-
- adjective + noun
|
|
128
|
-
- adjective + verb
|
|
129
|
-
- noun + verb
|
|
130
|
-
|
|
131
|
-
```typescript
|
|
132
|
-
fab.generate.words(); // "happy dog" or "quick run" or "mountain climb"
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
#### `generate.person(id?)`: Complex Person Generator
|
|
136
|
-
Creates realistic person data with probabilistic variations:
|
|
137
|
-
|
|
138
|
-
```typescript
|
|
139
|
-
const person = fab.generate.person();
|
|
140
|
-
// {
|
|
141
|
-
// id: "uuid",
|
|
142
|
-
// firstName: "Jordan",
|
|
143
|
-
// middleName: "Alex" | undefined,
|
|
144
|
-
// lastName: "Smith" | "Smith-Jones",
|
|
145
|
-
// fullName: "Jordan Smith" | "Jordan Alex Smith"
|
|
146
|
-
// }
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
**Probabilistic Rules** (using `CHANCE` constants):
|
|
150
|
-
- **firstName**: 2.1% chance (RARE) to be a lastName instead
|
|
151
|
-
- **middleName**:
|
|
152
|
-
- 14.6% chance (UNCOMMON) to be missing
|
|
153
|
-
- 2.1% chance (RARE) to be a lastName
|
|
154
|
-
- 0.307% chance (EPIC) to have two middle names
|
|
155
|
-
- **lastName**: 2.1% chance (RARE) to be hyphenated
|
|
156
|
-
- **fullName**: 14.6% chance (UNCOMMON) to include middle name
|
|
157
|
-
|
|
158
|
-
**Independent Seeding**: Pass an optional UUID to get deterministic person data that's independent of the parent fabricator's seed:
|
|
159
|
-
|
|
160
|
-
```typescript
|
|
161
|
-
const person1 = fab1.generate.person("same-uuid");
|
|
162
|
-
const person2 = fab2.generate.person("same-uuid");
|
|
163
|
-
// person1 === person2, regardless of fab1/fab2 seeds
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
### 6. Probability Constants
|
|
167
|
-
|
|
168
|
-
```typescript
|
|
169
|
-
export const CHANCE = {
|
|
170
|
-
UNCOMMON: 0.146, // ~14.6%
|
|
171
|
-
RARE: 0.021, // ~2.1%
|
|
172
|
-
EPIC: 0.00307, // ~0.3%
|
|
173
|
-
LEGENDARY: 0.000441 // ~0.04%
|
|
174
|
-
};
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
Use these for consistent probability calculations across your test data.
|
|
178
|
-
|
|
179
|
-
### 7. Nested Fabricators
|
|
180
|
-
|
|
181
|
-
Create hierarchical data structures with `Fabricator.new()` and nested configurations:
|
|
182
|
-
|
|
183
|
-
```typescript
|
|
184
|
-
import { Fabricator } from "@jaypie/fabricator";
|
|
185
|
-
|
|
186
|
-
// Define a nested structure
|
|
187
|
-
const world = Fabricator.new({
|
|
188
|
-
seed: "my-world",
|
|
189
|
-
name: ({ fabricator }) => fabricator.location.city(),
|
|
190
|
-
fabricators: {
|
|
191
|
-
cities: {
|
|
192
|
-
name: ({ fabricator }) => fabricator.location.city(),
|
|
193
|
-
fabricators: {
|
|
194
|
-
streets: {
|
|
195
|
-
name: ({ fabricator }) => fabricator.location.street()
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
},
|
|
199
|
-
exports: {
|
|
200
|
-
name: ({ fabricator }) => fabricator.commerce.product()
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
// Access nested fabricators
|
|
206
|
-
const cities = world.cities(5); // Array of 5 city fabricators
|
|
207
|
-
const cityGen = world.cities(); // Generator for infinite cities
|
|
208
|
-
|
|
209
|
-
// Access nested child fabricators
|
|
210
|
-
const city = cities[0];
|
|
211
|
-
const streets = city.streets(10); // 10 streets for this city
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
**Deterministic Child Seeding**: Child fabricators are seeded deterministically based on their parent's ID and their index, ensuring reproducible hierarchies.
|
|
215
|
-
|
|
216
|
-
**Generator vs Array**:
|
|
217
|
-
- `world.cities()` - Returns a generator for unlimited fabricators
|
|
218
|
-
- `world.cities(n)` - Returns an array of exactly n fabricators
|
|
219
|
-
|
|
220
|
-
## Architecture
|
|
221
|
-
|
|
222
|
-
### Internal Utilities
|
|
223
|
-
|
|
224
|
-
- **`numericSeed()`**: Converts strings to numeric seeds using cyrb53 hash
|
|
225
|
-
- **`uuidToBytes()`**: Converts UUIDs to byte arrays for seeding
|
|
226
|
-
- **`uuidToNumber()`**: Converts UUIDs to single numbers (fallback)
|
|
227
|
-
- **`numericSeedArray()`**: Smart converter that detects UUIDs and uses appropriate conversion
|
|
228
|
-
- **`uuidFrom()`**: Generates deterministic UUID v5 from string or number using `JAYPIE_FABRICATOR_UUID` as namespace
|
|
229
|
-
- **`isUuid()`**: Validates if a string is a properly formatted UUID
|
|
230
|
-
|
|
231
|
-
## Best Practices & Patterns
|
|
232
|
-
|
|
233
|
-
### Seeding Strategy
|
|
234
|
-
|
|
235
|
-
Always tie seeds to fabricator instance ID for determinism with variety:
|
|
236
|
-
|
|
237
|
-
```typescript
|
|
238
|
-
// In parent fabricator method
|
|
239
|
-
for (let i = 0; i < count; i++) {
|
|
240
|
-
const seed = `${this.id}-tenant-${i}`;
|
|
241
|
-
tenants.push(new TenantFabricator({ seed, name }));
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// In child fabricator method
|
|
245
|
-
for (let i = 0; i < count; i++) {
|
|
246
|
-
const seed = `${this.id}-merchant-${i}`;
|
|
247
|
-
merchants.push(new MerchantFabricator({ seed, name }));
|
|
248
|
-
}
|
|
249
|
-
```
|
|
250
|
-
|
|
251
|
-
**Result:**
|
|
252
|
-
- Same fabricator instance โ same data (deterministic)
|
|
253
|
-
- Different fabricator instance โ different data (variety)
|
|
254
|
-
- Each entity gets unique seed: `parentId-entityType-index`
|
|
255
|
-
|
|
256
|
-
### Name Handling Pattern
|
|
257
|
-
|
|
258
|
-
When passing name functions to child fabricators, pass the function itself (don't call it):
|
|
259
|
-
|
|
260
|
-
```typescript
|
|
261
|
-
// โ
Correct - parent will invoke function
|
|
262
|
-
const name = config?.name ? () => config.name : undefined;
|
|
263
|
-
new TenantFabricator({ name, seed });
|
|
264
|
-
|
|
265
|
-
// โ Wrong - calling function too early
|
|
266
|
-
const name = config?.name ? config.name : generateName({ fabricator: this });
|
|
267
|
-
new TenantFabricator({ name: () => name, seed });
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
The parent Fabricator class invokes name functions lazily when `get name()` is accessed and caches the result.
|
|
271
|
-
|
|
272
|
-
### Generator Functions
|
|
273
|
-
|
|
274
|
-
Export generator functions separately for reusability and testability:
|
|
275
|
-
|
|
276
|
-
```typescript
|
|
277
|
-
// Export the generator function
|
|
278
|
-
export const generateTenantName = ({ fabricator }: FabricatorNameParams) => {
|
|
279
|
-
const city = fabricator.faker.location.city();
|
|
280
|
-
const suffixes = ["Financial", "Payments", "Commerce"];
|
|
281
|
-
const suffixIndex = Math.floor(fabricator.random() * suffixes.length);
|
|
282
|
-
return `${city} ${suffixes[suffixIndex]}`;
|
|
283
|
-
};
|
|
284
|
-
|
|
285
|
-
// Use in fabricator via generator option
|
|
286
|
-
const fab = new Fabricator({
|
|
287
|
-
seed: "my-seed",
|
|
288
|
-
generator: {
|
|
289
|
-
name: generateTenantName
|
|
290
|
-
}
|
|
291
|
-
});
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
### Extending Fabricator
|
|
295
|
-
|
|
296
|
-
Create domain-specific fabricators by extending the base class:
|
|
297
|
-
|
|
298
|
-
```typescript
|
|
299
|
-
import { Fabricator as JaypieFabricator } from "@jaypie/fabricator";
|
|
300
|
-
|
|
301
|
-
export class TenantFabricator extends JaypieFabricator {
|
|
302
|
-
constructor({
|
|
303
|
-
name,
|
|
304
|
-
seed,
|
|
305
|
-
}: {
|
|
306
|
-
name?: string | ((params: FabricatorNameParams) => string);
|
|
307
|
-
seed?: string | number;
|
|
308
|
-
} = {}) {
|
|
309
|
-
super({
|
|
310
|
-
name,
|
|
311
|
-
seed,
|
|
312
|
-
generator: {
|
|
313
|
-
name: generateTenantName,
|
|
314
|
-
},
|
|
315
|
-
});
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
// Add domain-specific methods
|
|
319
|
-
merchants(count: number): MerchantFabricator[] {
|
|
320
|
-
const merchants: MerchantFabricator[] = [];
|
|
321
|
-
for (let i = 0; i < count; i++) {
|
|
322
|
-
merchants.push(new MerchantFabricator({
|
|
323
|
-
seed: `${this.id}-merchant-${i}`
|
|
324
|
-
}));
|
|
325
|
-
}
|
|
326
|
-
return merchants;
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
```
|
|
330
|
-
|
|
331
|
-
### Common Anti-Patterns
|
|
332
|
-
|
|
333
|
-
**โ Don't manage name storage yourself**
|
|
334
|
-
```typescript
|
|
335
|
-
// Wrong - parent already handles this
|
|
336
|
-
private readonly _name: string;
|
|
337
|
-
get name(): string {
|
|
338
|
-
return this._name;
|
|
339
|
-
}
|
|
340
|
-
```
|
|
341
|
-
**โ
Right:** Use parent's `get name()` accessor via `generator` option.
|
|
342
|
-
|
|
343
|
-
**โ Don't pass index parameters**
|
|
344
|
-
```typescript
|
|
345
|
-
// Wrong - couples fabricator to sequence position
|
|
346
|
-
class TenantFabricator {
|
|
347
|
-
constructor({ index, seed }) {
|
|
348
|
-
this._name = this.generateName(index);
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
```
|
|
352
|
-
**โ
Right:** Use constructor config or generator functions.
|
|
353
|
-
|
|
354
|
-
## Key Design Patterns
|
|
355
|
-
|
|
356
|
-
1. **Subfaker Pattern**: Complex generators create independent seeded faker instances to ensure deterministic output regardless of parent state
|
|
357
|
-
2. **Probabilistic Variations**: Use float rolls with CHANCE constants for realistic data variety
|
|
358
|
-
3. **Proxy Access**: All faker modules exposed through getters for ergonomic API
|
|
359
|
-
4. **Seed Flexibility**: Accepts strings, numbers, or UUIDs as seeds
|
|
360
|
-
5. **Hierarchical Generation**: Nested fabricators enable deterministic tree structures for complex data models
|
|
361
|
-
6. **Identity & Naming**: Every fabricator has a unique ID and name for tracking and debugging
|
|
362
|
-
|
|
363
|
-
## EventFabricator: Temporal Event Generation
|
|
364
|
-
|
|
365
|
-
### Overview
|
|
366
|
-
|
|
367
|
-
`EventFabricator` is an abstract base class for generating temporally-distributed events across a year. It builds on `Fabricator` to add:
|
|
368
|
-
- Annual event counts with configurable distribution
|
|
369
|
-
- Temporal templates for hour, day, week, month, and date weighting
|
|
370
|
-
- Timezone-aware hour shifting
|
|
371
|
-
- Derived event system for cascading events
|
|
372
|
-
|
|
373
|
-
### Basic Usage
|
|
374
|
-
|
|
375
|
-
```typescript
|
|
376
|
-
import {
|
|
377
|
-
EventFabricator,
|
|
378
|
-
HOURS_BUSINESS,
|
|
379
|
-
DAYS_WEEKDAYS_ONLY,
|
|
380
|
-
type CreateEventParams,
|
|
381
|
-
} from "@jaypie/fabricator";
|
|
382
|
-
|
|
383
|
-
interface SimpleEvent {
|
|
384
|
-
id: string;
|
|
385
|
-
timestamp: Date;
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
class SimpleEventFabricator extends EventFabricator<SimpleEvent> {
|
|
389
|
-
protected createEvent({ seed, timestamp }: CreateEventParams): SimpleEvent {
|
|
390
|
-
return { id: seed, timestamp };
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
const fab = new SimpleEventFabricator({
|
|
395
|
-
seed: "my-events",
|
|
396
|
-
annualCount: 1000,
|
|
397
|
-
template: [HOURS_BUSINESS, DAYS_WEEKDAYS_ONLY],
|
|
398
|
-
});
|
|
399
|
-
|
|
400
|
-
const events = fab.events({ year: 2025 });
|
|
401
|
-
// Returns 1000 events during business hours on weekdays
|
|
402
|
-
```
|
|
403
|
-
|
|
404
|
-
### Temporal Templates
|
|
405
|
-
|
|
406
|
-
Templates control event distribution. They can be combined:
|
|
407
|
-
|
|
408
|
-
**Hour Templates:**
|
|
409
|
-
- `HOURS_BUSINESS` - 8am-5pm
|
|
410
|
-
- `HOURS_RETAIL` - 10am-8pm
|
|
411
|
-
- `HOURS_EVENING` - 6pm-10pm
|
|
412
|
-
- `HOURS_24_7` - All hours equal
|
|
413
|
-
|
|
414
|
-
**Day Templates:**
|
|
415
|
-
- `DAYS_WEEKDAYS_ONLY` - Mon-Fri only
|
|
416
|
-
- `DAYS_NO_SUNDAY` - All but Sunday
|
|
417
|
-
- `DAYS_NO_MONDAY` - All but Monday
|
|
418
|
-
|
|
419
|
-
**Curve Templates (gradual peaks):**
|
|
420
|
-
- `CURVE_EVENING_PEAK` - Peaks at 7pm
|
|
421
|
-
- `CURVE_ECOMMERCE` - Peaks at 8pm
|
|
422
|
-
- `CURVE_MIDDAY_PEAK` - Peaks at 11am
|
|
423
|
-
|
|
424
|
-
**Spike Templates (sharp peaks):**
|
|
425
|
-
- `SPIKE_MORNING` - 7-8am peak
|
|
426
|
-
- `SPIKE_LUNCH` - 12-1pm peak
|
|
427
|
-
- `SPIKE_EVENING` - 6-7pm peak
|
|
428
|
-
|
|
429
|
-
**Boost/Lull Templates (multipliers):**
|
|
430
|
-
- `BOOST_SUMMER`, `LULL_SUMMER`
|
|
431
|
-
- `BOOST_WINTER`, `LULL_WINTER`
|
|
432
|
-
- `BOOST_WEEKENDS`, `LULL_WEEKENDS`
|
|
433
|
-
- `BOOST_HOLIDAY_SEASON` - Nov-Dec 1.5x
|
|
434
|
-
|
|
435
|
-
```typescript
|
|
436
|
-
const fab = new MyFabricator({
|
|
437
|
-
template: [
|
|
438
|
-
HOURS_BUSINESS,
|
|
439
|
-
DAYS_WEEKDAYS_ONLY,
|
|
440
|
-
BOOST_HOLIDAY_SEASON,
|
|
441
|
-
CURVE_MIDDAY_PEAK,
|
|
442
|
-
],
|
|
443
|
-
});
|
|
444
|
-
```
|
|
445
|
-
|
|
446
|
-
## Derived Events: Cascading Event Generation
|
|
447
|
-
|
|
448
|
-
### Overview
|
|
449
|
-
|
|
450
|
-
The derived event system allows events to spawn follow-up events based on probabilistic rules. This is ideal for modeling:
|
|
451
|
-
- Financial transactions with voids, refunds, chargebacks
|
|
452
|
-
- Subscription renewals with payment retries
|
|
453
|
-
- Any event chains with cause-and-effect relationships
|
|
454
|
-
|
|
455
|
-
### Configuration
|
|
456
|
-
|
|
457
|
-
```typescript
|
|
458
|
-
import {
|
|
459
|
-
EventFabricator,
|
|
460
|
-
CHANCE,
|
|
461
|
-
type DerivedConfig,
|
|
462
|
-
type TimestampedEvent,
|
|
463
|
-
} from "@jaypie/fabricator";
|
|
464
|
-
|
|
465
|
-
interface Transaction extends TimestampedEvent {
|
|
466
|
-
id: string;
|
|
467
|
-
amount: number;
|
|
468
|
-
type: "purchase" | "void" | "refund" | "chargeback";
|
|
469
|
-
parentId?: string;
|
|
470
|
-
timestamp: Date;
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
const derivedConfig: DerivedConfig<Transaction> = {
|
|
474
|
-
rules: [
|
|
475
|
-
{
|
|
476
|
-
name: "void",
|
|
477
|
-
probability: CHANCE.RARE, // 2.1%
|
|
478
|
-
condition: (parent) => parent.type === "purchase",
|
|
479
|
-
timing: { mode: "same-day" },
|
|
480
|
-
createDerived: ({ parent, seed, timestamp }) => ({
|
|
481
|
-
id: seed,
|
|
482
|
-
type: "void",
|
|
483
|
-
amount: -parent.amount,
|
|
484
|
-
timestamp,
|
|
485
|
-
parentId: parent.id,
|
|
486
|
-
}),
|
|
487
|
-
},
|
|
488
|
-
{
|
|
489
|
-
name: "refund",
|
|
490
|
-
probability: 0.10, // 10%
|
|
491
|
-
timing: {
|
|
492
|
-
mode: "range",
|
|
493
|
-
delayMin: 1,
|
|
494
|
-
delayMax: 14,
|
|
495
|
-
unit: "days",
|
|
496
|
-
},
|
|
497
|
-
createDerived: ({ parent, seed, timestamp }) => ({
|
|
498
|
-
id: seed,
|
|
499
|
-
type: "refund",
|
|
500
|
-
amount: -parent.amount,
|
|
501
|
-
timestamp,
|
|
502
|
-
parentId: parent.id,
|
|
503
|
-
}),
|
|
504
|
-
},
|
|
505
|
-
],
|
|
506
|
-
boundaryBehavior: "include", // include | exclude | clamp
|
|
507
|
-
maxDepth: 4, // Prevent infinite chains
|
|
508
|
-
};
|
|
509
|
-
```
|
|
510
|
-
|
|
511
|
-
### Timing Modes
|
|
512
|
-
|
|
513
|
-
```typescript
|
|
514
|
-
interface DerivedTiming {
|
|
515
|
-
mode: "same-day" | "fixed" | "range" | "recurring";
|
|
516
|
-
delayMin?: number;
|
|
517
|
-
delayMax?: number;
|
|
518
|
-
unit?: "days" | "weeks" | "months" | "hours" | "minutes" | "seconds";
|
|
519
|
-
interval?: number; // For recurring
|
|
520
|
-
maxRecurrences?: number; // For recurring
|
|
521
|
-
until?: Date | "end-of-year";
|
|
522
|
-
}
|
|
523
|
-
```
|
|
524
|
-
|
|
525
|
-
**Examples:**
|
|
526
|
-
- Same day: `{ mode: "same-day" }`
|
|
527
|
-
- Fixed delay: `{ mode: "fixed", delayMin: 7, unit: "days" }`
|
|
528
|
-
- Range: `{ mode: "range", delayMin: 1, delayMax: 14, unit: "days" }`
|
|
529
|
-
- Monthly recurring: `{ mode: "recurring", interval: 1, unit: "months", until: "end-of-year" }`
|
|
530
|
-
|
|
531
|
-
### Nested Derived Events (Chains)
|
|
532
|
-
|
|
533
|
-
Derived events can spawn their own derived events:
|
|
534
|
-
|
|
535
|
-
```typescript
|
|
536
|
-
{
|
|
537
|
-
name: "chargeback",
|
|
538
|
-
probability: CHANCE.RARE,
|
|
539
|
-
timing: { mode: "range", delayMin: 30, delayMax: 90, unit: "days" },
|
|
540
|
-
createDerived: ({ parent, seed, timestamp }) => ({
|
|
541
|
-
id: seed,
|
|
542
|
-
type: "chargeback",
|
|
543
|
-
amount: -parent.amount,
|
|
544
|
-
timestamp,
|
|
545
|
-
parentId: parent.id,
|
|
546
|
-
}),
|
|
547
|
-
derived: [ // Nested rules for chargebacks
|
|
548
|
-
{
|
|
549
|
-
name: "representment",
|
|
550
|
-
probability: 0.70, // 70% of chargebacks
|
|
551
|
-
timing: { mode: "range", delayMin: 7, delayMax: 21, unit: "days" },
|
|
552
|
-
createDerived: ({ parent, seed, timestamp }) => ({
|
|
553
|
-
id: seed,
|
|
554
|
-
type: "representment",
|
|
555
|
-
amount: Math.abs(parent.amount), // Re-charge
|
|
556
|
-
timestamp,
|
|
557
|
-
parentId: parent.id,
|
|
558
|
-
}),
|
|
559
|
-
},
|
|
560
|
-
],
|
|
561
|
-
}
|
|
562
|
-
```
|
|
563
|
-
|
|
564
|
-
### Using with EventFabricator
|
|
565
|
-
|
|
566
|
-
```typescript
|
|
567
|
-
class TransactionFabricator extends EventFabricator<Transaction> {
|
|
568
|
-
constructor(options = {}) {
|
|
569
|
-
super({
|
|
570
|
-
...options,
|
|
571
|
-
derived: derivedConfig,
|
|
572
|
-
template: [HOURS_BUSINESS, DAYS_WEEKDAYS_ONLY],
|
|
573
|
-
});
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
protected createEvent({ seed, timestamp }: CreateEventParams): Transaction {
|
|
577
|
-
const fab = new Fabricator({ seed });
|
|
578
|
-
return {
|
|
579
|
-
id: seed,
|
|
580
|
-
type: "purchase",
|
|
581
|
-
amount: fab.random({ min: 10, max: 500, currency: true }),
|
|
582
|
-
timestamp,
|
|
583
|
-
};
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
const fab = new TransactionFabricator({
|
|
588
|
-
seed: "my-business",
|
|
589
|
-
annualCount: 1000,
|
|
590
|
-
});
|
|
591
|
-
|
|
592
|
-
// Get all events including derived events
|
|
593
|
-
const events = fab.events({ year: 2025 });
|
|
594
|
-
|
|
595
|
-
// Get events with metadata (parent refs, depth, rule names)
|
|
596
|
-
const eventsWithMeta = fab.eventsWithMeta({ year: 2025 });
|
|
597
|
-
```
|
|
598
|
-
|
|
599
|
-
### Event Metadata
|
|
600
|
-
|
|
601
|
-
`eventsWithMeta()` returns events with relationship data:
|
|
602
|
-
|
|
603
|
-
```typescript
|
|
604
|
-
interface EventWithDerivedMeta<T> {
|
|
605
|
-
event: T;
|
|
606
|
-
depth: number; // 0 = primary, 1 = first derived, etc.
|
|
607
|
-
parentSeed?: string; // Seed of parent event
|
|
608
|
-
ruleName?: string; // Rule that created this derived event
|
|
609
|
-
}
|
|
610
|
-
```
|
|
611
|
-
|
|
612
|
-
### Key Properties
|
|
613
|
-
|
|
614
|
-
1. **Deterministic**: Same seed produces same derived event cascade
|
|
615
|
-
2. **Chronological**: All events (primary + derived) sorted by timestamp
|
|
616
|
-
3. **Depth-Limited**: `maxDepth` prevents infinite chains
|
|
617
|
-
4. **Boundary Control**: Events outside target year can be included, excluded, or clamped
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Specification for an ideal project structure, referenced by monorepo init
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
# Ideal Project Structure ๐
|
|
6
|
-
|
|
7
|
-
Specification for an ideal project structure
|
|
8
|
-
|
|
9
|
-
## โ๏ธ Overview
|
|
10
|
-
|
|
11
|
-
* Jaypie project opinions and tooling
|
|
12
|
-
* ESLint 9+ with @jaypie/eslint
|
|
13
|
-
* NPM with Workspaces ("monorepo")
|
|
14
|
-
* TypeScript
|
|
15
|
-
* Vite
|
|
16
|
-
* Vitest .spec sibling to implementation
|
|
17
|
-
|
|
18
|
-
## ๐งน ESLint
|
|
19
|
-
|
|
20
|
-
Only use ESLint 9+ with the flat file config.
|
|
21
|
-
|
|
22
|
-
```javascript
|
|
23
|
-
// eslint.config.mjs
|
|
24
|
-
export { default as default } from "@jaypie/eslint";
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
See context/prompts/Jaypie_Eslint_NPM_Package.md for details and troubleshooting.
|
|
28
|
-
|
|
29
|
-
## ๐ฆ NPM
|
|
30
|
-
|
|
31
|
-
### Configuration
|
|
32
|
-
|
|
33
|
-
Follow the naming convention of other subpackages in a monorepo.
|
|
34
|
-
Use `"version": "0.0.1"`, `"type": "module"`, and `"private": true` for all newly created package.json
|
|
35
|
-
Do not include authors, keywords, external links.
|
|
36
|
-
|
|
37
|
-
### Dev Dependencies
|
|
38
|
-
|
|
39
|
-
#### Top-Level
|
|
40
|
-
|
|
41
|
-
* @jaypie/eslint
|
|
42
|
-
* @jaypie/testkit
|
|
43
|
-
* eslint
|
|
44
|
-
* rimraf
|
|
45
|
-
* sort-package-json
|
|
46
|
-
* tsx
|
|
47
|
-
* vitest
|
|
48
|
-
|
|
49
|
-
#### Package-Level
|
|
50
|
-
|
|
51
|
-
Anything particular to that package outside aforementioned.
|
|
52
|
-
|
|
53
|
-
### Scripts
|
|
54
|
-
|
|
55
|
-
| Script | Top-level | Sub-packages | Root-level |
|
|
56
|
-
| ------ | --------- | ------------ | ---------- |
|
|
57
|
-
| `build` | `npm run build --workspaces` | `vite build` | N/A |
|
|
58
|
-
| `clean` | `npm run clean --workspaces && npm run clean:root` | `rimfaf dist` | `clean:root` |
|
|
59
|
-
| `format` | `eslint --fix` | `eslint --fix` | N/A |
|
|
60
|
-
| `format:package` | `sort-package-json ./package.json ./packages/*/package.json` | `sort-package-json` | N/A |
|
|
61
|
-
| `lint` | `eslint` | `eslint .` | N/A |
|
|
62
|
-
| `test` | `vitest run` | `vitest run` | N/A |
|
|
63
|
-
| `typecheck` | `npm run typecheck --workspaces` | `tsc --noEmit` | N/A |
|
|
64
|
-
|
|
65
|
-
## ๐งช Testing
|
|
66
|
-
|
|
67
|
-
### Configure Vitest Workspaces
|
|
68
|
-
|
|
69
|
-
`vitest.workspace.js`
|
|
70
|
-
```javascript
|
|
71
|
-
import { defineWorkspace } from "vitest/config";
|
|
72
|
-
|
|
73
|
-
export default defineWorkspace([
|
|
74
|
-
"packages/<package>",
|
|
75
|
-
]);
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
See context/prompts/Jaypie_Mocks_and_Testkit.md for details and troubleshooting.
|