@waypointjs/next 0.1.2 → 0.1.5

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 CHANGED
@@ -26,6 +26,8 @@ function WaypointRunner({
26
26
  onComplete,
27
27
  onStepComplete,
28
28
  onDataChange,
29
+ customFieldTypes,
30
+ externalEnums,
29
31
  children
30
32
  }) {
31
33
  const router = navigation.useRouter();
@@ -49,7 +51,7 @@ function WaypointRunner({
49
51
  store.getState().resume(schema, externalVars);
50
52
  const state2 = store.getState();
51
53
  if (state2.currentStepId) {
52
- const tree2 = core.resolveTree(schema, state2.data, state2.externalVars);
54
+ const tree2 = core.resolveTree(schema, state2.data, state2.externalVars, externalEnums);
53
55
  const step = tree2.steps.find(
54
56
  (s) => s.definition.id === state2.currentStepId
55
57
  );
@@ -73,7 +75,7 @@ function WaypointRunner({
73
75
  if (cancelled) return;
74
76
  store.getState().init(schema, { data, externalVars });
75
77
  const state = store.getState();
76
- const tree = core.resolveTree(schema, state.data, state.externalVars);
78
+ const tree = core.resolveTree(schema, state.data, state.externalVars, externalEnums);
77
79
  const lastValid = core.findLastValidStep(tree.steps, state.data, state.externalVars);
78
80
  if (lastValid && lastValid.definition.url !== pathname) {
79
81
  router.push(lastValid.definition.url);
@@ -95,10 +97,12 @@ function WaypointRunner({
95
97
  store,
96
98
  onComplete,
97
99
  onStepComplete,
98
- onDataChange
100
+ onDataChange,
101
+ customFieldTypes,
102
+ externalEnums
99
103
  }),
100
104
  // eslint-disable-next-line react-hooks/exhaustive-deps
101
- [schema, store]
105
+ [schema, store, externalEnums]
102
106
  );
103
107
  if (missingVars.length > 0) {
104
108
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -127,7 +131,7 @@ function WaypointRunner({
127
131
  return /* @__PURE__ */ jsxRuntime.jsx(WaypointRuntimeContext.Provider, { value: contextValue, children });
128
132
  }
129
133
  function useWaypointStep() {
130
- const { schema, store, onComplete, onStepComplete, onDataChange } = useWaypointRuntimeContext();
134
+ const { schema, store, onComplete, onStepComplete, onDataChange, externalEnums } = useWaypointRuntimeContext();
131
135
  const router = navigation.useRouter();
132
136
  const pathname = navigation.usePathname();
133
137
  const { data, externalVars, currentStepId, isSubmitting } = zustand.useStore(
@@ -140,8 +144,8 @@ function useWaypointStep() {
140
144
  })
141
145
  );
142
146
  const tree = react.useMemo(
143
- () => core.resolveTree(schema, data, externalVars),
144
- [schema, data, externalVars]
147
+ () => core.resolveTree(schema, data, externalVars, externalEnums),
148
+ [schema, data, externalVars, externalEnums]
145
149
  );
146
150
  const currentStep = react.useMemo(() => {
147
151
  return tree.steps.find((s) => {
@@ -195,7 +199,7 @@ function useWaypointStep() {
195
199
  const oldVisibleIds = tree.steps.map((s) => s.definition.id).join(",");
196
200
  store.getState().setStepData(currentStep.definition.id, values);
197
201
  const allData = store.getState().data;
198
- const updatedTree = core.resolveTree(schema, allData, externalVars);
202
+ const updatedTree = core.resolveTree(schema, allData, externalVars, externalEnums);
199
203
  const newVisibleIds = updatedTree.steps.map((s) => s.definition.id).join(",");
200
204
  if (oldVisibleIds !== newVisibleIds) {
201
205
  store.getState().truncateHistoryAt(currentStep.definition.id);
@@ -221,6 +225,7 @@ function useWaypointStep() {
221
225
  schema,
222
226
  tree.steps,
223
227
  externalVars,
228
+ externalEnums,
224
229
  onDataChange,
225
230
  onStepComplete,
226
231
  onComplete,
@@ -241,6 +246,7 @@ function useWaypointStep() {
241
246
  }
242
247
 
243
248
  exports.WaypointRunner = WaypointRunner;
249
+ exports.WaypointRuntimeContext = WaypointRuntimeContext;
244
250
  exports.useWaypointRuntimeContext = useWaypointRuntimeContext;
245
251
  exports.useWaypointStep = useWaypointStep;
246
252
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/context.ts","../src/WaypointRunner.tsx","../src/useWaypointStep.ts"],"names":["createContext","useContext","useRouter","usePathname","useRef","createRuntimeStore","useStore","resolveTree","useEffect","hasPersistedState","state","tree","findLastValidStep","useMemo","jsxs","jsx","buildZodSchema","useForm","zodResolver","findStepIndex","calculateProgress","useCallback","getPreviousStep","getNextStep"],"mappings":"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","file":"index.cjs","sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { StoreApi } from \"zustand\";\n\nimport type { WaypointSchema } from \"@waypointjs/core\";\nimport type { WaypointRuntimeStore } from \"@waypointjs/core\";\n\n// ---------------------------------------------------------------------------\n// Context value\n// ---------------------------------------------------------------------------\n\nexport interface WaypointRuntimeContextValue {\n schema: WaypointSchema;\n store: StoreApi<WaypointRuntimeStore>;\n onComplete?: (data: Record<string, Record<string, unknown>>) => void | Promise<void>;\n onStepComplete?: (\n stepId: string,\n data: Record<string, unknown>\n ) => void | Promise<void>;\n onDataChange?: (data: Record<string, Record<string, unknown>>) => void;\n}\n\n// ---------------------------------------------------------------------------\n// Context\n// ---------------------------------------------------------------------------\n\nexport const WaypointRuntimeContext =\n createContext<WaypointRuntimeContextValue | null>(null);\n\n/**\n * Returns the current WaypointRunner context.\n * Must be used inside a `<WaypointRunner>` component.\n */\nexport function useWaypointRuntimeContext(): WaypointRuntimeContextValue {\n const ctx = useContext(WaypointRuntimeContext);\n if (!ctx) {\n throw new Error(\n \"useWaypointRuntimeContext must be called inside a <WaypointRunner> component.\"\n );\n }\n return ctx;\n}\n","\"use client\";\n\nimport { useEffect, useMemo, useRef } from \"react\";\nimport { useStore } from \"zustand\";\nimport { useRouter, usePathname } from \"next/navigation\";\n\nimport {\n createRuntimeStore,\n hasPersistedState,\n resolveTree,\n findLastValidStep,\n} from \"@waypointjs/core\";\nimport type { WaypointSchema, WaypointRuntimeStore } from \"@waypointjs/core\";\n\nimport { WaypointRuntimeContext } from \"./context\";\n\n// ---------------------------------------------------------------------------\n// Props\n// ---------------------------------------------------------------------------\n\nexport interface WaypointRunnerProps {\n schema: WaypointSchema;\n externalVars?: Record<string, unknown>;\n defaultValues?: Record<string, Record<string, unknown>>;\n /** Async function to load previously-saved data (for deep-link resume) */\n fetchData?: () => Promise<Record<string, Record<string, unknown>>>;\n /** Called when the user completes the last step */\n onComplete?: (data: Record<string, Record<string, unknown>>) => void | Promise<void>;\n /** Called after each step is validated and submitted */\n onStepComplete?: (\n stepId: string,\n data: Record<string, unknown>\n ) => void | Promise<void>;\n /** Called whenever any field value changes */\n onDataChange?: (data: Record<string, Record<string, unknown>>) => void;\n children: React.ReactNode;\n}\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\n/**\n * Context provider that initialises the runtime store and wires up callbacks.\n *\n * ### Multi-journey support\n * Each `<WaypointRunner>` creates its own **isolated** Zustand store instance,\n * so multiple runners with different schemas can coexist in the same app\n * without any state interference:\n *\n * ```tsx\n * // Both journeys live side-by-side — each has its own store\n * <WaypointRunner schema={projectSchema}>…</WaypointRunner>\n * <WaypointRunner schema={depositSchema}>…</WaypointRunner>\n * ```\n *\n * ### Pause & Resume\n * When `schema.persistenceMode === \"zustand\"`, each journey's state\n * (`data`, `currentStepId`, `history`) is saved to localStorage under a\n * per-schema key (`waypoint-runtime-<schemaId>`).\n *\n * On remount, `WaypointRunner` detects the saved state and calls `resume()`\n * instead of `init()`, so the user lands back exactly where they left off —\n * navigation state and form data intact.\n *\n * @example\n * <WaypointRunner schema={mySchema} onComplete={handleComplete}>\n * {children}\n * </WaypointRunner>\n */\nexport function WaypointRunner({\n schema,\n externalVars = {},\n defaultValues = {},\n fetchData,\n onComplete,\n onStepComplete,\n onDataChange,\n children,\n}: WaypointRunnerProps) {\n const router = useRouter();\n const pathname = usePathname();\n\n // Create store once per runner instance.\n // The persist middleware (when active) hydrates synchronously from localStorage,\n // so by the time the first useEffect fires, persisted state is already in the store.\n const storeRef = useRef<ReturnType<typeof createRuntimeStore> | null>(null);\n if (storeRef.current === null) {\n storeRef.current = createRuntimeStore({\n persistenceMode: schema.persistenceMode,\n schemaId: schema.id,\n });\n }\n const store = storeRef.current;\n\n // Subscribe to missing blocking vars to show error UI reactively\n const missingVars = useStore(store, (s: WaypointRuntimeStore) => {\n if (!s.schema) return [];\n return resolveTree(s.schema, s.data, s.externalVars).missingExternalVars;\n });\n\n // ---------------------------------------------------------------------------\n // Init / Resume on mount\n // ---------------------------------------------------------------------------\n useEffect(() => {\n let cancelled = false;\n\n async function initialize() {\n // ── Resume path ────────────────────────────────────────────────────────\n // When persistenceMode is \"zustand\", the persist middleware has already\n // synchronously hydrated the store from localStorage by the time this\n // effect runs. If the saved schemaId matches, we resume instead of\n // resetting so the user picks up exactly where they left off.\n if (\n schema.persistenceMode === \"zustand\" &&\n hasPersistedState(store, schema.id)\n ) {\n // Keep data + currentStepId + history — just update schema & externalVars\n store.getState().resume(schema, externalVars);\n\n // Navigate to the persisted step\n const state = store.getState();\n if (state.currentStepId) {\n const tree = resolveTree(schema, state.data, state.externalVars);\n const step = tree.steps.find(\n (s) => s.definition.id === state.currentStepId\n );\n if (step && step.definition.url !== pathname) {\n router.push(step.definition.url);\n }\n }\n return;\n }\n\n // ── Fresh-start path ───────────────────────────────────────────────────\n let data = { ...defaultValues };\n\n if (fetchData) {\n try {\n const fetched = await fetchData();\n if (!cancelled) {\n data = { ...data, ...fetched };\n }\n } catch (err) {\n console.error(\"Waypoint: fetchData failed\", err);\n }\n }\n\n if (cancelled) return;\n\n store.getState().init(schema, { data, externalVars });\n\n // Deep-link resume: if the user already has data, redirect to the last\n // valid step instead of forcing them back to step 1.\n const state = store.getState();\n const tree = resolveTree(schema, state.data, state.externalVars);\n const lastValid = findLastValidStep(tree.steps, state.data, state.externalVars);\n\n if (lastValid && lastValid.definition.url !== pathname) {\n router.push(lastValid.definition.url);\n }\n }\n\n initialize();\n\n return () => {\n cancelled = true;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // Sync externalVars into the store when the prop changes after mount\n useEffect(() => {\n for (const [key, value] of Object.entries(externalVars)) {\n store.getState().setExternalVar(key, value);\n }\n }, [externalVars, store]);\n\n const contextValue = useMemo(\n () => ({\n schema,\n store,\n onComplete,\n onStepComplete,\n onDataChange,\n }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [schema, store]\n );\n\n // Show a blocking error if required external variables are missing after init\n if (missingVars.length > 0) {\n return (\n <div\n role=\"alert\"\n style={{\n color: \"#b91c1c\",\n background: \"#fef2f2\",\n border: \"1px solid #fca5a5\",\n borderRadius: 8,\n padding: \"1rem 1.25rem\",\n fontFamily: \"sans-serif\",\n }}\n >\n <strong>Waypoint Runtime Error</strong>\n <p style={{ margin: \"0.5rem 0 0\" }}>\n Missing required external variables:{\" \"}\n <code>{missingVars.join(\", \")}</code>\n </p>\n </div>\n );\n }\n\n return (\n <WaypointRuntimeContext.Provider value={contextValue}>\n {children}\n </WaypointRuntimeContext.Provider>\n );\n}\n","\"use client\";\n\nimport { useCallback, useEffect, useMemo } from \"react\";\nimport { useRouter, usePathname } from \"next/navigation\";\nimport { useStore } from \"zustand\";\nimport { useForm } from \"react-hook-form\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport type { FieldErrors, FieldValues, UseFormReturn } from \"react-hook-form\";\n\nimport {\n resolveTree,\n buildZodSchema,\n calculateProgress,\n getNextStep,\n getPreviousStep,\n findStepIndex,\n} from \"@waypointjs/core\";\nimport type { ResolvedField, ResolvedStep, WaypointRuntimeStore } from \"@waypointjs/core\";\n\nimport { useWaypointRuntimeContext } from \"./context\";\n\n// ---------------------------------------------------------------------------\n// Return type\n// ---------------------------------------------------------------------------\n\nexport interface WaypointStepReturn {\n // Step context\n currentStep: ResolvedStep | undefined;\n progress: number;\n isFirstStep: boolean;\n isLastStep: boolean;\n\n // React Hook Form\n form: UseFormReturn<FieldValues>;\n /** Visible fields for the current step */\n fields: ResolvedField[];\n\n // Actions\n /** Validate → persist → onStepComplete → navigate next (or onComplete on last step) */\n handleSubmit: () => Promise<void>;\n goBack: () => void;\n\n // State\n isSubmitting: boolean;\n errors: FieldErrors;\n}\n\n// ---------------------------------------------------------------------------\n// Hook\n// ---------------------------------------------------------------------------\n\n/**\n * Per-page hook for multi-step forms powered by WaypointRunner.\n *\n * Derives the current step from the URL, provides react-hook-form wired to\n * the step's Zod schema, and handles navigation automatically.\n *\n * Must be used inside a `<WaypointRunner>` component.\n *\n * @example\n * const { form, fields, handleSubmit, progress } = useWaypointStep();\n */\nexport function useWaypointStep(): WaypointStepReturn {\n const { schema, store, onComplete, onStepComplete, onDataChange } =\n useWaypointRuntimeContext();\n const router = useRouter();\n const pathname = usePathname();\n\n // Subscribe to store state\n const { data, externalVars, currentStepId, isSubmitting } = useStore(\n store,\n (s: WaypointRuntimeStore) => ({\n data: s.data,\n externalVars: s.externalVars,\n currentStepId: s.currentStepId,\n isSubmitting: s.isSubmitting,\n })\n );\n\n // Resolve the full tree\n const tree = useMemo(\n () => resolveTree(schema, data, externalVars),\n [schema, data, externalVars]\n );\n\n // Find the step matching the current pathname\n const currentStep = useMemo(() => {\n return tree.steps.find((s) => {\n const stepUrl = s.definition.url;\n // Exact match, or pathname ends with step URL (handles leading slash variants)\n return pathname === stepUrl || pathname.endsWith(stepUrl);\n });\n }, [tree.steps, pathname]);\n\n // Sync currentStepId into the store whenever the page changes\n useEffect(() => {\n if (currentStep && currentStep.definition.id !== currentStepId) {\n store.getState().setCurrentStep(currentStep.definition.id);\n }\n }, [currentStep, currentStepId, store]);\n\n // Visible fields for the current step\n const visibleFields = useMemo(\n () => currentStep?.fields.filter((f) => f.visible) ?? [],\n [currentStep]\n );\n\n // Build the Zod schema from visible fields\n const zodSchema = useMemo(() => buildZodSchema(visibleFields), [visibleFields]);\n\n // Existing step data used as default values\n const defaultValues = useMemo(\n () => (currentStep ? (data[currentStep.definition.id] ?? {}) : {}),\n // Only recompute when the step changes (not on every data write)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [currentStep?.definition.id]\n );\n\n // React Hook Form instance\n const form = useForm<FieldValues>({\n resolver: zodResolver(zodSchema),\n defaultValues,\n });\n\n // Reset form defaults when step changes\n useEffect(() => {\n if (currentStep) {\n form.reset(data[currentStep.definition.id] ?? {});\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [currentStep?.definition.id]);\n\n // Navigation position\n const stepIndex = currentStep\n ? findStepIndex(tree.steps, currentStep.definition.id)\n : -1;\n const isFirstStep = stepIndex === 0;\n const isLastStep = stepIndex === tree.steps.length - 1;\n const progress = currentStep\n ? calculateProgress(tree.steps, currentStep.definition.id)\n : 0;\n\n // goBack\n const goBack = useCallback(() => {\n if (!currentStep) return;\n const prev = getPreviousStep(tree.steps, currentStep.definition.id);\n if (prev) {\n router.push(prev.definition.url);\n }\n }, [currentStep, tree.steps, router]);\n\n // handleSubmit\n const handleSubmit = useCallback(async () => {\n if (!currentStep) return;\n\n // 1. Validate via RHF + Zod\n const isValid = await form.trigger();\n if (!isValid) return;\n\n const values = form.getValues();\n\n // 2. Persist data + update submitting state\n store.getState().setIsSubmitting(true);\n\n try {\n // 3. Snapshot visible step IDs before writing (to detect tree changes)\n const oldVisibleIds = tree.steps.map((s) => s.definition.id).join(\",\");\n\n // 4. Write validated data into the store\n store.getState().setStepData(currentStep.definition.id, values);\n\n // 5. Re-resolve tree with updated data — step visibility may have changed\n const allData = store.getState().data;\n const updatedTree = resolveTree(schema, allData, externalVars);\n const newVisibleIds = updatedTree.steps.map((s) => s.definition.id).join(\",\");\n\n // 6. If the visible tree changed, truncate stale forward history\n // (e.g. user went back, changed a dep value, tree changed → old path is invalid)\n if (oldVisibleIds !== newVisibleIds) {\n store.getState().truncateHistoryAt(currentStep.definition.id);\n }\n\n // 7. onDataChange callback\n onDataChange?.(allData);\n\n // 8. onStepComplete callback (may be async, e.g. backend-step mode)\n if (onStepComplete) {\n await onStepComplete(currentStep.definition.id, values);\n }\n\n // 9. Navigate using the UPDATED tree so we follow the new step order\n const nextStep = getNextStep(updatedTree.steps, currentStep.definition.id);\n if (nextStep) {\n router.push(nextStep.definition.url);\n } else {\n store.getState().setCompleted(true);\n await onComplete?.(allData);\n }\n } finally {\n store.getState().setIsSubmitting(false);\n }\n }, [\n currentStep,\n form,\n store,\n schema,\n tree.steps,\n externalVars,\n onDataChange,\n onStepComplete,\n onComplete,\n router,\n ]);\n\n return {\n currentStep,\n progress,\n isFirstStep,\n isLastStep,\n form,\n fields: visibleFields,\n handleSubmit,\n goBack,\n isSubmitting,\n errors: form.formState.errors,\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/context.ts","../src/WaypointRunner.tsx","../src/useWaypointStep.ts"],"names":["createContext","useContext","useRouter","usePathname","useRef","createRuntimeStore","useStore","resolveTree","useEffect","hasPersistedState","state","tree","findLastValidStep","useMemo","jsxs","jsx","buildZodSchema","useForm","zodResolver","findStepIndex","calculateProgress","useCallback","getPreviousStep","getNextStep"],"mappings":"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","file":"index.cjs","sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { StoreApi } from \"zustand\";\n\nimport type { WaypointSchema, CustomTypeDefinition, ExternalEnum } from \"@waypointjs/core\";\nimport type { WaypointRuntimeStore } from \"@waypointjs/core\";\n\n// ---------------------------------------------------------------------------\n// Context value\n// ---------------------------------------------------------------------------\n\nexport interface WaypointRuntimeContextValue {\n schema: WaypointSchema;\n store: StoreApi<WaypointRuntimeStore>;\n onComplete?: (data: Record<string, Record<string, unknown>>) => void | Promise<void>;\n onStepComplete?: (\n stepId: string,\n data: Record<string, unknown>\n ) => void | Promise<void>;\n onDataChange?: (data: Record<string, Record<string, unknown>>) => void;\n /** App-provided custom field types — available for rendering custom fields */\n customFieldTypes?: CustomTypeDefinition[];\n /** App-provided external enum lists — resolved into ResolvedField.resolvedOptions by the tree resolver */\n externalEnums?: ExternalEnum[];\n}\n\n// ---------------------------------------------------------------------------\n// Context\n// ---------------------------------------------------------------------------\n\nexport const WaypointRuntimeContext =\n createContext<WaypointRuntimeContextValue | null>(null);\n\n/**\n * Returns the current WaypointRunner context.\n * Must be used inside a `<WaypointRunner>` component.\n */\nexport function useWaypointRuntimeContext(): WaypointRuntimeContextValue {\n const ctx = useContext(WaypointRuntimeContext);\n if (!ctx) {\n throw new Error(\n \"useWaypointRuntimeContext must be called inside a <WaypointRunner> component.\"\n );\n }\n return ctx;\n}\n","\"use client\";\n\nimport { useEffect, useMemo, useRef } from \"react\";\nimport { useStore } from \"zustand\";\nimport { useRouter, usePathname } from \"next/navigation\";\n\nimport {\n createRuntimeStore,\n hasPersistedState,\n resolveTree,\n findLastValidStep,\n} from \"@waypointjs/core\";\nimport type { WaypointSchema, WaypointRuntimeStore, CustomTypeDefinition, ExternalEnum } from \"@waypointjs/core\";\n\nimport { WaypointRuntimeContext } from \"./context\";\n\n// ---------------------------------------------------------------------------\n// Props\n// ---------------------------------------------------------------------------\n\nexport interface WaypointRunnerProps {\n schema: WaypointSchema;\n externalVars?: Record<string, unknown>;\n defaultValues?: Record<string, Record<string, unknown>>;\n /** Async function to load previously-saved data (for deep-link resume) */\n fetchData?: () => Promise<Record<string, Record<string, unknown>>>;\n /** Called when the user completes the last step */\n onComplete?: (data: Record<string, Record<string, unknown>>) => void | Promise<void>;\n /** Called after each step is validated and submitted */\n onStepComplete?: (\n stepId: string,\n data: Record<string, unknown>\n ) => void | Promise<void>;\n /** Called whenever any field value changes */\n onDataChange?: (data: Record<string, Record<string, unknown>>) => void;\n /** App-provided custom field types — exposed via context for custom field rendering */\n customFieldTypes?: CustomTypeDefinition[];\n /** App-provided external enum lists — resolved into ResolvedField.resolvedOptions */\n externalEnums?: ExternalEnum[];\n children: React.ReactNode;\n}\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\n/**\n * Context provider that initialises the runtime store and wires up callbacks.\n *\n * ### Multi-journey support\n * Each `<WaypointRunner>` creates its own **isolated** Zustand store instance,\n * so multiple runners with different schemas can coexist in the same app\n * without any state interference:\n *\n * ```tsx\n * // Both journeys live side-by-side — each has its own store\n * <WaypointRunner schema={projectSchema}>…</WaypointRunner>\n * <WaypointRunner schema={depositSchema}>…</WaypointRunner>\n * ```\n *\n * ### Pause & Resume\n * When `schema.persistenceMode === \"zustand\"`, each journey's state\n * (`data`, `currentStepId`, `history`) is saved to localStorage under a\n * per-schema key (`waypoint-runtime-<schemaId>`).\n *\n * On remount, `WaypointRunner` detects the saved state and calls `resume()`\n * instead of `init()`, so the user lands back exactly where they left off —\n * navigation state and form data intact.\n *\n * @example\n * <WaypointRunner schema={mySchema} onComplete={handleComplete}>\n * {children}\n * </WaypointRunner>\n */\nexport function WaypointRunner({\n schema,\n externalVars = {},\n defaultValues = {},\n fetchData,\n onComplete,\n onStepComplete,\n onDataChange,\n customFieldTypes,\n externalEnums,\n children,\n}: WaypointRunnerProps) {\n const router = useRouter();\n const pathname = usePathname();\n\n // Create store once per runner instance.\n // The persist middleware (when active) hydrates synchronously from localStorage,\n // so by the time the first useEffect fires, persisted state is already in the store.\n const storeRef = useRef<ReturnType<typeof createRuntimeStore> | null>(null);\n if (storeRef.current === null) {\n storeRef.current = createRuntimeStore({\n persistenceMode: schema.persistenceMode,\n schemaId: schema.id,\n });\n }\n const store = storeRef.current;\n\n // Subscribe to missing blocking vars to show error UI reactively\n const missingVars = useStore(store, (s: WaypointRuntimeStore) => {\n if (!s.schema) return [];\n return resolveTree(s.schema, s.data, s.externalVars).missingExternalVars;\n });\n\n // ---------------------------------------------------------------------------\n // Init / Resume on mount\n // ---------------------------------------------------------------------------\n useEffect(() => {\n let cancelled = false;\n\n async function initialize() {\n // ── Resume path ────────────────────────────────────────────────────────\n // When persistenceMode is \"zustand\", the persist middleware has already\n // synchronously hydrated the store from localStorage by the time this\n // effect runs. If the saved schemaId matches, we resume instead of\n // resetting so the user picks up exactly where they left off.\n if (\n schema.persistenceMode === \"zustand\" &&\n hasPersistedState(store, schema.id)\n ) {\n // Keep data + currentStepId + history — just update schema & externalVars\n store.getState().resume(schema, externalVars);\n\n // Navigate to the persisted step\n const state = store.getState();\n if (state.currentStepId) {\n const tree = resolveTree(schema, state.data, state.externalVars, externalEnums);\n const step = tree.steps.find(\n (s) => s.definition.id === state.currentStepId\n );\n if (step && step.definition.url !== pathname) {\n router.push(step.definition.url);\n }\n }\n return;\n }\n\n // ── Fresh-start path ───────────────────────────────────────────────────\n let data = { ...defaultValues };\n\n if (fetchData) {\n try {\n const fetched = await fetchData();\n if (!cancelled) {\n data = { ...data, ...fetched };\n }\n } catch (err) {\n console.error(\"Waypoint: fetchData failed\", err);\n }\n }\n\n if (cancelled) return;\n\n store.getState().init(schema, { data, externalVars });\n\n // Deep-link resume: if the user already has data, redirect to the last\n // valid step instead of forcing them back to step 1.\n const state = store.getState();\n const tree = resolveTree(schema, state.data, state.externalVars, externalEnums);\n const lastValid = findLastValidStep(tree.steps, state.data, state.externalVars);\n\n if (lastValid && lastValid.definition.url !== pathname) {\n router.push(lastValid.definition.url);\n }\n }\n\n initialize();\n\n return () => {\n cancelled = true;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // Sync externalVars into the store when the prop changes after mount\n useEffect(() => {\n for (const [key, value] of Object.entries(externalVars)) {\n store.getState().setExternalVar(key, value);\n }\n }, [externalVars, store]);\n\n const contextValue = useMemo(\n () => ({\n schema,\n store,\n onComplete,\n onStepComplete,\n onDataChange,\n customFieldTypes,\n externalEnums,\n }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [schema, store, externalEnums]\n );\n\n // Show a blocking error if required external variables are missing after init\n if (missingVars.length > 0) {\n return (\n <div\n role=\"alert\"\n style={{\n color: \"#b91c1c\",\n background: \"#fef2f2\",\n border: \"1px solid #fca5a5\",\n borderRadius: 8,\n padding: \"1rem 1.25rem\",\n fontFamily: \"sans-serif\",\n }}\n >\n <strong>Waypoint Runtime Error</strong>\n <p style={{ margin: \"0.5rem 0 0\" }}>\n Missing required external variables:{\" \"}\n <code>{missingVars.join(\", \")}</code>\n </p>\n </div>\n );\n }\n\n return (\n <WaypointRuntimeContext.Provider value={contextValue}>\n {children}\n </WaypointRuntimeContext.Provider>\n );\n}\n","\"use client\";\n\nimport { useCallback, useEffect, useMemo } from \"react\";\nimport { useRouter, usePathname } from \"next/navigation\";\nimport { useStore } from \"zustand\";\nimport { useForm } from \"react-hook-form\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport type { FieldErrors, FieldValues, UseFormReturn } from \"react-hook-form\";\n\nimport {\n resolveTree,\n buildZodSchema,\n calculateProgress,\n getNextStep,\n getPreviousStep,\n findStepIndex,\n} from \"@waypointjs/core\";\nimport type { ResolvedField, ResolvedStep, WaypointRuntimeStore } from \"@waypointjs/core\";\n\nimport { useWaypointRuntimeContext } from \"./context\";\n\n// ---------------------------------------------------------------------------\n// Return type\n// ---------------------------------------------------------------------------\n\nexport interface WaypointStepReturn {\n // Step context\n currentStep: ResolvedStep | undefined;\n progress: number;\n isFirstStep: boolean;\n isLastStep: boolean;\n\n // React Hook Form\n form: UseFormReturn<FieldValues>;\n /** Visible fields for the current step */\n fields: ResolvedField[];\n\n // Actions\n /** Validate → persist → onStepComplete → navigate next (or onComplete on last step) */\n handleSubmit: () => Promise<void>;\n goBack: () => void;\n\n // State\n isSubmitting: boolean;\n errors: FieldErrors;\n}\n\n// ---------------------------------------------------------------------------\n// Hook\n// ---------------------------------------------------------------------------\n\n/**\n * Per-page hook for multi-step forms powered by WaypointRunner.\n *\n * Derives the current step from the URL, provides react-hook-form wired to\n * the step's Zod schema, and handles navigation automatically.\n *\n * Must be used inside a `<WaypointRunner>` component.\n *\n * @example\n * const { form, fields, handleSubmit, progress } = useWaypointStep();\n */\nexport function useWaypointStep(): WaypointStepReturn {\n const { schema, store, onComplete, onStepComplete, onDataChange, externalEnums } =\n useWaypointRuntimeContext();\n const router = useRouter();\n const pathname = usePathname();\n\n // Subscribe to store state\n const { data, externalVars, currentStepId, isSubmitting } = useStore(\n store,\n (s: WaypointRuntimeStore) => ({\n data: s.data,\n externalVars: s.externalVars,\n currentStepId: s.currentStepId,\n isSubmitting: s.isSubmitting,\n })\n );\n\n // Resolve the full tree\n const tree = useMemo(\n () => resolveTree(schema, data, externalVars, externalEnums),\n [schema, data, externalVars, externalEnums]\n );\n\n // Find the step matching the current pathname\n const currentStep = useMemo(() => {\n return tree.steps.find((s) => {\n const stepUrl = s.definition.url;\n // Exact match, or pathname ends with step URL (handles leading slash variants)\n return pathname === stepUrl || pathname.endsWith(stepUrl);\n });\n }, [tree.steps, pathname]);\n\n // Sync currentStepId into the store whenever the page changes\n useEffect(() => {\n if (currentStep && currentStep.definition.id !== currentStepId) {\n store.getState().setCurrentStep(currentStep.definition.id);\n }\n }, [currentStep, currentStepId, store]);\n\n // Visible fields for the current step\n const visibleFields = useMemo(\n () => currentStep?.fields.filter((f) => f.visible) ?? [],\n [currentStep]\n );\n\n // Build the Zod schema from visible fields\n const zodSchema = useMemo(() => buildZodSchema(visibleFields), [visibleFields]);\n\n // Existing step data used as default values\n const defaultValues = useMemo(\n () => (currentStep ? (data[currentStep.definition.id] ?? {}) : {}),\n // Only recompute when the step changes (not on every data write)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [currentStep?.definition.id]\n );\n\n // React Hook Form instance\n const form = useForm<FieldValues>({\n resolver: zodResolver(zodSchema),\n defaultValues,\n });\n\n // Reset form defaults when step changes\n useEffect(() => {\n if (currentStep) {\n form.reset(data[currentStep.definition.id] ?? {});\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [currentStep?.definition.id]);\n\n // Navigation position\n const stepIndex = currentStep\n ? findStepIndex(tree.steps, currentStep.definition.id)\n : -1;\n const isFirstStep = stepIndex === 0;\n const isLastStep = stepIndex === tree.steps.length - 1;\n const progress = currentStep\n ? calculateProgress(tree.steps, currentStep.definition.id)\n : 0;\n\n // goBack\n const goBack = useCallback(() => {\n if (!currentStep) return;\n const prev = getPreviousStep(tree.steps, currentStep.definition.id);\n if (prev) {\n router.push(prev.definition.url);\n }\n }, [currentStep, tree.steps, router]);\n\n // handleSubmit\n const handleSubmit = useCallback(async () => {\n if (!currentStep) return;\n\n // 1. Validate via RHF + Zod\n const isValid = await form.trigger();\n if (!isValid) return;\n\n const values = form.getValues();\n\n // 2. Persist data + update submitting state\n store.getState().setIsSubmitting(true);\n\n try {\n // 3. Snapshot visible step IDs before writing (to detect tree changes)\n const oldVisibleIds = tree.steps.map((s) => s.definition.id).join(\",\");\n\n // 4. Write validated data into the store\n store.getState().setStepData(currentStep.definition.id, values);\n\n // 5. Re-resolve tree with updated data — step visibility may have changed\n const allData = store.getState().data;\n const updatedTree = resolveTree(schema, allData, externalVars, externalEnums);\n const newVisibleIds = updatedTree.steps.map((s) => s.definition.id).join(\",\");\n\n // 6. If the visible tree changed, truncate stale forward history\n // (e.g. user went back, changed a dep value, tree changed → old path is invalid)\n if (oldVisibleIds !== newVisibleIds) {\n store.getState().truncateHistoryAt(currentStep.definition.id);\n }\n\n // 7. onDataChange callback\n onDataChange?.(allData);\n\n // 8. onStepComplete callback (may be async, e.g. backend-step mode)\n if (onStepComplete) {\n await onStepComplete(currentStep.definition.id, values);\n }\n\n // 9. Navigate using the UPDATED tree so we follow the new step order\n const nextStep = getNextStep(updatedTree.steps, currentStep.definition.id);\n if (nextStep) {\n router.push(nextStep.definition.url);\n } else {\n store.getState().setCompleted(true);\n await onComplete?.(allData);\n }\n } finally {\n store.getState().setIsSubmitting(false);\n }\n }, [\n currentStep,\n form,\n store,\n schema,\n tree.steps,\n externalVars,\n externalEnums,\n onDataChange,\n onStepComplete,\n onComplete,\n router,\n ]);\n\n return {\n currentStep,\n progress,\n isFirstStep,\n isLastStep,\n form,\n fields: visibleFields,\n handleSubmit,\n goBack,\n isSubmitting,\n errors: form.formState.errors,\n };\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { WaypointSchema, ResolvedStep, ResolvedField, WaypointRuntimeStore } from '@waypointjs/core';
2
+ import { WaypointSchema, CustomTypeDefinition, ExternalEnum, ResolvedStep, ResolvedField, WaypointRuntimeStore } from '@waypointjs/core';
3
3
  import { UseFormReturn, FieldValues, FieldErrors } from 'react-hook-form';
4
+ import * as react from 'react';
4
5
  import { StoreApi } from 'zustand';
5
6
 
6
7
  interface WaypointRunnerProps {
@@ -15,6 +16,10 @@ interface WaypointRunnerProps {
15
16
  onStepComplete?: (stepId: string, data: Record<string, unknown>) => void | Promise<void>;
16
17
  /** Called whenever any field value changes */
17
18
  onDataChange?: (data: Record<string, Record<string, unknown>>) => void;
19
+ /** App-provided custom field types — exposed via context for custom field rendering */
20
+ customFieldTypes?: CustomTypeDefinition[];
21
+ /** App-provided external enum lists — resolved into ResolvedField.resolvedOptions */
22
+ externalEnums?: ExternalEnum[];
18
23
  children: React.ReactNode;
19
24
  }
20
25
  /**
@@ -45,7 +50,7 @@ interface WaypointRunnerProps {
45
50
  * {children}
46
51
  * </WaypointRunner>
47
52
  */
48
- declare function WaypointRunner({ schema, externalVars, defaultValues, fetchData, onComplete, onStepComplete, onDataChange, children, }: WaypointRunnerProps): react_jsx_runtime.JSX.Element;
53
+ declare function WaypointRunner({ schema, externalVars, defaultValues, fetchData, onComplete, onStepComplete, onDataChange, customFieldTypes, externalEnums, children, }: WaypointRunnerProps): react_jsx_runtime.JSX.Element;
49
54
 
50
55
  interface WaypointStepReturn {
51
56
  currentStep: ResolvedStep | undefined;
@@ -80,11 +85,16 @@ interface WaypointRuntimeContextValue {
80
85
  onComplete?: (data: Record<string, Record<string, unknown>>) => void | Promise<void>;
81
86
  onStepComplete?: (stepId: string, data: Record<string, unknown>) => void | Promise<void>;
82
87
  onDataChange?: (data: Record<string, Record<string, unknown>>) => void;
88
+ /** App-provided custom field types — available for rendering custom fields */
89
+ customFieldTypes?: CustomTypeDefinition[];
90
+ /** App-provided external enum lists — resolved into ResolvedField.resolvedOptions by the tree resolver */
91
+ externalEnums?: ExternalEnum[];
83
92
  }
93
+ declare const WaypointRuntimeContext: react.Context<WaypointRuntimeContextValue | null>;
84
94
  /**
85
95
  * Returns the current WaypointRunner context.
86
96
  * Must be used inside a `<WaypointRunner>` component.
87
97
  */
88
98
  declare function useWaypointRuntimeContext(): WaypointRuntimeContextValue;
89
99
 
90
- export { WaypointRunner, type WaypointRunnerProps, type WaypointRuntimeContextValue, type WaypointStepReturn, useWaypointRuntimeContext, useWaypointStep };
100
+ export { WaypointRunner, type WaypointRunnerProps, WaypointRuntimeContext, type WaypointRuntimeContextValue, type WaypointStepReturn, useWaypointRuntimeContext, useWaypointStep };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { WaypointSchema, ResolvedStep, ResolvedField, WaypointRuntimeStore } from '@waypointjs/core';
2
+ import { WaypointSchema, CustomTypeDefinition, ExternalEnum, ResolvedStep, ResolvedField, WaypointRuntimeStore } from '@waypointjs/core';
3
3
  import { UseFormReturn, FieldValues, FieldErrors } from 'react-hook-form';
4
+ import * as react from 'react';
4
5
  import { StoreApi } from 'zustand';
5
6
 
6
7
  interface WaypointRunnerProps {
@@ -15,6 +16,10 @@ interface WaypointRunnerProps {
15
16
  onStepComplete?: (stepId: string, data: Record<string, unknown>) => void | Promise<void>;
16
17
  /** Called whenever any field value changes */
17
18
  onDataChange?: (data: Record<string, Record<string, unknown>>) => void;
19
+ /** App-provided custom field types — exposed via context for custom field rendering */
20
+ customFieldTypes?: CustomTypeDefinition[];
21
+ /** App-provided external enum lists — resolved into ResolvedField.resolvedOptions */
22
+ externalEnums?: ExternalEnum[];
18
23
  children: React.ReactNode;
19
24
  }
20
25
  /**
@@ -45,7 +50,7 @@ interface WaypointRunnerProps {
45
50
  * {children}
46
51
  * </WaypointRunner>
47
52
  */
48
- declare function WaypointRunner({ schema, externalVars, defaultValues, fetchData, onComplete, onStepComplete, onDataChange, children, }: WaypointRunnerProps): react_jsx_runtime.JSX.Element;
53
+ declare function WaypointRunner({ schema, externalVars, defaultValues, fetchData, onComplete, onStepComplete, onDataChange, customFieldTypes, externalEnums, children, }: WaypointRunnerProps): react_jsx_runtime.JSX.Element;
49
54
 
50
55
  interface WaypointStepReturn {
51
56
  currentStep: ResolvedStep | undefined;
@@ -80,11 +85,16 @@ interface WaypointRuntimeContextValue {
80
85
  onComplete?: (data: Record<string, Record<string, unknown>>) => void | Promise<void>;
81
86
  onStepComplete?: (stepId: string, data: Record<string, unknown>) => void | Promise<void>;
82
87
  onDataChange?: (data: Record<string, Record<string, unknown>>) => void;
88
+ /** App-provided custom field types — available for rendering custom fields */
89
+ customFieldTypes?: CustomTypeDefinition[];
90
+ /** App-provided external enum lists — resolved into ResolvedField.resolvedOptions by the tree resolver */
91
+ externalEnums?: ExternalEnum[];
83
92
  }
93
+ declare const WaypointRuntimeContext: react.Context<WaypointRuntimeContextValue | null>;
84
94
  /**
85
95
  * Returns the current WaypointRunner context.
86
96
  * Must be used inside a `<WaypointRunner>` component.
87
97
  */
88
98
  declare function useWaypointRuntimeContext(): WaypointRuntimeContextValue;
89
99
 
90
- export { WaypointRunner, type WaypointRunnerProps, type WaypointRuntimeContextValue, type WaypointStepReturn, useWaypointRuntimeContext, useWaypointStep };
100
+ export { WaypointRunner, type WaypointRunnerProps, WaypointRuntimeContext, type WaypointRuntimeContextValue, type WaypointStepReturn, useWaypointRuntimeContext, useWaypointStep };
package/dist/index.js CHANGED
@@ -24,6 +24,8 @@ function WaypointRunner({
24
24
  onComplete,
25
25
  onStepComplete,
26
26
  onDataChange,
27
+ customFieldTypes,
28
+ externalEnums,
27
29
  children
28
30
  }) {
29
31
  const router = useRouter();
@@ -47,7 +49,7 @@ function WaypointRunner({
47
49
  store.getState().resume(schema, externalVars);
48
50
  const state2 = store.getState();
49
51
  if (state2.currentStepId) {
50
- const tree2 = resolveTree(schema, state2.data, state2.externalVars);
52
+ const tree2 = resolveTree(schema, state2.data, state2.externalVars, externalEnums);
51
53
  const step = tree2.steps.find(
52
54
  (s) => s.definition.id === state2.currentStepId
53
55
  );
@@ -71,7 +73,7 @@ function WaypointRunner({
71
73
  if (cancelled) return;
72
74
  store.getState().init(schema, { data, externalVars });
73
75
  const state = store.getState();
74
- const tree = resolveTree(schema, state.data, state.externalVars);
76
+ const tree = resolveTree(schema, state.data, state.externalVars, externalEnums);
75
77
  const lastValid = findLastValidStep(tree.steps, state.data, state.externalVars);
76
78
  if (lastValid && lastValid.definition.url !== pathname) {
77
79
  router.push(lastValid.definition.url);
@@ -93,10 +95,12 @@ function WaypointRunner({
93
95
  store,
94
96
  onComplete,
95
97
  onStepComplete,
96
- onDataChange
98
+ onDataChange,
99
+ customFieldTypes,
100
+ externalEnums
97
101
  }),
98
102
  // eslint-disable-next-line react-hooks/exhaustive-deps
99
- [schema, store]
103
+ [schema, store, externalEnums]
100
104
  );
101
105
  if (missingVars.length > 0) {
102
106
  return /* @__PURE__ */ jsxs(
@@ -125,7 +129,7 @@ function WaypointRunner({
125
129
  return /* @__PURE__ */ jsx(WaypointRuntimeContext.Provider, { value: contextValue, children });
126
130
  }
127
131
  function useWaypointStep() {
128
- const { schema, store, onComplete, onStepComplete, onDataChange } = useWaypointRuntimeContext();
132
+ const { schema, store, onComplete, onStepComplete, onDataChange, externalEnums } = useWaypointRuntimeContext();
129
133
  const router = useRouter();
130
134
  const pathname = usePathname();
131
135
  const { data, externalVars, currentStepId, isSubmitting } = useStore(
@@ -138,8 +142,8 @@ function useWaypointStep() {
138
142
  })
139
143
  );
140
144
  const tree = useMemo(
141
- () => resolveTree(schema, data, externalVars),
142
- [schema, data, externalVars]
145
+ () => resolveTree(schema, data, externalVars, externalEnums),
146
+ [schema, data, externalVars, externalEnums]
143
147
  );
144
148
  const currentStep = useMemo(() => {
145
149
  return tree.steps.find((s) => {
@@ -193,7 +197,7 @@ function useWaypointStep() {
193
197
  const oldVisibleIds = tree.steps.map((s) => s.definition.id).join(",");
194
198
  store.getState().setStepData(currentStep.definition.id, values);
195
199
  const allData = store.getState().data;
196
- const updatedTree = resolveTree(schema, allData, externalVars);
200
+ const updatedTree = resolveTree(schema, allData, externalVars, externalEnums);
197
201
  const newVisibleIds = updatedTree.steps.map((s) => s.definition.id).join(",");
198
202
  if (oldVisibleIds !== newVisibleIds) {
199
203
  store.getState().truncateHistoryAt(currentStep.definition.id);
@@ -219,6 +223,7 @@ function useWaypointStep() {
219
223
  schema,
220
224
  tree.steps,
221
225
  externalVars,
226
+ externalEnums,
222
227
  onDataChange,
223
228
  onStepComplete,
224
229
  onComplete,
@@ -238,6 +243,6 @@ function useWaypointStep() {
238
243
  };
239
244
  }
240
245
 
241
- export { WaypointRunner, useWaypointRuntimeContext, useWaypointStep };
246
+ export { WaypointRunner, WaypointRuntimeContext, useWaypointRuntimeContext, useWaypointStep };
242
247
  //# sourceMappingURL=index.js.map
243
248
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/context.ts","../src/WaypointRunner.tsx","../src/useWaypointStep.ts"],"names":["state","tree","useRouter","usePathname","useStore","useMemo","resolveTree","useEffect"],"mappings":"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","file":"index.js","sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { StoreApi } from \"zustand\";\n\nimport type { WaypointSchema } from \"@waypointjs/core\";\nimport type { WaypointRuntimeStore } from \"@waypointjs/core\";\n\n// ---------------------------------------------------------------------------\n// Context value\n// ---------------------------------------------------------------------------\n\nexport interface WaypointRuntimeContextValue {\n schema: WaypointSchema;\n store: StoreApi<WaypointRuntimeStore>;\n onComplete?: (data: Record<string, Record<string, unknown>>) => void | Promise<void>;\n onStepComplete?: (\n stepId: string,\n data: Record<string, unknown>\n ) => void | Promise<void>;\n onDataChange?: (data: Record<string, Record<string, unknown>>) => void;\n}\n\n// ---------------------------------------------------------------------------\n// Context\n// ---------------------------------------------------------------------------\n\nexport const WaypointRuntimeContext =\n createContext<WaypointRuntimeContextValue | null>(null);\n\n/**\n * Returns the current WaypointRunner context.\n * Must be used inside a `<WaypointRunner>` component.\n */\nexport function useWaypointRuntimeContext(): WaypointRuntimeContextValue {\n const ctx = useContext(WaypointRuntimeContext);\n if (!ctx) {\n throw new Error(\n \"useWaypointRuntimeContext must be called inside a <WaypointRunner> component.\"\n );\n }\n return ctx;\n}\n","\"use client\";\n\nimport { useEffect, useMemo, useRef } from \"react\";\nimport { useStore } from \"zustand\";\nimport { useRouter, usePathname } from \"next/navigation\";\n\nimport {\n createRuntimeStore,\n hasPersistedState,\n resolveTree,\n findLastValidStep,\n} from \"@waypointjs/core\";\nimport type { WaypointSchema, WaypointRuntimeStore } from \"@waypointjs/core\";\n\nimport { WaypointRuntimeContext } from \"./context\";\n\n// ---------------------------------------------------------------------------\n// Props\n// ---------------------------------------------------------------------------\n\nexport interface WaypointRunnerProps {\n schema: WaypointSchema;\n externalVars?: Record<string, unknown>;\n defaultValues?: Record<string, Record<string, unknown>>;\n /** Async function to load previously-saved data (for deep-link resume) */\n fetchData?: () => Promise<Record<string, Record<string, unknown>>>;\n /** Called when the user completes the last step */\n onComplete?: (data: Record<string, Record<string, unknown>>) => void | Promise<void>;\n /** Called after each step is validated and submitted */\n onStepComplete?: (\n stepId: string,\n data: Record<string, unknown>\n ) => void | Promise<void>;\n /** Called whenever any field value changes */\n onDataChange?: (data: Record<string, Record<string, unknown>>) => void;\n children: React.ReactNode;\n}\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\n/**\n * Context provider that initialises the runtime store and wires up callbacks.\n *\n * ### Multi-journey support\n * Each `<WaypointRunner>` creates its own **isolated** Zustand store instance,\n * so multiple runners with different schemas can coexist in the same app\n * without any state interference:\n *\n * ```tsx\n * // Both journeys live side-by-side — each has its own store\n * <WaypointRunner schema={projectSchema}>…</WaypointRunner>\n * <WaypointRunner schema={depositSchema}>…</WaypointRunner>\n * ```\n *\n * ### Pause & Resume\n * When `schema.persistenceMode === \"zustand\"`, each journey's state\n * (`data`, `currentStepId`, `history`) is saved to localStorage under a\n * per-schema key (`waypoint-runtime-<schemaId>`).\n *\n * On remount, `WaypointRunner` detects the saved state and calls `resume()`\n * instead of `init()`, so the user lands back exactly where they left off —\n * navigation state and form data intact.\n *\n * @example\n * <WaypointRunner schema={mySchema} onComplete={handleComplete}>\n * {children}\n * </WaypointRunner>\n */\nexport function WaypointRunner({\n schema,\n externalVars = {},\n defaultValues = {},\n fetchData,\n onComplete,\n onStepComplete,\n onDataChange,\n children,\n}: WaypointRunnerProps) {\n const router = useRouter();\n const pathname = usePathname();\n\n // Create store once per runner instance.\n // The persist middleware (when active) hydrates synchronously from localStorage,\n // so by the time the first useEffect fires, persisted state is already in the store.\n const storeRef = useRef<ReturnType<typeof createRuntimeStore> | null>(null);\n if (storeRef.current === null) {\n storeRef.current = createRuntimeStore({\n persistenceMode: schema.persistenceMode,\n schemaId: schema.id,\n });\n }\n const store = storeRef.current;\n\n // Subscribe to missing blocking vars to show error UI reactively\n const missingVars = useStore(store, (s: WaypointRuntimeStore) => {\n if (!s.schema) return [];\n return resolveTree(s.schema, s.data, s.externalVars).missingExternalVars;\n });\n\n // ---------------------------------------------------------------------------\n // Init / Resume on mount\n // ---------------------------------------------------------------------------\n useEffect(() => {\n let cancelled = false;\n\n async function initialize() {\n // ── Resume path ────────────────────────────────────────────────────────\n // When persistenceMode is \"zustand\", the persist middleware has already\n // synchronously hydrated the store from localStorage by the time this\n // effect runs. If the saved schemaId matches, we resume instead of\n // resetting so the user picks up exactly where they left off.\n if (\n schema.persistenceMode === \"zustand\" &&\n hasPersistedState(store, schema.id)\n ) {\n // Keep data + currentStepId + history — just update schema & externalVars\n store.getState().resume(schema, externalVars);\n\n // Navigate to the persisted step\n const state = store.getState();\n if (state.currentStepId) {\n const tree = resolveTree(schema, state.data, state.externalVars);\n const step = tree.steps.find(\n (s) => s.definition.id === state.currentStepId\n );\n if (step && step.definition.url !== pathname) {\n router.push(step.definition.url);\n }\n }\n return;\n }\n\n // ── Fresh-start path ───────────────────────────────────────────────────\n let data = { ...defaultValues };\n\n if (fetchData) {\n try {\n const fetched = await fetchData();\n if (!cancelled) {\n data = { ...data, ...fetched };\n }\n } catch (err) {\n console.error(\"Waypoint: fetchData failed\", err);\n }\n }\n\n if (cancelled) return;\n\n store.getState().init(schema, { data, externalVars });\n\n // Deep-link resume: if the user already has data, redirect to the last\n // valid step instead of forcing them back to step 1.\n const state = store.getState();\n const tree = resolveTree(schema, state.data, state.externalVars);\n const lastValid = findLastValidStep(tree.steps, state.data, state.externalVars);\n\n if (lastValid && lastValid.definition.url !== pathname) {\n router.push(lastValid.definition.url);\n }\n }\n\n initialize();\n\n return () => {\n cancelled = true;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // Sync externalVars into the store when the prop changes after mount\n useEffect(() => {\n for (const [key, value] of Object.entries(externalVars)) {\n store.getState().setExternalVar(key, value);\n }\n }, [externalVars, store]);\n\n const contextValue = useMemo(\n () => ({\n schema,\n store,\n onComplete,\n onStepComplete,\n onDataChange,\n }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [schema, store]\n );\n\n // Show a blocking error if required external variables are missing after init\n if (missingVars.length > 0) {\n return (\n <div\n role=\"alert\"\n style={{\n color: \"#b91c1c\",\n background: \"#fef2f2\",\n border: \"1px solid #fca5a5\",\n borderRadius: 8,\n padding: \"1rem 1.25rem\",\n fontFamily: \"sans-serif\",\n }}\n >\n <strong>Waypoint Runtime Error</strong>\n <p style={{ margin: \"0.5rem 0 0\" }}>\n Missing required external variables:{\" \"}\n <code>{missingVars.join(\", \")}</code>\n </p>\n </div>\n );\n }\n\n return (\n <WaypointRuntimeContext.Provider value={contextValue}>\n {children}\n </WaypointRuntimeContext.Provider>\n );\n}\n","\"use client\";\n\nimport { useCallback, useEffect, useMemo } from \"react\";\nimport { useRouter, usePathname } from \"next/navigation\";\nimport { useStore } from \"zustand\";\nimport { useForm } from \"react-hook-form\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport type { FieldErrors, FieldValues, UseFormReturn } from \"react-hook-form\";\n\nimport {\n resolveTree,\n buildZodSchema,\n calculateProgress,\n getNextStep,\n getPreviousStep,\n findStepIndex,\n} from \"@waypointjs/core\";\nimport type { ResolvedField, ResolvedStep, WaypointRuntimeStore } from \"@waypointjs/core\";\n\nimport { useWaypointRuntimeContext } from \"./context\";\n\n// ---------------------------------------------------------------------------\n// Return type\n// ---------------------------------------------------------------------------\n\nexport interface WaypointStepReturn {\n // Step context\n currentStep: ResolvedStep | undefined;\n progress: number;\n isFirstStep: boolean;\n isLastStep: boolean;\n\n // React Hook Form\n form: UseFormReturn<FieldValues>;\n /** Visible fields for the current step */\n fields: ResolvedField[];\n\n // Actions\n /** Validate → persist → onStepComplete → navigate next (or onComplete on last step) */\n handleSubmit: () => Promise<void>;\n goBack: () => void;\n\n // State\n isSubmitting: boolean;\n errors: FieldErrors;\n}\n\n// ---------------------------------------------------------------------------\n// Hook\n// ---------------------------------------------------------------------------\n\n/**\n * Per-page hook for multi-step forms powered by WaypointRunner.\n *\n * Derives the current step from the URL, provides react-hook-form wired to\n * the step's Zod schema, and handles navigation automatically.\n *\n * Must be used inside a `<WaypointRunner>` component.\n *\n * @example\n * const { form, fields, handleSubmit, progress } = useWaypointStep();\n */\nexport function useWaypointStep(): WaypointStepReturn {\n const { schema, store, onComplete, onStepComplete, onDataChange } =\n useWaypointRuntimeContext();\n const router = useRouter();\n const pathname = usePathname();\n\n // Subscribe to store state\n const { data, externalVars, currentStepId, isSubmitting } = useStore(\n store,\n (s: WaypointRuntimeStore) => ({\n data: s.data,\n externalVars: s.externalVars,\n currentStepId: s.currentStepId,\n isSubmitting: s.isSubmitting,\n })\n );\n\n // Resolve the full tree\n const tree = useMemo(\n () => resolveTree(schema, data, externalVars),\n [schema, data, externalVars]\n );\n\n // Find the step matching the current pathname\n const currentStep = useMemo(() => {\n return tree.steps.find((s) => {\n const stepUrl = s.definition.url;\n // Exact match, or pathname ends with step URL (handles leading slash variants)\n return pathname === stepUrl || pathname.endsWith(stepUrl);\n });\n }, [tree.steps, pathname]);\n\n // Sync currentStepId into the store whenever the page changes\n useEffect(() => {\n if (currentStep && currentStep.definition.id !== currentStepId) {\n store.getState().setCurrentStep(currentStep.definition.id);\n }\n }, [currentStep, currentStepId, store]);\n\n // Visible fields for the current step\n const visibleFields = useMemo(\n () => currentStep?.fields.filter((f) => f.visible) ?? [],\n [currentStep]\n );\n\n // Build the Zod schema from visible fields\n const zodSchema = useMemo(() => buildZodSchema(visibleFields), [visibleFields]);\n\n // Existing step data used as default values\n const defaultValues = useMemo(\n () => (currentStep ? (data[currentStep.definition.id] ?? {}) : {}),\n // Only recompute when the step changes (not on every data write)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [currentStep?.definition.id]\n );\n\n // React Hook Form instance\n const form = useForm<FieldValues>({\n resolver: zodResolver(zodSchema),\n defaultValues,\n });\n\n // Reset form defaults when step changes\n useEffect(() => {\n if (currentStep) {\n form.reset(data[currentStep.definition.id] ?? {});\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [currentStep?.definition.id]);\n\n // Navigation position\n const stepIndex = currentStep\n ? findStepIndex(tree.steps, currentStep.definition.id)\n : -1;\n const isFirstStep = stepIndex === 0;\n const isLastStep = stepIndex === tree.steps.length - 1;\n const progress = currentStep\n ? calculateProgress(tree.steps, currentStep.definition.id)\n : 0;\n\n // goBack\n const goBack = useCallback(() => {\n if (!currentStep) return;\n const prev = getPreviousStep(tree.steps, currentStep.definition.id);\n if (prev) {\n router.push(prev.definition.url);\n }\n }, [currentStep, tree.steps, router]);\n\n // handleSubmit\n const handleSubmit = useCallback(async () => {\n if (!currentStep) return;\n\n // 1. Validate via RHF + Zod\n const isValid = await form.trigger();\n if (!isValid) return;\n\n const values = form.getValues();\n\n // 2. Persist data + update submitting state\n store.getState().setIsSubmitting(true);\n\n try {\n // 3. Snapshot visible step IDs before writing (to detect tree changes)\n const oldVisibleIds = tree.steps.map((s) => s.definition.id).join(\",\");\n\n // 4. Write validated data into the store\n store.getState().setStepData(currentStep.definition.id, values);\n\n // 5. Re-resolve tree with updated data — step visibility may have changed\n const allData = store.getState().data;\n const updatedTree = resolveTree(schema, allData, externalVars);\n const newVisibleIds = updatedTree.steps.map((s) => s.definition.id).join(\",\");\n\n // 6. If the visible tree changed, truncate stale forward history\n // (e.g. user went back, changed a dep value, tree changed → old path is invalid)\n if (oldVisibleIds !== newVisibleIds) {\n store.getState().truncateHistoryAt(currentStep.definition.id);\n }\n\n // 7. onDataChange callback\n onDataChange?.(allData);\n\n // 8. onStepComplete callback (may be async, e.g. backend-step mode)\n if (onStepComplete) {\n await onStepComplete(currentStep.definition.id, values);\n }\n\n // 9. Navigate using the UPDATED tree so we follow the new step order\n const nextStep = getNextStep(updatedTree.steps, currentStep.definition.id);\n if (nextStep) {\n router.push(nextStep.definition.url);\n } else {\n store.getState().setCompleted(true);\n await onComplete?.(allData);\n }\n } finally {\n store.getState().setIsSubmitting(false);\n }\n }, [\n currentStep,\n form,\n store,\n schema,\n tree.steps,\n externalVars,\n onDataChange,\n onStepComplete,\n onComplete,\n router,\n ]);\n\n return {\n currentStep,\n progress,\n isFirstStep,\n isLastStep,\n form,\n fields: visibleFields,\n handleSubmit,\n goBack,\n isSubmitting,\n errors: form.formState.errors,\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/context.ts","../src/WaypointRunner.tsx","../src/useWaypointStep.ts"],"names":["state","tree","useRouter","usePathname","useStore","useMemo","resolveTree","useEffect"],"mappings":"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","file":"index.js","sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { StoreApi } from \"zustand\";\n\nimport type { WaypointSchema, CustomTypeDefinition, ExternalEnum } from \"@waypointjs/core\";\nimport type { WaypointRuntimeStore } from \"@waypointjs/core\";\n\n// ---------------------------------------------------------------------------\n// Context value\n// ---------------------------------------------------------------------------\n\nexport interface WaypointRuntimeContextValue {\n schema: WaypointSchema;\n store: StoreApi<WaypointRuntimeStore>;\n onComplete?: (data: Record<string, Record<string, unknown>>) => void | Promise<void>;\n onStepComplete?: (\n stepId: string,\n data: Record<string, unknown>\n ) => void | Promise<void>;\n onDataChange?: (data: Record<string, Record<string, unknown>>) => void;\n /** App-provided custom field types — available for rendering custom fields */\n customFieldTypes?: CustomTypeDefinition[];\n /** App-provided external enum lists — resolved into ResolvedField.resolvedOptions by the tree resolver */\n externalEnums?: ExternalEnum[];\n}\n\n// ---------------------------------------------------------------------------\n// Context\n// ---------------------------------------------------------------------------\n\nexport const WaypointRuntimeContext =\n createContext<WaypointRuntimeContextValue | null>(null);\n\n/**\n * Returns the current WaypointRunner context.\n * Must be used inside a `<WaypointRunner>` component.\n */\nexport function useWaypointRuntimeContext(): WaypointRuntimeContextValue {\n const ctx = useContext(WaypointRuntimeContext);\n if (!ctx) {\n throw new Error(\n \"useWaypointRuntimeContext must be called inside a <WaypointRunner> component.\"\n );\n }\n return ctx;\n}\n","\"use client\";\n\nimport { useEffect, useMemo, useRef } from \"react\";\nimport { useStore } from \"zustand\";\nimport { useRouter, usePathname } from \"next/navigation\";\n\nimport {\n createRuntimeStore,\n hasPersistedState,\n resolveTree,\n findLastValidStep,\n} from \"@waypointjs/core\";\nimport type { WaypointSchema, WaypointRuntimeStore, CustomTypeDefinition, ExternalEnum } from \"@waypointjs/core\";\n\nimport { WaypointRuntimeContext } from \"./context\";\n\n// ---------------------------------------------------------------------------\n// Props\n// ---------------------------------------------------------------------------\n\nexport interface WaypointRunnerProps {\n schema: WaypointSchema;\n externalVars?: Record<string, unknown>;\n defaultValues?: Record<string, Record<string, unknown>>;\n /** Async function to load previously-saved data (for deep-link resume) */\n fetchData?: () => Promise<Record<string, Record<string, unknown>>>;\n /** Called when the user completes the last step */\n onComplete?: (data: Record<string, Record<string, unknown>>) => void | Promise<void>;\n /** Called after each step is validated and submitted */\n onStepComplete?: (\n stepId: string,\n data: Record<string, unknown>\n ) => void | Promise<void>;\n /** Called whenever any field value changes */\n onDataChange?: (data: Record<string, Record<string, unknown>>) => void;\n /** App-provided custom field types — exposed via context for custom field rendering */\n customFieldTypes?: CustomTypeDefinition[];\n /** App-provided external enum lists — resolved into ResolvedField.resolvedOptions */\n externalEnums?: ExternalEnum[];\n children: React.ReactNode;\n}\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\n/**\n * Context provider that initialises the runtime store and wires up callbacks.\n *\n * ### Multi-journey support\n * Each `<WaypointRunner>` creates its own **isolated** Zustand store instance,\n * so multiple runners with different schemas can coexist in the same app\n * without any state interference:\n *\n * ```tsx\n * // Both journeys live side-by-side — each has its own store\n * <WaypointRunner schema={projectSchema}>…</WaypointRunner>\n * <WaypointRunner schema={depositSchema}>…</WaypointRunner>\n * ```\n *\n * ### Pause & Resume\n * When `schema.persistenceMode === \"zustand\"`, each journey's state\n * (`data`, `currentStepId`, `history`) is saved to localStorage under a\n * per-schema key (`waypoint-runtime-<schemaId>`).\n *\n * On remount, `WaypointRunner` detects the saved state and calls `resume()`\n * instead of `init()`, so the user lands back exactly where they left off —\n * navigation state and form data intact.\n *\n * @example\n * <WaypointRunner schema={mySchema} onComplete={handleComplete}>\n * {children}\n * </WaypointRunner>\n */\nexport function WaypointRunner({\n schema,\n externalVars = {},\n defaultValues = {},\n fetchData,\n onComplete,\n onStepComplete,\n onDataChange,\n customFieldTypes,\n externalEnums,\n children,\n}: WaypointRunnerProps) {\n const router = useRouter();\n const pathname = usePathname();\n\n // Create store once per runner instance.\n // The persist middleware (when active) hydrates synchronously from localStorage,\n // so by the time the first useEffect fires, persisted state is already in the store.\n const storeRef = useRef<ReturnType<typeof createRuntimeStore> | null>(null);\n if (storeRef.current === null) {\n storeRef.current = createRuntimeStore({\n persistenceMode: schema.persistenceMode,\n schemaId: schema.id,\n });\n }\n const store = storeRef.current;\n\n // Subscribe to missing blocking vars to show error UI reactively\n const missingVars = useStore(store, (s: WaypointRuntimeStore) => {\n if (!s.schema) return [];\n return resolveTree(s.schema, s.data, s.externalVars).missingExternalVars;\n });\n\n // ---------------------------------------------------------------------------\n // Init / Resume on mount\n // ---------------------------------------------------------------------------\n useEffect(() => {\n let cancelled = false;\n\n async function initialize() {\n // ── Resume path ────────────────────────────────────────────────────────\n // When persistenceMode is \"zustand\", the persist middleware has already\n // synchronously hydrated the store from localStorage by the time this\n // effect runs. If the saved schemaId matches, we resume instead of\n // resetting so the user picks up exactly where they left off.\n if (\n schema.persistenceMode === \"zustand\" &&\n hasPersistedState(store, schema.id)\n ) {\n // Keep data + currentStepId + history — just update schema & externalVars\n store.getState().resume(schema, externalVars);\n\n // Navigate to the persisted step\n const state = store.getState();\n if (state.currentStepId) {\n const tree = resolveTree(schema, state.data, state.externalVars, externalEnums);\n const step = tree.steps.find(\n (s) => s.definition.id === state.currentStepId\n );\n if (step && step.definition.url !== pathname) {\n router.push(step.definition.url);\n }\n }\n return;\n }\n\n // ── Fresh-start path ───────────────────────────────────────────────────\n let data = { ...defaultValues };\n\n if (fetchData) {\n try {\n const fetched = await fetchData();\n if (!cancelled) {\n data = { ...data, ...fetched };\n }\n } catch (err) {\n console.error(\"Waypoint: fetchData failed\", err);\n }\n }\n\n if (cancelled) return;\n\n store.getState().init(schema, { data, externalVars });\n\n // Deep-link resume: if the user already has data, redirect to the last\n // valid step instead of forcing them back to step 1.\n const state = store.getState();\n const tree = resolveTree(schema, state.data, state.externalVars, externalEnums);\n const lastValid = findLastValidStep(tree.steps, state.data, state.externalVars);\n\n if (lastValid && lastValid.definition.url !== pathname) {\n router.push(lastValid.definition.url);\n }\n }\n\n initialize();\n\n return () => {\n cancelled = true;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // Sync externalVars into the store when the prop changes after mount\n useEffect(() => {\n for (const [key, value] of Object.entries(externalVars)) {\n store.getState().setExternalVar(key, value);\n }\n }, [externalVars, store]);\n\n const contextValue = useMemo(\n () => ({\n schema,\n store,\n onComplete,\n onStepComplete,\n onDataChange,\n customFieldTypes,\n externalEnums,\n }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [schema, store, externalEnums]\n );\n\n // Show a blocking error if required external variables are missing after init\n if (missingVars.length > 0) {\n return (\n <div\n role=\"alert\"\n style={{\n color: \"#b91c1c\",\n background: \"#fef2f2\",\n border: \"1px solid #fca5a5\",\n borderRadius: 8,\n padding: \"1rem 1.25rem\",\n fontFamily: \"sans-serif\",\n }}\n >\n <strong>Waypoint Runtime Error</strong>\n <p style={{ margin: \"0.5rem 0 0\" }}>\n Missing required external variables:{\" \"}\n <code>{missingVars.join(\", \")}</code>\n </p>\n </div>\n );\n }\n\n return (\n <WaypointRuntimeContext.Provider value={contextValue}>\n {children}\n </WaypointRuntimeContext.Provider>\n );\n}\n","\"use client\";\n\nimport { useCallback, useEffect, useMemo } from \"react\";\nimport { useRouter, usePathname } from \"next/navigation\";\nimport { useStore } from \"zustand\";\nimport { useForm } from \"react-hook-form\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport type { FieldErrors, FieldValues, UseFormReturn } from \"react-hook-form\";\n\nimport {\n resolveTree,\n buildZodSchema,\n calculateProgress,\n getNextStep,\n getPreviousStep,\n findStepIndex,\n} from \"@waypointjs/core\";\nimport type { ResolvedField, ResolvedStep, WaypointRuntimeStore } from \"@waypointjs/core\";\n\nimport { useWaypointRuntimeContext } from \"./context\";\n\n// ---------------------------------------------------------------------------\n// Return type\n// ---------------------------------------------------------------------------\n\nexport interface WaypointStepReturn {\n // Step context\n currentStep: ResolvedStep | undefined;\n progress: number;\n isFirstStep: boolean;\n isLastStep: boolean;\n\n // React Hook Form\n form: UseFormReturn<FieldValues>;\n /** Visible fields for the current step */\n fields: ResolvedField[];\n\n // Actions\n /** Validate → persist → onStepComplete → navigate next (or onComplete on last step) */\n handleSubmit: () => Promise<void>;\n goBack: () => void;\n\n // State\n isSubmitting: boolean;\n errors: FieldErrors;\n}\n\n// ---------------------------------------------------------------------------\n// Hook\n// ---------------------------------------------------------------------------\n\n/**\n * Per-page hook for multi-step forms powered by WaypointRunner.\n *\n * Derives the current step from the URL, provides react-hook-form wired to\n * the step's Zod schema, and handles navigation automatically.\n *\n * Must be used inside a `<WaypointRunner>` component.\n *\n * @example\n * const { form, fields, handleSubmit, progress } = useWaypointStep();\n */\nexport function useWaypointStep(): WaypointStepReturn {\n const { schema, store, onComplete, onStepComplete, onDataChange, externalEnums } =\n useWaypointRuntimeContext();\n const router = useRouter();\n const pathname = usePathname();\n\n // Subscribe to store state\n const { data, externalVars, currentStepId, isSubmitting } = useStore(\n store,\n (s: WaypointRuntimeStore) => ({\n data: s.data,\n externalVars: s.externalVars,\n currentStepId: s.currentStepId,\n isSubmitting: s.isSubmitting,\n })\n );\n\n // Resolve the full tree\n const tree = useMemo(\n () => resolveTree(schema, data, externalVars, externalEnums),\n [schema, data, externalVars, externalEnums]\n );\n\n // Find the step matching the current pathname\n const currentStep = useMemo(() => {\n return tree.steps.find((s) => {\n const stepUrl = s.definition.url;\n // Exact match, or pathname ends with step URL (handles leading slash variants)\n return pathname === stepUrl || pathname.endsWith(stepUrl);\n });\n }, [tree.steps, pathname]);\n\n // Sync currentStepId into the store whenever the page changes\n useEffect(() => {\n if (currentStep && currentStep.definition.id !== currentStepId) {\n store.getState().setCurrentStep(currentStep.definition.id);\n }\n }, [currentStep, currentStepId, store]);\n\n // Visible fields for the current step\n const visibleFields = useMemo(\n () => currentStep?.fields.filter((f) => f.visible) ?? [],\n [currentStep]\n );\n\n // Build the Zod schema from visible fields\n const zodSchema = useMemo(() => buildZodSchema(visibleFields), [visibleFields]);\n\n // Existing step data used as default values\n const defaultValues = useMemo(\n () => (currentStep ? (data[currentStep.definition.id] ?? {}) : {}),\n // Only recompute when the step changes (not on every data write)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [currentStep?.definition.id]\n );\n\n // React Hook Form instance\n const form = useForm<FieldValues>({\n resolver: zodResolver(zodSchema),\n defaultValues,\n });\n\n // Reset form defaults when step changes\n useEffect(() => {\n if (currentStep) {\n form.reset(data[currentStep.definition.id] ?? {});\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [currentStep?.definition.id]);\n\n // Navigation position\n const stepIndex = currentStep\n ? findStepIndex(tree.steps, currentStep.definition.id)\n : -1;\n const isFirstStep = stepIndex === 0;\n const isLastStep = stepIndex === tree.steps.length - 1;\n const progress = currentStep\n ? calculateProgress(tree.steps, currentStep.definition.id)\n : 0;\n\n // goBack\n const goBack = useCallback(() => {\n if (!currentStep) return;\n const prev = getPreviousStep(tree.steps, currentStep.definition.id);\n if (prev) {\n router.push(prev.definition.url);\n }\n }, [currentStep, tree.steps, router]);\n\n // handleSubmit\n const handleSubmit = useCallback(async () => {\n if (!currentStep) return;\n\n // 1. Validate via RHF + Zod\n const isValid = await form.trigger();\n if (!isValid) return;\n\n const values = form.getValues();\n\n // 2. Persist data + update submitting state\n store.getState().setIsSubmitting(true);\n\n try {\n // 3. Snapshot visible step IDs before writing (to detect tree changes)\n const oldVisibleIds = tree.steps.map((s) => s.definition.id).join(\",\");\n\n // 4. Write validated data into the store\n store.getState().setStepData(currentStep.definition.id, values);\n\n // 5. Re-resolve tree with updated data — step visibility may have changed\n const allData = store.getState().data;\n const updatedTree = resolveTree(schema, allData, externalVars, externalEnums);\n const newVisibleIds = updatedTree.steps.map((s) => s.definition.id).join(\",\");\n\n // 6. If the visible tree changed, truncate stale forward history\n // (e.g. user went back, changed a dep value, tree changed → old path is invalid)\n if (oldVisibleIds !== newVisibleIds) {\n store.getState().truncateHistoryAt(currentStep.definition.id);\n }\n\n // 7. onDataChange callback\n onDataChange?.(allData);\n\n // 8. onStepComplete callback (may be async, e.g. backend-step mode)\n if (onStepComplete) {\n await onStepComplete(currentStep.definition.id, values);\n }\n\n // 9. Navigate using the UPDATED tree so we follow the new step order\n const nextStep = getNextStep(updatedTree.steps, currentStep.definition.id);\n if (nextStep) {\n router.push(nextStep.definition.url);\n } else {\n store.getState().setCompleted(true);\n await onComplete?.(allData);\n }\n } finally {\n store.getState().setIsSubmitting(false);\n }\n }, [\n currentStep,\n form,\n store,\n schema,\n tree.steps,\n externalVars,\n externalEnums,\n onDataChange,\n onStepComplete,\n onComplete,\n router,\n ]);\n\n return {\n currentStep,\n progress,\n isFirstStep,\n isLastStep,\n form,\n fields: visibleFields,\n handleSubmit,\n goBack,\n isSubmitting,\n errors: form.formState.errors,\n };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@waypointjs/next",
3
- "version": "0.1.2",
3
+ "version": "0.1.5",
4
4
  "description": "Next.js integration for @waypointjs/react",
5
5
  "keywords": [
6
6
  "navigation",
@@ -27,8 +27,8 @@
27
27
  "@hookform/resolvers": "^3.9.1",
28
28
  "react-hook-form": "^7.53.2",
29
29
  "zustand": "^4.5.2",
30
- "@waypointjs/core": "0.1.2",
31
- "@waypointjs/react": "0.1.2"
30
+ "@waypointjs/react": "0.1.5",
31
+ "@waypointjs/core": "0.1.5"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@types/react": "^18.3.3",