swallowkit 1.0.0-beta.57 → 1.0.0-beta.59
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.ja.md +1 -0
- package/README.md +1 -0
- package/dist/cli/commands/add-auth.d.ts +3 -1
- package/dist/cli/commands/add-auth.d.ts.map +1 -1
- package/dist/cli/commands/add-auth.js +55 -21
- package/dist/cli/commands/add-auth.js.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +12 -498
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/core/operations/auth-operations.d.ts +4 -0
- package/dist/core/operations/auth-operations.d.ts.map +1 -1
- package/dist/core/operations/auth-operations.js +5 -2
- package/dist/core/operations/auth-operations.js.map +1 -1
- package/dist/core/operations/create-model.d.ts.map +1 -1
- package/dist/core/operations/create-model.js +6 -2
- package/dist/core/operations/create-model.js.map +1 -1
- package/dist/core/project/agent-instructions.d.ts +29 -0
- package/dist/core/project/agent-instructions.d.ts.map +1 -0
- package/dist/core/project/agent-instructions.js +159 -0
- package/dist/core/project/agent-instructions.js.map +1 -0
- package/dist/core/project/capabilities.d.ts +58 -0
- package/dist/core/project/capabilities.d.ts.map +1 -0
- package/dist/core/project/capabilities.js +135 -0
- package/dist/core/project/capabilities.js.map +1 -0
- package/dist/core/project/validation.d.ts.map +1 -1
- package/dist/core/project/validation.js +48 -0
- package/dist/core/project/validation.js.map +1 -1
- package/dist/core/project/workflows.d.ts.map +1 -1
- package/dist/core/project/workflows.js +46 -2
- package/dist/core/project/workflows.js.map +1 -1
- package/dist/core/scaffold/auth-generator.d.ts.map +1 -1
- package/dist/core/scaffold/auth-generator.js +6 -2
- package/dist/core/scaffold/auth-generator.js.map +1 -1
- package/dist/core/verify/index.d.ts +7 -0
- package/dist/core/verify/index.d.ts.map +1 -1
- package/dist/core/verify/index.js +69 -0
- package/dist/core/verify/index.js.map +1 -1
- package/dist/machine/index.d.ts.map +1 -1
- package/dist/machine/index.js +117 -41
- package/dist/machine/index.js.map +1 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +32 -10
- package/dist/mcp/index.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/agent-instructions.test.ts +237 -0
- package/src/__tests__/agent-loop-phase34.test.ts +66 -2
- package/src/__tests__/auth.test.ts +9 -0
- package/src/__tests__/init.test.ts +2 -0
- package/src/__tests__/machine.test.ts +15 -0
- package/src/__tests__/multi-auth.test.ts +12 -0
- package/src/__tests__/plan-apply.test.ts +38 -0
- package/src/cli/commands/add-auth.ts +59 -21
- package/src/cli/commands/init.ts +19 -525
- package/src/cli/index.ts +2 -0
- package/src/core/operations/auth-operations.ts +9 -2
- package/src/core/operations/create-model.ts +7 -2
- package/src/core/project/agent-instructions.ts +167 -0
- package/src/core/project/agent-templates/AGENTS.md +502 -0
- package/src/core/project/agent-templates/CLAUDE.md +31 -0
- package/src/core/project/agent-templates/azure-functions.instructions.md +22 -0
- package/src/core/project/agent-templates/bff-routes.instructions.md +18 -0
- package/src/core/project/agent-templates/copilot-instructions.md +27 -0
- package/src/core/project/agent-templates/shared-models.instructions.md +72 -0
- package/src/core/project/capabilities.ts +193 -0
- package/src/core/project/validation.ts +50 -0
- package/src/core/project/workflows.ts +48 -2
- package/src/core/scaffold/auth-generator.ts +7 -2
- package/src/core/verify/index.ts +76 -0
- package/src/machine/index.ts +133 -45
- package/src/mcp/index.ts +28 -10
|
@@ -51,6 +51,29 @@ import { buildSharedTsConfig } from "./init";
|
|
|
51
51
|
interface AddAuthOptions {
|
|
52
52
|
provider?: string;
|
|
53
53
|
scheme?: string;
|
|
54
|
+
/** SWA の許可する identity provider (例: ['github'])。未指定時は ['aad']。 */
|
|
55
|
+
allowedProviders?: string[];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const SWA_PROVIDER_LABELS: Record<string, string> = {
|
|
59
|
+
aad: "Microsoft",
|
|
60
|
+
github: "GitHub",
|
|
61
|
+
google: "Google",
|
|
62
|
+
twitter: "X",
|
|
63
|
+
apple: "Apple",
|
|
64
|
+
facebook: "Facebook",
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
function swaProviderLabel(provider: string): string {
|
|
68
|
+
return SWA_PROVIDER_LABELS[provider] ?? provider.charAt(0).toUpperCase() + provider.slice(1);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function normalizeSwaProviders(allowedProviders?: string[]): string[] {
|
|
72
|
+
const providers = (allowedProviders ?? []).map((p) => p.trim().toLowerCase()).filter(Boolean);
|
|
73
|
+
for (const provider of providers) {
|
|
74
|
+
if (!/^[a-z0-9-]+$/.test(provider)) throw new Error(`Invalid SWA identity provider name: ${provider}`);
|
|
75
|
+
}
|
|
76
|
+
return providers.length > 0 ? providers : ["aad"];
|
|
54
77
|
}
|
|
55
78
|
|
|
56
79
|
const AUTH_GENERATOR = "add-auth";
|
|
@@ -75,7 +98,8 @@ function defaultCustomJwtConfig(): CustomJwtConfig {
|
|
|
75
98
|
};
|
|
76
99
|
}
|
|
77
100
|
|
|
78
|
-
function setupSwaAuth(cwd: string, backendLanguage: BackendLanguage, session: FileOperationSession): void {
|
|
101
|
+
function setupSwaAuth(cwd: string, backendLanguage: BackendLanguage, session: FileOperationSession, allowedProviders?: string[]): void {
|
|
102
|
+
const providers = normalizeSwaProviders(allowedProviders);
|
|
79
103
|
const helperPath = backendLanguage === "typescript"
|
|
80
104
|
? path.join(cwd, "functions", "src", "auth", "jwt-helper.ts")
|
|
81
105
|
: backendLanguage === "csharp"
|
|
@@ -92,11 +116,11 @@ function setupSwaAuth(cwd: string, backendLanguage: BackendLanguage, session: Fi
|
|
|
92
116
|
session.writeFile(callFnPath, generateBFFCallFunctionWithSwaAuth(), managedMeta);
|
|
93
117
|
|
|
94
118
|
const authDir = path.join(cwd, "lib", "auth");
|
|
95
|
-
session.writeFile(path.join(authDir, "auth-context.tsx"), generateSwaAuthContext(), managedMeta);
|
|
119
|
+
session.writeFile(path.join(authDir, "auth-context.tsx"), generateSwaAuthContext(providers), managedMeta);
|
|
96
120
|
updateRootLayoutWithAuthProvider(cwd, session);
|
|
97
121
|
const loginDir = path.join(cwd, "app", "login");
|
|
98
|
-
session.writeFile(path.join(loginDir, "page.tsx"), generateSwaLoginPage(), managedMeta);
|
|
99
|
-
updateSwaRouteConfig(cwd, session);
|
|
122
|
+
session.writeFile(path.join(loginDir, "page.tsx"), generateSwaLoginPage(providers), managedMeta);
|
|
123
|
+
updateSwaRouteConfig(cwd, session, providers);
|
|
100
124
|
}
|
|
101
125
|
|
|
102
126
|
function setupExternalTokenAuth(cwd: string, backendLanguage: BackendLanguage, session: FileOperationSession): void {
|
|
@@ -143,15 +167,21 @@ function updatePythonAuthMeRegistration(cwd: string, session: FileOperationSessi
|
|
|
143
167
|
session.writeFile(appPath, content, extensionMeta);
|
|
144
168
|
}
|
|
145
169
|
|
|
146
|
-
function generateSwaLoginPage(): string {
|
|
170
|
+
function generateSwaLoginPage(allowedProviders?: string[]): string {
|
|
171
|
+
const providers = normalizeSwaProviders(allowedProviders);
|
|
172
|
+
const links = providers
|
|
173
|
+
.map((provider) => `<a href="/.auth/login/${provider}?post_login_redirect_uri=/">Sign in with ${swaProviderLabel(provider)}</a>`)
|
|
174
|
+
.join("");
|
|
147
175
|
return `'use client';
|
|
148
176
|
export default function LoginPage() {
|
|
149
|
-
return <main><h1>Sign in</h1
|
|
177
|
+
return <main><h1>Sign in</h1>${links}</main>;
|
|
150
178
|
}
|
|
151
179
|
`;
|
|
152
180
|
}
|
|
153
181
|
|
|
154
|
-
export function generateSwaAuthContext(): string {
|
|
182
|
+
export function generateSwaAuthContext(allowedProviders?: string[]): string {
|
|
183
|
+
const providers = normalizeSwaProviders(allowedProviders);
|
|
184
|
+
const primaryProvider = providers[0];
|
|
155
185
|
return `'use client';
|
|
156
186
|
import { createContext, useContext, useEffect, useState, type ReactNode } from 'react';
|
|
157
187
|
|
|
@@ -184,7 +214,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
|
|
184
214
|
.catch(() => setUser(null))
|
|
185
215
|
.finally(() => setLoading(false));
|
|
186
216
|
}, []);
|
|
187
|
-
const login = () => { window.location.href = '/.auth/login
|
|
217
|
+
const login = () => { window.location.href = '/.auth/login/${primaryProvider}?post_login_redirect_uri=/'; };
|
|
188
218
|
const logout = () => { window.location.href = '/.auth/logout?post_logout_redirect_uri=/'; };
|
|
189
219
|
const hasRole = (role: string) => user?.roles.includes(role) ?? false;
|
|
190
220
|
return <AuthContext.Provider value={{ user, loading, login, logout, hasRole, hasAnyRole: roles => roles.some(hasRole) }}>{children}</AuthContext.Provider>;
|
|
@@ -219,14 +249,15 @@ function updateRootLayoutWithAuthProvider(cwd: string, session: FileOperationSes
|
|
|
219
249
|
if (updated !== layout) session.writeFile(layoutPath, updated, extensionMeta);
|
|
220
250
|
}
|
|
221
251
|
|
|
222
|
-
function updateSwaRouteConfig(cwd: string, session: FileOperationSession): void {
|
|
252
|
+
function updateSwaRouteConfig(cwd: string, session: FileOperationSession, allowedProviders?: string[]): void {
|
|
253
|
+
const primaryProvider = normalizeSwaProviders(allowedProviders)[0];
|
|
223
254
|
const configPath = path.join(cwd, "staticwebapp.config.json");
|
|
224
255
|
const config = session.fileExists(configPath) ? JSON.parse(session.readFile(configPath)) : {};
|
|
225
256
|
config.routes = Array.isArray(config.routes)
|
|
226
257
|
? config.routes.filter((route: { route?: string }) => route.route !== "/api/*")
|
|
227
258
|
: [];
|
|
228
259
|
config.routes.unshift({ route: "/api/*", allowedRoles: ["authenticated"] });
|
|
229
|
-
config.responseOverrides = { ...(config.responseOverrides || {}), "401": { statusCode: 302, redirect:
|
|
260
|
+
config.responseOverrides = { ...(config.responseOverrides || {}), "401": { statusCode: 302, redirect: `/.auth/login/${primaryProvider}?post_login_redirect_uri=.referrer` } };
|
|
230
261
|
session.writeFile(configPath, JSON.stringify(config, null, 2), extensionMeta);
|
|
231
262
|
}
|
|
232
263
|
|
|
@@ -257,7 +288,7 @@ export async function addAuthCommand(options: AddAuthOptions) {
|
|
|
257
288
|
if (options.scheme) {
|
|
258
289
|
if (!/^[A-Za-z][A-Za-z0-9_-]*$/.test(options.scheme)) throw new Error("--scheme must start with a letter and contain only letters, digits, '_' or '-'");
|
|
259
290
|
if (provider === "none" || provider === "swa-custom") throw new Error(`Provider '${provider}' cannot be added as a named scheme`);
|
|
260
|
-
addNamedScheme(cwd, options.scheme, provider, backendLanguage, session);
|
|
291
|
+
addNamedScheme(cwd, options.scheme, provider, backendLanguage, session, options.allowedProviders);
|
|
261
292
|
await finalize();
|
|
262
293
|
console.log(`\n Named authentication scheme '${options.scheme}' added.`);
|
|
263
294
|
return;
|
|
@@ -273,8 +304,8 @@ export async function addAuthCommand(options: AddAuthOptions) {
|
|
|
273
304
|
return;
|
|
274
305
|
}
|
|
275
306
|
if (provider === "swa") {
|
|
276
|
-
setupSwaAuth(cwd, backendLanguage, session);
|
|
277
|
-
updateConfigWithAuth(cwd, provider, config.auth?.customJwt || defaultCustomJwtConfig(), session);
|
|
307
|
+
setupSwaAuth(cwd, backendLanguage, session, options.allowedProviders);
|
|
308
|
+
updateConfigWithAuth(cwd, provider, config.auth?.customJwt || defaultCustomJwtConfig(), session, options.allowedProviders);
|
|
278
309
|
await finalize();
|
|
279
310
|
console.log("\n SWA built-in authentication setup complete!");
|
|
280
311
|
return;
|
|
@@ -392,17 +423,22 @@ function findObjectEnd(content: string, open: number): number {
|
|
|
392
423
|
return -1;
|
|
393
424
|
}
|
|
394
425
|
|
|
395
|
-
function updateConfigWithNamedScheme(cwd: string, scheme: string, provider: AuthProvider, session: FileOperationSession): void {
|
|
426
|
+
function updateConfigWithNamedScheme(cwd: string, scheme: string, provider: AuthProvider, session: FileOperationSession, allowedProviders?: string[]): void {
|
|
396
427
|
const jsPath = path.join(cwd, "swallowkit.config.js");
|
|
397
428
|
const jsonPath = path.join(cwd, "swallowkit.config.json");
|
|
398
429
|
const configPath = session.fileExists(jsPath) ? jsPath : jsonPath;
|
|
399
430
|
if (!session.fileExists(configPath)) throw new Error("swallowkit.config.js or swallowkit.config.json is required");
|
|
431
|
+
const swaProviders = provider === "swa" ? normalizeSwaProviders(allowedProviders) : undefined;
|
|
400
432
|
if (configPath.endsWith(".json")) {
|
|
401
433
|
const value = JSON.parse(session.readFile(configPath));
|
|
402
434
|
value.auth ??= {};
|
|
403
435
|
value.auth.schemes ??= {};
|
|
404
436
|
if (value.auth.schemes[scheme]) throw new Error(`auth.schemes.${scheme} already exists; no files were changed`);
|
|
405
|
-
value.auth.schemes[scheme] = provider === "custom-jwt"
|
|
437
|
+
value.auth.schemes[scheme] = provider === "custom-jwt"
|
|
438
|
+
? { provider, customJwt: defaultCustomJwtConfig() }
|
|
439
|
+
: swaProviders
|
|
440
|
+
? { provider, swa: { allowedProviders: swaProviders } }
|
|
441
|
+
: { provider };
|
|
406
442
|
value.auth.authorization ??= { defaultPolicy: "anonymous", policies: {} };
|
|
407
443
|
session.writeFile(configPath, JSON.stringify(value, null, 2) + "\n", extensionMeta);
|
|
408
444
|
return;
|
|
@@ -412,7 +448,9 @@ function updateConfigWithNamedScheme(cwd: string, scheme: string, provider: Auth
|
|
|
412
448
|
const customJwt = defaultCustomJwtConfig();
|
|
413
449
|
const entry = provider === "custom-jwt"
|
|
414
450
|
? `\n ${scheme}: { provider: 'custom-jwt', customJwt: ${JSON.stringify(customJwt)} },`
|
|
415
|
-
:
|
|
451
|
+
: swaProviders
|
|
452
|
+
? `\n ${scheme}: { provider: '${provider}', swa: { allowedProviders: [${swaProviders.map((p) => `'${p}'`).join(", ")}] } },`
|
|
453
|
+
: `\n ${scheme}: { provider: '${provider}' },`;
|
|
416
454
|
if (!authMatch) {
|
|
417
455
|
const rootEnd = content.lastIndexOf("}");
|
|
418
456
|
const beforeRootEnd = content.slice(0, rootEnd).trimEnd();
|
|
@@ -436,9 +474,9 @@ function updateConfigWithNamedScheme(cwd: string, scheme: string, provider: Auth
|
|
|
436
474
|
session.writeFile(configPath, content, extensionMeta);
|
|
437
475
|
}
|
|
438
476
|
|
|
439
|
-
function addNamedScheme(cwd: string, scheme: string, provider: AuthProvider, language: BackendLanguage, session: FileOperationSession): void {
|
|
477
|
+
function addNamedScheme(cwd: string, scheme: string, provider: AuthProvider, language: BackendLanguage, session: FileOperationSession, allowedProviders?: string[]): void {
|
|
440
478
|
// Validate/update config before creating files, so duplicate names stop safely.
|
|
441
|
-
updateConfigWithNamedScheme(cwd, scheme, provider, session);
|
|
479
|
+
updateConfigWithNamedScheme(cwd, scheme, provider, session, allowedProviders);
|
|
442
480
|
const slug = scheme.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/_/g, "-").toLowerCase();
|
|
443
481
|
if (language === "typescript") {
|
|
444
482
|
const dir = path.join(cwd, "functions", "src", "auth", "schemes", slug);
|
|
@@ -462,7 +500,7 @@ function addNamedScheme(cwd: string, scheme: string, provider: AuthProvider, lan
|
|
|
462
500
|
writeIfMissing(session, path.join(clientDir, "authenticated-fetch.ts"), generateAuthenticatedFetch().replace("'./external-token-adapter'", "'./token-adapter'"));
|
|
463
501
|
writeIfMissing(session, path.join(clientDir, "auth-context.tsx"), generateExternalTokenAuthContext().replace(/external-token-adapter/g, "token-adapter"));
|
|
464
502
|
} else if (provider === "swa") {
|
|
465
|
-
writeIfMissing(session, path.join(clientDir, "auth-context.tsx"), generateSwaAuthContext());
|
|
503
|
+
writeIfMissing(session, path.join(clientDir, "auth-context.tsx"), generateSwaAuthContext(allowedProviders));
|
|
466
504
|
} else if (provider === "custom-jwt") {
|
|
467
505
|
writeIfMissing(session, path.join(clientDir, "auth-context.tsx"), generateAuthContext());
|
|
468
506
|
}
|
|
@@ -659,7 +697,7 @@ function generateBFFAuth(cwd: string, projectName: string, sharedPackageName: st
|
|
|
659
697
|
console.log(` Created: app/api/auth/me/route.ts`);
|
|
660
698
|
}
|
|
661
699
|
|
|
662
|
-
function updateConfigWithAuth(cwd: string, provider: AuthProvider, config: CustomJwtConfig, session: FileOperationSession): void {
|
|
700
|
+
function updateConfigWithAuth(cwd: string, provider: AuthProvider, config: CustomJwtConfig, session: FileOperationSession, allowedProviders?: string[]): void {
|
|
663
701
|
const configPath = path.join(cwd, "swallowkit.config.js");
|
|
664
702
|
if (!session.fileExists(configPath)) {
|
|
665
703
|
console.warn(" swallowkit.config.js not found. Please add auth config manually.");
|
|
@@ -694,7 +732,7 @@ function updateConfigWithAuth(cwd: string, provider: AuthProvider, config: Custo
|
|
|
694
732
|
tokenExpiry: '${config.tokenExpiry || "24h"}',
|
|
695
733
|
},` : provider === "swa" ? `
|
|
696
734
|
swa: {
|
|
697
|
-
allowedProviders: ['
|
|
735
|
+
allowedProviders: [${normalizeSwaProviders(allowedProviders).map((p) => `'${p}'`).join(", ")}],
|
|
698
736
|
roleSource: 'swa-roles',
|
|
699
737
|
},` : "";
|
|
700
738
|
|