craft-native 0.0.236 → 0.0.238
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/README.md +71 -0
- package/dist/android/src/emulator.d.ts +23 -0
- package/dist/android/src/index.d.ts +7 -1
- package/dist/android/src/index.js +280 -98
- package/dist/android/templates/CraftBridge.kt.template +65 -54
- package/dist/android/templates/CraftNative.kt.template +27 -0
- package/dist/android/templates/CraftNativeCapabilities.kt.template +55 -6
- package/dist/android/templates/CraftNativeServices.kt.template +45 -35
- package/dist/android/templates/CraftNativeShell.kt.template +103 -20
- package/dist/android/templates/LocationRecordingService.kt.template +252 -46
- package/dist/android/templates/MainActivityNative.kt.template +163 -5
- package/dist/android-adb.d.ts +2 -0
- package/dist/api/index.d.ts +1 -1
- package/dist/api/mobile.d.ts +49 -14
- package/dist/browser/storage.d.ts +3 -0
- package/dist/browser.js +163 -51
- package/dist/cli.js +1 -1
- package/dist/components/index.d.ts +33 -0
- package/dist/index.cjs +126 -12
- package/dist/index.js +126 -12
- package/dist/ios/src/app-shortcuts.d.ts +11 -0
- package/dist/ios/src/index.d.ts +7 -0
- package/dist/ios/src/index.js +65 -1
- package/dist/ios/src/testing.d.ts +2 -0
- package/dist/ios/templates/CraftApp.swift +392 -88
- package/dist/ios/templates/CraftHeartRate.swift +141 -44
- package/dist/ios/templates/CraftNativeActions.swift +46 -48
- package/dist/ios/templates/CraftNativeScreen.swift +177 -10
- package/dist/ios/templates/CraftPersistentStorage.swift +99 -0
- package/dist/ios/templates/CraftRecordingArchive.swift +85 -0
- package/dist/ios/templates/testing/DriverTests.swift.template +8 -1
- package/dist/mobile-testing.js +30 -4
- package/dist/mobile.js +169 -17
- package/package.json +1 -1
package/dist/mobile.js
CHANGED
|
@@ -355,6 +355,107 @@ var secureStorage = {
|
|
|
355
355
|
Object.keys(localStorage).filter((k) => k.startsWith("secure_")).forEach((k) => localStorage.removeItem(k));
|
|
356
356
|
}
|
|
357
357
|
};
|
|
358
|
+
function storageHost(method) {
|
|
359
|
+
const host = getCraftRoot()?.storage;
|
|
360
|
+
if (typeof host?.[method] !== "function")
|
|
361
|
+
throw Object.assign(new Error("This host does not provide native storage"), { code: "STORAGE_UNSUPPORTED" });
|
|
362
|
+
return host;
|
|
363
|
+
}
|
|
364
|
+
var crypto = {
|
|
365
|
+
async randomUUID() {
|
|
366
|
+
const root = getCraftRoot();
|
|
367
|
+
if (!root)
|
|
368
|
+
return secureUUID();
|
|
369
|
+
const caps = root.capabilities;
|
|
370
|
+
const advertised = Array.isArray(caps) ? caps.includes("crypto") : caps?.crypto === true;
|
|
371
|
+
if (!advertised || typeof root.crypto?.randomUUID !== "function")
|
|
372
|
+
throw Object.assign(new Error("This host does not provide secure UUIDs"), { code: "CRYPTO_UNSUPPORTED" });
|
|
373
|
+
const id = await root.crypto.randomUUID();
|
|
374
|
+
if (typeof id !== "string" || !/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(id))
|
|
375
|
+
throw Object.assign(new Error("The host returned an invalid secure UUID"), { code: "CRYPTO_INVALID_RESPONSE" });
|
|
376
|
+
return id;
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
var storage = {
|
|
380
|
+
supportsSetIfAbsent() {
|
|
381
|
+
const root = getCraftRoot();
|
|
382
|
+
const caps = root?.capabilities;
|
|
383
|
+
const supported = Array.isArray(caps) ? caps.includes("storageSetIfAbsent") : caps?.storageSetIfAbsent === true;
|
|
384
|
+
return supported && typeof root?.storage?.setIfAbsent === "function";
|
|
385
|
+
},
|
|
386
|
+
supportsCompareAndSet() {
|
|
387
|
+
const root = getCraftRoot();
|
|
388
|
+
const caps = root?.capabilities;
|
|
389
|
+
const supported = Array.isArray(caps) ? caps.includes("storageCompareAndSet") : caps?.storageCompareAndSet === true;
|
|
390
|
+
return supported && typeof root?.storage?.compareAndSet === "function";
|
|
391
|
+
},
|
|
392
|
+
async get(key) {
|
|
393
|
+
return storageHost("get").get(key);
|
|
394
|
+
},
|
|
395
|
+
async set(key, value) {
|
|
396
|
+
return storageHost("set").set(key, value);
|
|
397
|
+
},
|
|
398
|
+
async remove(key) {
|
|
399
|
+
return storageHost("remove").remove(key);
|
|
400
|
+
},
|
|
401
|
+
async clear() {
|
|
402
|
+
return storageHost("clear").clear();
|
|
403
|
+
},
|
|
404
|
+
async keys() {
|
|
405
|
+
return storageHost("keys").keys();
|
|
406
|
+
},
|
|
407
|
+
async setIfAbsent(key, value) {
|
|
408
|
+
if (!storage.supportsSetIfAbsent())
|
|
409
|
+
throw Object.assign(new Error("This app build cannot atomically register an intent"), { code: "STORAGE_SET_IF_ABSENT_UNSUPPORTED" });
|
|
410
|
+
if (typeof key !== "string" || !key.trim() || JSON.stringify(value) === undefined)
|
|
411
|
+
throw Object.assign(new Error("setIfAbsent needs a key and JSON value"), { code: "INVALID_ARGUMENT" });
|
|
412
|
+
return storageHost("setIfAbsent").setIfAbsent(key, value);
|
|
413
|
+
},
|
|
414
|
+
async compareAndSet(key, expectedValue, nextValue) {
|
|
415
|
+
if (!storage.supportsCompareAndSet())
|
|
416
|
+
throw Object.assign(new Error("This app build cannot atomically checkpoint existing storage"), { code: "STORAGE_COMPARE_AND_SET_UNSUPPORTED" });
|
|
417
|
+
if (typeof key !== "string" || !key.trim() || JSON.stringify(expectedValue) === undefined || JSON.stringify(nextValue) === undefined)
|
|
418
|
+
throw Object.assign(new Error("compareAndSet needs a key and JSON values"), { code: "INVALID_ARGUMENT" });
|
|
419
|
+
return storageHost("compareAndSet").compareAndSet(key, expectedValue, nextValue);
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
function recordingOwnershipSupported(namespace) {
|
|
423
|
+
const root = getCraftRoot();
|
|
424
|
+
const flag = namespace === "location" ? "locationRecordingOwnership" : "heartRateRecordingOwnership";
|
|
425
|
+
const caps = root?.capabilities;
|
|
426
|
+
const advertised = Array.isArray(caps) ? caps.includes(flag) : caps?.[flag] === true;
|
|
427
|
+
return advertised && ["pauseRecording", "resumeRecording", "stopRecording", "readRecording", "getRecordingState", "acknowledgeRecording"].every((method) => typeof root?.[namespace]?.[method] === "function");
|
|
428
|
+
}
|
|
429
|
+
function recordingIntentSupported(namespace) {
|
|
430
|
+
const root = getCraftRoot();
|
|
431
|
+
const flag = namespace === "location" ? "locationRecordingIntentIdentity" : "heartRateRecordingIntentIdentity";
|
|
432
|
+
const caps = root?.capabilities;
|
|
433
|
+
const advertised = Array.isArray(caps) ? caps.includes(flag) : caps?.[flag] === true;
|
|
434
|
+
return advertised && recordingOwnershipSupported(namespace) && typeof root?.[namespace]?.startRecording === "function";
|
|
435
|
+
}
|
|
436
|
+
function validateRecordingStart(namespace, options) {
|
|
437
|
+
if (!Object.prototype.hasOwnProperty.call(options, "recordingId"))
|
|
438
|
+
return;
|
|
439
|
+
if (typeof options.recordingId !== "string" || !options.recordingId.trim())
|
|
440
|
+
throw Object.assign(new Error("recordingId must be a nonempty recording ID"), { code: "INVALID_ARGUMENT" });
|
|
441
|
+
if (!recordingIntentSupported(namespace))
|
|
442
|
+
throw Object.assign(new Error("This app build cannot start the persisted recording intent"), { code: "RECORDING_INTENT_IDENTITY_UNSUPPORTED" });
|
|
443
|
+
}
|
|
444
|
+
function verifyRecordingStart(options, result) {
|
|
445
|
+
if (options.recordingId !== undefined && result.id !== options.recordingId)
|
|
446
|
+
throw Object.assign(new Error("The native recording does not match the persisted intent"), { code: "RECORDING_CONFLICT" });
|
|
447
|
+
return result;
|
|
448
|
+
}
|
|
449
|
+
function validateRecordingOwnership(namespace, options) {
|
|
450
|
+
if (!options || !Object.prototype.hasOwnProperty.call(options, "expectedId"))
|
|
451
|
+
return;
|
|
452
|
+
if (typeof options.expectedId !== "string" || !options.expectedId.trim())
|
|
453
|
+
throw Object.assign(new Error("expectedId must be a nonempty recording ID"), { code: "INVALID_ARGUMENT" });
|
|
454
|
+
if ((getCraftRoot() || namespace === "heartRate") && !recordingOwnershipSupported(namespace))
|
|
455
|
+
throw Object.assign(new Error("This app build cannot enforce recording ownership"), { code: "RECORDING_OWNERSHIP_UNSUPPORTED" });
|
|
456
|
+
if (namespace === "location" && !getCraftRoot() && options.expectedId !== webLocationRecording.id)
|
|
457
|
+
throw Object.assign(new Error("The active location recording has changed"), { code: "RECORDING_CONFLICT" });
|
|
458
|
+
}
|
|
358
459
|
var webLocationRecording = {
|
|
359
460
|
id: null,
|
|
360
461
|
active: false,
|
|
@@ -364,6 +465,8 @@ var webLocationRecording = {
|
|
|
364
465
|
};
|
|
365
466
|
var webLocationRecordingWatchId = null;
|
|
366
467
|
var location = {
|
|
468
|
+
supportsRecordingOwnership: () => recordingOwnershipSupported("location"),
|
|
469
|
+
supportsRecordingIntentIdentity: () => recordingIntentSupported("location"),
|
|
367
470
|
async getCurrentPosition(options = {}) {
|
|
368
471
|
const craft = getCraftMobile();
|
|
369
472
|
if (typeof window !== "undefined" && craft?.location) {
|
|
@@ -412,9 +515,12 @@ var location = {
|
|
|
412
515
|
navigator.geolocation.clearWatch(watchId);
|
|
413
516
|
},
|
|
414
517
|
async startRecording(options = {}) {
|
|
518
|
+
validateRecordingStart("location", options);
|
|
415
519
|
const craft = getCraftMobile();
|
|
416
520
|
if (typeof window !== "undefined" && craft?.location?.startRecording)
|
|
417
|
-
return craft.location.startRecording(options);
|
|
521
|
+
return verifyRecordingStart(options, await craft.location.startRecording(options));
|
|
522
|
+
if (webLocationRecording.active)
|
|
523
|
+
throw Object.assign(new Error("A location recording is already active"), { code: "RECORDING_ALREADY_ACTIVE" });
|
|
418
524
|
if (webLocationRecordingWatchId !== null)
|
|
419
525
|
navigator.geolocation.clearWatch(webLocationRecordingWatchId);
|
|
420
526
|
webLocationRecording = {
|
|
@@ -430,24 +536,27 @@ var location = {
|
|
|
430
536
|
}, options);
|
|
431
537
|
return { ...webLocationRecording, sampleCount: 0 };
|
|
432
538
|
},
|
|
433
|
-
async pauseRecording() {
|
|
539
|
+
async pauseRecording(options) {
|
|
540
|
+
validateRecordingOwnership("location", options);
|
|
434
541
|
const craft = getCraftMobile();
|
|
435
542
|
if (typeof window !== "undefined" && craft?.location?.pauseRecording)
|
|
436
|
-
return craft.location.pauseRecording();
|
|
543
|
+
return craft.location.pauseRecording(options);
|
|
437
544
|
webLocationRecording.paused = true;
|
|
438
545
|
return { ...webLocationRecording, sampleCount: webLocationRecording.locations.length };
|
|
439
546
|
},
|
|
440
|
-
async resumeRecording() {
|
|
547
|
+
async resumeRecording(options) {
|
|
548
|
+
validateRecordingOwnership("location", options);
|
|
441
549
|
const craft = getCraftMobile();
|
|
442
550
|
if (typeof window !== "undefined" && craft?.location?.resumeRecording)
|
|
443
|
-
return craft.location.resumeRecording();
|
|
551
|
+
return craft.location.resumeRecording(options);
|
|
444
552
|
webLocationRecording.paused = false;
|
|
445
553
|
return { ...webLocationRecording, sampleCount: webLocationRecording.locations.length };
|
|
446
554
|
},
|
|
447
|
-
async stopRecording() {
|
|
555
|
+
async stopRecording(options) {
|
|
556
|
+
validateRecordingOwnership("location", options);
|
|
448
557
|
const craft = getCraftMobile();
|
|
449
558
|
if (typeof window !== "undefined" && craft?.location?.stopRecording)
|
|
450
|
-
return craft.location.stopRecording();
|
|
559
|
+
return craft.location.stopRecording(options);
|
|
451
560
|
if (webLocationRecordingWatchId !== null) {
|
|
452
561
|
location.clearWatch(webLocationRecordingWatchId);
|
|
453
562
|
webLocationRecordingWatchId = null;
|
|
@@ -456,16 +565,30 @@ var location = {
|
|
|
456
565
|
webLocationRecording.paused = false;
|
|
457
566
|
return { ...webLocationRecording, locations: [...webLocationRecording.locations] };
|
|
458
567
|
},
|
|
459
|
-
async getRecordingState() {
|
|
568
|
+
async getRecordingState(options) {
|
|
569
|
+
validateRecordingOwnership("location", options);
|
|
460
570
|
const craft = getCraftMobile();
|
|
461
571
|
if (typeof window !== "undefined" && craft?.location?.getRecordingState)
|
|
462
|
-
return craft.location.getRecordingState();
|
|
572
|
+
return craft.location.getRecordingState(options);
|
|
463
573
|
return { ...webLocationRecording, sampleCount: webLocationRecording.locations.length };
|
|
464
574
|
},
|
|
465
|
-
async
|
|
575
|
+
async acknowledgeRecording(options) {
|
|
576
|
+
if (!options || !Object.prototype.hasOwnProperty.call(options, "expectedId"))
|
|
577
|
+
throw Object.assign(new Error("Acknowledgement needs expectedId"), { code: "INVALID_ARGUMENT" });
|
|
578
|
+
validateRecordingOwnership("location", options);
|
|
579
|
+
const craft = getCraftMobile();
|
|
580
|
+
if (craft?.location?.acknowledgeRecording)
|
|
581
|
+
return craft.location.acknowledgeRecording(options);
|
|
582
|
+
if (webLocationRecording.active)
|
|
583
|
+
throw Object.assign(new Error("The recording is still active"), { code: "RECORDING_ALREADY_ACTIVE" });
|
|
584
|
+
webLocationRecording = { id: null, active: false, paused: false, startedAt: null, locations: [] };
|
|
585
|
+
return true;
|
|
586
|
+
},
|
|
587
|
+
async readRecording(options) {
|
|
588
|
+
validateRecordingOwnership("location", options);
|
|
466
589
|
const craft = getCraftMobile();
|
|
467
590
|
if (typeof window !== "undefined" && craft?.location?.readRecording)
|
|
468
|
-
return craft.location.readRecording();
|
|
591
|
+
return craft.location.readRecording(options);
|
|
469
592
|
return [...webLocationRecording.locations];
|
|
470
593
|
}
|
|
471
594
|
};
|
|
@@ -1023,6 +1146,8 @@ async function heartRateCall(method, fallback, normalize, ...args) {
|
|
|
1023
1146
|
}
|
|
1024
1147
|
var noRecording = () => normalizeHeartRateRecording({});
|
|
1025
1148
|
var heartRate = {
|
|
1149
|
+
supportsRecordingOwnership: () => recordingOwnershipSupported("heartRate"),
|
|
1150
|
+
supportsRecordingIntentIdentity: () => recordingIntentSupported("heartRate"),
|
|
1026
1151
|
isAvailable() {
|
|
1027
1152
|
return typeof nativeHeartRate()?.getState === "function";
|
|
1028
1153
|
},
|
|
@@ -1037,12 +1162,37 @@ var heartRate = {
|
|
|
1037
1162
|
connect: (id) => heartRateCall("connect", noHeartRate, normalizeHeartRateState, id),
|
|
1038
1163
|
reconnect: () => heartRateCall("reconnect", noHeartRate, normalizeHeartRateState),
|
|
1039
1164
|
disconnect: (options = {}) => heartRateCall("disconnect", noHeartRate, normalizeHeartRateState, options),
|
|
1040
|
-
startRecording
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1165
|
+
async startRecording(options) {
|
|
1166
|
+
const supplied = options ?? {};
|
|
1167
|
+
validateRecordingStart("heartRate", supplied);
|
|
1168
|
+
return verifyRecordingStart(supplied, await heartRateCall("startRecording", noRecording, normalizeHeartRateRecording, ...options === undefined ? [] : [options]));
|
|
1169
|
+
},
|
|
1170
|
+
async pauseRecording(options) {
|
|
1171
|
+
validateRecordingOwnership("heartRate", options);
|
|
1172
|
+
return heartRateCall("pauseRecording", noRecording, normalizeHeartRateRecording, ...options === undefined ? [] : [options]);
|
|
1173
|
+
},
|
|
1174
|
+
async resumeRecording(options) {
|
|
1175
|
+
validateRecordingOwnership("heartRate", options);
|
|
1176
|
+
return heartRateCall("resumeRecording", noRecording, normalizeHeartRateRecording, ...options === undefined ? [] : [options]);
|
|
1177
|
+
},
|
|
1178
|
+
async stopRecording(options) {
|
|
1179
|
+
validateRecordingOwnership("heartRate", options);
|
|
1180
|
+
return heartRateCall("stopRecording", () => ({ ...noRecording(), samples: [] }), (value) => ({ ...normalizeHeartRateRecording(value), samples: normalizeHeartRateSamples(hrRecord(value).samples) }), ...options === undefined ? [] : [options]);
|
|
1181
|
+
},
|
|
1182
|
+
async getRecordingState(options) {
|
|
1183
|
+
validateRecordingOwnership("heartRate", options);
|
|
1184
|
+
return heartRateCall("getRecordingState", noRecording, normalizeHeartRateRecording, ...options === undefined ? [] : [options]);
|
|
1185
|
+
},
|
|
1186
|
+
async acknowledgeRecording(options) {
|
|
1187
|
+
if (!options || !Object.prototype.hasOwnProperty.call(options, "expectedId"))
|
|
1188
|
+
throw Object.assign(new Error("Acknowledgement needs expectedId"), { code: "INVALID_ARGUMENT" });
|
|
1189
|
+
validateRecordingOwnership("heartRate", options);
|
|
1190
|
+
return heartRateCall("acknowledgeRecording", () => false, (value) => value === true, options);
|
|
1191
|
+
},
|
|
1192
|
+
async readRecording(options = {}) {
|
|
1193
|
+
validateRecordingOwnership("heartRate", options);
|
|
1194
|
+
return heartRateCall("readRecording", () => [], normalizeHeartRateSamples, options);
|
|
1195
|
+
},
|
|
1046
1196
|
onReading: (callback) => hrSubscribe(heartRateWatch.readings, callback),
|
|
1047
1197
|
onDevice: (callback) => hrSubscribe(heartRateWatch.devices, callback),
|
|
1048
1198
|
onState: (callback) => hrSubscribe(heartRateWatch.states, callback)
|
|
@@ -1646,6 +1796,7 @@ export {
|
|
|
1646
1796
|
watchConnectivity,
|
|
1647
1797
|
tabBar,
|
|
1648
1798
|
symbols,
|
|
1799
|
+
storage,
|
|
1649
1800
|
statusBar,
|
|
1650
1801
|
splash,
|
|
1651
1802
|
speech,
|
|
@@ -1679,6 +1830,7 @@ export {
|
|
|
1679
1830
|
dialog,
|
|
1680
1831
|
device,
|
|
1681
1832
|
deepLinks,
|
|
1833
|
+
crypto,
|
|
1682
1834
|
crashReporter,
|
|
1683
1835
|
contextMenu,
|
|
1684
1836
|
chrome,
|