Refs #223. Changes what #228 shipped this afternoon, at the reporter's request.
#228skipped a vCard whose UID the book already held. He asked for the opposite on #174:
"update existing entries if existing entry via $criterium is found" [is] the right thing here, not skipping importing the entry. At least for the contact import — and no matter the source format.
He is right, and I would not have got there alone: the reason to import a file a second time is usually that the first one was not right, so skipping means a corrected export corrects nothing.
Merge, not replace — and this is the one I would like checked
Properties the file carries overwrite what is here; properties it does not mention are left alone. So a phone number added in ihasmail after the first import survives a re-import of the original file.
The cost is the mirror of that: a field genuinely deleted at the source stays here. I picked this way round because losing work nobody asked to lose is the worse failure, but it is a judgement rather than something he specified, and it is called out in the reply so he can say otherwise.
addressBookIds is deliberately left off the patch — the card is already in this book, so repeating it says nothing, and saying it on a card that also lives in another book would move it.
Batching
Creates and updates now share one budget. Stalwart counts every object in a /set together, so batching the halves separately would send 300 new and 300 changed as two calls of 300 — and be refused for a limit of 500 that neither half exceeds. There is a test for exactly that shape.
Scope
LDIF is untouched. It still imports everything and reports look-alikes without acting on them, because what it should match on is the question still open on #223. Both imports keep one answer shape so a caller need not know which it called; LDIF's updated is always 0, which is the honest number rather than a missing field.
The vCard-attached-to-a-message path changes with it — the newer copy now wins instead of being dropped, so it says the contact was brought up to date rather than that nothing was added.
Tests
Three rewritten (the two that asserted skipping, plus the whole-file case) and three new: the update is addressed by the id already present, addressBookIds is absent from the patch, unmentioned properties are left alone, and creates plus updates stay under one ceiling.
npm run typecheck, npm test (995 web + 142 server), npm run build pass. Three new plurals across nine catalogues, two now-dead strings retired.
Refs #223. Changes what #228 shipped this afternoon, at the reporter's request.
#228 **skipped** a vCard whose UID the book already held. He asked for the opposite on [#174](https://github.com/Coffey-Labs/ihasmail/issues/174#issuecomment-5512290384):
> "update existing entries if existing entry via `$criterium` is found" [is] the right thing here, *not* skipping importing the entry. At least for the *contact* import — and no matter the source format.
He is right, and I would not have got there alone: the reason to import a file a second time is usually that the first one was not right, so skipping means a corrected export corrects nothing.
## Merge, not replace — and this is the one I would like checked
Properties the file carries overwrite what is here; properties it does not mention are **left alone**. So a phone number added in ihasmail after the first import survives a re-import of the original file.
The cost is the mirror of that: a field genuinely *deleted* at the source stays here. I picked this way round because losing work nobody asked to lose is the worse failure, but it is a judgement rather than something he specified, and it is called out in the reply so he can say otherwise.
`addressBookIds` is deliberately left off the patch — the card is already in this book, so repeating it says nothing, and saying it on a card that also lives in another book would move it.
## Batching
Creates and updates now share one budget. Stalwart counts every object in a `/set` together, so batching the halves separately would send 300 new and 300 changed as two calls of 300 — and be refused for a limit of 500 that neither half exceeds. There is a test for exactly that shape.
## Scope
**LDIF is untouched.** It still imports everything and reports look-alikes without acting on them, because what it should match on is the question still open on #223. Both imports keep one answer shape so a caller need not know which it called; LDIF's `updated` is always `0`, which is the honest number rather than a missing field.
The vCard-attached-to-a-message path changes with it — the newer copy now wins instead of being dropped, so it says the contact was brought up to date rather than that nothing was added.
## Tests
Three rewritten (the two that asserted skipping, plus the whole-file case) and three new: the update is addressed by the id already present, `addressBookIds` is absent from the patch, unmentioned properties are left alone, and creates plus updates stay under one ceiling.
`npm run typecheck`, `npm test` (995 web + 142 server), `npm run build` pass. Three new plurals across nine catalogues, two now-dead strings retired.
**Merged** 2026-09-02 as coffey-labs/ihasmail@3582ad116e04
<sub>Rebuilt from: git history, session transcript.</sub>
This repo is archived. You cannot comment on issues.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Refs #223. Changes what #228 shipped this afternoon, at the reporter's request.
#228 skipped a vCard whose UID the book already held. He asked for the opposite on #174:
He is right, and I would not have got there alone: the reason to import a file a second time is usually that the first one was not right, so skipping means a corrected export corrects nothing.
Merge, not replace — and this is the one I would like checked
Properties the file carries overwrite what is here; properties it does not mention are left alone. So a phone number added in ihasmail after the first import survives a re-import of the original file.
The cost is the mirror of that: a field genuinely deleted at the source stays here. I picked this way round because losing work nobody asked to lose is the worse failure, but it is a judgement rather than something he specified, and it is called out in the reply so he can say otherwise.
addressBookIdsis deliberately left off the patch — the card is already in this book, so repeating it says nothing, and saying it on a card that also lives in another book would move it.Batching
Creates and updates now share one budget. Stalwart counts every object in a
/settogether, so batching the halves separately would send 300 new and 300 changed as two calls of 300 — and be refused for a limit of 500 that neither half exceeds. There is a test for exactly that shape.Scope
LDIF is untouched. It still imports everything and reports look-alikes without acting on them, because what it should match on is the question still open on #223. Both imports keep one answer shape so a caller need not know which it called; LDIF's
updatedis always0, which is the honest number rather than a missing field.The vCard-attached-to-a-message path changes with it — the newer copy now wins instead of being dropped, so it says the contact was brought up to date rather than that nothing was added.
Tests
Three rewritten (the two that asserted skipping, plus the whole-file case) and three new: the update is addressed by the id already present,
addressBookIdsis absent from the patch, unmentioned properties are left alone, and creates plus updates stay under one ceiling.npm run typecheck,npm test(995 web + 142 server),npm run buildpass. Three new plurals across nine catalogues, two now-dead strings retired.Merged 2026-09-02 as coffey-labs/ihasmail@3582ad116e
Rebuilt from: git history, session transcript.