clickgo 3.1.3-dev12 → 3.1.4-dev13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/lib/task.ts CHANGED
@@ -266,7 +266,7 @@ export async function run(url: string, opt: types.ITaskRunOptions = {}): Promise
266
266
  const unblock = opt.unblock ? tool.clone(opt.unblock) : [];
267
267
  const unblockSys = [
268
268
  'require',
269
- '__awaiter', 'eval', 'Math', 'Array', 'Blob', 'Error', 'Infinity', 'parseInt', 'parseFloat', 'Promise', 'Date', 'JSON', 'fetch'
269
+ '__awaiter', 'eval', 'Math', 'Array', 'Blob', 'Error', 'Infinity', 'parseInt', 'parseFloat', 'Promise', 'Date', 'JSON', 'fetch', 'Number'
270
270
  ];
271
271
  for (const name of unblockSys) {
272
272
  if (unblock.includes(name)) {
@@ -306,8 +306,65 @@ export async function run(url: string, opt: types.ITaskRunOptions = {}): Promise
306
306
  }
307
307
  // --- console ---
308
308
  invoke.console = {
309
- log: function(message?: any, ...optionalParams: any[]) {
310
- console.log(message, ...optionalParams);
309
+ assert: function(condition?: boolean, ...data: any[]): void {
310
+ console.assert(condition, ...data);
311
+ },
312
+ clear: function(): void {
313
+ console.clear();
314
+ },
315
+ count: function(label?: string): void {
316
+ console.count(label);
317
+ },
318
+ countReset: function(label?: string): void {
319
+ console.countReset(label);
320
+ },
321
+ debug: function(...data: any[]): void {
322
+ console.debug(...data);
323
+ },
324
+ dir: function(item?: any, options?: any): void {
325
+ console.dir(item, options);
326
+ },
327
+ dirxml: function(...data: any[]): void {
328
+ console.dirxml(...data);
329
+ },
330
+ error: function(...data: any[]): void {
331
+ console.error(...data);
332
+ },
333
+ group: function(...data: any[]): void {
334
+ console.group(...data);
335
+ },
336
+ groupCollapsed: function(...data: any[]): void {
337
+ console.groupCollapsed(...data);
338
+ },
339
+ groupEnd: function(): void {
340
+ console.groupEnd();
341
+ },
342
+ info: function(...data: any[]): void {
343
+ console.info(...data);
344
+ },
345
+ log: function(...data: any[]): void {
346
+ console.log(...data);
347
+ },
348
+ table: function(tabularData?: any, properties?: string[]): void {
349
+ console.table(tabularData, properties);
350
+ },
351
+ time: function(label?: string): void {
352
+ console.time(label);
353
+ },
354
+ timeEnd: function(label?: string): void {
355
+ console.timeEnd(label);
356
+ },
357
+ timeLog: function(label?: string, ...data: any[]): void {
358
+ console.timeLog(label, ...data);
359
+ },
360
+ timeStamp: function(label?: string): void {
361
+ console.timeStamp(label);
362
+ },
363
+ trace: function(...data: any[]): void {
364
+ console.trace(...data);
365
+ },
366
+ warn: function(...data: any[]): void {
367
+ console.warn(...data);
311
368
  }
312
369
  };
313
370
  // --- loader ---
@@ -394,6 +451,9 @@ export async function run(url: string, opt: types.ITaskRunOptions = {}): Promise
394
451
  },
395
452
  getAvailArea: function(): types.IAvailArea {
396
453
  return core.getAvailArea();
454
+ },
455
+ hash: function(hash: string): boolean {
456
+ return core.hash(hash, taskId);
397
457
  }
398
458
  },
399
459
  'dom': {
@@ -613,7 +673,7 @@ export async function run(url: string, opt: types.ITaskRunOptions = {}): Promise
613
673
  }
614
674
  return fs.getContent(path, options);
615
675
  },
