craft-native 0.0.418 → 0.0.420
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.
- package/dist/cli.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/macos/src/index.js +1 -1
- package/dist/macos/src/toolchain.d.ts +1 -1
- package/dist/macos/templates/CraftMacApplication.swift +18 -0
- package/dist/macos/templates/CraftMacRuntime.swift +7 -1
- package/dist/macos/templates/CraftMacWebView.swift +46 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -3047,7 +3047,7 @@ function pinCraftNativeDependency(packagePath, craftVersion) {
|
|
|
3047
3047
|
`);
|
|
3048
3048
|
}
|
|
3049
3049
|
// package.json
|
|
3050
|
-
var version = "0.0.
|
|
3050
|
+
var version = "0.0.420";
|
|
3051
3051
|
|
|
3052
3052
|
// bin/cli.ts
|
|
3053
3053
|
var spawnedFrom = process6.env[CRAFT_CLI_SPAWN_MARKER];
|
package/dist/index.cjs
CHANGED
package/dist/index.js
CHANGED
package/dist/macos/src/index.js
CHANGED
|
@@ -43,7 +43,7 @@ import process from "process";
|
|
|
43
43
|
var macosToolchain = {
|
|
44
44
|
zig: "0.18.0-dev.252+86dc5ce25",
|
|
45
45
|
sources: {
|
|
46
|
-
"zig-js": { repository: "zig-utils/zig-js", revision: "
|
|
46
|
+
"zig-js": { repository: "zig-utils/zig-js", revision: "fef92e7f6c5e110102df15e73bbea4a6be7493b5", paths: ["src", "include", "tests", "tools", "docs", "bench", "conformance"] },
|
|
47
47
|
"zig-regex": { repository: "zig-utils/zig-regex", revision: "fdd9c2e333ea6ac2fd06d080d939ef7f11d61b1b", paths: ["src"] },
|
|
48
48
|
"zig-gc": { repository: "zig-utils/zig-gc", revision: "0285ef801dd221a4ae8e88b31a09ae31627657a3", paths: ["src"] }
|
|
49
49
|
}
|
|
@@ -9,7 +9,7 @@ export declare const macosToolchain: {
|
|
|
9
9
|
readonly sources: {
|
|
10
10
|
readonly 'zig-js': {
|
|
11
11
|
readonly repository: 'zig-utils/zig-js';
|
|
12
|
-
readonly revision: '
|
|
12
|
+
readonly revision: 'fef92e7f6c5e110102df15e73bbea4a6be7493b5';
|
|
13
13
|
readonly paths: readonly ['src', 'include', 'tests', 'tools', 'docs', 'bench', 'conformance'];
|
|
14
14
|
};
|
|
15
15
|
readonly 'zig-regex': {
|
|
@@ -73,6 +73,8 @@ final class CraftMacScene: NSObject, NSWindowDelegate {
|
|
|
73
73
|
func windowWillClose(_ notification: Notification) {
|
|
74
74
|
runtime.dispose()
|
|
75
75
|
CraftMacApplication.shared.scenes.removeValue(forKey: name)
|
|
76
|
+
// The app's other windows learn it is gone (its tabs closed with it), so none waits on it.
|
|
77
|
+
for other in CraftMacApplication.shared.scenes.values { other.runtime.send("CRAFT_EVENT", ["event": "craftDesktopWindowClosed", "detail": ["name": name]]) }
|
|
76
78
|
CraftMacApplication.shared.quitReady(scene: name)
|
|
77
79
|
}
|
|
78
80
|
func windowDidBecomeKey(_ notification: Notification) { runtime.send("SCREEN_FOCUS", ["focused": true]) }
|
|
@@ -419,6 +421,22 @@ final class CraftMacApplication: NSObject, NSApplicationDelegate, UNUserNotifica
|
|
|
419
421
|
// After this result is written, from the run loop rather than a main-queue block (see applicationShouldTerminate).
|
|
420
422
|
if kind == "quit" { NSApp.perform(#selector(NSApplication.terminate(_:)), with: nil, afterDelay: 0); return ["ok": true] }
|
|
421
423
|
if kind == "openURL", let url = (action["url"] as? String).flatMap(URL.init(string:)) { application(NSApp, open: [url]); return ["ok": true] }
|
|
424
|
+
#if CRAFT_WEBKIT
|
|
425
|
+
// Script in a tab's page, as a person's click or a page's own code would act: what the
|
|
426
|
+
// renderer cannot reach (a link inside the page, an element to zap). `value` is the
|
|
427
|
+
// script's result as text; return JSON.stringify(...) for anything structured.
|
|
428
|
+
if kind == "web", let id = action["tab"] as? String, let script = action["js"] as? String {
|
|
429
|
+
guard let tab = CraftMacWebTabs.shared.tabs[id] else { return ["ok": false, "error": "No tab \(id)"] }
|
|
430
|
+
var answer: [String: Any]?
|
|
431
|
+
tab.view.evaluateJavaScript(script) { value, error in
|
|
432
|
+
if let error { answer = ["ok": false, "error": error.localizedDescription]; return }
|
|
433
|
+
answer = ["ok": true, "value": value.map { "\($0)" } ?? NSNull()]
|
|
434
|
+
}
|
|
435
|
+
let deadline = Date().addingTimeInterval(5)
|
|
436
|
+
while answer == nil && Date() < deadline { RunLoop.main.run(until: Date().addingTimeInterval(0.02)) }
|
|
437
|
+
return answer ?? ["ok": false, "error": "The page did not answer within 5 s"]
|
|
438
|
+
}
|
|
439
|
+
#endif
|
|
422
440
|
if kind == "appearance" {
|
|
423
441
|
NSApp.appearance = NSAppearance(named: action["value"] as? String == "dark" ? .darkAqua : .aqua)
|
|
424
442
|
appearanceChanged(); return ["ok": true]
|
|
@@ -68,7 +68,13 @@ final class CraftMacRuntime {
|
|
|
68
68
|
#endif
|
|
69
69
|
return list
|
|
70
70
|
}
|
|
71
|
-
|
|
71
|
+
/// zig-js's precise collector, not the default arena: an arena frees nothing until the
|
|
72
|
+
/// context goes, so a window open all day kept every object its screen ever made.
|
|
73
|
+
let context: ZigJSContext = {
|
|
74
|
+
let created = ZJSGlobalContextCreateGarbageCollected(true)!
|
|
75
|
+
defer { JSGlobalContextRelease(created) }
|
|
76
|
+
return ZigJSContext(jsGlobalContextRef: created)!
|
|
77
|
+
}()
|
|
72
78
|
let route: String
|
|
73
79
|
let network: CraftMacNetwork
|
|
74
80
|
weak var scene: CraftMacScene?
|
|
@@ -40,8 +40,17 @@ final class CraftMacWebProfiles {
|
|
|
40
40
|
return configuration.copy() as! WKWebViewConfiguration
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
/// One store per private profile for as long as the app runs, so clearing data or adding
|
|
44
|
+
/// cookies reaches the store its tabs use (each `.nonPersistent()` call makes a new, empty one).
|
|
45
|
+
private var ephemeralStores: [String: WKWebsiteDataStore] = [:]
|
|
46
|
+
|
|
43
47
|
func store(profile: String, ephemeral: Bool) -> WKWebsiteDataStore {
|
|
44
|
-
if ephemeral {
|
|
48
|
+
if ephemeral {
|
|
49
|
+
if let existing = ephemeralStores[profile] { return existing }
|
|
50
|
+
let made = WKWebsiteDataStore.nonPersistent()
|
|
51
|
+
ephemeralStores[profile] = made
|
|
52
|
+
return made
|
|
53
|
+
}
|
|
45
54
|
if profile == "default" { return .default() }
|
|
46
55
|
if #available(macOS 14.0, *) { return WKWebsiteDataStore(forIdentifier: Self.identifier(profile)) }
|
|
47
56
|
return .default()
|
|
@@ -494,6 +503,38 @@ final class CraftMacWebTabs {
|
|
|
494
503
|
}
|
|
495
504
|
}
|
|
496
505
|
|
|
506
|
+
/// Cookies put into a profile's store, as another browser's import brings them: `domain` with
|
|
507
|
+
/// a leading dot covers subdomains, `expires` is milliseconds since 1970 (absent for a session
|
|
508
|
+
/// cookie), `sameSite` is "lax", "strict" or "none". Answers how many were set, and how many
|
|
509
|
+
/// could not form a cookie (no name or domain).
|
|
510
|
+
func setCookies(_ options: [String: Any], answer: @escaping (Any) -> Void) {
|
|
511
|
+
let store = CraftMacWebProfiles.shared.store(profile: options["profile"] as? String ?? "default", ephemeral: options["private"] as? Bool == true).httpCookieStore
|
|
512
|
+
let cookies = (options["cookies"] as? [[String: Any]] ?? []).map(Self.cookie)
|
|
513
|
+
let group = DispatchGroup()
|
|
514
|
+
for case let cookie? in cookies {
|
|
515
|
+
group.enter()
|
|
516
|
+
store.setCookie(cookie) { group.leave() }
|
|
517
|
+
}
|
|
518
|
+
let set = cookies.compactMap { $0 }.count
|
|
519
|
+
group.notify(queue: .main) { answer(["set": set, "skipped": cookies.count - set]) }
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
static func cookie(_ fields: [String: Any]) -> HTTPCookie? {
|
|
523
|
+
guard let name = fields["name"] as? String, !name.isEmpty, let domain = fields["domain"] as? String, !domain.isEmpty else { return nil }
|
|
524
|
+
var properties: [HTTPCookiePropertyKey: Any] = [
|
|
525
|
+
.name: name, .value: fields["value"] as? String ?? "", .domain: domain, .path: fields["path"] as? String ?? "/",
|
|
526
|
+
]
|
|
527
|
+
if let expires = fields["expires"] as? Double { properties[.expires] = Date(timeIntervalSince1970: expires / 1000) }
|
|
528
|
+
if fields["secure"] as? Bool == true { properties[.secure] = "TRUE" }
|
|
529
|
+
if fields["httpOnly"] as? Bool == true { properties[HTTPCookiePropertyKey("HttpOnly")] = "TRUE" }
|
|
530
|
+
switch fields["sameSite"] as? String {
|
|
531
|
+
case "lax": properties[.sameSitePolicy] = HTTPCookieStringPolicy.sameSiteLax
|
|
532
|
+
case "strict": properties[.sameSitePolicy] = HTTPCookieStringPolicy.sameSiteStrict
|
|
533
|
+
default: break
|
|
534
|
+
}
|
|
535
|
+
return HTTPCookie(properties: properties)
|
|
536
|
+
}
|
|
537
|
+
|
|
497
538
|
/// A closed window takes its tabs with it.
|
|
498
539
|
func closeAll(owner: CraftMacRuntime) {
|
|
499
540
|
for (id, tab) in tabs where tab.owner == nil || tab.owner === owner { tabs.removeValue(forKey: id); tab.close() }
|
|
@@ -502,6 +543,9 @@ final class CraftMacWebTabs {
|
|
|
502
543
|
func perform(_ method: String, _ args: [Any], owner: CraftMacRuntime, answer: @escaping (Any) -> Void, fail: @escaping (String, String) -> Void) {
|
|
503
544
|
let options = args.first as? [String: Any] ?? [:]
|
|
504
545
|
if method == "create" {
|
|
546
|
+
// A tab that exists is answered as it is: loading the address again would reload a
|
|
547
|
+
// page that another window, say, hands over with its state intact.
|
|
548
|
+
if let id = options["id"] as? String, let existing = tabs[id] { answer(existing.state); return }
|
|
505
549
|
let tab = make(id: options["id"] as? String, profile: options["profile"] as? String ?? "default", ephemeral: options["private"] as? Bool == true, owner: owner)
|
|
506
550
|
tab.view.isInspectable = options["inspectable"] as? Bool ?? true
|
|
507
551
|
if let address = options["url"] as? String, !address.isEmpty {
|
|
@@ -525,6 +569,7 @@ final class CraftMacWebTabs {
|
|
|
525
569
|
let decision: WKPermissionDecision = { switch args.dropFirst().first as? String { case "grant": return .grant; case "prompt": return .prompt; default: return .deny } }()
|
|
526
570
|
answer(self.answer(args.first as? String ?? "", decision)); return
|
|
527
571
|
case "clearData": clearData(options, answer: answer); return
|
|
572
|
+
case "setCookies": setCookies(options, answer: answer); return
|
|
528
573
|
case "cancelDownload": answer(CraftMacDownloads.shared.cancel(args.first as? String ?? "")); return
|
|
529
574
|
case "revealDownload", "openDownload":
|
|
530
575
|
guard let file = CraftMacDownloads.downloadedFile(args.first as? String ?? "") else { fail("NOT_FOUND", "Not a file in Downloads"); return }
|