@wtfalch/email 0.4.1 → 0.6.0

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.
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Handing a blob to the browser as a file.
3
+ *
4
+ * Its own module, and in the react folder rather than beside `download.ts`,
5
+ * because this half is pure DOM: `download.ts` fetches bytes and runs
6
+ * anywhere, this needs a `document`. Splitting them keeps the fetch testable
7
+ * in Node and keeps an application that wants to do its own saving — write to
8
+ * a directory handle, open in a viewer, upload somewhere else — able to take
9
+ * the bytes and skip this.
10
+ */
11
+ /**
12
+ * Save a blob under a filename.
13
+ *
14
+ * **The object URL is revoked on a timer, not immediately.** Revoking in the
15
+ * same tick as the click cancels the download in Chrome and Safari: the
16
+ * anchor's activation is queued, and by the time the browser reads the URL it
17
+ * has already been released. A tick is the usual fix and is still a race; a
18
+ * minute is not, and an object URL is a pointer to memory the page already
19
+ * holds, so holding it a little longer costs nothing.
20
+ *
21
+ * **The anchor is not appended in Firefox's case only.** A detached anchor's
22
+ * click is ignored there, so it goes into the document and comes straight
23
+ * back out, which is invisible and works everywhere.
24
+ */
25
+ export function saveBlob(blob, name) {
26
+ if (typeof document === 'undefined') {
27
+ throw new Error('saveBlob needs a document; call it from the browser');
28
+ }
29
+ const url = URL.createObjectURL(blob);
30
+ const anchor = document.createElement('a');
31
+ anchor.href = url;
32
+ /* A filename with a path separator in it is a filename a server chose, and
33
+ browsers vary in how much of it they honour. Only the last segment is
34
+ ever a name. */
35
+ anchor.download = name.split(/[\\/]/).pop() || 'attachment';
36
+ anchor.rel = 'noopener';
37
+ anchor.style.display = 'none';
38
+ document.body.appendChild(anchor);
39
+ anchor.click();
40
+ anchor.remove();
41
+ window.setTimeout(() => URL.revokeObjectURL(url), 60_000);
42
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wtfalch/email",
3
- "version": "0.4.1",
3
+ "version": "0.6.0",
4
4
  "description": "The wtfalch estate: reading a mailbox over JMAP, and administering the Stalwart server it lives on. Two entries with no code in common.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -42,7 +42,7 @@
42
42
  "record-fixtures": "node scripts/record-fixtures.mjs"
43
43
  },
44
44
  "peerDependencies": {
45
- "@wtfalch/design": ">=0.4.0",
45
+ "@wtfalch/design": ">=0.5.0",
46
46
  "react": "^19.0.0",
47
47
  "react-dom": "^19.0.0"
48
48
  },
@@ -61,7 +61,7 @@
61
61
  "@types/node": "^22",
62
62
  "@types/react": "^19",
63
63
  "@types/react-dom": "^19",
64
- "@wtfalch/design": "^0.4.0",
64
+ "@wtfalch/design": "^0.7.0",
65
65
  "jsdom": "^30.0.1",
66
66
  "react": "^19",
67
67
  "react-dom": "^19",