{"version":3,"sources":["../src/workspace.ts"],"sourcesContent":["import { createPushChannel } from './pushChannel';\nimport { WORKSPACE, REQUEST_WORKSPACE } from './generated/protocol';\n\n/**\n * Which project the immediately.run workbench currently has open — the repository\n * the user is editing, not your app's own repository. `null` when your app is not\n * running inside an editing session at all (a standalone full-tab route, a task\n * overlay): that is a real answer, not a failure, and code here must handle it.\n *\n * ⚠ **`namespace` is not necessarily a GitHub owner.** A `local` session is\n * well-formed but not GitHub-shaped — `/edit/local/my-app-3fa9c2d1/my-app/live`\n * reads back as `{ provider: 'local', namespace: 'my-app-3fa9c2d1', repository:\n * 'my-app', ref: 'live' }`. Branch on `provider` before rendering a GitHub URL.\n *\n * Baseline capability `workspace:read` — every app may read it. It discloses\n * nothing that is not already in the session URL your app can read from\n * {@link onUrlChange} when it drives the host route; this channel exists for the\n * apps that *don't* — a self-routed panel keeps its own route, never receives\n * `urlchange`, and would otherwise have no way to learn which project it is in.\n *\n * There is deliberately no counterpart that lets an app *change* the workspace:\n * navigating the workbench stays a host action under the ordinary consent.\n *\n * ```ts\n * import { useWorkspace } from '@immediately-run/sdk';\n *\n * const workspace = useWorkspace();\n * // Scope your app's stored data to the project it belongs to.\n * const scope = workspace?.label ?? null;\n * ```\n */\nexport interface Workspace {\n  /** Loader provider — `'github'`, `'local'`, … Branch on this, never assume. */\n  provider: string;\n  /** Owner/namespace segment. See the warning above: NOT always a GitHub owner. */\n  namespace: string;\n  /** Repository/project segment. */\n  repository: string;\n  /** The branch, tag, or ref-shaped segment of the session. */\n  ref: string;\n  /**\n   * The session's display identity, `` `${namespace}/${repository}` `` — e.g.\n   * `neumark-family/recipes`.\n   *\n   * This is the string to key durable per-project data on. It is the SAME value the\n   * host labels the working-tree mount with, so an app that already scopes by that\n   * mount's label needs no migration to move onto this channel.\n   */\n  label: string;\n}\n\n/**\n * Assumed before the host reports — and the value that stands forever on a host too\n * old to push this channel. `null` means \"no project here\", which is also the honest\n * answer outside an editing session, so an app written against it degrades to\n * unscoped rather than to wrong.\n */\nconst DEFAULT_WORKSPACE: Workspace | null = null;\n\nconst isWorkspace = (v: unknown): v is Workspace => {\n  const w = v as Partial<Workspace> | null;\n  return (\n    !!w &&\n    typeof w === 'object' &&\n    typeof w.provider === 'string' &&\n    typeof w.namespace === 'string' &&\n    typeof w.repository === 'string' &&\n    typeof w.ref === 'string' &&\n    typeof w.label === 'string'\n  );\n};\n\n// Read over the transport (SDK_PACKAGING_SPEC §4): the host pushes `workspace` and\n// answers `request-workspace` (wire format: site-main channelBridge.ts).\n//\n// `parse` must distinguish the host SAYING `null` from a message it cannot read.\n// `undefined` means \"not a value for this channel\" (createPushChannel ignores it and\n// keeps the last known workspace), while `null` is the host reporting \"no session\"\n// and MUST land — otherwise a frame that navigates out of a session would keep\n// reporting the project it used to be in.\n//\n// It REBUILDS the value field by field rather than passing the host's object through.\n// This channel is baseline-readable by every app, so \"it carries only the session's\n// coordinates\" has to be a property of this code, not of the host's good manners: a\n// host that pushed a token or a user id alongside them would otherwise get it into\n// app-visible memory by reference.\nconst channel = createPushChannel<Workspace | null>({\n  pushType: WORKSPACE,\n  requestType: REQUEST_WORKSPACE,\n  initial: DEFAULT_WORKSPACE,\n  parse: (msg) => {\n    if (msg.workspace === null) return null;\n    if (!isWorkspace(msg.workspace)) return undefined;\n    const { provider, namespace, repository, ref, label } = msg.workspace;\n    return { provider, namespace, repository, ref, label };\n  },\n});\n\n/** Returns the current workspace, or `null` outside an editing session. */\nexport const getWorkspace = (): Workspace | null => channel.get();\n\n/**\n * Subscribe to workspace changes. The listener is invoked immediately with the\n * current value, then again on every change. Returns an unsubscribe fn.\n */\nexport const onWorkspaceChange = (listener: (workspace: Workspace | null) => void): (() => void) =>\n  channel.onChange(listener);\n\n/** React hook returning the current workspace, re-rendering on change. */\nexport const useWorkspace = (): Workspace | null => channel.use();\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAAkC;AAClC,sBAA6C;AAwD7C,MAAM,oBAAsC;AAE5C,MAAM,cAAc,CAAC,MAA+B;AAClD,QAAM,IAAI;AACV,SACE,CAAC,CAAC,KACF,OAAO,MAAM,YACb,OAAO,EAAE,aAAa,YACtB,OAAO,EAAE,cAAc,YACvB,OAAO,EAAE,eAAe,YACxB,OAAO,EAAE,QAAQ,YACjB,OAAO,EAAE,UAAU;AAEvB;AAgBA,MAAM,cAAU,sCAAoC;AAAA,EAClD,UAAU;AAAA,EACV,aAAa;AAAA,EACb,SAAS;AAAA,EACT,OAAO,CAAC,QAAQ;AACd,QAAI,IAAI,cAAc,KAAM,QAAO;AACnC,QAAI,CAAC,YAAY,IAAI,SAAS,EAAG,QAAO;AACxC,UAAM,EAAE,UAAU,WAAW,YAAY,KAAK,MAAM,IAAI,IAAI;AAC5D,WAAO,EAAE,UAAU,WAAW,YAAY,KAAK,MAAM;AAAA,EACvD;AACF,CAAC;AAGM,MAAM,eAAe,MAAwB,QAAQ,IAAI;AAMzD,MAAM,oBAAoB,CAAC,aAChC,QAAQ,SAAS,QAAQ;AAGpB,MAAM,eAAe,MAAwB,QAAQ,IAAI;","names":[]}