@karpeleslab/klbfw 0.2.4 → 0.2.5

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 (2) hide show
  1. package/package.json +1 -1
  2. package/upload.js +141 -126
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@karpeleslab/klbfw",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "description": "Frontend Framework",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
package/upload.js CHANGED
@@ -35,17 +35,20 @@
35
35
  * // For Node.js environments, first install dependencies:
36
36
  * // npm install node-fetch xmldom
37
37
  *
38
- * // Initialize upload with specific file paths
39
- * upload.upload.init('Misc/Debug:testUpload')(['./file1.txt', './file2.jpg'])
40
- * .then(result => console.log('Upload complete', result));
41
- *
42
- * // Or create a custom file object with path
38
+ * // Create a buffer-based file object for upload
43
39
  * const file = {
44
40
  * name: 'test.txt',
45
- * size: 1024,
41
+ * size: buffer.length,
46
42
  * type: 'text/plain',
47
- * path: '/path/to/file.txt'
43
+ * content: buffer, // Buffer or ArrayBuffer with file content
44
+ * lastModified: Date.now(),
45
+ * slice: function(start, end) {
46
+ * return {
47
+ * content: this.content.slice(start, end)
48
+ * };
49
+ * }
48
50
  * };
51
+ *
49
52
  * upload.upload.append('Misc/Debug:testUpload', file)
50
53
  * .then(result => console.log('Upload complete', result));
