@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,253 @@
1
+ import { SourceResolver } from "./sourceResolver";
2
+ import { Emitter } from "./emitter";
3
+ import { createLogger } from "./logger";
4
+ import { ModulesManager } from "./modulesManager";
5
+ import { AttributesManager } from "./attributesManager";
6
+ import { EffectsManager } from "./effectsManager";
7
+ import { DatafileReader } from "./datafileReader";
8
+ import { Validator } from "./validator";
9
+ import { ConditionsChecker } from "./conditions";
10
+ import { Transformer } from "./transformer";
11
+
12
+ describe("SourceResolver", () => {
13
+ // initialize the dependencies
14
+ const emitter = new Emitter();
15
+
16
+ const logger = createLogger({ level: "fatal" });
17
+
18
+ const datafileReader = new DatafileReader({
19
+ datafile: {
20
+ schemaVersion: "1",
21
+ revision: "0",
22
+ attributes: {
23
+ attribute1: {
24
+ type: "string",
25
+ },
26
+ attribute2: {
27
+ type: "number",
28
+ },
29
+ attribute3: {
30
+ type: "object",
31
+ properties: {
32
+ name: {
33
+ type: "string",
34
+ },
35
+ },
36
+ },
37
+ },
38
+ events: {},
39
+ destinations: {},
40
+ effects: {
41
+ effect1: {
42
+ state: {
43
+ nested: {
44
+ value: "effect1 value",
45
+ },
46
+ },
47
+ },
48
+ effect2: {
49
+ state: 123,
50
+ },
51
+ },
52
+ },
53
+ logger,
54
+ });
55
+
56
+ const modulesManager = new ModulesManager({
57
+ logger,
58
+ getDatafileReader: () => datafileReader,
59
+ getSourceResolver: () => sourceResolver,
60
+ });
61
+
62
+ modulesManager.registerModule({
63
+ name: "module1",
64
+ lookup: async ({ key }) => {
65
+ if (!key) {
66
+ return "module1 lookup value";
67
+ }
68
+
69
+ return `module1 lookup value: ${key}`;
70
+ },
71
+ });
72
+
73
+ const validator = new Validator({
74
+ logger,
75
+ getSourceResolver: () => sourceResolver,
76
+ });
77
+
78
+ const attributesManager = new AttributesManager({
79
+ logger,
80
+ emitter,
81
+ validator,
82
+ getDatafileReader: () => datafileReader,
83
+ getTransformer: () => transformer,
84
+ getConditionsChecker: () => conditionsChecker,
85
+ modulesManager,
86
+ });
87
+
88
+ attributesManager.setAttribute("attribute1", "attribute1 value");
89
+ attributesManager.setAttribute("attribute2", 200);
90
+ attributesManager.setAttribute("attribute3", { name: "attribute3 value" });
91
+
92
+ const effectsManager = new EffectsManager({
93
+ logger,
94
+ getDatafileReader: () => datafileReader,
95
+ getTransformer: () => transformer,
96
+ getConditionsChecker: () => conditionsChecker,
97
+ modulesManager: modulesManager,
98
+ });
99
+
100
+ const sourceResolver = new SourceResolver({
101
+ logger,
102
+ modulesManager,
103
+ attributesManager,
104
+ effectsManager,
105
+ });
106
+
107
+ const conditionsChecker = new ConditionsChecker({
108
+ logger,
109
+ getRegex: (regexString, regexFlags) => new RegExp(regexString, regexFlags),
110
+ sourceResolver,
111
+ });
112
+
113
+ const transformer = new Transformer({
114
+ logger,
115
+ conditionsChecker,
116
+ sourceResolver,
117
+ });
118
+
119
+ effectsManager.initialize();
120
+
121
+ it("should resolve plain source from inputs", async () => {
122
+ expect(
123
+ await sourceResolver.resolve("payload.age", {
124
+ payload: {
125
+ age: 25,
126
+ },
127
+ }),
128
+ ).toEqual(25);
129
+
130
+ expect(
131
+ await sourceResolver.resolve(
132
+ {
133
+ payload: "age",
134
+ },
135
+ {
136
+ payload: {
137
+ age: 25,
138
+ },
139
+ },
140
+ ),
141
+ ).toEqual(25);
142
+
143
+ expect(
144
+ await sourceResolver.resolve(
145
+ {
146
+ source: "payload.age",
147
+ },
148
+ {
149
+ payload: {
150
+ age: 25,
151
+ },
152
+ },
153
+ ),
154
+ ).toEqual(25);
155
+
156
+ expect(
157
+ await sourceResolver.resolve(
158
+ {
159
+ source: "payload.age",
160
+ },
161
+ {
162
+ payload: {
163
+ age: 25,
164
+ },
165
+ },
166
+ ),
167
+ ).toEqual(25);
168
+ });
169
+
170
+ it("should resolve attributes", async () => {
171
+ // all attributes
172
+ expect(await sourceResolver.resolve("attributes")).toEqual({
173
+ attribute1: "attribute1 value",
174
+ attribute2: 200,
175
+ attribute3: {
176
+ name: "attribute3 value",
177
+ },
178
+ });
179
+
180
+ // direct source
181
+ expect(await sourceResolver.resolve("attributes.attribute1")).toEqual("attribute1 value");
182
+ expect(await sourceResolver.resolve("attributes.attribute2")).toEqual(200);
183
+ expect(await sourceResolver.resolve("attributes.attribute3")).toEqual({
184
+ name: "attribute3 value",
185
+ });
186
+ expect(await sourceResolver.resolve("attributes.attribute3.name")).toEqual("attribute3 value");
187
+ expect(await sourceResolver.resolve("attributes.attribute4")).toEqual(null);
188
+
189
+ // attribute
190
+ expect(await sourceResolver.resolve({ attribute: "attribute1" })).toEqual("attribute1 value");
191
+ expect(await sourceResolver.resolve({ attribute: "attribute2" })).toEqual(200);
192
+ expect(await sourceResolver.resolve({ attribute: "attribute3" })).toEqual({
193
+ name: "attribute3 value",
194
+ });
195
+ expect(await sourceResolver.resolve({ attribute: "attribute3.name" })).toEqual(
196
+ "attribute3 value",
197
+ );
198
+ expect(await sourceResolver.resolve({ attribute: "attribute4" })).toEqual(null);
199
+ });
200
+
201
+ it("should resolve effect", async () => {
202
+ // all effects
203
+ expect(await sourceResolver.resolve("effects")).toEqual({
204
+ effect1: {
205
+ nested: {
206
+ value: "effect1 value",
207
+ },
208
+ },
209
+ effect2: 123,
210
+ });
211
+
212
+ // direct source
213
+ expect(await sourceResolver.resolve("effects.effect1")).toEqual({
214
+ nested: {
215
+ value: "effect1 value",
216
+ },
217
+ });
218
+ expect(await sourceResolver.resolve("effects.effect2")).toEqual(123);
219
+ // expect(await sourceResolver.resolve("effects.effect3")).toEqual(null);
220
+
221
+ // effect
222
+ expect(await sourceResolver.resolve({ effect: "effect1" })).toEqual({
223
+ nested: {
224
+ value: "effect1 value",
225
+ },
226
+ });
227
+ expect(await sourceResolver.resolve({ effect: "effect2" })).toEqual(123);
228
+ // expect(await sourceResolver.resolve({ effect: "effect3" })).toEqual(null);
229
+ });
230
+
231
+ it("should resolve state when inside an effect", async () => {
232
+ expect(
233
+ await sourceResolver.resolve(
234
+ { state: "counter" },
235
+ {
236
+ state: {
237
+ counter: 10,
238
+ },
239
+ },
240
+ ),
241
+ ).toEqual(10);
242
+ });
243
+
244
+ it("should resolve lookup", async () => {
245
+ expect(await sourceResolver.resolve({ lookup: "module1" })).toEqual("module1 lookup value");
246
+ expect(await sourceResolver.resolve({ lookup: "module1.key" })).toEqual(
247
+ "module1 lookup value: key",
248
+ );
249
+ expect(await sourceResolver.resolve({ lookup: "module1.key.subkey" })).toEqual(
250
+ "module1 lookup value: key.subkey",
251
+ );
252
+ });
253
+ });
@@ -0,0 +1,213 @@
1
+ import type { Source, SourceBase, Value, Inputs } from "@eventvisor/types";
2
+
3
+ import type { ModulesManager } from "./modulesManager";
4
+ import type { Logger } from "./logger";
5
+ import type { AttributesManager } from "./attributesManager";
6
+ import type { EffectsManager } from "./effectsManager";
7
+
8
+ export type GetSourceResolver = () => SourceResolver;
9
+
10
+ const SOURCE_PATH_SEPARATOR = ".";
11
+
12
+ export interface SourceResolverOptions {
13
+ logger: Logger;
14
+ modulesManager: ModulesManager;
15
+ attributesManager: AttributesManager;
16
+ effectsManager: EffectsManager;
17
+ }
18
+
19
+ export interface SourcePath {
20
+ name: string;
21
+ path: string[]; // dot-separated path after the name
22
+ fullKey: string;
23
+ }
24
+
25
+ export type SourceOrigin = SourcePath & {
26
+ originType: "attribute" | "attributes" | "effect" | "payload" | "lookup" | string;
27
+ };
28
+
29
+ function findValueAtPath(obj: any, path: string[]): any {
30
+ return path.reduce((acc, part) => acc[part], obj);
31
+ }
32
+
33
+ // @TODO: redo it with a better approach
34
+ export class SourceResolver {
35
+ private logger: Logger;
36
+
37
+ private modulesManager: ModulesManager;
38
+ private attributesManager: AttributesManager;
39
+ private effectsManager: EffectsManager;
40
+
41
+ constructor(options: SourceResolverOptions) {
42
+ const { logger, modulesManager, attributesManager, effectsManager } = options;
43
+
44
+ this.logger = logger;
45
+ this.modulesManager = modulesManager;
46
+ this.attributesManager = attributesManager;
47
+ this.effectsManager = effectsManager;
48
+ }
49
+
50
+ getPath(p: string): SourcePath {
51
+ const parts = p.split(SOURCE_PATH_SEPARATOR);
52
+
53
+ return {
54
+ name: parts[0],
55
+ path: parts.slice(1),
56
+ fullKey: p,
57
+ };
58
+ }
59
+
60
+ getOrigin(source: Source | Partial<SourceBase>): SourceOrigin | SourceOrigin[] | null {
61
+ if (typeof source === "string") {
62
+ const parts = source.split(SOURCE_PATH_SEPARATOR);
63
+ const originType = parts[0] as SourceOrigin["originType"];
64
+
65
+ // @TODO: validate type as one of known types
66
+
67
+ return {
68
+ originType,
69
+ name: parts[1],
70
+ path: parts.slice(2),
71
+ fullKey: source,
72
+ };
73
+ }
74
+
75
+ // @TODO: fix it better
76
+ if ("source" in source) {
77
+ return this.getOrigin(source.source as string);
78
+ }
79
+
80
+ if ("source" in source) {
81
+ return this.getOrigin(source.source as string);
82
+ }
83
+
84
+ if ("attribute" in source) {
85
+ return {
86
+ originType: "attribute",
87
+ ...this.getPath(source.attribute as string), // @TODO: consider array of strings here
88
+ };
89
+ }
90
+
91
+ if ("effect" in source) {
92
+ return {
93
+ originType: "effect",
94
+ ...this.getPath(source.effect as string),
95
+ };
96
+ }
97
+
98
+ if ("payload" in source) {
99
+ if (Array.isArray(source.payload)) {
100
+ return source.payload.map((p) => ({
101
+ originType: "payload",
102
+ ...this.getPath(p as string),
103
+ }));
104
+ } else {
105
+ return {
106
+ originType: "payload",
107
+ ...this.getPath(source.payload as string),
108
+ };
109
+ }
110
+ }
111
+
112
+ if ("lookup" in source) {
113
+ return {
114
+ originType: "lookup",
115
+ ...this.getPath(source.lookup as string),
116
+ };
117
+ }
118
+
119
+ if ("state" in source) {
120
+ return {
121
+ originType: "state",
122
+ ...this.getPath(source.state as string),
123
+ };
124
+ }
125
+
126
+ return null;
127
+ }
128
+
129
+ async resolveByOrigin(origin: SourceOrigin | null, inputs: Inputs = {}): Promise<Value> {
130
+ if (origin === null) {
131
+ return null;
132
+ }
133
+
134
+ if (origin.originType === "attributes") {
135
+ const result = origin.name
136
+ ? this.attributesManager.getAttributeValue(origin.name)
137
+ : this.attributesManager.getAttributesMap();
138
+
139
+ if (origin.path.length > 0) {
140
+ return findValueAtPath(result, origin.path);
141
+ }
142
+
143
+ return result;
144
+ }
145
+
146
+ if (origin.originType === "attribute") {
147
+ const result = this.attributesManager.getAttributeValue(origin.name);
148
+
149
+ if (origin.path.length > 0) {
150
+ return findValueAtPath(result, origin.path);
151
+ }
152
+
153
+ return result;
154
+ }
155
+
156
+ if (origin.originType === "effects") {
157
+ const result = origin.name
158
+ ? this.effectsManager.getStateValue(origin.name)
159
+ : this.effectsManager.getAllStates();
160
+
161
+ if (origin.path.length > 0) {
162
+ return findValueAtPath(result, origin.path);
163
+ }
164
+
165
+ return result;
166
+ }
167
+
168
+ if (origin.originType === "effect") {
169
+ const result = this.effectsManager.getStateValue(origin.name);
170
+
171
+ if (origin.path.length > 0) {
172
+ return findValueAtPath(result, origin.path);
173
+ }
174
+
175
+ return result;
176
+ }
177
+
178
+ if (origin.originType === "lookup") {
179
+ return this.modulesManager.lookup(origin.fullKey);
180
+ }
181
+
182
+ // if (origin.originType === "payload" && inputs.payload) {
183
+ // return findValueAtPath(inputs.payload, [...origin.path, ...[origin.name]]); // @TODO: make it better
184
+ // }
185
+
186
+ // if (origin.originType === "attributes" && inputs.attributes) {
187
+ // const p = [...origin.path, ...[origin.name]].filter(Boolean);
188
+ // return findValueAtPath(inputs["attributes"], p); // @TODO: make it better
189
+ // }
190
+
191
+ // handle any other input that is not known early
192
+ if (typeof inputs[origin.originType] !== "undefined") {
193
+ return findValueAtPath(
194
+ inputs[origin.originType],
195
+ [...[origin.name, ...origin.path]].filter(Boolean),
196
+ ); // @TODO: make it better
197
+ }
198
+
199
+ return null;
200
+ }
201
+
202
+ async resolve(source: Source | Partial<SourceBase>, inputs: Inputs = {}): Promise<Value> {
203
+ const origin = this.getOrigin(source);
204
+
205
+ if (Array.isArray(origin)) {
206
+ return Promise.all(origin.map((o) => this.resolveByOrigin(o, inputs)));
207
+ }
208
+
209
+ const result = await this.resolveByOrigin(origin, inputs);
210
+
211
+ return result;
212
+ }
213
+ }