effect-browserbase 0.2.0-beta.9 → 0.3.0-beta.10

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.
Files changed (155) hide show
  1. package/README.md +277 -319
  2. package/dist/Browserbase-CaPKwEY9.mjs +544 -0
  3. package/dist/Browserbase-CaPKwEY9.mjs.map +1 -0
  4. package/dist/Browserbase.d.mts +110 -0
  5. package/dist/Browserbase.mjs +3 -0
  6. package/dist/BrowserbaseClient-CtAQ2rg4.d.mts +195 -0
  7. package/dist/BrowserbaseClient.d.mts +2 -0
  8. package/dist/BrowserbaseClient.mjs +190 -0
  9. package/dist/BrowserbaseClient.mjs.map +1 -0
  10. package/dist/BrowserbaseError-D1EOrLNc.d.mts +79 -0
  11. package/dist/BrowserbaseError.d.mts +2 -0
  12. package/dist/BrowserbaseError.mjs +101 -0
  13. package/dist/BrowserbaseError.mjs.map +1 -0
  14. package/dist/ContextLease.d.mts +42 -0
  15. package/dist/ContextLease.mjs +80 -0
  16. package/dist/ContextLease.mjs.map +1 -0
  17. package/dist/index.d.mts +5 -27
  18. package/dist/index.mjs +5 -26
  19. package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
  20. package/dist/testing/index.d.mts +136 -0
  21. package/dist/testing/index.mjs +440 -0
  22. package/dist/testing/index.mjs.map +1 -0
  23. package/package.json +51 -121
  24. package/src/Browserbase.ts +729 -0
  25. package/src/BrowserbaseClient.ts +559 -0
  26. package/src/BrowserbaseError.ts +108 -0
  27. package/src/ContextLease.ts +118 -0
  28. package/src/index.ts +7 -0
  29. package/src/internal/captureConnection.ts +214 -0
  30. package/src/testing/BrowserbaseContract.ts +170 -0
  31. package/src/testing/TestBrowserbase.ts +509 -0
  32. package/src/testing/index.ts +7 -0
  33. package/dist/Account.d.mts +0 -32
  34. package/dist/Account.mjs +0 -36
  35. package/dist/Account.mjs.map +0 -1
  36. package/dist/Acquisition-BOABu_qJ.mjs +0 -244
  37. package/dist/Acquisition-BOABu_qJ.mjs.map +0 -1
  38. package/dist/Agents-CPaTOH_g.d.mts +0 -156
  39. package/dist/Agents.d.mts +0 -3
  40. package/dist/Agents.mjs +0 -292
  41. package/dist/Agents.mjs.map +0 -1
  42. package/dist/Allocation.d.mts +0 -97
  43. package/dist/Allocation.mjs +0 -27
  44. package/dist/Allocation.mjs.map +0 -1
  45. package/dist/Authorization-Ll5f_XNM.mjs +0 -12
  46. package/dist/Authorization-Ll5f_XNM.mjs.map +0 -1
  47. package/dist/Browser-ByrWJ0ss.mjs +0 -274
  48. package/dist/Browser-ByrWJ0ss.mjs.map +0 -1
  49. package/dist/Browser.d.mts +0 -83
  50. package/dist/Browser.mjs +0 -3
  51. package/dist/BrowserBinding.d.mts +0 -13
  52. package/dist/BrowserBinding.mjs +0 -31
  53. package/dist/BrowserBinding.mjs.map +0 -1
  54. package/dist/Certificates-D4NQmyeA.d.mts +0 -34
  55. package/dist/Certificates.d.mts +0 -3
  56. package/dist/Certificates.mjs +0 -85
  57. package/dist/Certificates.mjs.map +0 -1
  58. package/dist/Cleanup-DMTDBir8.d.mts +0 -23
  59. package/dist/Cleanup.d.mts +0 -3
  60. package/dist/Cleanup.mjs +0 -43
  61. package/dist/Cleanup.mjs.map +0 -1
  62. package/dist/Client-BEC4Q8ZS.mjs +0 -564
  63. package/dist/Client-BEC4Q8ZS.mjs.map +0 -1
  64. package/dist/Client-B_f6bBLT.d.mts +0 -52
  65. package/dist/Client.d.mts +0 -3
  66. package/dist/Client.mjs +0 -2
  67. package/dist/Connection-sxyxtwlk.mjs +0 -56
  68. package/dist/Connection-sxyxtwlk.mjs.map +0 -1
  69. package/dist/ContextCoordination.d.mts +0 -29
  70. package/dist/ContextCoordination.mjs +0 -94
  71. package/dist/ContextCoordination.mjs.map +0 -1
  72. package/dist/ContextWriter-CiEma24z.mjs +0 -204
  73. package/dist/ContextWriter-CiEma24z.mjs.map +0 -1
  74. package/dist/Contexts-DqdL7cyX.d.mts +0 -31
  75. package/dist/Contexts.d.mts +0 -3
  76. package/dist/Contexts.mjs +0 -97
  77. package/dist/Contexts.mjs.map +0 -1
  78. package/dist/Deadline-CrViaogr.mjs +0 -13
  79. package/dist/Deadline-CrViaogr.mjs.map +0 -1
  80. package/dist/Downloads-CX52GOFJ.d.mts +0 -43
  81. package/dist/Downloads.d.mts +0 -3
  82. package/dist/Downloads.mjs +0 -137
  83. package/dist/Downloads.mjs.map +0 -1
  84. package/dist/Errors.d.mts +0 -100
  85. package/dist/Errors.mjs +0 -194
  86. package/dist/Errors.mjs.map +0 -1
  87. package/dist/Extensions-BTUn9zhK.mjs +0 -231
  88. package/dist/Extensions-BTUn9zhK.mjs.map +0 -1
  89. package/dist/Extensions-DBXuVM1f.d.mts +0 -43
  90. package/dist/Extensions.d.mts +0 -3
  91. package/dist/Extensions.mjs +0 -2
  92. package/dist/Functions-CsKMMUuZ.d.mts +0 -226
  93. package/dist/Functions.d.mts +0 -3
  94. package/dist/Functions.mjs +0 -318
  95. package/dist/Functions.mjs.map +0 -1
  96. package/dist/Issued-DejqoMYL.mjs +0 -15
  97. package/dist/Issued-DejqoMYL.mjs.map +0 -1
  98. package/dist/Launch-cAdE2rrZ.mjs +0 -115
  99. package/dist/Launch-cAdE2rrZ.mjs.map +0 -1
  100. package/dist/Launch.d.mts +0 -127
  101. package/dist/Launch.mjs +0 -97
  102. package/dist/Launch.mjs.map +0 -1
  103. package/dist/PageFetch-CjAM_0DS.d.mts +0 -40
  104. package/dist/PageFetch.d.mts +0 -3
  105. package/dist/PageFetch.mjs +0 -59
  106. package/dist/PageFetch.mjs.map +0 -1
  107. package/dist/Projects-tm5bTDH0.d.mts +0 -38
  108. package/dist/Projects.d.mts +0 -3
  109. package/dist/Projects.mjs +0 -65
  110. package/dist/Projects.mjs.map +0 -1
  111. package/dist/Recordings-B9k5aODB.d.mts +0 -27
  112. package/dist/Recordings.d.mts +0 -3
  113. package/dist/Recordings.mjs +0 -144
  114. package/dist/Recordings.mjs.map +0 -1
  115. package/dist/References-DOH3Wu44.d.mts +0 -34
  116. package/dist/References.d.mts +0 -3
  117. package/dist/References.mjs +0 -33
  118. package/dist/References.mjs.map +0 -1
  119. package/dist/Replays-mV93WGtx.d.mts +0 -27
  120. package/dist/Replays.d.mts +0 -3
  121. package/dist/Replays.mjs +0 -107
  122. package/dist/Replays.mjs.map +0 -1
  123. package/dist/Resource-J7Kkk5mu.mjs +0 -54
  124. package/dist/Resource-J7Kkk5mu.mjs.map +0 -1
  125. package/dist/Search-B64e7g-h.d.mts +0 -37
  126. package/dist/Search.d.mts +0 -3
  127. package/dist/Search.mjs +0 -46
  128. package/dist/Search.mjs.map +0 -1
  129. package/dist/SessionData-5xYZDshJ.d.mts +0 -38
  130. package/dist/SessionData.d.mts +0 -3
  131. package/dist/SessionData.mjs +0 -48
  132. package/dist/SessionData.mjs.map +0 -1
  133. package/dist/Sessions-D2H5m6rF.mjs +0 -260
  134. package/dist/Sessions-D2H5m6rF.mjs.map +0 -1
  135. package/dist/Sessions-oJEUmZth.d.mts +0 -69
  136. package/dist/Sessions.d.mts +0 -4
  137. package/dist/Sessions.mjs +0 -3
  138. package/dist/Testing.d.mts +0 -119
  139. package/dist/Testing.mjs +0 -206
  140. package/dist/Testing.mjs.map +0 -1
  141. package/dist/TransferPolicy-DQ06i7SZ.mjs +0 -25
  142. package/dist/TransferPolicy-DQ06i7SZ.mjs.map +0 -1
  143. package/dist/Transfers-GVqzrDWl.d.mts +0 -89
  144. package/dist/Transfers.d.mts +0 -3
  145. package/dist/Transfers.mjs +0 -85
  146. package/dist/Transfers.mjs.map +0 -1
  147. package/dist/Uploads-C2_bACgv.d.mts +0 -28
  148. package/dist/Uploads.d.mts +0 -3
  149. package/dist/Uploads.mjs +0 -105
  150. package/dist/Uploads.mjs.map +0 -1
  151. package/dist/Webhooks-BoeRAxzG.d.mts +0 -59
  152. package/dist/Webhooks.d.mts +0 -3
  153. package/dist/Webhooks.mjs +0 -134
  154. package/dist/Webhooks.mjs.map +0 -1
  155. package/dist/WriterFacts-BBJRDZYY.d.mts +0 -54
