@ibm-aspera/sdk 0.2.6 → 0.2.7

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 (46) hide show
  1. package/.eslintrc.js +3 -0
  2. package/.github/workflows/documentation.yml +1 -1
  3. package/CHANGELOG.md +13 -0
  4. package/README.md +3 -0
  5. package/dist/commonjs/helpers/client/safari-client.d.ts +4 -2
  6. package/dist/commonjs/helpers/client/safari-client.js +22 -10
  7. package/dist/commonjs/index.d.ts +2 -2
  8. package/dist/commonjs/index.js +2 -1
  9. package/dist/commonjs/models/aspera-sdk.model.d.ts +16 -1
  10. package/dist/commonjs/models/aspera-sdk.model.js +32 -2
  11. package/dist/js/aspera-sdk.js +1 -1
  12. package/dist/js/aspera-sdk.js.LICENSE.txt +2 -2
  13. package/dist/js/aspera-sdk.js.map +1 -1
  14. package/example/README.md +7 -0
  15. package/example/index.html +14 -0
  16. package/example/package-lock.json +2847 -0
  17. package/example/package.json +29 -0
  18. package/example/public/404.html +5 -0
  19. package/example/public/sdk-code.js +334 -0
  20. package/example/src/App/App.scss +40 -0
  21. package/example/src/App/index.tsx +174 -0
  22. package/example/src/Views/AllTogether.tsx +26 -0
  23. package/example/src/Views/DragDrop.tsx +23 -0
  24. package/example/src/Views/Home.tsx +10 -0
  25. package/example/src/Views/Initialize.tsx +21 -0
  26. package/example/src/Views/Installer.tsx +119 -0
  27. package/example/src/Views/MonitorTransfers.tsx +88 -0
  28. package/example/src/Views/Other.tsx +25 -0
  29. package/example/src/Views/SelectItems.tsx +46 -0
  30. package/example/src/Views/StartTransfer.tsx +32 -0
  31. package/example/src/Views/Test.tsx +20 -0
  32. package/example/src/Views/Views.scss +111 -0
  33. package/example/src/helpers/index.ts +19 -0
  34. package/example/src/index.scss +47 -0
  35. package/example/src/main.tsx +17 -0
  36. package/example/src/vite-env.d.ts +2 -0
  37. package/example/tsconfig.json +30 -0
  38. package/example/vite.config.ts +22 -0
  39. package/package.json +4 -3
  40. package/src/helpers/client/safari-client.ts +26 -10
  41. package/src/index.ts +1 -0
  42. package/src/models/aspera-sdk.model.ts +37 -2
  43. package/tsconfig.json +4 -1
  44. package/tsconfig.module.json +1 -0
  45. package/webpack.config.js +1 -19
  46. package/src/index.html +0 -404
@@ -48,11 +48,9 @@ let keepAliveTimeout: ReturnType<typeof setTimeout>;
48
48
  * Handles communication with the Safari extension using JSON-RPC over custom events.
49
49
  */
