@objectstack/core 4.0.3 → 4.0.5

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 (75) hide show
  1. package/README.md +95 -10
  2. package/dist/index.cjs +169 -507
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +24 -223
  5. package/dist/index.d.ts +24 -223
  6. package/dist/index.js +175 -505
  7. package/dist/index.js.map +1 -1
  8. package/dist/logger.cjs +177 -0
  9. package/dist/logger.cjs.map +1 -0
  10. package/dist/logger.d.cts +26 -0
  11. package/dist/logger.d.ts +26 -0
  12. package/dist/logger.js +158 -0
  13. package/dist/logger.js.map +1 -0
  14. package/package.json +36 -15
  15. package/.turbo/turbo-build.log +0 -22
  16. package/ADVANCED_FEATURES.md +0 -380
  17. package/API_REGISTRY.md +0 -392
  18. package/CHANGELOG.md +0 -465
  19. package/PHASE2_IMPLEMENTATION.md +0 -388
  20. package/REFACTORING_SUMMARY.md +0 -40
  21. package/examples/api-registry-example.ts +0 -559
  22. package/examples/kernel-features-example.ts +0 -311
  23. package/examples/phase2-integration.ts +0 -357
  24. package/src/api-registry-plugin.test.ts +0 -393
  25. package/src/api-registry-plugin.ts +0 -89
  26. package/src/api-registry.test.ts +0 -1089
  27. package/src/api-registry.ts +0 -739
  28. package/src/contracts/data-engine.ts +0 -57
  29. package/src/contracts/http-server.ts +0 -151
  30. package/src/contracts/logger.ts +0 -72
  31. package/src/dependency-resolver.test.ts +0 -287
  32. package/src/dependency-resolver.ts +0 -390
  33. package/src/fallbacks/fallbacks.test.ts +0 -281
  34. package/src/fallbacks/index.ts +0 -26
  35. package/src/fallbacks/memory-cache.ts +0 -34
  36. package/src/fallbacks/memory-i18n.ts +0 -112
  37. package/src/fallbacks/memory-job.ts +0 -23
  38. package/src/fallbacks/memory-metadata.ts +0 -50
  39. package/src/fallbacks/memory-queue.ts +0 -28
  40. package/src/health-monitor.test.ts +0 -81
  41. package/src/health-monitor.ts +0 -318
  42. package/src/hot-reload.ts +0 -382
  43. package/src/index.ts +0 -50
  44. package/src/kernel-base.ts +0 -273
  45. package/src/kernel.test.ts +0 -624
  46. package/src/kernel.ts +0 -631
  47. package/src/lite-kernel.test.ts +0 -248
  48. package/src/lite-kernel.ts +0 -137
  49. package/src/logger.test.ts +0 -116
  50. package/src/logger.ts +0 -355
  51. package/src/namespace-resolver.test.ts +0 -130
  52. package/src/namespace-resolver.ts +0 -188
  53. package/src/package-manager.test.ts +0 -225
  54. package/src/package-manager.ts +0 -428
  55. package/src/plugin-loader.test.ts +0 -421
  56. package/src/plugin-loader.ts +0 -484
  57. package/src/qa/adapter.ts +0 -16
  58. package/src/qa/http-adapter.ts +0 -116
  59. package/src/qa/index.ts +0 -5
  60. package/src/qa/runner.ts +0 -189
  61. package/src/security/index.ts +0 -50
  62. package/src/security/permission-manager.test.ts +0 -256
  63. package/src/security/permission-manager.ts +0 -338
  64. package/src/security/plugin-config-validator.test.ts +0 -276
  65. package/src/security/plugin-config-validator.ts +0 -193
  66. package/src/security/plugin-permission-enforcer.test.ts +0 -251
  67. package/src/security/plugin-permission-enforcer.ts +0 -436
  68. package/src/security/plugin-signature-verifier.ts +0 -403
  69. package/src/security/sandbox-runtime.ts +0 -462
  70. package/src/security/security-scanner.ts +0 -367
  71. package/src/types.ts +0 -120
  72. package/src/utils/env.test.ts +0 -62
  73. package/src/utils/env.ts +0 -53
  74. package/tsconfig.json +0 -10
  75. package/vitest.config.ts +0 -10
