-
Notifications
You must be signed in to change notification settings - Fork 0
refactor: use the SDK's id validator instead of a local base58 check #67
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,10 @@ | ||
| import { type Channel, type Group, parseCommsURL, type Workspace } from '@doist/comms-sdk' | ||
| import { | ||
| type Channel, | ||
| type Group, | ||
| isValidUuidV7Base58, | ||
| parseCommsURL, | ||
| type Workspace, | ||
| } from '@doist/comms-sdk' | ||
| import { fetchWorkspaces, getGroup, getWorkspaceGroups, getCommsClient } from './api.js' | ||
| import { CliError, type ErrorCode, isCliErrorCode } from './errors.js' | ||
|
|
||
|
|
@@ -72,29 +78,8 @@ export function looksLikeRawId(ref: string): boolean { | |
| return /\d/.test(normalized) | ||
| } | ||
|
|
||
| export const BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' | ||
|
|
||
| /** | ||
| * Comms entity ids are 16 bytes, base58-encoded. About 3% of them carry no | ||
| * digit, so `looksLikeRawId` misses them; decoding is the only check that | ||
| * also keeps a long single-word name a name. Ids are timestamp-led, so they | ||
| * encode to 21 characters today and 22 at most (58^22 > 2^128). | ||
| */ | ||
| export function looksLikeOpaqueCommsId(ref: string): boolean { | ||
| if (ref.length < 21 || ref.length > 22) return false | ||
| let value = 0n | ||
| for (const char of ref) { | ||
| const digit = BASE58_ALPHABET.indexOf(char) | ||
| if (digit === -1) return false | ||
| value = value * 58n + BigInt(digit) | ||
| } | ||
| const leadingZeroBytes = ref.length - ref.replace(/^1+/, '').length | ||
| const byteLength = value === 0n ? 0 : Math.ceil(value.toString(16).length / 2) | ||
| return leadingZeroBytes + byteLength === 16 | ||
| } | ||
|
|
||
| function getOpaqueNameId(parsed: ParsedRef): string | null { | ||
| return parsed.type === 'name' && looksLikeOpaqueCommsId(parsed.name) ? parsed.name : null | ||
| return parsed.type === 'name' && isValidUuidV7Base58(parsed.name) ? parsed.name : null | ||
| } | ||
|
|
||
| export interface ParsedCommsUrl { | ||
|
|
@@ -322,33 +307,12 @@ export async function resolveChannelRef(ref: string, workspaceId: number): Promi | |
| ...joined, | ||
| ...publicChannels.filter((channel) => !joinedIds.has(channel.id)), | ||
| ] | ||
| try { | ||
| return matchByName(channels, parsed.name, { | ||
| ambiguousCode: 'AMBIGUOUS_CHANNEL', | ||
| notFoundCode: 'CHANNEL_NOT_FOUND', | ||
| ref, | ||
| listHint: 'Run: tdc channels to list available channels', | ||
| }) | ||
| } catch (error) { | ||
| if ( | ||
| !isCliErrorCode(error, 'CHANNEL_NOT_FOUND') || | ||
| !looksLikeOpaqueCommsId(parsed.name) | ||
| ) { | ||
| throw error | ||
| } | ||
| // Nothing by that name, and the token decodes to a Comms id: a bare | ||
| // digit-free channel id lands here rather than in `getDirectChannelId`. | ||
| try { | ||
| const channel = await client.channels.getChannel(parsed.name) | ||
| assertChannelInWorkspace(channel, workspaceId) | ||
| return channel | ||
| } catch (idError) { | ||
| // A miss (404) or a token the server will not take as an id | ||
| // (409, "must be UUIDv7") both mean it was a name after all. | ||
| if (isCliErrorCode(idError, 'NOT_FOUND', 'INVALID_REF')) throw error | ||
| throw idError | ||
| } | ||
| } | ||
| return matchByName(channels, parsed.name, { | ||
| ambiguousCode: 'AMBIGUOUS_CHANNEL', | ||
| notFoundCode: 'CHANNEL_NOT_FOUND', | ||
| ref, | ||
| listHint: 'Run: tdc channels to list available channels', | ||
| }) | ||
| } | ||
|
|
||
| throw new CliError('CHANNEL_NOT_FOUND', `Channel "${ref}" not found`, [ | ||
|
|
@@ -388,9 +352,7 @@ export function getDirectChannelId(ref: string): string | null { | |
| ) | ||
| } | ||
|
|
||
| // A bare digit-free token could be a channel name, so it goes to name | ||
| // lookup; `resolveChannelRef` tries it as an id only when no name matches. | ||
| return null | ||
| return getOpaqueNameId(parsed) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| } | ||
|
|
||
| export function resolveCommentId(ref: string): string { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolveChannelRefdirectly (channel threads,channel members list/add/remove/set, andchannel updatewith--workspace). A valid v7 id without a digit (e.g.CbjxNkWHJBwcaVkoTCRgM) is parsed as a name, so it now fails withCHANNEL_NOT_FOUNDinstead of resolving viaclient.channels.getChannel. OnlygetDirectChannelId/resolveChannelIdgained the SDK check; these callers did not. Restore the fallback inresolveChannelRefusingisValidUuidV7Base58(parsed.name), or route these commands throughgetDirectChannelIdfirst.