@wix/himalaya-cli 0.1576.0 → 0.1577.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 (76) hide show
  1. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/loyalty/src/index.d.ts +64 -6
  2. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/online-programs/src/index.d.ts +87 -0
  3. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-loyalty.mjs +15 -3
  4. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-online-programs.mjs +42 -0
  5. package/dist/cli.mjs +10810 -10592
  6. package/dist/preview-assets.generated.bin +0 -0
  7. package/fixtures/wix-canned.json +56 -3
  8. package/guides/one-app-section-mappings.md +1 -1
  9. package/guides/one-app-to-himi-app.md +50 -1
  10. package/guides/sdk-reference.json +133 -9
  11. package/package.json +2 -2
  12. package/templates/beauty-salon/tier5-src/_shared/branded-lite-hosted.ts +1 -0
  13. package/templates/beauty-salon/tier5-src/_shared/owner-load-error.ts +8 -0
  14. package/templates/beauty-salon-owner/dev/index.ts +52 -27
  15. package/templates/beauty-salon-owner/tier5-src/_shared/owner-load-error.ts +8 -0
  16. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-post/index.ts +20 -8
  17. package/templates/branded-lite/config.ts +7 -0
  18. package/templates/branded-lite/dev/index.ts +833 -0
  19. package/templates/branded-lite/himalaya.content.json +3 -0
  20. package/templates/branded-lite/l10n/en.json +4 -2
  21. package/templates/branded-lite/tier5-src/_shared/branded-lite-hosted.ts +1 -0
  22. package/templates/branded-lite/tier5-src/_shared/owner-load-error.ts +8 -0
  23. package/templates/branded-lite/tier5-src/himi-branded-lite-member-programs/index.ts +658 -0
  24. package/templates/community-club/tier5-src/_shared/branded-lite-hosted.ts +1 -0
  25. package/templates/community-club/tier5-src/_shared/owner-load-error.ts +8 -0
  26. package/templates/community-club-owner/dev/index.ts +52 -27
  27. package/templates/community-club-owner/tier5-src/_shared/owner-load-error.ts +8 -0
  28. package/templates/community-club-owner/tier5-src/himi-owner-lite-post/index.ts +20 -8
  29. package/templates/events-meetups/tier5-src/_shared/branded-lite-hosted.ts +1 -0
  30. package/templates/events-meetups/tier5-src/_shared/owner-load-error.ts +8 -0
  31. package/templates/events-meetups-owner/dev/index.ts +52 -27
  32. package/templates/events-meetups-owner/tier5-src/_shared/owner-load-error.ts +8 -0
  33. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-post/index.ts +20 -8
  34. package/templates/gym-fitness/tier5-src/_shared/branded-lite-hosted.ts +1 -0
  35. package/templates/gym-fitness/tier5-src/_shared/owner-load-error.ts +8 -0
  36. package/templates/gym-fitness-owner/dev/index.ts +52 -27
  37. package/templates/gym-fitness-owner/tier5-src/_shared/owner-load-error.ts +8 -0
  38. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-post/index.ts +20 -8
  39. package/templates/himi-chat/tier5-src/_shared/owner-load-error.ts +8 -0
  40. package/templates/nonprofit-cause/tier5-src/_shared/branded-lite-hosted.ts +1 -0
  41. package/templates/nonprofit-cause/tier5-src/_shared/owner-load-error.ts +8 -0
  42. package/templates/nonprofit-cause-owner/dev/index.ts +52 -27
  43. package/templates/nonprofit-cause-owner/tier5-src/_shared/owner-load-error.ts +8 -0
  44. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-post/index.ts +20 -8
  45. package/templates/owner-lite/dev/index.ts +52 -27
  46. package/templates/owner-lite/tier5-src/_shared/owner-load-error.ts +8 -0
  47. package/templates/owner-lite/tier5-src/himi-owner-lite-post/index.ts +20 -8
  48. package/templates/podcast-creator/tier5-src/_shared/branded-lite-hosted.ts +1 -0
  49. package/templates/podcast-creator/tier5-src/_shared/owner-load-error.ts +8 -0
  50. package/templates/podcast-creator-owner/dev/index.ts +52 -27
  51. package/templates/podcast-creator-owner/tier5-src/_shared/owner-load-error.ts +8 -0
  52. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-post/index.ts +20 -8
  53. package/templates/portfolio-creative/tier5-src/_shared/branded-lite-hosted.ts +1 -0
  54. package/templates/portfolio-creative/tier5-src/_shared/owner-load-error.ts +8 -0
  55. package/templates/portfolio-creative-owner/dev/index.ts +52 -27
  56. package/templates/portfolio-creative-owner/tier5-src/_shared/owner-load-error.ts +8 -0
  57. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-post/index.ts +20 -8
  58. package/templates/restaurant/tier5-src/_shared/branded-lite-hosted.ts +1 -0
  59. package/templates/restaurant/tier5-src/_shared/owner-load-error.ts +8 -0
  60. package/templates/restaurant-owner/dev/index.ts +52 -27
  61. package/templates/restaurant-owner/tier5-src/_shared/owner-load-error.ts +8 -0
  62. package/templates/restaurant-owner/tier5-src/himi-owner-lite-post/index.ts +20 -8
  63. package/templates/retail-store/tier5-src/_shared/branded-lite-hosted.ts +1 -0
  64. package/templates/retail-store/tier5-src/_shared/owner-load-error.ts +8 -0
  65. package/templates/retail-store-owner/dev/index.ts +52 -27
  66. package/templates/retail-store-owner/tier5-src/_shared/owner-load-error.ts +8 -0
  67. package/templates/retail-store-owner/tier5-src/himi-owner-lite-post/index.ts +20 -8
  68. package/templates/yoga-studio/config.ts +7 -0
  69. package/templates/yoga-studio/dev/index.ts +833 -0
  70. package/templates/yoga-studio/himalaya.content.json +3 -0
  71. package/templates/yoga-studio/tier5-src/_shared/branded-lite-hosted.ts +1 -0
  72. package/templates/yoga-studio/tier5-src/_shared/owner-load-error.ts +8 -0
  73. package/templates/yoga-studio/tier5-src/himi-branded-lite-member-programs/index.ts +658 -0
  74. package/templates/yoga-studio-owner/dev/index.ts +52 -27
  75. package/templates/yoga-studio-owner/tier5-src/_shared/owner-load-error.ts +8 -0
  76. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-post/index.ts +20 -8
