@fluojs/testing 1.0.5 → 2.0.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/README.ko.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  <p><a href="./README.md"><kbd>English</kbd></a> <strong><kbd>한국어</kbd></strong></p>
4
4
 
5
- fluo 애플리케이션을 위한 기본 request-level 테스트 헬퍼, 모듈 구성, 프로바이더 오버라이드 유틸리티입니다.
5
+ Node.js 20+ fluo 애플리케이션을 위한 기본 request-level 테스트 헬퍼, 모듈 구성, 프로바이더 오버라이드 유틸리티입니다.
6
6
 
7
7
  `@fluojs/testing`은 fluo 애플리케이션 테스트를 위한 공식적인 기준(Baseline)을 제공합니다. 격리된 테스트 환경을 구축하고, 의존성을 가짜(Fake)나 목(Mock)으로 교체하며, 모듈 그래프에서 직접 컴포넌트를 resolve하거나 `createTestApp(...).request(...).send()`로 가상 HTTP 요청을 실행하여 e2e 스타일 테스트를 수행할 수 있게 합니다.
8
8
 
@@ -45,20 +45,22 @@ import { createTestApp } from '@fluojs/testing';
45
45
 
46
46
  const app = await createTestApp({ rootModule: AppModule });
47
47
 
48
- const response = await app
49
- .request('POST', '/users/')
50
- .header('x-request-id', 'test-request-1')
51
- .query('include', 'profile')
52
- .principal({ subject: 'user-1', roles: ['admin'] })
53
- .body({ name: 'Ada' })
54
- .send();
55
-
56
- expect(response.status).toBe(201);
57
-
58
- await app.close();
48
+ try {
49
+ const response = await app
50
+ .request('POST', '/users/')
51
+ .header('x-request-id', 'test-request-1')
52
+ .query('include', 'profile')
53
+ .principal({ subject: 'user-1', roles: ['admin'] })
54
+ .body({ name: 'Ada' })
55
+ .send();
56
+
57
+ expect(response.status).toBe(201);
58
+ } finally {
59
+ await app.close();
60
+ }
59
61
  ```
60
62
 
61
- 애플리케이션 route, guard, interceptor, DTO validation, request body, query parameter, header, synthetic principal, serialized response를 검증하는 기본 HTTP/e2e 스타일 경로로는 `createTestApp({ rootModule })`을 사용하세요. 하나의 slice 안에서 module wiring, provider visibility, provider/guard/interceptor override가 계약일 때는 `createTestingModule(...)`을 사용합니다.
63
+ 애플리케이션 route, guard, interceptor, DTO validation, request body, query parameter, header, synthetic principal, request-scoped provider isolation, serialized response를 검증하는 기본 HTTP/e2e 스타일 경로로는 `createTestApp({ rootModule })`을 사용하세요. 하나의 slice 안에서 module wiring, provider visibility, provider/guard/interceptor override가 계약일 때는 `createTestingModule(...)`을 사용합니다.
62
64
 
63
65
  ## 주요 패턴
64
66
 
@@ -77,7 +79,7 @@ const module = await createTestingModule({ rootModule: AppModule })
77
79
  const service = await module.resolve(UserService);
78
80
  ```
79
81
 
80
- Testing builder는 route-pipeline 테스트에서 cross-cutting behavior를 교체할 수 있도록 `overrideGuard(...)`, `overrideInterceptor(...)`, `overrideFilter(...)`도 지원합니다.
82
+ Testing builder는 route-pipeline 테스트에서 cross-cutting behavior를 교체할 수 있도록 `overrideProviders([[token, value], ...])`, `overrideGuard(...)`, `overrideInterceptor(...)`, `overrideFilter(...)`도 지원합니다. Guard와 interceptor override는 route가 같은 token을 `@UseGuards(...)` 또는 `@UseInterceptors(...)`로 참조할 때 request path에서도 안전하게 검증할 수 있습니다. Filter override는 컴파일된 module graph의 token을 교체하므로, 해당 filter가 runtime app 표면에 등록되는 경우 request-level coverage와 함께 사용하세요.
81
83
 
82
84
  `compile()`은 lifecycle hook이 있는 singleton provider에 대해 production module bootstrap과 같은 의미를 따릅니다. effective provider graph를 해석하고, testing module을 반환하기 전에 provider 순서대로 각 instance의 `onModuleInit()`을 실행한 뒤 `onApplicationBootstrap()`을 실행합니다. `get()`은 synchronous singleton 및 multi-provider 경로에서도 DI ownership 의미를 보존하므로, 반복 sync read는 같은 singleton contribution을 재사용하고 `module.container.dispose()`가 해당 instance를 계속 정리할 수 있습니다.
83
85
 
@@ -101,20 +103,22 @@ import { createTestApp } from '@fluojs/testing';
101
103
 
102
104
  const app = await createTestApp({ rootModule: AppModule });
103
105
 
