fastify-txstate 3.6.9 → 4.0.1
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/README.md +392 -7
- package/lib/analytics.d.ts +5 -2
- package/lib/analytics.js +28 -33
- package/lib/error.d.ts +1 -10
- package/lib/error.js +7 -28
- package/lib/filestorage.d.ts +1 -1
- package/lib/filestorage.js +27 -31
- package/lib/index.d.ts +9 -194
- package/lib/index.js +9 -422
- package/lib/jwt-auth.d.ts +98 -0
- package/lib/jwt-auth.js +491 -0
- package/lib/oauth.d.ts +49 -0
- package/lib/oauth.js +272 -0
- package/lib/postformdata.js +13 -17
- package/{lib-esm/index.d.ts → lib/server.d.ts} +69 -37
- package/lib/server.js +441 -0
- package/lib/unified-auth.d.ts +13 -7
- package/lib/unified-auth.js +48 -174
- package/package.json +27 -25
- package/lib-esm/index.js +0 -20
- package/lib-esm/package.json +0 -3
package/lib/unified-auth.js
CHANGED
|
@@ -1,178 +1,34 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
exports.registerUaCookieRoutes = registerUaCookieRoutes;
|
|
7
|
-
const crypto_1 = require("crypto");
|
|
8
|
-
const jose_1 = require("jose");
|
|
9
|
-
const txstate_utils_1 = require("txstate-utils");
|
|
10
|
-
let hasInit = false;
|
|
11
|
-
const issuerKeys = new Map();
|
|
12
|
-
const issuerConfig = new Map();
|
|
13
|
-
const trustedClients = new Set();
|
|
14
|
-
const uaCookieName = process.env.UA_COOKIE_NAME ?? (0, crypto_1.randomBytes)(16).toString('hex');
|
|
15
|
-
const uaCookieNameRegex = new RegExp(`${uaCookieName}=([^;]+)`);
|
|
16
|
-
const uaServiceUrl = (0, txstate_utils_1.isNotBlank)(process.env.PUBLIC_URL) ? process.env.PUBLIC_URL + (process.env.PUBLIC_URL.endsWith('/') ? '' : '/') + '.uaService' : undefined;
|
|
17
|
-
const tokenCache = new txstate_utils_1.Cache(async (token, req) => {
|
|
18
|
-
const claims = (0, jose_1.decodeJwt)(token);
|
|
19
|
-
let verifyKey;
|
|
20
|
-
if (claims.iss && issuerKeys.has(claims.iss))
|
|
21
|
-
verifyKey = issuerKeys.get(claims.iss);
|
|
22
|
-
if (!verifyKey) {
|
|
23
|
-
req.log.warn(`Received token with issuer: ${claims.iss} but JWT secret could not be found. The server may be misconfigured or the user may have presented a JWT from an untrusted issuer.`);
|
|
24
|
-
return undefined;
|
|
25
|
-
}
|
|
26
|
-
try {
|
|
27
|
-
const { payload } = await (0, jose_1.jwtVerify)(token, verifyKey);
|
|
28
|
-
if (trustedClients.size && !trustedClients.has(payload.client_id)) {
|
|
29
|
-
req.log.warn(`Received token with untrusted client_id: ${payload.client_id}.`);
|
|
30
|
-
return undefined;
|
|
31
|
-
}
|
|
32
|
-
return payload;
|
|
33
|
-
}
|
|
34
|
-
catch (e) {
|
|
35
|
-
// squelch errors about bad tokens, we can already see the 401 in the log
|
|
36
|
-
if (e.code !== 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED')
|
|
37
|
-
req.log.error(e);
|
|
38
|
-
return undefined;
|
|
39
|
-
}
|
|
40
|
-
}, { freshseconds: 3600 });
|
|
41
|
-
const validateCache = new txstate_utils_1.Cache(async (token, payload) => {
|
|
42
|
-
const config = issuerConfig.get(payload.iss);
|
|
43
|
-
if (!config?.validateUrl)
|
|
44
|
-
return;
|
|
45
|
-
// avoid checking for deauth until the token is more than 5 minutes old
|
|
46
|
-
if (new Date(payload.iat * 1000) > new Date(new Date().getTime() - 1000 * 60 * 5))
|
|
47
|
-
return;
|
|
48
|
-
const validateUrl = new URL(config.validateUrl);
|
|
49
|
-
validateUrl.searchParams.set('unifiedJwt', token);
|
|
50
|
-
const resp = await fetch(validateUrl);
|
|
51
|
-
const validate = await resp.json();
|
|
52
|
-
if (!validate.valid)
|
|
53
|
-
throw new Error(validate.reason ?? 'Your session has been ended on another device or in another browser tab/window. It\'s also possible your NetID is no longer active.');
|
|
54
|
-
});
|
|
55
|
-
const jwkCache = new txstate_utils_1.Cache(async (url) => {
|
|
56
|
-
const { keys } = await (await fetch(url)).json();
|
|
57
|
-
const publicKeyByKid = {};
|
|
58
|
-
for (const jwk of keys) {
|
|
59
|
-
if (jwk.kid)
|
|
60
|
-
publicKeyByKid[jwk.kid] = await (0, jose_1.importJWK)(jwk);
|
|
61
|
-
}
|
|
62
|
-
return publicKeyByKid;
|
|
63
|
-
});
|
|
64
|
-
function remoteJWKSet(jwkUrl) {
|
|
65
|
-
return async (protectedHeader) => {
|
|
66
|
-
const publicKeyByKid = await jwkCache.get(jwkUrl);
|
|
67
|
-
return publicKeyByKid[protectedHeader.kid];
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
function processIssuerConfig(config) {
|
|
71
|
-
if (config.iss === 'unified-auth') {
|
|
72
|
-
const validateUrl = (0, txstate_utils_1.isNotBlank)(config.validateUrl)
|
|
73
|
-
? new URL(config.validateUrl, config.url)
|
|
74
|
-
: new URL('validateToken', config.url);
|
|
75
|
-
const logoutUrl = (0, txstate_utils_1.isNotBlank)(config.logoutUrl)
|
|
76
|
-
? new URL(config.logoutUrl, config.url)
|
|
77
|
-
: (0, txstate_utils_1.isNotBlank)(process.env.UA_URL)
|
|
78
|
-
? new URL(process.env.UA_URL + '/logout')
|
|
79
|
-
: new URL('logout', config.url);
|
|
80
|
-
return {
|
|
81
|
-
...config,
|
|
82
|
-
validateUrl,
|
|
83
|
-
logoutUrl
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
return {
|
|
87
|
-
...config,
|
|
88
|
-
validateUrl: undefined,
|
|
89
|
-
logoutUrl: config.logoutUrl ? new URL(config.logoutUrl, config.url) : undefined
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
function init() {
|
|
93
|
-
hasInit = true;
|
|
94
|
-
if (process.env.JWT_TRUSTED_ISSUERS) {
|
|
95
|
-
const issuers = (0, txstate_utils_1.toArray)(JSON.parse(process.env.JWT_TRUSTED_ISSUERS));
|
|
96
|
-
for (const issuer of issuers) {
|
|
97
|
-
issuerConfig.set(issuer.iss, processIssuerConfig(issuer));
|
|
98
|
-
if (issuer.iss === 'unified-auth')
|
|
99
|
-
issuerKeys.set(issuer.iss, remoteJWKSet(issuer.url));
|
|
100
|
-
else if (issuer.url)
|
|
101
|
-
issuerKeys.set(issuer.iss, (0, jose_1.createRemoteJWKSet)(new URL(issuer.url)));
|
|
102
|
-
else if (issuer.publicKey)
|
|
103
|
-
issuerKeys.set(issuer.iss, (0, crypto_1.createPublicKey)(issuer.publicKey));
|
|
104
|
-
else if (issuer.secret)
|
|
105
|
-
issuerKeys.set(issuer.iss, (0, crypto_1.createSecretKey)(Buffer.from(issuer.secret, 'ascii')));
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
for (const clientId of (process.env.JWT_TRUSTED_CLIENTIDS?.split(',').filter(txstate_utils_1.isNotBlank).map(clientId => clientId.trim()) ?? [])) {
|
|
109
|
-
trustedClients.add(clientId);
|
|
110
|
-
}
|
|
1
|
+
import { htmlEncode, isBlank, isNotBlank } from 'txstate-utils';
|
|
2
|
+
import { getIssuerConfig, jwtAuthenticate, registeredExceptRoutes, registeredOptionalRoutes, uaCookieName } from "./jwt-auth.js";
|
|
3
|
+
import { apiBaseUrl, uiBaseUrl } from "./server.js";
|
|
4
|
+
function uaServiceUrl(req) {
|
|
5
|
+
return apiBaseUrl(req) + '/.uaService';
|
|
111
6
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
async function unifiedAuthenticateInternal(req) {
|
|
121
|
-
if (!hasInit)
|
|
122
|
-
init();
|
|
123
|
-
const token = tokenFromReq(req);
|
|
124
|
-
if (!token)
|
|
125
|
-
return undefined;
|
|
126
|
-
const payload = await tokenCache.get(token, req);
|
|
127
|
-
if (!payload)
|
|
128
|
-
return undefined;
|
|
129
|
-
await validateCache.get(token, payload);
|
|
130
|
-
req.token = token;
|
|
131
|
-
return {
|
|
132
|
-
token,
|
|
133
|
-
issuerConfig: payload.iss ? issuerConfig.get(payload.iss) : undefined,
|
|
134
|
-
username: payload.sub,
|
|
135
|
-
sessionId: payload.sub + '-' + payload.iat,
|
|
136
|
-
sessionCreatedAt: payload.iat ? new Date(payload.iat * 1000) : undefined,
|
|
137
|
-
clientId: payload.client_id,
|
|
138
|
-
impersonatedBy: payload.act?.sub,
|
|
139
|
-
scope: payload.scope
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
async function unifiedAuthenticate(req, options) {
|
|
143
|
-
const auth = await unifiedAuthenticateInternal(req);
|
|
144
|
-
if (options?.usingUaCookieRoutes) {
|
|
145
|
-
options.exceptRoutes ??= new Set();
|
|
146
|
-
options.exceptRoutes.add('/.uaService');
|
|
147
|
-
options.exceptRoutes.add('/.uaRedirect');
|
|
148
|
-
options.optionalRoutes ??= new Set();
|
|
149
|
-
options.optionalRoutes.add('/.uaLogout');
|
|
150
|
-
}
|
|
151
|
-
const isNoAuthenticationRoute = options?.exceptRoutes?.has(req.routeOptions.url);
|
|
152
|
-
const requiresAuthenticationRoute = options?.authenticateAll &&
|
|
153
|
-
!options?.exceptRoutes?.has(req.routeOptions.url) &&
|
|
154
|
-
!options?.optionalRoutes?.has(req.routeOptions.url);
|
|
155
|
-
if (requiresAuthenticationRoute && (0, txstate_utils_1.isBlank)(auth?.username)) {
|
|
156
|
-
throw new Error('Request requires authentication.');
|
|
157
|
-
}
|
|
158
|
-
return isNoAuthenticationRoute ? undefined : auth;
|
|
7
|
+
/**
|
|
8
|
+
* @deprecated Use `jwtAuthenticate(options)` instead. Note the new shape: `jwtAuthenticate`
|
|
9
|
+
* is now a factory that takes options up front and returns the authenticator function
|
|
10
|
+
* (`authenticate: jwtAuthenticate({ authenticateAll: true })`), so the options actually
|
|
11
|
+
* take effect when wired into `new Server({ authenticate })`.
|
|
12
|
+
*/
|
|
13
|
+
export async function unifiedAuthenticate(req, options) {
|
|
14
|
+
return await jwtAuthenticate(options)(req);
|
|
159
15
|
}
|
|
160
16
|
/**
|
|
161
|
-
* @deprecated Use
|
|
17
|
+
* @deprecated Use `jwtAuthenticate({ authenticateAll: true })` instead.
|
|
162
18
|
*/
|
|
163
|
-
async function unifiedAuthenticateAll(req) {
|
|
164
|
-
return (await
|
|
19
|
+
export async function unifiedAuthenticateAll(req) {
|
|
20
|
+
return (await jwtAuthenticate({ authenticateAll: true })(req));
|
|
165
21
|
}
|
|
166
22
|
/**
|
|
167
23
|
* This function is available for server-side view code instead of a client-side application
|
|
168
24
|
* using a framework. It will automatically redirect the user to the Unified Auth login page
|
|
169
25
|
* and return true if they are not authenticated. Otherwise it simply returns false.
|
|
170
26
|
*/
|
|
171
|
-
async function
|
|
172
|
-
if (
|
|
27
|
+
export async function requireCookieAuthUa(req, res) {
|
|
28
|
+
if (isBlank(req.auth?.username)) {
|
|
173
29
|
const loginUrl = new URL(process.env.UA_URL + '/login');
|
|
174
|
-
loginUrl.searchParams.set('clientId', process.env.UA_CLIENTID);
|
|
175
|
-
loginUrl.searchParams.set('returnUrl', uaServiceUrl
|
|
30
|
+
loginUrl.searchParams.set('clientId', (process.env.UA_COOKIE_CLIENTID ?? process.env.UA_CLIENTID));
|
|
31
|
+
loginUrl.searchParams.set('returnUrl', uaServiceUrl(req));
|
|
176
32
|
loginUrl.searchParams.set('requestedUrl', req.originalUrl);
|
|
177
33
|
void res.redirect(loginUrl.toString());
|
|
178
34
|
return true;
|
|
@@ -181,7 +37,16 @@ async function requireCookieAuth(req, res) {
|
|
|
181
37
|
return false;
|
|
182
38
|
}
|
|
183
39
|
}
|
|
184
|
-
|
|
40
|
+
/**
|
|
41
|
+
* @deprecated Use requireCookieAuthUa instead.
|
|
42
|
+
*/
|
|
43
|
+
export async function requireCookieAuth(req, res) {
|
|
44
|
+
return await requireCookieAuthUa(req, res);
|
|
45
|
+
}
|
|
46
|
+
export function registerUaCookieRoutes(app) {
|
|
47
|
+
registeredExceptRoutes.add('/.uaService');
|
|
48
|
+
registeredExceptRoutes.add('/.uaRedirect');
|
|
49
|
+
registeredOptionalRoutes.add('/.uaLogout');
|
|
185
50
|
app.get('/.uaLogout', {
|
|
186
51
|
schema: {
|
|
187
52
|
headers: {
|
|
@@ -193,15 +58,15 @@ function registerUaCookieRoutes(app) {
|
|
|
193
58
|
}
|
|
194
59
|
}
|
|
195
60
|
}, async (req, res) => {
|
|
196
|
-
const redirectUrl = req.auth?.issuerConfig?.logoutUrl &&
|
|
61
|
+
const redirectUrl = req.auth?.issuerConfig?.logoutUrl && isNotBlank(req.auth.token)
|
|
197
62
|
? `${req.auth.issuerConfig.logoutUrl.toString()}?unifiedJwt=${encodeURIComponent(req.auth.token)}`
|
|
198
|
-
: (
|
|
63
|
+
: uiBaseUrl(req);
|
|
199
64
|
void res.header('Set-Cookie', `${uaCookieName}=; Path=/; Secure; HttpOnly; SameSite=Lax; Expires=Thu, 01 Jan 1970 00:00:00 GMT`);
|
|
200
65
|
return `<!DOCTYPE html>
|
|
201
66
|
<html lang="en">
|
|
202
67
|
<head>
|
|
203
68
|
<meta charset="UTF-8">
|
|
204
|
-
<meta http-equiv="refresh" content="0; url=${
|
|
69
|
+
<meta http-equiv="refresh" content="0; url=${htmlEncode(redirectUrl)}">
|
|
205
70
|
<title>Logging out...</title>
|
|
206
71
|
</head>
|
|
207
72
|
<body>
|
|
@@ -221,13 +86,18 @@ function registerUaCookieRoutes(app) {
|
|
|
221
86
|
}
|
|
222
87
|
}
|
|
223
88
|
}, async (req, res) => {
|
|
89
|
+
const destination = req.query.requestedUrl ?? uiBaseUrl(req);
|
|
90
|
+
if (req.query.requestedUrl && req.originChecker && !req.originChecker.check(req.query.requestedUrl, req.hostname)) {
|
|
91
|
+
void res.status(403);
|
|
92
|
+
return 'Requested URL failed origin check.';
|
|
93
|
+
}
|
|
224
94
|
void res.header('Set-Cookie', `${uaCookieName}=${req.query.unifiedJwt}; Path=/; Secure; HttpOnly; SameSite=Lax`);
|
|
225
95
|
void res.type('text/html');
|
|
226
96
|
return `<!DOCTYPE html>
|
|
227
97
|
<html lang="en">
|
|
228
98
|
<head>
|
|
229
99
|
<meta charset="UTF-8">
|
|
230
|
-
<meta http-equiv="refresh" content="0; url=${
|
|
100
|
+
<meta http-equiv="refresh" content="0; url=${htmlEncode(destination)}">
|
|
231
101
|
<title>Logging in...</title>
|
|
232
102
|
</head>
|
|
233
103
|
<body>
|
|
@@ -252,14 +122,18 @@ function registerUaCookieRoutes(app) {
|
|
|
252
122
|
}
|
|
253
123
|
}
|
|
254
124
|
}, async (req, res) => {
|
|
255
|
-
|
|
125
|
+
if (req.query.requestedUrl && req.originChecker && !req.originChecker.check(req.query.requestedUrl, req.hostname)) {
|
|
126
|
+
void res.status(403);
|
|
127
|
+
return 'Requested URL failed origin check.';
|
|
128
|
+
}
|
|
129
|
+
const loginUrl = isNotBlank(process.env.UA_URL)
|
|
256
130
|
? new URL(process.env.UA_URL + '/login')
|
|
257
|
-
: new URL('login',
|
|
258
|
-
loginUrl.searchParams.set('clientId', process.env.UA_CLIENTID ?? process.env.JWT_TRUSTED_CLIENTIDS.split(',')[0]);
|
|
259
|
-
const returnUrl = uaServiceUrl
|
|
131
|
+
: new URL('login', getIssuerConfig('unified-auth')?.url);
|
|
132
|
+
loginUrl.searchParams.set('clientId', process.env.UA_COOKIE_CLIENTID ?? process.env.UA_CLIENTID ?? process.env.JWT_TRUSTED_CLIENTIDS.split(',')[0]);
|
|
133
|
+
const returnUrl = uaServiceUrl(req);
|
|
260
134
|
loginUrl.searchParams.set('returnUrl', returnUrl);
|
|
261
135
|
if (req.query.requestedUrl)
|
|
262
136
|
loginUrl.searchParams.set('requestedUrl', req.query.requestedUrl);
|
|
263
|
-
return res.redirect(loginUrl.toString());
|
|
137
|
+
return await res.redirect(loginUrl.toString());
|
|
264
138
|
});
|
|
265
139
|
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fastify-txstate",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.1",
|
|
4
4
|
"description": "A small wrapper for fastify providing a set of common conventions & utility functions we use.",
|
|
5
|
+
"type": "module",
|
|
5
6
|
"exports": {
|
|
6
7
|
".": {
|
|
7
8
|
"types": "./lib/index.d.ts",
|
|
8
|
-
"
|
|
9
|
-
"import": "./lib-esm/index.js"
|
|
9
|
+
"import": "./lib/index.js"
|
|
10
10
|
}
|
|
11
11
|
},
|
|
12
12
|
"types": "./lib/index.d.ts",
|
|
@@ -14,37 +14,37 @@
|
|
|
14
14
|
"prepublishOnly": "npm run build",
|
|
15
15
|
"build": "rm -rf lib && tsc",
|
|
16
16
|
"test": "./test.sh",
|
|
17
|
-
"
|
|
18
|
-
"testserver": "node -
|
|
17
|
+
"nodetest": "node --experimental-transform-types --no-warnings --test test/**/*.ts",
|
|
18
|
+
"testserver": "node --experimental-transform-types --no-warnings testserver/index.ts",
|
|
19
|
+
"lint": "eslint src test testserver"
|
|
19
20
|
},
|
|
20
21
|
"dependencies": {
|
|
21
|
-
"@elastic/elasticsearch": "^
|
|
22
|
-
"@fastify/swagger": "^
|
|
23
|
-
"@fastify/swagger-ui": "^
|
|
24
|
-
"@fastify/type-provider-json-schema-to-ts": "^
|
|
22
|
+
"@elastic/elasticsearch": "^9.0.0",
|
|
23
|
+
"@fastify/swagger": "^9.0.0",
|
|
24
|
+
"@fastify/swagger-ui": "^5.0.0",
|
|
25
|
+
"@fastify/type-provider-json-schema-to-ts": "^5.0.0",
|
|
25
26
|
"@txstate-mws/fastify-shared": "^1.0.9",
|
|
26
|
-
"
|
|
27
|
+
"ajv": "^8.20.0",
|
|
27
28
|
"ajv-errors": "^3.0.0",
|
|
28
29
|
"ajv-formats": "^3.0.0",
|
|
29
|
-
"fastify": "^
|
|
30
|
-
"fastify-plugin": "^4.5.1",
|
|
30
|
+
"fastify": "^5.0.0",
|
|
31
31
|
"http-status-codes": "^2.1.4",
|
|
32
|
-
"jose": "^
|
|
32
|
+
"jose": "^6.0.0",
|
|
33
|
+
"openapi-types": "^12.1.3",
|
|
34
|
+
"pino": "^10.3.1",
|
|
33
35
|
"txstate-utils": "^1.9.5",
|
|
34
|
-
"ua-parser-js": "^
|
|
36
|
+
"ua-parser-js": "^2.0.0"
|
|
35
37
|
},
|
|
36
38
|
"devDependencies": {
|
|
37
|
-
"@fastify/multipart": "^
|
|
38
|
-
"@
|
|
39
|
-
"@types/
|
|
39
|
+
"@fastify/multipart": "^10.0.0",
|
|
40
|
+
"@stylistic/eslint-plugin": "^5.0.0",
|
|
41
|
+
"@types/chai": "^5.2.3",
|
|
40
42
|
"@types/node": "^24.0.0",
|
|
41
|
-
"axios": "^1.
|
|
42
|
-
"chai": "^
|
|
43
|
-
"eslint-config-
|
|
43
|
+
"axios": "^1.15.0",
|
|
44
|
+
"chai": "^6.0.0",
|
|
45
|
+
"eslint-config-love": "^154.0.0",
|
|
44
46
|
"json-schema-to-ts": "^3.0.1",
|
|
45
|
-
"
|
|
46
|
-
"ts-node": "^10.2.1",
|
|
47
|
-
"typescript": "^5.0.4"
|
|
47
|
+
"typescript": "^6.0.0"
|
|
48
48
|
},
|
|
49
49
|
"repository": {
|
|
50
50
|
"type": "git",
|
|
@@ -56,8 +56,10 @@
|
|
|
56
56
|
"url": "https://github.com/txstate-etc/fastify-txstate/issues"
|
|
57
57
|
},
|
|
58
58
|
"homepage": "https://github.com/txstate-etc/fastify-txstate#readme",
|
|
59
|
+
"engines": {
|
|
60
|
+
"node": ">=20"
|
|
61
|
+
},
|
|
59
62
|
"files": [
|
|
60
|
-
"lib/**/*"
|
|
61
|
-
"lib-esm/**/*"
|
|
63
|
+
"lib/**/*"
|
|
62
64
|
]
|
|
63
65
|
}
|
package/lib-esm/index.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import ftxst from '../lib/index.js'
|
|
2
|
-
|
|
3
|
-
export const devLogger = ftxst.devLogger
|
|
4
|
-
export const prodLogger = ftxst.prodLogger
|
|
5
|
-
export const HttpError = ftxst.HttpError
|
|
6
|
-
export const FailedValidationError = ftxst.FailedValidationError
|
|
7
|
-
export const ValidationError = ftxst.ValidationError
|
|
8
|
-
export const ValidationErrors = ftxst.ValidationErrors
|
|
9
|
-
export const unifiedAuthenticate = ftxst.unifiedAuthenticate
|
|
10
|
-
export const unifiedAuthenticateAll = ftxst.unifiedAuthenticateAll
|
|
11
|
-
export const registerUaCookieRoutes = ftxst.registerUaCookieRoutes
|
|
12
|
-
export const analyticsPlugin = ftxst.analyticsPlugin
|
|
13
|
-
export const AnalyticsClient = ftxst.AnalyticsClient
|
|
14
|
-
export const LoggingAnalyticsClient = ftxst.LoggingAnalyticsClient
|
|
15
|
-
export const ElasticAnalyticsClient = ftxst.ElasticAnalyticsClient
|
|
16
|
-
export const postFormData = ftxst.postFormData
|
|
17
|
-
export const readableToWebReadable = ftxst.readableToWebReadable
|
|
18
|
-
export const FileSystemHandler = ftxst.FileSystemHandler
|
|
19
|
-
export const fileHandler = ftxst.fileHandler
|
|
20
|
-
export default ftxst.default
|
package/lib-esm/package.json
DELETED