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,77 +0,0 @@
1
- /** @type {import('tailwindcss').Config} */
2
- export default {
3
- darkMode: ["class"],
4
- content: [
5
- './pages/**/*.{ts,tsx}',
6
- './components/**/*.{ts,tsx}',
7
- './app/**/*.{ts,tsx}',
8
- './src/**/*.{ts,tsx}',
9
- ],
10
- prefix: "",
11
- theme: {
12
- container: {
13
- center: true,
14
- padding: "2rem",
15
- screens: {
16
- "2xl": "1400px",
17
- },
18
- },
19
- extend: {
20
- colors: {
21
- border: "hsl(var(--border))",
22
- input: "hsl(var(--input))",
23
- ring: "hsl(var(--ring))",
24
- background: "hsl(var(--background))",
25
- foreground: "hsl(var(--foreground))",
26
- primary: {
27
- DEFAULT: "hsl(var(--primary))",
28
- foreground: "hsl(var(--primary-foreground))",
29
- },
30
- secondary: {
31
- DEFAULT: "hsl(var(--secondary))",
32
- foreground: "hsl(var(--secondary-foreground))",
33
- },
34
- destructive: {
35
- DEFAULT: "hsl(var(--destructive))",
36
- foreground: "hsl(var(--destructive-foreground))",
37
- },
38
- muted: {
39
- DEFAULT: "hsl(var(--muted))",
40
- foreground: "hsl(var(--muted-foreground))",
41
- },
42
- accent: {
43
- DEFAULT: "hsl(var(--accent))",
44
- foreground: "hsl(var(--accent-foreground))",
45
- },
46
- popover: {
47
- DEFAULT: "hsl(var(--popover))",
48
- foreground: "hsl(var(--popover-foreground))",
49
- },
50
- card: {
51
- DEFAULT: "hsl(var(--card))",
52
- foreground: "hsl(var(--card-foreground))",
53
- },
54
- },
55
- borderRadius: {
56
- lg: "var(--radius)",
57
- md: "calc(var(--radius) - 2px)",
58
- sm: "calc(var(--radius) - 4px)",
59
- },
60
- keyframes: {
61
- "accordion-down": {
62
- from: { height: "0" },
63
- to: { height: "var(--radix-accordion-content-height)" },
64
- },
65
- "accordion-up": {
66
- from: { height: "var(--radix-accordion-content-height)" },
67
- to: { height: "0" },
68
- },
69
- },
70
- animation: {
71
- "accordion-down": "accordion-down 0.2s ease-out",
72
- "accordion-up": "accordion-up 0.2s ease-out",
73
- },
74
- },
75
- },
76
- plugins: [require("tailwindcss-animate")],
77
- }
package/studio/utils.ts DELETED
@@ -1,54 +0,0 @@
1
- /**
2
- * Finds the indicator component name from an archetype's field list.
3
- * The indicator is used to identify entities of this archetype type.
4
- *
5
- * Priority order:
6
- * 1. {ArcheTypeName}Tag (e.g., UserTag)
7
- * 2. {ArcheTypeName}Id (e.g., UserId)
8
- * 3. Any field starting with {ArcheTypeName}
9
- * 4. Any field containing {ArcheTypeName}
10
- * 5. Fallback to first component
11
- */
12
- export function findIndicatorComponentName(
13
- archeTypeName: string,
14
- fields: Array<{ componentName: string; fieldName: string }>
15
- ): string | null {
16
- if (fields.length === 0) {
17
- return null;
18
- }
19
-
20
- const archeTypeNameLower = archeTypeName.toLowerCase();
21
- const componentNames = fields.map(field => field.componentName);
22
-
23
- const tagComponentName = `${archeTypeName}Tag`;
24
- const tagMatch = componentNames.find(
25
- name => name.toLowerCase() === tagComponentName.toLowerCase()
26
- );
27
- if (tagMatch) {
28
- return tagMatch;
29
- }
30
-
31
- const idComponentName = `${archeTypeName}Id`;
32
- const idMatch = componentNames.find(
33
- name => name.toLowerCase() === idComponentName.toLowerCase()
34
- );
35
- if (idMatch) {
36
- return idMatch;
37
- }
38
-
39
- const startsWithMatch = componentNames.find(
40
- name => name.toLowerCase().startsWith(archeTypeNameLower)
41
- );
42
- if (startsWithMatch) {
43
- return startsWithMatch;
44
- }
45
-
46
- const containsMatch = componentNames.find(
47
- name => name.toLowerCase().includes(archeTypeNameLower)
48
- );
49
- if (containsMatch) {
50
- return containsMatch;
51
- }
52
-
53
- return componentNames[0] ?? null;
54
- }
@@ -1,19 +0,0 @@
1
- import { defineConfig } from 'vite'
2
- import react from '@vitejs/plugin-react'
3
-
4
- export default defineConfig({
5
- base: '/studio/',
6
- plugins: [react()],
7
- build: {
8
- outDir: 'dist',
9
- rollupOptions: {
10
- output: {
11
- manualChunks: undefined,
12
- }
13
- }
14
- },
15
- server: {
16
- port: 5173,
17
- host: true
18
- }
19
- })
@@ -1,338 +0,0 @@
1
- # PGlite Persistent Benchmark Databases Plan
2
-
3
- ## Overview
4
-
5
- Create pre-populated PGlite databases at various scale tiers for consistent, reproducible query benchmarks without re-seeding overhead.
6
-
7
- ## Database Tiers
8
-
9
- | Tier | Entities | Products | Orders | Users | Reviews | Est. Size |
10
- |------|----------|----------|--------|-------|---------|-----------|
11
- | xs | 10,000 | 5,000 | 3,000 | 1,000 | 1,000 | ~50 MB |
12
- | sm | 50,000 | 25,000 | 15,000 | 5,000 | 5,000 | ~250 MB |
13
- | md | 100,000 | 50,000 | 30,000 | 10,000| 10,000 | ~500 MB |
14
- | lg | 500,000 | 250,000 | 150,000| 50,000| 50,000 | ~2.5 GB |
15
- | xl | 1,000,000| 500,000 | 300,000| 100,000| 100,000| ~5 GB |
16
-
17
- ## Directory Structure
18
-
19
- ```
20
- tests/benchmark/
21
- ├── BENCHMARK_DATABASES_PLAN.md # This plan
22
- ├── databases/ # Persistent PGlite databases (gitignored)
23
- │ ├── bench-xs/ # 10k entities
24
- │ ├── bench-sm/ # 50k entities
25
- │ ├── bench-md/ # 100k entities
26
- │ ├── bench-lg/ # 500k entities
27
- │ └── bench-xl/ # 1M entities
28
- ├── fixtures/
29
- │ └── BenchmarkComponents.ts # Reusable components for benchmarks
30
- ├── generators/
31
- │ ├── index.ts # Main generator entry point
32
- │ ├── DataGenerator.ts # Realistic data generation utilities
33
- │ ├── EntitySeeder.ts # Batch entity creation with relations
34
- │ └── ProgressReporter.ts # CLI progress reporting
35
- ├── runners/
36
- │ ├── QueryBenchmark.ts # Query benchmark runner
37
- │ └── RelationBenchmark.ts # BelongsTo/HasMany benchmark
38
- └── generate-databases.ts # CLI script to generate all tiers
39
- ```
40
-
41
- ## Data Model (E-commerce Scenario)
42
-
43
- ### Components (from RealisticComponents.ts)
44
-
45
- 1. **Product** - Core product data (name, SKU, category, status, rating)
46
- 2. **Inventory** - Stock tracking (quantity, warehouse, stock status)
47
- 3. **Pricing** - Price info (base, sale, cost, discount, currency)
48
- 4. **Vendor** - Supplier info (name, region, rating, tier)
49
- 5. **ProductMetrics** - Analytics (views, purchases, conversion)
50
-
51
- ### Additional Components for Relations
52
-
53
- 6. **User** - Customer data (name, email, tier, region)
54
- 7. **Order** - Order data (userId, status, total, itemCount)
55
- 8. **OrderItem** - Line items (orderId, productId, quantity, price)
56
- 9. **Review** - Product reviews (userId, productId, rating, text)
57
- 10. **Wishlist** - User wishlists (userId, productIds[])
58
-
59
- ### Relation Structure
60
-
61
- ```
62
- User (1) ──HasMany──> Order (N)
63
- User (1) ──HasMany──> Review (N)
64
- User (1) ──HasMany──> Wishlist (1)
65
-
66
- Order (1) ──HasMany──> OrderItem (N)
67
- Order (N) ──BelongsTo──> User (1)
68
-
69
- OrderItem (N) ──BelongsTo──> Order (1)
70
- OrderItem (N) ──BelongsTo──> Product (1)
71
-
72
- Review (N) ──BelongsTo──> User (1)
73
- Review (N) ──BelongsTo──> Product (1)
74
-
75
- Product (1) ──HasMany──> Review (N)
76
- Product (1) ──HasMany──> OrderItem (N)
77
- Product (N) ──BelongsTo──> Vendor (1)
78
- ```
79
-
80
- ## Implementation Steps
81
-
82
- ### Phase 1: Infrastructure Setup
83
-
84
- 1. **Create directory structure**
85
- - Create `tests/benchmark/` directories
86
- - Add `databases/` to `.gitignore`
87
-
88
- 2. **Create BenchmarkComponents.ts**
89
- - Define User, Order, OrderItem, Review, Wishlist components
90
- - Define BenchmarkArchetypes with proper relations
91
-
92
- 3. **Create DataGenerator.ts**
93
- - Faker-like utilities for realistic data (names, emails, addresses)
94
- - Deterministic seeding with configurable seed for reproducibility
95
- - Batch generation for memory efficiency
96
-
97
- ### Phase 2: Seeding System
98
-
99
- 4. **Create EntitySeeder.ts**
100
- ```typescript
101
- interface SeederConfig {
102
- tier: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
103
- seed?: number; // For reproducibility
104
- batchSize?: number; // Default 1000
105
- showProgress?: boolean; // CLI progress bar
106
- }
107
-
108
- class EntitySeeder {
109
- async seedProducts(count: number): Promise<string[]>;
110
- async seedUsers(count: number): Promise<string[]>;
111
- async seedOrders(userIds: string[], productIds: string[]): Promise<void>;
112
- async seedReviews(userIds: string[], productIds: string[]): Promise<void>;
113
- async createIndexes(): Promise<void>;
114
- async vacuum(): Promise<void>;
115
- }
116
- ```
117
-
118
- 5. **Create ProgressReporter.ts**
119
- - CLI progress bars for long-running operations
120
- - ETA calculation
121
- - Memory usage monitoring
122
-
123
- ### Phase 3: Generator Script
124
-
125
- 6. **Create generate-databases.ts**
126
- ```bash
127
- # Generate all tiers
128
- bun tests/benchmark/generate-databases.ts --all
129
-
130
- # Generate specific tier
131
- bun tests/benchmark/generate-databases.ts --tier=md
132
-
133
- # Regenerate (delete existing + recreate)
134
- bun tests/benchmark/generate-databases.ts --tier=sm --force
135
-
136
- # Custom seed for reproducibility
137
- bun tests/benchmark/generate-databases.ts --tier=xs --seed=42
138
- ```
139
-
140
- 7. **Generation Process**
141
- ```
142
- For each tier:
143
- 1. Check if database directory exists (skip if exists, unless --force)
144
- 2. Create PGlite with dataDir
145
- 3. Run database migrations (PrepareDatabase)
146
- 4. Register components
147
- 5. Seed entities in batches with progress
148
- 6. Create relation foreign keys
149
- 7. Create indexes (CreateRelationIndexes)
150
- 8. Run VACUUM ANALYZE
151
- 9. Report final stats (entity count, db size, time taken)
152
- ```
153
-
154
- ### Phase 4: Benchmark Runner
155
-
156
- 8. **Create benchmark loading utility**
157
- ```typescript
158
- // tests/benchmark/runners/loadBenchmarkDb.ts
159
- export async function loadBenchmarkDatabase(
160
- tier: 'xs' | 'sm' | 'md' | 'lg' | 'xl'
161
- ): Promise<{ pg: PGlite; server: PGLiteSocketServer; cleanup: () => Promise<void> }>;
162
- ```
163
-
164
- 9. **Create QueryBenchmark.ts**
165
- ```typescript
166
- interface BenchmarkResult {
167
- name: string;
168
- tier: string;
169
- entityCount: number;
170
- queryTimeMs: number;
171
- rowsReturned: number;
172
- memoryUsedMB: number;
173
- }
174
-
175
- // Benchmark scenarios
176
- - Simple filter (single component, indexed field)
177
- - Multi-component filter (2-3 components with AND)
178
- - Complex query (multi-component + sort + limit)
179
- - BelongsTo resolution (N users with their orders)
180
- - HasMany resolution (N products with their reviews)
181
- - Deep nesting (Order -> User -> Reviews -> Products)
182
- ```
183
-
184
- ### Phase 5: npm Scripts
185
-
186
- 10. **Add to package.json**
187
- ```json
188
- {
189
- "scripts": {
190
- "bench:generate": "bun tests/benchmark/generate-databases.ts --all",
191
- "bench:generate:xs": "bun tests/benchmark/generate-databases.ts --tier=xs",
192
- "bench:generate:sm": "bun tests/benchmark/generate-databases.ts --tier=sm",
193
- "bench:generate:md": "bun tests/benchmark/generate-databases.ts --tier=md",
194
- "bench:generate:lg": "bun tests/benchmark/generate-databases.ts --tier=lg",
195
- "bench:generate:xl": "bun tests/benchmark/generate-databases.ts --tier=xl",
196
- "bench:query": "bun tests/benchmark/runners/QueryBenchmark.ts",
197
- "bench:relations": "bun tests/benchmark/runners/RelationBenchmark.ts"
198
- }
199
- }
200
- ```
201
-
202
- ## Data Generation Details
203
-
204
- ### Realistic Distribution
205
-
206
- ```typescript
207
- // Product categories follow power-law distribution
208
- // ~40% in top 3 categories, ~60% in remaining 7
209
- const categoryWeights = {
210
- 'Electronics': 0.20,
211
- 'Clothing': 0.12,
212
- 'Home & Garden': 0.08,
213
- // ... rest evenly distributed
214
- };
215
-
216
- // Order counts per user follow exponential distribution
217
- // Most users: 1-5 orders, some power users: 50+ orders
218
- function getOrderCountForUser(): number {
219
- const r = Math.random();
220
- if (r < 0.7) return randomInt(1, 5);
221
- if (r < 0.9) return randomInt(6, 20);
222
- return randomInt(21, 100);
223
- }
224
-
225
- // Reviews are skewed toward extreme ratings (1, 4, 5 stars)
226
- function getReviewRating(): number {
227
- const r = Math.random();
228
- if (r < 0.05) return 1; // 5% - 1 star
229
- if (r < 0.10) return 2; // 5% - 2 stars
230
- if (r < 0.20) return 3; // 10% - 3 stars
231
- if (r < 0.50) return 4; // 30% - 4 stars
232
- return 5; // 50% - 5 stars
233
- }
234
- ```
235
-
236
- ### Batch Insertion Strategy
237
-
238
- ```typescript
239
- // Avoid memory issues with large datasets
240
- const BATCH_SIZE = 1000;
241
-
242
- async function seedInBatches<T>(
243
- total: number,
244
- generator: (batchIndex: number, batchSize: number) => Promise<T[]>,
245
- inserter: (items: T[]) => Promise<void>
246
- ) {
247
- const batches = Math.ceil(total / BATCH_SIZE);
248
- for (let i = 0; i < batches; i++) {
249
- const items = await generator(i, Math.min(BATCH_SIZE, total - i * BATCH_SIZE));
250
- await inserter(items);
251
- // Allow GC between batches
252
- if (i % 10 === 0) await Bun.sleep(1);
253
- }
254
- }
255
- ```
256
-
257
- ## Benchmark Scenarios
258
-
259
- ### Query Performance Matrix
260
-
261
- | Scenario | Description | Expected Behavior |
262
- |----------|-------------|-------------------|
263
- | Q1 | Filter by indexed field | O(log n), <50ms at 1M |
264
- | Q2 | Filter by non-indexed field | O(n), slower |
265
- | Q3 | Multi-component AND | Uses INTERSECT optimization |
266
- | Q4 | Multi-component + Sort | Uses scalar subquery sort |
267
- | Q5 | BelongsTo single | DataLoader batched |
268
- | Q6 | HasMany collection | Single filtered query |
269
- | Q7 | Nested relations 3-deep | DataLoader + batching |
270
- | Q8 | Pagination (offset) | Cursor recommended at scale |
271
- | Q9 | Count aggregation | Fast with proper indexes |
272
- | Q10 | Full table scan | Baseline comparison |
273
-
274
- ### Expected Performance Targets
275
-
276
- | Tier | Q1 (indexed) | Q3 (multi) | Q5 (BelongsTo) | Q7 (nested) |
277
- |------|--------------|------------|----------------|-------------|
278
- | xs | <10ms | <20ms | <30ms | <50ms |
279
- | sm | <15ms | <40ms | <50ms | <100ms |
280
- | md | <20ms | <60ms | <80ms | <150ms |
281
- | lg | <30ms | <100ms | <150ms | <300ms |
282
- | xl | <50ms | <150ms | <250ms | <500ms |
283
-
284
- ## Files to Create
285
-
286
- 1. `tests/benchmark/fixtures/BenchmarkComponents.ts`
287
- 2. `tests/benchmark/fixtures/BenchmarkArchetypes.ts`
288
- 3. `tests/benchmark/generators/DataGenerator.ts`
289
- 4. `tests/benchmark/generators/EntitySeeder.ts`
290
- 5. `tests/benchmark/generators/ProgressReporter.ts`
291
- 6. `tests/benchmark/generators/index.ts`
292
- 7. `tests/benchmark/generate-databases.ts`
293
- 8. `tests/benchmark/runners/loadBenchmarkDb.ts`
294
- 9. `tests/benchmark/runners/QueryBenchmark.ts`
295
- 10. `tests/benchmark/runners/RelationBenchmark.ts`
296
-
297
- ## Gitignore Addition
298
-
299
- ```gitignore
300
- # Benchmark databases (large, generated locally)
301
- tests/benchmark/databases/
302
- ```
303
-
304
- ## Usage Example
305
-
306
- ```bash
307
- # First time: Generate benchmark databases
308
- bun run bench:generate:xs # Quick: ~30s
309
- bun run bench:generate:md # Medium: ~5min
310
- bun run bench:generate:xl # Large: ~30min
311
-
312
- # Run benchmarks (uses pre-generated databases)
313
- bun run bench:query --tier=md
314
- bun run bench:relations --tier=lg
315
-
316
- # Results output
317
- # ┌─────────────────────────────────────────────────────────┐
318
- # │ Query Benchmark Results - Tier: md (100,000 entities) │
319
- # ├──────────────────────┬─────────┬──────────┬────────────┤
320
- # │ Scenario │ Time │ Rows │ Memory │
321
- # ├──────────────────────┼─────────┼──────────┼────────────┤
322
- # │ Q1: Indexed filter │ 18ms │ 1,234 │ 2.3 MB │
323
- # │ Q3: Multi-component │ 52ms │ 456 │ 4.1 MB │
324
- # │ Q5: BelongsTo batch │ 71ms │ 100 │ 5.8 MB │
325
- # │ Q7: Nested 3-deep │ 142ms │ 50 │ 12.4 MB │
326
- # └──────────────────────┴─────────┴──────────┴────────────┘
327
- ```
328
-
329
- ## Next Steps
330
-
331
- 1. Review and approve this plan
332
- 2. Create the directory structure
333
- 3. Implement Phase 1-2 (components + seeder)
334
- 4. Implement Phase 3 (generator script)
335
- 5. Generate xs/sm tiers for initial testing
336
- 6. Implement Phase 4 (benchmark runners)
337
- 7. Generate remaining tiers
338
- 8. Document results and establish baselines
@@ -1,9 +0,0 @@
1
- [test]
2
- # No preload for benchmark tests - we manage the database connection ourselves
3
- preload = []
4
-
5
- # Longer timeout for benchmarks
6
- timeout = 300000
7
-
8
- # Single-threaded to avoid connection conflicts
9
- smol = true