n20-common-lib 3.2.40 → 3.2.43

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/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "n20-common-lib",
3
- "version": "3.2.40",
3
+ "version": "3.2.43",
4
4
  "private": false,
5
5
  "scripts": {
6
- "serve": "vue-cli-service serve",
6
+ "serve": "pnpm --dir ../packages/docs run serve",
7
7
  "prepublishOnly": "npm run build:css",
8
8
  "build": "node versionInfo.js && npm run build:css && vue-cli-service build --testTheme",
9
9
  "test:unit": "vue-cli-service test:unit",
@@ -68,6 +68,7 @@
68
68
  "v-viewer": "1.6.4",
69
69
  "vue-jsonp": "2.0.0",
70
70
  "vuedraggable": "*",
71
+ "worker-loader": "^3.0.8",
71
72
  "webpack-duplicate-relano-plugin": "^0.1.0",
72
73
  "xe-utils": "4.0.2"
73
74
  },
@@ -22,6 +22,8 @@
22
22
  </template>
23
23
 
24
24
  <script>
25
+ import { $lc } from '../../utils/i18n/index'
26
+
25
27
  const LONG_TERM_DATE = new Date(9999, 11, 31, 23, 59, 59)
26
28
 
27
29
  export default {
@@ -45,7 +47,7 @@ export default {
45
47
  },
46
48
  longTermLabel: {
47
49
  type: String,
48
- default: $lc("长期")
50
+ default: $lc('长期')
49
51
  }
50
52
  },
