@objectstack/core 0.9.1 → 1.0.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 (94) hide show
  1. package/{ENHANCED_FEATURES.md → ADVANCED_FEATURES.md} +13 -13
  2. package/CHANGELOG.md +21 -0
  3. package/PHASE2_IMPLEMENTATION.md +388 -0
  4. package/README.md +12 -341
  5. package/REFACTORING_SUMMARY.md +40 -0
  6. package/dist/api-registry-plugin.test.js +23 -21
  7. package/dist/api-registry.test.js +2 -2
  8. package/dist/dependency-resolver.d.ts +62 -0
  9. package/dist/dependency-resolver.d.ts.map +1 -0
  10. package/dist/dependency-resolver.js +317 -0
  11. package/dist/dependency-resolver.test.d.ts +2 -0
  12. package/dist/dependency-resolver.test.d.ts.map +1 -0
  13. package/dist/dependency-resolver.test.js +241 -0
  14. package/dist/health-monitor.d.ts +65 -0
  15. package/dist/health-monitor.d.ts.map +1 -0
  16. package/dist/health-monitor.js +269 -0
  17. package/dist/health-monitor.test.d.ts +2 -0
  18. package/dist/health-monitor.test.d.ts.map +1 -0
  19. package/dist/health-monitor.test.js +68 -0
  20. package/dist/hot-reload.d.ts +79 -0
  21. package/dist/hot-reload.d.ts.map +1 -0
  22. package/dist/hot-reload.js +313 -0
  23. package/dist/index.d.ts +4 -1
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +5 -1
  26. package/dist/kernel-base.d.ts +2 -2
  27. package/dist/kernel-base.js +2 -2
  28. package/dist/kernel.d.ts +89 -31
  29. package/dist/kernel.d.ts.map +1 -1
  30. package/dist/kernel.js +430 -73
  31. package/dist/kernel.test.js +375 -122
  32. package/dist/lite-kernel.d.ts +55 -0
  33. package/dist/lite-kernel.d.ts.map +1 -0
  34. package/dist/lite-kernel.js +112 -0
  35. package/dist/lite-kernel.test.d.ts +2 -0
  36. package/dist/lite-kernel.test.d.ts.map +1 -0
  37. package/dist/lite-kernel.test.js +161 -0
  38. package/dist/logger.d.ts +2 -2
  39. package/dist/logger.d.ts.map +1 -1
  40. package/dist/logger.js +26 -7
  41. package/dist/plugin-loader.d.ts +15 -0
  42. package/dist/plugin-loader.d.ts.map +1 -1
  43. package/dist/plugin-loader.js +40 -10
  44. package/dist/plugin-loader.test.js +9 -0
  45. package/dist/security/index.d.ts +3 -0
  46. package/dist/security/index.d.ts.map +1 -1
  47. package/dist/security/index.js +4 -0
  48. package/dist/security/permission-manager.d.ts +96 -0
  49. package/dist/security/permission-manager.d.ts.map +1 -0
  50. package/dist/security/permission-manager.js +235 -0
  51. package/dist/security/permission-manager.test.d.ts +2 -0
  52. package/dist/security/permission-manager.test.d.ts.map +1 -0
  53. package/dist/security/permission-manager.test.js +220 -0
  54. package/dist/security/plugin-permission-enforcer.d.ts +1 -1
  55. package/dist/security/sandbox-runtime.d.ts +115 -0
  56. package/dist/security/sandbox-runtime.d.ts.map +1 -0
  57. package/dist/security/sandbox-runtime.js +310 -0
  58. package/dist/security/security-scanner.d.ts +92 -0
  59. package/dist/security/security-scanner.d.ts.map +1 -0
  60. package/dist/security/security-scanner.js +273 -0
  61. package/examples/{enhanced-kernel-example.ts → kernel-features-example.ts} +6 -6
  62. package/examples/phase2-integration.ts +355 -0
  63. package/package.json +3 -2
  64. package/src/api-registry-plugin.test.ts +23 -21
  65. package/src/api-registry.test.ts +2 -2
  66. package/src/dependency-resolver.test.ts +287 -0
  67. package/src/dependency-resolver.ts +388 -0
  68. package/src/health-monitor.test.ts +81 -0
  69. package/src/health-monitor.ts +316 -0
  70. package/src/hot-reload.ts +388 -0
  71. package/src/index.ts +6 -1
  72. package/src/kernel-base.ts +2 -2
  73. package/src/kernel.test.ts +471 -134
  74. package/src/kernel.ts +518 -76
  75. package/src/lite-kernel.test.ts +200 -0
  76. package/src/lite-kernel.ts +135 -0
  77. package/src/logger.ts +28 -7
  78. package/src/plugin-loader.test.ts +10 -1
  79. package/src/plugin-loader.ts +49 -13
  80. package/src/security/index.ts +19 -0
  81. package/src/security/permission-manager.test.ts +256 -0
  82. package/src/security/permission-manager.ts +336 -0
  83. package/src/security/plugin-permission-enforcer.test.ts +1 -1
  84. package/src/security/plugin-permission-enforcer.ts +1 -1
  85. package/src/security/sandbox-runtime.ts +432 -0
  86. package/src/security/security-scanner.ts +365 -0
  87. package/dist/enhanced-kernel.d.ts +0 -103
  88. package/dist/enhanced-kernel.d.ts.map +0 -1
  89. package/dist/enhanced-kernel.js +0 -403
  90. package/dist/enhanced-kernel.test.d.ts +0 -2
  91. package/dist/enhanced-kernel.test.d.ts.map +0 -1
  92. package/dist/enhanced-kernel.test.js +0 -412
  93. package/src/enhanced-kernel.test.ts +0 -535
  94. package/src/enhanced-kernel.ts +0 -496
