@lcap/nasl 3.7.2-alpha.1 → 3.7.2-beta.1

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 (62) hide show
  1. package/out/common/BaseNode.js.map +1 -1
  2. package/out/concepts/ViewElement__.js +8 -8
  3. package/out/concepts/ViewElement__.js.map +1 -1
  4. package/out/generator/genBundleFiles.js +2 -1
  5. package/out/generator/genBundleFiles.js.map +1 -1
  6. package/out/generator/release-body/body.js +2 -1
  7. package/out/generator/release-body/body.js.map +1 -1
  8. package/out/natural/genNaturalTS.js +1 -0
  9. package/out/natural/genNaturalTS.js.map +1 -1
  10. package/out/natural/transformTS2UI.js +4 -2
  11. package/out/natural/transformTS2UI.js.map +1 -1
  12. package/out/server/getLogics.js +1 -1
  13. package/out/server/getLogics.js.map +1 -1
  14. package/out/server/translator.js +7 -0
  15. package/out/server/translator.js.map +1 -1
  16. package/out/service/storage/init.d.ts +1 -1
  17. package/out/service/storage/init.js +71 -22
  18. package/out/service/storage/init.js.map +1 -1
  19. package/out/utils/env.d.ts +2 -0
  20. package/out/utils/env.js +5 -4
  21. package/out/utils/env.js.map +1 -1
  22. package/out/utils/index.d.ts +1 -0
  23. package/out/utils/index.js +14 -1
  24. package/out/utils/index.js.map +1 -1
  25. package/out/utils/language-cache/constant.d.ts +12 -0
  26. package/out/utils/language-cache/constant.js +17 -0
  27. package/out/utils/language-cache/constant.js.map +1 -0
  28. package/out/utils/language-cache/index.d.ts +2 -0
  29. package/out/utils/language-cache/index.js +19 -0
  30. package/out/utils/language-cache/index.js.map +1 -0
  31. package/out/utils/language-cache/nasl.d.ts +10 -0
  32. package/out/utils/language-cache/nasl.js +94 -0
  33. package/out/utils/language-cache/nasl.js.map +1 -0
  34. package/out/utils/language-cache/types.d.ts +40 -0
  35. package/out/utils/language-cache/types.js +4 -0
  36. package/out/utils/language-cache/types.js.map +1 -0
  37. package/out/utils/time-slicing/controller.js.map +1 -1
  38. package/out/utils/types.d.ts +17 -0
  39. package/package.json +2 -1
  40. package/src/common/BaseNode.ts +0 -1
  41. package/src/concepts/ViewElement__.ts +6 -6
  42. package/src/generator/genBundleFiles.ts +2 -1
  43. package/src/generator/release-body/body.ts +2 -1
  44. package/src/natural/genNaturalTS.ts +5 -6
  45. package/src/natural/transformTS2UI.ts +5 -3
  46. package/src/server/getLogics.ts +1 -1
  47. package/src/server/translator.ts +7 -0
  48. package/src/service/storage/init.ts +96 -45
  49. package/src/utils/env.ts +4 -5
  50. package/src/utils/index.ts +2 -0
  51. package/src/utils/language-cache/constant.ts +14 -0
  52. package/src/utils/language-cache/index.ts +2 -0
  53. package/src/utils/language-cache/nasl.ts +77 -0
  54. package/src/utils/language-cache/types.ts +44 -0
  55. package/src/utils/time-slicing/controller.ts +0 -1
  56. package/src/utils/types.ts +20 -0
  57. package/ts-worker/bundle.js +1 -1
  58. package/ts-worker/src/index.js +0 -1
  59. package/out/utils/delay/index.d.ts +0 -0
  60. package/out/utils/delay/index.js +0 -1
  61. package/out/utils/delay/index.js.map +0 -1
  62. package/src/utils/delay/index.ts +0 -0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lcap/nasl",
3
3
  "description": "NetEase Application Specific Language",
