mnfst 0.5.224 → 0.5.225

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.
@@ -70,6 +70,7 @@ async function getAppwriteConfig() {
70
70
 
71
71
  const magicEnabled = authMethods.includes("magic");
72
72
  const otpEnabled = authMethods.includes("otp");
73
+ const phoneEnabled = authMethods.includes("phone");
73
74
  const oauthEnabled = authMethods.includes("oauth");
74
75
 
75
76
  // Teams (presence of teams object enables it)
@@ -92,7 +93,7 @@ async function getAppwriteConfig() {
92
93
  : toBool(appwriteConfig.auth.teams.authenticated);
93
94
 
94
95
  // Guest upgrade: preserve the anonymous account + teams on sign-in (magic/oauth;
95
- // OTP can't convert anonymous accounts). Defaults to guestTeams.
96
+ // email/phone OTP can't convert anonymous accounts). Defaults to guestTeams.
96
97
  const guestUpgrade = appwriteConfig.auth?.guestUpgrade !== undefined
97
98
  ? toBool(appwriteConfig.auth.guestUpgrade)
98
99
  : guestTeams;
@@ -135,6 +136,7 @@ async function getAppwriteConfig() {
135
136
  anonymous: guestAuto, // back-compat alias
136
137
  magic: magicEnabled,
137
138
  otp: otpEnabled,
139
+ phone: phoneEnabled,
138
140
  oauth: oauthEnabled,
139
141
  teams: teamsEnabled,
140
142
  permanentTeams: permanentTeams,
@@ -329,10 +331,10 @@ function initializeAuthStore() {
329
331
  error: null,
330
332
  magicLinkSent: false,
331
333
  magicLinkExpired: false,
332
- otpSent: false, // Email OTP: a code has been emailed and is awaiting entry
333
- otpExpired: false, // Email OTP: the entered code was wrong/expired
334
- otpPhrase: null, // Email OTP: security phrase to display (when enabled)
335
- _otpUserId: null, // Email OTP: userId returned by createEmailToken, used by verifyOTP
334
+ otpSent: false, // OTP (email/phone): a code has been sent and is awaiting entry
335
+ otpExpired: false, // OTP (email/phone): the entered code was wrong/expired
336
+ otpPhrase: null, // Email OTP: security phrase to display (when enabled; phone has none)
337
+ _otpUserId: null, // OTP: userId returned by createEmailToken/createPhoneToken, used by verifyOTP
336
338
  teams: [], // List of user's teams
337
339
  currentTeam: null, // Currently selected/active team
338
340
  _teamsPollInterval: null, // Interval ID for teams polling (deprecated, use realtime instead)
@@ -933,7 +935,7 @@ function initializeAuthStore() {
933
935
  this.magicLinkSent = false;
934
936
  this.magicLinkExpired = false;
935
937
 
936
- // Clear email OTP flags
938
+ // Clear OTP flags (email/phone)
937
939
  this.otpSent = false;
938
940
  this.otpExpired = false;
939
941
  this.otpPhrase = null;
@@ -6294,7 +6296,8 @@ window.ManifestAppwriteAuthMagicLinks = {
6294
6296
  /* Auth email OTP (one-time passcode) */
6295
6297
 
6296
6298
  // Two-step in-page flow (no redirect): createEmailOTP(email) emails a code + returns
6297
- // a userId, verifyOTP(code) creates the session.
6299
+ // a userId, verifyOTP(code) creates the session. verifyOTP/submitOTP are shared
6300
+ // with phone OTP (users.phone.js).
6298
6301
  // Gotcha: Appwrite can't convert an anonymous guest via OTP — a guest verifying an OTP
6299
6302
  // gets a fresh account (guest teams lost). Use magic links for guest upgrade.
6300
6303
 
@@ -6364,17 +6367,20 @@ function initializeEmailOTP() {
6364
6367
  this._appwrite = await config.getAppwriteClient();
6365
6368
  }
6366
6369
  if (!this._appwrite) {
6367
- return { success: false, error: 'Appwrite not configured' };
6370
+ this.error = 'Appwrite not configured';
6371
+ return { success: false, error: this.error };
6368
6372
  }
6369
6373
 
6370
6374
  // Don't allow OTP request if already signed in (non-anonymous)
6371
6375
  if (this.isAuthenticated && !this.isAnonymous) {
6372
- return { success: false, error: 'Already signed in. Please logout first.' };
6376
+ this.error = 'Already signed in. Please logout first.';
6377
+ return { success: false, error: this.error };
6373
6378
  }
6374
6379
 
6375
6380
  const appwriteConfig = await config.getAppwriteConfig();
6376
6381
  if (appwriteConfig && !appwriteConfig.otp) {
6377
- return { success: false, error: 'Email OTP authentication is not enabled' };
6382
+ this.error = 'Email OTP authentication is not enabled';
6383
+ return { success: false, error: this.error };
6378
6384
  }
6379
6385
 
6380
6386
  // OTP can't convert a guest — warn so lost guest teams aren't a surprise.
@@ -6384,10 +6390,8 @@ function initializeEmailOTP() {
6384
6390
 
6385
6391
  const account = this._appwrite.account;
6386
6392
  if (typeof account.createEmailToken !== 'function') {
6387
- return {
6388
- success: false,
6389
- error: 'Email OTP method not available. Please ensure you are using a recent Appwrite SDK.'
6390
- };
6393
+ this.error = 'Email OTP method not available. Please ensure you are using a recent Appwrite SDK.';
6394
+ return { success: false, error: this.error };
6391
6395
  }
6392
6396
 
6393
6397
  this.inProgress = true;
@@ -6433,7 +6437,8 @@ function initializeEmailOTP() {
6433
6437
  const { email, inputEl, dataObj } = resolveEmailInput(emailInputOrRef);
6434
6438
 
6435
6439
  if (!email || !email.trim()) {
6436
- return { success: false, error: 'Email is required' };
6440
+ this.error = 'Email is required';
6441
+ return { success: false, error: this.error };
6437
6442
  }
6438
6443
 
6439
6444
  const result = await this.createEmailOTP(email.trim(), options);
@@ -6607,6 +6612,197 @@ window.ManifestAppwriteAuthEmailOTP = {
6607
6612
  };
6608
6613
 
6609
6614
 
6615
+ /* Auth phone OTP (SMS one-time passcode) */
6616
+
6617
+ // Two-step in-page flow (no redirect): createPhoneOTP(phone) texts a code + returns
6618
+ // a userId, then the shared verifyOTP(code)/submitOTP() (users.otp.js) creates the
6619
+ // session. Phone numbers must be E.164 (+ country code).
6620
+ // Gotcha: like email OTP, Appwrite can't convert an anonymous guest via phone OTP —
6621
+ // a guest verifying a code gets a fresh account. Use magic links for guest upgrade.
6622
+
6623
+ function initializePhoneOTP() {
6624
+ if (typeof Alpine === 'undefined') {
6625
+ return;
6626
+ }
6627
+
6628
+ const config = window.ManifestAppwriteAuthConfig;
6629
+ if (!config) {
6630
+ return;
6631
+ }
6632
+
6633
+ // Resolve a phone from an input/selector/{ phone } object/string, or auto-find
6634
+ // the nearest tel input. Returns { phone, inputEl, dataObj }.
6635
+ function resolvePhoneInput(phoneInputOrRef) {
6636
+ let phone = null;
6637
+ let inputEl = null;
6638
+ let dataObj = null;
6639
+
6640
+ if (phoneInputOrRef === undefined || phoneInputOrRef === null) {
6641
+ let eventTarget = (typeof window !== 'undefined' && window.event) ? window.event.target : null;
6642
+ if (eventTarget) {
6643
+ const form = eventTarget.closest('form');
6644
+ const scope = form || eventTarget.parentElement;
6645
+ if (scope) {
6646
+ inputEl = scope.querySelector('input[type="tel"]');
6647
+ if (inputEl) phone = inputEl.value;
6648
+ }
6649
+ }
6650
+ if (!inputEl) {
6651
+ inputEl = document.querySelector('input[type="tel"]');
6652
+ if (inputEl) phone = inputEl.value;
6653
+ }
6654
+ } else if (typeof phoneInputOrRef === 'string') {
6655
+ try {
6656
+ const element = document.querySelector(phoneInputOrRef);
6657
+ if (element && element.tagName === 'INPUT' && element.type === 'tel') {
6658
+ inputEl = element;
6659
+ phone = element.value;
6660
+ } else {
6661
+ phone = phoneInputOrRef; // Treat as a direct phone string
6662
+ }
6663
+ } catch (e) {
6664
+ phone = phoneInputOrRef; // Invalid selector -> treat as phone string
6665
+ }
6666
+ } else if (phoneInputOrRef && typeof phoneInputOrRef === 'object') {
6667
+ if (phoneInputOrRef.tagName === 'INPUT' || phoneInputOrRef.matches?.('input[type="tel"]')) {
6668
+ inputEl = phoneInputOrRef;
6669
+ phone = inputEl.value;
6670
+ } else if ('phone' in phoneInputOrRef) {
6671
+ phone = phoneInputOrRef.phone;
6672
+ dataObj = phoneInputOrRef;
6673
+ }
6674
+ }
6675
+
6676
+ return { phone, inputEl, dataObj };
6677
+ }
6678
+
6679
+ const waitForStore = () => {
6680
+ const store = Alpine.store('auth');
6681
+ if (store && !store.createPhoneOTP) {
6682
+ // Step 1: text a passcode. Verification reuses verifyOTP/submitOTP.
6683
+ store.createPhoneOTP = async function (phone) {
6684
+ if (!this._appwrite) {
6685
+ this._appwrite = await config.getAppwriteClient();
6686
+ }
6687
+ if (!this._appwrite) {
6688
+ this.error = 'Appwrite not configured';
6689
+ return { success: false, error: this.error };
6690
+ }
6691
+
6692
+ // Don't allow OTP request if already signed in (non-anonymous)
6693
+ if (this.isAuthenticated && !this.isAnonymous) {
6694
+ this.error = 'Already signed in. Please logout first.';
6695
+ return { success: false, error: this.error };
6696
+ }
6697
+
6698
+ const appwriteConfig = await config.getAppwriteConfig();
6699
+ if (appwriteConfig && !appwriteConfig.phone) {
6700
+ this.error = 'Phone OTP authentication is not enabled';
6701
+ return { success: false, error: this.error };
6702
+ }
6703
+
6704
+ // Normalize to E.164: strip separators, require the country code.
6705
+ const normalized = String(phone).replace(/[\s().-]/g, '');
6706
+ if (!/^\+\d{6,15}$/.test(normalized)) {
6707
+ this.error = 'Phone number must be in international format, e.g. +14155550123';
6708
+ return { success: false, error: this.error };
6709
+ }
6710
+
6711
+ // Phone OTP can't convert a guest — warn so lost guest teams aren't a surprise.
6712
+ if (this.isAnonymous && appwriteConfig?.guestUpgrade) {
6713
+ console.warn('[Manifest Appwrite Auth] Phone OTP cannot upgrade a guest account (Appwrite limitation); the guest session and any guest-created teams will be replaced. Use magic links for guest upgrade.');
6714
+ }
6715
+
6716
+ const account = this._appwrite.account;
6717
+ if (typeof account.createPhoneToken !== 'function') {
6718
+ this.error = 'Phone OTP method not available. Please ensure you are using a recent Appwrite SDK.';
6719
+ return { success: false, error: this.error };
6720
+ }
6721
+
6722
+ this.inProgress = true;
6723
+ this.error = null;
6724
+ this.otpExpired = false;
6725
+
6726
+ try {
6727
+ const uniqueId = (window.Appwrite?.ID?.unique) ? window.Appwrite.ID.unique() : 'unique()';
6728
+ const token = await account.createPhoneToken(uniqueId, normalized);
6729
+
6730
+ // Stash the userId Appwrite assigned; verifyOTP needs it to complete login.
6731
+ this._otpUserId = token.userId;
6732
+ this.otpPhrase = null; // phone tokens have no security phrase
6733
+ this.otpSent = true;
6734
+ this.otpExpired = false;
6735
+ this.error = null;
6736
+
6737
+ window.dispatchEvent(new CustomEvent('manifest:auth:otp-sent', {
6738
+ detail: { phone: normalized }
6739
+ }));
6740
+
6741
+ return { success: true, message: 'OTP sent by SMS' };
6742
+ } catch (error) {
6743
+ // Appwrite returns 501 when phone auth/SMS isn't set up — surface an
6744
+ // actionable message rather than the raw error.
6745
+ const code = error.code || error.statusCode;
6746
+ const notEnabled = code === 501 || /not implemented/i.test(error.message || '');
6747
+ this.error = notEnabled
6748
+ ? 'Phone OTP is not enabled for this Appwrite project. Enable Phone under Auth → Settings and configure an SMS provider under Messaging.'
6749
+ : error.message;
6750
+ this.otpSent = false;
6751
+ this.otpExpired = false;
6752
+ return { success: false, error: this.error };
6753
+ } finally {
6754
+ this.inProgress = false;
6755
+ }
6756
+ };
6757
+
6758
+ // Convenience: resolve the phone from an input/selector/object/string and send.
6759
+ // Clears the phone input on success (mirrors sendEmailOTP).
6760
+ store.sendPhoneOTP = async function (phoneInputOrRef) {
6761
+ const { phone, inputEl, dataObj } = resolvePhoneInput(phoneInputOrRef);
6762
+
6763
+ if (!phone || !String(phone).trim()) {
6764
+ this.error = 'Phone number is required';
6765
+ return { success: false, error: this.error };
6766
+ }
6767
+
6768
+ const result = await this.createPhoneOTP(String(phone).trim());
6769
+
6770
+ if (result.success) {
6771
+ Promise.resolve().then(() => {
6772
+ if (inputEl) {
6773
+ inputEl.value = '';
6774
+ inputEl.dispatchEvent(new Event('input', { bubbles: true }));
6775
+ } else if (dataObj) {
6776
+ dataObj.phone = '';
6777
+ }
6778
+ });
6779
+ }
6780
+
6781
+ return result;
6782
+ };
6783
+ } else if (!store) {
6784
+ setTimeout(waitForStore, 50);
6785
+ }
6786
+ };
6787
+
6788
+ setTimeout(waitForStore, 100);
6789
+ }
6790
+
6791
+ // Initialize when Alpine is ready
6792
+ document.addEventListener('alpine:init', () => {
6793
+ try {
6794
+ initializePhoneOTP();
6795
+ } catch (error) {
6796
+ // Failed to initialize phone OTP
6797
+ }
6798
+ });
6799
+
6800
+ // Export phone OTP interface
6801
+ window.ManifestAppwriteAuthPhoneOTP = {
6802
+ initialize: initializePhoneOTP
6803
+ };
6804
+
6805
+
6610
6806
  /* Auth OAuth */
6611
6807
 
6612
6808
  // Add OAuth methods to auth store
@@ -2,7 +2,7 @@
2
2
 
3
3
  (function () {
4
4
 
5
- const MANIFEST_BUILD_VERSION = '0.5.224';
5
+ const MANIFEST_BUILD_VERSION = '0.5.225';
6
6
 
7
7
  /* Manifest Data Sources - Appwrite Integration */
8
8
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  (function () {
4
4
 
5
- const MANIFEST_BUILD_VERSION = '0.5.224';
5
+ const MANIFEST_BUILD_VERSION = '0.5.225';
6
6
 
7
7
  /* Manifest Data Sources - Configuration */
8
8
 
@@ -1,6 +1,6 @@
1
1
  {
2
- "manifest.appwrite.auth.js": "sha384-tcpyR3WJvAL3TenZRsevGCGCHUrGgbU5wHuOYG5Emvny/wukRiaPEagrXiH5v7R6",
3
- "manifest.appwrite.data.js": "sha384-gnniX4ojmePGmJsC3KRZqP+jdIGe852Sr/XSkO+HJWljiNMeZVph5LYgYdcwoRNf",
2
+ "manifest.appwrite.auth.js": "sha384-eJ46LQV0lAoY48Zy1J8iecrK9azn/407FdeXz9+dwsuAJESUugOzlEBdTCw+i3m5",
3
+ "manifest.appwrite.data.js": "sha384-uWy++AIY+DZbiEm+Qhkqxc6OJ3E0NSzx5Mq+8TCaAAI+cCV6PSbrS84JRjYV2jNF",
4
4
  "manifest.appwrite.presences.js": "sha384-JyVDWFT69b7Svq8J8p9tNWe2v9PZNQBY5UyhtOl1vnuHFAX0CRQ3MA9SiKq5wzNh",
5
5
  "manifest.bindings.js": "sha384-lPtMf8Ykaic9gNVZKq8Scia1881ZeMAyxer3WCIkUNwwrgzGBWto1OXADUwjvORF",
6
6
  "manifest.charts.js": "sha384-gEAv/98+29RbhvNkiNWTNyBPbLTFr2Ld15wLu29FLCL57j1cwGChRrL9Pfk7dBtT",
@@ -11,7 +11,7 @@
11
11
  "manifest.combobox.js": "sha384-MILTkrgsTzsYUjGo4ZuADqV85Xs4GmfB4xexfe9vbJMIoQTZ42W4zs+DTaVSPEsx",
12
12
  "manifest.components.js": "sha384-4bc3HgPvF9DHMiOG08w+nWXI7Hho01RuAPAUL9xyLxOiS2igeZpulCVcvr5aT9v6",
13
13
  "manifest.computed.js": "sha384-GYML/e81gQfN+iDbqhPUgny8bINCyL+SrbTofoGk4hlCnLLjv51mCv42JMqq+ku5",
14
- "manifest.data.js": "sha384-5kg35XDQb/nTD4oED1HxAkyAhCpfieoUhaY1oCO/vLTc6oPXiPATzyW6/95KSPLw",
14
+ "manifest.data.js": "sha384-YljhDHjZ6BCU7qjoeecMvG1mgC7FwR7vw1EWBAyd6ZcMAlc3nmiztv3swRckN320",
15
15
  "manifest.datepicker.js": "sha384-XnbeURni2X91ozVzatQcSjYYIS4MoW12dBRJnYAGBr0nCQ624heH5s+xtQSpLZX6",
16
16
  "manifest.defer.js": "sha384-WcCf8YQ1bKIXCLSZ24a0bJwepbYntYc8TzQM50hklo5VMIH8AtzDnwl6WU4prRv1",
17
17
  "manifest.device.js": "sha384-g+txmrEszYtuOvRelTp/c1wSNR2AtllVI9rx5NPPD7DzhVUnMNughonZykv2xlBW",
@@ -28,7 +28,7 @@
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-AQB+zglu9/quyKkdAo2oIiWzfgCH7UqiOmFwrS2hqfzYsaSpYeDpmjb5d2tl/RTB",
31
+ "manifest.sw.js": "sha384-hEYCjpYM1VHGJLc/RnSyUsmhBx/XrGhxCA31U6kW/TpFadcUZ0Uz96Rqm0uFbr+I",
32
32
  "manifest.tabs.js": "sha384-OIIcyaQnGS0eNYk4zgAFEDboNYect7THvUd9D0V6nqCeDOxQcoiqLrBEmmWVTTet",
33
33
  "manifest.tailwind.js": "sha384-r/Jvqb+Piz21jUts10BbVAJoKkShOdJRVFpywyi4HPIi/OfOJq5CtPyWAPbdkMgt",
34
34
  "manifest.text.edit.js": "sha384-RzOvLQE8GbmI6gntAwiotDewYT/08OHcd7sJ5hLon8e8u1Xbq1D7VfEGuaRVd2CE",
@@ -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.224';
19
+ const BUILD_VERSION = '0.5.225';
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.224",
3
+ "version": "0.5.225",
4
4
  "private": false,
5
5
  "workspaces": [
6
6
  "templates/starter",