n20-common-lib 3.2.42 → 3.2.44

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 (28) hide show
  1. package/package.json +2 -1
  2. package/src/components/AdvancedFilter/filterItem.vue +1 -0
  3. package/src/components/DateChoose/index.vue +1 -0
  4. package/src/components/DateSelect/index.vue +1 -0
  5. package/src/components/DateSelect/quarterDatePicker.vue +1 -0
  6. package/src/components/DynamicField/DynamicField.vue +1 -0
  7. package/src/components/DynamicField/DynamicFieldOptions.vue +1 -0
  8. package/src/components/DynamicField/contentPop.vue +1 -0
  9. package/src/components/DynamicField/tableList.vue +1 -0
  10. package/src/components/FileUploadTable/aiCheckDialog.vue +1 -0
  11. package/src/components/Pivot/index.vue +2 -1
  12. package/src/components/ProFilterView/advancedQuery.vue +1 -0
  13. package/src/components/ProFilterView/index.vue +1 -0
  14. package/src/components/SelectDatePickerPro/busiDate.vue +1 -0
  15. package/src/components/SelectDatePickerPro/halfYearPicker.vue +1 -0
  16. package/src/components/SelectDatePickerPro/index.vue +1 -0
  17. package/src/components/SelectDatePickerPro/quarterDatePicker.vue +1 -0
  18. package/src/components/TablePro/filterContent_tree.vue +1 -0
  19. package/src/components/TableTransfer/index.vue +1 -0
  20. package/src/components/WornPagination/index.vue +1 -0
  21. package/src/components/operatingStatus/index.vue +1 -0
  22. package/src/components/v3/TablePro/filterContent_tree.vue +1 -0
  23. package/src/components/v3/UploadList/components/aiCheckDialog.vue +1 -0
  24. package/src/plugins/SetMenuTree/setmenutree.vue +1 -0
  25. package/src/utils/amountInWords.js +1 -0
  26. package/src/utils/asyncGetRelaNos.js +14 -5
  27. package/src/utils/permission-storage-worker.v1.js +296 -0
  28. package/src/utils/permissionStorage.js +138 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n20-common-lib",
3
- "version": "3.2.42",
3
+ "version": "3.2.44",
4
4
  "private": false,
5
5
  "scripts": {
6
6
  "serve": "pnpm --dir ../packages/docs run serve",
@@ -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
  },
@@ -16,6 +16,7 @@
16
16
  </template>
17
17
 
18
18
  <script>
19
+ import { $lc } from '../../utils/i18n/index'
19
20
  import importGlobal from '../../utils/importGlobal.js'
