@hot-updater/plugin-core
Version:
React Native OTA solution for self-hosted
91 lines (89 loc) • 2.79 kB
JavaScript
import { merge } from "es-toolkit";
//#region src/createDatabasePlugin.ts
/**
* Creates a database plugin with lazy initialization and automatic hook execution.
*
* This factory function abstracts the double currying pattern used by all database plugins,
* ensuring consistent lazy initialization behavior across different database providers.
* Hooks are automatically executed at appropriate times without requiring manual invocation.
*
* @param options - Configuration options for the database plugin
* @returns A double-curried function that lazily initializes the database plugin
*
* @example
* ```typescript
* export const postgres = createDatabasePlugin<PostgresConfig>({
* name: "postgres",
* factory: (config) => {
* const db = new Kysely(config);
* return {
* async getBundleById(bundleId) { ... },
* async getBundles(options) { ... },
* async getChannels() { ... },
* async commitBundle({ changedSets }) { ... }
* };
* }
* });
* ```
*/
function createDatabasePlugin(options) {
return (config, hooks) => {
return () => {
let cachedMethods = null;
const getMethods = () => {
if (!cachedMethods) cachedMethods = options.factory(config);
return cachedMethods;
};
const changedMap = /* @__PURE__ */ new Map();
const markChanged = (operation, data) => {
changedMap.set(data.id, {
operation,
data
});
};
return {
name: options.name,
async getBundleById(bundleId) {
return getMethods().getBundleById(bundleId);
},
async getBundles(options$1) {
return getMethods().getBundles(options$1);
},
async getChannels() {
return getMethods().getChannels();
},
async onUnmount() {
const methods = getMethods();
if (methods.onUnmount) return methods.onUnmount();
},
async commitBundle() {
await getMethods().commitBundle({ changedSets: Array.from(changedMap.values()) });
await hooks?.onDatabaseUpdated?.();
changedMap.clear();
},
async updateBundle(targetBundleId, newBundle) {
const pendingChange = changedMap.get(targetBundleId);
if (pendingChange) {
const updatedData = merge(pendingChange.data, newBundle);
changedMap.set(targetBundleId, {
operation: pendingChange.operation,
data: updatedData
});
return;
}
const currentBundle = await getMethods().getBundleById(targetBundleId);
if (!currentBundle) throw new Error("targetBundleId not found");
markChanged("update", merge(currentBundle, newBundle));
},
async appendBundle(inputBundle) {
markChanged("insert", inputBundle);
},
async deleteBundle(deleteBundle) {
markChanged("delete", deleteBundle);
}
};
};
};
}
//#endregion
export { createDatabasePlugin };