104
- const response = await app
105
- .request('POST', '/users/')
106
- .header('authorization', 'Bearer test-token')
107
- .query('include', ['profile', 'settings'])
108
- .principal({ subject: 'user-1', roles: ['member'] })
109
- .body({ name: 'Ada' })
110
- .send();
111
-
112
- expect(response.status).toBe(201);
113
-
114
- await app.close();
106
+ try {
107
+ const response = await app
108
+ .request('POST', '/users/')
109
+ .header('authorization', 'Bearer test-token')
110
+ .query('include', ['profile', 'settings'])
111
+ .principal({ subject: 'user-1', roles: ['member'] })
112
+ .body({ name: 'Ada' })
113
+ .send();
114
+
115
+ expect(response.status).toBe(201);
116
+ } finally {
117
+ await app.close();
118
+ }
115
119
  ```
116
120
 
117
- `app.request(...).send()`는 수동 `FrameworkRequest`/`FrameworkResponse` stub 없이 HTTP 의미에 가까운 테스트를 작성하게 해 주므로 애플리케이션 개발자의 기본 경로입니다. `app.dispatch(...)`, `makeRequest(...)`, raw `FluoFactory.create(...)` 테스트는 adapter/runtime contract, framework internal, 또는 low-level dispatch boundary 자체를 증명해야 하는 compatibility case에 남겨 둡니다.
121
+ `app.request(...).send()`는 수동 `FrameworkRequest`/`FrameworkResponse` stub 없이 HTTP 의미에 가까운 테스트를 작성하게 해 주고 runtime dispatch와 같은 isolated request-scoped DI boundary를 생성하므로 애플리케이션 개발자의 기본 경로입니다. Assertion 실패가 runtime resource 누수로 이어지지 않도록 반환된 app은 `finally` 블록에서 닫으세요. `app.dispatch(...)`, `makeRequest(...)`, raw `FluoFactory.create(...)` 테스트는 adapter/runtime contract, framework internal, 또는 low-level dispatch boundary 자체를 증명해야 하는 compatibility case에 남겨 둡니다.
118
122
 
119
123
  `createTestApp(...)`은 runtime HTTP bootstrap과 같은 application bootstrap option을 받습니다. 여기에는 `providers`, `filters`, `converters`, `interceptors`, `middleware`, `observers`, `versioning`, diagnostics option이 포함됩니다. 테스트 헬퍼는 request-context middleware를 앞에 추가하되, 호출자가 넘긴 middleware를 같은 app middleware chain 안에 보존합니다.
120
124
 
@@ -128,7 +132,7 @@ const repo = createMock<UserRepository>({ findById: vi.fn() });
128
132
  const mailer = createDeepMock(MailService);
129
133
  ```
130
134
 
131
- `asMock(value)`는 기존 값을 mock-friendly 타입으로 좁히고, `mockToken(token, value)`는 token 기반 dependency위한 provider override tuple을 만듭니다. `createMock(..., { strict: true })`는 지정하지 않은 member 접근을 거부합니다.
135
+ `asMock(value)`는 기존 값을 mock-friendly 타입으로 좁히고, `mockToken(token, value)`는 token 기반 override위해 `{ provide: token, useValue: value }` 형태의 `ValueProvider` descriptor를 만듭니다. `createMock(..., { strict: true })`는 지정하지 않은 member 접근을 거부합니다. `DeepMocked<T>`는 root `@fluojs/testing` 패키지, `@fluojs/testing/types`, `@fluojs/testing/mock`에서 모두 노출됩니다. 세 경로는 Vitest peer declaration을 non-mock runtime helper로 끌어들이지 않으면서 같은 Vitest-compatible mock type boundary를 공유합니다. Vitest를 사용하지 않는 소비자는 `@fluojs/testing/app`, `@fluojs/testing/module`, 또는 harness subpath에서 non-mock helper만 import하세요.
132
136
 
133
137
  배포된 런타임 import가 안정적으로 해석되도록, mock 헬퍼를 사용할 워크스페이스에는 `vitest`를 함께 설치해야 합니다.
134
138
 
@@ -136,7 +140,7 @@ const mailer = createDeepMock(MailService);
136
140
 
137
141
  프레임워크 지향 플랫폼 패키지를 작성할 때는 `@fluojs/testing/platform-conformance`, `@fluojs/testing/http-adapter-portability`, `@fluojs/testing/web-runtime-adapter-portability` 같은 서브패스를 사용해 적합성 및 이식성 검증을 수행합니다.
138
142
 
139
- 이식성 하니스의 cleanup도 계약에 포함됩니다. 앱이 bootstrap된 뒤 setup, `listen()`, assertion이 실패하면 하니스는 해당 partial app을 닫습니다. `app.close()`가 실패하면 하니스는 cleanup 실패를 보고하며, setup 또는 assertion이 이미 실패한 경우에는 원래 실패와 cleanup 실패를 모두 보존하는 aggregate error를 발생시킵니다.
143
+ 이식성 하니스의 cleanup도 계약에 포함됩니다. 앱이 bootstrap된 뒤 setup, `listen()`, partial app을 노출한 run callback, assertion이 실패하면 하니스는 해당 partial app을 닫습니다. `app.close()`가 실패하면 하니스는 cleanup 실패를 보고하며, setup 또는 assertion이 이미 실패한 경우에는 원래 실패와 cleanup 실패를 모두 보존하는 aggregate error를 발생시킵니다.
140
144
 
141
145
  `HttpAdapterPortabilityHarness`와 web-runtime portability harness 메서드는 공개 어댑터 계약 체크입니다. 직접 같은 검증을 다시 만들기보다 `assertPreservesMalformedCookieValues()`, `assertSupportsSseStreaming()`, `assertPreservesRawBodyForJsonAndText()`, `assertPreservesExactRawBodyBytesForByteSensitivePayloads()`, `assertExcludesRawBodyForMultipart()`, `assertDefaultsMultipartTotalLimitToMaxBodySize()`, `assertSettlesStreamDrainWaitOnClose()`, `assertReportsConfiguredHostInStartupLogs()`, `assertReportsHttpsStartupUrl(...)`, `assertRemovesShutdownSignalListenersAfterClose()`처럼 초점이 분명한 assertion을 사용하세요.
142
146
 
@@ -165,11 +169,15 @@ fluo는 테스트가 명시적인 `rootModule`을 이름으로 지정해야 한
165
169
 
166
170
  ## 공개 API
167
171
 
168
- - **루트 패키지**: `createTestingModule(...)`, `Test.createTestingModule(...)`, `createTestApp(...)`, 모듈 introspection 헬퍼, 공용 테스트 타입
169
- - **서브패스**: `@fluojs/testing/app`, `@fluojs/testing/module`, `@fluojs/testing/http`, `@fluojs/testing/mock`, `@fluojs/testing/types`, `@fluojs/testing/vitest`, `@fluojs/testing/vitest/tooling`
172
+ - **루트 패키지**: `createTestingModule(...)`, `Test.createTestingModule(...)`, `createTestApp(...)`, 모듈 introspection 헬퍼, `DeepMocked<T>`를 포함한 공용 app/module 테스트 타입
173
+ - **서브패스**: `@fluojs/testing/app`, `@fluojs/testing/module`, `@fluojs/testing/http`, `@fluojs/testing/mock` (`DeepMocked<T>` 포함), `@fluojs/testing/types` (`DeepMocked<T>` 포함), `@fluojs/testing/vitest`, `@fluojs/testing/vitest/tooling`
170
174
  - **하니스 서브패스**: `platform-conformance`, `http-adapter-portability`, `web-runtime-adapter-portability`, `fetch-style-websocket-conformance`
