craft-native 0.0.174 → 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 +998 -303
- 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 +410 -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
|
@@ -77,6 +77,25 @@ export interface CraftAndroidConfig {
|
|
|
77
77
|
enabled?: boolean;
|
|
78
78
|
minimumIntervalMinutes?: number;
|
|
79
79
|
};
|
|
80
|
+
/**
|
|
81
|
+
* Crash and error reporting to bughq (CraftCrashReporter.kt): uncaught JVM
|
|
82
|
+
* exceptions, ANRs and native crashes (ApplicationExitInfo), uncaught
|
|
83
|
+
* JavaScript errors, and what the page sends through `craft.crashReporter`.
|
|
84
|
+
* Queued on disk and sent when online. Release builds are minified: upload
|
|
85
|
+
* `app/build/outputs/mapping/release/mapping.txt` so bughq can retrace.
|
|
86
|
+
*/
|
|
87
|
+
crashReporting?: {
|
|
88
|
+
/** The bughq project's public ingest key. */
|
|
89
|
+
key: string;
|
|
90
|
+
/** Default https://bughq.org. */
|
|
91
|
+
host?: string;
|
|
92
|
+
/** Default `production`. */
|
|
93
|
+
environment?: string;
|
|
94
|
+
/** Default `<versionName>+<versionCode>`, e.g. `1.0.0+41`. */
|
|
95
|
+
release?: string;
|
|
96
|
+
/** One session per launch, for crash-free sessions. Default on. */
|
|
97
|
+
sessions?: boolean;
|
|
98
|
+
};
|
|
80
99
|
}
|
|
81
100
|
/** The shared health data types (the iOS builder's HealthKit names). */
|
|
82
101
|
export type HealthConnectType = 'steps' | 'heartRate' | 'activeEnergy' | 'distance' | 'workouts' | 'restingHeartRate' | 'heartRateVariability' | 'bodyMass' | 'sleep' | 'mindful' | 'dietaryEnergy' | 'dietaryProtein' | 'dietaryCarbohydrates' | 'dietaryFat';
|
|
@@ -79,6 +79,21 @@ function renderAndroidPromiseRuntime(indent = "") {
|
|
|
79
79
|
|
|
80
80
|
// ../android/src/index.ts
|
|
81
81
|
var TEMPLATES_DIR = join(dirname(import.meta.dir), "templates");
|
|
82
|
+
function resolveCraftAndroidVersion(startDir = import.meta.dir) {
|
|
83
|
+
let directory = resolve(startDir);
|
|
84
|
+
while (true) {
|
|
85
|
+
try {
|
|
86
|
+
const pkg = JSON.parse(readFileSync(join(directory, "package.json"), "utf8"));
|
|
87
|
+
if ((pkg.name === "@craft-native/android" || pkg.name === "craft-native") && typeof pkg.version === "string" && pkg.version)
|
|
88
|
+
return pkg.version;
|
|
89
|
+
} catch {}
|
|
90
|
+
const parent = dirname(directory);
|
|
91
|
+
if (parent === directory)
|
|
92
|
+
return "0.0.0";
|
|
93
|
+
directory = parent;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
var CRAFT_ANDROID_VERSION = resolveCraftAndroidVersion();
|
|
82
97
|
var LOCAL_DEVELOPMENT_HOSTS = new Set(["localhost", "127.0.0.1", "10.0.2.2"]);
|
|
83
98
|
var KOTLIN_KEYWORDS = new Set([
|
|
84
99
|
"as",
|
|
@@ -530,6 +545,7 @@ async function init(options) {
|
|
|
530
545
|
const mainActivityTemplate = readFileSync(join(TEMPLATES_DIR, config.renderer === "native" ? "MainActivityNative.kt.template" : "MainActivity.kt.template"), "utf-8");
|
|
531
546
|
const mainActivity = mainActivityTemplate.replace(/\{\{PACKAGE_NAME\}\}/g, finalPackageName).replace(/\{\{APP_NAME\}\}/g, name);
|
|
532
547
|
writeFileSync(join(output, "app/src/main/java", packagePath, "MainActivity.kt"), mainActivity);
|
|
548
|
+
writeFileSync(join(output, "app/src/main/java", packagePath, "CraftCrashReporter.kt"), readFileSync(join(TEMPLATES_DIR, "CraftCrashReporter.kt.template"), "utf-8").replace(/\{\{PACKAGE_NAME\}\}/g, finalPackageName).replace(/\{\{CRAFT_VERSION\}\}/g, CRAFT_ANDROID_VERSION));
|
|
533
549
|
if (config.renderer === "native") {
|
|
534
550
|
const mutationTemplate = readFileSync(join(TEMPLATES_DIR, "CraftNativeMutation.kt.template"), "utf-8");
|
|
535
551
|
const flatListTemplate = readFileSync(join(TEMPLATES_DIR, "CraftNativeFlatList.kt.template"), "utf-8");
|
|
@@ -573,7 +589,7 @@ async function init(options) {
|
|
|
573
589
|
const healthTemplate = readFileSync(join(TEMPLATES_DIR, config.enableHealthConnect ? "CraftHealthConnect.kt.template" : "CraftHealthConnectStub.kt.template"), "utf-8");
|
|
574
590
|
writeFileSync(join(output, "app/src/main/java", packagePath, "CraftHealthConnect.kt"), healthTemplate.replace(/\{\{PACKAGE_NAME\}\}/g, finalPackageName));
|
|
575
591
|
const manifestTemplate = readFileSync(join(TEMPLATES_DIR, "AndroidManifest.xml.template"), "utf-8");
|
|
576
|
-
const manifest = manifestTemplate.replace(/\{\{PACKAGE_NAME\}\}/g, finalPackageName).replace(/\{\{APP_NAME\}\}/g, name).replace(/\{\{PERMISSIONS\}\}/g, renderAndroidPermissions(config)).replace(/\{\{USES_CLEARTEXT\}\}/g, config.devServerURL?.startsWith("http://") || config.enableLocalNetwork ? "true" : "false").replace(/\{\{DEEP_LINK_INTENT_FILTERS\}\}/g, renderAndroidDeepLinks(config)).replace(/\{\{BACKGROUND_SERVICE\}\}/g, config.enableBackgroundLocation ? ' <service android:name="com.craft.runtime.LocationRecordingService" android:exported="false" android:foregroundServiceType="location" android:stopWithTask="false" />' : "").replace(/\{\{NATIVE_NOTIFICATION_RECEIVER\}\}/g, config.renderer === "native" ? renderNativeShellManifest(config) : "").replace(/\{\{GOOGLE_MAPS_API_KEY\}\}/g, config.mapsApiKey ? ` <meta-data android:name="com.google.android.geo.API_KEY" android:value="${escapeXml(config.mapsApiKey)}" />` : "").replace(/\{\{HEALTH_CONNECT_QUERIES\}\}/g, config.enableHealthConnect ? ` <queries>
|
|
592
|
+
const manifest = manifestTemplate.replace(/\{\{PACKAGE_NAME\}\}/g, finalPackageName).replace(/\{\{APP_NAME\}\}/g, name).replace(/\{\{PERMISSIONS\}\}/g, renderAndroidPermissions(config)).replace(/\{\{USES_CLEARTEXT\}\}/g, config.devServerURL?.startsWith("http://") || config.enableLocalNetwork ? "true" : "false").replace(/\{\{DEEP_LINK_INTENT_FILTERS\}\}/g, renderAndroidDeepLinks(config)).replace(/\{\{BACKGROUND_SERVICE\}\}/g, config.enableBackgroundLocation ? ' <service android:name="com.craft.runtime.LocationRecordingService" android:exported="false" android:foregroundServiceType="location" android:stopWithTask="false" />' : "").replace(/\{\{NATIVE_NOTIFICATION_RECEIVER\}\}/g, config.renderer === "native" ? renderNativeShellManifest(config) : "").replace(/\{\{CRASH_REPORTER_PROVIDER\}\}/g, ` <provider android:name=".CraftCrashReporterInit" android:authorities="\${applicationId}.craft-crash" android:exported="false" android:initOrder="1000" />`).replace(/\{\{GOOGLE_MAPS_API_KEY\}\}/g, config.mapsApiKey ? ` <meta-data android:name="com.google.android.geo.API_KEY" android:value="${escapeXml(config.mapsApiKey)}" />` : "").replace(/\{\{HEALTH_CONNECT_QUERIES\}\}/g, config.enableHealthConnect ? ` <queries>
|
|
577
593
|
<package android:name="com.google.android.apps.healthdata" />
|
|
578
594
|
</queries>` : "").replace(/\{\{HEALTH_CONNECT_RATIONALE\}\}/g, config.enableHealthConnect ? ` <intent-filter>
|
|
579
595
|
<action android:name="androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE" />
|
|
@@ -586,7 +602,8 @@ async function init(options) {
|
|
|
586
602
|
const projectGradleTemplate = readFileSync(join(TEMPLATES_DIR, "build.gradle.kts.project.template"), "utf-8");
|
|
587
603
|
writeFileSync(join(output, "build.gradle.kts"), projectGradleTemplate.replace(/\{\{GOOGLE_SERVICES_PLUGIN\}\}/g, hasGoogleServices ? ' id("com.google.gms.google-services") version "4.4.2" apply false' : ""));
|
|
588
604
|
const appGradleTemplate = readFileSync(join(TEMPLATES_DIR, "build.gradle.kts.app.template"), "utf-8");
|
|
589
|
-
const appGradle = appGradleTemplate.replace(/\{\{PACKAGE_NAME\}\}/g, finalPackageName).replace(/\{\{VERSION_NAME\}\}/g, escapeKotlinString(config.version || "1.0.0")).replace(/\{\{VERSION_CODE\}\}/g, String(config.versionCode || 1)).replace(/\{\{MIN_SDK\}\}/g, String(config.minSdk ?? ANDROID_MIN_SDK)).replace(/\{\{COMPILE_SDK\}\}/g, String(config.compileSdk || 36)).replace(/\{\{TARGET_SDK\}\}/g, String(config.targetSdk || 35)).replace(/\{\{GOOGLE_SERVICES_PLUGIN\}\}/g, hasGoogleServices ? ' id("com.google.gms.google-services")' : "").replace(/\{\{FIREBASE_MESSAGING_DEPENDENCY\}\}/g, config.enablePushNotifications ? ' implementation("com.google.firebase:firebase-messaging:24.1.0")' : "").replace(/\{\{REFRESH_LAYOUT_DEPENDENCY\}\}/g, config.renderer === "native" ? ' implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")' : "").replace(/\{\{
|
|
605
|
+
const appGradle = appGradleTemplate.replace(/\{\{PACKAGE_NAME\}\}/g, finalPackageName).replace(/\{\{VERSION_NAME\}\}/g, escapeKotlinString(config.version || "1.0.0")).replace(/\{\{VERSION_CODE\}\}/g, String(config.versionCode || 1)).replace(/\{\{MIN_SDK\}\}/g, String(config.minSdk ?? ANDROID_MIN_SDK)).replace(/\{\{COMPILE_SDK\}\}/g, String(config.compileSdk || 36)).replace(/\{\{TARGET_SDK\}\}/g, String(config.targetSdk || 35)).replace(/\{\{GOOGLE_SERVICES_PLUGIN\}\}/g, hasGoogleServices ? ' id("com.google.gms.google-services")' : "").replace(/\{\{FIREBASE_MESSAGING_DEPENDENCY\}\}/g, config.enablePushNotifications ? ' implementation("com.google.firebase:firebase-messaging:24.1.0")' : "").replace(/\{\{REFRESH_LAYOUT_DEPENDENCY\}\}/g, config.renderer === "native" ? ' implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")' : "").replace(/\{\{GOOGLE_MAPS_DEPENDENCY\}\}/g, config.renderer === "native" ? ` // Native STX Map overlays and trail routes
|
|
606
|
+
implementation("com.google.android.gms:play-services-maps:18.2.0")` : "").replace(/\{\{HEALTH_CONNECT_DEPENDENCIES\}\}/g, config.enableHealthConnect ? ` implementation("androidx.health.connect:connect-client:1.1.0")
|
|
590
607
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1")` : "").replace(/\{\{JAVASCRIPT_ENGINE_DEPENDENCY\}\}/g, config.renderer === "native" ? ` implementation("androidx.javascriptengine:javascriptengine:1.1.0")
|
|
591
608
|
// The in-app browser (Custom Tabs) and background refresh (WorkManager).
|
|
592
609
|
implementation("androidx.browser:browser:1.8.0")
|
|
@@ -418,6 +418,7 @@ class CraftBridge(
|
|
|
418
418
|
appReview: true,
|
|
419
419
|
flashlight: {{ENABLE_CAMERA}} && ${hasFlashlight()},
|
|
420
420
|
speech: true,
|
|
421
|
+
crashReporter: ${CraftCrashReporter.enabled},
|
|
421
422
|
brightness: true,
|
|
422
423
|
openURL: true,
|
|
423
424
|
vibrationPattern: true,
|
|
@@ -1748,6 +1749,26 @@ class CraftBridge(
|
|
|
1748
1749
|
return Promise.resolve().then(function() { CraftAndroid.stopSpeaking(); });
|
|
1749
1750
|
}
|
|
1750
1751
|
};
|
|
1752
|
+
// Crash and error reporting (CraftCrashReporter.kt): the page's
|
|
1753
|
+
// error SDK hands its events here, so they share the app's
|
|
1754
|
+
// on-disk outbox, its session and the device's own context.
|
|
1755
|
+
craft.crashReporter = {
|
|
1756
|
+
capture: function(event) {
|
|
1757
|
+
return Promise.resolve().then(function() { return CraftAndroid.crashReporterCapture(JSON.stringify(event || {})); });
|
|
1758
|
+
},
|
|
1759
|
+
setUser: function(id) {
|
|
1760
|
+
return Promise.resolve().then(function() { return CraftAndroid.crashReporterSetUser(id == null ? '' : String(id)); });
|
|
1761
|
+
},
|
|
1762
|
+
addBreadcrumb: function(breadcrumb) {
|
|
1763
|
+
return Promise.resolve().then(function() { return CraftAndroid.crashReporterAddBreadcrumb(JSON.stringify(breadcrumb || {})); });
|
|
1764
|
+
},
|
|
1765
|
+
flush: function() {
|
|
1766
|
+
return Promise.resolve().then(function() { return CraftAndroid.crashReporterFlush(); });
|
|
1767
|
+
},
|
|
1768
|
+
context: function() {
|
|
1769
|
+
return Promise.resolve().then(function() { return JSON.parse(CraftAndroid.crashReporterContext()); });
|
|
1770
|
+
}
|
|
1771
|
+
};
|
|
1751
1772
|
craft.location = {
|
|
1752
1773
|
getCurrentPosition: craft.getCurrentPosition,
|
|
1753
1774
|
watchPosition: craft.watchPosition,
|
|
@@ -1859,6 +1880,36 @@ class CraftBridge(
|
|
|
1859
1880
|
return true
|
|
1860
1881
|
}
|
|
1861
1882
|
|
|
1883
|
+
// ==================== Crash reporting ====================
|
|
1884
|
+
|
|
1885
|
+
/** Queues an event the page built; false when reporting is off or the event has no message. */
|
|
1886
|
+
@JavascriptInterface
|
|
1887
|
+
fun crashReporterCapture(eventJson: String): Boolean =
|
|
1888
|
+
runCatching { CraftCrashReporter.capture(JSONObject(eventJson)) }.getOrDefault(false)
|
|
1889
|
+
|
|
1890
|
+
@JavascriptInterface
|
|
1891
|
+
fun crashReporterSetUser(id: String): Boolean {
|
|
1892
|
+
CraftCrashReporter.setUser(id)
|
|
1893
|
+
return CraftCrashReporter.enabled
|
|
1894
|
+
}
|
|
1895
|
+
|
|
1896
|
+
@JavascriptInterface
|
|
1897
|
+
fun crashReporterAddBreadcrumb(breadcrumbJson: String): Boolean {
|
|
1898
|
+
val crumb = runCatching { JSONObject(breadcrumbJson) }.getOrNull() ?: return false
|
|
1899
|
+
CraftCrashReporter.addBreadcrumb(crumb)
|
|
1900
|
+
return CraftCrashReporter.enabled
|
|
1901
|
+
}
|
|
1902
|
+
|
|
1903
|
+
@JavascriptInterface
|
|
1904
|
+
fun crashReporterFlush(): Boolean {
|
|
1905
|
+
CraftCrashReporter.requestFlush()
|
|
1906
|
+
return CraftCrashReporter.enabled
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1909
|
+
@JavascriptInterface
|
|
1910
|
+
fun crashReporterContext(): String =
|
|
1911
|
+
if (CraftCrashReporter.enabled) CraftCrashReporter.context().toString() else JSONObject().put("enabled", false).toString()
|
|
1912
|
+
|
|
1862
1913
|
// ==================== Speech Recognition ====================
|
|
1863
1914
|
|
|
1864
1915
|
/**
|