diff --git a/.gitignore b/.gitignore index baeba0c..bc90d90 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,7 @@ # production /build - +**/dist/* # misc .DS_Store .env.local diff --git a/packages/v1-ready/docusign/.env.example b/packages/v1-ready/docusign/.env.example new file mode 100644 index 0000000..8c2f21e --- /dev/null +++ b/packages/v1-ready/docusign/.env.example @@ -0,0 +1,6 @@ +DOCUSIGN_CLIENT_ID= +DOCUSIGN_CLIENT_SECRET= +DOCUSIGN_ENVIRONMENT=dev +REDIRECT_URI=http://localhost:3000 + +DOCUSIGN_SCOPE= \ No newline at end of file diff --git a/packages/v1-ready/docusign/README.md b/packages/v1-ready/docusign/README.md new file mode 100644 index 0000000..ebe02fc --- /dev/null +++ b/packages/v1-ready/docusign/README.md @@ -0,0 +1,42 @@ +# DocuSign API Module + +This is the API Module for DocuSign that allows the [Frigg Framework](https://friggframework.org) to interact with the DocuSign eSignature REST API. + +## Features + +Currently implemented: +* List Envelopes +* Get Envelope Details +* Create Envelope +* Void Envelope +* List Templates +* Get Template Details +* Retrieve User Info (for account discovery) + +## Setup + +1. Install dependencies: `npm install` +2. Configure environment variables by copying `.env.example` to `.env` and filling in the required values (Client ID, Client Secret, Environment). + +## Usage + +```typescript +import { Api, definition } from '@friggframework/api-module-docusign'; + +// Configuration typically loaded from environment variables +const config = { + client_id: process.env.DOCUSIGN_CLIENT_ID, + client_secret: process.env.DOCUSIGN_CLIENT_SECRET, + // ... other credentials like access/refresh tokens if available + // ... account_id might be set here or retrieved later +}; + +const api = new Api(config); + +// Example: List sent envelopes +api.listEnvelopes({ status: 'sent' }) + .then(envelopes => console.log(envelopes)) + .catch(error => console.error(error)); +``` + +Read more on the [Frigg documentation site](https://docs.friggframework.org/). \ No newline at end of file diff --git a/packages/v1-ready/docusign/api.ts b/packages/v1-ready/docusign/api.ts new file mode 100644 index 0000000..1e68d58 --- /dev/null +++ b/packages/v1-ready/docusign/api.ts @@ -0,0 +1,308 @@ +import { OAuth2Requester } from '@friggframework/core'; + +interface DocuSignConstructorParams { + client_id: string; + client_secret: string; + redirect_uri: string; + scope: string; + environment: 'dev' | 'prod'; // Added environment + state?: string; + access_token?: string; + refresh_token?: string; + base_url?: string; // User override OR stored base_uri from entity + account_id?: string; +} + +interface EnvelopeDefinition { + // Define structure based on https://developers.docusign.com/docs/esign-rest-api/reference/envelopes/envelopes/create/ + emailSubject: string; + documents: any[]; // Replace 'any' with a proper Document type + recipients: any; // Replace 'any' with a proper Recipients type (signers, carbonCopies etc.) + status: 'sent' | 'created'; // Typically 'sent' to send immediately, 'created' for draft + [key: string]: any; // Allow other properties +} + +interface VoidEnvelopeRequest { + status: 'voided'; + voidedReason: string; +} + +interface TokenResponse { + access_token: string; + refresh_token: string; + token_type: string; + expires_in: number; + [key: string]: any; +} + +interface ListTemplatesQueryParams { + count?: number; + start_position?: number; + from_date?: string; // ISO 8601 format + to_date?: string; // ISO 8601 format + search_text?: string; + order?: 'asc' | 'desc'; + order_by?: 'name' | 'modified' | 'used'; + folder_ids?: string; // Comma-separated list of folder IDs + include?: string; // Comma-separated list (e.g., 'documents,recipients,tabs') + user_filter?: 'all' | 'owned_by_me' | 'shared_with_me'; + shared_by_me?: string; // 'true' or 'false' + used_from_date?: string; // ISO 8601 format + used_to_date?: string; // ISO 8601 format +} + +interface GetTemplateQueryParams { + include?: string; // Comma-separated list (e.g., 'documents,recipients,tabs') +} + +export class Api extends OAuth2Requester { + protected accountId?: string; + protected baseUriHost: string; // Renamed from baseUrl - stores the host part + protected environment: 'dev' | 'prod'; + public authorizationUri: string; + public tokenUri: string; + protected authHost: string; + + declare public client_id: string; + declare public client_secret: string; + declare public redirect_uri: string; + + constructor(params: DocuSignConstructorParams) { + super(params); + this.environment = params.environment || 'dev'; + + this.authHost = this.environment === 'prod' + ? 'https://account.docusign.com' + : 'https://account-d.docusign.com'; + + + let host = params.base_url; + if (!host) { + host = this.environment === 'prod' + ? 'https://docusign.net' + : 'https://demo.docusign.net'; + } + this.baseUriHost = host; + this.accountId = params.account_id; + + this.authorizationUri = `${this.authHost}/oauth/auth`; + this.tokenUri = `${this.authHost}/oauth/token`; + } + + setAccountId(accountId: string) { + this.accountId = accountId; + } + + private _getAccountApiBaseUrl(): string { + if (!this.accountId) { + throw new Error('DocuSign Account ID is required but not set.'); + } + if (!this.baseUriHost) { + throw new Error('DocuSign Base URI Host is required but not set.'); + } + + return `${this.baseUriHost}/restapi/v2.1/accounts/${this.accountId}`; + } + + getAuthorizationUri(): string { + const baseUri = `${this.authHost}/oauth/auth`; + const params = new URLSearchParams(); + + params.append('response_type', 'code'); + params.append('client_id', this.client_id); + params.append('redirect_uri', this.redirect_uri); + params.append('scope', this.scope); + if (this.state) { + params.append('state', this.state); + } + + // Note: Does not include PKCE parameters (code_challenge) + // Consider adding if needed for enhanced security, requires generating code_verifier. + + return `${baseUri}?${params.toString()}`; + } + + addJsonHeaders(options: any) { + const jsonHeaders = { + 'Content-Type': 'application/json', + Accept: 'application/json', + }; + options.headers = { + ...jsonHeaders, + ...options.headers, + }; + } + + async _get(options: any) { + this.addJsonHeaders(options); + return super._get(options); + } + + async _post(options: any, stringify?: boolean) { + this.addJsonHeaders(options); + return super._post(options, stringify); + } + + async _put(options: any, stringify?: boolean) { + this.addJsonHeaders(options); + return super._put(options, stringify); + } + + async _patch(options: any, stringify?: boolean) { + this.addJsonHeaders(options); + return super._patch(options, stringify); + } + + async _delete(options: any) { + this.addJsonHeaders(options); + return super._delete(options); + } + + // Method to handle the OAuth token exchange (authorization code flow) + async getTokenFromCode(code: string): Promise { + const url = this.tokenUri; + + // Ensure 'this' is used for base class protected members + const body = new URLSearchParams({ + grant_type: 'authorization_code', + code: code, + redirect_uri: this.redirect_uri, // Explicit this + }).toString(); + + const clientCredentials = Buffer.from( + `${this.client_id}:${this.client_secret}` // Explicit this + ).toString('base64'); + + const options = { + url: url, + headers: { + Authorization: `Basic ${clientCredentials}`, + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: body, + }; + + try { + const response = await fetch(url, { + method: 'POST', + headers: options.headers, + body: options.body, + }); + + const data = await response.json(); + + if (!response.ok) { + const errorPayload = data || { message: response.statusText }; + throw new Error( + `Token exchange failed with status ${response.status}: ${JSON.stringify(errorPayload)}` + ); + } + + this.setTokens(data); + + return data as TokenResponse; + } catch (error: any) { + console.error('Error during token exchange:', error); + throw new Error(`Token exchange request failed: ${error.message}`); + } + } + + /** + * Retrieves the list of envelopes for the account. + * DocuSign API: GET /envelopes + * @param query Optional query parameters + */ + async listEnvelopes(query?: Record) { + const baseUrl = this._getAccountApiBaseUrl(); + const options = { + url: `${baseUrl}/envelopes`, // Append specific endpoint + query: query || {}, + }; + return this._get(options); + } + + /** + * Retrieves the details of a specific envelope. + * DocuSign API: GET /envelopes/{envelopeId} + * @param envelopeId The ID of the envelope to retrieve. + * @param query Optional query parameters + */ + async getEnvelope(envelopeId: string, query?: Record) { + const baseUrl = this._getAccountApiBaseUrl(); + const options = { + url: `${baseUrl}/envelopes/${envelopeId}`, // Append specific endpoint + query: query || {}, + }; + return this._get(options); + } + + /** + * Creates a new envelope. + * DocuSign API: POST /envelopes + * @param definition The envelope definition + */ + async createEnvelope(definition: EnvelopeDefinition) { + const baseUrl = this._getAccountApiBaseUrl(); + const options = { + url: `${baseUrl}/envelopes`, // Append specific endpoint + body: definition, + }; + return this._post(options); + } + + /** + * Voids a sent envelope that is still in process. + * DocuSign API: PUT /envelopes/{envelopeId} + * @param envelopeId The ID of the envelope to void. + * @param reason The reason for voiding the envelope. + */ + async voidEnvelope(envelopeId: string, reason: string) { + const baseUrl = this._getAccountApiBaseUrl(); + const body: VoidEnvelopeRequest = { + status: 'voided', + voidedReason: reason, + }; + const options = { + url: `${baseUrl}/envelopes/${envelopeId}`, // Append specific endpoint + body: body, + }; + return this._put(options); + } + + /** + * Retrieves the list of templates for the account. + * DocuSign API: GET /templates + * @param query Optional query parameters + */ + async listTemplates(query?: ListTemplatesQueryParams) { + const baseUrl = this._getAccountApiBaseUrl(); + const options = { + url: `${baseUrl}/templates`, // Append specific endpoint + query: query || {}, + }; + return this._get(options); + } + + /** + * Retrieves the details of a specific template. + * DocuSign API: GET /templates/{templateId} + * @param templateId The ID of the template to retrieve. + * @param query Optional query parameters + */ + async getTemplate(templateId: string, query?: GetTemplateQueryParams) { + const baseUrl = this._getAccountApiBaseUrl(); + const options = { + url: `${baseUrl}/templates/${templateId}`, // Append specific endpoint + query: query || {}, + }; + return this._get(options); + } + + async getUserInfo() { + const options = { + url: `${this.authHost}/oauth/userinfo`, + }; + this.addJsonHeaders(options); + return super._get(options); + } +} \ No newline at end of file diff --git a/packages/v1-ready/docusign/defaultConfig.json b/packages/v1-ready/docusign/defaultConfig.json new file mode 100644 index 0000000..999cf6b --- /dev/null +++ b/packages/v1-ready/docusign/defaultConfig.json @@ -0,0 +1,13 @@ +{ + "name": "docusign", + "label": "DocuSign", + "productUrl": "https://www.docusign.com/", + "apiDocs": "https://developers.docusign.com/docs/esign-rest-api/", + "logoUrl": "https://static.wikia.nocookie.net/logopedia/images/a/ac/DocuSign_2024_S.svg/revision/latest/scale-to-width-down/250?cb=20240417143304", + "categories": [ + "eSignature", + "Document Management", + "Workflow Automation" + ], + "description": "DocuSign helps organizations connect and automate how they prepare, sign, act on, and manage agreements." +} \ No newline at end of file diff --git a/packages/v1-ready/docusign/definition.ts b/packages/v1-ready/docusign/definition.ts new file mode 100644 index 0000000..3690a36 --- /dev/null +++ b/packages/v1-ready/docusign/definition.ts @@ -0,0 +1,117 @@ +import 'dotenv/config' + +import { Api } from './api'; +import { get } from '@friggframework/core'; +import docusignDefaultConfig from './defaultConfig.json'; + +interface DefaultConfig { + name: string; + label: string; + productUrl: string; + apiDocs: string; + logoUrl: string; + categories: string[]; + description: string; +} + +const defaultConfig: DefaultConfig = docusignDefaultConfig; + +interface UserDetails { + sub: string; + name: string; + given_name: string; + family_name: string; + created: string; + email: string; + accounts: { + account_id: string; + is_default: boolean; + account_name: string; + base_uri: string; // This is the host, e.g., https://demo.docusign.net + }[]; +} + +interface TokenResponse { + access_token: string; + refresh_token: string; + [key: string]: any; +} + +export const Definition = { + API: Api, + getName: function(): string { + return defaultConfig.name; + }, + moduleName: defaultConfig.name, + modelName: 'DocuSign', + + requiredAuthMethods: { + getToken: async function (api: Api, params: any): Promise { + const code = get(params.data, 'code'); + if (!code) { + throw new Error('Authorization code not found in callback parameters.'); + } + return api.getTokenFromCode(code); + }, + + getEntityDetails: async function ( + api: Api, + callbackParams: any, + tokenResponse: TokenResponse, + userId: string + ): Promise<{ identifiers: { externalId: string; user: string; accountId?: string }, details: { name?: string; email?: string;[key: string]: any } }> { + const userDetails: UserDetails = await api.getUserInfo(); + const primaryAccount = userDetails.accounts?.find(acc => acc.is_default); + if (!primaryAccount || !primaryAccount.account_id || !primaryAccount.base_uri) { + throw new Error('Could not determine primary account ID and base URI from UserInfo.'); + } + api.setAccountId(primaryAccount.account_id); + + return { + identifiers: { + externalId: userDetails.sub, + user: userId, + accountId: primaryAccount.account_id + }, + details: { name: userDetails.name, email: userDetails.email }, + }; + }, + + apiPropertiesToPersist: { + credential: ['access_token', 'refresh_token'], + entity: ['accountId', 'base_url'], + }, + + getCredentialDetails: async function ( + api: Api, + userId: string + ): Promise<{ identifiers: { externalId: string; user: string; accountId?: string }, details: {} }> { + const userDetails: UserDetails = await api.getUserInfo(); + const primaryAccount = userDetails.accounts?.find(acc => acc.is_default); + if (!primaryAccount || !primaryAccount.account_id) { + throw new Error('Could not determine primary account ID from UserInfo.'); + } + return { + identifiers: { + externalId: userDetails.sub, + user: userId, + accountId: primaryAccount.account_id + }, + details: {}, + }; + }, + + testAuthRequest: async function (api: Api): Promise { + return api.getUserInfo(); + }, + }, + + env: { + client_id: process.env.DOCUSIGN_CLIENT_ID, + client_secret: process.env.DOCUSIGN_CLIENT_SECRET, + scope: process.env.DOCUSIGN_SCOPE, + redirect_uri: process.env.REDIRECT_URI + '/docusign', + environment: process.env.DOCUSIGN_ENVIRONMENT || 'dev', + account_id: process.env.DOCUSIGN_ACCOUNT_ID, + }, +}; \ No newline at end of file diff --git a/packages/v1-ready/docusign/frigg.d.ts b/packages/v1-ready/docusign/frigg.d.ts new file mode 100644 index 0000000..5e5a517 --- /dev/null +++ b/packages/v1-ready/docusign/frigg.d.ts @@ -0,0 +1,54 @@ +// Type definitions for @friggframework/core +// Define only the parts used by the docusign module + +declare module '@friggframework/core' { + // Define the structure of the parameters expected by the OAuth2Requester constructor + // Add properties as needed based on the actual usage in Frigg core + interface RequesterParams { + client_id: string; + client_secret: string; + redirect_uri: string; + scope: string; + state?: string; + access_token?: string; + refresh_token?: string; + [key: string]: any; // Allow other properties + } + + // Define the base class structure + export class OAuth2Requester { + protected access_token?: string; + protected refresh_token?: string; + protected client_id: string; + protected client_secret: string; + protected scope: string; + protected redirect_uri: string; + protected state?: string; + protected baseUrl?: string; + public authorizationUri?: string; // Make public if accessed directly + public tokenUri?: string; // Make public if accessed directly + + constructor(params: RequesterParams); + + // Define methods used by the docusign Api class + // Use 'any' for complex types initially, refine if necessary + protected _get(options: any): Promise; + protected _post(options: any, stringify?: boolean): Promise; + protected _put(options: any, stringify?: boolean): Promise; + protected _patch(options: any, stringify?: boolean): Promise; + protected _delete(options: any): Promise; + + protected addJsonHeaders(options: any): void; + + // Add other methods if used (e.g., getAuthUri, getToken, refreshAccessToken) + public getAuthUri(): string; + public getToken(callbackParams: any, code: string): Promise; + public refreshAccessToken(params?: any): Promise; + public setTokens(params: any): void; + } + + // Define the utility 'get' function + export function get(obj: Record | undefined | null, path: string | string[], defaultValue?: any): any; + + // Add other exports from @friggframework/core if they are used +} \ No newline at end of file diff --git a/packages/v1-ready/docusign/index.ts b/packages/v1-ready/docusign/index.ts new file mode 100644 index 0000000..df3221e --- /dev/null +++ b/packages/v1-ready/docusign/index.ts @@ -0,0 +1,9 @@ +import { Api } from './api'; +import { Definition } from './definition'; +import Config from './defaultConfig.json'; + +export { + Api, + Definition, + Config, +}; \ No newline at end of file diff --git a/packages/v1-ready/docusign/jest-setup.js b/packages/v1-ready/docusign/jest-setup.js new file mode 100644 index 0000000..b5a6c96 --- /dev/null +++ b/packages/v1-ready/docusign/jest-setup.js @@ -0,0 +1,3 @@ +const {globalSetup} = require('@friggframework/test'); +require('dotenv').config(); +module.exports = globalSetup; \ No newline at end of file diff --git a/packages/v1-ready/docusign/jest-teardown.js b/packages/v1-ready/docusign/jest-teardown.js new file mode 100644 index 0000000..d3fbf1e --- /dev/null +++ b/packages/v1-ready/docusign/jest-teardown.js @@ -0,0 +1,2 @@ +const {globalTeardown} = require('@friggframework/test'); +module.exports = globalTeardown; \ No newline at end of file diff --git a/packages/v1-ready/docusign/jest.config.js b/packages/v1-ready/docusign/jest.config.js new file mode 100644 index 0000000..684c569 --- /dev/null +++ b/packages/v1-ready/docusign/jest.config.js @@ -0,0 +1,20 @@ +/* + * For a detailed explanation regarding each configuration property, visit: + * https://jestjs.io/docs/configuration + */ +module.exports = { + // preset: '@friggframework/test-environment', + coverageThreshold: { + global: { + statements: 13, + branches: 0, + functions: 1, + lines: 13, + }, + }, + // A path to a module which exports an async function that is triggered once before all test suites + globalSetup: './jest-setup.js', + + // A path to a module which exports an async function that is triggered once after all test suites + globalTeardown: './jest-teardown.js', +}; \ No newline at end of file diff --git a/packages/v1-ready/docusign/package.json b/packages/v1-ready/docusign/package.json new file mode 100644 index 0000000..479e3ad --- /dev/null +++ b/packages/v1-ready/docusign/package.json @@ -0,0 +1,49 @@ +{ + "name": "@friggframework/api-module-docusign", + "version": "1.0.0", + "description": "DocuSign API Module for the Frigg Framework", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist", + "definition.ts", + "README.md", + "LICENSE" + ], + "scripts": { + "build": "tsc -p tsconfig.build.json", + "test": "jest", + "test:watch": "jest --watch", + "lint": "eslint . --ext .ts", + "format": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\"", + "prepare": "npm run build", + "prepublishOnly": "npm test && npm run lint" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/friggframework/frigg.git" + }, + "author": "Frigg Framework ", + "license": "MIT", + "bugs": { + "url": "https://github.com/friggframework/frigg/issues" + }, + "homepage": "https://github.com/friggframework/frigg/tree/main/packages/v1-ready/docusign#readme", + "dependencies": { + "@friggframework/core": "^2.0.0" + }, + "devDependencies": { + "@types/node": "^18.0.0", + "@typescript-eslint/eslint-plugin": "^5.0.0", + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^8.0.0", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-prettier": "^5.0.0", + "prettier": "^3.0.0", + "typescript": "^5.0.0", + "jest": "^29.0.0" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/v1-ready/docusign/tests/api.test.js b/packages/v1-ready/docusign/tests/api.test.js new file mode 100644 index 0000000..31715bc --- /dev/null +++ b/packages/v1-ready/docusign/tests/api.test.js @@ -0,0 +1,159 @@ +const { Authenticator } = require('@friggframework/test'); +const { Api } = require('../dist/api'); +const { Definition } = require('../dist/definition'); +const { documentInBase64 } = require('./fixtures/document-in-base64.json') + +describe('DocuSign API tests', () => { + const apiParams = { + client_id: process.env.DOCUSIGN_CLIENT_ID, + client_secret: process.env.DOCUSIGN_CLIENT_SECRET, + redirect_uri: process.env.REDIRECT_URI, + scope: process.env.DOCUSIGN_SCOPE, + environment: process.env.DOCUSIGN_ENVIRONMENT, + }; + + const api = new Api(apiParams); + + beforeAll(async () => { + const url = api.getAuthorizationUri(); + const response = await Authenticator.oauth2(url); + const baseArr = response.base.split('/'); + response.entityType = baseArr[baseArr.length - 1]; + delete response.base; + + console.log('Received callback data:', response.data); + expect(response.data.code).toBeDefined(); + + const tokenData = await api.getTokenFromCode(response.data.code); + expect(tokenData).toBeDefined(); + expect(tokenData.access_token).toBeDefined(); + + // Fetch user info to get accountId and baseUri + const userInfo = await api.getUserInfo(); + expect(userInfo).toBeDefined(); + const primaryAccount = userInfo.accounts?.find(acc => acc.is_default); + expect(primaryAccount).toBeDefined(); + expect(primaryAccount.account_id).toBeDefined(); + expect(primaryAccount.base_uri).toBeDefined(); + + api.setAccountId(primaryAccount.account_id); + }, 120000); + + describe('User Info', () => { + it('should return user details', async () => { + const response = await api.getUserInfo(); + expect(response).toBeDefined(); + expect(response.sub).toBeDefined(); + expect(response.name).toBeDefined(); + expect(response.email).toBeDefined(); + expect(response.accounts).toBeDefined(); + expect(Array.isArray(response.accounts)).toBe(true); + expect(response.accounts.length).toBeGreaterThan(0); + }); + }); + + describe('Envelopes', () => { + let testEnvelopeId = null; + + const sampleEnvelopeDefinition = { + emailSubject: `Frigg Test Envelope ${Date.now()}`, + status: 'created', + documents: [{ + documentId: '1', + name: 'test-doc.txt', + fileExtension: 'txt', + documentBase64: documentInBase64 + }], + recipients: { + signers: [{ + email: 'projectteam@lefthook.com', + name: 'Test Recipient', + recipientId: '1', + routingOrder: '1', + }] + } + }; + + it('should create an envelope', async () => { + const response = await api.createEnvelope(sampleEnvelopeDefinition); + expect(response).toBeDefined(); + expect(response.envelopeId).toBeDefined(); + expect(response.status).toBeDefined(); + testEnvelopeId = response.envelopeId; + console.log(`Created test envelope ID: ${testEnvelopeId}`); + }, 30000); + + it('should get envelope details', async () => { + expect(testEnvelopeId).toBeDefined(); + const response = await api.getEnvelope(testEnvelopeId); + expect(response).toBeDefined(); + expect(response.envelopeId).toEqual(testEnvelopeId); + expect(response.status).toBeDefined(); + expect(response.emailSubject).toEqual(sampleEnvelopeDefinition.emailSubject); + }); + + it('should list envelopes', async () => { + // Calculate date 30 days ago + const fromDate = new Date(); + fromDate.setDate(fromDate.getDate() - 30); + const fromDateISO = fromDate.toISOString(); + + const response = await api.listEnvelopes({ from_date: fromDateISO }); + expect(response).toBeDefined(); + expect(response.envelopes.length).toBeGreaterThan(0); + }); + + it('should void an envelope', async () => { + expect(testEnvelopeId).toBeDefined(); + + const reason = 'Frigg API Test Void'; + const response = await api.voidEnvelope(testEnvelopeId, reason); + expect(response).toBeDefined(); + expect(response.envelopeId).toEqual(testEnvelopeId); + }); + + + // Cleanup: Attempt to void the envelope if created (best effort) + // This runs even if void test is skipped. Only runs if create test passed. + afterAll(async () => { + if (testEnvelopeId) { + console.log(`Attempting cleanup: Voiding envelope ${testEnvelopeId}`); + try { + await api.voidEnvelope(testEnvelopeId, 'Frigg API Test Cleanup'); + console.log(`Voided envelope ${testEnvelopeId}`); + } catch (error) { + console.warn(`Could not void envelope ${testEnvelopeId} during cleanup (may have been only 'created'):`, error.message || error); + } + } + }, 30000); + }); + + describe('Templates', () => { + let testTemplateId = null; + + it('should list templates', async () => { + const response = await api.listTemplates(); + expect(response).toBeDefined(); + // Check if envelopeTemplates is an array, even if empty + expect(Array.isArray(response.envelopeTemplates)).toBe(true); + + // If templates exist, store one for the get test + if (response.envelopeTemplates.length > 0) { + testTemplateId = response.envelopeTemplates[0].templateId; + console.log(`Using template ID for get test: ${testTemplateId}`); + } else { + console.log('No templates found in account, skipping getTemplate test.'); + } + }); + + it('should get template details if a template exists', async () => { + if (!testTemplateId) { + throw new Error('Skipping get template details test as no template ID was found.'); + } + const response = await api.getTemplate(testTemplateId, { include: 'tabs' }); + expect(response).toBeDefined(); + expect(response.templateId).toEqual(testTemplateId); + expect(response.name).toBeDefined(); + }); + }); +}); diff --git a/packages/v1-ready/docusign/tests/auther.test.js b/packages/v1-ready/docusign/tests/auther.test.js new file mode 100644 index 0000000..70586c7 --- /dev/null +++ b/packages/v1-ready/docusign/tests/auther.test.js @@ -0,0 +1,128 @@ +const {connectToDatabase, disconnectFromDatabase, createObjectId, Auther} = require('@friggframework/core'); +const {testAutherDefinition} = require('@friggframework/devtools'); +const {Authenticator} = require('@friggframework/test'); +const { Definition } = require('../dist/definition'); + +const mocks = { + getUserDetails: { + sub: 'user-sub-12345', + name: 'Test User', + given_name: 'Test', + family_name: 'User', + created: '2024-01-01T00:00:00.000Z', + email: 'test.user@example.com', + accounts: [ + { + account_id: 'account-id-default-67890', + is_default: true, + account_name: 'Test Default Account', + base_uri: 'https://demo.docusign.net', + }, + { + account_id: 'account-id-other-11221', + is_default: false, + account_name: 'Test Other Account', + base_uri: 'https://demo.docusign.net', + }, + ], + }, + tokenResponse: { + access_token: 'mock-docusign-access-token', + refresh_token: 'mock-docusign-refresh-token', + token_type: 'Bearer', + expires_in: 28800, // 8 hours typical for DocuSign + }, + authorizeResponse: { + base: '/redirect/docusign', + data: { + code: 'mock-docusign-auth-code', + state: 'mock-state', + }, + } +}; + + +testAutherDefinition(Definition, mocks); + + +describe('DocuSign Module Live Tests', () => { + let module, authUrl; + beforeAll(async () => { + await connectToDatabase(); + module = await Auther.getInstance({ + definition: Definition, + userId: createObjectId(), + }); + }); + + afterAll(async () => { + if (module && module.CredentialModel) await module.CredentialModel.deleteMany(); + if (module && module.EntityModel) await module.EntityModel.deleteMany(); + await disconnectFromDatabase(); + }); + + describe('getAuthorizationRequirements() test', () => { + it('should return auth requirements', async () => { + const requirements = module.getAuthorizationRequirements(); + expect(requirements).toBeDefined(); + expect(requirements.type).toEqual('oauth2'); + expect(requirements.url).toBeDefined(); + authUrl = requirements.url; + console.log('Follow this URL to authorize:', authUrl); + }); + }); + + describe('Authorization requests', () => { + let firstRes; + it('processAuthorizationCallback()', async () => { + const response = await Authenticator.oauth2(authUrl); + + firstRes = await module.processAuthorizationCallback({ + data: { code: response.data.code }, + }); + expect(firstRes).toBeDefined(); + expect(firstRes.entity_id).toBeDefined(); + expect(firstRes.credential_id).toBeDefined(); + }, 60000); // Increased timeout for manual step + + it('retrieves existing entity on subsequent calls', async () => { + const response = await Authenticator.oauth2(authUrl); + const res = await module.processAuthorizationCallback({ + data: { code: response.data.code }, + }); + expect(res).toEqual(firstRes); + }, 30000); + }); + + describe('Test credential retrieval and module instantiation', () => { + it('retrieve by entity id', async () => { + expect(module.entity).toBeDefined(); + expect(module.entity.id).toBeDefined(); + const newModule = await Auther.getInstance({ + definition: Definition, + userId: module.userId, + entityId: module.entity.id, + }); + expect(newModule).toBeDefined(); + expect(newModule.entity).toBeDefined(); + expect(newModule.credential).toBeDefined(); + // Use the testAuth method which triggers Definition.requiredAuthMethods.testAuthRequest + const testResult = await newModule.testAuth(); + expect(testResult).toBe(true); + }); + + it('retrieve by credential id', async () => { + expect(module.credential).toBeDefined(); + expect(module.credential.id).toBeDefined(); + const newModule = await Auther.getInstance({ + userId: module.userId, + credentialId: module.credential.id, + definition: Definition, + }); + expect(newModule).toBeDefined(); + expect(newModule.credential).toBeDefined(); + const testResult = await newModule.testAuth(); + expect(testResult).toBe(true); + }); + }); +}); \ No newline at end of file diff --git a/packages/v1-ready/docusign/tests/fixtures/document-in-base64.json b/packages/v1-ready/docusign/tests/fixtures/document-in-base64.json new file mode 100644 index 0000000..db54769 --- /dev/null +++ b/packages/v1-ready/docusign/tests/fixtures/document-in-base64.json @@ -0,0 +1,3 @@ +{ + "documentInBase64": "JVBERi0xLjMKJcTl8uXrp/Og0MTGCjQgMCBvYmoKPDwgL0xlbmd0aCA1IDAgUiAvRmlsdGVyIC9GbGF0ZURlY29kZSA+PgpzdHJlYW0KeAGdWsuS20YSvOMr+siJ8IzZDT6PXtmK8MbaIYfHu4cNHzAkNGoHCFIEMJ+737KZ1VUNUKRkjfYR4oCNRnVWVtYD/Oh+cx/dHP9d+eDW2+DOtfuPa933bzrvdp3zrtt9+v17N39YzeU/Lk6/LHAzvyznfruab9KtEQ/wsuqe//AZu4P7x6MrV+kq/l0H5+cr93hw37/1D3Pni8f3bvZ7dTg1tXv349s79/iX++kRxs656Wc29Bt9zEY2LEPaMDzMC++44eOH2Dn8r3JdtK3d/5r6wb0dWvc+/f9hfNiN5xQwHEgRsXsiBsNXZXpOScP5nP+62b+O5zvn125WH+7c/da7mcO/5dLN4qkbeG29LuTaYutm+2Mj60Na5ktc62LPOzbp0gZXqkPdf3cHR+mqDb7bHduu3vV1P9Tnu+Ie+/M5PmCnah9PsdvF9lmfzG9oSd3EHoeEN3Qx7nr3oerqphk6XA8lvyju/Rwf3le72MQu8osS5+EmNOa4j0fuYSb7BUyu91yFT/Io3H2ID9jpT/f4z+S9G4CSCZ8Aut5cA/pmOFdPsR94SvhZnrDACboBRzzJgYIeaAObfh2aphLokzU06qVuYN4q3buEdS3O9YD95Lzufonj/dRHuW2drvF4XX04AVyn2K4mPlwoIB6rhx47LXQrj6c08MvQAeigd25w8Z3S4hhbeFLP4QM+VE38OFQHOJgsIf5brK/PFWkgPq+f6x7AvhbNbVlIXE3pefpQGT/7c4UHGEM9bdodD4fj/kiu6QHLILbcFaT0kVywkwYe9RBBdkU/rPEBtNzHQ932QnU/B2FICb+cu1k6JSJSLgUeTYhDELgoJF6LCfhTLQhLuOKvoeuPsmalG2HBJqRA8mtYAi/ANnyQVfQLvA524M9X4hbmm2vcfm57eAHQ3UO05BnlGswigms9Y1nCNLoNnrdQWCyWxezNuSJwesrFHOdpqiwTNQ2nvTS83OLUTXweGtlZ+VOSGLvhDMozCMw1W9xUt/FApmFP3r+A9M1+qNu6AsuWGo+LFT50u7qpz7H7ONRYJ1uAtQx0wQnX9GGLOT6IUbYn9HzWi0Z8Gc0iqftlTAeIyhUL/52Mq7s+Pg2NUIXRxRN4Pn4fEUEHRgIg5dUN/pU4EcPWigASCoMU5+cJuS4QeJEA/qEXPZ1gcobFJJQsJmp/Z0q12w2HjnAmxaMIAbQKfKC1ajSD5yX2Fa5ZOPgNvlTFtbOt8UGsw+P/LF6ljQEJ4QpHFQm/SYEQqI+aUhjXlBQDZg0En5oK0Qm+qdmBuh5bLKOICyRLX8zOQ38WnyzNbh7l4xA7rlSmBqYvU0ijZCDlTkPzElvITGE2GN5ESQlrDkPBMNFsb4rpPcx9qc4x5SU73xz2QmGeyeGF7YqkMQOxobhE9XUZJyxvaCQowWQRX6qDPJ7aJeCscLU7Nk3cIR/tBThLISURRJpmhCo7S/JPZMm4HZY4bARzVIL3SSR5VtkfhRAYU8f3dbsH1Es9dknaMGXh0Lq0ZMpKMYmL6faSafCEOIX61inOTavL1bwQLtKBZh6p0QO4Humn/gboVjdkcoyWLMUSBpb8JIHoaSmXTAkMI1+qlDMkJcZTLsRXW/AsuRdstlvh8t+H7gScYteBDKV9Q4o18Yk1kWQsbJ5ZTKFm9QIQTC8DcTxUfS9lTr4q2V/KsAX2k5Jog4Vah+XMFHiN2ftCwwP9wJT93RdBva2W0JarKP+AY1rSPotVpuya5r67K3hucsgzPbWsgSB0wDVdXeLi71Kj0WSuYwWlgmX09lSPakBwoTTSZcysP6T6hNvNE/2YZFR6UsmI/UDRl2Mz9CfJf6wa+Jjtopjcz/oMVxkq7+vhOUqFw824lA5/GZrT0FdJWie2g/zNN9STYXtDM6e8EY7w4UscnQLHjymcaKTgiEs5keBUJ/NEHyV6E0EcQjWlBEItJ7+xluHP8+OfA2WENaIlI+ayVCMWM6kRWV5wI+8JDBMJ/tCAxtpUJFAlFD9WA131HJXLqUItYEXF3iFokcI0AG52vZQkpWJMx3+ZrejcrhugEvy7ZGsWfyfpwonjPt9W3YyBEoe83BVt1buJrhGVsMKxpVPSI+ACXAAFHlG6l3yYq3FWjry1ZJi/O1d1B6HE33kDfH+duzSuAmWoHdodtEahlNSlpLHWLDBCYrUb4Fy4hg8sYCu8YK6BL3LvJDlUSltcVDeGJbanLMLUzyez28CBxVfANcen4znJm1fDpUnscZU16BYJn/xQspFgqZsT42GrxOVFpaY4sibbD0hKuXpFO1NoEhwPKT3OC0rStkpmWGcrXSY2RhBYf+mZy38VlK22p0Fa+oL+aibiWgNAVM6MZyNhxZ91aAyLXPu9viUt0Q9eYTr2zKhQFTjYIJ3n7niWptHqJOrxRa/ujD64g5KBE1CMSRQWRT8mFQoKMhb1sd3F/dAyjE1dmMrQEQNlHLllr6AqTn2P+7sCl4Xr2OayJSOP8Q2FvRnYp2JXy57cvY1PH6bUK76qcy/Rul3BlATRfBs85h4TgTd524BoXXWKNetQy9WBrMM5nLX2gV2TxRCjBbTlAT3D5VB1HTHAPCZdlNSvQr0DQ3MVTC2Vkkzx8RjjaElaqDqjh8C1qd5AsdUbnhXtyzQY2LLSDso3gH6pKOuvpxkbsSv8pLUpLGN7VlTGbpNzDmSEXVAM0TKYIbCwUtbiapU8DoYUWo4aSlisAm24+9yhsCzbqI8IXwpUSzZ+DfzHyUzuHzyplAlLl0yetYeXnwagA7dRi9LoC4ayhSusI/B86mRaguWf10EdKF62nAv0Q1dgWgbNIsgmElMlTMoYAWpnoP0XrbJXogUimkZznCDJIShHWhNaJg0SgJYALCrv0YIC63qHSs1anMA0dP5wbHfSZNhoy9OuCgtdlgAOWmR4SKlQvgX2tKMOaXOKuODEZQIssbYajVy36c9umOT7gqPgm5PaT4AFOlfAjpVcKvmR75gh6wFHyL0G5wzP5+ol7hmnlgECRVoWGigla6OR0JxQJKxxXimaGHs1dk6SyWSOLQycXJinNKKDwEQsmEUtAQ0IiW5KqmFHXGEBSJ+K/0QhbKwT2JXl2S0glJQulbfWu9/AULjRgNQJtcL44NL0xk0bOfjdpuBf56f1jQD4ub1zpnosaA/VgLaKlFJ98ATqUvdSGY+RB+GWXG/84+xbelIS1eKBfs/Fz73lKiq0OFmWYTOG4Eh8IwN5Ly218V5amYvCQbnAjMEIKay98SSB5tIcnOyI/qBrdaIrxYMOr2kSvU0Tpf5CMLx2WL1AZWE+zNP/iXSQJiw+wDhgZCMAT6JZPYIS0SJEBh7tkTmQTBd5ITI6kcUsRS8yD1ZYNf37VO9ZRuQsxWjToaPtLyOEGtWrRxaQ3bnIEqfJH/HQ1w9AVBAi1qiq+Zk5zspGHbUiQHGLNX5Y9EqpXmK2aCCmQECtn+Y+OFBOfTzFj0foJ9ARFiGEqdQN3lG0kXCAumIi/m1rzjgLdva85CkyUiHBUl6QG6X0IQNUCoi1tvLYzYSEDSvBvStGp1CHtN6w5I898FYpT3ViGlKKVOD5fDzqPGybw4mKLvnaZIxhlcddajgfPlHyL4J7sx9Y3hq9ynFczjZESep9GJDQWhVajpFRCplnU/wWL1wwahmbYThApwA2kGWLxQISZ01tPafVuDYOSfgXn8P3C2nyqA7wlCEbfmGBTRA8g1ymKXCpmbOFORYZCWGxncqRluZ4C6S5DuVtAu/Dtkin+SKmN2uLJYhihJWoL0BYSVj2Lg4dlZtRVg1RvhbRaLRZB8faMtMiAxWRNToeiy4bnNp5qVcnNGx9lJ7N6CmjAakq0Bqp/+jZPNW7K0h2wUZCu+ZYMU/WKEW5ZoUlmRRzbGajXTXFZpFpqq2l9ZHpw2xkSSoU+QpQi8u6YgnRvgCVL1InrYIhyUdYJmZPzYMtMWGS2l9Vk5VsTm25IZCacvKm07pD5oZxFGNDSI5epsWtHZGtTGqOc+ZhuTVpxICHRq/0wuPozNjAGkx0bHqm1P6g+zEnilof2xHJr63Qlsh6V0hqS4faEPuPE478Jktf/WnzgiNYBg4Mwk+n4TbYlc5rzGSFsYcvfNIYERslH0ntnCYlBhy6wdSgkEGKRSDuqPzT5Fbek+r9vCsPAS6FfRy/ahz5LRnASfw3TAyXt+avNyeGkrA1//AM5jpaZ12aZ7KGE8GRasyg8iaN4aadBQfkFTIbRt3puCRl6kxkVbrIactEHg1H3jwpOtBH5QKIjvqlqncYvMjYXYnJTMaAyfNFXE9J4cIAGQ6/vipa3hq58p2PU7126f2H+BjvPx6c5HXHwuzBYUbtpi9YHGpOlyyRMg1Af77AuJkDV5gJXQXEL8fzE1+mqGRIQaoDVnFYcU/N5rQPcqnBT/xHtiOBGd1wBwsQ1VnOVZKgAGJqEzWKjp3UbHx/iDXSyowVIIVWRrULoxJDJE8vFyZOzMlTRmbpZxgx+/GBukUajrDOx0WrnJj96gHBqXQTlqZ3SaPgyK+DvqIlXMGuK3zHw9l4LIwRDB8aRSXgdeCV8ES9wFFqemum6qEjnmcZc+n4Ck2twIVjWe3q2RbYMHWFKkaQZzrYx2eMOfDDH6y2bCtlCZKf/VhhKkMcqrxJr+bS7xfYs8CY7riLguT4SOCMsebxm16grZBTr6DDKy2MSTElga32uwjCJW41XSZEh4qHyifCb1lwEW/DiIh6n0nuVJ0hJRiuif4k5vH1IBibKyweEhqxg2ZypLjU3yex0DtHvIrHG3puaxNN8gwvRulHlRTPLv0qTwi9YBeQyxW9WsYfxKTeDEaLn7ABGo8v8u92fEMsr0C0hkXs2qff9Vhu5aM7/ekQOjTTAP7kJpUNOCk+i1EEkjNynbJq1YMa2ISDEiu1bcUJltHaszyFlAFiA4gzuAxCho1jbKW/3SuvGTUF7gZgbKHsyWT7kZLtQH+OxR7sNMPxOGlSXi/gq1uTXJEVTFD0OHxzC2WGdVZHeZYWk3FUFhuSBmEmLxXzK/stFOjTH6mkOl9mMdorU87waxoyVaiEeSTgng4Xk0/lZ1xa9tsYjp3ZZHSg/uI7I3kvBVU3qFi2WCvBOE8IimP/5gXuzf5gjX7D+KiTHY5hAZVNcH77P6d6b9EKZW5kc3RyZWFtCmVuZG9iago1IDAgb2JqCjM4NjYKZW5kb2JqCjIgMCBvYmoKPDwgL1R5cGUgL1BhZ2UgL1BhcmVudCAzIDAgUiAvUmVzb3VyY2VzIDYgMCBSIC9Db250ZW50cyA0IDAgUiAvTWVkaWFCb3ggWzAgMCA2MTIgNzkyXQo+PgplbmRvYmoKNiAwIG9iago8PCAvUHJvY1NldCBbIC9QREYgL1RleHQgXSAvQ29sb3JTcGFjZSA8PCAvQ3MxIDcgMCBSID4+IC9Gb250IDw8IC9GMS4wIDggMCBSCi9GMy4wIDEwIDAgUiAvRjIuMCA5IDAgUiA+PiA+PgplbmRvYmoKMTEgMCBvYmoKPDwgL0xlbmd0aCAxMiAwIFIgL04gMSAvQWx0ZXJuYXRlIC9EZXZpY2VHcmF5IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlID4+CnN0cmVhbQp4AYVST0gUURz+zTYShIhBhXiIdwoJlSmsrKDadnVZlW1bldKiGGffuqOzM9Ob2TXFkwRdojx1D6JjdOzQoZuXosCsS9cgqSAIPHXo+83s6iiEb3k73/v9/X7fe0RtnabvOylBVHNDlSulp25OTYuDHylFHdROWKYV+OlicYyx67mSv7vX1mfS2LLex7V2+/Y9tZVlYCHqLba3EPohkWYAH5mfKGWAs8Adlq/YPgE8WA6sGvAjogMPmrkw09GcdKWyLZFT5qIoKq9iO0mu+/m5xr6LtYmD/lyPZtaOvbPqqtFM1LT3RKG8D65EGc9fVPZsNRSnDeOcSEMaKfKu1d8rTMcRkSsQSgZSNWS5n2pOnXXgdRi7XbqT4/j2EKU+yWCoibXpspkdhX0AdirL7BDwBejxsmIP54F7Yf9bUcOTwCdhP2SHedatH/YXrlPge4Q9NeDOFK7F8dqKH14tAUP3VCNojHNNxNPXOXOkiO8x1BmY90Y5pgsxd5aqEzeAO2EfWapmCrFd+67qJe57AnfT4zvRmzkLXKAcSXKxFdkU0DwJWBR9i7BJDjw+zh5V4HeomMAcuYnczSj3HtURG2ejUoFWeo1Xxk/jufHF+GVsGM+Afqx213t8/+njFXXXtj48+Y163DmuvZ0bVWFWcWUL3f/HMoSP2Sc5psHToVlYa9h25A+azEywDCjEfwU+l/qSE1Xc1e7tuEUSzFA+LGwluktUbinU6j2DSqwcK9gAdnCSxCxaHLhTa7o5eHfYInpt+U1XsuuG/vr2evva8h5tyqgpKBPNs0RmlLFbo+TdeNv9ZpERnzg6vue9ilrJ/klFED+FOVoq8hRV9FZQ1sRvZw5+G7Z+XD+l5/VB/TwJPa2f0a/ooxG+DHRJz8JzUR+jSfCwaSHiEqCKgzPUTlRjjQPiKfHytFtkkf0PQBn9ZgplbmRzdHJlYW0KZW5kb2JqCjEyIDAgb2JqCjcwNAplbmRvYmoKNyAwIG9iagpbIC9JQ0NCYXNlZCAxMSAwIFIgXQplbmRvYmoKMyAwIG9iago8PCAvVHlwZSAvUGFnZXMgL01lZGlhQm94IFswIDAgNjEyIDc5Ml0gL0NvdW50IDEgL0tpZHMgWyAyIDAgUiBdID4+CmVuZG9iagoxMyAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMyAwIFIgPj4KZW5kb2JqCjE0IDAgb2JqCjw8IC9MZW5ndGggMTUgMCBSIC9MZW5ndGgxIDM0MzIgL0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4Kc3RyZWFtCngBrVZtaFvXGT7nXFmSpUiW5CsplmxZ17JuZEnWpyXZqT9kx7LlOHP8UTu6hiR2/J3Zq5e6IYFu9cI2Mv3o8qulsB8lHYVC6bRBg2r2I4x90LIfhnU/tmQshRXGGKM/kg3GbO05V7JZhymFTeLRed/3nPPe9zznfd8rQgkhp8guEUh2aWtxm7xInsHya6C8dHPHS0R8Cf02dM3q9tpWyDL6C+h3CWHvr23eXo2+bqlgKkuI7un6yuLyXzt9F+GwC+vT6zBof8X+AX0Tevv61s4t+6v0Q+j3oIc3X1ha1McMq9B/Cv301uKtbeYXRqB/DN37tcWtlX/95GMr9KfQ49svvLhD3qI3CDEFoHdv31jZ3iaGOegL0PUAxZd/ThEteRejl0zVLKr5f/ph2C2c4EGj2upOmDkyaWuC7sigjnpSr44GznEZfIyXSf1k4ceUvqqUaeU7ZTLc8gHWCFevdJYJDXu9uY3hEl2AwsIwBCVIQtg7UhL8I9MFn+Iteotjy0XviHd9cbmk8asjJlaKStRbIjOFDfw+X5BKWcV9LK4oyln40XA/2ILlRQUertc8YFRN0QMsqguPe0uCPFmYKpR2h92l7LDiliRvrvRwslB6OOyWFAWrtMeRIuJvbJyuxaxDzNog5vVVLzPwARdKsch9QmOyVHpYLLqLOIlq8UllSmoGnJSvEfy5Ms1OFvhU1ie5ucEn+STEoQzDd314fKaQQyQSj8TwxZQajwPF2lMIz6hSavo/UWr+MpQ2fClKLceRfo5SK2K2cEptJ1Pq+wJCjxnOnsDwbpXh3RMYbvwPhnn6MpJA2f0MxSEQHTESZEkU6RSNxf2SVfJbJSvdO9ylu4e36D09/Ux/iB3VfWlsXmAfoVabyFaZuKN7KFUTEULIbQt0QHxcJrrHg3Xk9/D9F4BdHnSjJHTYoiMdQA8wBijABnAb+B7wBvAO8AHwIWC6jMjMiIwisow1mfAwu2hmPgFiP0t1RZjv/CfptelEfPbrA08eDd660p25tJUS2X3PV+4sz70y08EeHKQDs3cXNopTEg5ACeKnf0P89aSVR645jly7X2OB4nk6PK/RKqUkaxK/5+kPD/MifZl+k3aLB2l6Hc2Vc8i5iKlcOMlLZdIELswgk3NhBg9kHzaMOvBhq/JhAx+2Iz5s4MMGPmzgwwY+bODDBj5s4MMGPmzgwwY+bODDBj5sVT4I4jviBNGpRMg+yYp4Ew6VnvNUSMz0tt1YO/yjSE3Li55UPkhzTfGx6OI1hK/Qy/OzwWynk/MhVT5jKfaI9JGZPdKK1sujb7VwbpActVttxSmMOI2RXwVkO2R7TQ7idHy+CyOfG3gMHv2Y7AGHNBkRfG1m3JqHOT2CGl1bhKV8Wg87usIzESHV1Q/Vw1ijKATOKanQpZFwMDcX6lDGoh1DM8GeqzlZpB3ja32R+Xz4zJCSiBRyoeDgpD/2fJ+fsd6ZpNMWHE11DZ5xGAyN/oF0ZCjY2NStZIcX+prt0Xyyeyhw2mQU28/GYgNygxgd5/fnqDyjnzIziZMR8voeKGhUT9z3udPvkQBv52AigNPVA8371TGxj7TWYpsDkIE0MALMAavATeC7wGvA28AD4JeA6TKK47cQPgXY5arnPOfNAd40SKNm+NRYbT3IwmQ/Pcp7nRkMOjhvmSSE52jKTCM0Za1yXHfMble/NpMW6+ankr2temf8QsqbTbRemQgMp3x6Jjk8Bqd4ijZH+tpio3EXLbhTE4lgT3uDK9zTO5AOGuhS/qI7mHC2tDbUCcwknY3lcvb2SNO5FrtIaZ25gY4Gu31mV2RAuSrnM212KeToSLYYNUzfYOa8NlWe0j+AVzdYGdhDHvGa4BnVfJxRjeCwEYdtBp8iZBEygdzCq7CNK4CRV72URpXXUiiTSGd8ZupDCvmlfoHz0knfY02ZS72h6X7ZlZ7tLdwyiqavTmUuxJz0jcNVoS0zFkxcSLoOUtcuRNpzy9mepbEAXZqfnZWHlHx6rs/rz84itAzCHEctC8S5p/5h4DcuWBAN7wdE7QdJa0ZkHx2ksQwfdQ/7DbOQGKns4adaLzGcguFEfDTxqt9He3wCoAvWkTchlKpKIzaYiAXwAjEgC0wCC8A2sAvUIz0sKC0Xd/UmcWG3q+pqH8KTqsJdueDKBVcuuHLBlQuuXHDlgisXXLlUVy1wZUFkfkToR6LJeIQFiVaNNwJrBMdtwYoEz0d+dt5r3IAesKtbKNFjyx6JggMu8V6ZtNZq2P5fIr89rc4XoTpcos6XyojOUL8cSnuMoiPY6w9mJJNokfujvYUGZprq9aTCHh1j7OCQ+kITz/maY9n2wwd0pH0w0XI63C8fvkeng7l481C6Z5DZ5YTnFZwdOZepTNK/I+dOkyB5p0zCCNRBnGq+OXAa3ov5yG+G4SBhyAZwwW1H+cdlXtsGjHK1X8vo1/JRv5bRr2X0axn9Wka/ltGvZfRrGf1aRr+W0a9l9GsZ/VpGvwa3/P3FM1lCOPwNKqm82WtSLO60Jj0o79q7rEqQz5ppk486Iq/pcWa7Pp2azrR4eqaSZ851yQYm/unKjebURDwxHm9yxsZLNDsyGzp/LX32Wj7gDPa03TuYKH/rdmJ+OBDIKYlOZbST56sK4XeDr11t6H1GrcKfYSE///526/HoqEwiox9Br1fj5RPYJ7xVCRG3ZvXwR5V5zSXVE585+hjpU5KgDlzDXZKmn5A06weWiATZgbkmcodkBAPJsCa1zprRCF4m96mG3mERdpfdF9aFf6pejWQTz20Dqv838KKF+D7mqtHbas/W4lVFJoZm83NzofzK5s2VnY2lxYmVl1Y6Zzd3bixe2Fhb3+HRVb2Qyg/4/50TPkbYBHIGl5pE9x4meXIRZTODHED1k9C/AesArfoKZW5kc3RyZWFtCmVuZG9iagoxNSAwIG9iagoyMTc2CmVuZG9iagoxNiAwIG9iago8PCAvVHlwZSAvRm9udERlc2NyaXB0b3IgL0FzY2VudCA5MzEgL0NhcEhlaWdodCA3MjMgL0Rlc2NlbnQgLTIxMyAvRmxhZ3MgMzIKL0ZvbnRCQm94IFstMzM3IC0yMTkgMTExMSA5MzFdIC9Gb250TmFtZSAvTkJVSFZWK0hlbHZldGljYU5ldWUtVWx0cmFMaWdodAovSXRhbGljQW5nbGUgMCAvU3RlbVYgMjAgL0xlYWRpbmcgMjcgL01heFdpZHRoIDExMjYgL1N0ZW1IIDIwIC9YSGVpZ2h0IDUyMAovRm9udEZpbGUyIDE0IDAgUiA+PgplbmRvYmoKMTcgMCBvYmoKWyAyNzggMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMAowIDY0OCAwIDQ4MSAwIDAgMCAwIDAgMCAwIDAgMCA1NzQgMCAwIDYxMSAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDQ4MSAwCjAgMCA1MDAgMCAwIDAgMCAwIDAgMTMwIDc3OCAwIDAgNTM3IF0KZW5kb2JqCjggMCBvYmoKPDwgL1R5cGUgL0ZvbnQgL1N1YnR5cGUgL1RydWVUeXBlIC9CYXNlRm9udCAvTkJVSFZWK0hlbHZldGljYU5ldWUtVWx0cmFMaWdodAovRm9udERlc2NyaXB0b3IgMTYgMCBSIC9XaWR0aHMgMTcgMCBSIC9GaXJzdENoYXIgMzIgL0xhc3RDaGFyIDExMiAvRW5jb2RpbmcKL01hY1JvbWFuRW5jb2RpbmcgPj4KZW5kb2JqCjE4IDAgb2JqCjw8IC9MZW5ndGggMTkgMCBSIC9MZW5ndGgxIDgzMzIgL0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4Kc3RyZWFtCngBrVl7bFvXeT/nXJKSSPElihSpy6cokuJDEiWKD4kSKdqULEuyLT9iiY5fsiLHcu1EecxIgBZ1gS5DhS1pi+7VbkuWJluTIo2bwoUiDImXdU0bbKtXrMUaGEaLokPQDmgWtAuWxNR+3728spw4Qf4IiY/3nHPvPef7vvP7XoeMM8Za2UUmsfGl84ur/BviCEb+BfS9pQsPBlk7vox/EX3d6dW7zydtu/4Z/a8wJi7ffe7h08433k3h1ixjnYUzy4t3/XcqnGbM/3k8nzuDAcNPJDP6/4B+95nzDz40/VXDv6H/c/T/4ty9S4v3/fRTbzAWeAj9vecXH1oV49Ih9H+AfvCexfPLQzv3PIs+nmGp1XsfeFC3R/enuAWe2D2r9y+vrjLjYfRpvWYQx5c+rczA1nENsvnGiDJ8y49AT7pl5MM6OtzQY8YmZQ3GWpQHjcyEdRiDfPhYQFZmU9rv/7GzNuaAHp3MxTqYm3lYJx6RmZf5mJ8FwGWIdbEw62YR0jcYT82ss5a5hW9z/mhtnW/+4Tqr+l7EutKJ473rjKeCwYmV6iV+Eh2RwkAihJaUCk5ekiKTBxbCteBacG33XWvByeCZxbsu6SLKFTeW12r9wUvs4MIKfg8thC6N1+St5nKtNoJ5dDQPXsHjazXMcLYxA67KUP8NPKRPzQQvSdG5hf0Lly5W5Uvj1ZocCgUnLl2ZW7h0pSqHajU8ZdjiFBx/ZsXd4LkJPBsSuN+sznIQc2CK2toazYmeiIYuXVlbk9cgiTISDq1z1hiApPSMFJlY5+NzC3RrPBySaSAcCofAR62KuVtSMwcXJsBJiDgxfrRKTVuM4tlWsGdSVGr+hFRq+TgqtX4sldq2OL1FpXbwbCOVtt1epeGPUOiWhsdvo+GLqoYv3kbDjm0aJswLNggTfAWGJcFWTLBAfT/g1J8eiITsoYg9ZOcb9Yv8Yv0h/sVm/mZzHW/Qh7Njmw+xy+wsrCiwoQxISaDaBoBcBfVjphbMxDBTPuzMuCyiyVmSspcPJbi+ta3VFXKZzp41+WSXLmnwRpIuZYpF/NKcEpM3FCegzsk2FKunjmRLDxR5Nrx46NBZPMzE5jv4+Yx4DXwZYZ94QuGfWCC2DIzeYnhLFhLP5EMdGf7cY2vP1X8xdZhPzO+uv8Hd/J76l/kLN3LXr2MafDiLbr4lnhA/Y3F2fgOmblImCdoqMuYLwiMEWRSUA02CDoNOgy6AHgH9GejvQN8FfR9kPlbRs5+g8SuQOAZpZHgd4ku2bWDGiNI2gEee6ZPCXRbhbPeLzGBJ5MMW9PtEdqiEvl+IA9Wm7L7FzKHPHkok8FO+ezbdVNV3j8wN7jpdCQQqp3fFdhXj/K3C4aJ/7O5HpqcfOT3WM312R/pgqTtXu2+kuFrLdaQqtO9T+PkjCNwMH/eZddYBdRkhPLFlxC52gPTXANBrYP518PtrkDgGFbSg4QHFQcOg3aAaaAX0MOgLoL8EPQN6EfRDkJmkboFmafoWSM2wmLYzDnumxLND0ZhkJyGd7RYRnnr7608//fXvfGnPvVNdXVP37hGv3cjpnn/hhed1N1bF4dDU/XNz90+FaK9IjhHIYWIZWkKVoAXc665iTZ2yJq0HT6wgYQMS65V2MzTusIeyALpCU/yt+gH+aP1+/gwtN8I7R7gY0dYwY40WBi01NdZowhriKhDHFaQ1bc0PzClt2lHIps1e5Yfrz/LP0dT8kDozzb0H030Jc0usYzvMlXlV+6E59lTpPTyGjyqzFx092Z6+wY/ehneIQVVmKGBLvin+H/VVRSaBtIADtUy8i/ctbH77mhtQYrPCuglbREHSZG8bbnBCfkG1aHjaxioMt0mfnKktc6Olyp1x4AvewyY+WRW/H/+dqN71v28rcqzc+HNB9NqNR8X9qky7wNPD4MnEZm7liUI4bR3puxlkvAoi/8LBEXHFQE23cGS8lQ8OHuxhvqsq+BOzVVE/qfBw9MZTuD4ljtL6qj3UsH4z4v0X1pkHE/IGaMireUBtsAe9ag962INeswc97EEPe9DDHvSwBz3sQQ970MMe9LAHPexBD3vQwx70sAc97EH1lSbCjxFL3WobrQ0sKXocdDnbDWEORZJLyA71ifAf/3rXwsKu+o+r/5feW/D7C3vT/BzvnpqenuLHga5BOTubTs9mZdrrEHzZAfiyCvtbMgMywQ0kLl3KEmHsc4ESGWzkOiv0K7ZegGwFTbYCZCtAtgJkK0C2AmQrQLYCZCtAtgJkK0C2AmQrQLaCIpv52gbrbWzCOuvFBrmhOzN0uPMarHKs4ZfHsHwPMira3h6gNR82KI5PR1L28z5Jc3wdGRK74RkD3C8pTgKe8UxVis+eK5ePjIUdel9mZ/Rbrpjf7h+ohKMT2ZCoJqdOZmdO5tvlVDHwuGcoKUeHd3gHDpe7f7Pj+KivI9LX29OaKiV9xqTe1hnx9ORCVmd8LJ4/WPAmS5OdiZEed3PM4PCn/LF82OZITSsqrECn54AVD5mbB0MkgAfCtMJqqN2q2iIaeMDRkNah2GPDtWcBSXJ05N3tlcDOlemZlZ2Bqje3d3Bgbw7GXc/nl6aTyemlPP9hfbI0n/d48vMljj3kSsw7gvWbsLhqB2QDEtYyNJSeHkC042EpLBl48z7eMvVf4g0F9a/d+Jz4LOE9uPkWrwsHy2JT/34D8HAonFcgRaqB+5Qa8SqIeBVEvAoiXgW+o4KIV0HEqyDiVRDxKoh4FUS8CiJeBRGvokW8CiJeBUAi3DlYRcEYMcqBBfLQ/qsg9KmdQzuF6wzhYxQZN6lxFMwMwJ9Re4C0h0ChhYgmC0DQCJMZpZHLZw3hrqy9ARW9hhECUb7E87mymB31DkScpnZ/e2ZpX1ruL3dH88mwXehtPrfZbmoxRbpy1a7e6XyQ7/EN7Oh2JYKO3sk7esxdBn66PNzijgednVaDMJiDY7nYcMRu6Yx52kNusxBZXZS3DVW6ze2JHQ8fiI4mXBY54o7GXaYmYTRBA9D5KHDjxr65oeLn1lkfNszdQI8bsgvoQJCtoK1DW4d2H9qt0BeNuTDmwlgr2h7VD3lgqx7NVj1oemCrHtiqB9vqga16YKse2KoHtuqBrXpgqx7Yqge26lFslWYOkx8iMPuBKdK2H5q3wzapbVdwm8lm/FB+wwPBFJvCdq4huJGfjI4I11K5OD/i9Y7MF72FdMQo+HFHfLx3cCLpcCQnBnvH4w7C4ancSHJmCRCfSVl8CS/vqRd69pVisdK+nsaVXFUXMPobYDRMeZi3gUqvikovUOkFKr1ApReo9AKVXqDSC1R6gUovUOkFKr1ApReo9Gqo9AKVXqgMOOtABCQJOyCtDhUdtSlqIg/jmrfJDOZuTcNc/Btl0wMHi3eWgsHSncWFVXNZ6uwZ8vVWezs68FOe5KdqC6nppUIBBrxyPJiLuaKlffH4vlJ0h4oDyi8FcNDJ+tjL66wfiueABy1PsUaAtP3vR9tyFYR9p3E32m48j01nXLEoGu3EaCeesKAtX1PyVBn6kaEfGfqRoR8Z+pGhHxn6kaEfGfqRoR8Z+pGhH1nTjwz9yNCPul6EkEEZTqLhIBLQVQsSWWK2BboKUWx9X9qacfIGIjQbFKIsjJF03rcFjuHBmFHU//qD4Kj/pzfhs6RUdCRtvoTnFBfvB4dQsPEmsGGEjCcoc3EpLFHmQiU+sUd2YgJxaIfaflzJ3zC0A2gH+oGBLghLD3cpiFfrBTvaHFOqGwI8hEoS+R19KHczDKnAAE76BH++/oLOkxiNwew76t/hfyLc+YXSWK0gy4Xa2KlPtZeteyd6xxMOHg3vzAQCmR213Mr+wf49pzKwgOTJhdGp6MhuwgaHBwcGgA1Zzc3aFSYkcNwOot2ndiftCqUo1kZyZFU4VsVWijA8ZMLDJohLfSfaJHbHVUiTDatx1dWRVXHu4yjRkGBw6bQweztNLrPVbYkOeI0LC2X+o2yvs1u2SSInhKunEOrP1gf4j8Ar7UGG0x4kWIn90zorfwSOy1h8O45VX6aa4E0ck3yE4bSK4TQ2NY39TQPDaWA4DQyngeE0MJwGhtPAcBoYTgPDaWA4rWE4DQyntzA8QpUA2QLtJ9VcLYh61KZKJNnAdRJYjtjDzluwnMtnKPnCFsfCfkCgJGIUsLeBm79ZFi2hRMZXmC8GOocXSsN3uMTehc7BuE9flmLF6Vh5Puf5VWo80e5IjPf2lnvanIlSgr9ec0Z9tviuE9n84lR8JD15/C6TO+aLF6NtXeNHp8PD07GePaORaGk2GpseDqvYKAIbVKR2sv0byoEUCdEJIVBhKQLZoLtOEEUSuqreQvHtZMXkRjZQ76kJtRMveoA4yt3VjF1FuRNIUGKoAS6+WOYdYCqa8beW21K7c/mjbfBcSDJ7o5V+2Z0YDtVf4rN90xnvcD8PABNjYPXT4JHOE2LbM3kVgOTXCMEGQrCavWNtZybbhFXHyuWyeG15+fs33hM61Pa/3NzRmMvJdn74XO1a9GqGPZBKqEzQ0G9FuxWLtuIkQl0HG2qIxpyDLiWa0aLtkaa2ZpPTFEp6WtT1XwomOedDoiM25BMZssubcn1UnTYG/m/WaaMwSD3so4f9+63cm69VJPYyNo0fQ+MxrfFbtaFX9hMHBxTYJTanjFIpjg2lF3+LrKHxYqPxsjpCL7rhm93ai3SbIgKdYpiZTYkXpP0oFBIFFGLYLEkZlTGauKb4ekKJ6l3CDaSEcZN0Sl6E2j54EapKNTvQmi4FOuEuQxP5FcAnO1puixR7/AmPqWwO5uLVXPmbmSMdloOZ8n6PJHQ33uPB+ETG1x7u76w/ybu7dw4Fdo7Xn+bHLw9m4GDT82CQMxX3DkAeKOgknj8u7j+I+Q9Deva2SCcOb4P0GnhQ/N9bwoj9dUCRr6KEgdrioDA26pOrE3ugcA8U78HMVtUvWuEXrfBnVvhFK/yiFX7RCr9ohV+0wi9a4Ret8ItW+EUr/KJV84tW+EUrwAF/aESuR2o0wgvQ2Ty12+EDuXYE03B0+W3nUVSWcZeW6GhXLRNqXPl1LdvRrlktGUI1Q0kR/n6gXHgHcmEHsJpi31RzYfJyxAZBjDwYXbU8iPJgC/RKYzdjB2tUrhQzPqmMWLEBqtrU7NcB/fgbqacf+iHcZ9RYoNTiCsw/kAgr2U7Bq2U75SWX4EtarmNPTA4pibDuRokHvQmvRcuFi7lT9bqW66TmRruREENXUehKQFeUL15W80X8e6PoioK65vGpdqC2prPtuePNaKBG2E8uS6SZlSyR6oebmeH2jDE9oH9/ZEVJhnxDcyCNOkLgJM8YGxze0psvTyVE/UlNc1oJ8ZJ45VRn3G/TskQLlFj/Ob+iqW5bDQEfzIvCgkwf7NGfR4QwCTybr4LUcyTiW42JVIoy3KBz64aH68hGVU/mzUwmJ5fdQnFbjvjEoCzKUrX8MOaEjwqgVnkFe1RkzxBgAso6VEYhwCptjrYJ437F39IRmnLorfRc6smLC9Wci3w3nbK6EJxdqOZcqOZcqOZcqOZcqOZcqOZcqOZcqOZcqOZcqOZcqOZcMPJ1FoONUM47iGsM1zGqp+XGcY963pxX2Hn/efPNU5Uocp2tSm/7KQy/ryz8mZ2xrolC2BUbCux3xoMOOTHQUb5DV26+787u6WK0M56VD9miQaerO+mU0xEXP490p93WVYiFegPO5ja9xRnoQGVsrIyfOGyLFBNdg11OnUNnbvc5PV3OFrM3qeizafNd3iIeh1gPfLA6MmCLaA+bcKX6xwVpaT8p1XVoFYutccZsg+J1OHSjjdfhAdpfOuJWAYC4iraWmurRbqfolkUmiJzYgCQhl8G5Qh/P2jPZJ8pHjvDBssVrkeUu+4rQfe1r2frjOwZEVhLhAD+aVbFQBBauI4NxU55280zog3Lc9GTbq7mtrI2iF50gNTdyVDqxpnpLMZdojFp0AkJZKr9ebouOxGfGRLk9OTEwcsIDnNb/JzYzEp6a5HP1b/dNDcr5oWOKbvEfDv9X8PdR50cyzzjo/Ei8uvMfK0+IJwn1mLHI8Wcy4V2hi0+88/YJ6+jvuV2i/3vZ9x5bDWhXZG8ZZD8/Q78FPkz94D3p1c0k8+lO1Z/ZrOkmlJkaN5VLlP+ODXKYBugYaFFc2HxHXGFR8RybEvtBHpz5X2d76CrhPAr3dokLGL/AQnimIvbj+SYWFCU2imsXrlG6cokZRAp9D2z0ReRzL27+kq6YY5TGlHv0Dp7nRTbKf8ECvIc10T3UOySBF99JdpKtsff4Sf5lfoW/zt8RVfF58ab0Vel16T3dd/V79RuGo4YfNPmbftrc07y3+SvN32o50PI3xlHjj02rpqdaZ1ufbX3H7MVspMMo+zRmDoJUDSESo3kZ91QNtzX0Y6C0Z+/c3L4KjjSXz11YfnBlaXHv8h8s986u3H3mwcbbbPOv6P+823yiGJMQ+u34xzqKbKUHjiXF+lEkDbAMy+HYdphV2QSk2wXt7mbTbIbN4r+Jfcg+97MD7CA7xO5AljHPFhiSHxgQjIkl/x9NC1daCmVuZHN0cmVhbQplbmRvYmoKMTkgMCBvYmoKNDkxMAplbmRvYmoKMjAgMCBvYmoKPDwgL1R5cGUgL0ZvbnREZXNjcmlwdG9yIC9Bc2NlbnQgOTY3IC9DYXBIZWlnaHQgNzIyIC9EZXNjZW50IC0yMTMgL0ZsYWdzIDMyCi9Gb250QkJveCBbLTM0MyAtMjE0IDEwOTMgOTY3XSAvRm9udE5hbWUgL05QUE9BRCtIZWx2ZXRpY2FOZXVlLUxpZ2h0IC9JdGFsaWNBbmdsZQowIC9TdGVtViA2OCAvTGVhZGluZyAyOSAvTWF4V2lkdGggMTEyMiAvU3RlbUggNTggL1hIZWlnaHQgNTI0IC9Gb250RmlsZTIgMTggMCBSCj4+CmVuZG9iagoyMSAwIG9iagpbIDI3OCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMjc4IDAgMjc4IDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwCjYzMCAwIDcwNCA2ODUgNTkzIDUzNyAwIDAgMjIyIDAgMCA1MzcgODMzIDcwNCAwIDYzMCAwIDAgNjMwIDAgNjg1IDU5MyAwIDAKMCAwIDAgMCAwIDAgMCAwIDUxOSA1NzQgNTE5IDU3NCA1MTkgMjU5IDU1NiA1MzcgMTg1IDE4NSAwIDE4NSA4MzMgNTM3IDU1Ngo1NzQgNTc0IDMxNSA0ODEgMjk2IDUzNyA0NjMgXQplbmRvYmoKMTAgMCBvYmoKPDwgL1R5cGUgL0ZvbnQgL1N1YnR5cGUgL1RydWVUeXBlIC9CYXNlRm9udCAvTlBQT0FEK0hlbHZldGljYU5ldWUtTGlnaHQgL0ZvbnREZXNjcmlwdG9yCjIwIDAgUiAvV2lkdGhzIDIxIDAgUiAvRmlyc3RDaGFyIDMyIC9MYXN0Q2hhciAxMTggL0VuY29kaW5nIC9NYWNSb21hbkVuY29kaW5nCj4+CmVuZG9iagoyMiAwIG9iago8PCAvTGVuZ3RoIDIzIDAgUiAvTGVuZ3RoMSA1MDU2IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlID4+CnN0cmVhbQp4AbVYa2xb1R0/517b1zfX1/EjfsWP+MaPWyd2Hr6NnYRgO5HTpkmA0KatzUhp1uZVWpptoTwmRAVj0jJpRQixFdCExDTgw1imqVvI9qGaNrR1ExQhBtr6YZs2aUNoQojXBLX3O/fagW6F8WHY+t97zrnnnvP//37/x7EJJYTYyGnCk9KRE/Orvc6BKYz8jhDqOnJqLUra8CX0TxgzLa4uneh27P4V+n8lhDu3dPyuxfmB92J49Dgh8sbywvzRNyKxPkJc05ifW8aAMGd5Df2voR9fPrF2Z+xucjf6T6M/ffzkkfngWuRh9N9Gf8eJ+TtXuR5+PyHuEPrR2+ZPLLzm+3UE/RH0s6snv7JGf8+9gf4q+hOrX15YXSUtB9DfRN8Kofiyjw1Ni9761IsxuTmFazauuPPEtN03o3XFsgIGrCJpkbClbCethDiIc3v659xwfZb1wR/ASU9tEnGm8iNKv1XdpPUHNkk5/BwRCX/LocwmoelodHylvEEPo8OlMdCloMWno7s2+MSuvZVYNboeXd9zdD26K7o8f3TDlNDveLCwXu2NbpB9lRVcZyvKRqka3G4uVKvDWMfE1sErmL5exQrHGivgrg/1XsYkc3oqusEnZyo3VjZOl4MbpXI1qCjR8Y3zM5WN8+WgUq1ilmVbU2h8z4q/obMAnS1deG41VtmHNbBEdX2drYkel1Q2zq+vB9dhiT4SUzYpaQzAUjaHT4xv0tJMhT0qxZQgG4gpMQV6VMtYW0xP7auMQxOFadLy6ZBK24pirg3qSTqk8v8JUvtngbT1M0Hq2Nb0Ckid0NnBIHVdHdLYpwC6jXDpKgifNhA+fRWE3R9DmPk2R7KI418gLnkiEInAS3rhTr19/QnFqSScipNu1U7T07U76YNW+qa1hjfYhxIfrveTY3ivlWzhihW6QbOjr/8amo/5Zv37j2GMIwho7hHuAvKFl3xpC5s49YmCY9RM/oDO6xBubjSIUHGSACQFGYLsgVQhK5C7IN+AnIU8A3kO8huIPLdJ/JewvwyN2P6yYwu72vU2gS5uXskWuIGdPVyMurWsp83OqU88RlsOP3QkGywtX1e7eN+Tryb33lvlLlzO7brjsdnRlanU5VXuwKuT37y1xHId038J+oukA+jQXra+0Fh/C6mK19sWfS/NreQVyHfPlvfQL0i1p/L0ARtbmc4OU254G497dDyC5PYtgGIs5jXwEICH0MRDAB4C8BCAhwA8BOAhAA8BeAjAQwAeAvAQgIcAPAQdj/Al6GmHnldi4tjGJEi1bIRjUMQkqgGVBkDfKb6kDCQ9wb5S4vVHy2Mf5m4aTYS18RQ9TuNypD+uXtvlpYdgTLbjmoP51FjGz/xgHPjcC3sEEsS+vI6PwQVxAC+I+SJ4EDQeW+VvtL1zpnzmHYAyDJQfZJAQrv5+/S36W85NSmSWvLdJ9sMFD0DylxDUl3Q/kYCL1MRFAi4ScJGAiwRcJOAiARcJuEjARQIuEnCRgIsEXCQdl/1QxXsRAhU7gHve6RrC+hiNYzSOUZXkibQ9msFoBlp4MWPyEhzUQibx2iRJQnKQXZADkEXIKcjXIY9Avg/5CeR5iDwH9V9B428Qbg6eOg6cmKeOw1NHmP+gPcK8RyvwBi8WwZIU7LynLcJp2VzeZ6cer5YtUjuNdSYHdha4fIHXPbrTzpkFNtoDD9eH84WiOZzORVxqh6ejI7JPi5Wy4UD3SDz3xSBHKTcQDIiCJEhek1UWTVKLEM8V21OTwyrNtrUemvZ2Rd1dhd1hmxINW+mxaCZoszjCPq3LaqVWiyc+mErmOh39GU6wC3lzP6WC3eZPDnU7M7097li6vaUtVbrv4PjeFl8skMr4BI5yFknUOSZ2cPwPcCyRLvKvTdINZNMQBzgWDY5FcCw2ORbBsQiORXAsgmMRHIvgWATHIjgWwbEIjkVwLIJjUeeYgDOCVbvBWTva7WinDO5S4C4F7lLgLgXuUuAuBe5S4C4F7lLgLgXuUuAuBe5STe5S4C4FtVBksKKN6YzVZbRl+EwUxxERPgNm4ySssxkHs54Gsx60KXIfY5mCZaowlr2eNotZKdAmi542EJzLx5pc0mdqP7R2aqPq2BStPUUf5rl2baJ3bHkicevS3Fp70VUpJocSLpqMj2kd14+1uEu+tDczORAevPn2wuLazZWh3SA95mXxiSMMPYT49JFuxKcHKssN3ViuZMczQ7dN4oRJLGbdiFek8HwBecLry/fAwSytNKZHMA3ZFlvFZLrF3SI5os5Sweao2m5aLC7SFwcmkjETt9PMjQ1NDdT66YuMd1p/F5e/YP8O8lUGi6xv5zGynQzG5SbjMhiXGexgXAbjMhiXwbgMxmUwLoNxGYzLYFwG47LOuMKyHaOcQpywrh2nVGZRO6yLMPtADqsDmvuj+EInZ2BvEWLuM2cLQy07cqVOjxpxmfiJ4m6rr2uoc/hwkCvqyVvsvyEfafHG2uWgrfZjOrXDkxhQHMUeGkfugn3kBdj38fpJUD+R8fICNjrjLXqP3VY4yV1YWHj+8oecycCk8Q6ruExz/Q02+2zhUbYnFjTm8WbETC/5YJP0YVo/pA8USaBKQtvwQoxjLIpI8huR5Aeu/iaufuDqB65+4OoHrn7g6geufuDqB65+4OoHrn7g6geufh3XZvREsXIYu4Wxm4S2akSTimhSEU0qoklFNKmIJhXRpCKaVESTimhSEU0qoklFNKnNaFIRTSpUa9htx7JBsNbVYK0LW1ghHmzJ2jFWOxrcIWoAKJKfHkAMWxZBSXinENOdVFDzR28rDLYksoXOnpRloqhJkcy1Ka3Xsqc4wZvKU8NzEc48lhs+HOLaONPlD6klc8NwZy7pb69t/DN7cDSZTQaCtWfpjCNsH5ku9OXzhQx308f92A0/PvG5+fF/+/BH1rOKbWFGf+S5zNqreu58mHPp9n2S5x5iNnH1l+tjnBP+5Sc72FnETkJ66NiN6AzBi0JNLwrBi0LwohC8KAQvCsGLQvCiELwoBC8KwYtC8KIQvCgELwrpXtTFopOdmdx4nUWlG1GpIFmztsKqHgzSImC0cQZhXOZ9LEHidGJUu2PfLhQ5cyQ9GBldnlT9/Xuy5YMK1/a93eVQfl9ufCoyNAtbCzTRkQnJI7fcMdJ7fb5jSJtLVsZ6F2ZzM+N9S/vzBoc11KBzsHeEPMmOceyYsoWCpOnqMO9m6c9jxJAG67Wm9RrU12C9Bus1WK/Beg3Wa7Beg/UarNdgvQbrNVivwXp2ytAa54kOuDmLT7Z+10UI66NdYGfXwUY+HgQ2CeQvhk0C2ORjFnYCMJBRe1DzC+hGOEHL4TiL4o8TgsUAqocrFLhobncyMTmSUPqvCXqRygKJjKe9R3FxRRrsL6c6yoNxv5rNZtVAINnjCQ6kAk/3j6fbHImRdLyv0yeJLm/IFex0W+VQVyRT3OFydA4koxnF1+YOdLYH4j7JFsGvBcDWVn8Lpd0EA1dYOfHoKssO/YwkIzPIyAwyMoOMzCAjM8jIDDIyg4zMICMzyMgMMjKDjMzA0rh+RpKRGVgxaGQGlsutDTSsQENxx3g7b5x1cnl0YLpxQNIE+tDJ4pC0Y6AY+48UbgRC7T0jgccDSOD0uto5PYG3FnoOIWuz+viSXh+DsKeI3T0gJ4zd/3edRAIGiX4Qys65IeSqT6qb+UY1+MT6+UdWJk4WT16ljn7QLBw69gx/suvZnwVuaR15hzr5vzPX/uWZ1Y7mnUU1qsYF/PbCaYqN4oN3+J/Wu0nIdLi2VN9rmsJ7rxiPGlc3fZtkKTsyeImP+wExcwHIKUiajHNC/X1OIHYKvLgAat5zhvCj9XdZnyvUX6Z/rtfwThv9OcMUCSBE+siDZJNG6Ri3k3uT7+Of4Lf4F0wPm2rmU5Z2y2nLa0wv5Ia7wANqaUNb/BuG5jk8Y0/ZCYbd2d9CUUJm9o9OV0a7JxaOn1pYWzkyf/3C7QuZ6ZWl5bU9a/PHV45gHmyu49+u+uPst+1VPm6M8fjxqOoBnSdlMoGQniLT5AYyQ24k++CxFXIef0AgEEn3vwHogcgHCmVuZHN0cmVhbQplbmRvYmoKMjMgMCBvYmoKMzA0OQplbmRvYmoKMjQgMCBvYmoKPDwgL1R5cGUgL0ZvbnREZXNjcmlwdG9yIC9Bc2NlbnQgOTUxIC9DYXBIZWlnaHQgNzIyIC9EZXNjZW50IC0yMTMgL0ZsYWdzIDk2Ci9Gb250QkJveCBbLTQwOSAtMjE0IDEwOTkgOTUxXSAvRm9udE5hbWUgL1BVQUxYQStIZWx2ZXRpY2FOZXVlLUxpZ2h0SXRhbGljCi9JdGFsaWNBbmdsZSAtMTIgL1N0ZW1WIDY3IC9MZWFkaW5nIDI4IC9NYXhXaWR0aCAxMTIwIC9TdGVtSCA1OCAvWEhlaWdodCA1MjQKL0ZvbnRGaWxlMiAyMiAwIFIgPj4KZW5kb2JqCjI1IDAgb2JqClsgMjc4IDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMjc4IDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAKMCAwIDY4NSAwIDUzNyAwIDAgMCAwIDAgMCAwIDAgMCA2MzAgMCAwIDAgNTU2IDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDUxOQowIDAgMCA1MTkgMjU5IDAgNTM3IDE4NSAwIDAgMTg1IDgzMyA1MzcgMCA1NzQgMCAwIDQ4MSAwIDUzNyAwIDAgMCAwIDAgMCAwCjAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAKMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMAowIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgNDQ0IF0KZW5kb2JqCjkgMCBvYmoKPDwgL1R5cGUgL0ZvbnQgL1N1YnR5cGUgL1RydWVUeXBlIC9CYXNlRm9udCAvUFVBTFhBK0hlbHZldGljYU5ldWUtTGlnaHRJdGFsaWMKL0ZvbnREZXNjcmlwdG9yIDI0IDAgUiAvV2lkdGhzIDI1IDAgUiAvRmlyc3RDaGFyIDMyIC9MYXN0Q2hhciAyMjIgL0VuY29kaW5nCi9NYWNSb21hbkVuY29kaW5nID4+CmVuZG9iagoxIDAgb2JqCjw8IC9UaXRsZSAoc2FtcGxlKSAvQXV0aG9yIChQaGlsaXAgSHV0Y2hpc29uKSAvQ3JlYXRvciAoUGFnZXMpIC9Qcm9kdWNlciAoTWFjIE9TIFggMTAuNS40IFF1YXJ0eiBQREZDb250ZXh0KQovQ3JlYXRpb25EYXRlIChEOjIwMDgwNzAxMDUyNDQ3WjAwJzAwJykgL01vZERhdGUgKEQ6MjAwODA3MDEwNTI0NDdaMDAnMDAnKQo+PgplbmRvYmoKeHJlZgowIDI2CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAxNzkzMCAwMDAwMCBuIAowMDAwMDAzOTgyIDAwMDAwIG4gCjAwMDAwMDUwNzMgMDAwMDAgbiAKMDAwMDAwMDAyMiAwMDAwMCBuIAowMDAwMDAzOTYyIDAwMDAwIG4gCjAwMDAwMDQwODYgMDAwMDAgbiAKMDAwMDAwNTAzNyAwMDAwMCBuIAowMDAwMDA3OTU3IDAwMDAwIG4gCjAwMDAwMTc3NDAgMDAwMDAgbiAKMDAwMDAxMzY5MiAwMDAwMCBuIAowMDAwMDA0MjA5IDAwMDAwIG4gCjAwMDAwMDUwMTcgMDAwMDAgbiAKMDAwMDAwNTE1NiAwMDAwMCBuIAowMDAwMDA1MjA2IDAwMDAwIG4gCjAwMDAwMDc0NzIgMDAwMDAgbiAKMDAwMDAwNzQ5MyAwMDAwMCBuIAowMDAwMDA3NzU1IDAwMDAwIG4gCjAwMDAwMDgxNDYgMDAwMDAgbiAKMDAwMDAxMzE0NiAwMDAwMCBuIAowMDAwMDEzMTY3IDAwMDAwIG4gCjAwMDAwMTM0MjQgMDAwMDAgbiAKMDAwMDAxMzg3NyAwMDAwMCBuIAowMDAwMDE3MDE2IDAwMDAwIG4gCjAwMDAwMTcwMzcgMDAwMDAgbiAKMDAwMDAxNzMwMiAwMDAwMCBuIAp0cmFpbGVyCjw8IC9TaXplIDI2IC9Sb290IDEzIDAgUiAvSW5mbyAxIDAgUiAvSUQgWyA8NGU5NDk1MTVhYWYxMzI0OThmNjUwZTdiZGU2Y2RjMmY+Cjw0ZTk0OTUxNWFhZjEzMjQ5OGY2NTBlN2JkZTZjZGMyZj4gXSA+PgpzdGFydHhyZWYKMTgxMzIKJSVFT0YK" +} \ No newline at end of file diff --git a/packages/v1-ready/docusign/tsconfig.build.json b/packages/v1-ready/docusign/tsconfig.build.json new file mode 100644 index 0000000..13eb964 --- /dev/null +++ b/packages/v1-ready/docusign/tsconfig.build.json @@ -0,0 +1,35 @@ +{ + // "extends": "../../tsconfig.base.json", // Removed as base file doesn't exist + "compilerOptions": { + // Base options commonly found in tsconfig.base.json + "target": "ES2016", // Or a newer target like ES2020, ES2022 + "module": "CommonJS", // Standard for Node.js libraries + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "lib": ["ESNext"], + + // Module-specific options + "outDir": "./dist", + "rootDir": ".", + "baseUrl": ".", + "paths": { + "*": ["node_modules/*"] + }, + "composite": false, // Set to false as it's not extending/being referenced by other projects in the same way + "declaration": true, + "declarationMap": true, + "sourceMap": true + }, + "include": [ + "./**/*.ts", + "./*.ts" + ], + "exclude": [ + "node_modules", + "dist", + "tests" + ] +} \ No newline at end of file diff --git a/packages/v1-ready/docusign/tsconfig.json b/packages/v1-ready/docusign/tsconfig.json new file mode 100644 index 0000000..053b62d --- /dev/null +++ b/packages/v1-ready/docusign/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "NodeNext", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "lib": ["ESNext"], + "baseUrl": ".", + "paths": { + "*": ["node_modules/*"] + }, + "sourceMap": true + }, + "include": [ + "./*.ts", + "./**/*.ts", + "./tests/**/*.ts", + "tests/auth.test.js" + ], + "exclude": [ + "node_modules", + "dist" + ] +} \ No newline at end of file