616
- putContent: function(path: string, data: string | Buffer, options: any = {}) {
676
+ putContent: function(path: string, data: string | Blob, options: any = {}) {
617
677
  if (!options.current) {
618
678
  options.current = list[taskId].current;
619
679
  }
@@ -745,17 +805,40 @@ export async function run(url: string, opt: types.ITaskRunOptions = {}): Promise
745
805
  return native.invoke(name, ...param);
746
806
  },
747
807
  max: async function(): Promise<void> {
808
+ const rtn = await checkPermission('native.form', false, undefined, taskId);
809
+ if (!rtn[0]) {
810
+ return;
811
+ }
748
812
  await native.max();
749
813
  },
750
814
  min: async function(): Promise<void> {
815
+ const rtn = await checkPermission('native.form', false, undefined, taskId);
816
+ if (!rtn[0]) {
817
+ return;
818
+ }
751
819
  await native.min();
752
820
  },
753
821
  restore: async function(): Promise<void> {
822
+ const rtn = await checkPermission('native.form', false, undefined, taskId);
823
+ if (!rtn[0]) {
824
+ return;
825
+ }
754
826
  await native.restore();
755
827
  },
756
828
  size: async function(width: number, height: number): Promise<void> {
829
+ const rtn = await checkPermission('native.form', false, undefined, taskId);
830
+ if (!rtn[0]) {
831
+ return;
832
+ }
757
833
  await native.size(width, height);
758
834
  },
835
+ maximizable: async function(val: boolean): Promise<void> {
836
+ const rtn = await checkPermission('native.form', false, undefined, taskId);
837
+ if (!rtn[0]) {
838
+ return;
839
+ }
840
+ await native.maximizable(val);
841
+ },
759
842
  ping: function(val: string): Promise<string> {
760
843
  return native.ping(val);
761
844
  },
@@ -796,8 +879,20 @@ export async function run(url: string, opt: types.ITaskRunOptions = {}): Promise
796
879
  }
797
880
  opt.unblock = inUnblock;
798
881
  }
882
+ if (opt.permissions) {
883
+ if (ntask && !ntask.runtime.permissions.includes('root')) {
884
+ opt.permissions = undefined;
885
+ }
886
+ }
799
887
  return run(url, opt);
800
888
  },
889
+ checkPermission: function(
890
+ vals: string | string[],
891
+ apply: boolean = false,
892
+ applyHandler?: (list: string[]) => void | Promise<void>
893
+ ): Promise<boolean[]> {
894
+ return checkPermission(vals, apply, applyHandler, taskId);
895
+ },
801
896
  end: function(tid: number): boolean {
802
897
  return end(tid ?? taskId);
803
898
  },
@@ -972,8 +1067,8 @@ export async function run(url: string, opt: types.ITaskRunOptions = {}): Promise
972
1067
  'current': current,
973
1068
 
974
1069
  'runtime': clickgo.vue.reactive({
975
- 'permissions': {},
976
- 'dialogFormIds': []
1070
+ 'dialogFormIds': [],
1071
+ 'permissions': opt.permissions ?? []
977
1072
  }),
978
1073
  'forms': {},
979
1074
  'controls': {},
@@ -1070,9 +1165,9 @@ export async function run(url: string, opt: types.ITaskRunOptions = {}): Promise
1070
1165
  }
1071
1166
  // --- 触发 taskStarted 事件 ---
1072
1167
  core.trigger('taskStarted', taskId);
1073
- // --- 第一个任务给 native 发送任务启动成功的消息 ---
1074
- if (taskId === 1) {
1075
- await native.invoke('cg-init', native.getToken());
1168
+ // --- 请求权限 ---
1169
+ if (app.config.permissions) {
1170
+ await checkPermission(app.config.permissions, true, undefined, taskId);
1076
1171
  }
1077
1172
  // --- 执行 app ---
1078
1173
  const appCls: core.AbstractApp = new expo.default();
@@ -1081,6 +1176,176 @@ export async function run(url: string, opt: types.ITaskRunOptions = {}): Promise
1081
1176
  return taskId;
1082
1177
  }
1083
1178
 
