@owlmeans/route 0.1.18-rc.34 → 0.1.18-rc.36

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 (41) hide show
  1. package/README.md +21 -18
  2. package/agent-meta/manifest.json +3 -3
  3. package/agent-meta/skills/route/SKILL.md +1 -1
  4. package/build/model.js.map +1 -1
  5. package/build/utils/declaration/types.d.ts +9 -0
  6. package/build/utils/declaration/types.d.ts.map +1 -0
  7. package/build/utils/declaration/types.js +2 -0
  8. package/build/utils/declaration/types.js.map +1 -0
  9. package/build/utils/declaration.d.ts +4 -0
  10. package/build/utils/declaration.d.ts.map +1 -0
  11. package/build/utils/declaration.js +18 -0
  12. package/build/utils/declaration.js.map +1 -0
  13. package/build/utils/index.d.ts +4 -0
  14. package/build/utils/index.d.ts.map +1 -1
  15. package/build/utils/index.js +1 -0
  16. package/build/utils/index.js.map +1 -1
  17. package/build/utils/route/types.d.ts +35 -0
  18. package/build/utils/route/types.d.ts.map +1 -0
  19. package/build/utils/route/types.js +2 -0
  20. package/build/utils/route/types.js.map +1 -0
  21. package/build/utils/route.d.ts +3 -25
  22. package/build/utils/route.d.ts.map +1 -1
  23. package/build/utils/route.js +76 -97
  24. package/build/utils/route.js.map +1 -1
  25. package/build/utils/service/types.d.ts +9 -0
  26. package/build/utils/service/types.d.ts.map +1 -0
  27. package/build/utils/service/types.js +2 -0
  28. package/build/utils/service/types.js.map +1 -0
  29. package/build/utils/service.d.ts +3 -4
  30. package/build/utils/service.d.ts.map +1 -1
  31. package/build/utils/service.js +7 -4
  32. package/build/utils/service.js.map +1 -1
  33. package/package.json +2 -2
  34. package/src/model.ts +1 -1
  35. package/src/utils/declaration/types.ts +9 -0
  36. package/src/utils/declaration.ts +24 -0
  37. package/src/utils/index.ts +4 -0
  38. package/src/utils/route/types.ts +35 -0
  39. package/src/utils/route.ts +85 -118
  40. package/src/utils/service/types.ts +9 -0
  41. package/src/utils/service.ts +12 -6
package/README.md CHANGED
@@ -16,7 +16,7 @@ context that asks, so the same declaration serves a server and a browser alike.
16
16
  ## Installation
17
17
 
18
18
  ```bash
19
- bun add @owlmeans/route@^0.1.18-rc.34
19
+ bun add @owlmeans/route@^0.1.18-rc.36
20
20
  ```
21
21
 
22
22
  ## Concepts
@@ -148,7 +148,7 @@ Once protocols are registered on a context, the entrypoint accessors answer addr
148
148
  ```ts
149
149
  import { AppType } from '@owlmeans/context'
150
150
  import { service } from '@owlmeans/config'
151
- import { resolveAddress, resolveMount } from '@owlmeans/route/utils'
151
+ import { routeAddressOf } from '@owlmeans/route/utils'
152
152
  import { invoiceProtocols } from 'my-app-common'
153
153
 
154
154
  // Config side: the service the backend routes resolve against
@@ -163,8 +163,9 @@ entrypoint.isLocal() // true inside the my-app-api process
163
163
 
164
164
  // Same answers from a declaration
165
165
  const declaration = invoiceProtocols.create.route.route
166
- resolveMount(context, declaration)
167
- resolveAddress(context, declaration)
166
+ const addresses = routeAddressOf(context)
167
+ addresses.resolveMount(declaration)
168
+ addresses.resolveAddress(declaration)
168
169
  ```
169
170
 
170
171
  ## API
@@ -197,20 +198,22 @@ resolveAddress(context, declaration)
197
198
 
198
199
  ### `@owlmeans/route/utils`
199
200
 
200
- The first six take `(context, declaration)`; the other four are pure and take no context.
201
+ `routeAddressOf(context)` answers address questions against a context (one helper per context,
202
+ built on first use; `makeRouteAddressHelper(context)` builds a fresh one). `routeDeclarationHelper`
203
+ and `serviceRouteHelper` are pure and take no context.
201
204
 
202
205
  | Symbol | Kind | Purpose |
203
206
  |--------|------|---------|
204
- | `resolvePath(context, route)` | function | Every ancestor's segment, then this one |
205
- | `resolveMount(context, route)` | function | Service `base` + the full path |
206
- | `resolveService(context, route)` | function | The named service, else the default of the route's type, else the first of that type |
207
- | `resolveAddress(context, route)` | function | The `RouteAddress`; a hop over the service's internal host is never TLS |
208
- | `isLocalRoute(context, route)` | function | Does the route belong to the context's own service |
209
- | `getParentRoute(context, route)` | function | The parent declaration, with parentship-cycle detection |
210
- | `overrideParams(route, overrides?, filter?)` | function | Fill only blank fields of `route` from `overrides`; mutates, returns nothing |
211
- | `prependBase(route, path)` | function | `path` prefixed with `route.base` when set |
212
- | `isServiceRoute(obj?)` | type guard | Is `obj` a `CommonServiceRoute` with a known `AppType` |
213
- | `isServiceRouteResolved(route)` | type guard | Does the service entry name a host |
207
+ | `routeAddressOf(context).resolvePath(route)` | helper member | Every ancestor's segment, then this one |
208
+ | `routeAddressOf(context).resolveMount(route)` | helper member | Service `base` + the full path |
209
+ | `routeAddressOf(context).resolveService(route)` | helper member | The named service, else the default of the route's type, else the first of that type |
210
+ | `routeAddressOf(context).resolveAddress(route)` | helper member | The `RouteAddress`; a hop over the service's internal host is never TLS |
211
+ | `routeAddressOf(context).isLocalRoute(route)` | helper member | Does the route belong to the context's own service |
212
+ | `routeAddressOf(context).getParentRoute(route)` | helper member | The parent declaration, with parentship-cycle detection |
213
+ | `routeDeclarationHelper.overrideParams(route, overrides?, filter?)` | helper member | Fill only blank fields of `route` from `overrides`; mutates, returns nothing |
214
+ | `routeDeclarationHelper.prependBase(route, path)` | helper member | `path` prefixed with `route.base` when set |
215
+ | `serviceRouteHelper.isServiceRoute(obj?)` | type guard | Is `obj` a `CommonServiceRoute` with a known `AppType` |
216
+ | `serviceRouteHelper.isServiceRouteResolved(route)` | type guard | Does the service entry name a host |
214
217
  | `CreateRouteSignature<R>` | interface | Call signature shared by `route()` and `createRoute()` |
