mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-17 15:19:00 +02:00
72 lines
2.9 KiB
TypeScript
72 lines
2.9 KiB
TypeScript
/**
|
|
* Centralized URL path builder. All navigation in shared packages (packages/views)
|
|
* MUST go through this module — no hardcoded string paths.
|
|
*
|
|
* Two kinds of paths:
|
|
* - workspace-scoped: paths.workspace(slug).xxx() — carry workspace in URL
|
|
* - global: paths.login(), paths.newWorkspace(), paths.invite(id) — pre-workspace routes
|
|
*
|
|
* Why pure functions + builder pattern:
|
|
* - Changing a route shape (e.g. adding workspace slug prefix) becomes a single-file edit
|
|
* - IDs are always URL-encoded here so callers can't forget
|
|
* - Zero runtime deps means this module is safe in Node (tests) and browsers
|
|
*/
|
|
|
|
const encode = (id: string) => encodeURIComponent(id);
|
|
|
|
function workspaceScoped(slug: string) {
|
|
const ws = `/${encode(slug)}`;
|
|
return {
|
|
root: () => `${ws}/issues`,
|
|
usage: () => `${ws}/usage`,
|
|
issues: () => `${ws}/issues`,
|
|
issueDetail: (id: string) => `${ws}/issues/${encode(id)}`,
|
|
projects: () => `${ws}/projects`,
|
|
projectDetail: (id: string) => `${ws}/projects/${encode(id)}`,
|
|
autopilots: () => `${ws}/autopilots`,
|
|
autopilotDetail: (id: string) => `${ws}/autopilots/${encode(id)}`,
|
|
agents: () => `${ws}/agents`,
|
|
newAgent: () => `${ws}/agents/new`,
|
|
agentDetail: (id: string) => `${ws}/agents/${encode(id)}`,
|
|
memberDetail: (id: string) => `${ws}/members/${encode(id)}`,
|
|
squads: () => `${ws}/squads`,
|
|
squadDetail: (id: string) => `${ws}/squads/${encode(id)}`,
|
|
inbox: () => `${ws}/inbox`,
|
|
chat: () => `${ws}/chat`,
|
|
myIssues: () => `${ws}/my-issues`,
|
|
runtimes: () => `${ws}/runtimes`,
|
|
runtimeDetail: (id: string) => `${ws}/runtimes/${encode(id)}`,
|
|
runtimeSettings: (machineId: string, runtimeId: string) =>
|
|
`${ws}/runtimes/${encode(machineId)}/runtime/${encode(runtimeId)}`,
|
|
skills: () => `${ws}/skills`,
|
|
skillDetail: (id: string) => `${ws}/skills/${encode(id)}`,
|
|
settings: () => `${ws}/settings`,
|
|
attachmentPreview: (id: string) => `${ws}/attachments/${encode(id)}/preview`,
|
|
};
|
|
}
|
|
|
|
export const paths = {
|
|
workspace: workspaceScoped,
|
|
|
|
// Global (pre-workspace) routes
|
|
login: () => "/login",
|
|
newWorkspace: () => "/workspaces/new",
|
|
invite: (id: string) => `/invite/${encode(id)}`,
|
|
invitations: () => "/invitations",
|
|
onboarding: () => "/onboarding",
|
|
authCallback: () => "/auth/callback",
|
|
root: () => "/",
|
|
};
|
|
|
|
export type WorkspacePaths = ReturnType<typeof workspaceScoped>;
|
|
|
|
// Prefixes — not slug names — because we match against full URL paths.
|
|
// A path is global if it equals or begins with any of these.
|
|
// Note: `/workspaces/` (trailing slash) is the prefix — `workspaces` is reserved,
|
|
// so any path starting with `/workspaces/...` is system-owned, not user-owned.
|
|
const GLOBAL_PREFIXES = ["/login", "/workspaces/", "/invite/", "/invitations", "/onboarding", "/auth/", "/logout", "/signup"];
|
|
|
|
export function isGlobalPath(path: string): boolean {
|
|
return GLOBAL_PREFIXES.some((p) => path === p || path.startsWith(p));
|
|
}
|