swallowkit 1.0.0-beta.5 → 1.0.0-beta.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/LICENSE +21 -21
- package/README.ja.md +251 -242
- package/README.md +252 -243
- package/dist/__tests__/fixtures.d.ts +14 -0
- package/dist/__tests__/fixtures.d.ts.map +1 -0
- package/dist/__tests__/fixtures.js +85 -0
- package/dist/__tests__/fixtures.js.map +1 -0
- package/dist/cli/commands/create-model.js +14 -14
- package/dist/cli/commands/dev.d.ts +8 -0
- package/dist/cli/commands/dev.d.ts.map +1 -1
- package/dist/cli/commands/dev.js +238 -30
- package/dist/cli/commands/dev.js.map +1 -1
- package/dist/cli/commands/init.d.ts +5 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +2507 -1664
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/scaffold.d.ts +3 -0
- package/dist/cli/commands/scaffold.d.ts.map +1 -1
- package/dist/cli/commands/scaffold.js +281 -117
- package/dist/cli/commands/scaffold.js.map +1 -1
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/core/config.d.ts +2 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +28 -0
- package/dist/core/config.js.map +1 -1
- package/dist/core/scaffold/functions-generator.d.ts +5 -0
- package/dist/core/scaffold/functions-generator.d.ts.map +1 -1
- package/dist/core/scaffold/functions-generator.js +649 -218
- package/dist/core/scaffold/functions-generator.js.map +1 -1
- package/dist/core/scaffold/model-parser.d.ts +1 -1
- package/dist/core/scaffold/model-parser.js +99 -99
- package/dist/core/scaffold/nextjs-generator.js +181 -181
- package/dist/core/scaffold/openapi-generator.d.ts +3 -0
- package/dist/core/scaffold/openapi-generator.d.ts.map +1 -0
- package/dist/core/scaffold/openapi-generator.js +190 -0
- package/dist/core/scaffold/openapi-generator.js.map +1 -0
- package/dist/core/scaffold/ui-generator.js +656 -656
- package/dist/database/base-model.d.ts +3 -3
- package/dist/database/base-model.js +3 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/package-manager.d.ts +2 -1
- package/dist/utils/package-manager.d.ts.map +1 -1
- package/dist/utils/package-manager.js +14 -10
- package/dist/utils/package-manager.js.map +1 -1
- package/package.json +81 -74
- package/src/__tests__/__snapshots__/functions-generator.test.ts.snap +445 -0
- package/src/__tests__/__snapshots__/nextjs-generator.test.ts.snap +194 -0
- package/src/__tests__/__snapshots__/ui-generator.test.ts.snap +524 -0
- package/src/__tests__/config.test.ts +122 -0
- package/src/__tests__/dev.test.ts +42 -0
- package/src/__tests__/fixtures.ts +83 -0
- package/src/__tests__/functions-generator.test.ts +101 -0
- package/src/__tests__/init.test.ts +59 -0
- package/src/__tests__/nextjs-generator.test.ts +97 -0
- package/src/__tests__/openapi-generator.test.ts +43 -0
- package/src/__tests__/package-manager.test.ts +189 -0
- package/src/__tests__/scaffold.test.ts +39 -0
- package/src/__tests__/string-utils.test.ts +75 -0
- package/src/__tests__/ui-generator.test.ts +144 -0
- package/src/cli/commands/create-model.ts +141 -0
- package/src/cli/commands/dev.ts +794 -0
- package/src/cli/commands/index.ts +8 -0
- package/src/cli/commands/init.ts +3363 -0
- package/src/cli/commands/provision.ts +193 -0
- package/src/cli/commands/scaffold.ts +786 -0
- package/src/cli/index.ts +73 -0
- package/src/core/config.ts +244 -0
- package/src/core/scaffold/functions-generator.ts +674 -0
- package/src/core/scaffold/model-parser.ts +627 -0
- package/src/core/scaffold/nextjs-generator.ts +217 -0
- package/src/core/scaffold/openapi-generator.ts +212 -0
- package/src/core/scaffold/ui-generator.ts +945 -0
- package/src/database/base-model.ts +184 -0
- package/src/database/client.ts +140 -0
- package/src/database/repository.ts +104 -0
- package/src/database/runtime-check.ts +25 -0
- package/src/index.ts +27 -0
- package/src/types/index.ts +45 -0
- package/src/utils/package-manager.ts +229 -0
- package/dist/cli/commands/build.d.ts +0 -6
- package/dist/cli/commands/build.d.ts.map +0 -1
- package/dist/cli/commands/build.js +0 -177
- package/dist/cli/commands/build.js.map +0 -1
- package/dist/cli/commands/deploy.d.ts +0 -3
- package/dist/cli/commands/deploy.d.ts.map +0 -1
- package/dist/cli/commands/deploy.js +0 -147
- package/dist/cli/commands/deploy.js.map +0 -1
- package/dist/cli/commands/setup.d.ts +0 -6
- package/dist/cli/commands/setup.d.ts.map +0 -1
- package/dist/cli/commands/setup.js +0 -254
- package/dist/cli/commands/setup.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"functions-generator.js","sourceRoot":"","sources":["../../../src/core/scaffold/functions-generator.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;AAQH,8EAiOC;
|
|
1
|
+
{"version":3,"file":"functions-generator.js","sourceRoot":"","sources":["../../../src/core/scaffold/functions-generator.ts"],"names":[],"mappings":"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"}
|
|
@@ -55,7 +55,7 @@ export declare function toCamelCase(str: string): string;
|
|
|
55
55
|
export declare function toKebabCase(str: string): string;
|
|
56
56
|
/**
|
|
57
57
|
* models ディレクトリから全てのモデル情報を取得
|
|
58
|
-
* @param modelsDir モデルディレクトリのパス(デフォルト: "
|
|
58
|
+
* @param modelsDir モデルディレクトリのパス(デフォルト: "shared/models")
|
|
59
59
|
* @returns モデル情報の配列
|
|
60
60
|
*/
|
|
61
61
|
export declare function getAllModels(modelsDir?: string): Promise<ModelInfo[]>;
|
|
@@ -328,104 +328,104 @@ async function extractFieldsFromSchema(modelPath, schemaName) {
|
|
|
328
328
|
}
|
|
329
329
|
// プロジェクトルート内に一時スクリプトファイルを作成
|
|
330
330
|
const tempScript = path.join(projectRoot, `.swallowkit-parser-${Date.now()}.mjs`);
|
|
331
|
-
const scriptCode = `
|
|
332
|
-
import { z } from 'zod/v4';
|
|
333
|
-
|
|
334
|
-
// インライン化した依存スキーマ
|
|
335
|
-
${inlinedDeps}
|
|
336
|
-
// モデルファイルの内容を評価
|
|
337
|
-
${modelContent}
|
|
338
|
-
|
|
339
|
-
const schema = ${schemaName};
|
|
340
|
-
|
|
341
|
-
// Zod v3とv4の両方に対応
|
|
342
|
-
const isObject = (schema && schema._def &&
|
|
343
|
-
(schema._def.typeName === 'ZodObject' || schema.constructor?.name === 'ZodObject' || typeof schema._def.shape === 'function'));
|
|
344
|
-
|
|
345
|
-
if (isObject) {
|
|
346
|
-
const shape = typeof schema._def.shape === 'function' ? schema._def.shape() : schema._def.shape;
|
|
347
|
-
const fields = Object.keys(shape).map(key => {
|
|
348
|
-
const field = shape[key];
|
|
349
|
-
let type = 'string';
|
|
350
|
-
let isOptional = false;
|
|
351
|
-
let isArray = false;
|
|
352
|
-
let enumValues = undefined;
|
|
353
|
-
|
|
354
|
-
// ZodOptional, ZodDefault, ZodEffects を unwrap
|
|
355
|
-
let fieldDef = field;
|
|
356
|
-
const getTypeName = (def) => def?._def?.typeName || def?.constructor?.name || '';
|
|
357
|
-
|
|
358
|
-
// 繰り返し unwrap(複数のラッパーがある場合に対応)
|
|
359
|
-
let unwrapped = false;
|
|
360
|
-
do {
|
|
361
|
-
unwrapped = false;
|
|
362
|
-
const typeName = getTypeName(fieldDef);
|
|
363
|
-
|
|
364
|
-
if (typeName === 'ZodOptional') {
|
|
365
|
-
isOptional = true;
|
|
366
|
-
fieldDef = fieldDef._def.innerType;
|
|
367
|
-
unwrapped = true;
|
|
368
|
-
} else if (typeName === 'ZodDefault') {
|
|
369
|
-
// .default() は optional と同様に扱う
|
|
370
|
-
isOptional = true;
|
|
371
|
-
fieldDef = fieldDef._def.innerType;
|
|
372
|
-
unwrapped = true;
|
|
373
|
-
} else if (typeName === 'ZodEffects') {
|
|
374
|
-
// .min(), .max(), .regex() などの Effects を unwrap
|
|
375
|
-
fieldDef = fieldDef._def.schema;
|
|
376
|
-
unwrapped = true;
|
|
377
|
-
}
|
|
378
|
-
} while (unwrapped);
|
|
379
|
-
|
|
380
|
-
// ZodArray をチェック
|
|
381
|
-
if (getTypeName(fieldDef) === 'ZodArray') {
|
|
382
|
-
isArray = true;
|
|
383
|
-
fieldDef = fieldDef._def.type || fieldDef._def.element;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
// 基本型を判定
|
|
387
|
-
const typeName = getTypeName(fieldDef);
|
|
388
|
-
if (typeName === 'ZodString') type = 'string';
|
|
389
|
-
else if (typeName === 'ZodNumber') type = 'number';
|
|
390
|
-
else if (typeName === 'ZodBoolean') type = 'boolean';
|
|
391
|
-
else if (typeName === 'ZodDate') type = 'date';
|
|
392
|
-
else if (typeName === 'ZodObject') type = 'object';
|
|
393
|
-
else if (typeName === 'ZodEnum' || typeName === 'ZodNativeEnum') {
|
|
394
|
-
type = 'string';
|
|
395
|
-
// enum の選択肢を取得(複数のZodバージョンに対応)
|
|
396
|
-
if (fieldDef.options) {
|
|
397
|
-
// Zod v3.23+ では options プロパティを使用
|
|
398
|
-
enumValues = Array.isArray(fieldDef.options)
|
|
399
|
-
? fieldDef.options
|
|
400
|
-
: Object.values(fieldDef.options);
|
|
401
|
-
} else if (fieldDef._def.values) {
|
|
402
|
-
// 古いバージョンでは _def.values を使用
|
|
403
|
-
enumValues = Array.isArray(fieldDef._def.values)
|
|
404
|
-
? fieldDef._def.values
|
|
405
|
-
: Object.values(fieldDef._def.values);
|
|
406
|
-
} else if (fieldDef._def.entries) {
|
|
407
|
-
// さらに古いバージョンでは _def.entries を使用
|
|
408
|
-
enumValues = Array.isArray(fieldDef._def.entries)
|
|
409
|
-
? fieldDef._def.entries
|
|
410
|
-
: Object.values(fieldDef._def.entries);
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
// 外部キー検出: フィールド名が <ModelName>Id のパターンの場合
|
|
415
|
-
let isForeignKey = false;
|
|
416
|
-
let referencedModel = undefined;
|
|
417
|
-
if (key.endsWith('Id') && key.length > 2 && type === 'string') {
|
|
418
|
-
// categoryId -> Category, userId -> User など
|
|
419
|
-
const modelName = key.slice(0, -2); // "Id" を除去
|
|
420
|
-
referencedModel = modelName.charAt(0).toUpperCase() + modelName.slice(1); // 先頭を大文字に
|
|
421
|
-
isForeignKey = true;
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
return { name: key, type, isOptional, isArray, enumValues, isForeignKey, referencedModel };
|
|
425
|
-
});
|
|
426
|
-
|
|
427
|
-
console.log(JSON.stringify(fields));
|
|
428
|
-
}
|
|
331
|
+
const scriptCode = `
|
|
332
|
+
import { z } from 'zod/v4';
|
|
333
|
+
|
|
334
|
+
// インライン化した依存スキーマ
|
|
335
|
+
${inlinedDeps}
|
|
336
|
+
// モデルファイルの内容を評価
|
|
337
|
+
${modelContent}
|
|
338
|
+
|
|
339
|
+
const schema = ${schemaName};
|
|
340
|
+
|
|
341
|
+
// Zod v3とv4の両方に対応
|
|
342
|
+
const isObject = (schema && schema._def &&
|
|
343
|
+
(schema._def.typeName === 'ZodObject' || schema.constructor?.name === 'ZodObject' || typeof schema._def.shape === 'function'));
|
|
344
|
+
|
|
345
|
+
if (isObject) {
|
|
346
|
+
const shape = typeof schema._def.shape === 'function' ? schema._def.shape() : schema._def.shape;
|
|
347
|
+
const fields = Object.keys(shape).map(key => {
|
|
348
|
+
const field = shape[key];
|
|
349
|
+
let type = 'string';
|
|
350
|
+
let isOptional = false;
|
|
351
|
+
let isArray = false;
|
|
352
|
+
let enumValues = undefined;
|
|
353
|
+
|
|
354
|
+
// ZodOptional, ZodDefault, ZodEffects を unwrap
|
|
355
|
+
let fieldDef = field;
|
|
356
|
+
const getTypeName = (def) => def?._def?.typeName || def?.constructor?.name || '';
|
|
357
|
+
|
|
358
|
+
// 繰り返し unwrap(複数のラッパーがある場合に対応)
|
|
359
|
+
let unwrapped = false;
|
|
360
|
+
do {
|
|
361
|
+
unwrapped = false;
|
|
362
|
+
const typeName = getTypeName(fieldDef);
|
|
363
|
+
|
|
364
|
+
if (typeName === 'ZodOptional') {
|
|
365
|
+
isOptional = true;
|
|
366
|
+
fieldDef = fieldDef._def.innerType;
|
|
367
|
+
unwrapped = true;
|
|
368
|
+
} else if (typeName === 'ZodDefault') {
|
|
369
|
+
// .default() は optional と同様に扱う
|
|
370
|
+
isOptional = true;
|
|
371
|
+
fieldDef = fieldDef._def.innerType;
|
|
372
|
+
unwrapped = true;
|
|
373
|
+
} else if (typeName === 'ZodEffects') {
|
|
374
|
+
// .min(), .max(), .regex() などの Effects を unwrap
|
|
375
|
+
fieldDef = fieldDef._def.schema;
|
|
376
|
+
unwrapped = true;
|
|
377
|
+
}
|
|
378
|
+
} while (unwrapped);
|
|
379
|
+
|
|
380
|
+
// ZodArray をチェック
|
|
381
|
+
if (getTypeName(fieldDef) === 'ZodArray') {
|
|
382
|
+
isArray = true;
|
|
383
|
+
fieldDef = fieldDef._def.type || fieldDef._def.element;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// 基本型を判定
|
|
387
|
+
const typeName = getTypeName(fieldDef);
|
|
388
|
+
if (typeName === 'ZodString') type = 'string';
|
|
389
|
+
else if (typeName === 'ZodNumber') type = 'number';
|
|
390
|
+
else if (typeName === 'ZodBoolean') type = 'boolean';
|
|
391
|
+
else if (typeName === 'ZodDate') type = 'date';
|
|
392
|
+
else if (typeName === 'ZodObject') type = 'object';
|
|
393
|
+
else if (typeName === 'ZodEnum' || typeName === 'ZodNativeEnum') {
|
|
394
|
+
type = 'string';
|
|
395
|
+
// enum の選択肢を取得(複数のZodバージョンに対応)
|
|
396
|
+
if (fieldDef.options) {
|
|
397
|
+
// Zod v3.23+ では options プロパティを使用
|
|
398
|
+
enumValues = Array.isArray(fieldDef.options)
|
|
399
|
+
? fieldDef.options
|
|
400
|
+
: Object.values(fieldDef.options);
|
|
401
|
+
} else if (fieldDef._def.values) {
|
|
402
|
+
// 古いバージョンでは _def.values を使用
|
|
403
|
+
enumValues = Array.isArray(fieldDef._def.values)
|
|
404
|
+
? fieldDef._def.values
|
|
405
|
+
: Object.values(fieldDef._def.values);
|
|
406
|
+
} else if (fieldDef._def.entries) {
|
|
407
|
+
// さらに古いバージョンでは _def.entries を使用
|
|
408
|
+
enumValues = Array.isArray(fieldDef._def.entries)
|
|
409
|
+
? fieldDef._def.entries
|
|
410
|
+
: Object.values(fieldDef._def.entries);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// 外部キー検出: フィールド名が <ModelName>Id のパターンの場合
|
|
415
|
+
let isForeignKey = false;
|
|
416
|
+
let referencedModel = undefined;
|
|
417
|
+
if (key.endsWith('Id') && key.length > 2 && type === 'string') {
|
|
418
|
+
// categoryId -> Category, userId -> User など
|
|
419
|
+
const modelName = key.slice(0, -2); // "Id" を除去
|
|
420
|
+
referencedModel = modelName.charAt(0).toUpperCase() + modelName.slice(1); // 先頭を大文字に
|
|
421
|
+
isForeignKey = true;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
return { name: key, type, isOptional, isArray, enumValues, isForeignKey, referencedModel };
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
console.log(JSON.stringify(fields));
|
|
428
|
+
}
|
|
429
429
|
`;
|
|
430
430
|
writeFileSync(tempScript, scriptCode, 'utf8');
|
|
431
431
|
try {
|
|
@@ -534,7 +534,7 @@ function toKebabCase(str) {
|
|
|
534
534
|
}
|
|
535
535
|
/**
|
|
536
536
|
* models ディレクトリから全てのモデル情報を取得
|
|
537
|
-
* @param modelsDir モデルディレクトリのパス(デフォルト: "
|
|
537
|
+
* @param modelsDir モデルディレクトリのパス(デフォルト: "shared/models")
|
|
538
538
|
* @returns モデル情報の配列
|
|
539
539
|
*/
|
|
540
540
|
async function getAllModels(modelsDir = "shared/models") {
|
|
@@ -12,113 +12,113 @@ const model_parser_1 = require("./model-parser");
|
|
|
12
12
|
* BFF callFunction ヘルパー (lib/api/call-function.ts) のコードを生成
|
|
13
13
|
*/
|
|
14
14
|
function generateBFFCallFunction() {
|
|
15
|
-
return `import { NextRequest, NextResponse } from 'next/server';
|
|
16
|
-
import { z } from 'zod/v4';
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* SwallowKit BFF Call Function Helper
|
|
20
|
-
* Azure Functions を呼び出す汎用ヘルパー
|
|
21
|
-
*
|
|
22
|
-
* @example
|
|
23
|
-
* // シンプルな GET
|
|
24
|
-
* return callFunction({ method: 'GET', path: '/api/todo', responseSchema: z.array(TodoSchema) });
|
|
25
|
-
*
|
|
26
|
-
* // バリデーション付き POST
|
|
27
|
-
* return callFunction({ method: 'POST', path: '/api/todo', body, inputSchema: InputSchema, responseSchema: TodoSchema, successStatus: 201 });
|
|
28
|
-
*
|
|
29
|
-
* // カスタムビジネスロジック関数の呼び出し
|
|
30
|
-
* return callFunction({ method: 'POST', path: '/api/todo/archive', body: { ids } });
|
|
31
|
-
*/
|
|
32
|
-
|
|
33
|
-
function getFunctionsBaseUrl(): string {
|
|
34
|
-
return process.env.BACKEND_FUNCTIONS_BASE_URL || 'http://localhost:7071';
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
interface CallFunctionConfig<TInput = any, TOutput = any> {
|
|
38
|
-
/** HTTP メソッド */
|
|
39
|
-
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
40
|
-
/** Azure Functions のパス (例: '/api/todo', '/api/todo/123') */
|
|
41
|
-
path: string;
|
|
42
|
-
/** リクエストボディ (POST/PUT 用) */
|
|
43
|
-
body?: any;
|
|
44
|
-
/** 入力バリデーション用 Zod スキーマ (省略時はバリデーションなし) */
|
|
45
|
-
inputSchema?: z.ZodSchema<TInput>;
|
|
46
|
-
/** 出力バリデーション用 Zod スキーマ (省略時はそのまま返す) */
|
|
47
|
-
responseSchema?: z.ZodSchema<TOutput>;
|
|
48
|
-
/** 成功時の HTTP ステータスコード (デフォルト: 200) */
|
|
49
|
-
successStatus?: number;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export async function callFunction<TInput = any, TOutput = any>(
|
|
53
|
-
config: CallFunctionConfig<TInput, TOutput>
|
|
54
|
-
): Promise<NextResponse> {
|
|
55
|
-
const { method, path, body, inputSchema, responseSchema, successStatus = 200 } = config;
|
|
56
|
-
|
|
57
|
-
try {
|
|
58
|
-
// 入力バリデーション
|
|
59
|
-
let validatedBody = body;
|
|
60
|
-
if (inputSchema && body !== undefined) {
|
|
61
|
-
const result = inputSchema.safeParse(body);
|
|
62
|
-
if (!result.success) {
|
|
63
|
-
console.error('[BFF] Validation failed:', result.error.issues);
|
|
64
|
-
return NextResponse.json(
|
|
65
|
-
{ error: 'Validation failed', details: result.error.issues },
|
|
66
|
-
{ status: 400 }
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
validatedBody = result.data;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Azure Functions を呼び出し
|
|
73
|
-
const functionsBaseUrl = getFunctionsBaseUrl();
|
|
74
|
-
const url = functionsBaseUrl + path;
|
|
75
|
-
console.log(\`[BFF] \${method} \${url}\`);
|
|
76
|
-
|
|
77
|
-
const response = await fetch(url, {
|
|
78
|
-
method,
|
|
79
|
-
headers: { 'Content-Type': 'application/json' },
|
|
80
|
-
body: validatedBody !== undefined ? JSON.stringify(validatedBody) : undefined,
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
console.log('[BFF] Functions response status:', response.status);
|
|
84
|
-
|
|
85
|
-
// エラーレスポンスの転送
|
|
86
|
-
if (!response.ok) {
|
|
87
|
-
const text = await response.text();
|
|
88
|
-
console.error('[BFF] Functions error:', { status: response.status, body: text });
|
|
89
|
-
|
|
90
|
-
let errorMessage = 'Request failed';
|
|
91
|
-
try {
|
|
92
|
-
const error = JSON.parse(text);
|
|
93
|
-
errorMessage = error.error || errorMessage;
|
|
94
|
-
} catch {
|
|
95
|
-
errorMessage = text || errorMessage;
|
|
96
|
-
}
|
|
97
|
-
return NextResponse.json({ error: errorMessage }, { status: response.status });
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// DELETE 204 の場合はボディなし
|
|
101
|
-
if (response.status === 204 || method === 'DELETE') {
|
|
102
|
-
return new NextResponse(null, { status: 204 });
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// レスポンスの取得と出力バリデーション
|
|
106
|
-
const data = await response.json();
|
|
107
|
-
|
|
108
|
-
if (responseSchema) {
|
|
109
|
-
const validated = responseSchema.parse(data);
|
|
110
|
-
return NextResponse.json(validated, { status: successStatus });
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
return NextResponse.json(data, { status: successStatus });
|
|
114
|
-
} catch (error: any) {
|
|
115
|
-
console.error(\`[BFF] Error:\`, error);
|
|
116
|
-
return NextResponse.json(
|
|
117
|
-
{ error: error.message || 'Internal server error' },
|
|
118
|
-
{ status: 500 }
|
|
119
|
-
);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
15
|
+
return `import { NextRequest, NextResponse } from 'next/server';
|
|
16
|
+
import { z } from 'zod/v4';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* SwallowKit BFF Call Function Helper
|
|
20
|
+
* Azure Functions を呼び出す汎用ヘルパー
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* // シンプルな GET
|
|
24
|
+
* return callFunction({ method: 'GET', path: '/api/todo', responseSchema: z.array(TodoSchema) });
|
|
25
|
+
*
|
|
26
|
+
* // バリデーション付き POST
|
|
27
|
+
* return callFunction({ method: 'POST', path: '/api/todo', body, inputSchema: InputSchema, responseSchema: TodoSchema, successStatus: 201 });
|
|
28
|
+
*
|
|
29
|
+
* // カスタムビジネスロジック関数の呼び出し
|
|
30
|
+
* return callFunction({ method: 'POST', path: '/api/todo/archive', body: { ids } });
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
function getFunctionsBaseUrl(): string {
|
|
34
|
+
return process.env.BACKEND_FUNCTIONS_BASE_URL || process.env.FUNCTIONS_BASE_URL || 'http://localhost:7071';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface CallFunctionConfig<TInput = any, TOutput = any> {
|
|
38
|
+
/** HTTP メソッド */
|
|
39
|
+
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
40
|
+
/** Azure Functions のパス (例: '/api/todo', '/api/todo/123') */
|
|
41
|
+
path: string;
|
|
42
|
+
/** リクエストボディ (POST/PUT 用) */
|
|
43
|
+
body?: any;
|
|
44
|
+
/** 入力バリデーション用 Zod スキーマ (省略時はバリデーションなし) */
|
|
45
|
+
inputSchema?: z.ZodSchema<TInput>;
|
|
46
|
+
/** 出力バリデーション用 Zod スキーマ (省略時はそのまま返す) */
|
|
47
|
+
responseSchema?: z.ZodSchema<TOutput>;
|
|
48
|
+
/** 成功時の HTTP ステータスコード (デフォルト: 200) */
|
|
49
|
+
successStatus?: number;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function callFunction<TInput = any, TOutput = any>(
|
|
53
|
+
config: CallFunctionConfig<TInput, TOutput>
|
|
54
|
+
): Promise<NextResponse> {
|
|
55
|
+
const { method, path, body, inputSchema, responseSchema, successStatus = 200 } = config;
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
// 入力バリデーション
|
|
59
|
+
let validatedBody = body;
|
|
60
|
+
if (inputSchema && body !== undefined) {
|
|
61
|
+
const result = inputSchema.safeParse(body);
|
|
62
|
+
if (!result.success) {
|
|
63
|
+
console.error('[BFF] Validation failed:', result.error.issues);
|
|
64
|
+
return NextResponse.json(
|
|
65
|
+
{ error: 'Validation failed', details: result.error.issues },
|
|
66
|
+
{ status: 400 }
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
validatedBody = result.data;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Azure Functions を呼び出し
|
|
73
|
+
const functionsBaseUrl = getFunctionsBaseUrl();
|
|
74
|
+
const url = functionsBaseUrl + path;
|
|
75
|
+
console.log(\`[BFF] \${method} \${url}\`);
|
|
76
|
+
|
|
77
|
+
const response = await fetch(url, {
|
|
78
|
+
method,
|
|
79
|
+
headers: { 'Content-Type': 'application/json' },
|
|
80
|
+
body: validatedBody !== undefined ? JSON.stringify(validatedBody) : undefined,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
console.log('[BFF] Functions response status:', response.status);
|
|
84
|
+
|
|
85
|
+
// エラーレスポンスの転送
|
|
86
|
+
if (!response.ok) {
|
|
87
|
+
const text = await response.text();
|
|
88
|
+
console.error('[BFF] Functions error:', { status: response.status, body: text });
|
|
89
|
+
|
|
90
|
+
let errorMessage = 'Request failed';
|
|
91
|
+
try {
|
|
92
|
+
const error = JSON.parse(text);
|
|
93
|
+
errorMessage = error.error || errorMessage;
|
|
94
|
+
} catch {
|
|
95
|
+
errorMessage = text || errorMessage;
|
|
96
|
+
}
|
|
97
|
+
return NextResponse.json({ error: errorMessage }, { status: response.status });
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// DELETE 204 の場合はボディなし
|
|
101
|
+
if (response.status === 204 || method === 'DELETE') {
|
|
102
|
+
return new NextResponse(null, { status: 204 });
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// レスポンスの取得と出力バリデーション
|
|
106
|
+
const data = await response.json();
|
|
107
|
+
|
|
108
|
+
if (responseSchema) {
|
|
109
|
+
const validated = responseSchema.parse(data);
|
|
110
|
+
return NextResponse.json(validated, { status: successStatus });
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return NextResponse.json(data, { status: successStatus });
|
|
114
|
+
} catch (error: any) {
|
|
115
|
+
console.error(\`[BFF] Error:\`, error);
|
|
116
|
+
return NextResponse.json(
|
|
117
|
+
{ error: error.message || 'Internal server error' },
|
|
118
|
+
{ status: 500 }
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
122
|
`;
|
|
123
123
|
}
|
|
124
124
|
/**
|
|
@@ -129,81 +129,81 @@ function generateCompactBFFRoutes(model, sharedPackageName) {
|
|
|
129
129
|
const modelCamel = (0, model_parser_1.toCamelCase)(modelName);
|
|
130
130
|
const modelKebab = (0, model_parser_1.toKebabCase)(modelName);
|
|
131
131
|
const schemaName = model.schemaName;
|
|
132
|
-
const listRoute = `import { NextRequest } from 'next/server';
|
|
133
|
-
import { callFunction } from '@/lib/api/call-function';
|
|
134
|
-
import { ${schemaName} } from '${sharedPackageName}';
|
|
135
|
-
import { z } from 'zod/v4';
|
|
136
|
-
|
|
137
|
-
const InputSchema = ${schemaName}.omit({ id: true, createdAt: true, updatedAt: true });
|
|
138
|
-
|
|
139
|
-
// GET /api/${modelCamel} - 一覧取得
|
|
140
|
-
export async function GET() {
|
|
141
|
-
return callFunction({
|
|
142
|
-
method: 'GET',
|
|
143
|
-
path: '/api/${modelCamel}',
|
|
144
|
-
responseSchema: z.array(${schemaName}),
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// POST /api/${modelCamel} - 新規作成
|
|
149
|
-
export async function POST(request: NextRequest) {
|
|
150
|
-
const body = await request.json();
|
|
151
|
-
return callFunction({
|
|
152
|
-
method: 'POST',
|
|
153
|
-
path: '/api/${modelCamel}',
|
|
154
|
-
body,
|
|
155
|
-
inputSchema: InputSchema,
|
|
156
|
-
responseSchema: ${schemaName},
|
|
157
|
-
successStatus: 201,
|
|
158
|
-
});
|
|
159
|
-
}
|
|
132
|
+
const listRoute = `import { NextRequest } from 'next/server';
|
|
133
|
+
import { callFunction } from '@/lib/api/call-function';
|
|
134
|
+
import { ${schemaName} } from '${sharedPackageName}';
|
|
135
|
+
import { z } from 'zod/v4';
|
|
136
|
+
|
|
137
|
+
const InputSchema = ${schemaName}.omit({ id: true, createdAt: true, updatedAt: true });
|
|
138
|
+
|
|
139
|
+
// GET /api/${modelCamel} - 一覧取得
|
|
140
|
+
export async function GET() {
|
|
141
|
+
return callFunction({
|
|
142
|
+
method: 'GET',
|
|
143
|
+
path: '/api/${modelCamel}',
|
|
144
|
+
responseSchema: z.array(${schemaName}),
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// POST /api/${modelCamel} - 新規作成
|
|
149
|
+
export async function POST(request: NextRequest) {
|
|
150
|
+
const body = await request.json();
|
|
151
|
+
return callFunction({
|
|
152
|
+
method: 'POST',
|
|
153
|
+
path: '/api/${modelCamel}',
|
|
154
|
+
body,
|
|
155
|
+
inputSchema: InputSchema,
|
|
156
|
+
responseSchema: ${schemaName},
|
|
157
|
+
successStatus: 201,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
160
|
`;
|
|
161
|
-
const detailRoute = `import { NextRequest } from 'next/server';
|
|
162
|
-
import { callFunction } from '@/lib/api/call-function';
|
|
163
|
-
import { ${schemaName} } from '${sharedPackageName}';
|
|
164
|
-
|
|
165
|
-
const InputSchema = ${schemaName}.omit({ id: true, createdAt: true, updatedAt: true });
|
|
166
|
-
|
|
167
|
-
// GET /api/${modelCamel}/{id} - 詳細取得
|
|
168
|
-
export async function GET(
|
|
169
|
-
_request: NextRequest,
|
|
170
|
-
{ params }: { params: Promise<{ id: string }> }
|
|
171
|
-
) {
|
|
172
|
-
const { id } = await params;
|
|
173
|
-
return callFunction({
|
|
174
|
-
method: 'GET',
|
|
175
|
-
path: \`/api/${modelCamel}/\${id}\`,
|
|
176
|
-
responseSchema: ${schemaName},
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// PUT /api/${modelCamel}/{id} - 更新
|
|
181
|
-
export async function PUT(
|
|
182
|
-
request: NextRequest,
|
|
183
|
-
{ params }: { params: Promise<{ id: string }> }
|
|
184
|
-
) {
|
|
185
|
-
const { id } = await params;
|
|
186
|
-
const body = await request.json();
|
|
187
|
-
return callFunction({
|
|
188
|
-
method: 'PUT',
|
|
189
|
-
path: \`/api/${modelCamel}/\${id}\`,
|
|
190
|
-
body,
|
|
191
|
-
inputSchema: InputSchema,
|
|
192
|
-
responseSchema: ${schemaName},
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// DELETE /api/${modelCamel}/{id} - 削除
|
|
197
|
-
export async function DELETE(
|
|
198
|
-
_request: NextRequest,
|
|
199
|
-
{ params }: { params: Promise<{ id: string }> }
|
|
200
|
-
) {
|
|
201
|
-
const { id } = await params;
|
|
202
|
-
return callFunction({
|
|
203
|
-
method: 'DELETE',
|
|
204
|
-
path: \`/api/${modelCamel}/\${id}\`,
|
|
205
|
-
});
|
|
206
|
-
}
|
|
161
|
+
const detailRoute = `import { NextRequest } from 'next/server';
|
|
162
|
+
import { callFunction } from '@/lib/api/call-function';
|
|
163
|
+
import { ${schemaName} } from '${sharedPackageName}';
|
|
164
|
+
|
|
165
|
+
const InputSchema = ${schemaName}.omit({ id: true, createdAt: true, updatedAt: true });
|
|
166
|
+
|
|
167
|
+
// GET /api/${modelCamel}/{id} - 詳細取得
|
|
168
|
+
export async function GET(
|
|
169
|
+
_request: NextRequest,
|
|
170
|
+
{ params }: { params: Promise<{ id: string }> }
|
|
171
|
+
) {
|
|
172
|
+
const { id } = await params;
|
|
173
|
+
return callFunction({
|
|
174
|
+
method: 'GET',
|
|
175
|
+
path: \`/api/${modelCamel}/\${id}\`,
|
|
176
|
+
responseSchema: ${schemaName},
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// PUT /api/${modelCamel}/{id} - 更新
|
|
181
|
+
export async function PUT(
|
|
182
|
+
request: NextRequest,
|
|
183
|
+
{ params }: { params: Promise<{ id: string }> }
|
|
184
|
+
) {
|
|
185
|
+
const { id } = await params;
|
|
186
|
+
const body = await request.json();
|
|
187
|
+
return callFunction({
|
|
188
|
+
method: 'PUT',
|
|
189
|
+
path: \`/api/${modelCamel}/\${id}\`,
|
|
190
|
+
body,
|
|
191
|
+
inputSchema: InputSchema,
|
|
192
|
+
responseSchema: ${schemaName},
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// DELETE /api/${modelCamel}/{id} - 削除
|
|
197
|
+
export async function DELETE(
|
|
198
|
+
_request: NextRequest,
|
|
199
|
+
{ params }: { params: Promise<{ id: string }> }
|
|
200
|
+
) {
|
|
201
|
+
const { id } = await params;
|
|
202
|
+
return callFunction({
|
|
203
|
+
method: 'DELETE',
|
|
204
|
+
path: \`/api/${modelCamel}/\${id}\`,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
207
|
`;
|
|
208
208
|
return {
|
|
209
209
|
listRoute,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openapi-generator.d.ts","sourceRoot":"","sources":["../../../src/core/scaffold/openapi-generator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,SAAS,EAAe,MAAM,gBAAgB,CAAC;AAqFnE,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,EAAE,SAAS,GAAG,MAAM,CA8HzF"}
|