@compilacion/colleciones-clientos 1.0.12 → 2.0.1

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