@fastls/fastls 0.10.1 → 0.11.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/README.md CHANGED
@@ -1,8 +1,22 @@
1
1
  # Fastls Node.js SDK
2
2
 
3
- > 发布状态(2026-10-27):0.10.1 已完成认证,尚待 npm 账号验证后上传。当前可安装 0.10.1-dev.0;下文新增接口与 0.10.1 安装命令需正式发布后使用。
3
+ 开发候选新增 Session `addressPolicy`、`profileSpec: new ProfileSpec(...)` 和
4
+ `httpVersion: "h3"`;Session/fetch 可选择完整 Spec 或显式协议。
5
+ `addressPolicy` 只能创建可信 owner,不能由 `session.fetch()` 或请求替换。
6
+ 现有发布库不具备新能力,旧库或缺符号会明确报错,见[能力契约](../CAPABILITIES.md)。
7
+
8
+ ```typescript
9
+ import { Session, ProfileSpec } from '@fastls/fastls';
10
+ import { readFileSync } from 'node:fs';
11
+ const profileSpec = new ProfileSpec(JSON.parse(readFileSync('browser.json', 'utf8')));
12
+ const session = new Session({ profileSpec });
13
+ try { await session.get('https://example.com'); } finally { await session.closeAsync(); }
14
+ // 强制 H3 直连,不支持时不会回退到 TCP;目标须支持 H3。
15
+ const h3 = new Session({ httpVersion: 'h3' });
16
+ try { await h3.get('https://example.com'); } finally { await h3.closeAsync(); }
17
+ ```
4
18
 
5
- Node.js 22+ 的 Fastls C ABI v1 绑定,提供 Promise API、TypeScript 声明、ESM 和 CommonJS 入口。本文对应正式版 **0.10.1**,发行文件与版本状态以 npm registry 为准。
19
+ Node.js 22+ 的 Fastls C ABI v1 绑定,提供 Promise API、TypeScript 声明、ESM 和 CommonJS 入口。本文对应 **0.10.1**。
6
20
 
7
21
  支持 HTTP/1.1、HTTP/2、流式上传下载、双工、SSE、WebSocket、会话 Cookie 和 HTTP/HTTPS/SOCKS 代理。网络请求由 Go 执行,通过 Koffi 在当前 Node 进程中调用;使用者无需 Python 或 RPC 服务。
8
22
 
