@wix/himalaya-cli 0.1585.0 → 0.1587.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.
@@ -31,6 +31,27 @@ export interface AppInfo {
31
31
  * False on older binaries and web. Does not grant member access or guarantee
32
32
  * the site's invitation permission; the server still authorizes each request. */
33
33
  oneAppInviteAvailable?: boolean;
34
+ /** Hardware model id — iOS `utsname` machine (e.g. "iPhone16,2"; a simulator reports the
35
+ * simulated model), Android `Build.MANUFACTURER Build.MODEL` (e.g. "Google Pixel 8"), web the
36
+ * user-agent's platform. "" when unknown. Never a device identifier. */
37
+ deviceModel: string;
38
+ /** Marketing name where the host knows it cheaply (e.g. "iPhone 15 Pro", "Pixel 8"), else "". */
39
+ deviceName: string;
40
+ /** OS version (e.g. "18.2", "14"). "" when unknown. */
41
+ osVersion: string;
42
+ /** The app's EFFECTIVE locale — the tag its LocaleStore renders in (which an in-app
43
+ * `locale.set` or a pinned app default can make differ from the system's). "" when unknown. */
44
+ locale: string;
45
+ /** Screen size in points (iOS/macOS) / dp (Android) / CSS px (web), and the pixel ratio.
46
+ * Zeros when unknown. */
47
+ screen: {
48
+ width: number;
49
+ height: number;
50
+ scale: number;
51
+ };
52
+ /** True when the signed-in user matches the app's configured internal rule
53
+ * (`appInfo.internal.emailDomains`). False when signed out or unconfigured. */
54
+ isInternal: boolean;
34
55
  }
35
56
  /**
36
57
  * Read this app's build + OTA metadata from the host.
@@ -44,11 +44,32 @@ export interface SnackbarOptions {
44
44
  tone?: SnackbarTone;
45
45
  /** Auto-dismiss delay in ms, on every host. Default 3000; 0 or less also means 3000. */
46
46
  durationMs?: number;
47
+ /** One action button ("Undo", "View"). Tapping it runs `actionId` with `args`, then dismisses the
48
+ * snackbar. The id must be a worker action:
49
+ * - one of this bundle's own `tier5.<svc>.<action>` ids — run on THIS worker, never another
50
+ * screen's or the app-level worker's;
51
+ * - an app service's `app.<svc>.<action>` — needs `app_services` and an exported action (an app
52
+ * service's own id needs neither). The web host refuses it until it hosts app services.
53
+ * Any other id (`capability.*`, `himi.*`, a registered handler) is refused when the snackbar is
54
+ * requested: the call rejects and nothing is shown. A host that cannot tell which worker asked
55
+ * (React Native, an embedded web region) refuses every `action` the same way. */
56
+ action?: SnackbarAction;
57
+ }
58
+ export interface SnackbarAction {
59
+ /** The button's text — also its VoiceOver / TalkBack / screen-reader label. */
60
+ label: string;
61
+ /** What the tap runs: one of this bundle's own `tier5.<svc>.<action>` ids, or `app.<svc>.<action>`. */
62
+ actionId: string;
63
+ /** The action's arguments, which the reducer receives as typed JSON. */
64
+ args?: Record<string, unknown>;
47
65
  }
48
66
  /** Show a transient snackbar toast: it floats above the tab bar, slides and fades in and out
49
67
  * (fades only under reduced motion), queues behind one that is already showing, and is read
50
68
  * by VoiceOver / TalkBack. Resolves once it is PRESENTED (not after it auto-dismisses).
51
- * Rejects with `no_ui_host` if no presenter is attached. */
69
+ * Rejects with `no_ui_host` if no presenter is attached; with an `action`, also with
70
+ * `invalid_action` (empty label or id), `action_not_allowed` (not one of this bundle's
71
+ * actions), or, for an `app.*` id, `undeclared` (no `app_services`), `not_exported`, `no_service`,
72
+ * `no_action`, `not_booted` or `unavailable`. */
52
73
  export declare function snackbar(options: SnackbarOptions): Promise<void>;
