Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,45 @@
import { spawnSync } from "node:child_process";
import { mkdtempSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { describe, expect, it } from "@suaveplan/testing/runner";
import { scaffold } from "../../src/generator/generator.js";
import { answersForFixture } from "../../src/prompt/prompt.js";
type Fixture =
| "trusted-postgres-full"
| "isolated-sqlite-rest-only"
| "sandboxed-none-rest-only";
const FIXTURES: readonly Fixture[] = [
"trusted-postgres-full",
"isolated-sqlite-rest-only",
"sandboxed-none-rest-only",
] as const;
describe("scaffold-output: every fixture passes bun test on first run", () => {
for (const fixture of FIXTURES) {
it(`every fixture passes bun test on first run :: ${fixture}`, {
timeout: 30000,
}, () => {
const root = mkdtempSync(join(tmpdir(), `cspp-bun-${fixture}-`));
try {
const answers = answersForFixture(fixture, "demo-plugin");
scaffold({ answers, targetDir: root, force: true });
const result = spawnSync("bun", ["test"], {
cwd: root,
encoding: "utf8",
env: { ...process.env, INTEGRATION: "0" },
});
if (result.status !== 0) {
// Surface output to ease debugging when the gate flips.
// eslint-disable-next-line no-console
console.error(result.stdout, result.stderr);
}
expect(result.status).toBe(0);
} finally {
rmSync(root, { recursive: true, force: true });
}
});
}
});

View File

@@ -0,0 +1,128 @@
import {
existsSync,
mkdtempSync,
readFileSync,
rmSync,
statSync,
} from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { describe, expect, it } from "@suaveplan/testing/runner";
import { SCAFFOLD_CODES, ScaffoldError } from "../../src/error-codes.js";
import { scaffold } from "../../src/generator/generator.js";
import { answersForFixture } from "../../src/prompt/prompt.js";
import { renderTree } from "../../src/templates/templates.js";
const FIXTURE_BY_DIR: Record<
string,
| "trusted-postgres-full"
| "isolated-sqlite-rest-only"
| "sandboxed-none-rest-only"
> = {
"trusted-postgres-full": "trusted-postgres-full",
"isolated-sqlite-rest-only": "isolated-sqlite-rest-only",
"sandboxed-none-rest-only": "sandboxed-none-rest-only",
};
const SNAPSHOT_ROOT = join(import.meta.dirname, "snapshots");
function makeTmp(prefix: string): string {
return mkdtempSync(join(tmpdir(), `${prefix}-`));
}
function readSnapshot(fixtureDir: string, relPath: string): string {
return readFileSync(join(SNAPSHOT_ROOT, fixtureDir, relPath), "utf8");
}
for (const [snapshotDir, fixture] of Object.entries(FIXTURE_BY_DIR)) {
describe(`scaffold-output: ${snapshotDir}`, () => {
it(`${snapshotDir} snapshot match`, { timeout: 30000 }, () => {
const files = renderTree(answersForFixture(fixture, "demo-plugin"));
for (const f of files) {
const expected = readSnapshot(snapshotDir, f.path);
expect(f.content).toBe(expected);
}
});
});
}
describe("scaffold-output: sandboxed-none-rest-only emits no integration / migrations", () => {
it("directories omitted when DB is none", { timeout: 5000 }, () => {
const files = renderTree(
answersForFixture("sandboxed-none-rest-only", "demo-plugin")
);
expect(files.some((f) => f.path.startsWith("tests/integration"))).toBe(
false
);
expect(files.some((f) => f.path.startsWith("migrations/"))).toBe(false);
});
});
describe("scaffold-output: vitest configs use createTestConfig", () => {
it("generated vitest configs use createTestConfig", {
timeout: 5000,
}, () => {
const files = renderTree(
answersForFixture("trusted-postgres-full", "demo-plugin")
);
const cfg = files.find((f) => f.path === "vitest.config.ts");
expect(cfg?.content.includes("@suaveplan/testing/config")).toBe(true);
expect(cfg?.content.includes("createTestConfig")).toBe(true);
expect(cfg?.content.includes("baseVitestConfig")).toBe(false);
const cfgI = files.find(
(f) => f.path === "vitest.integration.config.ts"
);
expect(cfgI?.content.includes("createTestConfig")).toBe(true);
});
});
describe("scaffold-output: postgres selection wires testing-integration-postgres", () => {
it("postgres selection wires testing-integration-postgres", {
timeout: 5000,
}, () => {
const files = renderTree(
answersForFixture("trusted-postgres-full", "demo-plugin")
);
const it_ = files.find(
(f) => f.path === "tests/integration/example.integration.test.ts"
);
expect(
it_?.content.includes("@suaveplan/testing-integration-postgres")
).toBe(true);
expect(it_?.content.includes("INTEGRATION")).toBe(true);
});
});
describe("scaffold-output: pre-existing directory refuses without force", () => {
it("pre-existing directory raises without force", { timeout: 5000 }, () => {
const root = makeTmp("scaffold-out-exists");
const answers = answersForFixture("sandboxed-none-rest-only", "demo");
// First write succeeds.
scaffold({ answers, targetDir: root, force: true });
// Second write without --force raises.
try {
expect(() => scaffold({ answers, targetDir: root })).toThrowError(
ScaffoldError
);
try {
scaffold({ answers, targetDir: root });
} catch (err) {
expect((err as ScaffoldError).code).toBe(
SCAFFOLD_CODES.DIRECTORY_EXISTS
);
}
} finally {
rmSync(root, { recursive: true, force: true });
}
});
it("--force overwrites prior files", { timeout: 5000 }, () => {
const root = makeTmp("scaffold-out-force");
const answers = answersForFixture("sandboxed-none-rest-only", "demo");
scaffold({ answers, targetDir: root, force: true });
scaffold({ answers, targetDir: root, force: true });
expect(existsSync(join(root, "plugin.json"))).toBe(true);
expect(statSync(join(root, "plugin.json")).isFile()).toBe(true);
rmSync(root, { recursive: true, force: true });
});
});

View File

@@ -0,0 +1,5 @@
node_modules/
dist/
coverage/
*.log
.DS_Store

View File

@@ -0,0 +1,16 @@
# demo-plugin
Suaveplan API plugin generated by `create-suaveplan-plugin`.
- **Isolation**: `isolated`
- **Database**: `sqlite`
- **Capabilities**: `rest`
## Getting started
```bash
bun install
bun test
```
Replace the stub handlers in `src/index.ts` with the real plugin implementation.

View File

@@ -0,0 +1,5 @@
-- 0001 — initial schema for demo-plugin
CREATE TABLE IF NOT EXISTS demo_plugin_state (
id TEXT PRIMARY KEY,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);

View File

@@ -0,0 +1,14 @@
{
"name": "demo-plugin",
"version": "0.0.0",
"type": "module",
"private": true,
"scripts": {
"test": "bun test",
"test:coverage": "bun test --coverage",
"lint": "bunx biome check src/",
"typecheck": "bunx tsc --noEmit",
"build": "echo 'configure your build pipeline here'",
"test:integration": "vitest run -c vitest.integration.config.ts"
}
}

View File

@@ -0,0 +1,9 @@
{
"name": "demo-plugin",
"version": "0.0.0",
"isolation": "isolated",
"database": "sqlite",
"capabilities": [
"rest"
]
}

View File

@@ -0,0 +1,16 @@
import { describe, expect, it } from "bun:test";
import { handlers, manifest } from "./index.ts";
describe("demo-plugin — scaffold smoke", () => {
it("manifest reports the configured isolation tier", () => {
expect(manifest.isolation).toBe("isolated");
});
it("manifest reports the configured database engine", () => {
expect(manifest.database).toBe("sqlite");
});
it("wires the rest capability", async () => {
const out = await handlers.rest();
expect(out.ok).toBe(true);
expect(out.capability).toBe("rest");
});
});

View File

@@ -0,0 +1,20 @@
// Generated by create-suaveplan-plugin.
// Replace the body below with the real plugin implementation.
export interface PluginManifest {
readonly name: string;
readonly isolation: "isolated";
readonly database: "sqlite";
readonly capabilities: readonly string[];
}
export const manifest: PluginManifest = Object.freeze({
name: "demo-plugin",
isolation: "isolated",
database: "sqlite",
capabilities: Object.freeze(["rest"]),
});
export const handlers = Object.freeze({
rest: async () => ({ ok: true as const, capability: "rest" }),
});

View File

@@ -0,0 +1,11 @@
import { describe, expect, it } from "vitest";
// Real-service fixture from @suaveplan/testing-integration-sqlite.
// import { createSqliteFixture } from "@suaveplan/testing-integration-sqlite";
const enabled = process.env["INTEGRATION"] === "1";
(enabled ? describe : describe.skip)("demo-plugin — integration", () => {
it("placeholder — replace with a real-service assertion", () => {
expect(true).toBe(true);
});
});

View File

@@ -0,0 +1,16 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"noUncheckedIndexedAccess": true,
"verbatimModuleSyntax": true,
"isolatedModules": true,
"esModuleInterop": true,
"skipLibCheck": true,
"types": ["bun-types"]
},
"include": ["src/**/*.ts", "tests/**/*.ts"]
}

