swallowkit 1.1.1 → 2.0.0-alpha.1
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 +47 -7
- package/README.md +47 -7
- package/dist/cli/commands/add-auth.d.ts.map +1 -1
- package/dist/cli/commands/add-auth.js +66 -21
- package/dist/cli/commands/add-auth.js.map +1 -1
- package/dist/cli/commands/dev.d.ts +4 -1
- package/dist/cli/commands/dev.d.ts.map +1 -1
- package/dist/cli/commands/dev.js +108 -44
- package/dist/cli/commands/dev.js.map +1 -1
- package/dist/cli/commands/init.d.ts +6 -5
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +83 -39
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/scaffold.d.ts +1 -1
- package/dist/cli/commands/scaffold.d.ts.map +1 -1
- package/dist/cli/commands/scaffold.js +44 -16
- package/dist/cli/commands/scaffold.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +6 -3
- package/dist/cli/index.js.map +1 -1
- package/dist/core/config.d.ts +2 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +19 -0
- package/dist/core/config.js.map +1 -1
- package/dist/core/operations/migration-generator.d.ts +1 -1
- package/dist/core/operations/migration-generator.d.ts.map +1 -1
- package/dist/core/operations/migration-generator.js +22 -0
- package/dist/core/operations/migration-generator.js.map +1 -1
- package/dist/core/project/agent-instructions.d.ts +2 -1
- package/dist/core/project/agent-instructions.d.ts.map +1 -1
- package/dist/core/project/agent-instructions.js +12 -0
- package/dist/core/project/agent-instructions.js.map +1 -1
- package/dist/core/project/capabilities.d.ts +7 -0
- package/dist/core/project/capabilities.d.ts.map +1 -1
- package/dist/core/project/capabilities.js +7 -0
- package/dist/core/project/capabilities.js.map +1 -1
- package/dist/core/project/manifest.d.ts +3 -1
- package/dist/core/project/manifest.d.ts.map +1 -1
- package/dist/core/project/manifest.js +47 -25
- package/dist/core/project/manifest.js.map +1 -1
- package/dist/core/project/react-project.d.ts +26 -0
- package/dist/core/project/react-project.d.ts.map +1 -0
- package/dist/core/project/react-project.js +422 -0
- package/dist/core/project/react-project.js.map +1 -0
- package/dist/core/project/validation.d.ts.map +1 -1
- package/dist/core/project/validation.js +32 -10
- package/dist/core/project/validation.js.map +1 -1
- package/dist/core/scaffold/react-bff-generator.d.ts +10 -0
- package/dist/core/scaffold/react-bff-generator.d.ts.map +1 -0
- package/dist/core/scaffold/react-bff-generator.js +252 -0
- package/dist/core/scaffold/react-bff-generator.js.map +1 -0
- package/dist/core/scaffold/react-ui-generator.d.ts +6 -0
- package/dist/core/scaffold/react-ui-generator.d.ts.map +1 -0
- package/dist/core/scaffold/react-ui-generator.js +41 -0
- package/dist/core/scaffold/react-ui-generator.js.map +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/machine/index.js +2 -2
- package/dist/machine/index.js.map +1 -1
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/package-manager.d.ts +2 -2
- package/dist/utils/package-manager.d.ts.map +1 -1
- package/dist/utils/package-manager.js +4 -1
- package/dist/utils/package-manager.js.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/agent-instructions.test.ts +1 -1
- package/src/__tests__/init.test.ts +29 -5
- package/src/__tests__/plan-apply.test.ts +101 -0
- package/src/__tests__/react.test.ts +819 -0
- package/src/__tests__/release-metadata.test.ts +121 -0
- package/src/cli/commands/add-auth.ts +67 -23
- package/src/cli/commands/dev.ts +109 -46
- package/src/cli/commands/init.ts +75 -35
- package/src/cli/commands/scaffold.ts +45 -20
- package/src/cli/index.ts +6 -3
- package/src/core/config.ts +21 -1
- package/src/core/operations/migration-generator.ts +35 -1
- package/src/core/project/agent-instructions.ts +14 -1
- package/src/core/project/agent-templates/AGENTS.md +24 -9
- package/src/core/project/agent-templates/bff-routes.instructions.md +5 -3
- package/src/core/project/capabilities.ts +14 -0
- package/src/core/project/manifest.ts +52 -27
- package/src/core/project/react-project.ts +507 -0
- package/src/core/project/validation.ts +32 -10
- package/src/core/scaffold/react-bff-generator.ts +292 -0
- package/src/core/scaffold/react-ui-generator.ts +51 -0
- package/src/index.ts +3 -1
- package/src/machine/index.ts +2 -2
- package/src/types/index.ts +4 -0
- package/src/utils/package-manager.ts +5 -2
|
@@ -40,6 +40,8 @@ export default nextConfig;
|
|
|
40
40
|
const updated = injectSwallowKitNextConfig(original, "sample-app");
|
|
41
41
|
|
|
42
42
|
expect(updated).toContain("output: 'standalone'");
|
|
43
|
+
expect(updated).toContain("cacheComponents: false");
|
|
44
|
+
expect(updated).not.toContain("partialPrefetching");
|
|
43
45
|
expect(updated).toContain("agentRules: false");
|
|
44
46
|
expect(updated).toContain("transpilePackages: ['@sample-app/shared']");
|
|
45
47
|
expect(updated).toContain("serverExternalPackages: ['applicationinsights', 'diagnostic-channel-publishers']");
|
|
@@ -60,6 +62,26 @@ module.exports = nextConfig;
|
|
|
60
62
|
expect(updated).toContain("transpilePackages: ['@sample-app/shared']");
|
|
61
63
|
expect(updated).toContain("agentRules: false");
|
|
62
64
|
expect(updated).toContain("module.exports = nextConfig;");
|
|
65
|
+
expect(updated).toContain("cacheComponents: false");
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it.each([true, false])("disables create-next-app Cache Components without duplicate keys (initially %s)", (enabled) => {
|
|
69
|
+
const original = `import type { NextConfig } from 'next';
|
|
70
|
+
const nextConfig: NextConfig = {
|
|
71
|
+
cacheComponents: ${enabled},
|
|
72
|
+
partialPrefetching: true,
|
|
73
|
+
reactCompiler: true,
|
|
74
|
+
};
|
|
75
|
+
export default nextConfig;
|
|
76
|
+
`;
|
|
77
|
+
const updated = injectSwallowKitNextConfig(original, "sample-app");
|
|
78
|
+
expect(updated).toContain("cacheComponents: false");
|
|
79
|
+
expect(updated).not.toContain("cacheComponents: true");
|
|
80
|
+
expect(updated).toContain("partialPrefetching: false");
|
|
81
|
+
expect(updated.match(/\bpartialPrefetching\s*:/g)).toHaveLength(1);
|
|
82
|
+
expect(updated.match(/\bcacheComponents\s*:/g)).toHaveLength(1);
|
|
83
|
+
expect(updated).toContain("reactCompiler: true");
|
|
84
|
+
expect(updated).toContain("output: 'standalone'");
|
|
63
85
|
});
|
|
64
86
|
|
|
65
87
|
it("generates a C# Program.cs compatible with the current worker packages", () => {
|
|
@@ -411,11 +433,13 @@ describe("SWA npm compatibility with pnpm projects", () => {
|
|
|
411
433
|
scripts: { prestart: "pnpm run build" },
|
|
412
434
|
});
|
|
413
435
|
const env = { ...process.env, npm_config_cache: path.join(dir, "cache") };
|
|
436
|
+
const npmCommand = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
437
|
+
const npmOptions = { cwd: dir, env, encoding: "utf8" as const, shell: process.platform === "win32" };
|
|
414
438
|
// setup-node's npm cache lookup is an npm command too, before any install.
|
|
415
|
-
const cacheBefore = spawnSync(
|
|
439
|
+
const cacheBefore = spawnSync(npmCommand, ["config", "get", "cache"], npmOptions);
|
|
416
440
|
expect(cacheBefore.status).not.toBe(0);
|
|
417
441
|
expect(cacheBefore.stderr).toContain("EBADDEVENGINES");
|
|
418
|
-
const before = spawnSync(
|
|
442
|
+
const before = spawnSync(npmCommand, ["run", "probe"], npmOptions);
|
|
419
443
|
expect(before.status).not.toBe(0);
|
|
420
444
|
expect(before.stderr).toContain("EBADDEVENGINES");
|
|
421
445
|
|
|
@@ -430,7 +454,7 @@ describe("SWA npm compatibility with pnpm projects", () => {
|
|
|
430
454
|
}
|
|
431
455
|
const runtimeCommand = workflow.match(/node -e "const fs=require\('fs'\);const p=JSON.parse[^\n]+/);
|
|
432
456
|
expect(runtimeCommand).not.toBeNull();
|
|
433
|
-
execFileSync(
|
|
457
|
+
execFileSync(process.execPath, ["-e", runtimeCommand![0].slice('node -e "'.length, -1)], { cwd: dir });
|
|
434
458
|
const match = workflow.match(/node <<'NODE'\n([\s\S]*?)\n\s+NODE/);
|
|
435
459
|
expect(match).not.toBeNull();
|
|
436
460
|
execFileSync(process.execPath, ["-e", match![1]], { cwd: dir });
|
|
@@ -441,9 +465,9 @@ describe("SWA npm compatibility with pnpm projects", () => {
|
|
|
441
465
|
expect(normalized.devEngines.runtime.version).toBe(">=22.14.0");
|
|
442
466
|
expect(normalized.workspaces).toEqual(hasFunctions ? ["shared", "functions"] : ["shared"]);
|
|
443
467
|
expect(normalized.dependencies["@sample/shared"]).toBe("file:shared");
|
|
444
|
-
const cacheAfter = execFileSync(
|
|
468
|
+
const cacheAfter = execFileSync(npmCommand, ["config", "get", "cache"], npmOptions);
|
|
445
469
|
expect(cacheAfter.trim()).toBe(env.npm_config_cache);
|
|
446
|
-
const after = execFileSync(
|
|
470
|
+
const after = execFileSync(npmCommand, ["run", "probe"], npmOptions);
|
|
447
471
|
expect(after).toContain("npm/");
|
|
448
472
|
} finally {
|
|
449
473
|
fs.rmSync(dir, { recursive: true, force: true });
|
|
@@ -83,6 +83,19 @@ function createInfrastructureFixture(rootDir: string): void {
|
|
|
83
83
|
writeFile(path.join(rootDir, "infra", "modules", "cosmosdb.bicep"), "// baseline cosmos\n");
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
function addLegacyContainerToInfrastructure(rootDir: string, modelKebab: string): void {
|
|
87
|
+
const containerPath = `containers/${modelKebab}-container.bicep`;
|
|
88
|
+
fs.appendFileSync(
|
|
89
|
+
path.join(rootDir, "infra", "main.bicep"),
|
|
90
|
+
`\nmodule ${modelKebab.replace(/-([a-z])/g, (_, letter: string) => letter.toUpperCase())}Container '${containerPath}' = {\n name: '${modelKebab}-container'\n}\n`,
|
|
91
|
+
"utf-8"
|
|
92
|
+
);
|
|
93
|
+
writeFile(
|
|
94
|
+
path.join(rootDir, "infra", containerPath),
|
|
95
|
+
"param partitionKeyPath string = '/id'\n\nresource container 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers@2023-11-15' = {\n name: 'Todos'\n}\n"
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
86
99
|
function createNativeBackendFixture(rootDir: string, backendLanguage: "csharp" | "python"): void {
|
|
87
100
|
createProjectFixture(rootDir);
|
|
88
101
|
writeFile(
|
|
@@ -254,6 +267,94 @@ describe("plan / apply / drift / verify machine commands", () => {
|
|
|
254
267
|
]);
|
|
255
268
|
});
|
|
256
269
|
|
|
270
|
+
it("updates baseline-owned model artifacts without creating a second container migration", async () => {
|
|
271
|
+
createNativeBackendFixture(tempDir, "csharp");
|
|
272
|
+
writeFile(path.join(tempDir, "shared", "models", "answer-record.ts"), createModelSource("AnswerRecord"));
|
|
273
|
+
createInfrastructureFixture(tempDir);
|
|
274
|
+
addLegacyContainerToInfrastructure(tempDir, "answer-record");
|
|
275
|
+
const original = initializeLegacyMigrations(tempDir);
|
|
276
|
+
const restoreCodegenMocks = mockSuccessfulNativeCodegen();
|
|
277
|
+
|
|
278
|
+
try {
|
|
279
|
+
const baselineModelApply = await runMachine(machineArgs("apply", "scaffold", "answer-record"));
|
|
280
|
+
expect(baselineModelApply.exitCode).toBe(0);
|
|
281
|
+
expect(loadMigrationManifest(tempDir).migrations).toEqual([]);
|
|
282
|
+
|
|
283
|
+
writeFile(
|
|
284
|
+
path.join(tempDir, "shared", "models", "written-mock-session.ts"),
|
|
285
|
+
createModelSource("WrittenMockSession")
|
|
286
|
+
);
|
|
287
|
+
const newModelApply = await runMachine(machineArgs("apply", "scaffold", "written-mock-session"));
|
|
288
|
+
expect(newModelApply.exitCode).toBe(0);
|
|
289
|
+
expect(loadMigrationManifest(tempDir).migrations).toEqual([
|
|
290
|
+
expect.objectContaining({
|
|
291
|
+
version: 1,
|
|
292
|
+
slug: "create-written-mock-session-container",
|
|
293
|
+
sourceModel: "WrittenMockSession",
|
|
294
|
+
}),
|
|
295
|
+
]);
|
|
296
|
+
|
|
297
|
+
writeFile(
|
|
298
|
+
path.join(tempDir, "shared", "models", "answer-record.ts"),
|
|
299
|
+
createModelSource("AnswerRecord", "\n writtenMockSessionId: z.string().optional(),")
|
|
300
|
+
);
|
|
301
|
+
const plan = await runMachine(machineArgs("plan", "scaffold", "answer-record"));
|
|
302
|
+
|
|
303
|
+
expect(plan.exitCode).toBe(0);
|
|
304
|
+
expect(plan.response.ok).toBe(true);
|
|
305
|
+
expect(plan.response.data.operations).toEqual(expect.arrayContaining([
|
|
306
|
+
expect.objectContaining({ path: "functions/openapi/answer-record.openapi.json", action: "update" }),
|
|
307
|
+
expect.objectContaining({
|
|
308
|
+
path: "functions/generated/csharp-models/src/SwallowKitBackendModels/Model/AnswerRecord.cs",
|
|
309
|
+
action: "update",
|
|
310
|
+
}),
|
|
311
|
+
expect.objectContaining({ path: "app/answer-record/_components/AnswerRecordForm.tsx", action: "update" }),
|
|
312
|
+
]));
|
|
313
|
+
expect(plan.response.data.operations).not.toEqual(expect.arrayContaining([
|
|
314
|
+
expect.objectContaining({ path: expect.stringContaining("infra/migrations/0002-") }),
|
|
315
|
+
]));
|
|
316
|
+
expect(plan.response.data.operations).not.toEqual(expect.arrayContaining([
|
|
317
|
+
expect.objectContaining({ path: MIGRATION_MANIFEST_PATH.replace(/\\/g, "/") }),
|
|
318
|
+
]));
|
|
319
|
+
|
|
320
|
+
const apply = await runMachine(machineArgs("apply", "scaffold", "--plan", plan.response.data.planId));
|
|
321
|
+
expect(apply.exitCode).toBe(0);
|
|
322
|
+
const updated = loadMigrationManifest(tempDir);
|
|
323
|
+
expect(updated.migrations).toHaveLength(1);
|
|
324
|
+
expect(updated.baseline).toEqual(original.baseline);
|
|
325
|
+
|
|
326
|
+
const verify = await runMachine(machineArgs("verify", "project", "--checks", "structure,drift"));
|
|
327
|
+
expect(verify.exitCode).toBe(0);
|
|
328
|
+
expect(verify.response.ok).toBe(true);
|
|
329
|
+
expect(verify.response.data.summary.done).toBe(true);
|
|
330
|
+
} finally {
|
|
331
|
+
restoreCodegenMocks();
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
it("requires an explicit migration when a baseline-owned container partition key changes", async () => {
|
|
336
|
+
createProjectFixture(tempDir);
|
|
337
|
+
writeFile(
|
|
338
|
+
path.join(tempDir, "shared", "models", "todo.ts"),
|
|
339
|
+
`${createModelSource("Todo")}\nexport const partitionKey = '/tenantId';\n`
|
|
340
|
+
);
|
|
341
|
+
createInfrastructureFixture(tempDir);
|
|
342
|
+
addLegacyContainerToInfrastructure(tempDir, "todo");
|
|
343
|
+
initializeLegacyMigrations(tempDir);
|
|
344
|
+
|
|
345
|
+
const plan = await runMachine(machineArgs("plan", "scaffold", "todo", "--api-only"));
|
|
346
|
+
|
|
347
|
+
expect(plan.exitCode).toBe(1);
|
|
348
|
+
expect(plan.response.ok).toBe(false);
|
|
349
|
+
expect(plan.response.status).toBe("blocked");
|
|
350
|
+
expect(plan.response.error.code).toBe("baseline-container-configuration-change");
|
|
351
|
+
expect(plan.response.error.details).toEqual(expect.objectContaining({
|
|
352
|
+
baselinePartitionKey: "/id",
|
|
353
|
+
requestedPartitionKey: "/tenantId",
|
|
354
|
+
}));
|
|
355
|
+
expect(fs.existsSync(path.join(tempDir, "infra", "migrations", "0001-create-todo-container"))).toBe(false);
|
|
356
|
+
});
|
|
357
|
+
|
|
257
358
|
it("uses the same approval-free manifest update for a non-legacy project", async () => {
|
|
258
359
|
createProjectFixture(tempDir);
|
|
259
360
|
createInfrastructureFixture(tempDir);
|