@wix/himalaya-cli 0.1565.0 → 0.1566.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.
package/dist/cli.mjs CHANGED
@@ -138916,6 +138916,7 @@ __export(build_exports2, {
138916
138916
  BAKE_OFFLINE_BASE: () => BAKE_OFFLINE_BASE,
138917
138917
  KNOWN_ICON_NAMES: () => KNOWN_ICON_NAMES,
138918
138918
  SOURCEMAP_ON_DEMAND_PATH: () => SOURCEMAP_ON_DEMAND_PATH,
138919
+ SUPPORTED_COMPONENT_MIN_VERSION: () => SUPPORTED_COMPONENT_MIN_VERSION,
138919
138920
  bakeCatalogs: () => bakeCatalogs,
138920
138921
  bakeNeedsLiveOrigin: () => bakeNeedsLiveOrigin,
138921
138922
  bakeWidgets: () => bakeWidgets,
@@ -138928,6 +138929,7 @@ __export(build_exports2, {
138928
138929
  countingHostAnswers: () => countingHostAnswers,
138929
138930
  countingWixCall: () => countingWixCall,
138930
138931
  declaresHostProvidedIdentity: () => declaresHostProvidedIdentity,
138932
+ explicitMinRuntimeVersion: () => explicitMinRuntimeVersion,
138931
138933
  gitSha: () => gitSha,
138932
138934
  isMonorepoCheckout: () => isMonorepoCheckout,
138933
138935
  l10nLintOptions: () => l10nLintOptions,
@@ -138937,6 +138939,7 @@ __export(build_exports2, {
138937
138939
  listBuildableApps: () => listBuildableApps,
138938
138940
  liveRenderServices: () => liveRenderServices,
138939
138941
  maximumBinaryVersion: () => maximumBinaryVersion,
138942
+ navBundleMinBinaryVersion: () => navBundleMinBinaryVersion,
138940
138943
  navLogicBundleUrl: () => navLogicBundleUrl,
138941
138944
  pinBakedServerBaseUrl: () => pinBakedServerBaseUrl,
138942
138945
  pinnedReleaseFromEnv: () => pinnedReleaseFromEnv,
@@ -138947,6 +138950,7 @@ __export(build_exports2, {
138947
138950
  shellSha: () => shellSha,
138948
138951
  snapshotTokens: () => snapshotTokens,
138949
138952
  tokensSnapshotMessage: () => tokensSnapshotMessage,
138953
+ unsupportedComponentMinVersions: () => unsupportedComponentMinVersions,
138950
138954
  withPinnedRelease: () => withPinnedRelease,
138951
138955
  withStoreNavBundle: () => withStoreNavBundle,
138952
138956
  withoutDevOnlyAppConfig: () => withoutDevOnlyAppConfig
@@ -139217,6 +139221,22 @@ function collectComponentTypes(value2, into = /* @__PURE__ */ new Set()) {
139217
139221
  for (const child of Object.values(obj7)) collectComponentTypes(child, into);
139218
139222
  return into;
139219
139223
  }
139224
+ function unsupportedComponentMinVersions(value2, into = []) {
139225
+ if (!value2 || typeof value2 !== "object") return into;
139226
+ const obj7 = value2;
139227
+ const isComponent2 = typeof obj7.id === "string" && (obj7.body !== null && typeof obj7.body === "object" || typeof obj7.package === "string" && typeof obj7.type === "string");
139228
+ if (isComponent2 && typeof obj7.minVersion === "number" && obj7.minVersion > SUPPORTED_COMPONENT_MIN_VERSION) {
139229
+ into.push({ id: obj7.id, minVersion: obj7.minVersion });
139230
+ }
139231
+ for (const child of Object.values(obj7)) unsupportedComponentMinVersions(child, into);
139232
+ return into;
139233
+ }
139234
+ function explicitMinRuntimeVersion(raw) {
139235
+ if (raw === void 0 || raw.trim() === "") return null;
139236
+ const v2 = raw.trim();
139237
+ if (!/^\d+(?:\.\d+){0,2}$/.test(v2)) throw new Error(`invalid --min-runtime-version "${raw}": expected a numeric version like 1.7.0`);
139238
+ return v2;
139239
+ }
139220
139240
  function collectNativeComponents(value2, into) {
139221
139241
  if (!value2 || typeof value2 !== "object") return;
139222
139242
  const obj7 = value2;
@@ -139239,6 +139259,21 @@ function maximumBinaryVersion(current, candidate) {
139239
139259
  }
139240
139260
  return current;
139241
139261
  }
139262
+ function navBundleMinBinaryVersion(text12) {
139263
+ let cfg;
139264
+ try {
139265
+ cfg = JSON.parse(text12);
139266
+ } catch {
139267
+ return null;
139268
+ }
139269
+ let floor2 = null;
139270
+ for (const ref of [cfg?.app?.logicBundle, cfg?.navigation?.navLogicBundle, cfg?.navLogicBundle]) {
139271
+ if (!ref || typeof ref !== "object") continue;
139272
+ floor2 = maximumBinaryVersion(floor2, ref.minBinaryVersion);
139273
+ floor2 = maximumBinaryVersion(floor2, ref.min_binary_version);
139274
+ }
139275
+ return floor2;
139276
+ }
139242
139277
  async function withStoreNavBundle(text12, address) {
139243
139278
  let cfg;
139244
139279
  try {
@@ -139313,7 +139348,8 @@ async function buildRelease(app, dest, opts = {}) {
139313
139348
  return (await buildFromLoaded(loaded, dest, {
139314
139349
  ...tier5Dir ? { tier5Dir } : {},
139315
139350
  ...opts.fetchFace ? { fetchFace: opts.fetchFace } : {},
139316
- ...opts.keepDevOnlyAppConfig ? { keepDevOnlyAppConfig: true } : {}
139351
+ ...opts.keepDevOnlyAppConfig ? { keepDevOnlyAppConfig: true } : {},
139352
+ ...opts.minRuntimeVersion ? { minRuntimeVersion: opts.minRuntimeVersion } : {}
139317
139353
  })).manifest;
139318
139354
  }
139319
139355
  function countingWixCall(base) {
@@ -139444,6 +139480,13 @@ function readTokensForLint(preloaded) {
139444
139480
  }
139445
139481
  async function buildFromLoaded(preloaded, dest, opts = {}) {
139446
139482
  const app = preloaded.name;
139483
+ const assertComponentMinVersions = (screenId, descriptor) => {
139484
+ const bad2 = unsupportedComponentMinVersions(descriptor);
139485
+ if (!bad2.length) return;
139486
+ throw new Error(
139487
+ `screen ${screenId}: component minVersion is not enforced by any runtime (supported: ${SUPPORTED_COMPONENT_MIN_VERSION}) \u2014 ` + bad2.map((c2) => `${c2.id} asks for ${c2.minVersion}`).join(", ") + `. Declare what the release needs from the runtime as its floor instead: min_binary_version on the logic bundle, or release:build --min-runtime-version.`
139488
+ );
139489
+ };
139447
139490
  const tier5Dir = opts.tier5Dir ?? TIER5_DIR;
139448
139491
  const bundlePath = (name) => {
139449
139492
  const own6 = join28(tier5Dir, `${name}.bundle.js`);
@@ -139630,7 +139673,7 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
139630
139673
  if (input && typeof input === "object" && !Array.isArray(input)) bundleInputs.set(bundleName, input);
139631
139674
  };
139632
139675
  let handlers;
139633
- let minBinaryVersion = null;
139676
+ let minBinaryVersion = explicitMinRuntimeVersion(opts.minRuntimeVersion);
139634
139677
  const requiredCapabilities2 = new Set(config.capabilities ?? []);
139635
139678
  const capabilityUsage = {
139636
139679
  app: [...config.capabilities ?? []],
@@ -139869,6 +139912,7 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
139869
139912
  continue;
139870
139913
  }
139871
139914
  const descriptor = await res.json();
139915
+ assertComponentMinVersions(screenId, descriptor);
139872
139916
  collectNativeComponents(descriptor, requiredNativeComponents);
139873
139917
  let bundleName = null;
139874
139918
  const lb = logicBundleOf(descriptor);
@@ -140039,6 +140083,7 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
140039
140083
  continue;
140040
140084
  }
140041
140085
  const descriptor = await res.json();
140086
+ assertComponentMinVersions(screenId, descriptor);
140042
140087
  collectNativeComponents(descriptor, requiredNativeComponents);
140043
140088
  let bundleName = null;
140044
140089
  const lb = logicBundleOf(descriptor);
@@ -140164,6 +140209,7 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
140164
140209
  });
140165
140210
  await store.putAppConfig(app, text12);
140166
140211
  appConfigHash = sha2563(text12);
140212
+ minBinaryVersion = maximumBinaryVersion(minBinaryVersion, navBundleMinBinaryVersion(text12));
140167
140213
  if (collect2?.descriptors) for (const id of linkableScreenIdsFrom(text12)) linkable.add(id);
140168
140214
  }
140169
140215
  const ahRes = await devFetch(`${base}/v1/action-handlers/${encodeURIComponent(app)}`, { headers: { "X-Himalaya-App": app } });
@@ -140369,7 +140415,7 @@ function declaresHostProvidedIdentity(auth) {
140369
140415
  const config = auth?.config;
140370
140416
  return typeof config === "object" && config !== null && config.hostProvided === true;
140371
140417
  }
140372
- var BAKE_OFFLINE_BASE, KNOWN_ICON_NAMES, KNOWN_ICONS, APP_SERVICES_LINT_SCREEN, REPO_ROOT5, TIER5_DIR, requireFromHere, SOURCEMAP_ON_DEMAND_PATH, BAKED_SERVER_BASE_URL, SETTLE_TURNS;
140418
+ var BAKE_OFFLINE_BASE, KNOWN_ICON_NAMES, KNOWN_ICONS, APP_SERVICES_LINT_SCREEN, REPO_ROOT5, TIER5_DIR, requireFromHere, SOURCEMAP_ON_DEMAND_PATH, BAKED_SERVER_BASE_URL, SUPPORTED_COMPONENT_MIN_VERSION, SETTLE_TURNS;
140373
140419
  var init_build3 = __esm({
140374
140420
  "../release-build/build.ts"() {
140375
140421
  init_site_view_metadata();
@@ -140413,6 +140459,7 @@ var init_build3 = __esm({
140413
140459
  requireFromHere = createRequire3(import.meta.url);
140414
140460
  SOURCEMAP_ON_DEMAND_PATH = join28(REPO_ROOT5, "core/dev-server/tier5-bundles-src/sourcemap-on-demand.mjs");
140415
140461
  BAKED_SERVER_BASE_URL = "http://baked.himalaya.invalid";
140462
+ SUPPORTED_COMPONENT_MIN_VERSION = 1;
140416
140463
  SETTLE_TURNS = 20;
140417
140464
  }
140418
140465
  });
@@ -103,9 +103,12 @@ support." (Wix refused it, so it stays held), `restoreUpToDateText` "Your purcha
103
103
  "Couldn't reach the App Store. Try again." (also shown when the host cannot sell). A message belongs
104
104
  to the session it ran for: after a sign-in or sign-out the control shows none.
105
105
 
106
- **Not covered yet.** After a reinstall the native managed ledger is gone and its tenant filter hides
107
- the unfinished transactions, so restore finds none of them until that is fixed; it still re-reads
108
- access. On iOS `AppStore.sync()` errors are swallowed natively, so a failed sync currently reads as
106
+ **Reinstall.** A reinstall deletes the native managed ledger. In an app's own bound build, an
107
+ unfinished transaction under the app's bundle id is still the app's own, so restore resubmits it.
108
+ In Player or an explicit host, the tenant filter hides those transactions and restore finds none;
109
+ it still re-reads access ([transaction contract](iap-transaction-contract.md#activation-limits-and-compatibility)).
110
+
111
+ **Not covered yet.** On iOS `AppStore.sync()` errors are swallowed natively, so a failed sync currently reads as
109
112
  up to date rather than the error copy.
110
113
 
111
114
  ## Migrating an existing app copy
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/himalaya-cli",
3
- "version": "0.1565.0",
3
+ "version": "0.1566.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": [
@@ -71,5 +71,5 @@
71
71
  "artifactId": "himalaya-cli"
72
72
  }
73
73
  },
74
- "falconPackageHash": "16b866eb8f71ec4f6b11fd676bbccd286b18aa734527d15c2f337eae"
74
+ "falconPackageHash": "59d9f7c34824b26777859c276927fa1bde614c8f64c26db04c10bfb3"
75
75
  }
@@ -234,6 +234,9 @@ let signInPresentationRequested: "" | "sheet" | "full_screen" = "";
234
234
  let signInReturnTabRequested = "";
235
235
  let standaloneRequested = false;
236
236
  let pollIdentityRequested = false;
237
+ // iOS can start the arg-less boot load after the appear refresh that asks for the row; the
238
+ // newer boot probe must not land, or be kept, without it.
239
+ let restorePurchasesRequested = false;
237
240
  // Bumped by every mode switch, so a dropped probe never puts back a mode the visitor left.
238
241
  let modeSwitches = 0;
239
242
 
@@ -246,6 +249,7 @@ function appearBase(): Partial<S> {
246
249
  signInScreen: signInScreenRequested,
247
250
  standalone: standaloneRequested,
248
251
  pollIdentity: pollIdentityRequested,
252
+ restorePurchases: restorePurchasesRequested,
249
253
  ...(pollIdentityRequested ? { _poll: { ...IDENTITY_POLL } } : {}),
250
254
  };
251
255
  }
@@ -268,6 +272,7 @@ function appearConfig(s: S, arg: unknown): AppearConfig {
268
272
  // Only the sign-in screen's route carries `mode`, so it marks the form from the boot load on.
269
273
  standaloneRequested ||= s.standalone || appearing || (!routeInputConsumed && !!routeInput().mode);
270
274
  pollIdentityRequested ||= s.pollIdentity || strField(arg, "pollIdentity") === "1";
275
+ restorePurchasesRequested ||= s.restorePurchases || strField(arg, "restorePurchases") === "1";
271
276
  // The form opens on the mode its route asked for each time it appears, sign-in by
272
277
  // default: a pooled worker reopened with the same params must not open on the mode the
273
278
  // last visit toggled to.
@@ -652,8 +657,7 @@ async function probe(s: S, arg?: unknown, config: AppearConfig = appearConfig(s,
652
657
  // Not the boot load: it never carries the copy, and the appear refresh that does is coming.
653
658
  if (strField(arg, "boot") !== "1") headingReady = true;
654
659
  const memberAccountLinks = memberAccountLinksRequested;
655
- const restorePurchases = s.restorePurchases || strField(arg, "restorePurchases") === "1";
656
- if (restorePurchases) enterCommerce();
660
+ if (restorePurchasesRequested) enterCommerce();
657
661
  const routes = memberAccountLinks ? await memberAccountRoutes() : { addresses: false, wallet: false };
658
662
  let activityRows: AccountActivityRow[] = [];
659
663
  if (profile.isMember) {
@@ -681,7 +685,6 @@ async function probe(s: S, arg?: unknown, config: AppearConfig = appearConfig(s,
681
685
  email: profile.memberEmail || s.email,
682
686
  message: "",
683
687
  memberAccountLinks,
684
- restorePurchases,
685
688
  ...appearBase(),
686
689
  ...config.mode,
687
690
  ...config.visit,
@@ -234,6 +234,9 @@ let signInPresentationRequested: "" | "sheet" | "full_screen" = "";
234
234
  let signInReturnTabRequested = "";
235
235
  let standaloneRequested = false;
236
236
  let pollIdentityRequested = false;
237
+ // iOS can start the arg-less boot load after the appear refresh that asks for the row; the
238
+ // newer boot probe must not land, or be kept, without it.
239
+ let restorePurchasesRequested = false;
237
240
  // Bumped by every mode switch, so a dropped probe never puts back a mode the visitor left.
238
241
  let modeSwitches = 0;
239
242
 
@@ -246,6 +249,7 @@ function appearBase(): Partial<S> {
246
249
  signInScreen: signInScreenRequested,
247
250
  standalone: standaloneRequested,
248
251
  pollIdentity: pollIdentityRequested,
252
+ restorePurchases: restorePurchasesRequested,
249
253
  ...(pollIdentityRequested ? { _poll: { ...IDENTITY_POLL } } : {}),
250
254
  };
251
255
  }
@@ -268,6 +272,7 @@ function appearConfig(s: S, arg: unknown): AppearConfig {
268
272
  // Only the sign-in screen's route carries `mode`, so it marks the form from the boot load on.
269
273
  standaloneRequested ||= s.standalone || appearing || (!routeInputConsumed && !!routeInput().mode);
270
274
  pollIdentityRequested ||= s.pollIdentity || strField(arg, "pollIdentity") === "1";
275
+ restorePurchasesRequested ||= s.restorePurchases || strField(arg, "restorePurchases") === "1";
271
276
  // The form opens on the mode its route asked for each time it appears, sign-in by
272
277
  // default: a pooled worker reopened with the same params must not open on the mode the
273
278
  // last visit toggled to.
@@ -652,8 +657,7 @@ async function probe(s: S, arg?: unknown, config: AppearConfig = appearConfig(s,
652
657
  // Not the boot load: it never carries the copy, and the appear refresh that does is coming.
653
658
  if (strField(arg, "boot") !== "1") headingReady = true;
654
659
  const memberAccountLinks = memberAccountLinksRequested;
655
- const restorePurchases = s.restorePurchases || strField(arg, "restorePurchases") === "1";
656
- if (restorePurchases) enterCommerce();
660
+ if (restorePurchasesRequested) enterCommerce();
657
661
  const routes = memberAccountLinks ? await memberAccountRoutes() : { addresses: false, wallet: false };
658
662
  let activityRows: AccountActivityRow[] = [];
659
663
  if (profile.isMember) {
@@ -681,7 +685,6 @@ async function probe(s: S, arg?: unknown, config: AppearConfig = appearConfig(s,
681
685
  email: profile.memberEmail || s.email,
682
686
  message: "",
683
687
  memberAccountLinks,
684
- restorePurchases,
685
688
  ...appearBase(),
686
689
  ...config.mode,
687
690
  ...config.visit,
@@ -234,6 +234,9 @@ let signInPresentationRequested: "" | "sheet" | "full_screen" = "";
234
234
  let signInReturnTabRequested = "";
235
235
  let standaloneRequested = false;
236
236
  let pollIdentityRequested = false;
237
+ // iOS can start the arg-less boot load after the appear refresh that asks for the row; the
238
+ // newer boot probe must not land, or be kept, without it.
239
+ let restorePurchasesRequested = false;
237
240
  // Bumped by every mode switch, so a dropped probe never puts back a mode the visitor left.
238
241
  let modeSwitches = 0;
239
242
 
@@ -246,6 +249,7 @@ function appearBase(): Partial<S> {
246
249
  signInScreen: signInScreenRequested,
247
250
  standalone: standaloneRequested,
248
251
  pollIdentity: pollIdentityRequested,
252
+ restorePurchases: restorePurchasesRequested,
249
253
  ...(pollIdentityRequested ? { _poll: { ...IDENTITY_POLL } } : {}),
250
254
  };
251
255
  }
@@ -268,6 +272,7 @@ function appearConfig(s: S, arg: unknown): AppearConfig {
268
272
  // Only the sign-in screen's route carries `mode`, so it marks the form from the boot load on.
269
273
  standaloneRequested ||= s.standalone || appearing || (!routeInputConsumed && !!routeInput().mode);
270
274
  pollIdentityRequested ||= s.pollIdentity || strField(arg, "pollIdentity") === "1";
275
+ restorePurchasesRequested ||= s.restorePurchases || strField(arg, "restorePurchases") === "1";
271
276
  // The form opens on the mode its route asked for each time it appears, sign-in by
272
277
  // default: a pooled worker reopened with the same params must not open on the mode the
273
278
  // last visit toggled to.
@@ -652,8 +657,7 @@ async function probe(s: S, arg?: unknown, config: AppearConfig = appearConfig(s,
652
657
  // Not the boot load: it never carries the copy, and the appear refresh that does is coming.
653
658
  if (strField(arg, "boot") !== "1") headingReady = true;
654
659
  const memberAccountLinks = memberAccountLinksRequested;
655
- const restorePurchases = s.restorePurchases || strField(arg, "restorePurchases") === "1";
656
- if (restorePurchases) enterCommerce();
660
+ if (restorePurchasesRequested) enterCommerce();
657
661
  const routes = memberAccountLinks ? await memberAccountRoutes() : { addresses: false, wallet: false };
658
662
  let activityRows: AccountActivityRow[] = [];
659
663
  if (profile.isMember) {
@@ -681,7 +685,6 @@ async function probe(s: S, arg?: unknown, config: AppearConfig = appearConfig(s,
681
685
  email: profile.memberEmail || s.email,
682
686
  message: "",
683
687
  memberAccountLinks,
684
- restorePurchases,
685
688
  ...appearBase(),
686
689
  ...config.mode,
687
690
  ...config.visit,
@@ -234,6 +234,9 @@ let signInPresentationRequested: "" | "sheet" | "full_screen" = "";
234
234
  let signInReturnTabRequested = "";
235
235
  let standaloneRequested = false;
236
236
  let pollIdentityRequested = false;
237
+ // iOS can start the arg-less boot load after the appear refresh that asks for the row; the
238
+ // newer boot probe must not land, or be kept, without it.
239
+ let restorePurchasesRequested = false;
237
240
  // Bumped by every mode switch, so a dropped probe never puts back a mode the visitor left.
238
241
  let modeSwitches = 0;
239
242
 
@@ -246,6 +249,7 @@ function appearBase(): Partial<S> {
246
249
  signInScreen: signInScreenRequested,
247
250
  standalone: standaloneRequested,
248
251
  pollIdentity: pollIdentityRequested,
252
+ restorePurchases: restorePurchasesRequested,
249
253
  ...(pollIdentityRequested ? { _poll: { ...IDENTITY_POLL } } : {}),
250
254
  };
251
255
  }
@@ -268,6 +272,7 @@ function appearConfig(s: S, arg: unknown): AppearConfig {
268
272
  // Only the sign-in screen's route carries `mode`, so it marks the form from the boot load on.
269
273
  standaloneRequested ||= s.standalone || appearing || (!routeInputConsumed && !!routeInput().mode);
270
274
  pollIdentityRequested ||= s.pollIdentity || strField(arg, "pollIdentity") === "1";
275
+ restorePurchasesRequested ||= s.restorePurchases || strField(arg, "restorePurchases") === "1";
271
276
  // The form opens on the mode its route asked for each time it appears, sign-in by
272
277
  // default: a pooled worker reopened with the same params must not open on the mode the
273
278
  // last visit toggled to.
@@ -652,8 +657,7 @@ async function probe(s: S, arg?: unknown, config: AppearConfig = appearConfig(s,
652
657
  // Not the boot load: it never carries the copy, and the appear refresh that does is coming.
653
658
  if (strField(arg, "boot") !== "1") headingReady = true;
654
659
  const memberAccountLinks = memberAccountLinksRequested;
655
- const restorePurchases = s.restorePurchases || strField(arg, "restorePurchases") === "1";
656
- if (restorePurchases) enterCommerce();
660
+ if (restorePurchasesRequested) enterCommerce();
657
661
  const routes = memberAccountLinks ? await memberAccountRoutes() : { addresses: false, wallet: false };
658
662
  let activityRows: AccountActivityRow[] = [];
659
663
  if (profile.isMember) {
@@ -681,7 +685,6 @@ async function probe(s: S, arg?: unknown, config: AppearConfig = appearConfig(s,
681
685
  email: profile.memberEmail || s.email,
682
686
  message: "",
683
687
  memberAccountLinks,
684
- restorePurchases,
685
688
  ...appearBase(),
686
689
  ...config.mode,
687
690
  ...config.visit,
@@ -234,6 +234,9 @@ let signInPresentationRequested: "" | "sheet" | "full_screen" = "";
234
234
  let signInReturnTabRequested = "";
235
235
  let standaloneRequested = false;
236
236
  let pollIdentityRequested = false;
237
+ // iOS can start the arg-less boot load after the appear refresh that asks for the row; the
238
+ // newer boot probe must not land, or be kept, without it.
239
+ let restorePurchasesRequested = false;
237
240
  // Bumped by every mode switch, so a dropped probe never puts back a mode the visitor left.
238
241
  let modeSwitches = 0;
239
242
 
@@ -246,6 +249,7 @@ function appearBase(): Partial<S> {
246
249
  signInScreen: signInScreenRequested,
247
250
  standalone: standaloneRequested,
248
251
  pollIdentity: pollIdentityRequested,
252
+ restorePurchases: restorePurchasesRequested,
249
253
  ...(pollIdentityRequested ? { _poll: { ...IDENTITY_POLL } } : {}),
250
254
  };
251
255
  }
@@ -268,6 +272,7 @@ function appearConfig(s: S, arg: unknown): AppearConfig {
268
272
  // Only the sign-in screen's route carries `mode`, so it marks the form from the boot load on.
269
273
  standaloneRequested ||= s.standalone || appearing || (!routeInputConsumed && !!routeInput().mode);
270
274
  pollIdentityRequested ||= s.pollIdentity || strField(arg, "pollIdentity") === "1";
275
+ restorePurchasesRequested ||= s.restorePurchases || strField(arg, "restorePurchases") === "1";
271
276
  // The form opens on the mode its route asked for each time it appears, sign-in by
272
277
  // default: a pooled worker reopened with the same params must not open on the mode the
273
278
  // last visit toggled to.
@@ -652,8 +657,7 @@ async function probe(s: S, arg?: unknown, config: AppearConfig = appearConfig(s,
652
657
  // Not the boot load: it never carries the copy, and the appear refresh that does is coming.
653
658
  if (strField(arg, "boot") !== "1") headingReady = true;
654
659
  const memberAccountLinks = memberAccountLinksRequested;
655
- const restorePurchases = s.restorePurchases || strField(arg, "restorePurchases") === "1";
656
- if (restorePurchases) enterCommerce();
660
+ if (restorePurchasesRequested) enterCommerce();
657
661
  const routes = memberAccountLinks ? await memberAccountRoutes() : { addresses: false, wallet: false };
658
662
  let activityRows: AccountActivityRow[] = [];
659
663
  if (profile.isMember) {
@@ -681,7 +685,6 @@ async function probe(s: S, arg?: unknown, config: AppearConfig = appearConfig(s,
681
685
  email: profile.memberEmail || s.email,
682
686
  message: "",
683
687
  memberAccountLinks,
684
- restorePurchases,
685
688
  ...appearBase(),
686
689
  ...config.mode,
687
690
  ...config.visit,
@@ -234,6 +234,9 @@ let signInPresentationRequested: "" | "sheet" | "full_screen" = "";
234
234
  let signInReturnTabRequested = "";
235
235
  let standaloneRequested = false;
236
236
  let pollIdentityRequested = false;
237
+ // iOS can start the arg-less boot load after the appear refresh that asks for the row; the
238
+ // newer boot probe must not land, or be kept, without it.
239
+ let restorePurchasesRequested = false;
237
240
  // Bumped by every mode switch, so a dropped probe never puts back a mode the visitor left.
238
241
  let modeSwitches = 0;
239
242
 
@@ -246,6 +249,7 @@ function appearBase(): Partial<S> {
246
249
  signInScreen: signInScreenRequested,
247
250
  standalone: standaloneRequested,
248
251
  pollIdentity: pollIdentityRequested,
252
+ restorePurchases: restorePurchasesRequested,
249
253
  ...(pollIdentityRequested ? { _poll: { ...IDENTITY_POLL } } : {}),
250
254
  };
251
255
  }
@@ -268,6 +272,7 @@ function appearConfig(s: S, arg: unknown): AppearConfig {
268
272
  // Only the sign-in screen's route carries `mode`, so it marks the form from the boot load on.
269
273
  standaloneRequested ||= s.standalone || appearing || (!routeInputConsumed && !!routeInput().mode);
270
274
  pollIdentityRequested ||= s.pollIdentity || strField(arg, "pollIdentity") === "1";
275
+ restorePurchasesRequested ||= s.restorePurchases || strField(arg, "restorePurchases") === "1";
271
276
  // The form opens on the mode its route asked for each time it appears, sign-in by
272
277
  // default: a pooled worker reopened with the same params must not open on the mode the
273
278
  // last visit toggled to.
@@ -652,8 +657,7 @@ async function probe(s: S, arg?: unknown, config: AppearConfig = appearConfig(s,
652
657
  // Not the boot load: it never carries the copy, and the appear refresh that does is coming.
653
658
  if (strField(arg, "boot") !== "1") headingReady = true;
654
659
  const memberAccountLinks = memberAccountLinksRequested;
655
- const restorePurchases = s.restorePurchases || strField(arg, "restorePurchases") === "1";
656
- if (restorePurchases) enterCommerce();
660
+ if (restorePurchasesRequested) enterCommerce();
657
661
  const routes = memberAccountLinks ? await memberAccountRoutes() : { addresses: false, wallet: false };
658
662
  let activityRows: AccountActivityRow[] = [];
659
663
  if (profile.isMember) {
@@ -681,7 +685,6 @@ async function probe(s: S, arg?: unknown, config: AppearConfig = appearConfig(s,
681
685
  email: profile.memberEmail || s.email,
682
686
  message: "",
683
687
  memberAccountLinks,
684
- restorePurchases,
685
688
  ...appearBase(),
686
689
  ...config.mode,
687
690
  ...config.visit,
@@ -234,6 +234,9 @@ let signInPresentationRequested: "" | "sheet" | "full_screen" = "";
234
234
  let signInReturnTabRequested = "";
235
235
  let standaloneRequested = false;
236
236
  let pollIdentityRequested = false;
237
+ // iOS can start the arg-less boot load after the appear refresh that asks for the row; the
238
+ // newer boot probe must not land, or be kept, without it.
239
+ let restorePurchasesRequested = false;
237
240
  // Bumped by every mode switch, so a dropped probe never puts back a mode the visitor left.
238
241
  let modeSwitches = 0;
239
242
 
@@ -246,6 +249,7 @@ function appearBase(): Partial<S> {
246
249
  signInScreen: signInScreenRequested,
247
250
  standalone: standaloneRequested,
248
251
  pollIdentity: pollIdentityRequested,
252
+ restorePurchases: restorePurchasesRequested,
249
253
  ...(pollIdentityRequested ? { _poll: { ...IDENTITY_POLL } } : {}),
250
254
  };
251
255
  }
@@ -268,6 +272,7 @@ function appearConfig(s: S, arg: unknown): AppearConfig {
268
272
  // Only the sign-in screen's route carries `mode`, so it marks the form from the boot load on.
269
273
  standaloneRequested ||= s.standalone || appearing || (!routeInputConsumed && !!routeInput().mode);
270
274
  pollIdentityRequested ||= s.pollIdentity || strField(arg, "pollIdentity") === "1";
275
+ restorePurchasesRequested ||= s.restorePurchases || strField(arg, "restorePurchases") === "1";
271
276
  // The form opens on the mode its route asked for each time it appears, sign-in by
272
277
  // default: a pooled worker reopened with the same params must not open on the mode the
273
278
  // last visit toggled to.
@@ -652,8 +657,7 @@ async function probe(s: S, arg?: unknown, config: AppearConfig = appearConfig(s,
652
657
  // Not the boot load: it never carries the copy, and the appear refresh that does is coming.
653
658
  if (strField(arg, "boot") !== "1") headingReady = true;
654
659
  const memberAccountLinks = memberAccountLinksRequested;
655
- const restorePurchases = s.restorePurchases || strField(arg, "restorePurchases") === "1";
656
- if (restorePurchases) enterCommerce();
660
+ if (restorePurchasesRequested) enterCommerce();
657
661
  const routes = memberAccountLinks ? await memberAccountRoutes() : { addresses: false, wallet: false };
658
662
  let activityRows: AccountActivityRow[] = [];
659
663
  if (profile.isMember) {
@@ -681,7 +685,6 @@ async function probe(s: S, arg?: unknown, config: AppearConfig = appearConfig(s,
681
685
  email: profile.memberEmail || s.email,
682
686
  message: "",
683
687
  memberAccountLinks,
684
- restorePurchases,
685
688
  ...appearBase(),
686
689
  ...config.mode,
687
690
  ...config.visit,
@@ -234,6 +234,9 @@ let signInPresentationRequested: "" | "sheet" | "full_screen" = "";
234
234
  let signInReturnTabRequested = "";
235
235
  let standaloneRequested = false;
236
236
  let pollIdentityRequested = false;
237
+ // iOS can start the arg-less boot load after the appear refresh that asks for the row; the
238
+ // newer boot probe must not land, or be kept, without it.
239
+ let restorePurchasesRequested = false;
237
240
  // Bumped by every mode switch, so a dropped probe never puts back a mode the visitor left.
238
241
  let modeSwitches = 0;
239
242
 
@@ -246,6 +249,7 @@ function appearBase(): Partial<S> {
246
249
  signInScreen: signInScreenRequested,
247
250
  standalone: standaloneRequested,
248
251
  pollIdentity: pollIdentityRequested,
252
+ restorePurchases: restorePurchasesRequested,
249
253
  ...(pollIdentityRequested ? { _poll: { ...IDENTITY_POLL } } : {}),
250
254
  };
251
255
  }
@@ -268,6 +272,7 @@ function appearConfig(s: S, arg: unknown): AppearConfig {
268
272
  // Only the sign-in screen's route carries `mode`, so it marks the form from the boot load on.
269
273
  standaloneRequested ||= s.standalone || appearing || (!routeInputConsumed && !!routeInput().mode);
270
274
  pollIdentityRequested ||= s.pollIdentity || strField(arg, "pollIdentity") === "1";
275
+ restorePurchasesRequested ||= s.restorePurchases || strField(arg, "restorePurchases") === "1";
271
276
  // The form opens on the mode its route asked for each time it appears, sign-in by
272
277
  // default: a pooled worker reopened with the same params must not open on the mode the
273
278
  // last visit toggled to.
@@ -652,8 +657,7 @@ async function probe(s: S, arg?: unknown, config: AppearConfig = appearConfig(s,
652
657
  // Not the boot load: it never carries the copy, and the appear refresh that does is coming.
653
658
  if (strField(arg, "boot") !== "1") headingReady = true;
654
659
  const memberAccountLinks = memberAccountLinksRequested;
655
- const restorePurchases = s.restorePurchases || strField(arg, "restorePurchases") === "1";
656
- if (restorePurchases) enterCommerce();
660
+ if (restorePurchasesRequested) enterCommerce();
657
661
  const routes = memberAccountLinks ? await memberAccountRoutes() : { addresses: false, wallet: false };
658
662
  let activityRows: AccountActivityRow[] = [];
659
663
  if (profile.isMember) {
@@ -681,7 +685,6 @@ async function probe(s: S, arg?: unknown, config: AppearConfig = appearConfig(s,
681
685
  email: profile.memberEmail || s.email,
682
686
  message: "",
683
687
  memberAccountLinks,
684
- restorePurchases,
685
688
  ...appearBase(),
686
689
  ...config.mode,
687
690
  ...config.visit,
@@ -234,6 +234,9 @@ let signInPresentationRequested: "" | "sheet" | "full_screen" = "";
234
234
  let signInReturnTabRequested = "";
235
235
  let standaloneRequested = false;
236
236
  let pollIdentityRequested = false;
237
+ // iOS can start the arg-less boot load after the appear refresh that asks for the row; the
238
+ // newer boot probe must not land, or be kept, without it.
239
+ let restorePurchasesRequested = false;
237
240
  // Bumped by every mode switch, so a dropped probe never puts back a mode the visitor left.
238
241
  let modeSwitches = 0;
239
242
 
@@ -246,6 +249,7 @@ function appearBase(): Partial<S> {
246
249
  signInScreen: signInScreenRequested,
247
250
  standalone: standaloneRequested,
248
251
  pollIdentity: pollIdentityRequested,
252
+ restorePurchases: restorePurchasesRequested,
249
253
  ...(pollIdentityRequested ? { _poll: { ...IDENTITY_POLL } } : {}),
250
254
  };
251
255
  }
@@ -268,6 +272,7 @@ function appearConfig(s: S, arg: unknown): AppearConfig {
268
272
  // Only the sign-in screen's route carries `mode`, so it marks the form from the boot load on.
269
273
  standaloneRequested ||= s.standalone || appearing || (!routeInputConsumed && !!routeInput().mode);
270
274
  pollIdentityRequested ||= s.pollIdentity || strField(arg, "pollIdentity") === "1";
275
+ restorePurchasesRequested ||= s.restorePurchases || strField(arg, "restorePurchases") === "1";
271
276
  // The form opens on the mode its route asked for each time it appears, sign-in by
272
277
  // default: a pooled worker reopened with the same params must not open on the mode the
273
278
  // last visit toggled to.
@@ -652,8 +657,7 @@ async function probe(s: S, arg?: unknown, config: AppearConfig = appearConfig(s,
652
657
  // Not the boot load: it never carries the copy, and the appear refresh that does is coming.
653
658
  if (strField(arg, "boot") !== "1") headingReady = true;
654
659
  const memberAccountLinks = memberAccountLinksRequested;
655
- const restorePurchases = s.restorePurchases || strField(arg, "restorePurchases") === "1";
656
- if (restorePurchases) enterCommerce();
660
+ if (restorePurchasesRequested) enterCommerce();
657
661
  const routes = memberAccountLinks ? await memberAccountRoutes() : { addresses: false, wallet: false };
658
662
  let activityRows: AccountActivityRow[] = [];
659
663
  if (profile.isMember) {
@@ -681,7 +685,6 @@ async function probe(s: S, arg?: unknown, config: AppearConfig = appearConfig(s,
681
685
  email: profile.memberEmail || s.email,
682
686
  message: "",
683
687
  memberAccountLinks,
684
- restorePurchases,
685
688
  ...appearBase(),
686
689
  ...config.mode,
687
690
  ...config.visit,
@@ -234,6 +234,9 @@ let signInPresentationRequested: "" | "sheet" | "full_screen" = "";
234
234
  let signInReturnTabRequested = "";
235
235
  let standaloneRequested = false;
236
236
  let pollIdentityRequested = false;
237
+ // iOS can start the arg-less boot load after the appear refresh that asks for the row; the
238
+ // newer boot probe must not land, or be kept, without it.
239
+ let restorePurchasesRequested = false;
237
240
  // Bumped by every mode switch, so a dropped probe never puts back a mode the visitor left.
238
241
  let modeSwitches = 0;
239
242
 
@@ -246,6 +249,7 @@ function appearBase(): Partial<S> {
246
249
  signInScreen: signInScreenRequested,
247
250
  standalone: standaloneRequested,
248
251
  pollIdentity: pollIdentityRequested,
252
+ restorePurchases: restorePurchasesRequested,
249
253
  ...(pollIdentityRequested ? { _poll: { ...IDENTITY_POLL } } : {}),
250
254
  };
251
255
  }
@@ -268,6 +272,7 @@ function appearConfig(s: S, arg: unknown): AppearConfig {
268
272
  // Only the sign-in screen's route carries `mode`, so it marks the form from the boot load on.
269
273
  standaloneRequested ||= s.standalone || appearing || (!routeInputConsumed && !!routeInput().mode);
270
274
  pollIdentityRequested ||= s.pollIdentity || strField(arg, "pollIdentity") === "1";
275
+ restorePurchasesRequested ||= s.restorePurchases || strField(arg, "restorePurchases") === "1";
271
276
  // The form opens on the mode its route asked for each time it appears, sign-in by
272
277
  // default: a pooled worker reopened with the same params must not open on the mode the
273
278
  // last visit toggled to.
@@ -652,8 +657,7 @@ async function probe(s: S, arg?: unknown, config: AppearConfig = appearConfig(s,
652
657
  // Not the boot load: it never carries the copy, and the appear refresh that does is coming.
653
658
  if (strField(arg, "boot") !== "1") headingReady = true;
654
659
  const memberAccountLinks = memberAccountLinksRequested;
655
- const restorePurchases = s.restorePurchases || strField(arg, "restorePurchases") === "1";
656
- if (restorePurchases) enterCommerce();
660
+ if (restorePurchasesRequested) enterCommerce();
657
661
  const routes = memberAccountLinks ? await memberAccountRoutes() : { addresses: false, wallet: false };
658
662
  let activityRows: AccountActivityRow[] = [];
659
663
  if (profile.isMember) {
@@ -681,7 +685,6 @@ async function probe(s: S, arg?: unknown, config: AppearConfig = appearConfig(s,
681
685
  email: profile.memberEmail || s.email,
682
686
  message: "",
683
687
  memberAccountLinks,
684
- restorePurchases,
685
688
  ...appearBase(),
686
689
  ...config.mode,
687
690
  ...config.visit,
@@ -234,6 +234,9 @@ let signInPresentationRequested: "" | "sheet" | "full_screen" = "";
234
234
  let signInReturnTabRequested = "";
235
235
  let standaloneRequested = false;
236
236
  let pollIdentityRequested = false;
237
+ // iOS can start the arg-less boot load after the appear refresh that asks for the row; the
238
+ // newer boot probe must not land, or be kept, without it.
239
+ let restorePurchasesRequested = false;
237
240
  // Bumped by every mode switch, so a dropped probe never puts back a mode the visitor left.
238
241
  let modeSwitches = 0;
239
242
 
@@ -246,6 +249,7 @@ function appearBase(): Partial<S> {
246
249
  signInScreen: signInScreenRequested,
247
250
  standalone: standaloneRequested,
248
251
  pollIdentity: pollIdentityRequested,
252
+ restorePurchases: restorePurchasesRequested,
249
253
  ...(pollIdentityRequested ? { _poll: { ...IDENTITY_POLL } } : {}),
250
254
  };
251
255
  }
@@ -268,6 +272,7 @@ function appearConfig(s: S, arg: unknown): AppearConfig {
268
272
  // Only the sign-in screen's route carries `mode`, so it marks the form from the boot load on.
269
273
  standaloneRequested ||= s.standalone || appearing || (!routeInputConsumed && !!routeInput().mode);
270
274
  pollIdentityRequested ||= s.pollIdentity || strField(arg, "pollIdentity") === "1";
275
+ restorePurchasesRequested ||= s.restorePurchases || strField(arg, "restorePurchases") === "1";
271
276
  // The form opens on the mode its route asked for each time it appears, sign-in by
272
277
  // default: a pooled worker reopened with the same params must not open on the mode the
273
278
  // last visit toggled to.
@@ -652,8 +657,7 @@ async function probe(s: S, arg?: unknown, config: AppearConfig = appearConfig(s,
652
657
  // Not the boot load: it never carries the copy, and the appear refresh that does is coming.
653
658
  if (strField(arg, "boot") !== "1") headingReady = true;
654
659
  const memberAccountLinks = memberAccountLinksRequested;
655
- const restorePurchases = s.restorePurchases || strField(arg, "restorePurchases") === "1";
656
- if (restorePurchases) enterCommerce();
660
+ if (restorePurchasesRequested) enterCommerce();
657
661
  const routes = memberAccountLinks ? await memberAccountRoutes() : { addresses: false, wallet: false };
658
662
  let activityRows: AccountActivityRow[] = [];
659
663
  if (profile.isMember) {
@@ -681,7 +685,6 @@ async function probe(s: S, arg?: unknown, config: AppearConfig = appearConfig(s,
681
685
  email: profile.memberEmail || s.email,
682
686
  message: "",
683
687
  memberAccountLinks,
684
- restorePurchases,
685
688
  ...appearBase(),
686
689
  ...config.mode,
687
690
  ...config.visit,