@dronedeploy/rocos-js-sdk 3.0.10 → 3.0.12

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 (123) hide show
  1. package/cjs/api/StreamRegister.d.ts +3 -3
  2. package/cjs/api/StreamRegister.js +6 -6
  3. package/cjs/api/streams/telemetry/TelemetryStreamAbstract.js +8 -7
  4. package/cjs/helpers/kscript/Context.d.ts +14 -8
  5. package/cjs/helpers/kscript/Context.js +59 -22
  6. package/cjs/helpers/kscript/kscript.d.ts +7 -2
  7. package/cjs/helpers/kscript/kscript.js +24 -14
  8. package/cjs/helpers/kscript/nodes/Identifier.d.ts +1 -0
  9. package/cjs/helpers/kscript/nodes/Identifier.js +2 -1
  10. package/cjs/helpers/kscript/nodes/Literal.d.ts +1 -0
  11. package/cjs/helpers/kscript/nodes/Literal.js +4 -1
  12. package/cjs/helpers/kscript/nodes/Node.d.ts +6 -1
  13. package/cjs/helpers/kscript/nodes/Node.js +0 -1
  14. package/cjs/helpers/kscript/nodes/Program.d.ts +1 -0
  15. package/cjs/helpers/kscript/nodes/Program.js +13 -4
  16. package/cjs/helpers/kscript/nodes/TemplateLiteral.d.ts +1 -0
  17. package/cjs/helpers/kscript/nodes/TemplateLiteral.js +8 -0
  18. package/cjs/helpers/kscript/nodes/VariableDeclaration.d.ts +8 -0
  19. package/cjs/helpers/kscript/nodes/VariableDeclaration.js +43 -0
  20. package/cjs/helpers/kscript/nodes/expressions/ArrayExpression.d.ts +1 -0
  21. package/cjs/helpers/kscript/nodes/expressions/ArrayExpression.js +11 -0
  22. package/cjs/helpers/kscript/nodes/expressions/ArrowFunctionExpression.d.ts +1 -0
  23. package/cjs/helpers/kscript/nodes/expressions/ArrowFunctionExpression.js +19 -5
  24. package/cjs/helpers/kscript/nodes/expressions/AssignmentExpression.d.ts +8 -0
  25. package/cjs/helpers/kscript/nodes/expressions/AssignmentExpression.js +33 -0
  26. package/cjs/helpers/kscript/nodes/expressions/BinaryExpression.d.ts +8 -0
  27. package/cjs/helpers/kscript/nodes/expressions/BinaryExpression.js +55 -22
  28. package/cjs/helpers/kscript/nodes/expressions/CallExpression.d.ts +1 -0
  29. package/cjs/helpers/kscript/nodes/expressions/CallExpression.js +11 -1
  30. package/cjs/helpers/kscript/nodes/expressions/ChainExpression.d.ts +1 -0
  31. package/cjs/helpers/kscript/nodes/expressions/ChainExpression.js +1 -0
  32. package/cjs/helpers/kscript/nodes/expressions/ConditionalExpression.d.ts +1 -0
  33. package/cjs/helpers/kscript/nodes/expressions/ConditionalExpression.js +10 -0
  34. package/cjs/helpers/kscript/nodes/expressions/LogicalExpression.d.ts +2 -0
  35. package/cjs/helpers/kscript/nodes/expressions/LogicalExpression.js +11 -0
  36. package/cjs/helpers/kscript/nodes/expressions/MemberExpression.d.ts +1 -0
  37. package/cjs/helpers/kscript/nodes/expressions/MemberExpression.js +7 -0
  38. package/cjs/helpers/kscript/nodes/expressions/NewExpression.d.ts +9 -0
  39. package/cjs/helpers/kscript/nodes/expressions/NewExpression.js +44 -0
  40. package/cjs/helpers/kscript/nodes/expressions/ObjectExpression.d.ts +1 -0
  41. package/cjs/helpers/kscript/nodes/expressions/ObjectExpression.js +29 -0
  42. package/cjs/helpers/kscript/nodes/expressions/UnaryExpression.d.ts +2 -0
  43. package/cjs/helpers/kscript/nodes/expressions/UnaryExpression.js +9 -0
  44. package/cjs/helpers/kscript/nodes/expressions/index.d.ts +2 -1
  45. package/cjs/helpers/kscript/nodes/expressions/index.js +5 -3
  46. package/cjs/helpers/kscript/nodes/nodeTypes.d.ts +9 -1
  47. package/cjs/helpers/kscript/nodes/nodeTypes.js +9 -1
  48. package/cjs/helpers/kscript/nodes/statements/BlockStatement.d.ts +8 -0
  49. package/cjs/helpers/kscript/nodes/statements/BlockStatement.js +29 -0
  50. package/cjs/helpers/kscript/nodes/{expressions → statements}/ExpressionStatement.d.ts +1 -0
  51. package/cjs/helpers/kscript/nodes/{expressions → statements}/ExpressionStatement.js +8 -1
  52. package/cjs/helpers/kscript/nodes/statements/ReturnStatement.d.ts +12 -0
  53. package/cjs/helpers/kscript/nodes/statements/ReturnStatement.js +33 -0
  54. package/cjs/helpers/kscript/nodes/statements/SwitchStatement.d.ts +9 -0
  55. package/cjs/helpers/kscript/nodes/statements/SwitchStatement.js +52 -0
  56. package/cjs/helpers/kscript/nodes/statements/index.d.ts +4 -0
  57. package/cjs/helpers/kscript/nodes/statements/index.js +14 -0
  58. package/cjs/models/asset-storage/SyncIntegrations.d.ts +3 -3
  59. package/cjs/services/AssetStorageService.d.ts +3 -3
  60. package/cjs/services/BaseStreamService.js +1 -1
  61. package/cjs/services/WebRTCSignallingService.js +1 -2
  62. package/esm/api/StreamRegister.d.ts +3 -3
  63. package/esm/api/StreamRegister.js +6 -6
  64. package/esm/api/streams/telemetry/TelemetryStreamAbstract.js +8 -7
  65. package/esm/helpers/kscript/Context.d.ts +14 -8
  66. package/esm/helpers/kscript/Context.js +59 -22
  67. package/esm/helpers/kscript/kscript.d.ts +7 -2
  68. package/esm/helpers/kscript/kscript.js +22 -13
  69. package/esm/helpers/kscript/nodes/Identifier.d.ts +1 -0
  70. package/esm/helpers/kscript/nodes/Identifier.js +2 -1
  71. package/esm/helpers/kscript/nodes/Literal.d.ts +1 -0
  72. package/esm/helpers/kscript/nodes/Literal.js +4 -1
  73. package/esm/helpers/kscript/nodes/Node.d.ts +6 -1
  74. package/esm/helpers/kscript/nodes/Node.js +0 -1
  75. package/esm/helpers/kscript/nodes/Program.d.ts +1 -0
  76. package/esm/helpers/kscript/nodes/Program.js +13 -4
  77. package/esm/helpers/kscript/nodes/TemplateLiteral.d.ts +1 -0
  78. package/esm/helpers/kscript/nodes/TemplateLiteral.js +8 -0
  79. package/esm/helpers/kscript/nodes/VariableDeclaration.d.ts +8 -0
  80. package/esm/helpers/kscript/nodes/VariableDeclaration.js +37 -0
  81. package/esm/helpers/kscript/nodes/expressions/ArrayExpression.d.ts +1 -0
  82. package/esm/helpers/kscript/nodes/expressions/ArrayExpression.js +11 -0
  83. package/esm/helpers/kscript/nodes/expressions/ArrowFunctionExpression.d.ts +1 -0
  84. package/esm/helpers/kscript/nodes/expressions/ArrowFunctionExpression.js +19 -5
  85. package/esm/helpers/kscript/nodes/expressions/AssignmentExpression.d.ts +8 -0
  86. package/esm/helpers/kscript/nodes/expressions/AssignmentExpression.js +27 -0
  87. package/esm/helpers/kscript/nodes/expressions/BinaryExpression.d.ts +8 -0
  88. package/esm/helpers/kscript/nodes/expressions/BinaryExpression.js +57 -23
  89. package/esm/helpers/kscript/nodes/expressions/CallExpression.d.ts +1 -0
  90. package/esm/helpers/kscript/nodes/expressions/CallExpression.js +11 -1
  91. package/esm/helpers/kscript/nodes/expressions/ChainExpression.d.ts +1 -0
  92. package/esm/helpers/kscript/nodes/expressions/ChainExpression.js +1 -0
  93. package/esm/helpers/kscript/nodes/expressions/ConditionalExpression.d.ts +1 -0
  94. package/esm/helpers/kscript/nodes/expressions/ConditionalExpression.js +10 -0
  95. package/esm/helpers/kscript/nodes/expressions/LogicalExpression.d.ts +2 -0
  96. package/esm/helpers/kscript/nodes/expressions/LogicalExpression.js +13 -1
  97. package/esm/helpers/kscript/nodes/expressions/MemberExpression.d.ts +1 -0
  98. package/esm/helpers/kscript/nodes/expressions/MemberExpression.js +7 -0
  99. package/esm/helpers/kscript/nodes/expressions/NewExpression.d.ts +9 -0
  100. package/esm/helpers/kscript/nodes/expressions/NewExpression.js +38 -0
  101. package/esm/helpers/kscript/nodes/expressions/ObjectExpression.d.ts +1 -0
  102. package/esm/helpers/kscript/nodes/expressions/ObjectExpression.js +29 -0
  103. package/esm/helpers/kscript/nodes/expressions/UnaryExpression.d.ts +2 -0
  104. package/esm/helpers/kscript/nodes/expressions/UnaryExpression.js +11 -1
  105. package/esm/helpers/kscript/nodes/expressions/index.d.ts +2 -1
  106. package/esm/helpers/kscript/nodes/expressions/index.js +2 -1
  107. package/esm/helpers/kscript/nodes/nodeTypes.d.ts +9 -1
  108. package/esm/helpers/kscript/nodes/nodeTypes.js +9 -1
  109. package/esm/helpers/kscript/nodes/statements/BlockStatement.d.ts +8 -0
  110. package/esm/helpers/kscript/nodes/statements/BlockStatement.js +23 -0
  111. package/esm/helpers/kscript/nodes/{expressions → statements}/ExpressionStatement.d.ts +1 -0
  112. package/esm/helpers/kscript/nodes/{expressions → statements}/ExpressionStatement.js +8 -1
  113. package/esm/helpers/kscript/nodes/statements/ReturnStatement.d.ts +12 -0
  114. package/esm/helpers/kscript/nodes/statements/ReturnStatement.js +25 -0
  115. package/esm/helpers/kscript/nodes/statements/SwitchStatement.d.ts +9 -0
  116. package/esm/helpers/kscript/nodes/statements/SwitchStatement.js +46 -0
  117. package/esm/helpers/kscript/nodes/statements/index.d.ts +4 -0
  118. package/esm/helpers/kscript/nodes/statements/index.js +4 -0
  119. package/esm/models/asset-storage/SyncIntegrations.d.ts +3 -3
  120. package/esm/services/AssetStorageService.d.ts +3 -3
  121. package/esm/services/BaseStreamService.js +1 -1
  122. package/esm/services/WebRTCSignallingService.js +1 -2
  123. package/package.json +1 -1
