swallowkit 1.0.0-beta.46 → 1.0.0-beta.48

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.
Files changed (84) hide show
  1. package/dist/cli/commands/add-auth.d.ts +3 -0
  2. package/dist/cli/commands/add-auth.d.ts.map +1 -1
  3. package/dist/cli/commands/add-auth.js +350 -17
  4. package/dist/cli/commands/add-auth.js.map +1 -1
  5. package/dist/cli/commands/dev.d.ts +4 -0
  6. package/dist/cli/commands/dev.d.ts.map +1 -1
  7. package/dist/cli/commands/dev.js +104 -13
  8. package/dist/cli/commands/dev.js.map +1 -1
  9. package/dist/cli/commands/init.d.ts +2 -0
  10. package/dist/cli/commands/init.d.ts.map +1 -1
  11. package/dist/cli/commands/init.js +56 -30
  12. package/dist/cli/commands/init.js.map +1 -1
  13. package/dist/cli/commands/scaffold.d.ts +9 -0
  14. package/dist/cli/commands/scaffold.d.ts.map +1 -1
  15. package/dist/cli/commands/scaffold.js +84 -8
  16. package/dist/cli/commands/scaffold.js.map +1 -1
  17. package/dist/cli/index.d.ts.map +1 -1
  18. package/dist/cli/index.js +5 -1
  19. package/dist/cli/index.js.map +1 -1
  20. package/dist/core/config.d.ts +7 -2
  21. package/dist/core/config.d.ts.map +1 -1
  22. package/dist/core/config.js +81 -1
  23. package/dist/core/config.js.map +1 -1
  24. package/dist/core/mock/connector-mock-server.js +2 -2
  25. package/dist/core/mock/connector-mock-server.js.map +1 -1
  26. package/dist/core/project/manifest.js +2 -2
  27. package/dist/core/project/manifest.js.map +1 -1
  28. package/dist/core/project/validation.d.ts.map +1 -1
  29. package/dist/core/project/validation.js +17 -0
  30. package/dist/core/project/validation.js.map +1 -1
  31. package/dist/core/scaffold/auth-generator.d.ts +28 -3
  32. package/dist/core/scaffold/auth-generator.d.ts.map +1 -1
  33. package/dist/core/scaffold/auth-generator.js +493 -23
  34. package/dist/core/scaffold/auth-generator.js.map +1 -1
  35. package/dist/core/scaffold/connector-functions-generator.d.ts +3 -3
  36. package/dist/core/scaffold/connector-functions-generator.d.ts.map +1 -1
  37. package/dist/core/scaffold/connector-functions-generator.js +23 -21
  38. package/dist/core/scaffold/connector-functions-generator.js.map +1 -1
  39. package/dist/core/scaffold/functions-generator.d.ts +2 -2
  40. package/dist/core/scaffold/functions-generator.d.ts.map +1 -1
  41. package/dist/core/scaffold/functions-generator.js +20 -18
  42. package/dist/core/scaffold/functions-generator.js.map +1 -1
  43. package/dist/core/scaffold/model-parser.d.ts.map +1 -1
  44. package/dist/core/scaffold/model-parser.js +8 -3
  45. package/dist/core/scaffold/model-parser.js.map +1 -1
  46. package/dist/core/scaffold/nextjs-generator.d.ts.map +1 -1
  47. package/dist/core/scaffold/nextjs-generator.js +13 -1
  48. package/dist/core/scaffold/nextjs-generator.js.map +1 -1
  49. package/dist/core/scaffold/ui-generator.d.ts +3 -0
  50. package/dist/core/scaffold/ui-generator.d.ts.map +1 -1
  51. package/dist/core/scaffold/ui-generator.js +12 -8
  52. package/dist/core/scaffold/ui-generator.js.map +1 -1
  53. package/dist/types/index.d.ts +35 -5
  54. package/dist/types/index.d.ts.map +1 -1
  55. package/dist/utils/package-manager.d.ts.map +1 -1
  56. package/dist/utils/package-manager.js +1 -0
  57. package/dist/utils/package-manager.js.map +1 -1
  58. package/package.json +1 -1
  59. package/src/__tests__/__snapshots__/functions-generator.test.ts.snap +20 -20
  60. package/src/__tests__/__snapshots__/nextjs-generator.test.ts.snap +13 -1
  61. package/src/__tests__/auth.test.ts +107 -0
  62. package/src/__tests__/config.test.ts +22 -1
  63. package/src/__tests__/dev.test.ts +27 -0
  64. package/src/__tests__/init.test.ts +19 -0
  65. package/src/__tests__/mcp.test.ts +2 -2
  66. package/src/__tests__/multi-auth.test.ts +133 -0
  67. package/src/__tests__/nextjs-generator.test.ts +8 -0
  68. package/src/cli/commands/add-auth.ts +351 -18
  69. package/src/cli/commands/dev.ts +116 -16
  70. package/src/cli/commands/init.ts +56 -30
  71. package/src/cli/commands/scaffold.ts +86 -10
  72. package/src/cli/index.ts +5 -1
  73. package/src/core/config.ts +69 -2
  74. package/src/core/mock/connector-mock-server.ts +2 -2
  75. package/src/core/project/manifest.ts +3 -3
  76. package/src/core/project/validation.ts +16 -0
  77. package/src/core/scaffold/auth-generator.ts +500 -24
  78. package/src/core/scaffold/connector-functions-generator.ts +26 -21
  79. package/src/core/scaffold/functions-generator.ts +21 -19
  80. package/src/core/scaffold/model-parser.ts +9 -3
  81. package/src/core/scaffold/nextjs-generator.ts +13 -1
  82. package/src/core/scaffold/ui-generator.ts +15 -8
  83. package/src/types/index.ts +36 -5
  84. package/src/utils/package-manager.ts +1 -0
