@hook-sdk/template 0.10.0 → 0.11.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.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 {
@@ -160,7 +161,9 @@ interface State {
160
161
  declare class ErrorBoundary extends Component<Props, State> {
161
162
  state: State;
162
163
  static getDerivedStateFromError(error: Error): State;
163
- componentDidCatch(error: Error): void;
164
+ componentDidCatch(error: Error, info: {
165
+ componentStack: string;
166
+ }): void;
164
167
  render(): string | number | boolean | Iterable<ReactNode> | react_jsx_runtime.JSX.Element;
165
168
  }
166
169
 
@@ -717,7 +720,7 @@ type OnboardingFlowProps = {
717
720
  onComplete: (value: Record<string, unknown>) => void;
718
721
  persistKey: string;
719
722
  };
720
- declare function OnboardingFlow({ steps, screens, onComplete, persistKey, }: OnboardingFlowProps): react_jsx_runtime.JSX.Element;
723
+ declare function OnboardingFlow({ steps, screens, onComplete, persistKey, }: OnboardingFlowProps): react_jsx_runtime.JSX.Element | null;
721
724
 
722
725
  type OnboardingStepCtx = {
723
726
  stepIndex: number;
@@ -815,6 +818,7 @@ declare const AppConfigSchema: z.ZodObject<{
815
818
  emailVerify: z.ZodOptional<z.ZodString>;
816
819
  }, z.core.$strict>>;
817
820
  features_enabled: z.ZodOptional<z.ZodArray<z.ZodString>>;
821
+ theme: z.ZodOptional<z.ZodObject<{}, z.core.$loose>>;
818
822
  }, z.core.$strict>;
819
823
  declare function parseAppConfig(input: unknown): AppConfig;
820
824
 
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 {
@@ -160,7 +161,9 @@ interface State {
160
161
  declare class ErrorBoundary extends Component<Props, State> {
161
162
  state: State;
162
163
  static getDerivedStateFromError(error: Error): State;
163
- componentDidCatch(error: Error): void;
164
+ componentDidCatch(error: Error, info: {
165
+ componentStack: string;
166
+ }): void;
164
167
  render(): string | number | boolean | Iterable<ReactNode> | react_jsx_runtime.JSX.Element;
165
168
  }
166
169
 
@@ -717,7 +720,7 @@ type OnboardingFlowProps = {
717
720
  onComplete: (value: Record<string, unknown>) => void;
718
721
  persistKey: string;
719
722
  };
720
- declare function OnboardingFlow({ steps, screens, onComplete, persistKey, }: OnboardingFlowProps): react_jsx_runtime.JSX.Element;
723
+ declare function OnboardingFlow({ steps, screens, onComplete, persistKey, }: OnboardingFlowProps): react_jsx_runtime.JSX.Element | null;
721
724
 
722
725
  type OnboardingStepCtx = {
723
726
  stepIndex: number;
@@ -815,6 +818,7 @@ declare const AppConfigSchema: z.ZodObject<{
815
818
  emailVerify: z.ZodOptional<z.ZodString>;
816
819
  }, z.core.$strict>>;
817
820
  features_enabled: z.ZodOptional<z.ZodArray<z.ZodString>>;
821
+ theme: z.ZodOptional<z.ZodObject<{}, z.core.$loose>>;
818
822
  }, z.core.$strict>;
819
823
  declare function parseAppConfig(input: unknown): AppConfig;
820
824
 
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("[ErrorBoundary] caught", 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) {
@@ -2515,7 +2527,7 @@ function PreAuthShell({
2515
2527
  }
2516
2528
 
2517
2529
  // src/OnboardingFlow.tsx
2518
- import { useCallback as useCallback11, useMemo as useMemo8, useRef as useRef3, useState as useState12 } from "react";
2530
+ import { useCallback as useCallback11, useMemo as useMemo8, useRef as useRef3 } from "react";
2519
2531
  import { usePersistedState } from "@hook-sdk/sdk";
2520
2532
 
2521
2533
  // src/hooks/useOnboardingStep.ts
@@ -2534,62 +2546,82 @@ function useOnboardingStep() {
2534
2546
  // src/OnboardingFlow.tsx
2535
2547
  import { jsx as jsx24 } from "react/jsx-runtime";
2536
2548
  var isFilled = (v) => v != null && v !== "";
2549
+ var CURRENT_STEP_FIELD = "currentStep";
2550
+ function readPersistedStepIdx(draft) {
2551
+ const raw = draft[CURRENT_STEP_FIELD];
2552
+ return typeof raw === "number" && Number.isFinite(raw) && raw >= 0 ? raw : 0;
2553
+ }
2537
2554
  function OnboardingFlow({
2538
2555
  steps,
2539
2556
  screens,
2540
2557
  onComplete,
2541
2558
  persistKey
2542
2559
  }) {
2543
- const [draft, setDraft] = usePersistedState(persistKey, {});
2544
- const [idx, setIdx] = useState12(0);
2560
+ const [draft, setDraft, status] = usePersistedState(persistKey, {});
2545
2561
  const draftRef = useRef3(draft);
2546
2562
  draftRef.current = draft;
2547
- const step = steps[idx];
2548
- if (!step) {
2549
- throw new Error(
2550
- `[hook-template] OnboardingFlow: step index ${idx} out of range (steps.length=${steps.length})`
2551
- );
2552
- }
2553
- const Screen = screens[step.screen];
2554
- if (!Screen) {
2555
- throw new Error(
2556
- `[hook-template] OnboardingFlow: missing screen component for step '${step.id}' (expected key '${step.screen}' in screens prop)`
2557
- );
2558
- }
2559
- const valid = useMemo8(
2560
- () => (step.validates ?? []).every((field) => isFilled(draft[field])),
2561
- [draft, step]
2563
+ const idx = readPersistedStepIdx(draft);
2564
+ const clampedIdx = Math.min(Math.max(idx, 0), Math.max(steps.length - 1, 0));
2565
+ const setIdx = useCallback11(
2566
+ (n) => {
2567
+ setDraft((prev) => {
2568
+ const prevIdx = readPersistedStepIdx(prev);
2569
+ const nextIdx = typeof n === "function" ? n(prevIdx) : n;
2570
+ return { ...prev, [CURRENT_STEP_FIELD]: nextIdx };
2571
+ });
2572
+ },
2573
+ [setDraft]
2562
2574
  );
2563
2575
  const setValue = useCallback11(
2564
2576
  (patch) => {
2565
2577
  draftRef.current = { ...draftRef.current, ...patch };
2566
- setDraft((prev2) => ({ ...prev2, ...patch }));
2578
+ setDraft((prev) => ({ ...prev, ...patch }));
2567
2579
  },
2568
2580
  [setDraft]
2569
2581
  );
2582
+ const step = steps[clampedIdx];
2583
+ const valid = useMemo8(
2584
+ () => step ? (step.validates ?? []).every((field) => isFilled(draft[field])) : false,
2585
+ [draft, step]
2586
+ );
2570
2587
  const next = useCallback11(() => {
2588
+ if (!step) return;
2571
2589
  const current = draftRef.current;
2572
2590
  const validNow = (step.validates ?? []).every((field) => isFilled(current[field]));
2573
2591
  if (!validNow) return;
2574
- if (idx + 1 >= steps.length) {
2592
+ if (clampedIdx + 1 >= steps.length) {
2575
2593
  onComplete(current);
2576
2594
  } else {
2577
- setIdx(idx + 1);
2595
+ setIdx(clampedIdx + 1);
2578
2596
  }
2579
- }, [idx, onComplete, step, steps.length]);
2580
- const prev = useCallback11(() => setIdx((i) => Math.max(0, i - 1)), []);
2597
+ }, [clampedIdx, onComplete, step, steps.length, setIdx]);
2598
+ const prevStep = useCallback11(() => setIdx((i) => Math.max(0, i - 1)), [setIdx]);
2581
2599
  const ctx = useMemo8(
2582
2600
  () => ({
2583
- stepIndex: idx,
2601
+ stepIndex: clampedIdx,
2584
2602
  totalSteps: steps.length,
2585
2603
  value: draft,
2586
2604
  setValue,
2587
2605
  valid,
2588
2606
  next,
2589
- prev
2607
+ prev: prevStep
2590
2608
  }),
2591
- [idx, steps.length, draft, setValue, valid, next, prev]
2609
+ [clampedIdx, steps.length, draft, setValue, valid, next, prevStep]
2592
2610
  );
2611
+ if (status.loading) {
2612
+ return null;
2613
+ }
2614
+ if (!step) {
2615
+ throw new Error(
2616
+ `[hook-template] OnboardingFlow: step index ${clampedIdx} out of range (steps.length=${steps.length})`
2617
+ );
2618
+ }
2619
+ const Screen = screens[step.screen];
2620
+ if (!Screen) {
2621
+ throw new Error(
2622
+ `[hook-template] OnboardingFlow: missing screen component for step '${step.id}' (expected key '${step.screen}' in screens prop)`
2623
+ );
2624
+ }
2593
2625
  return /* @__PURE__ */ jsx24(OnboardingStepContext.Provider, { value: ctx, children: /* @__PURE__ */ jsx24(Screen, {}) });
2594
2626
  }
2595
2627