swallowkit 1.0.0-beta.6 → 1.0.0-beta.60
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 +86 -177
- package/README.md +86 -178
- 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 +163 -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 +19 -81
- package/dist/cli/commands/create-model.js.map +1 -1
- package/dist/cli/commands/dev-seeds.d.ts +62 -0
- package/dist/cli/commands/dev-seeds.d.ts.map +1 -0
- package/dist/cli/commands/dev-seeds.js +474 -0
- package/dist/cli/commands/dev-seeds.js.map +1 -0
- package/dist/cli/commands/dev.d.ts +38 -0
- package/dist/cli/commands/dev.d.ts.map +1 -1
- package/dist/cli/commands/dev.js +733 -155
- package/dist/cli/commands/dev.js.map +1 -1
- package/dist/cli/commands/index.d.ts +1 -0
- package/dist/cli/commands/index.d.ts.map +1 -1
- package/dist/cli/commands/index.js +3 -1
- package/dist/cli/commands/index.js.map +1 -1
- package/dist/cli/commands/init.d.ts +78 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +1453 -898
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/scaffold.d.ts +24 -0
- package/dist/cli/commands/scaffold.d.ts.map +1 -1
- package/dist/cli/commands/scaffold.js +473 -90
- 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 +333 -55
- package/dist/cli/index.js.map +1 -1
- package/dist/core/config.d.ts +14 -3
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +179 -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 +159 -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 +7 -1
- package/dist/core/scaffold/functions-generator.d.ts.map +1 -1
- package/dist/core/scaffold/functions-generator.js +771 -69
- package/dist/core/scaffold/functions-generator.js.map +1 -1
- package/dist/core/scaffold/model-parser.d.ts +21 -1
- package/dist/core/scaffold/model-parser.d.ts.map +1 -1
- package/dist/core/scaffold/model-parser.js +243 -43
- 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 +153 -9
- package/dist/core/scaffold/nextjs-generator.js.map +1 -1
- package/dist/core/scaffold/openapi-generator.d.ts +3 -0
- package/dist/core/scaffold/openapi-generator.d.ts.map +1 -0
- package/dist/core/scaffold/openapi-generator.js +190 -0
- package/dist/core/scaffold/openapi-generator.js.map +1 -0
- 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/database/base-model.d.ts +3 -3
- package/dist/database/base-model.js +3 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- 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 +109 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/package-manager.d.ts +5 -2
- package/dist/utils/package-manager.d.ts.map +1 -1
- package/dist/utils/package-manager.js +30 -9
- 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 +9 -5
- package/src/__tests__/__snapshots__/functions-generator.test.ts.snap +1139 -0
- package/src/__tests__/__snapshots__/nextjs-generator.test.ts.snap +206 -0
- package/src/__tests__/__snapshots__/ui-generator.test.ts.snap +532 -0
- 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 +295 -0
- package/src/__tests__/connector-functions-generator.test.ts +288 -0
- package/src/__tests__/connector-mock-server.test.ts +439 -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 +236 -0
- package/src/__tests__/dev.test.ts +296 -0
- package/src/__tests__/file-session.test.ts +98 -0
- package/src/__tests__/fixtures.ts +184 -0
- package/src/__tests__/functions-generator.test.ts +237 -0
- package/src/__tests__/init.test.ts +411 -0
- 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 +81 -0
- package/src/__tests__/multi-auth.test.ts +160 -0
- package/src/__tests__/nextjs-generator.test.ts +105 -0
- package/src/__tests__/openapi-generator.test.ts +43 -0
- package/src/__tests__/package-manager.test.ts +227 -0
- package/src/__tests__/plan-apply.test.ts +499 -0
- package/src/__tests__/python-uv.test.ts +48 -0
- package/src/__tests__/scaffold.test.ts +302 -0
- package/src/__tests__/string-utils.test.ts +75 -0
- package/src/__tests__/tsconfig.json +15 -0
- package/src/__tests__/ui-generator.test.ts +248 -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 +158 -0
- package/src/cli/commands/cosmos-worktree-isolation.ts +130 -0
- package/src/cli/commands/create-model.ts +62 -0
- package/src/cli/commands/dev-seeds.ts +629 -0
- package/src/cli/commands/dev.ts +1252 -0
- package/src/cli/commands/index.ts +9 -0
- package/src/cli/commands/init.ts +3541 -0
- package/src/cli/commands/provision.ts +193 -0
- package/src/cli/commands/scaffold.ts +1081 -0
- package/src/cli/index.ts +350 -0
- package/src/core/config.ts +380 -0
- 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 +502 -0
- package/src/core/project/agent-templates/CLAUDE.md +31 -0
- package/src/core/project/agent-templates/azure-functions.instructions.md +22 -0
- package/src/core/project/agent-templates/bff-routes.instructions.md +18 -0
- package/src/core/project/agent-templates/copilot-instructions.md +27 -0
- package/src/core/project/agent-templates/shared-models.instructions.md +72 -0
- package/src/core/project/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 +972 -0
- package/src/core/scaffold/model-parser.ts +854 -0
- package/src/core/scaffold/native-schema-generator.ts +904 -0
- package/src/core/scaffold/nextjs-generator.ts +382 -0
- package/src/core/scaffold/openapi-generator.ts +212 -0
- package/src/core/scaffold/ui-generator.ts +1100 -0
- package/src/core/verify/index.ts +468 -0
- package/src/database/base-model.ts +184 -0
- package/src/database/client.ts +140 -0
- package/src/database/repository.ts +104 -0
- package/src/database/runtime-check.ts +25 -0
- package/src/index.ts +27 -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 +182 -0
- package/src/utils/package-manager.ts +246 -0
- package/src/utils/python-uv.ts +96 -0
- package/src/version.ts +13 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capabilities — 「SwallowKit に何ができるか」の機械可読な自己記述。
|
|
3
|
+
*
|
|
4
|
+
* inspect project/boundaries/infra が「今の状態」を返すのに対し、
|
|
5
|
+
* inspect capabilities は「モデルで宣言できるもの・認証の正しい導入手順・
|
|
6
|
+
* 生成 CRUD の守備範囲・シード適用方法」など、状態に依存しない
|
|
7
|
+
* フレームワーク仕様を返す。Coding Agent がドキュメント外の知識を
|
|
8
|
+
* 推測で埋めるのを構造的に防ぐことが目的。
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { getSwallowKitVersion } from "../../version";
|
|
12
|
+
|
|
13
|
+
export interface CapabilityModelDeclaration {
|
|
14
|
+
name: string;
|
|
15
|
+
required: boolean;
|
|
16
|
+
description: string;
|
|
17
|
+
format: string;
|
|
18
|
+
notes?: string[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface CapabilityAuthProvider {
|
|
22
|
+
provider: string;
|
|
23
|
+
description: string;
|
|
24
|
+
postApplySteps: string[];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface CapabilitiesContract {
|
|
28
|
+
contractVersion: number;
|
|
29
|
+
swallowkitVersion: string;
|
|
30
|
+
modelDeclarations: CapabilityModelDeclaration[];
|
|
31
|
+
authentication: {
|
|
32
|
+
correctWorkflow: string[];
|
|
33
|
+
rules: string[];
|
|
34
|
+
providers: CapabilityAuthProvider[];
|
|
35
|
+
authorizationPolicies: {
|
|
36
|
+
semantics: string;
|
|
37
|
+
configExample: string;
|
|
38
|
+
handEditAllowed: boolean;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
generatedCrud: {
|
|
42
|
+
guarantees: string[];
|
|
43
|
+
notGuaranteed: string[];
|
|
44
|
+
guidance: string[];
|
|
45
|
+
};
|
|
46
|
+
sharedModels: {
|
|
47
|
+
idFieldRule: string;
|
|
48
|
+
buildNote: string;
|
|
49
|
+
};
|
|
50
|
+
seeding: {
|
|
51
|
+
createTemplates: string;
|
|
52
|
+
applySeeds: string;
|
|
53
|
+
notes: string[];
|
|
54
|
+
};
|
|
55
|
+
machineCommands: string[];
|
|
56
|
+
verifyChecks: {
|
|
57
|
+
defaultChecks: string[];
|
|
58
|
+
optInChecks: string[];
|
|
59
|
+
customChecks: string;
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function inspectCapabilities(): CapabilitiesContract {
|
|
64
|
+
return {
|
|
65
|
+
contractVersion: 1,
|
|
66
|
+
swallowkitVersion: getSwallowKitVersion(),
|
|
67
|
+
modelDeclarations: [
|
|
68
|
+
{
|
|
69
|
+
name: "schema",
|
|
70
|
+
required: true,
|
|
71
|
+
description:
|
|
72
|
+
"Zod object schema; the schema constant and the inferred type share the same PascalCase name. `id` MUST be a required string — generated CRUD, UI, and repositories assume it is always present. Only `createdAt` / `updatedAt` are optional (backend-managed).",
|
|
73
|
+
format:
|
|
74
|
+
"export const Todo = z.object({ id: z.string(), title: z.string().min(1), createdAt: z.string().optional(), updatedAt: z.string().optional() });\nexport type Todo = z.infer<typeof Todo>;",
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: "displayName",
|
|
78
|
+
required: false,
|
|
79
|
+
description: "Human-readable name used by generated UI. Defaults to the model name.",
|
|
80
|
+
format: "export const displayName = 'Todo';",
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: "partitionKey",
|
|
84
|
+
required: false,
|
|
85
|
+
description:
|
|
86
|
+
"Cosmos DB partition key path for the generated container. Must start with '/' and reference a field that exists in the schema. Defaults to '/id'.",
|
|
87
|
+
format: "export const partitionKey = '/learnerId';",
|
|
88
|
+
notes: ["The referenced field (e.g. learnerId) must be declared in the Zod schema."],
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
name: "authPolicy",
|
|
92
|
+
required: false,
|
|
93
|
+
description:
|
|
94
|
+
"Role-based access control metadata applied to the generated CRUD Functions. `roles` guards all operations; `read`/`write` guard per-operation; `policy` references a named authorization policy from swallowkit.config auth.authorization.policies.",
|
|
95
|
+
format:
|
|
96
|
+
"export const authPolicy = { roles: ['admin'] };\n// or per-operation:\nexport const authPolicy = { read: ['user'], write: ['admin'] };\n// or a named policy:\nexport const authPolicy = { policy: 'adminOnly' };",
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
name: "connectorConfig",
|
|
100
|
+
required: false,
|
|
101
|
+
description:
|
|
102
|
+
"Binds the model to a configured external connector (RDB / API) instead of Cosmos DB. The connector must exist in swallowkit.config connectors.",
|
|
103
|
+
format:
|
|
104
|
+
"export const connectorConfig = {\n connector: 'mysql',\n table: 'todos',\n operations: ['list', 'get', 'create', 'update', 'delete'],\n};",
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
authentication: {
|
|
108
|
+
correctWorkflow: [
|
|
109
|
+
"1. plan auth --provider <custom-jwt|swa|external-token> [--scheme <name>] [--allowed-providers <csv>] — computes the change plan; SwallowKit writes auth.schemes into swallowkit.config for you.",
|
|
110
|
+
"2. apply auth --plan <planId> — generates auth code and appends the scheme to swallowkit.config.",
|
|
111
|
+
"3. Hand-edit swallowkit.config auth.authorization.policies to define named policies (the config is an extension point; this edit is expected).",
|
|
112
|
+
"4. For swa schemes: set swa.allowedProviders via --allowed-providers at plan time, or hand-edit it in the config after apply.",
|
|
113
|
+
"5. For external-token schemes: implement the generated verifier stub — it fails closed (throws) until implemented.",
|
|
114
|
+
"6. Re-run plan/apply scaffold for models that need auth guards, then verify project.",
|
|
115
|
+
],
|
|
116
|
+
rules: [
|
|
117
|
+
"NEVER hand-write auth.schemes entries in swallowkit.config — plan/apply auth owns them and duplicate names abort with 'already exists'.",
|
|
118
|
+
"authorization.policies and swa.allowedProviders ARE hand-edited after apply (extension-point edits).",
|
|
119
|
+
"Verifier stubs pass typecheck and verify; a passing verify does NOT mean external-token auth works end-to-end.",
|
|
120
|
+
],
|
|
121
|
+
providers: [
|
|
122
|
+
{
|
|
123
|
+
provider: "custom-jwt",
|
|
124
|
+
description: "Login/logout/me endpoints backed by an RDB user table, bcrypt password check, HS256 JWT.",
|
|
125
|
+
postApplySteps: ["Set JWT_SECRET in functions/local.settings.json.", "Ensure the user table matches auth.customJwt config."],
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
provider: "swa",
|
|
129
|
+
description: "Azure Static Web Apps built-in auth (EasyAuth). Principal arrives via x-ms-client-principal.",
|
|
130
|
+
postApplySteps: [
|
|
131
|
+
"Set swa.allowedProviders (e.g. ['github']) via --allowed-providers or by hand-editing the config; generated login URLs use the first entry.",
|
|
132
|
+
],
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
provider: "external-token",
|
|
136
|
+
description: "Bearer tokens issued by an external IdP (LINE, Auth0, ...). SwallowKit generates a fail-closed verifier stub.",
|
|
137
|
+
postApplySteps: [
|
|
138
|
+
"Implement the generated verifier (extension point / generated-once; edit freely).",
|
|
139
|
+
"Implement the frontend token adapter (lib/auth/**/token-adapter.ts).",
|
|
140
|
+
],
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
provider: "none",
|
|
144
|
+
description: "Disables authentication in the config.",
|
|
145
|
+
postApplySteps: [],
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
authorizationPolicies: {
|
|
149
|
+
semantics:
|
|
150
|
+
"A named policy lists which schemes may satisfy it and which roles are required. Generated guards call requireAuth(request, policyName). Exactly one scheme must match the presented credentials.",
|
|
151
|
+
configExample:
|
|
152
|
+
"auth: {\n schemes: { admin: { provider: 'swa', swa: { allowedProviders: ['github'] } }, api: { provider: 'external-token' } },\n authorization: {\n defaultPolicy: 'anonymous',\n policies: {\n adminOnly: { schemes: ['admin'], roles: ['administrator'] },\n learner: { schemes: ['api'] },\n },\n },\n}",
|
|
153
|
+
handEditAllowed: true,
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
generatedCrud: {
|
|
157
|
+
guarantees: [
|
|
158
|
+
"Generated CRUD endpoints enforce the model's authPolicy (authentication + role checks).",
|
|
159
|
+
"Zod validation runs before persistence.",
|
|
160
|
+
],
|
|
161
|
+
notGuaranteed: [
|
|
162
|
+
"Owner scoping: generated CRUD does NOT restrict rows to the authenticated principal. Any authenticated caller passing the policy can read/write any document.",
|
|
163
|
+
],
|
|
164
|
+
guidance: [
|
|
165
|
+
"For multi-user data where users may only touch their own records, add ai-authored custom endpoints that derive the scoping key (e.g. learnerId) from the verified principal — never from the request body — and restrict generated write endpoints to admin roles or disable them.",
|
|
166
|
+
],
|
|
167
|
+
},
|
|
168
|
+
sharedModels: {
|
|
169
|
+
idFieldRule: "id: z.string() (required). createdAt/updatedAt: z.string().optional(). The backend owns their values.",
|
|
170
|
+
buildNote:
|
|
171
|
+
"verify typecheck resolves the shared package from its built dist/. SwallowKit rebuilds shared automatically during verify; when typechecking manually, build the shared package first (errors like \"no exported member\" usually mean a stale dist).",
|
|
172
|
+
},
|
|
173
|
+
seeding: {
|
|
174
|
+
createTemplates: "swallowkit create-dev-seeds <environment> — writes dev-seeds/<environment>/<model>.json templates.",
|
|
175
|
+
applySeeds: "swallowkit dev --seed-env <environment> — applies the seed JSON to the local Cosmos DB emulator at dev startup.",
|
|
176
|
+
notes: ["Editing dev-seeds/*.json alone does nothing; seeds are only applied via dev --seed-env."],
|
|
177
|
+
},
|
|
178
|
+
machineCommands: [
|
|
179
|
+
"inspect project | entities | routes | artifacts | boundaries | infra | drift | capabilities",
|
|
180
|
+
"validate project",
|
|
181
|
+
"generate model <names...> | generate scaffold <model>",
|
|
182
|
+
"plan scaffold <models...> | plan auth --provider <p> [--scheme <name>] [--allowed-providers <csv>] | plan provision -g <rg> --location <loc> --swa-location <loc>",
|
|
183
|
+
"apply scaffold [models...] --plan <id> [--approve] | apply auth --plan <id> [--approve] | apply provision --plan <id> --approve",
|
|
184
|
+
"verify project [--checks <ids>] [--compact]",
|
|
185
|
+
"explain failure [--check <id>]",
|
|
186
|
+
],
|
|
187
|
+
verifyChecks: {
|
|
188
|
+
defaultChecks: ["structure", "drift", "typecheck"],
|
|
189
|
+
optInChecks: ["build", "lint", "test"],
|
|
190
|
+
customChecks: "swallowkit.config verify.checks: [{ id, command, title?, timeoutMs? }]",
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drift Detection — 生成物と現在のプロジェクト状態の乖離を検出する。
|
|
3
|
+
*
|
|
4
|
+
* 検出対象:
|
|
5
|
+
* - schema-drift: スキーマ変更後に生成物が再生成されていない
|
|
6
|
+
* - generator-drift: Generator (SwallowKit) のバージョンが生成時から変わっている
|
|
7
|
+
* - artifact-modified: 生成後にファイルが手動変更されている(再生成で失われる可能性)
|
|
8
|
+
* - artifact-missing: 台帳に記録された生成物が存在しない
|
|
9
|
+
* - manifest-drift: Manifest と実際のプロジェクト構造が一致していない
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import * as path from "path";
|
|
13
|
+
import { getSwallowKitVersion } from "../../version";
|
|
14
|
+
import { hashFileIfExists } from "../operations/file-session";
|
|
15
|
+
import { ArtifactRecord, loadArtifactLedger } from "./artifacts";
|
|
16
|
+
import { buildProjectManifest, readProjectManifest, SWALLOWKIT_MANIFEST_PATH } from "./manifest";
|
|
17
|
+
|
|
18
|
+
export type DriftKind =
|
|
19
|
+
| "schema-drift"
|
|
20
|
+
| "generator-drift"
|
|
21
|
+
| "artifact-modified"
|
|
22
|
+
| "artifact-missing"
|
|
23
|
+
| "manifest-drift";
|
|
24
|
+
|
|
25
|
+
export type DriftSeverity = "error" | "warning" | "info";
|
|
26
|
+
|
|
27
|
+
export interface DriftFinding {
|
|
28
|
+
kind: DriftKind;
|
|
29
|
+
severity: DriftSeverity;
|
|
30
|
+
message: string;
|
|
31
|
+
path?: string;
|
|
32
|
+
entity?: string;
|
|
33
|
+
expected?: string;
|
|
34
|
+
actual?: string;
|
|
35
|
+
repairAction: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface DriftDetectionResult {
|
|
39
|
+
ledgerFound: boolean;
|
|
40
|
+
findings: DriftFinding[];
|
|
41
|
+
checkedArtifacts: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function detectArtifactDrift(
|
|
45
|
+
record: ArtifactRecord,
|
|
46
|
+
projectRoot: string,
|
|
47
|
+
findings: DriftFinding[]
|
|
48
|
+
): void {
|
|
49
|
+
const absolutePath = path.join(projectRoot, record.path);
|
|
50
|
+
const currentHash = hashFileIfExists(absolutePath);
|
|
51
|
+
|
|
52
|
+
if (currentHash === null) {
|
|
53
|
+
findings.push({
|
|
54
|
+
kind: "artifact-missing",
|
|
55
|
+
severity: "warning",
|
|
56
|
+
message: `Generated artifact is missing: ${record.path}`,
|
|
57
|
+
path: record.path,
|
|
58
|
+
entity: record.sourceModel,
|
|
59
|
+
expected: record.contentHash,
|
|
60
|
+
actual: "missing",
|
|
61
|
+
repairAction: record.sourceModel
|
|
62
|
+
? `Run "swallowkit machine apply scaffold ${record.sourceModel}" to regenerate the artifact.`
|
|
63
|
+
: "Re-run the SwallowKit generator that produced this artifact.",
|
|
64
|
+
});
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (currentHash !== record.contentHash) {
|
|
69
|
+
const isUserEditable = record.ownership === "user-owned" || record.ownership === "generated-once";
|
|
70
|
+
findings.push({
|
|
71
|
+
kind: "artifact-modified",
|
|
72
|
+
severity: isUserEditable ? "info" : "warning",
|
|
73
|
+
message: isUserEditable
|
|
74
|
+
? `${record.ownership} artifact has been edited after generation (expected): ${record.path}`
|
|
75
|
+
: `${record.ownership} artifact has been modified after generation; regenerating will require approval: ${record.path}`,
|
|
76
|
+
path: record.path,
|
|
77
|
+
entity: record.sourceModel,
|
|
78
|
+
expected: record.contentHash,
|
|
79
|
+
actual: currentHash,
|
|
80
|
+
repairAction: isUserEditable
|
|
81
|
+
? "No action required. Edits to this ownership class are expected."
|
|
82
|
+
: "Keep the customization (plan/apply will flag a conflict) or revert the file to the generated content.",
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function detectSchemaDrift(
|
|
88
|
+
records: ArtifactRecord[],
|
|
89
|
+
projectRoot: string,
|
|
90
|
+
findings: DriftFinding[]
|
|
91
|
+
): void {
|
|
92
|
+
const bySchema = new Map<string, { schemaHash: string; sourceModel?: string; paths: string[] }>();
|
|
93
|
+
|
|
94
|
+
for (const record of records) {
|
|
95
|
+
if (!record.schemaHash || !record.sourceModel) continue;
|
|
96
|
+
const key = record.sourceModel;
|
|
97
|
+
const entry = bySchema.get(key) ?? { schemaHash: record.schemaHash, sourceModel: record.sourceModel, paths: [] };
|
|
98
|
+
entry.schemaHash = record.schemaHash;
|
|
99
|
+
entry.paths.push(record.path);
|
|
100
|
+
bySchema.set(key, entry);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
for (const [modelName, entry] of bySchema) {
|
|
104
|
+
// モデルファイルは shared/models/<kebab>.ts 規約
|
|
105
|
+
const kebab = modelName
|
|
106
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1-$2")
|
|
107
|
+
.replace(/([A-Z])([A-Z][a-z])/g, "$1-$2")
|
|
108
|
+
.toLowerCase();
|
|
109
|
+
const modelPath = path.join(projectRoot, "shared", "models", `${kebab}.ts`);
|
|
110
|
+
const currentSchemaHash = hashFileIfExists(modelPath);
|
|
111
|
+
if (currentSchemaHash === null) {
|
|
112
|
+
continue; // モデルファイルが見つからない場合は manifest-drift 側で検出される
|
|
113
|
+
}
|
|
114
|
+
if (currentSchemaHash !== entry.schemaHash) {
|
|
115
|
+
findings.push({
|
|
116
|
+
kind: "schema-drift",
|
|
117
|
+
severity: "warning",
|
|
118
|
+
message: `Model "${modelName}" changed after its artifacts were generated (${entry.paths.length} artifact(s) are stale).`,
|
|
119
|
+
path: `shared/models/${kebab}.ts`,
|
|
120
|
+
entity: modelName,
|
|
121
|
+
expected: entry.schemaHash,
|
|
122
|
+
actual: currentSchemaHash,
|
|
123
|
+
repairAction: `Run "swallowkit machine plan scaffold ${kebab}" then apply to regenerate artifacts for ${modelName}.`,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function detectGeneratorDrift(records: ArtifactRecord[], findings: DriftFinding[]): void {
|
|
130
|
+
const currentVersion = getSwallowKitVersion();
|
|
131
|
+
const staleVersions = new Map<string, number>();
|
|
132
|
+
|
|
133
|
+
for (const record of records) {
|
|
134
|
+
if (record.generatorVersion !== currentVersion) {
|
|
135
|
+
staleVersions.set(record.generatorVersion, (staleVersions.get(record.generatorVersion) ?? 0) + 1);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
for (const [version, count] of staleVersions) {
|
|
140
|
+
findings.push({
|
|
141
|
+
kind: "generator-drift",
|
|
142
|
+
severity: "info",
|
|
143
|
+
message: `${count} artifact(s) were generated with SwallowKit ${version}; current version is ${currentVersion}.`,
|
|
144
|
+
expected: currentVersion,
|
|
145
|
+
actual: version,
|
|
146
|
+
repairAction: "Re-run scaffold for affected models to regenerate artifacts with the current generator version.",
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async function detectManifestDrift(projectRoot: string, findings: DriftFinding[]): Promise<void> {
|
|
152
|
+
const stored = readProjectManifest(projectRoot);
|
|
153
|
+
if (!stored) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const { manifest: rebuilt } = await buildProjectManifest(projectRoot);
|
|
158
|
+
const sameEntitySet =
|
|
159
|
+
JSON.stringify(stored.entities.map((entity) => entity.name).sort()) ===
|
|
160
|
+
JSON.stringify(rebuilt.entities.map((entity) => entity.name).sort());
|
|
161
|
+
const sameRouteSet =
|
|
162
|
+
JSON.stringify(stored.routes.map((route) => route.name).sort()) ===
|
|
163
|
+
JSON.stringify(rebuilt.routes.map((route) => route.name).sort());
|
|
164
|
+
|
|
165
|
+
if (!sameEntitySet || !sameRouteSet) {
|
|
166
|
+
findings.push({
|
|
167
|
+
kind: "manifest-drift",
|
|
168
|
+
severity: "warning",
|
|
169
|
+
message: `${SWALLOWKIT_MANIFEST_PATH} does not match the current project structure.`,
|
|
170
|
+
path: SWALLOWKIT_MANIFEST_PATH.replace(/\\/g, "/"),
|
|
171
|
+
repairAction: "Run any SwallowKit generator command (or scaffold) to refresh the project manifest.",
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export async function detectDrift(projectRoot: string = process.cwd()): Promise<DriftDetectionResult> {
|
|
177
|
+
const findings: DriftFinding[] = [];
|
|
178
|
+
const ledger = loadArtifactLedger(projectRoot);
|
|
179
|
+
|
|
180
|
+
if (ledger) {
|
|
181
|
+
for (const record of ledger.artifacts) {
|
|
182
|
+
detectArtifactDrift(record, projectRoot, findings);
|
|
183
|
+
}
|
|
184
|
+
detectSchemaDrift(ledger.artifacts, projectRoot, findings);
|
|
185
|
+
detectGeneratorDrift(ledger.artifacts, findings);
|
|
186
|
+
} else {
|
|
187
|
+
findings.push({
|
|
188
|
+
kind: "artifact-modified",
|
|
189
|
+
severity: "info",
|
|
190
|
+
message: "No artifact ledger (.swallowkit/artifacts.json) found. Artifact-level drift is unknown for this project.",
|
|
191
|
+
repairAction: "Run scaffold (or apply) once with the current SwallowKit version to initialize the artifact ledger.",
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
await detectManifestDrift(projectRoot, findings);
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
ledgerFound: ledger !== null,
|
|
199
|
+
findings: findings.sort((a, b) => a.kind.localeCompare(b.kind) || (a.path ?? "").localeCompare(b.path ?? "")),
|
|
200
|
+
checkedArtifacts: ledger?.artifacts.length ?? 0,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Infra Inspection — infra/ 配下の Bicep 資産を決定論的に解析する。
|
|
3
|
+
* az CLI やネットワークアクセスは一切使わない(純粋なテキスト解析)。
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as fs from "fs";
|
|
7
|
+
import * as path from "path";
|
|
8
|
+
|
|
9
|
+
export interface InfraParam {
|
|
10
|
+
name: string;
|
|
11
|
+
type: string;
|
|
12
|
+
defaultValue?: string;
|
|
13
|
+
description?: string;
|
|
14
|
+
allowed?: string[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface InfraModule {
|
|
18
|
+
symbolicName: string;
|
|
19
|
+
source: string;
|
|
20
|
+
deploymentName?: string;
|
|
21
|
+
condition?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface InfraOutput {
|
|
25
|
+
name: string;
|
|
26
|
+
type: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface InfraContainer {
|
|
30
|
+
file: string;
|
|
31
|
+
/** main.bicep に module として配線済みか */
|
|
32
|
+
wired: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface InfraInspection {
|
|
36
|
+
hasInfraDirectory: boolean;
|
|
37
|
+
mainBicep: {
|
|
38
|
+
exists: boolean;
|
|
39
|
+
path: string;
|
|
40
|
+
params: InfraParam[];
|
|
41
|
+
modules: InfraModule[];
|
|
42
|
+
outputs: InfraOutput[];
|
|
43
|
+
};
|
|
44
|
+
parametersFile: {
|
|
45
|
+
exists: boolean;
|
|
46
|
+
path: string;
|
|
47
|
+
};
|
|
48
|
+
moduleFiles: string[];
|
|
49
|
+
containers: InfraContainer[];
|
|
50
|
+
boundary: {
|
|
51
|
+
zone: "shared";
|
|
52
|
+
note: string;
|
|
53
|
+
};
|
|
54
|
+
warnings: string[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function parseParams(content: string): InfraParam[] {
|
|
58
|
+
const params: InfraParam[] = [];
|
|
59
|
+
const lines = content.split(/\r?\n/);
|
|
60
|
+
let pendingDescription: string | undefined;
|
|
61
|
+
let pendingAllowed: string[] | undefined;
|
|
62
|
+
|
|
63
|
+
for (let i = 0; i < lines.length; i++) {
|
|
64
|
+
const line = lines[i].trim();
|
|
65
|
+
|
|
66
|
+
const descMatch = /^@description\('([^']*)'\)/.exec(line);
|
|
67
|
+
if (descMatch) {
|
|
68
|
+
pendingDescription = descMatch[1];
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (/^@allowed\(/.test(line)) {
|
|
73
|
+
// 単一行 @allowed(['a', 'b']) と複数行の両方に対応
|
|
74
|
+
let block = line;
|
|
75
|
+
let j = i;
|
|
76
|
+
while (!block.includes(")") && j + 1 < lines.length) {
|
|
77
|
+
j++;
|
|
78
|
+
block += "\n" + lines[j].trim();
|
|
79
|
+
}
|
|
80
|
+
i = j;
|
|
81
|
+
pendingAllowed = Array.from(block.matchAll(/'([^']*)'/g)).map((m) => m[1]);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const paramMatch = /^param\s+(\w+)\s+(\w+)(?:\s*=\s*(.+))?$/.exec(line);
|
|
86
|
+
if (paramMatch) {
|
|
87
|
+
params.push({
|
|
88
|
+
name: paramMatch[1],
|
|
89
|
+
type: paramMatch[2],
|
|
90
|
+
...(paramMatch[3] ? { defaultValue: paramMatch[3].trim() } : {}),
|
|
91
|
+
...(pendingDescription ? { description: pendingDescription } : {}),
|
|
92
|
+
...(pendingAllowed ? { allowed: pendingAllowed } : {}),
|
|
93
|
+
});
|
|
94
|
+
pendingDescription = undefined;
|
|
95
|
+
pendingAllowed = undefined;
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (line !== "" && !line.startsWith("@") && !line.startsWith("//")) {
|
|
100
|
+
pendingDescription = undefined;
|
|
101
|
+
pendingAllowed = undefined;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return params;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function parseModules(content: string): InfraModule[] {
|
|
109
|
+
const modules: InfraModule[] = [];
|
|
110
|
+
const moduleRegex = /module\s+(\w+)\s+'([^']+)'\s*=\s*(?:if\s*\(([^)]*)\)\s*)?\{/g;
|
|
111
|
+
for (const match of content.matchAll(moduleRegex)) {
|
|
112
|
+
const rest = content.slice((match.index ?? 0) + match[0].length);
|
|
113
|
+
const nameMatch = /name:\s*'([^']*)'/.exec(rest.slice(0, 200));
|
|
114
|
+
modules.push({
|
|
115
|
+
symbolicName: match[1],
|
|
116
|
+
source: match[2],
|
|
117
|
+
...(nameMatch ? { deploymentName: nameMatch[1] } : {}),
|
|
118
|
+
...(match[3] ? { condition: match[3].trim() } : {}),
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
return modules;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function parseOutputs(content: string): InfraOutput[] {
|
|
125
|
+
const outputs: InfraOutput[] = [];
|
|
126
|
+
for (const match of content.matchAll(/^output\s+(\w+)\s+(\w+)\s*=/gm)) {
|
|
127
|
+
outputs.push({ name: match[1], type: match[2] });
|
|
128
|
+
}
|
|
129
|
+
return outputs;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function listBicepFiles(dir: string): string[] {
|
|
133
|
+
if (!fs.existsSync(dir)) return [];
|
|
134
|
+
return fs
|
|
135
|
+
.readdirSync(dir)
|
|
136
|
+
.filter((f) => f.endsWith(".bicep"))
|
|
137
|
+
.sort();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export function inspectInfra(projectRoot: string = process.cwd()): InfraInspection {
|
|
141
|
+
const infraDir = path.join(projectRoot, "infra");
|
|
142
|
+
const mainBicepPath = path.join(infraDir, "main.bicep");
|
|
143
|
+
const parametersPath = path.join(infraDir, "main.parameters.json");
|
|
144
|
+
const warnings: string[] = [];
|
|
145
|
+
|
|
146
|
+
const hasInfraDirectory = fs.existsSync(infraDir);
|
|
147
|
+
const mainExists = fs.existsSync(mainBicepPath);
|
|
148
|
+
|
|
149
|
+
let params: InfraParam[] = [];
|
|
150
|
+
let modules: InfraModule[] = [];
|
|
151
|
+
let outputs: InfraOutput[] = [];
|
|
152
|
+
let mainContent = "";
|
|
153
|
+
|
|
154
|
+
if (mainExists) {
|
|
155
|
+
mainContent = fs.readFileSync(mainBicepPath, "utf-8");
|
|
156
|
+
params = parseParams(mainContent);
|
|
157
|
+
modules = parseModules(mainContent);
|
|
158
|
+
outputs = parseOutputs(mainContent);
|
|
159
|
+
} else if (hasInfraDirectory) {
|
|
160
|
+
warnings.push("infra/main.bicep not found; provisioning is not possible until it is restored.");
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const moduleFiles = listBicepFiles(path.join(infraDir, "modules")).map((f) => `infra/modules/${f}`);
|
|
164
|
+
|
|
165
|
+
const containers: InfraContainer[] = listBicepFiles(path.join(infraDir, "containers")).map((f) => {
|
|
166
|
+
const wired = mainContent.includes(`containers/${f}`);
|
|
167
|
+
if (!wired) {
|
|
168
|
+
warnings.push(
|
|
169
|
+
`container-not-wired: infra/containers/${f} is not referenced from infra/main.bicep; run scaffold again or wire it manually.`
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
return { file: `infra/containers/${f}`, wired };
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
return {
|
|
176
|
+
hasInfraDirectory,
|
|
177
|
+
mainBicep: {
|
|
178
|
+
exists: mainExists,
|
|
179
|
+
path: "infra/main.bicep",
|
|
180
|
+
params,
|
|
181
|
+
modules,
|
|
182
|
+
outputs,
|
|
183
|
+
},
|
|
184
|
+
parametersFile: {
|
|
185
|
+
exists: fs.existsSync(parametersPath),
|
|
186
|
+
path: "infra/main.parameters.json",
|
|
187
|
+
},
|
|
188
|
+
moduleFiles,
|
|
189
|
+
containers,
|
|
190
|
+
boundary: {
|
|
191
|
+
zone: "shared",
|
|
192
|
+
note: "infra/main.bicep is an extension point: scaffold appends container modules deterministically; humans may edit other sections. infra/containers/*.bicep are managed (regenerated by scaffold).",
|
|
193
|
+
},
|
|
194
|
+
warnings,
|
|
195
|
+
};
|
|
196
|
+
}
|