sounding 0.0.3 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/types.js ADDED
@@ -0,0 +1,675 @@
1
+ /**
2
+ * Shared TypeScript-consumable JSDoc typedefs for Sounding's public API.
3
+ *
4
+ * These comments are intentionally colocated with the JavaScript source so JSDoc
5
+ * stays the source of truth for editor autocomplete and local type checks.
6
+ *
7
+ * @typedef {'virtual' | 'http'} SoundingTransport
8
+ *
9
+ * @typedef {Record<string, any>} AnyRecord
10
+ *
11
+ * @typedef {string | number | boolean | null} JsonPrimitive
12
+ *
13
+ * @typedef {JsonPrimitive | any[] | AnyRecord} JsonValue
14
+ *
15
+ * @typedef {{
16
+ * id?: string | number,
17
+ * email?: string,
18
+ * fullName?: string,
19
+ * team?: string | number,
20
+ * teamId?: string | number,
21
+ * headers?: HeadersInit | AnyRecord,
22
+ * session?: AnyRecord,
23
+ * sounding?: {
24
+ * headers?: HeadersInit | AnyRecord,
25
+ * session?: AnyRecord,
26
+ * },
27
+ * [key: string]: any,
28
+ * }} SoundingActor
29
+ *
30
+ * @typedef {RequestInit & {
31
+ * headers?: HeadersInit | AnyRecord,
32
+ * session?: AnyRecord,
33
+ * transport?: SoundingTransport,
34
+ * baseUrl?: string,
35
+ * requestOptions?: SoundingRequestOptions,
36
+ * [key: string]: any,
37
+ * }} SoundingRequestOptions
38
+ *
39
+ * @typedef {{
40
+ * raw: unknown,
41
+ * ok: boolean,
42
+ * status: number,
43
+ * statusText: string,
44
+ * url?: string,
45
+ * request?: {
46
+ * method: string,
47
+ * target: string,
48
+ * transport: SoundingTransport | 'socket',
49
+ * url?: string,
50
+ * headers?: HeadersInit | AnyRecord,
51
+ * },
52
+ * redirected: boolean,
53
+ * session?: AnyRecord,
54
+ * headers: Headers,
55
+ * body: string,
56
+ * data: any,
57
+ * header(name: string): string | null,
58
+ * text(): Promise<string>,
59
+ * json(): Promise<any>,
60
+ * }} SoundingResponse
61
+ *
62
+ * @typedef {{
63
+ * readonly transport: SoundingTransport,
64
+ * request(method: string, target: string, options?: SoundingRequestOptions): Promise<SoundingResponse>,
65
+ * get(target: string, options?: SoundingRequestOptions): Promise<SoundingResponse>,
66
+ * head(target: string, options?: SoundingRequestOptions): Promise<SoundingResponse>,
67
+ * post(target: string, payload?: any, options?: SoundingRequestOptions): Promise<SoundingResponse>,
68
+ * put(target: string, payload?: any, options?: SoundingRequestOptions): Promise<SoundingResponse>,
69
+ * patch(target: string, payload?: any, options?: SoundingRequestOptions): Promise<SoundingResponse>,
70
+ * delete(target: string, payload?: any, options?: SoundingRequestOptions): Promise<SoundingResponse>,
71
+ * clearSession(): void,
72
+ * withHeaders(headers?: HeadersInit | AnyRecord): SoundingRequestClient,
73
+ * withSession(session?: AnyRecord): SoundingRequestClient,
74
+ * using(transport: SoundingTransport): SoundingRequestClient,
75
+ * as(actor?: SoundingActor | string | null): SoundingRequestClient,
76
+ * }} SoundingRequestClient
77
+ *
78
+ * @typedef {SoundingRequestOptions & {
79
+ * component?: string,
80
+ * only?: string[],
81
+ * except?: string[],
82
+ * reset?: string[],
83
+ * errorBag?: string,
84
+ * version?: string,
85
+ * }} SoundingVisitOptions
86
+ *
87
+ * @typedef {{
88
+ * (target: string, options?: SoundingVisitOptions): Promise<SoundingResponse>;
89
+ * readonly transport: SoundingTransport;
90
+ * get(target: string, options?: SoundingVisitOptions): Promise<SoundingResponse>;
91
+ * head(target: string, options?: SoundingVisitOptions): Promise<SoundingResponse>;
92
+ * post(target: string, payload?: any, options?: SoundingVisitOptions): Promise<SoundingResponse>;
93
+ * put(target: string, payload?: any, options?: SoundingVisitOptions): Promise<SoundingResponse>;
94
+ * patch(target: string, payload?: any, options?: SoundingVisitOptions): Promise<SoundingResponse>;
95
+ * delete(target: string, payload?: any, options?: SoundingVisitOptions): Promise<SoundingResponse>;
96
+ * del(target: string, payload?: any, options?: SoundingVisitOptions): Promise<SoundingResponse>;
97
+ * using(transport: SoundingTransport): SoundingVisitClient;
98
+ * as(actor?: SoundingActor | string | null): SoundingVisitClient;
99
+ * }} SoundingVisitClient
100
+ *
101
+ * @typedef {{
102
+ * capturedAt?: string,
103
+ * to?: string | string[],
104
+ * from?: string,
105
+ * subject?: string,
106
+ * text?: string,
107
+ * html?: string,
108
+ * ctaUrl?: string,
109
+ * [key: string]: any,
110
+ * }} SoundingMailMessage
111
+ *
112
+ * @typedef {{
113
+ * capture(message: SoundingMailMessage): SoundingMailMessage,
114
+ * all(): SoundingMailMessage[],
115
+ * latest(): SoundingMailMessage | undefined,
116
+ * clear(): void,
117
+ * }} SoundingMailbox
118
+ *
119
+ * @typedef {{
120
+ * install(): boolean,
121
+ * uninstall(): boolean,
122
+ * readonly installed: boolean,
123
+ * }} SoundingMailCapture
124
+ *
125
+ * @typedef {{
126
+ * toBe(expected: any): void,
127
+ * toEqual(expected: any): void,
128
+ * toContain(expected: any): void,
129
+ * toMatch(expected: string | RegExp): void,
130
+ * toBeTruthy(): void,
131
+ * toBeFalsy(): void,
132
+ * toBeDefined(): void,
133
+ * toHaveStatus(expected: number): void,
134
+ * toHaveHeader(name: string, expected?: string): void,
135
+ * toRedirectTo(expected: string): void,
136
+ * toHaveJsonPath(path: string, expected: any): void,
137
+ * toHaveSentCount(expected: number): void,
138
+ * toHaveSentMail(expected?: Partial<SoundingMailMessage> | ((message: SoundingMailMessage) => boolean)): void,
139
+ * toHaveCtaUrl(expected?: string | RegExp): void,
140
+ * toHaveSession(path: string, expected?: any): void,
141
+ * toHaveFlash(type: string, expected?: any): void,
142
+ * toBeInertiaPage(component: string): void,
143
+ * toHaveInertiaProp(path: string, expected?: any): void,
144
+ * toHaveInertiaProps(expected: Record<string, any>): void,
145
+ * toHaveInertiaPropCount(path: string, expected: number): void,
146
+ * toHaveOnlyInertiaProps(expected: string[]): void,
147
+ * toMatchInertiaProp(path: string, expected: string | RegExp): void,
148
+ * toHaveSharedInertiaProp(path: string, expected?: any): void,
149
+ * toHaveSharedInertiaProps(expected: Record<string, any>): void,
150
+ * toHaveInertiaError(path: string, expected?: any): void,
151
+ * toHaveInertiaErrors(expected?: string | string[] | Record<string, any>): void,
152
+ * toHaveNoInertiaErrors(): void,
153
+ * toHaveInertiaPartialReload(expected?: { component?: string, only?: string[], except?: string[], reset?: string[], version?: string, errorBag?: string }): void,
154
+ * toReceive(event: string, expected?: any, options?: { timeout?: number }): Promise<void>,
155
+ * toHaveReceived(event: string, expected?: any): void,
156
+ * not: {
157
+ * toHaveSentMail(expected?: Partial<SoundingMailMessage> | ((message: SoundingMailMessage) => boolean)): void,
158
+ * toHaveCtaUrl(expected?: string | RegExp): void,
159
+ * toHaveSession(path: string, expected?: any): void,
160
+ * toHaveFlash(type: string, expected?: any): void,
161
+ * toHaveInertiaProp(path: string, expected?: any): void,
162
+ * toHaveSharedInertiaProp(path: string, expected?: any): void,
163
+ * toHaveInertiaError(path: string, expected?: any): void,
164
+ * toReceive(event: string, expected?: any, options?: { timeout?: number }): Promise<void>,
165
+ * },
166
+ * [key: string]: any,
167
+ * }} SoundingExpectation
168
+ *
169
+ * @typedef {{
170
+ * (actual: any): SoundingExpectation;
171
+ * withFallback(fallback: (actual: any) => any): (actual: any) => SoundingExpectation | any;
172
+ * }} SoundingExpect
173
+ *
174
+ * @typedef {{
175
+ * goto(target: string): Promise<any> | any,
176
+ * fill(selector: string, value: string): Promise<any> | any,
177
+ * click(selector: string): Promise<any> | any,
178
+ * check?(selector: string): Promise<any> | any,
179
+ * [key: string]: any,
180
+ * }} SoundingPage
181
+ *
182
+ * @typedef {'off' | 'on' | 'on-failure'} SoundingBrowserArtifactMode
183
+ *
184
+ * @typedef {boolean | SoundingBrowserArtifactMode} SoundingBrowserArtifactSetting
185
+ *
186
+ * @typedef {{
187
+ * outputDir: string,
188
+ * screenshot: SoundingBrowserArtifactSetting,
189
+ * trace: SoundingBrowserArtifactSetting,
190
+ * video: SoundingBrowserArtifactSetting,
191
+ * currentUrl: boolean,
192
+ * }} SoundingBrowserArtifactsConfig
193
+ *
194
+ * @typedef {{
195
+ * outputDir: string,
196
+ * screenshot: SoundingBrowserArtifactMode,
197
+ * trace: SoundingBrowserArtifactMode,
198
+ * video: SoundingBrowserArtifactMode,
199
+ * currentUrl: boolean,
200
+ * }} SoundingBrowserResolvedArtifactsConfig
201
+ *
202
+ * @typedef {{
203
+ * outputDir: string,
204
+ * directory: string,
205
+ * project: string,
206
+ * trialName?: string,
207
+ * currentUrl?: string,
208
+ * currentUrlPath?: string,
209
+ * screenshot?: string,
210
+ * trace?: string,
211
+ * video?: string,
212
+ * errors: Array<{ artifact: string, message: string }>,
213
+ * }} SoundingBrowserArtifacts
214
+ *
215
+ * @typedef {boolean | {
216
+ * outputDir?: string,
217
+ * screenshot?: SoundingBrowserArtifactSetting,
218
+ * trace?: SoundingBrowserArtifactSetting,
219
+ * video?: SoundingBrowserArtifactSetting,
220
+ * currentUrl?: boolean,
221
+ * }} SoundingBrowserArtifactsOption
222
+ *
223
+ * @typedef {{
224
+ * width: number,
225
+ * height: number,
226
+ * }} SoundingBrowserViewport
227
+ *
228
+ * @typedef {{
229
+ * name?: string,
230
+ * type?: 'chromium' | 'firefox' | 'webkit' | string,
231
+ * device?: string,
232
+ * viewport?: SoundingBrowserViewport,
233
+ * contextOptions?: AnyRecord,
234
+ * launchOptions?: AnyRecord,
235
+ * }} SoundingBrowserProjectConfig
236
+ *
237
+ * @typedef {{
238
+ * type?: string,
239
+ * project?: string,
240
+ * launchOptions?: AnyRecord,
241
+ * contextOptions?: AnyRecord,
242
+ * artifacts?: SoundingBrowserArtifactsOption,
243
+ * trialName?: string,
244
+ * }} SoundingBrowserOpenOptions
245
+ *
246
+ * @typedef {{
247
+ * playwright: AnyRecord,
248
+ * browser: AnyRecord,
249
+ * context: AnyRecord,
250
+ * page: SoundingPage,
251
+ * expect?: (actual: any) => any,
252
+ * project: string,
253
+ * artifacts: SoundingBrowserResolvedArtifactsConfig,
254
+ * readonly latestArtifacts?: SoundingBrowserArtifacts | null,
255
+ * captureFailureArtifacts(error?: unknown): Promise<SoundingBrowserArtifacts>,
256
+ * closeSessionContext?(): Promise<SoundingBrowserArtifacts>,
257
+ * }} SoundingBrowserSession
258
+ *
259
+ * @typedef {{
260
+ * open(options?: SoundingBrowserOpenOptions): Promise<SoundingBrowserSession>,
261
+ * close(): Promise<void>,
262
+ * readonly active: boolean,
263
+ * readonly page?: SoundingPage,
264
+ * readonly context?: AnyRecord,
265
+ * readonly expect?: (actual: any) => any,
266
+ * }} SoundingBrowserManager
267
+ *
268
+ * @typedef {{
269
+ * timeout?: number,
270
+ * headers?: HeadersInit | AnyRecord,
271
+ * }} SoundingSocketRequestOptions
272
+ *
273
+ * @typedef {{
274
+ * event: string,
275
+ * data: any,
276
+ * args: any[],
277
+ * receivedAt: string,
278
+ * }} SoundingSocketEvent
279
+ *
280
+ * @typedef {{
281
+ * readonly id?: string,
282
+ * readonly connected: boolean,
283
+ * on(event: string, listener: (...args: any[]) => void): SoundingSocketClient,
284
+ * off(event: string, listener: (...args: any[]) => void): SoundingSocketClient,
285
+ * events(event?: string): any[],
286
+ * receive(event: string, options?: { timeout?: number }): Promise<any>,
287
+ * request(method: string, target: string, payloadOrOptions?: any, options?: SoundingSocketRequestOptions): Promise<SoundingResponse>,
288
+ * get(target: string, options?: SoundingSocketRequestOptions): Promise<SoundingResponse>,
289
+ * head(target: string, options?: SoundingSocketRequestOptions): Promise<SoundingResponse>,
290
+ * post(target: string, payload?: any, options?: SoundingSocketRequestOptions): Promise<SoundingResponse>,
291
+ * put(target: string, payload?: any, options?: SoundingSocketRequestOptions): Promise<SoundingResponse>,
292
+ * patch(target: string, payload?: any, options?: SoundingSocketRequestOptions): Promise<SoundingResponse>,
293
+ * delete(target: string, payload?: any, options?: SoundingSocketRequestOptions): Promise<SoundingResponse>,
294
+ * close(): Promise<void>,
295
+ * }} SoundingSocketClient
296
+ *
297
+ * @typedef {{
298
+ * baseUrl?: string,
299
+ * timeout?: number,
300
+ * transports?: string[],
301
+ * path?: string,
302
+ * headers?: HeadersInit | AnyRecord,
303
+ * initialConnectionHeaders?: HeadersInit | AnyRecord,
304
+ * }} SoundingSocketConnectOptions
305
+ *
306
+ * @typedef {{
307
+ * connect(options?: SoundingSocketConnectOptions): Promise<SoundingSocketClient>,
308
+ * as(actor?: SoundingActor | string | null): {
309
+ * connect(options?: SoundingSocketConnectOptions): Promise<SoundingSocketClient>,
310
+ * },
311
+ * closeAll(): Promise<void>,
312
+ * }} SoundingSocketManager
313
+ *
314
+ * @typedef {{
315
+ * sequence(nameOrBuilder?: string | ((next: number) => any), maybeBuilder?: (next: number) => any): any,
316
+ * fake: {
317
+ * person: { fullName(): string },
318
+ * internet: { email(): string },
319
+ * lorem: {
320
+ * words(count?: number): string,
321
+ * sentence(count?: number): string,
322
+ * },
323
+ * },
324
+ * seed: any,
325
+ * sails: SoundingSailsApp,
326
+ * }} SoundingFactoryHelpers
327
+ *
328
+ * @typedef {{
329
+ * name: string,
330
+ * __soundingType: 'factory',
331
+ * definition: AnyRecord | ((helpers: SoundingFactoryHelpers) => AnyRecord),
332
+ * traits: Array<[string, AnyRecord | ((base: AnyRecord) => AnyRecord)]>,
333
+ * trait(traitName: string, patch: AnyRecord | ((base: AnyRecord) => AnyRecord)): SoundingFactoryDefinition,
334
+ * }} SoundingFactoryDefinition
335
+ *
336
+ * @typedef {{
337
+ * trait(traitName: string, patch: AnyRecord | ((base: AnyRecord) => AnyRecord)): SoundingFactoryRegistration,
338
+ * }} SoundingFactoryRegistration
339
+ *
340
+ * @typedef {PromiseLike<any> & {
341
+ * trait(name: string): SoundingBuilder,
342
+ * traits(names?: string[]): SoundingBuilder,
343
+ * with(overrides?: AnyRecord): SoundingBuilder,
344
+ * withOnly(overrides?: AnyRecord): SoundingBuilder,
345
+ * value(): any,
346
+ * catch(onRejected?: ((reason: any) => any) | null): Promise<any>,
347
+ * finally(onFinally?: (() => void) | null): Promise<any>,
348
+ * }} SoundingBuilder
349
+ *
350
+ * @typedef {{
351
+ * build(name: string, overrides?: AnyRecord): SoundingBuilder,
352
+ * create(name: string, overrides?: AnyRecord): SoundingBuilder,
353
+ * defineFactory: SoundingWorldEngine['defineFactory'],
354
+ * defineScenario: SoundingWorldEngine['defineScenario'],
355
+ * sails: SoundingSailsApp,
356
+ * sequence(nameOrBuilder?: string | ((next: number) => any), maybeBuilder?: (next: number) => any): any,
357
+ * seed: any,
358
+ * context: AnyRecord,
359
+ * }} SoundingScenarioHelpers
360
+ *
361
+ * @typedef {{
362
+ * name: string,
363
+ * __soundingType: 'scenario',
364
+ * definition: (helpers: SoundingScenarioHelpers) => Promise<AnyRecord> | AnyRecord,
365
+ * }} SoundingScenarioDefinition
366
+ *
367
+ * @typedef {{
368
+ * build(name: string, overrides?: AnyRecord, options?: { traits?: string[] }): AnyRecord,
369
+ * buildMany(name: string, count: number, overrides?: AnyRecord, options?: { traits?: string[] }): Promise<AnyRecord[]>,
370
+ * create(name: string, overrides?: AnyRecord, options?: { traits?: string[] }): SoundingBuilder,
371
+ * createMany(name: string, count: number, overrides?: AnyRecord, options?: { traits?: string[] }): Promise<AnyRecord[]>,
372
+ * defineFactory(name: string, definition: AnyRecord | ((helpers: SoundingFactoryHelpers) => AnyRecord)): SoundingFactoryRegistration,
373
+ * defineFactory(definition: SoundingFactoryDefinition): SoundingFactoryRegistration,
374
+ * defineScenario(name: string, definition: SoundingScenarioDefinition['definition']): SoundingScenarioDefinition,
375
+ * defineScenario(definition: SoundingScenarioDefinition): SoundingScenarioDefinition,
376
+ * register(definition: SoundingFactoryDefinition | SoundingScenarioDefinition): SoundingFactoryRegistration | SoundingScenarioDefinition,
377
+ * readonly current: AnyRecord | null,
378
+ * readonly factories: string[],
379
+ * readonly scenarios: string[],
380
+ * reset(options?: { preserveSequences?: boolean }): void,
381
+ * seed(value: any): any,
382
+ * sequence(nameOrBuilder?: string | ((next: number) => any), maybeBuilder?: (next: number) => any): any,
383
+ * use(name: string, context?: AnyRecord): Promise<AnyRecord>,
384
+ * }} SoundingWorldEngine
385
+ *
386
+ * @typedef {{
387
+ * user: AnyRecord,
388
+ * email: string,
389
+ * token: string,
390
+ * url: string,
391
+ * }} SoundingMagicLink
392
+ *
393
+ * @typedef {{
394
+ * response: SoundingResponse,
395
+ * email: string,
396
+ * message?: SoundingMailMessage,
397
+ * url?: string,
398
+ * }} SoundingRequestMagicLinkResult
399
+ *
400
+ * @typedef {{
401
+ * actor: AnyRecord,
402
+ * email: string,
403
+ * path: string,
404
+ * }} SoundingBrowserLoginResult
405
+ *
406
+ * @typedef {{
407
+ * actor: AnyRecord,
408
+ * email: string,
409
+ * request: SoundingRequestClient,
410
+ * response: SoundingResponse,
411
+ * }} SoundingPasswordRequestResult
412
+ *
413
+ * @typedef {{
414
+ * as(actorOrEmail: SoundingActor | string, page: SoundingPage, options?: AnyRecord): Promise<SoundingMagicLink>,
415
+ * withPassword(actorOrEmail: SoundingActor | string, page: SoundingPage, options: { password: string, rememberMe?: boolean, returnUrl?: string, [key: string]: any }): Promise<SoundingBrowserLoginResult>,
416
+ * }} SoundingLoginHelpers
417
+ *
418
+ * @typedef {{
419
+ * conventions: AnyRecord,
420
+ * resolveActor(actorOrEmail: SoundingActor | string, options?: AnyRecord): Promise<AnyRecord>,
421
+ * resolveUser(actorOrEmail: SoundingActor | string, options?: AnyRecord): Promise<AnyRecord>,
422
+ * issueMagicLink(actorOrEmail: SoundingActor | string, options?: AnyRecord): Promise<SoundingMagicLink>,
423
+ * requestMagicLink(actorOrEmail: SoundingActor | string, options?: AnyRecord): Promise<SoundingRequestMagicLinkResult>,
424
+ * request: {
425
+ * withPassword(actorOrEmail: SoundingActor | string, options: { password: string, rememberMe?: boolean, returnUrl?: string, request?: SoundingRequestClient, requestOptions?: SoundingRequestOptions, [key: string]: any }): Promise<SoundingPasswordRequestResult>,
426
+ * },
427
+ * login: SoundingLoginHelpers,
428
+ * }} SoundingAuthHelpers
429
+ *
430
+ * @typedef {((identity: string, inputs?: AnyRecord) => Promise<any>) & {
431
+ * readonly path?: string,
432
+ * [key: string]: any,
433
+ * }} SoundingHelperRunner
434
+ *
435
+ * @typedef {{
436
+ * config?: AnyRecord,
437
+ * hooks?: AnyRecord,
438
+ * helpers?: AnyRecord,
439
+ * models?: AnyRecord,
440
+ * router?: {
441
+ * route?: (...args: any[]) => any,
442
+ * },
443
+ * sounding?: SoundingRuntime,
444
+ * request?: (...args: any[]) => any,
445
+ * lower?: (done?: (error?: Error) => void) => any,
446
+ * [key: string]: any,
447
+ * }} SoundingSailsApp
448
+ *
449
+ * @typedef {{
450
+ * environments: string[],
451
+ * app: {
452
+ * path: string,
453
+ * environment: string,
454
+ * quiet: boolean,
455
+ * loadOptions?: AnyRecord,
456
+ * liftOptions: AnyRecord,
457
+ * },
458
+ * world: {
459
+ * factories: string,
460
+ * scenarios: string,
461
+ * },
462
+ * datastore: {
463
+ * mode: 'managed' | 'inherit' | string,
464
+ * identity: string,
465
+ * adapter: string,
466
+ * root: string,
467
+ * isolation: 'worker' | 'run' | string,
468
+ * },
469
+ * browser: {
470
+ * enabled: boolean,
471
+ * type: string,
472
+ * projects: string[] | Array<string | (SoundingBrowserProjectConfig & { name: string })> | Record<string, SoundingBrowserProjectConfig>,
473
+ * defaultProject: string,
474
+ * baseUrl?: string,
475
+ * launchOptions: AnyRecord,
476
+ * artifacts: SoundingBrowserArtifactsConfig,
477
+ * },
478
+ * mail: {
479
+ * capture: boolean,
480
+ * layout: string | false,
481
+ * deliver?: boolean,
482
+ * mode?: 'capture' | 'passthrough' | string,
483
+ * },
484
+ * request: {
485
+ * transport: SoundingTransport,
486
+ * baseUrl?: string,
487
+ * },
488
+ * sockets: {
489
+ * enabled: boolean,
490
+ * timeout: number,
491
+ * transports: string[],
492
+ * path: string,
493
+ * baseUrl?: string,
494
+ * headers: AnyRecord,
495
+ * initialConnectionHeaders: AnyRecord,
496
+ * },
497
+ * auth: {
498
+ * defaultActor: string,
499
+ * modelIdentity: string | null,
500
+ * sessionKey: string | null,
501
+ * worldCollection: string | null,
502
+ * password: {
503
+ * loginPath: string,
504
+ * pagePath: string,
505
+ * pageQuery: AnyRecord,
506
+ * form: {
507
+ * email: string,
508
+ * password: string,
509
+ * rememberMe: string,
510
+ * returnUrl: string,
511
+ * },
512
+ * selectors: AnyRecord,
513
+ * },
514
+ * },
515
+ * }} SoundingConfig
516
+ *
517
+ * @typedef {Partial<SoundingConfig> & {
518
+ * datastore?: SoundingConfig['datastore'] | 'inherit' | 'managed',
519
+ * }} SoundingUserConfig
520
+ *
521
+ * @typedef {{
522
+ * mode: string,
523
+ * identity: string,
524
+ * config: AnyRecord,
525
+ * }} SoundingDatastoreState
526
+ *
527
+ * @typedef {{
528
+ * bootedAt: string,
529
+ * mode: string,
530
+ * config: SoundingConfig,
531
+ * datastore: SoundingDatastoreState | null,
532
+ * mail: {
533
+ * captureEnabled: boolean,
534
+ * captureInstalled: boolean,
535
+ * },
536
+ * world: {
537
+ * loadedFiles: string[],
538
+ * },
539
+ * }} SoundingRuntimeState
540
+ *
541
+ * @typedef {{
542
+ * sails: SoundingSailsApp,
543
+ * bootedAt: string,
544
+ * mode: string,
545
+ * config: SoundingConfig,
546
+ * datastore: SoundingDatastoreState | null,
547
+ * mail: {
548
+ * captureEnabled: boolean,
549
+ * captureInstalled: boolean,
550
+ * },
551
+ * helpers: SoundingHelperRunner,
552
+ * mailbox: SoundingMailbox,
553
+ * world: SoundingWorldEngine,
554
+ * request: SoundingRequestClient,
555
+ * visit: SoundingVisitClient,
556
+ * sockets: SoundingSocketManager,
557
+ * browser: SoundingBrowserManager,
558
+ * auth: SoundingAuthHelpers,
559
+ * login: SoundingLoginHelpers,
560
+ * }} SoundingBootResult
561
+ *
562
+ * @typedef {{
563
+ * readonly config: SoundingConfig,
564
+ * readonly appPath: string,
565
+ * readonly mailbox: SoundingMailbox,
566
+ * readonly world: SoundingWorldEngine,
567
+ * readonly helpers: SoundingHelperRunner,
568
+ * readonly request: SoundingRequestClient,
569
+ * readonly visit: SoundingVisitClient,
570
+ * readonly sockets: SoundingSocketManager,
571
+ * readonly browser: SoundingBrowserManager,
572
+ * readonly auth: SoundingAuthHelpers,
573
+ * configure(): SoundingDatastoreState,
574
+ * readonly datastore: SoundingDatastoreState | null,
575
+ * boot(options?: { mode?: string }): Promise<SoundingBootResult>,
576
+ * lower(): Promise<void>,
577
+ * readonly cacheStats: {
578
+ * config: { resolutions: number },
579
+ * worldLoader: { directoryScans: number, moduleLoads: number },
580
+ * },
581
+ * invalidateCaches(): void,
582
+ * readonly state: SoundingRuntimeState | null,
583
+ * }} SoundingRuntime
584
+ *
585
+ * @typedef {{
586
+ * appPath?: string,
587
+ * environment?: string,
588
+ * liftOptions?: AnyRecord,
589
+ * SailsConstructor?: new (...args: any[]) => SoundingSailsApp,
590
+ * loadSails?: (appPath: string) => any,
591
+ * }} SoundingAppManagerOptions
592
+ *
593
+ * @typedef {{
594
+ * mode: 'load' | 'lift',
595
+ * status: 'idle' | 'loading' | 'ready' | 'error',
596
+ * runs: number,
597
+ * reuses: number,
598
+ * reloads: number,
599
+ * durationMs: number | null,
600
+ * startedAt: string | null,
601
+ * readyAt: string | null,
602
+ * error: string | null,
603
+ * }} SoundingAppLifecycleState
604
+ *
605
+ * @typedef {{
606
+ * load(options?: { reload?: boolean }): Promise<SoundingSailsApp>,
607
+ * lift(options?: { reload?: boolean }): Promise<SoundingSailsApp>,
608
+ * runtime(options?: { transport?: SoundingTransport, app?: 'load' | 'lift', reload?: boolean }): Promise<SoundingRuntime>,
609
+ * lower(): Promise<void>,
610
+ * resolveConfig(): SoundingConfig,
611
+ * readonly lifecycle: {
612
+ * load: SoundingAppLifecycleState,
613
+ * lift: SoundingAppLifecycleState,
614
+ * },
615
+ * }} SoundingAppManager
616
+ *
617
+ * @typedef {string | {
618
+ * name: string,
619
+ * context?: AnyRecord,
620
+ * }} SoundingTrialWorldOption
621
+ *
622
+ * @typedef {SoundingRequestOptions & {
623
+ * transport?: SoundingTransport,
624
+ * browser?: boolean | string | SoundingBrowserOpenOptions,
625
+ * socket?: boolean | SoundingSocketConnectOptions,
626
+ * world?: SoundingTrialWorldOption,
627
+ * concurrent?: boolean,
628
+ * concurrency?: boolean | number,
629
+ * }} SoundingTestOptions
630
+ *
631
+ * @typedef {AnyRecord & {
632
+ * t: AnyRecord,
633
+ * expect: SoundingExpect,
634
+ * sails: SoundingSailsApp,
635
+ * request: SoundingRequestClient,
636
+ * visit: SoundingVisitClient,
637
+ * sockets: SoundingSocketManager,
638
+ * auth: SoundingAuthHelpers,
639
+ * login: SoundingLoginHelpers,
640
+ * world: SoundingWorldEngine,
641
+ * mailbox: SoundingMailbox,
642
+ * browser?: AnyRecord,
643
+ * browserContext?: AnyRecord,
644
+ * page?: SoundingPage,
645
+ * get: SoundingRequestClient['get'],
646
+ * head: SoundingRequestClient['head'],
647
+ * post: SoundingRequestClient['post'],
648
+ * put: SoundingRequestClient['put'],
649
+ * patch: SoundingRequestClient['patch'],
650
+ * del: SoundingRequestClient['delete'],
651
+ * }} SoundingTrialContext
652
+ *
653
+ * @typedef {(context: SoundingTrialContext) => any | Promise<any>} SoundingTrialHandler
654
+ *
655
+ * @typedef {{
656
+ * (title: string, handler: SoundingTrialHandler): unknown;
657
+ * (title: string, options: SoundingTestOptions, handler: SoundingTrialHandler): unknown;
658
+ * }} SoundingTrialRegistrar
659
+ *
660
+ * @typedef {SoundingTrialRegistrar & {
661
+ * skip(...args: any[]): unknown,
662
+ * todo(...args: any[]): unknown,
663
+ * only?: SoundingTrialRegistrar,
664
+ * concurrent: SoundingTrialRegistrar,
665
+ * }} SoundingTest
666
+ *
667
+ * @typedef {{
668
+ * defaults: { sounding: SoundingConfig },
669
+ * configure(): void,
670
+ * initialize(done: (error?: Error) => void): void,
671
+ * [key: string]: any,
672
+ * }} SoundingSailsHook
673
+ */
674
+
675
+ module.exports = {}