@pipeline-builder/api-core 4.1.2 → 4.1.4

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/README.md +3 -3
  2. package/lib/errors/app-errors.d.ts +0 -4
  3. package/lib/errors/app-errors.js +1 -8
  4. package/lib/helpers/restore-helpers.d.ts +3 -3
  5. package/lib/helpers/restore-helpers.js +4 -4
  6. package/lib/helpers/sse-helpers.d.ts +8 -1
  7. package/lib/helpers/sse-helpers.js +11 -3
  8. package/lib/middleware/auth.d.ts +174 -43
  9. package/lib/middleware/auth.js +384 -179
  10. package/lib/middleware/index.d.ts +1 -0
  11. package/lib/middleware/index.js +2 -1
  12. package/lib/middleware/route-table.d.ts +104 -0
  13. package/lib/middleware/route-table.js +230 -0
  14. package/lib/middleware/step-up.d.ts +48 -16
  15. package/lib/middleware/step-up.js +74 -36
  16. package/lib/services/api-key-exchange.d.ts +25 -0
  17. package/lib/services/api-key-exchange.js +219 -0
  18. package/lib/services/audit-spool.d.ts +2 -3
  19. package/lib/services/audit-spool.js +3 -4
  20. package/lib/services/cache-service.d.ts +65 -21
  21. package/lib/services/cache-service.js +173 -78
  22. package/lib/services/compliance-event-subscriber.d.ts +5 -4
  23. package/lib/services/compliance-event-subscriber.js +8 -6
  24. package/lib/services/durable-event-bus.d.ts +22 -39
  25. package/lib/services/durable-event-bus.js +109 -47
  26. package/lib/services/env-redis.d.ts +114 -35
  27. package/lib/services/env-redis.js +215 -73
  28. package/lib/services/http-client.d.ts +10 -15
  29. package/lib/services/http-client.js +32 -27
  30. package/lib/services/index.d.ts +3 -0
  31. package/lib/services/index.js +4 -1
  32. package/lib/services/jwks-cache.d.ts +102 -0
  33. package/lib/services/jwks-cache.js +222 -0
  34. package/lib/services/leader-lock.d.ts +6 -3
  35. package/lib/services/leader-lock.js +20 -5
  36. package/lib/services/quota.d.ts +44 -10
  37. package/lib/services/quota.js +95 -51
  38. package/lib/services/remote-audit-client.d.ts +1 -1
  39. package/lib/services/remote-audit-client.js +32 -1
  40. package/lib/services/scheduler.d.ts +2 -3
  41. package/lib/services/scheduler.js +2 -2
  42. package/lib/services/service-boot.d.ts +4 -2
  43. package/lib/services/service-boot.js +7 -3
  44. package/lib/services/service-keys.d.ts +83 -0
  45. package/lib/services/service-keys.js +354 -0
  46. package/lib/services/sse-ticket-store.d.ts +55 -22
  47. package/lib/services/sse-ticket-store.js +122 -79
  48. package/lib/services/token-revocation.d.ts +1 -1
  49. package/lib/services/token-revocation.js +31 -19
  50. package/lib/testing/route-coverage.d.ts +64 -0
  51. package/lib/testing/route-coverage.js +183 -0
  52. package/lib/testing/service-tokens.d.ts +66 -0
  53. package/lib/testing/service-tokens.js +137 -0
  54. package/lib/testing/tier-mock.d.ts +2 -2
  55. package/lib/testing/tier-mock.js +3 -3
  56. package/lib/testing/user-tokens.d.ts +75 -0
  57. package/lib/testing/user-tokens.js +115 -0
  58. package/lib/types/catalog-metadata.d.ts +0 -2
  59. package/lib/types/catalog-metadata.js +1 -3
  60. package/lib/types/common.d.ts +103 -4
  61. package/lib/types/common.js +11 -1
  62. package/lib/types/error-codes.d.ts +15 -0
  63. package/lib/types/error-codes.js +22 -1
  64. package/lib/types/permissions.d.ts +35 -1
  65. package/lib/types/permissions.js +70 -2
  66. package/lib/utils/api-key.d.ts +34 -0
  67. package/lib/utils/api-key.js +75 -0
  68. package/lib/utils/env.d.ts +0 -2
  69. package/lib/utils/env.js +1 -6
  70. package/lib/utils/index.d.ts +3 -0
  71. package/lib/utils/index.js +4 -1
  72. package/lib/utils/jwk.d.ts +98 -0
  73. package/lib/utils/jwk.js +139 -0
  74. package/lib/utils/logger.d.ts +4 -1
  75. package/lib/utils/logger.js +1 -22
  76. package/lib/utils/secret-encryption.d.ts +7 -2
  77. package/lib/utils/secret-encryption.js +47 -4
  78. package/lib/utils/secret-rotation.d.ts +17 -0
  79. package/lib/utils/secret-rotation.js +54 -0
  80. package/lib/validation/common-schemas.js +5 -4
  81. package/package.json +20 -1
@@ -1,3 +1,4 @@
1
1
  export * from './auth.js';
2
2
  export * from './mongo-sanitize.js';
3
3
  export * from './step-up.js';
4
+ export * from './route-table.js';
@@ -3,4 +3,5 @@
3
3
  export * from './auth.js';
4
4
  export * from './mongo-sanitize.js';
5
5
  export * from './step-up.js';
