@wix/himalaya-cli 0.1396.0 → 0.1398.0

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.
Files changed (45) hide show
  1. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/serve-runtime/src/wix-bridge.d.ts +30 -0
  2. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/icon-catalog.generated.d.ts +1 -1
  3. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/schema.d.ts +2 -0
  4. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/validation.d.ts +1 -0
  5. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/auth/src/copy.generated.d.ts +109 -0
  6. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/auth/src/feedback.d.ts +15 -0
  7. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/auth/src/index.d.ts +3 -0
  8. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/screens/src/index.d.ts +44 -0
  9. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/auth.mjs +304 -18
  10. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/core/validation.mjs +1 -1
  11. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/core.mjs +31 -1
  12. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/screens.mjs +377 -20
  13. package/dist/cli.mjs +1260 -540
  14. package/dist/preview-assets.generated.bin +0 -0
  15. package/guides/component-reference.json +1 -1
  16. package/guides/component-reference.md +2 -0
  17. package/guides/headless-loop.md +1 -1
  18. package/guides/himi-json-output.md +1 -0
  19. package/guides/importable-sdks.md +2 -1
  20. package/guides/one-app-to-himi-app.md +72 -2
  21. package/guides/one-app-viewer.md +8 -1
  22. package/guides/sdk-reference.json +92 -1
  23. package/package.json +2 -2
  24. package/templates/beauty-salon/dev/index.ts +41 -1
  25. package/templates/beauty-salon/tier5-src/himi-store-account/index.ts +90 -76
  26. package/templates/branded-lite/dev/index.ts +41 -1
  27. package/templates/branded-lite/tier5-src/himi-store-account/index.ts +90 -76
  28. package/templates/community-club/dev/index.ts +41 -1
  29. package/templates/community-club/tier5-src/himi-store-account/index.ts +90 -76
  30. package/templates/events-meetups/dev/index.ts +41 -1
  31. package/templates/events-meetups/tier5-src/himi-store-account/index.ts +90 -76
  32. package/templates/gym-fitness/dev/index.ts +41 -1
  33. package/templates/gym-fitness/tier5-src/himi-store-account/index.ts +90 -76
  34. package/templates/nonprofit-cause/dev/index.ts +41 -1
  35. package/templates/nonprofit-cause/tier5-src/himi-store-account/index.ts +90 -76
  36. package/templates/podcast-creator/dev/index.ts +41 -1
  37. package/templates/podcast-creator/tier5-src/himi-store-account/index.ts +90 -76
  38. package/templates/portfolio-creative/dev/index.ts +41 -1
  39. package/templates/portfolio-creative/tier5-src/himi-store-account/index.ts +90 -76
  40. package/templates/restaurant/dev/index.ts +41 -1
  41. package/templates/restaurant/tier5-src/himi-store-account/index.ts +90 -76
  42. package/templates/retail-store/dev/index.ts +41 -1
  43. package/templates/retail-store/tier5-src/himi-store-account/index.ts +90 -76
  44. package/templates/yoga-studio/dev/index.ts +41 -1
  45. package/templates/yoga-studio/tier5-src/himi-store-account/index.ts +90 -76
