@lumiastream/lumia-types 3.9.0 → 3.9.2

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.
@@ -69,4 +69,67 @@ These are the built-in `type` values for each system base.
69
69
 
70
70
  > Tip: most of the `lumia` and `overlay` actions already have dedicated helper functions (`tts`, `chatbot`, `overlaySetTextContent`, etc.) in `helper-functions.md`. Reach for `actions()` mainly when you need an integration action that does not have a helper yet.
71
71
 
72
+ ### Common `lumia` action examples
73
+
74
+ Most `lumia` actions have a dedicated helper already (`chatbot`, `tts`, `playAudio`, `setVariable`, etc.) — prefer those. Reach for `actions()` for the engagement / system features that have no helper. The `value` fields are not uniform across types, so if you need a type not shown here, set that action up once in the normal action editor to read off its fields. Verified examples:
75
+
76
+ ```js
77
+ async function() {
78
+ // Song requests
79
+ await actions([{ base: "lumia", type: "addSongRequest", value: { value: "never gonna give you up" } }]); // a search term or url
80
+ await actions([{ base: "lumia", type: "skipSongRequest" }]);
81
+
82
+ // Raffle (ends_after is in seconds; raffleStop / raffleEnd take no value)
83
+ await actions([{ base: "lumia", type: "raffleStart", value: { title: "My Raffle", auto_end: true, ends_after: 120 } }]);
84
+
85
+ // Viewer queue
86
+ await actions([{ base: "lumia", type: "viewerQueueEntry", value: { value: "{{username}}" } }]);
87
+
88
+ // Stream mode and connections
89
+ await actions([{ base: "lumia", type: "setStreamMode", value: { on: true } }]);
90
+ await actions([{ base: "lumia", type: "setConnection", value: { value: "obs", on: true } }]); // on:true enables, false disables
91
+
92
+ // Counter (operator is one of + - * /)
93
+ await actions([{ base: "lumia", type: "updateCounter", value: { value: "deaths", message: "1", operator: "+" } }]);
94
+ done();
95
+ }
96
+ ```
97
+
98
+ ### OBS (v5) actions
99
+
100
+ OBS has a dedicated helper, so you do **not** need `actions()` for it: pass any OBS websocket v5 request to `sendRawObsJson({ "request-type": "...", ...params })` (see `helper-functions.md`). The `request-type` and fields are exactly the same ones Lumia's OBS action editor uses. Common ones:
101
+
102
+ ```js
103
+ async function() {
104
+ // Scenes
105
+ sendRawObsJson({ "request-type": "SetCurrentProgramScene", "sceneName": "My Scene" });
106
+ sendRawObsJson({ "request-type": "SetCurrentPreviewScene", "sceneName": "My Scene" });
107
+
108
+ // Source visibility (by name — Lumia resolves the scene item id for you)
109
+ sendRawObsJson({ "request-type": "SetSceneItemEnabled", "sceneName": "My Scene", "inputName": "My Source", "sceneItemEnabled": true });
110
+ sendRawObsJson({ "request-type": "SetSourceFilterEnabled", "sourceName": "My Source", "filterName": "My Filter", "filterEnabled": true });
111
+
112
+ // Audio
113
+ sendRawObsJson({ "request-type": "SetInputMute", "inputName": "Mic/Aux", "inputMuted": true });
114
+ sendRawObsJson({ "request-type": "ToggleInputMute", "inputName": "Mic/Aux" });
115
+ sendRawObsJson({ "request-type": "SetInputVolume", "inputName": "Mic/Aux", "inputVolumeMul": 1 });
116
+
117
+ // Source settings (url / text / file / image / media)
118
+ sendRawObsJson({ "request-type": "SetInputSettings", "inputName": "Browser", "inputSettings": { "url": "https://lumiastream.com" } });
119
+
120
+ // Media, transitions, screenshot
121
+ sendRawObsJson({ "request-type": "TriggerMediaInputAction", "inputName": "My Video", "mediaAction": "OBS_WEBSOCKET_MEDIA_INPUT_ACTION_RESTART" });
122
+ sendRawObsJson({ "request-type": "SetCurrentSceneTransition", "transitionName": "Fade" });
123
+
124
+ // Recording / streaming / replay buffer / virtual cam (no params)
125
+ sendRawObsJson({ "request-type": "StartRecord" });
126
+ sendRawObsJson({ "request-type": "ToggleStream" });
127
+ sendRawObsJson({ "request-type": "SaveReplayBuffer" });
128
+ sendRawObsJson({ "request-type": "StartVirtualCam" });
129
+ done();
130
+ }
131
+ ```
132
+
133
+ Other available request-types include `SetCurrentProfile`, `SetCurrentSceneCollection`, `CreateInput`, `RemoveInput`, `SetSceneItemTransform`, `SetSceneItemBlendMode`, `SetInputAudioMonitorType`, `TriggerHotkeyByName`, `SetStudioModeEnabled`, `SendStreamCaption`, and the `Start/Stop/Toggle` variants for recording, streaming, replay buffer and virtual cam — the same list as the OBS action editor.
134
+
72
135
  This documentation can get extremely broad for every integration, so if you get stuck please visit our [**Discord**](https://discord.gg/R8rCaKb) to ask us any questions.
@@ -27,3 +27,31 @@ async function() {
27
27
  **code blocks** like the one above 👆 have a copy button on the **top right corner** click it then paste in Lumia stream
28
28
 
29
29
  :::
30
+
31
+ ## Show an OBS source and play a sound
32
+
33
+ You can drive OBS and audio straight from the code with the built-in helpers — there is no need to make a separate command and call it by an id. This rolls a dice, shows the matching `Dice1`–`Dice6` source in your scene, plays a sound on a 6, then hides the source again.
34
+
35
+ ```js
36
+ async function() {
37
+ const roll = Math.ceil(Math.random() * 6);
38
+ const sceneName = "IN-GAME ACTION";
39
+ const sourceName = "Dice" + roll;
40
+
41
+ chatbot({ message: "{{username}} rolled a " + roll + "!" });
42
+
43
+ // Show the matching dice source. Lumia finds the source's id from its name for you
44
+ sendRawObsJson({ "request-type": "SetSceneItemEnabled", "sceneName": sceneName, "inputName": sourceName, "sceneItemEnabled": true });
45
+
46
+ // Play a celebration sound only on a 6
47
+ if (roll === 6) {
48
+ playAudio({ path: "C:\\sounds\\gold.mp3", volume: 100 });
49
+ }
50
+
51
+ // Leave it on screen for 6 seconds, then hide it again
52
+ await delay(6000);
53
+ sendRawObsJson({ "request-type": "SetSceneItemEnabled", "sceneName": sceneName, "inputName": sourceName, "sceneItemEnabled": false });
54
+
55
+ done();
56
+ }
57
+ ```
@@ -41,6 +41,16 @@ You generate **Lumia Stream Custom Code**: JavaScript snippets that a streamer p
41
41
  8. For `callAlert`, the `name` must be a valid alert key from the list in `helper-functions.md` (e.g. `twitch-subscriber`, `kick-follower`, `kofi-donation`). Do not guess keys that aren't on that list.
42
42
  9. If a capability is not documented, say so clearly and offer a documented alternative rather than fabricating an API.
43
43
 
44
+ ## Use built-in helpers directly — never command IDs
45
+
46
+ Run actions straight from the code with the documented helpers. Do **not** tell the streamer to first build a separate Lumia Command/Alert and then call it by an ID, and never output placeholder IDs like `REPLACE_WITH_..._COMMAND_ID`. There is **no** `executeCommand` global. `callCommand`, `callChatbotCommand` and `callAlert` take a **name** and only trigger something the streamer has *already* created — they are not a way to change scenes or play sounds.
47
+
48
+ - **OBS scene change:** `sendRawObsJson({ "request-type": "SetCurrentProgramScene", "sceneName": "My Scene" })`
49
+ - **OBS show/hide a source (by name):** `sendRawObsJson({ "request-type": "SetSceneItemEnabled", "sceneName": "My Scene", "inputName": "My Source", "sceneItemEnabled": true })` — set `sceneItemEnabled: false` to hide. Lumia looks up the source's id from the name for you, so you never need a numeric `sceneItemId`.
50
+ - **Any other OBS request:** the same pattern through `sendRawObsJson` (for example `SetInputMute`, `TriggerMediaInputAction`, `SetCurrentSceneTransition`).
51
+ - **Play audio:** `playAudio({ path: "C:\\sounds\\gold.mp3", volume: 100 })` — a URL works too and `playSound` is an alias. Use `await playAudio({ ..., waitForAudioToStop: true })` to wait for it to finish.
52
+ - For an integration action that has no dedicated helper, use `actions([...])` (see `custom-actions.md`).
53
+
44
54
  ## Quality checklist before returning code
45
55
 
46
56
  - Wrapped in `async function() { ... }` and calls `done()` once.
@@ -456,16 +456,21 @@ async function() {
456
456
 
457
457
  ### Send Raw OBS JSON
458
458
 
459
- `sendRawObsJson(value: { request-type: string; sceneName?: string; sceneItemId?: number; [key: string]: any })`: You can send raw JSON to OBS that will automatically handle the context id. Just send end the request type and your other parameters and Lumia Stream will take care of the rest
459
+ `sendRawObsJson(value: { request-type: string; sceneName?: string; inputName?: string; sceneItemId?: number; [key: string]: any })`: You can send raw JSON to OBS that will automatically handle the context id. Just send the request type and your other parameters and Lumia Stream will take care of the rest. This is the simplest way to drive OBS from custom code — you do not need to build a separate OBS command or alert and call it by id.
460
460
 
461
461
  ```js
462
462
  async function() {
463
- sendRawObsJson({
464
- "request-type": "SetSceneItemEnabled",
465
- "sceneItemEnabled": true,
466
- "sceneItemId": 1,
467
- "sceneName": "Scene 1"
468
- });
463
+ // Change the active scene
464
+ sendRawObsJson({ "request-type": "SetCurrentProgramScene", "sceneName": "My Scene" });
465
+
466
+ // Show a source by its name. Lumia resolves the scene item id from the name for you, so no numeric id is needed
467
+ sendRawObsJson({ "request-type": "SetSceneItemEnabled", "sceneName": "My Scene", "inputName": "My Source", "sceneItemEnabled": true });
468
+
469
+ // Hide that same source again by passing false
470
+ sendRawObsJson({ "request-type": "SetSceneItemEnabled", "sceneName": "My Scene", "inputName": "My Source", "sceneItemEnabled": false });
471
+
472
+ // You can still target a source by its numeric sceneItemId if you already know it
473
+ sendRawObsJson({ "request-type": "SetSceneItemEnabled", "sceneName": "Scene 1", "sceneItemId": 1, "sceneItemEnabled": true });
469
474
  }
470
475
  ```
471
476
 
package/dist/esm/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  export { LumiaStreamingSites, LumiaActivityCommandTypes, LumiaExternalActivityCommandTypes, LumiaAlertValues, LumiaAlertFriendlyValues, LumiaActivityOriginTypes, LumiaActivityApiValueType, LumiaActivityNoValueTypes, LumiaActivityTestType, } from './activity.types.js';
2
2
  export { LumiaVariationConditions, LumiaVariationCurrency, VariationCurrencySymbol, LumiaRedemptionCurrency, LumiaRedemptionCurrencySymbol, LumiaAlertConfigs, } from './alert.types.js';
3
+ export { checkAlertVariation, resetVariationCumulativeTracking, sanitizeVariationVariableReference, } from './variation.helpers.js';
3
4
  export { LumiaIntegrations, LumiaEventTypes, } from './event.types.js';
4
5
  export { LumiaEventListTypes, LumiaMapAlertTypeToEventListType, AlertsToFilter, PlatformsToFilter, LumiaEventListTypeColors, getEventListCategoryColor } from './eventlist.types.js';
5
6
  export { SystemVariables, ReservedVariables, AllVariables, getAcceptedVariableName, getAcceptedVariableNames, } from './variables.types.js';
@@ -0,0 +1,432 @@
1
+ import { LumiaVariationConditions, LumiaVariationCurrency } from './alert.types.js';
2
+ const lastCountMultipleIdxByKey = new Map();
3
+ const subscriptionOrderNumbers = {
4
+ [LumiaVariationConditions.GIFT_SUB_EQUAL]: 1,
5
+ [LumiaVariationConditions.GIFT_SUB_GREATER]: 2,
6
+ [LumiaVariationConditions.IS_GIFT]: 3,
7
+ [LumiaVariationConditions.SUBSCRIBED_MONTHS_EQUAL]: 4,
8
+ [LumiaVariationConditions.SUBSCRIBED_MONTHS_GREATER]: 5,
9
+ [LumiaVariationConditions.IS_PRIME]: 6,
10
+ [LumiaVariationConditions.EQUAL_SELECTION]: 7,
11
+ [LumiaVariationConditions.RANDOM]: 8,
12
+ };
13
+ const variationBucketTypes = {
14
+ equals: [
15
+ LumiaVariationConditions.EQUAL_NUMBER,
16
+ LumiaVariationConditions.EQUAL_VARIABLE,
17
+ LumiaVariationConditions.EQUAL_CURRENCY_NUMBER,
18
+ LumiaVariationConditions.EQUAL_SELECTION,
19
+ LumiaVariationConditions.EQUAL_STRING,
20
+ LumiaVariationConditions.EQUAL_USERNAME,
21
+ LumiaVariationConditions.EQUAL_USER_LEVEL,
22
+ LumiaVariationConditions.SUBSCRIBED_MONTHS_EQUAL,
23
+ LumiaVariationConditions.GIFT_SUB_EQUAL,
24
+ LumiaVariationConditions.COUNT_IS_MULTIPLE_OF,
25
+ ],
26
+ greaters: [
27
+ LumiaVariationConditions.GREATER_NUMBER,
28
+ LumiaVariationConditions.GREATER_CURRENCY_NUMBER,
29
+ LumiaVariationConditions.SUBSCRIBED_MONTHS_GREATER,
30
+ LumiaVariationConditions.GIFT_SUB_GREATER,
31
+ ],
32
+ lessers: [LumiaVariationConditions.LESS_NUMBER],
33
+ checks: [LumiaVariationConditions.IS_GIFT, LumiaVariationConditions.IS_PRIME],
34
+ randoms: [LumiaVariationConditions.RANDOM],
35
+ };
36
+ export const sanitizeVariationVariableReference = (value) => (value ? value.trim().replace(/[={}]/g, '') : '');
37
+ const isVariationOnly = (config) => Boolean(config.onlyVariation || config.disableBaseAlert);
38
+ const isBaseAlertEnabled = (config, options) => { var _a, _b; return (_b = (_a = options.isBaseAlertEnabled) === null || _a === void 0 ? void 0 : _a.call(options, config)) !== null && _b !== void 0 ? _b : !isVariationOnly(config); };
39
+ const isVariationEnabled = (variation, config, options) => { var _a, _b; return (_b = (_a = options.isVariationEnabled) === null || _a === void 0 ? void 0 : _a.call(options, variation, config)) !== null && _b !== void 0 ? _b : variation.on !== false; };
40
+ const safeParse = (value, floor = false) => {
41
+ if (typeof value === 'number') {
42
+ return floor ? Math.floor(value) : value;
43
+ }
44
+ if (typeof value === 'boolean' || value === undefined || value === null) {
45
+ return value;
46
+ }
47
+ if (value.includes('.')) {
48
+ const parsedValue = parseFloat(value);
49
+ return floor ? Math.floor(parsedValue) : parsedValue;
50
+ }
51
+ const parsedValue = parseInt(value);
52
+ return floor ? Math.floor(parsedValue) : parsedValue;
53
+ };
54
+ const toFiniteNumber = (value) => {
55
+ if (typeof value === 'number') {
56
+ return Number.isFinite(value) ? value : undefined;
57
+ }
58
+ if (typeof value === 'string') {
59
+ const trimmedValue = value.trim();
60
+ if (!trimmedValue.length) {
61
+ return undefined;
62
+ }
63
+ const parsedValue = Number(trimmedValue);
64
+ return Number.isFinite(parsedValue) ? parsedValue : undefined;
65
+ }
66
+ return undefined;
67
+ };
68
+ const getRandomNumber = (min = 0, max = 255, random = Math.random) => Math.floor(random() * (max - min + 1) + min);
69
+ const rollChance = (percent = 100, random = Math.random) => getRandomNumber(0, 100, random) < percent;
70
+ const shuffle = (array, random = Math.random) => {
71
+ const newArray = [...array];
72
+ const startIndex = newArray.length;
73
+ let randomIndex = Math.floor(random() * startIndex);
74
+ for (let currentIndex = startIndex - 1; currentIndex >= 1; currentIndex--) {
75
+ [newArray[currentIndex], newArray[randomIndex]] = [newArray[randomIndex], newArray[currentIndex]];
76
+ randomIndex = Math.floor(random() * currentIndex);
77
+ }
78
+ return newArray;
79
+ };
80
+ const compareVariationValue = ({ expected, actual, caseSensitive }) => {
81
+ if (expected === undefined || expected === null || (typeof expected === 'string' && expected.trim().length === 0)) {
82
+ return actual !== undefined && actual !== null;
83
+ }
84
+ const expectedNumber = toFiniteNumber(expected);
85
+ const actualNumber = toFiniteNumber(actual);
86
+ if (expectedNumber !== undefined && actualNumber !== undefined) {
87
+ return expectedNumber === actualNumber;
88
+ }
89
+ const expectedString = `${expected}`;
90
+ const actualString = actual === undefined || actual === null ? '' : `${actual}`;
91
+ if (caseSensitive ? expectedString === actualString : expectedString.toLowerCase() === actualString.toLowerCase()) {
92
+ return true;
93
+ }
94
+ return expected == actual;
95
+ };
96
+ const getVariableValueFromCondition = (condition, variableReference, options) => {
97
+ var _a;
98
+ const normalizedVariableName = sanitizeVariationVariableReference(variableReference);
99
+ if (!normalizedVariableName) {
100
+ return undefined;
101
+ }
102
+ const conditionRecord = condition;
103
+ if (conditionRecord[normalizedVariableName] !== undefined) {
104
+ return conditionRecord[normalizedVariableName];
105
+ }
106
+ if (variableReference && conditionRecord[variableReference] !== undefined) {
107
+ return conditionRecord[variableReference];
108
+ }
109
+ return (_a = options.getVariableValue) === null || _a === void 0 ? void 0 : _a.call(options, { condition, variableReference, normalizedVariableName });
110
+ };
111
+ const toSortableCondition = (condition) => (typeof condition !== 'boolean' ? safeParse(condition) : condition);
112
+ const compareVariationCondition = (a, b, bucketKey) => {
113
+ const aCondition = toSortableCondition(a.condition);
114
+ const bCondition = toSortableCondition(b.condition);
115
+ if (aCondition === bCondition) {
116
+ return 0;
117
+ }
118
+ if (bucketKey === 'lessers') {
119
+ return aCondition <= bCondition ? -1 : 1;
120
+ }
121
+ return aCondition >= bCondition ? -1 : 1;
122
+ };
123
+ const getVariationBucketKey = (conditionType) => {
124
+ var _a;
125
+ if (!conditionType) {
126
+ return undefined;
127
+ }
128
+ return (_a = Object.entries(variationBucketTypes).find(([, conditionTypes]) => conditionTypes.includes(conditionType))) === null || _a === void 0 ? void 0 : _a[0];
129
+ };
130
+ const compareSubscriptionOrder = (currentVariation, previousVariation) => {
131
+ if (typeof currentVariation.conditionType === 'undefined' || typeof previousVariation.conditionType === 'undefined') {
132
+ return 0;
133
+ }
134
+ const currentOrder = subscriptionOrderNumbers[currentVariation.conditionType];
135
+ const previousOrder = subscriptionOrderNumbers[previousVariation.conditionType];
136
+ if (currentOrder === previousOrder) {
137
+ const currentBucketKey = getVariationBucketKey(currentVariation.conditionType);
138
+ const previousBucketKey = getVariationBucketKey(previousVariation.conditionType);
139
+ if (currentBucketKey && currentBucketKey === previousBucketKey) {
140
+ return compareVariationCondition(currentVariation, previousVariation, currentBucketKey);
141
+ }
142
+ return 0;
143
+ }
144
+ return currentOrder > previousOrder ? 1 : -1;
145
+ };
146
+ export const resetVariationCumulativeTracking = (logger = console) => {
147
+ lastCountMultipleIdxByKey.clear();
148
+ logger.debug('Cumulative tracking reset for new stream session');
149
+ };
150
+ const getBaseAlertFallback = (config, options) => {
151
+ var _a;
152
+ if (!isBaseAlertEnabled(config, options)) {
153
+ (_a = options.logger) === null || _a === void 0 ? void 0 : _a.debug('No variation found. Only accepting variations');
154
+ return null;
155
+ }
156
+ return { ...config };
157
+ };
158
+ const isEmptyVariationCondition = (variation) => {
159
+ const condition = variation.condition;
160
+ if (condition === undefined || condition === null) {
161
+ return true;
162
+ }
163
+ if (typeof condition === 'string') {
164
+ return condition.trim().length === 0;
165
+ }
166
+ if (typeof condition === 'number') {
167
+ return Number.isNaN(condition);
168
+ }
169
+ return false;
170
+ };
171
+ const evaluateVariationMatch = ({ variation, condition, config, options, }) => {
172
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v;
173
+ try {
174
+ if (!condition && variation.conditionType !== LumiaVariationConditions.RANDOM) {
175
+ return false;
176
+ }
177
+ if (config.matchEmptyCondition &&
178
+ isEmptyVariationCondition(variation) &&
179
+ variation.conditionType &&
180
+ [
181
+ LumiaVariationConditions.EQUAL_NUMBER,
182
+ LumiaVariationConditions.EQUAL_VARIABLE,
183
+ LumiaVariationConditions.EQUAL_STRING,
184
+ LumiaVariationConditions.EQUAL_SELECTION,
185
+ LumiaVariationConditions.GREATER_NUMBER,
186
+ LumiaVariationConditions.LESS_NUMBER,
187
+ ].includes(variation.conditionType)) {
188
+ return true;
189
+ }
190
+ switch (variation.conditionType) {
191
+ case LumiaVariationConditions.EQUAL_NUMBER:
192
+ return typeof variation.condition !== 'undefined' && safeParse(variation.condition) === safeParse(condition === null || condition === void 0 ? void 0 : condition.value);
193
+ case LumiaVariationConditions.EQUAL_SELECTION: {
194
+ let variationCondition = variation.condition;
195
+ let value = (_a = condition === null || condition === void 0 ? void 0 : condition.name) !== null && _a !== void 0 ? _a : condition === null || condition === void 0 ? void 0 : condition.value;
196
+ if (typeof variationCondition === 'string') {
197
+ variationCondition = variationCondition.toLowerCase();
198
+ }
199
+ if (typeof value === 'string') {
200
+ value = value.toLowerCase();
201
+ }
202
+ return variationCondition == value;
203
+ }
204
+ case LumiaVariationConditions.EQUAL_STRING: {
205
+ const name = (_b = condition === null || condition === void 0 ? void 0 : condition.name) !== null && _b !== void 0 ? _b : condition === null || condition === void 0 ? void 0 : condition.value;
206
+ if (config.caseSensitive) {
207
+ return ((_c = variation.condition) === null || _c === void 0 ? void 0 : _c.toString()) === (name === null || name === void 0 ? void 0 : name.toString());
208
+ }
209
+ return ((_e = (_d = variation.condition) === null || _d === void 0 ? void 0 : _d.toString()) === null || _e === void 0 ? void 0 : _e.toLowerCase()) == ((_f = name === null || name === void 0 ? void 0 : name.toString()) === null || _f === void 0 ? void 0 : _f.toLowerCase());
210
+ }
211
+ case LumiaVariationConditions.EQUAL_VARIABLE: {
212
+ const variableValue = condition ? getVariableValueFromCondition(condition, (_g = variation.conditionExtra) === null || _g === void 0 ? void 0 : _g.toString(), options) : undefined;
213
+ return compareVariationValue({ expected: variation.condition, actual: variableValue, caseSensitive: config.caseSensitive });
214
+ }
215
+ case LumiaVariationConditions.EQUAL_USERNAME: {
216
+ const name = (_h = condition === null || condition === void 0 ? void 0 : condition.username) !== null && _h !== void 0 ? _h : condition === null || condition === void 0 ? void 0 : condition.value;
217
+ return ((_k = (_j = variation.condition) === null || _j === void 0 ? void 0 : _j.toString()) === null || _k === void 0 ? void 0 : _k.toLowerCase()) == ((_l = name === null || name === void 0 ? void 0 : name.toString()) === null || _l === void 0 ? void 0 : _l.toLowerCase());
218
+ }
219
+ case LumiaVariationConditions.EQUAL_USER_LEVEL:
220
+ return Boolean((_m = condition === null || condition === void 0 ? void 0 : condition.lumiauserlevels) === null || _m === void 0 ? void 0 : _m.includes(safeParse(variation.condition)));
221
+ case LumiaVariationConditions.GREATER_NUMBER:
222
+ return typeof variation.condition !== 'undefined' && safeParse(condition === null || condition === void 0 ? void 0 : condition.value) >= safeParse(variation.condition);
223
+ case LumiaVariationConditions.LESS_NUMBER:
224
+ return typeof variation.condition !== 'undefined' && safeParse(condition === null || condition === void 0 ? void 0 : condition.value) <= safeParse(variation.condition);
225
+ case LumiaVariationConditions.EQUAL_CURRENCY_NUMBER: {
226
+ if (typeof variation.condition === 'undefined' || safeParse(variation.condition) !== safeParse(condition === null || condition === void 0 ? void 0 : condition.value)) {
227
+ return false;
228
+ }
229
+ const checkCurrency = (_p = (_o = condition === null || condition === void 0 ? void 0 : condition.currency) === null || _o === void 0 ? void 0 : _o.toUpperCase()) !== null && _p !== void 0 ? _p : condition === null || condition === void 0 ? void 0 : condition.currency;
230
+ return variation.conditionExtra === checkCurrency || variation.conditionExtra === LumiaVariationCurrency.NONE || !variation.conditionExtra;
231
+ }
232
+ case LumiaVariationConditions.GREATER_CURRENCY_NUMBER: {
233
+ const checkCurrency = (_r = (_q = condition === null || condition === void 0 ? void 0 : condition.currency) === null || _q === void 0 ? void 0 : _q.toUpperCase()) !== null && _r !== void 0 ? _r : condition === null || condition === void 0 ? void 0 : condition.currency;
234
+ if (typeof variation.condition !== 'undefined' && safeParse(condition === null || condition === void 0 ? void 0 : condition.value) >= safeParse(variation.condition)) {
235
+ return variation.conditionExtra === checkCurrency || variation.conditionExtra === LumiaVariationCurrency.NONE || !variation.conditionExtra;
236
+ }
237
+ return false;
238
+ }
239
+ case LumiaVariationConditions.GIFT_SUB_EQUAL:
240
+ return typeof variation.condition !== 'undefined' && safeParse(variation.condition) === safeParse(condition === null || condition === void 0 ? void 0 : condition.giftAmount);
241
+ case LumiaVariationConditions.GIFT_SUB_GREATER:
242
+ return typeof variation.condition !== 'undefined' && safeParse(condition === null || condition === void 0 ? void 0 : condition.giftAmount) >= safeParse(variation.condition);
243
+ case LumiaVariationConditions.IS_GIFT:
244
+ return Boolean((condition === null || condition === void 0 ? void 0 : condition.isGift) || (condition === null || condition === void 0 ? void 0 : condition.giftAmount));
245
+ case LumiaVariationConditions.IS_PRIME:
246
+ return Boolean((condition === null || condition === void 0 ? void 0 : condition.isPrime) || (condition === null || condition === void 0 ? void 0 : condition.value) === 'Prime');
247
+ case LumiaVariationConditions.SUBSCRIBED_MONTHS_EQUAL:
248
+ return typeof variation.condition !== 'undefined' && safeParse(variation.condition) === safeParse(condition === null || condition === void 0 ? void 0 : condition.subMonths);
249
+ case LumiaVariationConditions.SUBSCRIBED_MONTHS_GREATER:
250
+ return typeof variation.condition !== 'undefined' && safeParse(condition === null || condition === void 0 ? void 0 : condition.subMonths) >= safeParse(variation.condition);
251
+ case LumiaVariationConditions.RANDOM:
252
+ return rollChance((_u = (_t = (_s = options.getRandomChance) === null || _s === void 0 ? void 0 : _s.call(options, variation)) !== null && _t !== void 0 ? _t : variation.randomChance) !== null && _u !== void 0 ? _u : variation.condition, options.random);
253
+ case LumiaVariationConditions.COUNT_IS_MULTIPLE_OF:
254
+ return evaluateCountIsMultipleOf({ variation, condition, options });
255
+ default:
256
+ return false;
257
+ }
258
+ }
259
+ catch (error) {
260
+ (_v = options.logger) === null || _v === void 0 ? void 0 : _v.error('Error in variationCheck', error, variation, condition);
261
+ return false;
262
+ }
263
+ };
264
+ const evaluateCountIsMultipleOf = ({ variation, condition, options, }) => {
265
+ if (variation.condition === undefined || condition === undefined || condition === null) {
266
+ return false;
267
+ }
268
+ const step = safeParse(variation.condition);
269
+ const totalSource = condition.total !== undefined ? condition.total : options.useConditionValueForCountTotal ? condition.value : undefined;
270
+ const total = safeParse(totalSource);
271
+ const previousTotal = condition.previousTotal === undefined ? undefined : safeParse(condition.previousTotal);
272
+ const key = `${variation.id || 'unknown'}:${variation.name || 'variation'}`;
273
+ const numericStep = Number(step);
274
+ const numericTotal = Number(total);
275
+ if (!Number.isFinite(numericTotal) || !Number.isFinite(numericStep) || numericStep <= 0) {
276
+ return false;
277
+ }
278
+ const currIdx = Math.floor(numericTotal / numericStep);
279
+ const storedIdx = lastCountMultipleIdxByKey.get(key);
280
+ const hasPrevTotal = previousTotal !== undefined;
281
+ const shouldCommit = !options.skipVariationUpdates;
282
+ if (storedIdx !== undefined && currIdx < storedIdx) {
283
+ if (shouldCommit) {
284
+ lastCountMultipleIdxByKey.set(key, currIdx);
285
+ }
286
+ return false;
287
+ }
288
+ let baseIdx = storedIdx;
289
+ if (baseIdx === undefined) {
290
+ if (hasPrevTotal) {
291
+ const numericPreviousTotal = Number(previousTotal);
292
+ if (numericPreviousTotal > numericTotal) {
293
+ if (shouldCommit) {
294
+ lastCountMultipleIdxByKey.set(key, currIdx);
295
+ }
296
+ return false;
297
+ }
298
+ baseIdx = Math.floor(numericPreviousTotal / numericStep);
299
+ }
300
+ else {
301
+ if (shouldCommit) {
302
+ lastCountMultipleIdxByKey.set(key, currIdx);
303
+ }
304
+ return false;
305
+ }
306
+ }
307
+ const crossed = currIdx > baseIdx;
308
+ if (crossed) {
309
+ if (shouldCommit) {
310
+ lastCountMultipleIdxByKey.set(key, currIdx);
311
+ }
312
+ return true;
313
+ }
314
+ if (storedIdx === undefined && shouldCommit) {
315
+ lastCountMultipleIdxByKey.set(key, baseIdx);
316
+ }
317
+ return false;
318
+ };
319
+ const variationCheck = ({ variation, condition, config, options, }) => {
320
+ var _a, _b, _c;
321
+ if (variation.triggerOncePerStream && ((_a = options.hasTriggeredOnce) === null || _a === void 0 ? void 0 : _a.call(options, variation, config))) {
322
+ (_b = options.logger) === null || _b === void 0 ? void 0 : _b.debug('Variation already triggered this stream', variation.name);
323
+ return false;
324
+ }
325
+ const matched = evaluateVariationMatch({ variation, condition, config, options });
326
+ if (matched && variation.triggerOncePerStream && !options.skipVariationUpdates) {
327
+ (_c = options.markTriggeredOnce) === null || _c === void 0 ? void 0 : _c.call(options, variation, config);
328
+ }
329
+ return matched;
330
+ };
331
+ const checkByOrder = (config, condition, options) => {
332
+ var _a;
333
+ if (typeof config.variations === 'undefined') {
334
+ return undefined;
335
+ }
336
+ const variations = config.variations.filter((variation) => isVariationEnabled(variation, config, options)).sort(compareSubscriptionOrder);
337
+ let foundVariation;
338
+ let randoms = [];
339
+ variations.some((variation) => {
340
+ if (typeof variation.conditionType !== 'undefined' && variationBucketTypes.randoms.includes(variation.conditionType)) {
341
+ randoms.push(variation);
342
+ return false;
343
+ }
344
+ if (variationCheck({ variation, condition, config, options })) {
345
+ foundVariation = variation;
346
+ return true;
347
+ }
348
+ return false;
349
+ });
350
+ if (typeof foundVariation === 'undefined' && randoms.length) {
351
+ randoms = shuffle(randoms, options.random);
352
+ randoms.some((variation) => {
353
+ if (variationCheck({ variation, condition, config, options })) {
354
+ foundVariation = variation;
355
+ return true;
356
+ }
357
+ return false;
358
+ });
359
+ if (!foundVariation && ((_a = options.shouldUseRandomFallback) === null || _a === void 0 ? void 0 : _a.call(options, config))) {
360
+ foundVariation = randoms[0];
361
+ }
362
+ }
363
+ return foundVariation;
364
+ };
365
+ const checkByPriorities = (config, condition, options) => {
366
+ if (typeof config.variations === 'undefined') {
367
+ return undefined;
368
+ }
369
+ const variationBucket = {
370
+ equals: [],
371
+ greaters: [],
372
+ lessers: [],
373
+ checks: [],
374
+ randoms: [],
375
+ };
376
+ config.variations.forEach((variation) => {
377
+ if (!isVariationEnabled(variation, config, options) || typeof variation.conditionType === 'undefined') {
378
+ return;
379
+ }
380
+ if (variationBucketTypes.equals.includes(variation.conditionType)) {
381
+ variationBucket.equals.push(variation);
382
+ }
383
+ else if (variationBucketTypes.greaters.includes(variation.conditionType)) {
384
+ variationBucket.greaters.push(variation);
385
+ }
386
+ else if (variationBucketTypes.lessers.includes(variation.conditionType)) {
387
+ variationBucket.lessers.push(variation);
388
+ }
389
+ else if (variationBucketTypes.checks.includes(variation.conditionType)) {
390
+ variationBucket.checks.push(variation);
391
+ }
392
+ else if (variationBucketTypes.randoms.includes(variation.conditionType)) {
393
+ variationBucket.randoms.push(variation);
394
+ }
395
+ });
396
+ Object.keys(variationBucket).forEach((bucketKey) => {
397
+ variationBucket[bucketKey].sort((a, b) => compareVariationCondition(a, b, bucketKey));
398
+ });
399
+ let foundVariation;
400
+ const bucketKeys = ['equals', 'greaters', 'lessers', 'checks', 'randoms'];
401
+ bucketKeys.some((bucketKey) => {
402
+ var _a;
403
+ if (!variationBucket[bucketKey].length) {
404
+ return false;
405
+ }
406
+ const variations = bucketKey === 'randoms' ? shuffle(variationBucket[bucketKey], options.random) : variationBucket[bucketKey];
407
+ variations.some((variation) => {
408
+ if (variationCheck({ variation, condition, config, options })) {
409
+ foundVariation = variation;
410
+ return true;
411
+ }
412
+ return false;
413
+ });
414
+ if (!foundVariation && bucketKey === 'randoms' && ((_a = options.shouldUseRandomFallback) === null || _a === void 0 ? void 0 : _a.call(options, config))) {
415
+ foundVariation = variations[0];
416
+ }
417
+ return Boolean(foundVariation);
418
+ });
419
+ return foundVariation;
420
+ };
421
+ export const checkAlertVariation = (config, condition, options = {}) => {
422
+ var _a, _b;
423
+ if (!config.variations) {
424
+ return getBaseAlertFallback(config, options);
425
+ }
426
+ const foundVariation = ((_a = options.shouldUseSubscriptionOrder) === null || _a === void 0 ? void 0 : _a.call(options, config, condition)) ? checkByOrder(config, condition, options) : checkByPriorities(config, condition, options);
427
+ if (foundVariation) {
428
+ (_b = options.logger) === null || _b === void 0 ? void 0 : _b.debug('Found variation: ', foundVariation === null || foundVariation === void 0 ? void 0 : foundVariation.name);
429
+ return foundVariation;
430
+ }
431
+ return getBaseAlertFallback(config, options);
432
+ };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export { LumiaStreamingSites, LumiaActivityCommandTypes, LumiaExternalActivityCommandTypes, LumiaAlertValues, LumiaAlertFriendlyValues, LumiaActivityOriginTypes, LumiaActivityApiValueType, LumiaActivityNoValueTypes, LumiaActivityTestType, } from './activity.types';
2
2
  export { LumiaVariationConditions, LumiaVariationCurrency, VariationCurrencySymbol, LumiaRedemptionCurrency, LumiaRedemptionCurrencySymbol, LumiaAlertConfigs, LumiaDynamicCondition, type LumiaSelectionOption, } from './alert.types';
3
+ export { checkAlertVariation, resetVariationCumulativeTracking, sanitizeVariationVariableReference, type LumiaVariationCheckerOptions, type LumiaVariationConfigLike, type LumiaVariationLike, type LumiaVariationLogger, } from './variation.helpers';
3
4
  export { ILumiaSendPack, ILumiaEvent, ILumiaEventChatCommandBody, ILumiaEventChatBody, ILumiaEventAlertBody, ILumiaEventStateBody, ILumiaLight, LumiaIntegrations, LumiaEventTypes, } from './event.types';
4
5
  export { LumiaEventListTypes, LumiaMapAlertTypeToEventListType, AlertsToFilter, PlatformsToFilter, LumiaEventListTypeColors, getEventListCategoryColor } from './eventlist.types';
5
6
  export { SystemVariables, ReservedVariables, AllVariables, getAcceptedVariableName, getAcceptedVariableNames, type LumiaAcceptedVariable, type LumiaAcceptedVariableDefinition, type CountableVariableValue, type DonatorVariableValue, } from './variables.types';
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SongRequestPlaybackTarget = exports.SongRequestProvider = exports.SongRequestSource = exports.SongRequestStatus = exports.VIEWER_PROFILE_ACHIEVEMENTS = exports.YoutubeSuperstickerImageSelections = exports.YoutubeSuperstickersData = exports.TiktokGiftImageSelections = exports.TiktokGiftsData = exports.BLANK_OVERLAY_TEMPLATE_ID = exports.KickKicksImageSelections = exports.KickKicksData = exports.syncLinkedVariableFields = exports.isRedundantInputField = exports.aliasContentImageFromLegacy = exports.coerceNumericAlertFields = exports.buildExampleAlertVariables = exports.getExampleAlertVariableValue = exports.EMULATE_EXAMPLE_AVATAR_URL = exports.formatCondition = exports.getVariableExamples = exports.VARIABLE_EXAMPLES = exports.getAcceptedVariableNames = exports.getAcceptedVariableName = exports.AllVariables = exports.ReservedVariables = exports.SystemVariables = exports.getEventListCategoryColor = exports.LumiaEventListTypeColors = exports.PlatformsToFilter = exports.AlertsToFilter = exports.LumiaMapAlertTypeToEventListType = exports.LumiaEventListTypes = exports.LumiaEventTypes = exports.LumiaIntegrations = exports.LumiaAlertConfigs = exports.LumiaRedemptionCurrencySymbol = exports.LumiaRedemptionCurrency = exports.VariationCurrencySymbol = exports.LumiaVariationCurrency = exports.LumiaVariationConditions = exports.LumiaActivityTestType = exports.LumiaActivityNoValueTypes = exports.LumiaActivityApiValueType = exports.LumiaActivityOriginTypes = exports.LumiaAlertFriendlyValues = exports.LumiaAlertValues = exports.LumiaExternalActivityCommandTypes = exports.LumiaActivityCommandTypes = exports.LumiaStreamingSites = void 0;
3
+ exports.SongRequestStatus = exports.VIEWER_PROFILE_ACHIEVEMENTS = exports.YoutubeSuperstickerImageSelections = exports.YoutubeSuperstickersData = exports.TiktokGiftImageSelections = exports.TiktokGiftsData = exports.BLANK_OVERLAY_TEMPLATE_ID = exports.KickKicksImageSelections = exports.KickKicksData = exports.syncLinkedVariableFields = exports.isRedundantInputField = exports.aliasContentImageFromLegacy = exports.coerceNumericAlertFields = exports.buildExampleAlertVariables = exports.getExampleAlertVariableValue = exports.EMULATE_EXAMPLE_AVATAR_URL = exports.formatCondition = exports.getVariableExamples = exports.VARIABLE_EXAMPLES = exports.getAcceptedVariableNames = exports.getAcceptedVariableName = exports.AllVariables = exports.ReservedVariables = exports.SystemVariables = exports.getEventListCategoryColor = exports.LumiaEventListTypeColors = exports.PlatformsToFilter = exports.AlertsToFilter = exports.LumiaMapAlertTypeToEventListType = exports.LumiaEventListTypes = exports.LumiaEventTypes = exports.LumiaIntegrations = exports.sanitizeVariationVariableReference = exports.resetVariationCumulativeTracking = exports.checkAlertVariation = exports.LumiaAlertConfigs = exports.LumiaRedemptionCurrencySymbol = exports.LumiaRedemptionCurrency = exports.VariationCurrencySymbol = exports.LumiaVariationCurrency = exports.LumiaVariationConditions = exports.LumiaActivityTestType = exports.LumiaActivityNoValueTypes = exports.LumiaActivityApiValueType = exports.LumiaActivityOriginTypes = exports.LumiaAlertFriendlyValues = exports.LumiaAlertValues = exports.LumiaExternalActivityCommandTypes = exports.LumiaActivityCommandTypes = exports.LumiaStreamingSites = void 0;
4
+ exports.SongRequestPlaybackTarget = exports.SongRequestProvider = exports.SongRequestSource = void 0;
4
5
  var activity_types_1 = require("./activity.types");
5
6
  Object.defineProperty(exports, "LumiaStreamingSites", { enumerable: true, get: function () { return activity_types_1.LumiaStreamingSites; } });
6
7
  Object.defineProperty(exports, "LumiaActivityCommandTypes", { enumerable: true, get: function () { return activity_types_1.LumiaActivityCommandTypes; } });
@@ -18,6 +19,10 @@ Object.defineProperty(exports, "VariationCurrencySymbol", { enumerable: true, ge
18
19
  Object.defineProperty(exports, "LumiaRedemptionCurrency", { enumerable: true, get: function () { return alert_types_1.LumiaRedemptionCurrency; } });
19
20
  Object.defineProperty(exports, "LumiaRedemptionCurrencySymbol", { enumerable: true, get: function () { return alert_types_1.LumiaRedemptionCurrencySymbol; } });
20
21
  Object.defineProperty(exports, "LumiaAlertConfigs", { enumerable: true, get: function () { return alert_types_1.LumiaAlertConfigs; } });
22
+ var variation_helpers_1 = require("./variation.helpers");
23
+ Object.defineProperty(exports, "checkAlertVariation", { enumerable: true, get: function () { return variation_helpers_1.checkAlertVariation; } });
24
+ Object.defineProperty(exports, "resetVariationCumulativeTracking", { enumerable: true, get: function () { return variation_helpers_1.resetVariationCumulativeTracking; } });
25
+ Object.defineProperty(exports, "sanitizeVariationVariableReference", { enumerable: true, get: function () { return variation_helpers_1.sanitizeVariationVariableReference; } });
21
26
  var event_types_1 = require("./event.types");
22
27
  Object.defineProperty(exports, "LumiaIntegrations", { enumerable: true, get: function () { return event_types_1.LumiaIntegrations; } });
23
28
  Object.defineProperty(exports, "LumiaEventTypes", { enumerable: true, get: function () { return event_types_1.LumiaEventTypes; } });
@@ -0,0 +1,48 @@
1
+ import { type LumiaDynamicCondition, LumiaVariationConditions, LumiaVariationCurrency } from './alert.types';
2
+ export interface LumiaVariationLike {
3
+ id?: string | number;
4
+ name?: string;
5
+ on?: boolean;
6
+ condition?: string | number | boolean | null;
7
+ conditionType?: LumiaVariationConditions;
8
+ conditionExtra?: LumiaVariationCurrency | string | number | null;
9
+ randomChance?: number;
10
+ triggerOncePerStream?: boolean;
11
+ settings?: {
12
+ enabled?: boolean;
13
+ };
14
+ }
15
+ export interface LumiaVariationConfigLike<TVariation extends LumiaVariationLike> {
16
+ variations?: TVariation[];
17
+ onlyVariation?: boolean;
18
+ disableBaseAlert?: boolean;
19
+ subgift?: boolean;
20
+ on?: boolean;
21
+ matchEmptyCondition?: boolean;
22
+ caseSensitive?: boolean;
23
+ settings?: {
24
+ enabled?: boolean;
25
+ };
26
+ }
27
+ export type LumiaVariationLogger = Pick<Console, 'debug' | 'error'>;
28
+ export interface LumiaVariationCheckerOptions<TConfig extends LumiaVariationConfigLike<TVariation>, TVariation extends LumiaVariationLike> {
29
+ getRandomChance?: (variation: TVariation) => unknown;
30
+ getVariableValue?: (params: {
31
+ condition: LumiaDynamicCondition;
32
+ variableReference?: string;
33
+ normalizedVariableName: string;
34
+ }) => unknown;
35
+ hasTriggeredOnce?: (variation: TVariation, config: TConfig) => boolean;
36
+ isBaseAlertEnabled?: (config: TConfig) => boolean;
37
+ isVariationEnabled?: (variation: TVariation, config: TConfig) => boolean;
38
+ logger?: LumiaVariationLogger;
39
+ markTriggeredOnce?: (variation: TVariation, config: TConfig) => void;
40
+ random?: () => number;
41
+ shouldUseRandomFallback?: (config: TConfig) => boolean;
42
+ shouldUseSubscriptionOrder?: (config: TConfig, condition: LumiaDynamicCondition | undefined | null) => boolean;
43
+ skipVariationUpdates?: boolean;
44
+ useConditionValueForCountTotal?: boolean;
45
+ }
46
+ export declare const sanitizeVariationVariableReference: (value?: string) => string;
47
+ export declare const resetVariationCumulativeTracking: (logger?: LumiaVariationLogger) => void;
48
+ export declare const checkAlertVariation: <TConfig extends LumiaVariationConfigLike<TVariation>, TVariation extends LumiaVariationLike>(config: TConfig, condition: LumiaDynamicCondition | undefined | null, options?: LumiaVariationCheckerOptions<TConfig, TVariation>) => TVariation | TConfig | null;
@@ -0,0 +1,438 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkAlertVariation = exports.resetVariationCumulativeTracking = exports.sanitizeVariationVariableReference = void 0;
4
+ const alert_types_1 = require("./alert.types");
5
+ const lastCountMultipleIdxByKey = new Map();
6
+ const subscriptionOrderNumbers = {
7
+ [alert_types_1.LumiaVariationConditions.GIFT_SUB_EQUAL]: 1,
8
+ [alert_types_1.LumiaVariationConditions.GIFT_SUB_GREATER]: 2,
9
+ [alert_types_1.LumiaVariationConditions.IS_GIFT]: 3,
10
+ [alert_types_1.LumiaVariationConditions.SUBSCRIBED_MONTHS_EQUAL]: 4,
11
+ [alert_types_1.LumiaVariationConditions.SUBSCRIBED_MONTHS_GREATER]: 5,
12
+ [alert_types_1.LumiaVariationConditions.IS_PRIME]: 6,
13
+ [alert_types_1.LumiaVariationConditions.EQUAL_SELECTION]: 7,
14
+ [alert_types_1.LumiaVariationConditions.RANDOM]: 8,
15
+ };
16
+ const variationBucketTypes = {
17
+ equals: [
18
+ alert_types_1.LumiaVariationConditions.EQUAL_NUMBER,
19
+ alert_types_1.LumiaVariationConditions.EQUAL_VARIABLE,
20
+ alert_types_1.LumiaVariationConditions.EQUAL_CURRENCY_NUMBER,
21
+ alert_types_1.LumiaVariationConditions.EQUAL_SELECTION,
22
+ alert_types_1.LumiaVariationConditions.EQUAL_STRING,
23
+ alert_types_1.LumiaVariationConditions.EQUAL_USERNAME,
24
+ alert_types_1.LumiaVariationConditions.EQUAL_USER_LEVEL,
25
+ alert_types_1.LumiaVariationConditions.SUBSCRIBED_MONTHS_EQUAL,
26
+ alert_types_1.LumiaVariationConditions.GIFT_SUB_EQUAL,
27
+ alert_types_1.LumiaVariationConditions.COUNT_IS_MULTIPLE_OF,
28
+ ],
29
+ greaters: [
30
+ alert_types_1.LumiaVariationConditions.GREATER_NUMBER,
31
+ alert_types_1.LumiaVariationConditions.GREATER_CURRENCY_NUMBER,
32
+ alert_types_1.LumiaVariationConditions.SUBSCRIBED_MONTHS_GREATER,
33
+ alert_types_1.LumiaVariationConditions.GIFT_SUB_GREATER,
34
+ ],
35
+ lessers: [alert_types_1.LumiaVariationConditions.LESS_NUMBER],
36
+ checks: [alert_types_1.LumiaVariationConditions.IS_GIFT, alert_types_1.LumiaVariationConditions.IS_PRIME],
37
+ randoms: [alert_types_1.LumiaVariationConditions.RANDOM],
38
+ };
39
+ const sanitizeVariationVariableReference = (value) => (value ? value.trim().replace(/[={}]/g, '') : '');
40
+ exports.sanitizeVariationVariableReference = sanitizeVariationVariableReference;
41
+ const isVariationOnly = (config) => Boolean(config.onlyVariation || config.disableBaseAlert);
42
+ const isBaseAlertEnabled = (config, options) => { var _a, _b; return (_b = (_a = options.isBaseAlertEnabled) === null || _a === void 0 ? void 0 : _a.call(options, config)) !== null && _b !== void 0 ? _b : !isVariationOnly(config); };
43
+ const isVariationEnabled = (variation, config, options) => { var _a, _b; return (_b = (_a = options.isVariationEnabled) === null || _a === void 0 ? void 0 : _a.call(options, variation, config)) !== null && _b !== void 0 ? _b : variation.on !== false; };
44
+ const safeParse = (value, floor = false) => {
45
+ if (typeof value === 'number') {
46
+ return floor ? Math.floor(value) : value;
47
+ }
48
+ if (typeof value === 'boolean' || value === undefined || value === null) {
49
+ return value;
50
+ }
51
+ if (value.includes('.')) {
52
+ const parsedValue = parseFloat(value);
53
+ return floor ? Math.floor(parsedValue) : parsedValue;
54
+ }
55
+ const parsedValue = parseInt(value);
56
+ return floor ? Math.floor(parsedValue) : parsedValue;
57
+ };
58
+ const toFiniteNumber = (value) => {
59
+ if (typeof value === 'number') {
60
+ return Number.isFinite(value) ? value : undefined;
61
+ }
62
+ if (typeof value === 'string') {
63
+ const trimmedValue = value.trim();
64
+ if (!trimmedValue.length) {
65
+ return undefined;
66
+ }
67
+ const parsedValue = Number(trimmedValue);
68
+ return Number.isFinite(parsedValue) ? parsedValue : undefined;
69
+ }
70
+ return undefined;
71
+ };
72
+ const getRandomNumber = (min = 0, max = 255, random = Math.random) => Math.floor(random() * (max - min + 1) + min);
73
+ const rollChance = (percent = 100, random = Math.random) => getRandomNumber(0, 100, random) < percent;
74
+ const shuffle = (array, random = Math.random) => {
75
+ const newArray = [...array];
76
+ const startIndex = newArray.length;
77
+ let randomIndex = Math.floor(random() * startIndex);
78
+ for (let currentIndex = startIndex - 1; currentIndex >= 1; currentIndex--) {
79
+ [newArray[currentIndex], newArray[randomIndex]] = [newArray[randomIndex], newArray[currentIndex]];
80
+ randomIndex = Math.floor(random() * currentIndex);
81
+ }
82
+ return newArray;
83
+ };
84
+ const compareVariationValue = ({ expected, actual, caseSensitive }) => {
85
+ if (expected === undefined || expected === null || (typeof expected === 'string' && expected.trim().length === 0)) {
86
+ return actual !== undefined && actual !== null;
87
+ }
88
+ const expectedNumber = toFiniteNumber(expected);
89
+ const actualNumber = toFiniteNumber(actual);
90
+ if (expectedNumber !== undefined && actualNumber !== undefined) {
91
+ return expectedNumber === actualNumber;
92
+ }
93
+ const expectedString = `${expected}`;
94
+ const actualString = actual === undefined || actual === null ? '' : `${actual}`;
95
+ if (caseSensitive ? expectedString === actualString : expectedString.toLowerCase() === actualString.toLowerCase()) {
96
+ return true;
97
+ }
98
+ return expected == actual;
99
+ };
100
+ const getVariableValueFromCondition = (condition, variableReference, options) => {
101
+ var _a;
102
+ const normalizedVariableName = (0, exports.sanitizeVariationVariableReference)(variableReference);
103
+ if (!normalizedVariableName) {
104
+ return undefined;
105
+ }
106
+ const conditionRecord = condition;
107
+ if (conditionRecord[normalizedVariableName] !== undefined) {
108
+ return conditionRecord[normalizedVariableName];
109
+ }
110
+ if (variableReference && conditionRecord[variableReference] !== undefined) {
111
+ return conditionRecord[variableReference];
112
+ }
113
+ return (_a = options.getVariableValue) === null || _a === void 0 ? void 0 : _a.call(options, { condition, variableReference, normalizedVariableName });
114
+ };
115
+ const toSortableCondition = (condition) => (typeof condition !== 'boolean' ? safeParse(condition) : condition);
116
+ const compareVariationCondition = (a, b, bucketKey) => {
117
+ const aCondition = toSortableCondition(a.condition);
118
+ const bCondition = toSortableCondition(b.condition);
119
+ if (aCondition === bCondition) {
120
+ return 0;
121
+ }
122
+ if (bucketKey === 'lessers') {
123
+ return aCondition <= bCondition ? -1 : 1;
124
+ }
125
+ return aCondition >= bCondition ? -1 : 1;
126
+ };
127
+ const getVariationBucketKey = (conditionType) => {
128
+ var _a;
129
+ if (!conditionType) {
130
+ return undefined;
131
+ }
132
+ return (_a = Object.entries(variationBucketTypes).find(([, conditionTypes]) => conditionTypes.includes(conditionType))) === null || _a === void 0 ? void 0 : _a[0];
133
+ };
134
+ const compareSubscriptionOrder = (currentVariation, previousVariation) => {
135
+ if (typeof currentVariation.conditionType === 'undefined' || typeof previousVariation.conditionType === 'undefined') {
136
+ return 0;
137
+ }
138
+ const currentOrder = subscriptionOrderNumbers[currentVariation.conditionType];
139
+ const previousOrder = subscriptionOrderNumbers[previousVariation.conditionType];
140
+ if (currentOrder === previousOrder) {
141
+ const currentBucketKey = getVariationBucketKey(currentVariation.conditionType);
142
+ const previousBucketKey = getVariationBucketKey(previousVariation.conditionType);
143
+ if (currentBucketKey && currentBucketKey === previousBucketKey) {
144
+ return compareVariationCondition(currentVariation, previousVariation, currentBucketKey);
145
+ }
146
+ return 0;
147
+ }
148
+ return currentOrder > previousOrder ? 1 : -1;
149
+ };
150
+ const resetVariationCumulativeTracking = (logger = console) => {
151
+ lastCountMultipleIdxByKey.clear();
152
+ logger.debug('Cumulative tracking reset for new stream session');
153
+ };
154
+ exports.resetVariationCumulativeTracking = resetVariationCumulativeTracking;
155
+ const getBaseAlertFallback = (config, options) => {
156
+ var _a;
157
+ if (!isBaseAlertEnabled(config, options)) {
158
+ (_a = options.logger) === null || _a === void 0 ? void 0 : _a.debug('No variation found. Only accepting variations');
159
+ return null;
160
+ }
161
+ return { ...config };
162
+ };
163
+ const isEmptyVariationCondition = (variation) => {
164
+ const condition = variation.condition;
165
+ if (condition === undefined || condition === null) {
166
+ return true;
167
+ }
168
+ if (typeof condition === 'string') {
169
+ return condition.trim().length === 0;
170
+ }
171
+ if (typeof condition === 'number') {
172
+ return Number.isNaN(condition);
173
+ }
174
+ return false;
175
+ };
176
+ const evaluateVariationMatch = ({ variation, condition, config, options, }) => {
177
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v;
178
+ try {
179
+ if (!condition && variation.conditionType !== alert_types_1.LumiaVariationConditions.RANDOM) {
180
+ return false;
181
+ }
182
+ if (config.matchEmptyCondition &&
183
+ isEmptyVariationCondition(variation) &&
184
+ variation.conditionType &&
185
+ [
186
+ alert_types_1.LumiaVariationConditions.EQUAL_NUMBER,
187
+ alert_types_1.LumiaVariationConditions.EQUAL_VARIABLE,
188
+ alert_types_1.LumiaVariationConditions.EQUAL_STRING,
189
+ alert_types_1.LumiaVariationConditions.EQUAL_SELECTION,
190
+ alert_types_1.LumiaVariationConditions.GREATER_NUMBER,
191
+ alert_types_1.LumiaVariationConditions.LESS_NUMBER,
192
+ ].includes(variation.conditionType)) {
193
+ return true;
194
+ }
195
+ switch (variation.conditionType) {
196
+ case alert_types_1.LumiaVariationConditions.EQUAL_NUMBER:
197
+ return typeof variation.condition !== 'undefined' && safeParse(variation.condition) === safeParse(condition === null || condition === void 0 ? void 0 : condition.value);
198
+ case alert_types_1.LumiaVariationConditions.EQUAL_SELECTION: {
199
+ let variationCondition = variation.condition;
200
+ let value = (_a = condition === null || condition === void 0 ? void 0 : condition.name) !== null && _a !== void 0 ? _a : condition === null || condition === void 0 ? void 0 : condition.value;
201
+ if (typeof variationCondition === 'string') {
202
+ variationCondition = variationCondition.toLowerCase();
203
+ }
204
+ if (typeof value === 'string') {
205
+ value = value.toLowerCase();
206
+ }
207
+ return variationCondition == value;
208
+ }
209
+ case alert_types_1.LumiaVariationConditions.EQUAL_STRING: {
210
+ const name = (_b = condition === null || condition === void 0 ? void 0 : condition.name) !== null && _b !== void 0 ? _b : condition === null || condition === void 0 ? void 0 : condition.value;
211
+ if (config.caseSensitive) {
212
+ return ((_c = variation.condition) === null || _c === void 0 ? void 0 : _c.toString()) === (name === null || name === void 0 ? void 0 : name.toString());
213
+ }
214
+ return ((_e = (_d = variation.condition) === null || _d === void 0 ? void 0 : _d.toString()) === null || _e === void 0 ? void 0 : _e.toLowerCase()) == ((_f = name === null || name === void 0 ? void 0 : name.toString()) === null || _f === void 0 ? void 0 : _f.toLowerCase());
215
+ }
216
+ case alert_types_1.LumiaVariationConditions.EQUAL_VARIABLE: {
217
+ const variableValue = condition ? getVariableValueFromCondition(condition, (_g = variation.conditionExtra) === null || _g === void 0 ? void 0 : _g.toString(), options) : undefined;
218
+ return compareVariationValue({ expected: variation.condition, actual: variableValue, caseSensitive: config.caseSensitive });
219
+ }
220
+ case alert_types_1.LumiaVariationConditions.EQUAL_USERNAME: {
221
+ const name = (_h = condition === null || condition === void 0 ? void 0 : condition.username) !== null && _h !== void 0 ? _h : condition === null || condition === void 0 ? void 0 : condition.value;
222
+ return ((_k = (_j = variation.condition) === null || _j === void 0 ? void 0 : _j.toString()) === null || _k === void 0 ? void 0 : _k.toLowerCase()) == ((_l = name === null || name === void 0 ? void 0 : name.toString()) === null || _l === void 0 ? void 0 : _l.toLowerCase());
223
+ }
224
+ case alert_types_1.LumiaVariationConditions.EQUAL_USER_LEVEL:
225
+ return Boolean((_m = condition === null || condition === void 0 ? void 0 : condition.lumiauserlevels) === null || _m === void 0 ? void 0 : _m.includes(safeParse(variation.condition)));
226
+ case alert_types_1.LumiaVariationConditions.GREATER_NUMBER:
227
+ return typeof variation.condition !== 'undefined' && safeParse(condition === null || condition === void 0 ? void 0 : condition.value) >= safeParse(variation.condition);
228
+ case alert_types_1.LumiaVariationConditions.LESS_NUMBER:
229
+ return typeof variation.condition !== 'undefined' && safeParse(condition === null || condition === void 0 ? void 0 : condition.value) <= safeParse(variation.condition);
230
+ case alert_types_1.LumiaVariationConditions.EQUAL_CURRENCY_NUMBER: {
231
+ if (typeof variation.condition === 'undefined' || safeParse(variation.condition) !== safeParse(condition === null || condition === void 0 ? void 0 : condition.value)) {
232
+ return false;
233
+ }
234
+ const checkCurrency = (_p = (_o = condition === null || condition === void 0 ? void 0 : condition.currency) === null || _o === void 0 ? void 0 : _o.toUpperCase()) !== null && _p !== void 0 ? _p : condition === null || condition === void 0 ? void 0 : condition.currency;
235
+ return variation.conditionExtra === checkCurrency || variation.conditionExtra === alert_types_1.LumiaVariationCurrency.NONE || !variation.conditionExtra;
236
+ }
237
+ case alert_types_1.LumiaVariationConditions.GREATER_CURRENCY_NUMBER: {
238
+ const checkCurrency = (_r = (_q = condition === null || condition === void 0 ? void 0 : condition.currency) === null || _q === void 0 ? void 0 : _q.toUpperCase()) !== null && _r !== void 0 ? _r : condition === null || condition === void 0 ? void 0 : condition.currency;
239
+ if (typeof variation.condition !== 'undefined' && safeParse(condition === null || condition === void 0 ? void 0 : condition.value) >= safeParse(variation.condition)) {
240
+ return variation.conditionExtra === checkCurrency || variation.conditionExtra === alert_types_1.LumiaVariationCurrency.NONE || !variation.conditionExtra;
241
+ }
242
+ return false;
243
+ }
244
+ case alert_types_1.LumiaVariationConditions.GIFT_SUB_EQUAL:
245
+ return typeof variation.condition !== 'undefined' && safeParse(variation.condition) === safeParse(condition === null || condition === void 0 ? void 0 : condition.giftAmount);
246
+ case alert_types_1.LumiaVariationConditions.GIFT_SUB_GREATER:
247
+ return typeof variation.condition !== 'undefined' && safeParse(condition === null || condition === void 0 ? void 0 : condition.giftAmount) >= safeParse(variation.condition);
248
+ case alert_types_1.LumiaVariationConditions.IS_GIFT:
249
+ return Boolean((condition === null || condition === void 0 ? void 0 : condition.isGift) || (condition === null || condition === void 0 ? void 0 : condition.giftAmount));
250
+ case alert_types_1.LumiaVariationConditions.IS_PRIME:
251
+ return Boolean((condition === null || condition === void 0 ? void 0 : condition.isPrime) || (condition === null || condition === void 0 ? void 0 : condition.value) === 'Prime');
252
+ case alert_types_1.LumiaVariationConditions.SUBSCRIBED_MONTHS_EQUAL:
253
+ return typeof variation.condition !== 'undefined' && safeParse(variation.condition) === safeParse(condition === null || condition === void 0 ? void 0 : condition.subMonths);
254
+ case alert_types_1.LumiaVariationConditions.SUBSCRIBED_MONTHS_GREATER:
255
+ return typeof variation.condition !== 'undefined' && safeParse(condition === null || condition === void 0 ? void 0 : condition.subMonths) >= safeParse(variation.condition);
256
+ case alert_types_1.LumiaVariationConditions.RANDOM:
257
+ return rollChance((_u = (_t = (_s = options.getRandomChance) === null || _s === void 0 ? void 0 : _s.call(options, variation)) !== null && _t !== void 0 ? _t : variation.randomChance) !== null && _u !== void 0 ? _u : variation.condition, options.random);
258
+ case alert_types_1.LumiaVariationConditions.COUNT_IS_MULTIPLE_OF:
259
+ return evaluateCountIsMultipleOf({ variation, condition, options });
260
+ default:
261
+ return false;
262
+ }
263
+ }
264
+ catch (error) {
265
+ (_v = options.logger) === null || _v === void 0 ? void 0 : _v.error('Error in variationCheck', error, variation, condition);
266
+ return false;
267
+ }
268
+ };
269
+ const evaluateCountIsMultipleOf = ({ variation, condition, options, }) => {
270
+ if (variation.condition === undefined || condition === undefined || condition === null) {
271
+ return false;
272
+ }
273
+ const step = safeParse(variation.condition);
274
+ const totalSource = condition.total !== undefined ? condition.total : options.useConditionValueForCountTotal ? condition.value : undefined;
275
+ const total = safeParse(totalSource);
276
+ const previousTotal = condition.previousTotal === undefined ? undefined : safeParse(condition.previousTotal);
277
+ const key = `${variation.id || 'unknown'}:${variation.name || 'variation'}`;
278
+ const numericStep = Number(step);
279
+ const numericTotal = Number(total);
280
+ if (!Number.isFinite(numericTotal) || !Number.isFinite(numericStep) || numericStep <= 0) {
281
+ return false;
282
+ }
283
+ const currIdx = Math.floor(numericTotal / numericStep);
284
+ const storedIdx = lastCountMultipleIdxByKey.get(key);
285
+ const hasPrevTotal = previousTotal !== undefined;
286
+ const shouldCommit = !options.skipVariationUpdates;
287
+ if (storedIdx !== undefined && currIdx < storedIdx) {
288
+ if (shouldCommit) {
289
+ lastCountMultipleIdxByKey.set(key, currIdx);
290
+ }
291
+ return false;
292
+ }
293
+ let baseIdx = storedIdx;
294
+ if (baseIdx === undefined) {
295
+ if (hasPrevTotal) {
296
+ const numericPreviousTotal = Number(previousTotal);
297
+ if (numericPreviousTotal > numericTotal) {
298
+ if (shouldCommit) {
299
+ lastCountMultipleIdxByKey.set(key, currIdx);
300
+ }
301
+ return false;
302
+ }
303
+ baseIdx = Math.floor(numericPreviousTotal / numericStep);
304
+ }
305
+ else {
306
+ if (shouldCommit) {
307
+ lastCountMultipleIdxByKey.set(key, currIdx);
308
+ }
309
+ return false;
310
+ }
311
+ }
312
+ const crossed = currIdx > baseIdx;
313
+ if (crossed) {
314
+ if (shouldCommit) {
315
+ lastCountMultipleIdxByKey.set(key, currIdx);
316
+ }
317
+ return true;
318
+ }
319
+ if (storedIdx === undefined && shouldCommit) {
320
+ lastCountMultipleIdxByKey.set(key, baseIdx);
321
+ }
322
+ return false;
323
+ };
324
+ const variationCheck = ({ variation, condition, config, options, }) => {
325
+ var _a, _b, _c;
326
+ if (variation.triggerOncePerStream && ((_a = options.hasTriggeredOnce) === null || _a === void 0 ? void 0 : _a.call(options, variation, config))) {
327
+ (_b = options.logger) === null || _b === void 0 ? void 0 : _b.debug('Variation already triggered this stream', variation.name);
328
+ return false;
329
+ }
330
+ const matched = evaluateVariationMatch({ variation, condition, config, options });
331
+ if (matched && variation.triggerOncePerStream && !options.skipVariationUpdates) {
332
+ (_c = options.markTriggeredOnce) === null || _c === void 0 ? void 0 : _c.call(options, variation, config);
333
+ }
334
+ return matched;
335
+ };
336
+ const checkByOrder = (config, condition, options) => {
337
+ var _a;
338
+ if (typeof config.variations === 'undefined') {
339
+ return undefined;
340
+ }
341
+ const variations = config.variations.filter((variation) => isVariationEnabled(variation, config, options)).sort(compareSubscriptionOrder);
342
+ let foundVariation;
343
+ let randoms = [];
344
+ variations.some((variation) => {
345
+ if (typeof variation.conditionType !== 'undefined' && variationBucketTypes.randoms.includes(variation.conditionType)) {
346
+ randoms.push(variation);
347
+ return false;
348
+ }
349
+ if (variationCheck({ variation, condition, config, options })) {
350
+ foundVariation = variation;
351
+ return true;
352
+ }
353
+ return false;
354
+ });
355
+ if (typeof foundVariation === 'undefined' && randoms.length) {
356
+ randoms = shuffle(randoms, options.random);
357
+ randoms.some((variation) => {
358
+ if (variationCheck({ variation, condition, config, options })) {
359
+ foundVariation = variation;
360
+ return true;
361
+ }
362
+ return false;
363
+ });
364
+ if (!foundVariation && ((_a = options.shouldUseRandomFallback) === null || _a === void 0 ? void 0 : _a.call(options, config))) {
365
+ foundVariation = randoms[0];
366
+ }
367
+ }
368
+ return foundVariation;
369
+ };
370
+ const checkByPriorities = (config, condition, options) => {
371
+ if (typeof config.variations === 'undefined') {
372
+ return undefined;
373
+ }
374
+ const variationBucket = {
375
+ equals: [],
376
+ greaters: [],
377
+ lessers: [],
378
+ checks: [],
379
+ randoms: [],
380
+ };
381
+ config.variations.forEach((variation) => {
382
+ if (!isVariationEnabled(variation, config, options) || typeof variation.conditionType === 'undefined') {
383
+ return;
384
+ }
385
+ if (variationBucketTypes.equals.includes(variation.conditionType)) {
386
+ variationBucket.equals.push(variation);
387
+ }
388
+ else if (variationBucketTypes.greaters.includes(variation.conditionType)) {
389
+ variationBucket.greaters.push(variation);
390
+ }
391
+ else if (variationBucketTypes.lessers.includes(variation.conditionType)) {
392
+ variationBucket.lessers.push(variation);
393
+ }
394
+ else if (variationBucketTypes.checks.includes(variation.conditionType)) {
395
+ variationBucket.checks.push(variation);
396
+ }
397
+ else if (variationBucketTypes.randoms.includes(variation.conditionType)) {
398
+ variationBucket.randoms.push(variation);
399
+ }
400
+ });
401
+ Object.keys(variationBucket).forEach((bucketKey) => {
402
+ variationBucket[bucketKey].sort((a, b) => compareVariationCondition(a, b, bucketKey));
403
+ });
404
+ let foundVariation;
405
+ const bucketKeys = ['equals', 'greaters', 'lessers', 'checks', 'randoms'];
406
+ bucketKeys.some((bucketKey) => {
407
+ var _a;
408
+ if (!variationBucket[bucketKey].length) {
409
+ return false;
410
+ }
411
+ const variations = bucketKey === 'randoms' ? shuffle(variationBucket[bucketKey], options.random) : variationBucket[bucketKey];
412
+ variations.some((variation) => {
413
+ if (variationCheck({ variation, condition, config, options })) {
414
+ foundVariation = variation;
415
+ return true;
416
+ }
417
+ return false;
418
+ });
419
+ if (!foundVariation && bucketKey === 'randoms' && ((_a = options.shouldUseRandomFallback) === null || _a === void 0 ? void 0 : _a.call(options, config))) {
420
+ foundVariation = variations[0];
421
+ }
422
+ return Boolean(foundVariation);
423
+ });
424
+ return foundVariation;
425
+ };
426
+ const checkAlertVariation = (config, condition, options = {}) => {
427
+ var _a, _b;
428
+ if (!config.variations) {
429
+ return getBaseAlertFallback(config, options);
430
+ }
431
+ const foundVariation = ((_a = options.shouldUseSubscriptionOrder) === null || _a === void 0 ? void 0 : _a.call(options, config, condition)) ? checkByOrder(config, condition, options) : checkByPriorities(config, condition, options);
432
+ if (foundVariation) {
433
+ (_b = options.logger) === null || _b === void 0 ? void 0 : _b.debug('Found variation: ', foundVariation === null || foundVariation === void 0 ? void 0 : foundVariation.name);
434
+ return foundVariation;
435
+ }
436
+ return getBaseAlertFallback(config, options);
437
+ };
438
+ exports.checkAlertVariation = checkAlertVariation;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumiastream/lumia-types",
3
- "version": "3.9.0",
3
+ "version": "3.9.2",
4
4
  "description": "",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/esm/index.js",
@@ -33,8 +33,8 @@
33
33
  "scripts": {
34
34
  "build": "npm run clean && tsc -p tsconfig.json && tsc -p tsconfig.esm.json && npm run sync:custom-overlays && npm run sync:custom-code && npm run sync:chatbot && node ./scripts/finalize-esm-build.mjs",
35
35
  "watch": "node ./scripts/watch.mjs",
36
- "test": "node ./scripts/check-eventlist-alert-invariant.mjs",
37
- "prepublishOnly": "npm run build && npm test",
36
+ "test": "npm run build && node ./scripts/check-eventlist-alert-invariant.mjs && node ./scripts/test-variation-helpers.mjs",
37
+ "prepublishOnly": "npm test",
38
38
  "lint": "tslint --project tsconfig.json \"src/**/*.ts\"",
39
39
  "lint:fix": "tslint --project tsconfig.json --fix \"src/**/*.ts\"",
40
40
  "clean": "rm -rf dist",