@toa.io/extensions.exposition 1.0.0-alpha.4 → 1.0.0-alpha.5

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 (73) hide show
  1. package/features/cors.feature +2 -2
  2. package/features/etag.feature +11 -1
  3. package/package.json +7 -7
  4. package/source/Context.ts +6 -4
  5. package/source/Directive.test.ts +4 -4
  6. package/source/Directive.ts +7 -33
  7. package/source/Endpoint.ts +41 -6
  8. package/source/Factory.ts +10 -4
  9. package/source/Gateway.ts +4 -29
  10. package/source/RTD/Context.ts +7 -10
  11. package/source/RTD/Directives.ts +28 -4
  12. package/source/RTD/Endpoint.ts +6 -4
  13. package/source/RTD/Match.ts +2 -7
  14. package/source/RTD/Method.ts +7 -13
  15. package/source/RTD/Node.ts +13 -14
  16. package/source/RTD/Tree.ts +17 -16
  17. package/source/RTD/factory.ts +2 -5
  18. package/source/directives/auth/Authorization.ts +2 -3
  19. package/source/directives/cache/Cache.ts +2 -2
  20. package/source/directives/cors/CORS.ts +13 -7
  21. package/source/directives/dev/Development.ts +3 -3
  22. package/source/directives/index.ts +2 -2
  23. package/source/directives/octets/Octets.ts +2 -3
  24. package/source/directives/octets/Store.ts +8 -2
  25. package/source/directives/vary/Vary.ts +2 -2
  26. package/source/directives/vary/embeddings/Header.ts +1 -1
  27. package/source/directives/vary/embeddings/Language.ts +1 -1
  28. package/transpiled/Context.d.ts +6 -4
  29. package/transpiled/Directive.d.ts +4 -17
  30. package/transpiled/Directive.js +0 -3
  31. package/transpiled/Directive.js.map +1 -1
  32. package/transpiled/Endpoint.d.ts +5 -3
  33. package/transpiled/Endpoint.js +29 -4
  34. package/transpiled/Endpoint.js.map +1 -1
  35. package/transpiled/Factory.js +5 -1
  36. package/transpiled/Factory.js.map +1 -1
  37. package/transpiled/Gateway.d.ts +1 -4
  38. package/transpiled/Gateway.js +1 -17
  39. package/transpiled/Gateway.js.map +1 -1
  40. package/transpiled/RTD/Context.d.ts +7 -6
  41. package/transpiled/RTD/Directives.d.ts +19 -4
  42. package/transpiled/RTD/Endpoint.d.ts +6 -4
  43. package/transpiled/RTD/Match.d.ts +2 -4
  44. package/transpiled/RTD/Method.d.ts +7 -7
  45. package/transpiled/RTD/Method.js.map +1 -1
  46. package/transpiled/RTD/Node.d.ts +4 -6
  47. package/transpiled/RTD/Node.js +2 -1
  48. package/transpiled/RTD/Node.js.map +1 -1
  49. package/transpiled/RTD/Tree.d.ts +6 -6
  50. package/transpiled/RTD/Tree.js +4 -1
  51. package/transpiled/RTD/Tree.js.map +1 -1
  52. package/transpiled/RTD/factory.d.ts +2 -4
  53. package/transpiled/RTD/factory.js.map +1 -1
  54. package/transpiled/directives/auth/Authorization.d.ts +2 -3
  55. package/transpiled/directives/auth/Authorization.js.map +1 -1
  56. package/transpiled/directives/cache/Cache.d.ts +2 -2
  57. package/transpiled/directives/cors/CORS.d.ts +2 -3
  58. package/transpiled/directives/cors/CORS.js +13 -7
  59. package/transpiled/directives/cors/CORS.js.map +1 -1
  60. package/transpiled/directives/dev/Development.d.ts +3 -3
  61. package/transpiled/directives/dev/Development.js.map +1 -1
  62. package/transpiled/directives/index.d.ts +2 -2
  63. package/transpiled/directives/index.js.map +1 -1
  64. package/transpiled/directives/octets/Octets.d.ts +2 -3
  65. package/transpiled/directives/octets/Octets.js.map +1 -1
  66. package/transpiled/directives/octets/Store.js +7 -2
  67. package/transpiled/directives/octets/Store.js.map +1 -1
  68. package/transpiled/directives/vary/Vary.d.ts +2 -2
  69. package/transpiled/directives/vary/embeddings/Header.js +1 -1
  70. package/transpiled/directives/vary/embeddings/Header.js.map +1 -1
  71. package/transpiled/directives/vary/embeddings/Language.js +1 -1
  72. package/transpiled/directives/vary/embeddings/Language.js.map +1 -1
  73. package/transpiled/tsconfig.tsbuildinfo +1 -1