@@ -1,57 +0,0 @@
1
- // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
-
3
- import {
4
- EngineQueryOptions,
5
- DataEngineInsertOptions,
6
- EngineUpdateOptions,
7
- EngineDeleteOptions,
8
- EngineAggregateOptions,
9
- EngineCountOptions,
10
- DataEngineRequest,
11
- } from '@objectstack/spec/data';
12
-
13
- /**
14
- * IDataEngine - Standard Data Engine Interface
15
- *
16
- * Abstract interface for data persistence capabilities.
17
- * Following the Dependency Inversion Principle - plugins depend on this interface,
18
- * not on concrete database implementations.
19
- *
20
- * All query methods use standard QueryAST parameter names
21
- * (where/fields/orderBy/limit/offset/expand) to eliminate mechanical translation
22
- * between the Engine and Driver layers.
23
- *
24
- * Aligned with 'src/data/data-engine.zod.ts' in @objectstack/spec.
25
- */
26
-
27
- export interface IDataEngine {
28
- find(objectName: string, query?: EngineQueryOptions): Promise<any[]>;
29
- findOne(objectName: string, query?: EngineQueryOptions): Promise<any>;
30
- insert(objectName: string, data: any | any[], options?: DataEngineInsertOptions): Promise<any>;
31
- update(objectName: string, data: any, options?: EngineUpdateOptions): Promise<any>;
32
- delete(objectName: string, options?: EngineDeleteOptions): Promise<any>;
33
- count(objectName: string, query?: EngineCountOptions): Promise<number>;
34
- aggregate(objectName: string, query: EngineAggregateOptions): Promise<any[]>;
35
-
36
- /**
37
- * Vector Search (AI/RAG)
38
- */
39
- vectorFind?(objectName: string, vector: number[], options?: { where?: any, limit?: number, fields?: string[], threshold?: number }): Promise<any[]>;
40
-
41
- /**
42
- * Batch Operations (Transactional)
43
- */
44
- batch?(requests: DataEngineRequest[], options?: { transaction?: boolean }): Promise<any[]>;
45
-
46
- /**
47
- * Execute raw command (Escape hatch)
48
- */
49
- execute?(command: any, options?: Record<string, any>): Promise<any>;
50
- }
51
-
52
- /**
53
- * @deprecated Use `IDataDriver` from `@objectstack/spec/contracts` instead.
54
- * This type is re-exported from `@objectstack/spec/contracts` for backward compatibility only.
55
- */
56
- export type { DriverInterface } from '@objectstack/spec/contracts';
57
-
@@ -1,151 +0,0 @@
1
- // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
-
3
- /**
4
- * IHttpServer - Standard HTTP Server Interface
5
- *
6
- * Abstract interface for HTTP server capabilities.
7
- * This allows plugins to interact with HTTP servers without knowing
8
- * the underlying implementation (Express, Fastify, Hono, etc.).
9
- *
10
- * Follows Dependency Inversion Principle - plugins depend on this interface,
11
- * not on concrete HTTP framework implementations.
12
- */
13
-
14
- // We use Zod for validation but export interfaces for internal implementation
15
-
16
- /**
17
- * Generic HTTP Request type
18
- * Abstraction over framework-specific request objects
19
- */
20
- export interface IHttpRequest {
21
- /** Request path parameters */
22
- params: Record<string, string>;
23
- /** Request query parameters */
24
- query: Record<string, string | string[]>;
25
- /** Request body */
26
- body?: any;
27
- /** Request headers */
28
- headers: Record<string, string | string[]>;
29
- /** HTTP method */
30
- method: string;
31
- /** Request path */
32
- path: string;
33
- }
34
-
35
- /**
36
- * Generic HTTP Response type
37
- * Abstraction over framework-specific response objects
38
- */
39
- export interface IHttpResponse {
40
- /**
41
- * Send a JSON response
42
- * @param data - Data to send
43
- */
44
- json(data: any): void | Promise<void>;
45
-
46
- /**
47
- * Send a text/html response
48
- * @param data - Data to send
49
- */
50
- send(data: string): void | Promise<void>;
51
-
52
- /**
53
- * Set HTTP status code
54
- * @param code - HTTP status code
55
- */
56
- status(code: number): IHttpResponse;
57
-
58
- /**
59
- * Set response header
60
- * @param name - Header name
61
- * @param value - Header value (string or array of strings for multi-value headers)
62
- */
63
- header(name: string, value: string | string[]): IHttpResponse;
64
- }
65
-
66
- /**
67
- * Route handler function
68
- */
69
- export type RouteHandler = (
70
- req: IHttpRequest,
71
- res: IHttpResponse
72
- ) => void | Promise<void>;
73
-
74
- /**
75
- * Middleware function
76
- */
77
- export type Middleware = (
78
- req: IHttpRequest,
79
- res: IHttpResponse,
80
- next: () => void | Promise<void>
81
- ) => void | Promise<void>;
82
-
83
- /**
84
- * IHttpServer - HTTP Server capability interface
85
- *
86
- * Defines the contract for HTTP server implementations.
87
- * Concrete implementations (Express, Fastify, Hono) should implement this interface.
88
- */
89
- export interface IHttpServer {
90
- /**
91
- * Register a GET route handler
92
- * @param path - Route path (e.g., '/api/users/:id')
93
- * @param handler - Route handler function
94
- */
95
- get(path: string, handler: RouteHandler): void;
96
-
97
- /**
98
- * Register a POST route handler
99
- * @param path - Route path
100
- * @param handler - Route handler function
101
- */
102
- post(path: string, handler: RouteHandler): void;
103
-
104
- /**
105
- * Register a PUT route handler
106
- * @param path - Route path
107
- * @param handler - Route handler function
108
- */
109
- put(path: string, handler: RouteHandler): void;
110
-
111
- /**
112
- * Register a DELETE route handler
113
- * @param path - Route path
114
- * @param handler - Route handler function
115
- */
116
- delete(path: string, handler: RouteHandler): void;
117
-
118
- /**
119
- * Register a PATCH route handler
120
- * @param path - Route path
121
- * @param handler - Route handler function
122
- */
123
- patch(path: string, handler: RouteHandler): void;
124
-
125
- /**
126
- * Register middleware
127
- * @param path - Optional path to apply middleware to (if omitted, applies globally)
128
- * @param handler - Middleware function
129
- */
130
- use(path: string | Middleware, handler?: Middleware): void;
131
-
132
- /**
133
- * Start the HTTP server
134
- * @param port - Port number to listen on
135
- * @returns Promise that resolves when server is ready
136
- */
137
- listen(port: number): Promise<void>;
138
-
139
- /**
140
- * Get the port the server is listening on.
141
- * Returns the actual bound port after `listen()` resolves, or the
142
- * configured port before that.
143
- */
144
- getPort?(): number;
145
-
146
- /**
147
- * Stop the HTTP server
148
- * @returns Promise that resolves when server is stopped
149
- */
150
- close?(): Promise<void>;
151
- }
@@ -1,72 +0,0 @@
1
- // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
-
3
- /**
4
- * Logger Contract
5
- *
6
- * Defines the interface for logging in ObjectStack.
7
- * Compatible with both browser console and structured logging systems.
8
- */
9
- export interface Logger {
10
- /**
11
- * Log a debug message
12
- * @param message - The message to log
13
- * @param meta - Optional metadata to include
14
- */
15
- debug(message: string, meta?: Record<string, any>): void;
16
-
17
- /**
18
- * Log an informational message
19
- * @param message - The message to log
20
- * @param meta - Optional metadata to include
21
- */
22
- info(message: string, meta?: Record<string, any>): void;
23
-
24
- /**
25
- * Log a warning message
26
- * @param message - The message to log
27
- * @param meta - Optional metadata to include
28
- */
29
- warn(message: string, meta?: Record<string, any>): void;
30
-
31
- /**
32
- * Log an error message
33
- * @param message - The message to log
34
- * @param error - Optional error object
35
- * @param meta - Optional metadata to include
36
- */
37
- error(message: string, error?: Error, meta?: Record<string, any>): void;
38
-
39
- /**
40
- * Log a fatal error message
41
- * @param message - The message to log
42
- * @param error - Optional error object
43
- * @param meta - Optional metadata to include
44
- */
45
- fatal?(message: string, error?: Error, meta?: Record<string, any>): void;
46
-
47
- /**
48
- * Create a child logger with additional context
49
- * @param context - Context to add to all logs from this child
50
- */
51
- child?(context: Record<string, any>): Logger;
52
-
53
- /**
54
- * Set trace context for distributed tracing
55
- * @param traceId - Trace identifier
56
- * @param spanId - Span identifier
57
- */
58
- withTrace?(traceId: string, spanId?: string): Logger;
59
-
60
- /**
61
- * Compatibility method for console.log usage
62
- * @param message - The message to log
63
- * @param args - Additional arguments
64
- */
65
- log?(message: string, ...args: any[]): void;
66
-
67
- /**
68
- * Cleanup resources (close file streams, etc.)
69
- * Should be called when the logger is no longer needed
70
- */
71
- destroy?(): Promise<void>;
72
- }
@@ -1,287 +0,0 @@
1
- import { describe, it, expect, beforeEach } from 'vitest';
2
- import { SemanticVersionManager, DependencyResolver } from './dependency-resolver.js';
3
- import { createLogger } from './logger.js';
4
-
5
- describe('SemanticVersionManager', () => {
6
- describe('parse', () => {
7
- it('should parse standard semver', () => {
8
- const version = SemanticVersionManager.parse('1.2.3');
9
- expect(version).toEqual({
10
- major: 1,
11
- minor: 2,
12
- patch: 3,
13
- preRelease: undefined,
14
- build: undefined,
15
- });
16
- });
17
-
18
- it('should parse semver with pre-release', () => {
19
- const version = SemanticVersionManager.parse('1.2.3-alpha.1');
20
- expect(version).toEqual({
21
- major: 1,
22
- minor: 2,
23
- patch: 3,
24
- preRelease: 'alpha.1',
25
- build: undefined,
26
- });
27
- });
28
-
29
- it('should parse semver with build metadata', () => {
30
- const version = SemanticVersionManager.parse('1.2.3+build.123');
31
- expect(version).toEqual({
32
- major: 1,
33
- minor: 2,
34
- patch: 3,
35
- preRelease: undefined,
36
- build: 'build.123',
37
- });
38
- });
39
-
40
- it('should parse semver with both pre-release and build', () => {
41
- const version = SemanticVersionManager.parse('1.2.3-beta.2+build.456');
42
- expect(version).toEqual({
43
- major: 1,
44
- minor: 2,
45
- patch: 3,
46
- preRelease: 'beta.2',
47
- build: 'build.456',
48
- });
49
- });
50
-
51
- it('should handle v prefix', () => {
52
- const version = SemanticVersionManager.parse('v1.2.3');
53
- expect(version.major).toBe(1);
54
- expect(version.minor).toBe(2);
55
- expect(version.patch).toBe(3);
56
- });
57
- });
58
-
59
- describe('compare', () => {
60
- it('should compare major versions', () => {
61
- const v1 = SemanticVersionManager.parse('2.0.0');
62
- const v2 = SemanticVersionManager.parse('1.0.0');
63
- expect(SemanticVersionManager.compare(v1, v2)).toBeGreaterThan(0);
64
- expect(SemanticVersionManager.compare(v2, v1)).toBeLessThan(0);
65
- });
66
-
67
- it('should compare minor versions', () => {
68
- const v1 = SemanticVersionManager.parse('1.2.0');
69
- const v2 = SemanticVersionManager.parse('1.1.0');
70
- expect(SemanticVersionManager.compare(v1, v2)).toBeGreaterThan(0);
71
- });
72
-
73
- it('should compare patch versions', () => {
74
- const v1 = SemanticVersionManager.parse('1.0.2');
75
- const v2 = SemanticVersionManager.parse('1.0.1');
76
- expect(SemanticVersionManager.compare(v1, v2)).toBeGreaterThan(0);
77
- });
78
-
79
- it('should handle equal versions', () => {
80
- const v1 = SemanticVersionManager.parse('1.2.3');
81
- const v2 = SemanticVersionManager.parse('1.2.3');
82
- expect(SemanticVersionManager.compare(v1, v2)).toBe(0);
83
- });
84
-
85
- it('should treat pre-release as lower than release', () => {
86
- const v1 = SemanticVersionManager.parse('1.0.0-alpha');
87
- const v2 = SemanticVersionManager.parse('1.0.0');
88
- expect(SemanticVersionManager.compare(v1, v2)).toBeLessThan(0);
89
- });
90
- });
91
-
92
- describe('satisfies', () => {
93
- it('should match exact version', () => {
94
- const version = SemanticVersionManager.parse('1.2.3');
95
- expect(SemanticVersionManager.satisfies(version, '1.2.3')).toBe(true);
96
- expect(SemanticVersionManager.satisfies(version, '1.2.4')).toBe(false);
97
- });
98
-
99
- it('should match caret range', () => {
100
- const version = SemanticVersionManager.parse('1.2.5');
101
- expect(SemanticVersionManager.satisfies(version, '^1.2.3')).toBe(true);
102
- expect(SemanticVersionManager.satisfies(version, '^1.3.0')).toBe(false);
103
- expect(SemanticVersionManager.satisfies(version, '^2.0.0')).toBe(false);
104
- });
105
-
106
- it('should match tilde range', () => {
107
- const version = SemanticVersionManager.parse('1.2.5');
108
- expect(SemanticVersionManager.satisfies(version, '~1.2.3')).toBe(true);
109
- expect(SemanticVersionManager.satisfies(version, '~1.3.0')).toBe(false);
110
- });
111
-
112
- it('should match greater than or equal', () => {
113
- const version = SemanticVersionManager.parse('1.2.5');
114
- expect(SemanticVersionManager.satisfies(version, '>=1.2.3')).toBe(true);
115
- expect(SemanticVersionManager.satisfies(version, '>=1.2.5')).toBe(true);
116
- expect(SemanticVersionManager.satisfies(version, '>=1.3.0')).toBe(false);
117
- });
118
-
119
- it('should match less than', () => {
120
- const version = SemanticVersionManager.parse('1.2.5');
121
- expect(SemanticVersionManager.satisfies(version, '<1.3.0')).toBe(true);
122
- expect(SemanticVersionManager.satisfies(version, '<1.2.5')).toBe(false);
123
- });
124
-
125
- it('should match range', () => {
126
- const version = SemanticVersionManager.parse('1.2.5');
127
- expect(SemanticVersionManager.satisfies(version, '1.2.0 - 1.3.0')).toBe(true);
128
- expect(SemanticVersionManager.satisfies(version, '1.3.0 - 1.4.0')).toBe(false);
129
- });
130
-
131
- it('should match wildcard', () => {
132
- const version = SemanticVersionManager.parse('1.2.5');
133
- expect(SemanticVersionManager.satisfies(version, '*')).toBe(true);
134
- expect(SemanticVersionManager.satisfies(version, 'latest')).toBe(true);
135
- });
136
- });
137
-
138
- describe('getCompatibilityLevel', () => {
139
- it('should detect fully compatible versions', () => {
140
- const from = SemanticVersionManager.parse('1.2.3');
141
- const to = SemanticVersionManager.parse('1.2.3');
142
- expect(SemanticVersionManager.getCompatibilityLevel(from, to)).toBe('fully-compatible');
143
- });
144
-
145
- it('should detect backward compatible versions', () => {
146
- const from = SemanticVersionManager.parse('1.2.3');
147
- const to = SemanticVersionManager.parse('1.3.0');
148
- expect(SemanticVersionManager.getCompatibilityLevel(from, to)).toBe('backward-compatible');
149
- });
150
-
151
- it('should detect breaking changes', () => {
152
- const from = SemanticVersionManager.parse('1.2.3');
153
- const to = SemanticVersionManager.parse('2.0.0');
154
- expect(SemanticVersionManager.getCompatibilityLevel(from, to)).toBe('breaking-changes');
155
- });
156
-
157
- it('should detect incompatible (downgrade)', () => {
158
- const from = SemanticVersionManager.parse('1.3.0');
159
- const to = SemanticVersionManager.parse('1.2.0');
160
- expect(SemanticVersionManager.getCompatibilityLevel(from, to)).toBe('incompatible');
161
- });
162
- });
163
- });
164
-
165
- describe('DependencyResolver', () => {
166
- let resolver: DependencyResolver;
167
- let logger: ReturnType<typeof createLogger>;
168
-
169
- beforeEach(() => {
170
- logger = createLogger({ level: 'silent' });
171
- resolver = new DependencyResolver(logger);
172
- });
173
-
174
- describe('resolve', () => {
175
- it('should resolve dependencies in topological order', () => {
176
- const plugins = new Map([
177
- ['a', { dependencies: [] }],
178
- ['b', { dependencies: ['a'] }],
179
- ['c', { dependencies: ['a', 'b'] }],
180
- ]);
181
-
182
- const order = resolver.resolve(plugins);
183
-
184
- expect(order.indexOf('a')).toBeLessThan(order.indexOf('b'));
185
- expect(order.indexOf('b')).toBeLessThan(order.indexOf('c'));
186
- });
187
-
188
- it('should handle plugins with no dependencies', () => {
189
- const plugins = new Map([
190
- ['a', { dependencies: [] }],
191
- ['b', { dependencies: [] }],
192
- ]);
193
-
194
- const order = resolver.resolve(plugins);
195
- expect(order).toHaveLength(2);
196
- expect(order).toContain('a');
197
- expect(order).toContain('b');
198
- });
199
-
200
- it('should detect circular dependencies', () => {
201
- const plugins = new Map([
202
- ['a', { dependencies: ['b'] }],
203
- ['b', { dependencies: ['a'] }],
204
- ]);
205
-
206
- expect(() => resolver.resolve(plugins)).toThrow('Circular dependency');
207
- });
208
-
209
- it('should detect missing dependencies', () => {
210
- const plugins = new Map([
211
- ['a', { dependencies: ['missing'] }],
212
- ]);
213
-
214
- expect(() => resolver.resolve(plugins)).toThrow('Missing dependency');
215
- });
216
- });
217
-
218
- describe('detectConflicts', () => {
219
- it('should detect version mismatches', () => {
220
- const plugins = new Map<string, any>([
221
- ['core', { version: '1.0.0', dependencies: {} }],
222
- ['plugin-a', { version: '1.0.0', dependencies: { core: '^2.0.0' } }],
223
- ]);
224
-
225
- const conflicts = resolver.detectConflicts(plugins);
226
- expect(conflicts.length).toBeGreaterThan(0);
227
- expect(conflicts[0].type).toBe('version-mismatch');
228
- });
229
-
230
- it('should return no conflicts for compatible versions', () => {
231
- const plugins = new Map<string, any>([
232
- ['core', { version: '1.2.0', dependencies: {} }],
233
- ['plugin-a', { version: '1.0.0', dependencies: { core: '^1.0.0' } }],
234
- ]);
235
-
236
- const conflicts = resolver.detectConflicts(plugins);
237
- expect(conflicts.length).toBe(0);
238
- });
239
- });
240
-
241
- describe('findBestVersion', () => {
242
- it('should find highest matching version', () => {
243
- const available = ['1.0.0', '1.1.0', '1.2.0', '2.0.0'];
244
- const constraints = ['^1.0.0'];
245
-
246
- const best = resolver.findBestVersion(available, constraints);
247
- expect(best).toBe('1.2.0');
248
- });
249
-
250
- it('should satisfy all constraints', () => {
251
- const available = ['1.0.0', '1.1.0', '1.2.0', '2.0.0'];
252
- const constraints = ['^1.0.0', '>=1.1.0', '<2.0.0'];
253
-
254
- const best = resolver.findBestVersion(available, constraints);
255
- expect(best).toBe('1.2.0');
256
- });
257
-
258
- it('should return undefined if no version satisfies', () => {
259
- const available = ['1.0.0', '1.1.0'];
260
- const constraints = ['^2.0.0'];
261
-
262
- const best = resolver.findBestVersion(available, constraints);
263
- expect(best).toBeUndefined();
264
- });
265
- });
266
-
267
- describe('isAcyclic', () => {
268
- it('should detect acyclic graph', () => {
269
- const deps = new Map([
270
- ['a', []],
271
- ['b', ['a']],
272
- ['c', ['a', 'b']],
273
- ]);
274
-
275
- expect(resolver.isAcyclic(deps)).toBe(true);
276
- });
277
-
278
- it('should detect cyclic graph', () => {
279
- const deps = new Map([
280
- ['a', ['b']],
281
- ['b', ['a']],
282
- ]);
283
-
284
- expect(resolver.isAcyclic(deps)).toBe(false);
285
- });
286
- });
287
- });