craft-native 0.0.175 → 0.0.176
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/src/index.d.ts +19 -0
- package/dist/android/src/index.js +19 -2
- package/dist/android/templates/AndroidManifest.xml.template +1 -0
- package/dist/android/templates/CraftBridge.kt.template +51 -0
- package/dist/android/templates/CraftCrashReporter.kt.template +544 -0
- package/dist/android/templates/CraftNative.kt.template +17 -0
- package/dist/android/templates/CraftNativeFlatList.kt.template +20 -9
- package/dist/android/templates/CraftNativeMutation.kt.template +4 -1
- package/dist/android/templates/CraftNativeServices.kt.template +31 -1
- package/dist/android/templates/MainActivityNative.kt.template +986 -304
- package/dist/android/templates/build.gradle.kts.app.template +4 -2
- package/dist/api/mobile.d.ts +31 -0
- package/dist/cli.js +1 -1
- package/dist/components/index.d.ts +203 -2
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/ios/src/index.d.ts +19 -0
- package/dist/ios/src/index.js +7 -1
- package/dist/ios/templates/CraftApp.swift +51 -1
- package/dist/ios/templates/CraftCrashReporter.swift +1010 -0
- package/dist/ios/templates/CraftHybrid.swift +10 -0
- package/dist/ios/templates/CraftNativeActions.swift +49 -1
- package/dist/ios/templates/CraftNativeFlatList.swift +13 -21
- package/dist/ios/templates/CraftNativeMutation.swift +1 -1
- package/dist/ios/templates/CraftNativeScreen.swift +393 -55
- package/dist/ios/templates/project.yml.template +1 -0
- package/dist/macos/templates/CraftNativeMutation.swift +1 -1
- package/dist/macos/templates/native_layout.zig +389 -17
- package/dist/mobile.js +66 -0
- package/package.json +1 -1
|
@@ -421,6 +421,16 @@ final class CraftHybrid: NSObject {
|
|
|
421
421
|
#endif
|
|
422
422
|
}
|
|
423
423
|
|
|
424
|
+
#if DEBUG
|
|
425
|
+
/// The crash reporter's debug-only test (`-CraftCrashTest js`): an
|
|
426
|
+
/// uncaught error thrown in the app script, as a real one would be.
|
|
427
|
+
func debugThrowInAppScript(_ message: String) {
|
|
428
|
+
if appScript == nil { startAppScript() }
|
|
429
|
+
let literal = (try? JSONSerialization.data(withJSONObject: [message])).flatMap { String(data: $0, encoding: .utf8) } ?? "[\"test\"]"
|
|
430
|
+
appScript?.evaluateScript("(function crashReporterTest() { throw new TypeError(\(literal)[0]); })()")
|
|
431
|
+
}
|
|
432
|
+
#endif
|
|
433
|
+
|
|
424
434
|
/// The shell is up: what waited for it.
|
|
425
435
|
func shellReady() {
|
|
426
436
|
guard let controller else { return }
|
|
@@ -82,6 +82,8 @@ enum CraftNativeActions {
|
|
|
82
82
|
// which the native Video element plays in place of the address.
|
|
83
83
|
values.append("media")
|
|
84
84
|
if config.enableCamera { values.append("camera") }
|
|
85
|
+
// craft.crashReporter: the app's crash and error reporter (bughq).
|
|
86
|
+
if config.crashReporting?.key.isEmpty == false { values.append("crashReporter") }
|
|
85
87
|
return values
|
|
86
88
|
}
|
|
87
89
|
|
|
@@ -108,6 +110,32 @@ enum CraftNativeActions {
|
|
|
108
110
|
case "clipboardRead":
|
|
109
111
|
guard config.enableClipboard else { return failure("CAPABILITY_DISABLED", "Clipboard is disabled") }
|
|
110
112
|
return .success(UIPasteboard.general.string ?? "")
|
|
113
|
+
// Crash reporting (CraftCrashReporter.swift): on when the config names
|
|
114
|
+
// a bughq key. Each answers at once; delivery is the outbox's job.
|
|
115
|
+
case "crashReporterCapture":
|
|
116
|
+
guard CraftCrashReporter.shared.enabled else { return failure("CAPABILITY_DISABLED", "Crash reporting is not configured") }
|
|
117
|
+
guard let event = body["event"] as? [String: Any] else {
|
|
118
|
+
return failure("INVALID_ARGUMENT", "crashReporterCapture was called without the values it needs")
|
|
119
|
+
}
|
|
120
|
+
CraftCrashReporter.shared.capture(event)
|
|
121
|
+
return .success(true)
|
|
122
|
+
case "crashReporterSetUser":
|
|
123
|
+
guard CraftCrashReporter.shared.enabled else { return failure("CAPABILITY_DISABLED", "Crash reporting is not configured") }
|
|
124
|
+
CraftCrashReporter.shared.setUser(body["id"] as? String)
|
|
125
|
+
return .success(true)
|
|
126
|
+
case "crashReporterAddBreadcrumb":
|
|
127
|
+
guard CraftCrashReporter.shared.enabled else { return failure("CAPABILITY_DISABLED", "Crash reporting is not configured") }
|
|
128
|
+
guard let crumb = body["breadcrumb"] as? [String: Any] else {
|
|
129
|
+
return failure("INVALID_ARGUMENT", "crashReporterAddBreadcrumb was called without the values it needs")
|
|
130
|
+
}
|
|
131
|
+
CraftCrashReporter.shared.addBreadcrumb(crumb)
|
|
132
|
+
return .success(true)
|
|
133
|
+
case "crashReporterFlush":
|
|
134
|
+
guard CraftCrashReporter.shared.enabled else { return failure("CAPABILITY_DISABLED", "Crash reporting is not configured") }
|
|
135
|
+
CraftCrashReporter.shared.requestFlush()
|
|
136
|
+
return .success(true)
|
|
137
|
+
case "crashReporterContext":
|
|
138
|
+
return .success(CraftCrashReporter.shared.context())
|
|
111
139
|
default:
|
|
112
140
|
return nil
|
|
113
141
|
}
|
|
@@ -134,6 +162,16 @@ enum CraftNativeActions {
|
|
|
134
162
|
switch (module, method) {
|
|
135
163
|
case ("Device", "getInfo"):
|
|
136
164
|
sync = perform(action: "getDeviceInfo", body: [:], config: config)
|
|
165
|
+
case ("CrashReporter", "capture"):
|
|
166
|
+
sync = perform(action: "crashReporterCapture", body: ["event": args.first ?? NSNull()], config: config)
|
|
167
|
+
case ("CrashReporter", "setUser"):
|
|
168
|
+
sync = perform(action: "crashReporterSetUser", body: ["id": args.first ?? NSNull()], config: config)
|
|
169
|
+
case ("CrashReporter", "addBreadcrumb"):
|
|
170
|
+
sync = perform(action: "crashReporterAddBreadcrumb", body: ["breadcrumb": args.first ?? NSNull()], config: config)
|
|
171
|
+
case ("CrashReporter", "flush"):
|
|
172
|
+
sync = perform(action: "crashReporterFlush", body: [:], config: config)
|
|
173
|
+
case ("CrashReporter", "context"):
|
|
174
|
+
sync = perform(action: "crashReporterContext", body: [:], config: config)
|
|
137
175
|
case ("Haptics", "impact"):
|
|
138
176
|
sync = perform(action: "haptic", body: ["style": args.first ?? NSNull()], config: config)
|
|
139
177
|
case ("Haptics", "notification"):
|
|
@@ -575,9 +613,15 @@ enum CraftNativeActions {
|
|
|
575
613
|
cancellationLock.unlock()
|
|
576
614
|
if let error = error {
|
|
577
615
|
let code = (error as? URLError)?.code == .timedOut ? "TIMEOUT" : "NETWORK_ERROR"
|
|
616
|
+
if (error as? URLError)?.code != .cancelled {
|
|
617
|
+
CraftCrashReporter.shared.requestFailed(method: method, url: url, status: nil, error: code)
|
|
618
|
+
}
|
|
578
619
|
complete(requestToken, failure(code, error.localizedDescription), completion)
|
|
579
620
|
return
|
|
580
621
|
}
|
|
622
|
+
if let http = response as? HTTPURLResponse, http.statusCode >= 400 {
|
|
623
|
+
CraftCrashReporter.shared.requestFailed(method: method, url: url, status: http.statusCode, error: nil)
|
|
624
|
+
}
|
|
581
625
|
guard let http = response as? HTTPURLResponse else {
|
|
582
626
|
complete(requestToken, failure("NETWORK_ERROR", "fetch received a response that is not HTTP"), completion)
|
|
583
627
|
return
|
|
@@ -1236,13 +1280,17 @@ final class CraftNativePasskeys: NSObject, ASAuthorizationControllerDelegate, AS
|
|
|
1236
1280
|
guard let request = pending.removeValue(forKey: key) else { return }
|
|
1237
1281
|
controllers.removeValue(forKey: request.requestToken)
|
|
1238
1282
|
if let registration = authorization.credential as? ASAuthorizationPlatformPublicKeyCredentialRegistration {
|
|
1283
|
+
guard let attestationObject = registration.rawAttestationObject else {
|
|
1284
|
+
request.completion(.failure(CraftNativeActionError(code: "PASSKEY_FAILED", message: "Apple returned a registration without an attestation object")))
|
|
1285
|
+
return
|
|
1286
|
+
}
|
|
1239
1287
|
request.completion(.success([
|
|
1240
1288
|
"id": Self.base64url(registration.credentialID),
|
|
1241
1289
|
"rawId": Self.base64url(registration.credentialID),
|
|
1242
1290
|
"type": "public-key",
|
|
1243
1291
|
"response": [
|
|
1244
1292
|
"clientDataJSON": Self.base64url(registration.rawClientDataJSON),
|
|
1245
|
-
"attestationObject": Self.base64url(
|
|
1293
|
+
"attestationObject": Self.base64url(attestationObject),
|
|
1246
1294
|
],
|
|
1247
1295
|
]))
|
|
1248
1296
|
return
|
|
@@ -219,15 +219,20 @@ final class CraftNativeFlatList: UICollectionView, UICollectionViewDelegateFlowL
|
|
|
219
219
|
}
|
|
220
220
|
|
|
221
221
|
func setContentContainerStyle(_ raw: [String: Any]?) {
|
|
222
|
-
func
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
222
|
+
func nonNegative(_ value: Any?) -> CGFloat? {
|
|
223
|
+
guard let number = value as? NSNumber else { return nil }
|
|
224
|
+
let points = CGFloat(truncating: number)
|
|
225
|
+
guard points.isFinite, points >= 0 else { return nil }
|
|
226
|
+
return points
|
|
227
|
+
}
|
|
228
|
+
let padding = nonNegative(raw?["padding"]) ?? 0
|
|
229
|
+
let horizontal = nonNegative(raw?["paddingHorizontal"]) ?? padding
|
|
230
|
+
let vertical = nonNegative(raw?["paddingVertical"]) ?? padding
|
|
226
231
|
contentInset = UIEdgeInsets(
|
|
227
|
-
top:
|
|
228
|
-
left:
|
|
229
|
-
bottom:
|
|
230
|
-
right:
|
|
232
|
+
top: nonNegative(raw?["paddingTop"]) ?? vertical,
|
|
233
|
+
left: nonNegative(raw?["paddingLeft"]) ?? horizontal,
|
|
234
|
+
bottom: nonNegative(raw?["paddingBottom"]) ?? vertical,
|
|
235
|
+
right: nonNegative(raw?["paddingRight"]) ?? horizontal
|
|
231
236
|
)
|
|
232
237
|
scrollIndicatorInsets = contentInset
|
|
233
238
|
collectionViewLayout.invalidateLayout()
|
|
@@ -443,19 +448,6 @@ final class CraftNativeFlatList: UICollectionView, UICollectionViewDelegateFlowL
|
|
|
443
448
|
onMomentumScrollEnd?(scrollView)
|
|
444
449
|
}
|
|
445
450
|
|
|
446
|
-
func collectionView(
|
|
447
|
-
_ collectionView: UICollectionView,
|
|
448
|
-
layout collectionViewLayout: UICollectionViewLayout,
|
|
449
|
-
sizeForItemAt indexPath: IndexPath
|
|
450
|
-
) -> CGSize {
|
|
451
|
-
guard items.indices.contains(indexPath.item) else { return CGSize(width: bounds.width, height: 44) }
|
|
452
|
-
let item = items[indexPath.item]
|
|
453
|
-
if flowLayout.scrollDirection == .horizontal {
|
|
454
|
-
return CGSize(width: 44, height: itemExtent(isChrome: item.isChrome))
|
|
455
|
-
}
|
|
456
|
-
return CGSize(width: itemExtent(isChrome: item.isChrome), height: 44)
|
|
457
|
-
}
|
|
458
|
-
|
|
459
451
|
private func evaluateEndReached() {
|
|
460
452
|
guard !isApplyingSnapshot, pendingApply == nil,
|
|
461
453
|
let endReached = endReached,
|
|
@@ -305,7 +305,7 @@ final class CraftNativeMutationDocument {
|
|
|
305
305
|
static let containerTypes: Set<String> = [
|
|
306
306
|
"View", "SafeAreaView", "ScrollView", "KeyboardAvoidingView", "Modal", "Pressable",
|
|
307
307
|
"TouchableOpacity", "TouchableHighlight", "ImageBackground", "FlatList", "SectionList",
|
|
308
|
-
"Fragment", "Svg", "G",
|
|
308
|
+
"Fragment", "Svg", "G", "Map",
|
|
309
309
|
]
|
|
310
310
|
private static let containerTypeKeys = Set(containerTypes.map { $0.lowercased() })
|
|
311
311
|
|