sparkbun 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/bin/sparkbun.cjs +18 -0
  2. package/dist-linux-arm64/bsdiff +0 -0
  3. package/dist-linux-arm64/bspatch +0 -0
  4. package/dist-linux-arm64/libElectrobunCore.so +0 -0
  5. package/dist-linux-arm64/libNativeWrapper.so +0 -0
  6. package/dist-linux-arm64/libasar.so +0 -0
  7. package/dist-linux-x64/bsdiff +0 -0
  8. package/dist-linux-x64/bspatch +0 -0
  9. package/dist-linux-x64/libElectrobunCore.so +0 -0
  10. package/dist-linux-x64/libNativeWrapper.so +0 -0
  11. package/dist-linux-x64/libasar.so +0 -0
  12. package/dist-macos-arm64/bsdiff +0 -0
  13. package/dist-macos-arm64/bspatch +0 -0
  14. package/dist-macos-arm64/libElectrobunCore.dylib +0 -0
  15. package/dist-macos-arm64/libNativeWrapper.dylib +0 -0
  16. package/dist-macos-arm64/libasar.dylib +0 -0
  17. package/dist-macos-arm64/libwebgpu_dawn.dylib +0 -0
  18. package/dist-macos-arm64/preload-full.js +885 -0
  19. package/dist-macos-arm64/preload-sandboxed.js +111 -0
  20. package/dist-macos-arm64/process_helper +0 -0
  21. package/dist-win-x64/ElectrobunCore.dll +0 -0
  22. package/dist-win-x64/WebView2Loader.dll +0 -0
  23. package/dist-win-x64/bsdiff.exe +0 -0
  24. package/dist-win-x64/bspatch.exe +0 -0
  25. package/dist-win-x64/libNativeWrapper.dll +0 -0
  26. package/dist-win-x64/zig-asar/arm64/libasar.dll +0 -0
  27. package/dist-win-x64/zig-asar/x64/libasar.dll +0 -0
  28. package/package.json +47 -0
  29. package/scripts/build-and-upload-artifacts.js +207 -0
  30. package/scripts/gen-webgpu-ffi.mjs +162 -0
  31. package/scripts/install-windows-deps.ps1 +80 -0
  32. package/scripts/package-release.js +237 -0
  33. package/scripts/push-version.js +84 -0
  34. package/scripts/update-bun-version.ts +122 -0
  35. package/scripts/update-cef-version.ts +145 -0
  36. package/src/browser/builtinrpcSchema.ts +19 -0
  37. package/src/browser/global.d.ts +36 -0
  38. package/src/browser/index.ts +234 -0
  39. package/src/browser/webviewtag.ts +88 -0
  40. package/src/browser/wgputag.ts +48 -0
  41. package/src/bun/SparkBunConfig.ts +497 -0
  42. package/src/bun/__tests__/ffi-contract.test.ts +105 -0
  43. package/src/bun/core/ApplicationMenu.ts +70 -0
  44. package/src/bun/core/BrowserView.ts +416 -0
  45. package/src/bun/core/BrowserWindow.ts +396 -0
  46. package/src/bun/core/BuildConfig.ts +71 -0
  47. package/src/bun/core/ContextMenu.ts +75 -0
  48. package/src/bun/core/GpuWindow.ts +289 -0
  49. package/src/bun/core/Paths.ts +5 -0
  50. package/src/bun/core/Socket.ts +22 -0
  51. package/src/bun/core/Tray.ts +197 -0
  52. package/src/bun/core/Updater.ts +1131 -0
  53. package/src/bun/core/Utils.ts +487 -0
  54. package/src/bun/core/WGPUView.ts +167 -0
  55. package/src/bun/core/menuRoles.ts +181 -0
  56. package/src/bun/events/ApplicationEvents.ts +22 -0
  57. package/src/bun/events/event.ts +27 -0
  58. package/src/bun/events/eventEmitter.ts +45 -0
  59. package/src/bun/events/trayEvents.ts +11 -0
  60. package/src/bun/events/webviewEvents.ts +39 -0
  61. package/src/bun/events/windowEvents.ts +23 -0
  62. package/src/bun/index.ts +120 -0
  63. package/src/bun/preload/.generated/compiled.ts +2 -0
  64. package/src/bun/preload/build.ts +65 -0
  65. package/src/bun/preload/dragRegions.ts +41 -0
  66. package/src/bun/preload/encryption.ts +86 -0
  67. package/src/bun/preload/events.ts +171 -0
  68. package/src/bun/preload/globals.d.ts +45 -0
  69. package/src/bun/preload/index-sandboxed.ts +28 -0
  70. package/src/bun/preload/index.ts +77 -0
  71. package/src/bun/preload/internalRpc.ts +80 -0
  72. package/src/bun/preload/overlaySync.ts +107 -0
  73. package/src/bun/preload/webviewTag.ts +451 -0
  74. package/src/bun/preload/wgpuTag.ts +246 -0
  75. package/src/bun/proc/linux.md +43 -0
  76. package/src/bun/proc/native.ts +3253 -0
  77. package/src/bun/webGPU.ts +346 -0
  78. package/src/bun/webgpuAdapter.ts +3011 -0
  79. package/src/cli/bun.lockb +0 -0
  80. package/src/cli/index.ts +4653 -0
  81. package/src/cli/package-lock.json +81 -0
  82. package/src/cli/package.json +11 -0
  83. package/src/cli/templates/embedded.ts +2 -0
  84. package/src/core/build.zig +16 -0
  85. package/src/core/main.zig +3378 -0
  86. package/src/extractor/build.zig +22 -0
  87. package/src/installer/installer-template.ts +216 -0
  88. package/src/launcher/main.ts +221 -0
  89. package/src/native/build/libNativeWrapper.so +0 -0
  90. package/src/native/linux/build/nativeWrapper.o +0 -0
  91. package/src/native/linux/cef_loader.cpp +110 -0
  92. package/src/native/linux/cef_loader.h +28 -0
  93. package/src/native/linux/cef_process_helper_linux.cpp +160 -0
  94. package/src/native/linux/nativeWrapper.cpp +11768 -0
  95. package/src/native/macos/cef_process_helper_mac.cc +160 -0
  96. package/src/native/macos/nativeWrapper.mm +9172 -0
  97. package/src/native/shared/accelerator_parser.h +72 -0
  98. package/src/native/shared/app_paths.h +110 -0
  99. package/src/native/shared/asar.h +35 -0
  100. package/src/native/shared/cache_migration.h +244 -0
  101. package/src/native/shared/callbacks.h +57 -0
  102. package/src/native/shared/cef_response_filter.h +189 -0
  103. package/src/native/shared/chromium_flags.h +181 -0
  104. package/src/native/shared/config.h +66 -0
  105. package/src/native/shared/download_event.h +197 -0
  106. package/src/native/shared/ffi_helpers.h +139 -0
  107. package/src/native/shared/glob_match.h +59 -0
  108. package/src/native/shared/json_menu_parser.h +223 -0
  109. package/src/native/shared/mime_types.h +101 -0
  110. package/src/native/shared/navigation_rules.h +98 -0
  111. package/src/native/shared/partition_context.h +137 -0
  112. package/src/native/shared/pending_resize_queue.h +45 -0
  113. package/src/native/shared/permissions.h +118 -0
  114. package/src/native/shared/permissions_cef.h +74 -0
  115. package/src/native/shared/preload_script.h +71 -0
  116. package/src/native/shared/shutdown_guard.h +134 -0
  117. package/src/native/shared/thread_safe_map.h +138 -0
  118. package/src/native/shared/webview_storage.h +91 -0
  119. package/src/native/win/cef_process_helper_win.cpp +143 -0
  120. package/src/native/win/dcomp_compositor.h +352 -0
  121. package/src/native/win/nativeWrapper.cpp +12434 -0
  122. package/src/npmbin/index.js +34 -0
  123. package/src/shared/bun-version.ts +3 -0
  124. package/src/shared/cef-version.ts +5 -0
  125. package/src/shared/naming.test.ts +327 -0
  126. package/src/shared/naming.ts +188 -0
  127. package/src/shared/platform.ts +48 -0
  128. package/src/shared/rpc.ts +541 -0
  129. package/src/shared/sparkbun-version.ts +2 -0
  130. package/src/types/three.d.ts +1 -0
  131. package/tsconfig.json +31 -0
