@@ -2,6 +2,7 @@ import { sql, type Kysely, type Selectable } from "kysely";
22import { ulid } from "ulidx" ;
33
44import { invalidateTaxonomyObjectCache } from "../../object-cache/index.js" ;
5+ import { slugify } from "../../utils/slugify.js" ;
56import { withTransaction } from "../transaction.js" ;
67import type { Database , TaxonomyTable } from "../types.js" ;
78import { validateIdentifier } from "../validate.js" ;
@@ -18,6 +19,7 @@ export interface SiblingPosition {
1819 * statement inside D1's 100-parameter ceiling.
1920 */
2021const GROUPS_PER_UPDATE = 32 ;
22+ const NUMERIC_SUFFIX_PATTERN = / ^ \d + $ / ;
2123
2224/** Deal the listed groups back out over the slots they hold, in the order given. */
2325function permuteWithinSlots (
@@ -216,6 +218,29 @@ export class TaxonomyRepository {
216218 return row ? this . rowToTaxonomy ( row ) : null ;
217219 }
218220
221+ /** Generate a locale-scoped term slug, adding a numeric suffix when needed. */
222+ async generateUniqueSlug ( name : string , text : string , locale ?: string ) : Promise < string > {
223+ const baseSlug = slugify ( text ) ;
224+ let query = this . db
225+ . selectFrom ( "taxonomies" )
226+ . select ( "slug" )
227+ . where ( "name" , "=" , name )
228+ . where ( ( eb ) => eb . or ( [ eb ( "slug" , "=" , baseSlug ) , eb ( "slug" , "like" , `${ baseSlug } -%` ) ] ) ) ;
229+ if ( locale !== undefined ) query = query . where ( "locale" , "=" , locale ) ;
230+ const candidates = await query . execute ( ) ;
231+ if ( ! candidates . some ( ( candidate ) => candidate . slug === baseSlug ) ) return baseSlug ;
232+
233+ let maxSuffix = 0 ;
234+ const prefix = `${ baseSlug } -` ;
235+ for ( const candidate of candidates ) {
236+ if ( ! candidate . slug . startsWith ( prefix ) ) continue ;
237+ const suffix = candidate . slug . slice ( prefix . length ) ;
238+ if ( ! NUMERIC_SUFFIX_PATTERN . test ( suffix ) ) continue ;
239+ maxSuffix = Math . max ( maxSuffix , Number . parseInt ( suffix , 10 ) ) ;
240+ }
241+ return `${ baseSlug } -${ maxSuffix + 1 } ` ;
242+ }
243+
219244 /**
220245 * Get all terms for a taxonomy (e.g., all categories).
221246 *
0 commit comments