@toa.io/extensions.exposition 1.0.0-alpha.269 → 1.0.0-alpha.270

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 (81) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/components/exposition.stash/manifest.toa.yaml +13 -4
  3. package/components/identity.passkeys/operations/tsconfig.tsbuildinfo +1 -1
  4. package/documentation/io.md +26 -23
  5. package/documentation/notes/throttling.md +39 -5
  6. package/features/etag.feature +55 -5
  7. package/features/io.throttle.feature +3 -5
  8. package/package.json +5 -5
  9. package/schemas/io/throttle.cos.yaml +2 -3
  10. package/source/Directive.test.ts +3 -0
  11. package/source/Directive.ts +25 -4
  12. package/source/Endpoint.ts +7 -11
  13. package/source/Factory.ts +16 -1
  14. package/source/HTTP/Context.ts +39 -8
  15. package/source/HTTP/Server.ts +16 -11
  16. package/source/HTTP/exceptions.ts +9 -3
  17. package/source/HTTP/messages.ts +31 -0
  18. package/source/RTD/Directives.ts +6 -0
  19. package/source/directives/auth/Authorization.ts +29 -3
  20. package/source/directives/io/Directive.ts +5 -4
  21. package/source/directives/io/IO.ts +15 -10
  22. package/source/directives/io/Input.ts +3 -3
  23. package/source/directives/io/Output.ts +12 -4
  24. package/source/directives/io/Throttle.ts +9 -12
  25. package/source/directives/io/lib/throttle/Configuration.test.ts +1 -2
  26. package/source/directives/io/lib/throttle/Configuration.ts +2 -4
  27. package/source/directives/io/lib/throttle/Quotas.test.ts +233 -148
  28. package/source/directives/io/lib/throttle/Quotas.ts +129 -53
  29. package/source/directives/io/lib/throttle/Sync.test.ts +153 -0
  30. package/source/directives/io/lib/throttle/Sync.ts +99 -0
  31. package/source/directives/io/lib/throttle/index.ts +1 -0
  32. package/source/directives/io/schemas.test.ts +7 -1
  33. package/transpiled/Directive.d.ts +2 -0
  34. package/transpiled/Directive.js +16 -4
  35. package/transpiled/Directive.js.map +1 -1
  36. package/transpiled/Endpoint.js +7 -8
  37. package/transpiled/Endpoint.js.map +1 -1
  38. package/transpiled/Factory.js +14 -1
  39. package/transpiled/Factory.js.map +1 -1
  40. package/transpiled/HTTP/Context.d.ts +1 -1
  41. package/transpiled/HTTP/Context.js +30 -8
  42. package/transpiled/HTTP/Context.js.map +1 -1
  43. package/transpiled/HTTP/Server.js +14 -10
  44. package/transpiled/HTTP/Server.js.map +1 -1
  45. package/transpiled/HTTP/exceptions.d.ts +5 -2
  46. package/transpiled/HTTP/exceptions.js +7 -3
  47. package/transpiled/HTTP/exceptions.js.map +1 -1
  48. package/transpiled/HTTP/messages.d.ts +2 -0
  49. package/transpiled/HTTP/messages.js +21 -0
  50. package/transpiled/HTTP/messages.js.map +1 -1
  51. package/transpiled/RTD/Directives.d.ts +5 -0
  52. package/transpiled/directives/auth/Authorization.d.ts +1 -0
  53. package/transpiled/directives/auth/Authorization.js +21 -2
  54. package/transpiled/directives/auth/Authorization.js.map +1 -1
  55. package/transpiled/directives/io/Directive.d.ts +4 -4
  56. package/transpiled/directives/io/IO.d.ts +9 -4
  57. package/transpiled/directives/io/IO.js +14 -9
  58. package/transpiled/directives/io/IO.js.map +1 -1
  59. package/transpiled/directives/io/Input.d.ts +1 -1
  60. package/transpiled/directives/io/Input.js +3 -3
  61. package/transpiled/directives/io/Input.js.map +1 -1
  62. package/transpiled/directives/io/Output.d.ts +2 -0
  63. package/transpiled/directives/io/Output.js +9 -3
  64. package/transpiled/directives/io/Output.js.map +1 -1
  65. package/transpiled/directives/io/Throttle.d.ts +3 -5
  66. package/transpiled/directives/io/Throttle.js +8 -10
  67. package/transpiled/directives/io/Throttle.js.map +1 -1
  68. package/transpiled/directives/io/lib/throttle/Configuration.d.ts +0 -1
  69. package/transpiled/directives/io/lib/throttle/Configuration.js +2 -3
  70. package/transpiled/directives/io/lib/throttle/Configuration.js.map +1 -1
  71. package/transpiled/directives/io/lib/throttle/Quotas.d.ts +46 -22
  72. package/transpiled/directives/io/lib/throttle/Quotas.js +107 -46
  73. package/transpiled/directives/io/lib/throttle/Quotas.js.map +1 -1
  74. package/transpiled/directives/io/lib/throttle/Sync.d.ts +30 -0
  75. package/transpiled/directives/io/lib/throttle/Sync.js +76 -0
  76. package/transpiled/directives/io/lib/throttle/Sync.js.map +1 -0
  77. package/transpiled/directives/io/lib/throttle/index.d.ts +1 -0
  78. package/transpiled/directives/io/lib/throttle/index.js +3 -1
  79. package/transpiled/directives/io/lib/throttle/index.js.map +1 -1
  80. package/transpiled/tsconfig.tsbuildinfo +1 -1
  81. package/components/exposition.stash/operations/count.js +0 -7