6
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvbWlkZGxld2FyZS9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDLGNBQWMsV0FBVyxDQUFDO0FBQzFCLGNBQWMscUJBQXFCLENBQUM7QUFDcEMsY0FBYyxjQUFjLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuZXhwb3J0ICogZnJvbSAnLi9hdXRoLmpzJztcbmV4cG9ydCAqIGZyb20gJy4vbW9uZ28tc2FuaXRpemUuanMnO1xuZXhwb3J0ICogZnJvbSAnLi9zdGVwLXVwLmpzJztcbiJdfQ==
6
+ export * from './route-table.js';
7
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvbWlkZGxld2FyZS9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDLGNBQWMsV0FBVyxDQUFDO0FBQzFCLGNBQWMscUJBQXFCLENBQUM7QUFDcEMsY0FBYyxjQUFjLENBQUM7QUFDN0IsY0FBYyxrQkFBa0IsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIENvcHlyaWdodCAyMDI2IFBpcGVsaW5lIEJ1aWxkZXIgQ29udHJpYnV0b3JzXG4vLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuXG5leHBvcnQgKiBmcm9tICcuL2F1dGguanMnO1xuZXhwb3J0ICogZnJvbSAnLi9tb25nby1zYW5pdGl6ZS5qcyc7XG5leHBvcnQgKiBmcm9tICcuL3N0ZXAtdXAuanMnO1xuZXhwb3J0ICogZnJvbSAnLi9yb3V0ZS10YWJsZS5qcyc7XG4iXX0=
@@ -0,0 +1,104 @@
1
+ import type { Permission } from '../types/permissions.js';
2
+ /** One requirement a gate middleware enforces. */
3
+ export type RouteGate = {
4
+ kind: 'auth';
5
+ } | {
6
+ kind: 'permission';
7
+ mode: 'any' | 'all';
8
+ permissions: readonly Permission[];
9
+ allowService?: boolean;
10
+ } | {
11
+ kind: 'systemAdmin';
12
+ } | {
13
+ kind: 'servicePrincipal';
14
+ }
15
+ /** An INTERNAL route: user tokens refused outright, only these services admitted. */
16
+ | {
17
+ kind: 'internalService';
18
+ callers: readonly string[];
19
+ } | {
20
+ kind: 'stepUp';
21
+ methods?: readonly string[];
22
+ }
23
+ /** The whole SESSION must be MFA-grade (`requireAuth({ minAssurance })`), and
24
+ * optionally no older than `maxAge` seconds. */
25
+ | {
26
+ kind: 'assurance';
27
+ minAssurance: 1 | 2;
28
+ maxAge?: number;
29
+ } | {
30
+ kind: 'feature';
31
+ feature: string;
32
+ } | {
33
+ kind: 'scope';
34
+ scope: string;
35
+ } | {
36
+ kind: 'audit';
37
+ actions: readonly string[];
38
+ };
39
+ /**
40
+ * Attach gate metadata to a middleware function (appends to any existing tags)
41
+ * and return the same function. Use it for a service-local gate so the route
42
+ * table knows what it enforces — e.g. a handler-level `requireComplianceWrite`
43
+ * tagged `{ kind: 'permission', mode: 'any', permissions: ['compliance:write'] }`.
44
+ */
45
+ export declare function tagRouteGate<T extends object>(fn: T, ...gates: RouteGate[]): T;
46
+ /** The gate metadata attached to a middleware function (empty when untagged). */
47
+ export declare function getRouteGates(fn: unknown): readonly RouteGate[];
48
+ /**
49
+ * Declare the audit action(s) a route emits. A no-op middleware whose only job
50
+ * is to put the action on the route table, so the coverage test can require an
51
+ * audit declaration on every write route. Place it in the route's chain:
52
+ * `router.post('/', audited('pipeline.create'), handler)`. The handler still
53
+ * emits the event itself.
54
+ */
55
+ export declare function audited(...actions: string[]): (req: unknown, res: unknown, next: () => void) => void;
56
+ /** Resolved requirements for one method + path. */
57
+ export interface RouteTableEntry {
58
+ method: string;
59
+ path: string;
60
+ /** Authentication runs before the handler (requireAuth). */
61
+ auth: boolean;
62
+ /** Each permission gate in the chain (all must pass; each is any-of or all-of). */
63
+ permissions: Array<{
64
+ mode: 'any' | 'all';
65
+ permissions: Permission[];
66
+ allowService: boolean;
67
+ }>;
68
+ systemAdmin: boolean;
69
+ servicePrincipal: boolean;
70
+ /**
71
+ * The services allowed to call this route when it is INTERNAL (`requireInternalService`).
72
+ * An empty array means the route is not internal — a user token may reach it,
73
+ * subject to the other gates. Sorted, so the generated table is stable.
74
+ */
75
+ internalCallers: string[];
76
+ stepUp: boolean;
77
+ /** When the step-up gate names the factors that may earn it (`['webauthn',
78
+ * 'totp']` on the most dangerous routes), the allowed methods. Empty means
79
+ * any factor satisfies it. Sorted, so the generated table is stable. */
80
+ stepUpMethods: string[];
81
+ /** Highest `minAssurance` any gate in the chain demands; 0 when none does. */
82
+ minAssurance: 0 | 1 | 2;
83
+ /** Tightest `maxAge` (seconds) any assurance gate in the chain demands. */
84
+ maxAge?: number;
85
+ features: string[];
86
+ scopes: string[];
87
+ audit: string[];
88
+ }
89
+ /**
90
+ * Build the route table of an assembled Express app (or router): one entry per
91
+ * reachable method + path with every gate that runs before its handler.
92
+ * HEAD is folded into GET, as Express does.
93
+ */
94
+ export declare function buildRouteTable(app: unknown): RouteTableEntry[];
95
+ /** Whether a method mutates state (everything except GET/HEAD/OPTIONS). */
96
+ export declare function isWriteMethod(method: string): boolean;
97
+ /** Summary counts for a startup log line. */
98
+ export declare function summarizeRouteTable(table: readonly RouteTableEntry[]): {
99
+ routes: number;
100
+ writes: number;
101
+ ungated: number;
102
+ unaudited: number;
103
+ internal: number;
104
+ };
@@ -0,0 +1,230 @@
1
+ // Copyright 2026 Pipeline Builder Contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * Introspectable route table.
5
+ *
6
+ * Authorization is split between router mounts (`app.use('/x', gate, router)`)
7
+ * and individual routes, so grepping source can't prove that every route is
8
+ * gated. Instead, every gate middleware in this package carries METADATA (what it
9
+ * requires), and {@link buildRouteTable} walks an assembled Express app and
10
+ * resolves, per method + path, the full chain of gates Express would run before
11
+ * the handler: permissions, system-admin, service principal, step-up, feature,
12
+ * token scope, and the audit actions the route declares via {@link audited}.
13
+ *
14
+ * Express 5 (router@2) does not keep a mount's path string on its layer, so this
15
+ * module records it: on import it wraps `Router.prototype.use` to stamp each new
16
+ * layer with the path it was mounted at. api-core is imported before any route
17
+ * module registers a route, so every layer an app builds is stamped. The wrap
18
+ * only adds a symbol property; routing behaviour is unchanged.
19
+ */
20
+ import express from 'express';
21
+ // Symbol.for, not Symbol(): a test that loads api-core twice (ESM + a
22
+ // requireActual copy) must still see tags set by the other copy.
23
+ const GATES = Symbol.for('pipeline-builder.route-gates');
24
+ const LAYER_PATH = Symbol.for('pipeline-builder.layer-path');
25
+ const RECORDER_INSTALLED = Symbol.for('pipeline-builder.route-recorder');
26
+ /**
27
+ * Attach gate metadata to a middleware function (appends to any existing tags)
28
+ * and return the same function. Use it for a service-local gate so the route
29
+ * table knows what it enforces — e.g. a handler-level `requireComplianceWrite`
30
+ * tagged `{ kind: 'permission', mode: 'any', permissions: ['compliance:write'] }`.
31
+ */
32
+ export function tagRouteGate(fn, ...gates) {
33
+ const t = fn;
34
+ Object.defineProperty(t, GATES, { value: [...(t[GATES] ?? []), ...gates], configurable: true, enumerable: false, writable: true });
35
+ return fn;
36
+ }
37
+ /** The gate metadata attached to a middleware function (empty when untagged). */
38
+ export function getRouteGates(fn) {
39
+ if (typeof fn !== 'function')
40
+ return [];
41
+ return fn[GATES] ?? [];
42
+ }
43
+ /**
44
+ * Declare the audit action(s) a route emits. A no-op middleware whose only job
45
+ * is to put the action on the route table, so the coverage test can require an
46
+ * audit declaration on every write route. Place it in the route's chain:
47
+ * `router.post('/', audited('pipeline.create'), handler)`. The handler still
48
+ * emits the event itself.
49
+ */
50
+ export function audited(...actions) {
51
+ return tagRouteGate(function auditedRoute(_req, _res, next) { next(); }, { kind: 'audit', actions });
52
+ }
53
+ function installRouteRecorder() {
54
+ const proto = express.Router.prototype;
55
+ if (proto[RECORDER_INSTALLED])
56
+ return;
57
+ const originalUse = proto.use;
58
+ proto.use = function recordingUse(...args) {
59
+ const before = this.stack.length;
60
+ const result = originalUse.apply(this, args);
61
+ // Same path disambiguation as router@2's `use`: a leading non-function
62
+ // (possibly nested in arrays) is the path; otherwise the mount is '/'.
63
+ let first = args[0];
64
+ while (Array.isArray(first) && first.length > 0)
65
+ first = first[0];
66
+ const path = typeof first === 'function' ? '/' : String(args[0]);
67
+ for (let i = before; i < this.stack.length; i++) {
68
+ Object.defineProperty(this.stack[i], LAYER_PATH, { value: path, enumerable: false, configurable: true });
69
+ }
70
+ return result;
71
+ };
72
+ Object.defineProperty(proto, RECORDER_INSTALLED, { value: true, enumerable: false });
73
+ }
74
+ installRouteRecorder();
75
+ function joinPath(prefix, path) {
76
+ const joined = `${prefix.replace(/\/+$/, '')}/${path.replace(/^\/+/, '')}`;
77
+ return joined.length > 1 ? joined.replace(/\/+$/, '') : '/';
78
+ }
79
+ function isRouter(handle) {
80
+ return typeof handle === 'function' && Array.isArray(handle.stack);
81
+ }
82
+ /**
83
+ * Whether a middleware layer (mounted under `prefix`) runs for `fullPath`.
84
+ * Mirrors `Layer.prototype.match`, including its fast path: a layer added with
85
+ * no path (`router.use(gate)`) matches EVERY path under its router — which is
86
+ * exactly how the shared `requirePermission` gates between two `router.use(...)`
87
+ * mounts reach the routers registered after them.
88
+ */
89
+ function applies(c, fullPath) {
90
+ const base = c.prefix === '/' ? '' : c.prefix;
91
+ if (base && fullPath !== base && !fullPath.startsWith(`${base}/`))
92
+ return false;
93
+ if (c.layer.slash)
94
+ return true;
95
+ const rel = fullPath.slice(base.length) || '/';
96
+ return c.layer.matchers.some((m) => m(rel) !== false);
97
+ }
98
+ function collectGates(fns) {
99
+ return fns.flatMap((fn) => getRouteGates(fn));
100
+ }
101
+ function toEntry(method, path, gates) {
102
+ const entry = {
103
+ method,
104
+ path,
105
+ auth: false,
106
+ permissions: [],
107
+ systemAdmin: false,
108
+ servicePrincipal: false,
109
+ internalCallers: [],
110
+ stepUp: false,
111
+ stepUpMethods: [],
112
+ minAssurance: 0,
113
+ features: [],
114
+ scopes: [],
115
+ audit: [],
116
+ };
117
+ for (const g of gates) {
118
+ switch (g.kind) {
119
+ case 'auth':
120
+ entry.auth = true;
121
+ break;
122
+ case 'permission':
123
+ entry.permissions.push({ mode: g.mode, permissions: [...g.permissions], allowService: g.allowService === true });
124
+ break;
125
+ case 'systemAdmin':
126
+ entry.systemAdmin = true;
127
+ break;
128
+ case 'servicePrincipal':
129
+ entry.servicePrincipal = true;
130
+ break;
131
+ case 'internalService':
132
+ for (const c of g.callers)
133
+ if (!entry.internalCallers.includes(c))
134
+ entry.internalCallers.push(c);
135
+ break;
136
+ case 'stepUp':
137
+ entry.stepUp = true;
138
+ for (const m of g.methods ?? [])
139
+ if (!entry.stepUpMethods.includes(m))
140
+ entry.stepUpMethods.push(m);
141
+ break;
142
+ case 'assurance':
143
+ // Several gates may apply; the STRICTEST wins, since every one of them runs.
144
+ if (g.minAssurance > entry.minAssurance)
145
+ entry.minAssurance = g.minAssurance;
146
+ if (g.maxAge !== undefined)
147
+ entry.maxAge = entry.maxAge === undefined ? g.maxAge : Math.min(entry.maxAge, g.maxAge);
148
+ break;
149
+ case 'feature':
150
+ if (!entry.features.includes(g.feature))
151
+ entry.features.push(g.feature);
152
+ break;
153
+ case 'scope':
154
+ if (!entry.scopes.includes(g.scope))
155
+ entry.scopes.push(g.scope);
156
+ break;
157
+ case 'audit':
158
+ for (const a of g.actions)
159
+ if (!entry.audit.includes(a))
160
+ entry.audit.push(a);
161
+ break;
162
+ }
163
+ }
164
+ entry.internalCallers.sort();
165
+ entry.stepUpMethods.sort();
166
+ return entry;
167
+ }
168
+ function walk(stack, prefix, inherited, out, seen) {
169
+ const local = [];
170
+ for (const layer of stack) {
171
+ if (layer.route) {
172
+ const paths = Array.isArray(layer.route.path) ? layer.route.path : [layer.route.path];
173
+ const methods = Object.keys(layer.route.methods).map((m) => (m === '_all' ? '*' : m.toUpperCase()));
174
+ for (const routePath of paths) {
175
+ const full = joinPath(prefix, String(routePath));
176
+ const chain = [...inherited, ...local].filter((c) => applies(c, full)).map((c) => c.layer.handle);
177
+ for (const method of methods) {
178
+ const key = `${method} ${full}`;
179
+ // First registration wins, as in Express: a later duplicate is unreachable.
180
+ if (seen.has(key))
181
+ continue;
182
+ seen.add(key);
183
+ const routeFns = layer.route.stack
184
+ .filter((l) => l.method === undefined || l.method.toUpperCase() === method || method === '*')
185
+ .map((l) => l.handle);
186
+ out.push(toEntry(method, full, collectGates([...chain, ...routeFns])));
187
+ }
188
+ }
189
+ }
190
+ else if (isRouter(layer.handle)) {
191
+ const mountPath = joinPath(prefix, layer[LAYER_PATH] ?? '/');
192
+ // Middleware that runs before this router (ancestors + earlier siblings)
193
+ // is inherited; applicability is re-checked against each full route path.
194
+ walk(layer.handle.stack, mountPath, [...inherited, ...local], out, seen);
195
+ }
196
+ else {
197
+ local.push({ layer, prefix });
198
+ }
199
+ }
200
+ }
201
+ /**
202
+ * Build the route table of an assembled Express app (or router): one entry per
203
+ * reachable method + path with every gate that runs before its handler.
204
+ * HEAD is folded into GET, as Express does.
205
+ */
206
+ export function buildRouteTable(app) {
207
+ const root = app.router ?? app;
208
+ const out = [];
209
+ if (!root || !Array.isArray(root.stack))
210
+ return out;
211
+ walk(root.stack, '/', [], out, new Set());
212
+ return out.sort((a, b) => (a.path === b.path ? a.method.localeCompare(b.method) : a.path.localeCompare(b.path)));
213
+ }
214
+ /** Whether a method mutates state (everything except GET/HEAD/OPTIONS). */
215
+ export function isWriteMethod(method) {
216
+ return !['GET', 'HEAD', 'OPTIONS'].includes(method.toUpperCase());
217
+ }
218
+ /** Summary counts for a startup log line. */
219
+ export function summarizeRouteTable(table) {
220
+ const gated = (e) => e.permissions.length > 0 || e.systemAdmin || e.servicePrincipal;
221
+ const writes = table.filter((e) => isWriteMethod(e.method));
222
+ return {
223
+ routes: table.length,
224
+ writes: writes.length,
225
+ ungated: table.filter((e) => !gated(e)).length,
226
+ unaudited: writes.filter((e) => e.audit.length === 0).length,
227
+ internal: table.filter((e) => e.internalCallers.length > 0).length,
228
+ };
229
+ }
230
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUtdGFibGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvbWlkZGxld2FyZS9yb3V0ZS10YWJsZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDOzs7Ozs7Ozs7Ozs7Ozs7O0dBZ0JHO0FBRUgsT0FBTyxPQUFPLE1BQU0sU0FBUyxDQUFDO0FBbUI5QixzRUFBc0U7QUFDdEUsaUVBQWlFO0FBQ2pFLE1BQU0sS0FBSyxHQUFHLE1BQU0sQ0FBQyxHQUFHLENBQUMsOEJBQThCLENBQUMsQ0FBQztBQUN6RCxNQUFNLFVBQVUsR0FBRyxNQUFNLENBQUMsR0FBRyxDQUFDLDZCQUE2QixDQUFDLENBQUM7QUFDN0QsTUFBTSxrQkFBa0IsR0FBRyxNQUFNLENBQUMsR0FBRyxDQUFDLGlDQUFpQyxDQUFDLENBQUM7QUFJekU7Ozs7O0dBS0c7QUFDSCxNQUFNLFVBQVUsWUFBWSxDQUFtQixFQUFLLEVBQUUsR0FBRyxLQUFrQjtJQUN6RSxNQUFNLENBQUMsR0FBRyxFQUFnQixDQUFDO0lBQzNCLE1BQU0sQ0FBQyxjQUFjLENBQUMsQ0FBQyxFQUFFLEtBQUssRUFBRSxFQUFFLEtBQUssRUFBRSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLElBQUksRUFBRSxDQUFDLEVBQUUsR0FBRyxLQUFLLENBQUMsRUFBRSxZQUFZLEVBQUUsSUFBSSxFQUFFLFVBQVUsRUFBRSxLQUFLLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7SUFDbkksT0FBTyxFQUFFLENBQUM7QUFDWixDQUFDO0FBRUQsaUZBQWlGO0FBQ2pGLE1BQU0sVUFBVSxhQUFhLENBQUMsRUFBVztJQUN2QyxJQUFJLE9BQU8sRUFBRSxLQUFLLFVBQVU7UUFBRSxPQUFPLEVBQUUsQ0FBQztJQUN4QyxPQUFRLEVBQWEsQ0FBQyxLQUFLLENBQUMsSUFBSSxFQUFFLENBQUM7QUFDckMsQ0FBQztBQUVEOzs7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SyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsYUFBYSxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDO0lBQzVELE9BQU87UUFDTCxNQUFNLEVBQUUsS0FBSyxDQUFDLE1BQU07UUFDcEIsTUFBTSxFQUFFLE1BQU0sQ0FBQyxNQUFNO1FBQ3JCLE9BQU8sRUFBRSxLQUFLLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLE1BQU07UUFDOUMsU0FBUyxFQUFFLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsTUFBTSxLQUFLLENBQUMsQ0FBQyxDQUFDLE1BQU07UUFDNUQsUUFBUSxFQUFFLEtBQUssQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxlQUFlLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQyxDQUFDLE1BQU07S0FDbkUsQ0FBQztBQUNKLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuLyoqXG4gKiBJbnRyb3NwZWN0YWJsZSByb3V0ZSB0YWJsZS5cbiAqXG4gKiBBdXRob3JpemF0aW9uIGlzIHNwbGl0IGJldHdlZW4gcm91dGVyIG1vdW50cyAoYGFwcC51c2UoJy94JywgZ2F0ZSwgcm91dGVyKWApXG4gKiBhbmQgaW5kaXZpZHVhbCByb3V0ZXMsIHNvIGdyZXBwaW5nIHNvdXJjZSBjYW4ndCBwcm92ZSB0aGF0IGV2ZXJ5IHJvdXRlIGlzXG4gKiBnYXRlZC4gSW5zdGVhZCwgZXZlcnkgZ2F0ZSBtaWRkbGV3YXJlIGluIHRoaXMgcGFja2FnZSBjYXJyaWVzIE1FVEFEQVRBICh3aGF0IGl0XG4gKiByZXF1aXJlcyksIGFuZCB7QGxpbmsgYnVpbGRSb3V0ZVRhYmxlfSB3YWxrcyBhbiBhc3NlbWJsZWQgRXhwcmVzcyBhcHAgYW5kXG4gKiByZXNvbHZlcywgcGVyIG1ldGhvZCArIHBhdGgsIHRoZSBmdWxsIGNoYWluIG9mIGdhdGVzIEV4cHJlc3Mgd291bGQgcnVuIGJlZm9yZVxuICogdGhlIGhhbmRsZXI6IHBlcm1pc3Npb25zLCBzeXN0ZW0tYWRtaW4sIHNlcnZpY2UgcHJpbmNpcGFsLCBzdGVwLXVwLCBmZWF0dXJlLFxuICogdG9rZW4gc2NvcGUsIGFuZCB0aGUgYXVkaXQgYWN0aW9ucyB0aGUgcm91dGUgZGVjbGFyZXMgdmlhIHtAbGluayBhdWRpdGVkfS5cbiAqXG4gKiBFeHByZXNzIDUgKHJvdXRlckAyKSBkb2VzIG5vdCBrZWVwIGEgbW91bnQncyBwYXRoIHN0cmluZyBvbiBpdHMgbGF5ZXIsIHNvIHRoaXNcbiAqIG1vZHVsZSByZWNvcmRzIGl0OiBvbiBpbXBvcnQgaXQgd3JhcHMgYFJvdXRlci5wcm90b3R5cGUudXNlYCB0byBzdGFtcCBlYWNoIG5ld1xuICogbGF5ZXIgd2l0aCB0aGUgcGF0aCBpdCB3YXMgbW91bnRlZCBhdC4gYXBpLWNvcmUgaXMgaW1wb3J0ZWQgYmVmb3JlIGFueSByb3V0ZVxuICogbW9kdWxlIHJlZ2lzdGVycyBhIHJvdXRlLCBzbyBldmVyeSBsYXllciBhbiBhcHAgYnVpbGRzIGlzIHN0YW1wZWQuIFRoZSB3cmFwXG4gKiBvbmx5IGFkZHMgYSBzeW1ib2wgcHJvcGVydHk7IHJvdXRpbmcgYmVoYXZpb3VyIGlzIHVuY2hhbmdlZC5cbiAqL1xuXG5pbXBvcnQgZXhwcmVzcyBmcm9tICdleHByZXNzJztcbmltcG9ydCB0eXBlIHsgUGVybWlzc2lvbiB9IGZyb20gJy4uL3R5cGVzL3Blcm1pc3Npb25zLmpzJztcblxuLyoqIE9uZSByZXF1aXJlbWVudCBhIGdhdGUgbWlkZGxld2FyZSBlbmZvcmNlcy4gKi9cbmV4cG9ydCB0eXBlIFJvdXRlR2F0ZSA9XG4gIHwgeyBraW5kOiAnYXV0aCcgfVxuICB8IHsga2luZDogJ3Blcm1pc3Npb24nOyBtb2RlOiAnYW55JyB8ICdhbGwnOyBwZXJtaXNzaW9uczogcmVhZG9ubHkgUGVybWlzc2lvbltdOyBhbGxvd1NlcnZpY2U/OiBib29sZWFuIH1cbiAgfCB7IGtpbmQ6ICdzeXN0ZW1BZG1pbicgfVxuICB8IHsga2luZDogJ3NlcnZpY2VQcmluY2lwYWwnIH1cbiAgLyoqIEFuIElOVEVSTkFMIHJvdXRlOiB1c2VyIHRva2VucyByZWZ1c2VkIG91dHJpZ2h0LCBvbmx5IHRoZXNlIHNlcnZpY2VzIGFkbWl0dGVkLiAqL1xuICB8IHsga2luZDogJ2ludGVybmFsU2VydmljZSc7IGNhbGxlcnM6IHJlYWRvbmx5IHN0cmluZ1tdIH1cbiAgfCB7IGtpbmQ6ICdzdGVwVXAnOyBtZXRob2RzPzogcmVhZG9ubHkgc3RyaW5nW10gfVxuICAvKiogVGhlIHdob2xlIFNFU1NJT04gbXVzdCBiZSBNRkEtZ3JhZGUgKGByZXF1aXJlQXV0aCh7IG1pbkFzc3VyYW5jZSB9KWApLCBhbmRcbiAgICogIG9wdGlvbmFsbHkgbm8gb2xkZXIgdGhhbiBgbWF4QWdlYCBzZWNvbmRzLiAqL1xuICB8IHsga2luZDogJ2Fzc3VyYW5jZSc7IG1pbkFzc3VyYW5jZTogMSB8IDI7IG1heEFnZT86IG51bWJlciB9XG4gIHwgeyBraW5kOiAnZmVhdHVyZSc7IGZlYXR1cmU6IHN0cmluZyB9XG4gIHwgeyBraW5kOiAnc2NvcGUnOyBzY29wZTogc3RyaW5nIH1cbiAgfCB7IGtpbmQ6ICdhdWRpdCc7IGFjdGlvbnM6IHJlYWRvbmx5IHN0cmluZ1tdIH07XG5cbi8vIFN5bWJvbC5mb3IsIG5vdCBTeW1ib2woKTogYSB0ZXN0IHRoYXQgbG9hZHMgYXBpLWNvcmUgdHdpY2UgKEVTTSArIGFcbi8vIHJlcXVpcmVBY3R1YWwgY29weSkgbXVzdCBzdGlsbCBzZWUgdGFncyBzZXQgYnkgdGhlIG90aGVyIGNvcHkuXG5jb25zdCBHQVRFUyA9IFN5bWJvbC5mb3IoJ3BpcGVsaW5lLWJ1aWxkZXIucm91dGUtZ2F0ZXMnKTtcbmNvbnN0IExBWUVSX1BBVEggPSBTeW1ib2wuZm9yKCdwaXBlbGluZS1idWlsZGVyLmxheWVyLXBhdGgnKTtcbmNvbnN0IFJFQ09SREVSX0lOU1RBTExFRCA9IFN5bWJvbC5mb3IoJ3BpcGVsaW5lLWJ1aWxkZXIucm91dGUtcmVjb3JkZXInKTtcblxudHlwZSBUYWdnZWQgPSB7IFtHQVRFU10/OiBSb3V0ZUdhdGVbXSB9O1xuXG4vKipcbiAqIEF0dGFjaCBnYXRlIG1ldGFkYXRhIHRvIGEgbWlkZGxld2FyZSBmdW5jdGlvbiAoYXBwZW5kcyB0byBhbnkgZXhpc3RpbmcgdGFncylcbiAqIGFuZCByZXR1cm4gdGhlIHNhbWUgZnVuY3Rpb24uIFVzZSBpdCBmb3IgYSBzZXJ2aWNlLWxvY2FsIGdhdGUgc28gdGhlIHJvdXRlXG4gKiB0YWJsZSBrbm93cyB3aGF0IGl0IGVuZm9yY2VzIOKAlCBlLmcuIGEgaGFuZGxlci1sZXZlbCBgcmVxdWlyZUNvbXBsaWFuY2VXcml0ZWBcbiAqIHRhZ2dlZCBgeyBraW5kOiAncGVybWlzc2lvbicsIG1vZGU6ICdhbnknLCBwZXJtaXNzaW9uczogWydjb21wbGlhbmNlOndyaXRlJ10gfWAuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB0YWdSb3V0ZUdhdGU8VCBleHRlbmRzIG9iamVjdD4oZm46IFQsIC4uLmdhdGVzOiBSb3V0ZUdhdGVbXSk6IFQge1xuICBjb25zdCB0ID0gZm4gYXMgVCAmIFRhZ2dlZDtcbiAgT2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIEdBVEVTLCB7IHZhbHVlOiBbLi4uKHRbR0FURVNdID8/IFtdKSwgLi4uZ2F0ZXNdLCBjb25maWd1cmFibGU6IHRydWUsIGVudW1lcmFibGU6IGZhbHNlLCB3cml0YWJsZTogdHJ1ZSB9KTtcbiAgcmV0dXJuIGZuO1xufVxuXG4vKiogVGhlIGdhdGUgbWV0YWRhdGEgYXR0YWNoZWQgdG8gYSBtaWRkbGV3YXJlIGZ1bmN0aW9uIChlbXB0eSB3aGVuIHVudGFnZ2VkKS4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRSb3V0ZUdhdGVzKGZuOiB1bmtub3duKTogcmVhZG9ubHkgUm91dGVHYXRlW10ge1xuICBpZiAodHlwZW9mIGZuICE9PSAnZnVuY3Rpb24nKSByZXR1cm4gW107XG4gIHJldHVybiAoZm4gYXMgVGFnZ2VkKVtHQVRFU10gPz8gW107XG59XG5cbi8qKlxuICogRGVjbGFyZSB0aGUgYXVkaXQgYWN0aW9uKHMpIGEgcm91dGUgZW1pdHMuIEEgbm8tb3AgbWlkZGxld2FyZSB3aG9zZSBvbmx5IGpvYlxuICogaXMgdG8gcHV0IHRoZSBhY3Rpb24gb24gdGhlIHJvdXRlIHRhYmxlLCBzbyB0aGUgY292ZXJhZ2UgdGVzdCBjYW4gcmVxdWlyZSBhblxuICogYXVkaXQgZGVjbGFyYXRpb24gb24gZXZlcnkgd3JpdGUgcm91dGUuIFBsYWNlIGl0IGluIHRoZSByb3V0ZSdzIGNoYWluOlxuICogYHJvdXRlci5wb3N0KCcvJywgYXVkaXRlZCgncGlwZWxpbmUuY3JlYXRlJyksIGhhbmRsZXIpYC4gVGhlIGhhbmRsZXIgc3RpbGxcbiAqIGVtaXRzIHRoZSBldmVudCBpdHNlbGYuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBhdWRpdGVkKC4uLmFjdGlvbnM6IHN0cmluZ1tdKTogKHJlcTogdW5rbm93biwgcmVzOiB1bmtub3duLCBuZXh0OiAoKSA9PiB2b2lkKSA9PiB2b2lkIHtcbiAgcmV0dXJuIHRhZ1JvdXRlR2F0ZShmdW5jdGlvbiBhdWRpdGVkUm91dGUoX3JlcTogdW5rbm93biwgX3JlczogdW5rbm93biwgbmV4dDogKCkgPT4gdm9pZCkgeyBuZXh0KCk7IH0sIHsga2luZDogJ2F1ZGl0JywgYWN0aW9ucyB9KTtcbn1cblxuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4vLyBNb3VudC1wYXRoIHJlY29yZGluZ1xuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbmludGVyZmFjZSBMYXllciB7XG4gIGhhbmRsZTogdW5rbm93bjtcbiAgcm91dGU/OiB7IHBhdGg6IHN0cmluZyB8IHN0cmluZ1tdOyBtZXRob2RzOiBSZWNvcmQ8c3RyaW5nLCBib29sZWFuPjsgc3RhY2s6IEFycmF5PHsgbWV0aG9kPzogc3RyaW5nOyBoYW5kbGU6IHVua25vd24gfT4gfTtcbiAgbWF0Y2hlcnM6IEFycmF5PChwYXRoOiBzdHJpbmcpID0+IHVua25vd24+O1xuICAvKiogcm91dGVyQDIncyBcIm1vdW50ZWQgYXQgJy8nIHdpdGggZW5kOmZhbHNlXCIgZmFzdCBwYXRoIOKAlCBtYXRjaGVzIEFOWSBwYXRoLiAqL1xuICBzbGFzaD86IGJvb2xlYW47XG4gIFtMQVlFUl9QQVRIXT86IHN0cmluZztcbn1cblxuaW50ZXJmYWNlIFJvdXRlckxpa2UgeyBzdGFjazogTGF5ZXJbXSB9XG5cbmZ1bmN0aW9uIGluc3RhbGxSb3V0ZVJlY29yZGVyKCk6IHZvaWQge1xuICBjb25zdCBwcm90byA9IChleHByZXNzLlJvdXRlciBhcyB1bmtub3duIGFzIHsgcHJvdG90eXBlOiBSZWNvcmQ8c3RyaW5nIHwgc3ltYm9sLCB1bmtub3duPiB9KS5wcm90b3R5cGU7XG4gIGlmIChwcm90b1tSRUNPUkRFUl9JTlNUQUxMRURdKSByZXR1cm47XG4gIGNvbnN0IG9yaWdpbmFsVXNlID0gcHJvdG8udXNlIGFzICh0aGlzOiBSb3V0ZXJMaWtlLCAuLi5hcmdzOiB1bmtub3duW10pID0+IHVua25vd247XG4gIHByb3RvLnVzZSA9IGZ1bmN0aW9uIHJlY29yZGluZ1VzZSh0aGlzOiBSb3V0ZXJMaWtlLCAuLi5hcmdzOiB1bmtub3duW10pOiB1bmtub3duIHtcbiAgICBjb25zdCBiZWZvcmUgPSB0aGlzLnN0YWNrLmxlbmd0aDtcbiAgICBjb25zdCByZXN1bHQgPSBvcmlnaW5hbFVzZS5hcHBseSh0aGlzLCBhcmdzKTtcbiAgICAvLyBTYW1lIHBhdGggZGlzYW1iaWd1YXRpb24gYXMgcm91dGVyQDIncyBgdXNlYDogYSBsZWFkaW5nIG5vbi1mdW5jdGlvblxuICAgIC8vIChwb3NzaWJseSBuZXN0ZWQgaW4gYXJyYXlzKSBpcyB0aGUgcGF0aDsgb3RoZXJ3aXNlIHRoZSBtb3VudCBpcyAnLycuXG4gICAgbGV0IGZpcnN0OiB1bmtub3duID0gYXJnc1swXTtcbiAgICB3aGlsZSAoQXJyYXkuaXNBcnJheShmaXJzdCkgJiYgZmlyc3QubGVuZ3RoID4gMCkgZmlyc3QgPSBmaXJzdFswXTtcbiAgICBjb25zdCBwYXRoID0gdHlwZW9mIGZpcnN0ID09PSAnZnVuY3Rpb24nID8gJy8nIDogU3RyaW5nKGFyZ3NbMF0pO1xuICAgIGZvciAobGV0IGkgPSBiZWZvcmU7IGkgPCB0aGlzLnN0YWNrLmxlbmd0aDsgaSsrKSB7XG4gICAgICBPYmplY3QuZGVmaW5lUHJvcGVydHkodGhpcy5zdGFja1tpXSwgTEFZRVJfUEFUSCwgeyB2YWx1ZTogcGF0aCwgZW51bWVyYWJsZTogZmFsc2UsIGNvbmZpZ3VyYWJsZTogdHJ1ZSB9KTtcbiAgICB9XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfTtcbiAgT2JqZWN0LmRlZmluZVByb3BlcnR5KHByb3RvLCBSRUNPUkRFUl9JTlNUQUxMRUQsIHsgdmFsdWU6IHRydWUsIGVudW1lcmFibGU6IGZhbHNlIH0pO1xufVxuXG5pbnN0YWxsUm91dGVSZWNvcmRlcigpO1xuXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbi8vIFRhYmxlIGNvbnN0cnVjdGlvblxuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi8qKiBSZXNvbHZlZCByZXF1aXJlbWVudHMgZm9yIG9uZSBtZXRob2QgKyBwYXRoLiAqL1xuZXhwb3J0IGludGVyZmFjZSBSb3V0ZVRhYmxlRW50cnkge1xuICBtZXRob2Q6IHN0cmluZztcbiAgcGF0aDogc3RyaW5nO1xuICAvKiogQXV0aGVudGljYXRpb24gcnVucyBiZWZvcmUgdGhlIGhhbmRsZXIgKHJlcXVpcmVBdXRoKS4gKi9cbiAgYXV0aDogYm9vbGVhbjtcbiAgLyoqIEVhY2ggcGVybWlzc2lvbiBnYXRlIGluIHRoZSBjaGFpbiAoYWxsIG11c3QgcGFzczsgZWFjaCBpcyBhbnktb2Ygb3IgYWxsLW9mKS4gKi9cbiAgcGVybWlzc2lvbnM6IEFycmF5PHsgbW9kZTogJ2FueScgfCAnYWxsJzsgcGVybWlzc2lvbnM6IFBlcm1pc3Npb25bXTsgYWxsb3dTZXJ2aWNlOiBib29sZWFuIH0+O1xuICBzeXN0ZW1BZG1pbjogYm9vbGVhbjtcbiAgc2VydmljZVByaW5jaXBhbDogYm9vbGVhbjtcbiAgLyoqXG4gICAqIFRoZSBzZXJ2aWNlcyBhbGxvd2VkIHRvIGNhbGwgdGhpcyByb3V0ZSB3aGVuIGl0IGlzIElOVEVSTkFMIChgcmVxdWlyZUludGVybmFsU2VydmljZWApLlxuICAgKiBBbiBlbXB0eSBhcnJheSBtZWFucyB0aGUgcm91dGUgaXMgbm90IGludGVybmFsIOKAlCBhIHVzZXIgdG9rZW4gbWF5IHJlYWNoIGl0LFxuICAgKiBzdWJqZWN0IHRvIHRoZSBvdGhlciBnYXRlcy4gU29ydGVkLCBzbyB0aGUgZ2VuZXJhdGVkIHRhYmxlIGlzIHN0YWJsZS5cbiAgICovXG4gIGludGVybmFsQ2FsbGVyczogc3RyaW5nW107XG4gIHN0ZXBVcDogYm9vbGVhbjtcbiAgLyoqIFdoZW4gdGhlIHN0ZXAtdXAgZ2F0ZSBuYW1lcyB0aGUgZmFjdG9ycyB0aGF0IG1heSBlYXJuIGl0IChgWyd3ZWJhdXRobicsXG4gICAqICAndG90cCddYCBvbiB0aGUgbW9zdCBkYW5nZXJvdXMgcm91dGVzKSwgdGhlIGFsbG93ZWQgbWV0aG9kcy4gRW1wdHkgbWVhbnNcbiAgICogIGFueSBmYWN0b3Igc2F0aXNmaWVzIGl0LiBTb3J0ZWQsIHNvIHRoZSBnZW5lcmF0ZWQgdGFibGUgaXMgc3RhYmxlLiAqL1xuICBzdGVwVXBNZXRob2RzOiBzdHJpbmdbXTtcbiAgLyoqIEhpZ2hlc3QgYG1pbkFzc3VyYW5jZWAgYW55IGdhdGUgaW4gdGhlIGNoYWluIGRlbWFuZHM7IDAgd2hlbiBub25lIGRvZXMuICovXG4gIG1pbkFzc3VyYW5jZTogMCB8IDEgfCAyO1xuICAvKiogVGlnaHRlc3QgYG1heEFnZWAgKHNlY29uZHMpIGFueSBhc3N1cmFuY2UgZ2F0ZSBpbiB0aGUgY2hhaW4gZGVtYW5kcy4gKi9cbiAgbWF4QWdlPzogbnVtYmVyO1xuICBmZWF0dXJlczogc3RyaW5nW107XG4gIHNjb3Blczogc3RyaW5nW107XG4gIGF1ZGl0OiBzdHJpbmdbXTtcbn1cblxuaW50ZXJmYWNlIENhbmRpZGF0ZSB7IGxheWVyOiBMYXllcjsgcHJlZml4OiBzdHJpbmcgfVxuXG5mdW5jdGlvbiBqb2luUGF0aChwcmVmaXg6IHN0cmluZywgcGF0aDogc3RyaW5nKTogc3RyaW5nIHtcbiAgY29uc3Qgam9pbmVkID0gYCR7cHJlZml4LnJlcGxhY2UoL1xcLyskLywgJycpfS8ke3BhdGgucmVwbGFjZSgvXlxcLysvLCAnJyl9YDtcbiAgcmV0dXJuIGpvaW5lZC5sZW5ndGggPiAxID8gam9pbmVkLnJlcGxhY2UoL1xcLyskLywgJycpIDogJy8nO1xufVxuXG5mdW5jdGlvbiBpc1JvdXRlcihoYW5kbGU6IHVua25vd24pOiBoYW5kbGUgaXMgUm91dGVyTGlrZSB7XG4gIHJldHVybiB0eXBlb2YgaGFuZGxlID09PSAnZnVuY3Rpb24nICYmIEFycmF5LmlzQXJyYXkoKGhhbmRsZSBhcyBQYXJ0aWFsPFJvdXRlckxpa2U+KS5zdGFjayk7XG59XG5cbi8qKlxuICogV2hldGhlciBhIG1pZGRsZXdhcmUgbGF5ZXIgKG1vdW50ZWQgdW5kZXIgYHByZWZpeGApIHJ1bnMgZm9yIGBmdWxsUGF0aGAuXG4gKiBNaXJyb3JzIGBMYXllci5wcm90b3R5cGUubWF0Y2hgLCBpbmNsdWRpbmcgaXRzIGZhc3QgcGF0aDogYSBsYXllciBhZGRlZCB3aXRoXG4gKiBubyBwYXRoIChgcm91dGVyLnVzZShnYXRlKWApIG1hdGNoZXMgRVZFUlkgcGF0aCB1bmRlciBpdHMgcm91dGVyIOKAlCB3aGljaCBpc1xuICogZXhhY3RseSBob3cgdGhlIHNoYXJlZCBgcmVxdWlyZVBlcm1pc3Npb25gIGdhdGVzIGJldHdlZW4gdHdvIGByb3V0ZXIudXNlKC4uLilgXG4gKiBtb3VudHMgcmVhY2ggdGhlIHJvdXRlcnMgcmVnaXN0ZXJlZCBhZnRlciB0aGVtLlxuICovXG5mdW5jdGlvbiBhcHBsaWVzKGM6IENhbmRpZGF0ZSwgZnVsbFBhdGg6IHN0cmluZyk6IGJvb2xlYW4ge1xuICBjb25zdCBiYXNlID0gYy5wcmVmaXggPT09ICcvJyA/ICcnIDogYy5wcmVmaXg7XG4gIGlmIChiYXNlICYmIGZ1bGxQYXRoICE9PSBiYXNlICYmICFmdWxsUGF0aC5zdGFydHNXaXRoKGAke2Jhc2V9L2ApKSByZXR1cm4gZmFsc2U7XG4gIGlmIChjLmxheWVyLnNsYXNoKSByZXR1cm4gdHJ1ZTtcbiAgY29uc3QgcmVsID0gZnVsbFBhdGguc2xpY2UoYmFzZS5sZW5ndGgpIHx8ICcvJztcbiAgcmV0dXJuIGMubGF5ZXIubWF0Y2hlcnMuc29tZSgobSkgPT4gbShyZWwpICE9PSBmYWxzZSk7XG59XG5cbmZ1bmN0aW9uIGNvbGxlY3RHYXRlcyhmbnM6IHVua25vd25bXSk6IFJvdXRlR2F0ZVtdIHtcbiAgcmV0dXJuIGZucy5mbGF0TWFwKChmbikgPT4gZ2V0Um91dGVHYXRlcyhmbikpO1xufVxuXG5mdW5jdGlvbiB0b0VudHJ5KG1ldGhvZDogc3RyaW5nLCBwYXRoOiBzdHJpbmcsIGdhdGVzOiBSb3V0ZUdhdGVbXSk6IFJvdXRlVGFibGVFbnRyeSB7XG4gIGNvbnN0IGVudHJ5OiBSb3V0ZVRhYmxlRW50cnkgPSB7XG4gICAgbWV0aG9kLFxuICAgIHBhdGgsXG4gICAgYXV0aDogZmFsc2UsXG4gICAgcGVybWlzc2lvbnM6IFtdLFxuICAgIHN5c3RlbUFkbWluOiBmYWxzZSxcbiAgICBzZXJ2aWNlUHJpbmNpcGFsOiBmYWxzZSxcbiAgICBpbnRlcm5hbENhbGxlcnM6IFtdLFxuICAgIHN0ZXBVcDogZmFsc2UsXG4gICAgc3RlcFVwTWV0aG9kczogW10sXG4gICAgbWluQXNzdXJhbmNlOiAwLFxuICAgIGZlYXR1cmVzOiBbXSxcbiAgICBzY29wZXM6IFtdLFxuICAgIGF1ZGl0OiBbXSxcbiAgfTtcbiAgZm9yIChjb25zdCBnIG9mIGdhdGVzKSB7XG4gICAgc3dpdGNoIChnLmtpbmQpIHtcbiAgICAgIGNhc2UgJ2F1dGgnOiBlbnRyeS5hdXRoID0gdHJ1ZTsgYnJlYWs7XG4gICAgICBjYXNlICdwZXJtaXNzaW9uJzogZW50cnkucGVybWlzc2lvbnMucHVzaCh7IG1vZGU6IGcubW9kZSwgcGVybWlzc2lvbnM6IFsuLi5nLnBlcm1pc3Npb25zXSwgYWxsb3dTZXJ2aWNlOiBnLmFsbG93U2VydmljZSA9PT0gdHJ1ZSB9KTsgYnJlYWs7XG4gICAgICBjYXNlICdzeXN0ZW1BZG1pbic6IGVudHJ5LnN5c3RlbUFkbWluID0gdHJ1ZTsgYnJlYWs7XG4gICAgICBjYXNlICdzZXJ2aWNlUHJpbmNpcGFsJzogZW50cnkuc2VydmljZVByaW5jaXBhbCA9IHRydWU7IGJyZWFrO1xuICAgICAgY2FzZSAnaW50ZXJuYWxTZXJ2aWNlJzogZm9yIChjb25zdCBjIG9mIGcuY2FsbGVycykgaWYgKCFlbnRyeS5pbnRlcm5hbENhbGxlcnMuaW5jbHVkZXMoYykpIGVudHJ5LmludGVybmFsQ2FsbGVycy5wdXNoKGMpOyBicmVhaztcbiAgICAgIGNhc2UgJ3N0ZXBVcCc6XG4gICAgICAgIGVudHJ5LnN0ZXBVcCA9IHRydWU7XG4gICAgICAgIGZvciAoY29uc3QgbSBvZiBnLm1ldGhvZHMgPz8gW10pIGlmICghZW50cnkuc3RlcFVwTWV0aG9kcy5pbmNsdWRlcyhtKSkgZW50cnkuc3RlcFVwTWV0aG9kcy5wdXNoKG0pO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgJ2Fzc3VyYW5jZSc6XG4gICAgICAgIC8vIFNldmVyYWwgZ2F0ZXMgbWF5IGFwcGx5OyB0aGUgU1RSSUNURVNUIHdpbnMsIHNpbmNlIGV2ZXJ5IG9uZSBvZiB0aGVtIHJ1bnMuXG4gICAgICAgIGlmIChnLm1pbkFzc3VyYW5jZSA+IGVudHJ5Lm1pbkFzc3VyYW5jZSkgZW50cnkubWluQXNzdXJhbmNlID0gZy5taW5Bc3N1cmFuY2U7XG4gICAgICAgIGlmIChnLm1heEFnZSAhPT0gdW5kZWZpbmVkKSBlbnRyeS5tYXhBZ2UgPSBlbnRyeS5tYXhBZ2UgPT09IHVuZGVmaW5lZCA/IGcubWF4QWdlIDogTWF0aC5taW4oZW50cnkubWF4QWdlLCBnLm1heEFnZSk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAnZmVhdHVyZSc6IGlmICghZW50cnkuZmVhdHVyZXMuaW5jbHVkZXMoZy5mZWF0dXJlKSkgZW50cnkuZmVhdHVyZXMucHVzaChnLmZlYXR1cmUpOyBicmVhaztcbiAgICAgIGNhc2UgJ3Njb3BlJzogaWYgKCFlbnRyeS5zY29wZXMuaW5jbHVkZXMoZy5zY29wZSkpIGVudHJ5LnNjb3Blcy5wdXNoKGcuc2NvcGUpOyBicmVhaztcbiAgICAgIGNhc2UgJ2F1ZGl0JzogZm9yIChjb25zdCBhIG9mIGcuYWN0aW9ucykgaWYgKCFlbnRyeS5hdWRpdC5pbmNsdWRlcyhhKSkgZW50cnkuYXVkaXQucHVzaChhKTsgYnJlYWs7XG4gICAgfVxuICB9XG4gIGVudHJ5LmludGVybmFsQ2FsbGVycy5zb3J0KCk7XG4gIGVudHJ5LnN0ZXBVcE1ldGhvZHMuc29ydCgpO1xuICByZXR1cm4gZW50cnk7XG59XG5cbmZ1bmN0aW9uIHdhbGsoc3RhY2s6IExheWVyW10sIHByZWZpeDogc3RyaW5nLCBpbmhlcml0ZWQ6IENhbmRpZGF0ZVtdLCBvdXQ6IFJvdXRlVGFibGVFbnRyeVtdLCBzZWVuOiBTZXQ8c3RyaW5nPik6IHZvaWQge1xuICBjb25zdCBsb2NhbDogQ2FuZGlkYXRlW10gPSBbXTtcbiAgZm9yIChjb25zdCBsYXllciBvZiBzdGFjaykge1xuICAgIGlmIChsYXllci5yb3V0ZSkge1xuICAgICAgY29uc3QgcGF0aHMgPSBBcnJheS5pc0FycmF5KGxheWVyLnJvdXRlLnBhdGgpID8gbGF5ZXIucm91dGUucGF0aCA6IFtsYXllci5yb3V0ZS5wYXRoXTtcbiAgICAgIGNvbnN0IG1ldGhvZHMgPSBPYmplY3Qua2V5cyhsYXllci5yb3V0ZS5tZXRob2RzKS5tYXAoKG0pID0+IChtID09PSAnX2FsbCcgPyAnKicgOiBtLnRvVXBwZXJDYXNlKCkpKTtcbiAgICAgIGZvciAoY29uc3Qgcm91dGVQYXRoIG9mIHBhdGhzKSB7XG4gICAgICAgIGNvbnN0IGZ1bGwgPSBqb2luUGF0aChwcmVmaXgsIFN0cmluZyhyb3V0ZVBhdGgpKTtcbiAgICAgICAgY29uc3QgY2hhaW4gPSBbLi4uaW5oZXJpdGVkLCAuLi5sb2NhbF0uZmlsdGVyKChjKSA9PiBhcHBsaWVzKGMsIGZ1bGwpKS5tYXAoKGMpID0+IGMubGF5ZXIuaGFuZGxlKTtcbiAgICAgICAgZm9yIChjb25zdCBtZXRob2Qgb2YgbWV0aG9kcykge1xuICAgICAgICAgIGNvbnN0IGtleSA9IGAke21ldGhvZH0gJHtmdWxsfWA7XG4gICAgICAgICAgLy8gRmlyc3QgcmVnaXN0cmF0aW9uIHdpbnMsIGFzIGluIEV4cHJlc3M6IGEgbGF0ZXIgZHVwbGljYXRlIGlzIHVucmVhY2hhYmxlLlxuICAgICAgICAgIGlmIChzZWVuLmhhcyhrZXkpKSBjb250aW51ZTtcbiAgICAgICAgICBzZWVuLmFkZChrZXkpO1xuICAgICAgICAgIGNvbnN0IHJvdXRlRm5zID0gbGF5ZXIucm91dGUuc3RhY2tcbiAgICAgICAgICAgIC5maWx0ZXIoKGwpID0+IGwubWV0aG9kID09PSB1bmRlZmluZWQgfHwgbC5tZXRob2QudG9VcHBlckNhc2UoKSA9PT0gbWV0aG9kIHx8IG1ldGhvZCA9PT0gJyonKVxuICAgICAgICAgICAgLm1hcCgobCkgPT4gbC5oYW5kbGUpO1xuICAgICAgICAgIG91dC5wdXNoKHRvRW50cnkobWV0aG9kLCBmdWxsLCBjb2xsZWN0R2F0ZXMoWy4uLmNoYWluLCAuLi5yb3V0ZUZuc10pKSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9IGVsc2UgaWYgKGlzUm91dGVyKGxheWVyLmhhbmRsZSkpIHtcbiAgICAgIGNvbnN0IG1vdW50UGF0aCA9IGpvaW5QYXRoKHByZWZpeCwgbGF5ZXJbTEFZRVJfUEFUSF0gPz8gJy8nKTtcbiAgICAgIC8vIE1pZGRsZXdhcmUgdGhhdCBydW5zIGJlZm9yZSB0aGlzIHJvdXRlciAoYW5jZXN0b3JzICsgZWFybGllciBzaWJsaW5ncylcbiAgICAgIC8vIGlzIGluaGVyaXRlZDsgYXBwbGljYWJpbGl0eSBpcyByZS1jaGVja2VkIGFnYWluc3QgZWFjaCBmdWxsIHJvdXRlIHBhdGguXG4gICAgICB3YWxrKGxheWVyLmhhbmRsZS5zdGFjaywgbW91bnRQYXRoLCBbLi4uaW5oZXJpdGVkLCAuLi5sb2NhbF0sIG91dCwgc2Vlbik7XG4gICAgfSBlbHNlIHtcbiAgICAgIGxvY2FsLnB1c2goeyBsYXllciwgcHJlZml4IH0pO1xuICAgIH1cbiAgfVxufVxuXG4vKipcbiAqIEJ1aWxkIHRoZSByb3V0ZSB0YWJsZSBvZiBhbiBhc3NlbWJsZWQgRXhwcmVzcyBhcHAgKG9yIHJvdXRlcik6IG9uZSBlbnRyeSBwZXJcbiAqIHJlYWNoYWJsZSBtZXRob2QgKyBwYXRoIHdpdGggZXZlcnkgZ2F0ZSB0aGF0IHJ1bnMgYmVmb3JlIGl0cyBoYW5kbGVyLlxuICogSEVBRCBpcyBmb2xkZWQgaW50byBHRVQsIGFzIEV4cHJlc3MgZG9lcy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGJ1aWxkUm91dGVUYWJsZShhcHA6IHVua25vd24pOiBSb3V0ZVRhYmxlRW50cnlbXSB7XG4gIGNvbnN0IHJvb3QgPSAoYXBwIGFzIHsgcm91dGVyPzogUm91dGVyTGlrZSB9KS5yb3V0ZXIgPz8gKGFwcCBhcyBSb3V0ZXJMaWtlKTtcbiAgY29uc3Qgb3V0OiBSb3V0ZVRhYmxlRW50cnlbXSA9IFtdO1xuICBpZiAoIXJvb3QgfHwgIUFycmF5LmlzQXJyYXkocm9vdC5zdGFjaykpIHJldHVybiBvdXQ7XG4gIHdhbGsocm9vdC5zdGFjaywgJy8nLCBbXSwgb3V0LCBuZXcgU2V0KCkpO1xuICByZXR1cm4gb3V0LnNvcnQoKGEsIGIpID0+IChhLnBhdGggPT09IGIucGF0aCA/IGEubWV0aG9kLmxvY2FsZUNvbXBhcmUoYi5tZXRob2QpIDogYS5wYXRoLmxvY2FsZUNvbXBhcmUoYi5wYXRoKSkpO1xufVxuXG4vKiogV2hldGhlciBhIG1ldGhvZCBtdXRhdGVzIHN0YXRlIChldmVyeXRoaW5nIGV4Y2VwdCBHRVQvSEVBRC9PUFRJT05TKS4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpc1dyaXRlTWV0aG9kKG1ldGhvZDogc3RyaW5nKTogYm9vbGVhbiB7XG4gIHJldHVybiAhWydHRVQnLCAnSEVBRCcsICdPUFRJT05TJ10uaW5jbHVkZXMobWV0aG9kLnRvVXBwZXJDYXNlKCkpO1xufVxuXG4vKiogU3VtbWFyeSBjb3VudHMgZm9yIGEgc3RhcnR1cCBsb2cgbGluZS4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzdW1tYXJpemVSb3V0ZVRhYmxlKHRhYmxlOiByZWFkb25seSBSb3V0ZVRhYmxlRW50cnlbXSk6IHsgcm91dGVzOiBudW1iZXI7IHdyaXRlczogbnVtYmVyOyB1bmdhdGVkOiBudW1iZXI7IHVuYXVkaXRlZDogbnVtYmVyOyBpbnRlcm5hbDogbnVtYmVyIH0ge1xuICBjb25zdCBnYXRlZCA9IChlOiBSb3V0ZVRhYmxlRW50cnkpOiBib29sZWFuID0+IGUucGVybWlzc2lvbnMubGVuZ3RoID4gMCB8fCBlLnN5c3RlbUFkbWluIHx8IGUuc2VydmljZVByaW5jaXBhbDtcbiAgY29uc3Qgd3JpdGVzID0gdGFibGUuZmlsdGVyKChlKSA9PiBpc1dyaXRlTWV0aG9kKGUubWV0aG9kKSk7XG4gIHJldHVybiB7XG4gICAgcm91dGVzOiB0YWJsZS5sZW5ndGgsXG4gICAgd3JpdGVzOiB3cml0ZXMubGVuZ3RoLFxuICAgIHVuZ2F0ZWQ6IHRhYmxlLmZpbHRlcigoZSkgPT4gIWdhdGVkKGUpKS5sZW5ndGgsXG4gICAgdW5hdWRpdGVkOiB3cml0ZXMuZmlsdGVyKChlKSA9PiBlLmF1ZGl0Lmxlbmd0aCA9PT0gMCkubGVuZ3RoLFxuICAgIGludGVybmFsOiB0YWJsZS5maWx0ZXIoKGUpID0+IGUuaW50ZXJuYWxDYWxsZXJzLmxlbmd0aCA+IDApLmxlbmd0aCxcbiAgfTtcbn1cbiJdfQ==
@@ -2,9 +2,10 @@
2
2
  * `requireStepUp` — a shared step-up (recent-password-reverify) gate for the
