routesync 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/dist/core.d.ts ADDED
@@ -0,0 +1,206 @@
1
+ import { AxiosRequestConfig, AxiosInstance, InternalAxiosRequestConfig, AxiosResponse } from 'axios';
2
+
3
+ interface ServiceConfig {
4
+ baseURL: string;
5
+ token?: string;
6
+ headers?: Record<string, string>;
7
+ timeout?: number;
8
+ retry?: RetryConfig;
9
+ cache?: boolean;
10
+ }
11
+ interface RetryConfig {
12
+ attempts: number;
13
+ delay?: number;
14
+ statusCodes?: number[];
15
+ }
16
+ interface AuthConfig {
17
+ type: 'bearer' | 'basic' | 'api-key';
18
+ token?: string;
19
+ apiKey?: string;
20
+ apiKeyHeader?: string;
21
+ }
22
+
23
+ declare class HttpClient {
24
+ private client;
25
+ constructor(config: ServiceConfig);
26
+ private setupInterceptors;
27
+ setToken(token: string): void;
28
+ removeToken(): void;
29
+ get<T>(url: string, config?: AxiosRequestConfig): Promise<T>;
30
+ post<T>(url: string, body?: any, config?: AxiosRequestConfig): Promise<T>;
31
+ put<T>(url: string, body?: any, config?: AxiosRequestConfig): Promise<T>;
32
+ patch<T>(url: string, body?: any, config?: AxiosRequestConfig): Promise<T>;
33
+ delete<T>(url: string, config?: AxiosRequestConfig): Promise<T>;
34
+ upload<T>(url: string, formData: FormData): Promise<T>;
35
+ getInstance(): AxiosInstance;
36
+ }
37
+
38
+ type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
39
+ type RouteTransform = (value: unknown) => unknown;
40
+ interface RouteTransformMap {
41
+ params?: RouteTransform;
42
+ query?: RouteTransform;
43
+ body?: RouteTransform;
44
+ request?: RouteTransform;
45
+ response?: RouteTransform;
46
+ }
47
+ type RouteMapper = RouteTransform | RouteTransformMap;
48
+ interface RouteParserSchema {
49
+ parse?: RouteTransform;
50
+ safeParse?: RouteTransform;
51
+ }
52
+ interface RouteSchemaMap {
53
+ params?: RouteSchemaValue;
54
+ query?: RouteSchemaValue;
55
+ body?: RouteSchemaValue;
56
+ request?: RouteSchemaValue;
57
+ response?: RouteSchemaValue;
58
+ }
59
+ type RouteSchemaValue = RouteTransform | RouteParserSchema;
60
+ type RouteSchema = RouteSchemaValue | RouteSchemaMap;
61
+ interface RequestOptions {
62
+ params?: Record<string, any>;
63
+ headers?: Record<string, string>;
64
+ timeout?: number;
65
+ signal?: AbortSignal;
66
+ }
67
+ interface RouteDefinition {
68
+ method: HttpMethod;
69
+ path: string;
70
+ auth?: boolean;
71
+ schema?: RouteSchema;
72
+ mapper?: RouteMapper;
73
+ headers?: Record<string, string>;
74
+ cache?: unknown;
75
+ retry?: unknown;
76
+ body?: Record<string, any>;
77
+ params?: Record<string, any>;
78
+ query?: Record<string, any>;
79
+ }
80
+ interface ApiDefinition {
81
+ [group: string]: {
82
+ [action: string]: RouteDefinition;
83
+ };
84
+ }
85
+
86
+ declare class Request {
87
+ private _url;
88
+ private _method;
89
+ private _body?;
90
+ private _options?;
91
+ url(url: string): this;
92
+ method(method: HttpMethod): this;
93
+ body(data: any): this;
94
+ options(opts: RequestOptions): this;
95
+ build(): {
96
+ url: string;
97
+ method: HttpMethod;
98
+ body: any;
99
+ options: RequestOptions | undefined;
100
+ };
101
+ }
102
+
103
+ interface ApiResponse<T = any> {
104
+ success: boolean;
105
+ message?: string;
106
+ data: T;
107
+ meta?: PaginationMeta;
108
+ }
109
+ interface PaginationMeta {
110
+ current_page: number;
111
+ last_page: number;
112
+ per_page: number;
113
+ total: number;
114
+ from?: number;
115
+ to?: number;
116
+ }
117
+
118
+ declare class Response<T = any> {
119
+ private raw;
120
+ constructor(raw: ApiResponse<T>);
121
+ get data(): T;
122
+ get success(): boolean;
123
+ get message(): string | undefined;
124
+ isOk(): boolean;
125
+ unwrap(): T;
126
+ }
127
+
128
+ type RequestInterceptor = (config: InternalAxiosRequestConfig) => InternalAxiosRequestConfig | Promise<InternalAxiosRequestConfig>;
129
+ type ResponseInterceptor = (response: AxiosResponse) => AxiosResponse | Promise<AxiosResponse>;
130
+ type ErrorInterceptor = (error: any) => any;
131
+ declare class Interceptor {
132
+ private client;
133
+ constructor(client: AxiosInstance);
134
+ addRequestInterceptor(onFulfilled: RequestInterceptor, onRejected?: ErrorInterceptor): number;
135
+ addResponseInterceptor(onFulfilled: ResponseInterceptor, onRejected?: ErrorInterceptor): number;
136
+ removeRequestInterceptor(id: number): void;
137
+ removeResponseInterceptor(id: number): void;
138
+ }
139
+
140
+ declare class TokenManager {
141
+ private static TOKEN_KEY;
142
+ private token;
143
+ set(token: string): void;
144
+ get(): string | null;
145
+ clear(): void;
146
+ exists(): boolean;
147
+ }
148
+
149
+ declare class AuthMiddleware {
150
+ private tokenManager;
151
+ constructor(tokenManager: TokenManager);
152
+ inject(config: InternalAxiosRequestConfig): InternalAxiosRequestConfig;
153
+ getAuthHeader(): Record<string, string>;
154
+ }
155
+
156
+ declare class PathResolver {
157
+ /**
158
+ * Resolve path params.
159
+ * e.g. resolvePath('/produk/:id', { id: 10 }) => '/produk/10'
160
+ */
161
+ static resolve(path: string, params?: Record<string, any>): string;
162
+ static extractParams(path: string): string[];
163
+ static hasParams(path: string): boolean;
164
+ }
165
+
166
+ declare class QueryBuilder {
167
+ static build(params?: Record<string, any>): string;
168
+ static append(url: string, params?: Record<string, any>): string;
169
+ }
170
+
171
+ declare class ApiError extends Error {
172
+ readonly status?: number;
173
+ readonly errors?: Record<string, string[]>;
174
+ readonly success = false;
175
+ constructor(message: string, status?: number, errors?: Record<string, string[]>);
176
+ isUnauthorized(): boolean;
177
+ isForbidden(): boolean;
178
+ isNotFound(): boolean;
179
+ isValidationError(): boolean;
180
+ isServerError(): boolean;
181
+ getFirstError(field: string): string | undefined;
182
+ getAllErrors(): string[];
183
+ }
184
+
185
+ declare class ErrorHandler {
186
+ static handle(error: any): never;
187
+ static isApiError(error: unknown): error is ApiError;
188
+ }
189
+
190
+ interface RouteManifest {
191
+ version: string;
192
+ baseURL: string;
193
+ routes: ParsedRoute[];
194
+ generatedAt: string;
195
+ }
196
+ interface ParsedRoute {
197
+ name: string;
198
+ method: string;
199
+ path: string;
200
+ auth: boolean;
201
+ middleware: string[];
202
+ group?: string;
203
+ action?: string;
204
+ }
205
+
206
+ export { type ApiDefinition, ApiError, type ApiResponse, type AuthConfig, AuthMiddleware, ErrorHandler, HttpClient, type HttpMethod, Interceptor, type PaginationMeta, type ParsedRoute, PathResolver, QueryBuilder, Request, type RequestOptions, Response, type RetryConfig, type RouteDefinition, type RouteManifest, type RouteMapper, type RouteParserSchema, type RouteSchema, type RouteSchemaMap, type RouteSchemaValue, type RouteTransform, type RouteTransformMap, type ServiceConfig, TokenManager };
package/dist/core.js ADDED
@@ -0,0 +1,353 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // packages/core/src/index.ts
31
+ var src_exports = {};
32
+ __export(src_exports, {
33
+ ApiError: () => ApiError,
34
+ AuthMiddleware: () => AuthMiddleware,
35
+ ErrorHandler: () => ErrorHandler,
36
+ HttpClient: () => HttpClient,
37
+ Interceptor: () => Interceptor,
38
+ PathResolver: () => PathResolver,
39
+ QueryBuilder: () => QueryBuilder,
40
+ Request: () => Request,
41
+ Response: () => Response,
42
+ TokenManager: () => TokenManager
43
+ });
44
+ module.exports = __toCommonJS(src_exports);
45
+
46
+ // packages/core/src/client/HttpClient.ts
47
+ var import_axios = __toESM(require("axios"));
48
+ var HttpClient = class {
49
+ constructor(config) {
50
+ this.client = import_axios.default.create({
51
+ baseURL: config.baseURL,
52
+ timeout: config.timeout ?? 1e4,
53
+ headers: {
54
+ "Content-Type": "application/json",
55
+ ...config.headers ?? {}
56
+ }
57
+ });
58
+ if (config.token) {
59
+ this.setToken(config.token);
60
+ }
61
+ this.setupInterceptors();
62
+ }
63
+ setupInterceptors() {
64
+ this.client.interceptors.response.use(
65
+ (response) => response,
66
+ (error) => {
67
+ const message = error.response?.data?.message ?? error.message ?? "Unknown error";
68
+ return Promise.reject({
69
+ success: false,
70
+ message,
71
+ status: error.response?.status,
72
+ errors: error.response?.data?.errors
73
+ });
74
+ }
75
+ );
76
+ }
77
+ setToken(token) {
78
+ this.client.defaults.headers.common["Authorization"] = `Bearer ${token}`;
79
+ }
80
+ removeToken() {
81
+ delete this.client.defaults.headers.common["Authorization"];
82
+ }
83
+ async get(url, config) {
84
+ const response = await this.client.get(url, config);
85
+ return response.data;
86
+ }
87
+ async post(url, body, config) {
88
+ const response = await this.client.post(url, body, config);
89
+ return response.data;
90
+ }
91
+ async put(url, body, config) {
92
+ const response = await this.client.put(url, body, config);
93
+ return response.data;
94
+ }
95
+ async patch(url, body, config) {
96
+ const response = await this.client.patch(url, body, config);
97
+ return response.data;
98
+ }
99
+ async delete(url, config) {
100
+ const response = await this.client.delete(url, config);
101
+ return response.data;
102
+ }
103
+ async upload(url, formData) {
104
+ const response = await this.client.post(url, formData, {
105
+ headers: { "Content-Type": "multipart/form-data" }
106
+ });
107
+ return response.data;
108
+ }
109
+ getInstance() {
110
+ return this.client;
111
+ }
112
+ };
113
+
114
+ // packages/core/src/client/Request.ts
115
+ var Request = class {
116
+ constructor() {
117
+ this._url = "";
118
+ this._method = "GET";
119
+ }
120
+ url(url) {
121
+ this._url = url;
122
+ return this;
123
+ }
124
+ method(method) {
125
+ this._method = method;
126
+ return this;
127
+ }
128
+ body(data) {
129
+ this._body = data;
130
+ return this;
131
+ }
132
+ options(opts) {
133
+ this._options = opts;
134
+ return this;
135
+ }
136
+ build() {
137
+ return {
138
+ url: this._url,
139
+ method: this._method,
140
+ body: this._body,
141
+ options: this._options
142
+ };
143
+ }
144
+ };
145
+
146
+ // packages/core/src/client/Response.ts
147
+ var Response = class {
148
+ constructor(raw) {
149
+ this.raw = raw;
150
+ }
151
+ get data() {
152
+ return this.raw.data;
153
+ }
154
+ get success() {
155
+ return this.raw.success;
156
+ }
157
+ get message() {
158
+ return this.raw.message;
159
+ }
160
+ isOk() {
161
+ return this.raw.success === true;
162
+ }
163
+ unwrap() {
164
+ if (!this.isOk()) {
165
+ throw new Error(this.raw.message ?? "Response failed");
166
+ }
167
+ return this.raw.data;
168
+ }
169
+ };
170
+
171
+ // packages/core/src/client/Interceptor.ts
172
+ var Interceptor = class {
173
+ constructor(client) {
174
+ this.client = client;
175
+ }
176
+ addRequestInterceptor(onFulfilled, onRejected) {
177
+ return this.client.interceptors.request.use(onFulfilled, onRejected);
178
+ }
179
+ addResponseInterceptor(onFulfilled, onRejected) {
180
+ return this.client.interceptors.response.use(onFulfilled, onRejected);
181
+ }
182
+ removeRequestInterceptor(id) {
183
+ this.client.interceptors.request.eject(id);
184
+ }
185
+ removeResponseInterceptor(id) {
186
+ this.client.interceptors.response.eject(id);
187
+ }
188
+ };
189
+
190
+ // packages/core/src/auth/TokenManager.ts
191
+ var _TokenManager = class _TokenManager {
192
+ constructor() {
193
+ this.token = null;
194
+ }
195
+ set(token) {
196
+ this.token = token;
197
+ if (typeof localStorage !== "undefined") {
198
+ localStorage.setItem(_TokenManager.TOKEN_KEY, token);
199
+ }
200
+ }
201
+ get() {
202
+ if (this.token) return this.token;
203
+ if (typeof localStorage !== "undefined") {
204
+ return localStorage.getItem(_TokenManager.TOKEN_KEY);
205
+ }
206
+ return null;
207
+ }
208
+ clear() {
209
+ this.token = null;
210
+ if (typeof localStorage !== "undefined") {
211
+ localStorage.removeItem(_TokenManager.TOKEN_KEY);
212
+ }
213
+ }
214
+ exists() {
215
+ return this.get() !== null;
216
+ }
217
+ };
218
+ _TokenManager.TOKEN_KEY = "routesync_token";
219
+ var TokenManager = _TokenManager;
220
+
221
+ // packages/core/src/auth/AuthMiddleware.ts
222
+ var AuthMiddleware = class {
223
+ constructor(tokenManager) {
224
+ this.tokenManager = tokenManager;
225
+ }
226
+ inject(config) {
227
+ const token = this.tokenManager.get();
228
+ if (token) {
229
+ config.headers = config.headers ?? {};
230
+ config.headers["Authorization"] = `Bearer ${token}`;
231
+ }
232
+ return config;
233
+ }
234
+ getAuthHeader() {
235
+ const token = this.tokenManager.get();
236
+ if (!token) return {};
237
+ return { Authorization: `Bearer ${token}` };
238
+ }
239
+ };
240
+
241
+ // packages/core/src/routing/PathResolver.ts
242
+ var PathResolver = class {
243
+ /**
244
+ * Resolve path params.
245
+ * e.g. resolvePath('/produk/:id', { id: 10 }) => '/produk/10'
246
+ */
247
+ static resolve(path, params) {
248
+ if (!params) return path;
249
+ let resolved = path;
250
+ for (const [key, value] of Object.entries(params)) {
251
+ resolved = resolved.replace(`:${key}`, String(value));
252
+ }
253
+ const unresolved = resolved.match(/:([a-zA-Z_]+)/g);
254
+ if (unresolved) {
255
+ throw new Error(
256
+ `Unresolved path params: ${unresolved.join(", ")}`
257
+ );
258
+ }
259
+ return resolved;
260
+ }
261
+ static extractParams(path) {
262
+ const matches = path.match(/:([a-zA-Z_]+)/g) ?? [];
263
+ return matches.map((m) => m.slice(1));
264
+ }
265
+ static hasParams(path) {
266
+ return /:([a-zA-Z_]+)/.test(path);
267
+ }
268
+ };
269
+
270
+ // packages/core/src/routing/QueryBuilder.ts
271
+ var QueryBuilder = class {
272
+ static build(params) {
273
+ if (!params || Object.keys(params).length === 0) return "";
274
+ const query = Object.entries(params).filter(([, v]) => v !== void 0 && v !== null && v !== "").map(([k, v]) => {
275
+ if (Array.isArray(v)) {
276
+ return v.map((item) => `${encodeURIComponent(k)}[]=${encodeURIComponent(item)}`).join("&");
277
+ }
278
+ return `${encodeURIComponent(k)}=${encodeURIComponent(v)}`;
279
+ }).join("&");
280
+ return query ? `?${query}` : "";
281
+ }
282
+ static append(url, params) {
283
+ const query = this.build(params);
284
+ return url + query;
285
+ }
286
+ };
287
+
288
+ // packages/core/src/errors/ApiError.ts
289
+ var ApiError = class extends Error {
290
+ constructor(message, status, errors) {
291
+ super(message);
292
+ this.success = false;
293
+ this.name = "ApiError";
294
+ this.status = status;
295
+ this.errors = errors;
296
+ }
297
+ isUnauthorized() {
298
+ return this.status === 401;
299
+ }
300
+ isForbidden() {
301
+ return this.status === 403;
302
+ }
303
+ isNotFound() {
304
+ return this.status === 404;
305
+ }
306
+ isValidationError() {
307
+ return this.status === 422;
308
+ }
309
+ isServerError() {
310
+ return (this.status ?? 0) >= 500;
311
+ }
312
+ getFirstError(field) {
313
+ return this.errors?.[field]?.[0];
314
+ }
315
+ getAllErrors() {
316
+ if (!this.errors) return [];
317
+ return Object.values(this.errors).flat();
318
+ }
319
+ };
320
+
321
+ // packages/core/src/errors/ErrorHandler.ts
322
+ var ErrorHandler = class {
323
+ static handle(error) {
324
+ if (error instanceof ApiError) throw error;
325
+ if (error?.response) {
326
+ throw new ApiError(
327
+ error.response.data?.message ?? "Request failed",
328
+ error.response.status,
329
+ error.response.data?.errors
330
+ );
331
+ }
332
+ if (error?.request) {
333
+ throw new ApiError("No response received from server");
334
+ }
335
+ throw new ApiError(error?.message ?? "Unknown error occurred");
336
+ }
337
+ static isApiError(error) {
338
+ return error instanceof ApiError;
339
+ }
340
+ };
341
+ // Annotate the CommonJS export names for ESM import in node:
342
+ 0 && (module.exports = {
343
+ ApiError,
344
+ AuthMiddleware,
345
+ ErrorHandler,
346
+ HttpClient,
347
+ Interceptor,
348
+ PathResolver,
349
+ QueryBuilder,
350
+ Request,
351
+ Response,
352
+ TokenManager
353
+ });