@@ -19,10 +19,10 @@ Feature: CORS Support
19
19
  204 No Content
20
20
  access-control-allow-origin: https://hello.world
21
21
  access-control-allow-methods: GET, POST, PUT, PATCH, DELETE
22
- access-control-allow-headers: accept, authorization, content-type
22
+ access-control-allow-headers: accept, authorization, content-type, etag, if-match, if-none-match
23
23
  access-control-allow-credentials: true
24
24
  access-control-max-age: 3600
25
- cache-control: public, max-age=3600
25
+ cache-control: max-age=3600
26
26
  vary: origin
27
27
  """
28
28
  When the following request is received:
@@ -1,6 +1,6 @@
1
1
  Feature: Optimistic concurrency control
2
2
 
3
- Scenario: Receiving `etag`
3
+ Scenario: Using `etag`
4
4
  Given the `pots` is running with the following manifest:
5
5
  """yaml
6
6
  exposition:
@@ -35,6 +35,16 @@ Feature: Optimistic concurrency control
35
35
  200 OK
36
36
  etag: "1"
37
37
  """
38
+ When the following request is received:
39
+ """
40
+ GET /pots/${{ id }}/ HTTP/1.1
41
+ if-none-match: "1"
42
+ """
43
+ Then the following reply is sent:
44
+ """
45
+ 304 Not Modified
46
+ etag: "1"
47
+ """
38
48
  When the following request is received:
39
49
  """
40
50
  PUT /pots/${{ id }}/ HTTP/1.1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toa.io/extensions.exposition",
3
- "version": "1.0.0-alpha.4",
3
+ "version": "1.0.0-alpha.5",
4
4
  "description": "Toa Exposition",
5
5
  "author": "temich <tema.gurtovoy@gmail.com>",
6
6
  "homepage": "https://github.com/toa-io/toa#readme",
@@ -17,9 +17,9 @@
17
17
  "access": "public"
18
18
  },
19
19
  "dependencies": {
20
- "@toa.io/core": "1.0.0-alpha.4",
21
- "@toa.io/generic": "1.0.0-alpha.4",
22
- "@toa.io/schemas": "1.0.0-alpha.4",
20
+ "@toa.io/core": "1.0.0-alpha.5",
21
+ "@toa.io/generic": "1.0.0-alpha.5",
22
+ "@toa.io/schemas": "1.0.0-alpha.5",
23
23
  "bcryptjs": "2.4.3",
24
24
  "error-value": "0.3.0",
25
25
  "js-yaml": "4.1.0",
@@ -43,11 +43,11 @@
43
43
  "features": "cucumber-js"
44
44
  },
45
45
  "devDependencies": {
46
- "@toa.io/agent": "1.0.0-alpha.4",
47
- "@toa.io/extensions.storages": "1.0.0-alpha.4",
46
+ "@toa.io/agent": "1.0.0-alpha.5",
47
+ "@toa.io/extensions.storages": "1.0.0-alpha.5",
48
48
  "@types/bcryptjs": "2.4.3",
49
49
  "@types/cors": "2.8.13",
50
50
  "@types/negotiator": "0.6.1"
51
51
  },
52
- "gitHead": "80a91c0d9c167484247a91e69a0c0a3c344f90d0"
52
+ "gitHead": "1e4bb4ac28a6dddff1f4b8c5be7224fcdc47b847"
53
53
  }