3
3
  * stateless api services, mirroring the platform middleware of the same name.
4
4
  *
5
- * Flow: the UI hits `POST /api/auth/step-up` (platform) to re-verify the user's
6
- * password; platform mints a short-lived (60s) `step-up` JWT signed with the
7
- * SAME `JWT_SECRET` these services already verify access tokens with. The UI
5
+ * Flow: the UI hits `POST /api/auth/step-up` (platform) to re-verify the user;
6
+ * platform mints a short-lived (60s) `step-up` JWT with the SAME ES256 signing
7
+ * key it mints access tokens with, so these services verify it through the same
8
+ * JWKS. The UI
8
9
  * replays it as the `X-Step-Up-Token` header on the destructive/sensitive call;
9
10
  * this middleware verifies it, binds it to the caller (`sub` match), and
10
11
  * consumes its `jti` once (Redis-backed cross-instance when configured, else a
@@ -15,30 +16,54 @@
15
16
  * verify + single-use jti into api-core so any service can require it.
16
17
  */
17
18
  import type { Request, Response, NextFunction } from 'express';
19
+ /**
20
+ * How a step-up token was earned. Mirrors platform's `StepUpMethod`; kept here
21
+ * as its own union so a verifier can name the factors it accepts without
22
+ * importing platform.
23
+ */
24
+ export type StepUpMethod = 'password' | 'webauthn' | 'totp' | 'reauth';
25
+ /**
26
+ * The factors that count as a SECOND FACTOR, for routes that may only be
27
+ * unlocked by one (#8: impersonation, KMS, IdP settings, platform admin).
28
+ *
29
+ * A password or a provider re-auth proves the same single factor the session was
30
+ * already opened with, so on those routes they are not enough: the point is that
31
+ * someone holding only a stolen session — or only a stolen password — cannot
32
+ * proceed.
33
+ */
34
+ export declare const STRONG_STEP_UP_METHODS: readonly StepUpMethod[];
18
35
  /** Payload of a platform-issued step-up token (see platform issueStepUpToken). */
19
36
  export interface StepUpTokenPayload {
20
37
  type: 'step-up';
21
38
  sub: string;
22
39
  jti: string;
40
+ /** Which factor earned it. Absent only on a token minted before the claim
41
+ * existed, which a method-restricted gate treats as "not proven". */
42
+ method?: StepUpMethod;
23
43
  iat: number;
24
44
  exp: number;
25
45
  }
46
+ /** Options for {@link requireStepUp}. */
47
+ export interface RequireStepUpOptions {
48
+ /** Restrict which factors may earn the step-up (e.g. {@link STRONG_STEP_UP_METHODS}).
49
+ * Omitted means any factor the platform will issue a token for. */
50
+ methods?: readonly StepUpMethod[];
51
+ }
26
52
  /**
27
- * Verify a step-up token: valid signature (primary `JWT_SECRET`, or
28
- * `JWT_SECRET_PREVIOUS` during a rotation), allow-listed algorithm, issuer and
29
- * audience when configured, AND the `type: 'step-up'` + `jti` claims — a plain
30
- * access token shares the secret and `sub`, so without asserting the step-up
31
- * shape it would bypass the gate. Throws on any failure. Does NOT bind to a
32
- * caller — `requireStepUp` does the `sub` match and single-use consume.
53
+ * Verify a step-up token: a valid ES256 signature from one of platform's
54
+ * published signing keys, issuer and audience when configured, AND the
55
+ * `type: 'step-up'` + `jti` claims — a plain access token is signed by the same
56
+ * key and carries the same `sub`, so without asserting the step-up shape it
57
+ * would bypass the gate. Throws on any failure (an unfetchable key set included:
58
+ * an unverifiable token is not a step-up). Does NOT bind to a caller —
59
+ * `requireStepUp` does the `sub` match and single-use consume.
33
60
  *
34
- * Delegates to `requireAuth`'s own primitives rather than re-implementing them.
35
- * The hand-rolled copy this replaced dropped TWO of their guards: it never
36
- * pinned issuer/audience (so a step-up token from any other system sharing
37
- * `JWT_SECRET` was accepted wherever `JWT_ISSUER`/`JWT_AUDIENCE` are set), and
38
- * its previous-secret retry had no expiry/not-before carve-out, so an EXPIRED
39
- * token was retried and surfaced as a signature error instead of an expiry one.
61
+ * Delegates to `requireAuth`'s own primitive (`verifyUserJwt`) rather than
62
+ * re-implementing it. The hand-rolled copy this replaced dropped TWO of its
63
+ * guards: it never pinned issuer/audience, and its previous-secret retry had no
64
+ * expiry/not-before carve-out, so an EXPIRED token surfaced as a signature error.
40
65
  */
41
- export declare function verifyStepUpToken(token: string): StepUpTokenPayload;
66
+ export declare function verifyStepUpToken(token: string): Promise<StepUpTokenPayload>;
42
67
  /**
43
68
  * Consume a step-up `jti` exactly once. Returns true if this call claimed it
44
69
  * (first use), false if it was already consumed (replay). `expEpochSeconds` is
@@ -48,5 +73,12 @@ export declare function consumeStepUpJti(jti: string, expEpochSeconds: number):
48
73
  /**
49
74
  * Express middleware: require a valid, caller-bound, single-use step-up token in
50
75
  * the `X-Step-Up-Token` header. Must run AFTER `requireAuth` (needs `req.user`).
76
+ *
77
+ * Use directly (`router.post('/x', requireAuth, requireStepUp, h)`) for the
78
+ * ordinary any-factor gate, or call it with `{ methods }` to demand that the
79
+ * confirmation was earned by a specific factor — `requireStepUp({ methods:
80
+ * STRONG_STEP_UP_METHODS })` on the routes where a password re-prompt is not a
81
+ * meaningful second proof.
51
82
  */
52
83
  export declare function requireStepUp(req: Request, res: Response, next: NextFunction): Promise<void>;
84
+ export declare function requireStepUp(options: RequireStepUpOptions): (req: Request, res: Response, next: NextFunction) => void;