@carrierllc/sdk 0.1.0
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 +18 -0
- package/dist/client.d.ts +26 -0
- package/dist/client.js +60 -0
- package/dist/error.d.ts +8 -0
- package/dist/error.js +26 -0
- package/dist/idempotency.d.ts +2 -0
- package/dist/idempotency.js +17 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/types.d.ts +54 -0
- package/dist/types.js +2 -0
- package/package.json +39 -0
package/README.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# @carrierllc/sdk
|
|
2
|
+
|
|
3
|
+
Typed client for one connectivity intent, and for the product an operator defines.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { Carrier } from "@carrierllc/sdk"
|
|
7
|
+
|
|
8
|
+
const carrier = new Carrier(process.env.CARRIER_API_KEY ?? "")
|
|
9
|
+
|
|
10
|
+
const intent = await carrier.intents.create(
|
|
11
|
+
{ plan: "test_global_1gb", metadata: { order: "123" } },
|
|
12
|
+
{ idempotencyKey: "order_123" },
|
|
13
|
+
)
|
|
14
|
+
```
|
|
15
|
+
|
|
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
|
+
|
|
18
|
+
`npm install @carrierllc/sdk` works after the connectivity release on main publishes this version. Until that release, call the HTTP intent. The published tarball is the compiled `dist` build.
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed client for the connectivity API.
|
|
3
|
+
*
|
|
4
|
+
* `intents.create` is the call that issues one eSIM. `products.create` writes
|
|
5
|
+
* a plan. Neither call debits a wallet.
|
|
6
|
+
*/
|
|
7
|
+
import type { ConnectivityIntent, ConnectivityPlan, IdempotentRequest, IntentCreateParams, ProductCreateParams } from "./types.js";
|
|
8
|
+
export interface CarrierOptions {
|
|
9
|
+
/** Defaults to https://api.carrier.llc. No trailing slash required. */
|
|
10
|
+
baseUrl?: string;
|
|
11
|
+
/** Override fetch in tests. */
|
|
12
|
+
fetch?: typeof fetch;
|
|
13
|
+
}
|
|
14
|
+
export declare class Carrier {
|
|
15
|
+
private readonly apiKey;
|
|
16
|
+
private readonly baseUrl;
|
|
17
|
+
private readonly fetchImpl;
|
|
18
|
+
readonly intents: {
|
|
19
|
+
create: (body: IntentCreateParams, options: IdempotentRequest) => Promise<ConnectivityIntent>;
|
|
20
|
+
};
|
|
21
|
+
readonly products: {
|
|
22
|
+
create: (body: ProductCreateParams, options: IdempotentRequest) => Promise<ConnectivityPlan>;
|
|
23
|
+
};
|
|
24
|
+
constructor(apiKey: string, options?: CarrierOptions);
|
|
25
|
+
private post;
|
|
26
|
+
}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed client for the connectivity API.
|
|
3
|
+
*
|
|
4
|
+
* `intents.create` is the call that issues one eSIM. `products.create` writes
|
|
5
|
+
* a plan. Neither call debits a wallet.
|
|
6
|
+
*/
|
|
7
|
+
import { CarrierError, errorFromBody } from "./error.js";
|
|
8
|
+
import { requireIdempotencyKey } from "./idempotency.js";
|
|
9
|
+
export class Carrier {
|
|
10
|
+
apiKey;
|
|
11
|
+
baseUrl;
|
|
12
|
+
fetchImpl;
|
|
13
|
+
intents;
|
|
14
|
+
products;
|
|
15
|
+
constructor(apiKey, options = {}) {
|
|
16
|
+
if (apiKey.trim() === "") {
|
|
17
|
+
throw new CarrierError(400, "invalid_api_key", "A Carrier API key is required.");
|
|
18
|
+
}
|
|
19
|
+
this.apiKey = apiKey;
|
|
20
|
+
this.baseUrl = (options.baseUrl ?? "https://api.carrier.llc").replace(/\/$/, "");
|
|
21
|
+
this.fetchImpl = options.fetch ?? fetch;
|
|
22
|
+
this.intents = {
|
|
23
|
+
create: (body, request) => this.post("/v1/connectivity/intents", body, request.idempotencyKey),
|
|
24
|
+
};
|
|
25
|
+
this.products = {
|
|
26
|
+
create: (body, request) => this.post("/v1/connectivity/products", body, request.idempotencyKey),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
async post(path, body, idempotencyKey) {
|
|
30
|
+
const key = requireIdempotencyKey(idempotencyKey);
|
|
31
|
+
let response;
|
|
32
|
+
try {
|
|
33
|
+
response = await this.fetchImpl(`${this.baseUrl}${path}`, {
|
|
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
|
+
});
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
const message = error instanceof Error ? error.message : "Network request failed.";
|
|
45
|
+
throw new CarrierError(0, "network_error", message, "api_error");
|
|
46
|
+
}
|
|
47
|
+
const parsed = await readJson(response);
|
|
48
|
+
if (!response.ok)
|
|
49
|
+
throw errorFromBody(response.status, parsed);
|
|
50
|
+
return parsed;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async function readJson(response) {
|
|
54
|
+
try {
|
|
55
|
+
return await response.json();
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
throw new CarrierError(response.status, "invalid_response", "Carrier returned a body that is not JSON.");
|
|
59
|
+
}
|
|
60
|
+
}
|
package/dist/error.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Error returned by the Carrier API, or raised before a request is sent. */
|
|
2
|
+
export declare class CarrierError extends Error {
|
|
3
|
+
readonly status: number;
|
|
4
|
+
readonly code: string;
|
|
5
|
+
readonly type: string;
|
|
6
|
+
constructor(status: number, code: string, message: string, type?: string);
|
|
7
|
+
}
|
|
8
|
+
export declare function errorFromBody(status: number, body: unknown): CarrierError;
|
package/dist/error.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** Error returned by the Carrier API, or raised before a request is sent. */
|
|
2
|
+
export class CarrierError extends Error {
|
|
3
|
+
status;
|
|
4
|
+
code;
|
|
5
|
+
type;
|
|
6
|
+
constructor(status, code, message, type = "invalid_request_error") {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = "CarrierError";
|
|
9
|
+
this.status = status;
|
|
10
|
+
this.code = code;
|
|
11
|
+
this.type = type;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export function errorFromBody(status, body) {
|
|
15
|
+
if (body && typeof body === "object" && "error" in body) {
|
|
16
|
+
const error = body.error;
|
|
17
|
+
if (error && typeof error === "object") {
|
|
18
|
+
const record = error;
|
|
19
|
+
const code = typeof record.code === "string" ? record.code : "api_error";
|
|
20
|
+
const message = typeof record.message === "string" ? record.message : "Carrier request failed.";
|
|
21
|
+
const type = typeof record.type === "string" ? record.type : "api_error";
|
|
22
|
+
return new CarrierError(status, code, message, type);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return new CarrierError(status, "api_error", "Carrier request failed.", "api_error");
|
|
26
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { CarrierError } from "./error.js";
|
|
2
|
+
/** Same limits as the API, so a bad key fails before a request is sent. */
|
|
3
|
+
export function requireIdempotencyKey(key) {
|
|
4
|
+
if (key === undefined || key.trim() === "") {
|
|
5
|
+
throw new CarrierError(400, "invalid_idempotency_key", "Idempotency-Key is required.");
|
|
6
|
+
}
|
|
7
|
+
if (key.length > 255) {
|
|
8
|
+
throw new CarrierError(400, "invalid_idempotency_key", "Idempotency-Key must be 255 characters or fewer.");
|
|
9
|
+
}
|
|
10
|
+
for (let index = 0; index < key.length; index++) {
|
|
11
|
+
const code = key.charCodeAt(index);
|
|
12
|
+
if (code <= 31 || code === 127) {
|
|
13
|
+
throw new CarrierError(400, "invalid_idempotency_key", "Idempotency-Key must not contain control characters.");
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return key;
|
|
17
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { Carrier, type CarrierOptions } from "./client.js";
|
|
2
|
+
export { CarrierError } from "./error.js";
|
|
3
|
+
export type { ConnectivityEsim, ConnectivityIntent, ConnectivityPlan, IdempotentRequest, IntentCreateParams, IntentStatus, PlanPrice, ProductCreateParams, } from "./types.js";
|
package/dist/index.js
ADDED
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/** Public objects returned by the connectivity API. */
|
|
2
|
+
export type IntentStatus = "requires_plan" | "processing" | "succeeded" | "failed";
|
|
3
|
+
export interface PlanPrice {
|
|
4
|
+
amount: number;
|
|
5
|
+
currency: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ConnectivityPlan {
|
|
8
|
+
id: string;
|
|
9
|
+
object: "connectivity.plan";
|
|
10
|
+
name: string;
|
|
11
|
+
data_mb: number;
|
|
12
|
+
validity_days: number;
|
|
13
|
+
countries: string[];
|
|
14
|
+
livemode: boolean;
|
|
15
|
+
price: PlanPrice | null;
|
|
16
|
+
}
|
|
17
|
+
export interface ConnectivityEsim {
|
|
18
|
+
iccid: string;
|
|
19
|
+
activation_code: string | null;
|
|
20
|
+
qr_code: string | null;
|
|
21
|
+
smdp_address: string | null;
|
|
22
|
+
matching_id: string | null;
|
|
23
|
+
}
|
|
24
|
+
export interface ConnectivityIntent {
|
|
25
|
+
id: string;
|
|
26
|
+
object: "connectivity.intent";
|
|
27
|
+
status: IntentStatus;
|
|
28
|
+
livemode: boolean;
|
|
29
|
+
plan: ConnectivityPlan | null;
|
|
30
|
+
esim: ConnectivityEsim | null;
|
|
31
|
+
metadata: Record<string, string>;
|
|
32
|
+
error: {
|
|
33
|
+
code: string;
|
|
34
|
+
message: string;
|
|
35
|
+
} | null;
|
|
36
|
+
created: number;
|
|
37
|
+
}
|
|
38
|
+
export interface IntentCreateParams {
|
|
39
|
+
plan?: string | null;
|
|
40
|
+
metadata?: Record<string, string>;
|
|
41
|
+
}
|
|
42
|
+
export interface ProductCreateParams {
|
|
43
|
+
name: string;
|
|
44
|
+
data_mb: number;
|
|
45
|
+
validity_days: number;
|
|
46
|
+
countries: string[];
|
|
47
|
+
price_cents: number;
|
|
48
|
+
currency: "USD" | "EUR" | "GBP" | "CHF";
|
|
49
|
+
package_template_id: number;
|
|
50
|
+
account_id?: number;
|
|
51
|
+
}
|
|
52
|
+
export interface IdempotentRequest {
|
|
53
|
+
idempotencyKey: string;
|
|
54
|
+
}
|
package/dist/types.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@carrierllc/sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Typed client for Carrier connectivity intents and products.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/node": "^26.1.2",
|
|
24
|
+
"eslint": "^10.9.1",
|
|
25
|
+
"@typescript-eslint/eslint-plugin": "^8.59.4",
|
|
26
|
+
"@typescript-eslint/parser": "^8.69.0",
|
|
27
|
+
"typescript": "^5.9.3",
|
|
28
|
+
"vitest": "^4.1.11"
|
|
29
|
+
},
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=22"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "tsc -p tsconfig.build.json",
|
|
35
|
+
"type-check": "tsc --noEmit",
|
|
36
|
+
"lint": "eslint src tests",
|
|
37
|
+
"test": "vitest run"
|
|
38
|
+
}
|
|
39
|
+
}
|