New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

balena-request

Package Overview
Dependencies
Maintainers
1
Versions
177
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

balena-request - npm Package Compare versions

Comparing version 13.0.0 to 13.1.0-build-otaviojacobi-does-multipart-form-when-blob-is-present-d807c01905068f81d9d353364742947142c9c272-1

2

build/progress.js

@@ -20,3 +20,3 @@ "use strict";

const tslib_1 = require("tslib");
const utils = require("./utils");
const utils = tslib_1.__importStar(require("./utils"));
/**

@@ -23,0 +23,0 @@ * @module progress

@@ -42,2 +42,5 @@ /// <reference types="node" />

}
export interface WebResourceFile extends Blob {
name: string;
}
export interface Interceptor {

@@ -57,2 +60,6 @@ request?(response: any): Promise<any>;

export type BalenaRequest = ReturnType<typeof getRequest>;
export declare class BalenaFile extends Blob implements WebResourceFile {
name: string;
constructor(name: string, blobParts?: BlobPart[], options?: BlobPropertyBag);
}
/**

@@ -59,0 +66,0 @@ * @module request

@@ -18,7 +18,17 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
exports.getRequest = void 0;
exports.getRequest = exports.BalenaFile = void 0;
const tslib_1 = require("tslib");
const urlLib = require("url");
const errors = require("balena-errors");
const utils = require("./utils");
const urlLib = tslib_1.__importStar(require("url"));
const errors = tslib_1.__importStar(require("balena-errors"));
const mime = tslib_1.__importStar(require("mime"));
const utils = tslib_1.__importStar(require("./utils"));
class BalenaFile extends Blob {
constructor(name, blobParts, options) {
var _a, _b;
options = Object.assign(Object.assign({}, options), { type: (_b = (_a = options === null || options === void 0 ? void 0 : options.type) !== null && _a !== void 0 ? _a : mime.getType(name)) !== null && _b !== void 0 ? _b : undefined });
super(blobParts, options);
this.name = name;
}
}
exports.BalenaFile = BalenaFile;
/**

@@ -25,0 +35,0 @@ * @module request

@@ -21,6 +21,7 @@ "use strict";

const { fetch: normalFetch, Headers: HeadersPonyfill } = require('fetch-ponyfill')({ Promise });
const urlLib = require("url");
const qs = require("qs");
const errors = require("balena-errors");
const urlLib = tslib_1.__importStar(require("url"));
const qs = tslib_1.__importStar(require("qs"));
const errors = tslib_1.__importStar(require("balena-errors"));
const balena_auth_1 = require("balena-auth");
const stream_1 = require("stream");
const IS_BROWSER = typeof window !== 'undefined' && window !== null;

@@ -318,4 +319,11 @@ /**

exports.getBody = getBody;
const isFile = (value) => {
return (value &&
value.name != null &&
typeof value.name === 'string' &&
value instanceof Blob);
};
// This is the actual implementation that hides the internal `retriesRemaining` parameter
function requestAsync(fetch, options, retriesRemaining) {
var _a;
return tslib_1.__awaiter(this, void 0, void 0, function* () {

@@ -336,2 +344,29 @@ const [url, opts] = processRequestOptions(options);

}
const NodeFormData = (yield import('formdata-node')).FormData;
const FormDataEncoder = (yield import('form-data-encoder')).FormDataEncoder;
const nodeForm = new NodeFormData();
const form = IS_BROWSER ? new FormData() : nodeForm;
let hasBlob = false;
for (const [k, v] of Object.entries((_a = options.body) !== null && _a !== void 0 ? _a : {})) {
if (isFile(v)) {
hasBlob = true;
}
form.append(k, v);
}
if (hasBlob) {
if (IS_BROWSER) {
// Browsers will handle set form data header and boundaries
// Given the correct header
opts.headers.delete('Content-Type');
// @ts-expect-error
opts.body = form;
}
else {
const encoder = new FormDataEncoder(form);
opts.headers.set('Content-Type', encoder.headers['Content-Type']);
opts.headers.set('Content-Length', encoder.headers['Content-Length']);
// @ts-expect-error
opts.body = stream_1.Readable.from(encoder);
}
}
try {

@@ -338,0 +373,0 @@ const requestTime = Date.now();

@@ -7,2 +7,6 @@ # Change Log

## 13.1.0 - 2023-10-11
* Add multi part request support when File-like is present [Otávio Jacobi]
## 13.0.0 - 2023-10-11

@@ -9,0 +13,0 @@

@@ -22,2 +22,3 @@ /*

import * as errors from 'balena-errors';
import * as mime from 'mime';
import * as utils from './utils';

@@ -65,2 +66,6 @@

export interface WebResourceFile extends Blob {
name: string;
}
export interface Interceptor {

@@ -83,2 +88,16 @@ request?(response: any): Promise<any>;

export class BalenaFile extends Blob implements WebResourceFile {
constructor(
public name: string,
blobParts?: BlobPart[],
options?: BlobPropertyBag,
) {
options = {
...options,
type: options?.type ?? mime.getType(name) ?? undefined,
};
super(blobParts, options);
}
}
/**

@@ -85,0 +104,0 @@ * @module request

@@ -26,3 +26,8 @@ /*

import { TokenType } from 'balena-auth';
import type { BalenaRequestOptions, BalenaRequestResponse } from './request';
import type {
BalenaRequestOptions,
BalenaRequestResponse,
WebResourceFile,
} from './request';
import { Readable } from 'stream';

@@ -376,4 +381,12 @@ const IS_BROWSER = typeof window !== 'undefined' && window !== null;

const isFile = (value: any): value is WebResourceFile => {
return (
value &&
value.name != null &&
typeof value.name === 'string' &&
value instanceof Blob
);
};
// This is the actual implementation that hides the internal `retriesRemaining` parameter
async function requestAsync(

@@ -399,3 +412,33 @@ fetch: typeof normalFetch,

}
const NodeFormData = (await import('formdata-node')).FormData;
const FormDataEncoder = (await import('form-data-encoder')).FormDataEncoder;
const nodeForm = new NodeFormData();
const form = IS_BROWSER ? new FormData() : nodeForm;
let hasBlob = false;
for (const [k, v] of Object.entries(options.body ?? {})) {
if (isFile(v)) {
hasBlob = true;
}
form.append(k, v as string | WebResourceFile);
}
if (hasBlob) {
if (IS_BROWSER) {
// Browsers will handle set form data header and boundaries
// Given the correct header
opts.headers.delete('Content-Type');
// @ts-expect-error
opts.body = form;
} else {
const encoder = new FormDataEncoder(form as typeof nodeForm);
opts.headers.set('Content-Type', encoder.headers['Content-Type']);
opts.headers.set('Content-Length', encoder.headers['Content-Length']!);
// @ts-expect-error
opts.body = Readable.from(encoder);
}
}
try {

@@ -402,0 +445,0 @@ const requestTime = Date.now();

{
"name": "balena-request",
"version": "13.0.0",
"version": "13.1.0-build-otaviojacobi-does-multipart-form-when-blob-is-present-d807c01905068f81d9d353364742947142c9c272-1",
"description": "Balena HTTP client",

@@ -42,3 +42,5 @@ "main": "build/request.js",

"@types/chai-as-promised": "^7.1.5",
"@types/mime": "^3.0.2",
"@types/mocha": "^9.1.1",
"@types/node": "^18.0.0",
"@types/progress-stream": "^2.0.0",

@@ -75,2 +77,5 @@ "@types/qs": "^6.9.3",

"fetch-readablestream": "^0.2.0",
"form-data-encoder": "^3.0.0",
"formdata-node": "^5.0.0",
"mime": "^3.0.0",
"progress-stream": "^2.0.0",

@@ -85,4 +90,4 @@ "qs": "^6.9.4",

"versionist": {
"publishedAt": "2023-10-11T17:31:39.709Z"
"publishedAt": "2023-10-11T20:07:35.004Z"
}
}
const Bluebird = require('bluebird');
const { expect } = require('chai');
const sinon = require('sinon');
const fs = require('fs');
const mockServer = require('mockttp').getLocal();
const { auth, request, getCustomRequest, IS_BROWSER } = require('./setup')();
const { auth, request, getCustomRequest, IS_BROWSER, BalenaFile } =
require('./setup')();

@@ -221,2 +223,35 @@ // Grab setTimeout before we replace it with a fake later, so

describe('given a body with a Blob data', function () {
this.beforeEach(() => {
mockServer.forPost('/multipart-endpoint').thenCallback(async (req) => {
return { statusCode: 200, body: req.headers['content-type'] };
});
});
it('should send the request as multipart/form-data with boundary', async function () {
let body;
const fileName = 'testfile.txt';
if (IS_BROWSER) {
body = {
content: new File(['a', 'test', 'blob'], fileName),
};
} else {
body = {
content: new BalenaFile(fileName, [
new Blob(['a', 'test', 'blob']),
]),
};
}
const res = await request.send({
method: 'POST',
baseUrl: mockServer.url,
url: '/multipart-endpoint',
body: body,
});
return expect(res.body.startsWith('multipart/form-data; boundary=')).to
.be.true;
});
});
describe('given an endpoint that fails the first two times', function () {

@@ -223,0 +258,0 @@ beforeEach(() =>

@@ -19,3 +19,4 @@ const IS_BROWSER = typeof window !== 'undefined' && window !== null;

const { getRequest } = require('../build/request');
const { BalenaFile, getRequest } = require('../build/request');
const chai = require('chai');

@@ -35,2 +36,3 @@ const chaiAsPromised = require('chai-as-promised');

getCustomRequest,
BalenaFile,
});
{
"compilerOptions": {
"module": "commonjs",
"module": "Node16",
"declaration": true,

@@ -13,3 +13,3 @@ "outDir": "build",

"noUnusedLocals": true,
"moduleResolution": "node",
"moduleResolution": "Node16",
"allowJs": true,

@@ -16,0 +16,0 @@ "checkJs": true,

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc