swallowkit 1.0.0-beta.8 → 1.0.0
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 +87 -181
- package/README.md +87 -238
- package/dist/__tests__/fixtures.d.ts +12 -0
- package/dist/__tests__/fixtures.d.ts.map +1 -1
- package/dist/__tests__/fixtures.js +101 -0
- package/dist/__tests__/fixtures.js.map +1 -1
- package/dist/cli/commands/add-auth.d.ts +15 -0
- package/dist/cli/commands/add-auth.d.ts.map +1 -0
- package/dist/cli/commands/add-auth.js +790 -0
- package/dist/cli/commands/add-auth.js.map +1 -0
- package/dist/cli/commands/add-connector.d.ts +20 -0
- package/dist/cli/commands/add-connector.d.ts.map +1 -0
- package/dist/cli/commands/add-connector.js +164 -0
- package/dist/cli/commands/add-connector.js.map +1 -0
- package/dist/cli/commands/cosmos-worktree-isolation.d.ts +25 -0
- package/dist/cli/commands/cosmos-worktree-isolation.d.ts.map +1 -0
- package/dist/cli/commands/cosmos-worktree-isolation.js +124 -0
- package/dist/cli/commands/cosmos-worktree-isolation.js.map +1 -0
- package/dist/cli/commands/create-model.d.ts +1 -4
- package/dist/cli/commands/create-model.d.ts.map +1 -1
- package/dist/cli/commands/create-model.js +20 -82
- package/dist/cli/commands/create-model.js.map +1 -1
- package/dist/cli/commands/dev-seeds.d.ts +27 -0
- package/dist/cli/commands/dev-seeds.d.ts.map +1 -1
- package/dist/cli/commands/dev-seeds.js +188 -6
- package/dist/cli/commands/dev-seeds.js.map +1 -1
- package/dist/cli/commands/dev.d.ts +30 -0
- package/dist/cli/commands/dev.d.ts.map +1 -1
- package/dist/cli/commands/dev.js +495 -129
- package/dist/cli/commands/dev.js.map +1 -1
- package/dist/cli/commands/init.d.ts +70 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +797 -768
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/scaffold.d.ts +24 -3
- package/dist/cli/commands/scaffold.d.ts.map +1 -1
- package/dist/cli/commands/scaffold.js +408 -189
- package/dist/cli/commands/scaffold.js.map +1 -1
- package/dist/cli/index.d.ts +5 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +335 -58
- package/dist/cli/index.js.map +1 -1
- package/dist/core/config.d.ts +13 -3
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +151 -8
- package/dist/core/config.js.map +1 -1
- package/dist/core/mock/connector-mock-server.d.ts +101 -0
- package/dist/core/mock/connector-mock-server.d.ts.map +1 -0
- package/dist/core/mock/connector-mock-server.js +480 -0
- package/dist/core/mock/connector-mock-server.js.map +1 -0
- package/dist/core/mock/zod-mock-generator.d.ts +14 -0
- package/dist/core/mock/zod-mock-generator.d.ts.map +1 -0
- package/dist/core/mock/zod-mock-generator.js +163 -0
- package/dist/core/mock/zod-mock-generator.js.map +1 -0
- package/dist/core/operations/auth-operations.d.ts +61 -0
- package/dist/core/operations/auth-operations.d.ts.map +1 -0
- package/dist/core/operations/auth-operations.js +272 -0
- package/dist/core/operations/auth-operations.js.map +1 -0
- package/dist/core/operations/create-model.d.ts +15 -0
- package/dist/core/operations/create-model.d.ts.map +1 -0
- package/dist/core/operations/create-model.js +185 -0
- package/dist/core/operations/create-model.js.map +1 -0
- package/dist/core/operations/file-session.d.ts +56 -0
- package/dist/core/operations/file-session.d.ts.map +1 -0
- package/dist/core/operations/file-session.js +203 -0
- package/dist/core/operations/file-session.js.map +1 -0
- package/dist/core/operations/provision-operations.d.ts +52 -0
- package/dist/core/operations/provision-operations.d.ts.map +1 -0
- package/dist/core/operations/provision-operations.js +211 -0
- package/dist/core/operations/provision-operations.js.map +1 -0
- package/dist/core/operations/runtime.d.ts +37 -0
- package/dist/core/operations/runtime.d.ts.map +1 -0
- package/dist/core/operations/runtime.js +248 -0
- package/dist/core/operations/runtime.js.map +1 -0
- package/dist/core/operations/scaffold-apply.d.ts +21 -0
- package/dist/core/operations/scaffold-apply.d.ts.map +1 -0
- package/dist/core/operations/scaffold-apply.js +57 -0
- package/dist/core/operations/scaffold-apply.js.map +1 -0
- package/dist/core/operations/scaffold-machine.d.ts +16 -0
- package/dist/core/operations/scaffold-machine.d.ts.map +1 -0
- package/dist/core/operations/scaffold-machine.js +51 -0
- package/dist/core/operations/scaffold-machine.js.map +1 -0
- package/dist/core/operations/scaffold-plan.d.ts +43 -0
- package/dist/core/operations/scaffold-plan.d.ts.map +1 -0
- package/dist/core/operations/scaffold-plan.js +187 -0
- package/dist/core/operations/scaffold-plan.js.map +1 -0
- 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 +160 -0
- package/dist/core/project/agent-instructions.js.map +1 -0
- package/dist/core/project/artifacts.d.ts +58 -0
- package/dist/core/project/artifacts.d.ts.map +1 -0
- package/dist/core/project/artifacts.js +179 -0
- package/dist/core/project/artifacts.js.map +1 -0
- package/dist/core/project/boundaries.d.ts +50 -0
- package/dist/core/project/boundaries.d.ts.map +1 -0
- package/dist/core/project/boundaries.js +117 -0
- package/dist/core/project/boundaries.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/drift.d.ts +29 -0
- package/dist/core/project/drift.d.ts.map +1 -0
- package/dist/core/project/drift.js +188 -0
- package/dist/core/project/drift.js.map +1 -0
- package/dist/core/project/infra.d.ts +49 -0
- package/dist/core/project/infra.d.ts.map +1 -0
- package/dist/core/project/infra.js +167 -0
- package/dist/core/project/infra.js.map +1 -0
- package/dist/core/project/manifest.d.ts +92 -0
- package/dist/core/project/manifest.d.ts.map +1 -0
- package/dist/core/project/manifest.js +321 -0
- package/dist/core/project/manifest.js.map +1 -0
- package/dist/core/project/state.d.ts +15 -0
- package/dist/core/project/state.d.ts.map +1 -0
- package/dist/core/project/state.js +106 -0
- package/dist/core/project/state.js.map +1 -0
- package/dist/core/project/validation.d.ts +20 -0
- package/dist/core/project/validation.d.ts.map +1 -0
- package/dist/core/project/validation.js +274 -0
- package/dist/core/project/validation.js.map +1 -0
- package/dist/core/project/workflows.d.ts +43 -0
- package/dist/core/project/workflows.d.ts.map +1 -0
- package/dist/core/project/workflows.js +299 -0
- package/dist/core/project/workflows.js.map +1 -0
- package/dist/core/scaffold/auth-generator.d.ts +63 -0
- package/dist/core/scaffold/auth-generator.d.ts.map +1 -0
- package/dist/core/scaffold/auth-generator.js +1774 -0
- package/dist/core/scaffold/auth-generator.js.map +1 -0
- package/dist/core/scaffold/connector-functions-generator.d.ts +41 -0
- package/dist/core/scaffold/connector-functions-generator.d.ts.map +1 -0
- package/dist/core/scaffold/connector-functions-generator.js +1029 -0
- package/dist/core/scaffold/connector-functions-generator.js.map +1 -0
- package/dist/core/scaffold/functions-generator.d.ts +4 -3
- package/dist/core/scaffold/functions-generator.d.ts.map +1 -1
- package/dist/core/scaffold/functions-generator.js +405 -134
- package/dist/core/scaffold/functions-generator.js.map +1 -1
- package/dist/core/scaffold/model-parser.d.ts +20 -0
- package/dist/core/scaffold/model-parser.d.ts.map +1 -1
- package/dist/core/scaffold/model-parser.js +229 -78
- package/dist/core/scaffold/model-parser.js.map +1 -1
- package/dist/core/scaffold/native-schema-generator.d.ts +19 -0
- package/dist/core/scaffold/native-schema-generator.d.ts.map +1 -0
- package/dist/core/scaffold/native-schema-generator.js +753 -0
- package/dist/core/scaffold/native-schema-generator.js.map +1 -0
- package/dist/core/scaffold/nextjs-generator.d.ts +8 -0
- package/dist/core/scaffold/nextjs-generator.d.ts.map +1 -1
- package/dist/core/scaffold/nextjs-generator.js +152 -8
- package/dist/core/scaffold/nextjs-generator.js.map +1 -1
- package/dist/core/scaffold/ui-generator.d.ts +17 -4
- package/dist/core/scaffold/ui-generator.d.ts.map +1 -1
- package/dist/core/scaffold/ui-generator.js +174 -34
- package/dist/core/scaffold/ui-generator.js.map +1 -1
- package/dist/core/verify/index.d.ts +69 -0
- package/dist/core/verify/index.d.ts.map +1 -0
- package/dist/core/verify/index.js +412 -0
- package/dist/core/verify/index.js.map +1 -0
- package/dist/machine/contracts.d.ts +24 -0
- package/dist/machine/contracts.d.ts.map +1 -0
- package/dist/machine/contracts.js +3 -0
- package/dist/machine/contracts.js.map +1 -0
- package/dist/machine/errors.d.ts +14 -0
- package/dist/machine/errors.d.ts.map +1 -0
- package/dist/machine/errors.js +42 -0
- package/dist/machine/errors.js.map +1 -0
- package/dist/machine/index.d.ts +3 -0
- package/dist/machine/index.d.ts.map +1 -0
- package/dist/machine/index.js +566 -0
- package/dist/machine/index.js.map +1 -0
- package/dist/mcp/index.d.ts +25 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +425 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/types/index.d.ts +105 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/package-manager.d.ts +7 -11
- package/dist/utils/package-manager.d.ts.map +1 -1
- package/dist/utils/package-manager.js +42 -17
- package/dist/utils/package-manager.js.map +1 -1
- package/dist/utils/python-uv.d.ts +21 -0
- package/dist/utils/python-uv.d.ts.map +1 -0
- package/dist/utils/python-uv.js +111 -0
- package/dist/utils/python-uv.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +49 -0
- package/dist/version.js.map +1 -0
- package/package.json +15 -17
- package/src/__tests__/__snapshots__/functions-generator.test.ts.snap +716 -22
- package/src/__tests__/__snapshots__/nextjs-generator.test.ts.snap +19 -7
- package/src/__tests__/__snapshots__/ui-generator.test.ts.snap +20 -12
- package/src/__tests__/agent-instructions.test.ts +237 -0
- package/src/__tests__/agent-loop-phase34.test.ts +513 -0
- package/src/__tests__/auth.test.ts +770 -0
- package/src/__tests__/bff-swa-auth.test.ts +275 -0
- package/src/__tests__/config.test.ts +176 -3
- package/src/__tests__/connector-functions-generator.test.ts +288 -0
- package/src/__tests__/connector-mock-server.test.ts +459 -0
- package/src/__tests__/connector-model-bff.test.ts +162 -0
- package/src/__tests__/cosmos-worktree-isolation.test.ts +149 -0
- package/src/__tests__/dev-seeds.test.ts +124 -0
- package/src/__tests__/dev.test.ts +254 -0
- package/src/__tests__/file-session.test.ts +98 -0
- package/src/__tests__/fixtures.ts +101 -0
- package/src/__tests__/functions-generator.test.ts +137 -1
- package/src/__tests__/init.test.ts +386 -1
- package/src/__tests__/jest-globals.d.ts +8 -0
- package/src/__tests__/machine.test.ts +371 -0
- package/src/__tests__/mcp.test.ts +253 -0
- package/src/__tests__/model-parser.test.ts +142 -0
- package/src/__tests__/multi-auth.test.ts +160 -0
- package/src/__tests__/nextjs-generator.test.ts +8 -0
- package/src/__tests__/package-manager.test.ts +59 -4
- package/src/__tests__/plan-apply.test.ts +499 -0
- package/src/__tests__/python-uv.test.ts +48 -0
- package/src/__tests__/scaffold.test.ts +287 -24
- package/src/__tests__/tsconfig.json +15 -0
- package/src/__tests__/ui-generator.test.ts +104 -0
- package/src/__tests__/zod-mock-generator.test.ts +132 -0
- package/src/cli/commands/add-auth.ts +853 -0
- package/src/cli/commands/add-connector.ts +159 -0
- package/src/cli/commands/cosmos-worktree-isolation.ts +130 -0
- package/src/cli/commands/create-model.ts +21 -100
- package/src/cli/commands/dev-seeds.ts +278 -7
- package/src/cli/commands/dev.ts +607 -160
- package/src/cli/commands/init.ts +880 -808
- package/src/cli/commands/scaffold.ts +531 -236
- package/src/cli/index.ts +338 -60
- package/src/core/config.ts +145 -9
- package/src/core/mock/connector-mock-server.ts +555 -0
- package/src/core/mock/zod-mock-generator.ts +205 -0
- package/src/core/operations/auth-operations.ts +360 -0
- package/src/core/operations/create-model.ts +191 -0
- package/src/core/operations/file-session.ts +222 -0
- package/src/core/operations/provision-operations.ts +297 -0
- package/src/core/operations/runtime.ts +258 -0
- package/src/core/operations/scaffold-apply.ts +106 -0
- package/src/core/operations/scaffold-machine.ts +77 -0
- package/src/core/operations/scaffold-plan.ts +215 -0
- package/src/core/project/agent-instructions.ts +167 -0
- package/src/core/project/agent-templates/AGENTS.md +504 -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/artifacts.ts +205 -0
- package/src/core/project/boundaries.ts +170 -0
- package/src/core/project/capabilities.ts +193 -0
- package/src/core/project/drift.ts +202 -0
- package/src/core/project/infra.ts +196 -0
- package/src/core/project/manifest.ts +402 -0
- package/src/core/project/state.ts +77 -0
- package/src/core/project/validation.ts +295 -0
- package/src/core/project/workflows.ts +295 -0
- package/src/core/scaffold/auth-generator.ts +1824 -0
- package/src/core/scaffold/connector-functions-generator.ts +1133 -0
- package/src/core/scaffold/functions-generator.ts +432 -134
- package/src/core/scaffold/model-parser.ts +244 -90
- package/src/core/scaffold/native-schema-generator.ts +904 -0
- package/src/core/scaffold/nextjs-generator.ts +174 -9
- package/src/core/scaffold/ui-generator.ts +191 -36
- package/src/core/verify/index.ts +468 -0
- package/src/machine/contracts.ts +33 -0
- package/src/machine/errors.ts +45 -0
- package/src/machine/index.ts +690 -0
- package/src/mcp/index.ts +423 -0
- package/src/types/index.ts +137 -0
- package/src/utils/package-manager.ts +41 -19
- package/src/utils/python-uv.ts +96 -0
- package/src/version.ts +13 -0
|
@@ -4,6 +4,8 @@ import * as fs from "fs";
|
|
|
4
4
|
import * as path from "path";
|
|
5
5
|
import { ensureSwallowKitProject } from "../../core/config";
|
|
6
6
|
import { FieldInfo, ModelInfo, getAllModels, toKebabCase } from "../../core/scaffold/model-parser";
|
|
7
|
+
import { resolveCosmosDatabaseNameForWorktree } from "./cosmos-worktree-isolation";
|
|
8
|
+
import type { GitCommandRunner } from "./cosmos-worktree-isolation";
|
|
7
9
|
|
|
8
10
|
export type SeedDocument = Record<string, unknown>;
|
|
9
11
|
|
|
@@ -12,6 +14,7 @@ interface GenerateDevSeedTemplatesOptions {
|
|
|
12
14
|
modelsDir?: string;
|
|
13
15
|
seedsDir?: string;
|
|
14
16
|
force?: boolean;
|
|
17
|
+
fromEmulator?: boolean;
|
|
15
18
|
}
|
|
16
19
|
|
|
17
20
|
interface ApplyDevSeedEnvironmentOptions {
|
|
@@ -29,8 +32,31 @@ interface LoadedSeedFile {
|
|
|
29
32
|
filePath: string;
|
|
30
33
|
}
|
|
31
34
|
|
|
35
|
+
export interface LocalCosmosConnectionInfo {
|
|
36
|
+
endpoint: string;
|
|
37
|
+
key: string;
|
|
38
|
+
databaseName: string;
|
|
39
|
+
worktreeIsolationEnabled?: boolean;
|
|
40
|
+
localSettingsPath: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type ResolveLocalCosmosConnectionResult =
|
|
44
|
+
| { ok: true; value: LocalCosmosConnectionInfo }
|
|
45
|
+
| {
|
|
46
|
+
ok: false;
|
|
47
|
+
reason: "missing-local-settings" | "missing-connection-string" | "invalid-connection-string";
|
|
48
|
+
localSettingsPath: string;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
interface ExportedContainerDocuments {
|
|
52
|
+
documents: SeedDocument[];
|
|
53
|
+
containerExists: boolean;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const COSMOS_SYSTEM_PROPERTIES = new Set(["_rid", "_self", "_etag", "_attachments", "_ts"]);
|
|
57
|
+
|
|
32
58
|
export function getContainerNameForModel(model: Pick<ModelInfo, "name">): string {
|
|
33
|
-
return `${model.name}s`;
|
|
59
|
+
return model.name.endsWith('s') ? model.name : `${model.name}s`;
|
|
34
60
|
}
|
|
35
61
|
|
|
36
62
|
export function getSeedEnvironmentDir(environment: string, seedsDir: string = "dev-seeds"): string {
|
|
@@ -65,6 +91,82 @@ export async function loadProjectModels(modelsDir: string = "shared/models"): Pr
|
|
|
65
91
|
return getAllModels(modelsDir);
|
|
66
92
|
}
|
|
67
93
|
|
|
94
|
+
export function buildDefaultCosmosDatabaseName(packageName: string): string {
|
|
95
|
+
const normalizedName = packageName.trim();
|
|
96
|
+
const baseName = normalizedName.length > 0 ? normalizedName : "App";
|
|
97
|
+
return `${baseName.charAt(0).toUpperCase()}${baseName.slice(1)}Database`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function getDefaultCosmosDatabaseName(projectDir: string = process.cwd()): string {
|
|
101
|
+
const packageJsonPath = path.join(projectDir, "package.json");
|
|
102
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8")) as { name?: string };
|
|
103
|
+
return buildDefaultCosmosDatabaseName(packageJson.name || "App");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function parseCosmosConnectionString(
|
|
107
|
+
connectionString: string
|
|
108
|
+
): Pick<LocalCosmosConnectionInfo, "endpoint" | "key"> | null {
|
|
109
|
+
const endpointMatch = connectionString.match(/AccountEndpoint=([^;]+)/);
|
|
110
|
+
const keyMatch = connectionString.match(/AccountKey=([^;]+)/);
|
|
111
|
+
|
|
112
|
+
if (!endpointMatch || !keyMatch) {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
endpoint: endpointMatch[1],
|
|
118
|
+
key: keyMatch[1],
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function resolveLocalCosmosConnectionInfo(
|
|
123
|
+
defaultDatabaseName: string,
|
|
124
|
+
functionsDir: string = path.join(process.cwd(), "functions"),
|
|
125
|
+
worktreeOptions: {
|
|
126
|
+
cwd?: string;
|
|
127
|
+
runGit?: GitCommandRunner;
|
|
128
|
+
} = {}
|
|
129
|
+
): ResolveLocalCosmosConnectionResult {
|
|
130
|
+
const localSettingsPath = path.join(functionsDir, "local.settings.json");
|
|
131
|
+
if (!fs.existsSync(localSettingsPath)) {
|
|
132
|
+
return { ok: false, reason: "missing-local-settings", localSettingsPath };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const localSettings = JSON.parse(fs.readFileSync(localSettingsPath, "utf-8")) as {
|
|
136
|
+
Values?: {
|
|
137
|
+
CosmosDBConnection?: string;
|
|
138
|
+
COSMOS_DB_DATABASE_NAME?: string;
|
|
139
|
+
};
|
|
140
|
+
};
|
|
141
|
+
const connectionString = localSettings.Values?.CosmosDBConnection;
|
|
142
|
+
|
|
143
|
+
if (!connectionString) {
|
|
144
|
+
return { ok: false, reason: "missing-connection-string", localSettingsPath };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const parsedConnection = parseCosmosConnectionString(connectionString);
|
|
148
|
+
if (!parsedConnection) {
|
|
149
|
+
return { ok: false, reason: "invalid-connection-string", localSettingsPath };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const baseDatabaseName = localSettings.Values?.COSMOS_DB_DATABASE_NAME || defaultDatabaseName;
|
|
153
|
+
const databaseNameResolution = resolveCosmosDatabaseNameForWorktree(
|
|
154
|
+
baseDatabaseName,
|
|
155
|
+
worktreeOptions.cwd,
|
|
156
|
+
worktreeOptions.runGit
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
ok: true,
|
|
161
|
+
value: {
|
|
162
|
+
...parsedConnection,
|
|
163
|
+
databaseName: databaseNameResolution.databaseName,
|
|
164
|
+
worktreeIsolationEnabled: databaseNameResolution.worktreeIsolationEnabled,
|
|
165
|
+
localSettingsPath,
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
68
170
|
export function buildSeedTemplateDocument(
|
|
69
171
|
model: ModelInfo,
|
|
70
172
|
allModels: ModelInfo[] = [model],
|
|
@@ -135,7 +237,7 @@ export async function applyDevSeedEnvironment({
|
|
|
135
237
|
console.log(`🧪 Applying Cosmos DB seed data for environment "${environment}"...`);
|
|
136
238
|
|
|
137
239
|
for (const seedFile of seedFiles) {
|
|
138
|
-
await recreateContainer(database, seedFile.containerName);
|
|
240
|
+
await recreateContainer(database, seedFile.containerName, seedFile.model.partitionKey);
|
|
139
241
|
const container = database.container(seedFile.containerName);
|
|
140
242
|
|
|
141
243
|
for (const document of seedFile.documents) {
|
|
@@ -197,14 +299,121 @@ export async function generateDevSeedTemplates({
|
|
|
197
299
|
console.log(` 2. Run 'swallowkit dev --seed-env ${environment}' to replace emulator data before startup`);
|
|
198
300
|
}
|
|
199
301
|
|
|
302
|
+
export function stripCosmosSystemProperties(document: SeedDocument): SeedDocument {
|
|
303
|
+
const sanitized: SeedDocument = {};
|
|
304
|
+
|
|
305
|
+
for (const [key, value] of Object.entries(document)) {
|
|
306
|
+
if (!COSMOS_SYSTEM_PROPERTIES.has(key)) {
|
|
307
|
+
sanitized[key] = value;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
return sanitized;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export function prepareSeedDocumentsForExport(documents: SeedDocument[], filePath: string): SeedDocument[] {
|
|
315
|
+
const preparedDocuments = documents
|
|
316
|
+
.map((document) => stripCosmosSystemProperties(document))
|
|
317
|
+
.sort(compareSeedDocumentsForExport);
|
|
318
|
+
|
|
319
|
+
validateSeedDocuments(preparedDocuments, filePath);
|
|
320
|
+
return preparedDocuments;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
export async function exportDevSeedsFromEmulator({
|
|
324
|
+
environment,
|
|
325
|
+
modelsDir = "shared/models",
|
|
326
|
+
seedsDir = "dev-seeds",
|
|
327
|
+
force = false,
|
|
328
|
+
}: GenerateDevSeedTemplatesOptions): Promise<void> {
|
|
329
|
+
ensureSwallowKitProject("create-dev-seeds");
|
|
330
|
+
|
|
331
|
+
console.log(`🧪 Exporting dev seeds from local Cosmos DB Emulator for environment "${environment}"...\n`);
|
|
332
|
+
const models = await loadProjectModels(modelsDir);
|
|
333
|
+
|
|
334
|
+
if (models.length === 0) {
|
|
335
|
+
console.log("⚠️ No schemas found under shared/models. Nothing was exported.");
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const connectionInfoResult = resolveLocalCosmosConnectionInfo(getDefaultCosmosDatabaseName());
|
|
340
|
+
if (!connectionInfoResult.ok) {
|
|
341
|
+
throw new Error(buildLocalCosmosConnectionError(connectionInfoResult));
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const { endpoint, key, databaseName, localSettingsPath } = connectionInfoResult.value;
|
|
345
|
+
console.log(`🗄️ Using local Cosmos DB settings from ${localSettingsPath}`);
|
|
346
|
+
console.log(`📦 Export source database: "${databaseName}"\n`);
|
|
347
|
+
|
|
348
|
+
const client = new CosmosClient({ endpoint, key });
|
|
349
|
+
const database = client.database(databaseName);
|
|
350
|
+
|
|
351
|
+
try {
|
|
352
|
+
await database.read();
|
|
353
|
+
} catch (error: unknown) {
|
|
354
|
+
if (isCosmosNotFoundError(error)) {
|
|
355
|
+
throw new Error(`Cosmos DB database "${databaseName}" was not found in the local emulator.`);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
throw error;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const environmentDir = getSeedEnvironmentDir(environment, seedsDir);
|
|
362
|
+
fs.mkdirSync(environmentDir, { recursive: true });
|
|
363
|
+
|
|
364
|
+
let writtenFiles = 0;
|
|
365
|
+
let skippedFiles = 0;
|
|
366
|
+
let totalDocuments = 0;
|
|
367
|
+
|
|
368
|
+
for (const model of models) {
|
|
369
|
+
const containerName = getContainerNameForModel(model);
|
|
370
|
+
const filePath = path.join(environmentDir, `${toKebabCase(model.name)}.json`);
|
|
371
|
+
|
|
372
|
+
if (!force && fs.existsSync(filePath)) {
|
|
373
|
+
skippedFiles += 1;
|
|
374
|
+
continue;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const exported = await readContainerSeedDocuments(database, containerName, filePath);
|
|
378
|
+
fs.writeFileSync(filePath, JSON.stringify(exported.documents, null, 2) + "\n", "utf-8");
|
|
379
|
+
writtenFiles += 1;
|
|
380
|
+
totalDocuments += exported.documents.length;
|
|
381
|
+
|
|
382
|
+
const detail = exported.containerExists
|
|
383
|
+
? `${exported.documents.length} item(s)`
|
|
384
|
+
: "container not found; wrote empty seed";
|
|
385
|
+
console.log(`✅ Exported "${containerName}" to ${filePath} (${detail})`);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if (skippedFiles > 0) {
|
|
389
|
+
console.log("");
|
|
390
|
+
console.log(`⏭️ Skipped ${skippedFiles} existing file(s). Use --force to overwrite them.`);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
console.log("");
|
|
394
|
+
console.log(`🧾 Export complete: ${writtenFiles} file(s), ${totalDocuments} item(s).`);
|
|
395
|
+
console.log(`📝 Next step: Run 'swallowkit dev --seed-env ${environment}' to replay the exported data.`);
|
|
396
|
+
}
|
|
397
|
+
|
|
200
398
|
export const devSeedsCommand = new Command()
|
|
201
399
|
.name("create-dev-seeds")
|
|
202
|
-
.description("Generate dev seed JSON
|
|
400
|
+
.description("Generate dev seed JSON files from shared/models schemas or export current local Cosmos DB Emulator data")
|
|
203
401
|
.argument("<environment>", "Seed environment name")
|
|
204
402
|
.option("--models-dir <dir>", "Models directory", "shared/models")
|
|
205
403
|
.option("--seeds-dir <dir>", "Dev seeds directory", "dev-seeds")
|
|
206
404
|
.option("--force", "Overwrite existing seed JSON files", false)
|
|
207
|
-
.
|
|
405
|
+
.option("--from-emulator", "Export current data from the local Cosmos DB Emulator instead of generating templates", false)
|
|
406
|
+
.action(async (environment: string, options: { modelsDir?: string; seedsDir?: string; force?: boolean; fromEmulator?: boolean }) => {
|
|
407
|
+
if (options.fromEmulator) {
|
|
408
|
+
await exportDevSeedsFromEmulator({
|
|
409
|
+
environment,
|
|
410
|
+
modelsDir: options.modelsDir,
|
|
411
|
+
seedsDir: options.seedsDir,
|
|
412
|
+
force: options.force,
|
|
413
|
+
});
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
|
|
208
417
|
await generateDevSeedTemplates({
|
|
209
418
|
environment,
|
|
210
419
|
modelsDir: options.modelsDir,
|
|
@@ -326,7 +535,69 @@ function validateSeedDocuments(documents: SeedDocument[], filePath: string): voi
|
|
|
326
535
|
});
|
|
327
536
|
}
|
|
328
537
|
|
|
329
|
-
|
|
538
|
+
function compareSeedDocumentsForExport(left: SeedDocument, right: SeedDocument): number {
|
|
539
|
+
const leftId = typeof left.id === "string" ? left.id : "";
|
|
540
|
+
const rightId = typeof right.id === "string" ? right.id : "";
|
|
541
|
+
|
|
542
|
+
if (leftId.length === 0 && rightId.length === 0) {
|
|
543
|
+
return 0;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
if (leftId.length === 0) {
|
|
547
|
+
return 1;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
if (rightId.length === 0) {
|
|
551
|
+
return -1;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
return leftId.localeCompare(rightId);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
function buildLocalCosmosConnectionError(
|
|
558
|
+
result: Exclude<ResolveLocalCosmosConnectionResult, { ok: true; value: LocalCosmosConnectionInfo }>
|
|
559
|
+
): string {
|
|
560
|
+
switch (result.reason) {
|
|
561
|
+
case "missing-local-settings":
|
|
562
|
+
return `local.settings.json not found at ${result.localSettingsPath}. Start from a SwallowKit app with Azure Functions configured.`;
|
|
563
|
+
case "missing-connection-string":
|
|
564
|
+
return `CosmosDBConnection not found in ${result.localSettingsPath}.`;
|
|
565
|
+
case "invalid-connection-string":
|
|
566
|
+
return `Invalid CosmosDBConnection format in ${result.localSettingsPath}.`;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
throw new Error(`Unhandled local Cosmos connection error: ${JSON.stringify(result)}`);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
async function readContainerSeedDocuments(
|
|
573
|
+
database: Database,
|
|
574
|
+
containerName: string,
|
|
575
|
+
filePath: string
|
|
576
|
+
): Promise<ExportedContainerDocuments> {
|
|
577
|
+
const container = database.container(containerName);
|
|
578
|
+
|
|
579
|
+
try {
|
|
580
|
+
await container.read();
|
|
581
|
+
} catch (error: unknown) {
|
|
582
|
+
if (isCosmosNotFoundError(error)) {
|
|
583
|
+
return { documents: [], containerExists: false };
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
throw error;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
const { resources } = await container.items.query<SeedDocument>("SELECT * FROM c").fetchAll();
|
|
590
|
+
return {
|
|
591
|
+
documents: prepareSeedDocumentsForExport(resources, filePath),
|
|
592
|
+
containerExists: true,
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
function isCosmosNotFoundError(error: unknown): boolean {
|
|
597
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === 404;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
async function recreateContainer(database: Database, containerName: string, partitionKeyPath: string = '/id'): Promise<void> {
|
|
330
601
|
try {
|
|
331
602
|
await database.container(containerName).delete();
|
|
332
603
|
} catch (error: any) {
|
|
@@ -339,7 +610,7 @@ async function recreateContainer(database: Database, containerName: string): Pro
|
|
|
339
610
|
await database.containers.createIfNotExists({
|
|
340
611
|
id: containerName,
|
|
341
612
|
partitionKey: {
|
|
342
|
-
paths: [
|
|
613
|
+
paths: [partitionKeyPath],
|
|
343
614
|
kind: PartitionKeyKind.Hash,
|
|
344
615
|
version: 2,
|
|
345
616
|
},
|
|
@@ -351,7 +622,7 @@ async function recreateContainer(database: Database, containerName: string): Pro
|
|
|
351
622
|
await database.containers.createIfNotExists({
|
|
352
623
|
id: containerName,
|
|
353
624
|
partitionKey: {
|
|
354
|
-
paths: [
|
|
625
|
+
paths: [partitionKeyPath],
|
|
355
626
|
},
|
|
356
627
|
});
|
|
357
628
|
}
|