@recordtimelabel/core 0.1.2 → 0.1.4

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.js +158 -4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@recordtimelabel/core",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "type": "module",
5
5
  "description": "Shared RecordTimeLabel data model, merge logic, operations, and sync engine.",
6
6
  "main": "./src/index.js",
package/src/index.js CHANGED
@@ -989,6 +989,146 @@ const mapValuesArray = (value = {}) => {
989
989
  return Object.values(value);
990
990
  };
991
991
 
992
+ const buildLiveV2RecordIdsByFolder = (recordDocuments = {}) => {
993
+ const liveRecordIdsByFolder = {};
994
+ const liveRecordIdSetsByFolder = {};
995
+
996
+ Object.values(recordDocuments || {}).forEach((recordDoc) => {
997
+ const recordId = normalizeId(recordDoc?.id);
998
+ const folderId = safeFolderId(recordDoc?.folderId);
999
+ if (!recordId) return;
1000
+
1001
+ if (!liveRecordIdsByFolder[folderId]) {
1002
+ liveRecordIdsByFolder[folderId] = [];
1003
+ liveRecordIdSetsByFolder[folderId] = new Set();
1004
+ }
1005
+
1006
+ if (liveRecordIdSetsByFolder[folderId].has(recordId)) return;
1007
+ liveRecordIdSetsByFolder[folderId].add(recordId);
1008
+ liveRecordIdsByFolder[folderId].push(recordId);
1009
+ });
1010
+
1011
+ return {
1012
+ liveRecordIdsByFolder,
1013
+ liveRecordIdSetsByFolder
1014
+ };
1015
+ };
1016
+
1017
+ const buildV2RecordOrderByFolder = (records = {}, recordDocuments = {}) => {
1018
+ const {
1019
+ liveRecordIdsByFolder,
1020
+ liveRecordIdSetsByFolder
1021
+ } = buildLiveV2RecordIdsByFolder(recordDocuments);
1022
+ const hasLiveRecordDocuments = Object.keys(recordDocuments || {}).length > 0;
1023
+ const orders = {};
1024
+ const seenByFolder = {};
1025
+
1026
+ Object.keys(liveRecordIdsByFolder).forEach((folderId) => {
1027
+ orders[folderId] = [];
1028
+ seenByFolder[folderId] = new Set();
1029
+ });
1030
+
1031
+ Object.entries(records || {}).forEach(([folderId, folderRecords]) => {
1032
+ const normalizedFolderId = safeFolderId(folderId);
1033
+ if (hasLiveRecordDocuments && !liveRecordIdSetsByFolder[normalizedFolderId]) return;
1034
+
1035
+ const seen = seenByFolder[normalizedFolderId] || new Set();
1036
+ orders[normalizedFolderId] = orders[normalizedFolderId] || [];
1037
+
1038
+ toArray(folderRecords).forEach((record) => {
1039
+ const recordId = normalizeId(record?.id);
1040
+ if (!recordId || seen.has(recordId)) return;
1041
+ if (
1042
+ liveRecordIdSetsByFolder[normalizedFolderId] &&
1043
+ !liveRecordIdSetsByFolder[normalizedFolderId].has(recordId)
1044
+ ) {
1045
+ return;
1046
+ }
1047
+
1048
+ seen.add(recordId);
1049
+ orders[normalizedFolderId].push(recordId);
1050
+ });
1051
+
1052
+ seenByFolder[normalizedFolderId] = seen;
1053
+ });
1054
+
1055
+ Object.entries(liveRecordIdsByFolder).forEach(([folderId, liveRecordIds]) => {
1056
+ const seen = seenByFolder[folderId] || new Set();
1057
+ orders[folderId] = orders[folderId] || [];
1058
+
1059
+ liveRecordIds.forEach((recordId) => {
1060
+ if (seen.has(recordId)) return;
1061
+ seen.add(recordId);
1062
+ orders[folderId].push(recordId);
1063
+ });
1064
+ });
1065
+
1066
+ return orders;
1067
+ };
1068
+
1069
+ const extractV2RecordOrderByFolder = (folderDocuments = {}) => {
1070
+ const orders = {};
1071
+
1072
+ Object.entries(folderDocuments || {}).forEach(([rawFolderId, folderDoc]) => {
1073
+ const folderId = normalizeId(folderDoc?.id || rawFolderId);
1074
+ if (!folderId || !Array.isArray(folderDoc?.recordOrder)) return;
1075
+
1076
+ const seen = new Set();
1077
+ orders[folderId] = [];
1078
+
1079
+ folderDoc.recordOrder.forEach((recordId) => {
1080
+ const normalized = normalizeId(recordId);
1081
+ if (!normalized || seen.has(normalized)) return;
1082
+ seen.add(normalized);
1083
+ orders[folderId].push(normalized);
1084
+ });
1085
+ });
1086
+
1087
+ return orders;
1088
+ };
1089
+
1090
+ const reorderV2RecordsByFolderRecordOrder = (records = {}, recordOrderByFolder = {}) => {
1091
+ const nextRecords = {};
1092
+
1093
+ Object.entries(records || {}).forEach(([folderId, folderRecords]) => {
1094
+ const fallbackRecords = toArray(folderRecords);
1095
+ const recordOrder = recordOrderByFolder[folderId];
1096
+
1097
+ if (!Array.isArray(recordOrder)) {
1098
+ nextRecords[folderId] = fallbackRecords;
1099
+ return;
1100
+ }
1101
+
1102
+ const recordsById = new Map();
1103
+ fallbackRecords.forEach((record) => {
1104
+ const recordId = normalizeId(record?.id);
1105
+ if (recordId && !recordsById.has(recordId)) {
1106
+ recordsById.set(recordId, { ...record, id: recordId });
1107
+ }
1108
+ });
1109
+
1110
+ const seen = new Set();
1111
+ const orderedRecords = [];
1112
+ recordOrder.forEach((recordId) => {
1113
+ const record = recordsById.get(recordId);
1114
+ if (!record || seen.has(recordId)) return;
1115
+ seen.add(recordId);
1116
+ orderedRecords.push(record);
1117
+ });
1118
+
1119
+ fallbackRecords.forEach((record) => {
1120
+ const recordId = normalizeId(record?.id);
1121
+ if (!recordId || seen.has(recordId)) return;
1122
+ seen.add(recordId);
1123
+ orderedRecords.push(record);
1124
+ });
1125
+
1126
+ nextRecords[folderId] = orderedRecords;
1127
+ });
1128
+
1129
+ return nextRecords;
1130
+ };
1131
+
992
1132
  const cleanV2RecordDocument = (record = {}, folderId, now) => {
993
1133
  const recordId = normalizeId(record.id);
994
1134
  if (!recordId) return null;
@@ -1005,8 +1145,15 @@ const cleanV2RecordDocument = (record = {}, folderId, now) => {
1005
1145
  const cleanV2FolderDocument = (folder = {}, now) => {
1006
1146
  const folderId = normalizeId(folder.id);
1007
1147
  if (!folderId) return null;
1148
+ const {
1149
+ schemaVersion,
1150
+ recordOrder,
1151
+ groupOrder,
1152
+ folderOrder,
1153
+ ...folderFields
1154
+ } = folder || {};
1008
1155
  return {
1009
- ...folder,
1156
+ ...folderFields,
1010
1157
  id: folderId,
1011
1158
  updatedAt: folder.updatedAt || folder.createdAt || now,
1012
1159
  schemaVersion: 2
@@ -1035,10 +1182,16 @@ export const buildFirestoreV2DocumentsFromState = (state = {}, options = {}) =>
1035
1182
  });
1036
1183
  });