@@ -46,6 +60,27 @@ try {
46
60
 
47
61
  源码构建、测试和发包见 [开发指南](DEVELOPMENT.md)。普通 npm 用户不需要这些步骤。
48
62
 
63
+ ## 设置浏览器请求头
64
+
65
+ Python 0.1.4 / Node 0.10.1 所用的默认 `chrome/156/windows` 配置没有为 `Accept-Encoding`、`Accept-Language` 和 `Priority` 设置默认值。下面的示例显式补充它们;其它配置的默认值可能不同。
66
+
67
+ ```js
68
+ import { fetch } from '@fastls/fastls';
69
+
70
+ const response = await fetch('https://tls.peet.ws/api/all', {
71
+ profile: 'chrome/156/windows',
72
+ headers: {
73
+ 'Accept-Encoding': 'gzip, deflate, br, zstd',
74
+ 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
75
+ 'Priority': 'u=0, i',
76
+ },
77
+ });
78
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
79
+ console.log(await response.json());
80
+ ```
81
+
82
+ `Accept-Language` 表示语言偏好,请按应用需要调整;`Priority` 的值也与请求场景有关,示例不是所有浏览器请求的固定模板。请求头顺序配置只排列已有字段,不会为其中的字段自动生成值。支持自动解压 gzip/deflate/br/zstd,不等于会自动发送对应的 `Accept-Encoding`。显式传入的头值优先于配置默认值;复用会话时,也可通过 Session 的 `headers` 设置。
83
+
49
84
  ## 从 Node fetch 迁移
50
85
 
51
86
  新增 `fetch` 入口沿用 Node 原生 fetch 的主要调用习惯,并增加指纹、代理和证书配置:
@@ -200,16 +235,10 @@ Session和请求的`proxy`接受URL字符串或null。请求省略或undefined
200
235
 
201
236
  `proxyCaFile`只控制HTTPS代理信任,`caFile`只控制目标信任,两者互不代替。未设置代理CA使用系统根;提供文件则替换系统根。构造Session时读取默认代理CA一次,请求指定路径时重新读取;请求undefined继承默认,null恢复系统根。直连、HTTP和SOCKS忽略代理CA默认值及请求覆盖。代理地址及凭据、profile、代理CA和目标CA共同隔离连接池。目标证书仍需验证;目标请求禁止设置Proxy-Authorization和Proxy-Connection。认证只用于代理握手,错误消息不回显凭据或代理任意响应短语。握手计入connect和total预算,AbortSignal取消会关闭未建立的隧道;取消单条H2流继续复用健康的共享隧道。代理拒绝后不降级或自动重试。
202
237
 
203
- 代理测试使用Node测试进程中的独立、仅访问loopback的CONNECT/RFC1928/RFC1929靶场;SDK本身不依赖Python或代理辅助进程。16 组 Python 行为映射由源码、内置库候选及 npm 拆分包统一验收;语言适配见PARITY.md。
204
-
205
- `npm run test:proxy:soak`默认以32路并发运行60秒,轮换正常请求、共享H2流取消、错误认证和停滞握手取消;四种scheme分别使用真实loopback代理。FASTLS_NODE_PROXY_SOAK_SECONDS可设为1..300。Linux当前Go/Node组合执行该独立测试,沿用现有RSS、Go堆、goroutine及句柄阈值,逐批记录写入build/node-proxy-recovery-*.json,不计入231项主套件。
206
-
207
- 代理独立长测与SSE长测一样,显式使用`--max-semi-space-size=16`固定测试进程的V8半空间上限。本机Windows Node24默认策略在持续分配时将new_space保留空间从4MiB扩大至128MiB,而Go堆、原生请求和隧道已回收。该参数仅作用于独立测试进程,不修改SDK用户进程的运行配置,也不放宽RSS或句柄阈值。证据保存运行参数、V8空间快照,以及阈值判定之后的完整GC诊断;判定使用GC前的最终采样。
238
+ 代理测试和资源长测的运行方式见 [开发说明](https://github.com/FastTLS/fastls-ffi/blob/main/node/DEVELOPMENT.md#代理长测)。
208
239
 
209
240
  私有HTTPS代理示例:`new Session({ proxy: 'https://proxy.example:443', proxyCaFile: '/path/to/proxy-ca.pem', caFile: '/path/to/target-ca.pem' })`。两类CA分别验证代理与目标;请求可用`{ proxyCaFile: null }`恢复代理系统信任,或指定其它代理CA文件,均不会复用不相符的已有隧道。
210
241
 
211
- 代理长测按靶场握手阶段同步取消,避免固定毫秒计时在慢runner上提前中止TLS而未触达待测CONNECT阶段。HTTPS靶场延迟50毫秒处理CONNECT,确保该时序在本地也能覆盖。H2可合并同一代理的在途建连;每批每种scheme至少触发一次真实握手取消,并逐批核对启动/回收计数,不依赖请求数等于物理建连数。资源阈值保持不变。
212
-
213
242
  ## HTTP/2 双工
214
243
 
215
244
  `duplex: true` 允许响应头到达后继续上传,可同时读取响应。仅支持 HTTP/2,并始终禁止自动重定向;普通上传仍拒绝未完成上传时提前返回的 2xx。
package/dist/errors.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export declare class FastlsError extends Error {
2
2
  name: string;
3
+ code?: string | number;
3
4
  }
4
5
  export declare class FastlsHTTPError extends FastlsError {
5
6
  readonly response: import("./response.js").Response | import("./response.js").StreamResponse;
package/dist/errors.js CHANGED
@@ -4,6 +4,7 @@ exports.AbortError = exports.FastlsDecodeError = exports.FastlsTimeoutError = ex
4
4
  exports.responseError = responseError;
5
5
  class FastlsError extends Error {
6
6
  name = "FastlsError";
7
+ code;
7
8
  }
8
9
  exports.FastlsError = FastlsError;
9
10
  class FastlsHTTPError extends FastlsError {
@@ -36,7 +37,9 @@ function responseError(info) {
36
37
  return new FastlsTimeoutError(message, (info.timeout_phase ?? "total"));
37
38
  if (info.error_code === "decode_error")
38
39
  return new FastlsDecodeError(message);
39
- return new FastlsError(message);
40
+ const error = new FastlsError(message);
41
+ error.code = info.error_code;
42
+ return error;
40
43
  }
41
44
  class AbortError extends FastlsError {
42
45
  name = "AbortError";
package/dist/fetch.d.ts CHANGED
@@ -1,5 +1,10 @@
1
1
  import { Session, type RequestOptions } from "./session.js";
2
+ import type { ProfileSpec, AddressPolicy } from "./profiles.js";
2
3
  export interface FetchOptions extends RequestInit {
4
+ profileSpec?: ProfileSpec;
5
+ httpVersion?: "auto" | "h3";
6
+ /** Only standalone fetch can create a new trusted policy owner. */
7
+ addressPolicy?: AddressPolicy;
3
8
  duplex?: "half";
4
9
  profile?: string;
5
10
  proxy?: string | null;
package/dist/fetch.js CHANGED
@@ -11,7 +11,9 @@ async function fetchWithSession(session, input, init = {}, owned = false) {
11
11
  if (init[key] !== undefined)
12
12
  throw new TypeError(`Fastls fetch does not support ${key}`);
13
13
  }
14
- const allowed = new Set(["method", "headers", "body", "redirect", "signal", "duplex", "profile", "proxy", "proxyCaFile", "caFile", "library", "timeoutMs", "maxRedirects"]);
14
+ const allowed = new Set(["method", "headers", "body", "redirect", "signal", "duplex", "profile", "profileSpec", "httpVersion", "addressPolicy", "proxy", "proxyCaFile", "caFile", "library", "timeoutMs", "maxRedirects"]);
15
+ if (!owned && init.addressPolicy !== undefined)
16
+ throw new TypeError("addressPolicy is a Session creation option");
15
17
  for (const key of Object.keys(init))
16
18
  if (!allowed.has(key))
17
19
  throw new TypeError(`Fastls fetch does not support ${key}`);
@@ -29,7 +31,7 @@ async function fetchWithSession(session, input, init = {}, owned = false) {
29
31
  body = node_stream_1.Readable.fromWeb(request.body);
30
32
  }
31
33
  const stream = await session.stream(request.method, request.url, {
32
- headers, body, profile: init.profile, proxy: init.proxy, proxyCaFile: init.proxyCaFile,
34
+ headers, body, profile: init.profile, profileSpec: init.profileSpec, httpVersion: init.httpVersion, proxy: init.proxy, proxyCaFile: init.proxyCaFile,
33
35
  timeoutMs: init.timeoutMs, maxRedirects: init.maxRedirects,
34
36
  allowRedirects: request.redirect === "follow", signal: request.signal,
35
37
  });
@@ -108,6 +110,7 @@ async function fetchWithSession(session, input, init = {}, owned = false) {
108
110
  /** Fetch-compatible entry point. No persistent cookie jar is created. */
109
111
  async function fetch(input, init = {}) {
110
112
  const options = { cookies: false, profile: init.profile, proxy: init.proxy,
113
+ profileSpec: init.profileSpec, httpVersion: init.httpVersion, addressPolicy: init.addressPolicy,
111
114
  proxyCaFile: init.proxyCaFile, caFile: init.caFile, library: init.library, timeoutMs: init.timeoutMs };
112
115
  return fetchWithSession(new session_js_1.Session(options), input, init, true);
113
116
  }
package/dist/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  export { Session } from "./session.js";
2
+ export { ProfileSpec } from "./profiles.js";
3
+ export type { AddressPolicy } from "./profiles.js";
2
4
  export { fetch } from "./fetch.js";
3
5
  export type { FetchOptions, FetchResponse } from "./fetch.js";
4
6
  export type { SessionOptions, RequestOptions, DuplexOptions, Stats } from "./session.js";
package/dist/index.js CHANGED
@@ -1,8 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FastlsTimeoutError = exports.FastlsDecodeError = exports.AbortError = exports.FastlsHTTPError = exports.FastlsError = exports.Cookies = exports.Headers = exports.StreamResponse = exports.Response = exports.SSEStream = exports.ServerSentEvent = exports.WebSocketClosed = exports.WebSocket = exports.DuplexExchange = exports.Session = void 0;
3
+ exports.FastlsTimeoutError = exports.FastlsDecodeError = exports.AbortError = exports.FastlsHTTPError = exports.FastlsError = exports.Cookies = exports.Headers = exports.StreamResponse = exports.Response = exports.SSEStream = exports.ServerSentEvent = exports.WebSocketClosed = exports.WebSocket = exports.DuplexExchange = exports.ProfileSpec = exports.Session = void 0;
4
4
  var session_js_1 = require("./session.js");
5
5
  Object.defineProperty(exports, "Session", { enumerable: true, get: function () { return session_js_1.Session; } });
6
+ var profiles_js_1 = require("./profiles.js");
7
+ Object.defineProperty(exports, "ProfileSpec", { enumerable: true, get: function () { return profiles_js_1.ProfileSpec; } });
6
8
  var fetch_js_1 = require("./fetch.js");
7
9
  Object.defineProperty(exports, "fetch", { enumerable: true, get: function () { return fetch_js_1.fetch; } });
8
10
  var duplex_js_1 = require("./duplex.js");
package/dist/index.mjs CHANGED
@@ -1,3 +1,3 @@
1
1
  import sdk from './index.js';
2
2
  export const fetch = sdk.fetch;
3
- export const { Session, Response, StreamResponse, DuplexExchange, WebSocket, WebSocketClosed, ServerSentEvent, SSEStream, Headers, Cookies, FastlsError, FastlsHTTPError, AbortError, FastlsDecodeError, FastlsTimeoutError } = sdk;
3
+ export const { Session, ProfileSpec, Response, StreamResponse, DuplexExchange, WebSocket, WebSocketClosed, ServerSentEvent, SSEStream, Headers, Cookies, FastlsError, FastlsHTTPError, AbortError, FastlsDecodeError, FastlsTimeoutError } = sdk;
package/dist/native.d.ts CHANGED
@@ -31,6 +31,7 @@ export interface NativeInfo {
31
31
  }
32
32
  export declare class Native {
33
33
  private functions;
34
+ readonly transportOptionsAvailable: boolean;
34
35
  readonly notificationsAvailable: boolean;
35
36
  readonly websocketAvailable: boolean;
36
37
  readonly path: string;
@@ -38,6 +39,7 @@ export declare class Native {
38
39
  private constructor();
39
40
  call<T = unknown>(name: string, ...args: unknown[]): T;
40
41
  start(input: unknown): bigint;
42
+ requireCapabilities(required: readonly string[]): void;
41
43
  wsStart(input: unknown): bigint;
42
44
  wsSend(handle: bigint, opcode: number, data: Uint8Array): number;
43
45
  wsRead(handle: bigint, buffer: Buffer): {
package/dist/native.js CHANGED
@@ -62,6 +62,7 @@ function pinLibrary(path) {
62
62
  }
63
63
  class Native {
64
64
  functions = {};
65
+ transportOptionsAvailable = false;
65
66
  notificationsAvailable;
66
67
  websocketAvailable;
67
68
  path;
@@ -103,6 +104,13 @@ class Native {
103
104
  this.functions.read = lib.func("int fastls_read(uint64_t handle, _Out_ void *buffer, size_t capacity, _Out_ size_t *length)");
104
105
  this.functions.write = lib.func("int fastls_write(uint64_t handle, const void *buffer, size_t length, int end)");
105
106
  const features = new Set(this.call("features"));
107
+ if (features.has("transport_options_v1")) {
108
+ try {
109
+ this.functions.transport_create_with_options = lib.func("void *fastls_transport_create_with_options(void *json, size_t length)");
110
+ this.transportOptionsAvailable = true;
111
+ }
112
+ catch { /* Optional symbol: old default transport stays loadable. */ }
113
+ }
106
114
  if (features.has("transport_context_v1")) {
107
115
  this.functions.transport_create = lib.func("void *fastls_transport_create(void)");
108
116
  }
@@ -149,19 +157,30 @@ class Native {
149
157
  // Starts, sessions and notification batches contain uint64 handles. Catalogs, headers
150
158
  // and info metadata use strings and bounded numeric fields; avoid the
151
159
  // allocating JS numeric parser on every poll and catalog lookup.
152
- envelope = (name === "start" || name === "ws_start" || name === "session_create" || name === "session_events" ? parseNativeJSON(json) : JSON.parse(json));
160
+ envelope = (name === "start" || name === "ws_start" || name === "session_create" || name === "transport_create" || name === "transport_create_with_options" || name === "session_events" ? parseNativeJSON(json) : JSON.parse(json));
153
161
  }
154
162
  finally {
155
163
  this.functions.free(pointer);
156
164
  }
157
- if (!envelope?.ok)
158
- throw new errors_js_1.FastlsError(envelope?.error ?? "invalid native result");
165
+ if (!envelope?.ok) {
166
+ const error = new errors_js_1.FastlsError(envelope?.error ?? "invalid native result");
167
+ error.code = envelope?.error_code;
168
+ throw error;
169
+ }
159
170
  return envelope.result;
160
171
  }
161
172
  start(input) {
162
173
  const raw = encodeNativeJSON(input);
163
174
  return asHandle(this.call("start", raw, raw.length));
164
175
  }
176
+ requireCapabilities(required) {
177
+ const features = new Set(this.call("features"));
178
+ if (!this.transportOptionsAvailable || required.some(name => !features.has(name))) {
179
+ const error = new errors_js_1.FastlsError("native transport capability is unavailable");
180
+ error.code = "unsupported_capability";
181
+ throw error;
182
+ }
183
+ }
165
184
  wsStart(input) {
166
185
  if (!this.websocketAvailable)
167
186
  throw new errors_js_1.FastlsError("WebSocket requires a newer Fastls native library");
@@ -0,0 +1,14 @@
1
+ /** Trusted immutable session policy. Keys follow the native versioned schema. */
2
+ export interface AddressPolicy {
3
+ readonly allow_schemes?: readonly ("http" | "https")[];
4
+ readonly allow_hosts?: readonly string[];
5
+ readonly allow_ports?: readonly number[];
6
+ readonly allow_cidrs?: readonly string[];
7
+ readonly deny_cidrs?: readonly string[];
8
+ }
9
+ /** Explicit snapshot of a complete schema-v1 profile, validated by the core. */
10
+ export declare class ProfileSpec {
11
+ private readonly snapshot;
12
+ constructor(value: Readonly<Record<string, unknown>>);
13
+ toJSON(): Record<string, unknown>;
14
+ }
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ProfileSpec = void 0;
4
+ /** Explicit snapshot of a complete schema-v1 profile, validated by the core. */
5
+ class ProfileSpec {
6
+ snapshot;
7
+ constructor(value) {
8
+ if (!value || typeof value !== "object" || Array.isArray(value))
9
+ throw new TypeError("ProfileSpec requires an object");
10
+ this.snapshot = JSON.stringify(value);
11
+ if (Buffer.byteLength(this.snapshot) > 256 * 1024)
12
+ throw new RangeError("profileSpec exceeds 256KiB");
13
+ }
14
+ toJSON() { return JSON.parse(this.snapshot); }
15
+ }
16
+ exports.ProfileSpec = ProfileSpec;
package/dist/session.d.ts CHANGED
@@ -7,7 +7,10 @@ import { SSEStream, type SSEOptions } from "./sse.js";
7
7
  import { type QueryParams, type JSONValue, type FormFields, type MultipartFiles } from "./request-options.js";
8
8
  import { WebSocket, type WebSocketOptions } from "./websocket.js";
9
9
  import { type FetchOptions, type FetchResponse } from "./fetch.js";
10
+ import { ProfileSpec, type AddressPolicy } from "./profiles.js";
10
11
  export interface RequestOptions {
12
+ profileSpec?: ProfileSpec;
13
+ httpVersion?: "auto" | "h3";
11
14
  /** Undefined inherits the session proxy; null or an empty string connects directly. */
12
15
  proxy?: string | null;
13
16
  /** Undefined inherits proxy trust; null uses system roots. Only HTTPS proxies use this file. */
@@ -32,6 +35,9 @@ export interface RequestOptions {
32
35
  }
33
36
  export type DuplexOptions = Omit<RequestOptions, "body" | "duplex" | "json" | "form" | "files">;
34
37
  export interface SessionOptions {
38
+ addressPolicy?: AddressPolicy;
39
+ profileSpec?: ProfileSpec;
40
+ httpVersion?: "auto" | "h3";
35
41
  /** HTTP/HTTPS CONNECT, socks5 (local DNS), or socks5h (proxy DNS) URL. */
36
42
  proxy?: string | null;
37
43
  /** Private roots for proxy TLS, independent of the target's caFile. */
package/dist/session.js CHANGED
@@ -15,6 +15,7 @@ const notifications_js_1 = require("./notifications.js");
15
15
  const request_options_js_1 = require("./request-options.js");
16
16
  const websocket_js_1 = require("./websocket.js");
17
17
  const fetch_js_1 = require("./fetch.js");
18
+ const profiles_js_1 = require("./profiles.js");
18
19
  function decoding(decode, maximum) {
19
20
  if (typeof decode !== "boolean")
20
21
  throw new TypeError("decodeContent must be a boolean");
@@ -86,7 +87,15 @@ class Session {
86
87
  const notifications = options.notifications === undefined ? "auto" : options.notifications;
87
88
  if (!["auto", "required", "poll"].includes(notifications))
88
89
  throw new TypeError("notifications must be auto, required or poll");
89
- this.profile = options.profile ?? "chrome/156/windows";
90
+ if (options.profile !== undefined && typeof options.profile !== "string")
91
+ throw new TypeError("profile must be a selector string");
92
+ if (options.profileSpec !== undefined && !(options.profileSpec instanceof profiles_js_1.ProfileSpec))
93
+ throw new TypeError("profileSpec must be a ProfileSpec");
94
+ if (options.profile !== undefined && options.profileSpec !== undefined)
95
+ throw new TypeError("profile and profileSpec are mutually exclusive");
96
+ if (options.httpVersion !== undefined && !["auto", "h3"].includes(options.httpVersion))
97
+ throw new TypeError("httpVersion must be auto or h3");
98
+ this.profile = options.profile ?? (options.profileSpec !== undefined || options.httpVersion === "h3" ? "" : "chrome/156/windows");
90
99
  this.timeouts = (0, timeout_js_1.timeoutFields)(options.timeoutMs ?? 30000);
91
100
  this.decodeContent = options.decodeContent ?? true;
92
101
  this.maxDecodedBytes = options.maxDecodedBytes ?? 256 * 1024 * 1024;
@@ -104,7 +113,36 @@ class Session {
104
113
  this.notificationMode = notifications !== "poll" && supported ? "async-events" : "poll";
105
114
  if (!this.native.call("features").includes("transport_context_v1"))
106
115
  throw new errors_js_1.FastlsError("SDK-owned sessions require the Fastls 0.1.4 native library or newer");
107
- this.handle = (0, native_js_1.asHandle)(this.native.call("transport_create"));
116
+ const configuration = { schema_version: 1 };
117
+ const required = [];
118
+ if (options.addressPolicy !== undefined) {
119
+ if (!options.addressPolicy || typeof options.addressPolicy !== "object" || Array.isArray(options.addressPolicy))
120
+ throw new TypeError("addressPolicy must be an object");
121
+ configuration.address_policy = options.addressPolicy;
122
+ required.push("transport_address_policy_v1");
123
+ }
124
+ if (options.profileSpec !== undefined) {
125
+ configuration.profile_spec = options.profileSpec.toJSON();
126
+ required.push("profile_spec_v1");
127
+ }
128
+ if (options.httpVersion === "h3") {
129
+ configuration.http_version = "h3";
130
+ required.push("http3_v1");
131
+ }
132
+ if (required.length) {
133
+ this.native.requireCapabilities(required);
134
+ if (options.profile !== undefined)
135
+ configuration.profile = options.profile;
136
+ if (this.proxy && (options.addressPolicy !== undefined || options.httpVersion === "h3")) {
137
+ const error = new errors_js_1.FastlsError("unsupported transport combination");
138
+ error.code = "unsupported_capability";
139
+ throw error;
140
+ }
141
+ const raw = (0, native_js_1.encodeNativeJSON)(configuration);
142
+ this.handle = (0, native_js_1.asHandle)(this.native.call("transport_create_with_options", raw, raw.length));
143
+ }
144
+ else
145
+ this.handle = (0, native_js_1.asHandle)(this.native.call("transport_create"));
108
146
  this.cookies = new cookies_js_1.Cookies(() => this.ensureOpen(), options.cookies !== false);
109
147
  }
110
148
  ensureOpen() {
@@ -157,6 +195,8 @@ class Session {
157
195
  }
158
196
  begin(method, url, options, manual = false, completeHeaders = false) {
159
197
  this.ensureOpen();
198
+ if ("addressPolicy" in options)
199
+ throw new TypeError("addressPolicy is a Session creation option");
160
200
  if (options.signal !== undefined && !(options.signal instanceof AbortSignal)) {
161
201
  throw new TypeError("signal must be an AbortSignal");
162
202
  }
@@ -215,9 +255,24 @@ class Session {
215
255
  trailers[name] = [...values];
216
256
  }
217
257
  }
258
+ if (options.profileSpec !== undefined && !(options.profileSpec instanceof profiles_js_1.ProfileSpec))
259
+ throw new TypeError("profileSpec must be a ProfileSpec");
260
+ if (options.profileSpec !== undefined && options.profile !== undefined)
261
+ throw new TypeError("profile and profileSpec are mutually exclusive");
262
+ const required = [];
263
+ if (options.profileSpec !== undefined)
264
+ required.push("profile_spec_v1");
265
+ if (options.httpVersion !== undefined) {
266
+ if (!["auto", "h3"].includes(options.httpVersion))
267
+ throw new TypeError("httpVersion must be auto or h3");
268
+ required.push("http3_v1");
269
+ }
270
+ if (required.length)
271
+ this.native.requireCapabilities(required);
218
272
  const handle = this.native.start({
219
273
  session: this.handle, url, method, headers, trailers, proxy: proxy || undefined, proxy_ca_pem: proxyCaPem,
220
- profile: options.profile ?? this.profile,
274
+ profile: options.profile ?? (options.profileSpec !== undefined || options.httpVersion === "h3" ? "" : this.profile),
275
+ profile_spec: options.profileSpec?.toJSON(), http_version: options.httpVersion,
221
276
  ...timeouts, decode_content: decodeContent, max_decoded_bytes: maxDecodedBytes,
222
277
  disable_redirect: true,
223
278
  ca_pem: this.caPem,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fastls/fastls",
3
- "version": "0.10.1",
3
+ "version": "0.11.0",
4
4
  "description": "Node.js bindings for the Fastls C ABI",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -35,10 +35,10 @@
35
35
  "registry": "https://registry.npmjs.org/"
36
36
  },
37
37
  "optionalDependencies": {
38
- "@fastls/fastls-win32-x64": "0.10.1",
39
- "@fastls/fastls-linux-x64-glibc": "0.10.1",
40
- "@fastls/fastls-linux-arm64-glibc": "0.10.1",
41
- "@fastls/fastls-darwin-x64": "0.10.1",
42
- "@fastls/fastls-darwin-arm64": "0.10.1"
38
+ "@fastls/fastls-win32-x64": "0.11.0",
39
+ "@fastls/fastls-linux-x64-glibc": "0.11.0",
40
+ "@fastls/fastls-linux-arm64-glibc": "0.11.0",
41
+ "@fastls/fastls-darwin-x64": "0.11.0",
42
+ "@fastls/fastls-darwin-arm64": "0.11.0"
43
43
  }
44
44
  }