feat: add module database ABI v1 adapters
CI / verify (pull_request) Successful in 58s
CI / infrastructure-smoke (pull_request) Successful in 35s
CI / migration-smoke (pull_request) Successful in 1m53s
CI / image-build (pull_request) Successful in 29s
CI / deploy-smoke (same-origin) (pull_request) Successful in 49s
CI / deploy-smoke (split) (pull_request) Successful in 1m31s

This commit is contained in:
ekklesia
2026-10-07 16:44:28 -05:00
parent 4a3958c616
commit 859675c3c7
18 changed files with 957 additions and 7 deletions
+1
View File
@@ -33,6 +33,7 @@
"devDependencies": {
"@biomejs/biome": "2.5.3",
"@ekklesia/core": "workspace:*",
"@ekklesia/module-database": "workspace:*",
"@ekklesia/module-sdk": "workspace:*",
"@ekklesia/module-template": "workspace:*",
"@types/node": "24.13.3",
@@ -181,6 +181,38 @@ describe("packModule: a self-contained backend", () => {
}
});
it("bundles the module-side Drizzle adapter with the module's ORM runtime", async () => {
const { archive } = await pack({
serverSource: `import { moduleDrizzle } from "@ekklesia/module-database/drizzle";
export const database = (executor) => moduleDrizzle(executor);
export default { id: "example", version: "0.1.0", register() {} };
`,
withModuleDatabase: true,
});
const files = fileEntries(archive);
const metadata = JSON.parse(files.get("example/ekklesia.package.json")?.toString("utf8") ?? "");
expect(metadata.incorporates).toMatchObject({
"@ekklesia/module-database": expect.any(String),
});
expect(metadata.transitional).toEqual({ databaseRuntime: { "drizzle-orm": "0.45.2" } });
for (const [file, data] of files) {
if (!file.endsWith(".js")) {
continue;
}
const code = data.toString("utf8");
const specifiers = [
...code.matchAll(/(?:^|\n)\s*(?:import|export)\s[^;]*?from\s*"([^"]+)"/gu),
...code.matchAll(/(?:^|\n)\s*import\s*"([^"]+)"/gu),
...code.matchAll(/\bimport\(\s*"([^"]+)"\s*\)/gu),
].map((match) => match[1] ?? "");
for (const specifier of specifiers) {
expect(specifier.startsWith(".") || isBuiltin(specifier), `${file}: ${specifier}`).toBe(
true,
);
}
}
});
it("refuses a dependency the bundle cannot resolve rather than leaving it external", async () => {
await packRejection(
{
@@ -25,6 +25,7 @@ export interface FixtureModuleOptions {
readonly id?: string;
readonly version?: string;
readonly serverSource?: string;
readonly withModuleDatabase?: boolean;
readonly withSchema?: boolean;
readonly fakePackages?: readonly FakePackage[];
readonly manifest?: (manifest: Record<string, unknown>) => Record<string, unknown>;
@@ -105,7 +106,11 @@ export async function fixtureModule(options: FixtureModuleOptions = {}): Promise
private: true,
type: "module",
scripts: { build: "tsc" },
dependencies: { "@ekklesia/module-sdk": "workspace:*", "drizzle-orm": "0.45.2" },
dependencies: {
"@ekklesia/module-sdk": "workspace:*",
...(options.withModuleDatabase ? { "@ekklesia/module-database": "workspace:*" } : {}),
"drizzle-orm": "0.45.2",
},
devDependencies: { typescript: "7.0.2" },
exports: { ".": "./dist/server/index.js" },
ekklesia: { layer: "module" },
@@ -166,6 +171,12 @@ export async function fixtureModule(options: FixtureModuleOptions = {}): Promise
await realpath(path.join(packageRoot, "node_modules", "@ekklesia", "module-sdk")),
path.join(modules, "@ekklesia", "module-sdk"),
);
if (options.withModuleDatabase) {
await symlink(
await realpath(path.join(packageRoot, "node_modules", "@ekklesia", "module-database")),
path.join(modules, "@ekklesia", "module-database"),
);
}
await symlink(
await realpath(path.join(packageRoot, "node_modules", "drizzle-orm")),
path.join(modules, "drizzle-orm"),