@computesdk/steel 0.2.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 computesdk
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/index.cjs ADDED
@@ -0,0 +1,247 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ steel: () => steel
34
+ });
35
+ module.exports = __toCommonJS(index_exports);
36
+ var import_steel_sdk = __toESM(require("steel-sdk"), 1);
37
+ var import_provider = require("@computesdk/provider");
38
+ function resolveConfig(config) {
39
+ const apiKey = config.apiKey || typeof process !== "undefined" && process.env?.STEEL_API_KEY || "";
40
+ if (!apiKey) {
41
+ throw new Error(
42
+ `Missing Steel API key. Provide 'apiKey' in config or set STEEL_API_KEY environment variable. Get your API key from https://app.steel.dev/settings/api-keys`
43
+ );
44
+ }
45
+ return { apiKey, baseUrl: config.baseUrl };
46
+ }
47
+ function createClient(config) {
48
+ const { apiKey, baseUrl } = resolveConfig(config);
49
+ return new import_steel_sdk.default({ steelAPIKey: apiKey, baseURL: baseUrl ?? void 0 });
50
+ }
51
+ function applyProxy(params, proxy) {
52
+ if (proxy.type === "custom" && proxy.server) {
53
+ const url = new URL(proxy.server);
54
+ if (proxy.username) url.username = proxy.username;
55
+ if (proxy.password) url.password = proxy.password;
56
+ params.proxyUrl = url.toString();
57
+ return;
58
+ }
59
+ if (proxy.geolocation?.country || proxy.geolocation?.state || proxy.geolocation?.city) {
60
+ params.useProxy = {
61
+ geolocation: {
62
+ country: proxy.geolocation.country,
63
+ ...proxy.geolocation.state ? { state: proxy.geolocation.state } : {},
64
+ ...proxy.geolocation.city ? { city: proxy.geolocation.city } : {}
65
+ }
66
+ };
67
+ return;
68
+ }
69
+ params.useProxy = true;
70
+ }
71
+ var warnedUnsupported = /* @__PURE__ */ new Set();
72
+ function warnOnce(field, reason) {
73
+ if (warnedUnsupported.has(field)) return;
74
+ warnedUnsupported.add(field);
75
+ console.warn(`[@computesdk/steel] '${field}' is ignored: ${reason}`);
76
+ }
77
+ function mapSessionOptions(options) {
78
+ if (!options) return {};
79
+ if (options.keepAlive === true) {
80
+ warnOnce("keepAlive", "Steel has no keep-alive flag; use a long `timeout` instead.");
81
+ }
82
+ if (options.recording === false) {
83
+ warnOnce("recording", "Steel always records sessions and cannot be disabled per session.");
84
+ }
85
+ if (options.logging === false) {
86
+ warnOnce("logging", "Steel always captures console logs and cannot be disabled per session.");
87
+ }
88
+ if (options.userMetadata && Object.keys(options.userMetadata).length > 0) {
89
+ warnOnce("userMetadata", "Steel does not support user metadata on sessions.");
90
+ }
91
+ const params = {};
92
+ if (options.viewport) params.dimensions = { width: options.viewport.width, height: options.viewport.height };
93
+ if (options.extensionIds && options.extensionIds.length > 0) params.extensionIds = options.extensionIds;
94
+ if (options.region) params.region = options.region;
95
+ if (options.timeout !== void 0) params.timeout = options.timeout * 1e3;
96
+ if (options.profileId) {
97
+ params.profileId = options.profileId;
98
+ params.persistProfile = true;
99
+ }
100
+ if (options.stealth) {
101
+ params.stealthConfig = { humanizeInteractions: true };
102
+ }
103
+ if (options.proxies === true) {
104
+ params.useProxy = true;
105
+ } else if (Array.isArray(options.proxies) && options.proxies.length > 0) {
106
+ applyProxy(params, options.proxies[0]);
107
+ }
108
+ return params;
109
+ }
110
+ function mapStatus(status) {
111
+ switch (status) {
112
+ case "live":
113
+ return "running";
114
+ case "released":
115
+ return "completed";
116
+ case "failed":
117
+ return "failed";
118
+ default:
119
+ return "created";
120
+ }
121
+ }
122
+ function buildConnectUrl(websocketUrl, apiKey, sessionId) {
123
+ const url = new URL(websocketUrl);
124
+ if (!url.searchParams.has("apiKey")) url.searchParams.set("apiKey", apiKey);
125
+ if (!url.searchParams.has("sessionId")) url.searchParams.set("sessionId", sessionId);
126
+ return url.toString();
127
+ }
128
+ var steel = (0, import_provider.defineBrowserProvider)({
129
+ name: "steel",
130
+ methods: {
131
+ // ─── Session Lifecycle ─────────────────────────────────────────────
132
+ session: {
133
+ create: async (config, options) => {
134
+ const { apiKey } = resolveConfig(config);
135
+ const client = createClient(config);
136
+ const session = await client.sessions.create(mapSessionOptions(options));
137
+ return {
138
+ session,
139
+ sessionId: session.id,
140
+ connectUrl: buildConnectUrl(session.websocketUrl, apiKey, session.id),
141
+ status: mapStatus(session.status)
142
+ };
143
+ },
144
+ getById: async (config, sessionId) => {
145
+ const { apiKey } = resolveConfig(config);
146
+ const client = createClient(config);
147
+ try {
148
+ const session = await client.sessions.retrieve(sessionId);
149
+ return {
150
+ session,
151
+ sessionId: session.id,
152
+ connectUrl: buildConnectUrl(session.websocketUrl, apiKey, session.id),
153
+ status: mapStatus(session.status)
154
+ };
155
+ } catch {
156
+ return null;
157
+ }
158
+ },
159
+ list: async (config) => {
160
+ const client = createClient(config);
161
+ const page = await client.sessions.list();
162
+ const sessions = page.sessions ?? [];
163
+ return sessions.map((s) => ({
164
+ session: s,
165
+ sessionId: s.id,
166
+ status: mapStatus(s.status)
167
+ }));
168
+ },
169
+ destroy: async (config, sessionId) => {
170
+ const client = createClient(config);
171
+ await client.sessions.release(sessionId);
172
+ },
173
+ getConnectUrl: async (config, sessionId) => {
174
+ const { apiKey } = resolveConfig(config);
175
+ const client = createClient(config);
176
+ const session = await client.sessions.retrieve(sessionId);
177
+ return buildConnectUrl(session.websocketUrl, apiKey, session.id);
178
+ }
179
+ },
180
+ // ─── Extensions ────────────────────────────────────────────────────
181
+ extension: {
182
+ create: async (config, options) => {
183
+ const client = createClient(config);
184
+ const buf = typeof options.file === "string" ? Buffer.from(options.file, "utf-8") : Buffer.from(options.file);
185
+ const file = await (0, import_steel_sdk.toFile)(buf, options.name ?? "extension.zip");
186
+ const result = await client.extensions.upload({ file });
187
+ return {
188
+ extensionId: result.id,
189
+ name: result.name
190
+ };
191
+ },
192
+ get: async (config, extensionId) => {
193
+ const client = createClient(config);
194
+ const response = await client.extensions.list();
195
+ const found = response.extensions.find((e) => e.id === extensionId);
196
+ if (!found) return null;
197
+ return { extensionId: found.id, name: found.name };
198
+ },
199
+ delete: async (config, extensionId) => {
200
+ const client = createClient(config);
201
+ await client.extensions.delete(extensionId);
202
+ }
203
+ },
204
+ // ─── Logs (RRWeb session events) ───────────────────────────────────
205
+ // Steel doesn't expose a console-log endpoint — the closest equivalent
206
+ // is the RRWeb event stream from `sessions.events`. We surface those as
207
+ // log entries so callers have a uniform shape.
208
+ logs: {
209
+ list: async (config, sessionId) => {
210
+ const client = createClient(config);
211
+ const events = await client.sessions.events(sessionId);
212
+ return events.map((event) => {
213
+ const ts = typeof event.timestamp === "number" ? event.timestamp : Date.now();
214
+ return {
215
+ timestamp: new Date(ts),
216
+ level: "info",
217
+ message: typeof event.type === "string" || typeof event.type === "number" ? `event:${String(event.type)}` : "event",
218
+ data: event
219
+ };
220
+ });
221
+ }
222
+ },
223
+ // ─── Recordings ────────────────────────────────────────────────────
224
+ recording: {
225
+ get: async (config, sessionId) => {
226
+ const client = createClient(config);
227
+ try {
228
+ const events = await client.sessions.events(sessionId);
229
+ if (!events || Array.isArray(events) && events.length === 0) return null;
230
+ return {
231
+ recordingId: sessionId,
232
+ sessionId,
233
+ format: "rrweb",
234
+ data: events
235
+ };
236
+ } catch {
237
+ return null;
238
+ }
239
+ }
240
+ }
241
+ }
242
+ });
243
+ // Annotate the CommonJS export names for ESM import in node:
244
+ 0 && (module.exports = {
245
+ steel
246
+ });
247
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Steel Browser Provider - Factory-based Implementation\n *\n * Cloud browser sessions with stealth mode, proxies, persistent profiles,\n * extensions, and session recordings via the Steel platform.\n */\n\nimport Steel, { toFile } from 'steel-sdk';\nimport type {\n Session as SteelSession,\n SessionCreateParams,\n Sessionslist,\n} from 'steel-sdk/resources/sessions';\nimport { defineBrowserProvider } from '@computesdk/provider';\n\nimport type {\n BrowserSession,\n CreateBrowserSessionOptions,\n BrowserExtension,\n BrowserLog,\n BrowserRecording,\n ProxyConfig,\n} from '@computesdk/provider';\n\n/**\n * Steel-specific configuration options\n */\nexport interface SteelConfig {\n /** Steel API key — falls back to STEEL_API_KEY env var */\n apiKey?: string;\n /** Optional API base URL override (defaults to https://api.steel.dev) */\n baseUrl?: string;\n}\n\n/**\n * The native Steel session object returned by their SDK\n */\ntype NativeSteelSession = SteelSession;\n\n/**\n * Resolve config values from explicit config or environment variables\n */\nfunction resolveConfig(config: SteelConfig) {\n const apiKey = config.apiKey || (typeof process !== 'undefined' && process.env?.STEEL_API_KEY) || '';\n\n if (!apiKey) {\n throw new Error(\n `Missing Steel API key. Provide 'apiKey' in config or set STEEL_API_KEY environment variable. ` +\n `Get your API key from https://app.steel.dev/settings/api-keys`\n );\n }\n\n return { apiKey, baseUrl: config.baseUrl };\n}\n\n/**\n * Create a Steel SDK client from config\n */\nfunction createClient(config: SteelConfig): Steel {\n const { apiKey, baseUrl } = resolveConfig(config);\n return new Steel({ steelAPIKey: apiKey, baseURL: baseUrl ?? undefined });\n}\n\n/**\n * Steel's `useProxy` field accepts either a boolean, a geolocation object,\n * or a custom server. Apply a single ProxyConfig onto the create-session params.\n * Steel only supports one proxy per session, so when multiple are passed\n * we honor the first one.\n */\nfunction applyProxy(params: SessionCreateParams, proxy: ProxyConfig) {\n if (proxy.type === 'custom' && proxy.server) {\n // Steel uses a separate proxyUrl field for custom proxies; this overrides\n // useProxy and disables Steel-provided proxies in favor of the user's.\n const url = new URL(proxy.server);\n if (proxy.username) url.username = proxy.username;\n if (proxy.password) url.password = proxy.password;\n params.proxyUrl = url.toString();\n return;\n }\n\n if (proxy.geolocation?.country || proxy.geolocation?.state || proxy.geolocation?.city) {\n params.useProxy = {\n geolocation: {\n country: proxy.geolocation.country as any,\n ...(proxy.geolocation.state ? { state: proxy.geolocation.state as any } : {}),\n ...(proxy.geolocation.city ? { city: proxy.geolocation.city as any } : {}),\n },\n };\n return;\n }\n\n params.useProxy = true;\n}\n\nconst warnedUnsupported = new Set<string>();\nfunction warnOnce(field: string, reason: string) {\n if (warnedUnsupported.has(field)) return;\n warnedUnsupported.add(field);\n console.warn(`[@computesdk/steel] '${field}' is ignored: ${reason}`);\n}\n\n/**\n * Map ComputeSDK session options to Steel create-session params.\n *\n * The following CreateBrowserSessionOptions fields are intentionally not\n * forwarded because Steel's API has no equivalent knob:\n * - `recording`: Steel always records sessions; fetch via the recording manager.\n * - `logging`: Steel always captures console logs; fetch via the logs manager.\n * - `keepAlive`: Steel sessions run until `timeout` elapses or are released\n * explicitly; there is no separate keep-alive flag. Use a long\n * `timeout` to approximate the behavior.\n * - `userMetadata`: Steel does not support arbitrary user metadata on sessions.\n *\n * If a caller passes a non-default value for any of these, we warn once per\n * field so the silent drop is at least visible in logs.\n */\nfunction mapSessionOptions(options?: CreateBrowserSessionOptions): SessionCreateParams {\n if (!options) return {};\n\n if (options.keepAlive === true) {\n warnOnce('keepAlive', 'Steel has no keep-alive flag; use a long `timeout` instead.');\n }\n if (options.recording === false) {\n warnOnce('recording', 'Steel always records sessions and cannot be disabled per session.');\n }\n if (options.logging === false) {\n warnOnce('logging', 'Steel always captures console logs and cannot be disabled per session.');\n }\n if (options.userMetadata && Object.keys(options.userMetadata).length > 0) {\n warnOnce('userMetadata', 'Steel does not support user metadata on sessions.');\n }\n\n const params: SessionCreateParams = {};\n\n if (options.viewport) params.dimensions = { width: options.viewport.width, height: options.viewport.height };\n if (options.extensionIds && options.extensionIds.length > 0) params.extensionIds = options.extensionIds;\n if (options.region) params.region = options.region as SessionCreateParams['region'];\n // Steel session timeout is in milliseconds (default 5 minutes)\n if (options.timeout !== undefined) params.timeout = options.timeout * 1000;\n if (options.profileId) {\n params.profileId = options.profileId;\n params.persistProfile = true;\n }\n\n // Stealth wraps a few related flags. We toggle humanizeInteractions as the\n // most user-visible knob; advanced users can override via options pass-through.\n if (options.stealth) {\n params.stealthConfig = { humanizeInteractions: true };\n }\n\n if (options.proxies === true) {\n params.useProxy = true;\n } else if (Array.isArray(options.proxies) && options.proxies.length > 0) {\n applyProxy(params, options.proxies[0]!);\n }\n\n return params;\n}\n\n/**\n * Map Steel's session status onto our standard set\n */\nfunction mapStatus(status: string | undefined): BrowserSession['status'] {\n switch (status) {\n case 'live': return 'running';\n case 'released': return 'completed';\n case 'failed': return 'failed';\n default: return 'created';\n }\n}\n\n/**\n * Append the apiKey (and sessionId if missing) to Steel's websocketUrl so\n * callers can pass it directly to playwright/puppeteer. Steel returns\n * `wss://connect.steel.dev?sessionId=...` and requires `apiKey` to be\n * appended for authentication.\n *\n * SECURITY: the returned URL embeds the account-wide Steel API key as a\n * query parameter. Treat it as a secret — do not log it, persist it, or\n * forward it to systems that retain URLs (access logs, error trackers,\n * tracing). Anyone with this URL has full access to the Steel account, not\n * just this session.\n */\nfunction buildConnectUrl(websocketUrl: string, apiKey: string, sessionId: string): string {\n const url = new URL(websocketUrl);\n if (!url.searchParams.has('apiKey')) url.searchParams.set('apiKey', apiKey);\n if (!url.searchParams.has('sessionId')) url.searchParams.set('sessionId', sessionId);\n return url.toString();\n}\n\n/**\n * Create a Steel browser provider instance\n *\n * SECURITY NOTE: Steel's CDP endpoint authenticates via an `apiKey` query\n * parameter on the WebSocket URL. As a result, every `connectUrl` returned\n * by this provider (from `session.create()` and `session.getById()`, and\n * `provider.getConnectUrl()`) embeds the account-wide Steel API key. Treat\n * `connectUrl` as a secret: do not log, persist, or forward it to systems\n * that retain URLs. `session.list()` deliberately omits `connectUrl` to\n * avoid amplifying this leak surface — call `provider.getConnectUrl(id)` on\n * demand instead.\n *\n * @example\n * ```ts\n * import { steel } from '@computesdk/steel';\n * import { chromium } from 'playwright-core';\n *\n * const st = steel({ apiKey: process.env.STEEL_API_KEY });\n *\n * // Create a stealth browser session\n * const session = await st.session.create({ stealth: true, proxies: true });\n *\n * // Connect with Playwright (do not log session.connectUrl — it contains the API key)\n * const browser = await chromium.connectOverCDP(session.connectUrl);\n * const page = browser.contexts()[0].pages()[0];\n * await page.goto('https://example.com');\n *\n * // Release the session\n * await session.destroy();\n * ```\n */\nexport const steel = defineBrowserProvider<NativeSteelSession, SteelConfig>({\n name: 'steel',\n methods: {\n // ─── Session Lifecycle ─────────────────────────────────────────────\n session: {\n create: async (config, options) => {\n const { apiKey } = resolveConfig(config);\n const client = createClient(config);\n const session = await client.sessions.create(mapSessionOptions(options));\n return {\n session,\n sessionId: session.id,\n connectUrl: buildConnectUrl(session.websocketUrl, apiKey, session.id),\n status: mapStatus(session.status),\n };\n },\n\n getById: async (config, sessionId) => {\n const { apiKey } = resolveConfig(config);\n const client = createClient(config);\n try {\n const session = await client.sessions.retrieve(sessionId);\n return {\n session,\n sessionId: session.id,\n connectUrl: buildConnectUrl(session.websocketUrl, apiKey, session.id),\n status: mapStatus(session.status),\n };\n } catch {\n return null;\n }\n },\n\n list: async (config) => {\n const client = createClient(config);\n const page = await client.sessions.list();\n const sessions = (page as unknown as { sessions: Sessionslist['sessions'] }).sessions ?? [];\n // We deliberately omit connectUrl from list entries: the URL would\n // need to embed the account-wide Steel API key, and emitting it once\n // per session inflates the leak surface (logs, traces, etc.). Callers\n // that need a connectable URL should use getConnectUrl(sessionId).\n return sessions.map((s) => ({\n session: s as NativeSteelSession,\n sessionId: s.id,\n status: mapStatus(s.status),\n }));\n },\n\n destroy: async (config, sessionId) => {\n const client = createClient(config);\n await client.sessions.release(sessionId);\n },\n\n getConnectUrl: async (config, sessionId) => {\n const { apiKey } = resolveConfig(config);\n const client = createClient(config);\n const session = await client.sessions.retrieve(sessionId);\n return buildConnectUrl(session.websocketUrl, apiKey, session.id);\n },\n },\n\n // ─── Extensions ────────────────────────────────────────────────────\n extension: {\n create: async (config, options) => {\n const client = createClient(config);\n const buf = typeof options.file === 'string'\n ? Buffer.from(options.file, 'utf-8')\n : Buffer.from(options.file);\n const file = await toFile(buf, options.name ?? 'extension.zip');\n const result = await client.extensions.upload({ file });\n return {\n extensionId: result.id,\n name: result.name,\n } satisfies BrowserExtension;\n },\n\n get: async (config, extensionId) => {\n const client = createClient(config);\n // Steel exposes only a list endpoint for extension lookup — filter the result.\n const response = await client.extensions.list();\n const found = response.extensions.find((e) => e.id === extensionId);\n if (!found) return null;\n return { extensionId: found.id, name: found.name } satisfies BrowserExtension;\n },\n\n delete: async (config, extensionId) => {\n const client = createClient(config);\n await client.extensions.delete(extensionId);\n },\n },\n\n // ─── Logs (RRWeb session events) ───────────────────────────────────\n // Steel doesn't expose a console-log endpoint — the closest equivalent\n // is the RRWeb event stream from `sessions.events`. We surface those as\n // log entries so callers have a uniform shape.\n logs: {\n list: async (config, sessionId) => {\n const client = createClient(config);\n const events = await client.sessions.events(sessionId);\n return (events as Array<Record<string, unknown>>).map((event) => {\n const ts = typeof event.timestamp === 'number' ? event.timestamp : Date.now();\n return {\n timestamp: new Date(ts),\n level: 'info' as BrowserLog['level'],\n message: typeof event.type === 'string' || typeof event.type === 'number'\n ? `event:${String(event.type)}`\n : 'event',\n data: event,\n } satisfies BrowserLog;\n });\n },\n },\n\n // ─── Recordings ────────────────────────────────────────────────────\n recording: {\n get: async (config, sessionId) => {\n const client = createClient(config);\n try {\n const events = await client.sessions.events(sessionId);\n if (!events || (Array.isArray(events) && events.length === 0)) return null;\n return {\n recordingId: sessionId,\n sessionId,\n format: 'rrweb',\n data: events,\n } satisfies BrowserRecording;\n } catch {\n return null;\n }\n },\n },\n },\n});\n"],"mappings":"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","names":["Steel"]}
@@ -0,0 +1,46 @@
1
+ import * as _computesdk_provider from '@computesdk/provider';
2
+ import Steel from 'steel-sdk';
3
+
4
+ /**
5
+ * Steel-specific configuration options
6
+ */
7
+ interface SteelConfig {
8
+ /** Steel API key — falls back to STEEL_API_KEY env var */
9
+ apiKey?: string;
10
+ /** Optional API base URL override (defaults to https://api.steel.dev) */
11
+ baseUrl?: string;
12
+ }
13
+ /**
14
+ * Create a Steel browser provider instance
15
+ *
16
+ * SECURITY NOTE: Steel's CDP endpoint authenticates via an `apiKey` query
17
+ * parameter on the WebSocket URL. As a result, every `connectUrl` returned
18
+ * by this provider (from `session.create()` and `session.getById()`, and
19
+ * `provider.getConnectUrl()`) embeds the account-wide Steel API key. Treat
20
+ * `connectUrl` as a secret: do not log, persist, or forward it to systems
21
+ * that retain URLs. `session.list()` deliberately omits `connectUrl` to
22
+ * avoid amplifying this leak surface — call `provider.getConnectUrl(id)` on
23
+ * demand instead.
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * import { steel } from '@computesdk/steel';
28
+ * import { chromium } from 'playwright-core';
29
+ *
30
+ * const st = steel({ apiKey: process.env.STEEL_API_KEY });
31
+ *
32
+ * // Create a stealth browser session
33
+ * const session = await st.session.create({ stealth: true, proxies: true });
34
+ *
35
+ * // Connect with Playwright (do not log session.connectUrl — it contains the API key)
36
+ * const browser = await chromium.connectOverCDP(session.connectUrl);
37
+ * const page = browser.contexts()[0].pages()[0];
38
+ * await page.goto('https://example.com');
39
+ *
40
+ * // Release the session
41
+ * await session.destroy();
42
+ * ```
43
+ */
44
+ declare const steel: (config: SteelConfig) => _computesdk_provider.BrowserProvider<Steel.Sessions.Session>;
45
+
46
+ export { type SteelConfig, steel };
@@ -0,0 +1,46 @@
1
+ import * as _computesdk_provider from '@computesdk/provider';
2
+ import Steel from 'steel-sdk';
3
+
4
+ /**
5
+ * Steel-specific configuration options
6
+ */
7
+ interface SteelConfig {
8
+ /** Steel API key — falls back to STEEL_API_KEY env var */
9
+ apiKey?: string;
10
+ /** Optional API base URL override (defaults to https://api.steel.dev) */
11
+ baseUrl?: string;
12
+ }
13
+ /**
14
+ * Create a Steel browser provider instance
15
+ *
16
+ * SECURITY NOTE: Steel's CDP endpoint authenticates via an `apiKey` query
17
+ * parameter on the WebSocket URL. As a result, every `connectUrl` returned
18
+ * by this provider (from `session.create()` and `session.getById()`, and
19
+ * `provider.getConnectUrl()`) embeds the account-wide Steel API key. Treat
20
+ * `connectUrl` as a secret: do not log, persist, or forward it to systems
21
+ * that retain URLs. `session.list()` deliberately omits `connectUrl` to
22
+ * avoid amplifying this leak surface — call `provider.getConnectUrl(id)` on
23
+ * demand instead.
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * import { steel } from '@computesdk/steel';
28
+ * import { chromium } from 'playwright-core';
29
+ *
30
+ * const st = steel({ apiKey: process.env.STEEL_API_KEY });
31
+ *
32
+ * // Create a stealth browser session
33
+ * const session = await st.session.create({ stealth: true, proxies: true });
34
+ *
35
+ * // Connect with Playwright (do not log session.connectUrl — it contains the API key)
36
+ * const browser = await chromium.connectOverCDP(session.connectUrl);
37
+ * const page = browser.contexts()[0].pages()[0];
38
+ * await page.goto('https://example.com');
39
+ *
40
+ * // Release the session
41
+ * await session.destroy();
42
+ * ```
43
+ */
44
+ declare const steel: (config: SteelConfig) => _computesdk_provider.BrowserProvider<Steel.Sessions.Session>;
45
+
46
+ export { type SteelConfig, steel };
package/dist/index.js ADDED
@@ -0,0 +1,212 @@
1
+ // src/index.ts
2
+ import Steel, { toFile } from "steel-sdk";
3
+ import { defineBrowserProvider } from "@computesdk/provider";
4
+ function resolveConfig(config) {
5
+ const apiKey = config.apiKey || typeof process !== "undefined" && process.env?.STEEL_API_KEY || "";
6
+ if (!apiKey) {
7
+ throw new Error(
8
+ `Missing Steel API key. Provide 'apiKey' in config or set STEEL_API_KEY environment variable. Get your API key from https://app.steel.dev/settings/api-keys`
9
+ );
10
+ }
11
+ return { apiKey, baseUrl: config.baseUrl };
12
+ }
13
+ function createClient(config) {
14
+ const { apiKey, baseUrl } = resolveConfig(config);
15
+ return new Steel({ steelAPIKey: apiKey, baseURL: baseUrl ?? void 0 });
16
+ }
17
+ function applyProxy(params, proxy) {
18
+ if (proxy.type === "custom" && proxy.server) {
19
+ const url = new URL(proxy.server);
20
+ if (proxy.username) url.username = proxy.username;
21
+ if (proxy.password) url.password = proxy.password;
22
+ params.proxyUrl = url.toString();
23
+ return;
24
+ }
25
+ if (proxy.geolocation?.country || proxy.geolocation?.state || proxy.geolocation?.city) {
26
+ params.useProxy = {
27
+ geolocation: {
28
+ country: proxy.geolocation.country,
29
+ ...proxy.geolocation.state ? { state: proxy.geolocation.state } : {},
30
+ ...proxy.geolocation.city ? { city: proxy.geolocation.city } : {}
31
+ }
32
+ };
33
+ return;
34
+ }
35
+ params.useProxy = true;
36
+ }
37
+ var warnedUnsupported = /* @__PURE__ */ new Set();
38
+ function warnOnce(field, reason) {
39
+ if (warnedUnsupported.has(field)) return;
40
+ warnedUnsupported.add(field);
41
+ console.warn(`[@computesdk/steel] '${field}' is ignored: ${reason}`);
42
+ }
43
+ function mapSessionOptions(options) {
44
+ if (!options) return {};
45
+ if (options.keepAlive === true) {
46
+ warnOnce("keepAlive", "Steel has no keep-alive flag; use a long `timeout` instead.");
47
+ }
48
+ if (options.recording === false) {
49
+ warnOnce("recording", "Steel always records sessions and cannot be disabled per session.");
50
+ }
51
+ if (options.logging === false) {
52
+ warnOnce("logging", "Steel always captures console logs and cannot be disabled per session.");
53
+ }
54
+ if (options.userMetadata && Object.keys(options.userMetadata).length > 0) {
55
+ warnOnce("userMetadata", "Steel does not support user metadata on sessions.");
56
+ }
57
+ const params = {};
58
+ if (options.viewport) params.dimensions = { width: options.viewport.width, height: options.viewport.height };
59
+ if (options.extensionIds && options.extensionIds.length > 0) params.extensionIds = options.extensionIds;
60
+ if (options.region) params.region = options.region;
61
+ if (options.timeout !== void 0) params.timeout = options.timeout * 1e3;
62
+ if (options.profileId) {
63
+ params.profileId = options.profileId;
64
+ params.persistProfile = true;
65
+ }
66
+ if (options.stealth) {
67
+ params.stealthConfig = { humanizeInteractions: true };
68
+ }
69
+ if (options.proxies === true) {
70
+ params.useProxy = true;
71
+ } else if (Array.isArray(options.proxies) && options.proxies.length > 0) {
72
+ applyProxy(params, options.proxies[0]);
73
+ }
74
+ return params;
75
+ }
76
+ function mapStatus(status) {
77
+ switch (status) {
78
+ case "live":
79
+ return "running";
80
+ case "released":
81
+ return "completed";
82
+ case "failed":
83
+ return "failed";
84
+ default:
85
+ return "created";
86
+ }
87
+ }
88
+ function buildConnectUrl(websocketUrl, apiKey, sessionId) {
89
+ const url = new URL(websocketUrl);
90
+ if (!url.searchParams.has("apiKey")) url.searchParams.set("apiKey", apiKey);
91
+ if (!url.searchParams.has("sessionId")) url.searchParams.set("sessionId", sessionId);
92
+ return url.toString();
93
+ }
94
+ var steel = defineBrowserProvider({
95
+ name: "steel",
96
+ methods: {
97
+ // ─── Session Lifecycle ─────────────────────────────────────────────
98
+ session: {
99
+ create: async (config, options) => {
100
+ const { apiKey } = resolveConfig(config);
101
+ const client = createClient(config);
102
+ const session = await client.sessions.create(mapSessionOptions(options));
103
+ return {
104
+ session,
105
+ sessionId: session.id,
106
+ connectUrl: buildConnectUrl(session.websocketUrl, apiKey, session.id),
107
+ status: mapStatus(session.status)
108
+ };
109
+ },
110
+ getById: async (config, sessionId) => {
111
+ const { apiKey } = resolveConfig(config);
112
+ const client = createClient(config);
113
+ try {
114
+ const session = await client.sessions.retrieve(sessionId);
115
+ return {
116
+ session,
117
+ sessionId: session.id,
118
+ connectUrl: buildConnectUrl(session.websocketUrl, apiKey, session.id),
119
+ status: mapStatus(session.status)
120
+ };
121
+ } catch {
122
+ return null;
123
+ }
124
+ },
125
+ list: async (config) => {
126
+ const client = createClient(config);
127
+ const page = await client.sessions.list();
128
+ const sessions = page.sessions ?? [];
129
+ return sessions.map((s) => ({
130
+ session: s,
131
+ sessionId: s.id,
132
+ status: mapStatus(s.status)
133
+ }));
134
+ },
135
+ destroy: async (config, sessionId) => {
136
+ const client = createClient(config);
137
+ await client.sessions.release(sessionId);
138
+ },
139
+ getConnectUrl: async (config, sessionId) => {
140
+ const { apiKey } = resolveConfig(config);
141
+ const client = createClient(config);
142
+ const session = await client.sessions.retrieve(sessionId);
143
+ return buildConnectUrl(session.websocketUrl, apiKey, session.id);
144
+ }
145
+ },
146
+ // ─── Extensions ────────────────────────────────────────────────────
147
+ extension: {
148
+ create: async (config, options) => {
149
+ const client = createClient(config);
150
+ const buf = typeof options.file === "string" ? Buffer.from(options.file, "utf-8") : Buffer.from(options.file);
151
+ const file = await toFile(buf, options.name ?? "extension.zip");
152
+ const result = await client.extensions.upload({ file });
153
+ return {
154
+ extensionId: result.id,
155
+ name: result.name
156
+ };
157
+ },
158
+ get: async (config, extensionId) => {
159
+ const client = createClient(config);
160
+ const response = await client.extensions.list();
161
+ const found = response.extensions.find((e) => e.id === extensionId);
162
+ if (!found) return null;
163
+ return { extensionId: found.id, name: found.name };
164
+ },
165
+ delete: async (config, extensionId) => {
166
+ const client = createClient(config);
167
+ await client.extensions.delete(extensionId);
168
+ }
169
+ },
170
+ // ─── Logs (RRWeb session events) ───────────────────────────────────
171
+ // Steel doesn't expose a console-log endpoint — the closest equivalent
172
+ // is the RRWeb event stream from `sessions.events`. We surface those as
173
+ // log entries so callers have a uniform shape.
174
+ logs: {
175
+ list: async (config, sessionId) => {
176
+ const client = createClient(config);
177
+ const events = await client.sessions.events(sessionId);
178
+ return events.map((event) => {
179
+ const ts = typeof event.timestamp === "number" ? event.timestamp : Date.now();
180
+ return {
181
+ timestamp: new Date(ts),
182
+ level: "info",
183
+ message: typeof event.type === "string" || typeof event.type === "number" ? `event:${String(event.type)}` : "event",
184
+ data: event
185
+ };
186
+ });
187
+ }
188
+ },
189
+ // ─── Recordings ────────────────────────────────────────────────────
190
+ recording: {
191
+ get: async (config, sessionId) => {
192
+ const client = createClient(config);
193
+ try {
194
+ const events = await client.sessions.events(sessionId);
195
+ if (!events || Array.isArray(events) && events.length === 0) return null;
196
+ return {
197
+ recordingId: sessionId,
198
+ sessionId,
199
+ format: "rrweb",
200
+ data: events
201
+ };
202
+ } catch {
203
+ return null;
204
+ }
205
+ }
206
+ }
207
+ }
208
+ });
209
+ export {
210
+ steel
211
+ };
212
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Steel Browser Provider - Factory-based Implementation\n *\n * Cloud browser sessions with stealth mode, proxies, persistent profiles,\n * extensions, and session recordings via the Steel platform.\n */\n\nimport Steel, { toFile } from 'steel-sdk';\nimport type {\n Session as SteelSession,\n SessionCreateParams,\n Sessionslist,\n} from 'steel-sdk/resources/sessions';\nimport { defineBrowserProvider } from '@computesdk/provider';\n\nimport type {\n BrowserSession,\n CreateBrowserSessionOptions,\n BrowserExtension,\n BrowserLog,\n BrowserRecording,\n ProxyConfig,\n} from '@computesdk/provider';\n\n/**\n * Steel-specific configuration options\n */\nexport interface SteelConfig {\n /** Steel API key — falls back to STEEL_API_KEY env var */\n apiKey?: string;\n /** Optional API base URL override (defaults to https://api.steel.dev) */\n baseUrl?: string;\n}\n\n/**\n * The native Steel session object returned by their SDK\n */\ntype NativeSteelSession = SteelSession;\n\n/**\n * Resolve config values from explicit config or environment variables\n */\nfunction resolveConfig(config: SteelConfig) {\n const apiKey = config.apiKey || (typeof process !== 'undefined' && process.env?.STEEL_API_KEY) || '';\n\n if (!apiKey) {\n throw new Error(\n `Missing Steel API key. Provide 'apiKey' in config or set STEEL_API_KEY environment variable. ` +\n `Get your API key from https://app.steel.dev/settings/api-keys`\n );\n }\n\n return { apiKey, baseUrl: config.baseUrl };\n}\n\n/**\n * Create a Steel SDK client from config\n */\nfunction createClient(config: SteelConfig): Steel {\n const { apiKey, baseUrl } = resolveConfig(config);\n return new Steel({ steelAPIKey: apiKey, baseURL: baseUrl ?? undefined });\n}\n\n/**\n * Steel's `useProxy` field accepts either a boolean, a geolocation object,\n * or a custom server. Apply a single ProxyConfig onto the create-session params.\n * Steel only supports one proxy per session, so when multiple are passed\n * we honor the first one.\n */\nfunction applyProxy(params: SessionCreateParams, proxy: ProxyConfig) {\n if (proxy.type === 'custom' && proxy.server) {\n // Steel uses a separate proxyUrl field for custom proxies; this overrides\n // useProxy and disables Steel-provided proxies in favor of the user's.\n const url = new URL(proxy.server);\n if (proxy.username) url.username = proxy.username;\n if (proxy.password) url.password = proxy.password;\n params.proxyUrl = url.toString();\n return;\n }\n\n if (proxy.geolocation?.country || proxy.geolocation?.state || proxy.geolocation?.city) {\n params.useProxy = {\n geolocation: {\n country: proxy.geolocation.country as any,\n ...(proxy.geolocation.state ? { state: proxy.geolocation.state as any } : {}),\n ...(proxy.geolocation.city ? { city: proxy.geolocation.city as any } : {}),\n },\n };\n return;\n }\n\n params.useProxy = true;\n}\n\nconst warnedUnsupported = new Set<string>();\nfunction warnOnce(field: string, reason: string) {\n if (warnedUnsupported.has(field)) return;\n warnedUnsupported.add(field);\n console.warn(`[@computesdk/steel] '${field}' is ignored: ${reason}`);\n}\n\n/**\n * Map ComputeSDK session options to Steel create-session params.\n *\n * The following CreateBrowserSessionOptions fields are intentionally not\n * forwarded because Steel's API has no equivalent knob:\n * - `recording`: Steel always records sessions; fetch via the recording manager.\n * - `logging`: Steel always captures console logs; fetch via the logs manager.\n * - `keepAlive`: Steel sessions run until `timeout` elapses or are released\n * explicitly; there is no separate keep-alive flag. Use a long\n * `timeout` to approximate the behavior.\n * - `userMetadata`: Steel does not support arbitrary user metadata on sessions.\n *\n * If a caller passes a non-default value for any of these, we warn once per\n * field so the silent drop is at least visible in logs.\n */\nfunction mapSessionOptions(options?: CreateBrowserSessionOptions): SessionCreateParams {\n if (!options) return {};\n\n if (options.keepAlive === true) {\n warnOnce('keepAlive', 'Steel has no keep-alive flag; use a long `timeout` instead.');\n }\n if (options.recording === false) {\n warnOnce('recording', 'Steel always records sessions and cannot be disabled per session.');\n }\n if (options.logging === false) {\n warnOnce('logging', 'Steel always captures console logs and cannot be disabled per session.');\n }\n if (options.userMetadata && Object.keys(options.userMetadata).length > 0) {\n warnOnce('userMetadata', 'Steel does not support user metadata on sessions.');\n }\n\n const params: SessionCreateParams = {};\n\n if (options.viewport) params.dimensions = { width: options.viewport.width, height: options.viewport.height };\n if (options.extensionIds && options.extensionIds.length > 0) params.extensionIds = options.extensionIds;\n if (options.region) params.region = options.region as SessionCreateParams['region'];\n // Steel session timeout is in milliseconds (default 5 minutes)\n if (options.timeout !== undefined) params.timeout = options.timeout * 1000;\n if (options.profileId) {\n params.profileId = options.profileId;\n params.persistProfile = true;\n }\n\n // Stealth wraps a few related flags. We toggle humanizeInteractions as the\n // most user-visible knob; advanced users can override via options pass-through.\n if (options.stealth) {\n params.stealthConfig = { humanizeInteractions: true };\n }\n\n if (options.proxies === true) {\n params.useProxy = true;\n } else if (Array.isArray(options.proxies) && options.proxies.length > 0) {\n applyProxy(params, options.proxies[0]!);\n }\n\n return params;\n}\n\n/**\n * Map Steel's session status onto our standard set\n */\nfunction mapStatus(status: string | undefined): BrowserSession['status'] {\n switch (status) {\n case 'live': return 'running';\n case 'released': return 'completed';\n case 'failed': return 'failed';\n default: return 'created';\n }\n}\n\n/**\n * Append the apiKey (and sessionId if missing) to Steel's websocketUrl so\n * callers can pass it directly to playwright/puppeteer. Steel returns\n * `wss://connect.steel.dev?sessionId=...` and requires `apiKey` to be\n * appended for authentication.\n *\n * SECURITY: the returned URL embeds the account-wide Steel API key as a\n * query parameter. Treat it as a secret — do not log it, persist it, or\n * forward it to systems that retain URLs (access logs, error trackers,\n * tracing). Anyone with this URL has full access to the Steel account, not\n * just this session.\n */\nfunction buildConnectUrl(websocketUrl: string, apiKey: string, sessionId: string): string {\n const url = new URL(websocketUrl);\n if (!url.searchParams.has('apiKey')) url.searchParams.set('apiKey', apiKey);\n if (!url.searchParams.has('sessionId')) url.searchParams.set('sessionId', sessionId);\n return url.toString();\n}\n\n/**\n * Create a Steel browser provider instance\n *\n * SECURITY NOTE: Steel's CDP endpoint authenticates via an `apiKey` query\n * parameter on the WebSocket URL. As a result, every `connectUrl` returned\n * by this provider (from `session.create()` and `session.getById()`, and\n * `provider.getConnectUrl()`) embeds the account-wide Steel API key. Treat\n * `connectUrl` as a secret: do not log, persist, or forward it to systems\n * that retain URLs. `session.list()` deliberately omits `connectUrl` to\n * avoid amplifying this leak surface — call `provider.getConnectUrl(id)` on\n * demand instead.\n *\n * @example\n * ```ts\n * import { steel } from '@computesdk/steel';\n * import { chromium } from 'playwright-core';\n *\n * const st = steel({ apiKey: process.env.STEEL_API_KEY });\n *\n * // Create a stealth browser session\n * const session = await st.session.create({ stealth: true, proxies: true });\n *\n * // Connect with Playwright (do not log session.connectUrl — it contains the API key)\n * const browser = await chromium.connectOverCDP(session.connectUrl);\n * const page = browser.contexts()[0].pages()[0];\n * await page.goto('https://example.com');\n *\n * // Release the session\n * await session.destroy();\n * ```\n */\nexport const steel = defineBrowserProvider<NativeSteelSession, SteelConfig>({\n name: 'steel',\n methods: {\n // ─── Session Lifecycle ─────────────────────────────────────────────\n session: {\n create: async (config, options) => {\n const { apiKey } = resolveConfig(config);\n const client = createClient(config);\n const session = await client.sessions.create(mapSessionOptions(options));\n return {\n session,\n sessionId: session.id,\n connectUrl: buildConnectUrl(session.websocketUrl, apiKey, session.id),\n status: mapStatus(session.status),\n };\n },\n\n getById: async (config, sessionId) => {\n const { apiKey } = resolveConfig(config);\n const client = createClient(config);\n try {\n const session = await client.sessions.retrieve(sessionId);\n return {\n session,\n sessionId: session.id,\n connectUrl: buildConnectUrl(session.websocketUrl, apiKey, session.id),\n status: mapStatus(session.status),\n };\n } catch {\n return null;\n }\n },\n\n list: async (config) => {\n const client = createClient(config);\n const page = await client.sessions.list();\n const sessions = (page as unknown as { sessions: Sessionslist['sessions'] }).sessions ?? [];\n // We deliberately omit connectUrl from list entries: the URL would\n // need to embed the account-wide Steel API key, and emitting it once\n // per session inflates the leak surface (logs, traces, etc.). Callers\n // that need a connectable URL should use getConnectUrl(sessionId).\n return sessions.map((s) => ({\n session: s as NativeSteelSession,\n sessionId: s.id,\n status: mapStatus(s.status),\n }));\n },\n\n destroy: async (config, sessionId) => {\n const client = createClient(config);\n await client.sessions.release(sessionId);\n },\n\n getConnectUrl: async (config, sessionId) => {\n const { apiKey } = resolveConfig(config);\n const client = createClient(config);\n const session = await client.sessions.retrieve(sessionId);\n return buildConnectUrl(session.websocketUrl, apiKey, session.id);\n },\n },\n\n // ─── Extensions ────────────────────────────────────────────────────\n extension: {\n create: async (config, options) => {\n const client = createClient(config);\n const buf = typeof options.file === 'string'\n ? Buffer.from(options.file, 'utf-8')\n : Buffer.from(options.file);\n const file = await toFile(buf, options.name ?? 'extension.zip');\n const result = await client.extensions.upload({ file });\n return {\n extensionId: result.id,\n name: result.name,\n } satisfies BrowserExtension;\n },\n\n get: async (config, extensionId) => {\n const client = createClient(config);\n // Steel exposes only a list endpoint for extension lookup — filter the result.\n const response = await client.extensions.list();\n const found = response.extensions.find((e) => e.id === extensionId);\n if (!found) return null;\n return { extensionId: found.id, name: found.name } satisfies BrowserExtension;\n },\n\n delete: async (config, extensionId) => {\n const client = createClient(config);\n await client.extensions.delete(extensionId);\n },\n },\n\n // ─── Logs (RRWeb session events) ───────────────────────────────────\n // Steel doesn't expose a console-log endpoint — the closest equivalent\n // is the RRWeb event stream from `sessions.events`. We surface those as\n // log entries so callers have a uniform shape.\n logs: {\n list: async (config, sessionId) => {\n const client = createClient(config);\n const events = await client.sessions.events(sessionId);\n return (events as Array<Record<string, unknown>>).map((event) => {\n const ts = typeof event.timestamp === 'number' ? event.timestamp : Date.now();\n return {\n timestamp: new Date(ts),\n level: 'info' as BrowserLog['level'],\n message: typeof event.type === 'string' || typeof event.type === 'number'\n ? `event:${String(event.type)}`\n : 'event',\n data: event,\n } satisfies BrowserLog;\n });\n },\n },\n\n // ─── Recordings ────────────────────────────────────────────────────\n recording: {\n get: async (config, sessionId) => {\n const client = createClient(config);\n try {\n const events = await client.sessions.events(sessionId);\n if (!events || (Array.isArray(events) && events.length === 0)) return null;\n return {\n recordingId: sessionId,\n sessionId,\n format: 'rrweb',\n data: events,\n } satisfies BrowserRecording;\n } catch {\n return null;\n }\n },\n },\n },\n});\n"],"mappings":";AAOA,OAAO,SAAS,cAAc;AAM9B,SAAS,6BAA6B;AA6BtC,SAAS,cAAc,QAAqB;AAC1C,QAAM,SAAS,OAAO,UAAW,OAAO,YAAY,eAAe,QAAQ,KAAK,iBAAkB;AAElG,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AAEA,SAAO,EAAE,QAAQ,SAAS,OAAO,QAAQ;AAC3C;AAKA,SAAS,aAAa,QAA4B;AAChD,QAAM,EAAE,QAAQ,QAAQ,IAAI,cAAc,MAAM;AAChD,SAAO,IAAI,MAAM,EAAE,aAAa,QAAQ,SAAS,WAAW,OAAU,CAAC;AACzE;AAQA,SAAS,WAAW,QAA6B,OAAoB;AACnE,MAAI,MAAM,SAAS,YAAY,MAAM,QAAQ;AAG3C,UAAM,MAAM,IAAI,IAAI,MAAM,MAAM;AAChC,QAAI,MAAM,SAAU,KAAI,WAAW,MAAM;AACzC,QAAI,MAAM,SAAU,KAAI,WAAW,MAAM;AACzC,WAAO,WAAW,IAAI,SAAS;AAC/B;AAAA,EACF;AAEA,MAAI,MAAM,aAAa,WAAW,MAAM,aAAa,SAAS,MAAM,aAAa,MAAM;AACrF,WAAO,WAAW;AAAA,MAChB,aAAa;AAAA,QACX,SAAS,MAAM,YAAY;AAAA,QAC3B,GAAI,MAAM,YAAY,QAAQ,EAAE,OAAO,MAAM,YAAY,MAAa,IAAI,CAAC;AAAA,QAC3E,GAAI,MAAM,YAAY,OAAO,EAAE,MAAM,MAAM,YAAY,KAAY,IAAI,CAAC;AAAA,MAC1E;AAAA,IACF;AACA;AAAA,EACF;AAEA,SAAO,WAAW;AACpB;AAEA,IAAM,oBAAoB,oBAAI,IAAY;AAC1C,SAAS,SAAS,OAAe,QAAgB;AAC/C,MAAI,kBAAkB,IAAI,KAAK,EAAG;AAClC,oBAAkB,IAAI,KAAK;AAC3B,UAAQ,KAAK,wBAAwB,KAAK,iBAAiB,MAAM,EAAE;AACrE;AAiBA,SAAS,kBAAkB,SAA4D;AACrF,MAAI,CAAC,QAAS,QAAO,CAAC;AAEtB,MAAI,QAAQ,cAAc,MAAM;AAC9B,aAAS,aAAa,6DAA6D;AAAA,EACrF;AACA,MAAI,QAAQ,cAAc,OAAO;AAC/B,aAAS,aAAa,mEAAmE;AAAA,EAC3F;AACA,MAAI,QAAQ,YAAY,OAAO;AAC7B,aAAS,WAAW,wEAAwE;AAAA,EAC9F;AACA,MAAI,QAAQ,gBAAgB,OAAO,KAAK,QAAQ,YAAY,EAAE,SAAS,GAAG;AACxE,aAAS,gBAAgB,mDAAmD;AAAA,EAC9E;AAEA,QAAM,SAA8B,CAAC;AAErC,MAAI,QAAQ,SAAU,QAAO,aAAa,EAAE,OAAO,QAAQ,SAAS,OAAO,QAAQ,QAAQ,SAAS,OAAO;AAC3G,MAAI,QAAQ,gBAAgB,QAAQ,aAAa,SAAS,EAAG,QAAO,eAAe,QAAQ;AAC3F,MAAI,QAAQ,OAAQ,QAAO,SAAS,QAAQ;AAE5C,MAAI,QAAQ,YAAY,OAAW,QAAO,UAAU,QAAQ,UAAU;AACtE,MAAI,QAAQ,WAAW;AACrB,WAAO,YAAY,QAAQ;AAC3B,WAAO,iBAAiB;AAAA,EAC1B;AAIA,MAAI,QAAQ,SAAS;AACnB,WAAO,gBAAgB,EAAE,sBAAsB,KAAK;AAAA,EACtD;AAEA,MAAI,QAAQ,YAAY,MAAM;AAC5B,WAAO,WAAW;AAAA,EACpB,WAAW,MAAM,QAAQ,QAAQ,OAAO,KAAK,QAAQ,QAAQ,SAAS,GAAG;AACvE,eAAW,QAAQ,QAAQ,QAAQ,CAAC,CAAE;AAAA,EACxC;AAEA,SAAO;AACT;AAKA,SAAS,UAAU,QAAsD;AACvE,UAAQ,QAAQ;AAAA,IACd,KAAK;AAAQ,aAAO;AAAA,IACpB,KAAK;AAAY,aAAO;AAAA,IACxB,KAAK;AAAU,aAAO;AAAA,IACtB;AAAS,aAAO;AAAA,EAClB;AACF;AAcA,SAAS,gBAAgB,cAAsB,QAAgB,WAA2B;AACxF,QAAM,MAAM,IAAI,IAAI,YAAY;AAChC,MAAI,CAAC,IAAI,aAAa,IAAI,QAAQ,EAAG,KAAI,aAAa,IAAI,UAAU,MAAM;AAC1E,MAAI,CAAC,IAAI,aAAa,IAAI,WAAW,EAAG,KAAI,aAAa,IAAI,aAAa,SAAS;AACnF,SAAO,IAAI,SAAS;AACtB;AAiCO,IAAM,QAAQ,sBAAuD;AAAA,EAC1E,MAAM;AAAA,EACN,SAAS;AAAA;AAAA,IAEP,SAAS;AAAA,MACP,QAAQ,OAAO,QAAQ,YAAY;AACjC,cAAM,EAAE,OAAO,IAAI,cAAc,MAAM;AACvC,cAAM,SAAS,aAAa,MAAM;AAClC,cAAM,UAAU,MAAM,OAAO,SAAS,OAAO,kBAAkB,OAAO,CAAC;AACvE,eAAO;AAAA,UACL;AAAA,UACA,WAAW,QAAQ;AAAA,UACnB,YAAY,gBAAgB,QAAQ,cAAc,QAAQ,QAAQ,EAAE;AAAA,UACpE,QAAQ,UAAU,QAAQ,MAAM;AAAA,QAClC;AAAA,MACF;AAAA,MAEA,SAAS,OAAO,QAAQ,cAAc;AACpC,cAAM,EAAE,OAAO,IAAI,cAAc,MAAM;AACvC,cAAM,SAAS,aAAa,MAAM;AAClC,YAAI;AACF,gBAAM,UAAU,MAAM,OAAO,SAAS,SAAS,SAAS;AACxD,iBAAO;AAAA,YACL;AAAA,YACA,WAAW,QAAQ;AAAA,YACnB,YAAY,gBAAgB,QAAQ,cAAc,QAAQ,QAAQ,EAAE;AAAA,YACpE,QAAQ,UAAU,QAAQ,MAAM;AAAA,UAClC;AAAA,QACF,QAAQ;AACN,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,MAEA,MAAM,OAAO,WAAW;AACtB,cAAM,SAAS,aAAa,MAAM;AAClC,cAAM,OAAO,MAAM,OAAO,SAAS,KAAK;AACxC,cAAM,WAAY,KAA2D,YAAY,CAAC;AAK1F,eAAO,SAAS,IAAI,CAAC,OAAO;AAAA,UAC1B,SAAS;AAAA,UACT,WAAW,EAAE;AAAA,UACb,QAAQ,UAAU,EAAE,MAAM;AAAA,QAC5B,EAAE;AAAA,MACJ;AAAA,MAEA,SAAS,OAAO,QAAQ,cAAc;AACpC,cAAM,SAAS,aAAa,MAAM;AAClC,cAAM,OAAO,SAAS,QAAQ,SAAS;AAAA,MACzC;AAAA,MAEA,eAAe,OAAO,QAAQ,cAAc;AAC1C,cAAM,EAAE,OAAO,IAAI,cAAc,MAAM;AACvC,cAAM,SAAS,aAAa,MAAM;AAClC,cAAM,UAAU,MAAM,OAAO,SAAS,SAAS,SAAS;AACxD,eAAO,gBAAgB,QAAQ,cAAc,QAAQ,QAAQ,EAAE;AAAA,MACjE;AAAA,IACF;AAAA;AAAA,IAGA,WAAW;AAAA,MACT,QAAQ,OAAO,QAAQ,YAAY;AACjC,cAAM,SAAS,aAAa,MAAM;AAClC,cAAM,MAAM,OAAO,QAAQ,SAAS,WAChC,OAAO,KAAK,QAAQ,MAAM,OAAO,IACjC,OAAO,KAAK,QAAQ,IAAI;AAC5B,cAAM,OAAO,MAAM,OAAO,KAAK,QAAQ,QAAQ,eAAe;AAC9D,cAAM,SAAS,MAAM,OAAO,WAAW,OAAO,EAAE,KAAK,CAAC;AACtD,eAAO;AAAA,UACL,aAAa,OAAO;AAAA,UACpB,MAAM,OAAO;AAAA,QACf;AAAA,MACF;AAAA,MAEA,KAAK,OAAO,QAAQ,gBAAgB;AAClC,cAAM,SAAS,aAAa,MAAM;AAElC,cAAM,WAAW,MAAM,OAAO,WAAW,KAAK;AAC9C,cAAM,QAAQ,SAAS,WAAW,KAAK,CAAC,MAAM,EAAE,OAAO,WAAW;AAClE,YAAI,CAAC,MAAO,QAAO;AACnB,eAAO,EAAE,aAAa,MAAM,IAAI,MAAM,MAAM,KAAK;AAAA,MACnD;AAAA,MAEA,QAAQ,OAAO,QAAQ,gBAAgB;AACrC,cAAM,SAAS,aAAa,MAAM;AAClC,cAAM,OAAO,WAAW,OAAO,WAAW;AAAA,MAC5C;AAAA,IACF;AAAA;AAAA;AAAA;AAAA;AAAA,IAMA,MAAM;AAAA,MACJ,MAAM,OAAO,QAAQ,cAAc;AACjC,cAAM,SAAS,aAAa,MAAM;AAClC,cAAM,SAAS,MAAM,OAAO,SAAS,OAAO,SAAS;AACrD,eAAQ,OAA0C,IAAI,CAAC,UAAU;AAC/D,gBAAM,KAAK,OAAO,MAAM,cAAc,WAAW,MAAM,YAAY,KAAK,IAAI;AAC5E,iBAAO;AAAA,YACL,WAAW,IAAI,KAAK,EAAE;AAAA,YACtB,OAAO;AAAA,YACP,SAAS,OAAO,MAAM,SAAS,YAAY,OAAO,MAAM,SAAS,WAC7D,SAAS,OAAO,MAAM,IAAI,CAAC,KAC3B;AAAA,YACJ,MAAM;AAAA,UACR;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA;AAAA,IAGA,WAAW;AAAA,MACT,KAAK,OAAO,QAAQ,cAAc;AAChC,cAAM,SAAS,aAAa,MAAM;AAClC,YAAI;AACF,gBAAM,SAAS,MAAM,OAAO,SAAS,OAAO,SAAS;AACrD,cAAI,CAAC,UAAW,MAAM,QAAQ,MAAM,KAAK,OAAO,WAAW,EAAI,QAAO;AACtE,iBAAO;AAAA,YACL,aAAa;AAAA,YACb;AAAA,YACA,QAAQ;AAAA,YACR,MAAM;AAAA,UACR;AAAA,QACF,QAAQ;AACN,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF,CAAC;","names":[]}
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "@computesdk/steel",
3
+ "version": "0.2.0",
4
+ "description": "Steel browser provider for ComputeSDK - cloud browser sessions with stealth mode, proxies, profiles, and session recording",
5
+ "author": "Garrison",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "main": "./dist/index.cjs",
9
+ "module": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.js",
15
+ "require": "./dist/index.cjs"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist"
20
+ ],
21
+ "dependencies": {
22
+ "dotenv": "^16.0.0",
23
+ "playwright-core": "^1.40.0",
24
+ "steel-sdk": "^0.18.0",
25
+ "@computesdk/provider": "2.0.0",
26
+ "computesdk": "4.0.0"
27
+ },
28
+ "keywords": [
29
+ "computesdk",
30
+ "steel",
31
+ "browser",
32
+ "headless",
33
+ "playwright",
34
+ "puppeteer",
35
+ "stealth",
36
+ "proxy",
37
+ "provider"
38
+ ],
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "https://github.com/computesdk/computesdk.git",
42
+ "directory": "packages/steel"
43
+ },
44
+ "homepage": "https://www.computesdk.com",
45
+ "bugs": {
46
+ "url": "https://github.com/computesdk/computesdk/issues"
47
+ },
48
+ "devDependencies": {
49
+ "@types/node": "^20.0.0",
50
+ "@vitest/coverage-v8": "^1.0.0",
51
+ "eslint": "^8.37.0",
52
+ "rimraf": "^5.0.0",
53
+ "tsup": "^8.0.0",
54
+ "typescript": "^5.0.0",
55
+ "vitest": "^1.0.0",
56
+ "@computesdk/test-utils": "2.0.0"
57
+ },
58
+ "scripts": {
59
+ "build": "tsup",
60
+ "clean": "rimraf dist",
61
+ "dev": "tsup --watch",
62
+ "test": "vitest run",
63
+ "test:watch": "vitest watch",
64
+ "test:coverage": "vitest run --coverage",
65
+ "typecheck": "tsc --noEmit",
66
+ "lint": "eslint"
67
+ }
68
+ }