bunsane 0.3.2 → 0.5.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 (220) hide show
  1. package/CHANGELOG.md +471 -370
  2. package/core/BatchLoader.ts +56 -32
  3. package/core/Entity.ts +93 -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 +8 -7
  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 +25 -10
  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 +383 -0
  28. package/core/entity/finders.ts +202 -0
  29. package/core/entity/getCacheManager.ts +10 -0
  30. package/core/entity/pendingOps.ts +72 -0
  31. package/core/entity/saveEntity.ts +375 -0
  32. package/core/health.ts +93 -4
  33. package/core/hooks/dispatcher.ts +439 -0
  34. package/core/hooks/guards.ts +155 -0
  35. package/core/hooks/registry.ts +247 -0
  36. package/core/metadata/definitions/Component.ts +1 -1
  37. package/core/metadata/index.ts +15 -4
  38. package/core/middleware/RateLimit.ts +102 -105
  39. package/core/middleware/RequestId.ts +2 -9
  40. package/core/middleware/SecurityHeaders.ts +2 -11
  41. package/core/middleware/headers.ts +28 -0
  42. package/core/remote/OutboxWorker.ts +213 -183
  43. package/core/remote/RemoteManager.ts +401 -400
  44. package/core/remote/StreamConsumer.ts +535 -535
  45. package/core/remote/types.ts +153 -151
  46. package/core/requestScope.ts +34 -0
  47. package/core/scheduler/cronEvaluator.ts +174 -0
  48. package/core/scheduler/lifecycleHooks.ts +21 -0
  49. package/core/scheduler/lockCoordinator.ts +27 -0
  50. package/core/scheduler/metrics.ts +14 -0
  51. package/core/scheduler/taskRunner.ts +420 -0
  52. package/core/validateEnv.ts +10 -0
  53. package/database/DatabaseHelper.ts +128 -101
  54. package/database/IndexingStrategy.ts +72 -2
  55. package/database/PreparedStatementCache.ts +8 -2
  56. package/database/cancellable.ts +35 -22
  57. package/database/index.ts +29 -3
  58. package/database/instrumentedDb.ts +141 -141
  59. package/database/sqlHelpers.ts +3 -1
  60. package/endpoints/archetypes.ts +2 -8
  61. package/endpoints/tables.ts +6 -1
  62. package/gql/index.ts +1 -1
  63. package/gql/schema/index.ts +15 -4
  64. package/gql/visitors/ResolverGeneratorVisitor.ts +25 -4
  65. package/package.json +22 -1
  66. package/query/CTENode.ts +5 -3
  67. package/query/ComponentInclusionNode.ts +245 -14
  68. package/query/OrNode.ts +8 -19
  69. package/query/Query.ts +208 -79
  70. package/query/QueryContext.ts +6 -0
  71. package/query/QueryDAG.ts +7 -2
  72. package/query/membershipSource.ts +66 -0
  73. package/storage/LocalStorageProvider.ts +8 -3
  74. package/studio/dist/assets/index-BMZ67Npg.js +254 -0
  75. package/studio/dist/assets/index-BpbuYz9g.css +1 -0
  76. package/studio/{index.html → dist/index.html} +3 -2
  77. package/swagger/generator.ts +11 -1
  78. package/upload/UploadManager.ts +8 -6
  79. package/utils/uuid.ts +40 -10
  80. package/.claude/scheduled_tasks.lock +0 -1
  81. package/.claude/settings.local.json +0 -47
  82. package/.prettierrc +0 -4
  83. package/.serena/memories/architectural-decision-no-dependency-injection.md +0 -76
  84. package/.serena/memories/architecture.md +0 -154
  85. package/.serena/memories/cache-interface-refactoring-2026-01-24.md +0 -165
  86. package/.serena/memories/code_style_and_conventions.md +0 -76
  87. package/.serena/memories/project_overview.md +0 -43
  88. package/.serena/memories/schema-dsl-plan.md +0 -107
  89. package/.serena/memories/suggested_commands.md +0 -80
  90. package/.serena/memories/typescript-compilation-status.md +0 -54
  91. package/.serena/project.yml +0 -114
  92. package/BunSane.jpg +0 -0
  93. package/CLAUDE.md +0 -198
  94. package/TODO.md +0 -2
  95. package/bun.lock +0 -302
  96. package/bunfig.toml +0 -10
  97. package/docs/RFC_APP_REFACTOR.md +0 -248
  98. package/docs/RFC_REFACTOR_TARGETS.md +0 -251
  99. package/docs/SCALABILITY_PLAN.md +0 -175
  100. package/studio/bun.lock +0 -482
  101. package/studio/package.json +0 -39
  102. package/studio/postcss.config.js +0 -6
  103. package/studio/src/components/DataTable.tsx +0 -211
  104. package/studio/src/components/Layout.tsx +0 -13
  105. package/studio/src/components/PageContainer.tsx +0 -9
  106. package/studio/src/components/PageHeader.tsx +0 -13
  107. package/studio/src/components/SearchBar.tsx +0 -57
  108. package/studio/src/components/Sidebar.tsx +0 -294
  109. package/studio/src/components/ui/button.tsx +0 -56
  110. package/studio/src/components/ui/checkbox.tsx +0 -26
  111. package/studio/src/components/ui/input.tsx +0 -25
  112. package/studio/src/hooks/useDataTable.ts +0 -131
  113. package/studio/src/index.css +0 -36
  114. package/studio/src/lib/api.ts +0 -186
  115. package/studio/src/lib/utils.ts +0 -13
  116. package/studio/src/main.tsx +0 -17
  117. package/studio/src/pages/ArcheType.tsx +0 -239
  118. package/studio/src/pages/Components.tsx +0 -124
  119. package/studio/src/pages/EntityInspector.tsx +0 -302
  120. package/studio/src/pages/QueryRunner.tsx +0 -246
  121. package/studio/src/pages/Table.tsx +0 -94
  122. package/studio/src/pages/Welcome.tsx +0 -241
  123. package/studio/src/routes.tsx +0 -45
  124. package/studio/src/store/archeTypeSettings.ts +0 -30
  125. package/studio/src/store/studio.ts +0 -65
  126. package/studio/src/utils/columnHelpers.tsx +0 -114
  127. package/studio/studio-instructions.md +0 -81
  128. package/studio/tailwind.config.js +0 -77
  129. package/studio/utils.ts +0 -54
  130. package/studio/vite.config.js +0 -19
  131. package/tests/benchmark/BENCHMARK_DATABASES_PLAN.md +0 -338
  132. package/tests/benchmark/bunfig.toml +0 -9
  133. package/tests/benchmark/fixtures/EcommerceComponents.ts +0 -283
  134. package/tests/benchmark/fixtures/EcommerceDataGenerators.ts +0 -301
  135. package/tests/benchmark/fixtures/RelationTracker.ts +0 -159
  136. package/tests/benchmark/fixtures/index.ts +0 -6
  137. package/tests/benchmark/index.ts +0 -22
  138. package/tests/benchmark/noop-preload.ts +0 -3
  139. package/tests/benchmark/query-lateral-benchmark.test.ts +0 -372
  140. package/tests/benchmark/runners/BenchmarkLoader.ts +0 -132
  141. package/tests/benchmark/runners/index.ts +0 -4
  142. package/tests/benchmark/scenarios/query-benchmarks.test.ts +0 -465
  143. package/tests/benchmark/scripts/generate-db.ts +0 -344
  144. package/tests/benchmark/scripts/run-benchmarks.ts +0 -97
  145. package/tests/e2e/http.test.ts +0 -130
  146. package/tests/fixtures/archetypes/TestUserArchetype.ts +0 -21
  147. package/tests/fixtures/components/TestOrder.ts +0 -23
  148. package/tests/fixtures/components/TestProduct.ts +0 -23
  149. package/tests/fixtures/components/TestUser.ts +0 -20
  150. package/tests/fixtures/components/index.ts +0 -6
  151. package/tests/graphql/SchemaGeneration.test.ts +0 -90
  152. package/tests/graphql/builders/ResolverBuilder.test.ts +0 -223
  153. package/tests/graphql/builders/TypeDefBuilder.test.ts +0 -153
  154. package/tests/helpers/MockRedisClient.ts +0 -113
  155. package/tests/helpers/MockRedisStreamServer.ts +0 -448
  156. package/tests/integration/archetype/ArcheType.persistence.test.ts +0 -241
  157. package/tests/integration/cache/CacheInvalidation.test.ts +0 -259
  158. package/tests/integration/entity/Entity.persistence.test.ts +0 -333
  159. package/tests/integration/entity/Entity.saveTimeout.test.ts +0 -110
  160. package/tests/integration/loaders/RequestLoaders.abort.test.ts +0 -82
  161. package/tests/integration/query/Query.abort.test.ts +0 -66
  162. package/tests/integration/query/Query.complexAnalysis.test.ts +0 -557
  163. package/tests/integration/query/Query.edgeCases.test.ts +0 -595
  164. package/tests/integration/query/Query.exec.test.ts +0 -576
  165. package/tests/integration/query/Query.explainAnalyze.test.ts +0 -233
  166. package/tests/integration/query/Query.jsonbArray.test.ts +0 -214
  167. package/tests/integration/remote/dlq.test.ts +0 -175
  168. package/tests/integration/remote/event-dispatch.test.ts +0 -114
  169. package/tests/integration/remote/outbox.test.ts +0 -130
  170. package/tests/integration/remote/rpc.test.ts +0 -177
  171. package/tests/pglite-setup.ts +0 -62
  172. package/tests/setup.ts +0 -164
  173. package/tests/stress/BenchmarkRunner.ts +0 -203
  174. package/tests/stress/DataSeeder.ts +0 -190
  175. package/tests/stress/StressTestReporter.ts +0 -229
  176. package/tests/stress/cursor-perf-test.ts +0 -171
  177. package/tests/stress/fixtures/RealisticComponents.ts +0 -235
  178. package/tests/stress/fixtures/StressTestComponents.ts +0 -58
  179. package/tests/stress/index.ts +0 -7
  180. package/tests/stress/scenarios/query-benchmarks.test.ts +0 -285
  181. package/tests/stress/scenarios/realistic-scenarios.test.ts +0 -1081
  182. package/tests/stress/scenarios/timeout-investigation.test.ts +0 -522
  183. package/tests/unit/BatchLoader.test.ts +0 -196
  184. package/tests/unit/archetype/ArcheType.test.ts +0 -107
  185. package/tests/unit/cache/CacheManager.test.ts +0 -498
  186. package/tests/unit/cache/MemoryCache.test.ts +0 -260
  187. package/tests/unit/cache/RedisCache.test.ts +0 -411
  188. package/tests/unit/database/cancellable.test.ts +0 -81
  189. package/tests/unit/database/instrumentedDb.test.ts +0 -160
  190. package/tests/unit/entity/Entity.components.test.ts +0 -317
  191. package/tests/unit/entity/Entity.drainSideEffects.test.ts +0 -51
  192. package/tests/unit/entity/Entity.reload.test.ts +0 -63
  193. package/tests/unit/entity/Entity.requireComponents.test.ts +0 -72
  194. package/tests/unit/entity/Entity.test.ts +0 -345
  195. package/tests/unit/gql/depthLimit.test.ts +0 -203
  196. package/tests/unit/gql/operationMiddleware.test.ts +0 -293
  197. package/tests/unit/health/Health.test.ts +0 -129
  198. package/tests/unit/middleware/AccessLog.test.ts +0 -37
  199. package/tests/unit/middleware/Middleware.test.ts +0 -98
  200. package/tests/unit/middleware/RequestId.test.ts +0 -54
  201. package/tests/unit/middleware/SecurityHeaders.test.ts +0 -66
  202. package/tests/unit/query/FilterBuilder.test.ts +0 -111
  203. package/tests/unit/query/JsonbArrayBuilder.test.ts +0 -178
  204. package/tests/unit/query/Query.emptyString.test.ts +0 -69
  205. package/tests/unit/query/Query.test.ts +0 -310
  206. package/tests/unit/remote/CircuitBreaker.test.ts +0 -159
  207. package/tests/unit/remote/RemoteError.test.ts +0 -55
  208. package/tests/unit/remote/decorators.test.ts +0 -195
  209. package/tests/unit/remote/metrics.test.ts +0 -115
  210. package/tests/unit/remote/mockRedisStreamServer.test.ts +0 -104
  211. package/tests/unit/scheduler/DistributedLock.test.ts +0 -274
  212. package/tests/unit/scheduler/SchedulerManager.timeBased.test.ts +0 -95
  213. package/tests/unit/schema/schema-integration.test.ts +0 -426
  214. package/tests/unit/schema/schema.test.ts +0 -580
  215. package/tests/unit/storage/S3StorageProvider.test.ts +0 -567
  216. package/tests/unit/upload/RestUpload.test.ts +0 -267
  217. package/tests/unit/validateEnv.test.ts +0 -82
  218. package/tests/utils/entity-tracker.ts +0 -57
  219. package/tests/utils/index.ts +0 -13
  220. 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