117 lines
3.4 KiB
TypeScript
117 lines
3.4 KiB
TypeScript
import { Knex } from "knex";
|
|
import { SysCallMapping } from "@plugos/plugos/system";
|
|
|
|
import {
|
|
ensureTable,
|
|
storeSyscalls,
|
|
} from "@plugos/plugos/syscalls/store.knex_node";
|
|
|
|
type IndexItem = {
|
|
page: string;
|
|
key: string;
|
|
value: any;
|
|
};
|
|
|
|
export type KV = {
|
|
key: string;
|
|
value: any;
|
|
};
|
|
|
|
/*
|
|
Keyspace design:
|
|
|
|
for page lookups:
|
|
p~page~key
|
|
|
|
for global lookups:
|
|
k~key~page
|
|
|
|
*/
|
|
|
|
function pageKey(page: string, key: string) {
|
|
return `p~${page}~${key}`;
|
|
}
|
|
|
|
function unpackPageKey(dbKey: string): { page: string; key: string } {
|
|
const [, page, key] = dbKey.split("~");
|
|
return { page, key };
|
|
}
|
|
|
|
function globalKey(page: string, key: string) {
|
|
return `k~${key}~${page}`;
|
|
}
|
|
|
|
function unpackGlobalKey(dbKey: string): { page: string; key: string } {
|
|
const [, key, page] = dbKey.split("~");
|
|
return { page, key };
|
|
}
|
|
|
|
export async function ensurePageIndexTable(db: Knex<any, unknown>) {
|
|
await ensureTable(db, "page_index");
|
|
}
|
|
|
|
export function pageIndexSyscalls(db: Knex<any, unknown>): SysCallMapping {
|
|
const storeCalls = storeSyscalls(db, "page_index");
|
|
const apiObj: SysCallMapping = {
|
|
"index.set": async (ctx, page: string, key: string, value: any) => {
|
|
await storeCalls["store.set"](ctx, pageKey(page, key), value);
|
|
await storeCalls["store.set"](ctx, globalKey(page, key), value);
|
|
},
|
|
"index.batchSet": async (ctx, page: string, kvs: KV[]) => {
|
|
for (let { key, value } of kvs) {
|
|
await apiObj["index.set"](ctx, page, key, value);
|
|
}
|
|
},
|
|
"index.delete": async (ctx, page: string, key: string) => {
|
|
await storeCalls["store.delete"](ctx, pageKey(page, key));
|
|
await storeCalls["store.delete"](ctx, globalKey(page, key));
|
|
},
|
|
"index.get": async (ctx, page: string, key: string) => {
|
|
return storeCalls["store.get"](ctx, pageKey(page, key));
|
|
},
|
|
"index.scanPrefixForPage": async (ctx, page: string, prefix: string) => {
|
|
return (
|
|
await storeCalls["store.queryPrefix"](ctx, pageKey(page, prefix))
|
|
).map(({ key, value }: { key: string; value: any }) => {
|
|
const { key: pageKey } = unpackPageKey(key);
|
|
return {
|
|
page,
|
|
key: pageKey,
|
|
value,
|
|
};
|
|
});
|
|
},
|
|
"index.scanPrefixGlobal": async (ctx, prefix: string) => {
|
|
return (await storeCalls["store.queryPrefix"](ctx, `k~${prefix}`)).map(
|
|
({ key, value }: { key: string; value: any }) => {
|
|
const { page, key: pageKey } = unpackGlobalKey(key);
|
|
return {
|
|
page,
|
|
key: pageKey,
|
|
value,
|
|
};
|
|
}
|
|
);
|
|
},
|
|
"index.clearPageIndexForPage": async (ctx, page: string) => {
|
|
await apiObj["index.deletePrefixForPage"](ctx, page, "");
|
|
},
|
|
"index.deletePrefixForPage": async (ctx, page: string, prefix: string) => {
|
|
// Collect all global keys for this page to delete
|
|
let keysToDelete = (
|
|
await storeCalls["store.queryPrefix"](ctx, pageKey(page, prefix))
|
|
).map(({ key }: { key: string; value: string }) =>
|
|
globalKey(page, unpackPageKey(key).key)
|
|
);
|
|
// Delete all page keys
|
|
await storeCalls["store.deletePrefix"](ctx, pageKey(page, prefix));
|
|
// console.log("Deleting keys", keysToDelete);
|
|
await storeCalls["store.batchDelete"](ctx, keysToDelete);
|
|
},
|
|
"index.clearPageIndex": async (ctx) => {
|
|
await storeCalls["store.deleteAll"](ctx);
|
|
},
|
|
};
|
|
return apiObj;
|
|
}
|