semola 0.5.4 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -50
- package/dist/lib/api/index.cjs +1 -523
- package/dist/lib/api/index.d.cts +1 -271
- package/dist/lib/api/index.d.mts +80 -84
- package/dist/lib/api/index.mjs +1 -521
- package/dist/lib/cache/index.cjs +1 -74
- package/dist/lib/cache/index.d.cts +1 -48
- package/dist/lib/cache/index.d.mts +3 -24
- package/dist/lib/cache/index.mjs +1 -73
- package/dist/lib/cron/index.cjs +1 -735
- package/dist/lib/cron/index.d.cts +1 -146
- package/dist/lib/cron/index.d.mts +99 -36
- package/dist/lib/cron/index.mjs +1 -726
- package/dist/lib/errors/index.cjs +1 -30
- package/dist/lib/errors/index.d.cts +1 -2
- package/dist/lib/errors/index.d.mts +12 -1
- package/dist/lib/errors/index.mjs +1 -26
- package/dist/lib/i18n/index.cjs +1 -42
- package/dist/lib/i18n/index.d.cts +1 -28
- package/dist/lib/i18n/index.mjs +1 -41
- package/dist/lib/logging/index.cjs +1 -387
- package/dist/lib/logging/index.d.cts +1 -108
- package/dist/lib/logging/index.mjs +1 -374
- package/dist/lib/orm/index.cjs +1 -0
- package/dist/lib/orm/index.d.cts +1 -0
- package/dist/lib/orm/index.d.mts +404 -0
- package/dist/lib/orm/index.mjs +1 -0
- package/dist/lib/policy/index.cjs +1 -285
- package/dist/lib/policy/index.d.cts +1 -77
- package/dist/lib/policy/index.mjs +1 -265
- package/dist/lib/prompts/index.cjs +6 -769
- package/dist/lib/prompts/index.d.cts +1 -96
- package/dist/lib/prompts/index.d.mts +12 -33
- package/dist/lib/prompts/index.mjs +6 -763
- package/dist/lib/pubsub/index.cjs +1 -117
- package/dist/lib/pubsub/index.d.cts +1 -41
- package/dist/lib/pubsub/index.d.mts +3 -18
- package/dist/lib/pubsub/index.mjs +1 -116
- package/dist/lib/queue/index.cjs +1 -182
- package/dist/lib/queue/index.d.cts +1 -74
- package/dist/lib/queue/index.d.mts +11 -4
- package/dist/lib/queue/index.mjs +1 -181
- package/dist/lib/workflow/index.cjs +1 -534
- package/dist/lib/workflow/index.d.cts +1 -85
- package/dist/lib/workflow/index.d.mts +76 -11
- package/dist/lib/workflow/index.mjs +1 -533
- package/dist/rolldown-runtime-CMqjfN_6.cjs +1 -0
- package/package.json +17 -5
- package/dist/index-BhGNDjPq.d.mts +0 -13
- package/dist/index-DxSbeGP-.d.cts +0 -13
package/dist/lib/api/index.mjs
CHANGED
|
@@ -1,521 +1 @@
|
|
|
1
|
-
import { err, mightThrow, ok } from "../errors/index.mjs";
|
|
2
|
-
//#region src/lib/api/openapi/index.ts
|
|
3
|
-
const toOpenAPISchema = (schema, io = "input") => ({ schema: schema["~standard"].jsonSchema[io]({ target: "draft-2020-12" }) });
|
|
4
|
-
const getSchemaDescription = (schema) => {
|
|
5
|
-
const metadata = schema["~standard"];
|
|
6
|
-
if (!metadata) return "";
|
|
7
|
-
if ("description" in metadata && typeof metadata.description === "string") return metadata.description;
|
|
8
|
-
return "";
|
|
9
|
-
};
|
|
10
|
-
const requestFields = [
|
|
11
|
-
"body",
|
|
12
|
-
"query",
|
|
13
|
-
"headers",
|
|
14
|
-
"cookies",
|
|
15
|
-
"params"
|
|
16
|
-
];
|
|
17
|
-
const mergeRequestSchemas = (schemas) => {
|
|
18
|
-
const merged = {};
|
|
19
|
-
for (const schema of schemas) {
|
|
20
|
-
if (!schema) continue;
|
|
21
|
-
for (const field of requestFields) if (schema[field]) merged[field] = schema[field];
|
|
22
|
-
}
|
|
23
|
-
return merged;
|
|
24
|
-
};
|
|
25
|
-
const mergeResponseSchemas = (schemas) => {
|
|
26
|
-
const merged = {};
|
|
27
|
-
for (const schema of schemas) {
|
|
28
|
-
if (!schema) continue;
|
|
29
|
-
for (const status in schema) {
|
|
30
|
-
const statusCode = Number(status);
|
|
31
|
-
const responseSchema = schema[statusCode];
|
|
32
|
-
if (responseSchema) merged[statusCode] = responseSchema;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
return Object.keys(merged).length > 0 ? merged : void 0;
|
|
36
|
-
};
|
|
37
|
-
const convertSchemaToOpenApi = async (schema, io = "input") => {
|
|
38
|
-
const result = toOpenAPISchema(schema, io);
|
|
39
|
-
const { schema: jsonSchema } = result;
|
|
40
|
-
const schemaId = jsonSchema.id;
|
|
41
|
-
if (schemaId && typeof schemaId === "string") {
|
|
42
|
-
const schemaWithoutId = { ...jsonSchema };
|
|
43
|
-
delete schemaWithoutId.id;
|
|
44
|
-
delete schemaWithoutId.$schema;
|
|
45
|
-
return {
|
|
46
|
-
schema: { $ref: `#/components/schemas/${schemaId}` },
|
|
47
|
-
components: { schemas: { [schemaId]: schemaWithoutId } }
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
if (jsonSchema.$schema) {
|
|
51
|
-
const schemaWithoutMeta = { ...jsonSchema };
|
|
52
|
-
delete schemaWithoutMeta.$schema;
|
|
53
|
-
return {
|
|
54
|
-
schema: schemaWithoutMeta,
|
|
55
|
-
components: void 0
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
return result;
|
|
59
|
-
};
|
|
60
|
-
const convertSchemaToInlineOpenApi = async (schema, io = "input") => {
|
|
61
|
-
const { schema: jsonSchema } = toOpenAPISchema(schema, io);
|
|
62
|
-
const cleanSchema = { ...jsonSchema };
|
|
63
|
-
delete cleanSchema.$schema;
|
|
64
|
-
delete cleanSchema.id;
|
|
65
|
-
return {
|
|
66
|
-
schema: cleanSchema,
|
|
67
|
-
components: void 0
|
|
68
|
-
};
|
|
69
|
-
};
|
|
70
|
-
const extractParametersFromSchema = async (schema, location) => {
|
|
71
|
-
const { schema: jsonSchema, components } = await convertSchemaToInlineOpenApi(schema);
|
|
72
|
-
if (jsonSchema.type !== "object") return {
|
|
73
|
-
parameters: [],
|
|
74
|
-
components
|
|
75
|
-
};
|
|
76
|
-
if (!jsonSchema.properties) return {
|
|
77
|
-
parameters: [],
|
|
78
|
-
components
|
|
79
|
-
};
|
|
80
|
-
const parameters = [];
|
|
81
|
-
const requiredFields = jsonSchema.required ?? [];
|
|
82
|
-
for (const name in jsonSchema.properties) {
|
|
83
|
-
const propertySchema = jsonSchema.properties[name];
|
|
84
|
-
const isRequired = requiredFields.includes(name);
|
|
85
|
-
parameters.push({
|
|
86
|
-
name,
|
|
87
|
-
in: location,
|
|
88
|
-
required: isRequired,
|
|
89
|
-
schema: propertySchema
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
return {
|
|
93
|
-
parameters,
|
|
94
|
-
components
|
|
95
|
-
};
|
|
96
|
-
};
|
|
97
|
-
const normalizePathForOpenAPI = (path) => {
|
|
98
|
-
return path.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, "{$1}");
|
|
99
|
-
};
|
|
100
|
-
const extractPathParameters = (path) => {
|
|
101
|
-
const matches = path.match(/:([a-zA-Z_][a-zA-Z0-9_]*)/g);
|
|
102
|
-
if (!matches) return [];
|
|
103
|
-
return matches.map((match) => match.slice(1));
|
|
104
|
-
};
|
|
105
|
-
const paramSources = [
|
|
106
|
-
["query", "query"],
|
|
107
|
-
["headers", "header"],
|
|
108
|
-
["cookies", "cookie"]
|
|
109
|
-
];
|
|
110
|
-
const createParameters = async (request, path) => {
|
|
111
|
-
const parameters = [];
|
|
112
|
-
const allComponents = [];
|
|
113
|
-
for (const [field, location] of paramSources) if (request[field]) {
|
|
114
|
-
const { parameters: params, components } = await extractParametersFromSchema(request[field], location);
|
|
115
|
-
parameters.push(...params);
|
|
116
|
-
if (components) allComponents.push(components);
|
|
117
|
-
}
|
|
118
|
-
const pathParamNames = extractPathParameters(path);
|
|
119
|
-
if (pathParamNames.length > 0 && request.params) {
|
|
120
|
-
const { schema: jsonSchema, components } = await convertSchemaToInlineOpenApi(request.params);
|
|
121
|
-
if (components) allComponents.push(components);
|
|
122
|
-
if (jsonSchema.type === "object" && jsonSchema.properties) for (const name of pathParamNames) {
|
|
123
|
-
const propertySchema = jsonSchema.properties[name];
|
|
124
|
-
if (propertySchema) parameters.push({
|
|
125
|
-
name,
|
|
126
|
-
in: "path",
|
|
127
|
-
required: true,
|
|
128
|
-
schema: propertySchema
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
return {
|
|
133
|
-
parameters,
|
|
134
|
-
components: allComponents
|
|
135
|
-
};
|
|
136
|
-
};
|
|
137
|
-
const createRequestBody = async (bodySchema) => {
|
|
138
|
-
const { schema, components } = await convertSchemaToOpenApi(bodySchema);
|
|
139
|
-
return {
|
|
140
|
-
components,
|
|
141
|
-
requestBody: {
|
|
142
|
-
required: true,
|
|
143
|
-
content: { "application/json": { schema } }
|
|
144
|
-
}
|
|
145
|
-
};
|
|
146
|
-
};
|
|
147
|
-
const createResponses = async (response) => {
|
|
148
|
-
const responses = {};
|
|
149
|
-
const allComponents = [];
|
|
150
|
-
if (!response) return {
|
|
151
|
-
responses,
|
|
152
|
-
components: allComponents
|
|
153
|
-
};
|
|
154
|
-
for (const status in response) {
|
|
155
|
-
const statusCode = String(status);
|
|
156
|
-
const schema = response[Number(status)];
|
|
157
|
-
if (!schema) continue;
|
|
158
|
-
const description = getSchemaDescription(schema);
|
|
159
|
-
const { schema: jsonSchema, components } = await convertSchemaToOpenApi(schema, "output");
|
|
160
|
-
if (components) allComponents.push(components);
|
|
161
|
-
responses[statusCode] = {
|
|
162
|
-
description: description || `Response with status ${statusCode}`,
|
|
163
|
-
content: { "application/json": { schema: jsonSchema } }
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
return {
|
|
167
|
-
responses,
|
|
168
|
-
components: allComponents
|
|
169
|
-
};
|
|
170
|
-
};
|
|
171
|
-
const createOperation = async (route, globalMiddlewares, prefix) => {
|
|
172
|
-
const allMiddlewares = [...globalMiddlewares ?? [], ...route.middlewares ?? []];
|
|
173
|
-
const requestSchemas = [];
|
|
174
|
-
const responseSchemas = [];
|
|
175
|
-
for (const middleware of allMiddlewares) {
|
|
176
|
-
requestSchemas.push(middleware.options.request);
|
|
177
|
-
responseSchemas.push(middleware.options.response);
|
|
178
|
-
}
|
|
179
|
-
requestSchemas.push(route.request);
|
|
180
|
-
responseSchemas.push(route.response);
|
|
181
|
-
const mergedRequest = mergeRequestSchemas(requestSchemas);
|
|
182
|
-
const mergedResponse = mergeResponseSchemas(responseSchemas);
|
|
183
|
-
const { parameters, components: parameterComponents } = await createParameters(mergedRequest, prefix ? prefix + route.path : route.path);
|
|
184
|
-
const { responses, components: responseComponents } = await createResponses(mergedResponse);
|
|
185
|
-
const operation = { responses };
|
|
186
|
-
const allComponents = [];
|
|
187
|
-
allComponents.push(...responseComponents);
|
|
188
|
-
allComponents.push(...parameterComponents);
|
|
189
|
-
for (const field of [
|
|
190
|
-
"summary",
|
|
191
|
-
"description",
|
|
192
|
-
"operationId"
|
|
193
|
-
]) if (route[field]) operation[field] = route[field];
|
|
194
|
-
if (route.tags && route.tags.length > 0) operation.tags = route.tags;
|
|
195
|
-
if (parameters.length > 0) operation.parameters = parameters;
|
|
196
|
-
const bodySchema = mergedRequest.body;
|
|
197
|
-
if (bodySchema) {
|
|
198
|
-
const { requestBody, components: bodyComponents } = await createRequestBody(bodySchema);
|
|
199
|
-
operation.requestBody = requestBody;
|
|
200
|
-
if (bodyComponents) allComponents.push(bodyComponents);
|
|
201
|
-
}
|
|
202
|
-
return {
|
|
203
|
-
operation,
|
|
204
|
-
components: allComponents
|
|
205
|
-
};
|
|
206
|
-
};
|
|
207
|
-
const componentKeys = [
|
|
208
|
-
"schemas",
|
|
209
|
-
"responses",
|
|
210
|
-
"parameters",
|
|
211
|
-
"requestBodies"
|
|
212
|
-
];
|
|
213
|
-
const mergeComponents = (componentsArray) => {
|
|
214
|
-
const merged = {};
|
|
215
|
-
for (const components of componentsArray) {
|
|
216
|
-
if (!components) continue;
|
|
217
|
-
for (const key of componentKeys) if (components[key]) merged[key] = {
|
|
218
|
-
...merged[key],
|
|
219
|
-
...components[key]
|
|
220
|
-
};
|
|
221
|
-
}
|
|
222
|
-
return merged;
|
|
223
|
-
};
|
|
224
|
-
const generateOpenApiSpec = async (options) => {
|
|
225
|
-
const spec = {
|
|
226
|
-
openapi: "3.1.0",
|
|
227
|
-
info: {
|
|
228
|
-
title: options.title,
|
|
229
|
-
description: options.description,
|
|
230
|
-
version: options.version
|
|
231
|
-
},
|
|
232
|
-
paths: {}
|
|
233
|
-
};
|
|
234
|
-
if (options.servers && options.servers.length > 0) spec.servers = options.servers;
|
|
235
|
-
if (options.securitySchemes) spec.components = { securitySchemes: options.securitySchemes };
|
|
236
|
-
const allRouteComponents = [];
|
|
237
|
-
for (const route of options.routes) {
|
|
238
|
-
const openApiPath = normalizePathForOpenAPI(options.prefix ? options.prefix + route.path : route.path);
|
|
239
|
-
const method = route.method.toLowerCase();
|
|
240
|
-
if (!spec.paths[openApiPath]) spec.paths[openApiPath] = {};
|
|
241
|
-
const { operation, components } = await createOperation(route, options.globalMiddlewares ?? [], options.prefix);
|
|
242
|
-
spec.paths[openApiPath][method] = operation;
|
|
243
|
-
allRouteComponents.push(...components);
|
|
244
|
-
}
|
|
245
|
-
const mergedComponents = mergeComponents(allRouteComponents);
|
|
246
|
-
if (!spec.components) spec.components = {};
|
|
247
|
-
if (options.securitySchemes) spec.components.securitySchemes = options.securitySchemes;
|
|
248
|
-
for (const key of componentKeys) {
|
|
249
|
-
const value = mergedComponents[key];
|
|
250
|
-
if (value && Object.keys(value).length > 0) spec.components[key] = value;
|
|
251
|
-
}
|
|
252
|
-
return spec;
|
|
253
|
-
};
|
|
254
|
-
//#endregion
|
|
255
|
-
//#region src/lib/api/validation/index.ts
|
|
256
|
-
const validateSchema = async (schema, data) => {
|
|
257
|
-
const result = await schema["~standard"].validate(data);
|
|
258
|
-
if (!result.issues) return ok(result.value);
|
|
259
|
-
return err("ValidationError", result.issues.map((issue) => {
|
|
260
|
-
let path = "unknown";
|
|
261
|
-
if (Array.isArray(issue.path)) path = issue.path.map(String).join(".");
|
|
262
|
-
return `${path}: ${issue.message ?? "validation failed"}`;
|
|
263
|
-
}).join(", "));
|
|
264
|
-
};
|
|
265
|
-
const validateBody = async (req, bodySchema, bodyCache) => {
|
|
266
|
-
if (!bodySchema) return ok(true);
|
|
267
|
-
if (!(req.headers.get("content-type") ?? "").includes("application/json")) return ok(void 0);
|
|
268
|
-
if (bodyCache?.parsed) return validateSchema(bodySchema, bodyCache.value);
|
|
269
|
-
const [parseError, parsedBody] = await mightThrow(req.json());
|
|
270
|
-
if (parseError) return err("ParseError", "Invalid JSON body");
|
|
271
|
-
if (bodyCache) {
|
|
272
|
-
bodyCache.parsed = true;
|
|
273
|
-
bodyCache.value = parsedBody;
|
|
274
|
-
}
|
|
275
|
-
return validateSchema(bodySchema, parsedBody);
|
|
276
|
-
};
|
|
277
|
-
const validateQuery = async (req, querySchema) => {
|
|
278
|
-
if (!querySchema) return ok(true);
|
|
279
|
-
const qIndex = req.url.indexOf("?");
|
|
280
|
-
if (qIndex === -1) return validateSchema(querySchema, {});
|
|
281
|
-
const hashIndex = req.url.indexOf("#", qIndex + 1);
|
|
282
|
-
const queryString = hashIndex === -1 ? req.url.slice(qIndex + 1) : req.url.slice(qIndex + 1, hashIndex);
|
|
283
|
-
const searchParams = new URLSearchParams(queryString);
|
|
284
|
-
const queryParams = {};
|
|
285
|
-
for (const key of searchParams.keys()) {
|
|
286
|
-
const values = searchParams.getAll(key);
|
|
287
|
-
const [firstValue] = values;
|
|
288
|
-
if (values.length === 1) queryParams[key] = firstValue;
|
|
289
|
-
else queryParams[key] = values;
|
|
290
|
-
}
|
|
291
|
-
return validateSchema(querySchema, queryParams);
|
|
292
|
-
};
|
|
293
|
-
const validateHeaders = async (req, headersSchema) => {
|
|
294
|
-
if (!headersSchema) return ok(true);
|
|
295
|
-
const headers = {};
|
|
296
|
-
req.headers.forEach((value, key) => {
|
|
297
|
-
headers[key] = value;
|
|
298
|
-
});
|
|
299
|
-
return validateSchema(headersSchema, headers);
|
|
300
|
-
};
|
|
301
|
-
const validateCookies = async (req, cookiesSchema) => {
|
|
302
|
-
if (!cookiesSchema) return ok(true);
|
|
303
|
-
const cookieHeader = req.headers.get("cookie") ?? "";
|
|
304
|
-
const cookieMap = new Bun.CookieMap(cookieHeader);
|
|
305
|
-
return validateSchema(cookiesSchema, Object.fromEntries(cookieMap));
|
|
306
|
-
};
|
|
307
|
-
const validateParams = async (req, paramsSchema) => {
|
|
308
|
-
if (!paramsSchema) return ok(true);
|
|
309
|
-
return validateSchema(paramsSchema, req.params);
|
|
310
|
-
};
|
|
311
|
-
//#endregion
|
|
312
|
-
//#region src/lib/api/core/index.ts
|
|
313
|
-
const defaultValidated = Object.freeze({
|
|
314
|
-
body: void 0,
|
|
315
|
-
query: void 0,
|
|
316
|
-
headers: void 0,
|
|
317
|
-
cookies: void 0,
|
|
318
|
-
params: void 0
|
|
319
|
-
});
|
|
320
|
-
const responseHelpers = {
|
|
321
|
-
json: (status, data) => Response.json(data, { status }),
|
|
322
|
-
text: (status, text) => new Response(text, { status }),
|
|
323
|
-
html: (status, html) => new Response(html, {
|
|
324
|
-
status,
|
|
325
|
-
headers: { "Content-Type": "text/html" }
|
|
326
|
-
}),
|
|
327
|
-
redirect: (status, url) => Response.redirect(url, status)
|
|
328
|
-
};
|
|
329
|
-
const noopGet = () => void 0;
|
|
330
|
-
const stripTrailingSlash = (path) => {
|
|
331
|
-
if (path !== "/" && path.endsWith("/")) return path.slice(0, -1);
|
|
332
|
-
return path;
|
|
333
|
-
};
|
|
334
|
-
const hasSchemas = (schema) => schema && (schema.body || schema.query || schema.headers || schema.cookies || schema.params);
|
|
335
|
-
const needsBodyCache = (schema) => schema?.body !== void 0;
|
|
336
|
-
const shouldCreateBodyCache = (hasMiddlewares, allMiddlewares, request) => {
|
|
337
|
-
if (needsBodyCache(request)) return true;
|
|
338
|
-
if (!hasMiddlewares) return false;
|
|
339
|
-
return allMiddlewares.some((mw) => needsBodyCache(mw.options.request));
|
|
340
|
-
};
|
|
341
|
-
const resolveValidation = (v) => {
|
|
342
|
-
if (v === void 0 || v === true) return {
|
|
343
|
-
input: true,
|
|
344
|
-
output: true
|
|
345
|
-
};
|
|
346
|
-
if (v === false) return {
|
|
347
|
-
input: false,
|
|
348
|
-
output: false
|
|
349
|
-
};
|
|
350
|
-
return {
|
|
351
|
-
input: v.input !== false,
|
|
352
|
-
output: v.output !== false
|
|
353
|
-
};
|
|
354
|
-
};
|
|
355
|
-
var Api = class {
|
|
356
|
-
options;
|
|
357
|
-
routes = [];
|
|
358
|
-
constructor(options = {}) {
|
|
359
|
-
this.options = options;
|
|
360
|
-
}
|
|
361
|
-
getFullPath(path) {
|
|
362
|
-
const normalizedPath = stripTrailingSlash(path) || "/";
|
|
363
|
-
if (!this.options.prefix) return normalizedPath;
|
|
364
|
-
const normalizedPrefix = stripTrailingSlash(this.options.prefix);
|
|
365
|
-
if (normalizedPrefix === "/") return normalizedPath;
|
|
366
|
-
if (normalizedPath === "/") return normalizedPrefix;
|
|
367
|
-
return normalizedPrefix + normalizedPath;
|
|
368
|
-
}
|
|
369
|
-
async validateRequestSchema(req, schema, bodyCache) {
|
|
370
|
-
if (!schema) return {
|
|
371
|
-
success: true,
|
|
372
|
-
data: {}
|
|
373
|
-
};
|
|
374
|
-
const v = {};
|
|
375
|
-
if (schema.body) {
|
|
376
|
-
const [err, val] = await validateBody(req, schema.body, bodyCache);
|
|
377
|
-
if (err) return {
|
|
378
|
-
success: false,
|
|
379
|
-
error: err
|
|
380
|
-
};
|
|
381
|
-
v.body = val;
|
|
382
|
-
}
|
|
383
|
-
if (schema.query) {
|
|
384
|
-
const [err, val] = await validateQuery(req, schema.query);
|
|
385
|
-
if (err) return {
|
|
386
|
-
success: false,
|
|
387
|
-
error: err
|
|
388
|
-
};
|
|
389
|
-
v.query = val;
|
|
390
|
-
}
|
|
391
|
-
if (schema.headers) {
|
|
392
|
-
const [err, val] = await validateHeaders(req, schema.headers);
|
|
393
|
-
if (err) return {
|
|
394
|
-
success: false,
|
|
395
|
-
error: err
|
|
396
|
-
};
|
|
397
|
-
v.headers = val;
|
|
398
|
-
}
|
|
399
|
-
if (schema.cookies) {
|
|
400
|
-
const [err, val] = await validateCookies(req, schema.cookies);
|
|
401
|
-
if (err) return {
|
|
402
|
-
success: false,
|
|
403
|
-
error: err
|
|
404
|
-
};
|
|
405
|
-
v.cookies = val;
|
|
406
|
-
}
|
|
407
|
-
if (schema.params) {
|
|
408
|
-
const [err, val] = await validateParams(req, schema.params);
|
|
409
|
-
if (err) return {
|
|
410
|
-
success: false,
|
|
411
|
-
error: err
|
|
412
|
-
};
|
|
413
|
-
v.params = val;
|
|
414
|
-
}
|
|
415
|
-
return {
|
|
416
|
-
success: true,
|
|
417
|
-
data: v
|
|
418
|
-
};
|
|
419
|
-
}
|
|
420
|
-
createContext(req, validated, extensions) {
|
|
421
|
-
return {
|
|
422
|
-
raw: req,
|
|
423
|
-
req: validated,
|
|
424
|
-
...responseHelpers,
|
|
425
|
-
get: (key) => extensions[key]
|
|
426
|
-
};
|
|
427
|
-
}
|
|
428
|
-
async validateResponseBody(response, schema) {
|
|
429
|
-
if (!schema) return response;
|
|
430
|
-
const statusSchema = schema[response.status];
|
|
431
|
-
if (!statusSchema) return response;
|
|
432
|
-
if (!(response.headers.get("content-type") ?? "").includes("application/json")) return response;
|
|
433
|
-
const [parseError, body] = await mightThrow(response.clone().json());
|
|
434
|
-
if (parseError) return responseHelpers.json(400, { message: "Invalid response body" });
|
|
435
|
-
const [validationError] = await validateSchema(statusSchema, body);
|
|
436
|
-
if (validationError) return responseHelpers.json(400, { message: validationError.message });
|
|
437
|
-
return response;
|
|
438
|
-
}
|
|
439
|
-
buildBunRoutes() {
|
|
440
|
-
const bunRoutes = {};
|
|
441
|
-
const validationConfig = resolveValidation(this.options.validation);
|
|
442
|
-
for (const route of this.routes) {
|
|
443
|
-
const { path, method, handler, request, response, middlewares } = route;
|
|
444
|
-
const fullPath = this.getFullPath(path);
|
|
445
|
-
if (!bunRoutes[fullPath]) bunRoutes[fullPath] = {};
|
|
446
|
-
const allMiddlewares = [...this.options.middlewares ?? [], ...middlewares ?? []];
|
|
447
|
-
const hasMiddlewares = allMiddlewares.length > 0;
|
|
448
|
-
const hasRouteSchemas = hasSchemas(request);
|
|
449
|
-
const effectiveOutputValidation = validationConfig.output && !!response;
|
|
450
|
-
if (!hasMiddlewares && !(validationConfig.input && hasRouteSchemas) && !effectiveOutputValidation) bunRoutes[fullPath][method] = (req) => {
|
|
451
|
-
const context = Object.create(responseHelpers);
|
|
452
|
-
context.raw = req;
|
|
453
|
-
context.req = defaultValidated;
|
|
454
|
-
context.get = noopGet;
|
|
455
|
-
return handler(context);
|
|
456
|
-
};
|
|
457
|
-
else bunRoutes[fullPath][method] = async (req) => {
|
|
458
|
-
const extensions = {};
|
|
459
|
-
const bodyCache = validationConfig.input && shouldCreateBodyCache(hasMiddlewares, allMiddlewares, request) ? {
|
|
460
|
-
parsed: false,
|
|
461
|
-
value: void 0
|
|
462
|
-
} : void 0;
|
|
463
|
-
for (const mw of allMiddlewares) {
|
|
464
|
-
const { request: reqSchema, handler: mwHandler } = mw.options;
|
|
465
|
-
let validated = defaultValidated;
|
|
466
|
-
if (validationConfig.input && hasSchemas(reqSchema)) {
|
|
467
|
-
const result = await this.validateRequestSchema(req, reqSchema, bodyCache);
|
|
468
|
-
if (!result.success) return responseHelpers.json(400, { message: result.error?.message });
|
|
469
|
-
validated = result.data;
|
|
470
|
-
}
|
|
471
|
-
const mwResult = await mwHandler(this.createContext(req, validated, extensions));
|
|
472
|
-
if (mwResult instanceof Response) return mwResult;
|
|
473
|
-
if (mwResult) Object.assign(extensions, mwResult);
|
|
474
|
-
}
|
|
475
|
-
let routeValidated = defaultValidated;
|
|
476
|
-
if (validationConfig.input && hasRouteSchemas) {
|
|
477
|
-
const result = await this.validateRequestSchema(req, request, bodyCache);
|
|
478
|
-
if (!result.success) return responseHelpers.json(400, { message: result.error?.message });
|
|
479
|
-
routeValidated = result.data;
|
|
480
|
-
}
|
|
481
|
-
const handlerResponse = await handler(this.createContext(req, routeValidated, extensions));
|
|
482
|
-
if (effectiveOutputValidation) return this.validateResponseBody(handlerResponse, response);
|
|
483
|
-
return handlerResponse;
|
|
484
|
-
};
|
|
485
|
-
}
|
|
486
|
-
return bunRoutes;
|
|
487
|
-
}
|
|
488
|
-
defineRoute(config) {
|
|
489
|
-
this.routes.push(config);
|
|
490
|
-
}
|
|
491
|
-
getOpenApiSpec() {
|
|
492
|
-
return generateOpenApiSpec({
|
|
493
|
-
title: this.options.openapi?.title ?? "API",
|
|
494
|
-
description: this.options.openapi?.description,
|
|
495
|
-
version: this.options.openapi?.version ?? "1.0.0",
|
|
496
|
-
prefix: this.options.prefix,
|
|
497
|
-
servers: this.options.openapi?.servers,
|
|
498
|
-
securitySchemes: this.options.openapi?.securitySchemes,
|
|
499
|
-
routes: this.routes,
|
|
500
|
-
globalMiddlewares: this.options.middlewares
|
|
501
|
-
});
|
|
502
|
-
}
|
|
503
|
-
serve(port, callback) {
|
|
504
|
-
const bunRoutes = this.buildBunRoutes();
|
|
505
|
-
const server = Bun.serve({
|
|
506
|
-
port,
|
|
507
|
-
routes: bunRoutes,
|
|
508
|
-
fetch: () => new Response("Not found", { status: 404 })
|
|
509
|
-
});
|
|
510
|
-
if (callback) callback(server);
|
|
511
|
-
}
|
|
512
|
-
};
|
|
513
|
-
//#endregion
|
|
514
|
-
//#region src/lib/api/middleware/index.ts
|
|
515
|
-
var Middleware = class {
|
|
516
|
-
constructor(options) {
|
|
517
|
-
this.options = options;
|
|
518
|
-
}
|
|
519
|
-
};
|
|
520
|
-
//#endregion
|
|
521
|
-
export { Api, Middleware };
|
|
1
|
+
const e={status:404},t=(e,t)=>{let n=e.split(`/`).slice(1),r=[],i=0;for(let e of n){if(e.startsWith(`:`)){r.push({value:``,paramName:e.slice(1)}),i++;continue}r.push({value:e})}return{segments:r,methods:t,paramStarts:Array(i),paramEnds:Array(i)}},n=(e,t)=>{let n=1,r=0;if(t===`/`)return e.segments.length===0;for(let i of e.segments){let a=t.indexOf(`/`,n),o=t.length;if(a!==-1&&(o=a),i.paramName){if(o===n)return!1;e.paramStarts[r]=n,e.paramEnds[r]=o,r++}else{let e=o-n;if(i.value.length!==e||!t.startsWith(i.value,n))return!1}n=a===-1?t.length:a+1}return n===t.length},r=(e,t)=>{let n={},r=0;for(let i of e.segments)i.paramName&&(n[i.paramName]=t.slice(e.paramStarts[r],e.paramEnds[r]),r++);return n},i=e=>{let t=e.indexOf(`://`),n=e.indexOf(`/`,t+3);if(n===-1)return`/`;let r=e.indexOf(`?`,n),i;return i=r===-1?e.slice(n):e.slice(n,r),i.length>1&&i.endsWith(`/`)?i.slice(0,-1):i},a=a=>{let o=Object.create(null),s=[],c=[];for(let[e,n]of Object.entries(a)){let r=n;if(e.includes(`*`)){c.push({pattern:new URLPattern({pathname:e}),methods:r});continue}if(e.includes(`:`)){s.push(t(e,r));continue}o[e]=r}return t=>{let a=i(t.url),l=t.method,u=o[a]?.[l];if(u)return u(t);for(let e of s){if(!n(e,a))continue;let i=e.methods[l];if(i)return t.params=r(e,a),i(t)}for(let e of c){let n=e.pattern.exec({pathname:a});if(!n)continue;let r=e.methods[l];if(r)return t.params=n.pathname.groups,r(t)}return new Response(`Not found`,e)}},o=e=>{if(Array.isArray(e))return e.map(o);if(typeof e!=`object`||!e)return e;let t=e;if(typeof t.$ref==`string`&&t.$ref.startsWith(`#/$defs/`))return{$ref:`#/components/schemas/${t.$ref.slice(8)}`};let n={};for(let e in t)n[e]=o(t[e]);return n},s=e=>{let t=e.$defs;if(!t||typeof t!=`object`)return{schema:e,components:void 0};let n=o({...e});delete n.$defs;let r=t,i={};for(let e in r)i[e]=o(r[e]);return{schema:n,components:{schemas:i}}},c=(e,t=`input`)=>{let n=e[`~standard`].jsonSchema[t]({target:`draft-2020-12`});return s(n)},l=e=>{let t=e[`~standard`];return t&&`description`in t&&typeof t.description==`string`?t.description:``},u=e=>{let t=e.meta;if(typeof t!=`function`)return;let n=t();if(typeof n==`object`&&n&&typeof n.id==`string`)return n.id},d=(e,t)=>typeof t.id==`string`?t.id:u(e),f=[`body`,`query`,`headers`,`cookies`,`params`],p=(e,t=`input`)=>{let n=c(e,t),{schema:r,components:i}=n,a=d(e,r);if(a){let e={...r};return delete e.id,delete e.$schema,{schema:{$ref:`#/components/schemas/${a}`},components:{...i,schemas:{...i?.schemas,[a]:e}}}}if(r.$schema){let e={...r};return delete e.$schema,{schema:e,components:i}}return n},m=(e,t=`input`)=>{let{schema:n,components:r}=c(e,t),i={...n};return delete i.$schema,delete i.id,{schema:i,components:r}},h=(e,t)=>{let{schema:n,components:r}=m(e);if(n.type!==`object`||!n.properties)return{parameters:[],components:r};let i=[],a=n.required??[];for(let e in n.properties){let r=n.properties[e],o=a.includes(e);i.push({name:e,in:t,required:o,schema:r})}return{parameters:i,components:r}},g=e=>e.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g,`{$1}`),_=e=>{let t=e.match(/:([a-zA-Z_][a-zA-Z0-9_]*)/g);return t?t.map(e=>e.slice(1)):[]},ee=[[`query`,`query`],[`headers`,`header`],[`cookies`,`cookie`]],te=(e,t)=>{let n=[],r=[];for(let[t,i]of ee)if(e[t]){let{parameters:a,components:o}=h(e[t],i);n.push(...a),o&&r.push(o)}let i=_(t);if(i.length>0&&e.params){let{schema:t,components:a}=m(e.params);if(a&&r.push(a),t.type===`object`&&t.properties)for(let e of i){let r=t.properties[e];r&&n.push({name:e,in:`path`,required:!0,schema:r})}}return{parameters:n,components:r}},ne=e=>{let{schema:t,components:n}=p(e);return{components:n,requestBody:{required:!0,content:{"application/json":{schema:t}}}}},re=e=>{let t={},n=[];if(!e)return{responses:t,components:n};for(let r in e){let i=String(r),a=e[Number(r)];if(!a)continue;let o=l(a),{schema:s,components:c}=p(a,`output`);c&&n.push(c),t[i]={description:o||`Response with status ${i}`,content:{"application/json":{schema:s}}}}return{responses:t,components:n}},v=(e,t,n)=>{let{request:r,response:i}=y(e,t),a=e.path;n&&(a=n+e.path);let{parameters:o,components:s}=te(r,a),{responses:c,components:l}=re(i),u={responses:c},d=[];d.push(...l),d.push(...s);for(let t of[`summary`,`description`,`operationId`])e[t]&&(u[t]=e[t]);e.tags&&e.tags.length>0&&(u.tags=e.tags),o.length>0&&(u.parameters=o);let f=r.body;if(f){let{requestBody:e,components:t}=ne(f);u.requestBody=e,t&&d.push(t)}return{operation:u,components:d}},y=(e,t)=>{let n={},r={};for(let i of[...t,...e.middlewares??[]])b(n,i.options.request),x(r,i.options.response);b(n,e.request),x(r,e.response);let i;return Object.keys(r).length>0&&(i=r),{request:n,response:i}},b=(e,t)=>{if(t)for(let n of f)t[n]&&(e[n]=t[n])},x=(e,t)=>{if(t)for(let n in t){let r=Number(n),i=t[r];i&&(e[r]=i)}},S=e=>{let t={openapi:`3.1.0`,info:{title:e.title,description:e.description,version:e.version},paths:{}};e.servers&&e.servers.length>0&&(t.servers=e.servers);let n={};for(let r of e.routes){let i=r.path;e.prefix&&(i=e.prefix+r.path);let a=g(i),o=r.method.toLowerCase();t.paths[a]??={};let{operation:s,components:c}=v(r,e.globalMiddlewares??[],e.prefix);t.paths[a][o]=s,c.forEach(e=>{Object.assign(n,e.schemas??{})})}let r=Object.keys(n).length>0;return(e.securitySchemes||r)&&(t.components={},e.securitySchemes&&(t.components.securitySchemes=e.securitySchemes),r&&(t.components.schemas=n)),t};var C=class extends Error{constructor(e){super(e),this.name=`ParseError`}},w=class extends Error{constructor(e){super(e),this.name=`ValidationError`}},T=class extends Error{constructor(e){super(e),this.name=`SchemaConfigError`}};function ie(e){if(e.path?.length){let t=``;for(let n of e.path){let e=typeof n==`object`?n.key:n;if(typeof e==`string`||typeof e==`number`)t?t+=`.${e}`:t+=e;else return null}return t}return null}const ae=e=>ie(e)??`unknown`,E=e=>e.map(e=>`${ae(e)}: ${e.message??`validation failed`}`).join(`, `),D=e=>!(!e.body||e.query||e.headers||e.cookies||e.params),O=e=>{let t=e[`~standard`].validate;return async e=>{let n=e.headers.get(`content-type`)??``,r;if(n.startsWith(`application/json`))try{r=await e.json()}catch{throw new C(`Invalid JSON body`)}else r=await e.text();let i=t(r);if(i instanceof Promise)throw new T(`Async schema validation is not supported`);if(i.issues)throw new w(E(i.issues))}},k=e=>{if(!e.issues)return e.value;throw new w(E(e.issues))},A=(e,t=!1)=>{let n=e;t&&e.includes(`+`)&&(n=e.replaceAll(`+`,` `));try{return decodeURIComponent(n)}catch{return n}},j=(e,t,n)=>{let r=e[t];if(r===void 0){e[t]=n;return}if(Array.isArray(r)){r.push(n);return}e[t]=[r,n]},M=(e,t)=>{let n=e[`~standard`].validate(t);if(n instanceof Promise)throw new T(`Async schema validation is not supported`);return k(n)},N=async(e,t,n)=>{if(!t)return!0;if(n?.parsed)return M(t,n.value);let r=e.headers.get(`content-type`)??``,i;if(r.startsWith(`application/json`))try{i=await e.json()}catch{throw new C(`Invalid JSON body`)}else i=await e.text();return n&&(n.parsed=!0,n.value=i),M(t,i)},P=(e,t)=>{if(!t)return!0;let n=e.url.indexOf(`?`);if(n===-1)return M(t,{});let r=e.url.indexOf(`#`,n+1),i=e.url.length;r!==-1&&(i=r);let a={},o=n+1;for(;o<=i;){let t=e.url.indexOf(`&`,o),n=i;if(t!==-1&&t<i&&(n=t),n>o){let t=e.url.indexOf(`=`,o),r=t!==-1&&t<n,i=e.url.slice(o,n),s=``;r&&(i=e.url.slice(o,t),s=e.url.slice(t+1,n)),j(a,A(i,!0),A(s,!0))}if(t===-1||t>=i)break;o=t+1}return M(t,a)},F=(e,t)=>{if(!t)return!0;let n={};for(let[t,r]of e.headers)n[t]=r;return M(t,n)},I=(e,t)=>{if(!t)return!0;let n=e.headers.get(`cookie`)??``,r={},i=0;for(;i<n.length;){let e=n.indexOf(`;`,i),t=n.length;e!==-1&&(t=e);let a=n.indexOf(`=`,i);if(a!==-1&&a<t){let e=n.slice(i,a).trim();if(e){let i=n.slice(a+1,t).trim();r[e]=A(i)}}if(e===-1)break;i=e+1}return M(t,r)},L=(e,t)=>t?M(t,e.params??{}):!0,R=async(e,t)=>{let n=e.schema;if(n)try{if(n.body){let r=await N(e.req,n.body,e.bodyCache);t&&(t.body=r)}if(n.query){let r=P(e.req,n.query);t&&(t.query=r)}if(n.headers){let r=F(e.req,n.headers);t&&(t.headers=r)}if(n.cookies){let r=I(e.req,n.cookies);t&&(t.cookies=r)}if(n.params){let r=L(e.req,n.params);t&&(t.params=r)}}catch(e){return e}},z=e=>{if(e){if(D(e)){let t=e.body;if(!t)return;let n=O(t);return async e=>{try{await n(e)}catch(e){return e}}}return(t,n)=>R({req:t,schema:e,bodyCache:n})}},B={"Content-Type":`text/html`},V={status:400},H=(e,t)=>e===200?Response.json(t):Response.json(t,{status:e}),U=(e,t)=>e===200?new Response(t):new Response(t,{status:e}),W=(e,t)=>new Response(t,{status:e,headers:B}),G=(e,t)=>Response.redirect(t,e),K=e=>Response.json({message:e},V),q=e=>{if(e instanceof w||e instanceof C)return K(e.message);throw e},oe=e=>(t,n)=>{let r=e[t];if(!r)return H(t,n);try{M(r,n)}catch(e){return q(e)}return H(t,n)},J=Object.freeze({body:void 0,query:void 0,headers:void 0,cookies:void 0,params:void 0}),se={req:J,get:()=>{},json:H,text:U,html:W,redirect:G},Y=(e,t,n,r)=>{let i=Object.create(se);return i.raw=e,t&&(i.req=t),n&&(i.get=n),r&&(i.json=r),i},X=e=>e!==`/`&&e.endsWith(`/`)?e.slice(0,-1):e,ce=e=>{let t=X(e.path)||`/`;if(!e.prefix)return t;let n=X(e.prefix);return n===`/`?t:t===`/`?n:n+t},le=e=>{let t=0;e.request?.body!==void 0&&t++;for(let n of e.middlewares)if(n.options.request?.body!==void 0&&(t++,t>1))return!0;return!1},ue=e=>e===void 0||e===!0?{input:!0,output:!0}:e===!1?{input:!1,output:!1}:{input:e.input!==!1,output:e.output!==!1},de=[],Z=e=>e instanceof Response?e:typeof e==`string`?new Response(e):Response.json(e),Q=async(e,t)=>{let n=Z(e),r=t?.[n.status];if(!r)return n;let i=e;e instanceof Response&&(i=await n.clone().json());try{M(r,i)}catch(e){return q(e)}return n},fe=(e,t)=>{if(e instanceof Response)return Q(e,t);let n=Z(e),r=t?.[n.status];if(!r)return n;try{M(r,e)}catch(e){return q(e)}return n},pe=(e,t,n,r=!1,i=!1)=>{let a=i?n:void 0,o;if(r&&t&&D(t)){let e=t.body;e&&(o=O(e))}let s=r&&!o?z(t):void 0,c=e();if(c instanceof Promise)return async t=>{if(o)try{await o(t)}catch(e){return q(e)}if(s){let e=await s(t);if(e)return q(e)}let n=await e();return Q(n,a)};let l=fe(c,a);if(!o&&!s)return l instanceof Promise?async()=>l:()=>l;if(o)return async e=>{try{await o(e)}catch(e){return q(e)}return l};let u=s;return u?async e=>{let t=await u(e);return t?q(t):l}:l instanceof Promise?async()=>l:()=>l},me=e=>t=>{let n=e(Y(t));return n instanceof Promise?n.then(e=>e instanceof Response?e:Z(e)):n instanceof Response?n:Z(n)},he=(e,t)=>e.middlewares?.length?t.length===0?e.middlewares:[...t,...e.middlewares]:t,ge=(e,t,n)=>{if(!e.input)return!1;if(t)return!0;for(let e of n)if(e.options.request)return!0;return!1},$=async(e,t)=>{let n,r,i;t.validateInput&&le({middlewares:t.middlewares,request:t.routeRequest})&&(i={parsed:!1,value:void 0});let a;t.validateOutput&&t.routeResponse&&(a=oe(t.routeResponse)),t.middlewares.length>0&&(r=e=>n?.[e]);for(let a of t.middlewares){let{request:o,handler:s}=a.options,c=J;if(t.validateInput&&o){let t={},n=await R({req:e,schema:o,bodyCache:i},t);if(n)return q(n);c=t}let l=await s(Y(e,c,r));if(l instanceof Response)return l;l&&(n||={},Object.assign(n,l))}let o=J;if(t.validateInput&&t.routeRequest){let n={},r=await R({req:e,schema:t.routeRequest,bodyCache:i},n);if(r)return q(r);o=n}let s=Y(e,o,r,a);return t.handler(s)},_e=(e,t,n)=>{let r=he(e,t),i=e.handler,a=r.length>0,o=ge(n,e.request,r),s=n.output&&!!e.response;if(!a&&typeof i==`function`&&i.length===0)return pe(i,e.request,e.response,o,s);if(!a&&typeof i==`function`&&i.length===1&&!o&&!s)return me(i);let c={middlewares:r,routeRequest:e.request,routeResponse:e.response,validateInput:o,validateOutput:s,handler:i};return e=>$(e,c)},ve=(e,t,n,r)=>{let i={};for(let a of e){let e=ce({prefix:t,path:a.path});i[e]||(i[e]={}),i[e][a.method]=_e(a,n,r)}return i};var ye=class{options;routes=[];routesDirty=!0;compiled;constructor(e={}){this.options=e}defineRoute(e){this.routes.push(e),this.routesDirty=!0}fetch=e=>this.ensureCompiled().fetch(e);getRouteHandlers(){return this.ensureCompiled().routes}getOpenApiSpec(){return S({title:this.options.openapi?.title??`API`,description:this.options.openapi?.description,version:this.options.openapi?.version??`1.0.0`,prefix:this.options.prefix,servers:this.options.openapi?.servers,securitySchemes:this.options.openapi?.securitySchemes,routes:this.routes,globalMiddlewares:this.options.middlewares})}serve(e,t){let n=Bun.serve({port:e,routes:this.getRouteHandlers(),fetch:()=>new Response(`Not found`,{status:404})});t&&t(n)}ensureCompiled(){if(!this.routesDirty&&this.compiled)return this.compiled;let e=ve(this.routes,this.options.prefix,this.options.middlewares??de,ue(this.options.validation));return this.compiled={routes:e,fetch:a(e)},this.routesDirty=!1,this.compiled}},be=class{options;constructor(e){this.options=e}};export{ye as Api,be as Middleware};
|
package/dist/lib/cache/index.cjs
CHANGED
|
@@ -1,74 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,
|
|
2
|
-
const require_lib_errors_index = require("../errors/index.cjs");
|
|
3
|
-
//#region src/lib/cache/index.ts
|
|
4
|
-
var Cache = class {
|
|
5
|
-
options;
|
|
6
|
-
serialize;
|
|
7
|
-
deserialize;
|
|
8
|
-
constructor(options) {
|
|
9
|
-
this.options = options;
|
|
10
|
-
this.serialize = options.serializer ?? JSON.stringify;
|
|
11
|
-
this.deserialize = options.deserializer ?? ((raw) => JSON.parse(raw));
|
|
12
|
-
}
|
|
13
|
-
async get(key) {
|
|
14
|
-
if (!this.isEnabled) return this.fail("NotFoundError", `Key ${key} not found`);
|
|
15
|
-
const resolvedKey = this.resolveKey(key);
|
|
16
|
-
const [error, value] = await require_lib_errors_index.mightThrow(this.options.redis.get(resolvedKey));
|
|
17
|
-
if (error) return this.fail("CacheError", `Unable to get value for key ${key}`);
|
|
18
|
-
if (value === null || value === void 0) return this.fail("NotFoundError", `Key ${key} not found`);
|
|
19
|
-
const [deserializeErr, deserialized] = require_lib_errors_index.mightThrowSync(() => this.deserialize(value));
|
|
20
|
-
if (deserializeErr) return this.fail("CacheError", `Unable to deserialize value for key ${key}`);
|
|
21
|
-
return require_lib_errors_index.ok(deserialized);
|
|
22
|
-
}
|
|
23
|
-
async set(key, value) {
|
|
24
|
-
if (!this.isEnabled) return require_lib_errors_index.ok(value);
|
|
25
|
-
const [serializeErr, serialized] = require_lib_errors_index.mightThrowSync(() => this.serialize(value));
|
|
26
|
-
if (serializeErr) return this.fail("CacheError", `Unable to serialize value for key ${key}`);
|
|
27
|
-
if (serialized === null || serialized === void 0) return this.fail("CacheError", `Unable to serialize value for key ${key}`);
|
|
28
|
-
const [ttlErr, ttl] = require_lib_errors_index.mightThrowSync(() => this.resolveTTL(key, value));
|
|
29
|
-
if (ttlErr) return this.fail("InvalidTTLError", `Unable to resolve ttl for key ${key}`);
|
|
30
|
-
if (!this.isTTLValid(ttl)) return this.fail("InvalidTTLError", `Unable to save records with ttl equal to ${ttl}`);
|
|
31
|
-
const resolvedKey = this.resolveKey(key);
|
|
32
|
-
const [setError] = await require_lib_errors_index.mightThrow(this.getSetPromise(resolvedKey, serialized, ttl));
|
|
33
|
-
if (setError) return this.fail("CacheError", `Unable to set value for key ${key}`);
|
|
34
|
-
return require_lib_errors_index.ok(value);
|
|
35
|
-
}
|
|
36
|
-
async delete(key) {
|
|
37
|
-
if (!this.isEnabled) return require_lib_errors_index.ok(0);
|
|
38
|
-
const resolvedKey = this.resolveKey(key);
|
|
39
|
-
const [error, data] = await require_lib_errors_index.mightThrow(this.options.redis.del(resolvedKey));
|
|
40
|
-
if (error) return this.fail("CacheError", `Unable to delete key ${key}`);
|
|
41
|
-
return require_lib_errors_index.ok(data);
|
|
42
|
-
}
|
|
43
|
-
fail(type, message) {
|
|
44
|
-
this.options.onError?.({
|
|
45
|
-
type,
|
|
46
|
-
message
|
|
47
|
-
});
|
|
48
|
-
return require_lib_errors_index.err(type, message);
|
|
49
|
-
}
|
|
50
|
-
get isEnabled() {
|
|
51
|
-
return this.options.enabled !== false;
|
|
52
|
-
}
|
|
53
|
-
resolveKey(key) {
|
|
54
|
-
if (this.options.prefix) return `${this.options.prefix}:${key}`;
|
|
55
|
-
return key;
|
|
56
|
-
}
|
|
57
|
-
resolveTTL(key, value) {
|
|
58
|
-
if (typeof this.options.ttl === "function") return this.options.ttl(key, value);
|
|
59
|
-
return this.options.ttl;
|
|
60
|
-
}
|
|
61
|
-
getSetPromise(key, value, ttl) {
|
|
62
|
-
if (ttl === void 0 || ttl === null) return this.options.redis.set(key, value);
|
|
63
|
-
return this.options.redis.set(key, value, "PX", ttl);
|
|
64
|
-
}
|
|
65
|
-
isTTLValid(ttl) {
|
|
66
|
-
if (ttl === void 0 || ttl === null) return true;
|
|
67
|
-
if (!Number.isFinite(ttl)) return false;
|
|
68
|
-
if (!Number.isInteger(ttl)) return false;
|
|
69
|
-
if (ttl < 0) return false;
|
|
70
|
-
return true;
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
//#endregion
|
|
74
|
-
exports.Cache = Cache;
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../errors/index.cjs");var t=class extends Error{constructor(e){super(e),this.name=`CacheError`}},n=class extends Error{constructor(e){super(e),this.name=`InvalidTTLError`}},r=class extends Error{constructor(e){super(e),this.name=`NotFoundError`}},i=class extends Error{constructor(e){super(e),this.name=`SerializationError`}},a=class extends Error{constructor(e){super(e),this.name=`DeserializationError`}},o=class{options;serialize;deserialize;constructor(e){this.options=e,this.serialize=e.serializer??JSON.stringify,this.deserialize=e.deserializer??(e=>JSON.parse(e))}async get(n){if(!this.isEnabled)throw new r(`Key ${n} not found`);let i=this.resolveKey(n),[o,s]=await e.mightThrow(this.options.redis.get(i));if(o)throw new t(`Unable to get value for key ${n}`);if(s==null)throw new r(`Key ${n} not found`);let[c,l]=e.mightThrowSync(()=>this.deserialize(s));if(c)throw new a(`Unable to deserialize value for key ${n}`);return l}async set(r,a){if(!this.isEnabled)return a;let[o,s]=e.mightThrowSync(()=>this.serialize(a));if(o||s==null)throw new i(`Unable to serialize value for key ${r}`);let[c,l]=e.mightThrowSync(()=>this.resolveTTL(r,a));if(c)throw new n(`Unable to resolve ttl for key ${r}`);if(!this.isTTLValid(l))throw new n(`Unable to save records with ttl equal to ${l}`);let u=this.resolveKey(r),[d]=await e.mightThrow(this.getSetPromise(u,s,l));if(d)throw new t(`Unable to set value for key ${r}`);return a}async delete(n){if(!this.isEnabled)return 0;let r=this.resolveKey(n),[i,a]=await e.mightThrow(this.options.redis.del(r));if(i)throw new t(`Unable to delete key ${n}`);return a}get isEnabled(){return this.options.enabled!==!1}resolveKey(e){return this.options.prefix?`${this.options.prefix}:${e}`:e}resolveTTL(e,t){return typeof this.options.ttl==`function`?this.options.ttl(e,t):this.options.ttl}getSetPromise(e,t,n){return n==null?this.options.redis.set(e,t):this.options.redis.set(e,t,`PX`,n)}isTTLValid(e){return e==null?!0:!(!Number.isFinite(e)||!Number.isInteger(e)||e<0)}};exports.Cache=o;
|
|
@@ -1,48 +1 @@
|
|
|
1
|
-
|
|
2
|
-
type CacheError = "CacheError" | "InvalidTTLError" | "NotFoundError";
|
|
3
|
-
type CacheOptions<T> = {
|
|
4
|
-
redis: Bun.RedisClient;
|
|
5
|
-
ttl?: number | ((key: string, value: T) => number);
|
|
6
|
-
enabled?: boolean;
|
|
7
|
-
prefix?: string;
|
|
8
|
-
serializer?: (value: T) => string;
|
|
9
|
-
deserializer?: (raw: string) => T;
|
|
10
|
-
onError?: (error: {
|
|
11
|
-
type: CacheError;
|
|
12
|
-
message: string;
|
|
13
|
-
}) => void;
|
|
14
|
-
};
|
|
15
|
-
//#endregion
|
|
16
|
-
//#region src/lib/cache/index.d.ts
|
|
17
|
-
declare class Cache<T> {
|
|
18
|
-
private options;
|
|
19
|
-
private serialize;
|
|
20
|
-
private deserialize;
|
|
21
|
-
constructor(options: CacheOptions<T>);
|
|
22
|
-
get(key: string): Promise<readonly [{
|
|
23
|
-
readonly type: "NotFoundError";
|
|
24
|
-
readonly message: string;
|
|
25
|
-
}, null] | readonly [{
|
|
26
|
-
readonly type: "CacheError";
|
|
27
|
-
readonly message: string;
|
|
28
|
-
}, null] | readonly [null, T]>;
|
|
29
|
-
set(key: string, value: T): Promise<readonly [{
|
|
30
|
-
readonly type: "CacheError";
|
|
31
|
-
readonly message: string;
|
|
32
|
-
}, null] | readonly [null, T] | readonly [{
|
|
33
|
-
readonly type: "InvalidTTLError";
|
|
34
|
-
readonly message: string;
|
|
35
|
-
}, null]>;
|
|
36
|
-
delete(key: string): Promise<readonly [null, number] | readonly [{
|
|
37
|
-
readonly type: "CacheError";
|
|
38
|
-
readonly message: string;
|
|
39
|
-
}, null]>;
|
|
40
|
-
private fail;
|
|
41
|
-
private get isEnabled();
|
|
42
|
-
private resolveKey;
|
|
43
|
-
private resolveTTL;
|
|
44
|
-
private getSetPromise;
|
|
45
|
-
private isTTLValid;
|
|
46
|
-
}
|
|
47
|
-
//#endregion
|
|
48
|
-
export { Cache };
|
|
1
|
+
export type * from './index.d.mts'
|