@livon/runtime 0.27.0-rc.1
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/README.md +8 -0
- package/THIRD_PARTY_NOTICES.md +28 -0
- package/dist/index.cjs +36 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +2 -0
- package/dist/runtime.cjs +372 -0
- package/dist/runtime.d.ts +13 -0
- package/dist/runtime.js +338 -0
- package/dist/runtime.spec.cjs +280 -0
- package/dist/runtime.spec.d.ts +1 -0
- package/dist/runtime.spec.js +274 -0
- package/dist/testing/mocks/index.cjs +42 -0
- package/dist/testing/mocks/index.d.ts +2 -0
- package/dist/testing/mocks/index.js +2 -0
- package/dist/testing/mocks/runtime.mock.cjs +63 -0
- package/dist/testing/mocks/runtime.mock.d.ts +22 -0
- package/dist/testing/mocks/runtime.mock.js +23 -0
- package/dist/types.cjs +18 -0
- package/dist/types.d.ts +143 -0
- package/dist/types.js +0 -0
- package/package.json +42 -0
package/dist/runtime.js
ADDED
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
const createHookStore = ()=>({
|
|
2
|
+
onReceive: [],
|
|
3
|
+
onSend: [],
|
|
4
|
+
onError: []
|
|
5
|
+
});
|
|
6
|
+
const createState = ()=>{
|
|
7
|
+
const store = new Map();
|
|
8
|
+
const get = (key)=>store.get(key);
|
|
9
|
+
const set = (key, value)=>{
|
|
10
|
+
store.set(key, value);
|
|
11
|
+
};
|
|
12
|
+
return {
|
|
13
|
+
get,
|
|
14
|
+
set
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
const mergeMetadata = (base, extra)=>{
|
|
18
|
+
if (!base && !extra) return;
|
|
19
|
+
return {
|
|
20
|
+
...base ?? {},
|
|
21
|
+
...extra ?? {}
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
let fallbackEventIdCounter = 0;
|
|
25
|
+
const createEventId = ()=>{
|
|
26
|
+
const cryptoValue = globalThis.crypto;
|
|
27
|
+
if (cryptoValue?.randomUUID) return cryptoValue.randomUUID();
|
|
28
|
+
fallbackEventIdCounter += 1;
|
|
29
|
+
return `evt_${Date.now().toString(36)}_${fallbackEventIdCounter.toString(36)}`;
|
|
30
|
+
};
|
|
31
|
+
const isContextRecord = (value)=>'object' == typeof value && null !== value && !Array.isArray(value);
|
|
32
|
+
const contextRecordFrom = (value)=>{
|
|
33
|
+
if (!value || !isContextRecord(value)) return;
|
|
34
|
+
return value;
|
|
35
|
+
};
|
|
36
|
+
const errorFromUnknown = (error)=>{
|
|
37
|
+
if (error instanceof Error) {
|
|
38
|
+
const context = isContextRecord(error.context) ? error.context : void 0;
|
|
39
|
+
return {
|
|
40
|
+
message: error.message,
|
|
41
|
+
...error.name ? {
|
|
42
|
+
name: error.name
|
|
43
|
+
} : {},
|
|
44
|
+
...error.stack ? {
|
|
45
|
+
stack: error.stack
|
|
46
|
+
} : {},
|
|
47
|
+
...context ? {
|
|
48
|
+
context
|
|
49
|
+
} : {}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
if ('string' == typeof error) return {
|
|
53
|
+
message: error
|
|
54
|
+
};
|
|
55
|
+
if ('object' == typeof error && null !== error) {
|
|
56
|
+
const value = error;
|
|
57
|
+
const message = 'string' == typeof value.message ? value.message : 'Unknown error';
|
|
58
|
+
const name = 'string' == typeof value.name ? value.name : void 0;
|
|
59
|
+
const stack = 'string' == typeof value.stack ? value.stack : void 0;
|
|
60
|
+
const context = isContextRecord(value.context) ? value.context : void 0;
|
|
61
|
+
return {
|
|
62
|
+
message,
|
|
63
|
+
...name ? {
|
|
64
|
+
name
|
|
65
|
+
} : {},
|
|
66
|
+
...stack ? {
|
|
67
|
+
stack
|
|
68
|
+
} : {},
|
|
69
|
+
...context ? {
|
|
70
|
+
context
|
|
71
|
+
} : {}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
message: 'Unknown error'
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
const registerHook = (hooks)=>(hook)=>{
|
|
79
|
+
hooks.push(hook);
|
|
80
|
+
const unsub = ()=>{
|
|
81
|
+
const index = hooks.indexOf(hook);
|
|
82
|
+
if (index >= 0) hooks.splice(index, 1);
|
|
83
|
+
};
|
|
84
|
+
return {
|
|
85
|
+
unsub
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
const registerErrorHook = (hooks)=>(hook)=>{
|
|
89
|
+
hooks.push(hook);
|
|
90
|
+
const unsub = ()=>{
|
|
91
|
+
const index = hooks.indexOf(hook);
|
|
92
|
+
if (index >= 0) hooks.splice(index, 1);
|
|
93
|
+
};
|
|
94
|
+
return {
|
|
95
|
+
unsub
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
const runtime = (inputOrModule, ...rest)=>{
|
|
99
|
+
const input = 'modules' in inputOrModule ? inputOrModule : {
|
|
100
|
+
modules: [
|
|
101
|
+
inputOrModule,
|
|
102
|
+
...rest
|
|
103
|
+
]
|
|
104
|
+
};
|
|
105
|
+
const modules = input.modules;
|
|
106
|
+
const state = createState();
|
|
107
|
+
const hooks = createHookStore();
|
|
108
|
+
const onReceive = registerHook(hooks.onReceive);
|
|
109
|
+
const onSend = registerHook(hooks.onSend);
|
|
110
|
+
const onError = registerErrorHook(hooks.onError);
|
|
111
|
+
const emitErrorHooks = ({ runtimeError, eventEnvelope, runtimeContext })=>{
|
|
112
|
+
hooks.onError.forEach((handler)=>{
|
|
113
|
+
try {
|
|
114
|
+
handler(runtimeError, eventEnvelope, runtimeContext);
|
|
115
|
+
} catch {}
|
|
116
|
+
});
|
|
117
|
+
};
|
|
118
|
+
const mergeContext = (base, update)=>{
|
|
119
|
+
const left = contextRecordFrom(base);
|
|
120
|
+
const right = contextRecordFrom(update);
|
|
121
|
+
if (!left && !right) return;
|
|
122
|
+
return {
|
|
123
|
+
...left ?? {},
|
|
124
|
+
...right ?? {}
|
|
125
|
+
};
|
|
126
|
+
};
|
|
127
|
+
const payloadFromEvent = (event)=>event.payload;
|
|
128
|
+
const errorFromEvent = (event)=>event.error;
|
|
129
|
+
const mergeEvent = (base, update)=>{
|
|
130
|
+
if (!update) return base;
|
|
131
|
+
const nextId = update.id ?? base.id;
|
|
132
|
+
const nextEvent = update.event ?? base.event;
|
|
133
|
+
const nextStatus = update.status ?? base.status;
|
|
134
|
+
const nextMetadata = mergeMetadata(base.metadata, update.metadata);
|
|
135
|
+
const nextContext = mergeContext(base.context, update.context);
|
|
136
|
+
const nextPayload = payloadFromEvent(update) ?? payloadFromEvent(base);
|
|
137
|
+
const nextError = errorFromEvent(update) ?? errorFromEvent(base);
|
|
138
|
+
if (void 0 === nextPayload && void 0 === nextError) throw new Error('Event envelope must contain payload or error.');
|
|
139
|
+
const mergedEnvelopeBase = {
|
|
140
|
+
...base,
|
|
141
|
+
id: nextId,
|
|
142
|
+
event: nextEvent,
|
|
143
|
+
status: nextStatus,
|
|
144
|
+
metadata: nextMetadata,
|
|
145
|
+
context: nextContext
|
|
146
|
+
};
|
|
147
|
+
if (void 0 !== nextPayload && void 0 !== nextError) return {
|
|
148
|
+
...mergedEnvelopeBase,
|
|
149
|
+
payload: nextPayload,
|
|
150
|
+
error: nextError
|
|
151
|
+
};
|
|
152
|
+
if (void 0 !== nextPayload) return {
|
|
153
|
+
...mergedEnvelopeBase,
|
|
154
|
+
payload: nextPayload
|
|
155
|
+
};
|
|
156
|
+
const definedError = nextError;
|
|
157
|
+
return {
|
|
158
|
+
...mergedEnvelopeBase,
|
|
159
|
+
error: definedError
|
|
160
|
+
};
|
|
161
|
+
};
|
|
162
|
+
const buildEnvelope = ({ emitInput, fallbackStatus, roomName })=>{
|
|
163
|
+
const { id: inputId, event, status: inputStatus, metadata: inputMetadata, context, payload, error } = emitInput;
|
|
164
|
+
const roomMeta = roomName ? {
|
|
165
|
+
room: roomName
|
|
166
|
+
} : void 0;
|
|
167
|
+
const metadata = mergeMetadata(roomMeta, inputMetadata);
|
|
168
|
+
const id = inputId ?? createEventId();
|
|
169
|
+
const status = inputStatus ?? fallbackStatus;
|
|
170
|
+
if (void 0 === payload && void 0 === error) throw new Error('Emit input must contain payload or error.');
|
|
171
|
+
const envelopeBase = {
|
|
172
|
+
id,
|
|
173
|
+
event,
|
|
174
|
+
status,
|
|
175
|
+
metadata,
|
|
176
|
+
context
|
|
177
|
+
};
|
|
178
|
+
if (void 0 !== payload && void 0 !== error) return {
|
|
179
|
+
...envelopeBase,
|
|
180
|
+
payload,
|
|
181
|
+
error
|
|
182
|
+
};
|
|
183
|
+
if (void 0 !== payload) return {
|
|
184
|
+
...envelopeBase,
|
|
185
|
+
payload
|
|
186
|
+
};
|
|
187
|
+
const definedError = error;
|
|
188
|
+
return {
|
|
189
|
+
...envelopeBase,
|
|
190
|
+
error: definedError
|
|
191
|
+
};
|
|
192
|
+
};
|
|
193
|
+
const buildFailedEnvelope = ({ runtimeError, eventEnvelope })=>{
|
|
194
|
+
const normalizedError = errorFromUnknown(runtimeError);
|
|
195
|
+
const nextContext = mergeContext(eventEnvelope.context, normalizedError.context);
|
|
196
|
+
return {
|
|
197
|
+
...eventEnvelope,
|
|
198
|
+
error: normalizedError,
|
|
199
|
+
context: nextContext
|
|
200
|
+
};
|
|
201
|
+
};
|
|
202
|
+
const invokeHook = (input)=>{
|
|
203
|
+
const hook = input.hookChain[input.hookIndex];
|
|
204
|
+
if (!hook) return Promise.resolve(input.eventEnvelope);
|
|
205
|
+
let nextPromise;
|
|
206
|
+
let nextCalled = false;
|
|
207
|
+
const next = (update)=>{
|
|
208
|
+
nextCalled = true;
|
|
209
|
+
nextPromise = invokeHook({
|
|
210
|
+
hookChain: input.hookChain,
|
|
211
|
+
hookIndex: input.hookIndex + 1,
|
|
212
|
+
eventEnvelope: mergeEvent(input.eventEnvelope, update),
|
|
213
|
+
runtimeContext: input.runtimeContext
|
|
214
|
+
});
|
|
215
|
+
return nextPromise;
|
|
216
|
+
};
|
|
217
|
+
const result = hook(input.eventEnvelope, input.runtimeContext, next);
|
|
218
|
+
if (void 0 === result && nextCalled) return nextPromise ?? Promise.resolve(input.eventEnvelope);
|
|
219
|
+
return Promise.resolve(result).then((value)=>value ?? nextPromise ?? input.eventEnvelope);
|
|
220
|
+
};
|
|
221
|
+
const runReceive = (eventEnvelope, runtimeContext)=>invokeHook({
|
|
222
|
+
hookChain: hooks.onReceive,
|
|
223
|
+
hookIndex: 0,
|
|
224
|
+
eventEnvelope,
|
|
225
|
+
runtimeContext
|
|
226
|
+
});
|
|
227
|
+
const runSend = (eventEnvelope, runtimeContext)=>invokeHook({
|
|
228
|
+
hookChain: hooks.onSend,
|
|
229
|
+
hookIndex: 0,
|
|
230
|
+
eventEnvelope,
|
|
231
|
+
runtimeContext
|
|
232
|
+
});
|
|
233
|
+
const createEmitError = ({ runtimeContext, roomName })=>async (emitInput)=>{
|
|
234
|
+
const envelope = buildEnvelope({
|
|
235
|
+
emitInput,
|
|
236
|
+
fallbackStatus: 'sending',
|
|
237
|
+
roomName
|
|
238
|
+
});
|
|
239
|
+
const fallbackError = envelope.error ?? {
|
|
240
|
+
message: 'Unknown error'
|
|
241
|
+
};
|
|
242
|
+
const failedEnvelope = {
|
|
243
|
+
...envelope,
|
|
244
|
+
error: fallbackError
|
|
245
|
+
};
|
|
246
|
+
emitErrorHooks({
|
|
247
|
+
runtimeError: fallbackError,
|
|
248
|
+
eventEnvelope: failedEnvelope,
|
|
249
|
+
runtimeContext
|
|
250
|
+
});
|
|
251
|
+
return {
|
|
252
|
+
ok: true
|
|
253
|
+
};
|
|
254
|
+
};
|
|
255
|
+
const createEmitReceive = ({ runtimeContext, roomName })=>async (emitInput)=>{
|
|
256
|
+
const envelope = buildEnvelope({
|
|
257
|
+
emitInput,
|
|
258
|
+
fallbackStatus: 'receiving',
|
|
259
|
+
roomName
|
|
260
|
+
});
|
|
261
|
+
try {
|
|
262
|
+
await runReceive(envelope, runtimeContext);
|
|
263
|
+
return {
|
|
264
|
+
ok: true
|
|
265
|
+
};
|
|
266
|
+
} catch (error) {
|
|
267
|
+
const failedEnvelope = buildFailedEnvelope({
|
|
268
|
+
runtimeError: error,
|
|
269
|
+
eventEnvelope: envelope
|
|
270
|
+
});
|
|
271
|
+
emitErrorHooks({
|
|
272
|
+
runtimeError: error,
|
|
273
|
+
eventEnvelope: failedEnvelope,
|
|
274
|
+
runtimeContext
|
|
275
|
+
});
|
|
276
|
+
return {
|
|
277
|
+
ok: false,
|
|
278
|
+
error: failedEnvelope.error.message
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
const createEmitSend = ({ runtimeContext, roomName })=>async (emitInput)=>{
|
|
283
|
+
const envelope = buildEnvelope({
|
|
284
|
+
emitInput,
|
|
285
|
+
fallbackStatus: 'sending',
|
|
286
|
+
roomName
|
|
287
|
+
});
|
|
288
|
+
try {
|
|
289
|
+
await runSend(envelope, runtimeContext);
|
|
290
|
+
return {
|
|
291
|
+
ok: true
|
|
292
|
+
};
|
|
293
|
+
} catch (error) {
|
|
294
|
+
const failedEnvelope = buildFailedEnvelope({
|
|
295
|
+
runtimeError: error,
|
|
296
|
+
eventEnvelope: envelope
|
|
297
|
+
});
|
|
298
|
+
emitErrorHooks({
|
|
299
|
+
runtimeError: error,
|
|
300
|
+
eventEnvelope: failedEnvelope,
|
|
301
|
+
runtimeContext
|
|
302
|
+
});
|
|
303
|
+
return {
|
|
304
|
+
ok: false,
|
|
305
|
+
error: failedEnvelope.error.message
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
const createEmitEvent = (input)=>async (eventInput)=>createEmitSend(input)(eventInput);
|
|
310
|
+
const createContext = (roomName)=>{
|
|
311
|
+
const runtimeContext = {};
|
|
312
|
+
const createEmitInput = {
|
|
313
|
+
runtimeContext,
|
|
314
|
+
roomName
|
|
315
|
+
};
|
|
316
|
+
runtimeContext.emitReceive = createEmitReceive(createEmitInput);
|
|
317
|
+
runtimeContext.emitSend = createEmitSend(createEmitInput);
|
|
318
|
+
runtimeContext.emitError = createEmitError(createEmitInput);
|
|
319
|
+
runtimeContext.emitEvent = createEmitEvent(createEmitInput);
|
|
320
|
+
runtimeContext.room = (nextRoomName)=>createContext(nextRoomName);
|
|
321
|
+
runtimeContext.state = state;
|
|
322
|
+
return runtimeContext;
|
|
323
|
+
};
|
|
324
|
+
const baseContext = createContext();
|
|
325
|
+
const registry = {
|
|
326
|
+
emitReceive: baseContext.emitReceive,
|
|
327
|
+
emitSend: baseContext.emitSend,
|
|
328
|
+
emitError: baseContext.emitError,
|
|
329
|
+
onReceive,
|
|
330
|
+
onSend,
|
|
331
|
+
onError,
|
|
332
|
+
state
|
|
333
|
+
};
|
|
334
|
+
modules.forEach((mod)=>{
|
|
335
|
+
mod.register(registry);
|
|
336
|
+
});
|
|
337
|
+
};
|
|
338
|
+
export { runtime };
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_exports__ = {};
|
|
3
|
+
const external_vitest_namespaceObject = require("vitest");
|
|
4
|
+
const external_runtime_cjs_namespaceObject = require("./runtime.cjs");
|
|
5
|
+
const index_cjs_namespaceObject = require("./testing/mocks/index.cjs");
|
|
6
|
+
const getRegistryFromModule = (moduleMock)=>{
|
|
7
|
+
const call = moduleMock.register.mock.calls[0];
|
|
8
|
+
if (!call) throw new Error('runtime module should receive a registry during startup');
|
|
9
|
+
return call[0];
|
|
10
|
+
};
|
|
11
|
+
(0, external_vitest_namespaceObject.describe)('runtime()', ()=>{
|
|
12
|
+
(0, external_vitest_namespaceObject.beforeAll)(()=>{
|
|
13
|
+
external_vitest_namespaceObject.vi.useRealTimers();
|
|
14
|
+
});
|
|
15
|
+
(0, external_vitest_namespaceObject.beforeEach)(()=>{
|
|
16
|
+
external_vitest_namespaceObject.vi.clearAllTimers();
|
|
17
|
+
});
|
|
18
|
+
(0, external_vitest_namespaceObject.afterEach)(()=>{
|
|
19
|
+
external_vitest_namespaceObject.vi.clearAllTimers();
|
|
20
|
+
});
|
|
21
|
+
(0, external_vitest_namespaceObject.afterAll)(()=>{
|
|
22
|
+
external_vitest_namespaceObject.vi.restoreAllMocks();
|
|
23
|
+
});
|
|
24
|
+
(0, external_vitest_namespaceObject.describe)('happy', ()=>{
|
|
25
|
+
(0, external_vitest_namespaceObject.it)('should register runtime registry when runtime starts with module input', ()=>{
|
|
26
|
+
const moduleMock = (0, index_cjs_namespaceObject.createRuntimeModuleMock)();
|
|
27
|
+
(0, external_runtime_cjs_namespaceObject.runtime)(moduleMock);
|
|
28
|
+
(0, external_vitest_namespaceObject.expect)(moduleMock.register).toHaveBeenCalledTimes(1);
|
|
29
|
+
const registry = getRegistryFromModule(moduleMock);
|
|
30
|
+
(0, external_vitest_namespaceObject.expect)(typeof registry.emitReceive).toBe('function');
|
|
31
|
+
(0, external_vitest_namespaceObject.expect)(typeof registry.emitSend).toBe('function');
|
|
32
|
+
(0, external_vitest_namespaceObject.expect)(typeof registry.emitError).toBe('function');
|
|
33
|
+
(0, external_vitest_namespaceObject.expect)(typeof registry.onReceive).toBe('function');
|
|
34
|
+
(0, external_vitest_namespaceObject.expect)(typeof registry.onSend).toBe('function');
|
|
35
|
+
(0, external_vitest_namespaceObject.expect)(typeof registry.onError).toBe('function');
|
|
36
|
+
(0, external_vitest_namespaceObject.expect)(typeof registry.state.get).toBe('function');
|
|
37
|
+
(0, external_vitest_namespaceObject.expect)(typeof registry.state.set).toBe('function');
|
|
38
|
+
});
|
|
39
|
+
(0, external_vitest_namespaceObject.it)('should register runtime registry when runtime starts with modules array input', ()=>{
|
|
40
|
+
const moduleMock = (0, index_cjs_namespaceObject.createRuntimeModuleMock)();
|
|
41
|
+
(0, external_runtime_cjs_namespaceObject.runtime)({
|
|
42
|
+
modules: [
|
|
43
|
+
moduleMock
|
|
44
|
+
]
|
|
45
|
+
});
|
|
46
|
+
(0, external_vitest_namespaceObject.expect)(moduleMock.register).toHaveBeenCalledTimes(1);
|
|
47
|
+
(0, external_vitest_namespaceObject.expect)(getRegistryFromModule(moduleMock)).toBeDefined();
|
|
48
|
+
});
|
|
49
|
+
(0, external_vitest_namespaceObject.it)('should execute receive hooks in registration order when emitReceive is called', async ()=>{
|
|
50
|
+
const hookOrder = [];
|
|
51
|
+
const firstHook = (0, index_cjs_namespaceObject.createRuntimeHookMock)(async (_envelope, _ctx, next)=>{
|
|
52
|
+
hookOrder.push('first');
|
|
53
|
+
return next({
|
|
54
|
+
metadata: {
|
|
55
|
+
first: true
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
const secondHook = (0, index_cjs_namespaceObject.createRuntimeHookMock)(async (_envelope, _ctx, next)=>{
|
|
60
|
+
hookOrder.push('second');
|
|
61
|
+
return next();
|
|
62
|
+
});
|
|
63
|
+
const moduleMock = (0, index_cjs_namespaceObject.createRuntimeModuleMock)({
|
|
64
|
+
register: (registry)=>{
|
|
65
|
+
registry.onReceive(firstHook);
|
|
66
|
+
registry.onReceive(secondHook);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
(0, external_runtime_cjs_namespaceObject.runtime)(moduleMock);
|
|
70
|
+
const registry = getRegistryFromModule(moduleMock);
|
|
71
|
+
const ack = await registry.emitReceive((0, index_cjs_namespaceObject.createEmitInputMock)({
|
|
72
|
+
event: 'receive.event'
|
|
73
|
+
}));
|
|
74
|
+
(0, external_vitest_namespaceObject.expect)(ack).toEqual({
|
|
75
|
+
ok: true
|
|
76
|
+
});
|
|
77
|
+
(0, external_vitest_namespaceObject.expect)(hookOrder).toEqual([
|
|
78
|
+
'first',
|
|
79
|
+
'second'
|
|
80
|
+
]);
|
|
81
|
+
(0, external_vitest_namespaceObject.expect)(firstHook).toHaveBeenCalledTimes(1);
|
|
82
|
+
const firstEnvelope = firstHook.mock.calls[0]?.[0];
|
|
83
|
+
(0, external_vitest_namespaceObject.expect)(firstEnvelope.event).toBe('receive.event');
|
|
84
|
+
(0, external_vitest_namespaceObject.expect)(firstEnvelope.status).toBe('receiving');
|
|
85
|
+
});
|
|
86
|
+
(0, external_vitest_namespaceObject.it)('should execute onSend hooks when emitEvent is called from receive context', async ()=>{
|
|
87
|
+
const sendHook = (0, index_cjs_namespaceObject.createRuntimeHookMock)((envelope, _ctx, next)=>next(envelope));
|
|
88
|
+
const moduleMock = (0, index_cjs_namespaceObject.createRuntimeModuleMock)({
|
|
89
|
+
register: (registry)=>{
|
|
90
|
+
registry.onReceive(async (_envelope, ctx, next)=>{
|
|
91
|
+
await ctx.emitEvent((0, index_cjs_namespaceObject.createEmitInputMock)({
|
|
92
|
+
event: 'nested.send'
|
|
93
|
+
}));
|
|
94
|
+
return next();
|
|
95
|
+
});
|
|
96
|
+
registry.onSend(sendHook);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
(0, external_runtime_cjs_namespaceObject.runtime)(moduleMock);
|
|
100
|
+
const registry = getRegistryFromModule(moduleMock);
|
|
101
|
+
const ack = await registry.emitReceive((0, index_cjs_namespaceObject.createEmitInputMock)({
|
|
102
|
+
event: 'root.receive'
|
|
103
|
+
}));
|
|
104
|
+
(0, external_vitest_namespaceObject.expect)(ack).toEqual({
|
|
105
|
+
ok: true
|
|
106
|
+
});
|
|
107
|
+
(0, external_vitest_namespaceObject.expect)(sendHook).toHaveBeenCalledTimes(1);
|
|
108
|
+
const sendEnvelope = sendHook.mock.calls[0]?.[0];
|
|
109
|
+
(0, external_vitest_namespaceObject.expect)(sendEnvelope.event).toBe('nested.send');
|
|
110
|
+
(0, external_vitest_namespaceObject.expect)(sendEnvelope.status).toBe('sending');
|
|
111
|
+
});
|
|
112
|
+
(0, external_vitest_namespaceObject.it)('should include room metadata when room context emits send event', async ()=>{
|
|
113
|
+
const sendHook = (0, index_cjs_namespaceObject.createRuntimeHookMock)((envelope, _ctx, next)=>next(envelope));
|
|
114
|
+
const moduleMock = (0, index_cjs_namespaceObject.createRuntimeModuleMock)({
|
|
115
|
+
register: (registry)=>{
|
|
116
|
+
registry.onReceive(async (_envelope, ctx, next)=>{
|
|
117
|
+
await ctx.room('team-alpha').emitSend((0, index_cjs_namespaceObject.createEmitInputMock)({
|
|
118
|
+
event: 'room.send'
|
|
119
|
+
}));
|
|
120
|
+
return next();
|
|
121
|
+
});
|
|
122
|
+
registry.onSend(sendHook);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
(0, external_runtime_cjs_namespaceObject.runtime)(moduleMock);
|
|
126
|
+
const registry = getRegistryFromModule(moduleMock);
|
|
127
|
+
const ack = await registry.emitReceive((0, index_cjs_namespaceObject.createEmitInputMock)({
|
|
128
|
+
event: 'root.receive'
|
|
129
|
+
}));
|
|
130
|
+
(0, external_vitest_namespaceObject.expect)(ack).toEqual({
|
|
131
|
+
ok: true
|
|
132
|
+
});
|
|
133
|
+
const sendEnvelope = sendHook.mock.calls[0]?.[0];
|
|
134
|
+
(0, external_vitest_namespaceObject.expect)(sendEnvelope.metadata).toEqual({
|
|
135
|
+
room: 'team-alpha'
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
(0, external_vitest_namespaceObject.it)('should stop calling receive hook when unsubscribe is executed', async ()=>{
|
|
139
|
+
const receiveHook = (0, index_cjs_namespaceObject.createRuntimeHookMock)((_envelope, _ctx, next)=>next());
|
|
140
|
+
const moduleMock = (0, index_cjs_namespaceObject.createRuntimeModuleMock)({
|
|
141
|
+
register: (registry)=>{
|
|
142
|
+
const subscription = registry.onReceive(receiveHook);
|
|
143
|
+
subscription.unsub();
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
(0, external_runtime_cjs_namespaceObject.runtime)(moduleMock);
|
|
147
|
+
const registry = getRegistryFromModule(moduleMock);
|
|
148
|
+
const ack = await registry.emitReceive((0, index_cjs_namespaceObject.createEmitInputMock)({
|
|
149
|
+
event: 'receive.event'
|
|
150
|
+
}));
|
|
151
|
+
(0, external_vitest_namespaceObject.expect)(ack).toEqual({
|
|
152
|
+
ok: true
|
|
153
|
+
});
|
|
154
|
+
(0, external_vitest_namespaceObject.expect)(receiveHook).not.toHaveBeenCalled();
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
(0, external_vitest_namespaceObject.describe)('sad', ()=>{
|
|
158
|
+
(0, external_vitest_namespaceObject.it)('should return failed ack and call onError when receive hook throws', async ()=>{
|
|
159
|
+
const errorHook = external_vitest_namespaceObject.vi.fn();
|
|
160
|
+
const moduleMock = (0, index_cjs_namespaceObject.createRuntimeModuleMock)({
|
|
161
|
+
register: (registry)=>{
|
|
162
|
+
registry.onReceive(()=>{
|
|
163
|
+
throw new Error('receive failed');
|
|
164
|
+
});
|
|
165
|
+
registry.onError(errorHook);
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
(0, external_runtime_cjs_namespaceObject.runtime)(moduleMock);
|
|
169
|
+
const registry = getRegistryFromModule(moduleMock);
|
|
170
|
+
const ack = await registry.emitReceive((0, index_cjs_namespaceObject.createEmitInputMock)({
|
|
171
|
+
event: 'receive.event'
|
|
172
|
+
}));
|
|
173
|
+
(0, external_vitest_namespaceObject.expect)(ack).toEqual({
|
|
174
|
+
ok: false,
|
|
175
|
+
error: 'receive failed'
|
|
176
|
+
});
|
|
177
|
+
(0, external_vitest_namespaceObject.expect)(errorHook).toHaveBeenCalledTimes(1);
|
|
178
|
+
const errorEnvelope = errorHook.mock.calls[0]?.[1];
|
|
179
|
+
(0, external_vitest_namespaceObject.expect)(errorEnvelope.status).toBe('receiving');
|
|
180
|
+
(0, external_vitest_namespaceObject.expect)(errorEnvelope.error?.message).toBe('receive failed');
|
|
181
|
+
});
|
|
182
|
+
(0, external_vitest_namespaceObject.it)('should return failed ack and merged context when send hook throws with context payload', async ()=>{
|
|
183
|
+
const errorHook = external_vitest_namespaceObject.vi.fn();
|
|
184
|
+
const moduleMock = (0, index_cjs_namespaceObject.createRuntimeModuleMock)({
|
|
185
|
+
register: (registry)=>{
|
|
186
|
+
registry.onSend(()=>{
|
|
187
|
+
throw {
|
|
188
|
+
message: 'send failed',
|
|
189
|
+
context: {
|
|
190
|
+
source: 'send-hook'
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
});
|
|
194
|
+
registry.onError(errorHook);
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
(0, external_runtime_cjs_namespaceObject.runtime)(moduleMock);
|
|
198
|
+
const registry = getRegistryFromModule(moduleMock);
|
|
199
|
+
const ack = await registry.emitSend((0, index_cjs_namespaceObject.createEmitInputMock)({
|
|
200
|
+
event: 'send.event',
|
|
201
|
+
context: {
|
|
202
|
+
requestId: 'req-1'
|
|
203
|
+
}
|
|
204
|
+
}));
|
|
205
|
+
(0, external_vitest_namespaceObject.expect)(ack).toEqual({
|
|
206
|
+
ok: false,
|
|
207
|
+
error: 'send failed'
|
|
208
|
+
});
|
|
209
|
+
(0, external_vitest_namespaceObject.expect)(errorHook).toHaveBeenCalledTimes(1);
|
|
210
|
+
const errorEnvelope = errorHook.mock.calls[0]?.[1];
|
|
211
|
+
(0, external_vitest_namespaceObject.expect)(errorEnvelope.context).toEqual({
|
|
212
|
+
requestId: 'req-1',
|
|
213
|
+
source: 'send-hook'
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
(0, external_vitest_namespaceObject.it)('should call onError with unknown fallback when emitError input has no error', async ()=>{
|
|
217
|
+
const errorHook = external_vitest_namespaceObject.vi.fn();
|
|
218
|
+
const moduleMock = (0, index_cjs_namespaceObject.createRuntimeModuleMock)({
|
|
219
|
+
register: (registry)=>{
|
|
220
|
+
registry.onError(errorHook);
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
(0, external_runtime_cjs_namespaceObject.runtime)(moduleMock);
|
|
224
|
+
const registry = getRegistryFromModule(moduleMock);
|
|
225
|
+
const ack = await registry.emitError((0, index_cjs_namespaceObject.createEmitInputMock)({
|
|
226
|
+
event: 'error.event',
|
|
227
|
+
payload: new Uint8Array([
|
|
228
|
+
1
|
|
229
|
+
])
|
|
230
|
+
}));
|
|
231
|
+
(0, external_vitest_namespaceObject.expect)(ack).toEqual({
|
|
232
|
+
ok: true
|
|
233
|
+
});
|
|
234
|
+
(0, external_vitest_namespaceObject.expect)(errorHook).toHaveBeenCalledTimes(1);
|
|
235
|
+
const runtimeError = errorHook.mock.calls[0]?.[0];
|
|
236
|
+
const errorEnvelope = errorHook.mock.calls[0]?.[1];
|
|
237
|
+
(0, external_vitest_namespaceObject.expect)(runtimeError.message).toBe('Unknown error');
|
|
238
|
+
(0, external_vitest_namespaceObject.expect)(errorEnvelope.error?.message).toBe('Unknown error');
|
|
239
|
+
});
|
|
240
|
+
(0, external_vitest_namespaceObject.it)('should throw input error when emit input has neither payload nor error', async ()=>{
|
|
241
|
+
const moduleMock = (0, index_cjs_namespaceObject.createRuntimeModuleMock)();
|
|
242
|
+
(0, external_runtime_cjs_namespaceObject.runtime)(moduleMock);
|
|
243
|
+
const registry = getRegistryFromModule(moduleMock);
|
|
244
|
+
const invalidInput = {
|
|
245
|
+
event: 'invalid.event'
|
|
246
|
+
};
|
|
247
|
+
await (0, external_vitest_namespaceObject.expect)(registry.emitReceive(invalidInput)).rejects.toThrow('Emit input must contain payload or error.');
|
|
248
|
+
});
|
|
249
|
+
(0, external_vitest_namespaceObject.it)('should continue onError chain when one error handler throws', async ()=>{
|
|
250
|
+
const failingErrorHook = external_vitest_namespaceObject.vi.fn(()=>{
|
|
251
|
+
throw new Error('secondary error');
|
|
252
|
+
});
|
|
253
|
+
const succeedingErrorHook = external_vitest_namespaceObject.vi.fn();
|
|
254
|
+
const moduleMock = (0, index_cjs_namespaceObject.createRuntimeModuleMock)({
|
|
255
|
+
register: (registry)=>{
|
|
256
|
+
registry.onReceive(()=>{
|
|
257
|
+
throw new Error('receive failed');
|
|
258
|
+
});
|
|
259
|
+
registry.onError(failingErrorHook);
|
|
260
|
+
registry.onError(succeedingErrorHook);
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
(0, external_runtime_cjs_namespaceObject.runtime)(moduleMock);
|
|
264
|
+
const registry = getRegistryFromModule(moduleMock);
|
|
265
|
+
const ack = await registry.emitReceive((0, index_cjs_namespaceObject.createEmitInputMock)({
|
|
266
|
+
event: 'receive.event'
|
|
267
|
+
}));
|
|
268
|
+
(0, external_vitest_namespaceObject.expect)(ack).toEqual({
|
|
269
|
+
ok: false,
|
|
270
|
+
error: 'receive failed'
|
|
271
|
+
});
|
|
272
|
+
(0, external_vitest_namespaceObject.expect)(failingErrorHook).toHaveBeenCalledTimes(1);
|
|
273
|
+
(0, external_vitest_namespaceObject.expect)(succeedingErrorHook).toHaveBeenCalledTimes(1);
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
for(var __rspack_i in __webpack_exports__)exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
278
|
+
Object.defineProperty(exports, '__esModule', {
|
|
279
|
+
value: true
|
|
280
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|