effect-browserbase 0.1.0-beta.102
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 +21 -0
- package/README.md +441 -0
- package/dist/Account.d.mts +34 -0
- package/dist/Account.mjs +41 -0
- package/dist/Account.mjs.map +1 -0
- package/dist/Acquisition-WBafHwXH.mjs +280 -0
- package/dist/Acquisition-WBafHwXH.mjs.map +1 -0
- package/dist/Agents.d.mts +156 -0
- package/dist/Agents.mjs +307 -0
- package/dist/Agents.mjs.map +1 -0
- package/dist/Allocation.d.mts +99 -0
- package/dist/Allocation.mjs +29 -0
- package/dist/Allocation.mjs.map +1 -0
- package/dist/Association-CEps4hYP.mjs +10 -0
- package/dist/Association-CEps4hYP.mjs.map +1 -0
- package/dist/Authorization-Ll5f_XNM.mjs +12 -0
- package/dist/Authorization-Ll5f_XNM.mjs.map +1 -0
- package/dist/Bootstrap-BXuzZojd.mjs +194 -0
- package/dist/Bootstrap-BXuzZojd.mjs.map +1 -0
- package/dist/Bootstrap-m7lHgj0D.d.mts +177 -0
- package/dist/Bootstrap.d.mts +2 -0
- package/dist/Bootstrap.mjs +2 -0
- package/dist/Browser-joE4Zgys.mjs +1557 -0
- package/dist/Browser-joE4Zgys.mjs.map +1 -0
- package/dist/Browser.d.mts +195 -0
- package/dist/Browser.mjs +2 -0
- package/dist/BrowserBinding-qbQA6JhH.mjs +2452 -0
- package/dist/BrowserBinding-qbQA6JhH.mjs.map +1 -0
- package/dist/BrowserBinding.d.mts +40 -0
- package/dist/BrowserBinding.mjs +2 -0
- package/dist/BrowserData-LpcAuAub.d.mts +369 -0
- package/dist/BrowserData.d.mts +2 -0
- package/dist/BrowserData.mjs +442 -0
- package/dist/BrowserData.mjs.map +1 -0
- package/dist/Capture-CZJPuD7b.mjs +532 -0
- package/dist/Capture-CZJPuD7b.mjs.map +1 -0
- package/dist/Capture-Dr5MkIJb.d.mts +125 -0
- package/dist/Capture.d.mts +2 -0
- package/dist/Capture.mjs +2 -0
- package/dist/Certificates.d.mts +35 -0
- package/dist/Certificates.mjs +90 -0
- package/dist/Certificates.mjs.map +1 -0
- package/dist/Cleanup-G2YpTCuU.d.mts +24 -0
- package/dist/Cleanup.d.mts +2 -0
- package/dist/Cleanup.mjs +48 -0
- package/dist/Cleanup.mjs.map +1 -0
- package/dist/Client-Ca0zSg1n.d.mts +53 -0
- package/dist/Client-D1_xYM9o.mjs +424 -0
- package/dist/Client-D1_xYM9o.mjs.map +1 -0
- package/dist/Client.d.mts +2 -0
- package/dist/Client.mjs +2 -0
- package/dist/ContextCoordination.d.mts +16 -0
- package/dist/ContextCoordination.mjs +67 -0
- package/dist/ContextCoordination.mjs.map +1 -0
- package/dist/ContextWriter-BrQmoXRg.mjs +187 -0
- package/dist/ContextWriter-BrQmoXRg.mjs.map +1 -0
- package/dist/Contexts.d.mts +32 -0
- package/dist/Contexts.mjs +103 -0
- package/dist/Contexts.mjs.map +1 -0
- package/dist/Deadline-CrViaogr.mjs +13 -0
- package/dist/Deadline-CrViaogr.mjs.map +1 -0
- package/dist/Downloads.d.mts +44 -0
- package/dist/Downloads.mjs +142 -0
- package/dist/Downloads.mjs.map +1 -0
- package/dist/Errors.d.mts +123 -0
- package/dist/Errors.mjs +329 -0
- package/dist/Errors.mjs.map +1 -0
- package/dist/Extensions-ClZbYb1H.mjs +237 -0
- package/dist/Extensions-ClZbYb1H.mjs.map +1 -0
- package/dist/Extensions.d.mts +44 -0
- package/dist/Extensions.mjs +2 -0
- package/dist/Functions.d.mts +227 -0
- package/dist/Functions.mjs +332 -0
- package/dist/Functions.mjs.map +1 -0
- package/dist/Images-DgscEfSf.mjs +127 -0
- package/dist/Images-DgscEfSf.mjs.map +1 -0
- package/dist/Issued-DejqoMYL.mjs +15 -0
- package/dist/Issued-DejqoMYL.mjs.map +1 -0
- package/dist/Launch-BHcAEq05.mjs +111 -0
- package/dist/Launch-BHcAEq05.mjs.map +1 -0
- package/dist/Launch-C8CfaYxb.d.mts +132 -0
- package/dist/Launch.d.mts +2 -0
- package/dist/Launch.mjs +105 -0
- package/dist/Launch.mjs.map +1 -0
- package/dist/PageControl.d.mts +16 -0
- package/dist/PageControl.mjs +36 -0
- package/dist/PageControl.mjs.map +1 -0
- package/dist/PageControlAssociation-DabEg5AR.mjs +10 -0
- package/dist/PageControlAssociation-DabEg5AR.mjs.map +1 -0
- package/dist/PageFetch.d.mts +40 -0
- package/dist/PageFetch.mjs +64 -0
- package/dist/PageFetch.mjs.map +1 -0
- package/dist/Projects.d.mts +39 -0
- package/dist/Projects.mjs +71 -0
- package/dist/Projects.mjs.map +1 -0
- package/dist/Recordings.d.mts +28 -0
- package/dist/Recordings.mjs +131 -0
- package/dist/Recordings.mjs.map +1 -0
- package/dist/References-URDAtapt.d.mts +35 -0
- package/dist/References.d.mts +2 -0
- package/dist/References.mjs +41 -0
- package/dist/References.mjs.map +1 -0
- package/dist/Replays.d.mts +28 -0
- package/dist/Replays.mjs +109 -0
- package/dist/Replays.mjs.map +1 -0
- package/dist/Resource-DBksC4zB.mjs +50 -0
- package/dist/Resource-DBksC4zB.mjs.map +1 -0
- package/dist/Search.d.mts +38 -0
- package/dist/Search.mjs +53 -0
- package/dist/Search.mjs.map +1 -0
- package/dist/SessionData-DRKcKCqw.d.mts +39 -0
- package/dist/SessionData.d.mts +2 -0
- package/dist/SessionData.mjs +55 -0
- package/dist/SessionData.mjs.map +1 -0
- package/dist/Sessions-B8E4kMxo.d.mts +68 -0
- package/dist/Sessions-CtbseWEh.mjs +244 -0
- package/dist/Sessions-CtbseWEh.mjs.map +1 -0
- package/dist/Sessions.d.mts +3 -0
- package/dist/Sessions.mjs +3 -0
- package/dist/TransferPolicy-DQ06i7SZ.mjs +25 -0
- package/dist/TransferPolicy-DQ06i7SZ.mjs.map +1 -0
- package/dist/Transfers-Czv5IZzJ.d.mts +76 -0
- package/dist/Transfers.d.mts +2 -0
- package/dist/Transfers.mjs +101 -0
- package/dist/Transfers.mjs.map +1 -0
- package/dist/Uploads.d.mts +29 -0
- package/dist/Uploads.mjs +107 -0
- package/dist/Uploads.mjs.map +1 -0
- package/dist/Webhooks.d.mts +60 -0
- package/dist/Webhooks.mjs +143 -0
- package/dist/Webhooks.mjs.map +1 -0
- package/dist/WriterFacts-osvYAsIx.d.mts +61 -0
- package/dist/index.d.mts +30 -0
- package/dist/index.mjs +30 -0
- package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
- package/package.json +163 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
+
import { Identifier } from "./References.mjs";
|
|
3
|
+
import { ProjectError } from "./Errors.mjs";
|
|
4
|
+
import { t as BrowserbaseClient } from "./Client-D1_xYM9o.mjs";
|
|
5
|
+
import { t as resource } from "./Resource-DBksC4zB.mjs";
|
|
6
|
+
import { Context, Effect, Layer, Schema } from "effect";
|
|
7
|
+
//#region src/Projects.ts
|
|
8
|
+
var Projects_exports = /* @__PURE__ */ __exportAll({
|
|
9
|
+
BrowserbaseProjects: () => BrowserbaseProjects,
|
|
10
|
+
ProjectMetadata: () => ProjectMetadata,
|
|
11
|
+
ProjectUsage: () => ProjectUsage
|
|
12
|
+
});
|
|
13
|
+
const Timestamp = Schema.String.check(Schema.isMaxLength(64));
|
|
14
|
+
var ProjectMetadata = class extends Schema.Class("BrowserbaseProjectMetadata")({
|
|
15
|
+
projectId: Identifier,
|
|
16
|
+
name: Schema.String.check(Schema.isMaxLength(1024)),
|
|
17
|
+
ownerId: Schema.String.check(Schema.isMaxLength(256)),
|
|
18
|
+
/** Default provider session lifetime in seconds. */
|
|
19
|
+
defaultTimeout: Schema.Int,
|
|
20
|
+
/** Maximum concurrent sessions. */
|
|
21
|
+
concurrency: Schema.Int,
|
|
22
|
+
createdAt: Timestamp,
|
|
23
|
+
updatedAt: Timestamp
|
|
24
|
+
}) {};
|
|
25
|
+
var ProjectUsage = class extends Schema.Class("BrowserbaseProjectUsage")({
|
|
26
|
+
projectId: Identifier,
|
|
27
|
+
browserMinutes: Schema.Natural,
|
|
28
|
+
proxyBytes: Schema.Natural
|
|
29
|
+
}) {};
|
|
30
|
+
const ProviderProject = Schema.Struct({
|
|
31
|
+
id: Identifier,
|
|
32
|
+
name: ProjectMetadata.fields.name,
|
|
33
|
+
ownerId: ProjectMetadata.fields.ownerId,
|
|
34
|
+
defaultTimeout: Schema.Int,
|
|
35
|
+
concurrency: Schema.Int,
|
|
36
|
+
createdAt: Timestamp,
|
|
37
|
+
updatedAt: Timestamp
|
|
38
|
+
});
|
|
39
|
+
const project = ({ id, ...rest }) => ProjectMetadata.make({
|
|
40
|
+
projectId: id,
|
|
41
|
+
...rest
|
|
42
|
+
});
|
|
43
|
+
/**
|
|
44
|
+
* Passive project inspection. `retrieve` and `usage` read the Client's own project; `list`
|
|
45
|
+
* reports every project the API key can see. Nothing here changes provider state.
|
|
46
|
+
*/
|
|
47
|
+
var BrowserbaseProjects = class BrowserbaseProjects extends Context.Service()("effect-browserbase/Projects") {
|
|
48
|
+
static layer = Layer.effect(BrowserbaseProjects, Effect.gen(function* () {
|
|
49
|
+
const client = yield* BrowserbaseClient;
|
|
50
|
+
const api = resource(client, (failure) => ProjectError.make(failure));
|
|
51
|
+
const own = `/v1/projects/${api.segment(client.projectId)}`;
|
|
52
|
+
const list = api.request("GET", "/v1/projects", Schema.Array(ProviderProject).check(Schema.isMaxLength(1024)), "project-list").pipe(Effect.map((rows) => rows.map(project)), Effect.withSpan("BrowserbaseProjects.list"));
|
|
53
|
+
const retrieve = api.request("GET", own, ProviderProject, "project-retrieve").pipe(Effect.flatMap((value) => value.id === client.projectId ? Effect.succeed(project(value)) : Effect.fail(api.malformed("project-retrieve", false))), Effect.withSpan("BrowserbaseProjects.retrieve"));
|
|
54
|
+
const usage = api.request("GET", `${own}/usage`, Schema.Struct({
|
|
55
|
+
browserMinutes: Schema.Natural,
|
|
56
|
+
proxyBytes: Schema.Natural
|
|
57
|
+
}), "project-usage").pipe(Effect.map((value) => ProjectUsage.make({
|
|
58
|
+
projectId: client.projectId,
|
|
59
|
+
...value
|
|
60
|
+
})), Effect.withSpan("BrowserbaseProjects.usage"));
|
|
61
|
+
return BrowserbaseProjects.of({
|
|
62
|
+
list,
|
|
63
|
+
retrieve,
|
|
64
|
+
usage
|
|
65
|
+
});
|
|
66
|
+
}));
|
|
67
|
+
};
|
|
68
|
+
//#endregion
|
|
69
|
+
export { BrowserbaseProjects, ProjectMetadata, ProjectUsage, Projects_exports as t };
|
|
70
|
+
|
|
71
|
+
//# sourceMappingURL=Projects.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Projects.mjs","names":[],"sources":["../src/Projects.ts"],"sourcesContent":["import { Context, Effect, Layer, Schema } from \"effect\";\n\nimport { BrowserbaseClient } from \"./Client.ts\";\nimport { ProjectError } from \"./Errors.ts\";\nimport { resource } from \"./internal/http/Resource.ts\";\nimport { Identifier } from \"./References.ts\";\n\nconst Timestamp = Schema.String.check(Schema.isMaxLength(64));\n\nexport class ProjectMetadata extends Schema.Class<ProjectMetadata>(\"BrowserbaseProjectMetadata\")({\n projectId: Identifier,\n name: Schema.String.check(Schema.isMaxLength(1024)),\n ownerId: Schema.String.check(Schema.isMaxLength(256)),\n /** Default provider session lifetime in seconds. */\n defaultTimeout: Schema.Int,\n /** Maximum concurrent sessions. */\n concurrency: Schema.Int,\n createdAt: Timestamp,\n updatedAt: Timestamp,\n}) {}\n\nexport class ProjectUsage extends Schema.Class<ProjectUsage>(\"BrowserbaseProjectUsage\")({\n projectId: Identifier,\n browserMinutes: Schema.Natural,\n proxyBytes: Schema.Natural,\n}) {}\n\nconst ProviderProject = Schema.Struct({\n id: Identifier,\n name: ProjectMetadata.fields.name,\n ownerId: ProjectMetadata.fields.ownerId,\n defaultTimeout: Schema.Int,\n concurrency: Schema.Int,\n createdAt: Timestamp,\n updatedAt: Timestamp,\n});\n\nconst project = ({ id, ...rest }: typeof ProviderProject.Type) =>\n ProjectMetadata.make({ projectId: id, ...rest });\n\n/**\n * Passive project inspection. `retrieve` and `usage` read the Client's own project; `list`\n * reports every project the API key can see. Nothing here changes provider state.\n */\nexport class BrowserbaseProjects extends Context.Service<\n BrowserbaseProjects,\n {\n readonly list: Effect.Effect<ReadonlyArray<ProjectMetadata>, ProjectError>;\n readonly retrieve: Effect.Effect<ProjectMetadata, ProjectError>;\n readonly usage: Effect.Effect<ProjectUsage, ProjectError>;\n }\n>()(\"effect-browserbase/Projects\") {\n static readonly layer: Layer.Layer<BrowserbaseProjects, never, BrowserbaseClient> = Layer.effect(\n BrowserbaseProjects,\n Effect.gen(function* () {\n const client = yield* BrowserbaseClient;\n const api = resource<ProjectError>(client, (failure) => ProjectError.make(failure));\n const own = `/v1/projects/${api.segment(client.projectId)}`;\n\n const list = api\n .request(\n \"GET\",\n \"/v1/projects\",\n Schema.Array(ProviderProject).check(Schema.isMaxLength(1024)),\n \"project-list\",\n )\n .pipe(\n Effect.map((rows) => rows.map(project)),\n Effect.withSpan(\"BrowserbaseProjects.list\"),\n );\n\n const retrieve = api.request(\"GET\", own, ProviderProject, \"project-retrieve\").pipe(\n Effect.flatMap((value) =>\n value.id === client.projectId\n ? Effect.succeed(project(value))\n : Effect.fail(api.malformed(\"project-retrieve\", false)),\n ),\n Effect.withSpan(\"BrowserbaseProjects.retrieve\"),\n );\n\n const usage = api\n .request(\n \"GET\",\n `${own}/usage`,\n Schema.Struct({ browserMinutes: Schema.Natural, proxyBytes: Schema.Natural }),\n \"project-usage\",\n )\n .pipe(\n Effect.map((value) => ProjectUsage.make({ projectId: client.projectId, ...value })),\n Effect.withSpan(\"BrowserbaseProjects.usage\"),\n );\n\n return BrowserbaseProjects.of({ list, retrieve, usage });\n }),\n );\n}\n"],"mappings":";;;;;;;;;;;;AAOA,MAAM,YAAY,OAAO,OAAO,MAAM,OAAO,YAAY,EAAE,CAAC;AAE5D,IAAa,kBAAb,cAAqC,OAAO,MAAuB,4BAA4B,CAAC,CAAC;CAC/F,WAAW;CACX,MAAM,OAAO,OAAO,MAAM,OAAO,YAAY,IAAI,CAAC;CAClD,SAAS,OAAO,OAAO,MAAM,OAAO,YAAY,GAAG,CAAC;;CAEpD,gBAAgB,OAAO;;CAEvB,aAAa,OAAO;CACpB,WAAW;CACX,WAAW;AACb,CAAC,CAAC,CAAC,CAAC;AAEJ,IAAa,eAAb,cAAkC,OAAO,MAAoB,yBAAyB,CAAC,CAAC;CACtF,WAAW;CACX,gBAAgB,OAAO;CACvB,YAAY,OAAO;AACrB,CAAC,CAAC,CAAC,CAAC;AAEJ,MAAM,kBAAkB,OAAO,OAAO;CACpC,IAAI;CACJ,MAAM,gBAAgB,OAAO;CAC7B,SAAS,gBAAgB,OAAO;CAChC,gBAAgB,OAAO;CACvB,aAAa,OAAO;CACpB,WAAW;CACX,WAAW;AACb,CAAC;AAED,MAAM,WAAW,EAAE,IAAI,GAAG,WACxB,gBAAgB,KAAK;CAAE,WAAW;CAAI,GAAG;AAAK,CAAC;;;;;AAMjD,IAAa,sBAAb,MAAa,4BAA4B,QAAQ,QAO/C,CAAC,CAAC,6BAA6B,CAAC,CAAC;CACjC,OAAgB,QAAoE,MAAM,OACxF,qBACA,OAAO,IAAI,aAAa;EACtB,MAAM,SAAS,OAAO;EACtB,MAAM,MAAM,SAAuB,SAAS,YAAY,aAAa,KAAK,OAAO,CAAC;EAClF,MAAM,MAAM,gBAAgB,IAAI,QAAQ,OAAO,SAAS;EAExD,MAAM,OAAO,IACV,QACC,OACA,gBACA,OAAO,MAAM,eAAe,CAAC,CAAC,MAAM,OAAO,YAAY,IAAI,CAAC,GAC5D,cACF,CAAC,CACA,KACC,OAAO,KAAK,SAAS,KAAK,IAAI,OAAO,CAAC,GACtC,OAAO,SAAS,0BAA0B,CAC5C;EAEF,MAAM,WAAW,IAAI,QAAQ,OAAO,KAAK,iBAAiB,kBAAkB,CAAC,CAAC,KAC5E,OAAO,SAAS,UACd,MAAM,OAAO,OAAO,YAChB,OAAO,QAAQ,QAAQ,KAAK,CAAC,IAC7B,OAAO,KAAK,IAAI,UAAU,oBAAoB,KAAK,CAAC,CAC1D,GACA,OAAO,SAAS,8BAA8B,CAChD;EAEA,MAAM,QAAQ,IACX,QACC,OACA,GAAG,IAAI,SACP,OAAO,OAAO;GAAE,gBAAgB,OAAO;GAAS,YAAY,OAAO;EAAQ,CAAC,GAC5E,eACF,CAAC,CACA,KACC,OAAO,KAAK,UAAU,aAAa,KAAK;GAAE,WAAW,OAAO;GAAW,GAAG;EAAM,CAAC,CAAC,GAClF,OAAO,SAAS,2BAA2B,CAC7C;EAEF,OAAO,oBAAoB,GAAG;GAAE;GAAM;GAAU;EAAM,CAAC;CACzD,CAAC,CACH;AACF"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { o as SessionReference } from "./References-URDAtapt.mjs";
|
|
2
|
+
import { ArtifactError } from "./Errors.mjs";
|
|
3
|
+
import { t as BrowserbaseClient } from "./Client-Ca0zSg1n.mjs";
|
|
4
|
+
import { t as BrowserbaseSessions } from "./Sessions-B8E4kMxo.mjs";
|
|
5
|
+
import { a as RecordingBatch, s as RecordingPageReference, t as ArtifactTransferPolicy } from "./Transfers-Czv5IZzJ.mjs";
|
|
6
|
+
import { Context, Effect, Layer, Stream } from "effect";
|
|
7
|
+
declare namespace Recordings_d_exports {
|
|
8
|
+
export { BrowserbaseRecordings, DownloadLimits, PollOptions };
|
|
9
|
+
}
|
|
10
|
+
export interface PollOptions {
|
|
11
|
+
readonly timeoutMillis?: number;
|
|
12
|
+
readonly intervalMillis?: number;
|
|
13
|
+
}
|
|
14
|
+
/** Recording transfers share the canonical artifact bounds. */
|
|
15
|
+
export type DownloadLimits = ArtifactTransferPolicy;
|
|
16
|
+
declare const BrowserbaseRecordings_base: Context.ServiceClass<BrowserbaseRecordings, "effect-browserbase/Recordings", {
|
|
17
|
+
readonly request: (reference: SessionReference, mode?: "initial" | "retry-failed") => Effect.Effect<RecordingBatch, ArtifactError>;
|
|
18
|
+
readonly status: (reference: SessionReference) => Effect.Effect<RecordingBatch, ArtifactError>;
|
|
19
|
+
readonly wait: (reference: SessionReference, options?: PollOptions) => Effect.Effect<RecordingBatch, ArtifactError>;
|
|
20
|
+
readonly download: (reference: RecordingPageReference, limits: DownloadLimits) => Stream.Stream<Uint8Array, ArtifactError>;
|
|
21
|
+
}>;
|
|
22
|
+
/** Independent of a live browser. Merely building this Layer performs no provider calls. */
|
|
23
|
+
export declare class BrowserbaseRecordings extends BrowserbaseRecordings_base {
|
|
24
|
+
static readonly layer: Layer.Layer<BrowserbaseRecordings, never, BrowserbaseClient | BrowserbaseSessions>;
|
|
25
|
+
}
|
|
26
|
+
//#endregion
|
|
27
|
+
export { Recordings_d_exports as t };
|
|
28
|
+
//# sourceMappingURL=Recordings.d.mts.map
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
+
import { Identifier } from "./References.mjs";
|
|
3
|
+
import { ArtifactError } from "./Errors.mjs";
|
|
4
|
+
import { RecordingBatch, RecordingPage, RecordingPageReference } from "./Transfers.mjs";
|
|
5
|
+
import { t as BrowserbaseClient } from "./Client-D1_xYM9o.mjs";
|
|
6
|
+
import { n as transferPolicy } from "./TransferPolicy-DQ06i7SZ.mjs";
|
|
7
|
+
import { n as nowMillis, r as until, t as deadlineAfter } from "./Deadline-CrViaogr.mjs";
|
|
8
|
+
import { t as BrowserbaseSessions } from "./Sessions-CtbseWEh.mjs";
|
|
9
|
+
import { t as requireTerminalSession } from "./Authorization-Ll5f_XNM.mjs";
|
|
10
|
+
import { Context, Effect, Layer, Redacted, Schema, Semaphore, Stream } from "effect";
|
|
11
|
+
//#region src/Recordings.ts
|
|
12
|
+
var Recordings_exports = /* @__PURE__ */ __exportAll({ BrowserbaseRecordings: () => BrowserbaseRecordings });
|
|
13
|
+
const RawPage = Schema.Struct({
|
|
14
|
+
pageId: Identifier,
|
|
15
|
+
status: Schema.Literals([
|
|
16
|
+
"NOT_REQUESTED",
|
|
17
|
+
"PENDING",
|
|
18
|
+
"COMPLETED",
|
|
19
|
+
"FAILED"
|
|
20
|
+
]),
|
|
21
|
+
completedAt: Schema.optionalKey(Schema.String.check(Schema.isMaxLength(64))),
|
|
22
|
+
downloadUrl: Schema.optionalKey(Schema.String.check(Schema.isMaxLength(16384)))
|
|
23
|
+
});
|
|
24
|
+
const RawBatch = Schema.Struct({ downloads: Schema.Array(RawPage).check(Schema.isMaxLength(256)) });
|
|
25
|
+
const failure = (operation, reason) => ArtifactError.make({
|
|
26
|
+
operation,
|
|
27
|
+
reason
|
|
28
|
+
});
|
|
29
|
+
const fromClient = (operation) => (error) => ArtifactError.make({
|
|
30
|
+
operation,
|
|
31
|
+
reason: error.reason,
|
|
32
|
+
...error.outcome === void 0 ? {} : { outcome: error.outcome },
|
|
33
|
+
...error.status === void 0 ? {} : { status: error.status },
|
|
34
|
+
...error.retryAfterMillis === void 0 ? {} : { retryAfterMillis: error.retryAfterMillis }
|
|
35
|
+
});
|
|
36
|
+
const within = (effect, deadline, operation) => until(effect, deadline, () => failure(operation, "timeout"));
|
|
37
|
+
/** Independent of a live browser. Merely building this Layer performs no provider calls. */
|
|
38
|
+
var BrowserbaseRecordings = class BrowserbaseRecordings extends Context.Service()("effect-browserbase/Recordings") {
|
|
39
|
+
static layer = Layer.effect(BrowserbaseRecordings, Effect.gen(function* () {
|
|
40
|
+
const client = yield* BrowserbaseClient;
|
|
41
|
+
const sessions = yield* BrowserbaseSessions;
|
|
42
|
+
const requests = yield* Semaphore.make(1);
|
|
43
|
+
const path = (ref) => `/v1/sessions/${encodeURIComponent(ref.sessionId)}/recording/downloads`;
|
|
44
|
+
const read = (ref, deadline) => client.json("GET", path(ref), void 0, deadline).pipe(Effect.mapError(fromClient("recording-status")), Effect.flatMap((raw) => Schema.decodeUnknownEffect(RawBatch)(raw).pipe(Effect.mapError(() => failure("recording-status", "malformed")))), Effect.filterOrFail((raw) => new Set(raw.downloads.map((page) => page.pageId)).size === raw.downloads.length, () => failure("recording-status", "malformed")));
|
|
45
|
+
const project = (ref, raw, timedOut = false) => RecordingBatch.make({
|
|
46
|
+
reference: ref,
|
|
47
|
+
timedOut,
|
|
48
|
+
pages: raw.downloads.map((page) => RecordingPage.make({
|
|
49
|
+
pageId: page.pageId,
|
|
50
|
+
status: page.status,
|
|
51
|
+
delivery: page.status !== "COMPLETED" ? "not-ready" : page.downloadUrl === void 0 ? "external-storage" : "download",
|
|
52
|
+
...page.completedAt === void 0 ? {} : { completedAt: page.completedAt }
|
|
53
|
+
}))
|
|
54
|
+
});
|
|
55
|
+
const authorize = (ref) => requireTerminalSession(sessions, ref, () => failure("recording", "active"), (error) => ArtifactError.make({
|
|
56
|
+
operation: "recording",
|
|
57
|
+
reason: error.reason
|
|
58
|
+
}));
|
|
59
|
+
const status = Effect.fnUntraced(function* (ref) {
|
|
60
|
+
yield* authorize(ref);
|
|
61
|
+
return project(ref, yield* read(ref));
|
|
62
|
+
});
|
|
63
|
+
const request = (ref, mode = "initial") => requests.withPermits(1)(Effect.gen(function* () {
|
|
64
|
+
if (mode !== "initial" && mode !== "retry-failed") return yield* ArtifactError.make({
|
|
65
|
+
operation: "recording-request",
|
|
66
|
+
reason: "configuration",
|
|
67
|
+
outcome: "undispatched"
|
|
68
|
+
});
|
|
69
|
+
yield* authorize(ref);
|
|
70
|
+
const before = yield* read(ref);
|
|
71
|
+
if (!(mode === "retry-failed" ? before.downloads.some((page) => page.status === "FAILED") : before.downloads.every((page) => page.status === "NOT_REQUESTED"))) return project(ref, before);
|
|
72
|
+
const raw = yield* client.json("POST", path(ref)).pipe(Effect.mapError((error) => ArtifactError.make({
|
|
73
|
+
operation: "recording-request",
|
|
74
|
+
reason: error.status !== void 0 && error.status >= 400 && error.status < 500 ? error.reason : "assembly-unknown",
|
|
75
|
+
outcome: error.status !== void 0 && error.status >= 400 && error.status < 500 ? "rejected" : "unknown",
|
|
76
|
+
...error.status === void 0 ? {} : { status: error.status },
|
|
77
|
+
...error.retryAfterMillis === void 0 ? {} : { retryAfterMillis: error.retryAfterMillis }
|
|
78
|
+
})), Effect.flatMap((value) => Schema.decodeUnknownEffect(RawBatch)(value).pipe(Effect.mapError(() => ArtifactError.make({
|
|
79
|
+
operation: "recording-request",
|
|
80
|
+
reason: "malformed",
|
|
81
|
+
outcome: "unknown"
|
|
82
|
+
})))));
|
|
83
|
+
return project(ref, raw);
|
|
84
|
+
}));
|
|
85
|
+
const wait = Effect.fnUntraced(function* (ref, options = {}) {
|
|
86
|
+
const timeout = options.timeoutMillis ?? 12e4;
|
|
87
|
+
const interval = options.intervalMillis ?? 3e3;
|
|
88
|
+
if (!Number.isSafeInteger(timeout) || timeout < 1 || timeout > 6e5 || !Number.isSafeInteger(interval) || interval < 10 || interval > 3e4) return yield* failure("recording-wait", "configuration");
|
|
89
|
+
const deadline = yield* deadlineAfter(timeout);
|
|
90
|
+
yield* within(authorize(ref), deadline, "recording-wait");
|
|
91
|
+
let latest = { downloads: [] };
|
|
92
|
+
for (;;) {
|
|
93
|
+
const result = yield* within(read(ref, deadline), deadline, "recording-wait").pipe(Effect.result);
|
|
94
|
+
if (result._tag === "Success") {
|
|
95
|
+
latest = result.success;
|
|
96
|
+
if (latest.downloads.every((page) => page.status === "COMPLETED" || page.status === "FAILED")) return project(ref, latest);
|
|
97
|
+
} else if (![
|
|
98
|
+
"timeout",
|
|
99
|
+
"rate-limited",
|
|
100
|
+
"provider",
|
|
101
|
+
"transport"
|
|
102
|
+
].includes(result.failure.reason)) return yield* result.failure;
|
|
103
|
+
const remaining = deadline - (yield* nowMillis);
|
|
104
|
+
if (remaining <= 0) return project(ref, latest, true);
|
|
105
|
+
yield* Effect.sleep(Math.min(interval, remaining));
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
const download = (ref, limits) => Stream.unwrap(Effect.gen(function* () {
|
|
109
|
+
yield* Schema.decodeEffect(RecordingPageReference)(ref).pipe(Effect.mapError(() => failure("recording-download", "configuration")));
|
|
110
|
+
const { maxBytes, timeoutMillis } = yield* transferPolicy(limits, () => failure("recording-download", "configuration"));
|
|
111
|
+
const deadline = yield* deadlineAfter(timeoutMillis);
|
|
112
|
+
yield* within(authorize(ref.session), deadline, "recording-download");
|
|
113
|
+
const page = (yield* within(read(ref.session, deadline), deadline, "recording-download")).downloads.find((entry) => entry.pageId === ref.pageId);
|
|
114
|
+
if (page === void 0) return yield* failure("recording-download", "not-found");
|
|
115
|
+
if (page.status === "FAILED") return yield* failure("recording-download", "failed");
|
|
116
|
+
if (page.status !== "COMPLETED") return yield* failure("recording-download", "active");
|
|
117
|
+
if (page.downloadUrl === void 0) return yield* failure("recording-download", "byos");
|
|
118
|
+
return client.media(Redacted.make(page.downloadUrl), maxBytes, ["video/mp4", "application/octet-stream"], timeoutMillis, deadline).pipe(Stream.mapError(fromClient("recording-download")));
|
|
119
|
+
}));
|
|
120
|
+
return BrowserbaseRecordings.of({
|
|
121
|
+
request,
|
|
122
|
+
status,
|
|
123
|
+
wait,
|
|
124
|
+
download
|
|
125
|
+
});
|
|
126
|
+
}));
|
|
127
|
+
};
|
|
128
|
+
//#endregion
|
|
129
|
+
export { BrowserbaseRecordings, Recordings_exports as t };
|
|
130
|
+
|
|
131
|
+
//# sourceMappingURL=Recordings.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Recordings.mjs","names":[],"sources":["../src/Recordings.ts"],"sourcesContent":["import { Context, Effect, Layer, Redacted, Schema, Semaphore, Stream } from \"effect\";\n\nimport { BrowserbaseClient } from \"./Client.ts\";\nimport { ArtifactError, type ClientError } from \"./Errors.ts\";\nimport { requireTerminalSession } from \"./internal/artifact/Authorization.ts\";\nimport { transferPolicy } from \"./internal/artifact/TransferPolicy.ts\";\nimport { deadlineAfter, nowMillis, until } from \"./internal/Deadline.ts\";\nimport { Identifier, type SessionReference } from \"./References.ts\";\nimport { BrowserbaseSessions } from \"./Sessions.ts\";\nimport {\n type ArtifactTransferPolicy,\n RecordingBatch,\n RecordingPage,\n RecordingPageReference,\n} from \"./Transfers.ts\";\n\nconst RawPage = Schema.Struct({\n pageId: Identifier,\n status: Schema.Literals([\"NOT_REQUESTED\", \"PENDING\", \"COMPLETED\", \"FAILED\"]),\n completedAt: Schema.optionalKey(Schema.String.check(Schema.isMaxLength(64))),\n downloadUrl: Schema.optionalKey(Schema.String.check(Schema.isMaxLength(16384))),\n});\n\nconst RawBatch = Schema.Struct({ downloads: Schema.Array(RawPage).check(Schema.isMaxLength(256)) });\n\nexport interface PollOptions {\n readonly timeoutMillis?: number;\n readonly intervalMillis?: number;\n}\n\n/** Recording transfers share the canonical artifact bounds. */\nexport type DownloadLimits = ArtifactTransferPolicy;\n\nconst failure = (operation: ArtifactError[\"operation\"], reason: ArtifactError[\"reason\"]) =>\n ArtifactError.make({ operation, reason });\n\nconst fromClient = (operation: ArtifactError[\"operation\"]) => (error: ClientError) =>\n ArtifactError.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\nconst within = <A, E, R>(\n effect: Effect.Effect<A, E, R>,\n deadline: number,\n operation: ArtifactError[\"operation\"],\n) => until(effect, deadline, () => failure(operation, \"timeout\"));\n\n/** Independent of a live browser. Merely building this Layer performs no provider calls. */\nexport class BrowserbaseRecordings extends Context.Service<\n BrowserbaseRecordings,\n {\n readonly request: (\n reference: SessionReference,\n mode?: \"initial\" | \"retry-failed\",\n ) => Effect.Effect<RecordingBatch, ArtifactError>;\n readonly status: (reference: SessionReference) => Effect.Effect<RecordingBatch, ArtifactError>;\n readonly wait: (\n reference: SessionReference,\n options?: PollOptions,\n ) => Effect.Effect<RecordingBatch, ArtifactError>;\n readonly download: (\n reference: RecordingPageReference,\n limits: DownloadLimits,\n ) => Stream.Stream<Uint8Array, ArtifactError>;\n }\n>()(\"effect-browserbase/Recordings\") {\n static readonly layer: Layer.Layer<\n BrowserbaseRecordings,\n never,\n BrowserbaseClient | BrowserbaseSessions\n > = Layer.effect(\n BrowserbaseRecordings,\n Effect.gen(function* () {\n const client = yield* BrowserbaseClient;\n const sessions = yield* BrowserbaseSessions;\n const requests = yield* Semaphore.make(1);\n\n const path = (ref: SessionReference) =>\n `/v1/sessions/${encodeURIComponent(ref.sessionId)}/recording/downloads`;\n\n const read = (ref: SessionReference, deadline?: number) =>\n client.json(\"GET\", path(ref), undefined, deadline).pipe(\n Effect.mapError(fromClient(\"recording-status\")),\n Effect.flatMap((raw) =>\n Schema.decodeUnknownEffect(RawBatch)(raw).pipe(\n Effect.mapError(() => failure(\"recording-status\", \"malformed\")),\n ),\n ),\n Effect.filterOrFail(\n (raw) =>\n new Set(raw.downloads.map((page) => page.pageId)).size === raw.downloads.length,\n () => failure(\"recording-status\", \"malformed\"),\n ),\n );\n\n const project = (ref: SessionReference, raw: typeof RawBatch.Type, timedOut = false) =>\n RecordingBatch.make({\n reference: ref,\n timedOut,\n pages: raw.downloads.map((page) =>\n RecordingPage.make({\n pageId: page.pageId,\n status: page.status,\n delivery:\n page.status !== \"COMPLETED\"\n ? \"not-ready\"\n : page.downloadUrl === undefined\n ? \"external-storage\"\n : \"download\",\n ...(page.completedAt === undefined ? {} : { completedAt: page.completedAt }),\n }),\n ),\n });\n\n const authorize = (ref: SessionReference) =>\n requireTerminalSession(\n sessions,\n ref,\n () => failure(\"recording\", \"active\"),\n (error) => ArtifactError.make({ operation: \"recording\", reason: error.reason }),\n );\n\n const status = Effect.fnUntraced(function* (ref: SessionReference) {\n yield* authorize(ref);\n\n return project(ref, yield* read(ref));\n });\n\n const request = (ref: SessionReference, mode: \"initial\" | \"retry-failed\" = \"initial\") =>\n requests.withPermits(1)(\n Effect.gen(function* () {\n if (mode !== \"initial\" && mode !== \"retry-failed\")\n return yield* ArtifactError.make({\n operation: \"recording-request\",\n reason: \"configuration\",\n outcome: \"undispatched\",\n });\n yield* authorize(ref);\n // A prior uncertain POST is reconciled by this GET before any resubmission.\n const before = yield* read(ref);\n\n const needed =\n mode === \"retry-failed\"\n ? before.downloads.some((page) => page.status === \"FAILED\")\n : before.downloads.every((page) => page.status === \"NOT_REQUESTED\");\n\n if (!needed) return project(ref, before);\n\n const raw = yield* client.json(\"POST\", path(ref)).pipe(\n Effect.mapError((error) =>\n ArtifactError.make({\n operation: \"recording-request\",\n reason:\n error.status !== undefined && error.status >= 400 && error.status < 500\n ? error.reason\n : \"assembly-unknown\",\n outcome:\n error.status !== undefined && error.status >= 400 && error.status < 500\n ? \"rejected\"\n : \"unknown\",\n ...(error.status === undefined ? {} : { status: error.status }),\n ...(error.retryAfterMillis === undefined\n ? {}\n : { retryAfterMillis: error.retryAfterMillis }),\n }),\n ),\n Effect.flatMap((value) =>\n Schema.decodeUnknownEffect(RawBatch)(value).pipe(\n Effect.mapError(() =>\n ArtifactError.make({\n operation: \"recording-request\",\n reason: \"malformed\",\n outcome: \"unknown\",\n }),\n ),\n ),\n ),\n );\n\n return project(ref, raw);\n }),\n );\n\n const wait = Effect.fnUntraced(function* (ref: SessionReference, options: PollOptions = {}) {\n const timeout = options.timeoutMillis ?? 120_000;\n const interval = options.intervalMillis ?? 3000;\n\n if (\n !Number.isSafeInteger(timeout) ||\n timeout < 1 ||\n timeout > 600_000 ||\n !Number.isSafeInteger(interval) ||\n interval < 10 ||\n interval > 30_000\n ) {\n return yield* failure(\"recording-wait\", \"configuration\");\n }\n const deadline = yield* deadlineAfter(timeout);\n\n yield* within(authorize(ref), deadline, \"recording-wait\");\n let latest: typeof RawBatch.Type = { downloads: [] };\n\n for (;;) {\n const result = yield* within(read(ref, deadline), deadline, \"recording-wait\").pipe(\n Effect.result,\n );\n\n if (result._tag === \"Success\") {\n latest = result.success;\n if (\n latest.downloads.every(\n (page) => page.status === \"COMPLETED\" || page.status === \"FAILED\",\n )\n )\n return project(ref, latest);\n } else if (\n ![\"timeout\", \"rate-limited\", \"provider\", \"transport\"].includes(result.failure.reason)\n ) {\n return yield* result.failure;\n }\n const remaining = deadline - (yield* nowMillis);\n\n if (remaining <= 0) return project(ref, latest, true);\n yield* Effect.sleep(Math.min(interval, remaining));\n }\n });\n\n const download = (ref: RecordingPageReference, limits: DownloadLimits) =>\n Stream.unwrap(\n Effect.gen(function* () {\n yield* Schema.decodeEffect(RecordingPageReference)(ref).pipe(\n Effect.mapError(() => failure(\"recording-download\", \"configuration\")),\n );\n\n const { maxBytes, timeoutMillis } = yield* transferPolicy(limits, () =>\n failure(\"recording-download\", \"configuration\"),\n );\n\n const deadline = yield* deadlineAfter(timeoutMillis);\n\n yield* within(authorize(ref.session), deadline, \"recording-download\");\n\n const latest = yield* within(\n read(ref.session, deadline),\n deadline,\n \"recording-download\",\n );\n\n const page = latest.downloads.find((entry) => entry.pageId === ref.pageId);\n\n if (page === undefined) return yield* failure(\"recording-download\", \"not-found\");\n if (page.status === \"FAILED\") return yield* failure(\"recording-download\", \"failed\");\n if (page.status !== \"COMPLETED\") return yield* failure(\"recording-download\", \"active\");\n if (page.downloadUrl === undefined) return yield* failure(\"recording-download\", \"byos\");\n\n // A fresh GET mints access for this subscription. No URL is persisted in the reference.\n return client\n .media(\n Redacted.make(page.downloadUrl),\n maxBytes,\n [\"video/mp4\", \"application/octet-stream\"],\n timeoutMillis,\n deadline,\n )\n .pipe(Stream.mapError(fromClient(\"recording-download\")));\n }),\n );\n\n return BrowserbaseRecordings.of({ request, status, wait, download });\n }),\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
declare namespace References_d_exports {
|
|
3
|
+
export { AllocationAttempt, ContextReference, ExtensionReference, Identifier, SessionReference };
|
|
4
|
+
}
|
|
5
|
+
declare const Identifier: Schema.NonEmptyString;
|
|
6
|
+
declare const SessionReference_base: Schema.Class<SessionReference, Schema.Struct<{
|
|
7
|
+
readonly provider: Schema.Literal<"browserbase">;
|
|
8
|
+
readonly projectId: Schema.NonEmptyString;
|
|
9
|
+
readonly sessionId: Schema.NonEmptyString;
|
|
10
|
+
}>, {}>;
|
|
11
|
+
declare class SessionReference extends SessionReference_base {}
|
|
12
|
+
declare const AllocationAttempt_base: Schema.Class<AllocationAttempt, Schema.Struct<{
|
|
13
|
+
readonly projectId: Schema.NonEmptyString;
|
|
14
|
+
readonly attemptId: Schema.NonEmptyString;
|
|
15
|
+
readonly requestedAtMillis: Schema.Finite;
|
|
16
|
+
readonly timeoutSeconds: Schema.Int;
|
|
17
|
+
}>, {}>;
|
|
18
|
+
/** The nonce is reconciliation metadata, not provider idempotency. */
|
|
19
|
+
declare class AllocationAttempt extends AllocationAttempt_base {}
|
|
20
|
+
declare const ContextReference_base: Schema.Class<ContextReference, Schema.Struct<{
|
|
21
|
+
readonly provider: Schema.Literal<"browserbase">;
|
|
22
|
+
readonly projectId: Schema.NonEmptyString;
|
|
23
|
+
readonly contextId: Schema.NonEmptyString;
|
|
24
|
+
}>, {}>;
|
|
25
|
+
/** Project-qualified durable identity; no browser connection or credential is retained. */
|
|
26
|
+
declare class ContextReference extends ContextReference_base {}
|
|
27
|
+
declare const ExtensionReference_base: Schema.Class<ExtensionReference, Schema.Struct<{
|
|
28
|
+
readonly provider: Schema.Literal<"browserbase">;
|
|
29
|
+
readonly projectId: Schema.NonEmptyString;
|
|
30
|
+
readonly extensionId: Schema.NonEmptyString;
|
|
31
|
+
}>, {}>;
|
|
32
|
+
declare class ExtensionReference extends ExtensionReference_base {}
|
|
33
|
+
//#endregion
|
|
34
|
+
export { References_d_exports as a, Identifier as i, ContextReference as n, SessionReference as o, ExtensionReference as r, AllocationAttempt as t };
|
|
35
|
+
//# sourceMappingURL=References-URDAtapt.d.mts.map
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
+
import { Schema } from "effect";
|
|
3
|
+
//#region src/References.ts
|
|
4
|
+
var References_exports = /* @__PURE__ */ __exportAll({
|
|
5
|
+
AllocationAttempt: () => AllocationAttempt,
|
|
6
|
+
ContextReference: () => ContextReference,
|
|
7
|
+
ExtensionReference: () => ExtensionReference,
|
|
8
|
+
Identifier: () => Identifier,
|
|
9
|
+
SessionReference: () => SessionReference
|
|
10
|
+
});
|
|
11
|
+
const Identifier = Schema.NonEmptyString.check(Schema.isMaxLength(256), Schema.isPattern(/^[A-Za-z0-9_-]+$/));
|
|
12
|
+
var SessionReference = class extends Schema.Class("BrowserbaseSessionReference")({
|
|
13
|
+
provider: Schema.Literal("browserbase"),
|
|
14
|
+
projectId: Identifier,
|
|
15
|
+
sessionId: Identifier
|
|
16
|
+
}) {};
|
|
17
|
+
/** The nonce is reconciliation metadata, not provider idempotency. */
|
|
18
|
+
var AllocationAttempt = class extends Schema.Class("BrowserbaseAllocationAttempt")({
|
|
19
|
+
projectId: Identifier,
|
|
20
|
+
attemptId: Identifier,
|
|
21
|
+
requestedAtMillis: Schema.Finite,
|
|
22
|
+
timeoutSeconds: Schema.Int.check(Schema.isBetween({
|
|
23
|
+
minimum: 60,
|
|
24
|
+
maximum: 21600
|
|
25
|
+
}))
|
|
26
|
+
}) {};
|
|
27
|
+
/** Project-qualified durable identity; no browser connection or credential is retained. */
|
|
28
|
+
var ContextReference = class extends Schema.Class("BrowserbaseContextReference")({
|
|
29
|
+
provider: Schema.Literal("browserbase"),
|
|
30
|
+
projectId: Identifier,
|
|
31
|
+
contextId: Identifier
|
|
32
|
+
}) {};
|
|
33
|
+
var ExtensionReference = class extends Schema.Class("BrowserbaseExtensionReference")({
|
|
34
|
+
provider: Schema.Literal("browserbase"),
|
|
35
|
+
projectId: Identifier,
|
|
36
|
+
extensionId: Identifier
|
|
37
|
+
}) {};
|
|
38
|
+
//#endregion
|
|
39
|
+
export { AllocationAttempt, ContextReference, ExtensionReference, Identifier, SessionReference, References_exports as t };
|
|
40
|
+
|
|
41
|
+
//# sourceMappingURL=References.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"References.mjs","names":[],"sources":["../src/References.ts"],"sourcesContent":["import { Schema } from \"effect\";\n\nexport const Identifier = Schema.NonEmptyString.check(\n Schema.isMaxLength(256),\n Schema.isPattern(/^[A-Za-z0-9_-]+$/),\n);\n\nexport class SessionReference extends Schema.Class<SessionReference>(\"BrowserbaseSessionReference\")(\n {\n provider: Schema.Literal(\"browserbase\"),\n projectId: Identifier,\n sessionId: Identifier,\n },\n) {}\n\n/** The nonce is reconciliation metadata, not provider idempotency. */\nexport class AllocationAttempt extends Schema.Class<AllocationAttempt>(\n \"BrowserbaseAllocationAttempt\",\n)({\n projectId: Identifier,\n attemptId: Identifier,\n requestedAtMillis: Schema.Finite,\n timeoutSeconds: Schema.Int.check(Schema.isBetween({ minimum: 60, maximum: 21600 })),\n}) {}\n\n/** Project-qualified durable identity; no browser connection or credential is retained. */\nexport class ContextReference extends Schema.Class<ContextReference>(\"BrowserbaseContextReference\")(\n {\n provider: Schema.Literal(\"browserbase\"),\n projectId: Identifier,\n contextId: Identifier,\n },\n) {}\n\nexport class ExtensionReference extends Schema.Class<ExtensionReference>(\n \"BrowserbaseExtensionReference\",\n)({\n provider: Schema.Literal(\"browserbase\"),\n projectId: Identifier,\n extensionId: Identifier,\n}) {}\n"],"mappings":";;;;;;;;;;AAEA,MAAa,aAAa,OAAO,eAAe,MAC9C,OAAO,YAAY,GAAG,GACtB,OAAO,UAAU,kBAAkB,CACrC;AAEA,IAAa,mBAAb,cAAsC,OAAO,MAAwB,6BAA6B,CAAC,CACjG;CACE,UAAU,OAAO,QAAQ,aAAa;CACtC,WAAW;CACX,WAAW;AACb,CACF,CAAC,CAAC,CAAC;;AAGH,IAAa,oBAAb,cAAuC,OAAO,MAC5C,8BACF,CAAC,CAAC;CACA,WAAW;CACX,WAAW;CACX,mBAAmB,OAAO;CAC1B,gBAAgB,OAAO,IAAI,MAAM,OAAO,UAAU;EAAE,SAAS;EAAI,SAAS;CAAM,CAAC,CAAC;AACpF,CAAC,CAAC,CAAC,CAAC;;AAGJ,IAAa,mBAAb,cAAsC,OAAO,MAAwB,6BAA6B,CAAC,CACjG;CACE,UAAU,OAAO,QAAQ,aAAa;CACtC,WAAW;CACX,WAAW;AACb,CACF,CAAC,CAAC,CAAC;AAEH,IAAa,qBAAb,cAAwC,OAAO,MAC7C,+BACF,CAAC,CAAC;CACA,UAAU,OAAO,QAAQ,aAAa;CACtC,WAAW;CACX,aAAa;AACf,CAAC,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { o as SessionReference } from "./References-URDAtapt.mjs";
|
|
2
|
+
import { ArtifactError } from "./Errors.mjs";
|
|
3
|
+
import { t as BrowserbaseClient } from "./Client-Ca0zSg1n.mjs";
|
|
4
|
+
import { t as BrowserbaseSessions } from "./Sessions-B8E4kMxo.mjs";
|
|
5
|
+
import { l as ReplayPage, s as RecordingPageReference, t as ArtifactTransferPolicy } from "./Transfers-Czv5IZzJ.mjs";
|
|
6
|
+
import { Context, Effect, Layer, Redacted, Stream } from "effect";
|
|
7
|
+
declare namespace Replays_d_exports {
|
|
8
|
+
export { BrowserbaseReplays, ReplayAccess };
|
|
9
|
+
}
|
|
10
|
+
export interface ReplayAccess {
|
|
11
|
+
readonly reference: RecordingPageReference;
|
|
12
|
+
/** Temporary playback material for an authorized host route; never includes the API key. */
|
|
13
|
+
readonly playlist: Redacted.Redacted<string>;
|
|
14
|
+
readonly fetchedAtMillis: number;
|
|
15
|
+
readonly mediaCount: number;
|
|
16
|
+
/** Optional host proxy of an indexed URI from this exact playlist; no arbitrary URL parameter. */
|
|
17
|
+
readonly media: (index: number, limits: ArtifactTransferPolicy) => Stream.Stream<Uint8Array, ArtifactError>;
|
|
18
|
+
}
|
|
19
|
+
declare const BrowserbaseReplays_base: Context.ServiceClass<BrowserbaseReplays, "effect-browserbase/Replays", {
|
|
20
|
+
readonly metadata: (reference: SessionReference) => Effect.Effect<ReadonlyArray<ReplayPage>, ArtifactError>;
|
|
21
|
+
readonly openPage: (reference: RecordingPageReference) => Effect.Effect<ReplayAccess, ArtifactError>;
|
|
22
|
+
}>;
|
|
23
|
+
export declare class BrowserbaseReplays extends BrowserbaseReplays_base {
|
|
24
|
+
static readonly layer: Layer.Layer<BrowserbaseReplays, never, BrowserbaseClient | BrowserbaseSessions>;
|
|
25
|
+
}
|
|
26
|
+
//#endregion
|
|
27
|
+
export { Replays_d_exports as t };
|
|
28
|
+
//# sourceMappingURL=Replays.d.mts.map
|
package/dist/Replays.mjs
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
+
import { Identifier } from "./References.mjs";
|
|
3
|
+
import { ArtifactError } from "./Errors.mjs";
|
|
4
|
+
import { RecordingPageReference, ReplayPage } from "./Transfers.mjs";
|
|
5
|
+
import { t as BrowserbaseClient } from "./Client-D1_xYM9o.mjs";
|
|
6
|
+
import { n as transferPolicy } from "./TransferPolicy-DQ06i7SZ.mjs";
|
|
7
|
+
import { t as BrowserbaseSessions } from "./Sessions-CtbseWEh.mjs";
|
|
8
|
+
import { t as requireTerminalSession } from "./Authorization-Ll5f_XNM.mjs";
|
|
9
|
+
import { Clock, Context, Effect, Layer, Redacted, Schema, Stream } from "effect";
|
|
10
|
+
//#region src/Replays.ts
|
|
11
|
+
var Replays_exports = /* @__PURE__ */ __exportAll({ BrowserbaseReplays: () => BrowserbaseReplays });
|
|
12
|
+
const Metadata = Schema.Struct({
|
|
13
|
+
pageCount: Schema.Natural,
|
|
14
|
+
pages: Schema.Array(Schema.Struct({
|
|
15
|
+
pageId: Identifier,
|
|
16
|
+
startTimeMs: Schema.Finite,
|
|
17
|
+
endTimeMs: Schema.Finite,
|
|
18
|
+
url: Schema.String.check(Schema.isMaxLength(16384))
|
|
19
|
+
})).check(Schema.isMaxLength(256))
|
|
20
|
+
});
|
|
21
|
+
const failure = (operation, reason) => ArtifactError.make({
|
|
22
|
+
operation,
|
|
23
|
+
reason
|
|
24
|
+
});
|
|
25
|
+
const fromClient = (operation) => (error) => ArtifactError.make({
|
|
26
|
+
operation,
|
|
27
|
+
reason: error.reason,
|
|
28
|
+
...error.outcome === void 0 ? {} : { outcome: error.outcome },
|
|
29
|
+
...error.status === void 0 ? {} : { status: error.status },
|
|
30
|
+
...error.retryAfterMillis === void 0 ? {} : { retryAfterMillis: error.retryAfterMillis }
|
|
31
|
+
});
|
|
32
|
+
/** Media-playlist validation, not a player. Unsupported URI-bearing tags fail closed. */
|
|
33
|
+
const playlistUrls = (body, allowed) => {
|
|
34
|
+
const lines = body.split(/\r?\n/);
|
|
35
|
+
if (lines[0] !== "#EXTM3U" || !lines.includes("#EXT-X-ENDLIST") || lines.length > 32768) throw failure("replay-playlist", "malformed");
|
|
36
|
+
const urls = [];
|
|
37
|
+
for (const raw of lines.slice(1)) {
|
|
38
|
+
const line = raw.trim();
|
|
39
|
+
if (line === "") continue;
|
|
40
|
+
if (line.startsWith("#EXT-X-STREAM-INF") || line.startsWith("#EXT-X-MEDIA:") || line.startsWith("#EXT-X-KEY:") && line !== "#EXT-X-KEY:METHOD=NONE") throw failure("replay-playlist", "unsupported");
|
|
41
|
+
let url;
|
|
42
|
+
if (!line.startsWith("#")) url = line;
|
|
43
|
+
else if (/URI\s*=/.test(line)) {
|
|
44
|
+
if (!line.startsWith("#EXT-X-MAP:")) throw failure("replay-playlist", "unsupported");
|
|
45
|
+
const match = /(?:^|,)URI="([^"]+)"(?:,|$)/.exec(line.slice(11));
|
|
46
|
+
if (match === null) throw failure("replay-playlist", "malformed");
|
|
47
|
+
url = match[1];
|
|
48
|
+
}
|
|
49
|
+
if (url !== void 0) {
|
|
50
|
+
if (!allowed(url) || urls.length >= 16384) throw failure("replay-playlist", "unsafe-url");
|
|
51
|
+
urls.push(url);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (urls.length === 0) throw failure("replay-playlist", "malformed");
|
|
55
|
+
return urls;
|
|
56
|
+
};
|
|
57
|
+
var BrowserbaseReplays = class BrowserbaseReplays extends Context.Service()("effect-browserbase/Replays") {
|
|
58
|
+
static layer = Layer.effect(BrowserbaseReplays, Effect.gen(function* () {
|
|
59
|
+
const client = yield* BrowserbaseClient;
|
|
60
|
+
const sessions = yield* BrowserbaseSessions;
|
|
61
|
+
const metadata = Effect.fnUntraced(function* (ref) {
|
|
62
|
+
yield* requireTerminalSession(sessions, ref, () => failure("replay", "active"), (error) => ArtifactError.make({
|
|
63
|
+
operation: "replay",
|
|
64
|
+
reason: error.reason
|
|
65
|
+
}));
|
|
66
|
+
const raw = yield* client.json("GET", `/v1/sessions/${encodeURIComponent(ref.sessionId)}/replays`).pipe(Effect.mapError(fromClient("replay-metadata")), Effect.flatMap((value) => Schema.decodeUnknownEffect(Metadata)(value).pipe(Effect.mapError(() => failure("replay-metadata", "malformed")))));
|
|
67
|
+
if (raw.pageCount !== raw.pages.length || new Set(raw.pages.map((page) => page.pageId)).size !== raw.pages.length || raw.pages.some((page) => page.startTimeMs < 0 || page.endTimeMs < page.startTimeMs)) return yield* failure("replay-metadata", "malformed");
|
|
68
|
+
return raw.pages.map((page) => ReplayPage.make({
|
|
69
|
+
pageId: page.pageId,
|
|
70
|
+
startTimeMs: page.startTimeMs,
|
|
71
|
+
endTimeMs: page.endTimeMs
|
|
72
|
+
}));
|
|
73
|
+
});
|
|
74
|
+
const openPage = Effect.fnUntraced(function* (ref) {
|
|
75
|
+
yield* Schema.decodeEffect(RecordingPageReference)(ref).pipe(Effect.mapError(() => failure("replay", "configuration")));
|
|
76
|
+
if (!(yield* metadata(ref.session)).some((page) => page.pageId === ref.pageId)) return yield* failure("replay", "not-found");
|
|
77
|
+
const body = yield* client.text(`/v1/sessions/${encodeURIComponent(ref.session.sessionId)}/replays/${encodeURIComponent(ref.pageId)}`, 1048576, ["application/vnd.apple.mpegurl", "application/x-mpegurl"]).pipe(Effect.mapError(fromClient("replay-playlist")));
|
|
78
|
+
const urls = yield* Effect.try({
|
|
79
|
+
try: () => playlistUrls(body, client.validateMediaUrl),
|
|
80
|
+
catch: () => failure("replay-playlist", "malformed")
|
|
81
|
+
});
|
|
82
|
+
return {
|
|
83
|
+
reference: ref,
|
|
84
|
+
playlist: Redacted.make(body),
|
|
85
|
+
fetchedAtMillis: yield* Clock.currentTimeMillis,
|
|
86
|
+
mediaCount: urls.length,
|
|
87
|
+
media: (index, limits) => {
|
|
88
|
+
const url = Number.isSafeInteger(index) ? urls[index] : void 0;
|
|
89
|
+
return url === void 0 ? Stream.fail(failure("replay-media", "not-found")) : Stream.unwrap(transferPolicy({
|
|
90
|
+
maxBytes: limits.maxBytes,
|
|
91
|
+
...limits.timeoutMillis === void 0 ? {} : { timeoutMillis: limits.timeoutMillis }
|
|
92
|
+
}, () => failure("replay-media", "configuration")).pipe(Effect.map(({ maxBytes, timeoutMillis }) => client.media(Redacted.make(url), maxBytes, [
|
|
93
|
+
"video/mp4",
|
|
94
|
+
"video/iso.segment",
|
|
95
|
+
"application/octet-stream"
|
|
96
|
+
], timeoutMillis).pipe(Stream.mapError(fromClient("replay-media"))))));
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
});
|
|
100
|
+
return BrowserbaseReplays.of({
|
|
101
|
+
metadata,
|
|
102
|
+
openPage
|
|
103
|
+
});
|
|
104
|
+
}));
|
|
105
|
+
};
|
|
106
|
+
//#endregion
|
|
107
|
+
export { BrowserbaseReplays, Replays_exports as t };
|
|
108
|
+
|
|
109
|
+
//# sourceMappingURL=Replays.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Replays.mjs","names":[],"sources":["../src/Replays.ts"],"sourcesContent":["import { Clock, Context, Effect, Layer, Redacted, Schema, Stream } from \"effect\";\n\nimport { BrowserbaseClient } from \"./Client.ts\";\nimport { ArtifactError, type ClientError } from \"./Errors.ts\";\nimport { requireTerminalSession } from \"./internal/artifact/Authorization.ts\";\nimport { transferPolicy } from \"./internal/artifact/TransferPolicy.ts\";\nimport { Identifier, type SessionReference } from \"./References.ts\";\nimport { BrowserbaseSessions } from \"./Sessions.ts\";\nimport {\n type ArtifactTransferPolicy as DownloadLimits,\n RecordingPageReference,\n ReplayPage,\n} from \"./Transfers.ts\";\n\nconst Metadata = Schema.Struct({\n pageCount: Schema.Natural,\n pages: Schema.Array(\n Schema.Struct({\n pageId: Identifier,\n startTimeMs: Schema.Finite,\n endTimeMs: Schema.Finite,\n url: Schema.String.check(Schema.isMaxLength(16384)),\n }),\n ).check(Schema.isMaxLength(256)),\n});\n\nconst failure = (operation: ArtifactError[\"operation\"], reason: ArtifactError[\"reason\"]) =>\n ArtifactError.make({ operation, reason });\n\nconst fromClient = (operation: ArtifactError[\"operation\"]) => (error: ClientError) =>\n ArtifactError.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\nexport interface ReplayAccess {\n readonly reference: RecordingPageReference;\n /** Temporary playback material for an authorized host route; never includes the API key. */\n readonly playlist: Redacted.Redacted<string>;\n readonly fetchedAtMillis: number;\n readonly mediaCount: number;\n /** Optional host proxy of an indexed URI from this exact playlist; no arbitrary URL parameter. */\n readonly media: (\n index: number,\n limits: DownloadLimits,\n ) => Stream.Stream<Uint8Array, ArtifactError>;\n}\n\n/** Media-playlist validation, not a player. Unsupported URI-bearing tags fail closed. */\nconst playlistUrls = (body: string, allowed: (url: string) => boolean): ReadonlyArray<string> => {\n const lines = body.split(/\\r?\\n/);\n\n if (lines[0] !== \"#EXTM3U\" || !lines.includes(\"#EXT-X-ENDLIST\") || lines.length > 32768) {\n throw failure(\"replay-playlist\", \"malformed\");\n }\n const urls: string[] = [];\n\n for (const raw of lines.slice(1)) {\n const line = raw.trim();\n\n if (line === \"\") continue;\n if (\n line.startsWith(\"#EXT-X-STREAM-INF\") ||\n line.startsWith(\"#EXT-X-MEDIA:\") ||\n (line.startsWith(\"#EXT-X-KEY:\") && line !== \"#EXT-X-KEY:METHOD=NONE\")\n ) {\n throw failure(\"replay-playlist\", \"unsupported\");\n }\n let url: string | undefined;\n\n if (!line.startsWith(\"#\")) url = line;\n else if (/URI\\s*=/.test(line)) {\n if (!line.startsWith(\"#EXT-X-MAP:\")) throw failure(\"replay-playlist\", \"unsupported\");\n const match = /(?:^|,)URI=\"([^\"]+)\"(?:,|$)/.exec(line.slice(\"#EXT-X-MAP:\".length));\n\n if (match === null) throw failure(\"replay-playlist\", \"malformed\");\n url = match[1];\n }\n if (url !== undefined) {\n if (!allowed(url) || urls.length >= 16384) throw failure(\"replay-playlist\", \"unsafe-url\");\n urls.push(url);\n }\n }\n if (urls.length === 0) throw failure(\"replay-playlist\", \"malformed\");\n\n return urls;\n};\n\nexport class BrowserbaseReplays extends Context.Service<\n BrowserbaseReplays,\n {\n readonly metadata: (\n reference: SessionReference,\n ) => Effect.Effect<ReadonlyArray<ReplayPage>, ArtifactError>;\n readonly openPage: (\n reference: RecordingPageReference,\n ) => Effect.Effect<ReplayAccess, ArtifactError>;\n }\n>()(\"effect-browserbase/Replays\") {\n static readonly layer: Layer.Layer<\n BrowserbaseReplays,\n never,\n BrowserbaseClient | BrowserbaseSessions\n > = Layer.effect(\n BrowserbaseReplays,\n Effect.gen(function* () {\n const client = yield* BrowserbaseClient;\n const sessions = yield* BrowserbaseSessions;\n\n const metadata = Effect.fnUntraced(function* (ref: SessionReference) {\n yield* requireTerminalSession(\n sessions,\n ref,\n () => failure(\"replay\", \"active\"),\n (error) => ArtifactError.make({ operation: \"replay\", reason: error.reason }),\n );\n\n const raw = yield* client\n .json(\"GET\", `/v1/sessions/${encodeURIComponent(ref.sessionId)}/replays`)\n .pipe(\n Effect.mapError(fromClient(\"replay-metadata\")),\n Effect.flatMap((value) =>\n Schema.decodeUnknownEffect(Metadata)(value).pipe(\n Effect.mapError(() => failure(\"replay-metadata\", \"malformed\")),\n ),\n ),\n );\n\n if (\n raw.pageCount !== raw.pages.length ||\n new Set(raw.pages.map((page) => page.pageId)).size !== raw.pages.length ||\n raw.pages.some((page) => page.startTimeMs < 0 || page.endTimeMs < page.startTimeMs)\n ) {\n return yield* failure(\"replay-metadata\", \"malformed\");\n }\n\n return raw.pages.map((page) =>\n ReplayPage.make({\n pageId: page.pageId,\n startTimeMs: page.startTimeMs,\n endTimeMs: page.endTimeMs,\n }),\n );\n });\n\n const openPage = Effect.fnUntraced(function* (ref: RecordingPageReference) {\n yield* Schema.decodeEffect(RecordingPageReference)(ref).pipe(\n Effect.mapError(() => failure(\"replay\", \"configuration\")),\n );\n\n const pages = yield* metadata(ref.session);\n\n if (!pages.some((page) => page.pageId === ref.pageId))\n return yield* failure(\"replay\", \"not-found\");\n\n const body = yield* client\n .text(\n `/v1/sessions/${encodeURIComponent(ref.session.sessionId)}/replays/${encodeURIComponent(ref.pageId)}`,\n 1024 * 1024,\n [\"application/vnd.apple.mpegurl\", \"application/x-mpegurl\"],\n )\n .pipe(Effect.mapError(fromClient(\"replay-playlist\")));\n\n // One sanitized rejection: an unsupported tag and an unapproved URI are both\n // refusals to interpret the playlist, and neither may echo its contents.\n const urls = yield* Effect.try({\n try: () => playlistUrls(body, client.validateMediaUrl),\n catch: () => failure(\"replay-playlist\", \"malformed\"),\n });\n\n return {\n reference: ref,\n playlist: Redacted.make(body),\n fetchedAtMillis: yield* Clock.currentTimeMillis,\n mediaCount: urls.length,\n media: (index: number, limits: DownloadLimits) => {\n const url = Number.isSafeInteger(index) ? urls[index] : undefined;\n\n return url === undefined\n ? Stream.fail(failure(\"replay-media\", \"not-found\"))\n : Stream.unwrap(\n transferPolicy(\n {\n maxBytes: limits.maxBytes,\n ...(limits.timeoutMillis === undefined\n ? {}\n : { timeoutMillis: limits.timeoutMillis }),\n },\n () => failure(\"replay-media\", \"configuration\"),\n ).pipe(\n Effect.map(({ maxBytes, timeoutMillis }) =>\n client\n .media(\n Redacted.make(url),\n maxBytes,\n [\"video/mp4\", \"video/iso.segment\", \"application/octet-stream\"],\n timeoutMillis,\n )\n .pipe(Stream.mapError(fromClient(\"replay-media\"))),\n ),\n ),\n );\n },\n } satisfies ReplayAccess;\n });\n\n return BrowserbaseReplays.of({ metadata, openPage });\n }),\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Effect, Schema } from "effect";
|
|
2
|
+
//#region src/internal/http/Resource.ts
|
|
3
|
+
/**
|
|
4
|
+
* The shared shape of every control-plane resource: strict input decoding, tolerant reply
|
|
5
|
+
* decoding, and one mapping from transport failures into the resource's own error.
|
|
6
|
+
*/
|
|
7
|
+
const resource = (client, make) => {
|
|
8
|
+
const fromClient = (operation) => (error) => make({
|
|
9
|
+
operation,
|
|
10
|
+
reason: error.reason,
|
|
11
|
+
...error.outcome === void 0 ? {} : { outcome: error.outcome },
|
|
12
|
+
...error.status === void 0 ? {} : { status: error.status },
|
|
13
|
+
...error.retryAfterMillis === void 0 ? {} : { retryAfterMillis: error.retryAfterMillis }
|
|
14
|
+
});
|
|
15
|
+
const configuration = (operation) => make({
|
|
16
|
+
operation,
|
|
17
|
+
reason: "configuration",
|
|
18
|
+
outcome: "undispatched"
|
|
19
|
+
});
|
|
20
|
+
const malformed = (operation, mutation) => make({
|
|
21
|
+
operation,
|
|
22
|
+
reason: "malformed",
|
|
23
|
+
...mutation ? { outcome: "unknown" } : {}
|
|
24
|
+
});
|
|
25
|
+
/** Rejects unknown or invalid caller input before any request is built. */
|
|
26
|
+
const input = (schema, value, operation) => Schema.decodeUnknownEffect(schema)(value, { onExcessProperty: "error" }).pipe(Effect.mapError(() => configuration(operation)));
|
|
27
|
+
const request = (method, path, reply, operation, body) => client.json(method, path, body).pipe(Effect.mapError(fromClient(operation)), Effect.flatMap((raw) => Schema.decodeEffect(reply)(raw).pipe(Effect.mapError(() => malformed(operation, method !== "GET")))));
|
|
28
|
+
const remove = (path, operation) => client.noContent("DELETE", path).pipe(Effect.mapError(fromClient(operation)));
|
|
29
|
+
const segment = (id) => encodeURIComponent(id);
|
|
30
|
+
const query = (entries) => {
|
|
31
|
+
const search = new URLSearchParams();
|
|
32
|
+
for (const [key, value] of Object.entries(entries)) if (value !== void 0) search.set(key, String(value));
|
|
33
|
+
return search.size === 0 ? "" : `?${search}`;
|
|
34
|
+
};
|
|
35
|
+
return {
|
|
36
|
+
fromClient,
|
|
37
|
+
configuration,
|
|
38
|
+
malformed,
|
|
39
|
+
input,
|
|
40
|
+
request,
|
|
41
|
+
remove,
|
|
42
|
+
segment,
|
|
43
|
+
query,
|
|
44
|
+
make
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
//#endregion
|
|
48
|
+
export { resource as t };
|
|
49
|
+
|
|
50
|
+
//# sourceMappingURL=Resource-DBksC4zB.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Resource-DBksC4zB.mjs","names":[],"sources":["../src/internal/http/Resource.ts"],"sourcesContent":["import { Effect, Schema } from \"effect\";\n\nimport type { BrowserbaseClient, ClientMethod } from \"../../Client.ts\";\nimport type { ClientError } from \"../../Errors.ts\";\n\ntype Reason = ClientError[\"reason\"];\ntype Outcome = NonNullable<ClientError[\"outcome\"]>;\n\nexport interface RequestFailure<Operation extends string> {\n readonly operation: Operation;\n readonly reason: Reason;\n readonly outcome?: Outcome;\n readonly status?: number;\n readonly retryAfterMillis?: number;\n}\n\n/**\n * The shared shape of every control-plane resource: strict input decoding, tolerant reply\n * decoding, and one mapping from transport failures into the resource's own error.\n */\nexport const resource = <E extends { readonly operation: string }>(\n client: BrowserbaseClient[\"Service\"],\n make: (failure: RequestFailure<E[\"operation\"]>) => E,\n) => {\n type Operation = E[\"operation\"];\n\n const fromClient = (operation: Operation) => (error: ClientError) =>\n 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 const configuration = (operation: Operation) =>\n make({ operation, reason: \"configuration\", outcome: \"undispatched\" });\n\n const malformed = (operation: Operation, mutation: boolean) =>\n make({ operation, reason: \"malformed\", ...(mutation ? { outcome: \"unknown\" } : {}) });\n\n /** Rejects unknown or invalid caller input before any request is built. */\n const input = <A>(\n schema: Schema.Codec<A, unknown, never, never>,\n value: unknown,\n operation: Operation,\n ): Effect.Effect<A, E> =>\n Schema.decodeUnknownEffect(schema)(value, { onExcessProperty: \"error\" }).pipe(\n Effect.mapError(() => configuration(operation)),\n );\n\n const request = <A>(\n method: ClientMethod,\n path: string,\n reply: Schema.Codec<A, unknown, never, never>,\n operation: Operation,\n body?: Schema.Json,\n ): Effect.Effect<A, E> =>\n client.json(method, path, body).pipe(\n Effect.mapError(fromClient(operation)),\n Effect.flatMap((raw) =>\n Schema.decodeEffect(reply)(raw).pipe(\n Effect.mapError(() => malformed(operation, method !== \"GET\")),\n ),\n ),\n );\n\n const remove = (path: string, operation: Operation): Effect.Effect<void, E> =>\n client.noContent(\"DELETE\", path).pipe(Effect.mapError(fromClient(operation)));\n\n const segment = (id: string) => encodeURIComponent(id);\n\n const query = (entries: Record<string, string | number | boolean | undefined>) => {\n const search = new URLSearchParams();\n\n for (const [key, value] of Object.entries(entries))\n if (value !== undefined) search.set(key, String(value));\n\n return search.size === 0 ? \"\" : `?${search}`;\n };\n\n return { fromClient, configuration, malformed, input, request, remove, segment, query, make };\n};\n"],"mappings":";;;;;;AAoBA,MAAa,YACX,QACA,SACG;CAGH,MAAM,cAAc,eAA0B,UAC5C,KAAK;EACH;EACA,QAAQ,MAAM;EACd,GAAI,MAAM,YAAY,KAAA,IAAY,CAAC,IAAI,EAAE,SAAS,MAAM,QAAQ;EAChE,GAAI,MAAM,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,QAAQ,MAAM,OAAO;EAC7D,GAAI,MAAM,qBAAqB,KAAA,IAAY,CAAC,IAAI,EAAE,kBAAkB,MAAM,iBAAiB;CAC7F,CAAC;CAEH,MAAM,iBAAiB,cACrB,KAAK;EAAE;EAAW,QAAQ;EAAiB,SAAS;CAAe,CAAC;CAEtE,MAAM,aAAa,WAAsB,aACvC,KAAK;EAAE;EAAW,QAAQ;EAAa,GAAI,WAAW,EAAE,SAAS,UAAU,IAAI,CAAC;CAAG,CAAC;;CAGtF,MAAM,SACJ,QACA,OACA,cAEA,OAAO,oBAAoB,MAAM,CAAC,CAAC,OAAO,EAAE,kBAAkB,QAAQ,CAAC,CAAC,CAAC,KACvE,OAAO,eAAe,cAAc,SAAS,CAAC,CAChD;CAEF,MAAM,WACJ,QACA,MACA,OACA,WACA,SAEA,OAAO,KAAK,QAAQ,MAAM,IAAI,CAAC,CAAC,KAC9B,OAAO,SAAS,WAAW,SAAS,CAAC,GACrC,OAAO,SAAS,QACd,OAAO,aAAa,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,KAC9B,OAAO,eAAe,UAAU,WAAW,WAAW,KAAK,CAAC,CAC9D,CACF,CACF;CAEF,MAAM,UAAU,MAAc,cAC5B,OAAO,UAAU,UAAU,IAAI,CAAC,CAAC,KAAK,OAAO,SAAS,WAAW,SAAS,CAAC,CAAC;CAE9E,MAAM,WAAW,OAAe,mBAAmB,EAAE;CAErD,MAAM,SAAS,YAAmE;EAChF,MAAM,SAAS,IAAI,gBAAgB;EAEnC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,GAC/C,IAAI,UAAU,KAAA,GAAW,OAAO,IAAI,KAAK,OAAO,KAAK,CAAC;EAExD,OAAO,OAAO,SAAS,IAAI,KAAK,IAAI;CACtC;CAEA,OAAO;EAAE;EAAY;EAAe;EAAW;EAAO;EAAS;EAAQ;EAAS;EAAO;CAAK;AAC9F"}
|