@autonomys/file-server 1.5.14

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,19 @@
1
+ import Keyv from 'keyv';
2
+ export declare const defaultMemoryAndSqliteConfig: ({ dirname, cacheMaxSize, cacheTtl, }: {
3
+ dirname: string;
4
+ cacheMaxSize: number;
5
+ cacheTtl: number;
6
+ }) => {
7
+ cacheDir: string;
8
+ pathPartitions: number;
9
+ stores: Keyv<any>[];
10
+ };
11
+ export declare const defaultInitMemoryConfig: ({ dirname, cacheMaxSize, }: {
12
+ dirname: string;
13
+ cacheMaxSize: number;
14
+ }) => {
15
+ cacheDir: string;
16
+ pathPartitions: number;
17
+ stores: Keyv<any>[];
18
+ };
19
+ //# sourceMappingURL=defaultConfigs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaultConfigs.d.ts","sourceRoot":"","sources":["../../src/caching/defaultConfigs.ts"],"names":[],"mappings":"AAEA,OAAO,IAAI,MAAM,MAAM,CAAA;AAKvB,eAAO,MAAM,4BAA4B,GAAI,sCAI1C;IACD,OAAO,EAAE,MAAM,CAAA;IACf,YAAY,EAAE,MAAM,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAA;CACjB;;;;CA2BA,CAAA;AAED,eAAO,MAAM,uBAAuB,GAAI,4BAGrC;IACD,OAAO,EAAE,MAAM,CAAA;IACf,YAAY,EAAE,MAAM,CAAA;CACrB;;;;CAoBA,CAAA"}
@@ -0,0 +1,55 @@
1
+ import { stringify } from '@autonomys/auto-utils';
2
+ import KeyvSqlite from '@keyvhq/sqlite';
3
+ import Keyv from 'keyv';
4
+ import { LRUCache } from 'lru-cache';
5
+ import path from 'path';
6
+ import { ensureDirectoryExists } from './utils.js';
7
+ export const defaultMemoryAndSqliteConfig = ({ dirname, cacheMaxSize, cacheTtl, }) => {
8
+ const cacheDir = ensureDirectoryExists(path.join(dirname, 'files'));
9
+ return {
10
+ cacheDir,
11
+ pathPartitions: 3,
12
+ stores: [
13
+ new Keyv({
14
+ serialize: stringify,
15
+ store: new LRUCache({
16
+ maxSize: cacheMaxSize,
17
+ maxEntrySize: Number.MAX_SAFE_INTEGER,
18
+ sizeCalculation: (value) => {
19
+ var _a;
20
+ const { value: parsedValue } = JSON.parse(value);
21
+ return Number((_a = parsedValue === null || parsedValue === void 0 ? void 0 : parsedValue.size) !== null && _a !== void 0 ? _a : 0);
22
+ },
23
+ }),
24
+ }),
25
+ new Keyv({
26
+ store: new KeyvSqlite({
27
+ uri: path.join(cacheDir, 'files.sqlite'),
28
+ }),
29
+ ttl: cacheTtl,
30
+ serialize: stringify,
31
+ }),
32
+ ],
33
+ };
34
+ };
35
+ export const defaultInitMemoryConfig = ({ dirname, cacheMaxSize, }) => {
36
+ const cacheDir = ensureDirectoryExists(path.join(dirname, 'files'));
37
+ return {
38
+ cacheDir,
39
+ pathPartitions: 3,
40
+ stores: [
41
+ new Keyv({
42
+ serialize: stringify,
43
+ store: new LRUCache({
44
+ maxSize: cacheMaxSize,
45
+ maxEntrySize: Number.MAX_SAFE_INTEGER,
46
+ sizeCalculation: (value) => {
47
+ var _a;
48
+ const { value: parsedValue } = JSON.parse(value);
49
+ return Number((_a = parsedValue === null || parsedValue === void 0 ? void 0 : parsedValue.size) !== null && _a !== void 0 ? _a : 0);
50
+ },
51
+ }),
52
+ }),
53
+ ],
54
+ };
55
+ };
@@ -0,0 +1,8 @@
1
+ import { BaseCacheConfig, FileCacheOptions, FileResponse } from '../models.js';
2
+ export declare const createFileCache: (config: BaseCacheConfig) => {
3
+ get: (cid: string, options?: FileCacheOptions) => Promise<FileResponse | null>;
4
+ set: (cid: string, fileResponse: FileResponse) => Promise<void>;
5
+ has: (cid: string) => Promise<boolean>;
6
+ remove: (cid: string) => Promise<void>;
7
+ };
8
+ //# sourceMappingURL=fileCache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fileCache.d.ts","sourceRoot":"","sources":["../../src/caching/fileCache.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAS9E,eAAO,MAAM,eAAe,GAAI,QAAQ,eAAe;eA8B7B,MAAM,YAAY,gBAAgB,KAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;eAyBjE,MAAM,gBAAgB,YAAY;eARlC,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC;kBAoBtB,MAAM;CA0BlC,CAAA"}
@@ -0,0 +1,98 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ var __rest = (this && this.__rest) || function (s, e) {
11
+ var t = {};
12
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
13
+ t[p] = s[p];
14
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
15
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
16
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
17
+ t[p[i]] = s[p[i]];
18
+ }
19
+ return t;
20
+ };
21
+ import { createCache } from 'cache-manager';
22
+ import fs from 'fs';
23
+ import fsPromises from 'fs/promises';
24
+ import path from 'path';
25
+ import { writeFile } from './utils.js';
26
+ const CHARS_PER_PARTITION = 2;
27
+ export const createFileCache = (config) => {
28
+ const cidToFilePath = (cid) => {
29
+ const partitions = config.pathPartitions;
30
+ let filePath = '';
31
+ let head = cid;
32
+ for (let i = 0; i < partitions; i++) {
33
+ filePath = path.join(filePath, `${head.slice(-CHARS_PER_PARTITION)}/`);
34
+ head = head.slice(0, -CHARS_PER_PARTITION);
35
+ }
36
+ filePath = path.join(filePath, head, cid);
37
+ return path.join(config.cacheDir, filePath);
38
+ };
39
+ const filepathCache = createCache({
40
+ stores: config.stores,
41
+ nonBlocking: false,
42
+ });
43
+ const deserialize = (data) => {
44
+ var _a;
45
+ if (!data) {
46
+ return null;
47
+ }
48
+ return Object.assign(Object.assign({}, data), { size: BigInt((_a = data.size) !== null && _a !== void 0 ? _a : 0) });
49
+ };
50
+ const get = (cid, options) => __awaiter(void 0, void 0, void 0, function* () {
51
+ var _a, _b;
52
+ const data = deserialize(yield filepathCache.get(cid));
53
+ if (!data) {
54
+ return null;
55
+ }
56
+ const path = cidToFilePath(cid);
57
+ return Object.assign(Object.assign({}, data), { data: fs.createReadStream(path, {
58
+ start: (_a = options === null || options === void 0 ? void 0 : options.byteRange) === null || _a === void 0 ? void 0 : _a[0],
59
+ end: (_b = options === null || options === void 0 ? void 0 : options.byteRange) === null || _b === void 0 ? void 0 : _b[1],
60
+ }) });
61
+ });
62
+ const has = (cid) => __awaiter(void 0, void 0, void 0, function* () {
63
+ const path = cidToFilePath(cid);
64
+ return fsPromises
65
+ .access(path, fs.constants.F_OK)
66
+ .then(() => true)
67
+ .catch(() => false);
68
+ });
69
+ const set = (cid, fileResponse) => __awaiter(void 0, void 0, void 0, function* () {
70
+ const filePath = cidToFilePath(cid);
71
+ const { data } = fileResponse, rest = __rest(fileResponse, ["data"]);
72
+ yield writeFile(filePath, data);
73
+ yield filepathCache.set(cid, Object.assign({}, rest));
74
+ });
75
+ const remove = (cid) => __awaiter(void 0, void 0, void 0, function* () {
76
+ const data = deserialize(yield filepathCache.get(cid));
77
+ if (!data) {
78
+ return;
79
+ }
80
+ const path = cidToFilePath(cid);
81
+ yield Promise.all([filepathCache.del(cid), fsPromises.rm(path)]);
82
+ });
83
+ filepathCache.on('del', (_a) => __awaiter(void 0, [_a], void 0, function* ({ key, error }) {
84
+ if (error) {
85
+ console.error(`Error deleting file cache entry for ${key}: ${error}`);
86
+ }
87
+ else {
88
+ yield fsPromises.rm(cidToFilePath(key));
89
+ }
90
+ }));
91
+ const cache = {
92
+ get,
93
+ set,
94
+ has,
95
+ remove,
96
+ };
97
+ return cache;
98
+ };
@@ -0,0 +1,4 @@
1
+ export * from './defaultConfigs.js';
2
+ export * from './fileCache.js';
3
+ export * from './utils.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/caching/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAA;AACnC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,YAAY,CAAA"}
@@ -0,0 +1,3 @@
1
+ export * from './defaultConfigs.js';
2
+ export * from './fileCache.js';
3
+ export * from './utils.js';
@@ -0,0 +1,5 @@
1
+ import { Readable } from 'stream';
2
+ export declare const writeFile: (filepath: string, data: Readable, ensureDirectoryExistance?: boolean) => Promise<void>;
3
+ export declare const ensureDirectoryExists: (dir: string) => string;
4
+ export declare const asyncEnsureDirectoryExists: (dir: string) => Promise<string>;
5
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/caching/utils.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAGjC,eAAO,MAAM,SAAS,GACpB,UAAU,MAAM,EAChB,MAAM,QAAQ,EACd,2BAA0B,OAAc,kBAUzC,CAAA;AAED,eAAO,MAAM,qBAAqB,GAAI,KAAK,MAAM,WAKhD,CAAA;AAED,eAAO,MAAM,0BAA0B,GAAU,KAAK,MAAM,oBAU3D,CAAA"}
@@ -0,0 +1,37 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import fs from 'fs';
11
+ import fsPromises from 'fs/promises';
12
+ import path from 'path';
13
+ import { v4 } from 'uuid';
14
+ export const writeFile = (filepath_1, data_1, ...args_1) => __awaiter(void 0, [filepath_1, data_1, ...args_1], void 0, function* (filepath, data, ensureDirectoryExistance = true) {
15
+ const tempFilePath = path.join(path.dirname(filepath), `${v4()}.tmp`);
16
+ if (ensureDirectoryExistance) {
17
+ yield asyncEnsureDirectoryExists(path.dirname(tempFilePath));
18
+ }
19
+ yield fsPromises.writeFile(tempFilePath, data);
20
+ yield fsPromises.rename(tempFilePath, filepath);
21
+ });
22
+ export const ensureDirectoryExists = (dir) => {
23
+ if (!fs.existsSync(dir)) {
24
+ fs.mkdirSync(dir, { recursive: true });
25
+ }
26
+ return dir;
27
+ };
28
+ export const asyncEnsureDirectoryExists = (dir) => __awaiter(void 0, void 0, void 0, function* () {
29
+ const exists = yield fsPromises
30
+ .access(dir)
31
+ .then(() => true)
32
+ .catch(() => false);
33
+ if (!exists) {
34
+ yield fsPromises.mkdir(dir, { recursive: true });
35
+ }
36
+ return dir;
37
+ });
@@ -0,0 +1,19 @@
1
+ import Keyv from 'keyv';
2
+ export declare const defaultMemoryAndSqliteConfig: ({ dirname, cacheMaxSize, cacheTtl, }: {
3
+ dirname: string;
4
+ cacheMaxSize: number;
5
+ cacheTtl: number;
6
+ }) => {
7
+ cacheDir: string;
8
+ pathPartitions: number;
9
+ stores: Keyv<any>[];
10
+ };
11
+ export declare const defaultInitMemoryConfig: ({ dirname, cacheMaxSize, }: {
12
+ dirname: string;
13
+ cacheMaxSize: number;
14
+ }) => {
15
+ cacheDir: string;
16
+ pathPartitions: number;
17
+ stores: Keyv<any>[];
18
+ };
19
+ //# sourceMappingURL=defaultConfigs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaultConfigs.d.ts","sourceRoot":"","sources":["../src/defaultConfigs.ts"],"names":[],"mappings":"AAEA,OAAO,IAAI,MAAM,MAAM,CAAA;AAKvB,eAAO,MAAM,4BAA4B,GAAI,sCAI1C;IACD,OAAO,EAAE,MAAM,CAAA;IACf,YAAY,EAAE,MAAM,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAA;CACjB;;;;CA2BA,CAAA;AAED,eAAO,MAAM,uBAAuB,GAAI,4BAGrC;IACD,OAAO,EAAE,MAAM,CAAA;IACf,YAAY,EAAE,MAAM,CAAA;CACrB;;;;CAoBA,CAAA"}
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.defaultInitMemoryConfig = exports.defaultMemoryAndSqliteConfig = void 0;
7
+ const auto_utils_1 = require("@autonomys/auto-utils");
8
+ const sqlite_1 = __importDefault(require("@keyvhq/sqlite"));
9
+ const keyv_1 = __importDefault(require("keyv"));
10
+ const lru_cache_1 = require("lru-cache");
11
+ const path_1 = __importDefault(require("path"));
12
+ const utils_1 = require("./utils");
13
+ const defaultMemoryAndSqliteConfig = ({ dirname, cacheMaxSize, cacheTtl, }) => {
14
+ const cacheDir = (0, utils_1.ensureDirectoryExists)(path_1.default.join(dirname, 'files'));
15
+ return {
16
+ cacheDir,
17
+ pathPartitions: 3,
18
+ stores: [
19
+ new keyv_1.default({
20
+ serialize: auto_utils_1.stringify,
21
+ store: new lru_cache_1.LRUCache({
22
+ maxSize: cacheMaxSize,
23
+ maxEntrySize: Number.MAX_SAFE_INTEGER,
24
+ sizeCalculation: (value) => {
25
+ var _a;
26
+ const { value: parsedValue } = JSON.parse(value);
27
+ return Number((_a = parsedValue === null || parsedValue === void 0 ? void 0 : parsedValue.size) !== null && _a !== void 0 ? _a : 0);
28
+ },
29
+ }),
30
+ }),
31
+ new keyv_1.default({
32
+ store: new sqlite_1.default({
33
+ uri: path_1.default.join(cacheDir, 'files.sqlite'),
34
+ }),
35
+ ttl: cacheTtl,
36
+ serialize: auto_utils_1.stringify,
37
+ }),
38
+ ],
39
+ };
40
+ };
41
+ exports.defaultMemoryAndSqliteConfig = defaultMemoryAndSqliteConfig;
42
+ const defaultInitMemoryConfig = ({ dirname, cacheMaxSize, }) => {
43
+ const cacheDir = (0, utils_1.ensureDirectoryExists)(path_1.default.join(dirname, 'files'));
44
+ return {
45
+ cacheDir,
46
+ pathPartitions: 3,
47
+ stores: [
48
+ new keyv_1.default({
49
+ serialize: auto_utils_1.stringify,
50
+ store: new lru_cache_1.LRUCache({
51
+ maxSize: cacheMaxSize,
52
+ maxEntrySize: Number.MAX_SAFE_INTEGER,
53
+ sizeCalculation: (value) => {
54
+ var _a;
55
+ const { value: parsedValue } = JSON.parse(value);
56
+ return Number((_a = parsedValue === null || parsedValue === void 0 ? void 0 : parsedValue.size) !== null && _a !== void 0 ? _a : 0);
57
+ },
58
+ }),
59
+ }),
60
+ ],
61
+ };
62
+ };
63
+ exports.defaultInitMemoryConfig = defaultInitMemoryConfig;
@@ -0,0 +1,8 @@
1
+ import { BaseCacheConfig, FileCacheOptions, FileResponse } from './models';
2
+ export declare const createFileCache: (config: BaseCacheConfig) => {
3
+ get: (cid: string, options?: FileCacheOptions) => Promise<FileResponse | null>;
4
+ set: (cid: string, fileResponse: FileResponse) => Promise<void>;
5
+ has: (cid: string) => Promise<boolean>;
6
+ remove: (cid: string) => Promise<void>;
7
+ };
8
+ //# sourceMappingURL=fileCache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fileCache.d.ts","sourceRoot":"","sources":["../src/fileCache.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAS1E,eAAO,MAAM,eAAe,GAAI,QAAQ,eAAe;eA8B7B,MAAM,YAAY,gBAAgB,KAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;eAyBjE,MAAM,gBAAgB,YAAY;eARlC,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC;kBAoBtB,MAAM;CA0BlC,CAAA"}
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __rest = (this && this.__rest) || function (s, e) {
12
+ var t = {};
13
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
14
+ t[p] = s[p];
15
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
16
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
17
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
18
+ t[p[i]] = s[p[i]];
19
+ }
20
+ return t;
21
+ };
22
+ var __importDefault = (this && this.__importDefault) || function (mod) {
23
+ return (mod && mod.__esModule) ? mod : { "default": mod };
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.createFileCache = void 0;
27
+ const cache_manager_1 = require("cache-manager");
28
+ const fs_1 = __importDefault(require("fs"));
29
+ const promises_1 = __importDefault(require("fs/promises"));
30
+ const path_1 = __importDefault(require("path"));
31
+ const utils_1 = require("./utils");
32
+ const CHARS_PER_PARTITION = 2;
33
+ const createFileCache = (config) => {
34
+ const cidToFilePath = (cid) => {
35
+ const partitions = config.pathPartitions;
36
+ let filePath = '';
37
+ let head = cid;
38
+ for (let i = 0; i < partitions; i++) {
39
+ filePath = path_1.default.join(filePath, `${head.slice(-CHARS_PER_PARTITION)}/`);
40
+ head = head.slice(0, -CHARS_PER_PARTITION);
41
+ }
42
+ filePath = path_1.default.join(filePath, head, cid);
43
+ return path_1.default.join(config.cacheDir, filePath);
44
+ };
45
+ const filepathCache = (0, cache_manager_1.createCache)({
46
+ stores: config.stores,
47
+ nonBlocking: false,
48
+ });
49
+ const deserialize = (data) => {
50
+ var _a;
51
+ if (!data) {
52
+ return null;
53
+ }
54
+ return Object.assign(Object.assign({}, data), { size: BigInt((_a = data.size) !== null && _a !== void 0 ? _a : 0) });
55
+ };
56
+ const get = (cid, options) => __awaiter(void 0, void 0, void 0, function* () {
57
+ var _a, _b;
58
+ const data = deserialize(yield filepathCache.get(cid));
59
+ if (!data) {
60
+ return null;
61
+ }
62
+ const path = cidToFilePath(cid);
63
+ return Object.assign(Object.assign({}, data), { data: fs_1.default.createReadStream(path, {
64
+ start: (_a = options === null || options === void 0 ? void 0 : options.byteRange) === null || _a === void 0 ? void 0 : _a[0],
65
+ end: (_b = options === null || options === void 0 ? void 0 : options.byteRange) === null || _b === void 0 ? void 0 : _b[1],
66
+ }) });
67
+ });
68
+ const has = (cid) => __awaiter(void 0, void 0, void 0, function* () {
69
+ const path = cidToFilePath(cid);
70
+ return promises_1.default
71
+ .access(path, fs_1.default.constants.F_OK)
72
+ .then(() => true)
73
+ .catch(() => false);
74
+ });
75
+ const set = (cid, fileResponse) => __awaiter(void 0, void 0, void 0, function* () {
76
+ const filePath = cidToFilePath(cid);
77
+ const { data } = fileResponse, rest = __rest(fileResponse, ["data"]);
78
+ yield (0, utils_1.writeFile)(filePath, data);
79
+ yield filepathCache.set(cid, Object.assign({}, rest));
80
+ });
81
+ const remove = (cid) => __awaiter(void 0, void 0, void 0, function* () {
82
+ const data = deserialize(yield filepathCache.get(cid));
83
+ if (!data) {
84
+ return;
85
+ }
86
+ const path = cidToFilePath(cid);
87
+ yield Promise.all([filepathCache.del(cid), promises_1.default.rm(path)]);
88
+ });
89
+ filepathCache.on('del', (_a) => __awaiter(void 0, [_a], void 0, function* ({ key, error }) {
90
+ if (error) {
91
+ console.error(`Error deleting file cache entry for ${key}: ${error}`);
92
+ }
93
+ else {
94
+ yield promises_1.default.rm(cidToFilePath(key));
95
+ }
96
+ }));
97
+ const cache = {
98
+ get,
99
+ set,
100
+ has,
101
+ remove,
102
+ };
103
+ return cache;
104
+ };
105
+ exports.createFileCache = createFileCache;
@@ -0,0 +1,6 @@
1
+ import { Request, Response } from 'express';
2
+ import { ByteRange, DownloadMetadata, DownloadOptions } from '../models.js';
3
+ export declare const handleDownloadResponseHeaders: (req: Request, res: Response, metadata: DownloadMetadata, options: DownloadOptions) => void;
4
+ export declare const handleS3DownloadResponseHeaders: (req: Request, res: Response, metadata: DownloadMetadata) => void;
5
+ export declare const getByteRange: (req: Request) => ByteRange | undefined;
6
+ //# sourceMappingURL=headers.d.ts.map
@@ -0,0 +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;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"}
@@ -0,0 +1,65 @@
1
+ import { CompressionAlgorithm, EncryptionAlgorithm } from '@autonomys/auto-dag-data';
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'));
5
+ };
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;
13
+ if (metadata.type === 'file') {
14
+ setFileResponseHeaders(res, metadata, isEncrypted, documentExpected, shouldHandleEncoding, safeName, options);
15
+ }
16
+ else {
17
+ setFolderResponseHeaders(res, isEncrypted, documentExpected, safeName);
18
+ }
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) {
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"`);
43
+ };
44
+ export const handleS3DownloadResponseHeaders = (req, res, metadata) => {
45
+ if (metadata.isEncrypted) {
46
+ res.set('x-amz-meta-encryption', EncryptionAlgorithm.AES_256_GCM);
47
+ }
48
+ if (metadata.isCompressed) {
49
+ res.set('x-amz-meta-compression', CompressionAlgorithm.ZLIB);
50
+ }
51
+ };
52
+ export const getByteRange = (req) => {
53
+ const byteRange = req.headers['range'];
54
+ if (byteRange == null) {
55
+ return undefined;
56
+ }
57
+ const header = 'bytes ';
58
+ const [start, end] = byteRange.slice(header.length).split('-');
59
+ const startNumber = Number(start);
60
+ const endNumber = end && !['*', ''].includes(end) ? Number(end) : undefined;
61
+ if (startNumber < 0 || (endNumber && endNumber < 0) || (endNumber && startNumber > endNumber)) {
62
+ return undefined;
63
+ }
64
+ return [startNumber, endNumber];
65
+ };
@@ -0,0 +1,2 @@
1
+ export * from './headers.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/http/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA"}
@@ -0,0 +1 @@
1
+ export * from './headers.js';
@@ -0,0 +1,5 @@
1
+ export declare const version: string;
2
+ export * from './caching/index.js';
3
+ export * from './http/index.js';
4
+ export * from './models.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,OAAO,QAAsB,CAAA;AAE1C,cAAc,oBAAoB,CAAA;AAClC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,aAAa,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ import packageJson from '../package.json' with { type: 'json' };
2
+ export const version = packageJson.version;
3
+ export * from './caching/index.js';
4
+ export * from './http/index.js';
5
+ export * from './models.js';
@@ -0,0 +1,41 @@
1
+ import { IPLDNodeData, OffchainMetadata } from '@autonomys/auto-dag-data';
2
+ import { Keyv } from 'keyv';
3
+ import { Readable } from 'stream';
4
+ export type FileResponse = {
5
+ data: Readable;
6
+ mimeType?: string;
7
+ filename?: string;
8
+ size?: bigint;
9
+ encoding?: string;
10
+ };
11
+ export interface BaseCacheConfig {
12
+ pathPartitions: number;
13
+ cacheDir: string;
14
+ stores: Keyv<FileResponse>[];
15
+ }
16
+ export interface FileCache {
17
+ get: (cid: string) => Promise<Buffer | Readable | null>;
18
+ set: (cid: string, data: Buffer | Readable) => Promise<void>;
19
+ remove: (cid: string) => Promise<void>;
20
+ }
21
+ export type ByteRange = [number, number | undefined];
22
+ export interface FileCacheOptions {
23
+ byteRange?: ByteRange;
24
+ }
25
+ export type DownloadMetadata = {
26
+ name: string;
27
+ type: OffchainMetadata['type'];
28
+ mimeType?: string;
29
+ size?: bigint;
30
+ isEncrypted: boolean;
31
+ isCompressed: boolean;
32
+ };
33
+ export type DownloadOptions = {
34
+ byteRange?: ByteRange;
35
+ rawMode?: boolean;
36
+ };
37
+ export declare class DownloadMetadataFactory {
38
+ static fromOffchainMetadata(metadata: OffchainMetadata): DownloadMetadata;
39
+ static fromIPLDData(data: IPLDNodeData): DownloadMetadata;
40
+ }
41
+ //# sourceMappingURL=models.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAgB,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AACvF,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAC3B,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAGjC,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,QAAQ,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,MAAM,WAAW,eAAe;IAC9B,cAAc,EAAE,MAAM,CAAA;IACtB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,IAAI,CAAC,YAAY,CAAC,EAAE,CAAA;CAC7B;AAED,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,QAAQ,GAAG,IAAI,CAAC,CAAA;IACvD,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAC5D,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CACvC;AAED,MAAM,MAAM,SAAS,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;AAEpD,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,EAAE,SAAS,CAAA;CACtB;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,gBAAgB,CAAC,MAAM,CAAC,CAAA;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,OAAO,CAAA;IACpB,YAAY,EAAE,OAAO,CAAA;CACtB,CAAA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,CAAC,EAAE,SAAS,CAAA;IACrB,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB,CAAA;AAED,qBAAa,uBAAuB;IAClC,MAAM,CAAC,oBAAoB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,gBAAgB;IAczE,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,YAAY,GAAG,gBAAgB;CAmB1D"}
package/dist/models.js ADDED
@@ -0,0 +1,33 @@
1
+ import { MetadataType } from '@autonomys/auto-dag-data';
2
+ import { inferMimeType } from './utils.js';
3
+ export class DownloadMetadataFactory {
4
+ static fromOffchainMetadata(metadata) {
5
+ var _a, _b, _c, _d, _e, _f;
6
+ return {
7
+ size: metadata.totalSize,
8
+ isEncrypted: !!((_b = (_a = metadata.uploadOptions) === null || _a === void 0 ? void 0 : _a.encryption) === null || _b === void 0 ? void 0 : _b.algorithm),
9
+ isCompressed: !!((_d = (_c = metadata.uploadOptions) === null || _c === void 0 ? void 0 : _c.compression) === null || _d === void 0 ? void 0 : _d.algorithm),
10
+ name: (_e = metadata.name) !== null && _e !== void 0 ? _e : 'download',
11
+ type: metadata.type,
12
+ mimeType: metadata.type === 'file'
13
+ ? ((_f = metadata.mimeType) !== null && _f !== void 0 ? _f : 'application/octet-stream')
14
+ : 'application/zip',
15
+ };
16
+ }
17
+ static fromIPLDData(data) {
18
+ var _a, _b, _c, _d, _e;
19
+ const type = [MetadataType.File, MetadataType.FileChunk, MetadataType.FileInlink].includes(data.type)
20
+ ? 'file'
21
+ : 'folder';
22
+ const name = (_a = data.name) !== null && _a !== void 0 ? _a : 'download';
23
+ const mimeType = inferMimeType(name);
24
+ return {
25
+ size: data.size,
26
+ isEncrypted: !!((_c = (_b = data.uploadOptions) === null || _b === void 0 ? void 0 : _b.encryption) === null || _c === void 0 ? void 0 : _c.algorithm),
27
+ isCompressed: !!((_e = (_d = data.uploadOptions) === null || _d === void 0 ? void 0 : _d.compression) === null || _e === void 0 ? void 0 : _e.algorithm),
28
+ name,
29
+ type,
30
+ mimeType,
31
+ };
32
+ }
33
+ }
@@ -0,0 +1,2 @@
1
+ export declare const inferMimeType: (filename: string) => string;
2
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,aAAa,GAAI,UAAU,MAAM,WAK7C,CAAA"}
package/dist/utils.js ADDED
@@ -0,0 +1,7 @@
1
+ import mime from 'mime-types';
2
+ export const inferMimeType = (filename) => {
3
+ const extension = filename.split('.').pop();
4
+ return extension
5
+ ? mime.lookup(extension) || 'application/octet-stream'
6
+ : 'application/octet-stream';
7
+ };
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@autonomys/file-server",
3
+ "packageManager": "yarn@4.7.0",
4
+ "version": "1.5.14",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/autonomys/auto-sdk"
10
+ },
11
+ "author": {
12
+ "name": "Autonomys",
13
+ "url": "https://www.autonomys.xyz"
14
+ },
15
+ "bugs": {
16
+ "url": "https://github.com/autonomys/auto-sdk/issues"
17
+ },
18
+ "engines": {
19
+ "node": ">=20.8.0"
20
+ },
21
+ "scripts": {
22
+ "build": "yarn tsc"
23
+ },
24
+ "main": "./dist/index.js",
25
+ "types": "./dist/index.d.ts",
26
+ "exports": {
27
+ ".": {
28
+ "browser": "./dist/index.js",
29
+ "node": "./dist/index.js",
30
+ "types": "./dist/index.d.ts"
31
+ }
32
+ },
33
+ "files": [
34
+ "dist"
35
+ ],
36
+ "devDependencies": {
37
+ "@prerenderer/rollup-plugin": "^0.3.12",
38
+ "@rollup/plugin-commonjs": "^28.0.3",
39
+ "@rollup/plugin-json": "^6.1.0",
40
+ "@rollup/plugin-node-resolve": "^16.0.1",
41
+ "@rollup/plugin-terser": "^0.4.4",
42
+ "@rollup/plugin-typescript": "^12.1.2",
43
+ "@types/express": "^5.0.0",
44
+ "@types/mime-types": "^2.1.4",
45
+ "rollup-plugin-jscc": "^2.0.0",
46
+ "tslib": "^2.8.1",
47
+ "typescript": "^5.8.3"
48
+ },
49
+ "dependencies": {
50
+ "@autonomys/asynchronous": "^1.5.14",
51
+ "@autonomys/auto-dag-data": "^1.5.14",
52
+ "@autonomys/auto-utils": "^1.5.14",
53
+ "@keyvhq/sqlite": "^2.1.7",
54
+ "cache-manager": "^6.4.2",
55
+ "express": "^4.19.2",
56
+ "jszip": "^3.10.1",
57
+ "keyv": "^5.3.2",
58
+ "mime-types": "^3.0.1",
59
+ "process": "^0.11.10",
60
+ "stream": "^0.0.3",
61
+ "uuid": "^11.1.0",
62
+ "zod": "^3.24.2"
63
+ },
64
+ "gitHead": "ef4c21d683cad697f7015e52becd399a8ca2ed84"
65
+ }