swallowkit 1.0.0-beta.55 → 1.0.0-beta.56

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 (135) hide show
  1. package/dist/cli/commands/add-auth.d.ts.map +1 -1
  2. package/dist/cli/commands/add-auth.js +154 -157
  3. package/dist/cli/commands/add-auth.js.map +1 -1
  4. package/dist/cli/commands/init.d.ts +5 -0
  5. package/dist/cli/commands/init.d.ts.map +1 -1
  6. package/dist/cli/commands/init.js +178 -25
  7. package/dist/cli/commands/init.js.map +1 -1
  8. package/dist/cli/commands/scaffold.d.ts +7 -2
  9. package/dist/cli/commands/scaffold.d.ts.map +1 -1
  10. package/dist/cli/commands/scaffold.js +111 -123
  11. package/dist/cli/commands/scaffold.js.map +1 -1
  12. package/dist/cli/index.d.ts.map +1 -1
  13. package/dist/cli/index.js +127 -2
  14. package/dist/cli/index.js.map +1 -1
  15. package/dist/core/config.d.ts +1 -1
  16. package/dist/core/config.d.ts.map +1 -1
  17. package/dist/core/config.js +8 -4
  18. package/dist/core/config.js.map +1 -1
  19. package/dist/core/operations/auth-operations.d.ts +57 -0
  20. package/dist/core/operations/auth-operations.d.ts.map +1 -0
  21. package/dist/core/operations/auth-operations.js +269 -0
  22. package/dist/core/operations/auth-operations.js.map +1 -0
  23. package/dist/core/operations/create-model.d.ts.map +1 -1
  24. package/dist/core/operations/create-model.js +23 -13
  25. package/dist/core/operations/create-model.js.map +1 -1
  26. package/dist/core/operations/file-session.d.ts +53 -0
  27. package/dist/core/operations/file-session.d.ts.map +1 -0
  28. package/dist/core/operations/file-session.js +195 -0
  29. package/dist/core/operations/file-session.js.map +1 -0
  30. package/dist/core/operations/provision-operations.d.ts +52 -0
  31. package/dist/core/operations/provision-operations.d.ts.map +1 -0
  32. package/dist/core/operations/provision-operations.js +211 -0
  33. package/dist/core/operations/provision-operations.js.map +1 -0
  34. package/dist/core/operations/runtime.d.ts +5 -0
  35. package/dist/core/operations/runtime.d.ts.map +1 -1
  36. package/dist/core/operations/runtime.js +23 -0
  37. package/dist/core/operations/runtime.js.map +1 -1
  38. package/dist/core/operations/scaffold-apply.d.ts +21 -0
  39. package/dist/core/operations/scaffold-apply.d.ts.map +1 -0
  40. package/dist/core/operations/scaffold-apply.js +57 -0
  41. package/dist/core/operations/scaffold-apply.js.map +1 -0
  42. package/dist/core/operations/scaffold-machine.d.ts.map +1 -1
  43. package/dist/core/operations/scaffold-machine.js +1 -13
  44. package/dist/core/operations/scaffold-machine.js.map +1 -1
  45. package/dist/core/operations/scaffold-plan.d.ts +43 -0
  46. package/dist/core/operations/scaffold-plan.d.ts.map +1 -0
  47. package/dist/core/operations/scaffold-plan.js +195 -0
  48. package/dist/core/operations/scaffold-plan.js.map +1 -0
  49. package/dist/core/project/artifacts.d.ts +58 -0
  50. package/dist/core/project/artifacts.d.ts.map +1 -0
  51. package/dist/core/project/artifacts.js +179 -0
  52. package/dist/core/project/artifacts.js.map +1 -0
  53. package/dist/core/project/boundaries.d.ts +50 -0
  54. package/dist/core/project/boundaries.d.ts.map +1 -0
  55. package/dist/core/project/boundaries.js +117 -0
  56. package/dist/core/project/boundaries.js.map +1 -0
  57. package/dist/core/project/drift.d.ts +29 -0
  58. package/dist/core/project/drift.d.ts.map +1 -0
  59. package/dist/core/project/drift.js +188 -0
  60. package/dist/core/project/drift.js.map +1 -0
  61. package/dist/core/project/infra.d.ts +49 -0
  62. package/dist/core/project/infra.d.ts.map +1 -0
  63. package/dist/core/project/infra.js +167 -0
  64. package/dist/core/project/infra.js.map +1 -0
  65. package/dist/core/project/state.d.ts +15 -0
  66. package/dist/core/project/state.d.ts.map +1 -0
  67. package/dist/core/project/state.js +106 -0
  68. package/dist/core/project/state.js.map +1 -0
  69. package/dist/core/project/workflows.d.ts +43 -0
  70. package/dist/core/project/workflows.d.ts.map +1 -0
  71. package/dist/core/project/workflows.js +255 -0
  72. package/dist/core/project/workflows.js.map +1 -0
  73. package/dist/core/scaffold/auth-generator.d.ts.map +1 -1
  74. package/dist/core/scaffold/auth-generator.js +24 -27
  75. package/dist/core/scaffold/auth-generator.js.map +1 -1
  76. package/dist/core/scaffold/functions-generator.js +9 -9
  77. package/dist/core/scaffold/nextjs-generator.js +6 -6
  78. package/dist/core/scaffold/ui-generator.js +19 -19
  79. package/dist/core/scaffold/ui-generator.js.map +1 -1
  80. package/dist/core/verify/index.d.ts +62 -0
  81. package/dist/core/verify/index.d.ts.map +1 -0
  82. package/dist/core/verify/index.js +343 -0
  83. package/dist/core/verify/index.js.map +1 -0
  84. package/dist/machine/contracts.d.ts +8 -0
  85. package/dist/machine/contracts.d.ts.map +1 -1
  86. package/dist/machine/errors.d.ts +4 -1
  87. package/dist/machine/errors.d.ts.map +1 -1
  88. package/dist/machine/errors.js +9 -1
  89. package/dist/machine/errors.js.map +1 -1
  90. package/dist/machine/index.d.ts.map +1 -1
  91. package/dist/machine/index.js +350 -16
  92. package/dist/machine/index.js.map +1 -1
  93. package/dist/mcp/index.d.ts.map +1 -1
  94. package/dist/mcp/index.js +202 -1
  95. package/dist/mcp/index.js.map +1 -1
  96. package/dist/types/index.d.ts +14 -0
  97. package/dist/types/index.d.ts.map +1 -1
  98. package/package.json +1 -1
  99. package/src/__tests__/__snapshots__/functions-generator.test.ts.snap +17 -17
  100. package/src/__tests__/__snapshots__/nextjs-generator.test.ts.snap +6 -6
  101. package/src/__tests__/__snapshots__/ui-generator.test.ts.snap +11 -11
  102. package/src/__tests__/agent-loop-phase34.test.ts +449 -0
  103. package/src/__tests__/file-session.test.ts +98 -0
  104. package/src/__tests__/init.test.ts +50 -0
  105. package/src/__tests__/mcp.test.ts +113 -5
  106. package/src/__tests__/plan-apply.test.ts +294 -0
  107. package/src/cli/commands/add-auth.ts +164 -164
  108. package/src/cli/commands/init.ts +186 -24
  109. package/src/cli/commands/scaffold.ts +128 -129
  110. package/src/cli/index.ts +148 -2
  111. package/src/core/config.ts +5 -4
  112. package/src/core/operations/auth-operations.ts +353 -0
  113. package/src/core/operations/create-model.ts +25 -13
  114. package/src/core/operations/file-session.ts +210 -0
  115. package/src/core/operations/provision-operations.ts +297 -0
  116. package/src/core/operations/runtime.ts +23 -0
  117. package/src/core/operations/scaffold-apply.ts +106 -0
  118. package/src/core/operations/scaffold-machine.ts +2 -16
  119. package/src/core/operations/scaffold-plan.ts +224 -0
  120. package/src/core/project/artifacts.ts +205 -0
  121. package/src/core/project/boundaries.ts +170 -0
  122. package/src/core/project/drift.ts +202 -0
  123. package/src/core/project/infra.ts +196 -0
  124. package/src/core/project/state.ts +77 -0
  125. package/src/core/project/workflows.ts +249 -0
  126. package/src/core/scaffold/auth-generator.ts +24 -27
  127. package/src/core/scaffold/functions-generator.ts +9 -9
  128. package/src/core/scaffold/nextjs-generator.ts +6 -6
  129. package/src/core/scaffold/ui-generator.ts +19 -19
  130. package/src/core/verify/index.ts +392 -0
  131. package/src/machine/contracts.ts +16 -0
  132. package/src/machine/errors.ts +12 -1
  133. package/src/machine/index.ts +444 -15
  134. package/src/mcp/index.ts +198 -2
  135. package/src/types/index.ts +17 -0
