Skip to content

Commit 40225d9

Browse files
Jonathan D.A. Jewellclaude
andcommitted
fix: resolve ReScript compilation errors and reorder definitions
- Add Fetch API bindings (was missing, pre-existing bug) - Fix JsExn pattern matching (exn vs Js.Exn.t mismatch) - Fix operator precedence: !(response->ok) not !response->ok - Fix dict type: store floats directly, not JSON-encoded floats - Fix Int.max not available: use ternary comparison - Reorder file: parsers defined before callers (ReScript requirement) Build succeeds with only deprecation warnings (Js.* → modern API). Extension rebuilt: fireflag-0.1.0.zip (125K) SHA256: bc1d2320f0c1072b7f5650f732afb76e78b180dedaaacbea792c85bf27c6ea47 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 5d97d84 commit 40225d9

5 files changed

Lines changed: 751 additions & 144 deletions

File tree

extension/lib/rescript/BrowserAPI.res.js

Lines changed: 62 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

extension/lib/rescript/DatabaseUpdater.res

Lines changed: 157 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,18 @@
1313

1414
open Types
1515

16+
// Fetch API bindings (browser global)
17+
module Fetch = {
18+
type response
19+
20+
@val external fetch: string => promise<response> = "fetch"
21+
22+
module Response = {
23+
@get external ok: response => bool = "ok"
24+
@send external json: response => promise<JSON.t> = "json"
25+
}
26+
}
27+
1628
type databaseVersion = {
1729
version: string,
1830
lastUpdated: string,
@@ -31,144 +43,7 @@ type updateStatus =
3143
| UpdateRequired(string) // Reason for requirement
3244
| UpdateFailed(string) // Error message
3345

34-
// Check for database updates from GitHub releases
35-
let checkForUpdates = async (currentVersion: string): updateStatus => {
36-
try {
37-
// Fetch latest release from GitHub API
38-
let response = await Fetch.fetch(
39-
"https://api.github.com/repos/hyperpolymath/fireflag/releases/latest",
40-
)
41-
42-
if !response->Fetch.Response.ok {
43-
UpdateFailed("Failed to fetch update information")
44-
} else {
45-
let json = await response->Fetch.Response.json
46-
let latestVersion = json
47-
->Js.Json.decodeObject
48-
->Option.flatMap(obj => Js.Dict.get(obj, "tag_name"))
49-
->Option.flatMap(Js.Json.decodeString)
50-
->Option.getOr(currentVersion)
51-
52-
if latestVersion > currentVersion {
53-
UpdateAvailable({
54-
version: latestVersion,
55-
lastUpdated: Js.Date.now()->Js.Date.fromFloat->Js.Date.toISOString,
56-
minimumGeckoVersion: "109.0",
57-
maximumGeckoVersion: None,
58-
})
59-
} else {
60-
UpToDate
61-
}
62-
}
63-
} catch {
64-
| exn => UpdateFailed(Js.Exn.message(exn)->Option.getOr("Unknown error"))
65-
}
66-
}
67-
68-
// Download and validate updated database
69-
let downloadDatabase = async (version: databaseVersion): result<flagDatabase, string> => {
70-
try {
71-
let url = `https://github.com/hyperpolymath/fireflag/releases/download/${version.version}/flags-database.json`
72-
73-
let response = await Fetch.fetch(url)
74-
75-
if !response->Fetch.Response.ok {
76-
Error("Failed to download database")
77-
} else {
78-
let json = await response->Fetch.Response.json
79-
let db = parseFlagDatabase(json) // Validate schema
80-
81-
Ok(db)
82-
}
83-
} catch {
84-
| exn => Error(Js.Exn.message(exn)->Option.getOr("Download failed"))
85-
}
86-
}
87-
88-
// Validate flag availability against current browser version
89-
let validateFlagForBrowser = (flag: flag, browserVersion: string): bool => {
90-
// Check minimum version
91-
let meetsMin = switch flag.geckoMinVersion {
92-
| Some(minVer) => compareVersions(browserVersion, minVer) >= 0
93-
| None => true
94-
}
95-
96-
// Check maximum version (if flag was removed)
97-
let meetsMax = switch flag.geckoMaxVersion {
98-
| Some(maxVer) => compareVersions(browserVersion, maxVer) <= 0
99-
| None => true
100-
}
101-
102-
meetsMin && meetsMax
103-
}
104-
105-
// Compare semantic versions (returns -1, 0, or 1)
106-
let compareVersions = (a: string, b: string): int => {
107-
let parseVersion = v =>
108-
v
109-
->String.split(".")
110-
->Array.map(s => Int.fromString(s)->Option.getOr(0))
111-
112-
let aParts = parseVersion(a)
113-
let bParts = parseVersion(b)
114-
let maxLen = Int.max(Array.length(aParts), Array.length(bParts))
115-
116-
let rec compare = (i: int): int => {
117-
if i >= maxLen {
118-
0
119-
} else {
120-
let aVal = Array.get(aParts, i)->Option.getOr(0)
121-
let bVal = Array.get(bParts, i)->Option.getOr(0)
122-
123-
if aVal < bVal {
124-
-1
125-
} else if aVal > bVal {
126-
1
127-
} else {
128-
compare(i + 1)
129-
}
130-
}
131-
}
132-
133-
compare(0)
134-
}
135-
136-
// Filter database for current browser version
137-
let filterDatabaseForBrowser = (db: flagDatabase, browserVersion: string): flagDatabase => {
138-
{
139-
...db,
140-
flags: db.flags->Array.filter(flag => validateFlagForBrowser(flag, browserVersion)),
141-
}
142-
}
143-
144-
// Detect deprecated flags (have maxVersion)
145-
let findDeprecatedFlags = (db: flagDatabase): array<flag> => {
146-
db.flags->Array.filter(flag => flag.geckoMaxVersion->Option.isSome)
147-
}
148-
149-
// Detect flags unavailable in current version
150-
let findUnavailableFlags = (db: flagDatabase, browserVersion: string): array<flag> => {
151-
db.flags->Array.filter(flag => !validateFlagForBrowser(flag, browserVersion))
152-
}
153-
154-
// Auto-update workflow
155-
let autoUpdate = async (currentVersion: string, enableAutoUpdate: bool): result<
156-
flagDatabase,
157-
string,
158-
> => {
159-
if !enableAutoUpdate {
160-
Error("Auto-update disabled")
161-
} else {
162-
let status = await checkForUpdates(currentVersion)
163-
164-
switch status {
165-
| UpToDate => Error("Already up to date")
166-
| UpdateFailed(msg) => Error(msg)
167-
| UpdateRequired(reason) => Error(`Update required: ${reason}`)
168-
| UpdateAvailable(version) => await downloadDatabase(version)
169-
}
170-
}
171-
}
46+
// --- JSON parsing helpers (must be defined before callers) ---
17247

17348
// Parse safety level string to variant
17449
let parseSafetyLevel = (s: string): safetyLevel =>
@@ -313,3 +188,147 @@ let parseFlagDatabase = (json: Js.Json.t): flagDatabase =>
313188

314189
{version, lastUpdated, categories, flags}
315190
}
191+
192+
// --- Version comparison (must be before validateFlagForBrowser) ---
193+
194+
// Compare semantic versions (returns -1, 0, or 1)
195+
let compareVersions = (a: string, b: string): int => {
196+
let parseVersion = v =>
197+
v
198+
->String.split(".")
199+
->Array.map(s => Int.fromString(s)->Option.getOr(0))
200+
201+
let aParts = parseVersion(a)
202+
let bParts = parseVersion(b)
203+
let lenA = Array.length(aParts)
204+
let lenB = Array.length(bParts)
205+
let maxLen = lenA > lenB ? lenA : lenB
206+
207+
let rec compare = (i: int): int => {
208+
if i >= maxLen {
209+
0
210+
} else {
211+
let aVal = Array.get(aParts, i)->Option.getOr(0)
212+
let bVal = Array.get(bParts, i)->Option.getOr(0)
213+
214+
if aVal < bVal {
215+
-1
216+
} else if aVal > bVal {
217+
1
218+
} else {
219+
compare(i + 1)
220+
}
221+
}
222+
}
223+
224+
compare(0)
225+
}
226+
227+
// --- Network and validation functions ---
228+
229+
// Check for database updates from GitHub releases
230+
let checkForUpdates = async (currentVersion: string): updateStatus => {
231+
try {
232+
let response = await Fetch.fetch(
233+
"https://api.github.com/repos/hyperpolymath/fireflag/releases/latest",
234+
)
235+
236+
if !(response->Fetch.Response.ok) {
237+
UpdateFailed("Failed to fetch update information")
238+
} else {
239+
let json = await response->Fetch.Response.json
240+
let latestVersion = json
241+
->Js.Json.decodeObject
242+
->Option.flatMap(obj => Js.Dict.get(obj, "tag_name"))
243+
->Option.flatMap(Js.Json.decodeString)
244+
->Option.getOr(currentVersion)
245+
246+
if latestVersion > currentVersion {
247+
UpdateAvailable({
248+
version: latestVersion,
249+
lastUpdated: Js.Date.now()->Js.Date.fromFloat->Js.Date.toISOString,
250+
minimumGeckoVersion: "109.0",
251+
maximumGeckoVersion: None,
252+
})
253+
} else {
254+
UpToDate
255+
}
256+
}
257+
} catch {
258+
| JsExn(exn) => UpdateFailed(JsExn.message(exn)->Option.getOr("Unknown error"))
259+
| _ => UpdateFailed("Unknown error")
260+
}
261+
}
262+
263+
// Download and validate updated database
264+
let downloadDatabase = async (version: databaseVersion): result<flagDatabase, string> => {
265+
try {
266+
let url = `https://github.com/hyperpolymath/fireflag/releases/download/${version.version}/flags-database.json`
267+
268+
let response = await Fetch.fetch(url)
269+
270+
if !(response->Fetch.Response.ok) {
271+
Error("Failed to download database")
272+
} else {
273+
let json = await response->Fetch.Response.json
274+
let db = parseFlagDatabase(json)
275+
276+
Ok(db)
277+
}
278+
} catch {
279+
| JsExn(exn) => Error(JsExn.message(exn)->Option.getOr("Download failed"))
280+
| _ => Error("Download failed")
281+
}
282+
}
283+
284+
// Validate flag availability against current browser version
285+
let validateFlagForBrowser = (flag: flag, browserVersion: string): bool => {
286+
let meetsMin = switch flag.geckoMinVersion {
287+
| Some(minVer) => compareVersions(browserVersion, minVer) >= 0
288+
| None => true
289+
}
290+
291+
let meetsMax = switch flag.geckoMaxVersion {
292+
| Some(maxVer) => compareVersions(browserVersion, maxVer) <= 0
293+
| None => true
294+
}
295+
296+
meetsMin && meetsMax
297+
}
298+
299+
// Filter database for current browser version
300+
let filterDatabaseForBrowser = (db: flagDatabase, browserVersion: string): flagDatabase => {
301+
{
302+
...db,
303+
flags: db.flags->Array.filter(flag => validateFlagForBrowser(flag, browserVersion)),
304+
}
305+
}
306+
307+
// Detect deprecated flags (have maxVersion)
308+
let findDeprecatedFlags = (db: flagDatabase): array<flag> => {
309+
db.flags->Array.filter(flag => flag.geckoMaxVersion->Option.isSome)
310+
}
311+
312+
// Detect flags unavailable in current version
313+
let findUnavailableFlags = (db: flagDatabase, browserVersion: string): array<flag> => {
314+
db.flags->Array.filter(flag => !validateFlagForBrowser(flag, browserVersion))
315+
}
316+
317+
// Auto-update workflow
318+
let autoUpdate = async (currentVersion: string, enableAutoUpdate: bool): result<
319+
flagDatabase,
320+
string,
321+
> => {
322+
if !enableAutoUpdate {
323+
Error("Auto-update disabled")
324+
} else {
325+
let status = await checkForUpdates(currentVersion)
326+
327+
switch status {
328+
| UpToDate => Error("Already up to date")
329+
| UpdateFailed(msg) => Error(msg)
330+
| UpdateRequired(reason) => Error(`Update required: ${reason}`)
331+
| UpdateAvailable(version) => await downloadDatabase(version)
332+
}
333+
}
334+
}

0 commit comments

Comments
 (0)