View File

@@ -0,0 +1,3 @@
import { createTestConfig } from "@suaveplan/testing/config";
export default createTestConfig({});

View File

@@ -0,0 +1,6 @@
import { createTestConfig } from "@suaveplan/testing/config";
export default createTestConfig({
additionalIncludes: ["tests/integration/**/*.integration.test.ts"],
testTimeout: 60_000,
});

View File

@@ -0,0 +1,5 @@
node_modules/
dist/
coverage/
*.log
.DS_Store

View File

@@ -0,0 +1,16 @@
# demo-plugin
Suaveplan API plugin generated by `create-suaveplan-plugin`.
- **Isolation**: `sandboxed`
- **Database**: `none`
- **Capabilities**: `rest`
## Getting started
```bash
bun install
bun test
```
Replace the stub handlers in `src/index.ts` with the real plugin implementation.

View File

@@ -0,0 +1,13 @@
{
"name": "demo-plugin",
"version": "0.0.0",
"type": "module",
"private": true,
"scripts": {
"test": "bun test",
"test:coverage": "bun test --coverage",
"lint": "bunx biome check src/",
"typecheck": "bunx tsc --noEmit",
"build": "echo 'configure your build pipeline here'"
}
}

View File

@@ -0,0 +1,9 @@
{
"name": "demo-plugin",
"version": "0.0.0",
"isolation": "sandboxed",
"database": "none",
"capabilities": [
"rest"
]
}

