@rpcbase/server 0.507.0 → 0.509.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.
- package/dist/{handler-CedzJJg0.js → handler-BNrqh1Kb.js} +2 -2
- package/dist/{handler-CedzJJg0.js.map → handler-BNrqh1Kb.js.map} +1 -1
- package/dist/{handler-qCAUmVgd.js → handler-Bh3a6Br1.js} +2 -2
- package/dist/{handler-qCAUmVgd.js.map → handler-Bh3a6Br1.js.map} +1 -1
- package/dist/{handler-BwK8qxLn.js → handler-r-HYO3Oy.js} +2 -2
- package/dist/{handler-BwK8qxLn.js.map → handler-r-HYO3Oy.js.map} +1 -1
- package/dist/index.js +354 -94
- package/dist/index.js.map +1 -1
- package/dist/notifications.js +1 -1
- package/dist/queryExecutor-BZ0GSPM1.js +130 -0
- package/dist/queryExecutor-BZ0GSPM1.js.map +1 -0
- package/dist/renderSSR.d.ts +2 -0
- package/dist/renderSSR.d.ts.map +1 -1
- package/dist/rts/index.d.ts.map +1 -1
- package/dist/rts/index.js +17 -46
- package/dist/rts/index.js.map +1 -1
- package/dist/rts/queryExecutor.d.ts +28 -0
- package/dist/rts/queryExecutor.d.ts.map +1 -0
- package/dist/rts/ssrHydration.d.ts +14 -0
- package/dist/rts/ssrHydration.d.ts.map +1 -0
- package/dist/{schemas-7qqi9OQy.js → schemas-Cjdjgehl.js} +4 -4
- package/dist/schemas-Cjdjgehl.js.map +1 -0
- package/dist/ssrMiddleware.d.ts.map +1 -1
- package/dist/uploads.js +1 -1
- package/package.json +1 -1
- package/dist/schemas-7qqi9OQy.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import MongoStore from "connect-mongo";
|
|
|
4
4
|
import { createClient } from "redis";
|
|
5
5
|
import { MongoClient } from "mongodb";
|
|
6
6
|
import env from "@rpcbase/env";
|
|
7
|
-
import { initApiClient, SsrErrorFallback, SSR_ERROR_STATE_GLOBAL_KEY, serializeSsrErrorState } from "@rpcbase/client";
|
|
7
|
+
import { initApiClient, STATIC_RPCBASE_RTS_HYDRATION_DATA_KEY, RtsSsrRuntimeProvider, SsrErrorFallback, SSR_ERROR_STATE_GLOBAL_KEY, serializeSsrErrorState } from "@rpcbase/client";
|
|
8
8
|
import { dirname, posix, sep } from "path";
|
|
9
9
|
import fs, { createReadStream, readFileSync } from "node:fs";
|
|
10
10
|
import { createInterface } from "node:readline";
|
|
@@ -16,11 +16,12 @@ import fsPromises from "node:fs/promises";
|
|
|
16
16
|
import inspector from "node:inspector";
|
|
17
17
|
import path from "node:path";
|
|
18
18
|
import { fileURLToPath } from "node:url";
|
|
19
|
-
import { Transform } from "node:stream";
|
|
19
|
+
import { Writable, Transform } from "node:stream";
|
|
20
20
|
import { StrictMode, createElement } from "react";
|
|
21
21
|
import { renderToPipeableStream, renderToStaticMarkup } from "react-dom/server";
|
|
22
22
|
import { jsx } from "react/jsx-runtime";
|
|
23
23
|
import { createPath, matchRoutes, parsePath, createStaticRouter, StaticRouterProvider } from "@rpcbase/router";
|
|
24
|
+
import { r as resolveRtsRequestTenantId, i as isRtsRequestAuthorized, b as buildRtsAbilityFromRequest, n as normalizeRtsQueryOptions, a as runRtsQuery } from "./queryExecutor-BZ0GSPM1.js";
|
|
24
25
|
import { s } from "./email-DEw8keax.js";
|
|
25
26
|
function getDefaultExportFromCjs(x) {
|
|
26
27
|
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
|
|
@@ -539,6 +540,8 @@ var types_PostHogPersistedProperty = /* @__PURE__ */ (function(PostHogPersistedP
|
|
|
539
540
|
PostHogPersistedProperty["AnonymousId"] = "anonymous_id";
|
|
540
541
|
PostHogPersistedProperty["DistinctId"] = "distinct_id";
|
|
541
542
|
PostHogPersistedProperty["Props"] = "props";
|
|
543
|
+
PostHogPersistedProperty["EnablePersonProcessing"] = "enable_person_processing";
|
|
544
|
+
PostHogPersistedProperty["PersonMode"] = "person_mode";
|
|
542
545
|
PostHogPersistedProperty["FeatureFlagDetails"] = "feature_flag_details";
|
|
543
546
|
PostHogPersistedProperty["FeatureFlags"] = "feature_flags";
|
|
544
547
|
PostHogPersistedProperty["FeatureFlagPayloads"] = "feature_flag_payloads";
|
|
@@ -573,7 +576,7 @@ const isObject = (x) => x === Object(x) && !isArray(x);
|
|
|
573
576
|
const isUndefined = (x) => void 0 === x;
|
|
574
577
|
const isString = (x) => "[object String]" == type_utils_toString.call(x);
|
|
575
578
|
const isEmptyString = (x) => isString(x) && 0 === x.trim().length;
|
|
576
|
-
const isNumber = (x) => "[object Number]" == type_utils_toString.call(x);
|
|
579
|
+
const isNumber = (x) => "[object Number]" == type_utils_toString.call(x) && x === x;
|
|
577
580
|
const isPlainError = (x) => x instanceof Error;
|
|
578
581
|
function isPrimitive(value) {
|
|
579
582
|
return null === value || "object" != typeof value;
|
|
@@ -855,10 +858,11 @@ class PostHogCoreStateless {
|
|
|
855
858
|
this.disableGeoip = options.disableGeoip ?? true;
|
|
856
859
|
this.disabled = options.disabled ?? false;
|
|
857
860
|
this.historicalMigration = options?.historicalMigration ?? false;
|
|
858
|
-
this.evaluationEnvironments = options?.evaluationEnvironments;
|
|
859
861
|
this._initPromise = Promise.resolve();
|
|
860
862
|
this._isInitialized = true;
|
|
861
863
|
this._logger = createLogger("[PostHog]", this.logMsgIfDebug.bind(this));
|
|
864
|
+
this.evaluationContexts = options?.evaluationContexts ?? options?.evaluationEnvironments;
|
|
865
|
+
if (options?.evaluationEnvironments && !options?.evaluationContexts) this._logger.warn("evaluationEnvironments is deprecated. Use evaluationContexts instead. This property will be removed in a future version.");
|
|
862
866
|
this.disableCompression = !isGzipSupported() || (options?.disableCompression ?? false);
|
|
863
867
|
}
|
|
864
868
|
logMsgIfDebug(fn) {
|
|
@@ -1033,7 +1037,7 @@ class PostHogCoreStateless {
|
|
|
1033
1037
|
group_properties: groupProperties,
|
|
1034
1038
|
...extraPayload
|
|
1035
1039
|
};
|
|
1036
|
-
if (this.
|
|
1040
|
+
if (this.evaluationContexts && this.evaluationContexts.length > 0) requestData.evaluation_contexts = this.evaluationContexts;
|
|
1037
1041
|
const fetchOptions = {
|
|
1038
1042
|
method: "POST",
|
|
1039
1043
|
headers: {
|
|
@@ -1045,10 +1049,35 @@ class PostHogCoreStateless {
|
|
|
1045
1049
|
this._logger.info("Flags URL", url);
|
|
1046
1050
|
return this.fetchWithRetry(url, fetchOptions, {
|
|
1047
1051
|
retryCount: 0
|
|
1048
|
-
}, this.featureFlagsRequestTimeoutMs).then((response) => response.json()).then((response) =>
|
|
1052
|
+
}, this.featureFlagsRequestTimeoutMs).then((response) => response.json()).then((response) => ({
|
|
1053
|
+
success: true,
|
|
1054
|
+
response: normalizeFlagsResponse(response)
|
|
1055
|
+
})).catch((error) => {
|
|
1049
1056
|
this._events.emit("error", error);
|
|
1057
|
+
return {
|
|
1058
|
+
success: false,
|
|
1059
|
+
error: this.categorizeRequestError(error)
|
|
1060
|
+
};
|
|
1050
1061
|
});
|
|
1051
1062
|
}
|
|
1063
|
+
categorizeRequestError(error) {
|
|
1064
|
+
if (error instanceof PostHogFetchHttpError) return {
|
|
1065
|
+
type: "api_error",
|
|
1066
|
+
statusCode: error.status
|
|
1067
|
+
};
|
|
1068
|
+
if (error instanceof PostHogFetchNetworkError) {
|
|
1069
|
+
const cause = error.error;
|
|
1070
|
+
if (cause instanceof Error && ("AbortError" === cause.name || "TimeoutError" === cause.name)) return {
|
|
1071
|
+
type: "timeout"
|
|
1072
|
+
};
|
|
1073
|
+
return {
|
|
1074
|
+
type: "connection_error"
|
|
1075
|
+
};
|
|
1076
|
+
}
|
|
1077
|
+
return {
|
|
1078
|
+
type: "unknown_error"
|
|
1079
|
+
};
|
|
1080
|
+
}
|
|
1052
1081
|
async getFeatureFlagStateless(key, distinctId, groups = {}, personProperties = {}, groupProperties = {}, disableGeoip) {
|
|
1053
1082
|
await this._initPromise;
|
|
1054
1083
|
const flagDetailResponse = await this.getFeatureFlagDetailStateless(key, distinctId, groups, personProperties, groupProperties, disableGeoip);
|
|
@@ -1115,8 +1144,9 @@ class PostHogCoreStateless {
|
|
|
1115
1144
|
const extraPayload = {};
|
|
1116
1145
|
if (disableGeoip ?? this.disableGeoip) extraPayload["geoip_disable"] = true;
|
|
1117
1146
|
if (flagKeysToEvaluate) extraPayload["flag_keys_to_evaluate"] = flagKeysToEvaluate;
|
|
1118
|
-
const
|
|
1119
|
-
if (
|
|
1147
|
+
const result = await this.getFlags(distinctId, groups, personProperties, groupProperties, extraPayload);
|
|
1148
|
+
if (!result.success) return;
|
|
1149
|
+
const flagsResponse = result.response;
|
|
1120
1150
|
if (flagsResponse.errorsWhileComputingFlags) console.error("[FEATURE FLAGS] Error while computing feature flags, some flags may be missing or incorrect. Learn more at https://posthog.com/docs/feature-flags/best-practices");
|
|
1121
1151
|
if (flagsResponse.quotaLimited?.includes("feature_flags")) {
|
|
1122
1152
|
console.warn("[FEATURE FLAGS] Feature flags quota limit exceeded - feature flags unavailable. Learn more about billing limits at https://posthog.com/docs/billing/limits-alerts");
|
|
@@ -1183,10 +1213,17 @@ class PostHogCoreStateless {
|
|
|
1183
1213
|
this.setPersistedProperty(types_PostHogPersistedProperty.Props, this.props);
|
|
1184
1214
|
});
|
|
1185
1215
|
}
|
|
1216
|
+
processBeforeEnqueue(message) {
|
|
1217
|
+
return message;
|
|
1218
|
+
}
|
|
1219
|
+
async flushStorage() {
|
|
1220
|
+
}
|
|
1186
1221
|
enqueue(type, _message, options) {
|
|
1187
1222
|
this.wrap(() => {
|
|
1188
1223
|
if (this.optedOut) return void this._events.emit(type, "Library is disabled. Not sending event. To re-enable, call posthog.optIn()");
|
|
1189
|
-
|
|
1224
|
+
let message = this.prepareMessage(type, _message, options);
|
|
1225
|
+
message = this.processBeforeEnqueue(message);
|
|
1226
|
+
if (null === message) return;
|
|
1190
1227
|
const queue = this.getPersistedProperty(types_PostHogPersistedProperty.Queue) || [];
|
|
1191
1228
|
if (queue.length >= this.maxQueueSize) {
|
|
1192
1229
|
queue.shift();
|
|
@@ -1205,10 +1242,13 @@ class PostHogCoreStateless {
|
|
|
1205
1242
|
if (this.disabled) return void this._logger.warn("The client is disabled");
|
|
1206
1243
|
if (!this._isInitialized) await this._initPromise;
|
|
1207
1244
|
if (this.optedOut) return void this._events.emit(type, "Library is disabled. Not sending event. To re-enable, call posthog.optIn()");
|
|
1245
|
+
let message = this.prepareMessage(type, _message, options);
|
|
1246
|
+
message = this.processBeforeEnqueue(message);
|
|
1247
|
+
if (null === message) return;
|
|
1208
1248
|
const data = {
|
|
1209
1249
|
api_key: this.apiKey,
|
|
1210
1250
|
batch: [
|
|
1211
|
-
|
|
1251
|
+
message
|
|
1212
1252
|
],
|
|
1213
1253
|
sent_at: currentISOTime()
|
|
1214
1254
|
};
|
|
@@ -1293,11 +1333,12 @@ class PostHogCoreStateless {
|
|
|
1293
1333
|
while (queue.length > 0 && sentMessages.length < originalQueueLength) {
|
|
1294
1334
|
const batchItems = queue.slice(0, this.maxBatchSize);
|
|
1295
1335
|
const batchMessages = batchItems.map((item) => item.message);
|
|
1296
|
-
const persistQueueChange = () => {
|
|
1336
|
+
const persistQueueChange = async () => {
|
|
1297
1337
|
const refreshedQueue = this.getPersistedProperty(types_PostHogPersistedProperty.Queue) || [];
|
|
1298
1338
|
const newQueue = refreshedQueue.slice(batchItems.length);
|
|
1299
1339
|
this.setPersistedProperty(types_PostHogPersistedProperty.Queue, newQueue);
|
|
1300
1340
|
queue = newQueue;
|
|
1341
|
+
await this.flushStorage();
|
|
1301
1342
|
};
|
|
1302
1343
|
const data = {
|
|
1303
1344
|
api_key: this.apiKey,
|
|
@@ -1333,11 +1374,11 @@ class PostHogCoreStateless {
|
|
|
1333
1374
|
this._logger.warn(`Received 413 when sending batch of size ${batchMessages.length}, reducing batch size to ${this.maxBatchSize}`);
|
|
1334
1375
|
continue;
|
|
1335
1376
|
}
|
|
1336
|
-
if (!(err instanceof PostHogFetchNetworkError)) persistQueueChange();
|
|
1377
|
+
if (!(err instanceof PostHogFetchNetworkError)) await persistQueueChange();
|
|
1337
1378
|
this._events.emit("error", err);
|
|
1338
1379
|
throw err;
|
|
1339
1380
|
}
|
|
1340
|
-
persistQueueChange();
|
|
1381
|
+
await persistQueueChange();
|
|
1341
1382
|
sentMessages.push(...batchMessages);
|
|
1342
1383
|
}
|
|
1343
1384
|
this._events.emit("flush", sentMessages);
|
|
@@ -1462,7 +1503,7 @@ class ErrorPropertiesBuilder {
|
|
|
1462
1503
|
};
|
|
1463
1504
|
const coercingContext = this.buildCoercingContext(mechanism, hint, 0);
|
|
1464
1505
|
const exceptionWithCause = coercingContext.apply(input);
|
|
1465
|
-
const parsingContext = this.buildParsingContext();
|
|
1506
|
+
const parsingContext = this.buildParsingContext(hint);
|
|
1466
1507
|
const exceptionWithStack = this.parseStacktrace(exceptionWithCause, parsingContext);
|
|
1467
1508
|
const exceptionList = this.convertToExceptionList(exceptionWithStack, mechanism);
|
|
1468
1509
|
return {
|
|
@@ -1486,7 +1527,7 @@ class ErrorPropertiesBuilder {
|
|
|
1486
1527
|
let cause;
|
|
1487
1528
|
if (null != err.cause) cause = this.parseStacktrace(err.cause, ctx);
|
|
1488
1529
|
let stack;
|
|
1489
|
-
if ("" != err.stack && null != err.stack) stack = this.applyChunkIds(this.stackParser(err.stack, err.synthetic ?
|
|
1530
|
+
if ("" != err.stack && null != err.stack) stack = this.applyChunkIds(this.stackParser(err.stack, err.synthetic ? ctx.skipFirstLines : 0), ctx.chunkIdMap);
|
|
1490
1531
|
return {
|
|
1491
1532
|
...err,
|
|
1492
1533
|
cause,
|
|
@@ -1531,9 +1572,10 @@ class ErrorPropertiesBuilder {
|
|
|
1531
1572
|
}));
|
|
1532
1573
|
return exceptionList;
|
|
1533
1574
|
}
|
|
1534
|
-
buildParsingContext() {
|
|
1575
|
+
buildParsingContext(hint) {
|
|
1535
1576
|
const context = {
|
|
1536
|
-
chunkIdMap: getFilenameToChunkIdMap(this.stackParser)
|
|
1577
|
+
chunkIdMap: getFilenameToChunkIdMap(this.stackParser),
|
|
1578
|
+
skipFirstLines: hint.skipFirstLines ?? 1
|
|
1537
1579
|
};
|
|
1538
1580
|
return context;
|
|
1539
1581
|
}
|
|
@@ -2105,7 +2147,7 @@ class ErrorTracking {
|
|
|
2105
2147
|
this._rateLimiter.stop();
|
|
2106
2148
|
}
|
|
2107
2149
|
}
|
|
2108
|
-
const version = "5.
|
|
2150
|
+
const version = "5.24.11";
|
|
2109
2151
|
const FeatureFlagError = {
|
|
2110
2152
|
ERRORS_WHILE_COMPUTING: "errors_while_computing_flags",
|
|
2111
2153
|
FLAG_MISSING: "flag_missing",
|
|
@@ -2170,6 +2212,7 @@ class FeatureFlagsPoller {
|
|
|
2170
2212
|
this.customHeaders = customHeaders;
|
|
2171
2213
|
this.onLoad = options.onLoad;
|
|
2172
2214
|
this.cacheProvider = options.cacheProvider;
|
|
2215
|
+
this.strictLocalEvaluation = options.strictLocalEvaluation ?? false;
|
|
2173
2216
|
this.loadFeatureFlags();
|
|
2174
2217
|
}
|
|
2175
2218
|
debug(enabled = true) {
|
|
@@ -2365,6 +2408,11 @@ class FeatureFlagsPoller {
|
|
|
2365
2408
|
this.cohorts = flagData.cohorts;
|
|
2366
2409
|
this.loadedSuccessfullyOnce = true;
|
|
2367
2410
|
}
|
|
2411
|
+
warnAboutExperienceContinuityFlags(flags) {
|
|
2412
|
+
if (this.strictLocalEvaluation) return;
|
|
2413
|
+
const experienceContinuityFlags = flags.filter((f) => f.ensure_experience_continuity);
|
|
2414
|
+
if (experienceContinuityFlags.length > 0) console.warn(`[PostHog] You are using local evaluation but ${experienceContinuityFlags.length} flag(s) have experience continuity enabled: ${experienceContinuityFlags.map((f) => f.key).join(", ")}. Experience continuity is incompatible with local evaluation and will cause a server request on every flag evaluation, negating local evaluation cost savings. To avoid server requests and unexpected costs, either disable experience continuity on these flags in PostHog, use strictLocalEvaluation: true in client init, or pass onlyEvaluateLocally: true per flag call (flags that cannot be evaluated locally will return undefined).`);
|
|
2415
|
+
}
|
|
2368
2416
|
async loadFromCache(debugMessage) {
|
|
2369
2417
|
if (!this.cacheProvider) return false;
|
|
2370
2418
|
try {
|
|
@@ -2373,6 +2421,7 @@ class FeatureFlagsPoller {
|
|
|
2373
2421
|
this.updateFlagState(cached);
|
|
2374
2422
|
this.logMsgIfDebug(() => console.debug(`[FEATURE FLAGS] ${debugMessage} (${cached.flags.length} flags)`));
|
|
2375
2423
|
this.onLoad?.(this.featureFlags.length);
|
|
2424
|
+
this.warnAboutExperienceContinuityFlags(cached.flags);
|
|
2376
2425
|
return true;
|
|
2377
2426
|
}
|
|
2378
2427
|
return false;
|
|
@@ -2466,6 +2515,7 @@ class FeatureFlagsPoller {
|
|
|
2466
2515
|
this.onError?.(new Error(`Failed to store in cache: ${err}`));
|
|
2467
2516
|
}
|
|
2468
2517
|
this.onLoad?.(this.featureFlags.length);
|
|
2518
|
+
this.warnAboutExperienceContinuityFlags(flagData.flags);
|
|
2469
2519
|
break;
|
|
2470
2520
|
}
|
|
2471
2521
|
default:
|
|
@@ -2720,7 +2770,8 @@ class PostHogBackendClient extends PostHogCoreStateless {
|
|
|
2720
2770
|
this._events.emit("localEvaluationFlagsLoaded", count);
|
|
2721
2771
|
},
|
|
2722
2772
|
customHeaders: this.getCustomHeaders(),
|
|
2723
|
-
cacheProvider: options.flagDefinitionCacheProvider
|
|
2773
|
+
cacheProvider: options.flagDefinitionCacheProvider,
|
|
2774
|
+
strictLocalEvaluation: options.strictLocalEvaluation
|
|
2724
2775
|
});
|
|
2725
2776
|
}
|
|
2726
2777
|
this.errorTracking = new ErrorTracking(this, options, this._logger);
|
|
@@ -2826,21 +2877,56 @@ class PostHogBackendClient extends PostHogCoreStateless {
|
|
|
2826
2877
|
});
|
|
2827
2878
|
});
|
|
2828
2879
|
}
|
|
2829
|
-
async
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2880
|
+
async _getFeatureFlagResult(key, distinctId, options = {}, matchValue) {
|
|
2881
|
+
const sendFeatureFlagEvents = options.sendFeatureFlagEvents ?? true;
|
|
2882
|
+
if (void 0 !== this._flagOverrides && key in this._flagOverrides) {
|
|
2883
|
+
const overrideValue = this._flagOverrides[key];
|
|
2884
|
+
if (void 0 === overrideValue) return;
|
|
2885
|
+
const overridePayload = this._payloadOverrides?.[key];
|
|
2886
|
+
return {
|
|
2887
|
+
key,
|
|
2888
|
+
enabled: false !== overrideValue,
|
|
2889
|
+
variant: "string" == typeof overrideValue ? overrideValue : void 0,
|
|
2890
|
+
payload: overridePayload
|
|
2891
|
+
};
|
|
2892
|
+
}
|
|
2893
|
+
const { groups, disableGeoip } = options;
|
|
2894
|
+
let { onlyEvaluateLocally, personProperties, groupProperties } = options;
|
|
2833
2895
|
const adjustedProperties = this.addLocalPersonAndGroupProperties(distinctId, groups, personProperties, groupProperties);
|
|
2834
2896
|
personProperties = adjustedProperties.allPersonProperties;
|
|
2835
2897
|
groupProperties = adjustedProperties.allGroupProperties;
|
|
2836
|
-
if (void 0 == onlyEvaluateLocally) onlyEvaluateLocally = false;
|
|
2837
|
-
|
|
2838
|
-
let
|
|
2839
|
-
const flagWasLocallyEvaluated = void 0 !== response;
|
|
2898
|
+
if (void 0 == onlyEvaluateLocally) onlyEvaluateLocally = this.options.strictLocalEvaluation ?? false;
|
|
2899
|
+
let result;
|
|
2900
|
+
let flagWasLocallyEvaluated = false;
|
|
2840
2901
|
let requestId;
|
|
2841
2902
|
let evaluatedAt;
|
|
2842
|
-
let flagDetail;
|
|
2843
2903
|
let featureFlagError;
|
|
2904
|
+
let flagId;
|
|
2905
|
+
let flagVersion;
|
|
2906
|
+
let flagReason;
|
|
2907
|
+
const localEvaluationEnabled = void 0 !== this.featureFlagsPoller;
|
|
2908
|
+
if (localEvaluationEnabled) {
|
|
2909
|
+
await this.featureFlagsPoller?.loadFeatureFlags();
|
|
2910
|
+
const flag = this.featureFlagsPoller?.featureFlagsByKey[key];
|
|
2911
|
+
if (flag) try {
|
|
2912
|
+
const localResult = await this.featureFlagsPoller?.computeFlagAndPayloadLocally(flag, distinctId, groups, personProperties, groupProperties, matchValue);
|
|
2913
|
+
if (localResult) {
|
|
2914
|
+
flagWasLocallyEvaluated = true;
|
|
2915
|
+
const value = localResult.value;
|
|
2916
|
+
flagId = flag.id;
|
|
2917
|
+
flagReason = "Evaluated locally";
|
|
2918
|
+
result = {
|
|
2919
|
+
key,
|
|
2920
|
+
enabled: false !== value,
|
|
2921
|
+
variant: "string" == typeof value ? value : void 0,
|
|
2922
|
+
payload: localResult.payload ?? void 0
|
|
2923
|
+
};
|
|
2924
|
+
}
|
|
2925
|
+
} catch (e) {
|
|
2926
|
+
if (e instanceof RequiresServerEvaluation || e instanceof InconclusiveMatchError) this._logger?.info(`${e.name} when computing flag locally: ${key}: ${e.message}`);
|
|
2927
|
+
else throw e;
|
|
2928
|
+
}
|
|
2929
|
+
}
|
|
2844
2930
|
if (!flagWasLocallyEvaluated && !onlyEvaluateLocally) {
|
|
2845
2931
|
const flagsResponse = await super.getFeatureFlagDetailsStateless(distinctId, groups, personProperties, groupProperties, disableGeoip, [
|
|
2846
2932
|
key
|
|
@@ -2852,68 +2938,87 @@ class PostHogBackendClient extends PostHogCoreStateless {
|
|
|
2852
2938
|
const errors = [];
|
|
2853
2939
|
if (flagsResponse.errorsWhileComputingFlags) errors.push(FeatureFlagError.ERRORS_WHILE_COMPUTING);
|
|
2854
2940
|
if (flagsResponse.quotaLimited?.includes("feature_flags")) errors.push(FeatureFlagError.QUOTA_LIMITED);
|
|
2855
|
-
flagDetail = flagsResponse.flags[key];
|
|
2941
|
+
const flagDetail = flagsResponse.flags[key];
|
|
2856
2942
|
if (void 0 === flagDetail) errors.push(FeatureFlagError.FLAG_MISSING);
|
|
2943
|
+
else {
|
|
2944
|
+
flagId = flagDetail.metadata?.id;
|
|
2945
|
+
flagVersion = flagDetail.metadata?.version;
|
|
2946
|
+
flagReason = flagDetail.reason?.description ?? flagDetail.reason?.code;
|
|
2947
|
+
let parsedPayload;
|
|
2948
|
+
if (flagDetail.metadata?.payload !== void 0) try {
|
|
2949
|
+
parsedPayload = JSON.parse(flagDetail.metadata.payload);
|
|
2950
|
+
} catch {
|
|
2951
|
+
parsedPayload = flagDetail.metadata.payload;
|
|
2952
|
+
}
|
|
2953
|
+
result = {
|
|
2954
|
+
key,
|
|
2955
|
+
enabled: flagDetail.enabled,
|
|
2956
|
+
variant: flagDetail.variant,
|
|
2957
|
+
payload: parsedPayload
|
|
2958
|
+
};
|
|
2959
|
+
}
|
|
2857
2960
|
if (errors.length > 0) featureFlagError = errors.join(",");
|
|
2858
|
-
response = getFeatureFlagValue(flagDetail);
|
|
2859
2961
|
}
|
|
2860
2962
|
}
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
if (
|
|
2865
|
-
|
|
2866
|
-
featureFlagReportedKey
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2963
|
+
if (sendFeatureFlagEvents) {
|
|
2964
|
+
const response = void 0 === result ? void 0 : false === result.enabled ? false : result.variant ?? true;
|
|
2965
|
+
const featureFlagReportedKey = `${key}_${response}`;
|
|
2966
|
+
if (!(distinctId in this.distinctIdHasSentFlagCalls) || !this.distinctIdHasSentFlagCalls[distinctId].includes(featureFlagReportedKey)) {
|
|
2967
|
+
if (Object.keys(this.distinctIdHasSentFlagCalls).length >= this.maxCacheSize) this.distinctIdHasSentFlagCalls = {};
|
|
2968
|
+
if (Array.isArray(this.distinctIdHasSentFlagCalls[distinctId])) this.distinctIdHasSentFlagCalls[distinctId].push(featureFlagReportedKey);
|
|
2969
|
+
else this.distinctIdHasSentFlagCalls[distinctId] = [
|
|
2970
|
+
featureFlagReportedKey
|
|
2971
|
+
];
|
|
2972
|
+
const properties = {
|
|
2973
|
+
$feature_flag: key,
|
|
2974
|
+
$feature_flag_response: response,
|
|
2975
|
+
$feature_flag_id: flagId,
|
|
2976
|
+
$feature_flag_version: flagVersion,
|
|
2977
|
+
$feature_flag_reason: flagReason,
|
|
2978
|
+
locally_evaluated: flagWasLocallyEvaluated,
|
|
2979
|
+
[`$feature/${key}`]: response,
|
|
2980
|
+
$feature_flag_request_id: requestId,
|
|
2981
|
+
$feature_flag_evaluated_at: evaluatedAt
|
|
2982
|
+
};
|
|
2983
|
+
if (featureFlagError) properties.$feature_flag_error = featureFlagError;
|
|
2984
|
+
this.capture({
|
|
2985
|
+
distinctId,
|
|
2986
|
+
event: "$feature_flag_called",
|
|
2987
|
+
properties,
|
|
2988
|
+
groups,
|
|
2989
|
+
disableGeoip
|
|
2990
|
+
});
|
|
2991
|
+
}
|
|
2887
2992
|
}
|
|
2888
|
-
|
|
2993
|
+
if (void 0 !== result && void 0 !== this._payloadOverrides && key in this._payloadOverrides) result = {
|
|
2994
|
+
...result,
|
|
2995
|
+
payload: this._payloadOverrides[key]
|
|
2996
|
+
};
|
|
2997
|
+
return result;
|
|
2998
|
+
}
|
|
2999
|
+
async getFeatureFlag(key, distinctId, options) {
|
|
3000
|
+
const result = await this._getFeatureFlagResult(key, distinctId, {
|
|
3001
|
+
...options,
|
|
3002
|
+
sendFeatureFlagEvents: options?.sendFeatureFlagEvents ?? this.options.sendFeatureFlagEvent ?? true
|
|
3003
|
+
});
|
|
3004
|
+
if (void 0 === result) return;
|
|
3005
|
+
if (false === result.enabled) return false;
|
|
3006
|
+
return result.variant ?? true;
|
|
2889
3007
|
}
|
|
2890
3008
|
async getFeatureFlagPayload(key, distinctId, matchValue, options) {
|
|
2891
3009
|
if (void 0 !== this._payloadOverrides && key in this._payloadOverrides) return this._payloadOverrides[key];
|
|
2892
|
-
const
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
if (result) {
|
|
2905
|
-
matchValue = result.value;
|
|
2906
|
-
response = result.payload;
|
|
2907
|
-
}
|
|
2908
|
-
} catch (e) {
|
|
2909
|
-
if (e instanceof RequiresServerEvaluation || e instanceof InconclusiveMatchError) this._logger?.info(`${e.name} when computing flag locally: ${flag.key}: ${e.message}`);
|
|
2910
|
-
else throw e;
|
|
2911
|
-
}
|
|
2912
|
-
}
|
|
2913
|
-
if (void 0 == onlyEvaluateLocally) onlyEvaluateLocally = false;
|
|
2914
|
-
const payloadWasLocallyEvaluated = void 0 !== response;
|
|
2915
|
-
if (!payloadWasLocallyEvaluated && !onlyEvaluateLocally) response = await super.getFeatureFlagPayloadStateless(key, distinctId, groups, personProperties, groupProperties, disableGeoip);
|
|
2916
|
-
return response;
|
|
3010
|
+
const result = await this._getFeatureFlagResult(key, distinctId, {
|
|
3011
|
+
...options,
|
|
3012
|
+
sendFeatureFlagEvents: false
|
|
3013
|
+
}, matchValue);
|
|
3014
|
+
if (void 0 === result) return;
|
|
3015
|
+
return result.payload ?? null;
|
|
3016
|
+
}
|
|
3017
|
+
async getFeatureFlagResult(key, distinctId, options) {
|
|
3018
|
+
return this._getFeatureFlagResult(key, distinctId, {
|
|
3019
|
+
...options,
|
|
3020
|
+
sendFeatureFlagEvents: options?.sendFeatureFlagEvents ?? this.options.sendFeatureFlagEvent ?? true
|
|
3021
|
+
});
|
|
2917
3022
|
}
|
|
2918
3023
|
async getRemoteConfigPayload(flagKey) {
|
|
2919
3024
|
if (!this.options.personalApiKey) throw new Error("Personal API key is required for remote config payload decryption");
|
|
@@ -2941,7 +3046,7 @@ class PostHogBackendClient extends PostHogCoreStateless {
|
|
|
2941
3046
|
const adjustedProperties = this.addLocalPersonAndGroupProperties(distinctId, groups, personProperties, groupProperties);
|
|
2942
3047
|
personProperties = adjustedProperties.allPersonProperties;
|
|
2943
3048
|
groupProperties = adjustedProperties.allGroupProperties;
|
|
2944
|
-
if (void 0 == onlyEvaluateLocally) onlyEvaluateLocally = false;
|
|
3049
|
+
if (void 0 == onlyEvaluateLocally) onlyEvaluateLocally = this.options.strictLocalEvaluation ?? false;
|
|
2945
3050
|
const localEvaluationResult = await this.featureFlagsPoller?.getAllFlagsAndPayloads(distinctId, groups, personProperties, groupProperties, flagKeys);
|
|
2946
3051
|
let featureFlags = {};
|
|
2947
3052
|
let featureFlagPayloads = {};
|
|
@@ -3091,7 +3196,7 @@ class PostHogBackendClient extends PostHogCoreStateless {
|
|
|
3091
3196
|
const finalPersonProperties = sendFeatureFlagsOptions?.personProperties || {};
|
|
3092
3197
|
const finalGroupProperties = sendFeatureFlagsOptions?.groupProperties || {};
|
|
3093
3198
|
const flagKeys = sendFeatureFlagsOptions?.flagKeys;
|
|
3094
|
-
const onlyEvaluateLocally = sendFeatureFlagsOptions?.onlyEvaluateLocally ?? false;
|
|
3199
|
+
const onlyEvaluateLocally = sendFeatureFlagsOptions?.onlyEvaluateLocally ?? this.options.strictLocalEvaluation ?? false;
|
|
3095
3200
|
if (onlyEvaluateLocally) if (!((this.featureFlagsPoller?.featureFlags?.length || 0) > 0)) return {};
|
|
3096
3201
|
else {
|
|
3097
3202
|
const groupsWithStringValues = {};
|
|
@@ -4319,6 +4424,142 @@ async function applyRouteLoaders(req, dataRoutes) {
|
|
|
4319
4424
|
statusCode
|
|
4320
4425
|
};
|
|
4321
4426
|
}
|
|
4427
|
+
const DEFAULT_MAX_QUERIES = 32;
|
|
4428
|
+
const DEFAULT_MAX_DOCS_PER_QUERY = 500;
|
|
4429
|
+
const DEFAULT_MAX_SERIALIZED_BYTES = 200 * 1024;
|
|
4430
|
+
const makeRegistrationKey = (modelName, queryKey) => `${modelName}.${queryKey}`;
|
|
4431
|
+
const hasSessionUser = (req) => {
|
|
4432
|
+
const id = req.session?.user?.id;
|
|
4433
|
+
return typeof id === "string" && id.trim().length > 0;
|
|
4434
|
+
};
|
|
4435
|
+
const escapeForInlineScript = (value) => {
|
|
4436
|
+
return value.replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
4437
|
+
};
|
|
4438
|
+
const createRtsSsrCollector = (req, opts) => {
|
|
4439
|
+
const maxQueries = DEFAULT_MAX_QUERIES;
|
|
4440
|
+
const maxDocsPerQuery = DEFAULT_MAX_DOCS_PER_QUERY;
|
|
4441
|
+
const maxSerializedBytes = DEFAULT_MAX_SERIALIZED_BYTES;
|
|
4442
|
+
const registrations = /* @__PURE__ */ new Map();
|
|
4443
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
4444
|
+
const runtime = {
|
|
4445
|
+
registerQuery(query) {
|
|
4446
|
+
const modelName = typeof query.modelName === "string" ? query.modelName.trim() : "";
|
|
4447
|
+
const queryKey = typeof query.queryKey === "string" ? query.queryKey.trim() : "";
|
|
4448
|
+
if (!modelName || !queryKey) return;
|
|
4449
|
+
const key = makeRegistrationKey(modelName, queryKey);
|
|
4450
|
+
if (registrations.has(key)) return;
|
|
4451
|
+
if (registrations.size >= maxQueries) return;
|
|
4452
|
+
registrations.set(key, {
|
|
4453
|
+
modelName,
|
|
4454
|
+
queryKey,
|
|
4455
|
+
query: query.query ?? {},
|
|
4456
|
+
options: query.options ?? {}
|
|
4457
|
+
});
|
|
4458
|
+
},
|
|
4459
|
+
getQueryData(modelName, queryKey) {
|
|
4460
|
+
return resolved.get(makeRegistrationKey(modelName, queryKey));
|
|
4461
|
+
}
|
|
4462
|
+
};
|
|
4463
|
+
const resolve = async () => {
|
|
4464
|
+
if (!registrations.size) return null;
|
|
4465
|
+
const tenantId = resolveRtsRequestTenantId(req);
|
|
4466
|
+
if (!tenantId) return null;
|
|
4467
|
+
if (hasSessionUser(req) && !isRtsRequestAuthorized(req, tenantId)) {
|
|
4468
|
+
return null;
|
|
4469
|
+
}
|
|
4470
|
+
const { ability, userId } = await buildRtsAbilityFromRequest(req, tenantId);
|
|
4471
|
+
const queryEntries = [];
|
|
4472
|
+
for (const registration of registrations.values()) {
|
|
4473
|
+
const options = normalizeRtsQueryOptions(registration.options);
|
|
4474
|
+
try {
|
|
4475
|
+
const data = await runRtsQuery({
|
|
4476
|
+
tenantId,
|
|
4477
|
+
ability,
|
|
4478
|
+
modelName: registration.modelName,
|
|
4479
|
+
query: registration.query,
|
|
4480
|
+
options
|
|
4481
|
+
});
|
|
4482
|
+
const boundedData = maxDocsPerQuery > 0 ? data.slice(0, maxDocsPerQuery) : data;
|
|
4483
|
+
queryEntries.push({
|
|
4484
|
+
modelName: registration.modelName,
|
|
4485
|
+
queryKey: registration.queryKey,
|
|
4486
|
+
data: boundedData
|
|
4487
|
+
});
|
|
4488
|
+
} catch {
|
|
4489
|
+
continue;
|
|
4490
|
+
}
|
|
4491
|
+
}
|
|
4492
|
+
if (!queryEntries.length) return null;
|
|
4493
|
+
const payload = {
|
|
4494
|
+
v: 1,
|
|
4495
|
+
tenantId,
|
|
4496
|
+
uid: userId,
|
|
4497
|
+
queries: []
|
|
4498
|
+
};
|
|
4499
|
+
for (const entry of queryEntries) {
|
|
4500
|
+
payload.queries.push(entry);
|
|
4501
|
+
const bytes = Buffer.byteLength(JSON.stringify(payload), "utf8");
|
|
4502
|
+
if (bytes > maxSerializedBytes) {
|
|
4503
|
+
payload.queries.pop();
|
|
4504
|
+
break;
|
|
4505
|
+
}
|
|
4506
|
+
}
|
|
4507
|
+
resolved.clear();
|
|
4508
|
+
for (const entry of payload.queries) {
|
|
4509
|
+
resolved.set(makeRegistrationKey(entry.modelName, entry.queryKey), entry.data);
|
|
4510
|
+
}
|
|
4511
|
+
return payload.queries.length ? payload : null;
|
|
4512
|
+
};
|
|
4513
|
+
const hasRegistrations = () => registrations.size > 0;
|
|
4514
|
+
return { runtime, hasRegistrations, resolve };
|
|
4515
|
+
};
|
|
4516
|
+
const renderRtsHydrationScript = (data) => {
|
|
4517
|
+
if (!data) return "";
|
|
4518
|
+
const serialized = escapeForInlineScript(JSON.stringify(data));
|
|
4519
|
+
return `<script>window.${STATIC_RPCBASE_RTS_HYDRATION_DATA_KEY}=${serialized};<\/script>`;
|
|
4520
|
+
};
|
|
4521
|
+
const RTS_SSR_PREPASS_TIMEOUT_MS = 4e3;
|
|
4522
|
+
const runRtsPrepass = async (element) => {
|
|
4523
|
+
return await new Promise((resolve) => {
|
|
4524
|
+
let isDone = false;
|
|
4525
|
+
let timeoutId = null;
|
|
4526
|
+
const finish = (ok) => {
|
|
4527
|
+
if (isDone) return;
|
|
4528
|
+
isDone = true;
|
|
4529
|
+
if (timeoutId) {
|
|
4530
|
+
clearTimeout(timeoutId);
|
|
4531
|
+
}
|
|
4532
|
+
resolve(ok);
|
|
4533
|
+
};
|
|
4534
|
+
const sink = new Writable({
|
|
4535
|
+
write(_chunk, _encoding, callback) {
|
|
4536
|
+
callback();
|
|
4537
|
+
}
|
|
4538
|
+
});
|
|
4539
|
+
sink.on("finish", () => {
|
|
4540
|
+
finish(true);
|
|
4541
|
+
});
|
|
4542
|
+
sink.on("error", () => {
|
|
4543
|
+
finish(false);
|
|
4544
|
+
});
|
|
4545
|
+
const { pipe, abort } = renderToPipeableStream(element, {
|
|
4546
|
+
onAllReady() {
|
|
4547
|
+
if (isDone) return;
|
|
4548
|
+
pipe(sink);
|
|
4549
|
+
},
|
|
4550
|
+
onShellError() {
|
|
4551
|
+
finish(false);
|
|
4552
|
+
abort();
|
|
4553
|
+
},
|
|
4554
|
+
onError() {
|
|
4555
|
+
}
|
|
4556
|
+
});
|
|
4557
|
+
timeoutId = setTimeout(() => {
|
|
4558
|
+
abort();
|
|
4559
|
+
finish(false);
|
|
4560
|
+
}, RTS_SSR_PREPASS_TIMEOUT_MS);
|
|
4561
|
+
});
|
|
4562
|
+
};
|
|
4322
4563
|
async function renderSSR(req, dataRoutes) {
|
|
4323
4564
|
const routerContext = await applyRouteLoaders(req, dataRoutes);
|
|
4324
4565
|
const isMatched = routerContext.matches.length > 0;
|
|
@@ -4329,7 +4570,8 @@ async function renderSSR(req, dataRoutes) {
|
|
|
4329
4570
|
statusCode: routerContext.statusCode ?? routerContext.redirectResponse.status ?? 302,
|
|
4330
4571
|
redirectResponse: routerContext.redirectResponse,
|
|
4331
4572
|
redirectRouteId: routerContext.redirectRouteId,
|
|
4332
|
-
redirectRoutePath: routerContext.redirectRoutePath
|
|
4573
|
+
redirectRoutePath: routerContext.redirectRoutePath,
|
|
4574
|
+
rtsHydrationData: null
|
|
4333
4575
|
};
|
|
4334
4576
|
}
|
|
4335
4577
|
if (routerContext.errors) {
|
|
@@ -4364,18 +4606,34 @@ async function renderSSR(req, dataRoutes) {
|
|
|
4364
4606
|
throw error;
|
|
4365
4607
|
}
|
|
4366
4608
|
}
|
|
4367
|
-
const
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4609
|
+
const buildElement = (runtime2) => {
|
|
4610
|
+
const router = createStaticRouter(dataRoutes, routerContext);
|
|
4611
|
+
const app = /* @__PURE__ */ jsx(
|
|
4612
|
+
StaticRouterProvider,
|
|
4613
|
+
{
|
|
4614
|
+
router,
|
|
4615
|
+
context: routerContext
|
|
4616
|
+
}
|
|
4617
|
+
);
|
|
4618
|
+
return /* @__PURE__ */ jsx(StrictMode, { children: runtime2 ? /* @__PURE__ */ jsx(RtsSsrRuntimeProvider, { value: runtime2, children: app }) : app });
|
|
4619
|
+
};
|
|
4620
|
+
const tenantId = resolveRtsRequestTenantId(req);
|
|
4621
|
+
let rtsHydrationData = null;
|
|
4622
|
+
let runtime = null;
|
|
4623
|
+
if (tenantId) {
|
|
4624
|
+
const collector = createRtsSsrCollector(req);
|
|
4625
|
+
const prepassOk = await runRtsPrepass(buildElement(collector.runtime));
|
|
4626
|
+
if (prepassOk && collector.hasRegistrations()) {
|
|
4627
|
+
rtsHydrationData = await collector.resolve();
|
|
4373
4628
|
}
|
|
4374
|
-
|
|
4629
|
+
runtime = collector.runtime;
|
|
4630
|
+
}
|
|
4631
|
+
const element = buildElement(runtime);
|
|
4375
4632
|
return {
|
|
4376
4633
|
element,
|
|
4377
4634
|
isMatched,
|
|
4378
|
-
statusCode: routerContext.statusCode ?? (routerContext.errors ? 500 : 200)
|
|
4635
|
+
statusCode: routerContext.statusCode ?? (routerContext.errors ? 500 : 200),
|
|
4636
|
+
rtsHydrationData
|
|
4379
4637
|
};
|
|
4380
4638
|
}
|
|
4381
4639
|
const ABORT_DELAY_MS = 1e4;
|
|
@@ -4526,6 +4784,7 @@ const ssrMiddleware = ({
|
|
|
4526
4784
|
element,
|
|
4527
4785
|
isMatched,
|
|
4528
4786
|
statusCode,
|
|
4787
|
+
rtsHydrationData,
|
|
4529
4788
|
redirectResponse,
|
|
4530
4789
|
redirectRouteId,
|
|
4531
4790
|
redirectRoutePath
|
|
@@ -4593,9 +4852,10 @@ const ssrMiddleware = ({
|
|
|
4593
4852
|
});
|
|
4594
4853
|
const start = htmlStart;
|
|
4595
4854
|
const end = htmlEnd;
|
|
4855
|
+
const rtsHydrationScript = renderRtsHydrationScript(rtsHydrationData);
|
|
4596
4856
|
res.write(start);
|
|
4597
4857
|
transformStream.on("finish", () => {
|
|
4598
|
-
res.end(end);
|
|
4858
|
+
res.end(`${rtsHydrationScript}${end}`);
|
|
4599
4859
|
});
|
|
4600
4860
|
pipe(transformStream);
|
|
4601
4861
|
}
|