@dronedeploy/rocos-js-sdk 3.0.0-alpha.6 → 3.0.1-alpha

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 (35) hide show
  1. package/helpers/getURLSearchParams.d.ts +2 -0
  2. package/helpers/getURLSearchParams.js +9 -0
  3. package/helpers/getURLSearchParams.spec.d.ts +1 -0
  4. package/helpers/getURLSearchParams.spec.js +19 -0
  5. package/helpers/index.d.ts +2 -0
  6. package/helpers/index.js +2 -0
  7. package/helpers/websandbox/connection.d.ts +72 -0
  8. package/helpers/websandbox/connection.js +140 -0
  9. package/helpers/websandbox/frame/frame.d.ts +12 -0
  10. package/helpers/websandbox/frame/frame.js +22 -0
  11. package/helpers/websandbox/frame/frame.source.d.ts +2 -0
  12. package/helpers/websandbox/frame/frame.source.js +4 -0
  13. package/helpers/websandbox/frame/index.d.ts +2 -0
  14. package/helpers/websandbox/frame/index.js +2 -0
  15. package/helpers/websandbox/frame/worker/index.d.ts +2 -0
  16. package/helpers/websandbox/frame/worker/index.js +2 -0
  17. package/helpers/websandbox/frame/worker/manager.d.ts +13 -0
  18. package/helpers/websandbox/frame/worker/manager.js +59 -0
  19. package/helpers/websandbox/frame/worker/types.d.ts +11 -0
  20. package/helpers/websandbox/frame/worker/types.js +1 -0
  21. package/helpers/websandbox/frame/worker/worker.d.ts +1 -0
  22. package/helpers/websandbox/frame/worker/worker.js +74 -0
  23. package/helpers/websandbox/frame/worker/worker.source.d.ts +2 -0
  24. package/helpers/websandbox/frame/worker/worker.source.js +4 -0
  25. package/helpers/websandbox/index.d.ts +2 -0
  26. package/helpers/websandbox/index.js +2 -0
  27. package/helpers/websandbox/sandbox.d.ts +48 -0
  28. package/helpers/websandbox/sandbox.js +116 -0
  29. package/helpers/websandbox/types.d.ts +13 -0
  30. package/helpers/websandbox/types.js +1 -0
  31. package/package.json +4 -2
  32. package/services/AssetStorageService.js +1 -1
  33. package/services/BaseServiceAbstract.d.ts +1 -1
  34. package/services/BaseServiceAbstract.js +2 -6
  35. package/services/TelemetryService.spec.js +2 -2
