{"version":3,"file":"path-resolution.mjs","names":[],"sources":["../../src/path-resolution.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Array from 'effect/Array';\nimport type * as Duration from 'effect/Duration';\nimport * as Effect from 'effect/Effect';\nimport * as Function from 'effect/Function';\nimport * as Option from 'effect/Option';\nimport * as Order from 'effect/Order';\nimport * as Record from 'effect/Record';\n\nimport * as EffectEx from '@dxos/effect/EffectEx';\nimport * as GraphNode from '@dxos/graph/GraphNode';\nimport { EntityId, SpaceId } from '@dxos/keys';\nimport { log } from '@dxos/log';\nimport * as Position from '@dxos/util/Position';\n\nimport * as Graph from './AppGraph.ts';\nimport * as GraphBuilder from './AppGraphBuilder.ts';\n\n/**\n * A single `(prefix, id?)` pair as parsed by `@dxos/app-toolkit`'s `UrlPath.parse`. Kept as a\n * plain structural type here (rather than importing `UrlPath.Pair`) because app-graph must not\n * depend on app-toolkit.\n */\nexport type UrlPair = {\n  key: string;\n  id?: string;\n  workspace: string;\n};\n\n/**\n * A resolved pair: the index it occupied in the parsed chain, and the qualified graph node id it\n * resolved to. `null` in the caller's result array means the pair didn't resolve (unknown key or\n * no matching node); how an unresolved pair is surfaced is the caller's concern.\n */\nexport type ResolvedPair = {\n  pairIndex: number;\n  nodeId: string;\n};\n\n/**\n * One pair's outcome. Exactly one of `nodeId`/`candidateId` is set — `candidateId` is the id the node\n * *would* have, so callers can keep addressing an unresolved pair by what it asked for. A pair with\n * no candidate at all (an unknown key) is `null`.\n */\nexport type PairResolution = {\n  pairIndex: number;\n  nodeId?: string;\n  candidateId?: string;\n};\n\n/** The graph-path representation of a node, the reverse of a `UrlPair` (`id` is absent for singleton keys). */\nexport type RepresentedNode = {\n  key: string;\n  id?: string;\n  workspace: string;\n};\n\n/** Reserved words that can never be registered as a `urlKey`, duplicated from `UrlPath.isReservedKey`\n * (rather than imported) to keep app-graph free of an app-toolkit dependency. A key declared by a\n * binding — including the `anchor` and `linked` tiers — is never reserved. */\nconst RESERVED_URL_KEYS = new Set(['reset', 'redirect', 'not-found']);\n\nconst isReservedUrlKey = (key: string): boolean =>\n  RESERVED_URL_KEYS.has(key) || SpaceId.isValid(key) || EntityId.isValid(key);\n\n/**\n * Ordered `urlKey`-declaring extensions: sorted by Position then insertion order (matching\n * connector-ordering semantics elsewhere in this package), with reserved-word and grammar keys dropped\n * (each with a `log.warn`). A single key may legitimately be shared by more than one extension (e.g. plugin-space\n * declares `collection` on both the root-collection children connector and the nested-collection\n * children connector, which together address any object reachable through a space's collection tree),\n * so keys are NOT deduped here — {@link buildKeyTable} groups the sharers under one key and forward\n * resolution matches a node produced by any of them. Shared with {@link buildUrlKeyTable} so the\n * reservation rule is expressed exactly once.\n */\ntype UrlKeyedExtension = GraphBuilder.BuilderExtension & { meta: GraphBuilder.UrlBinding };\n\n/** Narrows to an extension that declared a URL binding, so callers need no non-null assertion. */\nconst isUrlKeyed = (extension: GraphBuilder.BuilderExtension): extension is UrlKeyedExtension => !!extension.meta?.key;\n\nconst getKeyedExtensions = (builder: GraphBuilder.GraphBuilder): UrlKeyedExtension[] => {\n  const extensions = Function.pipe(Record.values(builder.getExtensions()), Array.sortBy(Position.compare));\n  const { anchorKey, linked } = builder.urlGrammar;\n\n  const keyed: UrlKeyedExtension[] = [];\n  for (const extension of extensions) {\n    if (!isUrlKeyed(extension)) {\n      continue;\n    }\n    if (isReservedUrlKey(extension.meta.key)) {\n      log.warn('reserved URL prefix key', { key: extension.meta.key, extension: extension.id });\n      continue;\n    }\n    if (extension.meta.key === linked.key || extension.meta.key === anchorKey) {\n      // The grammar's keys are read before any item lookup, so an extension bound to one could never resolve.\n      log.warn('URL prefix key is reserved by the grammar', { key: extension.meta.key, extension: extension.id });\n      continue;\n    }\n    keyed.push(extension);\n  }\n  return keyed;\n};\n\n/**\n * Build the global `urlKey -> extensionIds` table from the builder's current extensions. Recomputed\n * on every call — cheap (a synchronous scan of already-registered extensions) and always current, so\n * activating/deactivating plugins can never leave a stale table around. A key maps to the ordered list\n * of every extension that declared it (usually one); forward resolution treats a node produced by any\n * of them as a match for the key.\n */\nconst buildKeyTable = (builder: GraphBuilder.GraphBuilder): Map<string, string[]> => {\n  const table = new Map<string, string[]>();\n  for (const extension of getKeyedExtensions(builder)) {\n    const key = extension.meta.key;\n    const existing = table.get(key);\n    if (existing) {\n      existing.push(extension.id);\n    } else {\n      table.set(key, [extension.id]);\n    }\n  }\n  return table;\n};\n\n/**\n * A single registered URL prefix key, in the shape `UrlPath.parse` expects. Kept as a plain\n * structural type here (rather than importing `UrlPath.KeyTableEntry`) because app-graph must not\n * depend on app-toolkit.\n */\nexport type UrlKeyTableEntry = { key: string; hasId: boolean; anchor: boolean };\n\n/**\n * Build the `urlKey -> { key, hasId }` table consumed by `UrlPath.parse`, straight from the\n * builder's current `urlKey`/`urlKeyHasId` declarations — the \"registration, not parser\" property\n * the URL grammar requires. Callers (the layout url-handler) pass this to `UrlPath.parse`\n * to tokenize a pathname into a pair chain.\n */\nexport const buildUrlKeyTable = (builder: GraphBuilder.GraphBuilder): Map<string, UrlKeyTableEntry> => {\n  const table = new Map<string, UrlKeyTableEntry>();\n  const { anchorKey, linked } = builder.urlGrammar;\n  if (anchorKey) {\n    table.set(anchorKey, { key: anchorKey, hasId: true, anchor: true });\n  }\n  table.set(linked.key, { key: linked.key, hasId: true, anchor: false });\n  for (const extension of getKeyedExtensions(builder)) {\n    const key = extension.meta.key;\n    // The tokenizer's flat lookup is derived from `kind`: a singleton has no id.\n    const hasId = extension.meta.kind !== 'singleton';\n    const anchor = false;\n    const existing = table.get(key);\n    if (existing && existing.hasId !== hasId) {\n      // Extensions that share a key must agree on their kind — the parse table has one entry per key.\n      // A mismatch is a declaration bug; keep the first and warn.\n      log.warn('conflicting kind for shared URL prefix key', { key, extension: extension.id });\n      continue;\n    }\n    table.set(key, { key, hasId, anchor });\n  }\n  return table;\n};\n\n/**\n * Materialize a candidate qualified node id and confirm it exists: expand its ancestors, then check\n * the node is known. Returns the id on success, `null` otherwise.\n *\n * Expansion only triggers population — the objects behind it load out of band — so on a cold\n * restore the check can run before the node lands and report a false absence. Waiting for a node\n * that has not arrived is the caller's job ({@link resolveKeyId} races one deadline across every\n * candidate), so this stays immediate and a speculative candidate falls through at once.\n */\nconst materializeCandidate = async (\n  builder: GraphBuilder.GraphBuilder,\n  candidateId: string,\n): Promise<string | null> => {\n  Graph.expandPath(builder.graph, candidateId);\n  await GraphBuilder.flush(builder);\n  return Option.isSome(Graph.getNode(builder.graph, candidateId)) ? candidateId : null;\n};\n\n/**\n * Resolve a single `(key, id)` pair to a qualified node id, anchored at the workspace base. Resolution\n * is fully explicit — no search. Each binding for the key yields at most one candidate:\n *   1. Without a resolver (the preferred deterministic case), the node its shape addresses by the id\n *      ({@link GraphBuilder.urlCandidate}); a singleton addresses its fixed node.\n *   2. With a {@link GraphBuilder.PathResolver} (recursive/mutable shapes, i.e. nested collections), the\n *      node the resolver locates.\n * Static candidates are tried before resolved ones; an unmatched pair yields `null`.\n */\nconst resolveKeyId = async (\n  builder: GraphBuilder.GraphBuilder,\n  workspace: string,\n  bindings: ReadonlyArray<GraphBuilder.UrlBinding>,\n  id: string | undefined,\n  wait?: Duration.Input,\n): Promise<{ nodeId?: string; candidateId?: string }> => {\n  const { tailSeparator } = builder.urlGrammar;\n  const candidateIds = bindings.flatMap((binding) =>\n    Option.toArray(GraphBuilder.urlCandidate(binding, workspace, id, tailSeparator)),\n  );\n\n  // A defect in a resolver degrades to no candidate rather than crashing resolution.\n  const workspaceBaseId = `${GraphNode.RootId}/${workspace}`;\n  for (const binding of bindings) {\n    if (binding.resolve && id !== undefined && (binding.workspace?.(workspace) ?? true)) {\n      const candidateId = await EffectEx.runPromise(\n        binding.resolve({ id, workspace, workspaceBaseId }).pipe(Effect.catchDefect(() => Effect.succeed(null))),\n      );\n      if (candidateId) {\n        candidateIds.push(candidateId);\n      }\n    }\n  }\n\n  // Immediate pass in precedence order, so an already-materialized node still resolves to the\n  // first extension that claims it.\n  for (const candidateId of candidateIds) {\n    const resolved = await materializeCandidate(builder, candidateId);\n    if (resolved) {\n      return { nodeId: resolved };\n    }\n  }\n  // Highest precedence, so it is the id this pair would have had; reported whether or not the wait\n  // below succeeds, so an unresolved pair still names the node it was asking for.\n  const candidateId = candidateIds[0];\n  if (wait === undefined || candidateIds.length === 0) {\n    return { candidateId };\n  }\n\n  // One deadline for the pair, raced across every candidate — per-candidate waits run serially, so\n  // N key-sharing extensions would multiply the caller's bound by N. Dynamic candidates wait too:\n  // they name the recursive shapes (nested collections) whose containers are the slowest to\n  // materialize, which is exactly what the wait exists for.\n  const waited = await EffectEx.runPromise(\n    Effect.raceAll(\n      candidateIds.map((candidate) => Graph.waitFor(builder.graph, candidate).pipe(Effect.as(candidate))),\n    ).pipe(Effect.timeoutOrElse({ duration: wait, orElse: () => Effect.succeed<string | null>(null) })),\n  );\n  return waited ? { nodeId: waited } : { candidateId };\n};\n\nconst resolveLinked = async (\n  builder: GraphBuilder.GraphBuilder,\n  precedingNodeId: string,\n  variant: string,\n): Promise<string | null> => {\n  const { relation, prefix } = builder.urlGrammar.linked;\n  Graph.expandSync(builder.graph, precedingNodeId, relation);\n  await GraphBuilder.flush(builder);\n\n  const match = Graph.getConnections(builder.graph, precedingNodeId, relation).find(\n    (node) => linkedVariant(prefix, node.id) === variant,\n  );\n  return match?.id ?? null;\n};\n\nconst isLinkedId = (prefix: string, id: string): boolean => GraphNode.segmentId(id).startsWith(prefix);\n\nconst linkedVariant = (prefix: string, id: string): string => {\n  const segment = GraphNode.segmentId(id);\n  return segment.startsWith(prefix) ? segment.slice(prefix.length) : segment;\n};\n\nconst resolveUrlAsync = async (\n  builder: GraphBuilder.GraphBuilder,\n  parsed: { workspace: string; pairs: ReadonlyArray<UrlPair> },\n  options?: ResolveUrlOptions,\n): Promise<Array<PairResolution | null>> => {\n  const keyTable = buildKeyTable(builder);\n  const allExtensions = builder.getExtensions();\n  const results: Array<PairResolution | null> = parsed.pairs.map(() => null);\n\n  // The chain partitions into `[item, linked*]` groups: a linked pair resolves against the\n  // preceding ITEM, and item pairs resolve against the workspace base, so groups are independent.\n  // Running them concurrently is what keeps the caller's per-pair deadline a wall-clock bound —\n  // resolving serially spends it once per plank, and a multi-plank cold deep link then exceeds the\n  // module activation timeout, which disables the plugin rather than degrading to not-found.\n  const groups: Array<number[]> = [];\n  parsed.pairs.forEach((pair, pairIndex) => {\n    if (pair.key === builder.urlGrammar.linked.key && groups.length > 0) {\n      groups[groups.length - 1].push(pairIndex);\n    } else {\n      groups.push([pairIndex]);\n    }\n  });\n\n  const resolveItem = async (pairIndex: number): Promise<{ nodeId?: string; candidateId?: string } | null> => {\n    const pair = parsed.pairs[pairIndex];\n    const extensionIdList = keyTable.get(pair.key);\n    if (!extensionIdList || extensionIdList.length === 0) {\n      log.warn('unknown URL prefix key', { key: pair.key });\n      return null;\n    }\n\n    const bindings = extensionIdList.flatMap((extensionId) => allExtensions[extensionId]?.meta ?? []);\n    return resolveKeyId(builder, pair.workspace, bindings, pair.id, options?.wait?.(pairIndex));\n  };\n\n  await Promise.all(\n    groups.map(async ([headIndex, ...linkedIndexes]) => {\n      const headPair = parsed.pairs[headIndex];\n      // A leading linked pair has no item to attach to (groups only start with one when the chain\n      // opens with it), so it resolves to nothing rather than against a stale base.\n      const head = headPair.key === builder.urlGrammar.linked.key ? null : await resolveItem(headIndex);\n      const headNodeId = head?.nodeId;\n      results[headIndex] = head?.nodeId\n        ? { pairIndex: headIndex, nodeId: head.nodeId }\n        : head?.candidateId\n          ? { pairIndex: headIndex, candidateId: head.candidateId }\n          : null;\n\n      // Linked pairs attach to this group's item, and to each other in order.\n      let lastItemNodeId = headNodeId;\n      for (const pairIndex of linkedIndexes) {\n        const pair = parsed.pairs[pairIndex];\n        const nodeId = lastItemNodeId && pair.id ? await resolveLinked(builder, lastItemNodeId, pair.id) : null;\n        results[pairIndex] = nodeId ? { pairIndex, nodeId } : null;\n      }\n    }),\n  );\n\n  return results;\n};\n\n/**\n * Resolve a parsed URL's pair chain to graph node ids, walking left to right. Resolution is fully\n * explicit — each binding's shape names its candidate (a resolver locates data-dependent ones); there is\n * no generic search. {@link representNode} is the reverse.\n *\n * An unknown key, or a key whose extension produces no matching node, yields `null` at that index;\n * how a `null` is surfaced is the caller's concern. A linked pair resolves against the *preceding\n * item's* node, not the raw preceding pair.\n */\nexport type ResolveUrlOptions = {\n  /**\n   * How long to wait for a pair's candidate node to materialize, by pair index. Per-pair because\n   * the answer differs: a pair whose object is known to exist is merely late, while one nothing\n   * vouches for is absent and must not hold up the restore. Return `undefined` to read immediately.\n   */\n  readonly wait?: (pairIndex: number) => Duration.Input | undefined;\n};\n\nexport const resolveUrl = (\n  builder: GraphBuilder.GraphBuilder,\n  parsed: { workspace: string; pairs: ReadonlyArray<UrlPair> },\n  options?: ResolveUrlOptions,\n): Effect.Effect<Array<PairResolution | null>> => Effect.promise(() => resolveUrlAsync(builder, parsed, options));\n\n/**\n * Reverse-map a graph node id back to its `(key, id?, workspace)` representation, the inverse of\n * `resolveUrl`. A linked node (a `<prefix><variant>` segment) maps to the grammar's `linked` key with the\n * variant as its id. Any other node maps through the most specific binding whose shape addresses its id,\n * so the answer is the same whether or not the node is loaded; an id no binding addresses returns\n * `Option.none()`.\n */\nexport const representNode = (builder: GraphBuilder.GraphBuilder, nodeId: string): Option.Option<RepresentedNode> => {\n  // Canonical node ids are `root/<workspace>/...`; the workspace is always the second segment.\n  const workspace = nodeId.split(GraphNode.PathSeparator)[1];\n  if (!workspace) {\n    return Option.none();\n  }\n\n  const { linked, tailSeparator } = builder.urlGrammar;\n  if (isLinkedId(linked.prefix, nodeId)) {\n    return Option.some({ key: linked.key, id: linkedVariant(linked.prefix, nodeId), workspace });\n  }\n\n  const matches = getKeyedExtensions(builder).flatMap((extension) =>\n    Option.toArray(GraphBuilder.urlRepresentation(nodeId, extension.meta, tailSeparator)).map((represented) => ({\n      represented,\n      specificity: specificity(extension.meta),\n      extension: extension.id,\n    })),\n  );\n  const [best, ...rest] = Array.sortWith(matches, (match) => -match.specificity, Order.Number);\n  if (!best) {\n    return Option.none();\n  }\n  const rival = rest.find(\n    (match) =>\n      match.specificity === best.specificity &&\n      (match.represented.key !== best.represented.key || match.represented.id !== best.represented.id),\n  );\n  if (rival) {\n    log.warn('equally specific URL bindings address one node', {\n      nodeId,\n      extensions: [best.extension, rival.extension],\n    });\n  }\n  return Option.some({ ...best.represented, workspace });\n};\n\n/**\n * How narrowly a binding's shape pins its nodes: literal segments count most (a singleton's own segment\n * included), then its `minDepth`, so the deeper of two bindings sharing a path claims its ids.\n */\nconst specificity = (binding: GraphBuilder.UrlBinding): number =>\n  (binding.path.length + (binding.kind === 'singleton' ? 1 : 0)) * MAX_DEPTH_RANK + (binding.minDepth ?? 1);\n\n/** Above any `minDepth` a binding declares, so a literal segment always outranks depth. */\nconst MAX_DEPTH_RANK = 1_000;\n"],"mappings":"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"}