@compilacion/colleciones-clientos 1.0.11 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,54 +1,25 @@
1
1
  // helper 'private' functions
2
- const underscoreToCamelCase = function (str) {
3
- str = str.replace(/[^\x00-\x7F_]/g, '');
4
- return str
5
- .split('_')
6
- .map((word, index) => {
7
- if (index === 0) {
8
- return word;
9
- }
10
- return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
11
- })
12
- .join('');
13
- };
14
-
15
- const formatAdjectives = function (adjectiveParameter) {
16
- let errorMsg = new Error('Adjective must be a string, an array of strings, or undefined');
17
- if (typeof adjectiveParameter === 'undefined') {
18
- return undefined;
19
- }
20
- if (typeof adjectiveParameter === 'string') {
21
- return [underscoreToCamelCase(adjectiveParameter)];
22
- }
23
- if (Array.isArray(adjectiveParameter) && adjectiveParameter.every(item => typeof item === 'string')) {
24
- let returnValue = [];
25
- adjectiveParameter.forEach((adjectiveParameter) => {
26
- if (!typeof item === 'string') {
27
- throw errorMsg;
28
- }
29
- returnValue.push(underscoreToCamelCase(adjectiveParameter));
30
- });
31
- return returnValue;
32
- }
33
- throw errorMsg;
34
- };
35
2
 
36
- const stringifyAdjectives = function (adjective) {
37
- if (!Array.isArray(adjective) || adjective.length === 0) {
38
- return undefined;
39
- }
40
- const uniqueSorted = [...new Set(adjective)].sort();
41
- return '.' + uniqueSorted.join('.');
42
- };
43
3
 
