@nokinc-flur/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 +1 -0
- package/README.md +24 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +74 -0
- package/openapi/flur.openapi.json +58 -0
- package/package.json +43 -0
package/LICENSE
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
MIT
|
package/README.md
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# flur-sdk (Sprint 1 / Week 1)
|
|
2
|
+
|
|
3
|
+
Typed client scaffold aligned to backend OpenAPI.
|
|
4
|
+
|
|
5
|
+
## Install (local dev)
|
|
6
|
+
```bash
|
|
7
|
+
npm ci
|
|
8
|
+
npm test
|
|
9
|
+
npm run build
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Generate types (optional)
|
|
13
|
+
This repo includes a minimal OpenAPI file at `openapi/flur.openapi.json`.
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm run gen
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## E2E test against a running backend (optional)
|
|
20
|
+
1) Start backend locally (see backend README)
|
|
21
|
+
2) Run (E2E executes when RUN_E2E=1 or FLUR_BASE_URL is set):
|
|
22
|
+
```bash
|
|
23
|
+
FLUR_BASE_URL=http://localhost:8080 RUN_E2E=1 npm test
|
|
24
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
type FlurClientOptions = {
|
|
2
|
+
baseUrl: string;
|
|
3
|
+
fetchImpl?: typeof fetch;
|
|
4
|
+
timeoutMs?: number;
|
|
5
|
+
};
|
|
6
|
+
declare class FlurClient {
|
|
7
|
+
private readonly baseUrl;
|
|
8
|
+
private readonly fetchImpl;
|
|
9
|
+
private readonly timeoutMs;
|
|
10
|
+
constructor(opts: FlurClientOptions);
|
|
11
|
+
health(): Promise<{
|
|
12
|
+
ok: boolean;
|
|
13
|
+
}>;
|
|
14
|
+
welcome(): Promise<{
|
|
15
|
+
message: string;
|
|
16
|
+
}>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
type FlurErrorCode = "NETWORK_ERROR" | "HTTP_ERROR" | "TIMEOUT" | "UNKNOWN";
|
|
20
|
+
declare class FlurError extends Error {
|
|
21
|
+
readonly code: FlurErrorCode;
|
|
22
|
+
readonly status?: number;
|
|
23
|
+
readonly details?: unknown;
|
|
24
|
+
constructor(message: string, code: FlurErrorCode, opts?: {
|
|
25
|
+
status?: number;
|
|
26
|
+
details?: unknown;
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export { FlurClient, type FlurClientOptions, FlurError };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// src/errors.ts
|
|
2
|
+
var FlurError = class extends Error {
|
|
3
|
+
code;
|
|
4
|
+
status;
|
|
5
|
+
details;
|
|
6
|
+
constructor(message, code, opts) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = "FlurError";
|
|
9
|
+
this.code = code;
|
|
10
|
+
this.status = opts?.status;
|
|
11
|
+
this.details = opts?.details;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
async function mapToFlurError(res) {
|
|
15
|
+
let details = void 0;
|
|
16
|
+
try {
|
|
17
|
+
const ct = res.headers.get("content-type") ?? "";
|
|
18
|
+
details = ct.includes("application/json") ? await res.json() : await res.text();
|
|
19
|
+
} catch {
|
|
20
|
+
}
|
|
21
|
+
return new FlurError(`HTTP ${res.status}`, "HTTP_ERROR", { status: res.status, details });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// src/client.ts
|
|
25
|
+
var FlurClient = class {
|
|
26
|
+
baseUrl;
|
|
27
|
+
fetchImpl;
|
|
28
|
+
timeoutMs;
|
|
29
|
+
constructor(opts) {
|
|
30
|
+
this.baseUrl = opts.baseUrl.replace(/\/$/, "");
|
|
31
|
+
this.fetchImpl = opts.fetchImpl ?? fetch;
|
|
32
|
+
this.timeoutMs = opts.timeoutMs ?? 1e4;
|
|
33
|
+
}
|
|
34
|
+
async health() {
|
|
35
|
+
const url = `${this.baseUrl}/health`;
|
|
36
|
+
const controller = new AbortController();
|
|
37
|
+
const t = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
38
|
+
try {
|
|
39
|
+
const res = await this.fetchImpl(url, { method: "GET", signal: controller.signal });
|
|
40
|
+
if (!res.ok) throw await mapToFlurError(res);
|
|
41
|
+
return await res.json();
|
|
42
|
+
} catch (err) {
|
|
43
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
44
|
+
throw new FlurError("Request timed out", "TIMEOUT");
|
|
45
|
+
}
|
|
46
|
+
if (err instanceof FlurError) throw err;
|
|
47
|
+
throw new FlurError("Network error", "NETWORK_ERROR", { details: String(err) });
|
|
48
|
+
} finally {
|
|
49
|
+
clearTimeout(t);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
async welcome() {
|
|
53
|
+
const url = `${this.baseUrl}/welcome`;
|
|
54
|
+
const controller = new AbortController();
|
|
55
|
+
const t = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
56
|
+
try {
|
|
57
|
+
const res = await this.fetchImpl(url, { method: "GET", signal: controller.signal });
|
|
58
|
+
if (!res.ok) throw await mapToFlurError(res);
|
|
59
|
+
return await res.json();
|
|
60
|
+
} catch (err) {
|
|
61
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
62
|
+
throw new FlurError("Request timed out", "TIMEOUT");
|
|
63
|
+
}
|
|
64
|
+
if (err instanceof FlurError) throw err;
|
|
65
|
+
throw new FlurError("Network error", "NETWORK_ERROR", { details: String(err) });
|
|
66
|
+
} finally {
|
|
67
|
+
clearTimeout(t);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
export {
|
|
72
|
+
FlurClient,
|
|
73
|
+
FlurError
|
|
74
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"openapi": "3.0.3",
|
|
3
|
+
"info": {
|
|
4
|
+
"title": "Flur API",
|
|
5
|
+
"version": "0.1.0"
|
|
6
|
+
},
|
|
7
|
+
"paths": {
|
|
8
|
+
"/health": {
|
|
9
|
+
"get": {
|
|
10
|
+
"responses": {
|
|
11
|
+
"200": {
|
|
12
|
+
"description": "OK",
|
|
13
|
+
"content": {
|
|
14
|
+
"application/json": {
|
|
15
|
+
"schema": {
|
|
16
|
+
"type": "object",
|
|
17
|
+
"properties": {
|
|
18
|
+
"ok": {
|
|
19
|
+
"type": "boolean"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"required": [
|
|
23
|
+
"ok"
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"/welcome": {
|
|
33
|
+
"get": {
|
|
34
|
+
"responses": {
|
|
35
|
+
"200": {
|
|
36
|
+
"description": "OK",
|
|
37
|
+
"content": {
|
|
38
|
+
"application/json": {
|
|
39
|
+
"schema": {
|
|
40
|
+
"type": "object",
|
|
41
|
+
"properties": {
|
|
42
|
+
"message": {
|
|
43
|
+
"type": "string",
|
|
44
|
+
"example": "welcome to flur wallet. Today is 2026-02-07T19:09:53.238Z"
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"required": [
|
|
48
|
+
"message"
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nokinc-flur/sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Flur Wallet SDK (sprint 1 scaffold)",
|
|
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
|
+
"default": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"openapi",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"lint": "eslint .",
|
|
23
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
24
|
+
"test": "vitest run",
|
|
25
|
+
"build": "tsup src/index.ts --format esm --dts",
|
|
26
|
+
"gen": "openapi-typescript openapi/flur.openapi.json -o src/gen/openapi-types.ts"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"zod": "^3.23.8"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/node": "^20.11.30",
|
|
33
|
+
"@typescript-eslint/eslint-plugin": "^7.7.1",
|
|
34
|
+
"@typescript-eslint/parser": "^7.7.1",
|
|
35
|
+
"eslint": "^8.57.0",
|
|
36
|
+
"eslint-config-prettier": "^9.1.0",
|
|
37
|
+
"openapi-typescript": "^7.5.1",
|
|
38
|
+
"prettier": "^3.2.5",
|
|
39
|
+
"tsup": "^8.0.2",
|
|
40
|
+
"typescript": "^5.4.5",
|
|
41
|
+
"vitest": "^1.5.0"
|
|
42
|
+
}
|
|
43
|
+
}
|