@naturalcycles/abba 1.1.0 → 1.5.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.
Files changed (42) hide show
  1. package/dist/abba.d.ts +2 -2
  2. package/dist/abba.js +11 -5
  3. package/dist/index.d.ts +1 -1
  4. package/dist/prisma-output/index-browser.js +141 -0
  5. package/dist/prisma-output/index.d.ts +5521 -0
  6. package/dist/prisma-output/index.js +192 -0
  7. package/dist/prisma-output/libquery_engine-darwin.dylib.node +0 -0
  8. package/dist/prisma-output/libquery_engine-debian-openssl-1.1.x.so.node +0 -0
  9. package/dist/prisma-output/runtime/esm/index-browser.mjs +2370 -0
  10. package/dist/prisma-output/runtime/esm/index.mjs +40587 -0
  11. package/dist/prisma-output/runtime/esm/proxy.mjs +113 -0
  12. package/dist/prisma-output/runtime/index-browser.d.ts +269 -0
  13. package/dist/prisma-output/runtime/index-browser.js +2621 -0
  14. package/dist/prisma-output/runtime/index.d.ts +1373 -0
  15. package/dist/prisma-output/runtime/index.js +52855 -0
  16. package/dist/prisma-output/runtime/proxy.d.ts +1373 -0
  17. package/dist/prisma-output/runtime/proxy.js +13717 -0
  18. package/dist/prisma-output/schema.prisma +47 -0
  19. package/dist/types.d.ts +1 -1
  20. package/dist/util.d.ts +7 -14
  21. package/dist/util.js +18 -29
  22. package/package.json +5 -6
  23. package/readme.md +18 -14
  24. package/src/abba.ts +13 -13
  25. package/src/index.ts +1 -1
  26. package/src/prisma-output/index-browser.js +141 -0
  27. package/src/prisma-output/index.d.ts +5521 -0
  28. package/src/prisma-output/index.js +192 -0
  29. package/src/prisma-output/libquery_engine-darwin.dylib.node +0 -0
  30. package/src/prisma-output/libquery_engine-debian-openssl-1.1.x.so.node +0 -0
  31. package/src/prisma-output/runtime/esm/index-browser.mjs +2370 -0
  32. package/src/prisma-output/runtime/esm/index.mjs +40587 -0
  33. package/src/prisma-output/runtime/esm/proxy.mjs +113 -0
  34. package/src/prisma-output/runtime/index-browser.d.ts +269 -0
  35. package/src/prisma-output/runtime/index-browser.js +2621 -0
  36. package/src/prisma-output/runtime/index.d.ts +1373 -0
  37. package/src/prisma-output/runtime/index.js +52855 -0
  38. package/src/prisma-output/runtime/proxy.d.ts +1373 -0
  39. package/src/prisma-output/runtime/proxy.js +13717 -0
  40. package/src/prisma-output/schema.prisma +47 -0
  41. package/src/types.ts +1 -1
  42. package/src/util.ts +12 -20
@@ -0,0 +1,47 @@
1
+ generator client {
2
+ provider = "prisma-client-js"
3
+ output = "../src/prisma-output"
4
+ binaryTargets = ["native", "darwin", "debian-openssl-1.1.x"]
5
+ }
6
+
7
+ datasource db {
8
+ provider = "mysql"
9
+ url = env("ABBA_DB_URL")
10
+ }
11
+
12
+ model Bucket {
13
+ id Int @id @default(autoincrement())
14
+ experimentId Int
15
+ key String @db.VarChar(10)
16
+ ratio Int
17
+ createdAt DateTime @default(now())
18
+ updatedAt DateTime @updatedAt
19
+ experiment Experiment @relation(fields: [experimentId], references: [id], onDelete: Cascade)
20
+ userAssignments UserAssignment[]
21
+ }
22
+
23
+ model Experiment {
24
+ id Int @id @default(autoincrement())
25
+ name String
26
+ status Int
27
+ sampling Int
28
+ createdAt DateTime @default(now())
29
+ updatedAt DateTime @updatedAt
30
+ description String? @db.VarChar(240)
31
+ rules Json?
32
+ buckets Bucket[]
33
+ userAssignments UserAssignment[]
34
+ }
35
+
36
+ model UserAssignment {
37
+ id Int @id @default(autoincrement())
38
+ userId String
39
+ experimentId Int
40
+ bucketId Int?
41
+ createdAt DateTime @default(now())
42
+ updatedAt DateTime @updatedAt
43
+ bucket Bucket? @relation(fields: [bucketId], references: [id], onDelete: Cascade)
44
+ experiment Experiment @relation(fields: [experimentId], references: [id], onDelete: Cascade)
45
+
46
+ @@unique([userId, experimentId])
47
+ }
package/dist/types.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Bucket, Experiment } from '../prisma/generated/output';
1
+ import { Bucket, Experiment } from './prisma-output';
2
2
  export declare type Unsaved<T> = Omit<T, 'createdAt' | 'updatedAt'> & {
3
3
  id?: number;
4
4
  };