4
+ /**
5
+ * Encodes a string to Base64.
6
+ * Uses browser or Node.js method depending on environment.
7
+ * @param {string} str
8
+ * @returns {string}
9
+ */
44
10
  const toBase64 = function (str) {
45
- if (typeof window !== 'undefined' && window.btoa) {
11
+ if (typeof window !== 'undefined' && typeof window.btoa === 'function') {
46
12
  return window.btoa(unescape(encodeURIComponent(str)));
47
13
  } else {
48
14
  return Buffer.from(str, 'utf-8').toString('base64');
49
15
  }
50
16
  };
51
17
 
18
+ /**
19
+ * Collects browser-related context values like screen size, language, etc.
20
+ * Returns an empty object if not in browser environment.
21
+ * @returns {Object}
22
+ */
52
23
  const getBrowserContext = function() {
53
24
  if (typeof window === 'undefined' || typeof navigator === 'undefined') {
54
25
  return {};
@@ -68,108 +39,275 @@ const getBrowserContext = function() {
68
39
  };
69
40
  };
70
41
 
71
- class CollecionesBaseEvent {
42
+ /**
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.
45
+ */
46
+ class CollecionesEvent {
72
47
 
48
+ /**
49
+ * Constructs a new event with default metadata and timestamps.
50
+ */
73
51
  constructor() {
74
- this.eventName = '';
75
- this.data = {};
76
- this.data.meta = {
77
- eventFormat: 'CollecionesBaseEvent',
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',
78
66
  eventFormatVersion: '1'
79
67
  };
80
- this.data.timestamps = {};
81
- this.data.timestamps.clientDatetimeUtc = new Date().toISOString();
68
+ this.meta.tracker = '';
69
+ this.meta.app = '';
70
+ this.meta.timestamps = {};
71
+ this.meta.timestamps.clientDatetimeUtc = new Date().toISOString();
82
72
  if (typeof window !== 'undefined' && typeof navigator !== 'undefined' && navigator.language) {
83
- this.data.timestamps.clientDatetimeLocal = new Date(Date.now() - new Date().getTimezoneOffset() * 60000).toISOString();
84
- this.data.timestamps.timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
85
- this.data.timestamps.timeZoneOffset = new Date().getTimezoneOffset();
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();
86
76
  } else {
87
- this.data.timestamps.clientDatetimeLocal = new Date().toISOString();
88
- this.data.timestamps.timeZone = 'UTC';
77
+ this.meta.timestamps.clientDatetimeLocal = new Date().toISOString();
78
+ this.meta.timestamps.timeZone = 'UTC';
89
79
  }
90
80
  }
91
81
 
92
- setEventName(name) {
93
- this.eventName = name;
82
+ /**
83
+ * Returns the declared event format.
84
+ * @returns {string} The format name (e.g. 'CollecionesEvent').
85
+ */
86
+ getEventFormat() {
87
+ let v = this.meta?.eventFormat;
88
+ return (typeof v !== 'undefined') ? v : '1';
94
89
  }
95
90
 
96
- getEventName() {
97
- return this.eventName;
91
+ /**
92
+ * Returns the version of the event format.
93
+ * @returns {string} The format version string.
94
+ */
95
+ getEventFormatVersion() {
96
+ let v = this?.meta?.eventFormatVersion;
97
+ return (typeof v !== 'undefined') ? v : 'CollecionesEvent';
98
98
  }
99
99
 
100
- getEventFormat() {
101
- let v = this.data?.meta?.eventFormat;
102
- return (typeof v !== 'undefined') ? v : '1';
100
+ /**
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.
103
+ */
104
+ overrideDatetime(dateTimeObject = {}) {
105
+ for (const [key, value] of Object.entries(dateTimeObject)) {
106
+ this.meta.timestamps[key] = value;
107
+ }
103
108
  }
104
109
 
105
- getEventFormatVersion() {
106
- let v = this.data?.meta?.eventFormatVersion;
107
- return (typeof v !== 'undefined') ? v : 'CollecionesBaseEvent';
110
+ /**
111
+ * Sets the name of the tracker responsible for generating this event.
112
+ * @param {string} name - Tracker name.
113
+ */
114
+ setTracker(name) {
115
+ this.meta.tracker = name;
108
116
  }
109
117
 
110
- setData(data) {
111
- this.data = data;
118
+ /**
119
+ * Sets the name of the application that generated the event.
120
+ * @param {string} name - Application name.
121
+ */
122
+ setAppName(name) {
123
+ this.meta.appName = name;
112
124
  }
113
125
 
114
- addAttribute(name, value) {
115
- return this.addField(name, value);
126
+ /**
127
+ * Sets the expected schema name for this event.
128
+ * @param {string} schema - The name of the schema.
129
+ */
130
+ setSchema(schema) {
131
+ if (typeof schema !== 'string') {
132
+ throw new Error('Schema must be a string');
133
+ }
134
+ this.meta.schema = schema;
116
135
  }
117
136
 
118
- addField(name, value) {
119
- if (typeof this.data.fields !== 'object' || this.data.fields === null) {
120
- this.data.fields = {};
137
+ /**
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.
148
+ */
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');
121
160
  }
122
- this.data.fields[name] = value;
161
+ this.adjevtives.push(adjective);
123
162
  }
124
163
 
125
- setTracker(name) {
126
- this.data.tracker = name;
164
+ /**
165
+ * Adds or updates an identifier for the primary entity.
166
+ * @param {string} name - The identifier key.
167
+ * @param {*} identifier - The identifier value.
168
+ */
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;
127
174
  }
128
175
 
129
- setAppName(name) {
130
- this.data.appName = name;
176
+ /**
177
+ * Defines the name of the actor (who or what performed the action).
178
+ * @param {string} name - Actor name (e.g., 'user').
179
+ */
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;
131
185
  }
132
186
 
133
- convertParamIdentifiers(param) {
134
- if (typeof param === 'object' && param !== null && !Array.isArray(param)) {
135
- for (const [key, value] of Object.entries(param)) {
136
- this.addIdentifier(key, value);
137
- }
187
+ /**
188
+ * Adds or updates an identifier for the actor.
189
+ * @param {string} name - Identifier name.
190
+ * @param {*} identifier - Identifier value.
191
+ */
192
+ setActorIdentifier = function(name, identifier) {
193
+ if (typeof name !== 'string') {
194
+ throw new Error('Actor Identifier name must be a string');
138
195
  }
196
+ this.actor.identifiers[name] = identifier;
139
197
  }
140
198
 
141
- addIdentifier(name, value) {
142
- if (typeof this.data.identifiers !== 'object' || this.data.identifiers === null) {
143
- this.data.identifiers = {};
199
+ /**
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.
203
+ */
204
+ setContext = function(context, value) {
205
+ if (typeof context !== 'string') {
206
+ throw new Error('Context must be a string');
144
207
  }
145
- this.data.identifiers[name] = value;
208
+ this.context[context] = value;
146
209
  }
147
210
 
148
- getPostObject() {
149
- this.data.timestamps.sendDatetimeUtc = new Date().toISOString();
150
- this.data.timestamps.sendDatetimeLocal = new Date(Date.now() - new Date().getTimezoneOffset() * 60000).toISOString();
151
- return {
152
- eventname: this.getEventName(),
153
- eventFormat: this.getEventFormat(),
154
- eventFormatVersion: this.getEventFormatVersion(),
155
- payload: toBase64(this.getPayload())
156
- };
211
+ /**
212
+ * Declares an entity referenced by this event.
213
+ * @param {string} entity - The referenced entity name.
214
+ */
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
+ }
157
222
  }
158
223
 
159
- overrideDatetime(dateTimeObject = {}) {
160
- for (const [key, value] of Object.entries(dateTimeObject)) {
161
- this.data.timestamps[key] = value;
224
+ /**
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.
229
+ */
230
+ setRefenceIdentifier = function(entity, name, identifier) {
231
+ if (typeof entity !== 'string') {
232
+ throw new Error('Referenced entity name must be a string');
162
233
  }
234
+ if (typeof name !== 'string') {
235
+ throw new Error('Actor Identifier name must be a string');
236
+ }
237
+ this.setRefence(entity);
238
+ this.references[entity].identifiers[name] = identifier;
163
239
  }
164
240
 
165
- getPayload() {
166
- return JSON.stringify(this.data);
241
+ /**
242
+ * Serializes the event to a plain object suitable for JSON.stringify().
243
+ * @returns {object}
244
+ */
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
+ };
167
257
  }
168
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
+
169
280
  }
170
281
 
282
+ /**
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.
302
+ */
171
303
  class CollecionesEmitter {
172
304
 
305
+ /**
306
+ * Initializes the emitter with buffering settings.
307
+ * @param {string} [endpoint='/collect'] - The URL to send events to.
308
+ * @param {number} [flushSize=10] - Number of events to buffer before flushing.
309
+ * @param {number|boolean} [flushInterval=false] - Time in ms to flush events periodically.
310
+ */
173
311
  constructor(endpoint = '/collect', flushSize = 10, flushInterval = false) {
174
312
  this.endpoint = endpoint;
175
313
  this.flushInterval = flushInterval;
@@ -178,6 +316,10 @@ class CollecionesEmitter {
178
316
  this.timer = null;
179
317
  }
180
318
 
319
+ /**
320
+ * Starts the flush timer if a valid interval is set.
321
+ * @returns {void}
322
+ */
181
323
  startTimer() {
182
324
  this.stopTimer();
183
325
  if (typeof this.flushInterval == 'number' && this.flushInterval > 0) {
@@ -185,12 +327,20 @@ class CollecionesEmitter {
185
327
  }
186
328
  }
187
329
 
330
+ /**
331
+ * Starts the flush timer only if not already running.
332
+ * @returns {void}
333
+ */
188
334
  startTimerIfStopped() {
189
335
  if (!this.timer) {
190
336
  this.startTimer();
191
337
  }
192
338
  }
193
339
 
340
+ /**
341
+ * Stops the active flush timer.
342
+ * @returns {void}
343
+ */
194
344
  stopTimer() {
195
345
  if (this.timer) {
196
346
  clearInterval(this.timer);
@@ -198,29 +348,50 @@ class CollecionesEmitter {
198
348
  this.timer = null;
199
349
  }
200
350
 
351
+ /**
352
+ * Adds an event to the buffer and flushes if threshold is reached.
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}
357
+ */
201
358
  track(event) {
202
- if (!(event instanceof CollecionesBaseEvent)) {
203
- throw new Error('Event must be an instance of CollecionesBaseEvent');
359
+ if (!(event instanceof CollecionesEvent)) {
360
+ throw new Error('Event must be an instance of CollecionesEvent');
204
361
  }
205
362
  this.trackAsync(event);
206
363
  }
207
364
 
365
+ /**
366
+ * Asynchronously adds an event and flushes if the buffer size is exceeded.
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.
371
+ */
208
372
  async trackAsync(event) {
209
- if (!(event instanceof CollecionesBaseEvent)) {
210
- throw new Error('Event must be an instance of CollecionesBaseEvent');
373
+ if (!(event instanceof CollecionesEvent)) {
374
+ throw new Error('Event must be an instance of CollecionesEvent');
211
375
  }
212
376
  this.startTimerIfStopped();
213
377
  this.buffer.push(event);
214
378
  return (this.buffer.length >= this.flushSize) ? this.flush() : Promise.resolve();
215
379
  }
216
380
 
381
+ /**
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.
387
+ */
217
388
  async flush() {
218
389
  if (this.buffer.length === 0) return;
219
390
  const eventsToSend = [...this.buffer];
220
391
  this.buffer = [];
221
392
  let postPayload = [];
222
393
  eventsToSend.forEach((e) => {
223
- postPayload.push( e.getPostObject() );
394
+ postPayload.push( toBase64( JSON.stringify(e.toJSON())) );
224
395
  });
225
396
  this.stopTimer();
226
397
  try {
@@ -242,8 +413,17 @@ class CollecionesEmitter {
242
413
  }
243
414
  }
244
415
 
416
+ /**
417
+ * Tracks events by enriching them with shared identifiers and routing through configured emitters.
418
+ */
245
419
  class CollecionesTracker {
246
-
420
+
421
+ /**
422
+ * Constructs a new tracker instance.
423
+ * @param {Array} emitters - Array of emitter instances responsible for sending events.
424
+ * @param {string} trackerName - Name identifying this tracker.
425
+ * @param {string} appName - Name of the application generating events.
426
+ */
247
427
  constructor(emitters, trackerName, appName) {
248
428
  this.emitters = emitters;
249
429
  this.trackerName = trackerName;
@@ -251,12 +431,13 @@ class CollecionesTracker {
251
431
  this.identifiers = {};
252
432
  }
253
433
 
254
- addIdentifier(name, value) {
255
- this.identifiers[name] = value;
256
- }
257
-
434
+ /**
435
+ * Sends an event to all emitters after enriching it with identifiers and metadata.
436
+ * @param {CollecionesEvent} collecionesEvent - The event to be sent.
437
+ * @throws {Error} If the input is not an instance of CollecionesEvent.
438
+ */
258
439
  track(collecionesEvent) {
259
- if (!(collecionesEvent instanceof CollecionesBaseEvent)) {
440
+ if (!(collecionesEvent instanceof CollecionesEvent)) {
260
441
  throw new Error('Event must be of type CollecionesEvent');
261
442
  }
262
443
  for (const [key, value] of Object.entries(this.identifiers)) {
@@ -267,167 +448,269 @@ class CollecionesTracker {
267
448
  this.emitters.forEach(element => {
268
449
  element.track(collecionesEvent, this.trackerName, this.appName);
269
450
  });
270
-
271
451
  }
272
452
  }
273
453
 
454
+ /**
455
+ * Web-specific tracker that enriches events with browser context before sending them.
456
+ * Extends the base CollecionesTracker.
457
+ */
274
458
  class CollecionesWebTracker extends CollecionesTracker {
275
-
459
+ /**
460
+ * Creates a new instance of CollecionesWebTracker.
461
+ * @param {Array} emitters - A list of emitter instances used to send the event.
462
+ * @param {string} trackerName - The name of the tracker.
463
+ * @param {string} appName - The name of the application generating events.
464
+ */
276
465
  constructor(emitters, trackerName, appName) {
277
466
  super(emitters, trackerName, appName);
278
467
  }
279
468
 
469
+ /**
470
+ * Tracks an event, enriching it with browser context information.
471
+ * @param {CollecionesEvent} collecionesEvent - The event object to track.
472
+ * @throws {Error} If the event is not an instance of CollecionesEvent.
473
+ */
280
474
  track(collecionesEvent) {
281
- if (!(collecionesEvent instanceof CollecionesBaseEvent)) {
475
+ if (!(collecionesEvent instanceof CollecionesEvent)) {
282
476
  throw new Error('Event must be of type CollecionesEvent');
283
477
  }
284
- collecionesEvent.addField('browserContext', getBrowserContext());
478
+ collecionesEvent.setContext('browserContext', getBrowserContext());
285
479
  super.track(collecionesEvent);
286
480
  }
287
481
  }
288
482
 
289
- class CollecionesEvent extends CollecionesBaseEvent {
290
-
291
- constructor(name, data, identifiers) {
292
- super();
293
- this.setEventName(name);
294
- Object.assign(this.data, data);
295
- this.convertParamIdentifiers(identifiers);
296
- }
483
+ /**
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.
515
+ */
516
+
517
+
518
+ let init = (entity) => {
519
+ return ((entity)=>{
520
+ let eventInstance = new CollecionesEvent(); // Core event object
521
+ eventInstance.setEntity(entity);
522
+
523
+ let helpers = {
524
+ getEvent: () => {
525
+ return eventInstance;
526
+ },
527
+ };
297
528
 
298
- }
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
+ };
299
558
 
300
- const byteToHex = [];
301
- for (let i = 0; i < 256; ++i) {
302
- byteToHex.push((i + 0x100).toString(16).slice(1));
303
- }
304
- function unsafeStringify(arr, offset = 0) {
305
- return (byteToHex[arr[offset + 0]] +
306
- byteToHex[arr[offset + 1]] +
307
- byteToHex[arr[offset + 2]] +
308
- byteToHex[arr[offset + 3]] +
309
- '-' +
310
- byteToHex[arr[offset + 4]] +
311
- byteToHex[arr[offset + 5]] +
312
- '-' +
313
- byteToHex[arr[offset + 6]] +
314
- byteToHex[arr[offset + 7]] +
315
- '-' +
316
- byteToHex[arr[offset + 8]] +
317
- byteToHex[arr[offset + 9]] +
318
- '-' +
319
- byteToHex[arr[offset + 10]] +
320
- byteToHex[arr[offset + 11]] +
321
- byteToHex[arr[offset + 12]] +
322
- byteToHex[arr[offset + 13]] +
323
- byteToHex[arr[offset + 14]] +
324
- byteToHex[arr[offset + 15]]).toLowerCase();
325
- }
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
+ };
326
576
 
327
- let getRandomValues;
328
- const rnds8 = new Uint8Array(16);
329
- function rng() {
330
- if (!getRandomValues) {
331
- if (typeof crypto === 'undefined' || !crypto.getRandomValues) {
332
- throw new Error('crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported');
333
- }
334
- getRandomValues = crypto.getRandomValues.bind(crypto);
335
- }
336
- return getRandomValues(rnds8);
337
- }
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
+ };
338
587
 
339
- const randomUUID = typeof crypto !== 'undefined' && crypto.randomUUID && crypto.randomUUID.bind(crypto);
340
- var native = { randomUUID };
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
+ };
341
600
 
342
- function v4(options, buf, offset) {
343
- if (native.randomUUID && true && !options) {
344
- return native.randomUUID();
345
- }
346
- options = options || {};
347
- const rnds = options.random ?? options.rng?.() ?? rng();
348
- if (rnds.length < 16) {
349
- throw new Error('Random bytes length must be >= 16');
350
- }
351
- rnds[6] = (rnds[6] & 0x0f) | 0x40;
352
- rnds[8] = (rnds[8] & 0x3f) | 0x80;
353
- return unsafeStringify(rnds);
354
- }
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
+ }
611
+ };
355
612
 
356
- class CollecionesSemanticEvent extends CollecionesBaseEvent {
357
-
358
- constructor(entity, action, adjective, identifiers) {
359
- super();
360
- this.entity = underscoreToCamelCase(entity);
361
- this.action = underscoreToCamelCase(action);
362
- this.adjective = formatAdjectives(adjective);
363
- let adjectiveString = stringifyAdjectives(this.adjective);
364
- this.eventName = `${this.entity}${(adjectiveString !== undefined) ? adjectiveString : ''}__${this.action}`;
365
- this.convertParamIdentifiers(identifiers);
366
- this.data.entityIdentifiers = {};
367
- this.data.attributes = {};
368
- this.data.clientEventId = v4();
369
- this.data.meta = {
370
- eventFormat: 'CollecionesSemanticEvent',
371
- eventFormatVersion: '1'
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
+ }
372
627
  };
373
- }
374
628
 
375
- setData() {
376
- throw new Error('setData deprecated in semantic events');
377
- }
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
+ };
378
645
 
379
- addEntityIdentifier(key, value) {
380
- if (typeof key !== 'string') {
381
- throw new Error('Entity identifier key must be a string');
382
- }
383
- if (typeof value !== 'string' && typeof value !== 'number') {
384
- throw new Error('Entity identifier value must be a string or number');
385
- }
386
- this.data.entityIdentifiers[key] = value;
387
- }
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
+ };
388
658
 
389
- }
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
+ };
390
676
 
391
- class CollecionesSemanticCollectionEvent extends CollecionesSemanticEvent {
677
+ // Schema declaration: .conform.to('...')
678
+ conformingTo = (name) => {
679
+ eventInstance.setSchema(name);
680
+ return {
681
+ then: {track: {with: track}},
682
+ helpers
683
+ }
684
+ };
392
685
 
393
- constructor(itemEntity, action, adjective, identifiers, collectionEntity) {
394
- if(typeof collectionEntity == 'undefined') {
395
- collectionEntity = `${itemEntity}Collection`;
396
- }
397
- super(collectionEntity, action, undefined, identifiers);
398
- this.data.meta = {
399
- eventFormat: 'CollecionesSemanticCollectionEvent',
400
- eventFormatVersion: '1'
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
+ }
401
695
  };
402
- this.adjective = formatAdjectives(adjective);
403
- let adjectiveString = stringifyAdjectives(this.adjective);
404
- itemEntity = underscoreToCamelCase(itemEntity);
405
- this.data.itemEntity = itemEntity;
406
- this.data.itemEventName = `${itemEntity}${(adjectiveString !== undefined) ? adjectiveString : ''}__${this.action}`;
407
- this.data.entityItemIdentifiers = {};
408
- }
409
696
 
410
- addEntityItemIdentifier(key, value) {
411
- if (typeof key !== 'string') {
412
- throw new Error('Entity identifier key must be a string');
413
- }
414
- if (typeof value !== 'string' && typeof value !== 'number' && !Array.isArray(value)) {
415
- throw new Error('Entity identifier value must be a string or number');
416
- }
417
- if(typeof this.data.entityItemIdentifiers[key] == 'undefined') {
418
- this.data.entityItemIdentifiers[key] = [];
419
- }
420
- if(Array.isArray(value)) {
421
- value.forEach((v)=> {
422
- this.data.entityItemIdentifiers[key].push(v);
423
- });
424
- } else {
425
- this.data.entityItemIdentifiers[key].push(value);
697
+ return {
698
+ and: andEntity,
699
+ _: setEntityAfterAnd,
700
+ identified: {
701
+ by: setEntityIdentifiers
702
+ },
703
+ has: { been: setAction},
704
+ node: '_base',
705
+ helpers
426
706
  }
427
- }
428
707
 
429
-
430
- }
708
+ })(entity);
709
+ };
710
+
711
+ let collecionesDsl = {
712
+ the: init
713
+ };
431
714
 
432
- export { CollecionesEmitter, CollecionesEvent, CollecionesSemanticCollectionEvent, CollecionesSemanticEvent, CollecionesTracker, CollecionesWebTracker };
715
+ export { CollecionesEmitter, CollecionesEvent, CollecionesTracker, CollecionesWebTracker, collecionesDsl };
433
716
  //# sourceMappingURL=index.mjs.map