package/source/Context.ts CHANGED
@@ -1,6 +1,8 @@
1
- import { type Endpoint } from './Endpoint'
2
- import { type Directives } from './Directive'
3
- import { type Branch } from './Branch'
4
1
  import type * as RTD from './RTD'
5
2
 
6
- export type Context = RTD.Context<Endpoint, Directives, Branch>
3
+ export type Context = RTD.Context<Extension>
4
+
5
+ interface Extension {
6
+ namespace: string
7
+ component: string
8
+ }
@@ -1,11 +1,11 @@
1
1
  import assert from 'node:assert'
2
2
  import { generate } from 'randomstring'
3
- import { DirectivesFactory, type Family } from './Directive'
4
- import { type syntax } from './RTD'
5
- import { type Remotes } from './Remotes'
3
+ import { DirectivesFactory } from './Directive'
4
+ import type { syntax, DirectiveFamily } from './RTD'
5
+ import type { Remotes } from './Remotes'
6
6
  import type { Context } from './HTTP'
7
7
 
8
- const families: Array<jest.MockedObjectDeep<Family>> = [
8
+ const families: Array<jest.MockedObjectDeep<DirectiveFamily>> = [
9
9
  {
10
10
  name: 'foo',
11
11
  mandatory: true,
@@ -3,10 +3,10 @@ import type { Remotes } from './Remotes'
3
3
  import type { Output } from './io'
4
4
  import type * as RTD from './RTD'
5
5
 
6
- export class Directives implements RTD.Directives<Directives> {
7
- private readonly sets: DirectiveSet[]
6
+ export class Directives implements RTD.Directives {
7
+ private readonly sets: RTD.DirectiveSet[]
8
8
 
9
- public constructor (sets: DirectiveSet[]) {
9
+ public constructor (sets: RTD.DirectiveSet[]) {
10
10
  this.sets = sets
11
11
  }
12
12
 
@@ -32,18 +32,14 @@ export class Directives implements RTD.Directives<Directives> {
32
32
  if (set.family.settle !== undefined)
33
33
  await set.family.settle(set.directives, context, response)
34
34
  }
35
-
36
- public merge (directives: Directives): void {
37
- this.sets.push(...directives.sets)
38
- }
39
35
  }
40
36
 
41
- export class DirectivesFactory implements RTD.DirectivesFactory<Directives> {
37
+ export class DirectivesFactory implements RTD.DirectiveFactory {
42
38
  private readonly remotes: Remotes
43
- private readonly families: Record<string, Family> = {}
39
+ private readonly families: Record<string, RTD.DirectiveFamily> = {}
44
40
  private readonly mandatory: string[] = []
45
41
 
46
- public constructor (families: Family[], remotes: Remotes) {
42
+ public constructor (families: RTD.DirectiveFamily[], remotes: Remotes) {
47
43
  for (const family of families) {
48
44
  this.families[family.name] = family
49
45
 
@@ -74,7 +70,7 @@ export class DirectivesFactory implements RTD.DirectivesFactory<Directives> {
74
70
  mandatory.delete(family.name)
75
71
  }
76
72
 
77
- const sets: DirectiveSet[] = []
73
+ const sets: RTD.DirectiveSet[] = []
78
74
 
79
75
  for (const family of mandatory)
80
76
  sets.push({
@@ -99,25 +95,3 @@ export const shortcuts: RTD.syntax.Shortcuts = new Map([
99
95
  ['rule', 'auth:rule'],
100
96
  ['incept', 'auth:incept']
101
97
  ])
102
-
103
- export interface Family<TDirective = any, TExtension = any> {
104
- readonly name: string
105
- readonly mandatory: boolean
106
-
107
- // produce: (declarations: RTD.syntax.Directive[], remotes: Remotes) => TDirective[]
108
-
109
- create: (name: string, value: any, remotes: Remotes) => TDirective
110
-
111
- preflight?: (directives: TDirective[],
112
- request: Context & TExtension,
113
- parameters: RTD.Parameter[]) => Output | Promise<Output>
114
-
115
- settle?: (directives: TDirective[],
116
- request: Context & TExtension,
117
- response: OutgoingMessage) => void | Promise<void>
118
- }
119
-
120
- interface DirectiveSet {
121
- family: Family
122
- directives: any[]
123
- }
@@ -5,7 +5,7 @@ import { type Context } from './Context'
5
5
  import * as http from './HTTP'
6
6
  import type * as RTD from './RTD'
7
7
 
8
- export class Endpoint implements RTD.Endpoint<Endpoint> {
8
+ export class Endpoint implements RTD.Endpoint {
9
9
  private readonly endpoint: string
10
10
  private readonly mapping: Mapping
11
11
  private readonly discovery: Promise<Component>
@@ -18,7 +18,9 @@ export class Endpoint implements RTD.Endpoint<Endpoint> {
18
18
  }
19
19
 
20
20
  public async call
21
- (body: any, query: http.Query, parameters: RTD.Parameter[]): Promise<http.OutgoingMessage> {
21
+ (context: http.Context, parameters: RTD.Parameter[]): Promise<http.OutgoingMessage> {
22
+ const body = await context.body()
23
+ const query = this.query(context)
22
24
  const request = this.mapping.fit(body, query, parameters)
23
25
 
24
26
  this.remote ??= await this.discovery
@@ -28,14 +30,26 @@ export class Endpoint implements RTD.Endpoint<Endpoint> {
28
30
  if (reply instanceof Error)
29
31
  throw new http.Conflict(reply)
30
32
 
31
- const message: http.OutgoingMessage = { body: reply }
33
+ const message: http.OutgoingMessage = {}
32
34
 
33
35
  if (typeof reply === 'object' && reply !== null && '_version' in reply) {
36
+ const etag = context.request.headers['if-none-match']
37
+
34
38
  message.headers ??= new Headers()
35
- message.headers.set('etag', `"${reply._version.toString()}"`)
36
- delete reply._version
39
+
40
+ if (etag !== undefined && reply._version === this.version(etag)) {
41
+ message.status = 304
42
+ message.headers.set('etag', etag)
43
+
44
+ return message
45
+ } else {
46
+ message.headers.set('etag', `"${reply._version.toString()}"`)
47
+ delete reply._version
48
+ }
37
49
  }
38
50
 
51
+ message.body = reply
52
+
39
53
  return message
40
54
  }
41
55
 
@@ -44,9 +58,28 @@ export class Endpoint implements RTD.Endpoint<Endpoint> {
44
58
 
45
59
  await this.remote.disconnect(INTERRUPT)
46
60
  }
61
+
62
+ private query (context: http.Context): http.Query {
63
+ const query: http.Query = Object.fromEntries(context.url.searchParams)
64
+ const etag = context.request.headers['if-match']
65
+
66
+ if (etag !== undefined)
67
+ query.version = this.version(etag)
68
+
69
+ return query
70
+ }
71
+
72
+ private version (etag: string): number {
73
+ const match = etag.match(ETAG)
74
+
75
+ if (match === null)
76
+ throw new http.BadRequest('Invalid ETag.')
77
+
78
+ return Number.parseInt(match.groups!.version)
79
+ }
47
80
  }
48
81
 
49
- export class EndpointsFactory implements RTD.EndpointsFactory<Endpoint> {
82
+ export class EndpointsFactory implements RTD.EndpointsFactory {
50
83
  private readonly remotes: Remotes
51
84
 
52
85
  public constructor (remotes: Remotes) {
@@ -70,4 +103,6 @@ export class EndpointsFactory implements RTD.EndpointsFactory<Endpoint> {
70
103
  }
71
104
  }
72
105
 
106
+ const ETAG = /^"(?<version>\d{1,32})"$/
107
+
73
108
  const INTERRUPT = true
package/source/Factory.ts CHANGED
@@ -3,9 +3,9 @@ import { Gateway } from './Gateway'
3
3
  import { Remotes } from './Remotes'
4
4
  import { Tree, syntax } from './RTD'
5
5
  import { Server } from './HTTP'
6
- import { type Endpoint, EndpointsFactory } from './Endpoint'
6
+ import { EndpointsFactory } from './Endpoint'
7
7
  import { families, interceptors } from './directives'
8
- import { type Directives, DirectivesFactory } from './Directive'
8
+ import { DirectivesFactory } from './Directive'
9
9
  import { Composition } from './Composition'
10
10
  import * as root from './root'
11
11
  import { Interception } from './Interception'
@@ -29,13 +29,19 @@ export class Factory implements extensions.Factory {
29
29
  const debug = process.env.TOA_EXPOSITION_DEBUG === '1'
30
30
  const trace = process.env.TOA_EXPOSITION_TRACE === '1'
31
31
  const broadcast: Broadcast = this.boot.bindings.broadcast(CHANNEL)
32
- const server = Server.create({ methods: syntax.verbs, debug, trace })
32
+
33
+ const server = Server.create({
34
+ methods: syntax.verbs,
35
+ debug,
36
+ trace
37
+ })
38
+
33
39
  const remotes = new Remotes(this.boot)
34
40
  const node = root.resolve()
35
41
  const methods = new EndpointsFactory(remotes)
36
42
  const directives = new DirectivesFactory(families, remotes)
37
43
  const interception = new Interception(interceptors)
38
- const tree = new Tree<Endpoint, Directives>(node, methods, directives)
44
+ const tree = new Tree(node, methods, directives)
39
45
 
40
46
  const composition = new Composition(this.boot)
41
47
  const gateway = new Gateway(broadcast, tree, interception)
package/source/Gateway.ts CHANGED
@@ -5,16 +5,13 @@ import type { Interception } from './Interception'
5
5
  import type { Method, Parameter, Tree } from './RTD'
6
6
  import type { Label } from './discovery'
7
7
  import type { Branch } from './Branch'
8
- import type { Endpoint } from './Endpoint'
9
- import type { Directives } from './Directive'
10
8
 
11
9
  export class Gateway extends Connector {
12
10
  private readonly broadcast: Broadcast
13
- private readonly tree: Tree<Endpoint, Directives>
11
+ private readonly tree: Tree
14
12
  private readonly interceptor: Interception
15
13
 
16
- // eslint-disable-next-line max-len
17
- public constructor (broadcast: Broadcast, tree: Tree<Endpoint, Directives>, interception: Interception) {
14
+ public constructor (broadcast: Broadcast, tree: Tree, interception: Interception) {
18
15
  super()
19
16
 
20
17
  this.broadcast = broadcast
@@ -67,8 +64,7 @@ export class Gateway extends Connector {
67
64
  console.info('Gateway is closed.')
68
65
  }
69
66
 
70
- private async call
71
- (method: Method<Endpoint, Directives>, context: http.Context, parameters: Parameter[]):
67
+ private async call (method: Method, context: http.Context, parameters: Parameter[]):
72
68
  Promise<http.OutgoingMessage> {
73
69
  if (context.url.pathname[context.url.pathname.length - 1] !== '/')
74
70
  throw new http.NotFound('Trailing slash is required.')
@@ -79,30 +75,11 @@ export class Gateway extends Connector {
79
75
  if (method.endpoint === null)
80
76
  throw new http.MethodNotAllowed()
81
77
 
82
- const body = await context.body()
83
- const query = this.query(context)
84
-
85
78
  return await method.endpoint
86
- .call(body, query, parameters)
79
+ .call(context, parameters)
87
80
  .catch(rethrow) as http.OutgoingMessage
88
81
  }
89
82
 
90
- private query (context: http.Context): http.Query {
91
- const query: http.Query = Object.fromEntries(context.url.searchParams)
92
- const etag = context.request.headers['if-match']
93
-
94
- if (etag !== undefined) {
95
- const match = etag.match(ETAG)
96
-
97
- if (match === null)
98
- throw new http.BadRequest('Invalid ETag.')
99
- else
100
- query.version = parseInt(match.groups!.version)
101
- }
102
-
103
- return query
104
- }
105
-
106
83
  private async discover (): Promise<void> {
107
84
  await this.broadcast.receive<Branch>('expose', this.merge.bind(this))
108
85
  await this.broadcast.transmit<null>('ping', null)
@@ -120,6 +97,4 @@ export class Gateway extends Connector {
120
97
  }
121
98
  }
122
99
 
123
- const ETAG = /^"(?<version>\d{1,32})"$/
124
-
125
100
  export type Broadcast = bindings.Broadcast<Label>
@@ -1,16 +1,13 @@
1
- import { type Directives, type DirectivesFactory } from './Directives'
2
- import { type Endpoint, type EndpointsFactory } from './Endpoint'
1
+ import { type DirectiveFactory } from './Directives'
2
+ import { type EndpointsFactory } from './Endpoint'
3
+ import type { Directive } from './syntax'
3
4
 
4
- export interface Context<
5
- TEndpoint extends Endpoint = any,
6
- TDirectives extends Directives<TDirectives> = any,
7
- TExtension = any
8
- > {
5
+ export interface Context<TExtension = any> {
9
6
  readonly protected: boolean
10
- readonly endpoints: EndpointsFactory<TEndpoint>
7
+ readonly endpoints: EndpointsFactory
11
8
  readonly directives: {
12
- readonly factory: DirectivesFactory
13
- stack: TDirectives[]
9
+ readonly factory: DirectiveFactory
10
+ stack: Directive[]
14
11
  }
15
12
  readonly extension?: TExtension
16
13
  }
@@ -1,9 +1,33 @@
1
+ import type { Parameter } from './Match'
1
2
  import type * as syntax from './syntax'
3
+ import type { Context, OutgoingMessage } from '../HTTP'
4
+ import type { Output } from '../io'
2
5
 
3
- export interface Directives<TDirective = any> {
4
- merge: (directive: TDirective) => void
6
+ export interface Directives {
7
+ preflight: (context: Context, parameters: Parameter[]) => Promise<Output>
8
+ settle: (context: Context, response: OutgoingMessage) => Promise<void>
5
9
  }
6
10
 
7
- export interface DirectivesFactory<T = any> {
8
- create: (directives: syntax.Directive[]) => T
11
+ export interface DirectiveFactory {
12
+ create: (directives: syntax.Directive[]) => Directives
13
+ }
14
+
15
+ export interface DirectiveSet {
16
+ family: DirectiveFamily
17
+ directives: any[]
18
+ }
19
+
20
+ export interface DirectiveFamily<TDirective = any, TExtension = any> {
21
+ readonly name: string
22
+ readonly mandatory: boolean
23
+
24
+ create: (name: string, ...rest: any[]) => TDirective
25
+
26
+ preflight?: (directives: TDirective[],
27
+ request: Context & TExtension,
28
+ parameters: Parameter[]) => Output | Promise<Output>
29
+
30
+ settle?: (directives: TDirective[],
31
+ request: Context & TExtension,
32
+ response: OutgoingMessage) => void | Promise<void>
9
33
  }
@@ -1,11 +1,13 @@
1
1
  import { type Context } from './Context'
2
+ import type * as http from '../HTTP'
2
3
  import type * as syntax from './syntax'
4
+ import type * as RTD from './index'
3
5
 
4
- export interface Endpoint<T extends Endpoint = any> {
5
- call: T['call']
6
+ export interface Endpoint {
7
+ call: (context: http.Context, parameters: RTD.Parameter[]) => Promise<http.OutgoingMessage>
6
8
  close: () => Promise<void>
7
9
  }
8
10
 
9
- export interface EndpointsFactory<T extends Endpoint<T> = any> {
10
- create: (method: syntax.Method, context: Context) => T
11
+ export interface EndpointsFactory {
12
+ create: (method: syntax.Method, context: Context) => Endpoint
11
13
  }
@@ -1,12 +1,7 @@
1
1
  import { type Node } from './Node'
2
- import { type Directives } from './Directives'
3
- import { type Endpoint } from './Endpoint'
4
2
 
5
- export interface Match<
6
- TEndpoint extends Endpoint<TEndpoint> = any,
7
- TDirectives extends Directives<TDirectives> = any
8
- > {
9
- node: Node<TEndpoint, TDirectives>
3
+ export interface Match {
4
+ node: Node
10
5
  parameters: Parameter[]
11
6
  }
12
7
 
@@ -1,14 +1,11 @@
1
- import { type Directives } from './Directives'
2
- import { type Endpoint } from './Endpoint'
1
+ import type { Endpoint } from './Endpoint'
2
+ import type { Directives } from './Directives'
3
3
 
4
- export class Method<
5
- TEndpoint extends Endpoint<TEndpoint> = any,
6
- TDirectives extends Directives<TDirectives> = any
7
- > {
8
- public readonly endpoint: TEndpoint | null
9
- public readonly directives: TDirectives
4
+ export class Method {
5
+ public readonly endpoint: Endpoint | null
6
+ public readonly directives: Directives
10
7
 
11
- public constructor (endpoint: TEndpoint | null, directives: TDirectives) {
8
+ public constructor (endpoint: Endpoint | null, directives: Directives) {
12
9
  this.endpoint = endpoint
13
10
  this.directives = directives
14
11
  }
@@ -18,7 +15,4 @@ export class Method<
18
15
  }
19
16
  }
20
17
 
21
- export type Methods<
22
- TEndpoint extends Endpoint<TEndpoint> = any,
23
- TDirectives extends Directives<TDirectives> = any
24
- > = Record<string, Method<TEndpoint, TDirectives>>
18
+ export type Methods = Record<string, Method>
@@ -1,20 +1,15 @@
1
1
  import { type Route } from './Route'
2
2
  import { type Methods } from './Method'
3
3
  import { type Match, type Parameter } from './Match'
4
- import { type Directives } from './Directives'
5
- import { type Endpoint } from './Endpoint'
6
4
 
7
- export class Node<
8
- TEndpoint extends Endpoint<TEndpoint> = any,
9
- TDirectives extends Directives<TDirectives> = any
10
- > {
5
+ export class Node {
11
6
  public intermediate: boolean
12
- public methods: Methods<TEndpoint, TDirectives>
7
+ public methods: Methods
13
8
  private readonly protected: boolean
14
9
  private routes: Route[]
15
10
 
16
11
  public constructor
17
- (routes: Route[], methods: Methods<TEndpoint, TDirectives>, properties: Properties) {
12
+ (routes: Route[], methods: Methods, properties: Properties) {
18
13
  this.routes = routes
19
14
  this.methods = methods
20
15
  this.protected = properties.protected
@@ -34,26 +29,30 @@ export class Node<
34
29
  return null
35
30
  }
36
31
 
37
- public merge (node: Node<TEndpoint, TDirectives>): void {
32
+ public merge (node: Node): void {
38
33
  this.intermediate = node.intermediate
39
34
 
40
- if (!this.protected) this.replace(node)
41
- else this.append(node)
35
+ if (!this.protected)
36
+ this.replace(node)
37
+ else
38
+ this.append(node)
42
39
 
43
40
  this.sort()
44
41
  }
45
42
 
46
- private replace (node: Node<TEndpoint, TDirectives>): void {
43
+ private replace (node: Node): void {
47
44
  const methods = Object.values(this.methods)
48
45
 
49
46
  this.routes = node.routes
50
47
  this.methods = node.methods
51
48
 
52
49
  for (const method of methods)
53
- void method.close() // race condition is really unlikely
50
+ void method.close()
51
+
52
+ // race condition is really unlikely
54
53
  }
55
54
 
56
- private append (node: Node<TEndpoint, TDirectives>): void {
55
+ private append (node: Node): void {
57
56
  for (const route of node.routes)
58
57
  this.mergeRoute(route)
59
58
 
@@ -1,32 +1,32 @@
1
- import { type Node } from './Node'
2
1
  import { createNode } from './factory'
3
2
  import { fragment } from './segment'
4
- import { type Match } from './Match'
5
- import { type Context } from './Context'
6
- import { type Directives, type DirectivesFactory } from './Directives'
7
- import { type Endpoint, type EndpointsFactory } from './Endpoint'
3
+ import type { Node } from './Node'
4
+ import type { Match } from './Match'
5
+ import type { Context } from './Context'
6
+ import type { DirectiveFactory } from './Directives'
7
+ import type { EndpointsFactory } from './Endpoint'
8
8
  import type * as syntax from './syntax'
9
9
 
10
- export class Tree<
11
- TEndpoint extends Endpoint<TEndpoint> = any,
12
- TDirectives extends Directives<TDirectives> = any
13
- > {
10
+ export class Tree {
14
11
  private readonly root: syntax.Node
15
- private readonly trunk: Node<TEndpoint, TDirectives>
16
- private readonly endpoints: EndpointsFactory<TEndpoint>
17
- private readonly directives: DirectivesFactory
12
+ private readonly trunk: Node
13
+ private readonly endpoints: EndpointsFactory
14
+ private readonly directives: DirectiveFactory
18
15
 
19
16
  public constructor
20
- (node: syntax.Node, endpoints: EndpointsFactory, directives: DirectivesFactory) {
17
+ (node: syntax.Node, endpoints: EndpointsFactory, directives: DirectiveFactory) {
21
18
  this.endpoints = endpoints
22
19
  this.directives = directives
23
20
  this.trunk = this.createNode(node, PROTECTED)
24
21
  this.root = node
25
22
  }
26
23
 
27
- public match (path: string): Match<TEndpoint, TDirectives> | null {
24
+ public match (path: string): Match | null {
28
25
  if (path === '/')
29
- return { node: this.trunk, parameters: [] }
26
+ return {
27
+ node: this.trunk,
28
+ parameters: []
29
+ }
30
30
 
31
31
  const fragments = fragment(path)
32
32
 
@@ -39,7 +39,8 @@ export class Tree<
39
39
  this.trunk.merge(branch)
40
40
  }
41
41
 
42
- private createNode (node: syntax.Node, protect: boolean, extension?: any): Node {
42
+ private createNode
43
+ (node: syntax.Node, protect: boolean, extension?: any): Node {
43
44
  const context: Context = {
44
45
  protected: protect,
45
46
  endpoints: this.endpoints,
@@ -1,14 +1,11 @@
1
1
  import { Node, type Properties } from './Node'
2
2
  import { Route } from './Route'
3
- import { type Context } from './Context'
4
3
  import { segment } from './segment'
5
4
  import { Method, type Methods } from './Method'
6
- import { type Endpoint } from './Endpoint'
7
- import { type Directives } from './Directives'
5
+ import type { Context } from './Context'
8
6
  import type * as syntax from './syntax'
9
7
 
10
- export function createNode<TEndpoint extends Endpoint, TDirectives extends Directives>
11
- (node: syntax.Node, context: Context): Node<TEndpoint, TDirectives> {
8
+ export function createNode (node: syntax.Node, context: Context): Node {
12
9
  if (node.isolated === true)
13
10
  context.directives.stack = node.directives
14
11
  else
@@ -13,8 +13,7 @@ import { PRIMARY, PROVIDERS } from './schemes'
13
13
  import type { Output } from '../../io'
14
14
  import type { Component } from '@toa.io/core'
15
15
  import type { Remotes } from '../../Remotes'
16
- import type { Family } from '../../Directive'
17
- import type { Parameter } from '../../RTD'
16
+ import type { Parameter, DirectiveFamily } from '../../RTD'
18
17
  import type {
19
18
  AuthenticationResult,
20
19
  Ban,
@@ -27,7 +26,7 @@ import type {
27
26
  Schemes
28
27
  } from './types'
29
28
 
30
- export class Authorization implements Family<Directive, Extension> {
29
+ export class Authorization implements DirectiveFamily<Directive, Extension> {
31
30
  public readonly depends: string[] = ['Vary']
32
31
  public readonly name: string = 'auth'
33
32
  public readonly mandatory: boolean = true
@@ -2,10 +2,10 @@ import { Control } from './Control'
2
2
  import { Exact } from './Exact'
3
3
  import type { Input, Output } from '../../io'
4
4
  import type { Directive } from './types'
5
- import type { Family } from '../../Directive'
5
+ import type { DirectiveFamily } from '../../RTD'
6
6
  import type * as http from '../../HTTP'
7
7
 
8
- export class Cache implements Family<Directive> {
8
+ export class Cache implements DirectiveFamily<Directive> {
9
9
  public readonly name: string = 'cache'
10
10
  public readonly mandatory: boolean = false
11
11