@statezero/core 0.1.51 → 0.1.52
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.
|
@@ -11,13 +11,7 @@ export class FileObject {
|
|
|
11
11
|
lastModified: number | null;
|
|
12
12
|
uploaded: boolean;
|
|
13
13
|
uploading: boolean;
|
|
14
|
-
uploadResult:
|
|
15
|
-
file_path: any;
|
|
16
|
-
file_name: any;
|
|
17
|
-
file_url: any;
|
|
18
|
-
size: any;
|
|
19
|
-
mime_type: any;
|
|
20
|
-
} | null;
|
|
14
|
+
uploadResult: any;
|
|
21
15
|
uploadError: any;
|
|
22
16
|
fileData: any;
|
|
23
17
|
uploadType: string | null;
|
|
@@ -31,7 +25,6 @@ export class FileObject {
|
|
|
31
25
|
get status(): "failed" | "uploading" | "uploaded" | "pending";
|
|
32
26
|
get filePath(): any;
|
|
33
27
|
get fileUrl(): any;
|
|
34
|
-
serialize(): any;
|
|
35
28
|
_initializeAndStartUpload(file: any, options: any): any;
|
|
36
29
|
/**
|
|
37
30
|
* Fast upload using S3 presigned URLs with multipart support
|
|
@@ -40,23 +33,11 @@ export class FileObject {
|
|
|
40
33
|
/**
|
|
41
34
|
* Handle single file upload
|
|
42
35
|
*/
|
|
43
|
-
_singleUpload(file: any, uploadData: any, options: any): Promise<
|
|
44
|
-
file_path: any;
|
|
45
|
-
file_name: any;
|
|
46
|
-
file_url: any;
|
|
47
|
-
size: any;
|
|
48
|
-
mime_type: any;
|
|
49
|
-
} | null>;
|
|
36
|
+
_singleUpload(file: any, uploadData: any, options: any): Promise<any>;
|
|
50
37
|
/**
|
|
51
38
|
* Handle multipart upload with concurrency using p-queue
|
|
52
39
|
*/
|
|
53
|
-
_multipartUpload(file: any, uploadData: any, options: any): Promise<
|
|
54
|
-
file_path: any;
|
|
55
|
-
file_name: any;
|
|
56
|
-
file_url: any;
|
|
57
|
-
size: any;
|
|
58
|
-
mime_type: any;
|
|
59
|
-
} | null>;
|
|
40
|
+
_multipartUpload(file: any, uploadData: any, options: any): Promise<any>;
|
|
60
41
|
/**
|
|
61
42
|
* Create file chunks for multipart upload
|
|
62
43
|
*/
|
|
@@ -64,13 +45,7 @@ export class FileObject {
|
|
|
64
45
|
/**
|
|
65
46
|
* Complete the upload (both single and multipart)
|
|
66
47
|
*/
|
|
67
|
-
_completeUpload(filePath: any, originalName: any, uploadId?: null, parts?: null): Promise<
|
|
68
|
-
file_path: any;
|
|
69
|
-
file_name: any;
|
|
70
|
-
file_url: any;
|
|
71
|
-
size: any;
|
|
72
|
-
mime_type: any;
|
|
73
|
-
} | null>;
|
|
48
|
+
_completeUpload(filePath: any, originalName: any, uploadId?: null, parts?: null): Promise<any>;
|
|
74
49
|
/**
|
|
75
50
|
* Direct upload to Django backend (original method)
|
|
76
51
|
*/
|
|
@@ -85,10 +60,18 @@ export class FileObject {
|
|
|
85
60
|
getBlob(): Blob;
|
|
86
61
|
waitForUpload(): Promise<any>;
|
|
87
62
|
toJSON(): {
|
|
63
|
+
name: any;
|
|
64
|
+
size: any;
|
|
65
|
+
type: any;
|
|
66
|
+
status: string;
|
|
67
|
+
uploaded: boolean;
|
|
88
68
|
filePath: any;
|
|
89
|
-
fileName: any;
|
|
90
69
|
fileUrl: any;
|
|
91
|
-
|
|
92
|
-
|
|
70
|
+
uploadResult: any;
|
|
71
|
+
uploadError: string | null;
|
|
72
|
+
uploadType: string | null;
|
|
73
|
+
uploadId: any;
|
|
74
|
+
totalChunks: number;
|
|
75
|
+
completedChunks: number;
|
|
93
76
|
};
|
|
94
77
|
}
|
|
@@ -10,24 +10,17 @@ export class FileObject {
|
|
|
10
10
|
// Handle stored file data (from API)
|
|
11
11
|
if (file &&
|
|
12
12
|
typeof file === "object" &&
|
|
13
|
-
|
|
13
|
+
file.file_path &&
|
|
14
14
|
!(file instanceof File)) {
|
|
15
|
-
//
|
|
16
|
-
this.name = file.file_name
|
|
15
|
+
// This is stored file data from the backend
|
|
16
|
+
this.name = file.file_name;
|
|
17
17
|
this.size = file.size;
|
|
18
|
-
this.type = file.mime_type
|
|
18
|
+
this.type = file.mime_type; // Now coming from backend
|
|
19
19
|
this.lastModified = null;
|
|
20
20
|
// Mark as already uploaded
|
|
21
21
|
this.uploaded = true;
|
|
22
22
|
this.uploading = false;
|
|
23
|
-
// Store the entire response
|
|
24
|
-
this.uploadResult = {
|
|
25
|
-
file_path: file.file_path || file.filePath,
|
|
26
|
-
file_name: file.file_name || file.fileName,
|
|
27
|
-
file_url: file.file_url || file.fileUrl,
|
|
28
|
-
size: file.size,
|
|
29
|
-
mime_type: file.mime_type || file.mimeType,
|
|
30
|
-
};
|
|
23
|
+
this.uploadResult = file; // Store the entire response
|
|
31
24
|
this.uploadError = null;
|
|
32
25
|
this.fileData = null;
|
|
33
26
|
// No upload properties needed
|
|
@@ -66,13 +59,6 @@ export class FileObject {
|
|
|
66
59
|
`Provided: ${this.chunkSize / (1024 * 1024)}MB`);
|
|
67
60
|
}
|
|
68
61
|
this.maxConcurrency = options.maxConcurrency || 3;
|
|
69
|
-
// This is to make the fileObject serializable by IDB in the cache
|
|
70
|
-
Object.defineProperty(this, "uploadPromise", {
|
|
71
|
-
value: Promise.resolve(this.uploadResult),
|
|
72
|
-
writable: true,
|
|
73
|
-
enumerable: false,
|
|
74
|
-
configurable: true,
|
|
75
|
-
});
|
|
76
62
|
this.uploadPromise = this._initializeAndStartUpload(file, options);
|
|
77
63
|
}
|
|
78
64
|
get isStoredFile() {
|
|
@@ -96,24 +82,6 @@ export class FileObject {
|
|
|
96
82
|
}
|
|
97
83
|
return configInstance.buildFileUrl(this.uploadResult.file_url, this.constructor.configKey);
|
|
98
84
|
}
|
|
99
|
-
serialize() {
|
|
100
|
-
const status = this.status;
|
|
101
|
-
if (status === "uploaded" && this.filePath) {
|
|
102
|
-
return this.filePath;
|
|
103
|
-
}
|
|
104
|
-
else if (status === "failed") {
|
|
105
|
-
throw new Error(`Cannot use FileObject in query - upload failed: ${this.uploadError}`);
|
|
106
|
-
}
|
|
107
|
-
else if (status === "uploading") {
|
|
108
|
-
throw new Error(`Cannot use FileObject in query - file is still uploading. Wait for upload to complete before executing the query.`);
|
|
109
|
-
}
|
|
110
|
-
else if (status === "pending") {
|
|
111
|
-
throw new Error(`Cannot use FileObject in query - file upload has not started yet.`);
|
|
112
|
-
}
|
|
113
|
-
else {
|
|
114
|
-
throw new Error(`Cannot use FileObject in query - unexpected status: ${status}`);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
85
|
async _initializeAndStartUpload(file, options) {
|
|
118
86
|
const config = configInstance.getConfig();
|
|
119
87
|
const backend = config.backendConfigs?.[this.constructor.configKey];
|
|
@@ -384,11 +352,19 @@ export class FileObject {
|
|
|
384
352
|
}
|
|
385
353
|
toJSON() {
|
|
386
354
|
return {
|
|
355
|
+
name: this.name,
|
|
356
|
+
size: this.size,
|
|
357
|
+
type: this.type,
|
|
358
|
+
status: this.status,
|
|
359
|
+
uploaded: this.uploaded,
|
|
387
360
|
filePath: this.filePath,
|
|
388
|
-
fileName: this.name,
|
|
389
361
|
fileUrl: this.fileUrl,
|
|
390
|
-
|
|
391
|
-
|
|
362
|
+
uploadResult: this.uploadResult,
|
|
363
|
+
uploadError: this.uploadError ? String(this.uploadError) : null,
|
|
364
|
+
uploadType: this.uploadType,
|
|
365
|
+
uploadId: this.uploadId,
|
|
366
|
+
totalChunks: this.totalChunks,
|
|
367
|
+
completedChunks: this.completedChunks,
|
|
392
368
|
};
|
|
393
369
|
}
|
|
394
370
|
}
|
|
@@ -37,6 +37,51 @@ export function processIncludedEntities(modelStoreRegistry, included, ModelClass
|
|
|
37
37
|
throw new Error(`Failed to process included entities: ${error.message}`);
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Recursively processes an object to replace FileObject instances with their file paths.
|
|
42
|
+
* Throws an error if any FileObject is not yet uploaded.
|
|
43
|
+
*
|
|
44
|
+
* @param {any} obj - The object to process
|
|
45
|
+
* @returns {any} The processed object with FileObjects replaced by paths
|
|
46
|
+
*/
|
|
47
|
+
function processFileObjects(obj) {
|
|
48
|
+
if (obj === null || obj === undefined) {
|
|
49
|
+
return obj;
|
|
50
|
+
}
|
|
51
|
+
// Handle FileObject instances
|
|
52
|
+
if (obj instanceof FileObject) {
|
|
53
|
+
const status = obj.status;
|
|
54
|
+
if (status === 'uploaded' && obj.filePath) {
|
|
55
|
+
return obj.filePath;
|
|
56
|
+
}
|
|
57
|
+
else if (status === 'error') {
|
|
58
|
+
throw new Error(`Cannot use FileObject in query - upload failed: ${obj.uploadError}`);
|
|
59
|
+
}
|
|
60
|
+
else if (status === 'uploading') {
|
|
61
|
+
throw new Error(`Cannot use FileObject in query - file is still uploading. Wait for upload to complete before executing the query.`);
|
|
62
|
+
}
|
|
63
|
+
else if (status === 'pending') {
|
|
64
|
+
throw new Error(`Cannot use FileObject in query - file upload has not started yet.`);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
throw new Error(`Cannot use FileObject in query - unexpected status: ${status}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Handle arrays
|
|
71
|
+
if (Array.isArray(obj)) {
|
|
72
|
+
return obj.map(item => processFileObjects(item));
|
|
73
|
+
}
|
|
74
|
+
// Handle plain objects
|
|
75
|
+
if (typeof obj === 'object' && obj.constructor === Object) {
|
|
76
|
+
const processedObj = {};
|
|
77
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
78
|
+
processedObj[key] = processFileObjects(value);
|
|
79
|
+
}
|
|
80
|
+
return processedObj;
|
|
81
|
+
}
|
|
82
|
+
// Return primitive values as-is
|
|
83
|
+
return obj;
|
|
84
|
+
}
|
|
40
85
|
/**
|
|
41
86
|
* Makes an API call to the backend with the given QuerySet.
|
|
42
87
|
* Automatically handles FileObject replacement with file paths for write operations.
|
|
@@ -84,6 +129,15 @@ export async function makeApiCall(querySet, operationType, args = {}, operationI
|
|
|
84
129
|
"get_or_create", "update_or_create"
|
|
85
130
|
];
|
|
86
131
|
const isWriteOperation = writeOperations.includes(operationType);
|
|
132
|
+
// Process FileObjects for write operations
|
|
133
|
+
if (isWriteOperation) {
|
|
134
|
+
try {
|
|
135
|
+
payload = processFileObjects(payload);
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
throw new Error(`Failed to process file uploads: ${error.message}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
87
141
|
const baseUrl = backend.API_URL.replace(/\/+$/, "");
|
|
88
142
|
const finalUrl = `${baseUrl}/${ModelClass.modelName}/`;
|
|
89
143
|
const headers = backend.getAuthHeaders ? backend.getAuthHeaders() : {};
|
|
@@ -13,7 +13,6 @@ import { FileObject } from './files.js';
|
|
|
13
13
|
import { configInstance } from "../../config.js";
|
|
14
14
|
import { parseStateZeroError, MultipleObjectsReturned, DoesNotExist, } from "./errors.js";
|
|
15
15
|
import axios from "axios";
|
|
16
|
-
import { typeOf } from "mathjs";
|
|
17
16
|
/**
|
|
18
17
|
* A constructor for a Model.
|
|
19
18
|
*
|
|
@@ -101,15 +100,16 @@ export class Model {
|
|
|
101
100
|
// Let DateParsingHelpers.parseDate throw if it fails
|
|
102
101
|
return DateParsingHelpers.parseDate(value, field, ModelClass.schema);
|
|
103
102
|
}
|
|
103
|
+
// File/Image fields need special handling - wrap as FileObject
|
|
104
104
|
const fileFormats = ["file-path", "image-path"];
|
|
105
105
|
if (ModelClass.schema &&
|
|
106
106
|
fileFormats.includes(ModelClass.schema.properties[field]?.format) &&
|
|
107
107
|
value) {
|
|
108
|
-
//
|
|
108
|
+
// Check if it's already a FileObject
|
|
109
109
|
if (value instanceof FileObject) {
|
|
110
|
-
return value
|
|
110
|
+
return value;
|
|
111
111
|
}
|
|
112
|
-
// If it's stored file data from API,
|
|
112
|
+
// If it's stored file data from API, wrap it as FileObject
|
|
113
113
|
if (typeof value === "object" && value.file_path) {
|
|
114
114
|
// Create anonymous subclass with correct configKey
|
|
115
115
|
const BackendFileObject = (_a = class extends FileObject {
|
|
@@ -117,12 +117,7 @@ export class Model {
|
|
|
117
117
|
__setFunctionName(_a, "BackendFileObject"),
|
|
118
118
|
_a.configKey = ModelClass.configKey,
|
|
119
119
|
_a);
|
|
120
|
-
|
|
121
|
-
return fileObj.toJSON(); // Get the computed fileUrl, etc.
|
|
122
|
-
}
|
|
123
|
-
// If it's just a file path string, return as-is
|
|
124
|
-
if (typeof value === "string") {
|
|
125
|
-
return value;
|
|
120
|
+
return new BackendFileObject(value);
|
|
126
121
|
}
|
|
127
122
|
}
|
|
128
123
|
// relationship fields need special handling
|
package/package.json
CHANGED