# Mail merge review — local source

Artifact: AX069 v1, 2026-10-09. Download `mail-merge-review.mjs` beside this
README. Import `buildMailMergeDrafts(records, subjectTemplate, bodyTemplate)`
in a JavaScript ES-module environment. It returns one complete plain-text review
packet or throws an error; nothing is printed unless your caller prints it.
It exports only that function and uses the same core as the browser job.

Supply a literal rectangular TSV header and 1–25 data rows. Headers are trimmed,
unique, case-sensitive and nonempty; include an exact `email` header. Header
braces and control characters are rejected. All cells reject control characters, including Unicode line separators. Cells must be single-line; quoted
TSV and embedded tabs are unsupported. CRLF/CR become LF; one final newline is
allowed. Blank rows, uneven widths, missing addresses, malformed/basic-ASCII
unsupported addresses and case-insensitive duplicate addresses reject the
entire packet. Dots and plus tags remain distinct. Address checks cannot prove
mailbox ownership, deliverability or consent.

Subject/body templates support case-sensitive `{{field}}` placeholders, with
whitespace around the field name allowed. Static text also works. Missing,
unmatched, empty or nested placeholders reject the packet. Values remain
literal: no recursive substitution, HTML rendering, code evaluation or formula
execution. Template bodies may contain newlines. Subjects, including rendered
values, must be nonempty, one line without control characters and at most 200
characters. Rendered bodies must contain non-whitespace text.

Limits: 100,000 characters per input, 500 columns, 25 data rows, 100,000
characters per rendered template and 100,000 characters for the complete
packet. Errors return no partial packet; nothing is silently skipped/truncated.

## Executable fictional example

Save this code as `example.mjs` beside the downloaded module and run
`node example.mjs` in an existing Node.js ES-module environment.

```js
import { buildMailMergeDrafts } from './mail-merge-review.mjs';
const records = [
  'email\tname\tappointment',
  'alex@example.com\tAlex\tTuesday at 10:00',
  'sam@example.com\tSam\tWednesday at 14:00',
].join('\n');
const subjectTemplate = 'Appointment details for {{name}}';
const bodyTemplate = 'Hello {{name}},\nYour appointment is {{appointment}}.\nPlease check these details and reply if a change is needed.';
const packet = buildMailMergeDrafts(records, subjectTemplate, bodyTemplate);
console.log(packet);
```

Expected exact console output:

```text
MAIL MERGE REVIEW PACKET — no message sent; no Gmail draft created.

Before using Gmail, manually check consent, every recipient address, subject and body content.

Draft 1
To: alex@example.com
Subject: Appointment details for Alex
Body:
Hello Alex,
Your appointment is Tuesday at 10:00.
Please check these details and reply if a change is needed.

Draft 2
To: sam@example.com
Subject: Appointment details for Sam
Body:
Hello Sam,
Your appointment is Wednesday at 14:00.
Please check these details and reply if a change is needed.
```

## Review and privacy

Review every recipient, consent, subject and body against the original Sheet.
Refresh inputs and rerun after changes. This is plain text, not a Gmail import
file. Manually create messages through your normal Gmail workflow and review
again before sending. No Gmail access, OAuth, Gmail draft creation or sending
occurs. Native Gmail templates or manual composition remain alternatives.

The dependency-free module has no network, filesystem, process, secret access,
telemetry or sends. Your caller controls input/output handling and may retain
or transmit data; this module cannot make an external agent environment private.
Choose an appropriate local environment for private lists. The example is
fictional operator QA. Source availability and QA do not establish independent
agent execution, useful customer completion, sending/delivery, repeat use,
paid demand or a time advantage.

## License

MIT License

Copyright (c) 2026 QuasiLife

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
