payload-nested-docs-page-tree 1.2.1 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -175,10 +175,18 @@ function compareSortValues(leftValue, rightValue) {
|
|
|
175
175
|
}
|
|
176
176
|
return leftValue < rightValue ? -1 : 1;
|
|
177
177
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
178
|
+
// Plain lexicographic comparison. Required for fractional-indexing keys (e.g. `_order`
|
|
179
|
+
// values like `a5`, `a53`, `a5i`), which encode position as raw lex order — using
|
|
180
|
+
// `localeCompare` with `numeric: true` would interpret digit runs as numbers and
|
|
181
|
+
// produce a different ordering than the database (e.g. `a53 > a5i` numerically vs
|
|
182
|
+
// `a53 < a5i` lexicographically). It also matches MongoDB's default string ordering,
|
|
183
|
+
// so children sort consistently between server-side and client-side.
|
|
184
|
+
const leftString = String(leftValue);
|
|
185
|
+
const rightString = String(rightValue);
|
|
186
|
+
if (leftString === rightString) {
|
|
187
|
+
return 0;
|
|
188
|
+
}
|
|
189
|
+
return leftString < rightString ? -1 : 1;
|
|
182
190
|
}
|
|
183
191
|
function createTreeNodeComparator(sort) {
|
|
184
192
|
const sortTokens = parseSortTokens(sort);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utilities/pageTree.ts"],"sourcesContent":["import type { PageTreeSourceDoc } from '../types.js'\r\nexport type { PageTreeSourceDoc } from '../types.js'\r\n\r\nexport type PageTreeMeta = {\r\n __pageTreeAncestorIDs: string[]\r\n __pageTreeDepth: number\r\n __pageTreeHasChildren: boolean\r\n __pageTreeID: string\r\n __pageTreeOrderIndex: number\r\n __pageTreeParentID: null | string\r\n __pageTreeShadeLevel: number\r\n}\r\n\r\nexport type PageTreeDoc = PageTreeMeta & PageTreeSourceDoc\r\n\r\ntype SortToken = {\r\n descending: boolean\r\n path: string[]\r\n}\r\n\r\ntype SortValue = null | number | string\r\n\r\ntype TreeNode = {\r\n doc: PageTreeSourceDoc\r\n id: string\r\n originalIndex: number\r\n parentID: null | string\r\n}\r\n\r\nconst dateLikePattern = /^\\d{4}-\\d{2}-\\d{2}(?:[T\\s].*)?$/\r\n\r\nexport type BuildPageTreeDocsOptions = {\r\n parentFieldSlug?: string\r\n sort?: string\r\n}\r\n\r\nexport function stringifyDocID(id: number | string | undefined): string {\r\n return String(id ?? '')\r\n}\r\n\r\nexport function getRelationshipID(value: unknown): null | string {\r\n if (typeof value === 'number' || typeof value === 'string') {\r\n return stringifyDocID(value)\r\n }\r\n\r\n if (value && typeof value === 'object' && 'id' in value) {\r\n const relationshipID = (value as { id?: number | string }).id\r\n\r\n if (typeof relationshipID === 'number' || typeof relationshipID === 'string') {\r\n return stringifyDocID(relationshipID)\r\n }\r\n }\r\n\r\n return null\r\n}\r\n\r\nexport function getDocParentID(\r\n doc: PageTreeSourceDoc,\r\n parentFieldSlug = 'parent',\r\n): null | string {\r\n return getRelationshipID(doc[parentFieldSlug])\r\n}\r\n\r\nexport function getDocDisplayLabel(doc: PageTreeDoc): string {\r\n const title = doc.title\r\n\r\n if (typeof title === 'string' && title.trim().length > 0) {\r\n return title\r\n }\r\n\r\n if (typeof doc.slug === 'string' && doc.slug.trim().length > 0) {\r\n return doc.slug\r\n }\r\n\r\n return doc.__pageTreeID\r\n}\r\n\r\nexport function getDocSlugSegment(doc: PageTreeDoc): string {\r\n if (typeof doc.slug === 'string' && doc.slug.trim().length > 0) {\r\n return doc.slug.trim().replace(/^\\/+|\\/+$/g, '')\r\n }\r\n\r\n return doc.__pageTreeID\r\n}\r\n\r\nexport function buildDocSlugPath(args: {\r\n doc: PageTreeDoc\r\n docsByID: Map<string, PageTreeDoc>\r\n}): string {\r\n const { doc, docsByID } = args\r\n const segments = doc.__pageTreeAncestorIDs\r\n .map((ancestorID) => docsByID.get(ancestorID))\r\n .filter((ancestorDoc): ancestorDoc is PageTreeDoc => Boolean(ancestorDoc))\r\n .map(getDocSlugSegment)\r\n\r\n segments.push(getDocSlugSegment(doc))\r\n\r\n return `/${segments.filter(Boolean).join('/')}`\r\n}\r\n\r\nexport function buildProspectiveDocSlugPath(args: {\r\n activeDoc: PageTreeDoc\r\n docsByID: Map<string, PageTreeDoc>\r\n targetDoc?: PageTreeDoc\r\n}): string {\r\n const { activeDoc, docsByID, targetDoc } = args\r\n\r\n if (!targetDoc) {\r\n return `/${getDocSlugSegment(activeDoc)}`\r\n }\r\n\r\n const segments = targetDoc.__pageTreeAncestorIDs\r\n .map((ancestorID) => docsByID.get(ancestorID))\r\n .filter((ancestorDoc): ancestorDoc is PageTreeDoc => Boolean(ancestorDoc))\r\n .map(getDocSlugSegment)\r\n\r\n segments.push(getDocSlugSegment(targetDoc), getDocSlugSegment(activeDoc))\r\n\r\n return `/${segments.filter(Boolean).join('/')}`\r\n}\r\n\r\nexport function buildChildrenByParentID(args: {\r\n docs: PageTreeSourceDoc[]\r\n parentFieldSlug?: string\r\n}): Map<null | string, string[]> {\r\n const { docs, parentFieldSlug = 'parent' } = args\r\n const childrenByParentID = new Map<null | string, string[]>()\r\n\r\n for (const doc of docs) {\r\n const docID = stringifyDocID(doc.id)\r\n const parentID = getDocParentID(doc, parentFieldSlug)\r\n const existingChildren = childrenByParentID.get(parentID) ?? []\r\n\r\n existingChildren.push(docID)\r\n childrenByParentID.set(parentID, existingChildren)\r\n }\r\n\r\n return childrenByParentID\r\n}\r\n\r\nexport function collectDescendantIDs(\r\n rootID: string,\r\n childrenByParentID: Map<null | string, string[]>,\r\n): string[] {\r\n const descendantIDs: string[] = []\r\n const stack = [...(childrenByParentID.get(rootID) ?? [])]\r\n\r\n while (stack.length > 0) {\r\n const currentID = stack.pop()\r\n\r\n if (!currentID) {\r\n continue\r\n }\r\n\r\n descendantIDs.push(currentID)\r\n stack.push(...(childrenByParentID.get(currentID) ?? []))\r\n }\r\n\r\n return descendantIDs\r\n}\r\n\r\nfunction getCycleNodeIDs(nodes: TreeNode[], nodesByID: Map<string, TreeNode>): Set<string> {\r\n const cycleNodeIDs = new Set<string>()\r\n const pathStack: string[] = []\r\n const visitState = new Map<string, 'done' | 'visiting'>()\r\n\r\n const walk = (node: TreeNode) => {\r\n const currentState = visitState.get(node.id)\r\n\r\n if (currentState === 'done') {\r\n return\r\n }\r\n\r\n if (currentState === 'visiting') {\r\n const cycleStartIndex = pathStack.lastIndexOf(node.id)\r\n const cycleIDs = cycleStartIndex >= 0 ? pathStack.slice(cycleStartIndex) : [node.id]\r\n\r\n for (const cycleID of cycleIDs) {\r\n cycleNodeIDs.add(cycleID)\r\n }\r\n\r\n return\r\n }\r\n\r\n visitState.set(node.id, 'visiting')\r\n pathStack.push(node.id)\r\n\r\n if (node.parentID !== null) {\r\n const parentNode = nodesByID.get(node.parentID)\r\n\r\n if (parentNode) {\r\n walk(parentNode)\r\n }\r\n }\r\n\r\n pathStack.pop()\r\n visitState.set(node.id, 'done')\r\n }\r\n\r\n for (const node of nodes) {\r\n walk(node)\r\n }\r\n\r\n return cycleNodeIDs\r\n}\r\n\r\nfunction parseSortTokens(sort?: string): SortToken[] {\r\n if (typeof sort !== 'string' || sort.trim().length === 0) {\r\n return []\r\n }\r\n\r\n return sort\r\n .split(',')\r\n .map((token) => token.trim())\r\n .filter((token) => token.length > 0)\r\n .map((token) => {\r\n const descending = token.startsWith('-')\r\n const normalizedToken = descending ? token.slice(1) : token\r\n\r\n return {\r\n descending,\r\n path: normalizedToken.split('.').filter(Boolean),\r\n }\r\n })\r\n .filter((token) => token.path.length > 0)\r\n}\r\n\r\nfunction resolveSortValue(source: unknown, path: string[]): SortValue | undefined {\r\n let currentValue = source\r\n\r\n for (const segment of path) {\r\n if (!currentValue || typeof currentValue !== 'object' || Array.isArray(currentValue)) {\r\n return undefined\r\n }\r\n\r\n if (!(segment in currentValue)) {\r\n return undefined\r\n }\r\n\r\n currentValue = (currentValue as Record<string, unknown>)[segment]\r\n }\r\n\r\n if (currentValue === null || currentValue === undefined) {\r\n return null\r\n }\r\n\r\n if (currentValue instanceof Date) {\r\n const timestamp = currentValue.getTime()\r\n\r\n return Number.isNaN(timestamp) ? undefined : timestamp\r\n }\r\n\r\n if (typeof currentValue === 'boolean') {\r\n return currentValue ? 1 : 0\r\n }\r\n\r\n if (typeof currentValue === 'number') {\r\n return Number.isFinite(currentValue) ? currentValue : undefined\r\n }\r\n\r\n if (typeof currentValue !== 'string') {\r\n return undefined\r\n }\r\n\r\n const normalizedValue = currentValue.trim()\r\n\r\n if (dateLikePattern.test(normalizedValue)) {\r\n const timestamp = Date.parse(normalizedValue)\r\n\r\n if (!Number.isNaN(timestamp)) {\r\n return timestamp\r\n }\r\n }\r\n\r\n return normalizedValue\r\n}\r\n\r\nfunction compareSortValues(leftValue: SortValue, rightValue: SortValue): number {\r\n if (leftValue === null && rightValue === null) {\r\n return 0\r\n }\r\n\r\n if (leftValue === null) {\r\n return -1\r\n }\r\n\r\n if (rightValue === null) {\r\n return 1\r\n }\r\n\r\n if (typeof leftValue === 'number' && typeof rightValue === 'number') {\r\n if (leftValue === rightValue) {\r\n return 0\r\n }\r\n\r\n return leftValue < rightValue ? -1 : 1\r\n }\r\n\r\n return String(leftValue).localeCompare(String(rightValue), undefined, {\r\n numeric: true,\r\n sensitivity: 'base',\r\n })\r\n}\r\n\r\nfunction createTreeNodeComparator(sort?: string) {\r\n const sortTokens = parseSortTokens(sort)\r\n\r\n if (sortTokens.length === 0) {\r\n return null\r\n }\r\n\r\n return (leftNode: TreeNode, rightNode: TreeNode): number => {\r\n for (const sortToken of sortTokens) {\r\n const leftValue = resolveSortValue(leftNode.doc, sortToken.path)\r\n const rightValue = resolveSortValue(rightNode.doc, sortToken.path)\r\n\r\n if (leftValue === undefined || rightValue === undefined) {\r\n continue\r\n }\r\n\r\n const comparison = compareSortValues(leftValue, rightValue)\r\n\r\n if (comparison !== 0) {\r\n return sortToken.descending ? -comparison : comparison\r\n }\r\n }\r\n\r\n return leftNode.originalIndex - rightNode.originalIndex\r\n }\r\n}\r\n\r\nexport function buildPageTreeDocs(\r\n docs: PageTreeSourceDoc[],\r\n options: BuildPageTreeDocsOptions = {},\r\n): PageTreeDoc[] {\r\n const parentFieldSlug = options.parentFieldSlug ?? 'parent'\r\n const nodes: TreeNode[] = docs.map((doc, index) => ({\r\n doc,\r\n id: stringifyDocID(doc.id),\r\n originalIndex: index,\r\n parentID: getDocParentID(doc, parentFieldSlug),\r\n }))\r\n const nodesByID = new Map(nodes.map((node) => [node.id, node]))\r\n const cycleNodeIDs = getCycleNodeIDs(nodes, nodesByID)\r\n const effectiveParentIDByNodeID = new Map<string, null | string>()\r\n const childrenByParentID = new Map<null | string, TreeNode[]>()\r\n const compareTreeNodes = createTreeNodeComparator(options.sort)\r\n\r\n for (const node of nodes) {\r\n const parentExists = node.parentID !== null && nodesByID.has(node.parentID)\r\n const effectiveParentID =\r\n parentExists && !cycleNodeIDs.has(node.id) ? node.parentID : null\r\n\r\n effectiveParentIDByNodeID.set(node.id, effectiveParentID)\r\n\r\n const currentChildren = childrenByParentID.get(effectiveParentID) ?? []\r\n currentChildren.push(node)\r\n childrenByParentID.set(effectiveParentID, currentChildren)\r\n }\r\n\r\n if (compareTreeNodes) {\r\n for (const children of childrenByParentID.values()) {\r\n children.sort(compareTreeNodes)\r\n }\r\n }\r\n\r\n const orderedDocs: PageTreeDoc[] = []\r\n const subtreeDepthByID = new Map<string, number>()\r\n const visitedNodeIDs = new Set<string>()\r\n\r\n const getShadeLevel = (rootMaxDepth: number, depth: number): number => {\r\n if (rootMaxDepth === 0) {\r\n return 0\r\n }\r\n\r\n return Math.max(rootMaxDepth - depth + 1, 1)\r\n }\r\n\r\n const getSubtreeDepth = (node: TreeNode): number => {\r\n const cachedDepth = subtreeDepthByID.get(node.id)\r\n\r\n if (typeof cachedDepth === 'number') {\r\n return cachedDepth\r\n }\r\n\r\n const children = childrenByParentID.get(node.id) ?? []\r\n\r\n if (children.length === 0) {\r\n subtreeDepthByID.set(node.id, 0)\r\n return 0\r\n }\r\n\r\n const depth = Math.max(...children.map((child) => getSubtreeDepth(child) + 1))\r\n subtreeDepthByID.set(node.id, depth)\r\n return depth\r\n }\r\n\r\n const visit = (node: TreeNode, ancestorIDs: string[], rootMaxDepth: number) => {\r\n if (visitedNodeIDs.has(node.id)) {\r\n return\r\n }\r\n\r\n visitedNodeIDs.add(node.id)\r\n\r\n const children = childrenByParentID.get(node.id) ?? []\r\n const depth = ancestorIDs.length\r\n const shadeLevel = getShadeLevel(rootMaxDepth, depth)\r\n\r\n orderedDocs.push({\r\n ...(node.doc as PageTreeSourceDoc),\r\n __pageTreeAncestorIDs: ancestorIDs,\r\n __pageTreeDepth: depth,\r\n __pageTreeHasChildren: children.length > 0,\r\n __pageTreeID: node.id,\r\n __pageTreeOrderIndex: orderedDocs.length,\r\n __pageTreeParentID: effectiveParentIDByNodeID.get(node.id) ?? null,\r\n __pageTreeShadeLevel: shadeLevel,\r\n })\r\n\r\n for (const child of children) {\r\n visit(child, [...ancestorIDs, node.id], rootMaxDepth)\r\n }\r\n }\r\n\r\n const roots = [...(childrenByParentID.get(null) ?? [])]\r\n\r\n for (const root of roots) {\r\n visit(root, [], getSubtreeDepth(root))\r\n }\r\n\r\n const fallbackNodes = compareTreeNodes ? [...nodes].sort(compareTreeNodes) : nodes\r\n\r\n for (const node of fallbackNodes) {\r\n if (!visitedNodeIDs.has(node.id)) {\r\n visit(node, [], getSubtreeDepth(node))\r\n }\r\n }\r\n\r\n return orderedDocs\r\n}\r\n\r\nexport function getVisibleTreeDocs(\r\n docs: PageTreeDoc[],\r\n collapsedIDs: ReadonlySet<string>,\r\n): PageTreeDoc[] {\r\n return docs.filter((doc) =>\r\n doc.__pageTreeAncestorIDs.every((ancestorID) => !collapsedIDs.has(ancestorID)),\r\n )\r\n}\r\n"],"names":["dateLikePattern","stringifyDocID","id","String","getRelationshipID","value","relationshipID","getDocParentID","doc","parentFieldSlug","getDocDisplayLabel","title","trim","length","slug","__pageTreeID","getDocSlugSegment","replace","buildDocSlugPath","args","docsByID","segments","__pageTreeAncestorIDs","map","ancestorID","get","filter","ancestorDoc","Boolean","push","join","buildProspectiveDocSlugPath","activeDoc","targetDoc","buildChildrenByParentID","docs","childrenByParentID","Map","docID","parentID","existingChildren","set","collectDescendantIDs","rootID","descendantIDs","stack","currentID","pop","getCycleNodeIDs","nodes","nodesByID","cycleNodeIDs","Set","pathStack","visitState","walk","node","currentState","cycleStartIndex","lastIndexOf","cycleIDs","slice","cycleID","add","parentNode","parseSortTokens","sort","split","token","descending","startsWith","normalizedToken","path","resolveSortValue","source","currentValue","segment","Array","isArray","undefined","Date","timestamp","getTime","Number","isNaN","isFinite","normalizedValue","test","parse","compareSortValues","leftValue","rightValue","localeCompare","numeric","sensitivity","createTreeNodeComparator","sortTokens","leftNode","rightNode","sortToken","comparison","originalIndex","buildPageTreeDocs","options","index","effectiveParentIDByNodeID","compareTreeNodes","parentExists","has","effectiveParentID","currentChildren","children","values","orderedDocs","subtreeDepthByID","visitedNodeIDs","getShadeLevel","rootMaxDepth","depth","Math","max","getSubtreeDepth","cachedDepth","child","visit","ancestorIDs","shadeLevel","__pageTreeDepth","__pageTreeHasChildren","__pageTreeOrderIndex","__pageTreeParentID","__pageTreeShadeLevel","roots","root","fallbackNodes","getVisibleTreeDocs","collapsedIDs","every"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/pageTree.ts"],"sourcesContent":["import type { PageTreeSourceDoc } from '../types.js'\r\nexport type { PageTreeSourceDoc } from '../types.js'\r\n\r\nexport type PageTreeMeta = {\r\n __pageTreeAncestorIDs: string[]\r\n __pageTreeDepth: number\r\n __pageTreeHasChildren: boolean\r\n __pageTreeID: string\r\n __pageTreeOrderIndex: number\r\n __pageTreeParentID: null | string\r\n __pageTreeShadeLevel: number\r\n}\r\n\r\nexport type PageTreeDoc = PageTreeMeta & PageTreeSourceDoc\r\n\r\ntype SortToken = {\r\n descending: boolean\r\n path: string[]\r\n}\r\n\r\ntype SortValue = null | number | string\r\n\r\ntype TreeNode = {\r\n doc: PageTreeSourceDoc\r\n id: string\r\n originalIndex: number\r\n parentID: null | string\r\n}\r\n\r\nconst dateLikePattern = /^\\d{4}-\\d{2}-\\d{2}(?:[T\\s].*)?$/\r\n\r\nexport type BuildPageTreeDocsOptions = {\r\n parentFieldSlug?: string\r\n sort?: string\r\n}\r\n\r\nexport function stringifyDocID(id: number | string | undefined): string {\r\n return String(id ?? '')\r\n}\r\n\r\nexport function getRelationshipID(value: unknown): null | string {\r\n if (typeof value === 'number' || typeof value === 'string') {\r\n return stringifyDocID(value)\r\n }\r\n\r\n if (value && typeof value === 'object' && 'id' in value) {\r\n const relationshipID = (value as { id?: number | string }).id\r\n\r\n if (typeof relationshipID === 'number' || typeof relationshipID === 'string') {\r\n return stringifyDocID(relationshipID)\r\n }\r\n }\r\n\r\n return null\r\n}\r\n\r\nexport function getDocParentID(\r\n doc: PageTreeSourceDoc,\r\n parentFieldSlug = 'parent',\r\n): null | string {\r\n return getRelationshipID(doc[parentFieldSlug])\r\n}\r\n\r\nexport function getDocDisplayLabel(doc: PageTreeDoc): string {\r\n const title = doc.title\r\n\r\n if (typeof title === 'string' && title.trim().length > 0) {\r\n return title\r\n }\r\n\r\n if (typeof doc.slug === 'string' && doc.slug.trim().length > 0) {\r\n return doc.slug\r\n }\r\n\r\n return doc.__pageTreeID\r\n}\r\n\r\nexport function getDocSlugSegment(doc: PageTreeDoc): string {\r\n if (typeof doc.slug === 'string' && doc.slug.trim().length > 0) {\r\n return doc.slug.trim().replace(/^\\/+|\\/+$/g, '')\r\n }\r\n\r\n return doc.__pageTreeID\r\n}\r\n\r\nexport function buildDocSlugPath(args: {\r\n doc: PageTreeDoc\r\n docsByID: Map<string, PageTreeDoc>\r\n}): string {\r\n const { doc, docsByID } = args\r\n const segments = doc.__pageTreeAncestorIDs\r\n .map((ancestorID) => docsByID.get(ancestorID))\r\n .filter((ancestorDoc): ancestorDoc is PageTreeDoc => Boolean(ancestorDoc))\r\n .map(getDocSlugSegment)\r\n\r\n segments.push(getDocSlugSegment(doc))\r\n\r\n return `/${segments.filter(Boolean).join('/')}`\r\n}\r\n\r\nexport function buildProspectiveDocSlugPath(args: {\r\n activeDoc: PageTreeDoc\r\n docsByID: Map<string, PageTreeDoc>\r\n targetDoc?: PageTreeDoc\r\n}): string {\r\n const { activeDoc, docsByID, targetDoc } = args\r\n\r\n if (!targetDoc) {\r\n return `/${getDocSlugSegment(activeDoc)}`\r\n }\r\n\r\n const segments = targetDoc.__pageTreeAncestorIDs\r\n .map((ancestorID) => docsByID.get(ancestorID))\r\n .filter((ancestorDoc): ancestorDoc is PageTreeDoc => Boolean(ancestorDoc))\r\n .map(getDocSlugSegment)\r\n\r\n segments.push(getDocSlugSegment(targetDoc), getDocSlugSegment(activeDoc))\r\n\r\n return `/${segments.filter(Boolean).join('/')}`\r\n}\r\n\r\nexport function buildChildrenByParentID(args: {\r\n docs: PageTreeSourceDoc[]\r\n parentFieldSlug?: string\r\n}): Map<null | string, string[]> {\r\n const { docs, parentFieldSlug = 'parent' } = args\r\n const childrenByParentID = new Map<null | string, string[]>()\r\n\r\n for (const doc of docs) {\r\n const docID = stringifyDocID(doc.id)\r\n const parentID = getDocParentID(doc, parentFieldSlug)\r\n const existingChildren = childrenByParentID.get(parentID) ?? []\r\n\r\n existingChildren.push(docID)\r\n childrenByParentID.set(parentID, existingChildren)\r\n }\r\n\r\n return childrenByParentID\r\n}\r\n\r\nexport function collectDescendantIDs(\r\n rootID: string,\r\n childrenByParentID: Map<null | string, string[]>,\r\n): string[] {\r\n const descendantIDs: string[] = []\r\n const stack = [...(childrenByParentID.get(rootID) ?? [])]\r\n\r\n while (stack.length > 0) {\r\n const currentID = stack.pop()\r\n\r\n if (!currentID) {\r\n continue\r\n }\r\n\r\n descendantIDs.push(currentID)\r\n stack.push(...(childrenByParentID.get(currentID) ?? []))\r\n }\r\n\r\n return descendantIDs\r\n}\r\n\r\nfunction getCycleNodeIDs(nodes: TreeNode[], nodesByID: Map<string, TreeNode>): Set<string> {\r\n const cycleNodeIDs = new Set<string>()\r\n const pathStack: string[] = []\r\n const visitState = new Map<string, 'done' | 'visiting'>()\r\n\r\n const walk = (node: TreeNode) => {\r\n const currentState = visitState.get(node.id)\r\n\r\n if (currentState === 'done') {\r\n return\r\n }\r\n\r\n if (currentState === 'visiting') {\r\n const cycleStartIndex = pathStack.lastIndexOf(node.id)\r\n const cycleIDs = cycleStartIndex >= 0 ? pathStack.slice(cycleStartIndex) : [node.id]\r\n\r\n for (const cycleID of cycleIDs) {\r\n cycleNodeIDs.add(cycleID)\r\n }\r\n\r\n return\r\n }\r\n\r\n visitState.set(node.id, 'visiting')\r\n pathStack.push(node.id)\r\n\r\n if (node.parentID !== null) {\r\n const parentNode = nodesByID.get(node.parentID)\r\n\r\n if (parentNode) {\r\n walk(parentNode)\r\n }\r\n }\r\n\r\n pathStack.pop()\r\n visitState.set(node.id, 'done')\r\n }\r\n\r\n for (const node of nodes) {\r\n walk(node)\r\n }\r\n\r\n return cycleNodeIDs\r\n}\r\n\r\nfunction parseSortTokens(sort?: string): SortToken[] {\r\n if (typeof sort !== 'string' || sort.trim().length === 0) {\r\n return []\r\n }\r\n\r\n return sort\r\n .split(',')\r\n .map((token) => token.trim())\r\n .filter((token) => token.length > 0)\r\n .map((token) => {\r\n const descending = token.startsWith('-')\r\n const normalizedToken = descending ? token.slice(1) : token\r\n\r\n return {\r\n descending,\r\n path: normalizedToken.split('.').filter(Boolean),\r\n }\r\n })\r\n .filter((token) => token.path.length > 0)\r\n}\r\n\r\nfunction resolveSortValue(source: unknown, path: string[]): SortValue | undefined {\r\n let currentValue = source\r\n\r\n for (const segment of path) {\r\n if (!currentValue || typeof currentValue !== 'object' || Array.isArray(currentValue)) {\r\n return undefined\r\n }\r\n\r\n if (!(segment in currentValue)) {\r\n return undefined\r\n }\r\n\r\n currentValue = (currentValue as Record<string, unknown>)[segment]\r\n }\r\n\r\n if (currentValue === null || currentValue === undefined) {\r\n return null\r\n }\r\n\r\n if (currentValue instanceof Date) {\r\n const timestamp = currentValue.getTime()\r\n\r\n return Number.isNaN(timestamp) ? undefined : timestamp\r\n }\r\n\r\n if (typeof currentValue === 'boolean') {\r\n return currentValue ? 1 : 0\r\n }\r\n\r\n if (typeof currentValue === 'number') {\r\n return Number.isFinite(currentValue) ? currentValue : undefined\r\n }\r\n\r\n if (typeof currentValue !== 'string') {\r\n return undefined\r\n }\r\n\r\n const normalizedValue = currentValue.trim()\r\n\r\n if (dateLikePattern.test(normalizedValue)) {\r\n const timestamp = Date.parse(normalizedValue)\r\n\r\n if (!Number.isNaN(timestamp)) {\r\n return timestamp\r\n }\r\n }\r\n\r\n return normalizedValue\r\n}\r\n\r\nfunction compareSortValues(leftValue: SortValue, rightValue: SortValue): number {\r\n if (leftValue === null && rightValue === null) {\r\n return 0\r\n }\r\n\r\n if (leftValue === null) {\r\n return -1\r\n }\r\n\r\n if (rightValue === null) {\r\n return 1\r\n }\r\n\r\n if (typeof leftValue === 'number' && typeof rightValue === 'number') {\r\n if (leftValue === rightValue) {\r\n return 0\r\n }\r\n\r\n return leftValue < rightValue ? -1 : 1\r\n }\r\n\r\n // Plain lexicographic comparison. Required for fractional-indexing keys (e.g. `_order`\r\n // values like `a5`, `a53`, `a5i`), which encode position as raw lex order — using\r\n // `localeCompare` with `numeric: true` would interpret digit runs as numbers and\r\n // produce a different ordering than the database (e.g. `a53 > a5i` numerically vs\r\n // `a53 < a5i` lexicographically). It also matches MongoDB's default string ordering,\r\n // so children sort consistently between server-side and client-side.\r\n const leftString = String(leftValue)\r\n const rightString = String(rightValue)\r\n\r\n if (leftString === rightString) {\r\n return 0\r\n }\r\n\r\n return leftString < rightString ? -1 : 1\r\n}\r\n\r\nfunction createTreeNodeComparator(sort?: string) {\r\n const sortTokens = parseSortTokens(sort)\r\n\r\n if (sortTokens.length === 0) {\r\n return null\r\n }\r\n\r\n return (leftNode: TreeNode, rightNode: TreeNode): number => {\r\n for (const sortToken of sortTokens) {\r\n const leftValue = resolveSortValue(leftNode.doc, sortToken.path)\r\n const rightValue = resolveSortValue(rightNode.doc, sortToken.path)\r\n\r\n if (leftValue === undefined || rightValue === undefined) {\r\n continue\r\n }\r\n\r\n const comparison = compareSortValues(leftValue, rightValue)\r\n\r\n if (comparison !== 0) {\r\n return sortToken.descending ? -comparison : comparison\r\n }\r\n }\r\n\r\n return leftNode.originalIndex - rightNode.originalIndex\r\n }\r\n}\r\n\r\nexport function buildPageTreeDocs(\r\n docs: PageTreeSourceDoc[],\r\n options: BuildPageTreeDocsOptions = {},\r\n): PageTreeDoc[] {\r\n const parentFieldSlug = options.parentFieldSlug ?? 'parent'\r\n const nodes: TreeNode[] = docs.map((doc, index) => ({\r\n doc,\r\n id: stringifyDocID(doc.id),\r\n originalIndex: index,\r\n parentID: getDocParentID(doc, parentFieldSlug),\r\n }))\r\n const nodesByID = new Map(nodes.map((node) => [node.id, node]))\r\n const cycleNodeIDs = getCycleNodeIDs(nodes, nodesByID)\r\n const effectiveParentIDByNodeID = new Map<string, null | string>()\r\n const childrenByParentID = new Map<null | string, TreeNode[]>()\r\n const compareTreeNodes = createTreeNodeComparator(options.sort)\r\n\r\n for (const node of nodes) {\r\n const parentExists = node.parentID !== null && nodesByID.has(node.parentID)\r\n const effectiveParentID =\r\n parentExists && !cycleNodeIDs.has(node.id) ? node.parentID : null\r\n\r\n effectiveParentIDByNodeID.set(node.id, effectiveParentID)\r\n\r\n const currentChildren = childrenByParentID.get(effectiveParentID) ?? []\r\n currentChildren.push(node)\r\n childrenByParentID.set(effectiveParentID, currentChildren)\r\n }\r\n\r\n if (compareTreeNodes) {\r\n for (const children of childrenByParentID.values()) {\r\n children.sort(compareTreeNodes)\r\n }\r\n }\r\n\r\n const orderedDocs: PageTreeDoc[] = []\r\n const subtreeDepthByID = new Map<string, number>()\r\n const visitedNodeIDs = new Set<string>()\r\n\r\n const getShadeLevel = (rootMaxDepth: number, depth: number): number => {\r\n if (rootMaxDepth === 0) {\r\n return 0\r\n }\r\n\r\n return Math.max(rootMaxDepth - depth + 1, 1)\r\n }\r\n\r\n const getSubtreeDepth = (node: TreeNode): number => {\r\n const cachedDepth = subtreeDepthByID.get(node.id)\r\n\r\n if (typeof cachedDepth === 'number') {\r\n return cachedDepth\r\n }\r\n\r\n const children = childrenByParentID.get(node.id) ?? []\r\n\r\n if (children.length === 0) {\r\n subtreeDepthByID.set(node.id, 0)\r\n return 0\r\n }\r\n\r\n const depth = Math.max(...children.map((child) => getSubtreeDepth(child) + 1))\r\n subtreeDepthByID.set(node.id, depth)\r\n return depth\r\n }\r\n\r\n const visit = (node: TreeNode, ancestorIDs: string[], rootMaxDepth: number) => {\r\n if (visitedNodeIDs.has(node.id)) {\r\n return\r\n }\r\n\r\n visitedNodeIDs.add(node.id)\r\n\r\n const children = childrenByParentID.get(node.id) ?? []\r\n const depth = ancestorIDs.length\r\n const shadeLevel = getShadeLevel(rootMaxDepth, depth)\r\n\r\n orderedDocs.push({\r\n ...(node.doc as PageTreeSourceDoc),\r\n __pageTreeAncestorIDs: ancestorIDs,\r\n __pageTreeDepth: depth,\r\n __pageTreeHasChildren: children.length > 0,\r\n __pageTreeID: node.id,\r\n __pageTreeOrderIndex: orderedDocs.length,\r\n __pageTreeParentID: effectiveParentIDByNodeID.get(node.id) ?? null,\r\n __pageTreeShadeLevel: shadeLevel,\r\n })\r\n\r\n for (const child of children) {\r\n visit(child, [...ancestorIDs, node.id], rootMaxDepth)\r\n }\r\n }\r\n\r\n const roots = [...(childrenByParentID.get(null) ?? [])]\r\n\r\n for (const root of roots) {\r\n visit(root, [], getSubtreeDepth(root))\r\n }\r\n\r\n const fallbackNodes = compareTreeNodes ? [...nodes].sort(compareTreeNodes) : nodes\r\n\r\n for (const node of fallbackNodes) {\r\n if (!visitedNodeIDs.has(node.id)) {\r\n visit(node, [], getSubtreeDepth(node))\r\n }\r\n }\r\n\r\n return orderedDocs\r\n}\r\n\r\nexport function getVisibleTreeDocs(\r\n docs: PageTreeDoc[],\r\n collapsedIDs: ReadonlySet<string>,\r\n): PageTreeDoc[] {\r\n return docs.filter((doc) =>\r\n doc.__pageTreeAncestorIDs.every((ancestorID) => !collapsedIDs.has(ancestorID)),\r\n )\r\n}\r\n"],"names":["dateLikePattern","stringifyDocID","id","String","getRelationshipID","value","relationshipID","getDocParentID","doc","parentFieldSlug","getDocDisplayLabel","title","trim","length","slug","__pageTreeID","getDocSlugSegment","replace","buildDocSlugPath","args","docsByID","segments","__pageTreeAncestorIDs","map","ancestorID","get","filter","ancestorDoc","Boolean","push","join","buildProspectiveDocSlugPath","activeDoc","targetDoc","buildChildrenByParentID","docs","childrenByParentID","Map","docID","parentID","existingChildren","set","collectDescendantIDs","rootID","descendantIDs","stack","currentID","pop","getCycleNodeIDs","nodes","nodesByID","cycleNodeIDs","Set","pathStack","visitState","walk","node","currentState","cycleStartIndex","lastIndexOf","cycleIDs","slice","cycleID","add","parentNode","parseSortTokens","sort","split","token","descending","startsWith","normalizedToken","path","resolveSortValue","source","currentValue","segment","Array","isArray","undefined","Date","timestamp","getTime","Number","isNaN","isFinite","normalizedValue","test","parse","compareSortValues","leftValue","rightValue","leftString","rightString","createTreeNodeComparator","sortTokens","leftNode","rightNode","sortToken","comparison","originalIndex","buildPageTreeDocs","options","index","effectiveParentIDByNodeID","compareTreeNodes","parentExists","has","effectiveParentID","currentChildren","children","values","orderedDocs","subtreeDepthByID","visitedNodeIDs","getShadeLevel","rootMaxDepth","depth","Math","max","getSubtreeDepth","cachedDepth","child","visit","ancestorIDs","shadeLevel","__pageTreeDepth","__pageTreeHasChildren","__pageTreeOrderIndex","__pageTreeParentID","__pageTreeShadeLevel","roots","root","fallbackNodes","getVisibleTreeDocs","collapsedIDs","every"],"mappings":"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"}
|