171
175
  - **도구 지원**: `@fluojs/testing/vitest`의 `fluoBabelDecoratorsPlugin()` 및 `@fluojs/testing/vitest/tooling`의 Vitest workspace config helper (`vitest`와 `@babel/core`를 함께 요구)
172
176
 
177
+ Package manifest는 `engines.node >=20.0.0`을 선언합니다. 문서화된 경우 non-Node runtime 애플리케이션 테스트에서 runtime-native 도구를 사용할 수 있지만, 배포된 `@fluojs/testing` 패키지 자체는 이 Node.js engine floor를 따릅니다.
178
+
179
+ `@fluojs/testing/vitest/tooling`은 각 package의 공개 `exports`에 선언된 entrypoint만 workspace alias로 매핑합니다. Private source file, internal helper, export되지 않은 source entrypoint는 의도적으로 제외하므로 테스트가 published package 소비자에게 제공되는 import boundary와 같은 경계를 검증합니다.
180
+
173
181
  ## 관련 패키지
174
182
 
175
183
  - `@fluojs/di`: 테스트 컨테이너가 사용하는 기반 DI 시스템입니다.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  <p><strong><kbd>English</kbd></strong> <a href="./README.ko.md"><kbd>한국어</kbd></a></p>
4
4
 
5
- Default request-level testing helpers, testing module construction, and provider overrides for fluo applications.
5
+ Node.js 20+ request-level testing helpers, testing module construction, and provider overrides for fluo applications.
6
6
 
7
7
  ## Table of Contents
8
8
 
@@ -18,7 +18,7 @@ Default request-level testing helpers, testing module construction, and provider
18
18
  ## Installation
19
19
 
20
20
  ```bash
21
- npm install --save-dev @fluojs/testing vitest
21
+ pnpm add -D @fluojs/testing vitest
22
22
  ```
23
23
 
24
24
  `vitest` is a required peer dependency for the mock helpers and the `@fluojs/testing/vitest` entrypoint. `@babel/core` is declared as a peer because the Vitest decorators plugin loads Babel from the consuming workspace; package managers may surface that peer even when you only use the non-Vitest harness subpaths.
@@ -26,7 +26,7 @@ npm install --save-dev @fluojs/testing vitest
26
26
  If you use `@fluojs/testing/vitest`, install `@babel/core` in the consuming workspace as well because `fluoBabelDecoratorsPlugin()` invokes Babel at runtime. The Vitest plugin transforms `.ts`, `.tsx`, `.mts`, and `.cts` source ids after removing Vite query/hash suffixes, skips `node_modules`, and resolves the nearest root Babel config named `babel.config.cjs`, `babel.config.mjs`, `babel.config.js`, or `babel.config.json`:
27
27
 
28
28
  ```bash
29
- npm install --save-dev @babel/core
29
+ pnpm add -D @babel/core
30
30
  ```
31
31
 
32
32
  ## When to Use
@@ -43,20 +43,22 @@ import { createTestApp } from '@fluojs/testing';
43
43
 
44
44
  const app = await createTestApp({ rootModule: AppModule });
45
45
 
46
- const response = await app
47
- .request('POST', '/users/')
48
- .header('x-request-id', 'test-request-1')
49
- .query('include', 'profile')
50
- .principal({ subject: 'user-1', roles: ['admin'] })
51
- .body({ name: 'Ada' })
52
- .send();
53
-
54
- expect(response.status).toBe(201);
55
-
56
- await app.close();
46
+ try {
47
+ const response = await app
48
+ .request('POST', '/users/')
49
+ .header('x-request-id', 'test-request-1')
50
+ .query('include', 'profile')
51
+ .principal({ subject: 'user-1', roles: ['admin'] })
52
+ .body({ name: 'Ada' })
53
+ .send();
54
+
55
+ expect(response.status).toBe(201);
56
+ } finally {
57
+ await app.close();
58
+ }
57
59
  ```
58
60
 
59
- Use `createTestApp({ rootModule })` as the default HTTP/e2e-style path for application routes, guards, interceptors, DTO validation, request bodies, query parameters, headers, synthetic principals, and serialized responses. Reach for `createTestingModule(...)` when the contract is module wiring, provider visibility, or provider/guard/interceptor overrides inside one slice.
61
+ Use `createTestApp({ rootModule })` as the default HTTP/e2e-style path for application routes, guards, interceptors, DTO validation, request bodies, query parameters, headers, synthetic principals, request-scoped provider isolation, and serialized responses. Reach for `createTestingModule(...)` when the contract is module wiring, provider visibility, or provider/guard/interceptor overrides inside one slice.
60
62
 
61
63
  ## Common Patterns
62
64
 
@@ -75,7 +77,7 @@ const module = await createTestingModule({ rootModule: AppModule })
75
77
  const service = await module.resolve(UserService);
76
78
  ```
77
79
 
78
- The testing builder also supports `overrideGuard(...)`, `overrideInterceptor(...)`, and `overrideFilter(...)` for route-pipeline tests that need to replace cross-cutting behavior.
80
+ The testing builder also supports `overrideProviders([[token, value], ...])`, `overrideGuard(...)`, `overrideInterceptor(...)`, and `overrideFilter(...)` for route-pipeline tests that need to replace cross-cutting behavior. Guard and interceptor overrides are request-path safe when the route references the same token via `@UseGuards(...)` or `@UseInterceptors(...)`; filter overrides replace the token in the compiled module graph and should be paired with request-level coverage where that filter is registered in the runtime app surface.
79
81
 
