mnfst 0.5.249 → 0.5.251

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.
@@ -2,7 +2,7 @@
2
2
 
3
3
  (function () {
4
4
 
5
- const MANIFEST_BUILD_VERSION = '0.5.249';
5
+ const MANIFEST_BUILD_VERSION = '0.5.251';
6
6
 
7
7
  /* Manifest Data Sources - Appwrite Integration */
8
8
 
package/lib/manifest.d.ts CHANGED
@@ -342,7 +342,14 @@ export interface ManifestHaptics {
342
342
  /** `$biometric` from the device plugin. */
343
343
  export interface ManifestBiometric {
344
344
  available(): Promise<boolean>;
345
- verify(opts?: Record<string, unknown>): Promise<unknown>;
345
+ verify(opts?: {
346
+ reason?: string;
347
+ title?: string;
348
+ subtitle?: string;
349
+ cancelTitle?: string;
350
+ fallbackTitle?: string;
351
+ allowDeviceCredential?: boolean;
352
+ }): Promise<{ verified: boolean; error?: string; code?: string }>;
346
353
  }
347
354
 
348
355
  /** `$camera` from the device plugin. */
@@ -2,7 +2,7 @@
2
2
 
3
3
  (function () {
4
4
 
5
- const MANIFEST_BUILD_VERSION = '0.5.249';
5
+ const MANIFEST_BUILD_VERSION = '0.5.251';
6
6
 
7
7
  /* Manifest Data Sources - Configuration */
8
8
 
@@ -15,6 +15,25 @@ function manifestNativePlugin(name) {
15
15
  const cap = window.Capacitor;
16
16
  return (cap && cap.Plugins && cap.Plugins[name]) || null;
17
17
  }
18
+ // Whether a plugin exposes a native method. Bundled apps get a registerPlugin
19
+ // Proxy where every property is a function, so prefer the native header; a
20
+ // header list without this plugin means it isn't installed natively.
21
+ function manifestNativeHas(name, plugin, method) {
22
+ const headers = window.Capacitor && window.Capacitor.PluginHeaders;
23
+ if (Array.isArray(headers)) {
24
+ const h = headers.find(x => x && x.name === name);
25
+ if (!h) return false;
26
+ if (Array.isArray(h.methods)) return h.methods.some(m => m && m.name === method);
27
+ }
28
+ return !!plugin && typeof plugin[method] === 'function';
29
+ }
30
+ // A plugin with a native method, or null. Web builds use Manifest's own fallbacks,
31
+ // not plugin web stubs, so bundled and plain web builds behave the same.
32
+ function manifestNativeUse(name, method) {
33
+ if (!manifestNativeIsNative()) return null;
34
+ const plugin = manifestNativePlugin(name);
35
+ return plugin && manifestNativeHas(name, plugin, method) ? plugin : null;
36
+ }
18
37
  function manifestNativePlatform() {
19
38
  const cap = window.Capacitor;
20
39
  if (cap && typeof cap.getPlatform === 'function') { try { return cap.getPlatform(); } catch (e) {} }
@@ -96,7 +115,7 @@ function manifestShareWeb(payload) {
96
115
  }
97
116
  function manifestShare(opts) {
98
117
  const payload = opts || {};
99
- const Share = manifestNativePlugin('Share');
118
+ const Share = manifestNativeUse('Share', 'share');
100
119
  if (Share) {
101
120
  // When the native sheet is present, never fall through to the web sheet
102
121
  // (that would double-prompt); report the native outcome instead.
@@ -128,7 +147,7 @@ function manifestNativeApplyOnline(connected) {
128
147
  } catch (e) {}
129
148
  }
130
149
  function initManifestNativeNetwork() {
131
- const Network = manifestNativePlugin('Network');
150
+ const Network = manifestNativeUse('Network', 'getStatus');
132
151
  if (!Network) return;
133
152
  try { Network.getStatus().then(s => manifestNativeApplyOnline(s && s.connected)).catch(() => {}); } catch (e) {}
134
153
  try { Network.addListener('networkStatusChange', s => manifestNativeApplyOnline(s && s.connected)); } catch (e) {}
@@ -137,9 +156,8 @@ function initManifestNativeNetwork() {
137
156
 
138
157
  // $secure — Keychain/Keystore-backed key/value storage (native) with a
139
158
  // namespaced localStorage fallback on the web. Intended for session tokens etc.
140
- // Native: install a Capacitor secure-storage plugin registered as
141
- // window.Capacitor.Plugins.SecureStorage; if its method shape differs from the
142
- // adapters below, override with $secure.use(adapter). Verify on device.
159
+ // Native: a plugin registered as SecureStorage (@aparajita/capacitor-secure-storage,
160
+ // or any getItem/setItem or get/set shape); $secure.use(adapter) overrides.
143
161
  // The web fallback is NOT encrypted — it degrades honestly, not securely.
144
162
 
145
163
  const MANIFEST_SECURE_NS = 'mnfst:';
@@ -158,33 +176,63 @@ function manifestSecureWebBackend() {
158
176
  };
159
177
  }
160
178
 
161
- // Adapts the two common Capacitor secure-storage plugin shapes: localStorage-like
162
- // (getItem/setItem/removeItem) or object-arg (get/set/remove with {key,value}).
163
- function manifestSecureNativeBackend(plugin) {
164
- const itemApi = typeof plugin.getItem === 'function';
179
+ // aparajita's default prefix, so keys interop with its own getItem/setItem
180
+ const MANIFEST_SECURE_NATIVE_PREFIX = 'capacitor-storage_';
181
+
182
+ function manifestSecureFail(op, e) {
183
+ try { console.error('[Manifest] $secure.' + op + ' failed', e); } catch (_) {}
184
+ throw e instanceof Error ? e : new Error('$secure.' + op + ' failed: ' + ((e && (e.message || e.code)) || e));
185
+ }
186
+
187
+ // Adapts three native shapes: aparajita (internalGetItem…), localStorage-like
188
+ // (getItem/setItem/removeItem) and object-arg (get/set/remove with {key,value}).
189
+ function manifestSecureNativeBackend(plugin, name) {
190
+ const n = name || 'SecureStorage';
191
+ const has = (m) => manifestNativeHas(n, plugin, m);
192
+ const P = MANIFEST_SECURE_NATIVE_PREFIX;
193
+ const shape = has('internalGetItem') ? 'internal' : has('getItem') ? 'item' : has('get') ? 'object' : null;
194
+ const call = async (op, fn) => {
195
+ if (!shape) manifestSecureFail(op, new Error(n + ' plugin has no supported API; use $secure.use(adapter)'));
196
+ try { return await fn(); } catch (e) { manifestSecureFail(op, e); }
197
+ };
165
198
  return {
166
- async get(key) {
167
- try {
168
- if (itemApi) { const v = await plugin.getItem(key); return v == null ? null : v; }
169
- const r = await plugin.get({ key }); return r && typeof r.value !== 'undefined' ? r.value : (r == null ? null : r);
170
- } catch (e) { return null; }
171
- },
172
- async set(key, value) {
173
- try { if (itemApi) await plugin.setItem(key, String(value)); else await plugin.set({ key, value: String(value) }); } catch (e) {}
174
- },
175
- async remove(key) {
176
- try { if (typeof plugin.removeItem === 'function') await plugin.removeItem(key); else await plugin.remove({ key }); } catch (e) {}
177
- },
178
- async keys() { try { const r = await plugin.keys(); return Array.isArray(r) ? r : (r && (r.keys || r.value)) || []; } catch (e) { return []; } },
179
- async clear() { try { await plugin.clear(); } catch (e) {} }
199
+ get: (key) => call('get', async () => {
200
+ if (key == null || key === '') return null;
201
+ if (shape === 'internal') { const r = await plugin.internalGetItem({ prefixedKey: P + key }); return r && r.data != null ? r.data : null; }
202
+ if (shape === 'item') { const v = await plugin.getItem(key); return v == null ? null : v; }
203
+ const r = await plugin.get({ key }); return r && typeof r.value !== 'undefined' ? r.value : (r == null ? null : r);
204
+ }),
205
+ set: (key, value) => call('set', async () => {
206
+ if (key == null || key === '') throw new Error('No key provided');
207
+ if (shape === 'internal') await plugin.internalSetItem({ prefixedKey: P + key, data: String(value) });
208
+ else if (shape === 'item') await plugin.setItem(key, String(value));
209
+ else await plugin.set({ key, value: String(value) });
210
+ }),
211
+ remove: (key) => call('remove', async () => {
212
+ if (key == null || key === '') return;
213
+ if (shape === 'internal') await plugin.internalRemoveItem({ prefixedKey: P + key });
214
+ else if (shape === 'item') {
215
+ if (!has('removeItem')) throw new Error(n + ' plugin has no removeItem(); use $secure.use(adapter)');
216
+ await plugin.removeItem(key);
217
+ }
218
+ else await plugin.remove({ key });
219
+ }),
220
+ keys: () => call('keys', async () => {
221
+ if (shape === 'internal') { const r = await plugin.internalGetPrefixedKeys({ prefix: P }); return ((r && r.keys) || []).map(k => k.slice(P.length)); }
222
+ const r = await plugin.keys(); return Array.isArray(r) ? r : (r && (r.keys || r.value)) || [];
223
+ }),
224
+ clear: () => call('clear', async () => {
225
+ if (shape === 'internal') await plugin.internalClearItemsWithPrefix({ prefix: P });
226
+ else await plugin.clear();
227
+ })
180
228
  };
181
229
  }
182
230
 
183
231
  let _manifestSecureBackend = null;
184
232
  function manifestSecureBackend() {
185
233
  if (_manifestSecureBackend) return _manifestSecureBackend;
186
- const plugin = manifestNativePlugin('SecureStorage');
187
- _manifestSecureBackend = plugin ? manifestSecureNativeBackend(plugin) : manifestSecureWebBackend();
234
+ const plugin = manifestNativeIsNative() ? manifestNativePlugin('SecureStorage') : null;
235
+ _manifestSecureBackend = plugin ? manifestSecureNativeBackend(plugin, 'SecureStorage') : manifestSecureWebBackend();
188
236
  return _manifestSecureBackend;
189
237
  }
190
238
 
@@ -251,7 +299,7 @@ function initManifestLinks() {
251
299
  get last() { const s = window.Alpine.store('links'); return s ? s.last : null; }
252
300
  }));
253
301
 
254
- const App = manifestNativePlugin('App');
302
+ const App = manifestNativeUse('App', 'getLaunchUrl');
255
303
  if (App) {
256
304
  try { App.addListener('appUrlOpen', (data) => { if (data && data.url) manifestHandleUrl(data.url); }); } catch (e) {}
257
305
  try { App.getLaunchUrl().then(r => { if (r && r.url) manifestHandleUrl(r.url); }).catch(() => {}); } catch (e) {}
@@ -298,7 +346,7 @@ function initManifestPush() {
298
346
  window.Alpine.store('push', store);
299
347
  }
300
348
 
301
- const Push = manifestNativePlugin('PushNotifications');
349
+ const Push = manifestNativeUse('PushNotifications', 'register');
302
350
  if (Push) {
303
351
  try { Push.addListener('registration', t => { store.token = t && t.value; _manifestPushTokenHandlers.forEach(fn => { try { fn(store.token); } catch (e) {} }); }); } catch (e) {}
304
352
  try { Push.addListener('pushNotificationReceived', n => { _manifestPushReceiveHandlers.forEach(fn => { try { fn(n); } catch (e) {} }); }); } catch (e) {}
@@ -307,13 +355,13 @@ function initManifestPush() {
307
355
 
308
356
  window.Alpine.magic('push', () => ({
309
357
  async request() {
310
- const P = manifestNativePlugin('PushNotifications');
358
+ const P = manifestNativeUse('PushNotifications', 'requestPermissions');
311
359
  if (P) { try { const r = await P.requestPermissions(); const p = manifestPushMapPermission(r && r.receive); window.Alpine.store('push').permission = p; return p; } catch (e) { return 'denied'; } }
312
360
  if (typeof Notification !== 'undefined' && Notification.requestPermission) { try { const p = manifestPushMapPermission(await Notification.requestPermission()); window.Alpine.store('push').permission = p; return p; } catch (e) { return 'denied'; } }
313
361
  return 'unsupported';
314
362
  },
315
363
  async register() {
316
- const P = manifestNativePlugin('PushNotifications');
364
+ const P = manifestNativeUse('PushNotifications', 'register');
317
365
  if (P) { try { await P.register(); } catch (e) {} }
318
366
  },
319
367
  onToken(fn) { if (typeof fn === 'function') _manifestPushTokenHandlers.push(fn); },
@@ -341,7 +389,7 @@ function initManifestApp() {
341
389
  const visible = (typeof document !== 'undefined') ? document.visibilityState !== 'hidden' : true;
342
390
  if (!window.Alpine.store('app')) window.Alpine.store('app', { active: visible });
343
391
 
344
- const App = manifestNativePlugin('App');
392
+ const App = manifestNativeUse('App', 'getState');
345
393
  if (App) {
346
394
  try { App.addListener('appStateChange', s => manifestAppSetActive(s && s.isActive)); } catch (e) {}
347
395
  } else if (typeof document !== 'undefined') {
@@ -369,22 +417,22 @@ function manifestVibrate(pattern) {
369
417
  function initManifestHaptics() {
370
418
  window.Alpine.magic('haptics', () => ({
371
419
  impact(style) {
372
- const H = manifestNativePlugin('Haptics');
420
+ const H = manifestNativeUse('Haptics', 'impact');
373
421
  if (H) { try { return H.impact({ style: style || 'MEDIUM' }); } catch (e) {} }
374
422
  manifestVibrate(10); return Promise.resolve();
375
423
  },
376
424
  notification(type) {
377
- const H = manifestNativePlugin('Haptics');
425
+ const H = manifestNativeUse('Haptics', 'notification');
378
426
  if (H) { try { return H.notification({ type: type || 'SUCCESS' }); } catch (e) {} }
379
427
  manifestVibrate([10, 40, 10]); return Promise.resolve();
380
428
  },
381
429
  selection() {
382
- const H = manifestNativePlugin('Haptics');
430
+ const H = manifestNativeUse('Haptics', 'selectionChanged');
383
431
  if (H) { try { return H.selectionChanged(); } catch (e) {} }
384
432
  manifestVibrate(5); return Promise.resolve();
385
433
  },
386
434
  vibrate(ms) {
387
- const H = manifestNativePlugin('Haptics');
435
+ const H = manifestNativeUse('Haptics', 'vibrate');
388
436
  if (H) { try { return H.vibrate({ duration: ms || 300 }); } catch (e) {} }
389
437
  manifestVibrate(ms || 300); return Promise.resolve();
390
438
  }
@@ -392,33 +440,57 @@ function initManifestHaptics() {
392
440
  }
393
441
 
394
442
 
395
- // $biometric — Face ID / Touch ID. Native: a Capacitor biometric plugin
396
- // (BiometricAuth or NativeBiometric; method shapes differ, adapted below). The web
397
- // has no equivalent one-shot verify (WebAuthn is a separate server ceremony), so
398
- // available() is false and verify() reports unsupported — a genuine native win.
443
+ // $biometric — Face ID / Touch ID. Native: @aparajita/capacitor-biometric-auth
444
+ // (BiometricAuthNative) or @capgo/capacitor-native-biometric (NativeBiometric).
445
+ // No web equivalent: available() is false and verify() reports unsupported.
446
+
447
+ const MANIFEST_BIOMETRIC_PLUGINS = ['BiometricAuthNative', 'BiometricAuth', 'NativeBiometric'];
399
448
 
449
+ // Native only: bundled web builds expose plugin web stubs that always "verify".
400
450
  function manifestBiometricPlugin() {
401
- return manifestNativePlugin('BiometricAuth') || manifestNativePlugin('NativeBiometric') || null;
451
+ if (!manifestNativeIsNative()) return null;
452
+ for (const name of MANIFEST_BIOMETRIC_PLUGINS) {
453
+ const plugin = manifestNativePlugin(name);
454
+ if (plugin) return { name, plugin };
455
+ }
456
+ return null;
457
+ }
458
+
459
+ function manifestBiometricWarn(msg, e) {
460
+ try { console.warn('[Manifest] $biometric: ' + msg, e || ''); } catch (_) {}
402
461
  }
403
462
 
404
463
  async function manifestBiometricAvailable() {
405
- const B = manifestBiometricPlugin();
406
- if (!B) return false;
464
+ const found = manifestBiometricPlugin();
465
+ if (!found) return false;
466
+ const { name, plugin: B } = found;
407
467
  try {
408
- if (typeof B.checkBiometry === 'function') { const r = await B.checkBiometry(); return !!(r && r.isAvailable); }
409
- if (typeof B.isAvailable === 'function') { const r = await B.isAvailable(); return !!(r && r.isAvailable !== false); }
410
- } catch (e) {}
468
+ if (manifestNativeHas(name, B, 'checkBiometry')) { const r = await B.checkBiometry(); return !!(r && r.isAvailable); }
469
+ if (manifestNativeHas(name, B, 'isAvailable')) { const r = await B.isAvailable(); return !!(r && r.isAvailable !== false); }
470
+ } catch (e) { manifestBiometricWarn('availability check failed', e); return false; }
471
+ manifestBiometricWarn(name + ' has no checkBiometry() or isAvailable()');
411
472
  return false;
412
473
  }
413
474
 
414
475
  async function manifestBiometricVerify(opts) {
415
476
  const o = opts || {};
416
- const B = manifestBiometricPlugin();
417
- if (!B) return { verified: false, error: 'unsupported' };
477
+ const found = manifestBiometricPlugin();
478
+ if (!found) return { verified: false, error: 'unsupported' };
479
+ const { name, plugin: B } = found;
418
480
  try {
419
- if (typeof B.authenticate === 'function') { await B.authenticate({ reason: o.reason, title: o.title, subtitle: o.subtitle, cancelTitle: o.cancelTitle }); return { verified: true }; }
420
- if (typeof B.verifyIdentity === 'function') { await B.verifyIdentity({ reason: o.reason, title: o.title, subtitle: o.subtitle }); return { verified: true }; }
421
- } catch (e) { return { verified: false, error: (e && (e.message || e.code)) || 'failed' }; }
481
+ // aparajita: native method + its own option names
482
+ if (manifestNativeHas(name, B, 'internalAuthenticate') || manifestNativeHas(name, B, 'authenticate')) {
483
+ const args = { reason: o.reason, cancelTitle: o.cancelTitle, iosFallbackTitle: o.fallbackTitle, androidTitle: o.title, androidSubtitle: o.subtitle, allowDeviceCredential: o.allowDeviceCredential };
484
+ if (manifestNativeHas(name, B, 'internalAuthenticate')) await B.internalAuthenticate(args);
485
+ else await B.authenticate(args);
486
+ return { verified: true };
487
+ }
488
+ if (manifestNativeHas(name, B, 'verifyIdentity')) {
489
+ await B.verifyIdentity({ reason: o.reason, description: o.reason, title: o.title, subtitle: o.subtitle, negativeButtonText: o.cancelTitle, useFallback: !!o.allowDeviceCredential, fallbackTitle: o.fallbackTitle });
490
+ return { verified: true };
491
+ }
492
+ } catch (e) { return { verified: false, error: (e && (e.message || e.code)) || 'failed', code: (e && e.code) || undefined }; }
493
+ manifestBiometricWarn(name + ' has no supported verify method');
422
494
  return { verified: false, error: 'unsupported' };
423
495
  }
424
496
 
@@ -471,7 +543,7 @@ function manifestCameraWeb(opts) {
471
543
 
472
544
  function manifestCameraPhoto(opts) {
473
545
  const o = opts || {};
474
- const Cam = manifestNativePlugin('Camera');
546
+ const Cam = manifestNativeUse('Camera', 'getPhoto');
475
547
  if (Cam) {
476
548
  return Cam.getPhoto({ resultType: 'dataUrl', source: o.source === 'photos' ? 'PHOTOS' : 'CAMERA', quality: o.quality || 90 })
477
549
  .then(r => ({ dataUrl: r && (r.dataUrl || r.webPath), format: r && r.format }))
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "manifest.appwrite.auth.js": "sha384-dsIWnH0qCAGSzAXMGrd8fvOrVl4tOIE6j5tC35JPTrQXZ56qLD36ARyvhR7vi3Id",
3
- "manifest.appwrite.data.js": "sha384-DmpgWkBF02iuwOvFEq/0rpjHm5ts9F4Y961J8h6+gT2WhyGm3Ryp9Pgk0D/CWDki",
3
+ "manifest.appwrite.data.js": "sha384-ctnqsfCC4EDugNArRonGeZMTQn2iwksugU0309QM6l0uh6JJZOaU/Gsvvp8JrmZ3",
4
4
  "manifest.appwrite.presences.js": "sha384-JyVDWFT69b7Svq8J8p9tNWe2v9PZNQBY5UyhtOl1vnuHFAX0CRQ3MA9SiKq5wzNh",
5
5
  "manifest.bindings.js": "sha384-lPtMf8Ykaic9gNVZKq8Scia1881ZeMAyxer3WCIkUNwwrgzGBWto1OXADUwjvORF",
6
6
  "manifest.charts.js": "sha384-G+huD7UvjF9zDovdVVlh4I5X1Mq4oNnJfoFg4NnGdP3R/4Skq9hy5cGuHNYLxDYm",
@@ -11,24 +11,24 @@
11
11
  "manifest.combobox.js": "sha384-MILTkrgsTzsYUjGo4ZuADqV85Xs4GmfB4xexfe9vbJMIoQTZ42W4zs+DTaVSPEsx",
12
12
  "manifest.components.js": "sha384-BUFA8h+G5Plr82sV7bKzO42pPoiRZwRQcKj1lt2ROFiKjjlkayU/vmkAFqEsdBA+",
13
13
  "manifest.computed.js": "sha384-GYML/e81gQfN+iDbqhPUgny8bINCyL+SrbTofoGk4hlCnLLjv51mCv42JMqq+ku5",
14
- "manifest.data.js": "sha384-viViXTPQiJr7QNSx9Ln+uMXyNif1EK10JAmyPqeW4I6FrNtUu5MgKt04sa7LkABg",
14
+ "manifest.data.js": "sha384-ryR3dpYNybgJ8GONlEhkB0myOYz7fhGdnjEpXvKXcCQJUjZQaZbX7J8caPWv7UsN",
15
15
  "manifest.datepicker.js": "sha384-2XwFFnS4Bg5FDvG/v8x6KUvrjcBQEOYyTxynX2K/D/Qb3PI6+Y04lEwCnVDJLzwL",
16
16
  "manifest.defer.js": "sha384-pMRGqQKiEoJhpUcD40QeM4jEHau7sz9Xn/YCpiUamT1T4xJBZDSiYX+xnVrSWoq+",
17
- "manifest.device.js": "sha384-g+txmrEszYtuOvRelTp/c1wSNR2AtllVI9rx5NPPD7DzhVUnMNughonZykv2xlBW",
17
+ "manifest.device.js": "sha384-cm59lFxLFOu+DzQGkTUax0lfjZIeEz21vnk26c6nN0+/7irQwzVGt9u545BQjgzM",
18
18
  "manifest.dropdowns.js": "sha384-acufUps7R9WjqB4XXW5JLACqniuHJGM4CNTb2rF4B2Q/jv7ZRmsAwfr2McanOz3s",
19
19
  "manifest.edit.js": "sha384-MOgQNdNLeHijhR0u2W6ntoOulKFglsATCFDqkinesOU15tHoILk/JNzHKkGi5dUG",
20
20
  "manifest.export.js": "sha384-Rx6XvqBZc4apzP/FwcDr6yIln4/zNa08Q4ZqBZcEqUz1aNZQfl4h0+Ae7mqCQyuG",
21
21
  "manifest.icons.js": "sha384-kKOINpX70YzNO3FvkV6umrmO3f0KIu0DNbwB+ej55a7y/lhU7v/myQtXL0bwCTbt",
22
22
  "manifest.localization.js": "sha384-EImsIw8Z3l13V5eH9ZJF/yfsOfIvR1oPRNSArANkPji7V8O3IRf6zMwsNiAGj3Hk",
23
23
  "manifest.markdown.js": "sha384-heyDR40LXHaAmAuA+fa381uFwShk1oAqpFFY6E2Qq4T3VN0TQfRqyc++MF40K5Hj",
24
- "manifest.native.js": "sha384-g+txmrEszYtuOvRelTp/c1wSNR2AtllVI9rx5NPPD7DzhVUnMNughonZykv2xlBW",
24
+ "manifest.native.js": "sha384-cm59lFxLFOu+DzQGkTUax0lfjZIeEz21vnk26c6nN0+/7irQwzVGt9u545BQjgzM",
25
25
  "manifest.payments.js": "sha384-8pofk2zhxtapPMpwnFyTelPMrYhShNhDCE722uQh/EYFvZFuPLmSdgunqsarVQjv",
26
26
  "manifest.rich.text.js": "sha384-ZBV9jXmu2nmprEHicddRE/m7nR4RPZtVc969Jte0+EJYBcVph7itkjcgN/61PyeC",
27
27
  "manifest.router.js": "sha384-JcKnEb0E9azl6DVSN1DQMysyZOpGkskSQREjD947sMEs87nFEeZpCigB0iq8psz5",
28
28
  "manifest.slides.js": "sha384-3uRTkyK9XPLmnxI2+igZlpi4EyPlU/7IHj5j3BZJJ2KN455vXyk99fiXV3feO/XY",
29
29
  "manifest.status.js": "sha384-MBO345ab7H6Uq1X2dZLt4Ge1Gcra+Z2qYApvlAeIV8CJhDDJrz2FPXpp+Kcp8zcM",
30
30
  "manifest.svg.js": "sha384-qr/8H9+2cIhSXNbRWRtIdrDlTOxKlNJqwEefHvCYoiBNW/kKhPTFa109Orv/EBnb",
31
- "manifest.sw.js": "sha384-XGf/EbAhHXR7BCSlP6QFnCWPNxuwxyHgU/I8DdxEYw0UPQaZEGDlUILxExqx+ae+",
31
+ "manifest.sw.js": "sha384-9+0Y6SDUeQcjS5w5yr2OjMbyuM2uc3U4htcj+f+GUHBFL4X7zxm4u7fekQwAzyxI",
32
32
  "manifest.tabs.js": "sha384-OIIcyaQnGS0eNYk4zgAFEDboNYect7THvUd9D0V6nqCeDOxQcoiqLrBEmmWVTTet",
33
33
  "manifest.tailwind.js": "sha384-r/Jvqb+Piz21jUts10BbVAJoKkShOdJRVFpywyi4HPIi/OfOJq5CtPyWAPbdkMgt",
34
34
  "manifest.toasts.js": "sha384-3zkWxzw693pxg3QDFsoYCA3CqYaLKmBaMArULXODD66Utolpewl0OAeTB92aWUVv",
@@ -15,6 +15,25 @@ function manifestNativePlugin(name) {
15
15
  const cap = window.Capacitor;
16
16
  return (cap && cap.Plugins && cap.Plugins[name]) || null;
17
17
  }
18
+ // Whether a plugin exposes a native method. Bundled apps get a registerPlugin
19
+ // Proxy where every property is a function, so prefer the native header; a
20
+ // header list without this plugin means it isn't installed natively.
21
+ function manifestNativeHas(name, plugin, method) {
22
+ const headers = window.Capacitor && window.Capacitor.PluginHeaders;
23
+ if (Array.isArray(headers)) {
24
+ const h = headers.find(x => x && x.name === name);
25
+ if (!h) return false;
26
+ if (Array.isArray(h.methods)) return h.methods.some(m => m && m.name === method);
27
+ }
28
+ return !!plugin && typeof plugin[method] === 'function';
29
+ }
30
+ // A plugin with a native method, or null. Web builds use Manifest's own fallbacks,
31
+ // not plugin web stubs, so bundled and plain web builds behave the same.
32
+ function manifestNativeUse(name, method) {
33
+ if (!manifestNativeIsNative()) return null;
34
+ const plugin = manifestNativePlugin(name);
35
+ return plugin && manifestNativeHas(name, plugin, method) ? plugin : null;
36
+ }
18
37
  function manifestNativePlatform() {
19
38
  const cap = window.Capacitor;
20
39
  if (cap && typeof cap.getPlatform === 'function') { try { return cap.getPlatform(); } catch (e) {} }
@@ -96,7 +115,7 @@ function manifestShareWeb(payload) {
96
115
  }
97
116
  function manifestShare(opts) {
98
117
  const payload = opts || {};
99
- const Share = manifestNativePlugin('Share');
118
+ const Share = manifestNativeUse('Share', 'share');
100
119
  if (Share) {
101
120
  // When the native sheet is present, never fall through to the web sheet
102
121
  // (that would double-prompt); report the native outcome instead.
@@ -128,7 +147,7 @@ function manifestNativeApplyOnline(connected) {
128
147
  } catch (e) {}
129
148
  }
130
149
  function initManifestNativeNetwork() {
131
- const Network = manifestNativePlugin('Network');
150
+ const Network = manifestNativeUse('Network', 'getStatus');
132
151
  if (!Network) return;
133
152
  try { Network.getStatus().then(s => manifestNativeApplyOnline(s && s.connected)).catch(() => {}); } catch (e) {}
134
153
  try { Network.addListener('networkStatusChange', s => manifestNativeApplyOnline(s && s.connected)); } catch (e) {}
@@ -137,9 +156,8 @@ function initManifestNativeNetwork() {
137
156
 
138
157
  // $secure — Keychain/Keystore-backed key/value storage (native) with a
139
158
  // namespaced localStorage fallback on the web. Intended for session tokens etc.
140
- // Native: install a Capacitor secure-storage plugin registered as
141
- // window.Capacitor.Plugins.SecureStorage; if its method shape differs from the
142
- // adapters below, override with $secure.use(adapter). Verify on device.
159
+ // Native: a plugin registered as SecureStorage (@aparajita/capacitor-secure-storage,
160
+ // or any getItem/setItem or get/set shape); $secure.use(adapter) overrides.
143
161
  // The web fallback is NOT encrypted — it degrades honestly, not securely.
144
162
 
145
163
  const MANIFEST_SECURE_NS = 'mnfst:';
@@ -158,33 +176,63 @@ function manifestSecureWebBackend() {
158
176
  };
159
177
  }
160
178
 
161
- // Adapts the two common Capacitor secure-storage plugin shapes: localStorage-like
162
- // (getItem/setItem/removeItem) or object-arg (get/set/remove with {key,value}).
163
- function manifestSecureNativeBackend(plugin) {
164
- const itemApi = typeof plugin.getItem === 'function';
179
+ // aparajita's default prefix, so keys interop with its own getItem/setItem
180
+ const MANIFEST_SECURE_NATIVE_PREFIX = 'capacitor-storage_';
181
+
182
+ function manifestSecureFail(op, e) {
183
+ try { console.error('[Manifest] $secure.' + op + ' failed', e); } catch (_) {}
184
+ throw e instanceof Error ? e : new Error('$secure.' + op + ' failed: ' + ((e && (e.message || e.code)) || e));
185
+ }
186
+
187
+ // Adapts three native shapes: aparajita (internalGetItem…), localStorage-like
188
+ // (getItem/setItem/removeItem) and object-arg (get/set/remove with {key,value}).
189
+ function manifestSecureNativeBackend(plugin, name) {
190
+ const n = name || 'SecureStorage';
191
+ const has = (m) => manifestNativeHas(n, plugin, m);
192
+ const P = MANIFEST_SECURE_NATIVE_PREFIX;
193
+ const shape = has('internalGetItem') ? 'internal' : has('getItem') ? 'item' : has('get') ? 'object' : null;
194
+ const call = async (op, fn) => {
195
+ if (!shape) manifestSecureFail(op, new Error(n + ' plugin has no supported API; use $secure.use(adapter)'));
196
+ try { return await fn(); } catch (e) { manifestSecureFail(op, e); }
197
+ };
165
198
  return {
166
- async get(key) {
167
- try {
168
- if (itemApi) { const v = await plugin.getItem(key); return v == null ? null : v; }
169
- const r = await plugin.get({ key }); return r && typeof r.value !== 'undefined' ? r.value : (r == null ? null : r);
170
- } catch (e) { return null; }
171
- },
172
- async set(key, value) {
173
- try { if (itemApi) await plugin.setItem(key, String(value)); else await plugin.set({ key, value: String(value) }); } catch (e) {}
174
- },
175
- async remove(key) {
176
- try { if (typeof plugin.removeItem === 'function') await plugin.removeItem(key); else await plugin.remove({ key }); } catch (e) {}
177
- },
178
- async keys() { try { const r = await plugin.keys(); return Array.isArray(r) ? r : (r && (r.keys || r.value)) || []; } catch (e) { return []; } },
179
- async clear() { try { await plugin.clear(); } catch (e) {} }
199
+ get: (key) => call('get', async () => {
200
+ if (key == null || key === '') return null;
201
+ if (shape === 'internal') { const r = await plugin.internalGetItem({ prefixedKey: P + key }); return r && r.data != null ? r.data : null; }
202
+ if (shape === 'item') { const v = await plugin.getItem(key); return v == null ? null : v; }
203
+ const r = await plugin.get({ key }); return r && typeof r.value !== 'undefined' ? r.value : (r == null ? null : r);
204
+ }),
205
+ set: (key, value) => call('set', async () => {
206
+ if (key == null || key === '') throw new Error('No key provided');
207
+ if (shape === 'internal') await plugin.internalSetItem({ prefixedKey: P + key, data: String(value) });
208
+ else if (shape === 'item') await plugin.setItem(key, String(value));
209
+ else await plugin.set({ key, value: String(value) });
210
+ }),
211
+ remove: (key) => call('remove', async () => {
212
+ if (key == null || key === '') return;
213
+ if (shape === 'internal') await plugin.internalRemoveItem({ prefixedKey: P + key });
214
+ else if (shape === 'item') {
215
+ if (!has('removeItem')) throw new Error(n + ' plugin has no removeItem(); use $secure.use(adapter)');
216
+ await plugin.removeItem(key);
217
+ }
218
+ else await plugin.remove({ key });
219
+ }),
220
+ keys: () => call('keys', async () => {
221
+ if (shape === 'internal') { const r = await plugin.internalGetPrefixedKeys({ prefix: P }); return ((r && r.keys) || []).map(k => k.slice(P.length)); }
222
+ const r = await plugin.keys(); return Array.isArray(r) ? r : (r && (r.keys || r.value)) || [];
223
+ }),
224
+ clear: () => call('clear', async () => {
225
+ if (shape === 'internal') await plugin.internalClearItemsWithPrefix({ prefix: P });
226
+ else await plugin.clear();
227
+ })
180
228
  };
181
229
  }
182
230
 
183
231
  let _manifestSecureBackend = null;
184
232
  function manifestSecureBackend() {
185
233
  if (_manifestSecureBackend) return _manifestSecureBackend;
186
- const plugin = manifestNativePlugin('SecureStorage');
187
- _manifestSecureBackend = plugin ? manifestSecureNativeBackend(plugin) : manifestSecureWebBackend();
234
+ const plugin = manifestNativeIsNative() ? manifestNativePlugin('SecureStorage') : null;
235
+ _manifestSecureBackend = plugin ? manifestSecureNativeBackend(plugin, 'SecureStorage') : manifestSecureWebBackend();
188
236
  return _manifestSecureBackend;
189
237
  }
190
238
 
@@ -251,7 +299,7 @@ function initManifestLinks() {
251
299
  get last() { const s = window.Alpine.store('links'); return s ? s.last : null; }
252
300
  }));
253
301
 
254
- const App = manifestNativePlugin('App');
302
+ const App = manifestNativeUse('App', 'getLaunchUrl');
255
303
  if (App) {
256
304
  try { App.addListener('appUrlOpen', (data) => { if (data && data.url) manifestHandleUrl(data.url); }); } catch (e) {}
257
305
  try { App.getLaunchUrl().then(r => { if (r && r.url) manifestHandleUrl(r.url); }).catch(() => {}); } catch (e) {}
@@ -298,7 +346,7 @@ function initManifestPush() {
298
346
  window.Alpine.store('push', store);
299
347
  }
300
348
 
301
- const Push = manifestNativePlugin('PushNotifications');
349
+ const Push = manifestNativeUse('PushNotifications', 'register');
302
350
  if (Push) {
303
351
  try { Push.addListener('registration', t => { store.token = t && t.value; _manifestPushTokenHandlers.forEach(fn => { try { fn(store.token); } catch (e) {} }); }); } catch (e) {}
304
352
  try { Push.addListener('pushNotificationReceived', n => { _manifestPushReceiveHandlers.forEach(fn => { try { fn(n); } catch (e) {} }); }); } catch (e) {}
@@ -307,13 +355,13 @@ function initManifestPush() {
307
355
 
308
356
  window.Alpine.magic('push', () => ({
309
357
  async request() {
310
- const P = manifestNativePlugin('PushNotifications');
358
+ const P = manifestNativeUse('PushNotifications', 'requestPermissions');
311
359
  if (P) { try { const r = await P.requestPermissions(); const p = manifestPushMapPermission(r && r.receive); window.Alpine.store('push').permission = p; return p; } catch (e) { return 'denied'; } }
312
360
  if (typeof Notification !== 'undefined' && Notification.requestPermission) { try { const p = manifestPushMapPermission(await Notification.requestPermission()); window.Alpine.store('push').permission = p; return p; } catch (e) { return 'denied'; } }
313
361
  return 'unsupported';
314
362
  },
315
363
  async register() {
316
- const P = manifestNativePlugin('PushNotifications');
364
+ const P = manifestNativeUse('PushNotifications', 'register');
317
365
  if (P) { try { await P.register(); } catch (e) {} }
318
366
  },
319
367
  onToken(fn) { if (typeof fn === 'function') _manifestPushTokenHandlers.push(fn); },
@@ -341,7 +389,7 @@ function initManifestApp() {
341
389
  const visible = (typeof document !== 'undefined') ? document.visibilityState !== 'hidden' : true;
342
390
  if (!window.Alpine.store('app')) window.Alpine.store('app', { active: visible });
343
391
 
344
- const App = manifestNativePlugin('App');
392
+ const App = manifestNativeUse('App', 'getState');
345
393
  if (App) {
346
394
  try { App.addListener('appStateChange', s => manifestAppSetActive(s && s.isActive)); } catch (e) {}
347
395
  } else if (typeof document !== 'undefined') {
@@ -369,22 +417,22 @@ function manifestVibrate(pattern) {
369
417
  function initManifestHaptics() {
370
418
  window.Alpine.magic('haptics', () => ({
371
419
  impact(style) {
372
- const H = manifestNativePlugin('Haptics');
420
+ const H = manifestNativeUse('Haptics', 'impact');
373
421
  if (H) { try { return H.impact({ style: style || 'MEDIUM' }); } catch (e) {} }
374
422
  manifestVibrate(10); return Promise.resolve();
375
423
  },
376
424
  notification(type) {
377
- const H = manifestNativePlugin('Haptics');
425
+ const H = manifestNativeUse('Haptics', 'notification');
378
426
  if (H) { try { return H.notification({ type: type || 'SUCCESS' }); } catch (e) {} }
379
427
  manifestVibrate([10, 40, 10]); return Promise.resolve();
380
428
  },
381
429
  selection() {
382
- const H = manifestNativePlugin('Haptics');
430
+ const H = manifestNativeUse('Haptics', 'selectionChanged');
383
431
  if (H) { try { return H.selectionChanged(); } catch (e) {} }
384
432
  manifestVibrate(5); return Promise.resolve();
385
433
  },
386
434
  vibrate(ms) {
387
- const H = manifestNativePlugin('Haptics');
435
+ const H = manifestNativeUse('Haptics', 'vibrate');
388
436
  if (H) { try { return H.vibrate({ duration: ms || 300 }); } catch (e) {} }
389
437
  manifestVibrate(ms || 300); return Promise.resolve();
390
438
  }
@@ -392,33 +440,57 @@ function initManifestHaptics() {
392
440
  }
393
441
 
394
442
 
395
- // $biometric — Face ID / Touch ID. Native: a Capacitor biometric plugin
396
- // (BiometricAuth or NativeBiometric; method shapes differ, adapted below). The web
397
- // has no equivalent one-shot verify (WebAuthn is a separate server ceremony), so
398
- // available() is false and verify() reports unsupported — a genuine native win.
443
+ // $biometric — Face ID / Touch ID. Native: @aparajita/capacitor-biometric-auth
444
+ // (BiometricAuthNative) or @capgo/capacitor-native-biometric (NativeBiometric).
445
+ // No web equivalent: available() is false and verify() reports unsupported.
446
+
447
+ const MANIFEST_BIOMETRIC_PLUGINS = ['BiometricAuthNative', 'BiometricAuth', 'NativeBiometric'];
399
448
 
449
+ // Native only: bundled web builds expose plugin web stubs that always "verify".
400
450
  function manifestBiometricPlugin() {
401
- return manifestNativePlugin('BiometricAuth') || manifestNativePlugin('NativeBiometric') || null;
451
+ if (!manifestNativeIsNative()) return null;
452
+ for (const name of MANIFEST_BIOMETRIC_PLUGINS) {
453
+ const plugin = manifestNativePlugin(name);
454
+ if (plugin) return { name, plugin };
455
+ }
456
+ return null;
457
+ }
458
+
459
+ function manifestBiometricWarn(msg, e) {
460
+ try { console.warn('[Manifest] $biometric: ' + msg, e || ''); } catch (_) {}
402
461
  }
403
462
 
404
463
  async function manifestBiometricAvailable() {
405
- const B = manifestBiometricPlugin();
406
- if (!B) return false;
464
+ const found = manifestBiometricPlugin();
465
+ if (!found) return false;
466
+ const { name, plugin: B } = found;
407
467
  try {
408
- if (typeof B.checkBiometry === 'function') { const r = await B.checkBiometry(); return !!(r && r.isAvailable); }
409
- if (typeof B.isAvailable === 'function') { const r = await B.isAvailable(); return !!(r && r.isAvailable !== false); }
410
- } catch (e) {}
468
+ if (manifestNativeHas(name, B, 'checkBiometry')) { const r = await B.checkBiometry(); return !!(r && r.isAvailable); }
469
+ if (manifestNativeHas(name, B, 'isAvailable')) { const r = await B.isAvailable(); return !!(r && r.isAvailable !== false); }
470
+ } catch (e) { manifestBiometricWarn('availability check failed', e); return false; }
471
+ manifestBiometricWarn(name + ' has no checkBiometry() or isAvailable()');
411
472
  return false;
412
473
  }
413
474
 
414
475
  async function manifestBiometricVerify(opts) {
415
476
  const o = opts || {};
416
- const B = manifestBiometricPlugin();
417
- if (!B) return { verified: false, error: 'unsupported' };
477
+ const found = manifestBiometricPlugin();
478
+ if (!found) return { verified: false, error: 'unsupported' };
479
+ const { name, plugin: B } = found;
418
480
  try {
419
- if (typeof B.authenticate === 'function') { await B.authenticate({ reason: o.reason, title: o.title, subtitle: o.subtitle, cancelTitle: o.cancelTitle }); return { verified: true }; }
420
- if (typeof B.verifyIdentity === 'function') { await B.verifyIdentity({ reason: o.reason, title: o.title, subtitle: o.subtitle }); return { verified: true }; }
421
- } catch (e) { return { verified: false, error: (e && (e.message || e.code)) || 'failed' }; }
481
+ // aparajita: native method + its own option names
482
+ if (manifestNativeHas(name, B, 'internalAuthenticate') || manifestNativeHas(name, B, 'authenticate')) {
483
+ const args = { reason: o.reason, cancelTitle: o.cancelTitle, iosFallbackTitle: o.fallbackTitle, androidTitle: o.title, androidSubtitle: o.subtitle, allowDeviceCredential: o.allowDeviceCredential };
484
+ if (manifestNativeHas(name, B, 'internalAuthenticate')) await B.internalAuthenticate(args);
485
+ else await B.authenticate(args);
486
+ return { verified: true };
487
+ }
488
+ if (manifestNativeHas(name, B, 'verifyIdentity')) {
489
+ await B.verifyIdentity({ reason: o.reason, description: o.reason, title: o.title, subtitle: o.subtitle, negativeButtonText: o.cancelTitle, useFallback: !!o.allowDeviceCredential, fallbackTitle: o.fallbackTitle });
490
+ return { verified: true };
491
+ }
492
+ } catch (e) { return { verified: false, error: (e && (e.message || e.code)) || 'failed', code: (e && e.code) || undefined }; }
493
+ manifestBiometricWarn(name + ' has no supported verify method');
422
494
  return { verified: false, error: 'unsupported' };
423
495
  }
424
496
 
@@ -471,7 +543,7 @@ function manifestCameraWeb(opts) {
471
543
 
472
544
  function manifestCameraPhoto(opts) {
473
545
  const o = opts || {};
474
- const Cam = manifestNativePlugin('Camera');
546
+ const Cam = manifestNativeUse('Camera', 'getPhoto');
475
547
  if (Cam) {
476
548
  return Cam.getPhoto({ resultType: 'dataUrl', source: o.source === 'photos' ? 'PHOTOS' : 'CAMERA', quality: o.quality || 90 })
477
549
  .then(r => ({ dataUrl: r && (r.dataUrl || r.webPath), format: r && r.format }))
@@ -16,7 +16,7 @@
16
16
 
17
17
  // Build stamps the published version here; unstamped (dev) falls back to the
18
18
  // registration URL's `v`, then 'dev'.
19
- const BUILD_VERSION = '0.5.249';
19
+ const BUILD_VERSION = '0.5.251';
20
20
  const CACHE_PREFIX = 'mnfst-sw:';
21
21
  const META_CACHE = CACHE_PREFIX + 'meta';
22
22
  // Sized to hold a full precache.json warm-up (PRECACHE_MAX_ENTRIES server-side)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mnfst",
3
- "version": "0.5.249",
3
+ "version": "0.5.251",
4
4
  "private": false,
5
5
  "workspaces": [
6
6
  "templates/starter",