@wix/himalaya-cli 0.1024.0 → 0.1026.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 (84) hide show
  1. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/schema.d.ts +12 -1
  2. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/core.mjs +6 -0
  3. package/dist/cli.mjs +29 -18
  4. package/dist/preview-assets.generated.bin +0 -0
  5. package/guides/sdk-reference.json +1 -1
  6. package/package.json +2 -2
  7. package/templates/beauty-salon/dev/index.ts +1031 -240
  8. package/templates/beauty-salon/tier5-src/_shared/detail-presence.ts +51 -0
  9. package/templates/beauty-salon/tier5-src/himi-branded-lite-orders/index.ts +4 -1
  10. package/templates/beauty-salon/tier5-src/wix-spaces-collection-detail/index.ts +9 -9
  11. package/templates/beauty-salon/tier5-src/wix-spaces-create-post/index.ts +14 -4
  12. package/templates/beauty-salon/tier5-src/wix-spaces-plan-detail/index.ts +7 -9
  13. package/templates/beauty-salon/tier5-src/wix-spaces-subscription-detail/index.ts +12 -11
  14. package/templates/beauty-salon/tier5-src/wix-spaces-view-profile/index.ts +22 -12
  15. package/templates/branded-lite/config.ts +3 -1
  16. package/templates/branded-lite/dev/index.ts +3455 -813
  17. package/templates/branded-lite/tier5-src/_shared/detail-presence.ts +51 -0
  18. package/templates/branded-lite/tier5-src/himi-branded-lite-orders/index.ts +4 -1
  19. package/templates/branded-lite/tier5-src/himi-branded-lite-portfolio-project/index.ts +7 -2
  20. package/templates/branded-lite/tier5-src/himi-branded-lite-simple-detail/index.ts +4 -3
  21. package/templates/branded-lite/tier5-src/wix-spaces-chat-thread/index.ts +17 -4
  22. package/templates/branded-lite/tier5-src/wix-spaces-collection-detail/index.ts +9 -9
  23. package/templates/branded-lite/tier5-src/wix-spaces-coupon-detail/index.ts +7 -9
  24. package/templates/branded-lite/tier5-src/wix-spaces-create-post/index.ts +14 -4
  25. package/templates/branded-lite/tier5-src/wix-spaces-food-order-detail/index.ts +6 -3
  26. package/templates/branded-lite/tier5-src/wix-spaces-form-detail/index.ts +7 -9
  27. package/templates/branded-lite/tier5-src/wix-spaces-forum-post/index.ts +7 -9
  28. package/templates/branded-lite/tier5-src/wix-spaces-invoice-detail/index.ts +7 -9
  29. package/templates/branded-lite/tier5-src/wix-spaces-member-detail/index.ts +8 -8
  30. package/templates/branded-lite/tier5-src/wix-spaces-photo-albums/index.ts +8 -2
  31. package/templates/branded-lite/tier5-src/wix-spaces-plan-detail/index.ts +7 -9
  32. package/templates/branded-lite/tier5-src/wix-spaces-subscription-detail/index.ts +12 -11
  33. package/templates/branded-lite/tier5-src/wix-spaces-view-profile/index.ts +22 -12
  34. package/templates/community-club/dev/index.ts +345 -97
  35. package/templates/community-club/tier5-src/wix-spaces-create-post/index.ts +14 -4
  36. package/templates/community-club/tier5-src/wix-spaces-view-profile/index.ts +22 -12
  37. package/templates/events-meetups/dev/index.ts +345 -97
  38. package/templates/events-meetups/tier5-src/wix-spaces-create-post/index.ts +14 -4
  39. package/templates/events-meetups/tier5-src/wix-spaces-view-profile/index.ts +22 -12
  40. package/templates/gym-fitness/dev/index.ts +1031 -240
  41. package/templates/gym-fitness/tier5-src/_shared/detail-presence.ts +51 -0
  42. package/templates/gym-fitness/tier5-src/himi-branded-lite-orders/index.ts +4 -1
  43. package/templates/gym-fitness/tier5-src/wix-spaces-collection-detail/index.ts +9 -9
  44. package/templates/gym-fitness/tier5-src/wix-spaces-create-post/index.ts +14 -4
  45. package/templates/gym-fitness/tier5-src/wix-spaces-plan-detail/index.ts +7 -9
  46. package/templates/gym-fitness/tier5-src/wix-spaces-subscription-detail/index.ts +12 -11
  47. package/templates/gym-fitness/tier5-src/wix-spaces-view-profile/index.ts +22 -12
  48. package/templates/nonprofit-cause/dev/index.ts +732 -188
  49. package/templates/nonprofit-cause/tier5-src/_shared/detail-presence.ts +51 -0
  50. package/templates/nonprofit-cause/tier5-src/wix-spaces-create-post/index.ts +14 -4
  51. package/templates/nonprofit-cause/tier5-src/wix-spaces-plan-detail/index.ts +7 -9
  52. package/templates/nonprofit-cause/tier5-src/wix-spaces-subscription-detail/index.ts +12 -11
  53. package/templates/nonprofit-cause/tier5-src/wix-spaces-view-profile/index.ts +22 -12
  54. package/templates/podcast-creator/dev/index.ts +942 -321
  55. package/templates/podcast-creator/tier5-src/_shared/detail-presence.ts +51 -0
  56. package/templates/podcast-creator/tier5-src/himi-branded-lite-simple-detail/index.ts +4 -3
  57. package/templates/podcast-creator/tier5-src/wix-spaces-create-post/index.ts +14 -4
  58. package/templates/podcast-creator/tier5-src/wix-spaces-plan-detail/index.ts +7 -9
  59. package/templates/podcast-creator/tier5-src/wix-spaces-subscription-detail/index.ts +12 -11
  60. package/templates/podcast-creator/tier5-src/wix-spaces-view-profile/index.ts +22 -12
  61. package/templates/portfolio-creative/dev/index.ts +676 -169
  62. package/templates/portfolio-creative/tier5-src/_shared/detail-presence.ts +51 -0
  63. package/templates/portfolio-creative/tier5-src/himi-branded-lite-orders/index.ts +4 -1
  64. package/templates/portfolio-creative/tier5-src/himi-branded-lite-portfolio-project/index.ts +7 -2
  65. package/templates/portfolio-creative/tier5-src/wix-spaces-collection-detail/index.ts +9 -9
  66. package/templates/portfolio-creative/tier5-src/wix-spaces-create-post/index.ts +14 -4
  67. package/templates/portfolio-creative/tier5-src/wix-spaces-view-profile/index.ts +22 -12
  68. package/templates/restaurant/dev/index.ts +529 -149
  69. package/templates/restaurant/tier5-src/_shared/detail-presence.ts +51 -0
  70. package/templates/restaurant/tier5-src/wix-spaces-create-post/index.ts +14 -4
  71. package/templates/restaurant/tier5-src/wix-spaces-food-order-detail/index.ts +6 -3
  72. package/templates/restaurant/tier5-src/wix-spaces-view-profile/index.ts +22 -12
  73. package/templates/retail-store/dev/index.ts +643 -148
  74. package/templates/retail-store/tier5-src/_shared/detail-presence.ts +51 -0
  75. package/templates/retail-store/tier5-src/himi-branded-lite-orders/index.ts +4 -1
  76. package/templates/retail-store/tier5-src/wix-spaces-collection-detail/index.ts +9 -9
  77. package/templates/retail-store/tier5-src/wix-spaces-create-post/index.ts +14 -4
  78. package/templates/retail-store/tier5-src/wix-spaces-view-profile/index.ts +22 -12
  79. package/templates/yoga-studio/dev/index.ts +711 -183
  80. package/templates/yoga-studio/tier5-src/_shared/detail-presence.ts +51 -0
  81. package/templates/yoga-studio/tier5-src/wix-spaces-create-post/index.ts +14 -4
  82. package/templates/yoga-studio/tier5-src/wix-spaces-plan-detail/index.ts +7 -9
  83. package/templates/yoga-studio/tier5-src/wix-spaces-subscription-detail/index.ts +12 -11
  84. package/templates/yoga-studio/tier5-src/wix-spaces-view-profile/index.ts +22 -12
