@podosoft/podokit-api-client 0.4.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 +34 -0
- package/dist/index.d.ts +1680 -0
- package/dist/index.js +87 -0
- package/package.json +39 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { createAuthClient } from "better-auth/client";
|
|
2
|
+
import { adminClient, twoFactorClient } from "better-auth/client/plugins";
|
|
3
|
+
/** Error thrown when the API returns the standard error envelope or a non-2xx status. */
|
|
4
|
+
export class ApiError extends Error {
|
|
5
|
+
code;
|
|
6
|
+
statusCode;
|
|
7
|
+
details;
|
|
8
|
+
constructor(code, message, statusCode, details) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = "ApiError";
|
|
11
|
+
this.code = code;
|
|
12
|
+
this.statusCode = statusCode;
|
|
13
|
+
this.details = details;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function toApiError(data, response) {
|
|
17
|
+
if (data && typeof data === "object" && "error" in data) {
|
|
18
|
+
const error = data.error;
|
|
19
|
+
if (error && typeof error === "object") {
|
|
20
|
+
const record = error;
|
|
21
|
+
return new ApiError(typeof record.code === "string" ? record.code : "HTTP_ERROR", typeof record.message === "string" ? record.message : response.statusText, typeof record.statusCode === "number" ? record.statusCode : response.status, record.details);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return new ApiError("HTTP_ERROR", response.statusText || "Request failed", response.status);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Create a typed API client. Every frontend talks to the PodoKit backend
|
|
28
|
+
* through this single entry point — never with a raw fetch.
|
|
29
|
+
*
|
|
30
|
+
* - `client.auth` is the better-auth client (email/password, sessions, and the
|
|
31
|
+
* admin plugin: `client.auth.admin.listUsers()`, `banUser`, `setRole`, ...).
|
|
32
|
+
* - `client.get/post/put/patch/del` call the app's REST endpoints and parse the
|
|
33
|
+
* standard error envelope, throwing {@link ApiError} on failure.
|
|
34
|
+
*/
|
|
35
|
+
export function createApiClient(options = {}) {
|
|
36
|
+
const baseUrl = options.baseUrl ?? "";
|
|
37
|
+
const apiBasePath = options.apiBasePath ?? "/api";
|
|
38
|
+
const credentials = options.credentials ?? "include";
|
|
39
|
+
const doFetch = options.fetch ?? globalThis.fetch;
|
|
40
|
+
// better-auth mounts at /api/auth and needs an absolute origin. In the
|
|
41
|
+
// browser an empty baseUrl resolves to the current origin.
|
|
42
|
+
const authOrigin = options.baseUrl ?? (typeof globalThis.location === "undefined" ? "" : globalThis.location.origin);
|
|
43
|
+
// Inner factory so the return type keeps the admin plugin's client methods
|
|
44
|
+
// (a bare ReturnType<typeof createAuthClient> would drop the plugin augmentation).
|
|
45
|
+
const makeAuthClient = () => createAuthClient({
|
|
46
|
+
baseURL: authOrigin,
|
|
47
|
+
plugins: [adminClient(), twoFactorClient()],
|
|
48
|
+
fetchOptions: {
|
|
49
|
+
credentials,
|
|
50
|
+
...(options.fetch ? { customFetchImpl: options.fetch } : {}),
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
// Created lazily: REST-only usage (and SSR without an origin) never triggers
|
|
54
|
+
// the better-auth client's eager URL validation.
|
|
55
|
+
let authClient;
|
|
56
|
+
function getAuth() {
|
|
57
|
+
authClient ??= makeAuthClient();
|
|
58
|
+
return authClient;
|
|
59
|
+
}
|
|
60
|
+
async function request(method, path, body) {
|
|
61
|
+
const response = await doFetch(`${baseUrl}${apiBasePath}${path}`, {
|
|
62
|
+
method,
|
|
63
|
+
credentials,
|
|
64
|
+
headers: body === undefined ? undefined : { "content-type": "application/json" },
|
|
65
|
+
body: body === undefined ? undefined : JSON.stringify(body),
|
|
66
|
+
});
|
|
67
|
+
const text = await response.text();
|
|
68
|
+
const data = text ? JSON.parse(text) : undefined;
|
|
69
|
+
if (!response.ok) {
|
|
70
|
+
throw toApiError(data, response);
|
|
71
|
+
}
|
|
72
|
+
return data;
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
/** The better-auth client (auth + admin plugin), created on first access. */
|
|
76
|
+
get auth() {
|
|
77
|
+
return getAuth();
|
|
78
|
+
},
|
|
79
|
+
/** Low-level typed request against the app's REST API. */
|
|
80
|
+
request,
|
|
81
|
+
get: (path) => request("GET", path),
|
|
82
|
+
post: (path, body) => request("POST", path, body),
|
|
83
|
+
put: (path, body) => request("PUT", path, body),
|
|
84
|
+
patch: (path, body) => request("PATCH", path, body),
|
|
85
|
+
del: (path) => request("DELETE", path),
|
|
86
|
+
};
|
|
87
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@podosoft/podokit-api-client",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Typed API client for PodoKit backends: better-auth (auth + admin) plus a standard error-envelope request layer.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/podosoft-dev/podokit.git",
|
|
10
|
+
"directory": "packages/api-client"
|
|
11
|
+
},
|
|
12
|
+
"main": "./dist/index.js",
|
|
13
|
+
"module": "./dist/index.js",
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"import": "./dist/index.js"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist"
|
|
23
|
+
],
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=20"
|
|
26
|
+
},
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public",
|
|
29
|
+
"provenance": true
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"better-auth": "^1.6.23"
|
|
33
|
+
},
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "tsc -p tsconfig.json",
|
|
36
|
+
"lint": "tsc -p tsconfig.json --noEmit",
|
|
37
|
+
"test": "vitest run"
|
|
38
|
+
}
|
|
39
|
+
}
|