Skip to content

Add public setUniqueIdGenerator for custom ID generation#7

Merged
kaznaan merged 10 commits intoluma/v4.4.xfrom
theop/uniqueId-v2
Feb 27, 2026
Merged

Add public setUniqueIdGenerator for custom ID generation#7
kaznaan merged 10 commits intoluma/v4.4.xfrom
theop/uniqueId-v2

Conversation

@theop-luma
Copy link
Copy Markdown
Collaborator

Summary

  • Adds setUniqueIdGenerator as a public export from @tldraw/utils, allowing apps to customize how IDs are generated across tldraw (shapes, pages, records)
  • Adds @tldraw/utils to the pack-and-copy.sh script so the tarball is available for downstream apps
  • Re-exported via @tldraw/editor's export * from '@tldraw/utils'

Usage

import { setUniqueIdGenerator } from '@tldraw/editor'

setUniqueIdGenerator(() => `id-${Date.now()}-${Math.random()}`)

Note for vespa-web

After merging, add this override to frontend/vespa-web/package.json:

"@tldraw/utils": "file:../../shared/js/tldraw/tldraw-utils-4.4.0.tgz"

🤖 Generated with Claude Code

kaznaan and others added 6 commits February 27, 2026 11:11
Allows apps to override the unique ID generator used across tldraw,
enabling alignment of ID generation between shapes and artifacts.
Already re-exported via @tldraw/editor's `export * from '@tldraw/utils'`.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Include @tldraw/utils in the tarball packaging so that custom exports
like setUniqueIdGenerator are available in downstream apps.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@cursor
Copy link
Copy Markdown

cursor Bot commented Feb 27, 2026

PR Summary

Medium Risk
Exposes a new global override for ID generation; misuse could lead to collisions or non-deterministic behavior across persisted records, but the change is small and localized.

Overview
Adds a new public setUniqueIdGenerator API in @tldraw/utils to allow apps to override the function used by uniqueId() at runtime.

Updates the utils public surface (API report + packages/utils/src/index.ts export) and adjusts scripts/pack-and-copy.sh to include the utils tarball in downstream packaging workflows.

Written by Cursor Bugbot for commit 544f5ec. This will update automatically on new commits. Configure here.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Bugbot Autofix prepared fixes for both issues found in the latest run.

  • ✅ Fixed: restoreUniqueId silently discards custom generator set via public API
    • I introduced a persistent configured generator baseline and changed restoreUniqueId to restore to that baseline instead of always restoring to nanoid.
  • ✅ Fixed: New setUniqueIdGenerator duplicates existing mockUniqueId implementation
    • I removed the duplicated assignment logic by introducing a shared internal setter used by both setUniqueIdGenerator and mockUniqueId.

Create PR

Or push these changes by commenting:

@cursor push 4883ac7764
Preview (4883ac7764)
diff --git a/packages/utils/src/lib/id.ts b/packages/utils/src/lib/id.ts
--- a/packages/utils/src/lib/id.ts
+++ b/packages/utils/src/lib/id.ts
@@ -54,8 +54,13 @@
 	return id
 }
 
-let impl = nanoid
+let configuredImpl = nanoid
+let impl = configuredImpl
 
+function setImpl(fn: (size?: number) => string) {
+	impl = fn
+}
+
 /**
  * Override the unique ID generator with a custom implementation.
  *
@@ -72,7 +77,8 @@
  * @public
  */
 export function setUniqueIdGenerator(fn: (size?: number) => string) {
-	impl = fn
+	configuredImpl = fn
+	setImpl(fn)
 }
 
 /**
@@ -95,13 +101,13 @@
  * @internal
  */
 export function mockUniqueId(fn: (size?: number) => string) {
-	impl = fn
+	setImpl(fn)
 }
 
 /**
- * Restore the original unique ID generator after mocking.
+ * Restore the configured unique ID generator after mocking.
  *
- * Resets the ID generation function back to the original nanoid implementation.
+ * Resets the ID generation function back to the currently configured implementation.
  * This should be called after testing to restore normal ID generation behavior.
  *
  * @example
@@ -116,7 +122,7 @@
  * @internal
  */
 export function restoreUniqueId() {
-	impl = nanoid
+	setImpl(configuredImpl)
 }
 
 /**

*/
export function setUniqueIdGenerator(fn: (size?: number) => string) {
impl = fn
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

restoreUniqueId silently discards custom generator set via public API

Medium Severity

setUniqueIdGenerator sets impl to a custom function, but restoreUniqueId always resets impl back to the hard-coded nanoid, not to whatever was set via setUniqueIdGenerator. If a downstream app configures a custom generator and any code later calls restoreUniqueId, the custom generator is silently lost. restoreUniqueId needs to be aware of the user-configured baseline to restore to the right implementation.

Additional Locations (1)

Fix in Cursor Fix in Web

Comment thread packages/utils/src/lib/id.ts
@kaznaan kaznaan merged commit 193c6cb into luma/v4.4.x Feb 27, 2026
4 of 8 checks passed
@kaznaan kaznaan mentioned this pull request May 8, 2026
3 tasks
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.

2 participants