@@ -10,7 +10,7 @@ import { getPost, getDraftPost, publishDraftPost, revertDraftPost, deleteDraftPo
10
10
  import { parseRicosJson } from "@himalaya/wix-rich-content";
11
11
  import { openRichLink } from "../_shared/rich-content-link.js";
12
12
  import { richBody } from "../_shared/rich-body.js";
13
- import { ownerLoadError, isBridgeUnavailable, isTransportFailure, isOwnerPreviewDemo, ownerDemoFallback, ownerWriteError, isNotFound } from "../_shared/owner-load-error.js";
13
+ import { ownerLoadError, isBridgeUnavailable, readWasUnreachable, isOwnerPreviewDemo, ownerDemoFallback, ownerWriteError, isNotFound } from "../_shared/owner-load-error.js";
14
14
  import { DEMO_POSTS, DEMO_NOTE, DEMO_SAVE_NOTE } from "../_shared/owner-preview-demo.js";
15
15
  import { stashCopy, copyParam } from "../_shared/himi-copy.js";
16
16
  import { get as dbGet, upsert as dbUpsert, remove as dbRemove, type DbEntity } from "../_shared/owner-local-db.js";
@@ -270,14 +270,24 @@ async function refresh(prev: PostState, id: string, actionMessage = "", opts: {
270
270
  try { post = (await getPost(id)).post; } catch (e) { lastError = postError = e; /* draft-only */ }
271
271
  try { draft = (await getDraftPost(id)).draftPost; } catch (e) { lastError = e; /* published-only or missing */ }
272
272
  if (!post && !draft) {
273
- // Both arms swallowed their errors above; surface the DESIGNED bridge
274
- // boundary (and a device with no connection) so the callers' catches can fall
275
- // back to the cached row or the demo post — never "post not found" for a post
276
- // that was only unreachable.
273
+ // Both arms swallowed their errors above. Neither a failed published read
274
+ // nor a missing draft proves the post is gone.
277
275
  // Either read counts: a published-only post's draft read is always a 404, so the
278
276
  // published read's failure is the one that says whether the post was only unreachable.
279
277
  for (const err of [postError, lastError]) {
280
- if (isBridgeUnavailable(err) || isTransportFailure(err)) throw err;
278
+ // getPost may wrap the original read failure when its query fallback is
279
+ // empty. Preserve that cause's status instead of painting the wrapper's
280
+ // "Blog post not found" text for a gateway failure.
281
+ const cause = (err as { cause?: unknown } | undefined)?.cause;
282
+ for (const failure of [cause, err]) {
283
+ if (isBridgeUnavailable(failure) || readWasUnreachable(failure)) throw failure;
284
+ }
285
+ }
286
+ for (const err of [postError, lastError]) {
287
+ // Non-transient access/validation failures keep their status too: the
288
+ // SDK's empty-query wrapper is not the reason this read failed.
289
+ const failure = (err as { cause?: unknown } | undefined)?.cause ?? err;
290
+ if (failure !== undefined && !isNotFound(failure)) throw failure;
281
291
  }
282
292
  // Wix says it is gone on BOTH reads (a published-only post's draft read is a 404 on its
283
293
  // own): its cached row must not outlive it, or an offline load repaints a deleted post.
@@ -361,7 +371,8 @@ const svc = defineService<PostState, {
361
371
  } catch (e) {
362
372
  // Offline: fall back to the last-synced local row before the demo/error paths.
363
373
  // Wix says it is gone (e.g. a delete whose reply was lost): drop the row, never show it.
364
- const cached = isNotFound(e) ? (await cacheDrop(id), null) : await cacheGet(id);
374
+ const cached = isNotFound(e) ? (await cacheDrop(id), null)
375
+ : readWasUnreachable(e) || isBridgeUnavailable(e) ? await cacheGet(id) : null;
365
376
  if (cached) return cachedDetail(s, cached);
366
377
  if (ownerDemoFallback(e)) {
367
378
  const demo = demoDetail(s, id);
@@ -385,7 +396,8 @@ const svc = defineService<PostState, {
385
396
  return loaded;
386
397
  } catch (e) {
387
398
  // Wix says it is gone (e.g. a delete whose reply was lost): drop the row, never show it.
388
- const cached = isNotFound(e) ? (await cacheDrop(s.id), null) : await cacheGet(s.id);
399
+ const cached = isNotFound(e) ? (await cacheDrop(s.id), null)
400
+ : readWasUnreachable(e) || isBridgeUnavailable(e) ? await cacheGet(s.id) : null;
389
401
  if (cached) return cachedDetail(s, cached);
390
402
  if (ownerDemoFallback(e)) {
391
403
  const demo = demoDetail(s, s.id);
@@ -194,6 +194,14 @@ export function isTransportFailure(e: unknown): boolean {
194
194
  return TRANSPORT.some((re) => re.test(msg));
195
195
  }
196
196
 
197
+ /** A failed READ says nothing about the record's existence when Wix is
198
+ * unreachable, overloaded or failing. Unlike the legacy bridge predicate this
199
+ * is independent of preview mode; a live preview's gateway failure is one too. */
200
+ export function readWasUnreachable(e: unknown): boolean {
201
+ const msg = String((e as Error)?.message ?? e ?? "");
202
+ return isTransportFailure(e) || /HTTP (?:408|429|5\d{2})\b/.test(msg);
203
+ }
204
+
197
205
  /** Transport failures that happen while CONNECTING — no connection, no route, no such host,
198
206
  * connection refused — so the request provably never left the device. Every other
199
207
  * transport failure (a timeout, a reset, a bad or truncated response, a browser's fetch
@@ -119,6 +119,7 @@ export const HOSTED_SCREEN_VERTICALS: Readonly<Record<string, readonly BrandedLi
119
119
  himi_portfolio: ["portfolio"],
120
120
  himi_portfolio_project: ["portfolio"],
121
121
  himi_programs: ["programs"],
122
+ himi_member_programs: ["programs"],
122
123
  himi_reservations: ["reservations"],
123
124
  himi_reserve: ["restaurants"],
124
125
  himi_review: ["reviews"],
@@ -194,6 +194,14 @@ export function isTransportFailure(e: unknown): boolean {
194
194
  return TRANSPORT.some((re) => re.test(msg));
195
195
  }
196
196
 
197
+ /** A failed READ says nothing about the record's existence when Wix is
198
+ * unreachable, overloaded or failing. Unlike the legacy bridge predicate this
199
+ * is independent of preview mode; a live preview's gateway failure is one too. */
200
+ export function readWasUnreachable(e: unknown): boolean {
201
+ const msg = String((e as Error)?.message ?? e ?? "");
202
+ return isTransportFailure(e) || /HTTP (?:408|429|5\d{2})\b/.test(msg);
203
+ }
204
+
197
205
  /** Transport failures that happen while CONNECTING — no connection, no route, no such host,
198
206
  * connection refused — so the request provably never left the device. Every other
199
207
  * transport failure (a timeout, a reset, a bad or truncated response, a browser's fetch
@@ -65377,16 +65377,17 @@ const SCREENS = runtimeSetting({
65377
65377
  "type": "VStack",
65378
65378
  "props": {
65379
65379
  "spacing": 10,
65380
+ "alignment": "leading",
65380
65381
  "children": [
65381
65382
  {
65382
- "id": "himi_owner_lite_inbox_thread.sk1",
65383
+ "id": "himi_owner_lite_inbox_thread.loading_text",
65383
65384
  "body": {
65384
65385
  "package": "himalaya",
65385
- "type": "Skeleton",
65386
+ "type": "Text",
65386
65387
  "props": {
65387
- "width": 220,
65388
- "height": 44,
65389
- "cornerRadius": 16
65388
+ "text": "Loading conversation…",
65389
+ "style": "body",
65390
+ "color": "textSecondary"
65390
65391
  }
65391
65392
  }
65392
65393
  },
@@ -65394,51 +65395,75 @@ const SCREENS = runtimeSetting({
65394
65395
  "id": "",
65395
65396
  "body": {
65396
65397
  "package": "himalaya",
65397
- "type": "HStack",
65398
+ "type": "VStack",
65398
65399
  "props": {
65400
+ "spacing": 10,
65399
65401
  "children": [
65402
+ {
65403
+ "id": "himi_owner_lite_inbox_thread.sk1",
65404
+ "body": {
65405
+ "package": "himalaya",
65406
+ "type": "Skeleton",
65407
+ "props": {
65408
+ "width": 220,
65409
+ "height": 44,
65410
+ "cornerRadius": 16
65411
+ }
65412
+ }
65413
+ },
65400
65414
  {
65401
65415
  "id": "",
65402
65416
  "body": {
65403
65417
  "package": "himalaya",
65404
- "type": "Spacer",
65405
- "props": {}
65418
+ "type": "HStack",
65419
+ "props": {
65420
+ "children": [
65421
+ {
65422
+ "id": "",
65423
+ "body": {
65424
+ "package": "himalaya",
65425
+ "type": "Spacer",
65426
+ "props": {}
65427
+ }
65428
+ },
65429
+ {
65430
+ "id": "himi_owner_lite_inbox_thread.sk2",
65431
+ "body": {
65432
+ "package": "himalaya",
65433
+ "type": "Skeleton",
65434
+ "props": {
65435
+ "width": 180,
65436
+ "height": 44,
65437
+ "cornerRadius": 16
65438
+ }
65439
+ }
65440
+ }
65441
+ ]
65442
+ }
65406
65443
  }
65407
65444
  },
65408
65445
  {
65409
- "id": "himi_owner_lite_inbox_thread.sk2",
65446
+ "id": "himi_owner_lite_inbox_thread.sk3",
65410
65447
  "body": {
65411
65448
  "package": "himalaya",
65412
65449
  "type": "Skeleton",
65413
65450
  "props": {
65414
- "width": 180,
65415
- "height": 44,
65451
+ "width": 240,
65452
+ "height": 64,
65416
65453
  "cornerRadius": 16
65417
65454
  }
65418
65455
  }
65419
65456
  }
65420
65457
  ]
65421
65458
  }
65422
- }
65423
- },
65424
- {
65425
- "id": "himi_owner_lite_inbox_thread.sk3",
65426
- "body": {
65427
- "package": "himalaya",
65428
- "type": "Skeleton",
65429
- "props": {
65430
- "width": 240,
65431
- "height": 64,
65432
- "cornerRadius": 16
65433
- }
65459
+ },
65460
+ "modifiers": {
65461
+ "fillMaxWidth": true,
65462
+ "accessibilityRole": "decorative"
65434
65463
  }
65435
65464
  }
65436
65465
  ]
65437
65466
  }
65438
- },
65439
- "modifiers": {
65440
- "fillMaxWidth": true,
65441
- "accessibilityRole": "decorative"
65442
65467
  }
65443
65468
  }
65444
65469
  },
@@ -194,6 +194,14 @@ export function isTransportFailure(e: unknown): boolean {
194
194
  return TRANSPORT.some((re) => re.test(msg));
195
195
  }
196
196
 
197
+ /** A failed READ says nothing about the record's existence when Wix is
198
+ * unreachable, overloaded or failing. Unlike the legacy bridge predicate this
199
+ * is independent of preview mode; a live preview's gateway failure is one too. */
200
+ export function readWasUnreachable(e: unknown): boolean {
201
+ const msg = String((e as Error)?.message ?? e ?? "");
202
+ return isTransportFailure(e) || /HTTP (?:408|429|5\d{2})\b/.test(msg);
203
+ }
204
+
197
205
  /** Transport failures that happen while CONNECTING — no connection, no route, no such host,
198
206
  * connection refused — so the request provably never left the device. Every other
199
207
  * transport failure (a timeout, a reset, a bad or truncated response, a browser's fetch
@@ -10,7 +10,7 @@ import { getPost, getDraftPost, publishDraftPost, revertDraftPost, deleteDraftPo
10
10
  import { parseRicosJson } from "@himalaya/wix-rich-content";
11
11
  import { openRichLink } from "../_shared/rich-content-link.js";
12
12
  import { richBody } from "../_shared/rich-body.js";
13
- import { ownerLoadError, isBridgeUnavailable, isTransportFailure, isOwnerPreviewDemo, ownerDemoFallback, ownerWriteError, isNotFound } from "../_shared/owner-load-error.js";
13
+ import { ownerLoadError, isBridgeUnavailable, readWasUnreachable, isOwnerPreviewDemo, ownerDemoFallback, ownerWriteError, isNotFound } from "../_shared/owner-load-error.js";
14
14
  import { DEMO_POSTS, DEMO_NOTE, DEMO_SAVE_NOTE } from "../_shared/owner-preview-demo.js";
15
15
  import { stashCopy, copyParam } from "../_shared/himi-copy.js";
16
16
  import { get as dbGet, upsert as dbUpsert, remove as dbRemove, type DbEntity } from "../_shared/owner-local-db.js";
@@ -270,14 +270,24 @@ async function refresh(prev: PostState, id: string, actionMessage = "", opts: {
270
270
  try { post = (await getPost(id)).post; } catch (e) { lastError = postError = e; /* draft-only */ }
271
271
  try { draft = (await getDraftPost(id)).draftPost; } catch (e) { lastError = e; /* published-only or missing */ }
272
272
  if (!post && !draft) {
273
- // Both arms swallowed their errors above; surface the DESIGNED bridge
274
- // boundary (and a device with no connection) so the callers' catches can fall
275
- // back to the cached row or the demo post — never "post not found" for a post
276
- // that was only unreachable.
273
+ // Both arms swallowed their errors above. Neither a failed published read
274
+ // nor a missing draft proves the post is gone.
277
275
  // Either read counts: a published-only post's draft read is always a 404, so the
278
276
  // published read's failure is the one that says whether the post was only unreachable.
279
277
  for (const err of [postError, lastError]) {
280
- if (isBridgeUnavailable(err) || isTransportFailure(err)) throw err;
278
+ // getPost may wrap the original read failure when its query fallback is
279
+ // empty. Preserve that cause's status instead of painting the wrapper's
280
+ // "Blog post not found" text for a gateway failure.
281
+ const cause = (err as { cause?: unknown } | undefined)?.cause;
282
+ for (const failure of [cause, err]) {
283
+ if (isBridgeUnavailable(failure) || readWasUnreachable(failure)) throw failure;
284
+ }
285
+ }
286
+ for (const err of [postError, lastError]) {
287
+ // Non-transient access/validation failures keep their status too: the
288
+ // SDK's empty-query wrapper is not the reason this read failed.
289
+ const failure = (err as { cause?: unknown } | undefined)?.cause ?? err;
290
+ if (failure !== undefined && !isNotFound(failure)) throw failure;
281
291
  }
282
292
  // Wix says it is gone on BOTH reads (a published-only post's draft read is a 404 on its
283
293
  // own): its cached row must not outlive it, or an offline load repaints a deleted post.
@@ -361,7 +371,8 @@ const svc = defineService<PostState, {
361
371
  } catch (e) {
362
372
  // Offline: fall back to the last-synced local row before the demo/error paths.
363
373
  // Wix says it is gone (e.g. a delete whose reply was lost): drop the row, never show it.
364
- const cached = isNotFound(e) ? (await cacheDrop(id), null) : await cacheGet(id);
374
+ const cached = isNotFound(e) ? (await cacheDrop(id), null)
375
+ : readWasUnreachable(e) || isBridgeUnavailable(e) ? await cacheGet(id) : null;
365
376
  if (cached) return cachedDetail(s, cached);
366
377
  if (ownerDemoFallback(e)) {
367
378
  const demo = demoDetail(s, id);
@@ -385,7 +396,8 @@ const svc = defineService<PostState, {
385
396
  return loaded;
386
397
  } catch (e) {
387
398
  // Wix says it is gone (e.g. a delete whose reply was lost): drop the row, never show it.
388
- const cached = isNotFound(e) ? (await cacheDrop(s.id), null) : await cacheGet(s.id);
399
+ const cached = isNotFound(e) ? (await cacheDrop(s.id), null)
400
+ : readWasUnreachable(e) || isBridgeUnavailable(e) ? await cacheGet(s.id) : null;
389
401
  if (cached) return cachedDetail(s, cached);
390
402
  if (ownerDemoFallback(e)) {
391
403
  const demo = demoDetail(s, s.id);
@@ -65754,16 +65754,17 @@ const SCREENS = runtimeSetting({
65754
65754
  "type": "VStack",
65755
65755
  "props": {
65756
65756
  "spacing": 10,
65757
+ "alignment": "leading",
65757
65758
  "children": [
65758
65759
  {
65759
- "id": "himi_owner_lite_inbox_thread.sk1",
65760
+ "id": "himi_owner_lite_inbox_thread.loading_text",
65760
65761
  "body": {
65761
65762
  "package": "himalaya",
65762
- "type": "Skeleton",
65763
+ "type": "Text",
65763
65764
  "props": {
65764
- "width": 220,
65765
- "height": 44,
65766
- "cornerRadius": 16
65765
+ "text": "Loading conversation…",
65766
+ "style": "body",
65767
+ "color": "textSecondary"
65767
65768
  }
65768
65769
  }
65769
65770
  },
@@ -65771,51 +65772,75 @@ const SCREENS = runtimeSetting({
65771
65772
  "id": "",
65772
65773
  "body": {
65773
65774
  "package": "himalaya",
65774
- "type": "HStack",
65775
+ "type": "VStack",
65775
65776
  "props": {
65777
+ "spacing": 10,
65776
65778
  "children": [
65779
+ {
65780
+ "id": "himi_owner_lite_inbox_thread.sk1",
65781
+ "body": {
65782
+ "package": "himalaya",
65783
+ "type": "Skeleton",
65784
+ "props": {
65785
+ "width": 220,
65786
+ "height": 44,
65787
+ "cornerRadius": 16
65788
+ }
65789
+ }
65790
+ },
65777
65791
  {
65778
65792
  "id": "",
65779
65793
  "body": {
65780
65794
  "package": "himalaya",
65781
- "type": "Spacer",
65782
- "props": {}
65795
+ "type": "HStack",
65796
+ "props": {
65797
+ "children": [
65798
+ {
65799
+ "id": "",
65800
+ "body": {
65801
+ "package": "himalaya",
65802
+ "type": "Spacer",
65803
+ "props": {}
65804
+ }
65805
+ },
65806
+ {
65807
+ "id": "himi_owner_lite_inbox_thread.sk2",
65808
+ "body": {
65809
+ "package": "himalaya",
65810
+ "type": "Skeleton",
65811
+ "props": {
65812
+ "width": 180,
65813
+ "height": 44,
65814
+ "cornerRadius": 16
65815
+ }
65816
+ }
65817
+ }
65818
+ ]
65819
+ }
65783
65820
  }
65784
65821
  },
65785
65822
  {
65786
- "id": "himi_owner_lite_inbox_thread.sk2",
65823
+ "id": "himi_owner_lite_inbox_thread.sk3",
65787
65824
  "body": {
65788
65825
  "package": "himalaya",
65789
65826
  "type": "Skeleton",
65790
65827
  "props": {
65791
- "width": 180,
65792
- "height": 44,
65828
+ "width": 240,
65829
+ "height": 64,
65793
65830
  "cornerRadius": 16
65794
65831
  }
65795
65832
  }
65796
65833
  }
65797
65834
  ]
65798
65835
  }
65799
- }
65800
- },
65801
- {
65802
- "id": "himi_owner_lite_inbox_thread.sk3",
65803
- "body": {
65804
- "package": "himalaya",
65805
- "type": "Skeleton",
65806
- "props": {
65807
- "width": 240,
65808
- "height": 64,
65809
- "cornerRadius": 16
65810
- }
65836
+ },
65837
+ "modifiers": {
65838
+ "fillMaxWidth": true,
65839
+ "accessibilityRole": "decorative"
65811
65840
  }
65812
65841
  }
65813
65842
  ]
65814
65843
  }
65815
- },
65816
- "modifiers": {
65817
- "fillMaxWidth": true,
65818
- "accessibilityRole": "decorative"
65819
65844
  }
65820
65845
  }
65821
65846
  },
@@ -194,6 +194,14 @@ export function isTransportFailure(e: unknown): boolean {
194
194
  return TRANSPORT.some((re) => re.test(msg));
195
195
  }
196
196
 
197
+ /** A failed READ says nothing about the record's existence when Wix is
198
+ * unreachable, overloaded or failing. Unlike the legacy bridge predicate this
199
+ * is independent of preview mode; a live preview's gateway failure is one too. */
200
+ export function readWasUnreachable(e: unknown): boolean {
201
+ const msg = String((e as Error)?.message ?? e ?? "");
202
+ return isTransportFailure(e) || /HTTP (?:408|429|5\d{2})\b/.test(msg);
203
+ }
204
+
197
205
  /** Transport failures that happen while CONNECTING — no connection, no route, no such host,
198
206
  * connection refused — so the request provably never left the device. Every other
199
207
  * transport failure (a timeout, a reset, a bad or truncated response, a browser's fetch
@@ -10,7 +10,7 @@ import { getPost, getDraftPost, publishDraftPost, revertDraftPost, deleteDraftPo
10
10
  import { parseRicosJson } from "@himalaya/wix-rich-content";
11
11
  import { openRichLink } from "../_shared/rich-content-link.js";
12
12
  import { richBody } from "../_shared/rich-body.js";
13
- import { ownerLoadError, isBridgeUnavailable, isTransportFailure, isOwnerPreviewDemo, ownerDemoFallback, ownerWriteError, isNotFound } from "../_shared/owner-load-error.js";
13
+ import { ownerLoadError, isBridgeUnavailable, readWasUnreachable, isOwnerPreviewDemo, ownerDemoFallback, ownerWriteError, isNotFound } from "../_shared/owner-load-error.js";
14
14
  import { DEMO_POSTS, DEMO_NOTE, DEMO_SAVE_NOTE } from "../_shared/owner-preview-demo.js";
15
15
  import { stashCopy, copyParam } from "../_shared/himi-copy.js";
16
16
  import { get as dbGet, upsert as dbUpsert, remove as dbRemove, type DbEntity } from "../_shared/owner-local-db.js";
@@ -270,14 +270,24 @@ async function refresh(prev: PostState, id: string, actionMessage = "", opts: {
270
270
  try { post = (await getPost(id)).post; } catch (e) { lastError = postError = e; /* draft-only */ }
271
271
  try { draft = (await getDraftPost(id)).draftPost; } catch (e) { lastError = e; /* published-only or missing */ }
272
272
  if (!post && !draft) {
273
- // Both arms swallowed their errors above; surface the DESIGNED bridge
274
- // boundary (and a device with no connection) so the callers' catches can fall
275
- // back to the cached row or the demo post — never "post not found" for a post
276
- // that was only unreachable.
273
+ // Both arms swallowed their errors above. Neither a failed published read
274
+ // nor a missing draft proves the post is gone.
277
275
  // Either read counts: a published-only post's draft read is always a 404, so the
278
276
  // published read's failure is the one that says whether the post was only unreachable.
279
277
  for (const err of [postError, lastError]) {
280
- if (isBridgeUnavailable(err) || isTransportFailure(err)) throw err;
278
+ // getPost may wrap the original read failure when its query fallback is
279
+ // empty. Preserve that cause's status instead of painting the wrapper's
280
+ // "Blog post not found" text for a gateway failure.
281
+ const cause = (err as { cause?: unknown } | undefined)?.cause;
282
+ for (const failure of [cause, err]) {
283
+ if (isBridgeUnavailable(failure) || readWasUnreachable(failure)) throw failure;
284
+ }
285
+ }
286
+ for (const err of [postError, lastError]) {
287
+ // Non-transient access/validation failures keep their status too: the
288
+ // SDK's empty-query wrapper is not the reason this read failed.
289
+ const failure = (err as { cause?: unknown } | undefined)?.cause ?? err;
290
+ if (failure !== undefined && !isNotFound(failure)) throw failure;
281
291
  }
282
292
  // Wix says it is gone on BOTH reads (a published-only post's draft read is a 404 on its
283
293
  // own): its cached row must not outlive it, or an offline load repaints a deleted post.
@@ -361,7 +371,8 @@ const svc = defineService<PostState, {
361
371
  } catch (e) {
362
372
  // Offline: fall back to the last-synced local row before the demo/error paths.
363
373
  // Wix says it is gone (e.g. a delete whose reply was lost): drop the row, never show it.
364
- const cached = isNotFound(e) ? (await cacheDrop(id), null) : await cacheGet(id);
374
+ const cached = isNotFound(e) ? (await cacheDrop(id), null)
375
+ : readWasUnreachable(e) || isBridgeUnavailable(e) ? await cacheGet(id) : null;
365
376
  if (cached) return cachedDetail(s, cached);
366
377
  if (ownerDemoFallback(e)) {
367
378
  const demo = demoDetail(s, id);
@@ -385,7 +396,8 @@ const svc = defineService<PostState, {
385
396
  return loaded;
386
397
  } catch (e) {
387
398
  // Wix says it is gone (e.g. a delete whose reply was lost): drop the row, never show it.
388
- const cached = isNotFound(e) ? (await cacheDrop(s.id), null) : await cacheGet(s.id);
399
+ const cached = isNotFound(e) ? (await cacheDrop(s.id), null)
400
+ : readWasUnreachable(e) || isBridgeUnavailable(e) ? await cacheGet(s.id) : null;
389
401
  if (cached) return cachedDetail(s, cached);
390
402
  if (ownerDemoFallback(e)) {
391
403
  const demo = demoDetail(s, s.id);
@@ -119,6 +119,7 @@ export const HOSTED_SCREEN_VERTICALS: Readonly<Record<string, readonly BrandedLi
119
119
  himi_portfolio: ["portfolio"],
120
120
  himi_portfolio_project: ["portfolio"],
121
121
  himi_programs: ["programs"],
122
+ himi_member_programs: ["programs"],
122
123
  himi_reservations: ["reservations"],
123
124
  himi_reserve: ["restaurants"],
124
125
  himi_review: ["reviews"],
@@ -194,6 +194,14 @@ export function isTransportFailure(e: unknown): boolean {
194
194
  return TRANSPORT.some((re) => re.test(msg));
195
195
  }
196
196
 
197
+ /** A failed READ says nothing about the record's existence when Wix is
198
+ * unreachable, overloaded or failing. Unlike the legacy bridge predicate this
199
+ * is independent of preview mode; a live preview's gateway failure is one too. */
200
+ export function readWasUnreachable(e: unknown): boolean {
201
+ const msg = String((e as Error)?.message ?? e ?? "");
202
+ return isTransportFailure(e) || /HTTP (?:408|429|5\d{2})\b/.test(msg);
203
+ }
204
+
197
205
  /** Transport failures that happen while CONNECTING — no connection, no route, no such host,
198
206
  * connection refused — so the request provably never left the device. Every other
199
207
  * transport failure (a timeout, a reset, a bad or truncated response, a browser's fetch