@lcap/nasl 3.3.2-beta.2 → 3.3.2-beta.4

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 (51) hide show
  1. package/out/bak/translator.js +31 -8
  2. package/out/bak/translator.js.map +1 -1
  3. package/out/common/BaseNode.js +2 -1
  4. package/out/common/BaseNode.js.map +1 -1
  5. package/out/concepts/CallFunction__.js +1 -1
  6. package/out/concepts/CallFunction__.js.map +1 -1
  7. package/out/concepts/CallLogic__.js +2 -46
  8. package/out/concepts/CallLogic__.js.map +1 -1
  9. package/out/concepts/MemberExpression__.js +2 -2
  10. package/out/concepts/MemberExpression__.js.map +1 -1
  11. package/out/concepts/TypeAnnotation__.js +21 -21
  12. package/out/concepts/TypeAnnotation__.js.map +1 -1
  13. package/out/concepts/ViewElement__.js +9 -5
  14. package/out/concepts/ViewElement__.js.map +1 -1
  15. package/out/concepts/View__.d.ts +1 -0
  16. package/out/concepts/View__.js +15 -0
  17. package/out/concepts/View__.js.map +1 -1
  18. package/out/index.d.ts +1 -1
  19. package/out/index.js +5 -1
  20. package/out/index.js.map +1 -1
  21. package/out/natural/transformTSCode.js +27 -5
  22. package/out/natural/transformTSCode.js.map +1 -1
  23. package/out/server/createUiTs.js +1 -1
  24. package/out/server/createUiTs.js.map +1 -1
  25. package/out/service/creator/add.configs.js +3 -0
  26. package/out/service/creator/add.configs.js.map +1 -1
  27. package/out/service/storage/init.d.ts +20 -0
  28. package/out/service/storage/init.js +284 -9
  29. package/out/service/storage/init.js.map +1 -1
  30. package/out/utils/index.d.ts +1 -0
  31. package/out/utils/index.js +14 -1
  32. package/out/utils/index.js.map +1 -1
  33. package/package.json +7 -7
  34. package/sandbox-natural/stdlib/nasl.util.ts +1 -0
  35. package/src/bak/translator.js +42 -21
  36. package/src/common/BaseNode.ts +2 -1
  37. package/src/concepts/CallFunction__.ts +1 -1
  38. package/src/concepts/CallLogic__.ts +3 -48
  39. package/src/concepts/MemberExpression__.ts +2 -2
  40. package/src/concepts/TypeAnnotation__.ts +21 -21
  41. package/src/concepts/ViewElement__.ts +9 -6
  42. package/src/concepts/View__.ts +16 -0
  43. package/src/index.ts +1 -1
  44. package/src/natural/transformTSCode.ts +26 -6
  45. package/src/server/createUiTs.ts +1 -1
  46. package/src/service/creator/add.configs.js +6 -0
  47. package/src/service/storage/init.ts +292 -10
  48. package/src/utils/index.ts +13 -0
  49. package/test/concepts/member-expression/__snapshots__/toJS.spec.ts.snap +1 -1
  50. package/test/concepts/member-expression/__snapshots__/toVue.spec.ts.snap +1 -1
  51. package/test/concepts/view-element/__snapshots__/toVue.spec.ts.snap +1 -22
@@ -1,3 +1,4 @@
1
+ import { cloneDeep } from 'lodash';
1
2
  import { App, LogicItem, Logic, Module, View, ViewElement, Argument, SelectMembers, SynatxNode, BindDirective, CallLogic } from '../../concepts';
2
3
  import { addBreakpointNodesFromApp } from '../../breakpoint';
3
4
  import { getConceptConstructor } from '../../decorators';
@@ -5,9 +6,9 @@ import { config } from '../../config';
5
6
  import storageService from './service';
6
7
  import { v4 as uuidv4 } from 'uuid';
7
8
  import stepRecorder from '../../manager/stepRecorder';
