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,285 +0,0 @@
1
- /**
2
- * Stress Tests - Query Performance Benchmarks
3
- *
4
- * Tests query performance with configurable data volumes
5
- * Default: 10,000 records (smoke test)
6
- * Set STRESS_RECORD_COUNT env var for larger tests
7
- */
8
- import { describe, test, beforeAll, afterAll, expect } from 'bun:test';
9
- import { DataSeeder } from '../DataSeeder';
10
- import { BenchmarkRunner } from '../BenchmarkRunner';
11
- import { StressTestReporter } from '../StressTestReporter';
12
- import { Query, FilterOp } from '../../../query/Query';
13
- import { StressUser, StressProfile } from '../fixtures/StressTestComponents';
14
- import { ensureComponentsRegistered } from '../../utils';
15
-
16
- // Configurable via environment variable
17
- const RECORD_COUNT = parseInt(process.env.STRESS_RECORD_COUNT || '10000', 10);
18
- const BATCH_SIZE = Math.min(5000, Math.floor(RECORD_COUNT / 10) || 1000);
19
-
20
- describe('Stress Tests - Query Performance', () => {
21
- const seeder = new DataSeeder();
22
- const benchmark = new BenchmarkRunner();
23
- const reporter = new StressTestReporter();
24
- let entityIds: string[] = [];
25
- let setupTime = 0;
26
-
27
- beforeAll(async () => {
28
- const startSetup = performance.now();
29
-
30
- // Ensure components are registered
31
- await ensureComponentsRegistered(StressUser, StressProfile);
32
-
33
- // Wait for index creation to settle (prevents deadlocks from concurrent index creation)
34
- await new Promise(resolve => setTimeout(resolve, 3000));
35
-
36
- console.log(`\n Seeding ${RECORD_COUNT.toLocaleString()} records...`);
37
-
38
- const result = await seeder.seed(
39
- StressUser,
40
- (i) => ({
41
- name: `User ${i}`,
42
- email: `user${i}@stress.test`,
43
- age: 18 + (i % 62),
44
- status: ['active', 'inactive', 'pending', 'banned'][i % 4],
45
- score: Math.random() * 1000,
46
- createdAt: new Date(Date.now() - Math.random() * 365 * 24 * 60 * 60 * 1000)
47
- }),
48
- {
49
- totalEntities: RECORD_COUNT,
50
- batchSize: BATCH_SIZE,
51
- onProgress: (current, total, elapsed) => {
52
- if (current % (BATCH_SIZE * 2) === 0 || current === total) {
53
- const pct = ((current / total) * 100).toFixed(1);
54
- const rate = ((current / elapsed) * 1000).toFixed(0);
55
- console.log(` Progress: ${pct}% (${rate} records/sec)`);
56
- }
57
- }
58
- }
59
- );
60
-
61
- entityIds = result.entityIds;
62
- console.log(` Seeded in ${(result.totalTime / 1000).toFixed(1)}s (${result.recordsPerSecond.toFixed(0)} records/sec)`);
63
-
64
- // Add profile components to 50% of entities
65
- if (RECORD_COUNT >= 1000) {
66
- console.log(' Adding profile components to 50% of entities...');
67
- await seeder.seedAdditionalComponent(
68
- entityIds.slice(0, Math.floor(entityIds.length / 2)),
69
- StressProfile,
70
- (i) => ({
71
- bio: `This is bio ${i}`,
72
- avatarUrl: `https://example.com/avatar/${i}.png`,
73
- verified: i % 3 === 0
74
- }),
75
- BATCH_SIZE
76
- );
77
- }
78
-
79
- console.log(' Running VACUUM ANALYZE...');
80
- await seeder.optimize();
81
-
82
- setupTime = performance.now() - startSetup;
83
- console.log(` Setup complete in ${(setupTime / 1000).toFixed(1)}s\n`);
84
- });
85
-
86
- afterAll(async () => {
87
- // Print report
88
- const recordCount = await seeder.getRecordCount();
89
- const report = reporter.generateReport(benchmark.getResults(), {
90
- recordCount,
91
- environment: `PostgreSQL, Bun ${Bun.version}`,
92
- duration: setupTime
93
- });
94
- console.log('\n' + report);
95
-
96
- // Cleanup seeded data
97
- console.log('\n Cleaning up test data...');
98
- await seeder.cleanup(entityIds, BATCH_SIZE);
99
- console.log(' Cleanup complete.');
100
- });
101
-
102
- test('indexed equality filter (status = active)', async () => {
103
- const result = await benchmark.runWithOutput(
104
- 'Filter: status = active',
105
- () => new Query()
106
- .with(StressUser, { filters: [{ field: 'status', operator: FilterOp.EQ, value: 'active' }] })
107
- .take(100)
108
- .exec(),
109
- { targetP95: 50, iterations: 15 }
110
- );
111
- expect(result.passed).toBe(true);
112
- });
113
-
114
- test('indexed range filter (age 25-35)', async () => {
115
- const result = await benchmark.runWithOutput(
116
- 'Filter: age 25-35',
117
- () => new Query()
118
- .with(StressUser, {
119
- filters: [
120
- { field: 'age', operator: FilterOp.GTE, value: 25 },
121
- { field: 'age', operator: FilterOp.LTE, value: 35 }
122
- ]
123
- })
124
- .take(100)
125
- .exec(),
126
- { targetP95: 75, iterations: 15 }
127
- );
128
- expect(result.passed).toBe(true);
129
- });
130
-
131
- test('count query', async () => {
132
- const result = await benchmark.runWithOutput(
133
- 'COUNT all',
134
- async () => [await new Query().with(StressUser).count()],
135
- { targetP95: 100, iterations: 15 }
136
- );
137
- expect(result.passed).toBe(true);
138
- });
139
-
140
- test('pagination - shallow offset (1000)', async () => {
141
- const result = await benchmark.runWithOutput(
142
- 'Offset 1000',
143
- () => new Query()
144
- .with(StressUser)
145
- .sortBy(StressUser, 'name', 'ASC')
146
- .take(100)
147
- .offset(1000)
148
- .exec(),
149
- { targetP95: 75, iterations: 15 }
150
- );
151
- expect(result.passed).toBe(true);
152
- });
153
-
154
- test('pagination - deep offset (50% of records)', async () => {
155
- const deepOffset = Math.floor(RECORD_COUNT / 2);
156
- const result = await benchmark.runWithOutput(
157
- `Offset ${deepOffset.toLocaleString()}`,
158
- () => new Query()
159
- .with(StressUser)
160
- .sortBy(StressUser, 'name', 'ASC')
161
- .take(100)
162
- .offset(deepOffset)
163
- .exec(),
164
- { targetP95: 500, iterations: 10 }
165
- );
166
- // Deep pagination is expected to be slower, use a more lenient target
167
- expect(result.timings.p95).toBeLessThan(2000);
168
- });
169
-
170
- test('cursor pagination - first page', async () => {
171
- const result = await benchmark.runWithOutput(
172
- 'Cursor: first page',
173
- () => new Query()
174
- .with(StressUser)
175
- .take(100)
176
- .exec(),
177
- { targetP95: 50, iterations: 15 }
178
- );
179
- expect(result.passed).toBe(true);
180
- });
181
-
182
- test('cursor pagination - from middle (using cursor)', async () => {
183
- // First, get an entity ID from the middle of the dataset
184
- const midpointResults = await new Query()
185
- .with(StressUser)
186
- .take(1)
187
- .offset(Math.floor(RECORD_COUNT / 2))
188
- .exec();
189
-
190
- const cursorId = midpointResults[0]?.id;
191
- expect(cursorId).toBeDefined();
192
-
193
- const result = await benchmark.runWithOutput(
194
- 'Cursor: from middle (O(1))',
195
- () => new Query()
196
- .with(StressUser)
197
- .cursor(cursorId!)
198
- .take(100)
199
- .exec(),
200
- { targetP95: 50, iterations: 15 }
201
- );
202
- // Cursor pagination should be fast regardless of position
203
- expect(result.passed).toBe(true);
204
- });
205
-
206
- test('cursor pagination - near end (using cursor)', async () => {
207
- // Get an entity ID from near the end (90%)
208
- const nearEndResults = await new Query()
209
- .with(StressUser)
210
- .take(1)
211
- .offset(Math.floor(RECORD_COUNT * 0.9))
212
- .exec();
213
-
214
- const cursorId = nearEndResults[0]?.id;
215
- expect(cursorId).toBeDefined();
216
-
217
- const result = await benchmark.runWithOutput(
218
- 'Cursor: near end (O(1))',
219
- () => new Query()
220
- .with(StressUser)
221
- .cursor(cursorId!)
222
- .take(100)
223
- .exec(),
224
- { targetP95: 50, iterations: 15 }
225
- );
226
- // Cursor pagination should be fast regardless of position
227
- expect(result.passed).toBe(true);
228
- });
229
-
230
- test('multi-component join (User + Profile)', async () => {
231
- const result = await benchmark.runWithOutput(
232
- 'Join: User + Profile',
233
- () => new Query()
234
- .with(StressUser)
235
- .with(StressProfile)
236
- .take(100)
237
- .exec(),
238
- { targetP95: 150, iterations: 15 }
239
- );
240
- expect(result.passed).toBe(true);
241
- });
242
-
243
- test('combined filters and sort', async () => {
244
- const result = await benchmark.runWithOutput(
245
- 'Filter + Sort + Limit',
246
- () => new Query()
247
- .with(StressUser, {
248
- filters: [
249
- { field: 'status', operator: FilterOp.EQ, value: 'active' },
250
- { field: 'age', operator: FilterOp.GTE, value: 21 }
251
- ]
252
- })
253
- .sortBy(StressUser, 'score', 'DESC')
254
- .take(50)
255
- .exec(),
256
- { targetP95: 100, iterations: 15 }
257
- );
258
- expect(result.passed).toBe(true);
259
- });
260
-
261
- test('simple query without filters', async () => {
262
- const result = await benchmark.runWithOutput(
263
- 'Simple: take 100',
264
- () => new Query()
265
- .with(StressUser)
266
- .take(100)
267
- .exec(),
268
- { targetP95: 50, iterations: 15 }
269
- );
270
- expect(result.passed).toBe(true);
271
- });
272
-
273
- test('query with populate', async () => {
274
- const result = await benchmark.runWithOutput(
275
- 'Populated: take 50',
276
- () => new Query()
277
- .with(StressUser)
278
- .populate()
279
- .take(50)
280
- .exec(),
281
- { targetP95: 100, iterations: 15 }
282
- );
283
- expect(result.passed).toBe(true);
284
- });
285
- });