@getrheo/flow-runtime 1.0.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.
Files changed (126) hide show
  1. package/dist/agentPrompt/index.d.ts +72 -0
  2. package/dist/agentPrompt/index.js +739 -0
  3. package/dist/agentPrompt/index.js.map +1 -0
  4. package/dist/aiFlowGenerationMerge.d.ts +32 -0
  5. package/dist/aiFlowGenerationMerge.js +120 -0
  6. package/dist/aiFlowGenerationMerge.js.map +1 -0
  7. package/dist/animations.d.ts +110 -0
  8. package/dist/animations.js +312 -0
  9. package/dist/animations.js.map +1 -0
  10. package/dist/assignment.d.ts +7 -0
  11. package/dist/assignment.js +25 -0
  12. package/dist/assignment.js.map +1 -0
  13. package/dist/brandGradient.d.ts +57 -0
  14. package/dist/brandGradient.js +137 -0
  15. package/dist/brandGradient.js.map +1 -0
  16. package/dist/brandGradientManifestIssues.d.ts +11 -0
  17. package/dist/brandGradientManifestIssues.js +302 -0
  18. package/dist/brandGradientManifestIssues.js.map +1 -0
  19. package/dist/buildFlowPreview.d.ts +7 -0
  20. package/dist/buildFlowPreview.js +81 -0
  21. package/dist/buildFlowPreview.js.map +1 -0
  22. package/dist/buttonVariantChrome.d.ts +26 -0
  23. package/dist/buttonVariantChrome.js +59 -0
  24. package/dist/buttonVariantChrome.js.map +1 -0
  25. package/dist/checkboxGlyphStyle.d.ts +31 -0
  26. package/dist/checkboxGlyphStyle.js +241 -0
  27. package/dist/checkboxGlyphStyle.js.map +1 -0
  28. package/dist/choiceOptionSelection.d.ts +11 -0
  29. package/dist/choiceOptionSelection.js +120 -0
  30. package/dist/choiceOptionSelection.js.map +1 -0
  31. package/dist/colorAlpha.d.ts +8 -0
  32. package/dist/colorAlpha.js +48 -0
  33. package/dist/colorAlpha.js.map +1 -0
  34. package/dist/counterLayer.d.ts +42 -0
  35. package/dist/counterLayer.js +95 -0
  36. package/dist/counterLayer.js.map +1 -0
  37. package/dist/decisionEval.d.ts +27 -0
  38. package/dist/decisionEval.js +197 -0
  39. package/dist/decisionEval.js.map +1 -0
  40. package/dist/dropShadow.d.ts +26 -0
  41. package/dist/dropShadow.js +76 -0
  42. package/dist/dropShadow.js.map +1 -0
  43. package/dist/emailPasswordAuthValidation.d.ts +16 -0
  44. package/dist/emailPasswordAuthValidation.js +25 -0
  45. package/dist/emailPasswordAuthValidation.js.map +1 -0
  46. package/dist/flowBuilderRules.d.ts +15 -0
  47. package/dist/flowBuilderRules.js +368 -0
  48. package/dist/flowBuilderRules.js.map +1 -0
  49. package/dist/flowGraph.d.ts +19 -0
  50. package/dist/flowGraph.js +373 -0
  51. package/dist/flowGraph.js.map +1 -0
  52. package/dist/hyperlinkLabel.d.ts +19 -0
  53. package/dist/hyperlinkLabel.js +232 -0
  54. package/dist/hyperlinkLabel.js.map +1 -0
  55. package/dist/index.d.ts +48 -0
  56. package/dist/index.js +4200 -0
  57. package/dist/index.js.map +1 -0
  58. package/dist/interpolateTemplate.d.ts +44 -0
  59. package/dist/interpolateTemplate.js +188 -0
  60. package/dist/interpolateTemplate.js.map +1 -0
  61. package/dist/layerRotate.d.ts +10 -0
  62. package/dist/layerRotate.js +9 -0
  63. package/dist/layerRotate.js.map +1 -0
  64. package/dist/layerTypography.d.ts +36 -0
  65. package/dist/layerTypography.js +68 -0
  66. package/dist/layerTypography.js.map +1 -0
  67. package/dist/layers.d.ts +69 -0
  68. package/dist/layers.js +257 -0
  69. package/dist/layers.js.map +1 -0
  70. package/dist/layout/index.d.ts +57 -0
  71. package/dist/layout/index.js +151 -0
  72. package/dist/layout/index.js.map +1 -0
  73. package/dist/manifestBillingSlice.d.ts +17 -0
  74. package/dist/manifestBillingSlice.js +102 -0
  75. package/dist/manifestBillingSlice.js.map +1 -0
  76. package/dist/prepareAiGeneratedScreen.d.ts +17 -0
  77. package/dist/prepareAiGeneratedScreen.js +99 -0
  78. package/dist/prepareAiGeneratedScreen.js.map +1 -0
  79. package/dist/publish-exports.json +166 -0
  80. package/dist/responsive/breakpoints.d.ts +34 -0
  81. package/dist/responsive/breakpoints.js +52 -0
  82. package/dist/responsive/breakpoints.js.map +1 -0
  83. package/dist/responsive/index.d.ts +8 -0
  84. package/dist/responsive/index.js +307 -0
  85. package/dist/responsive/index.js.map +1 -0
  86. package/dist/responsive/layerResolve.d.ts +43 -0
  87. package/dist/responsive/layerResolve.js +168 -0
  88. package/dist/responsive/layerResolve.js.map +1 -0
  89. package/dist/responsive/merge.d.ts +19 -0
  90. package/dist/responsive/merge.js +74 -0
  91. package/dist/responsive/merge.js.map +1 -0
  92. package/dist/responsive/previewSafeAreaInsets.d.ts +14 -0
  93. package/dist/responsive/previewSafeAreaInsets.js +24 -0
  94. package/dist/responsive/previewSafeAreaInsets.js.map +1 -0
  95. package/dist/responsive/screenContainerResolve.d.ts +11 -0
  96. package/dist/responsive/screenContainerResolve.js +122 -0
  97. package/dist/responsive/screenContainerResolve.js.map +1 -0
  98. package/dist/responsive/screenShellInsets.d.ts +11 -0
  99. package/dist/responsive/screenShellInsets.js +26 -0
  100. package/dist/responsive/screenShellInsets.js.map +1 -0
  101. package/dist/restingMotion.d.ts +167 -0
  102. package/dist/restingMotion.js +484 -0
  103. package/dist/restingMotion.js.map +1 -0
  104. package/dist/rheoAgentManifestMerge.d.ts +33 -0
  105. package/dist/rheoAgentManifestMerge.js +55 -0
  106. package/dist/rheoAgentManifestMerge.js.map +1 -0
  107. package/dist/scaleInputStyle.d.ts +35 -0
  108. package/dist/scaleInputStyle.js +77 -0
  109. package/dist/scaleInputStyle.js.map +1 -0
  110. package/dist/scaleValidation.d.ts +9 -0
  111. package/dist/scaleValidation.js +21 -0
  112. package/dist/scaleValidation.js.map +1 -0
  113. package/dist/stateMachine.d.ts +105 -0
  114. package/dist/stateMachine.js +674 -0
  115. package/dist/stateMachine.js.map +1 -0
  116. package/dist/stepResponse-BXgoZ7o-.d.ts +112 -0
  117. package/dist/textInputValidation.d.ts +14 -0
  118. package/dist/textInputValidation.js +46 -0
  119. package/dist/textInputValidation.js.map +1 -0
  120. package/dist/translationPlaceholders.d.ts +9 -0
  121. package/dist/translationPlaceholders.js +52 -0
  122. package/dist/translationPlaceholders.js.map +1 -0
  123. package/dist/validation.d.ts +31 -0
  124. package/dist/validation.js +233 -0
  125. package/dist/validation.js.map +1 -0
  126. package/package.json +242 -0