20
21
  export default {
21
22
  name: 'FilterItem',
@@ -119,6 +119,7 @@
119
119
  </template>
120
120
 
121
121
  <script>
122
+ import { $lc } from '../../utils/i18n/index'
122
123
  import Dialog from '../Dialog/index.vue'
123
124
  export default {
124
125
  name: 'DateChoose',
@@ -73,6 +73,7 @@
73
73
 
74
74
  <script>
75
75
  // TODO: 双休绑定数值格式不统一
76
+ import { $lc } from '../../utils/i18n/index'
76
77
  import quarterDatePicker from './quarterDatePicker'
77
78
  import dayjs from 'dayjs'
78
79
 
@@ -93,6 +93,7 @@
93
93
  </template>
94
94
 
95
95
  <script>
96
+ import { $lc } from '../../utils/i18n/index'
96
97
  import quarter from 'dayjs/plugin/quarterOfYear'
97
98
  import dayjs from 'dayjs'
98
99
  dayjs.extend(quarter)
@@ -724,6 +724,7 @@
724
724
  </template>
725
725
 
726
726
  <script>
727
+ import { $lc } from '../../utils/i18n/index'
727
728
  import clAnchorItem from '../Anchor/AnchorItem.vue'
728
729
  import clDatePicker from '../DatePicker/index.vue'
729
730
  import clDatePickerPor from '../DatePicker/por.vue'
@@ -130,6 +130,7 @@
130
130
  </template>
131
131
 
132
132
  <script>
133
+ import { $lc } from '../../utils/i18n/index'
133
134
  import ClPage from '../PageLayout/page.vue'
134
135
  import Sortable from 'sortablejs'
135
136
  import customFormOptionsData from './jsonData/customFormOptionsData.json'
@@ -29,6 +29,7 @@
29
29
  </template>
30
30
 
31
31
  <script>
32
+ import { $lc } from '../../utils/i18n/index'
32
33
  import ClPage from '../PageLayout/page.vue'
33
34
  import ClTablePro from '../TablePro/index.vue'
34
35
  export default {
@@ -74,6 +74,7 @@
74
74
  </template>
75
75
 
76
76
  <script>
77
+ import { $lc } from '../../utils/i18n/index'
77
78
  import DynamicTable from './DynamicTable.vue'
78
79
  import clPage from '../PageLayout/page.vue'
79
80
  import clStatisItem from '../Statis/statisItem.vue'
@@ -35,6 +35,7 @@
35
35
  </template>
36
36
 
37
37
  <script>
38
+ import { $lc } from '../../utils/i18n/index'
38
39
  import Dialog from '../Dialog/index.vue'
39
40
  import Table from '../Table/index.vue'
40
41
  import axios from '../../utils/axios.js'
@@ -113,6 +113,7 @@
113
113
  </template>
114
114
 
115
115
  <script>
116
+ import { $lc } from '../../utils/i18n/index'
116
117
  import XEUtils, { merge } from 'xe-utils'
117
118
 
118
119
  import ChartView from './ChartView.vue'
@@ -1540,4 +1541,4 @@ export default {
1540
1541
  }
1541
1542
  }
1542
1543
  }
1543
- </script>
1544
+ </script>
@@ -180,6 +180,7 @@
180
180
  </template>
181
181
 
182
182
  <script>
183
+ import { $lc } from '../../utils/i18n/index'
183
184
  import axios from '../../utils/axios.js'
184
185
  import ClDatePicker from '../DatePicker/index.vue'
185
186
  import ClDatePickerPor from '../DatePicker/por.vue'
@@ -219,6 +219,7 @@
219
219
  </template>
220
220
 
221
221
  <script>
222
+ import { $lc } from '../../utils/i18n/index'
222
223
  import advancedQuery from './advancedQuery.vue'
223
224
 
224
225
  import axios from '../../utils/axios.js'
@@ -30,6 +30,7 @@
30
30
  </template>
31
31
 
32
32
  <script>
33
+ import { $lc } from '../../utils/i18n/index'
33
34
  import dayjs from 'dayjs'
34
35
  export default {
35
36
  name: 'BusiDatePicker',
@@ -68,6 +68,7 @@
68
68
  </template>
69
69
 
70
70
  <script>
71
+ import { $lc } from '../../utils/i18n/index'
71
72
  export default {
72
73
  name: 'HalfYearPicker',
73
74
  props: {
@@ -76,6 +76,7 @@
76
76
 
77
77
  <script>
78
78
  // TODO: 双向绑定数值格式不统一
79
+ import { $lc } from '../../utils/i18n/index'
79
80
  import dayjs from 'dayjs'
80
81
 
81
82
  import HalfYearPicker from './halfYearPicker'
@@ -93,6 +93,7 @@
93
93
  </template>
94
94
 
95
95
  <script>
96
+ import { $lc } from '../../utils/i18n/index'
96
97
  import quarter from 'dayjs/plugin/quarterOfYear'
97
98
  import dayjs from 'dayjs'
98
99
  dayjs.extend(quarter)
@@ -31,6 +31,7 @@
31
31
  </template>
32
32
 
33
33
  <script>
34
+ import { $lc } from '../../utils/i18n/index'
34
35
  export default {
35
36
  name: 'FilterContent',
36
37
  props: {
@@ -25,6 +25,7 @@
25
25
  </template>
26
26
 
27
27
  <script>
28
+ import { $lc } from '../../utils/i18n/index'
28
29
  export default {
29
30
  name: 'TableTransfer',
30
31
  props: {
@@ -5,6 +5,7 @@
5
5
  </template>
6
6
 
7
7
  <script>
8
+ import { $lc } from '../../utils/i18n/index'
8
9
  import Pagination from '../Pagination/index.vue'
9
10
  export default {
10
11
  name: 'WornPagination',
@@ -10,6 +10,7 @@
10
10
  </template>
11
11
 
12
12
  <script>
13
+ import { $lc } from '../../utils/i18n/index'
13
14
  import XEUtils from 'xe-utils'
14
15
  import getJsonc from '../../assets/getJsonc.js'
15
16
  import axios from '../../utils/axios'
@@ -31,6 +31,7 @@
31
31
  </template>
32
32
 
33
33
  <script>
34
+ import { $lc } from '../../../utils/i18n/index'
34
35
  export default {
35
36
  name: 'FilterContent',
36
37
  props: {
@@ -35,6 +35,7 @@
35
35
  </template>
36
36
 
37
37
  <script>
38
+ import { $lc } from '../../../../utils/i18n/index'
38
39
  import axios from '../../../../utils/axios.js'
39
40
  import Dialog from '../../../Dialog/index.vue'
40
41
  import Table from '../../../Table/index.vue'
@@ -149,6 +149,7 @@
149
149
  <script>
150
150
  /* eslint-disable no-unused-vars */
151
151
 
152
+ import { $lc } from '../../utils/i18n/index'
152
153
  import { nanoid } from 'nanoid'
153
154
  import forEachs from '../../utils/forEachs'
154
155
  import list2tree from '../../utils/list2tree'
@@ -1,3 +1,4 @@
1
+ import { $lc } from './i18n/index'
1
2
  import Nzh from 'nzh'
2
3
 
3
4
  export const convert = (
@@ -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)
@@ -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
+ }