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
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:
@@ -0,0 +1,85 @@
|
||||
import type {
|
||||
ModuleDatabaseExecutor,
|
||||
ModuleDatabaseParameter,
|
||||
ModuleDatabaseQueryable,
|
||||
ModuleDatabaseTransaction,
|
||||
} from "@ekklesia/contracts";
|
||||
import { drizzle, type PgRemoteDatabase } from "drizzle-orm/pg-proxy";
|
||||
import type { DrizzleConfig } from "drizzle-orm/utils";
|
||||
|
||||
type MaybePromise<T> = Promise<T> | T;
|
||||
type DrizzleSurface<TSchema extends Record<string, unknown>> = Pick<
|
||||
PgRemoteDatabase<TSchema>,
|
||||
"delete" | "insert" | "select" | "update"
|
||||
>;
|
||||
|
||||
export type ModuleDrizzleTransaction<
|
||||
TSchema extends Record<string, unknown> = Record<string, never>,
|
||||
> = DrizzleSurface<TSchema> & {
|
||||
transaction<T>(
|
||||
callback: (transaction: ModuleDrizzleTransaction<TSchema>) => MaybePromise<T>,
|
||||
): Promise<T>;
|
||||
};
|
||||
|
||||
export type ModuleDrizzleDatabase<TSchema extends Record<string, unknown> = Record<string, never>> =
|
||||
DrizzleSurface<TSchema> & {
|
||||
transaction<T>(
|
||||
callback: (transaction: ModuleDrizzleTransaction<TSchema>) => MaybePromise<T>,
|
||||
): Promise<T>;
|
||||
};
|
||||
|
||||
type TransactionRunner<TSchema extends Record<string, unknown>> = <T>(
|
||||
callback: (transaction: ModuleDrizzleTransaction<TSchema>) => MaybePromise<T>,
|
||||
) => Promise<T>;
|
||||
|
||||
function remoteDatabase<TSchema extends Record<string, unknown>>(
|
||||
executor: ModuleDatabaseQueryable,
|
||||
config: DrizzleConfig<TSchema>,
|
||||
runTransaction: TransactionRunner<TSchema>,
|
||||
): ModuleDrizzleDatabase<TSchema> {
|
||||
const database = drizzle<TSchema>(async (text, parameters) => {
|
||||
const result = await executor.query({
|
||||
text,
|
||||
// Drizzle performs column-specific parameter encoding before this callback. Values outside
|
||||
// ABI v1 remain unchanged so the ABI executor, not the adapter, rejects them.
|
||||
values: parameters as readonly ModuleDatabaseParameter[],
|
||||
// Drizzle maps selected fields by position. Array rows also remain correct when a join returns
|
||||
// duplicate PostgreSQL column names.
|
||||
rowMode: "array",
|
||||
});
|
||||
return { rows: result.rows as unknown as unknown[][] };
|
||||
}, config);
|
||||
|
||||
return Object.freeze({
|
||||
delete: database.delete.bind(database),
|
||||
insert: database.insert.bind(database),
|
||||
select: database.select.bind(database),
|
||||
transaction: runTransaction,
|
||||
update: database.update.bind(database),
|
||||
}) as ModuleDrizzleDatabase<TSchema>;
|
||||
}
|
||||
|
||||
function transactionDatabase<TSchema extends Record<string, unknown>>(
|
||||
transaction: ModuleDatabaseTransaction,
|
||||
config: DrizzleConfig<TSchema>,
|
||||
): ModuleDrizzleTransaction<TSchema> {
|
||||
return remoteDatabase(transaction, config, (callback) =>
|
||||
transaction.savepoint((savepoint) => callback(transactionDatabase(savepoint, config))),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a module-owned Drizzle database backed exclusively by Module Database ABI v1 calls.
|
||||
*
|
||||
* Drizzle compiles and decodes on the module side. Core receives only SQL text, ordered parameter
|
||||
* values, and `rowMode: "array"`. Top-level transactions use the ABI transaction callback; nested
|
||||
* Drizzle transactions use ABI savepoints.
|
||||
*/
|
||||
export function moduleDrizzle<TSchema extends Record<string, unknown> = Record<string, never>>(
|
||||
executor: ModuleDatabaseExecutor,
|
||||
config: DrizzleConfig<TSchema> = {},
|
||||
): ModuleDrizzleDatabase<TSchema> {
|
||||
return remoteDatabase(executor, config, (callback) =>
|
||||
executor.transaction((transaction) => callback(transactionDatabase(transaction, config))),
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user