@ai-matrx/media 0.7.16 → 0.9.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 +25 -0
- package/dist/files/engine/api/assets.d.ts +223 -0
- package/dist/files/engine/api/assets.js +124 -0
- package/dist/files/engine/api/assets.js.map +1 -0
- package/dist/files/engine/api/direct.d.ts +43 -0
- package/dist/files/engine/api/direct.js +73 -0
- package/dist/files/engine/api/direct.js.map +1 -0
- package/dist/files/engine/api/fileOrganization.d.ts +34 -0
- package/dist/files/engine/api/fileOrganization.js +38 -0
- package/dist/files/engine/api/fileOrganization.js.map +1 -0
- package/dist/files/engine/api/files.d.ts +249 -0
- package/dist/files/engine/api/files.js +231 -0
- package/dist/files/engine/api/files.js.map +1 -0
- package/dist/files/engine/api/folders.d.ts +63 -0
- package/dist/files/engine/api/folders.js +56 -0
- package/dist/files/engine/api/folders.js.map +1 -0
- package/dist/files/engine/api/office.d.ts +27 -0
- package/dist/files/engine/api/office.js +19 -0
- package/dist/files/engine/api/office.js.map +1 -0
- package/dist/files/engine/api/permissions.d.ts +37 -0
- package/dist/files/engine/api/permissions.js +109 -0
- package/dist/files/engine/api/permissions.js.map +1 -0
- package/dist/files/engine/api/versions.d.ts +26 -0
- package/dist/files/engine/api/versions.js +37 -0
- package/dist/files/engine/api/versions.js.map +1 -0
- package/dist/files/engine/cache/idb-store.d.ts +95 -0
- package/dist/files/engine/cache/idb-store.js +172 -0
- package/dist/files/engine/cache/idb-store.js.map +1 -0
- package/dist/files/engine/cache/policy.d.ts +14 -0
- package/dist/files/engine/cache/policy.js +40 -0
- package/dist/files/engine/cache/policy.js.map +1 -0
- package/dist/files/engine/cache/register-service-worker.d.ts +57 -0
- package/dist/files/engine/cache/register-service-worker.js +80 -0
- package/dist/files/engine/cache/register-service-worker.js.map +1 -0
- package/dist/files/engine/db-types.d.ts +48865 -0
- package/dist/files/engine/db-types.js +1 -0
- package/dist/files/engine/db-types.js.map +1 -0
- package/dist/files/engine/filesDb.d.ts +1913 -0
- package/dist/files/engine/filesDb.js +28 -0
- package/dist/files/engine/filesDb.js.map +1 -0
- package/dist/files/engine/handler/handler.d.ts +82 -0
- package/dist/files/engine/handler/handler.js +124 -0
- package/dist/files/engine/handler/handler.js.map +1 -0
- package/dist/files/engine/handler/hooks/useFile.d.ts +17 -0
- package/dist/files/engine/handler/hooks/useFile.js +75 -0
- package/dist/files/engine/handler/hooks/useFile.js.map +1 -0
- package/dist/files/engine/handler/hooks/useFileUpload.d.ts +76 -0
- package/dist/files/engine/handler/hooks/useFileUpload.js +67 -0
- package/dist/files/engine/handler/hooks/useFileUpload.js.map +1 -0
- package/dist/files/engine/handler/input/normalize.d.ts +14 -0
- package/dist/files/engine/handler/input/normalize.js +365 -0
- package/dist/files/engine/handler/input/normalize.js.map +1 -0
- package/dist/files/engine/handler/intelligence/access.d.ts +35 -0
- package/dist/files/engine/handler/intelligence/access.js +85 -0
- package/dist/files/engine/handler/intelligence/access.js.map +1 -0
- package/dist/files/engine/handler/intelligence/magic-bytes.d.ts +21 -0
- package/dist/files/engine/handler/intelligence/magic-bytes.js +67 -0
- package/dist/files/engine/handler/intelligence/magic-bytes.js.map +1 -0
- package/dist/files/engine/handler/output/target.d.ts +19 -0
- package/dist/files/engine/handler/output/target.js +222 -0
- package/dist/files/engine/handler/output/target.js.map +1 -0
- package/dist/files/engine/handler/resolver.d.ts +34 -0
- package/dist/files/engine/handler/resolver.js +133 -0
- package/dist/files/engine/handler/resolver.js.map +1 -0
- package/dist/files/engine/handler/types.d.ts +367 -0
- package/dist/files/engine/handler/types.js +1 -0
- package/dist/files/engine/handler/types.js.map +1 -0
- package/dist/files/engine/handler/upload.d.ts +32 -0
- package/dist/files/engine/handler/upload.js +295 -0
- package/dist/files/engine/handler/upload.js.map +1 -0
- package/dist/files/engine/handler/utils/classify.d.ts +14 -0
- package/dist/files/engine/handler/utils/classify.js +27 -0
- package/dist/files/engine/handler/utils/classify.js.map +1 -0
- package/dist/files/engine/handler/utils/prefer-locator.d.ts +32 -0
- package/dist/files/engine/handler/utils/prefer-locator.js +34 -0
- package/dist/files/engine/handler/utils/prefer-locator.js.map +1 -0
- package/dist/files/engine/handler/utils/python-base.d.ts +129 -0
- package/dist/files/engine/handler/utils/python-base.js +60 -0
- package/dist/files/engine/handler/utils/python-base.js.map +1 -0
- package/dist/files/engine/hooks/blob-cache.d.ts +130 -0
- package/dist/files/engine/hooks/blob-cache.js +162 -0
- package/dist/files/engine/hooks/blob-cache.js.map +1 -0
- package/dist/files/engine/hooks/office-extraction-cache.d.ts +28 -0
- package/dist/files/engine/hooks/office-extraction-cache.js +67 -0
- package/dist/files/engine/hooks/office-extraction-cache.js.map +1 -0
- package/dist/files/engine/host/configure.d.ts +166 -0
- package/dist/files/engine/host/configure.js +30 -0
- package/dist/files/engine/host/configure.js.map +1 -0
- package/dist/files/engine/host/org.d.ts +8 -0
- package/dist/files/engine/host/org.js +16 -0
- package/dist/files/engine/host/org.js.map +1 -0
- package/dist/files/engine/host/python-client.d.ts +60 -0
- package/dist/files/engine/host/python-client.js +46 -0
- package/dist/files/engine/host/python-client.js.map +1 -0
- package/dist/files/engine/host/share-links.d.ts +20 -0
- package/dist/files/engine/host/share-links.js +32 -0
- package/dist/files/engine/host/share-links.js.map +1 -0
- package/dist/files/engine/host/store.d.ts +24 -0
- package/dist/files/engine/host/store.js +23 -0
- package/dist/files/engine/host/store.js.map +1 -0
- package/dist/files/engine/host/supabase.d.ts +3808 -0
- package/dist/files/engine/host/supabase.js +33 -0
- package/dist/files/engine/host/supabase.js.map +1 -0
- package/dist/files/engine/host/toast.d.ts +14 -0
- package/dist/files/engine/host/toast.js +20 -0
- package/dist/files/engine/host/toast.js.map +1 -0
- package/dist/files/engine/host/typed-client.d.ts +136 -0
- package/dist/files/engine/host/typed-client.js +66 -0
- package/dist/files/engine/host/typed-client.js.map +1 -0
- package/dist/files/engine/index.d.ts +22 -0
- package/dist/files/engine/index.js +49 -0
- package/dist/files/engine/index.js.map +1 -0
- package/dist/files/engine/media/our-file-sources.d.ts +39 -0
- package/dist/files/engine/media/our-file-sources.js +83 -0
- package/dist/files/engine/media/our-file-sources.js.map +1 -0
- package/dist/files/engine/media/signed-url.d.ts +1 -0
- package/dist/files/engine/media/signed-url.js +6 -0
- package/dist/files/engine/media/signed-url.js.map +1 -0
- package/dist/files/engine/redux/converters.d.ts +73 -0
- package/dist/files/engine/redux/converters.js +286 -0
- package/dist/files/engine/redux/converters.js.map +1 -0
- package/dist/files/engine/redux/file-hydration.d.ts +15 -0
- package/dist/files/engine/redux/file-hydration.js +46 -0
- package/dist/files/engine/redux/file-hydration.js.map +1 -0
- package/dist/files/engine/redux/file-tree-auth-boundary.d.ts +5 -0
- package/dist/files/engine/redux/file-tree-auth-boundary.js +28 -0
- package/dist/files/engine/redux/file-tree-auth-boundary.js.map +1 -0
- package/dist/files/engine/redux/file-tree-timeout.d.ts +32 -0
- package/dist/files/engine/redux/file-tree-timeout.js +73 -0
- package/dist/files/engine/redux/file-tree-timeout.js.map +1 -0
- package/dist/files/engine/redux/mutation-toast-middleware.d.ts +27 -0
- package/dist/files/engine/redux/mutation-toast-middleware.js +103 -0
- package/dist/files/engine/redux/mutation-toast-middleware.js.map +1 -0
- package/dist/files/engine/redux/realtime-middleware.d.ts +61 -0
- package/dist/files/engine/redux/realtime-middleware.js +431 -0
- package/dist/files/engine/redux/realtime-middleware.js.map +1 -0
- package/dist/files/engine/redux/request-ledger.d.ts +68 -0
- package/dist/files/engine/redux/request-ledger.js +66 -0
- package/dist/files/engine/redux/request-ledger.js.map +1 -0
- package/dist/files/engine/redux/selectors.d.ts +3254 -0
- package/dist/files/engine/redux/selectors.js +428 -0
- package/dist/files/engine/redux/selectors.js.map +1 -0
- package/dist/files/engine/redux/slice.d.ts +157 -0
- package/dist/files/engine/redux/slice.js +836 -0
- package/dist/files/engine/redux/slice.js.map +1 -0
- package/dist/files/engine/redux/thunks.d.ts +449 -0
- package/dist/files/engine/redux/thunks.js +1684 -0
- package/dist/files/engine/redux/thunks.js.map +1 -0
- package/dist/files/engine/redux/tree-utils.d.ts +90 -0
- package/dist/files/engine/redux/tree-utils.js +200 -0
- package/dist/files/engine/redux/tree-utils.js.map +1 -0
- package/dist/files/engine/support/claimsUser.d.ts +77 -0
- package/dist/files/engine/support/claimsUser.js +48 -0
- package/dist/files/engine/support/claimsUser.js.map +1 -0
- package/dist/files/engine/support/datetime.d.ts +6 -0
- package/dist/files/engine/support/datetime.js +15 -0
- package/dist/files/engine/support/datetime.js.map +1 -0
- package/dist/files/engine/support/document-visibility.d.ts +14 -0
- package/dist/files/engine/support/document-visibility.js +28 -0
- package/dist/files/engine/support/document-visibility.js.map +1 -0
- package/dist/files/engine/support/logger.d.ts +28 -0
- package/dist/files/engine/support/logger.js +36 -0
- package/dist/files/engine/support/logger.js.map +1 -0
- package/dist/files/engine/types.d.ts +1081 -0
- package/dist/files/engine/types.js +30 -0
- package/dist/files/engine/types.js.map +1 -0
- package/dist/files/engine/upload/cloudUpload.d.ts +182 -0
- package/dist/files/engine/upload/cloudUpload.js +418 -0
- package/dist/files/engine/upload/cloudUpload.js.map +1 -0
- package/dist/files/engine/upload/tusUpload.d.ts +100 -0
- package/dist/files/engine/upload/tusUpload.js +258 -0
- package/dist/files/engine/upload/tusUpload.js.map +1 -0
- package/dist/files/engine/upload/uploadDedupGuard.d.ts +58 -0
- package/dist/files/engine/upload/uploadDedupGuard.js +41 -0
- package/dist/files/engine/upload/uploadDedupGuard.js.map +1 -0
- package/dist/files/engine/upload/uploadGuardOpeners.d.ts +70 -0
- package/dist/files/engine/upload/uploadGuardOpeners.js +54 -0
- package/dist/files/engine/upload/uploadGuardOpeners.js.map +1 -0
- package/dist/files/engine/utils/file-types.d.ts +243 -0
- package/dist/files/engine/utils/file-types.js +1722 -0
- package/dist/files/engine/utils/file-types.js.map +1 -0
- package/dist/files/engine/utils/user-visible.d.ts +133 -0
- package/dist/files/engine/utils/user-visible.js +115 -0
- package/dist/files/engine/utils/user-visible.js.map +1 -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 +404 -9
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* features/files/handler/types.ts
|
|
3
|
+
*
|
|
4
|
+
* The universal file handler. ONE shape goes in (FileSource), ONE shape
|
|
5
|
+
* comes out (FileTarget). Between them, every file in the app — owned
|
|
6
|
+
* cld_files row, blob from a paste, base64 string, external URL — funnels
|
|
7
|
+
* through the same NormalizedFile representation so the core never
|
|
8
|
+
* branches on origin.
|
|
9
|
+
*
|
|
10
|
+
* Every callsite that touches a file MUST go through this feature. Direct
|
|
11
|
+
* construction of ImageBlock/AudioBlock/VideoBlock/DocumentBlock literals
|
|
12
|
+
* and direct object-store SDK calls are banned by ESLint (see .eslintrc).
|
|
13
|
+
*/
|
|
14
|
+
import type { Asset, AssetPreset, CloudFile, FileUploadResponse, MediaRef, PermissionLevel, Visibility } from "../types.js";
|
|
15
|
+
import type { CustomVariantSpec } from "../api/assets.js";
|
|
16
|
+
import type { FileCategory, PreviewKind, ThumbnailStrategy } from "../utils/file-types.js";
|
|
17
|
+
import type { AudioBlock, DocumentBlock, ImageBlock, VideoBlock, YouTubeVideoBlock } from "@ai-matrx/agents/message-parts";
|
|
18
|
+
import type { MessagePart } from "@ai-matrx/agents/generated/stream-events";
|
|
19
|
+
/**
|
|
20
|
+
* Discriminated union of every file shape that can enter the handler.
|
|
21
|
+
*
|
|
22
|
+
* Resolution priority enforced by the resolver: owned identifiers
|
|
23
|
+
* (`cloud_file`, `file_id`) beat opaque URLs (`share_link`, `public_cdn`)
|
|
24
|
+
* beat raw bytes (`blob`, `file`, `data_uri`, ...). Two locators on the
|
|
25
|
+
* same source are tolerated; the resolver picks the best.
|
|
26
|
+
*/
|
|
27
|
+
export type FileSource = BlobSource | FileObjectSource | BufferSource_ | StreamSource | DataUriSource | Base64Source | ExternalUrlSource | YouTubeSource | CloudFileSource | FileIdSource | ShareLinkSource | PublicCdnSource | UploadResultSource | StreamEventSource;
|
|
28
|
+
export interface BlobSource {
|
|
29
|
+
kind: "blob";
|
|
30
|
+
blob: Blob;
|
|
31
|
+
fileName?: string;
|
|
32
|
+
mime?: string;
|
|
33
|
+
}
|
|
34
|
+
export interface FileObjectSource {
|
|
35
|
+
kind: "file";
|
|
36
|
+
file: File;
|
|
37
|
+
}
|
|
38
|
+
export interface BufferSource_ {
|
|
39
|
+
kind: "buffer";
|
|
40
|
+
buffer: ArrayBuffer | Uint8Array;
|
|
41
|
+
mime: string;
|
|
42
|
+
fileName?: string;
|
|
43
|
+
}
|
|
44
|
+
export interface StreamSource {
|
|
45
|
+
kind: "stream";
|
|
46
|
+
stream: ReadableStream<Uint8Array>;
|
|
47
|
+
mime: string;
|
|
48
|
+
fileName?: string;
|
|
49
|
+
}
|
|
50
|
+
export interface DataUriSource {
|
|
51
|
+
kind: "data_uri";
|
|
52
|
+
dataUri: string;
|
|
53
|
+
}
|
|
54
|
+
export interface Base64Source {
|
|
55
|
+
kind: "base64";
|
|
56
|
+
base64: string;
|
|
57
|
+
mime: string;
|
|
58
|
+
fileName?: string;
|
|
59
|
+
}
|
|
60
|
+
export interface ExternalUrlSource {
|
|
61
|
+
kind: "external_url";
|
|
62
|
+
url: string;
|
|
63
|
+
mime?: string;
|
|
64
|
+
}
|
|
65
|
+
export interface YouTubeSource {
|
|
66
|
+
kind: "youtube";
|
|
67
|
+
url: string;
|
|
68
|
+
}
|
|
69
|
+
export interface CloudFileSource {
|
|
70
|
+
kind: "cloud_file";
|
|
71
|
+
cloudFile: CloudFile;
|
|
72
|
+
}
|
|
73
|
+
export interface FileIdSource {
|
|
74
|
+
kind: "file_id";
|
|
75
|
+
fileId: string;
|
|
76
|
+
mime?: string;
|
|
77
|
+
}
|
|
78
|
+
export interface ShareLinkSource {
|
|
79
|
+
kind: "share_link";
|
|
80
|
+
token: string;
|
|
81
|
+
mime?: string;
|
|
82
|
+
}
|
|
83
|
+
export interface PublicCdnSource {
|
|
84
|
+
kind: "public_cdn";
|
|
85
|
+
url: string;
|
|
86
|
+
fileId?: string;
|
|
87
|
+
mime?: string;
|
|
88
|
+
}
|
|
89
|
+
/** Legacy-compat shape — `{ fileId, url, ... }` payload emitted by older upload pathways. */
|
|
90
|
+
export interface UploadResultSource {
|
|
91
|
+
kind: "upload_result";
|
|
92
|
+
uploadResult: {
|
|
93
|
+
fileId?: string;
|
|
94
|
+
url: string;
|
|
95
|
+
pageUrl?: string;
|
|
96
|
+
type?: string;
|
|
97
|
+
metadata?: Record<string, unknown>;
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Stream event payload. The handler unwraps RenderBlock/data/tool-output
|
|
102
|
+
* envelopes and produces a NormalizedFile. The shape mirrors the
|
|
103
|
+
* `TypedDataPayload` discriminator for image_output/audio_output/video_output
|
|
104
|
+
* plus the structured render-block payload for type=image/video/document.
|
|
105
|
+
*/
|
|
106
|
+
export interface StreamEventSource {
|
|
107
|
+
kind: "stream_event";
|
|
108
|
+
payload: unknown;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* The canonical internal shape. After normalization, every code path past
|
|
112
|
+
* the resolver sees only this. There is no second internal shape — adding
|
|
113
|
+
* one would defeat the entire purpose of the handler.
|
|
114
|
+
*/
|
|
115
|
+
export interface NormalizedFile {
|
|
116
|
+
/** cld_files UUID — set whenever known. */
|
|
117
|
+
fileId?: string;
|
|
118
|
+
/**
|
|
119
|
+
* User-facing logical path for a persisted cloud file. This is never an
|
|
120
|
+
* object-store key or transport identity; it is retained so canonical
|
|
121
|
+
* version writers can target the existing row after resolving by fileId.
|
|
122
|
+
*/
|
|
123
|
+
filePath?: string;
|
|
124
|
+
/** Any URL safe to hand to the browser. */
|
|
125
|
+
url?: string;
|
|
126
|
+
/** Inline base64. Last resort. Always include `meta.mime`. */
|
|
127
|
+
base64?: string;
|
|
128
|
+
/** Resolved share-link token, when the source was a share link. */
|
|
129
|
+
shareToken?: string;
|
|
130
|
+
/** YouTube videos are first-class — backend has dedicated handling. */
|
|
131
|
+
youtubeUrl?: string;
|
|
132
|
+
origin: FileOrigin;
|
|
133
|
+
capabilities: FileCapabilities;
|
|
134
|
+
meta: FileMeta;
|
|
135
|
+
lifecycle: FileLifecycle;
|
|
136
|
+
scope: FileScope;
|
|
137
|
+
derivedFrom?: {
|
|
138
|
+
fileId: string;
|
|
139
|
+
kind: string;
|
|
140
|
+
};
|
|
141
|
+
/**
|
|
142
|
+
* Full {@link Asset} envelope — populated when the file was uploaded
|
|
143
|
+
* through the `/assets` preset pipeline (or when callers explicitly
|
|
144
|
+
* resolve to one). Carries every rendered variant URL keyed by
|
|
145
|
+
* canonical variant key (e.g. `og_url`, `thumbnail_url`). Read this
|
|
146
|
+
* instead of making a second round-trip for variants.
|
|
147
|
+
*/
|
|
148
|
+
asset?: Asset;
|
|
149
|
+
/**
|
|
150
|
+
* Original input — kept so we can re-resolve, refresh, or diagnose.
|
|
151
|
+
* Never read this from a render path; use the normalized fields.
|
|
152
|
+
*/
|
|
153
|
+
__source: FileSource;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Result of a successful canonical upload. Unlike a general NormalizedFile,
|
|
157
|
+
* a newly-persisted upload must always carry its durable cld_files identity.
|
|
158
|
+
*/
|
|
159
|
+
export interface UploadedNormalizedFile extends NormalizedFile {
|
|
160
|
+
fileId: string;
|
|
161
|
+
}
|
|
162
|
+
export type FileOrigin = "owned" | "shared" | "public" | "external" | "ephemeral" | "anonymous_public";
|
|
163
|
+
export interface FileCapabilities {
|
|
164
|
+
canRead: boolean;
|
|
165
|
+
canEdit: boolean;
|
|
166
|
+
canShare: boolean;
|
|
167
|
+
canDelete: boolean;
|
|
168
|
+
/** True when the consumer needs to hold a JWT to open this URL. */
|
|
169
|
+
requiresAuth: boolean;
|
|
170
|
+
/**
|
|
171
|
+
* True when the URL can be `fetch()`ed from the browser — durable
|
|
172
|
+
* `/files/{id}/download` route URLs qualify (the python-client attaches
|
|
173
|
+
* Authorization headers). False for opaque third-party URLs that may be
|
|
174
|
+
* CORS-blocked; output adapters that need bytes route through the
|
|
175
|
+
* backend instead when this is false.
|
|
176
|
+
*/
|
|
177
|
+
transportSafeForFetch: boolean;
|
|
178
|
+
}
|
|
179
|
+
export interface FileMeta {
|
|
180
|
+
fileName?: string;
|
|
181
|
+
mime?: string;
|
|
182
|
+
sizeBytes?: number;
|
|
183
|
+
checksum?: string;
|
|
184
|
+
width?: number;
|
|
185
|
+
height?: number;
|
|
186
|
+
durationSec?: number;
|
|
187
|
+
pageCount?: number;
|
|
188
|
+
category: FileCategory;
|
|
189
|
+
previewKind: PreviewKind;
|
|
190
|
+
thumbnailStrategy: ThumbnailStrategy;
|
|
191
|
+
}
|
|
192
|
+
export interface FileLifecycle {
|
|
193
|
+
/** ms epoch — only set for time-bounded references. */
|
|
194
|
+
expiresAt?: number;
|
|
195
|
+
/** True if we have enough identity to re-mint a fresh URL. */
|
|
196
|
+
refreshable: boolean;
|
|
197
|
+
/** ms epoch of the last full re-validation. */
|
|
198
|
+
lastVerifiedAt?: number;
|
|
199
|
+
/** True once the file is durably persisted (vs in-flight upload). */
|
|
200
|
+
persisted: boolean;
|
|
201
|
+
}
|
|
202
|
+
export interface FileScope {
|
|
203
|
+
ownerId?: string;
|
|
204
|
+
organizationId?: string;
|
|
205
|
+
projectId?: string;
|
|
206
|
+
taskId?: string;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Discriminated union of every consumer surface. The output dispatcher picks
|
|
210
|
+
* the optimal locator on the NormalizedFile for each target — `<img src>`
|
|
211
|
+
* prefers a permanent CDN URL, `fetchable_url` prefers the same-origin proxy
|
|
212
|
+
* for CORS safety, AI media blocks prefer `file_id`.
|
|
213
|
+
*/
|
|
214
|
+
export type FileTarget = {
|
|
215
|
+
kind: "media_block";
|
|
216
|
+
} | {
|
|
217
|
+
kind: "media_ref";
|
|
218
|
+
} | {
|
|
219
|
+
kind: "html_src";
|
|
220
|
+
} | {
|
|
221
|
+
kind: "fetchable_url";
|
|
222
|
+
} | {
|
|
223
|
+
kind: "blob";
|
|
224
|
+
} | {
|
|
225
|
+
kind: "data_uri";
|
|
226
|
+
} | {
|
|
227
|
+
kind: "form_data_part";
|
|
228
|
+
field: string;
|
|
229
|
+
} | {
|
|
230
|
+
kind: "anchor_download";
|
|
231
|
+
suggestedName?: string;
|
|
232
|
+
} | {
|
|
233
|
+
kind: "og_image";
|
|
234
|
+
} | {
|
|
235
|
+
kind: "jsonb_content_part";
|
|
236
|
+
} | {
|
|
237
|
+
kind: "rag_ingest_source";
|
|
238
|
+
};
|
|
239
|
+
export type RenderedFor<T extends FileTarget> = T extends {
|
|
240
|
+
kind: "media_block";
|
|
241
|
+
} ? MediaBlock : T extends {
|
|
242
|
+
kind: "media_ref";
|
|
243
|
+
} ? MediaRef : T extends {
|
|
244
|
+
kind: "html_src";
|
|
245
|
+
} ? string : T extends {
|
|
246
|
+
kind: "fetchable_url";
|
|
247
|
+
} ? string : T extends {
|
|
248
|
+
kind: "blob";
|
|
249
|
+
} ? Blob : T extends {
|
|
250
|
+
kind: "data_uri";
|
|
251
|
+
} ? string : T extends {
|
|
252
|
+
kind: "form_data_part";
|
|
253
|
+
} ? {
|
|
254
|
+
value: Blob;
|
|
255
|
+
filename: string;
|
|
256
|
+
} : T extends {
|
|
257
|
+
kind: "anchor_download";
|
|
258
|
+
} ? {
|
|
259
|
+
url: string;
|
|
260
|
+
filename: string;
|
|
261
|
+
} : T extends {
|
|
262
|
+
kind: "og_image";
|
|
263
|
+
} ? string : T extends {
|
|
264
|
+
kind: "jsonb_content_part";
|
|
265
|
+
} ? MessagePart : T extends {
|
|
266
|
+
kind: "rag_ingest_source";
|
|
267
|
+
} ? RagIngestSource : never;
|
|
268
|
+
/** All four media block types plus YouTube — what AI APIs accept. */
|
|
269
|
+
export type MediaBlock = ImageBlock | AudioBlock | VideoBlock | DocumentBlock | YouTubeVideoBlock;
|
|
270
|
+
export interface RagIngestSource {
|
|
271
|
+
source_kind: "cld_file" | "external_url" | "inline";
|
|
272
|
+
source_id?: string;
|
|
273
|
+
url?: string;
|
|
274
|
+
inline?: {
|
|
275
|
+
mime: string;
|
|
276
|
+
bytes: Uint8Array;
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
export interface UploadOpts {
|
|
280
|
+
/**
|
|
281
|
+
* Logical folder path. Backend creates intermediate folders atomically.
|
|
282
|
+
* If omitted, defaults to a per-feature inbox like "Inbox/Pasted".
|
|
283
|
+
*/
|
|
284
|
+
folderPath?: string;
|
|
285
|
+
/**
|
|
286
|
+
* Full logical file path (folder + filename). Wins over `folderPath`
|
|
287
|
+
* when set. Use this to write a new version of an existing file by
|
|
288
|
+
* pointing at the same path the existing file occupies — the Python
|
|
289
|
+
* backend treats matching paths as version-increments on the existing
|
|
290
|
+
* cld_files row rather than creating a new sibling.
|
|
291
|
+
*/
|
|
292
|
+
filePath?: string;
|
|
293
|
+
/**
|
|
294
|
+
* One-line note attached to the new version row (`cld_file_versions.change_summary`).
|
|
295
|
+
* Surfaces in the version history UI.
|
|
296
|
+
*/
|
|
297
|
+
changeSummary?: string;
|
|
298
|
+
/** Visibility on the new cld_files row. Default "personal" (or "public" for preset uploads). */
|
|
299
|
+
visibility?: Visibility;
|
|
300
|
+
/** Initial sharing grants. */
|
|
301
|
+
shareWith?: string[];
|
|
302
|
+
shareLevel?: PermissionLevel;
|
|
303
|
+
/** Free-form metadata stored on the row. */
|
|
304
|
+
metadata?: Record<string, unknown>;
|
|
305
|
+
/**
|
|
306
|
+
* The organization that owns the uploaded bytes. When supplied, this wins
|
|
307
|
+
* over the ambient app context for every visibility and is written into
|
|
308
|
+
* `metadata.scope.organization_id` before the transport starts.
|
|
309
|
+
*/
|
|
310
|
+
organizationId?: string;
|
|
311
|
+
/** Override filename when source has no inherent name. */
|
|
312
|
+
fileName?: string;
|
|
313
|
+
/**
|
|
314
|
+
* When true, scope columns (organization_id/project_id/task_id) are read
|
|
315
|
+
* from `appContext` and stamped into `metadata.scope` so the row carries
|
|
316
|
+
* its scope context. Defaults to false for personal uploads and true for
|
|
317
|
+
* public/shared uploads.
|
|
318
|
+
*/
|
|
319
|
+
inheritActiveScope?: boolean;
|
|
320
|
+
/** Per-byte progress callback. */
|
|
321
|
+
onProgress?: (loaded: number, total: number) => void;
|
|
322
|
+
/** Abort the upload (buffered XHR abort / TUS abort). */
|
|
323
|
+
signal?: AbortSignal;
|
|
324
|
+
/**
|
|
325
|
+
* Transport override. Default policy: files ≥
|
|
326
|
+
* `TUS_TRANSPORT_THRESHOLD_BYTES` (features/files/upload/cloudUpload.ts)
|
|
327
|
+
* go resumable TUS; smaller files use the buffered multipart POST.
|
|
328
|
+
*/
|
|
329
|
+
transport?: "buffered" | "tus";
|
|
330
|
+
/**
|
|
331
|
+
* If true, the handler also creates a permanent share link after upload.
|
|
332
|
+
* The returned NormalizedFile carries `shareToken` + `url` pointing at
|
|
333
|
+
* Python's `{BACKEND}/share/{token}` endpoint (no Next.js hop, no
|
|
334
|
+
* expiry — embed in `<img src>`, persist to DB columns, etc.).
|
|
335
|
+
*/
|
|
336
|
+
createShareLink?: boolean;
|
|
337
|
+
/** Default "read". `admin` is not valid for share links. */
|
|
338
|
+
shareLinkPermissionLevel?: "viewer" | "editor";
|
|
339
|
+
shareLinkExpiresAt?: string | null;
|
|
340
|
+
shareLinkMaxUses?: number | null;
|
|
341
|
+
/**
|
|
342
|
+
* Override origin for the returned share URL. Defaults to
|
|
343
|
+
* `window.location.origin`. Rare — useful for SSR/worker contexts that
|
|
344
|
+
* synthesize their own origin.
|
|
345
|
+
*/
|
|
346
|
+
appOrigin?: string;
|
|
347
|
+
/**
|
|
348
|
+
* Asset-pipeline preset. When set, the handler routes through
|
|
349
|
+
* `POST /assets` (which renders preset variants server-side) instead of
|
|
350
|
+
* the plain `POST /files/upload` write. The returned `NormalizedFile`
|
|
351
|
+
* carries the full {@link Asset} envelope on `asset` so consumers can
|
|
352
|
+
* read every rendered variant URL without a second round-trip.
|
|
353
|
+
*
|
|
354
|
+
* Set this for image uploads that need server-rendered variant sets
|
|
355
|
+
* (podcast covers, OG cards, org logos, avatars, favicons). Omit for
|
|
356
|
+
* generic file uploads.
|
|
357
|
+
*/
|
|
358
|
+
preset?: AssetPreset;
|
|
359
|
+
/**
|
|
360
|
+
* Ad-hoc custom variant specs sent verbatim to the server in
|
|
361
|
+
* `custom_variants_json`. Use when the preset registry doesn't carry a
|
|
362
|
+
* key you need; otherwise prefer presets so downstream renderers
|
|
363
|
+
* recognize the resulting variant keys.
|
|
364
|
+
*/
|
|
365
|
+
customVariants?: CustomVariantSpec[];
|
|
366
|
+
}
|
|
367
|
+
export type { FileUploadResponse };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* features/files/handler/upload.ts
|
|
3
|
+
*
|
|
4
|
+
* The single write path. Coerces a `FileSource` into a `File`, then runs
|
|
5
|
+
* it through `cloudUpload` (which dispatches optimistic Redux updates,
|
|
6
|
+
* calls Python's `/files/upload`, and optionally creates a share link in
|
|
7
|
+
* the same round-trip). The returned `NormalizedFile` reflects the new
|
|
8
|
+
* `cld_files` row and carries the share-link URL when requested.
|
|
9
|
+
*
|
|
10
|
+
* Org-scope routing is visibility-aware: public/shared uploads inherit
|
|
11
|
+
* active organization / project / task ids by default; personal uploads
|
|
12
|
+
* remain independent of ambient app scope unless explicitly opted in.
|
|
13
|
+
*/
|
|
14
|
+
import type { FileSource, NormalizedFile, UploadedNormalizedFile, UploadOpts } from "./types.js";
|
|
15
|
+
/**
|
|
16
|
+
* Personal files belong to the individual, independent of whichever
|
|
17
|
+
* organization, project, or task happens to be active in the UI.
|
|
18
|
+
*/
|
|
19
|
+
export declare function shouldInheritActiveScope(visibility: NonNullable<UploadOpts["visibility"]>, override?: boolean, folderPath?: string): boolean;
|
|
20
|
+
export declare function uploadInternal(source: FileSource, opts: UploadOpts): Promise<UploadedNormalizedFile>;
|
|
21
|
+
/**
|
|
22
|
+
* Merge metadata from a post-upload hydration without letting that secondary
|
|
23
|
+
* read erase or replace the identity acknowledged by the creation response.
|
|
24
|
+
* This is shared by ordinary and asset uploads because both are the same
|
|
25
|
+
* two-step contract: create → hydrate.
|
|
26
|
+
*/
|
|
27
|
+
export declare function preserveUploadedIdentity(hydrated: NormalizedFile, authoritativeFileId: string, label?: string): UploadedNormalizedFile;
|
|
28
|
+
export declare function stampScope(metadata: Record<string, unknown>, inherit: boolean, organizationId?: string, activeScope?: {
|
|
29
|
+
organizationId?: string | null;
|
|
30
|
+
projectId?: string | null;
|
|
31
|
+
taskId?: string | null;
|
|
32
|
+
}): Record<string, unknown>;
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import { getStoreSingleton } from "../host/store.js";
|
|
2
|
+
import {
|
|
3
|
+
cloudUpload,
|
|
4
|
+
isCloudUploadFailure
|
|
5
|
+
} from "../upload/cloudUpload.js";
|
|
6
|
+
import * as Files from "../api/files.js";
|
|
7
|
+
import {
|
|
8
|
+
uploadAsset,
|
|
9
|
+
uploadAssetWithProgress
|
|
10
|
+
} from "../api/assets.js";
|
|
11
|
+
import { pythonShareUrl } from "./utils/python-base.js";
|
|
12
|
+
import { apiFileRecordToCloudFile } from "../redux/converters.js";
|
|
13
|
+
import {
|
|
14
|
+
selectOrganizationId,
|
|
15
|
+
selectProjectId,
|
|
16
|
+
selectTaskId
|
|
17
|
+
} from "../host/store.js";
|
|
18
|
+
import { FileUploadError, UploadCancelledError } from "../../../files.js";
|
|
19
|
+
import { fromCloudFile } from "./input/normalize.js";
|
|
20
|
+
import { setCached } from "../hooks/blob-cache.js";
|
|
21
|
+
const DEFAULT_FOLDER = "Inbox";
|
|
22
|
+
function shouldInheritActiveScope(visibility, override, folderPath) {
|
|
23
|
+
if (override !== void 0) return override;
|
|
24
|
+
const normalizedFolder = folderPath?.replace(/^\/+|\/+$/g, "") ?? "";
|
|
25
|
+
if (normalizedFolder === "Shared Assets" || normalizedFolder.startsWith("Shared Assets/") || normalizedFolder === "Private Assets" || normalizedFolder.startsWith("Private Assets/")) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
return visibility !== "personal";
|
|
29
|
+
}
|
|
30
|
+
async function uploadInternal(source, opts) {
|
|
31
|
+
const file = await sourceToFile(source, opts.fileName);
|
|
32
|
+
if (!file) {
|
|
33
|
+
throw new FileUploadError(
|
|
34
|
+
"Cannot upload: source did not produce raw bytes (use the input shape that already has a fileId instead)"
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
const store = getStoreSingleton();
|
|
38
|
+
if (!store) {
|
|
39
|
+
throw new FileUploadError("Cannot upload: redux store not yet initialized");
|
|
40
|
+
}
|
|
41
|
+
const dispatch = store.dispatch;
|
|
42
|
+
const folderPath = opts.folderPath ?? defaultFolderForSource(source);
|
|
43
|
+
const visibility = opts.visibility ?? (opts.preset ? "public" : "personal");
|
|
44
|
+
const metadata = stampScope(
|
|
45
|
+
opts.metadata ?? {},
|
|
46
|
+
shouldInheritActiveScope(visibility, opts.inheritActiveScope, folderPath),
|
|
47
|
+
opts.organizationId
|
|
48
|
+
);
|
|
49
|
+
if (opts.preset) {
|
|
50
|
+
const params = {
|
|
51
|
+
file,
|
|
52
|
+
preset: opts.preset,
|
|
53
|
+
folder: folderPath.replace(/^\/+|\/+$/g, ""),
|
|
54
|
+
visibility,
|
|
55
|
+
customVariants: opts.customVariants,
|
|
56
|
+
shareWith: opts.shareWith,
|
|
57
|
+
shareLevel: opts.shareLevel,
|
|
58
|
+
metadata
|
|
59
|
+
};
|
|
60
|
+
const { data: asset } = opts.onProgress ? await uploadAssetWithProgress(
|
|
61
|
+
params,
|
|
62
|
+
(event) => opts.onProgress(event.loaded, event.total)
|
|
63
|
+
) : await uploadAsset(params);
|
|
64
|
+
setCached(asset.file_id, file, URL.createObjectURL(file), {
|
|
65
|
+
mimeType: file.type
|
|
66
|
+
});
|
|
67
|
+
const { data: full2 } = await Files.getFile(asset.file_id);
|
|
68
|
+
const cloudFile2 = apiFileRecordToCloudFile(full2);
|
|
69
|
+
const normalized2 = preserveUploadedIdentity(
|
|
70
|
+
fromCloudFile(cloudFile2, source),
|
|
71
|
+
asset.file_id,
|
|
72
|
+
"asset upload"
|
|
73
|
+
);
|
|
74
|
+
return {
|
|
75
|
+
...normalized2,
|
|
76
|
+
asset,
|
|
77
|
+
url: asset.primary_url ?? normalized2.url
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
const result = await cloudUpload(
|
|
81
|
+
file,
|
|
82
|
+
{
|
|
83
|
+
// filePath wins when set — used by version-replace callers (e.g. the
|
|
84
|
+
// image editor "Save" path that overwrites the existing cloud file).
|
|
85
|
+
...opts.filePath ? { filePath: opts.filePath.replace(/^\/+/, "") } : { folderPath: folderPath.replace(/^\/+|\/+$/g, "") },
|
|
86
|
+
visibility,
|
|
87
|
+
shareWith: opts.shareWith,
|
|
88
|
+
shareLevel: opts.shareLevel,
|
|
89
|
+
changeSummary: opts.changeSummary,
|
|
90
|
+
metadata,
|
|
91
|
+
onProgress: opts.onProgress ? (event) => opts.onProgress(event.loaded, event.total) : void 0,
|
|
92
|
+
signal: opts.signal,
|
|
93
|
+
transport: opts.transport,
|
|
94
|
+
createShareLink: opts.createShareLink,
|
|
95
|
+
shareLinkPermissionLevel: opts.shareLinkPermissionLevel,
|
|
96
|
+
shareLinkExpiresAt: opts.shareLinkExpiresAt,
|
|
97
|
+
shareLinkMaxUses: opts.shareLinkMaxUses
|
|
98
|
+
},
|
|
99
|
+
dispatch
|
|
100
|
+
);
|
|
101
|
+
if (isCloudUploadFailure(result)) {
|
|
102
|
+
if (result.errorCode === "upload_cancelled") {
|
|
103
|
+
throw new UploadCancelledError(result.error);
|
|
104
|
+
}
|
|
105
|
+
throw new FileUploadError(result.error);
|
|
106
|
+
}
|
|
107
|
+
setCached(result.fileId, file, URL.createObjectURL(file), {
|
|
108
|
+
mimeType: file.type
|
|
109
|
+
});
|
|
110
|
+
const { data: full } = await Files.getFile(result.fileId);
|
|
111
|
+
const cloudFile = apiFileRecordToCloudFile(full);
|
|
112
|
+
const normalized = preserveUploadedIdentity(
|
|
113
|
+
fromCloudFile(cloudFile, source),
|
|
114
|
+
result.fileId
|
|
115
|
+
);
|
|
116
|
+
if (result.shareToken) {
|
|
117
|
+
const appShareUrl = opts.appOrigin ? `${opts.appOrigin.replace(/\/$/, "")}/s/${result.shareToken}` : void 0;
|
|
118
|
+
const publicUrl = cloudFile.visibility === "public" ? normalized.url : void 0;
|
|
119
|
+
const url = publicUrl || result.directUrl || appShareUrl || result.shareUrl || pythonShareUrl(result.shareToken);
|
|
120
|
+
return {
|
|
121
|
+
...normalized,
|
|
122
|
+
shareToken: result.shareToken,
|
|
123
|
+
url
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
return normalized;
|
|
127
|
+
}
|
|
128
|
+
function preserveUploadedIdentity(hydrated, authoritativeFileId, label = "upload") {
|
|
129
|
+
if (hydrated.fileId !== authoritativeFileId) {
|
|
130
|
+
console.error(
|
|
131
|
+
`[file-handler] hydrated ${label} lost or changed its durable identity (upload=${authoritativeFileId}, hydrated=${hydrated.fileId ?? "<missing>"}); preserving the upload acknowledgement.`
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
const uploaded = {
|
|
135
|
+
...hydrated,
|
|
136
|
+
fileId: authoritativeFileId
|
|
137
|
+
};
|
|
138
|
+
assertUploadedIdentity(uploaded);
|
|
139
|
+
return uploaded;
|
|
140
|
+
}
|
|
141
|
+
function assertUploadedIdentity(file) {
|
|
142
|
+
if (!file.fileId) {
|
|
143
|
+
throw new FileUploadError(
|
|
144
|
+
"Upload completed without a durable file identity; refusing to attach a temporary URL"
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
async function sourceToFile(source, overrideName) {
|
|
149
|
+
switch (source.kind) {
|
|
150
|
+
case "file":
|
|
151
|
+
return overrideName ? renameFile(source.file, overrideName) : source.file;
|
|
152
|
+
case "blob": {
|
|
153
|
+
const name = overrideName ?? source.fileName ?? guessFilename(source.blob.type);
|
|
154
|
+
return new File([source.blob], name, {
|
|
155
|
+
type: source.mime ?? source.blob.type ?? "application/octet-stream"
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
case "buffer": {
|
|
159
|
+
const blob = bufferToBlob(source.buffer, source.mime);
|
|
160
|
+
const name = overrideName ?? source.fileName ?? guessFilename(source.mime);
|
|
161
|
+
return new File([blob], name, { type: source.mime });
|
|
162
|
+
}
|
|
163
|
+
case "stream": {
|
|
164
|
+
const reader = source.stream.getReader();
|
|
165
|
+
const parts = [];
|
|
166
|
+
while (true) {
|
|
167
|
+
const { value, done } = await reader.read();
|
|
168
|
+
if (done) break;
|
|
169
|
+
if (value) {
|
|
170
|
+
const copy = new Uint8Array(value.byteLength);
|
|
171
|
+
copy.set(value);
|
|
172
|
+
parts.push(copy.buffer);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
const blob = new Blob(parts, { type: source.mime });
|
|
176
|
+
const name = overrideName ?? source.fileName ?? guessFilename(source.mime);
|
|
177
|
+
return new File([blob], name, { type: source.mime });
|
|
178
|
+
}
|
|
179
|
+
case "data_uri":
|
|
180
|
+
case "base64": {
|
|
181
|
+
const dataUri = source.kind === "data_uri" ? source.dataUri : `data:${source.mime};base64,${source.base64}`;
|
|
182
|
+
const blob = await dataUriToBlob(dataUri);
|
|
183
|
+
const fallbackName = "fileName" in source && source.fileName || guessFilename(blob.type);
|
|
184
|
+
const name = overrideName ?? fallbackName;
|
|
185
|
+
return new File([blob], name, { type: blob.type });
|
|
186
|
+
}
|
|
187
|
+
case "external_url": {
|
|
188
|
+
const res = await fetch(source.url);
|
|
189
|
+
if (!res.ok) {
|
|
190
|
+
throw new FileUploadError(
|
|
191
|
+
`Failed to fetch external URL for upload (${res.status})`
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
const blob = await res.blob();
|
|
195
|
+
const name = overrideName ?? filenameFromUrl(source.url) ?? guessFilename(blob.type);
|
|
196
|
+
return new File([blob], name, { type: source.mime ?? blob.type });
|
|
197
|
+
}
|
|
198
|
+
case "youtube":
|
|
199
|
+
throw new FileUploadError(
|
|
200
|
+
"YouTube URLs cannot be uploaded \u2014 pass them as a source directly"
|
|
201
|
+
);
|
|
202
|
+
default:
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
function bufferToBlob(buffer, mime) {
|
|
207
|
+
if (buffer instanceof Uint8Array) {
|
|
208
|
+
const copy2 = new Uint8Array(buffer.byteLength);
|
|
209
|
+
copy2.set(buffer);
|
|
210
|
+
return new Blob([copy2.buffer], { type: mime });
|
|
211
|
+
}
|
|
212
|
+
if (buffer instanceof ArrayBuffer) return new Blob([buffer], { type: mime });
|
|
213
|
+
const view = new Uint8Array(buffer);
|
|
214
|
+
const copy = new Uint8Array(view.byteLength);
|
|
215
|
+
copy.set(view);
|
|
216
|
+
return new Blob([copy.buffer], { type: mime });
|
|
217
|
+
}
|
|
218
|
+
function dataUriToBlob(dataUri) {
|
|
219
|
+
return fetch(dataUri).then((r) => r.blob());
|
|
220
|
+
}
|
|
221
|
+
function renameFile(file, name) {
|
|
222
|
+
return new File([file], name, {
|
|
223
|
+
type: file.type,
|
|
224
|
+
lastModified: file.lastModified
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
function filenameFromUrl(url) {
|
|
228
|
+
try {
|
|
229
|
+
const path = new URL(url).pathname;
|
|
230
|
+
const last = path.split("/").pop();
|
|
231
|
+
return last && last.length > 0 ? last : void 0;
|
|
232
|
+
} catch {
|
|
233
|
+
return void 0;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
function guessFilename(mime) {
|
|
237
|
+
if (!mime) return "upload.bin";
|
|
238
|
+
const ext = mime.split("/")[1]?.split(";")[0] ?? "bin";
|
|
239
|
+
return `upload-${Date.now()}.${ext}`;
|
|
240
|
+
}
|
|
241
|
+
function stampScope(metadata, inherit, organizationId, activeScope) {
|
|
242
|
+
if (!inherit && !organizationId) return metadata;
|
|
243
|
+
let resolvedActiveScope = activeScope;
|
|
244
|
+
if (inherit && !resolvedActiveScope) {
|
|
245
|
+
const store = getStoreSingleton();
|
|
246
|
+
if (store) {
|
|
247
|
+
const state = store.getState();
|
|
248
|
+
resolvedActiveScope = {
|
|
249
|
+
organizationId: selectOrganizationId(state),
|
|
250
|
+
projectId: selectProjectId(state),
|
|
251
|
+
taskId: selectTaskId(state)
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
const activeOrganizationId = resolvedActiveScope?.organizationId;
|
|
256
|
+
const projectId = resolvedActiveScope?.projectId;
|
|
257
|
+
const taskId = resolvedActiveScope?.taskId;
|
|
258
|
+
if (!organizationId && !activeOrganizationId && !projectId && !taskId) {
|
|
259
|
+
return metadata;
|
|
260
|
+
}
|
|
261
|
+
const existing = metadata.scope ?? {};
|
|
262
|
+
return {
|
|
263
|
+
...metadata,
|
|
264
|
+
scope: {
|
|
265
|
+
...existing,
|
|
266
|
+
...activeOrganizationId ? { organization_id: activeOrganizationId } : {},
|
|
267
|
+
...projectId ? { project_id: projectId } : {},
|
|
268
|
+
...taskId ? { task_id: taskId } : {},
|
|
269
|
+
// An explicit owner is a record-bound write. It must never be replaced
|
|
270
|
+
// by whatever organization happens to be active in the shell.
|
|
271
|
+
...organizationId ? { organization_id: organizationId } : {}
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
function defaultFolderForSource(source) {
|
|
276
|
+
switch (source.kind) {
|
|
277
|
+
case "file":
|
|
278
|
+
case "blob":
|
|
279
|
+
return DEFAULT_FOLDER;
|
|
280
|
+
case "data_uri":
|
|
281
|
+
case "base64":
|
|
282
|
+
return `${DEFAULT_FOLDER}/Pasted`;
|
|
283
|
+
case "external_url":
|
|
284
|
+
return `${DEFAULT_FOLDER}/Imported`;
|
|
285
|
+
default:
|
|
286
|
+
return DEFAULT_FOLDER;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
export {
|
|
290
|
+
preserveUploadedIdentity,
|
|
291
|
+
shouldInheritActiveScope,
|
|
292
|
+
stampScope,
|
|
293
|
+
uploadInternal
|
|
294
|
+
};
|
|
295
|
+
//# sourceMappingURL=upload.js.map
|