Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 14 additions & 13 deletions src/lib/ark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ const ARK_ID_LENGTH = 10;
export function computeNcdaCheckChar(name: string): string {
let total = 0;
for (let i = 0; i < name.length; i++) {
total += BETANUMERIC.indexOf(name[i]) * (i + 1);
total += BETANUMERIC.indexOf(name[i]!) * (i + 1);
}
return BETANUMERIC[total % BETANUMERIC.length];
return BETANUMERIC[total % BETANUMERIC.length]!;
}

// Converts a collection UUID to a 10-char betanumeric string.
Expand All @@ -30,12 +30,12 @@ export function collectionToArkId(collectionId: string): string {
const base = BigInt(BETANUMERIC.length);
const chars: string[] = [];
for (let i = 0; i < ARK_ID_LENGTH; i++) {
chars.unshift(BETANUMERIC[Number(n % base)]);
chars.unshift(BETANUMERIC[Number(n % base)]!);
n = n / base;
}
// Primordinal shoulder parsing requires collection IDs start with a consonant
if (!BETANUMERIC_CONSONANTS.includes(chars[0])) {
chars[0] = BETANUMERIC_CONSONANTS[hash[8] % BETANUMERIC_CONSONANTS.length];
if (!BETANUMERIC_CONSONANTS.includes(chars[0]!)) {
chars[0] = BETANUMERIC_CONSONANTS[hash[8]! % BETANUMERIC_CONSONANTS.length]!;
}
return chars.join("");
}
Expand All @@ -48,7 +48,7 @@ export function nextShoulderCounter(count: number): string {
let result = "";
while (n > 0) {
n -= 1;
result = BETANUMERIC_CONSONANTS[n % base] + result;
result = BETANUMERIC_CONSONANTS[n % base]! + result;
n = Math.floor(n / base);
}
return result;
Expand All @@ -63,10 +63,10 @@ export async function getOrMintShoulder(accountId: string): Promise<string> {
if (existing) return existing.shoulder;

for (let attempt = 0; attempt < 10; attempt++) {
const [{ count }] = await db
const [countRow] = await db
.select({ count: sql<number>`count(*)::int` })
.from(schema.arkShoulders);
const counter = nextShoulderCounter(count);
const counter = nextShoulderCounter(countRow?.count ?? 0);
const digit = Math.floor(Math.random() * 10).toString();
const shoulder = `ul${counter}${digit}`;
try {
Expand All @@ -92,14 +92,14 @@ export interface ArkComponents {
// Handles: shoulder+arkId, optional .vN version suffix, optional /recordType/recordId.
export function parseArkPath(pathAfterNaan: string): ArkComponents | null {
const parts = pathAfterNaan.split("/");
const firstSeg = parts[0];
const firstSeg = parts[0]!;

if (!firstSeg.startsWith("ul")) return null;

// Shoulder = "ul" + consonant counter + single digit
let i = 2;
while (i < firstSeg.length && BETANUMERIC_CONSONANTS.includes(firstSeg[i])) i++;
if (i >= firstSeg.length || !/^\d$/.test(firstSeg[i])) return null;
while (i < firstSeg.length && BETANUMERIC_CONSONANTS.includes(firstSeg[i]!)) i++;
if (i >= firstSeg.length || !/^\d$/.test(firstSeg[i]!)) return null;
const shoulder = firstSeg.slice(0, i + 1);
const remainder = firstSeg.slice(i + 1);

Expand All @@ -122,9 +122,10 @@ export function parseArkPath(pathAfterNaan: string): ArkComponents | null {
const checkChar = arkIdWithCheck.slice(-1);
if (computeNcdaCheckChar(collectionArkId) !== checkChar) return null;

const result: ArkComponents = { shoulder, collectionArkId, version };
const result: ArkComponents = { shoulder, collectionArkId };
if (version !== undefined) result.version = version;
if (parts.length >= 3) {
result.recordType = decodeURIComponent(parts[1]);
result.recordType = decodeURIComponent(parts[1]!);
result.recordId = parts.slice(2).map(decodeURIComponent).join("/");
}
return result;
Expand Down