chore(typescript): convert more of fxa-shared to TS

Because:
 - there were some JS in fxa-shared in fxa-shared that can be converted
   to TS

This commit:
 - convert some shared JS to TS
This commit is contained in:
Barry Chen 2022-11-22 14:17:42 -06:00
Родитель 981fe0bf11
Коммит 95cded6e96
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 228DB2785954A0D0
45 изменённых файлов: 491 добавлений и 276 удалений

Просмотреть файл

@ -52,7 +52,7 @@
}
},
"lint-staged": {
"*.js": [
"packages/!(fxa-payments-server)/**/*.js": [
"prettier --config _dev/.prettierrc --write",
"eslint"
],

Просмотреть файл

@ -8,7 +8,7 @@ import path from 'path';
import mysql from 'mysql';
import patcher from 'mysql-patcher';
import convict from 'convict';
import { makeMySQLConfig } from 'fxa-shared/db/config.js';
import { makeMySQLConfig } from 'fxa-shared/db/config';
const patch = promisify(patcher.patch);
const conf = convict({

Просмотреть файл

@ -15,7 +15,8 @@
const { Container } = require('typedi');
const { StatsD } = require('hot-shots');
const { GROUPS, initialize } = require('fxa-shared/metrics/amplitude');
const { GROUPS, initialize } =
require('fxa-shared/metrics/amplitude').amplitude;
const { version: VERSION } = require('../../package.json');
// Maps template name to email type

Просмотреть файл

@ -1175,7 +1175,7 @@ export class AccountHandler {
scope = { contains: () => true };
} else {
uid = auth.credentials.user;
scope = ScopeSet.fromArray(auth.credentials.scope);
scope = ScopeSet.fromArray(auth.credentials.scope!);
}
const res: Record<string, any> = {};

Просмотреть файл

@ -13,7 +13,7 @@ const { OAUTH_SCOPE_OLD_SYNC } = require('fxa-shared/oauth/constants');
const encrypt = require('fxa-shared/auth/encrypt');
const oauthDB = require('../../oauth/db');
const client = require('../../oauth/client');
const ScopeSet = require('fxa-shared/oauth/scopes');
const ScopeSet = require('fxa-shared/oauth/scopes').scopeSetHelpers;
// the refresh token scheme is currently used by things connected to sync,
// and we're at a transitionary stage of its evolution into something more generic,

Просмотреть файл

@ -6,7 +6,7 @@
const MISC_DOCS = require('../../docs/swagger/misc-api').default;
const validators = require('./validators');
const ScopeSet = require('fxa-shared/oauth/scopes');
const ScopeSet = require('fxa-shared/oauth/scopes').scopeSetHelpers;
const AppError = require('../../lib/error');
const Joi = require('joi');

Просмотреть файл

@ -101,7 +101,7 @@ export class AppleIapHandler {
// once VPN migration is complete (FXA-5848).
'profile:subscriptions',
];
const scope = ScopeSet.fromArray(auth.credentials.scope);
const scope = ScopeSet.fromArray(auth.credentials.scope!);
if (!scopes.some((requiredScope) => scope.contains(requiredScope))) {
throw error.invalidScopes();
}

Просмотреть файл

@ -15,7 +15,7 @@ export async function handleAuth(
auth: AuthRequest['auth'],
fetchEmail = false
) {
const scope = ScopeSet.fromArray(auth.credentials.scope);
const scope = ScopeSet.fromArray(auth.credentials.scope!);
if (!scope.contains(OAUTH_SCOPE_SUBSCRIPTIONS)) {
throw error.invalidScopes();
}
@ -36,7 +36,7 @@ export async function handleAuth(
}
export function handleUidAuth(auth: AuthRequest['auth']): string {
const scope = ScopeSet.fromArray(auth.credentials.scope);
const scope = ScopeSet.fromArray(auth.credentials.scope!);
if (!scope.contains(OAUTH_SCOPE_SUBSCRIPTIONS)) {
throw error.invalidScopes();
}
@ -44,7 +44,7 @@ export function handleUidAuth(auth: AuthRequest['auth']): string {
}
export function handleAuthScoped(auth: AuthRequest['auth'], scopes: string[]) {
const scope = ScopeSet.fromArray(auth.credentials.scope);
const scope = ScopeSet.fromArray(auth.credentials.scope!);
for (const requiredScope of scopes) {
if (!scope.contains(requiredScope)) {
throw error.invalidScopes();

Просмотреть файл

@ -8,7 +8,7 @@ const sinon = require('sinon');
const assert = { ...sinon.assert, ...require('chai').assert };
const getRoute = require('../../routes_helpers').getRoute;
const mocks = require('../../mocks');
const ScopeSet = require('fxa-shared/oauth/scopes');
const ScopeSet = require('fxa-shared/oauth/scopes').scopeSetHelpers;
const error = require('../../../lib/error');
const { INVALID_PARAMETER, MISSING_PARAMETER } = error.ERRNO;

Просмотреть файл

@ -34,13 +34,13 @@ const serveStatic = require('serve-static');
const sentry = require('../lib/sentry');
const statsd = require('../lib/statsd');
const { cors, routing } = require('fxa-shared/express')();
const { cors, routing } = require('fxa-shared/express').express();
const {
useSettingsProxy,
modifySettingsStatic,
} = require('../lib/beta-settings');
const userAgent = require('fxa-shared/metrics/user-agent');
const userAgent = require('fxa-shared/metrics/user-agent').default;
if (!userAgent.isToVersionStringSupported()) {
// npm@3 installs the incorrect version of node-uap, one without `toVersionString`.
// To ensure the correct version is installed, check toVersionString is available.

Просмотреть файл

@ -23,9 +23,9 @@ const {
mapLocation,
mapOs,
validate,
} = require('fxa-shared/metrics/amplitude');
} = require('fxa-shared/metrics/amplitude').amplitude;
const logger = require('./logging/log')();
const ua = require('fxa-shared/metrics/user-agent');
const ua = require('fxa-shared/metrics/user-agent').default;
const config = require('./configuration');
const { version: VERSION } = require('../../package.json');

Просмотреть файл

@ -10,20 +10,17 @@ const flowMetrics = require('./flow-metrics');
const log = require('./logging/log')('server.flow-event');
const geodbConfig = config.get('geodb');
const geodb = require('fxa-geodb')(geodbConfig);
const remoteAddress = require('fxa-shared/express/remote-address')(
config.get('clientAddressDepth')
);
const geolocate = require('fxa-shared/express/geo-locate')(geodb)(
const remoteAddress =
require('fxa-shared/express/remote-address').remoteAddress(
config.get('clientAddressDepth')
);
const geolocate = require('fxa-shared/express/geo-locate').geolocate(geodb)(
remoteAddress
)(log);
const os = require('os');
const statsd = require('./statsd');
const {
VERSION,
PERFORMANCE_TIMINGS,
limitLength,
isValidTime,
} = require('fxa-shared').metrics.flowPerformance;
const { VERSION, PERFORMANCE_TIMINGS, limitLength, isValidTime } =
require('fxa-shared').metrics.flowPerformance;
const VALIDATION_PATTERNS = require('./validation').PATTERNS;
const DNT_ALLOWED_DATA = ['context', 'entrypoint', 'service'];

Просмотреть файл

@ -10,9 +10,10 @@
const logger = require('./log')('server.requests');
const morgan = require('morgan');
const config = require('../configuration').getProperties();
const remoteAddress = require('fxa-shared/express/remote-address')(
config.clientAddressDepth
);
const remoteAddress =
require('fxa-shared/express/remote-address').remoteAddress(
config.clientAddressDepth
);
/**
* Enhances connect logger middleware - custom formats.

Просмотреть файл

@ -12,10 +12,11 @@ const logFlowEvent = require('../flow-event').logFlowEvent;
const logger = require('../logging/log')('server.get-metrics-flow');
const geodbConfig = config.get('geodb');
const geodb = require('fxa-geodb')(geodbConfig);
const remoteAddress = require('fxa-shared/express/remote-address')(
config.get('clientAddressDepth')
);
const geolocate = require('fxa-shared/express/geo-locate')(geodb)(
const remoteAddress =
require('fxa-shared/express/remote-address').remoteAddress(
config.get('clientAddressDepth')
);
const geolocate = require('fxa-shared/express/geo-locate').geolocate(geodb)(
remoteAddress
)(logger);
const uuid = require('node-uuid');

Просмотреть файл

@ -38,7 +38,12 @@ const amplitude = proxyquire(path.resolve('server/lib/amplitude'), {
},
},
'./logging/log': () => logger,
'fxa-shared/metrics/amplitude': { validate: schemaValidatorStub },
'fxa-shared/metrics/amplitude': {
amplitude: {
...require('fxa-shared/metrics/amplitude').amplitude,
validate: schemaValidatorStub,
},
},
'@sentry/node': mockSentry,
});
const Sentry = require('@sentry/node');

Просмотреть файл

@ -50,7 +50,7 @@ registerSuite('flow-event', {
'./amplitude': mocks.amplitude,
'./configuration': mocks.config,
'./flow-metrics': mocks.flowMetrics,
'fxa-shared/express/geo-locate': mocks.geolocate,
'fxa-shared/express/geo-locate': { geolocate: mocks.geolocate },
'./statsd': mocks.statsd,
}).metricsRequest;
},

Просмотреть файл

@ -53,7 +53,7 @@ registerSuite('routes/get-metrics-flow', {
route = proxyquire('../../../server/lib/routes/get-metrics-flow', {
'../amplitude': mocks.amplitude,
'../flow-event': mocks.flowEvent,
'fxa-shared/express/geo-locate': mocks.geolocate,
'fxa-shared/express/geo-locate': { geolocate: mocks.geolocate },
'../logging/log': () => mocks.log,
});
instance = route(mocks.config);

Просмотреть файл

@ -11,7 +11,7 @@ const {
mapOs,
toSnakeCase,
validate,
} = require('fxa-shared/metrics/amplitude');
} = require('fxa-shared/metrics/amplitude').amplitude;
const config = require('../config');
const amplitude = config.get('amplitude');
const log = require('./logging/log')();

Просмотреть файл

@ -9,9 +9,11 @@ const mockAmplitudeConfig = {
schemaValidation: true,
rawEvents: false,
};
jest.mock('fxa-shared/metrics/amplitude.js', () => ({
...jest.requireActual('fxa-shared/metrics/amplitude.js'),
validate: mockSchemaValidatorFn,
jest.mock('fxa-shared/metrics/amplitude', () => ({
amplitude: {
...jest.requireActual('fxa-shared/metrics/amplitude').amplitude,
validate: mockSchemaValidatorFn,
},
}));
let scope;
const mockSentry = {

Просмотреть файл

@ -7,9 +7,10 @@
const logger = require('./log')('server.requests');
const morgan = require('morgan');
const config = require('../../config');
const remoteAddress = require('fxa-shared/express/remote-address')(
config.get('clientAddressDepth')
);
const remoteAddress =
require('fxa-shared/express/remote-address').remoteAddress(
config.get('clientAddressDepth')
);
const { enabled, format } = config.get('logging.routes');

Просмотреть файл

@ -31,7 +31,7 @@ module.exports = () => {
const csp = require('../lib/csp');
const cspRulesBlocking = require('../lib/csp/blocking')(config);
const cspRulesReportOnly = require('../lib/csp/report-only')(config);
const { cors, routing } = require('fxa-shared/express')();
const { cors, routing } = require('fxa-shared/express').express();
const { v4: uuid } = require('uuid');
const {

Просмотреть файл

@ -1,7 +1,7 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import * as ScopeSet from '../oauth/scopes';
import ScopeSet from '../oauth/scopes';
import { IClientFormatter } from './formatters';
import {
AttachedClient,

Просмотреть файл

@ -7,7 +7,7 @@ import mysql from 'mysql';
import { AccessToken as AccessToken } from '../db/models/auth/access-token';
import { ILogger } from '../log';
import * as ScopeSet from '../oauth/scopes';
import ScopeSet from '../oauth/scopes';
// TODO: Improve types. Ported form javascript...
const buf = require('buf').hex;

Просмотреть файл

@ -2,4 +2,6 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
module.exports = require('cors');
import cors from 'cors';
export default cors;

Просмотреть файл

@ -1,16 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// IP address geolocation
'use strict';
module.exports = (geodb) => (remoteAddress) => (log) => (request) => {
try {
return geodb(remoteAddress(request).clientAddress);
} catch (err) {
log.error('geodb.error', err);
return {};
}
};

Просмотреть файл

@ -0,0 +1,23 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// IP address geolocation
import express from 'express';
import { Logger } from 'mozlog';
export const geolocate =
(geodb: Function) =>
(remoteAddress: Function) =>
(log: Logger) =>
(request: express.Request) => {
try {
return geodb(remoteAddress(request).clientAddress);
} catch (err) {
log.error('geodb.error', err);
return {};
}
};
export default geolocate;

Просмотреть файл

@ -2,12 +2,12 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
module.exports = () => {
const cors = require('./cors');
const routing = require('./routing');
import cors from './cors';
import routing from './routing';
return {
cors,
routing,
};
};
export const express = () => ({
cors,
routing,
});
export default express;

Просмотреть файл

@ -1,31 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// Utility function to parse the client IP address from request headers.
'use strict';
const joi = require('joi');
const IP_ADDRESS = joi.string().ip().required();
module.exports = (clientIpAddressDepth) => (request) => {
let ipAddresses = (request.headers['x-forwarded-for'] || '')
.split(',')
.map((address) => address.trim());
ipAddresses.push(request.ip || request.connection.remoteAddress);
ipAddresses = ipAddresses.filter(
(ipAddress) => !IP_ADDRESS.validate(ipAddress).error
);
let clientAddressIndex = ipAddresses.length - clientIpAddressDepth;
if (clientAddressIndex < 0) {
clientAddressIndex = 0;
}
return {
addresses: ipAddresses,
clientAddress: ipAddresses[clientAddressIndex],
};
};

Просмотреть файл

@ -0,0 +1,35 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// Utility function to parse the client IP address from request headers.
import express from 'express';
import joi from 'joi';
const IP_ADDRESS = joi.string().ip().required();
export const remoteAddress =
(clientIpAddressDepth: number) => (request: express.Request) => {
let ipAddresses = ((request.headers['x-forwarded-for'] as string) || '')
.split(',')
.map((address) => address.trim());
ipAddresses.push(
request.ip || (request.connection.remoteAddress as string)
);
ipAddresses = ipAddresses.filter(
(ipAddress) => !IP_ADDRESS.validate(ipAddress).error
);
let clientAddressIndex = ipAddresses.length - clientIpAddressDepth;
if (clientAddressIndex < 0) {
clientAddressIndex = 0;
}
return {
addresses: ipAddresses,
clientAddress: ipAddresses[clientAddressIndex],
};
};
export default remoteAddress;

Просмотреть файл

@ -2,8 +2,29 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
module.exports = (app, logger) => {
const cors = require('./cors');
import express from 'express';
import Logger from '../lib/logger';
import cors from './cors';
type RouteMethod =
| 'all'
| 'get'
| 'post'
| 'put'
| 'delete'
| 'patch'
| 'options'
| 'head';
type RouteDefinition = {
method: RouteMethod;
path: string | RegExp;
process: Function;
cors?: any;
preProcess?: Function;
validate?: Object;
};
export const routing = (app: express.Express, logger: Logger) => {
const {
celebrate,
isCelebrateError: isValidationError,
@ -26,7 +47,7 @@ module.exports = (app, logger) => {
* @param {Object} [routeDefinition.validate] declare JOI validation.
* Follows [celebrate](https://www.npmjs.com/package/celebrate) conventions.
*/
addRoute(routeDefinition) {
addRoute(routeDefinition: RouteDefinition) {
if (!isValidRouteDefinition(routeDefinition)) {
logger.error('route definition invalid: ', routeDefinition);
throw new Error('Invalid route definition');
@ -45,7 +66,10 @@ module.exports = (app, logger) => {
: undefined;
// Enable the pre-flight OPTIONS request
const corsHandler = cors(corsConfig);
app.options(routeDefinition.path, corsHandler);
app.options(
routeDefinition.path as string,
corsHandler as express.RequestHandler
);
routeHandlers.push(corsHandler);
}
@ -63,10 +87,18 @@ module.exports = (app, logger) => {
}
routeHandlers.push(routeDefinition.process);
app[routeDefinition.method](routeDefinition.path, ...routeHandlers);
app[routeDefinition.method as RouteMethod](
routeDefinition.path as string,
...routeHandlers
);
},
validationErrorHandler(err, req, res, next) {
validationErrorHandler(
err: Error,
req: express.Request,
res: express.Response,
next: express.NextFunction
) {
if (err && isValidationError(err)) {
logger.error('validation.failed', {
err,
@ -81,7 +113,9 @@ module.exports = (app, logger) => {
},
};
function isValidRouteDefinition(route) {
function isValidRouteDefinition(route: { [key: string]: unknown }) {
return !!route.method && route.path && route.process;
}
};
export default routing;

Просмотреть файл

@ -6,6 +6,7 @@ import * as invoice from './dto/auth/payments/invoice';
import * as emailHelpers from './email/helpers';
import popularDomains from './email/popularDomains.json';
import BaseGroupingRule from './experiments/base';
import express from './express';
import featureFlags from './feature-flags';
import { localizeTimestamp } from './l10n/localizeTimestamp';
import supportedLanguages from './l10n/supportedLanguages.json';
@ -29,6 +30,7 @@ module.exports = {
experiments: {
BaseGroupingRule,
},
express,
featureFlags,
l10n: {
localizeTimestamp,

Просмотреть файл

@ -2,9 +2,19 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
'use strict';
import Ajv from 'ajv';
import { ParsedUserAgentProperties, ParsedUa, ParsedOs } from './user-agent';
import { Location } from '../connected-services/models/Location';
type AmplitudeEventGroup = typeof GROUPS;
type AmplitudeEventGroupKey = keyof AmplitudeEventGroup;
type AmplitudeEventFuzzyEventGroupMapFn = (category: string) => string;
type AmplitudeEventFuzzyEventNameMapFn = (
category: string,
target: string
) => string;
type EventData = { [key: string]: any };
const Ajv = require('ajv');
const ajv = new Ajv();
const amplitudeSchema = require('./amplitude-event.1.schema.json');
const validateAmplitudeEvent = ajv.compile(amplitudeSchema);
@ -72,11 +82,17 @@ const EVENT_PROPERTIES = {
function NOP() {}
function mapConnectDeviceFlow(eventType, eventCategory, eventTarget) {
function mapConnectDeviceFlow(
eventType: string,
eventCategory: string,
eventTarget: string
) {
// @ts-ignore
const connect_device_flow = CONNECT_DEVICE_FLOWS[eventCategory];
if (connect_device_flow) {
const result = { connect_device_flow };
const result: { connect_device_flow: string; connect_device_os?: string } =
{ connect_device_flow };
if (eventTarget) {
result.connect_device_os = eventTarget;
@ -84,13 +100,20 @@ function mapConnectDeviceFlow(eventType, eventCategory, eventTarget) {
return result;
}
return;
}
function mapEmailType(eventType, eventCategory, eventTarget, data) {
function mapEmailType(
eventType: string,
eventCategory: string,
eventTarget: string,
data: EventData
) {
const email_type = data.emailTypes[eventCategory];
if (email_type) {
const result = {
const result: { [key: string]: string } = {
email_type,
email_provider: data.emailDomain,
};
@ -103,41 +126,47 @@ function mapEmailType(eventType, eventCategory, eventTarget, data) {
return result;
}
return;
}
function mapSettingsEventProperties(...args) {
function mapSettingsEventProperties(...args: [string, string]) {
return {
...mapDisconnectReason(...args),
};
}
function mapDisconnectReason(eventType, eventCategory) {
function mapDisconnectReason(eventType: string, eventCategory: string) {
if (eventType === 'disconnect_device' && eventCategory) {
return { reason: eventCategory };
}
return;
}
function mapDomainValidationResult(
eventType,
eventCategory,
eventTarget,
data
eventType: string,
eventCategory: string,
eventTarget: string,
data: EventData
) {
// This function is called for all fxa_reg event types, only add the event
// properties for the results pertaining to domain_validation_result.
if (eventType === 'domain_validation_result' && eventCategory) {
return { validation_result: eventCategory };
}
return;
}
function mapSubscriptionUpgradeEventProperties(
eventType,
eventCategory,
eventTarget,
data
eventType: string,
eventCategory: string,
eventTarget: string,
data: EventData
) {
if (data) {
const properties = {};
const properties: { [key: string]: string } = {};
if (data.previousPlanId) {
properties['previous_plan_id'] = data.previousPlanId;
@ -149,16 +178,18 @@ function mapSubscriptionUpgradeEventProperties(
return properties;
}
return;
}
function mapSubscriptionPaymentEventProperties(
eventType,
eventCategory,
eventTarget,
data
eventType: string,
eventCategory: string,
eventTarget: string,
data: EventData
) {
if (data) {
const properties = {};
const properties: { [key: string]: string } = {};
if (data.sourceCountry) {
properties['source_country'] = data.sourceCountry;
@ -174,9 +205,11 @@ function mapSubscriptionPaymentEventProperties(
return properties;
}
return undefined;
}
function validate(event) {
function validate(event: { [key: string]: any }) {
if (!validateAmplitudeEvent(event)) {
throw new Error(
`Invalid data: ${ajv.errorsText(validateAmplitudeEvent.errors, {
@ -187,7 +220,7 @@ function validate(event) {
return true;
}
module.exports = {
export const amplitude = {
EVENT_PROPERTIES,
GROUPS,
mapBrowser,
@ -227,7 +260,23 @@ module.exports = {
*
* @returns {Function} The mapper function.
*/
initialize(services, events, fuzzyEvents) {
initialize(
services: { [key: string]: string },
events: {
[key: string]: {
group: AmplitudeEventGroupKey | Function;
event: string | AmplitudeEventFuzzyEventNameMapFn;
minimal?: boolean;
};
},
fuzzyEvents: Map<
RegExp,
{
group: AmplitudeEventGroupKey | AmplitudeEventFuzzyEventGroupMapFn;
event: string | AmplitudeEventFuzzyEventNameMapFn;
}
>
) {
/**
* Map from a source event and it's associated data to an amplitude event.
*
@ -242,7 +291,7 @@ module.exports = {
* numerous to list here, but may be discerned with
* ease by perusing the code.
*/
return (event, data) => {
return (event: { [key: string]: any }, data: EventData) => {
if (!event || !data) {
return;
}
@ -288,6 +337,7 @@ module.exports = {
let version;
try {
// @ts-ignore
version = /([0-9]+)\.([0-9]+)$/.exec(data.version)[0];
} catch (err) {}
@ -317,22 +367,28 @@ module.exports = {
device_model: data.formFactor,
event_properties: mapEventProperties(
eventType,
eventGroup,
eventCategory,
eventTarget,
eventGroup as string,
eventCategory as string,
eventTarget as string,
data
),
user_properties: mapUserProperties(
eventGroup as string,
eventCategory as string,
data
),
user_properties: mapUserProperties(eventGroup, eventCategory, data),
});
}
return;
};
function mapEventProperties(
eventType,
eventGroup,
eventCategory,
eventTarget,
data
eventType: string,
eventGroup: string,
eventCategory: string,
eventTarget: string,
data: EventData
) {
const { serviceName, clientId } = getServiceNameAndClientId(data);
@ -356,7 +412,7 @@ module.exports = {
);
}
function getServiceNameAndClientId(data) {
function getServiceNameAndClientId(data: EventData) {
let serviceName, clientId;
const { service } = data;
@ -372,7 +428,11 @@ module.exports = {
return { serviceName, clientId };
}
function mapUserProperties(eventGroup, eventCategory, data) {
function mapUserProperties(
eventGroup: string,
eventCategory: string,
data: EventData
) {
return Object.assign(
pruneUnsetValues({
entrypoint: data.entrypoint,
@ -395,7 +455,7 @@ module.exports = {
);
}
function mapAppendProperties(data) {
function mapAppendProperties(data: EventData) {
const servicesUsed = mapServicesUsed(data);
const experiments = mapExperiments(data);
const userPreferences = mapUserPreferences(data);
@ -410,9 +470,11 @@ module.exports = {
),
};
}
return;
}
function mapServicesUsed(data) {
function mapServicesUsed(data: EventData) {
const { serviceName } = getServiceNameAndClientId(data);
if (serviceName) {
@ -420,12 +482,14 @@ module.exports = {
fxa_services_used: serviceName,
};
}
return;
}
},
};
function pruneUnsetValues(data) {
const result = {};
function pruneUnsetValues(data: EventData) {
const result: Partial<EventData> = {};
Object.keys(data).forEach((key) => {
const value = data[key];
@ -438,7 +502,7 @@ function pruneUnsetValues(data) {
return result;
}
function mapExperiments(data) {
function mapExperiments(data: EventData) {
const { experiments } = data;
if (Array.isArray(experiments) && experiments.length > 0) {
@ -448,9 +512,11 @@ function mapExperiments(data) {
),
};
}
return;
}
function mapUserPreferences(data) {
function mapUserPreferences(data: EventData) {
const { userPreferences } = data;
// Don't send user preferences metric if there are none!
@ -458,7 +524,7 @@ function mapUserPreferences(data) {
return;
}
const formattedUserPreferences = {};
const formattedUserPreferences: { [key: string]: any } = {};
for (const pref in userPreferences) {
formattedUserPreferences[toSnakeCase(pref)] = userPreferences[pref];
}
@ -466,7 +532,7 @@ function mapUserPreferences(data) {
return formattedUserPreferences;
}
function toSnakeCase(string) {
function toSnakeCase(string: string) {
return string
.replace(/([a-z])([A-Z])/g, (s, c1, c2) => `${c1}_${c2.toLowerCase()}`)
.replace(/([A-Z])/g, (c) => c.toLowerCase())
@ -474,79 +540,102 @@ function toSnakeCase(string) {
.replace(/-/g, '_');
}
function mapSyncDevices(data) {
function mapSyncDevices(data: EventData) {
const { devices } = data;
if (Array.isArray(devices)) {
return {
sync_device_count: devices.length,
sync_active_devices_day: countDevices(devices, DAY),
sync_active_devices_week: countDevices(devices, WEEK),
sync_active_devices_month: countDevices(devices, FOUR_WEEKS),
sync_active_devices_day: countDevices(
devices as [{ [key: string]: any }],
DAY
),
sync_active_devices_week: countDevices(
devices as [{ [key: string]: any }],
WEEK
),
sync_active_devices_month: countDevices(
devices as [{ [key: string]: any }],
FOUR_WEEKS
),
};
}
return;
}
function countDevices(devices, period) {
function countDevices(devices: [{ [key: string]: any }], period: number) {
return devices.filter(
(device) => device.lastAccessTime >= Date.now() - period
).length;
}
function mapSyncEngines(data) {
function mapSyncEngines(data: EventData) {
const { syncEngines: sync_engines } = data;
if (Array.isArray(sync_engines) && sync_engines.length > 0) {
return { sync_engines };
}
return;
}
function mapNewsletters(data) {
function mapNewsletters(data: EventData) {
let { newsletters } = data;
if (newsletters) {
newsletters = newsletters.map((newsletter) => {
newsletters = newsletters.map((newsletter: string) => {
return toSnakeCase(newsletter);
});
return { newsletters, newsletter_state: 'subscribed' };
}
return;
}
function mapBrowser(userAgent) {
function mapBrowser(userAgent: ParsedUserAgentProperties) {
return mapUserAgentProperties(userAgent, 'ua', 'browser', 'browserVersion');
}
function mapOs(userAgent) {
function mapOs(userAgent: ParsedUserAgentProperties) {
return mapUserAgentProperties(userAgent, 'os', 'os', 'osVersion');
}
function mapUserAgentProperties(
userAgent,
key,
familyProperty,
versionProperty
userAgent: ParsedUserAgentProperties,
key: keyof ParsedUserAgentProperties,
familyProperty: string,
versionProperty: string
) {
const group = userAgent[key];
const { family } = group;
if (family && family !== 'Other') {
return {
[familyProperty]: family,
[versionProperty]: group.toVersionString(),
[versionProperty]: (group as ParsedUa | ParsedOs).toVersionString(),
};
}
return;
}
function mapFormFactor(userAgent) {
function mapFormFactor(userAgent: ParsedUserAgentProperties) {
const { brand, family: formFactor } = userAgent.device;
if (brand && formFactor && brand !== 'Generic') {
return { formFactor };
}
return;
}
function mapLocation(location) {
function mapLocation(location: Location) {
if (location && (location.country || location.state)) {
return {
country: location.country,
region: location.state,
};
}
return;
}
export default amplitude;

Просмотреть файл

@ -2,11 +2,9 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
'use strict';
const MAX_DATA_LENGTH = 100;
const VERSION = 1;
const PERFORMANCE_TIMINGS = [
export const VERSION = 1;
export const PERFORMANCE_TIMINGS = [
// These timings are only an approximation, to be used as extra signals
// when looking for correlations in the flow data. They're not perfect
// representations, for instance:
@ -37,7 +35,7 @@ const PERFORMANCE_TIMINGS = [
},
];
function limitLength(data) {
export function limitLength(data: string) {
if (data && data.length > MAX_DATA_LENGTH) {
return data.substr(0, MAX_DATA_LENGTH);
}
@ -45,7 +43,11 @@ function limitLength(data) {
return data;
}
function isValidTime(time, requestReceivedTime, expiry) {
export function isValidTime(
time: any,
requestReceivedTime: number,
expiry: number
) {
if (typeof time !== 'number') {
return false;
}
@ -58,9 +60,4 @@ function isValidTime(time, requestReceivedTime, expiry) {
return true;
}
module.exports = {
VERSION,
PERFORMANCE_TIMINGS,
limitLength,
isValidTime,
};
export default { VERSION, PERFORMANCE_TIMINGS, limitLength, isValidTime };

Просмотреть файл

@ -1,9 +1,13 @@
const joi = require('joi');
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import joi from 'joi';
const TIMESTAMP_MS = joi.number().required();
// Validate the subset of PerformanceNavgationTiming properties used here.
// (Ref: https://www.w3.org/TR/navigation-timing-2/#dom-performancenavigationtiming)
module.exports.navigationTimingSchema = joi.object().keys({
export const navigationTimingSchema = joi.object().keys({
domainLookupStart: TIMESTAMP_MS.min(0),
domComplete: TIMESTAMP_MS.min(joi.ref('domInteractive')),
domInteractive: TIMESTAMP_MS.min(joi.ref('responseEnd')),
@ -15,3 +19,5 @@ module.exports.navigationTimingSchema = joi.object().keys({
responseEnd: TIMESTAMP_MS.min(joi.ref('responseStart')),
responseStart: TIMESTAMP_MS.min(joi.ref('requestStart')),
});
export default { navigationTimingSchema };

25
packages/fxa-shared/metrics/user-agent.d.ts поставляемый
Просмотреть файл

@ -1,25 +0,0 @@
export type ParsedUa = {
family: string | null;
major: string | null;
minor: string | null;
patch: string | null;
toVersionString: () => string;
};
export type ParsedOs = ParsedUa & {
patchMinor: string | null;
};
export type ParsedDevice = {
family: string | null;
brand: string | null;
model: string | null;
};
export type ParsedUserAgentProperties = {
ua: ParsedUa;
os: ParsedOs;
device: ParsedDevice;
};
export function parse(uaString: string | undefined): ParsedUserAgentProperties;
export function isToVersionStringSupported(ua: ParsedUa): boolean;

Просмотреть файл

@ -2,12 +2,32 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
export type ParsedUa = {
family: string | null;
major: string | null;
minor: string | null;
patch: string | null;
toVersionString: () => string;
};
export type ParsedOs = ParsedUa & {
patchMinor: string | null;
};
export type ParsedDevice = {
family: string | null;
brand: string | null;
model: string | null;
};
export type ParsedUserAgentProperties = {
ua: ParsedUa;
os: ParsedOs;
device: ParsedDevice;
};
// Safe wrapper around node-uap, which prevents unsafe input from
// leaking back to the result data.
'use strict';
const ua = require('node-uap');
// @ts-ignore
import * as ua from 'node-uap';
// We know this won't match "Symbian^3", "UI/WKWebView" or "Mail.ru" but
// it's simpler and safer to limit to alphanumerics, underscore and space.
@ -15,7 +35,9 @@ const VALID_FAMILY = /^[\w ]{1,32}$/;
const VALID_VERSION = /^[\w.]{1,16}$/;
exports.parse = (userAgentString) => {
export const parse = (
userAgentString: string | undefined
): ParsedUserAgentProperties => {
const result = ua.parse(userAgentString);
safeFamily(result.ua);
@ -27,7 +49,9 @@ exports.parse = (userAgentString) => {
return result;
};
exports.isToVersionStringSupported = (result) => {
export const isToVersionStringSupported = (
result: ParsedUserAgentProperties
): boolean => {
if (!result) {
result = exports.parse(
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:65.0) Gecko/20100101 Firefox/65.0'
@ -45,13 +69,13 @@ exports.isToVersionStringSupported = (result) => {
return true;
};
function safeFamily(parent) {
if (!VALID_FAMILY.test(parent.family)) {
function safeFamily(parent: ParsedUa) {
if (!VALID_FAMILY.test(parent.family as string)) {
parent.family = null;
}
}
function safeVersion(parent) {
function safeVersion(parent: ParsedOs) {
if (
parent &&
parent.toVersionString &&
@ -60,3 +84,5 @@ function safeVersion(parent) {
parent.major = parent.minor = parent.patch = parent.patchMinor = null;
}
}
export default { parse, isToVersionStringSupported };

Просмотреть файл

@ -1,18 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const OAUTH_SCOPE_SUBSCRIPTIONS =
'https://identity.mozilla.com/account/subscriptions';
module.exports = {
OAUTH_SCOPE_OLD_SYNC: 'https://identity.mozilla.com/apps/oldsync',
OAUTH_SCOPE_SESSION_TOKEN: 'https://identity.mozilla.com/tokens/session',
OAUTH_SCOPE_NEWSLETTERS: 'https://identity.mozilla.com/account/newsletters',
OAUTH_SCOPE_SUBSCRIPTIONS,
OAUTH_SCOPE_SUBSCRIPTIONS_IAP: `${OAUTH_SCOPE_SUBSCRIPTIONS}/iap`,
SHORT_ACCESS_TOKEN_TTL_IN_MS: 1000 * 60 * 60 * 6,
// Maximum age an account is considered "new", useful when sending
// notification emails
MAX_NEW_ACCOUNT_AGE: 1000 * 60 * 60 * 24,
};

Просмотреть файл

@ -0,0 +1,29 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
export const OAUTH_SCOPE_SUBSCRIPTIONS =
'https://identity.mozilla.com/account/subscriptions';
export const OAUTH_SCOPE_OLD_SYNC = 'https://identity.mozilla.com/apps/oldsync';
export const OAUTH_SCOPE_SESSION_TOKEN =
'https://identity.mozilla.com/tokens/session';
export const OAUTH_SCOPE_NEWSLETTERS =
'https://identity.mozilla.com/account/newsletters';
export const OAUTH_SCOPE_SUBSCRIPTIONS_IAP = `${OAUTH_SCOPE_SUBSCRIPTIONS}/iap`;
export const SHORT_ACCESS_TOKEN_TTL_IN_MS = 1000 * 60 * 60 * 6;
// Maximum age an account is considered "new"; useful when sending
// notification emails
export const MAX_NEW_ACCOUNT_AGE = 1000 * 60 * 60 * 24;
export const OauthConsts = {
OAUTH_SCOPE_OLD_SYNC,
OAUTH_SCOPE_SESSION_TOKEN,
OAUTH_SCOPE_NEWSLETTERS,
OAUTH_SCOPE_SUBSCRIPTIONS,
OAUTH_SCOPE_SUBSCRIPTIONS_IAP,
SHORT_ACCESS_TOKEN_TTL_IN_MS,
MAX_NEW_ACCOUNT_AGE,
};
export default OauthConsts;

Просмотреть файл

@ -2,9 +2,9 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
'use strict';
import { URL } from 'url';
const { URL } = require('url');
type Coerceable = ScopeSet | string[] | string;
// These character ranges are from the OAuth RFC,
// https://tools.ietf.org/html/rfc6749#section-3.3
@ -75,7 +75,10 @@ const VALID_FRAGMENT_VALUE = /^#[a-zA-Z0-9_]+$/;
*
*/
class ScopeSet {
constructor(scopes = []) {
private _scopesToImplicants: { [key: string]: Set<string> };
private _implicantsToScopes: { [key: string]: Set<string> };
constructor(scopes: string[] = []) {
// To support efficient lookups, we store the set of scopes and
// their fully-expanded set of implicants in a bi-directional mapping.
//
@ -103,7 +106,7 @@ class ScopeSet {
* Check whether this `ScopeSet` contains the given scope.
*
*/
_hasScope(scope) {
_hasScope(scope: string) {
return scope in this._scopesToImplicants;
}
@ -111,7 +114,7 @@ class ScopeSet {
* Check whether this `ScopeSet` contains one of the given scopes.
*
*/
_hasSomeScope(scopes) {
_hasSomeScope(scopes: Set<string>) {
for (const scope of scopes) {
if (this._hasScope(scope)) {
return true;
@ -125,7 +128,7 @@ class ScopeSet {
* the given scope.
*
*/
_hasImplicant(scope) {
_hasImplicant(scope: string) {
return scope in this._implicantsToScopes;
}
@ -134,7 +137,7 @@ class ScopeSet {
* one of the given scopes.
*
*/
_hasSomeImplicant(scopes) {
_hasSomeImplicant(scopes: string[]) {
for (const scope of scopes) {
if (scope in this._implicantsToScopes) {
return true;
@ -148,7 +151,7 @@ class ScopeSet {
* corresponding sets of implicants.
*
*/
_iterScopes(cb) {
_iterScopes(cb: (scope: string, implicants: Set<string>) => void) {
for (const scope in this._scopesToImplicants) {
cb(scope, this._scopesToImplicants[scope]);
}
@ -159,7 +162,7 @@ class ScopeSet {
* corresponding sets of implied scopes.
*
*/
_iterImplicants(cb) {
_iterImplicants(cb: (implicant: string, scopes: Set<string>) => void) {
for (const implicant in this._implicantsToScopes) {
cb(implicant, this._implicantsToScopes[implicant]);
}
@ -170,7 +173,7 @@ class ScopeSet {
* the given scope.
*
*/
_iterImpliedScopes(implicant, cb) {
_iterImpliedScopes(implicant: string, cb: (s: string) => void) {
const impliedScopes = this._implicantsToScopes[implicant];
if (impliedScopes) {
for (const impliedScope of impliedScopes) {
@ -185,7 +188,7 @@ class ScopeSet {
* at the first successful match.
*
*/
_searchScopes(cb) {
_searchScopes(cb: (scope: string, implicants: Set<string>) => boolean) {
for (const scope in this._scopesToImplicants) {
if (cb(scope, this._scopesToImplicants[scope])) {
return true;
@ -200,7 +203,7 @@ class ScopeSet {
* at the first successful match.
*
*/
_searchImplicants(cb) {
_searchImplicants(cb: (implicant: string, scopes: Set<string>) => boolean) {
for (const implicant in this._implicantsToScopes) {
if (cb(implicant, this._implicantsToScopes[implicant])) {
return true;
@ -216,7 +219,7 @@ class ScopeSet {
* order to keep memory usage down and simplify further handling.
*
*/
_addScope(scope, implicants) {
_addScope(scope: string, implicants: Set<string>) {
// If the scope is already implied by something in this `ScopeSet`,
// then we can safely ignore it.
if (this._hasSomeScope(implicants)) {
@ -244,7 +247,7 @@ class ScopeSet {
* scopes and their implicants.
*
*/
_removeScope(scope) {
_removeScope(scope: string) {
const implicants = this._scopesToImplicants[scope];
for (const implicant of implicants) {
const impliedScopes = this._implicantsToScopes[implicant];
@ -307,8 +310,8 @@ class ScopeSet {
* quadratic" performance traps.
*
*/
add(other) {
other = coerce(other)._iterScopes((scope, implicants) => {
add(other: Coerceable) {
coerce(other)._iterScopes((scope, implicants) => {
this._addScope(scope, implicants);
});
}
@ -322,7 +325,7 @@ class ScopeSet {
* than `B`.
*
*/
contains(other) {
contains(other: Coerceable) {
return !coerce(other)._searchScopes((scope, implicants) => {
return !this._hasSomeScope(implicants);
});
@ -336,14 +339,14 @@ class ScopeSet {
* some scope value in `A` that is implied by a scope value in `B`.
*
*/
intersects(other) {
intersects(other: Parameters<typeof coerce>[number]) {
other = coerce(other);
return (
other._searchImplicants((implicant) => {
return this._hasScope(implicant);
}) ||
this._searchImplicants((implicant) => {
return other._hasScope(implicant);
return (other as ScopeSet)._hasScope(implicant);
})
);
}
@ -375,11 +378,11 @@ class ScopeSet {
* directly a member of `A`, it will not appear in the result.
*
*/
filtered(other) {
filtered(other: Coerceable) {
other = coerce(other);
const result = new ScopeSet();
this._iterScopes((scope, implicants) => {
if (other._hasSomeScope(implicants)) {
if ((other as ScopeSet)._hasSomeScope(implicants)) {
result._addScope(scope, implicants);
}
});
@ -396,11 +399,11 @@ class ScopeSet {
* `A.filtered(B)`. It returns a new `ScopeSet` object.
*
*/
difference(other) {
difference(other: Coerceable) {
other = coerce(other);
const result = new ScopeSet();
this._iterScopes((scope, implicants) => {
if (!other._hasSomeScope(implicants)) {
if (!(other as ScopeSet)._hasSomeScope(implicants)) {
result._addScope(scope, implicants);
}
});
@ -415,7 +418,7 @@ class ScopeSet {
* either `A` or `B` (or both). It returns a new `ScopeSet` object.
*
*/
union(other) {
union(other: Coerceable) {
other = coerce(other);
const result = new ScopeSet();
this._iterScopes((scope, implicants) => {
@ -433,7 +436,7 @@ class ScopeSet {
* kinds of input data into a `ScopeSet` instance.
*
*/
function coerce(scopes) {
function coerce(scopes: Coerceable) {
if (scopes instanceof ScopeSet) {
return scopes;
}
@ -452,7 +455,7 @@ function coerce(scopes) {
* An iterator yielding all implicants of the given scope value.
*
*/
function getImplicantValues(value) {
function getImplicantValues(value: string) {
if (value.startsWith('https:')) {
return getImplicantValuesForURLScope(value);
} else {
@ -478,7 +481,7 @@ function getImplicantValues(value) {
* only "write" scopes can imply other "write" scopes.
*
*/
function* getImplicantValuesForShortScope(value) {
function* getImplicantValuesForShortScope(value: string) {
if (!VALID_SCOPE_VALUE.test(value)) {
throw new Error('Invalid scope value: ' + value);
}
@ -522,7 +525,7 @@ function* getImplicantValuesForShortScope(value) {
* URL is a child of another.
*
*/
function* getImplicantValuesForURLScope(value) {
function* getImplicantValuesForURLScope(value: string) {
if (!VALID_SCOPE_VALUE.test(value)) {
throw new Error('Invalid scope value: ' + value);
}
@ -532,7 +535,7 @@ function* getImplicantValuesForURLScope(value) {
throw new Error('Invalid scope value: ' + value);
}
// No credentials or query params are allowed.
if (url.username || url.password || url.query) {
if (url.username || url.password || url.search) {
throw new Error('Invalid scope value: ' + value);
}
// The pathname must be non-empty and not end in a slash.
@ -561,12 +564,12 @@ function* getImplicantValuesForURLScope(value) {
}
}
module.exports = {
export const scopeSetHelpers = {
/**
* Parse a list of strings into a Scope object.
*
*/
fromArray(scopesArray) {
fromArray(scopesArray: string[]) {
return new ScopeSet(scopesArray);
},
@ -578,7 +581,7 @@ module.exports = {
* case-sensitive strings identifying individual scope values.
*
*/
fromString(scopesString) {
fromString(scopesString: string) {
// Split the string by one or more space characters.
return new ScopeSet(
scopesString.split(/ +/).filter((scopeString) => {
@ -597,7 +600,9 @@ module.exports = {
* characters in the scopes will be expanded.
*
*/
fromURLEncodedString(encodedScopesString) {
fromURLEncodedString(
encodedScopesString: ReturnType<typeof encodeURIComponent>
) {
// Split the string by a literal plus character.
return new ScopeSet(
encodedScopesString
@ -611,3 +616,5 @@ module.exports = {
);
},
};
export default scopeSetHelpers;

Просмотреть файл

@ -5,7 +5,52 @@
"main": "dist/index.js",
"exports": {
".": "./dist/index.js",
"./": "./dist/"
"./*": "./dist/*",
"./auth": "./dist/auth/index.js",
"./auth/*": "./dist/auth/*.js",
"./cache/*": "./dist/cache/*.js",
"./configuration/*": "./dist/configuration/*.js",
"./connected-services": "./dist/connected-services/index.js",
"./connected-services/*": "./dist/connected-services/*.js",
"./coverage/*": "./dist/coverage/*.js",
"./db": "./dist/db/index.js",
"./db/*": "./dist/db/*.js",
"./db/config": "./dist/db/config.js",
"./db/models": "./dist/db/models/index.js",
"./db/models/auth": "./dist/db/models/auth/index.js",
"./db/models/auth/*": "./dist/db/models/auth/*.js",
"./db/models/profile": "./dist/db/models/profile/index.js",
"./db/mysql": "./dist/db/mysql.js",
"./db/redis": "./dist/db/redis.js",
"./dto/*": "./dist/dto/*.js",
"./email/*": "./dist/email/*.js",
"./email/popularDomains.json": "./dist/email/popularDomains.json",
"./experiments/*": "./dist/experiments/*.js",
"./express": "./dist/express/index.js",
"./express/*": "./dist/express/*.js",
"./feature-flags": "./dist/feature-flags/index.js",
"./feature-flags/*": "./dist/feature-flags/*.js",
"./guards": "./dist/guards/index.js",
"./guards/*": "./dist/guards/*.js",
"./l10n/*": "./dist/l10n/*.js",
"./lib/*": "./dist/lib/*.js",
"./log": "./dist/log/index.js",
"./log/*": "./dist/log/*.js",
"./metrics/*": "./dist/metrics/*.js",
"./nestjs/*": "./dist/nestjs/*.js",
"./oauth/*": "./dist/oauth/*.js",
"./payments/*": "./dist/payments/*.js",
"./payments/configuration/*": "./dist/payments/configuration/*.js",
"./payments/iap/*": "./dist/payments/iap/*.js",
"./payments/iap/apple-app-store/types": "./dist/payments/iap/apple-app-store/types/index.js",
"./payments/iap/google-play/types": "./dist/payments/iap/google-play/types/index.js",
"./payments/stripe": "./dist/payments/stripe.js",
"./payments/stripe-firestore": "./dist/payments/stripe-firestore.js",
"./scripts/*": "./dist/scripts/*.js",
"./sentry": "./dist/sentry/index.js",
"./sentry/*": "./dist/sentry/*.js",
"./subscriptions/*": "./dist/subscriptions/*.js",
"./tracing/*": "./dist/tracing/*.js"
},
"scripts": {
"postinstall": "yarn build || true",
@ -43,6 +88,7 @@
"@types/accept-language-parser": "^1.5.3",
"@types/chai": "^4.2.18",
"@types/chance": "^1.1.2",
"@types/express": "^4.17.12",
"@types/generic-pool": "^3.1.9",
"@types/i18n-abide": "^0",
"@types/ioredis": "^4.26.4",
@ -106,8 +152,6 @@
"@sentry/browser": "^6.19.7",
"@sentry/integrations": "^6.19.1",
"@sentry/node": "^6.19.1",
"@types/js-md5": "^0.4.2",
"@types/uuid": "^8.3.1",
"accept-language-parser": "^1.5.0",
"ajv": "^8.11.0",
"apollo-server": "^2.26.0",
@ -120,6 +164,7 @@
"cldr-localenames-full": "42.0.0",
"cors": "^2.8.5",
"db-migrations": "workspace:*",
"express": "^4.17.2",
"find-up": "^5.0.0",
"generic-pool": "^3.8.2",
"graphql": "^15.6.1",

Просмотреть файл

@ -5,7 +5,7 @@
'use strict';
const { assert } = require('chai');
const remoteAddress = require('../../express/remote-address')(3);
const remoteAddress = require('../../express/remote-address').remoteAddress(3);
describe('remote-address', () => {
it('has the correct interface', () => {

Просмотреть файл

@ -34,7 +34,7 @@ describe('express/routing:', () => {
routingFactory = proxyquire('../../express/routing', {
celebrate: celebrateMock,
'./cors': () => corsHandler,
'./cors': { default: () => corsHandler },
});
appMock = {
@ -49,7 +49,7 @@ describe('express/routing:', () => {
error: sinon.spy(),
};
routing = routingFactory(appMock, loggerMock);
routing = routingFactory.default(appMock, loggerMock);
});
it('exposes the correct interface', () => {

Просмотреть файл

@ -15,7 +15,7 @@ describe('metrics/amplitude:', () => {
let amplitude;
before(() => {
amplitude = require('../../metrics/amplitude');
amplitude = require('../../metrics/amplitude').amplitude;
});
it('exports the event groups', () => {

Просмотреть файл

@ -10,7 +10,7 @@ describe('oauth/scopes:', () => {
let scopes;
before(() => {
scopes = require('../../oauth/scopes');
scopes = require('../../oauth/scopes').default;
});
describe('valid implications', () => {

Просмотреть файл

@ -25680,6 +25680,7 @@ fsevents@~2.1.1:
"@types/accept-language-parser": ^1.5.3
"@types/chai": ^4.2.18
"@types/chance": ^1.1.2
"@types/express": ^4.17.12
"@types/generic-pool": ^3.1.9
"@types/i18n-abide": ^0
"@types/ioredis": ^4.26.4
@ -25714,6 +25715,7 @@ fsevents@~2.1.1:
esbuild-register: ^3.2.0
eslint: ^7.32.0
eslint-plugin-fxa: ^2.0.2
express: ^4.17.2
find-up: ^5.0.0
generic-pool: ^3.8.2
graphql: ^15.6.1