@@ -11,6 +11,7 @@ import {
11
11
  ApiModelConnectorConfig,
12
12
  ConnectorOperation,
13
13
  ModelAuthPolicy,
14
+ AuthProvider,
14
15
  } from "../../types";
15
16
  import { generateAuthImportTS, generateAuthGuardTS } from "./auth-generator";
16
17
 
@@ -24,7 +25,8 @@ export function generateRdbConnectorFunctionTS(
24
25
  sharedPackageName: string,
25
26
  connectorDef: RdbConnectorConfig,
26
27
  modelConnector: RdbModelConnectorConfig,
27
- authPolicy?: ModelAuthPolicy
28
+ authPolicy?: ModelAuthPolicy,
29
+ authProvider?: AuthProvider
28
30
  ): string {
29
31
  const modelCamel = toCamelCase(model.name);
30
32
  const schemaName = model.schemaName;
@@ -122,8 +124,9 @@ export function generateRdbConnectorFunctionTS(
122
124
 
123
125
  // Auth guard setup
124
126
  const hasAuth = !!authPolicy;
125
- const authImport = hasAuth ? `\n${generateAuthImportTS()}\n` : '';
126
- const readGuard = hasAuth ? `\n${generateAuthGuardTS(authPolicy!, 'read')}\n` : '';
127
+ const namedAuth = Boolean(authPolicy && (authPolicy.policy || typeof authPolicy.read === 'string' || typeof authPolicy.write === 'string'));
128
+ const authImport = hasAuth ? `\n${generateAuthImportTS(namedAuth)}\n` : '';
129
+ const readGuard = hasAuth ? `\n${generateAuthGuardTS(authPolicy!, 'read', authProvider)}\n` : '';
127
130
  const authCatchBlock = hasAuth
128
131
  ? ` const authErr = handleAuthError(error);
129
132
  if (authErr) return authErr;\n `
@@ -143,7 +146,7 @@ ${getConnection}
143
146
  app.http('${modelCamel}-get-all', {
144
147
  methods: ['GET'],
145
148
  route: '${modelCamel}',
146
- authLevel: 'anonymous',
149
+ authLevel: 'function',
147
150
  handler: async (request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> => {
148
151
  try {${readGuard}
149
152
  ${queryAll}
@@ -162,7 +165,7 @@ app.http('${modelCamel}-get-all', {
162
165
  app.http('${modelCamel}-get-by-id', {
163
166
  methods: ['GET'],
164
167
  route: '${modelCamel}/{id}',
165
- authLevel: 'anonymous',
168
+ authLevel: 'function',
166
169
  handler: async (request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> => {
167
170
  try {${readGuard}
168
171
  const id = request.params.id;
@@ -190,7 +193,8 @@ export function generateApiConnectorFunctionTS(
190
193
  sharedPackageName: string,
191
194
  connectorDef: ApiConnectorConfig,
192
195
  modelConnector: ApiModelConnectorConfig,
193
- authPolicy?: ModelAuthPolicy
196
+ authPolicy?: ModelAuthPolicy,
197
+ authProvider?: AuthProvider
194
198
  ): string {
195
199
  const modelCamel = toCamelCase(model.name);
196
200
  const schemaName = model.schemaName;
@@ -202,9 +206,10 @@ export function generateApiConnectorFunctionTS(
202
206
 
203
207
  // Auth guard setup (user access control, separate from connector auth to external APIs)
204
208
  const hasAuth = !!authPolicy;
205
- const authImportLine = hasAuth ? `\n${generateAuthImportTS()}\n` : '';
206
- const readGuard = hasAuth ? `\n${generateAuthGuardTS(authPolicy!, 'read')}\n` : '';
207
- const writeGuard = hasAuth ? `\n${generateAuthGuardTS(authPolicy!, 'write')}\n` : '';
209
+ const namedAuth = Boolean(authPolicy && (authPolicy.policy || typeof authPolicy.read === 'string' || typeof authPolicy.write === 'string'));
210
+ const authImportLine = hasAuth ? `\n${generateAuthImportTS(namedAuth)}\n` : '';
211
+ const readGuard = hasAuth ? `\n${generateAuthGuardTS(authPolicy!, 'read', authProvider)}\n` : '';
212
+ const writeGuard = hasAuth ? `\n${generateAuthGuardTS(authPolicy!, 'write', authProvider)}\n` : '';
208
213
  const authCatchBlock = hasAuth
209
214
  ? ` const authErr = handleAuthError(error);
210
215
  if (authErr) return authErr;\n `
@@ -229,7 +234,7 @@ ${authSetup.helperCode}
229
234
  app.http('${modelCamel}-get-all', {
230
235
  methods: ['GET'],
231
236
  route: '${modelCamel}',
232
- authLevel: 'anonymous',
237
+ authLevel: 'function',
233
238
  handler: async (request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> => {
234
239
  try {${readGuard}
235
240
  const url = getBaseUrl() + '${epPath}';
@@ -263,7 +268,7 @@ app.http('${modelCamel}-get-all', {
263
268
  app.http('${modelCamel}-get-by-id', {
264
269
  methods: ['GET'],
265
270
  route: '${modelCamel}/{id}',
266
- authLevel: 'anonymous',
271
+ authLevel: 'function',
267
272
  handler: async (request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> => {
268
273
  try {${readGuard}
269
274
  const id = request.params.id;
@@ -303,7 +308,7 @@ app.http('${modelCamel}-get-by-id', {
303
308
  app.http('${modelCamel}-create', {
304
309
  methods: ['POST'],
305
310
  route: '${modelCamel}',
306
- authLevel: 'anonymous',
311
+ authLevel: 'function',
307
312
  handler: async (request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> => {
308
313
  try {${writeGuard}
309
314
  const body = await request.json();
@@ -341,7 +346,7 @@ app.http('${modelCamel}-create', {
341
346
  app.http('${modelCamel}-update', {
342
347
  methods: ['PUT'],
343
348
  route: '${modelCamel}/{id}',
344
- authLevel: 'anonymous',
349
+ authLevel: 'function',
345
350
  handler: async (request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> => {
346
351
  try {${writeGuard}
347
352
  const id = request.params.id;
@@ -381,7 +386,7 @@ app.http('${modelCamel}-update', {
381
386
  app.http('${modelCamel}-delete', {
382
387
  methods: ['DELETE'],
383
388
  route: '${modelCamel}/{id}',
384
- authLevel: 'anonymous',
389
+ authLevel: 'function',
385
390
  handler: async (request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> => {
386
391
  try {${writeGuard}
387
392
  const id = request.params.id;
@@ -496,7 +501,7 @@ export function generateRdbConnectorFunctionCSharp(
496
501
  methods += `
497
502
  [Function("${modelCamel}GetAll")]
498
503
  public async Task<HttpResponseData> GetAll(
499
- [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "${modelCamel}")] HttpRequestData request)
504
+ [HttpTrigger(AuthorizationLevel.Function, "get", Route = "${modelCamel}")] HttpRequestData request)
500
505
  {
501
506
  try
502
507
  {
@@ -531,7 +536,7 @@ export function generateRdbConnectorFunctionCSharp(
531
536
  methods += `
532
537
  [Function("${modelCamel}GetById")]
533
538
  public async Task<HttpResponseData> GetById(
534
- [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "${modelCamel}/{id}")] HttpRequestData request,
539
+ [HttpTrigger(AuthorizationLevel.Function, "get", Route = "${modelCamel}/{id}")] HttpRequestData request,
535
540
  string id)
536
541
  {
537
542
  try
@@ -618,7 +623,7 @@ export function generateApiConnectorFunctionCSharp(
618
623
  methods += `
619
624
  [Function("${modelCamel}GetAll")]
620
625
  public async Task<HttpResponseData> GetAll(
621
- [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "${modelCamel}")] HttpRequestData request)
626
+ [HttpTrigger(AuthorizationLevel.Function, "get", Route = "${modelCamel}")] HttpRequestData request)
622
627
  {
623
628
  try
624
629
  {
@@ -651,7 +656,7 @@ export function generateApiConnectorFunctionCSharp(
651
656
  methods += `
652
657
  [Function("${modelCamel}GetById")]
653
658
  public async Task<HttpResponseData> GetById(
654
- [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "${modelCamel}/{id}")] HttpRequestData request,
659
+ [HttpTrigger(AuthorizationLevel.Function, "get", Route = "${modelCamel}/{id}")] HttpRequestData request,
655
660
  string id)
656
661
  {
657
662
  try
@@ -689,7 +694,7 @@ export function generateApiConnectorFunctionCSharp(
689
694
  methods += `
690
695
  [Function("${modelCamel}Create")]
691
696
  public async Task<HttpResponseData> Create(
692
- [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "${modelCamel}")] HttpRequestData request)
697
+ [HttpTrigger(AuthorizationLevel.Function, "post", Route = "${modelCamel}")] HttpRequestData request)
693
698
  {
694
699
  try
695
700
  {
@@ -725,7 +730,7 @@ export function generateApiConnectorFunctionCSharp(
725
730
  methods += `
726
731
  [Function("${modelCamel}Update")]
727
732
  public async Task<HttpResponseData> Update(
728
- [HttpTrigger(AuthorizationLevel.Anonymous, "put", Route = "${modelCamel}/{id}")] HttpRequestData request,
733
+ [HttpTrigger(AuthorizationLevel.Function, "put", Route = "${modelCamel}/{id}")] HttpRequestData request,
729
734
  string id)
730
735
  {
731
736
  try
@@ -759,7 +764,7 @@ export function generateApiConnectorFunctionCSharp(
759
764
  methods += `
760
765
  [Function("${modelCamel}Delete")]
761
766
  public async Task<HttpResponseData> Delete(
762
- [HttpTrigger(AuthorizationLevel.Anonymous, "delete", Route = "${modelCamel}/{id}")] HttpRequestData request,
767
+ [HttpTrigger(AuthorizationLevel.Function, "delete", Route = "${modelCamel}/{id}")] HttpRequestData request,
763
768
  string id)
764
769
  {
765
770
  try
@@ -5,14 +5,14 @@
5
5
  */
6
6
 
7
7
  import { ModelInfo, toCamelCase, toKebabCase } from "./model-parser";
8
- import { ModelAuthPolicy } from "../../types";
8
+ import { AuthProvider, ModelAuthPolicy } from "../../types";
9
9
  import { generateAuthImportTS, generateAuthGuardTS, generateAuthGuardCSharp, generateAuthGuardPython } from "./auth-generator";
10
10
 
11
11
  /**
12
12
  * Azure Functions エンティティファイルを生成(インラインハンドラー方式)
13
13
  * 各ハンドラーがベタ書きされており、ビジネスロジックの追加・変更が容易
14
14
  */
15
- export function generateCompactAzureFunctionsCRUD(model: ModelInfo, sharedPackageName: string, authPolicy?: ModelAuthPolicy): string {
15
+ export function generateCompactAzureFunctionsCRUD(model: ModelInfo, sharedPackageName: string, authPolicy?: ModelAuthPolicy, authProvider?: AuthProvider): string {
16
16
  const modelName = model.name;
17
17
  const modelCamel = toCamelCase(modelName);
18
18
  const schemaName = model.schemaName;
@@ -22,9 +22,10 @@ export function generateCompactAzureFunctionsCRUD(model: ModelInfo, sharedPackag
22
22
  const isIdPartition = partitionKeyField === 'id';
23
23
 
24
24
  const hasAuth = !!authPolicy;
25
- const authImport = hasAuth ? `\n${generateAuthImportTS()}\n` : '';
26
- const readGuard = hasAuth ? `\n${generateAuthGuardTS(authPolicy!, 'read')}\n` : '';
27
- const writeGuard = hasAuth ? `\n${generateAuthGuardTS(authPolicy!, 'write')}\n` : '';
25
+ const namedAuth = Boolean(authPolicy && (authPolicy.policy || typeof authPolicy.read === 'string' || typeof authPolicy.write === 'string'));
26
+ const authImport = hasAuth ? `\n${generateAuthImportTS(namedAuth)}\n` : '';
27
+ const readGuard = hasAuth ? `\n${generateAuthGuardTS(authPolicy!, 'read', authProvider)}\n` : '';
28
+ const writeGuard = hasAuth ? `\n${generateAuthGuardTS(authPolicy!, 'write', authProvider)}\n` : '';
28
29
  const authCatchBlock = hasAuth
29
30
  ? ` const authErr = handleAuthError(error);
30
31
  if (authErr) return authErr;\n`
@@ -69,7 +70,7 @@ const containerName = '${modelName.endsWith('s') ? modelName : modelName + 's'}'
69
70
  app.http('${modelCamel}-get-all', {
70
71
  methods: ['GET'],
71
72
  route: '${modelCamel}',
72
- authLevel: 'anonymous',
73
+ authLevel: 'function',
73
74
  extraInputs: [
74
75
  {
75
76
  type: 'cosmosDB',
@@ -105,7 +106,7 @@ ${getByIdHandler}
105
106
  app.http('${modelCamel}-create', {
106
107
  methods: ['POST'],
107
108
  route: '${modelCamel}',
108
- authLevel: 'anonymous',
109
+ authLevel: 'function',
109
110
  extraOutputs: [
110
111
  {
111
112
  type: 'cosmosDB',
@@ -158,7 +159,7 @@ function generateGetByIdWithBinding(modelCamel: string, schemaName: string, read
158
159
  app.http('${modelCamel}-get-by-id', {
159
160
  methods: ['GET'],
160
161
  route: '${modelCamel}/{id}',
161
- authLevel: 'anonymous',
162
+ authLevel: 'function',
162
163
  extraInputs: [
163
164
  {
164
165
  type: 'cosmosDB',
@@ -193,7 +194,7 @@ function generateGetByIdWithSdk(modelCamel: string, schemaName: string, readGuar
193
194
  app.http('${modelCamel}-get-by-id', {
194
195
  methods: ['GET'],
195
196
  route: '${modelCamel}/{id}',
196
- authLevel: 'anonymous',
197
+ authLevel: 'function',
197
198
  handler: async (request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> => {
198
199
  try {${readGuard}
199
200
  const id = request.params.id;
@@ -229,7 +230,7 @@ function generateUpdateWithBinding(modelCamel: string, schemaName: string, write
229
230
  app.http('${modelCamel}-update', {
230
231
  methods: ['PUT'],
231
232
  route: '${modelCamel}/{id}',
232
- authLevel: 'anonymous',
233
+ authLevel: 'function',
233
234
  extraInputs: [
234
235
  {
235
236
  type: 'cosmosDB',
@@ -294,7 +295,7 @@ function generateUpdateWithSdk(modelCamel: string, schemaName: string, partition
294
295
  app.http('${modelCamel}-update', {
295
296
  methods: ['PUT'],
296
297
  route: '${modelCamel}/{id}',
297
- authLevel: 'anonymous',
298
+ authLevel: 'function',
298
299
  handler: async (request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> => {
299
300
  try {${writeGuard}
300
301
  const id = request.params.id;
@@ -349,7 +350,7 @@ function generateDeleteIdPartition(modelCamel: string, writeGuard: string, authC
349
350
  app.http('${modelCamel}-delete', {
350
351
  methods: ['DELETE'],
351
352
  route: '${modelCamel}/{id}',
352
- authLevel: 'anonymous',
353
+ authLevel: 'function',
353
354
  handler: async (request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> => {
354
355
  try {${writeGuard}
355
356
  const id = request.params.id;
@@ -379,7 +380,7 @@ function generateDeleteCustomPartition(modelCamel: string, partitionKeyField: st
379
380
  app.http('${modelCamel}-delete', {
380
381
  methods: ['DELETE'],
381
382
  route: '${modelCamel}/{id}',
382
- authLevel: 'anonymous',
383
+ authLevel: 'function',
383
384
  handler: async (request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> => {
384
385
  try {${writeGuard}
385
386
  const id = request.params.id;
@@ -596,7 +597,7 @@ ${readItemMethod}
596
597
 
597
598
  [Function("${modelCamel}GetAll")]
598
599
  public async Task<HttpResponseData> GetAll(
599
- [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "${modelCamel}")] HttpRequestData request)
600
+ [HttpTrigger(AuthorizationLevel.Function, "get", Route = "${modelCamel}")] HttpRequestData request)
600
601
  {
601
602
  try
602
603
  {${readGuard}
@@ -637,7 +638,7 @@ ${readItemMethod}
637
638
 
638
639
  [Function("${modelCamel}GetById")]
639
640
  public async Task<HttpResponseData> GetById(
640
- [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "${modelCamel}/{id}")] HttpRequestData request,
641
+ [HttpTrigger(AuthorizationLevel.Function, "get", Route = "${modelCamel}/{id}")] HttpRequestData request,
641
642
  string id)
642
643
  {
643
644
  try
@@ -660,7 +661,7 @@ ${readItemMethod}
660
661
 
661
662
  [Function("${modelCamel}Create")]
662
663
  public async Task<HttpResponseData> Create(
663
- [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "${modelCamel}")] HttpRequestData request)
664
+ [HttpTrigger(AuthorizationLevel.Function, "post", Route = "${modelCamel}")] HttpRequestData request)
664
665
  {
665
666
  try
666
667
  {${writeGuard}
@@ -695,7 +696,7 @@ ${readItemMethod}
695
696
 
696
697
  [Function("${modelCamel}Update")]
697
698
  public async Task<HttpResponseData> Update(
698
- [HttpTrigger(AuthorizationLevel.Anonymous, "put", Route = "${modelCamel}/{id}")] HttpRequestData request,
699
+ [HttpTrigger(AuthorizationLevel.Function, "put", Route = "${modelCamel}/{id}")] HttpRequestData request,
699
700
  string id)
700
701
  {
701
702
  try
@@ -740,7 +741,7 @@ ${readItemMethod}
740
741
 
741
742
  [Function("${modelCamel}Delete")]
742
743
  public async Task<HttpResponseData> Delete(
743
- [HttpTrigger(AuthorizationLevel.Anonymous, "delete", Route = "${modelCamel}/{id}")] HttpRequestData request,
744
+ [HttpTrigger(AuthorizationLevel.Function, "delete", Route = "${modelCamel}/{id}")] HttpRequestData request,
744
745
  string id)
745
746
  {
746
747
  try
@@ -779,7 +780,8 @@ export function generatePythonAzureFunctionsCRUD(model: ModelInfo, authPolicy?:
779
780
  const isIdPartition = partitionKeyField === 'id';
780
781
 
781
782
  const hasAuth = !!authPolicy;
782
- const authImport = hasAuth ? '\nfrom auth.jwt_helper import require_auth, require_roles, handle_auth_error\n' : '';
783
+ const namedAuth = Boolean(authPolicy && (authPolicy.policy || typeof authPolicy.read === 'string' || typeof authPolicy.write === 'string'));
784
+ const authImport = hasAuth ? `\nfrom auth.${namedAuth ? 'auth_router' : 'jwt_helper'} import require_auth, require_roles, handle_auth_error\n` : '';
783
785
  // generateAuthGuardPython outputs at 4-space indent; inside try: we need 8-space
784
786
  const readGuardRaw = hasAuth ? generateAuthGuardPython(authPolicy!, 'read') : '';
785
787
  const writeGuardRaw = hasAuth ? generateAuthGuardPython(authPolicy!, 'write') : '';
@@ -779,14 +779,20 @@ export function parseAuthPolicy(content: string): ModelAuthPolicy | undefined {
779
779
  const read = extractRoles('read');
780
780
  const write = extractRoles('write');
781
781
 
782
- if (!roles && !read && !write) {
782
+ const extractName = (key: string): string | undefined => objectStr.match(new RegExp(`${key}\\s*:\\s*['"]([^'"]+)['"]`))?.[1];
783
+ const namedPolicy = extractName('policy');
784
+ const namedRead = extractName('read');
785
+ const namedWrite = extractName('write');
786
+
787
+ if (!roles && !read && !write && !namedPolicy && !namedRead && !namedWrite) {
783
788
  return undefined;
784
789
  }
785
790
 
786
791
  return {
787
792
  ...(roles ? { roles } : {}),
788
- ...(read ? { read } : {}),
789
- ...(write ? { write } : {}),
793
+ ...(namedRead ? { read: namedRead } : read ? { read } : {}),
794
+ ...(namedWrite ? { write: namedWrite } : write ? { write } : {}),
795
+ ...(namedPolicy ? { policy: namedPolicy } : {}),
790
796
  };
791
797
  }
792
798
 
@@ -32,6 +32,17 @@ function getFunctionsBaseUrl(): string {
32
32
  return process.env.BACKEND_FUNCTIONS_BASE_URL || process.env.FUNCTIONS_BASE_URL || 'http://localhost:7071';
33
33
  }
34
34
 
35
+ function isLocalFunctionsUrl(url: string): boolean {
36
+ return url.startsWith('http://localhost:') || url.startsWith('http://127.0.0.1:');
37
+ }
38
+
39
+ function getFunctionsKeyHeaders(functionsBaseUrl: string): Record<string, string> {
40
+ const key = process.env.BACKEND_FUNCTIONS_KEY;
41
+ if (key) return { 'x-functions-key': key };
42
+ if (isLocalFunctionsUrl(functionsBaseUrl)) return {};
43
+ throw new Error('BACKEND_FUNCTIONS_KEY is required for non-local Functions calls');
44
+ }
45
+
35
46
  interface CallFunctionConfig<TInput = any, TOutput = any> {
36
47
  /** HTTP メソッド */
37
48
  method: 'GET' | 'POST' | 'PUT' | 'DELETE';
@@ -70,11 +81,12 @@ export async function callFunction<TInput = any, TOutput = any>(
70
81
  // Azure Functions を呼び出し
71
82
  const functionsBaseUrl = getFunctionsBaseUrl();
72
83
  const url = functionsBaseUrl + path;
84
+ const keyHeaders = getFunctionsKeyHeaders(functionsBaseUrl);
73
85
  console.log(\`[BFF] \${method} \${url}\`);
74
86
 
75
87
  const response = await fetch(url, {
76
88
  method,
77
- headers: { 'Content-Type': 'application/json' },
89
+ headers: { 'Content-Type': 'application/json', ...keyHeaders },
78
90
  body: validatedBody !== undefined ? JSON.stringify(validatedBody) : undefined,
79
91
  });
80
92
 
@@ -10,6 +10,9 @@ import { ModelAuthPolicy } from "../../types";
10
10
  export interface UIAuthOptions {
11
11
  /** モデル固有のロールポリシー */
12
12
  authPolicy: ModelAuthPolicy;
13
+ writeRoles?: string[];
14
+ authenticatedFetchImport?: string;
15
+ authContextImport?: string;
13
16
  }
14
17
 
15
18
  /**
@@ -21,7 +24,8 @@ export function generateListPage(model: ModelInfo, sharedPackageName: string, au
21
24
  const modelKebab = toKebabCase(modelName);
22
25
 
23
26
  const hasAuth = !!authOptions;
24
- const writeRoles = authOptions?.authPolicy?.write || authOptions?.authPolicy?.roles;
27
+ const authContextImport = authOptions?.authContextImport ?? '@/lib/auth/auth-context';
28
+ const writeRoles = authOptions?.writeRoles ?? (Array.isArray(authOptions?.authPolicy?.write) ? authOptions.authPolicy.write : authOptions?.authPolicy?.roles);
25
29
  const hasWriteRoles = hasAuth && writeRoles && writeRoles.length > 0;
26
30
 
27
31
  // フィールドから表示するカラムを抽出(id以外の最初の3つ)
@@ -71,7 +75,7 @@ import { useEffect, useState } from 'react';
71
75
  import Link from 'next/link';
72
76
  import { z } from 'zod/v4';
73
77
  ${schemaImportLine}
74
- ${hasAuth ? `import { useAuth } from '@/lib/auth/auth-context';` : ''}
78
+ ${hasAuth ? `import { useAuth } from '${authContextImport}';` : ''}
75
79
 
76
80
  type ${modelName} = z.infer<typeof ${localSchemaName}>;
77
81
 
@@ -266,7 +270,8 @@ export function generateDetailPage(model: ModelInfo, sharedPackageName: string,
266
270
  const modelKebab = toKebabCase(modelName);
267
271
 
268
272
  const hasAuth = !!authOptions;
269
- const writeRoles = authOptions?.authPolicy?.write || authOptions?.authPolicy?.roles;
273
+ const authContextImport = authOptions?.authContextImport ?? '@/lib/auth/auth-context';
274
+ const writeRoles = authOptions?.writeRoles ?? (Array.isArray(authOptions?.authPolicy?.write) ? authOptions.authPolicy.write : authOptions?.authPolicy?.roles);
270
275
  const hasWriteRoles = hasAuth && writeRoles && writeRoles.length > 0;
271
276
 
272
277
  // 外部キーフィールドを検出
@@ -311,7 +316,7 @@ import { useParams, useRouter } from 'next/navigation';
311
316
  import Link from 'next/link';
312
317
  import { z } from 'zod/v4';
313
318
  ${schemaImportLine}
314
- ${hasAuth ? `import { useAuth } from '@/lib/auth/auth-context';` : ''}
319
+ ${hasAuth ? `import { useAuth } from '${authContextImport}';` : ''}
315
320
 
316
321
  type ${modelName} = z.infer<typeof ${localSchemaName}>;
317
322
 
@@ -910,7 +915,8 @@ export function generateNewPage(model: ModelInfo, authOptions?: UIAuthOptions):
910
915
  const modelKebab = toKebabCase(modelName);
911
916
 
912
917
  const hasAuth = !!authOptions;
913
- const writeRoles = authOptions?.authPolicy?.write || authOptions?.authPolicy?.roles;
918
+ const authContextImport = authOptions?.authContextImport ?? '@/lib/auth/auth-context';
919
+ const writeRoles = authOptions?.writeRoles ?? (Array.isArray(authOptions?.authPolicy?.write) ? authOptions.authPolicy.write : authOptions?.authPolicy?.roles);
914
920
  const hasWriteRoles = hasAuth && writeRoles && writeRoles.length > 0;
915
921
 
916
922
  if (!hasAuth) {
@@ -936,7 +942,7 @@ export default function New${modelName}Page() {
936
942
  import { useEffect } from 'react';
937
943
  import { useRouter } from 'next/navigation';
938
944
  import ${modelName}Form from '../_components/${modelName}Form';
939
- import { useAuth } from '@/lib/auth/auth-context';
945
+ import { useAuth } from '${authContextImport}';
940
946
 
941
947
  export default function New${modelName}Page() {
942
948
  const router = useRouter();
@@ -979,7 +985,8 @@ export function generateEditPage(model: ModelInfo, sharedPackageName: string, au
979
985
  const modelKebab = toKebabCase(modelName);
980
986
 
981
987
  const hasAuth = !!authOptions;
982
- const writeRoles = authOptions?.authPolicy?.write || authOptions?.authPolicy?.roles;
988
+ const authContextImport = authOptions?.authContextImport ?? '@/lib/auth/auth-context';
989
+ const writeRoles = authOptions?.writeRoles ?? (Array.isArray(authOptions?.authPolicy?.write) ? authOptions.authPolicy.write : authOptions?.authPolicy?.roles);
983
990
  const hasWriteRoles = hasAuth && writeRoles && writeRoles.length > 0;
984
991
 
985
992
  const schemaName = model.schemaName;
@@ -997,7 +1004,7 @@ import { useParams, useRouter } from 'next/navigation';
997
1004
  import ${modelName}Form from '../../_components/${modelName}Form';
998
1005
  import { z } from 'zod/v4';
999
1006
  ${schemaImportLine}
1000
- ${hasAuth ? `import { useAuth } from '@/lib/auth/auth-context';` : ''}
1007
+ ${hasAuth ? `import { useAuth } from '${authContextImport}';` : ''}
1001
1008
 
1002
1009
  type ${modelName} = z.infer<typeof ${localSchemaName}>;
1003
1010
 
@@ -73,7 +73,7 @@ export interface ApiModelConnectorConfig {
73
73
  export type ModelConnectorConfig = RdbModelConnectorConfig | ApiModelConnectorConfig;
74
74
 
75
75
  // 認証プロバイダー種別
76
- export type AuthProvider = "custom-jwt" | "swa" | "swa-custom" | "none";
76
+ export type AuthProvider = "custom-jwt" | "swa" | "external-token" | "swa-custom" | "none";
77
77
 
78
78
  // custom-jwt プロバイダー設定
79
79
  export interface CustomJwtConfig {
@@ -95,12 +95,25 @@ export interface SwaAuthConfig {
95
95
 
96
96
  // 認可設定
97
97
  export interface AuthorizationConfig {
98
- defaultPolicy?: "authenticated" | "anonymous";
98
+ defaultPolicy?: "authenticated" | "anonymous" | "public" | string;
99
+ policies?: Record<string, AuthorizationPolicy>;
100
+ }
101
+
102
+ export interface AuthorizationPolicy {
103
+ schemes: string[];
104
+ roles?: string[];
105
+ }
106
+
107
+ export interface AuthSchemeConfig {
108
+ provider: AuthProvider;
109
+ customJwt?: CustomJwtConfig;
110
+ swa?: SwaAuthConfig;
99
111
  }
100
112
 
101
113
  // 認証設定
102
114
  export interface AuthConfig {
103
- provider: AuthProvider;
115
+ provider?: AuthProvider;
116
+ schemes?: Record<string, AuthSchemeConfig>;
104
117
  customJwt?: CustomJwtConfig;
105
118
  swa?: SwaAuthConfig;
106
119
  authorization?: AuthorizationConfig;
@@ -109,8 +122,26 @@ export interface AuthConfig {
109
122
  // モデルに付与する認可ポリシー
110
123
  export interface ModelAuthPolicy {
111
124
  roles?: string[];
112
- read?: string[];
113
- write?: string[];
125
+ read?: string[] | string;
126
+ write?: string[] | string;
127
+ policy?: string;
128
+ }
129
+
130
+ export interface NormalizedAuthConfig extends Omit<AuthConfig, "schemes" | "authorization"> {
131
+ schemes: Record<string, AuthSchemeConfig>;
132
+ authorization: AuthorizationConfig & { defaultPolicy: string; policies: Record<string, AuthorizationPolicy> };
133
+ }
134
+
135
+ export interface AuthPrincipal {
136
+ subject: string;
137
+ scheme: string;
138
+ issuer: string;
139
+ roles: string[];
140
+ claims: Record<string, unknown>;
141
+ /** @deprecated Use subject. */
142
+ userId?: string;
143
+ /** @deprecated Read provider data from claims. */
144
+ userDetails?: string;
114
145
  }
115
146
 
116
147
  // CLI設定の型
@@ -178,6 +178,7 @@ export function getCiSetupStep(pm: PackageManager): string {
178
178
  }
179
179
 
180
180
  export function getCiInstallCommand(ciPm: PackageManager, projectPm: PackageManager = ciPm): string {
181
+ void projectPm; // Reserved for package-manager-specific CI bridging.
181
182
  if (ciPm === "pnpm") {
182
183
  return getCommands(ciPm).ci;
183
184
  }