@@ -0,0 +1,33 @@
1
+ import { FlowManifest } from '@getrheo/contracts/manifest';
2
+ import { Screen } from '@getrheo/contracts/screens';
3
+ import { DecisionNode } from '@getrheo/contracts/decisions';
4
+ import { ExternalSurfaceNode } from '@getrheo/contracts/externalSurfaces';
5
+
6
+ /**
7
+ * Partial manifest edit emitted by Rheo Agent in patch mode. Only the fields the
8
+ * model intends to change are present; everything else is preserved from the draft.
9
+ */
10
+ type RheoAgentManifestPatch = {
11
+ /** Upserted by `id`: replaces a matching screen, otherwise appended. */
12
+ screens?: Screen[];
13
+ /** Screen ids to drop from the draft. */
14
+ removeScreenIds?: string[];
15
+ /** Upserted by `id` when provided; omit to leave decision nodes unchanged. */
16
+ decisionNodes?: DecisionNode[];
17
+ /** Upserted by `id` when provided; omit to leave external surface nodes unchanged. */
18
+ externalSurfaceNodes?: ExternalSurfaceNode[];
19
+ /** Applied only when present. */
20
+ entryScreenId?: string;
21
+ /** Replaces the full key list only when present. */
22
+ sdkAttributeKeys?: string[];
23
+ };
24
+ /**
25
+ * Applies a Rheo Agent patch onto the authoritative draft, preserving `builderMeta`
26
+ * (canvas layout) and ensuring layout nodes exist for any newly added screens.
27
+ *
28
+ * Identity fields (`flowId`, `schemaVersion`, `version`, `defaultLocale`, `locales`,
29
+ * `theme`) are always taken from the draft and never accepted from the patch.
30
+ */
31
+ declare const mergeRheoAgentPatchIntoDraft: (draft: FlowManifest, patch: RheoAgentManifestPatch) => FlowManifest;
32
+
33
+ export { type RheoAgentManifestPatch, mergeRheoAgentPatchIntoDraft };
@@ -0,0 +1,55 @@
1
+ // src/aiFlowGenerationMerge.ts
2
+ var ensureLayoutNodes = (manifest) => {
3
+ const existing = new Map(
4
+ (manifest.builderMeta?.layout?.nodes ?? []).map((n) => [n.id, n])
5
+ );
6
+ const nodes = manifest.screens.map((s, idx) => {
7
+ const hit = existing.get(s.id);
8
+ if (hit) return { ...hit };
9
+ return { id: s.id, x: 80 + idx * 360, y: 120 };
10
+ });
11
+ return {
12
+ ...manifest,
13
+ builderMeta: {
14
+ ...manifest.builderMeta ?? {},
15
+ layout: {
16
+ ...manifest.builderMeta?.layout ?? {},
17
+ nodes
18
+ }
19
+ }
20
+ };
21
+ };
22
+
23
+ // src/rheoAgentManifestMerge.ts
24
+ var upsertById = (existing, incoming) => {
25
+ const incomingById = new Map(incoming.map((item) => [item.id, item]));
26
+ const merged = existing.map((item) => incomingById.get(item.id) ?? item);
27
+ const existingIds = new Set(existing.map((item) => item.id));
28
+ for (const item of incoming) {
29
+ if (!existingIds.has(item.id)) merged.push(item);
30
+ }
31
+ return merged;
32
+ };
33
+ var mergeRheoAgentPatchIntoDraft = (draft, patch) => {
34
+ const removeIds = new Set(patch.removeScreenIds ?? []);
35
+ let screens = patch.screens ? upsertById(draft.screens, patch.screens) : draft.screens;
36
+ if (removeIds.size > 0) {
37
+ screens = screens.filter((screen) => !removeIds.has(screen.id));
38
+ }
39
+ const decisionNodes = patch.decisionNodes ? upsertById(draft.decisionNodes ?? [], patch.decisionNodes) : draft.decisionNodes;
40
+ const externalSurfaceNodes = patch.externalSurfaceNodes ? upsertById(draft.externalSurfaceNodes ?? [], patch.externalSurfaceNodes) : draft.externalSurfaceNodes;
41
+ const merged = {
42
+ ...draft,
43
+ screens,
44
+ decisionNodes,
45
+ externalSurfaceNodes,
46
+ ...patch.entryScreenId !== void 0 ? { entryScreenId: patch.entryScreenId } : {},
47
+ ...patch.sdkAttributeKeys !== void 0 ? { sdkAttributeKeys: patch.sdkAttributeKeys } : {},
48
+ builderMeta: draft.builderMeta
49
+ };
50
+ return ensureLayoutNodes(merged);
51
+ };
52
+
53
+ export { mergeRheoAgentPatchIntoDraft };
54
+ //# sourceMappingURL=rheoAgentManifestMerge.js.map
55
+ //# sourceMappingURL=rheoAgentManifestMerge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/aiFlowGenerationMerge.ts","../src/rheoAgentManifestMerge.ts"],"names":[],"mappings":"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","file":"rheoAgentManifestMerge.js","sourcesContent":["import type { FlowManifest } from '@getrheo/contracts/manifest';\nimport type { Screen } from '@getrheo/contracts/screens';\n\n/** Blank seed from {@link buildBlankManifest}: single empty screen awaiting AI replacement. */\nexport const AI_FLOW_PLACEHOLDER_SCREEN_ID = 'scr_blank';\n\nconst bodyStackChildrenLen = (screen: Screen): number => {\n const body = screen.regions.body;\n if (body.kind !== 'stack') return -1;\n return body.children.length;\n};\n\nexport const isAiFlowPlaceholderManifest = (manifest: FlowManifest): boolean =>\n manifest.screens.length === 1 &&\n manifest.screens[0]?.id === AI_FLOW_PLACEHOLDER_SCREEN_ID &&\n bodyStackChildrenLen(manifest.screens[0]) === 0;\n\n/** Last screen on the default-next chain starting at entry (linear v1 assumption for AI expansion). */\nexport const findDefaultPathTailScreen = (manifest: FlowManifest): Screen | undefined => {\n const map = new Map(manifest.screens.map((s) => [s.id, s]));\n const startId = manifest.entryScreenId ?? manifest.screens[0]?.id;\n let cur = startId ? map.get(startId) : undefined;\n const seen = new Set<string>();\n let last: Screen | undefined;\n while (cur && !seen.has(cur.id)) {\n seen.add(cur.id);\n last = cur;\n const n = cur.next.default;\n if (!n) break;\n cur = map.get(n);\n }\n return last;\n};\n\nexport const ensureLayoutNodes = (manifest: FlowManifest): FlowManifest => {\n const existing = new Map(\n (manifest.builderMeta?.layout?.nodes ?? []).map((n) => [n.id, n] as const),\n );\n const nodes = manifest.screens.map((s, idx) => {\n const hit = existing.get(s.id);\n if (hit) return { ...hit };\n return { id: s.id, x: 80 + idx * 360, y: 120 };\n });\n return {\n ...manifest,\n builderMeta: {\n ...(manifest.builderMeta ?? {}),\n layout: {\n ...(manifest.builderMeta?.layout ?? {}),\n nodes,\n },\n },\n };\n};\n\n/**\n * Insert a model-generated screen into a draft manifest.\n * — Replaces the blank placeholder as the first screen when still on the initial seed.\n * — Otherwise appends after the default-path tail and wires `next`.\n */\nexport const mergeAiGeneratedScreenIntoManifest = (\n manifest: FlowManifest,\n screen: Screen,\n): FlowManifest => {\n if (isAiFlowPlaceholderManifest(manifest)) {\n const screens: Screen[] = [{ ...screen, next: { default: null } }];\n let builderMeta = manifest.builderMeta;\n const oldEntry = manifest.entryScreenId;\n if (builderMeta?.layout?.nodes?.length) {\n builderMeta = {\n ...builderMeta,\n layout: {\n ...builderMeta.layout,\n nodes: builderMeta.layout.nodes.map((n) =>\n n.id === oldEntry ? { ...n, id: screen.id } : { ...n },\n ),\n },\n };\n }\n const merged: FlowManifest = {\n ...manifest,\n screens,\n entryScreenId: screen.id,\n builderMeta,\n };\n return ensureLayoutNodes(merged);\n }\n\n const tail = findDefaultPathTailScreen(manifest);\n if (!tail) {\n throw new Error('mergeAiGeneratedScreenIntoManifest: no tail on default path');\n }\n if (tail.next.default !== null) {\n throw new Error('mergeAiGeneratedScreenIntoManifest: tail must end the default path (next is null)');\n }\n\n const screens: Screen[] = manifest.screens.map((s) =>\n s.id === tail.id ? { ...s, next: { default: screen.id } } : s,\n );\n screens.push({ ...screen, next: { default: null } });\n const merged: FlowManifest = { ...manifest, screens };\n return ensureLayoutNodes(merged);\n};\n\n/**\n * Appends a model-generated screen without changing any existing screen's `next` links.\n * The new screen always ends with `next: { default: null }` (canvas: user wires edges manually).\n */\nexport const appendGeneratedScreenToManifest = (manifest: FlowManifest, newScreen: Screen): FlowManifest => {\n const wiredNew: Screen = {\n ...newScreen,\n next: { default: null },\n };\n const merged: FlowManifest = {\n ...manifest,\n screens: [...manifest.screens, wiredNew],\n };\n return ensureLayoutNodes(merged);\n};\n\n/**\n * Inserts `newScreen` immediately after `anchorScreenId` on the default-next chain:\n * anchor → newScreen → (previous successor of anchor, if any).\n */\nexport const insertScreenAfterAnchorInManifest = (\n manifest: FlowManifest,\n anchorScreenId: string,\n newScreen: Screen,\n): FlowManifest => {\n const anchor = manifest.screens.find((s) => s.id === anchorScreenId);\n if (!anchor) {\n throw new Error('insertScreenAfterAnchorInManifest: anchor screen not found');\n }\n\n const forwarded = anchor.next.default;\n const wiredNew: Screen = {\n ...newScreen,\n next: { default: forwarded ?? null },\n };\n\n const screens: Screen[] = manifest.screens.map((s) =>\n s.id === anchorScreenId ? { ...s, next: { default: wiredNew.id } } : s,\n );\n screens.push(wiredNew);\n const merged: FlowManifest = { ...manifest, screens };\n return ensureLayoutNodes(merged);\n};\n\n/**\n * Applies a slim model manifest onto the authoritative draft while preserving\n * `builderMeta` (canvas layout) and ensuring layout nodes exist for new screens.\n */\nexport const mergeSlimManifestPreservingBuilderMeta = (\n draft: FlowManifest,\n slim: FlowManifest,\n): FlowManifest => {\n const merged: FlowManifest = {\n ...slim,\n builderMeta: draft.builderMeta,\n };\n return ensureLayoutNodes(merged);\n};\n","import type { FlowManifest } from '@getrheo/contracts/manifest';\nimport type { Screen } from '@getrheo/contracts/screens';\nimport type { DecisionNode } from '@getrheo/contracts/decisions';\nimport type { ExternalSurfaceNode } from '@getrheo/contracts/externalSurfaces';\nimport { ensureLayoutNodes } from './aiFlowGenerationMerge';\n\n/**\n * Partial manifest edit emitted by Rheo Agent in patch mode. Only the fields the\n * model intends to change are present; everything else is preserved from the draft.\n */\nexport type RheoAgentManifestPatch = {\n /** Upserted by `id`: replaces a matching screen, otherwise appended. */\n screens?: Screen[];\n /** Screen ids to drop from the draft. */\n removeScreenIds?: string[];\n /** Upserted by `id` when provided; omit to leave decision nodes unchanged. */\n decisionNodes?: DecisionNode[];\n /** Upserted by `id` when provided; omit to leave external surface nodes unchanged. */\n externalSurfaceNodes?: ExternalSurfaceNode[];\n /** Applied only when present. */\n entryScreenId?: string;\n /** Replaces the full key list only when present. */\n sdkAttributeKeys?: string[];\n};\n\nconst upsertById = <T extends { id: string }>(existing: T[], incoming: T[]): T[] => {\n const incomingById = new Map(incoming.map((item) => [item.id, item]));\n const merged = existing.map((item) => incomingById.get(item.id) ?? item);\n const existingIds = new Set(existing.map((item) => item.id));\n for (const item of incoming) {\n if (!existingIds.has(item.id)) merged.push(item);\n }\n return merged;\n};\n\n/**\n * Applies a Rheo Agent patch onto the authoritative draft, preserving `builderMeta`\n * (canvas layout) and ensuring layout nodes exist for any newly added screens.\n *\n * Identity fields (`flowId`, `schemaVersion`, `version`, `defaultLocale`, `locales`,\n * `theme`) are always taken from the draft and never accepted from the patch.\n */\nexport const mergeRheoAgentPatchIntoDraft = (\n draft: FlowManifest,\n patch: RheoAgentManifestPatch,\n): FlowManifest => {\n const removeIds = new Set(patch.removeScreenIds ?? []);\n\n let screens = patch.screens ? upsertById(draft.screens, patch.screens) : draft.screens;\n if (removeIds.size > 0) {\n screens = screens.filter((screen) => !removeIds.has(screen.id));\n }\n\n const decisionNodes = patch.decisionNodes\n ? upsertById(draft.decisionNodes ?? [], patch.decisionNodes)\n : draft.decisionNodes;\n\n const externalSurfaceNodes = patch.externalSurfaceNodes\n ? upsertById(draft.externalSurfaceNodes ?? [], patch.externalSurfaceNodes)\n : draft.externalSurfaceNodes;\n\n const merged: FlowManifest = {\n ...draft,\n screens,\n decisionNodes,\n externalSurfaceNodes,\n ...(patch.entryScreenId !== undefined ? { entryScreenId: patch.entryScreenId } : {}),\n ...(patch.sdkAttributeKeys !== undefined ? { sdkAttributeKeys: patch.sdkAttributeKeys } : {}),\n builderMeta: draft.builderMeta,\n };\n\n return ensureLayoutNodes(merged);\n};\n"]}
@@ -0,0 +1,35 @@
1
+ import { ScaleInputLabelStyle, ScaleInputLayer } from '@getrheo/contracts/layers';
2
+ import { Theme } from '@getrheo/contracts/manifest';
3
+
4
+ type ResolvedScaleInputTextStyle = {
5
+ fontFamily: string | undefined;
6
+ fontSizePx: number;
7
+ fontWeight: number | undefined;
8
+ color: string;
9
+ textAlign: ScaleInputLabelStyle['align'];
10
+ lineHeight: number | undefined;
11
+ opacity: number;
12
+ };
13
+ type ResolvedScaleInputSliderStyle = {
14
+ showLabels: boolean;
15
+ showValue: boolean;
16
+ trackHeightPx: number;
17
+ trackColor: string;
18
+ fillColor: string;
19
+ thumbSizePx: number;
20
+ thumbColor: string;
21
+ label: ResolvedScaleInputTextStyle;
22
+ value: ResolvedScaleInputTextStyle;
23
+ };
24
+ /** @deprecated Use {@link ResolvedScaleInputTextStyle}. */
25
+ type ResolvedScaleInputLabelStyle = ResolvedScaleInputTextStyle;
26
+ /**
27
+ * Merge author patches into stored scale-input text style (inspector).
28
+ * `undefined` values in `patch` remove that key so defaults apply at render time.
29
+ */
30
+ declare const mergeScaleInputLabelStyle: (prev: ScaleInputLabelStyle | undefined, patch: Partial<ScaleInputLabelStyle>) => ScaleInputLabelStyle | undefined;
31
+ /** Alias for {@link mergeScaleInputLabelStyle} — same shape for label and value typography. */
32
+ declare const mergeScaleInputValueStyle: (prev: ScaleInputLabelStyle | undefined, patch: Partial<ScaleInputLabelStyle>) => ScaleInputLabelStyle | undefined;
33
+ declare const resolveScaleInputSliderForRender: (layer: Pick<ScaleInputLayer, "labelStyle" | "valueStyle" | "showLabels" | "showValue" | "trackHeight" | "trackColor" | "fillColor" | "thumbSize" | "thumbColor">, theme: Theme | undefined, palette: "light" | "dark") => ResolvedScaleInputSliderStyle;
34
+
35
+ export { type ResolvedScaleInputLabelStyle, type ResolvedScaleInputSliderStyle, type ResolvedScaleInputTextStyle, mergeScaleInputLabelStyle, mergeScaleInputValueStyle, resolveScaleInputSliderForRender };
@@ -0,0 +1,77 @@
1
+ import '@getrheo/contracts/localized';
2
+ import '@getrheo/contracts/layers';
3
+
4
+ // src/layers.ts
5
+ var resolveTokens = (theme, value) => {
6
+ if (value === void 0) return value;
7
+ if (typeof value !== "string" || !value.startsWith("$")) return value;
8
+ const key = value.slice(1);
9
+ const literal = theme?.[key];
10
+ if (typeof literal === "string") return literal;
11
+ return value;
12
+ };
13
+ var resolveThemedColor = (theme, palette, value) => {
14
+ if (value === void 0) return void 0;
15
+ if (typeof value === "string") return resolveTokens(theme, value);
16
+ const raw = palette === "dark" ? value.dark ?? value.light : value.light ?? value.dark;
17
+ if (raw === void 0) return void 0;
18
+ return resolveTokens(theme, raw);
19
+ };
20
+
21
+ // src/scaleInputStyle.ts
22
+ var defaultTrackColor = (palette) => palette === "dark" ? "#3f3f46" : "#e4e4e7";
23
+ var defaultFillColor = (theme, palette) => {
24
+ const fromTheme = theme?.primary ? resolveThemedColor(theme, palette, theme.primary) : void 0;
25
+ return fromTheme ?? (palette === "dark" ? "#fafafa" : "#0a0a0a");
26
+ };
27
+ var defaultLabelColor = (palette) => palette === "dark" ? "#a1a1aa" : "#52525b";
28
+ var defaultValueColor = (palette) => palette === "dark" ? "#fafafa" : "#0a0a0a";
29
+ var mergeScaleInputLabelStyle = (prev, patch) => {
30
+ const out = { ...prev ?? {} };
31
+ for (const key of Object.keys(patch)) {
32
+ const v = patch[key];
33
+ if (v === void 0) delete out[key];
34
+ else out[key] = v;
35
+ }
36
+ return Object.keys(out).length > 0 ? out : void 0;
37
+ };
38
+ var mergeScaleInputValueStyle = mergeScaleInputLabelStyle;
39
+ var resolveColor = (theme, palette, color, fallback) => resolveThemedColor(theme, palette, color) ?? fallback;
40
+ var resolveTextStyle = (style, theme, palette, defaults) => ({
41
+ fontFamily: style?.fontFamily,
42
+ fontSizePx: style?.fontSize ?? defaults.fontSizePx,
43
+ fontWeight: style?.fontWeight ?? defaults.fontWeight,
44
+ color: resolveColor(theme, palette, style?.color, defaults.color),
45
+ textAlign: style?.align ?? defaults.textAlign,
46
+ lineHeight: style?.lineHeight,
47
+ opacity: style?.opacity ?? defaults.opacity
48
+ });
49
+ var resolveScaleInputSliderForRender = (layer, theme, palette) => {
50
+ const trackFallback = defaultTrackColor(palette);
51
+ const fillFallback = defaultFillColor(theme, palette);
52
+ return {
53
+ showLabels: layer.showLabels !== false,
54
+ showValue: layer.showValue !== false,
55
+ trackHeightPx: layer.trackHeight ?? 4,
56
+ trackColor: resolveColor(theme, palette, layer.trackColor, trackFallback),
57
+ fillColor: resolveColor(theme, palette, layer.fillColor, fillFallback),
58
+ thumbSizePx: layer.thumbSize ?? 16,
59
+ thumbColor: resolveColor(theme, palette, layer.thumbColor, fillFallback),
60
+ label: resolveTextStyle(layer.labelStyle, theme, palette, {
61
+ fontSizePx: 11,
62
+ color: defaultLabelColor(palette),
63
+ opacity: 0.75
64
+ }),
65
+ value: resolveTextStyle(layer.valueStyle, theme, palette, {
66
+ fontSizePx: 14,
67
+ fontWeight: 600,
68
+ color: defaultValueColor(palette),
69
+ opacity: 1,
70
+ textAlign: "center"
71
+ })
72
+ };
73
+ };
74
+
75
+ export { mergeScaleInputLabelStyle, mergeScaleInputValueStyle, resolveScaleInputSliderForRender };
76
+ //# sourceMappingURL=scaleInputStyle.js.map
77
+ //# sourceMappingURL=scaleInputStyle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/layers.ts","../src/scaleInputStyle.ts"],"names":[],"mappings":"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","file":"scaleInputStyle.js","sourcesContent":["import type { Branding } from '@getrheo/contracts/dashboard';\nimport type { FlowManifest, Theme } from '@getrheo/contracts/manifest';\nimport type { Screen } from '@getrheo/contracts/screens';\nimport {\n brandGradientFromThemedColor,\n brandGradientNativeLinear,\n brandGradientSolidFallback,\n isStoredLinearGradientCss,\n nativeLinearFromAngleAndStops,\n parseLinearGradientCss,\n resolveBrandGradientToken,\n type BrandGradientNativeLinear,\n} from './brandGradient';\nimport type {\n InputLayer,\n Layer,\n MultipleChoiceLayer,\n ScaleInputLayer,\n SingleChoiceLayer,\n StackLayer,\n TextInputLayer,\n ThemedColor,\n} from '@getrheo/contracts/layers';\nimport type { LocalizedText } from '@getrheo/contracts/localized';\nimport { resolveLocalizedText } from '@getrheo/contracts/localized';\nimport { isInputLayer } from '@getrheo/contracts/layers';\n\n/** Walk a layer tree depth-first. */\nexport const walkLayers = (root: Layer, fn: (l: Layer, depth: number) => void): void => {\n const visit = (l: Layer, depth: number): void => {\n fn(l, depth);\n if (l.kind === 'stack') l.children.forEach((c) => visit(c, depth + 1));\n else if (l.kind === 'carousel') l.slides.forEach((c) => visit(c, depth + 1));\n else if (l.kind === 'button') l.children.forEach((c) => visit(c, depth + 1));\n else if (l.kind === 'back_button') l.children.forEach((c) => visit(c, depth + 1));\n else if (l.kind === 'hyperlink') l.children.forEach((c) => visit(c, depth + 1));\n else if (l.kind === 'single_choice' || l.kind === 'multiple_choice') {\n l.children.forEach((c) => visit(c, depth + 1));\n } else if (l.kind === 'text_input' || l.kind === 'scale_input') {\n l.children?.forEach((c) => visit(c, depth + 1));\n } else if (l.kind === 'oauth_login') {\n l.children.forEach((c) => visit(c, depth + 1));\n } else if (l.kind === 'oauth_provider' && l.variant === 'custom') {\n l.children.forEach((c) => visit(c, depth + 1));\n } else if (l.kind === 'email_password_auth') {\n l.children.forEach((c) => visit(c, depth + 1));\n } else if (l.kind === 'email_password_field') {\n l.children?.forEach((c) => visit(c, depth + 1));\n } else if (l.kind === 'email_password_submit') {\n l.children.forEach((c) => visit(c, depth + 1));\n }\n };\n visit(root, 0);\n};\n\n/** Walk every layer in a screen's regions (header → body → footer). */\nexport const walkScreen = (screen: Screen, fn: (l: Layer) => void): void => {\n if (screen.regions.header) walkLayers(screen.regions.header, fn);\n walkLayers(screen.regions.body, fn);\n if (screen.regions.footer) walkLayers(screen.regions.footer, fn);\n};\n\n/** Find the screen's lone input layer (if any). Schema enforces ≤1. */\nexport const findInputLayer = (screen: Screen): InputLayer | null => {\n let found: InputLayer | null = null;\n walkScreen(screen, (l) => {\n if (!found && isInputLayer(l)) found = l;\n });\n return found;\n};\n\n/** Input kinds that use a screen draft and require an explicit Continue to submit. */\nexport const findManualSubmitInputLayer = (\n screen: Screen,\n): MultipleChoiceLayer | TextInputLayer | ScaleInputLayer | null => {\n const input = findInputLayer(screen);\n if (!input) return null;\n if (input.kind === 'multiple_choice' || input.kind === 'text_input' || input.kind === 'scale_input') {\n return input;\n }\n return null;\n};\n\n/**\n * Whether the screen contains any Button layer that submits the screen\n * (i.e. `action.kind === 'continue'`). Used by input layers to decide\n * between auto-submit-on-tap (legacy behaviour for choice-only screens)\n * and writing into the screen-level draft for a Button to submit.\n */\nexport const screenHasContinueButton = (screen: Screen): boolean => {\n let found = false;\n walkScreen(screen, (l) => {\n if (l.kind === 'button' && l.action.kind === 'continue') found = true;\n });\n return found;\n};\n\n/**\n * Resolve a choice layer's option stack by stable optionId via its\n * binding. Returns null when the binding is missing or the bound child\n * was removed (manifest validation rejects that, but runtime stays safe).\n */\nexport const findOptionStackForChoice = (\n layer: SingleChoiceLayer | MultipleChoiceLayer,\n optionId: string,\n): StackLayer | null => {\n const binding = layer.optionBindings.find((b) => b.optionId === optionId);\n if (!binding) return null;\n const stack = layer.children.find((c) => c.id === binding.rootLayerId);\n return stack ?? null;\n};\n\n/**\n * Best-effort textual label for a choice option, used by interpolation\n * (e.g. `{{ goal }}` rendering the chosen option's label) and by editor\n * surfaces that show option rows. Walks the option's child subtree\n * depth-first and returns the first `text` layer's content; falls back\n * to the option's stable id when no text is present.\n */\nexport const choiceOptionLabel = (\n layer: SingleChoiceLayer | MultipleChoiceLayer,\n optionId: string,\n locale: string,\n): string => {\n const stack = findOptionStackForChoice(layer, optionId);\n if (!stack) return '';\n let foundText: LocalizedText | null = null;\n walkLayers(stack, (l) => {\n if (foundText) return;\n if (l.kind === 'text') foundText = l.text;\n });\n if (foundText) return resolveLocalizedText(foundText, locale);\n return optionId;\n};\n\n/** Find a layer in a screen by id, including nested children/slides. */\nexport const findLayerById = (screen: Screen, id: string): Layer | null => {\n let found: Layer | null = null;\n walkScreen(screen, (l) => {\n if (!found && l.id === id) found = l;\n });\n return found;\n};\n\n/** Collect all input fieldKeys across a manifest. */\nexport const collectFieldKeys = (manifest: FlowManifest): { fieldKey: string; screenId: string }[] => {\n const out: { fieldKey: string; screenId: string }[] = [];\n for (const screen of manifest.screens) {\n walkScreen(screen as unknown as Screen, (l) => {\n if (isInputLayer(l)) out.push({ fieldKey: l.fieldKey, screenId: screen.id });\n if (l.kind === 'checkbox') out.push({ fieldKey: l.fieldKey, screenId: screen.id });\n if (l.kind === 'email_password_auth') {\n out.push({ fieldKey: l.fieldKey, screenId: screen.id });\n }\n });\n }\n return out;\n};\n\n/**\n * Pick a snake_case field key starting from `base` that is not in `used`\n * (e.g. `text` → `text_2` → `text_3` when `text` is taken).\n */\nexport const nextUniqueFieldKey = (base: string, used: Iterable<string>): string => {\n const set = used instanceof Set ? used : new Set(used);\n if (!set.has(base)) return base;\n let n = 2;\n while (set.has(`${base}_${n}`)) n += 1;\n return `${base}_${n}`;\n};\n\n/**\n * Resolve a token reference like `$primary` to a literal value from `theme`.\n * Pass-through for non-token strings; returns undefined for `undefined`.\n */\nexport const resolveTokens = <T extends string | undefined>(\n theme: Theme | undefined,\n value: T,\n): T | string => {\n if (value === undefined) return value;\n if (typeof value !== 'string' || !value.startsWith('$')) return value;\n const key = value.slice(1) as keyof Theme;\n const literal = theme?.[key];\n if (typeof literal === 'string') return literal;\n return value;\n};\n\n/**\n * Resolve a layer color for the current appearance (`light` | `dark`).\n * Plain string uses `resolveTokens` for both modes (legacy). Object form\n * picks `light` / `dark` with fallback to the other key when one is omitted.\n */\nexport const resolveThemedColor = (\n theme: Theme | undefined,\n palette: 'light' | 'dark',\n value: ThemedColor | undefined,\n): string | undefined => {\n if (value === undefined) return undefined;\n if (typeof value === 'string') return resolveTokens(theme, value) as string;\n const raw = palette === 'dark' ? (value.dark ?? value.light) : (value.light ?? value.dark);\n if (raw === undefined) return undefined;\n return resolveTokens(theme, raw) as string;\n};\n\n/**\n * Resolve a themed value used for CSS `background` (or RN background fill).\n * Supports `$brandGradient:<uuid>` when branding presets are provided; other values match {@link resolveThemedColor}.\n */\nexport const resolveThemedBackground = (\n theme: Theme | undefined,\n branding: Branding | undefined,\n palette: 'light' | 'dark',\n value: ThemedColor | undefined,\n): string | undefined => {\n if (value === undefined) return undefined;\n if (typeof value === 'string') {\n if (value.startsWith('$brandGradient:')) {\n return resolveBrandGradientToken(branding, value);\n }\n return resolveTokens(theme, value) as string;\n }\n const raw = palette === 'dark' ? (value.dark ?? value.light) : (value.light ?? value.dark);\n if (raw === undefined) return undefined;\n if (raw.startsWith('$brandGradient:')) {\n return resolveBrandGradientToken(branding, raw);\n }\n return resolveTokens(theme, raw) as string;\n};\n\nexport const nativeBrandBackgroundFromThemedColor = (\n theme: Theme | undefined,\n branding: Branding | undefined,\n palette: 'light' | 'dark',\n value: ThemedColor | undefined,\n): { solid?: string; linear?: BrandGradientNativeLinear } => {\n const preset = brandGradientFromThemedColor(branding, palette, value);\n if (preset) {\n const lin = brandGradientNativeLinear(preset);\n if (lin) return { linear: lin };\n return { solid: brandGradientSolidFallback(preset) };\n }\n const bg = resolveThemedBackground(theme, branding, palette, value) as string | undefined;\n if (!bg) return {};\n const parsed = parseLinearGradientCss(bg);\n if (parsed) {\n return {\n linear: nativeLinearFromAngleAndStops(\n parsed.angleDeg,\n parsed.stops.map((s) => ({ color: s.color, offsetPct: s.offsetPct })),\n ),\n };\n }\n if (isStoredLinearGradientCss(bg)) {\n const first = bg.match(/#[0-9a-fA-F]{3,8}/);\n return { solid: first ? first[0] : '#808080' };\n }\n return { solid: bg };\n};\n","import type { ScaleInputLabelStyle, ScaleInputLayer, ThemedColor } from '@getrheo/contracts/layers';\nimport type { Theme } from '@getrheo/contracts/manifest';\nimport { resolveThemedColor } from './layers';\n\nexport type ResolvedScaleInputTextStyle = {\n fontFamily: string | undefined;\n fontSizePx: number;\n fontWeight: number | undefined;\n color: string;\n textAlign: ScaleInputLabelStyle['align'];\n lineHeight: number | undefined;\n opacity: number;\n};\n\nexport type ResolvedScaleInputSliderStyle = {\n showLabels: boolean;\n showValue: boolean;\n trackHeightPx: number;\n trackColor: string;\n fillColor: string;\n thumbSizePx: number;\n thumbColor: string;\n label: ResolvedScaleInputTextStyle;\n value: ResolvedScaleInputTextStyle;\n};\n\n/** @deprecated Use {@link ResolvedScaleInputTextStyle}. */\nexport type ResolvedScaleInputLabelStyle = ResolvedScaleInputTextStyle;\n\nconst defaultTrackColor = (palette: 'light' | 'dark'): string =>\n palette === 'dark' ? '#3f3f46' : '#e4e4e7';\n\nconst defaultFillColor = (\n theme: Theme | undefined,\n palette: 'light' | 'dark',\n): string => {\n const fromTheme = theme?.primary\n ? (resolveThemedColor(theme, palette, theme.primary) as string | undefined)\n : undefined;\n return fromTheme ?? (palette === 'dark' ? '#fafafa' : '#0a0a0a');\n};\n\nconst defaultLabelColor = (palette: 'light' | 'dark'): string =>\n palette === 'dark' ? '#a1a1aa' : '#52525b';\n\nconst defaultValueColor = (palette: 'light' | 'dark'): string =>\n palette === 'dark' ? '#fafafa' : '#0a0a0a';\n\n/**\n * Merge author patches into stored scale-input text style (inspector).\n * `undefined` values in `patch` remove that key so defaults apply at render time.\n */\nexport const mergeScaleInputLabelStyle = (\n prev: ScaleInputLabelStyle | undefined,\n patch: Partial<ScaleInputLabelStyle>,\n): ScaleInputLabelStyle | undefined => {\n const out: ScaleInputLabelStyle = { ...(prev ?? {}) };\n for (const key of Object.keys(patch) as (keyof ScaleInputLabelStyle)[]) {\n const v = patch[key];\n if (v === undefined) delete (out as Record<string, unknown>)[key];\n else (out as Record<string, unknown>)[key] = v;\n }\n return Object.keys(out).length > 0 ? out : undefined;\n};\n\n/** Alias for {@link mergeScaleInputLabelStyle} — same shape for label and value typography. */\nexport const mergeScaleInputValueStyle = mergeScaleInputLabelStyle;\n\nconst resolveColor = (\n theme: Theme | undefined,\n palette: 'light' | 'dark',\n color: ThemedColor | undefined,\n fallback: string,\n): string =>\n (resolveThemedColor(theme, palette, color) as string | undefined) ?? fallback;\n\nconst resolveTextStyle = (\n style: ScaleInputLabelStyle | undefined,\n theme: Theme | undefined,\n palette: 'light' | 'dark',\n defaults: {\n fontSizePx: number;\n fontWeight?: number;\n color: string;\n opacity: number;\n textAlign?: ScaleInputLabelStyle['align'];\n },\n): ResolvedScaleInputTextStyle => ({\n fontFamily: style?.fontFamily,\n fontSizePx: style?.fontSize ?? defaults.fontSizePx,\n fontWeight: style?.fontWeight ?? defaults.fontWeight,\n color: resolveColor(theme, palette, style?.color, defaults.color),\n textAlign: style?.align ?? defaults.textAlign,\n lineHeight: style?.lineHeight,\n opacity: style?.opacity ?? defaults.opacity,\n});\n\nexport const resolveScaleInputSliderForRender = (\n layer: Pick<\n ScaleInputLayer,\n | 'labelStyle'\n | 'valueStyle'\n | 'showLabels'\n | 'showValue'\n | 'trackHeight'\n | 'trackColor'\n | 'fillColor'\n | 'thumbSize'\n | 'thumbColor'\n >,\n theme: Theme | undefined,\n palette: 'light' | 'dark',\n): ResolvedScaleInputSliderStyle => {\n const trackFallback = defaultTrackColor(palette);\n const fillFallback = defaultFillColor(theme, palette);\n return {\n showLabels: layer.showLabels !== false,\n showValue: layer.showValue !== false,\n trackHeightPx: layer.trackHeight ?? 4,\n trackColor: resolveColor(theme, palette, layer.trackColor, trackFallback),\n fillColor: resolveColor(theme, palette, layer.fillColor, fillFallback),\n thumbSizePx: layer.thumbSize ?? 16,\n thumbColor: resolveColor(theme, palette, layer.thumbColor, fillFallback),\n label: resolveTextStyle(layer.labelStyle, theme, palette, {\n fontSizePx: 11,\n color: defaultLabelColor(palette),\n opacity: 0.75,\n }),\n value: resolveTextStyle(layer.valueStyle, theme, palette, {\n fontSizePx: 14,\n fontWeight: 600,\n color: defaultValueColor(palette),\n opacity: 1,\n textAlign: 'center',\n }),\n };\n};\n"]}
@@ -0,0 +1,9 @@
1
+ import { ScaleInputLayer } from '@getrheo/contracts/layers';
2
+
3
+ declare const scaleStep: (layer: ScaleInputLayer) => number;
4
+ /** Snap `raw` to the nearest valid value on the discrete scale. */
5
+ declare const snapScaleValue: (layer: ScaleInputLayer, raw: number) => number;
6
+ declare const scaleValueIsOnStep: (layer: ScaleInputLayer, value: number) => boolean;
7
+ declare const scaleValueInRange: (layer: ScaleInputLayer, value: number) => boolean;
8
+
9
+ export { scaleStep, scaleValueInRange, scaleValueIsOnStep, snapScaleValue };
@@ -0,0 +1,21 @@
1
+ // src/scaleValidation.ts
2
+ var scaleStep = (layer) => layer.step ?? 1;
3
+ var snapScaleValue = (layer, raw) => {
4
+ const step = scaleStep(layer);
5
+ const { min, max } = layer;
6
+ const n = Math.round((raw - min) / step);
7
+ const v = min + n * step;
8
+ if (v < min) return min;
9
+ if (v > max) return max;
10
+ return v;
11
+ };
12
+ var scaleValueIsOnStep = (layer, value) => {
13
+ const step = scaleStep(layer);
14
+ const n = (value - layer.min) / step;
15
+ return Number.isFinite(n) && Math.abs(n - Math.round(n)) < 1e-6;
16
+ };
17
+ var scaleValueInRange = (layer, value) => value >= layer.min && value <= layer.max;
18
+
19
+ export { scaleStep, scaleValueInRange, scaleValueIsOnStep, snapScaleValue };
20
+ //# sourceMappingURL=scaleValidation.js.map
21
+ //# sourceMappingURL=scaleValidation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/scaleValidation.ts"],"names":[],"mappings":";AAEO,IAAM,SAAA,GAAY,CAAC,KAAA,KAAmC,KAAA,CAAM,IAAA,IAAQ;AAGpE,IAAM,cAAA,GAAiB,CAAC,KAAA,EAAwB,GAAA,KAAwB;AAC7E,EAAA,MAAM,IAAA,GAAO,UAAU,KAAK,CAAA;AAC5B,EAAA,MAAM,EAAE,GAAA,EAAK,GAAA,EAAI,GAAI,KAAA;AACrB,EAAA,MAAM,CAAA,GAAI,IAAA,CAAK,KAAA,CAAA,CAAO,GAAA,GAAM,OAAO,IAAI,CAAA;AACvC,EAAA,MAAM,CAAA,GAAI,MAAM,CAAA,GAAI,IAAA;AACpB,EAAA,IAAI,CAAA,GAAI,KAAK,OAAO,GAAA;AACpB,EAAA,IAAI,CAAA,GAAI,KAAK,OAAO,GAAA;AACpB,EAAA,OAAO,CAAA;AACT;AAEO,IAAM,kBAAA,GAAqB,CAAC,KAAA,EAAwB,KAAA,KAA2B;AACpF,EAAA,MAAM,IAAA,GAAO,UAAU,KAAK,CAAA;AAC5B,EAAA,MAAM,CAAA,GAAA,CAAK,KAAA,GAAQ,KAAA,CAAM,GAAA,IAAO,IAAA;AAChC,EAAA,OAAO,MAAA,CAAO,QAAA,CAAS,CAAC,CAAA,IAAK,IAAA,CAAK,GAAA,CAAI,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,CAAC,CAAC,CAAA,GAAI,IAAA;AAC7D;AAEO,IAAM,iBAAA,GAAoB,CAAC,KAAA,EAAwB,KAAA,KACxD,SAAS,KAAA,CAAM,GAAA,IAAO,SAAS,KAAA,CAAM","file":"scaleValidation.js","sourcesContent":["import type { ScaleInputLayer } from '@getrheo/contracts/layers';\n\nexport const scaleStep = (layer: ScaleInputLayer): number => layer.step ?? 1;\n\n/** Snap `raw` to the nearest valid value on the discrete scale. */\nexport const snapScaleValue = (layer: ScaleInputLayer, raw: number): number => {\n const step = scaleStep(layer);\n const { min, max } = layer;\n const n = Math.round((raw - min) / step);\n const v = min + n * step;\n if (v < min) return min;\n if (v > max) return max;\n return v;\n};\n\nexport const scaleValueIsOnStep = (layer: ScaleInputLayer, value: number): boolean => {\n const step = scaleStep(layer);\n const n = (value - layer.min) / step;\n return Number.isFinite(n) && Math.abs(n - Math.round(n)) < 1e-6;\n};\n\nexport const scaleValueInRange = (layer: ScaleInputLayer, value: number): boolean =>\n value >= layer.min && value <= layer.max;\n"]}
@@ -0,0 +1,105 @@
1
+ import { S as StepResponse } from './stepResponse-BXgoZ7o-.js';
2
+ export { C as ConsumedDraftPayload, a as StepResponseCore, b as SurfaceSdkKeyPatch, e as externalSurfaceResponseKey, i as isEligibleConsumedDraft } from './stepResponse-BXgoZ7o-.js';
3
+ import { FlowManifest } from '@getrheo/contracts/manifest';
4
+ import { Screen } from '@getrheo/contracts/screens';
5
+ import { ExternalSurfaceNode, NormalizedSurfaceOutcome } from '@getrheo/contracts/externalSurfaces';
6
+ import { DecisionEvaluationTelemetry } from './decisionEval.js';
7
+ import { OAuthLoginProvider, EmailPasswordAuthMode } from '@getrheo/contracts/layers';
8
+ import '@getrheo/contracts/decisions';
9
+
10
+ type FlowSessionContext = {
11
+ locale: string;
12
+ platform: string;
13
+ sdkAttributes: Record<string, unknown>;
14
+ };
15
+ type FlowState = {
16
+ manifest: FlowManifest;
17
+ currentScreenId: string | null;
18
+ /**
19
+ * Non-null while the flow is awaiting an outcome from an external surface
20
+ * (e.g. RevenueCat paywall). `currentScreenId` is null in this state so the
21
+ * native renderer doesn't try to paint a screen.
22
+ */
23
+ pendingExternalSurface: {
24
+ nodeId: string;
25
+ } | null;
26
+ history: string[];
27
+ /** Responses keyed by `fieldKey` (or screen id for non-input responses like CTA/skip). */
28
+ responses: Record<string, StepResponse>;
29
+ session: FlowSessionContext;
30
+ status: 'idle' | 'running' | 'completed' | 'abandoned';
31
+ startedAt: string | null;
32
+ completedAt: string | null;
33
+ };
34
+ type SubmitResponseOptions = {
35
+ now?: string;
36
+ onDecisionEvaluated?: (payload: DecisionEvaluationTelemetry) => void;
37
+ };
38
+ declare const findScreen: (manifest: FlowManifest, screenId: string) => Screen | undefined;
39
+ declare const findExternalSurface: (manifest: FlowManifest, nodeId: string) => ExternalSurfaceNode | undefined;
40
+ declare const initFlowState: (manifest: FlowManifest, sessionPartial?: Partial<FlowSessionContext>) => FlowState;
41
+
42
+ /**
43
+ * Result of walking the manifest from a cursor id past decision nodes:
44
+ * either we landed on a screen (renderable), an external surface (await
45
+ * outcome), or fell off the graph (flow complete).
46
+ */
47
+ type GraphLanding = {
48
+ kind: 'screen';
49
+ screenId: string;
50
+ } | {
51
+ kind: 'surface';
52
+ nodeId: string;
53
+ } | {
54
+ kind: 'end';
55
+ };
56
+ declare const resolveNextScreenId: (screen: Screen, response: StepResponse) => string | null;
57
+ declare const resolveThroughGraph: (manifest: FlowManifest, cursor: string | null, responses: Record<string, StepResponse>, session: FlowSessionContext, onDecisionEvaluated?: (payload: DecisionEvaluationTelemetry) => void) => GraphLanding;
58
+ /** Determine the storage key for a response on a given screen. Input layers
59
+ * use their `fieldKey`; others use the screen id (for analytics breadcrumbs). */
60
+ declare const responseKeyFor: (screen: Screen, response: StepResponse) => string;
61
+ /** Apply a surface landing to a state update (history + pending flag). */
62
+ declare const applyGraphLanding: (base: FlowState, landing: GraphLanding, nextResponses: Record<string, StepResponse>, now: string) => FlowState;
63
+
64
+ type StartFlowOptions = {
65
+ now?: string;
66
+ onDecisionEvaluated?: (payload: DecisionEvaluationTelemetry) => void;
67
+ };
68
+ declare const startFlow: (state: FlowState, second?: string | StartFlowOptions) => FlowState;
69
+ declare const submitResponse: (state: FlowState, response: StepResponse, third?: string | SubmitResponseOptions) => FlowState;
70
+
71
+ /** Response map keys produced by OAuth / email-password layers (see `oauthLoginResponseKey`, `emailPasswordAuthResponseKey`). */
72
+ declare const isAuthTerminalExportResponseKey: (key: string) => boolean;
73
+ /** Copy of `responses` without OAuth / email-password auth entries (hosts use auth provider callbacks for those). */
74
+ declare const stripAuthResponsesForTerminalExport: (responses: Record<string, StepResponse>) => Record<string, StepResponse>;
75
+ /** Values stored under each field key in {@link buildCompletionResponses} / terminal `answers`. */
76
+ type FlowTerminalAnswerEntryValue = string | string[] | number | {
77
+ value: string;
78
+ classification: string;
79
+ } | {
80
+ success: boolean;
81
+ customerExternalId?: string;
82
+ provider: OAuthLoginProvider;
83
+ } | {
84
+ success: boolean;
85
+ mode: EmailPasswordAuthMode;
86
+ email: string;
87
+ } | {
88
+ bypassed: true;
89
+ via: 'skip' | 'go_to_screen';
90
+ } | {
91
+ outcome: NormalizedSurfaceOutcome;
92
+ } | boolean | 'end_flow' | 'skip' | 'carousel' | {
93
+ goToScreen: string;
94
+ };
95
+ /**
96
+ * Maps one stored {@link StepResponse} to a JSON-safe completion value.
97
+ * `screen_commit` should not appear in `FlowState.responses` (it unwraps in `submitResponse`).
98
+ */
99
+ declare const stepResponseToCompletionValue: (r: StepResponse) => FlowTerminalAnswerEntryValue | undefined;
100
+ declare const buildCompletionResponses: (state: FlowState) => Record<string, FlowTerminalAnswerEntryValue>;
101
+ /** Normalized `answers` map in {@link FlowTerminalSnapshot} (from {@link buildCompletionResponses}). */
102
+ type FlowTerminalAnswerMap = ReturnType<typeof buildCompletionResponses>;
103
+ declare const abandonFlow: (state: FlowState, now?: string) => FlowState;
104
+
105
+ export { DecisionEvaluationTelemetry, type FlowSessionContext, type FlowState, type FlowTerminalAnswerEntryValue, type FlowTerminalAnswerMap, type GraphLanding, type StartFlowOptions, StepResponse, type SubmitResponseOptions, abandonFlow, applyGraphLanding, buildCompletionResponses, findExternalSurface, findScreen, initFlowState, isAuthTerminalExportResponseKey, resolveNextScreenId, resolveThroughGraph, responseKeyFor, startFlow, stepResponseToCompletionValue, stripAuthResponsesForTerminalExport, submitResponse };