@@ -0,0 +1,2 @@
1
+ export type QueryParams = Record<string, string | number | boolean> | URLSearchParams;
2
+ export declare const getURLSearchParams: (params: QueryParams) => URLSearchParams;
@@ -0,0 +1,9 @@
1
+ export const getURLSearchParams = (params) => {
2
+ if (params instanceof URLSearchParams)
3
+ return params;
4
+ const stringParams = Object.entries(params ?? {}).reduce((acc, [key, value]) => {
5
+ acc[key] = value.toString();
6
+ return acc;
7
+ }, {});
8
+ return new URLSearchParams(stringParams);
9
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,19 @@
1
+ import { getURLSearchParams } from './getURLSearchParams';
2
+ describe('getURLSearchParams', () => {
3
+ it('Should return the original param if URLSearchParams', () => {
4
+ const param = new URLSearchParams();
5
+ const searchParam = getURLSearchParams(param);
6
+ expect(searchParam).toBe(param);
7
+ });
8
+ it('Should a new URLSearchParams ', () => {
9
+ const param = {
10
+ foo: 'bar',
11
+ bar: true,
12
+ foobar: 123,
13
+ };
14
+ const searchParam = getURLSearchParams(param);
15
+ expect(searchParam.get('foo')).toBe('bar');
16
+ expect(searchParam.get('bar')).toBe('true');
17
+ expect(searchParam.get('foobar')).toBe('123');
18
+ });
19
+ });
@@ -1,3 +1,5 @@
1
1
  export * from './average';
2
2
  export * from './standardDeviation';
3
3
  export * from './generateUUID';
4
+ export * from './getURLSearchParams';
5
+ export * from './websandbox';
package/helpers/index.js CHANGED
@@ -1,3 +1,5 @@
1
1
  export * from './average';
2
2
  export * from './standardDeviation';
3
3
  export * from './generateUUID';
4
+ export * from './getURLSearchParams';
5
+ export * from './websandbox';
@@ -0,0 +1,72 @@
1
+ import { APIDeclaration } from './types';
2
+ export interface ConnectionOptions {
3
+ allowedSenderOrigin?: string;
4
+ debugMode: boolean;
5
+ }
6
+ type ListenerCallback = (e: MessageEvent<Message>) => void;
7
+ declare enum MessageType {
8
+ RESPONSE = "response",
9
+ MESSAGE = "message"
10
+ }
11
+ interface BaseMessageData {
12
+ type: MessageType;
13
+ callId: string;
14
+ }
15
+ interface ResponseData extends BaseMessageData {
16
+ type: MessageType.RESPONSE;
17
+ success: boolean;
18
+ result: unknown;
19
+ }
20
+ interface MessageData extends BaseMessageData {
21
+ type: MessageType.MESSAGE;
22
+ methodName: string;
23
+ arguments: unknown[];
24
+ }
25
+ type Message = MessageData | ResponseData;
26
+ export default class Connection<Local extends APIDeclaration<Local>, Remote extends APIDeclaration<Remote>> {
27
+ private readonly name;
28
+ private incrementalID;
29
+ private readonly options;
30
+ private readonly postMessageInternal;
31
+ private callbacks;
32
+ private serviceMethods;
33
+ constructor(name: string, postMessage: typeof window.postMessage, methods: Local, registerOnMessageListener: (listener: ListenerCallback) => void, options?: Partial<ConnectionOptions>);
34
+ /** Update the local methods available to the remote
35
+ *
36
+ * @param methods - methods to expose to the remote
37
+ */
38
+ defineMethods(methods: Local): void;
39
+ /** Call a remote method
40
+ *
41
+ * returns a promise that resolves when the remote responds
42
+ *
43
+ * @param name - name of remote method to call
44
+ * @param args - arguments to pass to remote method
45
+ */
46
+ callRemoteMethod<Method extends keyof Remote>(name: Method, ...args: Parameters<Remote[Method]>): Promise<ReturnType<Remote[Method]>>;
47
+ private onMessageListener;
48
+ private callLocalMethod;
49
+ /** Respond to remote call
50
+ *
51
+ * @param id - remote call ID
52
+ * @param result - result to pass to calling function
53
+ * @param success - whether the call was successful
54
+ */
55
+ private responseOtherSide;
56
+ /** Store a callback to be called when the remote responds
57
+ *
58
+ * @param success - callback to be called on success
59
+ * @param failure - callback to be called on failure
60
+ */
61
+ private registerCallback;
62
+ /** Calls and deletes stored callback
63
+ *
64
+ * @param callId - ID of callback to call
65
+ * @param success - whether the call was successful
66
+ * @param result - result of remote call
67
+ */
68
+ private popCallback;
69
+ private postMessage;
70
+ private log;
71
+ }
72
+ export {};
@@ -0,0 +1,140 @@
1
+ var MessageType;
2
+ (function (MessageType) {
3
+ MessageType["RESPONSE"] = "response";
4
+ MessageType["MESSAGE"] = "message";
5
+ })(MessageType || (MessageType = {}));
6
+ const defaultOptions = {
7
+ allowedSenderOrigin: undefined,
8
+ debugMode: false,
9
+ };
10
+ export default class Connection {
11
+ constructor(name, postMessage, methods, registerOnMessageListener, options = {}) {
12
+ this.callbacks = new Map();
13
+ this.serviceMethods = new Map();
14
+ this.name = name;
15
+ this.options = { ...defaultOptions, ...options };
16
+ this.log('Created connection w/ allowedOrigin:', this.options.allowedSenderOrigin);
17
+ this.serviceMethods = new Map(Object.entries(methods));
18
+ // Assign a random starting ID to this connection
19
+ const [id] = crypto.getRandomValues(new Uint32Array(1));
20
+ this.incrementalID = id;
21
+ this.postMessageInternal = postMessage;
22
+ registerOnMessageListener((e) => this.onMessageListener(e));
23
+ }
24
+ /** Update the local methods available to the remote
25
+ *
26
+ * @param methods - methods to expose to the remote
27
+ */
28
+ defineMethods(methods) {
29
+ this.serviceMethods = new Map(Object.entries(methods));
30
+ }
31
+ /** Call a remote method
32
+ *
33
+ * returns a promise that resolves when the remote responds
34
+ *
35
+ * @param name - name of remote method to call
36
+ * @param args - arguments to pass to remote method
37
+ */
38
+ callRemoteMethod(name, ...args) {
39
+ this.log('Calling Remote Method', {
40
+ name,
41
+ args,
42
+ });
43
+ return new Promise((resolve, reject) => {
44
+ const id = this.registerCallback(resolve, reject);
45
+ this.postMessage({
46
+ callId: id,
47
+ type: MessageType.MESSAGE,
48
+ methodName: name,
49
+ arguments: args,
50
+ });
51
+ });
52
+ }
53
+ onMessageListener(e) {
54
+ this.log('Received message', e);
55
+ const { data } = e;
56
+ const { allowedSenderOrigin } = this.options;
57
+ if (allowedSenderOrigin && e.origin !== allowedSenderOrigin) {
58
+ console.warn(`Received message from invalid origin: ${e.origin}`);
59
+ }
60
+ switch (data.type) {
61
+ case MessageType.RESPONSE:
62
+ this.popCallback(data.callId, data.success, data.result);
63
+ return;
64
+ case MessageType.MESSAGE:
65
+ this.callLocalMethod(data.methodName, data.arguments)
66
+ .then((res) => this.responseOtherSide(data.callId, res))
67
+ .catch((err) => this.responseOtherSide(data.callId, err, false));
68
+ }
69
+ }
70
+ async callLocalMethod(methodName, args) {
71
+ this.log('calling local method', methodName, args);
72
+ const method = this.serviceMethods.get(methodName);
73
+ if (!method) {
74
+ throw new Error(`service method ${methodName} not found`);
75
+ }
76
+ return method(...args);
77
+ }
78
+ /** Respond to remote call
79
+ *
80
+ * @param id - remote call ID
81
+ * @param result - result to pass to calling function
82
+ * @param success - whether the call was successful
83
+ */
84
+ responseOtherSide(id, result, success = true) {
85
+ this.log('responding to remote call', { id, result, success });
86
+ const doPost = (result) => {
87
+ this.postMessage({
88
+ callId: id,
89
+ type: MessageType.RESPONSE,
90
+ success,
91
+ result,
92
+ });
93
+ };
94
+ try {
95
+ doPost(result);
96
+ }
97
+ catch (err) {
98
+ if (err instanceof DOMException) {
99
+ doPost(JSON.parse(JSON.stringify(result)));
100
+ }
101
+ }
102
+ }
103
+ /** Store a callback to be called when the remote responds
104
+ *
105
+ * @param success - callback to be called on success
106
+ * @param failure - callback to be called on failure
107
+ */
108
+ registerCallback(success, failure) {
109
+ const id = (++this.incrementalID).toString();
110
+ this.log('registering callback for id', id);
111
+ this.callbacks.set(id, { success, failure });
112
+ return id;
113
+ }
114
+ /** Calls and deletes stored callback
115
+ *
116
+ * @param callId - ID of callback to call
117
+ * @param success - whether the call was successful
118
+ * @param result - result of remote call
119
+ */
120
+ popCallback(callId, success, result) {
121
+ this.log('calling callback for id', callId, { success, result });
122
+ const callbacks = this.callbacks.get(callId);
123
+ if (success) {
124
+ callbacks?.success(result);
125
+ }
126
+ else {
127
+ callbacks?.failure(result);
128
+ }
129
+ this.callbacks.delete(callId);
130
+ }
131
+ postMessage(data, targetOrigin = '*') {
132
+ this.log('sending message', { data, targetOrigin });
133
+ this.postMessageInternal(data, targetOrigin);
134
+ }
135
+ log(...args) {
136
+ if (this.options.debugMode) {
137
+ console.debug(`[${this.name}]`, ...args);
138
+ }
139
+ }
140
+ }
@@ -0,0 +1,12 @@
1
+ import { Task } from '../types';
2
+ export interface FrameMethods {
3
+ startTask: (task: Task) => unknown;
4
+ }
5
+ declare class Frame {
6
+ private connection;
7
+ private workerManager;
8
+ constructor();
9
+ private runCode;
10
+ }
11
+ declare const frame: Frame;
12
+ export default frame;
@@ -0,0 +1,22 @@
1
+ // this file is bundled via webpack and included in the iframe source
2
+ import Connection from '../connection';
3
+ import WorkerManager from './worker';
4
+ class Frame {
5
+ constructor() {
6
+ const isDebugMode = !!window?.debugMode;
7
+ this.connection = new Connection('FRAME', window.parent.postMessage.bind(window.parent), {
8
+ startTask: this.runCode.bind(this),
9
+ }, (listener) => {
10
+ window.addEventListener('message', listener);
11
+ }, {
12
+ debugMode: isDebugMode,
13
+ });
14
+ void this.connection.callRemoteMethod('iframeInitialised');
15
+ this.workerManager = new WorkerManager();
16
+ }
17
+ async runCode(task) {
18
+ return this.workerManager.execute(task);
19
+ }
20
+ }
21
+ const frame = new Frame();
22
+ export default frame;
@@ -0,0 +1,2 @@
1
+ declare const source: string;
2
+ export default source;
@@ -0,0 +1,4 @@
1
+ // Auto-generated file
2
+ /* eslint-disable */
3
+ const source = "(()=>{\"use strict\";var e={880:(e,t)=>{var s;Object.defineProperty(t,\"__esModule\",{value:!0}),function(e){e.RESPONSE=\"response\",e.MESSAGE=\"message\"}(s||(s={}));const r={allowedSenderOrigin:void 0,debugMode:!1};t.default=class{constructor(e,t,s,o,n={}){this.callbacks=new Map,this.serviceMethods=new Map,this.name=e,this.options={...r,...n},this.log(\"Created connection w/ allowedOrigin:\",this.options.allowedSenderOrigin),this.serviceMethods=new Map(Object.entries(s));const[i]=crypto.getRandomValues(new Uint32Array(1));this.incrementalID=i,this.postMessageInternal=t,o((e=>this.onMessageListener(e)))}defineMethods(e){this.serviceMethods=new Map(Object.entries(e))}callRemoteMethod(e,...t){return this.log(\"Calling Remote Method\",{name:e,args:t}),new Promise(((r,o)=>{const n=this.registerCallback(r,o);this.postMessage({callId:n,type:s.MESSAGE,methodName:e,arguments:t})}))}onMessageListener(e){this.log(\"Received message\",e);const{data:t}=e,{allowedSenderOrigin:r}=this.options;switch(r&&e.origin!==r&&console.warn(`Received message from invalid origin: ${e.origin}`),t.type){case s.RESPONSE:return void this.popCallback(t.callId,t.success,t.result);case s.MESSAGE:this.callLocalMethod(t.methodName,t.arguments).then((e=>this.responseOtherSide(t.callId,e))).catch((e=>this.responseOtherSide(t.callId,e,!1)))}}async callLocalMethod(e,t){this.log(\"calling local method\",e,t);const s=this.serviceMethods.get(e);if(!s)throw new Error(`service method ${e} not found`);return s(...t)}responseOtherSide(e,t,r=!0){this.log(\"responding to remote call\",{id:e,result:t,success:r});const o=t=>{this.postMessage({callId:e,type:s.RESPONSE,success:r,result:t})};try{o(t)}catch(e){e instanceof DOMException&&o(JSON.parse(JSON.stringify(t)))}}registerCallback(e,t){const s=(++this.incrementalID).toString();return this.log(\"registering callback for id\",s),this.callbacks.set(s,{success:e,failure:t}),s}popCallback(e,t,s){this.log(\"calling callback for id\",e,{success:t,result:s});const r=this.callbacks.get(e);t?r?.success(s):r?.failure(s),this.callbacks.delete(e)}postMessage(e,t=\"*\"){this.log(\"sending message\",{data:e,targetOrigin:t}),this.postMessageInternal(e,t)}log(...e){this.options.debugMode&&console.debug(`[${this.name}]`,...e)}}},306:function(e,t,s){var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,\"__esModule\",{value:!0});const o=r(s(880)),n=r(s(54)),i=new class{constructor(){const e=!!window?.debugMode;this.connection=new o.default(\"FRAME\",window.parent.postMessage.bind(window.parent),{startTask:this.runCode.bind(this)},(e=>{window.addEventListener(\"message\",e)}),{debugMode:e}),this.connection.callRemoteMethod(\"iframeInitialised\"),this.workerManager=new n.default}async runCode(e){return this.workerManager.execute(e)}};t.default=i},54:function(e,t,s){var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,\"__esModule\",{value:!0});const o=r(s(420));t.default=o.default},420:function(e,t,s){var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,\"__esModule\",{value:!0});const o=r(s(967));t.default=class{constructor(){this.worker=this.createWorker()}async execute(e){const t={...e,contextVariable:e.contextVariable??\"ctx\"},s=this.runTask(t),r=this.timeout(e.timeout??1e3);return Promise.race([s,r]).finally((()=>{this.removeMessageListener()}))}terminate(){this.removeMessageListener(),this.worker.terminate(),this.worker=this.createWorker()}runTask(e){return new Promise(((t,s)=>{const r=r=>{const{data:o}=r;o.id===e.id&&(this.stopTimer(),o.success?t(o.result):s(o.result))};this.worker.addEventListener(\"message\",r),this.removeMessageListener=()=>this.worker.removeEventListener(\"message\",r),this.worker.postMessage(e)}))}timeout(e){return new Promise(((t,s)=>{this.timerId=window.setTimeout((()=>{this.terminate(),s(new Error(\"maximum execution time exceeded\"))}),e)}))}stopTimer(){clearTimeout(this.timerId)}createWorker(){const e=URL.createObjectURL(new Blob([o.default],{type:\"application/javascript\"}));return new Worker(e)}removeMessageListener(){}}},967:(e,t)=>{Object.defineProperty(t,\"__esModule\",{value:!0}),t.default='(()=>{\"use strict\";(()=>{const e=[\"TEMPORARY\",\"PERSISTENT\",\"console\",\"self\",\"onmessage\",\"postMessage\",\"global\",\"allowed\",\"Array\",\"Boolean\",\"Date\",\"Function\",\"Number\",\"Object\",\"RegExp\",\"String\",\"Error\",\"EvalError\",\"RangeError\",\"ReferenceError\",\"SyntaxError\",\"TypeError\",\"URIError\",\"decodeURI\",\"decodeURIComponent\",\"encodeURI\",\"encodeURIComponent\",\"isFinite\",\"isNaN\",\"parseFloat\",\"parseInt\",\"Infinity\",\"JSON\",\"Math\",\"NaN\",\"undefined\"];[...Object.getOwnPropertyNames(self),...Object.getOwnPropertyNames(self.__proto__)].forEach((r=>{e.includes(r)||Object.defineProperty(self,r,{get:()=>{throw new Error(`Security Exception: cannot access ${r}`)},set:()=>{throw new Error(`Security Exception: cannot set ${r}`)},configurable:!1})})),onmessage=e=>{let r,t=!0;try{r=Function(e.data.contextVariable,`\"use strict\";return (${e.data.code});`)(e.data.context)}catch(e){r=e,t=!1}const o={id:e.data.id,result:r,success:t};postMessage(o)}})()})();'}},t={};!function s(r){var o=t[r];if(void 0!==o)return o.exports;var n=t[r]={exports:{}};return e[r].call(n.exports,n,n.exports,s),n.exports}(306)})();";
4
+ export default source;
@@ -0,0 +1,2 @@
1
+ import source from './frame.source';
2
+ export default source;
@@ -0,0 +1,2 @@
1
+ import source from './frame.source';
2
+ export default source;
@@ -0,0 +1,2 @@
1
+ import Manager from './manager';
2
+ export default Manager;
@@ -0,0 +1,2 @@
1
+ import Manager from './manager';
2
+ export default Manager;
@@ -0,0 +1,13 @@
1
+ import { Task } from '../../types';
2
+ export default class Manager {
3
+ private worker;
4
+ private timerId?;
5
+ constructor();
6
+ execute(task: Task): Promise<unknown>;
7
+ terminate(): void;
8
+ private runTask;
9
+ private timeout;
10
+ private stopTimer;
11
+ private createWorker;
12
+ private removeMessageListener;
13
+ }
@@ -0,0 +1,59 @@
1
+ import workerSource from './worker.source';
2
+ export default class Manager {
3
+ constructor() {
4
+ this.worker = this.createWorker();
5
+ }
6
+ async execute(task) {
7
+ const workerTask = {
8
+ ...task,
9
+ contextVariable: task.contextVariable ?? 'ctx',
10
+ };
11
+ const result = this.runTask(workerTask);
12
+ const timeout = this.timeout(task.timeout ?? 1000);
13
+ return Promise.race([result, timeout]).finally(() => {
14
+ this.removeMessageListener();
15
+ });
16
+ }
17
+ terminate() {
18
+ this.removeMessageListener();
19
+ this.worker.terminate();
20
+ this.worker = this.createWorker();
21
+ }
22
+ runTask(task) {
23
+ return new Promise((resolve, reject) => {
24
+ const listener = (event) => {
25
+ const { data } = event;
26
+ if (data.id === task.id) {
27
+ this.stopTimer();
28
+ if (data.success) {
29
+ resolve(data.result);
30
+ }
31
+ else {
32
+ reject(data.result);
33
+ }
34
+ }
35
+ };
36
+ this.worker.addEventListener('message', listener);
37
+ this.removeMessageListener = () => this.worker.removeEventListener('message', listener);
38
+ this.worker.postMessage(task);
39
+ });
40
+ }
41
+ timeout(timeoutMs) {
42
+ return new Promise((_, reject) => {
43
+ this.timerId = window.setTimeout(() => {
44
+ this.terminate();
45
+ reject(new Error('maximum execution time exceeded'));
46
+ }, timeoutMs);
47
+ });
48
+ }
49
+ stopTimer() {
50
+ clearTimeout(this.timerId);
51
+ }
52
+ createWorker() {
53
+ const blob = URL.createObjectURL(new Blob([workerSource], { type: 'application/javascript' }));
54
+ return new Worker(blob);
55
+ }
56
+ removeMessageListener() {
57
+ // replaced by constructor
58
+ }
59
+ }
@@ -0,0 +1,11 @@
1
+ export interface WorkerResult {
2
+ id: string;
3
+ result: unknown;
4
+ success: boolean;
5
+ }
6
+ export interface WorkerTask {
7
+ id: string;
8
+ code: string;
9
+ contextVariable: string;
10
+ context?: unknown;
11
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,74 @@
1
+ const WHITELIST = [
2
+ 'TEMPORARY',
3
+ 'PERSISTENT',
4
+ 'console',
5
+ 'self',
6
+ 'onmessage',
7
+ 'postMessage',
8
+ 'global',
9
+ 'allowed',
10
+ 'Array',
11
+ 'Boolean',
12
+ 'Date',
13
+ 'Function',
14
+ 'Number',
15
+ 'Object',
16
+ 'RegExp',
17
+ 'String',
18
+ 'Error',
19
+ 'EvalError',
20
+ 'RangeError',
21
+ 'ReferenceError',
22
+ 'SyntaxError',
23
+ 'TypeError',
24
+ 'URIError',
25
+ 'decodeURI',
26
+ 'decodeURIComponent',
27
+ 'encodeURI',
28
+ 'encodeURIComponent',
29
+ 'isFinite',
30
+ 'isNaN',
31
+ 'parseFloat',
32
+ 'parseInt',
33
+ 'Infinity',
34
+ 'JSON',
35
+ 'Math',
36
+ 'NaN',
37
+ 'undefined',
38
+ ];
39
+ const windowProps = Object.getOwnPropertyNames(self);
40
+ const protoProps = Object.getOwnPropertyNames(self.__proto__);
41
+ const props = [...windowProps, ...protoProps];
42
+ props.forEach((prop) => {
43
+ if (!WHITELIST.includes(prop)) {
44
+ Object.defineProperty(self, prop, {
45
+ get: () => {
46
+ throw new Error(`Security Exception: cannot access ${prop}`);
47
+ },
48
+ set: () => {
49
+ throw new Error(`Security Exception: cannot set ${prop}`);
50
+ },
51
+ configurable: false,
52
+ });
53
+ }
54
+ });
55
+ // Parses and calls function string with args
56
+ onmessage = (event) => {
57
+ let result;
58
+ let success = true;
59
+ try {
60
+ const fn = Function(event.data.contextVariable, `"use strict";return (${event.data.code});`);
61
+ result = fn(event.data.context);
62
+ }
63
+ catch (e) {
64
+ result = e;
65
+ success = false;
66
+ }
67
+ const message = {
68
+ id: event.data.id,
69
+ result,
70
+ success,
71
+ };
72
+ postMessage(message);
73
+ };
74
+ export {};
@@ -0,0 +1,2 @@
1
+ declare const source: string;
2
+ export default source;
@@ -0,0 +1,4 @@
1
+ // Auto-generated file
2
+ /* eslint-disable */
3
+ const source = "(()=>{\"use strict\";(()=>{const e=[\"TEMPORARY\",\"PERSISTENT\",\"console\",\"self\",\"onmessage\",\"postMessage\",\"global\",\"allowed\",\"Array\",\"Boolean\",\"Date\",\"Function\",\"Number\",\"Object\",\"RegExp\",\"String\",\"Error\",\"EvalError\",\"RangeError\",\"ReferenceError\",\"SyntaxError\",\"TypeError\",\"URIError\",\"decodeURI\",\"decodeURIComponent\",\"encodeURI\",\"encodeURIComponent\",\"isFinite\",\"isNaN\",\"parseFloat\",\"parseInt\",\"Infinity\",\"JSON\",\"Math\",\"NaN\",\"undefined\"];[...Object.getOwnPropertyNames(self),...Object.getOwnPropertyNames(self.__proto__)].forEach((r=>{e.includes(r)||Object.defineProperty(self,r,{get:()=>{throw new Error(`Security Exception: cannot access ${r}`)},set:()=>{throw new Error(`Security Exception: cannot set ${r}`)},configurable:!1})})),onmessage=e=>{let r,t=!0;try{r=Function(e.data.contextVariable,`\"use strict\";return (${e.data.code});`)(e.data.context)}catch(e){r=e,t=!1}const o={id:e.data.id,result:r,success:t};postMessage(o)}})()})();";
4
+ export default source;
@@ -0,0 +1,2 @@
1
+ import WebSandbox from './sandbox';
2
+ export { WebSandbox };
@@ -0,0 +1,2 @@
1
+ import WebSandbox from './sandbox';
2
+ export { WebSandbox };
@@ -0,0 +1,48 @@
1
+ export interface SandboxOptions extends Required<RunCodeOptions> {
2
+ /** The selector or element to append the iframe to */
3
+ frameContainer: string | Element;
4
+ /** Whether to enable verbose logging */
5
+ debugMode: boolean;
6
+ }
7
+ export interface RunCodeOptions {
8
+ /** The name of the variable to use for the context object */
9
+ contextVariable?: string;
10
+ timeout?: {
11
+ /** The minimum time to wait for a task to complete
12
+ *
13
+ * The worker will attempt to kill the task after this time.
14
+ */
15
+ minimumMs: number;
16
+ /** The maximum time to wait for a task to complete
17
+ *
18
+ * The sandbox will be destroyed and rebuilt after this time.
19
+ */
20
+ maximumMs: number;
21
+ };
22
+ }
23
+ export declare const BaseOptions: SandboxOptions;
24
+ export interface SandboxMethods {
25
+ iframeInitialised: () => void;
26
+ }
27
+ export default class WebSandbox {
28
+ private options;
29
+ private frame;
30
+ private connection?;
31
+ initialised: Promise<WebSandbox>;
32
+ static new(options?: Partial<SandboxOptions>): Promise<WebSandbox>;
33
+ private constructor();
34
+ /** Runs code in the sandbox. Can be either a string or a function */
35
+ run<T = unknown>(code: string | (() => T), context?: unknown, options?: RunCodeOptions): Promise<T>;
36
+ /** Destroys the sandbox.
37
+ *
38
+ * This will remove the iframe from the DOM and remove the message listener.
39
+ */
40
+ destroy(): void;
41
+ private initialise;
42
+ private destroyAndRebuild;
43
+ private removeMessageListener;
44
+ private runFunction;
45
+ private runCode;
46
+ private createFrame;
47
+ private prepareFrameContent;
48
+ }
@@ -0,0 +1,116 @@
1
+ import Connection from './connection';
2
+ import frameSource from './frame';
3
+ import { generateUUID } from '../generateUUID';
4
+ export const BaseOptions = {
5
+ frameContainer: 'body',
6
+ debugMode: false,
7
+ contextVariable: 'ctx',
8
+ timeout: {
9
+ minimumMs: 500,
10
+ maximumMs: 1000,
11
+ },
12
+ };
13
+ export default class WebSandbox {
14
+ static async new(options = {}) {
15
+ return new WebSandbox(options).initialised;
16
+ }
17
+ constructor(options) {
18
+ this.options = { ...BaseOptions, ...options };
19
+ this.frame = this.createFrame();
20
+ this.initialised = this.initialise().then(() => this);
21
+ }
22
+ /** Runs code in the sandbox. Can be either a string or a function */
23
+ async run(code, context, options) {
24
+ if (typeof code === 'function') {
25
+ return this.runFunction(code, context);
26
+ }
27
+ return this.runCode(code, context, options);
28
+ }
29
+ /** Destroys the sandbox.
30
+ *
31
+ * This will remove the iframe from the DOM and remove the message listener.
32
+ */
33
+ destroy() {
34
+ this.frame.remove();
35
+ this.removeMessageListener();
36
+ }
37
+ initialise() {
38
+ return new Promise((resolve) => {
39
+ this.connection = new Connection('SANDBOX', this.frame.contentWindow.postMessage.bind(this.frame.contentWindow), {
40
+ iframeInitialised: () => resolve(),
41
+ }, (listener) => {
42
+ const sourceCheckListener = (event) => {
43
+ if (event.source !== this.frame.contentWindow) {
44
+ return;
45
+ }
46
+ return listener(event);
47
+ };
48
+ window.addEventListener('message', sourceCheckListener);
49
+ this.removeMessageListener = () => window.removeEventListener('message', sourceCheckListener);
50
+ }, { allowedSenderOrigin: 'null', debugMode: this.options.debugMode });
51
+ });
52
+ }
53
+ async destroyAndRebuild() {
54
+ this.destroy();
55
+ this.frame = this.createFrame();
56
+ await this.initialise();
57
+ }
58
+ removeMessageListener() {
59
+ // replaced by constructor
60
+ }
61
+ async runFunction(fn, context, options) {
62
+ return this.runCode(`(${fn.toString()})()`, context, options);
63
+ }
64
+ async runCode(code, context, options) {
65
+ if (!this.connection) {
66
+ throw new Error('sandbox not initialised');
67
+ }
68
+ let timerId;
69
+ const timeout = new Promise((_, reject) => {
70
+ setTimeout(() => {
71
+ this.destroyAndRebuild().finally(() => reject(new Error('sandbox timed out')));
72
+ }, options?.timeout?.maximumMs ?? this.options.timeout.maximumMs);
73
+ });
74
+ const taskId = generateUUID();
75
+ const task = await this.connection.callRemoteMethod('startTask', {
76
+ id: taskId,
77
+ code,
78
+ context,
79
+ timeout: this.options.timeout.minimumMs,
80
+ contextVariable: options?.contextVariable ?? this.options.contextVariable,
81
+ });
82
+ return Promise.race([task, timeout]).finally(() => {
83
+ clearTimeout(timerId);
84
+ });
85
+ }
86
+ createFrame() {
87
+ const containerSelector = this.options.frameContainer;
88
+ const container = typeof containerSelector === 'string' ? document.querySelector(containerSelector) : containerSelector;
89
+ if (!container) {
90
+ throw new Error('unable to find container for sandbox');
91
+ }
92
+ const iframe = document.createElement('iframe');
93
+ iframe.sandbox.add('allow-scripts');
94
+ iframe.srcdoc = this.prepareFrameContent();
95
+ iframe.style.display = 'none';
96
+ container.appendChild(iframe);
97
+ return iframe;
98
+ }
99
+ prepareFrameContent() {
100
+ let script = frameSource;
101
+ if (this.options.debugMode) {
102
+ script = `window.debugMode=true;${script}`;
103
+ }
104
+ return `
105
+ <!DOCTYPE html>
106
+ <html>
107
+ <head>
108
+ <meta charset="UTF-8">
109
+ <script>
110
+ ${script}
111
+ </script>
112
+ </head>
113
+ </html>
114
+ `;
115
+ }
116
+ }
@@ -0,0 +1,13 @@
1
+ type APIMethod = (...args: any[]) => unknown;
2
+ export type API = Map<string, APIMethod>;
3
+ export type APIDeclaration<T> = {
4
+ [K in keyof T]: T[K] extends APIMethod ? T[K] : never;
5
+ };
6
+ export interface Task {
7
+ id: string;
8
+ code: string;
9
+ context?: unknown;
10
+ timeout: number;
11
+ contextVariable?: string;
12
+ }
13
+ export {};
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dronedeploy/rocos-js-sdk",
3
- "version": "3.0.0-alpha.6",
3
+ "version": "3.0.1-alpha",
4
4
  "description": "Javascript SDK for rocos",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -23,5 +23,7 @@
23
23
  "rxjs": "^6.6.6 || ^7.0.0"
24
24
  },
25
25
  "repository": {},
26
- "private": false
26
+ "private": false,
27
+ "type": "module",
28
+ "sideEffects": false
27
29
  }
