@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.
Files changed (61) hide show
  1. package/README.md +217 -70
  2. package/agent-meta/manifest.json +3 -3
  3. package/agent-meta/skills/route/SKILL.md +15 -17
  4. package/build/consts.d.ts +2 -3
  5. package/build/consts.d.ts.map +1 -1
  6. package/build/consts.js +1 -2
  7. package/build/consts.js.map +1 -1
  8. package/build/helper.d.ts +0 -6
  9. package/build/helper.d.ts.map +1 -1
  10. package/build/helper.js +0 -10
  11. package/build/helper.js.map +1 -1
  12. package/build/model.d.ts.map +1 -1
  13. package/build/model.js +4 -1
  14. package/build/model.js.map +1 -1
  15. package/build/types.d.ts +11 -14
  16. package/build/types.d.ts.map +1 -1
  17. package/build/utils/declaration/types.d.ts +9 -0
  18. package/build/utils/declaration/types.d.ts.map +1 -0
  19. package/build/utils/declaration/types.js +2 -0
  20. package/build/utils/declaration/types.js.map +1 -0
  21. package/build/utils/declaration.d.ts +4 -0
  22. package/build/utils/declaration.d.ts.map +1 -0
  23. package/build/utils/declaration.js +18 -0
  24. package/build/utils/declaration.js.map +1 -0
  25. package/build/utils/index.d.ts +4 -0
  26. package/build/utils/index.d.ts.map +1 -1
  27. package/build/utils/index.js +1 -0
  28. package/build/utils/index.js.map +1 -1
  29. package/build/utils/route/types.d.ts +35 -0
  30. package/build/utils/route/types.d.ts.map +1 -0
  31. package/build/utils/route/types.js +2 -0
  32. package/build/utils/route/types.js.map +1 -0
  33. package/build/utils/route.d.ts +5 -28
  34. package/build/utils/route.d.ts.map +1 -1
  35. package/build/utils/route.js +76 -97
  36. package/build/utils/route.js.map +1 -1
  37. package/build/utils/service/types.d.ts +9 -0
  38. package/build/utils/service/types.d.ts.map +1 -0
  39. package/build/utils/service/types.js +2 -0
  40. package/build/utils/service/types.js.map +1 -0
  41. package/build/utils/service.d.ts +3 -4
  42. package/build/utils/service.d.ts.map +1 -1
  43. package/build/utils/service.js +7 -4
  44. package/build/utils/service.js.map +1 -1
  45. package/build/utils/types.local.d.ts +9 -0
  46. package/build/utils/types.local.d.ts.map +1 -0
  47. package/build/utils/types.local.js +2 -0
  48. package/build/utils/types.local.js.map +1 -0
  49. package/package.json +3 -3
  50. package/src/consts.ts +2 -3
  51. package/src/helper.ts +0 -13
  52. package/src/model.ts +5 -2
  53. package/src/types.ts +13 -14
  54. package/src/utils/declaration/types.ts +9 -0
  55. package/src/utils/declaration.ts +24 -0
  56. package/src/utils/index.ts +4 -0
  57. package/src/utils/route/types.ts +35 -0
  58. package/src/utils/route.ts +85 -125
  59. package/src/utils/service/types.ts +9 -0
  60. package/src/utils/service.ts +12 -6
  61. 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
+ }
@@ -1,151 +1,111 @@
1
- import type { BasicConfig, BasicContext, Contextual } from '@owlmeans/context'
2
- import type { RouteAddress, RouteDeclaration, RouteModel, CommonServiceRoute, ResolvedServiceRoute } from '../types.js'
3
- import { isServiceRoute, isServiceRouteResolved } from './service.js'
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
- type Config = BasicConfig
10
+ export const makeRouteAddressHelper = <C extends Config, T extends BasicContext<C>>(context: T): RouteAddressHelper => {
11
+ const { isServiceRoute, isServiceRouteResolved } = serviceRouteHelper
8
12
 
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
- }
13
+ const carrier = (alias: string): RouteCarrier =>
14
+ context.entrypoint<RouteCarrier>(alias)
14
15
 
15
- const carrier = <C extends Config, T extends BasicContext<C>>(context: T, alias: string): RouteCarrier =>
16
- context.entrypoint<RouteCarrier>(alias)
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
- 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
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
- return parent.route.route
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
- export const overrideParams = (route: RouteDeclaration, overrides?: Partial<RouteDeclaration>, filter?: string[]) => {
34
- Object.entries(overrides ?? {}).forEach(([key, value]) => {
35
- if (route[key as keyof RouteDeclaration] == null
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
- * 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 => {
51
- if (context.cfg.services == null) {
52
- throw new SyntaxError('Services aren\'t configured to resolve routes')
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
- const named = context.cfg.services[route.service ?? context.cfg.service]
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 service = named?.type === route.type ? named
61
- : Object.values(context.cfg.services).find<CommonServiceRoute>(
62
- (candidate): candidate is CommonServiceRoute => {
63
- const _candidate = candidate as CommonServiceRoute
64
- return _candidate.default === true && _candidate.type === route.type
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
- if (!isServiceRoute(service)) {
71
- throw new SyntaxError('Service is not a valid service route')
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
- return service
78
- }
70
+ const resolveMount = (route: RouteDeclaration): string => {
71
+ const path = resolvePath(route)
72
+ const base = route.base ?? resolveService(route).base
79
73
 
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
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
- * 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,
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
- /** 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
-
134
- /**
135
- * @throws {SyntaxError}
136
- */
137
- const assertCycle = <C extends BasicConfig, T extends BasicContext<C>>(
138
- context: T, route: RouteDeclaration, parent: RouteDeclaration
139
- ) => {
140
- while (parent.parent != null) {
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 prependBase = (route: RouteDeclaration, path: string) =>
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
+ }
@@ -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 isServiceRoute = (obj?: Object): obj is CommonServiceRoute =>
5
- obj != null && ('type' in obj) && ('service' in obj)
6
- && (Object.values(AppType).includes(obj.type as AppType))
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
- /** A service route is usable exactly when it knows the host it answers on. */
9
- export const isServiceRouteResolved = (route: CommonServiceRoute): route is ResolvedServiceRoute =>
10
- route.host != null && route.host.trim() !== ''
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
+ }