@active-reach/web-sdk 1.10.0 → 1.11.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/aegis.min.js +1 -1
- package/dist/aegis.min.js.map +1 -1
- package/dist/{analytics-Cc4-QQBf.mjs → analytics-6PR9ERDS.mjs} +190 -4
- package/dist/analytics-6PR9ERDS.mjs.map +1 -0
- package/dist/core/analytics.d.ts +1 -0
- package/dist/core/analytics.d.ts.map +1 -1
- package/dist/governance/index.d.ts +2 -0
- package/dist/governance/index.d.ts.map +1 -1
- package/dist/governance/trait-governor.d.ts +63 -0
- package/dist/governance/trait-governor.d.ts.map +1 -0
- package/dist/index.js +234 -10
- package/dist/index.js.map +1 -1
- package/dist/react.js +1 -1
- package/dist/runtime/AegisMessageRuntime.d.ts +0 -1
- package/dist/runtime/AegisMessageRuntime.d.ts.map +1 -1
- package/dist/state/intent_ledger.d.ts +136 -0
- package/dist/state/intent_ledger.d.ts.map +1 -0
- package/dist/triggers/IntentRuleEvaluator.d.ts +167 -0
- package/dist/triggers/IntentRuleEvaluator.d.ts.map +1 -0
- package/dist/triggers/IntentSnapshotCollector.d.ts +83 -0
- package/dist/triggers/IntentSnapshotCollector.d.ts.map +1 -0
- package/dist/triggers/SelectorBinder.d.ts +118 -0
- package/dist/triggers/SelectorBinder.d.ts.map +1 -0
- package/dist/triggers/TriggerEngine.d.ts +177 -0
- package/dist/triggers/TriggerEngine.d.ts.map +1 -1
- package/dist/triggers/index.d.ts +7 -1
- package/dist/triggers/index.d.ts.map +1 -1
- package/dist/widgets/AegisWidgetManager.d.ts +6 -0
- package/dist/widgets/AegisWidgetManager.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/analytics-Cc4-QQBf.mjs.map +0 -1
|
@@ -2034,6 +2034,187 @@ class NameGovernor {
|
|
|
2034
2034
|
};
|
|
2035
2035
|
}
|
|
2036
2036
|
}
|
|
2037
|
+
const RESERVED_PREFIXES = [
|
|
2038
|
+
"system.",
|
|
2039
|
+
"user.",
|
|
2040
|
+
"loyalty.",
|
|
2041
|
+
"review.",
|
|
2042
|
+
"cart.",
|
|
2043
|
+
"checkout.",
|
|
2044
|
+
"product.",
|
|
2045
|
+
"pos.",
|
|
2046
|
+
"bill.",
|
|
2047
|
+
"feedback.",
|
|
2048
|
+
"chat.",
|
|
2049
|
+
"delivery.",
|
|
2050
|
+
"event.",
|
|
2051
|
+
"$",
|
|
2052
|
+
"_"
|
|
2053
|
+
];
|
|
2054
|
+
const KEY_SOFT_VALUE_CAP = 512;
|
|
2055
|
+
const KEY_HARD_VALUE_CAP = 1e4;
|
|
2056
|
+
const CAMEL_BOUNDARY_1 = /(.)([A-Z][a-z]+)/g;
|
|
2057
|
+
const CAMEL_BOUNDARY_2 = /([a-z0-9])([A-Z])/g;
|
|
2058
|
+
const NON_SNAKE = /[\s\-.]+/g;
|
|
2059
|
+
const DUPE_UNDERSCORE = /_+/g;
|
|
2060
|
+
const ISO_8601 = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:?\d{2})?)?$/;
|
|
2061
|
+
const DATE_KEY_HINTS = /* @__PURE__ */ new Set([
|
|
2062
|
+
"at",
|
|
2063
|
+
"on",
|
|
2064
|
+
"date",
|
|
2065
|
+
"time",
|
|
2066
|
+
"timestamp",
|
|
2067
|
+
"dob",
|
|
2068
|
+
"birthday",
|
|
2069
|
+
"joined",
|
|
2070
|
+
"expired",
|
|
2071
|
+
"expires",
|
|
2072
|
+
"created",
|
|
2073
|
+
"updated",
|
|
2074
|
+
"started",
|
|
2075
|
+
"ended"
|
|
2076
|
+
]);
|
|
2077
|
+
function normalizeKey(key) {
|
|
2078
|
+
if (!key) return null;
|
|
2079
|
+
const stage1 = key.replace(CAMEL_BOUNDARY_1, "$1_$2").replace(CAMEL_BOUNDARY_2, "$1_$2");
|
|
2080
|
+
const stage2 = stage1.replace(NON_SNAKE, "_").toLowerCase();
|
|
2081
|
+
const result = stage2.replace(DUPE_UNDERSCORE, "_").replace(/^_+|_+$/g, "");
|
|
2082
|
+
return result || null;
|
|
2083
|
+
}
|
|
2084
|
+
function startsWithReserved(key) {
|
|
2085
|
+
const lower = key.toLowerCase();
|
|
2086
|
+
for (const prefix of RESERVED_PREFIXES) {
|
|
2087
|
+
if (lower.startsWith(prefix)) return prefix;
|
|
2088
|
+
}
|
|
2089
|
+
return null;
|
|
2090
|
+
}
|
|
2091
|
+
function looksLikeDateKey(key) {
|
|
2092
|
+
for (const part of key.toLowerCase().split(/[_\-.\s]+/)) {
|
|
2093
|
+
if (DATE_KEY_HINTS.has(part)) return true;
|
|
2094
|
+
}
|
|
2095
|
+
return false;
|
|
2096
|
+
}
|
|
2097
|
+
function parseDateValue(value) {
|
|
2098
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
2099
|
+
return value < 1e11 ? Math.floor(value * 1e3) : Math.floor(value);
|
|
2100
|
+
}
|
|
2101
|
+
if (typeof value === "string" && value) {
|
|
2102
|
+
const s = value.trim();
|
|
2103
|
+
if (ISO_8601.test(s)) {
|
|
2104
|
+
const ms = Date.parse(s);
|
|
2105
|
+
if (!Number.isNaN(ms)) return ms;
|
|
2106
|
+
}
|
|
2107
|
+
const asNum = Number(s);
|
|
2108
|
+
if (Number.isFinite(asNum)) {
|
|
2109
|
+
return asNum < 1e11 ? Math.floor(asNum * 1e3) : Math.floor(asNum);
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
return null;
|
|
2113
|
+
}
|
|
2114
|
+
function warnOnConsole(message) {
|
|
2115
|
+
if (typeof console === "undefined" || typeof console.warn !== "function") return;
|
|
2116
|
+
console.warn(message);
|
|
2117
|
+
}
|
|
2118
|
+
const _TraitGovernor = class _TraitGovernor {
|
|
2119
|
+
constructor() {
|
|
2120
|
+
this.warnCounts = /* @__PURE__ */ new Map();
|
|
2121
|
+
}
|
|
2122
|
+
/**
|
|
2123
|
+
* Apply all guards to a traits object. Returns the sanitized payload
|
|
2124
|
+
* to send + a list of drops/modifications.
|
|
2125
|
+
*
|
|
2126
|
+
* Pass `workspace_id` so warning rate-limits scope per workspace; an
|
|
2127
|
+
* agency user switching workspaces won't have their warning budget
|
|
2128
|
+
* consumed cross-workspace.
|
|
2129
|
+
*/
|
|
2130
|
+
process(traits, workspace_id) {
|
|
2131
|
+
const sanitized = {};
|
|
2132
|
+
const drops = [];
|
|
2133
|
+
if (!traits || typeof traits !== "object") {
|
|
2134
|
+
return { sanitized, drops };
|
|
2135
|
+
}
|
|
2136
|
+
for (const rawKey of Object.keys(traits)) {
|
|
2137
|
+
const prefix = startsWithReserved(rawKey);
|
|
2138
|
+
if (prefix !== null) {
|
|
2139
|
+
drops.push({
|
|
2140
|
+
originalKey: rawKey,
|
|
2141
|
+
verdict: "reserved_prefix",
|
|
2142
|
+
reason: `key uses reserved namespace ${JSON.stringify(prefix)}`
|
|
2143
|
+
});
|
|
2144
|
+
continue;
|
|
2145
|
+
}
|
|
2146
|
+
const normalizedKey = normalizeKey(rawKey);
|
|
2147
|
+
if (normalizedKey === null) {
|
|
2148
|
+
drops.push({
|
|
2149
|
+
originalKey: rawKey,
|
|
2150
|
+
verdict: "bad_key_format",
|
|
2151
|
+
reason: `key reduced to empty after normalization`
|
|
2152
|
+
});
|
|
2153
|
+
continue;
|
|
2154
|
+
}
|
|
2155
|
+
if (startsWithReserved(normalizedKey) !== null) {
|
|
2156
|
+
drops.push({
|
|
2157
|
+
originalKey: rawKey,
|
|
2158
|
+
verdict: "reserved_prefix",
|
|
2159
|
+
reason: `normalized key ${JSON.stringify(normalizedKey)} still uses a reserved namespace`
|
|
2160
|
+
});
|
|
2161
|
+
continue;
|
|
2162
|
+
}
|
|
2163
|
+
let value = traits[rawKey];
|
|
2164
|
+
if (typeof value === "string") {
|
|
2165
|
+
if (value.length > KEY_HARD_VALUE_CAP) {
|
|
2166
|
+
drops.push({
|
|
2167
|
+
originalKey: rawKey,
|
|
2168
|
+
verdict: "value_too_long",
|
|
2169
|
+
reason: `value length ${value.length} exceeds hard cap ${KEY_HARD_VALUE_CAP}`
|
|
2170
|
+
});
|
|
2171
|
+
continue;
|
|
2172
|
+
}
|
|
2173
|
+
if (value.length > KEY_SOFT_VALUE_CAP) {
|
|
2174
|
+
drops.push({
|
|
2175
|
+
originalKey: rawKey,
|
|
2176
|
+
verdict: "value_too_long",
|
|
2177
|
+
reason: `value truncated from ${value.length} to ${KEY_SOFT_VALUE_CAP} chars`
|
|
2178
|
+
});
|
|
2179
|
+
value = value.slice(0, KEY_SOFT_VALUE_CAP);
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
if (looksLikeDateKey(normalizedKey) && typeof value === "string") {
|
|
2183
|
+
const parsed = parseDateValue(value);
|
|
2184
|
+
if (parsed === null) {
|
|
2185
|
+
drops.push({
|
|
2186
|
+
originalKey: rawKey,
|
|
2187
|
+
verdict: "bad_date_format",
|
|
2188
|
+
reason: `value ${JSON.stringify(value)} on date-keyed field ${JSON.stringify(normalizedKey)} didn't parse as ISO-8601 / epoch`
|
|
2189
|
+
});
|
|
2190
|
+
continue;
|
|
2191
|
+
}
|
|
2192
|
+
value = parsed;
|
|
2193
|
+
}
|
|
2194
|
+
sanitized[normalizedKey] = value;
|
|
2195
|
+
}
|
|
2196
|
+
for (const drop of drops) {
|
|
2197
|
+
this.maybeWarn(workspace_id ?? null, drop);
|
|
2198
|
+
}
|
|
2199
|
+
return { sanitized, drops };
|
|
2200
|
+
}
|
|
2201
|
+
maybeWarn(workspace_id, drop) {
|
|
2202
|
+
const ws = workspace_id ?? "__no_workspace__";
|
|
2203
|
+
let perVerdict = this.warnCounts.get(ws);
|
|
2204
|
+
if (!perVerdict) {
|
|
2205
|
+
perVerdict = /* @__PURE__ */ new Map();
|
|
2206
|
+
this.warnCounts.set(ws, perVerdict);
|
|
2207
|
+
}
|
|
2208
|
+
const count = perVerdict.get(drop.verdict) ?? 0;
|
|
2209
|
+
if (count >= _TraitGovernor.WARN_CAP) return;
|
|
2210
|
+
perVerdict.set(drop.verdict, count + 1);
|
|
2211
|
+
warnOnConsole(
|
|
2212
|
+
`[Aegis SDK] trait ${drop.verdict}: ${drop.reason} (original key: ${JSON.stringify(drop.originalKey)}). Backend will reject; fix the SDK call to silence this warning.`
|
|
2213
|
+
);
|
|
2214
|
+
}
|
|
2215
|
+
};
|
|
2216
|
+
_TraitGovernor.WARN_CAP = 3;
|
|
2217
|
+
let TraitGovernor = _TraitGovernor;
|
|
2037
2218
|
class Aegis {
|
|
2038
2219
|
constructor() {
|
|
2039
2220
|
this.config = null;
|
|
@@ -2046,6 +2227,7 @@ class Aegis {
|
|
|
2046
2227
|
this._ecommerce = null;
|
|
2047
2228
|
this.rateLimiter = null;
|
|
2048
2229
|
this.nameGovernor = new NameGovernor();
|
|
2230
|
+
this.traitGovernor = new TraitGovernor();
|
|
2049
2231
|
this._lastPageUrl = null;
|
|
2050
2232
|
this._popstateHandler = null;
|
|
2051
2233
|
this._originalPushState = null;
|
|
@@ -2238,10 +2420,12 @@ class Aegis {
|
|
|
2238
2420
|
}
|
|
2239
2421
|
identify(userId, traits) {
|
|
2240
2422
|
if (!this.assertInitialized()) return;
|
|
2241
|
-
this.
|
|
2423
|
+
const wsForGovernor = this.config.workspace_id || null;
|
|
2424
|
+
const { sanitized: governedTraits } = this.traitGovernor.process(traits, wsForGovernor);
|
|
2425
|
+
this.identity.setUserId(userId, governedTraits);
|
|
2242
2426
|
const event = {
|
|
2243
2427
|
type: "identify",
|
|
2244
|
-
traits:
|
|
2428
|
+
traits: governedTraits,
|
|
2245
2429
|
messageId: generateMessageId(),
|
|
2246
2430
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2247
2431
|
anonymousId: this.identity.getAnonymousId(),
|
|
@@ -2270,10 +2454,12 @@ class Aegis {
|
|
|
2270
2454
|
}
|
|
2271
2455
|
group(groupId, traits) {
|
|
2272
2456
|
if (!this.assertInitialized()) return;
|
|
2457
|
+
const wsForGovernor = this.config.workspace_id || null;
|
|
2458
|
+
const { sanitized: governedTraits } = this.traitGovernor.process(traits, wsForGovernor);
|
|
2273
2459
|
const event = {
|
|
2274
2460
|
type: "group",
|
|
2275
2461
|
groupId,
|
|
2276
|
-
traits:
|
|
2462
|
+
traits: governedTraits,
|
|
2277
2463
|
messageId: generateMessageId(),
|
|
2278
2464
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2279
2465
|
anonymousId: this.identity.getAnonymousId(),
|
|
@@ -2597,4 +2783,4 @@ export {
|
|
|
2597
2783
|
logger as l,
|
|
2598
2784
|
murmurhash3_x86_32 as m
|
|
2599
2785
|
};
|
|
2600
|
-
//# sourceMappingURL=analytics-
|
|
2786
|
+
//# sourceMappingURL=analytics-6PR9ERDS.mjs.map
|