@@ -110,7 +110,7 @@ export class AssetStorageService extends BaseServiceAbstract {
110
110
  */
111
111
  async listMissionAssets(projectId, assetIdList) {
112
112
  const searchParams = new URLSearchParams();
113
- assetIdList.forEach((assetId) => searchParams.append('assetId', assetId));
113
+ assetIdList.forEach((assetId) => searchParams.append('assetID', assetId));
114
114
  return this.callGet(formatServiceUrl(API_PROJECT_MISSION_ASSETS_PATH_URL, { url: this.config.url, projectId }, this.config.insecure), `Failed to get asset for ${projectId}, assetIdList ${assetIdList}.`, searchParams);
115
115
  }
116
116
  /**
@@ -1,6 +1,6 @@
1
1
  import { IRocosSDKConfig, RocosError } from '../models';
2
+ import { QueryParams } from '../helpers';
2
3
  import { Logger } from 'loglevel';
3
- type QueryParams = Record<string, string | number | boolean> | URLSearchParams;
4
4
  type ResponseType = 'json' | 'blob' | 'stream' | 'text' | 'raw';
5
5
  interface RequestConfig {
6
6
  /**
@@ -1,4 +1,5 @@
1
1
  import { RocosError } from '../models';
2
+ import { getURLSearchParams } from '../helpers';
2
3
  import { RocosStore } from '../store/RocosStore';
3
4
  class HttpError extends Error {
4
5
  constructor(response) {
@@ -26,12 +27,7 @@ export class BaseServiceAbstract {
26
27
  async call(url, method, options) {
27
28
  const { errorMessage, config, payload, params } = options;
28
29
  try {
29
- // change all params to string
30
- const stringParams = Object.entries(params ?? {}).reduce((acc, [key, value]) => {
31
- acc[key] = value.toString();
32
- return acc;
33
- }, {});
34
- const formattedUrl = params ? `${url}?${new URLSearchParams(stringParams)}` : url;
30
+ const formattedUrl = params ? `${url}?${getURLSearchParams(params)}` : url;
35
31
  const defaultHeaders = {};
36
32
  if (!config?.public) {
37
33
  const token = await RocosStore.getSDKInstance(this.config).getAuthService().getToken();
@@ -1,6 +1,6 @@
1
- import { TelemetryService } from './TelemetryService';
2
- import { delay, from, lastValueFrom, NEVER, take, throwError } from 'rxjs';
3
1
  import { CallsignStatus } from '../models';
2
+ import { NEVER, delay, from, lastValueFrom, take, throwError } from 'rxjs';
3
+ import { TelemetryService } from './TelemetryService';
4
4
  describe('TelemetryService', () => {
5
5
  describe('getRobotStatusChanges', () => {
6
6
  it('should emit unknown to begin with', async () => {