2ac1b6aa18
Extracts the per-key version grouping + same-version dedupe (org-owned shadows builtin) into lib/versionedLibrary.groupVersions and the semver patch bump into lib/semver.bumpPatch, both of which were duplicated byte-for-byte across the Skills and Agent-profiles pages. One source of truth so the two libraries can't drift. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
38 lines
1.3 KiB
TypeScript
38 lines
1.3 KiB
TypeScript
/**
|
|
* Shared grouping for the versioned libraries (skills and agent profiles). Both pages show one card
|
|
* per key with a version picker, and both must collapse a builtin that an org has forked at the same
|
|
* version — the org's own copy shadows the builtin (it's the one that resolves at run time and the
|
|
* one you can edit), keeping the picker unambiguous. Kept in one place so the two libraries can't drift.
|
|
*/
|
|
export interface VersionedItem {
|
|
version: string
|
|
origin: string
|
|
}
|
|
|
|
/** Group items by key, dedupe per version (org-owned shadows builtin), and sort keys alphabetically. */
|
|
export function groupVersions<T extends VersionedItem>(
|
|
items: T[],
|
|
keyOf: (item: T) => string,
|
|
): [string, T[]][] {
|
|
const byKey = new Map<string, T[]>()
|
|
for (const item of items) {
|
|
const key = keyOf(item)
|
|
const list = byKey.get(key) ?? []
|
|
list.push(item)
|
|
byKey.set(key, list)
|
|
}
|
|
|
|
for (const [key, list] of byKey) {
|
|
const perVersion = new Map<string, T>()
|
|
for (const item of list) {
|
|
const existing = perVersion.get(item.version)
|
|
if (!existing || (existing.origin === 'Builtin' && item.origin !== 'Builtin')) {
|
|
perVersion.set(item.version, item)
|
|
}
|
|
}
|
|
byKey.set(key, [...perVersion.values()])
|
|
}
|
|
|
|
return [...byKey.entries()].sort((a, b) => a[0].localeCompare(b[0]))
|
|
}
|