@wix/himalaya-cli 0.982.0 → 0.984.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 (29) hide show
  1. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/notification-preferences/src/index.d.ts +105 -33
  2. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-notification-preferences.mjs +66 -5
  3. package/dist/cli.mjs +3437 -331
  4. package/fixtures/wix-canned.json +36 -29
  5. package/guides/sdk-reference.json +86 -49
  6. package/package.json +3 -2
  7. package/templates/beauty-salon/dev/index.ts +27 -2
  8. package/templates/beauty-salon/tier5-src/himi-branded-lite-order/index.ts +14 -6
  9. package/templates/beauty-salon/tier5-src/himi-store-account/index.ts +59 -20
  10. package/templates/branded-lite/dev/index.ts +27 -2
  11. package/templates/branded-lite/tier5-src/himi-branded-lite-notifsettings/index.ts +42 -14
  12. package/templates/branded-lite/tier5-src/himi-branded-lite-order/index.ts +14 -6
  13. package/templates/branded-lite/tier5-src/himi-store-account/index.ts +59 -20
  14. package/templates/community-club/tier5-src/himi-store-account/index.ts +59 -20
  15. package/templates/events-meetups/tier5-src/himi-store-account/index.ts +59 -20
  16. package/templates/gym-fitness/dev/index.ts +27 -2
  17. package/templates/gym-fitness/tier5-src/himi-branded-lite-order/index.ts +14 -6
  18. package/templates/gym-fitness/tier5-src/himi-store-account/index.ts +59 -20
  19. package/templates/nonprofit-cause/tier5-src/himi-store-account/index.ts +59 -20
  20. package/templates/podcast-creator/tier5-src/himi-store-account/index.ts +59 -20
  21. package/templates/portfolio-creative/dev/index.ts +25 -1
  22. package/templates/portfolio-creative/tier5-src/himi-branded-lite-order/index.ts +14 -6
  23. package/templates/portfolio-creative/tier5-src/himi-store-account/index.ts +59 -20
  24. package/templates/restaurant/tier5-src/himi-store-account/index.ts +59 -20
  25. package/templates/retail-store/dev/index.ts +25 -1
  26. package/templates/retail-store/tier5-src/himi-branded-lite-order/index.ts +14 -6
  27. package/templates/retail-store/tier5-src/himi-store-account/index.ts +59 -20
  28. package/templates/yoga-studio/dev/index.ts +2 -1
  29. package/templates/yoga-studio/tier5-src/himi-store-account/index.ts +59 -20
@@ -8,7 +8,7 @@
8
8
  // `wix-site-id` header and nothing else, so every read stayed on the baked site while the
9
9
  // UI claimed otherwise. Managing several businesses is Owner Lite's job (account auth, a
10
10
  // real Sites List, and per-call site scope via `site.forSite`).
11
- import { defineService } from "@himalaya/state";
11
+ import { defineService, navigate, type NavDirective } from "@himalaya/state";
12
12
  import { dispatch } from "@himalaya/dispatch";
13
13
  import { searchOrders, type WixOrder } from "@himalaya/wix-ecom";
14
14
  import { guestProfile, loadMemberProfile, strOf, fmtDate, titleCase, type AccountActivityRow } from "../_shared/himi-account";
@@ -21,6 +21,9 @@ interface AuthRouteInput {
21
21
  returnId?: string;
22
22
  }
23
23
 
24
+ // The screen's boot input survives refresh; consuming it must survive too.
25
+ let routeInputConsumed = false;
26
+
24
27
  /** Mid-flow open params land on `himalaya.input` (paramKeys on himi_account). */
