@owlmeans/client-route 0.1.1 → 0.1.3
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 +1 -1
- package/README.md +29 -339
- package/package.json +6 -4
- package/tsconfig.json +5 -10
package/LICENSE
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c)
|
|
3
|
+
Copyright (c) 2026 OwlMeans Common — Fullstack typescript framework
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
package/README.md
CHANGED
|
@@ -1,368 +1,58 @@
|
|
|
1
1
|
# @owlmeans/client-route
|
|
2
2
|
|
|
3
|
-
Client-side
|
|
3
|
+
Client-side route model extension — marks routes as client-side and provides URL parameter utilities.
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
### Key Features
|
|
12
|
-
|
|
13
|
-
- **Client Route Models** - Extends base route models with client-specific behavior
|
|
14
|
-
- **Partial Path Preservation** - Maintains original path patterns for client-side processing
|
|
15
|
-
- **Parameter Extraction** - Utility functions for extracting route parameters
|
|
16
|
-
- **Promise-based Resolution** - Manages asynchronous route resolution with state tracking
|
|
17
|
-
- **Type Safety** - Full TypeScript support with proper type definitions
|
|
7
|
+
- `route(routeModel, opts?)` — wraps a `CommonRouteModel` into a `ClientRouteModel` (marks `_client: true`)
|
|
8
|
+
- `isClientRouteModel(route)` — type guard distinguishing client routes from server routes
|
|
9
|
+
- `extractParams(path)` — extracts path parameter names (`:param` segments) from a URL pattern
|
|
10
|
+
- Used internally by `@owlmeans/client-module` when building client-side module URLs
|
|
18
11
|
|
|
19
12
|
## Installation
|
|
20
13
|
|
|
21
14
|
```bash
|
|
22
|
-
|
|
15
|
+
bun add @owlmeans/client-route
|
|
23
16
|
```
|
|
24
17
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
## Core Concepts
|
|
28
|
-
|
|
29
|
-
### Client Route
|
|
30
|
-
A client route extends the base `CommonRoute` with additional client-specific properties, particularly the `partialPath` which preserves the original path pattern for client-side processing.
|
|
31
|
-
|
|
32
|
-
### Client Route Model
|
|
33
|
-
A client route model wraps a client route with additional behavior, including promise-based resolution tracking and client-specific resolution logic.
|
|
34
|
-
|
|
35
|
-
### Parameter Extraction
|
|
36
|
-
The package provides utilities to extract parameters from route paths, useful for client-side routing and navigation.
|
|
37
|
-
|
|
38
|
-
## API Reference
|
|
39
|
-
|
|
40
|
-
### Types
|
|
41
|
-
|
|
42
|
-
#### ClientRoute
|
|
43
|
-
Extended route interface with client-specific properties.
|
|
18
|
+
## Usage
|
|
44
19
|
|
|
45
20
|
```typescript
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
```
|
|
21
|
+
import { route, isClientRouteModel, extractParams } from '@owlmeans/client-route'
|
|
22
|
+
import type { ClientRouteModel } from '@owlmeans/client-route'
|
|
50
23
|
|
|
51
|
-
|
|
52
|
-
|
|
24
|
+
// Wrap a route as a client route
|
|
25
|
+
const clientRoute = route(someRouteModel, { overrides: { partialPath: '/items' } })
|
|
53
26
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
```typescript
|
|
58
|
-
interface ClientRouteModel extends CommonRouteModel {
|
|
59
|
-
route: ClientRoute // The wrapped client route
|
|
60
|
-
_resolved?: Promise<void> // Promise tracking resolution state
|
|
61
|
-
_client: true // Client route marker
|
|
27
|
+
// Type guard
|
|
28
|
+
if (isClientRouteModel(arg)) {
|
|
29
|
+
// arg is ClientRouteModel
|
|
62
30
|
}
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
**Properties:**
|
|
66
|
-
- **route**: `ClientRoute` - The wrapped client route object
|
|
67
|
-
- **_resolved**: `Promise<void>` - Optional promise that tracks the resolution state
|
|
68
|
-
- **_client**: `true` - Marker indicating this is a client route model
|
|
69
31
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
```typescript
|
|
74
|
-
interface ClientRouteOptions {
|
|
75
|
-
overrides?: Partial<ClientRoute> // Route parameter overrides
|
|
76
|
-
}
|
|
32
|
+
// Extract named params from a path pattern
|
|
33
|
+
const params = extractParams('/projects/:projectId/items/:itemId')
|
|
34
|
+
// => ['projectId', 'itemId']
|
|
77
35
|
```
|
|
78
36
|
|
|
79
|
-
|
|
80
|
-
- **overrides**: `Partial<ClientRoute>` - Optional route parameter overrides
|
|
81
|
-
|
|
82
|
-
### Core Functions
|
|
37
|
+
## API
|
|
83
38
|
|
|
84
|
-
|
|
85
|
-
Wraps a base route model with client-specific functionality.
|
|
39
|
+
### `route(route, opts?): ClientRouteModel`
|
|
86
40
|
|
|
87
|
-
|
|
88
|
-
const route: (route: CommonRouteModel, opts?: ClientRouteOptions) => ClientRouteModel
|
|
89
|
-
```
|
|
41
|
+
Converts a `CommonRouteModel` to a `ClientRouteModel`. Options: `overrides?: Partial<ClientRoute>`.
|
|
90
42
|
|
|
91
|
-
|
|
92
|
-
- `route: CommonRouteModel` - Base route model from `@owlmeans/route`
|
|
93
|
-
- `opts?: ClientRouteOptions` - Optional configuration options
|
|
43
|
+
### `isClientRouteModel(route): route is ClientRouteModel`
|
|
94
44
|
|
|
95
|
-
|
|
45
|
+
Returns `true` if the route was created with `route()`.
|
|
96
46
|
|
|
97
|
-
|
|
98
|
-
- Preserves original path as `partialPath`
|
|
99
|
-
- Adds promise-based resolution tracking
|
|
100
|
-
- Provides client-specific resolution logic
|
|
101
|
-
- Supports parameter overrides
|
|
102
|
-
|
|
103
|
-
**Example:**
|
|
104
|
-
```typescript
|
|
105
|
-
import { route as baseRoute } from '@owlmeans/route'
|
|
106
|
-
import { route as clientRoute } from '@owlmeans/client-route'
|
|
47
|
+
### `extractParams(path): string[]`
|
|
107
48
|
|
|
108
|
-
|
|
109
|
-
const userRoute = baseRoute('user', '/users/:id')
|
|
49
|
+
Returns the list of `:param` segment names in a URL pattern string.
|
|
110
50
|
|
|
111
|
-
|
|
112
|
-
const clientUserRoute = clientRoute(userRoute, {
|
|
113
|
-
overrides: {
|
|
114
|
-
secure: true
|
|
115
|
-
}
|
|
116
|
-
})
|
|
117
|
-
|
|
118
|
-
// Use client-specific features
|
|
119
|
-
console.log(clientUserRoute.route.partialPath) // '/users/:id'
|
|
120
|
-
const resolved = await clientUserRoute.resolve(context)
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
### Helper Functions
|
|
124
|
-
|
|
125
|
-
#### isClientRouteModel(route)
|
|
126
|
-
Type guard to check if a route object is a client route model.
|
|
127
|
-
|
|
128
|
-
```typescript
|
|
129
|
-
const isClientRouteModel: (route: Object) => route is ClientRouteModel
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
**Parameters:**
|
|
133
|
-
- `route: Object` - Object to check
|
|
134
|
-
|
|
135
|
-
**Returns:** `boolean` - True if the object is a client route model
|
|
136
|
-
|
|
137
|
-
**Example:**
|
|
138
|
-
```typescript
|
|
139
|
-
import { isClientRouteModel } from '@owlmeans/client-route'
|
|
140
|
-
|
|
141
|
-
if (isClientRouteModel(routeObject)) {
|
|
142
|
-
// TypeScript knows this is a ClientRouteModel
|
|
143
|
-
console.log(routeObject.route.partialPath)
|
|
144
|
-
}
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
#### extractParams(path)
|
|
148
|
-
Extracts parameter names from a route path.
|
|
149
|
-
|
|
150
|
-
```typescript
|
|
151
|
-
const extractParams: (path: string) => string[]
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
**Parameters:**
|
|
155
|
-
- `path: string` - Route path pattern
|
|
156
|
-
|
|
157
|
-
**Returns:** `string[]` - Array of parameter names
|
|
158
|
-
|
|
159
|
-
**Example:**
|
|
160
|
-
```typescript
|
|
161
|
-
import { extractParams } from '@owlmeans/client-route'
|
|
162
|
-
|
|
163
|
-
const params = extractParams('/users/:id/posts/:postId')
|
|
164
|
-
console.log(params) // ['id', 'postId']
|
|
165
|
-
|
|
166
|
-
const simpleParams = extractParams('/api/users/:userId')
|
|
167
|
-
console.log(simpleParams) // ['userId']
|
|
168
|
-
|
|
169
|
-
const noParams = extractParams('/api/users')
|
|
170
|
-
console.log(noParams) // []
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
## Usage Examples
|
|
174
|
-
|
|
175
|
-
### Basic Client Route Creation
|
|
176
|
-
|
|
177
|
-
```typescript
|
|
178
|
-
import { route as baseRoute } from '@owlmeans/route'
|
|
179
|
-
import { route as clientRoute } from '@owlmeans/client-route'
|
|
180
|
-
|
|
181
|
-
// Create a base route
|
|
182
|
-
const apiRoute = baseRoute('api', '/api/users/:id')
|
|
183
|
-
|
|
184
|
-
// Wrap with client functionality
|
|
185
|
-
const clientApiRoute = clientRoute(apiRoute)
|
|
186
|
-
|
|
187
|
-
// Access client-specific features
|
|
188
|
-
console.log(clientApiRoute.route.partialPath) // '/api/users/:id'
|
|
189
|
-
console.log(clientApiRoute._client) // true
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
### Route Resolution with State Tracking
|
|
193
|
-
|
|
194
|
-
```typescript
|
|
195
|
-
import { route as baseRoute } from '@owlmeans/route'
|
|
196
|
-
import { route as clientRoute } from '@owlmeans/client-route'
|
|
51
|
+
### `ClientRouteModel`
|
|
197
52
|
|
|
198
|
-
|
|
199
|
-
const clientUserRoute = clientRoute(userRoute)
|
|
200
|
-
|
|
201
|
-
// Resolve the route
|
|
202
|
-
const resolved = await clientUserRoute.resolve(context)
|
|
203
|
-
|
|
204
|
-
// The partial path is preserved
|
|
205
|
-
console.log(resolved.partialPath) // '/users/:id'
|
|
206
|
-
console.log(resolved.path) // '/users/123' (resolved)
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
### Parameter Extraction
|
|
210
|
-
|
|
211
|
-
```typescript
|
|
212
|
-
import { extractParams } from '@owlmeans/client-route'
|
|
213
|
-
|
|
214
|
-
// Extract parameters from different path patterns
|
|
215
|
-
const userParams = extractParams('/users/:id')
|
|
216
|
-
console.log(userParams) // ['id']
|
|
217
|
-
|
|
218
|
-
const nestedParams = extractParams('/users/:userId/posts/:postId/comments/:commentId')
|
|
219
|
-
console.log(nestedParams) // ['userId', 'postId', 'commentId']
|
|
220
|
-
|
|
221
|
-
// Handle paths with no parameters
|
|
222
|
-
const staticParams = extractParams('/api/health')
|
|
223
|
-
console.log(staticParams) // []
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
### Type Guards and Validation
|
|
227
|
-
|
|
228
|
-
```typescript
|
|
229
|
-
import { isClientRouteModel } from '@owlmeans/client-route'
|
|
230
|
-
import { route as baseRoute } from '@owlmeans/route'
|
|
231
|
-
import { route as clientRoute } from '@owlmeans/client-route'
|
|
232
|
-
|
|
233
|
-
const baseRouteModel = baseRoute('api', '/api')
|
|
234
|
-
const clientRouteModel = clientRoute(baseRouteModel)
|
|
235
|
-
|
|
236
|
-
console.log(isClientRouteModel(baseRouteModel)) // false
|
|
237
|
-
console.log(isClientRouteModel(clientRouteModel)) // true
|
|
238
|
-
|
|
239
|
-
// Type-safe access
|
|
240
|
-
if (isClientRouteModel(someRoute)) {
|
|
241
|
-
console.log(someRoute.route.partialPath) // TypeScript knows this exists
|
|
242
|
-
}
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
### Route Overrides
|
|
246
|
-
|
|
247
|
-
```typescript
|
|
248
|
-
import { route as baseRoute } from '@owlmeans/route'
|
|
249
|
-
import { route as clientRoute } from '@owlmeans/client-route'
|
|
250
|
-
|
|
251
|
-
const baseUserRoute = baseRoute('user', '/users/:id')
|
|
252
|
-
|
|
253
|
-
// Override route properties
|
|
254
|
-
const clientUserRoute = clientRoute(baseUserRoute, {
|
|
255
|
-
overrides: {
|
|
256
|
-
secure: true,
|
|
257
|
-
method: RouteMethod.GET
|
|
258
|
-
}
|
|
259
|
-
})
|
|
260
|
-
|
|
261
|
-
console.log(clientUserRoute.route.secure) // true
|
|
262
|
-
console.log(clientUserRoute.route.partialPath) // '/users/:id'
|
|
263
|
-
```
|
|
264
|
-
|
|
265
|
-
## Integration with OwlMeans Context
|
|
266
|
-
|
|
267
|
-
The client-route package integrates seamlessly with the OwlMeans context system and works in conjunction with other OwlMeans Common modules:
|
|
268
|
-
|
|
269
|
-
```typescript
|
|
270
|
-
import { route as baseRoute, frontend } from '@owlmeans/route'
|
|
271
|
-
import { route as clientRoute } from '@owlmeans/client-route'
|
|
272
|
-
import { makeClientContext } from '@owlmeans/client-context'
|
|
273
|
-
|
|
274
|
-
// Create a frontend route
|
|
275
|
-
const homeRoute = baseRoute('home', '/', frontend())
|
|
276
|
-
|
|
277
|
-
// Wrap with client functionality
|
|
278
|
-
const clientHomeRoute = clientRoute(homeRoute)
|
|
279
|
-
|
|
280
|
-
// Use with client context
|
|
281
|
-
const context = makeClientContext(config)
|
|
282
|
-
const resolved = await clientHomeRoute.resolve(context)
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
## Advanced Usage
|
|
286
|
-
|
|
287
|
-
### Custom Resolution Logic
|
|
288
|
-
|
|
289
|
-
```typescript
|
|
290
|
-
import { route as baseRoute } from '@owlmeans/route'
|
|
291
|
-
import { route as clientRoute } from '@owlmeans/client-route'
|
|
292
|
-
|
|
293
|
-
const baseRoute = baseRoute('api', '/api/:version')
|
|
294
|
-
const clientApiRoute = clientRoute(baseRoute)
|
|
295
|
-
|
|
296
|
-
// The client route preserves the original path pattern
|
|
297
|
-
const resolved = await clientApiRoute.resolve(context)
|
|
298
|
-
|
|
299
|
-
// Use partial path for client-side routing
|
|
300
|
-
const navigationPath = clientApiRoute.route.partialPath
|
|
301
|
-
const resolvedPath = resolved.path
|
|
302
|
-
|
|
303
|
-
console.log(navigationPath) // '/api/:version' (for client routing)
|
|
304
|
-
console.log(resolvedPath) // '/api/v1' (resolved for actual requests)
|
|
305
|
-
```
|
|
306
|
-
|
|
307
|
-
### Parameter-Based Navigation
|
|
308
|
-
|
|
309
|
-
```typescript
|
|
310
|
-
import { extractParams } from '@owlmeans/client-route'
|
|
311
|
-
|
|
312
|
-
const buildNavigationUrl = (pathPattern: string, params: Record<string, string>) => {
|
|
313
|
-
const paramNames = extractParams(pathPattern)
|
|
314
|
-
let url = pathPattern
|
|
315
|
-
|
|
316
|
-
paramNames.forEach(param => {
|
|
317
|
-
url = url.replace(`:${param}`, params[param] || '')
|
|
318
|
-
})
|
|
319
|
-
|
|
320
|
-
return url
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// Usage
|
|
324
|
-
const pattern = '/users/:userId/posts/:postId'
|
|
325
|
-
const url = buildNavigationUrl(pattern, { userId: '123', postId: '456' })
|
|
326
|
-
console.log(url) // '/users/123/posts/456'
|
|
327
|
-
```
|
|
328
|
-
|
|
329
|
-
## Error Handling
|
|
330
|
-
|
|
331
|
-
The package provides comprehensive error handling:
|
|
332
|
-
|
|
333
|
-
- **SyntaxError** - Thrown when attempting to resolve a route that's already resolved without proper promise handling
|
|
334
|
-
- **Type Guards** - Use `isClientRouteModel` to ensure type safety
|
|
335
|
-
- **Promise Management** - The `_resolved` promise prevents multiple resolution attempts
|
|
336
|
-
|
|
337
|
-
## Best Practices
|
|
338
|
-
|
|
339
|
-
1. **Use Type Guards** - Always use `isClientRouteModel` when working with mixed route types
|
|
340
|
-
2. **Handle Promises** - Properly handle the `_resolved` promise to avoid resolution conflicts
|
|
341
|
-
3. **Preserve Partial Paths** - Use the `partialPath` property for client-side routing logic
|
|
342
|
-
4. **Parameter Extraction** - Use `extractParams` for dynamic route handling
|
|
343
|
-
5. **Integration** - Combine with other OwlMeans Common modules for full functionality
|
|
344
|
-
|
|
345
|
-
## Related Modules
|
|
346
|
-
|
|
347
|
-
- **@owlmeans/route** - Base routing functionality (required dependency)
|
|
348
|
-
- **@owlmeans/client-context** - Client-side context management (required dependency)
|
|
349
|
-
- **@owlmeans/context** - Core context system for route resolution
|
|
350
|
-
- **@owlmeans/server-route** - Server-side route implementations
|
|
351
|
-
|
|
352
|
-
## Migration from @owlmeans/route
|
|
353
|
-
|
|
354
|
-
If you're upgrading from using `@owlmeans/route` directly in a client application:
|
|
355
|
-
|
|
356
|
-
```typescript
|
|
357
|
-
// Before
|
|
358
|
-
import { route } from '@owlmeans/route'
|
|
359
|
-
const userRoute = route('user', '/users/:id')
|
|
53
|
+
Extends `CommonRouteModel` with `_client: true` flag and `partialPath` for relative URL segments.
|
|
360
54
|
|
|
361
|
-
|
|
362
|
-
import { route as baseRoute } from '@owlmeans/route'
|
|
363
|
-
import { route as clientRoute } from '@owlmeans/client-route'
|
|
364
|
-
const userRoute = clientRoute(baseRoute('user', '/users/:id'))
|
|
55
|
+
## Related Packages
|
|
365
56
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
```
|
|
57
|
+
- [`@owlmeans/route`](../route) — `CommonRouteModel`, `CommonRoute` base types
|
|
58
|
+
- [`@owlmeans/client-module`](../client-module) — uses `route()` and `isClientRouteModel()` when constructing modules
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@owlmeans/client-route",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
|
+
"license": "MIT",
|
|
4
5
|
"type": "module",
|
|
5
6
|
"scripts": {
|
|
6
7
|
"build": "tsc -b",
|
|
@@ -20,12 +21,13 @@
|
|
|
20
21
|
}
|
|
21
22
|
},
|
|
22
23
|
"dependencies": {
|
|
23
|
-
"@owlmeans/client-context": "^0.1.
|
|
24
|
-
"@owlmeans/route": "^0.1.
|
|
24
|
+
"@owlmeans/client-context": "^0.1.3",
|
|
25
|
+
"@owlmeans/route": "^0.1.3"
|
|
25
26
|
},
|
|
26
27
|
"devDependencies": {
|
|
28
|
+
"@owlmeans/dep-config": "workspace:*",
|
|
27
29
|
"nodemon": "^3.1.11",
|
|
28
|
-
"typescript": "^
|
|
30
|
+
"typescript": "^6.0.2"
|
|
29
31
|
},
|
|
30
32
|
"publishConfig": {
|
|
31
33
|
"access": "public"
|
package/tsconfig.json
CHANGED
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"extends": [
|
|
3
|
-
"
|
|
3
|
+
"@owlmeans/dep-config/tsconfig.base.json"
|
|
4
4
|
],
|
|
5
5
|
"compilerOptions": {
|
|
6
|
-
"rootDir": "./src/",
|
|
7
|
-
"outDir": "./build/"
|
|
8
|
-
"moduleResolution": "Bundler"
|
|
6
|
+
"rootDir": "./src/",
|
|
7
|
+
"outDir": "./build/"
|
|
9
8
|
},
|
|
10
|
-
"exclude": [
|
|
11
|
-
|
|
12
|
-
"./build/**/*",
|
|
13
|
-
"./*.ts"
|
|
14
|
-
]
|
|
15
|
-
}
|
|
9
|
+
"exclude": ["./dist/**/*", "./build/**/*", "./*.ts"]
|
|
10
|
+
}
|