@wix/himalaya-cli 0.943.0 → 0.945.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/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/schema.d.ts +6 -0
- package/dist/cli.mjs +20 -15
- package/dist/preview-assets.generated.bin +0 -0
- package/guides/component-reference.json +372 -1
- package/guides/component-reference.md +215 -0
- package/guides/recipe-dynamic-list.md +45 -2
- package/package.json +2 -2
package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/schema.d.ts
CHANGED
|
@@ -533,6 +533,12 @@ export type TextProps = TextContent & {
|
|
|
533
533
|
* the current left-to-right behavior. Both renderers honor it (parity). */
|
|
534
534
|
textDirection?: "auto" | "ltr" | "rtl";
|
|
535
535
|
decoration?: "underline" | "strikethrough";
|
|
536
|
+
/** EXTRA SPACE BETWEEN LINES, in points/px — added ON TOP of the line height the `style`
|
|
537
|
+
* token already sets. It is NOT a multiple: `lineSpacing: 6` means six points of extra
|
|
538
|
+
* leading, not six times the font size, and every renderer agrees on that. To
|
|
539
|
+
* change the line height as a ratio of the font size, set the typography token's `line`
|
|
540
|
+
* instead; that is where a body style's 1.4/1.7 lives. Absent/0 = the token's own line
|
|
541
|
+
* height, which is the right answer for almost every screen. */
|
|
536
542
|
lineSpacing?: number;
|
|
537
543
|
/** Per-item gate (usually an `$item.` field pre-substituted per row): when it
|
|
538
544
|
* resolves empty, the text collapses to zero size instead of taking layout. */
|
package/dist/cli.mjs
CHANGED
|
@@ -18546,6 +18546,7 @@ function shouldRevealError(args) {
|
|
|
18546
18546
|
var EMAIL_RE, URL_HOST_RE, INTEGER_RE, NUMBER_RE;
|
|
18547
18547
|
var init_validation = __esm({
|
|
18548
18548
|
"../../core/ts/src/validation.ts"() {
|
|
18549
|
+
"use strict";
|
|
18549
18550
|
EMAIL_RE = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*\.[A-Za-z]{2,}$/;
|
|
18550
18551
|
URL_HOST_RE = /^[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*(:[0-9]+)?$/;
|
|
18551
18552
|
INTEGER_RE = /^[+-]?[0-9]+$/;
|
|
@@ -21969,6 +21970,7 @@ function validateWorkerSubtree(raw, slotId, policy, actionCatalog) {
|
|
|
21969
21970
|
var WORKER_SUBTREE_CEILINGS, ID_PART, ACTION_ID;
|
|
21970
21971
|
var init_worker_subtree = __esm({
|
|
21971
21972
|
"../../core/ts/src/worker-subtree.ts"() {
|
|
21973
|
+
"use strict";
|
|
21972
21974
|
init_worker_subtree_safe_types_generated();
|
|
21973
21975
|
WORKER_SUBTREE_CEILINGS = Object.freeze({
|
|
21974
21976
|
maxEncodedBytes: 65536,
|
|
@@ -25593,7 +25595,7 @@ var init_renderNode = __esm({
|
|
|
25593
25595
|
const content = countContent != null ? countContent : icuPattern != null ? /* @__PURE__ */ jsx6(InterpolatedTextView, { pattern: icuPattern, params: props.template?.params ?? {}, ctx }) : mdSource != null ? /* @__PURE__ */ jsx6("span", { dangerouslySetInnerHTML: mdInline(mdSource) }) : txt(props.text, ctx);
|
|
25594
25596
|
const tAlign = props.textAlign === "center" ? "center" : props.textAlign === "trailing" ? "right" : props.textAlign === "justify" ? "justify" : props.textAlign === "leading" ? "left" : void 0;
|
|
25595
25597
|
const tDecor = props.decoration === "strikethrough" ? "line-through" : props.decoration === "underline" ? "underline" : void 0;
|
|
25596
|
-
const ln = typeof props.lineSpacing === "number"
|
|
25598
|
+
const ln = typeof props.lineSpacing === "number" ? `${(f.line ?? 1.4) * f.size + props.lineSpacing}px` : f.line;
|
|
25597
25599
|
return /* @__PURE__ */ jsx6("div", { ...countAttrs, style: { fontFamily: fontStack(f.family), fontSize: f.size, fontWeight: f.weight, lineHeight: ln, letterSpacing: f.letterSpacing, color: C(resolveScalarStatic(props.color, ctx) ?? "textPrimary", "#13171e"), whiteSpace: "pre-wrap", overflowWrap: "anywhere", wordBreak: "break-word", minWidth: 0, ...tAlign ? { textAlign: tAlign } : {}, ...tDecor ? { textDecoration: tDecor } : {}, ...props.maxLines ? { display: "-webkit-box", WebkitLineClamp: props.maxLines, WebkitBoxOrient: "vertical", overflow: "hidden" } : {}, ...wrap }, children: content == null || content === "" ? collapse ? null : "\xA0" : content });
|
|
25598
25600
|
});
|
|
25599
25601
|
ImageNode = withExtras("ImageNode", ({ props, ctx, wrap }) => {
|
|
@@ -43281,19 +43283,19 @@ var init_preview_assets_generated = __esm({
|
|
|
43281
43283
|
"../../packages/serve/src/preview-assets.generated.ts"() {
|
|
43282
43284
|
init_preview_assets_format();
|
|
43283
43285
|
PREVIEW_ASSET_MANIFEST = [
|
|
43284
|
-
{ key: "assets/index-
|
|
43285
|
-
{ key: "fonts/WixMadeforDisplay-Bold.ttf", contentType: "font/ttf", offset:
|
|
43286
|
-
{ key: "fonts/WixMadeforDisplay-ExtraBold.ttf", contentType: "font/ttf", offset:
|
|
43287
|
-
{ key: "fonts/WixMadeforDisplay-Medium.ttf", contentType: "font/ttf", offset:
|
|
43288
|
-
{ key: "fonts/WixMadeforDisplay-Regular.ttf", contentType: "font/ttf", offset:
|
|
43289
|
-
{ key: "fonts/WixMadeforDisplay-SemiBold.ttf", contentType: "font/ttf", offset:
|
|
43290
|
-
{ key: "fonts/WixMadeforText-Bold.ttf", contentType: "font/ttf", offset:
|
|
43291
|
-
{ key: "fonts/WixMadeforText-ExtraBold.ttf", contentType: "font/ttf", offset:
|
|
43292
|
-
{ key: "fonts/WixMadeforText-Medium.ttf", contentType: "font/ttf", offset:
|
|
43293
|
-
{ key: "fonts/WixMadeforText-Regular.ttf", contentType: "font/ttf", offset:
|
|
43294
|
-
{ key: "fonts/WixMadeforText-SemiBold.ttf", contentType: "font/ttf", offset:
|
|
43295
|
-
{ key: "index.html", contentType: "text/html; charset=utf-8", offset:
|
|
43296
|
-
{ key: "t5-frame.html", contentType: "text/html; charset=utf-8", offset:
|
|
43286
|
+
{ key: "assets/index-C40lmxo4.js", contentType: "application/javascript; charset=utf-8", offset: 8, length: 983092, sha256: "919193db2182eb6a481f3729dda29af2441d6c46027c7fab508c2abb78f8fc0f" },
|
|
43287
|
+
{ key: "fonts/WixMadeforDisplay-Bold.ttf", contentType: "font/ttf", offset: 983100, length: 90924, sha256: "ec4c7d63772d980d6e147b2df6215fd2737e4b04cebe0cf7e11dc695fb63cdcf" },
|
|
43288
|
+
{ key: "fonts/WixMadeforDisplay-ExtraBold.ttf", contentType: "font/ttf", offset: 1074024, length: 90984, sha256: "bf4c16a2774edd38a921edf3099469866cd74c094fd596f3fa4e81f54a04dfaa" },
|
|
43289
|
+
{ key: "fonts/WixMadeforDisplay-Medium.ttf", contentType: "font/ttf", offset: 1165008, length: 90980, sha256: "368faca44fb9f7207e4af784e94750b1ee59c1098b8689e0f4a918926f244fb5" },
|
|
43290
|
+
{ key: "fonts/WixMadeforDisplay-Regular.ttf", contentType: "font/ttf", offset: 1255988, length: 90872, sha256: "97ed8f1a6772abe26e3d8af5cecfc4b02faa94497d585b0398b5053b739a70c1" },
|
|
43291
|
+
{ key: "fonts/WixMadeforDisplay-SemiBold.ttf", contentType: "font/ttf", offset: 1346860, length: 90980, sha256: "642feab8a48ddf6c7a89d7c2a7c0edfa0e6e773f671287e460e222703563191e" },
|
|
43292
|
+
{ key: "fonts/WixMadeforText-Bold.ttf", contentType: "font/ttf", offset: 1437840, length: 108636, sha256: "37c179b4fefb63ea9637322b84583a2df53aa226c150294eea1675c14b1b8e29" },
|
|
43293
|
+
{ key: "fonts/WixMadeforText-ExtraBold.ttf", contentType: "font/ttf", offset: 1546476, length: 109084, sha256: "62e2fe4718129f36ecdb0ae4de385bd6b81ecc876146447167c6c140fad699a9" },
|
|
43294
|
+
{ key: "fonts/WixMadeforText-Medium.ttf", contentType: "font/ttf", offset: 1655560, length: 109048, sha256: "4ecf270136af928612c76fe4ebade91d8156975e5ea4c0cf9225501dd1788e02" },
|
|
43295
|
+
{ key: "fonts/WixMadeforText-Regular.ttf", contentType: "font/ttf", offset: 1764608, length: 107700, sha256: "dee56f09fd3efc127c1ea95557edc74110c1460a46287da36e0af25efd341ef5" },
|
|
43296
|
+
{ key: "fonts/WixMadeforText-SemiBold.ttf", contentType: "font/ttf", offset: 1872308, length: 88108, sha256: "dced97a77624bc5f8b1ae65850360908297f4588e09feb6f22792d29dc7e765b" },
|
|
43297
|
+
{ key: "index.html", contentType: "text/html; charset=utf-8", offset: 1960416, length: 4448, sha256: "4cff52eaf77b0c3c09effb2dd0447d78d2b03ecaa68893281b1637510b93d8ad" },
|
|
43298
|
+
{ key: "t5-frame.html", contentType: "text/html; charset=utf-8", offset: 1964864, length: 13784, sha256: "cfb9465254fab84f0e084bb4542c282758ea29774b5f172840cc0b9e4194ef89" }
|
|
43297
43299
|
];
|
|
43298
43300
|
}
|
|
43299
43301
|
});
|
|
@@ -55207,7 +55209,10 @@ function ownerPlaneHint(status, error, target) {
|
|
|
55207
55209
|
return { hint: "this environment has no token signing secret configured \u2014 an operator must set SERVE_CONTROL_SIGNING_SECRET" };
|
|
55208
55210
|
}
|
|
55209
55211
|
if (error === "owner_requires_selfserve_token") {
|
|
55210
|
-
|
|
55212
|
+
const base = `that credential is an operator token, not a self-serve one \u2014 run \`himi login --browser --target ${target}\` to get an owner identity`;
|
|
55213
|
+
return {
|
|
55214
|
+
hint: isPublicTarget(target) ? base : `${base}. On ${target} that REPLACES the employee token stored for this target, and \`himi dist\` then charges the self-serve tier instead of the employee one \u2014 deliberately the tightest of the three, so far fewer concurrent builds and far fewer per day. \`himi login --dpx --target ${target}\` switches back.`
|
|
55215
|
+
};
|
|
55211
55216
|
}
|
|
55212
55217
|
if (error === "token_revoked") {
|
|
55213
55218
|
return { hint: `this token was revoked \u2014 run \`himi login --browser --target ${target}\` to get a new one` };
|
|
Binary file
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
"bindings": [
|
|
102
102
|
"text"
|
|
103
103
|
],
|
|
104
|
-
"propsSource": "export type TextProps = TextContent & {\n style?: TokenName;\n /** Foreground color token (default \"textPrimary\"). */\n color?: TokenName;\n /** Tail-truncate after N lines (absent = unlimited). */\n maxLines?: number;\n /** Lever D — a live time value (countdown/relative) the OS ticks with no refresh.\n * When present it OVERRIDES `text`. Widget-safe render only. */\n timeRef?: WidgetTimeRef;\n /** Render `text` as a constrained Markdown subset. */\n markdown?: boolean;\n /** Capability enrichments. */\n textAlign?: \"leading\" | \"center\" | \"trailing\" | \"justify\";\n /** Base writing direction. \"auto\" (or \"rtl\") makes a bidi paragraph resolve +\n * align by its own strong-directional content (Hebrew/Arabic → right-aligned,\n * RTL layout), matching per-message direction in chat bubbles. Absent/\"ltr\" =\n * the current left-to-right behavior. Both renderers honor it (parity). */\n textDirection?: \"auto\" | \"ltr\" | \"rtl\";\n decoration?: \"underline\" | \"strikethrough\";\n lineSpacing?: number;\n /** Per-item gate (usually an `$item.` field pre-substituted per row): when it\n * resolves empty, the text collapses to zero size instead of taking layout. */\n collapseWhenEmpty?: boolean;\n /** Motion grammar (Batch 1, item 4) — count-up tween for a numeric value on\n * change; non-numeric values render instantly. Gated by reduced motion. */\n animateValue?: boolean;\n /** Motion grammar — reveal the (post-substitution literal) text word-by-word,\n * once per distinct content, like a chat assistant \"typing\" its reply. Each\n * content animates a single time (progress is remembered), so list re-renders\n * and scrolls don't restart it. Gated by reduced motion (snaps to full).\n * Both renderers honor it (parity). Default nil/false. */\n animateReveal?: boolean;\n};"
|
|
104
|
+
"propsSource": "export type TextProps = TextContent & {\n style?: TokenName;\n /** Foreground color token (default \"textPrimary\"). */\n color?: TokenName;\n /** Tail-truncate after N lines (absent = unlimited). */\n maxLines?: number;\n /** Lever D — a live time value (countdown/relative) the OS ticks with no refresh.\n * When present it OVERRIDES `text`. Widget-safe render only. */\n timeRef?: WidgetTimeRef;\n /** Render `text` as a constrained Markdown subset. */\n markdown?: boolean;\n /** Capability enrichments. */\n textAlign?: \"leading\" | \"center\" | \"trailing\" | \"justify\";\n /** Base writing direction. \"auto\" (or \"rtl\") makes a bidi paragraph resolve +\n * align by its own strong-directional content (Hebrew/Arabic → right-aligned,\n * RTL layout), matching per-message direction in chat bubbles. Absent/\"ltr\" =\n * the current left-to-right behavior. Both renderers honor it (parity). */\n textDirection?: \"auto\" | \"ltr\" | \"rtl\";\n decoration?: \"underline\" | \"strikethrough\";\n /** EXTRA SPACE BETWEEN LINES, in points/px — added ON TOP of the line height the `style`\n * token already sets. It is NOT a multiple: `lineSpacing: 6` means six points of extra\n * leading, not six times the font size, and every renderer agrees on that. To\n * change the line height as a ratio of the font size, set the typography token's `line`\n * instead; that is where a body style's 1.4/1.7 lives. Absent/0 = the token's own line\n * height, which is the right answer for almost every screen. */\n lineSpacing?: number;\n /** Per-item gate (usually an `$item.` field pre-substituted per row): when it\n * resolves empty, the text collapses to zero size instead of taking layout. */\n collapseWhenEmpty?: boolean;\n /** Motion grammar (Batch 1, item 4) — count-up tween for a numeric value on\n * change; non-numeric values render instantly. Gated by reduced motion. */\n animateValue?: boolean;\n /** Motion grammar — reveal the (post-substitution literal) text word-by-word,\n * once per distinct content, like a chat assistant \"typing\" its reply. Each\n * content animates a single time (progress is remembered), so list re-renders\n * and scrolls don't restart it. Gated by reduced motion (snaps to full).\n * Both renderers honor it (parity). Default nil/false. */\n animateReveal?: boolean;\n};"
|
|
105
105
|
},
|
|
106
106
|
{
|
|
107
107
|
"name": "Image",
|
|
@@ -139,6 +139,44 @@
|
|
|
139
139
|
"focusPath",
|
|
140
140
|
"errorPath"
|
|
141
141
|
],
|
|
142
|
+
"dispatches": [
|
|
143
|
+
{
|
|
144
|
+
"callback": "onChange",
|
|
145
|
+
"key": "value",
|
|
146
|
+
"encoding": "string",
|
|
147
|
+
"note": "The typed text, clamped to maxLength by the renderer.",
|
|
148
|
+
"divergences": [
|
|
149
|
+
{
|
|
150
|
+
"arms": [
|
|
151
|
+
"ios",
|
|
152
|
+
"android",
|
|
153
|
+
"web"
|
|
154
|
+
],
|
|
155
|
+
"kind": "additive",
|
|
156
|
+
"actual": "additionally injects `fieldId` when the text field is inside a Form, so one worker action can serve several fields.",
|
|
157
|
+
"reason": "Additive, not conflicting, and shipped bundles already duck-type it. Every arm guards on the field actually being registered in a Form, so a standalone TextField still sends no `fieldId`. Still a divergence rather than a promoted contract key because RN does not send it and the contract does not require it."
|
|
158
|
+
}
|
|
159
|
+
]
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
"callback": "onSubmit",
|
|
163
|
+
"key": "value",
|
|
164
|
+
"encoding": "string",
|
|
165
|
+
"note": "The field's current text at return-key time.",
|
|
166
|
+
"divergences": [
|
|
167
|
+
{
|
|
168
|
+
"arms": [
|
|
169
|
+
"ios",
|
|
170
|
+
"android",
|
|
171
|
+
"web"
|
|
172
|
+
],
|
|
173
|
+
"kind": "additive",
|
|
174
|
+
"actual": "additionally injects `fieldId` when the text field is inside a Form, so one worker action can serve several fields.",
|
|
175
|
+
"reason": "Additive, not conflicting, and shipped bundles already duck-type it. Every arm guards on the field actually being registered in a Form, so a standalone TextField still sends no `fieldId`. Still a divergence rather than a promoted contract key because RN does not send it and the contract does not require it."
|
|
176
|
+
}
|
|
177
|
+
]
|
|
178
|
+
}
|
|
179
|
+
],
|
|
142
180
|
"propsSource": "export interface TextFieldProps {\n placeholder?: string;\n /** LITERAL initial text. Renderers paint it verbatim — a `$service:` sentinel\n * here shows the raw token to the user. Bind `valuePath` instead. */\n value?: string;\n /** `$service:`/`$item.` binding → live text from the worker (overrides `value`).\n * Adopted while the field is NOT focused; an empty worker value (a draft reset\n * after send/post/submit) is adopted even mid-focus, so a cleared draft empties\n * the field without the worker ever fighting the caret. */\n valuePath?: string;\n onChange?: Action;\n /** Text input enrichments. */\n keyboard?: Keyboard;\n secure?: boolean;\n multilineMaxLines?: number;\n maxLength?: number;\n autoCapitalization?: AutoCapitalization;\n onSubmit?: Action;\n /** Keyboard control. `suggestions: \"off\"` kills autocorrect + the suggestion\n * strip. `submitLabel` paints the return key and, with `onSubmit`, fires it —\n * submitting dismisses the keyboard except for `\"next\"`, which keeps it up so\n * a worker can move focus on. */\n suggestions?: Suggestions;\n submitLabel?: SubmitLabel;\n /** `$service:` binding → a TOKEN the worker changes to focus this field. Focus\n * is requested whenever the resolved value becomes a new NON-EMPTY value\n * (the first seed included, so a non-empty init focuses on open). Use a\n * monotonic nonce — `String(Date.now())` — since a latched boolean cannot\n * re-fire. Blur is the keyboard-dismiss capability, not a token here. */\n focusPath?: string;\n /** Accessory & state enrichments. */\n leadingIcon?: IconRef;\n trailingIcon?: IconRef;\n clearButton?: boolean;\n helperText?: string;\n error?: boolean;\n prefix?: string;\n suffix?: string;\n /** Drop the default rounded-border chrome so a caller background shows (composer pill). */\n borderless?: boolean;\n /** Declarative syntactic rules the RENDERER evaluates, so they hold with no\n * worker running (Tier 0-4) and with no bridge round-trip. Before this,\n * validation was reachable only by wrapping the field in `Form` -> `FormField`\n * - the shape the design guide steers you away from for hero flows. */\n validation?: Validation;\n /** `$service:`/`$item.` binding -> a worker-computed error string. Non-empty =\n * invalid. This is how SEMANTIC and CROSS-FIELD errors (\"passwords don't\n * match\", \"email taken\") reach the same error slot, with the same styling and\n * a11y wiring, as the declarative ones. Wins over `validation.message`. */\n errorPath?: string;\n /** LITERAL error string for a server-rendered screen with no worker to bind.\n * Lower precedence than `errorPath`, higher than the rules. */\n errorText?: string;\n}"
|
|
143
181
|
},
|
|
144
182
|
{
|
|
@@ -148,6 +186,23 @@
|
|
|
148
186
|
"bindings": [
|
|
149
187
|
"valuePath"
|
|
150
188
|
],
|
|
189
|
+
"dispatches": [
|
|
190
|
+
{
|
|
191
|
+
"callback": "onChange",
|
|
192
|
+
"key": "value",
|
|
193
|
+
"encoding": "boolean",
|
|
194
|
+
"note": "The NEXT state, not the previous one.",
|
|
195
|
+
"divergences": [
|
|
196
|
+
{
|
|
197
|
+
"arms": [
|
|
198
|
+
"ios"
|
|
199
|
+
],
|
|
200
|
+
"kind": "additive",
|
|
201
|
+
"actual": "additionally injects `fieldId` when the control is inside a Form — via bindingForBool, for a Toggle nested in a FormField."
|
|
202
|
+
}
|
|
203
|
+
]
|
|
204
|
+
}
|
|
205
|
+
],
|
|
151
206
|
"propsSource": "export interface ToggleProps {\n label?: string;\n value: boolean;\n /** `$service:`/`$item.` binding → a live bool (overrides `value`). */\n valuePath?: string;\n onChange?: Action;\n /** #1047 — ON-state track color token (default: `accentPrimary`). Setting any\n * appearance prop switches to a custom-drawn control so a host can match its own\n * switch and keep the screen OTA (vs the `intents:` escape hatch). */\n trackOn?: TokenName;\n /** #1047 — OFF-state track color token (default: system/`surfaceTertiary`). */\n trackOff?: TokenName;\n /** #1047 — thumb color token (default: white). */\n thumb?: TokenName;\n /** #1047 — track width (pt/dp/px). Absent = system size. */\n width?: number;\n /** #1047 — track height (pt/dp/px). Absent = system size. */\n height?: number;\n}"
|
|
152
207
|
},
|
|
153
208
|
{
|
|
@@ -157,6 +212,25 @@
|
|
|
157
212
|
"bindings": [
|
|
158
213
|
"valuePath"
|
|
159
214
|
],
|
|
215
|
+
"dispatches": [
|
|
216
|
+
{
|
|
217
|
+
"callback": "onChange",
|
|
218
|
+
"key": "value",
|
|
219
|
+
"encoding": "int",
|
|
220
|
+
"note": "The clamped next value. A no-change step must not dispatch.",
|
|
221
|
+
"divergences": [
|
|
222
|
+
{
|
|
223
|
+
"arms": [
|
|
224
|
+
"web",
|
|
225
|
+
"rn"
|
|
226
|
+
],
|
|
227
|
+
"kind": "params-mirror",
|
|
228
|
+
"actual": "mirrors the value into `params` as a NUMBER.",
|
|
229
|
+
"reason": "Violates rule 2 — `Action.params` is `map<string,string>`. Harmless through the JS hosts, wrong on the wire."
|
|
230
|
+
}
|
|
231
|
+
]
|
|
232
|
+
}
|
|
233
|
+
],
|
|
160
234
|
"propsSource": "export interface StepperProps {\n value: number;\n /** `$service:`/`$item.` binding → a live integer (overrides `value`). */\n valuePath?: string;\n min?: number;\n max?: number;\n onChange?: Action;\n}"
|
|
161
235
|
},
|
|
162
236
|
{
|
|
@@ -238,6 +312,24 @@
|
|
|
238
312
|
"category": "Form primitives",
|
|
239
313
|
"propsType": "PickerProps",
|
|
240
314
|
"bindings": [],
|
|
315
|
+
"dispatches": [
|
|
316
|
+
{
|
|
317
|
+
"callback": "onChange",
|
|
318
|
+
"key": "selectedId",
|
|
319
|
+
"encoding": "string",
|
|
320
|
+
"note": "The chosen option's id — an enum the reducer switches on, never an index.",
|
|
321
|
+
"divergences": [
|
|
322
|
+
{
|
|
323
|
+
"arms": [
|
|
324
|
+
"ios"
|
|
325
|
+
],
|
|
326
|
+
"kind": "probe",
|
|
327
|
+
"actual": "inside a FormField, the non-text kinds store into the form bag and RETURN without dispatching — the renderer's own code.",
|
|
328
|
+
"reason": "A formField({kind:'picker'}) onChange is a dead action on iOS while every other arm fires it."
|
|
329
|
+
}
|
|
330
|
+
]
|
|
331
|
+
}
|
|
332
|
+
],
|
|
241
333
|
"notImplemented": {
|
|
242
334
|
"props": [
|
|
243
335
|
"optionsPath",
|
|
@@ -256,6 +348,39 @@
|
|
|
256
348
|
"category": "Form primitives",
|
|
257
349
|
"propsType": "DatePickerProps",
|
|
258
350
|
"bindings": [],
|
|
351
|
+
"dispatches": [
|
|
352
|
+
{
|
|
353
|
+
"callback": "onChange",
|
|
354
|
+
"key": "value",
|
|
355
|
+
"encoding": "unix-seconds-string",
|
|
356
|
+
"note": "Unix SECONDS, string-encoded so the params mirror stays legal. iOS ships `valueUnix` as an int instead; RN ships nothing. The normative key is `value` because that is what three of four arms already use for every other scalar control.",
|
|
357
|
+
"divergences": [
|
|
358
|
+
{
|
|
359
|
+
"arms": [
|
|
360
|
+
"ios"
|
|
361
|
+
],
|
|
362
|
+
"kind": "wrong-key",
|
|
363
|
+
"actual": "sends `valueUnix` as an int instead of the contract's `value`.",
|
|
364
|
+
"reason": "Only web conforms. This is the widest split in the vocabulary and the clearest argument for the file."
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
"arms": [
|
|
368
|
+
"rn"
|
|
369
|
+
],
|
|
370
|
+
"kind": "no-payload",
|
|
371
|
+
"actual": "dispatches the bare action with no date at all."
|
|
372
|
+
},
|
|
373
|
+
{
|
|
374
|
+
"arms": [
|
|
375
|
+
"ios"
|
|
376
|
+
],
|
|
377
|
+
"kind": "probe",
|
|
378
|
+
"actual": "inside a FormField, the non-text kinds store into the form bag and RETURN without dispatching — the renderer's own code.",
|
|
379
|
+
"reason": "A formField({kind:'picker'}) onChange is a dead action on iOS while every other arm fires it."
|
|
380
|
+
}
|
|
381
|
+
]
|
|
382
|
+
}
|
|
383
|
+
],
|
|
259
384
|
"notImplemented": {
|
|
260
385
|
"props": [
|
|
261
386
|
"mode",
|
|
@@ -308,6 +433,20 @@
|
|
|
308
433
|
"category": "Search & filter",
|
|
309
434
|
"propsType": "SearchFieldProps",
|
|
310
435
|
"bindings": [],
|
|
436
|
+
"dispatches": [
|
|
437
|
+
{
|
|
438
|
+
"callback": "onChange",
|
|
439
|
+
"key": "value",
|
|
440
|
+
"encoding": "string",
|
|
441
|
+
"note": "Debounced by `debounceMs` before dispatch."
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
"callback": "onSubmit",
|
|
445
|
+
"key": "value",
|
|
446
|
+
"encoding": "string",
|
|
447
|
+
"note": "These two rows share a control and key, so they are NOT independently gated: a payload observation cannot say which callback produced it, and a helper-mediated dispatch does not name the callback at the write site at all. The gate proves the callback is still referenced in the component that dispatches, not that this callback carries the payload."
|
|
448
|
+
}
|
|
449
|
+
],
|
|
311
450
|
"propsSource": "export interface SearchFieldProps {\n placeholder?: string;\n value?: string;\n onChange?: Action;\n onSubmit?: Action;\n debounceMs?: number;\n}"
|
|
312
451
|
},
|
|
313
452
|
{
|
|
@@ -318,6 +457,24 @@
|
|
|
318
457
|
"optionsPath",
|
|
319
458
|
"selectedIds"
|
|
320
459
|
],
|
|
460
|
+
"dispatches": [
|
|
461
|
+
{
|
|
462
|
+
"callback": "onChange",
|
|
463
|
+
"key": "selectedIds",
|
|
464
|
+
"encoding": "string-array",
|
|
465
|
+
"note": "The full selection AFTER the toggle, not the chip that moved.",
|
|
466
|
+
"divergences": [
|
|
467
|
+
{
|
|
468
|
+
"arms": [
|
|
469
|
+
"rn"
|
|
470
|
+
],
|
|
471
|
+
"kind": "params-mirror",
|
|
472
|
+
"actual": "mirrors `selectedIds` into `params` as an ARRAY.",
|
|
473
|
+
"reason": "Rule-2 violation."
|
|
474
|
+
}
|
|
475
|
+
]
|
|
476
|
+
}
|
|
477
|
+
],
|
|
321
478
|
"propsSource": "export interface FilterChipsProps {\n chips: FilterChip[];\n /** Literal ids, or a binding-aware `$service:`/`$item.` live selection. */\n selectedIds?: string[];\n multiSelect?: boolean;\n onChange?: Action;\n /** `$service:` binding → a live options array (overrides `chips`). */\n optionsPath?: string;\n}"
|
|
322
479
|
},
|
|
323
480
|
{
|
|
@@ -329,6 +486,13 @@
|
|
|
329
486
|
"activeColorPath",
|
|
330
487
|
"selectedId"
|
|
331
488
|
],
|
|
489
|
+
"dispatches": [
|
|
490
|
+
{
|
|
491
|
+
"callback": "onChange",
|
|
492
|
+
"key": "selectedId",
|
|
493
|
+
"encoding": "string"
|
|
494
|
+
}
|
|
495
|
+
],
|
|
332
496
|
"notImplemented": {
|
|
333
497
|
"props": [
|
|
334
498
|
"style"
|
|
@@ -349,6 +513,13 @@
|
|
|
349
513
|
"optionsPath",
|
|
350
514
|
"selectedId"
|
|
351
515
|
],
|
|
516
|
+
"dispatches": [
|
|
517
|
+
{
|
|
518
|
+
"callback": "onChange",
|
|
519
|
+
"key": "selectedId",
|
|
520
|
+
"encoding": "string"
|
|
521
|
+
}
|
|
522
|
+
],
|
|
352
523
|
"propsSource": "export interface SegmentedControlProps {\n options: TabItem[];\n /** Literal id, or a binding-aware `$service:`/`$item.` live selection. */\n selectedId: string;\n onChange?: Action;\n /** `$service:` binding → a live options array (overrides `options`). */\n optionsPath?: string;\n}"
|
|
353
524
|
},
|
|
354
525
|
{
|
|
@@ -456,6 +627,40 @@
|
|
|
456
627
|
"bindings": [
|
|
457
628
|
"valuePath"
|
|
458
629
|
],
|
|
630
|
+
"dispatches": [
|
|
631
|
+
{
|
|
632
|
+
"callback": "onChange",
|
|
633
|
+
"key": "value",
|
|
634
|
+
"encoding": "double",
|
|
635
|
+
"note": "Range mode additionally sends `upperValue`.",
|
|
636
|
+
"divergences": [
|
|
637
|
+
{
|
|
638
|
+
"arms": [
|
|
639
|
+
"web"
|
|
640
|
+
],
|
|
641
|
+
"kind": "no-payload",
|
|
642
|
+
"actual": "dispatches the bare action — the renderer's own code — with no injected value."
|
|
643
|
+
},
|
|
644
|
+
{
|
|
645
|
+
"arms": [
|
|
646
|
+
"rn"
|
|
647
|
+
],
|
|
648
|
+
"kind": "no-payload",
|
|
649
|
+
"actual": "renders a static visual and never dispatches at all.",
|
|
650
|
+
"reason": "RN Slider is unimplemented rather than divergent; listed so the gap is counted, not hidden."
|
|
651
|
+
},
|
|
652
|
+
{
|
|
653
|
+
"arms": [
|
|
654
|
+
"ios",
|
|
655
|
+
"android"
|
|
656
|
+
],
|
|
657
|
+
"kind": "additive",
|
|
658
|
+
"actual": "range mode additionally sends `upperValue` beside `value`.",
|
|
659
|
+
"reason": "Additive, and the contract's Slider note already describes it. Same problem as groupId: allowed by a hard-coded list rather than declared."
|
|
660
|
+
}
|
|
661
|
+
]
|
|
662
|
+
}
|
|
663
|
+
],
|
|
459
664
|
"notImplemented": {
|
|
460
665
|
"props": [
|
|
461
666
|
"debounceMs"
|
|
@@ -475,6 +680,38 @@
|
|
|
475
680
|
"bindings": [
|
|
476
681
|
"valuePath"
|
|
477
682
|
],
|
|
683
|
+
"dispatches": [
|
|
684
|
+
{
|
|
685
|
+
"callback": "onChange",
|
|
686
|
+
"key": "value",
|
|
687
|
+
"encoding": "boolean",
|
|
688
|
+
"note": "The next checked state. iOS additionally sends `groupId` for radio groups.",
|
|
689
|
+
"divergences": [
|
|
690
|
+
{
|
|
691
|
+
"arms": [
|
|
692
|
+
"web"
|
|
693
|
+
],
|
|
694
|
+
"kind": "no-payload",
|
|
695
|
+
"actual": "dispatches the bare action — the renderer's own code — with no injected value."
|
|
696
|
+
},
|
|
697
|
+
{
|
|
698
|
+
"arms": [
|
|
699
|
+
"rn"
|
|
700
|
+
],
|
|
701
|
+
"kind": "params-mirror",
|
|
702
|
+
"actual": "mirrors the value into `params` as a BOOLEAN."
|
|
703
|
+
},
|
|
704
|
+
{
|
|
705
|
+
"arms": [
|
|
706
|
+
"ios"
|
|
707
|
+
],
|
|
708
|
+
"kind": "additive",
|
|
709
|
+
"actual": "additionally sends `groupId` so a radio group can identify which set changed.",
|
|
710
|
+
"reason": "Additive, not conflicting. It was previously hard-coded as an allowed key in the gate, which put it outside the contract — the one place this PR claims is the single source of truth."
|
|
711
|
+
}
|
|
712
|
+
]
|
|
713
|
+
}
|
|
714
|
+
],
|
|
478
715
|
"propsSource": "export interface SelectionControlProps {\n label?: string;\n value?: boolean;\n kind?: \"checkbox\" | \"radio\";\n /** Radios sharing a `groupId` are mutually exclusive. */\n groupId?: string;\n onChange?: Action;\n /** \"$service:\"/\"$item.\" binding → bool (overrides `value`). */\n valuePath?: string;\n disabled?: boolean;\n /** Checkbox tri-state (renders a dash); ignored for radio. */\n indeterminate?: boolean;\n}"
|
|
479
716
|
},
|
|
480
717
|
{
|
|
@@ -592,6 +829,23 @@
|
|
|
592
829
|
"bindings": [
|
|
593
830
|
"valuesPath"
|
|
594
831
|
],
|
|
832
|
+
"dispatches": [
|
|
833
|
+
{
|
|
834
|
+
"callback": "onRemove",
|
|
835
|
+
"key": "value",
|
|
836
|
+
"encoding": "string",
|
|
837
|
+
"note": "The removed chip's value.",
|
|
838
|
+
"divergences": [
|
|
839
|
+
{
|
|
840
|
+
"arms": [
|
|
841
|
+
"web"
|
|
842
|
+
],
|
|
843
|
+
"kind": "no-payload",
|
|
844
|
+
"actual": "dispatches the bare action — the renderer's own code — with no injected value."
|
|
845
|
+
}
|
|
846
|
+
]
|
|
847
|
+
}
|
|
848
|
+
],
|
|
595
849
|
"notImplemented": {
|
|
596
850
|
"props": [
|
|
597
851
|
"onAdd"
|
|
@@ -660,6 +914,41 @@
|
|
|
660
914
|
"bindings": [
|
|
661
915
|
"valuePath"
|
|
662
916
|
],
|
|
917
|
+
"dispatches": [
|
|
918
|
+
{
|
|
919
|
+
"callback": "onChange",
|
|
920
|
+
"key": "value",
|
|
921
|
+
"encoding": "int",
|
|
922
|
+
"note": "1-based star index.",
|
|
923
|
+
"divergences": [
|
|
924
|
+
{
|
|
925
|
+
"arms": [
|
|
926
|
+
"web",
|
|
927
|
+
"rn"
|
|
928
|
+
],
|
|
929
|
+
"kind": "params-only",
|
|
930
|
+
"actual": "writes the value into `params` ONLY, never `args`.",
|
|
931
|
+
"reason": "Survives only because the Tier-5 hosts merge params into args. Any consumer reading `args` — which rule 1 says is the payload map — gets nothing."
|
|
932
|
+
},
|
|
933
|
+
{
|
|
934
|
+
"arms": [
|
|
935
|
+
"ios"
|
|
936
|
+
],
|
|
937
|
+
"kind": "encoding",
|
|
938
|
+
"actual": "sends `value` as .double where the contract says int."
|
|
939
|
+
},
|
|
940
|
+
{
|
|
941
|
+
"arms": [
|
|
942
|
+
"web",
|
|
943
|
+
"rn"
|
|
944
|
+
],
|
|
945
|
+
"kind": "params-mirror",
|
|
946
|
+
"actual": "stringifies the 1-based index into `params` (`String(i + 1)`) for a control the contract declares `int`.",
|
|
947
|
+
"reason": "Rule 2 permits a params mirror only for string-encoded values, and Rating is int. The earlier expression heuristic missed it because `String(i + 1)` does not look like a literal — it surfaced only once rule 2 keyed off the declared encoding."
|
|
948
|
+
}
|
|
949
|
+
]
|
|
950
|
+
}
|
|
951
|
+
],
|
|
663
952
|
"propsSource": "export interface RatingProps {\n value?: number;\n max?: number;\n interactive?: boolean;\n onChange?: Action;\n color?: TokenName;\n icon?: IconRef;\n valuePath?: string;\n}"
|
|
664
953
|
},
|
|
665
954
|
{
|
|
@@ -744,6 +1033,31 @@
|
|
|
744
1033
|
"optionsPath",
|
|
745
1034
|
"selectedId"
|
|
746
1035
|
],
|
|
1036
|
+
"dispatches": [
|
|
1037
|
+
{
|
|
1038
|
+
"callback": "onChange",
|
|
1039
|
+
"key": "selectedId",
|
|
1040
|
+
"encoding": "string",
|
|
1041
|
+
"divergences": [
|
|
1042
|
+
{
|
|
1043
|
+
"arms": [
|
|
1044
|
+
"rn"
|
|
1045
|
+
],
|
|
1046
|
+
"kind": "params-wrong-key",
|
|
1047
|
+
"actual": "writes `params.value = String(id)` but `args.selectedId = id` — a different key in each map.",
|
|
1048
|
+
"reason": "Conforms on `args` (rule 1) but the params mirror uses the wrong key, so a params-reading consumer sees a key no other arm sends."
|
|
1049
|
+
},
|
|
1050
|
+
{
|
|
1051
|
+
"arms": [
|
|
1052
|
+
"web"
|
|
1053
|
+
],
|
|
1054
|
+
"kind": "no-payload",
|
|
1055
|
+
"actual": "renders a static window of options and never dispatches — the case has no onAction.",
|
|
1056
|
+
"reason": "Unimplemented on web rather than divergent. Surfaced by the missing-site assertion; the other four it reported were blind spots in the scan, now fixed."
|
|
1057
|
+
}
|
|
1058
|
+
]
|
|
1059
|
+
}
|
|
1060
|
+
],
|
|
747
1061
|
"propsSource": "export interface WheelPickerProps {\n options?: PickerOption[];\n /** Literal option id, or binding-aware `$service:`/`$item.` live id. */\n selectedId?: string;\n onChange?: Action;\n optionsPath?: string;\n}"
|
|
748
1062
|
},
|
|
749
1063
|
{
|
|
@@ -753,6 +1067,25 @@
|
|
|
753
1067
|
"bindings": [
|
|
754
1068
|
"valuePath"
|
|
755
1069
|
],
|
|
1070
|
+
"dispatches": [
|
|
1071
|
+
{
|
|
1072
|
+
"callback": "onChange",
|
|
1073
|
+
"key": "value",
|
|
1074
|
+
"encoding": "string",
|
|
1075
|
+
"note": "Hex, including the leading '#'.",
|
|
1076
|
+
"divergences": [
|
|
1077
|
+
{
|
|
1078
|
+
"arms": [
|
|
1079
|
+
"web",
|
|
1080
|
+
"rn"
|
|
1081
|
+
],
|
|
1082
|
+
"kind": "params-only",
|
|
1083
|
+
"actual": "writes the value into `params` ONLY, never `args`.",
|
|
1084
|
+
"reason": "Survives only because the Tier-5 hosts merge params into args. Any consumer reading `args` — which rule 1 says is the payload map — gets nothing."
|
|
1085
|
+
}
|
|
1086
|
+
]
|
|
1087
|
+
}
|
|
1088
|
+
],
|
|
756
1089
|
"propsSource": "export interface ColorPickerProps {\n value?: string;\n onChange?: Action;\n swatches?: string[];\n label?: string;\n valuePath?: string;\n}"
|
|
757
1090
|
},
|
|
758
1091
|
{
|
|
@@ -762,6 +1095,44 @@
|
|
|
762
1095
|
"bindings": [
|
|
763
1096
|
"valuePath"
|
|
764
1097
|
],
|
|
1098
|
+
"dispatches": [
|
|
1099
|
+
{
|
|
1100
|
+
"callback": "onChange",
|
|
1101
|
+
"key": "value",
|
|
1102
|
+
"encoding": "string",
|
|
1103
|
+
"note": "The assembled code. Only RN dispatches today; web, iOS and Android render the boxes read-only.",
|
|
1104
|
+
"divergences": [
|
|
1105
|
+
{
|
|
1106
|
+
"arms": [
|
|
1107
|
+
"web",
|
|
1108
|
+
"ios",
|
|
1109
|
+
"android"
|
|
1110
|
+
],
|
|
1111
|
+
"kind": "no-payload",
|
|
1112
|
+
"actual": "renders the digit boxes without wiring onChange — the control dispatches nothing.",
|
|
1113
|
+
"reason": "Unimplemented rather than divergent, but it must be declared: leaving OtpInput out of the contract is what let the gate whitelist RN's payload without checking it."
|
|
1114
|
+
}
|
|
1115
|
+
]
|
|
1116
|
+
},
|
|
1117
|
+
{
|
|
1118
|
+
"callback": "onComplete",
|
|
1119
|
+
"key": "value",
|
|
1120
|
+
"encoding": "string",
|
|
1121
|
+
"note": "Fires once the last box is filled, carrying the same assembled code as onChange. Declared separately because it is a second public callback — leaving it out left one outside the contract.",
|
|
1122
|
+
"divergences": [
|
|
1123
|
+
{
|
|
1124
|
+
"arms": [
|
|
1125
|
+
"web",
|
|
1126
|
+
"ios",
|
|
1127
|
+
"android"
|
|
1128
|
+
],
|
|
1129
|
+
"kind": "no-payload",
|
|
1130
|
+
"actual": "renders the digit boxes without wiring onChange — the control dispatches nothing.",
|
|
1131
|
+
"reason": "Unimplemented rather than divergent, but it must be declared: leaving OtpInput out of the contract is what let the gate whitelist RN's payload without checking it."
|
|
1132
|
+
}
|
|
1133
|
+
]
|
|
1134
|
+
}
|
|
1135
|
+
],
|
|
765
1136
|
"notImplemented": {
|
|
766
1137
|
"props": [
|
|
767
1138
|
"onChange",
|
|
@@ -180,6 +180,36 @@ The two share a leaf name and mean opposite things — descriptor side takes the
|
|
|
180
180
|
registry takes the HEX — so a validator error about `shadows.card.color` is not this table
|
|
181
181
|
contradicting itself.
|
|
182
182
|
|
|
183
|
+
## What a control sends (action payloads)
|
|
184
|
+
|
|
185
|
+
A component's props say what it READS. This says what it SENDS: the value a control injects
|
|
186
|
+
into the `Action` it dispatches, so a Tier-5 reducer can be written from this page instead of
|
|
187
|
+
from trial and error.
|
|
188
|
+
|
|
189
|
+
1. **The payload is in `args`.** `Action.args` is a `Value`, so it holds the real type
|
|
190
|
+
(bool / int / double / array), and every host merges `{...params, ...args}` with `args`
|
|
191
|
+
winning. Read `args` first.
|
|
192
|
+
2. **`params` may mirror it, but only for a string.** `Action.params` is declared
|
|
193
|
+
`map<string,string>` in the descriptor schema; a number, bool or array there is a type
|
|
194
|
+
violation even where JavaScript tolerates it.
|
|
195
|
+
3. **The key and the encoding below are normative.** A renderer that sends a different key —
|
|
196
|
+
or no payload at all — is a divergence, and every one that exists today is printed with the
|
|
197
|
+
component it affects, because that is precisely the case a reducer has to be written around.
|
|
198
|
+
|
|
199
|
+
Every row here is generated from the platform's own payload contract and re-checked against
|
|
200
|
+
all four renderers on every build, so it cannot drift from what the components actually send.
|
|
201
|
+
|
|
202
|
+
**What it covers, and what it does not.** The contract is about **value controls** — the ones
|
|
203
|
+
whose callback carries the thing the user chose or typed. A value callback absent from a
|
|
204
|
+
component's block injects nothing: it dispatches the author's action verbatim, so any
|
|
205
|
+
discriminator must come from the `args` the author wrote (`Chip.onRemove` and
|
|
206
|
+
`DisclosureGroup.onToggle` are the two that work this way today).
|
|
207
|
+
|
|
208
|
+
That is NOT a statement about every action a component can fire. Gesture and event callbacks —
|
|
209
|
+
`Map.onMapTap` / `onMarkerTap`, a media player's progress events — inject payloads of their
|
|
210
|
+
own and are outside this contract, so read the component's own prop documentation for those
|
|
211
|
+
rather than concluding from a missing row here that nothing arrives.
|
|
212
|
+
|
|
183
213
|
## Props (verbatim from schema.ts)
|
|
184
214
|
|
|
185
215
|
### Core
|
|
@@ -379,6 +409,12 @@ export type TextProps = TextContent & {
|
|
|
379
409
|
* the current left-to-right behavior. Both renderers honor it (parity). */
|
|
380
410
|
textDirection?: "auto" | "ltr" | "rtl";
|
|
381
411
|
decoration?: "underline" | "strikethrough";
|
|
412
|
+
/** EXTRA SPACE BETWEEN LINES, in points/px — added ON TOP of the line height the `style`
|
|
413
|
+
* token already sets. It is NOT a multiple: `lineSpacing: 6` means six points of extra
|
|
414
|
+
* leading, not six times the font size, and every renderer agrees on that. To
|
|
415
|
+
* change the line height as a ratio of the font size, set the typography token's `line`
|
|
416
|
+
* instead; that is where a body style's 1.4/1.7 lives. Absent/0 = the token's own line
|
|
417
|
+
* height, which is the right answer for almost every screen. */
|
|
382
418
|
lineSpacing?: number;
|
|
383
419
|
/** Per-item gate (usually an `$item.` field pre-substituted per row): when it
|
|
384
420
|
* resolves empty, the text collapses to zero size instead of taking layout. */
|
|
@@ -519,6 +555,21 @@ export interface TextFieldProps {
|
|
|
519
555
|
}
|
|
520
556
|
```
|
|
521
557
|
|
|
558
|
+
**Dispatches** — see [What a control sends](#what-a-control-sends-action-payloads):
|
|
559
|
+
|
|
560
|
+
| callback | key | type |
|
|
561
|
+
|---|---|---|
|
|
562
|
+
| `onChange` | `value` | string |
|
|
563
|
+
| `onSubmit` | `value` | string |
|
|
564
|
+
|
|
565
|
+
> `onChange` — The typed text, clamped to maxLength by the renderer.
|
|
566
|
+
|
|
567
|
+
> ⚠️ **`onChange` on ios + android + web — ALSO sends:** additionally injects `fieldId` when the text field is inside a Form, so one worker action can serve several fields. Additive, not conflicting, and shipped bundles already duck-type it. Every arm guards on the field actually being registered in a Form, so a standalone TextField still sends no `fieldId`. Still a divergence rather than a promoted contract key because RN does not send it and the contract does not require it.
|
|
568
|
+
|
|
569
|
+
> `onSubmit` — The field's current text at return-key time.
|
|
570
|
+
|
|
571
|
+
> ⚠️ **`onSubmit` on ios + android + web — ALSO sends:** additionally injects `fieldId` when the text field is inside a Form, so one worker action can serve several fields. Additive, not conflicting, and shipped bundles already duck-type it. Every arm guards on the field actually being registered in a Form, so a standalone TextField still sends no `fieldId`. Still a divergence rather than a promoted contract key because RN does not send it and the contract does not require it.
|
|
572
|
+
|
|
522
573
|
#### Toggle
|
|
523
574
|
|
|
524
575
|
```ts
|
|
@@ -543,6 +594,16 @@ export interface ToggleProps {
|
|
|
543
594
|
}
|
|
544
595
|
```
|
|
545
596
|
|
|
597
|
+
**Dispatches** — see [What a control sends](#what-a-control-sends-action-payloads):
|
|
598
|
+
|
|
599
|
+
| callback | key | type |
|
|
600
|
+
|---|---|---|
|
|
601
|
+
| `onChange` | `value` | boolean |
|
|
602
|
+
|
|
603
|
+
> `onChange` — The NEXT state, not the previous one.
|
|
604
|
+
|
|
605
|
+
> ⚠️ **`onChange` on ios — ALSO sends:** additionally injects `fieldId` when the control is inside a Form — via bindingForBool, for a Toggle nested in a FormField.
|
|
606
|
+
|
|
546
607
|
#### Stepper
|
|
547
608
|
|
|
548
609
|
```ts
|
|
@@ -556,6 +617,16 @@ export interface StepperProps {
|
|
|
556
617
|
}
|
|
557
618
|
```
|
|
558
619
|
|
|
620
|
+
**Dispatches** — see [What a control sends](#what-a-control-sends-action-payloads):
|
|
621
|
+
|
|
622
|
+
| callback | key | type |
|
|
623
|
+
|---|---|---|
|
|
624
|
+
| `onChange` | `value` | int |
|
|
625
|
+
|
|
626
|
+
> `onChange` — The clamped next value. A no-change step must not dispatch.
|
|
627
|
+
|
|
628
|
+
> ⚠️ **`onChange` on web + rn — DIVERGES:** mirrors the value into `params` as a NUMBER. Violates rule 2 — `Action.params` is `map<string,string>`. Harmless through the JS hosts, wrong on the wire.
|
|
629
|
+
|
|
559
630
|
#### Avatar
|
|
560
631
|
|
|
561
632
|
```ts
|
|
@@ -712,6 +783,16 @@ export interface PickerProps {
|
|
|
712
783
|
|
|
713
784
|
> ⚠️ **Implemented, but these props are inert on ios:** `optionsPath`, `placeholder` — `optionsPath` does not bind options; `placeholder` is not shown.
|
|
714
785
|
|
|
786
|
+
**Dispatches** — see [What a control sends](#what-a-control-sends-action-payloads):
|
|
787
|
+
|
|
788
|
+
| callback | key | type |
|
|
789
|
+
|---|---|---|
|
|
790
|
+
| `onChange` | `selectedId` | string |
|
|
791
|
+
|
|
792
|
+
> `onChange` — The chosen option's id — an enum the reducer switches on, never an index.
|
|
793
|
+
|
|
794
|
+
> ⚠️ **`onChange` on ios — DIVERGES:** inside a FormField, the non-text kinds store into the form bag and RETURN without dispatching — the renderer's own code. A formField({kind:'picker'}) onChange is a dead action on iOS while every other arm fires it.
|
|
795
|
+
|
|
715
796
|
#### DatePicker
|
|
716
797
|
|
|
717
798
|
```ts
|
|
@@ -728,6 +809,20 @@ export interface DatePickerProps {
|
|
|
728
809
|
|
|
729
810
|
> ⚠️ **Implemented, but these props are inert on ios:** `mode`, `minUnix`, `maxUnix`, `valuePath` — The picker itself renders as a native date wheel — these four props are inert: `mode: "time"` still renders a date wheel; `minUnix`/`maxUnix` do not clamp; `valuePath` does not bind.
|
|
730
811
|
|
|
812
|
+
**Dispatches** — see [What a control sends](#what-a-control-sends-action-payloads):
|
|
813
|
+
|
|
814
|
+
| callback | key | type |
|
|
815
|
+
|---|---|---|
|
|
816
|
+
| `onChange` | `value` | unix-seconds-string |
|
|
817
|
+
|
|
818
|
+
> `onChange` — Unix SECONDS, string-encoded so the params mirror stays legal. iOS ships `valueUnix` as an int instead; RN ships nothing. The normative key is `value` because that is what three of four arms already use for every other scalar control.
|
|
819
|
+
|
|
820
|
+
> ⚠️ **`onChange` on ios — DIVERGES:** sends `valueUnix` as an int instead of the contract's `value`. Only web conforms. This is the widest split in the vocabulary and the clearest argument for the file.
|
|
821
|
+
|
|
822
|
+
> ⚠️ **`onChange` on rn — DIVERGES:** dispatches the bare action with no date at all.
|
|
823
|
+
|
|
824
|
+
> ⚠️ **`onChange` on ios — DIVERGES:** inside a FormField, the non-text kinds store into the form bag and RETURN without dispatching — the renderer's own code. A formField({kind:'picker'}) onChange is a dead action on iOS while every other arm fires it.
|
|
825
|
+
|
|
731
826
|
### State envelopes
|
|
732
827
|
|
|
733
828
|
#### StatefulSection
|
|
@@ -793,6 +888,17 @@ export interface SearchFieldProps {
|
|
|
793
888
|
}
|
|
794
889
|
```
|
|
795
890
|
|
|
891
|
+
**Dispatches** — see [What a control sends](#what-a-control-sends-action-payloads):
|
|
892
|
+
|
|
893
|
+
| callback | key | type |
|
|
894
|
+
|---|---|---|
|
|
895
|
+
| `onChange` | `value` | string |
|
|
896
|
+
| `onSubmit` | `value` | string |
|
|
897
|
+
|
|
898
|
+
> `onChange` — Debounced by `debounceMs` before dispatch.
|
|
899
|
+
|
|
900
|
+
> `onSubmit` — These two rows share a control and key, so they are NOT independently gated: a payload observation cannot say which callback produced it, and a helper-mediated dispatch does not name the callback at the write site at all. The gate proves the callback is still referenced in the component that dispatches, not that this callback carries the payload.
|
|
901
|
+
|
|
796
902
|
#### FilterChips
|
|
797
903
|
|
|
798
904
|
```ts
|
|
@@ -807,6 +913,16 @@ export interface FilterChipsProps {
|
|
|
807
913
|
}
|
|
808
914
|
```
|
|
809
915
|
|
|
916
|
+
**Dispatches** — see [What a control sends](#what-a-control-sends-action-payloads):
|
|
917
|
+
|
|
918
|
+
| callback | key | type |
|
|
919
|
+
|---|---|---|
|
|
920
|
+
| `onChange` | `selectedIds` | string-array |
|
|
921
|
+
|
|
922
|
+
> `onChange` — The full selection AFTER the toggle, not the chip that moved.
|
|
923
|
+
|
|
924
|
+
> ⚠️ **`onChange` on rn — DIVERGES:** mirrors `selectedIds` into `params` as an ARRAY. Rule-2 violation.
|
|
925
|
+
|
|
810
926
|
### Navigation primitives
|
|
811
927
|
|
|
812
928
|
#### Tabs
|
|
@@ -827,6 +943,12 @@ export interface TabsProps {
|
|
|
827
943
|
|
|
828
944
|
> ⚠️ **Implemented, but these props are inert on ios:** `style` — The tab bar itself renders — only `style` is inert: `swipe` still renders as top tabs.
|
|
829
945
|
|
|
946
|
+
**Dispatches** — see [What a control sends](#what-a-control-sends-action-payloads):
|
|
947
|
+
|
|
948
|
+
| callback | key | type |
|
|
949
|
+
|---|---|---|
|
|
950
|
+
| `onChange` | `selectedId` | string |
|
|
951
|
+
|
|
830
952
|
#### SegmentedControl
|
|
831
953
|
|
|
832
954
|
```ts
|
|
@@ -840,6 +962,12 @@ export interface SegmentedControlProps {
|
|
|
840
962
|
}
|
|
841
963
|
```
|
|
842
964
|
|
|
965
|
+
**Dispatches** — see [What a control sends](#what-a-control-sends-action-payloads):
|
|
966
|
+
|
|
967
|
+
| callback | key | type |
|
|
968
|
+
|---|---|---|
|
|
969
|
+
| `onChange` | `selectedId` | string |
|
|
970
|
+
|
|
843
971
|
#### Carousel
|
|
844
972
|
|
|
845
973
|
```ts
|
|
@@ -1056,6 +1184,20 @@ export interface SliderProps {
|
|
|
1056
1184
|
|
|
1057
1185
|
> ⚠️ **Implemented, but these props are inert on ios:** `debounceMs` — `debounceMs` does not throttle the onChange stream.
|
|
1058
1186
|
|
|
1187
|
+
**Dispatches** — see [What a control sends](#what-a-control-sends-action-payloads):
|
|
1188
|
+
|
|
1189
|
+
| callback | key | type |
|
|
1190
|
+
|---|---|---|
|
|
1191
|
+
| `onChange` | `value` | double |
|
|
1192
|
+
|
|
1193
|
+
> `onChange` — Range mode additionally sends `upperValue`.
|
|
1194
|
+
|
|
1195
|
+
> ⚠️ **`onChange` on web — DIVERGES:** dispatches the bare action — the renderer's own code — with no injected value.
|
|
1196
|
+
|
|
1197
|
+
> ⚠️ **`onChange` on rn — DIVERGES:** renders a static visual and never dispatches at all. RN Slider is unimplemented rather than divergent; listed so the gap is counted, not hidden.
|
|
1198
|
+
|
|
1199
|
+
> ⚠️ **`onChange` on ios + android — ALSO sends:** range mode additionally sends `upperValue` beside `value`. Additive, and the contract's Slider note already describes it. Same problem as groupId: allowed by a hard-coded list rather than declared.
|
|
1200
|
+
|
|
1059
1201
|
#### SelectionControl
|
|
1060
1202
|
|
|
1061
1203
|
```ts
|
|
@@ -1074,6 +1216,20 @@ export interface SelectionControlProps {
|
|
|
1074
1216
|
}
|
|
1075
1217
|
```
|
|
1076
1218
|
|
|
1219
|
+
**Dispatches** — see [What a control sends](#what-a-control-sends-action-payloads):
|
|
1220
|
+
|
|
1221
|
+
| callback | key | type |
|
|
1222
|
+
|---|---|---|
|
|
1223
|
+
| `onChange` | `value` | boolean |
|
|
1224
|
+
|
|
1225
|
+
> `onChange` — The next checked state. iOS additionally sends `groupId` for radio groups.
|
|
1226
|
+
|
|
1227
|
+
> ⚠️ **`onChange` on web — DIVERGES:** dispatches the bare action — the renderer's own code — with no injected value.
|
|
1228
|
+
|
|
1229
|
+
> ⚠️ **`onChange` on rn — DIVERGES:** mirrors the value into `params` as a BOOLEAN.
|
|
1230
|
+
|
|
1231
|
+
> ⚠️ **`onChange` on ios — ALSO sends:** additionally sends `groupId` so a radio group can identify which set changed. Additive, not conflicting. It was previously hard-coded as an allowed key in the gate, which put it outside the contract — the one place this PR claims is the single source of truth.
|
|
1232
|
+
|
|
1077
1233
|
#### ActivityIndicator
|
|
1078
1234
|
|
|
1079
1235
|
```ts
|
|
@@ -1200,6 +1356,16 @@ export interface ChipsInputProps {
|
|
|
1200
1356
|
|
|
1201
1357
|
> ⚠️ **Implemented, but these props are inert on ios:** `onAdd` — `onAdd` never dispatches.
|
|
1202
1358
|
|
|
1359
|
+
**Dispatches** — see [What a control sends](#what-a-control-sends-action-payloads):
|
|
1360
|
+
|
|
1361
|
+
| callback | key | type |
|
|
1362
|
+
|---|---|---|
|
|
1363
|
+
| `onRemove` | `value` | string |
|
|
1364
|
+
|
|
1365
|
+
> `onRemove` — The removed chip's value.
|
|
1366
|
+
|
|
1367
|
+
> ⚠️ **`onRemove` on web — DIVERGES:** dispatches the bare action — the renderer's own code — with no injected value.
|
|
1368
|
+
|
|
1203
1369
|
### Structure & disclosure
|
|
1204
1370
|
|
|
1205
1371
|
#### Section
|
|
@@ -1296,6 +1462,20 @@ export interface RatingProps {
|
|
|
1296
1462
|
}
|
|
1297
1463
|
```
|
|
1298
1464
|
|
|
1465
|
+
**Dispatches** — see [What a control sends](#what-a-control-sends-action-payloads):
|
|
1466
|
+
|
|
1467
|
+
| callback | key | type |
|
|
1468
|
+
|---|---|---|
|
|
1469
|
+
| `onChange` | `value` | int |
|
|
1470
|
+
|
|
1471
|
+
> `onChange` — 1-based star index.
|
|
1472
|
+
|
|
1473
|
+
> ⚠️ **`onChange` on web + rn — DIVERGES:** writes the value into `params` ONLY, never `args`. Survives only because the Tier-5 hosts merge params into args. Any consumer reading `args` — which rule 1 says is the payload map — gets nothing.
|
|
1474
|
+
|
|
1475
|
+
> ⚠️ **`onChange` on ios — DIVERGES:** sends `value` as .double where the contract says int.
|
|
1476
|
+
|
|
1477
|
+
> ⚠️ **`onChange` on web + rn — DIVERGES:** stringifies the 1-based index into `params` (`String(i + 1)`) for a control the contract declares `int`. Rule 2 permits a params mirror only for string-encoded values, and Rating is int. The earlier expression heuristic missed it because `String(i + 1)` does not look like a literal — it surfaced only once rule 2 keyed off the declared encoding.
|
|
1478
|
+
|
|
1299
1479
|
#### Chart
|
|
1300
1480
|
|
|
1301
1481
|
```ts
|
|
@@ -1463,6 +1643,16 @@ export interface WheelPickerProps {
|
|
|
1463
1643
|
}
|
|
1464
1644
|
```
|
|
1465
1645
|
|
|
1646
|
+
**Dispatches** — see [What a control sends](#what-a-control-sends-action-payloads):
|
|
1647
|
+
|
|
1648
|
+
| callback | key | type |
|
|
1649
|
+
|---|---|---|
|
|
1650
|
+
| `onChange` | `selectedId` | string |
|
|
1651
|
+
|
|
1652
|
+
> ⚠️ **`onChange` on rn — DIVERGES:** writes `params.value = String(id)` but `args.selectedId = id` — a different key in each map. Conforms on `args` (rule 1) but the params mirror uses the wrong key, so a params-reading consumer sees a key no other arm sends.
|
|
1653
|
+
|
|
1654
|
+
> ⚠️ **`onChange` on web — DIVERGES:** renders a static window of options and never dispatches — the case has no onAction. Unimplemented on web rather than divergent. Surfaced by the missing-site assertion; the other four it reported were blind spots in the scan, now fixed.
|
|
1655
|
+
|
|
1466
1656
|
#### ColorPicker
|
|
1467
1657
|
|
|
1468
1658
|
```ts
|
|
@@ -1475,6 +1665,16 @@ export interface ColorPickerProps {
|
|
|
1475
1665
|
}
|
|
1476
1666
|
```
|
|
1477
1667
|
|
|
1668
|
+
**Dispatches** — see [What a control sends](#what-a-control-sends-action-payloads):
|
|
1669
|
+
|
|
1670
|
+
| callback | key | type |
|
|
1671
|
+
|---|---|---|
|
|
1672
|
+
| `onChange` | `value` | string |
|
|
1673
|
+
|
|
1674
|
+
> `onChange` — Hex, including the leading '#'.
|
|
1675
|
+
|
|
1676
|
+
> ⚠️ **`onChange` on web + rn — DIVERGES:** writes the value into `params` ONLY, never `args`. Survives only because the Tier-5 hosts merge params into args. Any consumer reading `args` — which rule 1 says is the payload map — gets nothing.
|
|
1677
|
+
|
|
1478
1678
|
#### OtpInput
|
|
1479
1679
|
|
|
1480
1680
|
```ts
|
|
@@ -1494,6 +1694,21 @@ export interface OtpInputProps {
|
|
|
1494
1694
|
|
|
1495
1695
|
> ⚠️ **Implemented, but these props are inert on ios:** `onChange`, `onComplete` — Neither callback dispatches.
|
|
1496
1696
|
|
|
1697
|
+
**Dispatches** — see [What a control sends](#what-a-control-sends-action-payloads):
|
|
1698
|
+
|
|
1699
|
+
| callback | key | type |
|
|
1700
|
+
|---|---|---|
|
|
1701
|
+
| `onChange` | `value` | string |
|
|
1702
|
+
| `onComplete` | `value` | string |
|
|
1703
|
+
|
|
1704
|
+
> `onChange` — The assembled code. Only RN dispatches today; web, iOS and Android render the boxes read-only.
|
|
1705
|
+
|
|
1706
|
+
> ⚠️ **`onChange` on web + ios + android — DIVERGES:** renders the digit boxes without wiring onChange — the control dispatches nothing. Unimplemented rather than divergent, but it must be declared: leaving OtpInput out of the contract is what let the gate whitelist RN's payload without checking it.
|
|
1707
|
+
|
|
1708
|
+
> `onComplete` — Fires once the last box is filled, carrying the same assembled code as onChange. Declared separately because it is a second public callback — leaving it out left one outside the contract.
|
|
1709
|
+
|
|
1710
|
+
> ⚠️ **`onComplete` on web + ios + android — DIVERGES:** renders the digit boxes without wiring onChange — the control dispatches nothing. Unimplemented rather than divergent, but it must be declared: leaving OtpInput out of the contract is what let the gate whitelist RN's payload without checking it.
|
|
1711
|
+
|
|
1497
1712
|
### Cross-cutting & media
|
|
1498
1713
|
|
|
1499
1714
|
#### Fab
|
|
@@ -14,6 +14,13 @@ lists) and `apps/family-cart` (cart list). The `examples/dynamic-list.*` pair th
|
|
|
14
14
|
card grids). `Carousel` and `Menu` take `itemsPath` the same way. Do **not** hand-roll
|
|
15
15
|
a "VStack of Cards" — that renders only static items and can't bind live data.
|
|
16
16
|
|
|
17
|
+
**`Grid` is not a `LazyList` with one column.** It shares `itemsPath` and the row
|
|
18
|
+
template, and nothing else: the gesture props in §5 (`onPullToRefresh`, `pagination`)
|
|
19
|
+
and `stickyHeader`/`rowSwipeActions` are declared on `LazyListProps` only. A key `Grid`
|
|
20
|
+
does not declare is **silently dropped** by the native decoders (`ignoreUnknownKeys =
|
|
21
|
+
true`) — `himi validate` catches it and names the declared set, but nothing catches it
|
|
22
|
+
at runtime. If you need pull-to-refresh or load-more, use `LazyList`.
|
|
23
|
+
|
|
17
24
|
## The shape (canonical off-repo)
|
|
18
25
|
|
|
19
26
|
```jsonc
|
|
@@ -70,6 +77,25 @@ including derived presentation fields (`initial` for an avatar letter, `hasImage
|
|
|
70
77
|
booleans, pre-formatted date lines). Vertical-client responses are raw REST JSON —
|
|
71
78
|
map them defensively; typed-looking fields can be absent at runtime.
|
|
72
79
|
|
|
80
|
+
**One carve-out, and it is the useful one: a NESTED REPEATER may bind to the row's own
|
|
81
|
+
array.** "Flat fields" is about *scalar* bindings — `$item.price`, not
|
|
82
|
+
`$item.priceData.formatted.amount`. A `Carousel`/`LazyList`/`Grid` *inside* a row template
|
|
83
|
+
may set `itemsPath: "$item.<field>"` and render that element's own array, with its own
|
|
84
|
+
`$item.` scope one level down. All four renderers resolve it as of #2265; before that fix web
|
|
85
|
+
rendered zero rows silently, which is why older guidance says it cannot be done. So a photo
|
|
86
|
+
carousel per feed row **is** expressible:
|
|
87
|
+
|
|
88
|
+
```jsonc
|
|
89
|
+
{ "id": "photos_$item.id", "body": { "package": "himalaya", "type": "Carousel",
|
|
90
|
+
"props": { "itemsPath": "$item.images", // ← the ROW's array, not a $service: path
|
|
91
|
+
"items": [ { "id": "photo_$item.url", "body": { "package": "himalaya",
|
|
92
|
+
"type": "Image", "props": { "url": "$item.url" } } } ] } } }
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
`normalize()` still has to put a real array on the row (`images: []`, never `null`), and the
|
|
96
|
+
inner template obeys every rule above — flat scalar fields, no strict-enum bindings. Keep the
|
|
97
|
+
nesting to one level: a carousel of carousels is legal and unreadable.
|
|
98
|
+
|
|
73
99
|
## 3. The descriptor binds the array
|
|
74
100
|
|
|
75
101
|
```jsonc
|
|
@@ -130,8 +156,10 @@ full state-machine idiom: `himi docs recipe-four-state`.
|
|
|
130
156
|
|
|
131
157
|
## 5. Refresh + load-more gestures (RFC 0008)
|
|
132
158
|
|
|
133
|
-
`LazyList`
|
|
134
|
-
|
|
159
|
+
`LazyList` takes two optional gesture props — both are plain action wiring, no new
|
|
160
|
+
worker concepts. **`LazyList` only**: `GridProps` declares `items`, `columns`,
|
|
161
|
+
`columnsMedium`, `columnsExpanded`, `spacing`, `itemsPath` and `staggerMs`, and the
|
|
162
|
+
native decoders drop anything else without a word:
|
|
135
163
|
|
|
136
164
|
```jsonc
|
|
137
165
|
"props": {
|
|
@@ -159,6 +187,21 @@ plain action wiring, no new worker concepts:
|
|
|
159
187
|
- APIs that don't page (or v1 fallbacks without cursors) simply never set
|
|
160
188
|
`nextCursor` — the gesture props are safe to leave in place.
|
|
161
189
|
|
|
190
|
+
## 6. Tapping a row into a detail screen
|
|
191
|
+
|
|
192
|
+
A row usually opens something, and the id space is usually unbounded (every product, every
|
|
193
|
+
listing). **Use ONE canonical detail screen and pass the id as a nav param** — the row's action is
|
|
194
|
+
`{ "actionId": "himi.open", "params": { "screenId": "property_detail", "id": "$item.id" } }`, and
|
|
195
|
+
the detail worker reads `himalaya.input.id`.
|
|
196
|
+
|
|
197
|
+
The alternative, a **templated route** (`screens.templated` with a `pattern`), is for a *bounded*
|
|
198
|
+
catalog and nothing else. It resolves live for any id while you are developing, which is what makes it
|
|
199
|
+
look like the general answer — but a production OTA release serves only `screens.exact`, so the
|
|
200
|
+
build materializes one screen per id from the route's `seedIds` and a record created after that
|
|
201
|
+
push is simply not in the release. Reach for it when the set is small and known (a handful of
|
|
202
|
+
categories); reach for the canonical screen whenever the ids come from a live collection.
|
|
203
|
+
Mechanics and the prod caveat in full: `himi docs tier-5-add-a-screen`.
|
|
204
|
+
|
|
162
205
|
## Gotchas (each of these has cost an agent a full push→drive cycle)
|
|
163
206
|
|
|
164
207
|
- **`itemsPath` must bind to an array.** `himi validate` errors if the bound path's
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wix/himalaya-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.945.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": [
|
|
@@ -64,5 +64,5 @@
|
|
|
64
64
|
"artifactId": "himalaya-cli"
|
|
65
65
|
}
|
|
66
66
|
},
|
|
67
|
-
"falconPackageHash": "
|
|
67
|
+
"falconPackageHash": "7c47343c4a5027aabc50c963502aa5034b2828f8bf4e1e133fec1b94"
|
|
68
68
|
}
|