4
- "version": "3.7.2-alpha.1",
4
+ "version": "3.7.2-beta.1",
5
5
  "author": "Forrest <rainforest92@126.com>",
6
6
  "scripts": {
7
7
  "clear": "rimraf ./out",
@@ -43,6 +43,7 @@
43
43
  "decimal.js": "10.4.3",
44
44
  "faker": "5.4.0",
45
45
  "fs-extra": "10.1.0",
46
+ "idb": "8.0.0",
46
47
  "json5": "2.2.3",
47
48
  "lodash": "4.17.21",
48
49
  "postcss": "8.4.14",
@@ -30,7 +30,6 @@ const patchCompose = (node: BaseNode, type = 'add') => {
30
30
  }
31
31
  }
32
32
 
33
-
34
33
  type NodePathOption = {
35
34
  useName?: boolean,
36
35
  }
@@ -3283,17 +3283,17 @@ export class ViewElement extends BaseNode {
3283
3283
  if (!styleObj['--constraint-modified'] && !ops.isUpdateConstraintRule) {
3284
3284
  if (Math.abs(staticLeft - staticRight) < threshold) {
3285
3285
  constraintX = 'center';
3286
- } else if (staticLeft < staticRight) {
3287
- constraintX = 'left';
3288
- } else if (staticLeft > staticRight) {
3286
+ } else if (staticLeft > parentWidth/2) {
3289
3287
  constraintX = 'right';
3288
+ } else if (staticRight > parentWidth/2) {
3289
+ constraintX = 'left';
3290
3290
  }
3291
3291
  if (Math.abs(staticTop - staticBottom) < threshold) {
3292
3292
  constraintY = 'center';
3293
- } else if (staticTop < staticBottom) {
3294
- constraintY = 'top';
3295
- } else if (staticTop > staticBottom) {
3293
+ } else if (staticTop > parentHeight/2) {
3296
3294
  constraintY = 'bottom';
3295
+ } else if (staticBottom > parentHeight/2) {
3296
+ constraintY = 'top';
3297
3297
  }
3298
3298
  }
3299
3299
 
@@ -800,7 +800,8 @@ export function genBundleFiles(app: App, frontend: Frontend, config: Config) {
800
800
  const asset = assetsMap.get(url);
801
801
  if (asset) {
802
802
  const basePath = frontend?.basePath;
803
- const assetDir = basePath ? `${basePath}/assets/${asset.name}` : `/assets/${asset.name}`;
803
+ const assetName = encodeURIComponent(asset.name);
804
+ const assetDir = basePath ? `${basePath}/assets/${assetName}` : `/assets/${assetName}`;
804
805
  let path = assetDir;
805
806
  const sysPrefixPath = frontend?.app?.sysPrefixPath;
806
807
  if (sysPrefixPath) {
@@ -79,7 +79,8 @@ async function getNaslAnnotatedJSON(app: App, opt: InternalReleaseData) {
79
79
  node[key] = url.replace(regex, (url) => {
80
80
  const asset = assetsMap.get(url);
81
81
  if (asset) {
82
- const path = `/assets/${asset.name}`;
82
+ const assetName = encodeURIComponent(asset.name);
83
+ const path = `/assets/${assetName}`;
83
84
  globalConfig.assets.push({
84
85
  path,
85
86
  isDir: false,
@@ -22,12 +22,11 @@ import { createCompilerState } from '../translator';
22
22
  export function genNaturalTS(app: App, currentNode?: BaseNode, focusedNodePath?: string) {
23
23
  if (currentNode?.concept === 'Logic') {
24
24
  const logic = currentNode as Logic;
25
- const code = logic?.toNaturalTS(
26
- createCompilerState(undefined, {
27
- focusedNodePath,
28
- autoPrefixName: true,
29
- })
30
- );
25
+ const code = logic?.toNaturalTS(createCompilerState(undefined, {
26
+ focusedNodePath,
27
+ needNamespace: true,
28
+ autoPrefixName: true,
29
+ }));
31
30
  return code;
32
31
  }
33
32
  return getCurrentNodeContextForUI(currentNode).code;
@@ -1,6 +1,6 @@
1
1
  /* eslint-disable global-require */
2
2
  /* eslint-disable @typescript-eslint/no-use-before-define */
3
- import { kebabCase } from 'lodash';
3
+ import { kebabCase, cloneDeep } from 'lodash';
4
4
  import * as babel from '@babel/core';
5
5
  import * as babelTypes from '@babel/types';
6
6
  import generate from '@babel/generator';
@@ -1112,11 +1112,12 @@ function transformNode2ViewConstruct(node: babelTypes.Node): naslTypes.ViewEleme
1112
1112
  } else if (properties?.[2].value.value === 'BindAttribute') {
1113
1113
  newNode = transformNode2Attribute({ key: element.left, value: element.right });
1114
1114
  }
1115
+ const newNodeBackup = cloneDeep(newNode);
1115
1116
  (newNode as any).aiParams = {
1116
1117
  action: properties?.[0].value.value,
1117
1118
  selectedNodeName: properties?.[1].value.value,
1118
1119
  concept: properties?.[2].value.value,
1119
- object: newNode
1120
+ object: newNodeBackup
1120
1121
  };
1121
1122
  newViewElement.push(newNode);
1122
1123
  });
@@ -1152,7 +1153,8 @@ function transformNode2View(node: babelTypes.FunctionDeclaration, root?: babelTy
1152
1153
  if (item.type === 'ReturnStatement' && item?.argument?.type === 'CallExpression') {
1153
1154
  const action = (item.argument as any)?.callee?.name;
1154
1155
  const selectedNodeName = (item.argument as any).arguments?.[0]?.property?.name;
1155
- (viewItem as any).aiParams = { selectedNodeName, action, concept: 'ViewElement', object: viewItem };
1156
+ const newNodeBackup = cloneDeep(viewItem);
1157
+ (viewItem as any).aiParams = { selectedNodeName, action, concept: 'ViewElement', object: newNodeBackup};
1156
1158
  }
1157
1159
  if (item.type === 'ReturnStatement' && item?.argument?.type === 'ArrayExpression') {
1158
1160
  viewItem?.forEach((element: any) => {
@@ -500,7 +500,7 @@ export function getLogicsSync(node: CallLogic | BindEvent | Identifier | Logic,
500
500
  ],
501
501
  expanded: false,
502
502
  });
503
- if (!app.processes.length && !app.processV2s.length) systemTree.children.shift();
503
+ if (!(app.processes?.length) && !(app.processV2s?.length)) systemTree.children.shift();
504
504
 
505
505
  systemTree.expanded = false;
506
506
  systemTree.children.forEach((child) => (child.expanded = false));
@@ -914,6 +914,13 @@ export function naslNodeTranslateMessage(minRange: MinRange, tsErrorDetail: Diag
914
914
  * @param tsErrorDetail
915
915
  */
916
916
  function getConnectorErrorDetail(node: BaseNode, tsErrorDetail: Diagnostic) {
917
+ // 如果message 为纯英文,直接返回
918
+ if (/^[A-Za-z\s]+[.,!?;:]$/.test(tsErrorDetail.message)) {
919
+ return {
920
+ node,
921
+ ...tsErrorDetail,
922
+ };
923
+ }
917
924
  const connector = node.getAncestor('Connector') as Connector;
918
925
  let targetNode = node;
919
926
  let msgPrefix = '';
@@ -4,16 +4,16 @@ import { addBreakpointNodesFromApp } from '../../breakpoint';
4
4
  import { getConceptConstructor } from '../../decorators';
5
5
  import { config } from '../../config';
6
6
  import storageService from './service';
7
+ import { LsCache, BreakpointStatus, FullResponse, isDebugMode, BatchInstructResult } from '../../utils';
7
8
  import { v4 as uuidv4 } from 'uuid';
8
9
  import stepRecorder from '../../manager/stepRecorder';
9
10
  import * as jsoner from './jsoner';
10
11
  /// #if !process.env.NODE_ENV || process.env.BUILD_TARGET === 'node'
11
12
  import * as fs from 'fs-extra';
12
13
  import type { NaslServer } from 'src/server/naslServer';
14
+ import { NaslCacheData, NaslData } from 'src/utils/language-cache';
13
15
  /// #endif
14
16
 
15
-
16
-
17
17
  export const batchQuery = storageService.batchQuery;
18
18
  export const batchAction = storageService.batchAction;
19
19
  export const batchInstruct = storageService.batchInstruct;
@@ -58,12 +58,12 @@ export function doOperationRecord(operation: Operation) {
58
58
  transaction.oncomplete = () => {
59
59
  db.close();
60
60
  };
61
- } catch(err) {
61
+ } catch (err) {
62
62
  console.log(err);
63
63
  }
64
64
  };
65
65
 
66
- request.onerror = function(event) {
66
+ request.onerror = function (event) {
67
67
  console.log('Failed to open database');
68
68
  };
69
69
  }
@@ -228,7 +228,7 @@ export async function operationRecordPlayback(app: any, operationRecordAction: '
228
228
  const pathArr = path?.split('.') || [];
229
229
  const lastPathItem = pathArr?.pop();
230
230
  if (lastPathItem) {
231
- pathArr.push(lastPathItem.replace(/(name=)[^\]]+/, `$1${object?.name}`));
231
+ pathArr.push(lastPathItem.replace(/(name=)[^\]]+/, `$1${object?.name}`));
232
232
  }
233
233
  newPath = pathArr.join('.');
234
234
  }
@@ -254,7 +254,7 @@ export async function operationRecordPlayback(app: any, operationRecordAction: '
254
254
  case 'update':
255
255
  for (const key in oldObject) {
256
256
  if (node) {
257
- (node as any)[key] = oldObject[key] ?? null;
257
+ (node as any)[key] = oldObject[key] ?? null;
258
258
  }
259
259
  }
260
260
  break;
@@ -427,7 +427,7 @@ async function doAction(app: any, actionItem: any) {
427
427
  let hasBackEnd = false;
428
428
  const actionList: any[] = [];
429
429
  const allRiskList: any[] = [];
430
- const { list, actionMsg, action, extra} = actionItem || {};
430
+ const { list, actionMsg, action, extra } = actionItem || {};
431
431
  handleAIPoint(app, actionItem);
432
432
  const itemloop = (_i: LogicItem, app: any, diffArr: string[]) => {
433
433
  const _l = getStatement(_i, app, diffArr);
@@ -543,9 +543,9 @@ async function doAction(app: any, actionItem: any) {
543
543
  const isDelete = event?.originEvent?.action === 'delete';
544
544
  const isAuthView = (emitTarget as View).auth && event?.originEvent?.object?.name;
545
545
  const isUpdateAuth = event?.originEvent?.action === 'update' && "auth" in (event?.originEvent?.object || {});
546
- const isUpdateAuthDes = event?.originEvent?.action === 'update' && "authDescription" in (event?.originEvent?.object || {});
546
+ const isUpdateAuthDes = event?.originEvent?.action === 'update' && "authDescription" in (event?.originEvent?.object || {});
547
547
  const isUpdateBindRoles = event?.originEvent?.action === 'update' && event?.originEvent?.object && event?.originEvent?.object?.bindRoles;
548
- if (isDelete || isUpdateAuth || isUpdateBindRoles || isAuthView||isUpdateAuthDes) {
548
+ if (isDelete || isUpdateAuth || isUpdateBindRoles || isAuthView || isUpdateAuthDes) {
549
549
  hasBackEnd = true;
550
550
  // const app = emitTarget.app;
551
551
  // const diffArr: string[] = [];
@@ -566,7 +566,7 @@ async function doAction(app: any, actionItem: any) {
566
566
  hasFrontEnd = true;
567
567
  }
568
568
  if (emitTarget.concept === 'ViewElement') {
569
- if(event?.originEvent?.action === 'update' || event?.originEvent?.action === 'delete'){
569
+ if (event?.originEvent?.action === 'update' || event?.originEvent?.action === 'delete') {
570
570
  if ('authDescription' in (event?.originEvent?.object ?? {})) {
571
571
  hasBackEnd = true;
572
572
  }
@@ -597,7 +597,7 @@ async function doAction(app: any, actionItem: any) {
597
597
  }
598
598
  if (emitTarget.concept === 'BindAttribute' && emitTarget.name === 'url') {
599
599
  const needUpdateBackEnd =
600
- (emitTarget as BindAttribute)?.value?.endsWith('/import') || (emitTarget as BindAttribute)?.value?.includes('/upload/')
600
+ (emitTarget as BindAttribute)?.value?.endsWith('/import') || (emitTarget as BindAttribute)?.value?.includes('/upload/')
601
601
  if (!(emitTarget as BindAttribute).view.parentAuth && needUpdateBackEnd) {
602
602
  hasBackEnd = true
603
603
  }
@@ -888,7 +888,7 @@ function ensureSafe(instructList: any[]) {
888
888
  instruct?.actions?.forEach((actionItem: any) => {
889
889
  const { path, action, object } = actionItem || {};
890
890
  if (path === 'app') {
891
- switch(action) {
891
+ switch (action) {
892
892
  case 'create':
893
893
  riskList.push('重复创建App');
894
894
  break;
@@ -918,7 +918,8 @@ async function _saveNasl(options: TaskOption) {
918
918
  } else if (hasBackEnd) {
919
919
  ChangedNASLType = 'backend';
920
920
  }
921
- let res: any;
921
+
922
+ let res: BatchInstructResult;
922
923
  let err: any;
923
924
 
924
925
  if (config.storage.protocol === 'http') {
@@ -996,6 +997,13 @@ async function _saveNasl(options: TaskOption) {
996
997
  });
997
998
  }
998
999
  app.emit?.('saved', err);
1000
+
1001
+ // 更新缓存
1002
+ await LsCache.updateNaslCache(app.id, {
1003
+ ...res,
1004
+ getData: () => cloneDeep(app.toJSON()),
1005
+ });
1006
+
999
1007
  return err;
1000
1008
  }
1001
1009
 
@@ -1138,61 +1146,104 @@ export function handleApp(app: ProxyApp) {
1138
1146
  });
1139
1147
  }
1140
1148
 
1141
- type BreakpointItem =
1142
- | any
1143
- | {
1144
- path: string
1145
- breakpointStatus: null | 'ENABLED' | 'DISABLED'
1146
- }
1149
+ async function getBatchAdditionalData(appId: string) {
1150
+ const { data: { result: [globalChangedTime] }, headers }: FullResponse<number> = await storageService.batchQuery({
1151
+ body: [
1152
+ {
1153
+ path: 'app.globalChangedTime',
1154
+ },
1155
+ ],
1156
+ headers: {
1157
+ appId,
1158
+ checkTabOpenTime: 'true',
1159
+ },
1160
+ config: {
1161
+ shortResponse: false
1162
+ },
1163
+ });
1164
+
1165
+ return {
1166
+ globalChangedTime,
1167
+ tabTimestamp: headers['tabtimestamp'],
1168
+ };
1169
+ }
1170
+
1171
+ async function getNaslFromServer(appId: string, globalChangedTime: number | null) {
1172
+ const [naslData] = await storageService.batchQuery({
1173
+ body: [
1174
+ {
1175
+ path: 'app',
1176
+ },
1177
+ ],
1178
+ headers: {
1179
+ appId,
1180
+ },
1181
+ });
1182
+
1183
+ // 后端没有此值时,表示为存量应用,不可缓存
1184
+ if (!globalChangedTime) {
1185
+ return naslData;
1186
+ }
1187
+
1188
+ await LsCache.updateNaslCache(appId, {
1189
+ getData: () => naslData,
1190
+ // 初次缓存时此值无用,故赋值为 0
1191
+ preGlobalChangedTime: 0,
1192
+ globalChangedTime: globalChangedTime,
1193
+ });
1194
+
1195
+ return naslData as NaslData;
1196
+ }
1197
+
1198
+ async function getNaslFromCache(appId: string) {
1199
+ return LsCache.getNaslCacheWithoutCheck(appId).then((data) => data.data);
1200
+ }
1147
1201
 
1148
1202
  /**
1149
1203
  * 加载 app
1150
1204
  * @param appId 如果是从文件读,就不需要传
1151
1205
  * @returns app 对象
1152
1206
  */
1153
- export async function loadApp(appId?: string) {
1207
+ export async function loadApp(appId: string) {
1154
1208
  try {
1155
1209
  // 删除失效数据
1156
1210
  deleteExpiredRecords();
1157
- } catch(err) { }
1211
+ } catch (err) { }
1158
1212
  let app: App;
1159
1213
  if (config.storage.protocol === 'http') {
1160
1214
  console.time('batchQuery');
1215
+ const additionalData = await getBatchAdditionalData(appId);
1216
+ const useCache = await LsCache.canUseNaslCache(appId, additionalData.globalChangedTime);
1161
1217
  const promises = [
1162
- storageService.batchQuery({
1163
- body: [
1164
- {
1165
- path: 'app'
1166
- // excludes: ['views'],
1167
- }
1168
- ],
1169
- headers: {
1170
- appId,
1171
- checkTabOpenTime: 'true'
1172
- // 其他封装在 storageService 里了
1173
- },
1174
- config: {
1175
- shortResponse: false
1176
- }
1177
- }),
1218
+ useCache
1219
+ ? getNaslFromCache(appId)
1220
+ : getNaslFromServer(appId, additionalData.globalChangedTime),
1178
1221
  storageService.breakpoint({
1179
1222
  body: { appId }
1180
- })
1181
- ]
1223
+ }),
1224
+ useCache ? Promise.resolve() : LsCache.clearCache(appId),
1225
+ ] as [Promise<LsCache.NaslCacheData>, Promise<BreakpointStatus[]>, Promise<void>];
1226
+
1227
+ if (isDebugMode) {
1228
+ if (useCache) {
1229
+ console.log('命中 Nasl 前端缓存');
1230
+ }
1231
+ else {
1232
+ console.log('未命中 Nasl 前端缓存');
1233
+ }
1234
+ }
1182
1235
 
1183
1236
  const [batchQueryRes, breakpointRes] = await Promise.all(promises)
1184
1237
 
1185
1238
  // 请求
1186
1239
  console.timeEnd('batchQuery');
1187
-
1188
- tabTimestamp = batchQueryRes?.headers?.tabtimestamp;
1189
- const data = batchQueryRes?.data?.result;
1240
+ tabTimestamp = additionalData.tabTimestamp;
1190
1241
 
1191
1242
  console.time('new App');
1192
- app = new App(Object.assign(data?.[0] || {}, { id: appId }));
1243
+ app = new App(Object.assign(batchQueryRes, { id: appId }));
1193
1244
 
1194
- breakpointRes?.forEach?.((item: BreakpointItem = {}) => {
1195
- const { path, breakpointStatus } = item
1245
+ breakpointRes?.forEach?.((item) => {
1246
+ const { path, breakpointStatus } = item ?? {};
1196
1247
 
1197
1248
  if (breakpointStatus) {
1198
1249
  const node = app.findNodeByPath(path)
package/src/utils/env.ts CHANGED
@@ -2,10 +2,9 @@
2
2
  export const isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined';
3
3
  /** Node 环境 */
4
4
  export const isNode = process.env.BUILD_TARGET === 'node' || !globalThis.window;
5
+ /** 本地模式 */
6
+ export const isLocalMode = isBrowser && location.hostname.includes('localhost');
5
7
  /** 调试模式 */
6
- export const isDebugMode = isBrowser && /[?&]debug=true/.test(window.location.search);
8
+ export const isDebugMode = isBrowser && (/[?&]debug=true/.test(location.search) || isLocalMode);
7
9
  /** 是否是浏览器测试环境 */
8
- export const isTestBrowser = isBrowser && (
9
- /codewave-(test|dev)\.163yun\.com$/.test(location.hostname) ||
10
- location.hostname.includes('localhost')
11
- );
10
+ export const isTestBrowser = isBrowser && (/codewave-(test|dev)\.163yun\.com$/.test(location.hostname) || isLocalMode);
@@ -16,6 +16,8 @@ export * from './env';
16
16
  export * from './file';
17
17
  export * from './time-slicing';
18
18
 
19
+ export * as LsCache from './language-cache';
20
+
19
21
  // 查找最近的模块
20
22
  export function findClosestModule(node: BaseNode) {
21
23
  let moduleNode = node;
@@ -0,0 +1,14 @@
1
+ /** 数据库名称 */
2
+ export const DbName = 'language-cache';
3
+
4
+ /**
5
+ * 缓存时长
6
+ *
7
+ * @description 3 天时间
8
+ */
9
+ export const NaslCacheLimit = 3 * 24 * 60 * 60 * 1000;
10
+
11
+ /** 数据库储存表 */
12
+ export enum StoreName {
13
+ Nasl = 'Nasl',
14
+ }
@@ -0,0 +1,2 @@
1
+ export * from './nasl';
2
+ export * from './types';
@@ -0,0 +1,77 @@
1
+ import { isNode } from '../env';
2
+ import { StoreName, DbName, NaslCacheLimit } from './constant';
3
+ import { LanguageDatabase, NaslCacheData, NaslCacheUpdateData } from './types';
4
+
5
+ /** 缓存数据库 */
6
+ const database = isNode
7
+ ? undefined
8
+ : import('idb').then((idb) => idb.openDB<LanguageDatabase>(DbName, 1, {
9
+ upgrade(db) {
10
+ db.createObjectStore(StoreName.Nasl);
11
+ },
12
+ blocked(currentVersion, blockedVersion, event) {
13
+ // TODO: 超出容量限制
14
+ },
15
+ }));
16
+
17
+ export async function canUseNaslCache(appId: string, globalChangedTime: number) {
18
+ const data = await getNaslCacheWithoutCheck(appId);
19
+ const now = Date.now();
20
+
21
+ if (!data || data.forceUpdate || ((now - data.createdTime) > NaslCacheLimit)) {
22
+ return false;
23
+ }
24
+
25
+ // 缓存等于服务端,则表示可以使用缓存
26
+ return data.updatedTime === globalChangedTime;
27
+ }
28
+
29
+ /**
30
+ * 取出缓存
31
+ *
32
+ * @description 只是取出缓存,并不校验缓存的有效性
33
+ */
34
+ export async function getNaslCacheWithoutCheck(appId: string) {
35
+ if (isNode) {
36
+ return;
37
+ }
38
+
39
+ const db = await database;
40
+ const data = await db.get(StoreName.Nasl, appId);
41
+ return data;
42
+ }
43
+
44
+ export async function clearCache(appId: string) {
45
+ if (isNode) {
46
+ return;
47
+ }
48
+
49
+ const db = await database;
50
+ await db.delete(StoreName.Nasl, appId);
51
+ }
52
+
53
+ export async function updateNaslCache(appId: string, data: NaslCacheUpdateData) {
54
+ if (isNode) {
55
+ return;
56
+ }
57
+
58
+ const db = await database;
59
+ const cache = await db.get(StoreName.Nasl, appId);
60
+
61
+ // 数据库中数据为新的,不更新缓存
62
+ if (cache && (data.globalChangedTime <= cache.updatedTime)) {
63
+ return;
64
+ }
65
+
66
+ const oldGlobalChangedTime = cache?.updatedTime ?? 0;
67
+ const newData: NaslCacheData = {
68
+ id: appId,
69
+ data: data.getData(),
70
+ updatedTime: data.globalChangedTime,
71
+ forceUpdate: cache ? data.preGlobalChangedTime !== oldGlobalChangedTime : false,
72
+ createdTime: cache?.createdTime ?? Date.now(),
73
+ };
74
+
75
+ // await db.delete(StoreName.Nasl, appId);
76
+ await db.put(StoreName.Nasl, newData, appId);
77
+ }
@@ -0,0 +1,44 @@
1
+ import { DBSchema } from 'idb';
2
+ import { StoreName } from './constant';
3
+
4
+ export type NaslData = object;
5
+
6
+ export interface NaslCacheData {
7
+ /** 应用编号 appId */
8
+ id: string;
9
+ /** 开始缓存的时间 */
10
+ createdTime: number;
11
+ /**
12
+ * 变更时间
13
+ *
14
+ * @description 后端为空时,默认为当前时间
15
+ */
16
+ updatedTime: number;
17
+ /**
18
+ * 强制弃用缓存标志位
19
+ *
20
+ * 在某些特殊情况下,比如 A、B 两个人,A 先改了页面 PageA,然后 B 改了页面 PageB,再然后 A 又修改了页面 PageA。
21
+ * 此时 A 的电脑上标志位是最新的,所以他刷新页面是会走缓存,而不会走后端。
22
+ * 此时两个后端的 Nasl 已经不同,但是因为两个人改的是不同的页面,所以继续走缓存也没关系。
23
+ * 当 A 进入 B 页面的时候,后端会判定产生互锁,让 A 强制刷新页面,此时 A 的标志位是最新的,所以 A 即便刷新页面也会走缓存,
24
+ * 所以这里就需要另外的标志位来让 A 强制不走缓存。
25
+ */
26
+ forceUpdate: boolean;
27
+ /** 应用 Nasl 数据 */
28
+ data: NaslData;
29
+ }
30
+
31
+ export interface NaslCacheUpdateData {
32
+ preGlobalChangedTime: number;
33
+ globalChangedTime: number;
34
+ getData: () => NaslData;
35
+ }
36
+
37
+ export interface LanguageDatabase extends DBSchema {
38
+ /** Nasl 缓存数据库 */
39
+ [StoreName.Nasl]: {
40
+ /** AppId 为键名 */
41
+ key: string;
42
+ value: NaslCacheData;
43
+ };
44
+ }
@@ -16,7 +16,6 @@ import {
16
16
  NextState,
17
17
  TheoreticalLimitCalculationCoefficient as Coefficient,
18
18
  } from './constant';
19
- import { delayInWorker } from '../delay';
20
19
 
21
20
  /** 时间分片控制器 */
22
21
  export class TimeSlicingController {
@@ -15,3 +15,23 @@ export interface FileNode extends BaseNode {
15
15
  }
16
16
 
17
17
  export type EmbeddedTSFileGenerator = Generator<void, EmbeddedTSFileResult, string>;
18
+
19
+ export interface FullResponse<D> {
20
+ headers: Record<string, string>;
21
+ data: {
22
+ code: number;
23
+ msg: string;
24
+ success: boolean;
25
+ result: D[];
26
+ };
27
+ }
28
+
29
+ export interface BreakpointStatus {
30
+ path: string;
31
+ breakpointStatus: null | 'ENABLED' | 'DISABLED'
32
+ }
33
+
34
+ export interface BatchInstructResult {
35
+ globalChangedTime: number;
36
+ preGlobalChangedTime: number;
37
+ }