@@ -44,6 +44,8 @@ import {
44
44
  generateNamedExternalTokenVerifierPython,
45
45
  } from "../../core/scaffold/auth-generator";
46
46
  import { syncProjectManifest } from "../../core/project/manifest";
47
+ import { FileOperationSession, getActiveFileSession } from "../../core/operations/file-session";
48
+ import { recordSessionOperations } from "../../core/project/artifacts";
47
49
  import { buildSharedTsConfig } from "./init";
48
50
 
49
51
  interface AddAuthOptions {
@@ -51,10 +53,14 @@ interface AddAuthOptions {
51
53
  scheme?: string;
52
54
  }
53
55
 
54
- function writeIfMissing(filePath: string, content: string): void {
55
- if (fs.existsSync(filePath)) return;
56
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
57
- fs.writeFileSync(filePath, content, "utf-8");
56
+ const AUTH_GENERATOR = "add-auth";
57
+ const managedMeta = { ownership: "managed" as const, generator: AUTH_GENERATOR };
58
+ const extensionMeta = { ownership: "extension-point" as const, generator: AUTH_GENERATOR };
59
+ const onceMeta = { ownership: "generated-once" as const, generator: AUTH_GENERATOR };
60
+
61
+ function writeIfMissing(session: FileOperationSession, filePath: string, content: string): void {
62
+ if (session.fileExists(filePath)) return;
63
+ session.writeFile(filePath, content, onceMeta);
58
64
  }
59
65
 
60
66
  function defaultCustomJwtConfig(): CustomJwtConfig {
@@ -69,79 +75,72 @@ function defaultCustomJwtConfig(): CustomJwtConfig {
69
75
  };
70
76
  }
71
77
 
72
- function setupSwaAuth(cwd: string, backendLanguage: BackendLanguage): void {
78
+ function setupSwaAuth(cwd: string, backendLanguage: BackendLanguage, session: FileOperationSession): void {
73
79
  const helperPath = backendLanguage === "typescript"
74
80
  ? path.join(cwd, "functions", "src", "auth", "jwt-helper.ts")
75
81
  : backendLanguage === "csharp"
76
82
  ? path.join(cwd, "functions", "Auth", "JwtHelper.cs")
77
83
  : path.join(cwd, "functions", "auth", "jwt_helper.py");
78
- fs.mkdirSync(path.dirname(helperPath), { recursive: true });
79
84
  const helper = backendLanguage === "typescript"
80
85
  ? generateSwaAuthHelperTS()
81
86
  : backendLanguage === "csharp"
82
87
  ? generateSwaAuthHelperCSharp()
83
88
  : generateSwaAuthHelperPython();
84
- fs.writeFileSync(helperPath, helper, "utf-8");
89
+ session.writeFile(helperPath, helper, managedMeta);
85
90
 
86
91
  const callFnPath = path.join(cwd, "lib", "api", "call-function.ts");
87
- fs.mkdirSync(path.dirname(callFnPath), { recursive: true });
88
- fs.writeFileSync(callFnPath, generateBFFCallFunctionWithSwaAuth(), "utf-8");
92
+ session.writeFile(callFnPath, generateBFFCallFunctionWithSwaAuth(), managedMeta);
89
93
 
90
94
  const authDir = path.join(cwd, "lib", "auth");
91
- fs.mkdirSync(authDir, { recursive: true });
92
- fs.writeFileSync(path.join(authDir, "auth-context.tsx"), generateSwaAuthContext(), "utf-8");
93
- updateRootLayoutWithAuthProvider(cwd);
95
+ session.writeFile(path.join(authDir, "auth-context.tsx"), generateSwaAuthContext(), managedMeta);
96
+ updateRootLayoutWithAuthProvider(cwd, session);
94
97
  const loginDir = path.join(cwd, "app", "login");
95
- fs.mkdirSync(loginDir, { recursive: true });
96
- fs.writeFileSync(path.join(loginDir, "page.tsx"), generateSwaLoginPage(), "utf-8");
97
- updateSwaRouteConfig(cwd);
98
+ session.writeFile(path.join(loginDir, "page.tsx"), generateSwaLoginPage(), managedMeta);
99
+ updateSwaRouteConfig(cwd, session);
98
100
  }
99
101
 
100
- function setupExternalTokenAuth(cwd: string, backendLanguage: BackendLanguage): void {
102
+ function setupExternalTokenAuth(cwd: string, backendLanguage: BackendLanguage, session: FileOperationSession): void {
101
103
  const authDir = path.join(cwd, "lib", "auth");
102
- fs.mkdirSync(authDir, { recursive: true });
103
- writeIfMissing(path.join(authDir, "external-token-adapter.ts"), generateExternalTokenAdapter());
104
- fs.writeFileSync(path.join(authDir, "authenticated-fetch.ts"), generateAuthenticatedFetch(), "utf-8");
105
- fs.writeFileSync(path.join(authDir, "auth-context.tsx"), generateExternalTokenAuthContext(), "utf-8");
106
- fs.writeFileSync(path.join(cwd, "lib", "api", "call-function.ts"), generateBFFCallFunctionWithAuth(), "utf-8");
104
+ writeIfMissing(session, path.join(authDir, "external-token-adapter.ts"), generateExternalTokenAdapter());
105
+ session.writeFile(path.join(authDir, "authenticated-fetch.ts"), generateAuthenticatedFetch(), managedMeta);
106
+ session.writeFile(path.join(authDir, "auth-context.tsx"), generateExternalTokenAuthContext(), managedMeta);
107
+ session.writeFile(path.join(cwd, "lib", "api", "call-function.ts"), generateBFFCallFunctionWithAuth(), managedMeta);
107
108
  const meDir = path.join(cwd, "app", "api", "auth", "me");
108
- fs.mkdirSync(meDir, { recursive: true });
109
- fs.writeFileSync(path.join(meDir, "route.ts"), generateExternalTokenBFFMeRoute(), "utf-8");
110
- updateRootLayoutWithAuthProvider(cwd);
109
+ session.writeFile(path.join(meDir, "route.ts"), generateExternalTokenBFFMeRoute(), managedMeta);
110
+ updateRootLayoutWithAuthProvider(cwd, session);
111
111
 
112
112
  if (backendLanguage === "typescript") {
113
113
  const verifier = path.join(cwd, "functions", "src", "auth", "external-token-verifier.ts");
114
- writeIfMissing(verifier, generateExternalTokenVerifierTS());
115
- fs.writeFileSync(path.join(cwd, "functions", "src", "auth", "jwt-helper.ts"), generateExternalTokenHelperTS(), "utf-8");
116
- fs.writeFileSync(path.join(cwd, "functions", "src", "auth-me.ts"), generateExternalAuthMeTS(), "utf-8");
114
+ writeIfMissing(session, verifier, generateExternalTokenVerifierTS());
115
+ session.writeFile(path.join(cwd, "functions", "src", "auth", "jwt-helper.ts"), generateExternalTokenHelperTS(), managedMeta);
116
+ session.writeFile(path.join(cwd, "functions", "src", "auth-me.ts"), generateExternalAuthMeTS(), managedMeta);
117
117
  } else if (backendLanguage === "csharp") {
118
118
  const verifier = path.join(cwd, "functions", "Auth", "ExternalTokenVerifier.cs");
119
- writeIfMissing(verifier, generateExternalTokenVerifierCSharp());
120
- fs.writeFileSync(path.join(cwd, "functions", "Auth", "JwtHelper.cs"), generateExternalTokenHelperCSharp(), "utf-8");
121
- fs.writeFileSync(path.join(cwd, "functions", "Auth", "ExternalAuthMe.cs"), generateExternalAuthMeCSharp(), "utf-8");
119
+ writeIfMissing(session, verifier, generateExternalTokenVerifierCSharp());
120
+ session.writeFile(path.join(cwd, "functions", "Auth", "JwtHelper.cs"), generateExternalTokenHelperCSharp(), managedMeta);
121
+ session.writeFile(path.join(cwd, "functions", "Auth", "ExternalAuthMe.cs"), generateExternalAuthMeCSharp(), managedMeta);
122
122
  } else {
123
123
  const verifier = path.join(cwd, "functions", "auth", "external_token_verifier.py");
124
- writeIfMissing(verifier, generateExternalTokenVerifierPython());
125
- fs.writeFileSync(path.join(cwd, "functions", "auth", "jwt_helper.py"), generateExternalTokenHelperPython(), "utf-8");
124
+ writeIfMissing(session, verifier, generateExternalTokenVerifierPython());
125
+ session.writeFile(path.join(cwd, "functions", "auth", "jwt_helper.py"), generateExternalTokenHelperPython(), managedMeta);
126
126
  const authMePath = path.join(cwd, "functions", "blueprints", "auth_me.py");
127
- fs.mkdirSync(path.dirname(authMePath), { recursive: true });
128
- fs.writeFileSync(authMePath, generateExternalAuthMePython(), "utf-8");
129
- updatePythonAuthMeRegistration(cwd);
127
+ session.writeFile(authMePath, generateExternalAuthMePython(), managedMeta);
128
+ updatePythonAuthMeRegistration(cwd, session);
130
129
  }
131
130
  }
132
131
 
133
- function updatePythonAuthMeRegistration(cwd: string): void {
132
+ function updatePythonAuthMeRegistration(cwd: string, session: FileOperationSession): void {
134
133
  const appPath = path.join(cwd, "functions", "function_app.py");
135
- if (!fs.existsSync(appPath)) return;
134
+ if (!session.fileExists(appPath)) return;
136
135
  const importLine = "from blueprints.auth_me import bp as external_auth_me_bp";
137
136
  const registerLine = "app.register_blueprint(external_auth_me_bp)";
138
- let content = fs.readFileSync(appPath, "utf-8");
137
+ let content = session.readFile(appPath);
139
138
  if (content.includes(importLine)) return;
140
139
  const marker = "# SwallowKit scaffold registrations";
141
140
  content = content.includes(marker)
142
141
  ? content.replace(marker, `${importLine}\n${registerLine}\n${marker}`)
143
142
  : `${content.trimEnd()}\n${importLine}\n${registerLine}\n`;
144
- fs.writeFileSync(appPath, content, "utf-8");
143
+ session.writeFile(appPath, content, extensionMeta);
145
144
  }
146
145
 
147
146
  function generateSwaLoginPage(): string {
@@ -210,25 +209,25 @@ export function addAuthProviderToRootLayout(layout: string): string {
210
209
  return updated;
211
210
  }
212
211
 
213
- function updateRootLayoutWithAuthProvider(cwd: string): void {
212
+ function updateRootLayoutWithAuthProvider(cwd: string, session: FileOperationSession): void {
214
213
  const layoutPath = path.join(cwd, "app", "layout.tsx");
215
- if (!fs.existsSync(layoutPath)) {
214
+ if (!session.fileExists(layoutPath)) {
216
215
  throw new Error("app/layout.tsx was not found; cannot install AuthProvider");
217
216
  }
218
- const layout = fs.readFileSync(layoutPath, "utf-8");
217
+ const layout = session.readFile(layoutPath);
219
218
  const updated = addAuthProviderToRootLayout(layout);
220
- if (updated !== layout) fs.writeFileSync(layoutPath, updated, "utf-8");
219
+ if (updated !== layout) session.writeFile(layoutPath, updated, extensionMeta);
221
220
  }
222
221
 
223
- function updateSwaRouteConfig(cwd: string): void {
222
+ function updateSwaRouteConfig(cwd: string, session: FileOperationSession): void {
224
223
  const configPath = path.join(cwd, "staticwebapp.config.json");
225
- const config = fs.existsSync(configPath) ? JSON.parse(fs.readFileSync(configPath, "utf-8")) : {};
224
+ const config = session.fileExists(configPath) ? JSON.parse(session.readFile(configPath)) : {};
226
225
  config.routes = Array.isArray(config.routes)
227
226
  ? config.routes.filter((route: { route?: string }) => route.route !== "/api/*")
228
227
  : [];
229
228
  config.routes.unshift({ route: "/api/*", allowedRoles: ["authenticated"] });
230
229
  config.responseOverrides = { ...(config.responseOverrides || {}), "401": { statusCode: 302, redirect: "/.auth/login/aad?post_login_redirect_uri=.referrer" } };
231
- fs.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
230
+ session.writeFile(configPath, JSON.stringify(config, null, 2), extensionMeta);
232
231
  }
233
232
 
234
233
  export async function addAuthCommand(options: AddAuthOptions) {
@@ -246,11 +245,20 @@ export async function addAuthCommand(options: AddAuthOptions) {
246
245
  const config = getValidatedFullConfig();
247
246
  const cwd = process.cwd();
248
247
 
248
+ // Plan(dry-run) や Apply からセッションが供給される。単体実行時は commit セッションを生成。
249
+ const session = getActiveFileSession() ?? new FileOperationSession("commit");
250
+ const finalize = async () => {
251
+ if (session.mode === "commit") {
252
+ recordSessionOperations(session.operations);
253
+ await syncProjectManifest();
254
+ }
255
+ };
256
+
249
257
  if (options.scheme) {
250
258
  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 '-'");
251
259
  if (provider === "none" || provider === "swa-custom") throw new Error(`Provider '${provider}' cannot be added as a named scheme`);
252
- addNamedScheme(cwd, options.scheme, provider, backendLanguage);
253
- await syncProjectManifest();
260
+ addNamedScheme(cwd, options.scheme, provider, backendLanguage, session);
261
+ await finalize();
254
262
  console.log(`\n Named authentication scheme '${options.scheme}' added.`);
255
263
  return;
256
264
  }
@@ -259,22 +267,22 @@ export async function addAuthCommand(options: AddAuthOptions) {
259
267
  throw new Error("The swa-custom provider is not implemented. Use --provider swa or --provider custom-jwt.");
260
268
  }
261
269
  if (provider === "none") {
262
- updateConfigWithAuth(cwd, provider, config.auth?.customJwt || defaultCustomJwtConfig());
263
- await syncProjectManifest();
270
+ updateConfigWithAuth(cwd, provider, config.auth?.customJwt || defaultCustomJwtConfig(), session);
271
+ await finalize();
264
272
  console.log(" Authentication disabled in swallowkit.config.js");
265
273
  return;
266
274
  }
267
275
  if (provider === "swa") {
268
- setupSwaAuth(cwd, backendLanguage);
269
- updateConfigWithAuth(cwd, provider, config.auth?.customJwt || defaultCustomJwtConfig());
270
- await syncProjectManifest();
276
+ setupSwaAuth(cwd, backendLanguage, session);
277
+ updateConfigWithAuth(cwd, provider, config.auth?.customJwt || defaultCustomJwtConfig(), session);
278
+ await finalize();
271
279
  console.log("\n SWA built-in authentication setup complete!");
272
280
  return;
273
281
  }
274
282
  if (provider === "external-token") {
275
- setupExternalTokenAuth(cwd, backendLanguage);
276
- updateConfigWithAuth(cwd, provider, config.auth?.customJwt || defaultCustomJwtConfig());
277
- await syncProjectManifest();
283
+ setupExternalTokenAuth(cwd, backendLanguage, session);
284
+ updateConfigWithAuth(cwd, provider, config.auth?.customJwt || defaultCustomJwtConfig(), session);
285
+ await finalize();
278
286
  console.log("\n External token authentication setup complete!");
279
287
  console.log(" Implement the generated frontend adapter and backend verifier before use.");
280
288
  return;
@@ -301,16 +309,15 @@ export async function addAuthCommand(options: AddAuthOptions) {
301
309
  // 1. Generate shared/models/auth.ts
302
310
  console.log(" Generating auth models...");
303
311
  const modelsDir = path.join(cwd, "shared", "models");
304
- fs.mkdirSync(modelsDir, { recursive: true });
305
312
  const authModelPath = path.join(modelsDir, "auth.ts");
306
- fs.writeFileSync(authModelPath, generateAuthModels(), "utf-8");
313
+ session.writeFile(authModelPath, generateAuthModels(), managedMeta);
307
314
  console.log(` Created: shared/models/auth.ts`);
308
315
 
309
316
  // Ensure shared package has build infrastructure (tsconfig, build script)
310
- ensureSharedBuildInfrastructure(cwd);
317
+ ensureSharedBuildInfrastructure(cwd, session);
311
318
 
312
319
  // Update shared/index.ts to re-export auth
313
- updateSharedIndex(cwd);
320
+ updateSharedIndex(cwd, session);
314
321
 
315
322
  // Resolve RDB provider for dependency installation
316
323
  const connDef = getConnectorDefinition(customJwtConfig.userConnector);
@@ -318,52 +325,48 @@ export async function addAuthCommand(options: AddAuthOptions) {
318
325
 
319
326
  // 2. Generate Functions auth code
320
327
  console.log("\n Generating auth functions...");
321
- generateFunctionsAuth(cwd, backendLanguage, sharedPackageName, customJwtConfig);
328
+ generateFunctionsAuth(cwd, backendLanguage, sharedPackageName, customJwtConfig, session);
322
329
 
323
330
  // 3. Generate BFF auth routes
324
331
  console.log("\n Generating BFF auth routes...");
325
- generateBFFAuth(cwd, projectName, sharedPackageName);
332
+ generateBFFAuth(cwd, projectName, sharedPackageName, session);
326
333
 
327
334
  // 4. Generate proxy
328
335
  console.log("\n Generating proxy...");
329
336
  const proxyPath = path.join(cwd, "proxy.ts");
330
- fs.writeFileSync(proxyPath, generateProxy(projectName), "utf-8");
337
+ session.writeFile(proxyPath, generateProxy(projectName), managedMeta);
331
338
  console.log(` Created: proxy.ts`);
332
339
 
333
340
  // 5. Generate login page
334
341
  console.log("\n Generating login page...");
335
342
  const loginDir = path.join(cwd, "app", "login");
336
- fs.mkdirSync(loginDir, { recursive: true });
337
- fs.writeFileSync(path.join(loginDir, "page.tsx"), generateLoginPage(), "utf-8");
343
+ session.writeFile(path.join(loginDir, "page.tsx"), generateLoginPage(), managedMeta);
338
344
  console.log(` Created: app/login/page.tsx`);
339
345
 
340
346
  // 6. Generate auth context
341
347
  console.log("\n Generating auth context...");
342
348
  const authLibDir = path.join(cwd, "lib", "auth");
343
- fs.mkdirSync(authLibDir, { recursive: true });
344
- fs.writeFileSync(path.join(authLibDir, "auth-context.tsx"), generateAuthContext(), "utf-8");
349
+ session.writeFile(path.join(authLibDir, "auth-context.tsx"), generateAuthContext(), managedMeta);
345
350
  console.log(`✅ Created: lib/auth/auth-context.tsx`);
346
351
 
347
352
  // 7. Update callFunction with auth support
348
353
  console.log("\n Updating callFunction with auth support...");
349
354
  const callFnPath = path.join(cwd, "lib", "api", "call-function.ts");
350
- const callFnDir = path.dirname(callFnPath);
351
- fs.mkdirSync(callFnDir, { recursive: true });
352
- fs.writeFileSync(callFnPath, generateBFFCallFunctionWithAuth(), "utf-8");
355
+ session.writeFile(callFnPath, generateBFFCallFunctionWithAuth(), managedMeta);
353
356
  console.log(` Updated: lib/api/call-function.ts`);
354
357
 
355
358
  // 8. Update swallowkit.config.js
356
359
  console.log("\n Updating configuration...");
357
- updateConfigWithAuth(cwd, provider, customJwtConfig);
360
+ updateConfigWithAuth(cwd, provider, customJwtConfig, session);
358
361
 
359
362
  // 9. Update environment files
360
363
  console.log("\n Updating environment files...");
361
- updateEnvironmentFiles(cwd);
364
+ updateEnvironmentFiles(cwd, session);
362
365
 
363
366
  // 10. Install dependencies
364
367
  console.log("\n Installing auth dependencies...");
365
- await installAuthDependencies(cwd, backendLanguage, rdbProvider);
366
- await syncProjectManifest();
368
+ await installAuthDependencies(cwd, backendLanguage, rdbProvider, session);
369
+ await finalize();
367
370
 
368
371
  console.log("\n Authentication setup complete!");
369
372
  console.log("\n Next steps:");
@@ -389,22 +392,22 @@ function findObjectEnd(content: string, open: number): number {
389
392
  return -1;
390
393
  }
391
394
 
392
- function updateConfigWithNamedScheme(cwd: string, scheme: string, provider: AuthProvider): void {
395
+ function updateConfigWithNamedScheme(cwd: string, scheme: string, provider: AuthProvider, session: FileOperationSession): void {
393
396
  const jsPath = path.join(cwd, "swallowkit.config.js");
394
397
  const jsonPath = path.join(cwd, "swallowkit.config.json");
395
- const configPath = fs.existsSync(jsPath) ? jsPath : jsonPath;
396
- if (!fs.existsSync(configPath)) throw new Error("swallowkit.config.js or swallowkit.config.json is required");
398
+ const configPath = session.fileExists(jsPath) ? jsPath : jsonPath;
399
+ if (!session.fileExists(configPath)) throw new Error("swallowkit.config.js or swallowkit.config.json is required");
397
400
  if (configPath.endsWith(".json")) {
398
- const value = JSON.parse(fs.readFileSync(configPath, "utf-8"));
401
+ const value = JSON.parse(session.readFile(configPath));
399
402
  value.auth ??= {};
400
403
  value.auth.schemes ??= {};
401
404
  if (value.auth.schemes[scheme]) throw new Error(`auth.schemes.${scheme} already exists; no files were changed`);
402
405
  value.auth.schemes[scheme] = provider === "custom-jwt" ? { provider, customJwt: defaultCustomJwtConfig() } : { provider };
403
406
  value.auth.authorization ??= { defaultPolicy: "anonymous", policies: {} };
404
- fs.writeFileSync(configPath, JSON.stringify(value, null, 2) + "\n", "utf-8");
407
+ session.writeFile(configPath, JSON.stringify(value, null, 2) + "\n", extensionMeta);
405
408
  return;
406
409
  }
407
- let content = fs.readFileSync(configPath, "utf-8");
410
+ let content = session.readFile(configPath);
408
411
  const authMatch = /\bauth\s*:\s*\{/.exec(content);
409
412
  const customJwt = defaultCustomJwtConfig();
410
413
  const entry = provider === "custom-jwt"
@@ -430,45 +433,45 @@ function updateConfigWithNamedScheme(cwd: string, scheme: string, provider: Auth
430
433
  content = content.slice(0, authOpen + 1) + `\n schemes: {${entry}\n },` + content.slice(authOpen + 1);
431
434
  }
432
435
  }
433
- fs.writeFileSync(configPath, content, "utf-8");
436
+ session.writeFile(configPath, content, extensionMeta);
434
437
  }
435
438
 
436
- function addNamedScheme(cwd: string, scheme: string, provider: AuthProvider, language: BackendLanguage): void {
439
+ function addNamedScheme(cwd: string, scheme: string, provider: AuthProvider, language: BackendLanguage, session: FileOperationSession): void {
437
440
  // Validate/update config before creating files, so duplicate names stop safely.
438
- updateConfigWithNamedScheme(cwd, scheme, provider);
441
+ updateConfigWithNamedScheme(cwd, scheme, provider, session);
439
442
  const slug = scheme.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/_/g, "-").toLowerCase();
440
443
  if (language === "typescript") {
441
444
  const dir = path.join(cwd, "functions", "src", "auth", "schemes", slug);
442
- if (provider === "external-token") writeIfMissing(path.join(dir, "verifier.ts"), generateExternalTokenVerifierTS());
443
- else if (provider === "swa") writeIfMissing(path.join(dir, "adapter.ts"), generateSwaAuthHelperTS());
444
- else writeIfMissing(path.join(dir, "jwt-helper.ts"), generateJwtHelperTS());
445
+ if (provider === "external-token") writeIfMissing(session, path.join(dir, "verifier.ts"), generateExternalTokenVerifierTS());
446
+ else if (provider === "swa") writeIfMissing(session, path.join(dir, "adapter.ts"), generateSwaAuthHelperTS());
447
+ else writeIfMissing(session, path.join(dir, "jwt-helper.ts"), generateJwtHelperTS());
445
448
  } else if (language === "csharp") {
446
449
  const dir = path.join(cwd, "functions", "Auth", "Schemes", slug);
447
- if (provider === "external-token") writeIfMissing(path.join(dir, "ExternalTokenVerifier.cs"), generateNamedExternalTokenVerifierCSharp(scheme));
448
- else if (provider === "swa") writeIfMissing(path.join(dir, "SwaAdapter.cs"), generateSwaAuthHelperCSharp());
449
- else writeIfMissing(path.join(dir, "JwtHelper.cs"), generateJwtHelperCSharp());
450
+ if (provider === "external-token") writeIfMissing(session, path.join(dir, "ExternalTokenVerifier.cs"), generateNamedExternalTokenVerifierCSharp(scheme));
451
+ else if (provider === "swa") writeIfMissing(session, path.join(dir, "SwaAdapter.cs"), generateSwaAuthHelperCSharp());
452
+ else writeIfMissing(session, path.join(dir, "JwtHelper.cs"), generateJwtHelperCSharp());
450
453
  } else {
451
454
  const dir = path.join(cwd, "functions", "auth", "schemes", slug.replace(/-/g, "_"));
452
- if (provider === "external-token") writeIfMissing(path.join(dir, "verifier.py"), generateNamedExternalTokenVerifierPython(scheme));
453
- else if (provider === "swa") writeIfMissing(path.join(dir, "adapter.py"), generateSwaAuthHelperPython());
454
- else writeIfMissing(path.join(dir, "jwt_helper.py"), generateJwtHelperPython());
455
+ if (provider === "external-token") writeIfMissing(session, path.join(dir, "verifier.py"), generateNamedExternalTokenVerifierPython(scheme));
456
+ else if (provider === "swa") writeIfMissing(session, path.join(dir, "adapter.py"), generateSwaAuthHelperPython());
457
+ else writeIfMissing(session, path.join(dir, "jwt_helper.py"), generateJwtHelperPython());
455
458
  }
456
459
  const clientDir = path.join(cwd, "lib", "auth", "schemes", slug);
457
460
  if (provider === "external-token") {
458
- writeIfMissing(path.join(clientDir, "token-adapter.ts"), generateExternalTokenAdapter());
459
- writeIfMissing(path.join(clientDir, "authenticated-fetch.ts"), generateAuthenticatedFetch().replace("'./external-token-adapter'", "'./token-adapter'"));
460
- writeIfMissing(path.join(clientDir, "auth-context.tsx"), generateExternalTokenAuthContext().replace(/external-token-adapter/g, "token-adapter"));
461
+ writeIfMissing(session, path.join(clientDir, "token-adapter.ts"), generateExternalTokenAdapter());
462
+ writeIfMissing(session, path.join(clientDir, "authenticated-fetch.ts"), generateAuthenticatedFetch().replace("'./external-token-adapter'", "'./token-adapter'"));
463
+ writeIfMissing(session, path.join(clientDir, "auth-context.tsx"), generateExternalTokenAuthContext().replace(/external-token-adapter/g, "token-adapter"));
461
464
  } else if (provider === "swa") {
462
- writeIfMissing(path.join(clientDir, "auth-context.tsx"), generateSwaAuthContext());
465
+ writeIfMissing(session, path.join(clientDir, "auth-context.tsx"), generateSwaAuthContext());
463
466
  } else if (provider === "custom-jwt") {
464
- writeIfMissing(path.join(clientDir, "auth-context.tsx"), generateAuthContext());
467
+ writeIfMissing(session, path.join(clientDir, "auth-context.tsx"), generateAuthContext());
465
468
  }
466
469
  const callFunctionPath = path.join(cwd, "lib", "api", "call-function.ts");
467
- if (!fs.existsSync(callFunctionPath)) writeIfMissing(callFunctionPath, generateBFFCallFunctionWithMultipleAuth());
470
+ if (!session.fileExists(callFunctionPath)) writeIfMissing(session, callFunctionPath, generateBFFCallFunctionWithMultipleAuth());
468
471
  else {
469
- const current = fs.readFileSync(callFunctionPath, "utf-8");
472
+ const current = session.readFile(callFunctionPath);
470
473
  if (!current.includes("x-ms-client-principal") || !current.includes("fetchHeaders['Authorization']")) {
471
- if (current.includes("SwallowKit BFF Call Function Helper")) fs.writeFileSync(callFunctionPath, generateBFFCallFunctionWithMultipleAuth(), "utf-8");
474
+ if (current.includes("SwallowKit BFF Call Function Helper")) session.writeFile(callFunctionPath, generateBFFCallFunctionWithMultipleAuth(), managedMeta);
472
475
  else console.warn(" Existing customized lib/api/call-function.ts was preserved. Ensure it forwards Bearer and SWA credentials without logging them.");
473
476
  }
474
477
  }
@@ -479,12 +482,12 @@ function addNamedScheme(cwd: string, scheme: string, provider: AuthProvider, lan
479
482
  * (tsconfig.json, build script, typescript devDependency).
480
483
  * Required for `dev` command which runs `npm run --workspace=shared build`.
481
484
  */
482
- function ensureSharedBuildInfrastructure(cwd: string): void {
485
+ function ensureSharedBuildInfrastructure(cwd: string, session: FileOperationSession): void {
483
486
  const sharedDir = path.join(cwd, "shared");
484
487
  const pkgPath = path.join(sharedDir, "package.json");
485
- if (!fs.existsSync(pkgPath)) return;
488
+ if (!session.fileExists(pkgPath)) return;
486
489
 
487
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
490
+ const pkg = JSON.parse(session.readFile(pkgPath));
488
491
  let updated = false;
489
492
 
490
493
  // Ensure scripts.build exists
@@ -510,29 +513,29 @@ function ensureSharedBuildInfrastructure(cwd: string): void {
510
513
  }
511
514
 
512
515
  if (updated) {
513
- fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2), "utf-8");
516
+ session.writeFile(pkgPath, JSON.stringify(pkg, null, 2), extensionMeta);
514
517
  console.log(` Updated: shared/package.json (added build infrastructure)`);
515
518
  }
516
519
 
517
520
  // Ensure tsconfig.json exists
518
521
  const tsconfigPath = path.join(sharedDir, "tsconfig.json");
519
- if (!fs.existsSync(tsconfigPath)) {
520
- fs.writeFileSync(tsconfigPath, JSON.stringify(buildSharedTsConfig(), null, 2), "utf-8");
522
+ if (!session.fileExists(tsconfigPath)) {
523
+ session.writeFile(tsconfigPath, JSON.stringify(buildSharedTsConfig(), null, 2), onceMeta);
521
524
  console.log(` Created: shared/tsconfig.json`);
522
525
  }
523
526
  }
524
527
 
525
- function updateSharedIndex(cwd: string): void {
528
+ function updateSharedIndex(cwd: string, session: FileOperationSession): void {
526
529
  const indexPath = path.join(cwd, "shared", "index.ts");
527
- if (fs.existsSync(indexPath)) {
528
- let content = fs.readFileSync(indexPath, "utf-8");
530
+ if (session.fileExists(indexPath)) {
531
+ let content = session.readFile(indexPath);
529
532
  if (!content.includes("./models/auth")) {
530
533
  content += `\nexport { LoginRequest, AuthUser, LoginResponse } from './models/auth';\n`;
531
- fs.writeFileSync(indexPath, content, "utf-8");
534
+ session.writeFile(indexPath, content, extensionMeta);
532
535
  console.log(` Updated: shared/index.ts`);
533
536
  }
534
537
  } else {
535
- fs.writeFileSync(indexPath, `export { LoginRequest, AuthUser, LoginResponse } from './models/auth';\n`, "utf-8");
538
+ session.writeFile(indexPath, `export { LoginRequest, AuthUser, LoginResponse } from './models/auth';\n`, extensionMeta);
536
539
  console.log(` Created: shared/index.ts`);
537
540
  }
538
541
  }
@@ -542,6 +545,7 @@ function generateFunctionsAuth(
542
545
  backendLanguage: BackendLanguage,
543
546
  sharedPackageName: string,
544
547
  config: CustomJwtConfig,
548
+ session: FileOperationSession,
545
549
  ): void {
546
550
  const functionsDir = path.join(cwd, "functions");
547
551
 
@@ -552,64 +556,61 @@ function generateFunctionsAuth(
552
556
  if (backendLanguage === "typescript") {
553
557
  // Auth functions
554
558
  const srcDir = path.join(functionsDir, "src");
555
- fs.mkdirSync(srcDir, { recursive: true });
556
- fs.writeFileSync(
559
+ session.writeFile(
557
560
  path.join(srcDir, "auth.ts"),
558
561
  generateAuthFunctionsTS(sharedPackageName, config, provider),
559
- "utf-8"
562
+ managedMeta
560
563
  );
561
564
  console.log(` Created: functions/src/auth.ts`);
562
565
 
563
566
  // JWT helper
564
567
  const authDir = path.join(srcDir, "auth");
565
- fs.mkdirSync(authDir, { recursive: true });
566
- fs.writeFileSync(
568
+ session.writeFile(
567
569
  path.join(authDir, "jwt-helper.ts"),
568
570
  generateJwtHelperTS(),
569
- "utf-8"
571
+ managedMeta
570
572
  );
571
573
  console.log(` Created: functions/src/auth/jwt-helper.ts`);
572
574
  } else if (backendLanguage === "csharp") {
573
575
  const authDir = path.join(functionsDir, "Auth");
574
- fs.mkdirSync(authDir, { recursive: true });
575
- fs.writeFileSync(
576
+ session.writeFile(
576
577
  path.join(authDir, "AuthFunctions.cs"),
577
578
  generateAuthFunctionsCSharp(config, provider),
578
- "utf-8"
579
+ managedMeta
579
580
  );
580
581
  console.log(` Created: functions/Auth/AuthFunctions.cs`);
581
- fs.writeFileSync(
582
+ session.writeFile(
582
583
  path.join(authDir, "JwtHelper.cs"),
583
584
  generateJwtHelperCSharp(),
584
- "utf-8"
585
+ managedMeta
585
586
  );
586
587
  console.log(` Created: functions/Auth/JwtHelper.cs`);
587
588
  } else if (backendLanguage === "python") {
588
589
  const blueprintsDir = path.join(functionsDir, "blueprints");
589
- fs.mkdirSync(blueprintsDir, { recursive: true });
590
- fs.writeFileSync(
590
+ session.writeFile(
591
591
  path.join(blueprintsDir, "auth.py"),
592
592
  generateAuthFunctionsPython(config, provider),
593
- "utf-8"
593
+ managedMeta
594
594
  );
595
595
  console.log(` Created: functions/blueprints/auth.py`);
596
596
 
597
597
  const authDir = path.join(functionsDir, "auth");
598
- fs.mkdirSync(authDir, { recursive: true });
599
- fs.writeFileSync(
598
+ session.writeFile(
600
599
  path.join(authDir, "jwt_helper.py"),
601
600
  generateJwtHelperPython(),
602
- "utf-8"
601
+ managedMeta
603
602
  );
604
603
  console.log(` Created: functions/auth/jwt_helper.py`);
605
604
 
606
605
  // __init__.py
607
- fs.writeFileSync(path.join(authDir, "__init__.py"), "", "utf-8");
606
+ if (!session.fileExists(path.join(authDir, "__init__.py"))) {
607
+ session.writeFile(path.join(authDir, "__init__.py"), "", onceMeta);
608
+ }
608
609
 
609
610
  // Register auth blueprint in function_app.py
610
611
  const functionAppPath = path.join(functionsDir, "function_app.py");
611
- if (fs.existsSync(functionAppPath)) {
612
- const content = fs.readFileSync(functionAppPath, "utf-8");
612
+ if (session.fileExists(functionAppPath)) {
613
+ const content = session.readFile(functionAppPath);
613
614
  const authImport = "from blueprints.auth import bp as auth_bp";
614
615
  const authRegister = "app.register_blueprint(auth_bp)";
615
616
  if (!content.includes(authImport)) {
@@ -619,7 +620,7 @@ function generateFunctionsAuth(
619
620
  marker,
620
621
  `${authImport}\n${authRegister}\n${marker}`
621
622
  );
622
- fs.writeFileSync(functionAppPath, updated, "utf-8");
623
+ session.writeFile(functionAppPath, updated, extensionMeta);
623
624
  console.log(` Updated: functions/function_app.py (registered auth blueprint)`);
624
625
  }
625
626
  }
@@ -627,48 +628,45 @@ function generateFunctionsAuth(
627
628
  }
628
629
  }
629
630
 
630
- function generateBFFAuth(cwd: string, projectName: string, sharedPackageName: string): void {
631
+ function generateBFFAuth(cwd: string, projectName: string, sharedPackageName: string, session: FileOperationSession): void {
631
632
  const authApiDir = path.join(cwd, "app", "api", "auth");
632
633
 
633
634
  // Login route
634
635
  const loginDir = path.join(authApiDir, "login");
635
- fs.mkdirSync(loginDir, { recursive: true });
636
- fs.writeFileSync(
636
+ session.writeFile(
637
637
  path.join(loginDir, "route.ts"),
638
638
  generateBFFAuthLoginRoute(projectName, sharedPackageName),
639
- "utf-8"
639
+ managedMeta
640
640
  );
641
641
  console.log(` Created: app/api/auth/login/route.ts`);
642
642
 
643
643
  // Logout route
644
644
  const logoutDir = path.join(authApiDir, "logout");
645
- fs.mkdirSync(logoutDir, { recursive: true });
646
- fs.writeFileSync(
645
+ session.writeFile(
647
646
  path.join(logoutDir, "route.ts"),
648
647
  generateBFFAuthLogoutRoute(projectName),
649
- "utf-8"
648
+ managedMeta
650
649
  );
651
650
  console.log(` Created: app/api/auth/logout/route.ts`);
652
651
 
653
652
  // Me route
654
653
  const meDir = path.join(authApiDir, "me");
655
- fs.mkdirSync(meDir, { recursive: true });
656
- fs.writeFileSync(
654
+ session.writeFile(
657
655
  path.join(meDir, "route.ts"),
658
656
  generateBFFAuthMeRoute(),
659
- "utf-8"
657
+ managedMeta
660
658
  );
661
659
  console.log(` Created: app/api/auth/me/route.ts`);
662
660
  }
663
661
 
664
- function updateConfigWithAuth(cwd: string, provider: AuthProvider, config: CustomJwtConfig): void {
662
+ function updateConfigWithAuth(cwd: string, provider: AuthProvider, config: CustomJwtConfig, session: FileOperationSession): void {
665
663
  const configPath = path.join(cwd, "swallowkit.config.js");
666
- if (!fs.existsSync(configPath)) {
664
+ if (!session.fileExists(configPath)) {
667
665
  console.warn(" swallowkit.config.js not found. Please add auth config manually.");
668
666
  return;
669
667
  }
670
668
 
671
- const content = fs.readFileSync(configPath, "utf-8");
669
+ const content = session.readFile(configPath);
672
670
 
673
671
  if (content.includes("auth:") || content.includes("auth :")) {
674
672
  console.log(" 'auth' section already exists in swallowkit.config.js");
@@ -712,40 +710,40 @@ ${providerSettings}
712
710
  `;
713
711
 
714
712
  const newContent = content.substring(0, closingBraceIdx) + authBlock + content.substring(closingBraceIdx);
715
- fs.writeFileSync(configPath, newContent, "utf-8");
713
+ session.writeFile(configPath, newContent, extensionMeta);
716
714
  console.log(` Updated: swallowkit.config.js`);
717
715
  }
718
716
 
719
- function updateEnvironmentFiles(cwd: string): void {
717
+ function updateEnvironmentFiles(cwd: string, session: FileOperationSession): void {
720
718
  // Update functions/local.settings.json
721
719
  const localSettingsPath = path.join(cwd, "functions", "local.settings.json");
722
- if (fs.existsSync(localSettingsPath)) {
723
- const settings = JSON.parse(fs.readFileSync(localSettingsPath, "utf-8"));
720
+ if (session.fileExists(localSettingsPath)) {
721
+ const settings = JSON.parse(session.readFile(localSettingsPath));
724
722
  if (!settings.Values) settings.Values = {};
725
723
  if (!settings.Values.JWT_SECRET) {
726
724
  settings.Values.JWT_SECRET = "dev-jwt-secret-change-in-production-min-32-chars!!";
727
725
  }
728
- fs.writeFileSync(localSettingsPath, JSON.stringify(settings, null, 2), "utf-8");
726
+ session.writeFile(localSettingsPath, JSON.stringify(settings, null, 2), extensionMeta);
729
727
  console.log(` Updated: functions/local.settings.json`);
730
728
  }
731
729
 
732
730
  // Update .env.example
733
731
  const envExamplePath = path.join(cwd, ".env.example");
734
- if (fs.existsSync(envExamplePath)) {
735
- let content = fs.readFileSync(envExamplePath, "utf-8");
732
+ if (session.fileExists(envExamplePath)) {
733
+ let content = session.readFile(envExamplePath);
736
734
  if (!content.includes("JWT_SECRET")) {
737
735
  content += "\n# Authentication\nJWT_SECRET=your-jwt-secret-key-at-least-32-chars\n";
738
- fs.writeFileSync(envExamplePath, content, "utf-8");
736
+ session.writeFile(envExamplePath, content, extensionMeta);
739
737
  console.log(` Updated: .env.example`);
740
738
  }
741
739
  }
742
740
  }
743
741
 
744
- async function installAuthDependencies(cwd: string, backendLanguage: BackendLanguage, provider: "mysql" | "postgres" | "sqlserver" = "mysql"): Promise<void> {
742
+ async function installAuthDependencies(cwd: string, backendLanguage: BackendLanguage, provider: "mysql" | "postgres" | "sqlserver" = "mysql", session: FileOperationSession): Promise<void> {
745
743
  if (backendLanguage === "typescript") {
746
744
  const funcPkgPath = path.join(cwd, "functions", "package.json");
747
- if (fs.existsSync(funcPkgPath)) {
748
- const funcPkg = JSON.parse(fs.readFileSync(funcPkgPath, "utf-8"));
745
+ if (session.fileExists(funcPkgPath)) {
746
+ const funcPkg = JSON.parse(session.readFile(funcPkgPath));
749
747
  if (!funcPkg.dependencies) funcPkg.dependencies = {};
750
748
  funcPkg.dependencies["jsonwebtoken"] = "^9.0.0";
751
749
  funcPkg.dependencies["bcryptjs"] = "^2.4.3";
@@ -757,16 +755,18 @@ async function installAuthDependencies(cwd: string, backendLanguage: BackendLang
757
755
  funcPkg.devDependencies["@types/jsonwebtoken"] = "^9.0.0";
758
756
  funcPkg.devDependencies["@types/bcryptjs"] = "^2.4.0";
759
757
  if (provider === "postgres") funcPkg.devDependencies["@types/pg"] = "^8.11.0";
760
- fs.writeFileSync(funcPkgPath, JSON.stringify(funcPkg, null, 2), "utf-8");
758
+ session.writeFile(funcPkgPath, JSON.stringify(funcPkg, null, 2), extensionMeta);
761
759
  console.log(` Updated: functions/package.json with auth dependencies (${provider})`);
762
760
  }
763
761
  } else if (backendLanguage === "csharp") {
764
762
  // Add NuGet package references to .csproj
765
763
  const functionsDir = path.join(cwd, "functions");
766
- const csprojFiles = fs.readdirSync(functionsDir).filter((f: string) => f.endsWith(".csproj"));
764
+ const csprojFiles = fs.existsSync(functionsDir)
765
+ ? fs.readdirSync(functionsDir).filter((f: string) => f.endsWith(".csproj"))
766
+ : [];
767
767
  if (csprojFiles.length > 0) {
768
768
  const csprojPath = path.join(functionsDir, csprojFiles[0]);
769
- let csprojContent = fs.readFileSync(csprojPath, "utf-8");
769
+ let csprojContent = session.readFile(csprojPath);
770
770
  const nugetPackages: { name: string; version: string }[] = [
771
771
  { name: "System.IdentityModel.Tokens.Jwt", version: "7.0.0" },
772
772
  { name: "Microsoft.IdentityModel.Tokens", version: "7.0.0" },
@@ -787,7 +787,7 @@ async function installAuthDependencies(cwd: string, backendLanguage: BackendLang
787
787
  }
788
788
  }
789
789
  }
790
- fs.writeFileSync(csprojPath, csprojContent, "utf-8");
790
+ session.writeFile(csprojPath, csprojContent, extensionMeta);
791
791
  console.log(` Updated: ${csprojFiles[0]} with auth NuGet packages (${provider})`);
792
792
  }
793
793
  } else if (backendLanguage === "python") {
@@ -798,17 +798,17 @@ async function installAuthDependencies(cwd: string, backendLanguage: BackendLang
798
798
  if (provider === "mysql") baseDeps.push("mysql-connector-python>=8.3.0");
799
799
  else if (provider === "postgres") baseDeps.push("psycopg2-binary>=2.9.0");
800
800
  else baseDeps.push("pymssql>=2.2.0");
801
- if (fs.existsSync(requirementsPath)) {
802
- let content = fs.readFileSync(requirementsPath, "utf-8");
801
+ if (session.fileExists(requirementsPath)) {
802
+ let content = session.readFile(requirementsPath);
803
803
  for (const dep of baseDeps) {
804
804
  const pkgName = dep.split(">=")[0].split("==")[0];
805
805
  if (!content.includes(pkgName)) {
806
806
  content += `${dep}\n`;
807
807
  }
808
808
  }
809
- fs.writeFileSync(requirementsPath, content, "utf-8");
809
+ session.writeFile(requirementsPath, content, extensionMeta);
810
810
  } else {
811
- fs.writeFileSync(requirementsPath, baseDeps.join("\n") + "\n", "utf-8");
811
+ session.writeFile(requirementsPath, baseDeps.join("\n") + "\n", extensionMeta);
812
812
  }
813
813
  console.log(` Updated: functions/requirements.txt with auth dependencies`);
814
814
  }