mirror of
https://github.com/oven-sh/bun
synced 2026-02-15 21:32:05 +00:00
Merge remote-tracking branch 'origin/main' into pfg/bun-test-diff
This commit is contained in:
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@@ -168,5 +168,5 @@
|
||||
"WebKit/WebInspectorUI": true,
|
||||
},
|
||||
"git.detectSubmodules": false,
|
||||
// "bun.test.customScript": "./build/debug/bun-debug test"
|
||||
"bun.test.customScript": "./build/debug/bun-debug test"
|
||||
}
|
||||
|
||||
@@ -547,6 +547,7 @@ src/http/AsyncHTTP.zig
|
||||
src/http/CertificateInfo.zig
|
||||
src/http/Decompressor.zig
|
||||
src/http/Encoding.zig
|
||||
src/http/ETag.zig
|
||||
src/http/FetchRedirect.zig
|
||||
src/http/HeaderBuilder.zig
|
||||
src/http/Headers.zig
|
||||
@@ -634,6 +635,7 @@ src/install/resolvers/folder_resolver.zig
|
||||
src/install/versioned_url.zig
|
||||
src/install/windows-shim/BinLinkingShim.zig
|
||||
src/install/windows-shim/bun_shim_impl.zig
|
||||
src/install/yarn.zig
|
||||
src/interchange.zig
|
||||
src/interchange/json.zig
|
||||
src/interchange/toml.zig
|
||||
|
||||
@@ -646,11 +646,10 @@ pub fn deinit(dev: *DevServer) void {
|
||||
.next_bundle = {
|
||||
var r = dev.next_bundle.requests.first;
|
||||
while (r) |request| {
|
||||
defer dev.deferred_request_pool.put(request);
|
||||
// TODO: deinitializing in this state is almost certainly an assertion failure.
|
||||
// This code is shipped in release because it is only reachable by experimenntal server components.
|
||||
bun.debugAssert(request.data.handler != .server_handler);
|
||||
request.data.deinit();
|
||||
defer request.data.deref();
|
||||
r = request.next;
|
||||
}
|
||||
dev.next_bundle.route_queue.deinit(allocator);
|
||||
@@ -1088,6 +1087,8 @@ fn deferRequest(
|
||||
const method = bun.http.Method.which(req.method()) orelse .POST;
|
||||
deferred.data = .{
|
||||
.route_bundle_index = route_bundle_index,
|
||||
.dev = dev,
|
||||
.ref_count = .init(),
|
||||
.handler = switch (kind) {
|
||||
.bundled_html_page => .{ .bundled_html_page = .{ .response = resp, .method = method } },
|
||||
.server_handler => .{
|
||||
@@ -1095,6 +1096,7 @@ fn deferRequest(
|
||||
},
|
||||
},
|
||||
};
|
||||
deferred.data.ref();
|
||||
resp.onAborted(*DeferredRequest, DeferredRequest.onAbort, &deferred.data);
|
||||
requests_array.prepend(deferred);
|
||||
}
|
||||
@@ -1533,8 +1535,20 @@ pub const DeferredRequest = struct {
|
||||
pub const List = std.SinglyLinkedList(DeferredRequest);
|
||||
pub const Node = List.Node;
|
||||
|
||||
const RefCount = bun.ptr.RefCount(@This(), "ref_count", deinitImpl, .{});
|
||||
|
||||
route_bundle_index: RouteBundle.Index,
|
||||
handler: Handler,
|
||||
dev: *DevServer,
|
||||
|
||||
/// This struct can have at most 2 references it:
|
||||
/// - The dev server (`dev.current_bundle.requests`)
|
||||
/// - uws.Response as a user data pointer
|
||||
ref_count: RefCount,
|
||||
|
||||
// expose `ref` and `deref` as public methods
|
||||
pub const ref = RefCount.ref;
|
||||
pub const deref = RefCount.deref;
|
||||
|
||||
const Handler = union(enum) {
|
||||
/// For a .framework route. This says to call and render the page.
|
||||
@@ -1560,10 +1574,15 @@ pub const DeferredRequest = struct {
|
||||
assert(this.handler == .aborted);
|
||||
}
|
||||
|
||||
/// *WARNING*: Do not call this directly, instead call `.deref()`
|
||||
///
|
||||
/// Calling this is only required if the desired handler is going to be avoided,
|
||||
/// such as for bundling failures or aborting the server.
|
||||
/// Does not free the underlying `DeferredRequest.Node`
|
||||
fn deinit(this: *DeferredRequest) void {
|
||||
fn deinitImpl(this: *DeferredRequest) void {
|
||||
bun.assert(this.ref_count.active_counts == 0);
|
||||
|
||||
defer this.dev.deferred_request_pool.put(@fieldParentPtr("data", this));
|
||||
switch (this.handler) {
|
||||
.server_handler => |*saved| saved.deinit(),
|
||||
.bundled_html_page, .aborted => {},
|
||||
@@ -1572,17 +1591,18 @@ pub const DeferredRequest = struct {
|
||||
|
||||
/// Deinitializes state by aborting the connection.
|
||||
fn abort(this: *DeferredRequest) void {
|
||||
switch (this.handler) {
|
||||
var handler = this.handler;
|
||||
this.handler = .aborted;
|
||||
switch (handler) {
|
||||
.server_handler => |*saved| {
|
||||
saved.response.endWithoutBody(true);
|
||||
saved.deinit();
|
||||
saved.ctx.onAbort(saved.response);
|
||||
saved.js_request.deinit();
|
||||
},
|
||||
.bundled_html_page => |r| {
|
||||
r.response.endWithoutBody(true);
|
||||
},
|
||||
.aborted => return,
|
||||
.aborted => {},
|
||||
}
|
||||
this.handler = .aborted;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1997,8 +2017,8 @@ pub fn finalizeBundle(
|
||||
Output.debug("current_bundle.requests.first != null. this leaves pending requests without an error page!", .{});
|
||||
}
|
||||
while (current_bundle.requests.popFirst()) |node| {
|
||||
defer dev.deferred_request_pool.put(node);
|
||||
const req = &node.data;
|
||||
defer req.deref();
|
||||
req.abort();
|
||||
}
|
||||
}
|
||||
@@ -2504,8 +2524,8 @@ pub fn finalizeBundle(
|
||||
|
||||
var inspector_agent = dev.inspector();
|
||||
while (current_bundle.requests.popFirst()) |node| {
|
||||
defer dev.deferred_request_pool.put(node);
|
||||
const req = &node.data;
|
||||
defer req.deref();
|
||||
|
||||
const rb = dev.routeBundlePtr(req.route_bundle_index);
|
||||
rb.server_state = .possible_bundling_failures;
|
||||
@@ -2608,8 +2628,8 @@ pub fn finalizeBundle(
|
||||
defer dev.graph_safety_lock.lock();
|
||||
|
||||
while (current_bundle.requests.popFirst()) |node| {
|
||||
defer dev.deferred_request_pool.put(node);
|
||||
const req = &node.data;
|
||||
defer req.deref();
|
||||
|
||||
const rb = dev.routeBundlePtr(req.route_bundle_index);
|
||||
rb.server_state = .loaded;
|
||||
@@ -3951,7 +3971,7 @@ pub fn onPluginsResolved(dev: *DevServer, plugins: ?*Plugin) !void {
|
||||
pub fn onPluginsRejected(dev: *DevServer) !void {
|
||||
dev.plugin_state = .err;
|
||||
while (dev.next_bundle.requests.popFirst()) |item| {
|
||||
defer dev.deferred_request_pool.put(item);
|
||||
defer item.data.deref();
|
||||
item.data.abort();
|
||||
}
|
||||
dev.next_bundle.route_queue.clearRetainingCapacity();
|
||||
|
||||
@@ -199,6 +199,28 @@ pub fn getRequest(self: AnyRequestContext) ?*uws.Request {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn onAbort(self: AnyRequestContext, response: uws.AnyResponse) void {
|
||||
if (self.tagged_pointer.isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (self.tagged_pointer.tag()) {
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(HTTPServer.RequestContext))) => {
|
||||
self.tagged_pointer.as(HTTPServer.RequestContext).onAbort(response.TCP);
|
||||
},
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(HTTPSServer.RequestContext))) => {
|
||||
self.tagged_pointer.as(HTTPSServer.RequestContext).onAbort(response.SSL);
|
||||
},
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(DebugHTTPServer.RequestContext))) => {
|
||||
self.tagged_pointer.as(DebugHTTPServer.RequestContext).onAbort(response.TCP);
|
||||
},
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(DebugHTTPSServer.RequestContext))) => {
|
||||
self.tagged_pointer.as(DebugHTTPSServer.RequestContext).onAbort(response.SSL);
|
||||
},
|
||||
else => @panic("Unexpected AnyRequestContext tag"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deref(self: AnyRequestContext) void {
|
||||
if (self.tagged_pointer.isNull()) {
|
||||
return;
|
||||
|
||||
@@ -37,12 +37,9 @@ pub fn initFromAnyBlob(blob: *const AnyBlob, options: InitFromBytesOptions) *Sta
|
||||
|
||||
// Generate ETag if not already present
|
||||
if (headers.get("etag") == null) {
|
||||
const slice = blob.slice();
|
||||
const hash = std.hash.XxHash64.hash(0, slice);
|
||||
|
||||
var etag_buf: [32]u8 = undefined;
|
||||
const etag_str = std.fmt.bufPrint(&etag_buf, "\"{x}\"", .{hash}) catch bun.outOfMemory();
|
||||
headers.append("ETag", etag_str) catch bun.outOfMemory();
|
||||
if (blob.slice().len > 0) {
|
||||
ETag.appendToHeaders(blob.slice(), &headers) catch bun.outOfMemory();
|
||||
}
|
||||
}
|
||||
|
||||
return bun.new(StaticRoute, .{
|
||||
@@ -151,16 +148,9 @@ pub fn fromJS(globalThis: *jsc.JSGlobalObject, argument: jsc.JSValue) bun.JSErro
|
||||
|
||||
// Generate ETag if not already present
|
||||
if (headers.get("etag") == null) {
|
||||
const slice = blob.slice();
|
||||
const hash = std.hash.XxHash64.hash(0, slice);
|
||||
|
||||
var etag_buf: [32]u8 = undefined;
|
||||
const etag_str = std.fmt.bufPrint(&etag_buf, "\"{x}\"", .{hash}) catch bun.outOfMemory();
|
||||
headers.append("ETag", etag_str) catch {
|
||||
var mutable_blob = blob;
|
||||
mutable_blob.detach();
|
||||
return globalThis.throwOutOfMemory();
|
||||
};
|
||||
if (blob.slice().len > 0) {
|
||||
try ETag.appendToHeaders(blob.slice(), &headers);
|
||||
}
|
||||
}
|
||||
|
||||
return bun.new(StaticRoute, .{
|
||||
@@ -181,18 +171,7 @@ pub fn fromJS(globalThis: *jsc.JSGlobalObject, argument: jsc.JSValue) bun.JSErro
|
||||
pub fn onHEADRequest(this: *StaticRoute, req: *uws.Request, resp: AnyResponse) void {
|
||||
// Check If-None-Match for HEAD requests with 200 status
|
||||
if (this.status_code == 200) {
|
||||
if (evaluateIfNoneMatch(this, req)) {
|
||||
// Return 304 Not Modified
|
||||
req.setYield(false);
|
||||
this.ref();
|
||||
if (this.server) |server| {
|
||||
server.onPendingRequest();
|
||||
resp.timeout(server.config().idleTimeout);
|
||||
}
|
||||
this.doWriteStatus(304, resp);
|
||||
this.doWriteHeaders(resp);
|
||||
resp.endWithoutBody(resp.shouldCloseConnection());
|
||||
this.onResponseComplete(resp);
|
||||
if (this.render304NotModifiedIfNoneMatch(req, resp)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -235,18 +214,7 @@ pub fn onRequest(this: *StaticRoute, req: *uws.Request, resp: AnyResponse) void
|
||||
pub fn onGET(this: *StaticRoute, req: *uws.Request, resp: AnyResponse) void {
|
||||
// Check If-None-Match for GET requests with 200 status
|
||||
if (this.status_code == 200) {
|
||||
if (evaluateIfNoneMatch(this, req)) {
|
||||
// Return 304 Not Modified
|
||||
req.setYield(false);
|
||||
this.ref();
|
||||
if (this.server) |server| {
|
||||
server.onPendingRequest();
|
||||
resp.timeout(server.config().idleTimeout);
|
||||
}
|
||||
this.doWriteStatus(304, resp);
|
||||
this.doWriteHeaders(resp);
|
||||
resp.endWithoutBody(resp.shouldCloseConnection());
|
||||
this.onResponseComplete(resp);
|
||||
if (this.render304NotModifiedIfNoneMatch(req, resp)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -292,7 +260,7 @@ fn onResponseComplete(this: *StaticRoute, resp: AnyResponse) void {
|
||||
this.deref();
|
||||
}
|
||||
|
||||
pub fn doRenderBlob(this: *StaticRoute, resp: AnyResponse, did_finish: *bool) void {
|
||||
fn doRenderBlob(this: *StaticRoute, resp: AnyResponse, did_finish: *bool) void {
|
||||
// We are not corked
|
||||
// The body is small
|
||||
// Faster to do the memcpy than to do the two network calls
|
||||
@@ -305,7 +273,7 @@ pub fn doRenderBlob(this: *StaticRoute, resp: AnyResponse, did_finish: *bool) vo
|
||||
}
|
||||
}
|
||||
|
||||
pub fn doRenderBlobCorked(this: *StaticRoute, resp: AnyResponse, did_finish: *bool) void {
|
||||
fn doRenderBlobCorked(this: *StaticRoute, resp: AnyResponse, did_finish: *bool) void {
|
||||
this.renderMetadata(resp);
|
||||
this.renderBytes(resp, did_finish);
|
||||
}
|
||||
@@ -382,68 +350,42 @@ pub fn onWithMethod(this: *StaticRoute, method: bun.http.Method, resp: AnyRespon
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse a single entity tag from a string, returns the tag without quotes and whether it's weak
|
||||
fn parseEntityTag(tag_str: []const u8) struct { tag: []const u8, is_weak: bool } {
|
||||
var str = std.mem.trim(u8, tag_str, " \t");
|
||||
|
||||
// Check for weak indicator
|
||||
var is_weak = false;
|
||||
if (std.mem.startsWith(u8, str, "W/")) {
|
||||
is_weak = true;
|
||||
str = str[2..];
|
||||
str = std.mem.trimLeft(u8, str, " \t");
|
||||
}
|
||||
|
||||
// Remove surrounding quotes
|
||||
if (str.len >= 2 and str[0] == '"' and str[str.len - 1] == '"') {
|
||||
str = str[1 .. str.len - 1];
|
||||
}
|
||||
|
||||
return .{ .tag = str, .is_weak = is_weak };
|
||||
}
|
||||
|
||||
/// Perform weak comparison between two entity tags according to RFC 9110 Section 8.8.3.2
|
||||
fn weakEntityTagMatch(tag1: []const u8, is_weak1: bool, tag2: []const u8, is_weak2: bool) bool {
|
||||
_ = is_weak1;
|
||||
_ = is_weak2;
|
||||
// For weak comparison, we only compare the opaque tag values, ignoring weak indicators
|
||||
return std.mem.eql(u8, tag1, tag2);
|
||||
}
|
||||
|
||||
/// Evaluate If-None-Match condition according to RFC 9110 Section 13.1.2
|
||||
fn evaluateIfNoneMatch(this: *StaticRoute, req: *uws.Request) bool {
|
||||
fn render304NotModifiedIfNoneMatch(this: *StaticRoute, req: *uws.Request, resp: AnyResponse) bool {
|
||||
const if_none_match = req.header("if-none-match") orelse return false;
|
||||
|
||||
// Get the ETag from our headers
|
||||
const our_etag = this.headers.get("etag") orelse return false;
|
||||
const our_parsed = parseEntityTag(our_etag);
|
||||
|
||||
// Handle "*" case
|
||||
if (std.mem.eql(u8, std.mem.trim(u8, if_none_match, " \t"), "*")) {
|
||||
return true; // Condition is false, so we should return 304
|
||||
const etag = this.headers.get("etag") orelse return false;
|
||||
if (if_none_match.len == 0 or etag.len == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse comma-separated list of entity tags
|
||||
var iter = std.mem.splitScalar(u8, if_none_match, ',');
|
||||
while (iter.next()) |tag_str| {
|
||||
const parsed = parseEntityTag(tag_str);
|
||||
if (weakEntityTagMatch(our_parsed.tag, our_parsed.is_weak, parsed.tag, parsed.is_weak)) {
|
||||
return true; // Condition is false, so we should return 304
|
||||
}
|
||||
if (!ETag.ifNoneMatch(etag, if_none_match)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false; // Condition is true, continue with normal processing
|
||||
// Return 304 Not Modified
|
||||
req.setYield(false);
|
||||
this.ref();
|
||||
if (this.server) |server| {
|
||||
server.onPendingRequest();
|
||||
resp.timeout(server.config().idleTimeout);
|
||||
}
|
||||
this.doWriteStatus(304, resp);
|
||||
this.doWriteHeaders(resp);
|
||||
resp.endWithoutBody(resp.shouldCloseConnection());
|
||||
this.onResponseComplete(resp);
|
||||
return true;
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
const bun = @import("bun");
|
||||
const jsc = bun.jsc;
|
||||
const Headers = bun.http.Headers;
|
||||
const api = bun.schema.api;
|
||||
const AnyServer = jsc.API.AnyServer;
|
||||
const writeStatus = bun.api.server.writeStatus;
|
||||
const AnyBlob = jsc.WebCore.Blob.Any;
|
||||
|
||||
const ETag = bun.http.ETag;
|
||||
const Headers = bun.http.Headers;
|
||||
|
||||
const uws = bun.uws;
|
||||
const AnyResponse = uws.AnyResponse;
|
||||
|
||||
@@ -426,6 +426,17 @@ pub fn constructJSON(
|
||||
did_succeed = true;
|
||||
return bun.new(Response, response).toJS(globalThis);
|
||||
}
|
||||
|
||||
fn validateRedirectStatusCode(globalThis: *jsc.JSGlobalObject, status_code: i32) bun.JSError!u16 {
|
||||
switch (status_code) {
|
||||
301, 302, 303, 307, 308 => return @intCast(status_code),
|
||||
else => {
|
||||
const err = globalThis.createRangeErrorInstance("Failed to execute 'redirect' on 'Response': Invalid status code", .{});
|
||||
return globalThis.throwValue(err);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn constructRedirect(
|
||||
globalThis: *jsc.JSGlobalObject,
|
||||
callframe: *jsc.CallFrame,
|
||||
@@ -464,36 +475,17 @@ pub fn constructRedirect(
|
||||
|
||||
if (args.nextEat()) |init| {
|
||||
if (init.isUndefinedOrNull()) {} else if (init.isNumber()) {
|
||||
const status_number = init.toInt32();
|
||||
// Validate redirect status codes (301, 302, 303, 307, 308)
|
||||
if (status_number == 301 or status_number == 302 or status_number == 303 or status_number == 307 or status_number == 308) {
|
||||
response.init.status_code = @as(u16, @intCast(status_number));
|
||||
} else {
|
||||
const err = globalThis.createRangeErrorInstance("Failed to execute 'redirect' on 'Response': Invalid status code", .{});
|
||||
return globalThis.throwValue(err);
|
||||
}
|
||||
} else if (init.isObject()) {
|
||||
// Only process object init values to prevent crash with non-object values
|
||||
if (Response.Init.init(globalThis, init) catch |err|
|
||||
if (err == error.JSError) return .zero else null) |_init|
|
||||
{
|
||||
// Validate that status code is a valid redirect status if provided
|
||||
if (_init.status_code != 200) { // 200 is the default, so if it's changed, validate it
|
||||
if (_init.status_code == 301 or _init.status_code == 302 or _init.status_code == 303 or _init.status_code == 307 or _init.status_code == 308) {
|
||||
response.init = _init;
|
||||
} else {
|
||||
response.init.deinit(bun.default_allocator);
|
||||
const err = globalThis.createRangeErrorInstance("Failed to execute 'redirect' on 'Response': Invalid status code", .{});
|
||||
return globalThis.throwValue(err);
|
||||
}
|
||||
} else {
|
||||
response.init = _init;
|
||||
response.init.status_code = 302; // Default redirect status
|
||||
}
|
||||
response.init.status_code = try validateRedirectStatusCode(globalThis, init.toInt32());
|
||||
} else if (try Response.Init.init(globalThis, init)) |_init| {
|
||||
errdefer response.init.deinit(bun.default_allocator);
|
||||
response.init = _init;
|
||||
|
||||
if (_init.status_code != 200) {
|
||||
response.init.status_code = try validateRedirectStatusCode(globalThis, _init.status_code);
|
||||
}
|
||||
}
|
||||
// Non-object, non-number init values are ignored (like strings, booleans, etc.)
|
||||
}
|
||||
|
||||
if (globalThis.hasException()) {
|
||||
return .zero;
|
||||
}
|
||||
@@ -642,7 +634,13 @@ pub const Init = struct {
|
||||
if (!response_init.isCell())
|
||||
return null;
|
||||
|
||||
if (response_init.jsType() == .DOMWrapper) {
|
||||
const js_type = response_init.jsType();
|
||||
|
||||
if (!js_type.isObject()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (js_type == .DOMWrapper) {
|
||||
// fast path: it's a Request object or a Response object
|
||||
// we can skip calling JS getters
|
||||
if (response_init.asDirect(Request)) |req| {
|
||||
|
||||
@@ -702,7 +702,7 @@ pub const Signal = struct {
|
||||
pub fn HTTPServerWritable(comptime ssl: bool) type {
|
||||
return struct {
|
||||
const UWSResponse = uws.NewApp(ssl).Response;
|
||||
res: *UWSResponse,
|
||||
res: ?*UWSResponse,
|
||||
buffer: bun.ByteList,
|
||||
pooled_buffer: ?*WebCore.ByteListPool.Node = null,
|
||||
offset: Blob.SizeType = 0,
|
||||
@@ -774,22 +774,26 @@ pub fn HTTPServerWritable(comptime ssl: bool) type {
|
||||
bun.assert(!this.done);
|
||||
defer log("send: {d} bytes (backpressure: {any})", .{ buf.len, this.has_backpressure });
|
||||
|
||||
if (this.requested_end and !this.res.state().isHttpWriteCalled()) {
|
||||
const res = this.res orelse {
|
||||
return false;
|
||||
};
|
||||
|
||||
if (this.requested_end and !res.state().isHttpWriteCalled()) {
|
||||
this.handleFirstWriteIfNecessary();
|
||||
const success = this.res.tryEnd(buf, this.end_len, false);
|
||||
const success = res.tryEnd(buf, this.end_len, false);
|
||||
if (success) {
|
||||
this.has_backpressure = false;
|
||||
this.handleWrote(this.end_len);
|
||||
} else {
|
||||
} else if (this.res != null) {
|
||||
this.has_backpressure = true;
|
||||
this.res.onWritable(*@This(), onWritable, this);
|
||||
res.onWritable(*@This(), onWritable, this);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
// clean this so we know when its relevant or not
|
||||
this.end_len = 0;
|
||||
// we clear the onWritable handler so uWS can handle the backpressure for us
|
||||
this.res.clearOnWritable();
|
||||
res.clearOnWritable();
|
||||
this.handleFirstWriteIfNecessary();
|
||||
// uWebSockets lacks a tryWrite() function
|
||||
// This means that backpressure will be handled by appending to an "infinite" memory buffer
|
||||
@@ -797,10 +801,10 @@ pub fn HTTPServerWritable(comptime ssl: bool) type {
|
||||
// so in this scenario, we just append to the buffer
|
||||
// and report success
|
||||
if (this.requested_end) {
|
||||
this.res.end(buf, false);
|
||||
res.end(buf, false);
|
||||
this.has_backpressure = false;
|
||||
} else {
|
||||
this.has_backpressure = this.res.write(buf) == .backpressure;
|
||||
this.has_backpressure = res.write(buf) == .backpressure;
|
||||
}
|
||||
this.handleWrote(buf.len);
|
||||
return true;
|
||||
@@ -821,7 +825,6 @@ pub fn HTTPServerWritable(comptime ssl: bool) type {
|
||||
// onWritable reset backpressure state to allow flushing
|
||||
this.has_backpressure = false;
|
||||
if (this.aborted) {
|
||||
this.res.clearOnWritable();
|
||||
this.signal.close(null);
|
||||
this.flushPromise();
|
||||
this.finalize();
|
||||
@@ -837,7 +840,6 @@ pub fn HTTPServerWritable(comptime ssl: bool) type {
|
||||
// if we have nothing to write, we are done
|
||||
if (chunk.len == 0) {
|
||||
if (this.done) {
|
||||
this.res.clearOnWritable();
|
||||
this.signal.close(null);
|
||||
this.flushPromise();
|
||||
this.finalize();
|
||||
@@ -851,7 +853,9 @@ pub fn HTTPServerWritable(comptime ssl: bool) type {
|
||||
total_written = chunk.len;
|
||||
|
||||
if (this.requested_end) {
|
||||
this.res.clearOnWritable();
|
||||
if (this.res) |res| {
|
||||
res.clearOnWritable();
|
||||
}
|
||||
this.signal.close(null);
|
||||
this.flushPromise();
|
||||
this.finalize();
|
||||
@@ -875,7 +879,7 @@ pub fn HTTPServerWritable(comptime ssl: bool) type {
|
||||
}
|
||||
|
||||
pub fn start(this: *@This(), stream_start: Start) bun.sys.Maybe(void) {
|
||||
if (this.aborted or this.res.hasResponded()) {
|
||||
if (this.aborted or this.res == null or this.res.?.hasResponded()) {
|
||||
this.markDone();
|
||||
this.signal.close(null);
|
||||
return .success;
|
||||
@@ -985,7 +989,7 @@ pub fn HTTPServerWritable(comptime ssl: bool) type {
|
||||
return .success;
|
||||
}
|
||||
|
||||
if (this.res.hasResponded()) {
|
||||
if (this.res == null or this.res.?.hasResponded()) {
|
||||
this.markDone();
|
||||
this.signal.close(null);
|
||||
}
|
||||
@@ -1040,7 +1044,7 @@ pub fn HTTPServerWritable(comptime ssl: bool) type {
|
||||
return .{ .owned = 0 };
|
||||
}
|
||||
|
||||
if (this.res.hasResponded()) {
|
||||
if (this.res == null or this.res.?.hasResponded()) {
|
||||
this.signal.close(null);
|
||||
this.markDone();
|
||||
return .{ .done = {} };
|
||||
@@ -1098,7 +1102,7 @@ pub fn HTTPServerWritable(comptime ssl: bool) type {
|
||||
return .{ .owned = 0 };
|
||||
}
|
||||
|
||||
if (this.res.hasResponded()) {
|
||||
if (this.res == null or this.res.?.hasResponded()) {
|
||||
this.signal.close(null);
|
||||
this.markDone();
|
||||
return .{ .done = {} };
|
||||
@@ -1138,7 +1142,7 @@ pub fn HTTPServerWritable(comptime ssl: bool) type {
|
||||
return .success;
|
||||
}
|
||||
|
||||
if (this.done or this.res.hasResponded()) {
|
||||
if (this.done or this.res == null or this.res.?.hasResponded()) {
|
||||
this.signal.close(err);
|
||||
this.markDone();
|
||||
this.finalize();
|
||||
@@ -1167,7 +1171,7 @@ pub fn HTTPServerWritable(comptime ssl: bool) type {
|
||||
return .{ .result = jsc.JSValue.jsNumber(0) };
|
||||
}
|
||||
|
||||
if (this.done or this.res.hasResponded()) {
|
||||
if (this.done or this.res == null or this.res.?.hasResponded()) {
|
||||
this.requested_end = true;
|
||||
this.signal.close(null);
|
||||
this.markDone();
|
||||
@@ -1188,7 +1192,9 @@ pub fn HTTPServerWritable(comptime ssl: bool) type {
|
||||
return .{ .result = value };
|
||||
}
|
||||
} else {
|
||||
this.res.end("", false);
|
||||
if (this.res) |res| {
|
||||
res.end("", false);
|
||||
}
|
||||
}
|
||||
|
||||
this.markDone();
|
||||
@@ -1206,6 +1212,7 @@ pub fn HTTPServerWritable(comptime ssl: bool) type {
|
||||
pub fn abort(this: *@This()) void {
|
||||
log("onAborted()", .{});
|
||||
this.done = true;
|
||||
this.res = null;
|
||||
this.unregisterAutoFlusher();
|
||||
|
||||
this.aborted = true;
|
||||
@@ -1222,8 +1229,9 @@ pub fn HTTPServerWritable(comptime ssl: bool) type {
|
||||
}
|
||||
|
||||
fn registerAutoFlusher(this: *@This()) void {
|
||||
const res = this.res orelse return;
|
||||
// if we enqueue data we should reset the timeout
|
||||
this.res.resetTimeout();
|
||||
res.resetTimeout();
|
||||
if (!this.auto_flusher.registered)
|
||||
AutoFlusher.registerDeferredMicrotaskWithTypeUnchecked(@This(), this, this.globalThis.bunVM());
|
||||
}
|
||||
@@ -1268,14 +1276,19 @@ pub fn HTTPServerWritable(comptime ssl: bool) type {
|
||||
log("finalize()", .{});
|
||||
if (!this.done) {
|
||||
this.unregisterAutoFlusher();
|
||||
// make sure we detached the handlers before flushing inside the finalize function
|
||||
this.res.clearOnWritable();
|
||||
this.res.clearAborted();
|
||||
this.res.clearOnData();
|
||||
if (this.res) |res| {
|
||||
// make sure we detached the handlers before flushing inside the finalize function
|
||||
res.clearOnWritable();
|
||||
res.clearAborted();
|
||||
res.clearOnData();
|
||||
}
|
||||
_ = this.flushNoWait();
|
||||
this.done = true;
|
||||
// is actually fine to call this if the socket is closed because of flushNoWait, the free will be defered by usockets
|
||||
this.res.endStream(false);
|
||||
|
||||
if (this.res) |res| {
|
||||
// is actually fine to call this if the socket is closed because of flushNoWait, the free will be defered by usockets
|
||||
res.endStream(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (comptime !FeatureFlags.http_buffer_pooling) {
|
||||
|
||||
@@ -2424,6 +2424,7 @@ pub const ThreadlocalAsyncHTTP = struct {
|
||||
async_http: AsyncHTTP,
|
||||
};
|
||||
|
||||
pub const ETag = @import("./http/ETag.zig");
|
||||
pub const Method = @import("./http/Method.zig").Method;
|
||||
pub const Headers = @import("./http/Headers.zig");
|
||||
pub const MimeType = @import("./http/MimeType.zig");
|
||||
|
||||
65
src/http/ETag.zig
Normal file
65
src/http/ETag.zig
Normal file
@@ -0,0 +1,65 @@
|
||||
const ETag = @This();
|
||||
|
||||
/// Parse a single entity tag from a string, returns the tag without quotes and whether it's weak
|
||||
fn parse(tag_str: []const u8) struct { tag: []const u8, is_weak: bool } {
|
||||
var str = std.mem.trim(u8, tag_str, " \t");
|
||||
|
||||
// Check for weak indicator
|
||||
var is_weak = false;
|
||||
if (bun.strings.hasPrefix(str, "W/")) {
|
||||
is_weak = true;
|
||||
str = str[2..];
|
||||
str = std.mem.trimLeft(u8, str, " \t");
|
||||
}
|
||||
|
||||
// Remove surrounding quotes
|
||||
if (str.len >= 2 and str[0] == '"' and str[str.len - 1] == '"') {
|
||||
str = str[1 .. str.len - 1];
|
||||
}
|
||||
|
||||
return .{ .tag = str, .is_weak = is_weak };
|
||||
}
|
||||
|
||||
/// Perform weak comparison between two entity tags according to RFC 9110 Section 8.8.3.2
|
||||
fn weakMatch(tag1: []const u8, is_weak1: bool, tag2: []const u8, is_weak2: bool) bool {
|
||||
_ = is_weak1;
|
||||
_ = is_weak2;
|
||||
// For weak comparison, we only compare the opaque tag values, ignoring weak indicators
|
||||
return std.mem.eql(u8, tag1, tag2);
|
||||
}
|
||||
|
||||
pub fn appendToHeaders(bytes: []const u8, headers: *bun.http.Headers) !void {
|
||||
const hash = std.hash.XxHash64.hash(0, bytes);
|
||||
|
||||
var etag_buf: [40]u8 = undefined;
|
||||
const etag_str = std.fmt.bufPrint(&etag_buf, "\"{}\"", .{bun.fmt.hexIntLower(hash)}) catch unreachable;
|
||||
try headers.append("etag", etag_str);
|
||||
}
|
||||
|
||||
pub fn ifNoneMatch(
|
||||
/// "ETag" header
|
||||
etag: []const u8,
|
||||
/// "If-None-Match" header
|
||||
if_none_match: []const u8,
|
||||
) bool {
|
||||
const our_parsed = parse(etag);
|
||||
|
||||
// Handle "*" case
|
||||
if (std.mem.eql(u8, std.mem.trim(u8, if_none_match, " \t"), "*")) {
|
||||
return true; // Condition is false, so we should return 304
|
||||
}
|
||||
|
||||
// Parse comma-separated list of entity tags
|
||||
var iter = std.mem.splitScalar(u8, if_none_match, ',');
|
||||
while (iter.next()) |tag_str| {
|
||||
const parsed = parse(tag_str);
|
||||
if (weakMatch(our_parsed.tag, our_parsed.is_weak, parsed.tag, parsed.is_weak)) {
|
||||
return true; // Condition is false, so we should return 304
|
||||
}
|
||||
}
|
||||
|
||||
return false; // Condition is true, continue with normal processing
|
||||
}
|
||||
|
||||
const bun = @import("bun");
|
||||
const std = @import("std");
|
||||
@@ -52,6 +52,38 @@ pub fn detectAndLoadOtherLockfile(
|
||||
return migrate_result;
|
||||
}
|
||||
|
||||
yarn: {
|
||||
var timer = std.time.Timer.start() catch unreachable;
|
||||
const lockfile = File.openat(dir, "yarn.lock", bun.O.RDONLY, 0).unwrap() catch break :yarn;
|
||||
defer lockfile.close();
|
||||
const data = lockfile.readToEnd(allocator).unwrap() catch break :yarn;
|
||||
const migrate_result = @import("./yarn.zig").migrateYarnLockfile(this, manager, allocator, log, data, dir) catch |err| {
|
||||
if (Environment.isDebug) {
|
||||
bun.handleErrorReturnTrace(err, @errorReturnTrace());
|
||||
|
||||
Output.prettyErrorln("Error: {s}", .{@errorName(err)});
|
||||
log.print(Output.errorWriter()) catch {};
|
||||
Output.prettyErrorln("Invalid yarn.lock\nIn a release build, this would ignore and do a fresh install.\nAborting", .{});
|
||||
Global.exit(1);
|
||||
}
|
||||
return LoadResult{ .err = .{
|
||||
.step = .migrating,
|
||||
.value = err,
|
||||
.lockfile_path = "yarn.lock",
|
||||
.format = .binary,
|
||||
} };
|
||||
};
|
||||
|
||||
if (migrate_result == .ok) {
|
||||
Output.printElapsed(@as(f64, @floatFromInt(timer.read())) / std.time.ns_per_ms);
|
||||
Output.prettyError(" ", .{});
|
||||
Output.prettyErrorln("<d>migrated lockfile from <r><green>yarn.lock<r>", .{});
|
||||
Output.flush();
|
||||
}
|
||||
|
||||
return migrate_result;
|
||||
}
|
||||
|
||||
return LoadResult{ .not_found = {} };
|
||||
}
|
||||
|
||||
|
||||
1718
src/install/yarn.zig
Normal file
1718
src/install/yarn.zig
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1201
test/cli/install/migration/yarn-lock-migration.test.ts
Normal file
1201
test/cli/install/migration/yarn-lock-migration.test.ts
Normal file
File diff suppressed because it is too large
Load Diff
2
test/cli/install/migration/yarn/.gitignore
vendored
Normal file
2
test/cli/install/migration/yarn/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# we do need it the files here
|
||||
!yarn.lock
|
||||
157
test/cli/install/migration/yarn/yarn-cli-repo/package.json
Normal file
157
test/cli/install/migration/yarn/yarn-cli-repo/package.json
Normal file
@@ -0,0 +1,157 @@
|
||||
{
|
||||
"name": "yarn",
|
||||
"installationMethod": "unknown",
|
||||
"version": "1.23.0-0",
|
||||
"license": "BSD-2-Clause",
|
||||
"preferGlobal": true,
|
||||
"description": "📦🐈 Fast, reliable, and secure dependency management.",
|
||||
"dependencies": {
|
||||
"@zkochan/cmd-shim": "^3.1.0",
|
||||
"babel-runtime": "^6.26.0",
|
||||
"bytes": "^3.0.0",
|
||||
"camelcase": "^4.0.0",
|
||||
"chalk": "^2.1.0",
|
||||
"cli-table3": "^0.4.0",
|
||||
"commander": "^2.9.0",
|
||||
"death": "^1.0.0",
|
||||
"debug": "^3.0.0",
|
||||
"deep-equal": "^1.0.1",
|
||||
"detect-indent": "^5.0.0",
|
||||
"dnscache": "^1.0.1",
|
||||
"glob": "^7.1.1",
|
||||
"gunzip-maybe": "^1.4.0",
|
||||
"hash-for-dep": "^1.2.3",
|
||||
"imports-loader": "^0.8.0",
|
||||
"ini": "^1.3.4",
|
||||
"inquirer": "^6.2.0",
|
||||
"invariant": "^2.2.0",
|
||||
"is-builtin-module": "^2.0.0",
|
||||
"is-ci": "^1.0.10",
|
||||
"is-webpack-bundle": "^1.0.0",
|
||||
"js-yaml": "^3.13.1",
|
||||
"leven": "^2.0.0",
|
||||
"loud-rejection": "^1.2.0",
|
||||
"micromatch": "^2.3.11",
|
||||
"mkdirp": "^0.5.1",
|
||||
"node-emoji": "^1.6.1",
|
||||
"normalize-url": "^2.0.0",
|
||||
"npm-logical-tree": "^1.2.1",
|
||||
"object-path": "^0.11.2",
|
||||
"proper-lockfile": "^2.0.0",
|
||||
"puka": "^1.0.0",
|
||||
"read": "^1.0.7",
|
||||
"request": "^2.87.0",
|
||||
"request-capture-har": "^1.2.2",
|
||||
"rimraf": "^2.5.0",
|
||||
"semver": "^5.1.0",
|
||||
"ssri": "^5.3.0",
|
||||
"strip-ansi": "^4.0.0",
|
||||
"strip-bom": "^3.0.0",
|
||||
"tar-fs": "^1.16.0",
|
||||
"tar-stream": "^1.6.1",
|
||||
"uuid": "^3.0.1",
|
||||
"v8-compile-cache": "^2.0.0",
|
||||
"validate-npm-package-license": "^3.0.4",
|
||||
"yn": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-core": "^6.26.0",
|
||||
"babel-eslint": "^7.2.3",
|
||||
"babel-loader": "^6.2.5",
|
||||
"babel-plugin-array-includes": "^2.0.3",
|
||||
"babel-plugin-inline-import": "^3.0.0",
|
||||
"babel-plugin-transform-builtin-extend": "^1.1.2",
|
||||
"babel-plugin-transform-inline-imports-commonjs": "^1.0.0",
|
||||
"babel-plugin-transform-runtime": "^6.4.3",
|
||||
"babel-preset-env": "^1.6.0",
|
||||
"babel-preset-flow": "^6.23.0",
|
||||
"babel-preset-stage-0": "^6.0.0",
|
||||
"babylon": "^6.5.0",
|
||||
"commitizen": "^2.9.6",
|
||||
"cz-conventional-changelog": "^2.0.0",
|
||||
"eslint": "^4.3.0",
|
||||
"eslint-config-fb-strict": "^22.0.0",
|
||||
"eslint-plugin-babel": "^5.0.0",
|
||||
"eslint-plugin-flowtype": "^2.35.0",
|
||||
"eslint-plugin-jasmine": "^2.6.2",
|
||||
"eslint-plugin-jest": "^21.0.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.0.2",
|
||||
"eslint-plugin-prefer-object-spread": "^1.2.1",
|
||||
"eslint-plugin-prettier": "^2.1.2",
|
||||
"eslint-plugin-react": "^7.1.0",
|
||||
"eslint-plugin-relay": "^0.0.28",
|
||||
"eslint-plugin-yarn-internal": "file:scripts/eslint-rules",
|
||||
"execa": "^0.11.0",
|
||||
"fancy-log": "^1.3.2",
|
||||
"flow-bin": "^0.66.0",
|
||||
"git-release-notes": "^3.0.0",
|
||||
"gulp": "^4.0.0",
|
||||
"gulp-babel": "^7.0.0",
|
||||
"gulp-if": "^2.0.1",
|
||||
"gulp-newer": "^1.0.0",
|
||||
"gulp-plumber": "^1.0.1",
|
||||
"gulp-sourcemaps": "^2.2.0",
|
||||
"jest": "^22.4.4",
|
||||
"jsinspect": "^0.12.6",
|
||||
"minimatch": "^3.0.4",
|
||||
"mock-stdin": "^0.3.0",
|
||||
"prettier": "^1.5.2",
|
||||
"string-replace-loader": "^2.1.1",
|
||||
"temp": "^0.8.3",
|
||||
"webpack": "^2.1.0-beta.25",
|
||||
"yargs": "^6.3.0"
|
||||
},
|
||||
"resolutions": {
|
||||
"sshpk": "^1.14.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
},
|
||||
"repository": "yarnpkg/yarn",
|
||||
"bin": {
|
||||
"yarn": "./bin/yarn.js",
|
||||
"yarnpkg": "./bin/yarn.js"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "gulp build",
|
||||
"build-bundle": "node ./scripts/build-webpack.js",
|
||||
"build-chocolatey": "powershell ./scripts/build-chocolatey.ps1",
|
||||
"build-deb": "./scripts/build-deb.sh",
|
||||
"build-dist": "bash ./scripts/build-dist.sh",
|
||||
"build-win-installer": "scripts\\build-windows-installer.bat",
|
||||
"changelog": "git-release-notes $(git describe --tags --abbrev=0 $(git describe --tags --abbrev=0)^)..$(git describe --tags --abbrev=0) scripts/changelog.md",
|
||||
"dupe-check": "yarn jsinspect ./src",
|
||||
"lint": "eslint . && flow check",
|
||||
"pkg-tests": "yarn --cwd packages/pkg-tests jest yarn.test.js",
|
||||
"prettier": "eslint src __tests__ --fix",
|
||||
"release-branch": "./scripts/release-branch.sh",
|
||||
"test": "yarn lint && yarn test-only",
|
||||
"test-only": "node --max_old_space_size=4096 node_modules/jest/bin/jest.js --verbose",
|
||||
"test-only-debug": "node --inspect-brk --max_old_space_size=4096 node_modules/jest/bin/jest.js --runInBand --verbose",
|
||||
"test-coverage": "node --max_old_space_size=4096 node_modules/jest/bin/jest.js --coverage --verbose",
|
||||
"watch": "gulp watch",
|
||||
"commit": "git-cz"
|
||||
},
|
||||
"jest": {
|
||||
"collectCoverageFrom": [
|
||||
"src/**/*.js"
|
||||
],
|
||||
"testEnvironment": "node",
|
||||
"modulePathIgnorePatterns": [
|
||||
"__tests__/fixtures/",
|
||||
"packages/pkg-tests/pkg-tests-fixtures",
|
||||
"dist/"
|
||||
],
|
||||
"testPathIgnorePatterns": [
|
||||
"__tests__/(fixtures|__mocks__)/",
|
||||
"updates/",
|
||||
"_(temp|mock|install|init|helpers).js$",
|
||||
"packages/pkg-tests"
|
||||
]
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "./node_modules/cz-conventional-changelog"
|
||||
}
|
||||
}
|
||||
}
|
||||
7925
test/cli/install/migration/yarn/yarn-cli-repo/yarn.lock
Normal file
7925
test/cli/install/migration/yarn/yarn-cli-repo/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
1
test/cli/install/migration/yarn/yarn-lock-mkdirp-file-dep/.gitignore
vendored
Normal file
1
test/cli/install/migration/yarn/yarn-lock-mkdirp-file-dep/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
bun.*
|
||||
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"name": "mkdirp",
|
||||
"description": "Recursively mkdir, like `mkdir -p`",
|
||||
"version": "1.0.2",
|
||||
"main": "index.js",
|
||||
"keywords": [
|
||||
"mkdir",
|
||||
"directory",
|
||||
"make dir",
|
||||
"make",
|
||||
"dir",
|
||||
"recursive",
|
||||
"native"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/isaacs/node-mkdirp.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "tap",
|
||||
"snap": "tap",
|
||||
"preversion": "npm test",
|
||||
"postversion": "npm publish",
|
||||
"postpublish": "git push origin --follow-tags"
|
||||
},
|
||||
"tap": {
|
||||
"check-coverage": true,
|
||||
"coverage-map": "map.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"require-inject": "^1.4.4",
|
||||
"tap": "^14.10.6"
|
||||
},
|
||||
"bin": "bin/cmd.js",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"files": [
|
||||
"bin",
|
||||
"lib",
|
||||
"index.js"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"mkdirp": "file:mkdirp"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"mkdirp@file:mkdirp":
|
||||
version "1.0.2"
|
||||
1
test/cli/install/migration/yarn/yarn-lock-mkdirp-no-resolved/.gitignore
vendored
Normal file
1
test/cli/install/migration/yarn/yarn-lock-mkdirp-no-resolved/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
bun.*
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"mkdirp": "^1.0.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
mkdirp@^1.0.2:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
|
||||
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
|
||||
1
test/cli/install/migration/yarn/yarn-lock-mkdirp/.gitignore
vendored
Normal file
1
test/cli/install/migration/yarn/yarn-lock-mkdirp/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
bun.*
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"mkdirp": "^1.0.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
mkdirp@^1.0.2:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
|
||||
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
|
||||
1
test/cli/install/migration/yarn/yarn-stuff/.gitignore
vendored
Normal file
1
test/cli/install/migration/yarn/yarn-stuff/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
bun.*
|
||||
BIN
test/cli/install/migration/yarn/yarn-stuff/abbrev-1.1.1.tgz
Normal file
BIN
test/cli/install/migration/yarn/yarn-stuff/abbrev-1.1.1.tgz
Normal file
Binary file not shown.
@@ -0,0 +1 @@
|
||||
{"name":"abbrev","version":"1.1.1"}
|
||||
@@ -0,0 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
15
test/cli/install/migration/yarn/yarn-stuff/package.json
Normal file
15
test/cli/install/migration/yarn/yarn-stuff/package.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "a",
|
||||
"version": "1.2.3",
|
||||
"dependencies": {
|
||||
"abbrev": "^1.1.1",
|
||||
"full-git-url": "git+https://github.com/isaacs/abbrev-js.git",
|
||||
"ghshort": "github:isaacs/abbrev-js",
|
||||
"old": "npm:abbrev@1.0.x",
|
||||
"pinned": "npm:abbrev@1.1.1",
|
||||
"reg": "npm:abbrev@^1.1.1",
|
||||
"remote": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
||||
"symlink": "file:./abbrev-link-target",
|
||||
"tarball": "file:abbrev-1.1.1.tgz"
|
||||
}
|
||||
}
|
||||
42
test/cli/install/migration/yarn/yarn-stuff/yarn.lock
Normal file
42
test/cli/install/migration/yarn/yarn-stuff/yarn.lock
Normal file
@@ -0,0 +1,42 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
abbrev@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
|
||||
integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
|
||||
|
||||
"full-git-url@git+https://github.com/isaacs/abbrev-js.git":
|
||||
version "3.0.1"
|
||||
resolved "git+https://github.com/isaacs/abbrev-js.git#3f9802e56ff878761a338e43ecacbfed39d2181d"
|
||||
|
||||
"ghshort@github:isaacs/abbrev-js":
|
||||
version "3.0.1"
|
||||
resolved "https://codeload.github.com/isaacs/abbrev-js/tar.gz/3f9802e56ff878761a338e43ecacbfed39d2181d"
|
||||
|
||||
"old@npm:abbrev@1.0.x":
|
||||
version "1.0.9"
|
||||
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135"
|
||||
integrity sha512-LEyx4aLEC3x6T0UguF6YILf+ntvmOaWsVfENmIW0E9H09vKlLDGelMjjSm0jkDHALj8A8quZ/HapKNigzwge+Q==
|
||||
|
||||
"pinned@npm:abbrev@1.1.1":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
|
||||
integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
|
||||
|
||||
"reg@npm:abbrev@^1.1.1":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
|
||||
integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
|
||||
|
||||
"remote@https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
|
||||
|
||||
"symlink@file:./abbrev-link-target":
|
||||
version "1.1.1"
|
||||
|
||||
"tarball@file:abbrev-1.1.1.tgz":
|
||||
version "1.1.1"
|
||||
resolved "file:abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
|
||||
Reference in New Issue
Block a user