50
50
  export class SafariClient implements Client {
51
- private statusInterval = 100;
52
51
  private keepAliveInterval = 1000;
53
52
  private promiseExecutors: Map<string, PromiseExecutor>;
54
53
 
55
- private isFirstPing = true;
56
54
  private lastPing: number|null = null;
57
55
  private lastPong: number|null = null;
58
56
  private safariExtensionEnabled = false;
@@ -67,6 +65,7 @@ export class SafariClient implements Client {
67
65
  this.listenResponseEvents();
68
66
  this.listenTransferActivityEvents();
69
67
  this.listenStatusEvents();
68
+ this.listenClientStatusEvents();
70
69
  this.listenPongEvents();
71
70
 
72
71
  if (keepAliveTimeout) {
@@ -207,6 +206,10 @@ export class SafariClient implements Client {
207
206
  */
208
207
  private listenResponseEvents() {
209
208
  document.addEventListener('AsperaDesktop.Response', (event: CustomEvent<JSONRPCResponse>) => {
209
+ if (!event.detail) {
210
+ return;
211
+ }
212
+
210
213
  this.handleResponse(event.detail);
211
214
  });
212
215
  }
@@ -216,6 +219,10 @@ export class SafariClient implements Client {
216
219
  */
217
220
  private listenTransferActivityEvents() {
218
221
  document.addEventListener('AsperaDesktop.TransferActivity', (event: any) => {
222
+ if (!event.detail) {
223
+ return;
224
+ }
225
+
219
226
  asperaSdk.activityTracking.handleTransferActivity(event.detail);
220
227
  });
221
228
  }
@@ -225,10 +232,27 @@ export class SafariClient implements Client {
225
232
  */
226
233
  private listenStatusEvents() {
227
234
  document.addEventListener('AsperaDesktop.Status', (event: any) => {
235
+ if (!event.detail) {
236
+ return;
237
+ }
238
+
228
239
  asperaSdk.activityTracking.handleWebSocketEvents(event.detail);
229
240
  });
230
241
  }
231
242
 
243
+ /**
244
+ * Listens for 'isAppAlive' events. This was introduced in version 1.0.9.
245
+ */
246
+ private listenClientStatusEvents() {
247
+ document.addEventListener('isAppAlive', (event: any) => {
248
+ if (!event.detail?.running) {
249
+ return;
250
+ }
251
+
252
+ asperaSdk.activityTracking.handleClientEvents(event.detail.running);
253
+ });
254
+ }
255
+
232
256
  /**
233
257
  * Listens for 'AsperaDesktop.Pong' events.
234
258
  */
@@ -246,14 +270,6 @@ export class SafariClient implements Client {
246
270
  this.lastPing = Date.now();
247
271
  this.dispatchEvent(SafariExtensionEventType.Ping);
248
272
 
249
- if (this.isFirstPing) {
250
- this.isFirstPing = false;
251
- } else {
252
- setTimeout(() => {
253
- this.checkSafariExtensionStatus();
254
- }, this.statusInterval);
255
- }
256
-
257
273
  keepAliveTimeout = setTimeout(() => {
258
274
  this.keepAlive();
259
275
  }, this.keepAliveInterval);
package/src/index.ts CHANGED
@@ -64,6 +64,7 @@ export {
64
64
  modifyTransfer,
65
65
  createDropzone,
66
66
  removeDropzone,
67
+ initDragDrop,
67
68
  getInstallerInfo,
68
69
  registerStatusCallback,
69
70
  deregisterStatusCallback,
@@ -82,6 +82,8 @@ export class ActivityTracking {
82
82
 
83
83
  /** Keep track of the last WebSocket event **/
84
84
  private lastWebSocketEvent: WebsocketEvent = 'CLOSED';
85
+ /** Keep track of the last notified WebSocket event **/
86
+ private lastNotifiedWebSocketEvent: WebsocketEvent = undefined;
85
87
  /** Keep track of the last Safari extension event **/
86
88
  private lastSafariExtensionEvent: SafariExtensionEvent = 'DISABLED';
87
89
 
@@ -109,7 +111,7 @@ export class ActivityTracking {
109
111
  }
110
112
 
111
113
  /**
112
- * Notify all consumers when a connection webSocketEvent occurs. For example, when the SDK
114
+ * Handle and notify if needed when a connection webSocketEvent occurs. For example, when the SDK
113
115
  * websocket connection to IBM Aspera App is closed or reconnected.
114
116
  *
115
117
  * @param webSocketEvent the event type.
@@ -119,13 +121,46 @@ export class ActivityTracking {
119
121
  return;
120
122
  }
121
123
 
124
+ this.lastWebSocketEvent = webSocketEvent;
125
+
126
+ this.notifyWebSocketEvent(webSocketEvent);
127
+ }
128
+
129
+ /**
130
+ * Notify all consumers when a connection webSocketEvent occurs.
131
+ *
132
+ * @param webSocketEvent the event type.
133
+ */
134
+ private notifyWebSocketEvent(webSocketEvent: WebsocketEvent): void {
135
+ if (this.lastNotifiedWebSocketEvent === webSocketEvent) {
136
+ return;
137
+ }
138
+
139
+ this.lastNotifiedWebSocketEvent = webSocketEvent;
140
+
122
141
  this.event_callbacks.forEach(callback => {
123
142
  if (typeof callback === 'function') {
124
143
  callback(webSocketEvent);
125
144
  }
126
145
  });
146
+ }
127
147
 
128
- this.lastWebSocketEvent = webSocketEvent;
148
+ /**
149
+ * Notify all consumers when the client changes status. For example, when
150
+ * IBM Aspera App is launched or closed.
151
+ *
152
+ * @param running whether the client is running or not.
153
+ */
154
+ handleClientEvents(running: boolean): void {
155
+ let webSocketEvent: WebsocketEvent;
156
+
157
+ if (!running) {
158
+ webSocketEvent = 'CLOSED';
159
+ } else {
160
+ webSocketEvent = this.lastWebSocketEvent;
161
+ }
162
+
163
+ this.notifyWebSocketEvent(webSocketEvent);
129
164
  }
130
165
 
131
166
  /**
package/tsconfig.json CHANGED
@@ -6,5 +6,8 @@
6
6
  "target": "es5",
7
7
  "esModuleInterop": true,
8
8
  "moduleResolution": "node",
9
- }
9
+ },
10
+ "exclude": [
11
+ "./example"
12
+ ]
10
13
  }
@@ -9,6 +9,7 @@
9
9
  },
10
10
  "exclude": [
11
11
  "node_modules",
12
+ "example",
12
13
  "dist",
13
14
  "tests"
14
15
  ]
package/webpack.config.js CHANGED
@@ -8,7 +8,7 @@ if (packageFile.version) {
8
8
  version = `v${packageFile.version}`;
9
9
  }
10
10
 
11
- const banner = `IBM Aspera SDK ${version}\nLicensed Materials – Property of IBM\n© Copyright IBM Corp. 2024\nU.S. Government Users Restricted Rights: Use, duplication or disclosure restricted by\nGSA ADP Schedule Contract with IBM Corp.`;
11
+ const banner = `IBM Aspera SDK ${version}\nLicensed Materials – Property of IBM\n© Copyright IBM Corp. 2024, 2025\nU.S. Government Users Restricted Rights: Use, duplication or disclosure restricted by\nGSA ADP Schedule Contract with IBM Corp.`;
12
12
 
13
13
  module.exports = {
14
14
  entry: './src/index.ts',
@@ -29,24 +29,6 @@ module.exports = {
29
29
  }
30
30
  ]
31
31
  },
32
- devServer: {
33
- server: {
34
- type: 'https',
35
- },
36
- open: true,
37
- static: [
38
- {
39
- publicPath: '/',
40
- directory: './src',
41
- },
42
- {
43
- publicPath: '/aspera-sdk-js/docs',
44
- directory: './dist/js/docs',
45
- },
46
- ],
47
- host: 'js-sdk.aspera.us',
48
- port: 4205
49
- },
50
32
  plugins: [
51
33
  new webpack.BannerPlugin(banner)
52
34
  ]
package/src/index.html DELETED
@@ -1,404 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <script src="aspera-sdk.js"></script>
5
- <title>JavaScript SDK Test Server</title>
6
- <style>
7
- body {
8
- padding: 30px;
9
- }
10
- .test-buttons {
11
- margin: 30px 0;
12
- }
13
- .test-buttons button {
14
- margin: 0 10px;
15
- padding: 10px;
16
- }
17
- .flex-page {
18
- display: flex;
19
- }
20
- .text-area, .transfer-monitor {
21
- flex: 1;
22
- padding-right: 20px;
23
- }
24
- .text-area * {
25
- display: block;
26
- }
27
- .text-area textarea {
28
- width: 600px;
29
- padding: 3px;
30
- font-size: 16px;
31
- font-family: monospace;
32
- height: 300px;
33
- }
34
- .text-area input {
35
- font-size: 16px;
36
- font-family: monospace;
37
- padding: 3px;
38
- width: 600px;
39
- }
40
- label {
41
- font-weight: bold;
42
- margin-top: 25px;
43
- }
44
- .transfer {
45
- display: block;
46
- margin-bottom: 20px;
47
- position: relative;
48
- }
49
- .transfer .progress {
50
- background-color: grey;
51
- width: 100%;
52
- height: 30px;
53
- }
54
-
55
- .transfer .bar {
56
- background-color: green;
57
- height: 100%;
58
- }
59
-
60
- .transfer .status {
61
- position: absolute;
62
- top: 5px;
63
- left: 10px;
64
- color: white;
65
- font-weight: bold;
66
- }
67
-
68
- .drop-area {
69
- height: 80px;
70
- background-color: rgba(0,0,255,0.3);
71
- text-align: center;
72
- padding-top: 40px;
73
- font-size: 18px;
74
- margin-bottom: 20px;
75
- font-weight: bold;
76
- }
77
-
78
- .loader {
79
- border: 16px solid #f3f3f3;
80
- border-top: 16px solid #3498db;
81
- border-radius: 50%;
82
- width: 120px;
83
- height: 120px;
84
- animation: spin 2s linear infinite;
85
- margin: 0 auto;
86
- }
87
-
88
- @keyframes spin {
89
- 0% { transform: rotate(0deg); }
90
- 100% { transform: rotate(360deg); }
91
- }
92
-
93
- .error {
94
- color: red;
95
- margin: 15px 0;
96
- text-align: center;
97
- font-size: 32px;
98
- }
99
-
100
- .docs {
101
- color: #343a40;
102
- text-decoration: none;
103
- font-family: Arial, sans-serif;
104
- font-size: 16px;
105
- border-bottom: 1px dashed #343a40;
106
- transition: color 0.3s ease, border-bottom-color 0.3s ease;
107
- }
108
-
109
- .doc-link:hover {
110
- color: #1d2124;
111
- border-bottom-color: #1d2124;
112
- }
113
- </style>
114
- <script>
115
- function bytesCalc(value) {
116
- if (value === null || value === '' || value === undefined) {
117
- return '\u2014';
118
- }
119
- const base = 1024;
120
- const units = [
121
- 'B',
122
- 'KB',
123
- 'MB',
124
- 'GB',
125
- 'TB'
126
- ];
127
-
128
- let unitIndex = 0;
129
-
130
- if (Math.abs(value) > base) {
131
- do {
132
- value /= base;
133
- ++unitIndex;
134
- } while ( Math.abs(value) >= base && unitIndex < units.length - 1 );
135
- }
136
- value = value.toFixed(2);
137
- return `${value} ${units[unitIndex]}`;
138
- }
139
-
140
- function generateObjectText() {
141
- const text = JSON.stringify(asperaSdk, undefined, 2);
142
- document.querySelector('#test-output').value = text;
143
- setTimeout(() => {
144
- generateObjectText();
145
- }, 1000);
146
- }
147
-
148
- function init(supportMultipleUsers) {
149
- console.log('INITIALIZING...');
150
-
151
- const appId = document.querySelector('#app-id').value;
152
- const options = {appId, supportMultipleUsers};
153
-
154
- asperaSdk.init(options).then(() => {
155
- console.log('INIT SUCCESS');
156
-
157
- asperaSdk.getAllTransfers().then(response => {
158
- monitorTransfers({ transfers: response });
159
- }).catch(error => {
160
- console.log('GET ALL TRANSFERS FAIL', error);
161
- });
162
- }).catch(error => {
163
- console.log('INIT FAIL', error);
164
- })
165
- }
166
-
167
- function testServer() {
168
- asperaSdk.testConnection().then(response => {
169
- console.log('TEST SUCCESS', response);
170
- }).catch(error => {
171
- console.log('TEST FAIL', error);
172
- })
173
- }
174
-
175
- function getInfo() {
176
- asperaSdk.getInfo().then(response => {
177
- console.log('GET INFO SUCCESS', response);
178
- }).catch(error => {
179
- console.log('GET INFO FAIL', error);
180
- })
181
- }
182
-
183
- function transfer() {
184
- const asperaSpec = {
185
- use_absolute_destination_path: false
186
- };
187
-
188
- try {
189
- const transferSpec = JSON.parse(document.querySelector('#test-input').value);
190
- asperaSdk.startTransfer(transferSpec, asperaSpec).then(response => {
191
- console.log('TRANSFER SUCCESS', response);
192
- }).catch(error => {
193
- console.log('TRANSFER FAIL', error);
194
- })
195
- } catch (error) {
196
- console.log('Unable to parse transferSpec', error);
197
- }
198
- }
199
-
200
- function remove(id) {
201
- asperaSdk.removeTransfer(id).then(response => {
202
- console.log('REMOVE SUCCESS', response);
203
- }).catch(error => {
204
- console.log('REMOVE FAIL', error);
205
- })
206
- }
207
-
208
- function stopTransfer(id) {
209
- asperaSdk.stopTransfer(id).then(response => {
210
- console.log('STOP SUCCESS', response);
211
- }).catch(error => {
212
- console.log('STOP FAIL', error);
213
- })
214
- }
215
-
216
- function resumeTransfer(id) {
217
- try {
218
- const transferSpec = JSON.parse(document.querySelector('#test-input').value);
219
- asperaSdk.resumeTransfer(id, transferSpec).then(response => {
220
- console.log('RESUME SUCCESS', response);
221
- }).catch(error => {
222
- console.log('RESUME FAIL', error);
223
- })
224
- } catch (error) {
225
- console.log('Unable to parse transferSpec for resume', error);
226
- }
227
- }
228
-
229
- function getAllTransfers() {
230
- asperaSdk.getAllTransfers().then(response => {
231
- console.log('GET ALL TRANSFERS SUCCESS', response);
232
- }).catch(error => {
233
- console.log('GET ALL TRANSFERS FAIL', error);
234
- });
235
- }
236
-
237
- function getTransfer(id) {
238
- asperaSdk.getTransfer(id).then(response => {
239
- console.log('GET TRANSFER SUCCESS', response);
240
- }).catch(error => {
241
- console.log('GET TRANSFER FAIL', error);
242
- });
243
- }
244
-
245
- function showDirectory(id) {
246
- asperaSdk.showDirectory(id).then(response => {
247
- console.log('SHOW DIRECTORY SUCCESS', response);
248
- }).catch(error => {
249
- console.log('SHOW DIRECTORY FAIL', error);
250
- });
251
- }
252
-
253
- function showSelectFileDialog() {
254
- asperaSdk.showSelectFileDialog().then(response => {
255
- console.log('SHOW FILE DIALOG SUCCESS', response);
256
- }).catch(error => {
257
- console.log('SHOW FILE DIALOG FAIL', error);
258
- });
259
- }
260
-
261
- function showSelectFolderDialog() {
262
- asperaSdk.showSelectFolderDialog().then(response => {
263
- console.log('SHOW FOLDER DIALOG SUCCESS', response);
264
- }).catch(error => {
265
- console.log('SHOW FOLDER DIALOG FAIL', error);
266
- });
267
- }
268
-
269
- function openPreferences() {
270
- asperaSdk.showPreferences().then(response => {
271
- console.log('SHOW PREFERENCES SUCCESS', response);
272
- }).catch(error => {
273
- console.log('SHOW PREFERENCES FAIL', error);
274
- });
275
- }
276
-
277
- function modifyTransfer(id) {
278
- const options = {
279
- target_rate_kbps: 10,
280
- };
281
-
282
- asperaSdk.modifyTransfer(id, options).then(response => {
283
- console.log('MODIFY TRANSFER SUCCESS', response);
284
- }).catch(error => {
285
- console.log('MODIFY TRANSFER FAIL', error);
286
- });
287
- }
288
-
289
- function testDrop() {
290
- const callback = files => {
291
- console.log('DROP ZONE CALLBACK', files);
292
- }
293
-
294
- asperaSdk.createDropzone(callback, '#drop-area');
295
- }
296
-
297
- function getInstallerInfo() {
298
- asperaSdk.getInstallerInfo().then(response => {
299
- console.log('INSTALLER INFO SUCCESS', response);
300
- }).catch(error => {
301
- console.log('INSTALLER INFO FAIL', error);
302
- })
303
- }
304
-
305
- function monitorRemovedTransfers(transfer) {
306
- console.log('TRANSFER WAS REMOVED', transfer);
307
- }
308
-
309
- function monitorTransfers(transfers) {
310
- const transferDom = document.querySelector('#transfer');
311
- while (transferDom.firstChild) {
312
- transferDom.removeChild(transferDom.firstChild);
313
- }
314
- if (!transfers.transfers.length) {
315
- const element = document.createElement('div');
316
- element.setAttribute('class', 'transfer');
317
- element.innerHTML = `
318
- <div class="name">No transfers</div>
319
- `;
320
- transferDom.appendChild(element);
321
- return;
322
- }
323
- transfers.transfers.forEach(transfer => {
324
- const element = document.createElement('div');
325
- element.setAttribute('class', 'transfer-item');
326
- element.innerHTML = `
327
- <div class="progress">
328
- <div class="bar" style="width: ${(transfer.percentage * 100).toFixed(2)}%;"></div>
329
- </div>
330
- <div class="status">${transfer.status} - ${bytesCalc(transfer.bytes_written)} / ${bytesCalc(transfer.bytes_expected)}</div>
331
- <button onclick=remove("${transfer.uuid}")>Remove</button>
332
- <button onclick=stopTransfer("${transfer.uuid}")>Stop</button>
333
- <button onclick=resumeTransfer("${transfer.uuid}")>Resume</button>
334
- <button onclick=getTransfer("${transfer.uuid}")>Info</button>
335
- <button onclick=showDirectory("${transfer.uuid}")>Show</button>
336
- <button onclick=modifyTransfer("${transfer.uuid}")>Modify</button>
337
- `;
338
- transferDom.appendChild(element);
339
- });
340
- }
341
-
342
- function launch() {
343
- asperaSdk.launch();
344
- }
345
-
346
- function initDragDrop() {
347
- asperaSdk.initDragDrop();
348
- }
349
-
350
- setTimeout(() => {
351
- generateObjectText();
352
- }, 3000);
353
-
354
- asperaSdk.registerActivityCallback(monitorTransfers);
355
- asperaSdk.registerRemovedCallback(monitorRemovedTransfers);
356
-
357
- asperaSdk.registerStatusCallback((status) => {
358
- console.log('Websocket status changed to:', status);
359
- });
360
-
361
- if (asperaSdk.isSafari()) {
362
- asperaSdk.registerSafariExtensionStatusCallback((status) => {
363
- console.log('Safari extension status changed to:', status);
364
- });
365
- }
366
- </script>
367
- </head>
368
- <body>
369
- <h1>Aspera SDK Test Server</h1>
370
- <a href="/aspera-sdk-js/docs/index.html" class="docs" target="_blank">Documentation</a>
371
- <div class="test-buttons">
372
- <button onclick="init(false)" type="button">Init SDK</button>
373
- <button onclick="init(true)" type="button">Init SDK with session</button>
374
- <button onclick="testServer()" type="button">Test desktop</button>
375
- <button onclick="getInfo()" type="button">Get Info</button>
376
- <button onclick="transfer()" type="button">Start transfer</button>
377
- <button onclick="getAllTransfers()" type="button">Get transfers</button>
378
- <button onclick="showSelectFileDialog()" type="button">Select File</button>
379
- <button onclick="showSelectFolderDialog()" type="button">Select Folder</button>
380
- <button onclick="openPreferences()" type="button">Open Preferences</button>
381
- <button onclick="launch()" type="button">Launch</button>
382
- <button onclick="testDrop()" type="button">Set dropzone</button>
383
- <button onclick="getInstallerInfo()" type="button">Installers</button>
384
- <button onclick="initDragDrop()" type="button">Init drag-drop</button>
385
- </div>
386
- <div class="flex-page">
387
- <div class="text-area">
388
- <label for="app-id">App ID</label>
389
- <input type="text" id="app-id" value="test1"></input>
390
- <label for="test-input">transferSpec for download/upload</label>
391
- <textarea id="test-input"></textarea>
392
- <label for="test-output">Current values in asperaSdk</label>
393
- <textarea id="test-output" readonly></textarea>
394
- </div>
395
- <div id="transfer-monitor" class="transfer-monitor">
396
- <br />
397
- <hl />
398
- <p>Drop test</p>
399
- <div class="drop-area" id="drop-area">Drop area</div>
400
- <div id="transfer" class="transfer"><div class="name">No transfers</div></div>
401
- </div>
402
- </div>
403
- </body>
404
- </html>