@toa.io/extensions.exposition 1.0.0-alpha.239 → 1.0.0-alpha.241

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toa.io/extensions.exposition",
3
- "version": "1.0.0-alpha.239",
3
+ "version": "1.0.0-alpha.241",
4
4
  "description": "Toa Exposition",
5
5
  "author": "temich <tema.gurtovoy@gmail.com>",
6
6
  "homepage": "https://github.com/toa-io/toa#readme",
@@ -64,5 +64,5 @@
64
64
  },
65
65
  "testEnvironment": "node"
66
66
  },
67
- "gitHead": "35935a713890e680fccbd23f77570da2bc12fd64"
67
+ "gitHead": "6aba7bc4701b32d2c85d8adf64736ec8301beca7"
68
68
  }
package/source/Gateway.ts CHANGED
@@ -5,7 +5,7 @@ import { type bindings, Connector } from '@toa.io/core'
5
5
  import * as http from './HTTP'
6
6
  import { rethrow } from './exceptions'
7
7
  import type { Interception } from './Interception'
8
- import type { Node, Method, Parameter, Tree, Match } from './RTD'
8
+ import type { Method, Node, Parameter, Tree, Match } from './RTD'
9
9
  import type { Label } from './discovery'
10
10
  import type { Branch } from './Branch'
11
11
 
