@eventvisor/sdk 0.0.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.
Files changed (67) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +9 -0
  3. package/dist/attributesManager.d.ts +36 -0
  4. package/dist/bucketer.d.ts +30 -0
  5. package/dist/compareVersions.d.ts +4 -0
  6. package/dist/conditions.d.ts +20 -0
  7. package/dist/datafileReader.d.ts +29 -0
  8. package/dist/effectsManager.d.ts +33 -0
  9. package/dist/emitter.d.ts +11 -0
  10. package/dist/index.d.ts +12 -0
  11. package/dist/index.js +2 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/index.mjs +2 -0
  14. package/dist/index.mjs.gz +0 -0
  15. package/dist/index.mjs.map +1 -0
  16. package/dist/instance.d.ts +67 -0
  17. package/dist/logger.d.ts +26 -0
  18. package/dist/modulesManager.d.ts +67 -0
  19. package/dist/murmurhash.d.ts +1 -0
  20. package/dist/persister.d.ts +40 -0
  21. package/dist/sourceResolver.d.ts +31 -0
  22. package/dist/transformer.d.ts +21 -0
  23. package/dist/validator.d.ts +28 -0
  24. package/jest.config.js +6 -0
  25. package/lib/attributesManager.d.ts +36 -0
  26. package/lib/bucketer.d.ts +30 -0
  27. package/lib/compareVersions.d.ts +4 -0
  28. package/lib/conditions.d.ts +20 -0
  29. package/lib/datafileReader.d.ts +29 -0
  30. package/lib/effectsManager.d.ts +33 -0
  31. package/lib/emitter.d.ts +11 -0
  32. package/lib/index.d.ts +12 -0
  33. package/lib/instance.d.ts +67 -0
  34. package/lib/logger.d.ts +26 -0
  35. package/lib/modulesManager.d.ts +67 -0
  36. package/lib/murmurhash.d.ts +1 -0
  37. package/lib/persister.d.ts +40 -0
  38. package/lib/sourceResolver.d.ts +31 -0
  39. package/lib/transformer.d.ts +21 -0
  40. package/lib/validator.d.ts +28 -0
  41. package/package.json +45 -0
  42. package/src/attributesManager.ts +181 -0
  43. package/src/bucketer.spec.ts +156 -0
  44. package/src/bucketer.ts +152 -0
  45. package/src/compareVersions.ts +93 -0
  46. package/src/conditions.ts +224 -0
  47. package/src/datafileReader.ts +133 -0
  48. package/src/effectsManager.ts +214 -0
  49. package/src/emitter.ts +64 -0
  50. package/src/index.spec.ts +5 -0
  51. package/src/index.ts +14 -0
  52. package/src/instance.spec.ts +184 -0
  53. package/src/instance.ts +608 -0
  54. package/src/logger.ts +90 -0
  55. package/src/modulesManager.ts +276 -0
  56. package/src/murmurhash.ts +71 -0
  57. package/src/persister.ts +162 -0
  58. package/src/sourceResolver.spec.ts +253 -0
  59. package/src/sourceResolver.ts +213 -0
  60. package/src/transformer.ts +316 -0
  61. package/src/transformer_static.spec.ts +377 -0
  62. package/src/transformer_types.spec.ts +820 -0
  63. package/src/validator.spec.ts +579 -0
  64. package/src/validator.ts +366 -0
  65. package/tsconfig.cjs.json +8 -0
  66. package/tsconfig.esm.json +8 -0
  67. package/webpack.config.js +80 -0