@@ -0,0 +1,51 @@
1
+ // Whether a detail screen's entity is there — the worker half of the shared not-found
2
+ // state (the `detailNotFound` descriptor builder).
3
+ //
4
+ // A detail screen's descriptor asks two questions, both as `hiddenWhen` flags:
5
+ // • `noEntity` → hide the entity's content AND its actions (Like, Follow, Send…)
6
+ // • `hideNotFound` → hide the not-found state
7
+ // Four answers exist, and only one of them shows the not-found state:
8
+ // loading — a loading line (`hideLoading` false); no content, no actions, no verdict
9
+ // found — content + actions shown
10
+ // missing — not-found shown, content + actions hidden
11
+ // failed — a load-error line (`hideLoadError` false); no content, no actions
12
+ // A load that FAILS for any reason other than "not found" is `failed`, never `missing`:
13
+ // an offline device must not tell the user their invoice was deleted.
14
+
15
+ export type Presence = "loading" | "found" | "missing" | "failed";
16
+
17
+ export interface PresenceFlags {
18
+ found: boolean;
19
+ notFound: boolean;
20
+ noEntity: boolean;
21
+ hideNotFound: boolean;
22
+ hideLoading: boolean;
23
+ hideLoadError: boolean;
24
+ }
25
+
26
+ export function presence(p: Presence): PresenceFlags {
27
+ return {
28
+ found: p === "found", notFound: p === "missing", noEntity: p !== "found", hideNotFound: p !== "missing",
29
+ hideLoading: p !== "loading", hideLoadError: p !== "failed",
30
+ };
31
+ }
32
+
33
+ /** The entity id the host baked into this detail screen (`himi.open` params). */
34
+ export function detailInputId(): string {
35
+ const v = (globalThis as { himalaya?: { input?: { id?: unknown } } }).himalaya?.input?.id;
36
+ return typeof v === "string" ? v.trim() : "";
37
+ }
38
+
39
+ /** True when a Wix read failed because the record does not exist: the SDK rejects
40
+ * with "HTTP <code>: <body>", so an HTTP 404, or Wix's `NOT_FOUND` error code. A bare
41
+ * "not found" in a message is NOT enough — "capability wix_api not found" or "Method
42
+ * not found" is a broken integration, and must not tell the user the record is gone. */
43
+ export function isNotFoundError(error: unknown): boolean {
44
+ const msg = String((error as { message?: unknown } | null)?.message ?? error ?? "");
45
+ return /\bHTTP\s*404\b/.test(msg) || /\bNOT_FOUND\b/.test(msg);
46
+ }
47
+
48
+ /** `missing` for a not-found failure, `failed` (claim nothing) otherwise. */
49
+ export function presenceAfterError(error: unknown): PresenceFlags {
50
+ return presence(isNotFoundError(error) ? "missing" : "failed");
51
+ }
@@ -54,6 +54,7 @@ interface State {
54
54
  noEmpty: boolean;
55
55
  hideList: boolean;
56
56
  needsSignIn?: boolean;
57
+ noSignIn?: boolean;
57
58
  nextCursor?: string;
58
59
  complete?: boolean;
59
60
  pageError?: boolean;
@@ -127,7 +128,9 @@ function derive(state: State): State {
127
128
  notLoading: state.status !== "loading",
128
129
  isErrored: state.status === "error",
129
130
  notErrored: state.status !== "error",
130
- noEmpty: state.status !== "ready" || hasRows,
131
+ // A guest gets the shared member gate, not the "Browse products" empty card.
132
+ noEmpty: state.status !== "ready" || hasRows || state.needsSignIn === true,
133
+ noSignIn: state.status !== "ready" || state.needsSignIn !== true,
131
134
  hideList: state.status !== "ready" || state.needsSignIn === true,
132
135
  };
133
136
  }
