apitally 0.22.1 → 0.23.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/dist/common/client.cjs +3 -1
- package/dist/common/client.cjs.map +1 -1
- package/dist/common/client.js +3 -1
- package/dist/common/client.js.map +1 -1
- package/dist/common/resources.cjs +49 -0
- package/dist/common/resources.cjs.map +1 -0
- package/dist/common/resources.d.cts +6 -0
- package/dist/common/resources.d.ts +6 -0
- package/dist/common/resources.js +26 -0
- package/dist/common/resources.js.map +1 -0
- package/dist/common/types.cjs.map +1 -1
- package/dist/common/types.d.cts +4 -0
- package/dist/common/types.d.ts +4 -0
- package/dist/elysia/plugin.cjs +14 -11
- package/dist/elysia/plugin.cjs.map +1 -1
- package/dist/elysia/plugin.js +14 -11
- package/dist/elysia/plugin.js.map +1 -1
- package/package.json +3 -3
package/dist/common/client.cjs
CHANGED
|
@@ -41,6 +41,7 @@ var import_logging = require("./logging.js");
|
|
|
41
41
|
var import_paramValidation = require("./paramValidation.js");
|
|
42
42
|
var import_requestCounter = __toESM(require("./requestCounter.js"), 1);
|
|
43
43
|
var import_requestLogger = __toESM(require("./requestLogger.js"), 1);
|
|
44
|
+
var import_resources = require("./resources.js");
|
|
44
45
|
var import_serverErrorCounter = __toESM(require("./serverErrorCounter.js"), 1);
|
|
45
46
|
var import_validationErrorCounter = __toESM(require("./validationErrorCounter.js"), 1);
|
|
46
47
|
var _a;
|
|
@@ -224,7 +225,8 @@ const _ApitallyClient = class _ApitallyClient {
|
|
|
224
225
|
requests: this.requestCounter.getAndResetRequests(),
|
|
225
226
|
validation_errors: this.validationErrorCounter.getAndResetValidationErrors(),
|
|
226
227
|
server_errors: this.serverErrorCounter.getAndResetServerErrors(),
|
|
227
|
-
consumers: this.consumerRegistry.getAndResetUpdatedConsumers()
|
|
228
|
+
consumers: this.consumerRegistry.getAndResetUpdatedConsumers(),
|
|
229
|
+
resources: (0, import_resources.getCpuMemoryUsage)()
|
|
228
230
|
};
|
|
229
231
|
this.syncDataQueue.push(newPayload);
|
|
230
232
|
let i = 0;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/common/client.ts"],"sourcesContent":["import fetchRetry from \"fetch-retry\";\nimport { randomUUID } from \"node:crypto\";\n\nimport ConsumerRegistry from \"./consumerRegistry.js\";\nimport { Logger, getLogger } from \"./logging.js\";\nimport { isValidClientId, isValidEnv } from \"./paramValidation.js\";\nimport RequestCounter from \"./requestCounter.js\";\nimport RequestLogger from \"./requestLogger.js\";\nimport ServerErrorCounter from \"./serverErrorCounter.js\";\nimport {\n ApitallyConfig,\n StartupData,\n StartupPayload,\n SyncPayload,\n} from \"./types.js\";\nimport ValidationErrorCounter from \"./validationErrorCounter.js\";\n\nconst SYNC_INTERVAL = 60000; // 60 seconds\nconst INITIAL_SYNC_INTERVAL = 10000; // 10 seconds\nconst INITIAL_SYNC_INTERVAL_DURATION = 3600000; // 1 hour\nconst MAX_QUEUE_TIME = 3.6e6; // 1 hour\n\nclass HTTPError extends Error {\n public response: Response;\n\n constructor(response: Response) {\n const reason = response.status\n ? `status code ${response.status}`\n : \"an unknown error\";\n super(`Request failed with ${reason}`);\n this.response = response;\n }\n}\n\nexport class ApitallyClient {\n private clientId: string;\n private env: string;\n\n private static instance?: ApitallyClient;\n private instanceUuid: string;\n private syncDataQueue: SyncPayload[];\n private syncIntervalId?: NodeJS.Timeout;\n public startupData?: StartupData;\n private startupDataSent: boolean = false;\n private enabled: boolean = true;\n\n public requestCounter: RequestCounter;\n public requestLogger: RequestLogger;\n public validationErrorCounter: ValidationErrorCounter;\n public serverErrorCounter: ServerErrorCounter;\n public consumerRegistry: ConsumerRegistry;\n public logger: Logger;\n\n constructor({\n clientId,\n env = \"dev\",\n requestLogging,\n requestLoggingConfig,\n logger,\n }: ApitallyConfig) {\n if (ApitallyClient.instance) {\n throw new Error(\"Apitally client is already initialized\");\n }\n\n this.logger = logger ?? getLogger();\n\n if (!isValidClientId(clientId)) {\n this.logger.error(\n `Invalid Apitally client ID '${clientId}' (expecting hexadecimal UUID format)`,\n );\n this.enabled = false;\n }\n if (!isValidEnv(env)) {\n this.logger.error(\n `Invalid Apitally env '${env}' (expecting 1-32 alphanumeric characters and hyphens only)`,\n );\n this.enabled = false;\n }\n if (requestLoggingConfig && !requestLogging) {\n console.warn(\n \"requestLoggingConfig is deprecated, use requestLogging instead.\",\n );\n }\n\n ApitallyClient.instance = this;\n this.clientId = clientId;\n this.env = env;\n this.instanceUuid = randomUUID();\n this.syncDataQueue = [];\n this.requestCounter = new RequestCounter();\n this.requestLogger = new RequestLogger(\n requestLogging ?? requestLoggingConfig,\n );\n this.validationErrorCounter = new ValidationErrorCounter();\n this.serverErrorCounter = new ServerErrorCounter();\n this.consumerRegistry = new ConsumerRegistry();\n this.handleShutdown = this.handleShutdown.bind(this);\n }\n\n public static getInstance() {\n if (!ApitallyClient.instance) {\n throw new Error(\"Apitally client is not initialized\");\n }\n return ApitallyClient.instance;\n }\n\n public isEnabled() {\n return this.enabled;\n }\n\n public static async shutdown() {\n if (ApitallyClient.instance) {\n await ApitallyClient.instance.handleShutdown();\n }\n }\n\n public async handleShutdown() {\n this.enabled = false;\n this.stopSync();\n await this.sendSyncData();\n await this.sendLogData();\n await this.requestLogger.close();\n ApitallyClient.instance = undefined;\n }\n\n private getHubUrlPrefix() {\n const baseURL =\n process.env.APITALLY_HUB_BASE_URL || \"https://hub.apitally.io\";\n const version = \"v2\";\n return `${baseURL}/${version}/${this.clientId}/${this.env}/`;\n }\n\n private async sendData(url: string, payload: any) {\n const fetchWithRetry = fetchRetry(fetch, {\n retries: 3,\n retryDelay: 1000,\n retryOn: [408, 429, 500, 502, 503, 504],\n });\n const response = await fetchWithRetry(this.getHubUrlPrefix() + url, {\n method: \"POST\",\n body: JSON.stringify(payload),\n headers: { \"Content-Type\": \"application/json\" },\n });\n if (!response.ok) {\n throw new HTTPError(response);\n }\n }\n\n public startSync() {\n if (!this.enabled) {\n return;\n }\n this.sync();\n this.syncIntervalId = setInterval(() => {\n this.sync();\n }, INITIAL_SYNC_INTERVAL);\n setTimeout(() => {\n if (this.syncIntervalId) {\n clearInterval(this.syncIntervalId);\n this.syncIntervalId = setInterval(() => {\n this.sync();\n }, SYNC_INTERVAL);\n }\n }, INITIAL_SYNC_INTERVAL_DURATION);\n }\n\n private async sync() {\n try {\n const promises = [this.sendSyncData(), this.sendLogData()];\n if (!this.startupDataSent) {\n promises.push(this.sendStartupData());\n }\n await Promise.all(promises);\n } catch (error) {\n this.logger.error(\"Error while syncing with Apitally Hub\", {\n error,\n });\n }\n }\n\n private stopSync() {\n if (this.syncIntervalId) {\n clearInterval(this.syncIntervalId);\n this.syncIntervalId = undefined;\n }\n }\n\n public setStartupData(data: StartupData) {\n this.startupData = data;\n this.startupDataSent = false;\n }\n\n private async sendStartupData() {\n if (this.startupData) {\n this.logger.debug(\"Sending startup data to Apitally Hub\");\n const payload: StartupPayload = {\n instance_uuid: this.instanceUuid,\n message_uuid: randomUUID(),\n ...this.startupData,\n };\n try {\n await this.sendData(\"startup\", payload);\n this.startupDataSent = true;\n } catch (error) {\n const handled = this.handleHubError(error);\n if (!handled) {\n this.logger.error((error as Error).message);\n this.logger.debug(\n \"Error while sending startup data to Apitally Hub (will retry)\",\n { error },\n );\n }\n }\n }\n }\n\n private async sendSyncData() {\n this.logger.debug(\"Synchronizing data with Apitally Hub\");\n const newPayload: SyncPayload = {\n timestamp: Date.now() / 1000,\n instance_uuid: this.instanceUuid,\n message_uuid: randomUUID(),\n requests: this.requestCounter.getAndResetRequests(),\n validation_errors:\n this.validationErrorCounter.getAndResetValidationErrors(),\n server_errors: this.serverErrorCounter.getAndResetServerErrors(),\n consumers: this.consumerRegistry.getAndResetUpdatedConsumers(),\n };\n this.syncDataQueue.push(newPayload);\n\n let i = 0;\n while (this.syncDataQueue.length > 0) {\n const payload = this.syncDataQueue.shift();\n if (payload) {\n try {\n if (Date.now() - payload.timestamp * 1000 <= MAX_QUEUE_TIME) {\n if (i > 0) {\n await this.randomDelay();\n }\n await this.sendData(\"sync\", payload);\n i += 1;\n }\n } catch (error) {\n const handled = this.handleHubError(error);\n if (!handled) {\n this.logger.debug(\n \"Error while synchronizing data with Apitally Hub (will retry)\",\n { error },\n );\n this.syncDataQueue.push(payload);\n break;\n }\n }\n }\n }\n }\n\n private async sendLogData() {\n this.logger.debug(\"Sending request log data to Apitally Hub\");\n await this.requestLogger.rotateFile();\n\n const fetchWithRetry = fetchRetry(fetch, {\n retries: 3,\n retryDelay: 1000,\n retryOn: [408, 429, 500, 502, 503, 504],\n });\n\n let i = 0;\n let logFile;\n while ((logFile = this.requestLogger.getFile())) {\n if (i > 0) {\n await this.randomDelay();\n }\n\n try {\n const response = await fetchWithRetry(\n `${this.getHubUrlPrefix()}log?uuid=${logFile.uuid}`,\n {\n method: \"POST\",\n body: (await logFile.getContent()) as any,\n },\n );\n\n if (response.status === 402 && response.headers.has(\"Retry-After\")) {\n const retryAfter = parseInt(\n response.headers.get(\"Retry-After\") ?? \"0\",\n );\n if (retryAfter > 0) {\n this.requestLogger.suspendUntil = Date.now() + retryAfter * 1000;\n this.requestLogger.clear();\n return;\n }\n }\n\n if (!response.ok) {\n throw new HTTPError(response);\n }\n\n logFile.delete();\n } catch (error) {\n this.requestLogger.retryFileLater(logFile);\n break;\n }\n\n i++;\n if (i >= 10) break;\n }\n }\n\n private handleHubError(error: unknown) {\n if (error instanceof HTTPError) {\n if (error.response.status === 404) {\n this.logger.error(`Invalid Apitally client ID: '${this.clientId}'`);\n this.enabled = false;\n this.stopSync();\n return true;\n }\n if (error.response.status === 422) {\n this.logger.error(\"Received validation error from Apitally Hub\");\n return true;\n }\n }\n return false;\n }\n\n private async randomDelay() {\n const delay = 100 + Math.random() * 400;\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;;;;;AAAA,yBAAuB;AACvB,yBAA2B;AAE3B,8BAA6B;AAC7B,qBAAkC;AAClC,6BAA4C;AAC5C,4BAA2B;AAC3B,2BAA0B;AAC1B,gCAA+B;AAO/B,oCAAmC;AAfnC;AAiBA,MAAMA,gBAAgB;AACtB,MAAMC,wBAAwB;AAC9B,MAAMC,iCAAiC;AACvC,MAAMC,iBAAiB;AAEvB,IAAMC,aAAN,mBAAwBC,MAAAA;EACfC;EAEP,YAAYA,UAAoB;AAC9B,UAAMC,SAASD,SAASE,SACpB,eAAeF,SAASE,MAAM,KAC9B;AACJ,UAAM,uBAAuBD,MAAAA,EAAQ;AACrC,SAAKD,WAAWA;EAClB;AACF,GAVwBD,yBAAxB;AAYO,MAAMI,kBAAN,MAAMA,gBAAAA;EACHC;EACAC;EAGAC;EACAC;EACAC;EACDC;EACCC,kBAA2B;EAC3BC,UAAmB;EAEpBC;EACAC;EACAC;EACAC;EACAC;EACAC;EAEP,YAAY,EACVb,UACAC,MAAM,OACNa,gBACAC,sBACAF,OAAM,GACW;AACjB,QAAId,gBAAeiB,UAAU;AAC3B,YAAM,IAAIrB,MAAM,wCAAA;IAClB;AAEA,SAAKkB,SAASA,cAAUI,0BAAAA;AAExB,QAAI,KAACC,wCAAgBlB,QAAAA,GAAW;AAC9B,WAAKa,OAAOM,MACV,+BAA+BnB,QAAAA,uCAA+C;AAEhF,WAAKO,UAAU;IACjB;AACA,QAAI,KAACa,mCAAWnB,GAAAA,GAAM;AACpB,WAAKY,OAAOM,MACV,yBAAyBlB,GAAAA,6DAAgE;AAE3F,WAAKM,UAAU;IACjB;AACA,QAAIQ,wBAAwB,CAACD,gBAAgB;AAC3CO,cAAQC,KACN,iEAAA;IAEJ;AAEAvB,oBAAeiB,WAAW;AAC1B,SAAKhB,WAAWA;AAChB,SAAKC,MAAMA;AACX,SAAKC,mBAAeqB,+BAAAA;AACpB,SAAKpB,gBAAgB,CAAA;AACrB,SAAKK,iBAAiB,IAAIgB,sBAAAA,QAAAA;AAC1B,SAAKf,gBAAgB,IAAIgB,qBAAAA,QACvBX,kBAAkBC,oBAAAA;AAEpB,SAAKL,yBAAyB,IAAIgB,8BAAAA,QAAAA;AAClC,SAAKf,qBAAqB,IAAIgB,0BAAAA,QAAAA;AAC9B,SAAKf,mBAAmB,IAAIgB,wBAAAA,QAAAA;AAC5B,SAAKC,iBAAiB,KAAKA,eAAeC,KAAK,IAAI;EACrD;EAEA,OAAcC,cAAc;AAC1B,QAAI,CAAChC,gBAAeiB,UAAU;AAC5B,YAAM,IAAIrB,MAAM,oCAAA;IAClB;AACA,WAAOI,gBAAeiB;EACxB;EAEOgB,YAAY;AACjB,WAAO,KAAKzB;EACd;EAEA,aAAoB0B,WAAW;AAC7B,QAAIlC,gBAAeiB,UAAU;AAC3B,YAAMjB,gBAAeiB,SAASa,eAAc;IAC9C;EACF;EAEA,MAAaA,iBAAiB;AAC5B,SAAKtB,UAAU;AACf,SAAK2B,SAAQ;AACb,UAAM,KAAKC,aAAY;AACvB,UAAM,KAAKC,YAAW;AACtB,UAAM,KAAK3B,cAAc4B,MAAK;AAC9BtC,oBAAeiB,WAAWsB;EAC5B;EAEQC,kBAAkB;AACxB,UAAMC,UACJC,QAAQxC,IAAIyC,yBAAyB;AACvC,UAAMC,UAAU;AAChB,WAAO,GAAGH,OAAAA,IAAWG,OAAAA,IAAW,KAAK3C,QAAQ,IAAI,KAAKC,GAAG;EAC3D;EAEA,MAAc2C,SAASC,KAAaC,SAAc;AAChD,UAAMC,qBAAiBC,mBAAAA,SAAWC,OAAO;MACvCC,SAAS;MACTC,YAAY;MACZC,SAAS;QAAC;QAAK;QAAK;QAAK;QAAK;QAAK;;IACrC,CAAA;AACA,UAAMxD,WAAW,MAAMmD,eAAe,KAAKR,gBAAe,IAAKM,KAAK;MAClEQ,QAAQ;MACRC,MAAMC,KAAKC,UAAUV,OAAAA;MACrBW,SAAS;QAAE,gBAAgB;MAAmB;IAChD,CAAA;AACA,QAAI,CAAC7D,SAAS8D,IAAI;AAChB,YAAM,IAAIhE,UAAUE,QAAAA;IACtB;EACF;EAEO+D,YAAY;AACjB,QAAI,CAAC,KAAKpD,SAAS;AACjB;IACF;AACA,SAAKqD,KAAI;AACT,SAAKxD,iBAAiByD,YAAY,MAAA;AAChC,WAAKD,KAAI;IACX,GAAGrE,qBAAAA;AACHuE,eAAW,MAAA;AACT,UAAI,KAAK1D,gBAAgB;AACvB2D,sBAAc,KAAK3D,cAAc;AACjC,aAAKA,iBAAiByD,YAAY,MAAA;AAChC,eAAKD,KAAI;QACX,GAAGtE,aAAAA;MACL;IACF,GAAGE,8BAAAA;EACL;EAEA,MAAcoE,OAAO;AACnB,QAAI;AACF,YAAMI,WAAW;QAAC,KAAK7B,aAAY;QAAI,KAAKC,YAAW;;AACvD,UAAI,CAAC,KAAK9B,iBAAiB;AACzB0D,iBAASC,KAAK,KAAKC,gBAAe,CAAA;MACpC;AACA,YAAMC,QAAQC,IAAIJ,QAAAA;IACpB,SAAS7C,OAAO;AACd,WAAKN,OAAOM,MAAM,yCAAyC;QACzDA;MACF,CAAA;IACF;EACF;EAEQe,WAAW;AACjB,QAAI,KAAK9B,gBAAgB;AACvB2D,oBAAc,KAAK3D,cAAc;AACjC,WAAKA,iBAAiBkC;IACxB;EACF;EAEO+B,eAAeC,MAAmB;AACvC,SAAKjE,cAAciE;AACnB,SAAKhE,kBAAkB;EACzB;EAEA,MAAc4D,kBAAkB;AAC9B,QAAI,KAAK7D,aAAa;AACpB,WAAKQ,OAAO0D,MAAM,sCAAA;AAClB,YAAMzB,UAA0B;QAC9B0B,eAAe,KAAKtE;QACpBuE,kBAAclD,+BAAAA;QACd,GAAG,KAAKlB;MACV;AACA,UAAI;AACF,cAAM,KAAKuC,SAAS,WAAWE,OAAAA;AAC/B,aAAKxC,kBAAkB;MACzB,SAASa,OAAO;AACd,cAAMuD,UAAU,KAAKC,eAAexD,KAAAA;AACpC,YAAI,CAACuD,SAAS;AACZ,eAAK7D,OAAOM,MAAOA,MAAgByD,OAAO;AAC1C,eAAK/D,OAAO0D,MACV,iEACA;YAAEpD;UAAM,CAAA;QAEZ;MACF;IACF;EACF;EAEA,MAAcgB,eAAe;AAC3B,SAAKtB,OAAO0D,MAAM,sCAAA;AAClB,UAAMM,aAA0B;MAC9BC,WAAWC,KAAKC,IAAG,IAAK;MACxBR,eAAe,KAAKtE;MACpBuE,kBAAclD,+BAAAA;MACd0D,UAAU,KAAKzE,eAAe0E,oBAAmB;MACjDC,mBACE,KAAKzE,uBAAuB0E,4BAA2B;MACzDC,eAAe,KAAK1E,mBAAmB2E,wBAAuB;MAC9DC,WAAW,KAAK3E,iBAAiB4E,4BAA2B;IAC9D;AACA,SAAKrF,cAAc8D,KAAKY,UAAAA;AAExB,QAAIY,IAAI;AACR,WAAO,KAAKtF,cAAcuF,SAAS,GAAG;AACpC,YAAM5C,UAAU,KAAK3C,cAAcwF,MAAK;AACxC,UAAI7C,SAAS;AACX,YAAI;AACF,cAAIiC,KAAKC,IAAG,IAAKlC,QAAQgC,YAAY,OAAQrF,gBAAgB;AAC3D,gBAAIgG,IAAI,GAAG;AACT,oBAAM,KAAKG,YAAW;YACxB;AACA,kBAAM,KAAKhD,SAAS,QAAQE,OAAAA;AAC5B2C,iBAAK;UACP;QACF,SAAStE,OAAO;AACd,gBAAMuD,UAAU,KAAKC,eAAexD,KAAAA;AACpC,cAAI,CAACuD,SAAS;AACZ,iBAAK7D,OAAO0D,MACV,iEACA;cAAEpD;YAAM,CAAA;AAEV,iBAAKhB,cAAc8D,KAAKnB,OAAAA;AACxB;UACF;QACF;MACF;IACF;EACF;EAEA,MAAcV,cAAc;AAC1B,SAAKvB,OAAO0D,MAAM,0CAAA;AAClB,UAAM,KAAK9D,cAAcoF,WAAU;AAEnC,UAAM9C,qBAAiBC,mBAAAA,SAAWC,OAAO;MACvCC,SAAS;MACTC,YAAY;MACZC,SAAS;QAAC;QAAK;QAAK;QAAK;QAAK;QAAK;;IACrC,CAAA;AAEA,QAAIqC,IAAI;AACR,QAAIK;AACJ,WAAQA,UAAU,KAAKrF,cAAcsF,QAAO,GAAK;AAC/C,UAAIN,IAAI,GAAG;AACT,cAAM,KAAKG,YAAW;MACxB;AAEA,UAAI;AACF,cAAMhG,WAAW,MAAMmD,eACrB,GAAG,KAAKR,gBAAe,CAAA,YAAcuD,QAAQE,IAAI,IACjD;UACE3C,QAAQ;UACRC,MAAO,MAAMwC,QAAQG,WAAU;QACjC,CAAA;AAGF,YAAIrG,SAASE,WAAW,OAAOF,SAAS6D,QAAQyC,IAAI,aAAA,GAAgB;AAClE,gBAAMC,aAAaC,SACjBxG,SAAS6D,QAAQ4C,IAAI,aAAA,KAAkB,GAAA;AAEzC,cAAIF,aAAa,GAAG;AAClB,iBAAK1F,cAAc6F,eAAevB,KAAKC,IAAG,IAAKmB,aAAa;AAC5D,iBAAK1F,cAAc8F,MAAK;AACxB;UACF;QACF;AAEA,YAAI,CAAC3G,SAAS8D,IAAI;AAChB,gBAAM,IAAIhE,UAAUE,QAAAA;QACtB;AAEAkG,gBAAQU,OAAM;MAChB,SAASrF,OAAO;AACd,aAAKV,cAAcgG,eAAeX,OAAAA;AAClC;MACF;AAEAL;AACA,UAAIA,KAAK,GAAI;IACf;EACF;EAEQd,eAAexD,OAAgB;AACrC,QAAIA,iBAAiBzB,WAAW;AAC9B,UAAIyB,MAAMvB,SAASE,WAAW,KAAK;AACjC,aAAKe,OAAOM,MAAM,gCAAgC,KAAKnB,QAAQ,GAAG;AAClE,aAAKO,UAAU;AACf,aAAK2B,SAAQ;AACb,eAAO;MACT;AACA,UAAIf,MAAMvB,SAASE,WAAW,KAAK;AACjC,aAAKe,OAAOM,MAAM,6CAAA;AAClB,eAAO;MACT;IACF;AACA,WAAO;EACT;EAEA,MAAcyE,cAAc;AAC1B,UAAMc,QAAQ,MAAMC,KAAKC,OAAM,IAAK;AACpC,UAAM,IAAIzC,QAAQ,CAAC0C,YAAY/C,WAAW+C,SAASH,KAAAA,CAAAA;EACrD;AACF;AAvSa3G;AAIX,cAJWA,iBAIIiB;AAJV,IAAMjB,iBAAN;","names":["SYNC_INTERVAL","INITIAL_SYNC_INTERVAL","INITIAL_SYNC_INTERVAL_DURATION","MAX_QUEUE_TIME","HTTPError","Error","response","reason","status","ApitallyClient","clientId","env","instanceUuid","syncDataQueue","syncIntervalId","startupData","startupDataSent","enabled","requestCounter","requestLogger","validationErrorCounter","serverErrorCounter","consumerRegistry","logger","requestLogging","requestLoggingConfig","instance","getLogger","isValidClientId","error","isValidEnv","console","warn","randomUUID","RequestCounter","RequestLogger","ValidationErrorCounter","ServerErrorCounter","ConsumerRegistry","handleShutdown","bind","getInstance","isEnabled","shutdown","stopSync","sendSyncData","sendLogData","close","undefined","getHubUrlPrefix","baseURL","process","APITALLY_HUB_BASE_URL","version","sendData","url","payload","fetchWithRetry","fetchRetry","fetch","retries","retryDelay","retryOn","method","body","JSON","stringify","headers","ok","startSync","sync","setInterval","setTimeout","clearInterval","promises","push","sendStartupData","Promise","all","setStartupData","data","debug","instance_uuid","message_uuid","handled","handleHubError","message","newPayload","timestamp","Date","now","requests","getAndResetRequests","validation_errors","getAndResetValidationErrors","server_errors","getAndResetServerErrors","consumers","getAndResetUpdatedConsumers","i","length","shift","randomDelay","rotateFile","logFile","getFile","uuid","getContent","has","retryAfter","parseInt","get","suspendUntil","clear","delete","retryFileLater","delay","Math","random","resolve"]}
|
|
1
|
+
{"version":3,"sources":["../../src/common/client.ts"],"sourcesContent":["import fetchRetry from \"fetch-retry\";\nimport { randomUUID } from \"node:crypto\";\n\nimport ConsumerRegistry from \"./consumerRegistry.js\";\nimport { Logger, getLogger } from \"./logging.js\";\nimport { isValidClientId, isValidEnv } from \"./paramValidation.js\";\nimport RequestCounter from \"./requestCounter.js\";\nimport RequestLogger from \"./requestLogger.js\";\nimport { getCpuMemoryUsage } from \"./resources.js\";\nimport ServerErrorCounter from \"./serverErrorCounter.js\";\nimport {\n ApitallyConfig,\n StartupData,\n StartupPayload,\n SyncPayload,\n} from \"./types.js\";\nimport ValidationErrorCounter from \"./validationErrorCounter.js\";\n\nconst SYNC_INTERVAL = 60000; // 60 seconds\nconst INITIAL_SYNC_INTERVAL = 10000; // 10 seconds\nconst INITIAL_SYNC_INTERVAL_DURATION = 3600000; // 1 hour\nconst MAX_QUEUE_TIME = 3.6e6; // 1 hour\n\nclass HTTPError extends Error {\n public response: Response;\n\n constructor(response: Response) {\n const reason = response.status\n ? `status code ${response.status}`\n : \"an unknown error\";\n super(`Request failed with ${reason}`);\n this.response = response;\n }\n}\n\nexport class ApitallyClient {\n private clientId: string;\n private env: string;\n\n private static instance?: ApitallyClient;\n private instanceUuid: string;\n private syncDataQueue: SyncPayload[];\n private syncIntervalId?: NodeJS.Timeout;\n public startupData?: StartupData;\n private startupDataSent: boolean = false;\n private enabled: boolean = true;\n\n public requestCounter: RequestCounter;\n public requestLogger: RequestLogger;\n public validationErrorCounter: ValidationErrorCounter;\n public serverErrorCounter: ServerErrorCounter;\n public consumerRegistry: ConsumerRegistry;\n public logger: Logger;\n\n constructor({\n clientId,\n env = \"dev\",\n requestLogging,\n requestLoggingConfig,\n logger,\n }: ApitallyConfig) {\n if (ApitallyClient.instance) {\n throw new Error(\"Apitally client is already initialized\");\n }\n\n this.logger = logger ?? getLogger();\n\n if (!isValidClientId(clientId)) {\n this.logger.error(\n `Invalid Apitally client ID '${clientId}' (expecting hexadecimal UUID format)`,\n );\n this.enabled = false;\n }\n if (!isValidEnv(env)) {\n this.logger.error(\n `Invalid Apitally env '${env}' (expecting 1-32 alphanumeric characters and hyphens only)`,\n );\n this.enabled = false;\n }\n if (requestLoggingConfig && !requestLogging) {\n console.warn(\n \"requestLoggingConfig is deprecated, use requestLogging instead.\",\n );\n }\n\n ApitallyClient.instance = this;\n this.clientId = clientId;\n this.env = env;\n this.instanceUuid = randomUUID();\n this.syncDataQueue = [];\n this.requestCounter = new RequestCounter();\n this.requestLogger = new RequestLogger(\n requestLogging ?? requestLoggingConfig,\n );\n this.validationErrorCounter = new ValidationErrorCounter();\n this.serverErrorCounter = new ServerErrorCounter();\n this.consumerRegistry = new ConsumerRegistry();\n this.handleShutdown = this.handleShutdown.bind(this);\n }\n\n public static getInstance() {\n if (!ApitallyClient.instance) {\n throw new Error(\"Apitally client is not initialized\");\n }\n return ApitallyClient.instance;\n }\n\n public isEnabled() {\n return this.enabled;\n }\n\n public static async shutdown() {\n if (ApitallyClient.instance) {\n await ApitallyClient.instance.handleShutdown();\n }\n }\n\n public async handleShutdown() {\n this.enabled = false;\n this.stopSync();\n await this.sendSyncData();\n await this.sendLogData();\n await this.requestLogger.close();\n ApitallyClient.instance = undefined;\n }\n\n private getHubUrlPrefix() {\n const baseURL =\n process.env.APITALLY_HUB_BASE_URL || \"https://hub.apitally.io\";\n const version = \"v2\";\n return `${baseURL}/${version}/${this.clientId}/${this.env}/`;\n }\n\n private async sendData(url: string, payload: any) {\n const fetchWithRetry = fetchRetry(fetch, {\n retries: 3,\n retryDelay: 1000,\n retryOn: [408, 429, 500, 502, 503, 504],\n });\n const response = await fetchWithRetry(this.getHubUrlPrefix() + url, {\n method: \"POST\",\n body: JSON.stringify(payload),\n headers: { \"Content-Type\": \"application/json\" },\n });\n if (!response.ok) {\n throw new HTTPError(response);\n }\n }\n\n public startSync() {\n if (!this.enabled) {\n return;\n }\n this.sync();\n this.syncIntervalId = setInterval(() => {\n this.sync();\n }, INITIAL_SYNC_INTERVAL);\n setTimeout(() => {\n if (this.syncIntervalId) {\n clearInterval(this.syncIntervalId);\n this.syncIntervalId = setInterval(() => {\n this.sync();\n }, SYNC_INTERVAL);\n }\n }, INITIAL_SYNC_INTERVAL_DURATION);\n }\n\n private async sync() {\n try {\n const promises = [this.sendSyncData(), this.sendLogData()];\n if (!this.startupDataSent) {\n promises.push(this.sendStartupData());\n }\n await Promise.all(promises);\n } catch (error) {\n this.logger.error(\"Error while syncing with Apitally Hub\", {\n error,\n });\n }\n }\n\n private stopSync() {\n if (this.syncIntervalId) {\n clearInterval(this.syncIntervalId);\n this.syncIntervalId = undefined;\n }\n }\n\n public setStartupData(data: StartupData) {\n this.startupData = data;\n this.startupDataSent = false;\n }\n\n private async sendStartupData() {\n if (this.startupData) {\n this.logger.debug(\"Sending startup data to Apitally Hub\");\n const payload: StartupPayload = {\n instance_uuid: this.instanceUuid,\n message_uuid: randomUUID(),\n ...this.startupData,\n };\n try {\n await this.sendData(\"startup\", payload);\n this.startupDataSent = true;\n } catch (error) {\n const handled = this.handleHubError(error);\n if (!handled) {\n this.logger.error((error as Error).message);\n this.logger.debug(\n \"Error while sending startup data to Apitally Hub (will retry)\",\n { error },\n );\n }\n }\n }\n }\n\n private async sendSyncData() {\n this.logger.debug(\"Synchronizing data with Apitally Hub\");\n const newPayload: SyncPayload = {\n timestamp: Date.now() / 1000,\n instance_uuid: this.instanceUuid,\n message_uuid: randomUUID(),\n requests: this.requestCounter.getAndResetRequests(),\n validation_errors:\n this.validationErrorCounter.getAndResetValidationErrors(),\n server_errors: this.serverErrorCounter.getAndResetServerErrors(),\n consumers: this.consumerRegistry.getAndResetUpdatedConsumers(),\n resources: getCpuMemoryUsage(),\n };\n this.syncDataQueue.push(newPayload);\n\n let i = 0;\n while (this.syncDataQueue.length > 0) {\n const payload = this.syncDataQueue.shift();\n if (payload) {\n try {\n if (Date.now() - payload.timestamp * 1000 <= MAX_QUEUE_TIME) {\n if (i > 0) {\n await this.randomDelay();\n }\n await this.sendData(\"sync\", payload);\n i += 1;\n }\n } catch (error) {\n const handled = this.handleHubError(error);\n if (!handled) {\n this.logger.debug(\n \"Error while synchronizing data with Apitally Hub (will retry)\",\n { error },\n );\n this.syncDataQueue.push(payload);\n break;\n }\n }\n }\n }\n }\n\n private async sendLogData() {\n this.logger.debug(\"Sending request log data to Apitally Hub\");\n await this.requestLogger.rotateFile();\n\n const fetchWithRetry = fetchRetry(fetch, {\n retries: 3,\n retryDelay: 1000,\n retryOn: [408, 429, 500, 502, 503, 504],\n });\n\n let i = 0;\n let logFile;\n while ((logFile = this.requestLogger.getFile())) {\n if (i > 0) {\n await this.randomDelay();\n }\n\n try {\n const response = await fetchWithRetry(\n `${this.getHubUrlPrefix()}log?uuid=${logFile.uuid}`,\n {\n method: \"POST\",\n body: (await logFile.getContent()) as any,\n },\n );\n\n if (response.status === 402 && response.headers.has(\"Retry-After\")) {\n const retryAfter = parseInt(\n response.headers.get(\"Retry-After\") ?? \"0\",\n );\n if (retryAfter > 0) {\n this.requestLogger.suspendUntil = Date.now() + retryAfter * 1000;\n this.requestLogger.clear();\n return;\n }\n }\n\n if (!response.ok) {\n throw new HTTPError(response);\n }\n\n logFile.delete();\n } catch (error) {\n this.requestLogger.retryFileLater(logFile);\n break;\n }\n\n i++;\n if (i >= 10) break;\n }\n }\n\n private handleHubError(error: unknown) {\n if (error instanceof HTTPError) {\n if (error.response.status === 404) {\n this.logger.error(`Invalid Apitally client ID: '${this.clientId}'`);\n this.enabled = false;\n this.stopSync();\n return true;\n }\n if (error.response.status === 422) {\n this.logger.error(\"Received validation error from Apitally Hub\");\n return true;\n }\n }\n return false;\n }\n\n private async randomDelay() {\n const delay = 100 + Math.random() * 400;\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n}\n"],"mappings":"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","names":["SYNC_INTERVAL","INITIAL_SYNC_INTERVAL","INITIAL_SYNC_INTERVAL_DURATION","MAX_QUEUE_TIME","HTTPError","Error","response","reason","status","ApitallyClient","clientId","env","instanceUuid","syncDataQueue","syncIntervalId","startupData","startupDataSent","enabled","requestCounter","requestLogger","validationErrorCounter","serverErrorCounter","consumerRegistry","logger","requestLogging","requestLoggingConfig","instance","getLogger","isValidClientId","error","isValidEnv","console","warn","randomUUID","RequestCounter","RequestLogger","ValidationErrorCounter","ServerErrorCounter","ConsumerRegistry","handleShutdown","bind","getInstance","isEnabled","shutdown","stopSync","sendSyncData","sendLogData","close","undefined","getHubUrlPrefix","baseURL","process","APITALLY_HUB_BASE_URL","version","sendData","url","payload","fetchWithRetry","fetchRetry","fetch","retries","retryDelay","retryOn","method","body","JSON","stringify","headers","ok","startSync","sync","setInterval","setTimeout","clearInterval","promises","push","sendStartupData","Promise","all","setStartupData","data","debug","instance_uuid","message_uuid","handled","handleHubError","message","newPayload","timestamp","Date","now","requests","getAndResetRequests","validation_errors","getAndResetValidationErrors","server_errors","getAndResetServerErrors","consumers","getAndResetUpdatedConsumers","resources","getCpuMemoryUsage","i","length","shift","randomDelay","rotateFile","logFile","getFile","uuid","getContent","has","retryAfter","parseInt","get","suspendUntil","clear","delete","retryFileLater","delay","Math","random","resolve"]}
|
package/dist/common/client.js
CHANGED
|
@@ -10,6 +10,7 @@ import { getLogger } from "./logging.js";
|
|
|
10
10
|
import { isValidClientId, isValidEnv } from "./paramValidation.js";
|
|
11
11
|
import RequestCounter from "./requestCounter.js";
|
|
12
12
|
import RequestLogger from "./requestLogger.js";
|
|
13
|
+
import { getCpuMemoryUsage } from "./resources.js";
|
|
13
14
|
import ServerErrorCounter from "./serverErrorCounter.js";
|
|
14
15
|
import ValidationErrorCounter from "./validationErrorCounter.js";
|
|
15
16
|
const SYNC_INTERVAL = 6e4;
|
|
@@ -192,7 +193,8 @@ const _ApitallyClient = class _ApitallyClient {
|
|
|
192
193
|
requests: this.requestCounter.getAndResetRequests(),
|
|
193
194
|
validation_errors: this.validationErrorCounter.getAndResetValidationErrors(),
|
|
194
195
|
server_errors: this.serverErrorCounter.getAndResetServerErrors(),
|
|
195
|
-
consumers: this.consumerRegistry.getAndResetUpdatedConsumers()
|
|
196
|
+
consumers: this.consumerRegistry.getAndResetUpdatedConsumers(),
|
|
197
|
+
resources: getCpuMemoryUsage()
|
|
196
198
|
};
|
|
197
199
|
this.syncDataQueue.push(newPayload);
|
|
198
200
|
let i = 0;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/common/client.ts"],"sourcesContent":["import fetchRetry from \"fetch-retry\";\nimport { randomUUID } from \"node:crypto\";\n\nimport ConsumerRegistry from \"./consumerRegistry.js\";\nimport { Logger, getLogger } from \"./logging.js\";\nimport { isValidClientId, isValidEnv } from \"./paramValidation.js\";\nimport RequestCounter from \"./requestCounter.js\";\nimport RequestLogger from \"./requestLogger.js\";\nimport ServerErrorCounter from \"./serverErrorCounter.js\";\nimport {\n ApitallyConfig,\n StartupData,\n StartupPayload,\n SyncPayload,\n} from \"./types.js\";\nimport ValidationErrorCounter from \"./validationErrorCounter.js\";\n\nconst SYNC_INTERVAL = 60000; // 60 seconds\nconst INITIAL_SYNC_INTERVAL = 10000; // 10 seconds\nconst INITIAL_SYNC_INTERVAL_DURATION = 3600000; // 1 hour\nconst MAX_QUEUE_TIME = 3.6e6; // 1 hour\n\nclass HTTPError extends Error {\n public response: Response;\n\n constructor(response: Response) {\n const reason = response.status\n ? `status code ${response.status}`\n : \"an unknown error\";\n super(`Request failed with ${reason}`);\n this.response = response;\n }\n}\n\nexport class ApitallyClient {\n private clientId: string;\n private env: string;\n\n private static instance?: ApitallyClient;\n private instanceUuid: string;\n private syncDataQueue: SyncPayload[];\n private syncIntervalId?: NodeJS.Timeout;\n public startupData?: StartupData;\n private startupDataSent: boolean = false;\n private enabled: boolean = true;\n\n public requestCounter: RequestCounter;\n public requestLogger: RequestLogger;\n public validationErrorCounter: ValidationErrorCounter;\n public serverErrorCounter: ServerErrorCounter;\n public consumerRegistry: ConsumerRegistry;\n public logger: Logger;\n\n constructor({\n clientId,\n env = \"dev\",\n requestLogging,\n requestLoggingConfig,\n logger,\n }: ApitallyConfig) {\n if (ApitallyClient.instance) {\n throw new Error(\"Apitally client is already initialized\");\n }\n\n this.logger = logger ?? getLogger();\n\n if (!isValidClientId(clientId)) {\n this.logger.error(\n `Invalid Apitally client ID '${clientId}' (expecting hexadecimal UUID format)`,\n );\n this.enabled = false;\n }\n if (!isValidEnv(env)) {\n this.logger.error(\n `Invalid Apitally env '${env}' (expecting 1-32 alphanumeric characters and hyphens only)`,\n );\n this.enabled = false;\n }\n if (requestLoggingConfig && !requestLogging) {\n console.warn(\n \"requestLoggingConfig is deprecated, use requestLogging instead.\",\n );\n }\n\n ApitallyClient.instance = this;\n this.clientId = clientId;\n this.env = env;\n this.instanceUuid = randomUUID();\n this.syncDataQueue = [];\n this.requestCounter = new RequestCounter();\n this.requestLogger = new RequestLogger(\n requestLogging ?? requestLoggingConfig,\n );\n this.validationErrorCounter = new ValidationErrorCounter();\n this.serverErrorCounter = new ServerErrorCounter();\n this.consumerRegistry = new ConsumerRegistry();\n this.handleShutdown = this.handleShutdown.bind(this);\n }\n\n public static getInstance() {\n if (!ApitallyClient.instance) {\n throw new Error(\"Apitally client is not initialized\");\n }\n return ApitallyClient.instance;\n }\n\n public isEnabled() {\n return this.enabled;\n }\n\n public static async shutdown() {\n if (ApitallyClient.instance) {\n await ApitallyClient.instance.handleShutdown();\n }\n }\n\n public async handleShutdown() {\n this.enabled = false;\n this.stopSync();\n await this.sendSyncData();\n await this.sendLogData();\n await this.requestLogger.close();\n ApitallyClient.instance = undefined;\n }\n\n private getHubUrlPrefix() {\n const baseURL =\n process.env.APITALLY_HUB_BASE_URL || \"https://hub.apitally.io\";\n const version = \"v2\";\n return `${baseURL}/${version}/${this.clientId}/${this.env}/`;\n }\n\n private async sendData(url: string, payload: any) {\n const fetchWithRetry = fetchRetry(fetch, {\n retries: 3,\n retryDelay: 1000,\n retryOn: [408, 429, 500, 502, 503, 504],\n });\n const response = await fetchWithRetry(this.getHubUrlPrefix() + url, {\n method: \"POST\",\n body: JSON.stringify(payload),\n headers: { \"Content-Type\": \"application/json\" },\n });\n if (!response.ok) {\n throw new HTTPError(response);\n }\n }\n\n public startSync() {\n if (!this.enabled) {\n return;\n }\n this.sync();\n this.syncIntervalId = setInterval(() => {\n this.sync();\n }, INITIAL_SYNC_INTERVAL);\n setTimeout(() => {\n if (this.syncIntervalId) {\n clearInterval(this.syncIntervalId);\n this.syncIntervalId = setInterval(() => {\n this.sync();\n }, SYNC_INTERVAL);\n }\n }, INITIAL_SYNC_INTERVAL_DURATION);\n }\n\n private async sync() {\n try {\n const promises = [this.sendSyncData(), this.sendLogData()];\n if (!this.startupDataSent) {\n promises.push(this.sendStartupData());\n }\n await Promise.all(promises);\n } catch (error) {\n this.logger.error(\"Error while syncing with Apitally Hub\", {\n error,\n });\n }\n }\n\n private stopSync() {\n if (this.syncIntervalId) {\n clearInterval(this.syncIntervalId);\n this.syncIntervalId = undefined;\n }\n }\n\n public setStartupData(data: StartupData) {\n this.startupData = data;\n this.startupDataSent = false;\n }\n\n private async sendStartupData() {\n if (this.startupData) {\n this.logger.debug(\"Sending startup data to Apitally Hub\");\n const payload: StartupPayload = {\n instance_uuid: this.instanceUuid,\n message_uuid: randomUUID(),\n ...this.startupData,\n };\n try {\n await this.sendData(\"startup\", payload);\n this.startupDataSent = true;\n } catch (error) {\n const handled = this.handleHubError(error);\n if (!handled) {\n this.logger.error((error as Error).message);\n this.logger.debug(\n \"Error while sending startup data to Apitally Hub (will retry)\",\n { error },\n );\n }\n }\n }\n }\n\n private async sendSyncData() {\n this.logger.debug(\"Synchronizing data with Apitally Hub\");\n const newPayload: SyncPayload = {\n timestamp: Date.now() / 1000,\n instance_uuid: this.instanceUuid,\n message_uuid: randomUUID(),\n requests: this.requestCounter.getAndResetRequests(),\n validation_errors:\n this.validationErrorCounter.getAndResetValidationErrors(),\n server_errors: this.serverErrorCounter.getAndResetServerErrors(),\n consumers: this.consumerRegistry.getAndResetUpdatedConsumers(),\n };\n this.syncDataQueue.push(newPayload);\n\n let i = 0;\n while (this.syncDataQueue.length > 0) {\n const payload = this.syncDataQueue.shift();\n if (payload) {\n try {\n if (Date.now() - payload.timestamp * 1000 <= MAX_QUEUE_TIME) {\n if (i > 0) {\n await this.randomDelay();\n }\n await this.sendData(\"sync\", payload);\n i += 1;\n }\n } catch (error) {\n const handled = this.handleHubError(error);\n if (!handled) {\n this.logger.debug(\n \"Error while synchronizing data with Apitally Hub (will retry)\",\n { error },\n );\n this.syncDataQueue.push(payload);\n break;\n }\n }\n }\n }\n }\n\n private async sendLogData() {\n this.logger.debug(\"Sending request log data to Apitally Hub\");\n await this.requestLogger.rotateFile();\n\n const fetchWithRetry = fetchRetry(fetch, {\n retries: 3,\n retryDelay: 1000,\n retryOn: [408, 429, 500, 502, 503, 504],\n });\n\n let i = 0;\n let logFile;\n while ((logFile = this.requestLogger.getFile())) {\n if (i > 0) {\n await this.randomDelay();\n }\n\n try {\n const response = await fetchWithRetry(\n `${this.getHubUrlPrefix()}log?uuid=${logFile.uuid}`,\n {\n method: \"POST\",\n body: (await logFile.getContent()) as any,\n },\n );\n\n if (response.status === 402 && response.headers.has(\"Retry-After\")) {\n const retryAfter = parseInt(\n response.headers.get(\"Retry-After\") ?? \"0\",\n );\n if (retryAfter > 0) {\n this.requestLogger.suspendUntil = Date.now() + retryAfter * 1000;\n this.requestLogger.clear();\n return;\n }\n }\n\n if (!response.ok) {\n throw new HTTPError(response);\n }\n\n logFile.delete();\n } catch (error) {\n this.requestLogger.retryFileLater(logFile);\n break;\n }\n\n i++;\n if (i >= 10) break;\n }\n }\n\n private handleHubError(error: unknown) {\n if (error instanceof HTTPError) {\n if (error.response.status === 404) {\n this.logger.error(`Invalid Apitally client ID: '${this.clientId}'`);\n this.enabled = false;\n this.stopSync();\n return true;\n }\n if (error.response.status === 422) {\n this.logger.error(\"Received validation error from Apitally Hub\");\n return true;\n }\n }\n return false;\n }\n\n private async randomDelay() {\n const delay = 100 + Math.random() * 400;\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n}\n"],"mappings":"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","names":["fetchRetry","randomUUID","ConsumerRegistry","getLogger","isValidClientId","isValidEnv","RequestCounter","RequestLogger","ServerErrorCounter","ValidationErrorCounter","SYNC_INTERVAL","INITIAL_SYNC_INTERVAL","INITIAL_SYNC_INTERVAL_DURATION","MAX_QUEUE_TIME","HTTPError","Error","response","reason","status","ApitallyClient","clientId","env","instanceUuid","syncDataQueue","syncIntervalId","startupData","startupDataSent","enabled","requestCounter","requestLogger","validationErrorCounter","serverErrorCounter","consumerRegistry","logger","requestLogging","requestLoggingConfig","instance","error","console","warn","handleShutdown","bind","getInstance","isEnabled","shutdown","stopSync","sendSyncData","sendLogData","close","undefined","getHubUrlPrefix","baseURL","process","APITALLY_HUB_BASE_URL","version","sendData","url","payload","fetchWithRetry","fetch","retries","retryDelay","retryOn","method","body","JSON","stringify","headers","ok","startSync","sync","setInterval","setTimeout","clearInterval","promises","push","sendStartupData","Promise","all","setStartupData","data","debug","instance_uuid","message_uuid","handled","handleHubError","message","newPayload","timestamp","Date","now","requests","getAndResetRequests","validation_errors","getAndResetValidationErrors","server_errors","getAndResetServerErrors","consumers","getAndResetUpdatedConsumers","i","length","shift","randomDelay","rotateFile","logFile","getFile","uuid","getContent","has","retryAfter","parseInt","get","suspendUntil","clear","delete","retryFileLater","delay","Math","random","resolve"]}
|
|
1
|
+
{"version":3,"sources":["../../src/common/client.ts"],"sourcesContent":["import fetchRetry from \"fetch-retry\";\nimport { randomUUID } from \"node:crypto\";\n\nimport ConsumerRegistry from \"./consumerRegistry.js\";\nimport { Logger, getLogger } from \"./logging.js\";\nimport { isValidClientId, isValidEnv } from \"./paramValidation.js\";\nimport RequestCounter from \"./requestCounter.js\";\nimport RequestLogger from \"./requestLogger.js\";\nimport { getCpuMemoryUsage } from \"./resources.js\";\nimport ServerErrorCounter from \"./serverErrorCounter.js\";\nimport {\n ApitallyConfig,\n StartupData,\n StartupPayload,\n SyncPayload,\n} from \"./types.js\";\nimport ValidationErrorCounter from \"./validationErrorCounter.js\";\n\nconst SYNC_INTERVAL = 60000; // 60 seconds\nconst INITIAL_SYNC_INTERVAL = 10000; // 10 seconds\nconst INITIAL_SYNC_INTERVAL_DURATION = 3600000; // 1 hour\nconst MAX_QUEUE_TIME = 3.6e6; // 1 hour\n\nclass HTTPError extends Error {\n public response: Response;\n\n constructor(response: Response) {\n const reason = response.status\n ? `status code ${response.status}`\n : \"an unknown error\";\n super(`Request failed with ${reason}`);\n this.response = response;\n }\n}\n\nexport class ApitallyClient {\n private clientId: string;\n private env: string;\n\n private static instance?: ApitallyClient;\n private instanceUuid: string;\n private syncDataQueue: SyncPayload[];\n private syncIntervalId?: NodeJS.Timeout;\n public startupData?: StartupData;\n private startupDataSent: boolean = false;\n private enabled: boolean = true;\n\n public requestCounter: RequestCounter;\n public requestLogger: RequestLogger;\n public validationErrorCounter: ValidationErrorCounter;\n public serverErrorCounter: ServerErrorCounter;\n public consumerRegistry: ConsumerRegistry;\n public logger: Logger;\n\n constructor({\n clientId,\n env = \"dev\",\n requestLogging,\n requestLoggingConfig,\n logger,\n }: ApitallyConfig) {\n if (ApitallyClient.instance) {\n throw new Error(\"Apitally client is already initialized\");\n }\n\n this.logger = logger ?? getLogger();\n\n if (!isValidClientId(clientId)) {\n this.logger.error(\n `Invalid Apitally client ID '${clientId}' (expecting hexadecimal UUID format)`,\n );\n this.enabled = false;\n }\n if (!isValidEnv(env)) {\n this.logger.error(\n `Invalid Apitally env '${env}' (expecting 1-32 alphanumeric characters and hyphens only)`,\n );\n this.enabled = false;\n }\n if (requestLoggingConfig && !requestLogging) {\n console.warn(\n \"requestLoggingConfig is deprecated, use requestLogging instead.\",\n );\n }\n\n ApitallyClient.instance = this;\n this.clientId = clientId;\n this.env = env;\n this.instanceUuid = randomUUID();\n this.syncDataQueue = [];\n this.requestCounter = new RequestCounter();\n this.requestLogger = new RequestLogger(\n requestLogging ?? requestLoggingConfig,\n );\n this.validationErrorCounter = new ValidationErrorCounter();\n this.serverErrorCounter = new ServerErrorCounter();\n this.consumerRegistry = new ConsumerRegistry();\n this.handleShutdown = this.handleShutdown.bind(this);\n }\n\n public static getInstance() {\n if (!ApitallyClient.instance) {\n throw new Error(\"Apitally client is not initialized\");\n }\n return ApitallyClient.instance;\n }\n\n public isEnabled() {\n return this.enabled;\n }\n\n public static async shutdown() {\n if (ApitallyClient.instance) {\n await ApitallyClient.instance.handleShutdown();\n }\n }\n\n public async handleShutdown() {\n this.enabled = false;\n this.stopSync();\n await this.sendSyncData();\n await this.sendLogData();\n await this.requestLogger.close();\n ApitallyClient.instance = undefined;\n }\n\n private getHubUrlPrefix() {\n const baseURL =\n process.env.APITALLY_HUB_BASE_URL || \"https://hub.apitally.io\";\n const version = \"v2\";\n return `${baseURL}/${version}/${this.clientId}/${this.env}/`;\n }\n\n private async sendData(url: string, payload: any) {\n const fetchWithRetry = fetchRetry(fetch, {\n retries: 3,\n retryDelay: 1000,\n retryOn: [408, 429, 500, 502, 503, 504],\n });\n const response = await fetchWithRetry(this.getHubUrlPrefix() + url, {\n method: \"POST\",\n body: JSON.stringify(payload),\n headers: { \"Content-Type\": \"application/json\" },\n });\n if (!response.ok) {\n throw new HTTPError(response);\n }\n }\n\n public startSync() {\n if (!this.enabled) {\n return;\n }\n this.sync();\n this.syncIntervalId = setInterval(() => {\n this.sync();\n }, INITIAL_SYNC_INTERVAL);\n setTimeout(() => {\n if (this.syncIntervalId) {\n clearInterval(this.syncIntervalId);\n this.syncIntervalId = setInterval(() => {\n this.sync();\n }, SYNC_INTERVAL);\n }\n }, INITIAL_SYNC_INTERVAL_DURATION);\n }\n\n private async sync() {\n try {\n const promises = [this.sendSyncData(), this.sendLogData()];\n if (!this.startupDataSent) {\n promises.push(this.sendStartupData());\n }\n await Promise.all(promises);\n } catch (error) {\n this.logger.error(\"Error while syncing with Apitally Hub\", {\n error,\n });\n }\n }\n\n private stopSync() {\n if (this.syncIntervalId) {\n clearInterval(this.syncIntervalId);\n this.syncIntervalId = undefined;\n }\n }\n\n public setStartupData(data: StartupData) {\n this.startupData = data;\n this.startupDataSent = false;\n }\n\n private async sendStartupData() {\n if (this.startupData) {\n this.logger.debug(\"Sending startup data to Apitally Hub\");\n const payload: StartupPayload = {\n instance_uuid: this.instanceUuid,\n message_uuid: randomUUID(),\n ...this.startupData,\n };\n try {\n await this.sendData(\"startup\", payload);\n this.startupDataSent = true;\n } catch (error) {\n const handled = this.handleHubError(error);\n if (!handled) {\n this.logger.error((error as Error).message);\n this.logger.debug(\n \"Error while sending startup data to Apitally Hub (will retry)\",\n { error },\n );\n }\n }\n }\n }\n\n private async sendSyncData() {\n this.logger.debug(\"Synchronizing data with Apitally Hub\");\n const newPayload: SyncPayload = {\n timestamp: Date.now() / 1000,\n instance_uuid: this.instanceUuid,\n message_uuid: randomUUID(),\n requests: this.requestCounter.getAndResetRequests(),\n validation_errors:\n this.validationErrorCounter.getAndResetValidationErrors(),\n server_errors: this.serverErrorCounter.getAndResetServerErrors(),\n consumers: this.consumerRegistry.getAndResetUpdatedConsumers(),\n resources: getCpuMemoryUsage(),\n };\n this.syncDataQueue.push(newPayload);\n\n let i = 0;\n while (this.syncDataQueue.length > 0) {\n const payload = this.syncDataQueue.shift();\n if (payload) {\n try {\n if (Date.now() - payload.timestamp * 1000 <= MAX_QUEUE_TIME) {\n if (i > 0) {\n await this.randomDelay();\n }\n await this.sendData(\"sync\", payload);\n i += 1;\n }\n } catch (error) {\n const handled = this.handleHubError(error);\n if (!handled) {\n this.logger.debug(\n \"Error while synchronizing data with Apitally Hub (will retry)\",\n { error },\n );\n this.syncDataQueue.push(payload);\n break;\n }\n }\n }\n }\n }\n\n private async sendLogData() {\n this.logger.debug(\"Sending request log data to Apitally Hub\");\n await this.requestLogger.rotateFile();\n\n const fetchWithRetry = fetchRetry(fetch, {\n retries: 3,\n retryDelay: 1000,\n retryOn: [408, 429, 500, 502, 503, 504],\n });\n\n let i = 0;\n let logFile;\n while ((logFile = this.requestLogger.getFile())) {\n if (i > 0) {\n await this.randomDelay();\n }\n\n try {\n const response = await fetchWithRetry(\n `${this.getHubUrlPrefix()}log?uuid=${logFile.uuid}`,\n {\n method: \"POST\",\n body: (await logFile.getContent()) as any,\n },\n );\n\n if (response.status === 402 && response.headers.has(\"Retry-After\")) {\n const retryAfter = parseInt(\n response.headers.get(\"Retry-After\") ?? \"0\",\n );\n if (retryAfter > 0) {\n this.requestLogger.suspendUntil = Date.now() + retryAfter * 1000;\n this.requestLogger.clear();\n return;\n }\n }\n\n if (!response.ok) {\n throw new HTTPError(response);\n }\n\n logFile.delete();\n } catch (error) {\n this.requestLogger.retryFileLater(logFile);\n break;\n }\n\n i++;\n if (i >= 10) break;\n }\n }\n\n private handleHubError(error: unknown) {\n if (error instanceof HTTPError) {\n if (error.response.status === 404) {\n this.logger.error(`Invalid Apitally client ID: '${this.clientId}'`);\n this.enabled = false;\n this.stopSync();\n return true;\n }\n if (error.response.status === 422) {\n this.logger.error(\"Received validation error from Apitally Hub\");\n return true;\n }\n }\n return false;\n }\n\n private async randomDelay() {\n const delay = 100 + Math.random() * 400;\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n}\n"],"mappings":";;;;AAAA;OAAOA,gBAAgB;AACvB,SAASC,kBAAkB;AAE3B,OAAOC,sBAAsB;AAC7B,SAAiBC,iBAAiB;AAClC,SAASC,iBAAiBC,kBAAkB;AAC5C,OAAOC,oBAAoB;AAC3B,OAAOC,mBAAmB;AAC1B,SAASC,yBAAyB;AAClC,OAAOC,wBAAwB;AAO/B,OAAOC,4BAA4B;AAEnC,MAAMC,gBAAgB;AACtB,MAAMC,wBAAwB;AAC9B,MAAMC,iCAAiC;AACvC,MAAMC,iBAAiB;AAEvB,IAAMC,aAAN,mBAAwBC,MAAAA;EACfC;EAEP,YAAYA,UAAoB;AAC9B,UAAMC,SAASD,SAASE,SACpB,eAAeF,SAASE,MAAM,KAC9B;AACJ,UAAM,uBAAuBD,MAAAA,EAAQ;AACrC,SAAKD,WAAWA;EAClB;AACF,GAVwBD,yBAAxB;AAYO,MAAMI,kBAAN,MAAMA,gBAAAA;EACHC;EACAC;EAGAC;EACAC;EACAC;EACDC;EACCC,kBAA2B;EAC3BC,UAAmB;EAEpBC;EACAC;EACAC;EACAC;EACAC;EACAC;EAEP,YAAY,EACVb,UACAC,MAAM,OACNa,gBACAC,sBACAF,OAAM,GACW;AACjB,QAAId,gBAAeiB,UAAU;AAC3B,YAAM,IAAIrB,MAAM,wCAAA;IAClB;AAEA,SAAKkB,SAASA,UAAU/B,UAAAA;AAExB,QAAI,CAACC,gBAAgBiB,QAAAA,GAAW;AAC9B,WAAKa,OAAOI,MACV,+BAA+BjB,QAAAA,uCAA+C;AAEhF,WAAKO,UAAU;IACjB;AACA,QAAI,CAACvB,WAAWiB,GAAAA,GAAM;AACpB,WAAKY,OAAOI,MACV,yBAAyBhB,GAAAA,6DAAgE;AAE3F,WAAKM,UAAU;IACjB;AACA,QAAIQ,wBAAwB,CAACD,gBAAgB;AAC3CI,cAAQC,KACN,iEAAA;IAEJ;AAEApB,oBAAeiB,WAAW;AAC1B,SAAKhB,WAAWA;AAChB,SAAKC,MAAMA;AACX,SAAKC,eAAetB,WAAAA;AACpB,SAAKuB,gBAAgB,CAAA;AACrB,SAAKK,iBAAiB,IAAIvB,eAAAA;AAC1B,SAAKwB,gBAAgB,IAAIvB,cACvB4B,kBAAkBC,oBAAAA;AAEpB,SAAKL,yBAAyB,IAAIrB,uBAAAA;AAClC,SAAKsB,qBAAqB,IAAIvB,mBAAAA;AAC9B,SAAKwB,mBAAmB,IAAI/B,iBAAAA;AAC5B,SAAKuC,iBAAiB,KAAKA,eAAeC,KAAK,IAAI;EACrD;EAEA,OAAcC,cAAc;AAC1B,QAAI,CAACvB,gBAAeiB,UAAU;AAC5B,YAAM,IAAIrB,MAAM,oCAAA;IAClB;AACA,WAAOI,gBAAeiB;EACxB;EAEOO,YAAY;AACjB,WAAO,KAAKhB;EACd;EAEA,aAAoBiB,WAAW;AAC7B,QAAIzB,gBAAeiB,UAAU;AAC3B,YAAMjB,gBAAeiB,SAASI,eAAc;IAC9C;EACF;EAEA,MAAaA,iBAAiB;AAC5B,SAAKb,UAAU;AACf,SAAKkB,SAAQ;AACb,UAAM,KAAKC,aAAY;AACvB,UAAM,KAAKC,YAAW;AACtB,UAAM,KAAKlB,cAAcmB,MAAK;AAC9B7B,oBAAeiB,WAAWa;EAC5B;EAEQC,kBAAkB;AACxB,UAAMC,UACJC,QAAQ/B,IAAIgC,yBAAyB;AACvC,UAAMC,UAAU;AAChB,WAAO,GAAGH,OAAAA,IAAWG,OAAAA,IAAW,KAAKlC,QAAQ,IAAI,KAAKC,GAAG;EAC3D;EAEA,MAAckC,SAASC,KAAaC,SAAc;AAChD,UAAMC,iBAAiB3D,WAAW4D,OAAO;MACvCC,SAAS;MACTC,YAAY;MACZC,SAAS;QAAC;QAAK;QAAK;QAAK;QAAK;QAAK;;IACrC,CAAA;AACA,UAAM9C,WAAW,MAAM0C,eAAe,KAAKR,gBAAe,IAAKM,KAAK;MAClEO,QAAQ;MACRC,MAAMC,KAAKC,UAAUT,OAAAA;MACrBU,SAAS;QAAE,gBAAgB;MAAmB;IAChD,CAAA;AACA,QAAI,CAACnD,SAASoD,IAAI;AAChB,YAAM,IAAItD,UAAUE,QAAAA;IACtB;EACF;EAEOqD,YAAY;AACjB,QAAI,CAAC,KAAK1C,SAAS;AACjB;IACF;AACA,SAAK2C,KAAI;AACT,SAAK9C,iBAAiB+C,YAAY,MAAA;AAChC,WAAKD,KAAI;IACX,GAAG3D,qBAAAA;AACH6D,eAAW,MAAA;AACT,UAAI,KAAKhD,gBAAgB;AACvBiD,sBAAc,KAAKjD,cAAc;AACjC,aAAKA,iBAAiB+C,YAAY,MAAA;AAChC,eAAKD,KAAI;QACX,GAAG5D,aAAAA;MACL;IACF,GAAGE,8BAAAA;EACL;EAEA,MAAc0D,OAAO;AACnB,QAAI;AACF,YAAMI,WAAW;QAAC,KAAK5B,aAAY;QAAI,KAAKC,YAAW;;AACvD,UAAI,CAAC,KAAKrB,iBAAiB;AACzBgD,iBAASC,KAAK,KAAKC,gBAAe,CAAA;MACpC;AACA,YAAMC,QAAQC,IAAIJ,QAAAA;IACpB,SAASrC,OAAO;AACd,WAAKJ,OAAOI,MAAM,yCAAyC;QACzDA;MACF,CAAA;IACF;EACF;EAEQQ,WAAW;AACjB,QAAI,KAAKrB,gBAAgB;AACvBiD,oBAAc,KAAKjD,cAAc;AACjC,WAAKA,iBAAiByB;IACxB;EACF;EAEO8B,eAAeC,MAAmB;AACvC,SAAKvD,cAAcuD;AACnB,SAAKtD,kBAAkB;EACzB;EAEA,MAAckD,kBAAkB;AAC9B,QAAI,KAAKnD,aAAa;AACpB,WAAKQ,OAAOgD,MAAM,sCAAA;AAClB,YAAMxB,UAA0B;QAC9ByB,eAAe,KAAK5D;QACpB6D,cAAcnF,WAAAA;QACd,GAAG,KAAKyB;MACV;AACA,UAAI;AACF,cAAM,KAAK8B,SAAS,WAAWE,OAAAA;AAC/B,aAAK/B,kBAAkB;MACzB,SAASW,OAAO;AACd,cAAM+C,UAAU,KAAKC,eAAehD,KAAAA;AACpC,YAAI,CAAC+C,SAAS;AACZ,eAAKnD,OAAOI,MAAOA,MAAgBiD,OAAO;AAC1C,eAAKrD,OAAOgD,MACV,iEACA;YAAE5C;UAAM,CAAA;QAEZ;MACF;IACF;EACF;EAEA,MAAcS,eAAe;AAC3B,SAAKb,OAAOgD,MAAM,sCAAA;AAClB,UAAMM,aAA0B;MAC9BC,WAAWC,KAAKC,IAAG,IAAK;MACxBR,eAAe,KAAK5D;MACpB6D,cAAcnF,WAAAA;MACd2F,UAAU,KAAK/D,eAAegE,oBAAmB;MACjDC,mBACE,KAAK/D,uBAAuBgE,4BAA2B;MACzDC,eAAe,KAAKhE,mBAAmBiE,wBAAuB;MAC9DC,WAAW,KAAKjE,iBAAiBkE,4BAA2B;MAC5DC,WAAW5F,kBAAAA;IACb;AACA,SAAKgB,cAAcoD,KAAKY,UAAAA;AAExB,QAAIa,IAAI;AACR,WAAO,KAAK7E,cAAc8E,SAAS,GAAG;AACpC,YAAM5C,UAAU,KAAKlC,cAAc+E,MAAK;AACxC,UAAI7C,SAAS;AACX,YAAI;AACF,cAAIgC,KAAKC,IAAG,IAAKjC,QAAQ+B,YAAY,OAAQ3E,gBAAgB;AAC3D,gBAAIuF,IAAI,GAAG;AACT,oBAAM,KAAKG,YAAW;YACxB;AACA,kBAAM,KAAKhD,SAAS,QAAQE,OAAAA;AAC5B2C,iBAAK;UACP;QACF,SAAS/D,OAAO;AACd,gBAAM+C,UAAU,KAAKC,eAAehD,KAAAA;AACpC,cAAI,CAAC+C,SAAS;AACZ,iBAAKnD,OAAOgD,MACV,iEACA;cAAE5C;YAAM,CAAA;AAEV,iBAAKd,cAAcoD,KAAKlB,OAAAA;AACxB;UACF;QACF;MACF;IACF;EACF;EAEA,MAAcV,cAAc;AAC1B,SAAKd,OAAOgD,MAAM,0CAAA;AAClB,UAAM,KAAKpD,cAAc2E,WAAU;AAEnC,UAAM9C,iBAAiB3D,WAAW4D,OAAO;MACvCC,SAAS;MACTC,YAAY;MACZC,SAAS;QAAC;QAAK;QAAK;QAAK;QAAK;QAAK;;IACrC,CAAA;AAEA,QAAIsC,IAAI;AACR,QAAIK;AACJ,WAAQA,UAAU,KAAK5E,cAAc6E,QAAO,GAAK;AAC/C,UAAIN,IAAI,GAAG;AACT,cAAM,KAAKG,YAAW;MACxB;AAEA,UAAI;AACF,cAAMvF,WAAW,MAAM0C,eACrB,GAAG,KAAKR,gBAAe,CAAA,YAAcuD,QAAQE,IAAI,IACjD;UACE5C,QAAQ;UACRC,MAAO,MAAMyC,QAAQG,WAAU;QACjC,CAAA;AAGF,YAAI5F,SAASE,WAAW,OAAOF,SAASmD,QAAQ0C,IAAI,aAAA,GAAgB;AAClE,gBAAMC,aAAaC,SACjB/F,SAASmD,QAAQ6C,IAAI,aAAA,KAAkB,GAAA;AAEzC,cAAIF,aAAa,GAAG;AAClB,iBAAKjF,cAAcoF,eAAexB,KAAKC,IAAG,IAAKoB,aAAa;AAC5D,iBAAKjF,cAAcqF,MAAK;AACxB;UACF;QACF;AAEA,YAAI,CAAClG,SAASoD,IAAI;AAChB,gBAAM,IAAItD,UAAUE,QAAAA;QACtB;AAEAyF,gBAAQU,OAAM;MAChB,SAAS9E,OAAO;AACd,aAAKR,cAAcuF,eAAeX,OAAAA;AAClC;MACF;AAEAL;AACA,UAAIA,KAAK,GAAI;IACf;EACF;EAEQf,eAAehD,OAAgB;AACrC,QAAIA,iBAAiBvB,WAAW;AAC9B,UAAIuB,MAAMrB,SAASE,WAAW,KAAK;AACjC,aAAKe,OAAOI,MAAM,gCAAgC,KAAKjB,QAAQ,GAAG;AAClE,aAAKO,UAAU;AACf,aAAKkB,SAAQ;AACb,eAAO;MACT;AACA,UAAIR,MAAMrB,SAASE,WAAW,KAAK;AACjC,aAAKe,OAAOI,MAAM,6CAAA;AAClB,eAAO;MACT;IACF;AACA,WAAO;EACT;EAEA,MAAckE,cAAc;AAC1B,UAAMc,QAAQ,MAAMC,KAAKC,OAAM,IAAK;AACpC,UAAM,IAAI1C,QAAQ,CAAC2C,YAAYhD,WAAWgD,SAASH,KAAAA,CAAAA;EACrD;AACF;AAxSalG;AAIX,cAJWA,iBAIIiB;AAJV,IAAMjB,iBAAN;","names":["fetchRetry","randomUUID","ConsumerRegistry","getLogger","isValidClientId","isValidEnv","RequestCounter","RequestLogger","getCpuMemoryUsage","ServerErrorCounter","ValidationErrorCounter","SYNC_INTERVAL","INITIAL_SYNC_INTERVAL","INITIAL_SYNC_INTERVAL_DURATION","MAX_QUEUE_TIME","HTTPError","Error","response","reason","status","ApitallyClient","clientId","env","instanceUuid","syncDataQueue","syncIntervalId","startupData","startupDataSent","enabled","requestCounter","requestLogger","validationErrorCounter","serverErrorCounter","consumerRegistry","logger","requestLogging","requestLoggingConfig","instance","error","console","warn","handleShutdown","bind","getInstance","isEnabled","shutdown","stopSync","sendSyncData","sendLogData","close","undefined","getHubUrlPrefix","baseURL","process","APITALLY_HUB_BASE_URL","version","sendData","url","payload","fetchWithRetry","fetch","retries","retryDelay","retryOn","method","body","JSON","stringify","headers","ok","startSync","sync","setInterval","setTimeout","clearInterval","promises","push","sendStartupData","Promise","all","setStartupData","data","debug","instance_uuid","message_uuid","handled","handleHubError","message","newPayload","timestamp","Date","now","requests","getAndResetRequests","validation_errors","getAndResetValidationErrors","server_errors","getAndResetServerErrors","consumers","getAndResetUpdatedConsumers","resources","i","length","shift","randomDelay","rotateFile","logFile","getFile","uuid","getContent","has","retryAfter","parseInt","get","suspendUntil","clear","delete","retryFileLater","delay","Math","random","resolve"]}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
var resources_exports = {};
|
|
21
|
+
__export(resources_exports, {
|
|
22
|
+
getCpuMemoryUsage: () => getCpuMemoryUsage
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(resources_exports);
|
|
25
|
+
let lastCpuUsage = null;
|
|
26
|
+
let lastCpuTime = null;
|
|
27
|
+
function getCpuMemoryUsage() {
|
|
28
|
+
const currentCpuUsage = process.cpuUsage();
|
|
29
|
+
const currentTime = performance.now();
|
|
30
|
+
const memoryRss = process.memoryUsage().rss;
|
|
31
|
+
let cpuPercent = null;
|
|
32
|
+
if (lastCpuUsage !== null && lastCpuTime !== null) {
|
|
33
|
+
const elapsedTime = (currentTime - lastCpuTime) * 1e3;
|
|
34
|
+
const cpuTime = currentCpuUsage.user - lastCpuUsage.user + (currentCpuUsage.system - lastCpuUsage.system);
|
|
35
|
+
cpuPercent = cpuTime / elapsedTime * 100;
|
|
36
|
+
}
|
|
37
|
+
lastCpuUsage = currentCpuUsage;
|
|
38
|
+
lastCpuTime = currentTime;
|
|
39
|
+
return cpuPercent !== null ? {
|
|
40
|
+
cpu_percent: cpuPercent,
|
|
41
|
+
memory_rss: memoryRss
|
|
42
|
+
} : null;
|
|
43
|
+
}
|
|
44
|
+
__name(getCpuMemoryUsage, "getCpuMemoryUsage");
|
|
45
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
46
|
+
0 && (module.exports = {
|
|
47
|
+
getCpuMemoryUsage
|
|
48
|
+
});
|
|
49
|
+
//# sourceMappingURL=resources.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/common/resources.ts"],"sourcesContent":["let lastCpuUsage: { user: number; system: number } | null = null;\nlet lastCpuTime: number | null = null;\n\nexport function getCpuMemoryUsage() {\n const currentCpuUsage = process.cpuUsage();\n const currentTime = performance.now();\n const memoryRss = process.memoryUsage().rss;\n\n let cpuPercent = null;\n\n if (lastCpuUsage !== null && lastCpuTime !== null) {\n // Calculate elapsed time in microseconds\n const elapsedTime = (currentTime - lastCpuTime) * 1000;\n\n // Calculate CPU time used (user + system) in microseconds\n const cpuTime =\n currentCpuUsage.user -\n lastCpuUsage.user +\n (currentCpuUsage.system - lastCpuUsage.system);\n\n // Calculate percentage\n cpuPercent = (cpuTime / elapsedTime) * 100;\n }\n\n // Update last values for next call\n lastCpuUsage = currentCpuUsage;\n lastCpuTime = currentTime;\n\n return cpuPercent !== null\n ? {\n cpu_percent: cpuPercent,\n memory_rss: memoryRss,\n }\n : null;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;;;;;AAAA,IAAIA,eAAwD;AAC5D,IAAIC,cAA6B;AAE1B,SAASC,oBAAAA;AACd,QAAMC,kBAAkBC,QAAQC,SAAQ;AACxC,QAAMC,cAAcC,YAAYC,IAAG;AACnC,QAAMC,YAAYL,QAAQM,YAAW,EAAGC;AAExC,MAAIC,aAAa;AAEjB,MAAIZ,iBAAiB,QAAQC,gBAAgB,MAAM;AAEjD,UAAMY,eAAeP,cAAcL,eAAe;AAGlD,UAAMa,UACJX,gBAAgBY,OAChBf,aAAae,QACZZ,gBAAgBa,SAAShB,aAAagB;AAGzCJ,iBAAcE,UAAUD,cAAe;EACzC;AAGAb,iBAAeG;AACfF,gBAAcK;AAEd,SAAOM,eAAe,OAClB;IACEK,aAAaL;IACbM,YAAYT;EACd,IACA;AACN;AA/BgBP;","names":["lastCpuUsage","lastCpuTime","getCpuMemoryUsage","currentCpuUsage","process","cpuUsage","currentTime","performance","now","memoryRss","memoryUsage","rss","cpuPercent","elapsedTime","cpuTime","user","system","cpu_percent","memory_rss"]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
+
let lastCpuUsage = null;
|
|
4
|
+
let lastCpuTime = null;
|
|
5
|
+
function getCpuMemoryUsage() {
|
|
6
|
+
const currentCpuUsage = process.cpuUsage();
|
|
7
|
+
const currentTime = performance.now();
|
|
8
|
+
const memoryRss = process.memoryUsage().rss;
|
|
9
|
+
let cpuPercent = null;
|
|
10
|
+
if (lastCpuUsage !== null && lastCpuTime !== null) {
|
|
11
|
+
const elapsedTime = (currentTime - lastCpuTime) * 1e3;
|
|
12
|
+
const cpuTime = currentCpuUsage.user - lastCpuUsage.user + (currentCpuUsage.system - lastCpuUsage.system);
|
|
13
|
+
cpuPercent = cpuTime / elapsedTime * 100;
|
|
14
|
+
}
|
|
15
|
+
lastCpuUsage = currentCpuUsage;
|
|
16
|
+
lastCpuTime = currentTime;
|
|
17
|
+
return cpuPercent !== null ? {
|
|
18
|
+
cpu_percent: cpuPercent,
|
|
19
|
+
memory_rss: memoryRss
|
|
20
|
+
} : null;
|
|
21
|
+
}
|
|
22
|
+
__name(getCpuMemoryUsage, "getCpuMemoryUsage");
|
|
23
|
+
export {
|
|
24
|
+
getCpuMemoryUsage
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=resources.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/common/resources.ts"],"sourcesContent":["let lastCpuUsage: { user: number; system: number } | null = null;\nlet lastCpuTime: number | null = null;\n\nexport function getCpuMemoryUsage() {\n const currentCpuUsage = process.cpuUsage();\n const currentTime = performance.now();\n const memoryRss = process.memoryUsage().rss;\n\n let cpuPercent = null;\n\n if (lastCpuUsage !== null && lastCpuTime !== null) {\n // Calculate elapsed time in microseconds\n const elapsedTime = (currentTime - lastCpuTime) * 1000;\n\n // Calculate CPU time used (user + system) in microseconds\n const cpuTime =\n currentCpuUsage.user -\n lastCpuUsage.user +\n (currentCpuUsage.system - lastCpuUsage.system);\n\n // Calculate percentage\n cpuPercent = (cpuTime / elapsedTime) * 100;\n }\n\n // Update last values for next call\n lastCpuUsage = currentCpuUsage;\n lastCpuTime = currentTime;\n\n return cpuPercent !== null\n ? {\n cpu_percent: cpuPercent,\n memory_rss: memoryRss,\n }\n : null;\n}\n"],"mappings":";;AAAA,IAAIA,eAAwD;AAC5D,IAAIC,cAA6B;AAE1B,SAASC,oBAAAA;AACd,QAAMC,kBAAkBC,QAAQC,SAAQ;AACxC,QAAMC,cAAcC,YAAYC,IAAG;AACnC,QAAMC,YAAYL,QAAQM,YAAW,EAAGC;AAExC,MAAIC,aAAa;AAEjB,MAAIZ,iBAAiB,QAAQC,gBAAgB,MAAM;AAEjD,UAAMY,eAAeP,cAAcL,eAAe;AAGlD,UAAMa,UACJX,gBAAgBY,OAChBf,aAAae,QACZZ,gBAAgBa,SAAShB,aAAagB;AAGzCJ,iBAAcE,UAAUD,cAAe;EACzC;AAGAb,iBAAeG;AACfF,gBAAcK;AAEd,SAAOM,eAAe,OAClB;IACEK,aAAaL;IACbM,YAAYT;EACd,IACA;AACN;AA/BgBP;","names":["lastCpuUsage","lastCpuTime","getCpuMemoryUsage","currentCpuUsage","process","cpuUsage","currentTime","performance","now","memoryRss","memoryUsage","rss","cpuPercent","elapsedTime","cpuTime","user","system","cpu_percent","memory_rss"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/common/types.ts"],"sourcesContent":["import { Logger } from \"./logging.js\";\nimport { RequestLoggingConfig } from \"./requestLogger.js\";\n\nexport type ApitallyConfig = {\n clientId: string;\n env?: string;\n requestLogging?: Partial<RequestLoggingConfig>;\n appVersion?: string;\n logger?: Logger;\n\n /** @deprecated Use requestLogging instead */\n requestLoggingConfig?: Partial<RequestLoggingConfig>;\n};\n\nexport type ApitallyConsumer = {\n identifier: string;\n name?: string | null;\n group?: string | null;\n};\n\nexport type PathInfo = {\n method: string;\n path: string;\n};\n\nexport type StartupData = {\n paths: PathInfo[];\n versions: Record<string, string>;\n client: string;\n};\n\nexport type StartupPayload = {\n instance_uuid: string;\n message_uuid: string;\n} & StartupData;\n\nexport type ConsumerMethodPath = {\n consumer?: string | null;\n method: string;\n path: string;\n};\n\nexport type RequestInfo = ConsumerMethodPath & {\n statusCode: number;\n responseTime: number;\n requestSize?: string | number | null;\n responseSize?: string | number | null;\n};\n\nexport type RequestsItem = ConsumerMethodPath & {\n status_code: number;\n request_count: number;\n request_size_sum: number;\n response_size_sum: number;\n response_times: Record<number, number>;\n request_sizes: Record<number, number>;\n response_sizes: Record<number, number>;\n};\n\nexport type ValidationError = {\n loc: string;\n msg: string;\n type: string;\n};\n\nexport type ValidationErrorsItem = ConsumerMethodPath & {\n loc: Array<string>;\n msg: string;\n type: string;\n error_count: number;\n};\n\nexport type ServerError = {\n type: string;\n msg: string;\n traceback: string;\n};\n\nexport type ServerErrorsItem = ConsumerMethodPath & {\n type: string;\n msg: string;\n traceback: string;\n sentry_event_id: string | null;\n error_count: number;\n};\n\nexport type ConsumerItem = ApitallyConsumer;\n\nexport type SyncPayload = {\n timestamp: number;\n instance_uuid: string;\n message_uuid: string;\n requests: Array<RequestsItem>;\n validation_errors: Array<ValidationErrorsItem>;\n server_errors: Array<ServerErrorsItem>;\n consumers: Array<ConsumerItem>;\n};\n"],"mappings":";;;;;;;;;;;;;;AAwFA;;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/common/types.ts"],"sourcesContent":["import { Logger } from \"./logging.js\";\nimport { RequestLoggingConfig } from \"./requestLogger.js\";\n\nexport type ApitallyConfig = {\n clientId: string;\n env?: string;\n requestLogging?: Partial<RequestLoggingConfig>;\n appVersion?: string;\n logger?: Logger;\n\n /** @deprecated Use requestLogging instead */\n requestLoggingConfig?: Partial<RequestLoggingConfig>;\n};\n\nexport type ApitallyConsumer = {\n identifier: string;\n name?: string | null;\n group?: string | null;\n};\n\nexport type PathInfo = {\n method: string;\n path: string;\n};\n\nexport type StartupData = {\n paths: PathInfo[];\n versions: Record<string, string>;\n client: string;\n};\n\nexport type StartupPayload = {\n instance_uuid: string;\n message_uuid: string;\n} & StartupData;\n\nexport type ConsumerMethodPath = {\n consumer?: string | null;\n method: string;\n path: string;\n};\n\nexport type RequestInfo = ConsumerMethodPath & {\n statusCode: number;\n responseTime: number;\n requestSize?: string | number | null;\n responseSize?: string | number | null;\n};\n\nexport type RequestsItem = ConsumerMethodPath & {\n status_code: number;\n request_count: number;\n request_size_sum: number;\n response_size_sum: number;\n response_times: Record<number, number>;\n request_sizes: Record<number, number>;\n response_sizes: Record<number, number>;\n};\n\nexport type ValidationError = {\n loc: string;\n msg: string;\n type: string;\n};\n\nexport type ValidationErrorsItem = ConsumerMethodPath & {\n loc: Array<string>;\n msg: string;\n type: string;\n error_count: number;\n};\n\nexport type ServerError = {\n type: string;\n msg: string;\n traceback: string;\n};\n\nexport type ServerErrorsItem = ConsumerMethodPath & {\n type: string;\n msg: string;\n traceback: string;\n sentry_event_id: string | null;\n error_count: number;\n};\n\nexport type ConsumerItem = ApitallyConsumer;\n\nexport type SyncPayload = {\n timestamp: number;\n instance_uuid: string;\n message_uuid: string;\n requests: Array<RequestsItem>;\n validation_errors: Array<ValidationErrorsItem>;\n server_errors: Array<ServerErrorsItem>;\n consumers: Array<ConsumerItem>;\n resources: {\n cpu_percent: number;\n memory_rss: number;\n } | null;\n};\n"],"mappings":";;;;;;;;;;;;;;AAwFA;;","names":[]}
|
package/dist/common/types.d.cts
CHANGED
|
@@ -84,6 +84,10 @@ type SyncPayload = {
|
|
|
84
84
|
validation_errors: Array<ValidationErrorsItem>;
|
|
85
85
|
server_errors: Array<ServerErrorsItem>;
|
|
86
86
|
consumers: Array<ConsumerItem>;
|
|
87
|
+
resources: {
|
|
88
|
+
cpu_percent: number;
|
|
89
|
+
memory_rss: number;
|
|
90
|
+
} | null;
|
|
87
91
|
};
|
|
88
92
|
|
|
89
93
|
export type { ApitallyConfig, ApitallyConsumer, ConsumerItem, ConsumerMethodPath, PathInfo, RequestInfo, RequestsItem, ServerError, ServerErrorsItem, StartupData, StartupPayload, SyncPayload, ValidationError, ValidationErrorsItem };
|
package/dist/common/types.d.ts
CHANGED
|
@@ -84,6 +84,10 @@ type SyncPayload = {
|
|
|
84
84
|
validation_errors: Array<ValidationErrorsItem>;
|
|
85
85
|
server_errors: Array<ServerErrorsItem>;
|
|
86
86
|
consumers: Array<ConsumerItem>;
|
|
87
|
+
resources: {
|
|
88
|
+
cpu_percent: number;
|
|
89
|
+
memory_rss: number;
|
|
90
|
+
} | null;
|
|
87
91
|
};
|
|
88
92
|
|
|
89
93
|
export type { ApitallyConfig, ApitallyConsumer, ConsumerItem, ConsumerMethodPath, PathInfo, RequestInfo, RequestsItem, ServerError, ServerErrorsItem, StartupData, StartupPayload, SyncPayload, ValidationError, ValidationErrorsItem };
|
package/dist/elysia/plugin.cjs
CHANGED
|
@@ -38,6 +38,7 @@ const RESPONSE_SYMBOL = Symbol("apitally.response");
|
|
|
38
38
|
const RESPONSE_PROMISE_SYMBOL = Symbol("apitally.responsePromise");
|
|
39
39
|
const ERROR_SYMBOL = Symbol("apitally.error");
|
|
40
40
|
const CLIENT_SYMBOL = Symbol("apitally.client");
|
|
41
|
+
const REQUEST_SYMBOL = Symbol("apitally.request");
|
|
41
42
|
function apitallyPlugin(config) {
|
|
42
43
|
const client = new import_client.ApitallyClient(config);
|
|
43
44
|
const logsContext = new import_node_async_hooks.AsyncLocalStorage();
|
|
@@ -51,7 +52,8 @@ function apitallyPlugin(config) {
|
|
|
51
52
|
const originalMapResponse = handler.mapResponse;
|
|
52
53
|
const originalMapCompactResponse = handler.mapCompactResponse;
|
|
53
54
|
const originalMapEarlyResponse = handler.mapEarlyResponse;
|
|
54
|
-
const captureMappedResponse = /* @__PURE__ */ __name((originalResponse, mappedResponse,
|
|
55
|
+
const captureMappedResponse = /* @__PURE__ */ __name((originalResponse, mappedResponse, set) => {
|
|
56
|
+
const request = set == null ? void 0 : set[REQUEST_SYMBOL];
|
|
55
57
|
if (request instanceof Request && mappedResponse instanceof Response && !(RESPONSE_SYMBOL in request) && CLIENT_SYMBOL in request) {
|
|
56
58
|
if (typeof originalResponse === "string") {
|
|
57
59
|
const responseBody = Buffer.from(originalResponse);
|
|
@@ -74,19 +76,19 @@ function apitallyPlugin(config) {
|
|
|
74
76
|
}
|
|
75
77
|
return mappedResponse;
|
|
76
78
|
}, "captureMappedResponse");
|
|
77
|
-
handler.mapResponse = /* @__PURE__ */ __name(function wrappedMapResponse(response, set
|
|
78
|
-
const mappedResponse = originalMapResponse(response, set
|
|
79
|
-
const newResponse = captureMappedResponse(response, mappedResponse,
|
|
79
|
+
handler.mapResponse = /* @__PURE__ */ __name(function wrappedMapResponse(response, set) {
|
|
80
|
+
const mappedResponse = originalMapResponse(response, set);
|
|
81
|
+
const newResponse = captureMappedResponse(response, mappedResponse, set);
|
|
80
82
|
return newResponse;
|
|
81
83
|
}, "wrappedMapResponse");
|
|
82
|
-
handler.mapCompactResponse = /* @__PURE__ */ __name(function wrappedMapCompactResponse(response
|
|
83
|
-
const mappedResponse = originalMapCompactResponse(response
|
|
84
|
-
const newResponse = captureMappedResponse(response, mappedResponse,
|
|
84
|
+
handler.mapCompactResponse = /* @__PURE__ */ __name(function wrappedMapCompactResponse(response) {
|
|
85
|
+
const mappedResponse = originalMapCompactResponse(response);
|
|
86
|
+
const newResponse = captureMappedResponse(response, mappedResponse, void 0);
|
|
85
87
|
return newResponse;
|
|
86
88
|
}, "wrappedMapCompactResponse");
|
|
87
|
-
handler.mapEarlyResponse = /* @__PURE__ */ __name(function wrappedMapEarlyResponse(response, set
|
|
88
|
-
const mappedResponse = originalMapEarlyResponse(response, set
|
|
89
|
-
const newResponse = captureMappedResponse(response, mappedResponse,
|
|
89
|
+
handler.mapEarlyResponse = /* @__PURE__ */ __name(function wrappedMapEarlyResponse(response, set) {
|
|
90
|
+
const mappedResponse = originalMapEarlyResponse(response, set);
|
|
91
|
+
const newResponse = captureMappedResponse(response, mappedResponse, set);
|
|
90
92
|
return newResponse;
|
|
91
93
|
}, "wrappedMapEarlyResponse");
|
|
92
94
|
}
|
|
@@ -96,12 +98,13 @@ function apitallyPlugin(config) {
|
|
|
96
98
|
client.startSync();
|
|
97
99
|
}).onStop(async () => {
|
|
98
100
|
await client.handleShutdown();
|
|
99
|
-
}).onRequest(async ({ request }) => {
|
|
101
|
+
}).onRequest(async ({ request, set }) => {
|
|
100
102
|
if (!client.isEnabled() || request.method.toUpperCase() === "OPTIONS") {
|
|
101
103
|
return;
|
|
102
104
|
}
|
|
103
105
|
request[CLIENT_SYMBOL] = client;
|
|
104
106
|
request[START_TIME_SYMBOL] = import_node_perf_hooks.performance.now();
|
|
107
|
+
set[REQUEST_SYMBOL] = request;
|
|
105
108
|
logsContext.enterWith([]);
|
|
106
109
|
if (client.requestLogger.enabled && client.requestLogger.config.logRequestBody) {
|
|
107
110
|
const contentType = request.headers.get("content-type");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/elysia/plugin.ts"],"sourcesContent":["import { Context, Elysia, StatusMap, ValidationError } from \"elysia\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { performance } from \"node:perf_hooks\";\n\nimport { ApitallyClient } from \"../common/client.js\";\nimport { consumerFromStringOrObject } from \"../common/consumerRegistry.js\";\nimport { parseContentLength } from \"../common/headers.js\";\nimport type { LogRecord } from \"../common/requestLogger.js\";\nimport { convertHeaders } from \"../common/requestLogger.js\";\nimport { CapturedResponse, captureResponse } from \"../common/response.js\";\nimport { ApitallyConfig, ApitallyConsumer } from \"../common/types.js\";\nimport { patchConsole, patchWinston } from \"../loggers/index.js\";\nimport { getAppInfo } from \"./utils.js\";\n\nconst START_TIME_SYMBOL = Symbol(\"apitally.startTime\");\nconst REQUEST_BODY_SYMBOL = Symbol(\"apitally.requestBody\");\nconst RESPONSE_SYMBOL = Symbol(\"apitally.response\");\nconst RESPONSE_PROMISE_SYMBOL = Symbol(\"apitally.responsePromise\");\nconst ERROR_SYMBOL = Symbol(\"apitally.error\");\nconst CLIENT_SYMBOL = Symbol(\"apitally.client\");\n\ndeclare global {\n interface Request {\n [START_TIME_SYMBOL]?: number;\n [REQUEST_BODY_SYMBOL]?: Buffer;\n [RESPONSE_SYMBOL]?: Response;\n [RESPONSE_PROMISE_SYMBOL]?: Promise<CapturedResponse>;\n [ERROR_SYMBOL]?: Readonly<Error>;\n [CLIENT_SYMBOL]?: ApitallyClient;\n }\n}\n\ninterface ApitallyContext {\n consumer?: ApitallyConsumer | string;\n}\n\nexport default function apitallyPlugin(config: ApitallyConfig) {\n const client = new ApitallyClient(config);\n const logsContext = new AsyncLocalStorage<LogRecord[]>();\n\n if (client.requestLogger.enabled && client.requestLogger.config.captureLogs) {\n patchConsole(logsContext);\n patchWinston(logsContext);\n }\n\n return (app: Elysia) => {\n const handler = app[\"~adapter\"].handler;\n\n if (!handler.mapResponse.name.startsWith(\"wrapped\")) {\n const originalMapResponse = handler.mapResponse;\n const originalMapCompactResponse = handler.mapCompactResponse;\n const originalMapEarlyResponse = handler.mapEarlyResponse;\n\n const captureMappedResponse = (\n originalResponse: unknown,\n mappedResponse: unknown,\n request?: Request,\n ) => {\n if (\n request instanceof Request &&\n mappedResponse instanceof Response &&\n !(RESPONSE_SYMBOL in request) &&\n CLIENT_SYMBOL in request\n ) {\n if (typeof originalResponse === \"string\") {\n // Preserve the response body value as Blob if the original response is a string,\n // so that Bun adds a Content-Type header.\n const responseBody = Buffer.from(originalResponse as string);\n request[RESPONSE_SYMBOL] = mappedResponse;\n request[RESPONSE_PROMISE_SYMBOL] = Promise.resolve({\n body: responseBody,\n size: responseBody.length,\n completed: true,\n });\n } else {\n // Otherwise capture the response using streaming\n const client = request[CLIENT_SYMBOL]!;\n const [newResponse, responsePromise] = captureResponse(\n mappedResponse,\n {\n captureBody:\n client.requestLogger.enabled &&\n client.requestLogger.config.logResponseBody,\n maxBodySize: client.requestLogger.maxBodySize,\n },\n );\n request[RESPONSE_SYMBOL] = newResponse;\n request[RESPONSE_PROMISE_SYMBOL] = responsePromise;\n return newResponse;\n }\n }\n return mappedResponse;\n };\n\n handler.mapResponse = function wrappedMapResponse(\n response: unknown,\n set: Context[\"set\"],\n request?: Request,\n ) {\n const mappedResponse = originalMapResponse(response, set, request);\n const newResponse = captureMappedResponse(\n response,\n mappedResponse,\n request,\n );\n return newResponse;\n };\n handler.mapCompactResponse = function wrappedMapCompactResponse(\n response: unknown,\n request?: Request,\n ) {\n const mappedResponse = originalMapCompactResponse(response, request);\n const newResponse = captureMappedResponse(\n response,\n mappedResponse,\n request,\n );\n return newResponse;\n };\n handler.mapEarlyResponse = function wrappedMapEarlyResponse(\n response: unknown,\n set: Context[\"set\"],\n request?: Request,\n ) {\n const mappedResponse = originalMapEarlyResponse(response, set, request);\n const newResponse = captureMappedResponse(\n response,\n mappedResponse,\n request,\n );\n return newResponse;\n };\n }\n\n return app\n .decorate(\"apitally\", {} as ApitallyContext)\n .onStart(() => {\n const appInfo = getAppInfo(app, config.appVersion);\n client.setStartupData(appInfo);\n client.startSync();\n })\n .onStop(async () => {\n await client.handleShutdown();\n })\n .onRequest(async ({ request }) => {\n if (!client.isEnabled() || request.method.toUpperCase() === \"OPTIONS\") {\n return;\n }\n\n request[CLIENT_SYMBOL] = client;\n request[START_TIME_SYMBOL] = performance.now();\n logsContext.enterWith([]);\n\n // Capture request body\n if (\n client.requestLogger.enabled &&\n client.requestLogger.config.logRequestBody\n ) {\n const contentType = request.headers.get(\"content-type\");\n const requestSize =\n parseContentLength(request.headers.get(\"content-length\")) ?? 0;\n\n if (\n client.requestLogger.isSupportedContentType(contentType) &&\n requestSize <= client.requestLogger.maxBodySize\n ) {\n try {\n request[REQUEST_BODY_SYMBOL] = Buffer.from(\n await request.clone().arrayBuffer(),\n );\n } catch (error) {\n // ignore\n }\n }\n }\n })\n .onAfterResponse(async ({ request, set, route, apitally }) => {\n if (!client.isEnabled() || request.method.toUpperCase() === \"OPTIONS\") {\n return;\n }\n\n const startTime = request[START_TIME_SYMBOL];\n const responseTime = startTime ? performance.now() - startTime : 0;\n\n const requestBody = request[REQUEST_BODY_SYMBOL];\n const requestSize =\n parseContentLength(request.headers.get(\"content-length\")) ??\n requestBody?.length;\n\n let responsePromise = request[RESPONSE_PROMISE_SYMBOL];\n let response = request[RESPONSE_SYMBOL];\n const error = request[ERROR_SYMBOL];\n\n if (\n !response &&\n error &&\n \"toResponse\" in error &&\n typeof error.toResponse === \"function\"\n ) {\n // Convert error to response\n try {\n response = error.toResponse() as Response;\n const errorResponseBody = Buffer.from(await response.arrayBuffer());\n responsePromise = Promise.resolve({\n body: errorResponseBody,\n size: errorResponseBody.length,\n completed: true,\n });\n } catch (error) {\n // ignore\n }\n }\n\n const statusCode = response?.status ?? getStatusCode(set) ?? 200;\n\n if (!response) {\n // Create empty fake response for errors without the toResponse method\n response = new Response(null, {\n status: statusCode,\n statusText: \"\",\n headers: new Headers(),\n });\n responsePromise = Promise.resolve({\n body: undefined,\n size: 0,\n completed: true,\n });\n }\n\n const consumer = apitally.consumer\n ? consumerFromStringOrObject(apitally.consumer)\n : null;\n client.consumerRegistry.addOrUpdateConsumer(consumer);\n\n // Log request when response has been fully captured\n responsePromise?.then(async (capturedResponse) => {\n const responseHeaders = response?.headers ?? set.headers;\n const responseSize = capturedResponse.completed\n ? capturedResponse.size\n : undefined;\n\n client.requestCounter.addRequest({\n consumer: consumer?.identifier,\n method: request.method,\n path: route,\n statusCode,\n responseTime,\n requestSize,\n responseSize,\n });\n\n if (client.requestLogger.enabled) {\n const logs = logsContext.getStore();\n client.requestLogger.logRequest(\n {\n timestamp: (Date.now() - responseTime) / 1000,\n method: request.method,\n path: route,\n url: request.url,\n headers: convertHeaders(\n Object.fromEntries(request.headers.entries()),\n ),\n size: requestSize,\n consumer: consumer?.identifier,\n body: requestBody,\n },\n {\n statusCode,\n responseTime: responseTime / 1000,\n headers: convertHeaders(responseHeaders),\n size: responseSize,\n body: capturedResponse.body,\n },\n error,\n logs,\n );\n }\n });\n\n // Handle validation errors\n if (\n (statusCode === 400 || statusCode === 422) &&\n error instanceof ValidationError\n ) {\n try {\n const parsedMessage = JSON.parse(error.message);\n client.validationErrorCounter.addValidationError({\n consumer: consumer?.identifier,\n method: request.method,\n path: route,\n loc:\n (parsedMessage.on ?? \"\") + \".\" + (parsedMessage.property ?? \"\"),\n msg: parsedMessage.message,\n type: \"\",\n });\n } catch (error) {\n // ignore\n }\n }\n\n // Handle server errors\n if (statusCode === 500 && error) {\n client.serverErrorCounter.addServerError({\n consumer: consumer?.identifier,\n method: request.method,\n path: route,\n type: error.name,\n msg: error.message,\n traceback: error.stack || \"\",\n });\n }\n })\n .onError(({ request, error }) => {\n if (client.isEnabled() && error instanceof Error) {\n request[ERROR_SYMBOL] = error;\n }\n });\n };\n}\n\nfunction getStatusCode(set: Context[\"set\"]) {\n if (typeof set.status === \"number\") {\n return set.status;\n } else if (typeof set.status === \"string\") {\n return StatusMap[set.status];\n }\n}\n"],"mappings":"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","names":["START_TIME_SYMBOL","Symbol","REQUEST_BODY_SYMBOL","RESPONSE_SYMBOL","RESPONSE_PROMISE_SYMBOL","ERROR_SYMBOL","CLIENT_SYMBOL","config","client","ApitallyClient","logsContext","AsyncLocalStorage","requestLogger","enabled","captureLogs","patchConsole","patchWinston","app","handler","mapResponse","name","startsWith","originalMapResponse","originalMapCompactResponse","mapCompactResponse","originalMapEarlyResponse","mapEarlyResponse","captureMappedResponse","originalResponse","mappedResponse","request","Request","Response","responseBody","Buffer","from","Promise","resolve","body","size","length","completed","newResponse","responsePromise","captureResponse","captureBody","logResponseBody","maxBodySize","wrappedMapResponse","response","set","wrappedMapCompactResponse","wrappedMapEarlyResponse","decorate","onStart","appInfo","getAppInfo","appVersion","setStartupData","startSync","onStop","handleShutdown","onRequest","isEnabled","method","toUpperCase","performance","now","enterWith","logRequestBody","contentType","headers","get","requestSize","parseContentLength","isSupportedContentType","clone","arrayBuffer","error","onAfterResponse","route","apitally","startTime","responseTime","requestBody","toResponse","errorResponseBody","statusCode","status","getStatusCode","statusText","Headers","undefined","consumer","consumerFromStringOrObject","consumerRegistry","addOrUpdateConsumer","then","capturedResponse","responseHeaders","responseSize","requestCounter","addRequest","identifier","path","logs","getStore","logRequest","timestamp","Date","url","convertHeaders","Object","fromEntries","entries","ValidationError","parsedMessage","JSON","parse","message","validationErrorCounter","addValidationError","loc","on","property","msg","type","serverErrorCounter","addServerError","traceback","stack","onError","Error","apitallyPlugin","StatusMap"]}
|
|
1
|
+
{"version":3,"sources":["../../src/elysia/plugin.ts"],"sourcesContent":["import { Context, Elysia, StatusMap, ValidationError } from \"elysia\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { performance } from \"node:perf_hooks\";\n\nimport { ApitallyClient } from \"../common/client.js\";\nimport { consumerFromStringOrObject } from \"../common/consumerRegistry.js\";\nimport { parseContentLength } from \"../common/headers.js\";\nimport type { LogRecord } from \"../common/requestLogger.js\";\nimport { convertHeaders } from \"../common/requestLogger.js\";\nimport { CapturedResponse, captureResponse } from \"../common/response.js\";\nimport { ApitallyConfig, ApitallyConsumer } from \"../common/types.js\";\nimport { patchConsole, patchWinston } from \"../loggers/index.js\";\nimport { getAppInfo } from \"./utils.js\";\n\nconst START_TIME_SYMBOL = Symbol(\"apitally.startTime\");\nconst REQUEST_BODY_SYMBOL = Symbol(\"apitally.requestBody\");\nconst RESPONSE_SYMBOL = Symbol(\"apitally.response\");\nconst RESPONSE_PROMISE_SYMBOL = Symbol(\"apitally.responsePromise\");\nconst ERROR_SYMBOL = Symbol(\"apitally.error\");\nconst CLIENT_SYMBOL = Symbol(\"apitally.client\");\nconst REQUEST_SYMBOL = Symbol(\"apitally.request\");\n\ndeclare global {\n interface Request {\n [START_TIME_SYMBOL]?: number;\n [REQUEST_BODY_SYMBOL]?: Buffer;\n [RESPONSE_SYMBOL]?: Response;\n [RESPONSE_PROMISE_SYMBOL]?: Promise<CapturedResponse>;\n [ERROR_SYMBOL]?: Readonly<Error>;\n [CLIENT_SYMBOL]?: ApitallyClient;\n }\n}\n\ntype ContextSet = Context[\"set\"] & {\n [REQUEST_SYMBOL]?: Request;\n};\n\ninterface ApitallyContext {\n consumer?: ApitallyConsumer | string;\n}\n\nexport default function apitallyPlugin(config: ApitallyConfig) {\n const client = new ApitallyClient(config);\n const logsContext = new AsyncLocalStorage<LogRecord[]>();\n\n if (client.requestLogger.enabled && client.requestLogger.config.captureLogs) {\n patchConsole(logsContext);\n patchWinston(logsContext);\n }\n\n return (app: Elysia) => {\n const handler = app[\"~adapter\"].handler;\n\n if (!handler.mapResponse.name.startsWith(\"wrapped\")) {\n const originalMapResponse = handler.mapResponse;\n const originalMapCompactResponse = handler.mapCompactResponse;\n const originalMapEarlyResponse = handler.mapEarlyResponse;\n\n const captureMappedResponse = (\n originalResponse: unknown,\n mappedResponse: unknown,\n set?: ContextSet,\n ) => {\n const request = set?.[REQUEST_SYMBOL];\n if (\n request instanceof Request &&\n mappedResponse instanceof Response &&\n !(RESPONSE_SYMBOL in request) &&\n CLIENT_SYMBOL in request\n ) {\n if (typeof originalResponse === \"string\") {\n // Preserve the response body value as Blob if the original response is a string,\n // so that Bun adds a Content-Type header.\n const responseBody = Buffer.from(originalResponse as string);\n request[RESPONSE_SYMBOL] = mappedResponse;\n request[RESPONSE_PROMISE_SYMBOL] = Promise.resolve({\n body: responseBody,\n size: responseBody.length,\n completed: true,\n });\n } else {\n // Otherwise capture the response using streaming\n const client = request[CLIENT_SYMBOL]!;\n const [newResponse, responsePromise] = captureResponse(\n mappedResponse,\n {\n captureBody:\n client.requestLogger.enabled &&\n client.requestLogger.config.logResponseBody,\n maxBodySize: client.requestLogger.maxBodySize,\n },\n );\n request[RESPONSE_SYMBOL] = newResponse;\n request[RESPONSE_PROMISE_SYMBOL] = responsePromise;\n return newResponse;\n }\n }\n return mappedResponse;\n };\n\n handler.mapResponse = function wrappedMapResponse(\n response: unknown,\n set: ContextSet,\n ) {\n const mappedResponse = originalMapResponse(response, set);\n const newResponse = captureMappedResponse(\n response,\n mappedResponse,\n set,\n );\n return newResponse;\n };\n handler.mapCompactResponse = function wrappedMapCompactResponse(\n response: unknown,\n ) {\n const mappedResponse = originalMapCompactResponse(response);\n const newResponse = captureMappedResponse(\n response,\n mappedResponse,\n undefined,\n );\n return newResponse;\n };\n handler.mapEarlyResponse = function wrappedMapEarlyResponse(\n response: unknown,\n set: ContextSet,\n ) {\n const mappedResponse = originalMapEarlyResponse(response, set);\n const newResponse = captureMappedResponse(\n response,\n mappedResponse,\n set,\n );\n return newResponse;\n };\n }\n\n return app\n .decorate(\"apitally\", {} as ApitallyContext)\n .onStart(() => {\n const appInfo = getAppInfo(app, config.appVersion);\n client.setStartupData(appInfo);\n client.startSync();\n })\n .onStop(async () => {\n await client.handleShutdown();\n })\n .onRequest(async ({ request, set }) => {\n if (!client.isEnabled() || request.method.toUpperCase() === \"OPTIONS\") {\n return;\n }\n\n request[CLIENT_SYMBOL] = client;\n request[START_TIME_SYMBOL] = performance.now();\n (set as ContextSet)[REQUEST_SYMBOL] = request;\n logsContext.enterWith([]);\n\n // Capture request body\n if (\n client.requestLogger.enabled &&\n client.requestLogger.config.logRequestBody\n ) {\n const contentType = request.headers.get(\"content-type\");\n const requestSize =\n parseContentLength(request.headers.get(\"content-length\")) ?? 0;\n\n if (\n client.requestLogger.isSupportedContentType(contentType) &&\n requestSize <= client.requestLogger.maxBodySize\n ) {\n try {\n request[REQUEST_BODY_SYMBOL] = Buffer.from(\n await request.clone().arrayBuffer(),\n );\n } catch (error) {\n // ignore\n }\n }\n }\n })\n .onAfterResponse(async ({ request, set, route, apitally }) => {\n if (!client.isEnabled() || request.method.toUpperCase() === \"OPTIONS\") {\n return;\n }\n\n const startTime = request[START_TIME_SYMBOL];\n const responseTime = startTime ? performance.now() - startTime : 0;\n\n const requestBody = request[REQUEST_BODY_SYMBOL];\n const requestSize =\n parseContentLength(request.headers.get(\"content-length\")) ??\n requestBody?.length;\n\n let responsePromise = request[RESPONSE_PROMISE_SYMBOL];\n let response = request[RESPONSE_SYMBOL];\n const error = request[ERROR_SYMBOL];\n\n if (\n !response &&\n error &&\n \"toResponse\" in error &&\n typeof error.toResponse === \"function\"\n ) {\n // Convert error to response\n try {\n response = error.toResponse() as Response;\n const errorResponseBody = Buffer.from(await response.arrayBuffer());\n responsePromise = Promise.resolve({\n body: errorResponseBody,\n size: errorResponseBody.length,\n completed: true,\n });\n } catch (error) {\n // ignore\n }\n }\n\n const statusCode = response?.status ?? getStatusCode(set) ?? 200;\n\n if (!response) {\n // Create empty fake response for errors without the toResponse method\n response = new Response(null, {\n status: statusCode,\n statusText: \"\",\n headers: new Headers(),\n });\n responsePromise = Promise.resolve({\n body: undefined,\n size: 0,\n completed: true,\n });\n }\n\n const consumer = apitally.consumer\n ? consumerFromStringOrObject(apitally.consumer)\n : null;\n client.consumerRegistry.addOrUpdateConsumer(consumer);\n\n // Log request when response has been fully captured\n responsePromise?.then(async (capturedResponse) => {\n const responseHeaders = response?.headers ?? set.headers;\n const responseSize = capturedResponse.completed\n ? capturedResponse.size\n : undefined;\n\n client.requestCounter.addRequest({\n consumer: consumer?.identifier,\n method: request.method,\n path: route,\n statusCode,\n responseTime,\n requestSize,\n responseSize,\n });\n\n if (client.requestLogger.enabled) {\n const logs = logsContext.getStore();\n client.requestLogger.logRequest(\n {\n timestamp: (Date.now() - responseTime) / 1000,\n method: request.method,\n path: route,\n url: request.url,\n headers: convertHeaders(\n Object.fromEntries(request.headers.entries()),\n ),\n size: requestSize,\n consumer: consumer?.identifier,\n body: requestBody,\n },\n {\n statusCode,\n responseTime: responseTime / 1000,\n headers: convertHeaders(responseHeaders),\n size: responseSize,\n body: capturedResponse.body,\n },\n error,\n logs,\n );\n }\n });\n\n // Handle validation errors\n if (\n (statusCode === 400 || statusCode === 422) &&\n error instanceof ValidationError\n ) {\n try {\n const parsedMessage = JSON.parse(error.message);\n client.validationErrorCounter.addValidationError({\n consumer: consumer?.identifier,\n method: request.method,\n path: route,\n loc:\n (parsedMessage.on ?? \"\") + \".\" + (parsedMessage.property ?? \"\"),\n msg: parsedMessage.message,\n type: \"\",\n });\n } catch (error) {\n // ignore\n }\n }\n\n // Handle server errors\n if (statusCode === 500 && error) {\n client.serverErrorCounter.addServerError({\n consumer: consumer?.identifier,\n method: request.method,\n path: route,\n type: error.name,\n msg: error.message,\n traceback: error.stack || \"\",\n });\n }\n })\n .onError(({ request, error }) => {\n if (client.isEnabled() && error instanceof Error) {\n request[ERROR_SYMBOL] = error;\n }\n });\n };\n}\n\nfunction getStatusCode(set: Context[\"set\"]) {\n if (typeof set.status === \"number\") {\n return set.status;\n } else if (typeof set.status === \"string\") {\n return StatusMap[set.status];\n }\n}\n"],"mappings":"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","names":["START_TIME_SYMBOL","Symbol","REQUEST_BODY_SYMBOL","RESPONSE_SYMBOL","RESPONSE_PROMISE_SYMBOL","ERROR_SYMBOL","CLIENT_SYMBOL","REQUEST_SYMBOL","config","client","ApitallyClient","logsContext","AsyncLocalStorage","requestLogger","enabled","captureLogs","patchConsole","patchWinston","app","handler","mapResponse","name","startsWith","originalMapResponse","originalMapCompactResponse","mapCompactResponse","originalMapEarlyResponse","mapEarlyResponse","captureMappedResponse","originalResponse","mappedResponse","set","request","Request","Response","responseBody","Buffer","from","Promise","resolve","body","size","length","completed","newResponse","responsePromise","captureResponse","captureBody","logResponseBody","maxBodySize","wrappedMapResponse","response","wrappedMapCompactResponse","undefined","wrappedMapEarlyResponse","decorate","onStart","appInfo","getAppInfo","appVersion","setStartupData","startSync","onStop","handleShutdown","onRequest","isEnabled","method","toUpperCase","performance","now","enterWith","logRequestBody","contentType","headers","get","requestSize","parseContentLength","isSupportedContentType","clone","arrayBuffer","error","onAfterResponse","route","apitally","startTime","responseTime","requestBody","toResponse","errorResponseBody","statusCode","status","getStatusCode","statusText","Headers","consumer","consumerFromStringOrObject","consumerRegistry","addOrUpdateConsumer","then","capturedResponse","responseHeaders","responseSize","requestCounter","addRequest","identifier","path","logs","getStore","logRequest","timestamp","Date","url","convertHeaders","Object","fromEntries","entries","ValidationError","parsedMessage","JSON","parse","message","validationErrorCounter","addValidationError","loc","on","property","msg","type","serverErrorCounter","addServerError","traceback","stack","onError","Error","apitallyPlugin","StatusMap"]}
|
package/dist/elysia/plugin.js
CHANGED
|
@@ -16,6 +16,7 @@ const RESPONSE_SYMBOL = Symbol("apitally.response");
|
|
|
16
16
|
const RESPONSE_PROMISE_SYMBOL = Symbol("apitally.responsePromise");
|
|
17
17
|
const ERROR_SYMBOL = Symbol("apitally.error");
|
|
18
18
|
const CLIENT_SYMBOL = Symbol("apitally.client");
|
|
19
|
+
const REQUEST_SYMBOL = Symbol("apitally.request");
|
|
19
20
|
function apitallyPlugin(config) {
|
|
20
21
|
const client = new ApitallyClient(config);
|
|
21
22
|
const logsContext = new AsyncLocalStorage();
|
|
@@ -29,7 +30,8 @@ function apitallyPlugin(config) {
|
|
|
29
30
|
const originalMapResponse = handler.mapResponse;
|
|
30
31
|
const originalMapCompactResponse = handler.mapCompactResponse;
|
|
31
32
|
const originalMapEarlyResponse = handler.mapEarlyResponse;
|
|
32
|
-
const captureMappedResponse = /* @__PURE__ */ __name((originalResponse, mappedResponse,
|
|
33
|
+
const captureMappedResponse = /* @__PURE__ */ __name((originalResponse, mappedResponse, set) => {
|
|
34
|
+
const request = set == null ? void 0 : set[REQUEST_SYMBOL];
|
|
33
35
|
if (request instanceof Request && mappedResponse instanceof Response && !(RESPONSE_SYMBOL in request) && CLIENT_SYMBOL in request) {
|
|
34
36
|
if (typeof originalResponse === "string") {
|
|
35
37
|
const responseBody = Buffer.from(originalResponse);
|
|
@@ -52,19 +54,19 @@ function apitallyPlugin(config) {
|
|
|
52
54
|
}
|
|
53
55
|
return mappedResponse;
|
|
54
56
|
}, "captureMappedResponse");
|
|
55
|
-
handler.mapResponse = /* @__PURE__ */ __name(function wrappedMapResponse(response, set
|
|
56
|
-
const mappedResponse = originalMapResponse(response, set
|
|
57
|
-
const newResponse = captureMappedResponse(response, mappedResponse,
|
|
57
|
+
handler.mapResponse = /* @__PURE__ */ __name(function wrappedMapResponse(response, set) {
|
|
58
|
+
const mappedResponse = originalMapResponse(response, set);
|
|
59
|
+
const newResponse = captureMappedResponse(response, mappedResponse, set);
|
|
58
60
|
return newResponse;
|
|
59
61
|
}, "wrappedMapResponse");
|
|
60
|
-
handler.mapCompactResponse = /* @__PURE__ */ __name(function wrappedMapCompactResponse(response
|
|
61
|
-
const mappedResponse = originalMapCompactResponse(response
|
|
62
|
-
const newResponse = captureMappedResponse(response, mappedResponse,
|
|
62
|
+
handler.mapCompactResponse = /* @__PURE__ */ __name(function wrappedMapCompactResponse(response) {
|
|
63
|
+
const mappedResponse = originalMapCompactResponse(response);
|
|
64
|
+
const newResponse = captureMappedResponse(response, mappedResponse, void 0);
|
|
63
65
|
return newResponse;
|
|
64
66
|
}, "wrappedMapCompactResponse");
|
|
65
|
-
handler.mapEarlyResponse = /* @__PURE__ */ __name(function wrappedMapEarlyResponse(response, set
|
|
66
|
-
const mappedResponse = originalMapEarlyResponse(response, set
|
|
67
|
-
const newResponse = captureMappedResponse(response, mappedResponse,
|
|
67
|
+
handler.mapEarlyResponse = /* @__PURE__ */ __name(function wrappedMapEarlyResponse(response, set) {
|
|
68
|
+
const mappedResponse = originalMapEarlyResponse(response, set);
|
|
69
|
+
const newResponse = captureMappedResponse(response, mappedResponse, set);
|
|
68
70
|
return newResponse;
|
|
69
71
|
}, "wrappedMapEarlyResponse");
|
|
70
72
|
}
|
|
@@ -74,12 +76,13 @@ function apitallyPlugin(config) {
|
|
|
74
76
|
client.startSync();
|
|
75
77
|
}).onStop(async () => {
|
|
76
78
|
await client.handleShutdown();
|
|
77
|
-
}).onRequest(async ({ request }) => {
|
|
79
|
+
}).onRequest(async ({ request, set }) => {
|
|
78
80
|
if (!client.isEnabled() || request.method.toUpperCase() === "OPTIONS") {
|
|
79
81
|
return;
|
|
80
82
|
}
|
|
81
83
|
request[CLIENT_SYMBOL] = client;
|
|
82
84
|
request[START_TIME_SYMBOL] = performance.now();
|
|
85
|
+
set[REQUEST_SYMBOL] = request;
|
|
83
86
|
logsContext.enterWith([]);
|
|
84
87
|
if (client.requestLogger.enabled && client.requestLogger.config.logRequestBody) {
|
|
85
88
|
const contentType = request.headers.get("content-type");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/elysia/plugin.ts"],"sourcesContent":["import { Context, Elysia, StatusMap, ValidationError } from \"elysia\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { performance } from \"node:perf_hooks\";\n\nimport { ApitallyClient } from \"../common/client.js\";\nimport { consumerFromStringOrObject } from \"../common/consumerRegistry.js\";\nimport { parseContentLength } from \"../common/headers.js\";\nimport type { LogRecord } from \"../common/requestLogger.js\";\nimport { convertHeaders } from \"../common/requestLogger.js\";\nimport { CapturedResponse, captureResponse } from \"../common/response.js\";\nimport { ApitallyConfig, ApitallyConsumer } from \"../common/types.js\";\nimport { patchConsole, patchWinston } from \"../loggers/index.js\";\nimport { getAppInfo } from \"./utils.js\";\n\nconst START_TIME_SYMBOL = Symbol(\"apitally.startTime\");\nconst REQUEST_BODY_SYMBOL = Symbol(\"apitally.requestBody\");\nconst RESPONSE_SYMBOL = Symbol(\"apitally.response\");\nconst RESPONSE_PROMISE_SYMBOL = Symbol(\"apitally.responsePromise\");\nconst ERROR_SYMBOL = Symbol(\"apitally.error\");\nconst CLIENT_SYMBOL = Symbol(\"apitally.client\");\n\ndeclare global {\n interface Request {\n [START_TIME_SYMBOL]?: number;\n [REQUEST_BODY_SYMBOL]?: Buffer;\n [RESPONSE_SYMBOL]?: Response;\n [RESPONSE_PROMISE_SYMBOL]?: Promise<CapturedResponse>;\n [ERROR_SYMBOL]?: Readonly<Error>;\n [CLIENT_SYMBOL]?: ApitallyClient;\n }\n}\n\ninterface ApitallyContext {\n consumer?: ApitallyConsumer | string;\n}\n\nexport default function apitallyPlugin(config: ApitallyConfig) {\n const client = new ApitallyClient(config);\n const logsContext = new AsyncLocalStorage<LogRecord[]>();\n\n if (client.requestLogger.enabled && client.requestLogger.config.captureLogs) {\n patchConsole(logsContext);\n patchWinston(logsContext);\n }\n\n return (app: Elysia) => {\n const handler = app[\"~adapter\"].handler;\n\n if (!handler.mapResponse.name.startsWith(\"wrapped\")) {\n const originalMapResponse = handler.mapResponse;\n const originalMapCompactResponse = handler.mapCompactResponse;\n const originalMapEarlyResponse = handler.mapEarlyResponse;\n\n const captureMappedResponse = (\n originalResponse: unknown,\n mappedResponse: unknown,\n request?: Request,\n ) => {\n if (\n request instanceof Request &&\n mappedResponse instanceof Response &&\n !(RESPONSE_SYMBOL in request) &&\n CLIENT_SYMBOL in request\n ) {\n if (typeof originalResponse === \"string\") {\n // Preserve the response body value as Blob if the original response is a string,\n // so that Bun adds a Content-Type header.\n const responseBody = Buffer.from(originalResponse as string);\n request[RESPONSE_SYMBOL] = mappedResponse;\n request[RESPONSE_PROMISE_SYMBOL] = Promise.resolve({\n body: responseBody,\n size: responseBody.length,\n completed: true,\n });\n } else {\n // Otherwise capture the response using streaming\n const client = request[CLIENT_SYMBOL]!;\n const [newResponse, responsePromise] = captureResponse(\n mappedResponse,\n {\n captureBody:\n client.requestLogger.enabled &&\n client.requestLogger.config.logResponseBody,\n maxBodySize: client.requestLogger.maxBodySize,\n },\n );\n request[RESPONSE_SYMBOL] = newResponse;\n request[RESPONSE_PROMISE_SYMBOL] = responsePromise;\n return newResponse;\n }\n }\n return mappedResponse;\n };\n\n handler.mapResponse = function wrappedMapResponse(\n response: unknown,\n set: Context[\"set\"],\n request?: Request,\n ) {\n const mappedResponse = originalMapResponse(response, set, request);\n const newResponse = captureMappedResponse(\n response,\n mappedResponse,\n request,\n );\n return newResponse;\n };\n handler.mapCompactResponse = function wrappedMapCompactResponse(\n response: unknown,\n request?: Request,\n ) {\n const mappedResponse = originalMapCompactResponse(response, request);\n const newResponse = captureMappedResponse(\n response,\n mappedResponse,\n request,\n );\n return newResponse;\n };\n handler.mapEarlyResponse = function wrappedMapEarlyResponse(\n response: unknown,\n set: Context[\"set\"],\n request?: Request,\n ) {\n const mappedResponse = originalMapEarlyResponse(response, set, request);\n const newResponse = captureMappedResponse(\n response,\n mappedResponse,\n request,\n );\n return newResponse;\n };\n }\n\n return app\n .decorate(\"apitally\", {} as ApitallyContext)\n .onStart(() => {\n const appInfo = getAppInfo(app, config.appVersion);\n client.setStartupData(appInfo);\n client.startSync();\n })\n .onStop(async () => {\n await client.handleShutdown();\n })\n .onRequest(async ({ request }) => {\n if (!client.isEnabled() || request.method.toUpperCase() === \"OPTIONS\") {\n return;\n }\n\n request[CLIENT_SYMBOL] = client;\n request[START_TIME_SYMBOL] = performance.now();\n logsContext.enterWith([]);\n\n // Capture request body\n if (\n client.requestLogger.enabled &&\n client.requestLogger.config.logRequestBody\n ) {\n const contentType = request.headers.get(\"content-type\");\n const requestSize =\n parseContentLength(request.headers.get(\"content-length\")) ?? 0;\n\n if (\n client.requestLogger.isSupportedContentType(contentType) &&\n requestSize <= client.requestLogger.maxBodySize\n ) {\n try {\n request[REQUEST_BODY_SYMBOL] = Buffer.from(\n await request.clone().arrayBuffer(),\n );\n } catch (error) {\n // ignore\n }\n }\n }\n })\n .onAfterResponse(async ({ request, set, route, apitally }) => {\n if (!client.isEnabled() || request.method.toUpperCase() === \"OPTIONS\") {\n return;\n }\n\n const startTime = request[START_TIME_SYMBOL];\n const responseTime = startTime ? performance.now() - startTime : 0;\n\n const requestBody = request[REQUEST_BODY_SYMBOL];\n const requestSize =\n parseContentLength(request.headers.get(\"content-length\")) ??\n requestBody?.length;\n\n let responsePromise = request[RESPONSE_PROMISE_SYMBOL];\n let response = request[RESPONSE_SYMBOL];\n const error = request[ERROR_SYMBOL];\n\n if (\n !response &&\n error &&\n \"toResponse\" in error &&\n typeof error.toResponse === \"function\"\n ) {\n // Convert error to response\n try {\n response = error.toResponse() as Response;\n const errorResponseBody = Buffer.from(await response.arrayBuffer());\n responsePromise = Promise.resolve({\n body: errorResponseBody,\n size: errorResponseBody.length,\n completed: true,\n });\n } catch (error) {\n // ignore\n }\n }\n\n const statusCode = response?.status ?? getStatusCode(set) ?? 200;\n\n if (!response) {\n // Create empty fake response for errors without the toResponse method\n response = new Response(null, {\n status: statusCode,\n statusText: \"\",\n headers: new Headers(),\n });\n responsePromise = Promise.resolve({\n body: undefined,\n size: 0,\n completed: true,\n });\n }\n\n const consumer = apitally.consumer\n ? consumerFromStringOrObject(apitally.consumer)\n : null;\n client.consumerRegistry.addOrUpdateConsumer(consumer);\n\n // Log request when response has been fully captured\n responsePromise?.then(async (capturedResponse) => {\n const responseHeaders = response?.headers ?? set.headers;\n const responseSize = capturedResponse.completed\n ? capturedResponse.size\n : undefined;\n\n client.requestCounter.addRequest({\n consumer: consumer?.identifier,\n method: request.method,\n path: route,\n statusCode,\n responseTime,\n requestSize,\n responseSize,\n });\n\n if (client.requestLogger.enabled) {\n const logs = logsContext.getStore();\n client.requestLogger.logRequest(\n {\n timestamp: (Date.now() - responseTime) / 1000,\n method: request.method,\n path: route,\n url: request.url,\n headers: convertHeaders(\n Object.fromEntries(request.headers.entries()),\n ),\n size: requestSize,\n consumer: consumer?.identifier,\n body: requestBody,\n },\n {\n statusCode,\n responseTime: responseTime / 1000,\n headers: convertHeaders(responseHeaders),\n size: responseSize,\n body: capturedResponse.body,\n },\n error,\n logs,\n );\n }\n });\n\n // Handle validation errors\n if (\n (statusCode === 400 || statusCode === 422) &&\n error instanceof ValidationError\n ) {\n try {\n const parsedMessage = JSON.parse(error.message);\n client.validationErrorCounter.addValidationError({\n consumer: consumer?.identifier,\n method: request.method,\n path: route,\n loc:\n (parsedMessage.on ?? \"\") + \".\" + (parsedMessage.property ?? \"\"),\n msg: parsedMessage.message,\n type: \"\",\n });\n } catch (error) {\n // ignore\n }\n }\n\n // Handle server errors\n if (statusCode === 500 && error) {\n client.serverErrorCounter.addServerError({\n consumer: consumer?.identifier,\n method: request.method,\n path: route,\n type: error.name,\n msg: error.message,\n traceback: error.stack || \"\",\n });\n }\n })\n .onError(({ request, error }) => {\n if (client.isEnabled() && error instanceof Error) {\n request[ERROR_SYMBOL] = error;\n }\n });\n };\n}\n\nfunction getStatusCode(set: Context[\"set\"]) {\n if (typeof set.status === \"number\") {\n return set.status;\n } else if (typeof set.status === \"string\") {\n return StatusMap[set.status];\n }\n}\n"],"mappings":"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","names":["StatusMap","ValidationError","AsyncLocalStorage","performance","ApitallyClient","consumerFromStringOrObject","parseContentLength","convertHeaders","captureResponse","patchConsole","patchWinston","getAppInfo","START_TIME_SYMBOL","Symbol","REQUEST_BODY_SYMBOL","RESPONSE_SYMBOL","RESPONSE_PROMISE_SYMBOL","ERROR_SYMBOL","CLIENT_SYMBOL","config","client","logsContext","requestLogger","enabled","captureLogs","app","handler","mapResponse","name","startsWith","originalMapResponse","originalMapCompactResponse","mapCompactResponse","originalMapEarlyResponse","mapEarlyResponse","captureMappedResponse","originalResponse","mappedResponse","request","Request","Response","responseBody","Buffer","from","Promise","resolve","body","size","length","completed","newResponse","responsePromise","captureBody","logResponseBody","maxBodySize","wrappedMapResponse","response","set","wrappedMapCompactResponse","wrappedMapEarlyResponse","decorate","onStart","appInfo","appVersion","setStartupData","startSync","onStop","handleShutdown","onRequest","isEnabled","method","toUpperCase","now","enterWith","logRequestBody","contentType","headers","get","requestSize","isSupportedContentType","clone","arrayBuffer","error","onAfterResponse","route","apitally","startTime","responseTime","requestBody","toResponse","errorResponseBody","statusCode","status","getStatusCode","statusText","Headers","undefined","consumer","consumerRegistry","addOrUpdateConsumer","then","capturedResponse","responseHeaders","responseSize","requestCounter","addRequest","identifier","path","logs","getStore","logRequest","timestamp","Date","url","Object","fromEntries","entries","parsedMessage","JSON","parse","message","validationErrorCounter","addValidationError","loc","on","property","msg","type","serverErrorCounter","addServerError","traceback","stack","onError","Error","apitallyPlugin"]}
|
|
1
|
+
{"version":3,"sources":["../../src/elysia/plugin.ts"],"sourcesContent":["import { Context, Elysia, StatusMap, ValidationError } from \"elysia\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { performance } from \"node:perf_hooks\";\n\nimport { ApitallyClient } from \"../common/client.js\";\nimport { consumerFromStringOrObject } from \"../common/consumerRegistry.js\";\nimport { parseContentLength } from \"../common/headers.js\";\nimport type { LogRecord } from \"../common/requestLogger.js\";\nimport { convertHeaders } from \"../common/requestLogger.js\";\nimport { CapturedResponse, captureResponse } from \"../common/response.js\";\nimport { ApitallyConfig, ApitallyConsumer } from \"../common/types.js\";\nimport { patchConsole, patchWinston } from \"../loggers/index.js\";\nimport { getAppInfo } from \"./utils.js\";\n\nconst START_TIME_SYMBOL = Symbol(\"apitally.startTime\");\nconst REQUEST_BODY_SYMBOL = Symbol(\"apitally.requestBody\");\nconst RESPONSE_SYMBOL = Symbol(\"apitally.response\");\nconst RESPONSE_PROMISE_SYMBOL = Symbol(\"apitally.responsePromise\");\nconst ERROR_SYMBOL = Symbol(\"apitally.error\");\nconst CLIENT_SYMBOL = Symbol(\"apitally.client\");\nconst REQUEST_SYMBOL = Symbol(\"apitally.request\");\n\ndeclare global {\n interface Request {\n [START_TIME_SYMBOL]?: number;\n [REQUEST_BODY_SYMBOL]?: Buffer;\n [RESPONSE_SYMBOL]?: Response;\n [RESPONSE_PROMISE_SYMBOL]?: Promise<CapturedResponse>;\n [ERROR_SYMBOL]?: Readonly<Error>;\n [CLIENT_SYMBOL]?: ApitallyClient;\n }\n}\n\ntype ContextSet = Context[\"set\"] & {\n [REQUEST_SYMBOL]?: Request;\n};\n\ninterface ApitallyContext {\n consumer?: ApitallyConsumer | string;\n}\n\nexport default function apitallyPlugin(config: ApitallyConfig) {\n const client = new ApitallyClient(config);\n const logsContext = new AsyncLocalStorage<LogRecord[]>();\n\n if (client.requestLogger.enabled && client.requestLogger.config.captureLogs) {\n patchConsole(logsContext);\n patchWinston(logsContext);\n }\n\n return (app: Elysia) => {\n const handler = app[\"~adapter\"].handler;\n\n if (!handler.mapResponse.name.startsWith(\"wrapped\")) {\n const originalMapResponse = handler.mapResponse;\n const originalMapCompactResponse = handler.mapCompactResponse;\n const originalMapEarlyResponse = handler.mapEarlyResponse;\n\n const captureMappedResponse = (\n originalResponse: unknown,\n mappedResponse: unknown,\n set?: ContextSet,\n ) => {\n const request = set?.[REQUEST_SYMBOL];\n if (\n request instanceof Request &&\n mappedResponse instanceof Response &&\n !(RESPONSE_SYMBOL in request) &&\n CLIENT_SYMBOL in request\n ) {\n if (typeof originalResponse === \"string\") {\n // Preserve the response body value as Blob if the original response is a string,\n // so that Bun adds a Content-Type header.\n const responseBody = Buffer.from(originalResponse as string);\n request[RESPONSE_SYMBOL] = mappedResponse;\n request[RESPONSE_PROMISE_SYMBOL] = Promise.resolve({\n body: responseBody,\n size: responseBody.length,\n completed: true,\n });\n } else {\n // Otherwise capture the response using streaming\n const client = request[CLIENT_SYMBOL]!;\n const [newResponse, responsePromise] = captureResponse(\n mappedResponse,\n {\n captureBody:\n client.requestLogger.enabled &&\n client.requestLogger.config.logResponseBody,\n maxBodySize: client.requestLogger.maxBodySize,\n },\n );\n request[RESPONSE_SYMBOL] = newResponse;\n request[RESPONSE_PROMISE_SYMBOL] = responsePromise;\n return newResponse;\n }\n }\n return mappedResponse;\n };\n\n handler.mapResponse = function wrappedMapResponse(\n response: unknown,\n set: ContextSet,\n ) {\n const mappedResponse = originalMapResponse(response, set);\n const newResponse = captureMappedResponse(\n response,\n mappedResponse,\n set,\n );\n return newResponse;\n };\n handler.mapCompactResponse = function wrappedMapCompactResponse(\n response: unknown,\n ) {\n const mappedResponse = originalMapCompactResponse(response);\n const newResponse = captureMappedResponse(\n response,\n mappedResponse,\n undefined,\n );\n return newResponse;\n };\n handler.mapEarlyResponse = function wrappedMapEarlyResponse(\n response: unknown,\n set: ContextSet,\n ) {\n const mappedResponse = originalMapEarlyResponse(response, set);\n const newResponse = captureMappedResponse(\n response,\n mappedResponse,\n set,\n );\n return newResponse;\n };\n }\n\n return app\n .decorate(\"apitally\", {} as ApitallyContext)\n .onStart(() => {\n const appInfo = getAppInfo(app, config.appVersion);\n client.setStartupData(appInfo);\n client.startSync();\n })\n .onStop(async () => {\n await client.handleShutdown();\n })\n .onRequest(async ({ request, set }) => {\n if (!client.isEnabled() || request.method.toUpperCase() === \"OPTIONS\") {\n return;\n }\n\n request[CLIENT_SYMBOL] = client;\n request[START_TIME_SYMBOL] = performance.now();\n (set as ContextSet)[REQUEST_SYMBOL] = request;\n logsContext.enterWith([]);\n\n // Capture request body\n if (\n client.requestLogger.enabled &&\n client.requestLogger.config.logRequestBody\n ) {\n const contentType = request.headers.get(\"content-type\");\n const requestSize =\n parseContentLength(request.headers.get(\"content-length\")) ?? 0;\n\n if (\n client.requestLogger.isSupportedContentType(contentType) &&\n requestSize <= client.requestLogger.maxBodySize\n ) {\n try {\n request[REQUEST_BODY_SYMBOL] = Buffer.from(\n await request.clone().arrayBuffer(),\n );\n } catch (error) {\n // ignore\n }\n }\n }\n })\n .onAfterResponse(async ({ request, set, route, apitally }) => {\n if (!client.isEnabled() || request.method.toUpperCase() === \"OPTIONS\") {\n return;\n }\n\n const startTime = request[START_TIME_SYMBOL];\n const responseTime = startTime ? performance.now() - startTime : 0;\n\n const requestBody = request[REQUEST_BODY_SYMBOL];\n const requestSize =\n parseContentLength(request.headers.get(\"content-length\")) ??\n requestBody?.length;\n\n let responsePromise = request[RESPONSE_PROMISE_SYMBOL];\n let response = request[RESPONSE_SYMBOL];\n const error = request[ERROR_SYMBOL];\n\n if (\n !response &&\n error &&\n \"toResponse\" in error &&\n typeof error.toResponse === \"function\"\n ) {\n // Convert error to response\n try {\n response = error.toResponse() as Response;\n const errorResponseBody = Buffer.from(await response.arrayBuffer());\n responsePromise = Promise.resolve({\n body: errorResponseBody,\n size: errorResponseBody.length,\n completed: true,\n });\n } catch (error) {\n // ignore\n }\n }\n\n const statusCode = response?.status ?? getStatusCode(set) ?? 200;\n\n if (!response) {\n // Create empty fake response for errors without the toResponse method\n response = new Response(null, {\n status: statusCode,\n statusText: \"\",\n headers: new Headers(),\n });\n responsePromise = Promise.resolve({\n body: undefined,\n size: 0,\n completed: true,\n });\n }\n\n const consumer = apitally.consumer\n ? consumerFromStringOrObject(apitally.consumer)\n : null;\n client.consumerRegistry.addOrUpdateConsumer(consumer);\n\n // Log request when response has been fully captured\n responsePromise?.then(async (capturedResponse) => {\n const responseHeaders = response?.headers ?? set.headers;\n const responseSize = capturedResponse.completed\n ? capturedResponse.size\n : undefined;\n\n client.requestCounter.addRequest({\n consumer: consumer?.identifier,\n method: request.method,\n path: route,\n statusCode,\n responseTime,\n requestSize,\n responseSize,\n });\n\n if (client.requestLogger.enabled) {\n const logs = logsContext.getStore();\n client.requestLogger.logRequest(\n {\n timestamp: (Date.now() - responseTime) / 1000,\n method: request.method,\n path: route,\n url: request.url,\n headers: convertHeaders(\n Object.fromEntries(request.headers.entries()),\n ),\n size: requestSize,\n consumer: consumer?.identifier,\n body: requestBody,\n },\n {\n statusCode,\n responseTime: responseTime / 1000,\n headers: convertHeaders(responseHeaders),\n size: responseSize,\n body: capturedResponse.body,\n },\n error,\n logs,\n );\n }\n });\n\n // Handle validation errors\n if (\n (statusCode === 400 || statusCode === 422) &&\n error instanceof ValidationError\n ) {\n try {\n const parsedMessage = JSON.parse(error.message);\n client.validationErrorCounter.addValidationError({\n consumer: consumer?.identifier,\n method: request.method,\n path: route,\n loc:\n (parsedMessage.on ?? \"\") + \".\" + (parsedMessage.property ?? \"\"),\n msg: parsedMessage.message,\n type: \"\",\n });\n } catch (error) {\n // ignore\n }\n }\n\n // Handle server errors\n if (statusCode === 500 && error) {\n client.serverErrorCounter.addServerError({\n consumer: consumer?.identifier,\n method: request.method,\n path: route,\n type: error.name,\n msg: error.message,\n traceback: error.stack || \"\",\n });\n }\n })\n .onError(({ request, error }) => {\n if (client.isEnabled() && error instanceof Error) {\n request[ERROR_SYMBOL] = error;\n }\n });\n };\n}\n\nfunction getStatusCode(set: Context[\"set\"]) {\n if (typeof set.status === \"number\") {\n return set.status;\n } else if (typeof set.status === \"string\") {\n return StatusMap[set.status];\n }\n}\n"],"mappings":"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","names":["StatusMap","ValidationError","AsyncLocalStorage","performance","ApitallyClient","consumerFromStringOrObject","parseContentLength","convertHeaders","captureResponse","patchConsole","patchWinston","getAppInfo","START_TIME_SYMBOL","Symbol","REQUEST_BODY_SYMBOL","RESPONSE_SYMBOL","RESPONSE_PROMISE_SYMBOL","ERROR_SYMBOL","CLIENT_SYMBOL","REQUEST_SYMBOL","config","client","logsContext","requestLogger","enabled","captureLogs","app","handler","mapResponse","name","startsWith","originalMapResponse","originalMapCompactResponse","mapCompactResponse","originalMapEarlyResponse","mapEarlyResponse","captureMappedResponse","originalResponse","mappedResponse","set","request","Request","Response","responseBody","Buffer","from","Promise","resolve","body","size","length","completed","newResponse","responsePromise","captureBody","logResponseBody","maxBodySize","wrappedMapResponse","response","wrappedMapCompactResponse","undefined","wrappedMapEarlyResponse","decorate","onStart","appInfo","appVersion","setStartupData","startSync","onStop","handleShutdown","onRequest","isEnabled","method","toUpperCase","now","enterWith","logRequestBody","contentType","headers","get","requestSize","isSupportedContentType","clone","arrayBuffer","error","onAfterResponse","route","apitally","startTime","responseTime","requestBody","toResponse","errorResponseBody","statusCode","status","getStatusCode","statusText","Headers","consumer","consumerRegistry","addOrUpdateConsumer","then","capturedResponse","responseHeaders","responseSize","requestCounter","addRequest","identifier","path","logs","getStore","logRequest","timestamp","Date","url","Object","fromEntries","entries","parsedMessage","JSON","parse","message","validationErrorCounter","addValidationError","loc","on","property","msg","type","serverErrorCounter","addServerError","traceback","stack","onError","Error","apitallyPlugin"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apitally",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.1",
|
|
4
4
|
"description": "Simple API monitoring & analytics for REST APIs built with Express, Fastify, NestJS, AdonisJS, Hono, H3, Elysia, Hapi, and Koa.",
|
|
5
5
|
"author": "Apitally <hello@apitally.io>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -291,7 +291,7 @@
|
|
|
291
291
|
"@types/koa__router": "^12.0.4",
|
|
292
292
|
"@types/koa-bodyparser": "^4.3.12",
|
|
293
293
|
"@types/koa-route": "^3.2.8",
|
|
294
|
-
"@types/node": "^
|
|
294
|
+
"@types/node": "^24.0.0",
|
|
295
295
|
"@types/supertest": "^6.0.0",
|
|
296
296
|
"@vinejs/vine": "^2.1.0 || ^3.0.1",
|
|
297
297
|
"@vitest/coverage-v8": "^3.0.0",
|
|
@@ -312,7 +312,7 @@
|
|
|
312
312
|
"koa-bodyparser": "^4.4.1",
|
|
313
313
|
"koa-route": "^4.0.0",
|
|
314
314
|
"nock": "^14.0.0",
|
|
315
|
-
"pino-http": "^
|
|
315
|
+
"pino-http": "^11.0.0",
|
|
316
316
|
"prettier": "^3.1.0",
|
|
317
317
|
"supertest": "^7.0.0",
|
|
318
318
|
"tsup": "^8.1.0",
|