ngx-safe-routes 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +125 -0
- package/dist/lib/build-path.d.ts +16 -0
- package/dist/lib/build-path.d.ts.map +1 -0
- package/dist/lib/build-path.js +18 -0
- package/dist/lib/build-path.js.map +1 -0
- package/dist/lib/define-routes.d.ts +26 -0
- package/dist/lib/define-routes.d.ts.map +1 -0
- package/dist/lib/define-routes.js +41 -0
- package/dist/lib/define-routes.js.map +1 -0
- package/dist/lib/types.d.ts +77 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/types.js +11 -0
- package/dist/lib/types.js.map +1 -0
- package/dist/public-api.d.ts +4 -0
- package/dist/public-api.d.ts.map +1 -0
- package/dist/public-api.js +4 -0
- package/dist/public-api.js.map +1 -0
- package/package.json +50 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sepehr Aghdasi
|
|
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,125 @@
|
|
|
1
|
+
# ngx-safe-routes
|
|
2
|
+
|
|
3
|
+
Compile-time-safe route paths for Angular. Define your routes once as a
|
|
4
|
+
plain nested object; every segment and absolute path is checked by the
|
|
5
|
+
TypeScript compiler afterward — so a typo'd or renamed route fails `tsc`,
|
|
6
|
+
not a click in the browser.
|
|
7
|
+
|
|
8
|
+
Angular's `Routes` array and `router.navigate()` both take plain strings,
|
|
9
|
+
so there's nothing stopping `path: 'bank-lsit'` or a stale
|
|
10
|
+
`router.navigate(['/old-path'])` from compiling. This package doesn't
|
|
11
|
+
change that at the Angular API boundary — it gives you a single typed
|
|
12
|
+
source of truth to generate those strings from, so mistakes show up where
|
|
13
|
+
you write the route definition, not at runtime.
|
|
14
|
+
|
|
15
|
+
> Requires TypeScript >= 5.0 (uses `const` type parameters to infer
|
|
16
|
+
> literal path types without needing `as const` everywhere).
|
|
17
|
+
> No runtime dependency on `@angular/core` or `@angular/router` — it's
|
|
18
|
+
> plain TypeScript, so it works with any Angular build system (CLI,
|
|
19
|
+
> esbuild, custom webpack) without extra configuration.
|
|
20
|
+
|
|
21
|
+
## Basic usage
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { defineRoutes } from 'ngx-safe-routes';
|
|
25
|
+
|
|
26
|
+
export const AccountingRoutes = defineRoutes({
|
|
27
|
+
dashboard: 'dashboard',
|
|
28
|
+
accessDenied: 'access-denied',
|
|
29
|
+
settings: 'settings',
|
|
30
|
+
users: {
|
|
31
|
+
path: 'users',
|
|
32
|
+
children: {
|
|
33
|
+
roleList: 'role-list',
|
|
34
|
+
userList: 'user-list',
|
|
35
|
+
detail: 'detail/:id',
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Every node exposes:
|
|
42
|
+
|
|
43
|
+
| Property | Example | Use it for |
|
|
44
|
+
| ----------- | --------------------------------------------------- | ------------------------------------ |
|
|
45
|
+
| `.segment` | `AccountingRoutes.users.children.roleList.segment` → `'role-list'` | `Routes[].path` in routing modules |
|
|
46
|
+
| `.fullPath` | `AccountingRoutes.users.children.roleList.fullPath` → `'/users/role-list'` | `router.navigate([...])`, `routerLink` |
|
|
47
|
+
| `.children` | `AccountingRoutes.users.children` | Nested routes |
|
|
48
|
+
|
|
49
|
+
No manual `${this.USERS}/${this.ROLE_LIST}` concatenation — `fullPath` is
|
|
50
|
+
derived automatically from nesting, and it's a literal type, not just
|
|
51
|
+
`string`.
|
|
52
|
+
|
|
53
|
+
## Routing module
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
import { AccountingRoutes } from './accounting-routes';
|
|
57
|
+
|
|
58
|
+
const routes: Routes = [
|
|
59
|
+
{
|
|
60
|
+
path: AccountingRoutes.users.children.roleList.segment, // bare segment
|
|
61
|
+
loadChildren: () => import('./role-list/role-list.module').then((m) => m.RoleListModule),
|
|
62
|
+
canActivate: [permissionGuard],
|
|
63
|
+
data: { permission: PermissionList.Users.RoleList.ViewList },
|
|
64
|
+
},
|
|
65
|
+
];
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Navigation
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
this.router.navigate([AccountingRoutes.settings.fullPath], {
|
|
72
|
+
queryParams: { category: this.defaultCategory },
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Parameterized routes
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
import { buildPath } from 'ngx-safe-routes';
|
|
80
|
+
|
|
81
|
+
buildPath(AccountingRoutes.users.children.detail.fullPath, { id: user.id });
|
|
82
|
+
// '/users/detail/42'
|
|
83
|
+
|
|
84
|
+
buildPath(AccountingRoutes.dashboard.fullPath, { id: 1 });
|
|
85
|
+
// Type error: 'dashboard' takes no params — second argument isn't allowed
|
|
86
|
+
|
|
87
|
+
buildPath(AccountingRoutes.users.children.detail.fullPath);
|
|
88
|
+
// Type error: 'detail/:id' requires { id }
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Params are inferred straight from the path string's `:param` segments —
|
|
92
|
+
you never declare a params type separately from the route.
|
|
93
|
+
|
|
94
|
+
## Migrating from a hand-written static class
|
|
95
|
+
|
|
96
|
+
If you currently have:
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
export class AccountingRoutes {
|
|
100
|
+
public static readonly USERS = 'users';
|
|
101
|
+
public static readonly ROLE_LIST = 'role-list';
|
|
102
|
+
public static readonly ROLE_LIST_PATH = `/${this.USERS}/${this.ROLE_LIST}`;
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
the equivalent is:
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
export const AccountingRoutes = defineRoutes({
|
|
110
|
+
users: { path: 'users', children: { roleList: 'role-list' } },
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
with `AccountingRoutes.users.children.roleList.segment` /
|
|
115
|
+
`.fullPath` replacing `ROLE_LIST` / `ROLE_LIST_PATH`. The main call-site
|
|
116
|
+
change is the extra `.children` step for nested routes — in exchange,
|
|
117
|
+
`fullPath` is generated instead of hand-written, and it can't drift out
|
|
118
|
+
of sync with the segments it's built from.
|
|
119
|
+
|
|
120
|
+
## API reference
|
|
121
|
+
|
|
122
|
+
- `defineRoutes(definition)` — builds the frozen route tree.
|
|
123
|
+
- `buildPath(fullPath, params?)` — interpolates `:param` placeholders,
|
|
124
|
+
with `params` type-inferred from the path.
|
|
125
|
+
- Types: `RouteNode`, `RouteTree`, `RouteDefinition`, `RouteParams<Path>`.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { RouteParams } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Interpolates `:param` placeholders in a fullPath. TypeScript infers which
|
|
4
|
+
* params are required straight from the path literal, so `params` is only
|
|
5
|
+
* requested (and only accepts the right keys) when the path actually has
|
|
6
|
+
* placeholders.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* buildPath(Routes.users.children.detail.fullPath, { id: 42 });
|
|
10
|
+
* // '/users/detail/42'
|
|
11
|
+
*
|
|
12
|
+
* buildPath(Routes.dashboard.fullPath);
|
|
13
|
+
* // '/dashboard' — no second argument allowed, none needed
|
|
14
|
+
*/
|
|
15
|
+
export declare function buildPath<Path extends string>(path: Path, ...params: RouteParams<Path> extends Record<string, never> ? [] : [params: RouteParams<Path>]): string;
|
|
16
|
+
//# sourceMappingURL=build-path.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-path.d.ts","sourceRoot":"","sources":["../../src/lib/build-path.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEtC;;;;;;;;;;;;GAYG;AACH,wBAAgB,SAAS,CAAC,IAAI,SAAS,MAAM,EACzC,IAAI,EAAE,IAAI,EACV,GAAG,MAAM,EAAE,WAAW,CAAC,IAAI,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC,GAC9F,MAAM,CAMR"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interpolates `:param` placeholders in a fullPath. TypeScript infers which
|
|
3
|
+
* params are required straight from the path literal, so `params` is only
|
|
4
|
+
* requested (and only accepts the right keys) when the path actually has
|
|
5
|
+
* placeholders.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* buildPath(Routes.users.children.detail.fullPath, { id: 42 });
|
|
9
|
+
* // '/users/detail/42'
|
|
10
|
+
*
|
|
11
|
+
* buildPath(Routes.dashboard.fullPath);
|
|
12
|
+
* // '/dashboard' — no second argument allowed, none needed
|
|
13
|
+
*/
|
|
14
|
+
export function buildPath(path, ...params) {
|
|
15
|
+
const values = (params[0] ?? {});
|
|
16
|
+
return Object.entries(values).reduce((result, [key, value]) => result.replace(`:${key}`, String(value)), path);
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=build-path.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-path.js","sourceRoot":"","sources":["../../src/lib/build-path.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,SAAS,CACrB,IAAU,EACV,GAAG,MAA0F;IAE7F,MAAM,MAAM,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAoC,CAAC;IACpE,OAAO,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,CAChC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,GAAG,EAAE,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,EAClE,IAAc,CACjB,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { RouteDefinitionMap, RouteTree } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Builds a frozen, type-safe route tree from a plain definition object.
|
|
4
|
+
*
|
|
5
|
+
* Each entry is either a bare segment string (leaf) or `{ path, children }`
|
|
6
|
+
* (branch). The absolute `fullPath` of every node is derived automatically —
|
|
7
|
+
* you never write `/${parent}/${child}` by hand.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* const Routes = defineRoutes({
|
|
11
|
+
* dashboard: 'dashboard',
|
|
12
|
+
* users: {
|
|
13
|
+
* path: 'users',
|
|
14
|
+
* children: {
|
|
15
|
+
* roleList: 'role-list',
|
|
16
|
+
* userList: 'user-list',
|
|
17
|
+
* },
|
|
18
|
+
* },
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* Routes.dashboard.segment // 'dashboard'
|
|
22
|
+
* Routes.dashboard.fullPath // '/dashboard'
|
|
23
|
+
* Routes.users.children.roleList.fullPath // '/users/role-list'
|
|
24
|
+
*/
|
|
25
|
+
export declare function defineRoutes<const T extends RouteDefinitionMap>(definition: T, parentPath?: string): RouteTree<T>;
|
|
26
|
+
//# sourceMappingURL=define-routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define-routes.d.ts","sourceRoot":"","sources":["../../src/lib/define-routes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,kBAAkB,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAE/E;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,YAAY,CAAC,KAAK,CAAC,CAAC,SAAS,kBAAkB,EAC3D,UAAU,EAAE,CAAC,EACb,UAAU,GAAE,MAAW,GACxB,SAAS,CAAC,CAAC,CAAC,CAmBd"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds a frozen, type-safe route tree from a plain definition object.
|
|
3
|
+
*
|
|
4
|
+
* Each entry is either a bare segment string (leaf) or `{ path, children }`
|
|
5
|
+
* (branch). The absolute `fullPath` of every node is derived automatically —
|
|
6
|
+
* you never write `/${parent}/${child}` by hand.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* const Routes = defineRoutes({
|
|
10
|
+
* dashboard: 'dashboard',
|
|
11
|
+
* users: {
|
|
12
|
+
* path: 'users',
|
|
13
|
+
* children: {
|
|
14
|
+
* roleList: 'role-list',
|
|
15
|
+
* userList: 'user-list',
|
|
16
|
+
* },
|
|
17
|
+
* },
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* Routes.dashboard.segment // 'dashboard'
|
|
21
|
+
* Routes.dashboard.fullPath // '/dashboard'
|
|
22
|
+
* Routes.users.children.roleList.fullPath // '/users/role-list'
|
|
23
|
+
*/
|
|
24
|
+
export function defineRoutes(definition, parentPath = '') {
|
|
25
|
+
const tree = {};
|
|
26
|
+
for (const key of Object.keys(definition)) {
|
|
27
|
+
const entry = definition[key];
|
|
28
|
+
const isBranch = typeof entry === 'object' && entry !== null;
|
|
29
|
+
const segment = isBranch ? entry.path : entry;
|
|
30
|
+
const fullPath = `${parentPath}/${segment}`;
|
|
31
|
+
const childDefs = isBranch ? entry.children : undefined;
|
|
32
|
+
tree[key] = Object.freeze({
|
|
33
|
+
segment,
|
|
34
|
+
fullPath,
|
|
35
|
+
children: childDefs ? defineRoutes(childDefs, fullPath) : undefined,
|
|
36
|
+
toString: () => fullPath,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
return Object.freeze(tree);
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=define-routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define-routes.js","sourceRoot":"","sources":["../../src/lib/define-routes.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,YAAY,CACxB,UAAa,EACb,aAAqB,EAAE;IAEvB,MAAM,IAAI,GAA4B,EAAE,CAAC;IAEzC,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QACxC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;QAC9B,MAAM,QAAQ,GAAG,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC;QAC7D,MAAM,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAE,KAA+B,CAAC,IAAI,CAAC,CAAC,CAAE,KAAgB,CAAC;QACrF,MAAM,QAAQ,GAAG,GAAG,UAAU,IAAI,OAAO,EAAE,CAAC;QAC5C,MAAM,SAAS,GAAG,QAAQ,CAAC,CAAC,CAAE,KAA+B,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;QAEnF,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC;YACtB,OAAO;YACP,QAAQ;YACR,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS;YACnE,QAAQ,EAAE,GAAG,EAAE,CAAC,QAAQ;SAC3B,CAAC,CAAC;IACP,CAAC;IAED,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,CAAiB,CAAC;AAC/C,CAAC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ============================================================================
|
|
3
|
+
* ROUTE DEFINITIONS — what you write
|
|
4
|
+
* ============================================================================
|
|
5
|
+
* You describe routes as a plain object. Each property is either:
|
|
6
|
+
*
|
|
7
|
+
* - a STRING ("leaf" route) e.g. `dashboard: 'dashboard'`
|
|
8
|
+
* - an OBJECT ("branch" route) e.g. `users: { path: 'users', children: { list: 'list' } }`
|
|
9
|
+
*/
|
|
10
|
+
export type RouteDefinition = string | RouteBranchDefinition;
|
|
11
|
+
export interface RouteBranchDefinition {
|
|
12
|
+
path: string;
|
|
13
|
+
children?: RouteDefinitionMap;
|
|
14
|
+
}
|
|
15
|
+
export type RouteDefinitionMap = Record<string, RouteDefinition>;
|
|
16
|
+
/**
|
|
17
|
+
* ============================================================================
|
|
18
|
+
* PATH JOINING
|
|
19
|
+
* ============================================================================
|
|
20
|
+
* Combines a parent's absolute path with a child's segment.
|
|
21
|
+
*
|
|
22
|
+
* Join<'', 'users'> -> '/users'
|
|
23
|
+
* Join<'/users', 'list'> -> '/users/list'
|
|
24
|
+
*/
|
|
25
|
+
export type Join<Parent extends string, Segment extends string> = Parent extends '' ? `/${Segment}` : `${Parent}/${Segment}`;
|
|
26
|
+
/**
|
|
27
|
+
* ============================================================================
|
|
28
|
+
* ROUTE NODE — what you get back
|
|
29
|
+
* ============================================================================
|
|
30
|
+
* Every route you define turns into one of these after `defineRoutes()` runs.
|
|
31
|
+
*/
|
|
32
|
+
export interface RouteNode<Segment extends string, FullPath extends string, Children> {
|
|
33
|
+
/** The route's own segment, e.g. 'users'. Use for Angular's `Routes[].path`. */
|
|
34
|
+
readonly segment: Segment;
|
|
35
|
+
/** The full absolute path, e.g. '/users/list'. Use for `router.navigate()` / `routerLink`. */
|
|
36
|
+
readonly fullPath: FullPath;
|
|
37
|
+
/** Nested routes, or `undefined` if this route has none. */
|
|
38
|
+
readonly children: Children;
|
|
39
|
+
/** Lets a node be used directly anywhere a plain string path is expected. */
|
|
40
|
+
toString(): FullPath;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* ============================================================================
|
|
44
|
+
* BUILDING THE TREE — turning definitions into nodes
|
|
45
|
+
* ============================================================================
|
|
46
|
+
* Done in small, separate steps instead of one big nested type, so each step
|
|
47
|
+
* can be read on its own.
|
|
48
|
+
*/
|
|
49
|
+
type NodeFromLeaf<Segment extends string, ParentPath extends string> = RouteNode<Segment, Join<ParentPath, Segment>, undefined>;
|
|
50
|
+
type NodeFromBranch<Branch extends RouteDefinition, ParentPath extends string> = Branch extends {
|
|
51
|
+
path: infer Segment extends string;
|
|
52
|
+
children: infer Children extends RouteDefinitionMap;
|
|
53
|
+
} ? RouteNode<Segment, Join<ParentPath, Segment>, RouteTree<Children, Join<ParentPath, Segment>>> : Branch extends {
|
|
54
|
+
path: infer Segment extends string;
|
|
55
|
+
} ? RouteNode<Segment, Join<ParentPath, Segment>, undefined> : never;
|
|
56
|
+
type NodeFromDefinition<Definition extends RouteDefinition, ParentPath extends string> = Definition extends string ? NodeFromLeaf<Definition, ParentPath> : NodeFromBranch<Definition, ParentPath>;
|
|
57
|
+
export type RouteTree<T extends RouteDefinitionMap, ParentPath extends string = ''> = {
|
|
58
|
+
readonly [Key in keyof T]: NodeFromDefinition<T[Key], ParentPath>;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* ============================================================================
|
|
62
|
+
* ROUTE PARAMS — reading `:id` out of a path
|
|
63
|
+
* ============================================================================
|
|
64
|
+
* ExtractParamNames<'/users/:id'> -> 'id'
|
|
65
|
+
* ExtractParamNames<'/users/:id/:tab'> -> 'id' | 'tab'
|
|
66
|
+
* ExtractParamNames<'/dashboard'> -> never (no params)
|
|
67
|
+
*/
|
|
68
|
+
export type ExtractParamNames<Path extends string> = Path extends `${string}:${infer Param}/${infer Rest}` ? Param | ExtractParamNames<`/${Rest}`> : Path extends `${string}:${infer Param}` ? Param : never;
|
|
69
|
+
/**
|
|
70
|
+
* RouteParams<'/users/:id'> -> { id: string | number }
|
|
71
|
+
* RouteParams<'/dashboard'> -> {} (nothing required)
|
|
72
|
+
*/
|
|
73
|
+
export type RouteParams<Path extends string> = [ExtractParamNames<Path>] extends [never] ? Record<string, never> : {
|
|
74
|
+
[Param in ExtractParamNames<Path>]: string | number;
|
|
75
|
+
};
|
|
76
|
+
export {};
|
|
77
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ============================================================================
|
|
3
|
+
* ROUTE DEFINITIONS — what you write
|
|
4
|
+
* ============================================================================
|
|
5
|
+
* You describe routes as a plain object. Each property is either:
|
|
6
|
+
*
|
|
7
|
+
* - a STRING ("leaf" route) e.g. `dashboard: 'dashboard'`
|
|
8
|
+
* - an OBJECT ("branch" route) e.g. `users: { path: 'users', children: { list: 'list' } }`
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public-api.d.ts","sourceRoot":"","sources":["../src/public-api.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public-api.js","sourceRoot":"","sources":["../src/public-api.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ngx-safe-routes",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Compile-time-safe route paths and i18n route maps for Angular. Catches typo'd/stale route strings at build time instead of at runtime navigation.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/public-api.js",
|
|
7
|
+
"module": "./dist/public-api.js",
|
|
8
|
+
"types": "./dist/public-api.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/public-api.d.ts",
|
|
12
|
+
"default": "./dist/public-api.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"sideEffects": false,
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsc -p tsconfig.build.json",
|
|
21
|
+
"prepublishOnly": "npm run build",
|
|
22
|
+
"typecheck": "tsc -p tsconfig.json"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"angular",
|
|
26
|
+
"router",
|
|
27
|
+
"routes",
|
|
28
|
+
"typescript",
|
|
29
|
+
"type-safety",
|
|
30
|
+
"route-map",
|
|
31
|
+
"i18n"
|
|
32
|
+
],
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"typescript": ">=5.0.0"
|
|
35
|
+
},
|
|
36
|
+
"author": "Sepehr Aghdasi",
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "git+https://github.com/Sepehr-Aghdasi/ngx-safe-routes.git"
|
|
41
|
+
},
|
|
42
|
+
"homepage": "https://github.com/Sepehr-Aghdasi/ngx-safe-routes#readme",
|
|
43
|
+
"bugs": {
|
|
44
|
+
"url": "https://github.com/Sepehr-Aghdasi/ngx-safe-routes/issues"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@types/node": "^26.1.1",
|
|
48
|
+
"typescript": "^7.0.2"
|
|
49
|
+
}
|
|
50
|
+
}
|