@@ -1,107 +1,183 @@
1
- import { console } from 'openspan'
2
1
  import { Keys } from './Keys'
3
- import type { Remote } from '@toa.io/core'
2
+ import type { Batch } from './Sync'
4
3
  import type { Configuration } from './Configuration'
5
4
  import type { Parameter } from '../../../../RTD'
6
5
  import type { Input as Context, Output } from '../../../../io'
7
6
 
8
7
  /**
9
- * Quota usage counted across every gateway process, and blocking decided in this one.
8
+ * A quota per key, metered by GCRA, decided in this process and shared with the others.
10
9
  *
11
- * The count comes from the `exposition.stash` component, which counts through Redis
12
- * and hands back a lower bound on what the whole group has reached. Blocking stays
13
- * local: every process sees the same number and reaches the same conclusion on its
14
- * own, and a process that cannot reach Redis still enforces what it has seen itself.
10
+ * The state of a key is one theoretical arrival time: the moment the key would be back
11
+ * at zero if nothing more arrived. An admitted request pushes it `emission` further out,
12
+ * time drags it back, and a request is admitted while it stays within `capacity` of now.
13
+ * So `requests` is what a key may spend at once, and `requests / interval` what it earns
14
+ * back — no window to burst across the edge of, and no lockout to time out of.
15
+ *
16
+ * Deciding needs nothing but the local map, which is what keeps the request path free of
17
+ * I/O. What the other gateways have spent arrives on {@link Sync}'s tick, as a debt in
18
+ * milliseconds — a duration, so it carries across processes without their clocks having
19
+ * to agree, and additive, which is what lets each process report only its own increments.
15
20
  */
