@autonomys/file-server 1.6.2 → 1.6.3

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.
@@ -1,6 +1,6 @@
1
1
  import { Request, Response } from 'express';
2
2
  import { ByteRange, DownloadMetadata, DownloadOptions } from '../models.js';
3
- export declare const handleDownloadResponseHeaders: (req: Request, res: Response, metadata: DownloadMetadata, { byteRange, rawMode }: DownloadOptions) => void;
3
+ export declare const handleDownloadResponseHeaders: (req: Request, res: Response, metadata: DownloadMetadata, options: DownloadOptions) => void;
4
4
  export declare const handleS3DownloadResponseHeaders: (req: Request, res: Response, metadata: DownloadMetadata) => void;
5
5
  export declare const getByteRange: (req: Request) => ByteRange | undefined;
6
6
  //# sourceMappingURL=headers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"headers.d.ts","sourceRoot":"","sources":["../../src/http/headers.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAC3C,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AA8C3E,eAAO,MAAM,6BAA6B,GACxC,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,UAAU,gBAAgB,EAC1B,wBAA4C,eAAe,SAoC5D,CAAA;AAED,eAAO,MAAM,+BAA+B,GAC1C,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,UAAU,gBAAgB,SAS3B,CAAA;AAED,eAAO,MAAM,YAAY,GAAI,KAAK,OAAO,KAAG,SAAS,GAAG,SAgBvD,CAAA"}