53
74
  /** Capability version, for bundles that gate on min binary version. */
54
75
  export declare const version = 1;
@@ -5,6 +5,7 @@ import type { NavigationConfig, SplashConfig } from "./schema.js";
5
5
  import { type AppServicesConfig } from "./app-services-gate.js";
6
6
  import { type LocaleConfig } from "./locale.js";
7
7
  import { type DistributionConfig } from "./distribution.js";
8
+ import { type StoreConfig } from "./store-adopt.js";
8
9
  import type { CmsConfig } from "./cms.js";
9
10
  import { type DataResourcesConfig } from "@wix/himalaya/data-resources";
10
11
  import { type Base44EntitySpec, type Base44FunctionSpec } from "./base44-entities.js";
@@ -517,7 +518,52 @@ export interface AppConfig {
517
518
  * `GET /.well-known/assetlinks.json`. Pure app identity.
518
519
  */
519
520
  androidAssetLinks?: AndroidAssetLinks;
521
+ /**
522
+ * `@himalaya/app-info` (#4693). `internal` is the rule that decides `AppInfo.isInternal` —
523
+ * whether the signed-in user is one of the app's own people (to show debug tools, tag
524
+ * analytics). It is CONFIGURED here, never guessed: the platform hard-codes no company domain.
525
+ * The host evaluates it against the signed-in identity's email and sends the worker only the
526
+ * boolean. Omit it ⇒ `isInternal` is always false.
527
+ *
528
+ * WIRING (read before relying on it). Nothing generates this into a binary yet: the shell
529
+ * hands the same list to its host arms at boot — `AppInfoCapability.configure(internalEmailDomains:)`
530
+ * (iOS/macOS), `AppInfoCapability.configureInternalEmailDomains(...)` (Android),
531
+ * `registerWebAppInfoRule(appId, rule)` (web entry) — and binds `AppInfoCapability.signedInEmail`
532
+ * (web: `WebAppInfo.signedInEmail`) to a VERIFIED member email, which core cannot know
533
+ * (`AuthUser` has no email). Never a login / registration email the user typed (e.g. the Wix
534
+ * consumer `registerDirect` input or the webwix identity): on an open-signup site anyone could
535
+ * register `x@<internal-domain>` and grant themselves `isInternal`. Until a shell does both,
536
+ * `isInternal` stays false — it fails closed, never open. apps/demo-app is the
537
+ * worked example, with a test pinning its shells to this value.
538
+ */
539
+ appInfo?: AppInfoConfig;
540
+ /**
541
+ * Store identity (#4647). `store.adopt` declares that this app IS an existing store app: the
542
+ * store build ships under its bundle id, team, App Groups, URL schemes and associated domains
543
+ * instead of a derived identity. Dev, device and QA builds keep `bundleId` / `applicationId`.
544
+ * Read only through `adoptedIdentity()` (./store-adopt.ts).
545
+ */
546
+ store?: StoreConfig;
520
547
  }
548
+ /** `defineApp({ appInfo })` — see `AppConfig.appInfo`. */
549
+ export interface AppInfoConfig {
550
+ internal?: AppInfoInternalRule;
551
+ }
552
+ /** Provider-agnostic internal-user rule. Every listed condition is OR-ed. */
553
+ export interface AppInfoInternalRule {
554
+ /** Email domains whose signed-in users are internal, e.g. `["wix.com"]`. Exact domain match,
555
+ * case-insensitive — `wix.com` does not match `notwix.com` or `wix.com.evil.io`; list a
556
+ * subdomain explicitly to include it. */
557
+ emailDomains?: readonly string[];
558
+ }
559
+ /** Normalize one configured domain: trimmed, lowercased, a leading "@" tolerated. */
560
+ export declare function normalizeInternalEmailDomain(domain: string): string;
561
+ /**
562
+ * The pure `isInternal` rule every host arm applies (Swift/Kotlin mirror it). False for an empty
563
+ * or malformed email and for an absent rule — the safe default for a flag that unlocks debug
564
+ * tools is off.
565
+ */
566
+ export declare function isInternalEmail(email: string | null | undefined, rule: AppInfoInternalRule | undefined): boolean;
521
567
  /** Which registrar the native push facade hands its device token to. */
