@pnt-team/pnt-component-frontend 0.0.91 → 0.0.95

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.
Files changed (32) hide show
  1. package/README.md +149 -54
  2. package/lib/cjs/api/cos.d.ts +103 -0
  3. package/lib/cjs/api/cos.js +137 -0
  4. package/lib/cjs/components/PntCosUploader/index.d.ts +135 -0
  5. package/lib/cjs/components/PntCosUploader/index.js +209 -0
  6. package/lib/cjs/components/PntImage/index.d.ts +9 -3
  7. package/lib/cjs/components/PntImage/index.js +9 -1
  8. package/lib/cjs/components/PntSecureBox/index.js +8 -5
  9. package/lib/cjs/components/PntUpload/index.d.ts +149 -0
  10. package/lib/cjs/components/PntUpload/index.js +228 -0
  11. package/lib/cjs/components/PntUpload/index.scss +101 -0
  12. package/lib/cjs/hooks/useHighSecurity.d.ts +55 -15
  13. package/lib/cjs/hooks/useHighSecurity.js +168 -35
  14. package/lib/cjs/index.d.ts +6 -0
  15. package/lib/cjs/index.js +18 -2
  16. package/lib/esm/api/cos.d.ts +103 -0
  17. package/lib/esm/api/cos.js +189 -0
  18. package/lib/esm/components/PntCosUploader/index.d.ts +135 -0
  19. package/lib/esm/components/PntCosUploader/index.js +289 -0
  20. package/lib/esm/components/PntImage/index.d.ts +9 -3
  21. package/lib/esm/components/PntImage/index.js +22 -3
  22. package/lib/esm/components/PntSecureBox/index.js +9 -3
  23. package/lib/esm/components/PntUpload/index.d.ts +149 -0
  24. package/lib/esm/components/PntUpload/index.js +276 -0
  25. package/lib/esm/components/PntUpload/index.scss +101 -0
  26. package/lib/esm/hooks/useHighSecurity.d.ts +55 -15
  27. package/lib/esm/hooks/useHighSecurity.js +174 -32
  28. package/lib/esm/index.d.ts +6 -0
  29. package/lib/esm/index.js +9 -1
  30. package/lib/umd/pnt-component-frontend.min.css +1 -1
  31. package/lib/umd/pnt-component-frontend.min.js +1 -1
  32. package/package.json +2 -2
