@environment-safe/file 0.0.1 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.mjs CHANGED
@@ -1,378 +1,138 @@
1
- /*
2
- import { isBrowser, isJsDom } from 'browser-or-node';
3
- import * as mod from 'module';
4
- import * as path from 'path';
5
- let internalRequire = null;
6
- if(typeof require !== 'undefined') internalRequire = require;
7
- const ensureRequire = ()=> (!internalRequire) && (internalRequire = mod.createRequire(import.meta.url));
8
- //*/
9
-
10
1
  /**
11
2
  * A JSON object
12
3
  * @typedef { object } JSON
13
4
  */
14
5
 
15
- import { isBrowser, isJsDom } from 'browser-or-node';
6
+ // Isn't the fact that we had 2 path scenarios driven by OS, fixed it, (win, unix)
7
+ // and we now have an explosion an obvious indicator that we went wrong?
8
+ // win, posix, file:win, file:posix, known dirs, web urls, plus file: behavior is different on local vs server
9
+
16
10
  import { FileBuffer } from './buffer.mjs';
17
- import * as fs from 'fs';
18
- import * as path from 'path';
19
-
20
- const inputQueue = [];
21
- const attachInputGenerator = (eventType)=>{
22
- const handler = (event)=>{
23
- if(inputQueue.length){
24
- const input = inputQueue.shift();
25
- try{
26
- input.handler(event, input.resolve, input.reject);
27
- }catch(ex){
28
- inputQueue.unshift(input);
29
- }
30
- }
31
- };
32
- window.addEventListener('load', (event) => {
33
- document.body.addEventListener(eventType, handler, false);
34
- });
35
- //document.body.addEventListener(eventType, handler, false);
36
- };
37
-
38
- if(isBrowser || isJsDom){
39
- attachInputGenerator('mousedown');
40
- // mousemove is cleanest, but seems unreliable
41
- // attachInputGenerator('mousemove');
42
- }
43
-
44
- const wantInput = async (id, handler, cache)=>{
45
- const promise = new Promise((resolve, reject)=>{
46
- inputQueue.push({ resolve, reject, handler });
47
- });
48
- const input = await promise;
49
- return await input;
11
+ import {
12
+ isServer, // is running on a server runtime
13
+ isLocalFileRoot, // run within a page using a file: url
14
+ variables
15
+ } from '@environment-safe/runtime-context';
16
+ import { localFile as lf, serverFile as sf, file as f, remote as r, setInputHandler, bindInput} from './filesystem.mjs';
17
+ import { Path } from './path.mjs';
18
+ export { Path, setInputHandler, bindInput };
19
+ const handleCanonicalPath = (dir, os, user)=>{
20
+
50
21
  };
51
22
 
52
- const getFilePickerOptions = (name, path)=>{
53
- let suffix = name.split('.').pop();
54
- if(suffix.length > 6) suffix = '';
55
- const options = {
56
- suggestedName: name
57
- };
58
- if(path) options.startIn = path;
59
- if(suffix){
60
- const accept = {};
61
- accept[mimesBySuffix[suffix]] = '.'+suffix;
62
- options.types = [{
63
- description: suffix,
64
- accept,
65
- }];
66
- options.excludeAcceptAllOption = true;
67
- }
68
- return options;
69
- };
23
+ let localFile=null;
24
+ let serverFile=null;
25
+ let file=null;
26
+ let remote=null;
70
27
 
71
- const makeLocation = (path, dir)=>{
72
- if(dir && dir[0] === '.'){
73
- if(dir[1] === '.'){
74
- if(dir[2] === '/' && dir[3]){
75
- return pathJoin(File.directory.current, '..', dir.substring(3), path);
76
- }else{
77
- if(dir[2]){
78
- return pathJoin(File.directory.current, '..', dir.substring(3), path);
79
- }else{
80
- return pathJoin(File.directory.current, '..', path);
81
- }
82
- }
28
+ export const initialized = async (path, options={})=>{
29
+ if(isServer){
30
+ if(serverFile) return;
31
+ serverFile = await sf.initialize();
32
+ }else{
33
+ if(isLocalFileRoot){
34
+ if(file) return;
35
+ file = await f.initialize();
83
36
  }else{
84
- if(dir[1] === '/'){
85
- return pathJoin(File.directory.current, dir.substring(2), path);
37
+ if(path.indexOf('://') !== -1){
38
+ if(path.indexOf('file://') === 0){
39
+ try{
40
+ Path.relative(path);
41
+ }catch(ex){
42
+ //if it's not
43
+ // 1) A file url root
44
+ // 2) within the web root
45
+ // we have no idea what to do
46
+ console.log(ex);
47
+ throw new Error(`Could not resolve path:${path}`);
48
+ }
49
+ }
50
+ if(remote) return;
51
+ //remote url
52
+ remote = await r.initialize();
86
53
  }else{
87
- if(dir[1]){
88
- return pathJoin(File.directory.current, dir, path);
54
+ if(options.filesystemAPI){
55
+ if(localFile) return;
56
+ //an absolute or relative file path
57
+ localFile = await lf.initialize();
89
58
  }else{
90
- return pathJoin(File.directory.current, path);
59
+ if(remote) return;
60
+ //remote url
61
+ remote = await r.initialize();
91
62
  }
92
63
  }
93
64
  }
94
65
  }
95
- return dir?handleCanonicalPath(dir, File.os, File.user)+ '/' + path:path;
96
66
  };
97
67
 
98
- export const save = async (name, dir, buffer, meta={})=>{
99
- const location = makeLocation(name, dir);
100
- if(isBrowser || isJsDom){
101
- const options = getFilePickerOptions(name, dir);
102
- const newHandle = await wantInput(location, (event, resolve, reject)=>{
103
- try{
104
- window.showSaveFilePicker(options).then((thisHandle)=>{
105
- resolve(thisHandle);
106
- }).catch((ex)=>{
107
- reject(ex);
108
- });
109
- }catch(ex){
110
- reject(ex);
111
- }
112
- }, meta.cache);
113
- const writableStream = await newHandle.createWritable();
114
- // write our file
115
- await writableStream.write(buffer);
116
- // close the file and write the contents to disk.
117
- await writableStream.close();
68
+ export const act = async (action, ...args)=>{
69
+ const path = args[0];
70
+ const options = args[1] || {};
71
+ await initialized(path, options);
72
+ if(isServer){
73
+ return serverFile[action].apply(serverFile, args);
118
74
  }else{
119
- return await new Promise((resolve, reject)=>{
120
- fs.writeFile(location, buffer, (err)=>{
121
- if(err) return reject(err);
122
- resolve();
123
- });
124
- });
125
- }
126
- };
127
-
128
- const mimesBySuffix = {
129
- json : 'application/json',
130
- jpg : 'image/jpeg',
131
- jpeg : 'image/jpeg',
132
- gif : 'image/gif',
133
- png : 'image/png',
134
- svg : 'image/sxg+xml',
135
- webp : 'image/webp',
136
- csv : 'text/csv',
137
- tsv : 'text/tsv',
138
- ssv : 'text/ssv',
139
- js : 'text/javascript',
140
- mjs : 'text/javascript',
141
- cjs : 'text/javascript',
142
- css : 'text/css',
143
- };
144
-
145
- export const pathJoin = (...parts)=>{ //returns buffer, eventually stream
146
- if(isBrowser || isJsDom){
147
- return parts.join('/');
148
- }else{
149
- return path.join.apply(path, parts);
150
- }
151
- };
152
-
153
-
154
- //todo: should I remove export (no one should use this)?
155
- export const fileBody = async (path, dir, baseDir, allowRedirect, forceReturn)=>{
156
- try{
157
- //let location = dir?dir+ '/' + path:path; //todo: looser handling
158
- let location = makeLocation(path, dir);
159
- if(canonicalLocationToPath['darwin'][dir]){
160
- if(baseDir){
161
- throw new Error('custom directories unsupported');
75
+ if(isLocalFileRoot){
76
+ return file[action].apply(file, args);
77
+ }else{
78
+ if(path.indexOf('://') !== -1){
79
+ if(path.indexOf('file://') === 0){
80
+ try{
81
+ const currentProtocol = variables.location?variables.location.protocol:'http:';
82
+ const relativePath = Path.from(path).toUrl(currentProtocol, true);
83
+ args[0] = '../'+relativePath;
84
+ }catch(ex){
85
+ //if it's not
86
+ // 1) A file url root
87
+ // 2) within the web root
88
+ // we have no idea what to do
89
+ throw new Error(`Could not resolve path:${path}`);
90
+ }
91
+ }
92
+ return remote[action].apply(remote, args);
93
+
162
94
  }else{
163
- location = 'file://'+handleCanonicalPath(dir, File.os, File.user)+'/'+path;
95
+ if(options.filesystemAPI){
96
+ return localFile[action].apply(localFile, args);
97
+ }else{
98
+ return remote[action].apply(remote, args);
99
+ }
164
100
  }
165
101
  }
166
- const response = await fetch(location);
167
- const text = await response.text();
168
- if(!(response.ok || (allowRedirect && response.redirected) || forceReturn)){
169
- return null;
170
- }
171
- return text;
172
- }catch(ex){
173
- //console.log(location, ex);
174
- return null;
175
102
  }
176
103
  };
177
104
 
178
- export const handle = async (path, dir, writable, cache={})=>{ //returns buffer, eventually stream
179
- let suffix = path.split('.').pop();
180
- if(suffix.length > 6) suffix = '';
181
- const location = makeLocation(path, dir);
182
- if(isBrowser || isJsDom){
183
- if(cache && cache[path]) return cache[path];
184
- const options = getFilePickerOptions(path);
185
- try{
186
- const response = await fileBody(path, dir);
187
- if(response === null) throw new Error('File not found');
188
- }catch(ex){
189
- const newHandle = await wantInput(location, (event, resolve, reject)=>{
190
- try{
191
- window.showSaveFilePicker(options).then((thisHandle)=>{
192
- resolve(thisHandle);
193
- }).catch((ex)=>{
194
- reject(ex);
195
- });
196
- }catch(ex){
197
- reject(ex);
198
- }
199
- });
200
- return newHandle;
201
- }
202
- const fileHandle = await wantInput(location, (event, resolve, reject)=>{
203
- try{
204
- window.showOpenFilePicker(options).then(([ handle ])=>{
205
- resolve(handle);
206
- }).catch((ex)=>{
207
- reject(ex);
208
- });
209
- }catch(ex){
210
- reject(ex);
211
- }
212
- }, cache);
213
- // eslint-disable-next-line no-undef
214
- if(cache) cache[location] = fileHandle;
215
- // eslint-disable-next-line no-undef
216
- return fileHandle;
217
- }else{
218
- // todo: impl
219
- }
105
+ //*
106
+ export const read = async (path, options)=>{
107
+ return await act('read', path, options);
220
108
  };
221
109
 
222
- export const load = async (path, dir, cache)=>{ //returns buffer, eventually stream
223
- const location = makeLocation(path, dir);
224
- if(isBrowser || isJsDom){
225
- try{
226
- const response = await fetch(location);
227
- if(!response){
228
- return new ArrayBuffer();
229
- }
230
- const buffer = await response.arrayBuffer();
231
- buffer;
232
- return buffer;
233
- }catch(ex){
234
- return new ArrayBuffer();
235
- }
236
- }else{
237
- return await new Promise((resolve, reject)=>{
238
- fs.readFile(location, (err, body)=>{
239
- if(err) return reject(err);
240
- resolve(body);
241
- });
242
- });
243
- }
110
+ export const list = async (path, options)=>{
111
+ return await act('list', path, options);
244
112
  };
245
113
 
246
- export const exists = async (path, dir, cache, incomingHandle)=>{ //returns buffer, eventually stream
247
- if(isBrowser || isJsDom){
248
- if(incomingHandle){
249
- const fileHandle = incomingHandle;
250
- const file = await fileHandle.getFile();
251
- const buffer = await file.arrayBuffer();
252
- return !!buffer;
253
- }else{
254
- const body = await fileBody(path, dir);
255
- return body !== null;
256
- }
257
- }else{
258
- return await new Promise((resolve, reject)=>{
259
- const location = makeLocation(path, dir);
260
- fs.stat(location, (err, res)=>{
261
- if(err) resolve(false);
262
- resolve(true);
263
- });
264
- });
265
- }
114
+ export const write = async (path, buffer, options)=>{
115
+ return await act('write', path, buffer, options);
266
116
  };
267
117
 
268
- export const remove = async (path, dir, cache)=>{
269
- if(isBrowser || isJsDom){
270
- const fileHandle = await handle(path, dir, true, cache);
271
- if(fileHandle.remove) fileHandle.remove(); //non-standard, but supported
272
- }else{
273
- // todo: impl
274
- }
118
+ export const create = async (path)=>{
119
+ return await act('create', path);
275
120
  };
276
121
 
277
- export const info = async (path, dir, cache)=>{
278
- if(isBrowser || isJsDom){
279
- // todo: impl
280
- }else{
281
- // todo: impl
282
- }
122
+ export const exists = async (path)=>{
123
+ return await act('exists', path);
283
124
  };
284
125
 
285
- export const list = async (path, options={})=>{
286
- if(isBrowser || isJsDom){
287
- // todo: impl
288
- switch(File.agent.name){
289
- case 'chrome': {
290
- const page = await fileBody('', path, null, null, true);
291
- let rows = (page && page.match( /<script>addRow\((.*)\);<\/script>/g ) ) || [];
292
- rows = rows.map((row)=>{
293
- return row.match( /<script>addRow\((.*)\);<\/script>/ )[1];
294
- });
295
- const jsonData = `[[${rows.join('], [')}]]`;
296
- const data = JSON.parse(jsonData);
297
- let results = data.map((meta)=>{
298
- return {
299
- name: meta[0],
300
- isFile: ()=>{
301
- return !!meta[2];
302
- }
303
- };
304
- });
305
- if(Object.keys(options).length){
306
- if(options.files === false){
307
- results = results.filter((file)=>{
308
- return !file.isFile();
309
- });
310
- }
311
- if(options.directories === false){
312
- results = results.filter((file)=>{
313
- return file.isFile();
314
- });
315
- }
316
- if(!options.hidden){
317
- results = results.filter((file)=>{
318
- return file !== '.' && file !== '..';
319
- });
320
- }
321
- }
322
- return results.map((file)=>{
323
- return file.name;
324
- });
325
- //TODO: apache fallback
326
- //break;
327
- }
328
- default: throw new Error(`Usupported Browser: ${File.os}`);
329
- }
330
- }else{
331
- //todo: platform safe separator
332
- const target = path.indexOf('/') === -1?makeLocation('', path):path;
333
- return await new Promise((resolve, reject)=>{
334
- fs.readdir(target, { withFileTypes: true }, (err, files)=>{
335
- if(err) return reject(err);
336
- let results = files;
337
- if(Object.keys(options).length){
338
- if(options.files === false){
339
- results = results.filter((file)=>{
340
- return !file.isFile();
341
- });
342
- }
343
- if(options.directories === false){
344
- results = results.filter((file)=>{
345
- return file.isFile();
346
- });
347
- }
348
- if(!options.hidden){
349
- results = results.filter((file)=>{
350
- return file !== '.' && file !== '..';
351
- });
352
- }
353
- }
354
- resolve(results.map((file)=>{
355
- return file.name;
356
- }));
357
- });
358
- });
359
- }
126
+ export const remove = async (path)=>{
127
+ return await act('delete', path);
360
128
  };
361
129
 
362
130
  const internalCache = {};
363
131
 
364
- export const listFiles = (path)=>{
365
- return list(path).map((src)=>{
366
- const url = src.indexOf('://') !== -1?src:`file://${src}`;
367
- return new File(url);
368
- });
369
- };
370
-
371
-
372
132
  export class File{
373
133
  constructor(path, options={}){
374
134
  //todo: clean this rats nest up
375
- const location = ( (path && path[0] === '/')?`file:${path}`:path ) ||
135
+ const location = ( path ) ||
376
136
  ( (!path) && options.directory && handleCanonicalPath(options.directory, File.os, File.user) ) ||
377
137
  ('/tmp/' + Math.floor( Math.random() * 10000 ));
378
138
  if(options.cache === true) options.cache = internalCache;
@@ -380,220 +140,87 @@ export class File{
380
140
  //one of: desktop, documents, downloads, music, pictures, videos
381
141
  this.directory = options.directory || '.';
382
142
  this.path = location;
383
- this.buffer = new FileBuffer();
143
+ this.setBuffer(FileBuffer.from(''));
384
144
  }
385
145
 
386
146
  async save(){
387
- await save(this.path, this.directory, this.buffer, this.options);
147
+ await write(this.path, this.buffer, this.options);
388
148
  return this;
389
149
  }
390
150
 
391
- async load(){
392
- const dir = this.path.indexOf('/') === -1?this.directory:'';
393
- this.buffer = await load(this.path, dir, this.options);
151
+ setBuffer(buffer){
152
+ this.buffer = buffer;
153
+ if(!this.buffer) throw new Error(`Error: ${this.path} ${this.options}`);
394
154
  this.buffer.cast = (type)=>{
395
155
  return FileBuffer.to(type, this.buffer);
396
156
  };
157
+ }
158
+
159
+ async load(){
160
+ this.setBuffer(await read(this.path, this.options));
397
161
  return this;
398
162
  }
399
163
 
400
164
  body(value){
401
165
  if(value === null || value === undefined) return this.buffer;
402
- this.buffer = FileBuffer.from(value);
403
- this.buffer.cast = (type)=>{
404
- return FileBuffer.to(type, this.buffer);
405
- };
406
- if(value) return this;
407
- return this.buffer;
166
+ this.setBuffer(FileBuffer.from(value));
167
+ return this;
408
168
  }
409
169
 
410
170
  async info(){
411
- return await info(this.path, this.directory);
171
+ //return await info(this.path);
412
172
  }
413
173
 
414
174
  async 'delete'(){
415
- await remove(this.path, this.directory, this.options);
175
+ await remove(this.path, this.options);
416
176
  return this;
417
177
  }
418
178
 
419
- static exists(path, directory){
420
- return exists(path, directory);
179
+ static async exists(path, directory){
180
+ return await exists(path, directory);
421
181
  }
422
182
 
423
- static list(path, options){
424
- return list(path, options);
183
+ static async list(path, options){
184
+ return await list(path, options);
425
185
  }
426
186
  }
427
- let user = '';
428
- Object.defineProperty(File, 'user', {
429
- get() {
430
- if(isBrowser || isJsDom){
431
- return user || 'khrome'; //todo: something real;
432
- }else{
433
- return user || 'khrome'; //todo: something real;
434
- }
435
- },
436
- set(newValue) {
437
- user = newValue;
438
- },
439
- enumerable: true,
440
- configurable: true,
441
- });
442
-
443
- Object.defineProperty(File, 'os', {
444
- get() {
445
- if(isBrowser || isJsDom){
446
- return 'darwin'; //todo: something real;
447
- }else{
448
- return 'darwin';
449
- }
450
- },
451
- set(newValue) {
452
- //do nothing
453
- },
454
- enumerable: true,
455
- configurable: true,
456
- });
457
-
458
- const canonicalLocationToPath = {
459
- darwin : {
460
- 'desktop': '~/Desktop',
461
- 'documents': '~/Documents',
462
- 'downloads': '~/Downloads',
463
- 'music': '~/Music',
464
- 'pictures': '~/Pictures',
465
- 'home': '~/Pictures',
466
- 'videos': '~/Movies'
467
- },
468
- win : {},
469
- linux : {},
470
- };
471
187
 
472
- const osToHome = {
473
- darwin : '/Users/${user}',
474
- win : 'C:/',
475
- linux : '/Users/${user}',
476
- };
477
-
478
- /*const handlePath = (path, os, username)=>{
479
- return path.replace('~', osToHome[os].replace('${user}', username));
480
- };*/
481
-
482
- const handleCanonicalPath = (name, os, username)=>{
483
- const path = canonicalLocationToPath[os][name];
484
- return path.replace('~', osToHome[os].replace('${user}', username));
485
- };
486
-
487
- File.directory = {};
488
- Object.defineProperty(File.directory, 'current', {
489
- get() {
490
- if(isBrowser || isJsDom){
491
- const base = document.getElementsByTagName('base')[0];
492
- let basedir = null;
493
- if(base && (basedir = base.getAttribute('href'))){
494
- return basedir;
495
- }else{
496
- let path = window.location.pathname;
497
- path = path.split('/');
498
- path.pop(); // drop the top one
499
- return path .join('/');
500
- }
501
- }else{
502
- return process.cwd();
503
- }
504
- },
505
- set(newValue) {
506
- //do nothing
507
- },
508
- enumerable: true,
509
- configurable: true,
510
- });
511
-
512
- Object.defineProperty(File, 'agent', {
513
- get() {
514
- if(isBrowser || isJsDom){
515
- //var nVer = navigator.appVersion;
516
- var nAgt = navigator.userAgent;
517
- var browserName = navigator.appName;
518
- var fullVersion = ''+parseFloat(navigator.appVersion);
519
- var majorVersion = parseInt(navigator.appVersion,10);
520
- var nameOffset,verOffset,ix;
521
-
522
- // In Opera, the true version is after "Opera" or after "Version"
523
- if ((verOffset=nAgt.indexOf('Opera'))!=-1) {
524
- browserName = 'Opera';
525
- fullVersion = nAgt.substring(verOffset+6);
526
- if ((verOffset=nAgt.indexOf('Version'))!=-1)
527
- fullVersion = nAgt.substring(verOffset+8);
528
- }
529
- // In MSIE, the true version is after 'MSIE' in userAgent
530
- else if ((verOffset=nAgt.indexOf('MSIE'))!=-1) {
531
- browserName = 'Microsoft Internet Explorer';
532
- fullVersion = nAgt.substring(verOffset+5);
533
- }
534
- // In Chrome, the true version is after 'Chrome'
535
- else if ((verOffset=nAgt.indexOf('Chrome'))!=-1) {
536
- browserName = 'Chrome';
537
- fullVersion = nAgt.substring(verOffset+7);
538
- }
539
- // In Safari, the true version is after 'Safari' or after 'Version'
540
- else if ((verOffset=nAgt.indexOf('Safari'))!=-1) {
541
- browserName = 'Safari';
542
- fullVersion = nAgt.substring(verOffset+7);
543
- if ((verOffset=nAgt.indexOf('Version'))!=-1)
544
- fullVersion = nAgt.substring(verOffset+8);
545
- }
546
- // In Firefox, the true version is after 'Firefox'
547
- else if ((verOffset=nAgt.indexOf('Firefox'))!=-1) {
548
- browserName = 'Firefox';
549
- fullVersion = nAgt.substring(verOffset+8);
550
- }
551
- // In most other browsers, 'name/version' is at the end of userAgent
552
- else if ( (nameOffset=nAgt.lastIndexOf(' ')+1) < (verOffset=nAgt.lastIndexOf('/')) ) {
553
- browserName = nAgt.substring(nameOffset,verOffset);
554
- fullVersion = nAgt.substring(verOffset+1);
555
- if (browserName.toLowerCase()==browserName.toUpperCase()) {
556
- browserName = navigator.appName;
557
- }
558
- }
559
- // trim the fullVersion string at semicolon/space if present
560
- if ((ix=fullVersion.indexOf(';'))!=-1)
561
- fullVersion=fullVersion.substring(0,ix);
562
- if ((ix=fullVersion.indexOf(' '))!=-1)
563
- fullVersion=fullVersion.substring(0,ix);
564
-
565
- majorVersion = parseInt(''+fullVersion,10);
566
- if (isNaN(majorVersion)) {
567
- fullVersion = ''+parseFloat(navigator.appVersion);
568
- majorVersion = parseInt(navigator.appVersion,10);
569
- }
570
- return { name: browserName.toLowerCase(), version: fullVersion, major: majorVersion };
571
- }else{
572
- return {};
188
+ let staticInstance = null;
189
+ export class Download{
190
+ constructor(){
191
+ if(staticInstance) return staticInstance;
192
+ this.promise = null;
193
+ staticInstance = this;
194
+ }
195
+ expect(){
196
+ this.promise = new Promise((resolve)=>{
197
+ this.resolve = resolve;
198
+ });
199
+ //if(isServer) this.flushPromise();
200
+ return this.promise;
201
+ }
202
+ flushPromise(result){
203
+ if(this.resolve){
204
+ this.resolve(result);
205
+ this.resolve = null;
573
206
  }
574
- },
575
- set(newValue) {
576
- //do nothing
577
- },
578
- enumerable: true,
579
- configurable: true,
580
- });
581
-
582
- const directoryGet = (type)=>{
583
- if(isBrowser || isJsDom){
584
- return handleCanonicalPath('home', File.os, File.user);
585
- }else{
586
- return process.cwd();
207
+ this.promise = null;
587
208
  }
588
- };
209
+ async observe(download){
210
+ const result = await download;
211
+ this.flushPromise(result);
212
+ }
213
+ }
589
214
 
590
- Object.keys(canonicalLocationToPath['darwin']).forEach((key)=>{
591
- // register all available keys
592
- Object.defineProperty(File.directory, key, {
593
- enumerable: true, configurable: true,
594
- get() {
595
- return directoryGet(key);
596
- },
597
- set(newValue){ }
598
- });
599
- });
215
+ (()=>{
216
+ globalThis.handleDownload = async (download)=>{
217
+ const root = Path.location('downloads');
218
+ const file = download.suggestedFilename();
219
+ const path = Path.join(root, file);
220
+ await download.saveAs(path);
221
+ };
222
+ if(variables.moka && variables.moka.bind){
223
+ //bind the input mechanics of File to moka
224
+ variables.moka.bind(bindInput());
225
+ }
226
+ })();