craft-native 0.0.178 → 0.0.179

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 CHANGED
@@ -3303,7 +3303,7 @@ function pinCraftNativeDependency(packagePath, craftVersion) {
3303
3303
  `);
3304
3304
  }
3305
3305
  // package.json
3306
- var version = "0.0.178";
3306
+ var version = "0.0.179";
3307
3307
 
3308
3308
  // bin/cli.ts
3309
3309
  var spawnedFrom = process6.env[CRAFT_CLI_SPAWN_MARKER];
package/dist/index.cjs CHANGED
@@ -310,7 +310,7 @@ function craftBinaryNotFoundMessage(triedPath) {
310
310
  `);
311
311
  }
312
312
  // package.json
313
- var version = "0.0.178";
313
+ var version = "0.0.179";
314
314
 
315
315
  // src/sdk-version.ts
316
316
  var SDK_VERSION = version;
package/dist/index.js CHANGED
@@ -58,7 +58,7 @@ function craftBinaryNotFoundMessage(triedPath) {
58
58
  `);
59
59
  }
60
60
  // package.json
61
- var version = "0.0.178";
61
+ var version = "0.0.179";
62
62
 
63
63
  // src/sdk-version.ts
64
64
  var SDK_VERSION = version;
@@ -189,7 +189,7 @@ private func craftCollectFrames(_ context: UnsafeMutableRawPointer?) -> Int {
189
189
  let returnAddress = craftStrip(record.1)
190
190
  // In a leaf function the caller is only in the link register: the
191
191
  // first saved return address then belongs to the caller's caller.
192
- if first, lr != 0, craftStrip(lr) != returnAddress, count < craftCrashMaxFrames {
192
+ if first, lr != 0, craftStrip(lr) != returnAddress, craftStrip(lr) != frames[0], count < craftCrashMaxFrames {
193
193
  frames[count] = craftStrip(lr); count += 1
194
194
  }
195
195
  first = false
@@ -478,7 +478,13 @@ final class CraftCrashReporter: NSObject, MXMetricManagerSubscriber {
478
478
  let frames = addresses.map { frame(for: $0, in: images) }
479
479
  let signalName = Self.signalName(signal)
480
480
  let type = exceptionName ?? signalName
481
- var message = messages.first(where: { $0.contains("Fatal error") || $0.contains("fatal error") }) ?? messages.first ?? ""
481
+ // A runtime's own words when it trapped on purpose (Swift's "Fatal
482
+ // error: …", an assertion); an abort's first annotation otherwise.
483
+ // Never the simulator's device banner, which some image's
484
+ // __crash_info carries on every simulator process.
485
+ let deliberate = messages.first(where: { $0.localizedCaseInsensitiveContains("fatal error") || $0.contains("Assertion failed") || $0.contains("Precondition failed") })
486
+ let annotation = signal == SIGABRT ? messages.first(where: { !$0.hasPrefix("CoreSimulator") }) : nil
487
+ var message: String = deliberate ?? annotation ?? ""
482
488
  if message.isEmpty {
483
489
  message = Self.signalDescription(signal, code: code, address: faultAddress)
484
490
  }
@@ -497,7 +503,8 @@ final class CraftCrashReporter: NSObject, MXMetricManagerSubscriber {
497
503
  event["timestamp"] = Self.iso(crashedAt)
498
504
  event["exception"] = exception
499
505
  event["breadcrumbs"] = Array(previousCrumbs.suffix(50))
500
- event["tags"] = ["mechanism": exception["mechanism"] as? String ?? "signal"]
506
+ let mechanism: String = exception["mechanism"] as? String ?? "signal"
507
+ event["tags"] = ["mechanism": mechanism]
501
508
  if let version = previousSession["version"] as? String, let build = previousSession["build"] as? String {
502
509
  var contexts = event["contexts"] as? [String: Any] ?? [:]
503
510
  var app = contexts["app"] as? [String: Any] ?? [:]
@@ -645,7 +652,8 @@ final class CraftCrashReporter: NSObject, MXMetricManagerSubscriber {
645
652
  if let last = self.lastJavaScriptError, last.key == key, Date().timeIntervalSince(last.at) < 5 { return }
646
653
  self.lastJavaScriptError = (key, Date())
647
654
  var event = self.baseEvent(release: self.release, sessionId: self.sessionId, sessionStart: Self.iso(self.sessionStart))
648
- event["type"] = (name?.isEmpty == false ? name : nil) ?? "Error"
655
+ let type: String = name.flatMap { $0.isEmpty ? nil : $0 } ?? "Error"
656
+ event["type"] = type
649
657
  event["message"] = message
650
658
  if !stack.isEmpty, stack != "undefined" { event["stack"] = stack }
651
659
  event["level"] = "error"
@@ -757,6 +765,11 @@ final class CraftCrashReporter: NSObject, MXMetricManagerSubscriber {
757
765
 
758
766
  private func baseEvent(release: String, sessionId: String?, sessionStart: String?) -> [String: Any] {
759
767
  let device = UIDevice.current
768
+ let info = Bundle.main.infoDictionary
769
+ let appName: String = info?["CFBundleDisplayName"] as? String ?? info?["CFBundleName"] as? String ?? ""
770
+ // Typed, not inline: against `Any`, `Locale.current.identifier` picks
771
+ // iOS 16's `identifier(_:)` method and stores a function.
772
+ let locale: String = Locale.current.identifier
760
773
  var event: [String: Any] = [
761
774
  "platform": "ios",
762
775
  "release": release,
@@ -768,7 +781,7 @@ final class CraftCrashReporter: NSObject, MXMetricManagerSubscriber {
768
781
  "contexts": [
769
782
  "app": [
770
783
  "id": Bundle.main.bundleIdentifier ?? "",
771
- "name": Bundle.main.infoDictionary?["CFBundleDisplayName"] as? String ?? (Bundle.main.infoDictionary?["CFBundleName"] as? String ?? ""),
784
+ "name": appName,
772
785
  "version": appVersion,
773
786
  "build": appBuild,
774
787
  ],
@@ -776,7 +789,7 @@ final class CraftCrashReporter: NSObject, MXMetricManagerSubscriber {
776
789
  "model": Self.deviceModel(),
777
790
  "family": device.model,
778
791
  "simulator": Self.isSimulator,
779
- "locale": Locale.current.identifier,
792
+ "locale": locale,
780
793
  ],
781
794
  "os": ["name": "iOS", "version": device.systemVersion],
782
795
  ],
@@ -789,8 +802,16 @@ final class CraftCrashReporter: NSObject, MXMetricManagerSubscriber {
789
802
  private var craftVersion: String { "{{CRAFT_VERSION}}" }
790
803
 
791
804
  private func enqueue(_ endpoint: String, _ body: [String: Any]) {
792
- guard let outbox, JSONSerialization.isValidJSONObject(["endpoint": endpoint, "body": body]),
793
- let data = try? JSONSerialization.data(withJSONObject: ["endpoint": endpoint, "body": body]) else { return }
805
+ // Through jsonSafe first: a `[String: Any]` built in Swift can hold an
806
+ // Optional (a `??` inferred against `Any`), and a page's event can hold
807
+ // anything at all. Either made the whole event invalid JSON, and it was
808
+ // dropped without a word.
809
+ let item: [String: Any] = ["endpoint": endpoint, "body": Self.jsonSafe(body)]
810
+ guard let outbox, JSONSerialization.isValidJSONObject(item),
811
+ let data = try? JSONSerialization.data(withJSONObject: item) else {
812
+ CraftNativeConsole.write("error", category: "crash-reporter", message: "dropped an \(endpoint) event that is not JSON")
813
+ return
814
+ }
794
815
  let name = String(format: "%013.0f-%@.json", Date().timeIntervalSince1970 * 1000, UUID().uuidString)
795
816
  try? data.write(to: outbox.appendingPathComponent(name), options: .atomic)
796
817
  flush()
@@ -893,6 +914,49 @@ final class CraftCrashReporter: NSObject, MXMetricManagerSubscriber {
893
914
 
894
915
  // MARK: Helpers
895
916
 
917
+ /// `value` as something JSONSerialization accepts: Optionals unwrapped
918
+ /// (nil is null), integers of every width as numbers, dates as ISO
919
+ /// strings, and anything else as its description.
920
+ static func jsonSafe(_ value: Any) -> Any {
921
+ let mirror = Mirror(reflecting: value)
922
+ if mirror.displayStyle == .optional {
923
+ guard let wrapped = mirror.children.first?.value else { return NSNull() }
924
+ return jsonSafe(wrapped)
925
+ }
926
+ switch value {
927
+ case let dictionary as [String: Any]:
928
+ return dictionary.mapValues { jsonSafe($0) }
929
+ case let dictionary as NSDictionary:
930
+ var out: [String: Any] = [:]
931
+ for (key, item) in dictionary { out[String(describing: key)] = jsonSafe(item) }
932
+ return out
933
+ case let array as [Any]:
934
+ return array.map { jsonSafe($0) }
935
+ case is String, is NSString, is NSNull:
936
+ return value
937
+ case let flag as Bool:
938
+ return flag
939
+ case let number as NSNumber:
940
+ return number
941
+ case let number as UInt64:
942
+ return NSNumber(value: number)
943
+ case let number as Int64:
944
+ return NSNumber(value: number)
945
+ case let number as UInt32:
946
+ return NSNumber(value: number)
947
+ case let number as Int32:
948
+ return NSNumber(value: number)
949
+ case let number as Double:
950
+ return number.isFinite ? NSNumber(value: number) : NSNull()
951
+ case let date as Date:
952
+ return iso(date)
953
+ case let url as URL:
954
+ return url.absoluteString
955
+ default:
956
+ return String(describing: value)
957
+ }
958
+ }
959
+
896
960
  private func write(_ object: [String: Any], to name: String) {
897
961
  if let data = try? JSONSerialization.data(withJSONObject: object) {
898
962
  try? data.write(to: directory.appendingPathComponent(name), options: .atomic)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "craft-native",
3
- "version": "0.0.178",
3
+ "version": "0.0.179",
4
4
  "type": "module",
5
5
  "description": "Build desktop apps with web languages - TypeScript SDK for Craft",
6
6
  "author": "Chris Breuer <chris@stacksjs.org>",