522
568
  export type PushRegistrar = "generic" | "wix-ping";
523
569
  /** Android notification channel (Android O+). `id` is required; the rest are
@@ -791,7 +837,7 @@ export interface OrientationEnvelope {
791
837
  export declare const DEFAULT_ORIENTATION_ENVELOPE: Required<OrientationEnvelope>;
792
838
  /** Fill in the absent families. Total, so callers never branch on `undefined`. */
793
839
  export declare function resolveOrientationEnvelope(env?: OrientationEnvelope): Required<OrientationEnvelope>;
794
- export type Capability = "camera" | "barcode_scanner" | "biometrics" | "secure_storage" | "share" | "push" | "file_picker" | "files" | "open_url" | "pdf" | "clipboard" | "theme" | "photos" | "presigned_upload" | "ui" | "calendar" | "contacts" | "tasks" | "audio_record" | "audio_record_background" | "calls" | "location" | "location_background" | "motion" | "health" | "media_playback" | "webview" | "browser_auth" | "haptic" | "himi_apps" | "iap" | "keyboard" | "network" | "wix_cashier" | "wix_chat" | "live_activity" | "local_database" | "speech" | "speaker_diarization" | "system_intents" | "on_device_ai" | "app_info" | "release_control" | "godot" | "base44" | "animation";
840
+ export type Capability = "camera" | "barcode_scanner" | "biometrics" | "secure_storage" | "share" | "push" | "file_picker" | "files" | "open_url" | "pdf" | "clipboard" | "theme" | "l10n" | "photos" | "presigned_upload" | "ui" | "calendar" | "contacts" | "tasks" | "audio_record" | "audio_record_background" | "calls" | "location" | "location_background" | "motion" | "health" | "media_playback" | "webview" | "browser_auth" | "haptic" | "himi_apps" | "iap" | "keyboard" | "network" | "wix_cashier" | "wix_chat" | "live_activity" | "local_database" | "speech" | "speaker_diarization" | "system_intents" | "on_device_ai" | "app_info" | "release_control" | "godot" | "base44" | "animation";
795
841
  /** `defineApp({ wixIap })` — the purchases-service app a build binds this app to. */
