craft-native 0.0.496 → 0.0.499
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/android/templates/CraftAppLinks.kt.template +28 -0
- package/dist/android/templates/CraftBridge.kt.template +3 -29
- package/dist/android/templates/CraftNativeBackground.kt.template +21 -2
- package/dist/android/templates/CraftNativeCapabilities.kt.template +145 -28
- package/dist/android/templates/CraftNativeShell.kt.template +49 -9
- package/dist/android/templates/CraftNativeSounds.kt.template +22 -3
- package/dist/android/templates/MainActivityNative.kt.template +19 -13
- package/dist/browser.js +27 -5
- package/dist/cli.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/ios/templates/CraftApp.swift +8 -1
- package/dist/ios/templates/CraftHybrid.swift +135 -49
- package/dist/ios/templates/CraftNativeActions.swift +47 -3
- package/dist/ios/templates/CraftNativeFiles.swift +8 -1
- package/dist/ios/templates/CraftNativeScreen.swift +46 -11
- package/dist/ios/templates/CraftPersistentStorage.swift +28 -5
- package/package.json +1 -1
|
@@ -13,11 +13,22 @@ enum CraftPersistentStorage {
|
|
|
13
13
|
let digest = SHA256.hash(data: Data(key.utf8)).map { String(format: "%02x", $0) }.joined()
|
|
14
14
|
return directory.appendingPathComponent(digest + ".json")
|
|
15
15
|
}
|
|
16
|
+
/// What an entry file holds, or nil when it is there and is not an entry.
|
|
17
|
+
///
|
|
18
|
+
/// A file that cannot be parsed is a corrupt value, as one that parses to
|
|
19
|
+
/// the wrong thing is, and an app starts that value again. Only a file
|
|
20
|
+
/// that cannot be read at all is the disk failing, which an app retries;
|
|
21
|
+
/// that still throws. The two used to share the disk's answer.
|
|
22
|
+
private static func entry(at source: URL) throws -> [String: Any]? {
|
|
23
|
+
let contents = try Data(contentsOf: source)
|
|
24
|
+
return (try? JSONSerialization.jsonObject(with: contents)) as? [String: Any]
|
|
25
|
+
}
|
|
26
|
+
|
|
16
27
|
static func get(_ key: String, prefix: String, storageDirectory: URL? = nil) throws -> Data? {
|
|
17
28
|
lock.lock(); defer { lock.unlock() }
|
|
18
29
|
let source = path(key, directory: storageDirectory ?? directory)
|
|
19
30
|
if FileManager.default.fileExists(atPath: source.path) {
|
|
20
|
-
guard let saved = try
|
|
31
|
+
guard let saved = try entry(at: source), saved["key"] as? String == key else {
|
|
21
32
|
throw CraftNativeActionError(code: "STORAGE_CORRUPT", message: "Invalid stored value")
|
|
22
33
|
}
|
|
23
34
|
if saved["deleted"] as? Bool == true { return nil }
|
|
@@ -26,7 +37,9 @@ enum CraftPersistentStorage {
|
|
|
26
37
|
}
|
|
27
38
|
guard let legacy = UserDefaults.standard.data(forKey: prefix + key) else { return nil }
|
|
28
39
|
// Only valid legacy JSON qualifies for deferred migration on I/O failure.
|
|
29
|
-
|
|
40
|
+
guard (try? JSONSerialization.jsonObject(with: legacy, options: .fragmentsAllowed)) != nil else {
|
|
41
|
+
throw CraftNativeActionError(code: "STORAGE_CORRUPT", message: "Stored value is not valid JSON")
|
|
42
|
+
}
|
|
30
43
|
// Reading a valid legacy checkpoint must survive a full disk. Migration
|
|
31
44
|
// can be retried on a later read; only a successful set promises a new
|
|
32
45
|
// durable checkpoint and permits the application to acknowledge it.
|
|
@@ -84,9 +97,11 @@ enum CraftPersistentStorage {
|
|
|
84
97
|
var result = Set(UserDefaults.standard.dictionaryRepresentation().keys.filter { $0.hasPrefix(prefix) }.map { String($0.dropFirst(prefix.count)) })
|
|
85
98
|
if FileManager.default.fileExists(atPath: directory.path) {
|
|
86
99
|
for source in try FileManager.default.contentsOfDirectory(at: directory, includingPropertiesForKeys: nil) where source.pathExtension == "json" {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
100
|
+
// A file that is not an entry names no key. It used to fail
|
|
101
|
+
// the whole listing, and `clear` with it, over a value
|
|
102
|
+
// neither call had been asked about. `get` of its key still
|
|
103
|
+
// answers STORAGE_CORRUPT.
|
|
104
|
+
guard let entry = try entry(at: source), let key = entry["key"] as? String else { continue }
|
|
90
105
|
if entry["deleted"] as? Bool == true { result.remove(key) } else { result.insert(key) }
|
|
91
106
|
}
|
|
92
107
|
}
|
|
@@ -95,5 +110,13 @@ enum CraftPersistentStorage {
|
|
|
95
110
|
static func clear(prefix: String) throws {
|
|
96
111
|
lock.lock(); defer { lock.unlock() }
|
|
97
112
|
for key in try keys(prefix: prefix) { try set(key, data: nil, prefix: prefix) }
|
|
113
|
+
// Clearing a key writes over its file, so what is still not an entry
|
|
114
|
+
// belongs to no key this store knows, and cannot be marked removed.
|
|
115
|
+
// It is deleted. No earlier value can come back in its place: every
|
|
116
|
+
// one this store mirrors was removed just above.
|
|
117
|
+
guard FileManager.default.fileExists(atPath: directory.path) else { return }
|
|
118
|
+
for source in try FileManager.default.contentsOfDirectory(at: directory, includingPropertiesForKeys: nil) where source.pathExtension == "json" {
|
|
119
|
+
if try entry(at: source)?["key"] as? String == nil { try FileManager.default.removeItem(at: source) }
|
|
120
|
+
}
|
|
98
121
|
}
|
|
99
122
|
}
|