equipped 5.0.13 → 5.0.15
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/CHANGELOG.md +15 -0
- package/dist/cjs/cache/base.cjs.map +1 -1
- package/dist/cjs/cache/base.min.cjs.map +1 -1
- package/dist/cjs/cache/types/in-memory.cjs +3 -3
- package/dist/cjs/cache/types/in-memory.cjs.map +1 -1
- package/dist/cjs/cache/types/in-memory.min.cjs +1 -1
- package/dist/cjs/cache/types/in-memory.min.cjs.map +1 -1
- package/dist/cjs/cache/types/redis.cjs +1 -1
- package/dist/cjs/cache/types/redis.cjs.map +1 -1
- package/dist/cjs/cache/types/redis.min.cjs +1 -1
- package/dist/cjs/cache/types/redis.min.cjs.map +1 -1
- package/dist/cjs/dbs/mongo/api.cjs +4 -6
- package/dist/cjs/dbs/mongo/api.cjs.map +1 -1
- package/dist/cjs/dbs/mongo/api.min.cjs +1 -1
- package/dist/cjs/dbs/mongo/api.min.cjs.map +1 -1
- package/dist/cjs/dbs/mongo/index.cjs +8 -8
- package/dist/cjs/dbs/mongo/index.cjs.map +1 -1
- package/dist/cjs/dbs/mongo/index.min.cjs +1 -1
- package/dist/cjs/dbs/mongo/index.min.cjs.map +1 -1
- package/dist/cjs/server/openapi.cjs +1 -1
- package/dist/cjs/server/openapi.cjs.map +1 -1
- package/dist/cjs/server/openapi.min.cjs +1 -1
- package/dist/cjs/server/openapi.min.cjs.map +1 -1
- package/dist/esm/cache/base.min.mjs.map +1 -1
- package/dist/esm/cache/base.mjs.map +1 -1
- package/dist/esm/cache/types/in-memory.min.mjs +1 -1
- package/dist/esm/cache/types/in-memory.min.mjs.map +1 -1
- package/dist/esm/cache/types/in-memory.mjs +3 -3
- package/dist/esm/cache/types/in-memory.mjs.map +1 -1
- package/dist/esm/cache/types/redis.min.mjs +1 -1
- package/dist/esm/cache/types/redis.min.mjs.map +1 -1
- package/dist/esm/cache/types/redis.mjs +1 -1
- package/dist/esm/cache/types/redis.mjs.map +1 -1
- package/dist/esm/dbs/mongo/api.min.mjs +1 -1
- package/dist/esm/dbs/mongo/api.min.mjs.map +1 -1
- package/dist/esm/dbs/mongo/api.mjs +4 -6
- package/dist/esm/dbs/mongo/api.mjs.map +1 -1
- package/dist/esm/dbs/mongo/index.min.mjs +1 -1
- package/dist/esm/dbs/mongo/index.min.mjs.map +1 -1
- package/dist/esm/dbs/mongo/index.mjs +8 -8
- package/dist/esm/dbs/mongo/index.mjs.map +1 -1
- package/dist/esm/server/openapi.min.mjs +1 -1
- package/dist/esm/server/openapi.min.mjs.map +1 -1
- package/dist/esm/server/openapi.mjs +1 -1
- package/dist/esm/server/openapi.mjs.map +1 -1
- package/dist/types/{base-k4t2NepI.d.ts → base-8yVXb67P.d.ts} +2 -2
- package/dist/types/cache/index.d.ts +5 -5
- package/dist/types/cache/types/in-memory.js +3 -3
- package/dist/types/cache/types/redis.js +1 -1
- package/dist/types/dbs/index.d.ts +2 -2
- package/dist/types/dbs/mongo/api.js +4 -6
- package/dist/types/dbs/mongo/index.js +8 -8
- package/dist/types/{index-BtF0VClz.d.ts → index-BS53hVyq.d.ts} +2 -2
- package/dist/types/{index-CegB2bgP.d.ts → index-CfuXekZB.d.ts} +2 -1
- package/dist/types/index.d.ts +3 -3
- package/dist/types/instance/index.d.ts +3 -3
- package/dist/types/server/index.d.ts +4 -4
- package/dist/types/server/openapi.js +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/server/openapi.ts"],"sourcesContent":["import { convert } from '@openapi-contrib/json-schema-to-openapi-schema'\nimport { OpenAPIV3_1 } from 'openapi-types'\nimport { capitalize, JsonSchema } from 'valleyed'\n\nimport { ServerConfig } from './pipes'\nimport { Router } from './routes'\nimport { Route } from './types'\n\ndeclare module 'openapi-types' {\n\tnamespace OpenAPIV3 {\n\t\tinterface Document {\n\t\t\t'x-tagGroups': { name: string; tags: string[] }[]\n\t\t}\n\t\tinterface TagObject {\n\t\t\t'x-displayName': string\n\t\t}\n\t}\n}\n\nexport type OpenApiSchemaDef = {\n\trequest: Partial<Record<'body' | 'query' | 'params' | 'headers' | 'response', JsonSchema>>\n\tresponse: Partial<Record<'response' | 'responseHeaders', { status: number; schema: JsonSchema; contentType: string }[]>>\n}\n\nexport class OpenApi {\n\t#registeredTags: Record<string, boolean> = {}\n\t#registeredTagGroups: Record<string, { name: string; tags: string[] }> = {}\n\t#baseOpenapiDoc: OpenAPIV3_1.Document\n\n\tconstructor(private config: ServerConfig) {\n\t\tthis.#baseOpenapiDoc = {\n\t\t\topenapi: '3.0.0',\n\t\t\tinfo: {\n\t\t\t\ttitle: `${config.app.name} ${config.app.id}`,\n\t\t\t\tversion: config.config.openapi.docsVersion ?? '',\n\t\t\t},\n\t\t\tservers: config.config.openapi.docsBaseUrl?.map((url) => ({ url })),\n\t\t\tpaths: {},\n\t\t\tcomponents: {\n\t\t\t\tschemas: {},\n\t\t\t\tsecuritySchemes: {\n\t\t\t\t\tAuthorization: {\n\t\t\t\t\t\ttype: 'apiKey',\n\t\t\t\t\t\tname: 'authorization',\n\t\t\t\t\t\tin: 'header',\n\t\t\t\t\t},\n\t\t\t\t\tRefreshToken: {\n\t\t\t\t\t\ttype: 'apiKey',\n\t\t\t\t\t\tname: 'x-refresh-token',\n\t\t\t\t\t\tin: 'header',\n\t\t\t\t\t},\n\t\t\t\t\tApiKey: {\n\t\t\t\t\t\ttype: 'apiKey',\n\t\t\t\t\t\tname: 'x-api-key',\n\t\t\t\t\t\tin: 'header',\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\ttags: [],\n\t\t\t'x-tagGroups': [],\n\t\t}\n\t}\n\n\tcleanPath(path: string) {\n\t\tlet cleaned = path.replace(/(\\/\\s*)+/g, '/')\n\t\tif (!cleaned.startsWith('/')) cleaned = `/${cleaned}`\n\t\tif (cleaned !== '/' && cleaned.endsWith('/')) cleaned = cleaned.slice(0, -1)\n\t\treturn cleaned\n\t}\n\n\tasync register(route: Route<any>, def: OpenApiSchemaDef) {\n\t\tif (route.hide) return\n\n\t\tconst tag = this.#buildTag(route.groups ?? [])\n\n\t\tconst cleanPath = this.cleanPath(route.path)\n\t\tconst operationId = `(${route.method.toUpperCase()}) ${cleanPath}`\n\t\tawait this.#addRouteToOpenApiDoc(cleanPath, route.method.toLowerCase(), def, {\n\t\t\toperationId,\n\t\t\tsummary: route.title ?? cleanPath,\n\t\t\tdescription: route.descriptions?.join('\\n\\n'),\n\t\t\ttags: tag ? [tag] : undefined,\n\t\t\tsecurity: route.security,\n\t\t})\n\t}\n\n\tasync #addRouteToOpenApiDoc(path: string, method: string, def: OpenApiSchemaDef, methodObj: OpenAPIV3_1.OperationObject) {\n\t\tif (def.response.response?.length) {\n\t\t\tmethodObj.responses ??= {}\n\t\t\tfor (const resp of def.response.response) {\n\t\t\t\tmethodObj.responses[resp.status] ??= { description: '', content: {} }\n\t\t\t\tconst res = methodObj.responses[resp.status] as OpenAPIV3_1.ResponseObject\n\t\t\t\tres.content![resp.contentType] = { schema: await convert(this.#visit(resp.schema)) }\n\t\t\t}\n\t\t}\n\n\t\tif (def.response.responseHeaders?.length) {\n\t\t\tmethodObj.responses ??= {}\n\t\t\tfor (const resp of def.response.responseHeaders) {\n\t\t\t\tmethodObj.responses[resp.status] ??= { description: '', content: {} }\n\t\t\t\tmethodObj.responses[resp.status] as OpenAPIV3_1.ResponseObject\n\t\t\t\tconst res = methodObj.responses[resp.status] as OpenAPIV3_1.ResponseObject\n\t\t\t\tres.headers = { schema: (await convert(this.#visit(resp.schema))) as any }\n\t\t\t}\n\t\t}\n\n\t\tif (def.request.body)\n\t\t\tmethodObj.requestBody = {\n\t\t\t\trequired: true,\n\t\t\t\tcontent: {\n\t\t\t\t\t'application/json': { schema: await convert(this.#visit(def.request.body)) },\n\t\t\t\t},\n\t\t\t}\n\n\t\tconst parameters: OpenAPIV3_1.ParameterObject[] = []\n\n\t\tconst addParams = async (location: 'query' | 'path' | 'header', schema: JsonSchema | undefined) => {\n\t\t\tif (!schema) return\n\t\t\tconst flat = this.#flattenForParameters(schema)\n\t\t\tfor (const schema of flat) {\n\t\t\t\tif (!schema.properties) continue\n\t\t\t\tfor (const [name, value] of Object.entries(schema.properties))\n\t\t\t\t\tparameters.push({\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tin: location,\n\t\t\t\t\t\tschema: await convert(this.#visit(value)),\n\t\t\t\t\t\trequired: (schema.required || []).includes(name),\n\t\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\tawait Promise.all([\n\t\t\taddParams('query', def.request.query),\n\t\t\taddParams('path', def.request.params),\n\t\t\taddParams('header', def.request.headers),\n\t\t])\n\t\tif (parameters.length) methodObj.parameters = parameters\n\n\t\tconst base = this.#baseOpenapiDoc\n\t\tif (!base.paths) base.paths = {}\n\t\tif (!base.paths[path]) base.paths[path] = {}\n\t\tbase.paths[path]![method] = methodObj\n\t}\n\n\trouter() {\n\t\tconst jsonPath = '/openapi.json'\n\t\tconst router = new Router({ path: this.config.config.openapi.docsPath ?? '/', hide: true })\n\t\trouter.get('/')((req) => req.res({ body: this.#html(`.${jsonPath}`), contentType: 'text/html' }))\n\t\trouter.get(jsonPath)((req) => req.res({ body: this.#baseOpenapiDoc }))\n\t\treturn router\n\t}\n\n\t#flattenForParameters(node: JsonSchema): JsonSchema[] {\n\t\tconst { allOf, oneOf, anyOf, ...schema } = node\n\t\tif (allOf) return allOf.flatMap((n) => this.#flattenForParameters(n))\n\t\treturn [schema]\n\t}\n\n\t#visit(node: JsonSchema) {\n\t\tif (!node || typeof node !== 'object') return node\n\t\tif (typeof node.$refId === 'string') {\n\t\t\tconst { $refId: id, ...rest } = node\n\t\t\tconst res = this.#visit(rest)\n\t\t\tif (this.#baseOpenapiDoc.components?.schemas) {\n\t\t\t\tthis.#baseOpenapiDoc.components.schemas[id] = res\n\t\t\t\treturn { $ref: `#/components/schemas/${id}` }\n\t\t\t} else return res\n\t\t}\n\n\t\tif (Array.isArray(node)) return node.map((n) => this.#visit(n)) as any\n\t\treturn Object.fromEntries(Object.entries(node).map(([key, value]) => [key, this.#visit(value as any)]))\n\t}\n\n\t#buildTag(groups: NonNullable<Route<any>['groups']>) {\n\t\tif (!groups.length) return undefined\n\t\tconst parsed = groups.map((g) => (typeof g === 'string' ? { name: g } : g))\n\t\tconst name = parsed.map((g) => g.name).join(' > ')\n\t\tconst displayName = parsed.at(-1)?.name ?? ''\n\t\tconst description = parsed\n\t\t\t.map((g) => g.description?.trim() ?? '')\n\t\t\t.filter(Boolean)\n\t\t\t.join('\\n\\n\\n\\n')\n\n\t\tif (!this.#registeredTags[name]) {\n\t\t\tthis.#registeredTags[name] = true\n\t\t\tthis.#baseOpenapiDoc.tags!.push({ name, 'x-displayName': displayName, description })\n\n\t\t\tconst tagGroups = parsed.slice(0, -1)\n\t\t\tconst groupName = tagGroups.map((g) => g.name).join(' > ') || 'default'\n\t\t\tif (!this.#registeredTagGroups[groupName]) {\n\t\t\t\tconst group = { name: groupName, tags: [] }\n\t\t\t\tthis.#baseOpenapiDoc['x-tagGroups'].push(group)\n\t\t\t\tthis.#registeredTagGroups[groupName] = group\n\t\t\t}\n\t\t\tthis.#registeredTagGroups[groupName].tags = [...new Set([...this.#registeredTagGroups[groupName].tags, name])]\n\t\t}\n\n\t\treturn name\n\t}\n\n\t#html(jsonPath: string) {\n\t\tconst title = capitalize(`${this.config.app.name} ${this.config.app.id}`)\n\t\treturn `\n<!doctype html>\n<html>\n <head>\n <title>${title}</title>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n\t<style>\n .darklight-reference {\n display: none;\n }\n </style>\n </head>\n <body>\n <script id=\"api-reference\" data-url=\"${jsonPath}\"></script>\n <script>\n const configuration = { theme: 'purple' };\n document.getElementById('api-reference').dataset.configuration = JSON.stringify(configuration);\n </script>\n <script src=\"https://cdn.jsdelivr.net/npm/@scalar/api-reference@1.28.33\"></script>\n </body>\n</html>\n`\n\t}\n}\n"],"mappings":"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","names":["convert","capitalize","Router","OpenApi","config","#baseOpenapiDoc","url","#registeredTags","#registeredTagGroups","path","cleaned","route","def","tag","#buildTag","cleanPath","operationId","#addRouteToOpenApiDoc","method","methodObj","resp","res","#visit","parameters","addParams","location","schema","flat","#flattenForParameters","name","value","base","jsonPath","router","req","#html","node","allOf","oneOf","anyOf","n","id","rest","key","groups","parsed","g","displayName","description","groupName","group"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/server/openapi.ts"],"sourcesContent":["import { convert } from '@openapi-contrib/json-schema-to-openapi-schema'\nimport { OpenAPIV3_1 } from 'openapi-types'\nimport { capitalize, JsonSchema } from 'valleyed'\n\nimport { ServerConfig } from './pipes'\nimport { Router } from './routes'\nimport { Route } from './types'\n\ndeclare module 'openapi-types' {\n\tnamespace OpenAPIV3 {\n\t\tinterface Document {\n\t\t\t'x-tagGroups': { name: string; tags: string[] }[]\n\t\t}\n\t\tinterface TagObject {\n\t\t\t'x-displayName': string\n\t\t}\n\t}\n}\n\nexport type OpenApiSchemaDef = {\n\trequest: Partial<Record<'body' | 'query' | 'params' | 'headers' | 'response', JsonSchema>>\n\tresponse: Partial<Record<'response' | 'responseHeaders', { status: number; schema: JsonSchema; contentType: string }[]>>\n}\n\nexport class OpenApi {\n\t#registeredTags: Record<string, boolean> = {}\n\t#registeredTagGroups: Record<string, { name: string; tags: string[] }> = {}\n\t#baseOpenapiDoc: OpenAPIV3_1.Document\n\n\tconstructor(private config: ServerConfig) {\n\t\tthis.#baseOpenapiDoc = {\n\t\t\topenapi: '3.0.0',\n\t\t\tinfo: {\n\t\t\t\ttitle: `${config.app.name} ${config.app.id}`,\n\t\t\t\tversion: config.config.openapi.docsVersion ?? '',\n\t\t\t},\n\t\t\tservers: config.config.openapi.docsBaseUrl?.map((url) => ({ url })),\n\t\t\tpaths: {},\n\t\t\tcomponents: {\n\t\t\t\tschemas: {},\n\t\t\t\tsecuritySchemes: {\n\t\t\t\t\tAuthorization: {\n\t\t\t\t\t\ttype: 'apiKey',\n\t\t\t\t\t\tname: 'authorization',\n\t\t\t\t\t\tin: 'header',\n\t\t\t\t\t},\n\t\t\t\t\tRefreshToken: {\n\t\t\t\t\t\ttype: 'apiKey',\n\t\t\t\t\t\tname: 'x-refresh-token',\n\t\t\t\t\t\tin: 'header',\n\t\t\t\t\t},\n\t\t\t\t\tApiKey: {\n\t\t\t\t\t\ttype: 'apiKey',\n\t\t\t\t\t\tname: 'x-api-key',\n\t\t\t\t\t\tin: 'header',\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\ttags: [],\n\t\t\t'x-tagGroups': [],\n\t\t}\n\t}\n\n\tcleanPath(path: string) {\n\t\tlet cleaned = path.replace(/(\\/\\s*)+/g, '/')\n\t\tif (!cleaned.startsWith('/')) cleaned = `/${cleaned}`\n\t\tif (cleaned !== '/' && cleaned.endsWith('/')) cleaned = cleaned.slice(0, -1)\n\t\treturn cleaned\n\t}\n\n\tasync register(route: Route<any>, def: OpenApiSchemaDef) {\n\t\tif (route.hide) return\n\n\t\tconst tag = this.#buildTag(route.groups ?? [])\n\n\t\tconst cleanPath = this.cleanPath(route.path)\n\t\tconst operationId = `(${route.method.toUpperCase()}) ${cleanPath}`\n\t\tawait this.#addRouteToOpenApiDoc(cleanPath, route.method.toLowerCase(), def, {\n\t\t\toperationId,\n\t\t\tsummary: route.title ?? cleanPath,\n\t\t\tdescription: route.descriptions?.join('\\n\\n'),\n\t\t\ttags: tag ? [tag] : undefined,\n\t\t\tsecurity: route.security,\n\t\t})\n\t}\n\n\tasync #addRouteToOpenApiDoc(path: string, method: string, def: OpenApiSchemaDef, methodObj: OpenAPIV3_1.OperationObject) {\n\t\tif (def.response.response?.length) {\n\t\t\tmethodObj.responses ??= {}\n\t\t\tfor (const resp of def.response.response) {\n\t\t\t\tmethodObj.responses[resp.status] ??= { description: '', content: {} }\n\t\t\t\tconst res = methodObj.responses[resp.status] as OpenAPIV3_1.ResponseObject\n\t\t\t\tres.content![resp.contentType] = { schema: await convert(this.#visit(resp.schema)) }\n\t\t\t}\n\t\t}\n\n\t\tif (def.response.responseHeaders?.length) {\n\t\t\tmethodObj.responses ??= {}\n\t\t\tfor (const resp of def.response.responseHeaders) {\n\t\t\t\tmethodObj.responses[resp.status] ??= { description: '', content: {} }\n\t\t\t\tmethodObj.responses[resp.status] as OpenAPIV3_1.ResponseObject\n\t\t\t\tconst res = methodObj.responses[resp.status] as OpenAPIV3_1.ResponseObject\n\t\t\t\tres.headers = { schema: (await convert(this.#visit(resp.schema))) as any }\n\t\t\t}\n\t\t}\n\n\t\tif (def.request.body)\n\t\t\tmethodObj.requestBody = {\n\t\t\t\trequired: true,\n\t\t\t\tcontent: {\n\t\t\t\t\t'application/json': { schema: await convert(this.#visit(def.request.body)) },\n\t\t\t\t},\n\t\t\t}\n\n\t\tconst parameters: OpenAPIV3_1.ParameterObject[] = []\n\n\t\tconst addParams = async (location: 'query' | 'path' | 'header', schema: JsonSchema | undefined) => {\n\t\t\tif (!schema) return\n\t\t\tconst flat = this.#flattenForParameters(schema)\n\t\t\tfor (const schema of flat) {\n\t\t\t\tif (!schema.properties) continue\n\t\t\t\tfor (const [name, value] of Object.entries(schema.properties))\n\t\t\t\t\tparameters.push({\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tin: location,\n\t\t\t\t\t\tschema: await convert(this.#visit(value)),\n\t\t\t\t\t\trequired: (schema.required || []).includes(name),\n\t\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\tawait Promise.all([\n\t\t\taddParams('query', def.request.query),\n\t\t\taddParams('path', def.request.params),\n\t\t\taddParams('header', def.request.headers),\n\t\t])\n\t\tif (parameters.length) methodObj.parameters = parameters\n\n\t\tconst base = this.#baseOpenapiDoc\n\t\tif (!base.paths) base.paths = {}\n\t\tif (!base.paths[path]) base.paths[path] = {}\n\t\tbase.paths[path]![method] = methodObj\n\t}\n\n\trouter() {\n\t\tconst jsonPath = '/openapi.json'\n\t\tconst router = new Router({ path: this.config.config.openapi.docsPath ?? '/', hide: true })\n\t\trouter.get('/index.html')((req) => req.res({ body: this.#html(`.${jsonPath}`), contentType: 'text/html' }))\n\t\trouter.get(jsonPath)((req) => req.res({ body: this.#baseOpenapiDoc }))\n\t\treturn router\n\t}\n\n\t#flattenForParameters(node: JsonSchema): JsonSchema[] {\n\t\tconst { allOf, oneOf, anyOf, ...schema } = node\n\t\tif (allOf) return allOf.flatMap((n) => this.#flattenForParameters(n))\n\t\treturn [schema]\n\t}\n\n\t#visit(node: JsonSchema) {\n\t\tif (!node || typeof node !== 'object') return node\n\t\tif (typeof node.$refId === 'string') {\n\t\t\tconst { $refId: id, ...rest } = node\n\t\t\tconst res = this.#visit(rest)\n\t\t\tif (this.#baseOpenapiDoc.components?.schemas) {\n\t\t\t\tthis.#baseOpenapiDoc.components.schemas[id] = res\n\t\t\t\treturn { $ref: `#/components/schemas/${id}` }\n\t\t\t} else return res\n\t\t}\n\n\t\tif (Array.isArray(node)) return node.map((n) => this.#visit(n)) as any\n\t\treturn Object.fromEntries(Object.entries(node).map(([key, value]) => [key, this.#visit(value as any)]))\n\t}\n\n\t#buildTag(groups: NonNullable<Route<any>['groups']>) {\n\t\tif (!groups.length) return undefined\n\t\tconst parsed = groups.map((g) => (typeof g === 'string' ? { name: g } : g))\n\t\tconst name = parsed.map((g) => g.name).join(' > ')\n\t\tconst displayName = parsed.at(-1)?.name ?? ''\n\t\tconst description = parsed\n\t\t\t.map((g) => g.description?.trim() ?? '')\n\t\t\t.filter(Boolean)\n\t\t\t.join('\\n\\n\\n\\n')\n\n\t\tif (!this.#registeredTags[name]) {\n\t\t\tthis.#registeredTags[name] = true\n\t\t\tthis.#baseOpenapiDoc.tags!.push({ name, 'x-displayName': displayName, description })\n\n\t\t\tconst tagGroups = parsed.slice(0, -1)\n\t\t\tconst groupName = tagGroups.map((g) => g.name).join(' > ') || 'default'\n\t\t\tif (!this.#registeredTagGroups[groupName]) {\n\t\t\t\tconst group = { name: groupName, tags: [] }\n\t\t\t\tthis.#baseOpenapiDoc['x-tagGroups'].push(group)\n\t\t\t\tthis.#registeredTagGroups[groupName] = group\n\t\t\t}\n\t\t\tthis.#registeredTagGroups[groupName].tags = [...new Set([...this.#registeredTagGroups[groupName].tags, name])]\n\t\t}\n\n\t\treturn name\n\t}\n\n\t#html(jsonPath: string) {\n\t\tconst title = capitalize(`${this.config.app.name} ${this.config.app.id}`)\n\t\treturn `\n<!doctype html>\n<html>\n <head>\n <title>${title}</title>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n\t<style>\n .darklight-reference {\n display: none;\n }\n </style>\n </head>\n <body>\n <script id=\"api-reference\" data-url=\"${jsonPath}\"></script>\n <script>\n const configuration = { theme: 'purple' };\n document.getElementById('api-reference').dataset.configuration = JSON.stringify(configuration);\n </script>\n <script src=\"https://cdn.jsdelivr.net/npm/@scalar/api-reference@1.28.33\"></script>\n </body>\n</html>\n`\n\t}\n}\n"],"mappings":"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","names":["convert","capitalize","Router","OpenApi","config","#baseOpenapiDoc","url","#registeredTags","#registeredTagGroups","path","cleaned","route","def","tag","#buildTag","cleanPath","operationId","#addRouteToOpenApiDoc","method","methodObj","resp","res","#visit","parameters","addParams","location","schema","flat","#flattenForParameters","name","value","base","jsonPath","router","req","#html","node","allOf","oneOf","anyOf","n","id","rest","key","groups","parsed","g","displayName","description","groupName","group"]}
|
|
@@ -112,7 +112,7 @@ class OpenApi {
|
|
|
112
112
|
router() {
|
|
113
113
|
const jsonPath = "/openapi.json";
|
|
114
114
|
const router = new Router({ path: this.config.config.openapi.docsPath ?? "/", hide: true });
|
|
115
|
-
router.get("/")((req) => req.res({ body: this.#html(`.${jsonPath}`), contentType: "text/html" }));
|
|
115
|
+
router.get("/index.html")((req) => req.res({ body: this.#html(`.${jsonPath}`), contentType: "text/html" }));
|
|
116
116
|
router.get(jsonPath)((req) => req.res({ body: this.#baseOpenapiDoc }));
|
|
117
117
|
return router;
|
|
118
118
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/server/openapi.ts"],"sourcesContent":["import { convert } from '@openapi-contrib/json-schema-to-openapi-schema'\nimport { OpenAPIV3_1 } from 'openapi-types'\nimport { capitalize, JsonSchema } from 'valleyed'\n\nimport { ServerConfig } from './pipes'\nimport { Router } from './routes'\nimport { Route } from './types'\n\ndeclare module 'openapi-types' {\n\tnamespace OpenAPIV3 {\n\t\tinterface Document {\n\t\t\t'x-tagGroups': { name: string; tags: string[] }[]\n\t\t}\n\t\tinterface TagObject {\n\t\t\t'x-displayName': string\n\t\t}\n\t}\n}\n\nexport type OpenApiSchemaDef = {\n\trequest: Partial<Record<'body' | 'query' | 'params' | 'headers' | 'response', JsonSchema>>\n\tresponse: Partial<Record<'response' | 'responseHeaders', { status: number; schema: JsonSchema; contentType: string }[]>>\n}\n\nexport class OpenApi {\n\t#registeredTags: Record<string, boolean> = {}\n\t#registeredTagGroups: Record<string, { name: string; tags: string[] }> = {}\n\t#baseOpenapiDoc: OpenAPIV3_1.Document\n\n\tconstructor(private config: ServerConfig) {\n\t\tthis.#baseOpenapiDoc = {\n\t\t\topenapi: '3.0.0',\n\t\t\tinfo: {\n\t\t\t\ttitle: `${config.app.name} ${config.app.id}`,\n\t\t\t\tversion: config.config.openapi.docsVersion ?? '',\n\t\t\t},\n\t\t\tservers: config.config.openapi.docsBaseUrl?.map((url) => ({ url })),\n\t\t\tpaths: {},\n\t\t\tcomponents: {\n\t\t\t\tschemas: {},\n\t\t\t\tsecuritySchemes: {\n\t\t\t\t\tAuthorization: {\n\t\t\t\t\t\ttype: 'apiKey',\n\t\t\t\t\t\tname: 'authorization',\n\t\t\t\t\t\tin: 'header',\n\t\t\t\t\t},\n\t\t\t\t\tRefreshToken: {\n\t\t\t\t\t\ttype: 'apiKey',\n\t\t\t\t\t\tname: 'x-refresh-token',\n\t\t\t\t\t\tin: 'header',\n\t\t\t\t\t},\n\t\t\t\t\tApiKey: {\n\t\t\t\t\t\ttype: 'apiKey',\n\t\t\t\t\t\tname: 'x-api-key',\n\t\t\t\t\t\tin: 'header',\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\ttags: [],\n\t\t\t'x-tagGroups': [],\n\t\t}\n\t}\n\n\tcleanPath(path: string) {\n\t\tlet cleaned = path.replace(/(\\/\\s*)+/g, '/')\n\t\tif (!cleaned.startsWith('/')) cleaned = `/${cleaned}`\n\t\tif (cleaned !== '/' && cleaned.endsWith('/')) cleaned = cleaned.slice(0, -1)\n\t\treturn cleaned\n\t}\n\n\tasync register(route: Route<any>, def: OpenApiSchemaDef) {\n\t\tif (route.hide) return\n\n\t\tconst tag = this.#buildTag(route.groups ?? [])\n\n\t\tconst cleanPath = this.cleanPath(route.path)\n\t\tconst operationId = `(${route.method.toUpperCase()}) ${cleanPath}`\n\t\tawait this.#addRouteToOpenApiDoc(cleanPath, route.method.toLowerCase(), def, {\n\t\t\toperationId,\n\t\t\tsummary: route.title ?? cleanPath,\n\t\t\tdescription: route.descriptions?.join('\\n\\n'),\n\t\t\ttags: tag ? [tag] : undefined,\n\t\t\tsecurity: route.security,\n\t\t})\n\t}\n\n\tasync #addRouteToOpenApiDoc(path: string, method: string, def: OpenApiSchemaDef, methodObj: OpenAPIV3_1.OperationObject) {\n\t\tif (def.response.response?.length) {\n\t\t\tmethodObj.responses ??= {}\n\t\t\tfor (const resp of def.response.response) {\n\t\t\t\tmethodObj.responses[resp.status] ??= { description: '', content: {} }\n\t\t\t\tconst res = methodObj.responses[resp.status] as OpenAPIV3_1.ResponseObject\n\t\t\t\tres.content![resp.contentType] = { schema: await convert(this.#visit(resp.schema)) }\n\t\t\t}\n\t\t}\n\n\t\tif (def.response.responseHeaders?.length) {\n\t\t\tmethodObj.responses ??= {}\n\t\t\tfor (const resp of def.response.responseHeaders) {\n\t\t\t\tmethodObj.responses[resp.status] ??= { description: '', content: {} }\n\t\t\t\tmethodObj.responses[resp.status] as OpenAPIV3_1.ResponseObject\n\t\t\t\tconst res = methodObj.responses[resp.status] as OpenAPIV3_1.ResponseObject\n\t\t\t\tres.headers = { schema: (await convert(this.#visit(resp.schema))) as any }\n\t\t\t}\n\t\t}\n\n\t\tif (def.request.body)\n\t\t\tmethodObj.requestBody = {\n\t\t\t\trequired: true,\n\t\t\t\tcontent: {\n\t\t\t\t\t'application/json': { schema: await convert(this.#visit(def.request.body)) },\n\t\t\t\t},\n\t\t\t}\n\n\t\tconst parameters: OpenAPIV3_1.ParameterObject[] = []\n\n\t\tconst addParams = async (location: 'query' | 'path' | 'header', schema: JsonSchema | undefined) => {\n\t\t\tif (!schema) return\n\t\t\tconst flat = this.#flattenForParameters(schema)\n\t\t\tfor (const schema of flat) {\n\t\t\t\tif (!schema.properties) continue\n\t\t\t\tfor (const [name, value] of Object.entries(schema.properties))\n\t\t\t\t\tparameters.push({\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tin: location,\n\t\t\t\t\t\tschema: await convert(this.#visit(value)),\n\t\t\t\t\t\trequired: (schema.required || []).includes(name),\n\t\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\tawait Promise.all([\n\t\t\taddParams('query', def.request.query),\n\t\t\taddParams('path', def.request.params),\n\t\t\taddParams('header', def.request.headers),\n\t\t])\n\t\tif (parameters.length) methodObj.parameters = parameters\n\n\t\tconst base = this.#baseOpenapiDoc\n\t\tif (!base.paths) base.paths = {}\n\t\tif (!base.paths[path]) base.paths[path] = {}\n\t\tbase.paths[path]![method] = methodObj\n\t}\n\n\trouter() {\n\t\tconst jsonPath = '/openapi.json'\n\t\tconst router = new Router({ path: this.config.config.openapi.docsPath ?? '/', hide: true })\n\t\trouter.get('/')((req) => req.res({ body: this.#html(`.${jsonPath}`), contentType: 'text/html' }))\n\t\trouter.get(jsonPath)((req) => req.res({ body: this.#baseOpenapiDoc }))\n\t\treturn router\n\t}\n\n\t#flattenForParameters(node: JsonSchema): JsonSchema[] {\n\t\tconst { allOf, oneOf, anyOf, ...schema } = node\n\t\tif (allOf) return allOf.flatMap((n) => this.#flattenForParameters(n))\n\t\treturn [schema]\n\t}\n\n\t#visit(node: JsonSchema) {\n\t\tif (!node || typeof node !== 'object') return node\n\t\tif (typeof node.$refId === 'string') {\n\t\t\tconst { $refId: id, ...rest } = node\n\t\t\tconst res = this.#visit(rest)\n\t\t\tif (this.#baseOpenapiDoc.components?.schemas) {\n\t\t\t\tthis.#baseOpenapiDoc.components.schemas[id] = res\n\t\t\t\treturn { $ref: `#/components/schemas/${id}` }\n\t\t\t} else return res\n\t\t}\n\n\t\tif (Array.isArray(node)) return node.map((n) => this.#visit(n)) as any\n\t\treturn Object.fromEntries(Object.entries(node).map(([key, value]) => [key, this.#visit(value as any)]))\n\t}\n\n\t#buildTag(groups: NonNullable<Route<any>['groups']>) {\n\t\tif (!groups.length) return undefined\n\t\tconst parsed = groups.map((g) => (typeof g === 'string' ? { name: g } : g))\n\t\tconst name = parsed.map((g) => g.name).join(' > ')\n\t\tconst displayName = parsed.at(-1)?.name ?? ''\n\t\tconst description = parsed\n\t\t\t.map((g) => g.description?.trim() ?? '')\n\t\t\t.filter(Boolean)\n\t\t\t.join('\\n\\n\\n\\n')\n\n\t\tif (!this.#registeredTags[name]) {\n\t\t\tthis.#registeredTags[name] = true\n\t\t\tthis.#baseOpenapiDoc.tags!.push({ name, 'x-displayName': displayName, description })\n\n\t\t\tconst tagGroups = parsed.slice(0, -1)\n\t\t\tconst groupName = tagGroups.map((g) => g.name).join(' > ') || 'default'\n\t\t\tif (!this.#registeredTagGroups[groupName]) {\n\t\t\t\tconst group = { name: groupName, tags: [] }\n\t\t\t\tthis.#baseOpenapiDoc['x-tagGroups'].push(group)\n\t\t\t\tthis.#registeredTagGroups[groupName] = group\n\t\t\t}\n\t\t\tthis.#registeredTagGroups[groupName].tags = [...new Set([...this.#registeredTagGroups[groupName].tags, name])]\n\t\t}\n\n\t\treturn name\n\t}\n\n\t#html(jsonPath: string) {\n\t\tconst title = capitalize(`${this.config.app.name} ${this.config.app.id}`)\n\t\treturn `\n<!doctype html>\n<html>\n <head>\n <title>${title}</title>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n\t<style>\n .darklight-reference {\n display: none;\n }\n </style>\n </head>\n <body>\n <script id=\"api-reference\" data-url=\"${jsonPath}\"></script>\n <script>\n const configuration = { theme: 'purple' };\n document.getElementById('api-reference').dataset.configuration = JSON.stringify(configuration);\n </script>\n <script src=\"https://cdn.jsdelivr.net/npm/@scalar/api-reference@1.28.33\"></script>\n </body>\n</html>\n`\n\t}\n}\n"],"mappings":"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","names":["schema"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/server/openapi.ts"],"sourcesContent":["import { convert } from '@openapi-contrib/json-schema-to-openapi-schema'\nimport { OpenAPIV3_1 } from 'openapi-types'\nimport { capitalize, JsonSchema } from 'valleyed'\n\nimport { ServerConfig } from './pipes'\nimport { Router } from './routes'\nimport { Route } from './types'\n\ndeclare module 'openapi-types' {\n\tnamespace OpenAPIV3 {\n\t\tinterface Document {\n\t\t\t'x-tagGroups': { name: string; tags: string[] }[]\n\t\t}\n\t\tinterface TagObject {\n\t\t\t'x-displayName': string\n\t\t}\n\t}\n}\n\nexport type OpenApiSchemaDef = {\n\trequest: Partial<Record<'body' | 'query' | 'params' | 'headers' | 'response', JsonSchema>>\n\tresponse: Partial<Record<'response' | 'responseHeaders', { status: number; schema: JsonSchema; contentType: string }[]>>\n}\n\nexport class OpenApi {\n\t#registeredTags: Record<string, boolean> = {}\n\t#registeredTagGroups: Record<string, { name: string; tags: string[] }> = {}\n\t#baseOpenapiDoc: OpenAPIV3_1.Document\n\n\tconstructor(private config: ServerConfig) {\n\t\tthis.#baseOpenapiDoc = {\n\t\t\topenapi: '3.0.0',\n\t\t\tinfo: {\n\t\t\t\ttitle: `${config.app.name} ${config.app.id}`,\n\t\t\t\tversion: config.config.openapi.docsVersion ?? '',\n\t\t\t},\n\t\t\tservers: config.config.openapi.docsBaseUrl?.map((url) => ({ url })),\n\t\t\tpaths: {},\n\t\t\tcomponents: {\n\t\t\t\tschemas: {},\n\t\t\t\tsecuritySchemes: {\n\t\t\t\t\tAuthorization: {\n\t\t\t\t\t\ttype: 'apiKey',\n\t\t\t\t\t\tname: 'authorization',\n\t\t\t\t\t\tin: 'header',\n\t\t\t\t\t},\n\t\t\t\t\tRefreshToken: {\n\t\t\t\t\t\ttype: 'apiKey',\n\t\t\t\t\t\tname: 'x-refresh-token',\n\t\t\t\t\t\tin: 'header',\n\t\t\t\t\t},\n\t\t\t\t\tApiKey: {\n\t\t\t\t\t\ttype: 'apiKey',\n\t\t\t\t\t\tname: 'x-api-key',\n\t\t\t\t\t\tin: 'header',\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\ttags: [],\n\t\t\t'x-tagGroups': [],\n\t\t}\n\t}\n\n\tcleanPath(path: string) {\n\t\tlet cleaned = path.replace(/(\\/\\s*)+/g, '/')\n\t\tif (!cleaned.startsWith('/')) cleaned = `/${cleaned}`\n\t\tif (cleaned !== '/' && cleaned.endsWith('/')) cleaned = cleaned.slice(0, -1)\n\t\treturn cleaned\n\t}\n\n\tasync register(route: Route<any>, def: OpenApiSchemaDef) {\n\t\tif (route.hide) return\n\n\t\tconst tag = this.#buildTag(route.groups ?? [])\n\n\t\tconst cleanPath = this.cleanPath(route.path)\n\t\tconst operationId = `(${route.method.toUpperCase()}) ${cleanPath}`\n\t\tawait this.#addRouteToOpenApiDoc(cleanPath, route.method.toLowerCase(), def, {\n\t\t\toperationId,\n\t\t\tsummary: route.title ?? cleanPath,\n\t\t\tdescription: route.descriptions?.join('\\n\\n'),\n\t\t\ttags: tag ? [tag] : undefined,\n\t\t\tsecurity: route.security,\n\t\t})\n\t}\n\n\tasync #addRouteToOpenApiDoc(path: string, method: string, def: OpenApiSchemaDef, methodObj: OpenAPIV3_1.OperationObject) {\n\t\tif (def.response.response?.length) {\n\t\t\tmethodObj.responses ??= {}\n\t\t\tfor (const resp of def.response.response) {\n\t\t\t\tmethodObj.responses[resp.status] ??= { description: '', content: {} }\n\t\t\t\tconst res = methodObj.responses[resp.status] as OpenAPIV3_1.ResponseObject\n\t\t\t\tres.content![resp.contentType] = { schema: await convert(this.#visit(resp.schema)) }\n\t\t\t}\n\t\t}\n\n\t\tif (def.response.responseHeaders?.length) {\n\t\t\tmethodObj.responses ??= {}\n\t\t\tfor (const resp of def.response.responseHeaders) {\n\t\t\t\tmethodObj.responses[resp.status] ??= { description: '', content: {} }\n\t\t\t\tmethodObj.responses[resp.status] as OpenAPIV3_1.ResponseObject\n\t\t\t\tconst res = methodObj.responses[resp.status] as OpenAPIV3_1.ResponseObject\n\t\t\t\tres.headers = { schema: (await convert(this.#visit(resp.schema))) as any }\n\t\t\t}\n\t\t}\n\n\t\tif (def.request.body)\n\t\t\tmethodObj.requestBody = {\n\t\t\t\trequired: true,\n\t\t\t\tcontent: {\n\t\t\t\t\t'application/json': { schema: await convert(this.#visit(def.request.body)) },\n\t\t\t\t},\n\t\t\t}\n\n\t\tconst parameters: OpenAPIV3_1.ParameterObject[] = []\n\n\t\tconst addParams = async (location: 'query' | 'path' | 'header', schema: JsonSchema | undefined) => {\n\t\t\tif (!schema) return\n\t\t\tconst flat = this.#flattenForParameters(schema)\n\t\t\tfor (const schema of flat) {\n\t\t\t\tif (!schema.properties) continue\n\t\t\t\tfor (const [name, value] of Object.entries(schema.properties))\n\t\t\t\t\tparameters.push({\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tin: location,\n\t\t\t\t\t\tschema: await convert(this.#visit(value)),\n\t\t\t\t\t\trequired: (schema.required || []).includes(name),\n\t\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\tawait Promise.all([\n\t\t\taddParams('query', def.request.query),\n\t\t\taddParams('path', def.request.params),\n\t\t\taddParams('header', def.request.headers),\n\t\t])\n\t\tif (parameters.length) methodObj.parameters = parameters\n\n\t\tconst base = this.#baseOpenapiDoc\n\t\tif (!base.paths) base.paths = {}\n\t\tif (!base.paths[path]) base.paths[path] = {}\n\t\tbase.paths[path]![method] = methodObj\n\t}\n\n\trouter() {\n\t\tconst jsonPath = '/openapi.json'\n\t\tconst router = new Router({ path: this.config.config.openapi.docsPath ?? '/', hide: true })\n\t\trouter.get('/index.html')((req) => req.res({ body: this.#html(`.${jsonPath}`), contentType: 'text/html' }))\n\t\trouter.get(jsonPath)((req) => req.res({ body: this.#baseOpenapiDoc }))\n\t\treturn router\n\t}\n\n\t#flattenForParameters(node: JsonSchema): JsonSchema[] {\n\t\tconst { allOf, oneOf, anyOf, ...schema } = node\n\t\tif (allOf) return allOf.flatMap((n) => this.#flattenForParameters(n))\n\t\treturn [schema]\n\t}\n\n\t#visit(node: JsonSchema) {\n\t\tif (!node || typeof node !== 'object') return node\n\t\tif (typeof node.$refId === 'string') {\n\t\t\tconst { $refId: id, ...rest } = node\n\t\t\tconst res = this.#visit(rest)\n\t\t\tif (this.#baseOpenapiDoc.components?.schemas) {\n\t\t\t\tthis.#baseOpenapiDoc.components.schemas[id] = res\n\t\t\t\treturn { $ref: `#/components/schemas/${id}` }\n\t\t\t} else return res\n\t\t}\n\n\t\tif (Array.isArray(node)) return node.map((n) => this.#visit(n)) as any\n\t\treturn Object.fromEntries(Object.entries(node).map(([key, value]) => [key, this.#visit(value as any)]))\n\t}\n\n\t#buildTag(groups: NonNullable<Route<any>['groups']>) {\n\t\tif (!groups.length) return undefined\n\t\tconst parsed = groups.map((g) => (typeof g === 'string' ? { name: g } : g))\n\t\tconst name = parsed.map((g) => g.name).join(' > ')\n\t\tconst displayName = parsed.at(-1)?.name ?? ''\n\t\tconst description = parsed\n\t\t\t.map((g) => g.description?.trim() ?? '')\n\t\t\t.filter(Boolean)\n\t\t\t.join('\\n\\n\\n\\n')\n\n\t\tif (!this.#registeredTags[name]) {\n\t\t\tthis.#registeredTags[name] = true\n\t\t\tthis.#baseOpenapiDoc.tags!.push({ name, 'x-displayName': displayName, description })\n\n\t\t\tconst tagGroups = parsed.slice(0, -1)\n\t\t\tconst groupName = tagGroups.map((g) => g.name).join(' > ') || 'default'\n\t\t\tif (!this.#registeredTagGroups[groupName]) {\n\t\t\t\tconst group = { name: groupName, tags: [] }\n\t\t\t\tthis.#baseOpenapiDoc['x-tagGroups'].push(group)\n\t\t\t\tthis.#registeredTagGroups[groupName] = group\n\t\t\t}\n\t\t\tthis.#registeredTagGroups[groupName].tags = [...new Set([...this.#registeredTagGroups[groupName].tags, name])]\n\t\t}\n\n\t\treturn name\n\t}\n\n\t#html(jsonPath: string) {\n\t\tconst title = capitalize(`${this.config.app.name} ${this.config.app.id}`)\n\t\treturn `\n<!doctype html>\n<html>\n <head>\n <title>${title}</title>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n\t<style>\n .darklight-reference {\n display: none;\n }\n </style>\n </head>\n <body>\n <script id=\"api-reference\" data-url=\"${jsonPath}\"></script>\n <script>\n const configuration = { theme: 'purple' };\n document.getElementById('api-reference').dataset.configuration = JSON.stringify(configuration);\n </script>\n <script src=\"https://cdn.jsdelivr.net/npm/@scalar/api-reference@1.28.33\"></script>\n </body>\n</html>\n`\n\t}\n}\n"],"mappings":"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","names":["schema"]}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
declare abstract class Cache {
|
|
2
|
-
abstract set(key: string, data: string, ttlInSecs
|
|
2
|
+
abstract set(key: string, data: string, ttlInSecs?: number): Promise<void>;
|
|
3
3
|
abstract get(key: string): Promise<string | null>;
|
|
4
4
|
abstract delete(key: string): Promise<void>;
|
|
5
|
-
abstract getOrSet<T>(key: string, fn: () => Promise<T>, ttlInSecs
|
|
5
|
+
abstract getOrSet<T>(key: string, fn: () => Promise<T>, ttlInSecs?: number): Promise<T>;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
export { Cache as C };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as Cache } from '../base-
|
|
1
|
+
import { C as Cache } from '../base-8yVXb67P.js';
|
|
2
2
|
import * as valleyed from 'valleyed';
|
|
3
3
|
import { PipeOutput } from 'valleyed';
|
|
4
4
|
import { Redis, Cluster, RedisOptions } from 'ioredis';
|
|
@@ -26,8 +26,8 @@ declare class InMemoryCache extends Cache {
|
|
|
26
26
|
private getScopedKey;
|
|
27
27
|
delete(key: string): Promise<void>;
|
|
28
28
|
get(key: string): Promise<string | null>;
|
|
29
|
-
set(key: string, data: string, ttlInSecs
|
|
30
|
-
getOrSet<T>(key: string, fn: () => Promise<T>, ttlInSecs
|
|
29
|
+
set(key: string, data: string, ttlInSecs?: number): Promise<void>;
|
|
30
|
+
getOrSet<T>(key: string, fn: () => Promise<T>, ttlInSecs?: number): Promise<T>;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
declare class RedisCache extends Cache {
|
|
@@ -36,8 +36,8 @@ declare class RedisCache extends Cache {
|
|
|
36
36
|
private getScopedKey;
|
|
37
37
|
delete(key: string): Promise<void>;
|
|
38
38
|
get(key: string): Promise<string | null>;
|
|
39
|
-
set(key: string, data: string, ttlInSecs
|
|
40
|
-
getOrSet<T>(key: string, fn: () => Promise<T>, ttlInSecs
|
|
39
|
+
set(key: string, data: string, ttlInSecs?: number): Promise<void>;
|
|
40
|
+
getOrSet<T>(key: string, fn: () => Promise<T>, ttlInSecs?: number): Promise<any>;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
export { Cache, InMemoryCache, RedisCache, type RedisConfig, redisConfigPipe };
|
|
@@ -11,7 +11,7 @@ class InMemoryCache extends Cache {
|
|
|
11
11
|
interval = setInterval(() => {
|
|
12
12
|
const now = Date.now();
|
|
13
13
|
for (const [key, record] of this.cache.entries()) {
|
|
14
|
-
if (record.expiredAt <= now) this.cache.delete(key);
|
|
14
|
+
if (record.expiredAt && record.expiredAt <= now) this.cache.delete(key);
|
|
15
15
|
}
|
|
16
16
|
}, 5e3);
|
|
17
17
|
},
|
|
@@ -27,11 +27,11 @@ class InMemoryCache extends Cache {
|
|
|
27
27
|
}
|
|
28
28
|
async get(key) {
|
|
29
29
|
const record = this.cache.get(this.getScopedKey(key));
|
|
30
|
-
if (record && record.expiredAt > Date.now()) return record.data;
|
|
30
|
+
if (record && (record.expiredAt === void 0 || record.expiredAt > Date.now())) return record.data;
|
|
31
31
|
return null;
|
|
32
32
|
}
|
|
33
33
|
async set(key, data, ttlInSecs) {
|
|
34
|
-
this.cache.set(this.getScopedKey(key), { data, expiredAt: Date.now() + ttlInSecs * 1e3 });
|
|
34
|
+
this.cache.set(this.getScopedKey(key), { data, expiredAt: ttlInSecs ? Date.now() + ttlInSecs * 1e3 : void 0 });
|
|
35
35
|
}
|
|
36
36
|
async getOrSet(key, fn, ttlInSecs) {
|
|
37
37
|
const cached = await this.get(key);
|
|
@@ -38,7 +38,7 @@ class RedisCache extends Cache {
|
|
|
38
38
|
return await this.client.get(this.getScopedKey(key));
|
|
39
39
|
}
|
|
40
40
|
async set(key, data, ttlInSecs) {
|
|
41
|
-
if (ttlInSecs
|
|
41
|
+
if (ttlInSecs) await this.client.setex(this.getScopedKey(key), ttlInSecs, data);
|
|
42
42
|
else this.client.set(this.getScopedKey(key), data);
|
|
43
43
|
}
|
|
44
44
|
async getOrSet(key, fn, ttlInSecs) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { M as Model, E as Entity, D as DbChange, a as MongoDbConfig, b as DbChangeConfig, c as DbChangeCallbacks } from '../index-
|
|
2
|
-
export { B as BulkWriteOperation, k as Conditions, h as Config, C as CreateInput, e as Db, i as DbConfig, g as EntityInput, I as IdType, j as MongoDb, O as Options, Q as QueryKeys, m as QueryParams, n as QueryParamsInput, s as QueryResults, p as QueryWhere, r as QueryWhereBlock, o as QueryWhereClause, f as Table, d as TableOptions, T as TopicPrefix, U as UpdateInput, t as mongoDbConfigPipe, q as queryParamsPipe, l as queryResultsPipe, w as wrapQueryParams } from '../index-
|
|
1
|
+
import { M as Model, E as Entity, D as DbChange, a as MongoDbConfig, b as DbChangeConfig, c as DbChangeCallbacks } from '../index-CfuXekZB.js';
|
|
2
|
+
export { B as BulkWriteOperation, k as Conditions, h as Config, C as CreateInput, e as Db, i as DbConfig, g as EntityInput, I as IdType, j as MongoDb, O as Options, Q as QueryKeys, m as QueryParams, n as QueryParamsInput, s as QueryResults, p as QueryWhere, r as QueryWhereBlock, o as QueryWhereClause, f as Table, d as TableOptions, T as TopicPrefix, U as UpdateInput, t as mongoDbConfigPipe, q as queryParamsPipe, l as queryResultsPipe, w as wrapQueryParams } from '../index-CfuXekZB.js';
|
|
3
3
|
import { MongoClient } from 'mongodb';
|
|
4
4
|
export { Filter } from 'mongodb';
|
|
5
5
|
import 'valleyed';
|
|
@@ -21,14 +21,12 @@ function getTable(config, collection) {
|
|
|
21
21
|
...base
|
|
22
22
|
};
|
|
23
23
|
}
|
|
24
|
-
function prepUpdateValue(value, now) {
|
|
24
|
+
function prepUpdateValue(value, now, upsert = false) {
|
|
25
25
|
return {
|
|
26
26
|
...value,
|
|
27
27
|
$set: {
|
|
28
28
|
...value.$set,
|
|
29
|
-
...Object.keys(value).length > 0 && !config.options?.skipAudit ? {
|
|
30
|
-
updatedAt: now.getTime()
|
|
31
|
-
} : {}
|
|
29
|
+
...upsert || Object.keys(value).length > 0 && !config.options?.skipAudit ? { updatedAt: now.getTime() } : {}
|
|
32
30
|
}
|
|
33
31
|
};
|
|
34
32
|
}
|
|
@@ -93,7 +91,7 @@ function getTable(config, collection) {
|
|
|
93
91
|
const doc = await collection.findOneAndUpdate(
|
|
94
92
|
filter,
|
|
95
93
|
{
|
|
96
|
-
...prepUpdateValue("update" in values ? values.update : {}, now),
|
|
94
|
+
...prepUpdateValue("update" in values ? values.update : {}, now, true),
|
|
97
95
|
// @ts-expect-error fighting ts
|
|
98
96
|
$setOnInsert: prepInsertValue(values.insert, options.makeId?.() ?? new ObjectId().toString(), now, true)
|
|
99
97
|
},
|
|
@@ -130,7 +128,7 @@ function getTable(config, collection) {
|
|
|
130
128
|
break;
|
|
131
129
|
case "upsert":
|
|
132
130
|
bulk.find(operation.filter).upsert().update({
|
|
133
|
-
...prepUpdateValue("update" in operation ? operation.update : {}, now),
|
|
131
|
+
...prepUpdateValue("update" in operation ? operation.update : {}, now, true),
|
|
134
132
|
$setOnInsert: prepInsertValue(
|
|
135
133
|
operation.insert,
|
|
136
134
|
operation.makeId?.(i) ?? new ObjectId().toString(),
|
|
@@ -8,11 +8,11 @@ class MongoDb extends Db {
|
|
|
8
8
|
constructor(mongoConfig, dbConfig) {
|
|
9
9
|
super(dbConfig);
|
|
10
10
|
this.mongoConfig = mongoConfig;
|
|
11
|
-
this
|
|
11
|
+
this.client = new MongoClient(mongoConfig.uri);
|
|
12
12
|
Instance.on(
|
|
13
13
|
"start",
|
|
14
14
|
async () => {
|
|
15
|
-
await this
|
|
15
|
+
await this.client.connect();
|
|
16
16
|
const grouped = this.#cols.reduce((acc, cur) => {
|
|
17
17
|
if (!acc[cur.db]) acc[cur.db] = [];
|
|
18
18
|
acc[cur.db].push(cur.col);
|
|
@@ -23,7 +23,7 @@ class MongoDb extends Db {
|
|
|
23
23
|
};
|
|
24
24
|
await Promise.all(
|
|
25
25
|
Object.entries(grouped).map(async ([dbName, colNames]) => {
|
|
26
|
-
const db = this
|
|
26
|
+
const db = this.client.db(dbName);
|
|
27
27
|
const collections = await db.listCollections().toArray();
|
|
28
28
|
return colNames.map(async (colName) => {
|
|
29
29
|
const existing = collections.find((collection) => collection.name === colName);
|
|
@@ -37,12 +37,12 @@ class MongoDb extends Db {
|
|
|
37
37
|
},
|
|
38
38
|
3
|
|
39
39
|
);
|
|
40
|
-
Instance.on("close", async () => this
|
|
40
|
+
Instance.on("close", async () => this.client.close(), 1);
|
|
41
41
|
}
|
|
42
|
-
|
|
42
|
+
client;
|
|
43
43
|
#cols = [];
|
|
44
44
|
async session(callback) {
|
|
45
|
-
return this
|
|
45
|
+
return this.client.withSession(callback);
|
|
46
46
|
}
|
|
47
47
|
id() {
|
|
48
48
|
return new ObjectId();
|
|
@@ -53,7 +53,7 @@ class MongoDb extends Db {
|
|
|
53
53
|
new MongoDbChange(
|
|
54
54
|
this.mongoConfig,
|
|
55
55
|
this.config.changes,
|
|
56
|
-
this
|
|
56
|
+
this.client,
|
|
57
57
|
this.getScopedDb(config.db),
|
|
58
58
|
config.col,
|
|
59
59
|
config.change,
|
|
@@ -61,7 +61,7 @@ class MongoDb extends Db {
|
|
|
61
61
|
);
|
|
62
62
|
}
|
|
63
63
|
this.#cols.push({ db: this.getScopedDb(config.db), col: config.col });
|
|
64
|
-
const collection = this
|
|
64
|
+
const collection = this.client.db(this.getScopedDb(config.db)).collection(config.col);
|
|
65
65
|
return getTable(config, collection);
|
|
66
66
|
}
|
|
67
67
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as valleyed from 'valleyed';
|
|
2
2
|
import { PipeOutput, ConditionalObjectKeys, PipeInput, IsInTypeList, DataClass, Pipe } from 'valleyed';
|
|
3
3
|
import { Logger } from 'pino';
|
|
4
|
-
import { C as Cache } from './base-
|
|
4
|
+
import { C as Cache } from './base-8yVXb67P.js';
|
|
5
5
|
import { E as EventBus } from './kafka-DrqkU2KH.js';
|
|
6
|
-
import { E as Entity, j as MongoDb } from './index-
|
|
6
|
+
import { E as Entity, j as MongoDb } from './index-CfuXekZB.js';
|
|
7
7
|
import { RedisJob } from './jobs/index.js';
|
|
8
8
|
import * as supertest_lib_agent from 'supertest/lib/agent';
|
|
9
9
|
import http from 'http';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as mongodb from 'mongodb';
|
|
2
|
-
import { Filter, ClientSession, UpdateFilter, ObjectId } from 'mongodb';
|
|
2
|
+
import { Filter, ClientSession, UpdateFilter, MongoClient, ObjectId } from 'mongodb';
|
|
3
3
|
import { PipeOutput, Pipe, ConditionalObjectKeys, PipeInput, DeepPartial, DistributiveOmit } from 'valleyed';
|
|
4
4
|
import { a as KafkaEventBus } from './kafka-DrqkU2KH.js';
|
|
5
5
|
|
|
@@ -272,6 +272,7 @@ declare class MongoDb extends Db<{
|
|
|
272
272
|
}> {
|
|
273
273
|
#private;
|
|
274
274
|
private mongoConfig;
|
|
275
|
+
client: MongoClient;
|
|
275
276
|
constructor(mongoConfig: MongoDbConfig, dbConfig: DbConfig);
|
|
276
277
|
session<T>(callback: (session: ClientSession) => Promise<T>): Promise<T>;
|
|
277
278
|
id(): ObjectId;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
export { I as Instance } from './index-
|
|
1
|
+
export { I as Instance } from './index-BS53hVyq.js';
|
|
2
2
|
import 'valleyed';
|
|
3
3
|
import 'pino';
|
|
4
|
-
import './base-
|
|
4
|
+
import './base-8yVXb67P.js';
|
|
5
5
|
import './kafka-DrqkU2KH.js';
|
|
6
6
|
import './overrides-6Hxg764S.js';
|
|
7
|
-
import './index-
|
|
7
|
+
import './index-CfuXekZB.js';
|
|
8
8
|
import 'mongodb';
|
|
9
9
|
import './jobs/index.js';
|
|
10
10
|
import 'supertest/lib/agent';
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import 'valleyed';
|
|
2
|
-
export { I as Instance } from '../index-
|
|
2
|
+
export { I as Instance } from '../index-BS53hVyq.js';
|
|
3
3
|
import '../requests-DBqR41Uw.js';
|
|
4
4
|
import 'pino';
|
|
5
|
-
import '../base-
|
|
5
|
+
import '../base-8yVXb67P.js';
|
|
6
6
|
import '../kafka-DrqkU2KH.js';
|
|
7
7
|
import '../overrides-6Hxg764S.js';
|
|
8
|
-
import '../index-
|
|
8
|
+
import '../index-CfuXekZB.js';
|
|
9
9
|
import 'mongodb';
|
|
10
10
|
import '../jobs/index.js';
|
|
11
11
|
import 'supertest/lib/agent';
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { S as Server, a as ServerConfig } from '../index-
|
|
2
|
-
export { B as BaseApiKeysUtility, b as BaseTokensUtility, C as CacheTokensUtility, O as OnJoinFn, R as Router, c as SocketCallbacks, d as SocketEmitter, s as serverPipe } from '../index-
|
|
1
|
+
import { S as Server, a as ServerConfig } from '../index-BS53hVyq.js';
|
|
2
|
+
export { B as BaseApiKeysUtility, b as BaseTokensUtility, C as CacheTokensUtility, O as OnJoinFn, R as Router, c as SocketCallbacks, d as SocketEmitter, s as serverPipe } from '../index-BS53hVyq.js';
|
|
3
3
|
import express from 'express';
|
|
4
4
|
import { FastifyRequest, FastifyReply } from 'fastify';
|
|
5
5
|
import { a as Request, c as RouteDefToReqRes, d as RouteDef, e as Route } from '../requests-DBqR41Uw.js';
|
|
6
6
|
export { D as DefaultHeaders, I as IncomingFile, j as MergeRouteDefs, M as Methods, f as MethodsEnum, b as Response, h as RouteConfig, k as RouteDefHandler, i as RouterConfig, S as StatusCodes, g as StatusCodesEnum, l as makeErrorMiddleware, m as makeMiddleware } from '../requests-DBqR41Uw.js';
|
|
7
7
|
import 'valleyed';
|
|
8
8
|
import 'pino';
|
|
9
|
-
import '../base-
|
|
9
|
+
import '../base-8yVXb67P.js';
|
|
10
10
|
import '../kafka-DrqkU2KH.js';
|
|
11
11
|
import '../overrides-6Hxg764S.js';
|
|
12
|
-
import '../index-
|
|
12
|
+
import '../index-CfuXekZB.js';
|
|
13
13
|
import 'mongodb';
|
|
14
14
|
import '../jobs/index.js';
|
|
15
15
|
import 'supertest/lib/agent';
|
|
@@ -112,7 +112,7 @@ class OpenApi {
|
|
|
112
112
|
router() {
|
|
113
113
|
const jsonPath = "/openapi.json";
|
|
114
114
|
const router = new Router({ path: this.config.config.openapi.docsPath ?? "/", hide: true });
|
|
115
|
-
router.get("/")((req) => req.res({ body: this.#html(`.${jsonPath}`), contentType: "text/html" }));
|
|
115
|
+
router.get("/index.html")((req) => req.res({ body: this.#html(`.${jsonPath}`), contentType: "text/html" }));
|
|
116
116
|
router.get(jsonPath)((req) => req.res({ body: this.#baseOpenapiDoc }));
|
|
117
117
|
return router;
|
|
118
118
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "equipped",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.15",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "",
|
|
6
6
|
"type": "module",
|
|
@@ -185,7 +185,7 @@
|
|
|
185
185
|
"redis": "5.5.6",
|
|
186
186
|
"socket.io": "4.8.1",
|
|
187
187
|
"supertest": "7.1.1",
|
|
188
|
-
"valleyed": "^4.5.
|
|
188
|
+
"valleyed": "^4.5.14"
|
|
189
189
|
},
|
|
190
190
|
"repository": {
|
|
191
191
|
"url": "git://github.com/kevinand11/equipped.git"
|