@dudousxd/nestjs-agent 1.9.0 → 1.11.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/README.md +159 -34
- package/dist/{agent-deps.factory-DuTy1UyU.d.cts → agent-deps.factory-BRTBu0lV.d.cts} +25 -21
- package/dist/{agent-deps.factory-DuTy1UyU.d.ts → agent-deps.factory-BRTBu0lV.d.ts} +25 -21
- package/dist/durable/index.cjs +153 -23
- package/dist/durable/index.cjs.map +1 -1
- package/dist/durable/index.d.cts +1 -1
- package/dist/durable/index.d.ts +1 -1
- package/dist/durable/index.js +156 -26
- package/dist/durable/index.js.map +1 -1
- package/dist/index.cjs +219 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +100 -11
- package/dist/index.d.ts +100 -11
- package/dist/index.js +218 -27
- package/dist/index.js.map +1 -1
- package/dist/media/index.cjs +641 -0
- package/dist/media/index.cjs.map +1 -0
- package/dist/media/index.d.cts +238 -0
- package/dist/media/index.d.ts +238 -0
- package/dist/media/index.js +608 -0
- package/dist/media/index.js.map +1 -0
- package/package.json +20 -3
|
@@ -0,0 +1,641 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
|
|
21
|
+
// src/media/index.ts
|
|
22
|
+
var media_exports = {};
|
|
23
|
+
__export(media_exports, {
|
|
24
|
+
AGENT_MEDIA_ATTACHMENTS: () => AGENT_MEDIA_ATTACHMENTS,
|
|
25
|
+
AGENT_MEDIA_ATTACHMENTS_OPTIONS: () => AGENT_MEDIA_ATTACHMENTS_OPTIONS,
|
|
26
|
+
AgentMediaAttachmentsModule: () => AgentMediaAttachmentsModule,
|
|
27
|
+
AgentMediaUploadsController: () => AgentMediaUploadsController,
|
|
28
|
+
DEFAULT_AGENT_MEDIA_COLLECTION: () => DEFAULT_AGENT_MEDIA_COLLECTION,
|
|
29
|
+
DEFAULT_AGENT_MEDIA_OWNER_TYPE: () => DEFAULT_AGENT_MEDIA_OWNER_TYPE,
|
|
30
|
+
DEFAULT_AGENT_MEDIA_TUS_BASE_PATH: () => DEFAULT_AGENT_MEDIA_TUS_BASE_PATH,
|
|
31
|
+
DEFAULT_AGENT_MEDIA_URL_TTL_SECONDS: () => DEFAULT_AGENT_MEDIA_URL_TTL_SECONDS,
|
|
32
|
+
MediaAttachmentStaging: () => MediaAttachmentStaging2
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(media_exports);
|
|
35
|
+
|
|
36
|
+
// src/media/media-attachment-staging.ts
|
|
37
|
+
var import_node_crypto = require("crypto");
|
|
38
|
+
var import_nestjs_media = require("@dudousxd/nestjs-media");
|
|
39
|
+
var import_common = require("@nestjs/common");
|
|
40
|
+
|
|
41
|
+
// src/media/media-attachments.options.ts
|
|
42
|
+
var DEFAULT_AGENT_MEDIA_COLLECTION = "agent-attachments";
|
|
43
|
+
var DEFAULT_AGENT_MEDIA_OWNER_TYPE = "agent-actor";
|
|
44
|
+
var DEFAULT_AGENT_MEDIA_URL_TTL_SECONDS = 7 * 24 * 60 * 60;
|
|
45
|
+
var DEFAULT_AGENT_MEDIA_TUS_BASE_PATH = "/media/uploads";
|
|
46
|
+
|
|
47
|
+
// src/media/media-attachment-staging.ts
|
|
48
|
+
var DEFAULT_MAX_BYTES = 20 * 1024 * 1024;
|
|
49
|
+
var DEFAULT_ALLOWED_CONTENT_TYPES = [
|
|
50
|
+
"image/png",
|
|
51
|
+
"image/jpeg",
|
|
52
|
+
"image/gif",
|
|
53
|
+
"image/webp",
|
|
54
|
+
"application/pdf",
|
|
55
|
+
"text/plain",
|
|
56
|
+
"text/csv"
|
|
57
|
+
];
|
|
58
|
+
var MediaAttachmentStaging2 = class _MediaAttachmentStaging {
|
|
59
|
+
static {
|
|
60
|
+
__name(this, "MediaAttachmentStaging");
|
|
61
|
+
}
|
|
62
|
+
deps;
|
|
63
|
+
options;
|
|
64
|
+
logger = new import_common.Logger(_MediaAttachmentStaging.name);
|
|
65
|
+
collection;
|
|
66
|
+
ownerType;
|
|
67
|
+
maxBytes;
|
|
68
|
+
allowed;
|
|
69
|
+
now;
|
|
70
|
+
newId;
|
|
71
|
+
warnedInline = false;
|
|
72
|
+
constructor(deps, options) {
|
|
73
|
+
this.deps = deps;
|
|
74
|
+
this.options = options;
|
|
75
|
+
this.collection = options.collection ?? DEFAULT_AGENT_MEDIA_COLLECTION;
|
|
76
|
+
this.ownerType = options.ownerType ?? DEFAULT_AGENT_MEDIA_OWNER_TYPE;
|
|
77
|
+
this.maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;
|
|
78
|
+
this.allowed = options.allowedContentTypes ?? DEFAULT_ALLOWED_CONTENT_TYPES;
|
|
79
|
+
this.now = options.clock ?? (() => /* @__PURE__ */ new Date());
|
|
80
|
+
this.newId = options.idGenerator ?? (() => (0, import_node_crypto.randomUUID)());
|
|
81
|
+
}
|
|
82
|
+
get diskName() {
|
|
83
|
+
return this.options.disk ?? this.deps.storage.defaultDisk;
|
|
84
|
+
}
|
|
85
|
+
/** Open a tus session for one file, owned by `actor`. Validation happens here, before any byte moves. */
|
|
86
|
+
async beginUpload(input) {
|
|
87
|
+
const uploads = this.deps.uploads;
|
|
88
|
+
if (uploads === null) {
|
|
89
|
+
throw new import_common.NotImplementedException("Resumable attachment uploads need MediaModule configured with `uploadSessions` and `tus`.");
|
|
90
|
+
}
|
|
91
|
+
this.validate(input.filename, input.contentType, input.size);
|
|
92
|
+
const id = this.newId();
|
|
93
|
+
const fileName = safeFileName(input.filename);
|
|
94
|
+
const path = this.pathFor(input.actor, id, fileName);
|
|
95
|
+
const session = await uploads.createUpload({
|
|
96
|
+
disk: this.diskName,
|
|
97
|
+
key: path,
|
|
98
|
+
size: input.size,
|
|
99
|
+
contentType: input.contentType,
|
|
100
|
+
metadata: {
|
|
101
|
+
agentMediaId: id
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
await this.saveRecord({
|
|
105
|
+
id,
|
|
106
|
+
actor: input.actor,
|
|
107
|
+
fileName,
|
|
108
|
+
mimeType: input.contentType,
|
|
109
|
+
size: input.size,
|
|
110
|
+
path,
|
|
111
|
+
state: {
|
|
112
|
+
status: "pending",
|
|
113
|
+
uploadId: session.id
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
const base = (this.options.tusBasePath ?? DEFAULT_AGENT_MEDIA_TUS_BASE_PATH).replace(/\/+$/, "");
|
|
117
|
+
return {
|
|
118
|
+
mediaId: id,
|
|
119
|
+
uploadId: session.id,
|
|
120
|
+
location: `${base}/${session.id}`
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Confirm the bytes of a `beginUpload` landed and whole. `null` for an unknown or foreign id;
|
|
125
|
+
* `409` while the bytes are still missing; `422` (and the record dropped) when what arrived is
|
|
126
|
+
* not the size that was declared. Idempotent on a ready record.
|
|
127
|
+
*/
|
|
128
|
+
async completeUpload(input) {
|
|
129
|
+
const record = await this.ownRecord(input.mediaId, input.actor);
|
|
130
|
+
if (record === null) return null;
|
|
131
|
+
const ready = await this.ensureReady(record);
|
|
132
|
+
if (ready === "missing") {
|
|
133
|
+
throw new import_common.ConflictException(`upload ${input.mediaId} has not finished`);
|
|
134
|
+
}
|
|
135
|
+
if (ready === "mismatch") {
|
|
136
|
+
await this.remove(record.id);
|
|
137
|
+
throw new import_common.UnprocessableEntityException(`upload ${input.mediaId} does not match its declared size`);
|
|
138
|
+
}
|
|
139
|
+
return this.toAttachment(ready, {
|
|
140
|
+
forModel: false
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
/** The actor drops one of their own attachments — aborting its upload if still in flight. */
|
|
144
|
+
async discard(input) {
|
|
145
|
+
const record = await this.ownRecord(input.mediaId, input.actor);
|
|
146
|
+
if (record === null) return false;
|
|
147
|
+
await this.remove(record.id);
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Delete an attachment's bytes, its in-flight tus session and its record. NO ownership check —
|
|
152
|
+
* the host's sweep calls this for what `AgentService.collectableAttachments` returned.
|
|
153
|
+
*/
|
|
154
|
+
async remove(mediaId) {
|
|
155
|
+
const record = await this.deps.store.find(mediaId);
|
|
156
|
+
if (record === null || !this.isOurs(record)) return;
|
|
157
|
+
const state = stateOf(record);
|
|
158
|
+
if (state.status === "pending" && state.uploadId !== void 0) {
|
|
159
|
+
await this.deps.uploads?.abort(state.uploadId).catch(() => void 0);
|
|
160
|
+
}
|
|
161
|
+
await this.deps.storage.disk(record.disk).delete(record.path).catch(() => void 0);
|
|
162
|
+
await this.deps.store.delete(record.id);
|
|
163
|
+
if (this.options.indexForRag === true && state.status === "ready") {
|
|
164
|
+
(0, import_nestjs_media.publishMedia)("delete", {
|
|
165
|
+
id: record.id,
|
|
166
|
+
ownerType: record.ownerType,
|
|
167
|
+
ownerId: record.ownerId
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
async stage(input) {
|
|
172
|
+
const id = this.newId();
|
|
173
|
+
const fileName = safeFileName(input.filename);
|
|
174
|
+
const path = this.pathFor(input.actor, id, fileName);
|
|
175
|
+
await this.deps.storage.disk(this.diskName).put(path, input.data, {
|
|
176
|
+
contentType: input.contentType
|
|
177
|
+
});
|
|
178
|
+
const record = await this.saveRecord({
|
|
179
|
+
id,
|
|
180
|
+
actor: input.actor,
|
|
181
|
+
fileName,
|
|
182
|
+
mimeType: input.contentType,
|
|
183
|
+
size: input.sizeBytes,
|
|
184
|
+
path,
|
|
185
|
+
state: {
|
|
186
|
+
status: "ready"
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
this.announce(record);
|
|
190
|
+
return this.toAttachment(record, {
|
|
191
|
+
forModel: false
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* The attachment for `mediaId`, or `null` when it is unknown, not this actor's, or its bytes have
|
|
196
|
+
* not (fully) arrived. The actor may use media they own, and media a message in one of THEIR
|
|
197
|
+
* threads already carries (a fork, a regenerate) — never anything else.
|
|
198
|
+
*/
|
|
199
|
+
async resolve(input) {
|
|
200
|
+
const record = await this.deps.store.find(input.mediaId);
|
|
201
|
+
if (record === null || !this.isOurs(record)) return null;
|
|
202
|
+
if (!await this.canUse(record, input.actor)) return null;
|
|
203
|
+
const ready = await this.ensureReady(record);
|
|
204
|
+
if (typeof ready === "string") return null;
|
|
205
|
+
return this.toAttachment(ready, {
|
|
206
|
+
forModel: true
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
async list(input) {
|
|
210
|
+
const records = await this.deps.store.listByOwner(this.ownerType, input.actor.id, this.collection);
|
|
211
|
+
const entries = records.map((record) => ({
|
|
212
|
+
mediaId: record.id,
|
|
213
|
+
name: record.fileName,
|
|
214
|
+
contentType: record.mimeType,
|
|
215
|
+
sizeBytes: record.size,
|
|
216
|
+
createdAt: new Date(record.createdAt).toISOString()
|
|
217
|
+
})).filter((entry) => input.stagedBefore === void 0 || entry.createdAt < input.stagedBefore).sort((a, b) => b.createdAt.localeCompare(a.createdAt));
|
|
218
|
+
return input.limit === void 0 ? entries : entries.slice(0, input.limit);
|
|
219
|
+
}
|
|
220
|
+
validate(filename, contentType, size) {
|
|
221
|
+
if (typeof filename !== "string" || filename.trim().length === 0) {
|
|
222
|
+
throw new import_common.BadRequestException("filename is required");
|
|
223
|
+
}
|
|
224
|
+
if (typeof contentType !== "string" || !this.allowed.includes(contentType)) {
|
|
225
|
+
throw new import_common.UnsupportedMediaTypeException(`content type "${String(contentType)}" is not allowed (allowed: ${this.allowed.join(", ")})`);
|
|
226
|
+
}
|
|
227
|
+
if (typeof size !== "number" || !Number.isInteger(size) || size <= 0) {
|
|
228
|
+
throw new import_common.BadRequestException("size must be a positive integer byte count");
|
|
229
|
+
}
|
|
230
|
+
if (size > this.maxBytes) {
|
|
231
|
+
throw new import_common.PayloadTooLargeException(`file exceeds the ${this.maxBytes}-byte limit`);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
pathFor(actor, id, fileName) {
|
|
235
|
+
return `${this.ownerType}/${encodeURIComponent(actor.id)}/${this.collection}/${id}/${fileName}`;
|
|
236
|
+
}
|
|
237
|
+
async saveRecord(input) {
|
|
238
|
+
const timestamp = this.now();
|
|
239
|
+
return this.deps.store.save({
|
|
240
|
+
id: input.id,
|
|
241
|
+
ownerType: this.ownerType,
|
|
242
|
+
ownerId: input.actor.id,
|
|
243
|
+
collection: this.collection,
|
|
244
|
+
name: stripExtension(input.fileName),
|
|
245
|
+
fileName: input.fileName,
|
|
246
|
+
mimeType: input.mimeType,
|
|
247
|
+
size: input.size,
|
|
248
|
+
disk: this.diskName,
|
|
249
|
+
path: input.path,
|
|
250
|
+
order: await this.deps.store.nextOrder(this.ownerType, input.actor.id, this.collection),
|
|
251
|
+
customProperties: {
|
|
252
|
+
agentAttachment: input.state
|
|
253
|
+
},
|
|
254
|
+
conversions: {},
|
|
255
|
+
createdAt: timestamp,
|
|
256
|
+
updatedAt: timestamp
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
isOurs(record) {
|
|
260
|
+
return record.ownerType === this.ownerType && record.collection === this.collection;
|
|
261
|
+
}
|
|
262
|
+
async ownRecord(mediaId, actor) {
|
|
263
|
+
const record = await this.deps.store.find(mediaId);
|
|
264
|
+
if (record === null || !this.isOurs(record) || record.ownerId !== actor.id) return null;
|
|
265
|
+
return record;
|
|
266
|
+
}
|
|
267
|
+
async canUse(record, actor) {
|
|
268
|
+
const allowed = await this.defaultAccess(record, actor);
|
|
269
|
+
const custom = this.options.canAccess;
|
|
270
|
+
return custom === void 0 ? allowed : await custom({
|
|
271
|
+
record,
|
|
272
|
+
actor,
|
|
273
|
+
allowed
|
|
274
|
+
}) === true;
|
|
275
|
+
}
|
|
276
|
+
async defaultAccess(record, actor) {
|
|
277
|
+
if (record.ownerId === actor.id) return true;
|
|
278
|
+
const referenced = this.deps.agentStore?.referencedMediaIds?.bind(this.deps.agentStore);
|
|
279
|
+
if (referenced === void 0) return false;
|
|
280
|
+
return (await referenced(actor.id, [
|
|
281
|
+
record.id
|
|
282
|
+
])).includes(record.id);
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* A ready record as-is; a pending one promoted to ready once its bytes are on the disk at exactly
|
|
286
|
+
* the declared size. `'missing'` while they are not; `'mismatch'` when they arrived at another size.
|
|
287
|
+
*/
|
|
288
|
+
async ensureReady(record) {
|
|
289
|
+
if (stateOf(record).status === "ready") return record;
|
|
290
|
+
const disk = this.deps.storage.disk(record.disk);
|
|
291
|
+
if (!await disk.exists(record.path)) return "missing";
|
|
292
|
+
const size = await disk.size(record.path);
|
|
293
|
+
if (size !== record.size || size > this.maxBytes) return "mismatch";
|
|
294
|
+
const ready = await this.deps.store.save({
|
|
295
|
+
...record,
|
|
296
|
+
customProperties: {
|
|
297
|
+
...record.customProperties,
|
|
298
|
+
agentAttachment: {
|
|
299
|
+
status: "ready"
|
|
300
|
+
}
|
|
301
|
+
},
|
|
302
|
+
updatedAt: this.now()
|
|
303
|
+
});
|
|
304
|
+
this.announce(ready);
|
|
305
|
+
return ready;
|
|
306
|
+
}
|
|
307
|
+
announce(record) {
|
|
308
|
+
if (this.options.indexForRag !== true) return;
|
|
309
|
+
(0, import_nestjs_media.publishMedia)("attach", {
|
|
310
|
+
id: record.id,
|
|
311
|
+
ownerType: record.ownerType,
|
|
312
|
+
ownerId: record.ownerId,
|
|
313
|
+
collection: record.collection,
|
|
314
|
+
disk: record.disk,
|
|
315
|
+
path: record.path,
|
|
316
|
+
size: record.size,
|
|
317
|
+
mimeType: record.mimeType
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* `forModel: false` is what goes back to the uploader: never inline bytes (they already have the
|
|
322
|
+
* file), so an inline-only disk answers with an empty url there.
|
|
323
|
+
*/
|
|
324
|
+
async toAttachment(record, { forModel }) {
|
|
325
|
+
const base = {
|
|
326
|
+
mediaId: record.id,
|
|
327
|
+
contentType: record.mimeType,
|
|
328
|
+
name: record.fileName
|
|
329
|
+
};
|
|
330
|
+
const disk = this.deps.storage.disk(record.disk);
|
|
331
|
+
if (this.options.resolveUrl !== void 0) {
|
|
332
|
+
return {
|
|
333
|
+
...base,
|
|
334
|
+
url: await this.options.resolveUrl(record, {
|
|
335
|
+
disk
|
|
336
|
+
})
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
if (this.options.visibility === "public") {
|
|
340
|
+
return {
|
|
341
|
+
...base,
|
|
342
|
+
url: await disk.url(record.path)
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
if (disk.capabilities.presign) {
|
|
346
|
+
const ttl = this.options.urlExpiresInSeconds ?? DEFAULT_AGENT_MEDIA_URL_TTL_SECONDS;
|
|
347
|
+
return {
|
|
348
|
+
...base,
|
|
349
|
+
url: await disk.temporaryUrl(record.path, ttl, {
|
|
350
|
+
responseContentType: record.mimeType
|
|
351
|
+
})
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
if (!forModel) return {
|
|
355
|
+
...base,
|
|
356
|
+
url: ""
|
|
357
|
+
};
|
|
358
|
+
if (!this.warnedInline) {
|
|
359
|
+
this.warnedInline = true;
|
|
360
|
+
this.logger.warn(`disk "${record.disk}" cannot presign urls: attachments reach the model inline (data: urls, persisted with each message). Use a presign-capable disk or resolveUrl in production.`);
|
|
361
|
+
}
|
|
362
|
+
const bytes = await disk.get(record.path);
|
|
363
|
+
return {
|
|
364
|
+
...base,
|
|
365
|
+
url: `data:${record.mimeType};base64,${bytes.toString("base64")}`
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
function stateOf(record) {
|
|
370
|
+
const state = record.customProperties.agentAttachment;
|
|
371
|
+
return state ?? {
|
|
372
|
+
status: "ready"
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
__name(stateOf, "stateOf");
|
|
376
|
+
function safeFileName(filename) {
|
|
377
|
+
const last = filename.split(/[\\/]/).pop() ?? "";
|
|
378
|
+
const cleaned = last.replace(/[\u0000-\u001f\u007f]/g, "").trim();
|
|
379
|
+
return cleaned === "" || cleaned === "." || cleaned === ".." ? "file" : cleaned;
|
|
380
|
+
}
|
|
381
|
+
__name(safeFileName, "safeFileName");
|
|
382
|
+
function stripExtension(fileName) {
|
|
383
|
+
const dot = fileName.lastIndexOf(".");
|
|
384
|
+
return dot > 0 ? fileName.slice(0, dot) : fileName;
|
|
385
|
+
}
|
|
386
|
+
__name(stripExtension, "stripExtension");
|
|
387
|
+
|
|
388
|
+
// src/media/media-attachments.module.ts
|
|
389
|
+
var import_nestjs_agent_core2 = require("@dudousxd/nestjs-agent-core");
|
|
390
|
+
var import_nestjs_media2 = require("@dudousxd/nestjs-media");
|
|
391
|
+
var import_common3 = require("@nestjs/common");
|
|
392
|
+
var import_core = require("@nestjs/core");
|
|
393
|
+
|
|
394
|
+
// src/media/media-uploads.controller.ts
|
|
395
|
+
var import_nestjs_agent_core = require("@dudousxd/nestjs-agent-core");
|
|
396
|
+
var import_common2 = require("@nestjs/common");
|
|
397
|
+
|
|
398
|
+
// src/media/tokens.ts
|
|
399
|
+
var AGENT_MEDIA_ATTACHMENTS = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent:media-attachments");
|
|
400
|
+
var AGENT_MEDIA_ATTACHMENTS_OPTIONS = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent:media-attachments-options");
|
|
401
|
+
|
|
402
|
+
// src/media/media-uploads.controller.ts
|
|
403
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
404
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
405
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
406
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
407
|
+
} else {
|
|
408
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
409
|
+
if (d = decorators[i]) {
|
|
410
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
415
|
+
}
|
|
416
|
+
__name(_ts_decorate, "_ts_decorate");
|
|
417
|
+
function _ts_metadata(metadataKey, metadataValue) {
|
|
418
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
|
|
419
|
+
return Reflect.metadata(metadataKey, metadataValue);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
__name(_ts_metadata, "_ts_metadata");
|
|
423
|
+
function _ts_param(paramIndex, decorator) {
|
|
424
|
+
return function(target, key) {
|
|
425
|
+
decorator(target, key, paramIndex);
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
__name(_ts_param, "_ts_param");
|
|
429
|
+
var AgentMediaUploadsController = class {
|
|
430
|
+
static {
|
|
431
|
+
__name(this, "AgentMediaUploadsController");
|
|
432
|
+
}
|
|
433
|
+
actorResolver;
|
|
434
|
+
staging;
|
|
435
|
+
constructor(actorResolver, staging) {
|
|
436
|
+
this.actorResolver = actorResolver;
|
|
437
|
+
this.staging = staging;
|
|
438
|
+
}
|
|
439
|
+
/** `{ filename, contentType, size }` → `{ mediaId, uploadId, location }`. */
|
|
440
|
+
async begin(req, body) {
|
|
441
|
+
if (typeof body !== "object" || body === null) {
|
|
442
|
+
throw new import_common2.BadRequestException("body must be { filename, contentType, size }");
|
|
443
|
+
}
|
|
444
|
+
const { filename, contentType, size } = body;
|
|
445
|
+
const actor = await this.actorResolver.resolve(req);
|
|
446
|
+
return this.staging.beginUpload({
|
|
447
|
+
actor,
|
|
448
|
+
filename,
|
|
449
|
+
contentType,
|
|
450
|
+
size
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
async complete(req, mediaId) {
|
|
454
|
+
const actor = await this.actorResolver.resolve(req);
|
|
455
|
+
const attachment = await this.staging.completeUpload({
|
|
456
|
+
actor,
|
|
457
|
+
mediaId
|
|
458
|
+
});
|
|
459
|
+
if (attachment === null) throw new import_common2.NotFoundException(`attachment ${mediaId} not found`);
|
|
460
|
+
return attachment;
|
|
461
|
+
}
|
|
462
|
+
async discard(req, mediaId) {
|
|
463
|
+
const actor = await this.actorResolver.resolve(req);
|
|
464
|
+
if (!await this.staging.discard({
|
|
465
|
+
actor,
|
|
466
|
+
mediaId
|
|
467
|
+
})) {
|
|
468
|
+
throw new import_common2.NotFoundException(`attachment ${mediaId} not found`);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
_ts_decorate([
|
|
473
|
+
(0, import_common2.Post)(),
|
|
474
|
+
_ts_param(0, (0, import_common2.Req)()),
|
|
475
|
+
_ts_param(1, (0, import_common2.Body)()),
|
|
476
|
+
_ts_metadata("design:type", Function),
|
|
477
|
+
_ts_metadata("design:paramtypes", [
|
|
478
|
+
typeof Request === "undefined" ? Object : Request,
|
|
479
|
+
Object
|
|
480
|
+
]),
|
|
481
|
+
_ts_metadata("design:returntype", Promise)
|
|
482
|
+
], AgentMediaUploadsController.prototype, "begin", null);
|
|
483
|
+
_ts_decorate([
|
|
484
|
+
(0, import_common2.Post)(":mediaId/complete"),
|
|
485
|
+
(0, import_common2.HttpCode)(200),
|
|
486
|
+
_ts_param(0, (0, import_common2.Req)()),
|
|
487
|
+
_ts_param(1, (0, import_common2.Param)("mediaId")),
|
|
488
|
+
_ts_metadata("design:type", Function),
|
|
489
|
+
_ts_metadata("design:paramtypes", [
|
|
490
|
+
typeof Request === "undefined" ? Object : Request,
|
|
491
|
+
String
|
|
492
|
+
]),
|
|
493
|
+
_ts_metadata("design:returntype", Promise)
|
|
494
|
+
], AgentMediaUploadsController.prototype, "complete", null);
|
|
495
|
+
_ts_decorate([
|
|
496
|
+
(0, import_common2.Delete)(":mediaId"),
|
|
497
|
+
(0, import_common2.HttpCode)(204),
|
|
498
|
+
_ts_param(0, (0, import_common2.Req)()),
|
|
499
|
+
_ts_param(1, (0, import_common2.Param)("mediaId")),
|
|
500
|
+
_ts_metadata("design:type", Function),
|
|
501
|
+
_ts_metadata("design:paramtypes", [
|
|
502
|
+
typeof Request === "undefined" ? Object : Request,
|
|
503
|
+
String
|
|
504
|
+
]),
|
|
505
|
+
_ts_metadata("design:returntype", Promise)
|
|
506
|
+
], AgentMediaUploadsController.prototype, "discard", null);
|
|
507
|
+
AgentMediaUploadsController = _ts_decorate([
|
|
508
|
+
(0, import_common2.Controller)("attachments/uploads"),
|
|
509
|
+
_ts_param(0, (0, import_common2.Inject)(import_nestjs_agent_core.AGENT_ACTOR_RESOLVER)),
|
|
510
|
+
_ts_param(1, (0, import_common2.Inject)(AGENT_MEDIA_ATTACHMENTS)),
|
|
511
|
+
_ts_metadata("design:type", Function),
|
|
512
|
+
_ts_metadata("design:paramtypes", [
|
|
513
|
+
typeof ActorResolver === "undefined" ? Object : ActorResolver,
|
|
514
|
+
typeof MediaAttachmentStaging === "undefined" ? Object : MediaAttachmentStaging
|
|
515
|
+
])
|
|
516
|
+
], AgentMediaUploadsController);
|
|
517
|
+
|
|
518
|
+
// src/media/media-attachments.module.ts
|
|
519
|
+
function _ts_decorate2(decorators, target, key, desc) {
|
|
520
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
521
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
522
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
523
|
+
} else {
|
|
524
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
525
|
+
if (d = decorators[i]) {
|
|
526
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
531
|
+
}
|
|
532
|
+
__name(_ts_decorate2, "_ts_decorate");
|
|
533
|
+
var GUARDS_METADATA = "__guards__";
|
|
534
|
+
function stagingProvider() {
|
|
535
|
+
return {
|
|
536
|
+
provide: AGENT_MEDIA_ATTACHMENTS,
|
|
537
|
+
inject: [
|
|
538
|
+
AGENT_MEDIA_ATTACHMENTS_OPTIONS,
|
|
539
|
+
import_nestjs_media2.MEDIA_STORAGE_SHARED,
|
|
540
|
+
import_nestjs_media2.MEDIA_STORE,
|
|
541
|
+
import_nestjs_media2.MEDIA_UPLOADS,
|
|
542
|
+
{
|
|
543
|
+
token: import_nestjs_agent_core2.AGENT_STORE,
|
|
544
|
+
optional: true
|
|
545
|
+
}
|
|
546
|
+
],
|
|
547
|
+
useFactory: /* @__PURE__ */ __name((options, storage, store, uploads, agentStore) => {
|
|
548
|
+
if (store === null) {
|
|
549
|
+
throw new Error("AgentMediaAttachmentsModule: MediaModule has no `store`. Chat attachments are media records, so configure MediaModule with a MediaStore (e.g. a database adapter).");
|
|
550
|
+
}
|
|
551
|
+
return new MediaAttachmentStaging2({
|
|
552
|
+
storage,
|
|
553
|
+
store,
|
|
554
|
+
uploads,
|
|
555
|
+
...agentStore !== void 0 ? {
|
|
556
|
+
agentStore
|
|
557
|
+
} : {}
|
|
558
|
+
}, options);
|
|
559
|
+
}, "useFactory")
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
__name(stagingProvider, "stagingProvider");
|
|
563
|
+
function build(mount, optionsProvider, imports = []) {
|
|
564
|
+
const routes = mount.routes !== false;
|
|
565
|
+
Reflect.defineMetadata(GUARDS_METADATA, mount.guards ?? [], AgentMediaUploadsController);
|
|
566
|
+
const guards = [
|
|
567
|
+
...new Set(mount.guards ?? [])
|
|
568
|
+
];
|
|
569
|
+
return {
|
|
570
|
+
module: AgentMediaAttachmentsModule,
|
|
571
|
+
global: true,
|
|
572
|
+
imports: [
|
|
573
|
+
...imports,
|
|
574
|
+
...routes ? [
|
|
575
|
+
import_core.RouterModule.register([
|
|
576
|
+
{
|
|
577
|
+
path: mount.path ?? "agent",
|
|
578
|
+
module: AgentMediaAttachmentsModule
|
|
579
|
+
}
|
|
580
|
+
])
|
|
581
|
+
] : []
|
|
582
|
+
],
|
|
583
|
+
controllers: routes ? [
|
|
584
|
+
AgentMediaUploadsController
|
|
585
|
+
] : [],
|
|
586
|
+
providers: [
|
|
587
|
+
optionsProvider,
|
|
588
|
+
stagingProvider(),
|
|
589
|
+
{
|
|
590
|
+
provide: import_nestjs_agent_core2.AGENT_ATTACHMENT_STAGING,
|
|
591
|
+
useExisting: AGENT_MEDIA_ATTACHMENTS
|
|
592
|
+
},
|
|
593
|
+
{
|
|
594
|
+
provide: MediaAttachmentStaging2,
|
|
595
|
+
useExisting: AGENT_MEDIA_ATTACHMENTS
|
|
596
|
+
},
|
|
597
|
+
...guards
|
|
598
|
+
],
|
|
599
|
+
exports: [
|
|
600
|
+
import_nestjs_agent_core2.AGENT_ATTACHMENT_STAGING,
|
|
601
|
+
AGENT_MEDIA_ATTACHMENTS,
|
|
602
|
+
MediaAttachmentStaging2
|
|
603
|
+
]
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
__name(build, "build");
|
|
607
|
+
var AgentMediaAttachmentsModule = class {
|
|
608
|
+
static {
|
|
609
|
+
__name(this, "AgentMediaAttachmentsModule");
|
|
610
|
+
}
|
|
611
|
+
static forRoot(options = {}) {
|
|
612
|
+
return build(options, {
|
|
613
|
+
provide: AGENT_MEDIA_ATTACHMENTS_OPTIONS,
|
|
614
|
+
useValue: options
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
static forRootAsync(options) {
|
|
618
|
+
return build(options, {
|
|
619
|
+
provide: AGENT_MEDIA_ATTACHMENTS_OPTIONS,
|
|
620
|
+
inject: options.inject ?? [],
|
|
621
|
+
useFactory: options.useFactory
|
|
622
|
+
}, options.imports);
|
|
623
|
+
}
|
|
624
|
+
};
|
|
625
|
+
AgentMediaAttachmentsModule = _ts_decorate2([
|
|
626
|
+
(0, import_common3.Global)(),
|
|
627
|
+
(0, import_common3.Module)({})
|
|
628
|
+
], AgentMediaAttachmentsModule);
|
|
629
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
630
|
+
0 && (module.exports = {
|
|
631
|
+
AGENT_MEDIA_ATTACHMENTS,
|
|
632
|
+
AGENT_MEDIA_ATTACHMENTS_OPTIONS,
|
|
633
|
+
AgentMediaAttachmentsModule,
|
|
634
|
+
AgentMediaUploadsController,
|
|
635
|
+
DEFAULT_AGENT_MEDIA_COLLECTION,
|
|
636
|
+
DEFAULT_AGENT_MEDIA_OWNER_TYPE,
|
|
637
|
+
DEFAULT_AGENT_MEDIA_TUS_BASE_PATH,
|
|
638
|
+
DEFAULT_AGENT_MEDIA_URL_TTL_SECONDS,
|
|
639
|
+
MediaAttachmentStaging
|
|
640
|
+
});
|
|
641
|
+
//# sourceMappingURL=index.cjs.map
|