@@ -0,0 +1,224 @@
1
+ import type { PlainCondition, Condition, Inputs } from "@eventvisor/types";
2
+
3
+ import type { GetRegex } from "./datafileReader";
4
+ import type { SourceResolver } from "./sourceResolver";
5
+
6
+ import { compareVersions } from "./compareVersions";
7
+ import { Logger } from "./logger";
8
+
9
+ export type GetConditionsChecker = () => ConditionsChecker;
10
+
11
+ export interface ConditionsCheckerOptions {
12
+ getRegex: GetRegex;
13
+ sourceResolver: SourceResolver;
14
+ logger: Logger;
15
+ }
16
+
17
+ export class ConditionsChecker {
18
+ private getRegex: GetRegex;
19
+ private sourceResolver: SourceResolver;
20
+ private logger: Logger;
21
+
22
+ constructor(options: ConditionsCheckerOptions) {
23
+ this.getRegex = options.getRegex;
24
+ this.sourceResolver = options.sourceResolver;
25
+ this.logger = options.logger;
26
+ }
27
+
28
+ async isMatched(condition: PlainCondition, inputs: Inputs): Promise<boolean> {
29
+ const { operator, value, regexFlags } = condition;
30
+
31
+ const sourceValue = await this.sourceResolver.resolve(condition, inputs);
32
+
33
+ if (operator === "equals") {
34
+ return sourceValue === value;
35
+ } else if (operator === "notEquals") {
36
+ return sourceValue !== value;
37
+ } else if (operator === "before" || operator === "after") {
38
+ // date comparisons
39
+ const valueInContext = sourceValue as string | Date;
40
+
41
+ const dateInContext =
42
+ valueInContext instanceof Date ? valueInContext : new Date(valueInContext);
43
+ const dateInCondition = value instanceof Date ? value : new Date(value as string);
44
+
45
+ return operator === "before"
46
+ ? dateInContext < dateInCondition
47
+ : dateInContext > dateInCondition;
48
+ } else if (
49
+ Array.isArray(value) &&
50
+ (["string", "number"].indexOf(typeof sourceValue) !== -1 || sourceValue === null)
51
+ ) {
52
+ // in / notIn (where condition value is an array)
53
+ const valueInContext = sourceValue as string;
54
+
55
+ if (operator === "in") {
56
+ return value.indexOf(valueInContext) !== -1;
57
+ } else if (operator === "notIn") {
58
+ return value.indexOf(valueInContext) === -1;
59
+ }
60
+ } else if (typeof sourceValue === "string" && typeof value === "string") {
61
+ // string
62
+ const valueInContext = sourceValue as string;
63
+
64
+ if (operator === "contains") {
65
+ return valueInContext.indexOf(value) !== -1;
66
+ } else if (operator === "notContains") {
67
+ return valueInContext.indexOf(value) === -1;
68
+ } else if (operator === "startsWith") {
69
+ return valueInContext.startsWith(value);
70
+ } else if (operator === "endsWith") {
71
+ return valueInContext.endsWith(value);
72
+ } else if (operator === "semverEquals") {
73
+ return compareVersions(valueInContext, value) === 0;
74
+ } else if (operator === "semverNotEquals") {
75
+ return compareVersions(valueInContext, value) !== 0;
76
+ } else if (operator === "semverGreaterThan") {
77
+ return compareVersions(valueInContext, value) === 1;
78
+ } else if (operator === "semverGreaterThanOrEquals") {
79
+ return compareVersions(valueInContext, value) >= 0;
80
+ } else if (operator === "semverLessThan") {
81
+ return compareVersions(valueInContext, value) === -1;
82
+ } else if (operator === "semverLessThanOrEquals") {
83
+ return compareVersions(valueInContext, value) <= 0;
84
+ } else if (operator === "matches") {
85
+ const regex = this.getRegex(value, regexFlags || "");
86
+ return regex.test(valueInContext);
87
+ } else if (operator === "notMatches") {
88
+ const regex = this.getRegex(value, regexFlags || "");
89
+ return !regex.test(valueInContext);
90
+ }
91
+ } else if (typeof sourceValue === "number" && typeof value === "number") {
92
+ // numeric
93
+ const valueInContext = sourceValue as number;
94
+
95
+ if (operator === "greaterThan") {
96
+ return valueInContext > value;
97
+ } else if (operator === "greaterThanOrEquals") {
98
+ return valueInContext >= value;
99
+ } else if (operator === "lessThan") {
100
+ return valueInContext < value;
101
+ } else if (operator === "lessThanOrEquals") {
102
+ return valueInContext <= value;
103
+ }
104
+ } else if (operator === "exists") {
105
+ // @TODO: may require extra care for null values
106
+ return typeof sourceValue !== "undefined";
107
+ } else if (operator === "notExists") {
108
+ return typeof sourceValue === "undefined";
109
+ } else if (Array.isArray(sourceValue) && typeof value === "string") {
110
+ // includes / notIncludes (where context value is an array)
111
+ const valueInContext = sourceValue as string[];
112
+
113
+ if (operator === "includes") {
114
+ return valueInContext.indexOf(value) > -1;
115
+ } else if (operator === "notIncludes") {
116
+ return valueInContext.indexOf(value) === -1;
117
+ }
118
+ }
119
+
120
+ return false;
121
+ }
122
+
123
+ private async _allAreMatched(
124
+ conditions: Condition[] | Condition,
125
+ inputs: Inputs,
126
+ ): Promise<boolean> {
127
+ if (typeof conditions === "string") {
128
+ if (conditions === "*") {
129
+ return true;
130
+ }
131
+
132
+ return false;
133
+ }
134
+
135
+ if ("operator" in conditions) {
136
+ try {
137
+ return this.isMatched(conditions, inputs);
138
+ } catch (e) {
139
+ this.logger.warn(e.message, {
140
+ error: e,
141
+ details: {
142
+ condition: conditions,
143
+ },
144
+ });
145
+
146
+ return false;
147
+ }
148
+ }
149
+
150
+ if ("and" in conditions && Array.isArray(conditions.and)) {
151
+ for (const c of conditions.and) {
152
+ if (!(await this._allAreMatched(c, inputs))) {
153
+ return false; // If any condition fails, return false
154
+ }
155
+ }
156
+ return true; // All conditions passed
157
+ }
158
+
159
+ if ("or" in conditions && Array.isArray(conditions.or)) {
160
+ for (const c of conditions.or) {
161
+ if (await this._allAreMatched(c, inputs)) {
162
+ return true; // If any condition passes, return true
163
+ }
164
+ }
165
+ return false; // No conditions passed
166
+ }
167
+
168
+ if ("not" in conditions && Array.isArray(conditions.not)) {
169
+ for (const c of conditions.not) {
170
+ if (await this._allAreMatched(c, inputs)) {
171
+ return false; // If any condition passes, return false (since this is NOT)
172
+ }
173
+ }
174
+ return true; // No conditions passed, which is what we want for NOT
175
+ }
176
+
177
+ if (Array.isArray(conditions)) {
178
+ let result = true;
179
+ for (const c of conditions) {
180
+ if (!(await this._allAreMatched(c, inputs))) {
181
+ result = false;
182
+ break;
183
+ }
184
+ }
185
+
186
+ return result;
187
+ }
188
+
189
+ return false;
190
+ }
191
+
192
+ async allAreMatched(conditions: Condition[] | Condition, inputs: Inputs): Promise<boolean> {
193
+ const parsedConditions = this.parseIfStringified(conditions);
194
+
195
+ const result = this._allAreMatched(parsedConditions, inputs);
196
+
197
+ return result;
198
+ }
199
+
200
+ parseIfStringified(conditions: Condition | Condition[]): Condition | Condition[] {
201
+ if (typeof conditions !== "string") {
202
+ // already parsed
203
+ return conditions;
204
+ }
205
+
206
+ if (conditions === "*") {
207
+ // everyone
208
+ return conditions;
209
+ }
210
+
211
+ try {
212
+ return JSON.parse(conditions);
213
+ } catch (e) {
214
+ this.logger.error("Error parsing conditions", {
215
+ error: e,
216
+ details: {
217
+ conditions,
218
+ },
219
+ });
220
+
221
+ return conditions;
222
+ }
223
+ }
224
+ }
@@ -0,0 +1,133 @@
1
+ import {
2
+ AttributeName,
3
+ DatafileContent,
4
+ DestinationName,
5
+ EffectName,
6
+ EventName,
7
+ Persist,
8
+ ComplexPersist,
9
+ Attribute,
10
+ Effect,
11
+ Event,
12
+ Destination,
13
+ } from "@eventvisor/types";
14
+
15
+ import { Logger } from "./logger";
16
+
17
+ export interface DatafileReaderOptions {
18
+ datafile: DatafileContent;
19
+ logger: Logger;
20
+ }
21
+
22
+ export type GetDatafileReader = () => DatafileReader;
23
+
24
+ export type GetRegex = (regexString: string, regexFlags?: string) => RegExp;
25
+
26
+ export const emptyDatafile: DatafileContent = {
27
+ schemaVersion: "1",
28
+ revision: "0",
29
+
30
+ attributes: {},
31
+ events: {},
32
+ destinations: {},
33
+ effects: {},
34
+ };
35
+
36
+ export function getComplexPersists(persist: Persist): ComplexPersist[] {
37
+ let result: ComplexPersist[] = [];
38
+
39
+ if (typeof persist === "string") {
40
+ result.push({ storage: persist });
41
+ } else if (Array.isArray(persist)) {
42
+ for (const p of persist) {
43
+ const r = getComplexPersists(p);
44
+
45
+ if (r) {
46
+ result = result.concat(r);
47
+ }
48
+ }
49
+ } else if (typeof persist === "object") {
50
+ result.push(persist);
51
+ }
52
+
53
+ return result;
54
+ }
55
+
56
+ export class DatafileReader {
57
+ private schemaVersion: string;
58
+ private revision: string;
59
+
60
+ private datafile: DatafileContent;
61
+ private logger: Logger;
62
+ private regexCache: Record<string, RegExp>;
63
+
64
+ constructor(options: DatafileReaderOptions) {
65
+ const { datafile, logger } = options;
66
+
67
+ this.datafile = datafile;
68
+ this.schemaVersion = datafile.schemaVersion;
69
+ this.revision = datafile.revision;
70
+
71
+ this.regexCache = {};
72
+
73
+ this.logger = logger;
74
+ }
75
+
76
+ getSchemaVersion(): string {
77
+ return this.schemaVersion;
78
+ }
79
+
80
+ getRevision(): string {
81
+ return this.revision;
82
+ }
83
+
84
+ getAttribute(attributeName: AttributeName): Attribute | undefined {
85
+ return this.datafile.attributes[attributeName];
86
+ }
87
+
88
+ getAttributeNames(): AttributeName[] {
89
+ return Object.keys(this.datafile.attributes);
90
+ }
91
+
92
+ getEvent(eventName: EventName): Event | undefined {
93
+ return this.datafile.events[eventName];
94
+ }
95
+
96
+ getDestination(destinationName: DestinationName): Destination | undefined {
97
+ return this.datafile.destinations[destinationName];
98
+ }
99
+
100
+ getDestinationNames(): DestinationName[] {
101
+ return Object.keys(this.datafile.destinations);
102
+ }
103
+
104
+ getEffect(effectName: EffectName): Effect | undefined {
105
+ return this.datafile.effects[effectName];
106
+ }
107
+
108
+ getEffectNames(): EffectName[] {
109
+ return Object.keys(this.datafile.effects);
110
+ }
111
+
112
+ getRegex(regexString: string, regexFlags?: string): RegExp {
113
+ const flags = regexFlags || "";
114
+ const cacheKey = `${regexString}-${flags}`;
115
+
116
+ if (this.regexCache[cacheKey]) {
117
+ return this.regexCache[cacheKey];
118
+ }
119
+
120
+ const regex = new RegExp(regexString, flags);
121
+ this.regexCache[cacheKey] = regex;
122
+
123
+ return regex;
124
+ }
125
+
126
+ getPersists(schema: Attribute | Effect): ComplexPersist[] | null {
127
+ if (!schema || !schema.persist) {
128
+ return null;
129
+ }
130
+
131
+ return getComplexPersists(schema.persist);
132
+ }
133
+ }
@@ -0,0 +1,214 @@
1
+ import type { EventName, AttributeName, EffectName, Value, EffectOnType } from "@eventvisor/types";
2
+
3
+ import type { GetDatafileReader } from "./datafileReader";
4
+ import type { Logger } from "./logger";
5
+ import type { GetTransformer } from "./transformer";
6
+ import type { GetConditionsChecker } from "./conditions";
7
+ import type { ModulesManager } from "./modulesManager";
8
+ import { initializeFromStorage, persistEntity } from "./persister";
9
+
10
+ export type StatesByEffect = Record<EffectName, Value>;
11
+
12
+ export interface DispatchOptions {
13
+ eventType: EffectOnType;
14
+ name: EventName | AttributeName;
15
+ value: Value;
16
+ }
17
+
18
+ export interface EffectsManagerOptions {
19
+ logger: Logger;
20
+ getDatafileReader: GetDatafileReader;
21
+ getTransformer: GetTransformer;
22
+ getConditionsChecker: GetConditionsChecker;
23
+ modulesManager: ModulesManager;
24
+ }
25
+
26
+ export class EffectsManager {
27
+ private logger: Logger;
28
+ private getDatafileReader: GetDatafileReader;
29
+ private getTransformer: GetTransformer;
30
+ private getConditionsChecker: GetConditionsChecker;
31
+ private modulesManager: ModulesManager;
32
+
33
+ private statesByEffect: StatesByEffect = {};
34
+
35
+ constructor(options: EffectsManagerOptions) {
36
+ this.logger = options.logger;
37
+ this.getDatafileReader = options.getDatafileReader;
38
+ this.getTransformer = options.getTransformer;
39
+ this.getConditionsChecker = options.getConditionsChecker;
40
+ this.modulesManager = options.modulesManager;
41
+ }
42
+
43
+ async initialize(): Promise<void> {
44
+ const datafileReader = this.getDatafileReader();
45
+ const effects = datafileReader.getEffectNames();
46
+
47
+ const persistedResult = await initializeFromStorage({
48
+ datafileReader,
49
+ conditionsChecker: this.getConditionsChecker(),
50
+ modulesManager: this.modulesManager,
51
+ storageKeyPrefix: "effects_",
52
+ getEntityNames: () => datafileReader.getEffectNames(),
53
+ getEntity: (entityName: string) => datafileReader.getEffect(entityName),
54
+ });
55
+
56
+ for (const effectName of effects) {
57
+ const effect = datafileReader.getEffect(effectName);
58
+
59
+ if (!effect) {
60
+ continue;
61
+ }
62
+
63
+ if (typeof this.statesByEffect[effectName] !== "undefined") {
64
+ // possibly called via refresh() method after initialization
65
+ continue;
66
+ }
67
+
68
+ if (typeof persistedResult[effectName] !== "undefined") {
69
+ // from storage
70
+ this.statesByEffect[effectName] = persistedResult[effectName];
71
+ } else {
72
+ // from initial state of effect
73
+ if (typeof effect.state !== "undefined") {
74
+ if (typeof this.statesByEffect[effectName] === "undefined") {
75
+ this.statesByEffect[effectName] = effect.state;
76
+ }
77
+ }
78
+ }
79
+ }
80
+ }
81
+
82
+ async dispatch(dispatchOptions: DispatchOptions) {
83
+ // @TODO: rename to actionType
84
+ const { eventType, name, value } = dispatchOptions;
85
+
86
+ const datafileReader = this.getDatafileReader();
87
+ const conditionsChecker = this.getConditionsChecker();
88
+ const transformer = this.getTransformer();
89
+
90
+ const allEffects = datafileReader.getEffectNames();
91
+
92
+ for (const effectName of allEffects) {
93
+ const effect = datafileReader.getEffect(effectName);
94
+
95
+ if (!effect) {
96
+ continue;
97
+ }
98
+
99
+ if (eventType === "event_tracked") {
100
+ if (Array.isArray(effect.on) && !effect.on.includes("event_tracked")) {
101
+ continue;
102
+ }
103
+
104
+ if (typeof effect.on === "object" && !effect.on["event_tracked"]?.includes(name)) {
105
+ continue;
106
+ }
107
+ }
108
+
109
+ if (eventType === "attribute_set") {
110
+ if (Array.isArray(effect.on) && !effect.on.includes("attribute_set")) {
111
+ continue;
112
+ }
113
+
114
+ if (typeof effect.on === "object" && !effect.on["attribute_set"]?.includes(name)) {
115
+ continue;
116
+ }
117
+ }
118
+
119
+ // conditions
120
+ if (effect.conditions) {
121
+ const isMatched = await conditionsChecker.allAreMatched(effect.conditions, {
122
+ payload: value,
123
+ eventName: eventType === "event_tracked" ? name : undefined,
124
+ attributeName: eventType === "attribute_set" ? name : undefined,
125
+ state: this.statesByEffect[effectName],
126
+ });
127
+
128
+ if (!isMatched) {
129
+ continue;
130
+ }
131
+ }
132
+
133
+ // steps
134
+ if (effect.steps) {
135
+ for (const step of effect.steps) {
136
+ let stepPassed = true;
137
+
138
+ // conditions
139
+ if (step.conditions) {
140
+ const conditionsChecker = this.getConditionsChecker();
141
+ const isMatched = await conditionsChecker.allAreMatched(step.conditions, {
142
+ payload: value,
143
+ eventName: eventType === "event_tracked" ? name : undefined,
144
+ attributeName: eventType === "attribute_set" ? name : undefined,
145
+ state: this.statesByEffect[effectName],
146
+ });
147
+
148
+ if (!isMatched) {
149
+ continue;
150
+ }
151
+ }
152
+
153
+ // handler
154
+ if (step.handler) {
155
+ try {
156
+ await this.modulesManager.handle(step.handler, effectName, effect, step, value);
157
+ } catch (handlerError) {
158
+ this.logger.error(`Effect handler error`, {
159
+ effectName,
160
+ step,
161
+ error: handlerError,
162
+ });
163
+
164
+ stepPassed = false;
165
+ }
166
+ }
167
+
168
+ // continueOnError
169
+ if (!stepPassed && typeof step.continueOnError === "boolean" && !step.continueOnError) {
170
+ break;
171
+ }
172
+
173
+ // transforms
174
+ if (step.transforms) {
175
+ this.statesByEffect[effectName] = await transformer.applyAll(
176
+ this.statesByEffect[effectName],
177
+ step.transforms,
178
+ {
179
+ eventName: eventType === "event_tracked" ? name : undefined,
180
+ attributeName: eventType === "attribute_set" ? name : undefined,
181
+ state: this.statesByEffect[effectName],
182
+ },
183
+ );
184
+ }
185
+ }
186
+ }
187
+
188
+ // persist
189
+ await persistEntity({
190
+ datafileReader,
191
+ conditionsChecker,
192
+ modulesManager: this.modulesManager,
193
+ storageKeyPrefix: "effects_",
194
+ entityName: effectName,
195
+ entity: effect,
196
+ value: this.statesByEffect[effectName],
197
+ });
198
+ }
199
+ }
200
+
201
+ // called after datafile refresh
202
+ refresh() {
203
+ // @TODO: think
204
+ this.initialize();
205
+ }
206
+
207
+ getAllStates() {
208
+ return this.statesByEffect;
209
+ }
210
+
211
+ getStateValue(name: EffectName) {
212
+ return this.statesByEffect[name];
213
+ }
214
+ }
package/src/emitter.ts ADDED
@@ -0,0 +1,64 @@
1
+ export type EmitType =
2
+ | "ready"
3
+ | "datafile_set"
4
+ | "attribute_set"
5
+ | "attribute_removed"
6
+ | "event_tracked";
7
+
8
+ export type EventDetails = Record<string, unknown>;
9
+
10
+ export type EventCallback = (details: EventDetails) => void;
11
+
12
+ export type Listeners = Record<EmitType, EventCallback[]> | {}; // eslint-disable-line
13
+
14
+ export class Emitter {
15
+ listeners: Listeners;
16
+
17
+ constructor() {
18
+ this.listeners = {};
19
+ }
20
+
21
+ on(emitType: EmitType, callback: EventCallback) {
22
+ if (!this.listeners[emitType]) {
23
+ this.listeners[emitType] = [];
24
+ }
25
+
26
+ const listeners = this.listeners[emitType];
27
+ listeners.push(callback);
28
+
29
+ let isActive = true;
30
+
31
+ return function unsubscribe() {
32
+ if (!isActive) {
33
+ return;
34
+ }
35
+
36
+ isActive = false;
37
+
38
+ const index = listeners.indexOf(callback);
39
+ if (index !== -1) {
40
+ listeners.splice(index, 1);
41
+ }
42
+ };
43
+ }
44
+
45
+ trigger(emitType: EmitType, details: EventDetails = {}) {
46
+ const listeners = this.listeners[emitType];
47
+
48
+ if (!listeners) {
49
+ return;
50
+ }
51
+
52
+ listeners.forEach(function (listener) {
53
+ try {
54
+ listener(details);
55
+ } catch (err) {
56
+ console.error(err);
57
+ }
58
+ });
59
+ }
60
+
61
+ clearAll() {
62
+ this.listeners = {};
63
+ }
64
+ }
@@ -0,0 +1,5 @@
1
+ describe("sdk: Index", function () {
2
+ it("should be a function", function () {
3
+ expect(true).toBe(true);
4
+ });
5
+ });
package/src/index.ts ADDED
@@ -0,0 +1,14 @@
1
+ export * from "./instance";
2
+
3
+ export * from "./attributesManager";
4
+ export * from "./datafileReader";
5
+ export * from "./emitter";
6
+ export * from "./logger";
7
+ export * from "./transformer";
8
+ export * from "./bucketer";
9
+ export * from "./sourceResolver";
10
+ export * from "./modulesManager";
11
+ export * from "./effectsManager";
12
+
13
+ export * from "./murmurhash";
14
+ export * from "./compareVersions";