diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8774bd1..4d89498 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,7 +29,7 @@ jobs: - name: Set up pnpm uses: pnpm/action-setup@v4 with: - version: '10.27.0' + version: '10.30.1' - name: Bootstrap run: ./scripts/bootstrap @@ -56,7 +56,7 @@ jobs: - name: Set up pnpm uses: pnpm/action-setup@v4 with: - version: '10.27.0' + version: '10.30.1' - name: Bootstrap run: ./scripts/bootstrap @@ -103,7 +103,7 @@ jobs: - name: Set up pnpm uses: pnpm/action-setup@v4 with: - version: '10.27.0' + version: '10.30.1' - name: Bootstrap run: ./scripts/bootstrap diff --git a/.github/workflows/publish-jsr.yml b/.github/workflows/publish-jsr.yml new file mode 100644 index 0000000..be603b3 --- /dev/null +++ b/.github/workflows/publish-jsr.yml @@ -0,0 +1,44 @@ +# This workflow is triggered when a GitHub release is created. +# It can also be run manually to re-publish to JSR in case it failed for some reason. +# You can run this workflow by navigating to https://www.github.com/lightsparkdev/grid-js-sdk/actions/workflows/publish-jsr.yml +name: Publish JSR +on: + workflow_dispatch: + + release: + types: [published] + +jobs: + publish: + name: publish + runs-on: ubuntu-latest + permissions: + contents: write + id-token: write + environment: npm + + steps: + - uses: actions/checkout@v6 + + - name: Set up Node + uses: actions/setup-node@v3 + with: + node-version: '20' + + - name: Set up pnpm + uses: pnpm/action-setup@v4 + + - name: Install dependencies + run: | + pnpm install + + - name: Publish to JSR + run: | + bash ./bin/publish-jsr + + - name: Upload MCP Server DXT GitHub release asset + run: | + gh release upload ${{ github.event.release.tag_name }} \ + packages/mcp-server/lightsparkdev_grid_api.mcpb + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.release-please-manifest.json b/.release-please-manifest.json index bcd0522..e7ca613 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.6.0" + ".": "0.7.0" } diff --git a/.stats.yml b/.stats.yml index f1806aa..fa0dfcb 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 44 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/lightspark%2Fgrid-55dc3fc30633610b571a49d307119528632e6def241fc91044cd5bed44b8b536.yml -openapi_spec_hash: f781bd45bfd0243ba7799b20a16a4d73 -config_hash: 3cd41f4d6744b7c31c1ee66b71070e2d +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/lightspark%2Fgrid-c62cf646aaeb4912ba5a5ddb452f360df5e866784090dd8fcddc294c5adde346.yml +openapi_spec_hash: a7022eff197b718e17c9a1feb089cd4a +config_hash: 0ed9a072d82566493e480b7f6ef94e68 diff --git a/CHANGELOG.md b/CHANGELOG.md index a65c4fb..d901756 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,41 @@ # Changelog +## 0.7.0 (2026-02-25) + +Full Changelog: [v0.6.0...v0.7.0](https://github.com/lightsparkdev/grid-js-sdk/compare/v0.6.0...v0.7.0) + +### Features + +* **api:** add BRL/DKK/GBP/HKD/IDR/INR/MXN/MYR/PHP/SGD/THB/USD/VND account types ([ab875a8](https://github.com/lightsparkdev/grid-js-sdk/commit/ab875a8f7c2996eeb963511f37d2190782353498)) +* **api:** add CAD and NGN account types to external accounts ([ec58891](https://github.com/lightsparkdev/grid-js-sdk/commit/ec58891a5b1855b44fcabbddc027c2f8dcef7abb)) +* **api:** add purposeOfPayment parameter to quotes create ([276ab08](https://github.com/lightsparkdev/grid-js-sdk/commit/276ab08f050996fa228bca5fa61967cd173f099e)) +* **api:** add response types to transferIn/transferOut/transactions methods ([4fc5674](https://github.com/lightsparkdev/grid-js-sdk/commit/4fc56740809d983455cf819dbffba458357fc8f5)) + + +### Bug Fixes + +* **api:** remove customerId, defaultUmaDepositAccount from external accounts create ([f75819a](https://github.com/lightsparkdev/grid-js-sdk/commit/f75819a102942478ec98ea24bf66c494e1ba6a43)) +* **api:** rename ID fields to id in quotes, bulk status, and webhook events ([5bcd4c9](https://github.com/lightsparkdev/grid-js-sdk/commit/5bcd4c9b599445c2eaab628a780b76c0d2b7b463)) +* **docs/contributing:** correct pnpm link command ([6c958b1](https://github.com/lightsparkdev/grid-js-sdk/commit/6c958b1ddd0843524cebe7f07f57561066425b2a)) +* **mcp:** initialize SDK lazily to avoid failing the connection on init errors ([7f3dfba](https://github.com/lightsparkdev/grid-js-sdk/commit/7f3dfbade0657f27c165b94da44c078d7bc02fc0)) +* **types:** standardize customers retrieve/update/delete return types to CustomerOneOf ([e8759c8](https://github.com/lightsparkdev/grid-js-sdk/commit/e8759c855de91ef8c39a22ed4fccaf399a716a34)) + + +### Chores + +* **internal:** cache fetch instruction calls in MCP server ([6f862a1](https://github.com/lightsparkdev/grid-js-sdk/commit/6f862a1fc663380572e5279f665a2c04c2c0c51c)) +* **internal:** make MCP code execution location configurable via a flag ([4beed6a](https://github.com/lightsparkdev/grid-js-sdk/commit/4beed6a1f017b430667cb806acda404b3acaa0e6)) +* **internal:** remove mock server code ([903ac44](https://github.com/lightsparkdev/grid-js-sdk/commit/903ac444c0c5100e4a6b92798ad5729a6ac66558)) +* **internal:** upgrade @modelcontextprotocol/sdk and hono ([1a99495](https://github.com/lightsparkdev/grid-js-sdk/commit/1a994952a2e184c13e3cb032cec22ee5807b90f4)) +* **internal:** upgrade pnpm version ([5ca7861](https://github.com/lightsparkdev/grid-js-sdk/commit/5ca78613e8ba357a685a85d2c1909a107c002581)) +* **mcp:** correctly update version in sync with sdk ([37235f0](https://github.com/lightsparkdev/grid-js-sdk/commit/37235f0792d795a1826265b638660fe4095896d1)) +* update mock server docs ([8d27681](https://github.com/lightsparkdev/grid-js-sdk/commit/8d276815e14d5e44e668ecf045f4e60acc833df4)) + + +### Documentation + +* **api:** clarify immediatelyExecute parameter requirements in quotes ([795e0b9](https://github.com/lightsparkdev/grid-js-sdk/commit/795e0b9493f11e9e63576b63095c8339f79f9f40)) + ## 0.6.0 (2026-02-19) Full Changelog: [v0.5.0...v0.6.0](https://github.com/lightsparkdev/grid-js-sdk/compare/v0.5.0...v0.6.0) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 41f99cd..cc25a73 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -60,17 +60,11 @@ $ yarn link @lightsparkdev/grid # With pnpm $ pnpm link --global $ cd ../my-package -$ pnpm link -—global @lightsparkdev/grid +$ pnpm link --global @lightsparkdev/grid ``` ## Running tests -Most tests require you to [set up a mock server](https://github.com/stoplightio/prism) against the OpenAPI spec to run the tests. - -```sh -$ npx prism mock path/to/your/openapi.yml -``` - ```sh $ pnpm run test ``` diff --git a/README.md b/README.md index b23b62f..ee0def0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Lightspark Grid TypeScript API Library -[![NPM version]()](https://npmjs.org/package/@lightsparkdev/grid) ![npm bundle size](https://img.shields.io/bundlephobia/minzip/@lightsparkdev/grid) +[![NPM version]()](https://npmjs.org/package/@lightsparkdev/grid) ![npm bundle size](https://img.shields.io/bundlephobia/minzip/@lightsparkdev/grid) [![JSR Version](https://jsr.io/badges/@lightsparkdev/grid)](https://jsr.io/@lightsparkdev/grid) This library provides convenient access to the Lightspark Grid REST API from server-side TypeScript or JavaScript. @@ -23,6 +23,21 @@ Use the Lightspark Grid MCP Server to enable AI assistants to interact with this npm install @lightsparkdev/grid ``` +### Installation from JSR + +```sh +deno add jsr:@lightsparkdev/grid +npx jsr add @lightsparkdev/grid +``` + +These commands will make the module importable from the `@lightsparkdev/grid` scope: + +You can also [import directly from JSR](https://jsr.io/docs/using-packages#importing-with-jsr-specifiers) without an install step if you're using the Deno JavaScript runtime: + +```ts +import LightsparkGrid from 'jsr:@lightsparkdev/grid'; +``` + ## Usage The full API of this library can be found in [api.md](api.md). @@ -49,7 +64,7 @@ const quote = await client.quotes.create({ }, }); -console.log(quote.createdAt); +console.log(quote.id); ``` ### Request & Response types @@ -285,7 +300,7 @@ const { data: quote, response: raw } = await client.quotes }) .withResponse(); console.log(raw.headers.get('X-My-Header')); -console.log(quote.createdAt); +console.log(quote.id); ``` ### Logging @@ -453,7 +468,7 @@ const client = new LightsparkGrid({ **Deno** [[docs](https://docs.deno.com/api/deno/~/Deno.createHttpClient)] ```ts -import LightsparkGrid from 'npm:@lightsparkdev/grid'; +import LightsparkGrid from 'jsr:@lightsparkdev/grid'; const httpClient = Deno.createHttpClient({ proxy: { url: 'http://localhost:8888' } }); const client = new LightsparkGrid({ diff --git a/api.md b/api.md index b70b111..4fc828a 100644 --- a/api.md +++ b/api.md @@ -19,18 +19,15 @@ Types: - CustomerCreate - CustomerOneOf - CustomerUpdate -- CustomerRetrieveResponse -- CustomerUpdateResponse -- CustomerDeleteResponse - CustomerGetKYCLinkResponse Methods: - client.customers.create({ ...params }) -> CustomerOneOf -- client.customers.retrieve(customerID) -> CustomerRetrieveResponse -- client.customers.update(customerID, { ...params }) -> CustomerUpdateResponse +- client.customers.retrieve(customerID) -> CustomerOneOf +- client.customers.update(customerID, { ...params }) -> CustomerOneOf - client.customers.list({ ...params }) -> CustomerOneovesDefaultPagination -- client.customers.delete(customerID) -> CustomerDeleteResponse +- client.customers.delete(customerID) -> CustomerOneOf - client.customers.getKYCLink({ ...params }) -> CustomerGetKYCLinkResponse - client.customers.listInternalAccounts({ ...params }) -> InternalAccountsDefaultPagination @@ -38,26 +35,43 @@ Methods: Types: +- Address - BaseWalletInfo -- BeneficiaryOneOf -- CadAccountInfo -- ClabeAccountInfo +- BrlBeneficiary +- BrlExternalAccountInfo +- BusinessBeneficiary +- DkkBeneficiary +- DkkExternalAccountInfo - ExternalAccount - ExternalAccountCreate - ExternalAccountInfoOneOf -- GbpAccountInfo -- IbanAccountInfo +- GbpBeneficiary +- GbpExternalAccountInfo +- HkdBeneficiary +- HkdExternalAccountInfo +- IdrBeneficiary +- IdrExternalAccountInfo +- InrBeneficiary +- InrExternalAccountInfo - LightningWalletInfo -- NgnAccountInfo -- PhpAccountInfo -- PixAccountInfo +- MxnBeneficiary +- MxnExternalAccountInfo +- MyrBeneficiary +- MyrExternalAccountInfo +- PhpBeneficiary +- PhpExternalAccountInfo - PolygonWalletInfo -- SgdAccountInfo +- SgdBeneficiary +- SgdExternalAccountInfo - SolanaWalletInfo - SparkWalletInfo +- ThbBeneficiary +- ThbExternalAccountInfo - TronWalletInfo -- UpiAccountInfo -- UsAccountInfo +- UsdBeneficiary +- UsdExternalAccountInfo +- VndBeneficiary +- VndExternalAccountInfo Methods: @@ -114,16 +128,21 @@ Types: - BaseTransactionDestination - Transaction +- TransferInCreateResponse Methods: -- client.transferIn.create({ ...params }) -> Transaction +- client.transferIn.create({ ...params }) -> TransferInCreateResponse # TransferOut +Types: + +- TransferOutCreateResponse + Methods: -- client.transferOut.create({ ...params }) -> Transaction +- client.transferOut.create({ ...params }) -> TransferOutCreateResponse # Receiver @@ -143,15 +162,11 @@ Methods: Types: -- BaseDestination -- BasePaymentAccountInfo -- BaseQuoteSource - Currency - OutgoingRateDetails - PaymentInstructions - Quote - QuoteDestinationOneOf -- QuoteSourceOneOf Methods: @@ -165,17 +180,17 @@ Methods: Types: - BaseTransactionSource -- CounterpartyInformation - IncomingTransaction -- TransactionDestinationOneOf - TransactionSourceOneOf - TransactionStatus - TransactionType +- TransactionRetrieveResponse +- TransactionListResponse Methods: -- client.transactions.retrieve(transactionID) -> Transaction -- client.transactions.list({ ...params }) -> TransactionsDefaultPagination +- client.transactions.retrieve(transactionID) -> TransactionRetrieveResponse +- client.transactions.list({ ...params }) -> TransactionListResponsesDefaultPagination - client.transactions.approve(transactionID, { ...params }) -> IncomingTransaction - client.transactions.reject(transactionID, { ...params }) -> IncomingTransaction @@ -183,18 +198,11 @@ Methods: Types: -- IncomingPaymentWebhookEvent -- OutgoingPaymentWebhookEvent -- TestWebhookWebhookEvent -- BulkUploadWebhookEvent -- InvitationClaimedWebhookEvent -- KYCStatusWebhookEvent -- AccountStatusWebhookEvent -- UnwrapWebhookEvent +- WebhookSendTestResponse Methods: -- client.webhooks.unwrap(body) -> void +- client.webhooks.sendTest() -> WebhookSendTestResponse # Invitations @@ -215,12 +223,10 @@ Methods: Types: - SandboxSendFundsResponse -- SandboxSendTestWebhookResponse Methods: - client.sandbox.sendFunds({ ...params }) -> SandboxSendFundsResponse -- client.sandbox.sendTestWebhook() -> SandboxSendTestWebhookResponse ## Uma diff --git a/bin/publish-jsr b/bin/publish-jsr new file mode 100644 index 0000000..fd7f114 --- /dev/null +++ b/bin/publish-jsr @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +set -eux + +# Build the project +pnpm build + +# Navigate to the dist directory +cd dist-deno + +npx jsr publish ${JSR_TOKEN:+"--token=$JSR_TOKEN"} diff --git a/jsr.json b/jsr.json new file mode 100644 index 0000000..10a09d2 --- /dev/null +++ b/jsr.json @@ -0,0 +1,10 @@ +{ + "name": "@lightsparkdev/grid", + "version": "0.7.0", + "exports": "./index.ts", + "publish": { + "exclude": [ + "!." + ] + } +} diff --git a/package.json b/package.json index 85f9e60..1fba1a9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lightsparkdev/grid", - "version": "0.6.0", + "version": "0.7.0", "description": "The official TypeScript library for the Lightspark Grid API", "author": "Lightspark Grid ", "types": "dist/index.d.ts", @@ -8,7 +8,7 @@ "type": "commonjs", "repository": "github:lightsparkdev/grid-js-sdk", "license": "Apache-2.0", - "packageManager": "pnpm@10.27.0", + "packageManager": "pnpm@10.30.1", "files": [ "**/*" ], @@ -35,6 +35,7 @@ "@types/node": "^20.17.6", "@typescript-eslint/eslint-plugin": "8.31.1", "@typescript-eslint/parser": "8.31.1", + "@typescript-eslint/typescript-estree": "8.31.1", "eslint": "^9.39.1", "eslint-plugin-prettier": "^5.4.1", "eslint-plugin-unused-imports": "^4.1.4", diff --git a/packages/mcp-server/Dockerfile b/packages/mcp-server/Dockerfile index 462a35a..27cad2a 100644 --- a/packages/mcp-server/Dockerfile +++ b/packages/mcp-server/Dockerfile @@ -43,9 +43,21 @@ ENV CI=true RUN pnpm install --frozen-lockfile && \ pnpm build -# Production stage +FROM denoland/deno:bin-2.6.10 AS deno_installer +FROM gcr.io/distroless/cc@sha256:66d87e170bc2c5e2b8cf853501141c3c55b4e502b8677595c57534df54a68cc5 AS cc + FROM node:24-alpine +# Install deno +COPY --from=deno_installer /deno /usr/local/bin/deno + +# Add in shared libraries needed by Deno +COPY --from=cc --chown=root:root --chmod=755 /lib/*-linux-gnu/* /usr/local/lib/ +COPY --from=cc --chown=root:root --chmod=755 /lib/ld-linux-* /lib/ + +RUN mkdir /lib64 && ln -s /usr/local/lib/ld-linux-* /lib64/ +ENV LD_LIBRARY_PATH=/usr/lib:/usr/local/lib + # Add non-root user RUN addgroup -g 1001 -S nodejs && adduser -S nodejs -u 1001 diff --git a/packages/mcp-server/manifest.json b/packages/mcp-server/manifest.json index 84a0a41..4c4ff3c 100644 --- a/packages/mcp-server/manifest.json +++ b/packages/mcp-server/manifest.json @@ -1,7 +1,7 @@ { "dxt_version": "0.2", "name": "@lightsparkdev/grid-mcp", - "version": "0.5.0", + "version": "0.7.0", "description": "The official MCP Server for the Lightspark Grid API", "author": { "name": "Lightspark Grid", @@ -17,7 +17,9 @@ "entry_point": "index.js", "mcp_config": { "command": "node", - "args": ["${__dirname}/index.js"], + "args": [ + "${__dirname}/index.js" + ], "env": { "GRID_CLIENT_ID": "${user_config.GRID_CLIENT_ID}", "GRID_CLIENT_SECRET": "${user_config.GRID_CLIENT_SECRET}", @@ -52,5 +54,7 @@ "node": ">=18.0.0" } }, - "keywords": ["api"] + "keywords": [ + "api" + ] } diff --git a/packages/mcp-server/package.json b/packages/mcp-server/package.json index 64eebeb..e27c034 100644 --- a/packages/mcp-server/package.json +++ b/packages/mcp-server/package.json @@ -1,6 +1,6 @@ { "name": "@lightsparkdev/grid-mcp", - "version": "0.6.0", + "version": "0.7.0", "description": "The official MCP Server for the Lightspark Grid API", "author": "Lightspark Grid ", "types": "dist/index.d.ts", @@ -13,7 +13,7 @@ }, "homepage": "https://github.com/lightsparkdev/grid-js-sdk/tree/main/packages/mcp-server#readme", "license": "Apache-2.0", - "packageManager": "pnpm@10.27.0", + "packageManager": "pnpm@10.30.1", "private": false, "publishConfig": { "access": "public" @@ -31,7 +31,7 @@ "dependencies": { "@lightsparkdev/grid": "workspace:*", "@cloudflare/cabidela": "^0.2.4", - "@modelcontextprotocol/sdk": "^1.25.2", + "@modelcontextprotocol/sdk": "^1.26.0", "@valtown/deno-http-worker": "^0.0.21", "cookie-parser": "^1.4.6", "cors": "^2.8.5", diff --git a/packages/mcp-server/src/code-tool-paths.cts b/packages/mcp-server/src/code-tool-paths.cts new file mode 100644 index 0000000..15ce7f5 --- /dev/null +++ b/packages/mcp-server/src/code-tool-paths.cts @@ -0,0 +1,3 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export const workerPath = require.resolve('./code-tool-worker.mjs'); diff --git a/packages/mcp-server/src/code-tool-types.ts b/packages/mcp-server/src/code-tool-types.ts index 0759a56..d9bd14b 100644 --- a/packages/mcp-server/src/code-tool-types.ts +++ b/packages/mcp-server/src/code-tool-types.ts @@ -8,6 +8,7 @@ export type WorkerInput = { client_opts: ClientOptions; intent?: string | undefined; }; + export type WorkerOutput = { is_error: boolean; result: unknown | null; diff --git a/packages/mcp-server/src/code-tool-worker.ts b/packages/mcp-server/src/code-tool-worker.ts new file mode 100644 index 0000000..e867c13 --- /dev/null +++ b/packages/mcp-server/src/code-tool-worker.ts @@ -0,0 +1,314 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import path from 'node:path'; +import util from 'node:util'; +import Fuse from 'fuse.js'; +import ts from 'typescript'; +import { WorkerOutput } from './code-tool-types'; +import { LightsparkGrid, ClientOptions } from '@lightsparkdev/grid'; + +function getRunFunctionSource(code: string): { + type: 'declaration' | 'expression'; + client: string | undefined; + code: string; +} | null { + const sourceFile = ts.createSourceFile('code.ts', code, ts.ScriptTarget.Latest, true); + const printer = ts.createPrinter(); + + for (const statement of sourceFile.statements) { + // Check for top-level function declarations + if (ts.isFunctionDeclaration(statement)) { + if (statement.name?.text === 'run') { + return { + type: 'declaration', + client: statement.parameters[0]?.name.getText(), + code: printer.printNode(ts.EmitHint.Unspecified, statement.body!, sourceFile), + }; + } + } + + // Check for variable declarations: const run = () => {} or const run = function() {} + if (ts.isVariableStatement(statement)) { + for (const declaration of statement.declarationList.declarations) { + if ( + ts.isIdentifier(declaration.name) && + declaration.name.text === 'run' && + // Check if it's initialized with a function + declaration.initializer && + (ts.isFunctionExpression(declaration.initializer) || ts.isArrowFunction(declaration.initializer)) + ) { + return { + type: 'expression', + client: declaration.initializer.parameters[0]?.name.getText(), + code: printer.printNode(ts.EmitHint.Unspecified, declaration.initializer, sourceFile), + }; + } + } + } + } + + return null; +} + +function getTSDiagnostics(code: string): string[] { + const functionSource = getRunFunctionSource(code)!; + const codeWithImport = [ + 'import { LightsparkGrid } from "@lightsparkdev/grid";', + functionSource.type === 'declaration' ? + `async function run(${functionSource.client}: LightsparkGrid)` + : `const run: (${functionSource.client}: LightsparkGrid) => Promise =`, + functionSource.code, + ].join('\n'); + const sourcePath = path.resolve('code.ts'); + const ast = ts.createSourceFile(sourcePath, codeWithImport, ts.ScriptTarget.Latest, true); + const options = ts.getDefaultCompilerOptions(); + options.target = ts.ScriptTarget.Latest; + options.module = ts.ModuleKind.NodeNext; + options.moduleResolution = ts.ModuleResolutionKind.NodeNext; + const host = ts.createCompilerHost(options, true); + const newHost: typeof host = { + ...host, + getSourceFile: (...args) => { + if (path.resolve(args[0]) === sourcePath) { + return ast; + } + return host.getSourceFile(...args); + }, + readFile: (...args) => { + if (path.resolve(args[0]) === sourcePath) { + return codeWithImport; + } + return host.readFile(...args); + }, + fileExists: (...args) => { + if (path.resolve(args[0]) === sourcePath) { + return true; + } + return host.fileExists(...args); + }, + }; + const program = ts.createProgram({ + options, + rootNames: [sourcePath], + host: newHost, + }); + const diagnostics = ts.getPreEmitDiagnostics(program, ast); + return diagnostics.map((d) => { + const message = ts.flattenDiagnosticMessageText(d.messageText, '\n'); + if (!d.file || !d.start) return `- ${message}`; + const { line: lineNumber } = ts.getLineAndCharacterOfPosition(d.file, d.start); + const line = codeWithImport.split('\n').at(lineNumber)?.trim(); + return line ? `- ${message}\n ${line}` : `- ${message}`; + }); +} + +const fuse = new Fuse( + [ + 'client.config.retrieve', + 'client.config.update', + 'client.customers.create', + 'client.customers.delete', + 'client.customers.getKYCLink', + 'client.customers.list', + 'client.customers.listInternalAccounts', + 'client.customers.retrieve', + 'client.customers.update', + 'client.customers.externalAccounts.create', + 'client.customers.externalAccounts.list', + 'client.customers.bulk.getJobStatus', + 'client.customers.bulk.uploadCsv', + 'client.platform.listInternalAccounts', + 'client.platform.externalAccounts.create', + 'client.platform.externalAccounts.list', + 'client.plaid.createLinkToken', + 'client.plaid.submitPublicToken', + 'client.transferIn.create', + 'client.transferOut.create', + 'client.receiver.lookupExternalAccount', + 'client.receiver.lookupUma', + 'client.quotes.create', + 'client.quotes.execute', + 'client.quotes.list', + 'client.quotes.retrieve', + 'client.transactions.approve', + 'client.transactions.list', + 'client.transactions.reject', + 'client.transactions.retrieve', + 'client.webhooks.sendTest', + 'client.invitations.cancel', + 'client.invitations.claim', + 'client.invitations.create', + 'client.invitations.retrieve', + 'client.sandbox.sendFunds', + 'client.sandbox.uma.receivePayment', + 'client.sandbox.internalAccounts.fund', + 'client.umaProviders.list', + 'client.tokens.create', + 'client.tokens.delete', + 'client.tokens.list', + 'client.tokens.retrieve', + 'client.exchangeRates.list', + ], + { threshold: 1, shouldSort: true }, +); + +function getMethodSuggestions(fullyQualifiedMethodName: string): string[] { + return fuse + .search(fullyQualifiedMethodName) + .map(({ item }) => item) + .slice(0, 5); +} + +const proxyToObj = new WeakMap(); +const objToProxy = new WeakMap(); + +type ClientProxyConfig = { + path: string[]; + isBelievedBad?: boolean; +}; + +function makeSdkProxy(obj: T, { path, isBelievedBad = false }: ClientProxyConfig): T { + let proxy: T = objToProxy.get(obj); + + if (!proxy) { + proxy = new Proxy(obj, { + get(target, prop, receiver) { + const propPath = [...path, String(prop)]; + const value = Reflect.get(target, prop, receiver); + + if (isBelievedBad || (!(prop in target) && value === undefined)) { + // If we're accessing a path that doesn't exist, it will probably eventually error. + // Let's proxy it and mark it bad so that we can control the error message. + // We proxy an empty class so that an invocation or construction attempt is possible. + return makeSdkProxy(class {}, { path: propPath, isBelievedBad: true }); + } + + if (value !== null && (typeof value === 'object' || typeof value === 'function')) { + return makeSdkProxy(value, { path: propPath, isBelievedBad }); + } + + return value; + }, + + apply(target, thisArg, args) { + if (isBelievedBad || typeof target !== 'function') { + const fullyQualifiedMethodName = path.join('.'); + const suggestions = getMethodSuggestions(fullyQualifiedMethodName); + throw new Error( + `${fullyQualifiedMethodName} is not a function. Did you mean: ${suggestions.join(', ')}`, + ); + } + + return Reflect.apply(target, proxyToObj.get(thisArg) ?? thisArg, args); + }, + + construct(target, args, newTarget) { + if (isBelievedBad || typeof target !== 'function') { + const fullyQualifiedMethodName = path.join('.'); + const suggestions = getMethodSuggestions(fullyQualifiedMethodName); + throw new Error( + `${fullyQualifiedMethodName} is not a constructor. Did you mean: ${suggestions.join(', ')}`, + ); + } + + return Reflect.construct(target, args, newTarget); + }, + }); + + objToProxy.set(obj, proxy); + proxyToObj.set(proxy, obj); + } + + return proxy; +} + +function parseError(code: string, error: unknown): string | undefined { + if (!(error instanceof Error)) return; + const message = error.name ? `${error.name}: ${error.message}` : error.message; + try { + // Deno uses V8; the first ":LINE:COLUMN" is the top of stack. + const lineNumber = error.stack?.match(/:([0-9]+):[0-9]+/)?.[1]; + // -1 for the zero-based indexing + const line = + lineNumber && + code + .split('\n') + .at(parseInt(lineNumber, 10) - 1) + ?.trim(); + return line ? `${message}\n at line ${lineNumber}\n ${line}` : message; + } catch { + return message; + } +} + +const fetch = async (req: Request): Promise => { + const { opts, code } = (await req.json()) as { opts: ClientOptions; code: string }; + + const runFunctionSource = code ? getRunFunctionSource(code) : null; + if (!runFunctionSource) { + const message = + code ? + 'The code is missing a top-level `run` function.' + : 'The code argument is missing. Provide one containing a top-level `run` function.'; + return Response.json( + { + is_error: true, + result: `${message} Write code within this template:\n\n\`\`\`\nasync function run(client) {\n // Fill this out\n}\n\`\`\``, + log_lines: [], + err_lines: [], + } satisfies WorkerOutput, + { status: 400, statusText: 'Code execution error' }, + ); + } + + const diagnostics = getTSDiagnostics(code); + if (diagnostics.length > 0) { + return Response.json( + { + is_error: true, + result: `The code contains TypeScript diagnostics:\n${diagnostics.join('\n')}`, + log_lines: [], + err_lines: [], + } satisfies WorkerOutput, + { status: 400, statusText: 'Code execution error' }, + ); + } + + const client = new LightsparkGrid({ + ...opts, + }); + + const log_lines: string[] = []; + const err_lines: string[] = []; + const console = { + log: (...args: unknown[]) => { + log_lines.push(util.format(...args)); + }, + error: (...args: unknown[]) => { + err_lines.push(util.format(...args)); + }, + }; + try { + let run_ = async (client: any) => {}; + eval(`${code}\nrun_ = run;`); + const result = await run_(makeSdkProxy(client, { path: ['client'] })); + return Response.json({ + is_error: false, + result, + log_lines, + err_lines, + } satisfies WorkerOutput); + } catch (e) { + return Response.json( + { + is_error: true, + result: parseError(code, e), + log_lines, + err_lines, + } satisfies WorkerOutput, + { status: 400, statusText: 'Code execution error' }, + ); + } +}; + +export default { fetch }; diff --git a/packages/mcp-server/src/code-tool.ts b/packages/mcp-server/src/code-tool.ts index f7a0eba..959a67e 100644 --- a/packages/mcp-server/src/code-tool.ts +++ b/packages/mcp-server/src/code-tool.ts @@ -1,6 +1,12 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +import fs from 'node:fs'; +import path from 'node:path'; +import url from 'node:url'; +import { newDenoHTTPWorker } from '@valtown/deno-http-worker'; +import { workerPath } from './code-tool-paths.cjs'; import { + ContentBlock, McpRequestContext, McpTool, Metadata, @@ -12,6 +18,8 @@ import { Tool } from '@modelcontextprotocol/sdk/types.js'; import { readEnv, requireValue } from './util'; import { WorkerInput, WorkerOutput } from './code-tool-types'; import { SdkMethod } from './methods'; +import { McpCodeExecutionMode } from './options'; +import { ClientOptions } from '@lightsparkdev/grid'; const prompt = `Runs JavaScript code to interact with the Lightspark Grid API. @@ -28,7 +36,7 @@ async function run(client) { source: { accountId: 'InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965', sourceType: 'ACCOUNT' }, }); - console.log(quote.createdAt); + console.log(quote.id); } \`\`\` @@ -45,9 +53,19 @@ Variables will not persist between calls, so make sure to return or log any data * we expose a single tool that can be used to search for endpoints by name, resource, operation, or tag, and then * a generic endpoint that can be used to invoke any endpoint with the provided arguments. * - * @param endpoints - The endpoints to include in the list. + * @param blockedMethods - The methods to block for code execution. Blocking is done by simple string + * matching, so it is not secure against obfuscation. For stronger security, block in the downstream API + * with limited API keys. + * @param codeExecutionMode - Whether to execute code in a local Deno environment or in a remote + * sandbox environment hosted by Stainless. */ -export function codeTool({ blockedMethods }: { blockedMethods: SdkMethod[] | undefined }): McpTool { +export function codeTool({ + blockedMethods, + codeExecutionMode, +}: { + blockedMethods: SdkMethod[] | undefined; + codeExecutionMode: McpCodeExecutionMode; +}): McpTool { const metadata: Metadata = { resource: 'all', operation: 'write', tags: [] }; const tool: Tool = { name: 'execute', @@ -67,6 +85,7 @@ export function codeTool({ blockedMethods }: { blockedMethods: SdkMethod[] | und required: ['code'], }, }; + const handler = async ({ reqContext, args, @@ -75,9 +94,6 @@ export function codeTool({ blockedMethods }: { blockedMethods: SdkMethod[] | und args: any; }): Promise => { const code = args.code as string; - const intent = args.intent as string | undefined; - const client = reqContext.client; - // Do very basic blocking of code that includes forbidden method names. // // WARNING: This is not secure against obfuscation and other evasion methods. If @@ -94,56 +110,260 @@ export function codeTool({ blockedMethods }: { blockedMethods: SdkMethod[] | und } } - const codeModeEndpoint = - readEnv('CODE_MODE_ENDPOINT_URL') ?? 'https://api.stainless.com/api/ai/code-tool'; - - // Setting a Stainless API key authenticates requests to the code tool endpoint. - const res = await fetch(codeModeEndpoint, { - method: 'POST', - headers: { - ...(reqContext.stainlessApiKey && { Authorization: reqContext.stainlessApiKey }), - 'Content-Type': 'application/json', - client_envs: JSON.stringify({ - GRID_CLIENT_ID: requireValue( - readEnv('GRID_CLIENT_ID') ?? client.username, - 'set GRID_CLIENT_ID environment variable or provide username client option', - ), - GRID_CLIENT_SECRET: requireValue( - readEnv('GRID_CLIENT_SECRET') ?? client.password, - 'set GRID_CLIENT_SECRET environment variable or provide password client option', - ), - GRID_WEBHOOK_PUBKEY: readEnv('GRID_WEBHOOK_PUBKEY') ?? client.webhookSignature ?? undefined, - LIGHTSPARK_GRID_BASE_URL: readEnv('LIGHTSPARK_GRID_BASE_URL') ?? client.baseURL ?? undefined, - }), - }, - body: JSON.stringify({ - project_name: 'grid', - code, - intent, - client_opts: {}, - } satisfies WorkerInput), - }); + if (codeExecutionMode === 'local') { + return await localDenoHandler({ reqContext, args }); + } else { + return await remoteStainlessHandler({ reqContext, args }); + } + }; + + return { metadata, tool, handler }; +} + +const remoteStainlessHandler = async ({ + reqContext, + args, +}: { + reqContext: McpRequestContext; + args: any; +}): Promise => { + const code = args.code as string; + const intent = args.intent as string | undefined; + const client = reqContext.client; - if (!res.ok) { - throw new Error( - `${res.status}: ${ - res.statusText - } error when trying to contact Code Tool server. Details: ${await res.text()}`, + const codeModeEndpoint = readEnv('CODE_MODE_ENDPOINT_URL') ?? 'https://api.stainless.com/api/ai/code-tool'; + + // Setting a Stainless API key authenticates requests to the code tool endpoint. + const res = await fetch(codeModeEndpoint, { + method: 'POST', + headers: { + ...(reqContext.stainlessApiKey && { Authorization: reqContext.stainlessApiKey }), + 'Content-Type': 'application/json', + client_envs: JSON.stringify({ + GRID_CLIENT_ID: requireValue( + readEnv('GRID_CLIENT_ID') ?? client.username, + 'set GRID_CLIENT_ID environment variable or provide username client option', + ), + GRID_CLIENT_SECRET: requireValue( + readEnv('GRID_CLIENT_SECRET') ?? client.password, + 'set GRID_CLIENT_SECRET environment variable or provide password client option', + ), + GRID_WEBHOOK_PUBKEY: readEnv('GRID_WEBHOOK_PUBKEY') ?? client.webhookSignature ?? undefined, + LIGHTSPARK_GRID_BASE_URL: readEnv('LIGHTSPARK_GRID_BASE_URL') ?? client.baseURL ?? undefined, + }), + }, + body: JSON.stringify({ + project_name: 'grid', + code, + intent, + client_opts: {}, + } satisfies WorkerInput), + }); + + if (!res.ok) { + throw new Error( + `${res.status}: ${ + res.statusText + } error when trying to contact Code Tool server. Details: ${await res.text()}`, + ); + } + + const { is_error, result, log_lines, err_lines } = (await res.json()) as WorkerOutput; + const hasLogs = log_lines.length > 0 || err_lines.length > 0; + const output = { + result, + ...(log_lines.length > 0 && { log_lines }), + ...(err_lines.length > 0 && { err_lines }), + }; + if (is_error) { + return asErrorResult(typeof result === 'string' && !hasLogs ? result : JSON.stringify(output, null, 2)); + } + return asTextContentResult(output); +}; + +const localDenoHandler = async ({ + reqContext, + args, +}: { + reqContext: McpRequestContext; + args: unknown; +}): Promise => { + const client = reqContext.client; + const baseURLHostname = new URL(client.baseURL).hostname; + const { code } = args as { code: string }; + + let denoPath: string; + + const packageRoot = path.resolve(path.dirname(workerPath), '..'); + const packageNodeModulesPath = path.resolve(packageRoot, 'node_modules'); + + // Check if deno is in PATH + const { execSync } = await import('node:child_process'); + try { + execSync('command -v deno', { stdio: 'ignore' }); + denoPath = 'deno'; + } catch { + try { + // Use deno binary in node_modules if it's found + const denoNodeModulesPath = path.resolve(packageNodeModulesPath, 'deno', 'bin.cjs'); + await fs.promises.access(denoNodeModulesPath, fs.constants.X_OK); + denoPath = denoNodeModulesPath; + } catch { + return asErrorResult( + 'Deno is required for code execution but was not found. ' + + 'Install it from https://deno.land or run: npm install deno', ); } + } + + const allowReadPaths = [ + 'code-tool-worker.mjs', + `${workerPath.replace(/([\/\\]node_modules)[\/\\].+$/, '$1')}/`, + packageRoot, + ]; - const { is_error, result, log_lines, err_lines } = (await res.json()) as WorkerOutput; - const hasLogs = log_lines.length > 0 || err_lines.length > 0; - const output = { - result, - ...(log_lines.length > 0 && { log_lines }), - ...(err_lines.length > 0 && { err_lines }), - }; - if (is_error) { - return asErrorResult(typeof result === 'string' && !hasLogs ? result : JSON.stringify(output, null, 2)); + // Follow symlinks in node_modules to allow read access to workspace-linked packages + try { + const sdkPkgName = '@lightsparkdev/grid'; + const sdkDir = path.resolve(packageNodeModulesPath, sdkPkgName); + const realSdkDir = fs.realpathSync(sdkDir); + if (realSdkDir !== sdkDir) { + allowReadPaths.push(realSdkDir); } - return asTextContentResult(output); - }; + } catch { + // Ignore if symlink resolution fails + } - return { metadata, tool, handler }; -} + const allowRead = allowReadPaths.join(','); + + const worker = await newDenoHTTPWorker(url.pathToFileURL(workerPath), { + denoExecutable: denoPath, + runFlags: [ + `--node-modules-dir=manual`, + `--allow-read=${allowRead}`, + `--allow-net=${baseURLHostname}`, + // Allow environment variables because instantiating the client will try to read from them, + // even though they are not set. + '--allow-env', + ], + printOutput: true, + spawnOptions: { + cwd: path.dirname(workerPath), + }, + }); + + try { + const resp = await new Promise((resolve, reject) => { + worker.addEventListener('exit', (exitCode) => { + reject(new Error(`Worker exited with code ${exitCode}`)); + }); + + const opts: ClientOptions = { + baseURL: client.baseURL, + username: client.username, + password: client.password, + webhookSignature: client.webhookSignature, + defaultHeaders: { + 'X-Stainless-MCP': 'true', + }, + }; + + const req = worker.request( + 'http://localhost', + { + headers: { + 'content-type': 'application/json', + }, + method: 'POST', + }, + (resp) => { + const body: Uint8Array[] = []; + resp.on('error', (err) => { + reject(err); + }); + resp.on('data', (chunk) => { + body.push(chunk); + }); + resp.on('end', () => { + resolve( + new Response(Buffer.concat(body).toString(), { + status: resp.statusCode ?? 200, + headers: resp.headers as any, + }), + ); + }); + }, + ); + + const body = JSON.stringify({ + opts, + code, + }); + + req.write(body, (err) => { + if (err != null) { + reject(err); + } + }); + + req.end(); + }); + + if (resp.status === 200) { + const { result, log_lines, err_lines } = (await resp.json()) as WorkerOutput; + const returnOutput: ContentBlock | null = + result == null ? null : ( + { + type: 'text', + text: typeof result === 'string' ? result : JSON.stringify(result), + } + ); + const logOutput: ContentBlock | null = + log_lines.length === 0 ? + null + : { + type: 'text', + text: log_lines.join('\n'), + }; + const errOutput: ContentBlock | null = + err_lines.length === 0 ? + null + : { + type: 'text', + text: 'Error output:\n' + err_lines.join('\n'), + }; + return { + content: [returnOutput, logOutput, errOutput].filter((block) => block !== null), + }; + } else { + const { result, log_lines, err_lines } = (await resp.json()) as WorkerOutput; + const messageOutput: ContentBlock | null = + result == null ? null : ( + { + type: 'text', + text: typeof result === 'string' ? result : JSON.stringify(result), + } + ); + const logOutput: ContentBlock | null = + log_lines.length === 0 ? + null + : { + type: 'text', + text: log_lines.join('\n'), + }; + const errOutput: ContentBlock | null = + err_lines.length === 0 ? + null + : { + type: 'text', + text: 'Error output:\n' + err_lines.join('\n'), + }; + return { + content: [messageOutput, logOutput, errOutput].filter((block) => block !== null), + isError: true, + }; + } + } finally { + worker.terminate(); + } +}; diff --git a/packages/mcp-server/src/http.ts b/packages/mcp-server/src/http.ts index dc44e5c..3463102 100644 --- a/packages/mcp-server/src/http.ts +++ b/packages/mcp-server/src/http.ts @@ -24,28 +24,17 @@ const newServer = async ({ const stainlessApiKey = getStainlessApiKey(req, mcpOptions); const server = await newMcpServer(stainlessApiKey); - try { - const authOptions = parseClientAuthHeaders(req, false); + const authOptions = parseClientAuthHeaders(req, false); - await initMcpServer({ - server: server, - mcpOptions: mcpOptions, - clientOptions: { - ...clientOptions, - ...authOptions, - }, - stainlessApiKey: stainlessApiKey, - }); - } catch (error) { - res.status(401).json({ - jsonrpc: '2.0', - error: { - code: -32000, - message: `Unauthorized: ${error instanceof Error ? error.message : error}`, - }, - }); - return null; - } + await initMcpServer({ + server: server, + mcpOptions: mcpOptions, + clientOptions: { + ...clientOptions, + ...authOptions, + }, + stainlessApiKey: stainlessApiKey, + }); return server; }; diff --git a/packages/mcp-server/src/index.ts b/packages/mcp-server/src/index.ts index 003a765..654d25c 100644 --- a/packages/mcp-server/src/index.ts +++ b/packages/mcp-server/src/index.ts @@ -24,7 +24,7 @@ async function main() { await launchStreamableHTTPServer({ mcpOptions: options, debug: options.debug, - port: options.port ?? options.socket, + port: options.socket ?? options.port, }); break; } diff --git a/packages/mcp-server/src/instructions.ts b/packages/mcp-server/src/instructions.ts new file mode 100644 index 0000000..b250a39 --- /dev/null +++ b/packages/mcp-server/src/instructions.ts @@ -0,0 +1,74 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { readEnv } from './util'; + +const INSTRUCTIONS_CACHE_TTL_MS = 15 * 60 * 1000; // 15 minutes + +interface InstructionsCacheEntry { + fetchedInstructions: string; + fetchedAt: number; +} + +const instructionsCache = new Map(); + +// Periodically evict stale entries so the cache doesn't grow unboundedly. +const _cacheCleanupInterval = setInterval(() => { + const now = Date.now(); + for (const [key, entry] of instructionsCache) { + if (now - entry.fetchedAt > INSTRUCTIONS_CACHE_TTL_MS) { + instructionsCache.delete(key); + } + } +}, INSTRUCTIONS_CACHE_TTL_MS); + +// Don't keep the process alive just for cleanup. +_cacheCleanupInterval.unref(); + +export async function getInstructions(stainlessApiKey: string | undefined): Promise { + const cacheKey = stainlessApiKey ?? ''; + const cached = instructionsCache.get(cacheKey); + + if (cached && Date.now() - cached.fetchedAt <= INSTRUCTIONS_CACHE_TTL_MS) { + return cached.fetchedInstructions; + } + + const fetchedInstructions = await fetchLatestInstructions(stainlessApiKey); + instructionsCache.set(cacheKey, { fetchedInstructions, fetchedAt: Date.now() }); + return fetchedInstructions; +} + +async function fetchLatestInstructions(stainlessApiKey: string | undefined): Promise { + // Setting the stainless API key is optional, but may be required + // to authenticate requests to the Stainless API. + const response = await fetch( + readEnv('CODE_MODE_INSTRUCTIONS_URL') ?? 'https://api.stainless.com/api/ai/instructions/grid', + { + method: 'GET', + headers: { ...(stainlessApiKey && { Authorization: stainlessApiKey }) }, + }, + ); + + let instructions: string | undefined; + if (!response.ok) { + console.warn( + 'Warning: failed to retrieve MCP server instructions. Proceeding with default instructions...', + ); + + instructions = ` + This is the grid MCP server. You will use Code Mode to help the user perform + actions. You can use search_docs tool to learn about how to take action with this server. Then, + you will write TypeScript code using the execute tool take action. It is CRITICAL that you be + thoughtful and deliberate when executing code. Always try to entirely solve the problem in code + block: it can be as long as you need to get the job done! + `; + } + + instructions ??= ((await response.json()) as { instructions: string }).instructions; + instructions = ` + If needed, you can get the current time by executing Date.now(). + + ${instructions} + `; + + return instructions; +} diff --git a/packages/mcp-server/src/methods.ts b/packages/mcp-server/src/methods.ts index 28d0b00..e34f196 100644 --- a/packages/mcp-server/src/methods.ts +++ b/packages/mcp-server/src/methods.ts @@ -190,7 +190,12 @@ export const sdkMethods: SdkMethod[] = [ httpMethod: 'post', httpPath: '/transactions/{transactionId}/reject', }, - { clientCallName: 'client.webhooks.unwrap', fullyQualifiedName: 'webhooks.unwrap' }, + { + clientCallName: 'client.webhooks.sendTest', + fullyQualifiedName: 'webhooks.sendTest', + httpMethod: 'post', + httpPath: '/webhooks/test', + }, { clientCallName: 'client.invitations.create', fullyQualifiedName: 'invitations.create', @@ -221,12 +226,6 @@ export const sdkMethods: SdkMethod[] = [ httpMethod: 'post', httpPath: '/sandbox/send', }, - { - clientCallName: 'client.sandbox.sendTestWebhook', - fullyQualifiedName: 'sandbox.sendTestWebhook', - httpMethod: 'post', - httpPath: '/webhooks/test', - }, { clientCallName: 'client.sandbox.uma.receivePayment', fullyQualifiedName: 'sandbox.uma.receivePayment', diff --git a/packages/mcp-server/src/options.ts b/packages/mcp-server/src/options.ts index 32a8871..9e9d15c 100644 --- a/packages/mcp-server/src/options.ts +++ b/packages/mcp-server/src/options.ts @@ -19,8 +19,11 @@ export type McpOptions = { codeAllowHttpGets?: boolean | undefined; codeAllowedMethods?: string[] | undefined; codeBlockedMethods?: string[] | undefined; + codeExecutionMode: McpCodeExecutionMode; }; +export type McpCodeExecutionMode = 'stainless-sandbox' | 'local'; + export function parseCLIOptions(): CLIOptions { const opts = yargs(hideBin(process.argv)) .option('code-allow-http-gets', { @@ -40,6 +43,13 @@ export function parseCLIOptions(): CLIOptions { description: 'Methods to explicitly block for code tool. Evaluated as regular expressions against method fully qualified names. If all code-allow-* flags are unset, then everything is allowed.', }) + .option('code-execution-mode', { + type: 'string', + choices: ['stainless-sandbox', 'local'], + default: 'stainless-sandbox', + description: + "Where to run code execution in code tool; 'stainless-sandbox' will execute code in Stainless-hosted sandboxes whereas 'local' will execute code locally on the MCP server machine.", + }) .option('debug', { type: 'boolean', description: 'Enable debug logging' }) .option('no-tools', { type: 'string', @@ -93,6 +103,7 @@ export function parseCLIOptions(): CLIOptions { codeAllowHttpGets: argv.codeAllowHttpGets, codeAllowedMethods: argv.codeAllowedMethods, codeBlockedMethods: argv.codeBlockedMethods, + codeExecutionMode: argv.codeExecutionMode as McpCodeExecutionMode, transport, port: argv.port, socket: argv.socket, @@ -124,6 +135,7 @@ export function parseQueryOptions(defaultOptions: McpOptions, query: unknown): M : defaultOptions.includeDocsTools; return { + codeExecutionMode: defaultOptions.codeExecutionMode, ...(docsTools !== undefined && { includeDocsTools: docsTools }), }; } diff --git a/packages/mcp-server/src/server.ts b/packages/mcp-server/src/server.ts index c0ba5b2..038b4b7 100644 --- a/packages/mcp-server/src/server.ts +++ b/packages/mcp-server/src/server.ts @@ -11,52 +11,16 @@ import { ClientOptions } from '@lightsparkdev/grid'; import LightsparkGrid from '@lightsparkdev/grid'; import { codeTool } from './code-tool'; import docsSearchTool from './docs-search-tool'; +import { getInstructions } from './instructions'; import { McpOptions } from './options'; import { blockedMethodsForCodeTool } from './methods'; import { HandlerFunction, McpRequestContext, ToolCallResult, McpTool } from './types'; -import { readEnv } from './util'; - -async function getInstructions(stainlessApiKey: string | undefined): Promise { - // Setting the stainless API key is optional, but may be required - // to authenticate requests to the Stainless API. - const response = await fetch( - readEnv('CODE_MODE_INSTRUCTIONS_URL') ?? 'https://api.stainless.com/api/ai/instructions/grid', - { - method: 'GET', - headers: { ...(stainlessApiKey && { Authorization: stainlessApiKey }) }, - }, - ); - - let instructions: string | undefined; - if (!response.ok) { - console.warn( - 'Warning: failed to retrieve MCP server instructions. Proceeding with default instructions...', - ); - - instructions = ` - This is the grid MCP server. You will use Code Mode to help the user perform - actions. You can use search_docs tool to learn about how to take action with this server. Then, - you will write TypeScript code using the execute tool take action. It is CRITICAL that you be - thoughtful and deliberate when executing code. Always try to entirely solve the problem in code - block: it can be as long as you need to get the job done! - `; - } - - instructions ??= ((await response.json()) as { instructions: string }).instructions; - instructions = ` - The current time in Unix timestamps is ${Date.now()}. - - ${instructions} - `; - - return instructions; -} export const newMcpServer = async (stainlessApiKey: string | undefined) => new McpServer( { name: 'lightsparkdev_grid_api', - version: '0.6.0', + version: '0.7.0', }, { instructions: await getInstructions(stainlessApiKey), @@ -91,14 +55,32 @@ export async function initMcpServer(params: { error: logAtLevel('error'), }; - let client = new LightsparkGrid({ - logger, - ...params.clientOptions, - defaultHeaders: { - ...params.clientOptions?.defaultHeaders, - 'X-Stainless-MCP': 'true', - }, - }); + let _client: LightsparkGrid | undefined; + let _clientError: Error | undefined; + let _logLevel: 'debug' | 'info' | 'warn' | 'error' | 'off' | undefined; + + const getClient = (): LightsparkGrid => { + if (_clientError) throw _clientError; + if (!_client) { + try { + _client = new LightsparkGrid({ + logger, + ...params.clientOptions, + defaultHeaders: { + ...params.clientOptions?.defaultHeaders, + 'X-Stainless-MCP': 'true', + }, + }); + if (_logLevel) { + _client = _client.withOptions({ logLevel: _logLevel }); + } + } catch (e) { + _clientError = e instanceof Error ? e : new Error(String(e)); + throw _clientError; + } + } + return _client; + }; const providedTools = selectTools(params.mcpOptions); const toolMap = Object.fromEntries(providedTools.map((mcpTool) => [mcpTool.tool.name, mcpTool])); @@ -116,6 +98,21 @@ export async function initMcpServer(params: { throw new Error(`Unknown tool: ${name}`); } + let client: LightsparkGrid; + try { + client = getClient(); + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Failed to initialize client: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + isError: true, + }; + } + return executeHandler({ handler: mcpTool.handler, reqContext: { @@ -128,24 +125,29 @@ export async function initMcpServer(params: { server.setRequestHandler(SetLevelRequestSchema, async (request) => { const { level } = request.params; + let logLevel: 'debug' | 'info' | 'warn' | 'error' | 'off'; switch (level) { case 'debug': - client = client.withOptions({ logLevel: 'debug' }); + logLevel = 'debug'; break; case 'info': - client = client.withOptions({ logLevel: 'info' }); + logLevel = 'info'; break; case 'notice': case 'warning': - client = client.withOptions({ logLevel: 'warn' }); + logLevel = 'warn'; break; case 'error': - client = client.withOptions({ logLevel: 'error' }); + logLevel = 'error'; break; default: - client = client.withOptions({ logLevel: 'off' }); + logLevel = 'off'; break; } + _logLevel = logLevel; + if (_client) { + _client = _client.withOptions({ logLevel }); + } return {}; }); } @@ -157,6 +159,7 @@ export function selectTools(options?: McpOptions): McpTool[] { const includedTools = [ codeTool({ blockedMethods: blockedMethodsForCodeTool(options), + codeExecutionMode: options?.codeExecutionMode ?? 'stainless-sandbox', }), ]; if (options?.includeDocsTools ?? true) { diff --git a/packages/mcp-server/tests/options.test.ts b/packages/mcp-server/tests/options.test.ts index 7a2d511..1730629 100644 --- a/packages/mcp-server/tests/options.test.ts +++ b/packages/mcp-server/tests/options.test.ts @@ -1,4 +1,4 @@ -import { parseCLIOptions, parseQueryOptions } from '../src/options'; +import { parseCLIOptions } from '../src/options'; // Mock process.argv const mockArgv = (args: string[]) => { @@ -30,21 +30,3 @@ describe('parseCLIOptions', () => { cleanup(); }); }); - -describe('parseQueryOptions', () => { - const defaultOptions = {}; - - it('default parsing should be empty', () => { - const query = ''; - const result = parseQueryOptions(defaultOptions, query); - - expect(result).toEqual({}); - }); - - it('should handle invalid query string gracefully', () => { - const query = 'invalid=value&tools=invalid-operation'; - - // Should throw due to Zod validation for invalid tools - expect(() => parseQueryOptions(defaultOptions, query)).toThrow(); - }); -}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2ce51e9..f06c7fc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -29,6 +29,9 @@ importers: '@typescript-eslint/parser': specifier: 8.31.1 version: 8.31.1(eslint@9.39.1)(typescript@5.8.3) + '@typescript-eslint/typescript-estree': + specifier: 8.31.1 + version: 8.31.1(typescript@5.8.3) eslint: specifier: ^9.39.1 version: 9.39.1 @@ -81,8 +84,8 @@ importers: specifier: workspace:* version: link:../.. '@modelcontextprotocol/sdk': - specifier: ^1.25.2 - version: 1.25.2(hono@4.11.4)(zod@3.25.76) + specifier: ^1.26.0 + version: 1.27.0(zod@3.25.76) '@valtown/deno-http-worker': specifier: ^0.0.21 version: 0.0.21 @@ -628,8 +631,8 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - '@modelcontextprotocol/sdk@1.25.2': - resolution: {integrity: sha512-LZFeo4F9M5qOhC/Uc1aQSrBHxMrvxett+9KLHt7OhcExtoiRN9DKgbZffMP/nxjutWDQpfMDfP3nkHI4X9ijww==} + '@modelcontextprotocol/sdk@1.27.0': + resolution: {integrity: sha512-qOdO524oPMkUsOJTrsH9vz/HN3B5pKyW+9zIW51A9kDMVe7ON70drz1ouoyoyOcfzc+oxhkQ6jWmbyKnlWmYqA==} engines: {node: '>=18'} peerDependencies: '@cfworker/json-schema': ^4.1.1 @@ -1476,8 +1479,8 @@ packages: resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - express-rate-limit@7.5.1: - resolution: {integrity: sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==} + express-rate-limit@8.2.1: + resolution: {integrity: sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g==} engines: {node: '>= 16'} peerDependencies: express: '>= 4.11' @@ -1741,6 +1744,10 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + ip-address@10.0.1: + resolution: {integrity: sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==} + engines: {node: '>= 12'} + ipaddr.js@1.9.1: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} @@ -2836,6 +2843,11 @@ packages: peerDependencies: zod: ^3.25 || ^4 + zod-to-json-schema@3.25.1: + resolution: {integrity: sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==} + peerDependencies: + zod: ^3.25 || ^4 + zod-validation-error@4.0.1: resolution: {integrity: sha512-F3rdaCOHs5ViJ5YTz5zzRtfkQdMdIeKudJAoxy7yB/2ZMEHw73lmCAcQw11r7++20MyGl4WV59EVh7A9rNAyog==} engines: {node: '>=18.0.0'} @@ -3520,7 +3532,7 @@ snapshots: '@jridgewell/resolve-uri': 3.1.1 '@jridgewell/sourcemap-codec': 1.4.15 - '@modelcontextprotocol/sdk@1.25.2(hono@4.11.4)(zod@3.25.76)': + '@modelcontextprotocol/sdk@1.27.0(zod@3.25.76)': dependencies: '@hono/node-server': 1.19.9(hono@4.11.4) ajv: 8.17.1 @@ -3531,15 +3543,15 @@ snapshots: eventsource: 3.0.7 eventsource-parser: 3.0.6 express: 5.2.1 - express-rate-limit: 7.5.1(express@5.2.1) + express-rate-limit: 8.2.1(express@5.2.1) + hono: 4.11.4 jose: 6.1.3 json-schema-typed: 8.0.2 pkce-challenge: 5.0.0 raw-body: 3.0.1 zod: 3.25.76 - zod-to-json-schema: 3.25.0(zod@3.25.76) + zod-to-json-schema: 3.25.1(zod@3.25.76) transitivePeerDependencies: - - hono - supports-color '@nodelib/fs.scandir@2.1.5': @@ -4508,9 +4520,10 @@ snapshots: jest-message-util: 29.7.0 jest-util: 29.7.0 - express-rate-limit@7.5.1(express@5.2.1): + express-rate-limit@8.2.1(express@5.2.1): dependencies: express: 5.2.1 + ip-address: 10.0.1 express@5.2.1: dependencies: @@ -4806,6 +4819,8 @@ snapshots: inherits@2.0.4: {} + ip-address@10.0.1: {} + ipaddr.js@1.9.1: {} is-arrayish@0.2.1: {} @@ -6136,6 +6151,10 @@ snapshots: dependencies: zod: 3.25.76 + zod-to-json-schema@3.25.1(zod@3.25.76): + dependencies: + zod: 3.25.76 + zod-validation-error@4.0.1(zod@3.25.76): dependencies: zod: 3.25.76 diff --git a/release-please-config.json b/release-please-config.json index c45c17b..80f8a85 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -67,6 +67,16 @@ "type": "json", "path": "packages/mcp-server/package.json", "jsonpath": "$.version" + }, + { + "type": "json", + "path": "packages/mcp-server/manifest.json", + "jsonpath": "$.version" + }, + { + "type": "json", + "path": "jsr.json", + "jsonpath": "$.version" } ] } diff --git a/scripts/build-deno b/scripts/build-deno new file mode 100755 index 0000000..028d9df --- /dev/null +++ b/scripts/build-deno @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +set -exuo pipefail + +cd "$(dirname "$0")/.." + +rm -rf dist-deno; mkdir dist-deno +cp -rp src/* jsr.json dist-deno + +for file in README.md LICENSE CHANGELOG.md; do + if [ -e "${file}" ]; then cp "${file}" dist-deno; fi +done + +node scripts/utils/convert-jsr-readme.cjs ./dist-deno/README.md diff --git a/scripts/detect-breaking-changes b/scripts/detect-breaking-changes index cb0fef2..9e74117 100755 --- a/scripts/detect-breaking-changes +++ b/scripts/detect-breaking-changes @@ -19,6 +19,7 @@ TEST_PATHS=( tests/api-resources/receiver.test.ts tests/api-resources/quotes.test.ts tests/api-resources/transactions.test.ts + tests/api-resources/webhooks.test.ts tests/api-resources/invitations.test.ts tests/api-resources/sandbox/sandbox.test.ts tests/api-resources/sandbox/uma.test.ts diff --git a/scripts/lint b/scripts/lint index 3ffb78a..97a3df8 100755 --- a/scripts/lint +++ b/scripts/lint @@ -19,3 +19,5 @@ node scripts/utils/attw-report.cjs echo "==> Running publint" ./node_modules/.bin/publint dist +echo "==> Running jsr publish --dry-run" +(cd dist-deno && npx jsr publish --dry-run --allow-dirty) diff --git a/scripts/mock b/scripts/mock deleted file mode 100755 index 0b28f6e..0000000 --- a/scripts/mock +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env bash - -set -e - -cd "$(dirname "$0")/.." - -if [[ -n "$1" && "$1" != '--'* ]]; then - URL="$1" - shift -else - URL="$(grep 'openapi_spec_url' .stats.yml | cut -d' ' -f2)" -fi - -# Check if the URL is empty -if [ -z "$URL" ]; then - echo "Error: No OpenAPI spec path/url provided or found in .stats.yml" - exit 1 -fi - -echo "==> Starting mock server with URL ${URL}" - -# Run prism mock on the given spec -if [ "$1" == "--daemon" ]; then - npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" &> .prism.log & - - # Wait for server to come online - echo -n "Waiting for server" - while ! grep -q "✖ fatal\|Prism is listening" ".prism.log" ; do - echo -n "." - sleep 0.1 - done - - if grep -q "✖ fatal" ".prism.log"; then - cat .prism.log - exit 1 - fi - - echo -else - npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" -fi diff --git a/scripts/test b/scripts/test index 7bce051..548da9b 100755 --- a/scripts/test +++ b/scripts/test @@ -4,53 +4,7 @@ set -e cd "$(dirname "$0")/.." -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[0;33m' -NC='\033[0m' # No Color -function prism_is_running() { - curl --silent "http://localhost:4010" >/dev/null 2>&1 -} - -kill_server_on_port() { - pids=$(lsof -t -i tcp:"$1" || echo "") - if [ "$pids" != "" ]; then - kill "$pids" - echo "Stopped $pids." - fi -} - -function is_overriding_api_base_url() { - [ -n "$TEST_API_BASE_URL" ] -} - -if ! is_overriding_api_base_url && ! prism_is_running ; then - # When we exit this script, make sure to kill the background mock server process - trap 'kill_server_on_port 4010' EXIT - - # Start the dev server - ./scripts/mock --daemon -fi - -if is_overriding_api_base_url ; then - echo -e "${GREEN}✔ Running tests against ${TEST_API_BASE_URL}${NC}" - echo -elif ! prism_is_running ; then - echo -e "${RED}ERROR:${NC} The test suite will not run without a mock Prism server" - echo -e "running against your OpenAPI spec." - echo - echo -e "To run the server, pass in the path or url of your OpenAPI" - echo -e "spec to the prism command:" - echo - echo -e " \$ ${YELLOW}npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock path/to/your.openapi.yml${NC}" - echo - - exit 1 -else - echo -e "${GREEN}✔ Mock prism server is running with your OpenAPI spec${NC}" - echo -fi echo "==> Running tests" ./node_modules/.bin/jest "$@" diff --git a/scripts/utils/convert-jsr-readme.cjs b/scripts/utils/convert-jsr-readme.cjs new file mode 100644 index 0000000..34e4778 --- /dev/null +++ b/scripts/utils/convert-jsr-readme.cjs @@ -0,0 +1,140 @@ +const fs = require('fs'); +const { parse } = require('@typescript-eslint/parser'); +const { TSError } = require('@typescript-eslint/typescript-estree'); + +/** + * Quick and dirty AST traversal + */ +function traverse(node, visitor) { + if (!node || typeof node.type !== 'string') return; + visitor.node?.(node); + visitor[node.type]?.(node); + for (const key in node) { + const value = node[key]; + if (Array.isArray(value)) { + for (const elem of value) traverse(elem, visitor); + } else if (value instanceof Object) { + traverse(value, visitor); + } + } +} + +/** + * Helper method for replacing arbitrary ranges of text in input code. + */ +function replaceRanges(code, replacer) { + const replacements = []; + replacer({ replace: (range, replacement) => replacements.push({ range, replacement }) }); + + if (!replacements.length) return code; + replacements.sort((a, b) => a.range[0] - b.range[0]); + const overlapIndex = replacements.findIndex( + (r, index) => index > 0 && replacements[index - 1].range[1] > r.range[0], + ); + if (overlapIndex >= 0) { + throw new Error( + `replacements overlap: ${JSON.stringify(replacements[overlapIndex - 1])} and ${JSON.stringify( + replacements[overlapIndex], + )}`, + ); + } + + const parts = []; + let end = 0; + for (const { + range: [from, to], + replacement, + } of replacements) { + if (from > end) parts.push(code.substring(end, from)); + parts.push(replacement); + end = to; + } + if (end < code.length) parts.push(code.substring(end)); + return parts.join(''); +} + +function replaceProcessEnv(content) { + // Replace process.env['KEY'] and process.env.KEY with Deno.env.get('KEY') + return content.replace(/process\.env(?:\.|\[['"])(.+?)(?:['"]\])/g, "Deno.env.get('$1')"); +} + +function replaceProcessStdout(content) { + return content.replace(/process\.stdout.write\(([^)]+)\)/g, 'Deno.stdout.writeSync($1)'); +} + +function replaceInstallationDirections(content) { + // Remove npm installation section + return content.replace(/```sh\nnpm install.*?\n```.*### Installation from JSR\n\n/s, ''); +} + +/** + * Maps over module paths in imports and exports + */ +function replaceImports(code, config) { + try { + const ast = parse(code, { sourceType: 'module', range: true }); + return replaceRanges(code, ({ replace }) => + traverse(ast, { + node(node) { + switch (node.type) { + case 'ImportDeclaration': + case 'ExportNamedDeclaration': + case 'ExportAllDeclaration': + case 'ImportExpression': + if (node.source) { + const { range, value } = node.source; + if (value.startsWith(config.npm)) { + replace(range, JSON.stringify(value.replace(config.npm, config.jsr))); + } + } + } + }, + }), + ); + } catch (e) { + if (e instanceof TSError) { + // This can error if the code block is not valid TS, in this case give up trying to transform the imports. + console.warn(`Original codeblock could not be parsed, replace import skipped: ${e}\n\n${code}`); + return code; + } + throw e; + } +} + +function processReadme(config, file) { + try { + let readmeContent = fs.readFileSync(file, 'utf8'); + + // First replace installation directions + readmeContent = replaceInstallationDirections(readmeContent); + + // Replace content in all code blocks with a single regex + readmeContent = readmeContent.replaceAll( + /```(?:typescript|ts|javascript|js)\n([\s\S]*?)```/g, + (match, codeBlock) => { + try { + let transformedCode = codeBlock.trim(); + transformedCode = replaceImports(transformedCode, config); + transformedCode = replaceProcessEnv(transformedCode); + transformedCode = replaceProcessStdout(transformedCode); + return '```typescript\n' + transformedCode + '\n```'; + } catch (error) { + console.warn(`Failed to transform code block: ${error}\n\n${codeBlock}`); + return match; // Return original code block if transformation fails + } + }, + ); + + fs.writeFileSync(file, readmeContent); + } catch (error) { + console.error('Error processing README:', error); + throw error; + } +} + +const config = { + npm: '@lightsparkdev/grid', + jsr: '@lightsparkdev/grid', +}; + +processReadme(config, process.argv[2]); diff --git a/src/client.ts b/src/client.ts index de09d12..dcef490 100644 --- a/src/client.ts +++ b/src/client.ts @@ -41,9 +41,6 @@ import { PlaidSubmitPublicTokenParams, } from './resources/plaid'; import { - BaseDestination, - BasePaymentAccountInfo, - BaseQuoteSource, Currency, OutgoingRateDetails, PaymentInstructions, @@ -51,7 +48,6 @@ import { QuoteCreateParams, QuoteDestinationOneOf, QuoteListParams, - QuoteSourceOneOf, Quotes, QuotesDefaultPagination, } from './resources/quotes'; @@ -74,12 +70,13 @@ import { } from './resources/tokens'; import { BaseTransactionSource, - CounterpartyInformation, IncomingTransaction, TransactionApproveParams, - TransactionDestinationOneOf, TransactionListParams, + TransactionListResponse, + TransactionListResponsesDefaultPagination, TransactionRejectParams, + TransactionRetrieveResponse, TransactionSourceOneOf, TransactionStatus, TransactionType, @@ -90,40 +87,28 @@ import { Transaction, TransferIn, TransferInCreateParams, + TransferInCreateResponse, } from './resources/transfer-in'; -import { TransferOut, TransferOutCreateParams } from './resources/transfer-out'; +import { TransferOut, TransferOutCreateParams, TransferOutCreateResponse } from './resources/transfer-out'; import { UmaProviderListParams, UmaProviderListResponse, UmaProviderListResponsesDefaultPagination, UmaProviders, } from './resources/uma-providers'; -import { - AccountStatusWebhookEvent, - BulkUploadWebhookEvent, - IncomingPaymentWebhookEvent, - InvitationClaimedWebhookEvent, - KYCStatusWebhookEvent, - OutgoingPaymentWebhookEvent, - TestWebhookWebhookEvent, - UnwrapWebhookEvent, - Webhooks, -} from './resources/webhooks'; +import { WebhookSendTestResponse, Webhooks } from './resources/webhooks'; import { Customer, CustomerCreate, CustomerCreateParams, - CustomerDeleteResponse, CustomerGetKYCLinkParams, CustomerGetKYCLinkResponse, CustomerListInternalAccountsParams, CustomerListParams, CustomerOneOf, CustomerOneovesDefaultPagination, - CustomerRetrieveResponse, CustomerUpdate, CustomerUpdateParams, - CustomerUpdateResponse, Customers, } from './resources/customers/customers'; import { @@ -131,12 +116,7 @@ import { PlatformListInternalAccountsParams, PlatformListInternalAccountsResponse, } from './resources/platform/platform'; -import { - Sandbox, - SandboxSendFundsParams, - SandboxSendFundsResponse, - SandboxSendTestWebhookResponse, -} from './resources/sandbox/sandbox'; +import { Sandbox, SandboxSendFundsParams, SandboxSendFundsResponse } from './resources/sandbox/sandbox'; import { type Fetch } from './internal/builtin-types'; import { HeadersLike, NullableHeaders, buildHeaders } from './internal/headers'; import { FinalRequestOptions, RequestOptions } from './internal/request-options'; @@ -971,9 +951,6 @@ export declare namespace LightsparkGrid { type CustomerCreate as CustomerCreate, type CustomerOneOf as CustomerOneOf, type CustomerUpdate as CustomerUpdate, - type CustomerRetrieveResponse as CustomerRetrieveResponse, - type CustomerUpdateResponse as CustomerUpdateResponse, - type CustomerDeleteResponse as CustomerDeleteResponse, type CustomerGetKYCLinkResponse as CustomerGetKYCLinkResponse, type CustomerOneovesDefaultPagination as CustomerOneovesDefaultPagination, type CustomerCreateParams as CustomerCreateParams, @@ -1000,10 +977,15 @@ export declare namespace LightsparkGrid { TransferIn as TransferIn, type BaseTransactionDestination as BaseTransactionDestination, type Transaction as Transaction, + type TransferInCreateResponse as TransferInCreateResponse, type TransferInCreateParams as TransferInCreateParams, }; - export { TransferOut as TransferOut, type TransferOutCreateParams as TransferOutCreateParams }; + export { + TransferOut as TransferOut, + type TransferOutCreateResponse as TransferOutCreateResponse, + type TransferOutCreateParams as TransferOutCreateParams, + }; export { Receiver as Receiver, @@ -1017,15 +999,11 @@ export declare namespace LightsparkGrid { export { Quotes as Quotes, - type BaseDestination as BaseDestination, - type BasePaymentAccountInfo as BasePaymentAccountInfo, - type BaseQuoteSource as BaseQuoteSource, type Currency as Currency, type OutgoingRateDetails as OutgoingRateDetails, type PaymentInstructions as PaymentInstructions, type Quote as Quote, type QuoteDestinationOneOf as QuoteDestinationOneOf, - type QuoteSourceOneOf as QuoteSourceOneOf, type QuotesDefaultPagination as QuotesDefaultPagination, type QuoteCreateParams as QuoteCreateParams, type QuoteListParams as QuoteListParams, @@ -1034,28 +1012,19 @@ export declare namespace LightsparkGrid { export { Transactions as Transactions, type BaseTransactionSource as BaseTransactionSource, - type CounterpartyInformation as CounterpartyInformation, type IncomingTransaction as IncomingTransaction, - type TransactionDestinationOneOf as TransactionDestinationOneOf, type TransactionSourceOneOf as TransactionSourceOneOf, type TransactionStatus as TransactionStatus, type TransactionType as TransactionType, + type TransactionRetrieveResponse as TransactionRetrieveResponse, + type TransactionListResponse as TransactionListResponse, + type TransactionListResponsesDefaultPagination as TransactionListResponsesDefaultPagination, type TransactionListParams as TransactionListParams, type TransactionApproveParams as TransactionApproveParams, type TransactionRejectParams as TransactionRejectParams, }; - export { - Webhooks as Webhooks, - type IncomingPaymentWebhookEvent as IncomingPaymentWebhookEvent, - type OutgoingPaymentWebhookEvent as OutgoingPaymentWebhookEvent, - type TestWebhookWebhookEvent as TestWebhookWebhookEvent, - type BulkUploadWebhookEvent as BulkUploadWebhookEvent, - type InvitationClaimedWebhookEvent as InvitationClaimedWebhookEvent, - type KYCStatusWebhookEvent as KYCStatusWebhookEvent, - type AccountStatusWebhookEvent as AccountStatusWebhookEvent, - type UnwrapWebhookEvent as UnwrapWebhookEvent, - }; + export { Webhooks as Webhooks, type WebhookSendTestResponse as WebhookSendTestResponse }; export { Invitations as Invitations, @@ -1068,7 +1037,6 @@ export declare namespace LightsparkGrid { export { Sandbox as Sandbox, type SandboxSendFundsResponse as SandboxSendFundsResponse, - type SandboxSendTestWebhookResponse as SandboxSendTestWebhookResponse, type SandboxSendFundsParams as SandboxSendFundsParams, }; diff --git a/src/resources/customers/bulk.ts b/src/resources/customers/bulk.ts index f8c0fae..ff1d210 100644 --- a/src/resources/customers/bulk.ts +++ b/src/resources/customers/bulk.ts @@ -110,7 +110,7 @@ export interface BulkGetJobStatusResponse { /** * Unique identifier for the bulk import job */ - jobId: string; + id: string; progress: BulkGetJobStatusResponse.Progress; diff --git a/src/resources/customers/customers.ts b/src/resources/customers/customers.ts index 0217842..55e1a21 100644 --- a/src/resources/customers/customers.ts +++ b/src/resources/customers/customers.ts @@ -6,10 +6,13 @@ import * as BulkAPI from './bulk'; import { Bulk, BulkGetJobStatusResponse, BulkUploadCsvParams, BulkUploadCsvResponse } from './bulk'; import * as ExternalAccountsAPI from './external-accounts'; import { + Address, BaseWalletInfo, - BeneficiaryOneOf, - CadAccountInfo, - ClabeAccountInfo, + BrlBeneficiary, + BrlExternalAccountInfo, + BusinessBeneficiary, + DkkBeneficiary, + DkkExternalAccountInfo, ExternalAccount, ExternalAccountCreate, ExternalAccountCreateParams, @@ -17,19 +20,33 @@ import { ExternalAccountListParams, ExternalAccounts, ExternalAccountsDefaultPagination, - GbpAccountInfo, - IbanAccountInfo, + GbpBeneficiary, + GbpExternalAccountInfo, + HkdBeneficiary, + HkdExternalAccountInfo, + IdrBeneficiary, + IdrExternalAccountInfo, + InrBeneficiary, + InrExternalAccountInfo, LightningWalletInfo, - NgnAccountInfo, - PhpAccountInfo, - PixAccountInfo, + MxnBeneficiary, + MxnExternalAccountInfo, + MyrBeneficiary, + MyrExternalAccountInfo, + PhpBeneficiary, + PhpExternalAccountInfo, PolygonWalletInfo, - SgdAccountInfo, + SgdBeneficiary, + SgdExternalAccountInfo, SolanaWalletInfo, SparkWalletInfo, + ThbBeneficiary, + ThbExternalAccountInfo, TronWalletInfo, - UpiAccountInfo, - UsAccountInfo, + UsdBeneficiary, + UsdExternalAccountInfo, + VndBeneficiary, + VndExternalAccountInfo, } from './external-accounts'; import * as InternalAccountsAPI from '../sandbox/internal-accounts'; import { InternalAccountsDefaultPagination } from '../sandbox/internal-accounts'; @@ -68,12 +85,12 @@ export class Customers extends APIResource { * * @example * ```ts - * const customer = await client.customers.retrieve( + * const customerOneOf = await client.customers.retrieve( * 'customerId', * ); * ``` */ - retrieve(customerID: string, options?: RequestOptions): APIPromise { + retrieve(customerID: string, options?: RequestOptions): APIPromise { return this._client.get(path`/customers/${customerID}`, options); } @@ -82,7 +99,7 @@ export class Customers extends APIResource { * * @example * ```ts - * const customer = await client.customers.update( + * const customerOneOf = await client.customers.update( * 'customerId', * { UpdateCustomerRequest: { customerType: 'INDIVIDUAL' } }, * ); @@ -92,7 +109,7 @@ export class Customers extends APIResource { customerID: string, params: CustomerUpdateParams, options?: RequestOptions, - ): APIPromise { + ): APIPromise { const { UpdateCustomerRequest } = params; return this._client.patch(path`/customers/${customerID}`, { body: UpdateCustomerRequest, ...options }); } @@ -122,12 +139,12 @@ export class Customers extends APIResource { * * @example * ```ts - * const customer = await client.customers.delete( + * const customerOneOf = await client.customers.delete( * 'customerId', * ); * ``` */ - delete(customerID: string, options?: RequestOptions): APIPromise { + delete(customerID: string, options?: RequestOptions): APIPromise { return this._client.delete(path`/customers/${customerID}`, options); } @@ -246,7 +263,7 @@ export namespace CustomerOneOf { export interface IndividualCustomer extends CustomersAPI.Customer { customerType: 'INDIVIDUAL'; - address?: IndividualCustomer.Address; + address?: ExternalAccountsAPI.Address; /** * Date of birth in ISO 8601 format (YYYY-MM-DD) @@ -264,691 +281,10 @@ export namespace CustomerOneOf { nationality?: string; } - export namespace IndividualCustomer { - export interface Address { - /** - * Country code (ISO 3166-1 alpha-2) - */ - country: string; - - /** - * Street address line 1 - */ - line1: string; - - /** - * Postal/ZIP code - */ - postalCode: string; - - /** - * City - */ - city?: string; - - /** - * Street address line 2 - */ - line2?: string; - - /** - * State/Province/Region - */ - state?: string; - } - } - - export interface BusinessCustomer extends CustomersAPI.Customer { - customerType: 'BUSINESS'; - - address?: BusinessCustomer.Address; - - beneficialOwners?: Array; - - businessInfo?: BusinessCustomer.BusinessInfo; - } - - export namespace BusinessCustomer { - export interface Address { - /** - * Country code (ISO 3166-1 alpha-2) - */ - country: string; - - /** - * Street address line 1 - */ - line1: string; - - /** - * Postal/ZIP code - */ - postalCode: string; - - /** - * City - */ - city?: string; - - /** - * Street address line 2 - */ - line2?: string; - - /** - * State/Province/Region - */ - state?: string; - } - - export interface BeneficialOwner { - /** - * Individual's full name - */ - fullName: string; - - /** - * Type of individual in the corporation - */ - individualType: - | 'DIRECTOR' - | 'CONTROL_PERSON' - | 'BUSINESS_POINT_OF_CONTACT' - | 'TRUSTEE' - | 'SETTLOR' - | 'GENERAL_PARTNER'; - - address?: BeneficialOwner.Address; - - /** - * Date of birth in ISO 8601 format (YYYY-MM-DD) - */ - birthDate?: string; - - /** - * Email address of the individual - */ - emailAddress?: string; - - /** - * Country code (ISO 3166-1 alpha-2) - */ - nationality?: string; - - /** - * Percent of ownership when individual type is beneficial owner - */ - percentageOwnership?: number; - - /** - * Phone number of the individual in E.164 format - */ - phoneNumber?: string; - - /** - * Tax identification number of the individual. This could be a Social Security - * Number (SSN) for US individuals, Tax Identification Number (TIN) for non-US - * individuals, or a Passport Number. - */ - taxId?: string; - - /** - * Title at company - */ - title?: string; - } - - export namespace BeneficialOwner { - export interface Address { - /** - * Country code (ISO 3166-1 alpha-2) - */ - country: string; - - /** - * Street address line 1 - */ - line1: string; - - /** - * Postal/ZIP code - */ - postalCode: string; - - /** - * City - */ - city?: string; - - /** - * Street address line 2 - */ - line2?: string; - - /** - * State/Province/Region - */ - state?: string; - } - } - - export interface BusinessInfo { - /** - * Legal name of the business - */ - legalName: string; - - /** - * Business registration number - */ - registrationNumber?: string; - - /** - * Tax identification number - */ - taxId?: string; - } - } -} - -export interface CustomerUpdate { - /** - * Optional UMA address identifier. If provided, the customer's UMA address will be - * updated. This is an optional identifier to route payments to the customer. - */ - umaAddress?: string; -} - -export type CustomerRetrieveResponse = - | CustomerRetrieveResponse.IndividualCustomer - | CustomerRetrieveResponse.BusinessCustomer; - -export namespace CustomerRetrieveResponse { - export interface IndividualCustomer extends CustomersAPI.Customer { - customerType: 'INDIVIDUAL'; - - address?: IndividualCustomer.Address; - - /** - * Date of birth in ISO 8601 format (YYYY-MM-DD) - */ - birthDate?: string; - - /** - * Individual's full name - */ - fullName?: string; - - /** - * Country code (ISO 3166-1 alpha-2) - */ - nationality?: string; - } - - export namespace IndividualCustomer { - export interface Address { - /** - * Country code (ISO 3166-1 alpha-2) - */ - country: string; - - /** - * Street address line 1 - */ - line1: string; - - /** - * Postal/ZIP code - */ - postalCode: string; - - /** - * City - */ - city?: string; - - /** - * Street address line 2 - */ - line2?: string; - - /** - * State/Province/Region - */ - state?: string; - } - } - - export interface BusinessCustomer extends CustomersAPI.Customer { - customerType: 'BUSINESS'; - - address?: BusinessCustomer.Address; - - beneficialOwners?: Array; - - businessInfo?: BusinessCustomer.BusinessInfo; - } - - export namespace BusinessCustomer { - export interface Address { - /** - * Country code (ISO 3166-1 alpha-2) - */ - country: string; - - /** - * Street address line 1 - */ - line1: string; - - /** - * Postal/ZIP code - */ - postalCode: string; - - /** - * City - */ - city?: string; - - /** - * Street address line 2 - */ - line2?: string; - - /** - * State/Province/Region - */ - state?: string; - } - - export interface BeneficialOwner { - /** - * Individual's full name - */ - fullName: string; - - /** - * Type of individual in the corporation - */ - individualType: - | 'DIRECTOR' - | 'CONTROL_PERSON' - | 'BUSINESS_POINT_OF_CONTACT' - | 'TRUSTEE' - | 'SETTLOR' - | 'GENERAL_PARTNER'; - - address?: BeneficialOwner.Address; - - /** - * Date of birth in ISO 8601 format (YYYY-MM-DD) - */ - birthDate?: string; - - /** - * Email address of the individual - */ - emailAddress?: string; - - /** - * Country code (ISO 3166-1 alpha-2) - */ - nationality?: string; - - /** - * Percent of ownership when individual type is beneficial owner - */ - percentageOwnership?: number; - - /** - * Phone number of the individual in E.164 format - */ - phoneNumber?: string; - - /** - * Tax identification number of the individual. This could be a Social Security - * Number (SSN) for US individuals, Tax Identification Number (TIN) for non-US - * individuals, or a Passport Number. - */ - taxId?: string; - - /** - * Title at company - */ - title?: string; - } - - export namespace BeneficialOwner { - export interface Address { - /** - * Country code (ISO 3166-1 alpha-2) - */ - country: string; - - /** - * Street address line 1 - */ - line1: string; - - /** - * Postal/ZIP code - */ - postalCode: string; - - /** - * City - */ - city?: string; - - /** - * Street address line 2 - */ - line2?: string; - - /** - * State/Province/Region - */ - state?: string; - } - } - - export interface BusinessInfo { - /** - * Legal name of the business - */ - legalName: string; - - /** - * Business registration number - */ - registrationNumber?: string; - - /** - * Tax identification number - */ - taxId?: string; - } - } -} - -export type CustomerUpdateResponse = - | CustomerUpdateResponse.IndividualCustomer - | CustomerUpdateResponse.BusinessCustomer; - -export namespace CustomerUpdateResponse { - export interface IndividualCustomer extends CustomersAPI.Customer { - customerType: 'INDIVIDUAL'; - - address?: IndividualCustomer.Address; - - /** - * Date of birth in ISO 8601 format (YYYY-MM-DD) - */ - birthDate?: string; - - /** - * Individual's full name - */ - fullName?: string; - - /** - * Country code (ISO 3166-1 alpha-2) - */ - nationality?: string; - } - - export namespace IndividualCustomer { - export interface Address { - /** - * Country code (ISO 3166-1 alpha-2) - */ - country: string; - - /** - * Street address line 1 - */ - line1: string; - - /** - * Postal/ZIP code - */ - postalCode: string; - - /** - * City - */ - city?: string; - - /** - * Street address line 2 - */ - line2?: string; - - /** - * State/Province/Region - */ - state?: string; - } - } - - export interface BusinessCustomer extends CustomersAPI.Customer { - customerType: 'BUSINESS'; - - address?: BusinessCustomer.Address; - - beneficialOwners?: Array; - - businessInfo?: BusinessCustomer.BusinessInfo; - } - - export namespace BusinessCustomer { - export interface Address { - /** - * Country code (ISO 3166-1 alpha-2) - */ - country: string; - - /** - * Street address line 1 - */ - line1: string; - - /** - * Postal/ZIP code - */ - postalCode: string; - - /** - * City - */ - city?: string; - - /** - * Street address line 2 - */ - line2?: string; - - /** - * State/Province/Region - */ - state?: string; - } - - export interface BeneficialOwner { - /** - * Individual's full name - */ - fullName: string; - - /** - * Type of individual in the corporation - */ - individualType: - | 'DIRECTOR' - | 'CONTROL_PERSON' - | 'BUSINESS_POINT_OF_CONTACT' - | 'TRUSTEE' - | 'SETTLOR' - | 'GENERAL_PARTNER'; - - address?: BeneficialOwner.Address; - - /** - * Date of birth in ISO 8601 format (YYYY-MM-DD) - */ - birthDate?: string; - - /** - * Email address of the individual - */ - emailAddress?: string; - - /** - * Country code (ISO 3166-1 alpha-2) - */ - nationality?: string; - - /** - * Percent of ownership when individual type is beneficial owner - */ - percentageOwnership?: number; - - /** - * Phone number of the individual in E.164 format - */ - phoneNumber?: string; - - /** - * Tax identification number of the individual. This could be a Social Security - * Number (SSN) for US individuals, Tax Identification Number (TIN) for non-US - * individuals, or a Passport Number. - */ - taxId?: string; - - /** - * Title at company - */ - title?: string; - } - - export namespace BeneficialOwner { - export interface Address { - /** - * Country code (ISO 3166-1 alpha-2) - */ - country: string; - - /** - * Street address line 1 - */ - line1: string; - - /** - * Postal/ZIP code - */ - postalCode: string; - - /** - * City - */ - city?: string; - - /** - * Street address line 2 - */ - line2?: string; - - /** - * State/Province/Region - */ - state?: string; - } - } - - export interface BusinessInfo { - /** - * Legal name of the business - */ - legalName: string; - - /** - * Business registration number - */ - registrationNumber?: string; - - /** - * Tax identification number - */ - taxId?: string; - } - } -} - -export type CustomerDeleteResponse = - | CustomerDeleteResponse.IndividualCustomer - | CustomerDeleteResponse.BusinessCustomer; - -export namespace CustomerDeleteResponse { - export interface IndividualCustomer extends CustomersAPI.Customer { - customerType: 'INDIVIDUAL'; - - address?: IndividualCustomer.Address; - - /** - * Date of birth in ISO 8601 format (YYYY-MM-DD) - */ - birthDate?: string; - - /** - * Individual's full name - */ - fullName?: string; - - /** - * Country code (ISO 3166-1 alpha-2) - */ - nationality?: string; - } - - export namespace IndividualCustomer { - export interface Address { - /** - * Country code (ISO 3166-1 alpha-2) - */ - country: string; - - /** - * Street address line 1 - */ - line1: string; - - /** - * Postal/ZIP code - */ - postalCode: string; - - /** - * City - */ - city?: string; - - /** - * Street address line 2 - */ - line2?: string; - - /** - * State/Province/Region - */ - state?: string; - } - } - export interface BusinessCustomer extends CustomersAPI.Customer { customerType: 'BUSINESS'; - address?: BusinessCustomer.Address; + address?: ExternalAccountsAPI.Address; beneficialOwners?: Array; @@ -956,38 +292,6 @@ export namespace CustomerDeleteResponse { } export namespace BusinessCustomer { - export interface Address { - /** - * Country code (ISO 3166-1 alpha-2) - */ - country: string; - - /** - * Street address line 1 - */ - line1: string; - - /** - * Postal/ZIP code - */ - postalCode: string; - - /** - * City - */ - city?: string; - - /** - * Street address line 2 - */ - line2?: string; - - /** - * State/Province/Region - */ - state?: string; - } - export interface BeneficialOwner { /** * Individual's full name @@ -1005,7 +309,7 @@ export namespace CustomerDeleteResponse { | 'SETTLOR' | 'GENERAL_PARTNER'; - address?: BeneficialOwner.Address; + address?: ExternalAccountsAPI.Address; /** * Date of birth in ISO 8601 format (YYYY-MM-DD) @@ -1045,40 +349,6 @@ export namespace CustomerDeleteResponse { title?: string; } - export namespace BeneficialOwner { - export interface Address { - /** - * Country code (ISO 3166-1 alpha-2) - */ - country: string; - - /** - * Street address line 1 - */ - line1: string; - - /** - * Postal/ZIP code - */ - postalCode: string; - - /** - * City - */ - city?: string; - - /** - * Street address line 2 - */ - line2?: string; - - /** - * State/Province/Region - */ - state?: string; - } - } - export interface BusinessInfo { /** * Legal name of the business @@ -1098,6 +368,14 @@ export namespace CustomerDeleteResponse { } } +export interface CustomerUpdate { + /** + * Optional UMA address identifier. If provided, the customer's UMA address will be + * updated. This is an optional identifier to route payments to the customer. + */ + umaAddress?: string; +} + export interface CustomerGetKYCLinkResponse { /** * The customer id of the newly created customer on the system @@ -1125,7 +403,7 @@ export namespace CustomerCreateParams { export interface IndividualCustomerCreateRequest extends CustomersAPI.CustomerCreate { customerType: 'INDIVIDUAL'; - address?: IndividualCustomerCreateRequest.Address; + address?: ExternalAccountsAPI.Address; /** * Date of birth in ISO 8601 format (YYYY-MM-DD) @@ -1143,44 +421,10 @@ export namespace CustomerCreateParams { nationality?: string; } - export namespace IndividualCustomerCreateRequest { - export interface Address { - /** - * Country code (ISO 3166-1 alpha-2) - */ - country: string; - - /** - * Street address line 1 - */ - line1: string; - - /** - * Postal/ZIP code - */ - postalCode: string; - - /** - * City - */ - city?: string; - - /** - * Street address line 2 - */ - line2?: string; - - /** - * State/Province/Region - */ - state?: string; - } - } - export interface BusinessCustomerCreateRequest extends CustomersAPI.CustomerCreate { customerType: 'BUSINESS'; - address?: BusinessCustomerCreateRequest.Address; + address?: ExternalAccountsAPI.Address; beneficialOwners?: Array; @@ -1188,38 +432,6 @@ export namespace CustomerCreateParams { } export namespace BusinessCustomerCreateRequest { - export interface Address { - /** - * Country code (ISO 3166-1 alpha-2) - */ - country: string; - - /** - * Street address line 1 - */ - line1: string; - - /** - * Postal/ZIP code - */ - postalCode: string; - - /** - * City - */ - city?: string; - - /** - * Street address line 2 - */ - line2?: string; - - /** - * State/Province/Region - */ - state?: string; - } - export interface BeneficialOwner { /** * Individual's full name @@ -1237,7 +449,7 @@ export namespace CustomerCreateParams { | 'SETTLOR' | 'GENERAL_PARTNER'; - address?: BeneficialOwner.Address; + address?: ExternalAccountsAPI.Address; /** * Date of birth in ISO 8601 format (YYYY-MM-DD) @@ -1277,40 +489,6 @@ export namespace CustomerCreateParams { title?: string; } - export namespace BeneficialOwner { - export interface Address { - /** - * Country code (ISO 3166-1 alpha-2) - */ - country: string; - - /** - * Street address line 1 - */ - line1: string; - - /** - * Postal/ZIP code - */ - postalCode: string; - - /** - * City - */ - city?: string; - - /** - * Street address line 2 - */ - line2?: string; - - /** - * State/Province/Region - */ - state?: string; - } - } - export interface BusinessInfo { /** * Legal name of the business @@ -1340,7 +518,7 @@ export namespace CustomerUpdateParams { export interface IndividualCustomerUpdateRequest extends CustomersAPI.CustomerUpdate { customerType: 'INDIVIDUAL'; - address?: IndividualCustomerUpdateRequest.Address; + address?: ExternalAccountsAPI.Address; /** * Date of birth in ISO 8601 format (YYYY-MM-DD) @@ -1358,44 +536,10 @@ export namespace CustomerUpdateParams { nationality?: string; } - export namespace IndividualCustomerUpdateRequest { - export interface Address { - /** - * Country code (ISO 3166-1 alpha-2) - */ - country: string; - - /** - * Street address line 1 - */ - line1: string; - - /** - * Postal/ZIP code - */ - postalCode: string; - - /** - * City - */ - city?: string; - - /** - * Street address line 2 - */ - line2?: string; - - /** - * State/Province/Region - */ - state?: string; - } - } - export interface BusinessCustomerUpdateRequest extends CustomersAPI.CustomerUpdate { customerType: 'BUSINESS'; - address?: BusinessCustomerUpdateRequest.Address; + address?: ExternalAccountsAPI.Address; beneficialOwners?: Array; @@ -1406,38 +550,6 @@ export namespace CustomerUpdateParams { } export namespace BusinessCustomerUpdateRequest { - export interface Address { - /** - * Country code (ISO 3166-1 alpha-2) - */ - country: string; - - /** - * Street address line 1 - */ - line1: string; - - /** - * Postal/ZIP code - */ - postalCode: string; - - /** - * City - */ - city?: string; - - /** - * Street address line 2 - */ - line2?: string; - - /** - * State/Province/Region - */ - state?: string; - } - export interface BeneficialOwner { /** * Individual's full name @@ -1455,7 +567,7 @@ export namespace CustomerUpdateParams { | 'SETTLOR' | 'GENERAL_PARTNER'; - address?: BeneficialOwner.Address; + address?: ExternalAccountsAPI.Address; /** * Date of birth in ISO 8601 format (YYYY-MM-DD) @@ -1495,40 +607,6 @@ export namespace CustomerUpdateParams { title?: string; } - export namespace BeneficialOwner { - export interface Address { - /** - * Country code (ISO 3166-1 alpha-2) - */ - country: string; - - /** - * Street address line 1 - */ - line1: string; - - /** - * Postal/ZIP code - */ - postalCode: string; - - /** - * City - */ - city?: string; - - /** - * Street address line 2 - */ - line2?: string; - - /** - * State/Province/Region - */ - state?: string; - } - } - /** * Additional information for business entities */ @@ -1637,9 +715,6 @@ export declare namespace Customers { type CustomerCreate as CustomerCreate, type CustomerOneOf as CustomerOneOf, type CustomerUpdate as CustomerUpdate, - type CustomerRetrieveResponse as CustomerRetrieveResponse, - type CustomerUpdateResponse as CustomerUpdateResponse, - type CustomerDeleteResponse as CustomerDeleteResponse, type CustomerGetKYCLinkResponse as CustomerGetKYCLinkResponse, type CustomerOneovesDefaultPagination as CustomerOneovesDefaultPagination, type CustomerCreateParams as CustomerCreateParams, @@ -1651,26 +726,43 @@ export declare namespace Customers { export { ExternalAccounts as ExternalAccounts, + type Address as Address, type BaseWalletInfo as BaseWalletInfo, - type BeneficiaryOneOf as BeneficiaryOneOf, - type CadAccountInfo as CadAccountInfo, - type ClabeAccountInfo as ClabeAccountInfo, + type BrlBeneficiary as BrlBeneficiary, + type BrlExternalAccountInfo as BrlExternalAccountInfo, + type BusinessBeneficiary as BusinessBeneficiary, + type DkkBeneficiary as DkkBeneficiary, + type DkkExternalAccountInfo as DkkExternalAccountInfo, type ExternalAccount as ExternalAccount, type ExternalAccountCreate as ExternalAccountCreate, type ExternalAccountInfoOneOf as ExternalAccountInfoOneOf, - type GbpAccountInfo as GbpAccountInfo, - type IbanAccountInfo as IbanAccountInfo, + type GbpBeneficiary as GbpBeneficiary, + type GbpExternalAccountInfo as GbpExternalAccountInfo, + type HkdBeneficiary as HkdBeneficiary, + type HkdExternalAccountInfo as HkdExternalAccountInfo, + type IdrBeneficiary as IdrBeneficiary, + type IdrExternalAccountInfo as IdrExternalAccountInfo, + type InrBeneficiary as InrBeneficiary, + type InrExternalAccountInfo as InrExternalAccountInfo, type LightningWalletInfo as LightningWalletInfo, - type NgnAccountInfo as NgnAccountInfo, - type PhpAccountInfo as PhpAccountInfo, - type PixAccountInfo as PixAccountInfo, + type MxnBeneficiary as MxnBeneficiary, + type MxnExternalAccountInfo as MxnExternalAccountInfo, + type MyrBeneficiary as MyrBeneficiary, + type MyrExternalAccountInfo as MyrExternalAccountInfo, + type PhpBeneficiary as PhpBeneficiary, + type PhpExternalAccountInfo as PhpExternalAccountInfo, type PolygonWalletInfo as PolygonWalletInfo, - type SgdAccountInfo as SgdAccountInfo, + type SgdBeneficiary as SgdBeneficiary, + type SgdExternalAccountInfo as SgdExternalAccountInfo, type SolanaWalletInfo as SolanaWalletInfo, type SparkWalletInfo as SparkWalletInfo, + type ThbBeneficiary as ThbBeneficiary, + type ThbExternalAccountInfo as ThbExternalAccountInfo, type TronWalletInfo as TronWalletInfo, - type UpiAccountInfo as UpiAccountInfo, - type UsAccountInfo as UsAccountInfo, + type UsdBeneficiary as UsdBeneficiary, + type UsdExternalAccountInfo as UsdExternalAccountInfo, + type VndBeneficiary as VndBeneficiary, + type VndExternalAccountInfo as VndExternalAccountInfo, type ExternalAccountsDefaultPagination as ExternalAccountsDefaultPagination, type ExternalAccountCreateParams as ExternalAccountCreateParams, type ExternalAccountListParams as ExternalAccountListParams, diff --git a/src/resources/customers/external-accounts.ts b/src/resources/customers/external-accounts.ts index add9bb1..6cb111e 100644 --- a/src/resources/customers/external-accounts.ts +++ b/src/resources/customers/external-accounts.ts @@ -1,6 +1,7 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. import { APIResource } from '../../core/resource'; +import * as ExternalAccountsAPI from './external-accounts'; import { APIPromise } from '../../core/api-promise'; import { DefaultPagination, type DefaultPaginationParams, PagePromise } from '../../core/pagination'; import { RequestOptions } from '../../internal/request-options'; @@ -30,10 +31,6 @@ export class ExternalAccounts extends APIResource { * await client.customers.externalAccounts.create({ * accountInfo: { * accountType: 'US_ACCOUNT', - * accountNumber: '12345678901', - * routingNumber: '123456789', - * accountCategory: 'CHECKING', - * bankName: 'Chase Bank', * beneficiary: { * beneficiaryType: 'INDIVIDUAL', * fullName: 'John Doe', @@ -87,6 +84,38 @@ export class ExternalAccounts extends APIResource { export type ExternalAccountsDefaultPagination = DefaultPagination; +export interface Address { + /** + * Country code (ISO 3166-1 alpha-2) + */ + country: string; + + /** + * Street address line 1 + */ + line1: string; + + /** + * Postal/ZIP code + */ + postalCode: string; + + /** + * City + */ + city?: string; + + /** + * Street address line 2 + */ + line2?: string; + + /** + * State/Province/Region + */ + state?: string; +} + export interface BaseWalletInfo { accountType: 'BASE_WALLET'; @@ -96,150 +125,167 @@ export interface BaseWalletInfo { address: string; } -export type BeneficiaryOneOf = BeneficiaryOneOf.IndividualBeneficiary | BeneficiaryOneOf.BusinessBeneficiary; +export interface BrlBeneficiary { + beneficiaryType: 'INDIVIDUAL'; -export namespace BeneficiaryOneOf { - export interface IndividualBeneficiary { - beneficiaryType: 'INDIVIDUAL'; + /** + * The full name of the beneficiary + */ + fullName: string; - /** - * Date of birth in ISO 8601 format (YYYY-MM-DD) - */ - birthDate: string; + address?: Address; - /** - * Individual's full name - */ - fullName: string; + /** + * The birth date of the beneficiary + */ + birthDate?: string; - /** - * Country code (ISO 3166-1 alpha-2) - */ - nationality: string; + /** + * The country of residence of the beneficiary + */ + countryOfResidence?: string; - address?: IndividualBeneficiary.Address; - } + /** + * The email of the beneficiary + */ + email?: string; - export namespace IndividualBeneficiary { - export interface Address { - /** - * Country code (ISO 3166-1 alpha-2) - */ - country: string; + /** + * The nationality of the beneficiary + */ + nationality?: string; - /** - * Street address line 1 - */ - line1: string; + /** + * The phone number of the beneficiary + */ + phoneNumber?: string; - /** - * Postal/ZIP code - */ - postalCode: string; + /** + * The registration number of the beneficiary + */ + registrationNumber?: string; +} - /** - * City - */ - city?: string; +export interface BrlExternalAccountInfo { + accountType: 'BRL_ACCOUNT'; - /** - * Street address line 2 - */ - line2?: string; + beneficiary: BrlBeneficiary | BusinessBeneficiary; - /** - * State/Province/Region - */ - state?: string; - } - } + countries: Array<'BR'>; - export interface BusinessBeneficiary { - beneficiaryType: 'BUSINESS'; + paymentRails: Array<'PIX'>; - /** - * Legal name of the business - */ - legalName: string; + /** + * The PIX key of the bank + */ + pixKey: string; - address?: BusinessBeneficiary.Address; + /** + * The type of PIX key of the bank + */ + pixKeyType: string; - /** - * Business registration number - */ - registrationNumber?: string; + /** + * The tax ID of the bank account + */ + taxId: string; +} - /** - * Tax identification number - */ - taxId?: string; - } +export interface BusinessBeneficiary { + beneficiaryType: 'BUSINESS'; - export namespace BusinessBeneficiary { - export interface Address { - /** - * Country code (ISO 3166-1 alpha-2) - */ - country: string; + /** + * The legal name of the business + */ + legalName: string; - /** - * Street address line 1 - */ - line1: string; + address?: Address; - /** - * Postal/ZIP code - */ - postalCode: string; + /** + * The country of residence of the beneficiary + */ + countryOfResidence?: string; - /** - * City - */ - city?: string; + /** + * The email of the beneficiary + */ + email?: string; - /** - * Street address line 2 - */ - line2?: string; + /** + * The phone number of the beneficiary + */ + phoneNumber?: string; - /** - * State/Province/Region - */ - state?: string; - } - } + /** + * The registration number of the business + */ + registrationNumber?: string; + + /** + * The tax identification number of the business + */ + taxId?: string; } -export interface CadAccountInfo { +export interface DkkBeneficiary { + beneficiaryType: 'INDIVIDUAL'; + /** - * Bank account number (7-12 digits) + * The full name of the beneficiary */ - accountNumber: string; + fullName: string; + + address?: Address; + + /** + * The birth date of the beneficiary + */ + birthDate?: string; - accountType: 'CAD_ACCOUNT'; + /** + * The country of residence of the beneficiary + */ + countryOfResidence?: string; + + /** + * The email of the beneficiary + */ + email?: string; /** - * Canadian financial institution number (3 digits) + * The nationality of the beneficiary */ - bankCode: string; + nationality?: string; - beneficiary: BeneficiaryOneOf; + /** + * The phone number of the beneficiary + */ + phoneNumber?: string; /** - * Transit number identifying the branch (5 digits) + * The registration number of the beneficiary */ - branchCode: string; + registrationNumber?: string; } -export interface ClabeAccountInfo { - accountType: 'CLABE'; +export interface DkkExternalAccountInfo { + accountType: 'DKK_ACCOUNT'; + + beneficiary: DkkBeneficiary | BusinessBeneficiary; - beneficiary: BeneficiaryOneOf; + countries: Array<'DK'>; /** - * 18-digit CLABE number (Mexican banking standard) + * The IBAN of the bank */ - clabeNumber: string; + iban: string; + + paymentRails: Array<'SEPA' | 'SEPA_INSTANT'>; + + /** + * The SWIFT BIC of the bank + */ + swiftBic?: string; } export interface ExternalAccount { @@ -345,16 +391,22 @@ export interface ExternalAccountCreate { } export type ExternalAccountInfoOneOf = - | UsAccountInfo - | ClabeAccountInfo - | PixAccountInfo - | IbanAccountInfo - | UpiAccountInfo - | NgnAccountInfo - | CadAccountInfo - | GbpAccountInfo - | PhpAccountInfo - | SgdAccountInfo + | BrlExternalAccountInfo + | ExternalAccountInfoOneOf.CadExternalAccountInfo + | DkkExternalAccountInfo + | ExternalAccountInfoOneOf.EurExternalAccountInfo + | GbpExternalAccountInfo + | HkdExternalAccountInfo + | IdrExternalAccountInfo + | InrExternalAccountInfo + | MxnExternalAccountInfo + | MyrExternalAccountInfo + | ExternalAccountInfoOneOf.NgnExternalAccountInfo + | PhpExternalAccountInfo + | SgdExternalAccountInfo + | ThbExternalAccountInfo + | UsdExternalAccountInfo + | VndExternalAccountInfo | SparkWalletInfo | LightningWalletInfo | SolanaWalletInfo @@ -362,127 +414,659 @@ export type ExternalAccountInfoOneOf = | PolygonWalletInfo | BaseWalletInfo; -export interface GbpAccountInfo { - /** - * UK bank account number (8 digits) - */ - accountNumber: string; +export namespace ExternalAccountInfoOneOf { + export interface CadExternalAccountInfo { + /** + * Bank account number (7-12 digits) + */ + accountNumber: string; - accountType: 'GBP_ACCOUNT'; + accountType: 'CAD_ACCOUNT'; - beneficiary: BeneficiaryOneOf; + /** + * Canadian financial institution number (3 digits) + */ + bankCode: string; - /** - * UK bank sort code (6 digits, may include hyphens) - */ - sortCode: string; -} + beneficiary: CadExternalAccountInfo.CadBeneficiary | ExternalAccountsAPI.BusinessBeneficiary; -export interface IbanAccountInfo { - accountType: 'IBAN'; + /** + * Transit number identifying the branch (5 digits) + */ + branchCode: string; - beneficiary: BeneficiaryOneOf; + countries: Array<'CA'>; - /** - * International Bank Account Number - */ - iban: string; + paymentRails: Array<'BANK_TRANSFER'>; + } - /** - * SWIFT/BIC code (8 or 11 characters) - */ - swiftBic: string; + export namespace CadExternalAccountInfo { + export interface CadBeneficiary { + beneficiaryType: 'INDIVIDUAL'; + + /** + * The full name of the beneficiary + */ + fullName: string; + + address?: ExternalAccountsAPI.Address; + + /** + * The birth date of the beneficiary + */ + birthDate?: string; + + /** + * The country of residence of the beneficiary + */ + countryOfResidence?: string; + + /** + * The email of the beneficiary + */ + email?: string; + + /** + * The nationality of the beneficiary + */ + nationality?: string; + + /** + * The phone number of the beneficiary + */ + phoneNumber?: string; + + /** + * The registration number of the beneficiary + */ + registrationNumber?: string; + } + } + + export interface EurExternalAccountInfo { + accountType: 'EUR_ACCOUNT'; + + beneficiary: EurExternalAccountInfo.EurBeneficiary | ExternalAccountsAPI.BusinessBeneficiary; + + countries: Array< + | 'AT' + | 'BE' + | 'CY' + | 'DE' + | 'EE' + | 'ES' + | 'FI' + | 'FR' + | 'GR' + | 'HR' + | 'IE' + | 'IT' + | 'LT' + | 'LU' + | 'LV' + | 'MT' + | 'NL' + | 'PT' + | 'SI' + | 'SK' + >; + + /** + * The IBAN of the bank + */ + iban: string; + + paymentRails: Array<'SEPA' | 'SEPA_INSTANT'>; + + /** + * The SWIFT BIC of the bank + */ + swiftBic?: string; + } + + export namespace EurExternalAccountInfo { + export interface EurBeneficiary { + beneficiaryType: 'INDIVIDUAL'; + + /** + * The full name of the beneficiary + */ + fullName: string; + + address?: ExternalAccountsAPI.Address; + + /** + * The birth date of the beneficiary + */ + birthDate?: string; + + /** + * The country of residence of the beneficiary + */ + countryOfResidence?: string; + + /** + * The email of the beneficiary + */ + email?: string; + + /** + * The nationality of the beneficiary + */ + nationality?: string; + + /** + * The phone number of the beneficiary + */ + phoneNumber?: string; + + /** + * The registration number of the beneficiary + */ + registrationNumber?: string; + } + } + + export interface NgnExternalAccountInfo { + /** + * Nigerian bank account number + */ + accountNumber: string; + + accountType: 'NGN_ACCOUNT'; + + /** + * Name of the bank + */ + bankName: string; + + beneficiary: NgnExternalAccountInfo.NgnBeneficiary | ExternalAccountsAPI.BusinessBeneficiary; + + countries: Array<'NG'>; + + paymentRails: Array<'BANK_TRANSFER'>; + } + + export namespace NgnExternalAccountInfo { + export interface NgnBeneficiary { + beneficiaryType: 'INDIVIDUAL'; + + /** + * The full name of the beneficiary + */ + fullName: string; + + address?: ExternalAccountsAPI.Address; + + /** + * The birth date of the beneficiary + */ + birthDate?: string; + + /** + * The country of residence of the beneficiary + */ + countryOfResidence?: string; + + /** + * The email of the beneficiary + */ + email?: string; + + /** + * The nationality of the beneficiary + */ + nationality?: string; + + /** + * The phone number of the beneficiary + */ + phoneNumber?: string; + + /** + * The registration number of the beneficiary + */ + registrationNumber?: string; + } + } } -export interface LightningWalletInfo { - accountType: 'LIGHTNING'; +export interface GbpBeneficiary { + beneficiaryType: 'INDIVIDUAL'; /** - * A bolt12 offer which can be reused as a payment destination + * The full name of the beneficiary */ - bolt12?: string; + fullName: string; + + address?: Address; /** - * 1-time use lightning bolt11 invoice payout destination + * The birth date of the beneficiary */ - invoice?: string; + birthDate?: string; /** - * A lightning address which can be used as a payment destination. Note that for - * UMA addresses, no external account is needed. You can use the UMA address - * directly as a destination. + * The country of residence of the beneficiary */ - lightningAddress?: string; -} + countryOfResidence?: string; -export interface NgnAccountInfo { /** - * Nigerian bank account number + * The email of the beneficiary */ - accountNumber: string; - - accountType: 'NGN_ACCOUNT'; + email?: string; /** - * Name of the bank + * The nationality of the beneficiary */ - bankName: string; + nationality?: string; - beneficiary: BeneficiaryOneOf; + /** + * The phone number of the beneficiary + */ + phoneNumber?: string; /** - * Purpose of payment + * The registration number of the beneficiary */ - purposeOfPayment: - | 'GIFT' - | 'SELF' - | 'GOODS_OR_SERVICES' - | 'EDUCATION' - | 'HEALTH_OR_MEDICAL' - | 'REAL_ESTATE_PURCHASE' - | 'LOAN_PAYMENT' - | 'TAX_PAYMENT' - | 'UTILITY_BILL' - | 'DONATION' - | 'TRAVEL' - | 'OTHER'; + registrationNumber?: string; } -export interface PhpAccountInfo { +export interface GbpExternalAccountInfo { /** - * Bank account number + * UK bank account number (8 digits) */ accountNumber: string; - accountType: 'PHP_ACCOUNT'; + accountType: 'GBP_ACCOUNT'; + + beneficiary: GbpBeneficiary | BusinessBeneficiary; + + countries: Array<'GB'>; + + paymentRails: Array<'FASTER_PAYMENTS'>; /** - * Name of the beneficiary's bank + * UK bank sort code (6 digits, may include hyphens) + */ + sortCode: string; +} + +export interface HkdBeneficiary { + beneficiaryType: 'INDIVIDUAL'; + + /** + * The full name of the beneficiary + */ + fullName: string; + + address?: Address; + + /** + * The birth date of the beneficiary + */ + birthDate?: string; + + /** + * The country of residence of the beneficiary + */ + countryOfResidence?: string; + + /** + * The email of the beneficiary + */ + email?: string; + + /** + * The nationality of the beneficiary + */ + nationality?: string; + + /** + * The phone number of the beneficiary + */ + phoneNumber?: string; + + /** + * The registration number of the beneficiary + */ + registrationNumber?: string; +} + +export interface HkdExternalAccountInfo { + /** + * The account number of the bank + */ + accountNumber: string; + + accountType: 'HKD_ACCOUNT'; + + /** + * The bank name of the bank */ bankName: string; - beneficiary: BeneficiaryOneOf; + beneficiary: HkdBeneficiary | BusinessBeneficiary; + + countries: Array<'HK'>; + + paymentRails: Array<'BANK_TRANSFER'>; } -export interface PixAccountInfo { - accountType: 'PIX'; +export interface IdrBeneficiary { + beneficiaryType: 'INDIVIDUAL'; + + /** + * The full name of the beneficiary + */ + fullName: string; + + address?: Address; - beneficiary: BeneficiaryOneOf; + /** + * The birth date of the beneficiary + */ + birthDate?: string; /** - * PIX key for Brazilian instant payments + * The country of residence of the beneficiary */ - pixKey: string; + countryOfResidence?: string; /** - * Type of PIX key being used + * The email of the beneficiary */ - pixKeyType: 'CPF' | 'CNPJ' | 'EMAIL' | 'PHONE' | 'RANDOM'; + email?: string; /** - * Tax ID of the account holder + * The nationality of the beneficiary */ - taxId: string; + nationality?: string; + + /** + * The phone number of the beneficiary + */ + phoneNumber?: string; + + /** + * The registration number of the beneficiary + */ + registrationNumber?: string; +} + +export interface IdrExternalAccountInfo { + /** + * The account number of the bank + */ + accountNumber: string; + + accountType: 'IDR_ACCOUNT'; + + beneficiary: IdrBeneficiary | BusinessBeneficiary; + + countries: Array<'ID'>; + + paymentRails: Array<'BANK_TRANSFER'>; + + /** + * The sort code of the bank + */ + sortCode: string; +} + +export interface InrBeneficiary { + beneficiaryType: 'INDIVIDUAL'; + + /** + * The full name of the beneficiary + */ + fullName: string; + + address?: Address; + + /** + * The birth date of the beneficiary + */ + birthDate?: string; + + /** + * The country of residence of the beneficiary + */ + countryOfResidence?: string; + + /** + * The email of the beneficiary + */ + email?: string; + + /** + * The nationality of the beneficiary + */ + nationality?: string; + + /** + * The phone number of the beneficiary + */ + phoneNumber?: string; + + /** + * The registration number of the beneficiary + */ + registrationNumber?: string; +} + +export interface InrExternalAccountInfo { + accountType: 'INR_ACCOUNT'; + + beneficiary: InrBeneficiary | BusinessBeneficiary; + + countries: Array<'IN'>; + + paymentRails: Array<'UPI' | 'IMPS'>; + + /** + * The VPA of the bank + */ + vpa: string; +} + +export interface LightningWalletInfo { + accountType: 'LIGHTNING'; + + /** + * A bolt12 offer which can be reused as a payment destination + */ + bolt12?: string; + + /** + * 1-time use lightning bolt11 invoice payout destination + */ + invoice?: string; + + /** + * A lightning address which can be used as a payment destination. Note that for + * UMA addresses, no external account is needed. You can use the UMA address + * directly as a destination. + */ + lightningAddress?: string; +} + +export interface MxnBeneficiary { + beneficiaryType: 'INDIVIDUAL'; + + /** + * The full name of the beneficiary + */ + fullName: string; + + address?: Address; + + /** + * The birth date of the beneficiary + */ + birthDate?: string; + + /** + * The country of residence of the beneficiary + */ + countryOfResidence?: string; + + /** + * The email of the beneficiary + */ + email?: string; + + /** + * The nationality of the beneficiary + */ + nationality?: string; + + /** + * The phone number of the beneficiary + */ + phoneNumber?: string; + + /** + * The registration number of the beneficiary + */ + registrationNumber?: string; +} + +export interface MxnExternalAccountInfo { + accountType: 'MXN_ACCOUNT'; + + beneficiary: MxnBeneficiary | BusinessBeneficiary; + + /** + * The CLABE number of the bank + */ + clabeNumber: string; + + countries: Array<'MX'>; + + paymentRails: Array<'SPEI'>; +} + +export interface MyrBeneficiary { + beneficiaryType: 'INDIVIDUAL'; + + /** + * The full name of the beneficiary + */ + fullName: string; + + address?: Address; + + /** + * The birth date of the beneficiary + */ + birthDate?: string; + + /** + * The country of residence of the beneficiary + */ + countryOfResidence?: string; + + /** + * The email of the beneficiary + */ + email?: string; + + /** + * The nationality of the beneficiary + */ + nationality?: string; + + /** + * The phone number of the beneficiary + */ + phoneNumber?: string; + + /** + * The registration number of the beneficiary + */ + registrationNumber?: string; +} + +export interface MyrExternalAccountInfo { + /** + * The account number of the bank + */ + accountNumber: string; + + accountType: 'MYR_ACCOUNT'; + + /** + * The bank name of the bank + */ + bankName: string; + + beneficiary: MyrBeneficiary | BusinessBeneficiary; + + countries: Array<'MY'>; + + paymentRails: Array<'BANK_TRANSFER'>; +} + +export interface PhpBeneficiary { + beneficiaryType: 'INDIVIDUAL'; + + /** + * The full name of the beneficiary + */ + fullName: string; + + address?: Address; + + /** + * The birth date of the beneficiary + */ + birthDate?: string; + + /** + * The country of residence of the beneficiary + */ + countryOfResidence?: string; + + /** + * The email of the beneficiary + */ + email?: string; + + /** + * The nationality of the beneficiary + */ + nationality?: string; + + /** + * The phone number of the beneficiary + */ + phoneNumber?: string; + + /** + * The registration number of the beneficiary + */ + registrationNumber?: string; +} + +export interface PhpExternalAccountInfo { + /** + * Bank account number + */ + accountNumber: string; + + accountType: 'PHP_ACCOUNT'; + + /** + * Name of the beneficiary's bank + */ + bankName: string; + + beneficiary: PhpBeneficiary | BusinessBeneficiary; + + countries: Array<'PH'>; + + paymentRails: Array<'BANK_TRANSFER'>; } export interface PolygonWalletInfo { @@ -494,7 +1078,48 @@ export interface PolygonWalletInfo { address: string; } -export interface SgdAccountInfo { +export interface SgdBeneficiary { + beneficiaryType: 'INDIVIDUAL'; + + /** + * The full name of the beneficiary + */ + fullName: string; + + address?: Address; + + /** + * The birth date of the beneficiary + */ + birthDate?: string; + + /** + * The country of residence of the beneficiary + */ + countryOfResidence?: string; + + /** + * The email of the beneficiary + */ + email?: string; + + /** + * The nationality of the beneficiary + */ + nationality?: string; + + /** + * The phone number of the beneficiary + */ + phoneNumber?: string; + + /** + * The registration number of the beneficiary + */ + registrationNumber?: string; +} + +export interface SgdExternalAccountInfo { /** * Bank account number */ @@ -507,7 +1132,11 @@ export interface SgdAccountInfo { */ bankName: string; - beneficiary: BeneficiaryOneOf; + beneficiary: SgdBeneficiary | BusinessBeneficiary; + + countries: Array<'SG'>; + + paymentRails: Array<'PAYNOW' | 'FAST' | 'BANK_TRANSFER'>; /** * SWIFT/BIC code (8 or 11 characters) @@ -533,6 +1162,67 @@ export interface SparkWalletInfo { address: string; } +export interface ThbBeneficiary { + beneficiaryType: 'INDIVIDUAL'; + + /** + * The full name of the beneficiary + */ + fullName: string; + + address?: Address; + + /** + * The birth date of the beneficiary + */ + birthDate?: string; + + /** + * The country of residence of the beneficiary + */ + countryOfResidence?: string; + + /** + * The email of the beneficiary + */ + email?: string; + + /** + * The nationality of the beneficiary + */ + nationality?: string; + + /** + * The phone number of the beneficiary + */ + phoneNumber?: string; + + /** + * The registration number of the beneficiary + */ + registrationNumber?: string; +} + +export interface ThbExternalAccountInfo { + /** + * The account number of the bank + */ + accountNumber: string; + + accountType: 'THB_ACCOUNT'; + + /** + * The bank name of the bank + */ + bankName: string; + + beneficiary: ThbBeneficiary | BusinessBeneficiary; + + countries: Array<'TH'>; + + paymentRails: Array<'BANK_TRANSFER'>; +} + export interface TronWalletInfo { accountType: 'TRON_WALLET'; @@ -542,41 +1232,126 @@ export interface TronWalletInfo { address: string; } -export interface UpiAccountInfo { - accountType: 'UPI'; +export interface UsdBeneficiary { + beneficiaryType: 'INDIVIDUAL'; - beneficiary: BeneficiaryOneOf; + /** + * The birth date of the beneficiary + */ + birthDate: string; /** - * Virtual Payment Address for UPI payments + * The full name of the beneficiary */ - vpa: string; -} + fullName: string; + + /** + * The nationality of the beneficiary + */ + nationality: string; + + address?: Address; + + /** + * The country of residence of the beneficiary + */ + countryOfResidence?: string; -export interface UsAccountInfo { /** - * Type of account (checking or savings) + * The email of the beneficiary */ - accountCategory: 'CHECKING' | 'SAVINGS'; + email?: string; /** - * US bank account number + * The phone number of the beneficiary + */ + phoneNumber?: string; + + /** + * The registration number of the beneficiary + */ + registrationNumber?: string; +} + +export interface UsdExternalAccountInfo { + /** + * The account number of the bank */ accountNumber: string; - accountType: 'US_ACCOUNT'; + accountType: 'USD_ACCOUNT'; + + beneficiary: UsdBeneficiary | BusinessBeneficiary; + + countries: Array<'US'>; - beneficiary: BeneficiaryOneOf; + paymentRails: Array<'ACH' | 'WIRE' | 'RTP' | 'FEDNOW'>; /** - * ACH routing number (9 digits) + * The routing number of the bank */ routingNumber: string; +} + +export interface VndBeneficiary { + beneficiaryType: 'INDIVIDUAL'; /** - * Name of the bank + * The full name of the beneficiary */ - bankName?: string; + fullName: string; + + address?: Address; + + /** + * The birth date of the beneficiary + */ + birthDate?: string; + + /** + * The country of residence of the beneficiary + */ + countryOfResidence?: string; + + /** + * The email of the beneficiary + */ + email?: string; + + /** + * The nationality of the beneficiary + */ + nationality?: string; + + /** + * The phone number of the beneficiary + */ + phoneNumber?: string; + + /** + * The registration number of the beneficiary + */ + registrationNumber?: string; +} + +export interface VndExternalAccountInfo { + /** + * The account number of the bank + */ + accountNumber: string; + + accountType: 'VND_ACCOUNT'; + + /** + * The bank name of the bank + */ + bankName: string; + + beneficiary: VndBeneficiary | BusinessBeneficiary; + + countries: Array<'VN'>; + + paymentRails: Array<'BANK_TRANSFER'>; } export interface ExternalAccountCreateParams { @@ -631,26 +1406,43 @@ export interface ExternalAccountListParams extends DefaultPaginationParams { export declare namespace ExternalAccounts { export { + type Address as Address, type BaseWalletInfo as BaseWalletInfo, - type BeneficiaryOneOf as BeneficiaryOneOf, - type CadAccountInfo as CadAccountInfo, - type ClabeAccountInfo as ClabeAccountInfo, + type BrlBeneficiary as BrlBeneficiary, + type BrlExternalAccountInfo as BrlExternalAccountInfo, + type BusinessBeneficiary as BusinessBeneficiary, + type DkkBeneficiary as DkkBeneficiary, + type DkkExternalAccountInfo as DkkExternalAccountInfo, type ExternalAccount as ExternalAccount, type ExternalAccountCreate as ExternalAccountCreate, type ExternalAccountInfoOneOf as ExternalAccountInfoOneOf, - type GbpAccountInfo as GbpAccountInfo, - type IbanAccountInfo as IbanAccountInfo, + type GbpBeneficiary as GbpBeneficiary, + type GbpExternalAccountInfo as GbpExternalAccountInfo, + type HkdBeneficiary as HkdBeneficiary, + type HkdExternalAccountInfo as HkdExternalAccountInfo, + type IdrBeneficiary as IdrBeneficiary, + type IdrExternalAccountInfo as IdrExternalAccountInfo, + type InrBeneficiary as InrBeneficiary, + type InrExternalAccountInfo as InrExternalAccountInfo, type LightningWalletInfo as LightningWalletInfo, - type NgnAccountInfo as NgnAccountInfo, - type PhpAccountInfo as PhpAccountInfo, - type PixAccountInfo as PixAccountInfo, + type MxnBeneficiary as MxnBeneficiary, + type MxnExternalAccountInfo as MxnExternalAccountInfo, + type MyrBeneficiary as MyrBeneficiary, + type MyrExternalAccountInfo as MyrExternalAccountInfo, + type PhpBeneficiary as PhpBeneficiary, + type PhpExternalAccountInfo as PhpExternalAccountInfo, type PolygonWalletInfo as PolygonWalletInfo, - type SgdAccountInfo as SgdAccountInfo, + type SgdBeneficiary as SgdBeneficiary, + type SgdExternalAccountInfo as SgdExternalAccountInfo, type SolanaWalletInfo as SolanaWalletInfo, type SparkWalletInfo as SparkWalletInfo, + type ThbBeneficiary as ThbBeneficiary, + type ThbExternalAccountInfo as ThbExternalAccountInfo, type TronWalletInfo as TronWalletInfo, - type UpiAccountInfo as UpiAccountInfo, - type UsAccountInfo as UsAccountInfo, + type UsdBeneficiary as UsdBeneficiary, + type UsdExternalAccountInfo as UsdExternalAccountInfo, + type VndBeneficiary as VndBeneficiary, + type VndExternalAccountInfo as VndExternalAccountInfo, type ExternalAccountsDefaultPagination as ExternalAccountsDefaultPagination, type ExternalAccountCreateParams as ExternalAccountCreateParams, type ExternalAccountListParams as ExternalAccountListParams, diff --git a/src/resources/customers/index.ts b/src/resources/customers/index.ts index 9dd6eb3..2bb9cda 100644 --- a/src/resources/customers/index.ts +++ b/src/resources/customers/index.ts @@ -12,9 +12,6 @@ export { type CustomerCreate, type CustomerOneOf, type CustomerUpdate, - type CustomerRetrieveResponse, - type CustomerUpdateResponse, - type CustomerDeleteResponse, type CustomerGetKYCLinkResponse, type CustomerCreateParams, type CustomerUpdateParams, @@ -25,26 +22,43 @@ export { } from './customers'; export { ExternalAccounts, + type Address, type BaseWalletInfo, - type BeneficiaryOneOf, - type CadAccountInfo, - type ClabeAccountInfo, + type BrlBeneficiary, + type BrlExternalAccountInfo, + type BusinessBeneficiary, + type DkkBeneficiary, + type DkkExternalAccountInfo, type ExternalAccount, type ExternalAccountCreate, type ExternalAccountInfoOneOf, - type GbpAccountInfo, - type IbanAccountInfo, + type GbpBeneficiary, + type GbpExternalAccountInfo, + type HkdBeneficiary, + type HkdExternalAccountInfo, + type IdrBeneficiary, + type IdrExternalAccountInfo, + type InrBeneficiary, + type InrExternalAccountInfo, type LightningWalletInfo, - type NgnAccountInfo, - type PhpAccountInfo, - type PixAccountInfo, + type MxnBeneficiary, + type MxnExternalAccountInfo, + type MyrBeneficiary, + type MyrExternalAccountInfo, + type PhpBeneficiary, + type PhpExternalAccountInfo, type PolygonWalletInfo, - type SgdAccountInfo, + type SgdBeneficiary, + type SgdExternalAccountInfo, type SolanaWalletInfo, type SparkWalletInfo, + type ThbBeneficiary, + type ThbExternalAccountInfo, type TronWalletInfo, - type UpiAccountInfo, - type UsAccountInfo, + type UsdBeneficiary, + type UsdExternalAccountInfo, + type VndBeneficiary, + type VndExternalAccountInfo, type ExternalAccountCreateParams, type ExternalAccountListParams, type ExternalAccountsDefaultPagination, diff --git a/src/resources/index.ts b/src/resources/index.ts index fdec045..4ce8780 100644 --- a/src/resources/index.ts +++ b/src/resources/index.ts @@ -13,9 +13,6 @@ export { type CustomerCreate, type CustomerOneOf, type CustomerUpdate, - type CustomerRetrieveResponse, - type CustomerUpdateResponse, - type CustomerDeleteResponse, type CustomerGetKYCLinkResponse, type CustomerCreateParams, type CustomerUpdateParams, @@ -45,15 +42,11 @@ export { } from './platform/platform'; export { Quotes, - type BaseDestination, - type BasePaymentAccountInfo, - type BaseQuoteSource, type Currency, type OutgoingRateDetails, type PaymentInstructions, type Quote, type QuoteDestinationOneOf, - type QuoteSourceOneOf, type QuoteCreateParams, type QuoteListParams, type QuotesDefaultPagination, @@ -67,12 +60,7 @@ export { type ReceiverLookupExternalAccountParams, type ReceiverLookupUmaParams, } from './receiver'; -export { - Sandbox, - type SandboxSendFundsResponse, - type SandboxSendTestWebhookResponse, - type SandboxSendFundsParams, -} from './sandbox/sandbox'; +export { Sandbox, type SandboxSendFundsResponse, type SandboxSendFundsParams } from './sandbox/sandbox'; export { Tokens, type APIToken, @@ -84,38 +72,29 @@ export { export { Transactions, type BaseTransactionSource, - type CounterpartyInformation, type IncomingTransaction, - type TransactionDestinationOneOf, type TransactionSourceOneOf, type TransactionStatus, type TransactionType, + type TransactionRetrieveResponse, + type TransactionListResponse, type TransactionListParams, type TransactionApproveParams, type TransactionRejectParams, + type TransactionListResponsesDefaultPagination, } from './transactions'; export { TransferIn, type BaseTransactionDestination, type Transaction, + type TransferInCreateResponse, type TransferInCreateParams, - type TransactionsDefaultPagination, } from './transfer-in'; -export { TransferOut, type TransferOutCreateParams } from './transfer-out'; +export { TransferOut, type TransferOutCreateResponse, type TransferOutCreateParams } from './transfer-out'; export { UmaProviders, type UmaProviderListResponse, type UmaProviderListParams, type UmaProviderListResponsesDefaultPagination, } from './uma-providers'; -export { - Webhooks, - type IncomingPaymentWebhookEvent, - type OutgoingPaymentWebhookEvent, - type TestWebhookWebhookEvent, - type BulkUploadWebhookEvent, - type InvitationClaimedWebhookEvent, - type KYCStatusWebhookEvent, - type AccountStatusWebhookEvent, - type UnwrapWebhookEvent, -} from './webhooks'; +export { Webhooks, type WebhookSendTestResponse } from './webhooks'; diff --git a/src/resources/platform/external-accounts.ts b/src/resources/platform/external-accounts.ts index 8ccfd56..164c61c 100644 --- a/src/resources/platform/external-accounts.ts +++ b/src/resources/platform/external-accounts.ts @@ -30,10 +30,6 @@ export class ExternalAccounts extends APIResource { * await client.platform.externalAccounts.create({ * accountInfo: { * accountType: 'US_ACCOUNT', - * accountNumber: '12345678901', - * routingNumber: '123456789', - * accountCategory: 'CHECKING', - * bankName: 'Chase Bank', * beneficiary: { * beneficiaryType: 'INDIVIDUAL', * fullName: 'John Doe', @@ -95,24 +91,6 @@ export interface ExternalAccountCreateParams { */ currency: string; - /** - * The ID of the customer for whom to create the external account. If not provided, - * the external account will be created on behalf of the platform. - */ - customerId?: string; - - /** - * Whether to set the external account as the default UMA deposit account. When set - * to true, incoming payments to this customer's UMA address will be automatically - * deposited into this external account. False if not provided. Note that only one - * external account can be set as the default UMA deposit account for a customer, - * so if there is already a default UMA deposit account, this will override the - * existing default UMA deposit account. If there is no default UMA deposit - * account, incoming UMA payments will be deposited into the primary internal - * account for the customer. - */ - defaultUmaDepositAccount?: boolean; - /** * Your platform's identifier for the account in your system. This can be used to * reference the account by your own identifier. diff --git a/src/resources/quotes.ts b/src/resources/quotes.ts index d8853ef..15ddee6 100644 --- a/src/resources/quotes.ts +++ b/src/resources/quotes.ts @@ -131,12 +131,6 @@ export class Quotes extends APIResource { export type QuotesDefaultPagination = DefaultPagination; -export type BaseDestination = unknown; - -export type BasePaymentAccountInfo = unknown; - -export type BaseQuoteSource = unknown; - export interface Currency { /** * Three-letter currency code (ISO 4217) for fiat currencies. Some cryptocurrencies @@ -204,11 +198,22 @@ export interface OutgoingRateDetails { export interface PaymentInstructions { accountOrWalletInfo: - | PaymentInstructions.PaymentClabeAccountInfo - | PaymentInstructions.PaymentUsAccountInfo - | PaymentInstructions.PixAccountInfo - | PaymentInstructions.PaymentIbanAccountInfo - | PaymentInstructions.UpiAccountInfo + | PaymentInstructions.PaymentUsdAccountInfo + | PaymentInstructions.PaymentBrlAccountInfo + | PaymentInstructions.PaymentMxnAccountInfo + | PaymentInstructions.PaymentDkkAccountInfo + | PaymentInstructions.PaymentEurAccountInfo + | PaymentInstructions.PaymentInrAccountInfo + | PaymentInstructions.PaymentNgnAccountInfo + | PaymentInstructions.PaymentCadAccountInfo + | PaymentInstructions.PaymentGbpAccountInfo + | PaymentInstructions.PaymentHkdAccountInfo + | PaymentInstructions.PaymentIdrAccountInfo + | PaymentInstructions.PaymentMyrAccountInfo + | PaymentInstructions.PaymentPhpAccountInfo + | PaymentInstructions.PaymentSgdAccountInfo + | PaymentInstructions.PaymentThbAccountInfo + | PaymentInstructions.PaymentVndAccountInfo | PaymentInstructions.PaymentSparkWalletInfo | PaymentInstructions.PaymentLightningInvoiceInfo | PaymentInstructions.PaymentSolanaWalletInfo @@ -228,14 +233,65 @@ export interface PaymentInstructions { } export namespace PaymentInstructions { - export interface PaymentClabeAccountInfo { - accountType: 'CLABE'; + export interface PaymentUsdAccountInfo { + /** + * The account number of the bank + */ + accountNumber: string; + + accountType: 'USD_ACCOUNT'; + + countries: Array<'US'>; + + paymentRails: Array<'ACH' | 'WIRE' | 'RTP' | 'FEDNOW'>; + + /** + * Unique reference code that must be included with the payment to properly credit + * it + */ + reference: string; + + /** + * The routing number of the bank + */ + routingNumber: string; + } + + export interface PaymentBrlAccountInfo { + accountType: 'BRL_ACCOUNT'; + + countries: Array<'BR'>; + + paymentRails: Array<'PIX'>; /** - * 18-digit CLABE number (Mexican banking standard) + * The PIX key of the bank + */ + pixKey: string; + + /** + * The type of PIX key of the bank + */ + pixKeyType: string; + + /** + * The tax ID of the bank account + */ + taxId: string; + } + + export interface PaymentMxnAccountInfo { + accountType: 'MXN_ACCOUNT'; + + /** + * The CLABE number of the bank */ clabeNumber: string; + countries: Array<'MX'>; + + paymentRails: Array<'SPEI'>; + /** * Unique reference code that must be included with the payment to properly credit * it @@ -243,18 +299,62 @@ export namespace PaymentInstructions { reference: string; } - export interface PaymentUsAccountInfo { + export interface PaymentDkkAccountInfo { + accountType: 'DKK_ACCOUNT'; + + countries: Array<'DK'>; + /** - * Type of account (checking or savings) + * The IBAN of the bank */ - accountCategory: 'CHECKING' | 'SAVINGS'; + iban: string; + + paymentRails: Array<'SEPA' | 'SEPA_INSTANT'>; /** - * US bank account number + * Unique reference code that must be included with the payment to properly credit + * it */ - accountNumber: string; + reference: string; + + /** + * The SWIFT BIC of the bank + */ + swiftBic?: string; + } + + export interface PaymentEurAccountInfo { + accountType: 'EUR_ACCOUNT'; + + countries: Array< + | 'AT' + | 'BE' + | 'CY' + | 'DE' + | 'EE' + | 'ES' + | 'FI' + | 'FR' + | 'GR' + | 'HR' + | 'IE' + | 'IT' + | 'LT' + | 'LU' + | 'LV' + | 'MT' + | 'NL' + | 'PT' + | 'SI' + | 'SK' + >; - accountType: 'US_ACCOUNT'; + /** + * The IBAN of the bank + */ + iban: string; + + paymentRails: Array<'SEPA' | 'SEPA_INSTANT'>; /** * Unique reference code that must be included with the payment to properly credit @@ -263,42 +363,213 @@ export namespace PaymentInstructions { reference: string; /** - * ACH routing number (9 digits) + * The SWIFT BIC of the bank */ - routingNumber: string; + swiftBic?: string; + } + + export interface PaymentInrAccountInfo { + accountType: 'INR_ACCOUNT'; + + countries: Array<'IN'>; + + paymentRails: Array<'UPI' | 'IMPS'>; + + /** + * The VPA of the bank + */ + vpa: string; + } + + export interface PaymentNgnAccountInfo { + /** + * Nigerian bank account number + */ + accountNumber: string; + + accountType: 'NGN_ACCOUNT'; /** * Name of the bank */ - bankName?: string; + bankName: string; + + countries: Array<'NG'>; + + paymentRails: Array<'BANK_TRANSFER'>; + + /** + * Unique reference code that must be included with the payment to properly credit + * it + */ + reference: string; } - export interface PixAccountInfo { - accountType: 'PIX'; + export interface PaymentCadAccountInfo { + /** + * Bank account number (7-12 digits) + */ + accountNumber: string; + + accountType: 'CAD_ACCOUNT'; /** - * PIX key for Brazilian instant payments + * Canadian financial institution number (3 digits) */ - pixKey: string; + bankCode: string; /** - * Type of PIX key being used + * Transit number identifying the branch (5 digits) */ - pixKeyType: 'CPF' | 'CNPJ' | 'EMAIL' | 'PHONE' | 'RANDOM'; + branchCode: string; + + countries: Array<'CA'>; + + paymentRails: Array<'BANK_TRANSFER'>; /** - * Tax ID of the account holder + * Unique reference code that must be included with the payment to properly credit + * it */ - taxId: string; + reference: string; + } + + export interface PaymentGbpAccountInfo { + /** + * UK bank account number (8 digits) + */ + accountNumber: string; + + accountType: 'GBP_ACCOUNT'; + + countries: Array<'GB'>; + + paymentRails: Array<'FASTER_PAYMENTS'>; + + /** + * Unique reference code that must be included with the payment to properly credit + * it + */ + reference: string; + + /** + * UK bank sort code (6 digits, may include hyphens) + */ + sortCode: string; } - export interface PaymentIbanAccountInfo { - accountType: 'IBAN'; + export interface PaymentHkdAccountInfo { + /** + * The account number of the bank + */ + accountNumber: string; + + accountType: 'HKD_ACCOUNT'; /** - * International Bank Account Number + * The bank name of the bank */ - iban: string; + bankName: string; + + countries: Array<'HK'>; + + paymentRails: Array<'BANK_TRANSFER'>; + + /** + * Unique reference code that must be included with the payment to properly credit + * it + */ + reference: string; + } + + export interface PaymentIdrAccountInfo { + /** + * The account number of the bank + */ + accountNumber: string; + + accountType: 'IDR_ACCOUNT'; + + countries: Array<'ID'>; + + paymentRails: Array<'BANK_TRANSFER'>; + + /** + * Unique reference code that must be included with the payment to properly credit + * it + */ + reference: string; + + /** + * The sort code of the bank + */ + sortCode: string; + } + + export interface PaymentMyrAccountInfo { + /** + * The account number of the bank + */ + accountNumber: string; + + accountType: 'MYR_ACCOUNT'; + + /** + * The bank name of the bank + */ + bankName: string; + + countries: Array<'MY'>; + + paymentRails: Array<'BANK_TRANSFER'>; + + /** + * Unique reference code that must be included with the payment to properly credit + * it + */ + reference: string; + } + + export interface PaymentPhpAccountInfo { + /** + * Bank account number + */ + accountNumber: string; + + accountType: 'PHP_ACCOUNT'; + + /** + * Name of the beneficiary's bank + */ + bankName: string; + + countries: Array<'PH'>; + + paymentRails: Array<'BANK_TRANSFER'>; + + /** + * Unique reference code that must be included with the payment to properly credit + * it + */ + reference: string; + } + + export interface PaymentSgdAccountInfo { + /** + * Bank account number + */ + accountNumber: string; + + accountType: 'SGD_ACCOUNT'; + + /** + * Name of the beneficiary's bank + */ + bankName: string; + + countries: Array<'SG'>; + + paymentRails: Array<'PAYNOW' | 'FAST' | 'BANK_TRANSFER'>; /** * Unique reference code that must be included with the payment to properly credit @@ -309,16 +580,55 @@ export namespace PaymentInstructions { /** * SWIFT/BIC code (8 or 11 characters) */ - swiftBic: string; + swiftCode: string; } - export interface UpiAccountInfo { - accountType: 'UPI'; + export interface PaymentThbAccountInfo { + /** + * The account number of the bank + */ + accountNumber: string; + + accountType: 'THB_ACCOUNT'; /** - * Virtual Payment Address for UPI payments + * The bank name of the bank */ - vpa: string; + bankName: string; + + countries: Array<'TH'>; + + paymentRails: Array<'BANK_TRANSFER'>; + + /** + * Unique reference code that must be included with the payment to properly credit + * it + */ + reference: string; + } + + export interface PaymentVndAccountInfo { + /** + * The account number of the bank + */ + accountNumber: string; + + accountType: 'VND_ACCOUNT'; + + /** + * The bank name of the bank + */ + bankName: string; + + countries: Array<'VN'>; + + paymentRails: Array<'BANK_TRANSFER'>; + + /** + * Unique reference code that must be included with the payment to properly credit + * it + */ + reference: string; } export interface PaymentSparkWalletInfo { @@ -407,6 +717,11 @@ export namespace PaymentInstructions { } export interface Quote { + /** + * Unique identifier for this quote + */ + id: string; + /** * When this quote was created */ @@ -433,11 +748,6 @@ export interface Quote { */ feesIncluded: number; - /** - * Unique identifier for this quote - */ - quoteId: string; - /** * Currency for the receiving amount */ @@ -451,7 +761,7 @@ export interface Quote { /** * Source account details */ - source: QuoteSourceOneOf; + source: Quote.AccountQuoteSource | Quote.RealtimeFundingQuoteSource; /** * Current status of the quote @@ -487,6 +797,52 @@ export interface Quote { rateDetails?: OutgoingRateDetails; } +export namespace Quote { + /** + * Source account details + */ + export interface AccountQuoteSource { + /** + * Source account identifier + */ + accountId: string; + + sourceType: 'ACCOUNT'; + + /** + * Required when funding from an FBO account to identify the customer on whose + * behalf the transaction is being initiated. Otherwise, will default to the + * customerId of the account owner. + */ + customerId?: string; + } + + /** + * Fund the quote using a real-time funding source (RTP, SEPA Instant, Spark, + * Stables, etc.). This will require manual just-in-time funding using + * `paymentInstructions` in the response. Because quotes expire quickly, this + * option is only valid for instant payment methods. Do not try to fund a quote + * with a non-instant payment method (ACH, etc.). + */ + export interface RealtimeFundingQuoteSource { + /** + * Currency code for the funding source. See + * [Supported Currencies](https://grid.lightspark.com/platform-overview/core-concepts/currencies-and-rails) + * for the full list of supported fiat and crypto currencies. + */ + currency: string; + + sourceType: 'REALTIME_FUNDING'; + + /** + * Source customer ID. If this transaction is being initiated on behalf of a + * customer, this is required. If customerId is not provided, the quote will be + * created on behalf of the platform itself. + */ + customerId?: string; + } +} + /** * Destination account details */ @@ -546,59 +902,6 @@ export namespace QuoteDestinationOneOf { } } -/** - * Source account details - */ -export type QuoteSourceOneOf = - | QuoteSourceOneOf.AccountQuoteSource - | QuoteSourceOneOf.RealtimeFundingQuoteSource; - -export namespace QuoteSourceOneOf { - /** - * Source account details - */ - export interface AccountQuoteSource { - /** - * Source account identifier - */ - accountId: string; - - sourceType: 'ACCOUNT'; - - /** - * Required when funding from an FBO account to identify the customer on whose - * behalf the transaction is being initiated. Otherwise, will default to the - * customerId of the account owner. - */ - customerId?: string; - } - - /** - * Fund the quote using a real-time funding source (RTP, SEPA Instant, Spark, - * Stables, etc.). This will require manual just-in-time funding using - * `paymentInstructions` in the response. Because quotes expire quickly, this - * option is only valid for instant payment methods. Do not try to fund a quote - * with a non-instant payment method (ACH, etc.). - */ - export interface RealtimeFundingQuoteSource { - /** - * Currency code for the funding source. See - * [Supported Currencies](https://grid.lightspark.com/platform-overview/core-concepts/currencies-and-rails) - * for the full list of supported fiat and crypto currencies. - */ - currency: string; - - sourceType: 'REALTIME_FUNDING'; - - /** - * Source customer ID. If this transaction is being initiated on behalf of a - * customer, this is required. If customerId is not provided, the quote will be - * created on behalf of the platform itself. - */ - customerId?: string; - } -} - export interface QuoteCreateParams { /** * Destination account details @@ -623,7 +926,7 @@ export interface QuoteCreateParams { /** * Source account details */ - source: QuoteSourceOneOf; + source: QuoteCreateParams.AccountQuoteSource | QuoteCreateParams.RealtimeFundingQuoteSource; /** * Optional description/memo for the transfer @@ -635,7 +938,9 @@ export interface QuoteCreateParams { * be executed and the transaction will be created at the current exchange rate. It * should only be used if you don't want to lock and view rate details before * executing the quote. If you are executing a pre-existing quote, use the - * `/quotes/{quoteId}/execute` endpoint instead. This is false by default. + * `/quotes/{quoteId}/execute` endpoint instead. This is false by default. This can + * only be used for quotes with a `source` which is either an internal account, or + * has direct pull functionality (e.g. ACH pull with an external account). */ immediatelyExecute?: boolean; @@ -647,6 +952,24 @@ export interface QuoteCreateParams { */ lookupId?: string; + /** + * The purpose of the payment. This may be required when sending to certain + * geographies such as India. + */ + purposeOfPayment?: + | 'GIFT' + | 'SELF' + | 'GOODS_OR_SERVICES' + | 'EDUCATION' + | 'HEALTH_OR_MEDICAL' + | 'REAL_ESTATE_PURCHASE' + | 'TAX_PAYMENT' + | 'LOAN_PAYMENT' + | 'UTILITY_BILL' + | 'DONATION' + | 'TRAVEL' + | 'OTHER'; + /** * Only relevant for UMA destinations. Key-value pairs of information about the * sender which was requested by the counterparty (recipient) institution. Any @@ -658,6 +981,52 @@ export interface QuoteCreateParams { senderCustomerInfo?: { [key: string]: unknown }; } +export namespace QuoteCreateParams { + /** + * Source account details + */ + export interface AccountQuoteSource { + /** + * Source account identifier + */ + accountId: string; + + sourceType: 'ACCOUNT'; + + /** + * Required when funding from an FBO account to identify the customer on whose + * behalf the transaction is being initiated. Otherwise, will default to the + * customerId of the account owner. + */ + customerId?: string; + } + + /** + * Fund the quote using a real-time funding source (RTP, SEPA Instant, Spark, + * Stables, etc.). This will require manual just-in-time funding using + * `paymentInstructions` in the response. Because quotes expire quickly, this + * option is only valid for instant payment methods. Do not try to fund a quote + * with a non-instant payment method (ACH, etc.). + */ + export interface RealtimeFundingQuoteSource { + /** + * Currency code for the funding source. See + * [Supported Currencies](https://grid.lightspark.com/platform-overview/core-concepts/currencies-and-rails) + * for the full list of supported fiat and crypto currencies. + */ + currency: string; + + sourceType: 'REALTIME_FUNDING'; + + /** + * Source customer ID. If this transaction is being initiated on behalf of a + * customer, this is required. If customerId is not provided, the quote will be + * created on behalf of the platform itself. + */ + customerId?: string; + } +} + export interface QuoteListParams extends DefaultPaginationParams { /** * Filter quotes created after this timestamp (inclusive) @@ -707,15 +1076,11 @@ export interface QuoteListParams extends DefaultPaginationParams { export declare namespace Quotes { export { - type BaseDestination as BaseDestination, - type BasePaymentAccountInfo as BasePaymentAccountInfo, - type BaseQuoteSource as BaseQuoteSource, type Currency as Currency, type OutgoingRateDetails as OutgoingRateDetails, type PaymentInstructions as PaymentInstructions, type Quote as Quote, type QuoteDestinationOneOf as QuoteDestinationOneOf, - type QuoteSourceOneOf as QuoteSourceOneOf, type QuotesDefaultPagination as QuotesDefaultPagination, type QuoteCreateParams as QuoteCreateParams, type QuoteListParams as QuoteListParams, diff --git a/src/resources/sandbox/index.ts b/src/resources/sandbox/index.ts index 0581be0..05a0d60 100644 --- a/src/resources/sandbox/index.ts +++ b/src/resources/sandbox/index.ts @@ -6,10 +6,5 @@ export { type InternalAccountFundParams, type InternalAccountsDefaultPagination, } from './internal-accounts'; -export { - Sandbox, - type SandboxSendFundsResponse, - type SandboxSendTestWebhookResponse, - type SandboxSendFundsParams, -} from './sandbox'; +export { Sandbox, type SandboxSendFundsResponse, type SandboxSendFundsParams } from './sandbox'; export { Uma, type UmaReceivePaymentParams } from './uma'; diff --git a/src/resources/sandbox/sandbox.ts b/src/resources/sandbox/sandbox.ts index db6fccf..f95132b 100644 --- a/src/resources/sandbox/sandbox.ts +++ b/src/resources/sandbox/sandbox.ts @@ -34,26 +34,9 @@ export class Sandbox extends APIResource { sendFunds(body: SandboxSendFundsParams, options?: RequestOptions): APIPromise { return this._client.post('/sandbox/send', { body, ...options }); } - - /** - * Send a test webhook to the configured endpoint - * - * @example - * ```ts - * const response = await client.sandbox.sendTestWebhook(); - * ``` - */ - sendTestWebhook(options?: RequestOptions): APIPromise { - return this._client.post('/webhooks/test', options); - } } -export interface SandboxSendFundsResponse extends TransferInAPI.Transaction { - /** - * Payment instructions for executing the payment. - */ - paymentInstructions: Array; - +export interface SandboxSendFundsResponse extends Omit { /** * Amount sent in the sender's currency */ @@ -64,6 +47,8 @@ export interface SandboxSendFundsResponse extends TransferInAPI.Transaction { */ source: TransactionsAPI.TransactionSourceOneOf; + type: 'OUTGOING'; + /** * Number of sending currency units per receiving currency unit. */ @@ -86,6 +71,11 @@ export interface SandboxSendFundsResponse extends TransferInAPI.Transaction { */ fees?: number; + /** + * Payment instructions for executing the payment. + */ + paymentInstructions?: Array; + /** * The ID of the quote that was used to trigger this payment */ @@ -129,23 +119,6 @@ export namespace SandboxSendFundsResponse { } } -export interface SandboxSendTestWebhookResponse { - /** - * The HTTP status code returned by the webhook endpoint - */ - response_status: number; - - /** - * The raw body content returned by the webhook endpoint in response to the request - */ - response_body?: string; - - /** - * URL where the webhook was sent - */ - url?: string; -} - export interface SandboxSendFundsParams { /** * Currency code for the funds to be sent @@ -170,7 +143,6 @@ Sandbox.InternalAccounts = InternalAccounts; export declare namespace Sandbox { export { type SandboxSendFundsResponse as SandboxSendFundsResponse, - type SandboxSendTestWebhookResponse as SandboxSendTestWebhookResponse, type SandboxSendFundsParams as SandboxSendFundsParams, }; diff --git a/src/resources/transactions.ts b/src/resources/transactions.ts index 126d8be..1930b16 100644 --- a/src/resources/transactions.ts +++ b/src/resources/transactions.ts @@ -3,8 +3,8 @@ import { APIResource } from '../core/resource'; import * as TransactionsAPI from './transactions'; import * as InvitationsAPI from './invitations'; +import * as QuotesAPI from './quotes'; import * as TransferInAPI from './transfer-in'; -import { TransactionsDefaultPagination } from './transfer-in'; import { APIPromise } from '../core/api-promise'; import { DefaultPagination, type DefaultPaginationParams, PagePromise } from '../core/pagination'; import { RequestOptions } from '../internal/request-options'; @@ -21,7 +21,7 @@ export class Transactions extends APIResource { * ); * ``` */ - retrieve(transactionID: string, options?: RequestOptions): APIPromise { + retrieve(transactionID: string, options?: RequestOptions): APIPromise { return this._client.get(path`/transactions/${transactionID}`, options); } @@ -33,7 +33,7 @@ export class Transactions extends APIResource { * @example * ```ts * // Automatically fetches more pages as needed. - * for await (const transaction of client.transactions.list()) { + * for await (const transactionListResponse of client.transactions.list()) { * // ... * } * ``` @@ -41,8 +41,8 @@ export class Transactions extends APIResource { list( query: TransactionListParams | null | undefined = {}, options?: RequestOptions, - ): PagePromise { - return this._client.getAPIList('/transactions', DefaultPagination, { + ): PagePromise { + return this._client.getAPIList('/transactions', DefaultPagination, { query, ...options, }); @@ -87,6 +87,8 @@ export class Transactions extends APIResource { } } +export type TransactionListResponsesDefaultPagination = DefaultPagination; + export interface BaseTransactionSource { /** * Currency code for the source @@ -94,19 +96,14 @@ export interface BaseTransactionSource { currency?: string; } -/** - * Additional information about the counterparty, if available and relevant to the - * transaction and platform. Only applicable for transactions to/from UMA - * addresses. - */ -export type CounterpartyInformation = { [key: string]: unknown }; - -export interface IncomingTransaction extends TransferInAPI.Transaction { +export interface IncomingTransaction extends Omit { /** * Amount received in the recipient's currency */ receivedAmount: InvitationsAPI.CurrencyAmount; + type: 'INCOMING'; + /** * If the transaction failed, this field provides the reason for failure. */ @@ -179,45 +176,13 @@ export namespace IncomingTransaction { } } -/** - * Destination account details - */ -export type TransactionDestinationOneOf = - | TransactionDestinationOneOf.AccountTransactionDestination - | TransactionDestinationOneOf.UmaAddressTransactionDestination; - -export namespace TransactionDestinationOneOf { - /** - * Destination account details - */ - export interface AccountTransactionDestination { - /** - * Destination account identifier - */ - accountId: string; - - destinationType: 'ACCOUNT'; - } - - /** - * UMA address destination details - */ - export interface UmaAddressTransactionDestination { - destinationType: 'UMA_ADDRESS'; - - /** - * UMA address of the recipient - */ - umaAddress: string; - } -} - /** * Source account details */ export type TransactionSourceOneOf = | TransactionSourceOneOf.AccountTransactionSource - | TransactionSourceOneOf.UmaAddressTransactionSource; + | TransactionSourceOneOf.UmaAddressTransactionSource + | TransactionSourceOneOf.RealtimeFundingTransactionSource; export namespace TransactionSourceOneOf { /** @@ -243,15 +208,46 @@ export namespace TransactionSourceOneOf { */ umaAddress: string; } + + /** + * Transaction was funded using a real-time funding source (RTP, SEPA Instant, + * Spark, Stables, etc.). + */ + export interface RealtimeFundingTransactionSource extends TransactionsAPI.BaseTransactionSource { + /** + * Currency code for the funding source + */ + currency: string; + + sourceType: 'REALTIME_FUNDING'; + + /** + * The customer on whose behalf the transaction was initiated. + */ + customerId?: string; + } } /** - * Status of a payment transaction + * Status of a payment transaction. + * + * | Status | Description | + * | ------------ | -------------------------------------------------------------------------------------------------- | + * | `CREATED` | Initial lookup has been created | + * | `PENDING` | Quote has been created | + * | `PROCESSING` | Funding has been received and payment initiated | + * | `SENT` | Cross border settlement has been initiated | + * | `COMPLETED` | Cross border payment has been received, converted and payment has been sent to the offramp network | + * | `REJECTED` | Receiving institution or wallet rejected payment, payment has been refunded | + * | `FAILED` | An error occurred during payment | + * | `REFUNDED` | Payment was unable to complete and refunded | + * | `EXPIRED` | Quote has expired | */ export type TransactionStatus = | 'CREATED' | 'PENDING' | 'PROCESSING' + | 'SENT' | 'COMPLETED' | 'REJECTED' | 'FAILED' @@ -263,6 +259,182 @@ export type TransactionStatus = */ export type TransactionType = 'INCOMING' | 'OUTGOING'; +export type TransactionRetrieveResponse = + | IncomingTransaction + | TransactionRetrieveResponse.OutgoingTransaction; + +export namespace TransactionRetrieveResponse { + export interface OutgoingTransaction extends Omit { + /** + * Amount sent in the sender's currency + */ + sentAmount: InvitationsAPI.CurrencyAmount; + + /** + * Source account details + */ + source: TransactionsAPI.TransactionSourceOneOf; + + type: 'OUTGOING'; + + /** + * Number of sending currency units per receiving currency unit. + */ + exchangeRate?: number; + + /** + * If the transaction failed, this field provides the reason for failure. + */ + failureReason?: + | 'QUOTE_EXPIRED' + | 'QUOTE_EXECUTION_FAILED' + | 'LIGHTNING_PAYMENT_FAILED' + | 'FUNDING_AMOUNT_MISMATCH' + | 'COUNTERPARTY_POST_TX_FAILED' + | 'TIMEOUT'; + + /** + * The fees associated with the quote in the smallest unit of the sending currency + * (eg. cents). + */ + fees?: number; + + /** + * Payment instructions for executing the payment. + */ + paymentInstructions?: Array; + + /** + * The ID of the quote that was used to trigger this payment + */ + quoteId?: string; + + /** + * Details about the rate and fees for the transaction. + */ + rateDetails?: QuotesAPI.OutgoingRateDetails; + + /** + * Amount to be received by recipient in the recipient's currency + */ + receivedAmount?: InvitationsAPI.CurrencyAmount; + + /** + * The refund if transaction was refunded. + */ + refund?: OutgoingTransaction.Refund; + } + + export namespace OutgoingTransaction { + /** + * The refund if transaction was refunded. + */ + export interface Refund { + /** + * When the refund was initiated + */ + initiatedAt: string; + + /** + * The unique reference code of the refund + */ + reference: string; + + /** + * When the refund was or will be settled + */ + settledAt?: string; + } + } +} + +export type TransactionListResponse = IncomingTransaction | TransactionListResponse.OutgoingTransaction; + +export namespace TransactionListResponse { + export interface OutgoingTransaction extends Omit { + /** + * Amount sent in the sender's currency + */ + sentAmount: InvitationsAPI.CurrencyAmount; + + /** + * Source account details + */ + source: TransactionsAPI.TransactionSourceOneOf; + + type: 'OUTGOING'; + + /** + * Number of sending currency units per receiving currency unit. + */ + exchangeRate?: number; + + /** + * If the transaction failed, this field provides the reason for failure. + */ + failureReason?: + | 'QUOTE_EXPIRED' + | 'QUOTE_EXECUTION_FAILED' + | 'LIGHTNING_PAYMENT_FAILED' + | 'FUNDING_AMOUNT_MISMATCH' + | 'COUNTERPARTY_POST_TX_FAILED' + | 'TIMEOUT'; + + /** + * The fees associated with the quote in the smallest unit of the sending currency + * (eg. cents). + */ + fees?: number; + + /** + * Payment instructions for executing the payment. + */ + paymentInstructions?: Array; + + /** + * The ID of the quote that was used to trigger this payment + */ + quoteId?: string; + + /** + * Details about the rate and fees for the transaction. + */ + rateDetails?: QuotesAPI.OutgoingRateDetails; + + /** + * Amount to be received by recipient in the recipient's currency + */ + receivedAmount?: InvitationsAPI.CurrencyAmount; + + /** + * The refund if transaction was refunded. + */ + refund?: OutgoingTransaction.Refund; + } + + export namespace OutgoingTransaction { + /** + * The refund if transaction was refunded. + */ + export interface Refund { + /** + * When the refund was initiated + */ + initiatedAt: string; + + /** + * The unique reference code of the refund + */ + reference: string; + + /** + * When the refund was or will be settled + */ + settledAt?: string; + } + } +} + export interface TransactionListParams extends DefaultPaginationParams { /** * Filter by system customer ID @@ -339,16 +511,15 @@ export interface TransactionRejectParams { export declare namespace Transactions { export { type BaseTransactionSource as BaseTransactionSource, - type CounterpartyInformation as CounterpartyInformation, type IncomingTransaction as IncomingTransaction, - type TransactionDestinationOneOf as TransactionDestinationOneOf, type TransactionSourceOneOf as TransactionSourceOneOf, type TransactionStatus as TransactionStatus, type TransactionType as TransactionType, + type TransactionRetrieveResponse as TransactionRetrieveResponse, + type TransactionListResponse as TransactionListResponse, + type TransactionListResponsesDefaultPagination as TransactionListResponsesDefaultPagination, type TransactionListParams as TransactionListParams, type TransactionApproveParams as TransactionApproveParams, type TransactionRejectParams as TransactionRejectParams, }; } - -export { type TransactionsDefaultPagination }; diff --git a/src/resources/transfer-in.ts b/src/resources/transfer-in.ts index 3bc3ca4..67fdeb2 100644 --- a/src/resources/transfer-in.ts +++ b/src/resources/transfer-in.ts @@ -1,9 +1,12 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. import { APIResource } from '../core/resource'; +import * as TransferInAPI from './transfer-in'; +import * as InvitationsAPI from './invitations'; +import * as QuotesAPI from './quotes'; import * as TransactionsAPI from './transactions'; +import * as ExternalAccountsAPI from './customers/external-accounts'; import { APIPromise } from '../core/api-promise'; -import { DefaultPagination } from '../core/pagination'; import { buildHeaders } from '../internal/headers'; import { RequestOptions } from '../internal/request-options'; @@ -16,7 +19,7 @@ export class TransferIn extends APIResource { * * @example * ```ts - * const transaction = await client.transferIn.create({ + * const transferIn = await client.transferIn.create({ * destination: { * accountId: * 'InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123', @@ -29,7 +32,7 @@ export class TransferIn extends APIResource { * }); * ``` */ - create(params: TransferInCreateParams, options?: RequestOptions): APIPromise { + create(params: TransferInCreateParams, options?: RequestOptions): APIPromise { const { 'Idempotency-Key': idempotencyKey, ...body } = params; return this._client.post('/transfer-in', { body, @@ -42,8 +45,6 @@ export class TransferIn extends APIResource { } } -export type TransactionsDefaultPagination = DefaultPagination; - export interface BaseTransactionDestination { /** * Currency code for the destination @@ -65,7 +66,10 @@ export interface Transaction { /** * Destination account details */ - destination: TransactionsAPI.TransactionDestinationOneOf; + destination: + | Transaction.AccountTransactionDestination + | Transaction.UmaAddressTransactionDestination + | Transaction.ExternalAccountDetailsTransactionDestination; /** * Platform-specific ID of the customer (sender for outgoing, recipient for @@ -74,7 +78,19 @@ export interface Transaction { platformCustomerId: string; /** - * Status of a payment transaction + * Status of a payment transaction. + * + * | Status | Description | + * | ------------ | -------------------------------------------------------------------------------------------------- | + * | `CREATED` | Initial lookup has been created | + * | `PENDING` | Quote has been created | + * | `PROCESSING` | Funding has been received and payment initiated | + * | `SENT` | Cross border settlement has been initiated | + * | `COMPLETED` | Cross border payment has been received, converted and payment has been sent to the offramp network | + * | `REJECTED` | Receiving institution or wallet rejected payment, payment has been refunded | + * | `FAILED` | An error occurred during payment | + * | `REFUNDED` | Payment was unable to complete and refunded | + * | `EXPIRED` | Quote has expired | */ status: TransactionsAPI.TransactionStatus; @@ -88,7 +104,7 @@ export interface Transaction { * transaction and platform. Only applicable for transactions to/from UMA * addresses. */ - counterpartyInformation?: TransactionsAPI.CounterpartyInformation; + counterpartyInformation?: { [key: string]: unknown }; /** * When the transaction was created @@ -111,6 +127,132 @@ export interface Transaction { updatedAt?: string; } +export namespace Transaction { + /** + * Destination account details + */ + export interface AccountTransactionDestination extends TransferInAPI.BaseTransactionDestination { + /** + * Destination account identifier + */ + accountId: string; + + destinationType: 'ACCOUNT'; + } + + /** + * UMA address destination details + */ + export interface UmaAddressTransactionDestination extends TransferInAPI.BaseTransactionDestination { + destinationType: 'UMA_ADDRESS'; + + /** + * UMA address of the recipient + */ + umaAddress: string; + } + + /** + * Transaction destination where external account details were provided inline at + * quote creation rather than using a pre-registered external account. + */ + export interface ExternalAccountDetailsTransactionDestination + extends TransferInAPI.BaseTransactionDestination { + destinationType: 'EXTERNAL_ACCOUNT_DETAILS'; + + externalAccountDetails: ExternalAccountsAPI.ExternalAccountCreate; + } +} + +export type TransferInCreateResponse = + | TransactionsAPI.IncomingTransaction + | TransferInCreateResponse.OutgoingTransaction; + +export namespace TransferInCreateResponse { + export interface OutgoingTransaction extends Omit { + /** + * Amount sent in the sender's currency + */ + sentAmount: InvitationsAPI.CurrencyAmount; + + /** + * Source account details + */ + source: TransactionsAPI.TransactionSourceOneOf; + + type: 'OUTGOING'; + + /** + * Number of sending currency units per receiving currency unit. + */ + exchangeRate?: number; + + /** + * If the transaction failed, this field provides the reason for failure. + */ + failureReason?: + | 'QUOTE_EXPIRED' + | 'QUOTE_EXECUTION_FAILED' + | 'LIGHTNING_PAYMENT_FAILED' + | 'FUNDING_AMOUNT_MISMATCH' + | 'COUNTERPARTY_POST_TX_FAILED' + | 'TIMEOUT'; + + /** + * The fees associated with the quote in the smallest unit of the sending currency + * (eg. cents). + */ + fees?: number; + + /** + * Payment instructions for executing the payment. + */ + paymentInstructions?: Array; + + /** + * The ID of the quote that was used to trigger this payment + */ + quoteId?: string; + + /** + * Details about the rate and fees for the transaction. + */ + rateDetails?: QuotesAPI.OutgoingRateDetails; + + /** + * Amount to be received by recipient in the recipient's currency + */ + receivedAmount?: InvitationsAPI.CurrencyAmount; + + /** + * The refund if transaction was refunded. + */ + refund?: OutgoingTransaction.Refund; + } + + export namespace OutgoingTransaction { + /** + * The refund if transaction was refunded. + */ + export interface Refund { + /** + * When the refund was initiated + */ + initiatedAt: string; + + /** + * The unique reference code of the refund + */ + reference: string; + + /** + * When the refund was or will be settled + */ + settledAt?: string; + } + } +} + export interface TransferInCreateParams { /** * Body param: Destination internal account details @@ -161,6 +303,7 @@ export declare namespace TransferIn { export { type BaseTransactionDestination as BaseTransactionDestination, type Transaction as Transaction, + type TransferInCreateResponse as TransferInCreateResponse, type TransferInCreateParams as TransferInCreateParams, }; } diff --git a/src/resources/transfer-out.ts b/src/resources/transfer-out.ts index bdad0f8..323cd03 100644 --- a/src/resources/transfer-out.ts +++ b/src/resources/transfer-out.ts @@ -1,6 +1,9 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. import { APIResource } from '../core/resource'; +import * as InvitationsAPI from './invitations'; +import * as QuotesAPI from './quotes'; +import * as TransactionsAPI from './transactions'; import * as TransferInAPI from './transfer-in'; import { APIPromise } from '../core/api-promise'; import { buildHeaders } from '../internal/headers'; @@ -13,7 +16,7 @@ export class TransferOut extends APIResource { * * @example * ```ts - * const transaction = await client.transferOut.create({ + * const transferOut = await client.transferOut.create({ * destination: { * accountId: * 'ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965', @@ -26,7 +29,7 @@ export class TransferOut extends APIResource { * }); * ``` */ - create(params: TransferOutCreateParams, options?: RequestOptions): APIPromise { + create(params: TransferOutCreateParams, options?: RequestOptions): APIPromise { const { 'Idempotency-Key': idempotencyKey, ...body } = params; return this._client.post('/transfer-out', { body, @@ -39,6 +42,95 @@ export class TransferOut extends APIResource { } } +export type TransferOutCreateResponse = + | TransactionsAPI.IncomingTransaction + | TransferOutCreateResponse.OutgoingTransaction; + +export namespace TransferOutCreateResponse { + export interface OutgoingTransaction extends Omit { + /** + * Amount sent in the sender's currency + */ + sentAmount: InvitationsAPI.CurrencyAmount; + + /** + * Source account details + */ + source: TransactionsAPI.TransactionSourceOneOf; + + type: 'OUTGOING'; + + /** + * Number of sending currency units per receiving currency unit. + */ + exchangeRate?: number; + + /** + * If the transaction failed, this field provides the reason for failure. + */ + failureReason?: + | 'QUOTE_EXPIRED' + | 'QUOTE_EXECUTION_FAILED' + | 'LIGHTNING_PAYMENT_FAILED' + | 'FUNDING_AMOUNT_MISMATCH' + | 'COUNTERPARTY_POST_TX_FAILED' + | 'TIMEOUT'; + + /** + * The fees associated with the quote in the smallest unit of the sending currency + * (eg. cents). + */ + fees?: number; + + /** + * Payment instructions for executing the payment. + */ + paymentInstructions?: Array; + + /** + * The ID of the quote that was used to trigger this payment + */ + quoteId?: string; + + /** + * Details about the rate and fees for the transaction. + */ + rateDetails?: QuotesAPI.OutgoingRateDetails; + + /** + * Amount to be received by recipient in the recipient's currency + */ + receivedAmount?: InvitationsAPI.CurrencyAmount; + + /** + * The refund if transaction was refunded. + */ + refund?: OutgoingTransaction.Refund; + } + + export namespace OutgoingTransaction { + /** + * The refund if transaction was refunded. + */ + export interface Refund { + /** + * When the refund was initiated + */ + initiatedAt: string; + + /** + * The unique reference code of the refund + */ + reference: string; + + /** + * When the refund was or will be settled + */ + settledAt?: string; + } + } +} + export interface TransferOutCreateParams { /** * Body param: Destination external account details @@ -86,5 +178,8 @@ export namespace TransferOutCreateParams { } export declare namespace TransferOut { - export { type TransferOutCreateParams as TransferOutCreateParams }; + export { + type TransferOutCreateResponse as TransferOutCreateResponse, + type TransferOutCreateParams as TransferOutCreateParams, + }; } diff --git a/src/resources/webhooks.ts b/src/resources/webhooks.ts index 7afb16e..c9b60e1 100644 --- a/src/resources/webhooks.ts +++ b/src/resources/webhooks.ts @@ -1,417 +1,35 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. import { APIResource } from '../core/resource'; -import * as InvitationsAPI from './invitations'; -import * as QuotesAPI from './quotes'; -import * as ReceiverAPI from './receiver'; -import * as TransactionsAPI from './transactions'; -import * as TransferInAPI from './transfer-in'; +import { APIPromise } from '../core/api-promise'; +import { RequestOptions } from '../internal/request-options'; export class Webhooks extends APIResource { - unwrap(body: string): UnwrapWebhookEvent { - return JSON.parse(body) as UnwrapWebhookEvent; - } -} - -export interface IncomingPaymentWebhookEvent { - /** - * ISO8601 timestamp when the webhook was sent (can be used to prevent replay - * attacks) - */ - timestamp: string; - - transaction: TransactionsAPI.IncomingTransaction; - /** - * Type of webhook event + * Send a test webhook to the configured endpoint */ - type: - | 'INCOMING_PAYMENT' - | 'OUTGOING_PAYMENT' - | 'TEST' - | 'BULK_UPLOAD' - | 'INVITATION_CLAIMED' - | 'KYC_STATUS' - | 'ACCOUNT_STATUS'; - - /** - * Unique identifier for this webhook delivery (can be used for idempotency) - */ - webhookId: string; - - /** - * Information required by the sender's VASP about the recipient. Platform must - * provide these in the 200 OK response if approving. Note that this only includes - * fields which Grid does not already have from initial customer registration. - */ - requestedReceiverCustomerInfoFields?: Array; -} - -export interface OutgoingPaymentWebhookEvent { - /** - * ISO8601 timestamp when the webhook was sent (can be used to prevent replay - * attacks) - */ - timestamp: string; - - transaction: OutgoingPaymentWebhookEvent.Transaction; - - /** - * Type of webhook event - */ - type: - | 'INCOMING_PAYMENT' - | 'OUTGOING_PAYMENT' - | 'TEST' - | 'BULK_UPLOAD' - | 'INVITATION_CLAIMED' - | 'KYC_STATUS' - | 'ACCOUNT_STATUS'; - - /** - * Unique identifier for this webhook delivery (can be used for idempotency) - */ - webhookId: string; -} - -export namespace OutgoingPaymentWebhookEvent { - export interface Transaction extends TransferInAPI.Transaction { - /** - * Payment instructions for executing the payment. - */ - paymentInstructions: Array; - - /** - * Amount sent in the sender's currency - */ - sentAmount: InvitationsAPI.CurrencyAmount; - - /** - * Source account details - */ - source: TransactionsAPI.TransactionSourceOneOf; - - /** - * Number of sending currency units per receiving currency unit. - */ - exchangeRate?: number; - - /** - * If the transaction failed, this field provides the reason for failure. - */ - failureReason?: - | 'QUOTE_EXPIRED' - | 'QUOTE_EXECUTION_FAILED' - | 'LIGHTNING_PAYMENT_FAILED' - | 'FUNDING_AMOUNT_MISMATCH' - | 'COUNTERPARTY_POST_TX_FAILED' - | 'TIMEOUT'; - - /** - * The fees associated with the quote in the smallest unit of the sending currency - * (eg. cents). - */ - fees?: number; - - /** - * The ID of the quote that was used to trigger this payment - */ - quoteId?: string; - - /** - * Details about the rate and fees for the transaction. - */ - rateDetails?: QuotesAPI.OutgoingRateDetails; - - /** - * Amount to be received by recipient in the recipient's currency - */ - receivedAmount?: InvitationsAPI.CurrencyAmount; - - /** - * The refund if transaction was refunded. - */ - refund?: Transaction.Refund; - } - - export namespace Transaction { - /** - * The refund if transaction was refunded. - */ - export interface Refund { - /** - * When the refund was initiated - */ - initiatedAt: string; - - /** - * The unique reference code of the refund - */ - reference: string; - - /** - * When the refund was or will be settled - */ - settledAt?: string; - } + sendTest(options?: RequestOptions): APIPromise { + return this._client.post('/webhooks/test', options); } } -export interface TestWebhookWebhookEvent { +export interface WebhookSendTestResponse { /** - * ISO8601 timestamp when the webhook was sent (can be used to prevent replay - * attacks) + * The HTTP status code returned by the webhook endpoint */ - timestamp: string; + response_status: number; /** - * Type of webhook event + * The raw body content returned by the webhook endpoint in response to the request */ - type: - | 'INCOMING_PAYMENT' - | 'OUTGOING_PAYMENT' - | 'TEST' - | 'BULK_UPLOAD' - | 'INVITATION_CLAIMED' - | 'KYC_STATUS' - | 'ACCOUNT_STATUS'; + response_body?: string; /** - * Unique identifier for this webhook delivery (can be used for idempotency) + * URL where the webhook was sent */ - webhookId: string; + url?: string; } -export interface BulkUploadWebhookEvent { - bulkCustomerImportJob: BulkUploadWebhookEvent.BulkCustomerImportJob; - - /** - * ISO8601 timestamp when the webhook was sent (can be used to prevent replay - * attacks) - */ - timestamp: string; - - /** - * Type of webhook event - */ - type: - | 'INCOMING_PAYMENT' - | 'OUTGOING_PAYMENT' - | 'TEST' - | 'BULK_UPLOAD' - | 'INVITATION_CLAIMED' - | 'KYC_STATUS' - | 'ACCOUNT_STATUS'; - - /** - * Unique identifier for this webhook delivery (can be used for idempotency) - */ - webhookId: string; -} - -export namespace BulkUploadWebhookEvent { - export interface BulkCustomerImportJob { - /** - * Unique identifier for the bulk import job - */ - jobId: string; - - progress: BulkCustomerImportJob.Progress; - - /** - * Current status of the job - */ - status: 'PENDING' | 'PROCESSING' | 'COMPLETED' | 'FAILED'; - - /** - * Timestamp when the job completed (only present for COMPLETED or FAILED status) - */ - completedAt?: string; - - /** - * Detailed error information for failed entries - */ - errors?: Array; - } - - export namespace BulkCustomerImportJob { - export interface Progress { - /** - * Number of customers that failed to create - */ - failed: number; - - /** - * Number of customers processed so far - */ - processed: number; - - /** - * Number of customers successfully created - */ - successful: number; - - /** - * Total number of customers to process - */ - total: number; - } - - export interface Error { - /** - * Platform customer ID or row number for the failed entry - */ - correlationId: string; - - /** - * Error code - */ - code?: string; - - /** - * Additional error details - */ - details?: { [key: string]: unknown }; - - /** - * Error message - */ - message?: string; - } - } -} - -export interface InvitationClaimedWebhookEvent { - invitation: InvitationsAPI.UmaInvitation; - - /** - * ISO8601 timestamp when the webhook was sent (can be used to prevent replay - * attacks) - */ - timestamp: string; - - /** - * Type of webhook event - */ - type: - | 'INVITATION_CLAIMED' - | 'INCOMING_PAYMENT' - | 'OUTGOING_PAYMENT' - | 'TEST' - | 'BULK_UPLOAD' - | 'KYC_STATUS' - | 'ACCOUNT_STATUS'; - - /** - * Unique identifier for this webhook delivery (can be used for idempotency) - */ - webhookId: string; -} - -export interface KYCStatusWebhookEvent { - /** - * System generated id of the customer - */ - customerId: string; - - /** - * The current KYC status of a customer - */ - kycStatus: - | 'APPROVED' - | 'REJECTED' - | 'PENDING_REVIEW' - | 'EXPIRED' - | 'CANCELED' - | 'MANUALLY_APPROVED' - | 'MANUALLY_REJECTED'; - - /** - * ISO8601 timestamp when the webhook was sent (can be used to prevent replay - * attacks) - */ - timestamp: string; - - /** - * Type of webhook event - */ - type: - | 'INCOMING_PAYMENT' - | 'OUTGOING_PAYMENT' - | 'TEST' - | 'BULK_UPLOAD' - | 'INVITATION_CLAIMED' - | 'KYC_STATUS' - | 'ACCOUNT_STATUS'; - - /** - * Unique identifier for this webhook delivery (can be used for idempotency) - */ - webhookId: string; -} - -export interface AccountStatusWebhookEvent { - /** - * The id of the account whose balance has changed - */ - accountId: string; - - /** - * ISO8601 timestamp when the webhook was sent (can be used to prevent replay - * attacks) - */ - timestamp: string; - - /** - * Type of webhook event - */ - type: - | 'INCOMING_PAYMENT' - | 'OUTGOING_PAYMENT' - | 'TEST' - | 'BULK_UPLOAD' - | 'INVITATION_CLAIMED' - | 'KYC_STATUS' - | 'ACCOUNT_STATUS'; - - /** - * Unique identifier for this webhook delivery (can be used for idempotency) - */ - webhookId: string; - - /** - * The ID of the customer associated with the internal account - */ - customerId?: string; - - newBalance?: InvitationsAPI.CurrencyAmount; - - oldBalance?: InvitationsAPI.CurrencyAmount; - - /** - * The ID of the customer as associated in your platform - */ - platformCustomerId?: string; -} - -export type UnwrapWebhookEvent = - | IncomingPaymentWebhookEvent - | OutgoingPaymentWebhookEvent - | TestWebhookWebhookEvent - | BulkUploadWebhookEvent - | InvitationClaimedWebhookEvent - | KYCStatusWebhookEvent - | AccountStatusWebhookEvent; - export declare namespace Webhooks { - export { - type IncomingPaymentWebhookEvent as IncomingPaymentWebhookEvent, - type OutgoingPaymentWebhookEvent as OutgoingPaymentWebhookEvent, - type TestWebhookWebhookEvent as TestWebhookWebhookEvent, - type BulkUploadWebhookEvent as BulkUploadWebhookEvent, - type InvitationClaimedWebhookEvent as InvitationClaimedWebhookEvent, - type KYCStatusWebhookEvent as KYCStatusWebhookEvent, - type AccountStatusWebhookEvent as AccountStatusWebhookEvent, - type UnwrapWebhookEvent as UnwrapWebhookEvent, - }; + export { type WebhookSendTestResponse as WebhookSendTestResponse }; } diff --git a/src/version.ts b/src/version.ts index 30c2817..d9da9f7 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1 +1 @@ -export const VERSION = '0.6.0'; // x-release-please-version +export const VERSION = '0.7.0'; // x-release-please-version diff --git a/tests/api-resources/config.test.ts b/tests/api-resources/config.test.ts index 7b7c86e..70a7668 100644 --- a/tests/api-resources/config.test.ts +++ b/tests/api-resources/config.test.ts @@ -9,7 +9,7 @@ const client = new LightsparkGrid({ }); describe('resource config', () => { - // Prism tests are disabled + // Mock server tests are disabled test.skip('retrieve', async () => { const responsePromise = client.config.retrieve(); const rawResponse = await responsePromise.asResponse(); @@ -21,7 +21,7 @@ describe('resource config', () => { expect(dataAndResponse.response).toBe(rawResponse); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('update', async () => { const responsePromise = client.config.update({}); const rawResponse = await responsePromise.asResponse(); diff --git a/tests/api-resources/customers/bulk.test.ts b/tests/api-resources/customers/bulk.test.ts index 8308089..5b48ac8 100644 --- a/tests/api-resources/customers/bulk.test.ts +++ b/tests/api-resources/customers/bulk.test.ts @@ -9,7 +9,7 @@ const client = new LightsparkGrid({ }); describe('resource bulk', () => { - // Prism tests are disabled + // Mock server tests are disabled test.skip('getJobStatus', async () => { const responsePromise = client.customers.bulk.getJobStatus('jobId'); const rawResponse = await responsePromise.asResponse(); @@ -21,7 +21,7 @@ describe('resource bulk', () => { expect(dataAndResponse.response).toBe(rawResponse); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('uploadCsv: only required params', async () => { const responsePromise = client.customers.bulk.uploadCsv({ file: await toFile(Buffer.from('# my file contents'), 'README.md'), @@ -35,7 +35,7 @@ describe('resource bulk', () => { expect(dataAndResponse.response).toBe(rawResponse); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('uploadCsv: required and optional params', async () => { const response = await client.customers.bulk.uploadCsv({ file: await toFile(Buffer.from('# my file contents'), 'README.md'), diff --git a/tests/api-resources/customers/customers.test.ts b/tests/api-resources/customers/customers.test.ts index 08d2f4b..24e2e1d 100644 --- a/tests/api-resources/customers/customers.test.ts +++ b/tests/api-resources/customers/customers.test.ts @@ -9,7 +9,7 @@ const client = new LightsparkGrid({ }); describe('resource customers', () => { - // Prism tests are disabled + // Mock server tests are disabled test.skip('create: only required params', async () => { const responsePromise = client.customers.create({ CreateCustomerRequest: { platformCustomerId: '9f84e0c2a72c4fa', customerType: 'INDIVIDUAL' }, @@ -23,7 +23,7 @@ describe('resource customers', () => { expect(dataAndResponse.response).toBe(rawResponse); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('create: required and optional params', async () => { const response = await client.customers.create({ CreateCustomerRequest: { @@ -45,7 +45,7 @@ describe('resource customers', () => { }); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('retrieve', async () => { const responsePromise = client.customers.retrieve('customerId'); const rawResponse = await responsePromise.asResponse(); @@ -57,7 +57,7 @@ describe('resource customers', () => { expect(dataAndResponse.response).toBe(rawResponse); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('update: only required params', async () => { const responsePromise = client.customers.update('customerId', { UpdateCustomerRequest: { customerType: 'INDIVIDUAL' }, @@ -71,7 +71,7 @@ describe('resource customers', () => { expect(dataAndResponse.response).toBe(rawResponse); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('update: required and optional params', async () => { const response = await client.customers.update('customerId', { UpdateCustomerRequest: { @@ -92,7 +92,7 @@ describe('resource customers', () => { }); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('list', async () => { const responsePromise = client.customers.list(); const rawResponse = await responsePromise.asResponse(); @@ -104,7 +104,7 @@ describe('resource customers', () => { expect(dataAndResponse.response).toBe(rawResponse); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('list: request options and params are passed correctly', async () => { // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error await expect( @@ -126,7 +126,7 @@ describe('resource customers', () => { ).rejects.toThrow(LightsparkGrid.NotFoundError); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('delete', async () => { const responsePromise = client.customers.delete('customerId'); const rawResponse = await responsePromise.asResponse(); @@ -138,7 +138,7 @@ describe('resource customers', () => { expect(dataAndResponse.response).toBe(rawResponse); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('getKYCLink: only required params', async () => { const responsePromise = client.customers.getKYCLink({ platformCustomerId: 'platformCustomerId' }); const rawResponse = await responsePromise.asResponse(); @@ -150,7 +150,7 @@ describe('resource customers', () => { expect(dataAndResponse.response).toBe(rawResponse); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('getKYCLink: required and optional params', async () => { const response = await client.customers.getKYCLink({ platformCustomerId: 'platformCustomerId', @@ -158,7 +158,7 @@ describe('resource customers', () => { }); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('listInternalAccounts', async () => { const responsePromise = client.customers.listInternalAccounts(); const rawResponse = await responsePromise.asResponse(); @@ -170,7 +170,7 @@ describe('resource customers', () => { expect(dataAndResponse.response).toBe(rawResponse); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('listInternalAccounts: request options and params are passed correctly', async () => { // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error await expect( diff --git a/tests/api-resources/customers/external-accounts.test.ts b/tests/api-resources/customers/external-accounts.test.ts index f725fce..2fc21d5 100644 --- a/tests/api-resources/customers/external-accounts.test.ts +++ b/tests/api-resources/customers/external-accounts.test.ts @@ -9,20 +9,17 @@ const client = new LightsparkGrid({ }); describe('resource externalAccounts', () => { - // Prism tests are disabled + // Mock server tests are disabled test.skip('create: only required params', async () => { const responsePromise = client.customers.externalAccounts.create({ accountInfo: { - accountCategory: 'CHECKING', - accountNumber: '12345678901', - accountType: 'US_ACCOUNT', - beneficiary: { - beneficiaryType: 'INDIVIDUAL', - birthDate: '1990-01-15', - fullName: 'John Doe', - nationality: 'US', - }, - routingNumber: '123456789', + accountType: 'BRL_ACCOUNT', + beneficiary: { beneficiaryType: 'INDIVIDUAL', fullName: 'John Doe' }, + countries: ['BR'], + paymentRails: ['PIX'], + pixKey: 'pixKey', + pixKeyType: 'pixKeyType', + taxId: 'taxId', }, currency: 'USD', }); @@ -35,18 +32,14 @@ describe('resource externalAccounts', () => { expect(dataAndResponse.response).toBe(rawResponse); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('create: required and optional params', async () => { const response = await client.customers.externalAccounts.create({ accountInfo: { - accountCategory: 'CHECKING', - accountNumber: '12345678901', - accountType: 'US_ACCOUNT', + accountType: 'BRL_ACCOUNT', beneficiary: { beneficiaryType: 'INDIVIDUAL', - birthDate: '1990-01-15', fullName: 'John Doe', - nationality: 'US', address: { country: 'US', line1: '123 Main Street', @@ -55,9 +48,18 @@ describe('resource externalAccounts', () => { line2: 'Apt 4B', state: 'CA', }, + birthDate: '1990-01-15', + countryOfResidence: 'countryOfResidence', + email: 'email', + nationality: 'US', + phoneNumber: 'phoneNumber', + registrationNumber: 'registrationNumber', }, - routingNumber: '123456789', - bankName: 'Chase Bank', + countries: ['BR'], + paymentRails: ['PIX'], + pixKey: 'pixKey', + pixKeyType: 'pixKeyType', + taxId: 'taxId', }, currency: 'USD', customerId: 'Customer:019542f5-b3e7-1d02-0000-000000000001', @@ -66,7 +68,7 @@ describe('resource externalAccounts', () => { }); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('list', async () => { const responsePromise = client.customers.externalAccounts.list(); const rawResponse = await responsePromise.asResponse(); @@ -78,7 +80,7 @@ describe('resource externalAccounts', () => { expect(dataAndResponse.response).toBe(rawResponse); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('list: request options and params are passed correctly', async () => { // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error await expect( diff --git a/tests/api-resources/exchange-rates.test.ts b/tests/api-resources/exchange-rates.test.ts index 7edd2f7..0032e4e 100644 --- a/tests/api-resources/exchange-rates.test.ts +++ b/tests/api-resources/exchange-rates.test.ts @@ -9,7 +9,7 @@ const client = new LightsparkGrid({ }); describe('resource exchangeRates', () => { - // Prism tests are disabled + // Mock server tests are disabled test.skip('list', async () => { const responsePromise = client.exchangeRates.list(); const rawResponse = await responsePromise.asResponse(); @@ -21,7 +21,7 @@ describe('resource exchangeRates', () => { expect(dataAndResponse.response).toBe(rawResponse); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('list: request options and params are passed correctly', async () => { // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error await expect( diff --git a/tests/api-resources/invitations.test.ts b/tests/api-resources/invitations.test.ts index 91b4d4e..0239bcb 100644 --- a/tests/api-resources/invitations.test.ts +++ b/tests/api-resources/invitations.test.ts @@ -9,7 +9,7 @@ const client = new LightsparkGrid({ }); describe('resource invitations', () => { - // Prism tests are disabled + // Mock server tests are disabled test.skip('create: only required params', async () => { const responsePromise = client.invitations.create({ inviterUma: '$inviter@uma.domain' }); const rawResponse = await responsePromise.asResponse(); @@ -21,7 +21,7 @@ describe('resource invitations', () => { expect(dataAndResponse.response).toBe(rawResponse); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('create: required and optional params', async () => { const response = await client.invitations.create({ inviterUma: '$inviter@uma.domain', @@ -31,7 +31,7 @@ describe('resource invitations', () => { }); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('retrieve', async () => { const responsePromise = client.invitations.retrieve('invitationCode'); const rawResponse = await responsePromise.asResponse(); @@ -43,7 +43,7 @@ describe('resource invitations', () => { expect(dataAndResponse.response).toBe(rawResponse); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('cancel', async () => { const responsePromise = client.invitations.cancel('invitationCode'); const rawResponse = await responsePromise.asResponse(); @@ -55,7 +55,7 @@ describe('resource invitations', () => { expect(dataAndResponse.response).toBe(rawResponse); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('claim: only required params', async () => { const responsePromise = client.invitations.claim('invitationCode', { inviteeUma: '$invitee@uma.domain' }); const rawResponse = await responsePromise.asResponse(); @@ -67,7 +67,7 @@ describe('resource invitations', () => { expect(dataAndResponse.response).toBe(rawResponse); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('claim: required and optional params', async () => { const response = await client.invitations.claim('invitationCode', { inviteeUma: '$invitee@uma.domain' }); }); diff --git a/tests/api-resources/plaid.test.ts b/tests/api-resources/plaid.test.ts index 7ada757..667b130 100644 --- a/tests/api-resources/plaid.test.ts +++ b/tests/api-resources/plaid.test.ts @@ -9,7 +9,7 @@ const client = new LightsparkGrid({ }); describe('resource plaid', () => { - // Prism tests are disabled + // Mock server tests are disabled test.skip('createLinkToken: only required params', async () => { const responsePromise = client.plaid.createLinkToken({ customerId: 'Customer:019542f5-b3e7-1d02-0000-000000000001', @@ -23,14 +23,14 @@ describe('resource plaid', () => { expect(dataAndResponse.response).toBe(rawResponse); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('createLinkToken: required and optional params', async () => { const response = await client.plaid.createLinkToken({ customerId: 'Customer:019542f5-b3e7-1d02-0000-000000000001', }); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('submitPublicToken: only required params', async () => { const responsePromise = client.plaid.submitPublicToken('link-sandbox-abc123xyz-1234-5678', { publicToken: 'public-sandbox-12345678-1234-1234-1234-123456789012', @@ -44,7 +44,7 @@ describe('resource plaid', () => { expect(dataAndResponse.response).toBe(rawResponse); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('submitPublicToken: required and optional params', async () => { const response = await client.plaid.submitPublicToken('link-sandbox-abc123xyz-1234-5678', { publicToken: 'public-sandbox-12345678-1234-1234-1234-123456789012', diff --git a/tests/api-resources/platform/external-accounts.test.ts b/tests/api-resources/platform/external-accounts.test.ts index a3238b1..9f94b31 100644 --- a/tests/api-resources/platform/external-accounts.test.ts +++ b/tests/api-resources/platform/external-accounts.test.ts @@ -9,20 +9,17 @@ const client = new LightsparkGrid({ }); describe('resource externalAccounts', () => { - // Prism tests are disabled + // Mock server tests are disabled test.skip('create: only required params', async () => { const responsePromise = client.platform.externalAccounts.create({ accountInfo: { - accountCategory: 'CHECKING', - accountNumber: '12345678901', - accountType: 'US_ACCOUNT', - beneficiary: { - beneficiaryType: 'INDIVIDUAL', - birthDate: '1990-01-15', - fullName: 'John Doe', - nationality: 'US', - }, - routingNumber: '123456789', + accountType: 'BRL_ACCOUNT', + beneficiary: { beneficiaryType: 'INDIVIDUAL', fullName: 'John Doe' }, + countries: ['BR'], + paymentRails: ['PIX'], + pixKey: 'pixKey', + pixKeyType: 'pixKeyType', + taxId: 'taxId', }, currency: 'USD', }); @@ -35,18 +32,14 @@ describe('resource externalAccounts', () => { expect(dataAndResponse.response).toBe(rawResponse); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('create: required and optional params', async () => { const response = await client.platform.externalAccounts.create({ accountInfo: { - accountCategory: 'CHECKING', - accountNumber: '12345678901', - accountType: 'US_ACCOUNT', + accountType: 'BRL_ACCOUNT', beneficiary: { beneficiaryType: 'INDIVIDUAL', - birthDate: '1990-01-15', fullName: 'John Doe', - nationality: 'US', address: { country: 'US', line1: '123 Main Street', @@ -55,18 +48,25 @@ describe('resource externalAccounts', () => { line2: 'Apt 4B', state: 'CA', }, + birthDate: '1990-01-15', + countryOfResidence: 'countryOfResidence', + email: 'email', + nationality: 'US', + phoneNumber: 'phoneNumber', + registrationNumber: 'registrationNumber', }, - routingNumber: '123456789', - bankName: 'Chase Bank', + countries: ['BR'], + paymentRails: ['PIX'], + pixKey: 'pixKey', + pixKeyType: 'pixKeyType', + taxId: 'taxId', }, currency: 'USD', - customerId: 'Customer:019542f5-b3e7-1d02-0000-000000000001', - defaultUmaDepositAccount: true, platformAccountId: 'ext_acc_123456', }); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('list', async () => { const responsePromise = client.platform.externalAccounts.list(); const rawResponse = await responsePromise.asResponse(); @@ -78,7 +78,7 @@ describe('resource externalAccounts', () => { expect(dataAndResponse.response).toBe(rawResponse); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('list: request options and params are passed correctly', async () => { // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error await expect( diff --git a/tests/api-resources/platform/platform.test.ts b/tests/api-resources/platform/platform.test.ts index 69183a3..934902e 100644 --- a/tests/api-resources/platform/platform.test.ts +++ b/tests/api-resources/platform/platform.test.ts @@ -9,7 +9,7 @@ const client = new LightsparkGrid({ }); describe('resource platform', () => { - // Prism tests are disabled + // Mock server tests are disabled test.skip('listInternalAccounts', async () => { const responsePromise = client.platform.listInternalAccounts(); const rawResponse = await responsePromise.asResponse(); @@ -21,7 +21,7 @@ describe('resource platform', () => { expect(dataAndResponse.response).toBe(rawResponse); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('listInternalAccounts: request options and params are passed correctly', async () => { // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error await expect( diff --git a/tests/api-resources/quotes.test.ts b/tests/api-resources/quotes.test.ts index 39b7954..dfd6af2 100644 --- a/tests/api-resources/quotes.test.ts +++ b/tests/api-resources/quotes.test.ts @@ -9,7 +9,7 @@ const client = new LightsparkGrid({ }); describe('resource quotes', () => { - // Prism tests are disabled + // Mock server tests are disabled test.skip('create: only required params', async () => { const responsePromise = client.quotes.create({ destination: { @@ -29,7 +29,7 @@ describe('resource quotes', () => { expect(dataAndResponse.response).toBe(rawResponse); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('create: required and optional params', async () => { const response = await client.quotes.create({ destination: { @@ -46,11 +46,12 @@ describe('resource quotes', () => { description: 'Transfer between accounts, either internal or external.', immediatelyExecute: false, lookupId: 'Lookup:019542f5-b3e7-1d02-0000-000000000009', + purposeOfPayment: 'GIFT', senderCustomerInfo: { FULL_NAME: 'bar', NATIONALITY: 'bar' }, }); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('retrieve', async () => { const responsePromise = client.quotes.retrieve('quoteId'); const rawResponse = await responsePromise.asResponse(); @@ -62,7 +63,7 @@ describe('resource quotes', () => { expect(dataAndResponse.response).toBe(rawResponse); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('list', async () => { const responsePromise = client.quotes.list(); const rawResponse = await responsePromise.asResponse(); @@ -74,7 +75,7 @@ describe('resource quotes', () => { expect(dataAndResponse.response).toBe(rawResponse); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('list: request options and params are passed correctly', async () => { // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error await expect( @@ -96,7 +97,7 @@ describe('resource quotes', () => { ).rejects.toThrow(LightsparkGrid.NotFoundError); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('execute', async () => { const responsePromise = client.quotes.execute('Quote:019542f5-b3e7-1d02-0000-000000000001'); const rawResponse = await responsePromise.asResponse(); diff --git a/tests/api-resources/receiver.test.ts b/tests/api-resources/receiver.test.ts index bfb256f..a11b75b 100644 --- a/tests/api-resources/receiver.test.ts +++ b/tests/api-resources/receiver.test.ts @@ -9,7 +9,7 @@ const client = new LightsparkGrid({ }); describe('resource receiver', () => { - // Prism tests are disabled + // Mock server tests are disabled test.skip('lookupExternalAccount', async () => { const responsePromise = client.receiver.lookupExternalAccount( 'ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965', @@ -23,7 +23,7 @@ describe('resource receiver', () => { expect(dataAndResponse.response).toBe(rawResponse); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('lookupExternalAccount: request options and params are passed correctly', async () => { // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error await expect( @@ -35,7 +35,7 @@ describe('resource receiver', () => { ).rejects.toThrow(LightsparkGrid.NotFoundError); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('lookupUma', async () => { const responsePromise = client.receiver.lookupUma('receiverUmaAddress'); const rawResponse = await responsePromise.asResponse(); @@ -47,7 +47,7 @@ describe('resource receiver', () => { expect(dataAndResponse.response).toBe(rawResponse); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('lookupUma: request options and params are passed correctly', async () => { // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error await expect( diff --git a/tests/api-resources/sandbox/internal-accounts.test.ts b/tests/api-resources/sandbox/internal-accounts.test.ts index 37f00a8..92110c5 100644 --- a/tests/api-resources/sandbox/internal-accounts.test.ts +++ b/tests/api-resources/sandbox/internal-accounts.test.ts @@ -9,7 +9,7 @@ const client = new LightsparkGrid({ }); describe('resource internalAccounts', () => { - // Prism tests are disabled + // Mock server tests are disabled test.skip('fund: only required params', async () => { const responsePromise = client.sandbox.internalAccounts.fund( 'InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123', @@ -24,7 +24,7 @@ describe('resource internalAccounts', () => { expect(dataAndResponse.response).toBe(rawResponse); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('fund: required and optional params', async () => { const response = await client.sandbox.internalAccounts.fund( 'InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123', diff --git a/tests/api-resources/sandbox/sandbox.test.ts b/tests/api-resources/sandbox/sandbox.test.ts index 9c281a0..d12cbde 100644 --- a/tests/api-resources/sandbox/sandbox.test.ts +++ b/tests/api-resources/sandbox/sandbox.test.ts @@ -9,7 +9,7 @@ const client = new LightsparkGrid({ }); describe('resource sandbox', () => { - // Prism tests are disabled + // Mock server tests are disabled test.skip('sendFunds: only required params', async () => { const responsePromise = client.sandbox.sendFunds({ currencyCode: 'USD', @@ -24,7 +24,7 @@ describe('resource sandbox', () => { expect(dataAndResponse.response).toBe(rawResponse); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('sendFunds: required and optional params', async () => { const response = await client.sandbox.sendFunds({ currencyCode: 'USD', @@ -32,16 +32,4 @@ describe('resource sandbox', () => { currencyAmount: 1000, }); }); - - // Prism tests are disabled - test.skip('sendTestWebhook', async () => { - const responsePromise = client.sandbox.sendTestWebhook(); - const rawResponse = await responsePromise.asResponse(); - expect(rawResponse).toBeInstanceOf(Response); - const response = await responsePromise; - expect(response).not.toBeInstanceOf(Response); - const dataAndResponse = await responsePromise.withResponse(); - expect(dataAndResponse.data).toBe(response); - expect(dataAndResponse.response).toBe(rawResponse); - }); }); diff --git a/tests/api-resources/sandbox/uma.test.ts b/tests/api-resources/sandbox/uma.test.ts index 808890d..4beed23 100644 --- a/tests/api-resources/sandbox/uma.test.ts +++ b/tests/api-resources/sandbox/uma.test.ts @@ -9,7 +9,7 @@ const client = new LightsparkGrid({ }); describe('resource uma', () => { - // Prism tests are disabled + // Mock server tests are disabled test.skip('receivePayment: only required params', async () => { const responsePromise = client.sandbox.uma.receivePayment({ receivingCurrencyAmount: 1000, @@ -25,7 +25,7 @@ describe('resource uma', () => { expect(dataAndResponse.response).toBe(rawResponse); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('receivePayment: required and optional params', async () => { const response = await client.sandbox.uma.receivePayment({ receivingCurrencyAmount: 1000, diff --git a/tests/api-resources/tokens.test.ts b/tests/api-resources/tokens.test.ts index 2f13e10..1e020d1 100644 --- a/tests/api-resources/tokens.test.ts +++ b/tests/api-resources/tokens.test.ts @@ -9,7 +9,7 @@ const client = new LightsparkGrid({ }); describe('resource tokens', () => { - // Prism tests are disabled + // Mock server tests are disabled test.skip('create: only required params', async () => { const responsePromise = client.tokens.create({ name: 'Sandbox read-only', permissions: ['VIEW'] }); const rawResponse = await responsePromise.asResponse(); @@ -21,12 +21,12 @@ describe('resource tokens', () => { expect(dataAndResponse.response).toBe(rawResponse); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('create: required and optional params', async () => { const response = await client.tokens.create({ name: 'Sandbox read-only', permissions: ['VIEW'] }); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('retrieve', async () => { const responsePromise = client.tokens.retrieve('tokenId'); const rawResponse = await responsePromise.asResponse(); @@ -38,7 +38,7 @@ describe('resource tokens', () => { expect(dataAndResponse.response).toBe(rawResponse); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('list', async () => { const responsePromise = client.tokens.list(); const rawResponse = await responsePromise.asResponse(); @@ -50,7 +50,7 @@ describe('resource tokens', () => { expect(dataAndResponse.response).toBe(rawResponse); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('list: request options and params are passed correctly', async () => { // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error await expect( @@ -69,7 +69,7 @@ describe('resource tokens', () => { ).rejects.toThrow(LightsparkGrid.NotFoundError); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('delete', async () => { const responsePromise = client.tokens.delete('tokenId'); const rawResponse = await responsePromise.asResponse(); diff --git a/tests/api-resources/transactions.test.ts b/tests/api-resources/transactions.test.ts index 10ac8f0..32c21a2 100644 --- a/tests/api-resources/transactions.test.ts +++ b/tests/api-resources/transactions.test.ts @@ -9,7 +9,7 @@ const client = new LightsparkGrid({ }); describe('resource transactions', () => { - // Prism tests are disabled + // Mock server tests are disabled test.skip('retrieve', async () => { const responsePromise = client.transactions.retrieve('transactionId'); const rawResponse = await responsePromise.asResponse(); @@ -21,7 +21,7 @@ describe('resource transactions', () => { expect(dataAndResponse.response).toBe(rawResponse); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('list', async () => { const responsePromise = client.transactions.list(); const rawResponse = await responsePromise.asResponse(); @@ -33,7 +33,7 @@ describe('resource transactions', () => { expect(dataAndResponse.response).toBe(rawResponse); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('list: request options and params are passed correctly', async () => { // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error await expect( @@ -57,7 +57,7 @@ describe('resource transactions', () => { ).rejects.toThrow(LightsparkGrid.NotFoundError); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('approve', async () => { const responsePromise = client.transactions.approve('transactionId'); const rawResponse = await responsePromise.asResponse(); @@ -69,7 +69,7 @@ describe('resource transactions', () => { expect(dataAndResponse.response).toBe(rawResponse); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('approve: request options and params are passed correctly', async () => { // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error await expect( @@ -81,7 +81,7 @@ describe('resource transactions', () => { ).rejects.toThrow(LightsparkGrid.NotFoundError); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('reject', async () => { const responsePromise = client.transactions.reject('transactionId'); const rawResponse = await responsePromise.asResponse(); @@ -93,7 +93,7 @@ describe('resource transactions', () => { expect(dataAndResponse.response).toBe(rawResponse); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('reject: request options and params are passed correctly', async () => { // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error await expect( diff --git a/tests/api-resources/transfer-in.test.ts b/tests/api-resources/transfer-in.test.ts index a0a3055..a810080 100644 --- a/tests/api-resources/transfer-in.test.ts +++ b/tests/api-resources/transfer-in.test.ts @@ -9,7 +9,7 @@ const client = new LightsparkGrid({ }); describe('resource transferIn', () => { - // Prism tests are disabled + // Mock server tests are disabled test.skip('create: only required params', async () => { const responsePromise = client.transferIn.create({ destination: { accountId: 'InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123' }, @@ -24,7 +24,7 @@ describe('resource transferIn', () => { expect(dataAndResponse.response).toBe(rawResponse); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('create: required and optional params', async () => { const response = await client.transferIn.create({ destination: { accountId: 'InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123' }, diff --git a/tests/api-resources/transfer-out.test.ts b/tests/api-resources/transfer-out.test.ts index 22f1d40..d1c9d84 100644 --- a/tests/api-resources/transfer-out.test.ts +++ b/tests/api-resources/transfer-out.test.ts @@ -9,7 +9,7 @@ const client = new LightsparkGrid({ }); describe('resource transferOut', () => { - // Prism tests are disabled + // Mock server tests are disabled test.skip('create: only required params', async () => { const responsePromise = client.transferOut.create({ destination: { accountId: 'ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965' }, @@ -24,7 +24,7 @@ describe('resource transferOut', () => { expect(dataAndResponse.response).toBe(rawResponse); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('create: required and optional params', async () => { const response = await client.transferOut.create({ destination: { accountId: 'ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965' }, diff --git a/tests/api-resources/uma-providers.test.ts b/tests/api-resources/uma-providers.test.ts index 0b3596f..37de7fd 100644 --- a/tests/api-resources/uma-providers.test.ts +++ b/tests/api-resources/uma-providers.test.ts @@ -9,7 +9,7 @@ const client = new LightsparkGrid({ }); describe('resource umaProviders', () => { - // Prism tests are disabled + // Mock server tests are disabled test.skip('list', async () => { const responsePromise = client.umaProviders.list(); const rawResponse = await responsePromise.asResponse(); @@ -21,7 +21,7 @@ describe('resource umaProviders', () => { expect(dataAndResponse.response).toBe(rawResponse); }); - // Prism tests are disabled + // Mock server tests are disabled test.skip('list: request options and params are passed correctly', async () => { // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error await expect( diff --git a/tests/api-resources/webhooks.test.ts b/tests/api-resources/webhooks.test.ts new file mode 100644 index 0000000..6ca7e7a --- /dev/null +++ b/tests/api-resources/webhooks.test.ts @@ -0,0 +1,23 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import LightsparkGrid from '@lightsparkdev/grid'; + +const client = new LightsparkGrid({ + username: 'My Username', + password: 'My Password', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource webhooks', () => { + // Mock server tests are disabled + test.skip('sendTest', async () => { + const responsePromise = client.webhooks.sendTest(); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); +}); diff --git a/tsconfig.json b/tsconfig.json index f4ead78..baf70b8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -30,7 +30,7 @@ "noUncheckedIndexedAccess": true, "noImplicitOverride": true, "noPropertyAccessFromIndexSignature": true, - "isolatedModules": false, + "isolatedModules": true, "skipLibCheck": true }