Skip to content

Commit 0a8e1cc

Browse files
authored
build: Release (#2939)
2 parents 0d5a1c4 + d112095 commit 0a8e1cc

6 files changed

Lines changed: 68 additions & 38 deletions

File tree

changelogs/CHANGELOG_alpha.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
# [8.4.0-alpha.1](https://github.com/parse-community/Parse-SDK-JS/compare/8.3.0...8.4.0-alpha.1) (2026-03-04)
2+
3+
4+
### Features
5+
6+
* Add support for `Parse.File.setDirectory`, `setMetadata`, `setTags` with stream-based file upload ([#2937](https://github.com/parse-community/Parse-SDK-JS/issues/2937)) ([18ded83](https://github.com/parse-community/Parse-SDK-JS/commit/18ded83e69692e67ca0aeec86f62ec24e7e4407e))
7+
18
# [8.3.0-alpha.1](https://github.com/parse-community/Parse-SDK-JS/compare/8.2.0...8.3.0-alpha.1) (2026-02-25)
29

310

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "parse",
3-
"version": "8.3.0",
3+
"version": "8.4.0-alpha.1",
44
"description": "Parse JavaScript SDK",
55
"homepage": "https://parseplatform.org",
66
"keywords": [

src/ParseFile.ts

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ class ParseFile {
9898
* JSON encoding if metadata or tags are set.
9999
* 6. (Node.js only) a Readable stream, or a Web ReadableStream.
100100
* Streamed as raw binary data directly into the upload request.
101-
* Throws if metadata or tags are set.
101+
* Supports metadata, tags, and directory when Parse Server >= 9.5.0.
102102
* For example:
103103
* <pre>
104104
* var fileUploadControl = $("#profilePhotoFileUpload")[0];
@@ -289,8 +289,8 @@ class ParseFile {
289289
* In Node.js, files created with Buffer or ReadableStream are uploaded as
290290
* raw binary data, avoiding base64 encoding overhead. If metadata
291291
* or tags are set on a Buffer-backed file, the upload falls back to base64
292-
* JSON encoding (since the binary endpoint does not support metadata).
293-
* Stream-backed files with metadata or tags will throw an error.
292+
* JSON encoding. Stream-backed files support metadata, tags, and directory
293+
* when Parse Server >= 9.5.0.
294294
*
295295
* @param {object} options
296296
* Valid options are:<ul>
@@ -322,24 +322,19 @@ class ParseFile {
322322
const controller = CoreManager.getFileController();
323323
if (!this._previousSave) {
324324
if (this._source.format === 'buffer' || this._source.format === 'stream') {
325-
const hasFileData =
326-
(this._metadata && Object.keys(this._metadata).length > 0) ||
327-
(this._tags && Object.keys(this._tags).length > 0) ||
328-
!!this._directory;
329-
330-
if (this._source.format === 'stream' && hasFileData) {
331-
throw new Error(
332-
'Cannot save a stream-based file with metadata, tags, or directory. Use a Buffer instead.'
333-
);
334-
}
335325
if (this._source.format === 'stream' && !controller.saveBinary) {
336326
throw new Error(
337327
'Cannot save a stream-based file without saveBinary support on the FileController.'
338328
);
339329
}
340330

341-
if (!hasFileData && controller.saveBinary) {
342-
// Binary upload via ajax
331+
const hasFileData =
332+
(this._metadata && Object.keys(this._metadata).length > 0) ||
333+
(this._tags && Object.keys(this._tags).length > 0) ||
334+
!!this._directory;
335+
336+
if (controller.saveBinary && (this._source.format === 'stream' || !hasFileData)) {
337+
// Binary upload via ajax (file data sent via headers for streams)
343338
this._previousSave = controller
344339
.saveBinary(this._name, this._source, options)
345340
.then(res => {
@@ -469,7 +464,8 @@ class ParseFile {
469464
}
470465

471466
/**
472-
* Sets metadata to be saved with file object. Overwrites existing metadata
467+
* Sets metadata to be saved with file object. Overwrites existing metadata.
468+
* When used with a stream-based file, requires Parse Server >= 9.5.0.
473469
*
474470
* @param {object} metadata Key value pairs to be stored with file object
475471
*/
@@ -483,6 +479,7 @@ class ParseFile {
483479

484480
/**
485481
* Sets metadata to be saved with file object. Adds to existing metadata.
482+
* When used with a stream-based file, requires Parse Server >= 9.5.0.
486483
*
487484
* @param {string} key key to store the metadata
488485
* @param {*} value metadata
@@ -494,7 +491,8 @@ class ParseFile {
494491
}
495492

496493
/**
497-
* Sets tags to be saved with file object. Overwrites existing tags
494+
* Sets tags to be saved with file object. Overwrites existing tags.
495+
* When used with a stream-based file, requires Parse Server >= 9.5.0.
498496
*
499497
* @param {object} tags Key value pairs to be stored with file object
500498
*/
@@ -508,6 +506,7 @@ class ParseFile {
508506

509507
/**
510508
* Sets tags to be saved with file object. Adds to existing tags.
509+
* When used with a stream-based file, requires Parse Server >= 9.5.0.
511510
*
512511
* @param {string} key key to store tags
513512
* @param {*} value tag
@@ -521,7 +520,7 @@ class ParseFile {
521520
/**
522521
* Sets the directory where the file will be stored.
523522
* Requires the Master Key when saving.
524-
* Requires Parse Server >= 9.4.0.
523+
* Requires Parse Server >= 9.4.0; when used with a stream-based file, requires Parse Server >= 9.5.0.
525524
*
526525
* @param {string} directory the directory path
527526
*/
@@ -624,6 +623,15 @@ const DefaultController = {
624623
'X-Parse-Upload-Mode': 'stream',
625624
};
626625
headers['Content-Type'] = (source.type || 'application/octet-stream').replace(/[\r\n]/g, '');
626+
if (options.directory) {
627+
headers['X-Parse-File-Directory'] = options.directory.replace(/[\r\n]/g, '');
628+
}
629+
if (options.metadata && Object.keys(options.metadata).length > 0) {
630+
headers['X-Parse-File-Metadata'] = JSON.stringify(options.metadata);
631+
}
632+
if (options.tags && Object.keys(options.tags).length > 0) {
633+
headers['X-Parse-File-Tags'] = JSON.stringify(options.tags);
634+
}
627635
const jsKey = CoreManager.get('JAVASCRIPT_KEY');
628636
if (jsKey) {
629637
headers['X-Parse-JavaScript-Key'] = jsKey;

src/__tests__/ParseFile-test.js

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1241,19 +1241,30 @@ describe('FileController', () => {
12411241
expect(ajax).not.toHaveBeenCalled();
12421242
});
12431243

1244-
it('stream with metadata throws error', async () => {
1244+
it('stream with file data uses saveBinary with headers', async () => {
1245+
const ajax = jest.fn().mockResolvedValue({
1246+
response: { name: 'mydir/parse.txt', url: 'https://files.example.com/a/mydir/parse.txt' },
1247+
});
1248+
const request = jest.fn();
1249+
CoreManager.setRESTController({ request, ajax });
1250+
CoreManager.set('APPLICATION_ID', 'testAppId');
1251+
CoreManager.set('MASTER_KEY', 'testMasterKey');
1252+
12451253
const { Readable } = require('stream');
1246-
const stream = new Readable({ read() { this.push(null); } });
1254+
const stream = new Readable({ read() { this.push('hello'); this.push(null); } });
12471255
const file = new ParseFile('parse.txt', stream, 'text/plain');
1248-
file.addMetadata('foo', 'bar');
1249-
try {
1250-
await file.save();
1251-
expect(true).toBe(false);
1252-
} catch (e) {
1253-
expect(e.message).toBe(
1254-
'Cannot save a stream-based file with metadata, tags, or directory. Use a Buffer instead.'
1255-
);
1256-
}
1256+
file.setDirectory('mydir');
1257+
file.addMetadata('key1', 'value1');
1258+
file.addTag('tag1', 'tagValue1');
1259+
const f = await file.save({ useMasterKey: true });
1260+
1261+
expect(f).toBe(file);
1262+
expect(ajax).toHaveBeenCalled();
1263+
const [, , , headers] = ajax.mock.calls[0];
1264+
expect(headers['X-Parse-File-Directory']).toBe('mydir');
1265+
expect(headers['X-Parse-File-Metadata']).toBe('{"key1":"value1"}');
1266+
expect(headers['X-Parse-File-Tags']).toBe('{"tag1":"tagValue1"}');
1267+
expect(request).not.toHaveBeenCalled();
12571268
});
12581269

12591270
it('buffer without metadata uses saveBinary', async () => {

types/ParseFile.d.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ declare class ParseFile {
6464
* JSON encoding if metadata or tags are set.
6565
* 6. (Node.js only) a Readable stream, or a Web ReadableStream.
6666
* Streamed as raw binary data directly into the upload request.
67-
* Throws if metadata or tags are set.
67+
* Supports metadata, tags, and directory when Parse Server >= 9.5.0.
6868
* For example:
6969
* <pre>
7070
* var fileUploadControl = $("#profilePhotoFileUpload")[0];
@@ -151,8 +151,8 @@ declare class ParseFile {
151151
* In Node.js, files created with Buffer or ReadableStream are uploaded as
152152
* raw binary data, avoiding base64 encoding overhead. If metadata
153153
* or tags are set on a Buffer-backed file, the upload falls back to base64
154-
* JSON encoding (since the binary endpoint does not support metadata).
155-
* Stream-backed files with metadata or tags will throw an error.
154+
* JSON encoding. Stream-backed files support metadata, tags, and directory
155+
* when Parse Server >= 9.5.0.
156156
*
157157
* @param {object} options
158158
* Valid options are:<ul>
@@ -200,26 +200,30 @@ declare class ParseFile {
200200
};
201201
equals(other: any): boolean;
202202
/**
203-
* Sets metadata to be saved with file object. Overwrites existing metadata
203+
* Sets metadata to be saved with file object. Overwrites existing metadata.
204+
* When used with a stream-based file, requires Parse Server >= 9.5.0.
204205
*
205206
* @param {object} metadata Key value pairs to be stored with file object
206207
*/
207208
setMetadata(metadata: Record<string, any>): void;
208209
/**
209210
* Sets metadata to be saved with file object. Adds to existing metadata.
211+
* When used with a stream-based file, requires Parse Server >= 9.5.0.
210212
*
211213
* @param {string} key key to store the metadata
212214
* @param {*} value metadata
213215
*/
214216
addMetadata(key: string, value: any): void;
215217
/**
216-
* Sets tags to be saved with file object. Overwrites existing tags
218+
* Sets tags to be saved with file object. Overwrites existing tags.
219+
* When used with a stream-based file, requires Parse Server >= 9.5.0.
217220
*
218221
* @param {object} tags Key value pairs to be stored with file object
219222
*/
220223
setTags(tags: Record<string, any>): void;
221224
/**
222225
* Sets tags to be saved with file object. Adds to existing tags.
226+
* When used with a stream-based file, requires Parse Server >= 9.5.0.
223227
*
224228
* @param {string} key key to store tags
225229
* @param {*} value tag
@@ -228,7 +232,7 @@ declare class ParseFile {
228232
/**
229233
* Sets the directory where the file will be stored.
230234
* Requires the Master Key when saving.
231-
* Requires Parse Server >= 9.4.0.
235+
* Requires Parse Server >= 9.4.0; when used with a stream-based file, requires Parse Server >= 9.5.0.
232236
*
233237
* @param {string} directory the directory path
234238
*/

0 commit comments

Comments
 (0)