@@ -5,6 +5,7 @@ import { defineService } from "@himalaya/state";
5
5
  import { listProjectItems, listProjects, type WixPortfolioProjectItem } from "@himalaya/wix-portfolio";
6
6
  import { wixMediaUrl, WIX_DETAIL_IMAGE } from "@himalaya/wix-media";
7
7
  import { copyParam, stashCopy } from "../_shared/himi-copy.js";
8
+ import { presence, type PresenceFlags } from "../_shared/detail-presence.js";
8
9
 
9
10
  interface MediaRow {
10
11
  id: string;
@@ -20,7 +21,7 @@ interface MediaRow {
20
21
  noMedia: boolean;
21
22
  }
22
23
 
23
- interface State {
24
+ interface State extends PresenceFlags {
24
25
  status: "loading" | "ready" | "error";
25
26
  id: string;
26
27
  title: string;
@@ -99,6 +100,9 @@ function unavailableProject(state: State, status: "ready" | "error", id: string,
99
100
  coverImage: "",
100
101
  items: [],
101
102
  emptyText,
103
+ // A project that is not there is `missing`; one that failed to load is not — the
104
+ // error card speaks, and nothing claims the project was removed.
105
+ ...presence(status === "ready" ? "missing" : "failed"),
102
106
  });
103
107
  }
104
108
 
@@ -106,7 +110,7 @@ const svc = defineService<State, { load: (state: State, arg?: unknown) => Promis
106
110
  state: derive({
107
111
  status: "loading", id: "", title: "", initial: "", description: "", coverImage: "", items: [], emptyText: "",
108
112
  isLoading: true, notLoading: false, isErrored: false, notErrored: true,
109
- hasCover: false, noCover: true, hasItems: false, noItems: true, hideContent: true, noEmpty: true,
113
+ hasCover: false, noCover: true, hasItems: false, noItems: true, hideContent: true, noEmpty: true, ...presence("loading"),
110
114
  }),
111
115
  actions: {
112
116
  load: async (state, arg) => {
@@ -132,6 +136,7 @@ const svc = defineService<State, { load: (state: State, arg?: unknown) => Promis
132
136
  coverImage: wixMediaUrl(project.coverImage || "", WIX_DETAIL_IMAGE),
133
137
  items,
134
138
  emptyText: items.length ? "" : copyParam("portfolioProject.noMediaText", "This project has no published media yet."),
139
+ ...presence("found"),
135
140
  });
136
141
  } catch {
137
142
  return unavailableProject(state, "error", id, copyParam("portfolioProject.errorText", "This project could not be loaded. Try again."));
@@ -7,19 +7,17 @@ import {
7
7
  getStoreCategory,
8
8
  type WixCollection,
9
9
  } from "@himalaya/wix-stores-collections";
10
+ import { presence, presenceAfterError, detailInputId, type PresenceFlags } from "../_shared/detail-presence.js";
10
11
 
11
- interface S { status: "loading" | "ready"; name: string; subtitle: string; description: string }
12
+ interface S extends PresenceFlags { status: "loading" | "ready"; name: string; subtitle: string; description: string }
12
13
 
13
- function inputId(): string {
14
- return (globalThis as { himalaya?: { input?: { id?: string } } }).himalaya?.input?.id ?? "";
15
- }
16
14
 
17
15
  const svc = defineService<S, { load: (s: S) => Promise<S> }>("spacesCollectionDetail", {
18
- state: { status: "loading", name: "", subtitle: "", description: "" },
16
+ state: { status: "loading", name: "", subtitle: "", description: "", ...presence("loading") },
19
17
  actions: {
20
18
  load: async (s) => {
21
- const id = inputId();
22
- if (!id) return { ...s, status: "ready" };
19
+ const id = detailInputId();
20
+ if (!id) return { ...s, status: "ready", ...presence("missing") };
23
21
  try {
24
22
  const res = await getStoreCategory(id);
25
23
  const c = res.category;
@@ -29,6 +27,7 @@ const svc = defineService<S, { load: (s: S) => Promise<S> }>("spacesCollectionDe
29
27
  name: c.name || "Category",
30
28
  subtitle: categoryCount(c),
31
29
  description: c.description || "",
30
+ ...presence("found"),
32
31
  };
33
32
  }
34
33
  } catch { /* fall through to V1 */ }
@@ -43,9 +42,10 @@ const svc = defineService<S, { load: (s: S) => Promise<S> }>("spacesCollectionDe
43
42
  name: c.name || "Collection",
44
43
  subtitle: n != null ? `${n} ${n === 1 ? "product" : "products"}` : "",
45
44
  description: c.description || "",
45
+ ...presence("found"),
46
46
  };
47
- } catch {
48
- return { ...s, status: "ready" };
47
+ } catch (e) {
48
+ return { ...s, status: "ready", ...presenceAfterError(e) };
49
49
  }
50
50
  },
51
51
  },
@@ -4,6 +4,7 @@
4
4
  // create endpoint in the SDK; blog is the available authored-content write.)
5
5
  import { defineService } from "@himalaya/state";
6
6
  import { createDraftPost, publishDraftPost } from "@himalaya/wix-blog";
7
+ import { memberAccess } from "../_shared/member-access.js";
7
8
 
8
9
  // `bodyFocus` is a focus TOKEN, not a flag: the body field focuses whenever this
9
10
  // becomes a new non-empty value (`TextFieldProps.focus_path`). A monotonic counter
@@ -14,7 +15,11 @@ import { createDraftPost, publishDraftPost } from "@himalaya/wix-blog";
14
15
  // the new state), so there is no way to observe an intermediate value while `submit`
15
16
  // is awaiting the network. A type that promises a state nobody can ever see reads as
16
17
  // an in-flight guard that exists — see `posting` below for the one that works.
17
- interface S { status: "ready"; title: string; body: string; hint: string; bodyFocus: string }
18
+ // `needsSignIn` / `noSignIn` / `hideForm` drive the member gate: a guest sees the
19
+ // gate and NO form, a member sees the form, and until `load` has asked the
20
+ // host who is signed in, neither shows — so a guest can never start typing a post
21
+ // that would only fail on submit.
22
+ interface S { status: "ready"; title: string; body: string; hint: string; bodyFocus: string; needsSignIn: boolean; noSignIn: boolean; hideForm: boolean }
18
23
 
19
24
  // The real double-press guard, module-local because it must live OUTSIDE published
20
25
  // state for the reason above. One worker instance owns one composer, so a module flag
@@ -33,15 +38,20 @@ function val(arg?: unknown): string {
33
38
  return typeof arg === "string" ? arg : ((arg as { value?: string; text?: string })?.value ?? (arg as { text?: string })?.text ?? "");
34
39
  }
35
40
 
36
- const svc = defineService<S, { setTitle: (s: S, a?: unknown) => S; setBody: (s: S, a?: unknown) => S; focusBody: (s: S) => S; submit: (s: S) => Promise<S> }>("spacesCreatePost", {
37
- state: { status: "ready", title: "", body: "", hint: "Share something with the community.", bodyFocus: "" },
41
+ const svc = defineService<S, { load: (s: S) => Promise<S>; setTitle: (s: S, a?: unknown) => S; setBody: (s: S, a?: unknown) => S; focusBody: (s: S) => S; submit: (s: S) => Promise<S> }>("spacesCreatePost", {
42
+ state: { status: "ready", title: "", body: "", hint: "Share something with the community.", bodyFocus: "", needsSignIn: false, noSignIn: true, hideForm: true },
38
43
  actions: {
44
+ load: async (s) => {
45
+ const { email } = await memberAccess();
46
+ return { ...s, needsSignIn: !email, noSignIn: !!email, hideForm: !email };
47
+ },
39
48
  setTitle: (s, a) => ({ ...s, title: val(a) }),
40
49
  setBody: (s, a) => ({ ...s, body: val(a) }),
41
50
  // Title's Next key lands here. The keyboard stays up (that is the "next"
42
51
  // contract) and the body field takes focus.
43
52
  focusBody: (s) => ({ ...s, bodyFocus: bumpToken(s.bodyFocus) }),
44
53
  submit: async (s) => {
54
+ if (s.needsSignIn) return { ...s, hint: "Sign in to post." };
45
55
  const title = s.title.trim();
46
56
  if (!title) return { ...s, hint: "Please enter a title." };
47
57
  // A second press while the first is still in flight is a NO-OP, not a second
@@ -63,7 +73,7 @@ const svc = defineService<S, { setTitle: (s: S, a?: unknown) => S; setBody: (s:
63
73
  // steals focus, so those transitions must clear it (see himi-store-account's
64
74
  // NO_FOCUS on its mode switches). Same token, opposite fix; the deciding
65
75
  // question is always whether the field stays mounted.
66
- return { status: "ready", title: "", body: "", bodyFocus: s.bodyFocus, hint: `Posted “${title}” ✓ — it now appears in Blog.` };
76
+ return { ...s, status: "ready", title: "", body: "", bodyFocus: s.bodyFocus, hint: `Posted “${title}” ✓ — it now appears in Blog.` };
67
77
  } catch {
68
78
  return { ...s, status: "ready", hint: "Couldn't post — check your connection and try again." };
69
79
  } finally {
@@ -3,10 +3,15 @@
3
3
  // paint the seeded "apptestercoowner" demo identity when the session is a real
4
4
  // member (or a guest — show honesty instead).
5
5
  import { defineService } from "@himalaya/state";
6
- import { dispatch } from "@himalaya/dispatch";
6
+ import { memberAccess } from "../_shared/member-access.js";
7
7
  import { getMyData, displayName } from "@himalaya/wix-users";
8
8
 
9
- interface S { status: "loading" | "ready"; name: string; handle: string; about: string; initial: string; photo: string }
9
+ // `needsSignIn` / `noSignIn` drive the shared member gate: a guest gets the
10
+ // gate with a Sign in button and no placeholder profile. `profileHidden` stays true until
11
+ // `load` has resolved a member, so a guest never glimpses the placeholder avatar while
12
+ // the membership check is still running; `hideLoading` is false for exactly that moment,
13
+ // so the screen shows a loading line instead of going blank.
14
+ interface S { status: "loading" | "ready"; name: string; handle: string; about: string; initial: string; photo: string; needsSignIn: boolean; noSignIn: boolean; profileHidden: boolean; hideLoading: boolean }
10
15
 
11
16
  const EMPTY: S = {
12
17
  status: "loading",
@@ -15,18 +20,17 @@ const EMPTY: S = {
15
20
  about: "No info to display.",
16
21
  initial: "?",
17
22
  photo: "",
23
+ needsSignIn: false,
24
+ noSignIn: true,
25
+ profileHidden: true,
26
+ hideLoading: false,
18
27
  };
19
28
 
29
+ // The SAME member check create-post and orders use (`memberAccess`: the host's
30
+ // memberIdentity, falling back to wix-users only on hosts that predate that action), so
31
+ // one shared gate never gives two answers in one app.
20
32
  async function sessionEmail(): Promise<string> {
21
- try {
22
- const identity = await dispatch("himi.wix.memberIdentity") as unknown;
23
- const direct = identity as { email?: unknown } | null;
24
- const wrapped = identity as { payload?: { email?: unknown } } | null;
25
- const email = direct?.email ?? wrapped?.payload?.email;
26
- return typeof email === "string" ? email.trim() : "";
27
- } catch {
28
- return "";
29
- }
33
+ return (await memberAccess()).email;
30
34
  }
31
35
 
32
36
  function wixApi() {
@@ -37,7 +41,7 @@ function fromParts(name: string, handleRaw: string, about: string, photo = ""):
37
41
  const clean = name.trim();
38
42
  const handle = "@" + (handleRaw || clean || "member").replace(/\s+/g, "").replace(/^@+/, "").toLowerCase();
39
43
  const initial = (clean.split(/\s+/).filter(Boolean).map((w) => w[0]).join("").slice(0, 2) || "?").toUpperCase();
40
- return { status: "ready", name: clean || "Member", handle, about: about || "No info to display.", initial, photo };
44
+ return { status: "ready", name: clean || "Member", handle, about: about || "No info to display.", initial, photo, needsSignIn: false, noSignIn: true, profileHidden: false, hideLoading: true };
41
45
  }
42
46
 
43
47
  const svc = defineService<S, { load: (s: S) => Promise<S> }>("spacesViewProfile", {
@@ -48,11 +52,17 @@ const svc = defineService<S, { load: (s: S) => Promise<S> }>("spacesViewProfile"
48
52
  if (!email) {
49
53
  return {
50
54
  status: "ready",
55
+ // Kept for hosts without the shared member gate (the Spaces clone renders these
56
+ // fields directly); branded-lite hides the profile block behind `profileHidden`.
51
57
  name: "Sign in to view your profile",
52
58
  handle: "",
53
59
  about: "Your public profile is available after you sign in from Account.",
54
60
  initial: "?",
55
61
  photo: "",
62
+ needsSignIn: true,
63
+ noSignIn: false,
64
+ profileHidden: true,
65
+ hideLoading: true,
56
66
  };
57
67
  }
58
68
  try {