@uniweb/core 0.11.2 → 0.11.3

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/tracker.js +50 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniweb/core",
3
- "version": "0.11.2",
3
+ "version": "0.11.3",
4
4
  "description": "Core classes for the Uniweb platform - Uniweb, Website, Page, Block",
5
5
  "type": "module",
6
6
  "exports": {
package/src/tracker.js CHANGED
@@ -414,15 +414,64 @@ export default class Tracker {
414
414
  * Report a page view. Framework-owned: it carries the acquisition context and
415
415
  * dedupes consecutive reports of the same path.
416
416
  *
417
+ * ⭐ **`first_of_load` marks the one view that opened this document**, and it
418
+ * is computed here rather than living in `acquisition` — precisely because
419
+ * everything in `acquisition` is *replayed* onto every view, and this must
420
+ * not be. Exactly one emission per `Tracker` can carry it: `currentPath` is
421
+ * `null` only before the first report and is never reset.
422
+ *
423
+ * ⛔ **Why it exists at all, since a consumer holding the events can derive
424
+ * it from `visit`.** Not every consumer holds the events. A counter store
425
+ * that keeps no per-event rows cannot ask *"was this the first `page_view`
426
+ * with this `visit`?"* without remembering which `visit` values it has seen —
427
+ * which is retaining `visit`, arrived at by the back door. This bit makes the
428
+ * document-scoped question answerable with **no collector state at all**, and
429
+ * a store that deliberately retains nothing is a supported consumer, not an
430
+ * unusual one.
431
+ *
432
+ * ⛔ **The name carries the unit on purpose.** Bare `first` reads as *the
433
+ * visitor's first ever view*, which is a claim this file refuses to make and
434
+ * could not make — nothing here survives the document. The unit is **one
435
+ * document load**, so a panel built on it counts loads, never people and
436
+ * never sessions. *(`lane-chain.md` §4a: when a misread name is paid by
437
+ * another lane, put the constraint in the name.)*
438
+ *
439
+ * ⛔ **IT MUST NEVER SHIP IN A RELEASE THAT LACKS `continues`, and that is a
440
+ * standing contract rather than an accident of ordering.** A consumer uses
441
+ * its *presence* as proof that this emitter is new enough to have sent
442
+ * `continues` had the referrer been same-origin — which is what lets an
443
+ * entry-page metric drop the "runtime too old to say" case entirely instead
444
+ * of caveating it. Backport `first_of_load` to a line without `continues`
445
+ * and every such consumer starts counting continuations as arrivals, with
446
+ * nothing anywhere reporting an error. *(The dependency is one-way:
447
+ * `continues` without `first_of_load` is fine and shipped that way.)*
448
+ *
449
+ * ⚖️ **It states a FACT, not a metric** — the same discipline as `continues`.
450
+ * Whether a first-of-load view is an "entry page" also depends on
451
+ * `continues`, and that combination is the consumer's to make; a field named
452
+ * `entry` would age badly the moment a second metric wanted this bit.
453
+ *
417
454
  * @param {string} path
418
455
  */
419
456
  trackPageView(path) {
420
457
  if (!this.arms('page_view') || !path) return
421
458
  if (path === this.currentPath) return
459
+ const firstOfLoad = this.currentPath === null
422
460
  this.currentPath = path
423
461
  // Promptly, rather than waiting out the batch window: a page view is the
424
462
  // event most likely to be the only one of a short visit.
425
- this.enqueue({ event: 'page_view', path, ...(this.acquisition || {}) }, true)
463
+ //
464
+ // Emitted only when true, like `continues` — absent is the negative, and it
465
+ // keeps every later view the size it already was.
466
+ this.enqueue(
467
+ {
468
+ event: 'page_view',
469
+ path,
470
+ ...(this.acquisition || {}),
471
+ ...(firstOfLoad ? { first_of_load: true } : {})
472
+ },
473
+ true
474
+ )
426
475
  }
427
476
 
428
477
  /**