@wise-old-man/utils 3.3.12 → 3.3.13

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.
@@ -1,11 +1,5 @@
1
1
  'use strict';
2
2
 
3
- var axios = require('axios');
4
- var prometheus = require('prom-client');
5
- var zod = require('zod');
6
- require('dotenv/config');
7
- var fetchable = require('@attio/fetchable');
8
-
9
3
  var config = {
10
4
  defaultUserAgent: `WiseOldMan JS Client v${process.env.npm_package_version}`,
11
5
  baseAPIUrl: 'https://api.wiseoldman.net/v2'
@@ -2067,226 +2061,6 @@ function getParentEfficiencyMetric(metric) {
2067
2061
  return null;
2068
2062
  }
2069
2063
 
2070
- /**
2071
- * This file has been created as a way to force any usage
2072
- * of process.env to go through a dotenv.config first.
2073
- */
2074
- /**
2075
- * This ensures that an env var is required in prod but optional in dev/test.
2076
- */
2077
- function prodOnly(varSchema) {
2078
- if (process.env.NODE_ENV === 'production') {
2079
- return varSchema;
2080
- }
2081
- return zod.z.optional(varSchema);
2082
- }
2083
- const envVariablesSchema = zod.z.object({
2084
- // Prisma Database URL
2085
- CORE_DATABASE_URL: zod.z.string().trim().min(1),
2086
- // Redis Configs
2087
- REDIS_HOST: zod.z.string().trim().min(1),
2088
- REDIS_PORT: zod.z.coerce.number().positive().int(),
2089
- // Node Environment
2090
- NODE_ENV: zod.z.enum(['development', 'production', 'test']),
2091
- // Port for the API to run on
2092
- API_PORT: zod.z.optional(zod.z.coerce.number().positive().int()),
2093
- // Admin Password (For mod+ operations)
2094
- ADMIN_PASSWORD: prodOnly(zod.z.string().trim().min(1)),
2095
- // Sentry (for error tracking)
2096
- API_SENTRY_DSN: prodOnly(zod.z.string().trim().min(1)),
2097
- // Patreon Token (to access their API)
2098
- PATREON_BEARER_TOKEN: prodOnly(zod.z.string().trim().min(1)),
2099
- // Discord Bot API URL (to send events to)
2100
- DISCORD_BOT_API_URL: prodOnly(zod.z.string().trim().min(1).url()),
2101
- // Our Prometheus metrics aggregator service URL
2102
- PROMETHEUS_METRICS_SERVICE_URL: prodOnly(zod.z.string().trim().min(1).url()),
2103
- // Discord Monitoring Webhooks
2104
- DISCORD_PATREON_WEBHOOK_URL: prodOnly(zod.z.string().trim().min(1).url()),
2105
- DISCORD_MONITORING_WEBHOOK_URL: prodOnly(zod.z.string().trim().min(1).url()),
2106
- // Proxy Configs
2107
- PROXY_LIST: prodOnly(zod.z.string().trim().min(1)),
2108
- PROXY_USER: prodOnly(zod.z.string().trim().min(1)),
2109
- PROXY_PASSWORD: prodOnly(zod.z.string().trim().min(1)),
2110
- PROXY_PORT: prodOnly(zod.z.coerce.number().positive().int()),
2111
- CPU_COUNT: prodOnly(zod.z.coerce.number().positive().int()),
2112
- // Openai API Key
2113
- OPENAI_API_KEY: prodOnly(zod.z.string().trim().min(1).startsWith('sk-'))
2114
- });
2115
- // This will load env vars from a .env file, type check them,and throw an error
2116
- // (interrupting the process) if they're required and missing, or of an invalid type.
2117
- try {
2118
- envVariablesSchema.parse(process.env);
2119
- }
2120
- catch (error) {
2121
- const errorPayload = JSON.stringify(error, null, 2);
2122
- throw new Error(`Invalid environment variables. Please check env.ts for more info.\n${errorPayload}`);
2123
- }
2124
- function getThreadIndex() {
2125
- if (process.env.pm_id === undefined) {
2126
- return null;
2127
- }
2128
- return parseInt(process.env.pm_id, 10);
2129
- }
2130
-
2131
- class PrometheusService {
2132
- constructor() {
2133
- this.pushInterval = null;
2134
- this.registry = new prometheus.Registry();
2135
- this.registry.setDefaultLabels({ app: 'wise-old-man', threadIndex: getThreadIndex() });
2136
- prometheus.collectDefaultMetrics({ register: this.registry });
2137
- this.effectHistogram = new prometheus.Histogram({
2138
- name: 'effect_duration_seconds',
2139
- help: 'Duration of effects in microseconds',
2140
- labelNames: ['effectName', 'status'],
2141
- buckets: [0.1, 0.3, 0.5, 0.7, 1, 3, 5, 7, 10, 30]
2142
- });
2143
- this.httpHistogram = new prometheus.Histogram({
2144
- name: 'http_request_duration_seconds',
2145
- help: 'Duration of HTTP requests in microseconds',
2146
- labelNames: ['method', 'route', 'status', 'userAgent'],
2147
- buckets: [0.1, 0.3, 0.5, 0.7, 1, 3, 5, 7, 10, 30]
2148
- });
2149
- this.jobHistogram = new prometheus.Histogram({
2150
- name: 'job_duration_seconds',
2151
- help: 'Duration of jobs in microseconds',
2152
- labelNames: ['jobName', 'status'],
2153
- buckets: [0.1, 0.5, 1, 5, 10, 30, 60]
2154
- });
2155
- this.jobQueueGauge = new prometheus.Gauge({
2156
- name: 'job_queue_size',
2157
- help: 'Number of jobs in different states for each queue',
2158
- labelNames: ['queueName', 'state']
2159
- });
2160
- this.eventCounter = new prometheus.Counter({
2161
- name: 'event_counter',
2162
- help: 'Count of events emitted',
2163
- labelNames: ['eventType']
2164
- });
2165
- this.customPeriodCounter = new prometheus.Counter({
2166
- name: 'custom_period_counter',
2167
- help: 'Count of custom period expressions used',
2168
- labelNames: ['customPeriod']
2169
- });
2170
- this.updatePlayerJobSourceCounter = new prometheus.Counter({
2171
- name: 'update_player_job_source_counter',
2172
- help: 'Count of update player jobs dispatched',
2173
- labelNames: ['source']
2174
- });
2175
- this.runeMetricsHistogram = new prometheus.Histogram({
2176
- name: 'runemetrics_duration_seconds',
2177
- help: 'Duration of RuneMetrics requests in microseconds',
2178
- labelNames: ['status'],
2179
- buckets: [0.1, 0.3, 0.5, 1, 5, 10, 30]
2180
- });
2181
- this.hiscoresHistogram = new prometheus.Histogram({
2182
- name: 'hiscores_duration_seconds',
2183
- help: 'Duration of hiscores requests in microseconds',
2184
- labelNames: ['status'],
2185
- buckets: [0.1, 0.3, 0.5, 1, 5, 10, 30]
2186
- });
2187
- this.registry.registerMetric(this.jobHistogram);
2188
- this.registry.registerMetric(this.jobQueueGauge);
2189
- this.registry.registerMetric(this.httpHistogram);
2190
- this.registry.registerMetric(this.effectHistogram);
2191
- this.registry.registerMetric(this.eventCounter);
2192
- this.registry.registerMetric(this.customPeriodCounter);
2193
- this.registry.registerMetric(this.updatePlayerJobSourceCounter);
2194
- this.registry.registerMetric(this.runeMetricsHistogram);
2195
- this.registry.registerMetric(this.hiscoresHistogram);
2196
- }
2197
- init() {
2198
- this.pushInterval = setInterval(() => {
2199
- this.pushMetrics();
2200
- }, 60000);
2201
- }
2202
- shutdown() {
2203
- if (this.pushInterval !== null) {
2204
- clearInterval(this.pushInterval);
2205
- }
2206
- }
2207
- pushMetrics() {
2208
- return __awaiter(this, void 0, void 0, function* () {
2209
- if (process.env.NODE_ENV === 'test') {
2210
- return fetchable.errored({ code: 'NOT_ALLOWED_IN_TEST_ENV' });
2211
- }
2212
- if (!process.env.PROMETHEUS_METRICS_SERVICE_URL) {
2213
- return fetchable.errored({ code: 'MISSING_METRICS_URL' });
2214
- }
2215
- const metricsResult = yield fetchable.fromPromise(this.registry.getMetricsAsJSON());
2216
- if (fetchable.isErrored(metricsResult)) {
2217
- return fetchable.errored({
2218
- code: 'FAILED_TO_GET_PROMETHEUS_METRICS',
2219
- subError: metricsResult.error
2220
- });
2221
- }
2222
- const requestResult = yield fetchable.fromPromise(axios.post(process.env.PROMETHEUS_METRICS_SERVICE_URL, {
2223
- source: 'api',
2224
- data: metricsResult.value,
2225
- threadIndex: getThreadIndex()
2226
- }));
2227
- if (fetchable.isErrored(requestResult)) {
2228
- return fetchable.errored({
2229
- code: 'FAILED_TO_PUSH_PROMETHEUS_METRICS',
2230
- subError: requestResult.error
2231
- });
2232
- }
2233
- return fetchable.complete(true);
2234
- });
2235
- }
2236
- trackHttpRequest() {
2237
- return this.httpHistogram.startTimer();
2238
- }
2239
- trackRuneMetricsRequest() {
2240
- return this.runeMetricsHistogram.startTimer();
2241
- }
2242
- trackHiscoresRequest() {
2243
- return this.hiscoresHistogram.startTimer();
2244
- }
2245
- trackEffect(effectName, fn) {
2246
- return __awaiter(this, void 0, void 0, function* () {
2247
- const endTimer = this.effectHistogram.startTimer();
2248
- try {
2249
- yield fn();
2250
- endTimer({ effectName, status: 1 });
2251
- }
2252
- catch (error) {
2253
- endTimer({ effectName, status: 0 });
2254
- throw error;
2255
- }
2256
- });
2257
- }
2258
- trackJob(jobName, handler) {
2259
- return __awaiter(this, void 0, void 0, function* () {
2260
- const endTimer = this.jobHistogram.startTimer();
2261
- try {
2262
- yield handler();
2263
- endTimer({ jobName, status: 1 });
2264
- }
2265
- catch (error) {
2266
- endTimer({ jobName, status: 0 });
2267
- throw error;
2268
- }
2269
- });
2270
- }
2271
- trackEventEmitted(eventType) {
2272
- this.eventCounter.inc({ eventType });
2273
- }
2274
- trackCustomPeriodExpression(customPeriod) {
2275
- this.customPeriodCounter.inc({ customPeriod });
2276
- }
2277
- trackUpdatePlayerJobSource(source) {
2278
- this.updatePlayerJobSourceCounter.inc({ source });
2279
- }
2280
- updateQueueMetrics(queueName, counts) {
2281
- return __awaiter(this, void 0, void 0, function* () {
2282
- for (const [state, count] of Object.entries(counts)) {
2283
- this.jobQueueGauge.set({ queueName, state }, count);
2284
- }
2285
- });
2286
- }
2287
- }
2288
- var prometheusService = new PrometheusService();
2289
-
2290
2064
  const CUSTOM_PERIOD_REGEX = /(\d+y)?(\d+m)?(\d+w)?(\d+d)?(\d+h)?/;
2291
2065
  const PeriodProps = {
2292
2066
  [Period.FIVE_MIN]: { name: '5 Min', milliseconds: 300000 },
@@ -2314,7 +2088,6 @@ function parsePeriodExpression(periodExpression) {
2314
2088
  durationMs: PeriodProps[fixed].milliseconds
2315
2089
  };
2316
2090
  }
2317
- prometheusService.trackCustomPeriodExpression(fixed);
2318
2091
  const result = fixed.match(CUSTOM_PERIOD_REGEX);
2319
2092
  if (!result || result.length === 0 || result[0] !== fixed)
2320
2093
  return null;
package/dist/es/index.js CHANGED
@@ -1,9 +1,3 @@
1
- import axios from 'axios';
2
- import prometheus from 'prom-client';
3
- import { z } from 'zod';
4
- import 'dotenv/config';
5
- import { errored, fromPromise, isErrored, complete } from '@attio/fetchable';
6
-
7
1
  var config = {
8
2
  defaultUserAgent: `WiseOldMan JS Client v${process.env.npm_package_version}`,
9
3
  baseAPIUrl: 'https://api.wiseoldman.net/v2'
@@ -2065,226 +2059,6 @@ function getParentEfficiencyMetric(metric) {
2065
2059
  return null;
2066
2060
  }
2067
2061
 
2068
- /**
2069
- * This file has been created as a way to force any usage
2070
- * of process.env to go through a dotenv.config first.
2071
- */
2072
- /**
2073
- * This ensures that an env var is required in prod but optional in dev/test.
2074
- */
2075
- function prodOnly(varSchema) {
2076
- if (process.env.NODE_ENV === 'production') {
2077
- return varSchema;
2078
- }
2079
- return z.optional(varSchema);
2080
- }
2081
- const envVariablesSchema = z.object({
2082
- // Prisma Database URL
2083
- CORE_DATABASE_URL: z.string().trim().min(1),
2084
- // Redis Configs
2085
- REDIS_HOST: z.string().trim().min(1),
2086
- REDIS_PORT: z.coerce.number().positive().int(),
2087
- // Node Environment
2088
- NODE_ENV: z.enum(['development', 'production', 'test']),
2089
- // Port for the API to run on
2090
- API_PORT: z.optional(z.coerce.number().positive().int()),
2091
- // Admin Password (For mod+ operations)
2092
- ADMIN_PASSWORD: prodOnly(z.string().trim().min(1)),
2093
- // Sentry (for error tracking)
2094
- API_SENTRY_DSN: prodOnly(z.string().trim().min(1)),
2095
- // Patreon Token (to access their API)
2096
- PATREON_BEARER_TOKEN: prodOnly(z.string().trim().min(1)),
2097
- // Discord Bot API URL (to send events to)
2098
- DISCORD_BOT_API_URL: prodOnly(z.string().trim().min(1).url()),
2099
- // Our Prometheus metrics aggregator service URL
2100
- PROMETHEUS_METRICS_SERVICE_URL: prodOnly(z.string().trim().min(1).url()),
2101
- // Discord Monitoring Webhooks
2102
- DISCORD_PATREON_WEBHOOK_URL: prodOnly(z.string().trim().min(1).url()),
2103
- DISCORD_MONITORING_WEBHOOK_URL: prodOnly(z.string().trim().min(1).url()),
2104
- // Proxy Configs
2105
- PROXY_LIST: prodOnly(z.string().trim().min(1)),
2106
- PROXY_USER: prodOnly(z.string().trim().min(1)),
2107
- PROXY_PASSWORD: prodOnly(z.string().trim().min(1)),
2108
- PROXY_PORT: prodOnly(z.coerce.number().positive().int()),
2109
- CPU_COUNT: prodOnly(z.coerce.number().positive().int()),
2110
- // Openai API Key
2111
- OPENAI_API_KEY: prodOnly(z.string().trim().min(1).startsWith('sk-'))
2112
- });
2113
- // This will load env vars from a .env file, type check them,and throw an error
2114
- // (interrupting the process) if they're required and missing, or of an invalid type.
2115
- try {
2116
- envVariablesSchema.parse(process.env);
2117
- }
2118
- catch (error) {
2119
- const errorPayload = JSON.stringify(error, null, 2);
2120
- throw new Error(`Invalid environment variables. Please check env.ts for more info.\n${errorPayload}`);
2121
- }
2122
- function getThreadIndex() {
2123
- if (process.env.pm_id === undefined) {
2124
- return null;
2125
- }
2126
- return parseInt(process.env.pm_id, 10);
2127
- }
2128
-
2129
- class PrometheusService {
2130
- constructor() {
2131
- this.pushInterval = null;
2132
- this.registry = new prometheus.Registry();
2133
- this.registry.setDefaultLabels({ app: 'wise-old-man', threadIndex: getThreadIndex() });
2134
- prometheus.collectDefaultMetrics({ register: this.registry });
2135
- this.effectHistogram = new prometheus.Histogram({
2136
- name: 'effect_duration_seconds',
2137
- help: 'Duration of effects in microseconds',
2138
- labelNames: ['effectName', 'status'],
2139
- buckets: [0.1, 0.3, 0.5, 0.7, 1, 3, 5, 7, 10, 30]
2140
- });
2141
- this.httpHistogram = new prometheus.Histogram({
2142
- name: 'http_request_duration_seconds',
2143
- help: 'Duration of HTTP requests in microseconds',
2144
- labelNames: ['method', 'route', 'status', 'userAgent'],
2145
- buckets: [0.1, 0.3, 0.5, 0.7, 1, 3, 5, 7, 10, 30]
2146
- });
2147
- this.jobHistogram = new prometheus.Histogram({
2148
- name: 'job_duration_seconds',
2149
- help: 'Duration of jobs in microseconds',
2150
- labelNames: ['jobName', 'status'],
2151
- buckets: [0.1, 0.5, 1, 5, 10, 30, 60]
2152
- });
2153
- this.jobQueueGauge = new prometheus.Gauge({
2154
- name: 'job_queue_size',
2155
- help: 'Number of jobs in different states for each queue',
2156
- labelNames: ['queueName', 'state']
2157
- });
2158
- this.eventCounter = new prometheus.Counter({
2159
- name: 'event_counter',
2160
- help: 'Count of events emitted',
2161
- labelNames: ['eventType']
2162
- });
2163
- this.customPeriodCounter = new prometheus.Counter({
2164
- name: 'custom_period_counter',
2165
- help: 'Count of custom period expressions used',
2166
- labelNames: ['customPeriod']
2167
- });
2168
- this.updatePlayerJobSourceCounter = new prometheus.Counter({
2169
- name: 'update_player_job_source_counter',
2170
- help: 'Count of update player jobs dispatched',
2171
- labelNames: ['source']
2172
- });
2173
- this.runeMetricsHistogram = new prometheus.Histogram({
2174
- name: 'runemetrics_duration_seconds',
2175
- help: 'Duration of RuneMetrics requests in microseconds',
2176
- labelNames: ['status'],
2177
- buckets: [0.1, 0.3, 0.5, 1, 5, 10, 30]
2178
- });
2179
- this.hiscoresHistogram = new prometheus.Histogram({
2180
- name: 'hiscores_duration_seconds',
2181
- help: 'Duration of hiscores requests in microseconds',
2182
- labelNames: ['status'],
2183
- buckets: [0.1, 0.3, 0.5, 1, 5, 10, 30]
2184
- });
2185
- this.registry.registerMetric(this.jobHistogram);
2186
- this.registry.registerMetric(this.jobQueueGauge);
2187
- this.registry.registerMetric(this.httpHistogram);
2188
- this.registry.registerMetric(this.effectHistogram);
2189
- this.registry.registerMetric(this.eventCounter);
2190
- this.registry.registerMetric(this.customPeriodCounter);
2191
- this.registry.registerMetric(this.updatePlayerJobSourceCounter);
2192
- this.registry.registerMetric(this.runeMetricsHistogram);
2193
- this.registry.registerMetric(this.hiscoresHistogram);
2194
- }
2195
- init() {
2196
- this.pushInterval = setInterval(() => {
2197
- this.pushMetrics();
2198
- }, 60000);
2199
- }
2200
- shutdown() {
2201
- if (this.pushInterval !== null) {
2202
- clearInterval(this.pushInterval);
2203
- }
2204
- }
2205
- pushMetrics() {
2206
- return __awaiter(this, void 0, void 0, function* () {
2207
- if (process.env.NODE_ENV === 'test') {
2208
- return errored({ code: 'NOT_ALLOWED_IN_TEST_ENV' });
2209
- }
2210
- if (!process.env.PROMETHEUS_METRICS_SERVICE_URL) {
2211
- return errored({ code: 'MISSING_METRICS_URL' });
2212
- }
2213
- const metricsResult = yield fromPromise(this.registry.getMetricsAsJSON());
2214
- if (isErrored(metricsResult)) {
2215
- return errored({
2216
- code: 'FAILED_TO_GET_PROMETHEUS_METRICS',
2217
- subError: metricsResult.error
2218
- });
2219
- }
2220
- const requestResult = yield fromPromise(axios.post(process.env.PROMETHEUS_METRICS_SERVICE_URL, {
2221
- source: 'api',
2222
- data: metricsResult.value,
2223
- threadIndex: getThreadIndex()
2224
- }));
2225
- if (isErrored(requestResult)) {
2226
- return errored({
2227
- code: 'FAILED_TO_PUSH_PROMETHEUS_METRICS',
2228
- subError: requestResult.error
2229
- });
2230
- }
2231
- return complete(true);
2232
- });
2233
- }
2234
- trackHttpRequest() {
2235
- return this.httpHistogram.startTimer();
2236
- }
2237
- trackRuneMetricsRequest() {
2238
- return this.runeMetricsHistogram.startTimer();
2239
- }
2240
- trackHiscoresRequest() {
2241
- return this.hiscoresHistogram.startTimer();
2242
- }
2243
- trackEffect(effectName, fn) {
2244
- return __awaiter(this, void 0, void 0, function* () {
2245
- const endTimer = this.effectHistogram.startTimer();
2246
- try {
2247
- yield fn();
2248
- endTimer({ effectName, status: 1 });
2249
- }
2250
- catch (error) {
2251
- endTimer({ effectName, status: 0 });
2252
- throw error;
2253
- }
2254
- });
2255
- }
2256
- trackJob(jobName, handler) {
2257
- return __awaiter(this, void 0, void 0, function* () {
2258
- const endTimer = this.jobHistogram.startTimer();
2259
- try {
2260
- yield handler();
2261
- endTimer({ jobName, status: 1 });
2262
- }
2263
- catch (error) {
2264
- endTimer({ jobName, status: 0 });
2265
- throw error;
2266
- }
2267
- });
2268
- }
2269
- trackEventEmitted(eventType) {
2270
- this.eventCounter.inc({ eventType });
2271
- }
2272
- trackCustomPeriodExpression(customPeriod) {
2273
- this.customPeriodCounter.inc({ customPeriod });
2274
- }
2275
- trackUpdatePlayerJobSource(source) {
2276
- this.updatePlayerJobSourceCounter.inc({ source });
2277
- }
2278
- updateQueueMetrics(queueName, counts) {
2279
- return __awaiter(this, void 0, void 0, function* () {
2280
- for (const [state, count] of Object.entries(counts)) {
2281
- this.jobQueueGauge.set({ queueName, state }, count);
2282
- }
2283
- });
2284
- }
2285
- }
2286
- var prometheusService = new PrometheusService();
2287
-
2288
2062
  const CUSTOM_PERIOD_REGEX = /(\d+y)?(\d+m)?(\d+w)?(\d+d)?(\d+h)?/;
2289
2063
  const PeriodProps = {
2290
2064
  [Period.FIVE_MIN]: { name: '5 Min', milliseconds: 300000 },
@@ -2312,7 +2086,6 @@ function parsePeriodExpression(periodExpression) {
2312
2086
  durationMs: PeriodProps[fixed].milliseconds
2313
2087
  };
2314
2088
  }
2315
- prometheusService.trackCustomPeriodExpression(fixed);
2316
2089
  const result = fixed.match(CUSTOM_PERIOD_REGEX);
2317
2090
  if (!result || result.length === 0 || result[0] !== fixed)
2318
2091
  return null;
package/dist/es/index.mjs CHANGED
@@ -1,9 +1,3 @@
1
- import axios from 'axios';
2
- import prometheus from 'prom-client';
3
- import { z } from 'zod';
4
- import 'dotenv/config';
5
- import { errored, fromPromise, isErrored, complete } from '@attio/fetchable';
6
-
7
1
  var config = {
8
2
  defaultUserAgent: `WiseOldMan JS Client v${process.env.npm_package_version}`,
9
3
  baseAPIUrl: 'https://api.wiseoldman.net/v2'
@@ -2065,226 +2059,6 @@ function getParentEfficiencyMetric(metric) {
2065
2059
  return null;
2066
2060
  }
2067
2061
 
2068
- /**
2069
- * This file has been created as a way to force any usage
2070
- * of process.env to go through a dotenv.config first.
2071
- */
2072
- /**
2073
- * This ensures that an env var is required in prod but optional in dev/test.
2074
- */
2075
- function prodOnly(varSchema) {
2076
- if (process.env.NODE_ENV === 'production') {
2077
- return varSchema;
2078
- }
2079
- return z.optional(varSchema);
2080
- }
2081
- const envVariablesSchema = z.object({
2082
- // Prisma Database URL
2083
- CORE_DATABASE_URL: z.string().trim().min(1),
2084
- // Redis Configs
2085
- REDIS_HOST: z.string().trim().min(1),
2086
- REDIS_PORT: z.coerce.number().positive().int(),
2087
- // Node Environment
2088
- NODE_ENV: z.enum(['development', 'production', 'test']),
2089
- // Port for the API to run on
2090
- API_PORT: z.optional(z.coerce.number().positive().int()),
2091
- // Admin Password (For mod+ operations)
2092
- ADMIN_PASSWORD: prodOnly(z.string().trim().min(1)),
2093
- // Sentry (for error tracking)
2094
- API_SENTRY_DSN: prodOnly(z.string().trim().min(1)),
2095
- // Patreon Token (to access their API)
2096
- PATREON_BEARER_TOKEN: prodOnly(z.string().trim().min(1)),
2097
- // Discord Bot API URL (to send events to)
2098
- DISCORD_BOT_API_URL: prodOnly(z.string().trim().min(1).url()),
2099
- // Our Prometheus metrics aggregator service URL
2100
- PROMETHEUS_METRICS_SERVICE_URL: prodOnly(z.string().trim().min(1).url()),
2101
- // Discord Monitoring Webhooks
2102
- DISCORD_PATREON_WEBHOOK_URL: prodOnly(z.string().trim().min(1).url()),
2103
- DISCORD_MONITORING_WEBHOOK_URL: prodOnly(z.string().trim().min(1).url()),
2104
- // Proxy Configs
2105
- PROXY_LIST: prodOnly(z.string().trim().min(1)),
2106
- PROXY_USER: prodOnly(z.string().trim().min(1)),
2107
- PROXY_PASSWORD: prodOnly(z.string().trim().min(1)),
2108
- PROXY_PORT: prodOnly(z.coerce.number().positive().int()),
2109
- CPU_COUNT: prodOnly(z.coerce.number().positive().int()),
2110
- // Openai API Key
2111
- OPENAI_API_KEY: prodOnly(z.string().trim().min(1).startsWith('sk-'))
2112
- });
2113
- // This will load env vars from a .env file, type check them,and throw an error
2114
- // (interrupting the process) if they're required and missing, or of an invalid type.
2115
- try {
2116
- envVariablesSchema.parse(process.env);
2117
- }
2118
- catch (error) {
2119
- const errorPayload = JSON.stringify(error, null, 2);
2120
- throw new Error(`Invalid environment variables. Please check env.ts for more info.\n${errorPayload}`);
2121
- }
2122
- function getThreadIndex() {
2123
- if (process.env.pm_id === undefined) {
2124
- return null;
2125
- }
2126
- return parseInt(process.env.pm_id, 10);
2127
- }
2128
-
2129
- class PrometheusService {
2130
- constructor() {
2131
- this.pushInterval = null;
2132
- this.registry = new prometheus.Registry();
2133
- this.registry.setDefaultLabels({ app: 'wise-old-man', threadIndex: getThreadIndex() });
2134
- prometheus.collectDefaultMetrics({ register: this.registry });
2135
- this.effectHistogram = new prometheus.Histogram({
2136
- name: 'effect_duration_seconds',
2137
- help: 'Duration of effects in microseconds',
2138
- labelNames: ['effectName', 'status'],
2139
- buckets: [0.1, 0.3, 0.5, 0.7, 1, 3, 5, 7, 10, 30]
2140
- });
2141
- this.httpHistogram = new prometheus.Histogram({
2142
- name: 'http_request_duration_seconds',
2143
- help: 'Duration of HTTP requests in microseconds',
2144
- labelNames: ['method', 'route', 'status', 'userAgent'],
2145
- buckets: [0.1, 0.3, 0.5, 0.7, 1, 3, 5, 7, 10, 30]
2146
- });
2147
- this.jobHistogram = new prometheus.Histogram({
2148
- name: 'job_duration_seconds',
2149
- help: 'Duration of jobs in microseconds',
2150
- labelNames: ['jobName', 'status'],
2151
- buckets: [0.1, 0.5, 1, 5, 10, 30, 60]
2152
- });
2153
- this.jobQueueGauge = new prometheus.Gauge({
2154
- name: 'job_queue_size',
2155
- help: 'Number of jobs in different states for each queue',
2156
- labelNames: ['queueName', 'state']
2157
- });
2158
- this.eventCounter = new prometheus.Counter({
2159
- name: 'event_counter',
2160
- help: 'Count of events emitted',
2161
- labelNames: ['eventType']
2162
- });
2163
- this.customPeriodCounter = new prometheus.Counter({
2164
- name: 'custom_period_counter',
2165
- help: 'Count of custom period expressions used',
2166
- labelNames: ['customPeriod']
2167
- });
2168
- this.updatePlayerJobSourceCounter = new prometheus.Counter({
2169
- name: 'update_player_job_source_counter',
2170
- help: 'Count of update player jobs dispatched',
2171
- labelNames: ['source']
2172
- });
2173
- this.runeMetricsHistogram = new prometheus.Histogram({
2174
- name: 'runemetrics_duration_seconds',
2175
- help: 'Duration of RuneMetrics requests in microseconds',
2176
- labelNames: ['status'],
2177
- buckets: [0.1, 0.3, 0.5, 1, 5, 10, 30]
2178
- });
2179
- this.hiscoresHistogram = new prometheus.Histogram({
2180
- name: 'hiscores_duration_seconds',
2181
- help: 'Duration of hiscores requests in microseconds',
2182
- labelNames: ['status'],
2183
- buckets: [0.1, 0.3, 0.5, 1, 5, 10, 30]
2184
- });
2185
- this.registry.registerMetric(this.jobHistogram);
2186
- this.registry.registerMetric(this.jobQueueGauge);
2187
- this.registry.registerMetric(this.httpHistogram);
2188
- this.registry.registerMetric(this.effectHistogram);
2189
- this.registry.registerMetric(this.eventCounter);
2190
- this.registry.registerMetric(this.customPeriodCounter);
2191
- this.registry.registerMetric(this.updatePlayerJobSourceCounter);
2192
- this.registry.registerMetric(this.runeMetricsHistogram);
2193
- this.registry.registerMetric(this.hiscoresHistogram);
2194
- }
2195
- init() {
2196
- this.pushInterval = setInterval(() => {
2197
- this.pushMetrics();
2198
- }, 60000);
2199
- }
2200
- shutdown() {
2201
- if (this.pushInterval !== null) {
2202
- clearInterval(this.pushInterval);
2203
- }
2204
- }
2205
- pushMetrics() {
2206
- return __awaiter(this, void 0, void 0, function* () {
2207
- if (process.env.NODE_ENV === 'test') {
2208
- return errored({ code: 'NOT_ALLOWED_IN_TEST_ENV' });
2209
- }
2210
- if (!process.env.PROMETHEUS_METRICS_SERVICE_URL) {
2211
- return errored({ code: 'MISSING_METRICS_URL' });
2212
- }
2213
- const metricsResult = yield fromPromise(this.registry.getMetricsAsJSON());
2214
- if (isErrored(metricsResult)) {
2215
- return errored({
2216
- code: 'FAILED_TO_GET_PROMETHEUS_METRICS',
2217
- subError: metricsResult.error
2218
- });
2219
- }
2220
- const requestResult = yield fromPromise(axios.post(process.env.PROMETHEUS_METRICS_SERVICE_URL, {
2221
- source: 'api',
2222
- data: metricsResult.value,
2223
- threadIndex: getThreadIndex()
2224
- }));
2225
- if (isErrored(requestResult)) {
2226
- return errored({
2227
- code: 'FAILED_TO_PUSH_PROMETHEUS_METRICS',
2228
- subError: requestResult.error
2229
- });
2230
- }
2231
- return complete(true);
2232
- });
2233
- }
2234
- trackHttpRequest() {
2235
- return this.httpHistogram.startTimer();
2236
- }
2237
- trackRuneMetricsRequest() {
2238
- return this.runeMetricsHistogram.startTimer();
2239
- }
2240
- trackHiscoresRequest() {
2241
- return this.hiscoresHistogram.startTimer();
2242
- }
2243
- trackEffect(effectName, fn) {
2244
- return __awaiter(this, void 0, void 0, function* () {
2245
- const endTimer = this.effectHistogram.startTimer();
2246
- try {
2247
- yield fn();
2248
- endTimer({ effectName, status: 1 });
2249
- }
2250
- catch (error) {
2251
- endTimer({ effectName, status: 0 });
2252
- throw error;
2253
- }
2254
- });
2255
- }
2256
- trackJob(jobName, handler) {
2257
- return __awaiter(this, void 0, void 0, function* () {
2258
- const endTimer = this.jobHistogram.startTimer();
2259
- try {
2260
- yield handler();
2261
- endTimer({ jobName, status: 1 });
2262
- }
2263
- catch (error) {
2264
- endTimer({ jobName, status: 0 });
2265
- throw error;
2266
- }
2267
- });
2268
- }
2269
- trackEventEmitted(eventType) {
2270
- this.eventCounter.inc({ eventType });
2271
- }
2272
- trackCustomPeriodExpression(customPeriod) {
2273
- this.customPeriodCounter.inc({ customPeriod });
2274
- }
2275
- trackUpdatePlayerJobSource(source) {
2276
- this.updatePlayerJobSourceCounter.inc({ source });
2277
- }
2278
- updateQueueMetrics(queueName, counts) {
2279
- return __awaiter(this, void 0, void 0, function* () {
2280
- for (const [state, count] of Object.entries(counts)) {
2281
- this.jobQueueGauge.set({ queueName, state }, count);
2282
- }
2283
- });
2284
- }
2285
- }
2286
- var prometheusService = new PrometheusService();
2287
-
2288
2062
  const CUSTOM_PERIOD_REGEX = /(\d+y)?(\d+m)?(\d+w)?(\d+d)?(\d+h)?/;
2289
2063
  const PeriodProps = {
2290
2064
  [Period.FIVE_MIN]: { name: '5 Min', milliseconds: 300000 },
@@ -2312,7 +2086,6 @@ function parsePeriodExpression(periodExpression) {
2312
2086
  durationMs: PeriodProps[fixed].milliseconds
2313
2087
  };
2314
2088
  }
2315
- prometheusService.trackCustomPeriodExpression(fixed);
2316
2089
  const result = fixed.match(CUSTOM_PERIOD_REGEX);
2317
2090
  if (!result || result.length === 0 || result[0] !== fixed)
2318
2091
  return null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wise-old-man/utils",
3
- "version": "3.3.12",
3
+ "version": "3.3.13",
4
4
  "description": "A JavaScript/TypeScript client that interfaces and consumes the Wise Old Man API, an API that tracks and measures players' progress in Old School Runescape.",
5
5
  "keywords": [
6
6
  "wiseoldman",