Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

S3 cleanup #16039

Open
wants to merge 45 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
46b0211
S3 cleanup
Jarred-Sumner Dec 29, 2024
820a763
todos
Jarred-Sumner Dec 29, 2024
cae56ac
fixup
Jarred-Sumner Dec 29, 2024
3dda8ba
more
cirospaciari Dec 30, 2024
75d546b
more
cirospaciari Dec 31, 2024
f4c7e28
more
cirospaciari Dec 31, 2024
0f70d1c
more
cirospaciari Dec 31, 2024
0ad86a2
more
cirospaciari Dec 31, 2024
f05fb4d
add S3Error name
cirospaciari Dec 31, 2024
4cbee12
always include path, name and code on S3Errors
cirospaciari Dec 31, 2024
e5193ed
comment
cirospaciari Dec 31, 2024
94c4f3e
errors should always come from service not hardcoded
cirospaciari Dec 31, 2024
30c150b
handle this correctly
cirospaciari Dec 31, 2024
0411c1e
add test
cirospaciari Dec 31, 2024
9fba298
fix folders
cirospaciari Dec 31, 2024
8d067e2
handle backslashes
cirospaciari Dec 31, 2024
9b55bee
create S3Error in a proper way, dont list code twice
cirospaciari Dec 31, 2024
749adcc
dont enum code on SystemError
cirospaciari Dec 31, 2024
fcb2aba
cleanup
cirospaciari Dec 31, 2024
80fc4fd
atoms
cirospaciari Dec 31, 2024
2a52a0e
more fixes
cirospaciari Jan 1, 2025
8460c1b
make console.log better for S3
cirospaciari Jan 1, 2025
4f863ba
fix types
cirospaciari Jan 1, 2025
cb3bdb0
move format to S3File
cirospaciari Jan 1, 2025
126d04f
fix build and format
cirospaciari Jan 1, 2025
79c349e
endpoint
cirospaciari Jan 1, 2025
a1d5c17
{ + }
cirospaciari Jan 1, 2025
56d5ff5
NetworkSink
cirospaciari Jan 1, 2025
1119397
session token + ACL
cirospaciari Jan 3, 2025
0abab99
Merge branch 'main' into jarred/s3-
cirospaciari Jan 3, 2025
16e6c79
fix build
cirospaciari Jan 3, 2025
a3e69c1
fix s3file
cirospaciari Jan 3, 2025
c48dc44
Merge branch 'main' into jarred/s3-
cirospaciari Jan 3, 2025
625607f
Update src/bun.js/bindings/bindings.zig
Jarred-Sumner Jan 3, 2025
5332739
Update src/bun.js/bindings/bindings.zig
Jarred-Sumner Jan 3, 2025
305f86a
fix error types and messages, add the right comments and use getOptio…
cirospaciari Jan 3, 2025
de4b2b2
make it build again
cirospaciari Jan 3, 2025
0bfb15f
more
cirospaciari Jan 3, 2025
619fa2c
Merge branch 'main' into jarred/s3-
cirospaciari Jan 3, 2025
bc1503b
unref
cirospaciari Jan 3, 2025
469345f
readonly
cirospaciari Jan 3, 2025
dc63f0f
Merge branch 'main' into jarred/s3-
cirospaciari Jan 3, 2025
e6b5617
ERR_S3
cirospaciari Jan 3, 2025
bec9c09
Improve error message
Jarred-Sumner Jan 4, 2025
cc85666
dont wait on it self
cirospaciari Jan 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading