@ai-matrx/media 0.7.15 → 0.8.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/CHANGELOG.md +16 -0
- package/dist/files.cjs +1298 -0
- package/dist/files.cjs.map +1 -0
- package/dist/files.d.cts +1171 -0
- package/dist/files.d.ts +1171 -0
- package/dist/files.js +1275 -0
- package/dist/files.js.map +1 -0
- package/package.json +14 -3
package/dist/files.cjs
ADDED
|
@@ -0,0 +1,1298 @@
|
|
|
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 __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/files.ts
|
|
21
|
+
var files_exports = {};
|
|
22
|
+
__export(files_exports, {
|
|
23
|
+
CloudFolderDescriptions: () => CloudFolderDescriptions,
|
|
24
|
+
CloudFolders: () => CloudFolders,
|
|
25
|
+
ExternalFetchError: () => ExternalFetchError,
|
|
26
|
+
FileAccessDeniedError: () => FileAccessDeniedError,
|
|
27
|
+
FileDeletedError: () => FileDeletedError,
|
|
28
|
+
FileHandlerError: () => FileHandlerError,
|
|
29
|
+
FileNotFoundError: () => FileNotFoundError,
|
|
30
|
+
FileUploadError: () => FileUploadError,
|
|
31
|
+
ShareLinkInvalidError: () => ShareLinkInvalidError,
|
|
32
|
+
UploadCancelledError: () => UploadCancelledError,
|
|
33
|
+
VideoPublishDate: () => VideoPublishDate,
|
|
34
|
+
blockFromMediaRef: () => blockFromMediaRef,
|
|
35
|
+
composeUploadFolderPath: () => composeUploadFolderPath,
|
|
36
|
+
createTrackedObjectUrl: () => createTrackedObjectUrl,
|
|
37
|
+
deriveViewerUrl: () => deriveViewerUrl,
|
|
38
|
+
extractFileIdFromUrl: () => extractFileIdFromUrl,
|
|
39
|
+
fileIdToMediaRef: () => fileIdToMediaRef,
|
|
40
|
+
folderForAgentApp: () => folderForAgentApp,
|
|
41
|
+
folderForAgentBlock: () => folderForAgentBlock,
|
|
42
|
+
folderForCaptures: () => folderForCaptures,
|
|
43
|
+
folderForConversation: () => folderForConversation,
|
|
44
|
+
folderForIntakeAsset: () => folderForIntakeAsset,
|
|
45
|
+
folderForOrg: () => folderForOrg,
|
|
46
|
+
folderForPodcast: () => folderForPodcast,
|
|
47
|
+
folderForProductCaptureItem: () => folderForProductCaptureItem,
|
|
48
|
+
folderForTask: () => folderForTask,
|
|
49
|
+
folderForWarRoomThread: () => folderForWarRoomThread,
|
|
50
|
+
formatVideoPublishDate: () => formatVideoPublishDate,
|
|
51
|
+
formatVideoPublishDateTitle: () => formatVideoPublishDateTitle,
|
|
52
|
+
fromCxAudioPart: () => fromCxAudioPart,
|
|
53
|
+
fromCxMediaPart: () => fromCxMediaPart,
|
|
54
|
+
fromCxVideoPart: () => fromCxVideoPart,
|
|
55
|
+
fromImageOutputData: () => fromImageOutputData,
|
|
56
|
+
fromMediaBlock: () => fromMediaBlock,
|
|
57
|
+
fromPartialImageData: () => fromPartialImageData,
|
|
58
|
+
fromRenderBlock: () => fromRenderBlock,
|
|
59
|
+
imageBlockFromMediaRef: () => imageBlockFromMediaRef,
|
|
60
|
+
isAudioBlock: () => isAudioBlock,
|
|
61
|
+
isConventionalFolder: () => isConventionalFolder,
|
|
62
|
+
isDocumentBlock: () => isDocumentBlock,
|
|
63
|
+
isExternalImageBlock: () => isExternalImageBlock,
|
|
64
|
+
isExternalMediaBlock: () => isExternalMediaBlock,
|
|
65
|
+
isHiddenFolder: () => isHiddenFolder,
|
|
66
|
+
isImageBlock: () => isImageBlock,
|
|
67
|
+
isMatrxImageBlock: () => isMatrxImageBlock,
|
|
68
|
+
isMatrxMediaBlock: () => isMatrxMediaBlock,
|
|
69
|
+
isMediaBlockData: () => isMediaBlockData,
|
|
70
|
+
isSystemPath: () => isSystemPath,
|
|
71
|
+
isUnifiedImageBlock: () => isUnifiedImageBlock,
|
|
72
|
+
isUnifiedMediaBlock: () => isUnifiedMediaBlock,
|
|
73
|
+
isUploadCancelledError: () => isUploadCancelledError,
|
|
74
|
+
isVideoBlock: () => isVideoBlock,
|
|
75
|
+
isYouTubeBlock: () => isYouTubeBlock,
|
|
76
|
+
parseFilenameFromUrl: () => parseFilenameFromUrl,
|
|
77
|
+
parseGenerationMetadata: () => parseGenerationMetadata,
|
|
78
|
+
resolveDefaultVisibility: () => resolveDefaultVisibility,
|
|
79
|
+
revokeTrackedObjectUrl: () => revokeTrackedObjectUrl,
|
|
80
|
+
toCxMediaPart: () => toCxMediaPart,
|
|
81
|
+
toVisibility: () => toVisibility,
|
|
82
|
+
urlToMediaRef: () => urlToMediaRef,
|
|
83
|
+
videoBlockFromMediaRef: () => videoBlockFromMediaRef,
|
|
84
|
+
videoPublishDateFromMetadata: () => videoPublishDateFromMetadata
|
|
85
|
+
});
|
|
86
|
+
module.exports = __toCommonJS(files_exports);
|
|
87
|
+
|
|
88
|
+
// src/files/ref-types.ts
|
|
89
|
+
function toVisibility(raw) {
|
|
90
|
+
if (raw === "public") return "public";
|
|
91
|
+
if (raw === "link") return "link";
|
|
92
|
+
if (raw === "internal") return "internal";
|
|
93
|
+
return "personal";
|
|
94
|
+
}
|
|
95
|
+
function fileIdToMediaRef(fileId, mimeType) {
|
|
96
|
+
const ref = { file_id: fileId };
|
|
97
|
+
if (mimeType) ref.mime_type = mimeType;
|
|
98
|
+
return ref;
|
|
99
|
+
}
|
|
100
|
+
function urlToMediaRef(url, mimeType) {
|
|
101
|
+
const ref = { url };
|
|
102
|
+
if (mimeType) ref.mime_type = mimeType;
|
|
103
|
+
return ref;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// src/files/blocks/types.ts
|
|
107
|
+
function parseGenerationMetadata(metadata) {
|
|
108
|
+
if (!metadata) return null;
|
|
109
|
+
const raw = metadata.generation;
|
|
110
|
+
if (!raw || typeof raw !== "object") return null;
|
|
111
|
+
const w = raw;
|
|
112
|
+
if (typeof w.kind !== "string") return null;
|
|
113
|
+
if (typeof w.provider !== "string") return null;
|
|
114
|
+
if (typeof w.model !== "string") return null;
|
|
115
|
+
if (typeof w.prompt !== "string") return null;
|
|
116
|
+
return {
|
|
117
|
+
kind: w.kind,
|
|
118
|
+
provider: w.provider,
|
|
119
|
+
model: w.model,
|
|
120
|
+
prompt: w.prompt,
|
|
121
|
+
negativePrompt: w.negative_prompt ?? null,
|
|
122
|
+
revisedPrompt: w.revised_prompt ?? null,
|
|
123
|
+
width: w.width ?? null,
|
|
124
|
+
height: w.height ?? null,
|
|
125
|
+
aspectRatio: w.aspect_ratio ?? null,
|
|
126
|
+
durationSeconds: w.duration_seconds ?? null,
|
|
127
|
+
quality: w.quality ?? null,
|
|
128
|
+
style: w.style ?? null,
|
|
129
|
+
seed: w.seed ?? null,
|
|
130
|
+
steps: w.steps ?? null,
|
|
131
|
+
cfgScale: w.cfg_scale ?? null,
|
|
132
|
+
nRequested: w.n_requested ?? 1,
|
|
133
|
+
nReturned: w.n_returned ?? 1,
|
|
134
|
+
responseId: w.response_id ?? null,
|
|
135
|
+
durationMs: w.duration_ms ?? null,
|
|
136
|
+
costUsd: w.cost_usd ?? null,
|
|
137
|
+
finishReason: w.finish_reason ?? null,
|
|
138
|
+
safetyFlagged: w.safety_flagged ?? false,
|
|
139
|
+
providerExtras: w.provider_extras ?? {}
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// src/files/blocks/guards.ts
|
|
144
|
+
function isObject(value) {
|
|
145
|
+
return value !== null && typeof value === "object";
|
|
146
|
+
}
|
|
147
|
+
function hasMediaShape(value) {
|
|
148
|
+
if (!isObject(value)) return false;
|
|
149
|
+
if (typeof value.kind !== "string") return false;
|
|
150
|
+
if (value.origin !== "matrx" && value.origin !== "external") return false;
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
function isMatrxMediaBlock(value) {
|
|
154
|
+
if (!hasMediaShape(value)) return false;
|
|
155
|
+
if (value.origin !== "matrx") return false;
|
|
156
|
+
return typeof value.fileId === "string";
|
|
157
|
+
}
|
|
158
|
+
function isExternalMediaBlock(value) {
|
|
159
|
+
if (!hasMediaShape(value)) return false;
|
|
160
|
+
if (value.origin !== "external") return false;
|
|
161
|
+
return typeof value.externalUrl === "string";
|
|
162
|
+
}
|
|
163
|
+
function isUnifiedMediaBlock(value) {
|
|
164
|
+
return isMatrxMediaBlock(value) || isExternalMediaBlock(value);
|
|
165
|
+
}
|
|
166
|
+
function isImageBlock(value) {
|
|
167
|
+
return isUnifiedMediaBlock(value) && value.kind === "image";
|
|
168
|
+
}
|
|
169
|
+
function isVideoBlock(value) {
|
|
170
|
+
return isUnifiedMediaBlock(value) && value.kind === "video";
|
|
171
|
+
}
|
|
172
|
+
function isAudioBlock(value) {
|
|
173
|
+
return isUnifiedMediaBlock(value) && value.kind === "audio";
|
|
174
|
+
}
|
|
175
|
+
function isDocumentBlock(value) {
|
|
176
|
+
return isUnifiedMediaBlock(value) && value.kind === "document";
|
|
177
|
+
}
|
|
178
|
+
function isYouTubeBlock(value) {
|
|
179
|
+
return isUnifiedMediaBlock(value) && value.kind === "youtube";
|
|
180
|
+
}
|
|
181
|
+
function isMatrxImageBlock(value) {
|
|
182
|
+
return isImageBlock(value) && value.origin === "matrx";
|
|
183
|
+
}
|
|
184
|
+
function isExternalImageBlock(value) {
|
|
185
|
+
return isImageBlock(value) && value.origin === "external";
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// src/files/blocks/image/guards.ts
|
|
189
|
+
function isUnifiedImageBlock(value) {
|
|
190
|
+
return isImageBlock(value);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// src/files/blocks/adapters/from-cx-av-part.ts
|
|
194
|
+
function str(v) {
|
|
195
|
+
return typeof v === "string" && v.length > 0 ? v : null;
|
|
196
|
+
}
|
|
197
|
+
function liftUrls(part) {
|
|
198
|
+
const metadata = part.metadata ?? null;
|
|
199
|
+
const cdn = str(metadata?.cdn_url);
|
|
200
|
+
const download = str(metadata?.download_url);
|
|
201
|
+
return {
|
|
202
|
+
// The on-disk `url` is a save-time snapshot; forward it as the fallback
|
|
203
|
+
// the handler only uses when no identity is available.
|
|
204
|
+
url: str(part.url) ?? cdn ?? "",
|
|
205
|
+
file_id: str(part.file_id) ?? str(metadata?.file_id),
|
|
206
|
+
cdn_url: cdn,
|
|
207
|
+
download_url: download
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
function fromCxAudioPart(part) {
|
|
211
|
+
return {
|
|
212
|
+
type: "audio_output",
|
|
213
|
+
...liftUrls(part),
|
|
214
|
+
mime_type: part.mime_type ?? "audio/*",
|
|
215
|
+
transcription_result: part.transcription_result ?? null
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
function fromCxVideoPart(part) {
|
|
219
|
+
return {
|
|
220
|
+
type: "video_output",
|
|
221
|
+
...liftUrls(part),
|
|
222
|
+
mime_type: part.mime_type ?? "video/*",
|
|
223
|
+
// A video reference's role (extend / restyle) and its `@name` — carried so
|
|
224
|
+
// a reload redisplays them (see ReferenceRoleCaption).
|
|
225
|
+
reference_role: part.role ?? null,
|
|
226
|
+
reference_name: part.name ?? null
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// src/files/blocks/adapters/from-media-block.ts
|
|
231
|
+
function pickStatus(s) {
|
|
232
|
+
return s === "streaming" || s === "error" ? s : "complete";
|
|
233
|
+
}
|
|
234
|
+
function pickVisibility(v) {
|
|
235
|
+
return toVisibility(v);
|
|
236
|
+
}
|
|
237
|
+
function commonFields(wire) {
|
|
238
|
+
return {
|
|
239
|
+
status: pickStatus(wire.status),
|
|
240
|
+
progress: wire.progress ?? null,
|
|
241
|
+
errorMessage: wire.error_message ?? null,
|
|
242
|
+
mimeType: wire.mime_type ?? null,
|
|
243
|
+
fileName: wire.file_name ?? null,
|
|
244
|
+
sizeBytes: wire.size_bytes ?? null,
|
|
245
|
+
base64: wire.base64 ?? null,
|
|
246
|
+
metadata: wire.metadata ?? null
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
function matrxFields(wire) {
|
|
250
|
+
if (wire.origin !== "matrx") return null;
|
|
251
|
+
if (!wire.file_id) return null;
|
|
252
|
+
return {
|
|
253
|
+
origin: "matrx",
|
|
254
|
+
fileId: wire.file_id,
|
|
255
|
+
visibility: pickVisibility(wire.visibility),
|
|
256
|
+
cdnUrl: wire.cdn_url ?? null,
|
|
257
|
+
downloadUrl: wire.download_url ?? null,
|
|
258
|
+
parentFileId: wire.parent_file_id ?? null,
|
|
259
|
+
derivationKind: wire.derivation_kind ?? null
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
function externalFields(wire) {
|
|
263
|
+
return {
|
|
264
|
+
origin: "external",
|
|
265
|
+
externalUrl: wire.external_url ?? "",
|
|
266
|
+
sourceLabel: wire.source_label ?? null
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
function liftImage(wire) {
|
|
270
|
+
const common = commonFields(wire);
|
|
271
|
+
const matrx = matrxFields(wire);
|
|
272
|
+
const imageBits = {
|
|
273
|
+
kind: "image",
|
|
274
|
+
width: wire.width ?? null,
|
|
275
|
+
height: wire.height ?? null,
|
|
276
|
+
visionClass: wire.vision_class ?? null
|
|
277
|
+
};
|
|
278
|
+
if (matrx) {
|
|
279
|
+
const block2 = { ...common, ...matrx, ...imageBits };
|
|
280
|
+
return block2;
|
|
281
|
+
}
|
|
282
|
+
const block = {
|
|
283
|
+
...common,
|
|
284
|
+
...externalFields(wire),
|
|
285
|
+
...imageBits
|
|
286
|
+
};
|
|
287
|
+
return block;
|
|
288
|
+
}
|
|
289
|
+
function liftVideo(wire) {
|
|
290
|
+
const common = commonFields(wire);
|
|
291
|
+
const matrx = matrxFields(wire);
|
|
292
|
+
const videoBits = {
|
|
293
|
+
kind: "video",
|
|
294
|
+
width: wire.width ?? null,
|
|
295
|
+
height: wire.height ?? null,
|
|
296
|
+
durationMs: wire.duration_ms ?? null,
|
|
297
|
+
posterUrl: wire.poster_url ?? null
|
|
298
|
+
};
|
|
299
|
+
if (matrx) {
|
|
300
|
+
const block2 = { ...common, ...matrx, ...videoBits };
|
|
301
|
+
return block2;
|
|
302
|
+
}
|
|
303
|
+
const block = {
|
|
304
|
+
...common,
|
|
305
|
+
...externalFields(wire),
|
|
306
|
+
...videoBits
|
|
307
|
+
};
|
|
308
|
+
return block;
|
|
309
|
+
}
|
|
310
|
+
function liftAudio(wire) {
|
|
311
|
+
const common = commonFields(wire);
|
|
312
|
+
const matrx = matrxFields(wire);
|
|
313
|
+
const audioBits = {
|
|
314
|
+
kind: "audio",
|
|
315
|
+
durationMs: wire.duration_ms ?? null,
|
|
316
|
+
transcript: wire.transcript ?? null
|
|
317
|
+
};
|
|
318
|
+
if (matrx) {
|
|
319
|
+
const block2 = { ...common, ...matrx, ...audioBits };
|
|
320
|
+
return block2;
|
|
321
|
+
}
|
|
322
|
+
const block = {
|
|
323
|
+
...common,
|
|
324
|
+
...externalFields(wire),
|
|
325
|
+
...audioBits
|
|
326
|
+
};
|
|
327
|
+
return block;
|
|
328
|
+
}
|
|
329
|
+
function liftDocument(wire) {
|
|
330
|
+
const common = commonFields(wire);
|
|
331
|
+
const matrx = matrxFields(wire);
|
|
332
|
+
const docBits = {
|
|
333
|
+
kind: "document",
|
|
334
|
+
pageCount: wire.page_count ?? null,
|
|
335
|
+
page1Url: wire.page1_url ?? null
|
|
336
|
+
};
|
|
337
|
+
if (matrx) {
|
|
338
|
+
const block2 = { ...common, ...matrx, ...docBits };
|
|
339
|
+
return block2;
|
|
340
|
+
}
|
|
341
|
+
const block = {
|
|
342
|
+
...common,
|
|
343
|
+
...externalFields(wire),
|
|
344
|
+
...docBits
|
|
345
|
+
};
|
|
346
|
+
return block;
|
|
347
|
+
}
|
|
348
|
+
function liftYouTube(wire) {
|
|
349
|
+
const common = commonFields(wire);
|
|
350
|
+
const block = {
|
|
351
|
+
...common,
|
|
352
|
+
...externalFields(wire),
|
|
353
|
+
kind: "youtube",
|
|
354
|
+
videoId: wire.video_id ?? null
|
|
355
|
+
};
|
|
356
|
+
return block;
|
|
357
|
+
}
|
|
358
|
+
function fromMediaBlock(wire) {
|
|
359
|
+
switch (wire.kind) {
|
|
360
|
+
case "image":
|
|
361
|
+
return liftImage(wire);
|
|
362
|
+
case "video":
|
|
363
|
+
return liftVideo(wire);
|
|
364
|
+
case "audio":
|
|
365
|
+
return liftAudio(wire);
|
|
366
|
+
case "document":
|
|
367
|
+
return liftDocument(wire);
|
|
368
|
+
case "youtube":
|
|
369
|
+
return liftYouTube(wire);
|
|
370
|
+
default: {
|
|
371
|
+
const base = wire;
|
|
372
|
+
const unknownKind = wire.kind;
|
|
373
|
+
const fallback = {
|
|
374
|
+
...commonFields(base),
|
|
375
|
+
...externalFields(base),
|
|
376
|
+
kind: "image",
|
|
377
|
+
width: null,
|
|
378
|
+
height: null,
|
|
379
|
+
visionClass: null,
|
|
380
|
+
metadata: {
|
|
381
|
+
...base.metadata ?? {},
|
|
382
|
+
_unknown_media_kind: unknownKind
|
|
383
|
+
},
|
|
384
|
+
errorMessage: `Unsupported media kind: ${unknownKind}`,
|
|
385
|
+
status: "error"
|
|
386
|
+
};
|
|
387
|
+
return fallback;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
function isMediaBlockData(value) {
|
|
392
|
+
if (value === null || typeof value !== "object") return false;
|
|
393
|
+
const v = value;
|
|
394
|
+
if (v.type !== "media_block") return false;
|
|
395
|
+
if (!v.block || typeof v.block !== "object") return false;
|
|
396
|
+
const b = v.block;
|
|
397
|
+
return typeof b.kind === "string" && typeof b.origin === "string";
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// src/files/blocks/adapters/from-media-ref.ts
|
|
401
|
+
function commonBase(ref) {
|
|
402
|
+
return {
|
|
403
|
+
status: "complete",
|
|
404
|
+
progress: null,
|
|
405
|
+
errorMessage: null,
|
|
406
|
+
mimeType: ref.mime_type ?? null,
|
|
407
|
+
fileName: null,
|
|
408
|
+
sizeBytes: null,
|
|
409
|
+
base64: null,
|
|
410
|
+
metadata: null
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
function matrxBase(fileId) {
|
|
414
|
+
return {
|
|
415
|
+
origin: "matrx",
|
|
416
|
+
fileId,
|
|
417
|
+
visibility: "personal",
|
|
418
|
+
cdnUrl: null,
|
|
419
|
+
downloadUrl: null,
|
|
420
|
+
parentFileId: null,
|
|
421
|
+
derivationKind: null
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
function externalBase(url) {
|
|
425
|
+
return {
|
|
426
|
+
origin: "external",
|
|
427
|
+
externalUrl: url,
|
|
428
|
+
sourceLabel: null
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
function imageBlockFromMediaRef(ref) {
|
|
432
|
+
if (!ref) return null;
|
|
433
|
+
const common = commonBase(ref);
|
|
434
|
+
const imageBits = {
|
|
435
|
+
kind: "image",
|
|
436
|
+
width: null,
|
|
437
|
+
height: null,
|
|
438
|
+
visionClass: null
|
|
439
|
+
};
|
|
440
|
+
if (ref.file_id) {
|
|
441
|
+
const block2 = {
|
|
442
|
+
...common,
|
|
443
|
+
...matrxBase(ref.file_id),
|
|
444
|
+
...imageBits
|
|
445
|
+
};
|
|
446
|
+
return block2;
|
|
447
|
+
}
|
|
448
|
+
const block = {
|
|
449
|
+
...common,
|
|
450
|
+
...externalBase(ref.url ?? ""),
|
|
451
|
+
...imageBits
|
|
452
|
+
};
|
|
453
|
+
return block;
|
|
454
|
+
}
|
|
455
|
+
function videoBlockFromMediaRef(ref) {
|
|
456
|
+
if (!ref) return null;
|
|
457
|
+
const common = commonBase(ref);
|
|
458
|
+
const videoBits = {
|
|
459
|
+
kind: "video",
|
|
460
|
+
width: null,
|
|
461
|
+
height: null,
|
|
462
|
+
durationMs: null,
|
|
463
|
+
posterUrl: null
|
|
464
|
+
};
|
|
465
|
+
if (ref.file_id) {
|
|
466
|
+
const block2 = {
|
|
467
|
+
...common,
|
|
468
|
+
...matrxBase(ref.file_id),
|
|
469
|
+
...videoBits
|
|
470
|
+
};
|
|
471
|
+
return block2;
|
|
472
|
+
}
|
|
473
|
+
const block = {
|
|
474
|
+
...common,
|
|
475
|
+
...externalBase(ref.url ?? ""),
|
|
476
|
+
...videoBits
|
|
477
|
+
};
|
|
478
|
+
return block;
|
|
479
|
+
}
|
|
480
|
+
function blockFromMediaRef(ref, kind) {
|
|
481
|
+
return kind === "video" ? videoBlockFromMediaRef(ref) : imageBlockFromMediaRef(ref);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// src/files/blocks/image/helpers/extract-file-id-from-url.ts
|
|
485
|
+
var import_uuid = require("@ai-matrx/kit/uuid");
|
|
486
|
+
function extractFileIdFromUrl(url) {
|
|
487
|
+
if (!url) return null;
|
|
488
|
+
try {
|
|
489
|
+
const parts = new URL(url).pathname.split("/").filter(Boolean);
|
|
490
|
+
if (parts.length >= 2) {
|
|
491
|
+
const candidate = parts[1];
|
|
492
|
+
if ((0, import_uuid.isUuidShape)(candidate)) return candidate;
|
|
493
|
+
}
|
|
494
|
+
const last = parts[parts.length - 1] ?? "";
|
|
495
|
+
const stripped = last.replace(/\.[^.]+$/, "");
|
|
496
|
+
if ((0, import_uuid.isUuidShape)(stripped)) return stripped;
|
|
497
|
+
return null;
|
|
498
|
+
} catch {
|
|
499
|
+
return null;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// src/files/blocks/image/helpers/parse-filename-from-url.ts
|
|
504
|
+
var FILENAME_QUERY_KEYS = [
|
|
505
|
+
"response-content-disposition",
|
|
506
|
+
"content-disposition"
|
|
507
|
+
];
|
|
508
|
+
function parseFilenameFromUrl(url) {
|
|
509
|
+
if (!url) return null;
|
|
510
|
+
let parsed;
|
|
511
|
+
try {
|
|
512
|
+
parsed = new URL(url);
|
|
513
|
+
} catch {
|
|
514
|
+
return null;
|
|
515
|
+
}
|
|
516
|
+
for (const key of FILENAME_QUERY_KEYS) {
|
|
517
|
+
const disposition = parsed.searchParams.get(key);
|
|
518
|
+
if (disposition) {
|
|
519
|
+
const name = parseContentDisposition(disposition);
|
|
520
|
+
if (name) return name;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
return null;
|
|
524
|
+
}
|
|
525
|
+
function parseContentDisposition(value) {
|
|
526
|
+
const extMatch = /filename\*\s*=\s*([A-Za-z0-9_-]+)'(?:[A-Za-z0-9_-]*)'([^;]+)/i.exec(value);
|
|
527
|
+
if (extMatch) {
|
|
528
|
+
const charset = extMatch[1];
|
|
529
|
+
const encoded = extMatch[2].trim();
|
|
530
|
+
try {
|
|
531
|
+
const decoded = decodeURIComponent(encoded);
|
|
532
|
+
if (charset.toLowerCase() === "utf-8") return safeName(decoded);
|
|
533
|
+
return safeName(decoded);
|
|
534
|
+
} catch {
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
const basicMatch = /filename\s*=\s*(?:"([^"]+)"|([^;]+))/i.exec(value);
|
|
538
|
+
if (basicMatch) {
|
|
539
|
+
const raw = (basicMatch[1] ?? basicMatch[2] ?? "").trim();
|
|
540
|
+
if (raw) return safeName(raw);
|
|
541
|
+
}
|
|
542
|
+
return null;
|
|
543
|
+
}
|
|
544
|
+
function safeName(name) {
|
|
545
|
+
const leaf = name.split(/[/\\]/).pop()?.trim();
|
|
546
|
+
if (!leaf) return null;
|
|
547
|
+
if (/[\x00-\x1f]/.test(leaf)) return null;
|
|
548
|
+
return leaf;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// src/files/blocks/image/adapters/from-image-output-data.ts
|
|
552
|
+
var import_files = require("@ai-matrx/data/files");
|
|
553
|
+
function metaString(metadata, key) {
|
|
554
|
+
if (!metadata) return null;
|
|
555
|
+
const value = metadata[key];
|
|
556
|
+
return typeof value === "string" ? value : null;
|
|
557
|
+
}
|
|
558
|
+
function metaNumber(metadata, key) {
|
|
559
|
+
if (!metadata) return null;
|
|
560
|
+
const value = metadata[key];
|
|
561
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
562
|
+
}
|
|
563
|
+
function metaVisibility(metadata) {
|
|
564
|
+
const value = metaString(metadata, "visibility");
|
|
565
|
+
if (value === "public" || value === "personal" || value === "internal" || value === "link")
|
|
566
|
+
return value;
|
|
567
|
+
if (value === "shared" || value === "private") return "personal";
|
|
568
|
+
return "personal";
|
|
569
|
+
}
|
|
570
|
+
function fromImageOutputData(data, carriedMetadata) {
|
|
571
|
+
const metadata = carriedMetadata ?? null;
|
|
572
|
+
const cdnUrl = data.cdn_url ?? null;
|
|
573
|
+
const downloadUrl = data.download_url ?? null;
|
|
574
|
+
const fallbackUrl = data.url;
|
|
575
|
+
const fallbackLooksSigned = (0, import_files.isSignedUrl)(fallbackUrl);
|
|
576
|
+
const finalCdnUrl = cdnUrl ?? (fallbackLooksSigned ? null : fallbackUrl ?? null);
|
|
577
|
+
const explicitFileId = data.file_id ?? null;
|
|
578
|
+
const inferredFileId = explicitFileId ?? extractFileIdFromUrl(finalCdnUrl) ?? extractFileIdFromUrl(fallbackUrl) ?? null;
|
|
579
|
+
const common = {
|
|
580
|
+
kind: "image",
|
|
581
|
+
base64: null,
|
|
582
|
+
mimeType: data.mime_type ?? null,
|
|
583
|
+
// Filename priority:
|
|
584
|
+
// 1. Explicit `file_name` in the event metadata (Python's intent).
|
|
585
|
+
// 2. The filename baked into the signed URL's
|
|
586
|
+
// `response-content-disposition` query param — Python sets this
|
|
587
|
+
// to the AI-chosen name on every signed-URL mint, and it's the
|
|
588
|
+
// ground truth for "what should the user see on download".
|
|
589
|
+
// 3. null — fall back to a generic `image.<ext>` at the callsite.
|
|
590
|
+
fileName: metaString(metadata, "file_name") ?? parseFilenameFromUrl(downloadUrl) ?? parseFilenameFromUrl(finalCdnUrl) ?? null,
|
|
591
|
+
width: metaNumber(metadata, "width"),
|
|
592
|
+
height: metaNumber(metadata, "height"),
|
|
593
|
+
// Phase 0 wire rename: prefer `size_bytes`, accept legacy `file_size`
|
|
594
|
+
// from in-flight services that haven't redeployed yet.
|
|
595
|
+
sizeBytes: metaNumber(metadata, "size_bytes") ?? metaNumber(metadata, "file_size"),
|
|
596
|
+
visionClass: metaString(metadata, "vision_class"),
|
|
597
|
+
status: "complete",
|
|
598
|
+
progress: null,
|
|
599
|
+
errorMessage: null,
|
|
600
|
+
metadata: metadata ?? null
|
|
601
|
+
};
|
|
602
|
+
if (inferredFileId) {
|
|
603
|
+
const matrx = {
|
|
604
|
+
...common,
|
|
605
|
+
origin: "matrx",
|
|
606
|
+
fileId: inferredFileId,
|
|
607
|
+
visibility: metaVisibility(metadata),
|
|
608
|
+
cdnUrl: finalCdnUrl,
|
|
609
|
+
downloadUrl,
|
|
610
|
+
// Phase 1b: thumbnails removed from MatrxImageBlock — the canonical
|
|
611
|
+
// source is `Asset.variants["thumbnail_url"].url` via GET /assets/{id}.
|
|
612
|
+
// For top-level listings, `CloudFile.thumbnailUrl` (lifted from
|
|
613
|
+
// `FileRecord.thumbnail_url`) is the FE-side cache of that resolved URL.
|
|
614
|
+
parentFileId: metaString(metadata, "parent_file_id"),
|
|
615
|
+
derivationKind: metaString(metadata, "derivation_kind")
|
|
616
|
+
};
|
|
617
|
+
return matrx;
|
|
618
|
+
}
|
|
619
|
+
const externalUrl = fallbackUrl ?? finalCdnUrl ?? "";
|
|
620
|
+
if (!externalUrl) {
|
|
621
|
+
const broken = {
|
|
622
|
+
...common,
|
|
623
|
+
origin: "external",
|
|
624
|
+
externalUrl: "",
|
|
625
|
+
sourceLabel: metaString(metadata, "source_label")
|
|
626
|
+
};
|
|
627
|
+
return broken;
|
|
628
|
+
}
|
|
629
|
+
const external = {
|
|
630
|
+
...common,
|
|
631
|
+
origin: "external",
|
|
632
|
+
externalUrl,
|
|
633
|
+
sourceLabel: metaString(metadata, "source_label")
|
|
634
|
+
};
|
|
635
|
+
return external;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// src/files/blocks/image/adapters/from-cx-media-part.ts
|
|
639
|
+
function fromCxMediaPart(part) {
|
|
640
|
+
const metadata = part.metadata ?? null;
|
|
641
|
+
const cdnFromMeta = typeof metadata?.cdn_url === "string" ? metadata.cdn_url : null;
|
|
642
|
+
const downloadFromMeta = typeof metadata?.download_url === "string" ? metadata.download_url : null;
|
|
643
|
+
const fileId = (typeof part.file_id === "string" ? part.file_id : null) ?? (typeof metadata?.file_id === "string" ? metadata.file_id : null);
|
|
644
|
+
const fallbackUrl = part.url ?? cdnFromMeta ?? "";
|
|
645
|
+
const block = fromImageOutputData(
|
|
646
|
+
{
|
|
647
|
+
type: "image_output",
|
|
648
|
+
url: fallbackUrl,
|
|
649
|
+
mime_type: part.mime_type ?? "image/*",
|
|
650
|
+
file_id: fileId,
|
|
651
|
+
cdn_url: cdnFromMeta,
|
|
652
|
+
download_url: downloadFromMeta
|
|
653
|
+
},
|
|
654
|
+
{
|
|
655
|
+
// Pass through all metadata so the extension fields (visibility,
|
|
656
|
+
// thumbnail, dimensions, etc.) get lifted by the rich adapter.
|
|
657
|
+
// Stored parts from before the storage-location eradication may
|
|
658
|
+
...metadata ?? {}
|
|
659
|
+
}
|
|
660
|
+
);
|
|
661
|
+
const withRole = {
|
|
662
|
+
...block,
|
|
663
|
+
referenceRole: part.role ?? null,
|
|
664
|
+
referenceName: part.name ?? null
|
|
665
|
+
};
|
|
666
|
+
if (typeof metadata?.base64_data === "string") {
|
|
667
|
+
return { ...withRole, base64: metadata.base64_data };
|
|
668
|
+
}
|
|
669
|
+
return withRole;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
// src/files/blocks/image/adapters/from-partial-image-data.ts
|
|
673
|
+
function fromPartialImageData(data, carriedMetadata) {
|
|
674
|
+
return {
|
|
675
|
+
kind: "image",
|
|
676
|
+
origin: "external",
|
|
677
|
+
base64: data.b64_json,
|
|
678
|
+
mimeType: data.mime_type ?? "image/png",
|
|
679
|
+
fileName: null,
|
|
680
|
+
width: null,
|
|
681
|
+
height: null,
|
|
682
|
+
sizeBytes: null,
|
|
683
|
+
visionClass: null,
|
|
684
|
+
status: "streaming",
|
|
685
|
+
progress: data.progress ?? null,
|
|
686
|
+
errorMessage: null,
|
|
687
|
+
metadata: carriedMetadata ?? null,
|
|
688
|
+
externalUrl: "",
|
|
689
|
+
sourceLabel: "Generating"
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
// src/files/blocks/image/adapters/from-render-block.ts
|
|
694
|
+
function fromRenderBlock(block) {
|
|
695
|
+
const data = block.data ?? {};
|
|
696
|
+
const src = typeof data.src === "string" ? data.src : "";
|
|
697
|
+
const mimeType = typeof data.mime_type === "string" ? data.mime_type : "image/*";
|
|
698
|
+
return fromImageOutputData(
|
|
699
|
+
{
|
|
700
|
+
type: "image_output",
|
|
701
|
+
url: src,
|
|
702
|
+
mime_type: mimeType
|
|
703
|
+
},
|
|
704
|
+
block.metadata ?? null
|
|
705
|
+
);
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
// src/files/blocks/image/adapters/to-cx-media-part.ts
|
|
709
|
+
function toCxMediaPart(block) {
|
|
710
|
+
const visibleUrl = block.origin === "matrx" ? block.cdnUrl ?? void 0 : block.externalUrl || void 0;
|
|
711
|
+
const callerMetadata = block.metadata ?? {};
|
|
712
|
+
const packed = {
|
|
713
|
+
...callerMetadata,
|
|
714
|
+
kind: block.kind,
|
|
715
|
+
origin: block.origin,
|
|
716
|
+
mime_type: block.mimeType,
|
|
717
|
+
file_name: block.fileName,
|
|
718
|
+
width: block.width,
|
|
719
|
+
height: block.height,
|
|
720
|
+
size_bytes: block.sizeBytes,
|
|
721
|
+
vision_class: block.visionClass,
|
|
722
|
+
status: block.status,
|
|
723
|
+
progress: block.progress,
|
|
724
|
+
error_message: block.errorMessage
|
|
725
|
+
};
|
|
726
|
+
delete packed.signed_url;
|
|
727
|
+
delete packed.signed_url_expires_at;
|
|
728
|
+
if (block.origin === "matrx") {
|
|
729
|
+
packed.cdn_url = block.cdnUrl;
|
|
730
|
+
packed.download_url = block.downloadUrl;
|
|
731
|
+
packed.file_id = block.fileId;
|
|
732
|
+
packed.visibility = block.visibility;
|
|
733
|
+
packed.parent_file_id = block.parentFileId;
|
|
734
|
+
packed.derivation_kind = block.derivationKind;
|
|
735
|
+
} else {
|
|
736
|
+
packed.external_url = block.externalUrl;
|
|
737
|
+
packed.source_label = block.sourceLabel;
|
|
738
|
+
}
|
|
739
|
+
if (block.base64) {
|
|
740
|
+
packed.base64_data = block.base64;
|
|
741
|
+
}
|
|
742
|
+
const common = {
|
|
743
|
+
type: "media",
|
|
744
|
+
kind: "image",
|
|
745
|
+
...block.mimeType !== null ? { mime_type: block.mimeType } : {},
|
|
746
|
+
...block.sizeBytes !== null ? { size_bytes: block.sizeBytes } : {},
|
|
747
|
+
...block.width !== null ? { width: block.width } : {},
|
|
748
|
+
...block.height !== null ? { height: block.height } : {},
|
|
749
|
+
metadata: packed
|
|
750
|
+
};
|
|
751
|
+
if (block.origin === "matrx") {
|
|
752
|
+
return {
|
|
753
|
+
...common,
|
|
754
|
+
origin: "matrx",
|
|
755
|
+
file_id: block.fileId,
|
|
756
|
+
...visibleUrl ? { url: visibleUrl } : {}
|
|
757
|
+
};
|
|
758
|
+
}
|
|
759
|
+
const externalUrl = visibleUrl ?? (block.base64 ? `data:${block.mimeType ?? "image/*"};base64,${block.base64}` : null);
|
|
760
|
+
if (!externalUrl) {
|
|
761
|
+
throw new TypeError(
|
|
762
|
+
"Cannot project external image into chat.message.content: missing URL and base64 locator"
|
|
763
|
+
);
|
|
764
|
+
}
|
|
765
|
+
return {
|
|
766
|
+
...common,
|
|
767
|
+
origin: "external",
|
|
768
|
+
url: externalUrl
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
// src/files/blocks/image/helpers/derive-viewer-url.ts
|
|
773
|
+
function deriveViewerUrl(block) {
|
|
774
|
+
if (block.origin === "external") return null;
|
|
775
|
+
return `/files/f/${block.fileId}`;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// src/react/cn.ts
|
|
779
|
+
var import_tailwind_merge = require("tailwind-merge");
|
|
780
|
+
function cn(...inputs) {
|
|
781
|
+
return (0, import_tailwind_merge.twMerge)(inputs.filter(Boolean).join(" "));
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
// src/files/media/video-date.ts
|
|
785
|
+
var COMPACT_VIDEO_DATE = new Intl.DateTimeFormat("en-US", {
|
|
786
|
+
month: "2-digit",
|
|
787
|
+
day: "2-digit",
|
|
788
|
+
year: "2-digit",
|
|
789
|
+
timeZone: "UTC"
|
|
790
|
+
});
|
|
791
|
+
var FULL_VIDEO_DATE = new Intl.DateTimeFormat("en-US", {
|
|
792
|
+
month: "long",
|
|
793
|
+
day: "numeric",
|
|
794
|
+
year: "numeric",
|
|
795
|
+
timeZone: "UTC"
|
|
796
|
+
});
|
|
797
|
+
function parseVideoDate(value) {
|
|
798
|
+
if (!value) return null;
|
|
799
|
+
const date = new Date(value);
|
|
800
|
+
return Number.isNaN(date.getTime()) ? null : date;
|
|
801
|
+
}
|
|
802
|
+
function formatVideoPublishDate(value) {
|
|
803
|
+
const date = parseVideoDate(value);
|
|
804
|
+
return date ? COMPACT_VIDEO_DATE.format(date) : "No Date";
|
|
805
|
+
}
|
|
806
|
+
function formatVideoPublishDateTitle(value) {
|
|
807
|
+
const date = parseVideoDate(value);
|
|
808
|
+
return date ? `Published ${FULL_VIDEO_DATE.format(date)}` : "Publish date unavailable";
|
|
809
|
+
}
|
|
810
|
+
var VIDEO_DATE_KEYS = [
|
|
811
|
+
"published_at",
|
|
812
|
+
"publishedAt",
|
|
813
|
+
"publish_date",
|
|
814
|
+
"publishDate",
|
|
815
|
+
"uploadDate",
|
|
816
|
+
"datePublished"
|
|
817
|
+
];
|
|
818
|
+
var VIDEO_METADATA_CONTAINERS = [
|
|
819
|
+
"video_metadata",
|
|
820
|
+
"schema_org",
|
|
821
|
+
"metadata"
|
|
822
|
+
];
|
|
823
|
+
function videoPublishDateFromMetadata(value) {
|
|
824
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
825
|
+
const record = value;
|
|
826
|
+
for (const key of VIDEO_DATE_KEYS) {
|
|
827
|
+
const candidate = record[key];
|
|
828
|
+
if (typeof candidate === "string" && parseVideoDate(candidate)) {
|
|
829
|
+
return candidate;
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
for (const key of VIDEO_METADATA_CONTAINERS) {
|
|
833
|
+
const nested = videoPublishDateFromMetadata(record[key]);
|
|
834
|
+
if (nested) return nested;
|
|
835
|
+
}
|
|
836
|
+
return null;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
// src/files/blocks/video/VideoPublishDate.tsx
|
|
840
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
841
|
+
function VideoPublishDate({
|
|
842
|
+
publishedAt,
|
|
843
|
+
className
|
|
844
|
+
}) {
|
|
845
|
+
const dateTime = publishedAt && !Number.isNaN(new Date(publishedAt).getTime()) ? publishedAt : void 0;
|
|
846
|
+
const content = formatVideoPublishDate(publishedAt);
|
|
847
|
+
const sharedProps = {
|
|
848
|
+
className: cn(
|
|
849
|
+
"shrink-0 whitespace-nowrap text-[10px] tabular-nums text-muted-foreground",
|
|
850
|
+
className
|
|
851
|
+
),
|
|
852
|
+
title: formatVideoPublishDateTitle(publishedAt),
|
|
853
|
+
"aria-label": formatVideoPublishDateTitle(publishedAt)
|
|
854
|
+
};
|
|
855
|
+
return dateTime ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("time", { ...sharedProps, dateTime, children: content }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { ...sharedProps, children: content });
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
// src/files/handler/errors.ts
|
|
859
|
+
var FileHandlerError = class extends Error {
|
|
860
|
+
code;
|
|
861
|
+
fileId;
|
|
862
|
+
cause;
|
|
863
|
+
constructor(code, message, opts) {
|
|
864
|
+
super(message);
|
|
865
|
+
this.name = "FileHandlerError";
|
|
866
|
+
this.code = code;
|
|
867
|
+
this.fileId = opts?.fileId;
|
|
868
|
+
this.cause = opts?.cause;
|
|
869
|
+
}
|
|
870
|
+
};
|
|
871
|
+
var FileAccessDeniedError = class extends FileHandlerError {
|
|
872
|
+
constructor(message = "This file isn't available to you.", opts) {
|
|
873
|
+
super("access_denied", message, opts);
|
|
874
|
+
this.name = "FileAccessDeniedError";
|
|
875
|
+
}
|
|
876
|
+
};
|
|
877
|
+
var FileNotFoundError = class extends FileHandlerError {
|
|
878
|
+
// A 404 from the file server. It proves the server could not retrieve the
|
|
879
|
+
// file — NOT that the file was deleted (that is `FileDeletedError`, which
|
|
880
|
+
// has a `deleted_at` to point at) and not that it never existed.
|
|
881
|
+
constructor(message = "We couldn't retrieve this file.", opts) {
|
|
882
|
+
super("not_found", message, opts);
|
|
883
|
+
this.name = "FileNotFoundError";
|
|
884
|
+
}
|
|
885
|
+
};
|
|
886
|
+
var FileDeletedError = class extends FileHandlerError {
|
|
887
|
+
constructor(message = "File is in trash", opts) {
|
|
888
|
+
super("deleted", message, opts);
|
|
889
|
+
this.name = "FileDeletedError";
|
|
890
|
+
}
|
|
891
|
+
};
|
|
892
|
+
var ShareLinkInvalidError = class extends FileHandlerError {
|
|
893
|
+
constructor(message = "Share link is invalid, expired, or revoked") {
|
|
894
|
+
super("share_link_invalid", message);
|
|
895
|
+
this.name = "ShareLinkInvalidError";
|
|
896
|
+
}
|
|
897
|
+
};
|
|
898
|
+
var ExternalFetchError = class extends FileHandlerError {
|
|
899
|
+
constructor(message = "Failed to fetch external URL", opts) {
|
|
900
|
+
super("external_fetch_failed", message, opts);
|
|
901
|
+
this.name = "ExternalFetchError";
|
|
902
|
+
}
|
|
903
|
+
};
|
|
904
|
+
var FileUploadError = class extends FileHandlerError {
|
|
905
|
+
constructor(message, opts) {
|
|
906
|
+
super("upload_failed", message, opts);
|
|
907
|
+
this.name = "FileUploadError";
|
|
908
|
+
}
|
|
909
|
+
};
|
|
910
|
+
var UploadCancelledError = class extends FileHandlerError {
|
|
911
|
+
constructor(message = "Upload cancelled \u2014 no workspace was selected.") {
|
|
912
|
+
super("upload_cancelled", message);
|
|
913
|
+
this.name = "UploadCancelledError";
|
|
914
|
+
}
|
|
915
|
+
};
|
|
916
|
+
function isUploadCancelledError(error) {
|
|
917
|
+
return error instanceof Error && error.name === "UploadCancelledError";
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
// src/files/utils/folder-conventions.ts
|
|
921
|
+
var CloudFolders = {
|
|
922
|
+
// ── Visible / user-managed ────────────────────────────────────────────
|
|
923
|
+
/** Top-level "Images" drawer. */
|
|
924
|
+
IMAGES: "Images",
|
|
925
|
+
/** Images pasted/attached inside a chat. */
|
|
926
|
+
IMAGES_CHAT: "Images/Chat",
|
|
927
|
+
/** Screenshots dragged or pasted into the app. */
|
|
928
|
+
IMAGES_SCREENSHOTS: "Images/Screenshots",
|
|
929
|
+
/** User avatars / profile images. */
|
|
930
|
+
IMAGES_AVATARS: "Images/Avatars",
|
|
931
|
+
/** Images generated by AI tools. */
|
|
932
|
+
IMAGES_GENERATED: "Images/Generated",
|
|
933
|
+
/** Screenshots and images flattened with user-authored markup. */
|
|
934
|
+
IMAGES_ANNOTATED: "Images/Annotated",
|
|
935
|
+
/** Top-level "Audio" drawer. */
|
|
936
|
+
AUDIO: "Audio",
|
|
937
|
+
/** User-initiated audio recordings (voice-to-text, voice pad, etc.). */
|
|
938
|
+
AUDIO_RECORDINGS: "Audio/Recordings",
|
|
939
|
+
/** Podcast asset uploads. */
|
|
940
|
+
AUDIO_PODCASTS: "Audio/Podcasts",
|
|
941
|
+
/** Top-level "Captures" drawer — everything the in-browser media-capture
|
|
942
|
+
* system saves (features/media-capture). User-produced, so visible. */
|
|
943
|
+
CAPTURES: "Captures",
|
|
944
|
+
/** Photos taken with the in-app camera (Capture Studio / device fallback). */
|
|
945
|
+
CAPTURES_PHOTOS: "Captures/Photos",
|
|
946
|
+
/** Videos recorded with the in-app camera (media-capture Phase 7). */
|
|
947
|
+
CAPTURES_VIDEOS: "Captures/Videos",
|
|
948
|
+
/** Audio recorded through the Capture Studio (media-capture Phase 7). */
|
|
949
|
+
CAPTURES_AUDIO: "Captures/Audio",
|
|
950
|
+
/** Product-capture staging (features/product-capture) — per-item folders of
|
|
951
|
+
* photos/videos/voice notes shot ahead of listing creation. Org-namespaced
|
|
952
|
+
* via `folderForProductCaptureItem` (same server one-path-one-org rule as
|
|
953
|
+
* Captures). User-produced, so visible. */
|
|
954
|
+
PRODUCT_CAPTURES: "Product Captures",
|
|
955
|
+
/** Commerce intake (W4 capture app): per-asset media filed under
|
|
956
|
+
* `Commerce Intake/<orgId>/<batchId>/<asset-leaf>` via
|
|
957
|
+
* `folderForIntakeAsset` (same server one-path-one-org rule as Captures).
|
|
958
|
+
* User-produced, so visible. */
|
|
959
|
+
COMMERCE_INTAKE: "Commerce Intake",
|
|
960
|
+
/**
|
|
961
|
+
* Static app assets (sounds, hero images, model-card thumbnails, demo
|
|
962
|
+
* data) owned by an admin service account and rendered globally.
|
|
963
|
+
* Always public — CDN URL is the goal. Source of truth for the
|
|
964
|
+
* static-asset migration (see scripts/migrate-public-assets-to-cdn.ts).
|
|
965
|
+
*/
|
|
966
|
+
APP_ASSETS: "App Assets",
|
|
967
|
+
/** Top-level documents (PDF, DOCX, etc.). */
|
|
968
|
+
DOCUMENTS: "Documents",
|
|
969
|
+
/** PDF-specific drawer. */
|
|
970
|
+
DOCUMENTS_PDFS: "Documents/PDFs",
|
|
971
|
+
/** Top-level code files (scripts, snippets). */
|
|
972
|
+
CODE: "Code",
|
|
973
|
+
/** Code saved via the code-editor window. */
|
|
974
|
+
CODE_EDITOR: "Code/Editor",
|
|
975
|
+
/** Generic AI-generated outputs. */
|
|
976
|
+
GENERATED: "Generated",
|
|
977
|
+
/** Files attached to agent apps, keyed by app id at runtime. */
|
|
978
|
+
AGENT_APPS: "Agent Apps",
|
|
979
|
+
/** Files attached to tasks, keyed by task id at runtime. */
|
|
980
|
+
TASK_ATTACHMENTS: "Task Attachments",
|
|
981
|
+
/** Files attached to War Room tiles, keyed by tile id at runtime. */
|
|
982
|
+
WAR_ROOM: "War Room",
|
|
983
|
+
/** Files dropped into a chat composer (pre-send or history). */
|
|
984
|
+
CHAT_ATTACHMENTS: "Chat Attachments",
|
|
985
|
+
/** Slack-imported files. */
|
|
986
|
+
SLACK_IMPORTS: "Slack Imports",
|
|
987
|
+
/** Web-scrape captures (HTML snapshots, exported markdown). */
|
|
988
|
+
SCRAPED_CONTENT: "Scraped Content",
|
|
989
|
+
// ── Shared assets / org-scoped public surfaces ─────────────────────────
|
|
990
|
+
/**
|
|
991
|
+
* Org-scoped shared assets — logos, hero images, anything an
|
|
992
|
+
* organization renders on a public surface. Keyed by org id at
|
|
993
|
+
* runtime (e.g. `Shared Assets/orgs/<org-id>`). Always public —
|
|
994
|
+
* the CDN URL is the rendering target.
|
|
995
|
+
*/
|
|
996
|
+
SHARED_ASSETS_ORGS: "Shared Assets/orgs",
|
|
997
|
+
/** Public screenshot attachments submitted through the feedback window. */
|
|
998
|
+
FEEDBACK_IMAGES: "Shared Assets/feedback-images",
|
|
999
|
+
/**
|
|
1000
|
+
* OG / social-preview covers for HTML pages the user publishes.
|
|
1001
|
+
* Lives under Shared Assets so all org/public-surface previews
|
|
1002
|
+
* share one CDN-backed bucket.
|
|
1003
|
+
*/
|
|
1004
|
+
HTML_PAGES_OG: "Shared Assets/html-pages",
|
|
1005
|
+
// ── App-asset subfolders ───────────────────────────────────────────────
|
|
1006
|
+
/**
|
|
1007
|
+
* Favicons rendered by prompt-app / agent-app standalone pages.
|
|
1008
|
+
* Always public — favicon requests are anonymous.
|
|
1009
|
+
*/
|
|
1010
|
+
PROMPT_APPS_FAVICONS: "App Assets/prompt-apps/favicons",
|
|
1011
|
+
/**
|
|
1012
|
+
* Per-block assets attached to agent blocks (icons, illustrations,
|
|
1013
|
+
* inline media). Keyed by block id at runtime via
|
|
1014
|
+
* `folderForAgentBlock(blockId)`.
|
|
1015
|
+
*/
|
|
1016
|
+
AGENT_BLOCKS: "Agent Apps/blocks",
|
|
1017
|
+
// ── Canvas / image-editor ──────────────────────────────────────────────
|
|
1018
|
+
/**
|
|
1019
|
+
* Cover images rendered on canvas social/share surfaces. Public so
|
|
1020
|
+
* the share-card OG image is a stable CDN URL.
|
|
1021
|
+
*/
|
|
1022
|
+
CANVAS_COVERS: "Images/Canvas/Covers",
|
|
1023
|
+
/**
|
|
1024
|
+
* Source images the user is editing in the image studio. Private —
|
|
1025
|
+
* these are the user's working files, NOT public render targets.
|
|
1026
|
+
* Outputs that the user explicitly publishes land elsewhere.
|
|
1027
|
+
*/
|
|
1028
|
+
IMAGES_EDITED_SOURCES: "Images/Edited/Sources",
|
|
1029
|
+
// ── Hidden / ephemeral ─────────────────────────────────────────────────
|
|
1030
|
+
// All hidden roots live under ".matrx-tmp/" so the tree has a single
|
|
1031
|
+
// ignorable folder rather than many. Callers that use these MUST delete
|
|
1032
|
+
// the file when done.
|
|
1033
|
+
/** Root for all ephemeral cloud-files work. Hidden in the tree. */
|
|
1034
|
+
TMP: ".matrx-tmp",
|
|
1035
|
+
/** Short-lived audio files staged for URL-based transcription. */
|
|
1036
|
+
TMP_TRANSCRIPTS: ".matrx-tmp/transcripts",
|
|
1037
|
+
/** Upload-in-progress staging (future use). */
|
|
1038
|
+
TMP_UPLOADS: ".matrx-tmp/uploads",
|
|
1039
|
+
// ── System / infrastructure ────────────────────────────────────────────
|
|
1040
|
+
// Owned and populated by the Python backend (not the user). These rows
|
|
1041
|
+
// exist in `cld_files` / `cld_folders` so they have asset IDs, but they
|
|
1042
|
+
// must NEVER appear in the user-facing tree.
|
|
1043
|
+
//
|
|
1044
|
+
// The Phase 1b/1c "SOCIAL_BASELINE" backfill (2026-05-16) created
|
|
1045
|
+
// `cld_files` rows under `system-files/variants/<file_id>/<og|thumb|tiny|page1_url>.jpg`
|
|
1046
|
+
// — three to four rows per source file. The cascade was cleaned up
|
|
1047
|
+
// server-side on 2026-05-16 (migrations 012/013); the tree RPCs now
|
|
1048
|
+
// exclude `parent_file_id IS NOT NULL` and `file_path LIKE
|
|
1049
|
+
// 'system-files/%'`. The FE keeps `isSystemPath` as a defensive guard
|
|
1050
|
+
// at every tree boundary (loadUserFileTree, loadFolderContents,
|
|
1051
|
+
// realtime middleware). See common-docs systems/files/file-service/WIRE_CONTRACT.md.
|
|
1052
|
+
/** Root for backend-owned infrastructure files (variants, posters, etc.). */
|
|
1053
|
+
SYSTEM_FILES: "system-files",
|
|
1054
|
+
/** SOCIAL_BASELINE variant storage: og.jpg / thumb.jpg / tiny.jpg / page1_url.jpg. */
|
|
1055
|
+
SYSTEM_VARIANTS: "system-files/variants",
|
|
1056
|
+
/**
|
|
1057
|
+
* Backend "generations" registry root — every AI generation (image,
|
|
1058
|
+
* video, audio/TTS) the server materializes lands under
|
|
1059
|
+
* `generations/<images|video|audio>/...`. Backend-owned; mirrors the
|
|
1060
|
+
* server's `is_system_path()` (which treats `system-files` AND
|
|
1061
|
+
* `generations` as system). Never shown in the user tree.
|
|
1062
|
+
*/
|
|
1063
|
+
GENERATIONS: "generations",
|
|
1064
|
+
/**
|
|
1065
|
+
* Recorded audio captured by the Transcripts / voice features. As of
|
|
1066
|
+
* 2026-06-14 the backend relocates every `origin: "transcripts"` upload
|
|
1067
|
+
* here, UNDER the hidden `system-files/` root — one
|
|
1068
|
+
* `recording_<iso>_<rand>.webm` per capture (80+ for an active user).
|
|
1069
|
+
* Because it's under `system-files/`, `isSystemPath` already hides it
|
|
1070
|
+
* from the tree, folder views, AND Recents — no separate predicate
|
|
1071
|
+
* needed. Managed exclusively via the Transcripts UI (by `cld_files.id`).
|
|
1072
|
+
* See docs/files/transcript-recordings-system-relocation.md.
|
|
1073
|
+
*/
|
|
1074
|
+
TRANSCRIPT_RECORDINGS: "system-files/transcripts/Recordings",
|
|
1075
|
+
/**
|
|
1076
|
+
* LEGACY user-namespace location for transcript recordings, before the
|
|
1077
|
+
* 2026-06-14 backend relocation to `system-files/transcripts/...`. Kept
|
|
1078
|
+
* ONLY as a defensive Recents guard: the backend relocates every
|
|
1079
|
+
* `origin: "transcripts"` upload, but the presigned/TUS (chunked + large
|
|
1080
|
+
* file) relocation path was genuinely missing and is committed but NOT yet
|
|
1081
|
+
* in prod as of 2026-06-15.
|
|
1082
|
+
*
|
|
1083
|
+
* DO NOT DROP THIS GUARD until that backend fix has DEPLOYED to prod —
|
|
1084
|
+
* dropping it earlier would let a chunked/large recording slip through
|
|
1085
|
+
* unhidden. Sequence: backend deploys → then remove this constant from
|
|
1086
|
+
* `isSystemManagedContentPath` (and delete it). See the FE-response /
|
|
1087
|
+
* "open items" in docs/files/transcript-recordings-system-relocation.md.
|
|
1088
|
+
*/
|
|
1089
|
+
TRANSCRIPT_RECORDINGS_LEGACY: "Transcripts/Recordings",
|
|
1090
|
+
// ── System-managed user content (browsable, but NOT in Recents) ─────────
|
|
1091
|
+
// Lives under USER roots (so it stays visible in the Files browser), but
|
|
1092
|
+
// is machine-named, high-volume, and background-produced — so it must
|
|
1093
|
+
// never pollute the Recents stream (your "entire reality" on open).
|
|
1094
|
+
/**
|
|
1095
|
+
* Per-tool generated image variants — `tool-images/<id>/v/<name>.jpg`,
|
|
1096
|
+
* multiple sizes per source. Machine-produced output, excluded from
|
|
1097
|
+
* Recents (see `isSystemManagedContentPath`).
|
|
1098
|
+
*/
|
|
1099
|
+
TOOL_IMAGES: "tool-images",
|
|
1100
|
+
/**
|
|
1101
|
+
* FastFire voice-drill recordings — full-session + per-card response clips.
|
|
1102
|
+
* Live under the hidden `system-files/fastfire/...` root (same class as
|
|
1103
|
+
* transcript recordings). Managed via Education / FastFire UI only.
|
|
1104
|
+
*/
|
|
1105
|
+
SYSTEM_FASTFIRE: "system-files/fastfire",
|
|
1106
|
+
SYSTEM_FASTFIRE_SESSIONS: "system-files/fastfire/sessions",
|
|
1107
|
+
SYSTEM_FASTFIRE_RESPONSES: "system-files/fastfire/responses",
|
|
1108
|
+
/**
|
|
1109
|
+
* Spoken Practice (oral exam / interview / debate) recordings — full-session
|
|
1110
|
+
* clips + per-prompt spoken-answer clips. Same hidden `system-files/` class as
|
|
1111
|
+
* FastFire (so `isSystemPath` keeps them out of the Files browser + Recents).
|
|
1112
|
+
*/
|
|
1113
|
+
SYSTEM_SPOKEN_PRACTICE: "system-files/spoken-practice",
|
|
1114
|
+
SYSTEM_SPOKEN_PRACTICE_SESSIONS: "system-files/spoken-practice/sessions",
|
|
1115
|
+
SYSTEM_SPOKEN_PRACTICE_RESPONSES: "system-files/spoken-practice/responses",
|
|
1116
|
+
/**
|
|
1117
|
+
* Handwritten / image-answer grading — photos of worked problems a learner
|
|
1118
|
+
* submits to the vision grader (assessment written-item photo answers +
|
|
1119
|
+
* the standalone "Grade my handwritten work" surface). Same hidden
|
|
1120
|
+
* `system-files/` class (kept out of the Files browser + Recents).
|
|
1121
|
+
*/
|
|
1122
|
+
SYSTEM_IMAGE_GRADE: "system-files/image-grade",
|
|
1123
|
+
SYSTEM_IMAGE_GRADE_RESPONSES: "system-files/image-grade/responses",
|
|
1124
|
+
/**
|
|
1125
|
+
* LEGACY user-namespace FastFire paths (pre-2026-07 relocation). Kept as a
|
|
1126
|
+
* defensive tree + Recents guard until every row is backfilled under
|
|
1127
|
+
* `system-files/fastfire/`.
|
|
1128
|
+
*/
|
|
1129
|
+
FASTFIRE_SESSIONS: "FastFire/sessions",
|
|
1130
|
+
FASTFIRE_RESPONSES: "FastFire/responses"
|
|
1131
|
+
};
|
|
1132
|
+
function folderForTask(taskId) {
|
|
1133
|
+
return `${CloudFolders.TASK_ATTACHMENTS}/${taskId}`;
|
|
1134
|
+
}
|
|
1135
|
+
function folderForWarRoomThread(threadId) {
|
|
1136
|
+
return `${CloudFolders.WAR_ROOM}/${threadId}`;
|
|
1137
|
+
}
|
|
1138
|
+
function folderForConversation(conversationId) {
|
|
1139
|
+
return `${CloudFolders.CHAT_ATTACHMENTS}/${conversationId}`;
|
|
1140
|
+
}
|
|
1141
|
+
function folderForAgentApp(appId) {
|
|
1142
|
+
return `${CloudFolders.AGENT_APPS}/${appId}`;
|
|
1143
|
+
}
|
|
1144
|
+
function folderForPodcast(podcastId) {
|
|
1145
|
+
return `${CloudFolders.AUDIO_PODCASTS}/${podcastId}`;
|
|
1146
|
+
}
|
|
1147
|
+
function folderForOrg(orgId) {
|
|
1148
|
+
return `${CloudFolders.SHARED_ASSETS_ORGS}/${orgId}`;
|
|
1149
|
+
}
|
|
1150
|
+
function folderForAgentBlock(blockId) {
|
|
1151
|
+
return `${CloudFolders.AGENT_BLOCKS}/${blockId}`;
|
|
1152
|
+
}
|
|
1153
|
+
function folderForCaptures(kind, orgId) {
|
|
1154
|
+
const leaf = kind === "photo" ? "Photos" : kind === "video" ? "Videos" : "Audio";
|
|
1155
|
+
const trimmed = typeof orgId === "string" ? orgId.trim() : "";
|
|
1156
|
+
if (trimmed) {
|
|
1157
|
+
return `${CloudFolders.CAPTURES}/${trimmed}/${leaf}`;
|
|
1158
|
+
}
|
|
1159
|
+
return kind === "photo" ? CloudFolders.CAPTURES_PHOTOS : kind === "video" ? CloudFolders.CAPTURES_VIDEOS : CloudFolders.CAPTURES_AUDIO;
|
|
1160
|
+
}
|
|
1161
|
+
function folderForProductCaptureItem(orgId, leaf) {
|
|
1162
|
+
const safe = leaf.replace(/[^A-Za-z0-9._@-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
1163
|
+
return `${CloudFolders.PRODUCT_CAPTURES}/${orgId.trim()}/${safe}`;
|
|
1164
|
+
}
|
|
1165
|
+
function folderForIntakeAsset(orgId, batchId, leaf) {
|
|
1166
|
+
const safe = leaf.replace(/[^A-Za-z0-9._@-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
1167
|
+
return `${CloudFolders.COMMERCE_INTAKE}/${orgId.trim()}/${batchId.trim()}/${safe || batchId.trim()}`;
|
|
1168
|
+
}
|
|
1169
|
+
function isHiddenFolder(folderPath) {
|
|
1170
|
+
return folderPath === CloudFolders.TMP || folderPath.startsWith(".matrx-tmp/");
|
|
1171
|
+
}
|
|
1172
|
+
function isSystemPath(path) {
|
|
1173
|
+
if (!path) return false;
|
|
1174
|
+
return path === CloudFolders.SYSTEM_FILES || path.startsWith(`${CloudFolders.SYSTEM_FILES}/`) || path === CloudFolders.GENERATIONS || path.startsWith(`${CloudFolders.GENERATIONS}/`) || path === CloudFolders.TMP || path.startsWith(`${CloudFolders.TMP}/`);
|
|
1175
|
+
}
|
|
1176
|
+
function isConventionalFolder(folderPath) {
|
|
1177
|
+
const all = Object.values(CloudFolders);
|
|
1178
|
+
return all.includes(folderPath);
|
|
1179
|
+
}
|
|
1180
|
+
var CloudFolderDescriptions = {
|
|
1181
|
+
[CloudFolders.IMAGES]: "All your images, organized.",
|
|
1182
|
+
[CloudFolders.IMAGES_CHAT]: "Images shared in chat.",
|
|
1183
|
+
[CloudFolders.IMAGES_SCREENSHOTS]: "Screenshots you pasted or dragged in.",
|
|
1184
|
+
[CloudFolders.IMAGES_AVATARS]: "Profile pictures and avatars.",
|
|
1185
|
+
[CloudFolders.IMAGES_GENERATED]: "AI-generated images.",
|
|
1186
|
+
[CloudFolders.AUDIO]: "Audio files.",
|
|
1187
|
+
[CloudFolders.AUDIO_RECORDINGS]: "Voice recordings.",
|
|
1188
|
+
[CloudFolders.AUDIO_PODCASTS]: "Podcast assets.",
|
|
1189
|
+
[CloudFolders.CAPTURES]: "Photos, videos, and audio captured in the app.",
|
|
1190
|
+
[CloudFolders.CAPTURES_PHOTOS]: "Photos taken with the in-app camera.",
|
|
1191
|
+
[CloudFolders.CAPTURES_VIDEOS]: "Videos recorded with the in-app camera.",
|
|
1192
|
+
[CloudFolders.CAPTURES_AUDIO]: "Audio recorded in the Capture Studio.",
|
|
1193
|
+
[CloudFolders.APP_ASSETS]: "Static app assets shared across the platform.",
|
|
1194
|
+
[CloudFolders.DOCUMENTS]: "Documents and reports.",
|
|
1195
|
+
[CloudFolders.DOCUMENTS_PDFS]: "PDFs.",
|
|
1196
|
+
[CloudFolders.CODE]: "Code files.",
|
|
1197
|
+
[CloudFolders.GENERATED]: "Files produced by AI tools.",
|
|
1198
|
+
[CloudFolders.AGENT_APPS]: "Assets attached to your agent apps.",
|
|
1199
|
+
[CloudFolders.TASK_ATTACHMENTS]: "Files attached to tasks.",
|
|
1200
|
+
[CloudFolders.CHAT_ATTACHMENTS]: "Files shared in conversations.",
|
|
1201
|
+
[CloudFolders.SLACK_IMPORTS]: "Files imported from Slack.",
|
|
1202
|
+
[CloudFolders.SCRAPED_CONTENT]: "Captures from the web scraper.",
|
|
1203
|
+
[CloudFolders.SHARED_ASSETS_ORGS]: "Organization-scoped shared assets.",
|
|
1204
|
+
[CloudFolders.HTML_PAGES_OG]: "OG covers for HTML pages.",
|
|
1205
|
+
[CloudFolders.PROMPT_APPS_FAVICONS]: "Favicons for prompt-app pages.",
|
|
1206
|
+
[CloudFolders.AGENT_BLOCKS]: "Per-block agent app assets.",
|
|
1207
|
+
[CloudFolders.CANVAS_COVERS]: "Canvas social cover images.",
|
|
1208
|
+
[CloudFolders.IMAGES_EDITED_SOURCES]: "Source images for the image studio.",
|
|
1209
|
+
[CloudFolders.TMP]: "Internal temporary workspace (hidden)."
|
|
1210
|
+
};
|
|
1211
|
+
var DEFAULT_VISIBILITY_RULES = [
|
|
1212
|
+
// Most-specific rules first — longest-prefix wins.
|
|
1213
|
+
{ prefix: "Shared Assets/feedback-images", visibility: "public" },
|
|
1214
|
+
{ prefix: CloudFolders.SHARED_ASSETS_ORGS, visibility: "public" },
|
|
1215
|
+
{ prefix: CloudFolders.HTML_PAGES_OG, visibility: "public" },
|
|
1216
|
+
{ prefix: CloudFolders.PROMPT_APPS_FAVICONS, visibility: "public" },
|
|
1217
|
+
{ prefix: CloudFolders.AGENT_BLOCKS, visibility: "public" },
|
|
1218
|
+
{ prefix: CloudFolders.CANVAS_COVERS, visibility: "public" },
|
|
1219
|
+
{ prefix: CloudFolders.APP_ASSETS, visibility: "public" },
|
|
1220
|
+
{ prefix: CloudFolders.IMAGES_AVATARS, visibility: "public" },
|
|
1221
|
+
{ prefix: CloudFolders.AUDIO_PODCASTS, visibility: "public" },
|
|
1222
|
+
{ prefix: CloudFolders.AGENT_APPS, visibility: "public" },
|
|
1223
|
+
{ prefix: "Shared Assets", visibility: "public" },
|
|
1224
|
+
// IMAGES_EDITED_SOURCES is private — these are the user's working
|
|
1225
|
+
// source files for the image studio, not public render targets.
|
|
1226
|
+
// Captures are the user's own camera/mic output — never public render
|
|
1227
|
+
// targets. Explicit rule (not just the fallback) so the intent is recorded.
|
|
1228
|
+
{ prefix: CloudFolders.CAPTURES, visibility: "personal" },
|
|
1229
|
+
{ prefix: CloudFolders.IMAGES_EDITED_SOURCES, visibility: "personal" },
|
|
1230
|
+
{ prefix: CloudFolders.IMAGES_GENERATED, visibility: "personal" },
|
|
1231
|
+
{ prefix: CloudFolders.CHAT_ATTACHMENTS, visibility: "personal" },
|
|
1232
|
+
{ prefix: CloudFolders.TASK_ATTACHMENTS, visibility: "personal" },
|
|
1233
|
+
{ prefix: CloudFolders.WAR_ROOM, visibility: "personal" },
|
|
1234
|
+
{ prefix: CloudFolders.SLACK_IMPORTS, visibility: "personal" },
|
|
1235
|
+
{ prefix: CloudFolders.SCRAPED_CONTENT, visibility: "personal" },
|
|
1236
|
+
{ prefix: "Private Assets", visibility: "personal" },
|
|
1237
|
+
{ prefix: CloudFolders.TMP, visibility: "personal" }
|
|
1238
|
+
];
|
|
1239
|
+
function resolveDefaultVisibility(folderPath) {
|
|
1240
|
+
const normalized = folderPath.replace(/^\/+|\/+$/g, "");
|
|
1241
|
+
let best = null;
|
|
1242
|
+
for (const rule of DEFAULT_VISIBILITY_RULES) {
|
|
1243
|
+
if (normalized === rule.prefix || normalized.startsWith(`${rule.prefix}/`)) {
|
|
1244
|
+
if (!best || rule.prefix.length > best.prefix.length) {
|
|
1245
|
+
best = rule;
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
return best?.visibility ?? "personal";
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
// src/files/handler/utils/upload-folder-path.ts
|
|
1253
|
+
var TOP_LEVEL_FOLDERS = {
|
|
1254
|
+
userContent: "My Files",
|
|
1255
|
+
images: CloudFolders.IMAGES,
|
|
1256
|
+
Images: CloudFolders.IMAGES,
|
|
1257
|
+
audio: CloudFolders.AUDIO,
|
|
1258
|
+
Audio: CloudFolders.AUDIO,
|
|
1259
|
+
"audio-recordings": CloudFolders.AUDIO_RECORDINGS,
|
|
1260
|
+
documents: CloudFolders.DOCUMENTS,
|
|
1261
|
+
Documents: CloudFolders.DOCUMENTS,
|
|
1262
|
+
code: CloudFolders.CODE,
|
|
1263
|
+
Code: CloudFolders.CODE,
|
|
1264
|
+
"any-file": "Uploads",
|
|
1265
|
+
attachments: CloudFolders.CHAT_ATTACHMENTS
|
|
1266
|
+
};
|
|
1267
|
+
function composeUploadFolderPath(root = "Uploads", path) {
|
|
1268
|
+
const top = (TOP_LEVEL_FOLDERS[root] ?? root).replace(/^\/+|\/+$/g, "");
|
|
1269
|
+
const sub = (path ?? "").replace(/^\/+|\/+$/g, "");
|
|
1270
|
+
return sub ? `${top}/${sub}` : top;
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
// src/files/media/object-url-registry.ts
|
|
1274
|
+
var MAX_LIVE = 256;
|
|
1275
|
+
var live = /* @__PURE__ */ new Map();
|
|
1276
|
+
function createTrackedObjectUrl(blob) {
|
|
1277
|
+
const url = URL.createObjectURL(blob);
|
|
1278
|
+
live.set(url, true);
|
|
1279
|
+
while (live.size > MAX_LIVE) {
|
|
1280
|
+
const oldest = live.keys().next().value;
|
|
1281
|
+
if (!oldest) break;
|
|
1282
|
+
live.delete(oldest);
|
|
1283
|
+
try {
|
|
1284
|
+
URL.revokeObjectURL(oldest);
|
|
1285
|
+
} catch {
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
return url;
|
|
1289
|
+
}
|
|
1290
|
+
function revokeTrackedObjectUrl(url) {
|
|
1291
|
+
if (!url || !live.has(url)) return;
|
|
1292
|
+
live.delete(url);
|
|
1293
|
+
try {
|
|
1294
|
+
URL.revokeObjectURL(url);
|
|
1295
|
+
} catch {
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
//# sourceMappingURL=files.cjs.map
|