@blamejs/core 0.11.39 → 0.11.40

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.
package/CHANGELOG.md CHANGED
@@ -8,6 +8,8 @@ upgrading across more than a few patches at a time.
8
8
 
9
9
  ## v0.11.x
10
10
 
11
+ - v0.11.40 (2026-05-21) — **Wiki Docker default port moves from 8080 → 3008.** The `examples/wiki` Docker stack now defaults to port 3008 throughout — `WIKI_PORT`, the Dockerfile `EXPOSE` directive, the in-container HEALTHCHECK URL, the dev `docker-compose.yml` host mapping, the Caddy reverse-proxy upstream, and the `server.js` + `lib/build-app.js` code defaults. Production deployments (`docker-compose.prod.yml`) are operator-invisible since Caddy fronts the container on 80/443 — the upstream port is never exposed to the host. Dev users hitting the wiki container directly now use `http://localhost:3008`. Operators who set `WIKI_PORT` explicitly in their `.env` keep working unchanged. **Changed:** *Wiki default port 8080 → 3008* — Default `WIKI_PORT` moves from 8080 (HTTP-alt, frequently collides with Tomcat / Jenkins / Confluence / Spring-Boot defaults) to 3008 (IANA-unassigned, no service-convention collision). The new default applies consistently across: `examples/wiki/Dockerfile` (`ENV WIKI_PORT`, `EXPOSE`, HEALTHCHECK URL), `examples/wiki/server.js`, `examples/wiki/lib/build-app.js`, `examples/wiki/docker-compose.yml` (host mapping `3008:3008`), `examples/wiki/docker-compose.prod.yml` (internal-network expose), `examples/wiki/Caddyfile` (upstream resolver), `examples/wiki/README.md`, `examples/wiki/DEPLOY.md`. · *Operator action* — Dev users with `localhost:8080` bookmarks, curl scripts, host-firewall allowlists, or IDE port forwarders pointed at the wiki need to switch to `localhost:3008`. Operators who set `WIKI_PORT=8080` explicitly in their `.env` keep working unchanged. Production deployments behind Caddy see no change — the upstream port is internal-network only. **References:** [IANA Service Name and Port Number Registry](https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml)
12
+
11
13
  - v0.11.39 (2026-05-21) — **`b.calendar.expandRecurrence` honors multiple `recurrenceRules` (RFC 8984 §4.3.2).** Closes the v0.11.31 deferral on the multi-rule path. `expandRecurrence` now walks every entry in `event.recurrenceRules`, expands each rule independently against the same `start` anchor, and UNIONs the resulting instances (dedup + sorted ascending). Per-rule `count` caps apply per-rule per RFC 8984 §4.3.2; the global `max` / `MAX_EXPAND_INSTANCES` cap applies to the unioned set. The step budget (`MAX_EXPAND_INSTANCES * 366`) is shared across all rules in the same expand call so an N-rule fan-out can't amplify the worst-case loop past the single-rule bound. **Added:** *Multi-rule expansion + UNION* — `event.recurrenceRules` of length > 1 now expands every rule, not just the first. Each rule's stepping (frequency / interval / count / until / BY* filters) operates independently; the resulting ISO 8601 UTC instant strings dedupe via object-keyed set semantics, then sort ascending. The single-rule case is structurally unchanged — same step loop, same per-rule cap behaviour. · *Per-rule `count` applies per-rule (RFC 8984 §4.3.2)* — Two rules with `count: 3` and `count: 2` produce up to 5 instances in the unioned output (minus any timestamps that dedupe across rules), not 5 instances total across the rules. Matches RFC 8984 §4.3.2 phrasing: each Recurrence Rule's count is applied per rule. · *Global step budget shared across rules* — The `MAX_EXPAND_INSTANCES * 366` step budget (introduced in v0.11.31) now tracks across rules in the same expand call. A 4-rule event with sparse BY* filters can't accumulate 4× the single-rule worst-case work; the budget is depleted across the full rule set. **Security:** *DoS amplification across N rules bounded by the same step budget* — The step budget is shared (not per-rule), so adversarial multi-rule events can't bypass the v0.11.31 BY*-filter cap by stacking N rules with sparse filters. Sparse-filter rules still complete within the original 10-year `MAX_EXPAND_SPAN_MS` window cap. **Detectors:** *No new detector — covered by existing recurrence-test surface* — Multi-rule UNION + dedup + global step budget are exercised by three new tests in `test/layer-0-primitives/calendar.test.js` (multi-rule union, same-rule dedup, global max applied to union). No detector needed — the bug class is testable end-to-end and the test file IS the canonical regression surface. **References:** [RFC 8984 §4.3.2 (JSCalendar RecurrenceRule — multi-rule expansion)](https://www.rfc-editor.org/rfc/rfc8984.html#section-4.3.2) · [RFC 5545 §3.8.5.3 (iCalendar RRULE — semantics under composition)](https://www.rfc-editor.org/rfc/rfc5545.html#section-3.8.5.3)