@@ -13,8 +13,11 @@ export class Gateway extends Connector {
13
13
  private readonly broadcast: Broadcast
14
14
  private readonly tree: Tree
15
15
  private readonly interceptor: Interception
16
- private readonly merged = new Set<string>()
16
+ private readonly branches = new Map<string, Exposed>()
17
17
  private lastMerge = 0
18
+ private lastPing = 0
19
+ private stopped = false
20
+ private reconciliation: NodeJS.Timeout | null = null
18
21
  private resolveFirstMerge: (() => void) | null = null
19
22
 
20
23
  public constructor (broadcast: Broadcast, tree: Tree, interception: Interception) {
@@ -68,6 +71,11 @@ export class Gateway extends Connector {
68
71
  }
69
72
 
70
73
  protected override dispose (): void {
74
+ this.stopped = true
75
+
76
+ if (this.reconciliation !== null)
77
+ clearInterval(this.reconciliation)
78
+
71
79
  this.tree.dispose()
72
80
 
73
81
  console.info('Gateway is closed')
@@ -76,8 +84,12 @@ export class Gateway extends Connector {
76
84
  private match (context: http.Context): Match {
77
85
  const match = this.tree.match(context.url.pathname)
78
86
 
79
- if (match === null)
87
+ if (match === null) {
88
+ // the route may be missing because an expose has been lost
89
+ this.reping()
90
+
80
91
  throw new http.NotFound('Route not found')
92
+ }
81
93
 
82
94
  if (match.node.forward === null)
83
95
  return match
@@ -127,8 +139,47 @@ export class Gateway extends Connector {
127
139
  })
128
140
 
129
141
  await this.broadcast.receive<Branch>('expose', this.merge.bind(this))
130
- await this.broadcast.transmit<null>('ping', null)
142
+
143
+ void this.knock()
144
+
131
145
  await this.settled(first)
146
+
147
+ this.reconciliation = setInterval(() => void this.ping(), RECONCILE_INTERVAL)
148
+ this.reconciliation.unref()
149
+ }
150
+
151
+ /**
152
+ * A single ping is enough only if every tenant is listening by then, which is
153
+ * not the case while the deployment is still rolling out.
154
+ */
155
+ private async knock (): Promise<void> {
156
+ for (const delay of KNOCK_DELAYS) {
157
+ if (this.stopped)
158
+ return
159
+
160
+ if (delay > 0)
161
+ await setTimeout(delay, undefined, { ref: false })
162
+
163
+ await this.ping()
164
+ }
165
+ }
166
+
167
+ private async ping (): Promise<void> {
168
+ if (this.stopped)
169
+ return
170
+
171
+ this.lastPing = Date.now()
172
+
173
+ await this.broadcast.transmit<null>('ping', null)
174
+ .catch((exception: Error) => console.error('Discovery ping failed',
175
+ { message: exception.message }))
176
+ }
177
+
178
+ private reping (): void {
179
+ if (Date.now() - this.lastPing < PING_COOLDOWN)
180
+ return
181
+
182
+ void this.ping()
132
183
  }
133
184
 
134
185
  private async settled (first: Promise<void>): Promise<void> {
@@ -151,7 +202,7 @@ export class Gateway extends Connector {
151
202
  }
152
203
 
153
204
  private merge (branch: Branch): void {
154
- const id = branch.namespace + '.' + branch.component + '@' + branch.version
205
+ const id = branch.namespace + '.' + branch.component
155
206
 
156
207
  const attributes = {
157
208
  namespace: branch.namespace,
@@ -159,23 +210,29 @@ export class Gateway extends Connector {
159
210
  version: branch.version
160
211
  }
161
212
 
162
- try {
163
- this.tree.merge(branch.node, branch)
164
- } catch (exception: unknown) {
165
- const message = exception instanceof Error ? exception.message : 'Unknown error'
213
+ const exposed = this.branches.get(id)
166
214
 
167
- console.error('Branch merge exception', { message, ...attributes })
215
+ // rebuilding an identical branch would only tear down its live endpoints
216
+ if (exposed?.version === branch.version) {
217
+ this.tree.refresh(exposed.nodes)
218
+ console.debug('Branch refreshed', attributes)
168
219
 
169
220
  return
170
221
  }
171
222
 
172
- if (this.merged.has(id)) {
173
- console.debug('Branch refreshed', attributes)
223
+ let nodes: Node[]
224
+
225
+ try {
226
+ nodes = this.tree.merge(branch.node, branch)
227
+ } catch (exception: unknown) {
228
+ const message = exception instanceof Error ? exception.message : 'Unknown error'
229
+
230
+ console.error('Branch merge exception', { message, ...attributes })
174
231
 
175
232
  return
176
233
  }
177
234
 
178
- this.merged.add(id)
235
+ this.branches.set(id, { version: branch.version, nodes })
179
236
  this.lastMerge = Date.now()
180
237
  this.resolveFirstMerge?.()
181
238
  this.resolveFirstMerge = null
@@ -186,6 +243,14 @@ export class Gateway extends Connector {
186
243
 
187
244
  export type Broadcast = bindings.Broadcast<Label>
188
245
 
246
+ interface Exposed {
247
+ version: string
248
+ nodes: Node[]
249
+ }
250
+
189
251
  const SETTLE_QUIET = 10_000
190
252
  const SETTLE_TIMEOUT = 30_000
191
253
  const SETTLE_POLL = 50
254
+ const KNOCK_DELAYS = [0, 500, 1000, 1500]
255
+ const RECONCILE_INTERVAL = 30_000
256
+ const PING_COOLDOWN = 5_000
@@ -33,15 +33,23 @@ export class Node {
33
33
  return null
34
34
  }
35
35
 
36
- public merge (node: Node): void {
36
+ /**
37
+ * Returns the nodes the merged branch has landed on, so that its expiration
38
+ * can later be extended without rebuilding anything.
39
+ */
40
+ public merge (node: Node): Node[] {
37
41
  this.intermediate = node.intermediate
38
42
 
39
- if (this.protected)
40
- this.append(node)
41
- else
42
- this.replace(node)
43
+ const nodes = this.protected ? this.append(node) : this.replace(node)
43
44
 
44
45
  this.sort()
46
+
47
+ return nodes
48
+ }
49
+
50
+ public touch (expiration: number): void {
51
+ if (!this.protected)
52
+ this.expiration = expiration
45
53
  }
46
54
 
47
55
  public async explain (parameters: Parameter[]): Promise<Record<string, unknown>> {
@@ -56,7 +64,7 @@ export class Node {
56
64
  return methods
57
65
  }
58
66
 
59
- private replace (node: Node): void {
67
+ private replace (node: Node): Node[] {
60
68
  const methods = Object.values(this.methods)
61
69
 
62
70
  this.routes = node.routes
@@ -67,25 +75,39 @@ export class Node {
67
75
  // race condition is really unlikely
68
76
  for (const method of methods)
69
77
  void method.close()
78
+
79
+ return this.nodes()
70
80
  }
71
81
 
72
- private append (node: Node): void {
82
+ private append (node: Node): Node[] {
83
+ const nodes: Node[] = []
84
+
73
85
  for (const route of node.routes)
74
- this.route(route)
86
+ nodes.push(...this.route(route))
75
87
 
76
88
  for (const [verb, method] of Object.entries(node.methods))
77
89
  this.methods[verb] = method
90
+
91
+ return nodes
78
92
  }
79
93
 
80
- private route (candidate: Route): void {
94
+ private route (candidate: Route): Node[] {
81
95
  for (const route of this.routes)
82
- if (candidate.equals(route)) {
83
- route.merge(candidate)
84
-
85
- return
86
- }
96
+ if (candidate.equals(route))
97
+ return route.merge(candidate)
87
98
 
88
99
  this.routes.push(candidate)
100
+
101
+ return candidate.node.nodes()
102
+ }
103
+
104
+ private nodes (): Node[] {
105
+ const nodes: Node[] = [this]
106
+
107
+ for (const route of this.routes)
108
+ nodes.push(...route.node.nodes())
109
+
110
+ return nodes
89
111
  }
90
112
 
91
113
  private sort (): void {
@@ -1,12 +1,12 @@
1
- import { type Node } from './Node'
2
1
  import { type Segment } from './segment'
3
2
  import { type Match, type Parameter } from './Match'
3
+ import type { Node } from './Node'
4
4
 
5
5
  export class Route {
6
6
  public readonly root: boolean
7
7
  public readonly variables: number = 0
8
8
  public readonly segments: Segment[]
9
- private readonly node: Node
9
+ public readonly node: Node
10
10
  private readonly wildcard: boolean = false
11
11
 
12
12
  public constructor (segments: Segment[], node: Node) {
@@ -57,8 +57,8 @@ export class Route {
57
57
  return true
58
58
  }
59
59
 
60
- public merge (route: Route): void {
61
- this.node.merge(route.node)
60
+ public merge (route: Route): Node[] {
61
+ return this.node.merge(route.node)
62
62
  }
63
63
 
64
64
  private matchNested (fragments: string[], parameters: Parameter[]): Match | null {
@@ -1,4 +1,4 @@
1
- import { createNode } from './factory'
1
+ import { branchTTL, createNode } from './factory'
2
2
  import { fragment } from './segment'
3
3
  import type { Node } from './Node'
4
4
  import type { Match } from './Match'
@@ -32,10 +32,21 @@ export class Tree {
32
32
  return this.trunk.match(fragments)
33
33
  }
34
34
 
35
- public merge (node: syntax.Node, extension: unknown): void {
35
+ public merge (node: syntax.Node, extension: unknown): Node[] {
36
36
  const branch = this.createNode(node, !PROTECTED, extension)
37
37
 
38
- this.trunk.merge(branch)
38
+ return this.trunk.merge(branch)
39
+ }
40
+
41
+ /**
42
+ * Extends the expiration of an already merged branch, leaving its endpoints
43
+ * and their remotes intact.
44
+ */
45
+ public refresh (nodes: Node[]): void {
46
+ const expiration = Date.now() + branchTTL()
47
+
48
+ for (const node of nodes)
49
+ node.touch(expiration)
39
50
  }
40
51
 
41
52
  public dispose (): void {
@@ -35,6 +35,30 @@ const pots: syntax.Node = {
35
35
  directives: []
36
36
  }
37
37
 
38
+ it('should extend expiration without rebuilding the branch', () => {
39
+ process.env.__TESTING_EXPOSITION_BRANCH_TTL = '1000'
40
+
41
+ const now = jest.spyOn(Date, 'now').mockReturnValue(10_000)
42
+ const tree = new Tree(root, endpoints, directives)
43
+ const nodes = tree.merge(pots, { namespace: 'default', component: 'pots' })
44
+ const match = tree.match('/pots/')
45
+
46
+ expect(match).not.toBeNull()
47
+
48
+ now.mockReturnValue(10_900)
49
+ tree.refresh(nodes)
50
+
51
+ now.mockReturnValue(11_500)
52
+
53
+ const refreshed = tree.match('/pots/')
54
+
55
+ expect(refreshed).not.toBeNull()
56
+ expect(refreshed?.node).toBe(match?.node)
57
+
58
+ now.mockRestore()
59
+ delete process.env.__TESTING_EXPOSITION_BRANCH_TTL
60
+ })
61
+
38
62
  it('should ignore expired nodes during match', () => {
39
63
  process.env.__TESTING_EXPOSITION_BRANCH_TTL = '1000'
40
64
 
@@ -29,7 +29,7 @@ export function createNode (node: syntax.Node, context: Context): Node {
29
29
  return new Node(routes, methods, properties)
30
30
  }
31
31
 
32
- function branchTTL (): number {
32
+ export function branchTTL (): number {
33
33
  const value = process.env.__TESTING_EXPOSITION_BRANCH_TTL
34
34
 
35
35
  if (value === undefined || value === '')
package/source/Tenant.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import { setTimeout } from 'node:timers/promises'
2
2
  import { Connector } from '@toa.io/core'
3
- import { BRANCH_TTL } from './const'
4
3
  import type { bindings } from '@toa.io/core'
5
4
  import type { Label } from './discovery'
6
5
  import type { Branch } from './Branch'
@@ -56,7 +55,9 @@ function exposeInterval (uptime: number): number {
56
55
  }
57
56
 
58
57
  const EXPOSE_MIN = 5_000
59
- const EXPOSE_MAX = Math.round(BRANCH_TTL / 2.1)
58
+
59
+ // a refresh no longer rebuilds the branch, so exposing often is cheap
60
+ const EXPOSE_MAX = 120_000
60
61
  const EXPOSE_TAU = 900_000
61
62
 
62
63
  type Broadcast = bindings.Broadcast<Label>
@@ -7,8 +7,11 @@ export declare class Gateway extends Connector {
7
7
  private readonly broadcast;
8
8
  private readonly tree;
9
9
  private readonly interceptor;
10
- private readonly merged;
10
+ private readonly branches;
11
11
  private lastMerge;
12
+ private lastPing;
13
+ private stopped;
14
+ private reconciliation;
12
15
  private resolveFirstMerge;
13
16
  constructor(broadcast: Broadcast, tree: Tree, interception: Interception);
14
17
  process(context: http.Context): Promise<http.OutgoingMessage>;
@@ -18,6 +21,13 @@ export declare class Gateway extends Connector {
18
21
  private call;
19
22
  private explain;
20
23
  private discover;
24
+ /**
25
+ * A single ping is enough only if every tenant is listening by then, which is
26
+ * not the case while the deployment is still rolling out.
27
+ */
28
+ private knock;
29
+ private ping;
30
+ private reping;
21
31
  private settled;
22
32
  private merge;
23
33
  }
@@ -37,8 +37,11 @@ class Gateway extends core_1.Connector {
37
37
  broadcast;
38
38
  tree;
39
39
  interceptor;
40
- merged = new Set();
40
+ branches = new Map();
41
41
  lastMerge = 0;
42
+ lastPing = 0;
43
+ stopped = false;
44
+ reconciliation = null;
42
45
  resolveFirstMerge = null;
43
46
  constructor(broadcast, tree, interception) {
44
47
  super();
@@ -71,13 +74,19 @@ class Gateway extends core_1.Connector {
71
74
  openspan_1.console.info('Gateway started');
72
75
  }
73
76
  dispose() {
77
+ this.stopped = true;
78
+ if (this.reconciliation !== null)
79
+ clearInterval(this.reconciliation);
74
80
  this.tree.dispose();
75
81
  openspan_1.console.info('Gateway is closed');
76
82
  }
77
83
  match(context) {
78
84
  const match = this.tree.match(context.url.pathname);
79
- if (match === null)
85
+ if (match === null) {
86
+ // the route may be missing because an expose has been lost
87
+ this.reping();
80
88
  throw new http.NotFound('Route not found');
89
+ }
81
90
  if (match.node.forward === null)
82
91
  return match;
83
92
  const destination = match.node.forward.replace(/\/:([^/]+)/g, (_, name) => {
@@ -111,8 +120,35 @@ class Gateway extends core_1.Connector {
111
120
  this.resolveFirstMerge = resolve;
112
121
  });
113
122
  await this.broadcast.receive('expose', this.merge.bind(this));
114
- await this.broadcast.transmit('ping', null);
123
+ void this.knock();
115
124
  await this.settled(first);
125
+ this.reconciliation = setInterval(() => void this.ping(), RECONCILE_INTERVAL);
126
+ this.reconciliation.unref();
127
+ }
128
+ /**
129
+ * A single ping is enough only if every tenant is listening by then, which is
130
+ * not the case while the deployment is still rolling out.
131
+ */
132
+ async knock() {
133
+ for (const delay of KNOCK_DELAYS) {
134
+ if (this.stopped)
135
+ return;
136
+ if (delay > 0)
137
+ await (0, promises_1.setTimeout)(delay, undefined, { ref: false });
138
+ await this.ping();
139
+ }
140
+ }
141
+ async ping() {
142
+ if (this.stopped)
143
+ return;
144
+ this.lastPing = Date.now();
145
+ await this.broadcast.transmit('ping', null)
146
+ .catch((exception) => openspan_1.console.error('Discovery ping failed', { message: exception.message }));
147
+ }
148
+ reping() {
149
+ if (Date.now() - this.lastPing < PING_COOLDOWN)
150
+ return;
151
+ void this.ping();
116
152
  }
117
153
  async settled(first) {
118
154
  const deadline = Date.now() + SETTLE_TIMEOUT;
@@ -128,25 +164,29 @@ class Gateway extends core_1.Connector {
128
164
  }
129
165
  }
130
166
  merge(branch) {
131
- const id = branch.namespace + '.' + branch.component + '@' + branch.version;
167
+ const id = branch.namespace + '.' + branch.component;
132
168
  const attributes = {
133
169
  namespace: branch.namespace,
134
170
  component: branch.component,
135
171
  version: branch.version
136
172
  };
173
+ const exposed = this.branches.get(id);
174
+ // rebuilding an identical branch would only tear down its live endpoints
175
+ if (exposed?.version === branch.version) {
176
+ this.tree.refresh(exposed.nodes);
177
+ openspan_1.console.debug('Branch refreshed', attributes);
178
+ return;
179
+ }
180
+ let nodes;
137
181
  try {
138
- this.tree.merge(branch.node, branch);
182
+ nodes = this.tree.merge(branch.node, branch);
139
183
  }
140
184
  catch (exception) {
141
185
  const message = exception instanceof Error ? exception.message : 'Unknown error';
142
186
  openspan_1.console.error('Branch merge exception', { message, ...attributes });
143
187
  return;
144
188
  }
145
- if (this.merged.has(id)) {
146
- openspan_1.console.debug('Branch refreshed', attributes);
147
- return;
148
- }
149
- this.merged.add(id);
189
+ this.branches.set(id, { version: branch.version, nodes });
150
190
  this.lastMerge = Date.now();
151
191
  this.resolveFirstMerge?.();
152
192
  this.resolveFirstMerge = null;
@@ -157,4 +197,7 @@ exports.Gateway = Gateway;
157
197
  const SETTLE_QUIET = 10_000;
158
198
  const SETTLE_TIMEOUT = 30_000;
159
199
  const SETTLE_POLL = 50;
200
+ const KNOCK_DELAYS = [0, 500, 1000, 1500];
201
+ const RECONCILE_INTERVAL = 30_000;
202
+ const PING_COOLDOWN = 5_000;
160
203
  //# sourceMappingURL=Gateway.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Gateway.js","sourceRoot":"","sources":["../source/Gateway.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Gateway.js","sourceRoot":"","sources":["../source/Gateway.ts"],"names":[],"mappings":"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"}
@@ -10,11 +10,17 @@ export declare class Node {
10
10
  private routes;
11
11
  constructor(routes: Route[], methods: Methods, properties: Properties);
12
12
  match(fragments: string[], parameters?: Parameter[]): Match | null;
13
- merge(node: Node): void;
13
+ /**
14
+ * Returns the nodes the merged branch has landed on, so that its expiration
15
+ * can later be extended without rebuilding anything.
16
+ */
17
+ merge(node: Node): Node[];
18
+ touch(expiration: number): void;
14
19
  explain(parameters: Parameter[]): Promise<Record<string, unknown>>;
15
20
  private replace;
16
21
  private append;
17
22
  private route;
23
+ private nodes;
18
24
  private sort;
19
25
  }
20
26
  export interface Properties {
@@ -26,13 +26,19 @@ class Node {
26
26
  }
27
27
  return null;
28
28
  }
29
+ /**
30
+ * Returns the nodes the merged branch has landed on, so that its expiration
31
+ * can later be extended without rebuilding anything.
32
+ */
29
33
  merge(node) {
30
34
  this.intermediate = node.intermediate;
31
- if (this.protected)
32
- this.append(node);
33
- else
34
- this.replace(node);
35
+ const nodes = this.protected ? this.append(node) : this.replace(node);
35
36
  this.sort();
37
+ return nodes;
38
+ }
39
+ touch(expiration) {
40
+ if (!this.protected)
41
+ this.expiration = expiration;
36
42
  }
37
43
  async explain(parameters) {
38
44
  const methods = {};
@@ -50,20 +56,28 @@ class Node {
50
56
  // race condition is really unlikely
51
57
  for (const method of methods)
52
58
  void method.close();
59
+ return this.nodes();
53
60
  }
54
61
  append(node) {
62
+ const nodes = [];
55
63
  for (const route of node.routes)
56
- this.route(route);
64
+ nodes.push(...this.route(route));
57
65
  for (const [verb, method] of Object.entries(node.methods))
58
66
  this.methods[verb] = method;
67
+ return nodes;
59
68
  }
60
69
  route(candidate) {
61
70
  for (const route of this.routes)
62
- if (candidate.equals(route)) {
63
- route.merge(candidate);
64
- return;
65
- }
71
+ if (candidate.equals(route))
72
+ return route.merge(candidate);
66
73
  this.routes.push(candidate);
74
+ return candidate.node.nodes();
75
+ }
76
+ nodes() {
77
+ const nodes = [this];
78
+ for (const route of this.routes)
79
+ nodes.push(...route.node.nodes());
80
+ return nodes;
67
81
  }
68
82
  sort() {
69
83
  this.routes.sort((a, b) => {
@@ -1 +1 @@
1
- {"version":3,"file":"Node.js","sourceRoot":"","sources":["../../source/RTD/Node.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Node.js","sourceRoot":"","sources":["../../source/RTD/Node.ts"],"names":[],"mappings":";;;AAIA,MAAa,IAAI;IACR,YAAY,CAAS;IACrB,OAAO,CAAe;IACtB,UAAU,CAAQ;IAClB,OAAO,CAAS;IACN,SAAS,CAAS;IAC3B,MAAM,CAAS;IAEvB,YAAoB,MAAe,EAAE,OAAgB,EAAE,UAAsB;QAC3E,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;QACpB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;QACtB,IAAI,CAAC,SAAS,GAAG,UAAU,CAAC,SAAS,CAAA;QACrC,IAAI,CAAC,OAAO,GAAG,UAAU,CAAC,OAAO,IAAI,IAAI,CAAA;QACzC,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC,UAAU,IAAI,QAAQ,CAAA;QACnD,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAA;QAEvE,IAAI,CAAC,IAAI,EAAE,CAAA;IACb,CAAC;IAEM,KAAK,CAAE,SAAmB,EAAE,aAA0B,EAAE;QAC7D,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChC,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,EAAE,CAAA;YACjC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,SAAS,EAAE,MAAM,CAAC,CAAA;YAE5C,IAAI,KAAK,KAAK,IAAI;gBAChB,OAAO,KAAK,CAAA;QAChB,CAAC;QAED,OAAO,IAAI,CAAA;IACb,CAAC;IAED;;;OAGG;IACI,KAAK,CAAE,IAAU;QACtB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,YAAY,CAAA;QAErC,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;QAErE,IAAI,CAAC,IAAI,EAAE,CAAA;QAEX,OAAO,KAAK,CAAA;IACd,CAAC;IAEM,KAAK,CAAE,UAAkB;QAC9B,IAAI,CAAC,IAAI,CAAC,SAAS;YACjB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;IAChC,CAAC;IAEM,KAAK,CAAC,OAAO,CAAE,UAAuB;QAC3C,MAAM,OAAO,GAA4B,EAAE,CAAA;QAE3C,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC;aAC3C,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,EAAE,CAC5B,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAA;QAEvD,MAAM,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;QAE5B,OAAO,OAAO,CAAA;IAChB,CAAC;IAEO,OAAO,CAAE,IAAU;QACzB,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAE3C,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAA;QACzB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAA;QAC3B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAA;QACjC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAA;QAE3B,oCAAoC;QACpC,KAAK,MAAM,MAAM,IAAI,OAAO;YAC1B,KAAK,MAAM,CAAC,KAAK,EAAE,CAAA;QAErB,OAAO,IAAI,CAAC,KAAK,EAAE,CAAA;IACrB,CAAC;IAEO,MAAM,CAAE,IAAU;QACxB,MAAM,KAAK,GAAW,EAAE,CAAA;QAExB,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM;YAC7B,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAA;QAElC,KAAK,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC;YACvD,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,CAAA;QAE7B,OAAO,KAAK,CAAA;IACd,CAAC;IAEO,KAAK,CAAE,SAAgB;QAC7B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM;YAC7B,IAAI,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC;gBACzB,OAAO,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;QAEjC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QAE3B,OAAO,SAAS,CAAC,IAAI,CAAC,KAAK,EAAE,CAAA;IAC/B,CAAC;IAEO,KAAK;QACX,MAAM,KAAK,GAAW,CAAC,IAAI,CAAC,CAAA;QAE5B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM;YAC7B,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAA;QAEnC,OAAO,KAAK,CAAA;IACd,CAAC;IAEO,IAAI;QACV,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YACxB,OAAO,CAAC,CAAC,SAAS,KAAK,CAAC,CAAC,SAAS;gBAChC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,oDAAoD;gBAC5F,CAAC,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,CAAA,CAAC,oDAAoD;QACpF,CAAC,CAAC,CAAA;IACJ,CAAC;CACF;AAnHD,oBAmHC"}
@@ -1,15 +1,15 @@
1
- import { type Node } from './Node';
2
1
  import { type Segment } from './segment';
3
2
  import { type Match, type Parameter } from './Match';
3
+ import type { Node } from './Node';
4
4
  export declare class Route {
5
5
  readonly root: boolean;
6
6
  readonly variables: number;
7
7
  readonly segments: Segment[];
8
- private readonly node;
8
+ readonly node: Node;
9
9
  private readonly wildcard;
10
10
  constructor(segments: Segment[], node: Node);
11
11
  match(fragments: string[], parameters: Parameter[]): Match | null;
12
12
  equals(route: Route): boolean;
13
- merge(route: Route): void;
13
+ merge(route: Route): Node[];
14
14
  private matchNested;
15
15
  }
@@ -44,7 +44,7 @@ class Route {
44
44
  return true;
45
45
  }
46
46
  merge(route) {
47
- this.node.merge(route.node);
47
+ return this.node.merge(route.node);
48
48
  }
49
49
  matchNested(fragments, parameters) {
50
50
  fragments = fragments.slice(this.segments.length);