Skip to content

Commit 0408064

Browse files
committed
add Bun.S3 and Bun.Secrets bindings
1 parent 41f31e0 commit 0408064

File tree

7 files changed

+327
-1
lines changed

7 files changed

+327
-1
lines changed

.changeset/fuzzy-books-rest.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"rescript-bun": patch
3+
---
4+
5+
Add Bun.Secrets bindings.

.changeset/mighty-masks-hang.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"rescript-bun": patch
3+
---
4+
5+
Add Bun.S3 bindings.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"description": "Use Bun with ReScript.",
77
"scripts": {
88
"build": "rescript build",
9-
"test": "bun test test/*.test.js",
9+
"test": "bun test ./test/*.test.js",
1010
"changeset": "changeset",
1111
"release": "changeset publish"
1212
},

src/Bun.js

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Bun.res

Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2497,6 +2497,287 @@ module CSRF = {
24972497
external verifyWithOptions: (string, verifyOptions) => bool = "verify"
24982498
}
24992499

2500+
/**
2501+
* Secure OS-backed secret storage (Keychain/libsecret/Credential Manager)
2502+
*
2503+
* Binds to Bun 1.3 `Bun.secrets` API for securely storing credentials.
2504+
*/
2505+
module Secrets = {
2506+
type getOptions = {service: string, name: string}
2507+
type setOptions = {service: string, name: string, value: string, allowUnrestrictedAccess?: bool}
2508+
type deleteOptions = {service: string, name: string}
2509+
2510+
/** Retrieve a stored credential. Returns `null` if not found. */
2511+
@module("bun") @scope("secrets")
2512+
external get: getOptions => promise<Null.t<string>> = "get"
2513+
2514+
/** Store or update a credential. Empty string deletes per Bun's semantics. */
2515+
@module("bun") @scope("secrets")
2516+
external set: setOptions => promise<unit> = "set"
2517+
2518+
/** Delete a stored credential. Returns true if deleted, false if not found. */
2519+
@module("bun") @scope("secrets")
2520+
external delete: deleteOptions => promise<bool> = "delete"
2521+
}
2522+
2523+
/**
2524+
* S3-compatible object storage client and file bindings
2525+
*
2526+
* Binds to Bun's `S3Client` and default `Bun.s3` instance.
2527+
*/
2528+
module S3 = {
2529+
/**
2530+
* Configuration for S3 operations
2531+
*/
2532+
type acl =
2533+
| @as("private") Private
2534+
| @as("public-read") PublicRead
2535+
| @as("public-read-write") PublicReadWrite
2536+
| @as("aws-exec-read") AwsExecRead
2537+
| @as("authenticated-read") AuthenticatedRead
2538+
| @as("bucket-owner-read") BucketOwnerRead
2539+
| @as("bucket-owner-full-control") BucketOwnerFullControl
2540+
| @as("log-delivery-write") LogDeliveryWrite
2541+
2542+
type storageClass =
2543+
| @as("STANDARD") Standard
2544+
| @as("DEEP_ARCHIVE") DeepArchive
2545+
| @as("EXPRESS_ONEZONE") ExpressOnezone
2546+
| @as("GLACIER") Glacier
2547+
| @as("GLACIER_IR") GlacierIr
2548+
| @as("INTELLIGENT_TIERING") IntelligentTiering
2549+
| @as("ONEZONE_IA") OnezoneIa
2550+
| @as("OUTPOSTS") Outposts
2551+
| @as("REDUCED_REDUNDANCY") ReducedRedundancy
2552+
| @as("SNOW") Snow
2553+
| @as("STANDARD_IA") StandardIa
2554+
2555+
type options = {
2556+
acl?: acl,
2557+
bucket?: string,
2558+
region?: string,
2559+
accessKeyId?: string,
2560+
secretAccessKey?: string,
2561+
sessionToken?: string,
2562+
endpoint?: string,
2563+
virtualHostedStyle?: bool,
2564+
partSize?: float,
2565+
queueSize?: float,
2566+
retry?: float,
2567+
@as("type") type_?: string,
2568+
storageClass?: storageClass,
2569+
/** @deprecated use partSize/queueSize */
2570+
highWaterMark?: float,
2571+
}
2572+
2573+
type presignOptions = {
2574+
...options,
2575+
expiresIn?: float,
2576+
method?: [#GET | #POST | #PUT | #DELETE | #HEAD],
2577+
}
2578+
2579+
type stats = {
2580+
size: float,
2581+
lastModified: Date.t,
2582+
etag: string,
2583+
@as("type") type_: string,
2584+
}
2585+
2586+
type listObjectsOptions = {
2587+
prefix?: string,
2588+
continuationToken?: string,
2589+
delimiter?: string,
2590+
maxKeys?: float,
2591+
startAfter?: string,
2592+
encodingType?: [#url],
2593+
fetchOwner?: bool,
2594+
}
2595+
2596+
type listObjectsResponse = {
2597+
commonPrefixes?: array<{prefix: string}>,
2598+
contents?: array<
2599+
{
2600+
checksumAlgorithm?: [#CRC32 | #CRC32C | #SHA1 | #SHA256 | #CRC64NVME],
2601+
checksumType?: [#COMPOSITE | #FULL_OBJECT],
2602+
etag?: string,
2603+
isLatest?: bool,
2604+
key?: string,
2605+
lastModified?: string,
2606+
owner?: {displayName?: string, id?: string},
2607+
restoreStatus?: string,
2608+
size?: float,
2609+
storageClass?: storageClass,
2610+
},
2611+
>,
2612+
isTruncated?: bool,
2613+
keyCount?: float,
2614+
maxKeys?: float,
2615+
name?: string,
2616+
nextContinuationToken?: string,
2617+
prefix?: string,
2618+
startAfter?: string,
2619+
}
2620+
2621+
module S3File = {
2622+
type t
2623+
2624+
@send external writer: (t, ~options: options=?) => fileSink = "writer"
2625+
2626+
@get external readable: t => ReadableStream.t<Uint8Array.t> = "readable"
2627+
@send external stream: t => ReadableStream.t<Uint8Array.t> = "stream"
2628+
2629+
@get external name: t => option<string> = "name"
2630+
@get external bucket: t => option<string> = "bucket"
2631+
2632+
@send external exists: t => promise<bool> = "exists"
2633+
2634+
@send external writeString: (t, string, ~options: options=?) => promise<float> = "write"
2635+
@send external writeBlob: (t, Blob.t, ~options: options=?) => promise<float> = "write"
2636+
@send external writeBunFile: (t, BunFile.t, ~options: options=?) => promise<float> = "write"
2637+
@send external writeS3File: (t, t, ~options: options=?) => promise<float> = "write"
2638+
@send
2639+
external writeArrayBuffer: (t, ArrayBuffer.t, ~options: options=?) => promise<float> = "write"
2640+
@send
2641+
external writeTypedArray: (t, ArrayBufferView.t, ~options: options=?) => promise<float> =
2642+
"write"
2643+
@send
2644+
external writeSharedArrayBuffer: (
2645+
t,
2646+
SharedArrayBuffer.t,
2647+
~options: options=?,
2648+
) => promise<float> = "write"
2649+
@send external writeResponse: (t, Response.t, ~options: options=?) => promise<float> = "write"
2650+
@send external writeRequest: (t, Request.t, ~options: options=?) => promise<float> = "write"
2651+
2652+
@send external presign: (t, ~options: presignOptions=?) => string = "presign"
2653+
2654+
@send external delete: t => promise<unit> = "delete"
2655+
@send external stat: t => promise<stats> = "stat"
2656+
}
2657+
2658+
module S3Client = {
2659+
type t
2660+
type listClientOptions = {
2661+
accessKeyId?: string,
2662+
secretAccessKey?: string,
2663+
sessionToken?: string,
2664+
region?: string,
2665+
bucket?: string,
2666+
endpoint?: string,
2667+
}
2668+
2669+
@module("bun") @new external make: options => t = "S3Client"
2670+
2671+
@send external file: (t, string, ~options: options=?) => S3File.t = "file"
2672+
@module("bun") @scope("S3Client")
2673+
external fileStatic: (string, ~options: options=?) => S3File.t = "file"
2674+
2675+
@send
2676+
external writeString: (t, string, string, ~options: options=?) => promise<float> = "write"
2677+
@send
2678+
external writeBlob: (t, string, Blob.t, ~options: options=?) => promise<float> = "write"
2679+
@send
2680+
external writeBunFile: (t, string, BunFile.t, ~options: options=?) => promise<float> = "write"
2681+
@send
2682+
external writeS3File: (t, string, S3File.t, ~options: options=?) => promise<float> = "write"
2683+
@send
2684+
external writeArrayBuffer: (t, string, ArrayBuffer.t, ~options: options=?) => promise<float> =
2685+
"write"
2686+
@send
2687+
external writeTypedArray: (
2688+
t,
2689+
string,
2690+
ArrayBufferView.t,
2691+
~options: options=?,
2692+
) => promise<float> = "write"
2693+
@send
2694+
external writeSharedArrayBuffer: (
2695+
t,
2696+
string,
2697+
SharedArrayBuffer.t,
2698+
~options: options=?,
2699+
) => promise<float> = "write"
2700+
@send
2701+
external writeResponse: (t, string, Response.t, ~options: options=?) => promise<float> = "write"
2702+
@send
2703+
external writeRequest: (t, string, Request.t, ~options: options=?) => promise<float> = "write"
2704+
2705+
@module("bun") @scope("S3Client")
2706+
external writeStringStatic: (string, string, ~options: options=?) => promise<float> = "write"
2707+
@module("bun") @scope("S3Client")
2708+
external writeBlobStatic: (string, Blob.t, ~options: options=?) => promise<float> = "write"
2709+
@module("bun") @scope("S3Client")
2710+
external writeBunFileStatic: (string, BunFile.t, ~options: options=?) => promise<float> =
2711+
"write"
2712+
@module("bun") @scope("S3Client")
2713+
external writeS3FileStatic: (string, S3File.t, ~options: options=?) => promise<float> = "write"
2714+
@module("bun") @scope("S3Client")
2715+
external writeArrayBufferStatic: (
2716+
string,
2717+
ArrayBuffer.t,
2718+
~options: options=?,
2719+
) => promise<float> = "write"
2720+
@module("bun") @scope("S3Client")
2721+
external writeTypedArrayStatic: (
2722+
string,
2723+
ArrayBufferView.t,
2724+
~options: options=?,
2725+
) => promise<float> = "write"
2726+
@module("bun") @scope("S3Client")
2727+
external writeSharedArrayBufferStatic: (
2728+
string,
2729+
SharedArrayBuffer.t,
2730+
~options: options=?,
2731+
) => promise<float> = "write"
2732+
@module("bun") @scope("S3Client")
2733+
external writeResponseStatic: (string, Response.t, ~options: options=?) => promise<float> =
2734+
"write"
2735+
@module("bun") @scope("S3Client")
2736+
external writeRequestStatic: (string, Request.t, ~options: options=?) => promise<float> =
2737+
"write"
2738+
2739+
@send external presign: (t, string, ~options: presignOptions=?) => string = "presign"
2740+
@module("bun") @scope("S3Client")
2741+
external presignStatic: (string, ~options: presignOptions=?) => string = "presign"
2742+
2743+
@send external unlink: (t, string, ~options: options=?) => promise<unit> = "unlink"
2744+
@module("bun") @scope("S3Client")
2745+
external unlinkStatic: (string, ~options: options=?) => promise<unit> = "unlink"
2746+
2747+
@send external delete: (t, string, ~options: options=?) => promise<unit> = "delete"
2748+
@module("bun") @scope("S3Client")
2749+
external deleteStatic: (string, ~options: options=?) => promise<unit> = "delete"
2750+
2751+
@send external size: (t, string, ~options: options=?) => promise<float> = "size"
2752+
@module("bun") @scope("S3Client")
2753+
external sizeStatic: (string, ~options: options=?) => promise<float> = "size"
2754+
2755+
@send external exists: (t, string, ~options: options=?) => promise<bool> = "exists"
2756+
@module("bun") @scope("S3Client")
2757+
external existsStatic: (string, ~options: options=?) => promise<bool> = "exists"
2758+
2759+
@send external stat: (t, string, ~options: options=?) => promise<stats> = "stat"
2760+
@module("bun") @scope("S3Client")
2761+
external statStatic: (string, ~options: options=?) => promise<stats> = "stat"
2762+
2763+
@send
2764+
external list: (
2765+
t,
2766+
~input: listObjectsOptions=?,
2767+
~options: listClientOptions=?,
2768+
) => promise<listObjectsResponse> = "list"
2769+
2770+
@module("bun") @scope("S3Client")
2771+
external listStatic: (
2772+
~input: listObjectsOptions=?,
2773+
~options: listClientOptions=?,
2774+
) => promise<listObjectsResponse> = "list"
2775+
}
2776+
2777+
@module("bun")
2778+
external s3: S3Client.t = "s3"
2779+
}
2780+
25002781
/**
25012782
* Resolve a `Promise` after milliseconds. This is like
25022783
* {@link setTimeout} except it returns a `Promise`.

test/S3.test.js

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/S3.test.res

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
open Test
2+
3+
describe("s3 presence", () => {
4+
test("S3Client constructor and default instance available", () => {
5+
let _bucket = Bun.S3.S3Client.make({})
6+
// no throw means basic bindings are present
7+
expect(true)->Expect.toBeTrue
8+
})
9+
})

0 commit comments

Comments
 (0)