@contentful/node-apps-toolkit
Advanced tools
Comparing version 1.4.0 to 2.0.0
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.ContentfulHeader = exports.verifyRequest = exports.signRequest = exports.getManagementToken = void 0; | ||
exports.verifyRequest = exports.signRequest = exports.getManagementToken = void 0; | ||
var keys_1 = require("./keys"); | ||
@@ -9,3 +9,2 @@ Object.defineProperty(exports, "getManagementToken", { enumerable: true, get: function () { return keys_1.getManagementToken; } }); | ||
Object.defineProperty(exports, "verifyRequest", { enumerable: true, get: function () { return requests_1.verifyRequest; } }); | ||
Object.defineProperty(exports, "ContentfulHeader", { enumerable: true, get: function () { return requests_1.ContentfulHeader; } }); | ||
//# sourceMappingURL=index.js.map |
@@ -43,3 +43,3 @@ "use strict"; | ||
*/ | ||
exports.createGetManagementToken = (log, http, existingCache) => { | ||
const createGetManagementToken = (log, http, existingCache) => { | ||
const cache = existingCache || new NodeCache(); | ||
@@ -74,2 +74,3 @@ return async (privateKey, opts) => { | ||
}; | ||
exports.createGetManagementToken = createGetManagementToken; | ||
/** | ||
@@ -98,5 +99,6 @@ * Returns a Contentful Management API token from private key | ||
*/ | ||
exports.getManagementToken = (privateKey, opts) => { | ||
const getManagementToken = (privateKey, opts) => { | ||
return exports.createGetManagementToken(utils_1.createLogger({ filename: __filename }), utils_1.createHttpClient())(privateKey, opts); | ||
}; | ||
exports.getManagementToken = getManagementToken; | ||
//# sourceMappingURL=get-management-token.js.map |
export { signRequest } from './sign-request'; | ||
export { verifyRequest } from './verify-request'; | ||
export { ContentfulHeader, ContentfulUserIdHeader, ContentfulAppIdHeader } from './typings'; | ||
export type { CanonicalRequest, SignedRequestHeaders } from './typings'; | ||
export { ContentfulHeader, ContentfulContextHeader } from './typings'; | ||
export type { CanonicalRequest, SignedRequestWithContextHeadersWithApp, SignedRequestWithContextHeadersWithUser, SignedRequestWithoutContextHeaders, SignedRequestHeaders, } from './typings'; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.ContentfulAppIdHeader = exports.ContentfulUserIdHeader = exports.ContentfulHeader = exports.verifyRequest = exports.signRequest = void 0; | ||
exports.verifyRequest = exports.signRequest = void 0; | ||
var sign_request_1 = require("./sign-request"); | ||
@@ -8,6 +8,2 @@ Object.defineProperty(exports, "signRequest", { enumerable: true, get: function () { return sign_request_1.signRequest; } }); | ||
Object.defineProperty(exports, "verifyRequest", { enumerable: true, get: function () { return verify_request_1.verifyRequest; } }); | ||
var typings_1 = require("./typings"); | ||
Object.defineProperty(exports, "ContentfulHeader", { enumerable: true, get: function () { return typings_1.ContentfulHeader; } }); | ||
Object.defineProperty(exports, "ContentfulUserIdHeader", { enumerable: true, get: function () { return typings_1.ContentfulUserIdHeader; } }); | ||
Object.defineProperty(exports, "ContentfulAppIdHeader", { enumerable: true, get: function () { return typings_1.ContentfulAppIdHeader; } }); | ||
//# sourceMappingURL=index.js.map |
@@ -1,2 +0,2 @@ | ||
import { CanonicalRequest, Secret, SignedRequestHeaders, Timestamp, ContextHeaders } from './typings'; | ||
import { CanonicalRequest, Secret, Timestamp, Context, SignedRequestWithoutContextHeaders, SubjectHeadersApp, SubjectHeadersUser, SignedRequestWithContextHeadersWithApp, SignedRequestWithContextHeadersWithUser } from './typings'; | ||
/** | ||
@@ -7,2 +7,4 @@ * Given a secret, a canonical request, a timestamp and context headers, generates a signature. | ||
*/ | ||
export declare const signRequest: (rawSecret: Secret, rawCanonicalRequest: CanonicalRequest, rawTimestamp?: Timestamp, rawContextHeaders?: ContextHeaders) => SignedRequestHeaders; | ||
export declare function signRequest(rawSecret: Secret, rawCanonicalRequest: CanonicalRequest, rawTimestamp?: Timestamp): SignedRequestWithoutContextHeaders; | ||
export declare function signRequest(rawSecret: Secret, rawCanonicalRequest: CanonicalRequest, rawTimestamp?: Timestamp, rawContext?: Context<SubjectHeadersApp>): SignedRequestWithContextHeadersWithApp; | ||
export declare function signRequest(rawSecret: Secret, rawCanonicalRequest: CanonicalRequest, rawTimestamp?: Timestamp, rawContext?: Context<SubjectHeadersUser>): SignedRequestWithContextHeadersWithUser; |
@@ -6,3 +6,2 @@ "use strict"; | ||
const typings_1 = require("./typings"); | ||
const typings_2 = require("./typings"); | ||
const utils_1 = require("./utils"); | ||
@@ -25,34 +24,20 @@ const hash = (normalizedCanonicalRequest, secret) => { | ||
const rawSignedHeaders = Object.keys(headers); | ||
if (!(typings_1.ContentfulHeader.SignedHeaders in headers)) { | ||
rawSignedHeaders.push(typings_1.ContentfulHeader.SignedHeaders); | ||
if (!("x-contentful-signed-headers" /* SignedHeaders */ in headers)) { | ||
rawSignedHeaders.push("x-contentful-signed-headers" /* SignedHeaders */); | ||
} | ||
if (!(typings_1.ContentfulHeader.Timestamp in headers)) { | ||
rawSignedHeaders.push(typings_1.ContentfulHeader.Timestamp); | ||
if (!("x-contentful-timestamp" /* Timestamp */ in headers)) { | ||
rawSignedHeaders.push("x-contentful-timestamp" /* Timestamp */); | ||
} | ||
const signedHeaders = rawSignedHeaders.sort(utils_1.sortHeaderKeys).join(','); | ||
headers[typings_1.ContentfulHeader.Timestamp] = timestamp.toString(); | ||
headers[typings_1.ContentfulHeader.SignedHeaders] = signedHeaders; | ||
headers["x-contentful-timestamp" /* Timestamp */] = timestamp.toString(); | ||
headers["x-contentful-signed-headers" /* SignedHeaders */] = signedHeaders; | ||
const sortedHeaders = Object.entries(headers).sort(([keyA], [keyB]) => utils_1.sortHeaderKeys(keyA, keyB)); | ||
return { sortedHeaders, signedHeaders }; | ||
}; | ||
const getSubjectHeaders = (contextHeaders) => { | ||
let headers = {}; | ||
if (contextHeaders[typings_1.ContentfulUserIdHeader]) { | ||
headers[typings_1.ContentfulUserIdHeader] = contextHeaders[typings_1.ContentfulUserIdHeader]; | ||
} | ||
else if (contextHeaders[typings_1.ContentfulAppIdHeader]) { | ||
headers[typings_1.ContentfulAppIdHeader] = contextHeaders[typings_1.ContentfulAppIdHeader]; | ||
} | ||
return headers; | ||
}; | ||
/** | ||
* Given a secret, a canonical request, a timestamp and context headers, generates a signature. | ||
* ~~~ | ||
* @category Requests | ||
*/ | ||
exports.signRequest = (rawSecret, rawCanonicalRequest, rawTimestamp = Date.now(), rawContextHeaders = {}) => { | ||
function signRequest(rawSecret, rawCanonicalRequest, rawTimestamp, rawContext) { | ||
var _a; | ||
const canonicalRequest = typings_2.CanonicalRequestValidator.check(rawCanonicalRequest); | ||
const timestamp = typings_2.TimestampValidator.check(rawTimestamp); | ||
const secret = typings_2.SecretValidator.check(rawSecret); | ||
const maybeDefaultTimestamp = rawTimestamp !== null && rawTimestamp !== void 0 ? rawTimestamp : Date.now(); | ||
const canonicalRequest = typings_1.CanonicalRequestValidator.check(rawCanonicalRequest); | ||
const timestamp = typings_1.TimestampValidator.check(maybeDefaultTimestamp); | ||
const secret = typings_1.SecretValidator.check(rawSecret); | ||
const path = utils_1.getNormalizedEncodedURI(canonicalRequest.path); | ||
@@ -62,14 +47,13 @@ const method = canonicalRequest.method; | ||
const body = (_a = canonicalRequest.body) !== null && _a !== void 0 ? _a : ''; | ||
const contextHeaders = utils_1.normalizeContextHeaders(rawContextHeaders); | ||
const subject = getSubjectHeaders(contextHeaders); | ||
const contextHeaders = rawContext ? utils_1.normalizeContextHeaders(rawContext) : {}; | ||
const { sortedHeaders, signedHeaders } = getSortedAndSignedHeaders({ ...headers, ...contextHeaders }, timestamp); | ||
return { | ||
[typings_1.ContentfulHeader.Signature]: hash({ method, headers: sortedHeaders, path, body }, secret), | ||
[typings_1.ContentfulHeader.SignedHeaders]: signedHeaders, | ||
[typings_1.ContentfulHeader.Timestamp]: timestamp.toString(), | ||
[typings_1.ContentfulHeader.SpaceId]: rawContextHeaders.spaceId, | ||
[typings_1.ContentfulHeader.EnvironmentId]: rawContextHeaders.envId, | ||
...subject, | ||
["x-contentful-signature" /* Signature */]: hash({ method, headers: sortedHeaders, path, body }, secret), | ||
["x-contentful-signed-headers" /* SignedHeaders */]: signedHeaders, | ||
["x-contentful-timestamp" /* Timestamp */]: timestamp.toString(), | ||
...contextHeaders, | ||
}; | ||
}; | ||
} | ||
exports.signRequest = signRequest; | ||
/*eslint-enable no-unused-vars,no-redeclare */ | ||
//# sourceMappingURL=sign-request.js.map |
@@ -118,4 +118,8 @@ "use strict"; | ||
}); | ||
it('does not return undefined headers', () => { | ||
const result = sign_request_1.signRequest(VALID_SECRET, VALID_REQUEST, undefined); | ||
assert.ok(Object.values(result).every((h) => typeof h !== 'undefined')); | ||
}); | ||
}); | ||
}); | ||
//# sourceMappingURL=sign-request.spec.js.map |
import * as runtypes from 'runtypes'; | ||
import { XOR } from 'ts-xor'; | ||
export declare enum ContentfulHeader { | ||
export declare const enum ContentfulHeader { | ||
Timestamp = "x-contentful-timestamp", | ||
SignedHeaders = "x-contentful-signed-headers", | ||
Signature = "x-contentful-signature", | ||
Signature = "x-contentful-signature" | ||
} | ||
export declare const enum ContentfulContextHeader { | ||
SpaceId = "x-contentful-space-id", | ||
EnvironmentId = "x-contentful-environment-id" | ||
EnvironmentId = "x-contentful-environment-id", | ||
UserId = "x-contentful-user-id", | ||
AppId = "x-contentful-app-id" | ||
} | ||
export declare const ContentfulUserIdHeader = "x-contentful-user-id"; | ||
export declare const ContentfulAppIdHeader = "x-contentful-app-id"; | ||
declare type ContentfulHeaderWithApp = { | ||
[ContentfulAppIdHeader]: string; | ||
}; | ||
declare type ContentfulHeaderWithUser = { | ||
[ContentfulUserIdHeader]: string; | ||
}; | ||
export declare const CanonicalRequestValidator: runtypes.Intersect2<runtypes.Record<{ | ||
@@ -44,15 +39,29 @@ method: runtypes.Union7<runtypes.Literal<"GET">, runtypes.Literal<"PATCH">, runtypes.Literal<"HEAD">, runtypes.Literal<"POST">, runtypes.Literal<"DELETE">, runtypes.Literal<"OPTIONS">, runtypes.Literal<"PUT">>; | ||
}; | ||
export declare type Subject = Partial<XOR<{ | ||
export declare type SubjectHeadersApp = { | ||
appId: string; | ||
}, { | ||
}; | ||
export declare type AppContextSignedHeaders = { | ||
[ContentfulContextHeader.AppId]: string; | ||
}; | ||
export declare type SubjectHeadersUser = { | ||
userId: string; | ||
}>>; | ||
export declare type SubjectHeader = Partial<XOR<ContentfulHeaderWithApp, ContentfulHeaderWithUser>>; | ||
export declare type ContextHeaders = { | ||
}; | ||
export declare type UserContextSignedHeaders = { | ||
[ContentfulContextHeader.UserId]: string; | ||
}; | ||
export declare type Context<SubjectContext> = { | ||
spaceId: string; | ||
envId: string; | ||
} & Subject; | ||
export declare type SignedRequestHeaders = { | ||
} & SubjectContext; | ||
declare type SignedHeadersWithoutSubject = { | ||
[ContentfulContextHeader.SpaceId]: string; | ||
[ContentfulContextHeader.EnvironmentId]: string; | ||
}; | ||
export declare type SignedContextHeaders<SubjectSignedHeaders> = SignedHeadersWithoutSubject & SubjectSignedHeaders; | ||
export declare type SignedRequestWithoutContextHeaders = { | ||
[key in ContentfulHeader]: string; | ||
} & (SubjectHeader | {}); | ||
}; | ||
export declare type SignedRequestWithContextHeadersWithUser = SignedRequestWithoutContextHeaders & SignedContextHeaders<UserContextSignedHeaders>; | ||
export declare type SignedRequestWithContextHeadersWithApp = SignedRequestWithoutContextHeaders & SignedContextHeaders<AppContextSignedHeaders>; | ||
export declare type SignedRequestHeaders = SignedRequestWithContextHeadersWithUser | SignedRequestWithContextHeadersWithApp | SignedRequestWithoutContextHeaders; | ||
export {}; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.TimeToLiveValidator = exports.RequestMetadataValidator = exports.TimestampValidator = exports.SecretValidator = exports.CanonicalRequestValidator = exports.ContentfulAppIdHeader = exports.ContentfulUserIdHeader = exports.ContentfulHeader = void 0; | ||
exports.TimeToLiveValidator = exports.RequestMetadataValidator = exports.TimestampValidator = exports.SecretValidator = exports.CanonicalRequestValidator = void 0; | ||
// Remove when this eslint rule covers all the cases | ||
@@ -8,12 +8,2 @@ // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/ROADMAP.md | ||
const runtypes = require("runtypes"); | ||
var ContentfulHeader; | ||
(function (ContentfulHeader) { | ||
ContentfulHeader["Timestamp"] = "x-contentful-timestamp"; | ||
ContentfulHeader["SignedHeaders"] = "x-contentful-signed-headers"; | ||
ContentfulHeader["Signature"] = "x-contentful-signature"; | ||
ContentfulHeader["SpaceId"] = "x-contentful-space-id"; | ||
ContentfulHeader["EnvironmentId"] = "x-contentful-environment-id"; | ||
})(ContentfulHeader = exports.ContentfulHeader || (exports.ContentfulHeader = {})); | ||
exports.ContentfulUserIdHeader = 'x-contentful-user-id'; | ||
exports.ContentfulAppIdHeader = 'x-contentful-app-id'; | ||
const MethodValidator = runtypes.Union(runtypes.Literal('GET'), runtypes.Literal('PATCH'), runtypes.Literal('HEAD'), runtypes.Literal('POST'), runtypes.Literal('DELETE'), runtypes.Literal('OPTIONS'), runtypes.Literal('PUT')); | ||
@@ -20,0 +10,0 @@ const PathValidator = runtypes.String.withConstraint((s) => s.startsWith('/'), { |
@@ -1,2 +0,2 @@ | ||
import { ContextHeaders, SignedRequestHeaders } from './typings'; | ||
import { AppContextSignedHeaders, Context, SignedContextHeaders, SubjectHeadersApp, SubjectHeadersUser, UserContextSignedHeaders } from './typings'; | ||
export declare const getNormalizedEncodedURI: (uri: string) => string; | ||
@@ -10,3 +10,4 @@ export declare const sortHeaderKeys: (keyA: string, keyB: string) => 1 | -1; | ||
}; | ||
export declare const normalizeContextHeaders: (rawContextHeaders: ContextHeaders) => Partial<SignedRequestHeaders>; | ||
export declare function normalizeContextHeaders(rawContext: Context<SubjectHeadersApp>): SignedContextHeaders<AppContextSignedHeaders>; | ||
export declare function normalizeContextHeaders(rawContext: Context<SubjectHeadersUser>): SignedContextHeaders<UserContextSignedHeaders>; | ||
export declare const filter: <T = string>(obj: Record<string, any>, callback: (entry: [string, T]) => boolean) => { | ||
@@ -13,0 +14,0 @@ [k: string]: any; |
@@ -5,4 +5,3 @@ "use strict"; | ||
const querystring = require("querystring"); | ||
const typings_1 = require("./typings"); | ||
exports.getNormalizedEncodedURI = (uri) => { | ||
const getNormalizedEncodedURI = (uri) => { | ||
const [pathname, search] = uri.split('?'); | ||
@@ -12,7 +11,10 @@ const escapedSearch = search ? querystring.escape(search) : ''; | ||
}; | ||
exports.sortHeaderKeys = (keyA, keyB) => (keyA > keyB ? 1 : -1); | ||
exports.getNormalizedEncodedURI = getNormalizedEncodedURI; | ||
const sortHeaderKeys = (keyA, keyB) => (keyA > keyB ? 1 : -1); | ||
exports.sortHeaderKeys = sortHeaderKeys; | ||
const normalizeHeaderKey = (key) => key.toLowerCase().trim(); | ||
const normalizeHeaderValue = (value) => value.trim(); | ||
exports.normalizeHeaders = (headers) => exports.map(headers, ([key, value]) => [normalizeHeaderKey(key), normalizeHeaderValue(value)]); | ||
exports.pickHeaders = (headers, keys) => { | ||
const normalizeHeaders = (headers) => exports.map(headers, ([key, value]) => [normalizeHeaderKey(key), normalizeHeaderValue(value)]); | ||
exports.normalizeHeaders = normalizeHeaders; | ||
const pickHeaders = (headers, keys) => { | ||
if (!headers) { | ||
@@ -26,28 +28,33 @@ return {}; | ||
}; | ||
exports.pickHeaders = pickHeaders; | ||
const contextHeadersMap = { | ||
spaceId: typings_1.ContentfulHeader.SpaceId, | ||
envId: typings_1.ContentfulHeader.EnvironmentId, | ||
appId: typings_1.ContentfulAppIdHeader, | ||
userId: typings_1.ContentfulUserIdHeader, | ||
spaceId: "x-contentful-space-id" /* SpaceId */, | ||
envId: "x-contentful-environment-id" /* EnvironmentId */, | ||
appId: "x-contentful-app-id" /* AppId */, | ||
userId: "x-contentful-user-id" /* UserId */, | ||
}; | ||
exports.normalizeContextHeaders = (rawContextHeaders) => { | ||
return Object.keys(rawContextHeaders).reduce((acc, curr) => { | ||
function normalizeContextHeaders(rawContext) { | ||
return Object.keys(rawContext).reduce((acc, header) => { | ||
var _a; | ||
if (contextHeadersMap[curr]) { | ||
let key = contextHeadersMap[curr]; | ||
acc[key] = (_a = acc[key]) !== null && _a !== void 0 ? _a : rawContextHeaders[curr]; | ||
if (contextHeadersMap[header]) { | ||
const key = normalizeHeaderKey(contextHeadersMap[header]); | ||
acc[key] = normalizeHeaderValue((_a = acc[key]) !== null && _a !== void 0 ? _a : rawContext[header]); | ||
} | ||
return acc; | ||
}, {}); | ||
}; | ||
} | ||
exports.normalizeContextHeaders = normalizeContextHeaders; | ||
/*eslint-enable no-unused-vars, no-redeclare*/ | ||
// Remove when this eslint rule covers all the cases | ||
// https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/ROADMAP.md | ||
/*eslint-disable no-unused-vars*/ | ||
exports.filter = (obj, callback) => { | ||
const filter = (obj, callback) => { | ||
return Object.fromEntries(Object.entries(obj).filter(callback)); | ||
}; | ||
exports.map = (obj, callback) => { | ||
exports.filter = filter; | ||
const map = (obj, callback) => { | ||
return Object.fromEntries(Object.entries(obj).map(callback)); | ||
}; | ||
exports.map = map; | ||
/*eslint-enable no-unused-vars*/ | ||
//# sourceMappingURL=utils.js.map |
@@ -10,5 +10,5 @@ "use strict"; | ||
var _a, _b; | ||
const signature = normalizedHeaders[typings_1.ContentfulHeader.Signature]; | ||
const signedHeaders = ((_a = normalizedHeaders[typings_1.ContentfulHeader.SignedHeaders]) !== null && _a !== void 0 ? _a : '').split(','); | ||
const timestamp = Number.parseInt((_b = normalizedHeaders[typings_1.ContentfulHeader.Timestamp]) !== null && _b !== void 0 ? _b : '', 10); | ||
const signature = normalizedHeaders["x-contentful-signature" /* Signature */]; | ||
const signedHeaders = ((_a = normalizedHeaders["x-contentful-signed-headers" /* SignedHeaders */]) !== null && _a !== void 0 ? _a : '').split(','); | ||
const timestamp = Number.parseInt((_b = normalizedHeaders["x-contentful-timestamp" /* Timestamp */]) !== null && _b !== void 0 ? _b : '', 10); | ||
return typings_1.RequestMetadataValidator.check({ signature, signedHeaders, timestamp }); | ||
@@ -50,3 +50,3 @@ }; | ||
*/ | ||
exports.verifyRequest = (rawSecret, rawCanonicalRequest, rawTimeToLive = 30) => { | ||
const verifyRequest = (rawSecret, rawCanonicalRequest, rawTimeToLive = 30) => { | ||
var _a; | ||
@@ -64,5 +64,6 @@ const canonicalRequest = typings_1.CanonicalRequestValidator.check(rawCanonicalRequest); | ||
}; | ||
const { [typings_1.ContentfulHeader.Signature]: computedSignature } = sign_request_1.signRequest(secret, requestToValidate, timestamp); | ||
const { ["x-contentful-signature" /* Signature */]: computedSignature } = sign_request_1.signRequest(secret, requestToValidate, timestamp); | ||
return signature === computedSignature; | ||
}; | ||
exports.verifyRequest = verifyRequest; | ||
//# sourceMappingURL=verify-request.js.map |
@@ -5,6 +5,15 @@ "use strict"; | ||
const verify_request_1 = require("./verify-request"); | ||
const typings_1 = require("./typings"); | ||
const sign_request_1 = require("./sign-request"); | ||
const exceptions_1 = require("./exceptions"); | ||
const makeIncomingRequest = ({ path = '/api/v1/resources/1', method = 'GET', headers, body, }, now = Date.now(), subject = {}) => { | ||
const makeContextHeaders = (subject) => { | ||
return subject | ||
? { | ||
spaceId: 'my-space', | ||
envId: 'my-environment', | ||
...subject, | ||
} | ||
: undefined; | ||
}; | ||
const makeIncomingRequest = ({ path = '/api/v1/resources/1', method = 'GET', headers, body }, contextHeaders, now = Date.now()) => { | ||
var _a; | ||
const request = { | ||
@@ -16,7 +25,2 @@ path, | ||
}; | ||
const contextHeaders = { | ||
spaceId: 'my-space', | ||
envId: 'my-environment', | ||
...subject, | ||
}; | ||
const signedHeaders = sign_request_1.signRequest(VALID_SECRET, request, now, contextHeaders); | ||
@@ -26,4 +30,4 @@ return { | ||
headers: { | ||
...request.headers, | ||
...signedHeaders, | ||
...((_a = request.headers) !== null && _a !== void 0 ? _a : {}), | ||
...(signedHeaders !== null && signedHeaders !== void 0 ? signedHeaders : {}), | ||
}, | ||
@@ -33,219 +37,239 @@ }; | ||
const VALID_SECRET = new Array(64).fill('a').join(''); | ||
describe('isVerifiedRequest', () => { | ||
it('verifies a verified request', () => { | ||
const now = Date.now(); | ||
const incomingRequest = makeIncomingRequest({ | ||
headers: { | ||
Authorization: 'Bearer TOKEN', | ||
}, | ||
}, now); | ||
assert(verify_request_1.verifyRequest(VALID_SECRET, incomingRequest, 0)); | ||
}); | ||
it('verifies a verified request with user-id', () => { | ||
const now = Date.now(); | ||
const incomingRequest = makeIncomingRequest({ | ||
headers: { | ||
Authorization: 'Bearer TOKEN', | ||
}, | ||
}, now, { userId: 'my-user' }); | ||
assert(verify_request_1.verifyRequest(VALID_SECRET, incomingRequest, 0)); | ||
}); | ||
it('verifies a verified request with app-id', () => { | ||
const now = Date.now(); | ||
const incomingRequest = makeIncomingRequest({ | ||
headers: { | ||
Authorization: 'Bearer TOKEN', | ||
}, | ||
}, now, { appId: 'my-app' }); | ||
assert(verify_request_1.verifyRequest(VALID_SECRET, incomingRequest, 0)); | ||
}); | ||
describe('with time to live', () => { | ||
it('throws if request is too old', () => { | ||
const oneMinuteAgo = Date.now() - 1000 * 60; | ||
const incomingRequest = makeIncomingRequest({ | ||
headers: { | ||
Authorization: 'Bearer TOKEN', | ||
}, | ||
}, oneMinuteAgo); | ||
assert.throws(() => verify_request_1.verifyRequest(VALID_SECRET, incomingRequest, 1), exceptions_1.ExpiredRequestException); | ||
}); | ||
it('does not check if ttl is 0', () => { | ||
const oneMinuteAgo = Date.now() - 1000 * 60; | ||
const incomingRequest = makeIncomingRequest({ | ||
headers: { | ||
Authorization: 'Bearer TOKEN', | ||
}, | ||
}, oneMinuteAgo); | ||
assert.doesNotThrow(() => verify_request_1.verifyRequest(VALID_SECRET, incomingRequest, 0), exceptions_1.ExpiredRequestException); | ||
}); | ||
}); | ||
describe('with contentful headers', () => { | ||
it('verifies correctly with keys with different casing', () => { | ||
const incomingRequest = makeIncomingRequest({}, Date.now(), { | ||
userId: 'my-user', | ||
const TestCase = { | ||
NoContext: 'no context', | ||
AppContext: 'app context', | ||
UserContext: 'user context', | ||
}; | ||
describe('verifyRequest', () => { | ||
for (const { testCase, contextHeaders } of [ | ||
{ testCase: TestCase.NoContext, contextHeaders: undefined }, | ||
{ testCase: TestCase.AppContext, contextHeaders: { appId: 'appId' } }, | ||
{ testCase: TestCase.UserContext, contextHeaders: { userId: 'userId' } }, | ||
]) { | ||
describe(`with ${testCase}`, () => { | ||
it('verifies a verified request', () => { | ||
const now = Date.now(); | ||
const incomingRequest = makeIncomingRequest({ | ||
headers: { | ||
Authorization: 'Bearer TOKEN', | ||
}, | ||
}, now); | ||
assert(verify_request_1.verifyRequest(VALID_SECRET, incomingRequest, 0)); | ||
}); | ||
// mess with casing | ||
incomingRequest.headers[typings_1.ContentfulHeader.Signature.toUpperCase()] = | ||
incomingRequest.headers[typings_1.ContentfulHeader.Signature]; | ||
incomingRequest.headers[typings_1.ContentfulHeader.SignedHeaders.toUpperCase()] = | ||
incomingRequest.headers[typings_1.ContentfulHeader.SignedHeaders]; | ||
incomingRequest.headers[typings_1.ContentfulHeader.Timestamp.toUpperCase()] = | ||
incomingRequest.headers[typings_1.ContentfulHeader.Timestamp]; | ||
incomingRequest.headers[typings_1.ContentfulHeader.SpaceId.toUpperCase()] = | ||
incomingRequest.headers[typings_1.ContentfulHeader.SpaceId]; | ||
incomingRequest.headers[typings_1.ContentfulUserIdHeader.toUpperCase()] = | ||
incomingRequest.headers[typings_1.ContentfulUserIdHeader]; | ||
// remove correctly cased ones | ||
delete incomingRequest.headers[typings_1.ContentfulHeader.Signature]; | ||
delete incomingRequest.headers[typings_1.ContentfulHeader.SignedHeaders]; | ||
delete incomingRequest.headers[typings_1.ContentfulHeader.Timestamp]; | ||
delete incomingRequest.headers[typings_1.ContentfulHeader.SpaceId]; | ||
delete incomingRequest.headers[typings_1.ContentfulUserIdHeader]; | ||
assert(verify_request_1.verifyRequest(VALID_SECRET, incomingRequest)); | ||
}); | ||
it('verifies correctly with keys with whitespace', () => { | ||
const incomingRequest = makeIncomingRequest({}, Date.now(), { | ||
appId: 'my-app', | ||
it('verifies a verified request with subject-id', () => { | ||
const now = Date.now(); | ||
const incomingRequest = makeIncomingRequest({ | ||
headers: { | ||
Authorization: 'Bearer TOKEN', | ||
}, | ||
}, makeContextHeaders(contextHeaders), now); | ||
assert(verify_request_1.verifyRequest(VALID_SECRET, incomingRequest, 0)); | ||
}); | ||
// mess with spacing | ||
incomingRequest.headers[`${typings_1.ContentfulHeader.Signature} `] = | ||
incomingRequest.headers[typings_1.ContentfulHeader.Signature]; | ||
incomingRequest.headers[` ${typings_1.ContentfulHeader.SignedHeaders} `] = | ||
incomingRequest.headers[typings_1.ContentfulHeader.SignedHeaders]; | ||
incomingRequest.headers[` ${typings_1.ContentfulHeader.Timestamp}`] = | ||
incomingRequest.headers[typings_1.ContentfulHeader.Timestamp]; | ||
incomingRequest.headers[` ${typings_1.ContentfulHeader.SpaceId} `] = | ||
incomingRequest.headers[typings_1.ContentfulHeader.SpaceId]; | ||
incomingRequest.headers[` ${typings_1.ContentfulAppIdHeader} `] = | ||
incomingRequest.headers[typings_1.ContentfulAppIdHeader]; | ||
// remove correctly spaced ones | ||
delete incomingRequest.headers[typings_1.ContentfulHeader.Signature]; | ||
delete incomingRequest.headers[typings_1.ContentfulHeader.SignedHeaders]; | ||
delete incomingRequest.headers[typings_1.ContentfulHeader.Timestamp]; | ||
delete incomingRequest.headers[typings_1.ContentfulAppIdHeader]; | ||
delete incomingRequest.headers[typings_1.ContentfulHeader.SpaceId]; | ||
assert(verify_request_1.verifyRequest(VALID_SECRET, incomingRequest)); | ||
}); | ||
it('throws when missing signature', () => { | ||
const incomingRequest = makeIncomingRequest({}); | ||
delete incomingRequest.headers[typings_1.ContentfulHeader.Signature]; | ||
assert.throws(() => verify_request_1.verifyRequest(VALID_SECRET, incomingRequest)); | ||
}); | ||
it('throws when missing timestamp', () => { | ||
const incomingRequest = makeIncomingRequest({}); | ||
delete incomingRequest.headers[typings_1.ContentfulHeader.Timestamp]; | ||
assert.throws(() => verify_request_1.verifyRequest(VALID_SECRET, incomingRequest)); | ||
}); | ||
it('throws when missing signed headers', () => { | ||
const incomingRequest = makeIncomingRequest({ | ||
headers: { Authorization: 'Bearer TOKEN' }, | ||
describe('with time to live', () => { | ||
it('throws if request is too old', () => { | ||
const oneMinuteAgo = Date.now() - 1000 * 60; | ||
const incomingRequest = makeIncomingRequest({ | ||
headers: { | ||
Authorization: 'Bearer TOKEN', | ||
}, | ||
}, makeContextHeaders(contextHeaders), oneMinuteAgo); | ||
assert.throws(() => verify_request_1.verifyRequest(VALID_SECRET, incomingRequest, 1), exceptions_1.ExpiredRequestException); | ||
}); | ||
it('does not check if ttl is 0', () => { | ||
const oneMinuteAgo = Date.now() - 1000 * 60; | ||
const incomingRequest = makeIncomingRequest({ | ||
headers: { | ||
Authorization: 'Bearer TOKEN', | ||
}, | ||
}, makeContextHeaders(contextHeaders), oneMinuteAgo); | ||
assert.doesNotThrow(() => verify_request_1.verifyRequest(VALID_SECRET, incomingRequest, 0), exceptions_1.ExpiredRequestException); | ||
}); | ||
}); | ||
delete incomingRequest.headers[typings_1.ContentfulHeader.SignedHeaders]; | ||
assert.throws(() => verify_request_1.verifyRequest(VALID_SECRET, incomingRequest)); | ||
}); | ||
}); | ||
describe('with other headers', () => { | ||
it('verifies with keys with different casing', () => { | ||
const authorization = 'Bearer TOKEN'; | ||
const cacheControl = 'no-cache'; | ||
const incomingRequest = makeIncomingRequest({ | ||
headers: { | ||
Authorization: authorization, | ||
'Cache-Control': cacheControl, | ||
}, | ||
describe('with contentful headers', () => { | ||
it('verifies correctly with keys with different casing', () => { | ||
const incomingRequest = makeIncomingRequest({}, makeContextHeaders(contextHeaders)); | ||
// mess with casing | ||
incomingRequest.headers["x-contentful-signature" /* Signature */.toUpperCase()] = | ||
incomingRequest.headers["x-contentful-signature" /* Signature */]; | ||
incomingRequest.headers["x-contentful-signed-headers" /* SignedHeaders */.toUpperCase()] = | ||
incomingRequest.headers["x-contentful-signed-headers" /* SignedHeaders */]; | ||
incomingRequest.headers["x-contentful-timestamp" /* Timestamp */.toUpperCase()] = | ||
incomingRequest.headers["x-contentful-timestamp" /* Timestamp */]; | ||
// remove correctly cased ones | ||
delete incomingRequest.headers["x-contentful-signature" /* Signature */]; | ||
delete incomingRequest.headers["x-contentful-signed-headers" /* SignedHeaders */]; | ||
delete incomingRequest.headers["x-contentful-timestamp" /* Timestamp */]; | ||
if (testCase === TestCase.UserContext) { | ||
incomingRequest.headers["x-contentful-space-id" /* SpaceId */.toUpperCase()] = | ||
incomingRequest.headers["x-contentful-space-id" /* SpaceId */]; | ||
incomingRequest.headers["x-contentful-user-id" /* UserId */.toUpperCase()] = | ||
incomingRequest.headers["x-contentful-user-id" /* UserId */]; | ||
delete incomingRequest.headers["x-contentful-space-id" /* SpaceId */]; | ||
delete incomingRequest.headers["x-contentful-user-id" /* UserId */]; | ||
} | ||
else if (testCase === TestCase.AppContext) { | ||
incomingRequest.headers["x-contentful-space-id" /* SpaceId */.toUpperCase()] = | ||
incomingRequest.headers["x-contentful-space-id" /* SpaceId */]; | ||
incomingRequest.headers["x-contentful-app-id" /* AppId */.toUpperCase()] = | ||
incomingRequest.headers["x-contentful-app-id" /* AppId */]; | ||
delete incomingRequest.headers["x-contentful-space-id" /* SpaceId */]; | ||
delete incomingRequest.headers["x-contentful-app-id" /* AppId */]; | ||
} | ||
assert(verify_request_1.verifyRequest(VALID_SECRET, incomingRequest)); | ||
}); | ||
it('verifies correctly with keys with whitespace', () => { | ||
const incomingRequest = makeIncomingRequest({}, makeContextHeaders(contextHeaders)); | ||
// mess with spacing | ||
incomingRequest.headers[`${"x-contentful-signature" /* Signature */} `] = | ||
incomingRequest.headers["x-contentful-signature" /* Signature */]; | ||
incomingRequest.headers[` ${"x-contentful-signed-headers" /* SignedHeaders */} `] = | ||
incomingRequest.headers["x-contentful-signed-headers" /* SignedHeaders */]; | ||
incomingRequest.headers[` ${"x-contentful-timestamp" /* Timestamp */}`] = | ||
incomingRequest.headers["x-contentful-timestamp" /* Timestamp */]; | ||
// remove correctly spaced ones | ||
delete incomingRequest.headers["x-contentful-signature" /* Signature */]; | ||
delete incomingRequest.headers["x-contentful-signed-headers" /* SignedHeaders */]; | ||
delete incomingRequest.headers["x-contentful-timestamp" /* Timestamp */]; | ||
if (testCase === TestCase.UserContext) { | ||
incomingRequest.headers[` ${"x-contentful-space-id" /* SpaceId */} `] = | ||
incomingRequest.headers["x-contentful-space-id" /* SpaceId */]; | ||
incomingRequest.headers[` ${"x-contentful-user-id" /* UserId */} `] = | ||
incomingRequest.headers["x-contentful-user-id" /* UserId */]; | ||
delete incomingRequest.headers["x-contentful-space-id" /* SpaceId */]; | ||
delete incomingRequest.headers["x-contentful-user-id" /* UserId */]; | ||
} | ||
else if (testCase === TestCase.AppContext) { | ||
incomingRequest.headers[` ${"x-contentful-space-id" /* SpaceId */} `] = | ||
incomingRequest.headers["x-contentful-space-id" /* SpaceId */]; | ||
incomingRequest.headers[` ${"x-contentful-app-id" /* AppId */} `] = | ||
incomingRequest.headers["x-contentful-app-id" /* AppId */]; | ||
delete incomingRequest.headers["x-contentful-space-id" /* SpaceId */]; | ||
delete incomingRequest.headers["x-contentful-app-id" /* AppId */]; | ||
} | ||
assert(verify_request_1.verifyRequest(VALID_SECRET, incomingRequest)); | ||
}); | ||
it('throws when missing signature', () => { | ||
const incomingRequest = makeIncomingRequest({}, makeContextHeaders(contextHeaders)); | ||
delete incomingRequest.headers["x-contentful-signature" /* Signature */]; | ||
assert.throws(() => verify_request_1.verifyRequest(VALID_SECRET, incomingRequest)); | ||
}); | ||
it('throws when missing timestamp', () => { | ||
const incomingRequest = makeIncomingRequest({}, makeContextHeaders(contextHeaders)); | ||
delete incomingRequest.headers["x-contentful-timestamp" /* Timestamp */]; | ||
assert.throws(() => verify_request_1.verifyRequest(VALID_SECRET, incomingRequest)); | ||
}); | ||
it('throws when missing signed headers', () => { | ||
const incomingRequest = makeIncomingRequest({ | ||
headers: { Authorization: 'Bearer TOKEN' }, | ||
}, makeContextHeaders(contextHeaders)); | ||
delete incomingRequest.headers["x-contentful-signed-headers" /* SignedHeaders */]; | ||
assert.throws(() => verify_request_1.verifyRequest(VALID_SECRET, incomingRequest)); | ||
}); | ||
}); | ||
// mess with casing | ||
incomingRequest.headers = { | ||
authORizAtioN: authorization, | ||
'CACHE-control': cacheControl, | ||
...incomingRequest.headers, | ||
}; | ||
// remove correctly cased ones | ||
delete incomingRequest.headers['Authorization']; | ||
delete incomingRequest.headers['Cache-Control']; | ||
assert(verify_request_1.verifyRequest(VALID_SECRET, incomingRequest)); | ||
}); | ||
it('verifies with keys with whitespace', () => { | ||
const authorization = 'Bearer TOKEN'; | ||
const cacheControl = 'no-cache'; | ||
const incomingRequest = makeIncomingRequest({ | ||
headers: { | ||
Authorization: authorization, | ||
'Cache-Control': cacheControl, | ||
}, | ||
describe('with other headers', () => { | ||
it('verifies with keys with different casing', () => { | ||
const authorization = 'Bearer TOKEN'; | ||
const cacheControl = 'no-cache'; | ||
const incomingRequest = makeIncomingRequest({ | ||
headers: { | ||
Authorization: authorization, | ||
'Cache-Control': cacheControl, | ||
}, | ||
}, makeContextHeaders(contextHeaders)); | ||
// mess with casing | ||
incomingRequest.headers = { | ||
authORizAtioN: authorization, | ||
'CACHE-control': cacheControl, | ||
...incomingRequest.headers, | ||
}; | ||
// remove correctly cased ones | ||
delete incomingRequest.headers['Authorization']; | ||
delete incomingRequest.headers['Cache-Control']; | ||
assert(verify_request_1.verifyRequest(VALID_SECRET, incomingRequest)); | ||
}); | ||
it('verifies with keys with whitespace', () => { | ||
const authorization = 'Bearer TOKEN'; | ||
const cacheControl = 'no-cache'; | ||
const incomingRequest = makeIncomingRequest({ | ||
headers: { | ||
Authorization: authorization, | ||
'Cache-Control': cacheControl, | ||
}, | ||
}, makeContextHeaders(contextHeaders)); | ||
// mess with spacing | ||
incomingRequest.headers = { | ||
'Authorization ': authorization, | ||
' Cache-Control ': cacheControl, | ||
...incomingRequest.headers, | ||
}; | ||
// remove correctly spaced ones | ||
delete incomingRequest.headers['Authorization']; | ||
delete incomingRequest.headers['Cache-Control']; | ||
assert(verify_request_1.verifyRequest(VALID_SECRET, incomingRequest)); | ||
}); | ||
it('verifies with different headers, if they are not signed', () => { | ||
const authorization = 'Bearer TOKEN'; | ||
const cacheControl = 'no-cache'; | ||
const incomingRequest = makeIncomingRequest({ | ||
headers: { | ||
Authorization: authorization, | ||
'Cache-Control': cacheControl, | ||
}, | ||
}, makeContextHeaders(contextHeaders)); | ||
incomingRequest.headers['Content-Type'] = 'application/json'; | ||
assert(verify_request_1.verifyRequest(VALID_SECRET, incomingRequest)); | ||
}); | ||
it('does not verify with different signed headers', () => { | ||
const authorization = 'Bearer TOKEN'; | ||
const cacheControl = 'no-cache'; | ||
const incomingRequest = makeIncomingRequest({ | ||
headers: { | ||
Authorization: authorization, | ||
'Cache-Control': cacheControl, | ||
}, | ||
}, makeContextHeaders(contextHeaders)); | ||
incomingRequest.headers['Authorization'] = 'something else'; | ||
assert(!verify_request_1.verifyRequest(VALID_SECRET, incomingRequest)); | ||
}); | ||
it("verifies with headers sorted lower than contentful's", () => { | ||
const incomingRequest = makeIncomingRequest({ | ||
headers: { | ||
Authorization: 'Bearer Token', | ||
'z-zzzzz': 'ronf ronf', | ||
}, | ||
}, makeContextHeaders(contextHeaders)); | ||
assert(verify_request_1.verifyRequest(VALID_SECRET, incomingRequest)); | ||
}); | ||
}); | ||
// mess with spacing | ||
incomingRequest.headers = { | ||
'Authorization ': authorization, | ||
' Cache-Control ': cacheControl, | ||
...incomingRequest.headers, | ||
}; | ||
// remove correctly spaced ones | ||
delete incomingRequest.headers['Authorization']; | ||
delete incomingRequest.headers['Cache-Control']; | ||
assert(verify_request_1.verifyRequest(VALID_SECRET, incomingRequest)); | ||
}); | ||
it('verifies with different headers, if they are not signed', () => { | ||
const authorization = 'Bearer TOKEN'; | ||
const cacheControl = 'no-cache'; | ||
const incomingRequest = makeIncomingRequest({ | ||
headers: { | ||
Authorization: authorization, | ||
'Cache-Control': cacheControl, | ||
}, | ||
describe('with paths', () => { | ||
it('does not verify with different query params', () => { | ||
const path = '/api/v1'; | ||
const pathWithQueryOne = `${path}?a=1&b=2`; | ||
const pathWithQueryTwo = `${path}?b=2&a=1`; | ||
const incomingRequest = makeIncomingRequest({ path: pathWithQueryOne }, makeContextHeaders(contextHeaders)); | ||
incomingRequest.path = pathWithQueryTwo; | ||
assert(!verify_request_1.verifyRequest(VALID_SECRET, incomingRequest)); | ||
}); | ||
}); | ||
incomingRequest.headers['Content-Type'] = 'application/json'; | ||
assert(verify_request_1.verifyRequest(VALID_SECRET, incomingRequest)); | ||
}); | ||
it('does not verify with different signed headers', () => { | ||
const authorization = 'Bearer TOKEN'; | ||
const cacheControl = 'no-cache'; | ||
const incomingRequest = makeIncomingRequest({ | ||
headers: { | ||
Authorization: authorization, | ||
'Cache-Control': cacheControl, | ||
}, | ||
describe('with method', () => { | ||
it('does not verify with different methods', () => { | ||
const incomingRequest = makeIncomingRequest({ method: 'GET' }, makeContextHeaders(contextHeaders)); | ||
incomingRequest.method = 'POST'; | ||
assert(!verify_request_1.verifyRequest(VALID_SECRET, incomingRequest)); | ||
}); | ||
}); | ||
incomingRequest.headers['Authorization'] = 'something else'; | ||
assert(!verify_request_1.verifyRequest(VALID_SECRET, incomingRequest)); | ||
}); | ||
it("verifies with headers sorted lower than contentful's", () => { | ||
const incomingRequest = makeIncomingRequest({ | ||
headers: { | ||
Authorization: 'Bearer Token', | ||
'z-zzzzz': 'ronf ronf', | ||
}, | ||
describe('with secrets', () => { | ||
it('does not verify with different secrets', () => { | ||
const incomingRequest = makeIncomingRequest({ | ||
headers: { | ||
Authorization: 'Bearer TOKEN', | ||
}, | ||
}, makeContextHeaders(contextHeaders)); | ||
const differentSecret = `q${VALID_SECRET.slice(1, VALID_SECRET.length)}`; | ||
assert(!verify_request_1.verifyRequest(differentSecret, incomingRequest)); | ||
}); | ||
}); | ||
assert(verify_request_1.verifyRequest(VALID_SECRET, incomingRequest)); | ||
}); | ||
}); | ||
describe('with paths', () => { | ||
it('does not verify with different query params', () => { | ||
const path = '/api/v1'; | ||
const pathWithQueryOne = `${path}?a=1&b=2`; | ||
const pathWithQueryTwo = `${path}?b=2&a=1`; | ||
const incomingRequest = makeIncomingRequest({ path: pathWithQueryOne }); | ||
incomingRequest.path = pathWithQueryTwo; | ||
assert(!verify_request_1.verifyRequest(VALID_SECRET, incomingRequest)); | ||
}); | ||
}); | ||
describe('with method', () => { | ||
it('does not verify with different methods', () => { | ||
const incomingRequest = makeIncomingRequest({ method: 'GET' }); | ||
incomingRequest.method = 'POST'; | ||
assert(!verify_request_1.verifyRequest(VALID_SECRET, incomingRequest)); | ||
}); | ||
}); | ||
describe('with secrets', () => { | ||
it('does not verify with different secrets', () => { | ||
const incomingRequest = makeIncomingRequest({ | ||
headers: { | ||
Authorization: 'Bearer TOKEN', | ||
}, | ||
}); | ||
const differentSecret = `q${VALID_SECRET.slice(1, VALID_SECRET.length)}`; | ||
assert(!verify_request_1.verifyRequest(differentSecret, incomingRequest)); | ||
}); | ||
}); | ||
} | ||
}); | ||
//# sourceMappingURL=verify-request.spec.js.map |
@@ -10,6 +10,7 @@ "use strict"; | ||
}; | ||
exports.createHttpClient = (configOverride = {}) => { | ||
const createHttpClient = (configOverride = {}) => { | ||
return got_1.default.extend({ ...config, ...configOverride }); | ||
}; | ||
exports.createValidateStatusCode = (allowedStatusCodes) => (response) => { | ||
exports.createHttpClient = createHttpClient; | ||
const createValidateStatusCode = (allowedStatusCodes) => (response) => { | ||
if (!allowedStatusCodes.includes(response.statusCode)) { | ||
@@ -21,2 +22,3 @@ console.log(response.body); | ||
}; | ||
exports.createValidateStatusCode = createValidateStatusCode; | ||
//# sourceMappingURL=http.js.map |
@@ -14,6 +14,7 @@ "use strict"; | ||
*/ | ||
exports.createLogger = (opts) => { | ||
const createLogger = (opts) => { | ||
const ns = 'namespace' in opts ? opts.namespace : getNamespaceFromFilename(opts.filename); | ||
return debug_1.default(APP_NAME).extend(ns); | ||
}; | ||
exports.createLogger = createLogger; | ||
//# sourceMappingURL=logger.js.map |
{ | ||
"name": "@contentful/node-apps-toolkit", | ||
"version": "1.4.0", | ||
"version": "2.0.0", | ||
"description": "A collection of helpers and utilities for creating NodeJS Contentful Apps", | ||
@@ -28,4 +28,3 @@ "main": "lib/index.js", | ||
"node-cache": "^5.1.2", | ||
"runtypes": "^5.0.1", | ||
"ts-xor": "^1.0.8" | ||
"runtypes": "^5.0.1" | ||
}, | ||
@@ -43,3 +42,3 @@ "devDependencies": { | ||
"dotenv": "^8.2.0", | ||
"eslint": "^7.10.0", | ||
"eslint": "^7.32.0", | ||
"eslint-config-prettier": "^6.12.0", | ||
@@ -56,3 +55,3 @@ "eslint-plugin-prettier": "^3.1.4", | ||
"typedoc": "^0.19.2", | ||
"typescript": "^4.0.3" | ||
"typescript": "^4.3.5" | ||
}, | ||
@@ -59,0 +58,0 @@ "husky": { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
99317
6
58
1151
- Removedts-xor@^1.0.8
- Removedts-xor@1.3.0(transitive)