( db: DatabaseAdapter, meta: PushImportMeta, members: PushImportMember[], messages: PushImportMessage[] )
| 221 | } |
| 222 | |
| 223 | function fullImport( |
| 224 | db: DatabaseAdapter, |
| 225 | meta: PushImportMeta, |
| 226 | members: PushImportMember[], |
| 227 | messages: PushImportMessage[] |
| 228 | ): { writtenCount: number; duplicateCount: number; membersAdded: number } { |
| 229 | db.exec(CHAT_DB_SCHEMA) |
| 230 | |
| 231 | // Auto-create minimal member entries for senders not listed in members, |
| 232 | // preserving the protocol promise that unknown senders are auto-created. |
| 233 | const knownIds = new Set(members.map((m) => m.platformId)) |
| 234 | const extraMembers: PushImportMember[] = [] |
| 235 | for (const msg of messages) { |
| 236 | if (!knownIds.has(msg.sender)) { |
| 237 | extraMembers.push({ |
| 238 | platformId: msg.sender, |
| 239 | accountName: normalizeSenderAccountName(msg.sender, msg.accountName), |
| 240 | groupNickname: normalizeSenderGroupNickname(msg.sender, msg.groupNickname), |
| 241 | }) |
| 242 | knownIds.add(msg.sender) |
| 243 | } |
| 244 | } |
| 245 | const allMembers = extraMembers.length > 0 ? [...members, ...extraMembers] : members |
| 246 | |
| 247 | // Deduplicate within the batch using the same pmid/hash logic as incremental imports. |
| 248 | const pmids = new Set<string>() |
| 249 | const hashKeys = new Set<string>() |
| 250 | let duplicateCount = 0 |
| 251 | const dedupedMessages: PushImportMessage[] = [] |
| 252 | for (const msg of messages) { |
| 253 | const key = generateMessageKey(msg.timestamp, msg.sender, msg.content ?? null) |
| 254 | if (msg.platformMessageId) { |
| 255 | if (pmids.has(msg.platformMessageId)) { |
| 256 | duplicateCount++ |
| 257 | continue |
| 258 | } |
| 259 | if (hashKeys.has(key)) { |
| 260 | duplicateCount++ |
| 261 | continue |
| 262 | } |
| 263 | pmids.add(msg.platformMessageId) |
| 264 | hashKeys.add(key) |
| 265 | } else { |
| 266 | if (hashKeys.has(key)) { |
| 267 | duplicateCount++ |
| 268 | continue |
| 269 | } |
| 270 | hashKeys.add(key) |
| 271 | } |
| 272 | dedupedMessages.push(msg) |
| 273 | } |
| 274 | |
| 275 | // Delegate to the shared writer so member_name_history is tracked correctly, |
| 276 | // matching the behaviour of file-based full imports. |
| 277 | const stats = writeParseResultToDb( |
| 278 | db, |
| 279 | meta, |
| 280 | allMembers.map((m) => ({ |
no test coverage detected