craft-native 0.0.237 → 0.0.241
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 +24 -2
- package/dist/android/src/index.js +362 -103
- package/dist/android/src/native-map.d.ts +7 -0
- package/dist/android/templates/CraftBridge.kt.template +67 -55
- package/dist/android/templates/CraftNative.kt.template +27 -0
- package/dist/android/templates/CraftNativeCapabilities.kt.template +36 -5
- package/dist/android/templates/CraftNativeMapGoogle.kt.template +440 -0
- package/dist/android/templates/CraftNativeMapLibre.kt.template +438 -0
- package/dist/android/templates/CraftNativeMessagingService.kt.template +3 -1
- package/dist/android/templates/CraftNativeNotificationReceiver.kt.template +2 -1
- 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 +255 -47
- package/dist/android/templates/MainActivityNative.kt.template +7 -428
- package/dist/android/templates/build.gradle.kts.app.template +1 -1
- package/dist/android/templates/build.gradle.kts.project.template +1 -1
- 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/index.cjs +126 -12
- package/dist/index.js +126 -12
- package/dist/ios/src/index.d.ts +5 -0
- package/dist/ios/src/index.js +17 -1
- package/dist/ios/src/testing.d.ts +16 -2
- package/dist/ios/templates/CraftApp.swift +418 -93
- package/dist/ios/templates/CraftHeartRate.swift +141 -44
- package/dist/ios/templates/CraftNativeActions.swift +46 -48
- package/dist/ios/templates/CraftNativeScreen.swift +82 -17
- package/dist/ios/templates/CraftPersistentStorage.swift +99 -0
- package/dist/ios/templates/CraftRecordingArchive.swift +85 -0
- package/dist/ios/templates/testing/DriverTests.swift.template +45 -1
- package/dist/macos/templates/native_layout.zig +41 -5
- package/dist/mobile-testing.js +30 -4
- package/dist/mobile.js +169 -17
- package/package.json +1 -1
package/dist/browser.js
CHANGED
|
@@ -1,7 +1,28 @@
|
|
|
1
|
+
// src/bridge/ids.ts
|
|
2
|
+
function secureUUID() {
|
|
3
|
+
const c = globalThis.crypto;
|
|
4
|
+
if (c && typeof c.randomUUID === "function") {
|
|
5
|
+
return c.randomUUID();
|
|
6
|
+
}
|
|
7
|
+
if (c && typeof c.getRandomValues === "function") {
|
|
8
|
+
const bytes = new Uint8Array(16);
|
|
9
|
+
c.getRandomValues(bytes);
|
|
10
|
+
bytes[6] = bytes[6] & 15 | 64;
|
|
11
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
12
|
+
const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
13
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
|
|
14
|
+
}
|
|
15
|
+
throw new Error("[Craft] No Web Crypto available; cannot generate secure IDs");
|
|
16
|
+
}
|
|
17
|
+
|
|
1
18
|
// src/browser/storage.ts
|
|
2
19
|
async function createBrowserStorage(options) {
|
|
3
20
|
const { storage, namespace, tokenKey, changed } = options;
|
|
4
21
|
const prefix = `${namespace}:`;
|
|
22
|
+
const lock = options.lock === null ? undefined : options.lock ?? (typeof navigator !== "undefined" && typeof navigator.locks?.request === "function" ? (name, operation) => navigator.locks.request(name, operation) : undefined);
|
|
23
|
+
const exclusive = (operation) => lock ? lock(`${namespace}:storage`, operation) : Promise.resolve().then(operation);
|
|
24
|
+
const scopeChanged = () => Object.assign(new Error("The storage account changed"), { code: "STORAGE_SCOPE_CHANGED" });
|
|
25
|
+
const pending = new Map;
|
|
5
26
|
let token = storage.getItem(tokenKey);
|
|
6
27
|
let scope = token ? await options.hash(token) : "guest";
|
|
7
28
|
let transitioning = false;
|
|
@@ -11,17 +32,61 @@ async function createBrowserStorage(options) {
|
|
|
11
32
|
const key = (name) => `${prefix}account:${scope}:${name}`;
|
|
12
33
|
const snapshotKey = (name) => name === "app" ? `${prefix}app` : key(`snapshot:${name}`);
|
|
13
34
|
const keys = (base) => Array.from({ length: storage.length }, (_, index) => storage.key(index)).filter((value) => value !== null && value.startsWith(base));
|
|
14
|
-
const
|
|
15
|
-
if (!available())
|
|
16
|
-
return null;
|
|
17
|
-
const value = storage.getItem(key(`storage:${name}`));
|
|
35
|
+
const parse = (value) => {
|
|
18
36
|
if (value === null)
|
|
19
37
|
return null;
|
|
20
38
|
try {
|
|
21
39
|
return JSON.parse(value);
|
|
22
40
|
} catch {
|
|
41
|
+
throw Object.assign(new Error("Stored value is not valid JSON"), { code: "STORAGE_CORRUPT" });
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
const encode = (value) => {
|
|
45
|
+
const json = JSON.stringify(value);
|
|
46
|
+
if (json === undefined)
|
|
47
|
+
throw Object.assign(new Error("Storage requires JSON values"), { code: "INVALID_ARGUMENT" });
|
|
48
|
+
return json;
|
|
49
|
+
};
|
|
50
|
+
const capture = () => ({ token, scope, revision });
|
|
51
|
+
const requireScope = (captured) => {
|
|
52
|
+
if (!available() || captured.token !== token || captured.scope !== scope || captured.revision !== revision)
|
|
53
|
+
throw scopeChanged();
|
|
54
|
+
};
|
|
55
|
+
const read = (name) => {
|
|
56
|
+
if (!available())
|
|
23
57
|
return null;
|
|
58
|
+
const address = key(`storage:${name}`);
|
|
59
|
+
const waiting = pending.get(address);
|
|
60
|
+
return parse(waiting ? waiting.json : storage.getItem(address));
|
|
61
|
+
};
|
|
62
|
+
const setSync = (name, value) => {
|
|
63
|
+
if (!available())
|
|
64
|
+
return;
|
|
65
|
+
const captured = capture();
|
|
66
|
+
const address = key(`storage:${name}`);
|
|
67
|
+
const json = value === null || value === undefined ? null : encode(value);
|
|
68
|
+
const write = () => {
|
|
69
|
+
if (json === null)
|
|
70
|
+
storage.removeItem(address);
|
|
71
|
+
else
|
|
72
|
+
storage.setItem(address, json);
|
|
73
|
+
};
|
|
74
|
+
if (!lock) {
|
|
75
|
+
write();
|
|
76
|
+
return;
|
|
24
77
|
}
|
|
78
|
+
const entry = { json };
|
|
79
|
+
pending.set(address, entry);
|
|
80
|
+
exclusive(() => {
|
|
81
|
+
requireScope(captured);
|
|
82
|
+
write();
|
|
83
|
+
}).catch((error) => {
|
|
84
|
+
if (error.code !== "STORAGE_SCOPE_CHANGED")
|
|
85
|
+
console.error("Browser storage write failed", error);
|
|
86
|
+
}).finally(() => {
|
|
87
|
+
if (pending.get(address) === entry)
|
|
88
|
+
pending.delete(address);
|
|
89
|
+
});
|
|
25
90
|
};
|
|
26
91
|
const accountChanged = async () => {
|
|
27
92
|
const next = storage.getItem(tokenKey);
|
|
@@ -32,14 +97,17 @@ async function createBrowserStorage(options) {
|
|
|
32
97
|
const previous = scope;
|
|
33
98
|
const current = ++revision;
|
|
34
99
|
transitioning = true;
|
|
35
|
-
for (const name of keys(`${prefix}account:${previous}:`))
|
|
36
|
-
storage.removeItem(name);
|
|
37
|
-
for (const name of options.accountKeys ?? []) {
|
|
38
|
-
if (name !== tokenKey)
|
|
39
|
-
storage.removeItem(name);
|
|
40
|
-
}
|
|
41
100
|
token = next;
|
|
101
|
+
pending.clear();
|
|
42
102
|
transition = (async () => {
|
|
103
|
+
await exclusive(() => {
|
|
104
|
+
for (const name of keys(`${prefix}account:${previous}:`))
|
|
105
|
+
storage.removeItem(name);
|
|
106
|
+
for (const name of options.accountKeys ?? []) {
|
|
107
|
+
if (name !== tokenKey)
|
|
108
|
+
storage.removeItem(name);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
43
111
|
const nextScope = next ? await options.hash(next) : "guest";
|
|
44
112
|
if (current !== revision)
|
|
45
113
|
return;
|
|
@@ -51,13 +119,11 @@ async function createBrowserStorage(options) {
|
|
|
51
119
|
};
|
|
52
120
|
const secureKey = (name) => name === "auth.token" ? tokenKey : key(`credential:${name}`);
|
|
53
121
|
return {
|
|
122
|
+
atomicStorage: lock !== undefined,
|
|
54
123
|
scope: () => scope,
|
|
55
124
|
accountChanged,
|
|
56
125
|
craft: {
|
|
57
|
-
storage: { getSync: read, setSync
|
|
58
|
-
if (available())
|
|
59
|
-
storage.setItem(key(`storage:${name}`), JSON.stringify(value));
|
|
60
|
-
} },
|
|
126
|
+
storage: { getSync: read, setSync },
|
|
61
127
|
secureStorage: { getSync: (name) => name === "auth.token" || available() ? storage.getItem(secureKey(name)) : null },
|
|
62
128
|
snapshots: {
|
|
63
129
|
get: (name) => {
|
|
@@ -97,54 +163,94 @@ async function createBrowserStorage(options) {
|
|
|
97
163
|
}
|
|
98
164
|
},
|
|
99
165
|
async call(module, method, args) {
|
|
166
|
+
const captured = capture();
|
|
167
|
+
const wasAvailable = available();
|
|
100
168
|
await accountChanged();
|
|
101
169
|
const name = String(args[0] ?? "");
|
|
102
170
|
if (module === "Storage") {
|
|
103
|
-
if (
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
171
|
+
if (!wasAvailable)
|
|
172
|
+
throw scopeChanged();
|
|
173
|
+
return exclusive(() => {
|
|
174
|
+
requireScope(captured);
|
|
175
|
+
const address = `${prefix}account:${captured.scope}:storage:${name}`;
|
|
176
|
+
if (method === "get")
|
|
177
|
+
return parse(storage.getItem(address));
|
|
178
|
+
if (method === "set") {
|
|
179
|
+
storage.setItem(address, encode(args[1]));
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
if (method === "remove") {
|
|
183
|
+
storage.removeItem(address);
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
if (method === "keys")
|
|
187
|
+
return keys(key("storage:")).map((item) => item.slice(key("storage:").length));
|
|
188
|
+
if (method === "clear") {
|
|
189
|
+
for (const item of keys(key("storage:")))
|
|
190
|
+
storage.removeItem(item);
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
if (method === "compareAndSet" || method === "setIfAbsent") {
|
|
194
|
+
if (!lock)
|
|
195
|
+
throw Object.assign(new Error("Atomic browser storage requires Web Locks"), { code: method === "setIfAbsent" ? "STORAGE_SET_IF_ABSENT_UNSUPPORTED" : "STORAGE_COMPARE_AND_SET_UNSUPPORTED" });
|
|
196
|
+
const raw = storage.getItem(address);
|
|
197
|
+
if (method === "setIfAbsent") {
|
|
198
|
+
if (raw !== null)
|
|
199
|
+
return false;
|
|
200
|
+
storage.setItem(address, encode(args[1]));
|
|
201
|
+
return true;
|
|
202
|
+
}
|
|
203
|
+
if (raw === null || !jsonEqual(parse(raw), args[1]))
|
|
204
|
+
return false;
|
|
205
|
+
storage.setItem(address, encode(args[2]));
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
208
|
+
throw Object.assign(new Error(`Unsupported browser storage operation ${method}`), { code: "UNSUPPORTED" });
|
|
209
|
+
});
|
|
120
210
|
}
|
|
121
211
|
if (module === "SecureStorage") {
|
|
122
|
-
if (
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
212
|
+
if (!wasAvailable)
|
|
213
|
+
throw scopeChanged();
|
|
214
|
+
const result = await exclusive(() => {
|
|
215
|
+
requireScope(captured);
|
|
216
|
+
const address = name === "auth.token" ? tokenKey : `${prefix}account:${captured.scope}:credential:${name}`;
|
|
217
|
+
if (method === "get")
|
|
218
|
+
return storage.getItem(address);
|
|
219
|
+
if (method === "set") {
|
|
220
|
+
storage.setItem(address, String(args[1]));
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
223
|
+
if (method === "remove") {
|
|
224
|
+
storage.removeItem(address);
|
|
225
|
+
return true;
|
|
226
|
+
}
|
|
227
|
+
if (method === "clear") {
|
|
228
|
+
for (const item of keys(`${prefix}account:${captured.scope}:credential:`))
|
|
229
|
+
storage.removeItem(item);
|
|
230
|
+
storage.removeItem(tokenKey);
|
|
231
|
+
return true;
|
|
232
|
+
}
|
|
233
|
+
throw Object.assign(new Error(`Unsupported browser storage operation ${module}.${method}`), { code: "UNSUPPORTED" });
|
|
234
|
+
});
|
|
235
|
+
if (method === "clear" || name === "auth.token" && (method === "set" || method === "remove"))
|
|
140
236
|
await accountChanged();
|
|
141
|
-
|
|
142
|
-
}
|
|
237
|
+
return result;
|
|
143
238
|
}
|
|
144
239
|
throw Object.assign(new Error(`Unsupported browser storage operation ${module}.${method}`), { code: "UNSUPPORTED" });
|
|
145
240
|
}
|
|
146
241
|
};
|
|
147
242
|
}
|
|
243
|
+
function jsonEqual(left, right) {
|
|
244
|
+
if (left === right)
|
|
245
|
+
return true;
|
|
246
|
+
if (Array.isArray(left) || Array.isArray(right))
|
|
247
|
+
return Array.isArray(left) && Array.isArray(right) && left.length === right.length && left.every((value, index) => jsonEqual(value, right[index]));
|
|
248
|
+
if (left === null || right === null || typeof left !== "object" || typeof right !== "object")
|
|
249
|
+
return false;
|
|
250
|
+
const a = left;
|
|
251
|
+
const b = right;
|
|
252
|
+
return Object.keys(a).length === Object.keys(b).length && Object.keys(a).every((key) => Object.prototype.hasOwnProperty.call(b, key) && jsonEqual(a[key], b[key]));
|
|
253
|
+
}
|
|
148
254
|
|
|
149
255
|
// src/browser/dialogs.ts
|
|
150
256
|
function showBrowserDialog(kind, options, signal) {
|
|
@@ -342,6 +448,10 @@ async function createBrowserPlatform(options = {}) {
|
|
|
342
448
|
};
|
|
343
449
|
window.addEventListener("storage", storageEvent);
|
|
344
450
|
const capabilityNames = ["storage", "secureStorage", "lifecycle", "dialog", "browser", "linking", "files", "share", "device"];
|
|
451
|
+
if (persistence.atomicStorage)
|
|
452
|
+
capabilityNames.push("storageCompareAndSet", "storageSetIfAbsent");
|
|
453
|
+
if (typeof globalThis.crypto?.randomUUID === "function" || typeof globalThis.crypto?.getRandomValues === "function")
|
|
454
|
+
capabilityNames.push("crypto");
|
|
345
455
|
if (navigator.clipboard)
|
|
346
456
|
capabilityNames.push("clipboard");
|
|
347
457
|
if ("vibrate" in navigator)
|
|
@@ -399,6 +509,8 @@ async function createBrowserPlatform(options = {}) {
|
|
|
399
509
|
await resetAccount();
|
|
400
510
|
return answer;
|
|
401
511
|
}
|
|
512
|
+
if (module === "Crypto" && method === "randomUUID")
|
|
513
|
+
return secureUUID();
|
|
402
514
|
if (module === "Dialog")
|
|
403
515
|
return showBrowserDialog(method, first, signal);
|
|
404
516
|
if (module === "Device" && method === "getInfo")
|
package/dist/cli.js
CHANGED
|
@@ -3424,7 +3424,7 @@ function pinCraftNativeDependency(packagePath, craftVersion) {
|
|
|
3424
3424
|
`);
|
|
3425
3425
|
}
|
|
3426
3426
|
// package.json
|
|
3427
|
-
var version = "0.0.
|
|
3427
|
+
var version = "0.0.241";
|
|
3428
3428
|
|
|
3429
3429
|
// bin/cli.ts
|
|
3430
3430
|
var spawnedFrom = process6.env[CRAFT_CLI_SPAWN_MARKER];
|
package/dist/index.cjs
CHANGED
|
@@ -77,6 +77,7 @@ __export(exports_src, {
|
|
|
77
77
|
styles: () => styles,
|
|
78
78
|
style: () => style,
|
|
79
79
|
storeKit: () => storeKit,
|
|
80
|
+
storage: () => storage2,
|
|
80
81
|
stat: () => stat,
|
|
81
82
|
startupTask: () => startupTask,
|
|
82
83
|
stageManager: () => stageManager,
|
|
@@ -309,7 +310,7 @@ function craftBinaryNotFoundMessage(triedPath) {
|
|
|
309
310
|
`);
|
|
310
311
|
}
|
|
311
312
|
// package.json
|
|
312
|
-
var version = "0.0.
|
|
313
|
+
var version = "0.0.241";
|
|
313
314
|
|
|
314
315
|
// src/sdk-version.ts
|
|
315
316
|
var SDK_VERSION = version;
|
|
@@ -5222,6 +5223,92 @@ var secureStorage = {
|
|
|
5222
5223
|
Object.keys(localStorage).filter((k) => k.startsWith("secure_")).forEach((k) => localStorage.removeItem(k));
|
|
5223
5224
|
}
|
|
5224
5225
|
};
|
|
5226
|
+
function storageHost(method) {
|
|
5227
|
+
const host2 = getCraftRoot()?.storage;
|
|
5228
|
+
if (typeof host2?.[method] !== "function")
|
|
5229
|
+
throw Object.assign(new Error("This host does not provide native storage"), { code: "STORAGE_UNSUPPORTED" });
|
|
5230
|
+
return host2;
|
|
5231
|
+
}
|
|
5232
|
+
var storage2 = {
|
|
5233
|
+
supportsSetIfAbsent() {
|
|
5234
|
+
const root = getCraftRoot();
|
|
5235
|
+
const caps = root?.capabilities;
|
|
5236
|
+
const supported = Array.isArray(caps) ? caps.includes("storageSetIfAbsent") : caps?.storageSetIfAbsent === true;
|
|
5237
|
+
return supported && typeof root?.storage?.setIfAbsent === "function";
|
|
5238
|
+
},
|
|
5239
|
+
supportsCompareAndSet() {
|
|
5240
|
+
const root = getCraftRoot();
|
|
5241
|
+
const caps = root?.capabilities;
|
|
5242
|
+
const supported = Array.isArray(caps) ? caps.includes("storageCompareAndSet") : caps?.storageCompareAndSet === true;
|
|
5243
|
+
return supported && typeof root?.storage?.compareAndSet === "function";
|
|
5244
|
+
},
|
|
5245
|
+
async get(key) {
|
|
5246
|
+
return storageHost("get").get(key);
|
|
5247
|
+
},
|
|
5248
|
+
async set(key, value) {
|
|
5249
|
+
return storageHost("set").set(key, value);
|
|
5250
|
+
},
|
|
5251
|
+
async remove(key) {
|
|
5252
|
+
return storageHost("remove").remove(key);
|
|
5253
|
+
},
|
|
5254
|
+
async clear() {
|
|
5255
|
+
return storageHost("clear").clear();
|
|
5256
|
+
},
|
|
5257
|
+
async keys() {
|
|
5258
|
+
return storageHost("keys").keys();
|
|
5259
|
+
},
|
|
5260
|
+
async setIfAbsent(key, value) {
|
|
5261
|
+
if (!storage2.supportsSetIfAbsent())
|
|
5262
|
+
throw Object.assign(new Error("This app build cannot atomically register an intent"), { code: "STORAGE_SET_IF_ABSENT_UNSUPPORTED" });
|
|
5263
|
+
if (typeof key !== "string" || !key.trim() || JSON.stringify(value) === undefined)
|
|
5264
|
+
throw Object.assign(new Error("setIfAbsent needs a key and JSON value"), { code: "INVALID_ARGUMENT" });
|
|
5265
|
+
return storageHost("setIfAbsent").setIfAbsent(key, value);
|
|
5266
|
+
},
|
|
5267
|
+
async compareAndSet(key, expectedValue, nextValue) {
|
|
5268
|
+
if (!storage2.supportsCompareAndSet())
|
|
5269
|
+
throw Object.assign(new Error("This app build cannot atomically checkpoint existing storage"), { code: "STORAGE_COMPARE_AND_SET_UNSUPPORTED" });
|
|
5270
|
+
if (typeof key !== "string" || !key.trim() || JSON.stringify(expectedValue) === undefined || JSON.stringify(nextValue) === undefined)
|
|
5271
|
+
throw Object.assign(new Error("compareAndSet needs a key and JSON values"), { code: "INVALID_ARGUMENT" });
|
|
5272
|
+
return storageHost("compareAndSet").compareAndSet(key, expectedValue, nextValue);
|
|
5273
|
+
}
|
|
5274
|
+
};
|
|
5275
|
+
function recordingOwnershipSupported(namespace) {
|
|
5276
|
+
const root = getCraftRoot();
|
|
5277
|
+
const flag = namespace === "location" ? "locationRecordingOwnership" : "heartRateRecordingOwnership";
|
|
5278
|
+
const caps = root?.capabilities;
|
|
5279
|
+
const advertised = Array.isArray(caps) ? caps.includes(flag) : caps?.[flag] === true;
|
|
5280
|
+
return advertised && ["pauseRecording", "resumeRecording", "stopRecording", "readRecording", "getRecordingState", "acknowledgeRecording"].every((method) => typeof root?.[namespace]?.[method] === "function");
|
|
5281
|
+
}
|
|
5282
|
+
function recordingIntentSupported(namespace) {
|
|
5283
|
+
const root = getCraftRoot();
|
|
5284
|
+
const flag = namespace === "location" ? "locationRecordingIntentIdentity" : "heartRateRecordingIntentIdentity";
|
|
5285
|
+
const caps = root?.capabilities;
|
|
5286
|
+
const advertised = Array.isArray(caps) ? caps.includes(flag) : caps?.[flag] === true;
|
|
5287
|
+
return advertised && recordingOwnershipSupported(namespace) && typeof root?.[namespace]?.startRecording === "function";
|
|
5288
|
+
}
|
|
5289
|
+
function validateRecordingStart(namespace, options) {
|
|
5290
|
+
if (!Object.prototype.hasOwnProperty.call(options, "recordingId"))
|
|
5291
|
+
return;
|
|
5292
|
+
if (typeof options.recordingId !== "string" || !options.recordingId.trim())
|
|
5293
|
+
throw Object.assign(new Error("recordingId must be a nonempty recording ID"), { code: "INVALID_ARGUMENT" });
|
|
5294
|
+
if (!recordingIntentSupported(namespace))
|
|
5295
|
+
throw Object.assign(new Error("This app build cannot start the persisted recording intent"), { code: "RECORDING_INTENT_IDENTITY_UNSUPPORTED" });
|
|
5296
|
+
}
|
|
5297
|
+
function verifyRecordingStart(options, result) {
|
|
5298
|
+
if (options.recordingId !== undefined && result.id !== options.recordingId)
|
|
5299
|
+
throw Object.assign(new Error("The native recording does not match the persisted intent"), { code: "RECORDING_CONFLICT" });
|
|
5300
|
+
return result;
|
|
5301
|
+
}
|
|
5302
|
+
function validateRecordingOwnership(namespace, options) {
|
|
5303
|
+
if (!options || !Object.prototype.hasOwnProperty.call(options, "expectedId"))
|
|
5304
|
+
return;
|
|
5305
|
+
if (typeof options.expectedId !== "string" || !options.expectedId.trim())
|
|
5306
|
+
throw Object.assign(new Error("expectedId must be a nonempty recording ID"), { code: "INVALID_ARGUMENT" });
|
|
5307
|
+
if ((getCraftRoot() || namespace === "heartRate") && !recordingOwnershipSupported(namespace))
|
|
5308
|
+
throw Object.assign(new Error("This app build cannot enforce recording ownership"), { code: "RECORDING_OWNERSHIP_UNSUPPORTED" });
|
|
5309
|
+
if (namespace === "location" && !getCraftRoot() && options.expectedId !== webLocationRecording.id)
|
|
5310
|
+
throw Object.assign(new Error("The active location recording has changed"), { code: "RECORDING_CONFLICT" });
|
|
5311
|
+
}
|
|
5225
5312
|
var webLocationRecording = {
|
|
5226
5313
|
id: null,
|
|
5227
5314
|
active: false,
|
|
@@ -5231,6 +5318,8 @@ var webLocationRecording = {
|
|
|
5231
5318
|
};
|
|
5232
5319
|
var webLocationRecordingWatchId = null;
|
|
5233
5320
|
var location = {
|
|
5321
|
+
supportsRecordingOwnership: () => recordingOwnershipSupported("location"),
|
|
5322
|
+
supportsRecordingIntentIdentity: () => recordingIntentSupported("location"),
|
|
5234
5323
|
async getCurrentPosition(options = {}) {
|
|
5235
5324
|
const craft = getCraftMobile();
|
|
5236
5325
|
if (typeof window !== "undefined" && craft?.location) {
|
|
@@ -5279,9 +5368,12 @@ var location = {
|
|
|
5279
5368
|
navigator.geolocation.clearWatch(watchId);
|
|
5280
5369
|
},
|
|
5281
5370
|
async startRecording(options = {}) {
|
|
5371
|
+
validateRecordingStart("location", options);
|
|
5282
5372
|
const craft = getCraftMobile();
|
|
5283
5373
|
if (typeof window !== "undefined" && craft?.location?.startRecording)
|
|
5284
|
-
return craft.location.startRecording(options);
|
|
5374
|
+
return verifyRecordingStart(options, await craft.location.startRecording(options));
|
|
5375
|
+
if (webLocationRecording.active)
|
|
5376
|
+
throw Object.assign(new Error("A location recording is already active"), { code: "RECORDING_ALREADY_ACTIVE" });
|
|
5285
5377
|
if (webLocationRecordingWatchId !== null)
|
|
5286
5378
|
navigator.geolocation.clearWatch(webLocationRecordingWatchId);
|
|
5287
5379
|
webLocationRecording = {
|
|
@@ -5297,24 +5389,27 @@ var location = {
|
|
|
5297
5389
|
}, options);
|
|
5298
5390
|
return { ...webLocationRecording, sampleCount: 0 };
|
|
5299
5391
|
},
|
|
5300
|
-
async pauseRecording() {
|
|
5392
|
+
async pauseRecording(options) {
|
|
5393
|
+
validateRecordingOwnership("location", options);
|
|
5301
5394
|
const craft = getCraftMobile();
|
|
5302
5395
|
if (typeof window !== "undefined" && craft?.location?.pauseRecording)
|
|
5303
|
-
return craft.location.pauseRecording();
|
|
5396
|
+
return craft.location.pauseRecording(options);
|
|
5304
5397
|
webLocationRecording.paused = true;
|
|
5305
5398
|
return { ...webLocationRecording, sampleCount: webLocationRecording.locations.length };
|
|
5306
5399
|
},
|
|
5307
|
-
async resumeRecording() {
|
|
5400
|
+
async resumeRecording(options) {
|
|
5401
|
+
validateRecordingOwnership("location", options);
|
|
5308
5402
|
const craft = getCraftMobile();
|
|
5309
5403
|
if (typeof window !== "undefined" && craft?.location?.resumeRecording)
|
|
5310
|
-
return craft.location.resumeRecording();
|
|
5404
|
+
return craft.location.resumeRecording(options);
|
|
5311
5405
|
webLocationRecording.paused = false;
|
|
5312
5406
|
return { ...webLocationRecording, sampleCount: webLocationRecording.locations.length };
|
|
5313
5407
|
},
|
|
5314
|
-
async stopRecording() {
|
|
5408
|
+
async stopRecording(options) {
|
|
5409
|
+
validateRecordingOwnership("location", options);
|
|
5315
5410
|
const craft = getCraftMobile();
|
|
5316
5411
|
if (typeof window !== "undefined" && craft?.location?.stopRecording)
|
|
5317
|
-
return craft.location.stopRecording();
|
|
5412
|
+
return craft.location.stopRecording(options);
|
|
5318
5413
|
if (webLocationRecordingWatchId !== null) {
|
|
5319
5414
|
location.clearWatch(webLocationRecordingWatchId);
|
|
5320
5415
|
webLocationRecordingWatchId = null;
|
|
@@ -5323,16 +5418,30 @@ var location = {
|
|
|
5323
5418
|
webLocationRecording.paused = false;
|
|
5324
5419
|
return { ...webLocationRecording, locations: [...webLocationRecording.locations] };
|
|
5325
5420
|
},
|
|
5326
|
-
async getRecordingState() {
|
|
5421
|
+
async getRecordingState(options) {
|
|
5422
|
+
validateRecordingOwnership("location", options);
|
|
5327
5423
|
const craft = getCraftMobile();
|
|
5328
5424
|
if (typeof window !== "undefined" && craft?.location?.getRecordingState)
|
|
5329
|
-
return craft.location.getRecordingState();
|
|
5425
|
+
return craft.location.getRecordingState(options);
|
|
5330
5426
|
return { ...webLocationRecording, sampleCount: webLocationRecording.locations.length };
|
|
5331
5427
|
},
|
|
5332
|
-
async
|
|
5428
|
+
async acknowledgeRecording(options) {
|
|
5429
|
+
if (!options || !Object.prototype.hasOwnProperty.call(options, "expectedId"))
|
|
5430
|
+
throw Object.assign(new Error("Acknowledgement needs expectedId"), { code: "INVALID_ARGUMENT" });
|
|
5431
|
+
validateRecordingOwnership("location", options);
|
|
5432
|
+
const craft = getCraftMobile();
|
|
5433
|
+
if (craft?.location?.acknowledgeRecording)
|
|
5434
|
+
return craft.location.acknowledgeRecording(options);
|
|
5435
|
+
if (webLocationRecording.active)
|
|
5436
|
+
throw Object.assign(new Error("The recording is still active"), { code: "RECORDING_ALREADY_ACTIVE" });
|
|
5437
|
+
webLocationRecording = { id: null, active: false, paused: false, startedAt: null, locations: [] };
|
|
5438
|
+
return true;
|
|
5439
|
+
},
|
|
5440
|
+
async readRecording(options) {
|
|
5441
|
+
validateRecordingOwnership("location", options);
|
|
5333
5442
|
const craft = getCraftMobile();
|
|
5334
5443
|
if (typeof window !== "undefined" && craft?.location?.readRecording)
|
|
5335
|
-
return craft.location.readRecording();
|
|
5444
|
+
return craft.location.readRecording(options);
|
|
5336
5445
|
return [...webLocationRecording.locations];
|
|
5337
5446
|
}
|
|
5338
5447
|
};
|
|
@@ -5454,6 +5563,11 @@ function onCraftEvent(eventName, callback) {
|
|
|
5454
5563
|
globalThis.addEventListener(eventName, listener);
|
|
5455
5564
|
return () => globalThis.removeEventListener(eventName, listener);
|
|
5456
5565
|
}
|
|
5566
|
+
function getCraftRoot() {
|
|
5567
|
+
if (typeof window === "undefined")
|
|
5568
|
+
return;
|
|
5569
|
+
return window.craft;
|
|
5570
|
+
}
|
|
5457
5571
|
function detectPlatform2() {
|
|
5458
5572
|
if (typeof window === "undefined")
|
|
5459
5573
|
return "linux";
|