1179
+ /** --- 本页用到的语言包 --- */
1180
+ const locale: Record<string, {
1181
+ 'unknown': string;
1182
+ // eslint-disable-next-line @typescript-eslint/naming-convention
1183
+ 'apply-permission': string;
1184
+ // eslint-disable-next-line @typescript-eslint/naming-convention
1185
+ 'native.form': string;
1186
+ 'hash': string;
1187
+ 'fs': string;
1188
+ 'readonly': string;
1189
+ // eslint-disable-next-line @typescript-eslint/naming-convention
1190
+ 'read-write': string;
1191
+ }> = {
1192
+ 'sc': {
1193
+ 'unknown': '未知权限',
1194
+ 'apply-permission': '正在申请权限,请您仔细确认',
1195
+ 'native.form': '实体窗体控制',
1196
+ 'hash': '可修改地址栏 hash',
1197
+ 'fs': '文件系统',
1198
+ 'readonly': '只读',
1199
+ 'read-write': '读写'
1200
+ },
1201
+ 'tc': {
1202
+ 'unknown': '未知許可權',
1203
+ 'apply-permission': '正在申請許可權,請您仔細確認',
1204
+ 'native.form': '實體視窗控制',
1205
+ 'hash': '可修改位址列 hash',
1206
+ 'fs': '檔案系統',
1207
+ 'readonly': '唯讀',
1208
+ 'read-write': '讀寫'
1209
+ },
1210
+ 'en': {
1211
+ 'unknown': 'Unknown',
1212
+ 'apply-permission': 'is applying for permissions, please check carefully',
1213
+ 'native.form': 'Native window control',
1214
+ 'hash': 'Can modify the location hash',
1215
+ 'fs': 'File system',
1216
+ 'readonly': 'Read only',
1217
+ 'read-write': 'Read and write'
1218
+ },
1219
+ 'ja': {
1220
+ 'unknown': '不明な許可',
1221
+ 'apply-permission': '許可申請中、よくご確認ください',
1222
+ 'native.form': 'ローカルウィンドウを操作する',
1223
+ 'hash': '網址の hash 変更可能',
1224
+ 'fs': '資料システム',
1225
+ 'readonly': '読み取り専用',
1226
+ 'read-write': '読み書き'
1227
+ }
1228
+ };
1229
+
1230
+ // fs.{path}{r/w},path 以 / 结尾则是路径权限,不以 / 结尾是文件权限
1231
+
1232
+ /**
1233
+ * --- 检测应用是否有相应的权限 ---
1234
+ * @param vals 要检测的权限
1235
+ * @param apply 如果没有权限是否自动弹出申请,默认为否
1236
+ * @param applyHandler 向用户申请成功的权限列表回调
1237
+ * @param taskId 要检查的任务 ID,App 模式下无效
1238
+ */
1239
+ export async function checkPermission(
1240
+ vals: string | string[],
1241
+ apply: boolean = false,
1242
+ applyHandler?: (list: string[]) => void | Promise<void>,
1243
+ taskId?: number
1244
+ ): Promise<boolean[]> {
1245
+ if (!taskId) {
1246
+ return [false];
1247
+ }
1248
+ const task = list[taskId];
1249
+ if (!task) {
1250
+ return [false];
1251
+ }
1252
+ if (typeof vals === 'string') {
1253
+ vals = [vals];
1254
+ }
1255
+ const rtn: boolean[] = [];
1256
+ /** --- 需要申请的权限 --- */
1257
+ const applyList: string[] = [];
1258
+ for (const val of vals) {
1259
+ if (task.runtime.permissions.includes('root')) {
1260
+ // --- 有 root 权限,一定成功 ---
1261
+ rtn.push(true);
1262
+ continue;
1263
+ }
1264
+ if (val.startsWith('fs.')) {
1265
+ // --- fs 判断比较特殊 ---
1266
+ let yes = false;
1267
+ const path = val.slice(3, -1);
1268
+ for (const v of task.runtime.permissions) {
1269
+ if (!v.startsWith('fs.')) {
1270
+ continue;
1271
+ }
1272
+ const pa = v.slice(3, -1);
1273
+ if (pa.endsWith('/')) {
1274
+ if (!path.startsWith(pa)) {
1275
+ continue;
1276
+ }
1277
+ }
1278
+ else if (pa !== path) {
1279
+ continue;
1280
+ }
1281
+ // --- 找到了 ---
1282
+ if (val.endsWith('w')) {
1283
+ // --- 用户要求读写 ---
1284
+ if (v.endsWith('r')) {
1285
+ // --- 但目前只有读的权限 ---
1286
+ continue;
1287
+ }
1288
+ }
1289
+ // --- 正常,有权限 ---
1290
+ yes = true;
1291
+ break;
1292
+ }
1293
+ rtn.push(yes);
1294
+ if (!yes && apply) {
1295
+ // --- 要申请权限 ---
1296
+ applyList.push(val);
1297
+ }
1298
+ continue;
1299
+ }
1300
+ // --- 其他权限判断 ---
1301
+ const result = task.runtime.permissions.includes(val);
1302
+ if (!result && apply) {
1303
+ // --- 要申请权限 ---
1304
+ applyList.push(val);
1305
+ }
1306
+ rtn.push(result);
1307
+ }
1308
+ // --- 申请权限 ---
1309
+ if (applyList.length) {
1310
+ let html = '<div>"' + tool.escapeHTML(task.app.config.name) + '" ' + ((locale[core.config.locale]?.['apply-permission'] ?? locale['en']['apply-permission']) + ':') + '</div>';
1311
+ for (const item of applyList) {
1312
+ if (item.startsWith('fs.')) {
1313
+ // --- fs 判断比较特殊 ---
1314
+ const path = item.slice(3, -1);
1315
+ html += '<div style="margin-top: 10px;">' +
1316
+ (locale[core.config.locale]?.fs ?? locale['en'].fs) + ' ' + tool.escapeHTML(path) + ' ' + (item.endsWith('r') ? (locale[core.config.locale]?.readonly ?? locale['en'].readonly) : (locale[core.config.locale]?.['read-write'] ?? locale['en']['read-write'])) +
1317
+ '<div style="color: var(--system-border-color);">' + tool.escapeHTML(item) + '</div>' +
1318
+ '</div>';
1319
+ continue;
1320
+ }
1321
+ const lang = (locale as any)[core.config.locale]?.[item] ?? (locale as any)['en'][item];
1322
+ html += '<div style="margin-top: 10px;">' +
1323
+ (lang ?? locale[core.config.locale]?.unknown ?? locale['en'].unknown) +
1324
+ '<div style="color: var(--system-border-color);">' + tool.escapeHTML(item) + '</div>' +
1325
+ '</div>';
1326
+ }
1327
+ if (await form.superConfirm(html)) {
1328
+ // --- 所有 false 变成 true ---
1329
+ for (let i = 0; i < rtn.length; ++i) {
1330
+ if (rtn[i]) {
1331
+ continue;
1332
+ }
1333
+ rtn[i] = true;
1334
+ }
1335
+ for (const item of applyList) {
1336
+ task.runtime.permissions.push(item);
1337
+ }
1338
+ try {
1339
+ applyHandler?.(applyList) as any;
1340
+ }
1341
+ catch (e) {
1342
+ console.log('task.checkPermission', e);
1343
+ }
1344
+ }
1345
+ }
1346
+ return rtn;
1347
+ }
1348
+
1084
1349
  /**
1085
1350
  * --- 完全结束任务 ---
1086
1351
  * @param taskId 任务 id
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clickgo",
3
- "version": "3.1.3-dev12",
3
+ "version": "3.1.4-dev13",
4
4
  "description": "Background interface, software interface, mobile phone APP interface operation library.",
5
5
  "keywords": [
6
6
  "deskrt",
@@ -20,7 +20,7 @@
20
20
  "devDependencies": {
21
21
  "@litert/eslint-plugin-rules": "^0.1.0",
22
22
  "@litert/loader": "^3.4.3",
23
- "@types/node": "^17.0.21",
23
+ "@types/node": "^18.11.14",
24
24
  "electron": "^19.1.3",
25
25
  "typescript": "^4.8.4"
26
26
  },
package/types/index.d.ts CHANGED
@@ -83,7 +83,7 @@ export interface IAvailArea {
83
83
  }
84
84
 
85
85
  /** --- 全局事件类型 --- */
