diff --git a/src/core/superstate/cacheParsers.ts b/src/core/superstate/cacheParsers.ts index 089f40ce..ebd6637c 100644 --- a/src/core/superstate/cacheParsers.ts +++ b/src/core/superstate/cacheParsers.ts @@ -7,6 +7,7 @@ import { SpaceInfo } from "shared/types/spaceInfo"; import { orderStringArrayByArray, uniq } from "shared/utils/array"; import { builtinSpaces } from "core/types/space"; +import { resolvePath } from "core/superstate/utils/path"; import { linkContextRow, mergeContextRows, propertyDependencies, syncContextRow } from "core/utils/contexts/linkContextRow"; import { pathByJoins } from "core/utils/spaces/query"; import { ensureArray, initiateString, tagSpacePathFromTag } from "core/utils/strings"; @@ -47,10 +48,11 @@ export const parseContextTableToCache = (space: SpaceInfo, mdb: SpaceTables, pat cols = defaultContextFields.rows as SpaceProperty[]; } const schema = mdb[defaultContextSchemaID]?.schema ?? defaultContextDBSchema; - const contextPaths = mdb[defaultContextSchemaID]?.rows?.map(f => f[PathPropertyName]) ?? []; - + const contextPaths = mdb[defaultContextSchemaID]?.rows?.map(f => resolvePath(f[PathPropertyName], space.path, (p) => pathsIndex.get(p)?.type == 'space')) ?? []; + const missingPaths = paths.filter(f => !contextPaths.includes(f)); - const newPaths = [...orderStringArrayByArray(paths ?? [], contextPaths), ...missingPaths]; + const knownPaths = orderStringArrayByArray((paths ?? []).filter(f => contextPaths.includes(f)), contextPaths); + const newPaths = [...knownPaths, ...missingPaths]; const dependencies = propertyDependencies(cols); const spacePath = pathsIndex.get(space.path); let rows = mergeContextRows(paths, mdb[defaultContextSchemaID]?.rows ?? [], pathsIndex, spacesMap, spacePath) diff --git a/src/core/utils/contexts/context.ts b/src/core/utils/contexts/context.ts index 250bf262..02b544f8 100644 --- a/src/core/utils/contexts/context.ts +++ b/src/core/utils/contexts/context.ts @@ -241,7 +241,7 @@ export const updateContextValue = async ( { const updateFunction = _updateFunction ?? updateValue let newMDB = updateFunction(f, PathPropertyName, path, field, value); - if (rank) + if (rank != null) newMDB = reorderRowsForPath(newMDB, [path], rank); if (manager.superstate.settings.enhancedLogs) { // Update Context Value diff --git a/src/core/utils/contexts/linkContextRow.ts b/src/core/utils/contexts/linkContextRow.ts index 3f50e89b..44d7f74e 100644 --- a/src/core/utils/contexts/linkContextRow.ts +++ b/src/core/utils/contexts/linkContextRow.ts @@ -109,9 +109,13 @@ const resolvedPath = resolvePath(_row[PathPropertyName], path?.path, (spacePath) export const mergeContextRows = ( paths: string[], rows: DBRows, pathStates: Map, spaceMap: IndexMap, path: PathState) => { // Filter existing rows to only include valid paths, preserving database order (rank) + // Dedupe by resolved path so a corrupted table with duplicate rows heals on merge + const seenPaths = new Set(); const validRows = rows.filter(row => { const resolvedPath = resolvePath(row[PathPropertyName], path?.path, (spacePath) => pathStates.get(spacePath)?.type == 'space'); - return paths.includes(resolvedPath); + if (!paths.includes(resolvedPath) || seenPaths.has(resolvedPath)) return false; + seenPaths.add(resolvedPath); + return true; }); // Find paths that are in the paths array but not in any existing row diff --git a/src/shared/utils/array.ts b/src/shared/utils/array.ts index 1b34fb10..f128eea9 100644 --- a/src/shared/utils/array.ts +++ b/src/shared/utils/array.ts @@ -50,7 +50,10 @@ export const onlyUniquePropCaseInsensitive = return array.sort( function (a, b) { const A = order.indexOf(a), B = order.indexOf(b); - + if (A == -1 && B == -1) { + // neither is ranked: keep original relative order (stable sort) + return 0 + } if (A > B) { if (A != -1 && B == -1) { return -1 @@ -62,9 +65,9 @@ export const onlyUniquePropCaseInsensitive = } return -1; } - + }); - + };