@haneinfo/attachments 0.0.0-stage → 0.1.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.
- package/LICENSE +14 -0
- package/README.md +75 -2
- package/agent-guide.md +86 -0
- package/dist/cleanup.worker.d.ts +13 -0
- package/dist/cleanup.worker.d.ts.map +1 -0
- package/dist/cleanup.worker.js +57 -0
- package/dist/cleanup.worker.js.map +1 -0
- package/dist/config.d.ts +38 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +74 -0
- package/dist/config.js.map +1 -0
- package/dist/controller.d.ts +45 -0
- package/dist/controller.d.ts.map +1 -0
- package/dist/controller.js +214 -0
- package/dist/controller.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp-tools.d.ts +10 -0
- package/dist/mcp-tools.d.ts.map +1 -0
- package/dist/mcp-tools.js +74 -0
- package/dist/mcp-tools.js.map +1 -0
- package/dist/module.d.ts +6 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +54 -0
- package/dist/module.js.map +1 -0
- package/dist/plugin.d.ts +44 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +90 -0
- package/dist/plugin.js.map +1 -0
- package/dist/service.d.ts +123 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +384 -0
- package/dist/service.js.map +1 -0
- package/dist/storage.d.ts +54 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +239 -0
- package/dist/storage.js.map +1 -0
- package/dist/store.d.ts +61 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +25 -0
- package/dist/store.js.map +1 -0
- package/dist/validation.d.ts +29 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +297 -0
- package/dist/validation.js.map +1 -0
- package/haneinfo.plugin.json +72 -0
- package/package.json +80 -3
- package/plugin.d.ts +2 -0
- package/plugin.js +4 -0
- package/prisma/attachments.prisma +32 -0
- package/prisma/attachments.sql +13 -0
package/dist/storage.js
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.S3FileStorage = exports.defaultS3ClientFactory = exports.LocalFileStorage = void 0;
|
|
40
|
+
exports.validateStorageKey = validateStorageKey;
|
|
41
|
+
const node_fs_1 = require("node:fs");
|
|
42
|
+
const promises_1 = require("node:fs/promises");
|
|
43
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
44
|
+
const node_stream_1 = require("node:stream");
|
|
45
|
+
const promises_2 = require("node:stream/promises");
|
|
46
|
+
function validateStorageKey(key) {
|
|
47
|
+
if (!/^[a-zA-Z0-9_-]+(?:\/[a-zA-Z0-9_-]+)*$/.test(key))
|
|
48
|
+
throw new Error('Invalid storage key');
|
|
49
|
+
}
|
|
50
|
+
class LocalFileStorage {
|
|
51
|
+
constructor(root) {
|
|
52
|
+
this.root = node_path_1.default.resolve(root);
|
|
53
|
+
}
|
|
54
|
+
async target(key, create = false) {
|
|
55
|
+
validateStorageKey(key);
|
|
56
|
+
const target = node_path_1.default.resolve(this.root, key);
|
|
57
|
+
if (!target.startsWith(`${this.root}${node_path_1.default.sep}`))
|
|
58
|
+
throw new Error('Storage key escapes root');
|
|
59
|
+
if (create)
|
|
60
|
+
await (0, promises_1.mkdir)(this.root, { recursive: true });
|
|
61
|
+
// Refuse symlink/junction components, including existing leaf objects.
|
|
62
|
+
let current = this.root;
|
|
63
|
+
for (const part of key.split('/')) {
|
|
64
|
+
current = node_path_1.default.join(current, part);
|
|
65
|
+
try {
|
|
66
|
+
if ((await (0, promises_1.lstat)(current)).isSymbolicLink())
|
|
67
|
+
throw new Error('Storage links are forbidden');
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
if (error.code !== 'ENOENT')
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (create) {
|
|
75
|
+
await (0, promises_1.mkdir)(node_path_1.default.dirname(target), { recursive: true });
|
|
76
|
+
const resolvedRoot = await (0, promises_1.realpath)(this.root);
|
|
77
|
+
const parent = await (0, promises_1.realpath)(node_path_1.default.dirname(target));
|
|
78
|
+
if (parent !== resolvedRoot && !parent.startsWith(`${resolvedRoot}${node_path_1.default.sep}`))
|
|
79
|
+
throw new Error('Storage path escapes root');
|
|
80
|
+
}
|
|
81
|
+
return target;
|
|
82
|
+
}
|
|
83
|
+
async put(key, stream, options = {}) {
|
|
84
|
+
const target = await this.target(key, true);
|
|
85
|
+
let size = 0;
|
|
86
|
+
const count = new node_stream_1.Transform({
|
|
87
|
+
transform(chunk, _encoding, callback) {
|
|
88
|
+
size += chunk.length;
|
|
89
|
+
callback(null, chunk);
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
let opened = false;
|
|
93
|
+
const output = (0, node_fs_1.createWriteStream)(target, { flags: 'wx', mode: 0o600 });
|
|
94
|
+
output.once('open', () => {
|
|
95
|
+
opened = true;
|
|
96
|
+
});
|
|
97
|
+
try {
|
|
98
|
+
await (0, promises_2.pipeline)(stream, count, output, { signal: options.signal });
|
|
99
|
+
return { size };
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
if (opened)
|
|
103
|
+
await (0, promises_1.rm)(target, { force: true });
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
async get(key) {
|
|
108
|
+
const target = await this.target(key);
|
|
109
|
+
await (0, promises_1.stat)(target);
|
|
110
|
+
return (0, node_fs_1.createReadStream)(target);
|
|
111
|
+
}
|
|
112
|
+
async delete(key) {
|
|
113
|
+
await (0, promises_1.rm)(await this.target(key), { force: true });
|
|
114
|
+
}
|
|
115
|
+
async exists(key) {
|
|
116
|
+
try {
|
|
117
|
+
return (await (0, promises_1.stat)(await this.target(key))).isFile();
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
if (error.code === 'ENOENT')
|
|
121
|
+
return false;
|
|
122
|
+
throw error;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
exports.LocalFileStorage = LocalFileStorage;
|
|
127
|
+
/** Variable imports keep the SDK optional, including in local-driver builds. */
|
|
128
|
+
const defaultS3ClientFactory = async (config) => {
|
|
129
|
+
let sdk;
|
|
130
|
+
let storage;
|
|
131
|
+
try {
|
|
132
|
+
sdk = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-s3')));
|
|
133
|
+
storage = await Promise.resolve().then(() => __importStar(require('@aws-sdk/lib-storage')));
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
throw new Error('S3 attachments require optional peers @aws-sdk/client-s3 and @aws-sdk/lib-storage');
|
|
137
|
+
}
|
|
138
|
+
const accessKeyId = process.env.ATTACHMENTS_S3_ACCESS_KEY_ID;
|
|
139
|
+
const secretAccessKey = process.env.ATTACHMENTS_S3_SECRET_ACCESS_KEY;
|
|
140
|
+
if (!accessKeyId || !secretAccessKey)
|
|
141
|
+
throw new Error('S3 attachments require ATTACHMENTS_S3_ACCESS_KEY_ID and ATTACHMENTS_S3_SECRET_ACCESS_KEY');
|
|
142
|
+
const client = new sdk.S3Client({
|
|
143
|
+
...config,
|
|
144
|
+
credentials: {
|
|
145
|
+
accessKeyId,
|
|
146
|
+
secretAccessKey,
|
|
147
|
+
sessionToken: process.env.ATTACHMENTS_S3_SESSION_TOKEN,
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
return {
|
|
151
|
+
client,
|
|
152
|
+
command: (kind, input) => {
|
|
153
|
+
const args = input;
|
|
154
|
+
if (kind === 'get')
|
|
155
|
+
return new sdk.GetObjectCommand(args);
|
|
156
|
+
if (kind === 'head')
|
|
157
|
+
return new sdk.HeadObjectCommand(args);
|
|
158
|
+
return new sdk.DeleteObjectCommand(args);
|
|
159
|
+
},
|
|
160
|
+
upload: (params) => new storage.Upload({
|
|
161
|
+
client,
|
|
162
|
+
params: params,
|
|
163
|
+
queueSize: 1,
|
|
164
|
+
partSize: 5 * 1024 * 1024,
|
|
165
|
+
leavePartsOnError: false,
|
|
166
|
+
}),
|
|
167
|
+
};
|
|
168
|
+
};
|
|
169
|
+
exports.defaultS3ClientFactory = defaultS3ClientFactory;
|
|
170
|
+
class S3FileStorage {
|
|
171
|
+
constructor(config, deps) {
|
|
172
|
+
this.config = config;
|
|
173
|
+
this.deps = deps;
|
|
174
|
+
}
|
|
175
|
+
static async create(config, factory = exports.defaultS3ClientFactory) {
|
|
176
|
+
return new S3FileStorage(config, await factory(config));
|
|
177
|
+
}
|
|
178
|
+
input(key) {
|
|
179
|
+
validateStorageKey(key);
|
|
180
|
+
return { Bucket: this.config.bucket, Key: key };
|
|
181
|
+
}
|
|
182
|
+
async put(key, stream, options = {}) {
|
|
183
|
+
let size = 0;
|
|
184
|
+
const count = new node_stream_1.Transform({
|
|
185
|
+
transform(chunk, _encoding, callback) {
|
|
186
|
+
size += chunk.length;
|
|
187
|
+
callback(null, chunk);
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
const upload = this.deps.upload({
|
|
191
|
+
...this.input(key),
|
|
192
|
+
Body: count,
|
|
193
|
+
ContentType: options.contentType,
|
|
194
|
+
});
|
|
195
|
+
const abort = () => {
|
|
196
|
+
void upload.abort().catch(() => { });
|
|
197
|
+
};
|
|
198
|
+
options.signal?.addEventListener('abort', abort, { once: true });
|
|
199
|
+
try {
|
|
200
|
+
await Promise.all([(0, promises_2.pipeline)(stream, count, { signal: options.signal }), upload.done()]);
|
|
201
|
+
return { size };
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
stream.destroy();
|
|
205
|
+
count.destroy();
|
|
206
|
+
await upload.abort().catch(() => { });
|
|
207
|
+
throw error;
|
|
208
|
+
}
|
|
209
|
+
finally {
|
|
210
|
+
options.signal?.removeEventListener('abort', abort);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
async get(key) {
|
|
214
|
+
const { Body } = await this.deps.client.send(this.deps.command('get', this.input(key)));
|
|
215
|
+
if (!(Body instanceof node_stream_1.Readable))
|
|
216
|
+
throw new Error('S3 returned no Node readable stream');
|
|
217
|
+
return Body;
|
|
218
|
+
}
|
|
219
|
+
async delete(key) {
|
|
220
|
+
await this.deps.client.send(this.deps.command('delete', this.input(key)));
|
|
221
|
+
}
|
|
222
|
+
async exists(key) {
|
|
223
|
+
try {
|
|
224
|
+
await this.deps.client.send(this.deps.command('head', this.input(key)));
|
|
225
|
+
return true;
|
|
226
|
+
}
|
|
227
|
+
catch (error) {
|
|
228
|
+
const e = error;
|
|
229
|
+
if (e.$metadata?.httpStatusCode === 404 || e.name === 'NotFound' || e.name === 'NoSuchKey')
|
|
230
|
+
return false;
|
|
231
|
+
throw error;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
onModuleDestroy() {
|
|
235
|
+
this.deps.client.destroy?.();
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
exports.S3FileStorage = S3FileStorage;
|
|
239
|
+
//# sourceMappingURL=storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"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"}
|
package/dist/store.d.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { StateMachineStore } from '@haneinfo/state-machine';
|
|
2
|
+
export declare const ATTACHMENTS_STORE: unique symbol;
|
|
3
|
+
export declare const ATTACHMENTS_CONFIG: unique symbol;
|
|
4
|
+
export declare const ATTACHMENTS_CLOCK: unique symbol;
|
|
5
|
+
export declare const SCAN_JOB = "attachments.scan";
|
|
6
|
+
export declare const CLEANUP_JOB = "attachments.cleanup";
|
|
7
|
+
export interface AttachmentRecord {
|
|
8
|
+
id: string;
|
|
9
|
+
instanceId: string;
|
|
10
|
+
fileName: string;
|
|
11
|
+
declaredContentType: string;
|
|
12
|
+
detectedContentType: string;
|
|
13
|
+
size: bigint;
|
|
14
|
+
sha256: string;
|
|
15
|
+
storageKey: string;
|
|
16
|
+
status: 'READY' | 'PENDING_SCAN' | 'REJECTED';
|
|
17
|
+
uploadedBy: string;
|
|
18
|
+
createdAt: Date;
|
|
19
|
+
deletedAt: Date | null;
|
|
20
|
+
deletedBy: string | null;
|
|
21
|
+
}
|
|
22
|
+
export interface GarbageRecord {
|
|
23
|
+
storageKey: string;
|
|
24
|
+
notBefore: Date;
|
|
25
|
+
}
|
|
26
|
+
type Args = Record<string, unknown>;
|
|
27
|
+
export interface AttachmentClient {
|
|
28
|
+
attachment: {
|
|
29
|
+
create(args: Args): Promise<AttachmentRecord>;
|
|
30
|
+
findUnique(args: Args): Promise<AttachmentRecord | null>;
|
|
31
|
+
findMany(args: Args): Promise<AttachmentRecord[]>;
|
|
32
|
+
count(args: Args): Promise<number>;
|
|
33
|
+
update(args: Args): Promise<AttachmentRecord>;
|
|
34
|
+
deleteMany(args: Args): Promise<{
|
|
35
|
+
count: number;
|
|
36
|
+
}>;
|
|
37
|
+
};
|
|
38
|
+
attachmentGarbage: {
|
|
39
|
+
create(args: Args): Promise<GarbageRecord>;
|
|
40
|
+
findMany(args: Args): Promise<GarbageRecord[]>;
|
|
41
|
+
deleteMany(args: Args): Promise<{
|
|
42
|
+
count: number;
|
|
43
|
+
}>;
|
|
44
|
+
};
|
|
45
|
+
backgroundJob: {
|
|
46
|
+
findFirst(args: Args): Promise<unknown>;
|
|
47
|
+
};
|
|
48
|
+
$queryRawUnsafe<T>(sql: string, ...values: unknown[]): Promise<T>;
|
|
49
|
+
}
|
|
50
|
+
interface Database extends AttachmentClient {
|
|
51
|
+
$transaction<T>(work: (tx: AttachmentClient) => Promise<T>, options?: Args): Promise<T>;
|
|
52
|
+
}
|
|
53
|
+
export declare class AttachmentsStore {
|
|
54
|
+
readonly instances: StateMachineStore;
|
|
55
|
+
readonly db: Database;
|
|
56
|
+
constructor(prisma: unknown, instances: StateMachineStore);
|
|
57
|
+
run<T>(tx: unknown, work: (client: AttachmentClient, tx: unknown) => Promise<T>): Promise<T>;
|
|
58
|
+
lock(client: AttachmentClient, id: string): Promise<AttachmentRecord | null>;
|
|
59
|
+
}
|
|
60
|
+
export {};
|
|
61
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
|
package/dist/store.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AttachmentsStore = exports.CLEANUP_JOB = exports.SCAN_JOB = exports.ATTACHMENTS_CLOCK = exports.ATTACHMENTS_CONFIG = exports.ATTACHMENTS_STORE = void 0;
|
|
4
|
+
exports.ATTACHMENTS_STORE = Symbol.for('haneinfo.attachments.store');
|
|
5
|
+
exports.ATTACHMENTS_CONFIG = Symbol.for('haneinfo.attachments.config');
|
|
6
|
+
exports.ATTACHMENTS_CLOCK = Symbol.for('haneinfo.attachments.clock');
|
|
7
|
+
exports.SCAN_JOB = 'attachments.scan';
|
|
8
|
+
exports.CLEANUP_JOB = 'attachments.cleanup';
|
|
9
|
+
class AttachmentsStore {
|
|
10
|
+
constructor(prisma, instances) {
|
|
11
|
+
this.instances = instances;
|
|
12
|
+
this.db = prisma;
|
|
13
|
+
}
|
|
14
|
+
run(tx, work) {
|
|
15
|
+
return tx
|
|
16
|
+
? work(tx, tx)
|
|
17
|
+
: this.db.$transaction((client) => work(client, client));
|
|
18
|
+
}
|
|
19
|
+
async lock(client, id) {
|
|
20
|
+
await client.$queryRawUnsafe('SELECT id FROM attachments WHERE id = $1 FOR UPDATE', id);
|
|
21
|
+
return client.attachment.findUnique({ where: { id } });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
exports.AttachmentsStore = AttachmentsStore;
|
|
25
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":";;;AAEa,QAAA,iBAAiB,GAAG,MAAM,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;AAC7D,QAAA,kBAAkB,GAAG,MAAM,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC;AAC/D,QAAA,iBAAiB,GAAG,MAAM,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;AAC7D,QAAA,QAAQ,GAAG,kBAAkB,CAAC;AAC9B,QAAA,WAAW,GAAG,qBAAqB,CAAC;AA0CjD,MAAa,gBAAgB;IAE3B,YACE,MAAe,EACN,SAA4B;QAA5B,cAAS,GAAT,SAAS,CAAmB;QAErC,IAAI,CAAC,EAAE,GAAG,MAAkB,CAAC;IAC/B,CAAC;IACD,GAAG,CAAI,EAAW,EAAE,IAA2D;QAC7E,OAAO,EAAE;YACP,CAAC,CAAC,IAAI,CAAC,EAAsB,EAAE,EAAE,CAAC;YAClC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC7D,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,MAAwB,EAAE,EAAU;QAC7C,MAAM,MAAM,CAAC,eAAe,CAAC,qDAAqD,EAAE,EAAE,CAAC,CAAC;QACxF,OAAO,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;IACzD,CAAC;CACF;AAjBD,4CAiBC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { createReadStream } from 'node:fs';
|
|
2
|
+
import { type Readable } from 'node:stream';
|
|
3
|
+
import { type ResolvedAttachmentsConfig } from './config';
|
|
4
|
+
export declare const STAGING_ROOT: string;
|
|
5
|
+
export declare const mimeTypes: Record<string, string>;
|
|
6
|
+
export declare function cleanFileName(raw: string): {
|
|
7
|
+
fileName: string;
|
|
8
|
+
extension: string;
|
|
9
|
+
};
|
|
10
|
+
export declare function downloadHeaders(record: {
|
|
11
|
+
fileName: string;
|
|
12
|
+
detectedContentType: string;
|
|
13
|
+
size: bigint | number;
|
|
14
|
+
}): {
|
|
15
|
+
'Content-Disposition': string;
|
|
16
|
+
'X-Content-Type-Options': string;
|
|
17
|
+
'Cache-Control': string;
|
|
18
|
+
'Content-Type': string;
|
|
19
|
+
'Content-Length': string;
|
|
20
|
+
};
|
|
21
|
+
export declare function stageUpload(stream: Readable, id: string, extension: string, config: ResolvedAttachmentsConfig, signal: AbortSignal): Promise<{
|
|
22
|
+
file: string;
|
|
23
|
+
size: number;
|
|
24
|
+
sha256: string;
|
|
25
|
+
detectedContentType: string;
|
|
26
|
+
}>;
|
|
27
|
+
export declare function cleanupStaging(before: Date, limit: number): Promise<number>;
|
|
28
|
+
export { createReadStream };
|
|
29
|
+
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAqB,MAAM,SAAS,CAAC;AAI9D,OAAO,EAAE,KAAK,QAAQ,EAAa,MAAM,aAAa,CAAC;AAIvD,OAAO,EAAmB,KAAK,yBAAyB,EAAE,MAAM,UAAU,CAAC;AAE3E,eAAO,MAAM,YAAY,QAAyD,CAAC;AAqCnF,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAW5C,CAAC;AAEF,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM;;;EAkCxC;AACD,wBAAgB,eAAe,CAAC,MAAM,EAAE;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB;;;;;;EAYA;AA4HD,wBAAsB,WAAW,CAC/B,MAAM,EAAE,QAAQ,EAChB,EAAE,EAAE,MAAM,EACV,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,yBAAyB,EACjC,MAAM,EAAE,WAAW;;;;;GAyDpB;AAED,wBAAsB,cAAc,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,mBAc/D;AACD,OAAO,EAAE,gBAAgB,EAAE,CAAC"}
|