1037
1184
 
1185
+ const recordOrderByFolder = buildV2RecordOrderByFolder(normalized.records || {}, records);
1038
1186
  const folders = {};
1039
1187
  toArray(normalized.folders).forEach((folder) => {
1040
1188
  const folderDoc = cleanV2FolderDocument(folder, now);
1041
- if (folderDoc) folders[folderDoc.id] = folderDoc;
1189
+ if (folderDoc) {
1190
+ folders[folderDoc.id] = {
1191
+ ...folderDoc,
1192
+ recordOrder: recordOrderByFolder[folderDoc.id] || []
1193
+ };
1194
+ }
1042
1195
  });
1043
1196
 
1044
1197
  const ops = {};
@@ -1094,11 +1247,12 @@ export const buildStateFromFirestoreV2Documents = (documents = {}, options = {})
1094
1247
  ];
1095
1248
  });
1096
1249
 
1250
+ const recordOrderByFolder = extractV2RecordOrderByFolder(documents?.folders);
1097
1251
  const folders = mapValuesArray(documents?.folders)
1098
1252
  .map((folderDoc) => {
1099
1253
  const folderId = normalizeId(folderDoc?.id);
1100
1254
  if (!folderId) return null;
1101
- const { schemaVersion, ...folder } = folderDoc;
1255
+ const { schemaVersion, recordOrder, groupOrder, folderOrder, ...folder } = folderDoc;
1102
1256
  return { ...folder, id: folderId };
1103
1257
  })
1104
1258
  .filter(Boolean);
@@ -1114,7 +1268,7 @@ export const buildStateFromFirestoreV2Documents = (documents = {}, options = {})
1114
1268
 
1115
1269
  const state = normalizeState({
1116
1270
  folders,
1117
- records,
1271
+ records: reorderV2RecordsByFolderRecordOrder(records, recordOrderByFolder),
1118
1272
  settings: rootDoc.settings || {},
1119
1273
  groupOrder: normalizeGroupOrderForOption(rootDoc.groupOrder, options.groupOrderNormalizer),
1120
1274
  folderOrder: rootDoc.folderOrder || [],