@carrierllc/sdk 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -2
- package/dist/adapters.d.ts +22 -0
- package/dist/adapters.js +83 -0
- package/dist/catalog.d.ts +44 -0
- package/dist/catalog.js +45 -0
- package/dist/cli.d.ts +30 -0
- package/dist/cli.js +70 -0
- package/dist/client.d.ts +8 -2
- package/dist/client.js +35 -12
- package/dist/data/cli.json +2856 -0
- package/dist/data/operations.json +2534 -0
- package/dist/idempotency.d.ts +2 -0
- package/dist/idempotency.js +7 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +5 -0
- package/dist/mcp.d.ts +41 -0
- package/dist/mcp.js +173 -0
- package/dist/operation-ids.d.ts +5 -0
- package/dist/operation-ids.js +1 -0
- package/dist/operations.d.ts +22 -0
- package/dist/operations.js +177 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.js +3 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @carrierllc/sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
TypeScript client for the Carrier API, the hosted MCP server, and the `carrier` CLI. `intents.create` issues one eSIM. `call` uses an OpenAPI operation id. `Mcp` speaks Streamable HTTP. `Cli` runs the binary with an argument list. `restTools`, `mcpTools`, and `cliTools` return plain tool objects. Pass `inputSchema` through the Vercel AI SDK `jsonSchema()` helper. This package does not depend on that SDK.
|
|
4
4
|
|
|
5
5
|
```ts
|
|
6
6
|
import { Carrier } from "@carrierllc/sdk"
|
|
@@ -15,4 +15,24 @@ const intent = await carrier.intents.create(
|
|
|
15
15
|
|
|
16
16
|
`intents.create` posts to `/v1/connectivity/intents`. The same idempotency key and body return the original intent. `products.create` posts to `/v1/connectivity/products`. The price on a product is catalogue data. Neither call debits a wallet.
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
```ts
|
|
19
|
+
import { Cli, Mcp, restTools } from "@carrierllc/sdk"
|
|
20
|
+
|
|
21
|
+
const accounts = await carrier.call("list_reseller_accounts")
|
|
22
|
+
const issued = await carrier.call("create_connectivity_intent", {
|
|
23
|
+
body: { plan: "test_global_1gb" },
|
|
24
|
+
idempotencyKey: "order_123",
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
const session = new Mcp(process.env.ESIMVAULT_API_TOKEN ?? "")
|
|
28
|
+
const tools = await session.listTools()
|
|
29
|
+
await session.callTool("list_subscribers", { limit: 5 })
|
|
30
|
+
|
|
31
|
+
const cli = new Cli()
|
|
32
|
+
await cli.run(["subscribers", "list", "--limit", "5"])
|
|
33
|
+
const agentTools = restTools(carrier)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`new Mcp(token, { auth: "bearer" })` sends an OAuth access token or a Clerk organization API key. The default sends `?TOKEN=` to `https://mcp.carrier.llc/mcp`. `Cli` never invokes a shell. Catalog entries marked `internal` do not accept a customer API key.
|
|
37
|
+
|
|
38
|
+
`npm install @carrierllc/sdk@0.2.1` installs this surface. `0.1.0` only creates connectivity intents and products. The published tarball is the compiled `dist` output, including the generated operation catalog.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency-free tool definitions for the Vercel AI SDK and other agent runtimes.
|
|
3
|
+
*
|
|
4
|
+
* Each tool has `description`, `inputSchema`, and `execute`. Pass `inputSchema`
|
|
5
|
+
* through the AI SDK `jsonSchema()` helper. This module does not import that SDK.
|
|
6
|
+
*/
|
|
7
|
+
import { Carrier } from "./client.js";
|
|
8
|
+
import { Cli } from "./cli.js";
|
|
9
|
+
import { Mcp } from "./mcp.js";
|
|
10
|
+
export interface CarrierTool {
|
|
11
|
+
description: string;
|
|
12
|
+
inputSchema: Record<string, unknown>;
|
|
13
|
+
/** Same object as inputSchema, for AI SDK v4 `parameters`. */
|
|
14
|
+
parameters: Record<string, unknown>;
|
|
15
|
+
execute: (input: Record<string, unknown>) => Promise<unknown>;
|
|
16
|
+
}
|
|
17
|
+
/** One tool per OpenAPI operation. `execute` calls `carrier.call`. */
|
|
18
|
+
export declare function restTools(carrier: Carrier): Record<string, CarrierTool>;
|
|
19
|
+
/** One tool per MCP tool advertised by `session.listTools()`. */
|
|
20
|
+
export declare function mcpTools(session: Mcp): Promise<Record<string, CarrierTool>>;
|
|
21
|
+
/** One tool per catalogued CLI command. Extra flags go in `args`. */
|
|
22
|
+
export declare function cliTools(cli: Cli): Record<string, CarrierTool>;
|
package/dist/adapters.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency-free tool definitions for the Vercel AI SDK and other agent runtimes.
|
|
3
|
+
*
|
|
4
|
+
* Each tool has `description`, `inputSchema`, and `execute`. Pass `inputSchema`
|
|
5
|
+
* through the AI SDK `jsonSchema()` helper. This module does not import that SDK.
|
|
6
|
+
*/
|
|
7
|
+
import { cliCommands, operations } from "./catalog.js";
|
|
8
|
+
import { CarrierError } from "./error.js";
|
|
9
|
+
import { argumentsToCall, inputSchema } from "./operations.js";
|
|
10
|
+
/** One tool per OpenAPI operation. `execute` calls `carrier.call`. */
|
|
11
|
+
export function restTools(carrier) {
|
|
12
|
+
const tools = {};
|
|
13
|
+
for (const spec of operations()) {
|
|
14
|
+
const schema = inputSchema(spec);
|
|
15
|
+
tools[spec.id] = {
|
|
16
|
+
description: `${spec.summary} (${spec.method} ${spec.path}, ${spec.security})`,
|
|
17
|
+
inputSchema: schema,
|
|
18
|
+
parameters: schema,
|
|
19
|
+
execute: (input) => carrier.call(spec.id, argumentsToCall(spec, input)),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
return tools;
|
|
23
|
+
}
|
|
24
|
+
/** One tool per MCP tool advertised by `session.listTools()`. */
|
|
25
|
+
export async function mcpTools(session) {
|
|
26
|
+
const tools = {};
|
|
27
|
+
for (const advertised of await session.listTools()) {
|
|
28
|
+
if (!isRecord(advertised) || typeof advertised.name !== "string")
|
|
29
|
+
continue;
|
|
30
|
+
const name = advertised.name;
|
|
31
|
+
const schema = isRecord(advertised.inputSchema)
|
|
32
|
+
? advertised.inputSchema
|
|
33
|
+
: { type: "object", properties: {} };
|
|
34
|
+
tools[name] = {
|
|
35
|
+
description: typeof advertised.description === "string" && advertised.description !== "" ? advertised.description : name,
|
|
36
|
+
inputSchema: schema,
|
|
37
|
+
parameters: schema,
|
|
38
|
+
execute: (input) => session.callTool(name, input),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
return tools;
|
|
42
|
+
}
|
|
43
|
+
/** One tool per catalogued CLI command. Extra flags go in `args`. */
|
|
44
|
+
export function cliTools(cli) {
|
|
45
|
+
const tools = {};
|
|
46
|
+
for (const command of cliCommands()) {
|
|
47
|
+
const tokens = command.invocation.split(" ").slice(1);
|
|
48
|
+
const schema = {
|
|
49
|
+
type: "object",
|
|
50
|
+
properties: {
|
|
51
|
+
args: {
|
|
52
|
+
type: "array",
|
|
53
|
+
items: { type: "string" },
|
|
54
|
+
description: "Flags and values after the command name.",
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
additionalProperties: false,
|
|
58
|
+
};
|
|
59
|
+
tools[`cli_${tokens.join("_")}`] = {
|
|
60
|
+
description: `${command.summary} Flags: ${flagText(command)}.`,
|
|
61
|
+
inputSchema: schema,
|
|
62
|
+
parameters: schema,
|
|
63
|
+
execute: (input) => cli.run([...tokens, ...stringArgs(input.args)]),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
return tools;
|
|
67
|
+
}
|
|
68
|
+
function flagText(command) {
|
|
69
|
+
if (command.options.length === 0)
|
|
70
|
+
return "no flags";
|
|
71
|
+
return command.options.map((option) => option.flags).join(", ");
|
|
72
|
+
}
|
|
73
|
+
function stringArgs(value) {
|
|
74
|
+
if (value === undefined || value === null)
|
|
75
|
+
return [];
|
|
76
|
+
if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
|
|
77
|
+
throw new CarrierError(400, "invalid_argument", "CLI tool args must be a list of strings.");
|
|
78
|
+
}
|
|
79
|
+
return value.filter((item) => typeof item === "string");
|
|
80
|
+
}
|
|
81
|
+
function isRecord(value) {
|
|
82
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
83
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Operation and CLI catalogs generated from the public contract.
|
|
3
|
+
*/
|
|
4
|
+
import type { OperationId } from "./operation-ids.js";
|
|
5
|
+
export interface Param {
|
|
6
|
+
name: string;
|
|
7
|
+
required: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface Operation {
|
|
10
|
+
id: OperationId;
|
|
11
|
+
method: string;
|
|
12
|
+
path: string;
|
|
13
|
+
tag: string;
|
|
14
|
+
summary: string;
|
|
15
|
+
security: "bearer" | "public" | "internal" | "storefront";
|
|
16
|
+
body: boolean;
|
|
17
|
+
bodyRequired: boolean;
|
|
18
|
+
pathParams: Param[];
|
|
19
|
+
queryParams: Param[];
|
|
20
|
+
headerParams: Param[];
|
|
21
|
+
}
|
|
22
|
+
export interface CliOption {
|
|
23
|
+
flags: string;
|
|
24
|
+
description: string;
|
|
25
|
+
required: boolean;
|
|
26
|
+
}
|
|
27
|
+
export interface CliCommand {
|
|
28
|
+
invocation: string;
|
|
29
|
+
domain: string;
|
|
30
|
+
summary: string;
|
|
31
|
+
write: boolean;
|
|
32
|
+
transport: string;
|
|
33
|
+
tool: string | null;
|
|
34
|
+
restMethod: string | null;
|
|
35
|
+
restPath: string | null;
|
|
36
|
+
options: CliOption[];
|
|
37
|
+
}
|
|
38
|
+
export declare const HEADER_FIELDS: Record<string, string>;
|
|
39
|
+
export declare const operationCatalog: Operation[];
|
|
40
|
+
export declare const cliCatalog: CliCommand[];
|
|
41
|
+
export declare function operations(): Operation[];
|
|
42
|
+
export declare function cliCommands(): CliCommand[];
|
|
43
|
+
export declare function operation(operationId: string): Operation;
|
|
44
|
+
export declare function commandForArgs(args: string[]): CliCommand | undefined;
|
package/dist/catalog.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Operation and CLI catalogs generated from the public contract.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync } from "node:fs";
|
|
5
|
+
import { dirname, join } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { CarrierError } from "./error.js";
|
|
8
|
+
export const HEADER_FIELDS = {
|
|
9
|
+
"Idempotency-Key": "idempotency_key",
|
|
10
|
+
"X-Idempotency-Key": "idempotency_key",
|
|
11
|
+
"X-Confirm-Token": "confirm_token",
|
|
12
|
+
"stripe-signature": "stripe_signature",
|
|
13
|
+
};
|
|
14
|
+
const dataDir = join(dirname(fileURLToPath(import.meta.url)), "data");
|
|
15
|
+
export const operationCatalog = loadJson("operations.json");
|
|
16
|
+
export const cliCatalog = loadJson("cli.json");
|
|
17
|
+
const byId = new Map(operationCatalog.map((item) => [item.id, item]));
|
|
18
|
+
export function operations() {
|
|
19
|
+
return operationCatalog;
|
|
20
|
+
}
|
|
21
|
+
export function cliCommands() {
|
|
22
|
+
return cliCatalog;
|
|
23
|
+
}
|
|
24
|
+
export function operation(operationId) {
|
|
25
|
+
const found = byId.get(operationId);
|
|
26
|
+
if (!found) {
|
|
27
|
+
throw new CarrierError(400, "unknown_operation", `Unknown operation ${operationId}.`);
|
|
28
|
+
}
|
|
29
|
+
return found;
|
|
30
|
+
}
|
|
31
|
+
export function commandForArgs(args) {
|
|
32
|
+
let best;
|
|
33
|
+
let bestLength = -1;
|
|
34
|
+
for (const command of cliCatalog) {
|
|
35
|
+
const tokens = command.invocation.split(" ").slice(1);
|
|
36
|
+
if (tokens.length > bestLength && tokens.every((token, index) => args[index] === token)) {
|
|
37
|
+
best = command;
|
|
38
|
+
bestLength = tokens.length;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return best;
|
|
42
|
+
}
|
|
43
|
+
function loadJson(name) {
|
|
44
|
+
return JSON.parse(readFileSync(join(dataDir, name), "utf8"));
|
|
45
|
+
}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runner for the carrier CLI. Arguments are passed as a list, never through a shell.
|
|
3
|
+
*/
|
|
4
|
+
import { type CliCommand } from "./catalog.js";
|
|
5
|
+
export interface CliResult {
|
|
6
|
+
code: number;
|
|
7
|
+
stdout: string;
|
|
8
|
+
stderr: string;
|
|
9
|
+
}
|
|
10
|
+
export type CliRunner = (binary: string, args: string[]) => Promise<CliResult>;
|
|
11
|
+
export interface CliOptions {
|
|
12
|
+
/** Executable name. Defaults to `carrier`. */
|
|
13
|
+
binary?: string;
|
|
14
|
+
/** Replace process execution in tests. */
|
|
15
|
+
runner?: CliRunner;
|
|
16
|
+
/** Reject commands that are not in the catalog. Defaults to true. */
|
|
17
|
+
strict?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export declare class Cli {
|
|
20
|
+
readonly binary: string;
|
|
21
|
+
private readonly runner;
|
|
22
|
+
private readonly strict;
|
|
23
|
+
constructor(options?: CliOptions);
|
|
24
|
+
/** Every command in the published catalog. */
|
|
25
|
+
commands(): CliCommand[];
|
|
26
|
+
/** Return one catalog entry, such as `carrier subscribers list`. */
|
|
27
|
+
describe(invocation: string): CliCommand;
|
|
28
|
+
/** Run carrier with args. Strict mode rejects commands missing from the catalog. */
|
|
29
|
+
run(args: string[]): Promise<CliResult>;
|
|
30
|
+
}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runner for the carrier CLI. Arguments are passed as a list, never through a shell.
|
|
3
|
+
*/
|
|
4
|
+
import { spawn } from "node:child_process";
|
|
5
|
+
import { cliCommands, commandForArgs } from "./catalog.js";
|
|
6
|
+
import { CarrierError } from "./error.js";
|
|
7
|
+
export class Cli {
|
|
8
|
+
binary;
|
|
9
|
+
runner;
|
|
10
|
+
strict;
|
|
11
|
+
constructor(options = {}) {
|
|
12
|
+
const binary = options.binary ?? "carrier";
|
|
13
|
+
if (binary.trim() === "" || binary.includes("\u0000")) {
|
|
14
|
+
throw new CarrierError(400, "invalid_cli", "The carrier binary path is not valid.");
|
|
15
|
+
}
|
|
16
|
+
this.binary = binary;
|
|
17
|
+
this.runner = options.runner ?? subprocessRunner;
|
|
18
|
+
this.strict = options.strict ?? true;
|
|
19
|
+
}
|
|
20
|
+
/** Every command in the published catalog. */
|
|
21
|
+
commands() {
|
|
22
|
+
return cliCommands();
|
|
23
|
+
}
|
|
24
|
+
/** Return one catalog entry, such as `carrier subscribers list`. */
|
|
25
|
+
describe(invocation) {
|
|
26
|
+
const found = cliCommands().find((command) => command.invocation === invocation);
|
|
27
|
+
if (!found)
|
|
28
|
+
throw new CarrierError(400, "unknown_command", `Unknown command ${invocation}.`);
|
|
29
|
+
return found;
|
|
30
|
+
}
|
|
31
|
+
/** Run carrier with args. Strict mode rejects commands missing from the catalog. */
|
|
32
|
+
async run(args) {
|
|
33
|
+
const argv = validateArgs(args);
|
|
34
|
+
if (this.strict && commandForArgs(argv) === undefined) {
|
|
35
|
+
const joined = argv.length > 0 ? argv.join(" ") : "(empty)";
|
|
36
|
+
throw new CarrierError(400, "unknown_command", `Unknown command carrier ${joined}.`);
|
|
37
|
+
}
|
|
38
|
+
return this.runner(this.binary, argv);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function validateArgs(args) {
|
|
42
|
+
for (const arg of args) {
|
|
43
|
+
if (typeof arg !== "string" || arg.includes("\u0000")) {
|
|
44
|
+
throw new CarrierError(400, "invalid_argument", "CLI arguments must be strings without NUL bytes.");
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return [...args];
|
|
48
|
+
}
|
|
49
|
+
function subprocessRunner(binary, args) {
|
|
50
|
+
return new Promise((resolve, reject) => {
|
|
51
|
+
const child = spawn(binary, args, { shell: false });
|
|
52
|
+
let stdout = "";
|
|
53
|
+
let stderr = "";
|
|
54
|
+
child.stdout.setEncoding("utf8");
|
|
55
|
+
child.stderr.setEncoding("utf8");
|
|
56
|
+
child.stdout.on("data", (chunk) => {
|
|
57
|
+
stdout += chunk;
|
|
58
|
+
});
|
|
59
|
+
child.stderr.on("data", (chunk) => {
|
|
60
|
+
stderr += chunk;
|
|
61
|
+
});
|
|
62
|
+
child.on("error", (error) => {
|
|
63
|
+
const message = error.code === "ENOENT" ? `Could not run ${binary}.` : error.message;
|
|
64
|
+
reject(new CarrierError(0, "cli_not_found", message));
|
|
65
|
+
});
|
|
66
|
+
child.on("close", (code) => {
|
|
67
|
+
resolve({ code: code ?? 1, stdout, stderr });
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
}
|
package/dist/client.d.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Typed client for the connectivity API.
|
|
3
3
|
*
|
|
4
|
-
* `intents.create` is the call that issues one eSIM. `
|
|
5
|
-
* a plan. Neither call
|
|
4
|
+
* `intents.create` is the call that issues one eSIM. `call` reaches every
|
|
5
|
+
* public OpenAPI operation. `products.create` writes a plan. Neither call
|
|
6
|
+
* debits a wallet.
|
|
6
7
|
*/
|
|
8
|
+
import type { OperationId } from "./operation-ids.js";
|
|
9
|
+
import { type CallParams } from "./operations.js";
|
|
7
10
|
import type { ConnectivityIntent, ConnectivityPlan, IdempotentRequest, IntentCreateParams, ProductCreateParams } from "./types.js";
|
|
8
11
|
export interface CarrierOptions {
|
|
9
12
|
/** Defaults to https://api.carrier.llc. No trailing slash required. */
|
|
@@ -22,5 +25,8 @@ export declare class Carrier {
|
|
|
22
25
|
create: (body: ProductCreateParams, options: IdempotentRequest) => Promise<ConnectivityPlan>;
|
|
23
26
|
};
|
|
24
27
|
constructor(apiKey: string, options?: CarrierOptions);
|
|
28
|
+
/** Call any operation id from the public OpenAPI document. */
|
|
29
|
+
call(operationId: OperationId, params?: CallParams): Promise<unknown>;
|
|
25
30
|
private post;
|
|
31
|
+
private request;
|
|
26
32
|
}
|
package/dist/client.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Typed client for the connectivity API.
|
|
3
3
|
*
|
|
4
|
-
* `intents.create` is the call that issues one eSIM. `
|
|
5
|
-
* a plan. Neither call
|
|
4
|
+
* `intents.create` is the call that issues one eSIM. `call` reaches every
|
|
5
|
+
* public OpenAPI operation. `products.create` writes a plan. Neither call
|
|
6
|
+
* debits a wallet.
|
|
6
7
|
*/
|
|
7
8
|
import { CarrierError, errorFromBody } from "./error.js";
|
|
8
9
|
import { requireIdempotencyKey } from "./idempotency.js";
|
|
10
|
+
import { prepareCall } from "./operations.js";
|
|
11
|
+
import { USER_AGENT } from "./version.js";
|
|
9
12
|
export class Carrier {
|
|
10
13
|
apiKey;
|
|
11
14
|
baseUrl;
|
|
@@ -26,19 +29,39 @@ export class Carrier {
|
|
|
26
29
|
create: (body, request) => this.post("/v1/connectivity/products", body, request.idempotencyKey),
|
|
27
30
|
};
|
|
28
31
|
}
|
|
32
|
+
/** Call any operation id from the public OpenAPI document. */
|
|
33
|
+
async call(operationId, params = {}) {
|
|
34
|
+
const prepared = prepareCall(operationId, params);
|
|
35
|
+
return this.request(prepared.method, prepared.path, prepared);
|
|
36
|
+
}
|
|
29
37
|
async post(path, body, idempotencyKey) {
|
|
30
|
-
|
|
38
|
+
return this.request("POST", path, { body, idempotencyKey });
|
|
39
|
+
}
|
|
40
|
+
async request(method, path, options) {
|
|
41
|
+
const url = new URL(`${this.baseUrl}${path}`);
|
|
42
|
+
for (const [key, value] of Object.entries(options.query ?? {}))
|
|
43
|
+
url.searchParams.set(key, value);
|
|
44
|
+
const headers = {
|
|
45
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
46
|
+
Accept: "application/json",
|
|
47
|
+
"User-Agent": USER_AGENT,
|
|
48
|
+
};
|
|
49
|
+
for (const [name, value] of Object.entries(options.headers ?? {})) {
|
|
50
|
+
if (["authorization", "accept", "user-agent", "content-type", "host"].includes(name.toLowerCase())) {
|
|
51
|
+
throw new CarrierError(400, "invalid_header", `${name} is set by the client.`);
|
|
52
|
+
}
|
|
53
|
+
headers[name] = value;
|
|
54
|
+
}
|
|
55
|
+
let payload;
|
|
56
|
+
if (options.body !== undefined) {
|
|
57
|
+
payload = JSON.stringify(options.body);
|
|
58
|
+
headers["Content-Type"] = "application/json";
|
|
59
|
+
}
|
|
60
|
+
if (options.idempotencyKey !== undefined)
|
|
61
|
+
headers["Idempotency-Key"] = requireIdempotencyKey(options.idempotencyKey);
|
|
31
62
|
let response;
|
|
32
63
|
try {
|
|
33
|
-
response = await this.fetchImpl(
|
|
34
|
-
method: "POST",
|
|
35
|
-
headers: {
|
|
36
|
-
Authorization: `Bearer ${this.apiKey}`,
|
|
37
|
-
"Content-Type": "application/json",
|
|
38
|
-
"Idempotency-Key": key,
|
|
39
|
-
},
|
|
40
|
-
body: JSON.stringify(body),
|
|
41
|
-
});
|
|
64
|
+
response = await this.fetchImpl(url.toString(), { method, headers, body: payload });
|
|
42
65
|
}
|
|
43
66
|
catch (error) {
|
|
44
67
|
const message = error instanceof Error ? error.message : "Network request failed.";
|