aboutsummaryrefslogtreecommitdiff
path: root/src/analytics/analytics_thread.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/analytics/analytics_thread.zig')
-rw-r--r--src/analytics/analytics_thread.zig157
1 files changed, 157 insertions, 0 deletions
diff --git a/src/analytics/analytics_thread.zig b/src/analytics/analytics_thread.zig
index 5484c3c0f..434bc0c7d 100644
--- a/src/analytics/analytics_thread.zig
+++ b/src/analytics/analytics_thread.zig
@@ -9,6 +9,124 @@ const Analytics = @import("./analytics_schema.zig").analytics;
const Writer = @import("./analytics_schema.zig").Writer;
const Headers = @import("../javascript/jsc/webcore/response.zig").Headers;
+fn NewUint64(val: u64) Analytics.Uint64 {
+ const bytes = std.mem.asBytes(&val);
+ return .{
+ .first = std.mem.readIntNative(u32, bytes[0..4]),
+ .second = std.mem.readIntNative(u32, bytes[4..]),
+ };
+}
+
+// This answers, "What parts of Bun are people actually using?"
+pub const Features = struct {
+ pub var single_page_app_routing = false;
+ pub var tsconfig_paths = false;
+ pub var fast_refresh = false;
+ pub var hot_module_reloading = false;
+ pub var jsx = false;
+ pub var always_bundle = false;
+ pub var tsconfig = false;
+ pub var bun_bun = false;
+ pub var filesystem_router = false;
+ pub var framework = false;
+ pub var bunjs = false;
+ pub var macros = false;
+ pub var public_folder = false;
+ pub var dotenv = false;
+ pub var define = false;
+ pub var loaders = false;
+ pub var origin = false;
+ pub var external = false;
+ pub var fetch = false;
+
+ const Bitset = std.bit_set.IntegerBitSet(32);
+
+ pub const Serializer = struct {
+ inline fn shiftIndex(index: u32) !u32 {
+ return @intCast(u32, @as(Bitset.MaskInt, 1) << @intCast(Bitset.ShiftInt, index));
+ }
+
+ fn writeField(comptime WriterType: type, writer: WriterType, field_name: string, index: u32) !void {
+ var output: [64]u8 = undefined;
+ const name = std.ascii.upperString(&output, field_name);
+
+ try writer.print("const Features_{s} = {d}\n", .{ name, shiftIndex(index) });
+ }
+
+ pub fn writeAll(comptime WriterType: type, writer: WriterType) !void {
+ try writer.writeAll("package analytics\n\n");
+ try writeField(WriterType, writer, "single_page_app_routing", 1);
+ try writeField(WriterType, writer, "tsconfig_paths", 2);
+ try writeField(WriterType, writer, "fast_refresh", 3);
+ try writeField(WriterType, writer, "hot_module_reloading", 4);
+ try writeField(WriterType, writer, "jsx", 5);
+ try writeField(WriterType, writer, "always_bundle", 6);
+ try writeField(WriterType, writer, "tsconfig", 7);
+ try writeField(WriterType, writer, "bun_bun", 8);
+ try writeField(WriterType, writer, "filesystem_router", 9);
+ try writeField(WriterType, writer, "framework", 10);
+ try writeField(WriterType, writer, "bunjs", 11);
+ try writeField(WriterType, writer, "macros", 12);
+ try writeField(WriterType, writer, "public_folder", 13);
+ try writeField(WriterType, writer, "dotenv", 14);
+ try writeField(WriterType, writer, "define", 15);
+ try writeField(WriterType, writer, "loaders", 16);
+ try writeField(WriterType, writer, "origin", 17);
+ try writeField(WriterType, writer, "external", 18);
+ try writeField(WriterType, writer, "fetch", 19);
+ try writer.writeAll("\n");
+ }
+ };
+
+ pub fn toInt() u32 {
+ var list = Bitset.initEmpty();
+ list.setValue(1, Features.single_page_app_routing);
+ list.setValue(2, Features.tsconfig_paths);
+ list.setValue(3, Features.fast_refresh);
+ list.setValue(4, Features.hot_module_reloading);
+ list.setValue(5, Features.jsx);
+ list.setValue(6, Features.always_bundle);
+ list.setValue(7, Features.tsconfig);
+ list.setValue(8, Features.bun_bun);
+ list.setValue(9, Features.filesystem_router);
+ list.setValue(10, Features.framework);
+ list.setValue(11, Features.bunjs);
+ list.setValue(12, Features.macros);
+ list.setValue(13, Features.public_folder);
+ list.setValue(14, Features.dotenv);
+ list.setValue(15, Features.define);
+ list.setValue(16, Features.loaders);
+ list.setValue(17, Features.origin);
+ list.setValue(18, Features.external);
+ list.setValue(19, Features.fetch);
+
+ if (comptime FeatureFlags.verbose_analytics) {
+ if (Features.single_page_app_routing) Output.pretty("<r><d>single_page_app_routing<r>,", .{});
+ if (Features.tsconfig_paths) Output.pretty("<r><d>tsconfig_paths<r>,", .{});
+ if (Features.fast_refresh) Output.pretty("<r><d>fast_refresh<r>,", .{});
+ if (Features.hot_module_reloading) Output.pretty("<r><d>hot_module_reloading<r>,", .{});
+ if (Features.jsx) Output.pretty("<r><d>jsx<r>,", .{});
+ if (Features.always_bundle) Output.pretty("<r><d>always_bundle<r>,", .{});
+ if (Features.tsconfig) Output.pretty("<r><d>tsconfig<r>,", .{});
+ if (Features.bun_bun) Output.pretty("<r><d>bun_bun<r>,", .{});
+ if (Features.filesystem_router) Output.pretty("<r><d>filesystem_router<r>,", .{});
+ if (Features.framework) Output.pretty("<r><d>framework<r>,", .{});
+ if (Features.bunjs) Output.pretty("<r><d>bunjs<r>,", .{});
+ if (Features.macros) Output.pretty("<r><d>macros<r>,", .{});
+ if (Features.public_folder) Output.pretty("<r><d>public_folder<r>,", .{});
+ if (Features.dotenv) Output.pretty("<r><d>dotenv<r>,", .{});
+ if (Features.define) Output.pretty("<r><d>define<r>,", .{});
+ if (Features.loaders) Output.pretty("<r><d>loaders<r>,", .{});
+ if (Features.origin) Output.pretty("<r><d>origin<r>,", .{});
+ if (Features.external) Output.pretty("<r><d>external<r>,", .{});
+ if (Features.fetch) Output.pretty("<r><d>fetch<r>,", .{});
+ Output.prettyln("\n", .{});
+ }
+
+ return @as(u32, list.mask);
+ }
+};
+
pub const EventName = enum(u8) {
bundle_success,
bundle_fail,
@@ -21,6 +139,7 @@ var random: std.rand.DefaultPrng = undefined;
const DotEnv = @import("../env_loader.zig");
const platform_arch = if (Environment.isAarch64) Analytics.Architecture.arm else Analytics.Architecture.x64;
+var project_id: Analytics.Uint64 = .{};
pub const Event = struct {
timestamp: u64,
@@ -58,12 +177,19 @@ var event_queue: EventQueue = undefined;
pub const GenerateHeader = struct {
pub fn generate() Analytics.EventListHeader {
+ if (comptime isDebug) {
+ if (project_id.first == 0 and project_id.second == 0) {
+ Output.prettyErrorln("warn: project_id is 0", .{});
+ }
+ }
+
if (Environment.isMac) {
return Analytics.EventListHeader{
.machine_id = GenerateMachineID.forMac() catch Analytics.Uint64{},
.platform = GeneratePlatform.forMac(),
.build_id = comptime @truncate(u32, Global.build_id),
.session_id = random.random.int(u32),
+ .project_id = project_id,
};
}
@@ -73,6 +199,7 @@ pub const GenerateHeader = struct {
.platform = GeneratePlatform.forLinux(),
.build_id = comptime @truncate(u32, Global.build_id),
.session_id = random.random.int(u32),
+ .project_id = project_id,
};
}
@@ -292,6 +419,7 @@ pub const EventList = struct {
const now = std.time.nanoTimestamp();
this.header.session_length = @truncate(u32, @intCast(u64, (now - start_time)) / std.time.ns_per_ms);
+ this.header.feature_usage = Features.toInt();
var list = Analytics.EventList{
.header = this.header,
@@ -372,3 +500,32 @@ pub const EventList = struct {
};
pub var is_ci = false;
+
+pub var username_only_for_determining_project_id_and_never_sent: string = "";
+pub fn setProjectID(folder_name_: string, package_name: string) void {
+ if (disabled) return;
+
+ var hasher = std.hash.Wyhash.init(10);
+
+ var folder_name = folder_name_;
+
+ // The idea here is
+ // When you're working at a mid-large company
+ // Basically everyone has standardized laptops
+ // The hardware may differ, but the folder structure is typically identical
+ // But the username or home folder may differ
+ // So when we hash, we skip that if it exists
+ if (username_only_for_determining_project_id_and_never_sent.len > 0) {
+ if (std.mem.indexOf(u8, folder_name, username_only_for_determining_project_id_and_never_sent)) |i| {
+ const offset = i + username_only_for_determining_project_id_and_never_sent.len + 1;
+ if (folder_name.len > offset) {
+ folder_name = folder_name[offset..];
+ }
+ }
+ }
+ hasher.update(folder_name);
+ hasher.update("@");
+ if (package_name.len > 0) hasher.update(package_name);
+ if (package_name.len == 0) hasher.update("\"\"");
+ project_id = NewUint64(hasher.final());
+}