clickgo 3.1.4-dev13 → 3.1.6-dev15

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 (96) hide show
  1. package/README.md +7 -7
  2. package/dist/app/demo/app.js +29 -3
  3. package/dist/app/demo/config.json +22 -3
  4. package/dist/app/demo/form/control/box/box.js +66 -0
  5. package/dist/app/demo/form/control/box/box.xml +18 -0
  6. package/dist/app/demo/form/control/button/button.js +24 -1
  7. package/dist/app/demo/form/control/check/check.js +24 -1
  8. package/dist/app/demo/form/control/dialog/dialog.js +24 -1
  9. package/dist/app/demo/form/control/file/file.js +24 -1
  10. package/dist/app/demo/form/control/flow/flow.js +24 -1
  11. package/dist/app/demo/form/control/form/form.js +24 -1
  12. package/dist/app/demo/form/control/layout/layout.js +57 -0
  13. package/dist/app/demo/form/control/layout/layout.xml +16 -0
  14. package/dist/app/demo/form/control/list/list.js +24 -1
  15. package/dist/app/demo/form/control/list/list.xml +8 -2
  16. package/dist/app/demo/form/control/marquee/marquee.js +24 -2
  17. package/dist/app/demo/form/control/marquee/marquee.xml +2 -5
  18. package/dist/app/demo/form/control/menu/menu.js +24 -1
  19. package/dist/app/demo/form/control/monaco/monaco.js +24 -1
  20. package/dist/app/demo/form/control/nav/nav.js +52 -0
  21. package/dist/app/demo/form/control/nav/nav.xml +43 -0
  22. package/dist/app/demo/form/control/panel/panel.js +67 -0
  23. package/dist/app/demo/form/control/panel/panel.xml +11 -0
  24. package/dist/app/demo/form/control/panel/test1.js +58 -0
  25. package/dist/app/demo/form/control/panel/test1.xml +16 -0
  26. package/dist/app/demo/form/control/panel/test2.xml +3 -0
  27. package/dist/app/demo/form/control/property/property.js +24 -1
  28. package/dist/app/demo/form/control/radio/radio.js +24 -1
  29. package/dist/app/demo/form/control/scroll/scroll.js +25 -1
  30. package/dist/app/demo/form/control/scroll/scroll.xml +5 -2
  31. package/dist/app/demo/form/control/select/select.js +24 -1
  32. package/dist/app/demo/form/control/tab/tab.js +24 -1
  33. package/dist/app/demo/form/control/table/table.js +164 -0
  34. package/dist/app/demo/form/control/table/table.xml +35 -0
  35. package/dist/app/demo/form/control/text/text.js +25 -1
  36. package/dist/app/demo/form/control/text/text.xml +1 -1
  37. package/dist/app/demo/form/control/vflow/vflow.js +24 -1
  38. package/dist/app/demo/form/event/form/form.js +24 -1
  39. package/dist/app/demo/form/event/other/other.js +24 -1
  40. package/dist/app/demo/form/event/screen/screen.js +24 -1
  41. package/dist/app/demo/form/event/task/task.js +24 -1
  42. package/dist/app/demo/form/main.js +130 -84
  43. package/dist/app/demo/form/main.xml +5 -0
  44. package/dist/app/demo/form/method/aform/aform.js +29 -15
  45. package/dist/app/demo/form/method/aform/aform.xml +0 -1
  46. package/dist/app/demo/form/method/aform/sd.js +25 -5
  47. package/dist/app/demo/form/method/core/core.js +24 -1
  48. package/dist/app/demo/form/method/dom/dom.js +48 -2
  49. package/dist/app/demo/form/method/dom/dom.xml +11 -0
  50. package/dist/app/demo/form/method/form/form.js +40 -7
  51. package/dist/app/demo/form/method/form/form.xml +3 -0
  52. package/dist/app/demo/form/method/{aform → form}/test.xml +0 -0
  53. package/dist/app/demo/form/method/fs/fs.js +139 -8
  54. package/dist/app/demo/form/method/fs/fs.xml +11 -1
  55. package/dist/app/demo/form/method/fs/text.js +24 -1
  56. package/dist/app/demo/form/method/native/native.js +24 -1
  57. package/dist/app/demo/form/method/system/system.js +24 -1
  58. package/dist/app/demo/form/method/task/task.js +31 -4
  59. package/dist/app/demo/form/method/task/task.xml +6 -1
  60. package/dist/app/demo/form/method/theme/theme.js +24 -1
  61. package/dist/app/demo/form/method/tool/tool.js +38 -1
  62. package/dist/app/demo/form/method/tool/tool.xml +1 -0
  63. package/dist/app/demo/form/method/zip/zip.js +30 -7
  64. package/dist/app/task/app.js +29 -3
  65. package/dist/app/task/form/bar/bar.js +24 -1
  66. package/dist/clickgo.js +33 -10
  67. package/dist/control/box.cgc +0 -0
  68. package/dist/control/common.cgc +0 -0
  69. package/dist/control/form.cgc +0 -0
  70. package/dist/control/monaco.cgc +0 -0
  71. package/dist/control/nav.cgc +0 -0
  72. package/dist/control/property.cgc +0 -0
  73. package/dist/control/table.cgc +0 -0
  74. package/dist/control/task.cgc +0 -0
  75. package/dist/global.css +1 -1
  76. package/dist/lib/control.js +53 -12
  77. package/dist/lib/control.ts +25 -5
  78. package/dist/lib/core.js +44 -50
  79. package/dist/lib/core.ts +18 -48
  80. package/dist/lib/dom.js +322 -108
  81. package/dist/lib/dom.ts +394 -127
  82. package/dist/lib/form.js +590 -226
  83. package/dist/lib/form.ts +706 -267
  84. package/dist/lib/fs.js +485 -224
  85. package/dist/lib/fs.ts +493 -287
  86. package/dist/lib/native.js +24 -1
  87. package/dist/lib/task.js +159 -139
  88. package/dist/lib/task.ts +148 -130
  89. package/dist/lib/theme.js +27 -4
  90. package/dist/lib/tool.js +57 -2
  91. package/dist/lib/tool.ts +68 -1
  92. package/dist/lib/zip.js +29 -3
  93. package/dist/lib/zip.ts +1 -1
  94. package/dist/theme/familiar.cgt +0 -0
  95. package/package.json +5 -7
  96. package/types/index.d.ts +51 -70
package/dist/lib/fs.ts CHANGED
@@ -8,8 +8,30 @@
8
8
  import * as types from '../../types';
9
9
  import * as tool from './tool';
10
10
  import * as task from './task';
11
+ import * as form from './form';
12
+ import * as core from './core';
13
+ import * as native from './native';
11
14
 
