next-modal-router 0.1.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/CHANGELOG.md ADDED
@@ -0,0 +1,13 @@
1
+ # Changelog
2
+
3
+ All notable changes are documented here. The project follows semantic versioning.
4
+
5
+ ## 0.1.0 - 2026-08-31
6
+
7
+ - Added the headless overlay navigation provider, link, hooks, and query helpers.
8
+ - Added safe-close provenance and lightweight focus restoration.
9
+ - Added route-segment parsing, interception calculation, and filesystem discovery.
10
+ - Added `init`, `add`, `check`, `doctor`, and `list` CLI commands.
11
+ - Added structured diagnostics, JSON/CI output, dry-run generation, and monorepo targeting.
12
+ - Added the Next.js 16 basic example, documentation, tests, and release metadata.
13
+ - Verified and declared compatibility with Next.js 14.2+, React 18.2+, and Node.js 18.17+.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ali Ranjbar
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,592 @@
1
+ # next-modal-router
2
+
3
+ > URL-native modals, drawers and overlays for Next.js App Router — without the routing headache.
4
+
5
+ ![next-modal-router — URL-native overlays for Next.js App Router](./assets/next-modal-router-header.png)
6
+
7
+ [GitHub Repository](https://github.com/RanjbarAli/next-modal-router) · [npm package](https://www.npmjs.com/package/next-modal-router) *(first release not published yet)*
8
+
9
+ [![npm version](https://img.shields.io/npm/v/next-modal-router?label=npm)](https://www.npmjs.com/package/next-modal-router)
10
+ [![npm downloads](https://img.shields.io/npm/dm/next-modal-router)](https://www.npmjs.com/package/next-modal-router)
11
+ [![CI](https://github.com/RanjbarAli/next-modal-router/actions/workflows/ci.yml/badge.svg)](https://github.com/RanjbarAli/next-modal-router/actions/workflows/ci.yml)
12
+ [![MIT License](https://img.shields.io/badge/license-MIT-111)](./LICENSE)
13
+
14
+ `next-modal-router` supplies the small runtime and the route tooling needed to build overlays with Next.js Parallel Routes and Intercepting Routes. It does not replace the App Router and it does not render a dialog design system.
15
+
16
+ ## Why?
17
+
18
+ The native model is excellent: navigate softly from `/products` to `/products/42`, show the product over the list, retain the real URL, and render a full page when that URL is loaded directly. The awkward part is maintaining infrastructure such as:
19
+
20
+ ```text
21
+ app/@modal/(.)products/[id]/page.tsx
22
+ ```
23
+
24
+ along with `default.tsx`, the full-page counterpart, the correct interception depth, safe closing, and nested browser history. This package turns that work into:
25
+
26
+ ```bash
27
+ npx next-modal-router init --yes
28
+ npx next-modal-router add product \
29
+ --route "/products/[id]" \
30
+ --source "/products" \
31
+ --type modal \
32
+ --fallback "/products"
33
+ ```
34
+
35
+ ## Features
36
+
37
+ - URL-native, refresh-safe routes built on the App Router
38
+ - Safe close behavior with an explicit fallback for direct entries
39
+ - Typed `OverlayLink`, navigation hook, state hook, and query helpers
40
+ - Intercepting-route generation with route-segment-aware calculations
41
+ - Config-driven validation and zero-config filesystem discovery
42
+ - Logical nested depth backed by URL navigation—not a detached SPA stack
43
+ - Headless modal, drawer, sheet, panel, or custom semantics
44
+ - CI-friendly JSON output, deterministic exit codes, dry runs, and `--cwd`
45
+ - Small ESM runtime with no UI dependency
46
+
47
+ ## Installation
48
+
49
+ ```bash
50
+ pnpm add next-modal-router
51
+ ```
52
+
53
+ ```bash
54
+ npm install next-modal-router
55
+ ```
56
+
57
+ Next.js 14.2–16 and React 18.2–19 are peer dependencies.
58
+
59
+ ## Quick start
60
+
61
+ Initialize the config and default slot:
62
+
63
+ ```bash
64
+ npx next-modal-router init --yes
65
+ ```
66
+
67
+ Generate a product overlay:
68
+
69
+ ```bash
70
+ npx next-modal-router add product \
71
+ --route "/products/[id]" \
72
+ --source "/products" \
73
+ --slot modal \
74
+ --type modal \
75
+ --fallback "/products"
76
+ ```
77
+
78
+ The command reports every write and creates a compilable starting point:
79
+
80
+ ```text
81
+ app/
82
+ ├── @modal/
83
+ │ ├── default.tsx
84
+ │ └── (.)products/
85
+ │ └── [id]/
86
+ │ └── page.tsx
87
+ └── products/
88
+ └── [id]/
89
+ └── page.tsx
90
+ ```
91
+
92
+ Wire the slot and provider into the layout that owns `@modal`:
93
+
94
+ ```tsx
95
+ import { Suspense, type ReactNode } from "react"
96
+ import { OverlayRouterProvider } from "next-modal-router"
97
+
98
+ export default function Layout({
99
+ children,
100
+ modal,
101
+ }: {
102
+ children: ReactNode
103
+ modal: ReactNode
104
+ }) {
105
+ return (
106
+ <OverlayRouterProvider>
107
+ {children}
108
+ <Suspense fallback={null}>{modal}</Suspense>
109
+ </OverlayRouterProvider>
110
+ )
111
+ }
112
+ ```
113
+
114
+ Link to the real route:
115
+
116
+ ```tsx
117
+ import { OverlayLink } from "next-modal-router"
118
+
119
+ <OverlayLink href="/products/42" fallback="/products" scroll={false}>
120
+ View product
121
+ </OverlayLink>
122
+ ```
123
+
124
+ Use your preferred dialog UI in the intercepted page and close it safely:
125
+
126
+ ```tsx
127
+ "use client"
128
+
129
+ import { useOverlayRouter } from "next-modal-router"
130
+
131
+ export default function ProductModal() {
132
+ const overlay = useOverlayRouter()
133
+
134
+ return (
135
+ <section role="dialog" aria-modal="true" aria-labelledby="product-title">
136
+ <h2 id="product-title">Product 42</h2>
137
+ <button onClick={() => overlay.close()}>Close</button>
138
+ </section>
139
+ )
140
+ }
141
+ ```
142
+
143
+ A click from `/products` shows an overlay at `/products/42`. Loading or refreshing `/products/42` renders `app/products/[id]/page.tsx` as a normal page.
144
+
145
+ ## URL-native overlays
146
+
147
+ An overlay is always a route. `OverlayLink` delegates navigation to Next.js `Link`; `useOverlayRouter` delegates to `useRouter`; and the generated folders use the official `@slot` and interception conventions. Bookmarks, sharing, reloads, and server rendering therefore retain native behavior.
148
+
149
+ Use an overlay when the destination deserves a URL. For transient confirmations or menus with no meaningful destination, a regular local-state component is a better fit.
150
+
151
+ ## Soft navigation versus direct navigation
152
+
153
+ | Entry | Next.js result | Package state |
154
+ | --- | --- | --- |
155
+ | Click `/products` → `/products/42` | Intercepted slot route | depth `1`, safe back recorded |
156
+ | Paste or refresh `/products/42` | Full-page route | depth `0`, close uses fallback |
157
+ | Back/forward | Native history traversal | App Router decides rendered route |
158
+
159
+ Interception is a Next.js soft-navigation behavior. This package deliberately does not force an intercepted UI on hard navigation.
160
+
161
+ ## Safe close behavior
162
+
163
+ Calling `router.back()` blindly can send a direct visitor back to another website. The provider records only overlay navigation initiated by `OverlayLink` or `overlay.open()` during the current application lifetime.
164
+
165
+ - A recorded current overlay closes with `router.back()`.
166
+ - A direct load, missing marker, or uncertain state closes with `router.replace(fallback)`.
167
+ - If no fallback exists, the conservative default is `/`.
168
+
169
+ ```tsx
170
+ <OverlayLink href="/products/42" fallback="/products">
171
+ View product
172
+ </OverlayLink>
173
+ ```
174
+
175
+ The browser does not expose arbitrary history entries, so this is intentionally a safe heuristic rather than a claim of perfect history knowledge. Configure `closeFallback` and pass `fallback` when the direct-entry close destination matters.
176
+
177
+ ## Modal routing
178
+
179
+ Set `type: "modal"` for documentation and tooling semantics. The intercepted page controls the actual markup:
180
+
181
+ ```ts
182
+ product: {
183
+ route: "/products/[id]",
184
+ source: "/products",
185
+ type: "modal",
186
+ slot: "modal",
187
+ closeFallback: "/products",
188
+ }
189
+ ```
190
+
191
+ The type never injects styles, portals, focus traps, or animation.
192
+
193
+ ## Drawer, sheet, and panel routing
194
+
195
+ Drawers and sheets use exactly the same routing mechanism. A separate slot is useful when two overlay surfaces should be independent:
196
+
197
+ ```ts
198
+ notifications: {
199
+ route: "/notifications",
200
+ source: "/dashboard",
201
+ type: "drawer",
202
+ slot: "panel",
203
+ closeFallback: "/dashboard",
204
+ }
205
+ ```
206
+
207
+ Render the intercepted `@panel/(.)notifications/page.tsx` with your own sheet or drawer component.
208
+
209
+ ## Nested overlays
210
+
211
+ Each call to `overlay.open()` or click on `OverlayLink` records one logical level:
212
+
213
+ ```text
214
+ /products → /products/42 → /products/42/reviews
215
+ depth 0 depth 1 depth 2
216
+ ```
217
+
218
+ ```tsx
219
+ <OverlayLink href={`/products/${id}/reviews`} fallback={`/products/${id}`}>
220
+ Reviews
221
+ </OverlayLink>
222
+ ```
223
+
224
+ `overlay.close()` traverses one native history entry when that level was recorded safely. The actual visual composition still follows Next.js slot rendering: use another parallel slot or render the parent shell in the nested intercepted route when both layers must remain visible.
225
+
226
+ See [Nested overlays](./docs/nested-overlays.md).
227
+
228
+ ## Browser back and forward
229
+
230
+ `overlay.back()` and `overlay.forward()` are direct App Router operations. Browser buttons work normally because the URL and Next.js history remain authoritative. The package does not patch `pushState`, attach a global popstate router, or keep a competing route store.
231
+
232
+ ## Query parameter helpers
233
+
234
+ Update a subset of parameters without discarding the rest:
235
+
236
+ ```tsx
237
+ const overlay = useOverlayRouter()
238
+
239
+ overlay.setSearchParams({ tab: "reviews" })
240
+ // /products/42?tab=reviews
241
+
242
+ overlay.setSearchParams({ tab: "details", sort: "newest" })
243
+ // /products/42?sort=newest&tab=details
244
+
245
+ overlay.setSearchParams({ tab: null })
246
+ // removes tab and preserves sort
247
+ ```
248
+
249
+ The standalone pure helper is useful outside React:
250
+
251
+ ```ts
252
+ import { updateSearchParams } from "next-modal-router"
253
+
254
+ const next = updateSearchParams("tab=details&sort=newest", { tab: "reviews" })
255
+ ```
256
+
257
+ Updates use `router.replace()` and preserve Next.js navigation semantics.
258
+
259
+ ## Scroll behavior
260
+
261
+ `OverlayLink` defaults `scroll` to `false`, which is normally appropriate for preserving the underlying page position. Override it with the standard Next.js prop when the destination should scroll:
262
+
263
+ ```tsx
264
+ <OverlayLink href="/products/42" scroll>
265
+ View product
266
+ </OverlayLink>
267
+ ```
268
+
269
+ The package adds no scroll restoration hacks.
270
+
271
+ ## Focus restoration
272
+
273
+ The provider records the focused trigger for internal overlay navigation and focuses it after close when it remains connected. This lightweight behavior is enabled by default and interoperates with dialog libraries:
274
+
275
+ ```tsx
276
+ <OverlayRouterProvider restoreFocus={false}>
277
+ {children}
278
+ {modal}
279
+ </OverlayRouterProvider>
280
+ ```
281
+
282
+ Disable it when your UI library owns focus restoration. Focus trapping and initial modal focus remain the responsibility of that accessible dialog implementation.
283
+
284
+ ## Route generation
285
+
286
+ `add` models URL segments separately from filesystem segments, ignores route groups for interception depth, validates route inputs against traversal, and emits atomic writes. Existing full-page routes and slot defaults are preserved; conflicting intercepted pages require `--force`.
287
+
288
+ ```bash
289
+ npx next-modal-router add reviews \
290
+ --route "/products/[id]/reviews" \
291
+ --source "/products/[id]" \
292
+ --type drawer \
293
+ --slot modal \
294
+ --fallback "/products/[id]" \
295
+ --dry-run
296
+ ```
297
+
298
+ ## Route validation
299
+
300
+ `check` verifies slot defaults, configured interceptors, full-page counterparts, and fallback pages. Diagnostics use stable codes:
301
+
302
+ | Code | Meaning |
303
+ | --- | --- |
304
+ | `NMR001` | Missing slot `default` route |
305
+ | `NMR002` | Missing configured interceptor |
306
+ | `NMR003` | Incorrect interception path |
307
+ | `NMR004` | Missing full-page counterpart |
308
+ | `NMR005` | Missing close fallback page |
309
+ | `NMR006` | Discovered overlay absent from config |
310
+
311
+ ## Zero-config discovery
312
+
313
+ `check` and `list` still scan existing `@slot` directories when no config exists. For example, `app/@modal/(.)photos/[id]/page.tsx` appears as a discovered overlay. Add a config when you want source, fallback, and expected-target validation.
314
+
315
+ ## Config-driven workflows
316
+
317
+ Configuration is the explicit contract used by generation and CI. It does not ship to the browser and is not required by the runtime.
318
+
319
+ ```ts
320
+ import { defineConfig } from "next-modal-router/config"
321
+
322
+ export default defineConfig({
323
+ defaultSlot: "modal",
324
+ overlays: {
325
+ product: {
326
+ route: "/products/[id]",
327
+ source: "/products",
328
+ type: "modal",
329
+ slot: "modal",
330
+ closeFallback: "/products",
331
+ },
332
+ },
333
+ })
334
+ ```
335
+
336
+ See [Configuration](./docs/configuration.md).
337
+
338
+ ## CLI usage
339
+
340
+ ### `init`
341
+
342
+ Detects a Next.js App Router project and TypeScript, then creates the config and default slot fallback.
343
+
344
+ ```bash
345
+ next-modal-router init [--slot modal] [--yes] [--dry-run] [--force]
346
+ ```
347
+
348
+ ```text
349
+ CREATE next-modal-router.config.ts
350
+ CREATE app/@modal/default.tsx
351
+
352
+ Initialized next-modal-router in /project.
353
+ ```
354
+
355
+ Exit `0` means success; misuse or a protected-file conflict exits `2`.
356
+
357
+ ### `add`
358
+
359
+ Interactively prompts for missing values on a terminal, or accepts a complete scriptable definition:
360
+
361
+ ```bash
362
+ next-modal-router add product --route "/products/[id]" --source "/products" --type modal --slot modal --fallback "/products"
363
+ ```
364
+
365
+ It generates the slot default if missing, intercepted page, full page if missing, and synchronized config. Exit `0` means every planned write completed.
366
+
367
+ ### `check`
368
+
369
+ ```bash
370
+ next-modal-router check --verbose
371
+ ```
372
+
373
+ ```text
374
+ next-modal-router
375
+
376
+ Checking 1 overlay...
377
+
378
+ ✓ Everything looks good.
379
+ ```
380
+
381
+ Validation errors exit `1`; invocation/configuration failures exit `2`.
382
+
383
+ ### `doctor`
384
+
385
+ Reports Node, Next.js, React, App Router, TypeScript, config, and validation health:
386
+
387
+ ```bash
388
+ next-modal-router doctor --cwd apps/web
389
+ ```
390
+
391
+ Use this first when the generator cannot locate the intended application.
392
+
393
+ ### `list`
394
+
395
+ Lists both configured and discovered routes:
396
+
397
+ ```bash
398
+ next-modal-router list
399
+ ```
400
+
401
+ ```text
402
+ NAME ROUTE TYPE SLOT ORIGIN
403
+ product /products/[id] modal modal config
404
+ ```
405
+
406
+ ### Global options
407
+
408
+ | Option | Commands | Effect |
409
+ | --- | --- | --- |
410
+ | `--cwd <path>` | all | Run against an app elsewhere in a monorepo |
411
+ | `--format json` | check, doctor, list | Emit JSON without ANSI text |
412
+ | `--dry-run` | init, add | Print writes without changing files |
413
+ | `--force` | init, add | Replace files owned by the requested operation |
414
+ | `--ci` | all | Disable prompting and keep deterministic output |
415
+ | `--verbose` | check | Include diagnostic filesystem paths |
416
+ | `--yes` | init, add | Accept non-interactive defaults |
417
+
418
+ ## CI usage
419
+
420
+ ```bash
421
+ next-modal-router check --ci --format json > overlay-report.json
422
+ ```
423
+
424
+ Exit codes are stable: `0` success, `1` route validation errors, `2` CLI/configuration misuse. Warnings do not fail `check`.
425
+
426
+ ## Monorepo support
427
+
428
+ Run the CLI from the actual application directory or target it explicitly:
429
+
430
+ ```bash
431
+ next-modal-router doctor --cwd ./apps/storefront
432
+ next-modal-router check --cwd ./apps/storefront --ci
433
+ ```
434
+
435
+ Discovery stops at the located package containing `app/` or `src/app/`; it does not scan unrelated workspaces.
436
+
437
+ ## UI-library interoperability
438
+
439
+ The route owns visibility; the UI library owns accessibility and presentation.
440
+
441
+ ### shadcn/ui / Radix Dialog
442
+
443
+ ```tsx
444
+ "use client"
445
+
446
+ import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog"
447
+ import { useOverlayRouter } from "next-modal-router"
448
+
449
+ export default function ProductOverlay() {
450
+ const overlay = useOverlayRouter()
451
+ return (
452
+ <Dialog open onOpenChange={(open) => !open && overlay.close()}>
453
+ <DialogContent>
454
+ <DialogTitle>Product</DialogTitle>
455
+ </DialogContent>
456
+ </Dialog>
457
+ )
458
+ }
459
+ ```
460
+
461
+ For Radix directly, use `Dialog.Root open` and the same `onOpenChange` rule. Disable provider focus restoration if Radix should restore focus itself.
462
+
463
+ ### Headless UI and sheets
464
+
465
+ Use `Dialog open` with `onClose={overlay.close}` in Headless UI. Sheet libraries built on Radix use the same controlled-open pattern. Do not toggle local `open` state after close; navigation unmounts the intercepted route.
466
+
467
+ More examples: [UI libraries](./docs/ui-libraries.md).
468
+
469
+ ## Configuration reference
470
+
471
+ `defineConfig(config)` returns the same object while preserving literal overlay names and values.
472
+
473
+ | Property | Type | Required | Default | Purpose |
474
+ | --- | --- | --- | --- | --- |
475
+ | `defaultSlot` | `string` | no | `"modal"` in CLI | Slot used when an overlay omits `slot` |
476
+ | `overlays` | `Record<string, OverlayDefinition>` | yes | — | Named overlay definitions |
477
+ | `route` | absolute route string | yes | — | Shareable destination URL pattern |
478
+ | `source` | absolute route string | yes | — | Page from which soft navigation is intended |
479
+ | `type` | `modal \| drawer \| sheet \| panel \| custom` | yes | — | Semantic tooling label only |
480
+ | `slot` | `string` | no | `defaultSlot` | Parallel route slot, without `@` |
481
+ | `closeFallback` | absolute route string | yes | — | Safe close destination on direct/uncertain entry |
482
+
483
+ Route values use URL syntax, not filesystem traversal. Route groups can appear and do not contribute a URL segment. JavaScript, MJS, MTS, and TypeScript configs are loadable; TypeScript is generated by default.
484
+
485
+ ## TypeScript API
486
+
487
+ ### `OverlayRouterProvider`
488
+
489
+ ```ts
490
+ function OverlayRouterProvider(props: {
491
+ children: ReactNode
492
+ defaultFallback?: string
493
+ restoreFocus?: boolean
494
+ }): JSX.Element
495
+ ```
496
+
497
+ Provides minimal in-memory navigation provenance. Place it around the layout children and relevant parallel slots.
498
+
499
+ ### `OverlayLink`
500
+
501
+ Accepts Next.js `LinkProps`, anchor attributes, and `fallback?: string`. It defaults `scroll` to `false`, forwards its ref, respects modified clicks, and records only navigations that Next.js will handle.
502
+
503
+ ### `useOverlayRouter`
504
+
505
+ Returns:
506
+
507
+ ```ts
508
+ interface OverlayRouter {
509
+ open(href: string, options?: { fallback?: string; scroll?: boolean }): void
510
+ replace(href: string, options?: { fallback?: string; scroll?: boolean }): void
511
+ close(fallback?: string): void
512
+ back(): void
513
+ forward(): void
514
+ refresh(): void
515
+ setSearchParams(updates: SearchParamUpdates, options?: { scroll?: boolean }): void
516
+ isOpen: boolean
517
+ isOverlayNavigation: boolean
518
+ pathname: string
519
+ searchParams: ReadonlyURLSearchParams
520
+ previousPathname?: string
521
+ depth: number
522
+ fallback?: string
523
+ canGoBackSafely: boolean
524
+ }
525
+ ```
526
+
527
+ ### `useOverlayState`
528
+
529
+ Returns the read-only navigation portion without router methods. Use it for diagnostics, breadcrumbs, or choosing nested UI presentation.
530
+
531
+ ### `updateSearchParams` and `withSearchParams`
532
+
533
+ Pure utilities accepting string, number, boolean, null, undefined, or arrays. `null`/`undefined` remove a key; all unrelated keys remain.
534
+
535
+ ### Public types
536
+
537
+ The root exports `OverlayConfig`, `OverlayDefinition`, `OverlayType`, `OverlayNavigationOptions`, `OverlayRouter`, `OverlayState`, `OverlayLinkProps`, `OverlayRouterProviderProps`, `SearchParamUpdates`, `SearchParamValue`, `RouteSegment`, `RouteSegmentKind`, `ValidationIssue`, and `ValidationResult`. Config helpers live at `next-modal-router/config`.
538
+
539
+ ## How it works
540
+
541
+ Parallel Routes let a layout render named slots beside `children`. Intercepting Routes tell Next.js that a soft navigation should render a target inside a slot. On a hard request the ordinary target page wins. The CLI creates and checks this filesystem contract; the runtime adds only navigation provenance, conservative closing, and query convenience.
542
+
543
+ No custom router, global history patch, local storage, or route manifest is required at runtime.
544
+
545
+ ## Troubleshooting
546
+
547
+ - **Modal remains visible after navigation:** ensure every slot has `default.tsx` returning `null`, and use navigation rather than only changing local dialog state.
548
+ - **Overlay appears on refresh:** confirm a full `app/products/[id]/page.tsx` exists outside `@modal`; an always-open dialog in a shared layout is not an intercepted route.
549
+ - **Overlay does not appear on click:** use `Link`, `OverlayLink`, or `router.push`; typing a URL is intentionally a hard navigation.
550
+ - **Back leaves the website:** use `overlay.close()` with a fallback, not unconditional `router.back()`.
551
+ - **Parallel slot produces 404:** pass the slot prop through the layout and add its `default.tsx`.
552
+ - **Incorrect intercept depth:** run `next-modal-router check --verbose`; matchers count route segments, not `@slots` or route groups.
553
+
554
+ See [Troubleshooting](./docs/troubleshooting.md) for detailed diagnosis.
555
+
556
+ ## Limitations
557
+
558
+ - Native App Router restrictions and interception behavior still apply.
559
+ - Browser history cannot be inspected arbitrarily; safe close relies on navigation recorded during the current provider lifetime plus an explicit fallback.
560
+ - Opening overlays through a raw custom `router.push` call cannot be recorded; use `overlay.open` or `OverlayLink`.
561
+ - The generator places configured slots at the app root. Existing nested slots are discovered and can be maintained manually.
562
+ - Rewriting an existing config during `add` normalizes it; comments and custom computed configuration are not preserved. Use `--dry-run` and maintain unusual configs manually.
563
+ - Visual accessibility beyond lightweight focus restoration belongs to the selected dialog library.
564
+
565
+ Full details: [Limitations](./docs/limitations.md).
566
+
567
+ ## Compatibility
568
+
569
+ | Dependency | Policy | Repository verification |
570
+ | --- | --- | --- |
571
+ | Node.js | `>=18.17` | compatibility CI: Node 18; primary CI: Node 20 and 22 |
572
+ | Next.js | `>=14.2 <17` | compatibility CI: 14.2 and 15.5; example: 16.3.3 |
573
+ | React / React DOM | `>=18.2 <20` | compatibility CI: React 18; example: React 19.2.8 |
574
+ | TypeScript | modern strict projects | development on 5.9 |
575
+
576
+ ## Migration and versioning
577
+
578
+ The project follows semantic versioning. Before `1.0`, minor versions may intentionally refine generated structure or public APIs and will document changes in [CHANGELOG.md](./CHANGELOG.md). Generated files remain application-owned; upgrading never rewrites them automatically.
579
+
580
+ ## Contributing
581
+
582
+ See [CONTRIBUTING.md](./CONTRIBUTING.md) for setup, architecture, test expectations, and pull-request guidance. Please follow the [Code of Conduct](./CODE_OF_CONDUCT.md). Security reports belong in GitHub private vulnerability reporting as described in [SECURITY.md](./SECURITY.md).
583
+
584
+ ## License
585
+
586
+ MIT © Ali Ranjbar. See [LICENSE](./LICENSE).
587
+
588
+ ## Project links
589
+
590
+ - GitHub: https://github.com/RanjbarAli/next-modal-router
591
+ - Issues: https://github.com/RanjbarAli/next-modal-router/issues
592
+ - npm: https://www.npmjs.com/package/next-modal-router *(available after the first publication)*
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ declare function run(argv?: string[]): Promise<number>;
2
+
3
+ export { run };