feat: transition module packages to database ABI v1
CI / verify (pull_request) Successful in 1m1s
CI / infrastructure-smoke (pull_request) Successful in 36s
CI / migration-smoke (pull_request) Successful in 1m54s
CI / image-build (pull_request) Successful in 31s
CI / deploy-smoke (same-origin) (pull_request) Successful in 52s
CI / deploy-smoke (split) (pull_request) Successful in 1m32s
CI / verify (pull_request) Successful in 1m1s
CI / infrastructure-smoke (pull_request) Successful in 36s
CI / migration-smoke (pull_request) Successful in 1m54s
CI / image-build (pull_request) Successful in 31s
CI / deploy-smoke (same-origin) (pull_request) Successful in 52s
CI / deploy-smoke (split) (pull_request) Successful in 1m32s
This commit is contained in:
@@ -3,7 +3,7 @@ import { readFile } from "node:fs/promises";
|
||||
import { isBuiltin } from "node:module";
|
||||
import path from "node:path";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { type ModulePackErrorCode, ModulePackError, packModule } from "../src/index.js";
|
||||
import { ModulePackError, type ModulePackErrorCode, packModule } from "../src/index.js";
|
||||
import {
|
||||
cleanupFixtures,
|
||||
type FixtureModuleOptions,
|
||||
@@ -181,11 +181,12 @@ describe("packModule: a self-contained backend", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("bundles the module-side Drizzle adapter with the module's ORM runtime", async () => {
|
||||
it("bundles ABI-v1 Drizzle as a module detail without transitional runtime metadata", async () => {
|
||||
const { archive } = await pack({
|
||||
databaseAbi: 1,
|
||||
serverSource: `import { moduleDrizzle } from "@ekklesia/module-database/drizzle";
|
||||
export const database = (executor) => moduleDrizzle(executor);
|
||||
export default { id: "example", version: "0.1.0", register() {} };
|
||||
export default { database: { abi: 1 }, id: "example", version: "0.1.0", register() {} };
|
||||
`,
|
||||
withModuleDatabase: true,
|
||||
});
|
||||
@@ -194,7 +195,7 @@ export default { id: "example", version: "0.1.0", register() {} };
|
||||
expect(metadata.incorporates).toMatchObject({
|
||||
"@ekklesia/module-database": expect.any(String),
|
||||
});
|
||||
expect(metadata.transitional).toEqual({ databaseRuntime: { "drizzle-orm": "0.45.2" } });
|
||||
expect(metadata.transitional).toBeUndefined();
|
||||
for (const [file, data] of files) {
|
||||
if (!file.endsWith(".js")) {
|
||||
continue;
|
||||
@@ -213,6 +214,14 @@ export default { id: "example", version: "0.1.0", register() {} };
|
||||
}
|
||||
});
|
||||
|
||||
it("keeps legacy-v8 Drizzle runtime metadata unchanged", async () => {
|
||||
const { archive } = await pack({ withSchema: true });
|
||||
const metadata = JSON.parse(
|
||||
fileEntries(archive).get("example/ekklesia.package.json")?.toString("utf8") ?? "",
|
||||
);
|
||||
expect(metadata.transitional).toEqual({ databaseRuntime: { "drizzle-orm": "0.45.2" } });
|
||||
});
|
||||
|
||||
it("refuses a dependency the bundle cannot resolve rather than leaving it external", async () => {
|
||||
await packRejection(
|
||||
{
|
||||
|
||||
@@ -22,6 +22,7 @@ export interface FakePackage {
|
||||
}
|
||||
|
||||
export interface FixtureModuleOptions {
|
||||
readonly databaseAbi?: 1;
|
||||
readonly id?: string;
|
||||
readonly version?: string;
|
||||
readonly serverSource?: string;
|
||||
@@ -55,19 +56,27 @@ async function write(file: string, contents: string | Uint8Array): Promise<void>
|
||||
const defaultServer = (
|
||||
id: string,
|
||||
version: string,
|
||||
databaseAbi?: 1,
|
||||
) => `import { defineModuleRuntime } from "@ekklesia/module-sdk";
|
||||
export default defineModuleRuntime({ id: ${JSON.stringify(id)}, version: ${JSON.stringify(version)}, register() {} });
|
||||
export default defineModuleRuntime({ ${databaseAbi === 1 ? "database: { abi: 1 }, " : ""}id: ${JSON.stringify(id)}, version: ${JSON.stringify(version)}, register() {} });
|
||||
`;
|
||||
|
||||
const schemaSource = (
|
||||
id: string,
|
||||
) => `import { createModuleTable, defineModuleSchema } from "@ekklesia/module-sdk";
|
||||
const schemaSource = (id: string, databaseAbi?: 1) => `${
|
||||
databaseAbi === 1
|
||||
? 'import { moduleDrizzleSchemaMetadata } from "@ekklesia/module-database/drizzle";'
|
||||
: 'import { defineModuleSchema } from "@ekklesia/module-sdk";'
|
||||
}
|
||||
import { createModuleTable } from "@ekklesia/module-sdk";
|
||||
import { text, uuid } from "drizzle-orm/pg-core";
|
||||
export const records = createModuleTable(${JSON.stringify(id)})("records", {
|
||||
id: uuid("id").primaryKey(),
|
||||
value: text("value").notNull(),
|
||||
});
|
||||
export default defineModuleSchema({ moduleId: ${JSON.stringify(id)}, tables: { records } });
|
||||
export default ${
|
||||
databaseAbi === 1
|
||||
? `moduleDrizzleSchemaMetadata(${JSON.stringify(id)}, { records })`
|
||||
: `defineModuleSchema({ moduleId: ${JSON.stringify(id)}, tables: { records } })`
|
||||
};
|
||||
`;
|
||||
|
||||
export async function fixtureModule(options: FixtureModuleOptions = {}): Promise<string> {
|
||||
@@ -76,6 +85,7 @@ export async function fixtureModule(options: FixtureModuleOptions = {}): Promise
|
||||
const root = await scratchDirectory("ekklesia-pack-fixture-");
|
||||
const moduleDirectory = path.join(root, id);
|
||||
const tableName = `${id.replaceAll("-", "_")}_records`;
|
||||
const usesModuleDatabase = options.withModuleDatabase || options.databaseAbi === 1;
|
||||
|
||||
const baseManifest: Record<string, unknown> = {
|
||||
manifestVersion: 8,
|
||||
@@ -83,6 +93,7 @@ export async function fixtureModule(options: FixtureModuleOptions = {}): Promise
|
||||
name: `${id} module`,
|
||||
version,
|
||||
compatibility: { platform: "^0.1.0", moduleSdk: "^0.1.0" },
|
||||
...(options.databaseAbi === 1 ? { database: { abi: 1 } } : {}),
|
||||
capabilities: [],
|
||||
permissions: [],
|
||||
entrypoints: {
|
||||
@@ -108,7 +119,7 @@ export async function fixtureModule(options: FixtureModuleOptions = {}): Promise
|
||||
scripts: { build: "tsc" },
|
||||
dependencies: {
|
||||
"@ekklesia/module-sdk": "workspace:*",
|
||||
...(options.withModuleDatabase ? { "@ekklesia/module-database": "workspace:*" } : {}),
|
||||
...(usesModuleDatabase ? { "@ekklesia/module-database": "workspace:*" } : {}),
|
||||
"drizzle-orm": "0.45.2",
|
||||
},
|
||||
devDependencies: { typescript: "7.0.2" },
|
||||
@@ -121,12 +132,15 @@ export async function fixtureModule(options: FixtureModuleOptions = {}): Promise
|
||||
);
|
||||
await write(
|
||||
path.join(moduleDirectory, "dist/server/index.js"),
|
||||
options.serverSource ?? defaultServer(id, version),
|
||||
options.serverSource ?? defaultServer(id, version, options.databaseAbi),
|
||||
);
|
||||
await write(path.join(moduleDirectory, "dist/server/index.js.map"), '{"version":3}\n');
|
||||
await write(path.join(moduleDirectory, "dist/server/index.d.ts"), "export {};\n");
|
||||
if (options.withSchema) {
|
||||
await write(path.join(moduleDirectory, "dist/schema/index.js"), schemaSource(id));
|
||||
await write(
|
||||
path.join(moduleDirectory, "dist/schema/index.js"),
|
||||
schemaSource(id, options.databaseAbi),
|
||||
);
|
||||
}
|
||||
await write(path.join(moduleDirectory, "src/server/index.ts"), "export {};\n");
|
||||
await write(path.join(moduleDirectory, "tests/server.test.ts"), "export {};\n");
|
||||
@@ -171,7 +185,7 @@ 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) {
|
||||
if (usesModuleDatabase) {
|
||||
await symlink(
|
||||
await realpath(path.join(packageRoot, "node_modules", "@ekklesia", "module-database")),
|
||||
path.join(modules, "@ekklesia", "module-database"),
|
||||
|
||||
@@ -31,11 +31,14 @@ async function readJson(file: string): Promise<Record<string, unknown>> {
|
||||
return JSON.parse(await readFile(file, "utf8")) as Record<string, unknown>;
|
||||
}
|
||||
|
||||
async function platform() {
|
||||
async function platform(databaseRuntimeVersion?: string) {
|
||||
const core = await readJson(path.join(repositoryRoot, "core", "package.json"));
|
||||
return {
|
||||
databaseRuntime: {
|
||||
"drizzle-orm": (core.dependencies as Record<string, string>)["drizzle-orm"] ?? "",
|
||||
"drizzle-orm":
|
||||
databaseRuntimeVersion ??
|
||||
(core.dependencies as Record<string, string>)["drizzle-orm"] ??
|
||||
"",
|
||||
},
|
||||
moduleSdkVersion: String(
|
||||
(await readJson(path.join(repositoryRoot, "packages", "module-sdk", "package.json"))).version,
|
||||
@@ -96,6 +99,32 @@ describe("the module template as a Module Package v1", () => {
|
||||
expect(await treeListing(template)).toEqual(templateBefore);
|
||||
});
|
||||
|
||||
it("packages the canonical template as ABI v1 with Drizzle private to the module", async () => {
|
||||
const { archive, result } = await packTemplate();
|
||||
const files = fileEntries(archive);
|
||||
const manifest = JSON.parse(files.get("example/ekklesia.module.json")?.toString("utf8") ?? "");
|
||||
const metadata = JSON.parse(files.get("example/ekklesia.package.json")?.toString("utf8") ?? "");
|
||||
|
||||
expect(manifest.database).toEqual({ abi: 1 });
|
||||
expect(metadata.transitional).toBeUndefined();
|
||||
expect(metadata.incorporates).toMatchObject({
|
||||
"@ekklesia/module-database": expect.any(String),
|
||||
});
|
||||
expect(
|
||||
[...files.entries()].some(
|
||||
([file, data]) => file.endsWith(".js") && data.toString("utf8").includes("drizzle-orm"),
|
||||
),
|
||||
).toBe(true);
|
||||
|
||||
await expect(
|
||||
verifyModulePackage({
|
||||
archivePath: result.archivePath,
|
||||
expectedSha256: result.sha256,
|
||||
platform: await platform("99.0.0"),
|
||||
}),
|
||||
).resolves.toMatchObject({ moduleId: "example" });
|
||||
});
|
||||
|
||||
it("verifies against this platform and extracts to an unrelated directory that holds no link", async () => {
|
||||
const { result } = await packTemplate();
|
||||
const target = path.join(await scratchDirectory("ekklesia-installed-elsewhere-"), "example");
|
||||
|
||||
Reference in New Issue
Block a user