@wix/himalaya-cli 0.1003.0 → 0.1004.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.
@@ -263,8 +263,22 @@ export interface WixBookSlotOptions {
263
263
  * for an OFFLINE booking, so do not read one as "charge now" and send a
264
264
  * pay-in-person customer into the cashier.
265
265
  *
266
- * Wix documents `selectedPaymentOption` as REQUIRED whenever the booking is paid
267
- * through an eCommerce checkout, and it must be an option the service offers.
266
+ * Wix's Create Booking reference says to specify `selectedPaymentOption` when the
267
+ * customer pays through a Wix eCommerce checkout, and that the call fails
268
+ * otherwise. That failure has not been observed. The member paid flow in the
269
+ * `himi-booking-time` worker calls `bookSlot` with no option and then checks
270
+ * out, and a live run of it created the booking, the checkout and the order.
271
+ * That run never completed payment, though, so whether a booking made without
272
+ * an option confirms once its order is paid is still unverified, and Wix does
273
+ * not document what an omitted option defaults to. Pass it when you know how
274
+ * the customer means to pay.
275
+ *
276
+ * Any value you pass must be one the service offers. Wix exposes that on the
277
+ * Services V2 service as `payment.options` (booleans `online`, `inPerson`,
278
+ * `deposit`, `pricingPlan`). The `WixService` type here does not declare it
279
+ * yet, so read it from the service response with a cast.
280
+ * Do not hard-code `ONLINE`: a service can be set up for in-person payment
281
+ * only, and then `ONLINE` is not one of its options.
268
282
  * Leave `options` out and the request is the one this function always sent. */
269
283
  export declare function bookSlot(slot: WixBookingSlotFull, firstName: string, lastName: string, email: string, options?: WixBookSlotOptions): Promise<WixCreateBookingResponse>;
270
284
  export interface WixBookingAdditionalField {
package/dist/cli.mjs CHANGED
@@ -5318,6 +5318,7 @@ function walk(node, registry, path2, out) {
5318
5318
  var SYSTEM_FONT_FAMILY;
5319
5319
  var init_tokens = __esm({
5320
5320
  "../../core/ts/src/tokens.ts"() {
5321
+ "use strict";
5321
5322
  SYSTEM_FONT_FAMILY = "$system";
5322
5323
  }
5323
5324
  });
@@ -19460,7 +19461,6 @@ function shouldRevealError(args) {
19460
19461
  var EMAIL_RE, URL_HOST_RE, INTEGER_RE, NUMBER_RE;
19461
19462
  var init_validation = __esm({
19462
19463
  "../../core/ts/src/validation.ts"() {
19463
- "use strict";
19464
19464
  EMAIL_RE = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*\.[A-Za-z]{2,}$/;
19465
19465
  URL_HOST_RE = /^[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*(:[0-9]+)?$/;
19466
19466
  INTEGER_RE = /^[+-]?[0-9]+$/;
@@ -22885,7 +22885,6 @@ function validateWorkerSubtree(raw, slotId, policy, actionCatalog) {
22885
22885
  var WORKER_SUBTREE_CEILINGS, ID_PART, ACTION_ID;
22886
22886
  var init_worker_subtree = __esm({
22887
22887
  "../../core/ts/src/worker-subtree.ts"() {
22888
- "use strict";
22889
22888
  init_worker_subtree_safe_types_generated();
22890
22889
  WORKER_SUBTREE_CEILINGS = Object.freeze({
22891
22890
  maxEncodedBytes: 65536,
@@ -41107,6 +41106,15 @@ var init_mobile_ux_lint = __esm({
41107
41106
  }
41108
41107
  });
41109
41108
 
41109
+ // ../../core/serve-runtime/src/site-lease.ts
41110
+ var SITE_CONTROL_TTL_SECONDS, SITE_AUTHORIZATION_TTL_SECONDS;
41111
+ var init_site_lease = __esm({
41112
+ "../../core/serve-runtime/src/site-lease.ts"() {
41113
+ SITE_CONTROL_TTL_SECONDS = 15 * 60;
41114
+ SITE_AUTHORIZATION_TTL_SECONDS = SITE_CONTROL_TTL_SECONDS + 45 * 60;
41115
+ }
41116
+ });
41117
+
41110
41118
  // ../../core/serve-runtime/src/preview-token.ts
41111
41119
  async function hmacKey(secret) {
41112
41120
  return crypto.subtle.importKey("raw", new TextEncoder().encode(secret), SUBTLE_ALGO, false, ["sign"]);
@@ -41147,6 +41155,7 @@ async function verifyPreviewToken(input) {
41147
41155
  var SUBTLE_ALGO, PREVIEW_TOKEN_VERSION, DEFAULT_PREVIEW_TOKEN_TTL_SECONDS, WEB_PREVIEW_TOKEN_TTL_SECONDS;
41148
41156
  var init_preview_token = __esm({
41149
41157
  "../../core/serve-runtime/src/preview-token.ts"() {
41158
+ init_site_lease();
41150
41159
  init_base64();
41151
41160
  SUBTLE_ALGO = { name: "HMAC", hash: "SHA-256" };
41152
41161
  PREVIEW_TOKEN_VERSION = 1;
@@ -42496,6 +42505,7 @@ function makeFunctionsHandler(options) {
42496
42505
  const token = request.headers.get("x-himalaya-preview-token") || new URL(request.url).searchParams.get("pvt");
42497
42506
  const verified = token ? await verifyPreviewToken({ token, secret: options.previewTokenSecret, app: target.app, releaseId: pin, nowSeconds: options.nowSeconds }) : { ok: false, error: "malformed" };
42498
42507
  if (!verified.ok || verified.claims.source !== "functions") return errorResponse(401, `preview_token_${verified.ok ? "scope_mismatch" : verified.error}`, "a valid function preview token is required", id);
42508
+ if (options.authorizePreview ? !await options.authorizePreview(verified.claims) : !!verified.claims.siteLease) return errorResponse(403, "site_preview_forbidden", "site authorization expired or changed", id);
42499
42509
  previewClaims = verified.claims;
42500
42510
  }
42501
42511
  if (pin) selected = options.store.getManifestById ? await options.store.getManifestById(target.app, pin) : null;
@@ -44242,13 +44252,16 @@ ${releaseId}`;
44242
44252
  }
44243
44253
  async function authorizePinnedRead(request, url, app, releaseId, screenId) {
44244
44254
  const secret = opts.previewTokenSecret;
44245
- if (!secret) return null;
44255
+ if (!secret) return opts.authorizePreview ? json3(503, { error: "preview_token_unavailable" }) : null;
44246
44256
  const token = resolvePreviewToken(request, url);
44247
44257
  const live2 = await manifest(app);
44248
44258
  const isLive = live2?.releaseId === releaseId;
44249
44259
  if (!token) return isLive ? null : json3(401, { error: "preview_token_required", app, releaseId });
44250
44260
  const verified = await verifyPreviewToken({ token, secret, app, releaseId, screenId });
44251
- if (verified.ok) return null;
44261
+ if (verified.ok) {
44262
+ if (opts.authorizePreview ? !await opts.authorizePreview(verified.claims) : !!verified.claims.siteLease) return json3(403, { error: "site_preview_forbidden" });
44263
+ return null;
44264
+ }
44252
44265
  return json3(verified.error === "scope_mismatch" ? 403 : 401, {
44253
44266
  error: `preview_token_${verified.error}`,
44254
44267
  app,
@@ -44681,7 +44694,8 @@ ${releaseId}`;
44681
44694
  templates.sort((a, b) => String(a.name).localeCompare(String(b.name)));
44682
44695
  return json3(200, { templates });
44683
44696
  }
44684
- const app = resolveApp(request, url);
44697
+ const cfgMatch = /^\/v1\/app-config\/([^/]+)$/.exec(path2);
44698
+ const app = cfgMatch?.[1] ?? resolveApp(request, url);
44685
44699
  const pin = resolvePin(request, url);
44686
44700
  if (path2 === "/v1/template-source") {
44687
44701
  if (!app) return json3(400, { error: "missing_app" });
@@ -44919,7 +44933,6 @@ ${releaseId}`;
44919
44933
  }));
44920
44934
  return json3(200, { handlers });
44921
44935
  }
44922
- const cfgMatch = /^\/v1\/app-config\/([^/]+)$/.exec(path2);
44923
44936
  if (cfgMatch) {
44924
44937
  const cfgApp = cfgMatch[1];
44925
44938
  if (pin) {
@@ -45008,7 +45021,12 @@ ${releaseId}`;
45008
45021
  if (request.method === "OPTIONS") {
45009
45022
  return new Response(null, { status: 204, headers: { ...corsHeaders(request), "content-length": "0" } });
45010
45023
  }
45011
- return withCors(request, await handle(request));
45024
+ const response = await handle(request);
45025
+ if (resolvePreviewToken(request, new URL(request.url))) {
45026
+ response.headers.set("cache-control", "private, no-store");
45027
+ response.headers.set("vary", "x-himalaya-preview-token, x-himalaya-release");
45028
+ }
45029
+ return withCors(request, response);
45012
45030
  };
45013
45031
  }
45014
45032
  var JSON_CT;
@@ -56859,7 +56877,7 @@ var init_context = __esm({
56859
56877
 
56860
56878
  // src/cli.ts
56861
56879
  import { mkdtempSync as mkdtempSync4, readFileSync as readFileSync67, mkdirSync as mkdirSync27, writeFileSync as writeFileSync25, existsSync as existsSync56, rmSync as rmSync10, readdirSync as readdirSync25, statSync as statSync13, lstatSync as lstatSync6, accessSync, constants } from "node:fs";
56862
- import { execFileSync as execFileSync7, spawnSync as spawnSync5, spawn as spawn5 } from "node:child_process";
56880
+ import { execFile as execFile4, execFileSync as execFileSync7, spawnSync as spawnSync5, spawn as spawn5 } from "node:child_process";
56863
56881
  import { createHash as createHash24, randomUUID as randomUUID3 } from "node:crypto";
56864
56882
  import { tmpdir as tmpdir5, homedir as homedir7, hostname } from "node:os";
56865
56883
  import { join as join52, resolve as resolve44, relative as relative8, basename as basename12, dirname as dirname31 } from "node:path";
@@ -60813,30 +60831,48 @@ function trustedConfiguredRegistry(env) {
60813
60831
  const configured = normalizeRegistry(env.npm_config_registry ?? env.NPM_CONFIG_REGISTRY);
60814
60832
  return isTrustedRegistry(configured) ? configured : void 0;
60815
60833
  }
60816
- function createScopedRegistryResolver(exec = execFileSync7) {
60834
+ function createScopedRegistryResolver(exec = execNpmConfig) {
60817
60835
  let resolved2;
60818
- return () => {
60836
+ return async (budgetMs = SCOPED_REGISTRY_TIMEOUT_MS) => {
60819
60837
  if (resolved2) return resolved2.value;
60820
- let value;
60838
+ const deadline = Date.now() + Math.min(budgetMs, SCOPED_REGISTRY_TIMEOUT_MS);
60821
60839
  try {
60822
- const configured = (key2) => {
60823
- const raw = exec("npm", ["config", "get", key2], {
60824
- cwd: homedir7(),
60825
- timeout: 5e3,
60826
- stdio: ["ignore", "pipe", "ignore"]
60827
- }).toString().trim();
60840
+ const configured = async (key2) => {
60841
+ const timeout = deadline - Date.now();
60842
+ if (timeout <= 0) throw new Error("npm config: out of budget");
60843
+ const raw = String(await exec("npm", ["config", "get", key2], { cwd: homedir7(), timeout })).trim();
60828
60844
  return raw && raw !== "undefined" ? raw : void 0;
60829
60845
  };
60830
- const effective = normalizeRegistry(configured("@wix:registry") ?? configured("registry"));
60831
- value = isTrustedRegistry(effective) ? effective : void 0;
60846
+ const effective = normalizeRegistry(await configured("@wix:registry") ?? await configured("registry"));
60847
+ resolved2 = { value: isTrustedRegistry(effective) ? effective : void 0 };
60848
+ return resolved2.value;
60832
60849
  } catch {
60833
- value = void 0;
60850
+ return void 0;
60834
60851
  }
60835
- resolved2 = { value };
60836
- return value;
60837
60852
  };
60838
60853
  }
60854
+ var SCOPED_REGISTRY_TIMEOUT_MS = 5e3;
60855
+ function execNpmConfig(file, args, options) {
60856
+ return new Promise((resolve45, reject) => {
60857
+ execFile4(file, args, { ...options, windowsHide: true }, (error, stdout) => error ? reject(error) : resolve45(String(stdout)));
60858
+ });
60859
+ }
60839
60860
  var npmScopedWixRegistry = createScopedRegistryResolver();
60861
+ async function scopedRegistryBefore(lookup2, deadline) {
60862
+ let cancel = () => {
60863
+ };
60864
+ const expired = new Promise((resolve45) => {
60865
+ const timer = setTimeout(() => resolve45(void 0), Math.max(0, deadline - Date.now()));
60866
+ cancel = () => clearTimeout(timer);
60867
+ });
60868
+ try {
60869
+ return await Promise.race([Promise.resolve().then(() => lookup2(Math.max(0, deadline - Date.now()))), expired]);
60870
+ } catch {
60871
+ return void 0;
60872
+ } finally {
60873
+ cancel();
60874
+ }
60875
+ }
60840
60876
  function registryCandidates(env, scoped) {
60841
60877
  const ordered = [isTrustedRegistry(scoped) ? normalizeRegistry(scoped) : void 0, trustedConfiguredRegistry(env), PUBLIC_NPM_REGISTRY];
60842
60878
  return [...new Set(ordered.filter((r) => r !== void 0))];
@@ -60863,7 +60899,8 @@ async function fetchPackumentFrom(registry, fetchImpl, timeoutMs) {
60863
60899
  }
60864
60900
  async function fetchCliPackument(fetchImpl = fetch, timeoutMs = DOCTOR_TIMEOUT_MS, env = process.env, scopedRegistry = npmScopedWixRegistry) {
60865
60901
  const deadline = Date.now() + timeoutMs;
60866
- for (const registry of registryCandidates(env, scopedRegistry())) {
60902
+ const scoped = await scopedRegistryBefore(scopedRegistry, deadline);
60903
+ for (const registry of registryCandidates(env, scoped)) {
60867
60904
  const remaining = deadline - Date.now();
60868
60905
  if (remaining <= 0) break;
60869
60906
  const packument = await fetchPackumentFrom(registry, fetchImpl, remaining);
@@ -61161,8 +61198,10 @@ async function refreshUpdateCache(opts = {}) {
61161
61198
  const packument = await fetchCliPackument(opts.fetchImpl ?? fetch, UPDATE_BACKGROUND_BUDGET_MS, env, opts.scopedRegistry);
61162
61199
  if (!packument) return false;
61163
61200
  const now = opts.now ?? Date.now();
61201
+ const current = readUpdateCache(cachePath);
61202
+ if (supersededInCache(current, packument.latest, now)) return true;
61164
61203
  writeUpdateCache(cachePath, {
61165
- ...readUpdateCache(cachePath),
61204
+ ...current,
61166
61205
  running,
61167
61206
  latest: packument.latest,
61168
61207
  runningPublishedAt: packument.time[running],
@@ -61170,6 +61209,11 @@ async function refreshUpdateCache(opts = {}) {
61170
61209
  });
61171
61210
  return true;
61172
61211
  }
61212
+ function supersededInCache(current, latest, now) {
61213
+ if (!current.latest || !latest || compareVersions2(latest, current.latest) >= 0) return false;
61214
+ const checkedAt = current.checkedAt ? Date.parse(current.checkedAt) : Number.NaN;
61215
+ return Number.isFinite(checkedAt) && now - checkedAt < UPDATE_CHECK_INTERVAL_MS;
61216
+ }
61173
61217
  function createUpdateRefreshSpawner(spawnFn = spawn5, execArgv = process.execArgv) {
61174
61218
  return () => {
61175
61219
  try {
@@ -65990,7 +66034,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
65990
66034
  const { iosProjectYml: iosYmlPath, hasRepoCheckout: hasRepoCheckout2 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
65991
66035
  const { ascStatus: ascStatus2 } = await Promise.resolve().then(() => (init_asc_status(), asc_status_exports));
65992
66036
  const { readFileSync: rf, readdirSync: rd, existsSync: ex, statSync: st } = await import("node:fs");
65993
- const { execFile: execFile4 } = await import("node:child_process");
66037
+ const { execFile: execFile5 } = await import("node:child_process");
65994
66038
  const { homedir: hd } = await import("node:os");
65995
66039
  const { join: pj, dirname: pd } = await import("node:path");
65996
66040
  const { fileURLToPath: f2p } = await import("node:url");
@@ -66005,7 +66049,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
66005
66049
  return ipas[0];
66006
66050
  };
66007
66051
  const sh = (cmd, args, opts) => new Promise((res) => {
66008
- const child = execFile4(
66052
+ const child = execFile5(
66009
66053
  cmd,
66010
66054
  args,
66011
66055
  { cwd: opts.cwd, env: { ...process.env, ...opts.env }, maxBuffer: 64 * 1024 * 1024 },
@@ -66776,7 +66820,7 @@ ${label2}`);
66776
66820
  try {
66777
66821
  const scoped = up.scopedRegistry ?? npmScopedWixRegistry;
66778
66822
  const answered = await fetchCliPackument(up.fetchImpl ?? fetch, UPDATE_EXPLICIT_BUDGET_MS, env, scoped);
66779
- const registry = answered?.registry ?? registryCandidates(env, scoped())[0];
66823
+ const registry = answered?.registry ?? registryCandidates(env, await scoped(UPDATE_EXPLICIT_BUDGET_MS))[0];
66780
66824
  if (answered?.latest && compareVersions2(answered.latest, before) <= 0) {
66781
66825
  if (flags.json) print({ ok: true, running: before, installed: before, changed: false, registry, latest: answered.latest });
66782
66826
  else io.log(`himi ${before} (unchanged \u2014 ${registry} has nothing newer; its latest is ${answered.latest})`);
@@ -7311,7 +7311,7 @@
7311
7311
  {
7312
7312
  "name": "bookSlot",
7313
7313
  "kind": "function",
7314
- "jsdoc": "/** Book a specific availability slot — POST /bookings/v2/bookings. Pass the FULL\n * slot from queryServiceAvailability + the booker's contact. The v1 createBooking\n * above rejects a bare slot; the v2 route with bookedEntity.slot is the one that\n * works. Verified live (status CREATED).\n *\n * A visitor booking a PAID service to pay in person (no on-device payment) is a\n * three-call chain:\n *\n * 1. `bookSlot(slot, first, last, email, { selectedPaymentOption: \"OFFLINE\" })`\n * → the booking, status CREATED.\n * 2. `createCheckout([{ quantity: 1, catalogReference: { appId: <Wix Bookings\n * appDefId 13d21c63-b5ec-5912-8397-c3a5ddb27a97>, catalogItemId: booking.id } }])`\n * from `@himalaya/wix-ecom` → the checkout id.\n * 3. `createOrder(checkoutId)` from `@himalaya/wix-ecom` → the order, with the\n * balance left for the venue to collect.\n *\n * This chain was verified with raw Wix calls, not through these wrappers.\n * It is not verified whether `createOrder` still returns a `paymentGatewayOrderId`\n * for an OFFLINE booking, so do not read one as \"charge now\" and send a\n * pay-in-person customer into the cashier.\n *\n * Wix documents `selectedPaymentOption` as REQUIRED whenever the booking is paid\n * through an eCommerce checkout, and it must be an option the service offers.\n * Leave `options` out and the request is the one this function always sent. */",
7314
+ "jsdoc": "/** Book a specific availability slot — POST /bookings/v2/bookings. Pass the FULL\n * slot from queryServiceAvailability + the booker's contact. The v1 createBooking\n * above rejects a bare slot; the v2 route with bookedEntity.slot is the one that\n * works. Verified live (status CREATED).\n *\n * A visitor booking a PAID service to pay in person (no on-device payment) is a\n * three-call chain:\n *\n * 1. `bookSlot(slot, first, last, email, { selectedPaymentOption: \"OFFLINE\" })`\n * → the booking, status CREATED.\n * 2. `createCheckout([{ quantity: 1, catalogReference: { appId: <Wix Bookings\n * appDefId 13d21c63-b5ec-5912-8397-c3a5ddb27a97>, catalogItemId: booking.id } }])`\n * from `@himalaya/wix-ecom` → the checkout id.\n * 3. `createOrder(checkoutId)` from `@himalaya/wix-ecom` → the order, with the\n * balance left for the venue to collect.\n *\n * This chain was verified with raw Wix calls, not through these wrappers.\n * It is not verified whether `createOrder` still returns a `paymentGatewayOrderId`\n * for an OFFLINE booking, so do not read one as \"charge now\" and send a\n * pay-in-person customer into the cashier.\n *\n * Wix's Create Booking reference says to specify `selectedPaymentOption` when the\n * customer pays through a Wix eCommerce checkout, and that the call fails\n * otherwise. That failure has not been observed. The member paid flow in the\n * `himi-booking-time` worker calls `bookSlot` with no option and then checks\n * out, and a live run of it created the booking, the checkout and the order.\n * That run never completed payment, though, so whether a booking made without\n * an option confirms once its order is paid is still unverified, and Wix does\n * not document what an omitted option defaults to. Pass it when you know how\n * the customer means to pay.\n *\n * Any value you pass must be one the service offers. Wix exposes that on the\n * Services V2 service as `payment.options` (booleans `online`, `inPerson`,\n * `deposit`, `pricingPlan`). The `WixService` type here does not declare it\n * yet, so read it from the service response with a cast.\n * Do not hard-code `ONLINE`: a service can be set up for in-person payment\n * only, and then `ONLINE` is not one of its options.\n * Leave `options` out and the request is the one this function always sent. */",
7315
7315
  "decl": "export function bookSlot(\n slot: WixBookingSlotFull,\n firstName: string,\n lastName: string,\n email: string,\n options?: WixBookSlotOptions,\n): Promise<WixCreateBookingResponse>;",
7316
7316
  "http": "POST /bookings/v2/bookings"
7317
7317
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/himalaya-cli",
3
- "version": "0.1003.0",
3
+ "version": "0.1004.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": [
@@ -65,5 +65,5 @@
65
65
  "artifactId": "himalaya-cli"
66
66
  }
67
67
  },
68
- "falconPackageHash": "7d08a874d8457e2d68a6d9861b88e3002a6892614e9554b006e2f8da"
68
+ "falconPackageHash": "11e1d994023f0d1e2b935c467bbaffa63805b7b7888b09e3f5d280b1"
69
69
  }