@@ -0,0 +1,269 @@
1
+ /**
2
+ * Plugin Health Monitor
3
+ *
4
+ * Monitors plugin health status and performs automatic recovery actions.
5
+ * Implements the advanced lifecycle health monitoring protocol.
6
+ */
7
+ export class PluginHealthMonitor {
8
+ constructor(logger) {
9
+ this.healthChecks = new Map();
10
+ this.healthStatus = new Map();
11
+ this.healthReports = new Map();
12
+ this.checkIntervals = new Map();
13
+ this.failureCounters = new Map();
14
+ this.successCounters = new Map();
15
+ this.restartAttempts = new Map();
16
+ this.logger = logger.child({ component: 'HealthMonitor' });
17
+ }
18
+ /**
19
+ * Register a plugin for health monitoring
20
+ */
21
+ registerPlugin(pluginName, config) {
22
+ this.healthChecks.set(pluginName, config);
23
+ this.healthStatus.set(pluginName, 'unknown');
24
+ this.failureCounters.set(pluginName, 0);
25
+ this.successCounters.set(pluginName, 0);
26
+ this.restartAttempts.set(pluginName, 0);
27
+ this.logger.info('Plugin registered for health monitoring', {
28
+ plugin: pluginName,
29
+ interval: config.interval
30
+ });
31
+ }
32
+ /**
33
+ * Start monitoring a plugin
34
+ */
35
+ startMonitoring(pluginName, plugin) {
36
+ const config = this.healthChecks.get(pluginName);
37
+ if (!config) {
38
+ this.logger.warn('Cannot start monitoring - plugin not registered', { plugin: pluginName });
39
+ return;
40
+ }
41
+ // Clear any existing interval
42
+ this.stopMonitoring(pluginName);
43
+ // Set up periodic health checks
44
+ const interval = setInterval(() => {
45
+ this.performHealthCheck(pluginName, plugin, config).catch(error => {
46
+ this.logger.error('Health check failed with error', {
47
+ plugin: pluginName,
48
+ error
49
+ });
50
+ });
51
+ }, config.interval);
52
+ this.checkIntervals.set(pluginName, interval);
53
+ this.logger.info('Health monitoring started', { plugin: pluginName });
54
+ // Perform initial health check
55
+ this.performHealthCheck(pluginName, plugin, config).catch(error => {
56
+ this.logger.error('Initial health check failed', {
57
+ plugin: pluginName,
58
+ error
59
+ });
60
+ });
61
+ }
62
+ /**
63
+ * Stop monitoring a plugin
64
+ */
65
+ stopMonitoring(pluginName) {
66
+ const interval = this.checkIntervals.get(pluginName);
67
+ if (interval) {
68
+ clearInterval(interval);
69
+ this.checkIntervals.delete(pluginName);
70
+ this.logger.info('Health monitoring stopped', { plugin: pluginName });
71
+ }
72
+ }
73
+ /**
74
+ * Perform a health check on a plugin
75
+ */
76
+ async performHealthCheck(pluginName, plugin, config) {
77
+ const startTime = Date.now();
78
+ let status = 'healthy';
79
+ let message;
80
+ const checks = [];
81
+ try {
82
+ // Check if plugin has a custom health check method
83
+ if (config.checkMethod && typeof plugin[config.checkMethod] === 'function') {
84
+ const checkResult = await Promise.race([
85
+ plugin[config.checkMethod](),
86
+ this.timeout(config.timeout, `Health check timeout after ${config.timeout}ms`)
87
+ ]);
88
+ if (checkResult === false || (checkResult && checkResult.status === 'unhealthy')) {
89
+ status = 'unhealthy';
90
+ message = checkResult?.message || 'Custom health check failed';
91
+ checks.push({ name: config.checkMethod, status: 'failed', message });
92
+ }
93
+ else {
94
+ checks.push({ name: config.checkMethod, status: 'passed' });
95
+ }
96
+ }
97
+ else {
98
+ // Default health check - just verify plugin is loaded
99
+ checks.push({ name: 'plugin-loaded', status: 'passed' });
100
+ }
101
+ // Update counters based on result
102
+ if (status === 'healthy') {
103
+ this.successCounters.set(pluginName, (this.successCounters.get(pluginName) || 0) + 1);
104
+ this.failureCounters.set(pluginName, 0);
105
+ // Recover from unhealthy state if we have enough successes
106
+ const currentStatus = this.healthStatus.get(pluginName);
107
+ if (currentStatus === 'unhealthy' || currentStatus === 'degraded') {
108
+ const successCount = this.successCounters.get(pluginName) || 0;
109
+ if (successCount >= config.successThreshold) {
110
+ this.healthStatus.set(pluginName, 'healthy');
111
+ this.logger.info('Plugin recovered to healthy state', { plugin: pluginName });
112
+ }
113
+ else {
114
+ this.healthStatus.set(pluginName, 'recovering');
115
+ }
116
+ }
117
+ else {
118
+ this.healthStatus.set(pluginName, 'healthy');
119
+ }
120
+ }
121
+ else {
122
+ this.failureCounters.set(pluginName, (this.failureCounters.get(pluginName) || 0) + 1);
123
+ this.successCounters.set(pluginName, 0);
124
+ const failureCount = this.failureCounters.get(pluginName) || 0;
125
+ if (failureCount >= config.failureThreshold) {
126
+ this.healthStatus.set(pluginName, 'unhealthy');
127
+ this.logger.warn('Plugin marked as unhealthy', {
128
+ plugin: pluginName,
129
+ failures: failureCount
130
+ });
131
+ // Attempt auto-restart if configured
132
+ if (config.autoRestart) {
133
+ await this.attemptRestart(pluginName, plugin, config);
134
+ }
135
+ }
136
+ else {
137
+ this.healthStatus.set(pluginName, 'degraded');
138
+ }
139
+ }
140
+ }
141
+ catch (error) {
142
+ status = 'failed';
143
+ message = error instanceof Error ? error.message : 'Unknown error';
144
+ this.failureCounters.set(pluginName, (this.failureCounters.get(pluginName) || 0) + 1);
145
+ this.healthStatus.set(pluginName, 'failed');
146
+ checks.push({
147
+ name: 'health-check',
148
+ status: 'failed',
149
+ message: message
150
+ });
151
+ this.logger.error('Health check exception', {
152
+ plugin: pluginName,
153
+ error
154
+ });
155
+ }
156
+ // Create health report
157
+ const report = {
158
+ status: this.healthStatus.get(pluginName) || 'unknown',
159
+ timestamp: new Date().toISOString(),
160
+ message,
161
+ metrics: {
162
+ uptime: Date.now() - startTime,
163
+ },
164
+ checks: checks.length > 0 ? checks : undefined,
165
+ };
166
+ this.healthReports.set(pluginName, report);
167
+ }
168
+ /**
169
+ * Attempt to restart a plugin
170
+ */
171
+ async attemptRestart(pluginName, plugin, config) {
172
+ const attempts = this.restartAttempts.get(pluginName) || 0;
173
+ if (attempts >= config.maxRestartAttempts) {
174
+ this.logger.error('Max restart attempts reached, giving up', {
175
+ plugin: pluginName,
176
+ attempts
177
+ });
178
+ this.healthStatus.set(pluginName, 'failed');
179
+ return;
180
+ }
181
+ this.restartAttempts.set(pluginName, attempts + 1);
182
+ // Calculate backoff delay
183
+ const delay = this.calculateBackoff(attempts, config.restartBackoff);
184
+ this.logger.info('Scheduling plugin restart', {
185
+ plugin: pluginName,
186
+ attempt: attempts + 1,
187
+ delay
188
+ });
189
+ await new Promise(resolve => setTimeout(resolve, delay));
190
+ try {
191
+ // Call destroy and init to restart
192
+ if (plugin.destroy) {
193
+ await plugin.destroy();
194
+ }
195
+ // Note: Full restart would require kernel context
196
+ // This is a simplified version - actual implementation would need kernel integration
197
+ this.logger.info('Plugin restarted', { plugin: pluginName });
198
+ // Reset counters on successful restart
199
+ this.failureCounters.set(pluginName, 0);
200
+ this.successCounters.set(pluginName, 0);
201
+ this.healthStatus.set(pluginName, 'recovering');
202
+ }
203
+ catch (error) {
204
+ this.logger.error('Plugin restart failed', {
205
+ plugin: pluginName,
206
+ error
207
+ });
208
+ this.healthStatus.set(pluginName, 'failed');
209
+ }
210
+ }
211
+ /**
212
+ * Calculate backoff delay for restarts
213
+ */
214
+ calculateBackoff(attempt, strategy) {
215
+ const baseDelay = 1000; // 1 second base
216
+ switch (strategy) {
217
+ case 'fixed':
218
+ return baseDelay;
219
+ case 'linear':
220
+ return baseDelay * (attempt + 1);
221
+ case 'exponential':
222
+ return baseDelay * Math.pow(2, attempt);
223
+ default:
224
+ return baseDelay;
225
+ }
226
+ }
227
+ /**
228
+ * Get current health status of a plugin
229
+ */
230
+ getHealthStatus(pluginName) {
231
+ return this.healthStatus.get(pluginName);
232
+ }
233
+ /**
234
+ * Get latest health report for a plugin
235
+ */
236
+ getHealthReport(pluginName) {
237
+ return this.healthReports.get(pluginName);
238
+ }
239
+ /**
240
+ * Get all health statuses
241
+ */
242
+ getAllHealthStatuses() {
243
+ return new Map(this.healthStatus);
244
+ }
245
+ /**
246
+ * Shutdown health monitor
247
+ */
248
+ shutdown() {
249
+ // Stop all monitoring intervals
250
+ for (const pluginName of this.checkIntervals.keys()) {
251
+ this.stopMonitoring(pluginName);
252
+ }
253
+ this.healthChecks.clear();
254
+ this.healthStatus.clear();
255
+ this.healthReports.clear();
256
+ this.failureCounters.clear();
257
+ this.successCounters.clear();
258
+ this.restartAttempts.clear();
259
+ this.logger.info('Health monitor shutdown complete');
260
+ }
261
+ /**
262
+ * Timeout helper
263
+ */
264
+ timeout(ms, message) {
265
+ return new Promise((_, reject) => {
266
+ setTimeout(() => reject(new Error(message)), ms);
267
+ });
268
+ }
269
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=health-monitor.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health-monitor.test.d.ts","sourceRoot":"","sources":["../src/health-monitor.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,68 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest';
2
+ import { PluginHealthMonitor } from './health-monitor.js';
3
+ import { createLogger } from './logger.js';
4
+ describe('PluginHealthMonitor', () => {
5
+ let monitor;
6
+ let logger;
7
+ beforeEach(() => {
8
+ logger = createLogger({ level: 'silent' });
9
+ monitor = new PluginHealthMonitor(logger);
10
+ });
11
+ it('should register plugin for health monitoring', () => {
12
+ const config = {
13
+ interval: 5000,
14
+ timeout: 1000,
15
+ failureThreshold: 3,
16
+ successThreshold: 1,
17
+ autoRestart: false,
18
+ maxRestartAttempts: 3,
19
+ restartBackoff: 'exponential',
20
+ };
21
+ monitor.registerPlugin('test-plugin', config);
22
+ expect(monitor.getHealthStatus('test-plugin')).toBe('unknown');
23
+ });
24
+ it('should report healthy status initially', () => {
25
+ const config = {
26
+ interval: 5000,
27
+ timeout: 1000,
28
+ failureThreshold: 3,
29
+ successThreshold: 1,
30
+ autoRestart: false,
31
+ maxRestartAttempts: 3,
32
+ restartBackoff: 'fixed',
33
+ };
34
+ monitor.registerPlugin('test-plugin', config);
35
+ expect(monitor.getHealthStatus('test-plugin')).toBe('unknown');
36
+ });
37
+ it('should get all health statuses', () => {
38
+ const config = {
39
+ interval: 5000,
40
+ timeout: 1000,
41
+ failureThreshold: 3,
42
+ successThreshold: 1,
43
+ autoRestart: false,
44
+ maxRestartAttempts: 3,
45
+ restartBackoff: 'linear',
46
+ };
47
+ monitor.registerPlugin('plugin1', config);
48
+ monitor.registerPlugin('plugin2', config);
49
+ const statuses = monitor.getAllHealthStatuses();
50
+ expect(statuses.size).toBe(2);
51
+ expect(statuses.has('plugin1')).toBe(true);
52
+ expect(statuses.has('plugin2')).toBe(true);
53
+ });
54
+ it('should shutdown cleanly', () => {
55
+ const config = {
56
+ interval: 5000,
57
+ timeout: 1000,
58
+ failureThreshold: 3,
59
+ successThreshold: 1,
60
+ autoRestart: false,
61
+ maxRestartAttempts: 3,
62
+ restartBackoff: 'exponential',
63
+ };
64
+ monitor.registerPlugin('test-plugin', config);
65
+ monitor.shutdown();
66
+ expect(monitor.getAllHealthStatuses().size).toBe(0);
67
+ });
68
+ });
@@ -0,0 +1,79 @@
1
+ import type { HotReloadConfig } from '@objectstack/spec/kernel';
2
+ import type { ObjectLogger } from './logger.js';
3
+ import type { Plugin } from './types.js';
4
+ /**
5
+ * Plugin State Manager
6
+ *
7
+ * Handles state persistence and restoration during hot reloads
8
+ */
9
+ declare class PluginStateManager {
10
+ private logger;
11
+ private stateSnapshots;
12
+ private memoryStore;
13
+ constructor(logger: ObjectLogger);
14
+ /**
15
+ * Save plugin state before reload
16
+ */
17
+ saveState(pluginId: string, version: string, state: Record<string, any>, config: HotReloadConfig): Promise<string>;
18
+ /**
19
+ * Restore plugin state after reload
20
+ */
21
+ restoreState(pluginId: string, snapshotId?: string): Promise<Record<string, any> | undefined>;
22
+ /**
23
+ * Clear state for a plugin
24
+ */
25
+ clearState(pluginId: string): void;
26
+ /**
27
+ * Calculate simple checksum for state verification
28
+ * WARNING: This is a simple hash for demo purposes.
29
+ * In production, use a cryptographic hash like SHA-256.
30
+ */
31
+ private calculateChecksum;
32
+ /**
33
+ * Shutdown state manager
34
+ */
35
+ shutdown(): void;
36
+ }
37
+ /**
38
+ * Hot Reload Manager
39
+ *
40
+ * Manages hot reloading of plugins with state preservation
41
+ */
42
+ export declare class HotReloadManager {
43
+ private logger;
44
+ private stateManager;
45
+ private reloadConfigs;
46
+ private watchHandles;
47
+ private reloadTimers;
48
+ constructor(logger: ObjectLogger);
49
+ /**
50
+ * Register a plugin for hot reload
51
+ */
52
+ registerPlugin(pluginName: string, config: HotReloadConfig): void;
53
+ /**
54
+ * Start watching for changes (requires file system integration)
55
+ */
56
+ startWatching(pluginName: string): void;
57
+ /**
58
+ * Stop watching for changes
59
+ */
60
+ stopWatching(pluginName: string): void;
61
+ /**
62
+ * Trigger hot reload for a plugin
63
+ */
64
+ reloadPlugin(pluginName: string, plugin: Plugin, version: string, getPluginState: () => Record<string, any>, restorePluginState: (state: Record<string, any>) => void): Promise<boolean>;
65
+ /**
66
+ * Schedule a reload with debouncing
67
+ */
68
+ scheduleReload(pluginName: string, reloadFn: () => Promise<void>): void;
69
+ /**
70
+ * Get state manager for direct access
71
+ */
72
+ getStateManager(): PluginStateManager;
73
+ /**
74
+ * Shutdown hot reload manager
75
+ */
76
+ shutdown(): void;
77
+ }
78
+ export {};
79
+ //# sourceMappingURL=hot-reload.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hot-reload.d.ts","sourceRoot":"","sources":["../src/hot-reload.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EAEhB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAezC;;;;GAIG;AACH,cAAM,kBAAkB;IACtB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,cAAc,CAA0C;IAChE,OAAO,CAAC,WAAW,CAA0B;gBAEjC,MAAM,EAAE,YAAY;IAIhC;;OAEG;IACG,SAAS,CACb,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC1B,MAAM,EAAE,eAAe,GACtB,OAAO,CAAC,MAAM,CAAC;IA8ClB;;OAEG;IACG,YAAY,CAChB,QAAQ,EAAE,MAAM,EAChB,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CAAC;IAgC3C;;OAEG;IACH,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAMlC;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IAazB;;OAEG;IACH,QAAQ,IAAI,IAAI;CAKjB;AAED;;;;GAIG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,aAAa,CAAsC;IAC3D,OAAO,CAAC,YAAY,CAA0B;IAC9C,OAAO,CAAC,YAAY,CAAqC;gBAE7C,MAAM,EAAE,YAAY;IAKhC;;OAEG;IACH,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI;IAcjE;;OAEG;IACH,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAcvC;;OAEG;IACH,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAgBtC;;OAEG;IACG,YAAY,CAChB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,cAAc,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACzC,kBAAkB,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,IAAI,GACvD,OAAO,CAAC,OAAO,CAAC;IA8EnB;;OAEG;IACH,cAAc,CACZ,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAC5B,IAAI;IAiCP;;OAEG;IACH,eAAe,IAAI,kBAAkB;IAIrC;;OAEG;IACH,QAAQ,IAAI,IAAI;CAkBjB"}