80
82
  `compile()` follows production module-bootstrap semantics for lifecycle-bearing singleton providers: it resolves the effective provider graph, runs `onModuleInit()` for each resolved instance, then runs `onApplicationBootstrap()` in the same provider order before the testing module is returned. `get()` keeps DI ownership semantics for synchronous singleton and multi-provider paths, so repeated sync reads reuse the same singleton contributions and `module.container.dispose()` can still clean them up.
81
83
 
@@ -99,20 +101,22 @@ import { createTestApp } from '@fluojs/testing';
99
101
 
100
102
  const app = await createTestApp({ rootModule: AppModule });
101
103
 
102
- const response = await app
103
- .request('POST', '/users/')
104
- .header('authorization', 'Bearer test-token')
105
- .query('include', ['profile', 'settings'])
106
- .principal({ subject: 'user-1', roles: ['member'] })
107
- .body({ name: 'Ada' })
108
- .send();
109
-
110
- expect(response.status).toBe(201);
111
-
112
- await app.close();
104
+ try {
105
+ const response = await app
106
+ .request('POST', '/users/')
107
+ .header('authorization', 'Bearer test-token')
108
+ .query('include', ['profile', 'settings'])
109
+ .principal({ subject: 'user-1', roles: ['member'] })
110
+ .body({ name: 'Ada' })
111
+ .send();
112
+
113
+ expect(response.status).toBe(201);
114
+ } finally {
115
+ await app.close();
116
+ }
113
117
  ```
114
118
 
115
- `app.request(...).send()` is the preferred app-developer path because it keeps tests close to HTTP semantics without manual `FrameworkRequest`/`FrameworkResponse` stubs. Keep `app.dispatch(...)`, `makeRequest(...)`, and raw `FluoFactory.create(...)` tests for adapter/runtime contracts, framework internals, or compatibility cases where the low-level dispatch boundary itself is what the test must prove.
119
+ `app.request(...).send()` is the preferred app-developer path because it keeps tests close to HTTP semantics without manual `FrameworkRequest`/`FrameworkResponse` stubs and creates the same isolated request-scoped DI boundary as runtime dispatch. Close the returned app from a `finally` block so assertion failures do not leak runtime resources. Keep `app.dispatch(...)`, `makeRequest(...)`, and raw `FluoFactory.create(...)` tests for adapter/runtime contracts, framework internals, or compatibility cases where the low-level dispatch boundary itself is what the test must prove.
116
120
 
117
121
  `createTestApp(...)` accepts the same application bootstrap options as the runtime HTTP bootstrap, including `providers`, `filters`, `converters`, `interceptors`, `middleware`, `observers`, `versioning`, and diagnostics options. The testing helper prepends its request-context middleware while preserving caller-provided middleware in the same app middleware chain.
118
122
 
@@ -126,7 +130,7 @@ const repo = createMock<UserRepository>({ findById: vi.fn() });
126
130
  const mailer = createDeepMock(MailService);
127
131
  ```
128
132
 
129
- `asMock(value)` narrows an existing value to a mock-friendly type, and `mockToken(token, value)` creates a provider override tuple for token-based dependencies. `createMock(..., { strict: true })` rejects access to unspecified members.
133
+ `asMock(value)` narrows an existing value to a mock-friendly type, and `mockToken(token, value)` creates a `ValueProvider` descriptor shaped as `{ provide: token, useValue: value }` for token-based overrides. `createMock(..., { strict: true })` rejects access to unspecified members. `DeepMocked<T>` is exposed from the root `@fluojs/testing` package, `@fluojs/testing/types`, and `@fluojs/testing/mock`; all three paths intentionally share the same Vitest-compatible mock type boundary without importing Vitest peer declarations through non-mock runtime helpers. Consumers that do not use Vitest should import only non-mock helpers from `@fluojs/testing/app`, `@fluojs/testing/module`, or the harness subpaths.
130
134
 
131
135
  Install `vitest` in the consuming workspace before using the mock helpers so the published runtime import resolves consistently.
132
136
 
@@ -134,7 +138,7 @@ Install `vitest` in the consuming workspace before using the mock helpers so the
134
138
 
135
139
  Use subpaths like `@fluojs/testing/platform-conformance`, `@fluojs/testing/http-adapter-portability`, and `@fluojs/testing/web-runtime-adapter-portability` when authoring framework-facing platform packages.
136
140
 
137
- Portability harness cleanup is part of the contract: if setup, `listen()`, or an assertion fails after an app has been bootstrapped, the harness closes that partial app. If `app.close()` fails, the harness reports that cleanup failure, and when setup or an assertion already failed it raises an aggregate error that preserves both the original failure and the cleanup failure.
141
+ Portability harness cleanup is part of the contract: if setup, `listen()`, a run callback that surfaces a partial app, or an assertion fails after an app has been bootstrapped, the harness closes that partial app. If `app.close()` fails, the harness reports that cleanup failure, and when setup or an assertion already failed it raises an aggregate error that preserves both the original failure and the cleanup failure.
138
142
 
139
143
  `HttpAdapterPortabilityHarness` and web-runtime portability harness methods are the public adapter contract checks. Prefer focused assertions such as `assertPreservesMalformedCookieValues()`, `assertSupportsSseStreaming()`, `assertPreservesRawBodyForJsonAndText()`, `assertPreservesExactRawBodyBytesForByteSensitivePayloads()`, `assertExcludesRawBodyForMultipart()`, `assertDefaultsMultipartTotalLimitToMaxBodySize()`, `assertSettlesStreamDrainWaitOnClose()`, `assertReportsConfiguredHostInStartupLogs()`, `assertReportsHttpsStartupUrl(...)`, and `assertRemovesShutdownSignalListenersAfterClose()` instead of hand-rolled equivalents.
140
144
 
@@ -163,11 +167,15 @@ fluo differs from NestJS by requiring tests to name an explicit `rootModule`. Th
163
167
 
164
168
  ## Public API
165
169
 
