bunsane 0.1.4 → 0.2.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 (257) hide show
  1. package/.claude/settings.local.json +47 -0
  2. package/.claude/skills/update-memory.md +74 -0
  3. package/.prettierrc +4 -0
  4. package/.serena/memories/architectural-decision-no-dependency-injection.md +76 -0
  5. package/.serena/memories/architecture.md +154 -0
  6. package/.serena/memories/cache-interface-refactoring-2026-01-24.md +165 -0
  7. package/.serena/memories/code_style_and_conventions.md +76 -0
  8. package/.serena/memories/project_overview.md +43 -0
  9. package/.serena/memories/schema-dsl-plan.md +107 -0
  10. package/.serena/memories/suggested_commands.md +80 -0
  11. package/.serena/memories/typescript-compilation-status.md +54 -0
  12. package/.serena/project.yml +114 -0
  13. package/TODO.md +1 -7
  14. package/bun.lock +150 -4
  15. package/bunfig.toml +10 -0
  16. package/config/cache.config.ts +77 -0
  17. package/config/upload.config.ts +4 -5
  18. package/core/App.ts +870 -123
  19. package/core/ArcheType.ts +2268 -377
  20. package/core/BatchLoader.ts +181 -71
  21. package/core/Config.ts +153 -0
  22. package/core/Decorators.ts +4 -1
  23. package/core/Entity.ts +621 -92
  24. package/core/EntityHookManager.ts +1 -1
  25. package/core/EntityInterface.ts +3 -1
  26. package/core/EntityManager.ts +1 -13
  27. package/core/ErrorHandler.ts +8 -2
  28. package/core/Logger.ts +9 -0
  29. package/core/Middleware.ts +34 -0
  30. package/core/RequestContext.ts +5 -1
  31. package/core/RequestLoaders.ts +227 -93
  32. package/core/SchedulerManager.ts +193 -52
  33. package/core/cache/CacheAnalytics.ts +399 -0
  34. package/core/cache/CacheFactory.ts +145 -0
  35. package/core/cache/CacheManager.ts +520 -0
  36. package/core/cache/CacheProvider.ts +34 -0
  37. package/core/cache/CacheWarmer.ts +157 -0
  38. package/core/cache/CompressionUtils.ts +110 -0
  39. package/core/cache/MemoryCache.ts +251 -0
  40. package/core/cache/MultiLevelCache.ts +180 -0
  41. package/core/cache/NoOpCache.ts +53 -0
  42. package/core/cache/RedisCache.ts +464 -0
  43. package/core/cache/TTLStrategy.ts +254 -0
  44. package/core/cache/index.ts +6 -0
  45. package/core/components/BaseComponent.ts +120 -0
  46. package/core/{ComponentRegistry.ts → components/ComponentRegistry.ts} +148 -54
  47. package/core/components/Decorators.ts +88 -0
  48. package/core/components/Interfaces.ts +7 -0
  49. package/core/components/index.ts +5 -0
  50. package/core/decorators/EntityHooks.ts +0 -3
  51. package/core/decorators/IndexedField.ts +26 -0
  52. package/core/decorators/ScheduledTask.ts +0 -47
  53. package/core/events/EntityLifecycleEvents.ts +1 -1
  54. package/core/health.ts +112 -0
  55. package/core/metadata/definitions/ArcheType.ts +14 -0
  56. package/core/metadata/definitions/Component.ts +9 -0
  57. package/core/metadata/definitions/gqlObject.ts +1 -1
  58. package/core/metadata/index.ts +42 -1
  59. package/core/metadata/metadata-storage.ts +28 -2
  60. package/core/middleware/AccessLog.ts +59 -0
  61. package/core/middleware/RequestId.ts +38 -0
  62. package/core/middleware/SecurityHeaders.ts +62 -0
  63. package/core/middleware/index.ts +3 -0
  64. package/core/scheduler/DistributedLock.ts +266 -0
  65. package/core/scheduler/index.ts +15 -0
  66. package/core/validateEnv.ts +92 -0
  67. package/database/DatabaseHelper.ts +416 -40
  68. package/database/IndexingStrategy.ts +342 -0
  69. package/database/PreparedStatementCache.ts +226 -0
  70. package/database/index.ts +32 -7
  71. package/database/sqlHelpers.ts +14 -2
  72. package/endpoints/archetypes.ts +362 -0
  73. package/endpoints/components.ts +58 -0
  74. package/endpoints/entity.ts +80 -0
  75. package/endpoints/index.ts +27 -0
  76. package/endpoints/query.ts +93 -0
  77. package/endpoints/stats.ts +76 -0
  78. package/endpoints/tables.ts +212 -0
  79. package/endpoints/types.ts +155 -0
  80. package/gql/ArchetypeOperations.ts +32 -86
  81. package/gql/Generator.ts +27 -315
  82. package/gql/GeneratorV2.ts +37 -0
  83. package/gql/builders/InputTypeBuilder.ts +99 -0
  84. package/gql/builders/ResolverBuilder.ts +234 -0
  85. package/gql/builders/TypeDefBuilder.ts +105 -0
  86. package/gql/builders/index.ts +3 -0
  87. package/gql/decorators/Upload.ts +1 -1
  88. package/gql/depthLimit.ts +85 -0
  89. package/gql/graph/GraphNode.ts +224 -0
  90. package/gql/graph/SchemaGraph.ts +278 -0
  91. package/gql/helpers.ts +8 -2
  92. package/gql/index.ts +56 -4
  93. package/gql/middleware.ts +79 -0
  94. package/gql/orchestration/GraphQLSchemaOrchestrator.ts +241 -0
  95. package/gql/orchestration/index.ts +1 -0
  96. package/gql/scanner/ServiceScanner.ts +347 -0
  97. package/gql/schema/index.ts +458 -0
  98. package/gql/strategies/TypeGenerationStrategy.ts +329 -0
  99. package/gql/types.ts +1 -0
  100. package/gql/utils/TypeSignature.ts +220 -0
  101. package/gql/utils/index.ts +1 -0
  102. package/gql/visitors/ArchetypePreprocessorVisitor.ts +80 -0
  103. package/gql/visitors/DeduplicationVisitor.ts +82 -0
  104. package/gql/visitors/GraphVisitor.ts +78 -0
  105. package/gql/visitors/ResolverGeneratorVisitor.ts +122 -0
  106. package/gql/visitors/SchemaGeneratorVisitor.ts +851 -0
  107. package/gql/visitors/TypeCollectorVisitor.ts +79 -0
  108. package/gql/visitors/VisitorComposer.ts +96 -0
  109. package/gql/visitors/index.ts +7 -0
  110. package/package.json +59 -37
  111. package/plugins/index.ts +2 -2
  112. package/query/CTENode.ts +97 -0
  113. package/query/ComponentInclusionNode.ts +689 -0
  114. package/query/FilterBuilder.ts +127 -0
  115. package/query/FilterBuilderRegistry.ts +202 -0
  116. package/query/OrNode.ts +517 -0
  117. package/query/OrQuery.ts +42 -0
  118. package/query/Query.ts +1022 -0
  119. package/query/QueryContext.ts +170 -0
  120. package/query/QueryDAG.ts +122 -0
  121. package/query/QueryNode.ts +65 -0
  122. package/query/SourceNode.ts +53 -0
  123. package/query/builders/FullTextSearchBuilder.ts +236 -0
  124. package/query/index.ts +21 -0
  125. package/scheduler/index.ts +40 -8
  126. package/service/Service.ts +2 -1
  127. package/service/ServiceRegistry.ts +6 -5
  128. package/{core/storage → storage}/LocalStorageProvider.ts +2 -2
  129. package/storage/S3StorageProvider.ts +316 -0
  130. package/{core/storage → storage}/StorageProvider.ts +7 -3
  131. package/studio/bun.lock +482 -0
  132. package/studio/index.html +13 -0
  133. package/studio/package.json +39 -0
  134. package/studio/postcss.config.js +6 -0
  135. package/studio/src/components/DataTable.tsx +211 -0
  136. package/studio/src/components/Layout.tsx +13 -0
  137. package/studio/src/components/PageContainer.tsx +9 -0
  138. package/studio/src/components/PageHeader.tsx +13 -0
  139. package/studio/src/components/SearchBar.tsx +57 -0
  140. package/studio/src/components/Sidebar.tsx +294 -0
  141. package/studio/src/components/ui/button.tsx +56 -0
  142. package/studio/src/components/ui/checkbox.tsx +26 -0
  143. package/studio/src/components/ui/input.tsx +25 -0
  144. package/studio/src/hooks/useDataTable.ts +131 -0
  145. package/studio/src/index.css +36 -0
  146. package/studio/src/lib/api.ts +186 -0
  147. package/studio/src/lib/utils.ts +13 -0
  148. package/studio/src/main.tsx +17 -0
  149. package/studio/src/pages/ArcheType.tsx +239 -0
  150. package/studio/src/pages/Components.tsx +124 -0
  151. package/studio/src/pages/EntityInspector.tsx +302 -0
  152. package/studio/src/pages/QueryRunner.tsx +246 -0
  153. package/studio/src/pages/Table.tsx +94 -0
  154. package/studio/src/pages/Welcome.tsx +241 -0
  155. package/studio/src/routes.tsx +45 -0
  156. package/studio/src/store/archeTypeSettings.ts +30 -0
  157. package/studio/src/store/studio.ts +65 -0
  158. package/studio/src/utils/columnHelpers.tsx +114 -0
  159. package/studio/studio-instructions.md +81 -0
  160. package/studio/tailwind.config.js +77 -0
  161. package/studio/tsconfig.json +24 -0
  162. package/studio/utils.ts +54 -0
  163. package/studio/vite.config.js +19 -0
  164. package/swagger/generator.ts +1 -1
  165. package/tests/e2e/http.test.ts +126 -0
  166. package/tests/fixtures/archetypes/TestUserArchetype.ts +21 -0
  167. package/tests/fixtures/components/TestOrder.ts +23 -0
  168. package/tests/fixtures/components/TestProduct.ts +23 -0
  169. package/tests/fixtures/components/TestUser.ts +20 -0
  170. package/tests/fixtures/components/index.ts +6 -0
  171. package/tests/graphql/SchemaGeneration.test.ts +90 -0
  172. package/tests/graphql/builders/ResolverBuilder.test.ts +223 -0
  173. package/tests/graphql/builders/TypeDefBuilder.test.ts +153 -0
  174. package/tests/integration/archetype/ArcheType.persistence.test.ts +241 -0
  175. package/tests/integration/cache/CacheInvalidation.test.ts +259 -0
  176. package/tests/integration/entity/Entity.persistence.test.ts +333 -0
  177. package/tests/integration/query/Query.exec.test.ts +523 -0
  178. package/tests/pglite-setup.ts +61 -0
  179. package/tests/setup.ts +164 -0
  180. package/tests/stress/BenchmarkRunner.ts +203 -0
  181. package/tests/stress/DataSeeder.ts +190 -0
  182. package/tests/stress/StressTestReporter.ts +229 -0
  183. package/tests/stress/cursor-perf-test.ts +171 -0
  184. package/tests/stress/fixtures/StressTestComponents.ts +58 -0
  185. package/tests/stress/index.ts +7 -0
  186. package/tests/stress/scenarios/query-benchmarks.test.ts +285 -0
  187. package/tests/unit/BatchLoader.test.ts +82 -0
  188. package/tests/unit/archetype/ArcheType.test.ts +107 -0
  189. package/tests/unit/cache/CacheManager.test.ts +347 -0
  190. package/tests/unit/cache/MemoryCache.test.ts +260 -0
  191. package/tests/unit/cache/RedisCache.test.ts +411 -0
  192. package/tests/unit/entity/Entity.components.test.ts +244 -0
  193. package/tests/unit/entity/Entity.test.ts +345 -0
  194. package/tests/unit/gql/depthLimit.test.ts +203 -0
  195. package/tests/unit/gql/operationMiddleware.test.ts +293 -0
  196. package/tests/unit/health/Health.test.ts +129 -0
  197. package/tests/unit/middleware/AccessLog.test.ts +37 -0
  198. package/tests/unit/middleware/Middleware.test.ts +98 -0
  199. package/tests/unit/middleware/RequestId.test.ts +54 -0
  200. package/tests/unit/middleware/SecurityHeaders.test.ts +66 -0
  201. package/tests/unit/query/FilterBuilder.test.ts +111 -0
  202. package/tests/unit/query/Query.test.ts +308 -0
  203. package/tests/unit/scheduler/DistributedLock.test.ts +274 -0
  204. package/tests/unit/schema/schema-integration.test.ts +426 -0
  205. package/tests/unit/schema/schema.test.ts +580 -0
  206. package/tests/unit/storage/S3StorageProvider.test.ts +571 -0
  207. package/tests/unit/upload/RestUpload.test.ts +267 -0
  208. package/tests/unit/validateEnv.test.ts +82 -0
  209. package/tests/utils/entity-tracker.ts +57 -0
  210. package/tests/utils/index.ts +13 -0
  211. package/tests/utils/test-context.ts +149 -0
  212. package/tsconfig.json +5 -1
  213. package/types/archetype.types.ts +6 -0
  214. package/types/hooks.types.ts +1 -1
  215. package/types/query.types.ts +110 -0
  216. package/types/scheduler.types.ts +68 -7
  217. package/types/upload.types.ts +1 -0
  218. package/{core → upload}/FileValidator.ts +10 -1
  219. package/upload/RestUpload.ts +130 -0
  220. package/{core/components → upload}/UploadComponent.ts +11 -11
  221. package/{core → upload}/UploadManager.ts +3 -3
  222. package/upload/index.ts +23 -7
  223. package/utils/UploadHelper.ts +27 -6
  224. package/utils/cronParser.ts +16 -6
  225. package/.github/workflows/deploy-docs.yml +0 -57
  226. package/core/Components.ts +0 -202
  227. package/core/EntityCache.ts +0 -15
  228. package/core/Query.ts +0 -880
  229. package/docs/README.md +0 -149
  230. package/docs/_coverpage.md +0 -36
  231. package/docs/_sidebar.md +0 -23
  232. package/docs/api/core.md +0 -568
  233. package/docs/api/hooks.md +0 -554
  234. package/docs/api/index.md +0 -222
  235. package/docs/api/query.md +0 -678
  236. package/docs/api/service.md +0 -744
  237. package/docs/core-concepts/archetypes.md +0 -512
  238. package/docs/core-concepts/components.md +0 -498
  239. package/docs/core-concepts/entity.md +0 -314
  240. package/docs/core-concepts/hooks.md +0 -683
  241. package/docs/core-concepts/query.md +0 -588
  242. package/docs/core-concepts/services.md +0 -647
  243. package/docs/examples/code-examples.md +0 -425
  244. package/docs/getting-started.md +0 -337
  245. package/docs/index.html +0 -97
  246. package/tests/bench/insert.bench.ts +0 -60
  247. package/tests/bench/relations.bench.ts +0 -270
  248. package/tests/bench/sorting.bench.ts +0 -416
  249. package/tests/component-hooks-simple.test.ts +0 -117
  250. package/tests/component-hooks.test.ts +0 -1461
  251. package/tests/component.test.ts +0 -339
  252. package/tests/errorHandling.test.ts +0 -155
  253. package/tests/hooks.test.ts +0 -667
  254. package/tests/query-sorting.test.ts +0 -101
  255. package/tests/query.test.ts +0 -81
  256. package/tests/relations.test.ts +0 -170
  257. package/tests/scheduler.test.ts +0 -724
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Custom Filter Builder System for Bunsane Query Framework
3
+ *
4
+ * This module provides the core types and interfaces for extensible query filtering,
5
+ * enabling plugins to register custom filter operators that integrate seamlessly
6
+ * with the DAG-based Query system.
7
+ */
8
+
9
+ import type { QueryFilter } from "./QueryContext";
10
+ import type { QueryContext } from "./QueryContext";
11
+
12
+ /**
13
+ * Result returned by a custom filter builder function
14
+ */
15
+ export interface FilterResult {
16
+ /** The SQL fragment to append to the WHERE clause */
17
+ sql: string;
18
+ /** Number of parameters added to the context by this filter */
19
+ addedParams: number;
20
+ }
21
+
22
+ /**
23
+ * Function signature for custom filter builders
24
+ *
25
+ * @param filter - The filter specification containing field, operator, and value
26
+ * @param alias - The table alias for the component table (e.g., "c" for components)
27
+ * @param context - The query context for parameter management and caching
28
+ * @returns FilterResult containing SQL fragment and parameter count
29
+ */
30
+ export type FilterBuilder = (filter: QueryFilter, alias: string, context: QueryContext) => FilterResult;
31
+
32
+ /**
33
+ * Options for advanced filter builder configuration
34
+ */
35
+ export interface FilterBuilderOptions {
36
+ /** Whether this filter supports LATERAL join optimization */
37
+ supportsLateral?: boolean;
38
+ /** Whether this filter requires database indexes for optimal performance */
39
+ requiresIndex?: boolean;
40
+ /** Complexity score for performance monitoring (0-10, higher = more complex) */
41
+ complexityScore?: number;
42
+ /** Optional validation function for filter values */
43
+ validate?: (filter: QueryFilter) => boolean;
44
+ }
45
+
46
+ /**
47
+ * Build a JSON path expression for nested field access
48
+ *
49
+ * @param field - The field path (e.g., "location.coordinates.latitude")
50
+ * @param alias - The table alias (e.g., "c")
51
+ * @returns PostgreSQL JSON path expression
52
+ *
53
+ * @example
54
+ * buildJSONPath("device.unique_id", "c") // "c.data->'device'->>'unique_id'"
55
+ * buildJSONPath("latitude", "c") // "c.data->>'latitude'"
56
+ */
57
+ export function buildJSONPath(field: string, alias: string): string {
58
+ if (field.includes('.')) {
59
+ const parts = field.split('.');
60
+ const lastPart = parts.pop()!;
61
+ const nestedPath = parts.map(p => `'${p}'`).join('->');
62
+ return `${alias}.data->${nestedPath}->>'${lastPart}'`;
63
+ } else {
64
+ return `${alias}.data->>'${field}'`;
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Compose multiple filter builders into a single builder that applies all conditions
70
+ *
71
+ * This allows chaining multiple custom filters together (e.g., spatial proximity AND full-text search).
72
+ * All builders are executed and their SQL fragments are combined with AND.
73
+ *
74
+ * @param builders - Array of filter builders to compose
75
+ * @returns A composed filter builder function
76
+ *
77
+ * @example
78
+ * const spatialAndTextBuilder = composeFilters([withinDistanceBuilder, fullTextSearchBuilder]);
79
+ * // Results in: (spatial_condition) AND (text_search_condition)
80
+ */
81
+ export function composeFilters(builders: FilterBuilder[]): FilterBuilder {
82
+ if (builders.length === 0) {
83
+ throw new Error('Cannot compose empty array of filter builders');
84
+ }
85
+
86
+ return (filter: QueryFilter, alias: string, context: QueryContext): FilterResult => {
87
+ const conditions: string[] = [];
88
+ let totalParams = 0;
89
+
90
+ for (const builder of builders) {
91
+ const result = builder(filter, alias, context);
92
+ if (result.sql.trim()) {
93
+ conditions.push(`(${result.sql})`);
94
+ }
95
+ totalParams += result.addedParams;
96
+ }
97
+
98
+ return {
99
+ sql: conditions.join(' AND '),
100
+ addedParams: totalParams
101
+ };
102
+ };
103
+ }
104
+
105
+ /**
106
+ * Create a filter builder that adds SQL hints for index usage
107
+ *
108
+ * This wrapper adds PostgreSQL query hints to suggest index usage to the planner.
109
+ * Useful for custom filters that require specific indexes for optimal performance.
110
+ *
111
+ * @param builder - The original filter builder
112
+ * @param indexHint - The index name to hint (e.g., "idx_spatial_location")
113
+ * @returns A filter builder that includes index hints
114
+ *
115
+ * @example
116
+ * const hintedBuilder = withIndexHint(spatialBuilder, 'idx_spatial_location');
117
+ * // Generates: /* INDEX: idx_spatial_location */ (spatial_condition)
118
+ */
119
+ export function withIndexHint(builder: FilterBuilder, indexHint: string): FilterBuilder {
120
+ return (filter: QueryFilter, alias: string, context: QueryContext): FilterResult => {
121
+ const result = builder(filter, alias, context);
122
+ return {
123
+ sql: `/* INDEX: ${indexHint} */ ${result.sql}`,
124
+ addedParams: result.addedParams
125
+ };
126
+ };
127
+ }
@@ -0,0 +1,202 @@
1
+ /**
2
+ * Global Registry for Custom Filter Builders
3
+ *
4
+ * Provides thread-safe registration and lookup of custom filter operators.
5
+ * This registry enables plugins to extend the query system with domain-specific
6
+ * filtering capabilities (e.g., spatial queries, full-text search).
7
+ */
8
+
9
+ import type { FilterBuilder, FilterBuilderOptions } from "./FilterBuilder";
10
+
11
+ /**
12
+ * Registry entry containing the builder function and its options
13
+ */
14
+ interface RegistryEntry {
15
+ builder: FilterBuilder;
16
+ options?: FilterBuilderOptions;
17
+ registeredBy?: string; // Plugin name for debugging
18
+ registeredAt: Date;
19
+ version?: string; // Semantic version for the filter implementation
20
+ }
21
+
22
+ /**
23
+ * Global registry for custom filter builders
24
+ *
25
+ * This class provides a static interface for registering and retrieving
26
+ * custom filter builders. It ensures thread-safe operations during
27
+ * plugin initialization.
28
+ */
29
+ export class FilterBuilderRegistry {
30
+ private static registry: Map<string, RegistryEntry> = new Map();
31
+ private static lock: boolean = false;
32
+
33
+ /**
34
+ * Register a custom filter builder for a specific operator
35
+ *
36
+ * @param operator - The filter operator (e.g., "within_distance", "contains_point")
37
+ * @param builder - The filter builder function
38
+ * @param options - Optional configuration for the filter builder
39
+ * @param pluginName - Name of the plugin registering this builder (for debugging)
40
+ * @param version - Optional semantic version for the filter implementation (allows upgrades)
41
+ * @throws Error if the operator is already registered by a different plugin (unless version allows override)
42
+ */
43
+ public static register(
44
+ operator: string,
45
+ builder: FilterBuilder,
46
+ options?: FilterBuilderOptions,
47
+ pluginName?: string,
48
+ version?: string
49
+ ): void {
50
+ // Simple lock mechanism for thread safety
51
+ while (this.lock) {
52
+ // Busy wait - in practice, this should be very short
53
+ // Consider using a proper mutex in high-concurrency environments
54
+ }
55
+
56
+ this.lock = true;
57
+
58
+ try {
59
+ const existing = this.registry.get(operator);
60
+
61
+ if (existing) {
62
+ // Allow override if same plugin or if version is newer
63
+ const canOverride =
64
+ (pluginName && existing.registeredBy === pluginName) ||
65
+ (version && existing.version && this.isNewerVersion(version, existing.version));
66
+
67
+ if (!canOverride) {
68
+ throw new Error(`Filter operator '${operator}' is already registered by '${existing.registeredBy || 'unknown'}' (v${existing.version || 'unknown'}). ` +
69
+ `Cannot register from '${pluginName || 'unknown'}' (v${version || 'unknown'}) without version upgrade.`);
70
+ }
71
+ }
72
+
73
+ this.registry.set(operator, {
74
+ builder,
75
+ options,
76
+ registeredBy: pluginName,
77
+ registeredAt: new Date(),
78
+ version
79
+ });
80
+ } finally {
81
+ this.lock = false;
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Check if a filter operator has a custom builder registered
87
+ *
88
+ * @param operator - The filter operator to check
89
+ * @returns true if a custom builder is registered for this operator
90
+ */
91
+ public static has(operator: string): boolean {
92
+ return this.registry.has(operator);
93
+ }
94
+
95
+ /**
96
+ * Get the custom filter builder for a specific operator
97
+ *
98
+ * @param operator - The filter operator
99
+ * @returns The filter builder function, or undefined if not registered
100
+ */
101
+ public static get(operator: string): FilterBuilder | undefined {
102
+ return this.registry.get(operator)?.builder;
103
+ }
104
+
105
+ /**
106
+ * Get the options for a registered filter builder
107
+ *
108
+ * @param operator - The filter operator
109
+ * @returns The filter builder options, or undefined if not registered
110
+ */
111
+ public static getOptions(operator: string): FilterBuilderOptions | undefined {
112
+ return this.registry.get(operator)?.options;
113
+ }
114
+
115
+ /**
116
+ * Unregister a custom filter builder
117
+ *
118
+ * @param operator - The filter operator to remove
119
+ * @returns true if the operator was registered and removed, false otherwise
120
+ */
121
+ public static unregister(operator: string): boolean {
122
+ // Simple lock mechanism
123
+ while (this.lock) {
124
+ // Busy wait
125
+ }
126
+
127
+ this.lock = true;
128
+
129
+ try {
130
+ return this.registry.delete(operator);
131
+ } finally {
132
+ this.lock = false;
133
+ }
134
+ }
135
+
136
+ /**
137
+ * List all registered filter operators with their metadata
138
+ *
139
+ * @returns Array of registered operators with metadata
140
+ */
141
+ public static listRegistered(): Array<{
142
+ operator: string;
143
+ options?: FilterBuilderOptions;
144
+ registeredBy?: string;
145
+ registeredAt: Date;
146
+ version?: string;
147
+ }> {
148
+ return Array.from(this.registry.entries()).map(([operator, entry]) => ({
149
+ operator,
150
+ options: entry.options,
151
+ registeredBy: entry.registeredBy,
152
+ registeredAt: entry.registeredAt,
153
+ version: entry.version
154
+ }));
155
+ }
156
+
157
+ /**
158
+ * Clear all registered filter builders (for testing purposes)
159
+ */
160
+ public static clear(): void {
161
+ // Simple lock mechanism
162
+ while (this.lock) {
163
+ // Busy wait
164
+ }
165
+
166
+ this.lock = true;
167
+
168
+ try {
169
+ this.registry.clear();
170
+ } finally {
171
+ this.lock = false;
172
+ }
173
+ }
174
+
175
+ /**
176
+ * Compare two semantic versions to determine if the first is newer
177
+ *
178
+ * @param newVersion - The new version string
179
+ * @param oldVersion - The old version string
180
+ * @returns true if newVersion is semantically newer than oldVersion
181
+ */
182
+ private static isNewerVersion(newVersion: string, oldVersion: string): boolean {
183
+ try {
184
+ const newParts = newVersion.split('.').map(n => parseInt(n, 10));
185
+ const oldParts = oldVersion.split('.').map(n => parseInt(n, 10));
186
+
187
+ // Compare major, minor, patch versions
188
+ for (let i = 0; i < Math.max(newParts.length, oldParts.length); i++) {
189
+ const newPart = newParts[i] || 0;
190
+ const oldPart = oldParts[i] || 0;
191
+
192
+ if (newPart > oldPart) return true;
193
+ if (newPart < oldPart) return false;
194
+ }
195
+
196
+ return false; // Same version
197
+ } catch {
198
+ // If parsing fails, fall back to string comparison
199
+ return newVersion > oldVersion;
200
+ }
201
+ }
202
+ }