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