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