🚀. Socket Launch Week Day 2:Introducing Manifest Alerts.Learn more
Sign In

tls-client-node

Package Overview
Dependencies
Maintainers
1
Versions
14
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

tls-client-node - npm Package Compare versions

Comparing version
0.1.7
to
0.1.8
+21
dist/multipart.d.ts
import type { MultipartBodyLike } from "./types";
export type MultipartFieldValue = string | number | boolean | Blob;
export type MultipartFileSource = string | Blob | ArrayBuffer | ArrayBufferView;
export interface MultipartFileOptions {
filename: string;
contentType?: string;
}
export interface MultipartFileValue extends MultipartFileOptions {
data: MultipartFileSource;
}
export type MultipartValue = MultipartFieldValue | MultipartFileValue;
export type MultipartRecord = Record<string, MultipartValue | MultipartValue[]>;
export declare class MultipartForm implements MultipartBodyLike {
private readonly formData;
append(name: string, value: MultipartFieldValue): this;
appendFile(name: string, value: MultipartFileSource, options: MultipartFileOptions): this;
appendJson(name: string, value: unknown, filename?: string): this;
toFormData(): FormData;
static fromRecord(record: MultipartRecord): MultipartForm;
}
export declare function createMultipartForm(record?: MultipartRecord): MultipartForm;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MultipartForm = void 0;
exports.createMultipartForm = createMultipartForm;
function isMultipartFileValue(value) {
return typeof value === "object" && value !== null && "data" in value;
}
function createBlobFromSource(value, contentType) {
if (value instanceof Blob && contentType === undefined) {
return value;
}
if (value instanceof Blob) {
return new Blob([value], { type: contentType });
}
if (value instanceof ArrayBuffer) {
return new Blob([value], { type: contentType });
}
if (ArrayBuffer.isView(value)) {
const bytes = Uint8Array.from(new Uint8Array(value.buffer, value.byteOffset, value.byteLength));
return new Blob([
bytes,
], { type: contentType });
}
return new Blob([value], { type: contentType });
}
class MultipartForm {
formData = new FormData();
append(name, value) {
if (value instanceof Blob) {
this.formData.append(name, value);
}
else {
this.formData.append(name, String(value));
}
return this;
}
appendFile(name, value, options) {
this.formData.append(name, createBlobFromSource(value, options.contentType), options.filename);
return this;
}
appendJson(name, value, filename = `${name}.json`) {
return this.appendFile(name, JSON.stringify(value), {
filename,
contentType: "application/json",
});
}
toFormData() {
return this.formData;
}
static fromRecord(record) {
const form = new MultipartForm();
for (const [name, rawValue] of Object.entries(record)) {
const values = Array.isArray(rawValue) ? rawValue : [rawValue];
for (const value of values) {
if (isMultipartFileValue(value)) {
form.appendFile(name, value.data, value);
}
else {
form.append(name, value);
}
}
}
return form;
}
}
exports.MultipartForm = MultipartForm;
function createMultipartForm(record) {
return record ? MultipartForm.fromRecord(record) : new MultipartForm();
}
+3
-3