package/dist/util.d.ts CHANGED
@@ -1,11 +1,11 @@
1
- import { Bucket, Experiment } from '../prisma/generated/output';
1
+ import { Bucket } from './prisma-output';
2
2
  import { BucketInput, SegmentationData, SegmentationRule } from '.';
3
3
  /**
4
4
  * Generate a random number between 0 and 100
5
5
  *
6
6
  * @returns
7
7
  */
8
- export declare function rollDie(): number;
8
+ export declare const rollDie: () => number;
9
9
  /**
10
10
  * Determines a users assignment for this experiment. Returns null if they are not considered to be in the sampling group
11
11
  *
@@ -13,28 +13,21 @@ export declare function rollDie(): number;
13
13
  * @param buckets
14
14
  * @returns
15
15
  */
16
- export declare function determineAssignment(sampling: number, buckets: Bucket[]): number | null;
17
- /**
18
- * Determines whether a user will be considered for a bucket assignment based on the experiment sampling rate
19
- *
20
- * @param experiment
21
- * @returns
22
- */
23
- export declare function determineExperiment(experiment: Experiment): boolean;
16
+ export declare const determineAssignment: (sampling: number, buckets: Bucket[]) => number | null;
24
17
  /**
25
18
  * Determines which bucket a user assignment will recieve
26
19
  *
27
20
  * @param buckets
28
21
  * @returns
29
22
  */
30
- export declare function determineBucket(buckets: Bucket[]): number;
23
+ export declare const determineBucket: (buckets: Bucket[]) => number;
31
24
  /**
32
25
  * Validate the total ratio of the buckets equals 100
33
26
  *
34
27
  * @param buckets
35
28
  * @returns
36
29
  */
37
- export declare function validateBuckets(buckets: BucketInput[]): void;
30
+ export declare const validateTotalBucketRatio: (buckets: BucketInput[]) => void;
38
31
  /**
39
32
  * Validate a users segmentation data against multiple rules. Returns false if any fail
40
33
  *
@@ -42,7 +35,7 @@ export declare function validateBuckets(buckets: BucketInput[]): void;
42
35
  * @param segmentationData
43
36
  * @returns
44
37
  */
45
- export declare function validateSegmentationRules(rules: SegmentationRule[], segmentationData: SegmentationData): boolean;
38
+ export declare const validateSegmentationRules: (rules: SegmentationRule[], segmentationData: SegmentationData) => boolean;
46
39
  /**
47
40
  * Validate a users segmentation data against a single rule
48
41
  *
@@ -50,4 +43,4 @@ export declare function validateSegmentationRules(rules: SegmentationRule[], seg
50
43
  * @param segmentationData
51
44
  * @returns
52
45
  */
53
- export declare function validateSegmentationRule(rule: SegmentationRule, data: SegmentationData): boolean;
46
+ export declare const validateSegmentationRule: (rule: SegmentationRule, data: SegmentationData) => boolean;
package/dist/util.js CHANGED
@@ -1,15 +1,15 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.validateSegmentationRule = exports.validateSegmentationRules = exports.validateBuckets = exports.determineBucket = exports.determineExperiment = exports.determineAssignment = exports.rollDie = void 0;
3
+ exports.validateSegmentationRule = exports.validateSegmentationRules = exports.validateTotalBucketRatio = exports.determineBucket = exports.determineAssignment = exports.rollDie = void 0;
4
4
  const semver_1 = require("semver");
