@fluojs/testing 1.0.0-beta.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/LICENSE +21 -0
- package/README.ko.md +117 -0
- package/README.md +115 -0
- package/dist/app.d.ts +16 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +54 -0
- package/dist/babel-decorators-plugin.d.ts +36 -0
- package/dist/babel-decorators-plugin.d.ts.map +1 -0
- package/dist/babel-decorators-plugin.js +67 -0
- package/dist/conformance/fetch-style-websocket-conformance.d.ts +14 -0
- package/dist/conformance/fetch-style-websocket-conformance.d.ts.map +1 -0
- package/dist/conformance/fetch-style-websocket-conformance.js +34 -0
- package/dist/conformance/platform-conformance.d.ts +42 -0
- package/dist/conformance/platform-conformance.d.ts.map +1 -0
- package/dist/conformance/platform-conformance.js +193 -0
- package/dist/http.d.ts +73 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +239 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/mock.d.ts +26 -0
- package/dist/mock.d.ts.map +1 -0
- package/dist/mock.js +60 -0
- package/dist/module.d.ts +45 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +405 -0
- package/dist/portability/http-adapter-portability.d.ts +83 -0
- package/dist/portability/http-adapter-portability.d.ts.map +1 -0
- package/dist/portability/http-adapter-portability.js +528 -0
- package/dist/portability/web-runtime-adapter-portability.d.ts +26 -0
- package/dist/portability/web-runtime-adapter-portability.d.ts.map +1 -0
- package/dist/portability/web-runtime-adapter-portability.js +260 -0
- package/dist/types.d.ts +76 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/vitest.d.ts +9 -0
- package/dist/vitest.d.ts.map +1 -0
- package/dist/vitest.js +11 -0
- package/package.json +102 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 fluo contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.ko.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# @fluojs/testing
|
|
2
|
+
|
|
3
|
+
<p><a href="./README.md"><kbd>English</kbd></a> <strong><kbd>한국어</kbd></strong></p>
|
|
4
|
+
|
|
5
|
+
fluo 애플리케이션 테스트를 위한 모듈 구성 및 프로바이더 오버라이드 유틸리티입니다.
|
|
6
|
+
|
|
7
|
+
`@fluojs/testing`은 fluo 애플리케이션 테스트를 위한 공식적인 기준(Baseline)을 제공합니다. 격리된 테스트 환경을 구축하고, 의존성을 가짜(Fake)나 목(Mock)으로 교체하며, 모듈 그래프에서 직접 컴포넌트를 resolve하거나 가상 HTTP 요청을 디스패치하여 통합 테스트를 수행할 수 있게 합니다.
|
|
8
|
+
|
|
9
|
+
## 목차
|
|
10
|
+
|
|
11
|
+
- [설치](#설치)
|
|
12
|
+
- [사용 시점](#사용-시점)
|
|
13
|
+
- [빠른 시작](#빠른-시작)
|
|
14
|
+
- [주요 패턴](#주요-패턴)
|
|
15
|
+
- [공개 API](#공개-api)
|
|
16
|
+
- [관련 패키지](#관련-패키지)
|
|
17
|
+
- [예제 소스](#예제-소스)
|
|
18
|
+
|
|
19
|
+
## 설치
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pnpm add -D @fluojs/testing vitest
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`vitest`는 mock 헬퍼와 `@fluojs/testing/vitest` 엔트리포인트가 요구하는 peer dependency입니다.
|
|
26
|
+
|
|
27
|
+
`@fluojs/testing/vitest`를 사용할 때는 `fluoBabelDecoratorsPlugin()`이 런타임에 Babel을 호출하므로, 사용하는 워크스페이스에 `@babel/core`도 함께 설치해야 합니다.
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pnpm add -D @babel/core
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## 사용 시점
|
|
34
|
+
|
|
35
|
+
- 프로덕션 모듈 트리를 모방하는 테스트 컨테이너를 생성해야 할 때.
|
|
36
|
+
- 실제 서비스(데이터베이스, 메일러, 외부 API 등)를 테스트 더블로 교체하고 싶을 때.
|
|
37
|
+
- 라이브러리나 어댑터 패키지에서 책임별 서브패스를 통해 적합성(conformance) 및 이식성(portability) 하니스를 사용해야 할 때.
|
|
38
|
+
- 스타터 템플릿이나 애플리케이션 테스트에 사용할 안정적인 unit / integration / e2e 스타일 기준선이 필요할 때.
|
|
39
|
+
|
|
40
|
+
## 빠른 시작
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
import { createTestingModule } from '@fluojs/testing';
|
|
44
|
+
import { vi } from 'vitest';
|
|
45
|
+
|
|
46
|
+
const module = await createTestingModule({ rootModule: AppModule })
|
|
47
|
+
.overrideProvider(USER_REPOSITORY, {
|
|
48
|
+
create: vi.fn().mockResolvedValue({ id: '1', name: 'Alice' }),
|
|
49
|
+
})
|
|
50
|
+
.compile();
|
|
51
|
+
|
|
52
|
+
const service = await module.resolve(UserService);
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## 주요 패턴
|
|
56
|
+
|
|
57
|
+
### 오버라이드를 이용한 유닛 테스트
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
const module = await createTestingModule({ rootModule: AppModule })
|
|
61
|
+
.overrideProviders([
|
|
62
|
+
[USER_REPOSITORY, fakeUserRepo],
|
|
63
|
+
[EMAIL_SERVICE, fakeEmailService],
|
|
64
|
+
])
|
|
65
|
+
.compile();
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### HTTP 통합 테스트
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
import { createTestApp } from '@fluojs/testing';
|
|
72
|
+
|
|
73
|
+
const app = await createTestApp({ rootModule: AppModule });
|
|
74
|
+
|
|
75
|
+
const response = await app
|
|
76
|
+
.request('GET', '/users/me')
|
|
77
|
+
.principal({ subject: 'user-1', roles: ['member'] })
|
|
78
|
+
.send();
|
|
79
|
+
|
|
80
|
+
await app.close();
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### 명시적 서브패스의 mock 헬퍼
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
import { createMock, createDeepMock } from '@fluojs/testing/mock';
|
|
87
|
+
import { vi } from 'vitest';
|
|
88
|
+
|
|
89
|
+
const repo = createMock<UserRepository>({ findById: vi.fn() });
|
|
90
|
+
const mailer = createDeepMock(MailService);
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
배포된 런타임 import가 안정적으로 해석되도록, mock 헬퍼를 사용할 워크스페이스에는 `vitest`를 함께 설치해야 합니다.
|
|
94
|
+
|
|
95
|
+
### 적합성 및 이식성 하니스
|
|
96
|
+
|
|
97
|
+
프레임워크 지향 플랫폼 패키지를 작성할 때는 `@fluojs/testing/platform-conformance`, `@fluojs/testing/http-adapter-portability`, `@fluojs/testing/web-runtime-adapter-portability` 같은 서브패스를 사용해 적합성 및 이식성 검증을 수행합니다.
|
|
98
|
+
|
|
99
|
+
## 공개 API
|
|
100
|
+
|
|
101
|
+
- **루트 패키지**: `createTestingModule(...)`, `createTestApp(...)`, 모듈 introspection 헬퍼, 공용 테스트 타입
|
|
102
|
+
- **Mock 서브패스**: `@fluojs/testing/mock`
|
|
103
|
+
- **HTTP 헬퍼**: `@fluojs/testing/http`
|
|
104
|
+
- **하니스 서브패스**: `platform-conformance`, `http-adapter-portability`, `web-runtime-adapter-portability`, `fetch-style-websocket-conformance`
|
|
105
|
+
- **도구 지원**: `@fluojs/testing/vitest`와 `fluoBabelDecoratorsPlugin()` (`vitest`와 `@babel/core`를 함께 요구)
|
|
106
|
+
|
|
107
|
+
## 관련 패키지
|
|
108
|
+
|
|
109
|
+
- `@fluojs/di`: 테스트 컨테이너가 사용하는 기반 DI 시스템입니다.
|
|
110
|
+
- `@fluojs/runtime`: 테스트 빌더가 확장하는 모듈 그래프 로직을 제공합니다.
|
|
111
|
+
- `@fluojs/http`: `TestApp`에서 사용하는 가상 디스패치 시스템입니다.
|
|
112
|
+
|
|
113
|
+
## 예제 소스
|
|
114
|
+
|
|
115
|
+
- `packages/testing/src/module.test.ts`
|
|
116
|
+
- `examples/minimal/src/app.test.ts`
|
|
117
|
+
- `examples/auth-jwt-passport/src/app.test.ts`
|
package/README.md
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# @fluojs/testing
|
|
2
|
+
|
|
3
|
+
<p><strong><kbd>English</kbd></strong> <a href="./README.ko.md"><kbd>한국어</kbd></a></p>
|
|
4
|
+
|
|
5
|
+
Testing module construction, provider overrides, and request-level test helpers for fluo applications.
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [Installation](#installation)
|
|
10
|
+
- [When to Use](#when-to-use)
|
|
11
|
+
- [Quick Start](#quick-start)
|
|
12
|
+
- [Common Patterns](#common-patterns)
|
|
13
|
+
- [Public API](#public-api)
|
|
14
|
+
- [Related Packages](#related-packages)
|
|
15
|
+
- [Example Sources](#example-sources)
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install --save-dev @fluojs/testing vitest
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`vitest` is a required peer dependency for the mock helpers and the `@fluojs/testing/vitest` entrypoint.
|
|
24
|
+
|
|
25
|
+
If you use `@fluojs/testing/vitest`, install `@babel/core` in the consuming workspace as well because `fluoBabelDecoratorsPlugin()` invokes Babel at runtime:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install --save-dev @babel/core
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## When to Use
|
|
32
|
+
|
|
33
|
+
- when you want to compile a real module graph but replace a few explicit providers with fakes
|
|
34
|
+
- when route-level tests should run through fluo's real dispatch stack without starting a network server
|
|
35
|
+
- when library or adapter packages need conformance and portability harnesses from responsibility-specific subpaths
|
|
36
|
+
- when starter templates need a stable baseline for unit, integration, and e2e-style tests
|
|
37
|
+
|
|
38
|
+
## Quick Start
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { createTestingModule } from '@fluojs/testing';
|
|
42
|
+
import { vi } from 'vitest';
|
|
43
|
+
|
|
44
|
+
const module = await createTestingModule({ rootModule: AppModule })
|
|
45
|
+
.overrideProvider(USER_REPOSITORY, {
|
|
46
|
+
create: vi.fn().mockResolvedValue({ id: '1', name: 'Alice' }),
|
|
47
|
+
})
|
|
48
|
+
.compile();
|
|
49
|
+
|
|
50
|
+
const service = await module.resolve(UserService);
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Common Patterns
|
|
54
|
+
|
|
55
|
+
### Override providers before compilation
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
const module = await createTestingModule({ rootModule: AppModule })
|
|
59
|
+
.overrideProviders([
|
|
60
|
+
[USER_REPOSITORY, fakeUserRepo],
|
|
61
|
+
[EMAIL_SERVICE, fakeEmailService],
|
|
62
|
+
])
|
|
63
|
+
.compile();
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Request-level tests with `createTestApp()`
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
import { createTestApp } from '@fluojs/testing';
|
|
70
|
+
|
|
71
|
+
const app = await createTestApp({ rootModule: AppModule });
|
|
72
|
+
|
|
73
|
+
const response = await app
|
|
74
|
+
.request('GET', '/users/me')
|
|
75
|
+
.principal({ subject: 'user-1', roles: ['member'] })
|
|
76
|
+
.send();
|
|
77
|
+
|
|
78
|
+
await app.close();
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Mock helpers from explicit subpaths
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
import { createDeepMock, createMock } from '@fluojs/testing/mock';
|
|
85
|
+
import { vi } from 'vitest';
|
|
86
|
+
|
|
87
|
+
const repo = createMock<UserRepository>({ findById: vi.fn() });
|
|
88
|
+
const mailer = createDeepMock(MailService);
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Install `vitest` in the consuming workspace before using the mock helpers so the published runtime import resolves consistently.
|
|
92
|
+
|
|
93
|
+
### Conformance and portability harnesses
|
|
94
|
+
|
|
95
|
+
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.
|
|
96
|
+
|
|
97
|
+
## Public API
|
|
98
|
+
|
|
99
|
+
- **Root package**: `createTestingModule(...)`, `createTestApp(...)`, module introspection helpers, shared testing types
|
|
100
|
+
- **Mock subpath**: `@fluojs/testing/mock`
|
|
101
|
+
- **HTTP helpers**: `@fluojs/testing/http`
|
|
102
|
+
- **Harness subpaths**: `platform-conformance`, `http-adapter-portability`, `web-runtime-adapter-portability`, `fetch-style-websocket-conformance`
|
|
103
|
+
- **Tooling**: `@fluojs/testing/vitest` with `fluoBabelDecoratorsPlugin()` (requires `vitest` and `@babel/core` in the consuming workspace)
|
|
104
|
+
|
|
105
|
+
## Related Packages
|
|
106
|
+
|
|
107
|
+
- `@fluojs/di`: powers provider resolution in compiled test containers
|
|
108
|
+
- `@fluojs/runtime`: provides the module graph behavior that testing builds on
|
|
109
|
+
- `@fluojs/http`: powers request dispatch used by `createTestApp()`
|
|
110
|
+
|
|
111
|
+
## Example Sources
|
|
112
|
+
|
|
113
|
+
- `packages/testing/src/module.test.ts`
|
|
114
|
+
- `examples/minimal/src/app.test.ts`
|
|
115
|
+
- `examples/auth-jwt-passport/src/app.test.ts`
|
package/dist/app.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { TestApp, TestingModuleOptions } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Boots a lightweight test app with the real dispatcher and a fluent request client.
|
|
4
|
+
*
|
|
5
|
+
* @param options Testing bootstrap options, including the root module and any extra providers.
|
|
6
|
+
* @returns A request-driven test app facade that dispatches through the real runtime stack.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* const app = await createTestApp({ rootModule: AppModule });
|
|
11
|
+
* const response = await app.request('GET', '/health').send();
|
|
12
|
+
* await app.close();
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export declare function createTestApp(options: TestingModuleOptions): Promise<TestApp>;
|
|
16
|
+
//# sourceMappingURL=app.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,OAAO,EACP,oBAAoB,EAErB,MAAM,YAAY,CAAC;AA6BpB;;;;;;;;;;;;GAYG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,OAAO,CAAC,CAyBnF"}
|
package/dist/app.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { bootstrapApplication } from '@fluojs/runtime';
|
|
2
|
+
import { createRequestBuilder, createTestRequestContextMiddleware, makeRequest } from './http.js';
|
|
3
|
+
function normalizeRequestInput(methodOrRequest, pathOrOptions, options) {
|
|
4
|
+
if (typeof methodOrRequest === 'string') {
|
|
5
|
+
if (typeof pathOrOptions !== 'string') {
|
|
6
|
+
throw new Error('Request path is required when using the (method, path, options) overload.');
|
|
7
|
+
}
|
|
8
|
+
return {
|
|
9
|
+
...options,
|
|
10
|
+
method: methodOrRequest,
|
|
11
|
+
path: pathOrOptions
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
if (typeof pathOrOptions === 'object') {
|
|
15
|
+
return {
|
|
16
|
+
...methodOrRequest,
|
|
17
|
+
...pathOrOptions
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
return methodOrRequest;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Boots a lightweight test app with the real dispatcher and a fluent request client.
|
|
25
|
+
*
|
|
26
|
+
* @param options Testing bootstrap options, including the root module and any extra providers.
|
|
27
|
+
* @returns A request-driven test app facade that dispatches through the real runtime stack.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```ts
|
|
31
|
+
* const app = await createTestApp({ rootModule: AppModule });
|
|
32
|
+
* const response = await app.request('GET', '/health').send();
|
|
33
|
+
* await app.close();
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export async function createTestApp(options) {
|
|
37
|
+
const app = await bootstrapApplication({
|
|
38
|
+
...options,
|
|
39
|
+
middleware: [createTestRequestContextMiddleware()]
|
|
40
|
+
});
|
|
41
|
+
const request = (methodOrRequest, pathOrOptions, options) => {
|
|
42
|
+
return createRequestBuilder(app.dispatcher, normalizeRequestInput(methodOrRequest, pathOrOptions, options));
|
|
43
|
+
};
|
|
44
|
+
const dispatch = async request => {
|
|
45
|
+
return makeRequest(app.dispatcher, request);
|
|
46
|
+
};
|
|
47
|
+
return {
|
|
48
|
+
request,
|
|
49
|
+
dispatch,
|
|
50
|
+
close: async () => {
|
|
51
|
+
await app.close();
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves the nearest `babel.config.cjs` file starting from the given file path
|
|
3
|
+
* and searching upwards through the directory hierarchy.
|
|
4
|
+
*
|
|
5
|
+
* @param filePath - The path to the file whose nearest Babel configuration should be found.
|
|
6
|
+
* @returns The absolute path to the nearest `babel.config.cjs` file.
|
|
7
|
+
* @throws Error if no configuration file can be located.
|
|
8
|
+
*/
|
|
9
|
+
export declare function resolveNearestBabelConfigFile(filePath: string): string;
|
|
10
|
+
/**
|
|
11
|
+
* Creates a Babel transformation plugin that handles fluo decorator syntax
|
|
12
|
+
* during the testing process.
|
|
13
|
+
*
|
|
14
|
+
* @param resolveConfigFile - A function that resolves the Babel configuration file path for a given file.
|
|
15
|
+
* @returns A transformation plugin compatible with testing tools like Vitest.
|
|
16
|
+
*/
|
|
17
|
+
export declare function createFluoBabelDecoratorsPlugin(resolveConfigFile: (filePath: string) => string): {
|
|
18
|
+
name: string;
|
|
19
|
+
transform(code: string, id: string): Promise<{
|
|
20
|
+
code: string;
|
|
21
|
+
map: {
|
|
22
|
+
version: number;
|
|
23
|
+
sources: string[];
|
|
24
|
+
names: string[];
|
|
25
|
+
sourceRoot?: string | undefined;
|
|
26
|
+
sourcesContent?: string[] | undefined;
|
|
27
|
+
mappings: string;
|
|
28
|
+
file: string;
|
|
29
|
+
} | null;
|
|
30
|
+
} | null>;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Re-export type for the fluo Babel decorators plugin.
|
|
34
|
+
*/
|
|
35
|
+
export type FluoBabelDecoratorsPlugin = ReturnType<typeof createFluoBabelDecoratorsPlugin>;
|
|
36
|
+
//# sourceMappingURL=babel-decorators-plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"babel-decorators-plugin.d.ts","sourceRoot":"","sources":["../src/babel-decorators-plugin.ts"],"names":[],"mappings":"AAOA;;;;;;;GAOG;AACH,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAyBtE;AAED;;;;;;GAMG;AACH,wBAAgB,+BAA+B,CAC7C,iBAAiB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM;;oBAIvB,MAAM,MAAM,MAAM;;;;;;;;;;;;EAsB3C;AAED;;GAEG;AACH,MAAM,MAAM,yBAAyB,GAAG,UAAU,CAAC,OAAO,+BAA+B,CAAC,CAAC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { transformAsync } from '@babel/core';
|
|
4
|
+
const babelConfigFileCache = new Map();
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Resolves the nearest `babel.config.cjs` file starting from the given file path
|
|
8
|
+
* and searching upwards through the directory hierarchy.
|
|
9
|
+
*
|
|
10
|
+
* @param filePath - The path to the file whose nearest Babel configuration should be found.
|
|
11
|
+
* @returns The absolute path to the nearest `babel.config.cjs` file.
|
|
12
|
+
* @throws Error if no configuration file can be located.
|
|
13
|
+
*/
|
|
14
|
+
export function resolveNearestBabelConfigFile(filePath) {
|
|
15
|
+
let currentDirectory = dirname(filePath);
|
|
16
|
+
while (true) {
|
|
17
|
+
const cachedConfigFile = babelConfigFileCache.get(currentDirectory);
|
|
18
|
+
if (cachedConfigFile) {
|
|
19
|
+
return cachedConfigFile;
|
|
20
|
+
}
|
|
21
|
+
const configFile = join(currentDirectory, 'babel.config.cjs');
|
|
22
|
+
if (existsSync(configFile)) {
|
|
23
|
+
babelConfigFileCache.set(currentDirectory, configFile);
|
|
24
|
+
return configFile;
|
|
25
|
+
}
|
|
26
|
+
const parentDirectory = dirname(currentDirectory);
|
|
27
|
+
if (parentDirectory === currentDirectory) {
|
|
28
|
+
throw new Error(`Unable to locate babel.config.cjs for ${filePath}.`);
|
|
29
|
+
}
|
|
30
|
+
currentDirectory = parentDirectory;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Creates a Babel transformation plugin that handles fluo decorator syntax
|
|
36
|
+
* during the testing process.
|
|
37
|
+
*
|
|
38
|
+
* @param resolveConfigFile - A function that resolves the Babel configuration file path for a given file.
|
|
39
|
+
* @returns A transformation plugin compatible with testing tools like Vitest.
|
|
40
|
+
*/
|
|
41
|
+
export function createFluoBabelDecoratorsPlugin(resolveConfigFile) {
|
|
42
|
+
return {
|
|
43
|
+
name: 'fluo-babel-decorators',
|
|
44
|
+
async transform(code, id) {
|
|
45
|
+
if (!id.endsWith('.ts') || id.includes('/node_modules/')) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
const result = await transformAsync(code, {
|
|
49
|
+
babelrc: false,
|
|
50
|
+
configFile: resolveConfigFile(id),
|
|
51
|
+
filename: id,
|
|
52
|
+
sourceMaps: true
|
|
53
|
+
});
|
|
54
|
+
if (!result?.code) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
code: result.code,
|
|
59
|
+
map: result.map ?? null
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Re-export type for the fluo Babel decorators plugin.
|
|
67
|
+
*/
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { FetchStyleHttpAdapterRealtimeCapability, HttpApplicationAdapter } from '@fluojs/http';
|
|
2
|
+
export interface FetchStyleWebSocketConformanceHarnessOptions<TAdapter extends HttpApplicationAdapter = HttpApplicationAdapter> {
|
|
3
|
+
createAdapter: () => TAdapter;
|
|
4
|
+
expectedReason: string;
|
|
5
|
+
expectedSupport?: FetchStyleHttpAdapterRealtimeCapability['support'];
|
|
6
|
+
name: string;
|
|
7
|
+
}
|
|
8
|
+
export declare class FetchStyleWebSocketConformanceHarness<TAdapter extends HttpApplicationAdapter = HttpApplicationAdapter> {
|
|
9
|
+
private readonly options;
|
|
10
|
+
constructor(options: FetchStyleWebSocketConformanceHarnessOptions<TAdapter>);
|
|
11
|
+
assertExposesRawWebSocketExpansionContract(): void;
|
|
12
|
+
}
|
|
13
|
+
export declare function createFetchStyleWebSocketConformanceHarness<TAdapter extends HttpApplicationAdapter = HttpApplicationAdapter>(options: FetchStyleWebSocketConformanceHarnessOptions<TAdapter>): FetchStyleWebSocketConformanceHarness<TAdapter>;
|
|
14
|
+
//# sourceMappingURL=fetch-style-websocket-conformance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch-style-websocket-conformance.d.ts","sourceRoot":"","sources":["../../src/conformance/fetch-style-websocket-conformance.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uCAAuC,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAEpG,MAAM,WAAW,4CAA4C,CAC3D,QAAQ,SAAS,sBAAsB,GAAG,sBAAsB;IAEhE,aAAa,EAAE,MAAM,QAAQ,CAAC;IAC9B,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,uCAAuC,CAAC,SAAS,CAAC,CAAC;IACrE,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,qCAAqC,CAChD,QAAQ,SAAS,sBAAsB,GAAG,sBAAsB;IAEpD,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,4CAA4C,CAAC,QAAQ,CAAC;IAE5F,0CAA0C,IAAI,IAAI;CAoCnD;AAED,wBAAgB,2CAA2C,CACzD,QAAQ,SAAS,sBAAsB,GAAG,sBAAsB,EAEhE,OAAO,EAAE,4CAA4C,CAAC,QAAQ,CAAC,GAC9D,qCAAqC,CAAC,QAAQ,CAAC,CAEjD"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export class FetchStyleWebSocketConformanceHarness {
|
|
2
|
+
constructor(options) {
|
|
3
|
+
this.options = options;
|
|
4
|
+
}
|
|
5
|
+
assertExposesRawWebSocketExpansionContract() {
|
|
6
|
+
const adapter = this.options.createAdapter();
|
|
7
|
+
if (typeof adapter.getRealtimeCapability !== 'function') {
|
|
8
|
+
throw new Error(`${this.options.name} adapter must expose getRealtimeCapability() for fetch-style websocket contract checks.`);
|
|
9
|
+
}
|
|
10
|
+
const capability = adapter.getRealtimeCapability();
|
|
11
|
+
const expectedSupport = this.options.expectedSupport ?? 'contract-only';
|
|
12
|
+
if (capability.kind !== 'fetch-style') {
|
|
13
|
+
throw new Error(`${this.options.name} adapter must expose a fetch-style realtime capability.`);
|
|
14
|
+
}
|
|
15
|
+
if (capability.contract !== 'raw-websocket-expansion') {
|
|
16
|
+
throw new Error(`${this.options.name} adapter changed the raw websocket expansion contract tag.`);
|
|
17
|
+
}
|
|
18
|
+
if (capability.mode !== 'request-upgrade') {
|
|
19
|
+
throw new Error(`${this.options.name} adapter changed the fetch-style raw websocket upgrade mode.`);
|
|
20
|
+
}
|
|
21
|
+
if (capability.version !== 1) {
|
|
22
|
+
throw new Error(`${this.options.name} adapter changed the fetch-style raw websocket contract version.`);
|
|
23
|
+
}
|
|
24
|
+
if (capability.support !== expectedSupport) {
|
|
25
|
+
throw new Error(`${this.options.name} adapter changed raw websocket support honesty. Expected "${expectedSupport}" but received "${capability.support}".`);
|
|
26
|
+
}
|
|
27
|
+
if (capability.reason !== this.options.expectedReason) {
|
|
28
|
+
throw new Error(`${this.options.name} adapter changed the fetch-style raw websocket contract reason.`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export function createFetchStyleWebSocketConformanceHarness(options) {
|
|
33
|
+
return new FetchStyleWebSocketConformanceHarness(options);
|
|
34
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { MaybePromise } from '@fluojs/core';
|
|
2
|
+
import type { PlatformComponent, PlatformDiagnosticIssue, PlatformSnapshot, PlatformState, PlatformValidationResult } from '@fluojs/runtime';
|
|
3
|
+
export interface PlatformConformanceScenario {
|
|
4
|
+
createComponent: () => PlatformComponent;
|
|
5
|
+
enterState: (component: PlatformComponent) => MaybePromise<void>;
|
|
6
|
+
name: string;
|
|
7
|
+
expectedState?: PlatformState;
|
|
8
|
+
}
|
|
9
|
+
export interface PlatformConformanceDiagnosticsOptions {
|
|
10
|
+
collect?: (component: PlatformComponent, validation: PlatformValidationResult) => MaybePromise<readonly PlatformDiagnosticIssue[]>;
|
|
11
|
+
expectedCodes?: readonly string[];
|
|
12
|
+
requireFixHintForSeverities?: ReadonlyArray<PlatformDiagnosticIssue['severity']>;
|
|
13
|
+
}
|
|
14
|
+
export interface PlatformConformanceSnapshotOptions {
|
|
15
|
+
allowKeyPatterns?: readonly RegExp[];
|
|
16
|
+
compare?: (left: unknown, right: unknown) => boolean;
|
|
17
|
+
forbiddenKeyPatterns?: readonly RegExp[];
|
|
18
|
+
sanitize?: (snapshot: PlatformSnapshot) => PlatformSnapshot;
|
|
19
|
+
}
|
|
20
|
+
export interface PlatformConformanceHarnessOptions {
|
|
21
|
+
captureValidationSideEffects?: (component: PlatformComponent) => MaybePromise<unknown>;
|
|
22
|
+
createComponent: () => PlatformComponent;
|
|
23
|
+
diagnostics?: PlatformConformanceDiagnosticsOptions;
|
|
24
|
+
scenarios?: {
|
|
25
|
+
degraded: PlatformConformanceScenario;
|
|
26
|
+
failed: PlatformConformanceScenario;
|
|
27
|
+
};
|
|
28
|
+
snapshot?: PlatformConformanceSnapshotOptions;
|
|
29
|
+
}
|
|
30
|
+
export declare class PlatformConformanceHarness {
|
|
31
|
+
private readonly options;
|
|
32
|
+
constructor(options: PlatformConformanceHarnessOptions);
|
|
33
|
+
assertValidationHasNoLongLivedSideEffects(): Promise<void>;
|
|
34
|
+
assertStartIsDeterministic(): Promise<void>;
|
|
35
|
+
assertStopIsIdempotent(): Promise<void>;
|
|
36
|
+
assertSnapshotSafeInDegradedAndFailedStates(): Promise<void>;
|
|
37
|
+
assertStableDiagnostics(): Promise<void>;
|
|
38
|
+
assertSnapshotSanitized(): Promise<void>;
|
|
39
|
+
assertAll(): Promise<void>;
|
|
40
|
+
}
|
|
41
|
+
export declare function createPlatformConformanceHarness(options: PlatformConformanceHarnessOptions): PlatformConformanceHarness;
|
|
42
|
+
//# sourceMappingURL=platform-conformance.d.ts.map
|
|
@@ -0,0 +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,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,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,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,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;AA4ED,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;IAwB3C,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC;IAkCvC,2CAA2C,IAAI,OAAO,CAAC,IAAI,CAAC;IA2B5D,uBAAuB,IAAI,OAAO,CAAC,IAAI,CAAC;IAuCxC,uBAAuB,IAAI,OAAO,CAAC,IAAI,CAAC;IAgBxC,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;CAQjC;AAED,wBAAgB,gCAAgC,CAC9C,OAAO,EAAE,iCAAiC,GACzC,0BAA0B,CAE5B"}
|