ga4-export-fixer 0.9.0-dev.12 → 0.9.0-dev.14

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.
@@ -235,9 +235,11 @@ const itemListAttributionExpr = (lookbackType, timestampColumn, lookbackTimeMs)
235
235
  frameBounds = `range between ${lookbackMicros} preceding and current row`;
236
236
  }
237
237
 
238
- // Refund events occur outside the selection-driven journey window — propagating
239
- // item_list_* attribution into them would misattribute. Suppress attribution on refund rows.
240
- return `if(event_name = 'refund', null, last_value(
238
+ // Suppress attribution for:
239
+ // - refund events (outside the selection-driven journey window)
240
+ // - unconsented events (user_pseudo_id is NULL) attribution requires a visitor
241
+ // identity to stitch select_* events to later receivers within the same visitor.
242
+ return `if(event_name = 'refund' or user_pseudo_id is null, null, last_value(
241
243
  if(${selectEvents}, ${structExpr}, null) ignore nulls
242
244
  ) over(
243
245
  partition by ${partitionBy}
@@ -260,6 +262,10 @@ const itemListAttributionExpr = (lookbackType, timestampColumn, lookbackTimeMs)
260
262
  * the rows are interchangeable, so arbitrary row number assignment between them
261
263
  * produces the same result.
262
264
  *
265
+ * Unconsented events (user_pseudo_id is NULL) use an empty-string sentinel inside
266
+ * concat — without it, CONCAT NULL-propagates and the row_id becomes NULL, which
267
+ * would prevent enrichments from applying to such events.
268
+ *
263
269
  * @param {string} ecommerceEventsFilter - Comma-separated, quoted list of event names
264
270
  * (e.g., "'purchase', 'add_to_cart'").
265
271
  * @returns {string} SQL expression that evaluates to the row id or NULL.
@@ -268,7 +274,7 @@ const itemRowId = (ecommerceEventsFilter) => {
268
274
  return `if(
269
275
  event_name in (${ecommerceEventsFilter}),
270
276
  farm_fingerprint(concat(
271
- user_pseudo_id,
277
+ ifnull(user_pseudo_id, ''),
272
278
  cast(event_timestamp as string),
273
279
  event_name,
274
280
  to_json_string(items),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ga4-export-fixer",
3
- "version": "0.9.0-dev.12",
3
+ "version": "0.9.0-dev.14",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -357,14 +357,17 @@ ${excludedEventsSQL}`,
357
357
  // items_unnested. When itemListAttribution is configured, override the three
358
358
  // attribution entries with their package-generated coalesce-with-passthrough
359
359
  // expressions. Item-level enrichment columns layer on top via the spread below.
360
+ // References are qualified with `items_unnested.` so that overlapping item-level
361
+ // enrichments (which JOIN against enrich_<name> CTEs that may share column names)
362
+ // do not produce ambiguous bare-column references.
360
363
  const preItemExpressions = {};
361
364
  for (const f of helpers.ga4ItemStructFields) {
362
- preItemExpressions[f] = f;
365
+ preItemExpressions[f] = `items_unnested.${f}`;
363
366
  }
364
367
  if (itemListAttribution) {
365
- preItemExpressions.item_list_name = `coalesce(if(${passthroughEvents}, item_list_name, _item_list_attr.item_list_name), '(not set)')`;
366
- preItemExpressions.item_list_id = `coalesce(if(${passthroughEvents}, item_list_id, _item_list_attr.item_list_id), '(not set)')`;
367
- preItemExpressions.item_list_index = `coalesce(if(${passthroughEvents}, item_list_index, _item_list_attr.item_list_index))`;
368
+ preItemExpressions.item_list_name = `coalesce(if(${passthroughEvents}, items_unnested.item_list_name, _item_list_attr.item_list_name), '(not set)')`;
369
+ preItemExpressions.item_list_id = `coalesce(if(${passthroughEvents}, items_unnested.item_list_id, _item_list_attr.item_list_id), '(not set)')`;
370
+ preItemExpressions.item_list_index = `coalesce(if(${passthroughEvents}, items_unnested.item_list_index, _item_list_attr.item_list_index))`;
368
371
  }
369
372
 
370
373
  // Wrap overlapping item-level enrichment columns in coalesce(<enrichExpr>, <originalExpr>)