@meago/core 0.2.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,65 @@
1
+ # @meago/core
2
+
3
+ Core TypeScript dùng chung cho MeagoServer, MeagoClient và các dự án Meago tiếp theo. Package cung cấp contract ổn định cùng các primitive nền tảng, không phụ thuộc NestJS, React, TypeORM, Redis hoặc domain nghiệp vụ cụ thể.
4
+
5
+ ## Phạm vi
6
+
7
+ - Response, error và pagination contract.
8
+ - Auth contract dùng chung cho JWT và stateful session.
9
+ - Result và transport-neutral error.
10
+ - Permission helpers.
11
+ - Clock, ID, hash và transaction ports.
12
+ - API path và response factory.
13
+
14
+ Framework adapter thuộc package hoặc layer khác. Không đưa controller, entity, guard hay React hook vào package này.
15
+
16
+ ## Ví dụ
17
+
18
+ ```ts
19
+ import {
20
+ AuthMode,
21
+ normalizePagination,
22
+ ok,
23
+ type AuthPrincipal,
24
+ type Result,
25
+ } from '@meago/core';
26
+
27
+ const pagination = normalizePagination({ page: 1, limit: 200 }, { maxLimit: 100 });
28
+ const principal: AuthPrincipal = { subjectId: 'user-id', sessionId: 'session-id' };
29
+ const result: Result<string, Error> = ok(`${principal.subjectId}:${AuthMode.SESSION}`);
30
+ ```
31
+
32
+ ## Phát triển
33
+
34
+ ```bash
35
+ npm install
36
+ npm run verify
37
+ ```
38
+
39
+ `verify` chạy strict type-check, build ESM/CJS/declaration, smoke test và kiểm tra nội dung tarball.
40
+
41
+ ## Dùng local
42
+
43
+ ```bash
44
+ npm install ../MeagoLibrary
45
+ ```
46
+
47
+ Consumer chỉ import từ `@meago/core`, không import `@meago/core/dist/...`.
48
+
49
+ ## Phát hành
50
+
51
+ 1. Chạy `npm run verify`.
52
+ 2. Cập nhật `CHANGELOG.md`.
53
+ 3. Tăng version theo semver.
54
+ 4. Chạy `npm publish` hoặc `npm run pub`.
55
+ 5. Nâng cùng version ở Server và Client, sau đó chạy test của cả hai repo.
56
+
57
+ Package đang cấu hình `access: restricted`; registry/account deploy phải hỗ trợ scoped private package. Đổi sang `public` nếu chủ đích phát hành công khai.
58
+
59
+ ## Compatibility
60
+
61
+ - Patch: không đổi public contract hoặc behavior.
62
+ - Minor: thêm export hoặc field optional.
63
+ - Major: xóa/đổi export, thêm field required hoặc đổi semantics.
64
+ - Entity database không phải contract và không được export từ library.
65
+
@@ -0,0 +1,417 @@
1
+ /** URL compose từ constant — cả FE lẫn tài liệu API dùng chung một nguồn. */
2
+ declare const API_VERSION = "/api/v1";
3
+ declare const API_CONTROLLERS: {
4
+ readonly AUTH: "auth";
5
+ readonly USERS: "users";
6
+ readonly ROLES: "roles";
7
+ readonly STORIES: "stories";
8
+ };
9
+ declare const API_ACTIONS: {
10
+ readonly LOGIN: "login";
11
+ readonly REGISTER: "register";
12
+ readonly REFRESH: "refresh";
13
+ readonly LOGOUT: "logout";
14
+ readonly LOGOUT_ALL: "logout-all";
15
+ readonly ME: "me";
16
+ };
17
+ /**
18
+ * Permission string "resource:action" — nguồn chân lý duy nhất,
19
+ * BE dùng trong @RequirePermissions, FE dùng trong PermissionGuard/usePermission,
20
+ * seed script dùng để tạo bảng permissions.
21
+ */
22
+ declare const PERMISSIONS: {
23
+ readonly USER: {
24
+ readonly READ: "user:read";
25
+ readonly MANAGE: "user:manage";
26
+ };
27
+ readonly ROLE: {
28
+ readonly READ: "role:read";
29
+ readonly MANAGE: "role:manage";
30
+ };
31
+ readonly STORY: {
32
+ readonly CREATE: "story:create";
33
+ readonly READ: "story:read";
34
+ readonly MANAGE: "story:manage";
35
+ };
36
+ };
37
+
38
+ declare const index$b_API_ACTIONS: typeof API_ACTIONS;
39
+ declare const index$b_API_CONTROLLERS: typeof API_CONTROLLERS;
40
+ declare const index$b_API_VERSION: typeof API_VERSION;
41
+ declare const index$b_PERMISSIONS: typeof PERMISSIONS;
42
+ declare namespace index$b {
43
+ export { index$b_API_ACTIONS as API_ACTIONS, index$b_API_CONTROLLERS as API_CONTROLLERS, index$b_API_VERSION as API_VERSION, index$b_PERMISSIONS as PERMISSIONS };
44
+ }
45
+
46
+ /** Trạng thái user — mirror EUserStatus của MeagoServer (user.entity.ts) */
47
+ declare enum EUserStatus {
48
+ ACTIVE = "active",
49
+ BLOCKED = "blocked"
50
+ }
51
+ declare enum ESortDir {
52
+ ASC = "ASC",
53
+ DESC = "DESC"
54
+ }
55
+
56
+ type index$a_ESortDir = ESortDir;
57
+ declare const index$a_ESortDir: typeof ESortDir;
58
+ type index$a_EUserStatus = EUserStatus;
59
+ declare const index$a_EUserStatus: typeof EUserStatus;
60
+ declare namespace index$a {
61
+ export { index$a_ESortDir as ESortDir, index$a_EUserStatus as EUserStatus };
62
+ }
63
+
64
+ interface ILoginDto {
65
+ email: string;
66
+ password: string;
67
+ }
68
+ interface IRegisterDto extends ILoginDto {
69
+ displayName: string;
70
+ }
71
+ interface ITokenResponse {
72
+ accessToken: string;
73
+ expiresIn?: number;
74
+ }
75
+ /** Minimal interoperable access JWT payload. */
76
+ interface IJwtPayload {
77
+ sub: string;
78
+ sid: string;
79
+ jti: string;
80
+ iss: string;
81
+ aud: string | string[];
82
+ iat: number;
83
+ exp: number;
84
+ email?: string;
85
+ }
86
+
87
+ type index$9_IJwtPayload = IJwtPayload;
88
+ type index$9_ILoginDto = ILoginDto;
89
+ type index$9_IRegisterDto = IRegisterDto;
90
+ type index$9_ITokenResponse = ITokenResponse;
91
+ declare namespace index$9 {
92
+ export type { index$9_IJwtPayload as IJwtPayload, index$9_ILoginDto as ILoginDto, index$9_IRegisterDto as IRegisterDto, index$9_ITokenResponse as ITokenResponse };
93
+ }
94
+
95
+ interface IBaseResponse<T> {
96
+ statusCode: number;
97
+ message: string;
98
+ data: T;
99
+ timestamp: string;
100
+ }
101
+ interface IErrorResponse<TDetails = unknown> {
102
+ statusCode: number;
103
+ error: string;
104
+ message: string | string[];
105
+ path: string;
106
+ timestamp: string;
107
+ code?: string;
108
+ details?: TDetails;
109
+ }
110
+ interface IPaginatedResult<T> {
111
+ items: T[];
112
+ totalItems: number;
113
+ page: number;
114
+ limit: number;
115
+ totalPages?: number;
116
+ hasNextPage?: boolean;
117
+ hasPreviousPage?: boolean;
118
+ }
119
+ interface IBaseQuery<TSortBy extends string = string> {
120
+ page?: number;
121
+ limit?: number;
122
+ sortBy?: TSortBy;
123
+ sortDir?: ESortDir | 'ASC' | 'DESC';
124
+ search?: string;
125
+ }
126
+ interface IBaseModel {
127
+ id: string;
128
+ version: number;
129
+ createdAt: string;
130
+ updatedAt: string | null;
131
+ }
132
+ interface ITrackingModel extends IBaseModel {
133
+ createdBy: string | null;
134
+ updatedBy: string | null;
135
+ }
136
+
137
+ type index$8_IBaseModel = IBaseModel;
138
+ type index$8_IBaseQuery<TSortBy extends string = string> = IBaseQuery<TSortBy>;
139
+ type index$8_IBaseResponse<T> = IBaseResponse<T>;
140
+ type index$8_IErrorResponse<TDetails = unknown> = IErrorResponse<TDetails>;
141
+ type index$8_IPaginatedResult<T> = IPaginatedResult<T>;
142
+ type index$8_ITrackingModel = ITrackingModel;
143
+ declare namespace index$8 {
144
+ export type { index$8_IBaseModel as IBaseModel, index$8_IBaseQuery as IBaseQuery, index$8_IBaseResponse as IBaseResponse, index$8_IErrorResponse as IErrorResponse, index$8_IPaginatedResult as IPaginatedResult, index$8_ITrackingModel as ITrackingModel };
145
+ }
146
+
147
+ /** User trả về từ GET /auth/me — kèm tập permission đã resolve. */
148
+ interface ICurrentUser {
149
+ id: string;
150
+ email: string;
151
+ displayName: string;
152
+ status: EUserStatus | string;
153
+ permissions: string[];
154
+ }
155
+ interface IUser extends IBaseModel {
156
+ email: string;
157
+ displayName: string;
158
+ status: EUserStatus | string;
159
+ }
160
+ interface IPermission extends IBaseModel {
161
+ name: string;
162
+ description?: string | null;
163
+ }
164
+ interface IRole extends IBaseModel {
165
+ name: string;
166
+ description?: string | null;
167
+ permissions: IPermission[];
168
+ }
169
+
170
+ type index$7_ICurrentUser = ICurrentUser;
171
+ type index$7_IPermission = IPermission;
172
+ type index$7_IRole = IRole;
173
+ type index$7_IUser = IUser;
174
+ declare namespace index$7 {
175
+ export type { index$7_ICurrentUser as ICurrentUser, index$7_IPermission as IPermission, index$7_IRole as IRole, index$7_IUser as IUser };
176
+ }
177
+
178
+ interface ResponseMeta {
179
+ statusCode?: number;
180
+ message?: string;
181
+ timestamp?: string;
182
+ }
183
+ declare function createSuccessResponse<T>(data: T, meta?: ResponseMeta): IBaseResponse<T>;
184
+ interface ErrorResponseInput<TDetails = unknown> extends Omit<ResponseMeta, 'message'> {
185
+ error: string;
186
+ message: string | string[];
187
+ path: string;
188
+ code?: string;
189
+ details?: TDetails;
190
+ }
191
+ declare function createErrorResponse<TDetails = unknown>(input: ErrorResponseInput<TDetails>): IErrorResponse<TDetails>;
192
+ declare function joinApiPath(...parts: ReadonlyArray<string | number | null | undefined>): string;
193
+
194
+ type index$6_ErrorResponseInput<TDetails = unknown> = ErrorResponseInput<TDetails>;
195
+ type index$6_ResponseMeta = ResponseMeta;
196
+ declare const index$6_createErrorResponse: typeof createErrorResponse;
197
+ declare const index$6_createSuccessResponse: typeof createSuccessResponse;
198
+ declare const index$6_joinApiPath: typeof joinApiPath;
199
+ declare namespace index$6 {
200
+ export { type index$6_ErrorResponseInput as ErrorResponseInput, type index$6_ResponseMeta as ResponseMeta, index$6_createErrorResponse as createErrorResponse, index$6_createSuccessResponse as createSuccessResponse, index$6_joinApiPath as joinApiPath };
201
+ }
202
+
203
+ declare const AuthMode: {
204
+ readonly JWT: "jwt";
205
+ readonly SESSION: "session";
206
+ };
207
+ type AuthMode = (typeof AuthMode)[keyof typeof AuthMode];
208
+ interface AuthPrincipal<TAttributes extends Record<string, unknown> = Record<string, unknown>> {
209
+ subjectId: string;
210
+ sessionId: string;
211
+ email?: string;
212
+ permissions?: readonly string[];
213
+ attributes?: Readonly<TAttributes>;
214
+ }
215
+ interface AuthIdentity<TAttributes extends Record<string, unknown> = Record<string, unknown>> {
216
+ subjectId: string;
217
+ email?: string;
218
+ attributes?: Readonly<TAttributes>;
219
+ }
220
+ interface AuthContext {
221
+ ip?: string;
222
+ userAgent?: string;
223
+ deviceId?: string;
224
+ deviceName?: string;
225
+ }
226
+ type AuthCredential = Readonly<{
227
+ kind: 'bearer';
228
+ token: string;
229
+ }> | Readonly<{
230
+ kind: 'refresh';
231
+ token: string;
232
+ }> | Readonly<{
233
+ kind: 'session';
234
+ sessionId: string;
235
+ }>;
236
+ type AuthResult = Readonly<{
237
+ mode: 'jwt';
238
+ principal: AuthPrincipal;
239
+ accessToken: string;
240
+ accessExpiresAt: Date;
241
+ refreshToken?: string;
242
+ refreshExpiresAt?: Date;
243
+ }> | Readonly<{
244
+ mode: 'session';
245
+ principal: AuthPrincipal;
246
+ sessionId: string;
247
+ expiresAt: Date;
248
+ }>;
249
+ interface AuthStrategy {
250
+ signIn(identity: AuthIdentity, context: AuthContext): Promise<AuthResult>;
251
+ authenticate(credential: AuthCredential, context: AuthContext): Promise<AuthPrincipal | null>;
252
+ renew(credential: AuthCredential, context: AuthContext): Promise<AuthResult>;
253
+ signOut(credential: AuthCredential, context: AuthContext): Promise<void>;
254
+ revokeAll(subjectId: string): Promise<void>;
255
+ }
256
+ interface SessionRecord<TData extends Record<string, unknown> = Record<string, unknown>> {
257
+ id: string;
258
+ subjectId: string;
259
+ createdAt: Date;
260
+ expiresAt: Date;
261
+ absoluteExpiresAt: Date;
262
+ lastSeenAt: Date;
263
+ revokedAt: Date | null;
264
+ context?: AuthContext;
265
+ data?: Readonly<TData>;
266
+ }
267
+ interface SessionStore<TData extends Record<string, unknown> = Record<string, unknown>> {
268
+ create(session: SessionRecord<TData>): Promise<void>;
269
+ findById(id: string): Promise<SessionRecord<TData> | null>;
270
+ touch(id: string, lastSeenAt: Date, expiresAt: Date): Promise<boolean>;
271
+ rotate(currentId: string, successor: SessionRecord<TData>): Promise<boolean>;
272
+ revoke(id: string, revokedAt: Date): Promise<void>;
273
+ revokeAll(subjectId: string, revokedAt: Date): Promise<void>;
274
+ }
275
+
276
+ type index$5_AuthContext = AuthContext;
277
+ type index$5_AuthCredential = AuthCredential;
278
+ type index$5_AuthIdentity<TAttributes extends Record<string, unknown> = Record<string, unknown>> = AuthIdentity<TAttributes>;
279
+ type index$5_AuthMode = AuthMode;
280
+ type index$5_AuthPrincipal<TAttributes extends Record<string, unknown> = Record<string, unknown>> = AuthPrincipal<TAttributes>;
281
+ type index$5_AuthResult = AuthResult;
282
+ type index$5_AuthStrategy = AuthStrategy;
283
+ type index$5_SessionRecord<TData extends Record<string, unknown> = Record<string, unknown>> = SessionRecord<TData>;
284
+ type index$5_SessionStore<TData extends Record<string, unknown> = Record<string, unknown>> = SessionStore<TData>;
285
+ declare namespace index$5 {
286
+ export type { index$5_AuthContext as AuthContext, index$5_AuthCredential as AuthCredential, index$5_AuthIdentity as AuthIdentity, index$5_AuthMode as AuthMode, index$5_AuthPrincipal as AuthPrincipal, index$5_AuthResult as AuthResult, index$5_AuthStrategy as AuthStrategy, index$5_SessionRecord as SessionRecord, index$5_SessionStore as SessionStore };
287
+ }
288
+
289
+ declare const CoreErrorCode: {
290
+ readonly VALIDATION: "VALIDATION";
291
+ readonly UNAUTHENTICATED: "UNAUTHENTICATED";
292
+ readonly FORBIDDEN: "FORBIDDEN";
293
+ readonly NOT_FOUND: "NOT_FOUND";
294
+ readonly CONFLICT: "CONFLICT";
295
+ readonly RATE_LIMITED: "RATE_LIMITED";
296
+ readonly UNAVAILABLE: "UNAVAILABLE";
297
+ readonly INTERNAL: "INTERNAL";
298
+ };
299
+ type CoreErrorCode = (typeof CoreErrorCode)[keyof typeof CoreErrorCode];
300
+ interface CoreErrorOptions<TDetails = unknown> {
301
+ code: CoreErrorCode | (string & {});
302
+ message: string;
303
+ details?: TDetails;
304
+ cause?: unknown;
305
+ }
306
+ /** Transport-neutral error; an outer adapter maps it to HTTP or another protocol. */
307
+ declare class CoreError<TDetails = unknown> extends Error {
308
+ readonly code: CoreErrorCode | (string & {});
309
+ readonly details?: TDetails;
310
+ readonly cause?: unknown;
311
+ constructor(options: CoreErrorOptions<TDetails>);
312
+ }
313
+ declare function isCoreError(error: unknown): error is CoreError;
314
+
315
+ type index$4_CoreError<TDetails = unknown> = CoreError<TDetails>;
316
+ declare const index$4_CoreError: typeof CoreError;
317
+ type index$4_CoreErrorCode = CoreErrorCode;
318
+ type index$4_CoreErrorOptions<TDetails = unknown> = CoreErrorOptions<TDetails>;
319
+ declare const index$4_isCoreError: typeof isCoreError;
320
+ declare namespace index$4 {
321
+ export { index$4_CoreError as CoreError, type index$4_CoreErrorCode as CoreErrorCode, type index$4_CoreErrorOptions as CoreErrorOptions, index$4_isCoreError as isCoreError };
322
+ }
323
+
324
+ interface PaginationInput {
325
+ page?: number;
326
+ limit?: number;
327
+ }
328
+ interface PaginationPolicy {
329
+ defaultPage?: number;
330
+ defaultLimit?: number;
331
+ maxLimit?: number;
332
+ }
333
+ interface NormalizedPagination {
334
+ page: number;
335
+ limit: number;
336
+ }
337
+ declare function normalizePagination(input: PaginationInput, policy?: PaginationPolicy): NormalizedPagination;
338
+ declare function getPaginationOffset(pagination: NormalizedPagination): number;
339
+ declare function createPaginatedResult<T>(items: T[], totalItems: number, pagination: NormalizedPagination): IPaginatedResult<T>;
340
+
341
+ type index$3_NormalizedPagination = NormalizedPagination;
342
+ type index$3_PaginationInput = PaginationInput;
343
+ type index$3_PaginationPolicy = PaginationPolicy;
344
+ declare const index$3_createPaginatedResult: typeof createPaginatedResult;
345
+ declare const index$3_getPaginationOffset: typeof getPaginationOffset;
346
+ declare const index$3_normalizePagination: typeof normalizePagination;
347
+ declare namespace index$3 {
348
+ export { type index$3_NormalizedPagination as NormalizedPagination, type index$3_PaginationInput as PaginationInput, type index$3_PaginationPolicy as PaginationPolicy, index$3_createPaginatedResult as createPaginatedResult, index$3_getPaginationOffset as getPaginationOffset, index$3_normalizePagination as normalizePagination };
349
+ }
350
+
351
+ type PermissionCollection = ReadonlySet<string> | readonly string[];
352
+ declare function hasAllPermissions(granted: PermissionCollection, required: readonly string[]): boolean;
353
+ declare function hasAnyPermission(granted: PermissionCollection, required: readonly string[]): boolean;
354
+
355
+ type index$2_PermissionCollection = PermissionCollection;
356
+ declare const index$2_hasAllPermissions: typeof hasAllPermissions;
357
+ declare const index$2_hasAnyPermission: typeof hasAnyPermission;
358
+ declare namespace index$2 {
359
+ export { type index$2_PermissionCollection as PermissionCollection, index$2_hasAllPermissions as hasAllPermissions, index$2_hasAnyPermission as hasAnyPermission };
360
+ }
361
+
362
+ interface Clock {
363
+ now(): Date;
364
+ }
365
+ interface IdGenerator {
366
+ generate(): string;
367
+ }
368
+ interface Hasher {
369
+ hash(value: string): Promise<string> | string;
370
+ verify?(value: string, hash: string): Promise<boolean> | boolean;
371
+ }
372
+ interface TransactionRunner<TContext = unknown> {
373
+ run<T>(work: (context: TContext) => Promise<T>): Promise<T>;
374
+ }
375
+ declare const systemClock: Clock;
376
+
377
+ type index$1_Clock = Clock;
378
+ type index$1_Hasher = Hasher;
379
+ type index$1_IdGenerator = IdGenerator;
380
+ type index$1_TransactionRunner<TContext = unknown> = TransactionRunner<TContext>;
381
+ declare const index$1_systemClock: typeof systemClock;
382
+ declare namespace index$1 {
383
+ export { type index$1_Clock as Clock, type index$1_Hasher as Hasher, type index$1_IdGenerator as IdGenerator, type index$1_TransactionRunner as TransactionRunner, index$1_systemClock as systemClock };
384
+ }
385
+
386
+ type Result<T, E> = Readonly<{
387
+ ok: true;
388
+ value: T;
389
+ }> | Readonly<{
390
+ ok: false;
391
+ error: E;
392
+ }>;
393
+ declare function ok<T>(value: T): Result<T, never>;
394
+ declare function err<E>(error: E): Result<never, E>;
395
+ declare function isOk<T, E>(result: Result<T, E>): result is Readonly<{
396
+ ok: true;
397
+ value: T;
398
+ }>;
399
+ declare function isErr<T, E>(result: Result<T, E>): result is Readonly<{
400
+ ok: false;
401
+ error: E;
402
+ }>;
403
+ declare function unwrapResult<T, E>(result: Result<T, E>): T;
404
+ declare function mapResult<T, U, E>(result: Result<T, E>, mapper: (value: T) => U): Result<U, E>;
405
+
406
+ type index_Result<T, E> = Result<T, E>;
407
+ declare const index_err: typeof err;
408
+ declare const index_isErr: typeof isErr;
409
+ declare const index_isOk: typeof isOk;
410
+ declare const index_mapResult: typeof mapResult;
411
+ declare const index_ok: typeof ok;
412
+ declare const index_unwrapResult: typeof unwrapResult;
413
+ declare namespace index {
414
+ export { type index_Result as Result, index_err as err, index_isErr as isErr, index_isOk as isOk, index_mapResult as mapResult, index_ok as ok, index_unwrapResult as unwrapResult };
415
+ }
416
+
417
+ export { API_ACTIONS, API_CONTROLLERS, API_VERSION, type AuthContext, type AuthCredential, type AuthIdentity, AuthMode, type AuthPrincipal, type AuthResult, type AuthStrategy, type Clock, CoreError, CoreErrorCode, type CoreErrorOptions, ESortDir, EUserStatus, type ErrorResponseInput, type Hasher, type IBaseModel, type IBaseQuery, type IBaseResponse, type ICurrentUser, type IErrorResponse, type IJwtPayload, type ILoginDto, type IPaginatedResult, type IPermission, type IRegisterDto, type IRole, type ITokenResponse, type ITrackingModel, type IUser, type IdGenerator, index$6 as MeagoApi, index$5 as MeagoAuth, index$b as MeagoConstants, index$9 as MeagoDto, index$a as MeagoEnums, index$4 as MeagoErrors, index$8 as MeagoInterfacesCommon, index$7 as MeagoInterfacesModels, index$3 as MeagoPagination, index$2 as MeagoPermissions, index$1 as MeagoPorts, index as MeagoResult, type NormalizedPagination, PERMISSIONS, type PaginationInput, type PaginationPolicy, type PermissionCollection, type ResponseMeta, type Result, type SessionRecord, type SessionStore, type TransactionRunner, createErrorResponse, createPaginatedResult, createSuccessResponse, err, getPaginationOffset, hasAllPermissions, hasAnyPermission, isCoreError, isErr, isOk, joinApiPath, mapResult, normalizePagination, ok, systemClock, unwrapResult };