86
- export type TGlobalEvent = 'error' | 'screenResize' | 'configChanged' | 'formCreated' | 'formRemoved' | 'formTitleChanged' | 'formIconChanged' | 'formStateMinChanged' | 'formStateMaxChanged' | 'formShowChanged' | 'formFocused' | 'formBlurred' | 'formFlash' | 'taskStarted' | 'taskEnded' | 'launcherFolderNameChanged';
86
+ export type TGlobalEvent = 'error' | 'screenResize' | 'configChanged' | 'formCreated' | 'formRemoved' | 'formTitleChanged' | 'formIconChanged' | 'formStateMinChanged' | 'formStateMaxChanged' | 'formShowChanged' | 'formFocused' | 'formBlurred' | 'formFlash' | 'taskStarted' | 'taskEnded' | 'launcherFolderNameChanged' | 'hashChanged';
87
87
 
88
88
  /** --- 现场下载 app 的参数 --- */
89
89
  export interface ICoreFetchAppOptions {
@@ -99,7 +99,7 @@ export interface IApp {
99
99
  'config': IAppConfig;
100
100
  /** --- 所有已加载的文件内容 --- */
101
101
  'files': Record<string, Blob | string>;
102
- /** --- 应用图标,net 模式下可能为空 --- */
102
+ /** --- 应用图标 --- */
103
103
  'icon': string;
104
104
  }
105
105
 
@@ -119,7 +119,7 @@ export interface IAppConfig {
119
119
  /** --- 将自动加载的主题 --- */
120
120
  'themes'?: string[];
121
121
  /** --- 将自动申请的权限 --- */
122
- 'permissions'?: Record<string, any>;
122
+ 'permissions'?: string[];
123
123
  /** --- 将自动加载的语言包,path: lang --- */
124
124
  'locales'?: Record<string, string>;
125
125
  /** --- 全局样式,不带扩展名,系统会在末尾添加 .css --- */
@@ -321,21 +321,64 @@ export interface IFormCreateCode {
321
321
  // --------- fs lib ---------
322
322
  // --------------------------
323
323
 
324
+ export interface IMountHandler {
325
+ 'taskId'?: number;
326
+ getContent?: (path: string, options?: BufferEncoding | {
327
+ 'encoding'?: BufferEncoding;
328
+ 'start'?: number;
329
+ 'end'?: number;
330
+ 'files'?: Record<string, Blob | string>;
331
+ 'current'?: string;
332
+ 'progress'?: (loaded: number, total: number) => void | Promise<void>;
333
+ }) => Promise<Blob | string | null>;
334
+ putContent?: (path: string, data: string | Blob, options?: {
335
+ 'encoding'?: BufferEncoding | null;
336
+ 'mode'?: string | number;
337
+ 'flag'?: string | number;
338
+ 'current'?: string;
339
+ }) => Promise<boolean>;
340
+ readLink?: (path: string, options?: BufferEncoding | {
341
+ 'encoding'?: BufferEncoding;
342
+ /** --- 不以 / 结尾的路径 --- */
343
+ 'current'?: string;
344
+ }) => Promise<string | null>;
345
+ symlink?: (filePath: string, linkPath: string, options?: {
346
+ 'type'?: 'dir' | 'file' | 'junction';
347
+ 'current'?: string;
348
+ }) => Promise<boolean>;
349
+ unlink?: (path: string, options?: {
350
+ 'current'?: string;
351
+ }) => Promise<boolean>;
352
+ stats: (path: string, options?: {
353
+ 'files'?: Record<string, Blob | string>;
354
+ 'current'?: string;
355
+ }) => Promise<IStats | null>;
356
+ mkdir: (path: string, mode?: number, options?: {
357
+ 'current'?: string;
358
+ }) => Promise<boolean>;
359
+ rmdir: (path: string, options?: {
360
+ 'current'?: string;
361
+ }) => Promise<boolean>;
362
+ chmod: (path: string, mod: string | number, options?: {
363
+ 'current'?: string;
364
+ }) => Promise<boolean>;
365
+ }
366
+
324
367
  /** --- 文件/文件夹信息对象 --- */
325
368
  export interface IStats {
326
369
  isFile(): boolean;
327
370
  isDirectory(): boolean;
328
371
  isSymbolicLink(): boolean;
329
- size: number;
330
- blksize: number;
331
- atimeMs: number;
332
- mtimeMs: number;
333
- ctimeMs: number;
334
- birthtimeMs: number;
335
- atime: Date;
336
- mtime: Date;
337
- ctime: Date;
338
- birthtime: Date;
372
+ 'size': number;
373
+ 'blksize': number;
374
+ 'atimeMs': number;
375
+ 'mtimeMs': number;
376
+ 'ctimeMs': number;
377
+ 'birthtimeMs': number;
378
+ 'atime': Date;
379
+ 'mtime': Date;
380
+ 'ctime': Date;
381
+ 'birthtime': Date;
339
382
  }
340
383
 
341
384
  /** --- 目录下项目 --- */
@@ -343,7 +386,7 @@ export interface IDirent {
343
386
  isFile(): boolean;
344
387
  isDirectory(): boolean;
345
388
  isSymbolicLink(): boolean;
346
- name: string;
389
+ 'name': string;
347
390
  }
348
391
 
349
392
  // --------------------------
@@ -370,7 +413,7 @@ export interface ITask {
370
413
  /** --- 独占窗体序列 --- */
371
414
  'dialogFormIds': number[];
372
415
  /** --- 已申请的权限列表 --- */
373
- 'permissions': Record<string, any>;
416
+ 'permissions': string[];
374
417
  };
375
418
  /** --- 窗体对象列表 --- */
376
419
  'forms': Record<string, IForm>;
@@ -403,8 +446,10 @@ export interface ITaskRunOptions {
403
446
  'notify'?: boolean;
404
447
  /** --- 所属任务,App 模式无法设置 --- */
405
448
  'taskId'?: number;
406
- /** --- 不禁止某些浏览器对象,App 模式下无法设置 --- */
449
+ /** --- 不禁止某些浏览器对象,App 模式下仅能设置基任务中已经 unblock 的值 --- */
407
450
  'unblock'?: string[];
451
+ /** --- 直接赋予此任务相应权限,App 模式下有 "root" 权限的应用才能设置 --- */
452
+ 'permissions'?: string[];
408
453
  }
409
454
 
410
455
  export interface ICreateTimerOptions {