12
- const clickgoFiles = ['/app/', '/app/demo/', '/app/demo/app.js', '/app/demo/config.json', '/app/demo/form/', '/app/demo/form/control/', '/app/demo/form/control/block/', '/app/demo/form/control/block/block.css', '/app/demo/form/control/block/block.xml', '/app/demo/form/control/button/', '/app/demo/form/control/button/button.css', '/app/demo/form/control/button/button.js', '/app/demo/form/control/button/button.xml', '/app/demo/form/control/check/', '/app/demo/form/control/check/check.js', '/app/demo/form/control/check/check.xml', '/app/demo/form/control/dialog/', '/app/demo/form/control/dialog/dialog.js', '/app/demo/form/control/dialog/dialog.xml', '/app/demo/form/control/file/', '/app/demo/form/control/file/file.js', '/app/demo/form/control/file/file.xml', '/app/demo/form/control/flow/', '/app/demo/form/control/flow/flow.css', '/app/demo/form/control/flow/flow.js', '/app/demo/form/control/flow/flow.xml', '/app/demo/form/control/form/', '/app/demo/form/control/form/form.css', '/app/demo/form/control/form/form.js', '/app/demo/form/control/form/form.xml', '/app/demo/form/control/img/', '/app/demo/form/control/img/img.xml', '/app/demo/form/control/label/', '/app/demo/form/control/label/label.xml', '/app/demo/form/control/list/', '/app/demo/form/control/list/list.css', '/app/demo/form/control/list/list.js', '/app/demo/form/control/list/list.xml', '/app/demo/form/control/loading/', '/app/demo/form/control/loading/loading.xml', '/app/demo/form/control/marquee/', '/app/demo/form/control/marquee/marquee.js', '/app/demo/form/control/marquee/marquee.xml', '/app/demo/form/control/menu/', '/app/demo/form/control/menu/menu.js', '/app/demo/form/control/menu/menu.xml', '/app/demo/form/control/monaco/', '/app/demo/form/control/monaco/monaco.js', '/app/demo/form/control/monaco/monaco.xml', '/app/demo/form/control/property/', '/app/demo/form/control/property/property.js', '/app/demo/form/control/property/property.xml', '/app/demo/form/control/radio/', '/app/demo/form/control/radio/radio.js', '/app/demo/form/control/radio/radio.xml', '/app/demo/form/control/scroll/', '/app/demo/form/control/scroll/scroll.js', '/app/demo/form/control/scroll/scroll.xml', '/app/demo/form/control/select/', '/app/demo/form/control/select/select.js', '/app/demo/form/control/select/select.xml', '/app/demo/form/control/tab/', '/app/demo/form/control/tab/tab.js', '/app/demo/form/control/tab/tab.xml', '/app/demo/form/control/text/', '/app/demo/form/control/text/text.js', '/app/demo/form/control/text/text.xml', '/app/demo/form/control/vflow/', '/app/demo/form/control/vflow/vflow.css', '/app/demo/form/control/vflow/vflow.js', '/app/demo/form/control/vflow/vflow.xml', '/app/demo/form/event/', '/app/demo/form/event/form/', '/app/demo/form/event/form/form.css', '/app/demo/form/event/form/form.js', '/app/demo/form/event/form/form.xml', '/app/demo/form/event/other/', '/app/demo/form/event/other/other.js', '/app/demo/form/event/other/other.xml', '/app/demo/form/event/screen/', '/app/demo/form/event/screen/screen.js', '/app/demo/form/event/screen/screen.xml', '/app/demo/form/event/task/', '/app/demo/form/event/task/task.js', '/app/demo/form/event/task/task.xml', '/app/demo/form/main.css', '/app/demo/form/main.js', '/app/demo/form/main.xml', '/app/demo/form/method/', '/app/demo/form/method/aform/', '/app/demo/form/method/aform/aform.js', '/app/demo/form/method/aform/aform.xml', '/app/demo/form/method/aform/sd.js', '/app/demo/form/method/aform/sd.xml', '/app/demo/form/method/aform/test.xml', '/app/demo/form/method/core/', '/app/demo/form/method/core/core.js', '/app/demo/form/method/core/core.xml', '/app/demo/form/method/dom/', '/app/demo/form/method/dom/dom.css', '/app/demo/form/method/dom/dom.js', '/app/demo/form/method/dom/dom.xml', '/app/demo/form/method/form/', '/app/demo/form/method/form/form.css', '/app/demo/form/method/form/form.js', '/app/demo/form/method/form/form.xml', '/app/demo/form/method/fs/', '/app/demo/form/method/fs/fs.js', '/app/demo/form/method/fs/fs.xml', '/app/demo/form/method/fs/text.js', '/app/demo/form/method/fs/text.xml', '/app/demo/form/method/native/', '/app/demo/form/method/native/native.js', '/app/demo/form/method/native/native.xml', '/app/demo/form/method/system/', '/app/demo/form/method/system/system.js', '/app/demo/form/method/system/system.xml', '/app/demo/form/method/task/', '/app/demo/form/method/task/locale1.json', '/app/demo/form/method/task/locale2.json', '/app/demo/form/method/task/task.js', '/app/demo/form/method/task/task.xml', '/app/demo/form/method/theme/', '/app/demo/form/method/theme/theme.js', '/app/demo/form/method/theme/theme.xml', '/app/demo/form/method/tool/', '/app/demo/form/method/tool/tool.js', '/app/demo/form/method/tool/tool.xml', '/app/demo/form/method/zip/', '/app/demo/form/method/zip/zip.js', '/app/demo/form/method/zip/zip.xml', '/app/demo/global.css', '/app/demo/res/', '/app/demo/res/icon.svg', '/app/demo/res/img.jpg', '/app/demo/res/r-1.svg', '/app/demo/res/r-2.svg', '/app/demo/res/sql.svg', '/app/demo/res/txt.svg', '/app/demo/res/zip.svg', '/app/task/', '/app/task/app.js', '/app/task/config.json', '/app/task/form/', '/app/task/form/bar/', '/app/task/form/bar/bar.js', '/app/task/form/bar/bar.xml', '/app/task/form/desktop/', '/app/task/form/desktop/desktop.xml', '/app/task/locale/', '/app/task/locale/en.json', '/app/task/locale/ja.json', '/app/task/locale/sc.json', '/app/task/locale/tc.json', '/clickgo.js', '/clickgo.ts', '/control/', '/control/common.cgc', '/control/form.cgc', '/control/monaco.cgc', '/control/property.cgc', '/control/task.cgc', '/global.css', '/icon.png', '/index.js', '/index.ts', '/lib/', '/lib/control.js', '/lib/control.ts', '/lib/core.js', '/lib/core.ts', '/lib/dom.js', '/lib/dom.ts', '/lib/form.js', '/lib/form.ts', '/lib/fs.js', '/lib/fs.ts', '/lib/native.js', '/lib/native.ts', '/lib/task.js', '/lib/task.ts', '/lib/theme.js', '/lib/theme.ts', '/lib/tool.js', '/lib/tool.ts', '/lib/zip.js', '/lib/zip.ts', '/theme/', '/theme/familiar.cgt'];
15
+ const clickgoFiles = ['/app/', '/app/demo/', '/app/demo/app.js', '/app/demo/config.json', '/app/demo/form/', '/app/demo/form/control/', '/app/demo/form/control/block/', '/app/demo/form/control/block/block.css', '/app/demo/form/control/block/block.xml', '/app/demo/form/control/box/', '/app/demo/form/control/box/box.js', '/app/demo/form/control/box/box.xml', '/app/demo/form/control/button/', '/app/demo/form/control/button/button.css', '/app/demo/form/control/button/button.js', '/app/demo/form/control/button/button.xml', '/app/demo/form/control/check/', '/app/demo/form/control/check/check.js', '/app/demo/form/control/check/check.xml', '/app/demo/form/control/dialog/', '/app/demo/form/control/dialog/dialog.js', '/app/demo/form/control/dialog/dialog.xml', '/app/demo/form/control/file/', '/app/demo/form/control/file/file.js', '/app/demo/form/control/file/file.xml', '/app/demo/form/control/flow/', '/app/demo/form/control/flow/flow.css', '/app/demo/form/control/flow/flow.js', '/app/demo/form/control/flow/flow.xml', '/app/demo/form/control/form/', '/app/demo/form/control/form/form.css', '/app/demo/form/control/form/form.js', '/app/demo/form/control/form/form.xml', '/app/demo/form/control/img/', '/app/demo/form/control/img/img.xml', '/app/demo/form/control/label/', '/app/demo/form/control/label/label.xml', '/app/demo/form/control/layout/', '/app/demo/form/control/layout/layout.js', '/app/demo/form/control/layout/layout.xml', '/app/demo/form/control/list/', '/app/demo/form/control/list/list.css', '/app/demo/form/control/list/list.js', '/app/demo/form/control/list/list.xml', '/app/demo/form/control/loading/', '/app/demo/form/control/loading/loading.xml', '/app/demo/form/control/marquee/', '/app/demo/form/control/marquee/marquee.js', '/app/demo/form/control/marquee/marquee.xml', '/app/demo/form/control/menu/', '/app/demo/form/control/menu/menu.js', '/app/demo/form/control/menu/menu.xml', '/app/demo/form/control/monaco/', '/app/demo/form/control/monaco/monaco.js', '/app/demo/form/control/monaco/monaco.xml', '/app/demo/form/control/nav/', '/app/demo/form/control/nav/nav.js', '/app/demo/form/control/nav/nav.xml', '/app/demo/form/control/panel/', '/app/demo/form/control/panel/panel.js', '/app/demo/form/control/panel/panel.xml', '/app/demo/form/control/panel/test1.js', '/app/demo/form/control/panel/test1.xml', '/app/demo/form/control/panel/test2.xml', '/app/demo/form/control/property/', '/app/demo/form/control/property/property.js', '/app/demo/form/control/property/property.xml', '/app/demo/form/control/radio/', '/app/demo/form/control/radio/radio.js', '/app/demo/form/control/radio/radio.xml', '/app/demo/form/control/scroll/', '/app/demo/form/control/scroll/scroll.js', '/app/demo/form/control/scroll/scroll.xml', '/app/demo/form/control/select/', '/app/demo/form/control/select/select.js', '/app/demo/form/control/select/select.xml', '/app/demo/form/control/tab/', '/app/demo/form/control/tab/tab.js', '/app/demo/form/control/tab/tab.xml', '/app/demo/form/control/table/', '/app/demo/form/control/table/table.js', '/app/demo/form/control/table/table.xml', '/app/demo/form/control/text/', '/app/demo/form/control/text/text.js', '/app/demo/form/control/text/text.xml', '/app/demo/form/control/vflow/', '/app/demo/form/control/vflow/vflow.css', '/app/demo/form/control/vflow/vflow.js', '/app/demo/form/control/vflow/vflow.xml', '/app/demo/form/event/', '/app/demo/form/event/form/', '/app/demo/form/event/form/form.css', '/app/demo/form/event/form/form.js', '/app/demo/form/event/form/form.xml', '/app/demo/form/event/other/', '/app/demo/form/event/other/other.js', '/app/demo/form/event/other/other.xml', '/app/demo/form/event/screen/', '/app/demo/form/event/screen/screen.js', '/app/demo/form/event/screen/screen.xml', '/app/demo/form/event/task/', '/app/demo/form/event/task/task.js', '/app/demo/form/event/task/task.xml', '/app/demo/form/main.css', '/app/demo/form/main.js', '/app/demo/form/main.xml', '/app/demo/form/method/', '/app/demo/form/method/aform/', '/app/demo/form/method/aform/aform.js', '/app/demo/form/method/aform/aform.xml', '/app/demo/form/method/aform/sd.js', '/app/demo/form/method/aform/sd.xml', '/app/demo/form/method/core/', '/app/demo/form/method/core/core.js', '/app/demo/form/method/core/core.xml', '/app/demo/form/method/dom/', '/app/demo/form/method/dom/dom.css', '/app/demo/form/method/dom/dom.js', '/app/demo/form/method/dom/dom.xml', '/app/demo/form/method/form/', '/app/demo/form/method/form/form.css', '/app/demo/form/method/form/form.js', '/app/demo/form/method/form/form.xml', '/app/demo/form/method/form/test.xml', '/app/demo/form/method/fs/', '/app/demo/form/method/fs/fs.js', '/app/demo/form/method/fs/fs.xml', '/app/demo/form/method/fs/text.js', '/app/demo/form/method/fs/text.xml', '/app/demo/form/method/native/', '/app/demo/form/method/native/native.js', '/app/demo/form/method/native/native.xml', '/app/demo/form/method/system/', '/app/demo/form/method/system/system.js', '/app/demo/form/method/system/system.xml', '/app/demo/form/method/task/', '/app/demo/form/method/task/locale1.json', '/app/demo/form/method/task/locale2.json', '/app/demo/form/method/task/task.js', '/app/demo/form/method/task/task.xml', '/app/demo/form/method/theme/', '/app/demo/form/method/theme/theme.js', '/app/demo/form/method/theme/theme.xml', '/app/demo/form/method/tool/', '/app/demo/form/method/tool/tool.js', '/app/demo/form/method/tool/tool.xml', '/app/demo/form/method/zip/', '/app/demo/form/method/zip/zip.js', '/app/demo/form/method/zip/zip.xml', '/app/demo/global.css', '/app/demo/res/', '/app/demo/res/icon.svg', '/app/demo/res/img.jpg', '/app/demo/res/r-1.svg', '/app/demo/res/r-2.svg', '/app/demo/res/sql.svg', '/app/demo/res/txt.svg', '/app/demo/res/zip.svg', '/app/task/', '/app/task/app.js', '/app/task/config.json', '/app/task/form/', '/app/task/form/bar/', '/app/task/form/bar/bar.js', '/app/task/form/bar/bar.xml', '/app/task/form/desktop/', '/app/task/form/desktop/desktop.xml', '/app/task/locale/', '/app/task/locale/en.json', '/app/task/locale/ja.json', '/app/task/locale/sc.json', '/app/task/locale/tc.json', '/clickgo.js', '/clickgo.ts', '/control/', '/control/box.cgc', '/control/common.cgc', '/control/form.cgc', '/control/monaco.cgc', '/control/nav.cgc', '/control/property.cgc', '/control/table.cgc', '/control/task.cgc', '/global.css', '/icon.png', '/index.js', '/index.ts', '/lib/', '/lib/control.js', '/lib/control.ts', '/lib/core.js', '/lib/core.ts', '/lib/dom.js', '/lib/dom.ts', '/lib/form.js', '/lib/form.ts', '/lib/fs.js', '/lib/fs.ts', '/lib/native.js', '/lib/native.ts', '/lib/task.js', '/lib/task.ts', '/lib/theme.js', '/lib/theme.ts', '/lib/tool.js', '/lib/tool.ts', '/lib/zip.js', '/lib/zip.ts', '/theme/', '/theme/familiar.cgt'];
16
+
17
+ /** --- fs lib 用到的语言包 --- */
18
+ const localeData: Record<string, {
19
+ // eslint-disable-next-line @typescript-eslint/naming-convention
20
+ 'apply-unmount': string;
21
+ }> = {
22
+ 'en': {
23
+ 'apply-unmount': 'Are you sure to unmount the "?" mount point?',
24
+ },
25
+ 'sc': {
26
+ 'apply-unmount': '确定卸载“?”挂载点吗?'
27
+ },
28
+ 'tc': {
29
+ 'apply-unmount': '確定卸載「?」掛載點嗎?'
30
+ },
31
+ 'ja': {
32
+ 'apply-unmount': '「?」マウント ポイントをアンマウントしますか?'
33
+ }
34
+ };
13
35
 
