swallowkit 1.0.0-beta.18 → 1.0.0-beta.19

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 (61) hide show
  1. package/dist/cli/commands/add-auth.d.ts.map +1 -1
  2. package/dist/cli/commands/add-auth.js +1 -4
  3. package/dist/cli/commands/add-auth.js.map +1 -1
  4. package/dist/cli/commands/add-connector.d.ts +1 -1
  5. package/dist/cli/commands/add-connector.d.ts.map +1 -1
  6. package/dist/cli/commands/add-connector.js +7 -7
  7. package/dist/cli/commands/add-connector.js.map +1 -1
  8. package/dist/cli/commands/dev.d.ts +11 -0
  9. package/dist/cli/commands/dev.d.ts.map +1 -1
  10. package/dist/cli/commands/dev.js +32 -22
  11. package/dist/cli/commands/dev.js.map +1 -1
  12. package/dist/cli/commands/init.js +1 -2
  13. package/dist/cli/commands/init.js.map +1 -1
  14. package/dist/cli/commands/scaffold.d.ts.map +1 -1
  15. package/dist/cli/commands/scaffold.js +3 -6
  16. package/dist/cli/commands/scaffold.js.map +1 -1
  17. package/dist/cli/index.d.ts +4 -1
  18. package/dist/cli/index.d.ts.map +1 -1
  19. package/dist/cli/index.js +153 -79
  20. package/dist/cli/index.js.map +1 -1
  21. package/dist/core/config.d.ts.map +1 -1
  22. package/dist/core/config.js +11 -3
  23. package/dist/core/config.js.map +1 -1
  24. package/dist/core/mock/connector-mock-server.d.ts.map +1 -1
  25. package/dist/core/mock/connector-mock-server.js +20 -17
  26. package/dist/core/mock/connector-mock-server.js.map +1 -1
  27. package/dist/core/scaffold/auth-generator.d.ts +1 -1
  28. package/dist/core/scaffold/auth-generator.d.ts.map +1 -1
  29. package/dist/core/scaffold/auth-generator.js +2 -1
  30. package/dist/core/scaffold/auth-generator.js.map +1 -1
  31. package/dist/core/scaffold/connector-functions-generator.d.ts.map +1 -1
  32. package/dist/core/scaffold/connector-functions-generator.js +0 -1
  33. package/dist/core/scaffold/connector-functions-generator.js.map +1 -1
  34. package/dist/core/scaffold/functions-generator.d.ts.map +1 -1
  35. package/dist/core/scaffold/functions-generator.js +0 -8
  36. package/dist/core/scaffold/functions-generator.js.map +1 -1
  37. package/dist/core/scaffold/model-parser.d.ts.map +1 -1
  38. package/dist/core/scaffold/model-parser.js +8 -10
  39. package/dist/core/scaffold/model-parser.js.map +1 -1
  40. package/dist/core/scaffold/nextjs-generator.d.ts.map +1 -1
  41. package/dist/core/scaffold/nextjs-generator.js +0 -1
  42. package/dist/core/scaffold/nextjs-generator.js.map +1 -1
  43. package/dist/core/scaffold/ui-generator.d.ts.map +1 -1
  44. package/dist/core/scaffold/ui-generator.js +0 -3
  45. package/dist/core/scaffold/ui-generator.js.map +1 -1
  46. package/package.json +1 -1
  47. package/src/__tests__/dev.test.ts +106 -0
  48. package/src/cli/commands/add-auth.ts +1 -5
  49. package/src/cli/commands/add-connector.ts +10 -11
  50. package/src/cli/commands/dev.ts +44 -25
  51. package/src/cli/commands/init.ts +1 -3
  52. package/src/cli/commands/scaffold.ts +0 -4
  53. package/src/cli/index.ts +167 -83
  54. package/src/core/config.ts +15 -3
  55. package/src/core/mock/connector-mock-server.ts +12 -12
  56. package/src/core/scaffold/auth-generator.ts +3 -2
  57. package/src/core/scaffold/connector-functions-generator.ts +0 -2
  58. package/src/core/scaffold/functions-generator.ts +0 -9
  59. package/src/core/scaffold/model-parser.ts +5 -9
  60. package/src/core/scaffold/nextjs-generator.ts +1 -2
  61. package/src/core/scaffold/ui-generator.ts +1 -5
@@ -6,7 +6,7 @@
6
6
  import * as fs from "fs";
7
7
  import * as path from "path";
8
8
  import { ensureSwallowKitProject } from "../../core/config";
9
- import { ConnectorDefinition } from "../../types";
9
+ import { ApiConnectorConfig, ConnectorDefinition } from "../../types";
10
10
 
