motia 0.11.1-beta.155 → 0.11.1-beta.156-879726
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.
- package/dist/cjs/__tests__/redis-memory-manager.test.d.ts +1 -0
- package/dist/cjs/__tests__/redis-memory-manager.test.js +175 -0
- package/dist/cjs/__tests__/test-helpers/redis-test-helper.d.ts +51 -0
- package/dist/cjs/__tests__/test-helpers/redis-test-helper.example.test.d.ts +1 -0
- package/dist/cjs/__tests__/test-helpers/redis-test-helper.example.test.js +83 -0
- package/dist/cjs/__tests__/test-helpers/redis-test-helper.js +101 -0
- package/dist/cjs/cloud/new-deployment/build.js +3 -1
- package/dist/cjs/dev.js +9 -11
- package/dist/cjs/generate-locked-data.d.ts +2 -0
- package/dist/cjs/generate-locked-data.js +2 -2
- package/dist/cjs/generate-types.js +3 -1
- package/dist/cjs/redis-memory-manager.d.ts +7 -0
- package/dist/cjs/redis-memory-manager.js +122 -0
- package/dist/cjs/start.js +9 -3
- package/dist/esm/__tests__/redis-memory-manager.test.d.ts +1 -0
- package/dist/esm/__tests__/redis-memory-manager.test.js +173 -0
- package/dist/esm/__tests__/test-helpers/redis-test-helper.d.ts +51 -0
- package/dist/esm/__tests__/test-helpers/redis-test-helper.example.test.d.ts +1 -0
- package/dist/esm/__tests__/test-helpers/redis-test-helper.example.test.js +81 -0
- package/dist/esm/__tests__/test-helpers/redis-test-helper.js +94 -0
- package/dist/esm/cloud/new-deployment/build.js +3 -1
- package/dist/esm/dev.js +10 -9
- package/dist/esm/generate-locked-data.d.ts +2 -0
- package/dist/esm/generate-locked-data.js +2 -2
- package/dist/esm/generate-types.js +3 -1
- package/dist/esm/redis-memory-manager.d.ts +7 -0
- package/dist/esm/redis-memory-manager.js +117 -0
- package/dist/esm/start.js +10 -4
- package/dist/types/__tests__/redis-memory-manager.test.d.ts +1 -0
- package/dist/types/__tests__/test-helpers/redis-test-helper.d.ts +51 -0
- package/dist/types/__tests__/test-helpers/redis-test-helper.example.test.d.ts +1 -0
- package/dist/types/generate-locked-data.d.ts +2 -0
- package/dist/types/redis-memory-manager.d.ts +7 -0
- package/package.json +8 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const fs_1 = require("fs");
|
|
4
|
+
const redis_1 = require("redis");
|
|
5
|
+
const redis_memory_server_1 = require("redis-memory-server");
|
|
6
|
+
const redis_memory_manager_1 = require("../redis-memory-manager");
|
|
7
|
+
jest.mock('fs');
|
|
8
|
+
jest.mock('redis');
|
|
9
|
+
jest.mock('redis-memory-server');
|
|
10
|
+
const mockMkdirSync = fs_1.mkdirSync;
|
|
11
|
+
const mockCreateClient = redis_1.createClient;
|
|
12
|
+
const mockRedisMemoryServer = redis_memory_server_1.RedisMemoryServer;
|
|
13
|
+
describe('redis-memory-manager', () => {
|
|
14
|
+
let mockRedisClient;
|
|
15
|
+
let mockServerInstance;
|
|
16
|
+
const originalEnv = process.env;
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
jest.clearAllMocks();
|
|
19
|
+
process.env = { ...originalEnv };
|
|
20
|
+
mockRedisClient = {
|
|
21
|
+
connect: jest.fn().mockResolvedValue(undefined),
|
|
22
|
+
quit: jest.fn().mockResolvedValue(undefined),
|
|
23
|
+
isOpen: true,
|
|
24
|
+
on: jest.fn(),
|
|
25
|
+
};
|
|
26
|
+
mockServerInstance = {
|
|
27
|
+
getHost: jest.fn().mockResolvedValue('127.0.0.1'),
|
|
28
|
+
getPort: jest.fn().mockResolvedValue(6379),
|
|
29
|
+
stop: jest.fn().mockResolvedValue(undefined),
|
|
30
|
+
};
|
|
31
|
+
mockCreateClient.mockReturnValue(mockRedisClient);
|
|
32
|
+
mockRedisMemoryServer.mockImplementation(() => mockServerInstance);
|
|
33
|
+
});
|
|
34
|
+
afterEach(async () => {
|
|
35
|
+
await (0, redis_memory_manager_1.stopRedisMemoryServer)();
|
|
36
|
+
process.env = originalEnv;
|
|
37
|
+
});
|
|
38
|
+
describe('instanceRedisMemoryServer', () => {
|
|
39
|
+
it('should start Redis server and return client', async () => {
|
|
40
|
+
const baseDir = '/test/dir';
|
|
41
|
+
const client = await (0, redis_memory_manager_1.instanceRedisMemoryServer)(baseDir, true);
|
|
42
|
+
expect(mockMkdirSync).toHaveBeenCalledWith(baseDir, { recursive: true });
|
|
43
|
+
expect(mockRedisMemoryServer).toHaveBeenCalledWith({
|
|
44
|
+
instance: {
|
|
45
|
+
ip: '127.0.0.1',
|
|
46
|
+
args: ['--appendonly', 'yes', '--save', '900 1', '--save', '300 10', '--save', '60 100', '--dir', baseDir],
|
|
47
|
+
},
|
|
48
|
+
autoStart: true,
|
|
49
|
+
});
|
|
50
|
+
expect(mockServerInstance.getHost).toHaveBeenCalled();
|
|
51
|
+
expect(mockServerInstance.getPort).toHaveBeenCalled();
|
|
52
|
+
expect(mockCreateClient).toHaveBeenCalledWith({
|
|
53
|
+
socket: {
|
|
54
|
+
host: '127.0.0.1',
|
|
55
|
+
port: 6379,
|
|
56
|
+
reconnectStrategy: expect.any(Function),
|
|
57
|
+
connectTimeout: 10000,
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
expect(mockRedisClient.connect).toHaveBeenCalled();
|
|
61
|
+
expect(client).toBe(mockRedisClient);
|
|
62
|
+
});
|
|
63
|
+
it('should use custom host from environment variable', async () => {
|
|
64
|
+
process.env.MOTIA_REDIS_HOST = '192.168.1.1';
|
|
65
|
+
const baseDir = '/test/dir';
|
|
66
|
+
await (0, redis_memory_manager_1.instanceRedisMemoryServer)(baseDir, false);
|
|
67
|
+
expect(mockRedisMemoryServer).toHaveBeenCalledWith({
|
|
68
|
+
instance: expect.objectContaining({
|
|
69
|
+
ip: '192.168.1.1',
|
|
70
|
+
}),
|
|
71
|
+
autoStart: false,
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
it('should use custom port from environment variable', async () => {
|
|
75
|
+
process.env.MOTIA_REDIS_PORT = '6380';
|
|
76
|
+
const baseDir = '/test/dir';
|
|
77
|
+
await (0, redis_memory_manager_1.instanceRedisMemoryServer)(baseDir, true);
|
|
78
|
+
expect(mockRedisMemoryServer).toHaveBeenCalledWith({
|
|
79
|
+
instance: expect.objectContaining({
|
|
80
|
+
port: 6380,
|
|
81
|
+
}),
|
|
82
|
+
autoStart: true,
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
it('should return existing client if already running', async () => {
|
|
86
|
+
const baseDir = '/test/dir';
|
|
87
|
+
const client1 = await (0, redis_memory_manager_1.instanceRedisMemoryServer)(baseDir, true);
|
|
88
|
+
const client2 = await (0, redis_memory_manager_1.instanceRedisMemoryServer)(baseDir, false);
|
|
89
|
+
expect(client1).toBe(client2);
|
|
90
|
+
expect(mockRedisMemoryServer).toHaveBeenCalledTimes(1);
|
|
91
|
+
expect(mockCreateClient).toHaveBeenCalledTimes(1);
|
|
92
|
+
});
|
|
93
|
+
it('should set up error handler on client', async () => {
|
|
94
|
+
const baseDir = '/test/dir';
|
|
95
|
+
await (0, redis_memory_manager_1.instanceRedisMemoryServer)(baseDir, true);
|
|
96
|
+
expect(mockRedisClient.on).toHaveBeenCalledWith('error', expect.any(Function));
|
|
97
|
+
});
|
|
98
|
+
it('should handle connection errors', async () => {
|
|
99
|
+
const baseDir = '/test/dir';
|
|
100
|
+
const error = new Error('Connection failed');
|
|
101
|
+
mockRedisClient.connect.mockRejectedValue(error);
|
|
102
|
+
await expect((0, redis_memory_manager_1.instanceRedisMemoryServer)(baseDir, true)).rejects.toThrow('Connection failed');
|
|
103
|
+
});
|
|
104
|
+
it('should handle server start errors', async () => {
|
|
105
|
+
const baseDir = '/test/dir';
|
|
106
|
+
const error = new Error('Server start failed');
|
|
107
|
+
mockServerInstance.getHost.mockRejectedValue(error);
|
|
108
|
+
await expect((0, redis_memory_manager_1.instanceRedisMemoryServer)(baseDir, true)).rejects.toThrow('Server start failed');
|
|
109
|
+
});
|
|
110
|
+
it('should implement reconnect strategy correctly', async () => {
|
|
111
|
+
const baseDir = '/test/dir';
|
|
112
|
+
await (0, redis_memory_manager_1.instanceRedisMemoryServer)(baseDir, true);
|
|
113
|
+
const callArgs = mockCreateClient.mock.calls[0];
|
|
114
|
+
expect(callArgs).toBeDefined();
|
|
115
|
+
const socketConfig = callArgs?.[0]?.socket;
|
|
116
|
+
expect(socketConfig).toBeDefined();
|
|
117
|
+
const reconnectStrategy = socketConfig?.reconnectStrategy;
|
|
118
|
+
expect(reconnectStrategy).toBeDefined();
|
|
119
|
+
expect(typeof reconnectStrategy).toBe('function');
|
|
120
|
+
if (reconnectStrategy && typeof reconnectStrategy === 'function') {
|
|
121
|
+
const mockError = new Error('test');
|
|
122
|
+
expect(reconnectStrategy(5, mockError)).toBe(500);
|
|
123
|
+
expect(reconnectStrategy(10, mockError)).toBe(1000);
|
|
124
|
+
expect(reconnectStrategy(11, mockError)).toBeInstanceOf(Error);
|
|
125
|
+
expect(reconnectStrategy(11, mockError).message).toBe('Redis connection retry limit exceeded');
|
|
126
|
+
expect(reconnectStrategy(15, mockError)).toBeInstanceOf(Error);
|
|
127
|
+
expect(reconnectStrategy(20, mockError)).toBeInstanceOf(Error);
|
|
128
|
+
expect(reconnectStrategy(30, mockError)).toBeInstanceOf(Error);
|
|
129
|
+
expect(reconnectStrategy(50, mockError)).toBeInstanceOf(Error);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
describe('stopRedisMemoryServer', () => {
|
|
134
|
+
it('should stop Redis server and close client', async () => {
|
|
135
|
+
const baseDir = '/test/dir';
|
|
136
|
+
await (0, redis_memory_manager_1.instanceRedisMemoryServer)(baseDir, true);
|
|
137
|
+
await (0, redis_memory_manager_1.stopRedisMemoryServer)();
|
|
138
|
+
expect(mockRedisClient.quit).toHaveBeenCalled();
|
|
139
|
+
expect(mockServerInstance.stop).toHaveBeenCalled();
|
|
140
|
+
});
|
|
141
|
+
it('should handle client close errors gracefully', async () => {
|
|
142
|
+
const baseDir = '/test/dir';
|
|
143
|
+
await (0, redis_memory_manager_1.instanceRedisMemoryServer)(baseDir, true);
|
|
144
|
+
const error = new Error('Close failed');
|
|
145
|
+
mockRedisClient.quit.mockRejectedValue(error);
|
|
146
|
+
await (0, redis_memory_manager_1.stopRedisMemoryServer)();
|
|
147
|
+
expect(mockRedisClient.quit).toHaveBeenCalled();
|
|
148
|
+
expect(mockServerInstance.stop).toHaveBeenCalled();
|
|
149
|
+
});
|
|
150
|
+
it('should handle server stop errors gracefully', async () => {
|
|
151
|
+
const baseDir = '/test/dir';
|
|
152
|
+
await (0, redis_memory_manager_1.instanceRedisMemoryServer)(baseDir, true);
|
|
153
|
+
const error = new Error('Stop failed');
|
|
154
|
+
mockServerInstance.stop.mockRejectedValue(error);
|
|
155
|
+
await (0, redis_memory_manager_1.stopRedisMemoryServer)();
|
|
156
|
+
expect(mockRedisClient.quit).toHaveBeenCalled();
|
|
157
|
+
expect(mockServerInstance.stop).toHaveBeenCalled();
|
|
158
|
+
});
|
|
159
|
+
it('should not throw if client is not open', async () => {
|
|
160
|
+
const baseDir = '/test/dir';
|
|
161
|
+
await (0, redis_memory_manager_1.instanceRedisMemoryServer)(baseDir, true);
|
|
162
|
+
Object.defineProperty(mockRedisClient, 'isOpen', {
|
|
163
|
+
value: false,
|
|
164
|
+
writable: true,
|
|
165
|
+
configurable: true,
|
|
166
|
+
});
|
|
167
|
+
await expect((0, redis_memory_manager_1.stopRedisMemoryServer)()).resolves.not.toThrow();
|
|
168
|
+
});
|
|
169
|
+
it('should do nothing if server is not running', async () => {
|
|
170
|
+
await (0, redis_memory_manager_1.stopRedisMemoryServer)();
|
|
171
|
+
expect(mockRedisClient.quit).not.toHaveBeenCalled();
|
|
172
|
+
expect(mockServerInstance.stop).not.toHaveBeenCalled();
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export interface MockRedisClient {
|
|
2
|
+
connect: jest.Mock<Promise<void>>;
|
|
3
|
+
quit: jest.Mock<Promise<void>>;
|
|
4
|
+
disconnect: jest.Mock<Promise<void>>;
|
|
5
|
+
isOpen: boolean;
|
|
6
|
+
on: jest.Mock;
|
|
7
|
+
get: jest.Mock<Promise<string | null>>;
|
|
8
|
+
set: jest.Mock<Promise<string | null>>;
|
|
9
|
+
del: jest.Mock<Promise<number>>;
|
|
10
|
+
exists: jest.Mock<Promise<number>>;
|
|
11
|
+
keys: jest.Mock<Promise<string[]>>;
|
|
12
|
+
flushAll: jest.Mock<Promise<string>>;
|
|
13
|
+
ping: jest.Mock<Promise<string>>;
|
|
14
|
+
xAdd: jest.Mock<Promise<string>>;
|
|
15
|
+
xRead: jest.Mock<Promise<unknown[]>>;
|
|
16
|
+
xReadGroup: jest.Mock<Promise<unknown[]>>;
|
|
17
|
+
xGroupCreate: jest.Mock<Promise<string>>;
|
|
18
|
+
xAck: jest.Mock<Promise<number>>;
|
|
19
|
+
hGet: jest.Mock<Promise<string | null>>;
|
|
20
|
+
hSet: jest.Mock<Promise<number>>;
|
|
21
|
+
hGetAll: jest.Mock<Promise<Record<string, string>>>;
|
|
22
|
+
hDel: jest.Mock<Promise<number>>;
|
|
23
|
+
hExists: jest.Mock<Promise<number>>;
|
|
24
|
+
expire: jest.Mock<Promise<number>>;
|
|
25
|
+
ttl: jest.Mock<Promise<number>>;
|
|
26
|
+
publish: jest.Mock<Promise<number>>;
|
|
27
|
+
[key: string]: unknown;
|
|
28
|
+
}
|
|
29
|
+
export interface RedisTestHelperOptions {
|
|
30
|
+
autoConnect?: boolean;
|
|
31
|
+
isOpen?: boolean;
|
|
32
|
+
}
|
|
33
|
+
export declare function createMockRedisClient(options?: RedisTestHelperOptions): MockRedisClient;
|
|
34
|
+
export declare function resetMockRedisClient(client: MockRedisClient): void;
|
|
35
|
+
export declare function setupRedisTestHelper(): {
|
|
36
|
+
mockClient: MockRedisClient;
|
|
37
|
+
reset: () => void;
|
|
38
|
+
};
|
|
39
|
+
export declare class RedisTestHelper {
|
|
40
|
+
private client;
|
|
41
|
+
constructor(options?: RedisTestHelperOptions);
|
|
42
|
+
getClient(): MockRedisClient;
|
|
43
|
+
reset(): void;
|
|
44
|
+
mockGet(key: string, value: string | null): void;
|
|
45
|
+
mockSet(_key: string, _value: string): void;
|
|
46
|
+
mockExists(key: string, exists: boolean): void;
|
|
47
|
+
mockKeys(_pattern: string, keys: string[]): void;
|
|
48
|
+
mockConnectError(error: Error): void;
|
|
49
|
+
mockQuitError(error: Error): void;
|
|
50
|
+
setConnectionState(isOpen: boolean): void;
|
|
51
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const redis_test_helper_1 = require("./redis-test-helper");
|
|
4
|
+
describe('RedisTestHelper Usage Examples', () => {
|
|
5
|
+
describe('using createMockRedisClient', () => {
|
|
6
|
+
it('should create a mock Redis client', async () => {
|
|
7
|
+
const mockClient = (0, redis_test_helper_1.createMockRedisClient)();
|
|
8
|
+
expect(mockClient.isOpen).toBe(true);
|
|
9
|
+
expect(mockClient.connect).toBeDefined();
|
|
10
|
+
expect(mockClient.quit).toBeDefined();
|
|
11
|
+
await mockClient.connect();
|
|
12
|
+
expect(mockClient.connect).toHaveBeenCalledTimes(1);
|
|
13
|
+
});
|
|
14
|
+
it('should create a disconnected client', () => {
|
|
15
|
+
const mockClient = (0, redis_test_helper_1.createMockRedisClient)({ isOpen: false });
|
|
16
|
+
expect(mockClient.isOpen).toBe(false);
|
|
17
|
+
});
|
|
18
|
+
it('should mock Redis operations', async () => {
|
|
19
|
+
const mockClient = (0, redis_test_helper_1.createMockRedisClient)();
|
|
20
|
+
mockClient.get.mockResolvedValue('test-value');
|
|
21
|
+
mockClient.set.mockResolvedValue('OK');
|
|
22
|
+
const value = await mockClient.get('test-key');
|
|
23
|
+
expect(value).toBe('test-value');
|
|
24
|
+
const result = await mockClient.set('test-key', 'test-value');
|
|
25
|
+
expect(result).toBe('OK');
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
describe('using setupRedisTestHelper', () => {
|
|
29
|
+
it('should provide reset functionality', () => {
|
|
30
|
+
const { mockClient, reset } = (0, redis_test_helper_1.setupRedisTestHelper)();
|
|
31
|
+
mockClient.get('key1');
|
|
32
|
+
expect(mockClient.get).toHaveBeenCalledTimes(1);
|
|
33
|
+
reset();
|
|
34
|
+
expect(mockClient.get).toHaveBeenCalledTimes(0);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
describe('using RedisTestHelper class', () => {
|
|
38
|
+
let helper;
|
|
39
|
+
beforeEach(() => {
|
|
40
|
+
helper = new redis_test_helper_1.RedisTestHelper();
|
|
41
|
+
});
|
|
42
|
+
afterEach(() => {
|
|
43
|
+
helper.reset();
|
|
44
|
+
});
|
|
45
|
+
it('should provide convenient mock methods', async () => {
|
|
46
|
+
const client = helper.getClient();
|
|
47
|
+
helper.mockGet('user:123', 'John Doe');
|
|
48
|
+
const value = await client.get('user:123');
|
|
49
|
+
expect(value).toBe('John Doe');
|
|
50
|
+
helper.mockExists('user:123', true);
|
|
51
|
+
const exists = await client.exists('user:123');
|
|
52
|
+
expect(exists).toBe(1);
|
|
53
|
+
});
|
|
54
|
+
it('should handle connection errors', async () => {
|
|
55
|
+
const error = new Error('Connection failed');
|
|
56
|
+
helper.mockConnectError(error);
|
|
57
|
+
const client = helper.getClient();
|
|
58
|
+
await expect(client.connect()).rejects.toThrow('Connection failed');
|
|
59
|
+
});
|
|
60
|
+
it('should allow changing connection state', () => {
|
|
61
|
+
const client = helper.getClient();
|
|
62
|
+
expect(client.isOpen).toBe(true);
|
|
63
|
+
helper.setConnectionState(false);
|
|
64
|
+
expect(client.isOpen).toBe(false);
|
|
65
|
+
});
|
|
66
|
+
it('should mock keys operation', async () => {
|
|
67
|
+
helper.mockKeys('user:*', ['user:1', 'user:2', 'user:3']);
|
|
68
|
+
const client = helper.getClient();
|
|
69
|
+
const keys = await client.keys('user:*');
|
|
70
|
+
expect(keys).toEqual(['user:1', 'user:2', 'user:3']);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
describe('integration with RedisClientType', () => {
|
|
74
|
+
it('should have required Redis client methods', () => {
|
|
75
|
+
const mockClient = (0, redis_test_helper_1.createMockRedisClient)();
|
|
76
|
+
expect(mockClient.connect).toBeDefined();
|
|
77
|
+
expect(mockClient.quit).toBeDefined();
|
|
78
|
+
expect(mockClient.isOpen).toBeDefined();
|
|
79
|
+
expect(typeof mockClient.connect).toBe('function');
|
|
80
|
+
expect(typeof mockClient.quit).toBe('function');
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
});
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RedisTestHelper = void 0;
|
|
4
|
+
exports.createMockRedisClient = createMockRedisClient;
|
|
5
|
+
exports.resetMockRedisClient = resetMockRedisClient;
|
|
6
|
+
exports.setupRedisTestHelper = setupRedisTestHelper;
|
|
7
|
+
function createMockRedisClient(options = {}) {
|
|
8
|
+
const { autoConnect = true, isOpen = true } = options;
|
|
9
|
+
const mockClient = {
|
|
10
|
+
connect: jest.fn().mockResolvedValue(undefined),
|
|
11
|
+
quit: jest.fn().mockResolvedValue(undefined),
|
|
12
|
+
disconnect: jest.fn().mockResolvedValue(undefined),
|
|
13
|
+
isOpen,
|
|
14
|
+
on: jest.fn(),
|
|
15
|
+
get: jest.fn().mockResolvedValue(null),
|
|
16
|
+
set: jest.fn().mockResolvedValue('OK'),
|
|
17
|
+
del: jest.fn().mockResolvedValue(1),
|
|
18
|
+
exists: jest.fn().mockResolvedValue(1),
|
|
19
|
+
keys: jest.fn().mockResolvedValue([]),
|
|
20
|
+
flushAll: jest.fn().mockResolvedValue('OK'),
|
|
21
|
+
ping: jest.fn().mockResolvedValue('PONG'),
|
|
22
|
+
xAdd: jest.fn().mockResolvedValue(''),
|
|
23
|
+
xRead: jest.fn().mockResolvedValue([]),
|
|
24
|
+
xReadGroup: jest.fn().mockResolvedValue([]),
|
|
25
|
+
xGroupCreate: jest.fn().mockResolvedValue('OK'),
|
|
26
|
+
xAck: jest.fn().mockResolvedValue(1),
|
|
27
|
+
hGet: jest.fn().mockResolvedValue(null),
|
|
28
|
+
hSet: jest.fn().mockResolvedValue(1),
|
|
29
|
+
hGetAll: jest.fn().mockResolvedValue({}),
|
|
30
|
+
hDel: jest.fn().mockResolvedValue(1),
|
|
31
|
+
hExists: jest.fn().mockResolvedValue(0),
|
|
32
|
+
expire: jest.fn().mockResolvedValue(1),
|
|
33
|
+
ttl: jest.fn().mockResolvedValue(-1),
|
|
34
|
+
publish: jest.fn().mockResolvedValue(0),
|
|
35
|
+
};
|
|
36
|
+
if (autoConnect) {
|
|
37
|
+
mockClient.connect.mockResolvedValue(undefined);
|
|
38
|
+
}
|
|
39
|
+
return mockClient;
|
|
40
|
+
}
|
|
41
|
+
function resetMockRedisClient(client) {
|
|
42
|
+
Object.keys(client).forEach((key) => {
|
|
43
|
+
if (jest.isMockFunction(client[key])) {
|
|
44
|
+
client[key].mockClear();
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
function setupRedisTestHelper() {
|
|
49
|
+
const mockClient = createMockRedisClient();
|
|
50
|
+
return {
|
|
51
|
+
mockClient,
|
|
52
|
+
reset: () => resetMockRedisClient(mockClient),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
class RedisTestHelper {
|
|
56
|
+
constructor(options = {}) {
|
|
57
|
+
this.client = createMockRedisClient(options);
|
|
58
|
+
}
|
|
59
|
+
getClient() {
|
|
60
|
+
return this.client;
|
|
61
|
+
}
|
|
62
|
+
reset() {
|
|
63
|
+
resetMockRedisClient(this.client);
|
|
64
|
+
}
|
|
65
|
+
mockGet(key, value) {
|
|
66
|
+
this.client.get.mockImplementation((k) => {
|
|
67
|
+
if (k === key) {
|
|
68
|
+
return Promise.resolve(value);
|
|
69
|
+
}
|
|
70
|
+
return Promise.resolve(null);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
mockSet(_key, _value) {
|
|
74
|
+
this.client.set.mockResolvedValue('OK');
|
|
75
|
+
}
|
|
76
|
+
mockExists(key, exists) {
|
|
77
|
+
this.client.exists.mockImplementation((k) => {
|
|
78
|
+
if (k === key) {
|
|
79
|
+
return Promise.resolve(exists ? 1 : 0);
|
|
80
|
+
}
|
|
81
|
+
return Promise.resolve(0);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
mockKeys(_pattern, keys) {
|
|
85
|
+
this.client.keys.mockResolvedValue(keys);
|
|
86
|
+
}
|
|
87
|
+
mockConnectError(error) {
|
|
88
|
+
this.client.connect.mockRejectedValue(error);
|
|
89
|
+
}
|
|
90
|
+
mockQuitError(error) {
|
|
91
|
+
this.client.quit.mockRejectedValue(error);
|
|
92
|
+
}
|
|
93
|
+
setConnectionState(isOpen) {
|
|
94
|
+
Object.defineProperty(this.client, 'isOpen', {
|
|
95
|
+
value: isOpen,
|
|
96
|
+
writable: true,
|
|
97
|
+
configurable: true,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
exports.RedisTestHelper = RedisTestHelper;
|
|
@@ -8,6 +8,7 @@ const core_1 = require("@motiadev/core");
|
|
|
8
8
|
const printer_1 = require("@motiadev/core/dist/src/printer");
|
|
9
9
|
const fs_1 = __importDefault(require("fs"));
|
|
10
10
|
const generate_locked_data_1 = require("../../generate-locked-data");
|
|
11
|
+
const redis_memory_manager_1 = require("../../redis-memory-manager");
|
|
11
12
|
const build_error_1 = require("../../utils/errors/build.error");
|
|
12
13
|
const builder_1 = require("../build/builder");
|
|
13
14
|
const node_1 = require("../build/builders/node");
|
|
@@ -26,7 +27,8 @@ const build = async (listener) => {
|
|
|
26
27
|
builder.registerBuilder('node', new node_1.NodeBuilder(builder, listener));
|
|
27
28
|
fs_1.default.rmSync(constants_1.distDir, { recursive: true, force: true });
|
|
28
29
|
fs_1.default.mkdirSync(constants_1.distDir, { recursive: true });
|
|
29
|
-
const
|
|
30
|
+
const redisClient = await (0, redis_memory_manager_1.instanceRedisMemoryServer)(constants_1.projectDir, false);
|
|
31
|
+
const lockedData = new core_1.LockedData(constants_1.projectDir, new core_1.MemoryStreamAdapterManager(), new printer_1.NoPrinter(), redisClient);
|
|
30
32
|
if (hasPythonSteps(stepFiles)) {
|
|
31
33
|
builder.registerBuilder('python', new python_1.PythonBuilder(builder, listener));
|
|
32
34
|
}
|
package/dist/cjs/dev.js
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.dev = void 0;
|
|
7
4
|
const analytics_node_1 = require("@amplitude/analytics-node");
|
|
5
|
+
const adapter_redis_state_1 = require("@motiadev/adapter-redis-state");
|
|
6
|
+
const adapter_redis_streams_1 = require("@motiadev/adapter-redis-streams");
|
|
8
7
|
const core_1 = require("@motiadev/core");
|
|
9
|
-
const path_1 = __importDefault(require("path"));
|
|
10
8
|
const endpoints_1 = require("./cloud/endpoints");
|
|
11
9
|
const constants_1 = require("./constants");
|
|
12
10
|
const dev_watchers_1 = require("./dev-watchers");
|
|
13
11
|
const generate_locked_data_1 = require("./generate-locked-data");
|
|
14
12
|
const load_motia_config_1 = require("./load-motia-config");
|
|
15
13
|
const plugins_1 = require("./plugins");
|
|
14
|
+
const redis_memory_manager_1 = require("./redis-memory-manager");
|
|
16
15
|
const activate_python_env_1 = require("./utils/activate-python-env");
|
|
17
16
|
const analytics_1 = require("./utils/analytics");
|
|
18
17
|
const version_1 = require("./version");
|
|
@@ -41,20 +40,18 @@ const dev = async (port, hostname, disableVerbose, enableMermaid, motiaFileStora
|
|
|
41
40
|
}
|
|
42
41
|
const motiaFileStoragePath = motiaFileStorageDir || '.motia';
|
|
43
42
|
const appConfig = await (0, load_motia_config_1.loadMotiaConfig)(baseDir);
|
|
43
|
+
const redisClient = await (0, redis_memory_manager_1.instanceRedisMemoryServer)(motiaFileStoragePath, true);
|
|
44
44
|
const adapters = {
|
|
45
45
|
eventAdapter: appConfig.adapters?.events || new core_1.DefaultQueueEventAdapter(),
|
|
46
46
|
cronAdapter: appConfig.adapters?.cron || new core_1.DefaultCronAdapter(),
|
|
47
|
-
streamAdapter: appConfig.adapters?.streams || new
|
|
47
|
+
streamAdapter: appConfig.adapters?.streams || new adapter_redis_streams_1.RedisStreamAdapterManager(redisClient),
|
|
48
48
|
};
|
|
49
49
|
const lockedData = await (0, generate_locked_data_1.generateLockedData)({
|
|
50
50
|
projectDir: baseDir,
|
|
51
51
|
streamAdapter: adapters.streamAdapter,
|
|
52
|
+
redisClient,
|
|
52
53
|
});
|
|
53
|
-
const state = appConfig.adapters?.state ||
|
|
54
|
-
(0, core_1.createStateAdapter)({
|
|
55
|
-
adapter: 'default',
|
|
56
|
-
filePath: path_1.default.join(baseDir, motiaFileStoragePath),
|
|
57
|
-
});
|
|
54
|
+
const state = appConfig.adapters?.state || new adapter_redis_state_1.RedisStateAdapter(redisClient);
|
|
58
55
|
const config = { isVerbose };
|
|
59
56
|
const motiaServer = (0, core_1.createServer)(lockedData, state, config, adapters, appConfig.app);
|
|
60
57
|
const watcher = (0, dev_watchers_1.createDevWatchers)(lockedData, motiaServer, motiaServer.motiaEventManager, motiaServer.cronManager);
|
|
@@ -101,11 +98,11 @@ const dev = async (port, hostname, disableVerbose, enableMermaid, motiaFileStora
|
|
|
101
98
|
motiaServer.server.listen(port, hostname);
|
|
102
99
|
console.log('🚀 Server ready and listening on port', port);
|
|
103
100
|
console.log(`🔗 Open http://localhost:${port}${constants_1.workbenchBase} to open workbench 🛠️`);
|
|
104
|
-
// 6) Gracefully shut down on SIGTERM
|
|
105
101
|
process.on('SIGTERM', async () => {
|
|
106
102
|
(0, core_1.trackEvent)('dev_server_shutdown', { reason: 'SIGTERM' });
|
|
107
103
|
motiaServer.server.close();
|
|
108
104
|
await watcher.stop();
|
|
105
|
+
await (0, redis_memory_manager_1.stopRedisMemoryServer)();
|
|
109
106
|
await (0, analytics_node_1.flush)().promise;
|
|
110
107
|
process.exit(0);
|
|
111
108
|
});
|
|
@@ -113,6 +110,7 @@ const dev = async (port, hostname, disableVerbose, enableMermaid, motiaFileStora
|
|
|
113
110
|
(0, core_1.trackEvent)('dev_server_shutdown', { reason: 'SIGINT' });
|
|
114
111
|
motiaServer.server.close();
|
|
115
112
|
await watcher.stop();
|
|
113
|
+
await (0, redis_memory_manager_1.stopRedisMemoryServer)();
|
|
116
114
|
await (0, analytics_node_1.flush)().promise;
|
|
117
115
|
process.exit(0);
|
|
118
116
|
});
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { LockedData, type Step, type StreamAdapterManager } from '@motiadev/core';
|
|
2
|
+
import type { RedisClientType } from 'redis';
|
|
2
3
|
export declare const getStepFiles: (projectDir: string) => string[];
|
|
3
4
|
export declare const getStreamFiles: (projectDir: string) => string[];
|
|
4
5
|
export declare const collectFlows: (projectDir: string, lockedData: LockedData) => Promise<Step[]>;
|
|
5
6
|
export declare const generateLockedData: (config: {
|
|
6
7
|
projectDir: string;
|
|
7
8
|
streamAdapter: StreamAdapterManager;
|
|
9
|
+
redisClient: RedisClientType;
|
|
8
10
|
printerType?: "disabled" | "default";
|
|
9
11
|
}) => Promise<LockedData>;
|
|
@@ -108,13 +108,13 @@ const collectFlows = async (projectDir, lockedData) => {
|
|
|
108
108
|
exports.collectFlows = collectFlows;
|
|
109
109
|
const generateLockedData = async (config) => {
|
|
110
110
|
try {
|
|
111
|
-
const { projectDir, streamAdapter, printerType = 'default' } = config;
|
|
111
|
+
const { projectDir, streamAdapter, printerType = 'default', redisClient } = config;
|
|
112
112
|
const printer = printerType === 'disabled' ? new printer_1.NoPrinter() : new printer_1.Printer(projectDir);
|
|
113
113
|
/*
|
|
114
114
|
* NOTE: right now for performance and simplicity let's enforce a folder,
|
|
115
115
|
* but we might want to remove this and scan the entire current directory
|
|
116
116
|
*/
|
|
117
|
-
const lockedData = new core_1.LockedData(projectDir, streamAdapter, printer);
|
|
117
|
+
const lockedData = new core_1.LockedData(projectDir, streamAdapter, printer, redisClient);
|
|
118
118
|
await (0, exports.collectFlows)(projectDir, lockedData);
|
|
119
119
|
lockedData.saveTypes();
|
|
120
120
|
return lockedData;
|
|
@@ -4,11 +4,13 @@ exports.generateTypes = void 0;
|
|
|
4
4
|
const core_1 = require("@motiadev/core");
|
|
5
5
|
const crypto_1 = require("crypto");
|
|
6
6
|
const generate_locked_data_1 = require("./generate-locked-data");
|
|
7
|
+
const redis_memory_manager_1 = require("./redis-memory-manager");
|
|
7
8
|
const version = `${(0, crypto_1.randomUUID)()}:${Math.floor(Date.now() / 1000)}`;
|
|
8
9
|
const generateTypes = async (projectDir) => {
|
|
9
10
|
const files = (0, generate_locked_data_1.getStepFiles)(projectDir);
|
|
10
11
|
const streamsFiles = (0, generate_locked_data_1.getStreamFiles)(projectDir);
|
|
11
|
-
const
|
|
12
|
+
const redisClient = await (0, redis_memory_manager_1.instanceRedisMemoryServer)(projectDir, false);
|
|
13
|
+
const lockedData = new core_1.LockedData(projectDir, new core_1.MemoryStreamAdapterManager(), new core_1.Printer(projectDir), redisClient);
|
|
12
14
|
for (const filePath of files) {
|
|
13
15
|
const config = await (0, core_1.getStepConfig)(filePath, projectDir);
|
|
14
16
|
if (config) {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type RedisClientType } from 'redis';
|
|
2
|
+
export interface RedisConnectionInfo {
|
|
3
|
+
host: string;
|
|
4
|
+
port: number;
|
|
5
|
+
}
|
|
6
|
+
export declare const instanceRedisMemoryServer: (baseDir: string, autoStart?: boolean) => Promise<RedisClientType>;
|
|
7
|
+
export declare const stopRedisMemoryServer: () => Promise<void>;
|