14
36
  /** --- 已经挂载的列表 --- */
15
37
  const mounts: Record<string, types.IMountHandler> = {};
@@ -17,23 +39,24 @@ const mounts: Record<string, types.IMountHandler> = {};
17
39
  /** --- 根据 mounted 的 path 获取挂载点 name --- */
18
40
  function getMountName(path: string): string {
19
41
  const io = path.slice(9).indexOf('/');
20
- return path.slice(9, io + 9);
42
+ return io === -1 ? path.slice(9) : path.slice(9, io + 9);
21
43
  }
22
44
 
23
45
  /**
24
46
  * --- 挂载到 mounted 目录下 ---
25
47
  * @param name 目录名
26
- * @param handler 回调相关,taskId 在 App 模式下无效
48
+ * @param handler 回调相关
49
+ * @param taskId App 模式下无效
27
50
  */
28
- export function mount(name: string, handler: types.IMountHandler): boolean {
51
+ export function mount(name: string, handler: types.IMountHandler, taskId?: number): boolean {
29
52
  if (mounts[name]) {
30
53
  return false;
31
54
  }
32
55
  if (!/^[a-zA-Z][\w]+$/.test(name)) {
33
56
  return false;
34
57
  }
35
- if (handler.taskId) {
36
- const t = task.list[handler.taskId];
58
+ if (taskId) {
59
+ const t = task.list[taskId];
37
60
  if (t) {
38
61
  const val = 'fs./mounted/' + name + '/w';
39
62
  if (!t.runtime.permissions.includes(val)) {
@@ -41,6 +64,7 @@ export function mount(name: string, handler: types.IMountHandler): boolean {
41
64
  }
42
65
  }
43
66
  }
67
+ handler.date = new Date();
44
68
  mounts[name] = handler;
45
69
  return true;
46
70
  }
@@ -48,13 +72,13 @@ export function mount(name: string, handler: types.IMountHandler): boolean {
48
72
  /**
49
73
  * --- 卸载 mounted ---
50
74
  * @param name 目录名
51
- * @param taskId 校验 taskId,App 模式下无效
52
75
  */
53
- export function unmount(name: string, taskId?: number): boolean {
76
+ export async function unmount(name: string): Promise<boolean> {
54
77
  if (!mounts[name]) {
55
78
  return true;
56
79
  }
57
- if (taskId && (mounts[name].taskId !== taskId)) {
80
+ const loc = localeData[core.config.locale]?.['apply-unmount'] ?? localeData['en']['apply-unmount'];
81
+ if (!await form.superConfirm(loc.replace('?', '/mount/' + name + '/'))) {
58
82
  return false;
59
83
  }
60
84
  delete mounts[name];
@@ -64,31 +88,26 @@ export function unmount(name: string, taskId?: number): boolean {
64
88
  export async function getContent(path: string, options?: {
65
89
  'start'?: number;
66
90
  'end'?: number;
67
- 'files'?: Record<string, Blob | string>;
68
- 'current'?: string;
69
91
  'progress'?: (loaded: number, total: number) => void | Promise<void>;
70
- }): Promise<string | Blob | null>;
92
+ }, taskId?: number): Promise<string | Blob | null>;
71
93
  export async function getContent(path: string, options: BufferEncoding | {
72
94
  'encoding': BufferEncoding;
73
95
  'start'?: number;
74
96
  'end'?: number;
75
- 'files'?: Record<string, Blob | string>;
76
- 'current'?: string;
77
97
  'progress'?: (loaded: number, total: number) => void | Promise<void>;
78
- }): Promise<string | null>;
98
+ }, taskId?: number): Promise<string | null>;
79
99
  /**
80
100
  * --- 读取完整文件或一段 ---
81
101
  * @param path 文件路径
82
102
  * @param options 编码或选项
103
+ * @param taskId App 模式下无效
83
104
  */
84
105
  export async function getContent(path: string, options?: BufferEncoding | {
85
106
  'encoding'?: BufferEncoding;
86
107
  'start'?: number;
87
108
  'end'?: number;
88
- 'files'?: Record<string, Blob | string>;
89
- 'current'?: string;
90
109
  'progress'?: (loaded: number, total: number) => void | Promise<void>;
91
- }): Promise<Blob | string | null> {
110
+ }, taskId?: number): Promise<Blob | string | null> {
92
111
  path = tool.urlResolve('/', path);
93
112
  const fpath = path.slice(8);
94
113
  if (typeof options === 'string') {
@@ -150,23 +169,43 @@ export async function getContent(path: string, options?: BufferEncoding | {
150
169
  return null;
151
170
  }
152
171
  }
153
- else if (path.startsWith('/storage/')) {
154
- // --- TODO ---
155
- return null;
156
- }
157
- else if (path.startsWith('/mounted/')) {
158
- const name = getMountName(path);
159
- const hanlder = mounts[name];
160
- if (!hanlder) {
172
+ else if (path.startsWith('/storage/') || path.startsWith('/mounted/')) {
173
+ const r = await task.checkPermission('fs.' + path + 'r', false, undefined, taskId);
174
+ if (!r[0]) {
175
+ return null;
176
+ }
177
+ if (path.startsWith('/mounted/')) {
178
+ const name = getMountName(path);
179
+ const hanlder = mounts[name];
180
+ if (!hanlder) {
181
+ return null;
182
+ }
183
+ return hanlder.getContent?.(path.slice(9 + name.length), options) ?? null;
184
+ }
185
+ // --- storage ---
186
+ if (options.progress) {
187
+ // --- native 暂不支持 progress ---
188
+ delete options.progress;
189
+ }
190
+ const rtn = await native.invoke('cg-fs-getContent', native.getToken(), fpath, options);
191
+ if (!rtn) {
161
192
  return null;
162
193
  }
163
- return hanlder.getContent?.(path.slice(9 + name.length), options) ?? null;
194
+ if (typeof rtn === 'string') {
195
+ return rtn;
196
+ }
197
+ return new Blob([rtn], {
198
+ 'type': tool.getMimeByPath(path).mime
199
+ });
164
200
  }
165
- else if (path.startsWith('/package/')) {
166
- if (!options.files) {
201
+ else if (path.startsWith('/package/') || path.startsWith('/current/')) {
202
+ if (!taskId) {
167
203
  return null;
168
204
  }
169
- const file = options.files[fpath];
205
+ if (path.startsWith('/current/')) {
206
+ return getContent(task.list[taskId].current + fpath, options, taskId);
207
+ }
208
+ const file = task.list[taskId].app.files[fpath];
170
209
  if (!file) {
171
210
  return null;
172
211
  }
@@ -176,7 +215,10 @@ export async function getContent(path: string, options?: BufferEncoding | {
176
215
  }
177
216
  if (!options.encoding) {
178
217
  // --- 没有编码则返回 blob ---
179
- return file;
218
+ if (start === undefined && end === undefined) {
219
+ return file;
220
+ }
221
+ return file.slice(start, end, file.type);
180
222
  }
181
223
  const encoding = options.encoding;
182
224
  return new Promise(function(resolve) {
@@ -187,13 +229,6 @@ export async function getContent(path: string, options?: BufferEncoding | {
187
229
  fr.readAsText(file, encoding);
188
230
  });
189
231
  }
190
- else if (path.startsWith('/current/')) {
191
- if (!options.current) {
192
- return null;
193
- }
194
- const current = options.current.endsWith('/') ? options.current.slice(0, -1) : options.current;
195
- return getContent(current + fpath, options);
196
- }
197
232
  else {
198
233
  return null;
199
234
  }
@@ -204,39 +239,46 @@ export async function getContent(path: string, options?: BufferEncoding | {
204
239
  * @param path 文件路径
205
240
  * @param data 要写入的内容
206
241
  * @param options 选项
242
+ * @param taskId App 模式下无效
207
243
  */
208
244
  export async function putContent(path: string, data: string | Blob, options: {
209
245
  'encoding'?: BufferEncoding | null;
210
246
  'mode'?: string | number;
211
247
  'flag'?: string | number;
212
- 'current'?: string;
213
- } = {}): Promise<boolean> {
248
+ } = {}, taskId?: number): Promise<boolean> {
214
249
  path = tool.urlResolve('/', path);
215
250
  const fpath = path.slice(8);
216
251
  if (path.startsWith('/clickgo/')) {
217
252
  return false;
218
253
  }
219
- else if (path.startsWith('/storage/')) {
220
- // --- TODO ---
221
- return false;
222
- }
223
- else if (path.startsWith('/mounted/')) {
224
- const name = getMountName(path);
225
- const hanlder = mounts[name];
226
- if (!hanlder) {
254
+ else if (path.startsWith('/storage/') || path.startsWith('/mounted/')) {
255
+ const r = await task.checkPermission('fs.' + path + 'w', false, undefined, taskId);
256
+ if (!r[0]) {
227
257
  return false;
228
258
  }
229
- return hanlder.putContent?.(path.slice(9 + name.length), data, options) ?? false;
259
+ if (path.startsWith('/mounted/')) {
260
+ const name = getMountName(path);
261
+ const hanlder = mounts[name];
262
+ if (!hanlder) {
263
+ return false;
264
+ }
265
+ return hanlder.putContent?.(path.slice(9 + name.length), data, options) ?? false;
266
+ }
267
+ // --- storage ---
268
+ let buf: Uint8Array | undefined = undefined;
269
+ if (data instanceof Blob) {
270
+ buf = new Uint8Array(await data.arrayBuffer());
271
+ }
272
+ return native.invoke('cg-fs-putContent', native.getToken(), fpath, buf ?? data, options);
230
273
  }
231
274
  else if (path.startsWith('/package/')) {
232
275
  return false;
233
276
  }
234
277
  else if (path.startsWith('/current/')) {
235
- if (!options.current) {
278
+ if (!taskId) {
236
279
  return false;
237
280
  }
238
- const current = options.current.endsWith('/') ? options.current.slice(0, -1) : options.current;
239
- return putContent(current + fpath, data, options);
281
+ return putContent(task.list[taskId].current + fpath, data, options, taskId);
240
282
  }
241
283
  else {
242
284
  return false;
@@ -248,47 +290,36 @@ export async function putContent(path: string, data: string | Blob, options: {
248
290
  * @param path 要读取的路径
249
291
  * @param options 选项
250
292
  */
251
- export async function readLink(path: string, options?: BufferEncoding | {
252
- 'encoding'?: BufferEncoding;
253
- /** --- 不以 / 结尾的路径 --- */
254
- 'current'?: string;
255
- }): Promise<string | null> {
293
+ export async function readLink(path: string, encoding?: BufferEncoding, taskId?: number): Promise<string | null> {
256
294
  path = tool.urlResolve('/', path);
257
295
  const fpath = path.slice(8);
258
- if (typeof options === 'string') {
259
- options = {
260
- 'encoding': options
261
- };
262
- }
263
- else if (!options) {
264
- options = {};
265
- }
266
296
  if (path.startsWith('/clickgo/')) {
267
297
  return null;
268
298
  }
269
- else if (path.startsWith('/storage/')) {
270
- // --- TODO ---
271
- return null;
272
- }
273
- else if (path.startsWith('/mounted/')) {
274
- const name = getMountName(path);
275
- const hanlder = mounts[name];
276
- if (!hanlder) {
299
+ else if (path.startsWith('/storage/') || path.startsWith('/mounted/')) {
300
+ const r = await task.checkPermission('fs.' + path + 'r', false, undefined, taskId);
301
+ if (!r[0]) {
277
302
  return null;
278
303
  }
279
- return hanlder.readLink?.(path.slice(9 + name.length), options) ?? null;
304
+ if (path.startsWith('/mounted/')) {
305
+ const name = getMountName(path);
306
+ const hanlder = mounts[name];
307
+ if (!hanlder) {
308
+ return null;
309
+ }
310
+ return hanlder.readLink?.(path.slice(9 + name.length), encoding) ?? null;
311
+ }
312
+ // --- storage ---
313
+ return native.invoke('cg-fs-readLink', native.getToken(), fpath, encoding);
280
314
  }
281
315
  else if (path.startsWith('/package/')) {
282
316
  return null;
283
317
  }
284
318
  else if (path.startsWith('/current/')) {
285
- if (!options.current) {
319
+ if (!taskId) {
286
320
  return null;
287
321
  }
288
- if (options.current.endsWith('/')) {
289
- return options.current.slice(0, -1);
290
- }
291
- return options.current;
322
+ return task.list[taskId].current;
292
323
  }
293
324
  else {
294
325
  return null;
@@ -299,45 +330,46 @@ export async function readLink(path: string, options?: BufferEncoding | {
299
330
  * --- 把源文件创建一个 link ---
300
331
  * @param filePath 源文件
301
332
  * @param linkPath 连接路径
302
- * @param options 选项
333
+ * @param type 选项
334
+ * @param taskId App 模式下无效
303
335
  */
304
- export async function symlink(filePath: string, linkPath: string, options: {
305
- 'type'?: 'dir' | 'file' | 'junction';
306
- 'current'?: string;
307
- } = {}): Promise<boolean> {
336
+ export async function symlink(filePath: string, linkPath: string, type?: 'dir' | 'file' | 'junction', taskId?: number): Promise<boolean> {
308
337
  filePath = tool.urlResolve('/', filePath);
309
338
  linkPath = tool.urlResolve('/', linkPath);
310
339
  if (filePath.startsWith('/clickgo/')) {
311
340
  return false;
312
341
  }
313
- else if (filePath.startsWith('/storage/')) {
314
- // --- TODO ---
315
- return false;
316
- }
317
- else if (filePath.startsWith('/mounted/')) {
318
- const fname = getMountName(filePath);
319
- const lname = getMountName(linkPath);
320
- if (fname !== lname) {
342
+ else if (filePath.startsWith('/storage/') || filePath.startsWith('/mounted/')) {
343
+ const r = await task.checkPermission('fs.' + filePath + 'w', false, undefined, taskId);
344
+ if (!r[0]) {
321
345
  return false;
322
346
  }
323
- const hanlder = mounts[fname];
324
- if (!hanlder) {
325
- return false;
347
+ if (filePath.startsWith('/mounted/')) {
348
+ const fname = getMountName(filePath);
349
+ const lname = getMountName(linkPath);
350
+ if (fname !== lname) {
351
+ return false;
352
+ }
353
+ const hanlder = mounts[fname];
354
+ if (!hanlder) {
355
+ return false;
356
+ }
357
+ return hanlder.symlink?.(filePath.slice(9 + fname.length), linkPath.slice(9 + fname.length), type) ?? false;
326
358
  }
327
- return hanlder.symlink?.(filePath.slice(9 + fname.length), linkPath.slice(9 + fname.length), options) ?? false;
359
+ // --- storage ---
360
+ return native.invoke('cg-fs-symlink', native.getToken(), filePath.slice(8), linkPath.slice(8), type);
328
361
  }
329
362
  else if (filePath.startsWith('/package/')) {
330
363
  return false;
331
364
  }
332
365
  else if (filePath.startsWith('/current/')) {
333
- if (!options.current) {
366
+ if (!taskId) {
334
367
  return false;
335
368
  }
336
- const current = options.current.endsWith('/') ? options.current.slice(0, -1) : options.current;
337
369
  if (linkPath.startsWith('/current/')) {
338
- linkPath = current + linkPath.slice(8);
370
+ linkPath = task.list[taskId].current + linkPath.slice(8);
339
371
  }
340
- return symlink(current + filePath.slice(8), linkPath, options);
372
+ return symlink(task.list[taskId].current + filePath.slice(8), linkPath, type, taskId);
341
373
  }
342
374
  else {
343
375
  return false;
@@ -347,36 +379,38 @@ export async function symlink(filePath: string, linkPath: string, options: {
347
379
  /**
348
380
  * --- 删除一个文件 ---
349
381
  * @param path 要删除的文件路径
382
+ * @param taskId App 模式下无效
350
383
  */
351
- export async function unlink(path: string, options: {
352
- 'current'?: string;
353
- } = {}): Promise<boolean> {
384
+ export async function unlink(path: string, taskId?: number): Promise<boolean> {
354
385
  path = tool.urlResolve('/', path);
355
386
  const fpath = path.slice(8);
356
387
  if (path.startsWith('/clickgo/')) {
357
388
  return false;
358
389
  }
359
- else if (path.startsWith('/storage/')) {
360
- // --- TODO ---
361
- return false;
362
- }
363
- else if (path.startsWith('/mounted/')) {
364
- const name = getMountName(path);
365
- const hanlder = mounts[name];
366
- if (!hanlder) {
390
+ else if (path.startsWith('/storage/') || path.startsWith('/mounted/')) {
391
+ const r = await task.checkPermission('fs.' + path + 'w', false, undefined, taskId);
392
+ if (!r[0]) {
367
393
  return false;
368
394
  }
369
- return hanlder.unlink?.(path.slice(9 + name.length), options) ?? false;
395
+ if (path.startsWith('/mounted/')) {
396
+ const name = getMountName(path);
397
+ const hanlder = mounts[name];
398
+ if (!hanlder) {
399
+ return false;
400
+ }
401
+ return hanlder.unlink?.(path.slice(9 + name.length)) ?? false;
402
+ }
403
+ // --- storage ---
404
+ return native.invoke('cg-fs-unlink', native.getToken(), fpath);
370
405
  }
371
406
  else if (path.startsWith('/package/')) {
372
407
  return false;
373
408
  }
374
409
  else if (path.startsWith('/current/')) {
375
- if (!options.current) {
410
+ if (!taskId) {
376
411
  return false;
377
412
  }
378
- const current = options.current.endsWith('/') ? options.current.slice(0, -1) : options.current;
379
- return unlink(current + fpath, options);
413
+ return unlink(task.list[taskId].current + fpath, taskId);
380
414
  }
381
415
  else {
382
416
  return false;
@@ -420,7 +454,11 @@ async function getClickGoStats(path: string): Promise<types.IStats | null> {
420
454
  });
421
455
  const hdate = res.headers.get('date');
422
456
  const hmdate = res.headers.get('last-modified');
423
- const hlength = res.headers.get('content-length');
457
+ let hlength = res.headers.get('content-range');
458
+ if (hlength) {
459
+ const lio = hlength.lastIndexOf('/');
460
+ hlength = hlength.slice(lio + 1);
461
+ }
424
462
  let date = new Date();
425
463
  let mdate = date;
426
464
  if (hdate) {
@@ -462,13 +500,63 @@ async function getClickGoStats(path: string): Promise<types.IStats | null> {
462
500
  /**
463
501
  * --- 获取对象是否存在,存在则返回 stats 对象,否则返回 null ---
464
502
  * @param path 对象路径
465
- * @param options 选项
503
+ * @param taskId App 模式下无效
466
504
  */
467
- export async function stats(path: string, options: {
468
- 'files'?: Record<string, Blob | string>;
469
- 'current'?: string;
470
- } = {}): Promise<types.IStats | null> {
505
+ export async function stats(path: string, taskId?: number): Promise<types.IStats | null> {
471
506
  path = tool.urlResolve('/', path);
507
+ if (path.endsWith('/')) {
508
+ path = path.slice(0, -1);
509
+ }
510
+ if (['', '/clickgo', '/storage', '/mounted', '/package'].includes(path)) {
511
+ const date = new Date();
512
+ const ms = date.getTime();
513
+ return {
514
+ isFile: function() {
515
+ return false;
516
+ },
517
+ isDirectory: function() {
518
+ return true;
519
+ },
520
+ isSymbolicLink: function() {
521
+ return false;
522
+ },
523
+ 'size': 0,
524
+ 'blksize': 0,
525
+ 'atimeMs': ms,
526
+ 'mtimeMs': ms,
527
+ 'ctimeMs': ms,
528
+ 'birthtimeMs': ms,
529
+ 'atime': date,
530
+ 'mtime': date,
531
+ 'ctime': date,
532
+ 'birthtime': date
533
+ };
534
+ }
535
+ if (path === '/current') {
536
+ const date = new Date();
537
+ const ms = date.getTime();
538
+ return {
539
+ isFile: function() {
540
+ return false;
541
+ },
542
+ isDirectory: function() {
543
+ return false;
544
+ },
545
+ isSymbolicLink: function() {
546
+ return true;
547
+ },
548
+ 'size': 0,
549
+ 'blksize': 0,
550
+ 'atimeMs': ms,
551
+ 'mtimeMs': ms,
552
+ 'ctimeMs': ms,
553
+ 'birthtimeMs': ms,
554
+ 'atime': date,
555
+ 'mtime': date,
556
+ 'ctime': date,
557
+ 'birthtime': date
558
+ };
559
+ }
472
560
  let fpath = path.slice(8);
473
561
  if (path.startsWith('/clickgo/')) {
474
562
  if (!clickgoFiles.includes(fpath)) {
@@ -480,8 +568,35 @@ export async function stats(path: string, options: {
480
568
  return getClickGoStats(fpath);
481
569
  }
482
570
  else if (path.startsWith('/storage/')) {
483
- // --- TODO ---
484
- return null;
571
+ const r = await task.checkPermission('fs.' + path + 'r', false, undefined, taskId);
572
+ if (!r[0]) {
573
+ return null;
574
+ }
575
+ const item = await native.invoke('cg-fs-stats', native.getToken(), fpath);
576
+ if (!item) {
577
+ return null;
578
+ }
579
+ return {
580
+ isFile: function() {
581
+ return item.isFile;
582
+ },
583
+ isDirectory: function() {
584
+ return item.isDirectory;
585
+ },
586
+ isSymbolicLink: function() {
587
+ return item.isSymbolicLink;
588
+ },
589
+ 'size': item.size,
590
+ 'blksize': item.blksize,
591
+ 'atimeMs': item.atimeMs,
592
+ 'mtimeMs': item.mtimeMs,
593
+ 'ctimeMs': item.ctimeMs,
594
+ 'birthtimeMs': item.birthtimeMs,
595
+ 'atime': item.atime,
596
+ 'mtime': item.mtime,
597
+ 'ctime': item.ctime,
598
+ 'birthtime': item.birthtime
599
+ };
485
600
  }
486
601
  else if (path.startsWith('/mounted/')) {
487
602
  const name = getMountName(path);
@@ -489,15 +604,46 @@ export async function stats(path: string, options: {
489
604
  if (!hanlder) {
490
605
  return null;
491
606
  }
492
- return hanlder.stats?.(path.slice(9 + name.length), options) ?? false;
607
+ if (path === '/mounted/' + name) {
608
+ const ms = hanlder.date!.getTime();
609
+ return {
610
+ isFile: function() {
611
+ return false;
612
+ },
613
+ isDirectory: function() {
614
+ return true;
615
+ },
616
+ isSymbolicLink: function() {
617
+ return false;
618
+ },
619
+ 'size': 0,
620
+ 'blksize': 0,
621
+ 'atimeMs': ms,
622
+ 'mtimeMs': ms,
623
+ 'ctimeMs': ms,
624
+ 'birthtimeMs': ms,
625
+ 'atime': hanlder.date!,
626
+ 'mtime': hanlder.date!,
627
+ 'ctime': hanlder.date!,
628
+ 'birthtime': hanlder.date!
629
+ };
630
+ }
631
+ const r = await task.checkPermission('fs.' + path + 'r', false, undefined, taskId);
632
+ if (!r[0]) {
633
+ return null;
634
+ }
635
+ return hanlder.stats?.(path.slice(9 + name.length)) ?? null;
493
636
  }
494
- else if (path.startsWith('/package/')) {
495
- if (!options.files) {
637
+ else if (path.startsWith('/package/') || path.startsWith('/current/')) {
638
+ if (!taskId) {
496
639
  return null;
497
640
  }
498
- if (options.files[fpath]) {
641
+ if (path.startsWith('/current/')) {
642
+ return stats(task.list[taskId].current + fpath, taskId);
643
+ }
644
+ if (task.list[taskId].app.files[fpath]) {
499
645
  // --- 文件 ---
500
- const file = options.files[fpath];
646
+ const file = task.list[taskId].app.files[fpath];
501
647
  const date = new Date();
502
648
  const ms = date.getTime();
503
649
  let size = 0;
@@ -533,7 +679,7 @@ export async function stats(path: string, options: {
533
679
  if (!fpath.endsWith('/')) {
534
680
  fpath += '/';
535
681
  }
536
- for (const p in options.files) {
682
+ for (const p in task.list[taskId].app.files) {
537
683
  if (!p.startsWith(fpath)) {
538
684
  continue;
539
685
  }
@@ -564,13 +710,6 @@ export async function stats(path: string, options: {
564
710
  }
565
711
  return null;
566
712
  }
567
- else if (path.startsWith('/current/')) {
568
- if (!options.current) {
569
- return null;
570
- }
571
- const current = options.current.endsWith('/') ? options.current.slice(0, -1) : options.current;
572
- return stats(current + fpath, options);
573
- }
574
713
  else {
575
714
  return null;
576
715
  }
@@ -578,15 +717,12 @@ export async function stats(path: string, options: {
578
717
 
579
718
  /**
580
719
  * --- 判断是否是目录或目录是否存在,是的话返回 stats ---
581
- * @param path 判断路径
582
- * @param options 选项
720
+ * @param path 判断路径woml
721
+ * @param taskId App 模式下无效
583
722
  */
584
- export async function isDir(path: string, options: {
585
- 'files'?: Record<string, Blob | string>;
586
- 'current'?: string;
587
- } = {}): Promise<types.IStats | false> {
588
- const pstats = await stats(path, options);
589
- if (!pstats || !pstats.isDirectory()) {
723
+ export async function isDir(path: string, taskId?: number): Promise<types.IStats | false> {
724
+ const pstats = await stats(path, taskId);
725
+ if (!pstats?.isDirectory()) {
590
726
  return false;
591
727
  }
592
728
  return pstats;
@@ -595,13 +731,11 @@ export async function isDir(path: string, options: {
595
731
  /**
596
732
  * --- 判断是否是文件或文件是否存在,是的话返回 stats ---
597
733
  * @param path 判断路径
734
+ * @param taskId App 模式下无效
598
735
  */
599
- export async function isFile(path: string, options: {
600
- 'files'?: Record<string, Blob | string>;
601
- 'current'?: string;
602
- } = {}): Promise<types.IStats | false> {
603
- const pstats = await stats(path, options);
604
- if (!pstats || !pstats.isFile()) {
736
+ export async function isFile(path: string, taskId?: number): Promise<types.IStats | false> {
737
+ const pstats = await stats(path, taskId);
738
+ if (!pstats?.isFile()) {
605
739
  return false;
606
740
  }
607
741
  return pstats;
@@ -611,39 +745,41 @@ export async function isFile(path: string, options: {
611
745
  * --- 深度创建目录,如果最末目录存在,则自动创建成功 ---
612
746
  * @param path 要创建的路径,如 /a/b/c/
613
747
  * @param mode 权限
748
+ * @param taskId App 模式下无效
614
749
  */
615
- export async function mkdir(path: string, mode: number = 0o755, options: {
616
- 'current'?: string;
617
- } = {}): Promise<boolean> {
750
+ export async function mkdir(path: string, mode: number = 0o755, taskId?: number): Promise<boolean> {
618
751
  path = tool.urlResolve('/', path);
619
- if (await isDir(path, options)) {
752
+ if (await isDir(path, taskId)) {
620
753
  return true;
621
754
  }
622
755
  const fpath = path.slice(8);
623
756
  if (path.startsWith('/clickgo/')) {
624
757
  return false;
625
758
  }
626
- else if (path.startsWith('/storage/')) {
627
- // --- TODO ---
628
- return false;
629
- }
630
- else if (path.startsWith('/mounted/')) {
631
- const name = getMountName(path);
632
- const hanlder = mounts[name];
633
- if (!hanlder) {
759
+ else if (path.startsWith('/storage/') || path.startsWith('/mounted/')) {
760
+ const r = await task.checkPermission('fs.' + path + 'w', false, undefined, taskId);
761
+ if (!r[0]) {
634
762
  return false;
635
763
  }
636
- return hanlder.mkdir?.(path.slice(9 + name.length), mode, options) ?? false;
764
+ if (path.startsWith('/mounted/')) {
765
+ const name = getMountName(path);
766
+ const hanlder = mounts[name];
767
+ if (!hanlder) {
768
+ return false;
769
+ }
770
+ return hanlder.mkdir?.(path.slice(9 + name.length), mode) ?? false;
771
+ }
772
+ // --- storage ---
773
+ return native.invoke('cg-fs-mkdir', native.getToken(), fpath, mode);
637
774
  }
638
775
  else if (path.startsWith('/package/')) {
639
776
  return false;
640
777
  }
641
778
  else if (path.startsWith('/current/')) {
642
- if (!options.current) {
779
+ if (!taskId) {
643
780
  return false;
644
781
  }
645
- const current = options.current.endsWith('/') ? options.current.slice(0, -1) : options.current;
646
- return mkdir(current + fpath, mode, options);
782
+ return mkdir(task.list[taskId].current + fpath, mode, taskId);
647
783
  }
648
784
  else {
649
785
  return false;
@@ -653,36 +789,38 @@ export async function mkdir(path: string, mode: number = 0o755, options: {
653
789
  /**
654
790
  * --- 删除空目录 ---
655
791
  * @param path 要删除的目录
792
+ * @param taskId App 模式下无效
656
793
  */
657
- export async function rmdir(path: string, options: {
658
- 'current'?: string;
659
- } = {}): Promise<boolean> {
794
+ export async function rmdir(path: string, taskId?: number): Promise<boolean> {
660
795
  path = tool.urlResolve('/', path);
661
796
  const fpath = path.slice(8);
662
797
  if (path.startsWith('/clickgo/')) {
663
798
  return false;
664
799
  }
665
- else if (path.startsWith('/storage/')) {
666
- // --- TODO ---
667
- return false;
668
- }
669
- else if (path.startsWith('/mounted/')) {
670
- const name = getMountName(path);
671
- const hanlder = mounts[name];
672
- if (!hanlder) {
800
+ else if (path.startsWith('/storage/') || path.startsWith('/mounted/')) {
801
+ const r = await task.checkPermission('fs.' + path + 'w', false, undefined, taskId);
802
+ if (!r[0]) {
673
803
  return false;
674
804
  }
675
- return hanlder.rmdir?.(path.slice(9 + name.length), options) ?? false;
805
+ if (path.startsWith('/mounted/')) {
806
+ const name = getMountName(path);
807
+ const hanlder = mounts[name];
808
+ if (!hanlder) {
809
+ return false;
810
+ }
811
+ return hanlder.rmdir?.(path.slice(9 + name.length)) ?? false;
812
+ }
813
+ // --- storage ---
814
+ return native.invoke('cg-fs-rmdir', native.getToken(), fpath);
676
815
  }
677
816
  else if (path.startsWith('/package/')) {
678
817
  return false;
679
818
  }
680
819
  else if (path.startsWith('/current/')) {
681
- if (!options.current) {
820
+ if (!taskId) {
682
821
  return false;
683
822
  }
684
- const current = options.current.endsWith('/') ? options.current.slice(0, -1) : options.current;
685
- return rmdir(current + fpath, options);
823
+ return rmdir(task.list[taskId].current + fpath, taskId);
686
824
  }
687
825
  else {
688
826
  return false;
@@ -692,71 +830,73 @@ export async function rmdir(path: string, options: {
692
830
  /**
693
831
  * --- 删除一个非空目录 ---
694
832
  * --- [ Danger ] [ 危险 ] ---
833
+ * @param path 目录路径
834
+ * @param taskId App 模式下无效
695
835
  */
696
- export async function rmdirDeep(path: string, options: {
697
- 'current'?: string;
698
- } = {}): Promise<boolean> {
836
+ export async function rmdirDeep(path: string, taskId?: number): Promise<boolean> {
699
837
  path = tool.urlResolve('/', path);
700
838
  if (!path.endsWith('/')) {
701
839
  path += '/';
702
840
  }
703
- const list = await readDir(path, options);
841
+ const list = await readDir(path, undefined, taskId);
704
842
  for (const item of list) {
705
- const stat = await stats(path + item.name, options);
843
+ const stat = await stats(path + item.name, taskId);
706
844
  if (!stat) {
707
845
  return false;
708
846
  }
709
847
  if (stat.isDirectory()) {
710
848
  // --- 目录 ---
711
- const rtn = await rmdirDeep(path + item.name, options);
849
+ const rtn = await rmdirDeep(path + item.name, taskId);
712
850
  if (!rtn) {
713
851
  return false;
714
852
  }
715
853
  }
716
854
  else {
717
- const rtn = await unlink(path + item.name, options);
855
+ const rtn = await unlink(path + item.name, taskId);
718
856
  if (!rtn) {
719
857
  return false;
720
858
  }
721
859
  }
722
860
  }
723
- return rmdir(path, options);
861
+ return rmdir(path, taskId);
724
862
  }
725
863
 
726
864
  /**
727
- * --- 修改权限
865
+ * --- 修改权限 ---
728
866
  * @param path 要修改的路径
729
867
  * @param mod 权限
868
+ * @param taskId App 模式下无效
730
869
  */
731
- export async function chmod(path: string, mod: string | number, options: {
732
- 'current'?: string;
733
- } = {}): Promise<boolean> {
870
+ export async function chmod(path: string, mod: string | number, taskId?: number): Promise<boolean> {
734
871
  path = tool.urlResolve('/', path);
735
872
  const fpath = path.slice(8);
736
873
  if (path.startsWith('/clickgo/')) {
737
874
  return false;
738
875
  }
739
- else if (path.startsWith('/storage/')) {
740
- // --- TODO ---
741
- return false;
742
- }
743
- else if (path.startsWith('/mounted/')) {
744
- const name = getMountName(path);
745
- const hanlder = mounts[name];
746
- if (!hanlder) {
876
+ else if (path.startsWith('/storage/') || path.startsWith('/mounted/')) {
877
+ const r = await task.checkPermission('fs.' + path + 'w', false, undefined, taskId);
878
+ if (!r[0]) {
747
879
  return false;
748
880
  }
749
- return hanlder.chmod?.(path.slice(9 + name.length), mod, options) ?? false;
881
+ if (path.startsWith('/mounted/')) {
882
+ const name = getMountName(path);
883
+ const hanlder = mounts[name];
884
+ if (!hanlder) {
885
+ return false;
886
+ }
887
+ return hanlder.chmod?.(path.slice(9 + name.length), mod) ?? false;
888
+ }
889
+ // --- storage ---
890
+ return native.invoke('cg-fs-chmod', native.getToken(), fpath, mod);
750
891
  }
751
892
  else if (path.startsWith('/package/')) {
752
893
  return false;
753
894
  }
754
895
  else if (path.startsWith('/current/')) {
755
- if (!options.current) {
896
+ if (!taskId) {
756
897
  return false;
757
898
  }
758
- const current = options.current.endsWith('/') ? options.current.slice(0, -1) : options.current;
759
- return chmod(current + fpath, mod, options);
899
+ return chmod(task.list[taskId].current + fpath, mod, taskId);
760
900
  }
761
901
  else {
762
902
  return false;
@@ -768,9 +908,7 @@ export async function chmod(path: string, mod: string | number, options: {
768
908
  * @param oldPath 老名
769
909
  * @param newPath 新名
770
910
  */
771
- export async function rename(oldPath: string, newPath: string, options: {
772
- 'current'?: string;
773
- } = {}): Promise<boolean> {
911
+ export async function rename(oldPath: string, newPath: string, taskId?: number): Promise<boolean> {
774
912
  oldPath = tool.urlResolve('/', oldPath);
775
913
  newPath = tool.urlResolve('/', newPath);
776
914
  if (!oldPath.startsWith(newPath.slice(0, 9))) {
@@ -781,23 +919,38 @@ export async function rename(oldPath: string, newPath: string, options: {
781
919
  if (oldPath.startsWith('/clickgo/')) {
782
920
  return false;
783
921
  }
784
- else if (oldPath.startsWith('/storage/')) {
785
- // --- TODO ---
786
- return false;
787
- }
788
- else if (oldPath.startsWith('/mounted/')) {
789
- // --- TODO ---
790
- return false;
922
+ else if (oldPath.startsWith('/storage/') || oldPath.startsWith('/mounted/')) {
923
+ let r = await task.checkPermission('fs.' + oldPath + 'w', false, undefined, taskId);
924
+ if (!r[0]) {
925
+ return false;
926
+ }
927
+ r = await task.checkPermission('fs.' + newPath + 'w', false, undefined, taskId);
928
+ if (!r[0]) {
929
+ return false;
930
+ }
931
+ if (oldPath.startsWith('/mounted/')) {
932
+ const fname = getMountName(oldPath);
933
+ const lname = getMountName(newPath);
934
+ if (fname !== lname) {
935
+ return false;
936
+ }
937
+ const hanlder = mounts[fname];
938
+ if (!hanlder) {
939
+ return false;
940
+ }
941
+ return hanlder.rename?.(oldPath.slice(9 + fname.length), newPath.slice(9 + fname.length)) ?? false;
942
+ }
943
+ // --- storage ---
944
+ return native.invoke('cg-fs-rename', native.getToken(), ofpath, nfpath);
791
945
  }
792
946
  else if (oldPath.startsWith('/package/')) {
793
947
  return false;
794
948
  }
795
949
  else if (oldPath.startsWith('/current/')) {
796
- if (!options.current) {
950
+ if (!taskId) {
797
951
  return false;
798
952
  }
799
- const current = options.current.endsWith('/') ? options.current.slice(0, -1) : options.current;
800
- return rename(current + ofpath, current + nfpath, options);
953
+ return rename(task.list[taskId].current + ofpath, task.list[taskId].current + nfpath, taskId);
801
954
  }
802
955
  else {
803
956
  return false;
@@ -807,12 +960,10 @@ export async function rename(oldPath: string, newPath: string, options: {
807
960
  /**
808
961
  * --- 获取文件夹下文件列表 ---
809
962
  * @param path 文件夹路径
963
+ * @param encoding 编码
964
+ * @param taskId App 模式下无效
810
965
  */
811
- export async function readDir(path: string, options: {
812
- 'encoding'?: BufferEncoding;
813
- 'files'?: Record<string, Blob | string>;
814
- 'current'?: string;
815
- } = {}): Promise<types.IDirent[]> {
966
+ export async function readDir(path: string, encoding?: BufferEncoding, taskId?: number): Promise<types.IDirent[]> {
816
967
  path = tool.urlResolve('/', path);
817
968
  if (path === '/') {
818
969
  const list = [
@@ -853,7 +1004,7 @@ export async function readDir(path: string, options: {
853
1004
  'name': 'mounted'
854
1005
  }
855
1006
  ];
856
- if (options.files) {
1007
+ if (taskId) {
857
1008
  list.push({
858
1009
  isFile: function() {
859
1010
  return false;
@@ -867,7 +1018,7 @@ export async function readDir(path: string, options: {
867
1018
  'name': 'package'
868
1019
  });
869
1020
  }
870
- if (options.current) {
1021
+ if (taskId) {
871
1022
  list.push({
872
1023
  isFile: function() {
873
1024
  return false;
@@ -937,21 +1088,65 @@ export async function readDir(path: string, options: {
937
1088
  }
938
1089
  return list;
939
1090
  }
940
- else if (path.startsWith('/storage/')) {
941
- // --- TODO ---
942
- return [];
943
- }
944
- else if (path.startsWith('/mounted/')) {
945
- // --- TODO ---
946
- return [];
1091
+ else if (path.startsWith('/storage/') || path.startsWith('/mounted/')) {
1092
+ const list: types.IDirent[] = [];
1093
+ if (path === '/mounted/') {
1094
+ for (const name in mounts) {
1095
+ list.push({
1096
+ isFile: function() {
1097
+ return false;
1098
+ },
1099
+ isDirectory: function() {
1100
+ return true;
1101
+ },
1102
+ isSymbolicLink: function() {
1103
+ return false;
1104
+ },
1105
+ 'name': name
1106
+ });
1107
+ }
1108
+ return list;
1109
+ }
1110
+ const r = await task.checkPermission('fs.' + path + 'r', false, undefined, taskId);
1111
+ if (!r[0]) {
1112
+ return [];
1113
+ }
1114
+ if (path.startsWith('/mounted/')) {
1115
+ const name = getMountName(path);
1116
+ const hanlder = mounts[name];
1117
+ if (!hanlder) {
1118
+ return [];
1119
+ }
1120
+ return hanlder.readDir?.(path.slice(9 + name.length), encoding) ?? [];
1121
+ }
1122
+ // --- storage ---
1123
+ const ls = await native.invoke('cg-fs-readDir', native.getToken(), fpath, encoding);
1124
+ for (const item of ls) {
1125
+ list.push({
1126
+ isFile: function() {
1127
+ return item.isFile;
1128
+ },
1129
+ isDirectory: function() {
1130
+ return item.isDirectory;
1131
+ },
1132
+ isSymbolicLink: function() {
1133
+ return item.isSymbolicLink;
1134
+ },
1135
+ 'name': item.name
1136
+ });
1137
+ }
1138
+ return list;
947
1139
  }
948
- else if (path.startsWith('/package/')) {
949
- if (!options.files) {
1140
+ else if (path.startsWith('/package/') || path.startsWith('/current/')) {
1141
+ if (!taskId) {
950
1142
  return [];
951
1143
  }
1144
+ if (path.startsWith('/current/')) {
1145
+ return readDir(task.list[taskId].current + fpath, encoding, taskId);
1146
+ }
952
1147
  const list: types.IDirent[] = [];
953
1148
  const dirs: string[] = [];
954
- for (const p in options.files) {
1149
+ for (const p in task.list[taskId].app.files) {
955
1150
  if (!p.startsWith(fpath)) {
956
1151
  continue;
957
1152
  }
@@ -993,13 +1188,6 @@ export async function readDir(path: string, options: {
993
1188
  }
994
1189
  return list;
995
1190
  }
996
- else if (path.startsWith('/current/')) {
997
- if (!options.current) {
998
- return [];
999
- }
1000
- const current = options.current.endsWith('/') ? options.current.slice(0, -1) : options.current;
1001
- return readDir(current + fpath, options);
1002
- }
1003
1191
  else {
1004
1192
  return [];
1005
1193
  }
@@ -1009,53 +1197,56 @@ export async function readDir(path: string, options: {
1009
1197
  * --- 复制文件夹里的内容到另一个地方,失败不会回滚 ---
1010
1198
  * @param from 源,末尾加 /
1011
1199
  * @param to 目标,末尾加 /
1012
- * @param options 选项
1200
+ * @param ignore 忽略的文件
1201
+ * @param taskId App 模式下无效
1013
1202
  */
1014
- export async function copyFolder(from: string, to: string, options: {
1015
- 'ignore'?: RegExp[];
1016
- 'current'?: string;
1017
- } = {}): Promise<number> {
1018
- from = tool.urlResolve('/', from);
1019
- to = tool.urlResolve('/', to);
1020
- if (!from.startsWith(to.slice(0, 9))) {
1021
- return 0;
1022
- }
1023
- const ffpath = from.slice(8);
1024
- const tfpath = from.slice(8);
1025
- if (from.startsWith('/clickgo/')) {
1026
- return 0;
1027
- }
1028
- else if (from.startsWith('/storage/')) {
1029
- // --- TODO ---
1030
- return 0;
1031
- }
1032
- else if (from.startsWith('/mounted/')) {
1033
- // --- TODO ---
1034
- return 0;
1035
- }
1036
- else if (from.startsWith('/package/')) {
1203
+ export async function copyFolder(from: string, to: string, ignore: RegExp[] = [], taskId?: number): Promise<number> {
1204
+ let num = 0;
1205
+ // --- 如果源目录不存在或不是目录,则直接成功 :) ---
1206
+ if (!await isDir(from, taskId)) {
1037
1207
  return 0;
1038
1208
  }
1039
- else if (from.startsWith('/current/')) {
1040
- if (!options.current) {
1041
- return 0;
1209
+ // --- 遍历源目录文件和文件夹,准备复制 ---
1210
+ const flist = await readDir(from, undefined, taskId);
1211
+ /** --- to 目录是否检查是否存在,空目录不复制,所以确定有 item file 的时候才创建 --- */
1212
+ let checkTo = false;
1213
+ for (const item of flist) {
1214
+ if (item.isDirectory()) {
1215
+ const r = await copyFolder(from + item.name + '/', to + item.name + '/', ignore, taskId);
1216
+ if (r === -1) {
1217
+ return r;
1218
+ }
1219
+ else {
1220
+ num += r;
1221
+ }
1222
+ }
1223
+ else if (item.isFile()) {
1224
+ // --- 先判断本文件是否被排除 ---
1225
+ if (ignore.length > 0 && tool.match(item.name, ignore)) {
1226
+ continue;
1227
+ }
1228
+ if (!checkTo) {
1229
+ if (!await mkdir(to, undefined, taskId)) {
1230
+ return -1;
1231
+ }
1232
+ checkTo = true;
1233
+ }
1234
+ if (!(await copyFile(from + item.name, to + item.name, taskId))) {
1235
+ continue;
1236
+ }
1237
+ ++num;
1042
1238
  }
1043
- const current = options.current.endsWith('/') ? options.current.slice(0, -1) : options.current;
1044
- return copyFolder(current + ffpath, current + tfpath, options);
1045
- }
1046
- else {
1047
- return 0;
1048
1239
  }
1240
+ return num;
1049
1241
  }
1050
1242
 
1051
1243
  /**
1052
1244
  * --- 复制文件 ---
1053
1245
  * @param src 源文件
1054
1246
  * @param dest 目标文件
1247
+ * @param taskId App 模式下无效
1055
1248
  */
1056
- export async function copyFile(src: string, dest: string, options: {
1057
- 'current'?: string;
1058
- } = {}): Promise<boolean> {
1249
+ export async function copyFile(src: string, dest: string, taskId?: number): Promise<boolean> {
1059
1250
  src = tool.urlResolve('/', src);
1060
1251
  dest = tool.urlResolve('/', dest);
1061
1252
  if (!src.startsWith(dest.slice(0, 9))) {
@@ -1066,23 +1257,38 @@ export async function copyFile(src: string, dest: string, options: {
1066
1257
  if (src.startsWith('/clickgo/')) {
1067
1258
  return false;
1068
1259
  }
1069
- else if (src.startsWith('/storage/')) {
1070
- // --- TODO ---
1071
- return false;
1072
- }
1073
- else if (src.startsWith('/mounted/')) {
1074
- // --- TODO ---
1075
- return false;
1260
+ else if (src.startsWith('/storage/') || dest.startsWith('/mounted/')) {
1261
+ let r = await task.checkPermission('fs.' + src + 'r', false, undefined, taskId);
1262
+ if (!r[0]) {
1263
+ return false;
1264
+ }
1265
+ r = await task.checkPermission('fs.' + dest + 'w', false, undefined, taskId);
1266
+ if (!r[0]) {
1267
+ return false;
1268
+ }
1269
+ if (src.startsWith('/mounted/')) {
1270
+ const fname = getMountName(src);
1271
+ const lname = getMountName(dest);
1272
+ if (fname !== lname) {
1273
+ return false;
1274
+ }
1275
+ const hanlder = mounts[fname];
1276
+ if (!hanlder) {
1277
+ return false;
1278
+ }
1279
+ return hanlder.copyFile?.(src.slice(9 + fname.length), dest.slice(9 + fname.length)) ?? false;
1280
+ }
1281
+ // --- storage ---
1282
+ return native.invoke('cg-fs-copyFile', native.getToken(), sfpath, dfpath);
1076
1283
  }
1077
1284
  else if (src.startsWith('/package/')) {
1078
1285
  return false;
1079
1286
  }
1080
1287
  else if (src.startsWith('/current/')) {
1081
- if (!options.current) {
1288
+ if (!taskId) {
1082
1289
  return false;
1083
1290
  }
1084
- const current = options.current.endsWith('/') ? options.current.slice(0, -1) : options.current;
1085
- return copyFile(current + sfpath, current + dfpath, options);
1291
+ return copyFile(task.list[taskId].current + sfpath, task.list[taskId].current + dfpath, taskId);
1086
1292
  }
1087
1293
  else {
1088
1294
  return false;