Skip to content

refactor(utils): add slugify and adopt it at the eight sites that hand-rolled it - #7018

Merged
waleedlatif1 merged 1 commit into
stagingfrom
deslop-utils
Aug 24, 2026
Merged

refactor(utils): add slugify and adopt it at the eight sites that hand-rolled it#7018
waleedlatif1 merged 1 commit into
stagingfrom
deslop-utils

Conversation

@waleedlatif1

Copy link
Copy Markdown
Collaborator

Stacked on #7016 (which is stacked on #7015). Retarget to staging once those merge.

What

The same three-step derivation — lowercase, collapse each non-alphanumeric run to a hyphen, strip the leading and trailing one — sat in eight files.

Two of them carried a comment whose only job was to warn that they mirrored a third:

  • instance-org.ts: "Derives a slug the same way the admin organization API does."
  • consolidate-users-into-organization.ts: "Mirrors the slug derivation used by POST /api/v1/admin/organizations."

A comment asserting that two implementations agree is what duplication looks like when nothing can check it.

Verified identical, not merely similar

All eight produce the same output. Two anchored the strip with -+ rather than -, and one followed it with a --+ collapse — but [^a-z0-9]+ has already collapsed every run by that point, so at most one leading and one trailing hyphen can exist and neither variant can ever match more than the single-hyphen form. .trim() in the skills helper is likewise a no-op: leading whitespace becomes a hyphen that is then stripped.

What stayed at the call sites

Truncation. Four sites bound the result — at 24, 64 and 80 — and only copy-chats.ts strips again afterwards, because slicing can land mid-run and leave a trailing hyphen the earlier strip never saw. A maxLength option would have had to pick one of those behaviors and impose it on the others, so the helper does the derivation and callers keep the bounding.

Fallbacks. Three sites need a non-empty result ('organization', 'chat', 'section'). What to fall back to is domain knowledge, so it stays with the caller.

One site deliberately not migrated

artifact-stylesheet.ts:687 keeps its copy. It lives inside the SIM_ARTIFACT_SHELL template literal and runs in the viewer's browser, where there is no import to resolve — migrating it would emit slugify is not defined into every generated artifact page.

Also considered and rejected

getFileExtension is byte-identical to a private extractExtension in the same directory. Deduping those three lines would cost a 32-file import churn, an import cycle, or a re-export from a non-barrel file (which CLAUDE.md bans). Left alone.

Testing

  • slugify has direct tests covering the empty, all-punctuation, non-Latin, and already-hyphenated cases.
  • 170 tests passing in packages/utils; 1361 across the touched apps/sim areas.
  • bun run type-check clean.

@vercel

vercel Bot commented Aug 23, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs Ready Ready Preview Aug 24, 2026 1:47am

Request Review

@cursor

cursor Bot commented Aug 23, 2026

Copy link
Copy Markdown

PR Summary

Low Risk
Pure string-helper extraction with no auth or data-path changes. Residual risk is a slug mismatch if any site’s old regex was not actually equivalent.

Overview
Adds a shared ASCII slugify helper in @sim/utils/string and switches the eight call sites that had been inlining the same lowercase / hyphenate / strip logic.

Callers still own truncation and empty-string fallbacks (org slugs, chat identifiers, skill names). Behavior is intended to stay identical; tests cover empty, punctuation-only, non-Latin, and already-hyphenated inputs.

Reviewed by Cursor Bugbot for commit 50594b8. Configure here.

@greptile-apps

greptile-apps Bot commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR centralizes eight equivalent slug derivations in a new @sim/utils/string helper while retaining caller-specific truncation and fallback behavior.

  • Adds and directly tests the ASCII-only slugify utility.
  • Replaces duplicated implementations across organization, billing, chat-copying, model, skill, and maintenance-script paths.
  • Preserves post-truncation cleanup where chat identifiers require it.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
packages/utils/src/string.ts Adds a documented shared slug helper whose transformation matches the migrated implementations.
packages/utils/src/string.test.ts Covers lowercasing, separator collapsing, boundary stripping, empty inputs, non-ASCII input, digits, and existing hyphens.
apps/sim/app/api/v1/admin/organizations/route.ts Replaces the local organization-slug derivation while preserving requested-slug precedence.
apps/sim/ee/workspace-forking/lib/copy/copy-chats.ts Adopts the shared derivation while retaining the 24-character limit, trailing-hyphen cleanup, and fallback.
apps/sim/lib/billing/enterprise-owner-claim.ts Adopts the shared helper while preserving truncation, fallback, and claim-specific suffix behavior.
apps/sim/lib/billing/enterprise-provisioning.ts Adopts the shared helper while preserving truncation, fallback, and organization-ID suffix behavior.
apps/sim/lib/organizations/instance-org.ts Reuses the canonical derivation while retaining explicit handling for unusable empty slugs.
apps/sim/scripts/consolidate-users-into-organization.ts Uses the same shared derivation consistently for organization lookup and creation.

Reviews (2): Last reviewed commit: "refactor(utils): add slugify and adopt i..." | Re-trigger Greptile

…d-rolled it

The same three-step derivation — lowercase, collapse each non-alphanumeric run
to a hyphen, strip the leading and trailing one — sat in eight files. Two of
them carried a TSDoc line whose only job was to warn that they mirrored a third
(`instance-org.ts`: "Derives a slug the same way the admin organization API
does"; `consolidate-users-into-organization.ts`: "Mirrors the slug derivation
used by POST /api/v1/admin/organizations"). A comment asserting two
implementations agree is the shape duplication takes when it cannot be checked.

All eight were semantically identical. Two anchored the strip with `-+` rather
than `-`, and one followed it with a `--+` collapse, but `[^a-z0-9]+` has
already collapsed every run by that point, so neither could ever match more than
the single-hyphen form. Nothing changes.

Truncation stays at the call sites. Four of them bound the result — at 24, 64
and 80 — and only `copy-chats.ts` strips again afterwards, because slicing can
land mid-run and leave a trailing hyphen the earlier strip never saw. Folding a
`maxLength` into the helper would have had to pick one of those behaviors and
silently impose it on the others.

`artifact-stylesheet.ts` keeps its copy: it lives inside the `SIM_ARTIFACT_SHELL`
template literal and runs in the viewer's browser, where there is no import to
resolve.
@waleedlatif1
waleedlatif1 changed the base branch from deslop-mechanical to staging August 24, 2026 01:42
@waleedlatif1 waleedlatif1 reopened this Aug 24, 2026
@waleedlatif1
waleedlatif1 merged commit cc08749 into staging Aug 24, 2026
31 checks passed
@waleedlatif1
waleedlatif1 deleted the deslop-utils branch August 24, 2026 02:01
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.

1 participant