@bastamp/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/LICENSE +21 -0
- package/README.md +180 -0
- package/dist/client.d.ts +66 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +197 -0
- package/dist/client.js.map +1 -0
- package/dist/errors.d.ts +38 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +62 -0
- package/dist/errors.js.map +1 -0
- package/dist/hash.d.ts +11 -0
- package/dist/hash.d.ts.map +1 -0
- package/dist/hash.js +34 -0
- package/dist/hash.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/retry.d.ts +21 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +51 -0
- package/dist/retry.js.map +1 -0
- package/dist/types.d.ts +95 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +55 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Aletheia Tech Ltd
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# @bastamp/sdk
|
|
2
|
+
|
|
3
|
+
Official TypeScript SDK for the [BA | Stamp](https://bastamp.com) REST API. Anchor SHA-256 hashes of arbitrary files on the Polygon blockchain (and Bitcoin via OpenTimestamps) from any Node.js, Bun, Deno, or browser runtime.
|
|
4
|
+
|
|
5
|
+
The bytes of the file you're stamping never leave your machine — only the SHA-256 hash is sent.
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { BAStamp, hashFile } from "@bastamp/sdk";
|
|
9
|
+
import { readFile } from "node:fs/promises";
|
|
10
|
+
|
|
11
|
+
const client = new BAStamp({ apiKey: process.env.BASTAMP_API_KEY! });
|
|
12
|
+
|
|
13
|
+
const bytes = await readFile("contract.pdf");
|
|
14
|
+
const { stamp, creditsCharged } = await client.stamps.create({
|
|
15
|
+
contentHash: await hashFile(bytes),
|
|
16
|
+
fileName: "contract.pdf",
|
|
17
|
+
fileSize: bytes.length,
|
|
18
|
+
mimeType: "application/pdf",
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
console.log(stamp.contentHash, creditsCharged);
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Install
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install @bastamp/sdk
|
|
28
|
+
# or
|
|
29
|
+
pnpm add @bastamp/sdk
|
|
30
|
+
# or
|
|
31
|
+
yarn add @bastamp/sdk
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Requires Node 20+ (uses `fetch`, `crypto.subtle`, and `crypto.randomUUID` from the platform). Works in modern browsers and Bun/Deno without changes.
|
|
35
|
+
|
|
36
|
+
## Auth
|
|
37
|
+
|
|
38
|
+
Create an API key at [bastamp.com/account/api-keys](https://bastamp.com/account/api-keys). The plain value is shown once — store it in a secret manager (Vercel/GitHub Actions/1Password). Format: `ba_live_<32 hex chars>`.
|
|
39
|
+
|
|
40
|
+
## Usage
|
|
41
|
+
|
|
42
|
+
### Create a stamp
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
const { stamp, creditsCharged } = await client.stamps.create({
|
|
46
|
+
contentHash: "0x...",
|
|
47
|
+
fileName: "contract.pdf", // optional, metadata only
|
|
48
|
+
fileSize: 184326, // optional, bytes
|
|
49
|
+
mimeType: "application/pdf",// optional
|
|
50
|
+
jurisdiction: "IT", // optional, drives certificate's legal framing
|
|
51
|
+
locale: "it", // optional, certificate language
|
|
52
|
+
});
|
|
53
|
+
// stamp.duplicate === true if this account already owned the hash → 0 credits charged
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
The SDK auto-generates an `Idempotency-Key` per call. To override (e.g. tie idempotency to a workflow run id):
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
await client.stamps.create({ contentHash }, { idempotencyKey: `gh-run-${runId}` });
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Batch up to 100 stamps in one call
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
const { results, creditsCharged, duplicateCount } = await client.stamps.createBatch({
|
|
66
|
+
items: [
|
|
67
|
+
{ contentHash: "0x...", fileName: "a.pdf" },
|
|
68
|
+
{ contentHash: "0x...", fileName: "b.pdf" },
|
|
69
|
+
],
|
|
70
|
+
jurisdiction: "IT",
|
|
71
|
+
locale: "it",
|
|
72
|
+
});
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Look up a stamp
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
const s = await client.stamps.get("0x...");
|
|
79
|
+
// s.status: "pending" | "anchored"
|
|
80
|
+
// s.anchor: { chain, merkleRoot, txHash, blockNumber, blockTime, bitcoin } | null
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Download the certificate PDF
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
const pdf = await client.stamps.downloadCertificate("0x...", { locale: "it", jurisdiction: "IT" });
|
|
87
|
+
// pdf is a Uint8Array — write to disk:
|
|
88
|
+
await fs.writeFile("certificate.pdf", pdf);
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Throws `BAStampConflictError` (`type === "not_anchored"`) if the stamp is still pending. Anchoring lands at the next 5-minute batch tick.
|
|
92
|
+
|
|
93
|
+
### Account info
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
const account = await client.account.get();
|
|
97
|
+
console.log(account.credits);
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Hash any byte source
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
import { hashFile } from "@bastamp/sdk";
|
|
104
|
+
|
|
105
|
+
await hashFile(uint8Array); // 0x...
|
|
106
|
+
await hashFile(arrayBuffer); // 0x...
|
|
107
|
+
await hashFile(blob); // 0x... (browser File / Blob)
|
|
108
|
+
await hashFile(buffer); // 0x... (Node Buffer)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
All variants delegate to `crypto.subtle.digest("SHA-256", ...)` — the computation is local; no network call.
|
|
112
|
+
|
|
113
|
+
## Errors
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
import {
|
|
117
|
+
BAStampNoCreditsError,
|
|
118
|
+
BAStampUnauthorizedError,
|
|
119
|
+
BAStampConflictError,
|
|
120
|
+
} from "@bastamp/sdk";
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
await client.stamps.create({ contentHash });
|
|
124
|
+
} catch (err) {
|
|
125
|
+
if (err instanceof BAStampNoCreditsError) {
|
|
126
|
+
// 402 — top up at bastamp.com/#pricing
|
|
127
|
+
} else if (err instanceof BAStampUnauthorizedError) {
|
|
128
|
+
// 401 — rotate the key
|
|
129
|
+
} else if (err instanceof BAStampConflictError && err.type === "idempotency_conflict") {
|
|
130
|
+
// 409 — Idempotency-Key reused with a different payload
|
|
131
|
+
} else {
|
|
132
|
+
throw err;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Error class hierarchy:
|
|
138
|
+
|
|
139
|
+
| Status | Class | When |
|
|
140
|
+
|---|---|---|
|
|
141
|
+
| 400 | `BAStampInvalidRequestError` | Bad hash format, missing field, etc. |
|
|
142
|
+
| 401 | `BAStampUnauthorizedError` | Missing / wrong Bearer token |
|
|
143
|
+
| 402 | `BAStampNoCreditsError` | Account is out of credits |
|
|
144
|
+
| 404 | `BAStampNotFoundError` | Stamp doesn't exist |
|
|
145
|
+
| 409 | `BAStampConflictError` | Idempotency conflict, or certificate requested before anchor |
|
|
146
|
+
| 429 | `BAStampRateLimitedError` | Rate limited (SDK retries automatically; surfaced only if retries exhaust) |
|
|
147
|
+
| other | `BAStampError` | Base class — `.status`, `.type`, `.body` |
|
|
148
|
+
|
|
149
|
+
## Retries
|
|
150
|
+
|
|
151
|
+
The SDK retries on 408, 429, 502, 503, 504 and on network errors. Up to `maxRetries` attempts (default 3, configurable via `new BAStamp({ apiKey, maxRetries })`) with exponential backoff + jitter (~200 ms → 5 s capped). Honors `Retry-After` headers when present.
|
|
152
|
+
|
|
153
|
+
Non-retryable: 400, 401, 402, 404, 409. They fail immediately.
|
|
154
|
+
|
|
155
|
+
## Cancel a request
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
const ctrl = new AbortController();
|
|
159
|
+
setTimeout(() => ctrl.abort(), 5_000);
|
|
160
|
+
await client.stamps.create({ contentHash }, { signal: ctrl.signal });
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Examples
|
|
164
|
+
|
|
165
|
+
See [examples/basic.ts](./examples/basic.ts) for a runnable end-to-end script (`tsx examples/basic.ts <file>`).
|
|
166
|
+
|
|
167
|
+
## Why an official SDK
|
|
168
|
+
|
|
169
|
+
The REST API is straightforward — anyone can call it with `fetch`. The SDK exists because:
|
|
170
|
+
|
|
171
|
+
- Type-safe request bodies and responses from the [OpenAPI spec](https://bastamp.com/openapi.yaml).
|
|
172
|
+
- Sensible defaults: retries, idempotency keys, error subclasses.
|
|
173
|
+
- One-line file hashing via `hashFile` that works across Node, browser, Bun, Deno.
|
|
174
|
+
- No dependencies — fewer supply-chain things to worry about.
|
|
175
|
+
|
|
176
|
+
For verifying stamps independently (without trusting bastamp.com), see the [open-source verifier](https://github.com/stamp-verify/stamp-verify).
|
|
177
|
+
|
|
178
|
+
## License
|
|
179
|
+
|
|
180
|
+
MIT — see [LICENSE](./LICENSE).
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { Account, CertificateOptions, ClientOptions, CreateBatchResponse, CreateStampResponse, RequestOptions, Stamp, StampInput, BatchInput } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* BA | Stamp REST client.
|
|
4
|
+
*
|
|
5
|
+
* ```ts
|
|
6
|
+
* import { BAStamp, hashFile } from "@bastamp/sdk";
|
|
7
|
+
* const client = new BAStamp({ apiKey: process.env.BASTAMP_API_KEY! });
|
|
8
|
+
*
|
|
9
|
+
* const bytes = await fs.readFile("contract.pdf");
|
|
10
|
+
* const { stamp } = await client.stamps.create({
|
|
11
|
+
* contentHash: await hashFile(bytes),
|
|
12
|
+
* fileName: "contract.pdf",
|
|
13
|
+
* fileSize: bytes.length,
|
|
14
|
+
* mimeType: "application/pdf",
|
|
15
|
+
* });
|
|
16
|
+
* console.log("anchored hash:", stamp.contentHash);
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export declare class BAStamp {
|
|
20
|
+
#private;
|
|
21
|
+
readonly stamps: StampsResource;
|
|
22
|
+
readonly account: AccountResource;
|
|
23
|
+
constructor(opts: ClientOptions);
|
|
24
|
+
/** @internal */
|
|
25
|
+
_request<T>(method: "GET" | "POST", path: string, options?: {
|
|
26
|
+
body?: unknown;
|
|
27
|
+
query?: Record<string, string | undefined>;
|
|
28
|
+
idempotencyKey?: string;
|
|
29
|
+
signal?: AbortSignal;
|
|
30
|
+
raw?: boolean;
|
|
31
|
+
}): Promise<T>;
|
|
32
|
+
}
|
|
33
|
+
declare class StampsResource {
|
|
34
|
+
#private;
|
|
35
|
+
constructor(client: BAStamp);
|
|
36
|
+
/**
|
|
37
|
+
* Anchor a single SHA-256 hash. Charges 1 credit; returns
|
|
38
|
+
* `{ duplicate: true, ...}` with 0 charge if the caller already owns
|
|
39
|
+
* this exact hash.
|
|
40
|
+
*/
|
|
41
|
+
create(input: StampInput, options?: RequestOptions): Promise<CreateStampResponse>;
|
|
42
|
+
/**
|
|
43
|
+
* Anchor up to 100 hashes in one call. One credit per *new* item;
|
|
44
|
+
* duplicates aren't charged but still appear in `results`.
|
|
45
|
+
*/
|
|
46
|
+
createBatch(input: BatchInput, options?: RequestOptions): Promise<CreateBatchResponse>;
|
|
47
|
+
/**
|
|
48
|
+
* Read a stamp's current status and on-chain anchor data. Returns
|
|
49
|
+
* `status: "pending"` until the next batch tick (~5 minutes) anchors
|
|
50
|
+
* it on Polygon.
|
|
51
|
+
*/
|
|
52
|
+
get(contentHash: string, options?: RequestOptions): Promise<Stamp>;
|
|
53
|
+
/**
|
|
54
|
+
* Download the PDF certificate for a stamp as raw bytes. Throws 409
|
|
55
|
+
* `not_anchored` if the stamp hasn't anchored on Polygon yet.
|
|
56
|
+
*/
|
|
57
|
+
downloadCertificate(contentHash: string, certOptions?: CertificateOptions & RequestOptions): Promise<Uint8Array>;
|
|
58
|
+
}
|
|
59
|
+
declare class AccountResource {
|
|
60
|
+
#private;
|
|
61
|
+
constructor(client: BAStamp);
|
|
62
|
+
/** Get the authenticated account's id, email, and remaining credits. */
|
|
63
|
+
get(options?: RequestOptions): Promise<Account>;
|
|
64
|
+
}
|
|
65
|
+
export {};
|
|
66
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,OAAO,EAEP,kBAAkB,EAClB,aAAa,EACb,mBAAmB,EACnB,mBAAmB,EACnB,cAAc,EACd,KAAK,EACL,UAAU,EACV,UAAU,EACX,MAAM,YAAY,CAAC;AAMpB;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,OAAO;;IAClB,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;gBAMtB,IAAI,EAAE,aAAa;IAY/B,gBAAgB;IACV,QAAQ,CAAC,CAAC,EACd,MAAM,EAAE,KAAK,GAAG,MAAM,EACtB,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE;QACP,IAAI,CAAC,EAAE,OAAO,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;QAC3C,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,MAAM,CAAC,EAAE,WAAW,CAAC;QAErB,GAAG,CAAC,EAAE,OAAO,CAAC;KACV,GACL,OAAO,CAAC,CAAC,CAAC;CA6Dd;AAED,cAAM,cAAc;;gBAEN,MAAM,EAAE,OAAO;IAI3B;;;;OAIG;IACG,MAAM,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAQ3F;;;OAGG;IACG,WAAW,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAchG;;;;OAIG;IACG,GAAG,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,KAAK,CAAC;IAM5E;;;OAGG;IACG,mBAAmB,CACvB,WAAW,EAAE,MAAM,EACnB,WAAW,GAAE,kBAAkB,GAAG,cAAmB,GACpD,OAAO,CAAC,UAAU,CAAC;CAavB;AAED,cAAM,eAAe;;gBAEP,MAAM,EAAE,OAAO;IAI3B,wEAAwE;IAClE,GAAG,CAAC,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,OAAO,CAAC;CAG1D"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { BAStampError, BAStampInvalidRequestError, BAStampUnauthorizedError, BAStampNoCreditsError, BAStampNotFoundError, BAStampConflictError, BAStampRateLimitedError, } from "./errors.js";
|
|
2
|
+
import { backoffMs, retryAfterMs, shouldRetry, sleep } from "./retry.js";
|
|
3
|
+
const DEFAULT_BASE_URL = "https://bastamp.com";
|
|
4
|
+
const DEFAULT_MAX_RETRIES = 3;
|
|
5
|
+
const USER_AGENT = "bastamp-sdk-ts/0.1.0";
|
|
6
|
+
/**
|
|
7
|
+
* BA | Stamp REST client.
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { BAStamp, hashFile } from "@bastamp/sdk";
|
|
11
|
+
* const client = new BAStamp({ apiKey: process.env.BASTAMP_API_KEY! });
|
|
12
|
+
*
|
|
13
|
+
* const bytes = await fs.readFile("contract.pdf");
|
|
14
|
+
* const { stamp } = await client.stamps.create({
|
|
15
|
+
* contentHash: await hashFile(bytes),
|
|
16
|
+
* fileName: "contract.pdf",
|
|
17
|
+
* fileSize: bytes.length,
|
|
18
|
+
* mimeType: "application/pdf",
|
|
19
|
+
* });
|
|
20
|
+
* console.log("anchored hash:", stamp.contentHash);
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export class BAStamp {
|
|
24
|
+
stamps;
|
|
25
|
+
account;
|
|
26
|
+
#apiKey;
|
|
27
|
+
#baseUrl;
|
|
28
|
+
#maxRetries;
|
|
29
|
+
#fetch;
|
|
30
|
+
constructor(opts) {
|
|
31
|
+
if (!opts?.apiKey || typeof opts.apiKey !== "string") {
|
|
32
|
+
throw new TypeError("BAStamp: `apiKey` is required");
|
|
33
|
+
}
|
|
34
|
+
this.#apiKey = opts.apiKey;
|
|
35
|
+
this.#baseUrl = (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
36
|
+
this.#maxRetries = Math.max(0, opts.maxRetries ?? DEFAULT_MAX_RETRIES);
|
|
37
|
+
this.#fetch = opts.fetch ?? globalThis.fetch.bind(globalThis);
|
|
38
|
+
this.stamps = new StampsResource(this);
|
|
39
|
+
this.account = new AccountResource(this);
|
|
40
|
+
}
|
|
41
|
+
/** @internal */
|
|
42
|
+
async _request(method, path, options = {}) {
|
|
43
|
+
const url = new URL(this.#baseUrl + path);
|
|
44
|
+
if (options.query) {
|
|
45
|
+
for (const [k, v] of Object.entries(options.query)) {
|
|
46
|
+
if (v != null)
|
|
47
|
+
url.searchParams.set(k, v);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const headers = {
|
|
51
|
+
"Authorization": `Bearer ${this.#apiKey}`,
|
|
52
|
+
"User-Agent": USER_AGENT,
|
|
53
|
+
"Accept": options.raw ? "application/pdf, application/json" : "application/json",
|
|
54
|
+
};
|
|
55
|
+
let bodyString;
|
|
56
|
+
if (options.body !== undefined) {
|
|
57
|
+
headers["Content-Type"] = "application/json";
|
|
58
|
+
bodyString = JSON.stringify(options.body);
|
|
59
|
+
}
|
|
60
|
+
if (options.idempotencyKey) {
|
|
61
|
+
headers["Idempotency-Key"] = options.idempotencyKey;
|
|
62
|
+
}
|
|
63
|
+
let attempt = 0;
|
|
64
|
+
// The +1 reads naturally: maxRetries=3 means up to 4 attempts total.
|
|
65
|
+
while (true) {
|
|
66
|
+
let response;
|
|
67
|
+
try {
|
|
68
|
+
response = await this.#fetch(url.toString(), {
|
|
69
|
+
method,
|
|
70
|
+
headers,
|
|
71
|
+
body: bodyString,
|
|
72
|
+
signal: options.signal,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
// Network-level error (DNS, TCP, abort). Retry transient kinds.
|
|
77
|
+
if (attempt < this.#maxRetries && !(err instanceof DOMException && err.name === "AbortError")) {
|
|
78
|
+
await sleep(backoffMs(attempt), options.signal);
|
|
79
|
+
attempt++;
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
throw err;
|
|
83
|
+
}
|
|
84
|
+
if (response.ok) {
|
|
85
|
+
if (options.raw)
|
|
86
|
+
return response;
|
|
87
|
+
// 204 has no body
|
|
88
|
+
if (response.status === 204)
|
|
89
|
+
return undefined;
|
|
90
|
+
return (await response.json());
|
|
91
|
+
}
|
|
92
|
+
const shouldRetryThis = shouldRetry(response.status) && attempt < this.#maxRetries;
|
|
93
|
+
if (shouldRetryThis) {
|
|
94
|
+
const waitMs = retryAfterMs(response.headers.get("retry-after")) ?? backoffMs(attempt);
|
|
95
|
+
await sleep(waitMs, options.signal);
|
|
96
|
+
attempt++;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
throw await buildError(response);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
class StampsResource {
|
|
104
|
+
#client;
|
|
105
|
+
constructor(client) {
|
|
106
|
+
this.#client = client;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Anchor a single SHA-256 hash. Charges 1 credit; returns
|
|
110
|
+
* `{ duplicate: true, ...}` with 0 charge if the caller already owns
|
|
111
|
+
* this exact hash.
|
|
112
|
+
*/
|
|
113
|
+
async create(input, options = {}) {
|
|
114
|
+
return this.#client._request("POST", "/api/v1/stamps", {
|
|
115
|
+
body: input,
|
|
116
|
+
idempotencyKey: options.idempotencyKey ?? randomIdempotencyKey(),
|
|
117
|
+
signal: options.signal,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Anchor up to 100 hashes in one call. One credit per *new* item;
|
|
122
|
+
* duplicates aren't charged but still appear in `results`.
|
|
123
|
+
*/
|
|
124
|
+
async createBatch(input, options = {}) {
|
|
125
|
+
if (!Array.isArray(input.items) || input.items.length === 0) {
|
|
126
|
+
throw new TypeError("createBatch: `items` must be a non-empty array");
|
|
127
|
+
}
|
|
128
|
+
if (input.items.length > 100) {
|
|
129
|
+
throw new TypeError(`createBatch: max 100 items per call (got ${input.items.length})`);
|
|
130
|
+
}
|
|
131
|
+
return this.#client._request("POST", "/api/v1/stamps/batch", {
|
|
132
|
+
body: input,
|
|
133
|
+
idempotencyKey: options.idempotencyKey ?? randomIdempotencyKey(),
|
|
134
|
+
signal: options.signal,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Read a stamp's current status and on-chain anchor data. Returns
|
|
139
|
+
* `status: "pending"` until the next batch tick (~5 minutes) anchors
|
|
140
|
+
* it on Polygon.
|
|
141
|
+
*/
|
|
142
|
+
async get(contentHash, options = {}) {
|
|
143
|
+
return this.#client._request("GET", `/api/v1/stamps/${encodeURIComponent(contentHash)}`, {
|
|
144
|
+
signal: options.signal,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Download the PDF certificate for a stamp as raw bytes. Throws 409
|
|
149
|
+
* `not_anchored` if the stamp hasn't anchored on Polygon yet.
|
|
150
|
+
*/
|
|
151
|
+
async downloadCertificate(contentHash, certOptions = {}) {
|
|
152
|
+
const response = await this.#client._request("GET", `/api/v1/stamps/${encodeURIComponent(contentHash)}/certificate`, {
|
|
153
|
+
query: { locale: certOptions.locale, jurisdiction: certOptions.jurisdiction },
|
|
154
|
+
signal: certOptions.signal,
|
|
155
|
+
raw: true,
|
|
156
|
+
});
|
|
157
|
+
const buf = await response.arrayBuffer();
|
|
158
|
+
return new Uint8Array(buf);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
class AccountResource {
|
|
162
|
+
#client;
|
|
163
|
+
constructor(client) {
|
|
164
|
+
this.#client = client;
|
|
165
|
+
}
|
|
166
|
+
/** Get the authenticated account's id, email, and remaining credits. */
|
|
167
|
+
async get(options = {}) {
|
|
168
|
+
return this.#client._request("GET", "/api/v1/account", { signal: options.signal });
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// ── helpers ──
|
|
172
|
+
async function buildError(response) {
|
|
173
|
+
const text = await response.text();
|
|
174
|
+
let parsed;
|
|
175
|
+
try {
|
|
176
|
+
parsed = JSON.parse(text);
|
|
177
|
+
}
|
|
178
|
+
catch { /* not JSON */ }
|
|
179
|
+
const type = parsed?.error?.type ?? "unknown";
|
|
180
|
+
const message = parsed?.error?.message ?? text ?? `HTTP ${response.status}`;
|
|
181
|
+
const body = parsed ?? text;
|
|
182
|
+
switch (response.status) {
|
|
183
|
+
case 400: return new BAStampInvalidRequestError(message, body);
|
|
184
|
+
case 401: return new BAStampUnauthorizedError(message, body);
|
|
185
|
+
case 402: return new BAStampNoCreditsError(message, body);
|
|
186
|
+
case 404: return new BAStampNotFoundError(message, body);
|
|
187
|
+
case 409: return new BAStampConflictError(message, type, body);
|
|
188
|
+
case 429: return new BAStampRateLimitedError(message, body);
|
|
189
|
+
default:
|
|
190
|
+
return new BAStampError(message, response.status, type, body);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
function randomIdempotencyKey() {
|
|
194
|
+
// crypto.randomUUID is available on Node 20+, all modern browsers, Bun, Deno.
|
|
195
|
+
return globalThis.crypto.randomUUID();
|
|
196
|
+
}
|
|
197
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base error for any non-2xx response from the BA Stamp API. Subclasses
|
|
3
|
+
* narrow on the response code (401 → Unauthorized, 402 → NoCredits, etc.).
|
|
4
|
+
*/
|
|
5
|
+
export declare class BAStampError extends Error {
|
|
6
|
+
/** HTTP status code. */
|
|
7
|
+
readonly status: number;
|
|
8
|
+
/** Machine-readable code from the API's error envelope (snake_case). */
|
|
9
|
+
readonly type: string;
|
|
10
|
+
/** Raw response body (parsed when JSON, otherwise the string). */
|
|
11
|
+
readonly body: unknown;
|
|
12
|
+
constructor(message: string, status: number, type: string, body: unknown);
|
|
13
|
+
}
|
|
14
|
+
/** 400 — payload, hash, or parameter failed validation. */
|
|
15
|
+
export declare class BAStampInvalidRequestError extends BAStampError {
|
|
16
|
+
constructor(message: string, body: unknown);
|
|
17
|
+
}
|
|
18
|
+
/** 401 — missing or wrong Authorization header. */
|
|
19
|
+
export declare class BAStampUnauthorizedError extends BAStampError {
|
|
20
|
+
constructor(message: string, body: unknown);
|
|
21
|
+
}
|
|
22
|
+
/** 402 — account credits don't cover the request. Top up at bastamp.com/#pricing. */
|
|
23
|
+
export declare class BAStampNoCreditsError extends BAStampError {
|
|
24
|
+
constructor(message: string, body: unknown);
|
|
25
|
+
}
|
|
26
|
+
/** 404 — no stamp exists for the requested hash. */
|
|
27
|
+
export declare class BAStampNotFoundError extends BAStampError {
|
|
28
|
+
constructor(message: string, body: unknown);
|
|
29
|
+
}
|
|
30
|
+
/** 409 — Idempotency-Key reused with a different payload, or stamp not anchored yet for certificate. */
|
|
31
|
+
export declare class BAStampConflictError extends BAStampError {
|
|
32
|
+
constructor(message: string, type: string, body: unknown);
|
|
33
|
+
}
|
|
34
|
+
/** 429 — rate-limited. SDK retries automatically; surfaced only if retries exhaust. */
|
|
35
|
+
export declare class BAStampRateLimitedError extends BAStampError {
|
|
36
|
+
constructor(message: string, body: unknown);
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qBAAa,YAAa,SAAQ,KAAK;IACrC,wBAAwB;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,wEAAwE;IACxE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,kEAAkE;IAClE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;gBAEX,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO;CAOzE;AAED,2DAA2D;AAC3D,qBAAa,0BAA2B,SAAQ,YAAY;gBAC9C,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO;CAI3C;AAED,mDAAmD;AACnD,qBAAa,wBAAyB,SAAQ,YAAY;gBAC5C,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO;CAI3C;AAED,qFAAqF;AACrF,qBAAa,qBAAsB,SAAQ,YAAY;gBACzC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO;CAI3C;AAED,oDAAoD;AACpD,qBAAa,oBAAqB,SAAQ,YAAY;gBACxC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO;CAI3C;AAED,wGAAwG;AACxG,qBAAa,oBAAqB,SAAQ,YAAY;gBACxC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO;CAIzD;AAED,uFAAuF;AACvF,qBAAa,uBAAwB,SAAQ,YAAY;gBAC3C,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO;CAI3C"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base error for any non-2xx response from the BA Stamp API. Subclasses
|
|
3
|
+
* narrow on the response code (401 → Unauthorized, 402 → NoCredits, etc.).
|
|
4
|
+
*/
|
|
5
|
+
export class BAStampError extends Error {
|
|
6
|
+
/** HTTP status code. */
|
|
7
|
+
status;
|
|
8
|
+
/** Machine-readable code from the API's error envelope (snake_case). */
|
|
9
|
+
type;
|
|
10
|
+
/** Raw response body (parsed when JSON, otherwise the string). */
|
|
11
|
+
body;
|
|
12
|
+
constructor(message, status, type, body) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.name = "BAStampError";
|
|
15
|
+
this.status = status;
|
|
16
|
+
this.type = type;
|
|
17
|
+
this.body = body;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/** 400 — payload, hash, or parameter failed validation. */
|
|
21
|
+
export class BAStampInvalidRequestError extends BAStampError {
|
|
22
|
+
constructor(message, body) {
|
|
23
|
+
super(message, 400, "invalid_request", body);
|
|
24
|
+
this.name = "BAStampInvalidRequestError";
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/** 401 — missing or wrong Authorization header. */
|
|
28
|
+
export class BAStampUnauthorizedError extends BAStampError {
|
|
29
|
+
constructor(message, body) {
|
|
30
|
+
super(message, 401, "unauthorized", body);
|
|
31
|
+
this.name = "BAStampUnauthorizedError";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/** 402 — account credits don't cover the request. Top up at bastamp.com/#pricing. */
|
|
35
|
+
export class BAStampNoCreditsError extends BAStampError {
|
|
36
|
+
constructor(message, body) {
|
|
37
|
+
super(message, 402, "no_credits", body);
|
|
38
|
+
this.name = "BAStampNoCreditsError";
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/** 404 — no stamp exists for the requested hash. */
|
|
42
|
+
export class BAStampNotFoundError extends BAStampError {
|
|
43
|
+
constructor(message, body) {
|
|
44
|
+
super(message, 404, "not_found", body);
|
|
45
|
+
this.name = "BAStampNotFoundError";
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/** 409 — Idempotency-Key reused with a different payload, or stamp not anchored yet for certificate. */
|
|
49
|
+
export class BAStampConflictError extends BAStampError {
|
|
50
|
+
constructor(message, type, body) {
|
|
51
|
+
super(message, 409, type, body);
|
|
52
|
+
this.name = "BAStampConflictError";
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/** 429 — rate-limited. SDK retries automatically; surfaced only if retries exhaust. */
|
|
56
|
+
export class BAStampRateLimitedError extends BAStampError {
|
|
57
|
+
constructor(message, body) {
|
|
58
|
+
super(message, 429, "rate_limited", body);
|
|
59
|
+
this.name = "BAStampRateLimitedError";
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,OAAO,YAAa,SAAQ,KAAK;IACrC,wBAAwB;IACf,MAAM,CAAS;IACxB,wEAAwE;IAC/D,IAAI,CAAS;IACtB,kEAAkE;IACzD,IAAI,CAAU;IAEvB,YAAY,OAAe,EAAE,MAAc,EAAE,IAAY,EAAE,IAAa;QACtE,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;QAC3B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAED,2DAA2D;AAC3D,MAAM,OAAO,0BAA2B,SAAQ,YAAY;IAC1D,YAAY,OAAe,EAAE,IAAa;QACxC,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,iBAAiB,EAAE,IAAI,CAAC,CAAC;QAC7C,IAAI,CAAC,IAAI,GAAG,4BAA4B,CAAC;IAC3C,CAAC;CACF;AAED,mDAAmD;AACnD,MAAM,OAAO,wBAAyB,SAAQ,YAAY;IACxD,YAAY,OAAe,EAAE,IAAa;QACxC,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,cAAc,EAAE,IAAI,CAAC,CAAC;QAC1C,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;IACzC,CAAC;CACF;AAED,qFAAqF;AACrF,MAAM,OAAO,qBAAsB,SAAQ,YAAY;IACrD,YAAY,OAAe,EAAE,IAAa;QACxC,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,YAAY,EAAE,IAAI,CAAC,CAAC;QACxC,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;IACtC,CAAC;CACF;AAED,oDAAoD;AACpD,MAAM,OAAO,oBAAqB,SAAQ,YAAY;IACpD,YAAY,OAAe,EAAE,IAAa;QACxC,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC;QACvC,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;IACrC,CAAC;CACF;AAED,wGAAwG;AACxG,MAAM,OAAO,oBAAqB,SAAQ,YAAY;IACpD,YAAY,OAAe,EAAE,IAAY,EAAE,IAAa;QACtD,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QAChC,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;IACrC,CAAC;CACF;AAED,uFAAuF;AACvF,MAAM,OAAO,uBAAwB,SAAQ,YAAY;IACvD,YAAY,OAAe,EAAE,IAAa;QACxC,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,cAAc,EAAE,IAAI,CAAC,CAAC;QAC1C,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;IACxC,CAAC;CACF"}
|
package/dist/hash.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compute the SHA-256 of any byte source and return it as a 0x-prefixed
|
|
3
|
+
* lowercase hex string — the exact format the BA Stamp API expects.
|
|
4
|
+
*
|
|
5
|
+
* Accepts `Uint8Array`, `ArrayBuffer`, `Blob` (browser/Bun), `Buffer`
|
|
6
|
+
* (Node), or anything that can be passed to `new Response()`. The
|
|
7
|
+
* computation is done locally via `crypto.subtle.digest`; the bytes
|
|
8
|
+
* never leave the caller's machine.
|
|
9
|
+
*/
|
|
10
|
+
export declare function hashFile(input: Uint8Array | ArrayBuffer | Blob | Buffer | ReadableStream<Uint8Array>): Promise<string>;
|
|
11
|
+
//# sourceMappingURL=hash.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hash.d.ts","sourceRoot":"","sources":["../src/hash.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,wBAAsB,QAAQ,CAC5B,KAAK,EAAE,UAAU,GAAG,WAAW,GAAG,IAAI,GAAG,MAAM,GAAG,cAAc,CAAC,UAAU,CAAC,GAC3E,OAAO,CAAC,MAAM,CAAC,CAsBjB"}
|
package/dist/hash.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compute the SHA-256 of any byte source and return it as a 0x-prefixed
|
|
3
|
+
* lowercase hex string — the exact format the BA Stamp API expects.
|
|
4
|
+
*
|
|
5
|
+
* Accepts `Uint8Array`, `ArrayBuffer`, `Blob` (browser/Bun), `Buffer`
|
|
6
|
+
* (Node), or anything that can be passed to `new Response()`. The
|
|
7
|
+
* computation is done locally via `crypto.subtle.digest`; the bytes
|
|
8
|
+
* never leave the caller's machine.
|
|
9
|
+
*/
|
|
10
|
+
export async function hashFile(input) {
|
|
11
|
+
let bytes;
|
|
12
|
+
if (input instanceof ArrayBuffer) {
|
|
13
|
+
bytes = input;
|
|
14
|
+
}
|
|
15
|
+
else if (typeof Blob !== "undefined" && input instanceof Blob) {
|
|
16
|
+
bytes = await input.arrayBuffer();
|
|
17
|
+
}
|
|
18
|
+
else if (input instanceof ReadableStream) {
|
|
19
|
+
bytes = await new Response(input).arrayBuffer();
|
|
20
|
+
}
|
|
21
|
+
else if (ArrayBuffer.isView(input)) {
|
|
22
|
+
const view = input;
|
|
23
|
+
bytes = view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength);
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
throw new TypeError("hashFile: expected Uint8Array, ArrayBuffer, Blob, Buffer, or ReadableStream");
|
|
27
|
+
}
|
|
28
|
+
const digest = await crypto.subtle.digest("SHA-256", bytes);
|
|
29
|
+
const hex = Array.from(new Uint8Array(digest))
|
|
30
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
31
|
+
.join("");
|
|
32
|
+
return "0x" + hex;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=hash.js.map
|
package/dist/hash.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hash.js","sourceRoot":"","sources":["../src/hash.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,KAA4E;IAE5E,IAAI,KAAkB,CAAC;IACvB,IAAI,KAAK,YAAY,WAAW,EAAE,CAAC;QACjC,KAAK,GAAG,KAAK,CAAC;IAChB,CAAC;SAAM,IAAI,OAAO,IAAI,KAAK,WAAW,IAAI,KAAK,YAAY,IAAI,EAAE,CAAC;QAChE,KAAK,GAAG,MAAM,KAAK,CAAC,WAAW,EAAE,CAAC;IACpC,CAAC;SAAM,IAAI,KAAK,YAAY,cAAc,EAAE,CAAC;QAC3C,KAAK,GAAG,MAAM,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;IAClD,CAAC;SAAM,IAAI,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QACrC,MAAM,IAAI,GAAG,KAAmB,CAAC;QACjC,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAgB,CAAC;IAC/F,CAAC;SAAM,CAAC;QACN,MAAM,IAAI,SAAS,CACjB,6EAA6E,CAC9E,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IAC5D,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;SAC3C,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;SAC3C,IAAI,CAAC,EAAE,CAAC,CAAC;IACZ,OAAO,IAAI,GAAG,GAAG,CAAC;AACpB,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { BAStamp } from "./client.js";
|
|
2
|
+
export { hashFile } from "./hash.js";
|
|
3
|
+
export { BAStampError, BAStampInvalidRequestError, BAStampUnauthorizedError, BAStampNoCreditsError, BAStampNotFoundError, BAStampConflictError, BAStampRateLimitedError, } from "./errors.js";
|
|
4
|
+
export type { Account, BatchInput, BitcoinAnchor, CertificateOptions, ClientOptions, ContentHash, CreateBatchResponse, CreateStampResponse, PolygonAnchor, RequestOptions, Stamp, StampInput, StampResult, StampStatus, } from "./types.js";
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EACL,YAAY,EACZ,0BAA0B,EAC1B,wBAAwB,EACxB,qBAAqB,EACrB,oBAAoB,EACpB,oBAAoB,EACpB,uBAAuB,GACxB,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,OAAO,EACP,UAAU,EACV,aAAa,EACb,kBAAkB,EAClB,aAAa,EACb,WAAW,EACX,mBAAmB,EACnB,mBAAmB,EACnB,aAAa,EACb,cAAc,EACd,KAAK,EACL,UAAU,EACV,WAAW,EACX,WAAW,GACZ,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { BAStamp } from "./client.js";
|
|
2
|
+
export { hashFile } from "./hash.js";
|
|
3
|
+
export { BAStampError, BAStampInvalidRequestError, BAStampUnauthorizedError, BAStampNoCreditsError, BAStampNotFoundError, BAStampConflictError, BAStampRateLimitedError, } from "./errors.js";
|
|
4
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EACL,YAAY,EACZ,0BAA0B,EAC1B,wBAAwB,EACxB,qBAAqB,EACrB,oBAAoB,EACpB,oBAAoB,EACpB,uBAAuB,GACxB,MAAM,aAAa,CAAC"}
|
package/dist/retry.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decide whether an HTTP status code is worth retrying. Retryable: 408
|
|
3
|
+
* (Request Timeout), 429 (Rate Limited), 502/503/504 (transient server
|
|
4
|
+
* issues). Everything else — including 4xx that we shouldn't replay —
|
|
5
|
+
* fails immediately.
|
|
6
|
+
*/
|
|
7
|
+
export declare function shouldRetry(status: number): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Exponential backoff with jitter. Attempt 0 → ~200ms, 1 → ~500ms,
|
|
10
|
+
* 2 → ~1000ms, 3 → ~2000ms, capped at ~5000ms. Jitter prevents
|
|
11
|
+
* thundering-herd retries when many clients see the same blip.
|
|
12
|
+
*/
|
|
13
|
+
export declare function backoffMs(attempt: number): number;
|
|
14
|
+
export declare function sleep(ms: number, signal?: AbortSignal): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* RFC 9110: if the server includes a `Retry-After` header (either a
|
|
17
|
+
* delta-seconds integer or an HTTP-date), honor that instead of our
|
|
18
|
+
* computed backoff — within reason (cap at 60s).
|
|
19
|
+
*/
|
|
20
|
+
export declare function retryAfterMs(header: string | null): number | null;
|
|
21
|
+
//# sourceMappingURL=retry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../src/retry.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAIjD;AAED,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAYrE;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAOjE"}
|
package/dist/retry.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decide whether an HTTP status code is worth retrying. Retryable: 408
|
|
3
|
+
* (Request Timeout), 429 (Rate Limited), 502/503/504 (transient server
|
|
4
|
+
* issues). Everything else — including 4xx that we shouldn't replay —
|
|
5
|
+
* fails immediately.
|
|
6
|
+
*/
|
|
7
|
+
export function shouldRetry(status) {
|
|
8
|
+
return status === 408 || status === 429 || status === 502 || status === 503 || status === 504;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Exponential backoff with jitter. Attempt 0 → ~200ms, 1 → ~500ms,
|
|
12
|
+
* 2 → ~1000ms, 3 → ~2000ms, capped at ~5000ms. Jitter prevents
|
|
13
|
+
* thundering-herd retries when many clients see the same blip.
|
|
14
|
+
*/
|
|
15
|
+
export function backoffMs(attempt) {
|
|
16
|
+
const base = Math.min(5000, 200 * Math.pow(2.5, attempt));
|
|
17
|
+
const jitter = base * 0.3 * Math.random();
|
|
18
|
+
return Math.round(base + jitter);
|
|
19
|
+
}
|
|
20
|
+
export function sleep(ms, signal) {
|
|
21
|
+
return new Promise((resolve, reject) => {
|
|
22
|
+
const t = setTimeout(resolve, ms);
|
|
23
|
+
if (signal) {
|
|
24
|
+
const onAbort = () => {
|
|
25
|
+
clearTimeout(t);
|
|
26
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
27
|
+
};
|
|
28
|
+
if (signal.aborted)
|
|
29
|
+
onAbort();
|
|
30
|
+
else
|
|
31
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* RFC 9110: if the server includes a `Retry-After` header (either a
|
|
37
|
+
* delta-seconds integer or an HTTP-date), honor that instead of our
|
|
38
|
+
* computed backoff — within reason (cap at 60s).
|
|
39
|
+
*/
|
|
40
|
+
export function retryAfterMs(header) {
|
|
41
|
+
if (!header)
|
|
42
|
+
return null;
|
|
43
|
+
const asNumber = Number(header);
|
|
44
|
+
if (Number.isFinite(asNumber))
|
|
45
|
+
return Math.min(60_000, Math.max(0, asNumber * 1000));
|
|
46
|
+
const asDate = Date.parse(header);
|
|
47
|
+
if (Number.isFinite(asDate))
|
|
48
|
+
return Math.min(60_000, Math.max(0, asDate - Date.now()));
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=retry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.js","sourceRoot":"","sources":["../src/retry.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,MAAc;IACxC,OAAO,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG,CAAC;AAChG,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,OAAe;IACvC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC;IAC1D,MAAM,MAAM,GAAG,IAAI,GAAG,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;IAC1C,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,EAAU,EAAE,MAAoB;IACpD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,CAAC,GAAG,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QAClC,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,OAAO,GAAG,GAAG,EAAE;gBACnB,YAAY,CAAC,CAAC,CAAC,CAAC;gBAChB,MAAM,CAAC,IAAI,YAAY,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC,CAAC;YACpD,CAAC,CAAC;YACF,IAAI,MAAM,CAAC,OAAO;gBAAE,OAAO,EAAE,CAAC;;gBACzB,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACjE,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,MAAqB;IAChD,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IAChC,IAAI,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC;IACrF,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAClC,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IACvF,OAAO,IAAI,CAAC;AACd,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/** 0x-prefixed lowercase SHA-256, exactly 66 chars long. */
|
|
2
|
+
export type ContentHash = string;
|
|
3
|
+
export interface StampInput {
|
|
4
|
+
/** SHA-256 of the file contents, 0x-prefixed lowercase. Get one with `hashFile()`. */
|
|
5
|
+
contentHash: ContentHash;
|
|
6
|
+
/** Original filename, stored as ownership metadata only. */
|
|
7
|
+
fileName?: string;
|
|
8
|
+
/** File size in bytes, metadata only. */
|
|
9
|
+
fileSize?: number;
|
|
10
|
+
/** MIME type, metadata only (e.g. `application/pdf`). */
|
|
11
|
+
mimeType?: string;
|
|
12
|
+
/** ISO 3166-1 alpha-2 (e.g. `IT`) — drives the legal framing on the certificate. */
|
|
13
|
+
jurisdiction?: string;
|
|
14
|
+
/** BCP-47 locale for the certificate (e.g. `it`, `en-US`). */
|
|
15
|
+
locale?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface StampResult {
|
|
18
|
+
ownershipId: string;
|
|
19
|
+
stampId: string;
|
|
20
|
+
contentHash: ContentHash;
|
|
21
|
+
fileName: string | null;
|
|
22
|
+
/** True when this caller already owned the same hash — no credit was charged. */
|
|
23
|
+
duplicate: boolean;
|
|
24
|
+
}
|
|
25
|
+
export interface CreateStampResponse {
|
|
26
|
+
stamp: StampResult;
|
|
27
|
+
creditsCharged: number;
|
|
28
|
+
}
|
|
29
|
+
export interface BatchInput {
|
|
30
|
+
items: StampInput[];
|
|
31
|
+
/** Default jurisdiction applied to every item without its own. */
|
|
32
|
+
jurisdiction?: string;
|
|
33
|
+
/** Default locale stored on each ownership row. */
|
|
34
|
+
locale?: string;
|
|
35
|
+
}
|
|
36
|
+
export interface CreateBatchResponse {
|
|
37
|
+
results: StampResult[];
|
|
38
|
+
creditsCharged: number;
|
|
39
|
+
duplicateCount: number;
|
|
40
|
+
}
|
|
41
|
+
export type StampStatus = "pending" | "anchored";
|
|
42
|
+
export interface BitcoinAnchor {
|
|
43
|
+
status: "pending" | "confirmed";
|
|
44
|
+
blockHeight: number | null;
|
|
45
|
+
blockTime: string | null;
|
|
46
|
+
}
|
|
47
|
+
export interface PolygonAnchor {
|
|
48
|
+
chain: "polygon" | "polygon-amoy";
|
|
49
|
+
merkleRoot: string;
|
|
50
|
+
txHash: string;
|
|
51
|
+
blockNumber: number;
|
|
52
|
+
blockTime: string;
|
|
53
|
+
bitcoin: BitcoinAnchor;
|
|
54
|
+
}
|
|
55
|
+
export interface Stamp {
|
|
56
|
+
contentHash: ContentHash;
|
|
57
|
+
status: StampStatus;
|
|
58
|
+
createdAt: string;
|
|
59
|
+
merkleProof: string[] | null;
|
|
60
|
+
anchor: PolygonAnchor | null;
|
|
61
|
+
}
|
|
62
|
+
export interface Account {
|
|
63
|
+
id: string;
|
|
64
|
+
email: string | null;
|
|
65
|
+
credits: number;
|
|
66
|
+
}
|
|
67
|
+
export interface CertificateOptions {
|
|
68
|
+
/** Locale of the certificate PDF (overrides stamp's stored locale). */
|
|
69
|
+
locale?: string;
|
|
70
|
+
/** Jurisdiction code for the legal framing block (overrides stamp's stored jurisdiction). */
|
|
71
|
+
jurisdiction?: string;
|
|
72
|
+
}
|
|
73
|
+
export interface RequestOptions {
|
|
74
|
+
/** Override the auto-generated Idempotency-Key for write operations. */
|
|
75
|
+
idempotencyKey?: string;
|
|
76
|
+
/** AbortSignal — call `controller.abort()` to cancel. */
|
|
77
|
+
signal?: AbortSignal;
|
|
78
|
+
}
|
|
79
|
+
export interface ClientOptions {
|
|
80
|
+
/** `ba_live_<32 hex chars>` from https://bastamp.com/account/api-keys */
|
|
81
|
+
apiKey: string;
|
|
82
|
+
/** Override the API root. Default: `https://bastamp.com`. */
|
|
83
|
+
baseUrl?: string;
|
|
84
|
+
/** Default fetch retry count on 429/5xx + network errors. Default: 3. */
|
|
85
|
+
maxRetries?: number;
|
|
86
|
+
/** Provide a custom fetch (e.g. for testing or non-browser/Node runtimes). */
|
|
87
|
+
fetch?: typeof globalThis.fetch;
|
|
88
|
+
}
|
|
89
|
+
export interface ApiErrorBody {
|
|
90
|
+
error: {
|
|
91
|
+
type: string;
|
|
92
|
+
message: string;
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC;AAEjC,MAAM,WAAW,UAAU;IACzB,sFAAsF;IACtF,WAAW,EAAE,WAAW,CAAC;IACzB,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oFAAoF;IACpF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,8DAA8D;IAC9D,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,WAAW,CAAC;IACzB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,iFAAiF;IACjF,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,WAAW,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,kEAAkE;IAClE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,UAAU,CAAC;AAEjD,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,SAAS,GAAG,WAAW,CAAC;IAChC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,SAAS,GAAG,cAAc,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,aAAa,CAAC;CACxB;AAED,MAAM,WAAW,KAAK;IACpB,WAAW,EAAE,WAAW,CAAC;IACzB,MAAM,EAAE,WAAW,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAC7B,MAAM,EAAE,aAAa,GAAG,IAAI,CAAC;CAC9B;AAED,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,uEAAuE;IACvE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6FAA6F;IAC7F,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,wEAAwE;IACxE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,yDAAyD;IACzD,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,yEAAyE;IACzE,MAAM,EAAE,MAAM,CAAC;IACf,6DAA6D;IAC7D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8EAA8E;IAC9E,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CACjC;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE;QACL,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bastamp/sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Official TypeScript SDK for the BA | Stamp REST API — anchor SHA-256 hashes on Polygon.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Aletheia Tech Ltd",
|
|
7
|
+
"homepage": "https://bastamp.com",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/stamp-verify/bastamp-sdk-ts.git"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/stamp-verify/bastamp-sdk-ts/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"bastamp",
|
|
17
|
+
"blockchain",
|
|
18
|
+
"timestamp",
|
|
19
|
+
"polygon",
|
|
20
|
+
"sha256",
|
|
21
|
+
"merkle-proof",
|
|
22
|
+
"evidence",
|
|
23
|
+
"eidas",
|
|
24
|
+
"sdk",
|
|
25
|
+
"typescript"
|
|
26
|
+
],
|
|
27
|
+
"type": "module",
|
|
28
|
+
"main": "./dist/index.js",
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"exports": {
|
|
31
|
+
".": {
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"default": "./dist/index.js"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"dist",
|
|
38
|
+
"README.md",
|
|
39
|
+
"LICENSE"
|
|
40
|
+
],
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=20"
|
|
43
|
+
},
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "tsc -p tsconfig.json",
|
|
46
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
47
|
+
"clean": "rm -rf dist",
|
|
48
|
+
"prepublishOnly": "npm run clean && npm run build"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@types/node": "^22.0.0",
|
|
52
|
+
"tsx": "^4.21.0",
|
|
53
|
+
"typescript": "^5.7.0"
|
|
54
|
+
}
|
|
55
|
+
}
|