bunsane 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.
Files changed (214) hide show
  1. package/CHANGELOG.md +445 -370
  2. package/core/BatchLoader.ts +56 -32
  3. package/core/Entity.ts +85 -1020
  4. package/core/EntityHookManager.ts +52 -754
  5. package/core/Logger.ts +10 -0
  6. package/core/RequestContext.ts +94 -85
  7. package/core/RequestLoaders.ts +98 -5
  8. package/core/SchedulerManager.ts +28 -600
  9. package/core/app/cors.ts +2 -11
  10. package/core/app/preparedStatementWarmup.ts +9 -49
  11. package/core/app/requestRouter.ts +9 -8
  12. package/core/app/restRegistry.ts +8 -0
  13. package/core/archetype/fieldResolvers.ts +85 -40
  14. package/core/archetype/relationLoader.ts +135 -92
  15. package/core/cache/CacheManager.ts +91 -302
  16. package/core/cache/CompressionUtils.ts +34 -3
  17. package/core/cache/MemoryCache.ts +40 -37
  18. package/core/cache/RedisCache.ts +4 -4
  19. package/core/cache/health.ts +30 -0
  20. package/core/cache/invalidation.ts +96 -0
  21. package/core/cache/strategies/writeInvalidate.ts +111 -0
  22. package/core/cache/strategies/writeThrough.ts +233 -0
  23. package/core/components/BaseComponent.ts +16 -8
  24. package/core/components/ComponentRegistry.ts +28 -0
  25. package/core/decorators/IndexedField.ts +1 -1
  26. package/core/entity/cacheStrategies.ts +97 -0
  27. package/core/entity/componentAccess.ts +364 -0
  28. package/core/entity/finders.ts +202 -0
  29. package/core/entity/pendingOps.ts +72 -0
  30. package/core/entity/saveEntity.ts +377 -0
  31. package/core/hooks/dispatcher.ts +439 -0
  32. package/core/hooks/guards.ts +155 -0
  33. package/core/hooks/registry.ts +247 -0
  34. package/core/metadata/definitions/Component.ts +1 -1
  35. package/core/metadata/index.ts +15 -4
  36. package/core/middleware/RateLimit.ts +102 -105
  37. package/core/middleware/RequestId.ts +2 -9
  38. package/core/middleware/SecurityHeaders.ts +2 -11
  39. package/core/middleware/headers.ts +28 -0
  40. package/core/remote/OutboxWorker.ts +213 -183
  41. package/core/remote/RemoteManager.ts +401 -400
  42. package/core/remote/types.ts +153 -151
  43. package/core/requestScope.ts +34 -0
  44. package/core/scheduler/cronEvaluator.ts +174 -0
  45. package/core/scheduler/lifecycleHooks.ts +21 -0
  46. package/core/scheduler/lockCoordinator.ts +27 -0
  47. package/core/scheduler/metrics.ts +14 -0
  48. package/core/scheduler/taskRunner.ts +420 -0
  49. package/database/DatabaseHelper.ts +128 -101
  50. package/database/IndexingStrategy.ts +72 -2
  51. package/database/PreparedStatementCache.ts +8 -2
  52. package/database/cancellable.ts +35 -22
  53. package/database/index.ts +15 -3
  54. package/database/instrumentedDb.ts +141 -141
  55. package/endpoints/archetypes.ts +2 -8
  56. package/endpoints/tables.ts +6 -1
  57. package/gql/index.ts +1 -1
  58. package/gql/visitors/ResolverGeneratorVisitor.ts +25 -4
  59. package/package.json +22 -1
  60. package/query/CTENode.ts +5 -3
  61. package/query/ComponentInclusionNode.ts +240 -13
  62. package/query/OrNode.ts +6 -5
  63. package/query/Query.ts +157 -46
  64. package/query/QueryContext.ts +6 -0
  65. package/query/QueryDAG.ts +7 -2
  66. package/query/membershipSource.ts +66 -0
  67. package/storage/LocalStorageProvider.ts +8 -3
  68. package/studio/dist/assets/index-BMZ67Npg.js +254 -0
  69. package/studio/dist/assets/index-BpbuYz9g.css +1 -0
  70. package/studio/{index.html → dist/index.html} +3 -2
  71. package/swagger/generator.ts +11 -1
  72. package/upload/UploadManager.ts +8 -6
  73. package/utils/uuid.ts +40 -10
  74. package/.claude/scheduled_tasks.lock +0 -1
  75. package/.claude/settings.local.json +0 -47
  76. package/.prettierrc +0 -4
  77. package/.serena/memories/architectural-decision-no-dependency-injection.md +0 -76
  78. package/.serena/memories/architecture.md +0 -154
  79. package/.serena/memories/cache-interface-refactoring-2026-01-24.md +0 -165
  80. package/.serena/memories/code_style_and_conventions.md +0 -76
  81. package/.serena/memories/project_overview.md +0 -43
  82. package/.serena/memories/schema-dsl-plan.md +0 -107
  83. package/.serena/memories/suggested_commands.md +0 -80
  84. package/.serena/memories/typescript-compilation-status.md +0 -54
  85. package/.serena/project.yml +0 -114
  86. package/BunSane.jpg +0 -0
  87. package/CLAUDE.md +0 -198
  88. package/TODO.md +0 -2
  89. package/bun.lock +0 -302
  90. package/bunfig.toml +0 -10
  91. package/docs/RFC_APP_REFACTOR.md +0 -248
  92. package/docs/RFC_REFACTOR_TARGETS.md +0 -251
  93. package/docs/SCALABILITY_PLAN.md +0 -175
  94. package/studio/bun.lock +0 -482
  95. package/studio/package.json +0 -39
  96. package/studio/postcss.config.js +0 -6
  97. package/studio/src/components/DataTable.tsx +0 -211
  98. package/studio/src/components/Layout.tsx +0 -13
  99. package/studio/src/components/PageContainer.tsx +0 -9
  100. package/studio/src/components/PageHeader.tsx +0 -13
  101. package/studio/src/components/SearchBar.tsx +0 -57
  102. package/studio/src/components/Sidebar.tsx +0 -294
  103. package/studio/src/components/ui/button.tsx +0 -56
  104. package/studio/src/components/ui/checkbox.tsx +0 -26
  105. package/studio/src/components/ui/input.tsx +0 -25
  106. package/studio/src/hooks/useDataTable.ts +0 -131
  107. package/studio/src/index.css +0 -36
  108. package/studio/src/lib/api.ts +0 -186
  109. package/studio/src/lib/utils.ts +0 -13
  110. package/studio/src/main.tsx +0 -17
  111. package/studio/src/pages/ArcheType.tsx +0 -239
  112. package/studio/src/pages/Components.tsx +0 -124
  113. package/studio/src/pages/EntityInspector.tsx +0 -302
  114. package/studio/src/pages/QueryRunner.tsx +0 -246
  115. package/studio/src/pages/Table.tsx +0 -94
  116. package/studio/src/pages/Welcome.tsx +0 -241
  117. package/studio/src/routes.tsx +0 -45
  118. package/studio/src/store/archeTypeSettings.ts +0 -30
  119. package/studio/src/store/studio.ts +0 -65
  120. package/studio/src/utils/columnHelpers.tsx +0 -114
  121. package/studio/studio-instructions.md +0 -81
  122. package/studio/tailwind.config.js +0 -77
  123. package/studio/utils.ts +0 -54
  124. package/studio/vite.config.js +0 -19
  125. package/tests/benchmark/BENCHMARK_DATABASES_PLAN.md +0 -338
  126. package/tests/benchmark/bunfig.toml +0 -9
  127. package/tests/benchmark/fixtures/EcommerceComponents.ts +0 -283
  128. package/tests/benchmark/fixtures/EcommerceDataGenerators.ts +0 -301
  129. package/tests/benchmark/fixtures/RelationTracker.ts +0 -159
  130. package/tests/benchmark/fixtures/index.ts +0 -6
  131. package/tests/benchmark/index.ts +0 -22
  132. package/tests/benchmark/noop-preload.ts +0 -3
  133. package/tests/benchmark/query-lateral-benchmark.test.ts +0 -372
  134. package/tests/benchmark/runners/BenchmarkLoader.ts +0 -132
  135. package/tests/benchmark/runners/index.ts +0 -4
  136. package/tests/benchmark/scenarios/query-benchmarks.test.ts +0 -465
  137. package/tests/benchmark/scripts/generate-db.ts +0 -344
  138. package/tests/benchmark/scripts/run-benchmarks.ts +0 -97
  139. package/tests/e2e/http.test.ts +0 -130
  140. package/tests/fixtures/archetypes/TestUserArchetype.ts +0 -21
  141. package/tests/fixtures/components/TestOrder.ts +0 -23
  142. package/tests/fixtures/components/TestProduct.ts +0 -23
  143. package/tests/fixtures/components/TestUser.ts +0 -20
  144. package/tests/fixtures/components/index.ts +0 -6
  145. package/tests/graphql/SchemaGeneration.test.ts +0 -90
  146. package/tests/graphql/builders/ResolverBuilder.test.ts +0 -223
  147. package/tests/graphql/builders/TypeDefBuilder.test.ts +0 -153
  148. package/tests/helpers/MockRedisClient.ts +0 -113
  149. package/tests/helpers/MockRedisStreamServer.ts +0 -448
  150. package/tests/integration/archetype/ArcheType.persistence.test.ts +0 -241
  151. package/tests/integration/cache/CacheInvalidation.test.ts +0 -259
  152. package/tests/integration/entity/Entity.persistence.test.ts +0 -333
  153. package/tests/integration/entity/Entity.saveTimeout.test.ts +0 -110
  154. package/tests/integration/loaders/RequestLoaders.abort.test.ts +0 -82
  155. package/tests/integration/query/Query.abort.test.ts +0 -66
  156. package/tests/integration/query/Query.complexAnalysis.test.ts +0 -557
  157. package/tests/integration/query/Query.edgeCases.test.ts +0 -595
  158. package/tests/integration/query/Query.exec.test.ts +0 -576
  159. package/tests/integration/query/Query.explainAnalyze.test.ts +0 -233
  160. package/tests/integration/query/Query.jsonbArray.test.ts +0 -214
  161. package/tests/integration/remote/dlq.test.ts +0 -175
  162. package/tests/integration/remote/event-dispatch.test.ts +0 -114
  163. package/tests/integration/remote/outbox.test.ts +0 -130
  164. package/tests/integration/remote/rpc.test.ts +0 -177
  165. package/tests/pglite-setup.ts +0 -62
  166. package/tests/setup.ts +0 -164
  167. package/tests/stress/BenchmarkRunner.ts +0 -203
  168. package/tests/stress/DataSeeder.ts +0 -190
  169. package/tests/stress/StressTestReporter.ts +0 -229
  170. package/tests/stress/cursor-perf-test.ts +0 -171
  171. package/tests/stress/fixtures/RealisticComponents.ts +0 -235
  172. package/tests/stress/fixtures/StressTestComponents.ts +0 -58
  173. package/tests/stress/index.ts +0 -7
  174. package/tests/stress/scenarios/query-benchmarks.test.ts +0 -285
  175. package/tests/stress/scenarios/realistic-scenarios.test.ts +0 -1081
  176. package/tests/stress/scenarios/timeout-investigation.test.ts +0 -522
  177. package/tests/unit/BatchLoader.test.ts +0 -196
  178. package/tests/unit/archetype/ArcheType.test.ts +0 -107
  179. package/tests/unit/cache/CacheManager.test.ts +0 -498
  180. package/tests/unit/cache/MemoryCache.test.ts +0 -260
  181. package/tests/unit/cache/RedisCache.test.ts +0 -411
  182. package/tests/unit/database/cancellable.test.ts +0 -81
  183. package/tests/unit/database/instrumentedDb.test.ts +0 -160
  184. package/tests/unit/entity/Entity.components.test.ts +0 -317
  185. package/tests/unit/entity/Entity.drainSideEffects.test.ts +0 -51
  186. package/tests/unit/entity/Entity.reload.test.ts +0 -63
  187. package/tests/unit/entity/Entity.requireComponents.test.ts +0 -72
  188. package/tests/unit/entity/Entity.test.ts +0 -345
  189. package/tests/unit/gql/depthLimit.test.ts +0 -203
  190. package/tests/unit/gql/operationMiddleware.test.ts +0 -293
  191. package/tests/unit/health/Health.test.ts +0 -129
  192. package/tests/unit/middleware/AccessLog.test.ts +0 -37
  193. package/tests/unit/middleware/Middleware.test.ts +0 -98
  194. package/tests/unit/middleware/RequestId.test.ts +0 -54
  195. package/tests/unit/middleware/SecurityHeaders.test.ts +0 -66
  196. package/tests/unit/query/FilterBuilder.test.ts +0 -111
  197. package/tests/unit/query/JsonbArrayBuilder.test.ts +0 -178
  198. package/tests/unit/query/Query.emptyString.test.ts +0 -69
  199. package/tests/unit/query/Query.test.ts +0 -310
  200. package/tests/unit/remote/CircuitBreaker.test.ts +0 -159
  201. package/tests/unit/remote/RemoteError.test.ts +0 -55
  202. package/tests/unit/remote/decorators.test.ts +0 -195
  203. package/tests/unit/remote/metrics.test.ts +0 -115
  204. package/tests/unit/remote/mockRedisStreamServer.test.ts +0 -104
  205. package/tests/unit/scheduler/DistributedLock.test.ts +0 -274
  206. package/tests/unit/scheduler/SchedulerManager.timeBased.test.ts +0 -95
  207. package/tests/unit/schema/schema-integration.test.ts +0 -426
  208. package/tests/unit/schema/schema.test.ts +0 -580
  209. package/tests/unit/storage/S3StorageProvider.test.ts +0 -567
  210. package/tests/unit/upload/RestUpload.test.ts +0 -267
  211. package/tests/unit/validateEnv.test.ts +0 -82
  212. package/tests/utils/entity-tracker.ts +0 -57
  213. package/tests/utils/index.ts +0 -13
  214. package/tests/utils/test-context.ts +0 -149
