@owlmeans/route 0.1.18-rc.2 → 0.1.18-rc.21
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/README.md +236 -43
- package/agent-meta/manifest.json +2 -2
- package/agent-meta/skills/route/SKILL.md +72 -14
- package/build/consts.d.ts +7 -1
- package/build/consts.d.ts.map +1 -1
- package/build/consts.js +6 -0
- package/build/consts.js.map +1 -1
- package/build/helper.d.ts +8 -2
- package/build/helper.d.ts.map +1 -1
- package/build/helper.js +10 -0
- package/build/helper.js.map +1 -1
- package/build/model.d.ts +3 -3
- package/build/model.d.ts.map +1 -1
- package/build/model.js +5 -10
- package/build/model.js.map +1 -1
- package/build/types.d.ts +46 -15
- package/build/types.d.ts.map +1 -1
- package/build/utils/route.d.ts +25 -5
- package/build/utils/route.d.ts.map +1 -1
- package/build/utils/route.js +68 -67
- package/build/utils/route.js.map +1 -1
- package/build/utils/service.d.ts +1 -0
- package/build/utils/service.d.ts.map +1 -1
- package/build/utils/service.js +2 -1
- package/build/utils/service.js.map +1 -1
- package/package.json +2 -2
- package/src/consts.ts +7 -1
- package/src/helper.ts +15 -2
- package/src/model.ts +9 -15
- package/src/types.ts +48 -15
- package/src/utils/route.ts +101 -71
- package/src/utils/service.ts +2 -1
- package/build/.gitkeep +0 -0
package/src/utils/route.ts
CHANGED
|
@@ -1,121 +1,151 @@
|
|
|
1
1
|
import type { BasicConfig, BasicContext, Contextual } from '@owlmeans/context'
|
|
2
|
-
import type {
|
|
2
|
+
import type { RouteAddress, RouteDeclaration, RouteModel, CommonServiceRoute, ResolvedServiceRoute } from '../types.js'
|
|
3
3
|
import { isServiceRoute, isServiceRouteResolved } from './service.js'
|
|
4
4
|
import { normalizePath } from '../helper.js'
|
|
5
|
-
import { SEP } from '../consts.js'
|
|
5
|
+
import { RouteProtocols, SEP } from '../consts.js'
|
|
6
6
|
|
|
7
7
|
type Config = BasicConfig
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
return parent.route.route
|
|
9
|
+
/** What an entrypoint looks like from the route layer: something that carries a route model. */
|
|
10
|
+
interface RouteCarrier extends Contextual {
|
|
11
|
+
_entrypoint: true
|
|
12
|
+
route: RouteModel
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const carrier = <C extends Config, T extends BasicContext<C>>(context: T, alias: string): RouteCarrier =>
|
|
16
|
+
context.entrypoint<RouteCarrier>(alias)
|
|
17
|
+
|
|
18
|
+
export const getParentRoute = <C extends Config, T extends BasicContext<C>>(
|
|
19
|
+
context: T, route: RouteDeclaration
|
|
20
|
+
): RouteDeclaration | null => {
|
|
21
|
+
if (route.parent == null) {
|
|
22
|
+
return null
|
|
24
23
|
}
|
|
24
|
+
const parent = carrier<C, T>(context, route.parent)
|
|
25
|
+
if (parent.route == null) {
|
|
26
|
+
throw new SyntaxError('Parent entrypoint doesn\'t provide a route')
|
|
27
|
+
}
|
|
28
|
+
assertCycle<C, T>(context, route, parent.route.route)
|
|
25
29
|
|
|
26
|
-
return
|
|
30
|
+
return parent.route.route
|
|
27
31
|
}
|
|
28
32
|
|
|
29
|
-
export const overrideParams = (route:
|
|
33
|
+
export const overrideParams = (route: RouteDeclaration, overrides?: Partial<RouteDeclaration>, filter?: string[]) => {
|
|
30
34
|
Object.entries(overrides ?? {}).forEach(([key, value]) => {
|
|
31
|
-
if (route[key as keyof
|
|
35
|
+
if (route[key as keyof RouteDeclaration] == null
|
|
32
36
|
&& (filter == null || filter.includes(key))) {
|
|
33
|
-
(route[key as keyof
|
|
37
|
+
(route[key as keyof RouteDeclaration] as RouteDeclaration[keyof RouteDeclaration]) = value
|
|
34
38
|
}
|
|
35
39
|
})
|
|
36
40
|
}
|
|
37
41
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Pick the service route this declaration answers on: the one it names, else the default of its
|
|
44
|
+
* app type, else the first of its app type.
|
|
45
|
+
*
|
|
46
|
+
* @throws {SyntaxError}
|
|
47
|
+
*/
|
|
48
|
+
export const resolveService = <C extends Config, T extends BasicContext<C>>(
|
|
49
|
+
context: T, route: RouteDeclaration
|
|
50
|
+
): ResolvedServiceRoute => {
|
|
42
51
|
if (context.cfg.services == null) {
|
|
43
52
|
throw new SyntaxError('Services aren\'t configured to resolve routes')
|
|
44
53
|
}
|
|
45
54
|
|
|
46
|
-
const
|
|
47
|
-
if (
|
|
55
|
+
const named = context.cfg.services[route.service ?? context.cfg.service]
|
|
56
|
+
if (named != null && !isServiceRoute(named)) {
|
|
48
57
|
throw new SyntaxError('Service is not a valid service route')
|
|
49
58
|
}
|
|
50
|
-
|
|
59
|
+
|
|
60
|
+
const service = named?.type === route.type ? named
|
|
51
61
|
: Object.values(context.cfg.services).find<CommonServiceRoute>(
|
|
52
|
-
(
|
|
53
|
-
const
|
|
54
|
-
return
|
|
62
|
+
(candidate): candidate is CommonServiceRoute => {
|
|
63
|
+
const _candidate = candidate as CommonServiceRoute
|
|
64
|
+
return _candidate.default === true && _candidate.type === route.type
|
|
55
65
|
}
|
|
56
66
|
) ?? Object.values(context.cfg.services).find<CommonServiceRoute>(
|
|
57
|
-
(
|
|
58
|
-
const _service = service as CommonServiceRoute
|
|
59
|
-
return _service.type === route.type
|
|
60
|
-
}
|
|
67
|
+
(candidate): candidate is CommonServiceRoute => (candidate as CommonServiceRoute).type === route.type
|
|
61
68
|
)
|
|
69
|
+
|
|
62
70
|
if (!isServiceRoute(service)) {
|
|
63
71
|
throw new SyntaxError('Service is not a valid service route')
|
|
64
72
|
}
|
|
65
|
-
if (!service.resolved && service.host != null) {
|
|
66
|
-
service.resolved = true
|
|
67
|
-
}
|
|
68
73
|
if (!isServiceRouteResolved(service)) {
|
|
69
74
|
throw new SyntaxError('Service route is not resolved')
|
|
70
75
|
}
|
|
71
76
|
|
|
72
|
-
|
|
77
|
+
return service
|
|
78
|
+
}
|
|
73
79
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
80
|
+
/**
|
|
81
|
+
* The full path: every ancestor's declared segment, then this one. Walks the parent chain through
|
|
82
|
+
* the context, so a declaration always states only what it contributes.
|
|
83
|
+
*/
|
|
84
|
+
export const resolvePath = <C extends Config, T extends BasicContext<C>>(
|
|
85
|
+
context: T, route: RouteDeclaration
|
|
86
|
+
): string => {
|
|
87
|
+
const parent = getParentRoute<C, T>(context, route)
|
|
88
|
+
if (parent == null) {
|
|
89
|
+
return route.path
|
|
84
90
|
}
|
|
85
|
-
|
|
91
|
+
const parentPath = resolvePath<C, T>(context, parent)
|
|
86
92
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
+ normalizePath(normalizePath(parent.path) + SEP + normalizePath(route.path))
|
|
91
|
-
}
|
|
93
|
+
return (parentPath.startsWith(SEP) ? SEP : '')
|
|
94
|
+
+ normalizePath(normalizePath(parentPath) + SEP + normalizePath(route.path))
|
|
95
|
+
}
|
|
92
96
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
97
|
+
/** `base` + the full path — what a server mounts and a client requests. */
|
|
98
|
+
export const resolveMount = <C extends Config, T extends BasicContext<C>>(
|
|
99
|
+
context: T, route: RouteDeclaration
|
|
100
|
+
): string => {
|
|
101
|
+
const path = resolvePath<C, T>(context, route)
|
|
102
|
+
const base = route.base ?? resolveService<C, T>(context, route).base
|
|
98
103
|
|
|
99
|
-
return
|
|
104
|
+
return base != null && base.trim() !== ''
|
|
105
|
+
? SEP + normalizePath(base) + SEP + normalizePath(path)
|
|
106
|
+
: path
|
|
100
107
|
}
|
|
101
108
|
|
|
109
|
+
/**
|
|
110
|
+
* Where the route answers. A backend caller reaching a service over its INTERNAL host is inside the
|
|
111
|
+
* cluster, so that hop is never TLS.
|
|
112
|
+
*/
|
|
113
|
+
export const resolveAddress = <C extends Config, T extends BasicContext<C>>(
|
|
114
|
+
context: T, route: RouteDeclaration
|
|
115
|
+
): RouteAddress => {
|
|
116
|
+
const service = resolveService<C, T>(context, route)
|
|
117
|
+
const host = route.host ?? service.host
|
|
118
|
+
const internalHost = route.internalHost ?? service.internalHost
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
host,
|
|
122
|
+
port: route.port ?? service.port,
|
|
123
|
+
base: route.base ?? service.base,
|
|
124
|
+
secure: internalHost != null && internalHost === host ? false : route.secure ?? true,
|
|
125
|
+
protocol: route.protocol ?? RouteProtocols.WEB,
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Does this route belong to the service the asking context IS? */
|
|
130
|
+
export const isLocalRoute = <C extends Config, T extends BasicContext<C>>(
|
|
131
|
+
context: T, route: RouteDeclaration
|
|
132
|
+
): boolean => (route.service ?? context.cfg.service) === context.cfg.service
|
|
133
|
+
|
|
102
134
|
/**
|
|
103
135
|
* @throws {SyntaxError}
|
|
104
136
|
*/
|
|
105
|
-
const assertCycle = <C extends BasicConfig, T extends BasicContext<C>>(
|
|
137
|
+
const assertCycle = <C extends BasicConfig, T extends BasicContext<C>>(
|
|
138
|
+
context: T, route: RouteDeclaration, parent: RouteDeclaration
|
|
139
|
+
) => {
|
|
106
140
|
while (parent.parent != null) {
|
|
107
141
|
if (parent.parent === route.alias) {
|
|
108
142
|
throw new SyntaxError(`Route parentship cycle detected. Parent: ${parent.alias} has his child as ancestor ${route.alias}`)
|
|
109
143
|
}
|
|
110
|
-
parent =
|
|
144
|
+
parent = carrier<C, T>(context, parent.parent).route.route
|
|
111
145
|
}
|
|
112
146
|
}
|
|
113
147
|
|
|
114
|
-
export const prependBase = (route:
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
// route.path = route.base.startsWith(SEP) ? SEP + route.path : route.path
|
|
119
|
-
}
|
|
120
|
-
return path
|
|
121
|
-
}
|
|
148
|
+
export const prependBase = (route: RouteDeclaration, path: string) =>
|
|
149
|
+
route.base != null && route.base.trim() !== ''
|
|
150
|
+
? SEP + normalizePath(route.base) + SEP + normalizePath(path)
|
|
151
|
+
: path
|
package/src/utils/service.ts
CHANGED
|
@@ -5,5 +5,6 @@ export const isServiceRoute = (obj?: Object): obj is CommonServiceRoute =>
|
|
|
5
5
|
obj != null && ('type' in obj) && ('service' in obj)
|
|
6
6
|
&& (Object.values(AppType).includes(obj.type as AppType))
|
|
7
7
|
|
|
8
|
+
/** A service route is usable exactly when it knows the host it answers on. */
|
|
8
9
|
export const isServiceRouteResolved = (route: CommonServiceRoute): route is ResolvedServiceRoute =>
|
|
9
|
-
route.
|
|
10
|
+
route.host != null && route.host.trim() !== ''
|
package/build/.gitkeep
DELETED
|
File without changes
|