@@ -0,0 +1,139 @@
1
+ // ffi_helpers.h - Cross-platform FFI helper utilities
2
+ // Helpers for passing strings and data across FFI boundaries
3
+ // Used across Windows, macOS, and Linux
4
+ //
5
+ // This is a header-only implementation to avoid build complexity.
6
+
7
+ #ifndef ELECTROBUN_FFI_HELPERS_H
8
+ #define ELECTROBUN_FFI_HELPERS_H
9
+
10
+ #include <string>
11
+ #include <cstring>
12
+ #include <cstdlib>
13
+
14
+ namespace electrobun {
15
+
16
+ // Create a copy of a string for FFI callbacks
17
+ // The returned string is allocated with malloc/strdup and must be freed by the caller
18
+ // Returns nullptr if the input is empty
19
+ inline char* createFFIString(const std::string& str) {
20
+ if (str.empty()) {
21
+ return nullptr;
22
+ }
23
+ return strdup(str.c_str());
24
+ }
25
+
26
+ // Create a copy of a C string for FFI callbacks
27
+ // The returned string is allocated with malloc/strdup and must be freed by the caller
28
+ // Returns nullptr if the input is null or empty
29
+ inline char* createFFIString(const char* str) {
30
+ if (!str || str[0] == '\0') {
31
+ return nullptr;
32
+ }
33
+ return strdup(str);
34
+ }
35
+
36
+ // Free an FFI string that was created with createFFIString
37
+ inline void freeFFIString(char* str) {
38
+ if (str) {
39
+ free(str);
40
+ }
41
+ }
42
+
43
+ // Free an FFI string (const version for convenience)
44
+ inline void freeFFIString(const char* str) {
45
+ if (str) {
46
+ free(const_cast<char*>(str));
47
+ }
48
+ }
49
+
50
+ // RAII wrapper for FFI strings
51
+ // Automatically frees the string when it goes out of scope
52
+ class FFIString {
53
+ public:
54
+ FFIString() : str_(nullptr) {}
55
+
56
+ explicit FFIString(const std::string& str)
57
+ : str_(createFFIString(str)) {}
58
+
59
+ explicit FFIString(const char* str)
60
+ : str_(createFFIString(str)) {}
61
+
62
+ ~FFIString() {
63
+ freeFFIString(str_);
64
+ }
65
+
66
+ // Get the raw pointer (for passing to FFI)
67
+ const char* get() const { return str_; }
68
+
69
+ // Release ownership (caller takes responsibility for freeing)
70
+ char* release() {
71
+ char* temp = str_;
72
+ str_ = nullptr;
73
+ return temp;
74
+ }
75
+
76
+ // Check if string is valid
77
+ bool valid() const { return str_ != nullptr; }
78
+ explicit operator bool() const { return valid(); }
79
+
80
+ // Non-copyable
81
+ FFIString(const FFIString&) = delete;
82
+ FFIString& operator=(const FFIString&) = delete;
83
+
84
+ // Movable
85
+ FFIString(FFIString&& other) noexcept : str_(other.str_) {
86
+ other.str_ = nullptr;
87
+ }
88
+
89
+ FFIString& operator=(FFIString&& other) noexcept {
90
+ if (this != &other) {
91
+ freeFFIString(str_);
92
+ str_ = other.str_;
93
+ other.str_ = nullptr;
94
+ }
95
+ return *this;
96
+ }
97
+
98
+ private:
99
+ char* str_;
100
+ };
101
+
102
+ // Helper to create event data strings for callbacks
103
+ // Format: "key1=value1&key2=value2&..."
104
+ class FFIEventBuilder {
105
+ public:
106
+ FFIEventBuilder& add(const std::string& key, const std::string& value) {
107
+ if (!data_.empty()) {
108
+ data_ += "&";
109
+ }
110
+ data_ += key + "=" + value;
111
+ return *this;
112
+ }
113
+
114
+ FFIEventBuilder& add(const std::string& key, int value) {
115
+ return add(key, std::to_string(value));
116
+ }
117
+
118
+ FFIEventBuilder& add(const std::string& key, double value) {
119
+ return add(key, std::to_string(value));
120
+ }
121
+
122
+ FFIEventBuilder& add(const std::string& key, bool value) {
123
+ return add(key, value ? "true" : "false");
124
+ }
125
+
126
+ std::string str() const { return data_; }
127
+
128
+ // Create FFI string (caller must free)
129
+ char* createFFIString() const {
130
+ return electrobun::createFFIString(data_);
131
+ }
132
+
133
+ private:
134
+ std::string data_;
135
+ };
136
+
137
+ } // namespace electrobun
138
+
139
+ #endif // ELECTROBUN_FFI_HELPERS_H
@@ -0,0 +1,59 @@
1
+ // glob_match.h - Cross-platform glob pattern matching utility
2
+ // Used for navigation rules matching across Windows, macOS, and Linux
3
+ //
4
+ // This is a header-only implementation to avoid build complexity.
5
+ // Supports * wildcard only, case-insensitive matching.
6
+
7
+ #ifndef ELECTROBUN_GLOB_MATCH_H
8
+ #define ELECTROBUN_GLOB_MATCH_H
9
+
10
+ #include <string>
11
+ #include <cctype>
12
+
13
+ namespace electrobun {
14
+
15
+ // Simple case-insensitive glob matcher (supports * wildcard only)
16
+ // Returns true if text matches the glob pattern
17
+ //
18
+ // Examples:
19
+ // globMatch("*.example.com", "www.example.com") -> true
20
+ // globMatch("https://*.wikipedia.org/*", "https://en.wikipedia.org/wiki/Test") -> true
21
+ // globMatch("exact.match.com", "exact.match.com") -> true
22
+ // globMatch("*.google.com", "www.bing.com") -> false
23
+ //
24
+ inline bool globMatch(const std::string& pattern, const std::string& text) {
25
+ size_t p = 0, t = 0;
26
+ size_t starP = std::string::npos, starT = 0;
27
+
28
+ while (t < text.size()) {
29
+ if (p < pattern.size() && (std::tolower(static_cast<unsigned char>(pattern[p])) ==
30
+ std::tolower(static_cast<unsigned char>(text[t])))) {
31
+ // Characters match (case-insensitive)
32
+ p++;
33
+ t++;
34
+ } else if (p < pattern.size() && pattern[p] == '*') {
35
+ // Wildcard: remember position and try matching zero characters
36
+ starP = p++;
37
+ starT = t;
38
+ } else if (starP != std::string::npos) {
39
+ // Mismatch but we have a previous wildcard: backtrack
40
+ p = starP + 1;
41
+ t = ++starT;
42
+ } else {
43
+ // Mismatch and no wildcard to backtrack to
44
+ return false;
45
+ }
46
+ }
47
+
48
+ // Skip any trailing wildcards in pattern
49
+ while (p < pattern.size() && pattern[p] == '*') {
50
+ p++;
51
+ }
52
+
53
+ // Match if we consumed the entire pattern
54
+ return p == pattern.size();
55
+ }
56
+
57
+ } // namespace electrobun
58
+
59
+ #endif // ELECTROBUN_GLOB_MATCH_H
@@ -0,0 +1,223 @@
1
+ // json_menu_parser.h - Cross-platform simple JSON menu parser
2
+ // Lightweight JSON parsing for menu configurations
3
+ // Used across Windows, macOS, and Linux
4
+ //
5
+ // This is a header-only implementation to avoid build complexity.
6
+ // For complex JSON needs, use a proper JSON library instead.
7
+
8
+ #ifndef ELECTROBUN_JSON_MENU_PARSER_H
9
+ #define ELECTROBUN_JSON_MENU_PARSER_H
10
+
11
+ #include <string>
12
+ #include <vector>
13
+
14
+ namespace electrobun {
15
+
16
+ // Represents a parsed menu item from JSON
17
+ struct MenuItemJson {
18
+ std::string type; // "normal", "separator", "checkbox", "submenu"
19
+ std::string label;
20
+ std::string action; // Action identifier for callbacks
21
+ std::string role; // System role (e.g., "quit", "copy", "paste")
22
+ std::string tooltip;
23
+ std::string accelerator; // Keyboard shortcut
24
+ bool enabled = true;
25
+ bool checked = false;
26
+ bool hidden = false;
27
+ std::vector<MenuItemJson> submenu;
28
+ };
29
+
30
+ // Simple JSON string value extractor
31
+ // Finds "key": "value" patterns and extracts the value
32
+ inline std::string extractJsonStringValue(const std::string& json,
33
+ const std::string& key,
34
+ size_t startPos,
35
+ size_t endPos) {
36
+ std::string searchKey = "\"" + key + "\":";
37
+ size_t keyPos = json.find(searchKey, startPos);
38
+
39
+ if (keyPos == std::string::npos || keyPos >= endPos) {
40
+ return "";
41
+ }
42
+
43
+ // Skip whitespace after colon
44
+ size_t valueStart = keyPos + searchKey.length();
45
+ while (valueStart < endPos && (json[valueStart] == ' ' || json[valueStart] == '\t')) {
46
+ valueStart++;
47
+ }
48
+
49
+ if (valueStart >= endPos) return "";
50
+
51
+ // Check if it's a string value (starts with quote)
52
+ if (json[valueStart] == '"') {
53
+ valueStart++;
54
+ size_t valueEnd = json.find('"', valueStart);
55
+ if (valueEnd != std::string::npos && valueEnd < endPos) {
56
+ return json.substr(valueStart, valueEnd - valueStart);
57
+ }
58
+ }
59
+
60
+ return "";
61
+ }
62
+
63
+ // Simple JSON boolean value extractor
64
+ inline bool extractJsonBoolValue(const std::string& json,
65
+ const std::string& key,
66
+ size_t startPos,
67
+ size_t endPos,
68
+ bool defaultValue = false) {
69
+ std::string searchKey = "\"" + key + "\":";
70
+ size_t keyPos = json.find(searchKey, startPos);
71
+
72
+ if (keyPos == std::string::npos || keyPos >= endPos) {
73
+ return defaultValue;
74
+ }
75
+
76
+ size_t valueStart = keyPos + searchKey.length();
77
+ while (valueStart < endPos && (json[valueStart] == ' ' || json[valueStart] == '\t')) {
78
+ valueStart++;
79
+ }
80
+
81
+ if (valueStart >= endPos) return defaultValue;
82
+
83
+ if (json.substr(valueStart, 4) == "true") {
84
+ return true;
85
+ } else if (json.substr(valueStart, 5) == "false") {
86
+ return false;
87
+ }
88
+
89
+ return defaultValue;
90
+ }
91
+
92
+ // Find the end of a JSON object starting at the given position
93
+ // Returns the position of the closing brace
94
+ inline size_t findObjectEnd(const std::string& json, size_t startPos) {
95
+ if (startPos >= json.length() || json[startPos] != '{') {
96
+ return std::string::npos;
97
+ }
98
+
99
+ int braceCount = 1;
100
+ size_t pos = startPos + 1;
101
+
102
+ while (pos < json.length() && braceCount > 0) {
103
+ if (json[pos] == '{') braceCount++;
104
+ else if (json[pos] == '}') braceCount--;
105
+ pos++;
106
+ }
107
+
108
+ return braceCount == 0 ? pos - 1 : std::string::npos;
109
+ }
110
+
111
+ // Find the end of a JSON array starting at the given position
112
+ inline size_t findArrayEnd(const std::string& json, size_t startPos) {
113
+ if (startPos >= json.length() || json[startPos] != '[') {
114
+ return std::string::npos;
115
+ }
116
+
117
+ int bracketCount = 1;
118
+ size_t pos = startPos + 1;
119
+
120
+ while (pos < json.length() && bracketCount > 0) {
121
+ if (json[pos] == '[') bracketCount++;
122
+ else if (json[pos] == ']') bracketCount--;
123
+ pos++;
124
+ }
125
+
126
+ return bracketCount == 0 ? pos - 1 : std::string::npos;
127
+ }
128
+
129
+ // Parse a single menu item from JSON object boundaries
130
+ inline MenuItemJson parseMenuItem(const std::string& json, size_t startPos, size_t endPos) {
131
+ MenuItemJson item;
132
+
133
+ item.label = extractJsonStringValue(json, "label", startPos, endPos);
134
+ item.type = extractJsonStringValue(json, "type", startPos, endPos);
135
+ item.action = extractJsonStringValue(json, "action", startPos, endPos);
136
+ item.role = extractJsonStringValue(json, "role", startPos, endPos);
137
+ item.tooltip = extractJsonStringValue(json, "tooltip", startPos, endPos);
138
+ item.accelerator = extractJsonStringValue(json, "accelerator", startPos, endPos);
139
+ item.enabled = extractJsonBoolValue(json, "enabled", startPos, endPos, true);
140
+ item.checked = extractJsonBoolValue(json, "checked", startPos, endPos, false);
141
+ item.hidden = extractJsonBoolValue(json, "hidden", startPos, endPos, false);
142
+
143
+ // Look for submenu array
144
+ std::string submenuKey = "\"submenu\":";
145
+ size_t submenuPos = json.find(submenuKey, startPos);
146
+ if (submenuPos != std::string::npos && submenuPos < endPos) {
147
+ size_t arrayStart = json.find('[', submenuPos);
148
+ if (arrayStart != std::string::npos && arrayStart < endPos) {
149
+ size_t arrayEnd = findArrayEnd(json, arrayStart);
150
+ if (arrayEnd != std::string::npos) {
151
+ // Recursively parse submenu items
152
+ size_t itemStart = json.find('{', arrayStart);
153
+ while (itemStart != std::string::npos && itemStart < arrayEnd) {
154
+ size_t itemEnd = findObjectEnd(json, itemStart);
155
+ if (itemEnd != std::string::npos && itemEnd <= arrayEnd) {
156
+ item.submenu.push_back(parseMenuItem(json, itemStart, itemEnd));
157
+ itemStart = json.find('{', itemEnd + 1);
158
+ } else {
159
+ break;
160
+ }
161
+ }
162
+ }
163
+ }
164
+ }
165
+
166
+ // Set default type based on content
167
+ if (item.type.empty()) {
168
+ if (item.label == "-" || item.label.empty()) {
169
+ item.type = "separator";
170
+ } else if (!item.submenu.empty()) {
171
+ item.type = "submenu";
172
+ } else {
173
+ item.type = "normal";
174
+ }
175
+ }
176
+
177
+ return item;
178
+ }
179
+
180
+ // Parse a JSON array of menu items
181
+ inline std::vector<MenuItemJson> parseMenuJson(const std::string& jsonStr) {
182
+ std::vector<MenuItemJson> items;
183
+
184
+ if (jsonStr.empty()) return items;
185
+
186
+ // Find the start of the array
187
+ size_t arrayStart = jsonStr.find('[');
188
+ if (arrayStart == std::string::npos) {
189
+ // Try parsing as a single object
190
+ size_t objStart = jsonStr.find('{');
191
+ if (objStart != std::string::npos) {
192
+ size_t objEnd = findObjectEnd(jsonStr, objStart);
193
+ if (objEnd != std::string::npos) {
194
+ items.push_back(parseMenuItem(jsonStr, objStart, objEnd));
195
+ }
196
+ }
197
+ return items;
198
+ }
199
+
200
+ size_t arrayEnd = findArrayEnd(jsonStr, arrayStart);
201
+ if (arrayEnd == std::string::npos) {
202
+ arrayEnd = jsonStr.length();
203
+ }
204
+
205
+ // Find and parse each menu item object
206
+ size_t pos = arrayStart + 1;
207
+ while (pos < arrayEnd) {
208
+ size_t objStart = jsonStr.find('{', pos);
209
+ if (objStart == std::string::npos || objStart >= arrayEnd) break;
210
+
211
+ size_t objEnd = findObjectEnd(jsonStr, objStart);
212
+ if (objEnd == std::string::npos || objEnd > arrayEnd) break;
213
+
214
+ items.push_back(parseMenuItem(jsonStr, objStart, objEnd));
215
+ pos = objEnd + 1;
216
+ }
217
+
218
+ return items;
219
+ }
220
+
221
+ } // namespace electrobun
222
+
223
+ #endif // ELECTROBUN_JSON_MENU_PARSER_H
@@ -0,0 +1,101 @@
1
+ // mime_types.h - Cross-platform MIME type detection
2
+ // Based on Bun runtime supported file types and web development standards
3
+ // Used across Windows, macOS, and Linux
4
+ //
5
+ // This is a header-only implementation to avoid build complexity.
6
+
7
+ #ifndef ELECTROBUN_MIME_TYPES_H
8
+ #define ELECTROBUN_MIME_TYPES_H
9
+
10
+ #include <string>
11
+
12
+ namespace electrobun {
13
+
14
+ // Get MIME type from a URL or file path based on extension
15
+ // Returns "application/octet-stream" as default for unknown types
16
+ inline std::string getMimeTypeFromUrl(const std::string& url) {
17
+ // Web/Code Files (Bun native support)
18
+ if (url.find(".html") != std::string::npos || url.find(".htm") != std::string::npos) {
19
+ return "text/html";
20
+ } else if (url.find(".js") != std::string::npos || url.find(".mjs") != std::string::npos || url.find(".cjs") != std::string::npos) {
21
+ return "text/javascript";
22
+ } else if (url.find(".ts") != std::string::npos || url.find(".mts") != std::string::npos || url.find(".cts") != std::string::npos) {
23
+ return "text/typescript";
24
+ } else if (url.find(".jsx") != std::string::npos) {
25
+ return "text/jsx";
26
+ } else if (url.find(".tsx") != std::string::npos) {
27
+ return "text/tsx";
28
+ } else if (url.find(".css") != std::string::npos) {
29
+ return "text/css";
30
+ } else if (url.find(".json") != std::string::npos) {
31
+ return "application/json";
32
+ } else if (url.find(".xml") != std::string::npos) {
33
+ return "application/xml";
34
+ } else if (url.find(".md") != std::string::npos) {
35
+ return "text/markdown";
36
+ } else if (url.find(".txt") != std::string::npos) {
37
+ return "text/plain";
38
+ } else if (url.find(".toml") != std::string::npos) {
39
+ return "application/toml";
40
+ } else if (url.find(".yaml") != std::string::npos || url.find(".yml") != std::string::npos) {
41
+ return "application/x-yaml";
42
+
43
+ // Image Files
44
+ } else if (url.find(".png") != std::string::npos) {
45
+ return "image/png";
46
+ } else if (url.find(".jpg") != std::string::npos || url.find(".jpeg") != std::string::npos) {
47
+ return "image/jpeg";
48
+ } else if (url.find(".gif") != std::string::npos) {
49
+ return "image/gif";
50
+ } else if (url.find(".webp") != std::string::npos) {
51
+ return "image/webp";
52
+ } else if (url.find(".svg") != std::string::npos) {
53
+ return "image/svg+xml";
54
+ } else if (url.find(".ico") != std::string::npos) {
55
+ return "image/x-icon";
56
+ } else if (url.find(".avif") != std::string::npos) {
57
+ return "image/avif";
58
+
59
+ // Font Files
60
+ } else if (url.find(".woff") != std::string::npos) {
61
+ return "font/woff";
62
+ } else if (url.find(".woff2") != std::string::npos) {
63
+ return "font/woff2";
64
+ } else if (url.find(".ttf") != std::string::npos) {
65
+ return "font/ttf";
66
+ } else if (url.find(".otf") != std::string::npos) {
67
+ return "font/otf";
68
+
69
+ // Media Files
70
+ } else if (url.find(".mp3") != std::string::npos) {
71
+ return "audio/mpeg";
72
+ } else if (url.find(".mp4") != std::string::npos) {
73
+ return "video/mp4";
74
+ } else if (url.find(".webm") != std::string::npos) {
75
+ return "video/webm";
76
+ } else if (url.find(".ogg") != std::string::npos) {
77
+ return "audio/ogg";
78
+ } else if (url.find(".wav") != std::string::npos) {
79
+ return "audio/wav";
80
+
81
+ // Document Files
82
+ } else if (url.find(".pdf") != std::string::npos) {
83
+ return "application/pdf";
84
+
85
+ // WebAssembly (Bun support)
86
+ } else if (url.find(".wasm") != std::string::npos) {
87
+ return "application/wasm";
88
+
89
+ // Compressed Files
90
+ } else if (url.find(".zip") != std::string::npos) {
91
+ return "application/zip";
92
+ } else if (url.find(".gz") != std::string::npos) {
93
+ return "application/gzip";
94
+ }
95
+
96
+ return "application/octet-stream"; // default
97
+ }
98
+
99
+ } // namespace electrobun
100
+
101
+ #endif // ELECTROBUN_MIME_TYPES_H
@@ -0,0 +1,98 @@
1
+ // navigation_rules.h - Cross-platform navigation rules checking
2
+ // Handles URL navigation rules with glob pattern matching
3
+ // Used across Windows, macOS, and Linux
4
+ //
5
+ // This is a header-only implementation to avoid build complexity.
6
+
7
+ #ifndef ELECTROBUN_NAVIGATION_RULES_H
8
+ #define ELECTROBUN_NAVIGATION_RULES_H
9
+
10
+ #include <string>
11
+ #include <vector>
12
+ #include "glob_match.h"
13
+
14
+ namespace electrobun {
15
+
16
+ // Represents a single navigation rule
17
+ // Rules starting with "^" are block rules (inverts the match)
18
+ struct NavigationRule {
19
+ std::string pattern;
20
+ bool isBlockRule;
21
+
22
+ NavigationRule(const std::string& ruleString) {
23
+ if (!ruleString.empty() && ruleString[0] == '^') {
24
+ isBlockRule = true;
25
+ pattern = ruleString.substr(1);
26
+ } else {
27
+ isBlockRule = false;
28
+ pattern = ruleString;
29
+ }
30
+ }
31
+ };
32
+
33
+ // Check if a URL should be allowed based on navigation rules
34
+ // Uses "last match wins" semantics
35
+ // Returns true if navigation should be allowed, false if blocked
36
+ //
37
+ // Rules are processed in order:
38
+ // - Normal rules (no ^ prefix): if URL matches, allow navigation
39
+ // - Block rules (^ prefix): if URL matches, block navigation
40
+ // - Last matching rule determines the outcome
41
+ // - If no rules match, returns defaultAllow
42
+ inline bool checkNavigationRulesForUrl(const std::vector<std::string>& rules,
43
+ const std::string& url,
44
+ bool defaultAllow = true) {
45
+ bool shouldAllow = defaultAllow;
46
+ bool anyMatch = false;
47
+
48
+ for (const auto& ruleString : rules) {
49
+ if (ruleString.empty()) continue;
50
+
51
+ NavigationRule rule(ruleString);
52
+
53
+ if (globMatch(rule.pattern, url)) {
54
+ anyMatch = true;
55
+ // If it's a block rule, matching means we should NOT allow
56
+ // If it's a normal rule, matching means we SHOULD allow
57
+ shouldAllow = !rule.isBlockRule;
58
+ }
59
+ }
60
+
61
+ return shouldAllow;
62
+ }
63
+
64
+ // Overload that takes a comma-separated rules string
65
+ inline bool checkNavigationRulesForUrl(const std::string& rulesString,
66
+ const std::string& url,
67
+ bool defaultAllow = true) {
68
+ std::vector<std::string> rules;
69
+
70
+ size_t start = 0;
71
+ size_t end = rulesString.find(',');
72
+
73
+ while (end != std::string::npos) {
74
+ std::string rule = rulesString.substr(start, end - start);
75
+ // Trim whitespace
76
+ size_t first = rule.find_first_not_of(" \t");
77
+ size_t last = rule.find_last_not_of(" \t");
78
+ if (first != std::string::npos) {
79
+ rules.push_back(rule.substr(first, last - first + 1));
80
+ }
81
+ start = end + 1;
82
+ end = rulesString.find(',', start);
83
+ }
84
+
85
+ // Add the last rule
86
+ std::string rule = rulesString.substr(start);
87
+ size_t first = rule.find_first_not_of(" \t");
88
+ size_t last = rule.find_last_not_of(" \t");
89
+ if (first != std::string::npos) {
90
+ rules.push_back(rule.substr(first, last - first + 1));
91
+ }
92
+
93
+ return checkNavigationRulesForUrl(rules, url, defaultAllow);
94
+ }
95
+
96
+ } // namespace electrobun
97
+
98
+ #endif // ELECTROBUN_NAVIGATION_RULES_H