Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,11 +1,33 @@
import { getCacheData } from "@nimpl/cache-tools";
import { createRouteHelpers } from "@nimpl/cache-tools";

// eslint-disable-next-line @typescript-eslint/no-require-imports
const cacheHandler = require("../../../../../cache-handler.js");

export const GET = async (_request: Request, { params }: { params: Promise<{ segments?: string[] }> }) => {
const { getCacheData, putCacheData, deleteCacheData } = createRouteHelpers(cacheHandler);

type RouteParams = { params: Promise<{ segments?: string[] }> };

export const GET = async (_request: Request, { params }: RouteParams) => {
const { segments } = await params;
const data = await getCacheData(segments);

if (!data) return new Response("", { status: 404 });

return new Response(JSON.stringify(data));
};

export const PUT = async (_request: Request, { params }: RouteParams) => {
const { segments } = await params;
const data = await putCacheData(segments);

if (!data) return new Response("", { status: 404 });

return new Response(JSON.stringify(data));
};

export const DELETE = async (_request: Request, { params }: RouteParams) => {
const { segments } = await params;
const data = await getCacheData(cacheHandler, segments);
const data = await deleteCacheData(segments);

if (!data) return new Response("", { status: 404 });

Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@
},
"resolutions": {
"@nimpl/cache-adapter>@nimpl/cache": "workspace:*",
"@nimpl/cache-server>@nimpl/cache": "workspace:*"
"@nimpl/cache-server>@nimpl/cache": "workspace:*",
"@nimpl/cache-tools>@nimpl/cache": "workspace:*"
},
"license": "MIT",
"packageManager": "pnpm@10.24.0+sha512.01ff8ae71b4419903b65c60fb2dc9d34cf8bb6e06d03bde112ef38f7a34d6904c424ba66bea5cdcf12890230bf39f9580473140ed9c946fef328b6e5238a345a"
Expand Down
1 change: 1 addition & 0 deletions packages/cache-tools/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"url": "https://github.com/alexdln/"
},
"devDependencies": {
"@nimpl/cache": "latest",
"@rollup/plugin-commonjs": "29.0.0",
"@rollup/plugin-node-resolve": "16.0.3",
"@rollup/plugin-terser": "0.4.4",
Expand Down
3 changes: 2 additions & 1 deletion packages/cache-tools/src/create-cache.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { type CacheHandler, type Metadata } from "./lib/types";
import { type CacheHandler, type Metadata } from "@nimpl/cache";

import { objectToStream, streamToRaw } from "./lib/stream";

export const cache =
Expand Down
108 changes: 11 additions & 97 deletions packages/cache-tools/src/create-helpers.ts
Original file line number Diff line number Diff line change
@@ -1,103 +1,17 @@
import { type CacheHandler, type KeysData } from "./lib/types";
import { streamToRaw } from "./lib/stream";
import { type CacheHandler } from "@nimpl/cache";

export const getKeys = async (
cacheHandler: CacheHandler,
type: "main" | "persistent" | "ephemeral" = "main",
): Promise<KeysData> => {
const layers = {
main: cacheHandler,
persistent: cacheHandler.persistentLayer,
ephemeral: cacheHandler.ephemeralLayer,
};
const handler = layers[type];
return handler.keys();
};

export const getKeyDetails = async (
cacheHandler: CacheHandler,
type: "main" | "persistent" | "ephemeral",
key: string,
) => {
const layers = {
main: cacheHandler,
persistent: cacheHandler.persistentLayer,
ephemeral: cacheHandler.ephemeralLayer,
};
const handler = layers[type];
try {
const cacheEntry = await handler.getEntry(key);

if (!cacheEntry) {
return {
key,
metadata: null,
value: null,
size: 0,
status: null,
};
}

const { entry, size, status } = cacheEntry;
const [cacheStream, responseStream] = entry.value.tee();
entry.value = cacheStream;
const value = await streamToRaw(responseStream);

return {
key,
metadata: {
tags: entry.tags,
timestamp: entry.timestamp,
stale: entry.stale,
revalidate: entry.revalidate,
expire: entry.expire,
},
value,
size,
status,
};
} catch (error) {
return {
key,
metadata: null,
value: null,
size: 0,
error: error instanceof Error ? error.message : "Unknown error",
status: null,
};
}
};

export const getCacheData = (cacheHandler: CacheHandler, segments?: string[]) => {
if (!segments?.length || segments.length > 2) {
return null;
}
const type = segments[0] as "main" | "persistent" | "ephemeral";
if (!["main", "persistent", "ephemeral"].includes(type)) {
return null;
}
if (segments.length === 1) {
return getKeys(cacheHandler, type);
}

return getKeyDetails(cacheHandler, type, segments[1]);
};

export const updateTags = async (cacheHandler: CacheHandler, tags: string[], duration: number) => {
return cacheHandler.updateTags(tags, { expire: duration });
};

export const updateKey = async (cacheHandler: CacheHandler, key: string, duration: number) => {
return cacheHandler.updateKey(key, { expire: duration });
};
import { type LayerType } from "./lib/types";
import { getKeys, getKeyDetails } from "./lib/get-helpers";
import { updateKey, updateTags } from "./lib/put-helpers";
import { deleteKey } from "./lib/delete-helpers";

export const createHelpers = (cacheHandler: CacheHandler) => {
return {
getKeys: (type: "main" | "persistent" | "ephemeral") => getKeys(cacheHandler, type),
getKeyDetails: (type: "main" | "persistent" | "ephemeral", key: string) =>
getKeyDetails(cacheHandler, type, key),
getCacheData: (segments?: string[]) => getCacheData(cacheHandler, segments),
updateTags: (tags: string[], duration: number) => updateTags(cacheHandler, tags, duration),
updateKey: (key: string, duration: number) => updateKey(cacheHandler, key, duration),
getKeys: (type: LayerType) => getKeys(cacheHandler, type),
getKeyDetails: (type: LayerType, key: string) => getKeyDetails(cacheHandler, type, key),
updateTags: (type: LayerType, tags: string[], duration: number) =>
updateTags(cacheHandler, type, tags, duration),
updateKey: (type: LayerType, key: string, duration: number) => updateKey(cacheHandler, type, key, duration),
deleteKey: (type: LayerType, key: string) => deleteKey(cacheHandler, type, key),
};
};
13 changes: 13 additions & 0 deletions packages/cache-tools/src/create-route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { type CacheHandler } from "@nimpl/cache";

import { getCacheData } from "./lib/get-helpers";
import { putCacheData } from "./lib/put-helpers";
import { deleteCacheData } from "./lib/delete-helpers";

export const createRouteHelpers = (cacheHandler: CacheHandler) => {
return {
getCacheData: (segments?: string[]) => getCacheData(cacheHandler, segments),
putCacheData: (segments?: string[]) => putCacheData(cacheHandler, segments),
deleteCacheData: (segments?: string[]) => deleteCacheData(cacheHandler, segments),
};
};
2 changes: 1 addition & 1 deletion packages/cache-tools/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export * from "./create-cache";
export * from "./create-helpers";
export * from "./lib/types";
export * from "./create-route";
1 change: 1 addition & 0 deletions packages/cache-tools/src/lib/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const LAYER_TYPES = ["main", "persistent", "ephemeral"] as const;
28 changes: 28 additions & 0 deletions packages/cache-tools/src/lib/delete-helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { CacheHandler } from "@nimpl/cache";

import { type LayerType } from "./types";
import { LAYER_TYPES } from "./constants";

export const deleteKey = async (cacheHandler: CacheHandler, type: LayerType, key: string) => {
const layers = {
main: cacheHandler,
persistent: cacheHandler.persistentLayer,
ephemeral: cacheHandler.ephemeralLayer,
};
return layers[type].delete(key);
};

export const deleteCacheData = async (cacheHandler: CacheHandler, segments?: string[]) => {
if (!segments?.length || segments.length > 3) {
return null;
}
const type = segments[0] as LayerType;
if (!LAYER_TYPES.includes(type)) {
return null;
}
if (segments[1] === "key") {
await deleteKey(cacheHandler, type, segments[2]);
return true;
}
return null;
};
80 changes: 80 additions & 0 deletions packages/cache-tools/src/lib/get-helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { type CacheHandler } from "@nimpl/cache";

import { type LayerType } from "./types";
import { streamToRaw } from "./stream";
import { LAYER_TYPES } from "./constants";

export const getKeys = async (cacheHandler: CacheHandler, type: LayerType = "main"): Promise<string[]> => {
const layers = {
main: cacheHandler,
persistent: cacheHandler.persistentLayer,
ephemeral: cacheHandler.ephemeralLayer,
};
const handler = layers[type];
return handler.keys();
};

export const getKeyDetails = async (cacheHandler: CacheHandler, type: LayerType, key: string) => {
const layers = {
main: cacheHandler,
persistent: cacheHandler.persistentLayer,
ephemeral: cacheHandler.ephemeralLayer,
};
const handler = layers[type];
try {
const cacheEntry = await handler.getEntry(key);

if (!cacheEntry) {
return {
key,
metadata: null,
value: null,
size: 0,
status: null,
};
}

const { entry, size, status } = cacheEntry;
const [cacheStream, responseStream] = entry.value.tee();
entry.value = cacheStream;
const value = await streamToRaw(responseStream);

return {
key,
metadata: {
tags: entry.tags,
timestamp: entry.timestamp,
stale: entry.stale,
revalidate: entry.revalidate,
expire: entry.expire,
},
value,
size,
status,
};
} catch (error) {
return {
key,
metadata: null,
value: null,
size: 0,
error: error instanceof Error ? error.message : "Unknown error",
status: null,
};
}
};

export const getCacheData = (cacheHandler: CacheHandler, segments?: string[]) => {
if (!segments?.length || segments.length > 2) {
return null;
}
const type = segments[0] as LayerType;
if (!LAYER_TYPES.includes(type)) {
return null;
}
if (segments.length === 1) {
return getKeys(cacheHandler, type);
}

return getKeyDetails(cacheHandler, type, segments[1]);
};
41 changes: 41 additions & 0 deletions packages/cache-tools/src/lib/put-helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { CacheHandler } from "@nimpl/cache";

import { type LayerType } from "./types";
import { LAYER_TYPES } from "./constants";

export const updateTags = async (cacheHandler: CacheHandler, type: LayerType, tags: string[], duration?: number) => {
const layers = {
main: cacheHandler,
persistent: cacheHandler.persistentLayer,
ephemeral: cacheHandler.ephemeralLayer,
};
return layers[type].updateTags(tags, duration ? { expire: duration } : undefined);
};

export const updateKey = async (cacheHandler: CacheHandler, type: LayerType, key: string, duration?: number) => {
const layers = {
main: cacheHandler,
persistent: cacheHandler.persistentLayer,
ephemeral: cacheHandler.ephemeralLayer,
};
return layers[type].updateKey(key, duration ? { expire: duration } : undefined);
};

export const putCacheData = async (cacheHandler: CacheHandler, segments?: string[]) => {
if (!segments?.length || segments.length > 3) {
return null;
}
const type = segments[0] as LayerType;
if (!LAYER_TYPES.includes(type)) {
return null;
}
if (segments[1] === "key") {
await updateKey(cacheHandler, type, segments[2]);
return true;
}
if (segments[1] === "tag") {
await updateTags(cacheHandler, type, [segments[2]]);
return true;
}
return null;
};
41 changes: 2 additions & 39 deletions packages/cache-tools/src/lib/types.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,3 @@
import { type ReadableStream as WebReadableStream } from "node:stream/web";
import { type LAYER_TYPES } from "./constants";

export type Metadata = {
tags: string[];
timestamp: number;
stale: number;
expire: number;
revalidate: number;
};

export type KeysData = string[];

type Entry = {
value: ReadableStream | WebReadableStream;
} & Metadata;

type CacheEntry = {
entry: Entry;
size: number;
status: string;
};

export type CacheHandler = {
getEntry: (key: string) => Promise<CacheEntry | undefined | null>;
get: (key: string) => Promise<Entry | undefined | null>;
set: (key: string, value: Promise<Entry>) => Promise<void>;
keys: () => Promise<KeysData>;
updateTags: (tags: string[], durations?: { expire?: number }) => Promise<void>;
updateKey: (key: string, durations?: { expire?: number }) => Promise<void>;
ephemeralLayer: {
getEntry: (key: string) => Promise<CacheEntry | undefined | null>;
get: (key: string) => Promise<Entry | undefined | null>;
keys: () => Promise<KeysData>;
};
persistentLayer: {
getEntry: (key: string) => Promise<CacheEntry | undefined | null>;
get: (key: string) => Promise<Entry | undefined | null>;
keys: () => Promise<KeysData>;
};
};
export type LayerType = (typeof LAYER_TYPES)[number];
Loading