51
54
  * ```
@@ -53,6 +56,8 @@
53
56
  * @module upload
54
57
  */
55
58
 
59
+ 'use strict';
60
+
56
61
  const rest = require('./rest');
57
62
  const fwWrapper = require('./fw-wrapper');
58
63
  const sha256 = require('js-sha256').sha256;
@@ -77,8 +82,6 @@ const env = {
77
82
  node: {
78
83
  fetch: null,
79
84
  xmlParser: null,
80
- fs: null,
81
- path: null,
82
85
  EventEmitter: null,
83
86
  eventEmitter: null
84
87
  }
@@ -91,8 +94,6 @@ if (env.isNode && !env.isBrowser) {
91
94
  try {
92
95
  env.node.fetch = require('node-fetch');
93
96
  env.node.xmlParser = require('xmldom');
94
- env.node.fs = require('fs');
95
- env.node.path = require('path');
96
97
  env.node.EventEmitter = require('events');
97
98
  env.node.eventEmitter = new (env.node.EventEmitter)();
98
99
  } catch (e) {
@@ -150,40 +151,73 @@ const utils = {
150
151
  },
151
152
 
152
153
  /**
153
- * Read a file as ArrayBuffer in any environment
154
- * @param {File|Object} file - File object or file-like object with path
154
+ * Read file content as ArrayBuffer
155
+ * Compatible with browser File objects and custom objects with content/slice
156
+ *
157
+ * @param {File|Object} file - File object or file-like object
158
+ * @param {Object} options - Options for reading (start, end)
155
159
  * @param {Function} callback - Callback function(buffer, error)
156
160
  */
157
- readFileAsArrayBuffer(file, callback) {
158
- if (env.isBrowser) {
161
+ readAsArrayBuffer(file, options, callback) {
162
+ // Handle case where options is the callback
163
+ if (typeof options === 'function') {
164
+ callback = options;
165
+ options = {};
166
+ }
167
+ options = options || {};
168
+
169
+ if (env.isBrowser && file instanceof File) {
170
+ // Browser: use native File API
171
+ const start = options.start || 0;
172
+ const end = options.end || file.size;
173
+ const slice = file.slice(start, end);
174
+
159
175
  const reader = new FileReader();
160
176
  reader.addEventListener('loadend', () => callback(reader.result));
161
177
  reader.addEventListener('error', (e) => callback(null, e));
162
- reader.readAsArrayBuffer(file);
163
- } else if (env.isNode && env.node.fs) {
164
- if (file.path) {
165
- // Read from filesystem
166
- const readStream = env.node.fs.createReadStream(file.path, {
167
- start: file.start || 0,
168
- end: file.end || undefined
169
- });
170
-
171
- const chunks = [];
172
- readStream.on('data', chunk => chunks.push(chunk));
173
- readStream.on('end', () => {
174
- const buffer = Buffer.concat(chunks);
175
- callback(buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength));
176
- });
177
- readStream.on('error', err => callback(null, err));
178
- } else if (file.content) {
179
- // Memory buffer
180
- const buffer = Buffer.from(file.content);
181
- callback(buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength));
178
+ reader.readAsArrayBuffer(slice);
179
+ } else if (file.content) {
180
+ // Memory buffer-based file
181
+ const start = options.start || 0;
182
+ const end = options.end || file.content.length || file.content.byteLength;
183
+ let content = file.content;
184
+
185
+ // Handle various content types
186
+ if (content instanceof ArrayBuffer) {
187
+ // Already an ArrayBuffer
188
+ if (start === 0 && end === content.byteLength) {
189
+ callback(content);
190
+ } else {
191
+ callback(content.slice(start, end));
192
+ }
193
+ } else if (content.buffer instanceof ArrayBuffer) {
194
+ // TypedArray (Uint8Array, etc.)
195
+ callback(content.buffer.slice(start, end));
196
+ } else if (typeof Buffer !== 'undefined' && content instanceof Buffer) {
197
+ // Node.js Buffer
198
+ const arrayBuffer = content.buffer.slice(
199
+ content.byteOffset + start,
200
+ content.byteOffset + Math.min(end, content.byteLength)
201
+ );
202
+ callback(arrayBuffer);
203
+ } else if (typeof content === 'string') {
204
+ // String content - convert to ArrayBuffer
205
+ const encoder = new TextEncoder();
206
+ const uint8Array = encoder.encode(content.slice(start, end));
207
+ callback(uint8Array.buffer);
182
208
  } else {
183
- callback(null, new Error('No file path or content provided'));
209
+ callback(null, new Error('Unsupported content type'));
184
210
  }
211
+ } else if (file.slice) {
212
+ // Object with slice method (custom implementation)
213
+ const start = options.start || 0;
214
+ const end = options.end;
215
+ const slice = file.slice(start, end);
216
+
217
+ // Recursively handle the slice
218
+ utils.readAsArrayBuffer(slice, callback);
185
219
  } else {
186
- callback(null, new Error('File reading not available in this environment'));
220
+ callback(null, new Error('Cannot read file content - no supported method available'));
187
221
  }
188
222
  },
189
223
 
@@ -530,28 +564,11 @@ module.exports.upload = (function () {
530
564
  const startByte = partNumber * up.bsize;
531
565
  const endByte = Math.min(startByte + up.bsize, up.file.size);
532
566
 
533
- // Get file slice based on environment
534
- let filePart;
535
-
536
- if (env.isBrowser) {
537
- // Browser: use native File.slice
538
- filePart = up.file.slice(startByte, endByte);
539
- } else if (env.isNode) {
540
- // Node.js: create a reference with start/end positions
541
- filePart = {
542
- path: up.file.path,
543
- start: startByte,
544
- end: endByte,
545
- type: up.file.type,
546
- content: up.file.content // For memory buffer based files
547
- };
548
- } else {
549
- handleFailure(up, new Error('Environment not supported'));
550
- return;
551
- }
552
-
553
- // Read the file part as ArrayBuffer
554
- utils.readFileAsArrayBuffer(filePart, (arrayBuffer, error) => {
567
+ // Read file slice as ArrayBuffer
568
+ utils.readAsArrayBuffer(up.file, {
569
+ start: startByte,
570
+ end: endByte
571
+ }, (arrayBuffer, error) => {
555
572
  if (error) {
556
573
  handleFailure(up, error);
557
574
  return;
@@ -633,7 +650,6 @@ module.exports.upload = (function () {
633
650
  .catch(error => handleFailure(up, error));
634
651
  }
635
652
 
636
-
637
653
  /**
638
654
  * Process an upload in progress
639
655
  * Manages uploading parts and completing the upload
@@ -781,8 +797,6 @@ module.exports.upload = (function () {
781
797
  sendProgress();
782
798
  }
783
799
 
784
- // No need for backward compatibility for private methods
785
-
786
800
  /**
787
801
  * Get current upload status
788
802
  * @returns {Object} Status object with queued, running and failed uploads
@@ -809,44 +823,50 @@ module.exports.upload = (function () {
809
823
  upload.run();
810
824
  };
811
825
 
812
- // Environment-specific initialization
813
- upload.init = function (path, params, notify) {
814
- // perform upload to a given API, for example Drive/Item/<id>:upload
815
- // will allow multiple files to be uploaded
826
+ /**
827
+ * Initialize uploads in different environments
828
+ *
829
+ * @param {string} path - API path to upload to
830
+ * @param {Object} params - Upload parameters
831
+ * @param {Function} notify - Notification callback
832
+ * @returns {Function} - Function to start uploads
833
+ */
834
+ upload.init = function(path, params, notify) {
816
835
  params = params || {};
817
836
 
818
- if (isBrowser) {
837
+ if (env.isBrowser) {
819
838
  // Browser implementation
820
- if (last_input != null) {
821
- last_input.parentNode.removeChild(last_input);
822
- last_input = null;
839
+ if (state.lastInput !== null) {
840
+ state.lastInput.parentNode.removeChild(state.lastInput);
841
+ state.lastInput = null;
823
842
  }
824
843
 
825
- var input = document.createElement("input");
844
+ const input = document.createElement("input");
826
845
  input.type = "file";
827
846
  input.style.display = "none";
828
- if (!params["single"]) {
847
+ if (!params.single) {
829
848
  input.multiple = "multiple";
830
849
  }
831
850
 
832
851
  document.getElementsByTagName('body')[0].appendChild(input);
833
- last_input = input;
852
+ state.lastInput = input;
834
853
 
835
- var promise = new Promise(function (resolve, reject) {
836
- input.onchange = function () {
837
- if (this.files.length == 0) {
838
- resolve();
854
+ const promise = new Promise(function(resolve, reject) {
855
+ input.onchange = function() {
856
+ if (this.files.length === 0) {
857
+ return resolve();
839
858
  }
840
859
 
841
- var count = this.files.length;
842
- if (notify !== undefined) notify({status: 'init', count: count});
843
- for (var i = 0; i < this.files.length; i++) {
844
- upload.append(path, this.files[i], params, fwWrapper.getContext()).then(function (obj) {
845
- count -= 1;
846
- // Todo notify process
847
- if (notify !== undefined) notify(obj);
848
- if (count == 0) resolve();
849
- });
860
+ let count = this.files.length;
861
+ if (notify) notify({status: 'init', count: count});
862
+
863
+ for (let i = 0; i < this.files.length; i++) {
864
+ upload.append(path, this.files[i], params, fwWrapper.getContext())
865
+ .then(function(obj) {
866
+ count -= 1;
867
+ if (notify) notify(obj);
868
+ if (count === 0) resolve();
869
+ });
850
870
  }
851
871
  upload.run();
852
872
  };
@@ -854,55 +874,58 @@ module.exports.upload = (function () {
854
874
 
855
875
  input.click();
856
876
  return promise;
857
- } else if (isNode) {
858
- // Node.js implementation
859
- return function(filePaths) {
860
- // Convert string to array if single file path provided
861
- if (typeof filePaths === 'string') {
862
- filePaths = [filePaths];
863
- }
864
-
865
- if (!Array.isArray(filePaths)) {
866
- throw new Error('filePaths must be a string or array of strings');
877
+ } else {
878
+ // Non-browser environment
879
+ return function(files) {
880
+ // Allow array, single file object, or file content buffer
881
+ if (!Array.isArray(files)) {
882
+ if (files instanceof ArrayBuffer ||
883
+ (files.buffer instanceof ArrayBuffer) ||
884
+ (typeof Buffer !== 'undefined' && files instanceof Buffer)) {
885
+ // If it's a buffer/ArrayBuffer, create a file-like object
886
+ files = [{
887
+ name: params.filename || 'file.bin',
888
+ size: files.byteLength || files.length,
889
+ type: params.type || 'application/octet-stream',
890
+ lastModified: Date.now(),
891
+ content: files
892
+ }];
893
+ } else {
894
+ // Single file object
895
+ files = [files];
896
+ }
867
897
  }
868
898
 
869
899
  return new Promise(function(resolve, reject) {
870
- const count = filePaths.length;
900
+ const count = files.length;
871
901
  if (count === 0) {
872
902
  return resolve();
873
903
  }
874
904
 
875
- if (notify !== undefined) notify({status: 'init', count: count});
905
+ if (notify) notify({status: 'init', count: count});
876
906
 
877
907
  let remainingCount = count;
878
908
 
879
- filePaths.forEach(filePath => {
909
+ files.forEach(file => {
880
910
  try {
881
- // Get file info
882
- const stats = nodeFs.statSync(filePath);
883
- const fileName = nodePath.basename(filePath);
911
+ // Ensure file has required properties
912
+ if (!file.name) file.name = 'file.bin';
913
+ if (!file.type) file.type = 'application/octet-stream';
914
+ if (!file.lastModified) file.lastModified = Date.now();
884
915
 
885
- // Create a file-like object
886
- const file = {
887
- name: fileName,
888
- size: stats.size,
889
- lastModified: stats.mtimeMs,
890
- type: 'application/octet-stream', // Default type
891
- path: filePath, // For Node.js reading
892
- // Mock methods needed by upload.js
893
- slice: function(start, end) {
916
+ // Add slice method if not present
917
+ if (!file.slice && file.content) {
918
+ file.slice = function(start, end) {
894
919
  return {
895
- path: filePath,
896
- start: start,
897
- end: end || stats.size
920
+ content: this.content.slice(start, end || this.size)
898
921
  };
899
- }
900
- };
922
+ };
923
+ }
901
924
 
902
925
  upload.append(path, file, params, fwWrapper.getContext())
903
926
  .then(function(obj) {
904
927
  remainingCount -= 1;
905
- if (notify !== undefined) notify(obj);
928
+ if (notify) notify(obj);
906
929
  if (remainingCount === 0) resolve();
907
930
  })
908
931
  .catch(function(err) {
@@ -920,15 +943,9 @@ module.exports.upload = (function () {
920
943
  upload.run();
921
944
  });
922
945
  };
923
- } else {
924
- // Default implementation for other environments
925
- return function() {
926
- return Promise.reject(new Error('File upload not supported in this environment'));
927
- };
928
946
  }
929
947
  };
930
948
 
931
-
932
949
  /**
933
950
  * Add a file to the upload queue
934
951
  * @param {string} path - API path to upload to
@@ -961,7 +978,6 @@ module.exports.upload = (function () {
961
978
  });
962
979
  };
963
980
 
964
-
965
981
  /**
966
982
  * Cancel an upload in progress or in queue
967
983
  * @param {number} uploadId - Upload ID to cancel
@@ -1105,7 +1121,6 @@ module.exports.upload = (function () {
1105
1121
  sendProgress();
1106
1122
  };
1107
1123
 
1108
-
1109
1124
  /**
1110
1125
  * Start or continue the upload process
1111
1126
  * Processes queued uploads and continues running uploads
@@ -1131,4 +1146,4 @@ module.exports.upload = (function () {
1131
1146
  };
1132
1147
 
1133
1148
  return upload;
1134
- }());
1149
+ }());