Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Add types for VGSCollect #34

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"description": "VGS Collect.js script loading module",
"license": "MIT",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"typings": "dist/index.d.ts",
"files": [
"dist",
Expand Down
4 changes: 2 additions & 2 deletions src/constants/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { v4 as uuidv4 } from 'uuid';
import { IConfig } from '../utils/IConfig';
import { LoadVGSCollectConfig } from '../types/config';

// Loading script from CloudFront CDN
const MAIN_SCRIPT_DOMAIN = `https://js.verygoodvault.com`;
Expand All @@ -8,7 +8,7 @@ const BACKUP_SCRIPT_DOMAIN = `https://js3.verygoodvault.com`;
const VGS_COLLECT_KEEPER = `https://vgs-collect-keeper.apps.verygood.systems`;
const SESSION_ID = uuidv4();

const DEFAULT_CONFIG: IConfig = {
const DEFAULT_CONFIG: LoadVGSCollectConfig = {
vaultId: '',
environment: 'sandbox',
version: 'canary',
Expand Down
10 changes: 8 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ import { ERROR_MESSAGE, DEFAULT_CONFIG } from './constants';

import { preFetch } from './sideEffects/preFetch';
import { preConnect } from './sideEffects/preConnect';
import { IConfig } from './utils/IConfig';

import { VGSCollectInstance } from './types/collect-api/form';
import { LoadVGSCollectConfig } from './types/config';

export * from './types/collect-api/form';

// side effects
Promise.resolve().then(() => {
Expand All @@ -19,7 +23,9 @@ Promise.resolve().then(() => {
}
});

const loadVGSCollect = (config: IConfig = isRequired('config')) => {
const loadVGSCollect = (
config: LoadVGSCollectConfig = isRequired('config')
): Promise<VGSCollectInstance | null> => {
const {
vaultId = isRequired('vaultId'),
environment = DEFAULT_CONFIG.environment,
Expand Down
105 changes: 105 additions & 0 deletions src/types/collect-api/field.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
type FieldState =
| 'dirty'
| 'empty'
| 'valid'
| 'invalid'
| 'focused'
| 'touched';
export type FieldType =
| 'text'
| 'card-number'
| 'card-expiration-date'
| 'card-security-code'
| 'ssn'
| 'zip-code'
| 'file'
| 'password'
| 'number'
| 'checkbox'
| 'radio'
| 'dropdown'
| 'textarea';
export type ClassMap = Partial<Record<FieldState, string>>;

type VGSKeyboardEventData<T = 'keydown' | 'keypress' | 'keyup'> = {
type: T;
timeStamp: number;
isTrusted: boolean;
key: string | null;
keyCode: number | null;
which: number | null;
metaKey: boolean;
ctrlKey: boolean;
valueHidden: boolean;
keyIndex: number;
};

type VGSFocusEventData<T = 'focus' | 'blur'> = {
type: T;
timeStamp: number;
isTrusted: boolean;
};

export interface FieldInstance {
classes: ClassMap;
container: HTMLElement;
debugId: string;
env: string;
fieldId: string;
formId: string;
name: string;
tnt: string;
type: FieldType;
/**
* Docs: https://www.verygoodsecurity.com/docs/vgs-collect/js/integration#field-instance-events
*/
on(
eventType: 'keydown',
callback: (event: VGSKeyboardEventData<'keydown'>) => void
): void;
on(
eventType: 'keypress',
callback: (event: VGSKeyboardEventData<'keypress'>) => void
): void;
on(
eventType: 'keyup',
callback: (event: VGSKeyboardEventData<'keyup'>) => void
): void;
on(eventType: 'delete', callback: () => void): void;
on(eventType: 'update', callback: (fieldState: any) => void): void;
on(
eventType: 'focus',
callback: (event: VGSFocusEventData<'focus'>) => void
): void;
on(
eventType: 'blur',
callback: (event: VGSFocusEventData<'blur'>) => void
): void;

/**
* Docs: https://www.verygoodsecurity.com/docs/vgs-collect/js/integration#field-instance-events
*/
off(
eventType: 'keydown',
callback: (event: VGSKeyboardEventData<'keydown'>) => void
): void;
off(
eventType: 'keypress',
callback: (event: VGSKeyboardEventData<'keypress'>) => void
): void;
off(
eventType: 'keyup',
callback: (event: VGSKeyboardEventData<'keyup'>) => void
): void;
off(eventType: 'delete', callback: () => void): void;
off(eventType: 'update', callback: (fieldState: any) => void): void;
off(
eventType: 'focus',
callback: (event: VGSFocusEventData<'focus'>) => void
): void;
off(
eventType: 'blur',
callback: (event: VGSFocusEventData<'blur'>) => void
): void;
off(eventType: 'update', callback: (fieldState: any) => void): void;
}
178 changes: 178 additions & 0 deletions src/types/collect-api/form.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
import { FieldInstance, ClassMap, FieldType } from './field';

/**
* Available options for .field() method configuration
*/
type BooleanValue = boolean | 'true' | 'false';
type FileType =
| 'image/png'
| 'image/apng'
| 'image/avif'
| 'image/gif'
| 'image/jpeg'
| 'image/svg+xml'
| 'image/svg'
| 'image/webp'
| 'image/*'
| '.pdf'
| '.csv';
type FileCapture = 'user' | 'environment';
type YearLength = '2' | '4' | 2 | 4;

/**
* Available options for .submit() method
*/
type SubmitMethod = 'post' | 'patch' | 'put' | 'delete' | 'get';
type SubmitSerializer = 'deep' | 'flat';
type SubmitSerialization = 'json' | 'formData';
type SubmitMapDotToObject = BooleanValue | 'merge' | 'mergeArray';

/**
* Available options for the form .on() method
*/
type FormEventTypes = 'enterPress';

interface VGSCollectStateParams {
name: string;
errorMessages: string[];
isDirty: boolean;
isTouched: boolean;
isFocused: boolean;
isValid: boolean;
isEmpty: boolean;
last4?: string;
bin?: string;
/**
* TODO: check for another properties
*/
}

interface CardInfo {
type: string;
pattern: RegExp;
format?: RegExp;
length?: number[];
cvvLength?: number[];
luhn?: Boolean;
}

interface VGSCollectSubmitOptions {
data: object | ((values: any) => any);
headers: object;
method: SubmitMethod;
serailizer: SubmitSerializer;
serialization: SubmitSerialization;
mapDotToObject: SubmitMapDotToObject;
withCredentials: BooleanValue;
}

export interface VGSCollectFieldOptions {
/**
* TODO: check full list
*/
type: FieldType;
name: string;
validations?: string[];
css?: Record<string, any>;
successColor?: string;
errorColor?: string;
classes?: ClassMap;
serializers?: any;
hideValue?: BooleanValue;
autoComplete?: string;
placeholder?: string;
autoFocus?: BooleanValue;
disabled?: BooleanValue;
ariaLabel?: string;
readonly?: BooleanValue;
/**
* card-number specific properties
*/
icons?: Record<string, string>;
showCardIcon?: BooleanValue | Record<string, any>;
addCardBrands?: CardInfo[];
/**
* file specific properties
*/
multiple?: BooleanValue;
accept?: FileType[];
capture?: FileCapture;
maxFileSize?: number;
maxFiles?: number;
/**
* text specific properties
*/
min?: number;
max?: number;
maxLength?: number;
step?: number;
/**
* card-expiration-date specific properties
*/
yearLength?: YearLength;
separator?: string;
/**
* checkbox specific properties
*/
value?: string;
/**
* dropdown specific properties
*/
options?: string[];
}

export type VGSCollectFormState = Record<string, VGSCollectStateParams> | null;

export interface VGSCollectForm {
/**
* Docs: https://www.verygoodsecurity.com/docs/vgs-collect/js/integration#create-and-setup-form-fields
*/
field(selector: string, options: VGSCollectFieldOptions): FieldInstance;

/**
* Docs: https://www.verygoodsecurity.com/docs/vgs-collect/js/integration#form-instance-events
*/
on(event: FormEventTypes, callback: (info: { name: string }) => void): void;
off(event: FormEventTypes, callback: () => void): void;

/**
* Docs: https://www.verygoodsecurity.com/docs/vgs-collect/js/integration#setup-form-submission
*/
submit(
path: string,
options: Partial<VGSCollectSubmitOptions>,
successCallback: (status: number | null, data: any) => any,
errorCallback: (error: VGSCollectFormState) => any
): any;

/**
* Docs: https://www.verygoodsecurity.com/docs/vgs-collect/js/integration#form-reset
*/
reset(): any;

/**
* Docs: https://www.verygoodsecurity.com/docs/vgs-collect/js/integration#form-unmount
*/
unmount(): void;

/**
* Docs: https://www.verygoodsecurity.com/docs/vgs-collect/js/cookbook#how-to-integrate-vgs-collectjs-with-vgs-satellite
*/
connectSatellite(port: number): any;

/**
* Docs: https://www.verygoodsecurity.com/docs/vgs-collect/js/integration#vgs-collect-with-cname
*/
useCname(cname: string): void;
}

export interface VGSCollectInstance {
init(
stateCallback?: (state: VGSCollectFormState) => void | undefined
): VGSCollectForm;
create(
tenantId: string,
environment: string,
stateCallback?: (state: VGSCollectFormState) => void
): VGSCollectForm;
}
2 changes: 1 addition & 1 deletion src/utils/IConfig.ts → src/types/config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export interface IConfig {
export interface LoadVGSCollectConfig {
vaultId: string;
environment: string;
version: string;
Expand Down
4 changes: 2 additions & 2 deletions src/utils/config.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { DEFAULT_CONFIG } from '../constants/index';
import { IConfig } from './IConfig';
import { LoadVGSCollectConfig } from '../types/config';

let config = DEFAULT_CONFIG;

const setConfig = (params: IConfig) => {
const setConfig = (params: LoadVGSCollectConfig) => {
if (params) {
config = { ...params };
}
Expand Down
6 changes: 1 addition & 5 deletions src/utils/initCollect.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
interface IStateCallback {
(state?: object): void;
}

const initCollect = (vaultId: string, environment: string): void => {
window.VGSCollect.init = (callback: IStateCallback = () => {}) => {
window.VGSCollect.init = callback => {
return window.VGSCollect.create(vaultId, environment, callback);
};
};
Expand Down
4 changes: 2 additions & 2 deletions src/utils/trackEvent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import {
VGS_COLLECT_KEEPER,
} from '../constants/index';
import { setConfig, getConfig } from './config';
import { IConfig } from './IConfig';
import { LoadVGSCollectConfig } from '../types/config';
import { validateConfig } from './validation';

const registerScriptLoading = (params: IConfig) => {
const registerScriptLoading = (params: LoadVGSCollectConfig) => {
validateConfig(params);
setConfig(params);
trackEvent({
Expand Down
4 changes: 2 additions & 2 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"include": ["src", "types"],
"compilerOptions": {
"include": ["src", "types"],
"compilerOptions": {
"module": "esnext",
"lib": ["dom", "esnext"],
"importHelpers": true,
Expand Down
4 changes: 2 additions & 2 deletions types/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
interface Window {
VGSCollect: any;
}
VGSCollect: import('../src/types/collect-api/form').VGSCollectInstance;
}