@naturalcycles/abba 1.25.0 → 1.25.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.
- package/dist/abba.d.ts +1 -1
- package/dist/abba.js +9 -5
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/util.js +1 -1
- package/package.json +10 -5
- package/src/abba.ts +10 -6
- package/src/index.ts +1 -1
- package/src/util.ts +2 -2
package/dist/abba.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Unsaved } from '@naturalcycles/js-lib';
|
|
2
|
-
import { AbbaConfig, Bucket, Experiment, ExperimentAssignmentStatistics, ExperimentWithBuckets, GeneratedUserAssignment } from './types';
|
|
3
2
|
import { SegmentationData } from '.';
|
|
3
|
+
import { AbbaConfig, Bucket, Experiment, ExperimentAssignmentStatistics, ExperimentWithBuckets, GeneratedUserAssignment } from './types';
|
|
4
4
|
export declare class Abba {
|
|
5
5
|
cfg: AbbaConfig;
|
|
6
6
|
private experimentDao;
|
package/dist/abba.js
CHANGED
|
@@ -32,7 +32,7 @@ class Abba {
|
|
|
32
32
|
*/
|
|
33
33
|
async updateUserId(oldId, newId) {
|
|
34
34
|
const query = this.userAssignmentDao.query().filterEq('userId', oldId);
|
|
35
|
-
await this.userAssignmentDao.
|
|
35
|
+
await this.userAssignmentDao.patchByQuery(query, { userId: newId });
|
|
36
36
|
}
|
|
37
37
|
/**
|
|
38
38
|
* Returns all experiments.
|
|
@@ -128,11 +128,12 @@ class Abba {
|
|
|
128
128
|
const experiment = await this.experimentDao.getOneBy('key', experimentKey);
|
|
129
129
|
(0, js_lib_1._assert)(experiment, `Experiment does not exist: ${experimentKey}`);
|
|
130
130
|
// Inactive experiments should never return an assignment
|
|
131
|
-
if (experiment.status === types_1.AssignmentStatus.Inactive)
|
|
131
|
+
if (experiment.status === types_1.AssignmentStatus.Inactive) {
|
|
132
132
|
return {
|
|
133
133
|
experiment,
|
|
134
134
|
assignment: null,
|
|
135
135
|
};
|
|
136
|
+
}
|
|
136
137
|
const buckets = await this.bucketDao.getBy('experimentId', experiment.id);
|
|
137
138
|
const existingAssignments = await this.userAssignmentDao.getBy('userId', userId);
|
|
138
139
|
const existing = existingAssignments.find(a => a.experimentId === experiment.id);
|
|
@@ -149,26 +150,29 @@ class Abba {
|
|
|
149
150
|
};
|
|
150
151
|
}
|
|
151
152
|
// No existing assignment, but we don't want to generate a new one
|
|
152
|
-
if (existingOnly || experiment.status === types_1.AssignmentStatus.Paused)
|
|
153
|
+
if (existingOnly || experiment.status === types_1.AssignmentStatus.Paused) {
|
|
153
154
|
return {
|
|
154
155
|
experiment,
|
|
155
156
|
assignment: null,
|
|
156
157
|
};
|
|
158
|
+
}
|
|
157
159
|
const experiments = await this.getAllExperiments();
|
|
158
160
|
const exclusionSet = (0, util_1.getUserExclusionSet)(experiments, existingAssignments);
|
|
159
|
-
if (!(0, util_1.canGenerateNewAssignments)(experiment, exclusionSet))
|
|
161
|
+
if (!(0, util_1.canGenerateNewAssignments)(experiment, exclusionSet)) {
|
|
160
162
|
return {
|
|
161
163
|
experiment,
|
|
162
164
|
assignment: null,
|
|
163
165
|
};
|
|
166
|
+
}
|
|
164
167
|
(0, js_lib_1._assert)(segmentationData, 'Segmentation data required when creating a new assignment');
|
|
165
168
|
const experimentWithBuckets = { ...experiment, buckets };
|
|
166
169
|
const assignment = (0, util_1.generateUserAssignmentData)(experimentWithBuckets, userId, segmentationData);
|
|
167
|
-
if (!assignment)
|
|
170
|
+
if (!assignment) {
|
|
168
171
|
return {
|
|
169
172
|
experiment,
|
|
170
173
|
assignment: null,
|
|
171
174
|
};
|
|
175
|
+
}
|
|
172
176
|
const newAssignment = await this.userAssignmentDao.save(assignment);
|
|
173
177
|
const bucket = buckets.find(b => b.id === newAssignment.bucketId);
|
|
174
178
|
return {
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const tslib_1 = require("tslib");
|
|
4
|
-
tslib_1.__exportStar(require("./types"), exports);
|
|
5
4
|
tslib_1.__exportStar(require("./abba"), exports);
|
|
5
|
+
tslib_1.__exportStar(require("./types"), exports);
|
package/dist/util.js
CHANGED
|
@@ -108,7 +108,7 @@ exports.segmentationRuleMap = {
|
|
|
108
108
|
return (0, semver_1.satisfies)(keyValue?.toString() || '', ruleValue.toString());
|
|
109
109
|
},
|
|
110
110
|
[types_1.SegmentationRuleOperator.Regex](keyValue, ruleValue) {
|
|
111
|
-
return new RegExp(
|
|
111
|
+
return new RegExp(ruleValue).test(keyValue?.toString() || '');
|
|
112
112
|
},
|
|
113
113
|
[types_1.SegmentationRuleOperator.Boolean](keyValue, ruleValue) {
|
|
114
114
|
// If it's true, then must be true
|
package/package.json
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@naturalcycles/abba",
|
|
3
|
-
"version": "1.25.
|
|
3
|
+
"version": "1.25.2",
|
|
4
4
|
"scripts": {
|
|
5
|
-
"prepare": "husky"
|
|
5
|
+
"prepare": "husky",
|
|
6
|
+
"build": "dev-lib build",
|
|
7
|
+
"test": "dev-lib test",
|
|
8
|
+
"lint": "dev-lib lint",
|
|
9
|
+
"bt": "dev-lib bt",
|
|
10
|
+
"lbt": "dev-lib lbt"
|
|
6
11
|
},
|
|
7
12
|
"dependencies": {
|
|
8
13
|
"@naturalcycles/db-lib": "^9.14.1",
|
|
@@ -11,8 +16,8 @@
|
|
|
11
16
|
"semver": "^7.3.5"
|
|
12
17
|
},
|
|
13
18
|
"devDependencies": {
|
|
14
|
-
"@naturalcycles/dev-lib": "^
|
|
15
|
-
"@types/node": "^
|
|
19
|
+
"@naturalcycles/dev-lib": "^15.19.0",
|
|
20
|
+
"@types/node": "^22.7.4",
|
|
16
21
|
"@types/semver": "^7.3.9",
|
|
17
22
|
"jest": "^29.3.1"
|
|
18
23
|
},
|
|
@@ -34,7 +39,7 @@
|
|
|
34
39
|
"url": "https://github.com/NaturalCycles/abba"
|
|
35
40
|
},
|
|
36
41
|
"engines": {
|
|
37
|
-
"node": ">=
|
|
42
|
+
"node": ">=20.13.0"
|
|
38
43
|
},
|
|
39
44
|
"description": "AB test assignment configuration tool for Node.js",
|
|
40
45
|
"author": "Natural Cycles Team",
|
package/src/abba.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { _assert, _Memo, _shuffle, pMap, Unsaved } from '@naturalcycles/js-lib'
|
|
2
2
|
import { LRUMemoCache } from '@naturalcycles/nodejs-lib'
|
|
3
|
+
import { SegmentationData } from '.'
|
|
3
4
|
import { bucketDao } from './dao/bucket.dao'
|
|
4
5
|
import { experimentDao } from './dao/experiment.dao'
|
|
5
6
|
import { userAssignmentDao } from './dao/userAssignment.dao'
|
|
@@ -20,7 +21,6 @@ import {
|
|
|
20
21
|
getUserExclusionSet,
|
|
21
22
|
validateTotalBucketRatio,
|
|
22
23
|
} from './util'
|
|
23
|
-
import { SegmentationData } from '.'
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* 10 minutes
|
|
@@ -48,7 +48,7 @@ export class Abba {
|
|
|
48
48
|
*/
|
|
49
49
|
async updateUserId(oldId: string, newId: string): Promise<void> {
|
|
50
50
|
const query = this.userAssignmentDao.query().filterEq('userId', oldId)
|
|
51
|
-
await this.userAssignmentDao.
|
|
51
|
+
await this.userAssignmentDao.patchByQuery(query, { userId: newId })
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
/**
|
|
@@ -183,11 +183,12 @@ export class Abba {
|
|
|
183
183
|
_assert(experiment, `Experiment does not exist: ${experimentKey}`)
|
|
184
184
|
|
|
185
185
|
// Inactive experiments should never return an assignment
|
|
186
|
-
if (experiment.status === AssignmentStatus.Inactive)
|
|
186
|
+
if (experiment.status === AssignmentStatus.Inactive) {
|
|
187
187
|
return {
|
|
188
188
|
experiment,
|
|
189
189
|
assignment: null,
|
|
190
190
|
}
|
|
191
|
+
}
|
|
191
192
|
|
|
192
193
|
const buckets = await this.bucketDao.getBy('experimentId', experiment.id)
|
|
193
194
|
const existingAssignments = await this.userAssignmentDao.getBy('userId', userId)
|
|
@@ -206,29 +207,32 @@ export class Abba {
|
|
|
206
207
|
}
|
|
207
208
|
|
|
208
209
|
// No existing assignment, but we don't want to generate a new one
|
|
209
|
-
if (existingOnly || experiment.status === AssignmentStatus.Paused)
|
|
210
|
+
if (existingOnly || experiment.status === AssignmentStatus.Paused) {
|
|
210
211
|
return {
|
|
211
212
|
experiment,
|
|
212
213
|
assignment: null,
|
|
213
214
|
}
|
|
215
|
+
}
|
|
214
216
|
|
|
215
217
|
const experiments = await this.getAllExperiments()
|
|
216
218
|
const exclusionSet = getUserExclusionSet(experiments, existingAssignments)
|
|
217
|
-
if (!canGenerateNewAssignments(experiment, exclusionSet))
|
|
219
|
+
if (!canGenerateNewAssignments(experiment, exclusionSet)) {
|
|
218
220
|
return {
|
|
219
221
|
experiment,
|
|
220
222
|
assignment: null,
|
|
221
223
|
}
|
|
224
|
+
}
|
|
222
225
|
|
|
223
226
|
_assert(segmentationData, 'Segmentation data required when creating a new assignment')
|
|
224
227
|
|
|
225
228
|
const experimentWithBuckets = { ...experiment, buckets }
|
|
226
229
|
const assignment = generateUserAssignmentData(experimentWithBuckets, userId, segmentationData)
|
|
227
|
-
if (!assignment)
|
|
230
|
+
if (!assignment) {
|
|
228
231
|
return {
|
|
229
232
|
experiment,
|
|
230
233
|
assignment: null,
|
|
231
234
|
}
|
|
235
|
+
}
|
|
232
236
|
|
|
233
237
|
const newAssignment = await this.userAssignmentDao.save(assignment)
|
|
234
238
|
|
package/src/index.ts
CHANGED
package/src/util.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { localDate, Unsaved } from '@naturalcycles/js-lib'
|
|
2
2
|
import { satisfies } from 'semver'
|
|
3
3
|
import {
|
|
4
4
|
AssignmentStatus,
|
|
@@ -127,7 +127,7 @@ export const segmentationRuleMap: Record<SegmentationRuleOperator, SegmentationR
|
|
|
127
127
|
return satisfies(keyValue?.toString() || '', ruleValue.toString())
|
|
128
128
|
},
|
|
129
129
|
[SegmentationRuleOperator.Regex](keyValue, ruleValue) {
|
|
130
|
-
return new RegExp(
|
|
130
|
+
return new RegExp(ruleValue).test(keyValue?.toString() || '')
|
|
131
131
|
},
|
|
132
132
|
[SegmentationRuleOperator.Boolean](keyValue, ruleValue) {
|
|
133
133
|
// If it's true, then must be true
|