@keystrokehq/ramp 0.0.1
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 +149 -0
- package/dist/_official/index.d.mts +3 -0
- package/dist/_official/index.mjs +3 -0
- package/dist/_runtime/index.d.mts +1 -0
- package/dist/_runtime/index.mjs +1 -0
- package/dist/accounting-fields.d.mts +116 -0
- package/dist/accounting-fields.mjs +108 -0
- package/dist/accounting-gl-accounts.d.mts +63 -0
- package/dist/accounting-gl-accounts.mjs +49 -0
- package/dist/accounting-i6txC-MI.mjs +116 -0
- package/dist/accounting-subsidiaries.d.mts +59 -0
- package/dist/accounting-subsidiaries.mjs +49 -0
- package/dist/accounting-sync.d.mts +57 -0
- package/dist/accounting-sync.mjs +51 -0
- package/dist/accounting-tracking-categories.d.mts +36 -0
- package/dist/accounting-tracking-categories.mjs +34 -0
- package/dist/accounting-vendors.d.mts +62 -0
- package/dist/accounting-vendors.mjs +49 -0
- package/dist/applications.d.mts +59 -0
- package/dist/applications.mjs +35 -0
- package/dist/bills-B8W9et3K.mjs +86 -0
- package/dist/bills.d.mts +381 -0
- package/dist/bills.mjs +134 -0
- package/dist/business-CztnWUvt.mjs +83 -0
- package/dist/business.d.mts +28 -0
- package/dist/business.mjs +20 -0
- package/dist/card-programs.d.mts +90 -0
- package/dist/card-programs.mjs +50 -0
- package/dist/cards-BfgPcR38.mjs +153 -0
- package/dist/cards.d.mts +287 -0
- package/dist/cards.mjs +166 -0
- package/dist/cashbacks.d.mts +46 -0
- package/dist/cashbacks.mjs +34 -0
- package/dist/client.d.mts +79 -0
- package/dist/client.mjs +248 -0
- package/dist/common-CUpmNYXw.mjs +64 -0
- package/dist/connection.d.mts +2 -0
- package/dist/connection.mjs +3 -0
- package/dist/departments.d.mts +63 -0
- package/dist/departments.mjs +79 -0
- package/dist/entities.d.mts +38 -0
- package/dist/entities.mjs +34 -0
- package/dist/errors-Sm92tugz.d.mts +42 -0
- package/dist/events.d.mts +30 -0
- package/dist/events.mjs +39 -0
- package/dist/factory-Da6YVHdL.mjs +15 -0
- package/dist/index.d.mts +1 -0
- package/dist/index.mjs +1 -0
- package/dist/integration-BGliAxAS.mjs +124 -0
- package/dist/integration-Dd6tOtSy.d.mts +47 -0
- package/dist/limits.d.mts +179 -0
- package/dist/limits.mjs +84 -0
- package/dist/locations.d.mts +69 -0
- package/dist/locations.mjs +80 -0
- package/dist/memos.d.mts +42 -0
- package/dist/memos.mjs +36 -0
- package/dist/messaging.d.mts +1 -0
- package/dist/messaging.mjs +1 -0
- package/dist/new-resources-BBJsJEjT.mjs +77 -0
- package/dist/organization-BSaEpvBn.mjs +103 -0
- package/dist/provider-app-JO525h-N.d.mts +45 -0
- package/dist/purchase-orders.d.mts +75 -0
- package/dist/purchase-orders.mjs +36 -0
- package/dist/receipt-integrations.d.mts +31 -0
- package/dist/receipt-integrations.mjs +38 -0
- package/dist/receipts-STq41c2M.mjs +53 -0
- package/dist/receipts.d.mts +62 -0
- package/dist/receipts.mjs +61 -0
- package/dist/reimbursements-OknrZUMF.mjs +37 -0
- package/dist/reimbursements.d.mts +89 -0
- package/dist/reimbursements.mjs +39 -0
- package/dist/schemas/index.d.mts +887 -0
- package/dist/schemas/index.mjs +56 -0
- package/dist/shared-CTTtP5gv.mjs +24 -0
- package/dist/spend-programs.d.mts +48 -0
- package/dist/spend-programs.mjs +34 -0
- package/dist/spend-requests.d.mts +95 -0
- package/dist/spend-requests.mjs +53 -0
- package/dist/statements.d.mts +41 -0
- package/dist/statements.mjs +37 -0
- package/dist/transactions-Djd0oIGK.mjs +53 -0
- package/dist/transactions.d.mts +181 -0
- package/dist/transactions.mjs +81 -0
- package/dist/transfers-WBQZVdGP.mjs +40 -0
- package/dist/transfers.d.mts +72 -0
- package/dist/transfers.mjs +36 -0
- package/dist/triggers/index.d.mts +81 -0
- package/dist/triggers/index.mjs +199 -0
- package/dist/unified-requests.d.mts +67 -0
- package/dist/unified-requests.mjs +37 -0
- package/dist/users-ufcXrfNK.mjs +74 -0
- package/dist/users.d.mts +204 -0
- package/dist/users.mjs +101 -0
- package/dist/vendor-bank-accounts.d.mts +110 -0
- package/dist/vendor-bank-accounts.mjs +83 -0
- package/dist/vendors-B7DQrlAn.d.mts +1116 -0
- package/dist/vendors-Bx1cgXwC.mjs +83 -0
- package/dist/vendors.d.mts +116 -0
- package/dist/vendors.mjs +84 -0
- package/dist/verification.d.mts +22 -0
- package/dist/verification.mjs +38 -0
- package/dist/webhook-CsbvcgrC.mjs +61 -0
- package/dist/webhook-sde3Ui2N.d.mts +68 -0
- package/dist/webhooks-mgmt-d1xgVpWx.mjs +35 -0
- package/dist/webhooks.d.mts +83 -0
- package/dist/webhooks.mjs +81 -0
- package/package.json +222 -0
package/dist/cards.mjs
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { createRampClient } from "./client.mjs";
|
|
2
|
+
import { _ as rampTerminateCardInputSchema, a as rampCardListSchema, d as rampCardSchema, g as rampSuspendCardInputSchema, h as rampCreateVirtualCardInputSchema, i as rampCardListInputSchema, m as rampCreatePhysicalCardInputSchema, n as rampCardFundTransferSchema, p as rampCardUpdateInputSchema, r as rampCardGetInputSchema, v as rampUnsuspendCardInputSchema } from "./cards-BfgPcR38.mjs";
|
|
3
|
+
import { t as rampDeferredTaskSchema } from "./users-ufcXrfNK.mjs";
|
|
4
|
+
import { t as rampOperation } from "./factory-Da6YVHdL.mjs";
|
|
5
|
+
import { t as omitUndefined } from "./shared-CTTtP5gv.mjs";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
|
|
8
|
+
//#region src/cards.ts
|
|
9
|
+
const listCards = rampOperation({
|
|
10
|
+
id: "list_cards",
|
|
11
|
+
name: "List Cards",
|
|
12
|
+
description: "List Ramp cards, optionally filtered by cardholder, entity, card program, or state.",
|
|
13
|
+
input: rampCardListInputSchema,
|
|
14
|
+
output: rampCardListSchema,
|
|
15
|
+
run: async (input, credentials) => {
|
|
16
|
+
const client = createRampClient(credentials);
|
|
17
|
+
const query = omitUndefined({
|
|
18
|
+
cardholder_id: input.cardholder_id,
|
|
19
|
+
entity_id: input.entity_id,
|
|
20
|
+
state: input.state,
|
|
21
|
+
card_program_id: input.card_program_id,
|
|
22
|
+
page_size: input.pageSize,
|
|
23
|
+
start: input.cursor
|
|
24
|
+
});
|
|
25
|
+
return rampCardListSchema.parse(await client.request("/cards", { query }));
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
const getCard = rampOperation({
|
|
29
|
+
id: "get_card",
|
|
30
|
+
name: "Get Card",
|
|
31
|
+
description: "Fetch a Ramp card by id.",
|
|
32
|
+
input: rampCardGetInputSchema,
|
|
33
|
+
output: rampCardSchema,
|
|
34
|
+
run: async (input, credentials) => {
|
|
35
|
+
const client = createRampClient(credentials);
|
|
36
|
+
return rampCardSchema.parse(await client.request(`/cards/${encodeURIComponent(input.cardId)}`));
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
const createVirtualCard = rampOperation({
|
|
40
|
+
id: "create_virtual_card",
|
|
41
|
+
name: "Create Virtual Card",
|
|
42
|
+
description: "Create a new virtual Ramp card for an existing user.",
|
|
43
|
+
input: rampCreateVirtualCardInputSchema,
|
|
44
|
+
output: rampDeferredTaskSchema,
|
|
45
|
+
needsApproval: true,
|
|
46
|
+
run: async (input, credentials) => {
|
|
47
|
+
const client = createRampClient(credentials);
|
|
48
|
+
const { idempotency_key, ...body } = input;
|
|
49
|
+
return rampDeferredTaskSchema.parse(await client.request("/cards/deferred/virtual", {
|
|
50
|
+
method: "POST",
|
|
51
|
+
body,
|
|
52
|
+
...idempotency_key !== void 0 ? { idempotencyKey: idempotency_key } : {}
|
|
53
|
+
}));
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
const createPhysicalCard = rampOperation({
|
|
57
|
+
id: "create_physical_card",
|
|
58
|
+
name: "Create Physical Card",
|
|
59
|
+
description: "Order a physical Ramp card for an existing user.",
|
|
60
|
+
input: rampCreatePhysicalCardInputSchema,
|
|
61
|
+
output: rampDeferredTaskSchema,
|
|
62
|
+
needsApproval: true,
|
|
63
|
+
run: async (input, credentials) => {
|
|
64
|
+
const client = createRampClient(credentials);
|
|
65
|
+
const { idempotency_key, ...body } = input;
|
|
66
|
+
return rampDeferredTaskSchema.parse(await client.request("/cards/deferred/physical", {
|
|
67
|
+
method: "POST",
|
|
68
|
+
body,
|
|
69
|
+
...idempotency_key !== void 0 ? { idempotencyKey: idempotency_key } : {}
|
|
70
|
+
}));
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
const updateCard = rampOperation({
|
|
74
|
+
id: "update_card",
|
|
75
|
+
name: "Update Card",
|
|
76
|
+
description: "Update display name or spending restrictions on an existing Ramp card.",
|
|
77
|
+
input: rampCardUpdateInputSchema,
|
|
78
|
+
output: rampCardSchema,
|
|
79
|
+
needsApproval: true,
|
|
80
|
+
run: async (input, credentials) => {
|
|
81
|
+
const client = createRampClient(credentials);
|
|
82
|
+
const { cardId, ...body } = input;
|
|
83
|
+
await client.request(`/cards/${encodeURIComponent(cardId)}`, {
|
|
84
|
+
method: "PATCH",
|
|
85
|
+
body: omitUndefined(body)
|
|
86
|
+
});
|
|
87
|
+
return rampCardSchema.parse(await client.request(`/cards/${encodeURIComponent(cardId)}`));
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
const suspendCard = rampOperation({
|
|
91
|
+
id: "suspend_card",
|
|
92
|
+
name: "Suspend Card",
|
|
93
|
+
description: "Temporarily suspend a Ramp card. Issues a deferred task.",
|
|
94
|
+
input: rampSuspendCardInputSchema,
|
|
95
|
+
output: rampDeferredTaskSchema,
|
|
96
|
+
needsApproval: true,
|
|
97
|
+
run: async (input, credentials) => {
|
|
98
|
+
const client = createRampClient(credentials);
|
|
99
|
+
return rampDeferredTaskSchema.parse(await client.request(`/cards/${encodeURIComponent(input.cardId)}/deferred/suspension`, {
|
|
100
|
+
method: "POST",
|
|
101
|
+
body: {}
|
|
102
|
+
}));
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
const unsuspendCard = rampOperation({
|
|
106
|
+
id: "unsuspend_card",
|
|
107
|
+
name: "Unsuspend Card",
|
|
108
|
+
description: "Restore a previously suspended Ramp card. Issues a deferred task.",
|
|
109
|
+
input: rampUnsuspendCardInputSchema,
|
|
110
|
+
output: rampDeferredTaskSchema,
|
|
111
|
+
needsApproval: true,
|
|
112
|
+
run: async (input, credentials) => {
|
|
113
|
+
const client = createRampClient(credentials);
|
|
114
|
+
return rampDeferredTaskSchema.parse(await client.request(`/cards/${encodeURIComponent(input.cardId)}/deferred/unsuspension`, {
|
|
115
|
+
method: "POST",
|
|
116
|
+
body: {}
|
|
117
|
+
}));
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
const terminateCard = rampOperation({
|
|
121
|
+
id: "terminate_card",
|
|
122
|
+
name: "Terminate Card",
|
|
123
|
+
description: "Permanently terminate a Ramp card. This is irreversible.",
|
|
124
|
+
input: rampTerminateCardInputSchema,
|
|
125
|
+
output: rampDeferredTaskSchema,
|
|
126
|
+
needsApproval: true,
|
|
127
|
+
run: async (input, credentials) => {
|
|
128
|
+
const client = createRampClient(credentials);
|
|
129
|
+
const { cardId, idempotency_key } = input;
|
|
130
|
+
return rampDeferredTaskSchema.parse(await client.request(`/cards/${encodeURIComponent(cardId)}/deferred/termination`, {
|
|
131
|
+
method: "POST",
|
|
132
|
+
body: {},
|
|
133
|
+
...idempotency_key !== void 0 ? { idempotencyKey: idempotency_key } : {}
|
|
134
|
+
}));
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
const listCardFundTransfers = rampOperation({
|
|
138
|
+
id: "list_card_fund_transfers",
|
|
139
|
+
name: "List Card Fund Transfers",
|
|
140
|
+
description: "List fund transfers associated with Ramp cards.",
|
|
141
|
+
input: z.object({
|
|
142
|
+
card_id: z.string().trim().min(1).optional(),
|
|
143
|
+
pageSize: z.number().int().positive().max(100).optional(),
|
|
144
|
+
cursor: z.string().trim().min(1).optional()
|
|
145
|
+
}),
|
|
146
|
+
output: z.object({
|
|
147
|
+
data: z.array(rampCardFundTransferSchema),
|
|
148
|
+
page: z.object({ next: z.string().optional() }).optional()
|
|
149
|
+
}),
|
|
150
|
+
run: async (input, credentials) => {
|
|
151
|
+
const client = createRampClient(credentials);
|
|
152
|
+
const query = omitUndefined({
|
|
153
|
+
card_id: input.card_id,
|
|
154
|
+
page_size: input.pageSize,
|
|
155
|
+
start: input.cursor
|
|
156
|
+
});
|
|
157
|
+
const response = await client.request("/card-fund-transfers", { query });
|
|
158
|
+
return {
|
|
159
|
+
data: (response?.data ?? []).map((item) => rampCardFundTransferSchema.parse(item)),
|
|
160
|
+
...response?.page !== void 0 ? { page: response.page } : {}
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
//#endregion
|
|
166
|
+
export { createPhysicalCard, createVirtualCard, getCard, listCardFundTransfers, listCards, suspendCard, terminateCard, unsuspendCard, updateCard };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import * as zod from "zod";
|
|
2
|
+
import * as _keystrokehq_core0 from "@keystrokehq/core";
|
|
3
|
+
import * as _keystrokehq_core_credential_set0 from "@keystrokehq/core/credential-set";
|
|
4
|
+
import * as zod_v4_core0 from "zod/v4/core";
|
|
5
|
+
|
|
6
|
+
//#region src/cashbacks.d.ts
|
|
7
|
+
declare const listCashbacks: _keystrokehq_core0.Operation<zod.ZodObject<{
|
|
8
|
+
pageSize: zod.ZodOptional<zod.ZodNumber>;
|
|
9
|
+
cursor: zod.ZodOptional<zod.ZodString>;
|
|
10
|
+
}, zod_v4_core0.$strip>, zod.ZodObject<{
|
|
11
|
+
data: zod.ZodArray<zod.ZodObject<{
|
|
12
|
+
id: zod.ZodString;
|
|
13
|
+
amount: zod.ZodOptional<zod.ZodObject<{
|
|
14
|
+
amountCents: zod.ZodNumber;
|
|
15
|
+
currency: zod.ZodString;
|
|
16
|
+
}, zod_v4_core0.$strip>>;
|
|
17
|
+
status: zod.ZodOptional<zod.ZodString>;
|
|
18
|
+
granted_date: zod.ZodOptional<zod.ZodString>;
|
|
19
|
+
entity_id: zod.ZodOptional<zod.ZodString>;
|
|
20
|
+
}, zod_v4_core0.$loose>>;
|
|
21
|
+
page: zod.ZodOptional<zod.ZodObject<{
|
|
22
|
+
next: zod.ZodOptional<zod.ZodString>;
|
|
23
|
+
}, zod_v4_core0.$strip>>;
|
|
24
|
+
}, zod_v4_core0.$strip>, readonly [_keystrokehq_core0.CredentialSet<"ramp", zod.ZodObject<{
|
|
25
|
+
RAMP_ACCESS_TOKEN: zod.ZodString;
|
|
26
|
+
}, zod_v4_core0.$strip>, readonly _keystrokehq_core_credential_set0.CredentialConnection<zod.ZodObject<{
|
|
27
|
+
RAMP_ACCESS_TOKEN: zod.ZodString;
|
|
28
|
+
}, zod_v4_core0.$strip>>[] | undefined>], undefined>;
|
|
29
|
+
declare const getCashback: _keystrokehq_core0.Operation<zod.ZodObject<{
|
|
30
|
+
cashbackId: zod.ZodString;
|
|
31
|
+
}, zod_v4_core0.$strip>, zod.ZodObject<{
|
|
32
|
+
id: zod.ZodString;
|
|
33
|
+
amount: zod.ZodOptional<zod.ZodObject<{
|
|
34
|
+
amountCents: zod.ZodNumber;
|
|
35
|
+
currency: zod.ZodString;
|
|
36
|
+
}, zod_v4_core0.$strip>>;
|
|
37
|
+
status: zod.ZodOptional<zod.ZodString>;
|
|
38
|
+
granted_date: zod.ZodOptional<zod.ZodString>;
|
|
39
|
+
entity_id: zod.ZodOptional<zod.ZodString>;
|
|
40
|
+
}, zod_v4_core0.$loose>, readonly [_keystrokehq_core0.CredentialSet<"ramp", zod.ZodObject<{
|
|
41
|
+
RAMP_ACCESS_TOKEN: zod.ZodString;
|
|
42
|
+
}, zod_v4_core0.$strip>, readonly _keystrokehq_core_credential_set0.CredentialConnection<zod.ZodObject<{
|
|
43
|
+
RAMP_ACCESS_TOKEN: zod.ZodString;
|
|
44
|
+
}, zod_v4_core0.$strip>>[] | undefined>], undefined>;
|
|
45
|
+
//#endregion
|
|
46
|
+
export { getCashback, listCashbacks };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { createRampClient } from "./client.mjs";
|
|
2
|
+
import { i as rampCashbackSchema, n as rampCashbackListInputSchema, r as rampCashbackListSchema, t as rampCashbackGetInputSchema } from "./transfers-WBQZVdGP.mjs";
|
|
3
|
+
import { t as rampOperation } from "./factory-Da6YVHdL.mjs";
|
|
4
|
+
import { t as omitUndefined } from "./shared-CTTtP5gv.mjs";
|
|
5
|
+
|
|
6
|
+
//#region src/cashbacks.ts
|
|
7
|
+
const listCashbacks = rampOperation({
|
|
8
|
+
id: "list_cashbacks",
|
|
9
|
+
name: "List Cashbacks",
|
|
10
|
+
description: "List cashback credits granted to the connected Ramp business.",
|
|
11
|
+
input: rampCashbackListInputSchema,
|
|
12
|
+
output: rampCashbackListSchema,
|
|
13
|
+
run: async (input, credentials) => {
|
|
14
|
+
const client = createRampClient(credentials);
|
|
15
|
+
return rampCashbackListSchema.parse(await client.request("/cashbacks", { query: omitUndefined({
|
|
16
|
+
page_size: input.pageSize,
|
|
17
|
+
start: input.cursor
|
|
18
|
+
}) }));
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
const getCashback = rampOperation({
|
|
22
|
+
id: "get_cashback",
|
|
23
|
+
name: "Get Cashback",
|
|
24
|
+
description: "Fetch a cashback credit by id.",
|
|
25
|
+
input: rampCashbackGetInputSchema,
|
|
26
|
+
output: rampCashbackSchema,
|
|
27
|
+
run: async (input, credentials) => {
|
|
28
|
+
const client = createRampClient(credentials);
|
|
29
|
+
return rampCashbackSchema.parse(await client.request(`/cashbacks/${encodeURIComponent(input.cashbackId)}`));
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
//#endregion
|
|
34
|
+
export { getCashback, listCashbacks };
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { n as RampCredentials } from "./integration-Dd6tOtSy.mjs";
|
|
2
|
+
import { t as RampErrorKind } from "./errors-Sm92tugz.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/client.d.ts
|
|
5
|
+
declare const RAMP_API_HOSTS: {
|
|
6
|
+
readonly production: "https://api.ramp.com";
|
|
7
|
+
readonly sandbox: "https://demo-api.ramp.com";
|
|
8
|
+
};
|
|
9
|
+
type RampEnvironment = keyof typeof RAMP_API_HOSTS;
|
|
10
|
+
type Primitive = string | number | boolean;
|
|
11
|
+
type QueryValue = Primitive | readonly Primitive[] | null | undefined;
|
|
12
|
+
interface RampQuery {
|
|
13
|
+
readonly [key: string]: QueryValue;
|
|
14
|
+
}
|
|
15
|
+
interface RampRequestOptions {
|
|
16
|
+
readonly method?: 'DELETE' | 'GET' | 'PATCH' | 'POST' | 'PUT';
|
|
17
|
+
readonly body?: unknown;
|
|
18
|
+
readonly query?: RampQuery;
|
|
19
|
+
readonly idempotencyKey?: string;
|
|
20
|
+
/** Signal a multipart upload. `body` must be a `FormData` instance. */
|
|
21
|
+
readonly multipart?: boolean;
|
|
22
|
+
/** When true, the raw Response is returned instead of the parsed body. */
|
|
23
|
+
readonly raw?: boolean;
|
|
24
|
+
readonly timeoutMs?: number;
|
|
25
|
+
readonly headers?: Record<string, string>;
|
|
26
|
+
}
|
|
27
|
+
interface RampClientOptions {
|
|
28
|
+
readonly environment?: RampEnvironment;
|
|
29
|
+
readonly fetchImpl?: typeof fetch;
|
|
30
|
+
readonly timeoutMs?: number;
|
|
31
|
+
readonly maxRetries?: number;
|
|
32
|
+
readonly now?: () => number;
|
|
33
|
+
/** Hook for tests: sleeps between retries. */
|
|
34
|
+
readonly sleep?: (ms: number) => Promise<void>;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Structured Ramp error. The `kind` discriminator tells downstream consumers
|
|
38
|
+
* how to react (retry, prompt re-auth, surface validation message, etc.).
|
|
39
|
+
*/
|
|
40
|
+
declare class RampApiError extends Error {
|
|
41
|
+
readonly kind: RampErrorKind;
|
|
42
|
+
readonly status: number;
|
|
43
|
+
readonly body: unknown;
|
|
44
|
+
readonly errorCode?: string;
|
|
45
|
+
readonly retryAfterMs?: number;
|
|
46
|
+
constructor(init: {
|
|
47
|
+
kind: RampErrorKind;
|
|
48
|
+
status: number;
|
|
49
|
+
message: string;
|
|
50
|
+
body?: unknown;
|
|
51
|
+
errorCode?: string;
|
|
52
|
+
retryAfterMs?: number;
|
|
53
|
+
cause?: unknown;
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Thrown when a Ramp operation requires an OAuth scope that was not granted
|
|
58
|
+
* to the connected app (including scopes Ramp has not yet published).
|
|
59
|
+
*/
|
|
60
|
+
declare class RampMissingScopeError extends Error {
|
|
61
|
+
readonly requiredScope: string;
|
|
62
|
+
readonly operation: string;
|
|
63
|
+
constructor(operation: string, requiredScope: string, detail?: string);
|
|
64
|
+
}
|
|
65
|
+
declare function createRampApiClient(accessToken: string, options?: RampClientOptions): RampApiClient;
|
|
66
|
+
interface RampApiClient {
|
|
67
|
+
request<T = unknown>(path: string, init?: RampRequestOptions): Promise<T>;
|
|
68
|
+
paginate<TItem>(path: string, init?: Omit<RampRequestOptions, 'method'> & {
|
|
69
|
+
readonly pageSize?: number;
|
|
70
|
+
}): AsyncGenerator<TItem, void, void>;
|
|
71
|
+
readonly environment: RampEnvironment;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Public factory — callers normally use {@link createRampClient} below.
|
|
75
|
+
*/
|
|
76
|
+
declare function createRampClient(credentials: RampCredentials, options?: RampClientOptions): RampClient;
|
|
77
|
+
type RampClient = RampApiClient;
|
|
78
|
+
//#endregion
|
|
79
|
+
export { RampApiClient, RampApiError, RampClient, RampClientOptions, RampEnvironment, RampMissingScopeError, RampQuery, RampRequestOptions, createRampApiClient, createRampClient };
|
package/dist/client.mjs
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { n as ramp } from "./integration-BGliAxAS.mjs";
|
|
2
|
+
import { CredentialRevokedError } from "@keystrokehq/core/errors";
|
|
3
|
+
import { createErrorNormalizingProxy } from "@keystrokehq/integration-authoring";
|
|
4
|
+
|
|
5
|
+
//#region src/client.ts
|
|
6
|
+
const RAMP_API_HOSTS = {
|
|
7
|
+
production: "https://api.ramp.com",
|
|
8
|
+
sandbox: "https://demo-api.ramp.com"
|
|
9
|
+
};
|
|
10
|
+
const RAMP_API_BASE_PATH = "/developer/v1";
|
|
11
|
+
const DEFAULT_TIMEOUT_MS = 3e4;
|
|
12
|
+
const MAX_RETRIES = 3;
|
|
13
|
+
const BASE_RETRY_DELAY_MS = 500;
|
|
14
|
+
const CONNECT_HINT = "If you haven't connected Ramp yet, run `keystroke connect ramp`.";
|
|
15
|
+
/**
|
|
16
|
+
* Structured Ramp error. The `kind` discriminator tells downstream consumers
|
|
17
|
+
* how to react (retry, prompt re-auth, surface validation message, etc.).
|
|
18
|
+
*/
|
|
19
|
+
var RampApiError = class extends Error {
|
|
20
|
+
kind;
|
|
21
|
+
status;
|
|
22
|
+
body;
|
|
23
|
+
errorCode;
|
|
24
|
+
retryAfterMs;
|
|
25
|
+
constructor(init) {
|
|
26
|
+
super(init.message, init.cause !== void 0 ? { cause: init.cause } : void 0);
|
|
27
|
+
this.name = "RampApiError";
|
|
28
|
+
this.kind = init.kind;
|
|
29
|
+
this.status = init.status;
|
|
30
|
+
this.body = init.body;
|
|
31
|
+
this.errorCode = init.errorCode;
|
|
32
|
+
this.retryAfterMs = init.retryAfterMs;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Thrown when a Ramp operation requires an OAuth scope that was not granted
|
|
37
|
+
* to the connected app (including scopes Ramp has not yet published).
|
|
38
|
+
*/
|
|
39
|
+
var RampMissingScopeError = class extends Error {
|
|
40
|
+
requiredScope;
|
|
41
|
+
operation;
|
|
42
|
+
constructor(operation, requiredScope, detail) {
|
|
43
|
+
super(`Ramp operation \`${operation}\` requires scope \`${requiredScope}\` which is not available on the connected credential. ${detail ?? ""}`.trim());
|
|
44
|
+
this.name = "RampMissingScopeError";
|
|
45
|
+
this.operation = operation;
|
|
46
|
+
this.requiredScope = requiredScope;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
function isRecord(value) {
|
|
50
|
+
return typeof value === "object" && value !== null;
|
|
51
|
+
}
|
|
52
|
+
function toQueryString(query) {
|
|
53
|
+
if (!query) return "";
|
|
54
|
+
const params = new URLSearchParams();
|
|
55
|
+
for (const [key, value] of Object.entries(query)) {
|
|
56
|
+
if (value === void 0 || value === null) continue;
|
|
57
|
+
if (Array.isArray(value)) {
|
|
58
|
+
if (value.length === 0) continue;
|
|
59
|
+
for (const item of value) params.append(key, String(item));
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
params.set(key, String(value));
|
|
63
|
+
}
|
|
64
|
+
const stringified = params.toString();
|
|
65
|
+
return stringified.length > 0 ? `?${stringified}` : "";
|
|
66
|
+
}
|
|
67
|
+
function classifyStatus(status, body) {
|
|
68
|
+
if (status === 401) return "auth_revoked";
|
|
69
|
+
if (status === 403) return (extractErrorMessage(body)?.toLowerCase() ?? "").includes("scope") ? "missing_scope" : "auth_revoked";
|
|
70
|
+
if (status === 404) return "not_found";
|
|
71
|
+
if (status === 409) return "conflict";
|
|
72
|
+
if (status === 422) return "validation";
|
|
73
|
+
if (status === 429) return "rate_limited";
|
|
74
|
+
if (status >= 500) return "server_error";
|
|
75
|
+
if (status >= 400) return "validation";
|
|
76
|
+
return "unknown";
|
|
77
|
+
}
|
|
78
|
+
function extractErrorMessage(body) {
|
|
79
|
+
if (!isRecord(body)) return void 0;
|
|
80
|
+
const errorV2 = isRecord(body.error_v2) ? body.error_v2 : void 0;
|
|
81
|
+
if (errorV2 && typeof errorV2.message === "string") return errorV2.message;
|
|
82
|
+
if (typeof body.message === "string") return body.message;
|
|
83
|
+
if (typeof body.error === "string") return body.error;
|
|
84
|
+
}
|
|
85
|
+
function extractErrorCode(body) {
|
|
86
|
+
if (!isRecord(body)) return void 0;
|
|
87
|
+
const errorV2 = isRecord(body.error_v2) ? body.error_v2 : void 0;
|
|
88
|
+
if (errorV2 && typeof errorV2.error_code === "string") return errorV2.error_code;
|
|
89
|
+
}
|
|
90
|
+
function parseRetryAfter(headerValue) {
|
|
91
|
+
if (!headerValue) return void 0;
|
|
92
|
+
const asNumber = Number(headerValue);
|
|
93
|
+
if (Number.isFinite(asNumber) && asNumber >= 0) return asNumber * 1e3;
|
|
94
|
+
const asDate = Date.parse(headerValue);
|
|
95
|
+
if (!Number.isNaN(asDate)) return Math.max(0, asDate - Date.now());
|
|
96
|
+
}
|
|
97
|
+
async function sleepDefault(ms) {
|
|
98
|
+
if (ms <= 0) return;
|
|
99
|
+
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
100
|
+
}
|
|
101
|
+
function shouldRetry(kind, attempt, maxRetries) {
|
|
102
|
+
if (attempt >= maxRetries) return false;
|
|
103
|
+
return kind === "rate_limited" || kind === "server_error" || kind === "timeout";
|
|
104
|
+
}
|
|
105
|
+
function isAuthRevoked(error) {
|
|
106
|
+
return error instanceof RampApiError && error.kind === "auth_revoked";
|
|
107
|
+
}
|
|
108
|
+
function normalizeRampError(error, context) {
|
|
109
|
+
if (isAuthRevoked(error)) return new CredentialRevokedError(ramp.id, `Ramp authentication failed while calling \`${context.methodPath}\`. The stored credentials for ${ramp.id} may be revoked or expired. ${CONNECT_HINT}`);
|
|
110
|
+
if (error instanceof RampApiError) return new Error(`Ramp API call failed while calling \`${context.methodPath}\` (${error.status} ${error.kind}): ${error.message}`, { cause: error });
|
|
111
|
+
if (error instanceof Error) return new Error(`Ramp API call failed while calling \`${context.methodPath}\`: ${error.message}`, { cause: error });
|
|
112
|
+
return /* @__PURE__ */ new Error(`Ramp API call failed while calling \`${context.methodPath}\`.`);
|
|
113
|
+
}
|
|
114
|
+
function createRampApiClient(accessToken, options = {}) {
|
|
115
|
+
const environment = options.environment ?? "production";
|
|
116
|
+
const baseUrl = `${RAMP_API_HOSTS[environment]}${RAMP_API_BASE_PATH}`;
|
|
117
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
118
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
119
|
+
const maxRetries = options.maxRetries ?? MAX_RETRIES;
|
|
120
|
+
const sleep = options.sleep ?? sleepDefault;
|
|
121
|
+
async function request(path, init = {}) {
|
|
122
|
+
const url = `${baseUrl}${path}${toQueryString(init.query)}`;
|
|
123
|
+
const method = init.method ?? "GET";
|
|
124
|
+
const headers = {
|
|
125
|
+
Authorization: `Bearer ${accessToken}`,
|
|
126
|
+
Accept: "application/json",
|
|
127
|
+
...init.headers
|
|
128
|
+
};
|
|
129
|
+
let body;
|
|
130
|
+
if (init.body !== void 0) if (init.multipart) {
|
|
131
|
+
if (!(init.body instanceof FormData)) throw new TypeError("RampApiClient: multipart=true requires body to be a FormData instance");
|
|
132
|
+
body = init.body;
|
|
133
|
+
} else {
|
|
134
|
+
headers["Content-Type"] = "application/json";
|
|
135
|
+
body = JSON.stringify(init.body);
|
|
136
|
+
}
|
|
137
|
+
if (init.idempotencyKey !== void 0) headers["Idempotency-Key"] = init.idempotencyKey;
|
|
138
|
+
let attempt = 0;
|
|
139
|
+
for (;;) {
|
|
140
|
+
const attemptTimeoutMs = init.timeoutMs ?? timeoutMs;
|
|
141
|
+
const controller = new AbortController();
|
|
142
|
+
const timer = setTimeout(() => controller.abort(), attemptTimeoutMs);
|
|
143
|
+
let response;
|
|
144
|
+
try {
|
|
145
|
+
response = await fetchImpl(url, {
|
|
146
|
+
method,
|
|
147
|
+
headers,
|
|
148
|
+
...body !== void 0 ? { body } : {},
|
|
149
|
+
signal: controller.signal
|
|
150
|
+
});
|
|
151
|
+
} catch (error) {
|
|
152
|
+
clearTimeout(timer);
|
|
153
|
+
const isAbort = error instanceof Error && (error.name === "AbortError" || error.message.includes("aborted"));
|
|
154
|
+
const kind = isAbort ? "timeout" : "network";
|
|
155
|
+
if (shouldRetry(kind, attempt, maxRetries)) {
|
|
156
|
+
attempt += 1;
|
|
157
|
+
await sleep(BASE_RETRY_DELAY_MS * 2 ** (attempt - 1));
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
throw new RampApiError({
|
|
161
|
+
kind,
|
|
162
|
+
status: 0,
|
|
163
|
+
message: isAbort ? `Request to \`${path}\` timed out after ${attemptTimeoutMs}ms` : `Request to \`${path}\` failed: ${error.message ?? String(error)}`,
|
|
164
|
+
cause: error
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
clearTimeout(timer);
|
|
168
|
+
if (init.raw) {
|
|
169
|
+
if (!response.ok) {
|
|
170
|
+
const raw = await response.text();
|
|
171
|
+
throw new RampApiError({
|
|
172
|
+
kind: classifyStatus(response.status, raw),
|
|
173
|
+
status: response.status,
|
|
174
|
+
message: `HTTP ${response.status} for \`${path}\``,
|
|
175
|
+
body: raw
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
return response;
|
|
179
|
+
}
|
|
180
|
+
if (response.status === 204) return;
|
|
181
|
+
const parsedBody = (response.headers.get("content-type") ?? "").includes("application/json") ? await response.json().catch(() => void 0) : await response.text().catch(() => void 0);
|
|
182
|
+
if (!response.ok) {
|
|
183
|
+
const kind = classifyStatus(response.status, parsedBody);
|
|
184
|
+
const message = extractErrorMessage(parsedBody) ?? `HTTP ${response.status}`;
|
|
185
|
+
const errorCode = extractErrorCode(parsedBody);
|
|
186
|
+
const retryAfterMs = parseRetryAfter(response.headers.get("retry-after"));
|
|
187
|
+
if (shouldRetry(kind, attempt, maxRetries)) {
|
|
188
|
+
attempt += 1;
|
|
189
|
+
await sleep(retryAfterMs ?? BASE_RETRY_DELAY_MS * 2 ** (attempt - 1));
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
throw new RampApiError({
|
|
193
|
+
kind,
|
|
194
|
+
status: response.status,
|
|
195
|
+
message,
|
|
196
|
+
body: parsedBody,
|
|
197
|
+
errorCode,
|
|
198
|
+
retryAfterMs
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
return parsedBody;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
async function* paginate(path, init = {}) {
|
|
205
|
+
let cursor;
|
|
206
|
+
let loops = 0;
|
|
207
|
+
const hardStop = 1e4;
|
|
208
|
+
for (;;) {
|
|
209
|
+
const query = {
|
|
210
|
+
...init.query ?? {},
|
|
211
|
+
...cursor !== void 0 ? { start: cursor } : {},
|
|
212
|
+
...init.pageSize !== void 0 ? { page_size: init.pageSize } : {}
|
|
213
|
+
};
|
|
214
|
+
const response = await request(path, {
|
|
215
|
+
...init,
|
|
216
|
+
query,
|
|
217
|
+
method: "GET"
|
|
218
|
+
});
|
|
219
|
+
const data = response?.data ?? [];
|
|
220
|
+
for (const item of data) yield item;
|
|
221
|
+
const next = response?.page?.next;
|
|
222
|
+
if (!next) return;
|
|
223
|
+
cursor = next;
|
|
224
|
+
loops += 1;
|
|
225
|
+
if (loops >= hardStop) throw new RampApiError({
|
|
226
|
+
kind: "unknown",
|
|
227
|
+
status: 0,
|
|
228
|
+
message: `Ramp pagination exceeded ${hardStop} pages for \`${path}\` — refusing to continue`
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return {
|
|
233
|
+
request,
|
|
234
|
+
paginate,
|
|
235
|
+
environment
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Public factory — callers normally use {@link createRampClient} below.
|
|
240
|
+
*/
|
|
241
|
+
function createRampClient(credentials, options = {}) {
|
|
242
|
+
const accessToken = credentials.RAMP_ACCESS_TOKEN.trim();
|
|
243
|
+
if (accessToken.length === 0) throw new Error(`Ramp authentication failed during client initialization. ${CONNECT_HINT}`);
|
|
244
|
+
return createErrorNormalizingProxy(createRampApiClient(accessToken, options), normalizeRampError);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
//#endregion
|
|
248
|
+
export { RampApiError, RampMissingScopeError, createRampApiClient, createRampClient };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
//#region src/schemas/common.ts
|
|
4
|
+
/**
|
|
5
|
+
* Non-empty identifier string, trimmed. Applied to every Ramp resource ID,
|
|
6
|
+
* path parameter, and cursor.
|
|
7
|
+
*/
|
|
8
|
+
const rampIdSchema = z.string().trim().min(1);
|
|
9
|
+
/**
|
|
10
|
+
* ISO-8601 timestamp. Ramp always returns UTC timestamps with `Z` suffix.
|
|
11
|
+
* We validate shape only; downstream mappers convert to `Date` when needed.
|
|
12
|
+
*/
|
|
13
|
+
const rampTimestampSchema = z.string().trim().min(1);
|
|
14
|
+
const rampCurrencyCodeSchema = z.string().trim().length(3).regex(/^[A-Z]{3}$/u, { message: "Currency must be an ISO-4217 3-letter uppercase code" });
|
|
15
|
+
/**
|
|
16
|
+
* Monetary value as surfaced to Keystroke users. Ramp stores amounts as integer
|
|
17
|
+
* cents; we preserve that representation so math stays exact.
|
|
18
|
+
*/
|
|
19
|
+
const rampMoneySchema = z.object({
|
|
20
|
+
amountCents: z.number().int(),
|
|
21
|
+
currency: rampCurrencyCodeSchema
|
|
22
|
+
});
|
|
23
|
+
const rampPageCursorSchema = z.string().trim().min(1);
|
|
24
|
+
const rampPageInfoSchema = z.object({ next: z.string().trim().min(1).optional() });
|
|
25
|
+
const rampListPaginationInputSchema = z.object({
|
|
26
|
+
pageSize: z.number().int().positive().max(100).optional(),
|
|
27
|
+
cursor: rampPageCursorSchema.optional()
|
|
28
|
+
});
|
|
29
|
+
/**
|
|
30
|
+
* Generic "one resource" envelope. Ramp returns single-resource endpoints as
|
|
31
|
+
* the resource directly (not wrapped), so this is rarely used; we keep it for
|
|
32
|
+
* consistency with list envelopes.
|
|
33
|
+
*/
|
|
34
|
+
function rampResourceEnvelope(itemSchema) {
|
|
35
|
+
return itemSchema;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Generic `{ data: T[], page: { next } }` list envelope — the canonical Ramp
|
|
39
|
+
* pagination wire shape.
|
|
40
|
+
*/
|
|
41
|
+
function rampListResponseSchema(itemSchema) {
|
|
42
|
+
return z.object({
|
|
43
|
+
data: z.array(itemSchema),
|
|
44
|
+
page: rampPageInfoSchema.optional()
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Ramp list endpoints accept `start`, `end`, `from`, `to`, `since`, and
|
|
49
|
+
* assorted domain-specific date filters. This schema captures the common
|
|
50
|
+
* start/end filter that many endpoints share.
|
|
51
|
+
*/
|
|
52
|
+
const rampDateRangeInputSchema = z.object({
|
|
53
|
+
start: rampTimestampSchema.optional(),
|
|
54
|
+
end: rampTimestampSchema.optional()
|
|
55
|
+
});
|
|
56
|
+
/**
|
|
57
|
+
* Ramp "accounting connection" identifier. Not a full resource in all
|
|
58
|
+
* endpoints but referenced widely.
|
|
59
|
+
*/
|
|
60
|
+
const rampAccountingConnectionIdSchema = rampIdSchema;
|
|
61
|
+
const rampMetadataSchema = z.record(z.string(), z.unknown());
|
|
62
|
+
|
|
63
|
+
//#endregion
|
|
64
|
+
export { rampListPaginationInputSchema as a, rampMoneySchema as c, rampResourceEnvelope as d, rampTimestampSchema as f, rampIdSchema as i, rampPageCursorSchema as l, rampCurrencyCodeSchema as n, rampListResponseSchema as o, rampDateRangeInputSchema as r, rampMetadataSchema as s, rampAccountingConnectionIdSchema as t, rampPageInfoSchema as u };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as rampBundle, i as ramp, n as RampCredentials, o as rampOfficialIntegration, r as RampOAuthScope, t as RAMP_OAUTH_SCOPES } from "./integration-Dd6tOtSy.mjs";
|
|
2
|
+
export { RAMP_OAUTH_SCOPES, type RampCredentials, type RampOAuthScope, ramp, rampBundle, rampOfficialIntegration };
|