@moneylion/engine-api 1.3.4 → 1.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +59 -0
- package/dist/decoders.d.ts +31 -0
- package/dist/decoders.js +29 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/leads.d.ts +28 -0
- package/dist/leads.js +45 -1
- package/dist/leads.test.js +81 -0
- package/dist/offer_catalog_rate_tables.d.ts +45 -0
- package/dist/offer_catalog_rate_tables.js +96 -0
- package/dist/offer_catalog_rate_tables.test.d.ts +1 -0
- package/dist/offer_catalog_rate_tables.test.js +215 -0
- package/package.json +46 -46
package/README.md
CHANGED
|
@@ -85,6 +85,65 @@ new AsyncRateTable({ uuid: rateTableUuid, client: client }).resolve();
|
|
|
85
85
|
new AsyncRateTable({ rateTable: rateTable, client: client }).resolve();
|
|
86
86
|
```
|
|
87
87
|
|
|
88
|
+
### Offer Catalog Rate Tables
|
|
89
|
+
|
|
90
|
+
Offer catalog rate tables work similarly to regular rate tables but use different endpoints and require `offerCatalogProductTypes` instead of `productTypes`.
|
|
91
|
+
|
|
92
|
+
#### Creating an Offer Catalog Rate Table
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
import { Leads } from "@moneylion/engine-api";
|
|
96
|
+
|
|
97
|
+
const leads = new Leads(endpoint, authentication_token);
|
|
98
|
+
|
|
99
|
+
// The lead data should include offerCatalogProductTypes
|
|
100
|
+
const leadData = {
|
|
101
|
+
personalInformation: {
|
|
102
|
+
firstName: "John",
|
|
103
|
+
lastName: "Doe",
|
|
104
|
+
email: "john@example.com",
|
|
105
|
+
// ... other fields
|
|
106
|
+
},
|
|
107
|
+
financialInformation: {
|
|
108
|
+
annualIncome: 75000,
|
|
109
|
+
// ... other fields
|
|
110
|
+
},
|
|
111
|
+
loanInformation: {
|
|
112
|
+
purpose: "debt_consolidation",
|
|
113
|
+
loanAmount: 10000
|
|
114
|
+
},
|
|
115
|
+
productTypes: ["DebtSettlement"] // Product types for offer catalog
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
// First, create or get a lead UUID
|
|
119
|
+
const leadUuid = await leads.create(leadData);
|
|
120
|
+
|
|
121
|
+
// Non-blocking: returns AsyncOfferCatalogRateTable that you can resolve
|
|
122
|
+
const asyncRateTable = await leads.getOfferCatalogRateTable(leadUuid, leadData);
|
|
123
|
+
const rateTable = await asyncRateTable.resolve();
|
|
124
|
+
|
|
125
|
+
// Blocking: waits for the final rate table
|
|
126
|
+
const rateTable = await leads.getOfferCatalogRateTableBlocking(leadUuid, leadData);
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
#### Fetching an Existing Offer Catalog Rate Table
|
|
130
|
+
|
|
131
|
+
If you already have a rate table UUID, you can fetch it directly:
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
import { AsyncOfferCatalogRateTable } from "@moneylion/engine-api";
|
|
135
|
+
|
|
136
|
+
new AsyncOfferCatalogRateTable({ uuid: offerCatalogRateTableUuid, host: endpoint, auth_token: authentication_token }).resolve();
|
|
137
|
+
|
|
138
|
+
// You can also pass a pre-created client instead of endpoint and token
|
|
139
|
+
new AsyncOfferCatalogRateTable({ uuid: offerCatalogRateTableUuid, client: client }).resolve();
|
|
140
|
+
|
|
141
|
+
// You can also pass an OfferCatalogRateTable object instead of a uuid.
|
|
142
|
+
new AsyncOfferCatalogRateTable({ rateTable: offerCatalogRateTable, client: client }).resolve();
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
The `AsyncOfferCatalogRateTable` class handles polling automatically, just like `AsyncRateTable`, waiting for all pending responses to complete before resolving.
|
|
146
|
+
|
|
88
147
|
## For Contributors
|
|
89
148
|
|
|
90
149
|
This is a relatively normal NPM and Typescript project.
|
package/dist/decoders.d.ts
CHANGED
|
@@ -43,4 +43,35 @@ type TempRateTableWithNulledFields = {
|
|
|
43
43
|
export type FixedRateTable = FixNull<TempRateTableWithNulledFields>;
|
|
44
44
|
export declare const rateTableDecoder: Decoder<FixedRateTable>;
|
|
45
45
|
export declare const leadUuidDecoder: Decoder<components["schemas"]["LeadUuid"]>;
|
|
46
|
+
type OfferCatalogOffer = FixNull<{
|
|
47
|
+
uuid: string;
|
|
48
|
+
version?: number;
|
|
49
|
+
productVersions?: number[];
|
|
50
|
+
productType: string;
|
|
51
|
+
productDisplayName?: string;
|
|
52
|
+
headline: string;
|
|
53
|
+
descriptionPoints: string[];
|
|
54
|
+
stateExclusions?: string[];
|
|
55
|
+
badges?: string[];
|
|
56
|
+
url: string;
|
|
57
|
+
financialInstitutionUuid: string;
|
|
58
|
+
financialInstitutionDisplayName: string;
|
|
59
|
+
financialInstitutionImageUrl: string;
|
|
60
|
+
enrollmentFeeBasisPointsMin?: number;
|
|
61
|
+
enrollmentFeeBasisPointsMax?: number;
|
|
62
|
+
debtAmountCentsMin?: number;
|
|
63
|
+
recommendationScore: number;
|
|
64
|
+
isMonetized?: boolean;
|
|
65
|
+
status?: string;
|
|
66
|
+
createdAt?: string;
|
|
67
|
+
updatedAt?: string;
|
|
68
|
+
}>;
|
|
69
|
+
export declare const offerCatalogOfferDecoder: Decoder<OfferCatalogOffer>;
|
|
70
|
+
export type FixedOfferCatalogRateTable = FixNull<{
|
|
71
|
+
uuid: string;
|
|
72
|
+
leadUuid: string;
|
|
73
|
+
offers: Array<OfferCatalogOffer>;
|
|
74
|
+
pendingResponses?: Array<FixNull<components["schemas"]["PendingResponse"]>>;
|
|
75
|
+
}>;
|
|
76
|
+
export declare const offerCatalogRateTableDecoder: Decoder<FixedOfferCatalogRateTable>;
|
|
46
77
|
export {};
|
package/dist/decoders.js
CHANGED
|
@@ -221,3 +221,32 @@ export const rateTableDecoder = object({
|
|
|
221
221
|
export const leadUuidDecoder = object({
|
|
222
222
|
uuid: string(),
|
|
223
223
|
});
|
|
224
|
+
export const offerCatalogOfferDecoder = object({
|
|
225
|
+
uuid: string(),
|
|
226
|
+
version: optional(number()),
|
|
227
|
+
productVersions: optional(array(number())),
|
|
228
|
+
productType: string(),
|
|
229
|
+
productDisplayName: optional(string()),
|
|
230
|
+
headline: string(),
|
|
231
|
+
descriptionPoints: array(string()),
|
|
232
|
+
stateExclusions: optional(array(string())),
|
|
233
|
+
badges: optional(array(string())),
|
|
234
|
+
url: string(),
|
|
235
|
+
financialInstitutionUuid: string(),
|
|
236
|
+
financialInstitutionDisplayName: string(),
|
|
237
|
+
financialInstitutionImageUrl: string(),
|
|
238
|
+
enrollmentFeeBasisPointsMin: optional(number()),
|
|
239
|
+
enrollmentFeeBasisPointsMax: optional(number()),
|
|
240
|
+
debtAmountCentsMin: optional(number()),
|
|
241
|
+
recommendationScore: number(),
|
|
242
|
+
isMonetized: optional(boolean()),
|
|
243
|
+
status: optional(string()),
|
|
244
|
+
createdAt: optional(string()),
|
|
245
|
+
updatedAt: optional(string()),
|
|
246
|
+
});
|
|
247
|
+
export const offerCatalogRateTableDecoder = object({
|
|
248
|
+
uuid: string(),
|
|
249
|
+
leadUuid: string(),
|
|
250
|
+
offers: array(offerCatalogOfferDecoder),
|
|
251
|
+
pendingResponses: optional(array(pendingResponseDecoder)),
|
|
252
|
+
});
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/leads.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AsyncRateTable, type RateTable } from "./rate_tables.js";
|
|
2
|
+
import { AsyncOfferCatalogRateTable, type OfferCatalogRateTable } from "./offer_catalog_rate_tables.js";
|
|
2
3
|
import type { components } from "./generated/schema";
|
|
3
4
|
export type LeadCreateData = components["schemas"]["LeadCreateData"];
|
|
4
5
|
/**
|
|
@@ -89,4 +90,31 @@ export declare class Leads {
|
|
|
89
90
|
* Only use this if you can handle waiting for that amount of time.
|
|
90
91
|
*/
|
|
91
92
|
getRateTableBlocking(leadUuid: string, lead: LeadCreateData): Promise<RateTable>;
|
|
93
|
+
/**
|
|
94
|
+
* Create a new offer catalog rate table for a lead.
|
|
95
|
+
*
|
|
96
|
+
* @param leadUuid - The UUID of the lead for which to create the rate table.
|
|
97
|
+
* @param lead - An object representing the lead information.
|
|
98
|
+
*
|
|
99
|
+
* @returns AsyncOfferCatalogRateTable
|
|
100
|
+
*
|
|
101
|
+
* @remarks
|
|
102
|
+
* This method returns an AsyncOfferCatalogRateTable.
|
|
103
|
+
* Use the resolve method on the returned object to retrieve the final Offer Catalog Rate Table.
|
|
104
|
+
*/
|
|
105
|
+
getOfferCatalogRateTable(leadUuid: string, lead: LeadCreateData): Promise<AsyncOfferCatalogRateTable>;
|
|
106
|
+
/**
|
|
107
|
+
* Create a new offer catalog rate table, immediately resolving it.
|
|
108
|
+
*
|
|
109
|
+
* @param leadUuid - The UUID of the lead for which to create the rate table.
|
|
110
|
+
* @param lead - An object representing the lead information.
|
|
111
|
+
*
|
|
112
|
+
* @returns OfferCatalogRateTable
|
|
113
|
+
*
|
|
114
|
+
* @remarks
|
|
115
|
+
* This method will immediately resolve the final Offer Catalog Rate Table.
|
|
116
|
+
* This may mean waiting a significant amount of time before it is finalized.
|
|
117
|
+
* Only use this if you can handle waiting for that amount of time.
|
|
118
|
+
*/
|
|
119
|
+
getOfferCatalogRateTableBlocking(leadUuid: string, lead: LeadCreateData): Promise<OfferCatalogRateTable>;
|
|
92
120
|
}
|
package/dist/leads.js
CHANGED
|
@@ -9,7 +9,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
};
|
|
10
10
|
import { Client } from "./client.js";
|
|
11
11
|
import { AsyncRateTable } from "./rate_tables.js";
|
|
12
|
-
import {
|
|
12
|
+
import { AsyncOfferCatalogRateTable, } from "./offer_catalog_rate_tables.js";
|
|
13
|
+
import { leadUuidDecoder, rateTableDecoder, offerCatalogRateTableDecoder, } from "./decoders.js";
|
|
13
14
|
/**
|
|
14
15
|
* The class used to manage leads.
|
|
15
16
|
* Both blocking and non-blocking methods are provided, depending on when you want to resolve the returned Rate Table.
|
|
@@ -143,4 +144,47 @@ export class Leads {
|
|
|
143
144
|
return rateTable;
|
|
144
145
|
});
|
|
145
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* Create a new offer catalog rate table for a lead.
|
|
149
|
+
*
|
|
150
|
+
* @param leadUuid - The UUID of the lead for which to create the rate table.
|
|
151
|
+
* @param lead - An object representing the lead information.
|
|
152
|
+
*
|
|
153
|
+
* @returns AsyncOfferCatalogRateTable
|
|
154
|
+
*
|
|
155
|
+
* @remarks
|
|
156
|
+
* This method returns an AsyncOfferCatalogRateTable.
|
|
157
|
+
* Use the resolve method on the returned object to retrieve the final Offer Catalog Rate Table.
|
|
158
|
+
*/
|
|
159
|
+
getOfferCatalogRateTable(leadUuid, lead) {
|
|
160
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
161
|
+
const resp = yield this.client.post(`leads/${leadUuid}/offerCatalogRateTables`, lead);
|
|
162
|
+
const rateTable = offerCatalogRateTableDecoder.runWithException(resp);
|
|
163
|
+
const asyncRateTable = new AsyncOfferCatalogRateTable({
|
|
164
|
+
rateTable,
|
|
165
|
+
client: this.client,
|
|
166
|
+
});
|
|
167
|
+
return asyncRateTable;
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Create a new offer catalog rate table, immediately resolving it.
|
|
172
|
+
*
|
|
173
|
+
* @param leadUuid - The UUID of the lead for which to create the rate table.
|
|
174
|
+
* @param lead - An object representing the lead information.
|
|
175
|
+
*
|
|
176
|
+
* @returns OfferCatalogRateTable
|
|
177
|
+
*
|
|
178
|
+
* @remarks
|
|
179
|
+
* This method will immediately resolve the final Offer Catalog Rate Table.
|
|
180
|
+
* This may mean waiting a significant amount of time before it is finalized.
|
|
181
|
+
* Only use this if you can handle waiting for that amount of time.
|
|
182
|
+
*/
|
|
183
|
+
getOfferCatalogRateTableBlocking(leadUuid, lead) {
|
|
184
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
185
|
+
const resp = yield this.client.post(`blocking/leads/${leadUuid}/offerCatalogRateTables`, lead);
|
|
186
|
+
const rateTable = offerCatalogRateTableDecoder.runWithException(resp);
|
|
187
|
+
return rateTable;
|
|
188
|
+
});
|
|
189
|
+
}
|
|
146
190
|
}
|
package/dist/leads.test.js
CHANGED
|
@@ -12,6 +12,7 @@ import { Leads } from "./leads";
|
|
|
12
12
|
import { setupServer } from "msw/node";
|
|
13
13
|
import { http, HttpResponse } from "msw";
|
|
14
14
|
import { AsyncRateTable } from "./rate_tables";
|
|
15
|
+
import { AsyncOfferCatalogRateTable } from "./offer_catalog_rate_tables";
|
|
15
16
|
const testLeadData = {
|
|
16
17
|
personalInformation: {
|
|
17
18
|
firstName: "testFirst",
|
|
@@ -176,6 +177,56 @@ const pendingRateTable = {
|
|
|
176
177
|
},
|
|
177
178
|
],
|
|
178
179
|
};
|
|
180
|
+
const fullOfferCatalogRateTable = {
|
|
181
|
+
uuid: "full-offer-catalog-rate-table-uuid",
|
|
182
|
+
leadUuid: "full-lead-uuid",
|
|
183
|
+
offers: [
|
|
184
|
+
{
|
|
185
|
+
uuid: "full-special-offer-uuid",
|
|
186
|
+
productType: "CreditBuilder",
|
|
187
|
+
headline: "Mock Credit Builder Offer",
|
|
188
|
+
descriptionPoints: ["Description of the credit builder offer"],
|
|
189
|
+
url: "https://offers.engine.tech/ref/d6888b75-5454-498c-8710-e4c22daaf64e",
|
|
190
|
+
financialInstitutionDisplayName: "Engine Demo Loans Demand Sub Account 1",
|
|
191
|
+
financialInstitutionImageUrl: "https://s3.amazonaws.com/images.evenfinancial.com/logos/dev/engine_demo_loans_demand_sub_account-wjksncio.svg",
|
|
192
|
+
recommendationScore: 10,
|
|
193
|
+
financialInstitutionUuid: "financial-institution-uuid",
|
|
194
|
+
},
|
|
195
|
+
],
|
|
196
|
+
pendingResponses: [],
|
|
197
|
+
};
|
|
198
|
+
const pendingOfferCatalogRateTable = {
|
|
199
|
+
uuid: "pending-offer-catalog-rate-table-uuid",
|
|
200
|
+
leadUuid: "pending-lead-uuid",
|
|
201
|
+
offers: [
|
|
202
|
+
{
|
|
203
|
+
uuid: "pending-special-offer-uuid",
|
|
204
|
+
productType: "DebtSettlement",
|
|
205
|
+
headline: "Mock Debt Settlement Offer",
|
|
206
|
+
descriptionPoints: ["Description of the debt settlement offer"],
|
|
207
|
+
url: "https://offers.engine.tech/ref/567beb02-9230-4bad-b60a-b2311bad400b",
|
|
208
|
+
financialInstitutionDisplayName: "Engine Demo Loans Demand Sub Account 2",
|
|
209
|
+
financialInstitutionImageUrl: "https://s3.amazonaws.com/images.evenfinancial.com/logos/dev/engine_demo_loans_demand_sub_account_2-wjkurpux.svg",
|
|
210
|
+
recommendationScore: 10,
|
|
211
|
+
financialInstitutionUuid: "financial-institution-uuid",
|
|
212
|
+
},
|
|
213
|
+
],
|
|
214
|
+
pendingResponses: [
|
|
215
|
+
{
|
|
216
|
+
partner: {
|
|
217
|
+
uuid: "partner-uuid",
|
|
218
|
+
name: "partner",
|
|
219
|
+
description: "partner-description",
|
|
220
|
+
disclaimer: "partner-disclaimer",
|
|
221
|
+
supportsPreSelect: false,
|
|
222
|
+
shouldDisplayPreSelect: false,
|
|
223
|
+
supportsPersonalizedOffers: false,
|
|
224
|
+
imageUrl: "partner-image",
|
|
225
|
+
},
|
|
226
|
+
productTypes: ["other"],
|
|
227
|
+
},
|
|
228
|
+
],
|
|
229
|
+
};
|
|
179
230
|
const doublePollUuid = "double-poll-uuid";
|
|
180
231
|
const testHost = "https://engine.com";
|
|
181
232
|
const token = "good_auth_token";
|
|
@@ -184,6 +235,8 @@ const patchLeadUuidResponse = "patch-lead-uuid-response";
|
|
|
184
235
|
const createLeadUuidResponse = "create-lead-uuid-response";
|
|
185
236
|
const getRateTableLeadUuid = "get-rate-table-lead-uuid";
|
|
186
237
|
const getRateTableLeadUuidBlocking = "get-rate-table-lead-uuid-blocking";
|
|
238
|
+
const getOfferCatalogRateTableLeadUuid = "get-offer-catalog-rate-table-lead-uuid";
|
|
239
|
+
const getOfferCatalogRateTableLeadUuidBlocking = "get-offer-catalog-rate-table-lead-uuid-blocking";
|
|
187
240
|
const handlers = [
|
|
188
241
|
http.post(`${testHost}/blocking/leads/rateTables`, () => {
|
|
189
242
|
return new HttpResponse(JSON.stringify(fullRateTable), { status: 200 });
|
|
@@ -223,6 +276,22 @@ const handlers = [
|
|
|
223
276
|
status: 200,
|
|
224
277
|
});
|
|
225
278
|
}),
|
|
279
|
+
// Offer Catalog endpoints
|
|
280
|
+
http.post(`${testHost}/leads/${getOfferCatalogRateTableLeadUuid}/offerCatalogRateTables`, () => {
|
|
281
|
+
return new HttpResponse(JSON.stringify(pendingOfferCatalogRateTable), {
|
|
282
|
+
status: 200,
|
|
283
|
+
});
|
|
284
|
+
}),
|
|
285
|
+
http.post(`${testHost}/blocking/leads/${getOfferCatalogRateTableLeadUuidBlocking}/offerCatalogRateTables`, () => {
|
|
286
|
+
return new HttpResponse(JSON.stringify(fullOfferCatalogRateTable), {
|
|
287
|
+
status: 200,
|
|
288
|
+
});
|
|
289
|
+
}),
|
|
290
|
+
http.get(`${testHost}/originator/offerCatalogRateTables/${pendingOfferCatalogRateTable.uuid}`, () => {
|
|
291
|
+
return new HttpResponse(JSON.stringify(fullOfferCatalogRateTable), {
|
|
292
|
+
status: 200,
|
|
293
|
+
});
|
|
294
|
+
}),
|
|
226
295
|
];
|
|
227
296
|
const server = setupServer(...handlers);
|
|
228
297
|
describe("Leads", () => {
|
|
@@ -269,4 +338,16 @@ describe("Leads", () => {
|
|
|
269
338
|
const resp = yield leads.getRateTableBlocking(getRateTableLeadUuidBlocking, testLeadData);
|
|
270
339
|
expect(resp).toEqual(fullRateTable);
|
|
271
340
|
}));
|
|
341
|
+
test("Get offer catalog rate table endpoint returns an async rate table", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
342
|
+
const leads = new Leads(testHost, token);
|
|
343
|
+
const resp = yield leads.getOfferCatalogRateTable(getOfferCatalogRateTableLeadUuid, testLeadData);
|
|
344
|
+
expect(resp).toBeInstanceOf(AsyncOfferCatalogRateTable);
|
|
345
|
+
const rateTable = yield resp.resolve();
|
|
346
|
+
expect(rateTable).toEqual(fullOfferCatalogRateTable);
|
|
347
|
+
}));
|
|
348
|
+
test("Get offer catalog rate table blocking endpoint returns a resolved rate table", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
349
|
+
const leads = new Leads(testHost, token);
|
|
350
|
+
const resp = yield leads.getOfferCatalogRateTableBlocking(getOfferCatalogRateTableLeadUuidBlocking, testLeadData);
|
|
351
|
+
expect(resp).toEqual(fullOfferCatalogRateTable);
|
|
352
|
+
}));
|
|
272
353
|
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Client } from "./client.js";
|
|
2
|
+
import { type FixedOfferCatalogRateTable } from "./decoders.js";
|
|
3
|
+
export type OfferCatalogRateTable = FixedOfferCatalogRateTable;
|
|
4
|
+
export interface AsyncOfferCatalogRateTableCtorArgs {
|
|
5
|
+
rateTable?: OfferCatalogRateTable;
|
|
6
|
+
client?: Client;
|
|
7
|
+
uuid?: string;
|
|
8
|
+
host?: string;
|
|
9
|
+
auth_token?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* The class used to fetch offer catalog rate tables from the Engine API.
|
|
13
|
+
* This class will handle polling the offer catalog rate tables endpoint until the pending responses are fulfilled.
|
|
14
|
+
*/
|
|
15
|
+
export declare class AsyncOfferCatalogRateTable {
|
|
16
|
+
private rateTable?;
|
|
17
|
+
private client;
|
|
18
|
+
private uuid?;
|
|
19
|
+
/**
|
|
20
|
+
* Construct a new AsyncOfferCatalogRateTable instance.
|
|
21
|
+
* @param rateTable - An existing Offer Catalog Rate Table structure to start from.
|
|
22
|
+
* @param uuid - A UUID of an existing Offer Catalog Rate Table to fetch.
|
|
23
|
+
* @param client - A Client instance to use for fetching the data.
|
|
24
|
+
* @param host - A host used to construct a Client instance if none is provided.
|
|
25
|
+
* @param auth_token - An authentication token used to construct a Client instance if none is provided.
|
|
26
|
+
*
|
|
27
|
+
* @remarks
|
|
28
|
+
* Either one of rateTable or uuid is required, but providing both is an error.
|
|
29
|
+
* Similarly, provide either a pre-constructed Client instance or the host and auth_token parameters to construct one but not both.
|
|
30
|
+
*/
|
|
31
|
+
constructor({ rateTable, uuid, client, host, auth_token, }: AsyncOfferCatalogRateTableCtorArgs);
|
|
32
|
+
/**
|
|
33
|
+
* Resolve the async Offer Catalog Rate Table into a real Offer Catalog Rate Table.
|
|
34
|
+
*
|
|
35
|
+
* @returns An Offer Catalog Rate Table with no pending responses.
|
|
36
|
+
*
|
|
37
|
+
* @remarks
|
|
38
|
+
*
|
|
39
|
+
* This will only make a network request if one is required.
|
|
40
|
+
* A network request is required if only a UUID was provided to the constructor, or if the provided Rate Table has pending responses.
|
|
41
|
+
* This method will also poll the API every second until the returned Rate Table has no more pending responses.
|
|
42
|
+
*/
|
|
43
|
+
resolve(): Promise<OfferCatalogRateTable>;
|
|
44
|
+
private getRateTable;
|
|
45
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { Client } from "./client.js";
|
|
11
|
+
import { offerCatalogRateTableDecoder, } from "./decoders.js";
|
|
12
|
+
const pollInterval = 1000; // 1 second
|
|
13
|
+
/**
|
|
14
|
+
* The class used to fetch offer catalog rate tables from the Engine API.
|
|
15
|
+
* This class will handle polling the offer catalog rate tables endpoint until the pending responses are fulfilled.
|
|
16
|
+
*/
|
|
17
|
+
export class AsyncOfferCatalogRateTable {
|
|
18
|
+
/**
|
|
19
|
+
* Construct a new AsyncOfferCatalogRateTable instance.
|
|
20
|
+
* @param rateTable - An existing Offer Catalog Rate Table structure to start from.
|
|
21
|
+
* @param uuid - A UUID of an existing Offer Catalog Rate Table to fetch.
|
|
22
|
+
* @param client - A Client instance to use for fetching the data.
|
|
23
|
+
* @param host - A host used to construct a Client instance if none is provided.
|
|
24
|
+
* @param auth_token - An authentication token used to construct a Client instance if none is provided.
|
|
25
|
+
*
|
|
26
|
+
* @remarks
|
|
27
|
+
* Either one of rateTable or uuid is required, but providing both is an error.
|
|
28
|
+
* Similarly, provide either a pre-constructed Client instance or the host and auth_token parameters to construct one but not both.
|
|
29
|
+
*/
|
|
30
|
+
constructor({ rateTable, uuid, client, host, auth_token, }) {
|
|
31
|
+
if (client && host && auth_token) {
|
|
32
|
+
throw new TypeError("Client cannot be provided at the same time as a host and auth_token.");
|
|
33
|
+
}
|
|
34
|
+
if (rateTable && uuid) {
|
|
35
|
+
throw new TypeError("Rate table and uuid cannot be provided at the same time.");
|
|
36
|
+
}
|
|
37
|
+
if (client) {
|
|
38
|
+
this.client = client;
|
|
39
|
+
}
|
|
40
|
+
else if (host && auth_token) {
|
|
41
|
+
this.client = new Client(host, auth_token);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
throw new TypeError("Either client must be provided or host and auth_token must be provided.");
|
|
45
|
+
}
|
|
46
|
+
this.rateTable = rateTable;
|
|
47
|
+
this.uuid = uuid;
|
|
48
|
+
if (!this.rateTable && !this.uuid) {
|
|
49
|
+
throw new TypeError("Either a rate table or a uuid must be provided.");
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Resolve the async Offer Catalog Rate Table into a real Offer Catalog Rate Table.
|
|
54
|
+
*
|
|
55
|
+
* @returns An Offer Catalog Rate Table with no pending responses.
|
|
56
|
+
*
|
|
57
|
+
* @remarks
|
|
58
|
+
*
|
|
59
|
+
* This will only make a network request if one is required.
|
|
60
|
+
* A network request is required if only a UUID was provided to the constructor, or if the provided Rate Table has pending responses.
|
|
61
|
+
* This method will also poll the API every second until the returned Rate Table has no more pending responses.
|
|
62
|
+
*/
|
|
63
|
+
resolve() {
|
|
64
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
65
|
+
if (this.rateTable &&
|
|
66
|
+
this.rateTable.pendingResponses &&
|
|
67
|
+
this.rateTable.pendingResponses.length > 0) {
|
|
68
|
+
const resolved = yield this.getRateTable(this.rateTable.uuid);
|
|
69
|
+
return new Promise((resolve) => {
|
|
70
|
+
setTimeout(() => {
|
|
71
|
+
resolve(resolved.resolve());
|
|
72
|
+
}, pollInterval);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
if (this.uuid) {
|
|
76
|
+
const resolved = yield this.getRateTable(this.uuid);
|
|
77
|
+
return resolved.resolve();
|
|
78
|
+
}
|
|
79
|
+
if (this.rateTable) {
|
|
80
|
+
return this.rateTable;
|
|
81
|
+
}
|
|
82
|
+
throw new TypeError("No uuid or rate table was provided to resolve");
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
getRateTable(uuid) {
|
|
86
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
87
|
+
const endpoint = `originator/offerCatalogRateTables/${uuid}`;
|
|
88
|
+
const resp = yield this.client.get(endpoint);
|
|
89
|
+
const rateTable = offerCatalogRateTableDecoder.runWithException(resp);
|
|
90
|
+
return new AsyncOfferCatalogRateTable({
|
|
91
|
+
rateTable,
|
|
92
|
+
client: this.client,
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { describe, expect, test, beforeAll, afterEach, afterAll, } from "@jest/globals";
|
|
11
|
+
import { Client } from "./client";
|
|
12
|
+
import { setupServer } from "msw/node";
|
|
13
|
+
import { http, HttpResponse } from "msw";
|
|
14
|
+
import { AsyncOfferCatalogRateTable, } from "./offer_catalog_rate_tables";
|
|
15
|
+
const fullRateTable = {
|
|
16
|
+
uuid: "full-offer-catalog-rate-table-uuid",
|
|
17
|
+
leadUuid: "full-lead-uuid",
|
|
18
|
+
offers: [
|
|
19
|
+
{
|
|
20
|
+
uuid: "full-special-offer-uuid",
|
|
21
|
+
productType: "CreditBuilder",
|
|
22
|
+
headline: "Mock Credit Builder Offer",
|
|
23
|
+
descriptionPoints: ["Description of the credit builder offer"],
|
|
24
|
+
url: "https://offers.engine.tech/ref/d6888b75-5454-498c-8710-e4c22daaf64e",
|
|
25
|
+
financialInstitutionDisplayName: "Engine Demo Loans Demand Sub Account 1",
|
|
26
|
+
financialInstitutionImageUrl: "https://s3.amazonaws.com/images.evenfinancial.com/logos/dev/engine_demo_loans_demand_sub_account-wjksncio.svg",
|
|
27
|
+
recommendationScore: 10,
|
|
28
|
+
financialInstitutionUuid: "financial-institution-uuid",
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
uuid: "full-special-offer-uuid-2",
|
|
32
|
+
productType: "DebtSettlement",
|
|
33
|
+
headline: "Mock Debt Relief Offer",
|
|
34
|
+
descriptionPoints: ["Description of the debt relief offer"],
|
|
35
|
+
url: "https://offers.engine.tech/ref/567beb02-9230-4bad-b60a-b2311bad400b",
|
|
36
|
+
financialInstitutionDisplayName: "Engine Demo Loans Demand Sub Account 2",
|
|
37
|
+
financialInstitutionImageUrl: "https://s3.amazonaws.com/images.evenfinancial.com/logos/dev/engine_demo_loans_demand_sub_account_2-wjkurpux.svg",
|
|
38
|
+
recommendationScore: 10,
|
|
39
|
+
financialInstitutionUuid: "financial-institution-uuid",
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
uuid: "full-special-offer-uuid-3",
|
|
43
|
+
productType: "InstallmentLoans",
|
|
44
|
+
headline: "Mock Installment Loans Offer",
|
|
45
|
+
descriptionPoints: ["Description of the installment loans offer"],
|
|
46
|
+
url: "https://offers.engine.tech/ref/ec88908d-bdee-4b5d-8c49-2268167ea3c5",
|
|
47
|
+
financialInstitutionDisplayName: "Engine Demo Loans Demand Sub Account 3",
|
|
48
|
+
financialInstitutionImageUrl: "https://s3.amazonaws.com/images.evenfinancial.com/logos/dev/engine_demo_loans_demand_sub_account_3-wjkuspug.svg",
|
|
49
|
+
recommendationScore: 10,
|
|
50
|
+
financialInstitutionUuid: "financial-institution-uuid",
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
pendingResponses: [],
|
|
54
|
+
};
|
|
55
|
+
const pendingRateTable = {
|
|
56
|
+
uuid: "pending-offer-catalog-rate-table-uuid",
|
|
57
|
+
leadUuid: "pending-lead-uuid",
|
|
58
|
+
offers: [
|
|
59
|
+
{
|
|
60
|
+
uuid: "pending-special-offer-uuid",
|
|
61
|
+
productType: "CreditBuilder",
|
|
62
|
+
headline: "Mock Credit Builder Offer",
|
|
63
|
+
descriptionPoints: ["Description of the credit builder offer"],
|
|
64
|
+
url: "https://offers.engine.tech/ref/d6888b75-5454-498c-8710-e4c22daaf64e",
|
|
65
|
+
financialInstitutionDisplayName: "Engine Demo Loans Demand Sub Account 1",
|
|
66
|
+
financialInstitutionImageUrl: "https://s3.amazonaws.com/images.evenfinancial.com/logos/dev/engine_demo_loans_demand_sub_account-wjksncio.svg",
|
|
67
|
+
recommendationScore: 10,
|
|
68
|
+
financialInstitutionUuid: "financial-institution-uuid",
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
uuid: "pending-special-offer-uuid-2",
|
|
72
|
+
productType: "DebtSettlement",
|
|
73
|
+
headline: "Mock Debt Relief Offer",
|
|
74
|
+
descriptionPoints: ["Description of the debt relief offer"],
|
|
75
|
+
url: "https://offers.engine.tech/ref/567beb02-9230-4bad-b60a-b2311bad400b",
|
|
76
|
+
financialInstitutionDisplayName: "Engine Demo Loans Demand Sub Account 2",
|
|
77
|
+
financialInstitutionImageUrl: "https://s3.amazonaws.com/images.evenfinancial.com/logos/dev/engine_demo_loans_demand_sub_account_2-wjkurpux.svg",
|
|
78
|
+
recommendationScore: 10,
|
|
79
|
+
financialInstitutionUuid: "financial-institution-uuid",
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
uuid: "pending-special-offer-uuid-3",
|
|
83
|
+
productType: "InstallmentLoans",
|
|
84
|
+
headline: "Mock Installment Loans Offer",
|
|
85
|
+
descriptionPoints: ["Description of the installment loans offer"],
|
|
86
|
+
url: "https://offers.engine.tech/ref/ec88908d-bdee-4b5d-8c49-2268167ea3c5",
|
|
87
|
+
financialInstitutionDisplayName: "Engine Demo Loans Demand Sub Account 3",
|
|
88
|
+
financialInstitutionImageUrl: "https://s3.amazonaws.com/images.evenfinancial.com/logos/dev/engine_demo_loans_demand_sub_account_3-wjkuspug.svg",
|
|
89
|
+
recommendationScore: 10,
|
|
90
|
+
financialInstitutionUuid: "financial-institution-uuid",
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
pendingResponses: [
|
|
94
|
+
{
|
|
95
|
+
partner: {
|
|
96
|
+
uuid: "partner-uuid",
|
|
97
|
+
name: "partner",
|
|
98
|
+
description: "partner-description",
|
|
99
|
+
disclaimer: "partner-disclaimer",
|
|
100
|
+
supportsPreSelect: false,
|
|
101
|
+
shouldDisplayPreSelect: false,
|
|
102
|
+
supportsPersonalizedOffers: false,
|
|
103
|
+
imageUrl: "partner-image",
|
|
104
|
+
},
|
|
105
|
+
productTypes: ["credit_card"],
|
|
106
|
+
},
|
|
107
|
+
],
|
|
108
|
+
};
|
|
109
|
+
const doublePollUuid = "double-poll-uuid";
|
|
110
|
+
const testHost = "https://engine.com";
|
|
111
|
+
const token = "good_auth_token";
|
|
112
|
+
const handlers = [
|
|
113
|
+
http.get(`${testHost}/originator/offerCatalogRateTables/${pendingRateTable.uuid}`, () => {
|
|
114
|
+
return new HttpResponse(JSON.stringify(fullRateTable), {
|
|
115
|
+
status: 200,
|
|
116
|
+
});
|
|
117
|
+
}),
|
|
118
|
+
http.get(`${testHost}/originator/offerCatalogRateTables/${doublePollUuid}`, () => {
|
|
119
|
+
return new HttpResponse(JSON.stringify(pendingRateTable), {
|
|
120
|
+
status: 200,
|
|
121
|
+
});
|
|
122
|
+
}),
|
|
123
|
+
];
|
|
124
|
+
const server = setupServer(...handlers);
|
|
125
|
+
describe("OfferCatalogRateTables", () => {
|
|
126
|
+
beforeAll(() => server.listen({
|
|
127
|
+
onUnhandledRequest: "error",
|
|
128
|
+
}));
|
|
129
|
+
afterEach(() => server.resetHandlers());
|
|
130
|
+
afterAll(() => server.close());
|
|
131
|
+
test("Either client or host and auth_token must be provided to constructor", () => {
|
|
132
|
+
expect(() => new AsyncOfferCatalogRateTable({
|
|
133
|
+
host: testHost,
|
|
134
|
+
auth_token: token,
|
|
135
|
+
uuid: "test-uuid",
|
|
136
|
+
})).not.toThrowError();
|
|
137
|
+
expect(() => new AsyncOfferCatalogRateTable({
|
|
138
|
+
client: new Client(testHost, token),
|
|
139
|
+
uuid: "test-uuid",
|
|
140
|
+
})).not.toThrowError();
|
|
141
|
+
expect(() => new AsyncOfferCatalogRateTable({ uuid: "test-uuid" })).toThrowError(TypeError);
|
|
142
|
+
});
|
|
143
|
+
test("Either a rate table or a uuid must be provided to constructor", () => {
|
|
144
|
+
expect(() => new AsyncOfferCatalogRateTable({
|
|
145
|
+
host: testHost,
|
|
146
|
+
auth_token: token,
|
|
147
|
+
})).toThrowError(TypeError);
|
|
148
|
+
expect(() => new AsyncOfferCatalogRateTable({
|
|
149
|
+
host: testHost,
|
|
150
|
+
auth_token: token,
|
|
151
|
+
uuid: "test-uuid",
|
|
152
|
+
})).not.toThrowError();
|
|
153
|
+
expect(() => new AsyncOfferCatalogRateTable({
|
|
154
|
+
host: testHost,
|
|
155
|
+
auth_token: token,
|
|
156
|
+
rateTable: fullRateTable,
|
|
157
|
+
})).not.toThrowError();
|
|
158
|
+
});
|
|
159
|
+
test("Providing both a rate table and a uuid to constructor is an error", () => {
|
|
160
|
+
expect(() => new AsyncOfferCatalogRateTable({
|
|
161
|
+
host: testHost,
|
|
162
|
+
auth_token: token,
|
|
163
|
+
rateTable: fullRateTable,
|
|
164
|
+
uuid: "test-uuid",
|
|
165
|
+
})).toThrowError(TypeError);
|
|
166
|
+
});
|
|
167
|
+
test("Providing both a client instance and a host/auth_token pair is an error", () => {
|
|
168
|
+
expect(() => new AsyncOfferCatalogRateTable({
|
|
169
|
+
client: new Client(testHost, token),
|
|
170
|
+
host: testHost,
|
|
171
|
+
auth_token: token,
|
|
172
|
+
uuid: "test-uuid",
|
|
173
|
+
})).toThrowError(TypeError);
|
|
174
|
+
});
|
|
175
|
+
test("Providing a UUID resolves the rate table.", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
176
|
+
const rateTables = new AsyncOfferCatalogRateTable({
|
|
177
|
+
host: testHost,
|
|
178
|
+
auth_token: token,
|
|
179
|
+
uuid: doublePollUuid,
|
|
180
|
+
});
|
|
181
|
+
const resp = yield rateTables.resolve();
|
|
182
|
+
expect(resp).toEqual(fullRateTable);
|
|
183
|
+
}));
|
|
184
|
+
test("Providing a rate table with pending responses resolves the rate table.", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
185
|
+
const rateTables = new AsyncOfferCatalogRateTable({
|
|
186
|
+
host: testHost,
|
|
187
|
+
auth_token: token,
|
|
188
|
+
rateTable: pendingRateTable,
|
|
189
|
+
});
|
|
190
|
+
const resp = yield rateTables.resolve();
|
|
191
|
+
expect(resp).toEqual(fullRateTable);
|
|
192
|
+
}));
|
|
193
|
+
test("Providing a rate table with no pending responses resolves the rate table.", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
194
|
+
const rateTables = new AsyncOfferCatalogRateTable({
|
|
195
|
+
host: testHost,
|
|
196
|
+
auth_token: token,
|
|
197
|
+
rateTable: fullRateTable,
|
|
198
|
+
});
|
|
199
|
+
const resp = yield rateTables.resolve();
|
|
200
|
+
expect(resp).toEqual(fullRateTable);
|
|
201
|
+
}));
|
|
202
|
+
test("If there are no pending responses no requests are made when resolving.", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
203
|
+
const rateTables = new AsyncOfferCatalogRateTable({
|
|
204
|
+
host: testHost,
|
|
205
|
+
auth_token: token,
|
|
206
|
+
rateTable: fullRateTable,
|
|
207
|
+
});
|
|
208
|
+
let requestSent = false;
|
|
209
|
+
server.events.on("request:start", () => {
|
|
210
|
+
requestSent = true;
|
|
211
|
+
});
|
|
212
|
+
yield rateTables.resolve();
|
|
213
|
+
expect(requestSent).toBe(false);
|
|
214
|
+
}));
|
|
215
|
+
});
|
package/package.json
CHANGED
|
@@ -1,48 +1,48 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
2
|
+
"name": "@moneylion/engine-api",
|
|
3
|
+
"version": "1.3.6",
|
|
4
|
+
"description": "Interface to engine.tech API",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"lint": "prettier . --check && eslint src/",
|
|
13
|
+
"lint:fix": "prettier . --write && eslint --fix src/",
|
|
14
|
+
"test": "npm run build && NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" jest dist/",
|
|
15
|
+
"generate:schema": "openapi-typescript https://even-api-reference.s3.amazonaws.com/releases/branches/public@1.49.0/latest/open-api/public/spec.json -o src/generated/schema.d.ts"
|
|
16
|
+
},
|
|
17
|
+
"author": "",
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@jest/globals": "^29.7.0",
|
|
21
|
+
"@tsconfig/node18": "^18.2.2",
|
|
22
|
+
"@typescript-eslint/eslint-plugin": "^7.3.1",
|
|
23
|
+
"@typescript-eslint/parser": "^7.3.1",
|
|
24
|
+
"eslint": "^8.57.0",
|
|
25
|
+
"eslint-config-prettier": "^9.1.0",
|
|
26
|
+
"eslint-plugin-tsdoc": "^0.2.17",
|
|
27
|
+
"jest": "^29.7.0",
|
|
28
|
+
"msw": "^2.2.10",
|
|
29
|
+
"openapi-typescript": "^6.7.5",
|
|
30
|
+
"prettier": "3.2.5",
|
|
31
|
+
"ts-jest": "^29.1.2",
|
|
32
|
+
"typescript": "^5.2.2"
|
|
33
|
+
},
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=18"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@mojotech/json-type-validation": "^3.1.0",
|
|
39
|
+
"node-fetch": "^3.3.2"
|
|
40
|
+
},
|
|
41
|
+
"prettier": {
|
|
42
|
+
"quoteProps": "consistent",
|
|
43
|
+
"singleQuote": false,
|
|
44
|
+
"tabWidth": 4,
|
|
45
|
+
"trailingComma": "es5",
|
|
46
|
+
"useTabs": true
|
|
47
|
+
}
|
|
48
48
|
}
|