@hook-sdk/template 0.10.0 → 0.12.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/index.cjs +110 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +21 -4
- package/dist/index.d.ts +21 -4
- package/dist/index.js +111 -32
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.cts
CHANGED
|
@@ -68,6 +68,7 @@ type AppConfig = {
|
|
|
68
68
|
onboarding?: OnboardingConfig;
|
|
69
69
|
deepLinks?: DeepLinks;
|
|
70
70
|
features_enabled?: string[];
|
|
71
|
+
theme?: Record<string, unknown>;
|
|
71
72
|
};
|
|
72
73
|
|
|
73
74
|
interface AuthScreenProps {
|
|
@@ -117,6 +118,16 @@ interface PushPromptTexts {
|
|
|
117
118
|
iosInstallCta?: string;
|
|
118
119
|
deniedTitle: string;
|
|
119
120
|
deniedBody: string;
|
|
121
|
+
/**
|
|
122
|
+
* Audit Wave 3 — Fix #33: per-platform recovery copy shown when permission
|
|
123
|
+
* is denied. All four are optional; if the matching platform's copy is
|
|
124
|
+
* missing the recovery paragraph is omitted (back-compat for callers that
|
|
125
|
+
* haven't supplied them yet).
|
|
126
|
+
*/
|
|
127
|
+
deniedRecoveryIos?: string;
|
|
128
|
+
deniedRecoveryAndroid?: string;
|
|
129
|
+
deniedRecoveryDesktop?: string;
|
|
130
|
+
deniedRecoveryInApp?: string;
|
|
120
131
|
unsupportedBody: string;
|
|
121
132
|
}
|
|
122
133
|
interface PushPromptProps {
|
|
@@ -160,7 +171,9 @@ interface State {
|
|
|
160
171
|
declare class ErrorBoundary extends Component<Props, State> {
|
|
161
172
|
state: State;
|
|
162
173
|
static getDerivedStateFromError(error: Error): State;
|
|
163
|
-
componentDidCatch(error: Error
|
|
174
|
+
componentDidCatch(error: Error, info: {
|
|
175
|
+
componentStack: string;
|
|
176
|
+
}): void;
|
|
164
177
|
render(): string | number | boolean | Iterable<ReactNode> | react_jsx_runtime.JSX.Element;
|
|
165
178
|
}
|
|
166
179
|
|
|
@@ -531,6 +544,8 @@ type PushUiState = {
|
|
|
531
544
|
kind: 'subscribed';
|
|
532
545
|
} | {
|
|
533
546
|
kind: 'denied';
|
|
547
|
+
} | {
|
|
548
|
+
kind: 'dismissed';
|
|
534
549
|
} | {
|
|
535
550
|
kind: 'error';
|
|
536
551
|
code: string;
|
|
@@ -540,6 +555,7 @@ declare function usePush(): {
|
|
|
540
555
|
state: PushUiState;
|
|
541
556
|
subscribe: () => Promise<void>;
|
|
542
557
|
unsubscribe: () => Promise<void>;
|
|
558
|
+
dismiss: () => void;
|
|
543
559
|
};
|
|
544
560
|
|
|
545
561
|
declare function useReminders(): {
|
|
@@ -557,7 +573,7 @@ declare function useReminders(): {
|
|
|
557
573
|
sendAt: string | Date;
|
|
558
574
|
title: string;
|
|
559
575
|
body: string;
|
|
560
|
-
url
|
|
576
|
+
url: string;
|
|
561
577
|
}>) => Promise<{
|
|
562
578
|
accepted: number;
|
|
563
579
|
rejected: number;
|
|
@@ -569,7 +585,7 @@ declare function useReminders(): {
|
|
|
569
585
|
slot: string;
|
|
570
586
|
title: string;
|
|
571
587
|
body: string;
|
|
572
|
-
url
|
|
588
|
+
url: string;
|
|
573
589
|
}>) => Promise<void>>[0]) => Promise<void>;
|
|
574
590
|
};
|
|
575
591
|
|
|
@@ -717,7 +733,7 @@ type OnboardingFlowProps = {
|
|
|
717
733
|
onComplete: (value: Record<string, unknown>) => void;
|
|
718
734
|
persistKey: string;
|
|
719
735
|
};
|
|
720
|
-
declare function OnboardingFlow({ steps, screens, onComplete, persistKey, }: OnboardingFlowProps): react_jsx_runtime.JSX.Element;
|
|
736
|
+
declare function OnboardingFlow({ steps, screens, onComplete, persistKey, }: OnboardingFlowProps): react_jsx_runtime.JSX.Element | null;
|
|
721
737
|
|
|
722
738
|
type OnboardingStepCtx = {
|
|
723
739
|
stepIndex: number;
|
|
@@ -815,6 +831,7 @@ declare const AppConfigSchema: z.ZodObject<{
|
|
|
815
831
|
emailVerify: z.ZodOptional<z.ZodString>;
|
|
816
832
|
}, z.core.$strict>>;
|
|
817
833
|
features_enabled: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
834
|
+
theme: z.ZodOptional<z.ZodObject<{}, z.core.$loose>>;
|
|
818
835
|
}, z.core.$strict>;
|
|
819
836
|
declare function parseAppConfig(input: unknown): AppConfig;
|
|
820
837
|
|
package/dist/index.d.ts
CHANGED
|
@@ -68,6 +68,7 @@ type AppConfig = {
|
|
|
68
68
|
onboarding?: OnboardingConfig;
|
|
69
69
|
deepLinks?: DeepLinks;
|
|
70
70
|
features_enabled?: string[];
|
|
71
|
+
theme?: Record<string, unknown>;
|
|
71
72
|
};
|
|
72
73
|
|
|
73
74
|
interface AuthScreenProps {
|
|
@@ -117,6 +118,16 @@ interface PushPromptTexts {
|
|
|
117
118
|
iosInstallCta?: string;
|
|
118
119
|
deniedTitle: string;
|
|
119
120
|
deniedBody: string;
|
|
121
|
+
/**
|
|
122
|
+
* Audit Wave 3 — Fix #33: per-platform recovery copy shown when permission
|
|
123
|
+
* is denied. All four are optional; if the matching platform's copy is
|
|
124
|
+
* missing the recovery paragraph is omitted (back-compat for callers that
|
|
125
|
+
* haven't supplied them yet).
|
|
126
|
+
*/
|
|
127
|
+
deniedRecoveryIos?: string;
|
|
128
|
+
deniedRecoveryAndroid?: string;
|
|
129
|
+
deniedRecoveryDesktop?: string;
|
|
130
|
+
deniedRecoveryInApp?: string;
|
|
120
131
|
unsupportedBody: string;
|
|
121
132
|
}
|
|
122
133
|
interface PushPromptProps {
|
|
@@ -160,7 +171,9 @@ interface State {
|
|
|
160
171
|
declare class ErrorBoundary extends Component<Props, State> {
|
|
161
172
|
state: State;
|
|
162
173
|
static getDerivedStateFromError(error: Error): State;
|
|
163
|
-
componentDidCatch(error: Error
|
|
174
|
+
componentDidCatch(error: Error, info: {
|
|
175
|
+
componentStack: string;
|
|
176
|
+
}): void;
|
|
164
177
|
render(): string | number | boolean | Iterable<ReactNode> | react_jsx_runtime.JSX.Element;
|
|
165
178
|
}
|
|
166
179
|
|
|
@@ -531,6 +544,8 @@ type PushUiState = {
|
|
|
531
544
|
kind: 'subscribed';
|
|
532
545
|
} | {
|
|
533
546
|
kind: 'denied';
|
|
547
|
+
} | {
|
|
548
|
+
kind: 'dismissed';
|
|
534
549
|
} | {
|
|
535
550
|
kind: 'error';
|
|
536
551
|
code: string;
|
|
@@ -540,6 +555,7 @@ declare function usePush(): {
|
|
|
540
555
|
state: PushUiState;
|
|
541
556
|
subscribe: () => Promise<void>;
|
|
542
557
|
unsubscribe: () => Promise<void>;
|
|
558
|
+
dismiss: () => void;
|
|
543
559
|
};
|
|
544
560
|
|
|
545
561
|
declare function useReminders(): {
|
|
@@ -557,7 +573,7 @@ declare function useReminders(): {
|
|
|
557
573
|
sendAt: string | Date;
|
|
558
574
|
title: string;
|
|
559
575
|
body: string;
|
|
560
|
-
url
|
|
576
|
+
url: string;
|
|
561
577
|
}>) => Promise<{
|
|
562
578
|
accepted: number;
|
|
563
579
|
rejected: number;
|
|
@@ -569,7 +585,7 @@ declare function useReminders(): {
|
|
|
569
585
|
slot: string;
|
|
570
586
|
title: string;
|
|
571
587
|
body: string;
|
|
572
|
-
url
|
|
588
|
+
url: string;
|
|
573
589
|
}>) => Promise<void>>[0]) => Promise<void>;
|
|
574
590
|
};
|
|
575
591
|
|
|
@@ -717,7 +733,7 @@ type OnboardingFlowProps = {
|
|
|
717
733
|
onComplete: (value: Record<string, unknown>) => void;
|
|
718
734
|
persistKey: string;
|
|
719
735
|
};
|
|
720
|
-
declare function OnboardingFlow({ steps, screens, onComplete, persistKey, }: OnboardingFlowProps): react_jsx_runtime.JSX.Element;
|
|
736
|
+
declare function OnboardingFlow({ steps, screens, onComplete, persistKey, }: OnboardingFlowProps): react_jsx_runtime.JSX.Element | null;
|
|
721
737
|
|
|
722
738
|
type OnboardingStepCtx = {
|
|
723
739
|
stepIndex: number;
|
|
@@ -815,6 +831,7 @@ declare const AppConfigSchema: z.ZodObject<{
|
|
|
815
831
|
emailVerify: z.ZodOptional<z.ZodString>;
|
|
816
832
|
}, z.core.$strict>>;
|
|
817
833
|
features_enabled: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
834
|
+
theme: z.ZodOptional<z.ZodObject<{}, z.core.$loose>>;
|
|
818
835
|
}, z.core.$strict>;
|
|
819
836
|
declare function parseAppConfig(input: unknown): AppConfig;
|
|
820
837
|
|
package/dist/index.js
CHANGED
|
@@ -83,7 +83,12 @@ var AppConfigSchema = z.object({
|
|
|
83
83
|
persistedKeys: z.array(PersistedKeySchema),
|
|
84
84
|
onboarding: OnboardingSchema.optional(),
|
|
85
85
|
deepLinks: DeepLinksSchema.optional(),
|
|
86
|
-
features_enabled: z.array(z.string()).optional()
|
|
86
|
+
features_enabled: z.array(z.string()).optional(),
|
|
87
|
+
// Build-time injected theme metadata (e.g. icon_url for InstallSplash).
|
|
88
|
+
// Apps don't author this directly; deploy workflows fill it from
|
|
89
|
+
// env-resolved bundle host. Permissive shape so apps/workflows can
|
|
90
|
+
// extend without re-bumping the template schema.
|
|
91
|
+
theme: z.object({}).passthrough().optional()
|
|
87
92
|
}).strict();
|
|
88
93
|
function parseAppConfig(input) {
|
|
89
94
|
const r = AppConfigSchema.safeParse(input);
|
|
@@ -1759,8 +1764,15 @@ var ErrorBoundary = class extends Component {
|
|
|
1759
1764
|
static getDerivedStateFromError(error) {
|
|
1760
1765
|
return { error };
|
|
1761
1766
|
}
|
|
1762
|
-
componentDidCatch(error) {
|
|
1763
|
-
console.error(
|
|
1767
|
+
componentDidCatch(error, info) {
|
|
1768
|
+
console.error(
|
|
1769
|
+
"[ErrorBoundary] caught:",
|
|
1770
|
+
error?.message || "(no message)",
|
|
1771
|
+
"\nstack:",
|
|
1772
|
+
error?.stack || "(no stack)",
|
|
1773
|
+
"\ncomponentStack:",
|
|
1774
|
+
info?.componentStack || "(no componentStack)"
|
|
1775
|
+
);
|
|
1764
1776
|
}
|
|
1765
1777
|
render() {
|
|
1766
1778
|
if (this.state.error) {
|
|
@@ -1988,6 +2000,8 @@ function FallbackPaywall() {
|
|
|
1988
2000
|
// src/hooks/usePush.ts
|
|
1989
2001
|
import { useCallback as useCallback4, useEffect as useEffect6, useState as useState5 } from "react";
|
|
1990
2002
|
import { useHook as useHook5 } from "@hook-sdk/sdk";
|
|
2003
|
+
var DISMISS_STORAGE_KEY = "push:dismissed-until";
|
|
2004
|
+
var DISMISS_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
1991
2005
|
function detectIosNeedsInstall() {
|
|
1992
2006
|
if (typeof navigator === "undefined" || typeof window === "undefined") return false;
|
|
1993
2007
|
const ua = navigator.userAgent || "";
|
|
@@ -1998,6 +2012,21 @@ function detectIosNeedsInstall() {
|
|
|
1998
2012
|
const legacyStandalone = typeof navigator.standalone === "boolean" ? navigator.standalone : false;
|
|
1999
2013
|
return !(standalone || legacyStandalone);
|
|
2000
2014
|
}
|
|
2015
|
+
function readDismissedUntil() {
|
|
2016
|
+
if (typeof localStorage === "undefined") return null;
|
|
2017
|
+
try {
|
|
2018
|
+
const raw = localStorage.getItem(DISMISS_STORAGE_KEY);
|
|
2019
|
+
if (raw === null) return null;
|
|
2020
|
+
const n = Number.parseInt(raw, 10);
|
|
2021
|
+
return Number.isFinite(n) ? n : null;
|
|
2022
|
+
} catch {
|
|
2023
|
+
return null;
|
|
2024
|
+
}
|
|
2025
|
+
}
|
|
2026
|
+
function isDismissedNow() {
|
|
2027
|
+
const until = readDismissedUntil();
|
|
2028
|
+
return until !== null && until > Date.now();
|
|
2029
|
+
}
|
|
2001
2030
|
function deriveState(push) {
|
|
2002
2031
|
if (!push.isAvailable()) {
|
|
2003
2032
|
if (detectIosNeedsInstall()) return { kind: "ios_needs_install" };
|
|
@@ -2010,6 +2039,7 @@ function deriveState(push) {
|
|
|
2010
2039
|
if (detectIosNeedsInstall()) return { kind: "ios_needs_install" };
|
|
2011
2040
|
return { kind: "unsupported" };
|
|
2012
2041
|
}
|
|
2042
|
+
if (isDismissedNow()) return { kind: "dismissed" };
|
|
2013
2043
|
return { kind: "prompt" };
|
|
2014
2044
|
}
|
|
2015
2045
|
function usePush() {
|
|
@@ -2039,14 +2069,41 @@ function usePush() {
|
|
|
2039
2069
|
throw e;
|
|
2040
2070
|
}
|
|
2041
2071
|
}, [push]);
|
|
2042
|
-
|
|
2072
|
+
const dismiss = useCallback4(() => {
|
|
2073
|
+
if (typeof localStorage !== "undefined") {
|
|
2074
|
+
try {
|
|
2075
|
+
localStorage.setItem(DISMISS_STORAGE_KEY, String(Date.now() + DISMISS_TTL_MS));
|
|
2076
|
+
} catch {
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
setState({ kind: "dismissed" });
|
|
2080
|
+
}, []);
|
|
2081
|
+
return { state, subscribe, unsubscribe, dismiss };
|
|
2043
2082
|
}
|
|
2044
2083
|
|
|
2045
2084
|
// src/components/PushPrompt.tsx
|
|
2046
2085
|
import { jsx as jsx19, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
2086
|
+
function platformRecoveryCopy(texts) {
|
|
2087
|
+
if (typeof navigator === "undefined") return null;
|
|
2088
|
+
const ua = navigator.userAgent || "";
|
|
2089
|
+
const platform = detectPlatform(ua);
|
|
2090
|
+
switch (platform) {
|
|
2091
|
+
case "ios-safari":
|
|
2092
|
+
case "ios-other":
|
|
2093
|
+
return texts.deniedRecoveryIos ?? null;
|
|
2094
|
+
case "android":
|
|
2095
|
+
return texts.deniedRecoveryAndroid ?? null;
|
|
2096
|
+
case "desktop":
|
|
2097
|
+
return texts.deniedRecoveryDesktop ?? null;
|
|
2098
|
+
case "in-app":
|
|
2099
|
+
return texts.deniedRecoveryInApp ?? null;
|
|
2100
|
+
default:
|
|
2101
|
+
return null;
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2047
2104
|
function PushPrompt2({ texts, onSubscribed, onDeclined, onInstallRequested, className }) {
|
|
2048
2105
|
const { state, subscribe } = usePush();
|
|
2049
|
-
if (state.kind === "subscribed") return null;
|
|
2106
|
+
if (state.kind === "subscribed" || state.kind === "dismissed") return null;
|
|
2050
2107
|
if (state.kind === "ios_needs_install") {
|
|
2051
2108
|
return /* @__PURE__ */ jsxs14("div", { className, role: "region", "aria-label": texts.iosInstallTitle, children: [
|
|
2052
2109
|
/* @__PURE__ */ jsx19("h3", { children: texts.iosInstallTitle }),
|
|
@@ -2055,9 +2112,11 @@ function PushPrompt2({ texts, onSubscribed, onDeclined, onInstallRequested, clas
|
|
|
2055
2112
|
] });
|
|
2056
2113
|
}
|
|
2057
2114
|
if (state.kind === "denied") {
|
|
2115
|
+
const recovery = platformRecoveryCopy(texts);
|
|
2058
2116
|
return /* @__PURE__ */ jsxs14("div", { className, role: "region", "aria-label": texts.deniedTitle, children: [
|
|
2059
2117
|
/* @__PURE__ */ jsx19("h3", { children: texts.deniedTitle }),
|
|
2060
|
-
/* @__PURE__ */ jsx19("p", { children: texts.deniedBody })
|
|
2118
|
+
/* @__PURE__ */ jsx19("p", { children: texts.deniedBody }),
|
|
2119
|
+
recovery && /* @__PURE__ */ jsx19("p", { "data-testid": "denied-recovery", children: recovery })
|
|
2061
2120
|
] });
|
|
2062
2121
|
}
|
|
2063
2122
|
if (state.kind === "unsupported") {
|
|
@@ -2515,7 +2574,7 @@ function PreAuthShell({
|
|
|
2515
2574
|
}
|
|
2516
2575
|
|
|
2517
2576
|
// src/OnboardingFlow.tsx
|
|
2518
|
-
import { useCallback as useCallback11, useMemo as useMemo8, useRef as useRef3
|
|
2577
|
+
import { useCallback as useCallback11, useMemo as useMemo8, useRef as useRef3 } from "react";
|
|
2519
2578
|
import { usePersistedState } from "@hook-sdk/sdk";
|
|
2520
2579
|
|
|
2521
2580
|
// src/hooks/useOnboardingStep.ts
|
|
@@ -2534,62 +2593,82 @@ function useOnboardingStep() {
|
|
|
2534
2593
|
// src/OnboardingFlow.tsx
|
|
2535
2594
|
import { jsx as jsx24 } from "react/jsx-runtime";
|
|
2536
2595
|
var isFilled = (v) => v != null && v !== "";
|
|
2596
|
+
var CURRENT_STEP_FIELD = "currentStep";
|
|
2597
|
+
function readPersistedStepIdx(draft) {
|
|
2598
|
+
const raw = draft[CURRENT_STEP_FIELD];
|
|
2599
|
+
return typeof raw === "number" && Number.isFinite(raw) && raw >= 0 ? raw : 0;
|
|
2600
|
+
}
|
|
2537
2601
|
function OnboardingFlow({
|
|
2538
2602
|
steps,
|
|
2539
2603
|
screens,
|
|
2540
2604
|
onComplete,
|
|
2541
2605
|
persistKey
|
|
2542
2606
|
}) {
|
|
2543
|
-
const [draft, setDraft] = usePersistedState(persistKey, {});
|
|
2544
|
-
const [idx, setIdx] = useState12(0);
|
|
2607
|
+
const [draft, setDraft, status] = usePersistedState(persistKey, {});
|
|
2545
2608
|
const draftRef = useRef3(draft);
|
|
2546
2609
|
draftRef.current = draft;
|
|
2547
|
-
const
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
}
|
|
2559
|
-
const valid = useMemo8(
|
|
2560
|
-
() => (step.validates ?? []).every((field) => isFilled(draft[field])),
|
|
2561
|
-
[draft, step]
|
|
2610
|
+
const idx = readPersistedStepIdx(draft);
|
|
2611
|
+
const clampedIdx = Math.min(Math.max(idx, 0), Math.max(steps.length - 1, 0));
|
|
2612
|
+
const setIdx = useCallback11(
|
|
2613
|
+
(n) => {
|
|
2614
|
+
setDraft((prev) => {
|
|
2615
|
+
const prevIdx = readPersistedStepIdx(prev);
|
|
2616
|
+
const nextIdx = typeof n === "function" ? n(prevIdx) : n;
|
|
2617
|
+
return { ...prev, [CURRENT_STEP_FIELD]: nextIdx };
|
|
2618
|
+
});
|
|
2619
|
+
},
|
|
2620
|
+
[setDraft]
|
|
2562
2621
|
);
|
|
2563
2622
|
const setValue = useCallback11(
|
|
2564
2623
|
(patch) => {
|
|
2565
2624
|
draftRef.current = { ...draftRef.current, ...patch };
|
|
2566
|
-
setDraft((
|
|
2625
|
+
setDraft((prev) => ({ ...prev, ...patch }));
|
|
2567
2626
|
},
|
|
2568
2627
|
[setDraft]
|
|
2569
2628
|
);
|
|
2629
|
+
const step = steps[clampedIdx];
|
|
2630
|
+
const valid = useMemo8(
|
|
2631
|
+
() => step ? (step.validates ?? []).every((field) => isFilled(draft[field])) : false,
|
|
2632
|
+
[draft, step]
|
|
2633
|
+
);
|
|
2570
2634
|
const next = useCallback11(() => {
|
|
2635
|
+
if (!step) return;
|
|
2571
2636
|
const current = draftRef.current;
|
|
2572
2637
|
const validNow = (step.validates ?? []).every((field) => isFilled(current[field]));
|
|
2573
2638
|
if (!validNow) return;
|
|
2574
|
-
if (
|
|
2639
|
+
if (clampedIdx + 1 >= steps.length) {
|
|
2575
2640
|
onComplete(current);
|
|
2576
2641
|
} else {
|
|
2577
|
-
setIdx(
|
|
2642
|
+
setIdx(clampedIdx + 1);
|
|
2578
2643
|
}
|
|
2579
|
-
}, [
|
|
2580
|
-
const
|
|
2644
|
+
}, [clampedIdx, onComplete, step, steps.length, setIdx]);
|
|
2645
|
+
const prevStep = useCallback11(() => setIdx((i) => Math.max(0, i - 1)), [setIdx]);
|
|
2581
2646
|
const ctx = useMemo8(
|
|
2582
2647
|
() => ({
|
|
2583
|
-
stepIndex:
|
|
2648
|
+
stepIndex: clampedIdx,
|
|
2584
2649
|
totalSteps: steps.length,
|
|
2585
2650
|
value: draft,
|
|
2586
2651
|
setValue,
|
|
2587
2652
|
valid,
|
|
2588
2653
|
next,
|
|
2589
|
-
prev
|
|
2654
|
+
prev: prevStep
|
|
2590
2655
|
}),
|
|
2591
|
-
[
|
|
2656
|
+
[clampedIdx, steps.length, draft, setValue, valid, next, prevStep]
|
|
2592
2657
|
);
|
|
2658
|
+
if (status.loading) {
|
|
2659
|
+
return null;
|
|
2660
|
+
}
|
|
2661
|
+
if (!step) {
|
|
2662
|
+
throw new Error(
|
|
2663
|
+
`[hook-template] OnboardingFlow: step index ${clampedIdx} out of range (steps.length=${steps.length})`
|
|
2664
|
+
);
|
|
2665
|
+
}
|
|
2666
|
+
const Screen = screens[step.screen];
|
|
2667
|
+
if (!Screen) {
|
|
2668
|
+
throw new Error(
|
|
2669
|
+
`[hook-template] OnboardingFlow: missing screen component for step '${step.id}' (expected key '${step.screen}' in screens prop)`
|
|
2670
|
+
);
|
|
2671
|
+
}
|
|
2593
2672
|
return /* @__PURE__ */ jsx24(OnboardingStepContext.Provider, { value: ctx, children: /* @__PURE__ */ jsx24(Screen, {}) });
|
|
2594
2673
|
}
|
|
2595
2674
|
|