t7m
Version:
Transformer for Elysia and Hono
163 lines (162 loc) • 5.93 kB
JavaScript
// src/abstractTransformer.ts
class AbstractTransformer {
clearCacheOnTransform;
constructor(params) {
this.clearCacheOnTransform = params?.clearCacheOnTransform ?? true;
}
includesMap = {};
cache = {};
clearCache = () => this._clearCache();
_clearCache = (clearedFor = new Set) => {
if (clearedFor.has(this))
return;
Object.keys(this.cache).forEach((key) => this.cache[key]?.clear());
clearedFor.add(this);
Object.keys(this.transformers).forEach((key) => {
const transformer = this.transformers[key];
if ("call" in transformer)
return transformer.call()._clearCache(clearedFor);
transformer._clearCache(clearedFor);
});
};
originalClearCacheOnTransform = null;
disableClearCacheForTransformers = (visited) => {
if (visited.has(this))
return;
visited.add(this);
if (this.originalClearCacheOnTransform === null)
this.originalClearCacheOnTransform = this.clearCacheOnTransform;
this.clearCacheOnTransform = false;
Object.keys(this.transformers).forEach((key) => {
const transformer = this.transformers[key];
if ("call" in transformer) {
transformer.call().disableClearCacheForTransformers(visited);
} else
transformer.disableClearCacheForTransformers(visited);
});
};
restoreClearCacheForTransformers = (visited) => {
if (visited.has(this))
return;
visited.add(this);
if (this.originalClearCacheOnTransform !== null) {
this.clearCacheOnTransform = this.originalClearCacheOnTransform;
this.originalClearCacheOnTransform = null;
}
Object.keys(this.transformers).forEach((key) => {
const transformer = this.transformers[key];
if ("call" in transformer) {
transformer.call().restoreClearCacheForTransformers(visited);
} else
transformer.restoreClearCacheForTransformers(visited);
});
};
transformers = {};
onBeforeTransform = () => {
if (this.originalClearCacheOnTransform !== null)
return;
const visited = new Set([this]);
Object.keys(this.transformers).forEach((key) => {
const transformer = this.transformers[key];
if ("call" in transformer) {
transformer.call().disableClearCacheForTransformers(visited);
} else
transformer.disableClearCacheForTransformers(visited);
});
};
onAfterTransform = () => {
if (this.originalClearCacheOnTransform !== null)
return;
const visited = new Set([this]);
Object.keys(this.transformers).forEach((key) => {
const transformer = this.transformers[key];
if ("call" in transformer) {
transformer.call().restoreClearCacheForTransformers(visited);
} else
transformer.restoreClearCacheForTransformers(visited);
});
};
async transform(params) {
const { input, props, includes, unsafeIncludes } = params;
const combinedIncludes = [...includes || [], ...unsafeIncludes || []];
return this.__transform(input, props, combinedIncludes);
}
async transformMany(params) {
const { inputs, props, includes, unsafeIncludes } = params;
const combinedIncludes = [...includes || [], ...unsafeIncludes || []];
return Promise.all(inputs.map((input) => this.__transform(input, props, combinedIncludes)));
}
async _transform(params) {
const { input, props, includes, unsafeIncludes } = params;
const combinedIncludes = [...includes || [], ...unsafeIncludes || []];
this.onBeforeTransform();
const output = await this.__transform(input, props, combinedIncludes);
if (this.clearCacheOnTransform)
this.clearCache();
this.onAfterTransform();
return output;
}
async _transformMany(params) {
const { inputs, props, includes, unsafeIncludes } = params;
const combinedIncludes = [...includes || [], ...unsafeIncludes || []];
this.onBeforeTransform();
const outputArray = await Promise.all(inputs.map((input) => this.__transform(input, props, combinedIncludes)));
if (this.clearCacheOnTransform)
this.clearCache();
this.onAfterTransform();
return outputArray;
}
async __transform(input, props, includes = []) {
const data = await this.data(input, props);
includes = Array.from(new Set(includes));
if (includes.length > 0 && typeof data === "object" && data !== null) {
const otherIncludes = includes.filter((include) => !(include in this.includesMap));
const validIncludes = includes.filter((include) => (include in this.includesMap)).map((include) => include);
await Promise.all(validIncludes.map(async (include) => {
if (!this.includesMap[include])
throw new Error(`Include function not found in includesMap`);
try {
data[include] = await this.includesMap[include](input, props, otherIncludes);
} catch (error) {
throw new Error(`[T7M] Error in include function '${String(include)}': ${error instanceof Error ? error.message : String(error)}`);
}
}));
}
return data;
}
}
// src/cache.ts
class Cache {
fn;
cacheOnObjectParams;
constructor(fn, ...on) {
this.fn = fn;
this.cacheOnObjectParams = on.length > 0 ? on : undefined;
}
cache = new Map;
objectCacheKey(keys, arg) {
return keys.slice().sort().map((key) => `${key.toString()}:${arg[key]}`).join("-");
}
call(...args) {
const arg = args[0] ?? "default-key";
let cacheKey;
if (typeof arg !== "object") {
cacheKey = arg.toString();
} else if (this.cacheOnObjectParams) {
cacheKey = this.objectCacheKey(this.cacheOnObjectParams, arg);
} else {
cacheKey = this.objectCacheKey(Object.keys(arg), arg);
}
let result = this.cache.get(cacheKey);
if (!result) {
result = this.fn(...args);
this.cache.set(cacheKey, result);
}
return result;
}
clear = () => this.cache.clear();
}
export {
Cache,
AbstractTransformer
};