@@ -8,3 +8,3 @@ import { CookieJar } from "tough-cookie";

}
declare function buildForwardPayload(url: string, sessionDefaults: SessionOptions | undefined, requestOptions: RequestOptions, sessionId?: string): {
declare function buildForwardPayload(url: string, sessionDefaults: SessionOptions | undefined, requestOptions: RequestOptions, sessionId?: string): Promise<{
requestUrl: string;

@@ -47,4 +47,4 @@ requestMethod: HttpMethod;

streamOutputEOFSymbol: string | undefined;
};
type ForwardPayload = ReturnType<typeof buildForwardPayload>;
}>;
type ForwardPayload = Awaited<ReturnType<typeof buildForwardPayload>>;
export declare class TLSClient {

@@ -51,0 +51,0 @@ private readonly options;

@@ -226,4 +226,20 @@ "use strict";

}
function encodeBody(body, headers, forceBinary) {
if (body === undefined || body === null) {
function normalizeRedirectBehavior(value) {
if (value === undefined) {
return undefined;
}
if (typeof value === "boolean") {
return value;
}
return value === "follow";
}
function isMultipartBodyLike(body) {
return typeof body === "object" && body !== null && "toFormData" in body;
}
function isFormDataBody(body) {
return typeof FormData !== "undefined" && body instanceof FormData;
}
async function encodeBody(body, headers, forceBinary) {
const normalizedBody = isMultipartBodyLike(body) ? body.toFormData() : body;
if (normalizedBody === undefined || normalizedBody === null) {
return {

@@ -234,9 +250,9 @@ requestBody: undefined,

}
if (typeof body === "string") {
if (typeof normalizedBody === "string") {
return {
requestBody: body,
requestBody: normalizedBody,
isByteRequest: Boolean(forceBinary),
};
}
if (body instanceof URLSearchParams) {
if (normalizedBody instanceof URLSearchParams) {
if (!headers["content-type"]) {

@@ -246,10 +262,25 @@ headers["content-type"] = "application/x-www-form-urlencoded;charset=UTF-8";

return {
requestBody: body.toString(),
requestBody: normalizedBody.toString(),
isByteRequest: false,
};
}
if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) {
const buffer = body instanceof ArrayBuffer
? Buffer.from(body)
: Buffer.from(body.buffer, body.byteOffset, body.byteLength);
if (isFormDataBody(normalizedBody)) {
const request = new Request("http://127.0.0.1/", {
method: "POST",
body: normalizedBody,
});
const contentType = request.headers.get("content-type");
const arrayBuffer = await request.arrayBuffer();
if (contentType) {
headers["content-type"] = contentType;
}
return {
requestBody: Buffer.from(arrayBuffer).toString("base64"),
isByteRequest: true,
};
}
if (normalizedBody instanceof ArrayBuffer || ArrayBuffer.isView(normalizedBody)) {
const buffer = normalizedBody instanceof ArrayBuffer
? Buffer.from(normalizedBody)
: Buffer.from(normalizedBody.buffer, normalizedBody.byteOffset, normalizedBody.byteLength);
if (!headers["content-type"]) {

@@ -267,3 +298,3 @@ headers["content-type"] = "application/octet-stream";

return {
requestBody: JSON.stringify(body),
requestBody: JSON.stringify(normalizedBody),
isByteRequest: false,

@@ -311,3 +342,3 @@ };

}
function buildForwardPayload(url, sessionDefaults, requestOptions, sessionId) {
async function buildForwardPayload(url, sessionDefaults, requestOptions, sessionId) {
const headers = mergeHeaders(sessionDefaults?.headers, requestOptions.headers);

@@ -317,3 +348,3 @@ const profileSelection = getProfileSelection(sessionDefaults, requestOptions);

const timeoutSeconds = pickDefined(requestOptions.timeoutSeconds, sessionDefaults?.timeoutSeconds, timeoutMilliseconds !== undefined ? 0 : 30);
const { requestBody, isByteRequest } = encodeBody(requestOptions.body, headers, requestOptions.isByteRequest);
const { requestBody, isByteRequest } = await encodeBody(requestOptions.body, headers, requestOptions.isByteRequest);
return {

@@ -326,3 +357,3 @@ requestUrl: url,

customTlsClient: profileSelection.customTlsClient,
followRedirects: pickDefined(requestOptions.followRedirects, sessionDefaults?.followRedirects, false),
followRedirects: pickDefined(normalizeRedirectBehavior(requestOptions.redirect), requestOptions.followRedirects, normalizeRedirectBehavior(sessionDefaults?.redirect), sessionDefaults?.followRedirects, false),
insecureSkipVerify: pickDefined(requestOptions.insecureSkipVerify, sessionDefaults?.insecureSkipVerify, false),

@@ -628,3 +659,3 @@ withoutCookieJar: pickDefined(requestOptions.withoutCookieJar, sessionDefaults?.withoutCookieJar, false),

async request(url, options = {}) {
const payload = buildForwardPayload(url, undefined, options, options.sessionId);
const payload = await buildForwardPayload(url, undefined, options, options.sessionId);
return this.forward(payload);

@@ -873,3 +904,3 @@ }

: normalizeCookieInput(options.cookies);
const payload = buildForwardPayload(url, this.defaults, {
const payload = await buildForwardPayload(url, this.defaults, {
...options,

@@ -876,0 +907,0 @@ cookies: requestCookies,

@@ -7,4 +7,4 @@ export { TLSClient, Session, fetch } from "./client";

export { TLSResponse } from "./response";
export { ClientIdentifier } from "./types";
export type { SerializedCookieJar } from "./types";
export { MultipartForm, createMultipartForm } from "./multipart";
export { ClientIdentifier, Emulation } from "./types";
export type * from "./types";
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ClientIdentifier = exports.TLSResponse = exports.TLSClientError = exports.CookieJar = exports.Session = exports.TLSClient = void 0;
exports.Emulation = exports.ClientIdentifier = exports.createMultipartForm = exports.MultipartForm = exports.TLSResponse = exports.TLSClientError = exports.CookieJar = exports.Session = exports.TLSClient = void 0;
var client_1 = require("./client");

@@ -14,3 +14,7 @@ Object.defineProperty(exports, "TLSClient", { enumerable: true, get: function () { return client_1.TLSClient; } });

Object.defineProperty(exports, "TLSResponse", { enumerable: true, get: function () { return response_1.TLSResponse; } });
var multipart_1 = require("./multipart");
Object.defineProperty(exports, "MultipartForm", { enumerable: true, get: function () { return multipart_1.MultipartForm; } });
Object.defineProperty(exports, "createMultipartForm", { enumerable: true, get: function () { return multipart_1.createMultipartForm; } });
var types_1 = require("./types");
Object.defineProperty(exports, "ClientIdentifier", { enumerable: true, get: function () { return types_1.ClientIdentifier; } });
Object.defineProperty(exports, "Emulation", { enumerable: true, get: function () { return types_1.Emulation; } });

@@ -86,3 +86,88 @@ import type { CookieJar } from "tough-cookie";

};
export declare const Emulation: {
readonly chrome_103: "chrome_103";
readonly chrome_104: "chrome_104";
readonly chrome_105: "chrome_105";
readonly chrome_106: "chrome_106";
readonly chrome_107: "chrome_107";
readonly chrome_108: "chrome_108";
readonly chrome_109: "chrome_109";
readonly chrome_110: "chrome_110";
readonly chrome_111: "chrome_111";
readonly chrome_112: "chrome_112";
readonly chrome_116_PSK: "chrome_116_PSK";
readonly chrome_116_PSK_PQ: "chrome_116_PSK_PQ";
readonly chrome_117: "chrome_117";
readonly chrome_120: "chrome_120";
readonly chrome_124: "chrome_124";
readonly chrome_130_PSK: "chrome_130_PSK";
readonly chrome_131: "chrome_131";
readonly chrome_131_PSK: "chrome_131_PSK";
readonly chrome_131_psk: "chrome_131_PSK";
readonly chrome_133: "chrome_133";
readonly chrome_133_PSK: "chrome_133_PSK";
readonly chrome_136: "chrome_136";
readonly chrome_144: "chrome_144";
readonly chrome_144_PSK: "chrome_144_PSK";
readonly chrome_145: "chrome_145";
readonly chrome_146: "chrome_146";
readonly chrome_146_PSK: "chrome_146_PSK";
readonly brave_146: "brave_146";
readonly brave_146_PSK: "brave_146_PSK";
readonly firefox_102: "firefox_102";
readonly firefox_104: "firefox_104";
readonly firefox_105: "firefox_105";
readonly firefox_106: "firefox_106";
readonly firefox_108: "firefox_108";
readonly firefox_110: "firefox_110";
readonly firefox_117: "firefox_117";
readonly firefox_120: "firefox_120";
readonly firefox_123: "firefox_123";
readonly firefox_132: "firefox_132";
readonly firefox_133: "firefox_133";
readonly firefox_135: "firefox_135";
readonly firefox_146_PSK: "firefox_146_PSK";
readonly firefox_147: "firefox_147";
readonly firefox_147_PSK: "firefox_147_PSK";
readonly firefox_148: "firefox_148";
readonly safari_15_6_1: "safari_15_6_1";
readonly safari_16_0: "safari_16_0";
readonly safari_ipad_15_6: "safari_ipad_15_6";
readonly safari_ios_15_5: "safari_ios_15_5";
readonly safari_ios_15_6: "safari_ios_15_6";
readonly safari_ios_16_0: "safari_ios_16_0";
readonly safari_ios_17_0: "safari_ios_17_0";
readonly safari_ios_18_0: "safari_ios_18_0";
readonly safari_ios_18_5: "safari_ios_18_5";
readonly safari_ios_26_0: "safari_ios_26_0";
readonly opera_89: "opera_89";
readonly opera_90: "opera_90";
readonly opera_91: "opera_91";
readonly okhttp4_android_7: "okhttp4_android_7";
readonly okhttp4_android_8: "okhttp4_android_8";
readonly okhttp4_android_9: "okhttp4_android_9";
readonly okhttp4_android_10: "okhttp4_android_10";
readonly okhttp4_android_11: "okhttp4_android_11";
readonly okhttp4_android_12: "okhttp4_android_12";
readonly okhttp4_android_13: "okhttp4_android_13";
readonly confirmed_android: "confirmed_android";
readonly confirmed_ios: "confirmed_ios";
readonly mesh_android: "mesh_android";
readonly mesh_android_1: "mesh_android_1";
readonly mesh_android_2: "mesh_android_2";
readonly mesh_ios: "mesh_ios";
readonly mesh_ios_1: "mesh_ios_1";
readonly mesh_ios_2: "mesh_ios_2";
readonly mms_ios: "mms_ios";
readonly mms_ios_1: "mms_ios_1";
readonly mms_ios_2: "mms_ios_2";
readonly mms_ios_3: "mms_ios_3";
readonly nike_android_mobile: "nike_android_mobile";
readonly nike_ios_mobile: "nike_ios_mobile";
readonly zalando_android_mobile: "zalando_android_mobile";
readonly zalando_ios_mobile: "zalando_ios_mobile";
readonly cloudscraper: "cloudscraper";
};
export type ClientIdentifier = (typeof ClientIdentifier)[keyof typeof ClientIdentifier] | (string & {});
export type Emulation = ClientIdentifier;
export type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS";

@@ -92,2 +177,6 @@ export type HeaderInputValue = string | number | boolean;

export type CookieMap = Record<string, string>;
export type RedirectBehavior = boolean | "follow" | "manual";
export interface MultipartBodyLike {
toFormData(): FormData;
}
export interface Cookie {

@@ -167,2 +256,3 @@ name: string;

isRotatingProxy?: boolean;
redirect?: RedirectBehavior;
followRedirects?: boolean;

@@ -195,3 +285,3 @@ insecureSkipVerify?: boolean;

}
export type RequestBody = string | URLSearchParams | ArrayBuffer | ArrayBufferView | Record<string, unknown> | null | undefined;
export type RequestBody = string | URLSearchParams | FormData | MultipartBodyLike | ArrayBuffer | ArrayBufferView | Record<string, unknown> | null | undefined;
export interface RequestOptions extends SessionOptions {

@@ -198,0 +288,0 @@ method?: HttpMethod | Lowercase<HttpMethod>;

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ClientIdentifier = void 0;
exports.Emulation = exports.ClientIdentifier = void 0;
exports.ClientIdentifier = {

@@ -88,1 +88,2 @@ chrome_103: "chrome_103",

};
exports.Emulation = exports.ClientIdentifier;

@@ -5,3 +5,6 @@ import pkg from "./dist/index.js";

ClientIdentifier,
Emulation,
CookieJar,
MultipartForm,
createMultipartForm,
TLSClient,

@@ -8,0 +11,0 @@ Session,

{
"name": "tls-client-node",
"version": "0.1.7",
"version": "0.1.8",
"description": "Node.js client for bogdanfinn/tls-client with native shared-library loading and optional managed runtime support.",

@@ -5,0 +5,0 @@ "author": "Fatih Kabak",

@@ -79,3 +79,6 @@ <div align="center">

ClientIdentifier,
Emulation,
MultipartForm,
TLSClient,
createMultipartForm,
} from "tls-client-node";

@@ -85,3 +88,3 @@

const session = client.session({
clientIdentifier: ClientIdentifier.chrome_136,
clientIdentifier: Emulation.chrome_136,
});

@@ -93,3 +96,3 @@ ```

```js
const { ClientIdentifier, TLSClient } = require("tls-client-node");
const { ClientIdentifier, Emulation, MultipartForm, TLSClient, createMultipartForm } = require("tls-client-node");
```

@@ -161,2 +164,59 @@

## Multipart Form Uploads
```ts
import { MultipartForm, TLSClient, createMultipartForm } from "tls-client-node";
const client = new TLSClient();
const form = createMultipartForm({
title: "example",
file: {
data: "hello world",
filename: "hello.txt",
contentType: "text/plain",
},
});
const builder = new MultipartForm()
.append("kind", "builder")
.appendJson("meta", { ok: true });
const response = await client.request("https://example.com/upload", {
method: "POST",
body: form,
});
console.log(response.status);
await client.request("https://example.com/upload-builder", {
method: "POST",
body: builder,
});
await client.stop();
```
## Redirect Ergonomics
```ts
import { TLSClient } from "tls-client-node";
const client = new TLSClient();
const session = client.session({
redirect: "follow",
});
await session.get("https://example.com/start", {
redirect: "manual",
});
await client.stop();
```
`redirect` is a higher-level alias for `followRedirects`.
- `redirect: "follow"` maps to `followRedirects: true`
- `redirect: "manual"` maps to `followRedirects: false`
- `redirect: true` and `redirect: false` are also accepted
## Runtime Modes

@@ -259,3 +319,6 @@

- Each `Session` keeps a `tough-cookie` jar in sync with request and response cookies. You can inspect URL cookies with `session.cookies(url)` or serialize the jar with `session.exportCookies()`.
- `Emulation` is exported as a higher-level alias for `ClientIdentifier`, so `Emulation.chrome_136` and `ClientIdentifier.chrome_136` are equivalent.
- Binary responses are returned as a data URL when `byteResponse: true` is enabled, matching upstream behavior.
- `FormData`, `MultipartForm`, and `createMultipartForm()` can all be used for multipart uploads, with the generated boundary preserved in the `content-type` header.
- `redirect` is a higher-level alias over `followRedirects`; it improves call-site clarity without changing upstream redirect semantics.
- WebSocket upgrade and frame APIs are not currently implemented in this wrapper.

@@ -262,0 +325,0 @@ - New upstream client identifiers can be passed as plain strings even before this package adds them to `ClientIdentifier`.