166
- - **Root package**: `createTestingModule(...)`, `Test.createTestingModule(...)`, `createTestApp(...)`, module introspection helpers, shared testing types
167
- - **Subpaths**: `@fluojs/testing/app`, `@fluojs/testing/module`, `@fluojs/testing/http`, `@fluojs/testing/mock`, `@fluojs/testing/types`, `@fluojs/testing/vitest`, `@fluojs/testing/vitest/tooling`
170
+ - **Root package**: `createTestingModule(...)`, `Test.createTestingModule(...)`, `createTestApp(...)`, module introspection helpers, and shared app/module testing types including `DeepMocked<T>`
171
+ - **Subpaths**: `@fluojs/testing/app`, `@fluojs/testing/module`, `@fluojs/testing/http`, `@fluojs/testing/mock` (including `DeepMocked<T>`), `@fluojs/testing/types` (including `DeepMocked<T>`), `@fluojs/testing/vitest`, `@fluojs/testing/vitest/tooling`
168
172
  - **Harness subpaths**: `platform-conformance`, `http-adapter-portability`, `web-runtime-adapter-portability`, `fetch-style-websocket-conformance`
169
173
  - **Tooling**: `@fluojs/testing/vitest` with `fluoBabelDecoratorsPlugin()` and `@fluojs/testing/vitest/tooling` with Vitest workspace config helpers (requires `vitest` and `@babel/core` in the consuming workspace)
170
174
 
175
+ The package manifest declares `engines.node >=20.0.0`. Non-Node runtime application tests can still use runtime-native tools where documented, but the published `@fluojs/testing` package itself is governed by that Node.js engine floor.
176
+
177
+ `@fluojs/testing/vitest/tooling` maps workspace aliases only for each package's declared public `exports`. Private source files, internal helpers, and unexported source entrypoints are intentionally excluded so tests exercise the same import boundaries that consumers receive from published packages.
178
+
171
179
  ## Related Packages
172
180
 
173
181
  - `@fluojs/di`: powers provider resolution in compiled test containers
package/dist/app.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,OAAO,EACP,yBAAyB,EAE1B,MAAM,YAAY,CAAC;AA6BpB;;;;;;;;;;;;GAYG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,OAAO,CAAC,CAyBxF"}
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,OAAO,EACP,yBAAyB,EAE1B,MAAM,YAAY,CAAC;AA6BpB;;;;;;;;;;;;GAYG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,OAAO,CAAC,CA2BxF"}
package/dist/app.js CHANGED
@@ -38,6 +38,7 @@ export async function createTestApp(options) {
38
38
  ...options,
39
39
  middleware: [createTestRequestContextMiddleware(), ...(options.middleware ?? [])]
40
40
  });
41
+ let closePromise;
41
42
  const request = (methodOrRequest, pathOrOptions, options) => {
42
43
  return createRequestBuilder(app.dispatcher, normalizeRequestInput(methodOrRequest, pathOrOptions, options));
43
44
  };
@@ -48,7 +49,8 @@ export async function createTestApp(options) {
48
49
  request,
49
50
  dispatch,
50
51
  close: async () => {
51
- await app.close();
52
+ closePromise ??= app.close();
53
+ await closePromise;
52
54
  }
53
55
  };
54
56
  }
