fix: escape dots in key names during flatten/unflatten#3192
fix: escape dots in key names during flatten/unflatten#3192MaxwellCalkin wants to merge 1 commit intotriggerdotdev:mainfrom
Conversation
Keys containing periods (e.g., "Key 0.002mm") were incorrectly split into nested structures during flattening. Now dots within key names are escaped as \. before joining with the . delimiter, and unescaped after splitting during unflattening. Fixes triggerdotdev#1510
|
|
Hi @MaxwellCalkin, thanks for your interest in contributing! This project requires that pull request authors are vouched, and you are not in the list of vouched users. This PR will be closed automatically. See https://github.com/triggerdotdev/trigger.dev/blob/main/CONTRIBUTING.md for more details. |
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
WalkthroughThis pull request modifies the flatten/unflatten attributes utility to handle keys containing dot characters by introducing an escaping mechanism. New utility functions ( Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🚩 Missing changeset for public package change
This PR modifies packages/core which is the published @trigger.dev/core package. Per CONTRIBUTING.md and CLAUDE.md, changes to packages in packages/* require a changeset (pnpm run changeset:add). The diff only shows changes to packages/core/src/v3/utils/flattenAttributes.ts and packages/core/test/flattenAttributes.test.ts with no changeset file visible. This is a process requirement rather than a code bug.
Was this helpful? React with 👍 or 👎 to provide feedback.
| const parts = splitKey(key).reduce( | ||
| (acc, part) => { | ||
| if (part.startsWith("[") && part.endsWith("]")) { | ||
| // Handle array indices more precisely | ||
| const match = part.match(/^\[(\d+)\]$/); | ||
| if (match && match[1]) { | ||
| acc.push(parseInt(match[1])); | ||
| const inner = part.slice(1, -1); | ||
| const match = inner.match(/^\d+$/); | ||
| if (match) { | ||
| acc.push(parseInt(inner)); | ||
| } else { | ||
| // Remove brackets for non-numeric array keys | ||
| acc.push(part.slice(1, -1)); | ||
| acc.push(unescapeKey(inner)); | ||
| } | ||
| } else { | ||
| acc.push(part); | ||
| acc.push(unescapeKey(part)); |
There was a problem hiding this comment.
🚩 Backward compatibility preserved for existing stored data
The change from key.split(".") to splitKey(key) in unflattenAttributes is backward-compatible: old flattened data (stored in PostgreSQL/ClickHouse via eventRepository.server.ts) never contains backslashes in keys, so splitKey behaves identically to .split(".") and unescapeKey is a no-op for those keys. However, the forward direction is a breaking format change: new flattenAttributes output (e.g., "Key 0\\.002mm") will be misinterpreted by old unflattenAttributes code that uses .split("."). In this monorepo, flattenAttributes runs both server-side (apps/webapp) and client-side (SDK in user containers via packages/core). If the SDK is upgraded but the server is not (or vice versa), escaped keys produced by the new code will be incorrectly split by the old code. This is acceptable if both are always deployed together, but worth noting for SDK consumers who may upgrade independently.
Was this helpful? React with 👍 or 👎 to provide feedback.
Note: This PR was authored by Claude (AI), operated by @MaxwellCalkin.
Summary
Fixes #1510
When logging objects with keys that contain periods (e.g.,
"Key 0.002mm"), the flattening code splits on.and creates a nested structure like{"Key 0": {"002mm": 31.4}}instead of preserving the original key.Root cause
flattenAttributesuses.as the delimiter when joining key path segments, andunflattenAttributesuses.split(".")to reconstruct the path. Neither function accounts for dots that appear within key names.Fix
Three helper functions are added:
escapeKey(key)— escapes backslashes and dots in key names before they are joined into a flattened path (\→\,.→\.)unescapeKey(key)— reverses the escaping after splitting (\.→.,\→\)splitKey(key)— splits a flattened path on unescaped dots only (respects\.escape sequences)The flattener now calls
escapeKey()on each object key (and Map key) before building the dotted path. The unflattener now usessplitKey()instead ofkey.split("."), and callsunescapeKey()on each resulting segment.Example
Tests added
"Key 0.002mm")"a.b.c")