@@ -2124,6 +2124,16 @@ const SCREENS = runtimeSetting({
2124
2124
  "props": {
2125
2125
  "placeholder": "Email",
2126
2126
  "keyboard": "email",
2127
+ "validation": {
2128
+ "required": true,
2129
+ "type": "email",
2130
+ "mode": "on_blur"
2131
+ },
2132
+ "errorPath": "$service:himiStoreAccount.emailError",
2133
+ "submitLabel": "go",
2134
+ "onSubmit": {
2135
+ "actionId": "tier5.himiStoreAccount.submit"
2136
+ },
2127
2137
  "autoCapitalization": "none",
2128
2138
  "focusPath": "$service:himiStoreAccount.emailFocus",
2129
2139
  "onChange": {
@@ -2139,8 +2149,19 @@ const SCREENS = runtimeSetting({
2139
2149
  "type": "TextField",
2140
2150
  "props": {
2141
2151
  "placeholder": "Password",
2142
- "valuePath": "$service:himiStoreAccount.password",
2143
2152
  "secure": true,
2153
+ "visibilityToggle": true,
2154
+ "validation": {
2155
+ "required": true,
2156
+ "mode": "on_blur",
2157
+ "message": "Enter your password."
2158
+ },
2159
+ "errorPath": "$service:himiStoreAccount.passwordError",
2160
+ "focusPath": "$service:himiStoreAccount.passwordFocus",
2161
+ "submitLabel": "go",
2162
+ "onSubmit": {
2163
+ "actionId": "tier5.himiStoreAccount.submit"
2164
+ },
2144
2165
  "autoCapitalization": "none",
2145
2166
  "onChange": {
2146
2167
  "actionId": "tier5.himiStoreAccount.setPassword"
@@ -2151,6 +2172,18 @@ const SCREENS = runtimeSetting({
2151
2172
  "hiddenPath": "$service:himiStoreAccount.isRecovering"
2152
2173
  }
2153
2174
  },
2175
+ {
2176
+ "id": "",
2177
+ "body": {
2178
+ "package": "himalaya",
2179
+ "type": "Text",
2180
+ "props": {
2181
+ "text": "$service:himiStoreAccount.busyMessage",
2182
+ "style": "footnote",
2183
+ "collapseWhenEmpty": true
2184
+ }
2185
+ }
2186
+ },
2154
2187
  {
2155
2188
  "id": "himi_account.message",
2156
2189
  "body": {
@@ -2173,6 +2206,7 @@ const SCREENS = runtimeSetting({
2173
2206
  "title": "Sign in",
2174
2207
  "style": "primary",
2175
2208
  "fullWidth": true,
2209
+ "disabledPath": "$service:himiStoreAccount.authBusy",
2176
2210
  "action": {
2177
2211
  "actionId": "tier5.himiStoreAccount.signIn"
2178
2212
  }
@@ -2191,6 +2225,7 @@ const SCREENS = runtimeSetting({
2191
2225
  "title": "Create account",
2192
2226
  "style": "primary",
2193
2227
  "fullWidth": true,
2228
+ "disabledPath": "$service:himiStoreAccount.authBusy",
2194
2229
  "action": {
2195
2230
  "actionId": "tier5.himiStoreAccount.register"
2196
2231
  }
@@ -2209,6 +2244,7 @@ const SCREENS = runtimeSetting({
2209
2244
  "title": "Send reset link",
2210
2245
  "style": "primary",
2211
2246
  "fullWidth": true,
2247
+ "disabledPath": "$service:himiStoreAccount.authBusy",
2212
2248
  "action": {
2213
2249
  "actionId": "tier5.himiStoreAccount.resetPassword"
2214
2250
  }
@@ -2227,6 +2263,7 @@ const SCREENS = runtimeSetting({
2227
2263
  "title": "Forgot password?",
2228
2264
  "style": "secondary",
2229
2265
  "fullWidth": true,
2266
+ "disabledPath": "$service:himiStoreAccount.authBusy",
2230
2267
  "action": {
2231
2268
  "actionId": "tier5.himiStoreAccount.showRecovery"
2232
2269
  }
@@ -2245,6 +2282,7 @@ const SCREENS = runtimeSetting({
2245
2282
  "title": "Register",
2246
2283
  "style": "secondary",
2247
2284
  "fullWidth": true,
2285
+ "disabledPath": "$service:himiStoreAccount.authBusy",
2248
2286
  "action": {
2249
2287
  "actionId": "tier5.himiStoreAccount.showRegister"
2250
2288
  }
@@ -2263,6 +2301,7 @@ const SCREENS = runtimeSetting({
2263
2301
  "title": "Sign in instead",
2264
2302
  "style": "secondary",
2265
2303
  "fullWidth": true,
2304
+ "disabledPath": "$service:himiStoreAccount.authBusy",
2266
2305
  "action": {
2267
2306
  "actionId": "tier5.himiStoreAccount.showSignIn"
2268
2307
  }
@@ -2281,6 +2320,7 @@ const SCREENS = runtimeSetting({
2281
2320
  "title": "Back to sign in",
2282
2321
  "style": "secondary",
2283
2322
  "fullWidth": true,
2323
+ "disabledPath": "$service:himiStoreAccount.authBusy",
2284
2324
  "action": {
2285
2325
  "actionId": "tier5.himiStoreAccount.showSignIn"
2286
2326
  }
@@ -1,3 +1,4 @@
1
+ import { authFailure, authMessage, identityErrors, type AuthOperation } from "@himalaya/auth";
1
2
  // Tier-5 bundle: himi-store — visitor/member account state.
2
3
  //
3
4
  // SINGLE-SITE BY DESIGN. A Branded Lite build talks to exactly one Wix site, and the
@@ -75,23 +76,50 @@ function resumeAfterAuth(next: S): S {
75
76
  // Actions are not serialized: two quick Sign in taps both run against the pre-tap
76
77
  // state, so without this both would submit credentials and both would resume.
77
78
  let authInFlight = false;
79
+ // Never projected or persisted. The renderer retains its own masked draft for retry.
80
+ let passwordDraft = "";
81
+ let editRevision = 0;
82
+ // Descriptor configuration belongs to the worker session. Keep it even when a
83
+ // delayed profile probe is discarded because the user edited the form.
84
+ let memberAccountLinksRequested = false;
85
+ const translated = (key: string) => copyParam(key, authMessage(key));
78
86
 
79
- async function authenticate(s: S, submit: () => Promise<unknown>, notMemberMessage: string, failedMessage: string): Promise<S | undefined> {
87
+ function validateAuth(s: S, operation: AuthOperation): S {
88
+ const errors = identityErrors({ email: s.email, password: passwordDraft }, operation);
89
+ return derive({ ...s, emailError: errors.email ? translated(errors.email) : "",
90
+ passwordError: errors.password ? translated(errors.password) : "",
91
+ emailFocus: errors.email ? bumpToken(s.emailFocus) : s.emailFocus,
92
+ passwordFocus: !errors.email && errors.password ? bumpToken(s.passwordFocus) : s.passwordFocus,
93
+ message: "" });
94
+ }
95
+ function rejectResult(raw: unknown, operation: AuthOperation) {
96
+ const value = raw as { status?: string; error?: unknown; payload?: { error?: unknown } } | null;
97
+ if (value?.status === "error" || value?.error || value?.payload?.error) throw authFailure(raw, operation, translated);
98
+ }
99
+ async function authenticate(s: S, operation: "signIn" | "register"): Promise<S | undefined> {
80
100
  if (authInFlight) return undefined;
101
+ const checked = validateAuth(s, operation);
102
+ if (checked.emailError || checked.passwordError) return checked;
81
103
  authInFlight = true;
104
+ const revision = editRevision;
105
+ const password = passwordDraft;
82
106
  try {
83
- await submit();
84
- const next = await probe(optimisticMember(s, s.email));
85
- if (!next.isMember) return derive({ ...next, message: notMemberMessage });
86
- return resumeAfterAuth(derive({ ...next, message: "" }));
87
- } catch {
88
- // Clear the password: service state is snapshotted to disk (the worker-state
89
- // store), so a failed attempt must not leave it there in cleartext. The Password
90
- // field binds `valuePath` to it, so the field empties too and a retry re-types.
91
- return derive({ ...s, password: "", message: failedMessage });
92
- } finally {
93
- authInFlight = false;
94
- }
107
+ await svc.beginAuth(); // publish busy before waiting for the network
108
+ const raw = await dispatch(operation === "signIn" ? "himi.wix.signInDirect" : "himi.wix.registerDirect", {
109
+ email: s.email.trim(), password,
110
+ ...(operation === "register" ? { firstName: s.firstName, lastName: s.lastName } : {}),
111
+ });
112
+ rejectResult(raw, operation);
113
+ if (revision !== editRevision) return derive({ ...svc.getState(), authBusy: false });
114
+ const next = await probe(svc.getState());
115
+ if (revision !== editRevision) return derive({ ...svc.getState(), authBusy: false });
116
+ if (!next.isMember) throw { code: "unknown" };
117
+ passwordDraft = "";
118
+ return resumeAfterAuth(completedAuth(next));
119
+ } catch (error) {
120
+ return derive({ ...svc.getState(), authBusy: false,
121
+ message: revision === editRevision ? authFailure(error, operation, translated).reason : "" });
122
+ } finally { authInFlight = false; }
95
123
  }
96
124
 
97
125
  // Next value of a focus token: monotonic, and never empty once requested. A counter
@@ -180,10 +208,14 @@ interface S extends IapRestoreFields {
180
208
  noReturnPath: boolean;
181
209
  returnCta: string;
182
210
  email: string;
183
- /** Held only from typing to the submit. Service state is persisted, so every
184
- * submitted sign-in/register clears it (success, not-a-member, and failure), as
185
- * do recovery and sign-out. Sign-in <-> register keeps it: the field stays up. */
211
+ /** Legacy snapshot slot, always empty. The active form keeps its password in
212
+ * transient memory for retry; hydration never restores a persisted secret. */
186
213
  password: string;
214
+ authBusy: boolean;
215
+ busyMessage?: string;
216
+ emailError: string;
217
+ passwordError: string;
218
+ passwordFocus: string;
187
219
  firstName: string;
188
220
  lastName: string;
189
221
  // Focus TOKENS (`TextFieldProps.focus_path`), not flags: the bound field focuses
@@ -247,6 +279,8 @@ function derive(s: S): S {
247
279
  const hasReturnPath = !!s.isMember && !!s.returnScreenId;
248
280
  return {
249
281
  ...s,
282
+ password: "",
283
+ busyMessage: s.authBusy ? translated("auth.feedback.busy") : "",
250
284
  isLoading: s.status === "loading",
251
285
  notLoading: s.status !== "loading",
252
286
  notMember: !s.isMember,
@@ -309,9 +343,10 @@ async function orderRows(memberEmail: string): Promise<AccountActivityRow[]> {
309
343
  }
310
344
 
311
345
  async function probe(s: S, arg?: unknown): Promise<S> {
346
+ memberAccountLinksRequested ||= s.memberAccountLinks || strField(arg, "memberAccountLinks") === "1";
312
347
  const intent = authIntentFromArg(arg, s);
313
348
  const profile = await loadMemberProfile(copyParam("storeAccount.guestDetail", "Sign in to keep your cart and orders connected."));
314
- const memberAccountLinks = s.memberAccountLinks || strField(arg, "memberAccountLinks") === "1";
349
+ const memberAccountLinks = memberAccountLinksRequested;
315
350
  const restorePurchases = s.restorePurchases || strField(arg, "restorePurchases") === "1";
316
351
  if (restorePurchases) enterCommerce();
317
352
  const routes = memberAccountLinks ? await memberAccountRoutes() : { addresses: false, wallet: false };
@@ -353,21 +388,11 @@ async function probe(s: S, arg?: unknown): Promise<S> {
353
388
  });
354
389
  }
355
390
 
356
- function optimisticMember(s: S, email: string): S {
391
+ function completedAuth(s: S): S {
357
392
  return derive({
358
393
  ...s,
359
- status: "ready",
360
- isMember: true,
361
- identityLine: copyParam("storeAccount.signedIn", "Signed in"),
362
- detailLine: email,
363
- memberName: email,
364
- memberEmail: email,
365
- memberImage: "",
366
- memberInitials: "M",
367
- hasMemberImage: false,
368
- noMemberImage: true,
369
- email,
370
394
  password: "",
395
+ authBusy: false, emailError: "", passwordError: "", passwordFocus: "",
371
396
  firstName: "",
372
397
  lastName: "",
373
398
  isRegistering: false,
@@ -377,6 +402,8 @@ function optimisticMember(s: S, email: string): S {
377
402
  }
378
403
 
379
404
  const svc = defineService<S, {
405
+ beginAuth: (s: S) => S;
406
+ submit: (s: S) => Promise<S | undefined>;
380
407
  load: (s: S, arg?: unknown) => Promise<S>;
381
408
  refresh: (s: S, arg?: unknown) => Promise<S>;
382
409
  setEmail: (s: S, arg?: unknown) => S;
@@ -395,7 +422,7 @@ const svc = defineService<S, {
395
422
  restorePublish: (s: S) => S;
396
423
  iapRestore: (s: S) => Promise<undefined>;
397
424
  }>("himiStoreAccount", {
398
- notHydrated: [...IAP_RESTORE_KEYS],
425
+ notHydrated: ["password", "authBusy", "emailError", "passwordError", "passwordFocus", ...IAP_RESTORE_KEYS],
399
426
  state: derive({
400
427
  status: "loading",
401
428
  isMember: false,
@@ -420,6 +447,7 @@ const svc = defineService<S, {
420
447
  returnCta: "Continue",
421
448
  email: "",
422
449
  password: "",
450
+ authBusy: false, emailError: "", passwordError: "", passwordFocus: "",
423
451
  firstName: "",
424
452
  lastName: "",
425
453
  lastNameFocus: "",
@@ -445,12 +473,14 @@ const svc = defineService<S, {
445
473
  ...NO_IAP_RESTORE,
446
474
  }),
447
475
  actions: {
448
- load: (s, arg) => { stashCopy(arg); return probe(s, arg); },
449
- refresh: (s, arg) => { stashCopy(arg); return probe(s, arg); },
450
- setEmail: (s, arg) => derive({ ...s, email: strOf(arg), message: "" }),
451
- setPassword: (s, arg) => derive({ ...s, password: strOf(arg), message: "" }),
452
- setFirstName: (s, arg) => derive({ ...s, firstName: strOf(arg), message: "" }),
453
- setLastName: (s, arg) => derive({ ...s, lastName: strOf(arg), message: "" }),
476
+ submit: (s): Promise<S | undefined> => s.isRecovering ? svc.resetPassword() : authenticate(s, s.isRegistering ? "register" : "signIn"),
477
+ beginAuth: (s) => derive({ ...s, authBusy: true, message: "", emailError: "", passwordError: "" }),
478
+ load: async (s, arg): Promise<S> => { stashCopy(arg); const revision = editRevision; const next = await probe(s, arg); return authInFlight || revision !== editRevision ? svc.getState() : next; },
479
+ refresh: async (s, arg): Promise<S> => { stashCopy(arg); const revision = editRevision; const next = await probe(s, arg); return authInFlight || revision !== editRevision ? svc.getState() : next; },
480
+ setEmail: (s, arg) => { editRevision++; const next = { ...s, email: strOf(arg).trim(), message: "" }; return derive({ ...next, emailError: s.emailError ? (identityErrors({ email: next.email }, "reset").email ? translated(identityErrors({ email: next.email }, "reset").email!) : "") : "" }); },
481
+ setPassword: (s, arg) => { editRevision++; passwordDraft = strOf(arg); return derive({ ...s, message: "", passwordError: s.passwordError && !passwordDraft ? translated("auth.validation.passwordRequired") : "" }); },
482
+ setFirstName: (s, arg) => { editRevision++; return derive({ ...s, firstName: strOf(arg), message: "" }); },
483
+ setLastName: (s, arg) => { editRevision++; return derive({ ...s, lastName: strOf(arg), message: "" }); },
454
484
  // The register form's Next keys. Only first/last name carry one: they share a
455
485
  // single visibility gate (notRegistering) so their target is always on screen.
456
486
  // Email deliberately does NOT chain to password — see the comment on the
@@ -463,48 +493,32 @@ const svc = defineService<S, {
463
493
  // register and coming back would open focused on Last name, skipping First name.
464
494
  // Only the tokens whose fields actually unmount here are cleared: see
465
495
  // NO_FOCUS_MODE_SWITCH for why Email's is deliberately NOT among them.
466
- showRegister: (s) => derive({ ...s, isRegistering: true, isRecovering: false, message: "", ...NO_FOCUS_MODE_SWITCH }),
467
- showSignIn: (s) => derive({ ...s, isRegistering: false, isRecovering: false, message: "", ...NO_FOCUS_MODE_SWITCH }),
468
- showRecovery: (s) => derive({ ...s, isRegistering: false, isRecovering: true, password: "", message: "", ...NO_FOCUS_MODE_SWITCH }),
469
- resetPassword: async (s) => {
470
- const email = s.email.trim();
471
- if (!email || !email.includes("@")) return derive({ ...s, message: copyParam("storeAccount.validationEmail", "Enter a valid email address.") });
496
+ showRegister: (s) => { if (authInFlight) return s; editRevision++; return derive({ ...s, isRegistering: true, isRecovering: false, message: "", emailError: "", passwordError: "", ...NO_FOCUS_MODE_SWITCH }) },
497
+ showSignIn: (s) => { if (authInFlight) return s; editRevision++; return derive({ ...s, isRegistering: false, isRecovering: false, message: "", emailError: "", passwordError: "", ...NO_FOCUS_MODE_SWITCH }) },
498
+ showRecovery: (s) => { if (authInFlight) return s; editRevision++; return derive({ ...s, isRegistering: false, isRecovering: true, password: "", message: "", emailError: "", passwordError: "", ...NO_FOCUS_MODE_SWITCH }) },
499
+ resetPassword: async (s): Promise<S> => {
500
+ if (authInFlight) return s;
501
+ const checked = validateAuth(s, "reset");
502
+ if (checked.emailError) return checked;
503
+ authInFlight = true;
504
+ const revision = editRevision;
472
505
  try {
473
- await dispatch("himi.wix.resetPassword", { email });
474
- return derive({
475
- ...s,
476
- email,
477
- isRecovering: false,
478
- message: copyParam("storeAccount.resetSent", "If that email is registered, a reset link is on its way."),
479
- });
480
- } catch {
481
- return derive({ ...s, message: copyParam("storeAccount.resetFailed", "Reset email could not be sent. Try again.") });
482
- }
483
- },
484
- signIn: async (s) => {
485
- if (!s.email || !s.password) return derive({ ...s, message: copyParam("storeAccount.validationEmailPassword", "Enter your email and password.") });
486
- return authenticate(
487
- s,
488
- () => dispatch("himi.wix.signInDirect", { email: s.email, password: s.password }),
489
- copyParam("storeAccount.signInCheckDetails", "Sign in failed. Check your details."),
490
- copyParam("storeAccount.signInFailed", "Sign in failed. Try again."),
491
- );
492
- },
493
- register: async (s) => {
494
- if (!s.email || !s.password) return derive({ ...s, message: copyParam("storeAccount.validationEmailPassword", "Enter your email and password.") });
495
- return authenticate(
496
- s,
497
- () => dispatch("himi.wix.registerDirect", {
498
- email: s.email,
499
- password: s.password,
500
- firstName: s.firstName,
501
- lastName: s.lastName,
502
- }),
503
- copyParam("storeAccount.registerTrySignIn", "Account creation failed. Try signing in."),
504
- copyParam("storeAccount.registerFailed", "Account creation failed. Try another email."),
505
- );
506
+ await svc.beginAuth();
507
+ const raw = await dispatch("himi.wix.resetPassword", { email: s.email.trim() });
508
+ rejectResult(raw, "reset");
509
+ if (revision !== editRevision) return derive({ ...svc.getState(), authBusy: false });
510
+ return derive({ ...svc.getState(), authBusy: false, isRecovering: false,
511
+ message: copyParam("storeAccount.resetSent", "If that email is registered, a reset link is on its way.") });
512
+ } catch (error) {
513
+ return derive({ ...svc.getState(), authBusy: false,
514
+ message: revision === editRevision ? authFailure(error, "reset", translated).reason : "" });
515
+ } finally { authInFlight = false; }
506
516
  },
507
- signOut: async (s) => {
517
+ signIn: (s) => authenticate(s, "signIn"),
518
+ register: (s) => authenticate(s, "register"),
519
+ signOut: async (s): Promise<S> => {
520
+ if (authInFlight) return svc.getState();
521
+ passwordDraft = ""; editRevision++;
508
522
  routeInputConsumed = true;
509
523
  try { await dispatch("himi.wix.signOut"); } catch { /* ignore */ }
510
524
  // Do NOT re-probe via getMyData after logout — visitor-scoped my-data can
@@ -1602,6 +1602,16 @@ const SCREENS = runtimeSetting({
1602
1602
  "props": {
1603
1603
  "placeholder": "Email",
1604
1604
  "keyboard": "email",
1605
+ "validation": {
1606
+ "required": true,
1607
+ "type": "email",
1608
+ "mode": "on_blur"
1609
+ },
1610
+ "errorPath": "$service:himiStoreAccount.emailError",
1611
+ "submitLabel": "go",
1612
+ "onSubmit": {
1613
+ "actionId": "tier5.himiStoreAccount.submit"
1614
+ },
1605
1615
  "autoCapitalization": "none",
1606
1616
  "focusPath": "$service:himiStoreAccount.emailFocus",
1607
1617
  "onChange": {
@@ -1617,8 +1627,19 @@ const SCREENS = runtimeSetting({
1617
1627
  "type": "TextField",
1618
1628
  "props": {
1619
1629
  "placeholder": "Password",
1620
- "valuePath": "$service:himiStoreAccount.password",
1621
1630
  "secure": true,
1631
+ "visibilityToggle": true,
1632
+ "validation": {
1633
+ "required": true,
1634
+ "mode": "on_blur",
1635
+ "message": "Enter your password."
1636
+ },
1637
+ "errorPath": "$service:himiStoreAccount.passwordError",
1638
+ "focusPath": "$service:himiStoreAccount.passwordFocus",
1639
+ "submitLabel": "go",
1640
+ "onSubmit": {
1641
+ "actionId": "tier5.himiStoreAccount.submit"
1642
+ },
1622
1643
  "autoCapitalization": "none",
1623
1644
  "onChange": {
1624
1645
  "actionId": "tier5.himiStoreAccount.setPassword"
@@ -1629,6 +1650,18 @@ const SCREENS = runtimeSetting({
1629
1650
  "hiddenPath": "$service:himiStoreAccount.isRecovering"
1630
1651
  }
1631
1652
  },
1653
+ {
1654
+ "id": "",
1655
+ "body": {
1656
+ "package": "himalaya",
1657
+ "type": "Text",
1658
+ "props": {
1659
+ "text": "$service:himiStoreAccount.busyMessage",
1660
+ "style": "footnote",
1661
+ "collapseWhenEmpty": true
1662
+ }
1663
+ }
1664
+ },
1632
1665
  {
1633
1666
  "id": "himi_account.message",
1634
1667
  "body": {
@@ -1651,6 +1684,7 @@ const SCREENS = runtimeSetting({
1651
1684
  "title": "Sign in",
1652
1685
  "style": "primary",
1653
1686
  "fullWidth": true,
1687
+ "disabledPath": "$service:himiStoreAccount.authBusy",
1654
1688
  "action": {
1655
1689
  "actionId": "tier5.himiStoreAccount.signIn"
1656
1690
  }
@@ -1669,6 +1703,7 @@ const SCREENS = runtimeSetting({
1669
1703
  "title": "Create account",
1670
1704
  "style": "primary",
1671
1705
  "fullWidth": true,
1706
+ "disabledPath": "$service:himiStoreAccount.authBusy",
1672
1707
  "action": {
1673
1708
  "actionId": "tier5.himiStoreAccount.register"
1674
1709
  }
@@ -1687,6 +1722,7 @@ const SCREENS = runtimeSetting({
1687
1722
  "title": "Send reset link",
1688
1723
  "style": "primary",
1689
1724
  "fullWidth": true,
1725
+ "disabledPath": "$service:himiStoreAccount.authBusy",
1690
1726
  "action": {
1691
1727
  "actionId": "tier5.himiStoreAccount.resetPassword"
1692
1728
  }
@@ -1705,6 +1741,7 @@ const SCREENS = runtimeSetting({
1705
1741
  "title": "Forgot password?",
1706
1742
  "style": "secondary",
1707
1743
  "fullWidth": true,
1744
+ "disabledPath": "$service:himiStoreAccount.authBusy",
1708
1745
  "action": {
1709
1746
  "actionId": "tier5.himiStoreAccount.showRecovery"
1710
1747
  }
@@ -1723,6 +1760,7 @@ const SCREENS = runtimeSetting({
1723
1760
  "title": "Register",
1724
1761
  "style": "secondary",
1725
1762
  "fullWidth": true,
1763
+ "disabledPath": "$service:himiStoreAccount.authBusy",
1726
1764
  "action": {
1727
1765
  "actionId": "tier5.himiStoreAccount.showRegister"
1728
1766
  }
@@ -1741,6 +1779,7 @@ const SCREENS = runtimeSetting({
1741
1779
  "title": "Sign in instead",
1742
1780
  "style": "secondary",
1743
1781
  "fullWidth": true,
1782
+ "disabledPath": "$service:himiStoreAccount.authBusy",
1744
1783
  "action": {
1745
1784
  "actionId": "tier5.himiStoreAccount.showSignIn"
1746
1785
  }
@@ -1759,6 +1798,7 @@ const SCREENS = runtimeSetting({
1759
1798
  "title": "Back to sign in",
1760
1799
  "style": "secondary",
1761
1800
  "fullWidth": true,
1801
+ "disabledPath": "$service:himiStoreAccount.authBusy",
1762
1802
  "action": {
1763
1803
  "actionId": "tier5.himiStoreAccount.showSignIn"
1764
1804
  }