package/dist/Testing.mjs DELETED
@@ -1,206 +0,0 @@
1
- import { Identifier } from "./References.mjs";
2
- import { ClientError } from "./Errors.mjs";
3
- import { recipe } from "./Launch.mjs";
4
- import { ProviderSession, SessionStatus } from "./SessionData.mjs";
5
- import { layer as layer$1 } from "./Account.mjs";
6
- import { layer as layer$2 } from "./BrowserBinding.mjs";
7
- import { t as BrowserbaseBrowser } from "./Browser-ByrWJ0ss.mjs";
8
- import { Context, Effect, Layer, Redacted, Schema } from "effect";
9
- import { FetchHttpClient } from "effect/unstable/http";
10
- import * as BrowserTesting from "effect-browser/testing";
11
- //#region src/Testing.ts
12
- /**
13
- * How the scripted control plane answers. Everything else about a session is what the real
14
- * `Client`, `Sessions`, allocation and cleanup code makes of those answers.
15
- */
16
- const ProviderScript = Schema.Struct({
17
- projectId: Schema.optionalKey(Identifier),
18
- /**
19
- * The creation reply: `Accept` allocates a session (running unless told otherwise), `Reject`
20
- * refuses with a status the real transport classifies, `Malformed` replies without an
21
- * identity, and `Lost` fails the request after it was sent, so the outcome is unknown.
22
- */
23
- create: Schema.optionalKey(Schema.Union([
24
- Schema.TaggedStruct("Accept", { status: Schema.optionalKey(SessionStatus) }),
25
- Schema.TaggedStruct("Reject", {
26
- status: Schema.Int.check(Schema.isBetween({
27
- minimum: 400,
28
- maximum: 599
29
- })),
30
- retryAfterMillis: Schema.optionalKey(Schema.Natural)
31
- }),
32
- Schema.TaggedStruct("Malformed", {}),
33
- Schema.TaggedStruct("Lost", {})
34
- ])),
35
- /** What a release request does: the session ends, stays running, or the request fails. */
36
- release: Schema.optionalKey(Schema.Literals([
37
- "confirmed",
38
- "pending",
39
- "failed"
40
- ])),
41
- liveView: Schema.optionalKey(Schema.Literals(["issued", "denied"])),
42
- region: Schema.optionalKey(ProviderSession.fields.region),
43
- /**
44
- * Whose identity a session's retrieval and release replies carry: its own, another session's
45
- * or another project's. The real client refuses a reply it cannot match, so a test can see
46
- * what allocation, connection and cleanup make of a provider that answers for someone else.
47
- */
48
- identity: Schema.optionalKey(Schema.Literals([
49
- "own",
50
- "foreign-session",
51
- "foreign-project"
52
- ]))
53
- });
54
- const secrets = {
55
- apiKey: "SCRIPTED-API-KEY-NOT-A-CREDENTIAL",
56
- connectUrl: "SCRIPTED-CONNECT-KEY",
57
- liveView: "SCRIPTED-LIVE-VIEW"
58
- };
59
- const json = (body, status = 200, headers = {}) => new Response(JSON.stringify(body), {
60
- status,
61
- headers: {
62
- "content-type": "application/json",
63
- ...headers
64
- }
65
- });
66
- /**
67
- * The reviewed session subset of the provider API, answered in memory: creation, retrieval,
68
- * listing, release, Live View issuance and uploads. Provide it as `FetchHttpClient.Fetch`
69
- * beneath the real `BrowserbaseClient.layer`, or use `layer` below, which does that.
70
- */
71
- const provider = Effect.fnUntraced(function* (script = {}) {
72
- const fixed = yield* Schema.decodeEffect(ProviderScript)(script, { onExcessProperty: "error" }).pipe(Effect.mapError(() => ClientError.make({
73
- operation: "configure",
74
- reason: "configuration",
75
- outcome: "undispatched"
76
- })));
77
- const projectId = fixed.projectId ?? "project-1";
78
- const region = fixed.region ?? "us-east-1";
79
- const calls = [];
80
- const sessions = /* @__PURE__ */ new Map();
81
- let serial = 0;
82
- const identity = fixed.identity ?? "own";
83
- const row = (session) => ({
84
- id: identity === "foreign-session" ? `${session.id}-other` : session.id,
85
- projectId: identity === "foreign-project" ? `${projectId}-other` : projectId,
86
- status: session.status,
87
- createdAt: "2026-01-01T00:00:00.000Z",
88
- updatedAt: "2026-01-01T00:00:01.000Z",
89
- expiresAt: "2026-01-01T01:00:00.000Z",
90
- startedAt: "2026-01-01T00:00:01.000Z",
91
- ...session.status === "COMPLETED" || session.status === "ERROR" || session.status === "TIMED_OUT" ? { endedAt: "2026-01-01T00:10:00.000Z" } : {},
92
- keepAlive: session.keepAlive,
93
- proxyBytes: 0,
94
- region,
95
- connectUrl: `wss://connect.browserbase.com/?session=${session.id}&key=${secrets.connectUrl}`
96
- });
97
- const create = fixed.create ?? { _tag: "Accept" };
98
- const fetch = async (input, init) => {
99
- const request = new Request(input, init);
100
- const url = new URL(request.url);
101
- calls.push({
102
- method: request.method,
103
- path: url.pathname
104
- });
105
- if (url.origin !== "https://api.browserbase.com") return json({ error: "unexpected origin" }, 404);
106
- if (request.headers.get("x-bb-api-key") !== secrets.apiKey) return json({ error: "unauthorized" }, 401);
107
- const parts = url.pathname.split("/").filter((part) => part.length > 0);
108
- if (parts[0] !== "v1" || parts[1] !== "sessions") return json({}, 404);
109
- if (parts.length === 2) {
110
- if (request.method === "GET") return json([...sessions.values()].map(row));
111
- if (request.method !== "POST") return json({}, 404);
112
- switch (create._tag) {
113
- case "Reject": return json({ error: "rejected" }, create.status, create.retryAfterMillis === void 0 ? {} : { "retry-after": String(Math.max(1, Math.ceil(create.retryAfterMillis / 1e3))) });
114
- case "Malformed": return json({ id: 12 });
115
- case "Lost": throw new Error("The scripted provider lost this reply");
116
- case "Accept": {
117
- const body = await request.json().catch(() => void 0);
118
- const session = {
119
- id: `session-${++serial}`,
120
- keepAlive: typeof body === "object" && body !== null && "keepAlive" in body && body.keepAlive === true,
121
- status: create.status ?? "RUNNING",
122
- releaseRequests: 0,
123
- uploads: []
124
- };
125
- sessions.set(session.id, session);
126
- return json({
127
- id: session.id,
128
- projectId
129
- });
130
- }
131
- }
132
- }
133
- const session = sessions.get(parts[2] ?? "");
134
- if (session === void 0) return json({ error: "not found" }, 404);
135
- if (parts.length === 3) {
136
- if (request.method === "GET") return json(row(session));
137
- if (request.method !== "POST") return json({}, 404);
138
- session.releaseRequests++;
139
- switch (fixed.release ?? "confirmed") {
140
- case "confirmed":
141
- session.status = "COMPLETED";
142
- return json(row(session));
143
- case "pending": return json(row(session));
144
- case "failed": return json({ error: "release failed" }, 500);
145
- }
146
- }
147
- if (parts[3] === "debug" && request.method === "GET") return fixed.liveView === "denied" ? json({ error: "denied" }, 403) : json({
148
- debuggerFullscreenUrl: `https://www.browserbase.com/devtools-fullscreen/inspector.html?scripted=${secrets.liveView}`,
149
- pages: []
150
- });
151
- if (parts[3] === "uploads" && request.method === "POST") {
152
- const file = (await request.formData()).get("file");
153
- session.uploads.push(file instanceof File ? file.name : "upload");
154
- return json({ message: "File uploaded successfully" });
155
- }
156
- return json({}, 404);
157
- };
158
- return {
159
- fetch,
160
- control: {
161
- projectId,
162
- calls: Effect.sync(() => calls.map((call) => Object.freeze({ ...call }))),
163
- sessions: Effect.sync(() => [...sessions.values()].map((session) => Object.freeze({
164
- id: session.id,
165
- status: session.status,
166
- releaseRequests: session.releaseRequests,
167
- uploads: Object.freeze([...session.uploads])
168
- }))),
169
- setStatus: (sessionId, status) => Effect.sync(() => {
170
- const session = sessions.get(sessionId);
171
- if (session !== void 0) session.status = status;
172
- }),
173
- secrets
174
- }
175
- };
176
- });
177
- /** The scripted provider and the scripted engines behind one `layer`, for a test to inspect. */
178
- var ScriptedBrowserbase = class extends Context.Service()("effect-browserbase/testing/ScriptedBrowserbase") {};
179
- /**
180
- * The real account and browser Layers over the scripted provider and the scripted engine.
181
- * Allocation attempts, release reconciliation, cleanup receipts, `onCleanup`,
182
- * `onAllocationUncertain`, borrowed attachment and every resource service run for real;
183
- * only the provider's replies and the browser's pages are scripted. Attempt ids and handoff
184
- * tokens come from `sequentialCrypto` in `effect-browser/testing`, so they are predictable too.
185
- */
186
- const layer = (options) => Layer.unwrap(Effect.gen(function* () {
187
- const scripted = yield* provider(options.provider);
188
- const engine = yield* BrowserTesting.binding(options.browser);
189
- const account = layer$1({
190
- projectId: scripted.control.projectId,
191
- apiKey: Redacted.make(scripted.control.secrets.apiKey)
192
- }).pipe(Layer.provide(Layer.succeed(FetchHttpClient.Fetch, scripted.fetch)));
193
- const browser = BrowserbaseBrowser.layer({
194
- launch: options.launch ?? recipe(),
195
- ...options.options
196
- }).pipe(Layer.provide(account), Layer.provide(layer$2(engine.binding)), Layer.provide(BrowserTesting.sequentialCrypto));
197
- const control = Layer.succeed(ScriptedBrowserbase, ScriptedBrowserbase.of({
198
- provider: scripted.control,
199
- browsers: engine.browsers
200
- }));
201
- return Layer.mergeAll(browser, account, control);
202
- }));
203
- //#endregion
204
- export { ProviderScript, ScriptedBrowserbase, layer, provider };
205
-
206
- //# sourceMappingURL=Testing.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Testing.mjs","names":["accountLayer","bindingLayer"],"sources":["../src/Testing.ts"],"sourcesContent":["import { Context, Effect, Layer, Redacted, Schema } from \"effect\";\nimport type { BrowserError } from \"effect-browser/errors\";\nimport * as BrowserTesting from \"effect-browser/testing\";\nimport { FetchHttpClient } from \"effect/unstable/http\";\n\nimport { type Services as AccountServices, layer as accountLayer } from \"./Account.ts\";\nimport { BrowserbaseBrowser, type BrowserOptions } from \"./Browser.ts\";\nimport { layer as bindingLayer } from \"./BrowserBinding.ts\";\nimport { ClientError } from \"./Errors.ts\";\nimport { type LaunchRecipe, recipe } from \"./Launch.ts\";\nimport { Identifier } from \"./References.ts\";\nimport { ProviderSession, SessionStatus } from \"./SessionData.ts\";\n\n/**\n * How the scripted control plane answers. Everything else about a session is what the real\n * `Client`, `Sessions`, allocation and cleanup code makes of those answers.\n */\nexport const ProviderScript = Schema.Struct({\n projectId: Schema.optionalKey(Identifier),\n /**\n * The creation reply: `Accept` allocates a session (running unless told otherwise), `Reject`\n * refuses with a status the real transport classifies, `Malformed` replies without an\n * identity, and `Lost` fails the request after it was sent, so the outcome is unknown.\n */\n create: Schema.optionalKey(\n Schema.Union([\n Schema.TaggedStruct(\"Accept\", { status: Schema.optionalKey(SessionStatus) }),\n Schema.TaggedStruct(\"Reject\", {\n status: Schema.Int.check(Schema.isBetween({ minimum: 400, maximum: 599 })),\n retryAfterMillis: Schema.optionalKey(Schema.Natural),\n }),\n Schema.TaggedStruct(\"Malformed\", {}),\n Schema.TaggedStruct(\"Lost\", {}),\n ]),\n ),\n /** What a release request does: the session ends, stays running, or the request fails. */\n release: Schema.optionalKey(Schema.Literals([\"confirmed\", \"pending\", \"failed\"])),\n liveView: Schema.optionalKey(Schema.Literals([\"issued\", \"denied\"])),\n region: Schema.optionalKey(ProviderSession.fields.region),\n /**\n * Whose identity a session's retrieval and release replies carry: its own, another session's\n * or another project's. The real client refuses a reply it cannot match, so a test can see\n * what allocation, connection and cleanup make of a provider that answers for someone else.\n */\n identity: Schema.optionalKey(Schema.Literals([\"own\", \"foreign-session\", \"foreign-project\"])),\n});\n\nexport type ProviderScript = typeof ProviderScript.Type;\n\nexport interface ProviderCall {\n readonly method: string;\n readonly path: string;\n}\n\nexport interface ProviderSessionRecord {\n readonly id: string;\n readonly status: SessionStatus;\n readonly releaseRequests: number;\n readonly uploads: ReadonlyArray<string>;\n}\n\nexport interface ProviderControl {\n readonly projectId: string;\n readonly calls: Effect.Effect<ReadonlyArray<ProviderCall>>;\n readonly sessions: Effect.Effect<ReadonlyArray<ProviderSessionRecord>>;\n /** Move a session as the provider might while it is connected, for example to `TIMED_OUT`. */\n readonly setStatus: (sessionId: string, status: SessionStatus) => Effect.Effect<void>;\n /** Marker strings the double uses where real secrets would be, to grep evidence for leaks. */\n readonly secrets: {\n readonly apiKey: string;\n readonly connectUrl: string;\n readonly liveView: string;\n };\n}\n\n/** A scripted control plane: a `FetchHttpClient.Fetch` and the handle that inspects it. */\nexport interface ScriptedProvider {\n readonly fetch: typeof globalThis.fetch;\n readonly control: ProviderControl;\n}\n\nconst secrets = {\n apiKey: \"SCRIPTED-API-KEY-NOT-A-CREDENTIAL\",\n connectUrl: \"SCRIPTED-CONNECT-KEY\",\n liveView: \"SCRIPTED-LIVE-VIEW\",\n} as const;\n\ninterface SessionRow {\n readonly id: string;\n readonly keepAlive: boolean;\n status: SessionStatus;\n releaseRequests: number;\n readonly uploads: Array<string>;\n}\n\nconst json = (body: unknown, status = 200, headers: Record<string, string> = {}) =>\n new Response(JSON.stringify(body), {\n status,\n headers: { \"content-type\": \"application/json\", ...headers },\n });\n\n/**\n * The reviewed session subset of the provider API, answered in memory: creation, retrieval,\n * listing, release, Live View issuance and uploads. Provide it as `FetchHttpClient.Fetch`\n * beneath the real `BrowserbaseClient.layer`, or use `layer` below, which does that.\n */\nexport const provider = Effect.fnUntraced(function* (\n script: ProviderScript = {},\n): Effect.fn.Return<ScriptedProvider, ClientError> {\n const fixed = yield* Schema.decodeEffect(ProviderScript)(script, {\n onExcessProperty: \"error\",\n }).pipe(\n Effect.mapError(() =>\n ClientError.make({\n operation: \"configure\",\n reason: \"configuration\",\n outcome: \"undispatched\",\n }),\n ),\n );\n\n const projectId = fixed.projectId ?? \"project-1\";\n const region = fixed.region ?? \"us-east-1\";\n const calls: Array<ProviderCall> = [];\n const sessions = new Map<string, SessionRow>();\n let serial = 0;\n\n const identity = fixed.identity ?? \"own\";\n\n const row = (session: SessionRow) => ({\n id: identity === \"foreign-session\" ? `${session.id}-other` : session.id,\n projectId: identity === \"foreign-project\" ? `${projectId}-other` : projectId,\n status: session.status,\n createdAt: \"2026-01-01T00:00:00.000Z\",\n updatedAt: \"2026-01-01T00:00:01.000Z\",\n expiresAt: \"2026-01-01T01:00:00.000Z\",\n startedAt: \"2026-01-01T00:00:01.000Z\",\n ...(session.status === \"COMPLETED\" ||\n session.status === \"ERROR\" ||\n session.status === \"TIMED_OUT\"\n ? { endedAt: \"2026-01-01T00:10:00.000Z\" }\n : {}),\n keepAlive: session.keepAlive,\n proxyBytes: 0,\n region,\n connectUrl: `wss://connect.browserbase.com/?session=${session.id}&key=${secrets.connectUrl}`,\n });\n\n const create = fixed.create ?? { _tag: \"Accept\" as const };\n\n // oxlint-disable-next-line effecttsgo/async-function -- stands in for the platform's fetch\n const fetch: typeof globalThis.fetch = async (input, init) => {\n const request = new Request(input, init);\n const url = new URL(request.url);\n\n calls.push({ method: request.method, path: url.pathname });\n if (url.origin !== \"https://api.browserbase.com\")\n return json({ error: \"unexpected origin\" }, 404);\n if (request.headers.get(\"x-bb-api-key\") !== secrets.apiKey)\n return json({ error: \"unauthorized\" }, 401);\n const parts = url.pathname.split(\"/\").filter((part) => part.length > 0);\n\n if (parts[0] !== \"v1\" || parts[1] !== \"sessions\") return json({}, 404);\n if (parts.length === 2) {\n if (request.method === \"GET\") return json([...sessions.values()].map(row));\n if (request.method !== \"POST\") return json({}, 404);\n switch (create._tag) {\n case \"Reject\":\n return json(\n { error: \"rejected\" },\n create.status,\n create.retryAfterMillis === undefined\n ? {}\n : { \"retry-after\": String(Math.max(1, Math.ceil(create.retryAfterMillis / 1000))) },\n );\n case \"Malformed\":\n return json({ id: 12 });\n case \"Lost\":\n throw new Error(\"The scripted provider lost this reply\");\n case \"Accept\": {\n const body: unknown = await request.json().catch(() => undefined);\n\n const session: SessionRow = {\n id: `session-${++serial}`,\n keepAlive:\n typeof body === \"object\" &&\n body !== null &&\n \"keepAlive\" in body &&\n body.keepAlive === true,\n status: create.status ?? \"RUNNING\",\n releaseRequests: 0,\n uploads: [],\n };\n\n sessions.set(session.id, session);\n\n return json({ id: session.id, projectId });\n }\n }\n }\n const session = sessions.get(parts[2] ?? \"\");\n\n if (session === undefined) return json({ error: \"not found\" }, 404);\n if (parts.length === 3) {\n if (request.method === \"GET\") return json(row(session));\n if (request.method !== \"POST\") return json({}, 404);\n session.releaseRequests++;\n switch (fixed.release ?? \"confirmed\") {\n case \"confirmed\":\n session.status = \"COMPLETED\";\n\n return json(row(session));\n case \"pending\":\n return json(row(session));\n case \"failed\":\n return json({ error: \"release failed\" }, 500);\n }\n }\n if (parts[3] === \"debug\" && request.method === \"GET\")\n return fixed.liveView === \"denied\"\n ? json({ error: \"denied\" }, 403)\n : json({\n debuggerFullscreenUrl: `https://www.browserbase.com/devtools-fullscreen/inspector.html?scripted=${secrets.liveView}`,\n pages: [],\n });\n if (parts[3] === \"uploads\" && request.method === \"POST\") {\n const file = (await request.formData()).get(\"file\");\n\n session.uploads.push(file instanceof File ? file.name : \"upload\");\n\n return json({ message: \"File uploaded successfully\" });\n }\n\n return json({}, 404);\n };\n\n const control: ProviderControl = {\n projectId,\n calls: Effect.sync(() => calls.map((call) => Object.freeze({ ...call }))),\n sessions: Effect.sync(() =>\n [...sessions.values()].map((session) =>\n Object.freeze({\n id: session.id,\n status: session.status,\n releaseRequests: session.releaseRequests,\n uploads: Object.freeze([...session.uploads]),\n }),\n ),\n ),\n setStatus: (sessionId, status) =>\n Effect.sync(() => {\n const session = sessions.get(sessionId);\n\n if (session !== undefined) session.status = status;\n }),\n secrets,\n };\n\n return { fetch, control };\n});\n\n/** The scripted provider and the scripted engines behind one `layer`, for a test to inspect. */\nexport class ScriptedBrowserbase extends Context.Service<\n ScriptedBrowserbase,\n {\n readonly provider: ProviderControl;\n /**\n * One control for each scripted browser the Layer connected to, in first-connection order:\n * one per provider session, which a keep-alive reconnection or a borrowed attachment reaches\n * again.\n */\n readonly browsers: Effect.Effect<ReadonlyArray<BrowserTesting.ScriptedControl>>;\n }\n>()(\"effect-browserbase/testing/ScriptedBrowserbase\") {}\n\nexport interface ScriptedLayerOptions {\n readonly browser: BrowserTesting.Script;\n readonly provider?: ProviderScript;\n /** Defaults to `recipe()`: a five-minute provider lifetime and a provider-managed viewport. */\n readonly launch?: LaunchRecipe;\n readonly options?: Omit<BrowserOptions, \"launch\">;\n}\n\n/**\n * The real account and browser Layers over the scripted provider and the scripted engine.\n * Allocation attempts, release reconciliation, cleanup receipts, `onCleanup`,\n * `onAllocationUncertain`, borrowed attachment and every resource service run for real;\n * only the provider's replies and the browser's pages are scripted. Attempt ids and handoff\n * tokens come from `sequentialCrypto` in `effect-browser/testing`, so they are predictable too.\n */\nexport const layer = (\n options: ScriptedLayerOptions,\n): Layer.Layer<\n BrowserbaseBrowser | AccountServices | ScriptedBrowserbase,\n BrowserError | ClientError\n> =>\n Layer.unwrap(\n Effect.gen(function* () {\n const scripted = yield* provider(options.provider);\n const engine = yield* BrowserTesting.binding(options.browser);\n\n const account = accountLayer({\n projectId: scripted.control.projectId,\n apiKey: Redacted.make(scripted.control.secrets.apiKey),\n }).pipe(Layer.provide(Layer.succeed(FetchHttpClient.Fetch, scripted.fetch)));\n\n const browser = BrowserbaseBrowser.layer({\n launch: options.launch ?? recipe(),\n ...options.options,\n }).pipe(\n Layer.provide(account),\n Layer.provide(bindingLayer(engine.binding)),\n Layer.provide(BrowserTesting.sequentialCrypto),\n );\n\n const control = Layer.succeed(\n ScriptedBrowserbase,\n ScriptedBrowserbase.of({ provider: scripted.control, browsers: engine.browsers }),\n );\n\n return Layer.mergeAll(browser, account, control);\n }),\n );\n"],"mappings":"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"}
@@ -1,25 +0,0 @@
1
- import { ArtifactTransferPolicy } from "./Transfers.mjs";
2
- import { Effect, Schema } from "effect";
3
- //#region src/internal/artifact/TransferPolicy.ts
4
- const DownloadTransferPolicy = Schema.Struct({
5
- ...ArtifactTransferPolicy.fields,
6
- mimeTypes: Schema.Array(Schema.String).check(Schema.isMinLength(1), Schema.isMaxLength(32))
7
- });
8
- /**
9
- * Own the validated scalars before crossing an asynchronous provider boundary. The caller
10
- * supplies its own declared failure so recordings, replays and files keep separate channels.
11
- */
12
- const transferPolicy = (policy, invalid) => Schema.decodeEffect(ArtifactTransferPolicy)(policy).pipe(Effect.map(({ maxBytes, timeoutMillis }) => Object.freeze({
13
- maxBytes,
14
- timeoutMillis: timeoutMillis ?? 6e4
15
- })), Effect.mapError(invalid));
16
- /** Preserve the existing case-sensitive allowlist while owning its contents. */
17
- const downloadTransferPolicy = (policy, invalid) => Schema.decodeEffect(DownloadTransferPolicy)(policy).pipe(Effect.map(({ maxBytes, timeoutMillis, mimeTypes }) => Object.freeze({
18
- maxBytes,
19
- timeoutMillis: timeoutMillis ?? 6e4,
20
- mimeTypes: Object.freeze([...mimeTypes])
21
- })), Effect.mapError(invalid));
22
- //#endregion
23
- export { transferPolicy as n, downloadTransferPolicy as t };
24
-
25
- //# sourceMappingURL=TransferPolicy-DQ06i7SZ.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"TransferPolicy-DQ06i7SZ.mjs","names":[],"sources":["../src/internal/artifact/TransferPolicy.ts"],"sourcesContent":["import { Effect, Schema } from \"effect\";\n\nimport { ArtifactTransferPolicy } from \"../../Transfers.ts\";\n\nconst DownloadTransferPolicy = Schema.Struct({\n ...ArtifactTransferPolicy.fields,\n mimeTypes: Schema.Array(Schema.String).check(Schema.isMinLength(1), Schema.isMaxLength(32)),\n});\n\n/**\n * Own the validated scalars before crossing an asynchronous provider boundary. The caller\n * supplies its own declared failure so recordings, replays and files keep separate channels.\n */\nexport const transferPolicy = <E>(policy: ArtifactTransferPolicy, invalid: () => E) =>\n Schema.decodeEffect(ArtifactTransferPolicy)(policy).pipe(\n Effect.map(({ maxBytes, timeoutMillis }) =>\n Object.freeze({ maxBytes, timeoutMillis: timeoutMillis ?? 60_000 }),\n ),\n Effect.mapError(invalid),\n );\n\n/** Preserve the existing case-sensitive allowlist while owning its contents. */\nexport const downloadTransferPolicy = <E>(\n policy: ArtifactTransferPolicy & { readonly mimeTypes: ReadonlyArray<string> },\n invalid: () => E,\n) =>\n Schema.decodeEffect(DownloadTransferPolicy)(policy).pipe(\n Effect.map(({ maxBytes, timeoutMillis, mimeTypes }) =>\n Object.freeze({\n maxBytes,\n timeoutMillis: timeoutMillis ?? 60_000,\n mimeTypes: Object.freeze([...mimeTypes]),\n }),\n ),\n Effect.mapError(invalid),\n );\n"],"mappings":";;;AAIA,MAAM,yBAAyB,OAAO,OAAO;CAC3C,GAAG,uBAAuB;CAC1B,WAAW,OAAO,MAAM,OAAO,MAAM,CAAC,CAAC,MAAM,OAAO,YAAY,CAAC,GAAG,OAAO,YAAY,EAAE,CAAC;AAC5F,CAAC;;;;;AAMD,MAAa,kBAAqB,QAAgC,YAChE,OAAO,aAAa,sBAAsB,CAAC,CAAC,MAAM,CAAC,CAAC,KAClD,OAAO,KAAK,EAAE,UAAU,oBACtB,OAAO,OAAO;CAAE;CAAU,eAAe,iBAAiB;AAAO,CAAC,CACpE,GACA,OAAO,SAAS,OAAO,CACzB;;AAGF,MAAa,0BACX,QACA,YAEA,OAAO,aAAa,sBAAsB,CAAC,CAAC,MAAM,CAAC,CAAC,KAClD,OAAO,KAAK,EAAE,UAAU,eAAe,gBACrC,OAAO,OAAO;CACZ;CACA,eAAe,iBAAiB;CAChC,WAAW,OAAO,OAAO,CAAC,GAAG,SAAS,CAAC;AACzC,CAAC,CACH,GACA,OAAO,SAAS,OAAO,CACzB"}
@@ -1,89 +0,0 @@
1
- import { a as SessionReference } from "./References-DOH3Wu44.mjs";
2
- import { Schema } from "effect";
3
- import { InlineFile, PositiveInt, SafeFilename as SafeFilename$1 } from "effect-browser/browser-data";
4
- //#region src/Transfers.d.ts
5
- /** Shared caller-owned transfer bounds. Omitted timeout retains the 60-second default. */
6
- declare const ArtifactTransferPolicy: Schema.Struct<{
7
- readonly maxBytes: Schema.Int;
8
- readonly timeoutMillis: Schema.optionalKey<Schema.Int>;
9
- }>;
10
- type ArtifactTransferPolicy = typeof ArtifactTransferPolicy.Type;
11
- /**
12
- * A path the provider reported for a file it already holds. It is only ever decoded from a
13
- * provider response: no caller, and certainly no model, names a remote filesystem path.
14
- */
15
- declare const RemoteFilePath: Schema.NonEmptyString;
16
- declare const UploadReceipt_base: Schema.Class<UploadReceipt, Schema.Struct<{
17
- readonly reference: typeof SessionReference;
18
- readonly filename: Schema.NonEmptyString;
19
- readonly bytes: Schema.Natural;
20
- readonly remotePath: Schema.optionalKey<Schema.NonEmptyString>;
21
- readonly acknowledgement: Schema.optionalKey<Schema.String>;
22
- }>, {}>;
23
- /**
24
- * Evidence of one accepted upload. `remotePath` exists only when the provider returned one;
25
- * its absence is reported rather than guessed, and attachment is refused without it.
26
- */
27
- declare class UploadReceipt extends UploadReceipt_base {}
28
- declare const DownloadMetadata_base: Schema.Class<DownloadMetadata, Schema.Struct<{
29
- readonly id: Schema.NonEmptyString;
30
- readonly sessionId: Schema.NonEmptyString;
31
- readonly filename: Schema.NonEmptyString;
32
- readonly mimeType: Schema.NonEmptyString;
33
- readonly size: Schema.Natural;
34
- readonly checksum: Schema.String;
35
- readonly createdAt: Schema.NonEmptyString;
36
- }>, {}>;
37
- declare class DownloadMetadata extends DownloadMetadata_base {}
38
- declare const DownloadObservation_base: Schema.Class<DownloadObservation, Schema.Struct<{
39
- readonly reference: typeof SessionReference;
40
- readonly downloadId: Schema.NonEmptyString;
41
- readonly filename: Schema.NonEmptyString;
42
- readonly state: Schema.Literals<readonly ["started", "completed", "failed", "unknown"]>;
43
- }>, {}>;
44
- /** A native event identity is not a provider download ID. Associate provider metadata explicitly. */
45
- declare class DownloadObservation extends DownloadObservation_base {}
46
- declare const RecordingPageReference_base: Schema.Class<RecordingPageReference, Schema.Struct<{
47
- readonly session: typeof SessionReference;
48
- readonly pageId: Schema.NonEmptyString;
49
- }>, {}>;
50
- declare class RecordingPageReference extends RecordingPageReference_base {}
51
- declare const RecordingPage_base: Schema.Class<RecordingPage, Schema.Struct<{
52
- readonly pageId: Schema.NonEmptyString;
53
- readonly status: Schema.Literals<readonly ["NOT_REQUESTED", "PENDING", "COMPLETED", "FAILED"]>;
54
- readonly delivery: Schema.Literals<readonly ["not-ready", "download", "external-storage"]>;
55
- readonly completedAt: Schema.optionalKey<Schema.String>;
56
- }>, {}>;
57
- declare class RecordingPage extends RecordingPage_base {}
58
- declare const RecordingBatch_base: Schema.Class<RecordingBatch, Schema.Struct<{
59
- readonly reference: typeof SessionReference;
60
- readonly pages: Schema.$Array<typeof RecordingPage>;
61
- readonly timedOut: Schema.Boolean;
62
- }>, {}>;
63
- declare class RecordingBatch extends RecordingBatch_base {}
64
- declare const ReplayPage_base: Schema.Class<ReplayPage, Schema.Struct<{
65
- readonly pageId: Schema.NonEmptyString;
66
- readonly startTimeMs: Schema.Finite;
67
- readonly endTimeMs: Schema.Finite;
68
- }>, {}>;
69
- declare class ReplayPage extends ReplayPage_base {}
70
- /**
71
- * The uploaded branch carries receipts, not paths. Attachment authority is the identity of a
72
- * receipt this package issued for this exact session, so decoding one back into a new value
73
- * would discard the very evidence being checked.
74
- */
75
- type FileSelection = {
76
- readonly _tag: "Inline";
77
- readonly files: ReadonlyArray<InlineFile>;
78
- } | {
79
- readonly _tag: "Uploaded";
80
- readonly uploads: ReadonlyArray<UploadReceipt>;
81
- };
82
- interface SelectFilesRequest {
83
- readonly selector: string;
84
- readonly selection: FileSelection;
85
- }
86
- //#endregion
87
- export { PositiveInt as a, RecordingPageReference as c, SafeFilename$1 as d, SelectFilesRequest as f, FileSelection as i, RemoteFilePath as l, DownloadMetadata as n, RecordingBatch as o, UploadReceipt as p, DownloadObservation as r, RecordingPage as s, ArtifactTransferPolicy as t, ReplayPage as u };
88
- export {};
89
- //# sourceMappingURL=Transfers-GVqzrDWl.d.mts.map
@@ -1,3 +0,0 @@
1
- import { a as PositiveInt, c as RecordingPageReference, d as SafeFilename, f as SelectFilesRequest, i as FileSelection, l as RemoteFilePath, n as DownloadMetadata, o as RecordingBatch, p as UploadReceipt, r as DownloadObservation, s as RecordingPage, t as ArtifactTransferPolicy, u as ReplayPage } from "./Transfers-GVqzrDWl.mjs";
2
- export { ArtifactTransferPolicy, DownloadMetadata, DownloadObservation, FileSelection, PositiveInt, RecordingBatch, RecordingPage, RecordingPageReference, RemoteFilePath, ReplayPage, SafeFilename, SelectFilesRequest, UploadReceipt };
3
- export {};
@@ -1,85 +0,0 @@
1
- import { Identifier as Identifier$1, SessionReference } from "./References.mjs";
2
- import { Schema } from "effect";
3
- import { PositiveInt, SafeFilename, SafeFilename as SafeFilename$1 } from "effect-browser/browser-data";
4
- //#region src/Transfers.ts
5
- /** Shared caller-owned transfer bounds. Omitted timeout retains the 60-second default. */
6
- const ArtifactTransferPolicy = Schema.Struct({
7
- maxBytes: Schema.Int.check(Schema.isBetween({
8
- minimum: 1,
9
- maximum: 2 ** 31 - 1
10
- })),
11
- timeoutMillis: Schema.optionalKey(Schema.Int.check(Schema.isBetween({
12
- minimum: 1,
13
- maximum: 6e5
14
- })))
15
- });
16
- /**
17
- * A path the provider reported for a file it already holds. It is only ever decoded from a
18
- * provider response: no caller, and certainly no model, names a remote filesystem path.
19
- */
20
- const RemoteFilePath = Schema.NonEmptyString.check(Schema.isMaxLength(4096), Schema.makeFilter((value) => value.startsWith("/") && !value.includes("\\") && !value.includes("//") && ![...value].some((character) => character < " " || character === "") && value.split("/").every((segment) => segment !== "." && segment !== ".."), { title: "an absolute, traversal-free remote path" }));
21
- /**
22
- * Evidence of one accepted upload. `remotePath` exists only when the provider returned one;
23
- * its absence is reported rather than guessed, and attachment is refused without it.
24
- */
25
- var UploadReceipt = class extends Schema.Class("BrowserbaseUploadReceipt")({
26
- reference: SessionReference,
27
- filename: SafeFilename$1,
28
- bytes: Schema.Natural,
29
- remotePath: Schema.optionalKey(RemoteFilePath),
30
- acknowledgement: Schema.optionalKey(Schema.String.check(Schema.isMaxLength(1024)))
31
- }) {};
32
- var DownloadMetadata = class extends Schema.Class("BrowserbaseDownloadMetadata")({
33
- id: Identifier$1,
34
- sessionId: Identifier$1,
35
- filename: SafeFilename$1,
36
- mimeType: Schema.NonEmptyString.check(Schema.isMaxLength(128)),
37
- size: Schema.Natural,
38
- checksum: Schema.String.check(Schema.isPattern(/^[a-fA-F0-9]{64}$/)),
39
- createdAt: Schema.NonEmptyString.check(Schema.isMaxLength(64))
40
- }) {};
41
- /** A native event identity is not a provider download ID. Associate provider metadata explicitly. */
42
- var DownloadObservation = class extends Schema.Class("BrowserbaseDownloadObservation")({
43
- reference: SessionReference,
44
- downloadId: Identifier$1,
45
- filename: SafeFilename$1,
46
- state: Schema.Literals([
47
- "started",
48
- "completed",
49
- "failed",
50
- "unknown"
51
- ])
52
- }) {};
53
- var RecordingPageReference = class extends Schema.Class("BrowserbaseRecordingPageReference")({
54
- session: SessionReference,
55
- pageId: Identifier$1
56
- }) {};
57
- var RecordingPage = class extends Schema.Class("BrowserbaseRecordingPage")({
58
- pageId: Identifier$1,
59
- status: Schema.Literals([
60
- "NOT_REQUESTED",
61
- "PENDING",
62
- "COMPLETED",
63
- "FAILED"
64
- ]),
65
- delivery: Schema.Literals([
66
- "not-ready",
67
- "download",
68
- "external-storage"
69
- ]),
70
- completedAt: Schema.optionalKey(Schema.String.check(Schema.isMaxLength(64)))
71
- }) {};
72
- var RecordingBatch = class extends Schema.Class("BrowserbaseRecordingBatch")({
73
- reference: SessionReference,
74
- pages: Schema.Array(RecordingPage).check(Schema.isMaxLength(256)),
75
- timedOut: Schema.Boolean
76
- }) {};
77
- var ReplayPage = class extends Schema.Class("BrowserbaseReplayPage")({
78
- pageId: Identifier$1,
79
- startTimeMs: Schema.Finite,
80
- endTimeMs: Schema.Finite
81
- }) {};
82
- //#endregion
83
- export { ArtifactTransferPolicy, DownloadMetadata, DownloadObservation, PositiveInt, RecordingBatch, RecordingPage, RecordingPageReference, RemoteFilePath, ReplayPage, SafeFilename, UploadReceipt };
84
-
85
- //# sourceMappingURL=Transfers.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Transfers.mjs","names":["SafeFilename","Identifier"],"sources":["../src/Transfers.ts"],"sourcesContent":["import { Schema } from \"effect\";\nimport { type InlineFile, SafeFilename } from \"effect-browser/browser-data\";\n\nexport { PositiveInt, SafeFilename } from \"effect-browser/browser-data\";\n\nimport { Identifier, SessionReference } from \"./References.ts\";\n\n/** Shared caller-owned transfer bounds. Omitted timeout retains the 60-second default. */\nexport const ArtifactTransferPolicy = Schema.Struct({\n maxBytes: Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 2 ** 31 - 1 })),\n timeoutMillis: Schema.optionalKey(\n Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 600_000 })),\n ),\n});\n\nexport type ArtifactTransferPolicy = typeof ArtifactTransferPolicy.Type;\n\n/**\n * A path the provider reported for a file it already holds. It is only ever decoded from a\n * provider response: no caller, and certainly no model, names a remote filesystem path.\n */\nexport const RemoteFilePath = Schema.NonEmptyString.check(\n Schema.isMaxLength(4096),\n Schema.makeFilter(\n (value) =>\n value.startsWith(\"/\") &&\n !value.includes(\"\\\\\") &&\n !value.includes(\"//\") &&\n ![...value].some((character) => character < \" \" || character === \"\\x7f\") &&\n value.split(\"/\").every((segment) => segment !== \".\" && segment !== \"..\"),\n { title: \"an absolute, traversal-free remote path\" },\n ),\n);\n\n/**\n * Evidence of one accepted upload. `remotePath` exists only when the provider returned one;\n * its absence is reported rather than guessed, and attachment is refused without it.\n */\nexport class UploadReceipt extends Schema.Class<UploadReceipt>(\"BrowserbaseUploadReceipt\")({\n reference: SessionReference,\n filename: SafeFilename,\n bytes: Schema.Natural,\n remotePath: Schema.optionalKey(RemoteFilePath),\n acknowledgement: Schema.optionalKey(Schema.String.check(Schema.isMaxLength(1024))),\n}) {}\n\nexport class DownloadMetadata extends Schema.Class<DownloadMetadata>(\"BrowserbaseDownloadMetadata\")(\n {\n id: Identifier,\n sessionId: Identifier,\n filename: SafeFilename,\n mimeType: Schema.NonEmptyString.check(Schema.isMaxLength(128)),\n size: Schema.Natural,\n checksum: Schema.String.check(Schema.isPattern(/^[a-fA-F0-9]{64}$/)),\n createdAt: Schema.NonEmptyString.check(Schema.isMaxLength(64)),\n },\n) {}\n\n/** A native event identity is not a provider download ID. Associate provider metadata explicitly. */\nexport class DownloadObservation extends Schema.Class<DownloadObservation>(\n \"BrowserbaseDownloadObservation\",\n)({\n reference: SessionReference,\n downloadId: Identifier,\n filename: SafeFilename,\n state: Schema.Literals([\"started\", \"completed\", \"failed\", \"unknown\"]),\n}) {}\n\nexport class RecordingPageReference extends Schema.Class<RecordingPageReference>(\n \"BrowserbaseRecordingPageReference\",\n)({\n session: SessionReference,\n pageId: Identifier,\n}) {}\n\nexport class RecordingPage extends Schema.Class<RecordingPage>(\"BrowserbaseRecordingPage\")({\n pageId: Identifier,\n status: Schema.Literals([\"NOT_REQUESTED\", \"PENDING\", \"COMPLETED\", \"FAILED\"]),\n delivery: Schema.Literals([\"not-ready\", \"download\", \"external-storage\"]),\n completedAt: Schema.optionalKey(Schema.String.check(Schema.isMaxLength(64))),\n}) {}\n\nexport class RecordingBatch extends Schema.Class<RecordingBatch>(\"BrowserbaseRecordingBatch\")({\n reference: SessionReference,\n pages: Schema.Array(RecordingPage).check(Schema.isMaxLength(256)),\n timedOut: Schema.Boolean,\n}) {}\n\nexport class ReplayPage extends Schema.Class<ReplayPage>(\"BrowserbaseReplayPage\")({\n pageId: Identifier,\n startTimeMs: Schema.Finite,\n endTimeMs: Schema.Finite,\n}) {}\n\n/**\n * The uploaded branch carries receipts, not paths. Attachment authority is the identity of a\n * receipt this package issued for this exact session, so decoding one back into a new value\n * would discard the very evidence being checked.\n */\nexport type FileSelection =\n | { readonly _tag: \"Inline\"; readonly files: ReadonlyArray<InlineFile> }\n | { readonly _tag: \"Uploaded\"; readonly uploads: ReadonlyArray<UploadReceipt> };\n\nexport interface SelectFilesRequest {\n readonly selector: string;\n readonly selection: FileSelection;\n}\n"],"mappings":";;;;;AAQA,MAAa,yBAAyB,OAAO,OAAO;CAClD,UAAU,OAAO,IAAI,MAAM,OAAO,UAAU;EAAE,SAAS;EAAG,SAAS,KAAK,KAAK;CAAE,CAAC,CAAC;CACjF,eAAe,OAAO,YACpB,OAAO,IAAI,MAAM,OAAO,UAAU;EAAE,SAAS;EAAG,SAAS;CAAQ,CAAC,CAAC,CACrE;AACF,CAAC;;;;;AAQD,MAAa,iBAAiB,OAAO,eAAe,MAClD,OAAO,YAAY,IAAI,GACvB,OAAO,YACJ,UACC,MAAM,WAAW,GAAG,KACpB,CAAC,MAAM,SAAS,IAAI,KACpB,CAAC,MAAM,SAAS,IAAI,KACpB,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,MAAM,cAAc,YAAY,OAAO,cAAc,GAAM,KACvE,MAAM,MAAM,GAAG,CAAC,CAAC,OAAO,YAAY,YAAY,OAAO,YAAY,IAAI,GACzE,EAAE,OAAO,0CAA0C,CACrD,CACF;;;;;AAMA,IAAa,gBAAb,cAAmC,OAAO,MAAqB,0BAA0B,CAAC,CAAC;CACzF,WAAW;CACX,UAAUA;CACV,OAAO,OAAO;CACd,YAAY,OAAO,YAAY,cAAc;CAC7C,iBAAiB,OAAO,YAAY,OAAO,OAAO,MAAM,OAAO,YAAY,IAAI,CAAC,CAAC;AACnF,CAAC,CAAC,CAAC,CAAC;AAEJ,IAAa,mBAAb,cAAsC,OAAO,MAAwB,6BAA6B,CAAC,CACjG;CACE,IAAIC;CACJ,WAAWA;CACX,UAAUD;CACV,UAAU,OAAO,eAAe,MAAM,OAAO,YAAY,GAAG,CAAC;CAC7D,MAAM,OAAO;CACb,UAAU,OAAO,OAAO,MAAM,OAAO,UAAU,mBAAmB,CAAC;CACnE,WAAW,OAAO,eAAe,MAAM,OAAO,YAAY,EAAE,CAAC;AAC/D,CACF,CAAC,CAAC,CAAC;;AAGH,IAAa,sBAAb,cAAyC,OAAO,MAC9C,gCACF,CAAC,CAAC;CACA,WAAW;CACX,YAAYC;CACZ,UAAUD;CACV,OAAO,OAAO,SAAS;EAAC;EAAW;EAAa;EAAU;CAAS,CAAC;AACtE,CAAC,CAAC,CAAC,CAAC;AAEJ,IAAa,yBAAb,cAA4C,OAAO,MACjD,mCACF,CAAC,CAAC;CACA,SAAS;CACT,QAAQC;AACV,CAAC,CAAC,CAAC,CAAC;AAEJ,IAAa,gBAAb,cAAmC,OAAO,MAAqB,0BAA0B,CAAC,CAAC;CACzF,QAAQA;CACR,QAAQ,OAAO,SAAS;EAAC;EAAiB;EAAW;EAAa;CAAQ,CAAC;CAC3E,UAAU,OAAO,SAAS;EAAC;EAAa;EAAY;CAAkB,CAAC;CACvE,aAAa,OAAO,YAAY,OAAO,OAAO,MAAM,OAAO,YAAY,EAAE,CAAC,CAAC;AAC7E,CAAC,CAAC,CAAC,CAAC;AAEJ,IAAa,iBAAb,cAAoC,OAAO,MAAsB,2BAA2B,CAAC,CAAC;CAC5F,WAAW;CACX,OAAO,OAAO,MAAM,aAAa,CAAC,CAAC,MAAM,OAAO,YAAY,GAAG,CAAC;CAChE,UAAU,OAAO;AACnB,CAAC,CAAC,CAAC,CAAC;AAEJ,IAAa,aAAb,cAAgC,OAAO,MAAkB,uBAAuB,CAAC,CAAC;CAChF,QAAQA;CACR,aAAa,OAAO;CACpB,WAAW,OAAO;AACpB,CAAC,CAAC,CAAC,CAAC"}
@@ -1,28 +0,0 @@
1
- import { a as SessionReference } from "./References-DOH3Wu44.mjs";
2
- import { FileError } from "./Errors.mjs";
3
- import { t as BrowserbaseClient } from "./Client-B_f6bBLT.mjs";
4
- import { t as BrowserbaseSessions } from "./Sessions-oJEUmZth.mjs";
5
- import { p as UploadReceipt } from "./Transfers-GVqzrDWl.mjs";
6
- import { Context, Effect, Layer } from "effect";
7
- //#region src/Uploads.d.ts
8
- interface UploadFile {
9
- readonly filename: string;
10
- readonly mediaType: string;
11
- readonly bytes: Uint8Array;
12
- readonly maxBytes?: number;
13
- readonly timeoutMillis?: number;
14
- }
15
- declare const BrowserbaseUploads_base: Context.ServiceClass<BrowserbaseUploads, "effect-browserbase/Uploads", {
16
- readonly create: (reference: SessionReference, file: UploadFile) => Effect.Effect<UploadReceipt, FileError>;
17
- }>;
18
- /**
19
- * A session upload places bytes where the remote browser can already reach them. It is the
20
- * large-file half of file selection; small in-memory selection needs no provisioning at all.
21
- */
22
- declare class BrowserbaseUploads extends BrowserbaseUploads_base {
23
- static readonly layer: Layer.Layer<BrowserbaseUploads, never, BrowserbaseClient | BrowserbaseSessions>;
24
- }
25
- //#endregion
26
- export { UploadFile as n, BrowserbaseUploads as t };
27
- export {};
28
- //# sourceMappingURL=Uploads-C2_bACgv.d.mts.map
@@ -1,3 +0,0 @@
1
- import { n as UploadFile, t as BrowserbaseUploads } from "./Uploads-C2_bACgv.mjs";
2
- export { BrowserbaseUploads, UploadFile };
3
- export {};
package/dist/Uploads.mjs DELETED
@@ -1,105 +0,0 @@
1
- import { FileError } from "./Errors.mjs";
2
- import { r as span, t as BrowserbaseClient } from "./Client-BEC4Q8ZS.mjs";
3
- import { RemoteFilePath, SafeFilename, UploadReceipt } from "./Transfers.mjs";
4
- import { t as BrowserbaseSessions } from "./Sessions-D2H5m6rF.mjs";
5
- import { n as recordIssuedUpload } from "./Issued-DejqoMYL.mjs";
6
- import { Context, Effect, Layer, Schema } from "effect";
7
- //#region src/Uploads.ts
8
- /** The provider's documented per-file ceiling for a session upload. */
9
- const MAX_UPLOAD_BYTES = 536870912;
10
- const MediaType = Schema.NonEmptyString.check(Schema.isMaxLength(128), Schema.isPattern(/^[a-z0-9][a-z0-9!#$&^_+.-]*\/[a-z0-9][a-z0-9!#$&^_+.-]*$/));
11
- const UploadOptions = Schema.Struct({
12
- filename: SafeFilename,
13
- mediaType: MediaType,
14
- maxBytes: Schema.optionalKey(Schema.Int.check(Schema.isBetween({
15
- minimum: 1,
16
- maximum: MAX_UPLOAD_BYTES
17
- }))),
18
- timeoutMillis: Schema.optionalKey(Schema.Int.check(Schema.isBetween({
19
- minimum: 1,
20
- maximum: 6e5
21
- })))
22
- });
23
- /**
24
- * Only fields this package can validate are carried forward. `path` is the provider's own
25
- * remote location for the stored file; when the response omits it, the receipt says so
26
- * instead of inventing one, and attachment by path is refused rather than guessed.
27
- */
28
- /**
29
- * Where the provider places session uploads inside the remote browser. Observed against the
30
- * provider on 21 September 2026 and documented at
31
- * https://docs.browserbase.com/platform/browser/files/uploads.md; the create reply does not
32
- * return it.
33
- */
34
- const UPLOAD_DIRECTORY = "/tmp/.uploads";
35
- const ProviderUpload = Schema.Struct({
36
- message: Schema.optionalKey(Schema.String.check(Schema.isMaxLength(1024))),
37
- path: Schema.optionalKey(RemoteFilePath)
38
- });
39
- const failure = (operation, reason) => FileError.make({
40
- operation,
41
- reason,
42
- outcome: "undispatched"
43
- });
44
- const fromClient = (operation) => (error) => FileError.make({
45
- operation,
46
- reason: error.reason,
47
- ...error.outcome === void 0 ? {} : { outcome: error.outcome },
48
- ...error.status === void 0 ? {} : { status: error.status },
49
- ...error.retryAfterMillis === void 0 ? {} : { retryAfterMillis: error.retryAfterMillis }
50
- });
51
- /**
52
- * A session upload places bytes where the remote browser can already reach them. It is the
53
- * large-file half of file selection; small in-memory selection needs no provisioning at all.
54
- */
55
- var BrowserbaseUploads = class BrowserbaseUploads extends Context.Service()("effect-browserbase/Uploads") {
56
- static layer = Layer.effect(BrowserbaseUploads, Effect.gen(function* () {
57
- const client = yield* BrowserbaseClient;
58
- const sessions = yield* BrowserbaseSessions;
59
- const create = Effect.fnUntraced(function* (reference, file) {
60
- const { bytes, ...rest } = file;
61
- const value = yield* Schema.decodeEffect(UploadOptions)(rest, { onExcessProperty: "error" }).pipe(Effect.mapError(() => failure("upload", "configuration")));
62
- if (!(bytes instanceof Uint8Array)) return yield* failure("upload", "configuration");
63
- const maxBytes = value.maxBytes ?? MAX_UPLOAD_BYTES;
64
- if (bytes.byteLength < 1 || bytes.byteLength > maxBytes) return yield* failure("upload", "limit");
65
- const state = yield* sessions.retrieve(reference).pipe(Effect.mapError((error) => FileError.make({
66
- operation: "upload",
67
- reason: error.reason
68
- })));
69
- if (state.status !== "RUNNING") return yield* failure("upload", "expired");
70
- const owned = new Uint8Array(bytes);
71
- const raw = yield* client.upload(`/v1/sessions/${encodeURIComponent(state.reference.sessionId)}/uploads`, {
72
- field: "file",
73
- filename: value.filename,
74
- mediaType: value.mediaType,
75
- bytes: owned
76
- }, {
77
- maxBytes,
78
- ...value.timeoutMillis === void 0 ? {} : { timeoutMillis: value.timeoutMillis }
79
- }).pipe(Effect.mapError(fromClient("upload")));
80
- const acknowledged = yield* Schema.decodeUnknownEffect(ProviderUpload)(raw).pipe(Effect.mapError(() => FileError.make({
81
- operation: "upload",
82
- reason: "malformed",
83
- outcome: "unknown"
84
- })));
85
- const remotePath = acknowledged.path ?? `${UPLOAD_DIRECTORY}/${value.filename}`;
86
- const receipt = UploadReceipt.make({
87
- reference: state.reference,
88
- filename: value.filename,
89
- bytes: owned.byteLength,
90
- remotePath,
91
- ...acknowledged.message === void 0 ? {} : { acknowledgement: acknowledged.message }
92
- });
93
- recordIssuedUpload(receipt, {
94
- reference: state.reference,
95
- remotePath
96
- });
97
- return receipt;
98
- }, span("BrowserbaseUploads.create"));
99
- return BrowserbaseUploads.of({ create });
100
- }));
101
- };
102
- //#endregion
103
- export { BrowserbaseUploads };
104
-
105
- //# sourceMappingURL=Uploads.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Uploads.mjs","names":["Trace.span"],"sources":["../src/Uploads.ts"],"sourcesContent":["import { Context, Effect, Layer, Schema } from \"effect\";\n\nimport { BrowserbaseClient } from \"./Client.ts\";\nimport { type ClientError, FileError } from \"./Errors.ts\";\nimport * as Trace from \"./internal/Trace.ts\";\nimport { recordIssuedUpload } from \"./internal/upload/Issued.ts\";\nimport type { SessionReference } from \"./References.ts\";\nimport { BrowserbaseSessions } from \"./Sessions.ts\";\nimport { RemoteFilePath, SafeFilename, UploadReceipt } from \"./Transfers.ts\";\n\n/** The provider's documented per-file ceiling for a session upload. */\nconst MAX_UPLOAD_BYTES = 512 * 1024 * 1024;\n\nconst MediaType = Schema.NonEmptyString.check(\n Schema.isMaxLength(128),\n Schema.isPattern(/^[a-z0-9][a-z0-9!#$&^_+.-]*\\/[a-z0-9][a-z0-9!#$&^_+.-]*$/),\n);\n\nconst UploadOptions = Schema.Struct({\n filename: SafeFilename,\n mediaType: MediaType,\n maxBytes: Schema.optionalKey(\n Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: MAX_UPLOAD_BYTES })),\n ),\n timeoutMillis: Schema.optionalKey(\n Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 600_000 })),\n ),\n});\n\n/**\n * Only fields this package can validate are carried forward. `path` is the provider's own\n * remote location for the stored file; when the response omits it, the receipt says so\n * instead of inventing one, and attachment by path is refused rather than guessed.\n */\n/**\n * Where the provider places session uploads inside the remote browser. Observed against the\n * provider on 21 September 2026 and documented at\n * https://docs.browserbase.com/platform/browser/files/uploads.md; the create reply does not\n * return it.\n */\nconst UPLOAD_DIRECTORY = \"/tmp/.uploads\";\n\nconst ProviderUpload = Schema.Struct({\n message: Schema.optionalKey(Schema.String.check(Schema.isMaxLength(1024))),\n path: Schema.optionalKey(RemoteFilePath),\n});\n\nexport interface UploadFile {\n readonly filename: string;\n readonly mediaType: string;\n readonly bytes: Uint8Array;\n readonly maxBytes?: number;\n readonly timeoutMillis?: number;\n}\n\nconst failure = (operation: FileError[\"operation\"], reason: FileError[\"reason\"]) =>\n FileError.make({ operation, reason, outcome: \"undispatched\" });\n\nconst fromClient = (operation: FileError[\"operation\"]) => (error: ClientError) =>\n FileError.make({\n operation,\n reason: error.reason,\n ...(error.outcome === undefined ? {} : { outcome: error.outcome }),\n ...(error.status === undefined ? {} : { status: error.status }),\n ...(error.retryAfterMillis === undefined ? {} : { retryAfterMillis: error.retryAfterMillis }),\n });\n\n/**\n * A session upload places bytes where the remote browser can already reach them. It is the\n * large-file half of file selection; small in-memory selection needs no provisioning at all.\n */\nexport class BrowserbaseUploads extends Context.Service<\n BrowserbaseUploads,\n {\n readonly create: (\n reference: SessionReference,\n file: UploadFile,\n ) => Effect.Effect<UploadReceipt, FileError>;\n }\n>()(\"effect-browserbase/Uploads\") {\n static readonly layer: Layer.Layer<\n BrowserbaseUploads,\n never,\n BrowserbaseClient | BrowserbaseSessions\n > = Layer.effect(\n BrowserbaseUploads,\n Effect.gen(function* () {\n const client = yield* BrowserbaseClient;\n const sessions = yield* BrowserbaseSessions;\n\n const create = Effect.fnUntraced(function* (reference: SessionReference, file: UploadFile) {\n const { bytes, ...rest } = file;\n\n const value = yield* Schema.decodeEffect(UploadOptions)(rest, {\n onExcessProperty: \"error\",\n }).pipe(Effect.mapError(() => failure(\"upload\", \"configuration\")));\n\n if (!(bytes instanceof Uint8Array)) return yield* failure(\"upload\", \"configuration\");\n const maxBytes = value.maxBytes ?? MAX_UPLOAD_BYTES;\n\n if (bytes.byteLength < 1 || bytes.byteLength > maxBytes)\n return yield* failure(\"upload\", \"limit\");\n\n // The exact owning session admits the upload; a terminal session cannot receive files.\n const state = yield* sessions\n .retrieve(reference)\n .pipe(\n Effect.mapError((error) =>\n FileError.make({ operation: \"upload\", reason: error.reason }),\n ),\n );\n\n if (state.status !== \"RUNNING\") return yield* failure(\"upload\", \"expired\");\n\n // Send the same immutable bytes that were measured a moment ago.\n const owned = new Uint8Array(bytes);\n\n const raw = yield* client\n .upload(\n `/v1/sessions/${encodeURIComponent(state.reference.sessionId)}/uploads`,\n {\n field: \"file\",\n filename: value.filename,\n mediaType: value.mediaType,\n bytes: owned,\n },\n {\n maxBytes,\n ...(value.timeoutMillis === undefined ? {} : { timeoutMillis: value.timeoutMillis }),\n },\n )\n .pipe(Effect.mapError(fromClient(\"upload\")));\n\n const acknowledged = yield* Schema.decodeUnknownEffect(ProviderUpload)(raw).pipe(\n Effect.mapError(() =>\n FileError.make({ operation: \"upload\", reason: \"malformed\", outcome: \"unknown\" }),\n ),\n );\n\n // The provider's reply carries only a message; its documented upload location is a\n // fixed directory keyed by the part's filename, which was validated above. A path the\n // provider does return still wins, and was checked for traversal when decoded.\n const remotePath = acknowledged.path ?? `${UPLOAD_DIRECTORY}/${value.filename}`;\n\n const receipt = UploadReceipt.make({\n reference: state.reference,\n filename: value.filename,\n bytes: owned.byteLength,\n remotePath,\n ...(acknowledged.message === undefined ? {} : { acknowledgement: acknowledged.message }),\n });\n\n recordIssuedUpload(receipt, { reference: state.reference, remotePath });\n\n return receipt;\n }, Trace.span(\"BrowserbaseUploads.create\"));\n\n return BrowserbaseUploads.of({ create });\n }),\n );\n}\n"],"mappings":"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"}