215
218
 
216
219
  ## Common pitfalls
@@ -224,8 +227,8 @@ The first six take `(context, declaration)`; the other four are pure and take no
224
227
  the chain.
225
228
  - Custom transport options are opaque here. Validate them through the package that owns the
226
229
  protocol.
227
- - Do not pass a context to the pure `./utils` helpers: `overrideParams(ctx, decl)` writes route
228
- fields into the context object instead of resolving anything.
230
+ - Do not pass a context to the pure `./utils` helpers: `routeDeclarationHelper.overrideParams(ctx, decl)`
231
+ writes route fields into the context object instead of resolving anything.
229
232
  - A mis-wired tree (no `cfg.services`, a service without a host, a parentship cycle) throws
230
233
  `SyntaxError` when an address is resolved. Fix the config; do not catch it.
231
234
  - `transportAlias` and `EntrypointTransport` live in `@owlmeans/entrypoint`, not here. A route only
@@ -250,7 +253,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
250
253
  your project's skill store (`.agents/skills/`):
251
254
 
252
255
  ```sh
253
- npx @owlmeans/agent-skills@^0.1.18-rc.48
256
+ npx @owlmeans/agent-skills@^0.1.18-rc.57
254
257
  ```
255
258
 
256
259
  The embedded files are version-matched to this package release. Do not edit them