9
+ import * as jsoner from './jsoner';
8
10
  /// #if !process.env.NODE_ENV || process.env.BUILD_TARGET === 'node'
9
11
  import * as fs from 'fs-extra';
10
- import * as jsoner from './jsoner';
11
12
  import type { NaslServer } from 'src/server/naslServer';
12
13
  /// #endif
13
14
 
@@ -21,6 +22,228 @@ export const breakpoint = storageService.breakpoint;
21
22
  let tabTimestamp: string;
22
23
  export const databaseTypes = storageService.databaseTypes;
23
24
 
25
+ export const operationRecordInfoMap: Map<string, any> = new Map();
26
+
27
+ // 操作栈记录
28
+ export function doOperationRecord(operation: Operation) {
29
+ const request = indexedDB.open('naslData', 1);
30
+
31
+ request.onupgradeneeded = (event: any) => {
32
+ const db = event.target.result;
33
+ const objectStore = db.createObjectStore('operationRecord', { autoIncrement: true });
34
+ objectStore.createIndex('appId', 'appId');
35
+ objectStore.createIndex('timestamp', 'timestamp');
36
+ };
37
+
38
+ request.onsuccess = (event: any) => {
39
+ const db = event.target.result;
40
+ const transaction = db.transaction('operationRecord', 'readwrite');
41
+ const objectStore = transaction.objectStore('operationRecord');
42
+ operation.appId = operationRecordInfoMap.get('appId');
43
+ operation.branchId = operationRecordInfoMap.get('branchId');
44
+ operation.accountId = operationRecordInfoMap.get('accountId');
45
+ operation.userId = operationRecordInfoMap.get('userId');
46
+ operation.phone = operationRecordInfoMap.get('phone');
47
+ // 将时间戳添加到记录中
48
+ operation.timestamp = Date.now();
49
+ // 这份数据不存在向上的引用,因此用cloneDeep没有关系
50
+ objectStore.put(cloneDeep(operation));
51
+ transaction.oncomplete = () => {
52
+ db.close();
53
+ };
54
+ };
55
+
56
+ request.onerror = function(event) {
57
+ console.log('Failed to open database');
58
+ };
59
+ }
60
+
61
+ // 删除失效数据
62
+ async function deleteExpiredRecords() {
63
+ return new Promise((resolve, reject) => {
64
+ const request = indexedDB.open('naslData', 1);
65
+
66
+ request.onupgradeneeded = (event: any) => {
67
+ const db = event.target.result;
68
+ const objectStore = db.createObjectStore('operationRecord', { autoIncrement: true });
69
+ objectStore.createIndex('appId', 'appId');
70
+ objectStore.createIndex('timestamp', 'timestamp');
71
+ };
72
+
73
+ request.onsuccess = (event: any) => {
74
+ const db = event.target.result;
75
+ const transaction = db.transaction('operationRecord', 'readwrite');
76
+ const objectStore = transaction.objectStore('operationRecord');
77
+ const index = objectStore.index('timestamp');
78
+ // 设置有效期,默认7天
79
+ const localStorageExpiresAt = window.localStorage.getItem('expiresAt');
80
+ const expiresAt = Date.now() - (localStorageExpiresAt ? +localStorageExpiresAt : 7 * 24 * 60 * 60 * 1000);
81
+ const range = IDBKeyRange.upperBound(expiresAt);
82
+ const expiredRecordsRequest = index.openCursor(range);
83
+
84
+ expiredRecordsRequest.onsuccess = (event: any) => {
85
+ const cursor = event.target.result;
86
+ if (cursor) {
87
+ console.log(cursor);
88
+ objectStore.delete(cursor.primaryKey);
89
+ cursor.continue();
90
+ } else {
91
+ resolve(void 0);
92
+ }
93
+ };
94
+
95
+ transaction.oncomplete = () => {
96
+ db.close();
97
+ };
98
+ };
99
+
100
+ request.onerror = (event) => {
101
+ reject('Failed to open database');
102
+ };
103
+ });
104
+ }
105
+
106
+ // 操作栈查询
107
+ export function operationRecordQuery(app: any) {
108
+ return new Promise((resolve, reject) => {
109
+ const appId = app?.id;
110
+ const request = indexedDB.open('naslData', 1);
111
+ request.onsuccess = (event: any) => {
112
+ const db = event.target.result;
113
+ const transaction = db.transaction('operationRecord', 'readonly');
114
+ const objectStore = transaction.objectStore('operationRecord');
115
+ const index = objectStore.index('appId'); // 查询 'appId' 索引
116
+ const range = IDBKeyRange.only(appId);
117
+ const request = index.openCursor(range);
118
+ const result: any[] = [];
119
+
120
+ request.onsuccess = (event: any) => {
121
+ const cursor = event.target.result;
122
+ if (cursor) {
123
+ const record = cursor.value;
124
+ const branchId = operationRecordInfoMap.get('branchId');
125
+ if (record.branchId === branchId) {
126
+ result.push(record);
127
+ }
128
+ cursor.continue();
129
+ } else {
130
+ console.log('操作记录栈:', result);
131
+ resolve(result);
132
+ }
133
+ };
134
+
135
+ transaction.oncomplete = () => {
136
+ db.close();
137
+ };
138
+ };
139
+
140
+ request.onerror = (event) => {
141
+ reject('Failed to open database');
142
+ };
143
+ });
144
+ }
145
+
146
+ // 获取父对象
147
+ function getParentInfo(appJson: any, path: string) {
148
+ const pathArr: string[] = [];
149
+ (path?.split('.') || []).forEach((pathItem: string) => {
150
+ const arrayPropertyKey = pathItem.split('[')[0];
151
+ const matchArr = pathItem.match(/\[(.+?)\]/);
152
+ if (matchArr) {
153
+ pathArr.push(arrayPropertyKey, matchArr[0]);
154
+ } else {
155
+ pathArr.push(pathItem);
156
+ }
157
+ });
158
+ pathArr.pop();
159
+ let parentPath = '';
160
+ pathArr.forEach((pathItem: string, index: number) => {
161
+ const matchArr = pathItem.match(/\[(.+?)\]/);
162
+ if (matchArr || index === 0) {
163
+ parentPath += pathItem;
164
+ } else {
165
+ parentPath += `.${pathItem}`
166
+ }
167
+ });
168
+ const parentNode = jsoner.queryNodeByPath(appJson, parentPath);
169
+ return {
170
+ parentPath,
171
+ parentNode,
172
+ };
173
+ }
174
+
175
+ // 操作记录回放
176
+ export async function operationRecordPlayback(app: any, action: 'undo' | 'redo') {
177
+ const operationRecord: any = await operationRecordQuery(app);
178
+ let operationRecordIndex = operationRecordInfoMap.get('operationRecordIndex');
179
+ if ([null, undefined].includes(operationRecordIndex)) {
180
+ operationRecordIndex = operationRecord?.length;
181
+ }
182
+ const appJson = app.toJSON();
183
+ if (action === 'undo') {
184
+ if (operationRecordIndex > 0) {
185
+ const currentRecordItem = operationRecord?.[operationRecordIndex];
186
+ const { type, action: recordItemAction } = currentRecordItem || {};
187
+ // 多人协作
188
+ if (type === 'cooperation' && recordItemAction === 'confirmPull') {
189
+ console.log(`当前所处栈位置:${operationRecordIndex},已经进行了多人协作“合并”操作,无法继续对操作栈进行回放!`);
190
+ return;
191
+ }
192
+ operationRecordIndex--;
193
+ } else {
194
+ console.log(`当前所处栈位置:${operationRecordIndex},无法继续回放`);
195
+ console.log('当前appJSON:', appJson);
196
+ return;
197
+ }
198
+ } else {
199
+ if (operationRecordIndex < operationRecord?.length) {
200
+ operationRecordIndex++;
201
+ } else {
202
+ console.log(`当前所处栈位置:${operationRecordIndex},无法继续前进`);
203
+ console.log('当前appJSON:', appJson);
204
+ return;
205
+ }
206
+ }
207
+ operationRecordInfoMap.set('operationRecordIndex', operationRecordIndex);
208
+ const currentIndex = operationRecordIndex;
209
+ ([...operationRecord].splice(currentIndex, operationRecord?.length) || []).reverse()?.forEach((recordItem: any) => {
210
+ const { actionItem } = recordItem || {};
211
+ const { list } = actionItem || {};
212
+ [...(list || [])].reverse().forEach((actionItem: any) => {
213
+ const { path, action, oldObject, parentKey, index: oldIndex } = actionItem || {};
214
+ const { parentNode } = getParentInfo(appJson, path);
215
+ const node = jsoner.queryNodeByPath(appJson, path);
216
+ switch (action) {
217
+ case 'create':
218
+ if (Array.isArray(parentNode)) {
219
+ const index = parentNode.indexOf(node);
220
+ if (index !== -1) {
221
+ parentNode.splice(index, 1);
222
+ }
223
+ } else {
224
+ parentNode[parentKey] = null;
225
+ }
226
+ break;
227
+ case 'delete':
228
+ if (Array.isArray(parentNode) && oldIndex !== -1) {
229
+ parentNode.splice(oldIndex, 0, oldObject);
230
+ } else {
231
+ parentNode[parentKey] = oldObject;
232
+ }
233
+ break;
234
+ case 'update':
235
+ for (const key in oldObject) {
236
+ (node as any)[key] = oldObject[key] ?? null;
237
+ }
238
+ break;
239
+ }
240
+ });
241
+ });
242
+ console.log('当前所处栈位置:', currentIndex);
243
+ console.log('当前appJSON:', appJson);
244
+ return appJson;
245
+ }
246
+
24
247
  function getLogic(key: string, app: any, diffLogicList: string[]) {
25
248
  if (!diffLogicList.includes(key)) {
26
249
  diffLogicList.push(key);
@@ -47,6 +270,7 @@ function getLogic(key: string, app: any, diffLogicList: string[]) {
47
270
  }
48
271
  }
49
272
  }
