/** * ParticlePath is a branded string type representing a URL-style path * to a particle in the hierarchy: /{networkId}/{segment1}/{segment2}/... * * Using a branded type prevents accidentally passing raw strings where * a validated particle path is expected. */ declare const __brand: unique symbol; export type ParticlePath = string & { readonly [__brand]: true }; /** * Construct a ParticlePath from a network ID and optional particle segments. * * @example * particlePath("net1", []) // => "/net1" * particlePath("net1", ["p1"]) // => "/net1/p1" * particlePath("net1", ["p1","p2"])// => "/net1/p1/p2" */ export function particlePath(networkId: string, segments: string[] = []): ParticlePath { return `/${[networkId, ...segments].join("/")}` as ParticlePath; } /** * Parse a ParticlePath back into its network ID and particle segments. */ export function parseParticlePath(path: ParticlePath): { networkId: string; segments: string[]; } { const parts = path.split("/").filter(Boolean); return { networkId: parts[0], segments: parts.slice(1) }; } /** * Convert a ParticlePath to the Firestore document path for that particle. * * Firestore structure: * /net1 → networks/net1/particles (collection) * /net1/p1 → networks/net1/particles/p1 (document) * /net1/p1/p2 → networks/net1/particles/p1/children/p2 (document) */ export function toFirestoreDocPath(path: ParticlePath): string { const { networkId, segments } = parseParticlePath(path); const base = `networks/${networkId}/particles`; if (segments.length === 0) return base; const parts: string[] = [base, segments[0]]; for (let i = 1; i < segments.length; i++) { parts.push("children", segments[i]); } return parts.join("/"); } /** * Convert a ParticlePath to the Firestore collection path for its children. * * /net1 → networks/net1/particles (root particles) * /net1/p1 → networks/net1/particles/p1/children * /net1/p1/p2 → networks/net1/particles/p1/children/p2/children */ export function toFirestoreChildrenPath(path: ParticlePath): string { const { segments } = parseParticlePath(path); if (segments.length === 0) { return toFirestoreDocPath(path); } return `${toFirestoreDocPath(path)}/children`; }