@@ -1,8 +1,8 @@
1
1
  {
2
- "schemaVersion": 2,
2
+ "schemaVersion": 3,
3
3
  "package": "@owlmeans/route",
4
- "version": "0.1.18-rc.34",
5
- "generatedAt": "2026-10-05T08:32:19.129Z",
4
+ "version": "0.1.18-rc.36",
5
+ "generatedAt": "2026-10-20T23:19:58.639Z",
6
6
  "canonicalRepo": "https://github.com/owlmeans/common",
7
7
  "entries": [
8
8
  {
@@ -8,7 +8,7 @@ user-invocable: false
8
8
  # @owlmeans/route
9
9
 
10
10
  **Layer:** Core
11
- **Install:** `"@owlmeans/route": "^0.1.18-rc.34"` in `dependencies`
11
+ **Install:** `"@owlmeans/route": "^0.1.18-rc.36"` in `dependencies`
12
12
 
13
13
  ## Key Exports
14
14
 
@@ -1 +1 @@
1
- {"version":3,"file":"model.js","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAE3C,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,KAAuB,EAAc,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAA;AAElF,MAAM,CAAC,MAAM,WAAW,GAA2C,CAAC,KAAK,EAAE,IAAI,EAAE,IAAK,EAAE,EAAE;IACxF,MAAM,OAAO,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;IAClE,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,OAAO,IAAI,EAAE,CAAA;IACzC,MAAM,KAAK,GAAqB;QAC9B,KAAK;QACL,IAAI,EAAE,OAAO,CAAC,OAAO;QACrB,IAAI;QACJ,GAAG,IAAI;QACP,GAAG,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;KAC1F,CAAA;IAED,OAAO,KAAK,CAAA;AACd,CAAC,CAAA"}
1
+ {"version":3,"file":"model.js","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAE3C,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,KAAuB,EAAc,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAA;AAElF,MAAM,CAAC,MAAM,WAAW,GAA2C,CAAC,KAAK,EAAE,IAAI,EAAE,IAAK,EAAoB,EAAE;IAC1G,MAAM,OAAO,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;IAClE,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,OAAO,IAAI,EAAE,CAAA;IACzC,MAAM,KAAK,GAAqB;QAC9B,KAAK;QACL,IAAI,EAAE,OAAO,CAAC,OAAO;QACrB,IAAI;QACJ,GAAG,IAAI;QACP,GAAG,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;KAC1F,CAAA;IAED,OAAO,KAAK,CAAA;AACd,CAAC,CAAA"}
@@ -0,0 +1,9 @@
1
+ import type { RouteDeclaration } from '../../types.js';
2
+ /** Context-free edits and reads of one route declaration. */
3
+ export interface RouteDeclarationHelper {
4
+ /** Fills the declaration's unset fields from `overrides`, limited to `filter` keys when given. */
5
+ overrideParams: (route: RouteDeclaration, overrides?: Partial<RouteDeclaration>, filter?: string[]) => void;
6
+ /** `path` under the declaration's own `base`, when it has one. */
7
+ prependBase: (route: RouteDeclaration, path: string) => string;
8
+ }
9
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/utils/declaration/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAEtD,6DAA6D;AAC7D,MAAM,WAAW,sBAAsB;IACrC,kGAAkG;IAClG,cAAc,EAAE,CAAC,KAAK,EAAE,gBAAgB,EAAE,SAAS,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,IAAI,CAAA;IAC3G,kEAAkE;IAClE,WAAW,EAAE,CAAC,KAAK,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;CAC/D"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/utils/declaration/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,4 @@
1
+ import type { RouteDeclarationHelper } from './declaration/types.js';
2
+ export declare const createRouteDeclarationHelper: () => RouteDeclarationHelper;
3
+ export declare const routeDeclarationHelper: RouteDeclarationHelper;
4
+ //# sourceMappingURL=declaration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"declaration.d.ts","sourceRoot":"","sources":["../../src/utils/declaration.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAA;AAEpE,eAAO,MAAM,4BAA4B,QAAO,sBAgB/C,CAAA;AAED,eAAO,MAAM,sBAAsB,wBAAiC,CAAA"}
@@ -0,0 +1,18 @@
1
+ import { normalizePath } from '../helper.js';
2
+ import { SEP } from '../consts.js';
3
+ export const createRouteDeclarationHelper = () => {
4
+ const overrideParams = (route, overrides, filter) => {
5
+ Object.entries(overrides ?? {}).forEach(([key, value]) => {
6
+ if (route[key] == null
7
+ && (filter == null || filter.includes(key))) {
8
+ route[key] = value;
9
+ }
10
+ });
11
+ };
12
+ const prependBase = (route, path) => route.base != null && route.base.trim() !== ''
13
+ ? SEP + normalizePath(route.base) + SEP + normalizePath(path)
14
+ : path;
15
+ return { overrideParams, prependBase };
16
+ };
17
+ export const routeDeclarationHelper = createRouteDeclarationHelper();
18
+ //# sourceMappingURL=declaration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"declaration.js","sourceRoot":"","sources":["../../src/utils/declaration.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAA;AAGlC,MAAM,CAAC,MAAM,4BAA4B,GAAG,GAA2B,EAAE;IACvE,MAAM,cAAc,GAAG,CAAC,KAAuB,EAAE,SAAqC,EAAE,MAAiB,EAAQ,EAAE;QACjH,MAAM,CAAC,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;YACvD,IAAI,KAAK,CAAC,GAA6B,CAAC,IAAI,IAAI;mBAC3C,CAAC,MAAM,IAAI,IAAI,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;gBAC7C,KAAK,CAAC,GAA6B,CAA8C,GAAG,KAAK,CAAA;YAC5F,CAAC;QACH,CAAC,CAAC,CAAA;IACJ,CAAC,CAAA;IAED,MAAM,WAAW,GAAG,CAAC,KAAuB,EAAE,IAAY,EAAU,EAAE,CACpE,KAAK,CAAC,IAAI,IAAI,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE;QAC5C,CAAC,CAAC,GAAG,GAAG,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,GAAG,GAAG,aAAa,CAAC,IAAI,CAAC;QAC7D,CAAC,CAAC,IAAI,CAAA;IAEV,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,CAAA;AACxC,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,sBAAsB,GAAG,4BAA4B,EAAE,CAAA"}
@@ -1,4 +1,8 @@
1
1
  export * from './types.js';
2
+ export type * from './route/types.js';
3
+ export type * from './declaration/types.js';
4
+ export type * from './service/types.js';
2
5
  export * from './service.js';
3
6
  export * from './route.js';
7
+ export * from './declaration.js';
4
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AACA,cAAc,YAAY,CAAA;AAC1B,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AACA,cAAc,YAAY,CAAA;AAC1B,mBAAmB,kBAAkB,CAAA;AACrC,mBAAmB,wBAAwB,CAAA;AAC3C,mBAAmB,oBAAoB,CAAA;AACvC,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA;AAC1B,cAAc,kBAAkB,CAAA"}
@@ -1,4 +1,5 @@
1
1
  export * from './types.js';
2
2
  export * from './service.js';
3
3
  export * from './route.js';
4
+ export * from './declaration.js';
4
5
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AACA,cAAc,YAAY,CAAA;AAC1B,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AACA,cAAc,YAAY,CAAA;AAI1B,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA;AAC1B,cAAc,kBAAkB,CAAA"}
@@ -0,0 +1,35 @@
1
+ import type { RouteAddress, RouteDeclaration, ResolvedServiceRoute } from '../../types.js';
2
+ /**
3
+ * Where routes answer, worked out on demand against the context that asks — a declaration states
4
+ * only what it contributes and every address question walks the parent chain through the context.
5
+ */
6
+ export interface RouteAddressHelper {
7
+ /**
8
+ * The declaration of the route's parent entrypoint, or `null` for a root route.
9
+ *
10
+ * @throws {SyntaxError} when the parent provides no route or the parent chain is a cycle
11
+ */
12
+ getParentRoute: (route: RouteDeclaration) => RouteDeclaration | null;
13
+ /**
14
+ * Pick the service route this declaration answers on: the one it names, else the default of its
15
+ * app type, else the first of its app type.
16
+ *
17
+ * @throws {SyntaxError}
18
+ */
19
+ resolveService: (route: RouteDeclaration) => ResolvedServiceRoute;
20
+ /**
21
+ * The full path: every ancestor's declared segment, then this one. Walks the parent chain through
22
+ * the context, so a declaration always states only what it contributes.
23
+ */
24
+ resolvePath: (route: RouteDeclaration) => string;
25
+ /** `base` + the full path — what a server mounts and a client requests. */
26
+ resolveMount: (route: RouteDeclaration) => string;
27
+ /**
28
+ * Where the route answers. A backend caller reaching a service over its INTERNAL host is inside the
29
+ * cluster, so that hop is never TLS.
30
+ */
31
+ resolveAddress: (route: RouteDeclaration) => RouteAddress;
32
+ /** Does this route belong to the service the asking context IS? */
33
+ isLocalRoute: (route: RouteDeclaration) => boolean;
34
+ }
35
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/utils/route/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AAE1F;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,cAAc,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,gBAAgB,GAAG,IAAI,CAAA;IACpE;;;;;OAKG;IACH,cAAc,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,oBAAoB,CAAA;IACjE;;;OAGG;IACH,WAAW,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,MAAM,CAAA;IAChD,2EAA2E;IAC3E,YAAY,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,MAAM,CAAA;IACjD;;;OAGG;IACH,cAAc,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,YAAY,CAAA;IACzD,mEAAmE;IACnE,YAAY,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,OAAO,CAAA;CACnD"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/utils/route/types.ts"],"names":[],"mappings":""}
@@ -1,28 +1,6 @@
1
1
  import type { BasicContext } from '@owlmeans/context';
2
- import type { RouteAddress, RouteDeclaration, ResolvedServiceRoute } from '../types.js';
3
2
  import type { Config } from './types.local.js';
4
- export declare const getParentRoute: <C extends Config, T extends BasicContext<C>>(context: T, route: RouteDeclaration) => RouteDeclaration | null;
5
- export declare const overrideParams: (route: RouteDeclaration, overrides?: Partial<RouteDeclaration>, filter?: string[]) => void;
6
- /**
7
- * Pick the service route this declaration answers on: the one it names, else the default of its
8
- * app type, else the first of its app type.
9
- *
10
- * @throws {SyntaxError}
11
- */
12
- export declare const resolveService: <C extends Config, T extends BasicContext<C>>(context: T, route: RouteDeclaration) => ResolvedServiceRoute;
13
- /**
14
- * The full path: every ancestor's declared segment, then this one. Walks the parent chain through
15
- * the context, so a declaration always states only what it contributes.
16
- */
17
- export declare const resolvePath: <C extends Config, T extends BasicContext<C>>(context: T, route: RouteDeclaration) => string;
18
- /** `base` + the full path — what a server mounts and a client requests. */
19
- export declare const resolveMount: <C extends Config, T extends BasicContext<C>>(context: T, route: RouteDeclaration) => string;
20
- /**
21
- * Where the route answers. A backend caller reaching a service over its INTERNAL host is inside the
22
- * cluster, so that hop is never TLS.
23
- */
24
- export declare const resolveAddress: <C extends Config, T extends BasicContext<C>>(context: T, route: RouteDeclaration) => RouteAddress;
25
- /** Does this route belong to the service the asking context IS? */
26
- export declare const isLocalRoute: <C extends Config, T extends BasicContext<C>>(context: T, route: RouteDeclaration) => boolean;
27
- export declare const prependBase: (route: RouteDeclaration, path: string) => string;
3
+ import type { RouteAddressHelper } from './route/types.js';
4
+ export declare const makeRouteAddressHelper: <C extends Config, T extends BasicContext<C>>(context: T) => RouteAddressHelper;
5
+ export declare const routeAddressOf: <C extends Config, T extends BasicContext<C>>(target: T) => RouteAddressHelper;
28
6
  //# sourceMappingURL=route.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../src/utils/route.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAClE,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAsB,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAI3G,OAAO,KAAK,EAAE,MAAM,EAAgB,MAAM,kBAAkB,CAAA;AAK5D,eAAO,MAAM,cAAc,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,YAAY,CAAC,CAAC,CAAC,WAC/D,CAAC,SAAS,gBAAgB,KAClC,gBAAgB,GAAG,IAWrB,CAAA;AAED,eAAO,MAAM,cAAc,UAAW,gBAAgB,cAAc,OAAO,CAAC,gBAAgB,CAAC,WAAW,MAAM,EAAE,SAO/G,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,cAAc,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,YAAY,CAAC,CAAC,CAAC,WAC/D,CAAC,SAAS,gBAAgB,KAClC,oBA4BF,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,WAAW,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,YAAY,CAAC,CAAC,CAAC,WAC5D,CAAC,SAAS,gBAAgB,KAClC,MASF,CAAA;AAED,2EAA2E;AAC3E,eAAO,MAAM,YAAY,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,YAAY,CAAC,CAAC,CAAC,WAC7D,CAAC,SAAS,gBAAgB,KAClC,MAOF,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,cAAc,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,YAAY,CAAC,CAAC,CAAC,WAC/D,CAAC,SAAS,gBAAgB,KAClC,YAYF,CAAA;AAED,mEAAmE;AACnE,eAAO,MAAM,YAAY,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,YAAY,CAAC,CAAC,CAAC,WAC7D,CAAC,SAAS,gBAAgB,KAClC,OAAyE,CAAA;AAgB5E,eAAO,MAAM,WAAW,UAAW,gBAAgB,QAAQ,MAAM,WAGvD,CAAA"}
1
+ {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../src/utils/route.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAKrD,OAAO,KAAK,EAAE,MAAM,EAAgB,MAAM,kBAAkB,CAAA;AAC5D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAE1D,eAAO,MAAM,sBAAsB,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,YAAY,CAAC,CAAC,CAAC,WAAW,CAAC,KAAG,kBAmGhG,CAAA;AAED,eAAO,MAAM,cAAc,GArGY,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,YAAY,CAAC,CAAC,CAAC,kCAqGV,CAAA"}
@@ -1,104 +1,83 @@
1
- import { isServiceRoute, isServiceRouteResolved } from './service.js';
1
+ import { memoHelper } from '@owlmeans/context';
2
+ import { serviceRouteHelper } from './service.js';
2
3
  import { normalizePath } from '../helper.js';
3
4
  import { RouteProtocols, SEP } from '../consts.js';
4
- const carrier = (context, alias) => context.entrypoint(alias);
5
- export const getParentRoute = (context, route) => {
6
- if (route.parent == null) {
7
- return null;
8
- }
9
- const parent = carrier(context, route.parent);
10
- if (parent.route == null) {
11
- throw new SyntaxError('Parent entrypoint doesn\'t provide a route');
12
- }
13
- assertCycle(context, route, parent.route.route);
14
- return parent.route.route;
15
- };
16
- export const overrideParams = (route, overrides, filter) => {
17
- Object.entries(overrides ?? {}).forEach(([key, value]) => {
18
- if (route[key] == null
19
- && (filter == null || filter.includes(key))) {
20
- route[key] = value;
5
+ export const makeRouteAddressHelper = (context) => {
6
+ const { isServiceRoute, isServiceRouteResolved } = serviceRouteHelper;
7
+ const carrier = (alias) => context.entrypoint(alias);
8
+ const getParentRoute = (route) => {
9
+ if (route.parent == null) {
10
+ return null;
21
11
  }
22
- });
23
- };
24
- /**
25
- * Pick the service route this declaration answers on: the one it names, else the default of its
26
- * app type, else the first of its app type.
27
- *
28
- * @throws {SyntaxError}
29
- */
30
- export const resolveService = (context, route) => {
31
- if (context.cfg.services == null) {
32
- throw new SyntaxError('Services aren\'t configured to resolve routes');
33
- }
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');
37
- }
38
- const service = named?.type === route.type ? named
39
- : Object.values(context.cfg.services).find((candidate) => {
40
- const _candidate = candidate;
41
- return _candidate.default === true && _candidate.type === route.type;
42
- }) ?? Object.values(context.cfg.services).find((candidate) => candidate.type === route.type);
43
- if (!isServiceRoute(service)) {
44
- throw new SyntaxError('Service is not a valid service route');
45
- }
46
- if (!isServiceRouteResolved(service)) {
47
- throw new SyntaxError('Service route is not resolved');
48
- }
49
- return service;
50
- };
51
- /**
52
- * The full path: every ancestor's declared segment, then this one. Walks the parent chain through
53
- * the context, so a declaration always states only what it contributes.
54
- */
55
- export const resolvePath = (context, route) => {
56
- const parent = getParentRoute(context, route);
57
- if (parent == null) {
58
- return route.path;
59
- }
60
- const parentPath = resolvePath(context, parent);
61
- return (parentPath.startsWith(SEP) ? SEP : '')
62
- + normalizePath(normalizePath(parentPath) + SEP + normalizePath(route.path));
63
- };
64
- /** `base` + the full path — what a server mounts and a client requests. */
65
- export const resolveMount = (context, route) => {
66
- const path = resolvePath(context, route);
67
- const base = route.base ?? resolveService(context, route).base;
68
- return base != null && base.trim() !== ''
69
- ? SEP + normalizePath(base) + SEP + normalizePath(path)
70
- : path;
71
- };
72
- /**
73
- * Where the route answers. A backend caller reaching a service over its INTERNAL host is inside the
74
- * cluster, so that hop is never TLS.
75
- */
76
- export const resolveAddress = (context, route) => {
77
- const service = resolveService(context, route);
78
- const host = route.host ?? service.host;
79
- const internalHost = route.internalHost ?? service.internalHost;
80
- return {
81
- host,
82
- port: route.port ?? service.port,
83
- base: route.base ?? service.base,
84
- secure: internalHost != null && internalHost === host ? false : route.secure ?? true,
85
- protocol: route.protocol ?? RouteProtocols.WEB,
12
+ const parent = carrier(route.parent);
13
+ if (parent.route == null) {
14
+ throw new SyntaxError('Parent entrypoint doesn\'t provide a route');
15
+ }
16
+ assertCycle(route, parent.route.route);
17
+ return parent.route.route;
86
18
  };
87
- };
88
- /** Does this route belong to the service the asking context IS? */
89
- export const isLocalRoute = (context, route) => (route.service ?? context.cfg.service) === context.cfg.service;
90
- /**
91
- * @throws {SyntaxError}
92
- */
93
- const assertCycle = (context, route, parent) => {
94
- while (parent.parent != null) {
95
- if (parent.parent === route.alias) {
96
- throw new SyntaxError(`Route parentship cycle detected. Parent: ${parent.alias} has his child as ancestor ${route.alias}`);
19
+ const resolveService = (route) => {
20
+ if (context.cfg.services == null) {
21
+ throw new SyntaxError('Services aren\'t configured to resolve routes');
22
+ }
23
+ const named = context.cfg.services[route.service ?? context.cfg.service];
24
+ if (named != null && !isServiceRoute(named)) {
25
+ throw new SyntaxError('Service is not a valid service route');
26
+ }
27
+ const service = named?.type === route.type ? named
28
+ : Object.values(context.cfg.services).find((candidate) => {
29
+ const _candidate = candidate;
30
+ return _candidate.default === true && _candidate.type === route.type;
31
+ }) ?? Object.values(context.cfg.services).find((candidate) => candidate.type === route.type);
32
+ if (!isServiceRoute(service)) {
33
+ throw new SyntaxError('Service is not a valid service route');
97
34
  }
98
- parent = carrier(context, parent.parent).route.route;
99
- }
35
+ if (!isServiceRouteResolved(service)) {
36
+ throw new SyntaxError('Service route is not resolved');
37
+ }
38
+ return service;
39
+ };
40
+ const resolvePath = (route) => {
41
+ const parent = getParentRoute(route);
42
+ if (parent == null) {
43
+ return route.path;
44
+ }
45
+ const parentPath = resolvePath(parent);
46
+ return (parentPath.startsWith(SEP) ? SEP : '')
47
+ + normalizePath(normalizePath(parentPath) + SEP + normalizePath(route.path));
48
+ };
49
+ const resolveMount = (route) => {
50
+ const path = resolvePath(route);
51
+ const base = route.base ?? resolveService(route).base;
52
+ return base != null && base.trim() !== ''
53
+ ? SEP + normalizePath(base) + SEP + normalizePath(path)
54
+ : path;
55
+ };
56
+ const resolveAddress = (route) => {
57
+ const service = resolveService(route);
58
+ const host = route.host ?? service.host;
59
+ const internalHost = route.internalHost ?? service.internalHost;
60
+ return {
61
+ host,
62
+ port: route.port ?? service.port,
63
+ base: route.base ?? service.base,
64
+ secure: internalHost != null && internalHost === host ? false : route.secure ?? true,
65
+ protocol: route.protocol ?? RouteProtocols.WEB,
66
+ };
67
+ };
68
+ const isLocalRoute = (route) => (route.service ?? context.cfg.service) === context.cfg.service;
69
+ /**
70
+ * @throws {SyntaxError}
71
+ */
72
+ const assertCycle = (route, parent) => {
73
+ while (parent.parent != null) {
74
+ if (parent.parent === route.alias) {
75
+ throw new SyntaxError(`Route parentship cycle detected. Parent: ${parent.alias} has his child as ancestor ${route.alias}`);
76
+ }
77
+ parent = carrier(parent.parent).route.route;
78
+ }
79
+ };
80
+ return { getParentRoute, resolveService, resolvePath, resolveMount, resolveAddress, isLocalRoute };
100
81
  };
101
- export const prependBase = (route, path) => route.base != null && route.base.trim() !== ''
102
- ? SEP + normalizePath(route.base) + SEP + normalizePath(path)
103
- : path;
82
+ export const routeAddressOf = memoHelper.oncePer(makeRouteAddressHelper);
104
83
  //# sourceMappingURL=route.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"route.js","sourceRoot":"","sources":["../../src/utils/route.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"route.js","sourceRoot":"","sources":["../../src/utils/route.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { CommonServiceRoute, ResolvedServiceRoute } from '../../types.js';
2
+ /** Recognizes configured service routes. */
3
+ export interface ServiceRouteHelper {
4
+ /** Whether a configured value is a service route: it names a service and a known app type. */
5
+ isServiceRoute: (obj?: Object) => obj is CommonServiceRoute;
6
+ /** A service route is usable exactly when it knows the host it answers on. */
7
+ isServiceRouteResolved: (route: CommonServiceRoute) => route is ResolvedServiceRoute;
8
+ }
9
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/utils/service/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AAE9E,4CAA4C;AAC5C,MAAM,WAAW,kBAAkB;IACjC,8FAA8F;IAC9F,cAAc,EAAE,CAAC,GAAG,CAAC,EAAE,MAAM,KAAK,GAAG,IAAI,kBAAkB,CAAA;IAC3D,8EAA8E;IAC9E,sBAAsB,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,KAAK,IAAI,oBAAoB,CAAA;CACrF"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/utils/service/types.ts"],"names":[],"mappings":""}
@@ -1,5 +1,4 @@
1
- import type { ResolvedServiceRoute, CommonServiceRoute } from '../types.js';
2
- export declare const isServiceRoute: (obj?: Object) => obj is CommonServiceRoute;
3
- /** A service route is usable exactly when it knows the host it answers on. */
4
- export declare const isServiceRouteResolved: (route: CommonServiceRoute) => route is ResolvedServiceRoute;
1
+ import type { ServiceRouteHelper } from './service/types.js';
2
+ export declare const createServiceRouteHelper: () => ServiceRouteHelper;
3
+ export declare const serviceRouteHelper: ServiceRouteHelper;
5
4
  //# sourceMappingURL=service.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/utils/service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAE3E,eAAO,MAAM,cAAc,SAAU,MAAM,KAAG,GAAG,IAAI,kBAEM,CAAA;AAE3D,8EAA8E;AAC9E,eAAO,MAAM,sBAAsB,UAAW,kBAAkB,KAAG,KAAK,IAAI,oBAC5B,CAAA"}
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/utils/service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AAE5D,eAAO,MAAM,wBAAwB,QAAO,kBAS3C,CAAA;AAED,eAAO,MAAM,kBAAkB,oBAA6B,CAAA"}
@@ -1,6 +1,9 @@
1
1
  import { AppType } from '@owlmeans/context';
2
- export const isServiceRoute = (obj) => obj != null && ('type' in obj) && ('service' in obj)
3
- && (Object.values(AppType).includes(obj.type));
4
- /** A service route is usable exactly when it knows the host it answers on. */
5
- export const isServiceRouteResolved = (route) => route.host != null && route.host.trim() !== '';
2
+ export const createServiceRouteHelper = () => {
3
+ const isServiceRoute = (obj) => obj != null && ('type' in obj) && ('service' in obj)
4
+ && (Object.values(AppType).includes(obj.type));
5
+ const isServiceRouteResolved = (route) => route.host != null && route.host.trim() !== '';
6
+ return { isServiceRoute, isServiceRouteResolved };
7
+ };
8
+ export const serviceRouteHelper = createServiceRouteHelper();
6
9
  //# sourceMappingURL=service.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/utils/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAG3C,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,GAAY,EAA6B,EAAE,CACxE,GAAG,IAAI,IAAI,IAAI,CAAC,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,SAAS,IAAI,GAAG,CAAC;OACjD,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAe,CAAC,CAAC,CAAA;AAE3D,8EAA8E;AAC9E,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,KAAyB,EAAiC,EAAE,CACjG,KAAK,CAAC,IAAI,IAAI,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,CAAA"}
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/utils/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAI3C,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAuB,EAAE;IAC/D,MAAM,cAAc,GAAG,CAAC,GAAY,EAA6B,EAAE,CACjE,GAAG,IAAI,IAAI,IAAI,CAAC,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,SAAS,IAAI,GAAG,CAAC;WACjD,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAe,CAAC,CAAC,CAAA;IAE3D,MAAM,sBAAsB,GAAG,CAAC,KAAyB,EAAiC,EAAE,CAC1F,KAAK,CAAC,IAAI,IAAI,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,CAAA;IAEhD,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,CAAA;AACnD,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,kBAAkB,GAAG,wBAAwB,EAAE,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owlmeans/route",
3
- "version": "0.1.18-rc.34",
3
+ "version": "0.1.18-rc.36",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -28,7 +28,7 @@
28
28
  }
29
29
  },
30
30
  "dependencies": {
31
- "@owlmeans/context": "^0.1.18-rc.33"
31
+ "@owlmeans/context": "^0.1.18-rc.35"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@owlmeans/dep-config": "workspace:*",
package/src/model.ts CHANGED
@@ -4,7 +4,7 @@ import { AppType } from '@owlmeans/context'
4
4
 
5
5
  export const makeRouteModel = (route: RouteDeclaration): RouteModel => ({ route })
6
6
 
7
- export const createRoute: CreateRouteSignature<RouteDeclaration> = (alias, path, opts?) => {
7
+ export const createRoute: CreateRouteSignature<RouteDeclaration> = (alias, path, opts?): RouteDeclaration => {
8
8
  const options = typeof opts === 'string' ? { parent: opts } : opts
9
9
  const { parent, ...rest } = options ?? {}
10
10
  const route: RouteDeclaration = {
@@ -0,0 +1,9 @@
1
+ import type { RouteDeclaration } from '../../types.js'
2
+
3
+ /** Context-free edits and reads of one route declaration. */
4
+ export interface RouteDeclarationHelper {
5
+ /** Fills the declaration's unset fields from `overrides`, limited to `filter` keys when given. */
6
+ overrideParams: (route: RouteDeclaration, overrides?: Partial<RouteDeclaration>, filter?: string[]) => void
7
+ /** `path` under the declaration's own `base`, when it has one. */
8
+ prependBase: (route: RouteDeclaration, path: string) => string
9
+ }
@@ -0,0 +1,24 @@
1
+ import type { RouteDeclaration } from '../types.js'
2
+ import { normalizePath } from '../helper.js'
3
+ import { SEP } from '../consts.js'
4
+ import type { RouteDeclarationHelper } from './declaration/types.js'
5
+
6
+ export const createRouteDeclarationHelper = (): RouteDeclarationHelper => {
7
+ const overrideParams = (route: RouteDeclaration, overrides?: Partial<RouteDeclaration>, filter?: string[]): void => {
8
+ Object.entries(overrides ?? {}).forEach(([key, value]) => {
9
+ if (route[key as keyof RouteDeclaration] == null
10
+ && (filter == null || filter.includes(key))) {
11
+ (route[key as keyof RouteDeclaration] as RouteDeclaration[keyof RouteDeclaration]) = value
12
+ }
13
+ })
14
+ }
15
+
16
+ const prependBase = (route: RouteDeclaration, path: string): string =>
17
+ route.base != null && route.base.trim() !== ''
18
+ ? SEP + normalizePath(route.base) + SEP + normalizePath(path)
19
+ : path
20
+
21
+ return { overrideParams, prependBase }
22
+ }
23
+
24
+ export const routeDeclarationHelper = createRouteDeclarationHelper()
@@ -1,4 +1,8 @@
1
1
 
2
2
  export * from './types.js'
3
+ export type * from './route/types.js'
4
+ export type * from './declaration/types.js'
5
+ export type * from './service/types.js'
3
6
  export * from './service.js'
4
7
  export * from './route.js'
8
+ export * from './declaration.js'
@@ -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,144 +1,111 @@
1
- import type { BasicConfig, BasicContext } from '@owlmeans/context'
1
+ import { memoHelper } from '@owlmeans/context'
2
+ import type { BasicContext } from '@owlmeans/context'
2
3
  import type { RouteAddress, RouteDeclaration, CommonServiceRoute, ResolvedServiceRoute } from '../types.js'
3
- import { isServiceRoute, isServiceRouteResolved } from './service.js'
4
+ import { serviceRouteHelper } from './service.js'
4
5
  import { normalizePath } from '../helper.js'
5
6
  import { RouteProtocols, SEP } from '../consts.js'
6
7
  import type { Config, RouteCarrier } from './types.local.js'
8
+ import type { RouteAddressHelper } from './route/types.js'
7
9
 
8
- const carrier = <C extends Config, T extends BasicContext<C>>(context: T, alias: string): RouteCarrier =>
9
- context.entrypoint<RouteCarrier>(alias)
10
+ export const makeRouteAddressHelper = <C extends Config, T extends BasicContext<C>>(context: T): RouteAddressHelper => {
11
+ const { isServiceRoute, isServiceRouteResolved } = serviceRouteHelper
10
12
 
11
- export const getParentRoute = <C extends Config, T extends BasicContext<C>>(
12
- context: T, route: RouteDeclaration
13
- ): RouteDeclaration | null => {
14
- if (route.parent == null) {
15
- return null
16
- }
17
- const parent = carrier<C, T>(context, route.parent)
18
- if (parent.route == null) {
19
- throw new SyntaxError('Parent entrypoint doesn\'t provide a route')
20
- }
21
- assertCycle<C, T>(context, route, parent.route.route)
22
-
23
- return parent.route.route
24
- }
13
+ const carrier = (alias: string): RouteCarrier =>
14
+ context.entrypoint<RouteCarrier>(alias)
25
15
 
26
- export const overrideParams = (route: RouteDeclaration, overrides?: Partial<RouteDeclaration>, filter?: string[]) => {
27
- Object.entries(overrides ?? {}).forEach(([key, value]) => {
28
- if (route[key as keyof RouteDeclaration] == null
29
- && (filter == null || filter.includes(key))) {
30
- (route[key as keyof RouteDeclaration] as RouteDeclaration[keyof RouteDeclaration]) = value
16
+ const getParentRoute = (route: RouteDeclaration): RouteDeclaration | null => {
17
+ if (route.parent == null) {
18
+ return null
31
19
  }
32
- })
33
- }
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)
34
25
 
35
- /**
36
- * Pick the service route this declaration answers on: the one it names, else the default of its
37
- * app type, else the first of its app type.
38
- *
39
- * @throws {SyntaxError}
40
- */
41
- export const resolveService = <C extends Config, T extends BasicContext<C>>(
42
- context: T, route: RouteDeclaration
43
- ): ResolvedServiceRoute => {
44
- if (context.cfg.services == null) {
45
- throw new SyntaxError('Services aren\'t configured to resolve routes')
26
+ return parent.route.route
46
27
  }
47
28
 
48
- const named = context.cfg.services[route.service ?? context.cfg.service]
49
- if (named != null && !isServiceRoute(named)) {
50
- throw new SyntaxError('Service is not a valid service route')
51
- }
29
+ const resolveService = (route: RouteDeclaration): ResolvedServiceRoute => {
30
+ if (context.cfg.services == null) {
31
+ throw new SyntaxError('Services aren\'t configured to resolve routes')
32
+ }
52
33
 
53
- const service = named?.type === route.type ? named
54
- : Object.values(context.cfg.services).find<CommonServiceRoute>(
55
- (candidate): candidate is CommonServiceRoute => {
56
- const _candidate = candidate as CommonServiceRoute
57
- return _candidate.default === true && _candidate.type === route.type
58
- }
59
- ) ?? Object.values(context.cfg.services).find<CommonServiceRoute>(
60
- (candidate): candidate is CommonServiceRoute => (candidate as CommonServiceRoute).type === route.type
61
- )
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')
37
+ }
62
38
 
63
- if (!isServiceRoute(service)) {
64
- throw new SyntaxError('Service is not a valid service route')
65
- }
66
- if (!isServiceRouteResolved(service)) {
67
- throw new SyntaxError('Service route is not resolved')
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
+ }
55
+
56
+ return service
68
57
  }
69
58
 
70
- return service
71
- }
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)
72
65
 
73
- /**
74
- * The full path: every ancestor's declared segment, then this one. Walks the parent chain through
75
- * the context, so a declaration always states only what it contributes.
76
- */
77
- export const resolvePath = <C extends Config, T extends BasicContext<C>>(
78
- context: T, route: RouteDeclaration
79
- ): string => {
80
- const parent = getParentRoute<C, T>(context, route)
81
- if (parent == null) {
82
- return route.path
66
+ return (parentPath.startsWith(SEP) ? SEP : '')
67
+ + normalizePath(normalizePath(parentPath) + SEP + normalizePath(route.path))
83
68
  }
84
- const parentPath = resolvePath<C, T>(context, parent)
85
69
 
86
- return (parentPath.startsWith(SEP) ? SEP : '')
87
- + normalizePath(normalizePath(parentPath) + SEP + normalizePath(route.path))
88
- }
89
-
90
- /** `base` + the full path — what a server mounts and a client requests. */
91
- export const resolveMount = <C extends Config, T extends BasicContext<C>>(
92
- context: T, route: RouteDeclaration
93
- ): string => {
94
- const path = resolvePath<C, T>(context, route)
95
- const base = route.base ?? resolveService<C, T>(context, route).base
70
+ const resolveMount = (route: RouteDeclaration): string => {
71
+ const path = resolvePath(route)
72
+ const base = route.base ?? resolveService(route).base
96
73
 
97
- return base != null && base.trim() !== ''
98
- ? SEP + normalizePath(base) + SEP + normalizePath(path)
99
- : path
100
- }
74
+ return base != null && base.trim() !== ''
75
+ ? SEP + normalizePath(base) + SEP + normalizePath(path)
76
+ : path
77
+ }
101
78
 
102
- /**
103
- * Where the route answers. A backend caller reaching a service over its INTERNAL host is inside the
104
- * cluster, so that hop is never TLS.
105
- */
106
- export const resolveAddress = <C extends Config, T extends BasicContext<C>>(
107
- context: T, route: RouteDeclaration
108
- ): RouteAddress => {
109
- const service = resolveService<C, T>(context, route)
110
- const host = route.host ?? service.host
111
- const internalHost = route.internalHost ?? service.internalHost
112
-
113
- return {
114
- host,
115
- port: route.port ?? service.port,
116
- base: route.base ?? service.base,
117
- secure: internalHost != null && internalHost === host ? false : route.secure ?? true,
118
- 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
+ }
119
91
  }
120
- }
121
92
 
122
- /** Does this route belong to the service the asking context IS? */
123
- export const isLocalRoute = <C extends Config, T extends BasicContext<C>>(
124
- context: T, route: RouteDeclaration
125
- ): boolean => (route.service ?? context.cfg.service) === context.cfg.service
126
-
127
- /**
128
- * @throws {SyntaxError}
129
- */
130
- const assertCycle = <C extends BasicConfig, T extends BasicContext<C>>(
131
- context: T, route: RouteDeclaration, parent: RouteDeclaration
132
- ) => {
133
- while (parent.parent != null) {
134
- if (parent.parent === route.alias) {
135
- 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
136
105
  }
137
- parent = carrier<C, T>(context, parent.parent).route.route
138
106
  }
107
+
108
+ return { getParentRoute, resolveService, resolvePath, resolveMount, resolveAddress, isLocalRoute }
139
109
  }
140
110
 
141
- export const prependBase = (route: RouteDeclaration, path: string) =>
142
- route.base != null && route.base.trim() !== ''
143
- ? SEP + normalizePath(route.base) + SEP + normalizePath(path)
144
- : 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()