273
+
50
274
  /* 获取LogicITEM */
51
275
  function getStatement(state: any, app: any, diffLogicList: string[]) {
52
276
  let logicItems = [];
@@ -149,6 +373,11 @@ const handleAIPoint = (app: any, actionItem: any) => {
149
373
  * 执行更新
150
374
  */
151
375
  async function doAction(app: any, actionItem: any) {
376
+ const isOperationRecord = operationRecordInfoMap.get('isOperationRecord');
377
+ if (isOperationRecord) {
378
+ console.log('正在进行操作栈回放操作,如需行任何Nasl操作请刷新页面');
379
+ return;
380
+ }
152
381
  let hasFrontEnd = false;
153
382
  let hasBackEnd = false;
154
383
  const actionList: any[] = [];
@@ -375,19 +604,39 @@ async function doAction(app: any, actionItem: any) {
375
604
  return;
376
605
  }
377
606
 
607
+ const uuid = uuidv4().replace(/-/g, '');
378
608
  const instructList = [{
379
- uuid: uuidv4().replace(/-/g, ''),
609
+ uuid,
380
610
  actions: actionList,
381
611
  }];
382
- saveNasl({ app, hasFrontEnd, hasBackEnd, instructList });
383
-
612
+ const operation: Operation = {
613
+ type: 'doAction',
614
+ uuid,
615
+ actionItem: {
616
+ actionMsg,
617
+ list: list.map((item: any) => {
618
+ const { path, action, object, oldObject, parentKey, index } = item?.originEvent || {};
619
+ return {
620
+ path,
621
+ action,
622
+ parentKey,
623
+ index,
624
+ object: object?.toJSON?.() || object,
625
+ oldObject: oldObject?.toJSON?.() || oldObject,
626
+ }
627
+ }),
628
+ },
629
+ };
630
+ saveNasl({ app, hasFrontEnd, hasBackEnd, instructList, operationList: [operation] });
631
+ // 进行nasl操作后需要将Index重置
632
+ operationRecordInfoMap.set('operationRecordIndex', null);
384
633
  if (!app._historying) {
385
634
  if (app._historyIndex !== app._historyList.length) {
386
635
  app._historyList = app._historyList.splice(0, app._historyIndex);
387
636
  }
388
637
  app._historyList.push({
389
638
  actionMsg,
390
- list: list.map((item: any) => item.originEvent),
639
+ list: list.map((item: any) => item?.originEvent),
391
640
  });
392
641
  app._historyIndex = app._historyList.length;
393
642
  } else {
@@ -414,6 +663,7 @@ type TaskOption = {
414
663
  hasFrontEnd: boolean,
415
664
  hasBackEnd: boolean,
416
665
  instructList: Instruct[],
666
+ operationList: Operation[],
417
667
  };
418
668
  enum TaskQueueStatus {
419
669
  ExceedMaxTaskCount,
@@ -461,6 +711,7 @@ class TaskQueue {
461
711
  let hasFrontEnd = false;
462
712
  let hasBackEnd = false;
463
713
  let instructList: Instruct[] = [];
714
+ let operationList: Operation[] = [];
464
715
 
465
716
  for (const task of queue) {
466
717
  if (task.hasFrontEnd)
@@ -468,6 +719,7 @@ class TaskQueue {
468
719
  if (task.hasBackEnd)
469
720
  hasBackEnd = true;
470
721
  instructList = instructList.concat(task.instructList);
722
+ operationList = operationList.concat(task.operationList);
471
723
  }
472
724
 
473
725
  if (queue === this.queue) {
@@ -477,11 +729,11 @@ class TaskQueue {
477
729
 
478
730
  this.running = true;
479
731
  const error = await _saveNasl({
480
- app, hasFrontEnd, hasBackEnd, instructList,
732
+ app, hasFrontEnd, hasBackEnd, instructList, operationList
481
733
  });
482
734
 
483
735
  // code: 401650, 检测到当前应用拉取操作已被强制结束, 用户确认后再刷新
484
- // code: 500502 msg: "该应用已在新tab 页打开,请刷新!" 不重试或刷新
736
+ // code: 500502 msg: '该应用已在新tab 页打开,请刷新!' 不重试或刷新
485
737
  if (!error)
486
738
  this.recover(app);
487
739
  else if (![401650, 500502].includes(error.code)) {
@@ -551,8 +803,24 @@ async function saveNasl(options: TaskOption) {
551
803
  taskQueue.addTask(options);
552
804
  }
553
805
 
806
+ class Operation {
807
+ appId?: string;
808
+ branchId?: string;
809
+ accountId?: string;
810
+ userId?: string;
811
+ phone?: string;
812
+ actionItem: any;
813
+ res?: any;
814
+ err?: any;
815
+ parentKey?: string;
816
+ timestamp?: any;
817
+ type?: string;
818
+ uuid?: string;
819
+ action?: string;
820
+ }
821
+
554
822
  async function _saveNasl(options: TaskOption) {
555
- const { app, hasFrontEnd, hasBackEnd, instructList } = options;
823
+ const { app, hasFrontEnd, hasBackEnd, instructList, operationList } = options;
556
824
  app.emit('saving');
557
825
  let ChangedNASLType = '';
558
826
  if (hasFrontEnd && hasBackEnd) {
@@ -562,10 +830,13 @@ async function _saveNasl(options: TaskOption) {
562
830
  } else if (hasBackEnd) {
563
831
  ChangedNASLType = 'backend';
564
832
  }
565
- let err;
833
+ let res: any;
834
+ let err: any;
835
+
566
836
  if (config.storage.protocol === 'http') {
567
837
  try {
568
- await storageService.batchInstruct({
838
+ // 接口请求
839
+ res = await storageService.batchInstruct({
569
840
  body: instructList,
570
841
  headers: {
571
842
  appId: app.id,
@@ -577,6 +848,13 @@ async function _saveNasl(options: TaskOption) {
577
848
  } catch (error) {
578
849
  err = error;
579
850
  }
851
+ operationList.forEach((operation) => {
852
+ doOperationRecord({
853
+ ...operation,
854
+ res,
855
+ err,
856
+ });
857
+ });
580
858
  } else if (config.storage.protocol === 'mock') {
581
859
  // Do nothing
582
860
  } else {
@@ -741,6 +1019,10 @@ type BreakpointItem =
741
1019
  * @returns app 对象
742
1020
  */
743
1021
  export async function loadApp(appId?: string) {
1022
+ try {
1023
+ // 删除失效数据
1024
+ deleteExpiredRecords();
1025
+ } catch(err) { }
744
1026
  let app: App;
745
1027
  if (config.storage.protocol === 'http') {
746
1028
  console.time('batchQuery');
@@ -188,3 +188,16 @@ export function replaceNode(oldNode: any, newNode: any) {
188
188
  app.emit('collect:end');
189
189
  }
190
190
  }
191
+
192
+
193
+ export function attrConvertCamelCase(str: string) {
194
+ let strList: string[] = str.split('-');
195
+ strList = strList.map((strItem, index) => {
196
+ if (index === 0) {
197
+ return strItem;
198
+ } else {
199
+ return `${strItem.slice(0, 1).toUpperCase()}${strItem.slice(1)}`;
200
+ }
201
+ });
202
+ return strList.join('');
203
+ }
@@ -6,7 +6,7 @@ exports[`member-expression:toJS app.enums.UserStatusEnum 1`] = `
6
6
  `;
7
7
 
8
8
  exports[`member-expression:toJS elements.table-view.data 1`] = `
9
- "this[\\"__tableView_data-source\\"];
9
+ "this[\\"__tableView_dataSource\\"];
10
10
  "
11
11
  `;
12
12
 
@@ -6,7 +6,7 @@ exports[`member-expression:toVue app.enums.UserStatusEnum 1`] = `
6
6
  `;
7
7
 
8
8
  exports[`member-expression:toVue elements.table-view.data 1`] = `
9
- "<template>this['__tableView_data-source']</template>
9
+ "<template>__tableView_dataSource</template>
10
10
  "
11
11
  `;
12
12
 
@@ -150,28 +150,7 @@ exports[`view-element:toVue with-table 1`] = `
150
150
  </u-linear-layout>
151
151
  <u-table-view
152
152
  :ref=\\"\`tableView\`\\"
153
- :data-source=\\"
154
- (params) => {
155
- this['__tableView_params'] = { ...params };
156
- return this.$logics['app.logics.loadTestTableView2']({
157
- config: {
158
- download: false,
159
- },
160
- query: {},
161
- headers: {
162
- 'lcap-calllogic-uuid': '217e2a4c84424b5387b6a1c69a4bbb47',
163
- },
164
- path: {},
165
- body: {
166
- page: this['__tableView_params']?.page,
167
- size: this['__tableView_params']?.size,
168
- sort: this['__tableView_params']?.sort,
169
- order: this['__tableView_params']?.order,
170
- filter: filter1,
171
- },
172
- });
173
- }
174
- \\"
153
+ :data-source=\\"__tableView_dataSourceLoad\\"
175
154
  :data-schema=\\"\`LoadTestTableView2Structure\`\\"
176
155
  :value-field=\\"\`entity2.id\`\\"
177
156
  :pagination=\\"true\\"