796
842
  export interface WixIapConfig {
797
843
  /** The purchases service's app UUID, registered on the app's own auth site. Not a secret. */
@@ -20,6 +20,7 @@ export * from "./worker-subtree-safe-types.generated.js";
20
20
  export * from "./icon-catalog.generated.js";
21
21
  export * from "./app-link.js";
22
22
  export * from "./app-identity.js";
23
+ export * from "./store-adopt.js";
23
24
  export * from "./app-registry-client.js";
24
25
  export * from "./site-shell.js";
25
26
  export * from "./site-menu.js";
@@ -80,3 +80,42 @@ export declare function resolveAppLocale(opts: {
80
80
  app?: LocaleConfig | null;
81
81
  device?: string | null;
82
82
  }): SupportedLocale;
83
+ /** `locale.set { tag: "system" }` — clear the user choice and follow the device language
84
+ * (under the `shipsLocale` rule, so an English-only app stays English on a Hebrew phone). */
85
+ export declare const LOCALE_SYSTEM = "system";
86
+ /** Endonyms for the picker rows. Mirrored by `LocaleStore.nativeNames` (Swift / Kotlin);
87
+ * a supported tag missing here falls back to the tag itself. */
88
+ export declare const LOCALE_NATIVE_NAMES: Readonly<Record<string, string>>;
89
+ /** Wire value of `locale.get`'s `source`. The store's internal `"app"` origin (the served
90
+ * `default`, pinned or not) reads `"default"` on the wire. */
91
+ export type LocaleChoiceSource = "user" | "device" | "default";
92
+ export interface AvailableLocale {
93
+ tag: SupportedLocale;
94
+ nativeName: string;
95
+ dir: "ltr" | "rtl";
96
+ }
97
+ /** `locale.get`'s payload. */
98
+ export interface LocaleGetResult {
99
+ tag: SupportedLocale;
100
+ source: LocaleChoiceSource;
101
+ available: AvailableLocale[];
102
+ }
103
+ export declare function localeChoiceSource(source: LocaleSource | string | null | undefined): LocaleChoiceSource;
104
+ /**
105
+ * The locales the app ships, source locale first, then `translations` in declared order,
106
+ * de-duplicated and coerced (an unsupported or malformed entry is dropped, exactly as
107
+ * `shipsLocale` ignores it).
108
+ */
109
+ export declare function availableLocales(app: LocaleConfig | null | undefined): AvailableLocale[];
110
+ /**
111
+ * Validate a `locale.set` tag. A shipped locale (matched by primary subtag, so `he-IL`
112
+ * selects `he`) or `"system"`; anything else is `unsupported_locale` and the caller must
113
+ * change nothing.
114
+ */
115
+ export declare function parseLocaleSetTag(app: LocaleConfig | null | undefined, tag: string | null | undefined): {
116
+ ok: true;
117
+ tag: SupportedLocale | typeof LOCALE_SYSTEM;
118
+ } | {
119
+ ok: false;
120
+ error: "unsupported_locale";
121
+ };
@@ -0,0 +1,73 @@
1
+ /** How the store build of the Android app is signed. Names a dist-plane keystore policy. */
2
+ export type AdoptedAndroidSigning = "wix-keystore" | "upload-keystore";
3
+ export declare const ADOPTED_ANDROID_SIGNING: readonly AdoptedAndroidSigning[];
4
+ export interface StoreAdoptConfig {
5
+ /** The existing App Store app's bundle id. Must differ from the app's dev `bundleId`. */
6
+ iosBundleId: string;
7
+ /** The Apple Developer team that owns the existing app record (10 characters). */
8
+ appleTeamId: string;
9
+ /** App Store Connect numeric app id of the existing listing. */
10
+ appStoreId?: string;
11
+ /** The existing Play listing's applicationId (the release variant ships under it). */
12
+ androidApplicationId: string;
13
+ /** App Groups the existing binary uses — each `group.…`. Installed users' shared data lives there. */
14
+ iosAppGroups?: string[];
15
+ /** URL schemes the existing binary registers, BEYOND `navigation.scheme`. */
16
+ urlSchemes?: string[];
17
+ /** Universal-link / App-Link hosts (`app.example.com`, or `host/path` to claim a path). No scheme prefix. */
18
+ associatedDomains?: string[];
19
+ /** Embedded extension bundle ids, keyed by the extension's suffix in project.yml (`<host>.<suffix>`). */
20
+ iosExtensionBundleIds?: Record<string, string>;
21
+ /** Which keystore signs the Android store build; it must be the one the existing listing was signed with. */
22
+ androidSigning?: AdoptedAndroidSigning;
23
+ /** Extensions the old binary shipped and the new one deliberately does not. Generates nothing. */
24
+ dropExtensions?: string[];
25
+ }
26
+ export interface StoreConfig {
27
+ /** Adopt an existing store listing's identity for store builds. */
28
+ adopt?: StoreAdoptConfig;
29
+ }
30
+ /** One associated domain, split. `path` is absent for a whole-host claim. */
31
+ export interface AdoptedDomain {
32
+ host: string;
33
+ path?: string;
34
+ }
35
+ /** The normalized adopted identity. Arrays are always present (possibly empty). */
36
+ export interface AdoptedIdentity {
37
+ iosBundleId: string;
38
+ appleTeamId: string;
39
+ appStoreId?: string;
40
+ androidApplicationId: string;
41
+ androidSigning?: AdoptedAndroidSigning;
42
+ iosAppGroups: string[];
43
+ /** Extra schemes only (navigation.scheme is not repeated here), lowercased, in declared order. */
44
+ urlSchemes: string[];
45
+ associatedDomains: AdoptedDomain[];
46
+ /** Distinct hosts of `associatedDomains`, in declared order — one `applinks:` entitlement each. */
47
+ associatedDomainHosts: string[];
48
+ iosExtensionBundleIds: Record<string, string>;
49
+ dropExtensions: string[];
50
+ }
51
+ /** The config fields `adoptedIdentity` reads. Structural, so any app-config shape can be passed. */
52
+ export interface StoreAdoptSource {
53
+ bundleId?: string;
54
+ applicationId?: string;
55
+ navigation?: {
56
+ scheme?: string;
57
+ };
58
+ store?: StoreConfig;
59
+ }
60
+ /** Parse one associated-domain entry (`host` or `host/path`), naming the field on failure. */
61
+ export declare function parseAdoptedDomain(entry: string, field?: string): AdoptedDomain;
62
+ /**
63
+ * The normalized adopted store identity of an app, or `undefined` when it adopts none.
64
+ *
65
+ * The single reader of `store.adopt`. Throws — naming the field — on any malformed value, so a
66
+ * config that would ship a wrong identity never builds. `defineApp` calls it, which is what makes
67
+ * every rejection surface at config load.
68
+ */
69
+ export declare function adoptedIdentity(config: StoreAdoptSource): AdoptedIdentity | undefined;
70
+ /** The `com.apple.developer.associated-domains` values for an adopted identity: one `applinks:<host>` per host. */
71
+ export declare function adoptedApplinks(identity: AdoptedIdentity): string[];
72
+ /** The iOS entitlement keys an adopted identity requires — what the signing step must request capabilities for. */
73
+ export declare function adoptedEntitlementKeys(identity: AdoptedIdentity): string[];
@@ -18,6 +18,14 @@ var PLATFORMS = ["ios", "android", "macos", "web", "react-native"];
18
18
  function str(v) {
19
19
  return typeof v === "string" ? v.trim() : "";
20
20
  }
21
+ function num(v) {
22
+ const n = typeof v === "number" ? v : typeof v === "string" && v.trim() !== "" ? Number(v) : NaN;
23
+ return Number.isFinite(n) && n > 0 ? n : 0;
24
+ }
25
+ function screenOf(v) {
26
+ const s = v && typeof v === "object" ? v : {};
27
+ return { width: num(s.width), height: num(s.height), scale: num(s.scale) };
28
+ }
21
29
  async function get() {
22
30
  const r = await dispatch("capability.app_info.get");
23
31
  const platform = str(r?.platform);
@@ -36,7 +44,15 @@ async function get() {
36
44
  serveBaseUrl: serveBaseUrl || null,
37
45
  platform: PLATFORMS.includes(platform) ? platform : "unknown",
38
46
  memberSiteBound: r?.memberSiteBound === true,
39
- oneAppInviteAvailable: r?.oneAppInviteAvailable === true
47
+ oneAppInviteAvailable: r?.oneAppInviteAvailable === true,
48
+ deviceModel: str(r?.deviceModel),
49
+ deviceName: str(r?.deviceName),
50
+ osVersion: str(r?.osVersion),
51
+ locale: str(r?.locale),
52
+ screen: screenOf(r?.screen),
53
+ // Exactly `true` only: a string or a number is not the host's answer, and the safe default
54
+ // for a flag that unlocks debug tools is off.
55
+ isInternal: r?.isInternal === true
40
56
  };
41
57
  }
42
58
  async function displayVersion() {