meocord 4.2.0 → 4.2.2
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/CHANGELOG.md +322 -0
- package/THIRD_PARTY_NOTICES.md +14 -13
- package/dist/cjs/_shared/{controller.decorator-CRn9kWob.cjs → controller.decorator-U2NBWcoU.cjs} +416 -108
- package/dist/cjs/_shared/{providers-ChvMqnLd.cjs → providers-B4MOFBrW.cjs} +202 -50
- package/dist/cjs/_shared/{sharded-cooldown-store-BdAUTdYs.cjs → sharded-cooldown-store-HTUb3Ii_.cjs} +10 -7
- package/dist/cjs/_shared/{startup-roots-CQo8mSP8.cjs → startup-roots-lA5xkBFq.cjs} +228 -71
- package/dist/cjs/_shared/{translate-error-DhDVbT5M.cjs → translate-error-CbBHUu5P.cjs} +10 -5
- package/dist/cjs/common/index.cjs +3 -3
- package/dist/cjs/core/index.cjs +188 -169
- package/dist/cjs/decorator/index.cjs +52 -7
- package/dist/cjs/testing/index.cjs +1854 -311
- package/dist/cli.json +1 -1
- package/dist/esm/bin/generator.js +4 -5
- package/dist/esm/bin/helper/filter-generator.helper.js +4 -5
- package/dist/esm/bin/helper/guard-generator.helper.js +4 -5
- package/dist/esm/bin/helper/interceptor-generator.helper.js +4 -5
- package/dist/esm/bin/helper/observer-generator.helper.js +4 -5
- package/dist/esm/bin/helper/pipe-generator.helper.js +4 -5
- package/dist/esm/bin/helper/service-generator.helper.js +4 -5
- package/dist/esm/bin/meocord.js +84 -25
- package/dist/esm/build/load-config.pre-entry.js +13 -3
- package/dist/esm/build/rsbuild-config.js +16 -12
- package/dist/esm/build/typed-externals.js +36 -0
- package/dist/esm/common/cooldown-store.js +10 -2
- package/dist/esm/common/errors.js +25 -0
- package/dist/esm/common/meocord-text.js +11 -8
- package/dist/esm/common/response/call-order.js +9 -4
- package/dist/esm/common/response/presenter.js +33 -6
- package/dist/esm/common/response/response-state.js +89 -15
- package/dist/esm/common/route.js +11 -4
- package/dist/esm/common/sharded-cooldown-store.js +1 -1
- package/dist/esm/common/translator.js +52 -11
- package/dist/esm/core/command-registration.js +93 -24
- package/dist/esm/core/command-routes.js +2 -2
- package/dist/esm/core/cooldown-runner.js +29 -8
- package/dist/esm/core/dispatcher.js +9 -8
- package/dist/esm/core/fallback.js +20 -11
- package/dist/esm/core/filter-runner.js +2 -2
- package/dist/esm/core/guard-runner.js +16 -3
- package/dist/esm/core/handler-input.js +4 -3
- package/dist/esm/core/handler-pipeline.js +10 -4
- package/dist/esm/core/lifecycle-hooks.js +134 -47
- package/dist/esm/core/meocord-factory.js +59 -34
- package/dist/esm/core/meocord.app.js +116 -75
- package/dist/esm/core/message-help.js +32 -6
- package/dist/esm/core/observer-runner.js +2 -1
- package/dist/esm/core/providers.js +19 -4
- package/dist/esm/core/running-calls.js +43 -0
- package/dist/esm/core/shard-context.js +5 -6
- package/dist/esm/core/shard-manager.js +16 -10
- package/dist/esm/core/stage-scope.js +2 -1
- package/dist/esm/core/startup-roots.js +21 -1
- package/dist/esm/core/theme-resolvers.js +4 -1
- package/dist/esm/core/theme-scope.js +7 -2
- package/dist/esm/core/theme-validation.js +17 -10
- package/dist/esm/decorator/app.decorator.js +31 -0
- package/dist/esm/decorator/controller.decorator.js +56 -20
- package/dist/esm/decorator/cooldown.decorator.js +17 -0
- package/dist/esm/decorator/filter.decorator.js +2 -2
- package/dist/esm/decorator/guard.decorator.js +3 -2
- package/dist/esm/package.json.js +1 -1
- package/dist/esm/testing/catalog.js +20 -13
- package/dist/esm/testing/cooldown-store-suite.js +67 -5
- package/dist/esm/testing/discord-defaults.js +225 -30
- package/dist/esm/testing/meocord-testing-module.js +158 -44
- package/dist/esm/testing/mock-fn.js +107 -26
- package/dist/esm/testing/mock-interaction.js +1250 -182
- package/dist/esm/testing/snowflake.js +9 -2
- package/dist/esm/testing/strict-mocks.js +40 -17
- package/dist/esm/util/common.util.js +47 -19
- package/dist/esm/util/dev-runner.util.js +8 -8
- package/dist/esm/util/generator-cli.util.js +4 -5
- package/dist/esm/util/interaction.util.js +2 -2
- package/dist/esm/util/meocord-config-loader.util.js +10 -2
- package/dist/esm/util/meocord-config-validation.util.js +3 -5
- package/dist/esm/util/meocord-source-config.util.js +65 -20
- package/dist/esm/util/method-wrapper.util.js +16 -0
- package/dist/esm/util/platform.util.js +10 -10
- package/dist/esm/util/shard-process.util.js +4 -1
- package/dist/esm/util/sharding-mode.util.js +5 -7
- package/dist/esm/util/shared-state.util.js +21 -0
- package/dist/esm/util/tsconfig-paths.util.js +29 -0
- package/dist/esm/util/tsconfig.util.js +10 -10
- package/dist/esm/util/value.util.js +30 -2
- package/dist/types/common/index.d.cts +10 -7
- package/dist/types/common/index.d.ts +10 -7
- package/dist/types/core/index.d.cts +3 -2
- package/dist/types/core/index.d.ts +3 -2
- package/dist/types/decorator/index.d.cts +1 -0
- package/dist/types/decorator/index.d.ts +1 -0
- package/dist/types/{index-CdXsFgjE.d.ts → index-DKJclIi-.d.ts} +45 -21
- package/dist/types/{index-nn5p7OoX.d.cts → index-Dlf0ZZtS.d.cts} +45 -21
- package/dist/types/interface/index.d.cts +1 -1
- package/dist/types/interface/index.d.ts +1 -1
- package/dist/types/{route-BZgFoNeB.d.cts → route-Bh1U13yy.d.cts} +3 -2
- package/dist/types/{route-BZgFoNeB.d.ts → route-Bh1U13yy.d.ts} +3 -2
- package/dist/types/testing/index.d.cts +198 -62
- package/dist/types/testing/index.d.ts +198 -62
- package/dist/types/{theme-resolvers-UKr3-6Ys.d.cts → theme-resolvers-CyEz9pnv.d.cts} +1 -1
- package/dist/types/{theme-resolvers-BcOJVzR9.d.ts → theme-resolvers-DA204O-q.d.ts} +1 -1
- package/dist/types/{translator-DHMkkCpg.d.cts → translator-Bg7hwxDF.d.cts} +21 -8
- package/dist/types/{translator-BViWKtLJ.d.ts → translator-CeP4bCzp.d.ts} +21 -8
- package/package.json +2 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { shared } from '../util/shared-state.util.js';
|
|
2
|
+
|
|
1
3
|
// Framework-agnostic mock function, so `meocord/testing` depends on neither jest nor vitest.
|
|
2
4
|
// Both detect it through `_isMockFunction` and read `fn.mock.calls` in their assertions.
|
|
3
|
-
// Each call signature of `T`, up to four, as a union: `Parameters` and `ReturnType` read only an overloaded function's
|
|
4
|
-
// last signature, such as the list form of a manager's `fetch`
|
|
5
5
|
/**
|
|
6
6
|
* Tells whether a value is a mock function: one from `meocord/testing`, `jest.fn()` or `vi.fn()`.
|
|
7
7
|
*
|
|
@@ -24,13 +24,20 @@
|
|
|
24
24
|
*/ function isMockFunction(fn) {
|
|
25
25
|
return typeof fn === 'function' && '_isMockFunction' in fn && fn._isMockFunction === true;
|
|
26
26
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
27
|
+
/**
|
|
28
|
+
* What `useMockFn` set and the mocks made, kept for both builds of this meocord version, so a setup file in one module
|
|
29
|
+
* format sets them for specs in the other:
|
|
30
|
+
* - `invocationOrder`: when each call of meocord's own mocks came, across all of them, as Vitest numbers its own;
|
|
31
|
+
* - `created`: every mock createMockFn made, held weakly so the mocks of a finished test can still be collected;
|
|
32
|
+
* - `runnerFactory` and `restoreImpl`: the runner's factory useMockFn registered, and for each of its mocks how to
|
|
33
|
+
* give back the implementation it was created with, through the runner's own setter, which a mock's behaviour may wrap.
|
|
34
|
+
*/ const mocks = shared('mocks', ()=>({
|
|
35
|
+
invocationOrder: 0,
|
|
36
|
+
created: new Set(),
|
|
37
|
+
runnerFactory: undefined,
|
|
38
|
+
restoreImpl: new WeakMap()
|
|
39
|
+
}));
|
|
40
|
+
const collected = new FinalizationRegistry((ref)=>mocks.created.delete(ref));
|
|
34
41
|
/**
|
|
35
42
|
* Makes every mock `meocord/testing` creates with the test runner's own mock function, such as Vitest's `vi.fn`.
|
|
36
43
|
*
|
|
@@ -61,26 +68,36 @@ const restoreImpl = new WeakMap();
|
|
|
61
68
|
* @category Mocks
|
|
62
69
|
* @see {@link createMockFn}
|
|
63
70
|
*/ function useMockFn(factory) {
|
|
64
|
-
if (factory === runnerFactory) return;
|
|
71
|
+
if (factory === mocks.runnerFactory) return;
|
|
65
72
|
if ([
|
|
66
|
-
...created
|
|
73
|
+
...mocks.created
|
|
67
74
|
].some((ref)=>ref.deref() !== undefined)) {
|
|
68
75
|
throw new Error("useMockFn() goes before any mock is made: a mock made before it keeps meocord's own function, and the two kinds " + "would mix. Call it once, in the test runner's setup file.");
|
|
69
76
|
}
|
|
70
|
-
const
|
|
77
|
+
const refusal = (cause)=>new Error("useMockFn() takes a function that makes mocks with jest's and Vitest's API, such as vi.fn, jest.fn or bun's " + "mock. node:test's mock.fn records calls in its own shape, so under node:test leave meocord's own mock function " + 'in place.', cause === undefined ? undefined : {
|
|
78
|
+
cause
|
|
79
|
+
});
|
|
80
|
+
let probe;
|
|
81
|
+
try {
|
|
82
|
+
probe = factory();
|
|
83
|
+
} catch (error) {
|
|
84
|
+
// node:test's mock.fn, given on its own, throws for want of its tracker
|
|
85
|
+
throw refusal(error);
|
|
86
|
+
}
|
|
71
87
|
const api = [
|
|
72
88
|
'mockImplementation',
|
|
73
89
|
'mockClear',
|
|
74
90
|
'mockReset'
|
|
75
91
|
];
|
|
76
92
|
if (!isMockFunction(probe) || !Array.isArray(probe.mock?.calls) || api.some((name)=>typeof probe[name] !== 'function')) {
|
|
77
|
-
throw
|
|
93
|
+
throw refusal();
|
|
78
94
|
}
|
|
79
|
-
runnerFactory = factory;
|
|
95
|
+
mocks.runnerFactory = factory;
|
|
80
96
|
}
|
|
97
|
+
/** Whether mocks are made with a runner's function, which {@link useMockFn} registered. */ const usesRunnerMockFn = ()=>mocks.runnerFactory !== undefined;
|
|
81
98
|
/** Tracks a mock for clearAllMocks and resetAllMocks. */ function track(mockFn) {
|
|
82
99
|
const ref = new WeakRef(mockFn);
|
|
83
|
-
created.add(ref);
|
|
100
|
+
mocks.created.add(ref);
|
|
84
101
|
collected.register(mockFn, ref);
|
|
85
102
|
return mockFn;
|
|
86
103
|
}
|
|
@@ -103,7 +120,7 @@ const restoreImpl = new WeakMap();
|
|
|
103
120
|
* @category Mocks
|
|
104
121
|
* @see {@link resetAllMocks}
|
|
105
122
|
*/ function clearAllMocks() {
|
|
106
|
-
for (const ref of created)ref.deref()?.mockClear();
|
|
123
|
+
for (const ref of mocks.created)ref.deref()?.mockClear();
|
|
107
124
|
}
|
|
108
125
|
/**
|
|
109
126
|
* Clears every mock from `meocord/testing`, and puts each back to the implementation it was created with.
|
|
@@ -126,12 +143,12 @@ const restoreImpl = new WeakMap();
|
|
|
126
143
|
* @category Mocks
|
|
127
144
|
* @see {@link clearAllMocks}
|
|
128
145
|
*/ function resetAllMocks() {
|
|
129
|
-
for (const ref of created){
|
|
146
|
+
for (const ref of mocks.created){
|
|
130
147
|
const mockFn = ref.deref();
|
|
131
148
|
if (!mockFn) continue;
|
|
132
149
|
mockFn.mockReset();
|
|
133
150
|
// A runner whose reset drops the implementation the mock was created with, as jest's and bun's do, gets it back
|
|
134
|
-
restoreImpl.get(mockFn)?.();
|
|
151
|
+
mocks.restoreImpl.get(mockFn)?.();
|
|
135
152
|
}
|
|
136
153
|
}
|
|
137
154
|
/**
|
|
@@ -145,6 +162,14 @@ const restoreImpl = new WeakMap();
|
|
|
145
162
|
* `mockReturnValue`, `mockResolvedValue`, `mockRejectedValue` and `mockImplementation` share one implementation, so
|
|
146
163
|
* the last one set wins. The `*Once` variants queue, and run first, in the order they were set.
|
|
147
164
|
*
|
|
165
|
+
* It records what Vitest's `toHaveResolved` and `toHaveBeenCalledBefore` read as well. Its call order is counted
|
|
166
|
+
* across meocord's own mocks, apart from the runner's, so compare the order of two of meocord's mocks, or of two of the
|
|
167
|
+
* runner's, not one of each.
|
|
168
|
+
*
|
|
169
|
+
* A promise the implementation returns is handed back as one that settles the same way, not as the same object: test
|
|
170
|
+
* it with `toEqual`, `toHaveReturnedWith` or by awaiting it, not with `toBe`. A property set on the given promise, such
|
|
171
|
+
* as a `cancel` method, isn't carried over.
|
|
172
|
+
*
|
|
148
173
|
* @param impl - The implementation to run until another is set.
|
|
149
174
|
*
|
|
150
175
|
* @example
|
|
@@ -162,11 +187,12 @@ const restoreImpl = new WeakMap();
|
|
|
162
187
|
* @category Mocks
|
|
163
188
|
* @see {@link MockInstance}
|
|
164
189
|
*/ function createMockFn(impl) {
|
|
190
|
+
const { runnerFactory } = mocks;
|
|
165
191
|
if (runnerFactory) {
|
|
166
192
|
const runnerMock = runnerFactory(impl);
|
|
167
193
|
if (impl) {
|
|
168
194
|
const setImpl = runnerMock.mockImplementation.bind(runnerMock);
|
|
169
|
-
restoreImpl.set(runnerMock, ()=>setImpl(impl));
|
|
195
|
+
mocks.restoreImpl.set(runnerMock, ()=>setImpl(impl));
|
|
170
196
|
}
|
|
171
197
|
return track(runnerMock);
|
|
172
198
|
}
|
|
@@ -179,9 +205,20 @@ const restoreImpl = new WeakMap();
|
|
|
179
205
|
const calls = [];
|
|
180
206
|
const results = [];
|
|
181
207
|
const instances = [];
|
|
208
|
+
const settledResults = [];
|
|
209
|
+
const invocationCallOrder = [];
|
|
210
|
+
const contexts = [];
|
|
182
211
|
const mockFn = function(...args) {
|
|
183
212
|
calls.push(args);
|
|
184
213
|
instances.push(this);
|
|
214
|
+
contexts.push(this);
|
|
215
|
+
invocationCallOrder.push(++mocks.invocationOrder);
|
|
216
|
+
// Updated in place as the call settles, so a promise's outcome shows once it resolves or rejects
|
|
217
|
+
const settled = {
|
|
218
|
+
type: 'incomplete',
|
|
219
|
+
value: undefined
|
|
220
|
+
};
|
|
221
|
+
settledResults.push(settled);
|
|
185
222
|
let type = 'return';
|
|
186
223
|
let value;
|
|
187
224
|
try {
|
|
@@ -192,10 +229,36 @@ const restoreImpl = new WeakMap();
|
|
|
192
229
|
} else {
|
|
193
230
|
value = undefined;
|
|
194
231
|
}
|
|
232
|
+
if (value instanceof Promise) {
|
|
233
|
+
// Returned in its place, so a rejection nobody awaits stays unhandled in the test, as it is on a bot: a handler
|
|
234
|
+
// on the implementation's own promise would mark it handled
|
|
235
|
+
value = value.then((resolved)=>{
|
|
236
|
+
Object.assign(settled, {
|
|
237
|
+
type: 'fulfilled',
|
|
238
|
+
value: resolved
|
|
239
|
+
});
|
|
240
|
+
return resolved;
|
|
241
|
+
}, (rejected)=>{
|
|
242
|
+
Object.assign(settled, {
|
|
243
|
+
type: 'rejected',
|
|
244
|
+
value: rejected
|
|
245
|
+
});
|
|
246
|
+
throw rejected;
|
|
247
|
+
});
|
|
248
|
+
} else {
|
|
249
|
+
Object.assign(settled, {
|
|
250
|
+
type: 'fulfilled',
|
|
251
|
+
value
|
|
252
|
+
});
|
|
253
|
+
}
|
|
195
254
|
return value;
|
|
196
255
|
} catch (err) {
|
|
197
256
|
type = 'throw';
|
|
198
257
|
value = err;
|
|
258
|
+
Object.assign(settled, {
|
|
259
|
+
type: 'rejected',
|
|
260
|
+
value: err
|
|
261
|
+
});
|
|
199
262
|
throw err;
|
|
200
263
|
} finally{
|
|
201
264
|
results.push({
|
|
@@ -223,6 +286,18 @@ const restoreImpl = new WeakMap();
|
|
|
223
286
|
return calls.length > 0 ? calls[calls.length - 1] : undefined;
|
|
224
287
|
}
|
|
225
288
|
};
|
|
289
|
+
// Out of the mock's keys, so a test comparing `mock` as a whole sees the shape it always had
|
|
290
|
+
Object.defineProperties(mock, {
|
|
291
|
+
settledResults: {
|
|
292
|
+
get: ()=>settledResults
|
|
293
|
+
},
|
|
294
|
+
invocationCallOrder: {
|
|
295
|
+
get: ()=>invocationCallOrder
|
|
296
|
+
},
|
|
297
|
+
contexts: {
|
|
298
|
+
get: ()=>contexts
|
|
299
|
+
}
|
|
300
|
+
});
|
|
226
301
|
Object.defineProperty(mockFn, 'mock', {
|
|
227
302
|
value: mock,
|
|
228
303
|
enumerable: false
|
|
@@ -259,16 +334,22 @@ const restoreImpl = new WeakMap();
|
|
|
259
334
|
onceQueue.push(fn);
|
|
260
335
|
return mockFn;
|
|
261
336
|
};
|
|
337
|
+
const forgetCalls = ()=>{
|
|
338
|
+
for (const record of [
|
|
339
|
+
calls,
|
|
340
|
+
results,
|
|
341
|
+
instances,
|
|
342
|
+
settledResults,
|
|
343
|
+
invocationCallOrder,
|
|
344
|
+
contexts
|
|
345
|
+
])record.length = 0;
|
|
346
|
+
};
|
|
262
347
|
mockFn.mockClear = ()=>{
|
|
263
|
-
|
|
264
|
-
results.length = 0;
|
|
265
|
-
instances.length = 0;
|
|
348
|
+
forgetCalls();
|
|
266
349
|
return mockFn;
|
|
267
350
|
};
|
|
268
351
|
mockFn.mockReset = ()=>{
|
|
269
|
-
|
|
270
|
-
results.length = 0;
|
|
271
|
-
instances.length = 0;
|
|
352
|
+
forgetCalls();
|
|
272
353
|
onceQueue = [];
|
|
273
354
|
currentImpl = impl;
|
|
274
355
|
return mockFn;
|
|
@@ -285,4 +366,4 @@ const restoreImpl = new WeakMap();
|
|
|
285
366
|
return track(mockFn);
|
|
286
367
|
}
|
|
287
368
|
|
|
288
|
-
export { clearAllMocks, createMockFn, isMockFunction, resetAllMocks, useMockFn };
|
|
369
|
+
export { clearAllMocks, createMockFn, isMockFunction, resetAllMocks, useMockFn, usesRunnerMockFn };
|