5
5
  /**
6
6
  * Generate a random number between 0 and 100
7
7
  *
8
8
  * @returns
9
9
  */
10
- function rollDie() {
10
+ const rollDie = () => {
11
11
  return Math.random() * 100;
12
- }
12
+ };
13
13
  exports.rollDie = rollDie;
14
14
  /**
15
15
  * Determines a users assignment for this experiment. Returns null if they are not considered to be in the sampling group
@@ -18,34 +18,23 @@ exports.rollDie = rollDie;
18
18
  * @param buckets
19
19
  * @returns
20
20
  */
21
- function determineAssignment(sampling, buckets) {
21
+ const determineAssignment = (sampling, buckets) => {
22
22
  // Should this person be considered for the experiment?
23
- const isIncludedInSample = rollDie() <= sampling;
24
- if (!isIncludedInSample) {
23
+ if ((0, exports.rollDie)() > sampling) {
25
24
  return null;
26
25
  }
27
26
  // get their bucket
28
- return determineBucket(buckets);
29
- }
27
+ return (0, exports.determineBucket)(buckets);
28
+ };
30
29
  exports.determineAssignment = determineAssignment;
31
- /**
32
- * Determines whether a user will be considered for a bucket assignment based on the experiment sampling rate
33
- *
34
- * @param experiment
35
- * @returns
36
- */
37
- function determineExperiment(experiment) {
38
- return rollDie() <= experiment.sampling; // between 0 and 100
39
- }
40
- exports.determineExperiment = determineExperiment;
41
30
  /**
42
31
  * Determines which bucket a user assignment will recieve
43
32
  *
44
33
  * @param buckets
45
34
  * @returns
46
35
  */
47
- function determineBucket(buckets) {
48
- const bucketRoll = rollDie();
36
+ const determineBucket = (buckets) => {
37
+ const bucketRoll = (0, exports.rollDie)();
49
38
  let range;
50
39
  const bucket = buckets.find(b => {
51
40
  if (!range) {
@@ -62,7 +51,7 @@ function determineBucket(buckets) {
62
51
  throw new Error('Could not detetermine bucket from ratios');
63
52
  }
64
53
  return bucket.id;
65
- }
54
+ };
66
55
  exports.determineBucket = determineBucket;
67
56
  /**
68
57
  * Validate the total ratio of the buckets equals 100
@@ -70,13 +59,13 @@ exports.determineBucket = determineBucket;
70
59
  * @param buckets
71
60
  * @returns
72
61
  */
73
- function validateBuckets(buckets) {
62
+ const validateTotalBucketRatio = (buckets) => {
74
63
  const bucketSum = buckets.reduce((sum, current) => sum + current.ratio, 0);
75
64
  if (bucketSum !== 100) {
76
65
  throw new Error('Total bucket ratio must be 100 before you can activate an experiment');
77
66
  }
78
- }
79
- exports.validateBuckets = validateBuckets;
67
+ };
68
+ exports.validateTotalBucketRatio = validateTotalBucketRatio;
80
69
  /**
81
70
  * Validate a users segmentation data against multiple rules. Returns false if any fail
82
71
  *
@@ -84,13 +73,13 @@ exports.validateBuckets = validateBuckets;
84
73
  * @param segmentationData
85
74
  * @returns
86
75
  */
87
- function validateSegmentationRules(rules, segmentationData) {
76
+ const validateSegmentationRules = (rules, segmentationData) => {
88
77
  for (const rule of rules) {
89
- if (!validateSegmentationRule(rule, segmentationData))
78
+ if (!(0, exports.validateSegmentationRule)(rule, segmentationData))
90
79
  return false;
91
80
  }
92
81
  return true;
93
- }
82
+ };
94
83
  exports.validateSegmentationRules = validateSegmentationRules;
95
84
  /**
96
85
  * Validate a users segmentation data against a single rule
@@ -99,7 +88,7 @@ exports.validateSegmentationRules = validateSegmentationRules;
99
88
  * @param segmentationData
100
89
  * @returns
101
90
  */
102
- function validateSegmentationRule(rule, data) {
91
+ const validateSegmentationRule = (rule, data) => {
103
92
  const { key, value, operator } = rule;
104
93
  if (operator === '==') {
105
94
  return data[key] === value;
@@ -119,5 +108,5 @@ function validateSegmentationRule(rule, data) {
119
108
  else {
120
109
  return false;
121
110
  }
122
- }
111
+ };
123
112
  exports.validateSegmentationRule = validateSegmentationRule;
package/package.json CHANGED
@@ -1,8 +1,11 @@
1
1
  {
2
2
  "name": "@naturalcycles/abba",
3
- "version": "1.1.0",
3
+ "version": "1.5.0",
4
4
  "scripts": {
5
- "prepare": "husky install"
5
+ "prepare": "husky install",
6
+ "build": "build && npm run copy-prisma-output",
7
+ "build-prod": "build-prod && npm run copy-prisma-output",
8
+ "copy-prisma-output": "cp -R src/prisma-output dist"
6
9
  },
7
10
  "dependencies": {
8
11
  "@prisma/client": "^3.9.2",
@@ -10,15 +13,11 @@
10
13
  },
11
14
  "devDependencies": {
12
15
  "@naturalcycles/dev-lib": "^12.0.0",
13
- "@types/json-logic-js": "^1.2.1",
14
16
  "@types/node": "^16.0.0",
15
17
  "@types/semver": "^7.3.9",
16
18
  "jest": "^27.5.1",
17
19
  "prisma": "^3.9.2"
18
20
  },
19
- "directories": {
20
- "prisma": "./prisma"
21
- },
22
21
  "files": [
23
22
  "dist",
24
23
  "src",
package/readme.md CHANGED
@@ -77,17 +77,9 @@ This template doesn't rely on any external dependencies or services._
77
77
  npm install @naturalcyles/abba
78
78
  ```
79
79
 
80
- 2. Execute the [sql script found here](/src/prisma/migrations/init.md) to generate the required DB
81
- Schema
82
-
83
- 3. Create a `.env` file add add your Database Url using the following key:
84
- ```sh
85
- EXPERIMENT_MANAGER_DB_URL="{{insert your instance url}}"
86
- ```
87
- 4. Create a new instance of the AssignmentManager class
88
- ```js
89
- const manager = new AssignmentManager()
90
- ```
80
+ 2. Execute the
81
+ [sql script found here](https://github.com/NaturalCycles/abba/blob/master/prisma/migrations/20220218075343_init/migration.sql)
82
+ to generate the required DB Schema
91
83
 
92
84
  <p align="right">(<a href="#top">back to top</a>)</p>
93
85
 
@@ -97,12 +89,24 @@ This template doesn't rely on any external dependencies or services._
97
89
 
98
90
  <div id="usage"></div>
99
91
 
92
+ ### Create an instance of Abba
93
+
94
+ Creates an instance of Abba. You can pass in the database url in the constructor. If it does not
95
+ exist it will fallback to trying to use the `ABBA_DB_URL` which must be added to your environment
96
+ variables.
97
+
98
+ ```js
99
+ const abba = new Abba('url')
100
+ // or reading from process.env.ABBA_DB_URL
101
+ const abba = new Abba()
102
+ ```
103
+
100
104
  ### Create a new experiment
101
105
 
102
106
  Creates a new experiment
103
107
 
104
108
  ```js
105
- async manager.createExperiment(
109
+ async createExperiment(
106
110
  input: ExperimentInput,
107
111
  buckets: BucketInput[]
108
112
  ): Promise<Experiment>
@@ -113,7 +117,7 @@ async manager.createExperiment(
113
117
  Updates an existing experiment.
114
118
 
115
119
  ```js
116
- async manager.updateExperiment(
120
+ async updateExperiment(
117
121
  id: number,
118
122
  input: ExperimentInput,
119
123
  buckets: BucketInput[]
@@ -125,7 +129,7 @@ async manager.updateExperiment(
125
129
  Delete an experiment. Removes all users assignments and buckets
126
130
 
127
131
  ```js
128
- async manager.deleteExperiment(
132
+ async deleteExperiment(
129
133
  id: number
130
134
  ): Promise<void>
131
135
  ```
package/src/abba.ts CHANGED
@@ -1,12 +1,6 @@
1
- import {
2
- Bucket,
3
- Experiment,
4
- Prisma,
5
- PrismaClient,
6
- UserAssignment,
7
- } from '../prisma/generated/output'
1
+ import { Bucket, Experiment, Prisma, PrismaClient, UserAssignment } from './prisma-output'
8
2
  import { AssignmentStatus } from './types'
9
- import { determineAssignment, validateSegmentationRules, validateBuckets } from './util'
3
+ import { determineAssignment, validateSegmentationRules, validateTotalBucketRatio } from './util'
10
4
  import {
11
5
  BucketInput,
12
6
  ExperimentWithBuckets,
@@ -23,8 +17,14 @@ import {
23
17
  export class Abba {
24
18
  private client: PrismaClient
25
19
 
26
- constructor() {
27
- this.client = new PrismaClient()
20
+ constructor(dbUrl?: string) {
21
+ this.client = new PrismaClient({
22
+ datasources: {
23
+ db: {
24
+ url: dbUrl,
25
+ },
26
+ },
27
+ })
28
28
  }
29
29
  /**
30
30
  * Returns all experiments
@@ -50,7 +50,7 @@ export class Abba {
50
50
  buckets: BucketInput[],
51
51
  ): Promise<ExperimentWithBuckets> {
52
52
  if (experiment.status === AssignmentStatus.Active) {
53
- validateBuckets(buckets)
53
+ validateTotalBucketRatio(buckets)
54
54
  }
55
55
 
56
56
  const created = await this.client.experiment.create({
@@ -85,7 +85,7 @@ export class Abba {
85
85
  buckets: BucketInput[],
86
86
  ): Promise<ExperimentWithBuckets> {
87
87
  if (experiment.status === AssignmentStatus.Active) {
88
- validateBuckets(buckets)
88
+ validateTotalBucketRatio(buckets)
89
89
  }
90
90
 
91
91
  const updatedExperiment = await this.updateExperiment(id, experiment)
@@ -260,7 +260,7 @@ export class Abba {
260
260
  where: { id },
261
261
  data: {
262
262
  ...experiment,
263
- rules: experiment.rules as Prisma.InputJsonObject,
263
+ rules: experiment.rules as Prisma.InputJsonArray,
264
264
  },
265
265
  })
266
266
  }
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type { Bucket, Experiment, UserAssignment } from '../prisma/generated/output'
1
+ export type { Bucket, Experiment, UserAssignment } from './prisma-output'
2
2
 
3
3
  export * from './types'
4
4
 
@@ -0,0 +1,141 @@
1
+ Object.defineProperty(exports, '__esModule', { value: true })
2
+
3
+ const { Decimal } = require('./runtime/index-browser')
4
+
5
+ const Prisma = {}
6
+
7
+ exports.Prisma = Prisma
8
+
9
+ /**
10
+ * Prisma Client JS version: 3.9.2
11
+ * Query Engine version: bcc2ff906db47790ee902e7bbc76d7ffb1893009
12
+ */
13
+ Prisma.prismaVersion = {
14
+ client: '3.9.2',
15
+ engine: 'bcc2ff906db47790ee902e7bbc76d7ffb1893009',
16
+ }
17
+
18
+ Prisma.PrismaClientKnownRequestError = () => {
19
+ throw new Error(`PrismaClientKnownRequestError is unable to be run in the browser.
20
+ In case this error is unexpected for you, please report it in https://github.com/prisma/prisma/issues`)
21
+ }
22
+ Prisma.PrismaClientUnknownRequestError = () => {
23
+ throw new Error(`PrismaClientUnknownRequestError is unable to be run in the browser.
24
+ In case this error is unexpected for you, please report it in https://github.com/prisma/prisma/issues`)
25
+ }
26
+ Prisma.PrismaClientRustPanicError = () => {
27
+ throw new Error(`PrismaClientRustPanicError is unable to be run in the browser.
28
+ In case this error is unexpected for you, please report it in https://github.com/prisma/prisma/issues`)
29
+ }
30
+ Prisma.PrismaClientInitializationError = () => {
31
+ throw new Error(`PrismaClientInitializationError is unable to be run in the browser.
32
+ In case this error is unexpected for you, please report it in https://github.com/prisma/prisma/issues`)
33
+ }
34
+ Prisma.PrismaClientValidationError = () => {
35
+ throw new Error(`PrismaClientValidationError is unable to be run in the browser.
36
+ In case this error is unexpected for you, please report it in https://github.com/prisma/prisma/issues`)
37
+ }
38
+ Prisma.Decimal = Decimal
39
+
40
+ /**
41
+ * Re-export of sql-template-tag
42
+ */
43
+ Prisma.sql = () => {
44
+ throw new Error(`sqltag is unable to be run in the browser.
45
+ In case this error is unexpected for you, please report it in https://github.com/prisma/prisma/issues`)
46
+ }
47
+ Prisma.empty = () => {
48
+ throw new Error(`empty is unable to be run in the browser.
49
+ In case this error is unexpected for you, please report it in https://github.com/prisma/prisma/issues`)
50
+ }
51
+ Prisma.join = () => {
52
+ throw new Error(`join is unable to be run in the browser.
53
+ In case this error is unexpected for you, please report it in https://github.com/prisma/prisma/issues`)
54
+ }
55
+ Prisma.raw = () => {
56
+ throw new Error(`raw is unable to be run in the browser.
57
+ In case this error is unexpected for you, please report it in https://github.com/prisma/prisma/issues`)
58
+ }
59
+ Prisma.validator = () => val => val
60
+
61
+ /**
62
+ * Shorthand utilities for JSON filtering
63
+ */
64
+ Prisma.DbNull = 'DbNull'
65
+ Prisma.JsonNull = 'JsonNull'
66
+ Prisma.AnyNull = 'AnyNull'
67
+
68
+ /**
69
+ * Enums
70
+ */
71
+ // Based on
72
+ // https://github.com/microsoft/TypeScript/issues/3192#issuecomment-261720275
73
+ function makeEnum(x) {
74
+ return x
75
+ }
76
+
77
+ exports.Prisma.BucketScalarFieldEnum = makeEnum({
78
+ id: 'id',
79
+ experimentId: 'experimentId',
80
+ key: 'key',
81
+ ratio: 'ratio',
82
+ createdAt: 'createdAt',
83
+ updatedAt: 'updatedAt',
84
+ })
85
+
86
+ exports.Prisma.ExperimentScalarFieldEnum = makeEnum({
87
+ id: 'id',
88
+ name: 'name',
89
+ status: 'status',
90
+ sampling: 'sampling',
91
+ createdAt: 'createdAt',
92
+ updatedAt: 'updatedAt',
93
+ description: 'description',
94
+ rules: 'rules',
95
+ })
96
+
97
+ exports.Prisma.UserAssignmentScalarFieldEnum = makeEnum({
98
+ id: 'id',
99
+ userId: 'userId',
100
+ experimentId: 'experimentId',
101
+ bucketId: 'bucketId',
102
+ createdAt: 'createdAt',
103
+ updatedAt: 'updatedAt',
104
+ })
105
+
106
+ exports.Prisma.SortOrder = makeEnum({
107
+ asc: 'asc',
108
+ desc: 'desc',
109
+ })
110
+
111
+ exports.Prisma.NullableJsonNullValueInput = makeEnum({
112
+ DbNull: 'DbNull',
113
+ JsonNull: 'JsonNull',
114
+ })
115
+
116
+ exports.Prisma.JsonNullValueFilter = makeEnum({
117
+ DbNull: 'DbNull',
118
+ JsonNull: 'JsonNull',
119
+ AnyNull: 'AnyNull',
120
+ })
121
+
122
+ exports.Prisma.ModelName = makeEnum({
123
+ Bucket: 'Bucket',
124
+ Experiment: 'Experiment',
125
+ UserAssignment: 'UserAssignment',
126
+ })
127
+
128
+ /**
129
+ * Create the Client
130
+ */
131
+ class PrismaClient {
132
+ constructor() {
133
+ throw new Error(
134
+ `PrismaClient is unable to be run in the browser.
135
+ In case this error is unexpected for you, please report it in https://github.com/prisma/prisma/issues`,
136
+ )
137
+ }
138
+ }
139
+ exports.PrismaClient = PrismaClient
140
+
141
+ Object.assign(exports, Prisma)