16
21
  export class Quotas {
17
- private readonly requests: number
18
- private readonly interval: number
19
- private readonly cooldown: number
22
+ /** How often the debt is worth reconciling with the other gateways, in milliseconds. */
23
+ public readonly period: number
24
+
25
+ /** Milliseconds of debt an admitted request adds, `interval / requests`. */
26
+ private readonly emission: number
27
+
28
+ /** Debt a key may carry before it is refused, `interval` — a burst of `requests`. */
29
+ private readonly capacity: number
30
+
20
31
  private readonly keys: Keys
21
- private readonly counter: Promise<Remote>
22
- private readonly blocked: Record<string, boolean | undefined> = {}
23
- private readonly timers = new Set<NodeJS.Timeout>()
32
+ private readonly prefix: string
33
+ private readonly conditional: boolean
34
+ private readonly entries = new Map<string, Entry>()
24
35
 
25
36
  /**
26
- * The key computed at preflight, for settle to count against.
37
+ * The key computed at preflight, for settle to charge against.
27
38
  *
28
39
  * `segment` reads the route parameters, which settle is not given, and `identity`
29
40
  * can be refreshed between the two — so recomputing there would key a request
30
- * differently than it was checked, and blocking would quietly stop working.
41
+ * differently than it was checked, and throttling would quietly stop working.
31
42
  */
32
43
  private readonly keyed = new WeakMap<Context, string>()
33
- private remote: Remote | null = null
34
44
 
35
45
  public constructor (options: Options) {
36
- this.requests = options.requests
37
- this.interval = options.interval
38
- this.cooldown = options.cooldown
39
46
  this.keys = options.keys
40
- this.counter = options.counter
47
+ this.conditional = options.conditional
48
+ this.emission = options.interval / options.requests
49
+ this.capacity = options.interval
50
+ this.period = period(options.interval)
51
+
52
+ // a key means one budget, so quotas that do not share one must not share a key
53
+ this.prefix = `t:${options.requests}:${options.interval}:`
41
54
  }
42
55
 
43
- public static create (configuration: Configuration, counter: Promise<Remote>,
44
- route: string = ''): Quotas {
45
- const { requests, interval, cooldown } = configuration
46
- const keys = Keys.create(configuration.key, configuration.condition, route)
56
+ public static create (configuration: Configuration, route: string = ''): Quotas {
57
+ const { requests, interval, condition } = configuration
58
+ const keys = Keys.create(configuration.key, condition, route)
47
59
 
48
- return new this({ requests, interval, cooldown, keys, counter })
60
+ return new this({ keys, requests, interval, conditional: condition !== undefined })
49
61
  }
50
62
 
51
- public ok (context: Context, parameters: Parameter[]): boolean {
63
+ /**
64
+ * Seconds to wait before the request would be admitted, or zero when it is.
65
+ *
66
+ * Charges here when nothing conditions the count, so that the request is metered by
67
+ * the same call that admits it; otherwise only the response can tell whether it counts,
68
+ * and {@link use} charges once it can.
69
+ */
70
+ public check (context: Context, parameters: Parameter[]): number {
52
71
  const key = this.keys.get(context, parameters)
53
72
 
54
73
  this.keyed.set(context, key)
55
74
 
56
- return this.blocked[key] !== true
75
+ const now = Date.now()
76
+ const entry = this.entries.get(key)
77
+ const tat = Math.max(entry?.tat ?? now, now)
78
+ const admit = tat + this.emission - this.capacity
79
+
80
+ if (admit > now)
81
+ return Math.ceil((admit - now) / 1000)
82
+
83
+ if (!this.conditional)
84
+ this.charge(key, entry, tat)
85
+
86
+ return 0
57
87
  }
58
88
 
59
- public async use (input: Context, output: Output): Promise<void> {
60
- if (!this.keys.matches(input, output))
89
+ /** Charges what the condition accepts, once the response can be matched against it. */
90
+ public use (input: Context, output: Output): void {
91
+ if (!this.conditional || !this.keys.matches(input, output))
61
92
  return
62
93
 
63
- // preflight always runs first, and a request it throws on never reaches settle
94
+ // preflight always runs first, and a request it refuses never reaches settle
64
95
  const key = this.keyed.get(input) ?? this.keys.get(input)
96
+ const now = Date.now()
97
+ const entry = this.entries.get(key)
98
+ const tat = Math.max(entry?.tat ?? now, now)
99
+
100
+ this.charge(key, entry, tat)
101
+ }
102
+
103
+ /**
104
+ * Adds what is worth reporting to the batch, and drops what has gone quiet.
105
+ *
106
+ * The debt is not cleared here but in {@link settled}, so a tick that fails to reach
107
+ * Redis leaves it to be reported by the next one instead of losing it.
108
+ */
109
+ public flush (now: number, batch: Batch[]): void {
110
+ for (const [key, entry] of this.entries) {
111
+ const delta = Math.round(entry.debt)
65
112
 
66
- this.remote ??= await this.counter
113
+ if (delta > 0) {
114
+ batch.push({ quotas: this, key, delta })
67
115
 
68
- const count = await this.remote.invoke<number>('count',
69
- { input: { name: key, interval: this.interval, amount: 1 } })
116
+ continue
117
+ }
70
118
 
71
- if (count >= this.requests)
72
- this.block(key)
119
+ // out of debt and with nothing left to report, a key says no more than an absent one
120
+ if (entry.tat <= now)
121
+ this.entries.delete(key)
122
+ }
73
123
  }
74
124
 
75
- public dispose (): void {
76
- for (const timer of this.timers)
77
- clearTimeout(timer)
125
+ /** Takes the group's debt back, and clears what this process contributed to it. */
126
+ public settled (reported: Batch, debt: number, now: number): void {
127
+ const entry = this.entries.get(reported.key)
78
128
 
79
- this.timers.clear()
80
- }
129
+ if (entry === undefined) {
130
+ if (debt > 0)
131
+ this.entries.set(reported.key, { tat: now + debt, debt: 0 })
81
132
 
82
- private block (key: string): void {
83
- if (this.blocked[key] === true)
84
133
  return
134
+ }
85
135
 
86
- this.blocked[key] = true
87
-
88
- const timer = setTimeout(() => {
89
- delete this.blocked[key]
136
+ entry.debt -= reported.delta
90
137
 
91
- this.timers.delete(timer)
92
- }, this.cooldown)
138
+ // the group has spent at least what this process has, so its debt is the one to keep
139
+ entry.tat = Math.max(entry.tat, now + debt)
140
+ }
93
141
 
94
- timer.unref()
95
- this.timers.add(timer)
142
+ public name (key: string): string {
143
+ return this.prefix + key
144
+ }
96
145
 
97
- console.info('Quota exceeded, key blocked', { key, cooldown: this.cooldown })
146
+ private charge (key: string, entry: Entry | undefined, tat: number): void {
147
+ if (entry === undefined)
148
+ this.entries.set(key, { tat: tat + this.emission, debt: this.emission })
149
+ else {
150
+ entry.tat = tat + this.emission
151
+ entry.debt += this.emission
152
+ }
98
153
  }
99
154
  }
100
155
 
156
+ /**
157
+ * How often to reconcile, in milliseconds.
158
+ *
159
+ * Reconciling is what bounds the overshoot — between two ticks a gateway is going on
160
+ * what it alone has spent — so it is worth doing an order of magnitude more often than
161
+ * the interval, within reason on either end.
162
+ */
163
+ function period (interval: number): number {
164
+ return Math.min(Math.max(interval / 10, MIN_PERIOD), MAX_PERIOD)
165
+ }
166
+
167
+ const MIN_PERIOD = 250
168
+ const MAX_PERIOD = 2000
169
+
170
+ interface Entry {
171
+ /** Theoretical arrival time: when the key is back at zero if nothing more arrives. */
172
+ tat: number
173
+
174
+ /** Milliseconds of debt this process has added and not yet reported. */
175
+ debt: number
176
+ }
177
+
101
178
  interface Options {
102
179
  keys: Keys
103
180
  requests: number
104
181
  interval: number
105
- cooldown: number
106
- counter: Promise<Remote>
182
+ conditional: boolean
107
183
  }
@@ -0,0 +1,153 @@
1
+ import { Sync } from './Sync'
2
+ import { Quotas } from './Quotas'
3
+ import type { Remote } from '@toa.io/core'
4
+ import type { Configuration } from './Configuration'
5
+ import type { Input as Context } from '../../../../io'
6
+
7
+ let sync: Sync
8
+ let invocations: Input[]
9
+ let context: Context
10
+
11
+ beforeEach(() => {
12
+ jest.useFakeTimers()
13
+
14
+ invocations = []
15
+ context = createContext()
16
+ })
17
+
18
+ afterEach(() => {
19
+ sync.dispose()
20
+
21
+ jest.useRealTimers()
22
+ })
23
+
24
+ it('should tick as often as the shortest interval asks', async () => {
25
+ sync = new Sync(createStash())
26
+
27
+ const slow = createQuotas({ interval: 60_000 })
28
+
29
+ // on its own a minute reconciles every two seconds, which is the cap
30
+ sync.register(slow)
31
+ slow.check(context, [])
32
+
33
+ await tick()
34
+
35
+ expect(invocations).toHaveLength(0)
36
+
37
+ // a second asks for every 250ms, and the shortest sets the pace for all of them
38
+ sync.register(createQuotas({ interval: 1000 }))
39
+
40
+ await tick()
41
+
42
+ expect(invocations).toHaveLength(1)
43
+ expect(jest.getTimerCount()).toBe(1)
44
+ })
45
+
46
+ it('should reconcile every quota in one call', async () => {
47
+ sync = new Sync(createStash())
48
+
49
+ const one = createQuotas()
50
+ const two = createQuotas({ key: [{ method: 'identity' }] })
51
+
52
+ sync.register(one)
53
+ sync.register(two)
54
+
55
+ one.check(context, [])
56
+ two.check(context, [])
57
+
58
+ await tick()
59
+
60
+ expect(invocations).toHaveLength(1)
61
+ expect(invocations[0].keys).toHaveLength(2)
62
+ expect(invocations[0].deltas).toStrictEqual([50, 50])
63
+ })
64
+
65
+ it('should not call when there is nothing to report', async () => {
66
+ sync = new Sync(createStash())
67
+
68
+ sync.register(createQuotas())
69
+
70
+ await tick()
71
+
72
+ expect(invocations).toHaveLength(0)
73
+ })
74
+
75
+ it('should take the group debt back', async () => {
76
+ sync = new Sync(createStash(() => [1000]))
77
+
78
+ const quotas = createQuotas()
79
+
80
+ sync.register(quotas)
81
+ quotas.check(context, [])
82
+
83
+ await tick()
84
+
85
+ // one request here, but the group is already at capacity
86
+ expect(quotas.check(context, [])).toBeGreaterThan(0)
87
+ })
88
+
89
+ it('should clear what it reported', async () => {
90
+ sync = new Sync(createStash())
91
+
92
+ const quotas = createQuotas()
93
+
94
+ sync.register(quotas)
95
+ quotas.check(context, [])
96
+
97
+ await tick()
98
+ await tick()
99
+
100
+ expect(invocations).toHaveLength(1)
101
+ })
102
+
103
+ it('should keep what it could not report, and keep serving', async () => {
104
+ sync = new Sync(createStash(() => {
105
+ throw new Error('Redis is unreachable')
106
+ }))
107
+
108
+ const quotas = createQuotas()
109
+
110
+ sync.register(quotas)
111
+ quotas.check(context, [])
112
+
113
+ await tick()
114
+ await tick()
115
+
116
+ expect(invocations).toHaveLength(2)
117
+ expect(invocations[1].deltas).toStrictEqual([50])
118
+ expect(quotas.check(context, [])).toBe(0)
119
+ })
120
+
121
+ async function tick (): Promise<void> {
122
+ await jest.advanceTimersByTimeAsync(250)
123
+ }
124
+
125
+ function createQuotas (properties?: Partial<Configuration>): Quotas {
126
+ const configuration = { key: [{ method: 'path' as const }], requests: 2, interval: 100 }
127
+
128
+ return Quotas.create(Object.assign(configuration, properties))
129
+ }
130
+
131
+ function createContext (properties?: any): Context {
132
+ return Object.assign({ url: new URL('http://localhost/'), identity: { id: 'one' } },
133
+ properties) as unknown as Context
134
+ }
135
+
136
+ function createStash (reply?: (deltas: number[]) => number[]): Promise<Remote> {
137
+ const remote = {
138
+ invoke: async (endpoint: string, request: { input: Input }): Promise<number[]> => {
139
+ expect(endpoint).toBe('meter')
140
+
141
+ invocations.push(request.input)
142
+
143
+ return reply === undefined ? request.input.deltas : reply(request.input.deltas)
144
+ }
145
+ }
146
+
147
+ return Promise.resolve(remote as unknown as Remote)
148
+ }
149
+
150
+ interface Input {
151
+ keys: string[]
152
+ deltas: number[]
153
+ }
@@ -0,0 +1,99 @@
1
+ import { console } from 'openspan'
2
+ import type { Quotas } from './Quotas'
3
+ import type { Remote } from '@toa.io/core'
4
+
5
+ /**
6
+ * Reconciles every quota of the process with the other gateways, on one tick.
7
+ *
8
+ * Nothing here is on the request path. Each tick takes what the quotas have spent since
9
+ * the last one and sends all of it as a single call, so the cost of staying in step is
10
+ * one round trip a tick — not one per key, and not one per directive. What comes back is
11
+ * the debt the whole group has reached, which the quotas then decide on locally.
12
+ */
13
+ export class Sync {
14
+ private readonly stash: Promise<Remote>
15
+ private readonly quotas: Quotas[] = []
16
+ private remote: Remote | null = null
17
+ private timer: NodeJS.Timeout | null = null
18
+ private period = Infinity
19
+
20
+ /** Ticks do not overlap: a slow round trip delays reconciling, it does not double it. */
21
+ private reconciling = false
22
+
23
+ public constructor (stash: Promise<Remote>) {
24
+ this.stash = stash
25
+ }
26
+
27
+ public register (quotas: Quotas): void {
28
+ this.quotas.push(quotas)
29
+
30
+ // the shortest interval sets the pace, and reconciling the others more often than
31
+ // they asked for only makes them more accurate — an idle quota contributes nothing
32
+ if (quotas.period < this.period) {
33
+ this.period = quotas.period
34
+
35
+ this.start()
36
+ }
37
+ }
38
+
39
+ public dispose (): void {
40
+ if (this.timer !== null) {
41
+ clearInterval(this.timer)
42
+
43
+ this.timer = null
44
+ }
45
+ }
46
+
47
+ private start (): void {
48
+ this.dispose()
49
+
50
+ this.timer = setInterval(this.tick, this.period)
51
+
52
+ this.timer.unref()
53
+ }
54
+
55
+ private readonly tick = (): void => {
56
+ if (this.reconciling)
57
+ return
58
+
59
+ const now = Date.now()
60
+ const batch: Batch[] = []
61
+
62
+ for (const quotas of this.quotas)
63
+ quotas.flush(now, batch)
64
+
65
+ if (batch.length === 0)
66
+ return
67
+
68
+ this.reconciling = true
69
+
70
+ void this.reconcile(batch).finally(() => {
71
+ this.reconciling = false
72
+ })
73
+ }
74
+
75
+ private async reconcile (batch: Batch[]): Promise<void> {
76
+ try {
77
+ this.remote ??= await this.stash
78
+
79
+ const keys = batch.map((entry) => entry.quotas.name(entry.key))
80
+ const deltas = batch.map((entry) => entry.delta)
81
+
82
+ const debts = await this.remote.invoke<number[]>('meter', { input: { keys, deltas } })
83
+ const now = Date.now()
84
+
85
+ for (let i = 0; i < batch.length; i++)
86
+ batch[i].quotas.settled(batch[i], debts[i], now)
87
+ } catch (error) {
88
+ // what was not reported stays to be reported by the next tick, and until then
89
+ // this gateway keeps throttling on what it alone has seen, and keeps serving
90
+ console.warn('Throttling could not reconcile with the group', error as Error)
91
+ }
92
+ }
93
+ }
94
+
95
+ export interface Batch {
96
+ quotas: Quotas
97
+ key: string
98
+ delta: number
99
+ }
@@ -1,2 +1,3 @@
1
1
  export { Quotas } from './Quotas'
2
+ export { Sync } from './Sync'
2
3
  export { parse, type Configuration, type Declaration } from './Configuration'
@@ -1,7 +1,7 @@
1
1
  import * as schemas from './schemas'
2
2
 
3
3
  describe('throttle', () => {
4
- const rest = { requests: 1, interval: 1, cooldown: 1 }
4
+ const rest = { requests: 1, interval: 1 }
5
5
 
6
6
  it('should validate key', () => {
7
7
  expect(() => schemas.throttle.validate({ key: 'ip', ...rest })).not.toThrow()
@@ -22,4 +22,10 @@ describe('throttle', () => {
22
22
  expect(() => schemas.throttle.validate({ key: 'header', ...rest })).toThrow()
23
23
  expect(() => schemas.throttle.validate({ key: { header: 'x-real-ip' }, ...rest })).toThrow()
24
24
  })
25
+
26
+ it('should reject a declaration it no longer honours', () => {
27
+ // metering earns the budget back rather than locking a key out, so a `cooldown`
28
+ // left behind is not something to ignore quietly
29
+ expect(() => schemas.throttle.validate({ key: 'ip', cooldown: 1, ...rest })).toThrow()
30
+ })
25
31
  })
@@ -4,6 +4,8 @@ import type { Output } from './io';
4
4
  import type * as RTD from './RTD';
5
5
  export declare class Directives implements RTD.Directives {
6
6
  private readonly sets;
7
+ /** the span of a stage depends only on the set, so it is built once per route */
8
+ private readonly spans;
7
9
  constructor(sets: RTD.DirectiveSet[]);
8
10
  preflight(context: Context, parameters: RTD.Parameter[]): Promise<Output>;
9
11
  settle(context: Context, response: OutgoingMessage): Promise<void>;
@@ -4,15 +4,22 @@ exports.shortcuts = exports.DirectivesFactory = exports.Directives = void 0;
4
4
  const openspan_1 = require("openspan");
5
5
  class Directives {
6
6
  sets;
7
+ /** the span of a stage depends only on the set, so it is built once per route */
8
+ spans;
7
9
  constructor(sets) {
8
10
  this.sets = sets;
11
+ this.spans = sets.map((set) => ({
12
+ preflight: options(set, 'preflight'),
13
+ settle: options(set, 'settle')
14
+ }));
9
15
  }
10
16
  async preflight(context, parameters) {
11
17
  let output = null;
12
- for (const set of this.sets) {
18
+ for (let i = 0; i < this.sets.length; i++) {
19
+ const set = this.sets[i];
13
20
  if (set.family.preflight === undefined)
14
21
  continue;
15
- const out = await openspan_1.console.span(options(set, 'preflight'), async () => await set.family.preflight(set.directives, context, parameters));
22
+ const out = await openspan_1.console.span(this.spans[i].preflight, async () => await set.family.preflight(set.directives, context, parameters));
16
23
  if (out === null)
17
24
  continue;
18
25
  if (output !== null)
@@ -23,9 +30,11 @@ class Directives {
23
30
  return output;
24
31
  }
25
32
  async settle(context, response) {
26
- for (const set of this.sets)
33
+ for (let i = 0; i < this.sets.length; i++) {
34
+ const set = this.sets[i];
27
35
  if (set.family.settle !== undefined)
28
- await openspan_1.console.span(options(set, 'settle'), async () => await set.family.settle(set.directives, context, response));
36
+ await openspan_1.console.span(this.spans[i].settle, async () => await set.family.settle(set.directives, context, response));
37
+ }
29
38
  }
30
39
  dispose() {
31
40
  for (const set of this.sets)
@@ -79,6 +88,9 @@ class DirectivesFactory {
79
88
  // can key a quota on it, and a request it denies should not reach `io` at all.
80
89
  // The rest keep the order they were declared in, the sort being stable.
81
90
  sets.sort((a, b) => this.rank(a.family.name) - this.rank(b.family.name));
91
+ // whatever order a family needs among its own directives is fixed here, not per request
92
+ for (const set of sets)
93
+ set.family.arrange?.(set.directives);
82
94
  const directives = new Directives(sets);
83
95
  this.instances.push(directives);
84
96
  return directives;
@@ -1 +1 @@
1
- {"version":3,"file":"Directive.js","sourceRoot":"","sources":["../source/Directive.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Directive.js","sourceRoot":"","sources":["../source/Directive.ts"],"names":[],"mappings":"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"}
@@ -25,7 +25,6 @@ var __importStar = (this && this.__importStar) || function (mod) {
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
26
  exports.EndpointsFactory = exports.Endpoint = void 0;
27
27
  const node_stream_1 = require("node:stream");
28
- const node_crypto_1 = require("node:crypto");
29
28
  const openspan_1 = require("openspan");
30
29
  const Mapping_1 = require("./Mapping");
31
30
  const http = __importStar(require("./HTTP"));
@@ -109,13 +108,13 @@ class Endpoint {
109
108
  }
110
109
  if (reply instanceof node_stream_1.Readable)
111
110
  return false;
112
- const hash = `"${(0, node_crypto_1.createHash)('sha256').update(JSON.stringify(reply)).digest('hex')}"`;
113
- if (etag === hash) {
114
- message.status = 304;
115
- message.headers.set('etag', etag);
116
- return true;
117
- }
118
- message.headers.set('etag', hash);
111
+ /*
112
+ * A reply that carries no version is tagged with a hash of its body. The body is
113
+ * serialized anyway when the response is written, so the tag is computed from what
114
+ * is actually sent rather than from a second serialization of the reply — which
115
+ * makes it specific to the negotiated representation, hence `vary: accept`.
116
+ */
117
+ message.etag = true;
119
118
  return false;
120
119
  }
121
120
  query(context) {
@@ -1 +1 @@
1
- {"version":3,"file":"Endpoint.js","sourceRoot":"","sources":["../source/Endpoint.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Endpoint.js","sourceRoot":"","sources":["../source/Endpoint.ts"],"names":[],"mappings":"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"}