@midnightntwrk/faucet-client 1.0.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 ADDED
@@ -0,0 +1,16 @@
1
+ # Faucet Client
2
+
3
+ A small package, allowing to use Faucet functionality in a programmatic way.
4
+ Its API is mostly a factory function to initialize the client, which can later
5
+ be used to request tokens from the faucet.
6
+
7
+ ## Example
8
+
9
+ ```ts
10
+ import { FaucetClient } from '@midnightntwrk/faucet-client';
11
+
12
+ const client = FaucetClient("http://localhost:5300/api");
13
+ const authToken = await client.login("<username>", "<password>");
14
+ const initialized = client.init(authToken);
15
+ const { transactionIdentifier } = initialized.requestTokens("<my wallet address>");
16
+ ```
@@ -0,0 +1,30 @@
1
+ import { ErrorResponse, FaucetClientRequests } from "@midnightntwrk/faucet-internal-api";
2
+ /**
3
+ * FaucetClient-specific subclass of `Error`
4
+ */
5
+ export declare class ClientError extends Error {
6
+ /**
7
+ * Error type. Following values are expected:
8
+ *
9
+ * - "decoding_error" -- to indicate decoding of request or response payload failed (is generally not expected when using the client)
10
+ * - "error" -- to indicate general/unexpected error that occurred when processing a request
11
+ * - "auth_error" -- to indicate authentication error
12
+ * - "rate_limit_error" -- to indicate exceeding rate limit set in place
13
+ */
14
+ readonly type: ErrorResponse["status"];
15
+ constructor(type: ErrorResponse["status"], message: string);
16
+ }
17
+ /**
18
+ * Client's entrypoint. It takes and (optionally) fetch function to use.
19
+ * @param url - API url, like `http://localhost:5300/api` in local development
20
+ * @param pollInterval - how often check request status
21
+ * @param fetchFn - fetch function to use. By default it is `global.fetch`, but in case of additional
22
+ * configurations needed it is possible to override it with any compatible function
23
+ */
24
+ interface FaucetClientI {
25
+ url: string;
26
+ pollInterval: number;
27
+ }
28
+ export declare const FaucetClient: ({ url, pollInterval, }: FaucetClientI) => FaucetClientRequests;
29
+ export {};
30
+ //# sourceMappingURL=FaucetClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FaucetClient.d.ts","sourceRoot":"","sources":["../src/FaucetClient.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,aAAa,EAEb,oBAAoB,EAErB,MAAM,oCAAoC,CAAC;AAM5C;;GAEG;AACH,qBAAa,WAAY,SAAQ,KAAK;IACpC;;;;;;;OAOG;IACH,SAAgB,IAAI,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;gBAElC,IAAI,EAAE,aAAa,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,MAAM;CAI3D;AAsDD;;;;;;GAMG;AAEH,UAAU,aAAa;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,MAAM,CAAC;CACtB;AACD,eAAO,MAAM,YAAY,GAAI,wBAG1B,aAAa,KAAG,oBAuFlB,CAAC"}
@@ -0,0 +1,125 @@
1
+ import { dripRequestCodec, dripResponseCodec, errorResponseCodec, } from "@midnightntwrk/faucet-internal-api";
2
+ import { either } from "fp-ts";
3
+ import * as t from "io-ts";
4
+ import { PathReporter } from "io-ts/lib/PathReporter.js";
5
+ import { catchError, distinctUntilChanged, from, of, shareReplay, switchMap, timer } from "rxjs";
6
+ /**
7
+ * FaucetClient-specific subclass of `Error`
8
+ */
9
+ export class ClientError extends Error {
10
+ /**
11
+ * Error type. Following values are expected:
12
+ *
13
+ * - "decoding_error" -- to indicate decoding of request or response payload failed (is generally not expected when using the client)
14
+ * - "error" -- to indicate general/unexpected error that occurred when processing a request
15
+ * - "auth_error" -- to indicate authentication error
16
+ * - "rate_limit_error" -- to indicate exceeding rate limit set in place
17
+ */
18
+ type;
19
+ constructor(type, message) {
20
+ super(message);
21
+ this.type = type;
22
+ }
23
+ }
24
+ const parseResponse = (codec) => (response) => {
25
+ const decodeResult = codec.decode(response);
26
+ return either.fold((errors) => Promise.resolve(errors)
27
+ .then(t.failures)
28
+ .then(PathReporter.report)
29
+ .then((array) => array.join("\n"))
30
+ .then((message) => Promise.reject(new Error(message))), (value) => Promise.resolve(value))(decodeResult);
31
+ };
32
+ const parseDripResponse = parseResponse(dripResponseCodec);
33
+ const parseJSON = async (response) => {
34
+ if (response.ok) {
35
+ return response.json();
36
+ }
37
+ else if (!response.ok && response.status === 401) {
38
+ return response.text().then((text) => Promise.reject(new ClientError("auth_error", text)));
39
+ }
40
+ else if (!response.ok && response.status === 429) {
41
+ return response
42
+ .text()
43
+ .then((text) => Promise.reject(new ClientError("rate_limit_error", text)));
44
+ }
45
+ else {
46
+ return response.json().then((parsed) => {
47
+ const maybeKnownError = errorResponseCodec.decode(parsed);
48
+ const error = either.fold(() => {
49
+ // Try to extract error message from drip API error format
50
+ const dripError = parsed;
51
+ if (dripError.error) {
52
+ return new ClientError("error", dripError.error);
53
+ }
54
+ return new ClientError("decoding_error", JSON.stringify(parsed));
55
+ }, (err) => new ClientError(err.status, err.message))(maybeKnownError);
56
+ return Promise.reject(error);
57
+ });
58
+ }
59
+ };
60
+ export const FaucetClient = ({ url, pollInterval = 10_000, }) => {
61
+ const pollForResponse = (dripId, errorRetriesLeft = 100) => {
62
+ const makeCall = () => {
63
+ return fetch(`${url}/drips/${dripId}`, {
64
+ headers: {
65
+ "Content-Type": "application/json",
66
+ },
67
+ })
68
+ .then(parseJSON)
69
+ .then(parseDripResponse);
70
+ };
71
+ const delay = () => new Promise((resolve) => {
72
+ setTimeout(resolve, pollInterval + Math.round((Math.random() * 2 * pollInterval) / 5 - pollInterval / 5)); // By default every 10s +/- 2s
73
+ });
74
+ return delay()
75
+ .then(() => makeCall())
76
+ .then((drip) => {
77
+ switch (drip.status) {
78
+ case "PENDING":
79
+ return pollForResponse(dripId, errorRetriesLeft);
80
+ case "CONFIRMED":
81
+ return Promise.resolve(drip);
82
+ case "FAILED":
83
+ return Promise.reject(new ClientError("error", drip.error ?? "Drip failed"));
84
+ }
85
+ }, (error) => {
86
+ // eslint-disable-next-line no-console
87
+ console.error("Got error from faucet", error);
88
+ if (errorRetriesLeft >= 1) {
89
+ return pollForResponse(dripId, errorRetriesLeft - 1);
90
+ }
91
+ else {
92
+ throw new ClientError("error", String(error));
93
+ }
94
+ });
95
+ };
96
+ const healthStatus$ = timer(0, 30_000).pipe(switchMap(() => from(fetch(`${url}/health`)
97
+ .then((response) => response.json())
98
+ .then((data) => {
99
+ const status = data.status === "SERVING"
100
+ ? "ok"
101
+ : "not_ok";
102
+ return { status };
103
+ })).pipe(catchError(() => of({ status: "not_ok" })))), distinctUntilChanged((prev, curr) => prev.status === curr.status), shareReplay(1));
104
+ return {
105
+ requestTokens(address, captchaToken, amount) {
106
+ const request = {
107
+ recipientAddress: address,
108
+ amount: BigInt(amount),
109
+ };
110
+ return fetch(`${url}/drips`, {
111
+ method: "POST",
112
+ headers: {
113
+ "Content-Type": "application/json",
114
+ "X-Captcha-Token": captchaToken,
115
+ },
116
+ body: JSON.stringify(dripRequestCodec.encode(request)),
117
+ })
118
+ .then(parseJSON)
119
+ .then(parseDripResponse)
120
+ .then((drip) => pollForResponse(drip?.dripId));
121
+ },
122
+ healthStatus$,
123
+ };
124
+ };
125
+ //# sourceMappingURL=FaucetClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FaucetClient.js","sourceRoot":"","sources":["../src/FaucetClient.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export * from "./FaucetClient.js";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export * from "./FaucetClient.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@midnightntwrk/faucet-client",
3
+ "version": "1.0.0",
4
+ "description": "Midnight faucet library",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./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
+ "repository": {
16
+ "type": "git",
17
+ "url": "https://github.com/midnightntwrk/midnight-faucet-api.git"
18
+ },
19
+ "author": "IOHK",
20
+ "license": "Apache-2.0",
21
+ "publishConfig": {
22
+ "access": "public"
23
+ },
24
+ "scripts": {
25
+ "lint": "eslint --max-warnings=0",
26
+ "build": "tsc -b ./tsconfig.build.json",
27
+ "typecheck": "tsc -p tsconfig.json --noEmit",
28
+ "prepack": "yarn build",
29
+ "do-publish": "yarn npm publish"
30
+ },
31
+ "files": [
32
+ "dist/"
33
+ ],
34
+ "devDependencies": {
35
+ "eslint": "10.10.0",
36
+ "fast-check": "4.9.0",
37
+ "testcontainers": "12.1.0",
38
+ "typescript": "6.0.3"
39
+ },
40
+ "dependencies": {
41
+ "@midnightntwrk/faucet-internal-api": "^1.0.0",
42
+ "luxon": "^3.4.3"
43
+ },
44
+ "peerDependencies": {
45
+ "fp-ts": "^2.16.0",
46
+ "io-ts": "^2.2.20",
47
+ "rxjs": "^7.5.5"
48
+ }
49
+ }