@rpcbase/server 0.482.0 → 0.484.0

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,7 +1,7 @@
1
1
  import { models } from "@rpcbase/db";
2
2
  import { buildAbilityFromSession, getAccessibleByQuery } from "@rpcbase/db/acl";
3
3
  import { createNotification, sendNotificationsDigestForUser } from "./notifications.js";
4
- import { o as object, b as boolean, n as number, a as array, s as string, r as record, u as unknown, _ as _enum } from "./schemas-D5T9tDtI.js";
4
+ import { o as object, b as boolean, n as number, a as array, s as string, r as record, _ as _enum, u as unknown } from "./schemas-7qqi9OQy.js";
5
5
  const getSessionUser = (ctx) => {
6
6
  const rawSessionUser = ctx.req.session?.user;
7
7
  const userId = typeof rawSessionUser?.id === "string" ? rawSessionUser.id.trim() : "";
@@ -1,6 +1,6 @@
1
1
  import { models, ZRBRtsChangeOp } from "@rpcbase/db";
2
2
  import { buildAbilityFromSession } from "@rpcbase/db/acl";
3
- import { o as object, a as array, s as string, n as number, b as boolean, _ as _enum } from "./schemas-D5T9tDtI.js";
3
+ import { o as object, a as array, s as string, n as number, b as boolean, _ as _enum } from "./schemas-7qqi9OQy.js";
4
4
  const Route = "/api/rb/rts/changes";
5
5
  const requestSchema = object({
6
6
  sinceSeq: number().int().min(0).default(0),
@@ -4,7 +4,7 @@ import { JSDOM } from "jsdom";
4
4
  import createDOMPurify from "dompurify";
5
5
  import { g as getTenantId, a as getModelCtx, b as buildUploadsAbility, c as getUploadSessionAccessQuery, e as ensureUploadIndexes, d as getBucketName, f as getUserId, h as getChunkSizeBytes, i as getSessionTtlMs, j as computeSha256Hex, t as toBufferPayload, n as normalizeSha256Hex, k as getMaxClientUploadBytesPerSecond, l as getRawBodyLimitBytes } from "./shared-BJomDDWK.js";
6
6
  import { randomBytes } from "node:crypto";
7
- import { o as object, n as number, b as boolean, s as string, a as array, _ as _enum } from "./schemas-D5T9tDtI.js";
7
+ import { o as object, n as number, b as boolean, s as string, a as array, _ as _enum } from "./schemas-7qqi9OQy.js";
8
8
  const MAX_SVG_BYTES = 128 * 1024;
9
9
  const window = new JSDOM("").window;
10
10
  const DOMPurify = createDOMPurify(window);
package/dist/index.js CHANGED
@@ -566,18 +566,12 @@ const type_utils_toString = ObjProto.toString;
566
566
  const isArray = nativeIsArray || function(obj) {
567
567
  return "[object Array]" === type_utils_toString.call(obj);
568
568
  };
569
+ const isObject = (x) => x === Object(x) && !isArray(x);
569
570
  const isUndefined = (x) => void 0 === x;
570
571
  const isString = (x) => "[object String]" == type_utils_toString.call(x);
571
572
  const isEmptyString = (x) => isString(x) && 0 === x.trim().length;
572
573
  const isNumber = (x) => "[object Number]" == type_utils_toString.call(x);
573
574
  const isPlainError = (x) => x instanceof Error;
574
- function isInstanceOf(candidate, base) {
575
- try {
576
- return candidate instanceof base;
577
- } catch {
578
- return false;
579
- }
580
- }
581
575
  function isPrimitive(value) {
582
576
  return null === value || "object" != typeof value;
583
577
  }
@@ -590,6 +584,13 @@ function isEvent(candidate) {
590
584
  function isPlainObject(candidate) {
591
585
  return isBuiltin(candidate, "Object");
592
586
  }
587
+ function isInstanceOf(candidate, base) {
588
+ try {
589
+ return candidate instanceof base;
590
+ } catch {
591
+ return false;
592
+ }
593
+ }
593
594
  function clampToRange(value, min, max, logger, fallbackValue) {
594
595
  if (min > max) {
595
596
  logger.warn("min cannot be greater than max.");
@@ -2050,6 +2051,9 @@ class ErrorTracking {
2050
2051
  });
2051
2052
  this.startAutocaptureIfEnabled();
2052
2053
  }
2054
+ static isPreviouslyCapturedError(x) {
2055
+ return isObject(x) && "__posthog_previously_captured_error" in x && true === x.__posthog_previously_captured_error;
2056
+ }
2053
2057
  static async buildEventMessage(error, hint, distinctId, additionalProperties) {
2054
2058
  const properties = {
2055
2059
  ...additionalProperties
@@ -2074,14 +2078,16 @@ class ErrorTracking {
2074
2078
  }
2075
2079
  onException(exception, hint) {
2076
2080
  this.client.addPendingPromise((async () => {
2077
- const eventMessage = await ErrorTracking.buildEventMessage(exception, hint);
2078
- const exceptionProperties = eventMessage.properties;
2079
- const exceptionType = exceptionProperties?.$exception_list[0]?.type ?? "Exception";
2080
- const isRateLimited = this._rateLimiter.consumeRateLimit(exceptionType);
2081
- if (isRateLimited) return void this._logger.info("Skipping exception capture because of client rate limiting.", {
2082
- exception: exceptionType
2083
- });
2084
- return this.client.capture(eventMessage);
2081
+ if (!ErrorTracking.isPreviouslyCapturedError(exception)) {
2082
+ const eventMessage = await ErrorTracking.buildEventMessage(exception, hint);
2083
+ const exceptionProperties = eventMessage.properties;
2084
+ const exceptionType = exceptionProperties?.$exception_list[0]?.type ?? "Exception";
2085
+ const isRateLimited = this._rateLimiter.consumeRateLimit(exceptionType);
2086
+ if (isRateLimited) return void this._logger.info("Skipping exception capture because of client rate limiting.", {
2087
+ exception: exceptionType
2088
+ });
2089
+ return this.client.capture(eventMessage);
2090
+ }
2085
2091
  })());
2086
2092
  }
2087
2093
  async onFatalError(exception) {
@@ -2096,7 +2102,7 @@ class ErrorTracking {
2096
2102
  this._rateLimiter.stop();
2097
2103
  }
2098
2104
  }
2099
- const version = "5.18.0";
2105
+ const version = "5.20.0";
2100
2106
  const FeatureFlagError = {
2101
2107
  ERRORS_WHILE_COMPUTING: "errors_while_computing_flags",
2102
2108
  FLAG_MISSING: "flag_missing",
@@ -2374,6 +2380,7 @@ class FeatureFlagsPoller {
2374
2380
  }
2375
2381
  async loadFeatureFlags(forceReload = false) {
2376
2382
  if (this.loadedSuccessfullyOnce && !forceReload) return;
2383
+ if (!forceReload && this.nextFetchAllowedAt && Date.now() < this.nextFetchAllowedAt) return void this.logMsgIfDebug(() => console.debug("[FEATURE FLAGS] Skipping fetch, in backoff period"));
2377
2384
  if (!this.loadingPromise) this.loadingPromise = this._loadFeatureFlags().catch((err) => this.logMsgIfDebug(() => console.debug(`[FEATURE FLAGS] Failed to load feature flags: ${err}`))).finally(() => {
2378
2385
  this.loadingPromise = void 0;
2379
2386
  });
@@ -2386,6 +2393,16 @@ class FeatureFlagsPoller {
2386
2393
  if (!this.shouldBeginExponentialBackoff) return this.pollingInterval;
2387
2394
  return Math.min(SIXTY_SECONDS, this.pollingInterval * 2 ** this.backOffCount);
2388
2395
  }
2396
+ beginBackoff() {
2397
+ this.shouldBeginExponentialBackoff = true;
2398
+ this.backOffCount += 1;
2399
+ this.nextFetchAllowedAt = Date.now() + this.getPollingInterval();
2400
+ }
2401
+ clearBackoff() {
2402
+ this.shouldBeginExponentialBackoff = false;
2403
+ this.backOffCount = 0;
2404
+ this.nextFetchAllowedAt = void 0;
2405
+ }
2389
2406
  async _loadFeatureFlags() {
2390
2407
  if (this.poller) {
2391
2408
  clearTimeout(this.poller);
@@ -2411,12 +2428,10 @@ class FeatureFlagsPoller {
2411
2428
  this.logMsgIfDebug(() => console.debug("[FEATURE FLAGS] Flags not modified (304), using cached data"));
2412
2429
  this.flagsEtag = res.headers?.get("ETag") ?? this.flagsEtag;
2413
2430
  this.loadedSuccessfullyOnce = true;
2414
- this.shouldBeginExponentialBackoff = false;
2415
- this.backOffCount = 0;
2431
+ this.clearBackoff();
2416
2432
  return;
2417
2433
  case 401:
2418
- this.shouldBeginExponentialBackoff = true;
2419
- this.backOffCount += 1;
2434
+ this.beginBackoff();
2420
2435
  throw new ClientError(`Your project key or personal API key is invalid. Setting next polling interval to ${this.getPollingInterval()}ms. More information: https://posthog.com/docs/api#rate-limiting`);
2421
2436
  case 402:
2422
2437
  console.warn("[FEATURE FLAGS] Feature flags quota limit exceeded - unsetting all local flags. Learn more about billing limits at https://posthog.com/docs/billing/limits-alerts");
@@ -2426,12 +2441,10 @@ class FeatureFlagsPoller {
2426
2441
  this.cohorts = {};
2427
2442
  return;
2428
2443
  case 403:
2429
- this.shouldBeginExponentialBackoff = true;
2430
- this.backOffCount += 1;
2444
+ this.beginBackoff();
2431
2445
  throw new ClientError(`Your personal API key does not have permission to fetch feature flag definitions for local evaluation. Setting next polling interval to ${this.getPollingInterval()}ms. Are you sure you're using the correct personal and Project API key pair? More information: https://posthog.com/docs/api/overview`);
2432
2446
  case 429:
2433
- this.shouldBeginExponentialBackoff = true;
2434
- this.backOffCount += 1;
2447
+ this.beginBackoff();
2435
2448
  throw new ClientError(`You are being rate limited. Setting next polling interval to ${this.getPollingInterval()}ms. More information: https://posthog.com/docs/api#rate-limiting`);
2436
2449
  case 200: {
2437
2450
  const responseJson = await res.json() ?? {};
@@ -2443,8 +2456,7 @@ class FeatureFlagsPoller {
2443
2456
  cohorts: responseJson.cohorts || {}
2444
2457
  };
2445
2458
  this.updateFlagState(flagData);
2446
- this.shouldBeginExponentialBackoff = false;
2447
- this.backOffCount = 0;
2459
+ this.clearBackoff();
2448
2460
  if (this.cacheProvider && shouldFetch) try {
2449
2461
  await this.cacheProvider.onFlagDefinitionsReceived(flagData);
2450
2462
  } catch (err) {
@@ -2812,6 +2824,7 @@ class PostHogBackendClient extends PostHogCoreStateless {
2812
2824
  });
2813
2825
  }
2814
2826
  async getFeatureFlag(key, distinctId, options) {
2827
+ if (void 0 !== this._flagOverrides && key in this._flagOverrides) return this._flagOverrides[key];
2815
2828
  const { groups, disableGeoip } = options || {};
2816
2829
  let { onlyEvaluateLocally, sendFeatureFlagEvents, personProperties, groupProperties } = options || {};
2817
2830
  const adjustedProperties = this.addLocalPersonAndGroupProperties(distinctId, groups, personProperties, groupProperties);
@@ -2872,6 +2885,7 @@ class PostHogBackendClient extends PostHogCoreStateless {
2872
2885
  return response;
2873
2886
  }
2874
2887
  async getFeatureFlagPayload(key, distinctId, matchValue, options) {
2888
+ if (void 0 !== this._payloadOverrides && key in this._payloadOverrides) return this._payloadOverrides[key];
2875
2889
  const { groups, disableGeoip } = options || {};
2876
2890
  let { onlyEvaluateLocally, personProperties, groupProperties } = options || {};
2877
2891
  const adjustedProperties = this.addLocalPersonAndGroupProperties(distinctId, groups, personProperties, groupProperties);
@@ -2945,6 +2959,14 @@ class PostHogBackendClient extends PostHogCoreStateless {
2945
2959
  ...remoteEvaluationResult.payloads || {}
2946
2960
  };
2947
2961
  }
2962
+ if (void 0 !== this._flagOverrides) featureFlags = {
2963
+ ...featureFlags,
2964
+ ...this._flagOverrides
2965
+ };
2966
+ if (void 0 !== this._payloadOverrides) featureFlagPayloads = {
2967
+ ...featureFlagPayloads,
2968
+ ...this._payloadOverrides
2969
+ };
2948
2970
  return {
2949
2971
  featureFlags,
2950
2972
  featureFlagPayloads
@@ -2958,6 +2980,53 @@ class PostHogBackendClient extends PostHogCoreStateless {
2958
2980
  async reloadFeatureFlags() {
2959
2981
  await this.featureFlagsPoller?.loadFeatureFlags(true);
2960
2982
  }
2983
+ overrideFeatureFlags(overrides) {
2984
+ const flagArrayToRecord = (flags) => Object.fromEntries(flags.map((f) => [
2985
+ f,
2986
+ true
2987
+ ]));
2988
+ if (false === overrides) {
2989
+ this._flagOverrides = void 0;
2990
+ this._payloadOverrides = void 0;
2991
+ return;
2992
+ }
2993
+ if (Array.isArray(overrides)) {
2994
+ this._flagOverrides = flagArrayToRecord(overrides);
2995
+ return;
2996
+ }
2997
+ if (this._isFeatureFlagOverrideOptions(overrides)) {
2998
+ if ("flags" in overrides) {
2999
+ if (false === overrides.flags) this._flagOverrides = void 0;
3000
+ else if (Array.isArray(overrides.flags)) this._flagOverrides = flagArrayToRecord(overrides.flags);
3001
+ else if (void 0 !== overrides.flags) this._flagOverrides = {
3002
+ ...overrides.flags
3003
+ };
3004
+ }
3005
+ if ("payloads" in overrides) {
3006
+ if (false === overrides.payloads) this._payloadOverrides = void 0;
3007
+ else if (void 0 !== overrides.payloads) this._payloadOverrides = {
3008
+ ...overrides.payloads
3009
+ };
3010
+ }
3011
+ return;
3012
+ }
3013
+ this._flagOverrides = {
3014
+ ...overrides
3015
+ };
3016
+ }
3017
+ _isFeatureFlagOverrideOptions(overrides) {
3018
+ if ("object" != typeof overrides || null === overrides || Array.isArray(overrides)) return false;
3019
+ const obj = overrides;
3020
+ if ("flags" in obj) {
3021
+ const flagsValue = obj["flags"];
3022
+ if (false === flagsValue || Array.isArray(flagsValue) || "object" == typeof flagsValue && null !== flagsValue) return true;
3023
+ }
3024
+ if ("payloads" in obj) {
3025
+ const payloadsValue = obj["payloads"];
3026
+ if (false === payloadsValue || "object" == typeof payloadsValue && null !== payloadsValue) return true;
3027
+ }
3028
+ return false;
3029
+ }
2961
3030
  withContext(data, fn, options) {
2962
3031
  if (!this.context) return fn();
2963
3032
  return this.context.run(data, fn, options);
@@ -3062,17 +3131,24 @@ class PostHogBackendClient extends PostHogCoreStateless {
3062
3131
  allGroupProperties
3063
3132
  };
3064
3133
  }
3065
- captureException(error, distinctId, additionalProperties) {
3066
- const syntheticException = new Error("PostHog syntheticException");
3067
- this.addPendingPromise(ErrorTracking.buildEventMessage(error, {
3068
- syntheticException
3069
- }, distinctId, additionalProperties).then((msg) => this.capture(msg)));
3134
+ captureException(error, distinctId, additionalProperties, uuid) {
3135
+ if (!ErrorTracking.isPreviouslyCapturedError(error)) {
3136
+ const syntheticException = new Error("PostHog syntheticException");
3137
+ this.addPendingPromise(ErrorTracking.buildEventMessage(error, {
3138
+ syntheticException
3139
+ }, distinctId, additionalProperties).then((msg) => this.capture({
3140
+ ...msg,
3141
+ uuid
3142
+ })));
3143
+ }
3070
3144
  }
3071
3145
  async captureExceptionImmediate(error, distinctId, additionalProperties) {
3072
- const syntheticException = new Error("PostHog syntheticException");
3073
- this.addPendingPromise(ErrorTracking.buildEventMessage(error, {
3074
- syntheticException
3075
- }, distinctId, additionalProperties).then((msg) => this.captureImmediate(msg)));
3146
+ if (!ErrorTracking.isPreviouslyCapturedError(error)) {
3147
+ const syntheticException = new Error("PostHog syntheticException");
3148
+ this.addPendingPromise(ErrorTracking.buildEventMessage(error, {
3149
+ syntheticException
3150
+ }, distinctId, additionalProperties).then((msg) => this.captureImmediate(msg)));
3151
+ }
3076
3152
  }
3077
3153
  async prepareEventMessage(props) {
3078
3154
  const { distinctId, event, properties, groups, sendFeatureFlags, timestamp, disableGeoip, uuid } = props;
@@ -3186,6 +3262,7 @@ function setupExpressErrorHandler(_posthog, app) {
3186
3262
  }
3187
3263
  function posthogErrorHandler(posthog) {
3188
3264
  return (error, req, res, next) => {
3265
+ if (ErrorTracking.isPreviouslyCapturedError(error)) return void next(error);
3189
3266
  const sessionId = req.headers["x-posthog-session-id"];
3190
3267
  const distinctId = req.headers["x-posthog-distinct-id"];
3191
3268
  const syntheticException = new Error("Synthetic exception");
@@ -1,7 +1,7 @@
1
1
  import { models } from "@rpcbase/db";
2
2
  import { s as sendEmail } from "./email-DEw8keax.js";
3
3
  const routes = Object.entries({
4
- .../* @__PURE__ */ Object.assign({ "./api/notifications/handler.ts": () => import("./handler-BvRk-c8E.js") })
4
+ .../* @__PURE__ */ Object.assign({ "./api/notifications/handler.ts": () => import("./handler-BwK8qxLn.js") })
5
5
  }).reduce((acc, [path, mod]) => {
6
6
  acc[path.replace("./api/", "@rpcbase/server/notifications/api/")] = mod;
7
7
  return acc;
@@ -1,6 +1,6 @@
1
1
  import { Server as HttpServer } from 'node:http';
2
2
  import { RequestHandler } from 'express';
3
- import { AppAbility } from '../../../db/acl/src';
3
+ import { AppAbility } from '../../../db/src/acl/index.ts';
4
4
  import { WebSocket } from 'ws';
5
5
  type SocketMeta = {
6
6
  tenantId: string;
package/dist/rts/index.js CHANGED
@@ -3,7 +3,7 @@ import { models } from "@rpcbase/db";
3
3
  import { buildAbilityFromSession, getTenantRolesFromSessionUser, buildAbility, getAccessibleByQuery } from "@rpcbase/db/acl";
4
4
  import { WebSocketServer } from "ws";
5
5
  const routes = Object.entries({
6
- .../* @__PURE__ */ Object.assign({ "./api/changes/handler.ts": () => import("../handler-lOVgWqyF.js") })
6
+ .../* @__PURE__ */ Object.assign({ "./api/changes/handler.ts": () => import("../handler-CedzJJg0.js") })
7
7
  }).reduce((acc, [path, mod]) => {
8
8
  acc[path.replace("./api/", "@rpcbase/server/rts/api/")] = mod;
9
9
  return acc;