mock-fried 1.0.6 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/dist/module.json +1 -1
- package/dist/runtime/server/handlers/openapi.js +34 -3
- package/dist/runtime/server/utils/mock/client-generator.d.ts +1 -0
- package/dist/runtime/server/utils/mock/client-generator.js +14 -14
- package/dist/runtime/server/utils/mock/index.d.ts +1 -1
- package/dist/runtime/server/utils/mock/openapi-generator.d.ts +15 -1
- package/dist/runtime/server/utils/mock/openapi-generator.js +82 -28
- package/dist/runtime/server/utils/mock/providers/openapi-item-provider.js +6 -0
- package/package.json +6 -1
package/README.md
CHANGED
|
@@ -278,7 +278,7 @@ Mock-Fried는 **결정론적(deterministic)** Mock 데이터를 생성합니다:
|
|
|
278
278
|
| Request Body | ✅ | POST/PUT/PATCH body 처리 |
|
|
279
279
|
| All HTTP Methods | ✅ | GET, POST, PUT, DELETE, PATCH |
|
|
280
280
|
|
|
281
|
-
### Protobuf RPC Mock (
|
|
281
|
+
### Protobuf RPC Mock (✅ Production Ready)
|
|
282
282
|
|
|
283
283
|
| Feature | Status | Description |
|
|
284
284
|
|---------|--------|-------------|
|
|
@@ -288,20 +288,20 @@ Mock-Fried는 **결정론적(deterministic)** Mock 데이터를 생성합니다:
|
|
|
288
288
|
| Basic Types | ✅ | string, int32/64, float, double, bool |
|
|
289
289
|
| Enum Types | ✅ | 첫 번째 enum 값 반환 |
|
|
290
290
|
| Nested Messages | ✅ | 중첩 메시지 타입 |
|
|
291
|
-
| Repeated Fields | ✅ | 배열 필드 (
|
|
291
|
+
| Repeated Fields | ✅ | 배열 필드 (자동 생성) |
|
|
292
292
|
| Map Fields | ✅ | map 타입 지원 |
|
|
293
|
+
| Page Pagination | ✅ | page/limit 기반 페이지네이션 |
|
|
294
|
+
| Cursor Pagination | ✅ | cursor 기반 무한 스크롤 |
|
|
295
|
+
| Deterministic Data | ✅ | 동일 요청 = 동일 응답 |
|
|
293
296
|
| Server Streaming | ❌ | 미구현 |
|
|
294
297
|
| Client Streaming | ❌ | 미구현 |
|
|
295
298
|
| Bidirectional Streaming | ❌ | 미구현 |
|
|
296
|
-
| Pagination | ❌ | 미구현 |
|
|
297
|
-
| Dynamic List Size | ❌ | repeated 필드 고정 1개 |
|
|
298
299
|
|
|
299
300
|
### 구현 예정 (Roadmap)
|
|
300
301
|
|
|
301
302
|
Proto RPC 기능 확장:
|
|
303
|
+
|
|
302
304
|
- [ ] Server streaming 지원
|
|
303
|
-
- [ ] Pagination 패턴 지원
|
|
304
|
-
- [ ] repeated 필드 다수 아이템 생성
|
|
305
305
|
- [ ] Well-known types (Timestamp, Duration 등)
|
|
306
306
|
|
|
307
307
|
## Compatibility
|
package/dist/module.json
CHANGED
|
@@ -20,6 +20,7 @@ let apiInstance = null;
|
|
|
20
20
|
let cachedSpecPath = null;
|
|
21
21
|
let specCursorManager = null;
|
|
22
22
|
let specPageManager = null;
|
|
23
|
+
let cachedOpenAPISpec = null;
|
|
23
24
|
function loadOpenAPISpec(specPath) {
|
|
24
25
|
const content = readFileSync(specPath, "utf-8");
|
|
25
26
|
if (specPath.endsWith(".yaml") || specPath.endsWith(".yml")) {
|
|
@@ -33,10 +34,12 @@ async function getOpenAPIBackend(specPath) {
|
|
|
33
34
|
}
|
|
34
35
|
const { OpenAPIBackend } = await import("openapi-backend");
|
|
35
36
|
const definition = loadOpenAPISpec(specPath);
|
|
37
|
+
cachedOpenAPISpec = definition;
|
|
36
38
|
apiInstance = new OpenAPIBackend({
|
|
37
39
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
38
40
|
definition,
|
|
39
|
-
quick:
|
|
41
|
+
quick: false,
|
|
42
|
+
// $ref 역참조를 위해 false로 설정
|
|
40
43
|
validate: false
|
|
41
44
|
// Mock 서버에서는 request validation 비활성화
|
|
42
45
|
});
|
|
@@ -52,6 +55,13 @@ async function getOpenAPIBackend(specPath) {
|
|
|
52
55
|
notImplemented: async (c) => {
|
|
53
56
|
const operationId = c.operation?.operationId || "unknown";
|
|
54
57
|
const responses = c.operation?.responses;
|
|
58
|
+
if (responses?.["204"]) {
|
|
59
|
+
return {
|
|
60
|
+
statusCode: 204,
|
|
61
|
+
body: null,
|
|
62
|
+
meta: { operationId }
|
|
63
|
+
};
|
|
64
|
+
}
|
|
55
65
|
const successResponse = responses?.["200"] || responses?.["201"] || Object.values(responses || {})[0];
|
|
56
66
|
const content = successResponse?.content;
|
|
57
67
|
const jsonContent = content?.["application/json"];
|
|
@@ -138,7 +148,16 @@ async function getOpenAPIBackend(specPath) {
|
|
|
138
148
|
}
|
|
139
149
|
} else {
|
|
140
150
|
const numericSeed = hashString(seed);
|
|
141
|
-
|
|
151
|
+
const apiSchemas = c.api?.document?.components?.schemas;
|
|
152
|
+
const schemaContext = {
|
|
153
|
+
schemas: apiSchemas || cachedOpenAPISpec?.components?.schemas,
|
|
154
|
+
maxDepth: 10
|
|
155
|
+
};
|
|
156
|
+
mockData = generateMockFromSchema(
|
|
157
|
+
schema,
|
|
158
|
+
numericSeed,
|
|
159
|
+
schemaContext
|
|
160
|
+
);
|
|
142
161
|
}
|
|
143
162
|
}
|
|
144
163
|
return {
|
|
@@ -170,6 +189,7 @@ let pagePaginationManager = null;
|
|
|
170
189
|
export function clearOpenApiCache() {
|
|
171
190
|
apiInstance = null;
|
|
172
191
|
cachedSpecPath = null;
|
|
192
|
+
cachedOpenAPISpec = null;
|
|
173
193
|
cachedClientPackage = null;
|
|
174
194
|
cachedClientPath = null;
|
|
175
195
|
mockGenerator = null;
|
|
@@ -236,7 +256,18 @@ function handleClientPackageRequest(pkg, generator, cursorManager, pageManager,
|
|
|
236
256
|
};
|
|
237
257
|
}
|
|
238
258
|
const { endpoint, pathParams } = match;
|
|
239
|
-
|
|
259
|
+
if (endpoint.responseType.toLowerCase() === "void") {
|
|
260
|
+
return {
|
|
261
|
+
statusCode: 204,
|
|
262
|
+
body: null,
|
|
263
|
+
meta: {
|
|
264
|
+
operationId: endpoint.operationId,
|
|
265
|
+
apiClass: endpoint.apiClassName,
|
|
266
|
+
responseType: endpoint.responseType
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
const primitiveTypes = ["object", "string", "number", "boolean", "any", "unknown"];
|
|
240
271
|
if (primitiveTypes.includes(endpoint.responseType.toLowerCase())) {
|
|
241
272
|
const pathLower = path.toLowerCase();
|
|
242
273
|
let primitiveResponse = {};
|
|
@@ -24,6 +24,7 @@ export declare function inferNumberByFieldName(fieldName: string, rng: SeededRan
|
|
|
24
24
|
export declare function inferBooleanByFieldName(fieldName: string, rng: SeededRandom): boolean;
|
|
25
25
|
/**
|
|
26
26
|
* Date 타입 필드의 값 생성
|
|
27
|
+
* 결정적 타임스탬프 생성 (2024-01-01 기준)
|
|
27
28
|
*/
|
|
28
29
|
export declare function inferDateByFieldName(fieldName: string, rng: SeededRandom): string;
|
|
29
30
|
/**
|
|
@@ -23,9 +23,9 @@ export function inferValueByFieldName(fieldName, rng, index = 0, idConfig = DEFA
|
|
|
23
23
|
return `https://picsum.photos/seed/${rng.nextInt(1, 1e3)}/200/200`;
|
|
24
24
|
}
|
|
25
25
|
if (name.includes("date") || name.endsWith("at") || name.includes("time") || name.includes("created") || name.includes("updated")) {
|
|
26
|
-
const
|
|
27
|
-
const offset = rng.nextInt(-365,
|
|
28
|
-
return new Date(
|
|
26
|
+
const baseTimestamp = 17040672e5;
|
|
27
|
+
const offset = rng.nextInt(-365, 365) * 24 * 60 * 60 * 1e3;
|
|
28
|
+
return new Date(baseTimestamp + offset).toISOString();
|
|
29
29
|
}
|
|
30
30
|
if (name.includes("description") || name.includes("content") || name.includes("body") || name.includes("text")) {
|
|
31
31
|
return `Mock ${fieldName} \uB370\uC774\uD130 #${rng.nextInt(1, 100)}`;
|
|
@@ -219,29 +219,29 @@ export function inferBooleanByFieldName(fieldName, rng) {
|
|
|
219
219
|
}
|
|
220
220
|
export function inferDateByFieldName(fieldName, rng) {
|
|
221
221
|
const name = fieldName.toLowerCase();
|
|
222
|
-
const
|
|
222
|
+
const baseTimestamp = 17040672e5;
|
|
223
223
|
if (name.includes("created") || name.includes("registered") || name.includes("joined")) {
|
|
224
224
|
const offset2 = rng.nextInt(-365, -1) * 24 * 60 * 60 * 1e3;
|
|
225
|
-
return new Date(
|
|
225
|
+
return new Date(baseTimestamp + offset2).toISOString();
|
|
226
226
|
}
|
|
227
227
|
if (name.includes("updated") || name.includes("modified")) {
|
|
228
228
|
const offset2 = rng.nextInt(-30, 0) * 24 * 60 * 60 * 1e3;
|
|
229
|
-
return new Date(
|
|
229
|
+
return new Date(baseTimestamp + offset2).toISOString();
|
|
230
230
|
}
|
|
231
231
|
if (name.includes("expir") || name.includes("deadline") || name.includes("due")) {
|
|
232
232
|
const offset2 = rng.nextInt(1, 365) * 24 * 60 * 60 * 1e3;
|
|
233
|
-
return new Date(
|
|
233
|
+
return new Date(baseTimestamp + offset2).toISOString();
|
|
234
234
|
}
|
|
235
235
|
if (name.includes("start") || name.includes("begin")) {
|
|
236
236
|
const offset2 = rng.nextInt(-30, 30) * 24 * 60 * 60 * 1e3;
|
|
237
|
-
return new Date(
|
|
237
|
+
return new Date(baseTimestamp + offset2).toISOString();
|
|
238
238
|
}
|
|
239
239
|
if (name.includes("end") || name.includes("finish")) {
|
|
240
240
|
const offset2 = rng.nextInt(1, 90) * 24 * 60 * 60 * 1e3;
|
|
241
|
-
return new Date(
|
|
241
|
+
return new Date(baseTimestamp + offset2).toISOString();
|
|
242
242
|
}
|
|
243
|
-
const offset = rng.nextInt(-365,
|
|
244
|
-
return new Date(
|
|
243
|
+
const offset = rng.nextInt(-365, 365) * 24 * 60 * 60 * 1e3;
|
|
244
|
+
return new Date(baseTimestamp + offset).toISOString();
|
|
245
245
|
}
|
|
246
246
|
export function inferStringByFieldName(fieldName, rng, index = 0, idConfig = DEFAULT_ID_CONFIG) {
|
|
247
247
|
const inferred = inferValueByFieldName(fieldName, rng, index, idConfig);
|
|
@@ -270,9 +270,9 @@ export function inferTypeFromFieldName(fieldName, rng, index, idConfig = DEFAULT
|
|
|
270
270
|
return rng.next() > 0.5;
|
|
271
271
|
}
|
|
272
272
|
if (name.includes("date") || name.endsWith("at") || name.includes("time") || name.includes("created") || name.includes("updated") || name.includes("modified") || name.includes("deadline")) {
|
|
273
|
-
const
|
|
274
|
-
const offset = rng.nextInt(-365,
|
|
275
|
-
return new Date(
|
|
273
|
+
const baseTimestamp = 17040672e5;
|
|
274
|
+
const offset = rng.nextInt(-365, 365) * 24 * 60 * 60 * 1e3;
|
|
275
|
+
return new Date(baseTimestamp + offset).toISOString();
|
|
276
276
|
}
|
|
277
277
|
if (name.includes("days") || name.includes("months") || name.includes("weeks") || name.includes("years")) {
|
|
278
278
|
return rng.nextInt(1, 30);
|
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
*/
|
|
5
5
|
export { hashString, seededRandom, SeededRandom, generateId, generateSnapshotId, DEFAULT_ID_CONFIG, isIdField, generateIdValue, generateByFormat, } from './shared.js';
|
|
6
6
|
export { generateMockValueForProtoField, generateMockMessage, deriveSeedFromRequest, } from './proto-generator.js';
|
|
7
|
-
export { generateMockFromSchema, } from './openapi-generator.js';
|
|
7
|
+
export { generateMockFromSchema, type SchemaContext, } from './openapi-generator.js';
|
|
8
8
|
export { inferValueByFieldName, generateValueByType, inferTypeFromFieldName, SchemaMockGenerator, extractDataModelName, type ResponseTypeInfo, } from './client-generator.js';
|
|
9
9
|
export { type CursorPayload, type PaginationSnapshot, type PagePaginationOptions, type PagePaginationResult, type CursorPaginationOptions, type CursorPaginationResult, type PaginationConfig, type CursorConfig, DEFAULT_PAGINATION_CONFIG, DEFAULT_CURSOR_CONFIG, SnapshotStore, getSnapshotStore, resetSnapshotStore, CursorPaginationManager, encodeCursor, decodeCursor, isCursorExpired, PagePaginationManager, } from './pagination/index.js';
|
|
@@ -1,4 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* $ref 참조를 해결하는 컨텍스트
|
|
3
|
+
*/
|
|
4
|
+
export interface SchemaContext {
|
|
5
|
+
schemas?: Record<string, Record<string, unknown>>;
|
|
6
|
+
maxDepth?: number;
|
|
7
|
+
/** 현재 처리 중인 스키마 경로 (순환 참조 감지용) */
|
|
8
|
+
_visitedRefs?: Set<string>;
|
|
9
|
+
}
|
|
1
10
|
/**
|
|
2
11
|
* OpenAPI 스키마에서 mock 값 생성
|
|
12
|
+
* @param schema - OpenAPI 스키마
|
|
13
|
+
* @param seed - 랜덤 시드
|
|
14
|
+
* @param context - $ref 해결을 위한 컨텍스트
|
|
15
|
+
* @param depth - 현재 재귀 깊이 (재귀 스키마 무한 루프 방지)
|
|
16
|
+
* @param debugPath - 디버그용 경로 (개발 시에만 사용)
|
|
3
17
|
*/
|
|
4
|
-
export declare function generateMockFromSchema(schema: Record<string, unknown>, seed?: number): unknown;
|
|
18
|
+
export declare function generateMockFromSchema(schema: Record<string, unknown>, seed?: number, context?: SchemaContext, depth?: number, debugPath?: string): unknown;
|
|
@@ -1,6 +1,40 @@
|
|
|
1
1
|
import { seededRandom } from "./shared.js";
|
|
2
|
-
|
|
2
|
+
function extractRefName(ref) {
|
|
3
|
+
const parts = ref.split("/");
|
|
4
|
+
return parts[parts.length - 1] || "";
|
|
5
|
+
}
|
|
6
|
+
function resolveRef(schema, context) {
|
|
7
|
+
const ref = schema.$ref;
|
|
8
|
+
if (!ref) return schema;
|
|
9
|
+
const schemaName = extractRefName(ref);
|
|
10
|
+
const resolved = context.schemas?.[schemaName];
|
|
11
|
+
if (!resolved) {
|
|
12
|
+
return schema;
|
|
13
|
+
}
|
|
14
|
+
return resolved;
|
|
15
|
+
}
|
|
16
|
+
export function generateMockFromSchema(schema, seed = 1, context = {}, depth = 0, debugPath = "root") {
|
|
17
|
+
const maxDepth = context.maxDepth ?? 5;
|
|
3
18
|
const random = seededRandom(seed);
|
|
19
|
+
if (depth > maxDepth) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
if (schema.$ref) {
|
|
23
|
+
const refPath = schema.$ref;
|
|
24
|
+
const visitedRefs = context._visitedRefs ?? /* @__PURE__ */ new Set();
|
|
25
|
+
const refKey = `${refPath}@${depth}`;
|
|
26
|
+
if (visitedRefs.has(refKey)) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
const newVisitedRefs = new Set(visitedRefs);
|
|
30
|
+
newVisitedRefs.add(refKey);
|
|
31
|
+
const newContext = { ...context, _visitedRefs: newVisitedRefs };
|
|
32
|
+
const resolved = resolveRef(schema, context);
|
|
33
|
+
if (resolved !== schema) {
|
|
34
|
+
return generateMockFromSchema(resolved, seed, newContext, depth + 1, `${debugPath}->$ref`);
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
4
38
|
const schemaType = schema.type;
|
|
5
39
|
if (schema.example !== void 0) {
|
|
6
40
|
return schema.example;
|
|
@@ -8,6 +42,38 @@ export function generateMockFromSchema(schema, seed = 1) {
|
|
|
8
42
|
if (Array.isArray(schema.enum) && schema.enum.length > 0) {
|
|
9
43
|
return schema.enum[0];
|
|
10
44
|
}
|
|
45
|
+
if (Array.isArray(schema.oneOf) && schema.oneOf.length > 0) {
|
|
46
|
+
const index = Math.floor(random() * schema.oneOf.length);
|
|
47
|
+
return generateMockFromSchema(
|
|
48
|
+
schema.oneOf[index],
|
|
49
|
+
seed,
|
|
50
|
+
context,
|
|
51
|
+
depth + 1,
|
|
52
|
+
`${debugPath}->oneOf[${index}]`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
if (Array.isArray(schema.anyOf) && schema.anyOf.length > 0) {
|
|
56
|
+
const index = Math.floor(random() * schema.anyOf.length);
|
|
57
|
+
return generateMockFromSchema(
|
|
58
|
+
schema.anyOf[index],
|
|
59
|
+
seed,
|
|
60
|
+
context,
|
|
61
|
+
depth + 1,
|
|
62
|
+
`${debugPath}->anyOf[${index}]`
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
if (Array.isArray(schema.allOf) && schema.allOf.length > 0) {
|
|
66
|
+
const merged = {};
|
|
67
|
+
let propSeed = seed;
|
|
68
|
+
let idx = 0;
|
|
69
|
+
for (const subSchema of schema.allOf) {
|
|
70
|
+
const generated = generateMockFromSchema(subSchema, propSeed++, context, depth + 1, `${debugPath}->allOf[${idx++}]`);
|
|
71
|
+
if (typeof generated === "object" && generated !== null) {
|
|
72
|
+
Object.assign(merged, generated);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return merged;
|
|
76
|
+
}
|
|
11
77
|
switch (schemaType) {
|
|
12
78
|
case "string": {
|
|
13
79
|
const format = schema.format;
|
|
@@ -59,26 +125,27 @@ export function generateMockFromSchema(schema, seed = 1) {
|
|
|
59
125
|
const items = schema.items;
|
|
60
126
|
if (items) {
|
|
61
127
|
const minItems = schema.minItems ?? 1;
|
|
62
|
-
const maxItems = schema.maxItems ?? 3;
|
|
128
|
+
const maxItems = schema.maxItems ?? (depth > 3 ? 1 : 3);
|
|
63
129
|
const count = Math.floor(random() * (maxItems - minItems + 1)) + minItems;
|
|
64
130
|
return Array.from(
|
|
65
131
|
{ length: count },
|
|
66
|
-
(_, i) => generateMockFromSchema(items, seed + i + 1)
|
|
132
|
+
(_, i) => generateMockFromSchema(items, seed + i + 1, context, depth + 1, `${debugPath}[${i}]`)
|
|
67
133
|
);
|
|
68
134
|
}
|
|
69
135
|
return [];
|
|
70
136
|
}
|
|
71
|
-
case "object":
|
|
137
|
+
case "object":
|
|
138
|
+
default: {
|
|
72
139
|
const properties = schema.properties;
|
|
73
140
|
if (properties) {
|
|
74
141
|
const result = {};
|
|
75
142
|
const required = schema.required ?? [];
|
|
76
143
|
let propSeed = seed;
|
|
77
144
|
for (const [propName, propSchema] of Object.entries(properties)) {
|
|
78
|
-
if (!required.includes(propName) && random() < 0.
|
|
145
|
+
if (!required.includes(propName) && random() < 0.2) {
|
|
79
146
|
continue;
|
|
80
147
|
}
|
|
81
|
-
result[propName] = generateMockFromSchema(propSchema, propSeed
|
|
148
|
+
result[propName] = generateMockFromSchema(propSchema, propSeed++, context, depth + 1, `${debugPath}.${propName}`);
|
|
82
149
|
}
|
|
83
150
|
return result;
|
|
84
151
|
}
|
|
@@ -86,31 +153,18 @@ export function generateMockFromSchema(schema, seed = 1) {
|
|
|
86
153
|
const additionalProps = schema.additionalProperties;
|
|
87
154
|
if (typeof additionalProps === "object") {
|
|
88
155
|
return {
|
|
89
|
-
|
|
156
|
+
key1: generateMockFromSchema(additionalProps, seed, context, depth + 1, `${debugPath}.key1`),
|
|
157
|
+
key2: generateMockFromSchema(additionalProps, seed + 1, context, depth + 1, `${debugPath}.key2`),
|
|
158
|
+
key3: generateMockFromSchema(additionalProps, seed + 2, context, depth + 1, `${debugPath}.key3`)
|
|
90
159
|
};
|
|
91
160
|
}
|
|
161
|
+
return {
|
|
162
|
+
key1: "value1",
|
|
163
|
+
key2: "value2"
|
|
164
|
+
};
|
|
92
165
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
// oneOf, anyOf, allOf 처리
|
|
96
|
-
default: {
|
|
97
|
-
if (Array.isArray(schema.oneOf) && schema.oneOf.length > 0) {
|
|
98
|
-
const index = Math.floor(random() * schema.oneOf.length);
|
|
99
|
-
return generateMockFromSchema(schema.oneOf[index], seed);
|
|
100
|
-
}
|
|
101
|
-
if (Array.isArray(schema.anyOf) && schema.anyOf.length > 0) {
|
|
102
|
-
const index = Math.floor(random() * schema.anyOf.length);
|
|
103
|
-
return generateMockFromSchema(schema.anyOf[index], seed);
|
|
104
|
-
}
|
|
105
|
-
if (Array.isArray(schema.allOf) && schema.allOf.length > 0) {
|
|
106
|
-
const merged = {};
|
|
107
|
-
for (const subSchema of schema.allOf) {
|
|
108
|
-
const generated = generateMockFromSchema(subSchema, seed);
|
|
109
|
-
if (typeof generated === "object" && generated !== null) {
|
|
110
|
-
Object.assign(merged, generated);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
return merged;
|
|
166
|
+
if (schemaType === "object") {
|
|
167
|
+
return {};
|
|
114
168
|
}
|
|
115
169
|
return null;
|
|
116
170
|
}
|
|
@@ -42,6 +42,9 @@ export class OpenAPIItemProvider {
|
|
|
42
42
|
*/
|
|
43
43
|
generateItemWithId(id, index, seed) {
|
|
44
44
|
const item = this.generateItem(index, seed);
|
|
45
|
+
if (typeof item !== "object" || item === null) {
|
|
46
|
+
return { [this.idFieldName]: id, value: item };
|
|
47
|
+
}
|
|
45
48
|
item[this.idFieldName] = id;
|
|
46
49
|
return item;
|
|
47
50
|
}
|
|
@@ -100,6 +103,9 @@ export function analyzePaginationSchema(schema) {
|
|
|
100
103
|
const foundCursorFields = cursorFields.filter((f) => f in properties);
|
|
101
104
|
const isPageBased = foundPageFields.length >= 2;
|
|
102
105
|
const isCursorBased = foundCursorFields.length >= 1;
|
|
106
|
+
if (!isPageBased && !isCursorBased) {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
103
109
|
return {
|
|
104
110
|
itemsFieldName,
|
|
105
111
|
itemSchema,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mock-fried",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.7",
|
|
4
4
|
"description": "Nuxt3 Mock API Module - OpenAPI & Protobuf RPC Mock Server",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -52,6 +52,11 @@
|
|
|
52
52
|
"format:check": "prettier --check .",
|
|
53
53
|
"test": "vitest run",
|
|
54
54
|
"test:watch": "vitest watch",
|
|
55
|
+
"test:unit": "vitest run --exclude 'test/e2e/**'",
|
|
56
|
+
"test:e2e": "vitest run test/e2e/",
|
|
57
|
+
"test:e2e:openapi": "vitest run test/e2e/playground-openapi.e2e.test.ts",
|
|
58
|
+
"test:e2e:openapi-client": "vitest run test/e2e/playground-openapi-client.e2e.test.ts",
|
|
59
|
+
"test:e2e:proto": "vitest run test/e2e/playground-proto.e2e.test.ts",
|
|
55
60
|
"test:types": "vue-tsc --noEmit && cd playground-openapi && vue-tsc --noEmit"
|
|
56
61
|
},
|
|
57
62
|
"dependencies": {
|