View File

@@ -0,0 +1,16 @@
import { describe, expect, it } from "bun:test";
import { handlers, manifest } from "./index.ts";
describe("demo-plugin — scaffold smoke", () => {
it("manifest reports the configured isolation tier", () => {
expect(manifest.isolation).toBe("sandboxed");
});
it("manifest reports the configured database engine", () => {
expect(manifest.database).toBe("none");
});
it("wires the rest capability", async () => {
const out = await handlers.rest();
expect(out.ok).toBe(true);
expect(out.capability).toBe("rest");
});
});

View File

@@ -0,0 +1,20 @@
// Generated by create-suaveplan-plugin.
// Replace the body below with the real plugin implementation.
export interface PluginManifest {
readonly name: string;
readonly isolation: "sandboxed";
readonly database: "none";
readonly capabilities: readonly string[];
}
export const manifest: PluginManifest = Object.freeze({
name: "demo-plugin",
isolation: "sandboxed",
database: "none",
capabilities: Object.freeze(["rest"]),
});
export const handlers = Object.freeze({
rest: async () => ({ ok: true as const, capability: "rest" }),
});

View File

@@ -0,0 +1,16 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"noUncheckedIndexedAccess": true,
"verbatimModuleSyntax": true,
"isolatedModules": true,
"esModuleInterop": true,
"skipLibCheck": true,
"types": ["bun-types"]
},
"include": ["src/**/*.ts", "tests/**/*.ts"]
}

View File

@@ -0,0 +1,3 @@
import { createTestConfig } from "@suaveplan/testing/config";
export default createTestConfig({});

View File

@@ -0,0 +1,5 @@
node_modules/
dist/
coverage/
*.log
.DS_Store

View File

@@ -0,0 +1,16 @@
# demo-plugin
Suaveplan API plugin generated by `create-suaveplan-plugin`.
- **Isolation**: `trusted`
- **Database**: `postgres`
- **Capabilities**: `rest`, `graphql`, `ws`, `sse`, `sagas`, `twoPhaseCommit`, `perTenantJobs`
## Getting started
```bash
bun install
bun test
```
Replace the stub handlers in `src/index.ts` with the real plugin implementation.

View File