@@ -0,0 +1,228 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
24
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
+ mod
26
+ ));
27
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
+
29
+ // src/components/PntUpload/index.tsx
30
+ var PntUpload_exports = {};
31
+ __export(PntUpload_exports, {
32
+ default: () => PntUpload_default
33
+ });
34
+ module.exports = __toCommonJS(PntUpload_exports);
35
+ var import_PntCosUploader = __toESM(require("../PntCosUploader"));
36
+ var import_PntImage = __toESM(require("../PntImage"));
37
+ var import_react = require("react");
38
+ var import_tdesign_icons_react = require("tdesign-icons-react");
39
+ var import_tdesign_react = require("tdesign-react");
40
+ var import_index = require("./index.scss");
41
+ var import_jsx_runtime = require("react/jsx-runtime");
42
+ var DEFAULT_TEXTS = {
43
+ uploading: "上传中",
44
+ uploadFailed: "上传失败,请重试",
45
+ sizeInvalid: "文件大小超出限制",
46
+ typeInvalid: "文件类型不支持",
47
+ selectFile: "选择文件",
48
+ reselect: "重新选择"
49
+ };
50
+ var toAcceptList = (accept) => {
51
+ const raw = Array.isArray(accept) ? accept : accept ? accept.split(",") : [];
52
+ return raw.map((item) => item.trim().toLowerCase()).filter(Boolean);
53
+ };
54
+ var matchAccept = (file, acceptList) => {
55
+ if (!acceptList.length) {
56
+ return true;
57
+ }
58
+ const name = file.name.toLowerCase();
59
+ const type = file.type.toLowerCase();
60
+ return acceptList.some((token) => {
61
+ if (token.startsWith(".")) {
62
+ return name.endsWith(token);
63
+ }
64
+ if (token.endsWith("/*")) {
65
+ return !!type && type.startsWith(token.slice(0, -1));
66
+ }
67
+ return type === token;
68
+ });
69
+ };
70
+ var getFileNameFromUrl = (url) => {
71
+ if (!url) {
72
+ return "";
73
+ }
74
+ const lastSeg = url.split("?")[0].split("/").pop() || "";
75
+ try {
76
+ return decodeURIComponent(lastSeg);
77
+ } catch {
78
+ return lastSeg;
79
+ }
80
+ };
81
+ var PntUpload = ({
82
+ value,
83
+ onChange,
84
+ onRemove,
85
+ onError,
86
+ scene,
87
+ pathKey,
88
+ context,
89
+ audience,
90
+ accept,
91
+ sizeLimit,
92
+ imagePreview = true,
93
+ previewStyle,
94
+ defaultImg,
95
+ fileNameStrategy,
96
+ useCdn,
97
+ getCredential,
98
+ normalizeCredential,
99
+ texts,
100
+ disabled = false,
101
+ style,
102
+ className
103
+ }) => {
104
+ const [uploading, setUploading] = (0, import_react.useState)(false);
105
+ const [percent, setPercent] = (0, import_react.useState)(0);
106
+ const [errorText, setErrorText] = (0, import_react.useState)("");
107
+ const [lastFile, setLastFile] = (0, import_react.useState)(null);
108
+ const finalTexts = (0, import_react.useMemo)(() => ({ ...DEFAULT_TEXTS, ...texts }), [texts]);
109
+ const acceptList = (0, import_react.useMemo)(() => toAcceptList(accept), [accept]);
110
+ const acceptString = (0, import_react.useMemo)(() => acceptList.join(", "), [acceptList]);
111
+ const uploader = (0, import_PntCosUploader.default)({
112
+ scene,
113
+ pathKey,
114
+ context,
115
+ audience,
116
+ fileNameStrategy,
117
+ useCdn,
118
+ getCredential,
119
+ normalizeCredential
120
+ });
121
+ const reportError = (message, phase, file) => {
122
+ setErrorText(message);
123
+ onError == null ? void 0 : onError(new Error(message), phase, file);
124
+ };
125
+ const requestMethod = async (files) => {
126
+ const tdFile = Array.isArray(files) ? files[0] : files;
127
+ const file = tdFile == null ? void 0 : tdFile.raw;
128
+ if (!file) {
129
+ return { status: "fail", error: "no file", response: {} };
130
+ }
131
+ if (!matchAccept(file, acceptList)) {
132
+ reportError(finalTexts.typeInvalid, "validate", file);
133
+ return { status: "fail", error: finalTexts.typeInvalid, response: {} };
134
+ }
135
+ if (typeof sizeLimit === "number" && file.size > sizeLimit) {
136
+ reportError(finalTexts.sizeInvalid, "validate", file);
137
+ return { status: "fail", error: finalTexts.sizeInvalid, response: {} };
138
+ }
139
+ setUploading(true);
140
+ setPercent(0);
141
+ setErrorText("");
142
+ try {
143
+ const result = await uploader.upload(file, setPercent);
144
+ setLastFile(file);
145
+ onChange == null ? void 0 : onChange(result.fileUrl, result.cosKey, file);
146
+ return {
147
+ status: "success",
148
+ response: { url: result.fileUrl }
149
+ };
150
+ } catch (e) {
151
+ const err = e instanceof Error ? e : new Error(String(e));
152
+ setErrorText(finalTexts.uploadFailed);
153
+ onError == null ? void 0 : onError(err, "upload", file);
154
+ return { status: "fail", error: err.message, response: {} };
155
+ } finally {
156
+ setUploading(false);
157
+ }
158
+ };
159
+ const handleRemove = (e) => {
160
+ e.stopPropagation();
161
+ if (disabled || uploading) {
162
+ return;
163
+ }
164
+ setLastFile(null);
165
+ setErrorText("");
166
+ onRemove == null ? void 0 : onRemove();
167
+ };
168
+ const wrapperClassName = [
169
+ "pnt-upload",
170
+ imagePreview ? "pnt-upload--image" : "pnt-upload--file",
171
+ disabled ? "pnt-upload--disabled" : "",
172
+ className || ""
173
+ ].filter(Boolean).join(" ");
174
+ const displayName = (lastFile == null ? void 0 : lastFile.name) || getFileNameFromUrl(value);
175
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: wrapperClassName, style, children: [
176
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
177
+ import_tdesign_react.Upload,
178
+ {
179
+ theme: "custom",
180
+ requestMethod,
181
+ accept: acceptString || void 0,
182
+ allowUploadDuplicateFile: true,
183
+ disabled: disabled || uploading,
184
+ className: "pnt-upload__trigger",
185
+ children: imagePreview ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
186
+ import_PntImage.default,
187
+ {
188
+ src: value,
189
+ loading: uploading,
190
+ defaultImg,
191
+ style: previewStyle,
192
+ texts: { uploading: finalTexts.uploading },
193
+ onRemove: disabled ? void 0 : handleRemove
194
+ }
195
+ ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pnt-upload-file", children: value && !uploading ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "pnt-upload-file__row", children: [
196
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tdesign_icons_react.FileIcon, { className: "pnt-upload-file__icon" }),
197
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pnt-upload-file__name", title: displayName, children: displayName }),
198
+ !disabled && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
199
+ import_tdesign_icons_react.DeleteIcon,
200
+ {
201
+ className: "pnt-upload-file__delete",
202
+ onClick: handleRemove
203
+ }
204
+ )
205
+ ] }) : uploading ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "pnt-upload-file__row pnt-upload-file__row--progress", children: [
206
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
207
+ import_tdesign_react.Progress,
208
+ {
209
+ percentage: percent,
210
+ className: "pnt-upload-file__progress"
211
+ }
212
+ ),
213
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "pnt-upload-file__percent", children: [
214
+ finalTexts.uploading,
215
+ " ",
216
+ percent,
217
+ "%"
218
+ ] })
219
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "pnt-upload-file__row pnt-upload-file__row--empty", children: [
220
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tdesign_icons_react.UploadIcon, { className: "pnt-upload-file__icon" }),
221
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: value ? finalTexts.reselect : finalTexts.selectFile })
222
+ ] }) })
223
+ }
224
+ ),
225
+ errorText && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "pnt-upload__error", children: errorText })
226
+ ] });
227
+ };
228
+ var PntUpload_default = PntUpload;
@@ -0,0 +1,101 @@
1
+ .pnt-upload {
2
+ display: inline-block;
3
+
4
+ // TDesign Upload custom 触发器外层
5
+ .pnt-upload__trigger {
6
+ display: inline-block;
7
+ cursor: pointer;
8
+ }
9
+
10
+ // 错误文案(图片 / 文件模式共用)
11
+ .pnt-upload__error {
12
+ margin: 4px 0 0;
13
+ color: var(--td-error-color, #d54941);
14
+ font-size: 12px;
15
+ line-height: 20px;
16
+ }
17
+
18
+ &--disabled {
19
+ .pnt-upload__trigger {
20
+ cursor: not-allowed;
21
+ }
22
+ }
23
+
24
+ // ========== 文件模式 ==========
25
+ .pnt-upload-file {
26
+ box-sizing: border-box;
27
+ min-width: 320px;
28
+ padding: 12px 16px;
29
+ border: 1px dashed var(--td-component-border, #dcdcdc);
30
+ border-radius: var(--td-radius-default, 6px);
31
+ background-color: var(--td-bg-color-container, #fff);
32
+ transition: border-color 0.2s;
33
+
34
+ &:hover {
35
+ border-color: var(--td-brand-color, #165dff);
36
+ }
37
+
38
+ &__row {
39
+ display: flex;
40
+ align-items: center;
41
+ gap: 8px;
42
+ color: var(--td-text-color-primary, #1f2329);
43
+ font-size: 14px;
44
+ line-height: 22px;
45
+
46
+ &--empty {
47
+ justify-content: flex-start;
48
+ color: var(--td-text-color-secondary, #6b7280);
49
+ }
50
+
51
+ &--progress {
52
+ align-items: center;
53
+ }
54
+ }
55
+
56
+ &__icon {
57
+ flex: none;
58
+ font-size: 20px;
59
+ color: var(--td-text-color-secondary, #6b7280);
60
+ }
61
+
62
+ &__name {
63
+ flex: 1;
64
+ overflow: hidden;
65
+ white-space: nowrap;
66
+ text-overflow: ellipsis;
67
+ }
68
+
69
+ &__delete {
70
+ flex: none;
71
+ font-size: 18px;
72
+ color: var(--td-text-color-secondary, #6b7280);
73
+ cursor: pointer;
74
+
75
+ &:hover {
76
+ color: var(--td-error-color, #d54941);
77
+ }
78
+ }
79
+
80
+ &__progress {
81
+ flex: 1;
82
+ min-width: 0;
83
+ }
84
+
85
+ &__percent {
86
+ flex: none;
87
+ color: var(--td-text-color-secondary, #6b7280);
88
+ font-size: 12px;
89
+ }
90
+ }
91
+
92
+ &--disabled {
93
+ .pnt-upload-file {
94
+ background-color: var(--td-bg-color-component-disabled, #f3f3f3);
95
+
96
+ &:hover {
97
+ border-color: var(--td-component-border, #dcdcdc);
98
+ }
99
+ }
100
+ }
101
+ }
@@ -1,22 +1,62 @@
1
- /** 高敏数据请求参数(组件层友好命名) */
1
+ /** 单个解锁项 */
2
+ export interface HighSecurityParamItem {
3
+ /** 字段标识(与后端约定) */
4
+ key: string;
5
+ /** 参数密文(_high_security_: 前缀) */
6
+ value: string;
7
+ }
8
+ /** 高敏数据解锁请求参数(组件层友好命名,库内部转换为后端 snake_case) */
2
9
  export interface HighSecurityRequestParams {
3
- /** 游戏 ID */
10
+ /** 业务 ID */
4
11
  gameId: string | number;
5
- /** 环境标识 */
12
+ /** 环境标识(1=dev 2=test 3=prod,以各业务现有约定为准) */
6
13
  env: string | number;
7
- /** 页面 ID(不传则默认取 location.pathname) */
14
+ /** 页面 ID(不传则默认取 location.pathname),用于操作日志和权限校验 */
8
15
  pageId?: string;
9
- /** 需要解锁的参数列表 */
10
- param: Array<{
11
- key: string;
12
- value: string;
13
- }>;
16
+ /** 需要解锁的参数列表,支持批量 */
17
+ param: HighSecurityParamItem[];
18
+ }
19
+ /** hook 初始化 scope:传入后 securityData/isUnlocked 自动按该业务+环境过滤 */
20
+ export interface UseHighSecurityOptions {
21
+ /** 当前业务 ID */
22
+ gameId?: string | number;
23
+ /** 当前环境 */
24
+ env?: string | number;
25
+ }
26
+ /** 清缓存过滤条件 */
27
+ export interface ClearSecurityDataFilter {
28
+ gameId?: string | number;
29
+ env?: string | number;
30
+ }
31
+ export interface UseHighSecurityResult {
32
+ /** 当前 scope 下已解锁的明文(key 为纯字段名);未传 scope 时为空对象 */
33
+ securityData: Record<string, string>;
34
+ /**
35
+ * 发起解锁
36
+ * @description
37
+ * - 已缓存的 key 直接返回缓存值,不再请求
38
+ * - 同一参数的在途请求自动去重
39
+ * - 成功:写入模块缓存并返回当次解密数组(含已缓存项)
40
+ * - 失败/部分失败:仅返回拿到的项(可能为空数组),不抛错
41
+ */
42
+ requestSecurityData: (params: HighSecurityRequestParams) => Promise<HighSecurityParamItem[]>;
43
+ /**
44
+ * 清缓存:不传参清空全部;传 gameId 清空该业务所有环境;
45
+ * 同时传 gameId+env 只清空该业务指定环境;仅传 env 清空所有业务该环境
46
+ */
47
+ clearSecurityData: (filter?: ClearSecurityDataFilter) => void;
48
+ /** 当前 scope 下某字段是否已解锁(两步式"查看 → 复制"UI 用) */
49
+ isUnlocked: (key: string) => boolean;
14
50
  }
15
51
  /**
16
- * 高敏数据解锁 Hook(PntSecureBox 内部依赖)
17
- * @internal 不对外导出,归 PntSecureBox 组件内部使用
52
+ * 高敏数据解锁 Hook(PntSecureBox 内部依赖,同时对外导出供"复制"等非展示场景使用)
53
+ *
54
+ * @example
55
+ * ```tsx
56
+ * // 展示场景请直接用 PntSecureBox;两步式"查看 → 复制":
57
+ * const { isUnlocked, requestSecurityData, securityData } = useHighSecurity({ gameId, env });
58
+ * const res = await requestSecurityData({ gameId, env, param: [{ key: 'SDK_KEY', value }] });
59
+ * if (res.length) copy(securityData.SDK_KEY);
60
+ * ```
18
61
  */
19
- export declare const useHighSecurity: () => {
20
- securityData: Record<string, string>;
21
- requestSecurityData: ({ gameId, param, env, pageId, }: HighSecurityRequestParams) => Promise<void>;
22
- };
62
+ export declare const useHighSecurity: (options?: UseHighSecurityOptions) => UseHighSecurityResult;
@@ -25,47 +25,180 @@ module.exports = __toCommonJS(useHighSecurity_exports);
25
25
  var import_api = require("../api");
26
26
  var import_config = require("../config");
27
27
  var import_react = require("react");
28
+ var buildCacheKey = (gameId, env, key) => `${gameId}:${env}:${key}`;
28
29
  var securityCache = /* @__PURE__ */ new Map();
29
- var useHighSecurity = () => {
30
- const { http } = (0, import_config.usePntConfig)();
31
- const [securityData, setSecurityData] = (0, import_react.useState)(
32
- () => {
33
- const cached = {};
34
- securityCache.forEach((value, key) => {
35
- cached[key] = value;
36
- });
37
- return cached;
30
+ var inflightRequests = /* @__PURE__ */ new Map();
31
+ var listeners = /* @__PURE__ */ new Set();
32
+ var emitChange = () => {
33
+ listeners.forEach((listener) => listener());
34
+ };
35
+ var subscribe = (listener) => {
36
+ listeners.add(listener);
37
+ return () => {
38
+ listeners.delete(listener);
39
+ };
40
+ };
41
+ var getSnapshot = () => securityCache;
42
+ var setEntries = (gameId, env, items) => {
43
+ if (!items.length)
44
+ return;
45
+ const next = new Map(securityCache);
46
+ items.forEach((item) => {
47
+ next.set(buildCacheKey(gameId, env, item.key), item.value);
48
+ });
49
+ securityCache = next;
50
+ emitChange();
51
+ };
52
+ var matchFilter = (cacheKey, filter) => {
53
+ if (!filter || filter.gameId === void 0 && filter.env === void 0) {
54
+ return true;
55
+ }
56
+ const parts = cacheKey.split(":");
57
+ const [cachedGameId, cachedEnv] = parts;
58
+ if (filter.gameId !== void 0 && cachedGameId !== String(filter.gameId)) {
59
+ return false;
60
+ }
61
+ if (filter.env !== void 0 && cachedEnv !== String(filter.env)) {
62
+ return false;
63
+ }
64
+ return true;
65
+ };
66
+ var clearEntries = (filter) => {
67
+ const next = /* @__PURE__ */ new Map();
68
+ securityCache.forEach((value, key) => {
69
+ if (!matchFilter(key, filter)) {
70
+ next.set(key, value);
38
71
  }
72
+ });
73
+ if (next.size !== securityCache.size) {
74
+ securityCache = next;
75
+ emitChange();
76
+ }
77
+ };
78
+ var getDefaultPageId = () => typeof window !== "undefined" && window.location ? window.location.pathname : "";
79
+ var useHighSecurity = (options) => {
80
+ const { http } = (0, import_config.usePntConfig)();
81
+ const snapshot = (0, import_react.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
82
+ const scopeGameId = (options == null ? void 0 : options.gameId) === void 0 ? void 0 : String(options.gameId);
83
+ const scopeEnv = (options == null ? void 0 : options.env) === void 0 ? void 0 : String(options.env);
84
+ const hasScope = scopeGameId !== void 0 && scopeEnv !== void 0;
85
+ const securityData = (0, import_react.useMemo)(() => {
86
+ if (!hasScope)
87
+ return {};
88
+ const prefix = `${scopeGameId}:${scopeEnv}:`;
89
+ const result = {};
90
+ snapshot.forEach((value, cacheKey) => {
91
+ if (cacheKey.startsWith(prefix)) {
92
+ result[cacheKey.slice(prefix.length)] = value;
93
+ }
94
+ });
95
+ return result;
96
+ }, [snapshot, hasScope, scopeGameId, scopeEnv]);
97
+ const isUnlocked = (0, import_react.useCallback)(
98
+ (key) => {
99
+ if (!hasScope)
100
+ return false;
101
+ return snapshot.has(buildCacheKey(scopeGameId, scopeEnv, key));
102
+ },
103
+ [snapshot, hasScope, scopeGameId, scopeEnv]
39
104
  );
40
- const requestSecurityData = async ({
41
- gameId,
42
- param,
43
- env,
44
- pageId
45
- }) => {
46
- const body = {
47
- game_id: Number(gameId),
48
- env: Number(env),
49
- page_id: pageId || location.pathname,
50
- param: param.map(
51
- (i) => ({
52
- key: i.key,
53
- value: i.value
54
- })
55
- )
56
- };
57
- const result = await (0, import_api.businessGameInfoHighSecurityDecrypt)(http, body);
58
- if (result.code === 0 && result.data) {
59
- const data = result.data;
60
- data.forEach((item) => {
61
- securityCache.set(item.key, item.value);
105
+ const requestSecurityData = (0, import_react.useCallback)(
106
+ async ({
107
+ gameId,
108
+ env,
109
+ pageId,
110
+ param
111
+ }) => {
112
+ const gid = String(gameId);
113
+ const eid = String(env);
114
+ const result = new Array(
115
+ param.length
116
+ );
117
+ const missing = [];
118
+ const missingIndexes = [];
119
+ param.forEach((item, index) => {
120
+ const cached = snapshot.get(buildCacheKey(gid, eid, item.key));
121
+ if (cached !== void 0) {
122
+ result[index] = { key: item.key, value: cached };
123
+ } else {
124
+ missing.push({ key: item.key, value: item.value });
125
+ missingIndexes.push(index);
126
+ }
62
127
  });
63
- setSecurityData(Object.fromEntries(securityCache));
64
- }
65
- };
128
+ if (!missing.length) {
129
+ return result.filter(
130
+ (item) => item !== void 0
131
+ );
132
+ }
133
+ const body = {
134
+ game_id: Number(gameId),
135
+ env: Number(env),
136
+ page_id: pageId || getDefaultPageId(),
137
+ param: missing
138
+ };
139
+ const requestKey = `${gid}:${eid}:${missing.map((item) => `${item.key}${item.value}`).join("")}`;
140
+ const existing = inflightRequests.get(requestKey);
141
+ if (existing)
142
+ return existing;
143
+ const promise = (async () => {
144
+ var _a;
145
+ try {
146
+ const res = await (0, import_api.businessGameInfoHighSecurityDecrypt)(http, body);
147
+ if (res.code === 0 && Array.isArray(res.data)) {
148
+ const decrypted = res.data.map((raw) => {
149
+ var _a2;
150
+ if (raw && typeof raw === "object" && "key" in raw) {
151
+ const item = raw;
152
+ if (typeof item.key === "string") {
153
+ return { key: item.key, value: String((_a2 = item.value) != null ? _a2 : "") };
154
+ }
155
+ }
156
+ return null;
157
+ }).filter(
158
+ (item) => item !== null
159
+ );
160
+ setEntries(gid, eid, decrypted);
161
+ decrypted.forEach((item) => {
162
+ const index = missingIndexes.find(
163
+ (i) => param[i].key === item.key
164
+ );
165
+ if (index !== void 0)
166
+ result[index] = item;
167
+ });
168
+ } else {
169
+ console.warn(
170
+ "[pnt-component-frontend] high-security-decrypt 解锁失败",
171
+ (_a = res == null ? void 0 : res.msg) != null ? _a : res == null ? void 0 : res.code
172
+ );
173
+ }
174
+ } catch (error) {
175
+ console.warn(
176
+ "[pnt-component-frontend] high-security-decrypt 请求异常",
177
+ error
178
+ );
179
+ } finally {
180
+ inflightRequests.delete(requestKey);
181
+ }
182
+ return result.filter(
183
+ (item) => item !== void 0
184
+ );
185
+ })();
186
+ inflightRequests.set(requestKey, promise);
187
+ return promise;
188
+ },
189
+ [http, snapshot]
190
+ );
191
+ const clearSecurityData = (0, import_react.useCallback)(
192
+ (filter) => {
193
+ clearEntries(filter);
194
+ },
195
+ []
196
+ );
66
197
  return {
67
198
  securityData,
68
- requestSecurityData
199
+ requestSecurityData,
200
+ clearSecurityData,
201
+ isUnlocked
69
202
  };
70
203
  };
71
204
  // Annotate the CommonJS export names for ESM import in node:
@@ -2,4 +2,10 @@ export { PntProvider, usePntConfig, type PntConfig, type commonEesType, } from '
2
2
  export { default as PntImage } from './components/PntImage';
3
3
  export { default as PntBusinessSelect } from './components/PntBusinessSelect';
4
4
  export { default as PntSecureBox } from './components/PntSecureBox';
5
+ export { default as PntUpload } from './components/PntUpload';
6
+ export type { PntUploadProps, PntUploadTexts, PntUploadErrorPhase, } from './components/PntUpload';
7
+ export { default as usePntCosUploader, createPntCosUploader, normalizeSnakeCredential, type PntCosUploadOptions, type PntCosUploadResult, type PntCosUploader as PntCosUploaderInstance, type PntCosRequestOverrides, type FileNameStrategy, type CosScene, type GetCosCredentialParams, type NormalizedCosCredential, type NormalizeCredential, } from './components/PntCosUploader';
8
+ export { useHighSecurity } from './hooks/useHighSecurity';
9
+ export type { HighSecurityParamItem, HighSecurityRequestParams, UseHighSecurityOptions, UseHighSecurityResult, ClearSecurityDataFilter, } from './hooks/useHighSecurity';
10
+ export { isSecurityData } from './utils';
5
11
  export { default as PntGeneralLayout } from './components/PntGeneralLayout';
package/lib/cjs/index.js CHANGED
@@ -34,13 +34,23 @@ __export(src_exports, {
34
34
  PntImage: () => import_PntImage.default,
35
35
  PntProvider: () => import_config.PntProvider,
36
36
  PntSecureBox: () => import_PntSecureBox.default,
37
- usePntConfig: () => import_config.usePntConfig
37
+ PntUpload: () => import_PntUpload.default,
38
+ createPntCosUploader: () => import_PntCosUploader.createPntCosUploader,
39
+ isSecurityData: () => import_utils.isSecurityData,
40
+ normalizeSnakeCredential: () => import_PntCosUploader.normalizeSnakeCredential,
41
+ useHighSecurity: () => import_useHighSecurity.useHighSecurity,
42
+ usePntConfig: () => import_config.usePntConfig,
43
+ usePntCosUploader: () => import_PntCosUploader.default
38
44
  });
39
45
  module.exports = __toCommonJS(src_exports);
40
46
  var import_config = require("./config");
41
47
  var import_PntImage = __toESM(require("./components/PntImage"));
42
48
  var import_PntBusinessSelect = __toESM(require("./components/PntBusinessSelect"));
43
49
  var import_PntSecureBox = __toESM(require("./components/PntSecureBox"));
50
+ var import_PntUpload = __toESM(require("./components/PntUpload"));
51
+ var import_PntCosUploader = __toESM(require("./components/PntCosUploader"));
52
+ var import_useHighSecurity = require("./hooks/useHighSecurity");
53
+ var import_utils = require("./utils");
44
54
  var import_PntGeneralLayout = __toESM(require("./components/PntGeneralLayout"));
45
55
  // Annotate the CommonJS export names for ESM import in node:
46
56
  0 && (module.exports = {
@@ -49,5 +59,11 @@ var import_PntGeneralLayout = __toESM(require("./components/PntGeneralLayout"));
49
59
  PntImage,
50
60
  PntProvider,
51
61
  PntSecureBox,
52
- usePntConfig
62
+ PntUpload,
63
+ createPntCosUploader,
64
+ isSecurityData,
65
+ normalizeSnakeCredential,
66
+ useHighSecurity,
67
+ usePntConfig,
68
+ usePntCosUploader
53
69
  });