@@ -1,283 +0,0 @@
1
- /**
2
- * E-commerce Components for Benchmark Testing
3
- *
4
- * Defines benchmark-specific components with indexed foreign keys
5
- * for testing query performance across relationships.
6
- */
7
- import { BaseComponent } from '../../../core/components/BaseComponent';
8
- import { Component, CompData } from '../../../core/components/Decorators';
9
- import { IndexedField } from '../../../core/decorators/IndexedField';
10
-
11
- /**
12
- * BenchUser - User profile component
13
- */
14
- @Component
15
- export class BenchUser extends BaseComponent {
16
- @CompData({ indexed: true })
17
- @IndexedField('btree')
18
- email!: string;
19
-
20
- @CompData({ indexed: true })
21
- @IndexedField('btree')
22
- username!: string;
23
-
24
- @CompData()
25
- firstName!: string;
26
-
27
- @CompData()
28
- lastName!: string;
29
-
30
- @CompData({ indexed: true })
31
- @IndexedField('btree')
32
- status!: 'active' | 'inactive' | 'suspended';
33
-
34
- @CompData({ indexed: true })
35
- @IndexedField('btree')
36
- tier!: 'free' | 'basic' | 'premium' | 'enterprise';
37
-
38
- @CompData()
39
- @IndexedField('btree')
40
- region!: string;
41
-
42
- @CompData()
43
- @IndexedField('numeric')
44
- orderCount!: number;
45
-
46
- @CompData()
47
- @IndexedField('numeric')
48
- totalSpent!: number;
49
-
50
- @CompData()
51
- @IndexedField('btree', true)
52
- createdAt!: Date;
53
-
54
- @CompData()
55
- @IndexedField('btree', true)
56
- lastLoginAt!: Date | null;
57
- }
58
-
59
- /**
60
- * BenchProduct - Product catalog component
61
- */
62
- @Component
63
- export class BenchProduct extends BaseComponent {
64
- @CompData({ indexed: true })
65
- @IndexedField('btree')
66
- sku!: string;
67
-
68
- @CompData({ indexed: true })
69
- @IndexedField('btree')
70
- name!: string;
71
-
72
- @CompData()
73
- description!: string;
74
-
75
- @CompData({ indexed: true })
76
- @IndexedField('btree')
77
- category!: string;
78
-
79
- @CompData()
80
- @IndexedField('btree')
81
- subcategory!: string;
82
-
83
- @CompData({ indexed: true })
84
- @IndexedField('btree')
85
- brand!: string;
86
-
87
- @CompData()
88
- @IndexedField('numeric')
89
- price!: number;
90
-
91
- @CompData()
92
- @IndexedField('numeric')
93
- cost!: number;
94
-
95
- @CompData()
96
- @IndexedField('numeric')
97
- stock!: number;
98
-
99
- @CompData({ indexed: true })
100
- @IndexedField('btree')
101
- status!: 'active' | 'inactive' | 'discontinued';
102
-
103
- @CompData()
104
- @IndexedField('numeric')
105
- rating!: number;
106
-
107
- @CompData()
108
- @IndexedField('numeric')
109
- reviewCount!: number;
110
-
111
- @CompData()
112
- @IndexedField('btree', true)
113
- createdAt!: Date;
114
- }
115
-
116
- /**
117
- * BenchOrder - Order transaction component
118
- */
119
- @Component
120
- export class BenchOrder extends BaseComponent {
121
- @CompData({ indexed: true })
122
- @IndexedField('btree')
123
- userId!: string; // Foreign key to BenchUser entity
124
-
125
- @CompData({ indexed: true })
126
- @IndexedField('btree')
127
- orderNumber!: string;
128
-
129
- @CompData({ indexed: true })
130
- @IndexedField('btree')
131
- status!: 'pending' | 'processing' | 'shipped' | 'delivered' | 'cancelled' | 'refunded';
132
-
133
- @CompData()
134
- @IndexedField('numeric')
135
- subtotal!: number;
136
-
137
- @CompData()
138
- @IndexedField('numeric')
139
- tax!: number;
140
-
141
- @CompData()
142
- @IndexedField('numeric')
143
- shipping!: number;
144
-
145
- @CompData()
146
- @IndexedField('numeric')
147
- total!: number;
148
-
149
- @CompData()
150
- @IndexedField('numeric')
151
- itemCount!: number;
152
-
153
- @CompData({ indexed: true })
154
- @IndexedField('btree')
155
- paymentMethod!: 'card' | 'paypal' | 'bank' | 'crypto';
156
-
157
- @CompData()
158
- @IndexedField('btree')
159
- shippingRegion!: string;
160
-
161
- @CompData()
162
- @IndexedField('btree', true)
163
- orderedAt!: Date;
164
-
165
- @CompData()
166
- @IndexedField('btree', true)
167
- shippedAt!: Date | null;
168
-
169
- @CompData()
170
- @IndexedField('btree', true)
171
- deliveredAt!: Date | null;
172
- }
173
-
174
- /**
175
- * BenchOrderItem - Line items in an order
176
- */
177
- @Component
178
- export class BenchOrderItem extends BaseComponent {
179
- @CompData({ indexed: true })
180
- @IndexedField('btree')
181
- orderId!: string; // Foreign key to BenchOrder entity
182
-
183
- @CompData({ indexed: true })
184
- @IndexedField('btree')
185
- productId!: string; // Foreign key to BenchProduct entity
186
-
187
- @CompData({ indexed: true })
188
- @IndexedField('btree')
189
- userId!: string; // Denormalized for faster user order queries
190
-
191
- @CompData()
192
- @IndexedField('numeric')
193
- quantity!: number;
194
-
195
- @CompData()
196
- @IndexedField('numeric')
197
- unitPrice!: number;
198
-
199
- @CompData()
200
- @IndexedField('numeric')
201
- discount!: number;
202
-
203
- @CompData()
204
- @IndexedField('numeric')
205
- total!: number;
206
-
207
- @CompData()
208
- @IndexedField('btree')
209
- status!: 'pending' | 'fulfilled' | 'returned' | 'refunded';
210
- }
211
-
212
- /**
213
- * BenchReview - Product review component
214
- */
215
- @Component
216
- export class BenchReview extends BaseComponent {
217
- @CompData({ indexed: true })
218
- @IndexedField('btree')
219
- productId!: string; // Foreign key to BenchProduct entity
220
-
221
- @CompData({ indexed: true })
222
- @IndexedField('btree')
223
- userId!: string; // Foreign key to BenchUser entity
224
-
225
- @CompData()
226
- @IndexedField('numeric')
227
- rating!: number; // 1-5
228
-
229
- @CompData()
230
- title!: string;
231
-
232
- @CompData()
233
- content!: string;
234
-
235
- @CompData({ indexed: true })
236
- @IndexedField('gin')
237
- verified!: boolean;
238
-
239
- @CompData()
240
- @IndexedField('numeric')
241
- helpfulCount!: number;
242
-
243
- @CompData()
244
- @IndexedField('btree', true)
245
- createdAt!: Date;
246
- }
247
-
248
- // Constants for data generation
249
- export const USER_TIERS = ['free', 'basic', 'premium', 'enterprise'] as const;
250
- export const USER_STATUSES = ['active', 'inactive', 'suspended'] as const;
251
- export const PRODUCT_STATUSES = ['active', 'inactive', 'discontinued'] as const;
252
- export const ORDER_STATUSES = ['pending', 'processing', 'shipped', 'delivered', 'cancelled', 'refunded'] as const;
253
- export const ORDER_ITEM_STATUSES = ['pending', 'fulfilled', 'returned', 'refunded'] as const;
254
- export const PAYMENT_METHODS = ['card', 'paypal', 'bank', 'crypto'] as const;
255
-
256
- export const REGIONS = [
257
- 'North America', 'Europe', 'Asia Pacific', 'Latin America',
258
- 'Middle East', 'Africa', 'Oceania'
259
- ];
260
-
261
- export const CATEGORIES = [
262
- 'Electronics', 'Clothing', 'Home & Garden', 'Sports', 'Books',
263
- 'Toys', 'Beauty', 'Automotive', 'Food', 'Health'
264
- ];
265
-
266
- export const SUBCATEGORIES: Record<string, string[]> = {
267
- 'Electronics': ['Smartphones', 'Laptops', 'Tablets', 'Accessories', 'Audio', 'Cameras'],
268
- 'Clothing': ['Men', 'Women', 'Kids', 'Shoes', 'Accessories', 'Athletic'],
269
- 'Home & Garden': ['Furniture', 'Decor', 'Kitchen', 'Garden', 'Bedding', 'Lighting'],
270
- 'Sports': ['Fitness', 'Outdoor', 'Team Sports', 'Water Sports', 'Winter', 'Cycling'],
271
- 'Books': ['Fiction', 'Non-Fiction', 'Technical', 'Children', 'Comics', 'Academic'],
272
- 'Toys': ['Action Figures', 'Board Games', 'Educational', 'Outdoor', 'Puzzles', 'Dolls'],
273
- 'Beauty': ['Skincare', 'Makeup', 'Haircare', 'Fragrance', 'Tools', 'Mens'],
274
- 'Automotive': ['Parts', 'Accessories', 'Tools', 'Care', 'Electronics', 'Safety'],
275
- 'Food': ['Snacks', 'Beverages', 'Organic', 'International', 'Specialty', 'Supplements'],
276
- 'Health': ['Vitamins', 'Supplements', 'Personal Care', 'Medical', 'Fitness', 'Wellness']
277
- };
278
-
279
- export const BRANDS = [
280
- 'TechCorp', 'StyleMax', 'HomeEssentials', 'SportPro', 'BookHouse',
281
- 'ToyWorld', 'BeautyGlow', 'AutoParts', 'FreshFoods', 'HealthPlus',
282
- 'GadgetZone', 'FashionHub', 'ComfortLiving', 'ActiveLife', 'PageTurner'
283
- ];
@@ -1,301 +0,0 @@
1
- /**
2
- * Data generators for benchmark e-commerce components.
3
- *
4
- * Uses deterministic seeding for reproducible data generation.
5
- * Implements realistic distributions (power-law, exponential).
6
- */
7
- import {
8
- USER_TIERS, USER_STATUSES, PRODUCT_STATUSES, ORDER_STATUSES,
9
- ORDER_ITEM_STATUSES, PAYMENT_METHODS, REGIONS, CATEGORIES,
10
- SUBCATEGORIES, BRANDS
11
- } from './EcommerceComponents';
12
- import type { RelationTracker } from './RelationTracker';
13
-
14
- /**
15
- * Seeded random number generator (Mulberry32)
16
- */
17
- export class SeededRandom {
18
- private state: number;
19
-
20
- constructor(seed: number) {
21
- this.state = seed;
22
- }
23
-
24
- next(): number {
25
- let t = this.state += 0x6D2B79F5;
26
- t = Math.imul(t ^ t >>> 15, t | 1);
27
- t ^= t + Math.imul(t ^ t >>> 7, t | 61);
28
- return ((t ^ t >>> 14) >>> 0) / 4294967296;
29
- }
30
-
31
- nextInt(min: number, max: number): number {
32
- return Math.floor(this.next() * (max - min + 1)) + min;
33
- }
34
-
35
- pick<T>(arr: readonly T[]): T {
36
- return arr[Math.floor(this.next() * arr.length)]!;
37
- }
38
-
39
- pickWeighted<T>(arr: readonly T[], weights: number[]): T {
40
- const total = weights.reduce((a, b) => a + b, 0);
41
- let r = this.next() * total;
42
- for (let i = 0; i < arr.length; i++) {
43
- r -= weights[i]!;
44
- if (r <= 0) return arr[i]!;
45
- }
46
- return arr[arr.length - 1]!;
47
- }
48
-
49
- /**
50
- * Power-law distribution (Pareto-like)
51
- * Used for category popularity, user activity, etc.
52
- */
53
- powerLaw(min: number, max: number, alpha: number = 1.5): number {
54
- const u = this.next();
55
- const minP = Math.pow(min, 1 - alpha);
56
- const maxP = Math.pow(max, 1 - alpha);
57
- return Math.pow((maxP - minP) * u + minP, 1 / (1 - alpha));
58
- }
59
-
60
- /**
61
- * Exponential distribution
62
- * Used for order frequency, review counts, etc.
63
- */
64
- exponential(lambda: number = 1): number {
65
- return -Math.log(1 - this.next()) / lambda;
66
- }
67
- }
68
-
69
- const FIRST_NAMES = [
70
- 'James', 'Mary', 'John', 'Patricia', 'Robert', 'Jennifer', 'Michael', 'Linda',
71
- 'William', 'Elizabeth', 'David', 'Barbara', 'Richard', 'Susan', 'Joseph', 'Jessica',
72
- 'Thomas', 'Sarah', 'Charles', 'Karen', 'Christopher', 'Lisa', 'Daniel', 'Nancy',
73
- 'Alex', 'Emma', 'Olivia', 'Liam', 'Noah', 'Ava', 'Sophia', 'Isabella'
74
- ];
75
-
76
- const LAST_NAMES = [
77
- 'Smith', 'Johnson', 'Williams', 'Brown', 'Jones', 'Garcia', 'Miller', 'Davis',
78
- 'Rodriguez', 'Martinez', 'Hernandez', 'Lopez', 'Gonzalez', 'Wilson', 'Anderson',
79
- 'Thomas', 'Taylor', 'Moore', 'Jackson', 'Martin', 'Lee', 'Perez', 'Thompson', 'White'
80
- ];
81
-
82
- const ADJECTIVES = [
83
- 'Premium', 'Professional', 'Ultra', 'Classic', 'Essential', 'Advanced',
84
- 'Pro', 'Elite', 'Basic', 'Standard', 'Deluxe', 'Supreme', 'Ultimate'
85
- ];
86
-
87
- const PRODUCT_NOUNS = [
88
- 'Widget', 'Device', 'Tool', 'Kit', 'Set', 'Pack', 'Bundle',
89
- 'System', 'Unit', 'Module', 'Gear', 'Equipment', 'Accessory'
90
- ];
91
-
92
- const REVIEW_TITLES = [
93
- 'Great product!', 'Exactly what I needed', 'Highly recommended',
94
- 'Good value', 'Works perfectly', 'Exceeded expectations',
95
- 'Not bad', 'Could be better', 'Disappointed', 'Amazing quality',
96
- 'Fast shipping', 'Would buy again', 'Perfect fit', 'Love it'
97
- ];
98
-
99
- const REVIEW_CONTENT_TEMPLATES = [
100
- 'This product is exactly what I was looking for. {adj} quality and {adj2} performance.',
101
- 'I\'ve been using this for {weeks} weeks now and it\'s been {adj}.',
102
- 'The {feature} is really {adj}. Would recommend to anyone looking for a {type}.',
103
- 'Shipping was fast and the product arrived in perfect condition. Very {adj}.',
104
- 'For the price, this is an {adj} deal. The {feature} works great.',
105
- 'Had some issues initially but customer support was {adj}. Now it works perfectly.',
106
- 'Compared to other {type}s I\'ve tried, this one is by far the most {adj}.',
107
- 'The build quality is {adj}. Feels {adj2} in hand.'
108
- ];
109
-
110
- /**
111
- * Generate user data
112
- */
113
- export function generateUserData(index: number, rng: SeededRandom) {
114
- const firstName = rng.pick(FIRST_NAMES);
115
- const lastName = rng.pick(LAST_NAMES);
116
- const username = `${firstName.toLowerCase()}${lastName.toLowerCase()}${index}`;
117
-
118
- // Power-law distribution for tier (most users are free)
119
- const tierWeights = [0.6, 0.25, 0.12, 0.03]; // free, basic, premium, enterprise
120
- const tier = rng.pickWeighted(USER_TIERS, tierWeights);
121
-
122
- // Most users are active
123
- const statusWeights = [0.85, 0.12, 0.03];
124
- const status = rng.pickWeighted(USER_STATUSES, statusWeights);
125
-
126
- const createdAt = new Date(Date.now() - rng.nextInt(0, 365 * 2) * 24 * 60 * 60 * 1000);
127
- const hasLoggedIn = rng.next() > 0.1;
128
- const lastLoginAt = hasLoggedIn
129
- ? new Date(createdAt.getTime() + rng.nextInt(0, Date.now() - createdAt.getTime()))
130
- : null;
131
-
132
- return {
133
- email: `${username}@example.com`,
134
- username,
135
- firstName,
136
- lastName,
137
- status,
138
- tier,
139
- region: rng.pick(REGIONS),
140
- orderCount: Math.floor(rng.exponential(0.1)),
141
- totalSpent: Math.floor(rng.exponential(0.01) * 100),
142
- createdAt,
143
- lastLoginAt
144
- };
145
- }
146
-
147
- /**
148
- * Generate product data
149
- */
150
- export function generateProductData(index: number, rng: SeededRandom) {
151
- const category = rng.pick(CATEGORIES);
152
- const subcategories = SUBCATEGORIES[category] || ['General'];
153
- const subcategory = rng.pick(subcategories);
154
- const brand = rng.pick(BRANDS);
155
-
156
- const adjective = rng.pick(ADJECTIVES);
157
- const noun = rng.pick(PRODUCT_NOUNS);
158
- const name = `${brand} ${adjective} ${noun} ${subcategory}`;
159
- const sku = `${category.substring(0, 3).toUpperCase()}-${index.toString().padStart(6, '0')}`;
160
-
161
- const basePrice = Math.floor(rng.powerLaw(10, 1000, 1.2));
162
- const cost = Math.floor(basePrice * (0.3 + rng.next() * 0.3));
163
-
164
- // Power-law for stock (most products have moderate stock)
165
- const stock = Math.floor(rng.powerLaw(0, 1000, 1.5));
166
-
167
- const statusWeights = [0.85, 0.10, 0.05];
168
- const status = rng.pickWeighted(PRODUCT_STATUSES, statusWeights);
169
-
170
- return {
171
- sku,
172
- name,
173
- description: `${name} - High quality ${subcategory.toLowerCase()} from ${brand}. Features include premium materials and excellent craftsmanship.`,
174
- category,
175
- subcategory,
176
- brand,
177
- price: basePrice,
178
- cost,
179
- stock,
180
- status,
181
- rating: Math.round((3 + rng.next() * 2) * 10) / 10, // 3.0 - 5.0
182
- reviewCount: Math.floor(rng.exponential(0.05)),
183
- createdAt: new Date(Date.now() - rng.nextInt(0, 365 * 3) * 24 * 60 * 60 * 1000)
184
- };
185
- }
186
-
187
- /**
188
- * Generate order data
189
- */
190
- export function generateOrderData(
191
- index: number,
192
- rng: SeededRandom,
193
- tracker: RelationTracker
194
- ) {
195
- const userId = tracker.getRandomUserId(rng);
196
- const orderNumber = `ORD-${Date.now().toString(36).toUpperCase()}-${index.toString().padStart(6, '0')}`;
197
-
198
- // Most orders are delivered
199
- const statusWeights = [0.05, 0.10, 0.15, 0.60, 0.07, 0.03];
200
- const status = rng.pickWeighted(ORDER_STATUSES, statusWeights);
201
-
202
- const subtotal = Math.floor(rng.powerLaw(20, 500, 1.3));
203
- const tax = Math.floor(subtotal * 0.08);
204
- const shipping = subtotal > 50 ? 0 : Math.floor(5 + rng.next() * 10);
205
- const total = subtotal + tax + shipping;
206
-
207
- const orderedAt = new Date(Date.now() - rng.nextInt(0, 365) * 24 * 60 * 60 * 1000);
208
- const isShipped = ['shipped', 'delivered'].includes(status);
209
- const shippedAt = isShipped
210
- ? new Date(orderedAt.getTime() + rng.nextInt(1, 5) * 24 * 60 * 60 * 1000)
211
- : null;
212
- const deliveredAt = status === 'delivered' && shippedAt
213
- ? new Date(shippedAt.getTime() + rng.nextInt(1, 7) * 24 * 60 * 60 * 1000)
214
- : null;
215
-
216
- return {
217
- userId,
218
- orderNumber,
219
- status,
220
- subtotal,
221
- tax,
222
- shipping,
223
- total,
224
- itemCount: rng.nextInt(1, 5),
225
- paymentMethod: rng.pick(PAYMENT_METHODS),
226
- shippingRegion: rng.pick(REGIONS),
227
- orderedAt,
228
- shippedAt,
229
- deliveredAt
230
- };
231
- }
232
-
233
- /**
234
- * Generate order item data
235
- */
236
- export function generateOrderItemData(
237
- index: number,
238
- rng: SeededRandom,
239
- tracker: RelationTracker,
240
- orderId?: string,
241
- orderUserId?: string
242
- ) {
243
- const resolvedOrderId = orderId || tracker.getRandomOrderId(rng);
244
- const productId = tracker.getRandomProductId(rng);
245
- const userId = orderUserId || tracker.getOrderUserId(resolvedOrderId) || tracker.getRandomUserId(rng);
246
-
247
- const quantity = rng.nextInt(1, 3);
248
- const unitPrice = Math.floor(rng.powerLaw(10, 200, 1.3));
249
- const discount = rng.next() < 0.3 ? Math.floor(unitPrice * rng.next() * 0.3) : 0;
250
- const total = (unitPrice - discount) * quantity;
251
-
252
- const statusWeights = [0.10, 0.80, 0.07, 0.03];
253
- const status = rng.pickWeighted(ORDER_ITEM_STATUSES, statusWeights);
254
-
255
- return {
256
- orderId: resolvedOrderId,
257
- productId,
258
- userId,
259
- quantity,
260
- unitPrice,
261
- discount,
262
- total,
263
- status
264
- };
265
- }
266
-
267
- /**
268
- * Generate review data
269
- */
270
- export function generateReviewData(
271
- index: number,
272
- rng: SeededRandom,
273
- tracker: RelationTracker
274
- ) {
275
- const productId = tracker.getRandomProductId(rng);
276
- const userId = tracker.getRandomUserId(rng);
277
-
278
- // Rating distribution skewed toward positive
279
- const ratingWeights = [0.05, 0.08, 0.15, 0.32, 0.40]; // 1-5 stars
280
- const rating = rng.pickWeighted([1, 2, 3, 4, 5], ratingWeights);
281
-
282
- const title = rng.pick(REVIEW_TITLES);
283
- const template = rng.pick(REVIEW_CONTENT_TEMPLATES);
284
- const content = template
285
- .replace('{adj}', rng.pick(['excellent', 'great', 'good', 'decent', 'amazing']))
286
- .replace('{adj2}', rng.pick(['solid', 'reliable', 'impressive', 'premium']))
287
- .replace('{weeks}', String(rng.nextInt(1, 12)))
288
- .replace('{feature}', rng.pick(['design', 'build quality', 'performance', 'value']))
289
- .replace('{type}', rng.pick(['product', 'item', 'device']));
290
-
291
- return {
292
- productId,
293
- userId,
294
- rating,
295
- title,
296
- content,
297
- verified: rng.next() > 0.3,
298
- helpfulCount: Math.floor(rng.exponential(0.2)),
299
- createdAt: new Date(Date.now() - rng.nextInt(0, 365) * 24 * 60 * 60 * 1000)
300
- };
301
- }