1
+ {"version":3,"file":"headers.d.ts","sourceRoot":"","sources":["../../src/http/headers.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAC3C,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAS3E,eAAO,MAAM,6BAA6B,GACxC,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,UAAU,gBAAgB,EAC1B,SAAS,eAAe,SAsBzB,CAAA;AA+CD,eAAO,MAAM,+BAA+B,GAC1C,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,UAAU,gBAAgB,SAS3B,CAAA;AAED,eAAO,MAAM,YAAY,GAAI,KAAK,OAAO,KAAG,SAAS,GAAG,SAgBvD,CAAA"}
@@ -1,73 +1,45 @@
1
1
  import { CompressionAlgorithm, EncryptionAlgorithm } from '@autonomys/auto-dag-data';
2
- // Check if this is actually a document navigation (based on headers only)
3
- // Used to determine if browser will auto-decompress Content-Encoding
4
- const isDocumentNavigation = (req) => {
5
- const destHeader = req.headers['sec-fetch-dest'];
6
- const dest = (Array.isArray(destHeader) ? destHeader[0] : (destHeader !== null && destHeader !== void 0 ? destHeader : '')).toLowerCase();
7
- if (dest && dest !== 'document')
8
- return false; // e.g. <img>, fetch(), etc.
9
- const modeHeader = req.headers['sec-fetch-mode'];
10
- const mode = (Array.isArray(modeHeader) ? modeHeader[0] : (modeHeader !== null && modeHeader !== void 0 ? modeHeader : '')).toLowerCase();
11
- if (mode && mode !== 'navigate')
12
- return false; // programmatic fetch
13
- return true;
2
+ const isExpectedDocument = (req) => {
3
+ return (req.headers['sec-fetch-site'] === 'none' ||
4
+ (req.headers['sec-fetch-site'] === 'same-site' && req.headers['sec-fetch-mode'] === 'navigate'));
14
5
  };
15
- const isInlineDocument = (req) => {
16
- // Check explicit query parameters - treat presence as boolean flag
17
- // ?download or ?download=true triggers attachment, ?download=false is ignored
18
- if (req.query.download === 'true' || req.query.download === '')
19
- return false;
20
- // ?inline or ?inline=true triggers inline, ?inline=false is ignored
21
- if (req.query.inline === 'true' || req.query.inline === '')
22
- return true;
23
- // Fall back to header-based detection
24
- return isDocumentNavigation(req);
25
- };
26
- // Helper to create an ASCII-safe fallback for filename parameter (RFC 2183/6266)
27
- const toAsciiFallback = (name) => name
28
- .replace(/[^\x20-\x7E]+/g, '_') // replace non-ASCII with underscore
29
- .replace(/["\\]/g, '\\$&'); // escape quotes and backslashes
30
- // RFC 5987 encoding for filename* parameter
31
- const rfc5987Encode = (str) => encodeURIComponent(str)
32
- .replace(/['()]/g, (c) => '%' + c.charCodeAt(0).toString(16).toUpperCase())
33
- .replace(/\*/g, '%2A');
34
- const buildDisposition = (req, filename) => {
35
- const fallbackName = toAsciiFallback(filename || 'download');
36
- const encoded = rfc5987Encode(filename || 'download');
37
- const type = isInlineDocument(req) ? 'inline' : 'attachment';
38
- return `${type}; filename="${fallbackName}"; filename*=UTF-8''${encoded}`;
39
- };
40
- export const handleDownloadResponseHeaders = (req, res, metadata, { byteRange = undefined, rawMode = false }) => {
41
- var _a;
42
- const baseName = metadata.name || 'download';
43
- const fileName = metadata.type === 'file' ? baseName : `${baseName}.zip`;
6
+ export const handleDownloadResponseHeaders = (req, res, metadata, options) => {
7
+ const safeName = encodeURIComponent(metadata.name || 'download');
8
+ const documentExpected = isExpectedDocument(req);
9
+ const shouldHandleEncoding = req.query.ignoreEncoding
10
+ ? req.query.ignoreEncoding !== 'true'
11
+ : documentExpected;
12
+ const isEncrypted = metadata.isEncrypted;
44
13
  if (metadata.type === 'file') {
45
- const contentType = (!metadata.isEncrypted && !rawMode && metadata.mimeType) || 'application/octet-stream';
46
- res.set('Content-Type', contentType);
47
- const compressedButNoEncrypted = metadata.isCompressed && !metadata.isEncrypted;
48
- // Only set Content-Encoding for document navigations where browsers auto-decompress
49
- // Don't set it for <img>, fetch(), etc. as browsers won't auto-decompress those
50
- const shouldHandleEncoding = req.query.ignoreEncoding
51
- ? req.query.ignoreEncoding !== 'true'
52
- : isDocumentNavigation(req);
53
- if (compressedButNoEncrypted && shouldHandleEncoding && !rawMode && !byteRange) {
54
- res.set('Content-Encoding', 'deflate');
55
- }
56
- if (byteRange) {
57
- res.status(206);
58
- res.set('Content-Range', `bytes ${byteRange[0]}-${byteRange[1]}/${metadata.size}`);
59
- const upperBound = (_a = byteRange[1]) !== null && _a !== void 0 ? _a : Number(metadata.size) - 1;
60
- res.set('Content-Length', (upperBound - byteRange[0] + 1).toString());
61
- }
62
- else if (metadata.size) {
63
- res.set('Content-Length', metadata.size.toString());
64
- }
14
+ setFileResponseHeaders(res, metadata, isEncrypted, documentExpected, shouldHandleEncoding, safeName, options);
65
15
  }
66
16
  else {
67
- const contentType = metadata.isEncrypted ? 'application/octet-stream' : 'application/zip';
68
- res.set('Content-Type', contentType);
17
+ setFolderResponseHeaders(res, isEncrypted, documentExpected, safeName);
69
18
  }
70
- res.set('Content-Disposition', buildDisposition(req, fileName));
19
+ };
20
+ const setFileResponseHeaders = (res, metadata, isEncrypted, isExpectedDocument, shouldHandleEncoding, safeName, { byteRange = undefined, rawMode = false }) => {
21
+ var _a;
22
+ const contentType = (!isEncrypted && !rawMode && metadata.mimeType) || 'application/octet-stream';
23
+ res.set('Content-Type', contentType);
24
+ res.set('Content-Disposition', `${isExpectedDocument ? 'inline' : 'attachment'}; filename="${safeName}"`);
25
+ const compressedButNoEncrypted = metadata.isCompressed && !isEncrypted;
26
+ if (compressedButNoEncrypted && shouldHandleEncoding && !rawMode && !byteRange) {
27
+ res.set('Content-Encoding', 'deflate');
28
+ }
29
+ if (byteRange) {
30
+ res.status(206);
31
+ res.set('Content-Range', `bytes ${byteRange[0]}-${byteRange[1]}/${metadata.size}`);
32
+ const upperBound = (_a = byteRange[1]) !== null && _a !== void 0 ? _a : Number(metadata.size) - 1;
33
+ res.set('Content-Length', (upperBound - byteRange[0] + 1).toString());
34
+ }
35
+ else if (metadata.size) {
36
+ res.set('Content-Length', metadata.size.toString());
37
+ }
38
+ };
39
+ const setFolderResponseHeaders = (res, isEncrypted, isExpectedDocument, safeName) => {
40
+ const contentType = isEncrypted ? 'application/octet-stream' : 'application/zip';
41
+ res.set('Content-Type', contentType);
42
+ res.set('Content-Disposition', `${isExpectedDocument ? 'inline' : 'attachment'}; filename="${safeName}.zip"`);
71
43
  };
72
44
  export const handleS3DownloadResponseHeaders = (req, res, metadata) => {
73
45
  if (metadata.isEncrypted) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@autonomys/file-server",
3
3
  "packageManager": "yarn@4.7.0",
4
- "version": "1.6.2",
4
+ "version": "1.6.3",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "repository": {
@@ -19,8 +19,7 @@
19
19
  "node": ">=20.8.0"
20
20
  },
21
21
  "scripts": {
22
- "build": "yarn tsc",
23
- "test": "node --experimental-vm-modules ../../../node_modules/jest/bin/jest.js --config ./jest.config.cjs"
22
+ "build": "yarn tsc"
24
23
  },
25
24
  "main": "./dist/index.js",
26
25
  "types": "./dist/index.d.ts",
@@ -48,19 +47,19 @@
48
47
  "typescript": "^5.8.3"
49
48
  },
50
49
  "dependencies": {
51
- "@autonomys/asynchronous": "^1.6.2",
52
- "@autonomys/auto-dag-data": "^1.6.2",
53
- "@autonomys/auto-utils": "^1.6.2",
50
+ "@autonomys/asynchronous": "^1.6.3",
51
+ "@autonomys/auto-dag-data": "^1.6.3",
52
+ "@autonomys/auto-utils": "^1.6.3",
54
53
  "@keyvhq/sqlite": "^2.1.7",
55
54
  "cache-manager": "^6.4.2",
56
55
  "express": "^4.19.2",
57
56
  "jszip": "^3.10.1",
58
57
  "keyv": "^5.3.2",
59
- "mime-types": "^3.0.1",
58
+ "mime-types": "^3.0.2",
60
59
  "process": "^0.11.10",
61
60
  "stream": "^0.0.3",
62
61
  "uuid": "^11.1.0",
63
62
  "zod": "^3.24.2"
64
63
  },
65
- "gitHead": "910aff6c92f736b87f7986780d667035b9c72217"
64
+ "gitHead": "b61097a2c527e541085e618db096d517dfa29c78"
66
65
  }