@nons-dev/sdk-react 1.0.3
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/dist/client.d.ts +8 -0
- package/dist/client.js +36 -0
- package/dist/client.js.map +1 -0
- package/dist/core/errors.d.ts +18 -0
- package/dist/core/errors.js +50 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/http.d.ts +14 -0
- package/dist/core/http.js +83 -0
- package/dist/core/http.js.map +1 -0
- package/dist/core/token-manager.d.ts +9 -0
- package/dist/core/token-manager.js +44 -0
- package/dist/core/token-manager.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/modules/auth.module.d.ts +22 -0
- package/dist/modules/auth.module.js +45 -0
- package/dist/modules/auth.module.js.map +1 -0
- package/dist/types/config.types.d.ts +19 -0
- package/dist/types/config.types.js +3 -0
- package/dist/types/config.types.js.map +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +18 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +27 -0
- package/readme.md +191 -0
package/dist/client.d.ts
ADDED
package/dist/client.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NonsClient = void 0;
|
|
4
|
+
const http_1 = require("./core/http");
|
|
5
|
+
const token_manager_1 = require("./core/token-manager");
|
|
6
|
+
const auth_module_1 = require("./modules/auth.module");
|
|
7
|
+
class MemoryStorageAdapter {
|
|
8
|
+
accessToken = null;
|
|
9
|
+
refreshToken = null;
|
|
10
|
+
getAccessToken() { return this.accessToken; }
|
|
11
|
+
setAccessToken(token) { this.accessToken = token; }
|
|
12
|
+
getRefreshToken() { return this.refreshToken; }
|
|
13
|
+
setRefreshToken(token) { this.refreshToken = token; }
|
|
14
|
+
clearTokens() {
|
|
15
|
+
this.accessToken = null;
|
|
16
|
+
this.refreshToken = null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
class NonsClient {
|
|
20
|
+
http;
|
|
21
|
+
tokenManager;
|
|
22
|
+
auth;
|
|
23
|
+
constructor(config) {
|
|
24
|
+
const storage = config.storage || new MemoryStorageAdapter();
|
|
25
|
+
this.tokenManager = new token_manager_1.TokenManager(storage);
|
|
26
|
+
this.http = new http_1.HttpClient({
|
|
27
|
+
baseUrl: config.baseUrl,
|
|
28
|
+
timeout: config.timeout ?? 10000,
|
|
29
|
+
tokenManager: this.tokenManager,
|
|
30
|
+
onUnauthorized: config.onUnauthorized,
|
|
31
|
+
});
|
|
32
|
+
this.auth = new auth_module_1.AuthModule(this.http, config.baseUrl);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
exports.NonsClient = NonsClient;
|
|
36
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":";;;AAAA,sCAAyC;AACzC,wDAAoD;AACpD,uDAAmD;AAGnD,MAAM,oBAAoB;IAChB,WAAW,GAAkB,IAAI,CAAC;IAClC,YAAY,GAAkB,IAAI,CAAC;IAE3C,cAAc,KAAK,OAAO,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC;IAC7C,cAAc,CAAC,KAAa,IAAI,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC,CAAC,CAAC;IAC3D,eAAe,KAAK,OAAO,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC;IAC/C,eAAe,CAAC,KAAa,IAAI,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC,CAAC,CAAC;IAC7D,WAAW;QACT,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QACxB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;IAC3B,CAAC;CACF;AAED,MAAa,UAAU;IACb,IAAI,CAAa;IACjB,YAAY,CAAe;IAE1B,IAAI,CAAa;IAE1B,YAAY,MAAwB;QAClC,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,IAAI,IAAI,oBAAoB,EAAE,CAAC;QAC7D,IAAI,CAAC,YAAY,GAAG,IAAI,4BAAY,CAAC,OAAO,CAAC,CAAC;QAE9C,IAAI,CAAC,IAAI,GAAG,IAAI,iBAAU,CAAC;YACzB,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,KAAK;YAChC,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,cAAc,EAAE,MAAM,CAAC,cAAc;SACtC,CAAC,CAAC;QAEH,IAAI,CAAC,IAAI,GAAG,IAAI,wBAAU,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IACxD,CAAC;CACF;AAnBD,gCAmBC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare enum ErrorCode {
|
|
2
|
+
UNAUTHORIZED = "UNAUTHORIZED",
|
|
3
|
+
TOKEN_EXPIRED = "TOKEN_EXPIRED",
|
|
4
|
+
INVALID_CREDENTIALS = "INVALID_CREDENTIALS",
|
|
5
|
+
VALIDATION_ERROR = "VALIDATION_ERROR",
|
|
6
|
+
SERVER_ERROR = "SERVER_ERROR",
|
|
7
|
+
NETWORK_ERROR = "NETWORK_ERROR",
|
|
8
|
+
UNKNOWN = "UNKNOWN"
|
|
9
|
+
}
|
|
10
|
+
export declare class NonsError extends Error {
|
|
11
|
+
readonly code: ErrorCode | string;
|
|
12
|
+
readonly statusCode: number;
|
|
13
|
+
readonly details?: unknown | undefined;
|
|
14
|
+
constructor(code: ErrorCode | string, message: string, statusCode: number, details?: unknown | undefined);
|
|
15
|
+
isAuthError(): boolean;
|
|
16
|
+
isValidationError(): boolean;
|
|
17
|
+
}
|
|
18
|
+
export declare function parseErrorResponse(response: Response): Promise<NonsError>;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NonsError = exports.ErrorCode = void 0;
|
|
4
|
+
exports.parseErrorResponse = parseErrorResponse;
|
|
5
|
+
var ErrorCode;
|
|
6
|
+
(function (ErrorCode) {
|
|
7
|
+
ErrorCode["UNAUTHORIZED"] = "UNAUTHORIZED";
|
|
8
|
+
ErrorCode["TOKEN_EXPIRED"] = "TOKEN_EXPIRED";
|
|
9
|
+
ErrorCode["INVALID_CREDENTIALS"] = "INVALID_CREDENTIALS";
|
|
10
|
+
ErrorCode["VALIDATION_ERROR"] = "VALIDATION_ERROR";
|
|
11
|
+
ErrorCode["SERVER_ERROR"] = "SERVER_ERROR";
|
|
12
|
+
ErrorCode["NETWORK_ERROR"] = "NETWORK_ERROR";
|
|
13
|
+
ErrorCode["UNKNOWN"] = "UNKNOWN";
|
|
14
|
+
})(ErrorCode || (exports.ErrorCode = ErrorCode = {}));
|
|
15
|
+
class NonsError extends Error {
|
|
16
|
+
code;
|
|
17
|
+
statusCode;
|
|
18
|
+
details;
|
|
19
|
+
constructor(code, message, statusCode, details) {
|
|
20
|
+
super(message);
|
|
21
|
+
this.code = code;
|
|
22
|
+
this.statusCode = statusCode;
|
|
23
|
+
this.details = details;
|
|
24
|
+
this.name = 'NonsError';
|
|
25
|
+
Object.setPrototypeOf(this, NonsError.prototype);
|
|
26
|
+
}
|
|
27
|
+
isAuthError() {
|
|
28
|
+
return this.statusCode === 401 || this.code === ErrorCode.UNAUTHORIZED;
|
|
29
|
+
}
|
|
30
|
+
isValidationError() {
|
|
31
|
+
return this.statusCode === 422 || this.code === ErrorCode.VALIDATION_ERROR;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.NonsError = NonsError;
|
|
35
|
+
async function parseErrorResponse(response) {
|
|
36
|
+
let code = ErrorCode.UNKNOWN;
|
|
37
|
+
let message = 'خطای ناشناخته';
|
|
38
|
+
let details;
|
|
39
|
+
try {
|
|
40
|
+
const body = await response.json();
|
|
41
|
+
code = body.code || (body.error && body.error.code) || ErrorCode.UNKNOWN;
|
|
42
|
+
message = body.message || (body.error && body.error.message) || message;
|
|
43
|
+
details = body.details || (body.error && body.error.details);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
message = `خطای سرور — کد ${response.status}`;
|
|
47
|
+
}
|
|
48
|
+
return new NonsError(code, message, response.status, details);
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":";;;AA+BA,gDAeC;AA9CD,IAAY,SAQX;AARD,WAAY,SAAS;IACnB,0CAAoC,CAAA;IACpC,4CAAqC,CAAA;IACrC,wDAA2C,CAAA;IAC3C,kDAAwC,CAAA;IACxC,0CAAoC,CAAA;IACpC,4CAAqC,CAAA;IACrC,gCAA+B,CAAA;AACjC,CAAC,EARW,SAAS,yBAAT,SAAS,QAQpB;AAED,MAAa,SAAU,SAAQ,KAAK;IAEhB;IAEA;IACA;IAJlB,YACkB,IAAwB,EACxC,OAAe,EACC,UAAkB,EAClB,OAAiB;QAEjC,KAAK,CAAC,OAAO,CAAC,CAAC;QALC,SAAI,GAAJ,IAAI,CAAoB;QAExB,eAAU,GAAV,UAAU,CAAQ;QAClB,YAAO,GAAP,OAAO,CAAU;QAGjC,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;QACxB,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACnD,CAAC;IAED,WAAW;QACT,OAAO,IAAI,CAAC,UAAU,KAAK,GAAG,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,YAAY,CAAC;IACzE,CAAC;IAED,iBAAiB;QACf,OAAO,IAAI,CAAC,UAAU,KAAK,GAAG,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,gBAAgB,CAAC;IAC7E,CAAC;CACF;AAnBD,8BAmBC;AAEM,KAAK,UAAU,kBAAkB,CAAC,QAAkB;IACzD,IAAI,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC;IAC7B,IAAI,OAAO,GAAG,eAAe,CAAC;IAC9B,IAAI,OAAgB,CAAC;IAErB,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACnC,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,OAAO,CAAC;QACzE,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC;QACxE,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC/D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,kBAAkB,QAAQ,CAAC,MAAM,EAAE,CAAC;IAChD,CAAC;IAED,OAAO,IAAI,SAAS,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAChE,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { HttpClientConfig } from '../types/config.types';
|
|
2
|
+
export declare class HttpClient {
|
|
3
|
+
private baseUrl;
|
|
4
|
+
private timeout;
|
|
5
|
+
private tokenManager;
|
|
6
|
+
private onUnauthorized?;
|
|
7
|
+
constructor(config: HttpClientConfig);
|
|
8
|
+
request<T>(path: string, options?: RequestInit): Promise<T>;
|
|
9
|
+
get<T>(path: string, params?: Record<string, unknown>): Promise<T>;
|
|
10
|
+
post<T>(path: string, body?: unknown): Promise<T>;
|
|
11
|
+
put<T>(path: string, body?: unknown): Promise<T>;
|
|
12
|
+
patch<T>(path: string, body?: unknown): Promise<T>;
|
|
13
|
+
delete<T>(path: string): Promise<T>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HttpClient = void 0;
|
|
4
|
+
const errors_1 = require("./errors");
|
|
5
|
+
class HttpClient {
|
|
6
|
+
baseUrl;
|
|
7
|
+
timeout;
|
|
8
|
+
tokenManager;
|
|
9
|
+
onUnauthorized;
|
|
10
|
+
constructor(config) {
|
|
11
|
+
this.baseUrl = config.baseUrl;
|
|
12
|
+
this.timeout = config.timeout ?? 10000;
|
|
13
|
+
this.tokenManager = config.tokenManager;
|
|
14
|
+
this.onUnauthorized = config.onUnauthorized;
|
|
15
|
+
}
|
|
16
|
+
async request(path, options = {}) {
|
|
17
|
+
const token = await this.tokenManager.getAccessToken();
|
|
18
|
+
const headers = {
|
|
19
|
+
'Content-Type': 'application/json',
|
|
20
|
+
...(options.headers || {}),
|
|
21
|
+
};
|
|
22
|
+
if (token) {
|
|
23
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
24
|
+
}
|
|
25
|
+
const controller = new AbortController();
|
|
26
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
27
|
+
let response;
|
|
28
|
+
try {
|
|
29
|
+
response = await fetch(`${this.baseUrl}${path}`, {
|
|
30
|
+
credentials: 'include',
|
|
31
|
+
...options,
|
|
32
|
+
headers,
|
|
33
|
+
signal: controller.signal,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
if (err.name === 'AbortError') {
|
|
38
|
+
throw new errors_1.NonsError(errors_1.ErrorCode.NETWORK_ERROR, 'درخواست به timeout رسید', 0);
|
|
39
|
+
}
|
|
40
|
+
throw new errors_1.NonsError(errors_1.ErrorCode.NETWORK_ERROR, 'خطا در اتصال به سرور', 0);
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
clearTimeout(timeoutId);
|
|
44
|
+
}
|
|
45
|
+
if (response.status === 401) {
|
|
46
|
+
const newToken = await this.tokenManager.refresh(this.baseUrl);
|
|
47
|
+
if (newToken) {
|
|
48
|
+
headers['Authorization'] = `Bearer ${newToken}`;
|
|
49
|
+
response = await fetch(`${this.baseUrl}${path}`, { credentials: 'include', ...options, headers });
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
if (this.onUnauthorized) {
|
|
53
|
+
this.onUnauthorized();
|
|
54
|
+
}
|
|
55
|
+
throw new errors_1.NonsError(errors_1.ErrorCode.UNAUTHORIZED, 'لطفاً دوباره وارد شوید', 401);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (response.ok) {
|
|
59
|
+
if (response.status === 204)
|
|
60
|
+
return undefined;
|
|
61
|
+
return response.json();
|
|
62
|
+
}
|
|
63
|
+
throw await (0, errors_1.parseErrorResponse)(response);
|
|
64
|
+
}
|
|
65
|
+
get(path, params) {
|
|
66
|
+
const url = params ? `${path}?${new URLSearchParams(params)}` : path;
|
|
67
|
+
return this.request(url, { method: 'GET' });
|
|
68
|
+
}
|
|
69
|
+
post(path, body) {
|
|
70
|
+
return this.request(path, { method: 'POST', body: JSON.stringify(body) });
|
|
71
|
+
}
|
|
72
|
+
put(path, body) {
|
|
73
|
+
return this.request(path, { method: 'PUT', body: JSON.stringify(body) });
|
|
74
|
+
}
|
|
75
|
+
patch(path, body) {
|
|
76
|
+
return this.request(path, { method: 'PATCH', body: JSON.stringify(body) });
|
|
77
|
+
}
|
|
78
|
+
delete(path) {
|
|
79
|
+
return this.request(path, { method: 'DELETE' });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
exports.HttpClient = HttpClient;
|
|
83
|
+
//# sourceMappingURL=http.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/core/http.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { StorageAdapter } from '../types/config.types';
|
|
2
|
+
export declare class TokenManager {
|
|
3
|
+
private storage;
|
|
4
|
+
constructor(storage: StorageAdapter);
|
|
5
|
+
getAccessToken(): Promise<string | null>;
|
|
6
|
+
setTokens(accessToken: string, refreshToken: string): Promise<void>;
|
|
7
|
+
clearTokens(): Promise<void>;
|
|
8
|
+
refresh(baseUrl: string): Promise<string | null>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TokenManager = void 0;
|
|
4
|
+
class TokenManager {
|
|
5
|
+
storage;
|
|
6
|
+
constructor(storage) {
|
|
7
|
+
this.storage = storage;
|
|
8
|
+
}
|
|
9
|
+
async getAccessToken() {
|
|
10
|
+
return this.storage.getAccessToken();
|
|
11
|
+
}
|
|
12
|
+
async setTokens(accessToken, refreshToken) {
|
|
13
|
+
await this.storage.setAccessToken(accessToken);
|
|
14
|
+
await this.storage.setRefreshToken(refreshToken);
|
|
15
|
+
}
|
|
16
|
+
async clearTokens() {
|
|
17
|
+
await this.storage.clearTokens();
|
|
18
|
+
}
|
|
19
|
+
async refresh(baseUrl) {
|
|
20
|
+
const refreshToken = await this.storage.getRefreshToken();
|
|
21
|
+
if (!refreshToken)
|
|
22
|
+
return null;
|
|
23
|
+
try {
|
|
24
|
+
const response = await fetch(`${baseUrl}/v1/auth/refresh`, {
|
|
25
|
+
method: 'POST',
|
|
26
|
+
headers: { 'Content-Type': 'application/json' },
|
|
27
|
+
body: JSON.stringify({ refreshToken }),
|
|
28
|
+
});
|
|
29
|
+
if (!response.ok) {
|
|
30
|
+
await this.storage.clearTokens();
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
const data = await response.json();
|
|
34
|
+
await this.setTokens(data.accessToken, data.refreshToken);
|
|
35
|
+
return data.accessToken;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
await this.storage.clearTokens();
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
exports.TokenManager = TokenManager;
|
|
44
|
+
//# sourceMappingURL=token-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-manager.js","sourceRoot":"","sources":["../../src/core/token-manager.ts"],"names":[],"mappings":";;;AAEA,MAAa,YAAY;IACH;IAApB,YAAoB,OAAuB;QAAvB,YAAO,GAAP,OAAO,CAAgB;IAAG,CAAC;IAE/C,KAAK,CAAC,cAAc;QAClB,OAAO,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,WAAmB,EAAE,YAAoB;QACvD,MAAM,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC;QAC/C,MAAM,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC;IACnD,CAAC;IAED,KAAK,CAAC,WAAW;QACf,MAAM,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,OAAe;QAC3B,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC;QAC1D,IAAI,CAAC,YAAY;YAAE,OAAO,IAAI,CAAC;QAE/B,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,kBAAkB,EAAE;gBACzD,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;gBAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,YAAY,EAAE,CAAC;aACvC,CAAC,CAAC;YAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;gBACjC,OAAO,IAAI,CAAC;YACd,CAAC;YAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YACnC,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;YAC1D,OAAO,IAAI,CAAC,WAAW,CAAC;QAC1B,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;YACjC,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;CACF;AAxCD,oCAwCC"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./client"), exports);
|
|
18
|
+
__exportStar(require("./core/errors"), exports);
|
|
19
|
+
__exportStar(require("./core/token-manager"), exports);
|
|
20
|
+
__exportStar(require("./core/http"), exports);
|
|
21
|
+
__exportStar(require("./modules/auth.module"), exports);
|
|
22
|
+
__exportStar(require("./types"), exports);
|
|
23
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2CAAyB;AACzB,gDAA8B;AAC9B,uDAAqC;AACrC,8CAA4B;AAC5B,wDAAsC;AACtC,0CAAwB"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { HttpClient } from '../core/http';
|
|
2
|
+
export declare class AuthModule {
|
|
3
|
+
private http;
|
|
4
|
+
private baseUrl;
|
|
5
|
+
constructor(http: HttpClient, baseUrl: string);
|
|
6
|
+
initializeLoginFlow(): Promise<{
|
|
7
|
+
id: string;
|
|
8
|
+
ui: any;
|
|
9
|
+
}>;
|
|
10
|
+
sendMagicCode(flowId: string, email: string): Promise<any>;
|
|
11
|
+
verifyMagicCode(flowId: string, code: string): Promise<any>;
|
|
12
|
+
getGoogleLoginUrl(loginChallenge: string): string;
|
|
13
|
+
getCurrentSession(): Promise<any>;
|
|
14
|
+
getAuthStatus(): Promise<{
|
|
15
|
+
authenticated: boolean;
|
|
16
|
+
email: string | null;
|
|
17
|
+
name: string | null;
|
|
18
|
+
identityId: string | null;
|
|
19
|
+
}>;
|
|
20
|
+
logout(logoutChallenge: string): Promise<void>;
|
|
21
|
+
isOnboardingRequired(session: any): boolean;
|
|
22
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AuthModule = void 0;
|
|
4
|
+
class AuthModule {
|
|
5
|
+
http;
|
|
6
|
+
baseUrl;
|
|
7
|
+
constructor(http, baseUrl) {
|
|
8
|
+
this.http = http;
|
|
9
|
+
this.baseUrl = baseUrl;
|
|
10
|
+
}
|
|
11
|
+
async initializeLoginFlow() {
|
|
12
|
+
return this.http.get('/v1/auth/kratos/self-service/login/api');
|
|
13
|
+
}
|
|
14
|
+
async sendMagicCode(flowId, email) {
|
|
15
|
+
return this.http.post(`/v1/auth/kratos/self-service/login?flow=${flowId}`, {
|
|
16
|
+
method: 'code',
|
|
17
|
+
identifier: email,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
async verifyMagicCode(flowId, code) {
|
|
21
|
+
return this.http.post(`/v1/auth/kratos/self-service/login?flow=${flowId}`, {
|
|
22
|
+
method: 'code',
|
|
23
|
+
code: code,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
getGoogleLoginUrl(loginChallenge) {
|
|
27
|
+
const postLoginUrl = `${this.baseUrl}/v1/auth/post-login?login_challenge=${encodeURIComponent(loginChallenge)}`;
|
|
28
|
+
return `${this.baseUrl}/v1/auth/kratos/self-service/login/browser?provider=google&return_to=${encodeURIComponent(postLoginUrl)}`;
|
|
29
|
+
}
|
|
30
|
+
async getCurrentSession() {
|
|
31
|
+
return this.http.get('/v1/auth/kratos/sessions/whoami');
|
|
32
|
+
}
|
|
33
|
+
async getAuthStatus() {
|
|
34
|
+
return this.http.get('/v1/auth');
|
|
35
|
+
}
|
|
36
|
+
async logout(logoutChallenge) {
|
|
37
|
+
await this.http.get(`/v1/auth/logout?logout_challenge=${encodeURIComponent(logoutChallenge)}`);
|
|
38
|
+
}
|
|
39
|
+
isOnboardingRequired(session) {
|
|
40
|
+
const onboarded = session?.identity?.traits?.onboarded;
|
|
41
|
+
return onboarded !== true;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
exports.AuthModule = AuthModule;
|
|
45
|
+
//# sourceMappingURL=auth.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.module.js","sourceRoot":"","sources":["../../src/modules/auth.module.ts"],"names":[],"mappings":";;;AAEA,MAAa,UAAU;IAGD;IAFZ,OAAO,CAAS;IAExB,YAAoB,IAAgB,EAAE,OAAe;QAAjC,SAAI,GAAJ,IAAI,CAAY;QAClC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,mBAAmB;QACvB,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAA0B,wCAAwC,CAAC,CAAC;IAC1F,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,MAAc,EAAE,KAAa;QAC/C,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAM,2CAA2C,MAAM,EAAE,EAAE;YAC9E,MAAM,EAAE,MAAM;YACd,UAAU,EAAE,KAAK;SAClB,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,MAAc,EAAE,IAAY;QAChD,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAM,2CAA2C,MAAM,EAAE,EAAE;YAC9E,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,IAAI;SACX,CAAC,CAAC;IACL,CAAC;IAED,iBAAiB,CAAC,cAAsB;QACtC,MAAM,YAAY,GAAG,GAAG,IAAI,CAAC,OAAO,uCAAuC,kBAAkB,CAAC,cAAc,CAAC,EAAE,CAAC;QAChH,OAAO,GAAG,IAAI,CAAC,OAAO,wEAAwE,kBAAkB,CAAC,YAAY,CAAC,EAAE,CAAC;IACnI,CAAC;IAED,KAAK,CAAC,iBAAiB;QACrB,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAM,iCAAiC,CAAC,CAAC;IAC/D,CAAC;IAED,KAAK,CAAC,aAAa;QACjB,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAmG,UAAU,CAAC,CAAC;IACrI,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,eAAuB;QAClC,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,oCAAoC,kBAAkB,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;IACjG,CAAC;IAED,oBAAoB,CAAC,OAAY;QAC/B,MAAM,SAAS,GAAG,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAC;QACvD,OAAO,SAAS,KAAK,IAAI,CAAC;IAC5B,CAAC;CACF;AA9CD,gCA8CC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface StorageAdapter {
|
|
2
|
+
getAccessToken(): string | null | Promise<string | null>;
|
|
3
|
+
setAccessToken(token: string): void | Promise<void>;
|
|
4
|
+
getRefreshToken(): string | null | Promise<string | null>;
|
|
5
|
+
setRefreshToken(token: string): void | Promise<void>;
|
|
6
|
+
clearTokens(): void | Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
export interface NonsClientConfig {
|
|
9
|
+
baseUrl: string;
|
|
10
|
+
storage?: StorageAdapter;
|
|
11
|
+
timeout?: number;
|
|
12
|
+
onUnauthorized?: () => void;
|
|
13
|
+
}
|
|
14
|
+
export interface HttpClientConfig {
|
|
15
|
+
baseUrl: string;
|
|
16
|
+
timeout: number;
|
|
17
|
+
tokenManager: any;
|
|
18
|
+
onUnauthorized?: () => void;
|
|
19
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.types.js","sourceRoot":"","sources":["../../src/types/config.types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './config.types';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./config.types"), exports);
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,iDAA+B"}
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nons-dev/sdk-react",
|
|
3
|
+
"version": "1.0.3",
|
|
4
|
+
"description": "NONS React/Frontend Authentication SDK",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/nons-dev/sdk-react.git"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"test": "vitest run"
|
|
17
|
+
},
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"access": "public",
|
|
20
|
+
"registry": "https://registry.npmjs.org"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/node": "^20.14.9",
|
|
24
|
+
"typescript": "^5.4.5",
|
|
25
|
+
"vitest": "^1.6.0"
|
|
26
|
+
}
|
|
27
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# NONS React Authentication SDK
|
|
2
|
+
`@nons/sdk-react`
|
|
3
|
+
|
|
4
|
+
Official React/Frontend Authentication SDK for the NONS Platform, built to interface seamlessly with Ory Kratos, Ory Hydra, and the API Gateway bridge.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 📋 Table of Contents
|
|
9
|
+
1. [Installation Guide](#-installation-guide)
|
|
10
|
+
2. [SDK Usage Guide](#-sdk-usage-guide)
|
|
11
|
+
3. [Development Guide](#-development-guide)
|
|
12
|
+
4. [Release Guide](#-release-guide)
|
|
13
|
+
5. [Consumer Setup](#-consumer-setup)
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 🚀 Installation Guide
|
|
18
|
+
|
|
19
|
+
Install the package directly from GitHub Packages:
|
|
20
|
+
|
|
21
|
+
### Using NPM
|
|
22
|
+
```bash
|
|
23
|
+
npm install @nons/sdk-react
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Using PNPM
|
|
27
|
+
```bash
|
|
28
|
+
pnpm add @nons/sdk-react
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
To configure GitHub Packages registry, add the following to your `.npmrc` file:
|
|
32
|
+
```ini
|
|
33
|
+
@nons:registry=https://npm.pkg.github.com
|
|
34
|
+
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## 📖 SDK Usage Guide
|
|
40
|
+
|
|
41
|
+
### 1. Initialize the Client
|
|
42
|
+
Instantiate `NonsClient` with your API Gateway bridge endpoint and a `StorageAdapter`:
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
import { NonsClient, StorageAdapter } from "@nons/sdk-react";
|
|
46
|
+
|
|
47
|
+
class LocalStorageStorageAdapter implements StorageAdapter {
|
|
48
|
+
getAccessToken() { return localStorage.getItem("access_token"); }
|
|
49
|
+
setAccessToken(token: string) { localStorage.setItem("access_token", token); }
|
|
50
|
+
getRefreshToken() { return localStorage.getItem("refresh_token"); }
|
|
51
|
+
setRefreshToken(token: string) { localStorage.setItem("refresh_token", token); }
|
|
52
|
+
clearTokens() {
|
|
53
|
+
localStorage.removeItem("access_token");
|
|
54
|
+
localStorage.removeItem("refresh_token");
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const nonsClient = new NonsClient({
|
|
59
|
+
baseUrl: "https://api.nons.ir",
|
|
60
|
+
storage: new LocalStorageStorageAdapter(),
|
|
61
|
+
timeout: 10000,
|
|
62
|
+
});
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 2. Magic Code Flow
|
|
66
|
+
|
|
67
|
+
#### Step A: Initialize Flow & Request Code
|
|
68
|
+
```typescript
|
|
69
|
+
const flow = await nonsClient.auth.initializeLoginFlow();
|
|
70
|
+
const flowId = flow.id; // Save this to submit with verification code
|
|
71
|
+
|
|
72
|
+
await nonsClient.auth.sendMagicCode(flowId, "user@example.com");
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
#### Step B: Submit Verification Code (OTP)
|
|
76
|
+
```typescript
|
|
77
|
+
const session = await nonsClient.auth.verifyMagicCode(flowId, "123456");
|
|
78
|
+
const onboardingRequired = nonsClient.auth.isOnboardingRequired(session);
|
|
79
|
+
|
|
80
|
+
if (onboardingRequired) {
|
|
81
|
+
// Redirect to Onboarding profile form
|
|
82
|
+
} else {
|
|
83
|
+
// Login successful
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### 3. Google OIDC Login
|
|
88
|
+
Construct the browser redirection URL and navigate the user to complete Google Consent verification:
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
const loginChallenge = "hydra_login_challenge_from_query_params";
|
|
92
|
+
const googleLoginUrl = nonsClient.auth.getGoogleLoginUrl(loginChallenge);
|
|
93
|
+
|
|
94
|
+
// Redirect the browser
|
|
95
|
+
window.location.assign(googleLoginUrl);
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### 4. Session & Logout
|
|
99
|
+
Check the active session or logout:
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
// Get Kratos Session
|
|
103
|
+
const session = await nonsClient.auth.getCurrentSession();
|
|
104
|
+
|
|
105
|
+
// Logout
|
|
106
|
+
const logoutChallenge = "hydra_logout_challenge";
|
|
107
|
+
await nonsClient.auth.logout(logoutChallenge);
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## 🛠️ Development Guide
|
|
113
|
+
|
|
114
|
+
### Install Dependencies
|
|
115
|
+
```bash
|
|
116
|
+
pnpm install
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Run Tests
|
|
120
|
+
The test suite utilizes `Vitest` to validate all authentication methods, error parsing, and timeout behaviors:
|
|
121
|
+
```bash
|
|
122
|
+
pnpm test
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Build the Package
|
|
126
|
+
Compiles TypeScript files and exports types to the `dist` directory:
|
|
127
|
+
```bash
|
|
128
|
+
pnpm run build
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## 📦 Release Guide
|
|
134
|
+
|
|
135
|
+
The package is versioned and published independently:
|
|
136
|
+
|
|
137
|
+
1. **Increment Version:** Update `version` in `package.json` according to semver.
|
|
138
|
+
2. **Commit and Tag:** Commit the changes and create a git tag matching the version:
|
|
139
|
+
```bash
|
|
140
|
+
git add package.json
|
|
141
|
+
git commit -m "chore: release v1.0.0"
|
|
142
|
+
git tag v1.0.0
|
|
143
|
+
```
|
|
144
|
+
3. **Publish to GitHub Packages:**
|
|
145
|
+
Pushing the tag triggers the GitHub Actions release workflow automatically. Alternatively, run:
|
|
146
|
+
```bash
|
|
147
|
+
pnpm run build
|
|
148
|
+
pnpm publish
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## 🌐 Consumer Setup
|
|
154
|
+
|
|
155
|
+
Instructions for setting up consumer projects (like `auth-ui`):
|
|
156
|
+
|
|
157
|
+
### 1. Local Development
|
|
158
|
+
To develop and test changes locally without publishing to GitHub Packages:
|
|
159
|
+
* Option A (File reference): Use a relative path dependency in `package.json`:
|
|
160
|
+
```json
|
|
161
|
+
"@nons/sdk-react": "file:../sdk-react"
|
|
162
|
+
```
|
|
163
|
+
* Option B (Symlinking): Link the package using `pnpm link`:
|
|
164
|
+
```bash
|
|
165
|
+
cd sdk-react && pnpm link --global
|
|
166
|
+
cd ../auth-ui && pnpm link --global @nons/sdk-react
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### 2. CI Pipelines
|
|
170
|
+
For GitHub Actions or other CI runners to resolve the dependency, configure a `GITHUB_TOKEN` environment variable with package read permission:
|
|
171
|
+
```yaml
|
|
172
|
+
- name: Setup Node.js
|
|
173
|
+
uses: actions/setup-node@v4
|
|
174
|
+
with:
|
|
175
|
+
node-version: '22'
|
|
176
|
+
registry-url: 'https://npm.pkg.github.com'
|
|
177
|
+
scope: '@nons'
|
|
178
|
+
env:
|
|
179
|
+
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### 3. Vercel Deployment
|
|
183
|
+
To deploy consumer applications to Vercel:
|
|
184
|
+
1. Ensure the `.npmrc` file is present in the repository root:
|
|
185
|
+
```ini
|
|
186
|
+
@nons:registry=https://npm.pkg.github.com/
|
|
187
|
+
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
|
|
188
|
+
```
|
|
189
|
+
2. Navigate to your Vercel Project Settings -> **Environment Variables**.
|
|
190
|
+
3. Create a new variable named `GITHUB_TOKEN` and paste your GitHub Personal Access Token (PAT) with `read:packages` scope as the value.
|
|
191
|
+
4. Trigger the deployment. Vercel will resolve, download, and build the project successfully.
|