12
14
 
13
15
  - v0.11.38 (2026-05-21) — **`b.mail.server.jmap.emailSubmissionSetHandler` — reference JMAP EmailSubmission/set composing `b.mail.send.deliver`.** Reference implementation of JMAP `EmailSubmission/set` (RFC 8621 §7.5) that composes `b.mail.send.deliver` (v0.11.24). Operators wire it as a method handler on `b.mail.server.jmap.create({ methods: ... })`. The handler walks `args.create`, validates each EmailSubmission's shape against the RFC 8621 §7.5 vocabulary (`identityId` / `emailId` / `envelope.mailFrom.email` / `envelope.rcptTo[]`), looks up the referenced Email blob via an operator-supplied `lookupEmail(emailId, accountId, actor)`, hands the RFC 822 body to `deliver(envelope)`, and maps the result into JMAP `deliveryStatus` (`recipient → { smtpReply, delivered, displayed }` per RFC 8621 §7.4). Closes the v0.11.24 deferral on a reference JMAP→deliver bridge. **Added:** *`b.mail.server.jmap.emailSubmissionSetHandler(opts)` factory* — Returns an async `(actor, args, ctx) → result` function suitable for the `opts.methods` map on `b.mail.server.jmap.create`. Required opts: `deliver` (a `b.mail.send.deliver` instance), `lookupEmail` (async `(emailId, accountId, actor) → Buffer|null`), `identities` (sync `(accountId) → Array<{ id, email }>`). Optional: `onCreated(subId, submission, accountId)` for persistence, `onDestroyed(subId, accountId)`, `onCancel(subId, accountId) → boolean` for undo support, `maxRecipients` (default 1000). · *Full RFC 8621 §7.5 error vocabulary* — Refusals map to the spec's typed `notCreated` shape with JMAP-namespaced types: `identityNotFound` (unknown identityId), `emailNotFound` (lookupEmail returned null), `forbiddenMailFrom` (envelope.mailFrom doesn't match identity.email), `invalidRecipients` (malformed envelope.rcptTo[i].email), `noRecipients` (empty rcptTo), `tooManyRecipients` (exceeds maxRecipients), `invalidProperties` (missing required keys). Per RFC 8621 §3.6.2, only `undoStatus` is honored in `args.update`; non-canceled values + non-undoStatus keys return `invalidProperties`. · *Delivery-result → JMAP deliveryStatus mapping* — `b.mail.send.deliver`'s `{ delivered, deferred, failed }` outcomes translate into the per-recipient JMAP deliveryStatus shape: `delivered` → `{ smtpReply, delivered: "yes" }`; `deferred` → `{ smtpReply, delivered: "queued" }`; `failed` → `{ smtpReply, delivered: "no" }`. `displayed` is `unknown` until a downstream MDN (RFC 9007) reports otherwise — operators with MDN ingest update via `EmailSubmission/set`'s update branch. · *Undo via `onCancel` hook* — `args.update[subId] = { undoStatus: "canceled" }` invokes `opts.onCancel(subId, accountId) → boolean`. Operators backing the JMAP server with a deferred-send queue (e.g. `b.outbox`) return true when the cancel succeeded; the handler refuses with `cannotUnsend` when `onCancel` isn't configured (operator hasn't wired a queue-based send model). · *Audit event on every set* — Emits `mail.jmap.emailsubmission.set` with `{ accountId, created, notCreated, updated, notUpdated, destroyed, notDestroyed }` counts. The metadata never carries recipient email addresses or RFC 822 body bytes — those stay in the operator's deliver primitive's per-host audit stream. **Security:** *Identity binding refuses spoofed `envelope.mailFrom`* — RFC 8621 §7.5.1.2 — every create gates `envelope.mailFrom.email` against the identity record's `email` field. The reference handler refuses with `forbiddenMailFrom` when they differ. Operators wiring a delegation model populate the identity's `mayDelegate` / per-domain authorized-senders list and supply the corresponding `identities(accountId)` lookup; the reference handler treats the lookup output as the trust source. · *Recipient count cap matches `b.mail.send.deliver`* — Default `maxRecipients = 1000` aligns with `b.mail.send.deliver`'s recipient-fan-out cap; operators reduce it for tighter postures (e.g. 50 for transactional-mail accounts) without weakening the deliver primitive's own gate. **Detectors:** *Two new KNOWN_CLUSTERS family-subset entries* — The new handler's opts-walk shingle structurally resembles `lib/importmap-integrity.js:build` (SRI module-map walk) and `lib/middleware/security-headers.js:create` (header-map walk). Added explanatory family-subset entries documenting why each primitive's per-entry body validates a distinct spec vocabulary (W3C Importmap-Integrity vs RFC 8621 §7.5 EmailSubmission vs HTTP header-value sanitisation) so the dup detector can't surface the false-positive again. **References:** [RFC 8621 §7 (JMAP EmailSubmission)](https://www.rfc-editor.org/rfc/rfc8621.html#section-7) · [RFC 8621 §7.5 (EmailSubmission/set)](https://www.rfc-editor.org/rfc/rfc8621.html#section-7.5) · [RFC 8621 §7.4 (deliveryStatus shape)](https://www.rfc-editor.org/rfc/rfc8621.html#section-7.4) · [RFC 8620 §3.6.1 (JMAP error vocabulary)](https://www.rfc-editor.org/rfc/rfc8620.html#section-3.6.1)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/core",
3
- "version": "0.11.39",
3
+ "version": "0.11.40",
4
4
  "description": "The Node framework that owns its stack.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "blamejs contributors",
package/sbom.cdx.json CHANGED
@@ -2,10 +2,10 @@
2
2
  "$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
3
3
  "bomFormat": "CycloneDX",
4
4
  "specVersion": "1.5",
5
- "serialNumber": "urn:uuid:9bd69988-4e90-47bf-a67c-b9301b0918ca",
5
+ "serialNumber": "urn:uuid:1f82638d-96d9-44e6-9642-4d336ee1d858",
6
6
  "version": 1,
7
7
  "metadata": {
8
- "timestamp": "2026-05-21T23:30:40.324Z",
8
+ "timestamp": "2026-05-22T00:19:00.297Z",
9
9
  "lifecycles": [
10
10
  {
11
11
  "phase": "build"
@@ -19,14 +19,14 @@
19
19
  }
20
20
  ],
21
21
  "component": {
22
- "bom-ref": "@blamejs/core@0.11.39",
22
+ "bom-ref": "@blamejs/core@0.11.40",
23
23
  "type": "application",
24
24
  "name": "blamejs",
25
- "version": "0.11.39",
25
+ "version": "0.11.40",
26
26
  "scope": "required",
27
27
  "author": "blamejs contributors",
28
28
  "description": "The Node framework that owns its stack.",
29
- "purl": "pkg:npm/%40blamejs/core@0.11.39",
29
+ "purl": "pkg:npm/%40blamejs/core@0.11.40",
30
30
  "properties": [],
31
31
  "externalReferences": [
32
32
  {
@@ -54,7 +54,7 @@
54
54
  "components": [],
55
55
  "dependencies": [
56
56
  {
57
- "ref": "@blamejs/core@0.11.39",
57
+ "ref": "@blamejs/core@0.11.40",
58
58
  "dependsOn": []
59
59
  }
60
60
  ]