51
53
  computed: {
@@ -268,7 +268,13 @@
268
268
  :close-on-click-modal="false"
269
269
  :destroy-on-open="true"
270
270
  >
271
- <el-select v-model="bathType" class="m-b-s w-100p" clearable @change="handleBathChange">
271
+ <el-select
272
+ v-if="!autoMatchBatchFileType"
273
+ v-model="bathType"
274
+ class="m-b-s w-100p"
275
+ clearable
276
+ @change="handleBathChange"
277
+ >
272
278
  <el-option
273
279
  v-for="item in typeOptions"
274
280
  :key="item.type"
@@ -278,7 +284,7 @@
278
284
  />
279
285
  </el-select>
280
286
  <Upload
281
- v-if="bathType"
287
+ v-if="autoMatchBatchFileType || bathType"
282
288
  ref="upload-batch"
283
289
  class="n20-upload-drag"
284
290
  :msg-type="null"
@@ -305,7 +311,7 @@
305
311
  </template>
306
312
  </Upload>
307
313
  <div class="dialog-footer">
308
- <el-button type="primary" @click="batchUploadFn">{{ '确认' | $lc }}</el-button>
314
+ <el-button type="primary" :loading="batchUploading" @click="batchUploadFn">{{ '确认' | $lc }}</el-button>
309
315
  <el-button plain @click="visibleBatch = false">{{ '取消' | $lc }}</el-button>
310
316
  </div>
311
317
  </Dialog>
@@ -434,6 +440,10 @@ export default {
434
440
  type: Boolean,
435
441
  default: false
436
442
  },
443
+ autoMatchBatchFileType: {
444
+ type: Boolean,
445
+ default: false
446
+ },
437
447
  showBatchPrint: {
438
448
  type: Boolean,
439
449
  default: false
@@ -498,6 +508,7 @@ export default {
498
508
  previewSameOrg: false,
499
509
  seeRow: {},
500
510
  officeStatus: false,
511
+ batchUploading: false,
501
512
  // 批量上传使用的文件数据(避免直接修改计算属性)
502
513
  _batchFileData: null
503
514
  }
@@ -752,11 +763,91 @@ export default {
752
763
  data: JSON.stringify(dto)
753
764
  }
754
765
  },
755
- batchUploadFn() {
766
+ async batchUploadFn() {
767
+ if (this.autoMatchBatchFileType) {
768
+ await this.autoMatchBatchUpload()
769
+ return
770
+ }
756
771
  let $uploadwrap = this.$refs['upload-batch']
757
772
  // 直接提交上传,关闭对话框的逻辑在 batchSuccess 回调中处理
758
773
  $uploadwrap.submit()
759
774
  },
775
+ async autoMatchBatchUpload() {
776
+ let $uploadwrap = this.$refs['upload-batch']
777
+ let uploadFiles = $uploadwrap?.$refs?.upload?.uploadFiles || []
778
+ let files = uploadFiles.filter((item) => item.status === 'ready' && item.raw)
779
+ if (!files.length) {
780
+ this.$message.warning($lc('请先选择上传文件'))
781
+ return
782
+ }
783
+
784
+ try {
785
+ for (let item of files) {
786
+ let result = await $uploadwrap.beforeUploadFn(item.raw)
787
+ if (result === false) {
788
+ return
789
+ }
790
+ }
791
+
792
+ let FD = new FormData()
793
+ files.forEach((item) => {
794
+ FD.append('file', item.raw)
795
+ })
796
+
797
+ let remoteFileUpload = JSON.parse(this.fileData?.data || '{}')
798
+ remoteFileUpload.bussValues = this.typeOptions.map((item) => item.type)
799
+ FD.append('remoteFileUpload', JSON.stringify(remoteFileUpload))
800
+
801
+ this.batchUploading = true
802
+ let { data } = await axios.post(
803
+ this.apiPrefix
804
+ ? `${this.apiPrefix}/neams/eamsbaserecord/batchSaveFile`
805
+ : '/neams/eamsbaserecord/batchSaveFile',
806
+ FD,
807
+ {
808
+ headers: Object.assign(auth.setHeaders(this.headers), { 'Content-Type': 'multipart/form-data' }),
809
+ loading: false,
810
+ noMsg: true,
811
+ timeout: 900000
812
+ }
813
+ )
814
+
815
+ let responseList = Array.isArray(data) ? data : []
816
+ let rows = responseList.map((item) => this.buildAutoMatchBatchRow(item))
817
+ this.tableData.splice(0, 0, ...rows)
818
+ let pendingFiles = [...files]
819
+ rows.forEach((row) => {
820
+ let fileIndex = pendingFiles.findIndex((item) => item.raw.name === row[this.keys.name])
821
+ let sourceFile = pendingFiles.splice(fileIndex > -1 ? fileIndex : 0, 1)[0]
822
+ this.$emit('on-success', sourceFile?.raw, row)
823
+ })
824
+
825
+ $uploadwrap.clearFiles()
826
+ $uploadwrap.fileList = []
827
+ this.visibleBatch = false
828
+ } catch (err) {
829
+ this.$message.error(err?.msg || $lc('上传失败'))
830
+ } finally {
831
+ this.batchUploading = false
832
+ }
833
+ },
834
+ buildAutoMatchBatchRow(item) {
835
+ let userInfo = JSON.parse(sessionStorage.getItem('userInfo') || '{}')
836
+ let row = {
837
+ ...item,
838
+ _name: item.fileName,
839
+ _percent: 100,
840
+ _status: 'success',
841
+ _typeDisabled: false
842
+ }
843
+ row[this.keys.rowKey] = item.beid
844
+ row[this.keys.type] = item.attno
845
+ row[this.keys.name] = item.fileName
846
+ row[this.keys.url] = this.keys.url === this.keys.rowKey ? item.beid : item.filePath || item.beid
847
+ row[this.keys.time] = row[this.keys.time] || dayjs().format('YYYY-MM-DD HH:mm:ss')
848
+ row[this.keys.user] = row[this.keys.user] || userInfo.uname
849
+ return row
850
+ },
760
851
  batchSuccess(response, file, fileList) {
761
852
  // 创建完整的行对象,初始化状态字段
762
853
  let row = {
@@ -1,5 +1,6 @@
1
1
  import axios from './axios'
2
2
  import { getItem, setItem } from './storageEdit'
3
+ import { persistPermissionItems } from './permissionStorage'
3
4
 
4
5
  /**
5
6
  * @description: 按需/按模块 加载权限
@@ -76,13 +77,21 @@ async function asyncGetRelaNos(appNo, rns) {
76
77
  // 将 ssCltrelaNos 重新赋值给 ssCltrelaNos ,并将 ssCltrelaNos 重新写回到 sessionStorage 中
77
78
  // 将 oRelaNos 重新赋值给 ssRelaNosG ,并将 ssRelaNosG 重新写回到 sessionStorage 中
78
79
  // 如果需要同步,则将 isSync 写回到 sessionStorage 中
79
- setItem('relaNos', ssRelaNos, isSync)
80
- setItem('cltrelaNos', ssCltrelaNos, isSync)
81
-
82
80
  let ssRelaNosG = getItem('relaNosGlobal')
83
81
  ssRelaNosG.push(...oRelaNos)
84
- setItem('relaNosGlobal', ssRelaNosG, isSync)
85
- isSync && sessionStorage.setItem('isSync', isSync)
82
+
83
+ if (isSync) {
84
+ await persistPermissionItems([
85
+ { key: 'relaNos', value: ssRelaNos },
86
+ { key: 'cltrelaNos', value: ssCltrelaNos },
87
+ { key: 'relaNosGlobal', value: ssRelaNosG }
88
+ ])
89
+ sessionStorage.setItem('isSync', isSync)
90
+ } else {
91
+ setItem('relaNos', ssRelaNos, false)
92
+ setItem('cltrelaNos', ssCltrelaNos, false)
93
+ setItem('relaNosGlobal', ssRelaNosG, false)
94
+ }
86
95
  } catch (error) {
87
96
  // 如果请求失败,从已加载列表中移除该模块
88
97
  const index = RLFM.indexOf(appNo)
package/src/utils/auth.js CHANGED
@@ -52,7 +52,7 @@ const auth = {
52
52
  }
53
53
  },
54
54
  setReqLang() {
55
- let langMap = { 'zh-cn': 'zh_CN', en: 'en_US', 'zh-hk': 'zh_TW', th: 'th_TH', vi: 'vi_VI' }
55
+ let langMap = { 'zh-cn': 'zh_CN', en: 'en_US', 'zh-hk': 'zh_TW', th: 'th_TH', vi: 'vi_VI', ja: 'ja_JP' }
56
56
  let langKey = window.localStorage.getItem('pageLang') || 'zh-cn'
57
57
  reqLang = langMap[langKey]
58
58
  },
@@ -0,0 +1,296 @@
1
+ /*
2
+ * 用于 n20 权限存储压缩的专用 Worker。
3
+ * 压缩算法兼容 lz-string 1.5.0。
4
+ * 不包含网络、cookie、DOM 或存储访问。
5
+ */
6
+ ;(function () {
7
+ 'use strict'
8
+
9
+ var BASE64_ALPHABET =
10
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
11
+
12
+ function compressToBase64(input) {
13
+ if (input == null) return ''
14
+
15
+ var result = compress(input, 6, function (value) {
16
+ return BASE64_ALPHABET.charAt(value)
17
+ })
18
+
19
+ switch (result.length % 4) {
20
+ case 1:
21
+ return result + '==='
22
+ case 2:
23
+ return result + '=='
24
+ case 3:
25
+ return result + '='
26
+ default:
27
+ return result
28
+ }
29
+ }
30
+
31
+ function compress(uncompressed, bitsPerChar, getCharFromInt) {
32
+ if (uncompressed == null) return ''
33
+
34
+ var index
35
+ var value
36
+ var bitIndex
37
+ var contextDictionary = {}
38
+ var contextDictionaryToCreate = {}
39
+ var contextCharacter = ''
40
+ var contextWordAndCharacter = ''
41
+ var contextWord = ''
42
+ var contextEnlargeIn = 2
43
+ var contextDictionarySize = 3
44
+ var contextNumberOfBits = 2
45
+ var contextData = []
46
+ var contextDataValue = 0
47
+ var contextDataPosition = 0
48
+
49
+ for (index = 0; index < uncompressed.length; index += 1) {
50
+ contextCharacter = uncompressed.charAt(index)
51
+
52
+ if (
53
+ !Object.prototype.hasOwnProperty.call(
54
+ contextDictionary,
55
+ contextCharacter
56
+ )
57
+ ) {
58
+ contextDictionary[contextCharacter] = contextDictionarySize++
59
+ contextDictionaryToCreate[contextCharacter] = true
60
+ }
61
+
62
+ contextWordAndCharacter = contextWord + contextCharacter
63
+ if (
64
+ Object.prototype.hasOwnProperty.call(
65
+ contextDictionary,
66
+ contextWordAndCharacter
67
+ )
68
+ ) {
69
+ contextWord = contextWordAndCharacter
70
+ continue
71
+ }
72
+
73
+ if (
74
+ Object.prototype.hasOwnProperty.call(
75
+ contextDictionaryToCreate,
76
+ contextWord
77
+ )
78
+ ) {
79
+ if (contextWord.charCodeAt(0) < 256) {
80
+ for (bitIndex = 0; bitIndex < contextNumberOfBits; bitIndex++) {
81
+ contextDataValue <<= 1
82
+ if (contextDataPosition === bitsPerChar - 1) {
83
+ contextDataPosition = 0
84
+ contextData.push(getCharFromInt(contextDataValue))
85
+ contextDataValue = 0
86
+ } else {
87
+ contextDataPosition++
88
+ }
89
+ }
90
+
91
+ value = contextWord.charCodeAt(0)
92
+ for (bitIndex = 0; bitIndex < 8; bitIndex++) {
93
+ contextDataValue = (contextDataValue << 1) | (value & 1)
94
+ if (contextDataPosition === bitsPerChar - 1) {
95
+ contextDataPosition = 0
96
+ contextData.push(getCharFromInt(contextDataValue))
97
+ contextDataValue = 0
98
+ } else {
99
+ contextDataPosition++
100
+ }
101
+ value >>= 1
102
+ }
103
+ } else {
104
+ value = 1
105
+ for (bitIndex = 0; bitIndex < contextNumberOfBits; bitIndex++) {
106
+ contextDataValue = (contextDataValue << 1) | value
107
+ if (contextDataPosition === bitsPerChar - 1) {
108
+ contextDataPosition = 0
109
+ contextData.push(getCharFromInt(contextDataValue))
110
+ contextDataValue = 0
111
+ } else {
112
+ contextDataPosition++
113
+ }
114
+ value = 0
115
+ }
116
+
117
+ value = contextWord.charCodeAt(0)
118
+ for (bitIndex = 0; bitIndex < 16; bitIndex++) {
119
+ contextDataValue = (contextDataValue << 1) | (value & 1)
120
+ if (contextDataPosition === bitsPerChar - 1) {
121
+ contextDataPosition = 0
122
+ contextData.push(getCharFromInt(contextDataValue))
123
+ contextDataValue = 0
124
+ } else {
125
+ contextDataPosition++
126
+ }
127
+ value >>= 1
128
+ }
129
+ }
130
+
131
+ contextEnlargeIn--
132
+ if (contextEnlargeIn === 0) {
133
+ contextEnlargeIn = Math.pow(2, contextNumberOfBits)
134
+ contextNumberOfBits++
135
+ }
136
+ delete contextDictionaryToCreate[contextWord]
137
+ } else {
138
+ value = contextDictionary[contextWord]
139
+ for (bitIndex = 0; bitIndex < contextNumberOfBits; bitIndex++) {
140
+ contextDataValue = (contextDataValue << 1) | (value & 1)
141
+ if (contextDataPosition === bitsPerChar - 1) {
142
+ contextDataPosition = 0
143
+ contextData.push(getCharFromInt(contextDataValue))
144
+ contextDataValue = 0
145
+ } else {
146
+ contextDataPosition++
147
+ }
148
+ value >>= 1
149
+ }
150
+ }
151
+
152
+ contextEnlargeIn--
153
+ if (contextEnlargeIn === 0) {
154
+ contextEnlargeIn = Math.pow(2, contextNumberOfBits)
155
+ contextNumberOfBits++
156
+ }
157
+
158
+ contextDictionary[contextWordAndCharacter] = contextDictionarySize++
159
+ contextWord = String(contextCharacter)
160
+ }
161
+
162
+ if (contextWord !== '') {
163
+ if (
164
+ Object.prototype.hasOwnProperty.call(
165
+ contextDictionaryToCreate,
166
+ contextWord
167
+ )
168
+ ) {
169
+ if (contextWord.charCodeAt(0) < 256) {
170
+ for (bitIndex = 0; bitIndex < contextNumberOfBits; bitIndex++) {
171
+ contextDataValue <<= 1
172
+ if (contextDataPosition === bitsPerChar - 1) {
173
+ contextDataPosition = 0
174
+ contextData.push(getCharFromInt(contextDataValue))
175
+ contextDataValue = 0
176
+ } else {
177
+ contextDataPosition++
178
+ }
179
+ }
180
+
181
+ value = contextWord.charCodeAt(0)
182
+ for (bitIndex = 0; bitIndex < 8; bitIndex++) {
183
+ contextDataValue = (contextDataValue << 1) | (value & 1)
184
+ if (contextDataPosition === bitsPerChar - 1) {
185
+ contextDataPosition = 0
186
+ contextData.push(getCharFromInt(contextDataValue))
187
+ contextDataValue = 0
188
+ } else {
189
+ contextDataPosition++
190
+ }
191
+ value >>= 1
192
+ }
193
+ } else {
194
+ value = 1
195
+ for (bitIndex = 0; bitIndex < contextNumberOfBits; bitIndex++) {
196
+ contextDataValue = (contextDataValue << 1) | value
197
+ if (contextDataPosition === bitsPerChar - 1) {
198
+ contextDataPosition = 0
199
+ contextData.push(getCharFromInt(contextDataValue))
200
+ contextDataValue = 0
201
+ } else {
202
+ contextDataPosition++
203
+ }
204
+ value = 0
205
+ }
206
+
207
+ value = contextWord.charCodeAt(0)
208
+ for (bitIndex = 0; bitIndex < 16; bitIndex++) {
209
+ contextDataValue = (contextDataValue << 1) | (value & 1)
210
+ if (contextDataPosition === bitsPerChar - 1) {
211
+ contextDataPosition = 0
212
+ contextData.push(getCharFromInt(contextDataValue))
213
+ contextDataValue = 0
214
+ } else {
215
+ contextDataPosition++
216
+ }
217
+ value >>= 1
218
+ }
219
+ }
220
+
221
+ contextEnlargeIn--
222
+ if (contextEnlargeIn === 0) {
223
+ contextEnlargeIn = Math.pow(2, contextNumberOfBits)
224
+ contextNumberOfBits++
225
+ }
226
+ delete contextDictionaryToCreate[contextWord]
227
+ } else {
228
+ value = contextDictionary[contextWord]
229
+ for (bitIndex = 0; bitIndex < contextNumberOfBits; bitIndex++) {
230
+ contextDataValue = (contextDataValue << 1) | (value & 1)
231
+ if (contextDataPosition === bitsPerChar - 1) {
232
+ contextDataPosition = 0
233
+ contextData.push(getCharFromInt(contextDataValue))
234
+ contextDataValue = 0
235
+ } else {
236
+ contextDataPosition++
237
+ }
238
+ value >>= 1
239
+ }
240
+ }
241
+
242
+ contextEnlargeIn--
243
+ if (contextEnlargeIn === 0) {
244
+ contextEnlargeIn = Math.pow(2, contextNumberOfBits)
245
+ contextNumberOfBits++
246
+ }
247
+ }
248
+
249
+ value = 2
250
+ for (bitIndex = 0; bitIndex < contextNumberOfBits; bitIndex++) {
251
+ contextDataValue = (contextDataValue << 1) | (value & 1)
252
+ if (contextDataPosition === bitsPerChar - 1) {
253
+ contextDataPosition = 0
254
+ contextData.push(getCharFromInt(contextDataValue))
255
+ contextDataValue = 0
256
+ } else {
257
+ contextDataPosition++
258
+ }
259
+ value >>= 1
260
+ }
261
+
262
+ while (true) {
263
+ contextDataValue <<= 1
264
+ if (contextDataPosition === bitsPerChar - 1) {
265
+ contextData.push(getCharFromInt(contextDataValue))
266
+ break
267
+ }
268
+ contextDataPosition++
269
+ }
270
+
271
+ return contextData.join('')
272
+ }
273
+
274
+ self.onmessage = function (event) {
275
+ var message = event.data
276
+ if (
277
+ !message ||
278
+ (typeof message.id !== 'number' && typeof message.id !== 'string') ||
279
+ typeof message.value !== 'string'
280
+ ) {
281
+ return
282
+ }
283
+
284
+ try {
285
+ self.postMessage({
286
+ id: message.id,
287
+ compressed: compressToBase64(message.value)
288
+ })
289
+ } catch (error) {
290
+ self.postMessage({
291
+ id: message.id,
292
+ error: error && error.message ? error.message : 'Compression failed'
293
+ })
294
+ }
295
+ }
296
+ })()
@@ -0,0 +1,138 @@
1
+ import PermissionStorageWorker from 'worker-loader?filename=js/permission-storage-worker.[contenthash:8].js!./permission-storage-worker.v1.js'
2
+ import { setItem } from './storageEdit'
3
+
4
+ const LARGE_STORAGE_THRESHOLD = 524288
5
+ const WORKER_TIMEOUT = 30000
6
+ let compressionWorker = null
7
+ let requestSequence = 0
8
+ const pendingRequests = new Map()
9
+
10
+ function rejectPendingRequests(error) {
11
+ pendingRequests.forEach(({ reject, timeoutId }) => {
12
+ clearTimeout(timeoutId)
13
+ reject(error)
14
+ })
15
+ pendingRequests.clear()
16
+ }
17
+
18
+ function disposeWorker(error) {
19
+ if (compressionWorker) {
20
+ compressionWorker.terminate()
21
+ compressionWorker = null
22
+ }
23
+ rejectPendingRequests(error)
24
+ }
25
+
26
+ function getCompressionWorker() {
27
+ if (compressionWorker) return compressionWorker
28
+ if (typeof Worker === 'undefined') return null
29
+
30
+ try {
31
+ compressionWorker = new PermissionStorageWorker()
32
+ } catch (error) {
33
+ compressionWorker = null
34
+ return null
35
+ }
36
+
37
+ compressionWorker.onmessage = ({ data }) => {
38
+ if (!data) return
39
+
40
+ const pending = pendingRequests.get(data.id)
41
+ if (!pending) return
42
+
43
+ clearTimeout(pending.timeoutId)
44
+ pendingRequests.delete(data.id)
45
+
46
+ if (data.error || typeof data.compressed !== 'string') {
47
+ pending.reject(new Error(data.error || 'Invalid compression result'))
48
+ return
49
+ }
50
+
51
+ pending.resolve(data.compressed)
52
+ }
53
+
54
+ compressionWorker.onerror = () => {
55
+ disposeWorker(new Error('Permission compression worker failed'))
56
+ }
57
+
58
+ return compressionWorker
59
+ }
60
+
61
+ function compressInWorker(value) {
62
+ const worker = getCompressionWorker()
63
+ if (!worker) {
64
+ return Promise.reject(
65
+ new Error('Permission compression worker is unavailable')
66
+ )
67
+ }
68
+
69
+ return new Promise((resolve, reject) => {
70
+ const id = ++requestSequence
71
+ const timeoutId = setTimeout(() => {
72
+ if (!pendingRequests.has(id)) return
73
+ disposeWorker(new Error('Permission compression worker timed out'))
74
+ }, WORKER_TIMEOUT)
75
+
76
+ pendingRequests.set(id, { resolve, reject, timeoutId })
77
+ worker.postMessage({ id, value })
78
+ })
79
+ }
80
+
81
+ function writeCompressedValue(key, compressed) {
82
+ const storage = window.sessionStorage
83
+ const rawSetItem = storage._setItem
84
+ const rawRemoveItem = storage._removeItem
85
+
86
+ if (
87
+ typeof rawSetItem !== 'function' ||
88
+ typeof rawRemoveItem !== 'function'
89
+ ) {
90
+ throw new Error('Extended Storage API is unavailable')
91
+ }
92
+
93
+ try {
94
+ rawSetItem.call(storage, `${key}:[LZ]`, compressed)
95
+ rawRemoveItem.call(storage, key)
96
+ } catch (error) {
97
+ // A previous uncompressed value can consume the remaining quota.
98
+ rawRemoveItem.call(storage, key)
99
+ rawSetItem.call(storage, `${key}:[LZ]`, compressed)
100
+ }
101
+ }
102
+
103
+ async function persistItem({ key, value, serialized }) {
104
+ if (
105
+ typeof serialized !== 'string' ||
106
+ serialized.length <= LARGE_STORAGE_THRESHOLD
107
+ ) {
108
+ window.sessionStorage.setItem(key, serialized)
109
+ return
110
+ }
111
+
112
+ try {
113
+ const compressed = await compressInWorker(serialized)
114
+ writeCompressedValue(key, compressed)
115
+ } catch (error) {
116
+ // Preserve the existing synchronous behavior when Worker is unavailable.
117
+ setItem(key, value, true)
118
+ }
119
+ }
120
+
121
+ /**
122
+ * Persist permission objects with the same cache keys and LZ storage format
123
+ * used by n20-common-lib, while moving large compression work off main thread.
124
+ */
125
+ export async function persistPermissionItems(items) {
126
+ const serializedItems = items.map(({ key, value }) => {
127
+ window[`_keep_${key}`] = value
128
+ return {
129
+ key,
130
+ value,
131
+ serialized: JSON.stringify(value)
132
+ }
133
+ })
134
+
135
+ for (const item of serializedItems) {
136
+ await persistItem(item)
137
+ }
138
+ }