@wix/himalaya-cli 0.1152.0 → 0.1153.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.
@@ -21,6 +21,8 @@ export interface WixServicePayment {
21
21
  }
22
22
  export interface WixService {
23
23
  id: string;
24
+ /** Optimistic-concurrency token — required by every v2 write (updateService reads it if omitted). */
25
+ revision?: string;
24
26
  name?: string;
25
27
  description?: string;
26
28
  /** Session length in minutes. */
@@ -84,13 +86,34 @@ export interface WixGetServiceResponse {
84
86
  /** Fetch a single service by id — GET /bookings/v2/services/{id}. Visitor-readable
85
87
  * (public GA route, not the owner `_api/` gateway). The detail/edit-prefill source. */
86
88
  export declare function getService(serviceId: string): Promise<WixGetServiceResponse>;
87
- /** Writable service fields for create/update. `name` + `duration` are required on create. */
89
+ /** Writable service fields for create/update, in the ERGONOMIC shape a form has. The helpers
90
+ * below encode it onto the Bookings v2 entity — they never send these flat fields as-is,
91
+ * because v2 silently ignores a flat `price`/`duration` (HTTP 200, revision bumps, nothing
92
+ * written: #3605, verified live 2026-10-01).
93
+ * - `price` → `payment.fixed.price.{value,currency}` (value is a decimal STRING), merged
94
+ * with the service's existing `payment` so `payment.options` (mandatory: online and/or
95
+ * in-person) and the currency carry over.
96
+ * - `duration` (minutes) → `schedule.availabilityConstraints.{durations,sessionDurations}`. */
88
97
  export interface WixServiceWrite {
89
98
  name?: string;
90
99
  description?: string;
91
100
  /** Session length in minutes; required, > 0 on create. */
92
101
  duration?: number;
102
+ /** Fixed price as a number (e.g. 120). Encoded onto `payment.fixed.price`. */
93
103
  price?: number;
104
+ /** ISO currency for `price`. Defaults to the service's current currency on update; required on create with a price. */
105
+ currency?: string;
106
+ /** Resource ids of the staff who deliver it (`queryStaff()` → `id`). An APPOINTMENT service needs ≥1;
107
+ * createService defaults to the site's `defaultStaff` resource when omitted. NOT staff-member ids. */
108
+ staffMemberIds?: string[];
109
+ /** v2 service type; createService defaults to "APPOINTMENT". */
110
+ type?: "APPOINTMENT" | "CLASS" | "COURSE";
111
+ onlineBooking?: {
112
+ enabled?: boolean;
113
+ };
114
+ /** Raw v2 payment object — wins over `price`/`currency` when both are given. */
115
+ payment?: Record<string, unknown>;
116
+ /** @deprecated v1 only — v2 services carry `category.id`; ignored by create/update. */
94
117
  category?: string;
95
118
  }
96
119
  export interface WixCreateServiceRequest {
@@ -107,7 +130,15 @@ export interface WixUpdateServiceResponse {
107
130
  }
108
131
  export interface WixDeleteServiceResponse {
109
132
  }
110
- /** Create a service — POST /bookings/v1/services. `name`, `duration` are required. */
133
+ /** Encode an ergonomic write onto the v2 service entity. `existing` (the current service, for
134
+ * an update) supplies the currency and `payment.options` a price change must carry. Exported
135
+ * for tests; throws instead of sending a payload Bookings would accept and then ignore. */
136
+ export declare function encodeServiceWrite(write: WixServiceWrite, existing?: WixService): Record<string, unknown>;
137
+ /** Create a service — POST /bookings/v2/services (the v1 route 400s for a v2 site: #3605).
138
+ * `name` + `duration` are required. Defaults: type APPOINTMENT, online booking on, and the
139
+ * site's `defaultStaff` resource when `staffMemberIds` is omitted (an APPOINTMENT service
140
+ * 400s MISSING_APPOINTMENT_RESOURCES without one), and — for a `price` without `currency` —
141
+ * the currency the site's existing services use. */
111
142
  export declare function createService(service: WixServiceWrite): Promise<WixCreateServiceResponse>;
112
143
  export declare function updateService(serviceId: string, service: WixServiceWrite & {
113
144
  revision?: string;
@@ -15,12 +15,17 @@ export interface WixDataPaging {
15
15
  limit?: number;
16
16
  offset?: number;
17
17
  }
18
+ /** One sort key — `{ fieldName: "postedDate", order: "DESC" }`. Keys apply in array order. */
19
+ export interface WixDataSort {
20
+ fieldName: string;
21
+ order?: "ASC" | "DESC";
22
+ }
18
23
  export interface WixQueryDataItemsRequest {
19
24
  dataCollectionId: string;
20
25
  query?: {
21
26
  filter?: Record<string, unknown>;
22
27
  paging?: WixDataPaging;
23
- sort?: unknown[];
28
+ sort?: WixDataSort[];
24
29
  };
25
30
  /** When true, `pagingMetadata.total` is filled (offset paging only). Default omitted. */
26
31
  returnTotalCount?: boolean;
@@ -72,9 +77,16 @@ export interface WixUpdateDataItemRequest {
72
77
  * const rest = await queryDataItems("Listings", 50, undefined, false, 50);
73
78
  * ```
74
79
  *
80
+ * `sort` orders server-side, so "newest first" is one call instead of fetching every row
81
+ * and sorting on the client (which also breaks once the collection outgrows `limit`):
82
+ *
83
+ * ```ts
84
+ * await queryDataItems("Jobs", 1, undefined, false, 0, [{ fieldName: "postedDate", order: "DESC" }]);
85
+ * ```
86
+ *
75
87
  * To read ONE row, use `getDataItem` rather than a page plus a client-side
76
88
  * `.find()` — that pattern breaks silently once the collection outgrows `limit`. */
77
- export declare function queryDataItems(dataCollectionId: string, limit?: number, filter?: Record<string, unknown>, returnTotalCount?: boolean, offset?: number): Promise<WixQueryDataItemsResponse>;
89
+ export declare function queryDataItems(dataCollectionId: string, limit?: number, filter?: Record<string, unknown>, returnTotalCount?: boolean, offset?: number, sort?: WixDataSort[]): Promise<WixQueryDataItemsResponse>;
78
90
  /** Read ONE item by id — GET /wix-data/v2/items/{dataItemId}?dataCollectionId=…
79
91
  *
80
92
  * Reach for this on a detail screen. Before it existed the only read this package
@@ -23,12 +23,77 @@ async function getService(serviceId) {
23
23
  const res = await wixCall("GET", `/bookings/v2/services/${serviceId}`);
24
24
  return { ...res, service: decodeService(res.service) };
25
25
  }
26
- function createService(service) {
27
- return wixCall("POST", "/bookings/v1/services", { service });
26
+ function encodeServiceWrite(write, existing) {
27
+ const { price, currency, duration, payment, category: _legacyCategory, ...rest } = write;
28
+ const out = { ...rest };
29
+ if (payment) {
30
+ out.payment = payment;
31
+ } else if (price != null) {
32
+ if (!Number.isFinite(price) || price < 0) throw new Error(`Bookings service price must be a number >= 0 (got ${price})`);
33
+ const prior = existing?.payment ?? {};
34
+ const cur = currency ?? prior.fixed?.price?.currency ?? prior.rateLabel?.currency ?? prior.currency;
35
+ if (!cur) throw new Error('Bookings service price needs a currency: pass `currency` (e.g. "USD") \u2014 the service has none to inherit');
36
+ const options = prior.options;
37
+ const { price: _flat, currency: _flatCur, ...priorRest } = prior;
38
+ out.payment = {
39
+ ...priorRest,
40
+ rateType: "FIXED",
41
+ fixed: { price: { value: String(price), currency: cur } },
42
+ // v2 400s unless online or inPerson is true; keep the service's own choice when it has one.
43
+ options: options && (options.online || options.inPerson) ? options : { ...options ?? {}, online: true }
44
+ };
45
+ }
46
+ if (duration != null) {
47
+ if (!Number.isFinite(duration) || duration <= 0) throw new Error(`Bookings service duration must be > 0 minutes (got ${duration})`);
48
+ out.schedule = { availabilityConstraints: { durations: [{ minutes: duration }], sessionDurations: [duration] } };
49
+ }
50
+ return out;
51
+ }
52
+ async function siteServiceCurrency() {
53
+ const PAGE = 100;
54
+ for (let offset = 0; offset < 500; offset += PAGE) {
55
+ const { services, pagingMetadata } = await queryServices({ query: { paging: { limit: PAGE, offset } } });
56
+ for (const x of services) {
57
+ const p = x.payment;
58
+ const cur = p?.fixed?.price?.currency ?? p?.varied?.defaultPrice?.currency ?? p?.varied?.minPrice?.currency ?? p?.varied?.maxPrice?.currency ?? p?.subscription?.amountPerBillingCycle?.currency ?? p?.subscription?.enrollmentFeeAmount?.currency ?? p?.rateLabel?.currency ?? p?.currency;
59
+ if (cur) return cur;
60
+ }
61
+ const meta = pagingMetadata;
62
+ if (services.length < PAGE || meta?.hasNext === false) return void 0;
63
+ }
64
+ return void 0;
28
65
  }
29
- function updateService(serviceId, service) {
66
+ async function createService(service) {
67
+ if (!service?.name) throw new Error("createService: name is required");
68
+ if (service.duration == null) throw new Error("createService: duration (minutes) is required");
69
+ const type = service.type ?? "APPOINTMENT";
70
+ let staffMemberIds = service.staffMemberIds;
71
+ if (type === "APPOINTMENT" && !staffMemberIds?.length) {
72
+ const staff = await queryStaff();
73
+ const pick = staff.find((r) => (r.tags ?? []).includes("defaultStaff")) ?? staff[0];
74
+ if (!pick?.id) throw new Error("createService: an APPOINTMENT service needs a staff member, and this site has none \u2014 add staff in Bookings, or pass staffMemberIds (resource ids from queryStaff())");
75
+ staffMemberIds = [pick.id];
76
+ }
77
+ let currency = service.currency;
78
+ if (service.price != null && !service.payment && !currency) currency = await siteServiceCurrency();
79
+ const body = encodeServiceWrite({ ...service, type, ...currency ? { currency } : {}, ...staffMemberIds ? { staffMemberIds } : {} });
80
+ if (!body.payment) body.payment = { rateType: "NO_FEE", options: { inPerson: true } };
81
+ const res = await wixCall("POST", "/bookings/v2/services", {
82
+ service: { onlineBooking: { enabled: true }, ...body }
83
+ });
84
+ return { ...res, service: decodeService(res.service) };
85
+ }
86
+ async function updateService(serviceId, service) {
30
87
  if (!serviceId) throw new Error("updateService: serviceId is required");
31
- return wixCall("PATCH", `/bookings/v2/services/${serviceId}`, { service: { id: serviceId, ...service } });
88
+ const { revision, ...write } = service;
89
+ const needsCurrent = !revision || write.price != null && !write.payment;
90
+ const current = needsCurrent ? (await getService(serviceId)).service : void 0;
91
+ const rev = revision ?? current?.revision;
92
+ if (!rev) throw new Error(`updateService: could not read the current revision of service ${serviceId}`);
93
+ const res = await wixCall("PATCH", `/bookings/v2/services/${serviceId}`, {
94
+ service: { id: serviceId, revision: rev, ...encodeServiceWrite(write, current) }
95
+ });
96
+ return { ...res, service: decodeService(res.service) };
32
97
  }
33
98
  function confirmBooking(bookingId, revision) {
34
99
  if (!bookingId || !revision) throw new Error("confirmBooking: bookingId + revision required");
@@ -198,7 +263,7 @@ function deleteService(serviceId, opts) {
198
263
  if (!serviceId) throw new Error("deleteService: serviceId is required");
199
264
  return wixCall(
200
265
  "DELETE",
201
- `/bookings/v1/services/${encodeURIComponent(serviceId)}`,
266
+ `/bookings/v2/services/${encodeURIComponent(serviceId)}`,
202
267
  void 0,
203
268
  opts
204
269
  );
@@ -213,6 +278,7 @@ export {
213
278
  declineBooking,
214
279
  decodeService,
215
280
  deleteService,
281
+ encodeServiceWrite,
216
282
  getBooking,
217
283
  getBookingFull,
218
284
  getService,
@@ -7,7 +7,7 @@ function wixCall(method, path, body) {
7
7
  }
8
8
  return globalThis.himalaya.wixApi.call(method, path, body);
9
9
  }
10
- function queryDataItems(dataCollectionId, limit = 50, filter, returnTotalCount = false, offset = 0) {
10
+ function queryDataItems(dataCollectionId, limit = 50, filter, returnTotalCount = false, offset = 0, sort) {
11
11
  if (!dataCollectionId) throw new Error("queryDataItems: dataCollectionId is required");
12
12
  const body = {
13
13
  dataCollectionId,
@@ -15,7 +15,8 @@ function queryDataItems(dataCollectionId, limit = 50, filter, returnTotalCount =
15
15
  // `offset` is omitted when 0 so a caller that does not page sends the byte-identical
16
16
  // body it sent before this parameter existed.
17
17
  paging: { limit, ...offset ? { offset } : {} },
18
- ...filter ? { filter } : {}
18
+ ...filter ? { filter } : {},
19
+ ...sort?.length ? { sort: sort.map((k) => ({ fieldName: k.fieldName, order: k.order ?? "ASC" })) } : {}
19
20
  },
20
21
  ...returnTotalCount ? { returnTotalCount: true } : {}
21
22
  };
package/dist/cli.mjs CHANGED
@@ -6765,7 +6765,6 @@ function walk(node, registry, path2, out2) {
6765
6765
  var SYSTEM_FONT_FAMILY, TONAL_ELEVATION_ALPHA;
6766
6766
  var init_tokens = __esm({
6767
6767
  "../../core/ts/src/tokens.ts"() {
6768
- "use strict";
6769
6768
  SYSTEM_FONT_FAMILY = "$system";
6770
6769
  TONAL_ELEVATION_ALPHA = [0, 0.05, 0.08, 0.11, 0.12, 0.14];
6771
6770
  }
@@ -6792,7 +6791,6 @@ function resolveTranslation(value, catalog, sourceCatalog) {
6792
6791
  var BINDING_SIGILS, BINDING_SIGIL_RE, TRANSLATION_PREFIX, TRANSLATION_KEY_RE;
6793
6792
  var init_bindings = __esm({
6794
6793
  "../../core/ts/src/bindings.ts"() {
6795
- "use strict";
6796
6794
  BINDING_SIGILS = ["service", "flag", "item", "t"];
6797
6795
  BINDING_SIGIL_RE = new RegExp(
6798
6796
  `^\\$(?:${BINDING_SIGILS.filter((s) => s !== "t").join("|")})\\b|^\\$t:`
@@ -21566,6 +21564,7 @@ __export(template_adopt_exports, {
21566
21564
  ADOPT_MATRIX_FILE: () => ADOPT_MATRIX_FILE,
21567
21565
  JOURNEY_STUB_FILE: () => JOURNEY_STUB_FILE,
21568
21566
  PLACEHOLDER: () => PLACEHOLDER,
21567
+ addScreen: () => addScreen,
21569
21568
  classifyAction: () => classifyAction,
21570
21569
  copyHits: () => copyHits,
21571
21570
  droppedPrimaryTaps: () => droppedPrimaryTaps,
@@ -21832,7 +21831,7 @@ function inventorySummary(inv) {
21832
21831
  function writeNoFollow(dir, rel, body) {
21833
21832
  const p = join34(dir, rel);
21834
21833
  const parent = dirname20(p);
21835
- if (existsSync32(parent) && lstatSync8(parent).isSymbolicLink()) throw new TrimRefusal(`${dirname20(rel)} is a symlink \u2014 refusing to write the adoption inventory through it`);
21834
+ if (existsSync32(parent) && lstatSync8(parent).isSymbolicLink()) throw new TrimRefusal(`${dirname20(rel)} is a symlink \u2014 refusing to write through it`);
21836
21835
  mkdirSync17(parent, { recursive: true });
21837
21836
  if (existsSync32(p) && lstatSync8(p).isSymbolicLink()) throw new TrimRefusal(`${rel} is a symlink \u2014 refusing to write through it`);
21838
21837
  const tmp = `${p}.${process.pid}.${Date.now()}.tmp`;
@@ -21890,6 +21889,50 @@ function replaceScreen(dir, id, entry) {
21890
21889
  writeFileSync15(devPath, next);
21891
21890
  return { ok: true, screen: id, bytes: body.length };
21892
21891
  }
21892
+ function addScreen(dir, id, entry, opts = {}) {
21893
+ if (!/^[a-z][a-z0-9_]*$/.test(id)) throw new TrimRefusal(`screen id "${id}" must be lowercase snake_case (e.g. himi_sellers)`);
21894
+ const src = readAppSources(dir);
21895
+ const app = parseApp(src.dev, src.config);
21896
+ if (findRoute(app, id)) throw new TrimRefusal(`"${id}" already exists \u2014 edit it with \`himi template screens --write ${id} --from <file>\``);
21897
+ const lastScreen = [...app.routes].filter((r) => r.kind === "screen").sort((a, b) => b.end - a.end)[0];
21898
+ if (!lastScreen) throw new TrimRefusal("dev/index.ts has no SCREENS entry to add after");
21899
+ const body = entry.endsWith("\n") ? entry : `${entry}
21900
+ `;
21901
+ const dev = src.dev.slice(0, lastScreen.end) + body + src.dev.slice(lastScreen.end);
21902
+ const nav = JSON.parse(JSON.stringify(app.nav.value));
21903
+ if (nav.linkableScreens === void 0) nav.linkableScreens = [];
21904
+ if (!Array.isArray(nav.linkableScreens)) throw new TrimRefusal("config.ts navigation.linkableScreens is not an array \u2014 fix it before adding a screen");
21905
+ const linkable = true;
21906
+ if (!nav.linkableScreens.some((l) => l.screenId === id)) {
21907
+ const parent = opts.parent ? nav.linkableScreens.find((l) => l.screenId === opts.parent) : void 0;
21908
+ if (opts.parent && !parent) throw new TrimRefusal(`--parent "${opts.parent}" is not a linkable screen in config.ts`);
21909
+ nav.linkableScreens.push({
21910
+ screenId: id,
21911
+ isPublic: true,
21912
+ paramKeys: [],
21913
+ parentScreenId: opts.parent ?? "",
21914
+ tabId: opts.tab ?? (typeof parent?.tabId === "string" ? parent.tabId : "")
21915
+ });
21916
+ }
21917
+ const config = linkable ? src.config.slice(0, app.nav.open) + serializeAt(nav, src.config, app.nav.open) + src.config.slice(app.nav.close) : src.config;
21918
+ let reparsed;
21919
+ try {
21920
+ reparsed = parseApp(dev, config);
21921
+ } catch (e) {
21922
+ throw new TrimRefusal(`the new entry does not parse, nothing written: ${e.message}`);
21923
+ }
21924
+ if (!findRoute(reparsed, id)) throw new TrimRefusal(`the entry does not declare "${id}" (its key and "screenId" must both be "${id}") \u2014 nothing written`);
21925
+ if (reparsed.routes.length !== app.routes.length + 1) {
21926
+ throw new TrimRefusal(`the entry changed the screen count by ${reparsed.routes.length - app.routes.length}, not 1; add one screen at a time \u2014 nothing written`);
21927
+ }
21928
+ for (const rel of ["dev", "dev/index.ts", ...config !== src.config ? ["config.ts"] : []]) {
21929
+ const p = join34(dir, rel);
21930
+ if (existsSync32(p) && lstatSync8(p).isSymbolicLink()) throw new TrimRefusal(`${rel} is a symlink \u2014 refusing to write through it (nothing written)`);
21931
+ }
21932
+ writeNoFollow(dir, "dev/index.ts", dev);
21933
+ if (config !== src.config) writeNoFollow(dir, "config.ts", config);
21934
+ return { ok: true, screen: id, bytes: body.length, linkable };
21935
+ }
21893
21936
  function specScreens(spec) {
21894
21937
  const body = spec.replace(/<!--[\s\S]*?(?:-->|$)/g, "");
21895
21938
  const m = /(?:^|\n)##[ \t]+Screen catalogue\b[^\n]*([\s\S]*?)(?=\n##[ \t]|$)/i.exec(body);
@@ -21930,6 +21973,7 @@ var ADOPT_FILE, JOURNEY_STUB_FILE, ADOPT_MATRIX_FILE, WIX_MODULE, RELATIVE_IMPOR
21930
21973
  var init_template_adopt = __esm({
21931
21974
  "src/template-adopt.ts"() {
21932
21975
  init_template_trim();
21976
+ init_home_descriptor();
21933
21977
  ADOPT_FILE = ".himi/adopt.json";
21934
21978
  JOURNEY_STUB_FILE = ".himi/himi-test.stub.json";
21935
21979
  ADOPT_MATRIX_FILE = ".himi/adopt-matrix.md";
@@ -38727,7 +38771,6 @@ var init_wix_cache_defaults_generated = __esm({
38727
38771
  { method: "DELETE", pathPrefix: "/blog/v3/posts", cacheable: false, ttl: 0, swr: 0, tag: "blog" },
38728
38772
  { method: "PATCH", pathPrefix: "/blog/v3/posts", cacheable: false, ttl: 0, swr: 0, tag: "blog" },
38729
38773
  { method: "POST", pathPrefix: "/blog/v3/posts", cacheable: false, ttl: 0, swr: 0, tag: "blog" },
38730
- { method: "DELETE", pathPrefix: "/bookings/v1/services", cacheable: false, ttl: 0, swr: 0, tag: "bookings/services" },
38731
38774
  { method: "POST", pathPrefix: "/bookings/v1/services", cacheable: false, ttl: 0, swr: 0, tag: "bookings/services" },
38732
38775
  { method: "PUT", pathPrefix: "/bookings/v1/services", cacheable: false, ttl: 0, swr: 0, tag: "bookings/services" },
38733
38776
  { method: "DELETE", pathPrefix: "/bookings/v2/services", cacheable: false, ttl: 0, swr: 0, tag: "bookings/services" },
@@ -46603,7 +46646,37 @@ function countingWixCall(base) {
46603
46646
  }
46604
46647
  async function liveRenderServices(bundleJs, opts) {
46605
46648
  const bus = hostDispatch(opts.localDbSchema, declaredCapabilitiesOf(opts.descriptor));
46606
- return loadBundleServicesLive(bundleJs, {
46649
+ const appear = rootOnAppear(opts.descriptor);
46650
+ const fired = appear ? parseWorkerAction(appear.actionId) : null;
46651
+ if (fired) {
46652
+ const w = await bootBundleWorker(bundleJs, { ...liveBootOptions(opts, bus), skipLoad: true });
46653
+ try {
46654
+ const handle = w.handles[fired.service];
46655
+ if (handle && handle.actionNames().includes(fired.action)) {
46656
+ try {
46657
+ await handle.dispatch(fired.action, ...workerArgs(appear));
46658
+ } catch (e) {
46659
+ w.errors.push({ service: fired.service, message: `onAppear ${fired.action}: ${e?.message ?? String(e)}` });
46660
+ }
46661
+ } else {
46662
+ for (const [service, h] of Object.entries(w.handles)) {
46663
+ if (!h.actionNames().includes("load")) continue;
46664
+ try {
46665
+ await h.dispatch("load");
46666
+ } catch (e) {
46667
+ w.errors.push({ service, message: e?.message ?? String(e) });
46668
+ }
46669
+ }
46670
+ }
46671
+ return { services: snapshot(w).state, calls: w.calls, errors: w.errors };
46672
+ } finally {
46673
+ w.dispose();
46674
+ }
46675
+ }
46676
+ return loadBundleServicesLive(bundleJs, liveBootOptions(opts, bus));
46677
+ }
46678
+ function liveBootOptions(opts, bus) {
46679
+ return {
46607
46680
  wixCall: opts.wixCall,
46608
46681
  input: opts.input,
46609
46682
  budgetMs: opts.budgetMs,
@@ -46617,7 +46690,7 @@ async function liveRenderServices(bundleJs, opts) {
46617
46690
  // holding this ONE verb on `null` keeps the previous behaviour while the capability bus
46618
46691
  // joins. Caught in pre-PR review; revisit when --live can mint a member token.
46619
46692
  dispatch: (action, params) => MEMBER_IDENTITY_ACTION.test(action) ? null : bus(action, params)
46620
- });
46693
+ };
46621
46694
  }
46622
46695
  async function buildReleaseFromContent(contentDir2, dest, opts = {}) {
46623
46696
  const { freshModule, ...rest } = opts;
@@ -75881,7 +75954,7 @@ function auditUx(content, strict = false) {
75881
75954
  if (usesAppNetwork(source)) {
75882
75955
  const mocksPath = join25(dir, "dev", "net-mocks.json");
75883
75956
  if (!existsSync22(mocksPath)) {
75884
- issue(issues, strict, "missing-app-mocks", "This package uses a Wix/network client but has no app-specific dev/net-mocks.json. Add fixtures for dynamic, entitlement, or recovery flows.", "dev/net-mocks.json");
75957
+ issue(issues, strict, "missing-app-mocks", "This package uses a Wix/network client but has no app-specific dev/net-mocks.json. `himi test --record` writes one from the live responses it sees; then add fixtures for dynamic, entitlement, or recovery flows.", "dev/net-mocks.json");
75885
75958
  } else {
75886
75959
  try {
75887
75960
  const parsed2 = JSON.parse(readFileSync22(mocksPath, "utf8"));
@@ -76115,10 +76188,11 @@ var USAGE = `himi \u2014 the Himalaya authoring CLI (remote dev loop)
76115
76188
  before data consent), placeholder + sample copy, the proposed Data access rows
76116
76189
  and typed-journey stubs. Writes .himi/adopt.json, .himi/adopt-matrix.md and
76117
76190
  .himi/himi-test.stub.json (--dry-run writes nothing)
76118
- himi template screens [--show <id>] [--write <id> --from <file>] [--content <dir>]
76191
+ himi template screens [--show <id>] [--write <id> --from <file>] [--add <id> --from <file> [--parent <id>] [--tab <id>]] [--content <dir>]
76119
76192
  list dev/index.ts screens (size, workers, onAppear); --show prints ONE entry so
76120
76193
  nobody reads the whole file; --write puts an edited entry back (re-parsed, refused
76121
76194
  if it breaks the file or changes the screen count)
76195
+ --add appends a NEW screen entry and registers it in linkableScreens (--parent/--tab)
76122
76196
  himi template sweep --set "Old=>New" [--set \u2026] | --map <file.json> [--content <dir>] [--dry-run]
76123
76197
  replace the template's sample identity/copy inside string literals only (never
76124
76198
  identifiers) across dev/index.ts, config.ts, tokens.json and tier5-src; re-parses
@@ -76799,7 +76873,7 @@ WHAT A PATCH DOES NOT CARRY \u2014 all reported in the output, never silent
76799
76873
  var COMMANDS2 = [
76800
76874
  { name: "captcha", summary: "create and store native CAPTCHA keys in Serve, or inspect setup status; customer setup verifies Wix site ownership; does not enable member CAPTCHA SDKs", required: "setup|status --app <app> [--content <dir>] [--site <msid>] [--ios-bundle-id <id>] [--android-package-name <id>] [--target <t>]" },
76801
76875
  { name: "init", summary: "scaffold a starter content package (OTA-only; no native shells). --template <t> starts from a full reference app (branded-lite = consumer storefront over a Wix site; owner-lite = owner CRUD/management) instead of the minimal counter; --templates lists bundled ones; --remote fetches the template off the serve plane (falls back to the bundled copy offline). The new app also gets its agent files (the same set `himi agent-init` writes: AGENTS.md, CLAUDE.md, the himalaya skill, HIMALAYA.md, a SessionStart hook) so the next session started inside it picks the work up; --no-agent skips them. It also makes the build board (.himi/studio/, exactly as `himi studio --no-open` does \u2014 it never opens a browser), so every later render, test, validate, push and progress entry refreshes it; and, for a template bundled with this CLI, it makes the FIRST PICTURE itself (never for a --remote one: that is its publisher's code, and a render runs it on this machine, so next[] leaves the render to the agent after it has read the code): exactly `himi render <homeScreenId> --png --mocks --device iphone` (offline \u2014 no request; never --live; --mocks loads the app's recorded data when it has any), run as a child it kills after a time budget, writing .himi/shots/<home>.png and journaling it (by: himi) as the STARTING POINT (\"Starting point: the <template> template, before any of your changes\") so the board and the journal hold a picture when init returns. init also records the source it wrote (.himi/init-source.json: a content fingerprint, Markdown left out), and the first home PNG render after the source diverges from it is journaled once as \"First look at your app\" \u2014 reported as `firstLook: {image}` or `{skipped: <reason>}`, never a failed init; `--no-first-look` skips it (for scripts and CI that render themselves). For a bundled template (never a --remote one \u2014 the preview would build and serve its publisher's code) init also STARTS the live preview itself: `himi run --watch --content <dir> --port <p> --no-open --idle-exit 30`, detached in its own process group with its output in .himi/watch.log, never waited for (the url is chosen before it builds), recorded in .himi/watch.json, and reported as `watch: {started, pid, url, board, log}` \u2014 or `{skipped: <reason>}` for a `CI` environment, a test runner, a watch already running, or a start that failed (never a failed init). Nothing else skips it: outside CI it is started, and `watch.note` says an opt-out was ignored. The envelope LEADS with `doNow`: three lines to run now (the consent preflight, the live preview \u2014 its url and board when init started it, else `himi run --watch --content <dir> &` with the board's path \u2014 and the postcard line), and ENDS with the same lines as `doNowAgain`, so an agent that reads only the head or only the tail (`| tail`) gets them; at a terminal they are also printed as plain text on stderr, before the JSON and again after it. Its `next[]` LEADS with the steps for THIS session, in this order for every app \u2014 the offline first render and that picture's `himi progress` postcard (only when init could not make the picture), the skill's consent preflight (authorize or mock any Wix or external startup request; with none, go straight on), and only then the live preview (the running one's url, or `himi run --watch &` when init did not start it) \u2014 ahead of the next-session guidance", required: "<name> [--dir] [--template <t>] [--remote] [--no-agent] [--no-first-look] | --templates" },
76802
- { name: "template", summary: "starter-template catalog + publishing. Every template is a SHOWCASE APP \u2014 a real published release you can open running before you start from it. `list` prints available templates with metadata (title/description/tags) plus each one's start prompt \u2014 pass --remote to include the serve-plane catalog so an agent can pick one; `prompt <name>` prints just that sentence for one template (the skill URL is the public door, or the plane named by --target/--serve-url); `publish` publishes an app AS a `himi init --template` (needs a himalaya.template.json; builds a previewable release + attaches the scaffold source; --source-dir overrides which tree becomes the source, --name sets the template name; bakes a launcher icon \u2014 --icon <png>, or the committed template-icons/<name>.png \u2014 and extracts accent/palette gallery meta from the scaffold's tokens.json; --visibility unlisted keeps it out of the /v1/templates catalog \u2014 templates default to public); `trim --keep <ids>` cuts an app scaffolded from a template down to those screens (the home, and any screen the navigation pins such as a drawer body, always stays) \u2014 it deletes the other SCREENS/TEMPLATED entries whole, prunes navigation (linkableScreens, tabBar, drawer, deep links, surfaces) and drops tier5-src workers no kept screen uses, then runs validate --strict and rolls everything back if it fails (--force keeps it, --dry-run writes nothing). A kept screen that links to a removed one is refused unless --links follow (keep those too) or --links drop (remove the taps). Adopting one: `inspect` writes the adoption inventory (.himi/adopt.json: screens, workers, which screens can call Wix/network on appear \u2014 bootsOffline \u2014 placeholder and sample copy, proposed Data access rows, typed-journey stubs); `screens` lists, shows or rewrites ONE dev/index.ts entry; `sweep` replaces sample identity/copy inside string literals; `recommend` ranks the catalog for a business description or a site's verticals; `trim --from-spec` takes the kept screens from SPEC.md's Screen catalogue", required: "list [--remote] | prompt <name> | publish [--dir <app>] [--source-dir <dir>] [--name <n>] [--icon <png>] [--visibility public|unlisted] [--target <t>] | trim --keep <id,id,\u2026>|--from-spec [SPEC.md] [--content <dir>] [--links follow|drop] [--dry-run] [--force] | inspect [--content <dir>] [--dry-run] | screens [--show <id>] [--write <id> --from <file>] | sweep --set 'Old=>New' | --map <file> [--dry-run] | recommend [--text <t>] [--verticals a,b] [--book <dir>] [--remote]" },
76876
+ { name: "template", summary: "starter-template catalog + publishing. Every template is a SHOWCASE APP \u2014 a real published release you can open running before you start from it. `list` prints available templates with metadata (title/description/tags) plus each one's start prompt \u2014 pass --remote to include the serve-plane catalog so an agent can pick one; `prompt <name>` prints just that sentence for one template (the skill URL is the public door, or the plane named by --target/--serve-url); `publish` publishes an app AS a `himi init --template` (needs a himalaya.template.json; builds a previewable release + attaches the scaffold source; --source-dir overrides which tree becomes the source, --name sets the template name; bakes a launcher icon \u2014 --icon <png>, or the committed template-icons/<name>.png \u2014 and extracts accent/palette gallery meta from the scaffold's tokens.json; --visibility unlisted keeps it out of the /v1/templates catalog \u2014 templates default to public); `trim --keep <ids>` cuts an app scaffolded from a template down to those screens (the home, and any screen the navigation pins such as a drawer body, always stays) \u2014 it deletes the other SCREENS/TEMPLATED entries whole, prunes navigation (linkableScreens, tabBar, drawer, deep links, surfaces) and drops tier5-src workers no kept screen uses, then runs validate --strict and rolls everything back if it fails (--force keeps it, --dry-run writes nothing). A kept screen that links to a removed one is refused unless --links follow (keep those too) or --links drop (remove the taps). Adopting one: `inspect` writes the adoption inventory (.himi/adopt.json: screens, workers, which screens can call Wix/network on appear \u2014 bootsOffline \u2014 placeholder and sample copy, proposed Data access rows, typed-journey stubs); `screens` lists, shows, rewrites or adds ONE dev/index.ts entry; `sweep` replaces sample identity/copy inside string literals; `recommend` ranks the catalog for a business description or a site's verticals; `trim --from-spec` takes the kept screens from SPEC.md's Screen catalogue", required: "list [--remote] | prompt <name> | publish [--dir <app>] [--source-dir <dir>] [--name <n>] [--icon <png>] [--visibility public|unlisted] [--target <t>] | trim --keep <id,id,\u2026>|--from-spec [SPEC.md] [--content <dir>] [--links follow|drop] [--dry-run] [--force] | inspect [--content <dir>] [--dry-run] | screens [--show <id>] [--write <id> --from <file>] [--add <id> --from <file>] | sweep --set 'Old=>New' | --map <file> [--dry-run] | recommend [--text <t>] [--verticals a,b] [--book <dir>] [--remote]" },
76803
76877
  { name: "build", summary: "compile tier5-src/<name>/index.ts -> dist/bundles/<name>.bundle.js with esbuild (--binary = resolve published @himalaya/*); semantic TypeScript checking belongs to himi validate", required: "[--content <dir>] [--binary]" },
76804
76878
  { name: "functions", summary: "inspect/invoke server functions and operate durable jobs or schedules", required: "list|describe|openapi|invoke | jobs list|get|cancel|retry|dlq | schedule describe|invoke [--app <a>] [--environment preview|production] [--release <id>]" },
76805
76879
  { name: "eject", summary: "take ownership of a shared flow module (auth|checkout|screens): copies its source into tier5-src/_ejected/ and repoints this package's imports at the copy. The ladder's escape hatch \u2014 for when the factory options cannot express what you need. AFTER EJECTING, CENTRAL FIXES NO LONGER REACH THIS APP, including future corrections to the fail-closed entitlement and confirm-the-session rules. The floor is unaffected: credentials still go only through the native auth actions and money only through the cashier rail.", required: "<auth|checkout|screens> [--content <dir>]" },
@@ -77942,7 +78016,13 @@ async function adoptAtInit(dir) {
77942
78016
  writeInventory2(dir, inv);
77943
78017
  return { adopt: inventorySummary2(inv) };
77944
78018
  } catch (e) {
77945
- return { adopt: { skipped: e.message } };
78019
+ const msg = e.message;
78020
+ const layout = /SCREENS = runtimeSetting/.test(msg);
78021
+ return {
78022
+ adopt: {
78023
+ skipped: layout ? "no adoption inventory for this template: its dev/index.ts is not in the generated SCREENS layout, so `himi template inspect | screens | trim` do not apply to it \u2014 edit its source directly" : `no adoption inventory: ${msg}`
78024
+ }
78025
+ };
77946
78026
  }
77947
78027
  }
77948
78028
  function keptPlan(kept) {
@@ -79033,6 +79113,13 @@ ${doNow.map((l) => ` ${l}`).join("\n")}`);
79033
79113
  const app = parseApp2(src.dev, src.config);
79034
79114
  const show = str9(flags.show);
79035
79115
  const target = str9(flags.write);
79116
+ const adding = str9(flags.add);
79117
+ if (adding) {
79118
+ const from = str9(flags.from);
79119
+ if (!from) throw new TrimRefusal2('--add <id> needs --from <file> holding the whole new SCREENS entry (` "<id>": { "screenId": "<id>", \u2026 },`)');
79120
+ print(adopt.addScreen(dir, adding, readFileSync88(from, "utf8"), { parent: str9(flags.parent), tab: str9(flags.tab) }));
79121
+ return 0;
79122
+ }
79036
79123
  if (target) {
79037
79124
  const from = str9(flags.from);
79038
79125
  if (!from) throw new TrimRefusal2("--write <id> needs --from <file> holding the whole entry, as --show prints it");
@@ -83466,6 +83553,10 @@ ${label2}`);
83466
83553
  const deadline = Date.now() + timeoutMs;
83467
83554
  const pending = new Map(jobs.map((j) => [String(j.jobId), String(j.platform)]));
83468
83555
  const done = [];
83556
+ const statusCmd = (id) => `himi dist status ${id}${str9(flags.target) ? ` --target ${str9(flags.target)}` : ""}`;
83557
+ for (const j of jobs) (flags.json === true ? io.error : io.log)(` ${j.platform} dispatched as ${j.jobId}`);
83558
+ const MAX_POLL_ERRORS = 3;
83559
+ const pollErrors = /* @__PURE__ */ new Map();
83469
83560
  while (pending.size > 0) {
83470
83561
  if (Date.now() >= deadline) {
83471
83562
  await printDistQrsForJobs(done);
@@ -83481,9 +83572,32 @@ ${label2}`);
83481
83572
  }
83482
83573
  await new Promise((res) => setTimeout(res, intervalMs));
83483
83574
  for (const jobId of [...pending.keys()]) {
83484
- const s = await c.distStatus(jobId);
83575
+ let s;
83576
+ let why = "";
83577
+ try {
83578
+ s = await c.distStatus(jobId);
83579
+ if (!s.ok) why = `HTTP ${s.status}`;
83580
+ } catch (e) {
83581
+ why = e.message;
83582
+ }
83583
+ if (why) {
83584
+ const n = (pollErrors.get(jobId) ?? 0) + 1;
83585
+ pollErrors.set(jobId, n);
83586
+ if (n < MAX_POLL_ERRORS) continue;
83587
+ await printDistQrsForJobs(done);
83588
+ print({
83589
+ ok: false,
83590
+ error: "dist_poll_failed",
83591
+ message: `could not read the status of ${pending.get(jobId)} job ${jobId} ${MAX_POLL_ERRORS} times in a row (${why}) \u2014 the build${pending.size > 1 ? "s were" : " was"} dispatched and may still finish`,
83592
+ pending: [...pending].map(([id, platform2]) => ({ platform: platform2, jobId: id, check: statusCmd(id) })),
83593
+ finished: flags.json === true ? done : done.map(humanDistJob),
83594
+ ...failures.length ? { failures } : {}
83595
+ });
83596
+ return 1;
83597
+ }
83598
+ pollErrors.delete(jobId);
83485
83599
  const rec = s.json ?? {};
83486
- if (!s.ok || !TERMINAL_DIST_STATUSES.has(String(rec.status))) continue;
83600
+ if (!TERMINAL_DIST_STATUSES.has(String(rec.status))) continue;
83487
83601
  pending.delete(jobId);
83488
83602
  done.push(rec);
83489
83603
  if (!flags.json) {
@@ -25,7 +25,10 @@ must be impossible for it to touch something it didn't create.
25
25
  | **Adopted** | `wixId: "<existing id>"` | **read only** — never create, never alter schema, never seed | CMS data the site already has |
26
26
 
27
27
  An owned id must be exactly **`himi_<appSlug>_<name>`**, where `appSlug` is the
28
- content-package app id with `-` folded to `_` and lowercased (`my-app` → `himi_my_app_`),
28
+ content package's **`name` in `himalaya.content.json`** — the name you passed to `himi init`,
29
+ or the folder's name when there is no manifest; **not** the dotted `id` in `config.ts` — with
30
+ `-` folded to `_` and lowercased (`my-app` → `himi_my_app_`). `himi cms create --dry-run`
31
+ prints the resolved prefix before anything is written,
29
32
  and **`<name>` is alphanumeric — no underscores** (`menuItems`, not `menu_items`). This is
30
33
  checked by `himi validate` and re-checked by every mutating verb. It is deliberately **not**
31
34
  auto-prefixed: what you declare is what lands on the site, and an unprefixed id is
@@ -77,7 +77,9 @@ worker layer — trim it or mine it (`himi docs trim-a-template`): **trim** to t
77
77
  `tier5-src/_shared/` helpers and Wix worker chains and write your own screens. Do not delete
78
78
  `tier5-src/` + `dev/` and start from blank. Never read that file whole: `himi template screens`
79
79
  lists its screens (size, workers, `onAppear`), `--show <id>` prints one entry, and
80
- `--write <id> --from <file>` puts an edited entry back (refused if it breaks the file). Not sure
80
+ `--write <id> --from <file>` puts an edited entry back (refused if it breaks the file), and
81
+ `--add <id> --from <file>` appends a NEW screen and registers it as linkable — the authoring half
82
+ of mining a template. Not sure
81
83
  which template fits? `himi template recommend --text "<the business>"` (or `--book <site analyze
82
84
  dir>` for a site's verticals) ranks the catalog with reasons. For a small **non-Wix** pattern, `himi init --template chat-bot --remote`. `himi template
83
85
  list --remote` shows the full catalog (title/description/tags) — including templates published
@@ -106,7 +106,9 @@ worker layer — trim it or mine it (`himi docs trim-a-template`): **trim** to t
106
106
  `tier5-src/_shared/` helpers and Wix worker chains and write your own screens. Do not delete
107
107
  `tier5-src/` + `dev/` and start from blank. Never read that file whole: `himi template screens`
108
108
  lists its screens (size, workers, `onAppear`), `--show <id>` prints one entry, and
109
- `--write <id> --from <file>` puts an edited entry back (refused if it breaks the file). Not sure
109
+ `--write <id> --from <file>` puts an edited entry back (refused if it breaks the file), and
110
+ `--add <id> --from <file>` appends a NEW screen and registers it as linkable — the authoring half
111
+ of mining a template. Not sure
110
112
  which template fits? `himi template recommend --text "<the business>"` (or `--book <site analyze
111
113
  dir>` for a site's verticals) ranks the catalog with reasons. For a small **non-Wix** pattern, `himi init --template chat-bot --remote`. `himi template
112
114
  list --remote` shows the full catalog (title/description/tags) — including templates published
@@ -112,6 +112,32 @@ grep -oE 'package="[^"]*"|android:label="[^"]*"' \
112
112
  himi-owner-lite-app/build/intermediates/merged_manifest/debug/*/AndroidManifest.xml
113
113
  ```
114
114
 
115
+ ## Off-repo: an app made with `himi init --template owner-lite`
116
+
117
+ Everything above describes Owner Lite as Wix builds it, with its per-customer binary. An
118
+ off-repo content package made with `himi init --template owner-lite` (or one of the `*-owner`
119
+ templates) has no `provisioning.json` and no `prepare:customer` step. What it does have is the
120
+ same split between the **owner** and **visitor** scopes, and that split decides what you can see:
121
+
122
+ - **The web preview is visitor-scope.** `himi run`, `himi render --live` and a pushed draft
123
+ preview all run with an anonymous visitor session. A visitor can read public catalog data
124
+ (Bookings services, Stores products, published posts), so those screens render real data
125
+ there. Anything owner-scoped cannot: a business's appointments and orders, its drafts, and
126
+ every management write (create or edit a service, product or post) answer 401/403. The
127
+ owner-only screens therefore show their empty or error state on the web, which is not a bug.
128
+ - **Owner flows are proven on a native build.** The owner signs in through the native owner
129
+ login (`himi dist` → install the APK/IPA). That build is the surface for "today's
130
+ appointments" and for the write round trips; the web preview cannot stand in for it.
131
+ - **Before that build exists**, prove a write with the site owner's own token against the same
132
+ REST endpoint the worker calls, then re-read it independently. That proves the payload, not
133
+ the app's sign-in. Record each native surface you could not drive as unavailable, with the
134
+ reason, rather than calling it passed:
135
+ `himi ux record --content . --platform ios --status unavailable --reason "owner flows need a native build; not driven yet"`
136
+ (and the same with `--platform android`).
137
+ - **An app that must render something to a visitor** (a public menu next to the owner tools)
138
+ binds the visitor-readable surfaces with a consumer provider and keeps the owner screens
139
+ behind the owner gate. The owner gate fails closed in a visitor session; that is its job.
140
+
115
141
  ## See also
116
142
 
117
143
  - [owner-lite-publish-binary.md](owner-lite-publish-binary.md) — pointing that binary at a serve app.
@@ -83,7 +83,48 @@ After setup, continue normal app development, validation, publish/preview/promot
83
83
  native distribution. Native push configuration, member authentication, device-token
84
84
  registration and a delivery/tap test are still required; for the test, see
85
85
  [Send a test notification](#send-a-test-notification). Setup does not enable native
86
- capabilities, choose a Ping family, send notifications or verify phone delivery.
86
+ capabilities, choose a Ping family, send notifications or verify phone delivery; the app's
87
+ `config.ts` does the first two (see [Native push configuration](#native-push-configuration)).
88
+
89
+ ## Native push configuration
90
+
91
+ The app's own `config.ts` turns push on; `himi notify setup` never edits it.
92
+
93
+ ```ts
94
+ auth: WixConsumerAuthProvider({
95
+ siteId: "<metaSiteId>", // the site himi notify setup configured
96
+ clientId: "<headless-client-id>",
97
+ siteUrl: "https://<your-site>",
98
+ redirectUri: "<scheme>://oauth/callback",
99
+ bootAsVisitor: true,
100
+ }),
101
+ capabilities: ["push" /* , … */],
102
+ push: {
103
+ registrar: "wix-ping",
104
+ pingAppName: "WixBrandedWebApp",
105
+ requestPermissionOnLaunch: true,
106
+ payloadAdapter: "wix-ping",
107
+ routes: [
108
+ { match: "my-account/:site/stores/orders/:orderId",
109
+ link: "<scheme>://screen/<order-screen>?id={orderId}" },
110
+ ],
111
+ },
112
+ ```
113
+
114
+ - `pingAppName` is the Ping app family the device registers under. Use `WixBrandedWebApp`:
115
+ Ping allows it for site members and uses the credentials `himi notify setup` stored for
116
+ the site. A build signed for the APNs development environment appends `.sandbox` itself;
117
+ do not add it.
118
+ - The device registers only after a member signs in; a visitor's device receives nothing.
119
+ Keep `siteId` in `auth` on the site `himi notify setup` configured. A changed `siteId` or
120
+ `clientId` ships with `himi push` like the rest of the config, and the app picks it up on
121
+ its next launch; until then iOS stops registering rather than register the old site.
122
+ - `routes` turn the Wix links inside notifications into the app's screens, one route per
123
+ link shape. A notification whose link matches no route opens the app without navigating.
124
+ `himi validate` warns when a route names a screen the app does not declare.
125
+ - The `push` block ships with `himi push`; changing it needs no new binary. The binary still
126
+ has to carry the push entitlement: the `branded-lite` shell requests it, and the signing
127
+ profile must grant `aps-environment` (see the QA build notes below).
87
128
 
88
129
  **Customer push QA builds use `himi dist --signing byo`.** After publishing an approved
89
130
  release, use a backend and reachable runner source that support the customer-signing flow.
@@ -7337,7 +7337,7 @@
7337
7337
  "kind": "function",
7338
7338
  "jsdoc": "",
7339
7339
  "decl": "export function deleteService(\n serviceId: string,\n opts?: WixCallOptions,\n): Promise<WixDeleteServiceResponse>;",
7340
- "http": "DELETE /bookings/v1/services/{…}"
7340
+ "http": "DELETE /bookings/v2/services/{…}"
7341
7341
  },
7342
7342
  {
7343
7343
  "name": "WixServiceMoney",
@@ -7355,7 +7355,7 @@
7355
7355
  "name": "WixService",
7356
7356
  "kind": "interface",
7357
7357
  "jsdoc": "",
7358
- "decl": "export interface WixService {\n id: string;\n name?: string;\n description?: string;\n /** Session length in minutes. */\n duration?: number;\n /** Raw Bookings v2 response shape. */\n payment?: WixServicePayment;\n schedule?: { availabilityConstraints?: { sessionDurations?: number[] } };\n /** @deprecated Compatibility projection populated by `decodeService`. */\n price?: number;\n /** @deprecated Legacy v1 compatibility field. */\n category?: string;\n}"
7358
+ "decl": "export interface WixService {\n id: string;\n /** Optimistic-concurrency token — required by every v2 write (updateService reads it if omitted). */\n revision?: string;\n name?: string;\n description?: string;\n /** Session length in minutes. */\n duration?: number;\n /** Raw Bookings v2 response shape. */\n payment?: WixServicePayment;\n schedule?: { availabilityConstraints?: { sessionDurations?: number[] } };\n /** @deprecated Compatibility projection populated by `decodeService`. */\n price?: number;\n /** @deprecated Legacy v1 compatibility field. */\n category?: string;\n}"
7359
7359
  },
7360
7360
  {
7361
7361
  "name": "decodeService",
@@ -7441,8 +7441,8 @@
7441
7441
  {
7442
7442
  "name": "WixServiceWrite",
7443
7443
  "kind": "interface",
7444
- "jsdoc": "/** Writable service fields for create/update. `name` + `duration` are required on create. */",
7445
- "decl": "export interface WixServiceWrite {\n name?: string;\n description?: string;\n /** Session length in minutes; required, > 0 on create. */\n duration?: number;\n price?: number;\n category?: string;\n}"
7444
+ "jsdoc": "/** Writable service fields for create/update, in the ERGONOMIC shape a form has. The helpers\n * below encode it onto the Bookings v2 entity — they never send these flat fields as-is,\n * because v2 silently ignores a flat `price`/`duration` (HTTP 200, revision bumps, nothing\n * written: #3605, verified live 2026-10-01).\n * - `price` → `payment.fixed.price.{value,currency}` (value is a decimal STRING), merged\n * with the service's existing `payment` so `payment.options` (mandatory: online and/or\n * in-person) and the currency carry over.\n * - `duration` (minutes) → `schedule.availabilityConstraints.{durations,sessionDurations}`. */",
7445
+ "decl": "export interface WixServiceWrite {\n name?: string;\n description?: string;\n /** Session length in minutes; required, > 0 on create. */\n duration?: number;\n /** Fixed price as a number (e.g. 120). Encoded onto `payment.fixed.price`. */\n price?: number;\n /** ISO currency for `price`. Defaults to the service's current currency on update; required on create with a price. */\n currency?: string;\n /** Resource ids of the staff who deliver it (`queryStaff()` → `id`). An APPOINTMENT service needs ≥1;\n * createService defaults to the site's `defaultStaff` resource when omitted. NOT staff-member ids. */\n staffMemberIds?: string[];\n /** v2 service type; createService defaults to \"APPOINTMENT\". */\n type?: \"APPOINTMENT\" | \"CLASS\" | \"COURSE\";\n onlineBooking?: { enabled?: boolean };\n /** Raw v2 payment object — wins over `price`/`currency` when both are given. */\n payment?: Record<string, unknown>;\n /** @deprecated v1 only — v2 services carry `category.id`; ignored by create/update. */\n category?: string;\n}"
7446
7446
  },
7447
7447
  {
7448
7448
  "name": "WixCreateServiceRequest",
@@ -7474,18 +7474,24 @@
7474
7474
  "jsdoc": "",
7475
7475
  "decl": "export interface WixDeleteServiceResponse {\n // Empty object on success.\n}"
7476
7476
  },
7477
+ {
7478
+ "name": "encodeServiceWrite",
7479
+ "kind": "function",
7480
+ "jsdoc": "/** Encode an ergonomic write onto the v2 service entity. `existing` (the current service, for\n * an update) supplies the currency and `payment.options` a price change must carry. Exported\n * for tests; throws instead of sending a payload Bookings would accept and then ignore. */",
7481
+ "decl": "export function encodeServiceWrite(write: WixServiceWrite, existing?: WixService): Record<string, unknown>;"
7482
+ },
7477
7483
  {
7478
7484
  "name": "createService",
7479
7485
  "kind": "function",
7480
- "jsdoc": "/** Create a service — POST /bookings/v1/services. `name`, `duration` are required. */",
7481
- "decl": "export function createService(service: WixServiceWrite): Promise<WixCreateServiceResponse>;",
7482
- "http": "POST /bookings/v1/services"
7486
+ "jsdoc": "/** Create a service — POST /bookings/v2/services (the v1 route 400s for a v2 site: #3605).\n * `name` + `duration` are required. Defaults: type APPOINTMENT, online booking on, and the\n * site's `defaultStaff` resource when `staffMemberIds` is omitted (an APPOINTMENT service\n * 400s MISSING_APPOINTMENT_RESOURCES without one), and — for a `price` without `currency` —\n * the currency the site's existing services use. */",
7487
+ "decl": "export async function createService(service: WixServiceWrite): Promise<WixCreateServiceResponse>;",
7488
+ "http": "POST /bookings/v2/services"
7483
7489
  },
7484
7490
  {
7485
7491
  "name": "updateService",
7486
7492
  "kind": "function",
7487
7493
  "jsdoc": "",
7488
- "decl": "export function updateService(serviceId: string, service: WixServiceWrite & { revision?: string }): Promise<WixUpdateServiceResponse>;",
7494
+ "decl": "export async function updateService(serviceId: string, service: WixServiceWrite & { revision?: string }): Promise<WixUpdateServiceResponse>;",
7489
7495
  "http": "PATCH /bookings/v2/services/{…}"
7490
7496
  },
7491
7497
  {
@@ -11347,11 +11353,17 @@
11347
11353
  "jsdoc": "",
11348
11354
  "decl": "export interface WixDataPaging {\n limit?: number;\n offset?: number;\n}"
11349
11355
  },
11356
+ {
11357
+ "name": "WixDataSort",
11358
+ "kind": "interface",
11359
+ "jsdoc": "/** One sort key — `{ fieldName: \"postedDate\", order: \"DESC\" }`. Keys apply in array order. */",
11360
+ "decl": "export interface WixDataSort {\n fieldName: string;\n order?: \"ASC\" | \"DESC\";\n}"
11361
+ },
11350
11362
  {
11351
11363
  "name": "WixQueryDataItemsRequest",
11352
11364
  "kind": "interface",
11353
11365
  "jsdoc": "",
11354
- "decl": "export interface WixQueryDataItemsRequest {\n dataCollectionId: string;\n query?: {\n filter?: Record<string, unknown>;\n paging?: WixDataPaging;\n sort?: unknown[];\n };\n /** When true, `pagingMetadata.total` is filled (offset paging only). Default omitted. */\n returnTotalCount?: boolean;\n}"
11366
+ "decl": "export interface WixQueryDataItemsRequest {\n dataCollectionId: string;\n query?: {\n filter?: Record<string, unknown>;\n paging?: WixDataPaging;\n sort?: WixDataSort[];\n };\n /** When true, `pagingMetadata.total` is filled (offset paging only). Default omitted. */\n returnTotalCount?: boolean;\n}"
11355
11367
  },
11356
11368
  {
11357
11369
  "name": "WixQueryDataItemsResponse",
@@ -11374,8 +11386,8 @@
11374
11386
  {
11375
11387
  "name": "queryDataItems",
11376
11388
  "kind": "function",
11377
- "jsdoc": "/** Query items in a collection — POST /wix-data/v2/items/query.\n *\n * `filter` is the Wix Data v2 filter object, which the type cannot express (it is\n * `Record<string, unknown>` by construction). A field name maps to a value for\n * equality, or to an operator object:\n *\n * ```ts\n * await queryDataItems(\"Listings\", 20, { city: \"Tel Aviv\" });\n * await queryDataItems(\"Listings\", 20, { price: { $lte: 2_500_000 } });\n * await queryDataItems(\"Listings\", 20, { _id: { $in: [a, b] } });\n * ```\n *\n * `offset` pages: ask for `limit` rows starting at `offset`. Pair it with\n * `returnTotalCount: true`, which fills `pagingMetadata.total` so a caller knows\n * when to stop — it is omitted by default because counting is extra work, and it\n * reports total for OFFSET paging only, which is the mode this function uses.\n *\n * ```ts\n * const first = await queryDataItems(\"Listings\", 50, undefined, true);\n * const total = first.pagingMetadata?.total ?? 0;\n * const rest = await queryDataItems(\"Listings\", 50, undefined, false, 50);\n * ```\n *\n * To read ONE row, use `getDataItem` rather than a page plus a client-side\n * `.find()` — that pattern breaks silently once the collection outgrows `limit`. */",
11378
- "decl": "export function queryDataItems(\n dataCollectionId: string,\n limit = 50,\n filter?: Record<string, unknown>,\n returnTotalCount = false,\n offset = 0,\n): Promise<WixQueryDataItemsResponse>;",
11389
+ "jsdoc": "/** Query items in a collection — POST /wix-data/v2/items/query.\n *\n * `filter` is the Wix Data v2 filter object, which the type cannot express (it is\n * `Record<string, unknown>` by construction). A field name maps to a value for\n * equality, or to an operator object:\n *\n * ```ts\n * await queryDataItems(\"Listings\", 20, { city: \"Tel Aviv\" });\n * await queryDataItems(\"Listings\", 20, { price: { $lte: 2_500_000 } });\n * await queryDataItems(\"Listings\", 20, { _id: { $in: [a, b] } });\n * ```\n *\n * `offset` pages: ask for `limit` rows starting at `offset`. Pair it with\n * `returnTotalCount: true`, which fills `pagingMetadata.total` so a caller knows\n * when to stop — it is omitted by default because counting is extra work, and it\n * reports total for OFFSET paging only, which is the mode this function uses.\n *\n * ```ts\n * const first = await queryDataItems(\"Listings\", 50, undefined, true);\n * const total = first.pagingMetadata?.total ?? 0;\n * const rest = await queryDataItems(\"Listings\", 50, undefined, false, 50);\n * ```\n *\n * `sort` orders server-side, so \"newest first\" is one call instead of fetching every row\n * and sorting on the client (which also breaks once the collection outgrows `limit`):\n *\n * ```ts\n * await queryDataItems(\"Jobs\", 1, undefined, false, 0, [{ fieldName: \"postedDate\", order: \"DESC\" }]);\n * ```\n *\n * To read ONE row, use `getDataItem` rather than a page plus a client-side\n * `.find()` — that pattern breaks silently once the collection outgrows `limit`. */",
11390
+ "decl": "export function queryDataItems(\n dataCollectionId: string,\n limit = 50,\n filter?: Record<string, unknown>,\n returnTotalCount = false,\n offset = 0,\n sort?: WixDataSort[],\n): Promise<WixQueryDataItemsResponse>;",
11379
11391
  "http": "POST /wix-data/v2/items/query"
11380
11392
  },
11381
11393
  {
@@ -115,6 +115,7 @@ himi template inspect # recompute the inventory after edi
115
115
  himi template screens # list dev/index.ts screens without reading the file
116
116
  himi template screens --show himi_saved > saved.txt # ONE screen's entry
117
117
  himi template screens --write himi_saved --from saved.txt # put it back — re-parsed, refused if it breaks the file
118
+ himi template screens --add himi_sellers --from sellers.txt --parent himi_branded_home # a NEW screen, registered as linkable
118
119
  himi template sweep --set "Sample content=>Our latest" --dry-run # copy inside string literals only
119
120
  himi template trim --from-spec --links drop --dry-run # keep SPEC.md's Screen catalogue ids — read fromSpec.droppedPrimary first
120
121
  ```
@@ -180,9 +180,13 @@ surface. Lead with it: it builds the request, calls the bridge, and decodes the
180
180
  ```ts
181
181
  import { queryDataItems, insertDataItem, updateDataItem, removeDataItem } from "@himalaya/wix-data";
182
182
 
183
- // queryDataItems(collectionId, limit = 50, filter?) → { dataItems?, items?, pagingMetadata? }
183
+ // queryDataItems(collectionId, limit = 50, filter?, returnTotalCount = false, offset = 0, sort?)
184
+ // → { dataItems?, items?, pagingMetadata? }
184
185
  const res = await queryDataItems("MyCollection", 50);
185
186
  const rows = (res.dataItems ?? res.items ?? []).map((it) => it.data); // parsed objects, not bytes
187
+
188
+ // Order server-side — "newest first" is one call, not every row fetched and sorted on the client:
189
+ const newest = await queryDataItems("Jobs", 1, undefined, false, 0, [{ fieldName: "postedDate", order: "DESC" }]);
186
190
  ```
187
191
 
188
192
  The full items CRUD is typed: `insertDataItem(collectionId, data)` (POST `/wix-data/v2/items`),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/himalaya-cli",
3
- "version": "0.1152.0",
3
+ "version": "0.1153.0",
4
4
  "type": "module",
5
5
  "description": "himi — the Himalaya authoring CLI. Build a Tier-5 content package on your own machine, push it to the production serve as a draft, preview it on web + Himi Player, and read worker logs back — the remote dev loop. Wraps @himalaya/serve-cli for transport.",
6
6
  "keywords": [
@@ -67,5 +67,5 @@
67
67
  "artifactId": "himalaya-cli"
68
68
  }
69
69
  },
70
- "falconPackageHash": "ec72821be8fc18dc66961c702e0a8b0f4653ea5cfb9bbe17d54ff092"
70
+ "falconPackageHash": "18d6e0952993df1eccb6d9a826c4ec2fa5cf335d69a8cdf02e4891f2"
71
71
  }
@@ -2336,7 +2336,7 @@ const SCREENS = runtimeSetting({
2336
2336
  "verticals": "[\"store\",\"bookings\",\"plans\",\"blog\"]",
2337
2337
  "homeModules": "[{\"id\":\"member_progress\",\"label\":\"This week's schedule\",\"dataSource\":\"member.bookings.schedule\",\"emptyWhen\":[\"hideMemberProgress\"]},{\"id\":\"up_next\",\"label\":\"Up next\",\"dataSource\":\"member.bookings\",\"emptyWhen\":[\"hideNextBooking\",\"hidePersonalError\"]},{\"id\":\"saved\",\"label\":\"Saved on this device\",\"dataSource\":\"device.saved_items\",\"emptyWhen\":[\"noSavedContent\"]},{\"id\":\"section_0\",\"label\":\"Discover 1\",\"dataSource\":\"salonLuxe.section0\",\"emptyWhen\":[\"hideHeroImage\"]},{\"id\":\"section_1\",\"label\":\"__HIMI_APP_NAME__\",\"dataSource\":\"salonLuxe.section1\"}]",
2338
2338
  "ownerDefaultLayout": "salonLuxe",
2339
- "copy": "{\"home.bookVisitCta\":\"Book a visit\",\"home.featuredTitle\":\"Salon picks\",\"home.stylistsCaption\":\"Meet your stylists\",\"home.treatmentsSubtitle\":\"Book your favorite in a couple of taps.\",\"home.treatmentsTitle\":\"Signature treatments\"}"
2339
+ "copy": "{\"home.bookVisitCta\":\"Book a visit\",\"home.featuredTitle\":\"Salon picks\",\"home.profileBody\":\"Sign in for orders, bookings, memberships and saved posts.\",\"home.stylistsCaption\":\"Meet your stylists\",\"home.treatmentsSubtitle\":\"Book your favorite in a couple of taps.\",\"home.treatmentsTitle\":\"Signature treatments\"}"
2340
2340
  }
2341
2341
  }
2342
2342
  }
@@ -2387,7 +2387,7 @@ const SCREENS = runtimeSetting({
2387
2387
  "package": "himalaya",
2388
2388
  "type": "Text",
2389
2389
  "props": {
2390
- "text": "Orders, bookings, RSVPs and saved content in one place.",
2390
+ "text": "Orders, bookings, memberships and saved content in one place.",
2391
2391
  "style": "body",
2392
2392
  "color": "textSecondary"
2393
2393
  }
@@ -3876,7 +3876,10 @@ const SCREENS = runtimeSetting({
3876
3876
  "bottom": 28
3877
3877
  },
3878
3878
  "onAppear": {
3879
- "actionId": "tier5.himiStoreAccount.refresh"
3879
+ "actionId": "tier5.himiStoreAccount.refresh",
3880
+ "params": {
3881
+ "copy": "{\"account.subtitle\":\"Orders, bookings, memberships and saved content in one place.\",\"storeAccount.authBody\":\"Sign in to continue — cart, bookings and memberships stay with you.\",\"storeAccount.authBodyRegister\":\"Create a member account to continue — cart, bookings and memberships stay with you.\"}"
3882
+ }
3880
3883
  }
3881
3884
  }
3882
3885
  },
@@ -1958,7 +1958,7 @@ const SCREENS = runtimeSetting({
1958
1958
  "verticals": "[\"feed\",\"events\",\"blog\",\"groups\",\"cms\"]",
1959
1959
  "homeModules": "[{\"id\":\"saved\",\"label\":\"Saved on this device\",\"dataSource\":\"device.saved_items\",\"emptyWhen\":[\"noSavedContent\"]},{\"id\":\"section_0\",\"label\":\"Discover 1\",\"dataSource\":\"communityPulse.section0\",\"emptyWhen\":[\"noMemberAvatars\"]},{\"id\":\"section_1\",\"label\":\"Announcement\",\"dataSource\":\"communityPulse.section1\",\"emptyWhen\":[\"noAnnouncement\",\"noUpcomingEvents\"]},{\"id\":\"section_2\",\"label\":\"Groups\",\"dataSource\":\"communityPulse.section2\",\"emptyWhen\":[\"noGroupCards\"]},{\"id\":\"section_3\",\"label\":\"Latest activity\",\"dataSource\":\"communityPulse.section3\"}]",
1960
1960
  "ownerDefaultLayout": "communityPulse",
1961
- "copy": "{\"home.activityTitle\":\"Latest activity\",\"home.announcementCaption\":\"Announcement\",\"home.eventsTitle\":\"Upcoming events\",\"home.groupsSubtitle\":\"Find your people.\",\"home.groupsTitle\":\"Groups\",\"home.membersCaption\":\"Our members\",\"home.onboardingBody\":\"Join groups, RSVP to events, and keep up with the community feed — all in one place.\"}"
1961
+ "copy": "{\"home.activityTitle\":\"Latest activity\",\"home.announcementCaption\":\"Announcement\",\"home.eventsTitle\":\"Upcoming events\",\"home.groupsSubtitle\":\"Find your people.\",\"home.groupsTitle\":\"Groups\",\"home.membersCaption\":\"Our members\",\"home.onboardingBody\":\"Join groups, RSVP to events, and keep up with the community feed — all in one place.\",\"home.profileBody\":\"Sign in for RSVPs, groups and saved posts.\"}"
1962
1962
  }
1963
1963
  }
1964
1964
  }
@@ -2009,7 +2009,7 @@ const SCREENS = runtimeSetting({
2009
2009
  "package": "himalaya",
2010
2010
  "type": "Text",
2011
2011
  "props": {
2012
- "text": "Orders, bookings, RSVPs and saved content in one place.",
2012
+ "text": "RSVPs, groups and saved content in one place.",
2013
2013
  "style": "body",
2014
2014
  "color": "textSecondary"
2015
2015
  }
@@ -3145,7 +3145,10 @@ const SCREENS = runtimeSetting({
3145
3145
  "bottom": 28
3146
3146
  },
3147
3147
  "onAppear": {
3148
- "actionId": "tier5.himiStoreAccount.refresh"
3148
+ "actionId": "tier5.himiStoreAccount.refresh",
3149
+ "params": {
3150
+ "copy": "{\"account.subtitle\":\"RSVPs, groups and saved content in one place.\",\"storeAccount.authBody\":\"Sign in to continue — RSVPs and groups stay with you.\",\"storeAccount.authBodyRegister\":\"Create a member account to continue — RSVPs and groups stay with you.\"}"
3151
+ }
3149
3152
  }
3150
3153
  }
3151
3154
  },
@@ -2142,7 +2142,7 @@ const SCREENS = runtimeSetting({
2142
2142
  "verticals": "[\"feed\",\"events\",\"blog\",\"groups\"]",
2143
2143
  "homeModules": "[{\"id\":\"saved\",\"label\":\"Saved on this device\",\"dataSource\":\"device.saved_items\",\"emptyWhen\":[\"noSavedContent\"]},{\"id\":\"section_0\",\"label\":\"Next up\",\"dataSource\":\"eventsFirst.section0\",\"emptyWhen\":[\"noNextEvent\"]},{\"id\":\"section_1\",\"label\":\"This season\",\"dataSource\":\"eventsFirst.section1\",\"emptyWhen\":[\"noUpcomingEvents\"]},{\"id\":\"section_2\",\"label\":\"Discover 3\",\"dataSource\":\"eventsFirst.section2\"},{\"id\":\"section_3\",\"label\":\"Latest from the community\",\"dataSource\":\"eventsFirst.section3\"}]",
2144
2144
  "ownerDefaultLayout": "eventsFirst",
2145
- "copy": "{\"home.activityTitle\":\"Latest from the community\",\"home.agendaTitle\":\"This season\",\"home.allEventsCta\":\"All events\",\"home.eventsTitle\":\"Upcoming events\",\"home.groupsSubtitle\":\"Find your crew.\",\"home.groupsTitle\":\"Groups\",\"home.membersCaption\":\"Who's coming\",\"home.nextUpCaption\":\"Next up\",\"home.onboardingBody\":\"See what's next, RSVP in a couple of taps, and keep up with the community between events — all in one place.\",\"home.recapsCaption\":\"Event recaps\",\"home.rsvpCta\":\"View event & RSVP\"}"
2145
+ "copy": "{\"home.activityTitle\":\"Latest from the community\",\"home.agendaTitle\":\"This season\",\"home.allEventsCta\":\"All events\",\"home.eventsTitle\":\"Upcoming events\",\"home.groupsSubtitle\":\"Find your crew.\",\"home.groupsTitle\":\"Groups\",\"home.membersCaption\":\"Who's coming\",\"home.nextUpCaption\":\"Next up\",\"home.onboardingBody\":\"See what's next, RSVP in a couple of taps, and keep up with the community between events — all in one place.\",\"home.profileBody\":\"Sign in for RSVPs, groups and saved posts.\",\"home.recapsCaption\":\"Event recaps\",\"home.rsvpCta\":\"View event & RSVP\"}"
2146
2146
  }
2147
2147
  }
2148
2148
  }
@@ -2193,7 +2193,7 @@ const SCREENS = runtimeSetting({
2193
2193
  "package": "himalaya",
2194
2194
  "type": "Text",
2195
2195
  "props": {
2196
- "text": "Orders, bookings, RSVPs and saved content in one place.",
2196
+ "text": "RSVPs, groups and saved content in one place.",
2197
2197
  "style": "body",
2198
2198
  "color": "textSecondary"
2199
2199
  }
@@ -3329,7 +3329,10 @@ const SCREENS = runtimeSetting({
3329
3329
  "bottom": 28
3330
3330
  },
3331
3331
  "onAppear": {
3332
- "actionId": "tier5.himiStoreAccount.refresh"
3332
+ "actionId": "tier5.himiStoreAccount.refresh",
3333
+ "params": {
3334
+ "copy": "{\"account.subtitle\":\"RSVPs, groups and saved content in one place.\",\"storeAccount.authBody\":\"Sign in to continue — RSVPs and groups stay with you.\",\"storeAccount.authBodyRegister\":\"Create a member account to continue — RSVPs and groups stay with you.\"}"
3335
+ }
3333
3336
  }
3334
3337
  }
3335
3338
  },
@@ -2424,7 +2424,7 @@ const SCREENS = runtimeSetting({
2424
2424
  "verticals": "[\"store\",\"bookings\",\"plans\",\"events\",\"blog\"]",
2425
2425
  "homeModules": "[{\"id\":\"member_progress\",\"label\":\"This week's schedule\",\"dataSource\":\"member.bookings.schedule\",\"emptyWhen\":[\"hideMemberProgress\"]},{\"id\":\"up_next\",\"label\":\"Up next\",\"dataSource\":\"member.bookings\",\"emptyWhen\":[\"hideNextBooking\",\"hidePersonalError\"]},{\"id\":\"saved\",\"label\":\"Saved on this device\",\"dataSource\":\"device.saved_items\",\"emptyWhen\":[\"noSavedContent\"]},{\"id\":\"section_0\",\"label\":\"Discover 1\",\"dataSource\":\"trainingFirst.section0\",\"emptyWhen\":[\"hideHeroImage\"]},{\"id\":\"section_1\",\"label\":\"__HIMI_APP_NAME__\",\"dataSource\":\"trainingFirst.section1\"}]",
2426
2426
  "ownerDefaultLayout": "trainingFirst",
2427
- "copy": "{\"home.storeFeaturedTitle\":\"Shop the gear\"}"
2427
+ "copy": "{\"home.profileBody\":\"Sign in for orders, bookings, RSVPs, memberships and saved posts.\",\"home.storeFeaturedTitle\":\"Shop the gear\"}"
2428
2428
  }
2429
2429
  }
2430
2430
  }
@@ -2475,7 +2475,7 @@ const SCREENS = runtimeSetting({
2475
2475
  "package": "himalaya",
2476
2476
  "type": "Text",
2477
2477
  "props": {
2478
- "text": "Orders, bookings, RSVPs and saved content in one place.",
2478
+ "text": "Orders, bookings, RSVPs, memberships and saved content in one place.",
2479
2479
  "style": "body",
2480
2480
  "color": "textSecondary"
2481
2481
  }
@@ -4052,7 +4052,10 @@ const SCREENS = runtimeSetting({
4052
4052
  "bottom": 28
4053
4053
  },
4054
4054
  "onAppear": {
4055
- "actionId": "tier5.himiStoreAccount.refresh"
4055
+ "actionId": "tier5.himiStoreAccount.refresh",
4056
+ "params": {
4057
+ "copy": "{\"account.subtitle\":\"Orders, bookings, RSVPs, memberships and saved content in one place.\",\"storeAccount.authBody\":\"Sign in to continue — cart, bookings, RSVPs and memberships stay with you.\",\"storeAccount.authBodyRegister\":\"Create a member account to continue — cart, bookings, RSVPs and memberships stay with you.\"}"
4058
+ }
4056
4059
  }
4057
4060
  }
4058
4061
  },
@@ -2078,7 +2078,7 @@ const SCREENS = runtimeSetting({
2078
2078
  "verticals": "[\"plans\",\"feed\",\"events\",\"blog\",\"groups\"]",
2079
2079
  "homeModules": "[{\"id\":\"saved\",\"label\":\"Saved on this device\",\"dataSource\":\"device.saved_items\",\"emptyWhen\":[\"noSavedContent\"]},{\"id\":\"section_0\",\"label\":\"Discover 1\",\"dataSource\":\"communityPulse.section0\",\"emptyWhen\":[\"noMemberAvatars\"]},{\"id\":\"section_1\",\"label\":\"From the field\",\"dataSource\":\"communityPulse.section1\",\"emptyWhen\":[\"noAnnouncement\",\"noUpcomingEvents\"]},{\"id\":\"section_2\",\"label\":\"Chapters & teams\",\"dataSource\":\"communityPulse.section2\",\"emptyWhen\":[\"noGroupCards\"]},{\"id\":\"section_3\",\"label\":\"Latest from the community\",\"dataSource\":\"communityPulse.section3\"}]",
2080
2080
  "ownerDefaultLayout": "communityPulse",
2081
- "copy": "{\"home.activityTitle\":\"Latest from the community\",\"home.announcementCaption\":\"From the field\",\"home.eventsTitle\":\"Volunteer opportunities\",\"home.groupsSubtitle\":\"Find your cause and pitch in.\",\"home.groupsTitle\":\"Chapters & teams\",\"home.joinConversationCta\":\"Share your story\",\"home.membersCaption\":\"Our volunteers\",\"home.onboardingBody\":\"Find a way to give, sign up to volunteer, and follow the mission's impact — all in one place.\"}"
2081
+ "copy": "{\"home.activityTitle\":\"Latest from the community\",\"home.announcementCaption\":\"From the field\",\"home.eventsTitle\":\"Volunteer opportunities\",\"home.groupsSubtitle\":\"Find your cause and pitch in.\",\"home.groupsTitle\":\"Chapters & teams\",\"home.joinConversationCta\":\"Share your story\",\"home.membersCaption\":\"Our volunteers\",\"home.onboardingBody\":\"Find a way to give, sign up to volunteer, and follow the mission's impact — all in one place.\",\"home.profileBody\":\"Sign in for RSVPs, memberships, groups and saved posts.\"}"
2082
2082
  }
2083
2083
  }
2084
2084
  }
@@ -2129,7 +2129,7 @@ const SCREENS = runtimeSetting({
2129
2129
  "package": "himalaya",
2130
2130
  "type": "Text",
2131
2131
  "props": {
2132
- "text": "Orders, bookings, RSVPs and saved content in one place.",
2132
+ "text": "RSVPs, memberships, groups and saved content in one place.",
2133
2133
  "style": "body",
2134
2134
  "color": "textSecondary"
2135
2135
  }
@@ -3442,7 +3442,10 @@ const SCREENS = runtimeSetting({
3442
3442
  "bottom": 28
3443
3443
  },
3444
3444
  "onAppear": {
3445
- "actionId": "tier5.himiStoreAccount.refresh"
3445
+ "actionId": "tier5.himiStoreAccount.refresh",
3446
+ "params": {
3447
+ "copy": "{\"account.subtitle\":\"RSVPs, memberships, groups and saved content in one place.\",\"storeAccount.authBody\":\"Sign in to continue — RSVPs, memberships and groups stay with you.\",\"storeAccount.authBodyRegister\":\"Create a member account to continue — RSVPs, memberships and groups stay with you.\"}"
3448
+ }
3446
3449
  }
3447
3450
  }
3448
3451
  },
@@ -2016,7 +2016,7 @@ const SCREENS = runtimeSetting({
2016
2016
  "verticals": "[\"plans\",\"feed\",\"events\",\"blog\",\"podcasts\"]",
2017
2017
  "homeModules": "[{\"id\":\"saved\",\"label\":\"Saved on this device\",\"dataSource\":\"device.saved_items\",\"emptyWhen\":[\"noSavedContent\"]},{\"id\":\"section_0\",\"label\":\"Latest episode\",\"dataSource\":\"episodeFirst.section0\",\"emptyWhen\":[\"noLatestPost\"]},{\"id\":\"section_1\",\"label\":\"All episodes\",\"dataSource\":\"episodeFirst.section1\",\"emptyWhen\":[\"noPostCards\"]},{\"id\":\"section_2\",\"label\":\"Discover 3\",\"dataSource\":\"episodeFirst.section2\"},{\"id\":\"section_3\",\"label\":\"Latest activity\",\"dataSource\":\"episodeFirst.section3\"}]",
2018
2018
  "ownerDefaultLayout": "episodeFirst",
2019
- "copy": "{\"home.allEpisodesCta\":\"Browse all episodes\",\"home.episodesTitle\":\"All episodes\",\"home.latestEpisodeCaption\":\"Latest episode\",\"home.listenCta\":\"Listen now\",\"home.listenersCaption\":\"Our listeners\",\"home.notesCta\":\"Read the show notes\",\"home.onboardingBody\":\"Catch the latest episode, read the show notes, meet other listeners and support the show — all in one place.\",\"home.supportSubtitle\":\"Join a membership and keep new episodes coming.\",\"home.supportTitle\":\"Support the show\"}"
2019
+ "copy": "{\"home.allEpisodesCta\":\"Browse all episodes\",\"home.episodesTitle\":\"All episodes\",\"home.latestEpisodeCaption\":\"Latest episode\",\"home.listenCta\":\"Listen now\",\"home.listenersCaption\":\"Our listeners\",\"home.notesCta\":\"Read the show notes\",\"home.onboardingBody\":\"Catch the latest episode, read the show notes, meet other listeners and support the show — all in one place.\",\"home.profileBody\":\"Sign in for RSVPs, memberships and saved posts.\",\"home.supportSubtitle\":\"Join a membership and keep new episodes coming.\",\"home.supportTitle\":\"Support the show\"}"
2020
2020
  }
2021
2021
  }
2022
2022
  }
@@ -2067,7 +2067,7 @@ const SCREENS = runtimeSetting({
2067
2067
  "package": "himalaya",
2068
2068
  "type": "Text",
2069
2069
  "props": {
2070
- "text": "Orders, bookings, RSVPs and saved content in one place.",
2070
+ "text": "RSVPs, memberships and saved content in one place.",
2071
2071
  "style": "body",
2072
2072
  "color": "textSecondary"
2073
2073
  }
@@ -3380,7 +3380,10 @@ const SCREENS = runtimeSetting({
3380
3380
  "bottom": 28
3381
3381
  },
3382
3382
  "onAppear": {
3383
- "actionId": "tier5.himiStoreAccount.refresh"
3383
+ "actionId": "tier5.himiStoreAccount.refresh",
3384
+ "params": {
3385
+ "copy": "{\"account.subtitle\":\"RSVPs, memberships and saved content in one place.\",\"storeAccount.authBody\":\"Sign in to continue — RSVPs and memberships stay with you.\",\"storeAccount.authBodyRegister\":\"Create a member account to continue — RSVPs and memberships stay with you.\"}"
3386
+ }
3384
3387
  }
3385
3388
  }
3386
3389
  },
@@ -1886,7 +1886,7 @@ const SCREENS = runtimeSetting({
1886
1886
  "verticals": "[\"store\",\"portfolio\",\"events\",\"blog\"]",
1887
1887
  "homeModules": "[{\"id\":\"saved\",\"label\":\"Saved on this device\",\"dataSource\":\"device.saved_items\",\"emptyWhen\":[\"noSavedContent\"]},{\"id\":\"section_0\",\"label\":\"Selected work\",\"dataSource\":\"showcaseFirst.section0\",\"emptyWhen\":[\"noPostCards\"]},{\"id\":\"section_1\",\"label\":\"Discover 2\",\"dataSource\":\"showcaseFirst.section1\"},{\"id\":\"section_2\",\"label\":\"Prints & editions\",\"dataSource\":\"showcaseFirst.section2\",\"emptyWhen\":[\"noFeaturedProducts\"]},{\"id\":\"section_3\",\"label\":\"Exhibitions & fairs\",\"dataSource\":\"showcaseFirst.section3\"}]",
1888
1888
  "ownerDefaultLayout": "showcaseFirst",
1889
- "copy": "{\"home.aboutTitle\":\"About the studio\",\"home.contactCta\":\"Get in touch\",\"home.exhibitionsTitle\":\"Exhibitions & fairs\",\"home.onboardingBody\":\"Browse the latest work, pick up a print, and catch the next exhibition — all in one place.\",\"home.printsTitle\":\"Prints & editions\",\"home.viewWorkCta\":\"View all work\",\"home.workSubtitle\":\"The latest pieces, straight from the studio.\",\"home.workTitle\":\"Selected work\"}"
1889
+ "copy": "{\"home.aboutTitle\":\"About the studio\",\"home.contactCta\":\"Get in touch\",\"home.exhibitionsTitle\":\"Exhibitions & fairs\",\"home.onboardingBody\":\"Browse the latest work, pick up a print, and catch the next exhibition — all in one place.\",\"home.printsTitle\":\"Prints & editions\",\"home.profileBody\":\"Sign in for orders, RSVPs and saved posts.\",\"home.viewWorkCta\":\"View all work\",\"home.workSubtitle\":\"The latest pieces, straight from the studio.\",\"home.workTitle\":\"Selected work\"}"
1890
1890
  }
1891
1891
  }
1892
1892
  }
@@ -1937,7 +1937,7 @@ const SCREENS = runtimeSetting({
1937
1937
  "package": "himalaya",
1938
1938
  "type": "Text",
1939
1939
  "props": {
1940
- "text": "Orders, bookings, RSVPs and saved content in one place.",
1940
+ "text": "Orders, RSVPs and saved content in one place.",
1941
1941
  "style": "body",
1942
1942
  "color": "textSecondary"
1943
1943
  }
@@ -3249,7 +3249,10 @@ const SCREENS = runtimeSetting({
3249
3249
  "bottom": 28
3250
3250
  },
3251
3251
  "onAppear": {
3252
- "actionId": "tier5.himiStoreAccount.refresh"
3252
+ "actionId": "tier5.himiStoreAccount.refresh",
3253
+ "params": {
3254
+ "copy": "{\"account.subtitle\":\"Orders, RSVPs and saved content in one place.\",\"storeAccount.authBody\":\"Sign in to continue — cart and RSVPs stay with you.\",\"storeAccount.authBodyRegister\":\"Create a member account to continue — cart and RSVPs stay with you.\"}"
3255
+ }
3253
3256
  }
3254
3257
  }
3255
3258
  },
@@ -1942,7 +1942,7 @@ const SCREENS = runtimeSetting({
1942
1942
  "verticals": "[\"restaurants\",\"events\",\"blog\"]",
1943
1943
  "homeModules": "[{\"id\":\"saved\",\"label\":\"Saved on this device\",\"dataSource\":\"device.saved_items\",\"emptyWhen\":[\"noSavedContent\"]},{\"id\":\"section_0\",\"label\":\"__HIMI_APP_NAME__\",\"dataSource\":\"menuFirst.section0\",\"emptyWhen\":[\"hasDishHeroImage\"]},{\"id\":\"section_1\",\"label\":\"Discover 2\",\"dataSource\":\"menuFirst.section1\",\"emptyWhen\":[\"noMenuCategories\"]},{\"id\":\"section_2\",\"label\":\"Popular dishes\",\"dataSource\":\"menuFirst.section2\",\"emptyWhen\":[\"noPopularDishes\"]},{\"id\":\"section_3\",\"label\":\"Find us\",\"dataSource\":\"menuFirst.section3\"}]",
1944
1944
  "ownerDefaultLayout": "menuFirst",
1945
- "copy": "{\"home.eventsTitle\":\"Wine nights & events\",\"home.findUsTitle\":\"Find us\",\"home.hoursTitle\":\"Opening hours\",\"home.onboardingBody\":\"Browse the menu, reserve a table, and keep up with wine nights and kitchen news — all in one place.\",\"home.popularSubtitle\":\"The plates everyone keeps coming back for.\",\"home.popularTitle\":\"Popular dishes\",\"home.reserveCta\":\"Reserve a table\"}"
1945
+ "copy": "{\"home.eventsTitle\":\"Wine nights & events\",\"home.findUsTitle\":\"Find us\",\"home.hoursTitle\":\"Opening hours\",\"home.onboardingBody\":\"Browse the menu, reserve a table, and keep up with wine nights and kitchen news — all in one place.\",\"home.popularSubtitle\":\"The plates everyone keeps coming back for.\",\"home.popularTitle\":\"Popular dishes\",\"home.profileBody\":\"Sign in for reservations, RSVPs and saved posts.\",\"home.reserveCta\":\"Reserve a table\"}"
1946
1946
  }
1947
1947
  }
1948
1948
  }
@@ -1993,7 +1993,7 @@ const SCREENS = runtimeSetting({
1993
1993
  "package": "himalaya",
1994
1994
  "type": "Text",
1995
1995
  "props": {
1996
- "text": "Orders, bookings, RSVPs and saved content in one place.",
1996
+ "text": "Reservations, RSVPs and saved content in one place.",
1997
1997
  "style": "body",
1998
1998
  "color": "textSecondary"
1999
1999
  }
@@ -3217,7 +3217,10 @@ const SCREENS = runtimeSetting({
3217
3217
  "bottom": 28
3218
3218
  },
3219
3219
  "onAppear": {
3220
- "actionId": "tier5.himiStoreAccount.refresh"
3220
+ "actionId": "tier5.himiStoreAccount.refresh",
3221
+ "params": {
3222
+ "copy": "{\"account.subtitle\":\"Reservations, RSVPs and saved content in one place.\",\"storeAccount.authBody\":\"Sign in to continue — reservations and RSVPs stay with you.\",\"storeAccount.authBodyRegister\":\"Create a member account to continue — reservations and RSVPs stay with you.\"}"
3223
+ }
3221
3224
  }
3222
3225
  }
3223
3226
  },
@@ -1419,7 +1419,8 @@ const SCREENS = runtimeSetting({
1419
1419
  "params": {
1420
1420
  "verticals": "[\"store\",\"blog\"]",
1421
1421
  "homeModules": "[{\"id\":\"saved\",\"label\":\"Saved on this device\",\"dataSource\":\"device.saved_items\",\"emptyWhen\":[\"noSavedContent\"]},{\"id\":\"section_0\",\"label\":\"Shop the collection\",\"dataSource\":\"storeFirst.section0\"},{\"id\":\"section_1\",\"label\":\"Explore\",\"dataSource\":\"storeFirst.section1\"},{\"id\":\"section_2\",\"label\":\"Shop\",\"dataSource\":\"storeFirst.section2\"}]",
1422
- "ownerDefaultLayout": "storeFirst"
1422
+ "ownerDefaultLayout": "storeFirst",
1423
+ "copy": "{\"home.profileBody\":\"Sign in for orders and saved posts.\"}"
1423
1424
  }
1424
1425
  }
1425
1426
  }
@@ -1470,7 +1471,7 @@ const SCREENS = runtimeSetting({
1470
1471
  "package": "himalaya",
1471
1472
  "type": "Text",
1472
1473
  "props": {
1473
- "text": "Orders, bookings, RSVPs and saved content in one place.",
1474
+ "text": "Orders and saved content in one place.",
1474
1475
  "style": "body",
1475
1476
  "color": "textSecondary"
1476
1477
  }
@@ -2694,7 +2695,10 @@ const SCREENS = runtimeSetting({
2694
2695
  "bottom": 28
2695
2696
  },
2696
2697
  "onAppear": {
2697
- "actionId": "tier5.himiStoreAccount.refresh"
2698
+ "actionId": "tier5.himiStoreAccount.refresh",
2699
+ "params": {
2700
+ "copy": "{\"account.subtitle\":\"Orders and saved content in one place.\",\"storeAccount.authBody\":\"Sign in to continue — your cart stays with you.\",\"storeAccount.authBodyRegister\":\"Create a member account to continue — your cart stays with you.\"}"
2701
+ }
2698
2702
  }
2699
2703
  }
2700
2704
  },
@@ -2350,7 +2350,7 @@ const SCREENS = runtimeSetting({
2350
2350
  "verticals": "[\"bookings\",\"programs\",\"plans\",\"events\",\"blog\"]",
2351
2351
  "homeModules": "[{\"id\":\"member_progress\",\"label\":\"Today's schedule\",\"dataSource\":\"member.bookings.schedule\",\"emptyWhen\":[\"hideMemberProgress\"]},{\"id\":\"up_next\",\"label\":\"Up next\",\"dataSource\":\"member.bookings\",\"emptyWhen\":[\"hideNextBooking\",\"hidePersonalError\"]},{\"id\":\"saved\",\"label\":\"Saved on this device\",\"dataSource\":\"device.saved_items\",\"emptyWhen\":[\"noSavedContent\"]},{\"id\":\"section_0\",\"label\":\"Today's schedule\",\"dataSource\":\"calmStudio.section0\",\"emptyWhen\":[\"noTodaysClasses\"]},{\"id\":\"section_1\",\"label\":\"Your practice\",\"dataSource\":\"calmStudio.section1\"}]",
2352
2352
  "ownerDefaultLayout": "calmStudio",
2353
- "copy": "{\"home.bookClassCta\":\"Reserve your spot\",\"home.onboardingBody\":\"Reserve classes, follow programs, and join retreats — all from one calm place.\",\"home.practiceTitle\":\"Your practice\",\"home.retreatsTitle\":\"Retreats & workshops\",\"home.scheduleTitle\":\"Today's schedule\",\"home.teachersCaption\":\"Your teachers\"}"
2353
+ "copy": "{\"home.bookClassCta\":\"Reserve your spot\",\"home.onboardingBody\":\"Reserve classes, follow programs, and join retreats — all from one calm place.\",\"home.practiceTitle\":\"Your practice\",\"home.profileBody\":\"Sign in for bookings, RSVPs, memberships, programs and saved posts.\",\"home.retreatsTitle\":\"Retreats & workshops\",\"home.scheduleTitle\":\"Today's schedule\",\"home.teachersCaption\":\"Your teachers\"}"
2354
2354
  }
2355
2355
  }
2356
2356
  }
@@ -2401,7 +2401,7 @@ const SCREENS = runtimeSetting({
2401
2401
  "package": "himalaya",
2402
2402
  "type": "Text",
2403
2403
  "props": {
2404
- "text": "Orders, bookings, RSVPs and saved content in one place.",
2404
+ "text": "Bookings, RSVPs, memberships, programs and saved content in one place.",
2405
2405
  "style": "body",
2406
2406
  "color": "textSecondary"
2407
2407
  }
@@ -3802,7 +3802,10 @@ const SCREENS = runtimeSetting({
3802
3802
  "bottom": 28
3803
3803
  },
3804
3804
  "onAppear": {
3805
- "actionId": "tier5.himiStoreAccount.refresh"
3805
+ "actionId": "tier5.himiStoreAccount.refresh",
3806
+ "params": {
3807
+ "copy": "{\"account.subtitle\":\"Bookings, RSVPs, memberships, programs and saved content in one place.\",\"storeAccount.authBody\":\"Sign in to continue — bookings, RSVPs, memberships and programs stay with you.\",\"storeAccount.authBodyRegister\":\"Create a member account to continue — bookings, RSVPs, memberships and programs stay with you.\"}"
3808
+ }
3806
3809
  }
3807
3810
  }
3808
3811
  },