@compilacion/colleciones-clientos 1.0.11 → 2.0.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.
- package/README.md +45 -83
- package/dist/collecionesClientos.iife.js +523 -241
- package/dist/collecionesClientos.iife.js.map +1 -1
- package/dist/index.cjs +523 -241
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +523 -240
- package/dist/index.mjs.map +1 -1
- package/docs/architecture.md +50 -0
- package/docs/concept.md +65 -0
- package/docs/dsl.md +92 -0
- package/docs/emitterAndTracker.md +63 -0
- package/docs/events.md +68 -0
- package/package.json +4 -3
package/dist/index.cjs
CHANGED
|
@@ -1,56 +1,27 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
// helper 'private' functions
|
|
4
|
-
const underscoreToCamelCase = function (str) {
|
|
5
|
-
str = str.replace(/[^\x00-\x7F_]/g, '');
|
|
6
|
-
return str
|
|
7
|
-
.split('_')
|
|
8
|
-
.map((word, index) => {
|
|
9
|
-
if (index === 0) {
|
|
10
|
-
return word;
|
|
11
|
-
}
|
|
12
|
-
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
|
|
13
|
-
})
|
|
14
|
-
.join('');
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
const formatAdjectives = function (adjectiveParameter) {
|
|
18
|
-
let errorMsg = new Error('Adjective must be a string, an array of strings, or undefined');
|
|
19
|
-
if (typeof adjectiveParameter === 'undefined') {
|
|
20
|
-
return undefined;
|
|
21
|
-
}
|
|
22
|
-
if (typeof adjectiveParameter === 'string') {
|
|
23
|
-
return [underscoreToCamelCase(adjectiveParameter)];
|
|
24
|
-
}
|
|
25
|
-
if (Array.isArray(adjectiveParameter) && adjectiveParameter.every(item => typeof item === 'string')) {
|
|
26
|
-
let returnValue = [];
|
|
27
|
-
adjectiveParameter.forEach((adjectiveParameter) => {
|
|
28
|
-
if (!typeof item === 'string') {
|
|
29
|
-
throw errorMsg;
|
|
30
|
-
}
|
|
31
|
-
returnValue.push(underscoreToCamelCase(adjectiveParameter));
|
|
32
|
-
});
|
|
33
|
-
return returnValue;
|
|
34
|
-
}
|
|
35
|
-
throw errorMsg;
|
|
36
|
-
};
|
|
37
4
|
|
|
38
|
-
const stringifyAdjectives = function (adjective) {
|
|
39
|
-
if (!Array.isArray(adjective) || adjective.length === 0) {
|
|
40
|
-
return undefined;
|
|
41
|
-
}
|
|
42
|
-
const uniqueSorted = [...new Set(adjective)].sort();
|
|
43
|
-
return '.' + uniqueSorted.join('.');
|
|
44
|
-
};
|
|
45
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Encodes a string to Base64.
|
|
8
|
+
* Uses browser or Node.js method depending on environment.
|
|
9
|
+
* @param {string} str
|
|
10
|
+
* @returns {string}
|
|
11
|
+
*/
|
|
46
12
|
const toBase64 = function (str) {
|
|
47
|
-
if (typeof window !== 'undefined' && window.btoa) {
|
|
13
|
+
if (typeof window !== 'undefined' && typeof window.btoa === 'function') {
|
|
48
14
|
return window.btoa(unescape(encodeURIComponent(str)));
|
|
49
15
|
} else {
|
|
50
16
|
return Buffer.from(str, 'utf-8').toString('base64');
|
|
51
17
|
}
|
|
52
18
|
};
|
|
53
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Collects browser-related context values like screen size, language, etc.
|
|
22
|
+
* Returns an empty object if not in browser environment.
|
|
23
|
+
* @returns {Object}
|
|
24
|
+
*/
|
|
54
25
|
const getBrowserContext = function() {
|
|
55
26
|
if (typeof window === 'undefined' || typeof navigator === 'undefined') {
|
|
56
27
|
return {};
|
|
@@ -70,108 +41,275 @@ const getBrowserContext = function() {
|
|
|
70
41
|
};
|
|
71
42
|
};
|
|
72
43
|
|
|
73
|
-
|
|
44
|
+
/**
|
|
45
|
+
* Base class representing a structured event with timestamp, identifiers, and data fields.
|
|
46
|
+
* This class is used to model events in a semantic and structured format for tracking purposes.
|
|
47
|
+
*/
|
|
48
|
+
class CollecionesEvent {
|
|
74
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Constructs a new event with default metadata and timestamps.
|
|
52
|
+
*/
|
|
75
53
|
constructor() {
|
|
76
|
-
|
|
77
|
-
this.
|
|
78
|
-
this.
|
|
79
|
-
|
|
54
|
+
// initialize event properties
|
|
55
|
+
this.entity = '';
|
|
56
|
+
this.adjevtives = [];
|
|
57
|
+
this.identifiers = {};
|
|
58
|
+
this.action = '';
|
|
59
|
+
this.actor = {
|
|
60
|
+
name:'',
|
|
61
|
+
identifiers: []
|
|
62
|
+
};
|
|
63
|
+
this.context = {};
|
|
64
|
+
this.references = {};
|
|
65
|
+
// set default values
|
|
66
|
+
this.meta = {
|
|
67
|
+
eventFormat: 'CollecionesEvent',
|
|
80
68
|
eventFormatVersion: '1'
|
|
81
69
|
};
|
|
82
|
-
this.
|
|
83
|
-
this.
|
|
70
|
+
this.meta.tracker = '';
|
|
71
|
+
this.meta.app = '';
|
|
72
|
+
this.meta.timestamps = {};
|
|
73
|
+
this.meta.timestamps.clientDatetimeUtc = new Date().toISOString();
|
|
84
74
|
if (typeof window !== 'undefined' && typeof navigator !== 'undefined' && navigator.language) {
|
|
85
|
-
this.
|
|
86
|
-
this.
|
|
87
|
-
this.
|
|
75
|
+
this.meta.timestamps.clientDatetimeLocal = new Date(Date.now() - new Date().getTimezoneOffset() * 60000).toISOString();
|
|
76
|
+
this.meta.timestamps.timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
77
|
+
this.meta.timestamps.timeZoneOffset = new Date().getTimezoneOffset();
|
|
88
78
|
} else {
|
|
89
|
-
this.
|
|
90
|
-
this.
|
|
79
|
+
this.meta.timestamps.clientDatetimeLocal = new Date().toISOString();
|
|
80
|
+
this.meta.timestamps.timeZone = 'UTC';
|
|
91
81
|
}
|
|
92
82
|
}
|
|
93
83
|
|
|
94
|
-
|
|
95
|
-
|
|
84
|
+
/**
|
|
85
|
+
* Returns the declared event format.
|
|
86
|
+
* @returns {string} The format name (e.g. 'CollecionesEvent').
|
|
87
|
+
*/
|
|
88
|
+
getEventFormat() {
|
|
89
|
+
let v = this.meta?.eventFormat;
|
|
90
|
+
return (typeof v !== 'undefined') ? v : '1';
|
|
96
91
|
}
|
|
97
92
|
|
|
98
|
-
|
|
99
|
-
|
|
93
|
+
/**
|
|
94
|
+
* Returns the version of the event format.
|
|
95
|
+
* @returns {string} The format version string.
|
|
96
|
+
*/
|
|
97
|
+
getEventFormatVersion() {
|
|
98
|
+
let v = this?.meta?.eventFormatVersion;
|
|
99
|
+
return (typeof v !== 'undefined') ? v : 'CollecionesEvent';
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
102
|
+
/**
|
|
103
|
+
* Overrides or supplements the event's timestamp fields with custom values.
|
|
104
|
+
* @param {object} dateTimeObject - Key-value pairs to merge into the existing timestamp object.
|
|
105
|
+
*/
|
|
106
|
+
overrideDatetime(dateTimeObject = {}) {
|
|
107
|
+
for (const [key, value] of Object.entries(dateTimeObject)) {
|
|
108
|
+
this.meta.timestamps[key] = value;
|
|
109
|
+
}
|
|
105
110
|
}
|
|
106
111
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
112
|
+
/**
|
|
113
|
+
* Sets the name of the tracker responsible for generating this event.
|
|
114
|
+
* @param {string} name - Tracker name.
|
|
115
|
+
*/
|
|
116
|
+
setTracker(name) {
|
|
117
|
+
this.meta.tracker = name;
|
|
110
118
|
}
|
|
111
119
|
|
|
112
|
-
|
|
113
|
-
|
|
120
|
+
/**
|
|
121
|
+
* Sets the name of the application that generated the event.
|
|
122
|
+
* @param {string} name - Application name.
|
|
123
|
+
*/
|
|
124
|
+
setAppName(name) {
|
|
125
|
+
this.meta.appName = name;
|
|
114
126
|
}
|
|
115
127
|
|
|
116
|
-
|
|
117
|
-
|
|
128
|
+
/**
|
|
129
|
+
* Sets the expected schema name for this event.
|
|
130
|
+
* @param {string} schema - The name of the schema.
|
|
131
|
+
*/
|
|
132
|
+
setSchema(schema) {
|
|
133
|
+
if (typeof schema !== 'string') {
|
|
134
|
+
throw new Error('Schema must be a string');
|
|
135
|
+
}
|
|
136
|
+
this.meta.schema = schema;
|
|
118
137
|
}
|
|
119
138
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
139
|
+
/**
|
|
140
|
+
* Sets the entity (subject) of the event.
|
|
141
|
+
* @param {string} entity - The entity name.
|
|
142
|
+
*/
|
|
143
|
+
setEntity = function(entity) {
|
|
144
|
+
this.entity = entity;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Defines the main action of the event (e.g., 'clicked').
|
|
149
|
+
* @param {string} action - The action string.
|
|
150
|
+
*/
|
|
151
|
+
setAction = function(action) {
|
|
152
|
+
this.action = action;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Adds an adjective that describes the entity in more detail.
|
|
157
|
+
* @param {string} adjective - An adjective string.
|
|
158
|
+
*/
|
|
159
|
+
addAdjective = function(adjective) {
|
|
160
|
+
if (typeof adjective !== 'string') {
|
|
161
|
+
throw new Error('Adjective must be a string');
|
|
123
162
|
}
|
|
124
|
-
this.
|
|
163
|
+
this.adjevtives.push(adjective);
|
|
125
164
|
}
|
|
126
165
|
|
|
127
|
-
|
|
128
|
-
|
|
166
|
+
/**
|
|
167
|
+
* Adds or updates an identifier for the primary entity.
|
|
168
|
+
* @param {string} name - The identifier key.
|
|
169
|
+
* @param {*} identifier - The identifier value.
|
|
170
|
+
*/
|
|
171
|
+
setIdentifier = function(name, identifier) {
|
|
172
|
+
if (typeof name !== 'string') {
|
|
173
|
+
throw new Error('Identifier name must be a string');
|
|
174
|
+
}
|
|
175
|
+
this.identifiers[name] = identifier;
|
|
129
176
|
}
|
|
130
177
|
|
|
131
|
-
|
|
132
|
-
|
|
178
|
+
/**
|
|
179
|
+
* Defines the name of the actor (who or what performed the action).
|
|
180
|
+
* @param {string} name - Actor name (e.g., 'user').
|
|
181
|
+
*/
|
|
182
|
+
setActor = function(name) {
|
|
183
|
+
if (typeof name !== 'string') {
|
|
184
|
+
throw new Error('Actor name must be a string');
|
|
185
|
+
}
|
|
186
|
+
this.actor.name = name;
|
|
133
187
|
}
|
|
134
188
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
189
|
+
/**
|
|
190
|
+
* Adds or updates an identifier for the actor.
|
|
191
|
+
* @param {string} name - Identifier name.
|
|
192
|
+
* @param {*} identifier - Identifier value.
|
|
193
|
+
*/
|
|
194
|
+
setActorIdentifier = function(name, identifier) {
|
|
195
|
+
if (typeof name !== 'string') {
|
|
196
|
+
throw new Error('Actor Identifier name must be a string');
|
|
140
197
|
}
|
|
198
|
+
this.actor.identifiers[name] = identifier;
|
|
141
199
|
}
|
|
142
200
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
201
|
+
/**
|
|
202
|
+
* Adds contextual information to the event.
|
|
203
|
+
* @param {string} context - The key of the context field.
|
|
204
|
+
* @param {*} value - The value of the context field.
|
|
205
|
+
*/
|
|
206
|
+
setContext = function(context, value) {
|
|
207
|
+
if (typeof context !== 'string') {
|
|
208
|
+
throw new Error('Context must be a string');
|
|
146
209
|
}
|
|
147
|
-
this.
|
|
210
|
+
this.context[context] = value;
|
|
148
211
|
}
|
|
149
212
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
213
|
+
/**
|
|
214
|
+
* Declares an entity referenced by this event.
|
|
215
|
+
* @param {string} entity - The referenced entity name.
|
|
216
|
+
*/
|
|
217
|
+
setRefence = function(entity) {
|
|
218
|
+
if (typeof entity !== 'string') {
|
|
219
|
+
throw new Error('Referenced entity must be a string');
|
|
220
|
+
}
|
|
221
|
+
if(this.references[entity] === undefined) {
|
|
222
|
+
this.references[entity] = {identifiers: {}};
|
|
223
|
+
}
|
|
159
224
|
}
|
|
160
225
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
226
|
+
/**
|
|
227
|
+
* Adds or updates an identifier for a referenced entity.
|
|
228
|
+
* @param {string} entity - The referenced entity name.
|
|
229
|
+
* @param {string} name - The identifier key.
|
|
230
|
+
* @param {*} identifier - The identifier value.
|
|
231
|
+
*/
|
|
232
|
+
setRefenceIdentifier = function(entity, name, identifier) {
|
|
233
|
+
if (typeof entity !== 'string') {
|
|
234
|
+
throw new Error('Referenced entity name must be a string');
|
|
164
235
|
}
|
|
236
|
+
if (typeof name !== 'string') {
|
|
237
|
+
throw new Error('Actor Identifier name must be a string');
|
|
238
|
+
}
|
|
239
|
+
this.setRefence(entity);
|
|
240
|
+
this.references[entity].identifiers[name] = identifier;
|
|
165
241
|
}
|
|
166
242
|
|
|
167
|
-
|
|
168
|
-
|
|
243
|
+
/**
|
|
244
|
+
* Serializes the event to a plain object suitable for JSON.stringify().
|
|
245
|
+
* @returns {object}
|
|
246
|
+
*/
|
|
247
|
+
toJSON() {
|
|
248
|
+
return {
|
|
249
|
+
class: 'CollecionesEvent',
|
|
250
|
+
entity: this.entity,
|
|
251
|
+
adjectives: this.adjevtives,
|
|
252
|
+
identifiers: this.identifiers,
|
|
253
|
+
action: this.action,
|
|
254
|
+
actor: this.actor,
|
|
255
|
+
context: this.context,
|
|
256
|
+
references: this.references,
|
|
257
|
+
meta: this.meta
|
|
258
|
+
};
|
|
169
259
|
}
|
|
170
260
|
|
|
261
|
+
/**
|
|
262
|
+
* Recreates an instance of CollecionesEvent from a plain object.
|
|
263
|
+
* @param {object} obj
|
|
264
|
+
* @returns {CollecionesEvent}
|
|
265
|
+
*/
|
|
266
|
+
static fromJSON(obj) {
|
|
267
|
+
if (!obj || obj.class !== 'CollecionesEvent') {
|
|
268
|
+
throw new Error('Invalid or missing event class type');
|
|
269
|
+
}
|
|
270
|
+
const instance = new CollecionesEvent();
|
|
271
|
+
instance.entity = obj.entity;
|
|
272
|
+
instance.adjevtives = obj.adjectives || [];
|
|
273
|
+
instance.identifiers = obj.identifiers || {};
|
|
274
|
+
instance.action = obj.action;
|
|
275
|
+
instance.actor = obj.actor || { name: '', identifiers: {} };
|
|
276
|
+
instance.context = obj.context || {};
|
|
277
|
+
instance.references = obj.references || {};
|
|
278
|
+
instance.meta = obj.meta || {};
|
|
279
|
+
return instance;
|
|
280
|
+
}
|
|
281
|
+
|
|
171
282
|
}
|
|
172
283
|
|
|
284
|
+
/**
|
|
285
|
+
* CollecionesEmitter
|
|
286
|
+
* -------------------
|
|
287
|
+
* This class collects and sends event objects to a remote endpoint (e.g., an event collector API).
|
|
288
|
+
* It is used in both client- and server-side tracking scenarios, typically in combination with
|
|
289
|
+
* CollecionesEvent or subclasses like CollecionesBaseEvent.
|
|
290
|
+
*
|
|
291
|
+
* Behavior:
|
|
292
|
+
* - Events are buffered via `.track()` or `.trackAsync()`.
|
|
293
|
+
* - If the number of buffered events reaches `flushSize`, the buffer is automatically flushed.
|
|
294
|
+
* - If `flushInterval` is provided, the buffer is flushed at a fixed interval.
|
|
295
|
+
* - Flushing serializes each event using `.toJSON()`, encodes it with base64, and posts
|
|
296
|
+
* the resulting array to the configured endpoint.
|
|
297
|
+
*
|
|
298
|
+
* Example usage:
|
|
299
|
+
* const emitter = new CollecionesEmitter('/track', 5, 10000);
|
|
300
|
+
* emitter.track(new CollecionesEvent());
|
|
301
|
+
*
|
|
302
|
+
* Note:
|
|
303
|
+
* This class is stateful. To stop periodic flushing, call `.stopTimer()` when the emitter is no longer in use.
|
|
304
|
+
*/
|
|
173
305
|
class CollecionesEmitter {
|
|
174
306
|
|
|
307
|
+
/**
|
|
308
|
+
* Initializes the emitter with buffering settings.
|
|
309
|
+
* @param {string} [endpoint='/collect'] - The URL to send events to.
|
|
310
|
+
* @param {number} [flushSize=10] - Number of events to buffer before flushing.
|
|
311
|
+
* @param {number|boolean} [flushInterval=false] - Time in ms to flush events periodically.
|
|
312
|
+
*/
|
|
175
313
|
constructor(endpoint = '/collect', flushSize = 10, flushInterval = false) {
|
|
176
314
|
this.endpoint = endpoint;
|
|
177
315
|
this.flushInterval = flushInterval;
|
|
@@ -180,6 +318,10 @@ class CollecionesEmitter {
|
|
|
180
318
|
this.timer = null;
|
|
181
319
|
}
|
|
182
320
|
|
|
321
|
+
/**
|
|
322
|
+
* Starts the flush timer if a valid interval is set.
|
|
323
|
+
* @returns {void}
|
|
324
|
+
*/
|
|
183
325
|
startTimer() {
|
|
184
326
|
this.stopTimer();
|
|
185
327
|
if (typeof this.flushInterval == 'number' && this.flushInterval > 0) {
|
|
@@ -187,12 +329,20 @@ class CollecionesEmitter {
|
|
|
187
329
|
}
|
|
188
330
|
}
|
|
189
331
|
|
|
332
|
+
/**
|
|
333
|
+
* Starts the flush timer only if not already running.
|
|
334
|
+
* @returns {void}
|
|
335
|
+
*/
|
|
190
336
|
startTimerIfStopped() {
|
|
191
337
|
if (!this.timer) {
|
|
192
338
|
this.startTimer();
|
|
193
339
|
}
|
|
194
340
|
}
|
|
195
341
|
|
|
342
|
+
/**
|
|
343
|
+
* Stops the active flush timer.
|
|
344
|
+
* @returns {void}
|
|
345
|
+
*/
|
|
196
346
|
stopTimer() {
|
|
197
347
|
if (this.timer) {
|
|
198
348
|
clearInterval(this.timer);
|
|
@@ -200,29 +350,50 @@ class CollecionesEmitter {
|
|
|
200
350
|
this.timer = null;
|
|
201
351
|
}
|
|
202
352
|
|
|
353
|
+
/**
|
|
354
|
+
* Adds an event to the buffer and flushes if threshold is reached.
|
|
355
|
+
* Validates that the event is an instance of CollecionesEvent to ensure correct event type.
|
|
356
|
+
* @param {CollecionesEvent} event - Event instance to be tracked.
|
|
357
|
+
* @throws {Error} Throws if event is not a CollecionesEvent instance.
|
|
358
|
+
* @returns {void}
|
|
359
|
+
*/
|
|
203
360
|
track(event) {
|
|
204
|
-
if (!(event instanceof
|
|
205
|
-
throw new Error('Event must be an instance of
|
|
361
|
+
if (!(event instanceof CollecionesEvent)) {
|
|
362
|
+
throw new Error('Event must be an instance of CollecionesEvent');
|
|
206
363
|
}
|
|
207
364
|
this.trackAsync(event);
|
|
208
365
|
}
|
|
209
366
|
|
|
367
|
+
/**
|
|
368
|
+
* Asynchronously adds an event and flushes if the buffer size is exceeded.
|
|
369
|
+
* Validates that the event is an instance of CollecionesEvent to ensure correct event type.
|
|
370
|
+
* @param {CollecionesEvent} event - Event instance to be tracked asynchronously.
|
|
371
|
+
* @throws {Error} Throws if event is not a CollecionesEvent instance.
|
|
372
|
+
* @returns {Promise<void>} Resolves when event is added and flush triggered if needed.
|
|
373
|
+
*/
|
|
210
374
|
async trackAsync(event) {
|
|
211
|
-
if (!(event instanceof
|
|
212
|
-
throw new Error('Event must be an instance of
|
|
375
|
+
if (!(event instanceof CollecionesEvent)) {
|
|
376
|
+
throw new Error('Event must be an instance of CollecionesEvent');
|
|
213
377
|
}
|
|
214
378
|
this.startTimerIfStopped();
|
|
215
379
|
this.buffer.push(event);
|
|
216
380
|
return (this.buffer.length >= this.flushSize) ? this.flush() : Promise.resolve();
|
|
217
381
|
}
|
|
218
382
|
|
|
383
|
+
/**
|
|
384
|
+
* Sends all buffered events in a single POST request to the server.
|
|
385
|
+
* Each event is serialized via `.toJSON()` and encoded as a base64 string.
|
|
386
|
+
* Response status is checked for HTTP success; errors are logged but not thrown.
|
|
387
|
+
*
|
|
388
|
+
* @returns {Promise<boolean|undefined>} Resolves true if flush was triggered, or undefined if buffer was empty.
|
|
389
|
+
*/
|
|
219
390
|
async flush() {
|
|
220
391
|
if (this.buffer.length === 0) return;
|
|
221
392
|
const eventsToSend = [...this.buffer];
|
|
222
393
|
this.buffer = [];
|
|
223
394
|
let postPayload = [];
|
|
224
395
|
eventsToSend.forEach((e) => {
|
|
225
|
-
postPayload.push( e.
|
|
396
|
+
postPayload.push( toBase64( JSON.stringify(e.toJSON())) );
|
|
226
397
|
});
|
|
227
398
|
this.stopTimer();
|
|
228
399
|
try {
|
|
@@ -244,8 +415,17 @@ class CollecionesEmitter {
|
|
|
244
415
|
}
|
|
245
416
|
}
|
|
246
417
|
|
|
418
|
+
/**
|
|
419
|
+
* Tracks events by enriching them with shared identifiers and routing through configured emitters.
|
|
420
|
+
*/
|
|
247
421
|
class CollecionesTracker {
|
|
248
|
-
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Constructs a new tracker instance.
|
|
425
|
+
* @param {Array} emitters - Array of emitter instances responsible for sending events.
|
|
426
|
+
* @param {string} trackerName - Name identifying this tracker.
|
|
427
|
+
* @param {string} appName - Name of the application generating events.
|
|
428
|
+
*/
|
|
249
429
|
constructor(emitters, trackerName, appName) {
|
|
250
430
|
this.emitters = emitters;
|
|
251
431
|
this.trackerName = trackerName;
|
|
@@ -253,12 +433,13 @@ class CollecionesTracker {
|
|
|
253
433
|
this.identifiers = {};
|
|
254
434
|
}
|
|
255
435
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
436
|
+
/**
|
|
437
|
+
* Sends an event to all emitters after enriching it with identifiers and metadata.
|
|
438
|
+
* @param {CollecionesEvent} collecionesEvent - The event to be sent.
|
|
439
|
+
* @throws {Error} If the input is not an instance of CollecionesEvent.
|
|
440
|
+
*/
|
|
260
441
|
track(collecionesEvent) {
|
|
261
|
-
if (!(collecionesEvent instanceof
|
|
442
|
+
if (!(collecionesEvent instanceof CollecionesEvent)) {
|
|
262
443
|
throw new Error('Event must be of type CollecionesEvent');
|
|
263
444
|
}
|
|
264
445
|
for (const [key, value] of Object.entries(this.identifiers)) {
|
|
@@ -269,172 +450,273 @@ class CollecionesTracker {
|
|
|
269
450
|
this.emitters.forEach(element => {
|
|
270
451
|
element.track(collecionesEvent, this.trackerName, this.appName);
|
|
271
452
|
});
|
|
272
|
-
|
|
273
453
|
}
|
|
274
454
|
}
|
|
275
455
|
|
|
456
|
+
/**
|
|
457
|
+
* Web-specific tracker that enriches events with browser context before sending them.
|
|
458
|
+
* Extends the base CollecionesTracker.
|
|
459
|
+
*/
|
|
276
460
|
class CollecionesWebTracker extends CollecionesTracker {
|
|
277
|
-
|
|
461
|
+
/**
|
|
462
|
+
* Creates a new instance of CollecionesWebTracker.
|
|
463
|
+
* @param {Array} emitters - A list of emitter instances used to send the event.
|
|
464
|
+
* @param {string} trackerName - The name of the tracker.
|
|
465
|
+
* @param {string} appName - The name of the application generating events.
|
|
466
|
+
*/
|
|
278
467
|
constructor(emitters, trackerName, appName) {
|
|
279
468
|
super(emitters, trackerName, appName);
|
|
280
469
|
}
|
|
281
470
|
|
|
471
|
+
/**
|
|
472
|
+
* Tracks an event, enriching it with browser context information.
|
|
473
|
+
* @param {CollecionesEvent} collecionesEvent - The event object to track.
|
|
474
|
+
* @throws {Error} If the event is not an instance of CollecionesEvent.
|
|
475
|
+
*/
|
|
282
476
|
track(collecionesEvent) {
|
|
283
|
-
if (!(collecionesEvent instanceof
|
|
477
|
+
if (!(collecionesEvent instanceof CollecionesEvent)) {
|
|
284
478
|
throw new Error('Event must be of type CollecionesEvent');
|
|
285
479
|
}
|
|
286
|
-
collecionesEvent.
|
|
480
|
+
collecionesEvent.setContext('browserContext', getBrowserContext());
|
|
287
481
|
super.track(collecionesEvent);
|
|
288
482
|
}
|
|
289
483
|
}
|
|
290
484
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
485
|
+
/**
|
|
486
|
+
* Colleciones DSL
|
|
487
|
+
* ---------------
|
|
488
|
+
* This module provides a fluent, human-readable DSL (domain-specific language) for constructing
|
|
489
|
+
* structured tracking events based on CollecionesBaseEvent. Each step in the chain represents
|
|
490
|
+
* a semantically meaningful piece of the event: the entity, the action, the actor, references, etc.
|
|
491
|
+
*
|
|
492
|
+
* Entry Point:
|
|
493
|
+
* collecionesDsl.the('dark')
|
|
494
|
+
*
|
|
495
|
+
* Chaining:
|
|
496
|
+
* - .and('...') : add additional adjectives
|
|
497
|
+
* - ._('entity') : specify the entity being acted on
|
|
498
|
+
* - .identified.by('field') : declare primary identifier for the subject
|
|
499
|
+
* - .has.been('action') : declare what happened
|
|
500
|
+
* - .by('actor') : describe the actor performing the action
|
|
501
|
+
* - .identified.by('id') : add identifiers for the actor
|
|
502
|
+
* - .with('key').set.to('val') : add contextual data fields
|
|
503
|
+
* - .referring.to('entity') : describe referenced/related entities
|
|
504
|
+
* - .identified.by('refId') : add identifiers for the reference
|
|
505
|
+
* - .conform.to('SchemaName') : attach schema metadata
|
|
506
|
+
* - .then.track.with(emitter) : finalize and emit the event
|
|
507
|
+
*
|
|
508
|
+
* Each function in the chain passes forward a scoped version of the CollecionesBaseEvent instance,
|
|
509
|
+
* and enforces semantic constraints (e.g. `.as()` is required after `.by()`).
|
|
510
|
+
*
|
|
511
|
+
* Internal:
|
|
512
|
+
* - Uses setRefence / setRefenceIdentifier for references
|
|
513
|
+
* - Uses helpers.getEvent() for test access
|
|
514
|
+
* - Uses instanceof CollecionesEmitter for validation
|
|
515
|
+
*
|
|
516
|
+
* This file is designed for internal use by developers building with the Colleciones tracking model.
|
|
517
|
+
*/
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
let init = (entity) => {
|
|
521
|
+
return ((entity)=>{
|
|
522
|
+
let eventInstance = new CollecionesEvent(); // Core event object
|
|
523
|
+
eventInstance.setEntity(entity);
|
|
524
|
+
|
|
525
|
+
let helpers = {
|
|
526
|
+
getEvent: () => {
|
|
527
|
+
return eventInstance;
|
|
528
|
+
},
|
|
529
|
+
};
|
|
299
530
|
|
|
300
|
-
|
|
531
|
+
// DSL function groups
|
|
532
|
+
let andEntity = () => {}; // .and(...) chaining after the()
|
|
533
|
+
let setEntityAfterAnd = () => {}; // ._('entity') after .and
|
|
534
|
+
let setEntityIdentifiers = () => {}; // .identified.by(...) for main subject
|
|
535
|
+
let setAction = () => {}; // .has.been(...) for action
|
|
536
|
+
let setActor = () => {}; // .by('actor')
|
|
537
|
+
let setActorIdentifier = () => {}; // .identified.by(...) inside .by(...)
|
|
538
|
+
let addContext = () => {}; // .with(...).set.to(...)
|
|
539
|
+
let addReference = () => {}; // .referring.to(...)
|
|
540
|
+
let getAddReference = () => {}; // .identified.by().as() inside .referring
|
|
541
|
+
let addReferenceIdentifier = () => {}; // .and(...) after reference
|
|
542
|
+
let conformingTo = () => {}; // .conform.to('SchemaName')
|
|
543
|
+
let track = () => {}; // .then.track.with(emitter)
|
|
544
|
+
|
|
545
|
+
// Adjective chaining: .and(...)._('entity')
|
|
546
|
+
setEntityAfterAnd = (entity) => {
|
|
547
|
+
eventInstance.addAdjective(eventInstance.entity);
|
|
548
|
+
eventInstance.setEntity(entity);
|
|
549
|
+
return {
|
|
550
|
+
as: () => {
|
|
551
|
+
return { helpers }
|
|
552
|
+
},
|
|
553
|
+
identified: {
|
|
554
|
+
by: setEntityIdentifiers
|
|
555
|
+
},
|
|
556
|
+
has: { been: setAction },
|
|
557
|
+
helpers
|
|
558
|
+
};
|
|
559
|
+
};
|
|
301
560
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
byteToHex[arr[offset + 9]] +
|
|
320
|
-
'-' +
|
|
321
|
-
byteToHex[arr[offset + 10]] +
|
|
322
|
-
byteToHex[arr[offset + 11]] +
|
|
323
|
-
byteToHex[arr[offset + 12]] +
|
|
324
|
-
byteToHex[arr[offset + 13]] +
|
|
325
|
-
byteToHex[arr[offset + 14]] +
|
|
326
|
-
byteToHex[arr[offset + 15]]).toLowerCase();
|
|
327
|
-
}
|
|
561
|
+
// Identifier setup: .identified.by().as()
|
|
562
|
+
setEntityIdentifiers = (name) => {
|
|
563
|
+
eventInstance.setIdentifier(name, null);
|
|
564
|
+
return {
|
|
565
|
+
as: (value) => {
|
|
566
|
+
eventInstance.setIdentifier(name, value);
|
|
567
|
+
return {
|
|
568
|
+
helpers,
|
|
569
|
+
and: {
|
|
570
|
+
by: setEntityIdentifiers
|
|
571
|
+
},
|
|
572
|
+
has: { been: setAction },
|
|
573
|
+
}
|
|
574
|
+
},
|
|
575
|
+
helpers
|
|
576
|
+
};
|
|
577
|
+
};
|
|
328
578
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
}
|
|
579
|
+
// Additional adjectives: .and('...')._()
|
|
580
|
+
andEntity = (entity) => {
|
|
581
|
+
eventInstance.addAdjective(eventInstance.entity);
|
|
582
|
+
eventInstance.setEntity(entity);
|
|
583
|
+
return {
|
|
584
|
+
and: andEntity,
|
|
585
|
+
_ : setEntityAfterAnd,
|
|
586
|
+
helpers
|
|
587
|
+
}
|
|
588
|
+
};
|
|
340
589
|
|
|
341
|
-
|
|
342
|
-
|
|
590
|
+
// Action: .has.been('...')
|
|
591
|
+
setAction = (action) => {
|
|
592
|
+
eventInstance.setAction(action);
|
|
593
|
+
return {
|
|
594
|
+
by: setActor,
|
|
595
|
+
referring: { to: addReference },
|
|
596
|
+
with: addContext,
|
|
597
|
+
conform: {to: conformingTo},
|
|
598
|
+
then: {track: {with: track}},
|
|
599
|
+
helpers
|
|
600
|
+
}
|
|
601
|
+
};
|
|
343
602
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
return unsafeStringify(rnds);
|
|
356
|
-
}
|
|
603
|
+
// Actor: .by('user')
|
|
604
|
+
setActor = (name) => {
|
|
605
|
+
eventInstance.setActor(name);
|
|
606
|
+
return {
|
|
607
|
+
identified: {
|
|
608
|
+
by: setActorIdentifier
|
|
609
|
+
},
|
|
610
|
+
with: addContext,
|
|
611
|
+
helpers
|
|
612
|
+
}
|
|
613
|
+
};
|
|
357
614
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
eventFormat: 'CollecionesSemanticEvent',
|
|
373
|
-
eventFormatVersion: '1'
|
|
615
|
+
// Actor identifier: .identified.by().as()
|
|
616
|
+
setActorIdentifier = (name) => {
|
|
617
|
+
return {
|
|
618
|
+
as: (value) => {
|
|
619
|
+
eventInstance.setActorIdentifier(name, value);
|
|
620
|
+
return {
|
|
621
|
+
helpers,
|
|
622
|
+
and: setActorIdentifier,
|
|
623
|
+
with: addContext,
|
|
624
|
+
referring: { to: addReference },
|
|
625
|
+
}
|
|
626
|
+
},
|
|
627
|
+
helpers
|
|
628
|
+
}
|
|
374
629
|
};
|
|
375
|
-
}
|
|
376
630
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
631
|
+
// Contextual field: .with('key').set.to('value')
|
|
632
|
+
addContext = (context) => {
|
|
633
|
+
return {
|
|
634
|
+
helpers,
|
|
635
|
+
set: {
|
|
636
|
+
to: (value) => {
|
|
637
|
+
eventInstance.setContext(context, value);
|
|
638
|
+
return {
|
|
639
|
+
helpers,
|
|
640
|
+
and: addContext,
|
|
641
|
+
referring: { to: addReference },
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
};
|
|
380
647
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
648
|
+
// Reference: .referring.to('...')
|
|
649
|
+
addReference = (entity) => {
|
|
650
|
+
eventInstance.setRefence(entity);
|
|
651
|
+
return {
|
|
652
|
+
identified: {
|
|
653
|
+
by: getAddReference(entity)
|
|
654
|
+
},
|
|
655
|
+
conform: {to: conformingTo},
|
|
656
|
+
then: {track: {with: track}},
|
|
657
|
+
helpers
|
|
658
|
+
}
|
|
659
|
+
};
|
|
390
660
|
|
|
391
|
-
|
|
661
|
+
// Reference identifier setup: .identified.by().as()
|
|
662
|
+
getAddReference = (entity) => {
|
|
663
|
+
return (name) => {
|
|
664
|
+
return {
|
|
665
|
+
as: (value) => {
|
|
666
|
+
eventInstance.setRefenceIdentifier(entity, name, value);
|
|
667
|
+
return {
|
|
668
|
+
helpers,
|
|
669
|
+
and: addReferenceIdentifier,
|
|
670
|
+
conform: {to: conformingTo},
|
|
671
|
+
then: {track: {with: track}},
|
|
672
|
+
}
|
|
673
|
+
},
|
|
674
|
+
helpers,
|
|
675
|
+
};
|
|
676
|
+
};
|
|
677
|
+
};
|
|
392
678
|
|
|
393
|
-
|
|
679
|
+
// Schema declaration: .conform.to('...')
|
|
680
|
+
conformingTo = (name) => {
|
|
681
|
+
eventInstance.setSchema(name);
|
|
682
|
+
return {
|
|
683
|
+
then: {track: {with: track}},
|
|
684
|
+
helpers
|
|
685
|
+
}
|
|
686
|
+
};
|
|
394
687
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
688
|
+
// Final dispatch: .then.track.with(emitter)
|
|
689
|
+
track = (emitter) => {
|
|
690
|
+
if(emitter instanceof CollecionesEmitter){
|
|
691
|
+
emitter.track(eventInstance);
|
|
692
|
+
}
|
|
693
|
+
return {
|
|
694
|
+
and: track,
|
|
695
|
+
helpers,
|
|
696
|
+
}
|
|
403
697
|
};
|
|
404
|
-
this.adjective = formatAdjectives(adjective);
|
|
405
|
-
let adjectiveString = stringifyAdjectives(this.adjective);
|
|
406
|
-
itemEntity = underscoreToCamelCase(itemEntity);
|
|
407
|
-
this.data.itemEntity = itemEntity;
|
|
408
|
-
this.data.itemEventName = `${itemEntity}${(adjectiveString !== undefined) ? adjectiveString : ''}__${this.action}`;
|
|
409
|
-
this.data.entityItemIdentifiers = {};
|
|
410
|
-
}
|
|
411
698
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
}
|
|
422
|
-
if(Array.isArray(value)) {
|
|
423
|
-
value.forEach((v)=> {
|
|
424
|
-
this.data.entityItemIdentifiers[key].push(v);
|
|
425
|
-
});
|
|
426
|
-
} else {
|
|
427
|
-
this.data.entityItemIdentifiers[key].push(value);
|
|
699
|
+
return {
|
|
700
|
+
and: andEntity,
|
|
701
|
+
_: setEntityAfterAnd,
|
|
702
|
+
identified: {
|
|
703
|
+
by: setEntityIdentifiers
|
|
704
|
+
},
|
|
705
|
+
has: { been: setAction},
|
|
706
|
+
node: '_base',
|
|
707
|
+
helpers
|
|
428
708
|
}
|
|
429
|
-
}
|
|
430
709
|
|
|
431
|
-
|
|
432
|
-
}
|
|
710
|
+
})(entity);
|
|
711
|
+
};
|
|
712
|
+
|
|
713
|
+
let collecionesDsl = {
|
|
714
|
+
the: init
|
|
715
|
+
};
|
|
433
716
|
|
|
434
717
|
exports.CollecionesEmitter = CollecionesEmitter;
|
|
435
718
|
exports.CollecionesEvent = CollecionesEvent;
|
|
436
|
-
exports.CollecionesSemanticCollectionEvent = CollecionesSemanticCollectionEvent;
|
|
437
|
-
exports.CollecionesSemanticEvent = CollecionesSemanticEvent;
|
|
438
719
|
exports.CollecionesTracker = CollecionesTracker;
|
|
439
720
|
exports.CollecionesWebTracker = CollecionesWebTracker;
|
|
721
|
+
exports.collecionesDsl = collecionesDsl;
|
|
440
722
|
//# sourceMappingURL=index.cjs.map
|