@@ -0,0 +1,5 @@
-- 0001 — initial schema for demo-plugin
CREATE TABLE IF NOT EXISTS demo_plugin_state (
id TEXT PRIMARY KEY,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

View File

@@ -0,0 +1,14 @@
{
"name": "demo-plugin",
"version": "0.0.0",
"type": "module",
"private": true,
"scripts": {
"test": "bun test",
"test:coverage": "bun test --coverage",
"lint": "bunx biome check src/",
"typecheck": "bunx tsc --noEmit",
"build": "echo 'configure your build pipeline here'",
"test:integration": "vitest run -c vitest.integration.config.ts"
}
}

View File

@@ -0,0 +1,15 @@
{
"name": "demo-plugin",
"version": "0.0.0",
"isolation": "trusted",
"database": "postgres",
"capabilities": [
"rest",
"graphql",
"ws",
"sse",
"sagas",
"twoPhaseCommit",
"perTenantJobs"
]
}

View File

@@ -0,0 +1,46 @@
import { describe, expect, it } from "bun:test";
import { handlers, manifest } from "./index.ts";
describe("demo-plugin — scaffold smoke", () => {
it("manifest reports the configured isolation tier", () => {
expect(manifest.isolation).toBe("trusted");
});
it("manifest reports the configured database engine", () => {
expect(manifest.database).toBe("postgres");
});
it("wires the rest capability", async () => {
const out = await handlers.rest();
expect(out.ok).toBe(true);
expect(out.capability).toBe("rest");
});
it("wires the graphql capability", async () => {
const out = await handlers.graphql();
expect(out.ok).toBe(true);
expect(out.capability).toBe("graphql");
});
it("wires the ws capability", async () => {
const out = await handlers.ws();
expect(out.ok).toBe(true);
expect(out.capability).toBe("ws");
});
it("wires the sse capability", async () => {
const out = await handlers.sse();
expect(out.ok).toBe(true);
expect(out.capability).toBe("sse");
});
it("wires the sagas capability", async () => {
const out = await handlers.sagas();
expect(out.ok).toBe(true);
expect(out.capability).toBe("sagas");
});
it("wires the twoPhaseCommit capability", async () => {
const out = await handlers.twoPhaseCommit();
expect(out.ok).toBe(true);
expect(out.capability).toBe("twoPhaseCommit");
});
it("wires the perTenantJobs capability", async () => {
const out = await handlers.perTenantJobs();
expect(out.ok).toBe(true);
expect(out.capability).toBe("perTenantJobs");
});
});

View File

@@ -0,0 +1,26 @@
// Generated by create-suaveplan-plugin.
// Replace the body below with the real plugin implementation.
export interface PluginManifest {
readonly name: string;
readonly isolation: "trusted";
readonly database: "postgres";
readonly capabilities: readonly string[];
}
export const manifest: PluginManifest = Object.freeze({
name: "demo-plugin",
isolation: "trusted",
database: "postgres",
capabilities: Object.freeze(["rest", "graphql", "ws", "sse", "sagas", "twoPhaseCommit", "perTenantJobs"]),
});
export const handlers = Object.freeze({
rest: async () => ({ ok: true as const, capability: "rest" }),
graphql: async () => ({ ok: true as const, capability: "graphql" }),
ws: async () => ({ ok: true as const, capability: "ws" }),
sse: async () => ({ ok: true as const, capability: "sse" }),
sagas: async () => ({ ok: true as const, capability: "sagas" }),
twoPhaseCommit: async () => ({ ok: true as const, capability: "twoPhaseCommit" }),
perTenantJobs: async () => ({ ok: true as const, capability: "perTenantJobs" }),
});

View File

@@ -0,0 +1,11 @@
import { describe, expect, it } from "vitest";
// Real-service fixture from @suaveplan/testing-integration-postgres.
// import { createPostgresFixture } from "@suaveplan/testing-integration-postgres";
const enabled = process.env["INTEGRATION"] === "1";
(enabled ? describe : describe.skip)("demo-plugin — integration", () => {
it("placeholder — replace with a real-service assertion", () => {
expect(true).toBe(true);
});
});

View File

@@ -0,0 +1,16 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"noUncheckedIndexedAccess": true,
"verbatimModuleSyntax": true,
"isolatedModules": true,
"esModuleInterop": true,
"skipLibCheck": true,
"types": ["bun-types"]
},
"include": ["src/**/*.ts", "tests/**/*.ts"]
}

View File

@@ -0,0 +1,3 @@
import { createTestConfig } from "@suaveplan/testing/config";
export default createTestConfig({});

View File

@@ -0,0 +1,6 @@
import { createTestConfig } from "@suaveplan/testing/config";
export default createTestConfig({
additionalIncludes: ["tests/integration/**/*.integration.test.ts"],
testTimeout: 60_000,
});