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