@@ -6,8 +6,8 @@ export declare class StreamRegister {
6
6
  private constructor();
7
7
  static getInstance(): StreamRegister;
8
8
  static getIdentifier($identifier: string, scope?: string): string;
9
- addStream(stream: IBaseStream): Promise<void>;
9
+ addStream(stream: IBaseStream): void;
10
10
  getStream(identifier: string): IBaseStream | undefined;
11
- removeStream(stream: IBaseStream): Promise<void>;
12
- removeAllStreams(): Promise<boolean>;
11
+ removeStream(stream: IBaseStream): void;
12
+ removeAllStreams(): boolean;
13
13
  }
@@ -17,10 +17,10 @@ class StreamRegister {
17
17
  static getIdentifier($identifier, scope) {
18
18
  return `${$identifier}-${scope ?? ''}`;
19
19
  }
20
- async addStream(stream) {
20
+ addStream(stream) {
21
21
  try {
22
22
  if (this.teleStreams.has(stream.identifier)) {
23
- await this.removeStream(stream);
23
+ this.removeStream(stream);
24
24
  }
25
25
  this.teleStreams.set(stream.identifier, stream);
26
26
  }
@@ -37,9 +37,9 @@ class StreamRegister {
37
37
  getStream(identifier) {
38
38
  return this.teleStreams.get(identifier);
39
39
  }
40
- async removeStream(stream) {
40
+ removeStream(stream) {
41
41
  try {
42
- await stream.stopStream();
42
+ stream.stopStream();
43
43
  this.teleStreams.delete(stream.identifier);
44
44
  }
45
45
  catch (e) {
@@ -52,10 +52,10 @@ class StreamRegister {
52
52
  }
53
53
  }
54
54
  }
55
- async removeAllStreams() {
55
+ removeAllStreams() {
56
56
  try {
57
57
  for (const [, stream] of this.teleStreams.entries()) {
58
- await this.removeStream(stream);
58
+ this.removeStream(stream);
59
59
  }
60
60
  }
61
61
  catch (e) {
@@ -103,6 +103,14 @@ class TelemetryStreamAbstract {
103
103
  const before = this.getSubscriptions();
104
104
  this.subscriptions.delete(params.uniqueId);
105
105
  const after = this.getSubscriptions();
106
+ // Do this before we send the telemetry request so that new subscriptions don't get this stream from the register while it is closing
107
+ if (!this.subscriptions.size) {
108
+ this.logger.info('No subscriptions remaining closing stream', params.uniqueId);
109
+ this.stopStream();
110
+ // self remove when no subscriptions are left
111
+ StreamRegister_1.StreamRegister.getInstance().removeStream(this);
112
+ this.logger.info('Stream closed');
113
+ }
106
114
  // assign the current values from state after change
107
115
  this.callsignsLookup = new models_1.CallsignsLookup(Object.keys(after));
108
116
  this.sources = (0, arrayUnique_1.arrayUnique)(Object.values(after).reduce((a, v) => a.concat(v), []));
@@ -116,13 +124,6 @@ class TelemetryStreamAbstract {
116
124
  this.logger.error(`Failed to unsubscribe: ${err}`);
117
125
  }
118
126
  }
119
- if (!this.subscriptions.size) {
120
- this.logger.info('No subscriptions remaining closing stream', params.uniqueId);
121
- this.stopStream();
122
- // self remove when no subscriptions a re left
123
- void StreamRegister_1.StreamRegister.getInstance().removeStream(this);
124
- this.logger.info('Stream closed');
125
- }
126
127
  }
127
128
  sendAcknowledgment(uid, status, noRetry) {
128
129
  return this.sendAcknowledgmentInternal(uid, status, noRetry);
@@ -7,19 +7,20 @@ import type { NodeType } from './nodes';
7
7
  * If the set is null, all properties are allowed.
8
8
  */
9
9
  export type PrototypeWhitelist = Map<unknown, Set<string> | null>;
10
- export type Scope = Map<string, unknown> | Record<string, unknown>;
10
+ export type Scope = Record<string, unknown> | Map<string, unknown>;
11
11
  export interface ContextOptions {
12
12
  nodeBlacklist?: Set<NodeType>;
13
13
  prototypeWhitelist?: PrototypeWhitelist;
14
14
  functionCallsAllowed?: boolean;
15
15
  }
16
16
  export default class Context {
17
- readonly scope: Map<string, unknown>;
18
- readonly isFunctionCallAllowed: boolean;
19
- private readonly options;
20
- constructor(scope?: Scope, options?: ContextOptions);
21
- isNodeAllowed(nodeType: AnyNode['type']): boolean;
22
- newChildContext(scope: Scope): Context;
17
+ private readonly parent;
18
+ private readonly scope;
19
+ private readonly config;
20
+ static create(scope?: Scope, options?: ContextOptions): Context;
21
+ private static buildMap;
22
+ private constructor();
23
+ newChild(scope?: Scope): Context;
23
24
  /** Returns true if the property is allowed to be accessed on the prototype.
24
25
  *
25
26
  * - If the prototype is not in the whitelist, the property is allowed only if it is an own property.
@@ -30,5 +31,10 @@ export default class Context {
30
31
  * @param ownProperty Whether the property is owned by the object it is being accessed on.
31
32
  */
32
33
  isPrototypeAllowed(prototype: unknown, property: string, ownProperty: boolean): boolean;
33
- private static buildMap;
34
+ isNodeAllowed(nodeType: AnyNode['type']): boolean;
35
+ get isFunctionCallAllowed(): boolean;
36
+ private getDescriptor;
37
+ get(key: string): unknown;
38
+ set(key: string, value: unknown): void;
39
+ declare(key: string, value: unknown, constant: boolean): void;
34
40
  }
@@ -1,28 +1,29 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  class Context {
4
- constructor(scope, options) {
4
+ static create(scope, options) {
5
+ return new Context(null, {
6
+ functionCallsAllowed: true,
7
+ nodeBlacklist: new Set(),
8
+ prototypeWhitelist: new Map(),
9
+ ...(options ?? {}),
10
+ }, scope);
11
+ }
12
+ static buildMap(obj) {
13
+ const entries = obj instanceof Map ? Array.from(obj.entries()) : Object.entries(obj);
14
+ return new Map(entries.map(([key, value]) => [key, { value, constant: false }]));
15
+ }
16
+ constructor(parent, config, scope) {
17
+ this.parent = parent;
5
18
  this.scope = scope ? Context.buildMap(scope) : new Map();
6
- this.options = {
7
- nodeBlacklist: options?.nodeBlacklist ?? new Set(),
8
- prototypeWhitelist: options?.prototypeWhitelist ?? new Map(),
9
- functionCallsAllowed: options?.functionCallsAllowed ?? true,
19
+ this.config = {
20
+ nodeBlacklist: config.nodeBlacklist ?? new Set(),
21
+ prototypeWhitelist: config.prototypeWhitelist ?? new Map(),
22
+ functionCallsAllowed: config.functionCallsAllowed ?? true,
10
23
  };
11
- this.isFunctionCallAllowed = this.options.functionCallsAllowed;
12
- }
13
- isNodeAllowed(nodeType) {
14
- return !this.options.nodeBlacklist.has(nodeType);
15
24
  }
16
- newChildContext(scope) {
17
- let newEntries;
18
- if (scope instanceof Map) {
19
- newEntries = [...scope.entries()];
20
- }
21
- else {
22
- newEntries = Object.entries(scope);
23
- }
24
- const newScope = new Map([...this.scope.entries(), ...newEntries]);
25
- return new Context(newScope, this.options);
25
+ newChild(scope) {
26
+ return new Context(this, this.config, scope);
26
27
  }
27
28
  /** Returns true if the property is allowed to be accessed on the prototype.
28
29
  *
@@ -34,7 +35,7 @@ class Context {
34
35
  * @param ownProperty Whether the property is owned by the object it is being accessed on.
35
36
  */
36
37
  isPrototypeAllowed(prototype, property, ownProperty) {
37
- const whitelist = this.options.prototypeWhitelist.get(prototype);
38
+ const whitelist = this.config.prototypeWhitelist.get(prototype);
38
39
  // If the object owns the property, and it's not in the whitelist, it's allowed.
39
40
  // This prevents direct calls to non-whitelisted properties on allowed prototypes
40
41
  // e.g. Object.prototype.hasOwnProperty.call(obj, 'toString')
@@ -45,8 +46,44 @@ class Context {
45
46
  return true;
46
47
  return whitelist.has(property);
47
48
  }
48
- static buildMap(obj) {
49
- return obj instanceof Map ? obj : new Map(Object.entries(obj));
49
+ isNodeAllowed(nodeType) {
50
+ return !this.config.nodeBlacklist.has(nodeType);
51
+ }
52
+ get isFunctionCallAllowed() {
53
+ return this.config.functionCallsAllowed;
54
+ }
55
+ getDescriptor(key, searchParents = true) {
56
+ if (this.scope.has(key)) {
57
+ return this.scope.get(key);
58
+ }
59
+ if (searchParents && this.parent) {
60
+ return this.parent.getDescriptor(key);
61
+ }
62
+ return undefined;
63
+ }
64
+ get(key) {
65
+ const descriptor = this.getDescriptor(key);
66
+ if (descriptor) {
67
+ return descriptor.value;
68
+ }
69
+ return undefined;
70
+ }
71
+ set(key, value) {
72
+ const descriptor = this.getDescriptor(key);
73
+ if (!descriptor) {
74
+ throw new Error(`no variable with name ${key}`);
75
+ }
76
+ if (descriptor.constant) {
77
+ throw new Error(`cannot reassign to constant variable ${key}`);
78
+ }
79
+ descriptor.value = value;
80
+ }
81
+ declare(key, value, constant) {
82
+ const descriptor = this.getDescriptor(key, false);
83
+ if (descriptor) {
84
+ throw new Error(`variable ${key} already declared`);
85
+ }
86
+ this.scope.set(key, { value, constant });
50
87
  }
51
88
  }
52
89
  exports.default = Context;
@@ -1,6 +1,11 @@
1
1
  import { ContextOptions, PrototypeWhitelist, Scope } from './Context';
2
2
  import { Node } from './nodes';
3
- export declare function execute(code: string, scope?: Scope, options?: ContextOptions): unknown;
4
- export declare function compile(code: string, scope?: Scope, options?: ContextOptions): Node;
3
+ export declare const compile: (code: string, scope?: Scope, options?: ContextOptions) => Node;
4
+ /** Throws an error if the code uses an invalid node.
5
+ *
6
+ * This does not check that the code will run without errors, only that the nodes used are allowed.
7
+ **/
8
+ export declare const validate: (code: string, options?: ContextOptions) => void;
9
+ export declare const execute: (code: string, scope?: Scope, options?: ContextOptions) => unknown;
5
10
  export declare const SAFE_GLOBALS: Map<string, unknown>;
6
11
  export declare const SAFE_PROTOTYPES: PrototypeWhitelist;
@@ -3,36 +3,46 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.SAFE_PROTOTYPES = exports.SAFE_GLOBALS = exports.compile = exports.execute = void 0;
6
+ exports.SAFE_PROTOTYPES = exports.SAFE_GLOBALS = exports.execute = exports.validate = exports.compile = void 0;
7
7
  const Context_1 = __importDefault(require("./Context"));
8
8
  const acorn_1 = require("acorn");
9
9
  const utils_1 = require("./utils");
10
- function execute(code, scope, options) {
11
- return compile(code, scope, options).run();
12
- }
13
- exports.execute = execute;
14
- function compile(code, scope, options) {
15
- let program;
10
+ const parse = (code) => {
16
11
  try {
17
12
  // we add the brackets to ensure that object literals are parsed correctly
18
- program = (0, acorn_1.parse)(`(${code})`, { ecmaVersion: 2020, sourceType: 'script' });
13
+ // e.g. `{ a: 1 }` is not a valid statement, but `({ a: 1 })` is
14
+ return (0, acorn_1.parse)(`(${code})`, { ecmaVersion: 2020, sourceType: 'script' });
19
15
  }
20
16
  catch (error) {
21
17
  if (error instanceof SyntaxError) {
22
18
  // fallback to non-wrapped code for edge cases where the brackets cause issues
23
- program = (0, acorn_1.parse)(`${code}`, { ecmaVersion: 2020, sourceType: 'script' });
24
- }
25
- else {
26
- throw error;
19
+ // e.g. `if (a) 2; else 4;` will throw a syntax error if wrapped in brackets
20
+ return (0, acorn_1.parse)(`${code}`, { ecmaVersion: 2020, sourceType: 'script' });
27
21
  }
22
+ throw error;
28
23
  }
29
- const context = new Context_1.default(scope ?? exports.SAFE_GLOBALS, {
24
+ };
25
+ const compile = (code, scope, options) => {
26
+ const program = parse(code);
27
+ const context = Context_1.default.create(scope ?? exports.SAFE_GLOBALS, {
30
28
  prototypeWhitelist: exports.SAFE_PROTOTYPES,
31
29
  ...options,
32
30
  });
33
31
  return (0, utils_1.construct)(program, context);
34
- }
32
+ };
35
33
  exports.compile = compile;
34
+ /** Throws an error if the code uses an invalid node.
35
+ *
36
+ * This does not check that the code will run without errors, only that the nodes used are allowed.
37
+ **/
38
+ const validate = (code, options) => {
39
+ (0, exports.compile)(code, undefined, options).validate(true);
40
+ };
41
+ exports.validate = validate;
42
+ const execute = (code, scope, options) => {
43
+ return (0, exports.compile)(code, scope, options).run();
44
+ };
45
+ exports.execute = execute;
36
46
  exports.SAFE_GLOBALS = new Map(Object.entries({
37
47
  console: {
38
48
  debug: console.debug,
@@ -8,5 +8,6 @@ import Node from './Node';
8
8
  */
9
9
  export default class IdentifierNode extends Node<Identifier> {
10
10
  constructor(node: AnyNode, context: Context);
11
+ validate(): void;
11
12
  run(): unknown;
12
13
  }
@@ -13,8 +13,9 @@ class IdentifierNode extends Node_1.default {
13
13
  constructor(node, context) {
14
14
  super(node, context, 'Identifier');
15
15
  }
16
+ validate() { }
16
17
  run() {
17
- return this.scope.get(this.node.name);
18
+ return this.context.get(this.node.name);
18
19
  }
19
20
  }
20
21
  exports.default = IdentifierNode;
@@ -7,5 +7,6 @@ import Node from './Node';
7
7
  */
8
8
  export default class LiteralNode extends Node<Literal> {
9
9
  constructor(node: AnyNode, context: Context);
10
+ validate(): void;
10
11
  run(): unknown;
11
12
  }
@@ -12,10 +12,13 @@ class LiteralNode extends Node_1.default {
12
12
  constructor(node, context) {
13
13
  super(node, context, 'Literal');
14
14
  }
15
- run() {
15
+ validate() {
16
16
  if (this.node.bigint) {
17
17
  throw new Error('BigInts are not supported');
18
18
  }
19
+ }
20
+ run() {
21
+ this.validate();
19
22
  return this.node.value;
20
23
  }
21
24
  }
@@ -3,7 +3,12 @@ import type Context from '../Context';
3
3
  export default abstract class Node<T extends AnyNode = AnyNode> {
4
4
  protected context: Context;
5
5
  protected node: T;
6
- protected scope: Context['scope'];
7
6
  protected constructor(node: AnyNode, context: Context, expectedType: T['type'] | T['type'][]);
7
+ /** Runs the node. */
8
8
  abstract run(): unknown;
9
+ /** Validates the node. If the node is invalid, an error is thrown.
10
+ *
11
+ * if `validateChildren` is true, the children of the node will also be validated.
12
+ */
13
+ abstract validate(validateChildren: boolean): void;
9
14
  }
@@ -9,7 +9,6 @@ class Node {
9
9
  throw new Error(`Expected node type to be ${expectedTypes.join(' or ')}, but got ${node.type}`);
10
10
  }
11
11
  this.node = node;
12
- this.scope = context.scope;
13
12
  }
14
13
  }
15
14
  exports.default = Node;
@@ -7,5 +7,6 @@ import Node from './Node';
7
7
  */
8
8
  export default class ProgramNode extends Node<Program> {
9
9
  constructor(node: AnyNode, scope: Context);
10
+ validate(validateChildren?: boolean): void;
10
11
  run(): unknown;
11
12
  }
@@ -13,12 +13,21 @@ class ProgramNode extends Node_1.default {
13
13
  constructor(node, scope) {
14
14
  super(node, scope, 'Program');
15
15
  }
16
+ validate(validateChildren = false) {
17
+ if (!validateChildren) {
18
+ return;
19
+ }
20
+ for (const child of this.node.body) {
21
+ (0, utils_1.construct)(child, this.context).validate(true);
22
+ }
23
+ }
16
24
  run() {
17
- const children = this.node.body;
18
- if (children.length !== 1) {
19
- throw new Error('Only a single statement is supported in the sandbox');
25
+ this.validate();
26
+ let result;
27
+ for (const child of this.node.body) {
28
+ result = (0, utils_1.construct)(child, this.context).run();
20
29
  }
21
- return (0, utils_1.construct)(children[0], this.context).run();
30
+ return result;
22
31
  }
23
32
  }
24
33
  exports.default = ProgramNode;
@@ -19,5 +19,6 @@ import Node from './Node';
19
19
  */
20
20
  export default class TemplateLiteralNode extends Node<TemplateLiteral> {
21
21
  constructor(node: AnyNode, scope: Context);
22
+ validate(validateChildren?: boolean): void;
22
23
  run(): unknown;
23
24
  }
@@ -25,6 +25,14 @@ class TemplateLiteralNode extends Node_1.default {
25
25
  constructor(node, scope) {
26
26
  super(node, scope, 'TemplateLiteral');
27
27
  }
28
+ validate(validateChildren = false) {
29
+ if (!validateChildren) {
30
+ return;
31
+ }
32
+ for (const expression of this.node.expressions) {
33
+ (0, utils_1.construct)(expression, this.context).validate(true);
34
+ }
35
+ }
28
36
  run() {
29
37
  return this.node.quasis.reduce((acc, quasi, i) => {
30
38
  const expression = this.node.expressions[i] ? (0, utils_1.construct)(this.node.expressions[i], this.context).run() : '';
@@ -0,0 +1,8 @@
1
+ import { AnyNode, VariableDeclaration } from 'acorn';
2
+ import Context from '../Context';
3
+ import Node from './Node';
4
+ export default class VariableDeclarationNode extends Node<VariableDeclaration> {
5
+ constructor(node: AnyNode, context: Context);
6
+ validate(validateChildren?: boolean): void;
7
+ run(): void;
8
+ }
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const Node_1 = __importDefault(require("./Node"));
7
+ const utils_1 = require("../utils");
8
+ class VariableDeclarationNode extends Node_1.default {
9
+ constructor(node, context) {
10
+ super(node, context, 'VariableDeclaration');
11
+ }
12
+ validate(validateChildren = false) {
13
+ if (this.node.kind === 'var') {
14
+ throw new Error('var is not supported. use let or const instead');
15
+ }
16
+ for (const declaration of this.node.declarations) {
17
+ if (declaration.id.type !== 'Identifier') {
18
+ throw new Error('variable name must be an identifier');
19
+ }
20
+ }
21
+ if (!validateChildren) {
22
+ return;
23
+ }
24
+ for (const declaration of this.node.declarations) {
25
+ (0, utils_1.construct)(declaration.id, this.context).validate(true);
26
+ if (declaration.init) {
27
+ (0, utils_1.construct)(declaration.init, this.context).validate(true);
28
+ }
29
+ }
30
+ }
31
+ run() {
32
+ this.validate();
33
+ for (const declaration of this.node.declarations) {
34
+ const id = declaration.id.name;
35
+ let init;
36
+ if (declaration.init) {
37
+ init = (0, utils_1.construct)(declaration.init, this.context).run();
38
+ }
39
+ this.context.declare(id, init, this.node.kind === 'const');
40
+ }
41
+ }
42
+ }
43
+ exports.default = VariableDeclarationNode;
@@ -3,5 +3,6 @@ import type Context from '../../Context';
3
3
  import Node from '../Node';
4
4
  export default class ArrayExpressionNode extends Node<ArrayExpression> {
5
5
  constructor(node: AnyNode, context: Context);
6
+ validate(validateChildren?: boolean): void;
6
7
  run(): unknown;
7
8
  }
@@ -9,6 +9,17 @@ class ArrayExpressionNode extends Node_1.default {
9
9
  constructor(node, context) {
10
10
  super(node, context, 'ArrayExpression');
11
11
  }
12
+ validate(validateChildren = false) {
13
+ if (!validateChildren) {
14
+ return;
15
+ }
16
+ for (const element of this.node.elements) {
17
+ if (element === null) {
18
+ continue;
19
+ }
20
+ (0, utils_1.construct)(element, this.context).validate(true);
21
+ }
22
+ }
12
23
  run() {
13
24
  const array = [];
14
25
  for (const element of this.node.elements) {
@@ -10,5 +10,6 @@ import Node from '../Node';
10
10
  export default class ArrowFunctionExpressionNode extends Node<ArrowFunctionExpression> {
11
11
  private readonly params;
12
12
  constructor(node: AnyNode, context: Context);
13
+ validate(validateChildren?: boolean): void;
13
14
  run(): unknown;
14
15
  }
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const Node_1 = __importDefault(require("../Node"));
7
+ const ReturnStatement_1 = require("../statements/ReturnStatement");
7
8
  const utils_1 = require("../../utils");
8
9
  /** Represents an arrow function expression.
9
10
  *
@@ -19,21 +20,34 @@ class ArrowFunctionExpressionNode extends Node_1.default {
19
20
  }
20
21
  this.params = this.node.params;
21
22
  }
22
- run() {
23
+ validate(validateChildren = false) {
23
24
  if (!this.context.isFunctionCallAllowed)
24
25
  throw new Error('functions are not allowed');
25
- if (!this.node.expression)
26
- throw new Error('functions must be an expression');
27
26
  if (this.node.async)
28
27
  throw new Error('async functions are not supported');
28
+ if (!validateChildren) {
29
+ return;
30
+ }
31
+ (0, utils_1.construct)(this.node.body, this.context).validate(true);
32
+ }
33
+ run() {
34
+ this.validate();
29
35
  return (...args) => {
30
36
  const scope = new Map();
31
37
  for (const param of this.params) {
32
38
  const index = this.params.indexOf(param);
33
39
  scope.set(param.name, args?.[index]);
34
40
  }
35
- const newContext = this.context.newChildContext(scope);
36
- return (0, utils_1.construct)(this.node.body, newContext).run();
41
+ const newContext = this.context.newChild(scope);
42
+ try {
43
+ return (0, utils_1.construct)(this.node.body, newContext).run();
44
+ }
45
+ catch (e) {
46
+ if (e instanceof ReturnStatement_1.ReturnStatementError) {
47
+ return e.value;
48
+ }
49
+ throw e;
50
+ }
37
51
  };
38
52
  }
39
53
  }
@@ -0,0 +1,8 @@
1
+ import { AnyNode, AssignmentExpression } from 'acorn';
2
+ import Context from '../../Context';
3
+ import Node from '../Node';
4
+ export default class AssignmentExpressionNode extends Node<AssignmentExpression> {
5
+ constructor(node: AnyNode, context: Context);
6
+ validate(validateChildren?: boolean): void;
7
+ run(): unknown;
8
+ }
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const Node_1 = __importDefault(require("../Node"));
7
+ const utils_1 = require("../../utils");
8
+ class AssignmentExpressionNode extends Node_1.default {
9
+ constructor(node, context) {
10
+ super(node, context, 'AssignmentExpression');
11
+ }
12
+ validate(validateChildren = false) {
13
+ if (this.node.operator !== '=') {
14
+ throw new Error('Only = operator is supported');
15
+ }
16
+ if (this.node.left.type !== 'Identifier') {
17
+ throw new Error('Left side of assignment must be an identifier');
18
+ }
19
+ if (!validateChildren) {
20
+ return;
21
+ }
22
+ (0, utils_1.construct)(this.node.left, this.context).validate(true);
23
+ (0, utils_1.construct)(this.node.right, this.context).validate(true);
24
+ }
25
+ run() {
26
+ this.validate();
27
+ const identifier = this.node.left.name;
28
+ const right = (0, utils_1.construct)(this.node.right, this.context).run();
29
+ this.context.set(identifier, right);
30
+ return right;
31
+ }
32
+ }
33
+ exports.default = AssignmentExpressionNode;
@@ -6,9 +6,17 @@ import Node from '../Node';
6
6
  * e.g. `1 + 2`, `a === b`, `c < d`
7
7
  */
8
8
  export default class BinaryExpressionNode extends Node<BinaryExpression> {
9
+ private static readonly EQUALITY_OPERATORS;
10
+ private static readonly COMPARISON_OPERATORS;
11
+ private static readonly ARITHMETIC_OPERATORS;
12
+ private static readonly BITWISE_OPERATORS;
13
+ private static readonly IN_OPERATOR;
14
+ private static readonly SUPPORTED_OPERATORS;
9
15
  constructor(node: AnyNode, context: Context);
16
+ validate(validateChildren?: boolean): void;
10
17
  run(): unknown;
11
18
  private operate;
19
+ private bitwise;
12
20
  private arithmetic;
13
21
  private comparison;
14
22
  private equality;