11
11
  interface AddConnectorOptions {
12
12
  name: string;
@@ -32,7 +32,7 @@ export async function addConnectorCommand(options: AddConnectorOptions) {
32
32
  const connectorDef = buildConnectorDefinition(options);
33
33
 
34
34
  // Update config file
35
- updateConfigWithConnector(configPath, options.name, connectorDef, options);
35
+ updateConfigWithConnector(configPath, options.name, connectorDef);
36
36
 
37
37
  console.log(`\nāœ… Connector '${options.name}' added successfully!`);
38
38
  console.log("\nšŸ“ Next steps:");
@@ -81,8 +81,7 @@ function buildConnectorDefinition(options: AddConnectorOptions): ConnectorDefini
81
81
  export function updateConfigWithConnector(
82
82
  configPath: string,
83
83
  connectorName: string,
84
- connectorDef: ConnectorDefinition,
85
- options: AddConnectorOptions
84
+ connectorDef: ConnectorDefinition
86
85
  ): void {
87
86
  const content = fs.readFileSync(configPath, "utf-8");
88
87
 
@@ -101,7 +100,7 @@ export function updateConfigWithConnector(
101
100
  // JS config — append connectors section
102
101
  if (content.includes("connectors:") || content.includes("connectors :")) {
103
102
  console.log(`āš ļø 'connectors' section already exists in ${configPath}. Please add the connector manually:`);
104
- console.log(formatConnectorSnippet(connectorName, connectorDef, options));
103
+ console.log(formatConnectorSnippet(connectorName, connectorDef));
105
104
  return;
106
105
  }
107
106
 
@@ -109,7 +108,7 @@ export function updateConfigWithConnector(
109
108
  const closingBraceIdx = content.lastIndexOf("}");
110
109
  if (closingBraceIdx === -1) {
111
110
  console.error("āŒ Could not parse config file structure. Please add the connector manually:");
112
- console.log(formatConnectorSnippet(connectorName, connectorDef, options));
111
+ console.log(formatConnectorSnippet(connectorName, connectorDef));
113
112
  return;
114
113
  }
115
114
 
@@ -117,7 +116,7 @@ export function updateConfigWithConnector(
117
116
  const beforeClosing = content.substring(0, closingBraceIdx).trimEnd();
118
117
  const needsComma = !beforeClosing.endsWith(",") && !beforeClosing.endsWith("{");
119
118
 
120
- const connectorBlock = generateConnectorJSBlock(connectorName, connectorDef, options);
119
+ const connectorBlock = generateConnectorJSBlock(connectorName, connectorDef);
121
120
  const insertion = `${needsComma ? "," : ""}\n // ć‚³ćƒć‚Æć‚æå®šē¾©\n connectors: {\n${connectorBlock}\n },\n`;
122
121
 
123
122
  const newContent = content.substring(0, closingBraceIdx) + insertion + content.substring(closingBraceIdx);
@@ -125,16 +124,16 @@ export function updateConfigWithConnector(
125
124
  console.log(`āœ… Updated: ${configPath}`);
126
125
  }
127
126
 
128
- function generateConnectorJSBlock(name: string, def: ConnectorDefinition, options: AddConnectorOptions): string {
127
+ function generateConnectorJSBlock(name: string, def: ConnectorDefinition): string {
129
128
  if (def.type === "rdb") {
130
129
  return ` ${name}: {
131
130
  type: 'rdb',
132
- provider: '${(def as any).provider}',
131
+ provider: '${def.provider}',
133
132
  connectionEnvVar: '${def.connectionEnvVar}',
134
133
  },`;
135
134
  }
136
135
 
137
- const apiDef = def as any;
136
+ const apiDef: ApiConnectorConfig = def;
138
137
  let authBlock = "";
139
138
  if (apiDef.auth) {
140
139
  authBlock = `
@@ -152,6 +151,6 @@ function generateConnectorJSBlock(name: string, def: ConnectorDefinition, option
152
151
  },`;
153
152
  }
154
153
 
155
- function formatConnectorSnippet(name: string, def: ConnectorDefinition, options: AddConnectorOptions): string {
154
+ function formatConnectorSnippet(name: string, def: ConnectorDefinition): string {
156
155
  return `\n ${name}: ${JSON.stringify(def, null, 4)}\n`;
157
156
  }
@@ -3,15 +3,16 @@ import { spawn, ChildProcess } from 'child_process';
3
3
  import * as path from 'path';
4
4
  import * as fs from 'fs';
5
5
  import * as os from 'os';
6
+ import * as net from 'net';
6
7
  import { CosmosClient, PartitionKeyKind } from '@azure/cosmos';
7
- import { ensureSwallowKitProject, getBackendLanguage, getAuthConfig, getFullConfig } from '../../core/config';
8
+ import { ensureSwallowKitProject, getBackendLanguage, getAuthConfig } from '../../core/config';
8
9
  import { ModelInfo } from '../../core/scaffold/model-parser';
9
10
  import { applyDevSeedEnvironment, getContainerNameForModel, loadProjectModels } from './dev-seeds';
10
11
  import { BackendLanguage } from '../../types';
11
12
  import { detectFromProject, getCommands } from '../../utils/package-manager';
12
13
  import { ConnectorMockServer } from '../../core/mock/connector-mock-server';
13
14
 
14
- interface DevOptions {
15
+ export interface DevOptions {
15
16
  port?: string;
16
17
  functionsPort?: string;
17
18
  host?: string;
@@ -22,6 +23,17 @@ interface DevOptions {
22
23
  mockConnectors?: boolean;
23
24
  }
24
25
 
26
+ type ParsedDevActionOptions = DevOptions & {
27
+ functions?: boolean;
28
+ };
29
+
30
+ function normalizeParsedDevOptions(options: ParsedDevActionOptions): DevOptions {
31
+ return {
32
+ ...options,
33
+ noFunctions: options.noFunctions ?? options.functions === false,
34
+ };
35
+ }
36
+
25
37
  export function buildFunctionsStartArgs(functionsPort: string): string[] {
26
38
  return ['start', '--port', functionsPort];
27
39
  }
@@ -292,7 +304,6 @@ async function preparePythonFunctionsEnvironment(functionsDir: string): Promise<
292
304
  */
293
305
  async function checkCosmosDBEmulator(): Promise<boolean> {
294
306
  return new Promise((resolve) => {
295
- const net = require('net');
296
307
  const socket = new net.Socket();
297
308
 
298
309
  const timeout = setTimeout(() => {
@@ -315,28 +326,37 @@ async function checkCosmosDBEmulator(): Promise<boolean> {
315
326
  });
316
327
  }
317
328
 
318
- export const devCommand = new Command()
319
- .name('dev')
320
- .description('Start SwallowKit development server (Cosmos DB + Next.js + Azure Functions)')
321
- .option('-p, --port <port>', 'Next.js port', '3000')
322
- .option('-f, --functions-port <port>', 'Azure Functions port', '7071')
323
- .option('--host <host>', 'Host name', 'localhost')
324
- .option('--open', 'Open browser automatically', false)
325
- .option('--verbose', 'Show verbose logs', false)
326
- .option('--no-functions', 'Skip Azure Functions startup', false)
327
- .option('--seed-env <environment>', 'Replace Cosmos DB Emulator data from dev-seeds/<environment> before startup')
328
- .option('--mock-connectors', 'Start mock server for connector models (serves Zod-generated data)', false)
329
- .action(async (options: DevOptions & { functionsPort?: string; noFunctions?: boolean; seedEnv?: string; mockConnectors?: boolean }) => {
330
- // SwallowKit ćƒ—ćƒ­ć‚øć‚§ć‚Æćƒˆćƒ‡ć‚£ćƒ¬ć‚ÆćƒˆćƒŖć‹ć©ć†ć‹ć‚’ę¤œčØ¼
331
- ensureSwallowKitProject("dev");
332
-
333
- console.log('šŸš€ Starting SwallowKit development environment...');
334
- if (options.verbose) {
335
- console.log('āš™ļø Options:', options);
336
- }
329
+ export function buildDevCommand(
330
+ runDevEnvironment: (options: DevOptions) => Promise<void> = startDevEnvironment,
331
+ verifyProject: (commandName: string, projectRoot?: string) => void = ensureSwallowKitProject
332
+ ): Command {
333
+ return new Command()
334
+ .name('dev')
335
+ .description('Start SwallowKit development server (Cosmos DB + Next.js + Azure Functions)')
336
+ .option('-p, --port <port>', 'Next.js port', '3000')
337
+ .option('-f, --functions-port <port>', 'Azure Functions port', '7071')
338
+ .option('--host <host>', 'Host name', 'localhost')
339
+ .option('--open', 'Open browser automatically', false)
340
+ .option('--verbose', 'Show verbose logs', false)
341
+ .option('--no-functions', 'Skip Azure Functions startup', false)
342
+ .option('--seed-env <environment>', 'Replace Cosmos DB Emulator data from dev-seeds/<environment> before startup')
343
+ .option('--mock-connectors', 'Start mock server for connector models (serves Zod-generated data)', false)
344
+ .action(async (options: ParsedDevActionOptions) => {
345
+ const normalizedOptions = normalizeParsedDevOptions(options);
346
+
347
+ // SwallowKit ćƒ—ćƒ­ć‚øć‚§ć‚Æćƒˆćƒ‡ć‚£ćƒ¬ć‚ÆćƒˆćƒŖć‹ć©ć†ć‹ć‚’ę¤œčØ¼
348
+ verifyProject("dev");
349
+
350
+ console.log('šŸš€ Starting SwallowKit development environment...');
351
+ if (normalizedOptions.verbose) {
352
+ console.log('āš™ļø Options:', normalizedOptions);
353
+ }
337
354
 
338
- await startDevEnvironment(options);
339
- });
355
+ await runDevEnvironment(normalizedOptions);
356
+ });
357
+ }
358
+
359
+ export const devCommand = buildDevCommand();
340
360
 
341
361
  interface CosmosInitializationResult {
342
362
  endpoint: string;
@@ -749,7 +769,6 @@ async function startDevEnvironment(options: DevOptions) {
749
769
  const authConfig = getAuthConfig();
750
770
  let mockAuthConfig: { jwtSecret: string; tokenExpiry?: string; customJwt?: { userTable: string; loginIdColumn: string; passwordHashColumn: string; rolesColumn: string }; defaultPolicy?: "authenticated" | "anonymous" } | undefined;
751
771
  if (authConfig?.provider === 'custom-jwt' && authConfig.customJwt) {
752
- const fullConfig = getFullConfig();
753
772
  // Read JWT_SECRET from functions/local.settings.json if available
754
773
  let jwtSecret = 'dev-jwt-secret-change-in-production-min-32-chars!!';
755
774
  try {
@@ -1806,7 +1806,7 @@ npx swallowkit scaffold shared/models/<name>.ts
1806
1806
  \`\`\`
1807
1807
 
1808
1808
  Generates:
1809
- - Azure Functions handlers (${backendLanguage === 'typescript' ? '\`functions/src/<name>.ts\`' : '\`functions/\` language-specific CRUD files + \`functions/generated/\` schema assets'})
1809
+ - Azure Functions handlers (${backendLanguage === 'typescript' ? '`functions/src/<name>.ts`' : '`functions/` language-specific CRUD files + `functions/generated/` schema assets'})
1810
1810
  - BFF API routes (\`app/api/<name>/route.ts\`, \`app/api/<name>/[id]/route.ts\`)
1811
1811
  - UI pages (\`app/<name>/page.tsx\`, detail, create, edit pages)
1812
1812
  - Cosmos DB Bicep container config (\`infra/containers/<name>-container.bicep\`)
@@ -3223,8 +3223,6 @@ async function createGitHubActionsWorkflows(
3223
3223
  backendLanguage: BackendLanguage
3224
3224
  ) {
3225
3225
  console.log('šŸ“¦ Creating GitHub Actions workflows...\n');
3226
-
3227
- const pmCmd = getCommands(pm);
3228
3226
  const workflowsDir = path.join(projectDir, '.github', 'workflows');
3229
3227
  fs.mkdirSync(workflowsDir, { recursive: true });
3230
3228
 
@@ -20,7 +20,6 @@ import {
20
20
  generateApiConnectorFunctionCSharp,
21
21
  generateRdbConnectorFunctionPython,
22
22
  generateApiConnectorFunctionPython,
23
- isReadOnlyConnector,
24
23
  } from "../../core/scaffold/connector-functions-generator";
25
24
  import { generateCompactBFFRoutes, generateBFFCallFunction, generateConnectorBFFRoutes } from "../../core/scaffold/nextjs-generator";
26
25
  import { generateOpenApiDocument } from "../../core/scaffold/openapi-generator";
@@ -508,7 +507,6 @@ async function installConnectorDriverDependencies(
508
507
  const pm = detectFromProject(functionsPath);
509
508
  const cmds = getCommands(pm);
510
509
 
511
- const { spawnSync } = require("child_process");
512
510
  if (entry.deps.length > 0) {
513
511
  spawnSync(cmds.name, [pm === "pnpm" ? "add" : "install", ...entry.deps], {
514
512
  cwd: functionsPath, stdio: "inherit", shell: true,
@@ -532,7 +530,6 @@ async function installConnectorDriverDependencies(
532
530
  const pkg = nugetMap[rdbDef.provider];
533
531
  if (!pkg) return;
534
532
  console.log(`\nšŸ“¦ Installing ${rdbDef.provider} NuGet package...`);
535
- const { spawnSync } = require("child_process");
536
533
  spawnSync("dotnet", ["add", path.join(functionsPath, "functions.csproj"), "package", pkg], {
537
534
  cwd: functionsPath, stdio: "inherit", shell: true,
538
535
  });
@@ -867,7 +864,6 @@ async function generateBFFRoutes(
867
864
  ): Promise<void> {
868
865
  console.log("\nšŸ”Ø Generating Next.js BFF API routes...");
869
866
 
870
- const modelKebab = toKebabCase(modelInfo.name);
871
867
  const modelCamel = modelInfo.name.charAt(0).toLowerCase() + modelInfo.name.slice(1);
872
868
 
873
869
  // List route: app/api/[model]/route.ts
package/src/cli/index.ts CHANGED
@@ -1,101 +1,185 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- // Ensure UTF-8 console output on Windows (fixes emoji/Unicode garbling in PowerShell 5.1)
4
- if (process.platform === 'win32') {
5
- try {
6
- require('child_process').execSync('chcp 65001', { stdio: 'ignore' });
7
- } catch { /* ignore — non-critical */ }
8
- }
9
-
3
+ import { execSync } from "child_process";
10
4
  import { Command } from "commander";
11
5
  import { initCommand, devCommand, devSeedsCommand, scaffoldCommand, createModelCommand } from "./commands";
12
6
  import { provisionCommand } from "./commands/provision";
13
7
  import { addConnectorCommand } from "./commands/add-connector";
14
8
  import { addAuthCommand } from "./commands/add-auth";
15
9
 
16
- const program = new Command();
17
-
18
- program
19
- .name("swallowkit")
20
- .description("Next.js framework optimized for Azure deployment - Automatically splits SSR into individual Azure Functions")
21
- .version("1.0.0-beta.9");
22
-
23
- // Register commands
24
- program
25
- .command("init [project-name]")
26
- .description("Initialize a new SwallowKit project")
27
- .option("--template <template>", "Template to use", "default")
28
- .option("--next-version <version>", "Next.js version to install (e.g., 16.0.7, latest)", "latest")
29
- .option("--cicd <provider>", "CI/CD provider: github | azure | skip")
30
- .option("--backend-language <language>", "Azure Functions backend language: typescript | csharp | python")
31
- .option("--cosmos-db-mode <mode>", "Cosmos DB mode: freetier | serverless")
32
- .option("--vnet <option>", "Network security: outbound | none")
33
- .action((projectName, options) => {
34
- initCommand({
35
- name: projectName || "swallowkit-app",
36
- template: options.template,
37
- nextVersion: options.nextVersion,
38
- cicd: options.cicd,
39
- backendLanguage: options.backendLanguage,
40
- cosmosDbMode: options.cosmosDbMode,
41
- vnet: options.vnet,
10
+ const DEV_OPTION_ARITY = new Map<string, 0 | 1>([
11
+ ["-p", 1],
12
+ ["--port", 1],
13
+ ["-f", 1],
14
+ ["--functions-port", 1],
15
+ ["--host", 1],
16
+ ["--seed-env", 1],
17
+ ["-o", 0],
18
+ ["--open", 0],
19
+ ["-v", 0],
20
+ ["--verbose", 0],
21
+ ["--no-functions", 0],
22
+ ["--mock-connectors", 0],
23
+ ]);
24
+
25
+ function ensureUtf8ConsoleOnWindows(): void {
26
+ if (process.platform === 'win32') {
27
+ try {
28
+ execSync('chcp 65001', { stdio: 'ignore' });
29
+ } catch {
30
+ // ignore — non-critical
31
+ }
32
+ }
33
+ }
34
+
35
+ function isDevOptionSequence(tokens: string[]): boolean {
36
+ if (tokens.length === 0) {
37
+ return false;
38
+ }
39
+
40
+ for (let index = 0; index < tokens.length; index += 1) {
41
+ const token = tokens[index];
42
+ const arity = DEV_OPTION_ARITY.get(token);
43
+
44
+ if (arity === undefined) {
45
+ return false;
46
+ }
47
+
48
+ if (arity === 1) {
49
+ const value = tokens[index + 1];
50
+ if (value === undefined || value === "dev") {
51
+ return false;
52
+ }
53
+ index += 1;
54
+ }
55
+ }
56
+
57
+ return true;
58
+ }
59
+
60
+ export function normalizeDevCommandArgv(argv: string[]): string[] {
61
+ if (argv.length <= 3) {
62
+ return argv;
63
+ }
64
+
65
+ const prefix = argv.slice(0, 2);
66
+ const args = argv.slice(2);
67
+ const devIndex = args.indexOf("dev");
68
+
69
+ if (devIndex <= 0) {
70
+ return argv;
71
+ }
72
+
73
+ const leadingTokens = args.slice(0, devIndex);
74
+ if (!isDevOptionSequence(leadingTokens)) {
75
+ return argv;
76
+ }
77
+
78
+ return [
79
+ ...prefix,
80
+ "dev",
81
+ ...leadingTokens,
82
+ ...args.slice(devIndex + 1),
83
+ ];
84
+ }
85
+
86
+ export function createProgram(devCommandOverride: Command = devCommand): Command {
87
+ const program = new Command();
88
+
89
+ program
90
+ .name("swallowkit")
91
+ .description("Next.js framework optimized for Azure deployment - Automatically splits SSR into individual Azure Functions")
92
+ .version("1.0.0-beta.9");
93
+
94
+ // Register commands
95
+ program
96
+ .command("init [project-name]")
97
+ .description("Initialize a new SwallowKit project")
98
+ .option("--template <template>", "Template to use", "default")
99
+ .option("--next-version <version>", "Next.js version to install (e.g., 16.0.7, latest)", "latest")
100
+ .option("--cicd <provider>", "CI/CD provider: github | azure | skip")
101
+ .option("--backend-language <language>", "Azure Functions backend language: typescript | csharp | python")
102
+ .option("--cosmos-db-mode <mode>", "Cosmos DB mode: freetier | serverless")
103
+ .option("--vnet <option>", "Network security: outbound | none")
104
+ .action((projectName, options) => {
105
+ initCommand({
106
+ name: projectName || "swallowkit-app",
107
+ template: options.template,
108
+ nextVersion: options.nextVersion,
109
+ cicd: options.cicd,
110
+ backendLanguage: options.backendLanguage,
111
+ cosmosDbMode: options.cosmosDbMode,
112
+ vnet: options.vnet,
113
+ });
42
114
  });
43
- });
44
115
 
45
- program.addCommand(devCommand);
46
- program.addCommand(devSeedsCommand);
47
-
48
- program.addCommand(provisionCommand);
49
-
50
- program
51
- .command("create-model <names...>")
52
- .description("Create model template files with id, createdAt, and updatedAt fields")
53
- .option("--models-dir <dir>", "Models directory", "shared/models")
54
- .option("--connector <name>", "Associate model with a connector defined in swallowkit.config.js")
55
- .action((names, options) => {
56
- createModelCommand({
57
- names,
58
- modelsDir: options.modelsDir,
59
- connector: options.connector,
116
+ program.addCommand(devCommandOverride);
117
+ program.addCommand(devSeedsCommand);
118
+
119
+ program.addCommand(provisionCommand);
120
+
121
+ program
122
+ .command("create-model <names...>")
123
+ .description("Create model template files with id, createdAt, and updatedAt fields")
124
+ .option("--models-dir <dir>", "Models directory", "shared/models")
125
+ .option("--connector <name>", "Associate model with a connector defined in swallowkit.config.js")
126
+ .action((names, options) => {
127
+ createModelCommand({
128
+ names,
129
+ modelsDir: options.modelsDir,
130
+ connector: options.connector,
131
+ });
60
132
  });
61
- });
62
133
 
63
- program
64
- .command("scaffold <model>")
65
- .description("Generate CRUD code for Azure Functions and Next.js BFF from Zod models")
66
- .option("--functions-dir <dir>", "Azure Functions directory", "functions")
67
- .option("--api-dir <dir>", "Next.js API routes directory", "app/api")
68
- .option("--api-only", "Generate API only, skip UI components", false)
69
- .action((model, options) => {
70
- scaffoldCommand({
71
- model,
72
- functionsDir: options.functionsDir,
73
- apiDir: options.apiDir,
74
- apiOnly: options.apiOnly,
134
+ program
135
+ .command("scaffold <model>")
136
+ .description("Generate CRUD code for Azure Functions and Next.js BFF from Zod models")
137
+ .option("--functions-dir <dir>", "Azure Functions directory", "functions")
138
+ .option("--api-dir <dir>", "Next.js API routes directory", "app/api")
139
+ .option("--api-only", "Generate API only, skip UI components", false)
140
+ .action((model, options) => {
141
+ scaffoldCommand({
142
+ model,
143
+ functionsDir: options.functionsDir,
144
+ apiDir: options.apiDir,
145
+ apiOnly: options.apiOnly,
146
+ });
75
147
  });
76
- });
77
148
 
78
- program
79
- .command("add-connector <name>")
80
- .description("Add an external data source connector configuration")
81
- .requiredOption("--type <type>", "Connector type: rdb | api")
82
- .option("--provider <provider>", "RDB provider: mysql | postgres | sqlserver", "mysql")
83
- .action((name, options) => {
84
- addConnectorCommand({
85
- name,
86
- type: options.type,
87
- provider: options.provider,
149
+ program
150
+ .command("add-connector <name>")
151
+ .description("Add an external data source connector configuration")
152
+ .requiredOption("--type <type>", "Connector type: rdb | api")
153
+ .option("--provider <provider>", "RDB provider: mysql | postgres | sqlserver", "mysql")
154
+ .action((name, options) => {
155
+ addConnectorCommand({
156
+ name,
157
+ type: options.type,
158
+ provider: options.provider,
159
+ });
88
160
  });
89
- });
90
161
 
91
- program
92
- .command("add-auth")
93
- .description("Add authentication and authorization to the project")
94
- .option("--provider <provider>", "Auth provider: custom-jwt | swa | swa-custom | none", "custom-jwt")
95
- .action((options) => {
96
- addAuthCommand({
97
- provider: options.provider,
162
+ program
163
+ .command("add-auth")
164
+ .description("Add authentication and authorization to the project")
165
+ .option("--provider <provider>", "Auth provider: custom-jwt | swa | swa-custom | none", "custom-jwt")
166
+ .action((options) => {
167
+ addAuthCommand({
168
+ provider: options.provider,
169
+ });
98
170
  });
99
- });
100
171
 
101
- program.parse();
172
+ return program;
173
+ }
174
+
175
+ export async function runCli(argv: string[] = process.argv): Promise<void> {
176
+ ensureUtf8ConsoleOnWindows();
177
+ await createProgram().parseAsync(normalizeDevCommandArgv(argv));
178
+ }
179
+
180
+ if (require.main === module) {
181
+ void runCli().catch((error) => {
182
+ console.error(error);
183
+ process.exitCode = 1;
184
+ });
185
+ }
@@ -1,8 +1,20 @@
1
1
  import * as fs from "fs";
2
+ import { createRequire } from "module";
2
3
  import * as path from "path";
3
4
  import { AuthConfig, BackendLanguage, ConnectorDefinition, SwallowKitConfig } from "../types";
4
5
  import { detectFromProject, getCommands } from "../utils/package-manager";
5
6
 
7
+ const requireFromHere = createRequire(__filename);
8
+
9
+ function unwrapConfigModule(
10
+ loadedConfig: Partial<SwallowKitConfig> | { default?: Partial<SwallowKitConfig> }
11
+ ): Partial<SwallowKitConfig> {
12
+ if (typeof loadedConfig === "object" && loadedConfig !== null && "default" in loadedConfig) {
13
+ return loadedConfig.default ?? {};
14
+ }
15
+ return loadedConfig as Partial<SwallowKitConfig>;
16
+ }
17
+
6
18
  const VALID_BACKEND_LANGUAGES: BackendLanguage[] = ["typescript", "csharp", "python"];
7
19
 
8
20
  /**
@@ -46,9 +58,9 @@ export function loadConfig(configPath?: string): SwallowKitConfig {
46
58
  const userConfig = JSON.parse(configData);
47
59
  return mergeConfig(DEFAULT_CONFIG, userConfig);
48
60
  } else if (filePath.endsWith(".js")) {
49
- delete require.cache[fullPath];
50
- const userConfig = require(fullPath);
51
- return mergeConfig(DEFAULT_CONFIG, userConfig.default || userConfig);
61
+ delete requireFromHere.cache[fullPath];
62
+ const loadedConfig = requireFromHere(fullPath) as Partial<SwallowKitConfig> | { default?: Partial<SwallowKitConfig> };
63
+ return mergeConfig(DEFAULT_CONFIG, unwrapConfigModule(loadedConfig));
52
64
  }
53
65
  } catch (error) {
54
66
  console.warn(`āš ļø čØ­å®šćƒ•ć‚”ć‚¤ćƒ«ć®čŖ­ćæč¾¼ćæć«å¤±ę•—: ${filePath}`, error);
@@ -5,6 +5,7 @@
5
5
  */
6
6
 
7
7
  import * as http from "http";
8
+ import jwt, { SignOptions } from "jsonwebtoken";
8
9
  import { ModelInfo, toCamelCase } from "../scaffold/model-parser";
9
10
  import { generateMockDocuments } from "./zod-mock-generator";
10
11
  import { loadDevSeedFiles } from "../../cli/commands/dev-seeds";
@@ -240,15 +241,17 @@ export class ConnectorMockServer {
240
241
  return;
241
242
 
242
243
  case "DELETE":
243
- req.resume();
244
- if (!ops.includes("delete")) {
245
- return this.sendJson(res, 405, { error: "delete not supported" });
244
+ {
245
+ req.resume();
246
+ if (!ops.includes("delete")) {
247
+ return this.sendJson(res, 405, { error: "delete not supported" });
248
+ }
249
+ if (!id) return this.sendJson(res, 400, { error: "id required" });
250
+ const deleteIdx = store.findIndex((doc) => doc.id === id);
251
+ if (deleteIdx === -1) return this.sendJson(res, 404, { error: "Item not found" });
252
+ store.splice(deleteIdx, 1);
253
+ return this.sendJson(res, 204, null);
246
254
  }
247
- if (!id) return this.sendJson(res, 400, { error: "id required" });
248
- const deleteIdx = store.findIndex((doc) => doc.id === id);
249
- if (deleteIdx === -1) return this.sendJson(res, 404, { error: "Item not found" });
250
- store.splice(deleteIdx, 1);
251
- return this.sendJson(res, 204, null);
252
255
 
253
256
  default:
254
257
  req.resume();
@@ -376,9 +379,8 @@ export class ConnectorMockServer {
376
379
  };
377
380
 
378
381
  // Generate JWT
379
- const jwt = require("jsonwebtoken");
380
382
  const secret = this.options.authConfig!.jwtSecret;
381
- const expiry = this.options.authConfig!.tokenExpiry || "24h";
383
+ const expiry = (this.options.authConfig!.tokenExpiry || "24h") as SignOptions["expiresIn"];
382
384
 
383
385
  const token = jwt.sign(
384
386
  {
@@ -408,7 +410,6 @@ export class ConnectorMockServer {
408
410
 
409
411
  const token = authHeader.slice(7);
410
412
  try {
411
- const jwt = require("jsonwebtoken");
412
413
  const secret = this.options.authConfig!.jwtSecret;
413
414
  const payload = jwt.verify(token, secret) as Record<string, unknown>;
414
415
  this.sendJson(res, 200, {
@@ -470,7 +471,6 @@ export class ConnectorMockServer {
470
471
 
471
472
  const token = authHeader.slice(7);
472
473
  try {
473
- const jwt = require("jsonwebtoken");
474
474
  const payload = jwt.verify(token, authCfg.jwtSecret) as Record<string, unknown>;
475
475
 
476
476
  // ćƒ­ćƒ¼ćƒ«ćƒć‚§ćƒƒć‚Æ
@@ -3,7 +3,7 @@
3
3
  * add-auth ć‚³ćƒžćƒ³ćƒ‰ćŠć‚ˆć³ scaffold ćƒ­ćƒ¼ćƒ«ć‚¬ćƒ¼ćƒ‰ęŒæå…„ć§ä½æē”Øć™ć‚‹ćƒ†ćƒ³ćƒ—ćƒ¬ćƒ¼ćƒˆē¾¤
4
4
  */
5
5
 
6
- import { CustomJwtConfig, ModelAuthPolicy, AuthConfig, RdbConnectorConfig } from "../../types";
6
+ import { CustomJwtConfig, ModelAuthPolicy, RdbConnectorConfig } from "../../types";
7
7
 
8
8
  type RdbProvider = RdbConnectorConfig["provider"]; // "mysql" | "postgres" | "sqlserver"
9
9
 
@@ -816,7 +816,8 @@ export async function POST() {
816
816
  `;
817
817
  }
818
818
 
819
- export function generateBFFAuthMeRoute(sharedPackageName: string): string {
819
+ export function generateBFFAuthMeRoute(_sharedPackageName?: string): string {
820
+ void _sharedPackageName;
820
821
  return `import { NextResponse } from 'next/server';
821
822
  import { headers } from 'next/headers';
822
823
 
@@ -5,7 +5,6 @@
5
5
 
6
6
  import { ModelInfo, toCamelCase, toKebabCase } from "./model-parser";
7
7
  import {
8
- ConnectorDefinition,
9
8
  RdbConnectorConfig,
10
9
  ApiConnectorConfig,
11
10
  RdbModelConnectorConfig,
@@ -125,7 +124,6 @@ export function generateRdbConnectorFunctionTS(
125
124
  const hasAuth = !!authPolicy;
126
125
  const authImport = hasAuth ? `\n${generateAuthImportTS()}\n` : '';
127
126
  const readGuard = hasAuth ? `\n${generateAuthGuardTS(authPolicy!, 'read')}\n` : '';
128
- const writeGuard = hasAuth ? `\n${generateAuthGuardTS(authPolicy!, 'write')}\n` : '';
129
127
  const authCatchBlock = hasAuth
130
128
  ? ` const authErr = handleAuthError(error);
131
129
  if (authErr) return authErr;\n `