* Build a gbrain-valid source id (1-32 lowercase alnum + interior hyphens). Sanitizes * `raw`, prefixes with `prefix`, and falls back to a hashed-tail form when total length * would exceed 32 chars. * * Truncation cuts on hyphen boundaries (whole-word units) from the right, never * mid-word. In
(prefix: string, raw: string)
| 573 | * "${prefix}-270c0001-<hash>", not "${prefix}-kill-270c0001-<hash>". |
| 574 | */ |
| 575 | function constrainSourceId(prefix: string, raw: string): string { |
| 576 | const MAX = 32; |
| 577 | const slug = raw.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, ""); |
| 578 | // Empty slug after sanitize (e.g. raw was all non-alnum like "___") would |
| 579 | // produce "${prefix}-" which fails gbrain's validator on the trailing |
| 580 | // hyphen. Fall back to a deterministic hash of the original input so the |
| 581 | // result is stable across runs of the same repo. |
| 582 | if (!slug) { |
| 583 | const hash = createHash("sha1").update(raw || "_empty").digest("hex").slice(0, 6); |
| 584 | return `${prefix}-${hash}`; |
| 585 | } |
| 586 | const full = `${prefix}-${slug}`; |
| 587 | if (full.length <= MAX) return full; |
| 588 | const hash = createHash("sha1").update(slug).digest("hex").slice(0, 6); |
| 589 | // Total budget: prefix + "-" + tail + "-" + hash |
| 590 | const tailBudget = MAX - prefix.length - 2 - hash.length; |
| 591 | if (tailBudget < 1) return `${prefix}-${hash}`; |
| 592 | // Cut on hyphen boundaries instead of mid-word. Walk tokens from the right, |
| 593 | // accumulating until adding the next token would exceed tailBudget. This |
| 594 | // preserves readable suffixes (pathhash, repo name) and avoids embarrassing |
| 595 | // mid-word artifacts like "skill" → "kill". |
| 596 | const tokens = slug.split("-").filter(Boolean); |
| 597 | const kept: string[] = []; |
| 598 | let len = 0; |
| 599 | for (let i = tokens.length - 1; i >= 0; i--) { |
| 600 | const add = kept.length === 0 ? tokens[i].length : tokens[i].length + 1; |
| 601 | if (len + add > tailBudget) break; |
| 602 | kept.unshift(tokens[i]); |
| 603 | len += add; |
| 604 | } |
| 605 | const tail = kept.join("-"); |
| 606 | return tail ? `${prefix}-${tail}-${hash}` : `${prefix}-${hash}`; |
| 607 | } |
| 608 | |
| 609 | // ── Lock file (D1) ───────────────────────────────────────────────────────── |
| 610 |
no outgoing calls
no test coverage detected