Skip to content

Commit

Permalink
S3 cleanup (#16039)
Browse files Browse the repository at this point in the history
Co-authored-by: Ciro Spaciari <[email protected]>
  • Loading branch information
Jarred-Sumner and cirospaciari authored Jan 4, 2025
1 parent 2043613 commit d5fc928
Show file tree
Hide file tree
Showing 40 changed files with 2,692 additions and 1,117 deletions.
96 changes: 77 additions & 19 deletions packages/bun-types/bun.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1226,8 +1226,56 @@ declare module "bun" {
*/
unlink(): Promise<void>;
}
interface NetworkSink extends FileSink {
/**
* Write a chunk of data to the network.
*
* If the network is not writable yet, the data is buffered.
*/
write(chunk: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer): number;
/**
* Flush the internal buffer, committing the data to the network.
*/
flush(): number | Promise<number>;
/**
* Finish the upload. This also flushes the internal buffer.
*/
end(error?: Error): number | Promise<number>;
}

interface S3FileOptions extends BlobPropertyBag {
interface S3Options extends BlobPropertyBag {
/**
* The ACL to used to write the file to S3. by default will omit the ACL header/parameter.
*/
acl?: /**
* Owner gets FULL_CONTROL. No one else has access rights (default).
*/
| "private"
/**
* Owner gets FULL_CONTROL. The AllUsers group (see Who is a grantee?) gets READ access.
*/
| "public-read"
/**
* Owner gets FULL_CONTROL. The AllUsers group gets READ and WRITE access. Granting this on a bucket is generally not recommended.
*/
| "public-read-write"
/**
* Owner gets FULL_CONTROL. Amazon EC2 gets READ access to GET an Amazon Machine Image (AMI) bundle from Amazon S3.
*/
| "aws-exec-read"
/**
* Owner gets FULL_CONTROL. The AuthenticatedUsers group gets READ access.
*/
| "authenticated-read"
/**
* Object owner gets FULL_CONTROL. Bucket owner gets READ access. If you specify this canned ACL when creating a bucket, Amazon S3 ignores it.
*/
| "bucket-owner-read"
/**
* Both the object owner and the bucket owner get FULL_CONTROL over the object. If you specify this canned ACL when creating a bucket, Amazon S3 ignores it.
*/
| "bucket-owner-full-control"
| "log-delivery-write";
/**
* The bucket to use for the S3 client. by default will use the `S3_BUCKET` and `AWS_BUCKET` environment variable, or deduce as first part of the path.
*/
Expand All @@ -1244,6 +1292,10 @@ declare module "bun" {
* The secret access key to use for the S3 client. By default, it will use the `S3_SECRET_ACCESS_KEY and `AWS_SECRET_ACCESS_KEY` environment variable.
*/
secretAccessKey?: string;
/**
* The session token to use for the S3 client. By default, it will use the `S3_SESSION_TOKEN` and `AWS_SESSION_TOKEN` environment variable.
*/
sessionToken?: string;

/**
* The endpoint to use for the S3 client. Defaults to `https://s3.{region}.amazonaws.com`, it will also use the `S3_ENDPOINT` and `AWS_ENDPOINT` environment variable.
Expand Down Expand Up @@ -1274,7 +1326,7 @@ declare module "bun" {
highWaterMark?: number;
}

interface S3FilePresignOptions extends S3FileOptions {
interface S3FilePresignOptions extends S3Options {
/**
* The number of seconds the presigned URL will be valid for. Defaults to 86400 (1 day).
*/
Expand All @@ -1290,7 +1342,7 @@ declare module "bun" {
* @param path - The path to the file. If bucket options is not provided or set in the path, it will be deduced from the path.
* @param options - The options to use for the S3 client.
*/
new (path: string | URL, options?: S3FileOptions): S3File;
new (path: string | URL, options?: S3Options): S3File;
/**
* The size of the file in bytes.
*/
Expand Down Expand Up @@ -1327,9 +1379,9 @@ declare module "bun" {
slice(contentType?: string): S3File;

/**
* Incremental writer to stream writes to S3, this is equivalent of using MultipartUpload and is suitable for large files.
* Incremental writer to stream writes to the network, this is equivalent of using MultipartUpload and is suitable for large files.
*/
writer(options?: S3FileOptions): FileSink;
writer(options?: S3Options): NetworkSink;

/**
* The readable stream of the file.
Expand Down Expand Up @@ -1364,7 +1416,7 @@ declare module "bun" {
*/
write(
data: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer | Request | Response | BunFile | S3File | Blob,
options?: S3FileOptions,
options?: S3Options,
): Promise<number>;

/**
Expand All @@ -1379,38 +1431,43 @@ declare module "bun" {
unlink(): Promise<void>;
}

namespace S3File {
interface S3Bucket {
/**
* Get a file from the bucket.
* @param path - The path to the file.
*/
(path: string, options?: S3Options): S3File;
/**
* Uploads the data to S3.
* Uploads the data to S3. This will overwrite the file if it already exists.
* @param data - The data to write.
* @param options - The options to use for the S3 client.
*/
function upload(
path: string | S3File,
write(
path: string,
data: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer | Request | Response | BunFile | S3File,
options?: S3FileOptions,
options?: S3Options,
): Promise<number>;

/**
* Returns a presigned URL for the file.
* @param options - The options to use for the presigned URL.
*/
function presign(path: string | S3File, options?: S3FilePresignOptions): string;
presign(path: string, options?: S3FilePresignOptions): string;

/**
* Deletes the file from S3.
*/
function unlink(path: string | S3File, options?: S3FileOptions): Promise<void>;
unlink(path: string, options?: S3Options): Promise<void>;

/**
* The size of the file in bytes.
*/
function size(path: string | S3File, options?: S3FileOptions): Promise<number>;
size(path: string, options?: S3Options): Promise<number>;

/**
* The size of the file in bytes.
* Does the file exist?
*/
function exists(path: string | S3File, options?: S3FileOptions): Promise<boolean>;
exists(path: string, options?: S3Options): Promise<boolean>;
}

/**
Expand Down Expand Up @@ -3268,11 +3325,12 @@ declare module "bun" {
* @param path - The path to the file. If bucket options is not provided or set in the path, it will be deduced from the path.
* @param options - The options to use for the S3 client.
*/
function s3(path: string | URL, options?: S3FileOptions): S3File;
function s3(path: string | URL, options?: S3Options): S3File;
/**
* The S3 file class.
* Create a configured S3 bucket reference.
* @param options - The options to use for the S3 client.
*/
const S3: typeof S3File;
function S3(options?: S3Options): S3Bucket;

/**
* Allocate a new [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) without zeroing the bytes.
Expand Down
7 changes: 6 additions & 1 deletion src/bun.js/ConsoleObject.zig
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@ const default_allocator = bun.default_allocator;
const JestPrettyFormat = @import("./test/pretty_format.zig").JestPrettyFormat;
const JSPromise = JSC.JSPromise;
const EventType = JSC.EventType;

const S3Bucket = @import("./webcore/S3Bucket.zig");
pub const shim = Shimmer("Bun", "ConsoleObject", @This());
pub const Type = *anyopaque;
pub const name = "Bun::ConsoleObject";
pub const include = "\"ConsoleObject.h\"";
pub const namespace = shim.namespace;

const Counter = std.AutoHashMapUnmanaged(u64, u32);

const BufferedWriter = std.io.BufferedWriter(4096, Output.WriterType);
Expand Down Expand Up @@ -2216,6 +2217,10 @@ pub const Formatter = struct {
);
},
.Class => {
if (S3Bucket.fromJS(value)) |s3bucket| {
S3Bucket.writeFormat(s3bucket, ConsoleObject.Formatter, this, writer_, enable_ansi_colors) catch {};
return;
}
var printable = ZigString.init(&name_buf);
value.getClassName(this.globalThis, &printable);
this.addForNewLine(printable.len);
Expand Down
7 changes: 3 additions & 4 deletions src/bun.js/api/BunObject.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const conv = std.builtin.CallingConvention.Unspecified;

const S3File = @import("../webcore/S3File.zig");
const S3Bucket = @import("../webcore/S3Bucket.zig");
/// How to add a new function or property to the Bun global
///
/// - Add a callback or property to the below struct
Expand Down Expand Up @@ -30,7 +31,7 @@ pub const BunObject = struct {
pub const registerMacro = toJSCallback(Bun.registerMacro);
pub const resolve = toJSCallback(Bun.resolve);
pub const resolveSync = toJSCallback(Bun.resolveSync);
pub const s3 = toJSCallback(WebCore.Blob.constructS3File);
pub const s3 = S3File.createJSS3File;
pub const serve = toJSCallback(Bun.serve);
pub const sha = toJSCallback(JSC.wrapStaticMethod(Crypto.SHA512_256, "hash_", true));
pub const shellEscape = toJSCallback(Bun.shellEscape);
Expand All @@ -56,7 +57,6 @@ pub const BunObject = struct {
pub const SHA384 = toJSGetter(Crypto.SHA384.getter);
pub const SHA512 = toJSGetter(Crypto.SHA512.getter);
pub const SHA512_256 = toJSGetter(Crypto.SHA512_256.getter);
pub const S3 = toJSGetter(JSC.WebCore.Blob.getJSS3FileConstructor);
pub const TOML = toJSGetter(Bun.getTOMLObject);
pub const Transpiler = toJSGetter(Bun.getTranspilerConstructor);
pub const argv = toJSGetter(Bun.getArgv);
Expand Down Expand Up @@ -109,7 +109,6 @@ pub const BunObject = struct {
@export(BunObject.FileSystemRouter, .{ .name = getterName("FileSystemRouter") });
@export(BunObject.MD4, .{ .name = getterName("MD4") });
@export(BunObject.MD5, .{ .name = getterName("MD5") });
@export(BunObject.S3, .{ .name = getterName("S3") });
@export(BunObject.SHA1, .{ .name = getterName("SHA1") });
@export(BunObject.SHA224, .{ .name = getterName("SHA224") });
@export(BunObject.SHA256, .{ .name = getterName("SHA256") });
Expand Down
3 changes: 3 additions & 0 deletions src/bun.js/bindings/BunClientData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
#include "JSDOMWrapper.h"
#include <JavaScriptCore/DeferredWorkTimer.h>
#include "NodeVM.h"
#include "JSS3Bucket.h"
#include "../../bake/BakeGlobalObject.h"

namespace WebCore {
using namespace JSC;

Expand All @@ -32,6 +34,7 @@ RefPtr<JSC::SourceProvider> createBuiltinsSourceProvider();
JSHeapData::JSHeapData(Heap& heap)
: m_heapCellTypeForJSWorkerGlobalScope(JSC::IsoHeapCellType::Args<Zig::GlobalObject>())
, m_heapCellTypeForNodeVMGlobalObject(JSC::IsoHeapCellType::Args<Bun::NodeVMGlobalObject>())
, m_heapCellTypeForJSS3Bucket(JSC::IsoHeapCellType::Args<Bun::JSS3Bucket>())
, m_heapCellTypeForBakeGlobalObject(JSC::IsoHeapCellType::Args<Bake::GlobalObject>())
, m_domBuiltinConstructorSpace ISO_SUBSPACE_INIT(heap, heap.cellHeapCellType, JSDOMBuiltinConstructorBase)
, m_domConstructorSpace ISO_SUBSPACE_INIT(heap, heap.cellHeapCellType, JSDOMConstructorBase)
Expand Down
1 change: 1 addition & 0 deletions src/bun.js/bindings/BunClientData.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ class JSHeapData {

JSC::IsoHeapCellType m_heapCellTypeForJSWorkerGlobalScope;
JSC::IsoHeapCellType m_heapCellTypeForNodeVMGlobalObject;
JSC::IsoHeapCellType m_heapCellTypeForJSS3Bucket;
JSC::IsoHeapCellType m_heapCellTypeForBakeGlobalObject;

private:
Expand Down
3 changes: 2 additions & 1 deletion src/bun.js/bindings/BunCommonStrings.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
// These ones don't need to be in BunBuiltinNames.h
// If we don't use it as an identifier name, but we want to avoid allocating the string frequently, put it in this list.
#define BUN_COMMON_STRINGS_EACH_NAME_NOT_BUILTIN_NAMES(macro) \
macro(SystemError)
macro(SystemError) \
macro(S3Error)
// clang-format on

#define BUN_COMMON_STRINGS_ACCESSOR_DEFINITION(name) \
Expand Down
2 changes: 1 addition & 1 deletion src/bun.js/bindings/BunObject+exports.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
macro(SHA512_256) \
macro(TOML) \
macro(Transpiler) \
macro(S3) \
macro(argv) \
macro(assetPrefix) \
macro(cwd) \
Expand Down Expand Up @@ -59,6 +58,7 @@
macro(resolve) \
macro(resolveSync) \
macro(s3) \
macro(S3) \
macro(serve) \
macro(sha) \
macro(shrink) \
Expand Down
3 changes: 2 additions & 1 deletion src/bun.js/bindings/BunObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ BUN_DECLARE_HOST_FUNCTION(Bun__DNSResolver__getCacheStats);
BUN_DECLARE_HOST_FUNCTION(Bun__fetch);
BUN_DECLARE_HOST_FUNCTION(Bun__fetchPreconnect);
BUN_DECLARE_HOST_FUNCTION(Bun__randomUUIDv7);
BUN_DECLARE_HOST_FUNCTION(Bun__S3Constructor);
namespace Bun {

using namespace JSC;
Expand Down Expand Up @@ -620,7 +621,6 @@ JSC_DEFINE_HOST_FUNCTION(functionFileURLToPath, (JSC::JSGlobalObject * globalObj
Glob BunObject_getter_wrap_Glob DontDelete|PropertyCallback
MD4 BunObject_getter_wrap_MD4 DontDelete|PropertyCallback
MD5 BunObject_getter_wrap_MD5 DontDelete|PropertyCallback
S3 BunObject_getter_wrap_S3 DontDelete|PropertyCallback
SHA1 BunObject_getter_wrap_SHA1 DontDelete|PropertyCallback
SHA224 BunObject_getter_wrap_SHA224 DontDelete|PropertyCallback
SHA256 BunObject_getter_wrap_SHA256 DontDelete|PropertyCallback
Expand Down Expand Up @@ -683,6 +683,7 @@ JSC_DEFINE_HOST_FUNCTION(functionFileURLToPath, (JSC::JSGlobalObject * globalObj
revision constructBunRevision ReadOnly|DontDelete|PropertyCallback
semver BunObject_getter_wrap_semver ReadOnly|DontDelete|PropertyCallback
s3 BunObject_callback_s3 DontDelete|Function 1
S3 Bun__S3Constructor DontDelete|Constructable|Function 1
sql constructBunSQLObject DontDelete|PropertyCallback
serve BunObject_callback_serve DontDelete|Function 1
sha BunObject_callback_sha DontDelete|Function 1
Expand Down
13 changes: 7 additions & 6 deletions src/bun.js/bindings/ErrorCode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,11 @@ export default [
["ERR_POSTGRES_CONNECTION_TIMEOUT", Error, "PostgresError"],
["ERR_POSTGRES_LIFETIME_TIMEOUT", Error, "PostgresError"],

// AWS
["ERR_AWS_MISSING_CREDENTIALS", Error],
["ERR_AWS_INVALID_METHOD", Error],
["ERR_AWS_INVALID_PATH", Error],
["ERR_AWS_INVALID_ENDPOINT", Error],
["ERR_AWS_INVALID_SIGNATURE", Error],
// S3
["ERR_S3_MISSING_CREDENTIALS", Error],
["ERR_S3_INVALID_METHOD", Error],
["ERR_S3_INVALID_PATH", Error],
["ERR_S3_INVALID_ENDPOINT", Error],
["ERR_S3_INVALID_SIGNATURE", Error],
["ERR_S3_INVALID_SESSION_TOKEN", Error],
] as ErrorCodeMapping;
10 changes: 5 additions & 5 deletions src/bun.js/bindings/JSDOMFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class JSDOMFile : public JSC::InternalFunction {

static JSDOMFile* create(JSC::VM& vm, JSGlobalObject* globalObject)
{
auto* zigGlobal = reinterpret_cast<Zig::GlobalObject*>(globalObject);
auto* zigGlobal = defaultGlobalObject(globalObject);
auto structure = createStructure(vm, globalObject, zigGlobal->functionPrototype());
auto* object = new (NotNull, JSC::allocateCell<JSDOMFile>(vm)) JSDOMFile(vm, structure);
object->finishCreation(vm);
Expand All @@ -65,7 +65,7 @@ class JSDOMFile : public JSC::InternalFunction {

static JSC_HOST_CALL_ATTRIBUTES JSC::EncodedJSValue construct(JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame)
{
Zig::GlobalObject* globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject);
auto* globalObject = defaultGlobalObject(lexicalGlobalObject);
JSC::VM& vm = globalObject->vm();
JSObject* newTarget = asObject(callFrame->newTarget());
auto* constructor = globalObject->JSDOMFileConstructor();
Expand All @@ -75,15 +75,15 @@ class JSDOMFile : public JSC::InternalFunction {

auto* functionGlobalObject = reinterpret_cast<Zig::GlobalObject*>(
// ShadowRealm functions belong to a different global object.
getFunctionRealm(globalObject, newTarget));
getFunctionRealm(lexicalGlobalObject, newTarget));
RETURN_IF_EXCEPTION(scope, {});
structure = InternalFunction::createSubclassStructure(
globalObject,
lexicalGlobalObject,
newTarget,
functionGlobalObject->JSBlobStructure());
}

void* ptr = JSDOMFile__construct(globalObject, callFrame);
void* ptr = JSDOMFile__construct(lexicalGlobalObject, callFrame);

if (UNLIKELY(!ptr)) {
return JSValue::encode(JSC::jsUndefined());
Expand Down
Loading

0 comments on commit d5fc928

Please sign in to comment.