1import { activeAppId } from "../runtime/config.ts";2import { getRuntime } from "../runtime/runtime.ts";3import { AccessError } from "./errors.ts";45/** App-scoped, non-secret Jazz principal identifier safe to copy between users. */6export type SharingIdentity = string & { readonly __lofiSharingIdentity: true };78const prefix = "lofi1";910/**11 * Encodes a raw Jazz principal as a versioned identity scoped to the active12 * sync location's app. Identities are exchanged between users of the same13 * store, so they carry the store's app id — the declared sink's when one is14 * enrolled, the compiled managed app's otherwise.15 */16export function encodeSharingIdentity(userId: string): SharingIdentity {17 if (!userId || /[\s:]/.test(userId)) {18 throw new AccessError("invalid-identity", "Jazz returned an invalid sharing identity.");19 }20 return `${prefix}:${activeAppId()}:${userId}` as SharingIdentity;21}2223/**24 * Validates an app-scoped sharing identity and returns its raw Jazz principal.25 * Surrounding whitespace from copy/paste is tolerated; whitespace inside the26 * principal is rejected — a grant for a padded principal would look active to27 * the owner while never matching the recipient's actual account.28 */29export function decodeSharingIdentity(identity: string): string {30 const [version, identityAppId, userId, extra] = identity.trim().split(":");31 if (32 version !== prefix || identityAppId !== activeAppId() || !userId || /\s/.test(userId) ||33 extra !== undefined34 ) {35 throw new AccessError(36 "invalid-identity",37 "That sharing identity belongs to another app or is malformed. Ask the recipient to copy it again from this app.",38 );39 }40 return userId;41}4243/** Returns the non-secret, app-scoped identity users may copy for shares. */44export async function sharingIdentity(): Promise<SharingIdentity> {45 const runtime = await getRuntime();46 const userId = runtime.db.getAuthState().session?.user_id;47 if (!userId) {48 throw new AccessError(49 "invalid-identity",50 "The current Jazz principal is not ready. Wait for account startup and try again.",51 );52 }53 return encodeSharingIdentity(userId);54}