Skip to content

fix: escape dots in key names during flatten/unflatten#3192

Closed
MaxwellCalkin wants to merge 1 commit intotriggerdotdev:mainfrom
MaxwellCalkin:fix/flatten-dot-escape-1510
Closed

fix: escape dots in key names during flatten/unflatten#3192
MaxwellCalkin wants to merge 1 commit intotriggerdotdev:mainfrom
MaxwellCalkin:fix/flatten-dot-escape-1510

Conversation

@MaxwellCalkin
Copy link

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

flattenAttributes uses . as the delimiter when joining key path segments, and unflattenAttributes uses .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 uses splitKey() instead of key.split("."), and calls unescapeKey() on each resulting segment.

Example

// Before (broken)
flattenAttributes({ "Key 0.002mm": 31.4 })
// => { "Key 0.002mm": 31.4 }  — unflatten sees "Key 0" + "002mm"

// After (fixed)
flattenAttributes({ "Key 0.002mm": 31.4 })
// => { "Key 0\.002mm": 31.4 }  — unflatten correctly produces { "Key 0.002mm": 31.4 }

Tests added

  • Keys containing periods (exact issue case: "Key 0.002mm")
  • Nested objects with dotted keys
  • Keys with multiple periods ("a.b.c")
  • Dotted keys mixed with normal nesting
  • Keys containing backslashes (escape-escape round-trip)
  • Dotted keys with array values
  • Unflatten with escaped dots and escaped backslashes

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
@changeset-bot
Copy link

changeset-bot bot commented Mar 8, 2026

⚠️ No Changeset found

Latest commit: 1d7fb14

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@github-actions
Copy link
Contributor

github-actions bot commented Mar 8, 2026

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.

@github-actions github-actions bot closed this Mar 8, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 8, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 56bfd57f-cd47-4dbc-823d-f6fe38fbea17

📥 Commits

Reviewing files that changed from the base of the PR and between e64b101 and 1d7fb14.

📒 Files selected for processing (2)
  • packages/core/src/v3/utils/flattenAttributes.ts
  • packages/core/test/flattenAttributes.test.ts

Walkthrough

This pull request modifies the flatten/unflatten attributes utility to handle keys containing dot characters by introducing an escaping mechanism. New utility functions (escapeKey, unescapeKey, splitKey) are added to preserve dots within keys during flattening and correctly parse escaped dots during unflattening. The changes affect how Maps and objects are flattened (escaping keys to prevent delimiter collisions) and how nested structures are reconstructed from flattened representations (parsing escaped keys and converting numeric array indices). Comprehensive test cases are added to validate the escaping behavior with various key patterns including dots, backslashes, and mixed nesting scenarios.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

View 2 additional findings in Devin Review.

Open in Devin Review

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚩 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.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +316 to +329
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));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚩 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.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[TRI-4123] Logging objects with keys with periods in doesn't render properly in the UI

1 participant