Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
158 commits
Select commit Hold shift + click to select a range
1aa45df
chore: prepare the repository for the upyo port
Alex-Dolid Oct 14, 2026
a558ce2
feat(core): port the message model and the transport contract from up…
Alex-Dolid Oct 14, 2026
d9df477
feat(mock): port the mock transport from upyo 0.7.0
Alex-Dolid Oct 14, 2026
c23eb4d
feat(mime): port the MIME builder and DKIM signing from upyo 0.7.0
Alex-Dolid Oct 14, 2026
23693f6
feat(smtp): port the SMTP transport from upyo 0.7.0
Alex-Dolid Oct 14, 2026
e7a1e7f
feat(ses): port the Amazon SES transport from upyo 0.7.0
Alex-Dolid Oct 14, 2026
0d83f7a
feat(resend): port the Resend transport from upyo 0.7.0
Alex-Dolid Oct 14, 2026
0909657
feat(sendgrid): port the SendGrid transport from upyo 0.7.0
Alex-Dolid Oct 14, 2026
d1a3bb5
feat(mailgun): port the Mailgun transport from upyo 0.7.0
Alex-Dolid Oct 14, 2026
e2cbea6
feat(mailtrap): port the Mailtrap transport from upyo 0.7.0
Alex-Dolid Oct 14, 2026
2902cf8
feat(maileroo): port the Maileroo transport from upyo 0.7.0
Alex-Dolid Oct 14, 2026
cbb6507
feat(lettermint): port the Lettermint transport from upyo 0.7.0
Alex-Dolid Oct 14, 2026
068bd92
feat(plunk): port the Plunk transport from upyo 0.7.0
Alex-Dolid Oct 14, 2026
bc7a2ce
feat(jmap): port the JMAP transport from upyo 0.7.0
Alex-Dolid Oct 14, 2026
ee484e8
feat(retry): port the retry decorator from upyo 0.7.0
Alex-Dolid Oct 14, 2026
0a61baa
feat(pool): port the pool decorator from upyo 0.7.0
Alex-Dolid Oct 14, 2026
64ad458
feat(otel): port the OpenTelemetry decorator from upyo 0.7.0
Alex-Dolid Oct 14, 2026
1561948
feat(logger): port the LogTape decorator of upyo 0.7.0 as ./logger
Alex-Dolid Oct 14, 2026
db4ee5b
docs: adapt the guides of upyo for every subpath
Alex-Dolid Oct 14, 2026
8e02f5d
refactor: remove the createMailer placeholder
Alex-Dolid Oct 14, 2026
c8aab7b
docs: show the ported transport layer on the home page
Alex-Dolid Oct 14, 2026
ecf4c23
fix(core): hold the domain of an address to host-name characters
Alex-Dolid Oct 15, 2026
a102e10
fix(mime): quote a display name that could add a mailbox
Alex-Dolid Oct 15, 2026
c3bd7f1
fix(mime): check an attachment's content type and content ID again
Alex-Dolid Oct 15, 2026
4244d8d
fix(smtp): bound and correctly split the replies of a server
Alex-Dolid Oct 15, 2026
f97f9e4
fix: keep the credentials of a transport out of inspect and JSON
Alex-Dolid Oct 15, 2026
3d03f04
fix(otel,logger): keep addresses out of exported error text by default
Alex-Dolid Oct 15, 2026
aa0f94b
feat(core)!: mark a failed receipt whose delivery is unknown
Alex-Dolid Oct 15, 2026
2c2a3b5
fix(smtp): report a failure after the end of DATA as of unknown delivery
Alex-Dolid Oct 15, 2026
3d1cada
fix(jmap): send the request that submits an email only once
Alex-Dolid Oct 15, 2026
1eb6bfe
fix: never send a message again after a provider answered 2xx
Alex-Dolid Oct 15, 2026
c9617b0
docs: say what is built and what is planned, and list the open issues
Alex-Dolid Oct 15, 2026
e36f3db
refactor(ses): send through a shared request helper
Alex-Dolid Oct 16, 2026
c619758
refactor(resend): send through the shared request helper
Alex-Dolid Oct 16, 2026
cbfffa7
refactor(sendgrid): send through the shared request helper
Alex-Dolid Oct 16, 2026
2536259
refactor(mailgun): send through the shared request helper
Alex-Dolid Oct 16, 2026
92de16b
refactor(mailtrap): send through the shared request helper
Alex-Dolid Oct 16, 2026
e69ff9e
refactor(maileroo): send through the shared request helper
Alex-Dolid Oct 16, 2026
59b137e
refactor(lettermint): send through the shared request helper
Alex-Dolid Oct 16, 2026
3d9752d
refactor(plunk): send through the shared request helper
Alex-Dolid Oct 16, 2026
5a3e90a
refactor(jmap): send through the shared request helper
Alex-Dolid Oct 16, 2026
6b41b51
fix(resend,plunk): stop waiting between retries when the caller aborts
Alex-Dolid Oct 16, 2026
fb8b6c8
fix(lettermint): keep the caller's reason when it aborts a wait
Alex-Dolid Oct 16, 2026
45aaaaa
fix: let the timeout of an HTTP provider cover the response body
Alex-Dolid Oct 16, 2026
971f09c
perf(ses): sign requests with node:crypto, deriving the key once a day
Alex-Dolid Oct 16, 2026
74d71ce
fix: give timeout 0 one meaning and check timeout and retries
Alex-Dolid Oct 16, 2026
01d40da
fix(core): read a Retry-After longer than a day as a day
Alex-Dolid Oct 16, 2026
80e7a80
fix: wait for Retry-After in every HTTP provider, up to 30 seconds
Alex-Dolid Oct 16, 2026
d96068a
fix(retry): return a Retry-After beyond the maximum delay
Alex-Dolid Oct 16, 2026
40e2cbc
fix: one retry policy for every HTTP provider
Alex-Dolid Oct 16, 2026
da08d07
fix: never follow a redirect with credentials
Alex-Dolid Oct 16, 2026
fc35c95
fix: refuse to send provider credentials over plain HTTP
Alex-Dolid Oct 16, 2026
6fe5b1f
fix: read a response body within 4 MiB
Alex-Dolid Oct 16, 2026
9e05ce2
fix: keep the cause of a provider error
Alex-Dolid Oct 16, 2026
095fc6c
fix(core): classify a failure by its status and code before its words
Alex-Dolid Oct 16, 2026
d2ade95
fix(ses): refuse a wrong authentication when the transport is made
Alex-Dolid Oct 16, 2026
c6acb77
fix: encode credentials outside Latin-1 as UTF-8
Alex-Dolid Oct 16, 2026
a354c1b
fix: do not resend an HTTP request that may have arrived
Alex-Dolid Oct 16, 2026
25b0548
fix(jmap): key uploaded blobs by attachment, not content ID
Alex-Dolid Oct 16, 2026
5c5d434
fix(jmap): trust only the Session's own origin with credentials
Alex-Dolid Oct 16, 2026
16437d0
fix(jmap): split sendMany by the server's maxObjectsInSet
Alex-Dolid Oct 16, 2026
eaf7659
fix: keep configured names inside their part of a request URL
Alex-Dolid Oct 16, 2026
8e772d9
chore(bench): measure the library itself rather than reference points
Alex-Dolid Oct 16, 2026
0472adb
perf(mime): encode attachments with Buffer and write lines into one b…
Alex-Dolid Oct 16, 2026
b207cca
perf(smtp): dot-stuff message data by searching for line feeds
Alex-Dolid Oct 16, 2026
f6a27c7
perf(core): check raw message bytes in an index loop
Alex-Dolid Oct 16, 2026
336a832
perf(mime): build quoted-printable and encoded words without quadrati…
Alex-Dolid Oct 16, 2026
ac5d6eb
perf: encode provider attachments with one Buffer-based encoder
Alex-Dolid Oct 16, 2026
4111b2d
perf(smtp): look capabilities up by keyword in an index
Alex-Dolid Oct 16, 2026
d4f93d1
perf: filter custom headers without rebuilding lists per call
Alex-Dolid Oct 16, 2026
0229ae0
perf(pool): select a transport in one pass without wrapper arrays
Alex-Dolid Oct 16, 2026
568e6f4
perf(core): drop undefined receipt fields in one pass
Alex-Dolid Oct 16, 2026
238df6d
perf(maileroo): hash a reference in one pass and encode it with Buffer
Alex-Dolid Oct 16, 2026
3e49407
perf: serialize a request body once rather than on every attempt
Alex-Dolid Oct 16, 2026
4a01de0
perf(core): scan atoms and address literals with one table and loop
Alex-Dolid Oct 16, 2026
9f47a1d
perf(smtp): check envelopes for SMTPUTF8 without spreading addresses
Alex-Dolid Oct 16, 2026
fdd76cf
perf(mime): encode RFC 2231 parameters in one pass over the bytes
Alex-Dolid Oct 16, 2026
2619379
perf(mime): count and check MIME text without encoding or splitting it
Alex-Dolid Oct 16, 2026
c90b8d1
fix(otel): record metrics for every operation and sample only spans
Alex-Dolid Oct 16, 2026
ffeba15
fix(otel): count active sends down when a batch ends
Alex-Dolid Oct 16, 2026
aa111cb
fix(otel): count each message once, with its priority and status
Alex-Dolid Oct 16, 2026
7e6139c
fix(otel): count a batch that failed entirely as a failure
Alex-Dolid Oct 16, 2026
90bd1e2
fix(otel): pass the duration buckets to the histogram
Alex-Dolid Oct 16, 2026
5feb44a
fix(otel)!: refuse the auto and service options it never read
Alex-Dolid Oct 16, 2026
d22d19d
fix(otel): classify thrown errors with the classifier of the receipts
Alex-Dolid Oct 16, 2026
a90e6dc
fix(otel): stream sendMany instead of reading its input first
Alex-Dolid Oct 16, 2026
df78ea8
feat: tell the caller of each retry through onRetry
Alex-Dolid Oct 16, 2026
36ece40
fix(otel): record the retries of the wrapped transport
Alex-Dolid Oct 16, 2026
201d571
feat(logger): log the retries of the wrapped transport
Alex-Dolid Oct 16, 2026
f818556
fix(otel): redact the error.message attribute of a thrown error
Alex-Dolid Oct 16, 2026
d0df291
refactor(otel)!: name metrics and attributes after the semantic conve…
Alex-Dolid Oct 16, 2026
2c8718c
perf(otel): estimate a message's size once, without encoding it
Alex-Dolid Oct 16, 2026
7435391
ci: raise the size budgets that the shared request code outgrew
Alex-Dolid Oct 16, 2026
f197c0a
fix(smtp): judge a loopback host by one rule everywhere
Alex-Dolid Oct 16, 2026
2c5495f
fix(smtp): send the host name for SNI over TLS and STARTTLS
Alex-Dolid Oct 16, 2026
5939dba
fix(smtp): connect implicit TLS once its handshake completes
Alex-Dolid Oct 16, 2026
2ee2c07
refactor(smtp): send a batch in one loop for both kinds of input
Alex-Dolid Oct 16, 2026
45e2b88
fix(smtp): compose a message before taking a connection
Alex-Dolid Oct 16, 2026
dd57efe
fix(smtp): reset a pooled connection when it is next taken
Alex-Dolid Oct 16, 2026
c1b2b44
fix(smtp): let an idle pooled connection not hold the process
Alex-Dolid Oct 16, 2026
1ea1c6f
fix(dkim): sign the instances of a field from the bottom up
Alex-Dolid Oct 16, 2026
c71f80a
fix(dkim): sign the fields a reader trusts by default
Alex-Dolid Oct 16, 2026
fb1d5a4
fix(dkim): refuse settings that could break the signature field
Alex-Dolid Oct 16, 2026
d17c9d5
fix(dkim): import a PKCS#1 private key
Alex-Dolid Oct 16, 2026
ab7041b
fix(dkim): import a key once and check it when the transport is made
Alex-Dolid Oct 16, 2026
c551c29
fix(retry): rethrow a bug of the wrapped transport
Alex-Dolid Oct 16, 2026
055ad1c
fix(pool): dispose of every transport once and report the failures
Alex-Dolid Oct 16, 2026
64b7a11
fix(logger): keep a throwing logger from losing a receipt
Alex-Dolid Oct 16, 2026
0fe4087
refactor: give the abortable sleep a module of its own
Alex-Dolid Oct 16, 2026
cd2f4ce
fix(mock): end the simulated delay when the signal aborts
Alex-Dolid Oct 16, 2026
62f1d46
fix(pool): reject an aborted send with the reason of the signal
Alex-Dolid Oct 16, 2026
b9833f6
fix(mime): draw a boundary from crypto.randomUUID
Alex-Dolid Oct 16, 2026
8dcf259
fix(resend): draw an idempotency key from crypto.randomUUID
Alex-Dolid Oct 16, 2026
2525bbe
feat(core): let the application generate the identifiers
Alex-Dolid Oct 16, 2026
08a7066
feat(mime,smtp): take generateId, and fetch for the OAuth 2.0 refresh
Alex-Dolid Oct 16, 2026
c11fdc6
feat: take fetch and generateId in every HTTP transport
Alex-Dolid Oct 16, 2026
64deeb7
feat(retry,pool,logger,mock,otel): take generateId and random
Alex-Dolid Oct 16, 2026
6b627c2
feat(core): share the logger types and add an internal log
Alex-Dolid Oct 16, 2026
838ebf4
fix(mime): tell an unsigned DKIM send to the logger
Alex-Dolid Oct 16, 2026
66a3aad
fix(plunk): fail a send whose attachment cannot be read
Alex-Dolid Oct 16, 2026
6eb5b52
feat(smtp): log the connection and the protocol, and warn of plain text
Alex-Dolid Oct 16, 2026
996360e
feat: log the retries of the HTTP transports
Alex-Dolid Oct 16, 2026
e747d96
feat(pool,retry): log failovers, retries and a selector that throws
Alex-Dolid Oct 16, 2026
6f07a4d
docs: tell the logger of a transport from the ./logger decorator
Alex-Dolid Oct 16, 2026
3955211
feat(scripts): keep the size budgets in one file and check every entry
Alex-Dolid Oct 16, 2026
71bf035
chore: check the tarball and the size budgets before publishing
Alex-Dolid Oct 16, 2026
c3a816c
docs: loops in one pass and keyed collections in src/
Alex-Dolid Oct 16, 2026
70e7a7c
refactor(core): loops in one pass, a table of word rules, no nested t…
Alex-Dolid Oct 16, 2026
bf41c53
refactor(mime): split buildMimeParts and move whole-body DKIM out of src
Alex-Dolid Oct 16, 2026
8eb9cab
refactor(smtp): split the long functions, loops instead of array methods
Alex-Dolid Oct 16, 2026
7680c98
refactor(jmap): one pass over the method responses, raw delivery in s…
Alex-Dolid Oct 16, 2026
3ca8a0d
refactor: share the converter code of the HTTP providers in internal/…
Alex-Dolid Oct 16, 2026
5f41279
refactor(ses): remove convertMessagesToBulk, dead upstream
Alex-Dolid Oct 16, 2026
ab9e9fc
refactor(pool): an attempt per method and a failure gathered in one r…
Alex-Dolid Oct 16, 2026
473178d
refactor(mock): find sent messages with loops
Alex-Dolid Oct 16, 2026
f3cbe0e
refactor(mime): prepareMimeStream reads, signs and replays in parts
Alex-Dolid Oct 16, 2026
ab16ab0
refactor(retry,otel): loops instead of some and find, a flatter batch…
Alex-Dolid Oct 16, 2026
20628b9
refactor(mailgun,resend): name the caught error error
Alex-Dolid Oct 16, 2026
750a971
chore(lint): enforce the conventions in oxlint
Alex-Dolid Oct 16, 2026
b97909b
fix(ses,resend,jmap): stream sendMany a batch at a time
Alex-Dolid Oct 16, 2026
994c385
feat(sendgrid,mailgun,plunk,maileroo): concurrency for sendMany
Alex-Dolid Oct 16, 2026
8ce8b15
fix(retry): hold back at most maxConcurrent receipts in sendMany
Alex-Dolid Oct 16, 2026
4d712b7
fix(pool): keep the round-robin rotation across batches
Alex-Dolid Oct 16, 2026
27b2a74
docs(logger): say how many messages sendMany holds for their receipts
Alex-Dolid Oct 16, 2026
4f3dcea
test: check the attribution header of every shipped file
Alex-Dolid Oct 16, 2026
c97bf4f
fix(jmap): report the retries of discovery through onRetry
Alex-Dolid Oct 16, 2026
171a288
fix(mime): put inline parts in multipart/related
Alex-Dolid Oct 16, 2026
12cc7fa
fix(mime): keep header lines with encoded words to 76 characters
Alex-Dolid Oct 16, 2026
ec998af
fix(mailgun): send an inline part under its Content-ID
Alex-Dolid Oct 16, 2026
edc3683
fix(jmap): respect the maxSizeRequest of the server
Alex-Dolid Oct 16, 2026
611e93f
fix(jmap): send requests and small raw messages from a buffer on Node 24
Alex-Dolid Oct 16, 2026
b004976
fix(ses): send custom and threading header fields
Alex-Dolid Oct 16, 2026
3263056
fix(smtp)!: drop cram-md5 and refuse an unknown AUTH method when made
Alex-Dolid Oct 16, 2026
a6a2bd8
fix: refuse validateSsl false, which fetch cannot honor
Alex-Dolid Oct 16, 2026
9d27caf
fix(pool)!: refuse continueOnSuccess, which would deliver a message t…
Alex-Dolid Oct 16, 2026
5e612f2
docs: put @alexify/mailer first in the comparison, pinned and in teal
Alex-Dolid Oct 17, 2026
7b81d7b
chore(release): v1.0.0
Alex-Dolid Oct 17, 2026
ae633f0
test(smtp): take EPIPE and ECONNABORTED from a client that has gone
Alex-Dolid Oct 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
18 changes: 13 additions & 5 deletions .claude/skills/js-conventions/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ description: Apply the Alexis JavaScript style (Metarhia-derived, linted by oxli
---

<!-- Adapted from metaskills v1.0.5 (skills/js-conventions) for the Alexis toolchain:
oxlint + oxfmt instead of ESLint + Prettier, pnpm instead of npm, 100 columns instead of 80. -->
oxlint + oxfmt instead of ESLint + Prettier, pnpm instead of npm, 100 columns instead of 80;
loops in one pass instead of array iteration methods in library code (src/). -->

# JavaScript Code Style

Expand Down Expand Up @@ -58,15 +59,18 @@ Use following conventions for JavaScript and TypeScript code.
- Do not mutate input parameters; avoid `arguments`
- Keep functions small and single-purpose
- Decompose long expressions into intermediate variables
- Prefer explicit loops (`for`, `for..of`) in hot paths
- Use array methods when they improve readability
- Iterate with `for` and `for..of` loops in library code (`src/`), index loops in hot paths; no
array iteration methods there (`.map`, `.filter`, `.some`, `.every`, `.find`, `.findIndex`,
`.findLast`, `.reduce`, `.flatMap`, `.forEach`), cold paths included
- Compute everything a collection gives in one pass with an early exit; never chain passes
(`.map().join()`, `Object.entries().filter()`, `Array.from().map()`), and do not build an array
only to iterate it (spread of an iterable, `Array.from(string)`, `split` only to test)
- Use `===` and `!==` only
- Keep return types consistent
- Avoid nested ternaries and deep callback nesting
- Self-documented and self-descriptive code: do not add
obvious comments, code should be clear without comments
- Use iteration methods like `.map`, `.filter`, `.reduce` if it is good for code semantic
- Try to avoid `.forEach` if callback operates with outer context
- Tests, scripts and benchmarks may use array iteration methods where they read better

## Optimizations

Expand All @@ -86,6 +90,10 @@ Use following conventions for JavaScript and TypeScript code.
- Move reusable callbacks outside loops
- Keep `try/catch`, spread/rest away from hot loops
- Use `Map` for dynamic key dictionaries, plain objects for fixed schemas
- Build a lookup once: a module-level `Set` for membership, a `Map` for keyed search, instead of
repeated linear scans (`.includes`, a `.find` per call)
- Keep caches and metadata on objects you do not own (a message, a config) in a `WeakMap` or
`WeakSet` rather than in a property on them
- Use `Set` for big and dynamic collections, prefer arrays for short and stable
- Use `Object.create(null)` for pure dictionaries
- Use typed arrays for numeric and binary workloads
Expand Down
15 changes: 9 additions & 6 deletions .claude/skills/npm-publish/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,14 @@ pnpm run lint # oxlint
pnpm run format:check # oxfmt
pnpm run test:coverage # tests under the c8 coverage gate
pnpm run test:types # tsd
pnpm run check:dts # tsc over index.d.ts
pnpm run check:dts # tsc over the d.ts of every entry of the exports map
pnpm size --check # the budgets of scripts/size-budgets.json
pnpm run check:pack # the tarball: every file of the exports map, nothing else
```

These are the same gates `prepublishOnly` runs. CI adds three more; run them too:
These are the same gates `prepublishOnly` runs. CI adds two more; run them too:

```bash
pnpm size --max-gzip 4 # the bundle budget
pnpm docs:comparison && git diff --exit-code docs/guide/comparison.md # the table matches docs/comparison.json
pnpm run docs:build # the docs site; a dead link fails it
```
Expand All @@ -58,7 +59,7 @@ Compute the new version based on user intent and semver rules:
- **prerelease**: `1.2.3` → `1.2.4-<label>.0`, or `1.2.4-beta.0` → `1.2.4-beta.1`
- **Removing prerelease tag**: `4.0.3-prerelease` → `4.0.3` (just strip the suffix)

Update `"version"` in `package.json` with the Edit tool, not `pnpm version`: the bump is a plain edit, and the commit, tag and changelog are handled by hand in the steps below.
Update `"version"` in `package.json` with the Edit tool, not `pnpm version`: the bump is a plain edit, and the commit, tag and changelog are handled by hand in the steps below. Bump `version` in `src/version.js` to the same value (the OpenTelemetry instrumentation scope reads it; `tests/unit/version.test.js` fails while the two differ).

## Step 3: Update CHANGELOG.md

Expand Down Expand Up @@ -163,10 +164,12 @@ Check these fields are correct:

```bash
pnpm pack --dry-run
pnpm run check:pack
pnpm run size
```

Review the file list:
`check:pack` fails on a file of the exports map missing from the tarball or a file outside the
`files` allowlist; review the file list anyway:

- Source files (`src/`) and root entry points are included
- Type definitions are included
Expand All @@ -180,7 +183,7 @@ Report the file count and the bundle size from `pnpm run size` to the user.
Run the gates one more time after all edits:

```bash
pnpm run lint && pnpm run format:check && pnpm run test:coverage && pnpm run test:types && pnpm run check:dts && pnpm size --max-gzip 4 && pnpm run docs:build
pnpm run lint && pnpm run format:check && pnpm run test:coverage && pnpm run test:types && pnpm run check:dts && pnpm size --check && pnpm run check:pack && pnpm run docs:build
```

Must exit 0. If it fails, fix and re-run.
Expand Down
7 changes: 7 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# LF everywhere, also on Windows checkouts: MIME output, DKIM canonicalization
# and the SMTP dot-stuffing tests compare bytes.
* text=auto eol=lf

# Fixtures compared byte for byte are never converted.
*.pem -text
*.eml -text
10 changes: 7 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,13 @@ jobs:
- run: pnpm install --frozen-lockfile
- run: pnpm lint
- run: pnpm format:check
# Smoke: keeps the bundle-size script working and fails over the
# min+gzip budget, which also catches a third-party module bundled in.
- run: pnpm size --max-gzip 4
# Fails over the min+gzip budget of an entry in scripts/size-budgets.json,
# which also catches a third-party module bundled in, and on an entry of
# the exports map without a budget.
- run: pnpm size --check
# The tarball holds every file of the exports map and nothing outside
# the files allowlist.
- run: pnpm run check:pack

types:
runs-on: ubuntu-latest
Expand Down
90 changes: 88 additions & 2 deletions .oxlintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -255,13 +255,99 @@
"no-setter-return": "error",
"no-unused-private-class-members": "error",
"no-constant-binary-expression": "error",
"no-console": "error"
"no-console": "error",
"func-style": [
"error",
"expression"
],
"no-nested-ternary": "error",
"max-depth": [
"error",
{
"max": 4
}
]
},
"overrides": [
{
"files": [
"src/**"
],
"plugins": [
"unicorn"
],
"rules": {
"no-restricted-properties": [
"error",
{
"property": "map",
"message": "src/ iterates with loops in one pass (CLAUDE.md, Conventions)."
},
{
"property": "filter",
"message": "src/ iterates with loops in one pass (CLAUDE.md, Conventions)."
},
{
"property": "some",
"message": "src/ iterates with loops in one pass (CLAUDE.md, Conventions)."
},
{
"property": "every",
"message": "src/ iterates with loops in one pass (CLAUDE.md, Conventions)."
},
{
"property": "find",
"message": "src/ iterates with loops in one pass (CLAUDE.md, Conventions)."
},
{
"property": "findIndex",
"message": "src/ iterates with loops in one pass (CLAUDE.md, Conventions)."
},
{
"property": "findLast",
"message": "src/ iterates with loops in one pass (CLAUDE.md, Conventions)."
},
{
"property": "findLastIndex",
"message": "src/ iterates with loops in one pass (CLAUDE.md, Conventions)."
},
{
"property": "reduce",
"message": "src/ iterates with loops in one pass (CLAUDE.md, Conventions)."
},
{
"property": "reduceRight",
"message": "src/ iterates with loops in one pass (CLAUDE.md, Conventions)."
},
{
"property": "flatMap",
"message": "src/ iterates with loops in one pass (CLAUDE.md, Conventions)."
},
{
"property": "forEach",
"message": "src/ iterates with loops in one pass (CLAUDE.md, Conventions)."
}
],
"max-lines-per-function": [
"warn",
{
"max": 80,
"skipBlankLines": true,
"skipComments": true
}
],
"unicorn/no-array-for-each": "error",
"unicorn/no-array-reduce": "error",
"unicorn/no-useless-spread": "error",
"unicorn/prefer-set-has": "error"
}
},
{
"files": [
"scripts/**",
"bench/**"
"bench/**",
"tests/e2e/**",
"tests/helpers/**"
],
"rules": {
"no-console": "off"
Expand Down
Loading
Loading