@yimingliao/cms 0.0.20 → 0.0.22
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/index.d.ts +30 -0
- package/dist/client/index.js +68 -0
- package/dist/index.d.ts +3 -20
- package/dist/server/index.d.ts +36 -1
- package/dist/server/index.js +83 -2
- package/dist/types-DHlRoJwv.d.ts +20 -0
- package/package.json +9 -2
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { R as Result } from '../types-DHlRoJwv.js';
|
|
2
|
+
import { Logger } from 'logry';
|
|
3
|
+
|
|
4
|
+
interface FetchContext {
|
|
5
|
+
input: string;
|
|
6
|
+
init: RequestInit;
|
|
7
|
+
meta: {
|
|
8
|
+
baseUrl?: string;
|
|
9
|
+
url?: string;
|
|
10
|
+
startTime?: number;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
type RequestInterceptor = (ctx: FetchContext) => Promise<FetchContext> | FetchContext;
|
|
14
|
+
type ResponseInterceptor = <T>(response: Response, ctx: FetchContext) => Promise<Result<T>>;
|
|
15
|
+
|
|
16
|
+
declare function createSmartFetch({ requestInterceptor, responseInterceptor, logger, }: {
|
|
17
|
+
requestInterceptor: RequestInterceptor;
|
|
18
|
+
responseInterceptor: ResponseInterceptor;
|
|
19
|
+
logger: Logger;
|
|
20
|
+
}): <T>(input: string, init?: RequestInit) => Promise<Result<T>>;
|
|
21
|
+
|
|
22
|
+
declare function createRequestInterceptor({ baseUrl }: {
|
|
23
|
+
baseUrl: string;
|
|
24
|
+
}): RequestInterceptor;
|
|
25
|
+
|
|
26
|
+
declare function createResponseInterceptor({ logger }: {
|
|
27
|
+
logger: Logger;
|
|
28
|
+
}): ResponseInterceptor;
|
|
29
|
+
|
|
30
|
+
export { createRequestInterceptor, createResponseInterceptor, createSmartFetch };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// src/client/infrastructure/smart-fetch/smart-fetch.ts
|
|
2
|
+
function createSmartFetch({
|
|
3
|
+
requestInterceptor,
|
|
4
|
+
responseInterceptor,
|
|
5
|
+
logger
|
|
6
|
+
}) {
|
|
7
|
+
const smartFetch = async (input, init = {}) => {
|
|
8
|
+
try {
|
|
9
|
+
let ctx = {
|
|
10
|
+
input,
|
|
11
|
+
init,
|
|
12
|
+
meta: {}
|
|
13
|
+
};
|
|
14
|
+
ctx = await requestInterceptor(ctx);
|
|
15
|
+
const response = await fetch(ctx.input, ctx.init);
|
|
16
|
+
return await responseInterceptor(response, ctx);
|
|
17
|
+
} catch (error) {
|
|
18
|
+
logger.error("smartFetch error", { error });
|
|
19
|
+
throw error instanceof Error ? error : new Error("Network Error");
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
return smartFetch;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// src/client/infrastructure/smart-fetch/request-interceptor.ts
|
|
26
|
+
function createRequestInterceptor({ baseUrl }) {
|
|
27
|
+
const requestInterceptor = (ctx) => {
|
|
28
|
+
const normalizedBase = baseUrl.replace(/\/+$/, "");
|
|
29
|
+
const normalizedPath = ctx.input.replace(/^\/+/, "");
|
|
30
|
+
const isAbsolute = /^[a-zA-Z]+:\/\//.test(ctx.input);
|
|
31
|
+
const input = isAbsolute ? ctx.input : `${normalizedBase}/${normalizedPath}`;
|
|
32
|
+
return {
|
|
33
|
+
...ctx,
|
|
34
|
+
input,
|
|
35
|
+
meta: {
|
|
36
|
+
...ctx.meta,
|
|
37
|
+
baseUrl,
|
|
38
|
+
url: ctx.input,
|
|
39
|
+
startTime: Date.now()
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
return requestInterceptor;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// src/client/infrastructure/smart-fetch/response-interceptor.ts
|
|
47
|
+
function createResponseInterceptor({ logger }) {
|
|
48
|
+
const responseInterceptor = async (response, ctx) => {
|
|
49
|
+
const duration = Date.now() - (ctx.meta.startTime ?? 0);
|
|
50
|
+
if (!response.ok) {
|
|
51
|
+
logger.warn("HTTP error", {
|
|
52
|
+
url: ctx.meta.url,
|
|
53
|
+
status: response.status,
|
|
54
|
+
duration
|
|
55
|
+
});
|
|
56
|
+
throw new Error(`HTTP Error ${response.status}`);
|
|
57
|
+
}
|
|
58
|
+
const data = await response.json();
|
|
59
|
+
logger.debug("Fetch success", {
|
|
60
|
+
url: ctx.meta.url,
|
|
61
|
+
duration
|
|
62
|
+
});
|
|
63
|
+
return data;
|
|
64
|
+
};
|
|
65
|
+
return responseInterceptor;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export { createRequestInterceptor, createResponseInterceptor, createSmartFetch };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { F as FolderFull } from './base-DbGnfZr6.js';
|
|
2
2
|
export { A as ADMIN_ROLES, a as Admin, b as AdminCard, c as AdminFull, d as AdminRefreshToken, e as AdminRole, f as AdminSafe, g as AdminTranslation, h as Alternate, B as BaseTranslation, D as DeviceInfo, E as ExternalLink, i as FILE_TYPES, j as Faq, k as File, l as FileCard, m as FileFull, n as FileTranslation, o as FileType, p as Folder, M as MultiItems, P as POST_TYPES, q as Post, r as PostFull, s as PostListCard, t as PostTranslation, u as PostType, S as SeoMetadata, v as SingleItem, T as TocItem, w as Translation } from './base-DbGnfZr6.js';
|
|
3
3
|
export { B as BlobFile } from './types-0oS1A2K5.js';
|
|
4
|
+
import { E as ErrorDetail, S as SuccessResult, a as ErrorResult } from './types-DHlRoJwv.js';
|
|
5
|
+
export { R as Result } from './types-DHlRoJwv.js';
|
|
4
6
|
|
|
5
7
|
declare const ROOT_FOLDER_ID = "01ARZ3NDEKTSV4RRFFQ69G5FAV";
|
|
6
8
|
declare const ROOT_FOLDER_NAME = "ROOT";
|
|
@@ -21,25 +23,6 @@ declare const getMediaInfo: (file: Blob) => Promise<MediaInfo>;
|
|
|
21
23
|
|
|
22
24
|
declare const formatFileSize: (size: number, decimals?: number) => string;
|
|
23
25
|
|
|
24
|
-
interface SuccessResult<D = unknown> {
|
|
25
|
-
success: true;
|
|
26
|
-
message?: string;
|
|
27
|
-
data?: D;
|
|
28
|
-
meta?: Record<string, unknown>;
|
|
29
|
-
}
|
|
30
|
-
interface ErrorResult {
|
|
31
|
-
success: false;
|
|
32
|
-
message?: string;
|
|
33
|
-
errors?: ErrorDetail[];
|
|
34
|
-
code?: string;
|
|
35
|
-
}
|
|
36
|
-
interface ErrorDetail {
|
|
37
|
-
field?: string;
|
|
38
|
-
message?: string;
|
|
39
|
-
code?: string;
|
|
40
|
-
}
|
|
41
|
-
type Result<D = unknown> = SuccessResult<D> | ErrorResult;
|
|
42
|
-
|
|
43
26
|
interface SuccessResultParams<D = unknown> {
|
|
44
27
|
message?: string;
|
|
45
28
|
data?: D;
|
|
@@ -61,4 +44,4 @@ declare const result: {
|
|
|
61
44
|
error: typeof error;
|
|
62
45
|
};
|
|
63
46
|
|
|
64
|
-
export {
|
|
47
|
+
export { ErrorDetail, ErrorResult, type ErrorResultParams, FolderFull, ROOT_FOLDER, ROOT_FOLDER_ID, ROOT_FOLDER_NAME, SIMPLE_UPLOAD_FOLDER_KEY, SIMPLE_UPLOAD_FOLDER_NAME, SuccessResult, type SuccessResultParams, classifyFileType, formatFileSize, getMediaInfo, mimeToExtension, result };
|
package/dist/server/index.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ import { cookies } from 'next/headers';
|
|
|
4
4
|
import Keyv from 'keyv';
|
|
5
5
|
import { Logger } from 'logry';
|
|
6
6
|
import { e as AdminRole, v as SingleItem, B as BaseTranslation, a as Admin, c as AdminFull, f as AdminSafe, D as DeviceInfo, d as AdminRefreshToken, k as File, m as FileFull, o as FileType, p as Folder, F as FolderFull, u as PostType, M as MultiItems, E as ExternalLink, j as Faq, T as TocItem, q as Post, s as PostListCard, t as PostTranslation, r as PostFull, S as SeoMetadata, g as AdminTranslation, n as FileTranslation, h as Alternate } from '../base-DbGnfZr6.js';
|
|
7
|
+
import { BaseTranslator, LocaleMessages } from 'intor';
|
|
8
|
+
import { S as SuccessResult, R as Result, E as ErrorDetail } from '../types-DHlRoJwv.js';
|
|
7
9
|
|
|
8
10
|
interface CreateJwtServiceOptions {
|
|
9
11
|
defaultSecret: string;
|
|
@@ -596,4 +598,37 @@ declare const POST_ORDER_BY: ({
|
|
|
596
598
|
index: "asc";
|
|
597
599
|
})[];
|
|
598
600
|
|
|
599
|
-
|
|
601
|
+
type Action<D> = (translator: BaseTranslator<LocaleMessages>) => Promise<Omit<SuccessResult<D>, "success"> & {
|
|
602
|
+
i18nKey?: string;
|
|
603
|
+
}>;
|
|
604
|
+
interface CreateServerActionOptions {
|
|
605
|
+
initI18n: () => Promise<BaseTranslator<LocaleMessages>>;
|
|
606
|
+
cacheResult: <T>({ key, ttl, load }: CacheResultOptions<T>) => Promise<T>;
|
|
607
|
+
cache: Keyv<unknown>;
|
|
608
|
+
logger: Logger;
|
|
609
|
+
}
|
|
610
|
+
interface ServerActionOptions {
|
|
611
|
+
type?: "command" | "query";
|
|
612
|
+
key?: RawCacheKey;
|
|
613
|
+
ttl?: number;
|
|
614
|
+
}
|
|
615
|
+
declare function createExecuteAction({ initI18n, cacheResult, cache, logger, }: CreateServerActionOptions): Promise<(<D = void>(fn: Action<D>, options?: ServerActionOptions) => Promise<Result<D>>)>;
|
|
616
|
+
|
|
617
|
+
declare const normalizeError: (error: unknown, translator: BaseTranslator<LocaleMessages>) => {
|
|
618
|
+
message: string;
|
|
619
|
+
errors?: ErrorDetail[];
|
|
620
|
+
statusCode: number;
|
|
621
|
+
isInternal?: boolean;
|
|
622
|
+
};
|
|
623
|
+
|
|
624
|
+
declare class ServerError extends Error {
|
|
625
|
+
readonly i18nKey?: string;
|
|
626
|
+
readonly statusCode?: number;
|
|
627
|
+
constructor({ message, i18nKey, statusCode, }: {
|
|
628
|
+
message?: string;
|
|
629
|
+
i18nKey?: string;
|
|
630
|
+
statusCode?: number;
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
export { ADMIN_ORDER_BY, ORDER_BY, POST_ORDER_BY, type RawCacheKey, ServerError, createAdminCommandRepository, createAdminQueryRepository, createAdminRefreshTokenCommandRepository, createAdminRefreshTokenQueryRepository, createArgon2Service, createCache, createCacheResult, createCookieService, createCryptoService, createExecuteAction, createFileCommandRepository, createFileQueryRepository, createFolderCommandRepository, createFolderQueryRepository, createIpRateLimiter, createJwtService, createPostCommandRepository, createPostQueryRepository, createSeoMetadataCommandRepository, normalizeCacheKey, normalizeError };
|
package/dist/server/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ADMIN_ROLES, ROOT_FOLDER_ID } from '../chunk-ZCOYQ5BG.js';
|
|
2
|
-
import { mimeToExtension, classifyFileType } from '../chunk-YX7IPIGU.js';
|
|
2
|
+
import { result, mimeToExtension, classifyFileType } from '../chunk-YX7IPIGU.js';
|
|
3
3
|
import jwt from 'jsonwebtoken';
|
|
4
4
|
import argon2 from 'argon2';
|
|
5
5
|
import crypto, { timingSafeEqual } from 'crypto';
|
|
@@ -7,6 +7,7 @@ import { headers } from 'next/headers';
|
|
|
7
7
|
import KeyvRedis from '@keyv/redis';
|
|
8
8
|
import Keyv from 'keyv';
|
|
9
9
|
import { ulid } from 'ulid';
|
|
10
|
+
import { ZodError } from 'zod';
|
|
10
11
|
|
|
11
12
|
function createJwtService({
|
|
12
13
|
defaultSecret,
|
|
@@ -1421,4 +1422,84 @@ function createSeoMetadataCommandRepository(prisma) {
|
|
|
1421
1422
|
};
|
|
1422
1423
|
}
|
|
1423
1424
|
|
|
1424
|
-
|
|
1425
|
+
// src/server/server-error.ts
|
|
1426
|
+
var ServerError = class extends Error {
|
|
1427
|
+
i18nKey;
|
|
1428
|
+
statusCode;
|
|
1429
|
+
constructor({
|
|
1430
|
+
message,
|
|
1431
|
+
i18nKey,
|
|
1432
|
+
statusCode
|
|
1433
|
+
}) {
|
|
1434
|
+
super(message);
|
|
1435
|
+
this.name = this.constructor.name;
|
|
1436
|
+
if (i18nKey) this.i18nKey = i18nKey;
|
|
1437
|
+
if (statusCode) this.statusCode = statusCode;
|
|
1438
|
+
}
|
|
1439
|
+
};
|
|
1440
|
+
|
|
1441
|
+
// src/server/interfaces/execute-action/normalize-error.ts
|
|
1442
|
+
var normalizeError = (error, translator) => {
|
|
1443
|
+
if (error instanceof ZodError) {
|
|
1444
|
+
const errors = error.issues.map((issue) => {
|
|
1445
|
+
let message = issue.message;
|
|
1446
|
+
if (issue.code === "custom" && issue.params?.["i18nKey"]) {
|
|
1447
|
+
message = translator.t(issue.params["i18nKey"]);
|
|
1448
|
+
}
|
|
1449
|
+
return {
|
|
1450
|
+
field: issue.path.join("."),
|
|
1451
|
+
// e.path: string[] e.g. ["name", "email"]
|
|
1452
|
+
message,
|
|
1453
|
+
code: issue.code
|
|
1454
|
+
};
|
|
1455
|
+
});
|
|
1456
|
+
return { message: "Validation faild", errors, statusCode: 422 };
|
|
1457
|
+
}
|
|
1458
|
+
if (error instanceof ServerError) {
|
|
1459
|
+
const message = translator.t(
|
|
1460
|
+
error.i18nKey ?? "error.internal-server-error"
|
|
1461
|
+
);
|
|
1462
|
+
return { message, statusCode: error.statusCode ?? 500 };
|
|
1463
|
+
}
|
|
1464
|
+
return {
|
|
1465
|
+
message: error instanceof Error ? error.message : JSON.stringify(error),
|
|
1466
|
+
statusCode: 500,
|
|
1467
|
+
isInternal: true
|
|
1468
|
+
};
|
|
1469
|
+
};
|
|
1470
|
+
|
|
1471
|
+
// src/server/interfaces/execute-action/create-execute-action.ts
|
|
1472
|
+
async function createExecuteAction({
|
|
1473
|
+
initI18n,
|
|
1474
|
+
cacheResult,
|
|
1475
|
+
cache,
|
|
1476
|
+
logger
|
|
1477
|
+
}) {
|
|
1478
|
+
return async function executeAction(fn, options = {}) {
|
|
1479
|
+
const translator = await initI18n();
|
|
1480
|
+
const withCache = options.key && options.type === "query";
|
|
1481
|
+
try {
|
|
1482
|
+
const { data, i18nKey, message, meta } = withCache ? await cacheResult({
|
|
1483
|
+
key: options.key,
|
|
1484
|
+
...options.ttl ? { ttl: options.ttl } : {},
|
|
1485
|
+
load: async () => fn(translator)
|
|
1486
|
+
}) : await fn(translator);
|
|
1487
|
+
if (options.type === "command") cache.clear();
|
|
1488
|
+
const finalMessage = i18nKey ? translator.t(i18nKey) : message;
|
|
1489
|
+
return result.success({
|
|
1490
|
+
...finalMessage ? { message: finalMessage } : {},
|
|
1491
|
+
...data ? { data: data ?? {} } : {},
|
|
1492
|
+
...meta ? { meta } : {}
|
|
1493
|
+
});
|
|
1494
|
+
} catch (error) {
|
|
1495
|
+
const { message, errors, isInternal } = normalizeError(error, translator);
|
|
1496
|
+
logger.error({ message, errors });
|
|
1497
|
+
return result.error({
|
|
1498
|
+
message: isInternal ? "Internal server error" : message,
|
|
1499
|
+
...errors !== void 0 ? { errors } : {}
|
|
1500
|
+
});
|
|
1501
|
+
}
|
|
1502
|
+
};
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
export { ADMIN_ORDER_BY, ORDER_BY, POST_ORDER_BY, ServerError, createAdminCommandRepository, createAdminQueryRepository, createAdminRefreshTokenCommandRepository, createAdminRefreshTokenQueryRepository, createArgon2Service, createCache, createCacheResult, createCookieService, createCryptoService, createExecuteAction, createFileCommandRepository, createFileQueryRepository, createFolderCommandRepository, createFolderQueryRepository, createIpRateLimiter, createJwtService, createPostCommandRepository, createPostQueryRepository, createSeoMetadataCommandRepository, normalizeCacheKey, normalizeError };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
interface SuccessResult<D = unknown> {
|
|
2
|
+
success: true;
|
|
3
|
+
message?: string;
|
|
4
|
+
data?: D;
|
|
5
|
+
meta?: Record<string, unknown>;
|
|
6
|
+
}
|
|
7
|
+
interface ErrorResult {
|
|
8
|
+
success: false;
|
|
9
|
+
message?: string;
|
|
10
|
+
errors?: ErrorDetail[];
|
|
11
|
+
code?: string;
|
|
12
|
+
}
|
|
13
|
+
interface ErrorDetail {
|
|
14
|
+
field?: string;
|
|
15
|
+
message?: string;
|
|
16
|
+
code?: string;
|
|
17
|
+
}
|
|
18
|
+
type Result<D = unknown> = SuccessResult<D> | ErrorResult;
|
|
19
|
+
|
|
20
|
+
export type { ErrorDetail as E, Result as R, SuccessResult as S, ErrorResult as a };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yimingliao/cms",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.22",
|
|
4
4
|
"author": "Yiming Liao",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -10,6 +10,11 @@
|
|
|
10
10
|
"import": "./dist/index.js",
|
|
11
11
|
"require": "./dist/index.js"
|
|
12
12
|
},
|
|
13
|
+
"./client": {
|
|
14
|
+
"types": "./dist/client/index.d.ts",
|
|
15
|
+
"import": "./dist/client/index.js",
|
|
16
|
+
"require": "./dist/client/index.js"
|
|
17
|
+
},
|
|
13
18
|
"./server": {
|
|
14
19
|
"types": "./dist/server/index.d.ts",
|
|
15
20
|
"import": "./dist/server/index.js",
|
|
@@ -51,10 +56,12 @@
|
|
|
51
56
|
"@types/mime-types": "^3.0.1",
|
|
52
57
|
"@types/ssh2-sftp-client": "^9.0.6",
|
|
53
58
|
"generic-pool": "^3.9.0",
|
|
59
|
+
"intor": "^2.5.0",
|
|
54
60
|
"next": "^16.1.6",
|
|
55
61
|
"prisma": "6.5.0",
|
|
56
62
|
"tsup": "^8.5.1",
|
|
57
|
-
"typescript": "^5.9.3"
|
|
63
|
+
"typescript": "^5.9.3",
|
|
64
|
+
"zod": "^4.3.6"
|
|
58
65
|
},
|
|
59
66
|
"peerDependencies": {
|
|
60
67
|
"@aws-sdk/client-s3": "^3.0.0",
|