@@ -1 +1 @@
1
- {"version":3,"file":"platform-conformance.d.ts","sourceRoot":"","sources":["../../src/conformance/platform-conformance.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,KAAK,EACV,iBAAiB,EACjB,uBAAuB,EACvB,gBAAgB,EAChB,aAAa,EACb,wBAAwB,EACzB,MAAM,iBAAiB,CAAC;AAEzB;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C,eAAe,EAAE,MAAM,iBAAiB,CAAC;IACzC,UAAU,EAAE,CAAC,SAAS,EAAE,iBAAiB,KAAK,YAAY,CAAC,IAAI,CAAC,CAAC;IACjE,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,qCAAqC;IACpD,OAAO,CAAC,EAAE,CACR,SAAS,EAAE,iBAAiB,EAC5B,UAAU,EAAE,wBAAwB,KACjC,YAAY,CAAC,SAAS,uBAAuB,EAAE,CAAC,CAAC;IACtD,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,2BAA2B,CAAC,EAAE,aAAa,CAAC,uBAAuB,CAAC,UAAU,CAAC,CAAC,CAAC;CAClF;AAED;;GAEG;AACH,MAAM,WAAW,kCAAkC;IACjD,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;IACrD,oBAAoB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,gBAAgB,KAAK,gBAAgB,CAAC;CAC7D;AAED;;GAEG;AACH,MAAM,WAAW,iCAAiC;IAChD,4BAA4B,CAAC,EAAE,CAAC,SAAS,EAAE,iBAAiB,KAAK,YAAY,CAAC,OAAO,CAAC,CAAC;IACvF,eAAe,EAAE,MAAM,iBAAiB,CAAC;IACzC,WAAW,CAAC,EAAE,qCAAqC,CAAC;IACpD,SAAS,CAAC,EAAE;QACV,QAAQ,EAAE,2BAA2B,CAAC;QACtC,MAAM,EAAE,2BAA2B,CAAC;KACrC,CAAC;IACF,QAAQ,CAAC,EAAE,kCAAkC,CAAC;CAC/C;AAmGD;;GAEG;AACH,qBAAa,0BAA0B;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,iCAAiC;IAEjE,yCAAyC,IAAI,OAAO,CAAC,IAAI,CAAC;IA4B1D,0BAA0B,IAAI,OAAO,CAAC,IAAI,CAAC;IA6B3C,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC;IAkCvC,2CAA2C,IAAI,OAAO,CAAC,IAAI,CAAC;IAoC5D,uBAAuB,IAAI,OAAO,CAAC,IAAI,CAAC;IA4CxC,uBAAuB,IAAI,OAAO,CAAC,IAAI,CAAC;IAgBxC,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;CAQjC;AAED;;;;;GAKG;AACH,wBAAgB,gCAAgC,CAC9C,OAAO,EAAE,iCAAiC,GACzC,0BAA0B,CAE5B"}
1
+ {"version":3,"file":"platform-conformance.d.ts","sourceRoot":"","sources":["../../src/conformance/platform-conformance.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,KAAK,EACV,iBAAiB,EACjB,uBAAuB,EACvB,gBAAgB,EAChB,aAAa,EACb,wBAAwB,EACzB,MAAM,iBAAiB,CAAC;AAEzB;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C,eAAe,EAAE,MAAM,iBAAiB,CAAC;IACzC,UAAU,EAAE,CAAC,SAAS,EAAE,iBAAiB,KAAK,YAAY,CAAC,IAAI,CAAC,CAAC;IACjE,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,qCAAqC;IACpD,OAAO,CAAC,EAAE,CACR,SAAS,EAAE,iBAAiB,EAC5B,UAAU,EAAE,wBAAwB,KACjC,YAAY,CAAC,SAAS,uBAAuB,EAAE,CAAC,CAAC;IACtD,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,2BAA2B,CAAC,EAAE,aAAa,CAAC,uBAAuB,CAAC,UAAU,CAAC,CAAC,CAAC;CAClF;AAED;;GAEG;AACH,MAAM,WAAW,kCAAkC;IACjD,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;IACrD,oBAAoB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,gBAAgB,KAAK,gBAAgB,CAAC;CAC7D;AAED;;GAEG;AACH,MAAM,WAAW,iCAAiC;IAChD,4BAA4B,CAAC,EAAE,CAAC,SAAS,EAAE,iBAAiB,KAAK,YAAY,CAAC,OAAO,CAAC,CAAC;IACvF,eAAe,EAAE,MAAM,iBAAiB,CAAC;IACzC,WAAW,CAAC,EAAE,qCAAqC,CAAC;IACpD,SAAS,CAAC,EAAE;QACV,QAAQ,EAAE,2BAA2B,CAAC;QACtC,MAAM,EAAE,2BAA2B,CAAC;KACrC,CAAC;IACF,QAAQ,CAAC,EAAE,kCAAkC,CAAC;CAC/C;AAmGD;;GAEG;AACH,qBAAa,0BAA0B;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,iCAAiC;IAEjE,yCAAyC,IAAI,OAAO,CAAC,IAAI,CAAC;IAwC1D,0BAA0B,IAAI,OAAO,CAAC,IAAI,CAAC;IA6B3C,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC;IAyCvC,2CAA2C,IAAI,OAAO,CAAC,IAAI,CAAC;IAoC5D,uBAAuB,IAAI,OAAO,CAAC,IAAI,CAAC;IAoDxC,uBAAuB,IAAI,OAAO,CAAC,IAAI,CAAC;IAwBxC,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;CAQjC;AAED;;;;;GAKG;AACH,wBAAgB,gCAAgC,CAC9C,OAAO,EAAE,iCAAiC,GACzC,0BAA0B,CAE5B"}
@@ -91,20 +91,27 @@ export class PlatformConformanceHarness {
91
91
  }
92
92
  async assertValidationHasNoLongLivedSideEffects() {
93
93
  const component = this.options.createComponent();
94
- const beforeState = component.state();
95
- const beforeEffects = this.options.captureValidationSideEffects ? await this.options.captureValidationSideEffects(component) : undefined;
96
- await component.validate();
97
- const afterState = component.state();
98
- if (beforeState !== afterState) {
99
- throw new Error(`validate() must not transition component state. Expected "${beforeState}" but received "${afterState}".`);
100
- }
101
- if (!this.options.captureValidationSideEffects) {
102
- return;
103
- }
104
- const compare = this.options.snapshot?.compare ?? defaultCompare;
105
- const afterEffects = await this.options.captureValidationSideEffects(component);
106
- if (!compare(beforeEffects, afterEffects)) {
107
- throw new Error('validate() introduced long-lived side effects.');
94
+ let assertionError;
95
+ try {
96
+ const beforeState = component.state();
97
+ const beforeEffects = this.options.captureValidationSideEffects ? await this.options.captureValidationSideEffects(component) : undefined;
98
+ await component.validate();
99
+ const afterState = component.state();
100
+ if (beforeState !== afterState) {
101
+ throw new Error(`validate() must not transition component state. Expected "${beforeState}" but received "${afterState}".`);
102
+ }
103
+ if (!this.options.captureValidationSideEffects) {
104
+ return;
105
+ }
106
+ const compare = this.options.snapshot?.compare ?? defaultCompare;
107
+ const afterEffects = await this.options.captureValidationSideEffects(component);
108
+ if (!compare(beforeEffects, afterEffects)) {
109
+ throw new Error('validate() introduced long-lived side effects.');
110
+ }
111
+ } catch (error) {
112
+ assertionError = error;
113
+ } finally {
114
+ await runCleanupWithAssertionContext(() => component.stop(), 'stop() after validation side-effect check', assertionError);
108
115
  }
109
116
  }
110
117
  async assertStartIsDeterministic() {
@@ -133,27 +140,34 @@ export class PlatformConformanceHarness {
133
140
  async assertStopIsIdempotent() {
134
141
  const component = this.options.createComponent();
135
142
  const compare = this.options.snapshot?.compare ?? defaultCompare;
136
- const startOutcome = await captureOutcome(() => component.start());
137
- if (!startOutcome.ok) {
138
- throw new Error(`stop() idempotency check requires a startable component: ${startOutcome.message}`);
139
- }
140
- const firstStop = await captureOutcome(() => component.stop());
141
- if (!firstStop.ok) {
142
- throw new Error(`first stop() call failed: ${firstStop.message}`);
143
- }
144
- const firstState = component.state();
145
- const firstSnapshot = component.snapshot();
146
- const secondStop = await captureOutcome(() => component.stop());
147
- if (!secondStop.ok) {
148
- throw new Error(`stop() is not idempotent: second call failed with "${secondStop.message}".`);
149
- }
150
- const secondState = component.state();
151
- const secondSnapshot = component.snapshot();
152
- if (firstState !== secondState) {
153
- throw new Error(`stop() changed state across duplicate calls (${firstState} -> ${secondState}).`);
154
- }
155
- if (!compare(firstSnapshot, secondSnapshot)) {
156
- throw new Error('stop() is not idempotent: duplicate calls changed component snapshot output.');
143
+ let assertionError;
144
+ try {
145
+ const startOutcome = await captureOutcome(() => component.start());
146
+ if (!startOutcome.ok) {
147
+ throw new Error(`stop() idempotency check requires a startable component: ${startOutcome.message}`);
148
+ }
149
+ const firstStop = await captureOutcome(() => component.stop());
150
+ if (!firstStop.ok) {
151
+ throw new Error(`first stop() call failed: ${firstStop.message}`);
152
+ }
153
+ const firstState = component.state();
154
+ const firstSnapshot = component.snapshot();
155
+ const secondStop = await captureOutcome(() => component.stop());
156
+ if (!secondStop.ok) {
157
+ throw new Error(`stop() is not idempotent: second call failed with "${secondStop.message}".`);
158
+ }
159
+ const secondState = component.state();
160
+ const secondSnapshot = component.snapshot();
161
+ if (firstState !== secondState) {
162
+ throw new Error(`stop() changed state across duplicate calls (${firstState} -> ${secondState}).`);
163
+ }
164
+ if (!compare(firstSnapshot, secondSnapshot)) {
165
+ throw new Error('stop() is not idempotent: duplicate calls changed component snapshot output.');
166
+ }
167
+ } catch (error) {
168
+ assertionError = error;
169
+ } finally {
170
+ await runCleanupWithAssertionContext(() => component.stop(), 'final stop() after stop() idempotency check', assertionError);
157
171
  }
158
172
  }
159
173
  async assertSnapshotSafeInDegradedAndFailedStates() {
@@ -178,44 +192,58 @@ export class PlatformConformanceHarness {
178
192
  }
179
193
  async assertStableDiagnostics() {
180
194
  const component = this.options.createComponent();
181
- const validation = await component.validate();
182
- const diagnostics = [...validation.issues, ...(validation.warnings ?? [])];
183
- if (this.options.diagnostics?.collect) {
184
- const extra = await this.options.diagnostics.collect(component, validation);
185
- diagnostics.push(...extra);
186
- }
187
- const requiredFixHintSeverities = this.options.diagnostics?.requireFixHintForSeverities ?? DEFAULT_REQUIRED_FIX_HINT_SEVERITIES;
188
- for (const issue of diagnostics) {
189
- if (issue.code.trim().length === 0) {
190
- throw new Error(`Diagnostics must provide a stable non-empty code. Received message: ${issue.message}`);
195
+ let assertionError;
196
+ try {
197
+ const validation = await component.validate();
198
+ const diagnostics = [...validation.issues, ...(validation.warnings ?? [])];
199
+ if (this.options.diagnostics?.collect) {
200
+ const extra = await this.options.diagnostics.collect(component, validation);
201
+ diagnostics.push(...extra);
191
202
  }
192
- if (requiredFixHintSeverities.includes(issue.severity) && (!issue.fixHint || issue.fixHint.trim().length === 0)) {
193
- throw new Error(`Diagnostic ${issue.code} (${issue.severity}) must provide a fixHint. Message: ${issue.message}`);
203
+ const requiredFixHintSeverities = this.options.diagnostics?.requireFixHintForSeverities ?? DEFAULT_REQUIRED_FIX_HINT_SEVERITIES;
204
+ for (const issue of diagnostics) {
205
+ if (issue.code.trim().length === 0) {
206
+ throw new Error(`Diagnostics must provide a stable non-empty code. Received message: ${issue.message}`);
207
+ }
208
+ if (requiredFixHintSeverities.includes(issue.severity) && (!issue.fixHint || issue.fixHint.trim().length === 0)) {
209
+ throw new Error(`Diagnostic ${issue.code} (${issue.severity}) must provide a fixHint. Message: ${issue.message}`);
210
+ }
194
211
  }
195
- }
196
- const expectedCodes = this.options.diagnostics?.expectedCodes;
197
- if (!expectedCodes) {
198
- return;
199
- }
200
- const normalizeCodes = codes => [...new Set(codes)].sort();
201
- const actualCodes = normalizeCodes(diagnostics.map(diagnostic => diagnostic.code));
202
- const normalizedExpectedCodes = normalizeCodes(expectedCodes);
203
- if (!defaultCompare(actualCodes, normalizedExpectedCodes)) {
204
- const actualDetails = diagnostics.map(diagnostic => `${diagnostic.code}(${diagnostic.severity}): ${diagnostic.message}`).join('; ');
205
- throw new Error(`Diagnostic code set changed. Expected [${normalizedExpectedCodes.join(', ')}] but received [${actualCodes.join(', ')}]. Actual diagnostics: ${actualDetails}`);
212
+ const expectedCodes = this.options.diagnostics?.expectedCodes;
213
+ if (!expectedCodes) {
214
+ return;
215
+ }
216
+ const normalizeCodes = codes => [...new Set(codes)].sort();
217
+ const actualCodes = normalizeCodes(diagnostics.map(diagnostic => diagnostic.code));
218
+ const normalizedExpectedCodes = normalizeCodes(expectedCodes);
219
+ if (!defaultCompare(actualCodes, normalizedExpectedCodes)) {
220
+ const actualDetails = diagnostics.map(diagnostic => `${diagnostic.code}(${diagnostic.severity}): ${diagnostic.message}`).join('; ');
221
+ throw new Error(`Diagnostic code set changed. Expected [${normalizedExpectedCodes.join(', ')}] but received [${actualCodes.join(', ')}]. Actual diagnostics: ${actualDetails}`);
222
+ }
223
+ } catch (error) {
224
+ assertionError = error;
225
+ } finally {
226
+ await runCleanupWithAssertionContext(() => component.stop(), 'stop() after stable diagnostics check', assertionError);
206
227
  }
207
228
  }
208
229
  async assertSnapshotSanitized() {
209
230
  const component = this.options.createComponent();
210
- const snapshot = component.snapshot();
211
- const sanitize = this.options.snapshot?.sanitize;
212
- const candidate = sanitize ? sanitize(snapshot) : snapshot;
213
- const forbiddenPatterns = this.options.snapshot?.forbiddenKeyPatterns ?? DEFAULT_FORBIDDEN_KEY_PATTERNS;
214
- const allowPatterns = this.options.snapshot?.allowKeyPatterns ?? [];
215
- const violations = [];
216
- collectForbiddenKeyPaths(candidate, forbiddenPatterns, allowPatterns, '', violations);
217
- if (violations.length > 0) {
218
- throw new Error(`snapshot() contains unsanitized keys: ${violations.join(', ')}`);
231
+ let assertionError;
232
+ try {
233
+ const snapshot = component.snapshot();
234
+ const sanitize = this.options.snapshot?.sanitize;
235
+ const candidate = sanitize ? sanitize(snapshot) : snapshot;
236
+ const forbiddenPatterns = this.options.snapshot?.forbiddenKeyPatterns ?? DEFAULT_FORBIDDEN_KEY_PATTERNS;
237
+ const allowPatterns = this.options.snapshot?.allowKeyPatterns ?? [];
238
+ const violations = [];
239
+ collectForbiddenKeyPaths(candidate, forbiddenPatterns, allowPatterns, '', violations);
240
+ if (violations.length > 0) {
241
+ throw new Error(`snapshot() contains unsanitized keys: ${violations.join(', ')}`);
242
+ }
243
+ } catch (error) {
244
+ assertionError = error;
245
+ } finally {
246
+ await runCleanupWithAssertionContext(() => component.stop(), 'stop() after snapshot sanitization check', assertionError);
219
247
  }
220
248
  }
221
249
  async assertAll() {
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export * from './app.js';
2
2
  export { Test, createTestingModule, extractModuleProviders, extractModuleControllers, extractModuleImports } from './module.js';
3
- export * from './types.js';
3
+ export type { DeepMocked, OverrideProviderBuilder, TestApp, TestingApplicationOptions, TestingModuleBuilder, TestingModuleOptions, TestingModuleRef, TestRequestOptions, } from './types.js';
4
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,OAAO,EAAE,IAAI,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,wBAAwB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAChI,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,OAAO,EAAE,IAAI,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,wBAAwB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAChI,YAAY,EACV,UAAU,EACV,uBAAuB,EACvB,OAAO,EACP,yBAAyB,EACzB,oBAAoB,EACpB,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -1,3 +1,2 @@
1
1
  export * from './app.js';
2
- export { Test, createTestingModule, extractModuleProviders, extractModuleControllers, extractModuleImports } from './module.js';
3
- export * from './types.js';
2
+ export { Test, createTestingModule, extractModuleProviders, extractModuleControllers, extractModuleImports } from './module.js';
@@ -0,0 +1,2 @@
1
+ export type { DeepMocked } from './types.js';
2
+ //# sourceMappingURL=mock-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mock-types.d.ts","sourceRoot":"","sources":["../src/mock-types.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
package/dist/mock.d.ts CHANGED
@@ -1,7 +1,8 @@
1
- import type { Mock } from 'vitest';
2
1
  import type { Token } from '@fluojs/core';
3
2
  import type { ValueProvider } from '@fluojs/di';
4
- import type { DeepMocked } from './types.js';
3
+ import type { Mock } from 'vitest';
4
+ import type { DeepMocked } from './mock-types.js';
5
+ export type { DeepMocked } from './mock-types.js';
5
6
  /**
6
7
  * Defines the mocked methods type.
7
8
  */
@@ -1 +1 @@
1
- {"version":3,"file":"mock.d.ts","sourceRoot":"","sources":["../src/mock.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAEnC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C;;GAEG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI;KAC5B,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CAC/E,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,EACzC,OAAO,GAAE,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAM,EACvC,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GACjC,aAAa,CAAC,CAAC,CAAC,CAsBlB;AAED;;;;;GAKG;AACH,wBAAgB,MAAM,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,EAAE,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAE9E;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,MAAM,EAAE,IAAI,EAAE,KAAK,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAkBnG;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,GAAE,OAAO,CAAC,CAAC,CAAM,GAAG,aAAa,CAAC,CAAC,CAAC,CAExF"}
1
+ {"version":3,"file":"mock.d.ts","sourceRoot":"","sources":["../src/mock.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAGnC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,YAAY,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD;;GAEG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI;KAC5B,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CAC/E,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,EACzC,OAAO,GAAE,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAM,EACvC,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GACjC,aAAa,CAAC,CAAC,CAAC,CAsBlB;AAED;;;;;GAKG;AACH,wBAAgB,MAAM,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,EAAE,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAE9E;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,MAAM,EAAE,IAAI,EAAE,KAAK,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAkBnG;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,GAAE,OAAO,CAAC,CAAC,CAAM,GAAG,aAAa,CAAC,CAAC,CAAC,CAExF"}
@@ -1 +1 @@
1
- {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,SAAS,EAKd,KAAK,QAAQ,EAEd,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAqC,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAMrF,OAAO,KAAK,EAA2B,oBAAoB,EAAE,oBAAoB,EAAoB,MAAM,YAAY,CAAC;AAExH;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,UAAU,GAAG,QAAQ,EAAE,CAQzE;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,UAAU,GAAG,SAAS,EAAE,CAQ5E;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,UAAU,GAAG,UAAU,EAAE,CAQzE;AA+vBD;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,oBAAoB,CAEvF;AAED;;GAEG;AACH,eAAO,MAAM,IAAI;;CAEhB,CAAC"}
1
+ {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,SAAS,EAKd,KAAK,QAAQ,EAGd,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAGV,UAAU,EACX,MAAM,iBAAiB,CAAC;AAMzB,OAAO,KAAK,EAA2B,oBAAoB,EAAE,oBAAoB,EAAoB,MAAM,YAAY,CAAC;AAYxH;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,UAAU,GAAG,QAAQ,EAAE,CAQzE;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,UAAU,GAAG,SAAS,EAAE,CAQ5E;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,UAAU,GAAG,UAAU,EAAE,CAQzE;AAyuBD;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,oBAAoB,CAEvF;AAED;;GAEG;AACH,eAAO,MAAM,IAAI;;CAEhB,CAAC"}