25
28
  function routeInput(): AuthRouteInput {
26
29
  const input = (globalThis as { himalaya?: { input?: AuthRouteInput } }).himalaya?.input;
@@ -35,7 +38,7 @@ function strField(arg: unknown, key: string): string {
35
38
 
36
39
  /** Mid-flow auth (UX F3): intent + return path from himi.open / himalaya.input. */
37
40
  function authIntentFromArg(arg: unknown, prev: Pick<S, "authIntent" | "authContextName" | "returnScreenId" | "returnId">): Pick<S, "authIntent" | "authContextName" | "returnScreenId" | "returnId"> {
38
- const route = routeInput();
41
+ const route = routeInputConsumed ? {} : routeInput();
39
42
  return {
40
43
  authIntent: strField(arg, "intent") || route.intent || prev.authIntent || "",
41
44
  authContextName: strField(arg, "contextName") || route.contextName || prev.authContextName || "",
@@ -44,6 +47,42 @@ function authIntentFromArg(arg: unknown, prev: Pick<S, "authIntent" | "authConte
44
47
  };
45
48
  }
46
49
 
50
+ /** Where a successful sign-in returns to, or null. Never Account itself — that would
51
+ * only push a second copy of the screen the member is already on. */
52
+ function resumeTarget(s: Pick<S, "returnScreenId" | "returnId">): NavDirective | null {
53
+ if (!s.returnScreenId || s.returnScreenId === "himi_account") return null;
54
+ return s.returnId ? { screenId: s.returnScreenId, args: { id: s.returnId } } : { screenId: s.returnScreenId };
55
+ }
56
+
57
+ /** Resume the member-only screen that sent the visitor here, once. The return path is
58
+ * consumed so a later refresh cannot fire it again; the host opens the target only if
59
+ * it is linkable, the same check an authored `himi.open` gets. */
60
+ function resumeAfterAuth(next: S): S {
61
+ const target = resumeTarget(next);
62
+ if (!next.isMember || !target) return next;
63
+ routeInputConsumed = true;
64
+ return navigate(derive({ ...next, authIntent: "", authContextName: "", returnScreenId: "", returnId: "" }), target);
65
+ }
66
+
67
+ // Actions are not serialized: two quick Sign in taps both run against the pre-tap
68
+ // state, so without this both would submit credentials and both would resume.
69
+ let authInFlight = false;
70
+
71
+ async function authenticate(s: S, submit: () => Promise<unknown>, notMemberMessage: string, failedMessage: string): Promise<S | undefined> {
72
+ if (authInFlight) return undefined;
73
+ authInFlight = true;
74
+ try {
75
+ await submit();
76
+ const next = await probe(optimisticMember(s, s.email));
77
+ if (!next.isMember) return derive({ ...next, message: notMemberMessage });
78
+ return resumeAfterAuth(derive({ ...next, message: "" }));
79
+ } catch {
80
+ return derive({ ...s, message: failedMessage });
81
+ } finally {
82
+ authInFlight = false;
83
+ }
84
+ }
85
+
47
86
  // Next value of a focus token: monotonic, and never empty once requested. A counter
48
87
  // rather than Date.now() — deterministic under test, and immune to two focus
49
88
  // requests landing inside the same millisecond.
@@ -123,7 +162,8 @@ interface S {
123
162
  authContextName: string;
124
163
  returnScreenId: string;
125
164
  returnId: string;
126
- /** Show Continue after a successful mid-flow sign-in. */
165
+ /** Show Continue when an already-signed-in member opens Account with a return path.
166
+ * A sign-in made here resumes on its own (resumeAfterAuth). */
127
167
  hasReturnPath: boolean;
128
168
  noReturnPath: boolean;
129
169
  returnCta: string;
@@ -296,8 +336,8 @@ const svc = defineService<S, {
296
336
  showSignIn: (s: S) => S;
297
337
  showRecovery: (s: S) => S;
298
338
  resetPassword: (s: S) => Promise<S>;
299
- signIn: (s: S) => Promise<S>;
300
- register: (s: S) => Promise<S>;
339
+ signIn: (s: S) => Promise<S | undefined>;
340
+ register: (s: S) => Promise<S | undefined>;
301
341
  signOut: (s: S) => Promise<S>;
302
342
  }>("himiStoreAccount", {
303
343
  state: derive({
@@ -381,30 +421,29 @@ const svc = defineService<S, {
381
421
  },
382
422
  signIn: async (s) => {
383
423
  if (!s.email || !s.password) return derive({ ...s, message: copyParam("storeAccount.validationEmailPassword", "Enter your email and password.") });
384
- try {
385
- await dispatch("himi.wix.signInDirect", { email: s.email, password: s.password });
386
- const next = await probe(optimisticMember(s, s.email));
387
- return derive({ ...next, message: next.isMember ? "" : copyParam("storeAccount.signInCheckDetails", "Sign in failed. Check your details.") });
388
- } catch {
389
- return derive({ ...s, message: copyParam("storeAccount.signInFailed", "Sign in failed. Try again.") });
390
- }
424
+ return authenticate(
425
+ s,
426
+ () => dispatch("himi.wix.signInDirect", { email: s.email, password: s.password }),
427
+ copyParam("storeAccount.signInCheckDetails", "Sign in failed. Check your details."),
428
+ copyParam("storeAccount.signInFailed", "Sign in failed. Try again."),
429
+ );
391
430
  },
392
431
  register: async (s) => {
393
432
  if (!s.email || !s.password) return derive({ ...s, message: copyParam("storeAccount.validationEmailPassword", "Enter your email and password.") });
394
- try {
395
- await dispatch("himi.wix.registerDirect", {
433
+ return authenticate(
434
+ s,
435
+ () => dispatch("himi.wix.registerDirect", {
396
436
  email: s.email,
397
437
  password: s.password,
398
438
  firstName: s.firstName,
399
439
  lastName: s.lastName,
400
- });
401
- const next = await probe(optimisticMember(s, s.email));
402
- return derive({ ...next, message: next.isMember ? "" : copyParam("storeAccount.registerTrySignIn", "Account creation failed. Try signing in.") });
403
- } catch {
404
- return derive({ ...s, message: copyParam("storeAccount.registerFailed", "Account creation failed. Try another email.") });
405
- }
440
+ }),
441
+ copyParam("storeAccount.registerTrySignIn", "Account creation failed. Try signing in."),
442
+ copyParam("storeAccount.registerFailed", "Account creation failed. Try another email."),
443
+ );
406
444
  },
407
445
  signOut: async (s) => {
446
+ routeInputConsumed = true;
408
447
  try { await dispatch("himi.wix.signOut"); } catch { /* ignore */ }
409
448
  // Do NOT re-probe via getMyData after logout — visitor-scoped my-data can
410
449
  // still return a site nickname and resurrect isMember (BUG-3). Trust the