@dronedeploy/rocos-js-sdk 3.0.1-alpha.20 → 3.0.1-alpha.21

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 (101) hide show
  1. package/cjs/helpers/index.d.ts +1 -0
  2. package/cjs/helpers/index.js +1 -0
  3. package/cjs/helpers/kscript/Context.d.ts +34 -0
  4. package/cjs/helpers/kscript/Context.js +52 -0
  5. package/cjs/helpers/kscript/index.d.ts +1 -0
  6. package/cjs/helpers/kscript/index.js +27 -0
  7. package/cjs/helpers/kscript/kscript.d.ts +6 -0
  8. package/cjs/helpers/kscript/kscript.js +115 -0
  9. package/cjs/helpers/kscript/nodes/Identifier.d.ts +12 -0
  10. package/cjs/helpers/kscript/nodes/Identifier.js +20 -0
  11. package/cjs/helpers/kscript/nodes/Literal.d.ts +11 -0
  12. package/cjs/helpers/kscript/nodes/Literal.js +22 -0
  13. package/cjs/helpers/kscript/nodes/Node.d.ts +9 -0
  14. package/cjs/helpers/kscript/nodes/Node.js +15 -0
  15. package/cjs/helpers/kscript/nodes/Program.d.ts +11 -0
  16. package/cjs/helpers/kscript/nodes/Program.js +24 -0
  17. package/cjs/helpers/kscript/nodes/TemplateLiteral.d.ts +23 -0
  18. package/cjs/helpers/kscript/nodes/TemplateLiteral.js +35 -0
  19. package/cjs/helpers/kscript/nodes/expressions/ArrayExpression.d.ts +7 -0
  20. package/cjs/helpers/kscript/nodes/expressions/ArrayExpression.js +31 -0
  21. package/cjs/helpers/kscript/nodes/expressions/ArrowFunctionExpression.d.ts +14 -0
  22. package/cjs/helpers/kscript/nodes/expressions/ArrowFunctionExpression.js +40 -0
  23. package/cjs/helpers/kscript/nodes/expressions/BinaryExpression.d.ts +16 -0
  24. package/cjs/helpers/kscript/nodes/expressions/BinaryExpression.js +104 -0
  25. package/cjs/helpers/kscript/nodes/expressions/CallExpression.d.ts +8 -0
  26. package/cjs/helpers/kscript/nodes/expressions/CallExpression.js +38 -0
  27. package/cjs/helpers/kscript/nodes/expressions/ChainExpression.d.ts +16 -0
  28. package/cjs/helpers/kscript/nodes/expressions/ChainExpression.js +25 -0
  29. package/cjs/helpers/kscript/nodes/expressions/ConditionalExpression.d.ts +14 -0
  30. package/cjs/helpers/kscript/nodes/expressions/ConditionalExpression.js +30 -0
  31. package/cjs/helpers/kscript/nodes/expressions/ExpressionStatement.d.ts +7 -0
  32. package/cjs/helpers/kscript/nodes/expressions/ExpressionStatement.js +18 -0
  33. package/cjs/helpers/kscript/nodes/expressions/LogicalExpression.d.ts +10 -0
  34. package/cjs/helpers/kscript/nodes/expressions/LogicalExpression.js +47 -0
  35. package/cjs/helpers/kscript/nodes/expressions/MemberExpression.d.ts +44 -0
  36. package/cjs/helpers/kscript/nodes/expressions/MemberExpression.js +98 -0
  37. package/cjs/helpers/kscript/nodes/expressions/ObjectExpression.d.ts +11 -0
  38. package/cjs/helpers/kscript/nodes/expressions/ObjectExpression.js +59 -0
  39. package/cjs/helpers/kscript/nodes/expressions/UnaryExpression.d.ts +12 -0
  40. package/cjs/helpers/kscript/nodes/expressions/UnaryExpression.js +44 -0
  41. package/cjs/helpers/kscript/nodes/expressions/index.d.ts +11 -0
  42. package/cjs/helpers/kscript/nodes/expressions/index.js +28 -0
  43. package/cjs/helpers/kscript/nodes/index.d.ts +4 -0
  44. package/cjs/helpers/kscript/nodes/index.js +10 -0
  45. package/cjs/helpers/kscript/nodes/nodeTypes.d.ts +33 -0
  46. package/cjs/helpers/kscript/nodes/nodeTypes.js +29 -0
  47. package/cjs/helpers/kscript/utils.d.ts +4 -0
  48. package/cjs/helpers/kscript/utils.js +15 -0
  49. package/cjs/services/AuthService.d.ts +3 -3
  50. package/cjs/services/AuthService.js +7 -6
  51. package/esm/helpers/index.d.ts +1 -0
  52. package/esm/helpers/index.js +1 -0
  53. package/esm/helpers/kscript/Context.d.ts +34 -0
  54. package/esm/helpers/kscript/Context.js +49 -0
  55. package/esm/helpers/kscript/index.d.ts +1 -0
  56. package/esm/helpers/kscript/index.js +1 -0
  57. package/esm/helpers/kscript/kscript.d.ts +6 -0
  58. package/esm/helpers/kscript/kscript.js +107 -0
  59. package/esm/helpers/kscript/nodes/Identifier.d.ts +12 -0
  60. package/esm/helpers/kscript/nodes/Identifier.js +14 -0
  61. package/esm/helpers/kscript/nodes/Literal.d.ts +11 -0
  62. package/esm/helpers/kscript/nodes/Literal.js +16 -0
  63. package/esm/helpers/kscript/nodes/Node.d.ts +9 -0
  64. package/esm/helpers/kscript/nodes/Node.js +12 -0
  65. package/esm/helpers/kscript/nodes/Program.d.ts +11 -0
  66. package/esm/helpers/kscript/nodes/Program.js +18 -0
  67. package/esm/helpers/kscript/nodes/TemplateLiteral.d.ts +23 -0
  68. package/esm/helpers/kscript/nodes/TemplateLiteral.js +29 -0
  69. package/esm/helpers/kscript/nodes/expressions/ArrayExpression.d.ts +7 -0
  70. package/esm/helpers/kscript/nodes/expressions/ArrayExpression.js +25 -0
  71. package/esm/helpers/kscript/nodes/expressions/ArrowFunctionExpression.d.ts +14 -0
  72. package/esm/helpers/kscript/nodes/expressions/ArrowFunctionExpression.js +34 -0
  73. package/esm/helpers/kscript/nodes/expressions/BinaryExpression.d.ts +16 -0
  74. package/esm/helpers/kscript/nodes/expressions/BinaryExpression.js +98 -0
  75. package/esm/helpers/kscript/nodes/expressions/CallExpression.d.ts +8 -0
  76. package/esm/helpers/kscript/nodes/expressions/CallExpression.js +32 -0
  77. package/esm/helpers/kscript/nodes/expressions/ChainExpression.d.ts +16 -0
  78. package/esm/helpers/kscript/nodes/expressions/ChainExpression.js +19 -0
  79. package/esm/helpers/kscript/nodes/expressions/ConditionalExpression.d.ts +14 -0
  80. package/esm/helpers/kscript/nodes/expressions/ConditionalExpression.js +24 -0
  81. package/esm/helpers/kscript/nodes/expressions/ExpressionStatement.d.ts +7 -0
  82. package/esm/helpers/kscript/nodes/expressions/ExpressionStatement.js +12 -0
  83. package/esm/helpers/kscript/nodes/expressions/LogicalExpression.d.ts +10 -0
  84. package/esm/helpers/kscript/nodes/expressions/LogicalExpression.js +41 -0
  85. package/esm/helpers/kscript/nodes/expressions/MemberExpression.d.ts +44 -0
  86. package/esm/helpers/kscript/nodes/expressions/MemberExpression.js +92 -0
  87. package/esm/helpers/kscript/nodes/expressions/ObjectExpression.d.ts +11 -0
  88. package/esm/helpers/kscript/nodes/expressions/ObjectExpression.js +53 -0
  89. package/esm/helpers/kscript/nodes/expressions/UnaryExpression.d.ts +12 -0
  90. package/esm/helpers/kscript/nodes/expressions/UnaryExpression.js +38 -0
  91. package/esm/helpers/kscript/nodes/expressions/index.d.ts +11 -0
  92. package/esm/helpers/kscript/nodes/expressions/index.js +11 -0
  93. package/esm/helpers/kscript/nodes/index.d.ts +4 -0
  94. package/esm/helpers/kscript/nodes/index.js +3 -0
  95. package/esm/helpers/kscript/nodes/nodeTypes.d.ts +33 -0
  96. package/esm/helpers/kscript/nodes/nodeTypes.js +24 -0
  97. package/esm/helpers/kscript/utils.d.ts +4 -0
  98. package/esm/helpers/kscript/utils.js +11 -0
  99. package/esm/services/AuthService.d.ts +3 -3
  100. package/esm/services/AuthService.js +7 -6
  101. package/package.json +2 -1
@@ -1,5 +1,5 @@
1
- import { IBaseService, IRocosSDKConfig, RocosError, Token } from '../models';
2
1
  import { Observable } from 'rxjs';
2
+ import { IBaseService, IRocosSDKConfig, RocosError, Token } from '../models';
3
3
  import { BaseServiceAbstract } from './BaseServiceAbstract';
4
4
  export declare class AuthService extends BaseServiceAbstract implements IBaseService {
5
5
  protected config: IRocosSDKConfig;
@@ -12,9 +12,9 @@ export declare class AuthService extends BaseServiceAbstract implements IBaseSer
12
12
  * This is useful for getting notified when the token changes.
13
13
  * i.e. when the token is refreshed from the token refresh checker.
14
14
  *
15
- * This will not emit until the token is set, either by `setToken` or by getting a new token.
15
+ * It will emit the current token when you subscribe and then emit new tokens when they are set.
16
16
  */
17
- get tokenUpdates$(): Observable<Token>;
17
+ get token$(): Observable<Token>;
18
18
  getStatus(): boolean;
19
19
  teardown(): void;
20
20
  protected getError(e: RocosError): RocosError;
@@ -2,24 +2,25 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.AuthService = void 0;
4
4
  const api_1 = require("../constants/api");
5
- const models_1 = require("../models");
6
5
  const rxjs_1 = require("rxjs");
6
+ const models_1 = require("../models");
7
7
  const BaseServiceAbstract_1 = require("./BaseServiceAbstract");
8
8
  const RocosLogger_1 = require("../logger/RocosLogger");
9
9
  const RocosStore_1 = require("../store/RocosStore");
10
+ const operators_1 = require("rxjs/operators");
10
11
  const formatServiceUrl_1 = require("../helpers/formatServiceUrl");
11
12
  const auth_1 = require("../constants/auth");
12
13
  class AuthService extends BaseServiceAbstract_1.BaseServiceAbstract {
13
14
  constructor(config) {
14
15
  super(config);
15
16
  this.config = config;
16
- this.tokenSubject$ = new rxjs_1.Subject();
17
+ this.tokenSubject$ = new rxjs_1.BehaviorSubject(null);
17
18
  this.config = config;
18
19
  this.logger = RocosLogger_1.RocosLogger.getInstance(`AuthService(${this.config.url})`);
19
20
  if (this.config.token) {
20
21
  this.setToken(this.config.token);
21
22
  }
22
- this.tokenUpdates$.subscribe((token) => {
23
+ this.token$.subscribe((token) => {
23
24
  RocosStore_1.RocosStore.getChangeSubject().next({ type: 'token', url: this.config.url, data: token.value });
24
25
  });
25
26
  }
@@ -28,10 +29,10 @@ class AuthService extends BaseServiceAbstract_1.BaseServiceAbstract {
28
29
  * This is useful for getting notified when the token changes.
29
30
  * i.e. when the token is refreshed from the token refresh checker.
30
31
  *
31
- * This will not emit until the token is set, either by `setToken` or by getting a new token.
32
+ * It will emit the current token when you subscribe and then emit new tokens when they are set.
32
33
  */
33
- get tokenUpdates$() {
34
- return this.tokenSubject$.asObservable();
34
+ get token$() {
35
+ return this.tokenSubject$.asObservable().pipe((0, operators_1.filter)(Boolean));
35
36
  }
36
37
  getStatus() {
37
38
  return true;
@@ -3,3 +3,4 @@ export * from './standardDeviation';
3
3
  export * from './generateUUID';
4
4
  export * from './getURLSearchParams';
5
5
  export * from './websandbox';
6
+ export * from './kscript';
@@ -3,3 +3,4 @@ export * from './standardDeviation';
3
3
  export * from './generateUUID';
4
4
  export * from './getURLSearchParams';
5
5
  export * from './websandbox';
6
+ export * from './kscript';
@@ -0,0 +1,34 @@
1
+ import { AnyNode } from 'acorn';
2
+ import type { NodeType } from './nodes';
3
+ /**
4
+ * A map of node types to a set of property names that are allowed to be accessed
5
+ * on the prototype of the object.
6
+ *
7
+ * If the set is null, all properties are allowed.
8
+ */
9
+ export type PrototypeWhitelist = Map<unknown, Set<string> | null>;
10
+ export type Scope = Map<string, unknown> | Record<string, unknown>;
11
+ export interface ContextOptions {
12
+ nodeBlacklist?: Set<NodeType>;
13
+ prototypeWhitelist?: PrototypeWhitelist;
14
+ functionCallsAllowed?: boolean;
15
+ }
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;
23
+ /** Returns true if the property is allowed to be accessed on the prototype.
24
+ *
25
+ * - If the prototype is not in the whitelist, the property is allowed only if it is an own property.
26
+ * - If the prototype is in the whitelist, the property is allowed if it is in the set or the set is null.
27
+ *
28
+ * @param prototype The prototype which contains the property being accessed.
29
+ * @param property The name of the property being accessed.
30
+ * @param ownProperty Whether the property is owned by the object it is being accessed on.
31
+ */
32
+ isPrototypeAllowed(prototype: unknown, property: string, ownProperty: boolean): boolean;
33
+ private static buildMap;
34
+ }
@@ -0,0 +1,49 @@
1
+ export default class Context {
2
+ constructor(scope, options) {
3
+ this.scope = scope ? Context.buildMap(scope) : new Map();
4
+ this.options = {
5
+ nodeBlacklist: options?.nodeBlacklist ?? new Set(),
6
+ prototypeWhitelist: options?.prototypeWhitelist ?? new Map(),
7
+ functionCallsAllowed: options?.functionCallsAllowed ?? true,
8
+ };
9
+ this.isFunctionCallAllowed = this.options.functionCallsAllowed;
10
+ }
11
+ isNodeAllowed(nodeType) {
12
+ return !this.options.nodeBlacklist.has(nodeType);
13
+ }
14
+ newChildContext(scope) {
15
+ let newEntries;
16
+ if (scope instanceof Map) {
17
+ newEntries = [...scope.entries()];
18
+ }
19
+ else {
20
+ newEntries = Object.entries(scope);
21
+ }
22
+ const newScope = new Map([...this.scope.entries(), ...newEntries]);
23
+ return new Context(newScope, this.options);
24
+ }
25
+ /** Returns true if the property is allowed to be accessed on the prototype.
26
+ *
27
+ * - If the prototype is not in the whitelist, the property is allowed only if it is an own property.
28
+ * - If the prototype is in the whitelist, the property is allowed if it is in the set or the set is null.
29
+ *
30
+ * @param prototype The prototype which contains the property being accessed.
31
+ * @param property The name of the property being accessed.
32
+ * @param ownProperty Whether the property is owned by the object it is being accessed on.
33
+ */
34
+ isPrototypeAllowed(prototype, property, ownProperty) {
35
+ const whitelist = this.options.prototypeWhitelist.get(prototype);
36
+ // If the object owns the property, and it's not in the whitelist, it's allowed.
37
+ // This prevents direct calls to non-whitelisted properties on allowed prototypes
38
+ // e.g. Object.prototype.hasOwnProperty.call(obj, 'toString')
39
+ if (whitelist === undefined)
40
+ return ownProperty;
41
+ // all properties allowed if no set is provided
42
+ if (whitelist === null)
43
+ return true;
44
+ return whitelist.has(property);
45
+ }
46
+ static buildMap(obj) {
47
+ return obj instanceof Map ? obj : new Map(Object.entries(obj));
48
+ }
49
+ }
@@ -0,0 +1 @@
1
+ export * as kscript from './kscript';
@@ -0,0 +1 @@
1
+ export * as kscript from './kscript';
@@ -0,0 +1,6 @@
1
+ import { ContextOptions, PrototypeWhitelist, Scope } from './Context';
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;
5
+ export declare const SAFE_GLOBALS: Map<string, unknown>;
6
+ export declare const SAFE_PROTOTYPES: PrototypeWhitelist;
@@ -0,0 +1,107 @@
1
+ import Context from './Context';
2
+ import { parse } from 'acorn';
3
+ import { construct } from './utils';
4
+ export function execute(code, scope, options) {
5
+ return compile(code, scope, options).run();
6
+ }
7
+ export function compile(code, scope, options) {
8
+ let program;
9
+ try {
10
+ // we add the brackets to ensure that object literals are parsed correctly
11
+ program = parse(`(${code})`, { ecmaVersion: 2020, sourceType: 'script' });
12
+ }
13
+ catch (error) {
14
+ if (error instanceof SyntaxError) {
15
+ // fallback to non-wrapped code for edge cases where the brackets cause issues
16
+ program = parse(`${code}`, { ecmaVersion: 2020, sourceType: 'script' });
17
+ }
18
+ else {
19
+ throw error;
20
+ }
21
+ }
22
+ const context = new Context(scope ?? SAFE_GLOBALS, {
23
+ prototypeWhitelist: SAFE_PROTOTYPES,
24
+ ...options,
25
+ });
26
+ return construct(program, context);
27
+ }
28
+ export const SAFE_GLOBALS = new Map(Object.entries({
29
+ console: {
30
+ debug: console.debug,
31
+ error: console.error,
32
+ info: console.info,
33
+ log: console.log,
34
+ warn: console.warn,
35
+ },
36
+ isFinite,
37
+ isNaN,
38
+ Number,
39
+ Object,
40
+ Array,
41
+ Symbol,
42
+ Error,
43
+ JSON,
44
+ Math,
45
+ Date,
46
+ RegExp,
47
+ }));
48
+ export const SAFE_PROTOTYPES = new Map([
49
+ [Number.prototype, new Set(['toString', 'toFixed', 'toExponential', 'toPrecision'])],
50
+ [
51
+ String.prototype,
52
+ new Set([
53
+ 'toString',
54
+ 'charAt',
55
+ 'charCodeAt',
56
+ 'concat',
57
+ 'indexOf',
58
+ 'lastIndexOf',
59
+ 'localeCompare',
60
+ 'match',
61
+ 'replace',
62
+ 'search',
63
+ 'slice',
64
+ 'split',
65
+ 'substring',
66
+ 'toLowerCase',
67
+ 'toUpperCase',
68
+ 'trim',
69
+ 'valueOf',
70
+ ]),
71
+ ],
72
+ [
73
+ Array.prototype,
74
+ new Set([
75
+ 'filter',
76
+ 'map',
77
+ 'find',
78
+ 'findIndex',
79
+ 'forEach',
80
+ 'every',
81
+ 'some',
82
+ 'reduce',
83
+ 'reduceRight',
84
+ 'sort',
85
+ 'slice',
86
+ 'splice',
87
+ 'concat',
88
+ 'indexOf',
89
+ 'lastIndexOf',
90
+ 'includes',
91
+ 'join',
92
+ 'reverse',
93
+ 'shift',
94
+ 'unshift',
95
+ 'pop',
96
+ 'push',
97
+ 'copyWithin',
98
+ 'fill',
99
+ 'entries',
100
+ 'keys',
101
+ 'values',
102
+ 'flat',
103
+ 'flatMap',
104
+ ]),
105
+ ],
106
+ [Object.prototype, new Set(['toString'])],
107
+ ]);
@@ -0,0 +1,12 @@
1
+ import { AnyNode, Identifier } from 'acorn';
2
+ import type Context from '../Context';
3
+ import Node from './Node';
4
+ /** An Identifier is a reference to a variable.
5
+ *
6
+ * Variables must be declared in the current scope.
7
+ * If the variable is not found, undefined is returned.
8
+ */
9
+ export default class IdentifierNode extends Node<Identifier> {
10
+ constructor(node: AnyNode, context: Context);
11
+ run(): unknown;
12
+ }
@@ -0,0 +1,14 @@
1
+ import Node from './Node';
2
+ /** An Identifier is a reference to a variable.
3
+ *
4
+ * Variables must be declared in the current scope.
5
+ * If the variable is not found, undefined is returned.
6
+ */
7
+ export default class IdentifierNode extends Node {
8
+ constructor(node, context) {
9
+ super(node, context, 'Identifier');
10
+ }
11
+ run() {
12
+ return this.scope.get(this.node.name);
13
+ }
14
+ }
@@ -0,0 +1,11 @@
1
+ import { AnyNode, Literal } from 'acorn';
2
+ import Context from '../Context';
3
+ import Node from './Node';
4
+ /** Represents a literal value
5
+ *
6
+ * e.g. `1`, `'hello'`, `true`
7
+ */
8
+ export default class LiteralNode extends Node<Literal> {
9
+ constructor(node: AnyNode, context: Context);
10
+ run(): unknown;
11
+ }
@@ -0,0 +1,16 @@
1
+ import Node from './Node';
2
+ /** Represents a literal value
3
+ *
4
+ * e.g. `1`, `'hello'`, `true`
5
+ */
6
+ export default class LiteralNode extends Node {
7
+ constructor(node, context) {
8
+ super(node, context, 'Literal');
9
+ }
10
+ run() {
11
+ if (this.node.bigint) {
12
+ throw new Error('BigInts are not supported');
13
+ }
14
+ return this.node.value;
15
+ }
16
+ }
@@ -0,0 +1,9 @@
1
+ import { AnyNode } from 'acorn';
2
+ import type Context from '../Context';
3
+ export default abstract class Node<T extends AnyNode = AnyNode> {
4
+ protected context: Context;
5
+ protected node: T;
6
+ protected scope: Context['scope'];
7
+ protected constructor(node: AnyNode, context: Context, expectedType: T['type'] | T['type'][]);
8
+ abstract run(): unknown;
9
+ }
@@ -0,0 +1,12 @@
1
+ export default class Node {
2
+ constructor(node, context, expectedType) {
3
+ this.context = context;
4
+ const expectedTypes = Array.isArray(expectedType) ? expectedType : [expectedType];
5
+ if (!expectedTypes.includes(node.type)) {
6
+ console.error(node);
7
+ throw new Error(`Expected node type to be ${expectedTypes.join(' or ')}, but got ${node.type}`);
8
+ }
9
+ this.node = node;
10
+ this.scope = context.scope;
11
+ }
12
+ }
@@ -0,0 +1,11 @@
1
+ import { AnyNode, Program } from 'acorn';
2
+ import Context from '../Context';
3
+ import Node from './Node';
4
+ /** Represents the root node of the program
5
+ *
6
+ * Since the sandbox only supports a single statement, this node will only ever run the first child.
7
+ */
8
+ export default class ProgramNode extends Node<Program> {
9
+ constructor(node: AnyNode, scope: Context);
10
+ run(): unknown;
11
+ }
@@ -0,0 +1,18 @@
1
+ import Node from './Node';
2
+ import { construct } from '../utils';
3
+ /** Represents the root node of the program
4
+ *
5
+ * Since the sandbox only supports a single statement, this node will only ever run the first child.
6
+ */
7
+ export default class ProgramNode extends Node {
8
+ constructor(node, scope) {
9
+ super(node, scope, 'Program');
10
+ }
11
+ run() {
12
+ const children = this.node.body;
13
+ if (children.length !== 1) {
14
+ throw new Error('Only a single statement is supported in the sandbox');
15
+ }
16
+ return construct(children[0], this.context).run();
17
+ }
18
+ }
@@ -0,0 +1,23 @@
1
+ import { AnyNode, TemplateLiteral } from 'acorn';
2
+ import Context from '../Context';
3
+ import Node from './Node';
4
+ /**
5
+ * A template literal is a string literal that allows embedded expressions.
6
+ *
7
+ * e.g. `Hello ${name}`
8
+ *
9
+ * The `quasis` property is an array of strings that are the static parts of the template literal.
10
+ * The `expressions` property is an array of expressions that are the dynamic parts of the template literal.
11
+ *
12
+ * The template literal is constructed by merging the static parts with the results of evaluating the dynamic parts.
13
+ *
14
+ * i.e. `Hello ${name}. My name is ${name}.`
15
+ * Would result in three quasi values: `Hello `, `. My name is ` and `.`
16
+ * And two expressions: `Identifier<name>` and `Identifier<name>`
17
+ *
18
+ * There is *always* one more quasi value than there are expressions, even if the last quasi is an empty string.
19
+ */
20
+ export default class TemplateLiteralNode extends Node<TemplateLiteral> {
21
+ constructor(node: AnyNode, scope: Context);
22
+ run(): unknown;
23
+ }
@@ -0,0 +1,29 @@
1
+ import Node from './Node';
2
+ import { construct } from '../utils';
3
+ /**
4
+ * A template literal is a string literal that allows embedded expressions.
5
+ *
6
+ * e.g. `Hello ${name}`
7
+ *
8
+ * The `quasis` property is an array of strings that are the static parts of the template literal.
9
+ * The `expressions` property is an array of expressions that are the dynamic parts of the template literal.
10
+ *
11
+ * The template literal is constructed by merging the static parts with the results of evaluating the dynamic parts.
12
+ *
13
+ * i.e. `Hello ${name}. My name is ${name}.`
14
+ * Would result in three quasi values: `Hello `, `. My name is ` and `.`
15
+ * And two expressions: `Identifier<name>` and `Identifier<name>`
16
+ *
17
+ * There is *always* one more quasi value than there are expressions, even if the last quasi is an empty string.
18
+ */
19
+ export default class TemplateLiteralNode extends Node {
20
+ constructor(node, scope) {
21
+ super(node, scope, 'TemplateLiteral');
22
+ }
23
+ run() {
24
+ return this.node.quasis.reduce((acc, quasi, i) => {
25
+ const expression = this.node.expressions[i] ? construct(this.node.expressions[i], this.context).run() : '';
26
+ return acc + quasi.value.raw + expression;
27
+ }, '');
28
+ }
29
+ }
@@ -0,0 +1,7 @@
1
+ import { AnyNode, ArrayExpression } from 'acorn';
2
+ import type Context from '../../Context';
3
+ import Node from '../Node';
4
+ export default class ArrayExpressionNode extends Node<ArrayExpression> {
5
+ constructor(node: AnyNode, context: Context);
6
+ run(): unknown;
7
+ }
@@ -0,0 +1,25 @@
1
+ import Node from '../Node';
2
+ import { construct } from '../../utils';
3
+ export default class ArrayExpressionNode extends Node {
4
+ constructor(node, context) {
5
+ super(node, context, 'ArrayExpression');
6
+ }
7
+ run() {
8
+ const array = [];
9
+ for (const element of this.node.elements) {
10
+ if (element === null) {
11
+ // null implies an empty slot in the array (e.g. [1, , 3])
12
+ // an actual null value would be a literal node.
13
+ array.push(null);
14
+ delete array[array.length - 1];
15
+ }
16
+ else if (element.type === 'SpreadElement') {
17
+ array.push(...construct(element.argument, this.context).run());
18
+ }
19
+ else {
20
+ array.push(construct(element, this.context).run());
21
+ }
22
+ }
23
+ return array;
24
+ }
25
+ }
@@ -0,0 +1,14 @@
1
+ import { AnyNode, ArrowFunctionExpression } from 'acorn';
2
+ import type Context from '../../Context';
3
+ import Node from '../Node';
4
+ /** Represents an arrow function expression.
5
+ *
6
+ * e.g. `(a, b) => a + b`
7
+ *
8
+ * In the sandbox this is the only way to define a function.
9
+ */
10
+ export default class ArrowFunctionExpressionNode extends Node<ArrowFunctionExpression> {
11
+ private readonly params;
12
+ constructor(node: AnyNode, context: Context);
13
+ run(): unknown;
14
+ }
@@ -0,0 +1,34 @@
1
+ import Node from '../Node';
2
+ import { construct } from '../../utils';
3
+ /** Represents an arrow function expression.
4
+ *
5
+ * e.g. `(a, b) => a + b`
6
+ *
7
+ * In the sandbox this is the only way to define a function.
8
+ */
9
+ export default class ArrowFunctionExpressionNode extends Node {
10
+ constructor(node, context) {
11
+ super(node, context, 'ArrowFunctionExpression');
12
+ if (this.node.params.some((param) => param.type !== 'Identifier')) {
13
+ throw new Error('only identifiers are supported as parameters');
14
+ }
15
+ this.params = this.node.params;
16
+ }
17
+ run() {
18
+ if (!this.context.isFunctionCallAllowed)
19
+ throw new Error('functions are not allowed');
20
+ if (!this.node.expression)
21
+ throw new Error('functions must be an expression');
22
+ if (this.node.async)
23
+ throw new Error('async functions are not supported');
24
+ return (...args) => {
25
+ const scope = new Map();
26
+ for (const param of this.params) {
27
+ const index = this.params.indexOf(param);
28
+ scope.set(param.name, args?.[index]);
29
+ }
30
+ const newContext = this.context.newChildContext(scope);
31
+ return construct(this.node.body, newContext).run();
32
+ };
33
+ }
34
+ }
@@ -0,0 +1,16 @@
1
+ import { AnyNode, BinaryExpression } from 'acorn';
2
+ import Context from '../../Context';
3
+ import Node from '../Node';
4
+ /** A binary expression is an expression that consists of two operands and an operator
5
+ *
6
+ * e.g. `1 + 2`, `a === b`, `c < d`
7
+ */
8
+ export default class BinaryExpressionNode extends Node<BinaryExpression> {
9
+ constructor(node: AnyNode, context: Context);
10
+ run(): unknown;
11
+ private operate;
12
+ private arithmetic;
13
+ private comparison;
14
+ private equality;
15
+ private checkIn;
16
+ }
@@ -0,0 +1,98 @@
1
+ import Node from '../Node';
2
+ import { construct } from '../../utils';
3
+ /** A binary expression is an expression that consists of two operands and an operator
4
+ *
5
+ * e.g. `1 + 2`, `a === b`, `c < d`
6
+ */
7
+ export default class BinaryExpressionNode extends Node {
8
+ constructor(node, context) {
9
+ super(node, context, 'BinaryExpression');
10
+ }
11
+ run() {
12
+ const left = construct(this.node.left, this.context).run();
13
+ const right = construct(this.node.right, this.context).run();
14
+ return this.operate(left, right);
15
+ }
16
+ operate(left, right) {
17
+ switch (this.node.operator) {
18
+ case '==':
19
+ case '!=':
20
+ case '===':
21
+ case '!==':
22
+ return this.equality(this.node.operator, left, right);
23
+ case '<':
24
+ case '<=':
25
+ case '>':
26
+ case '>=':
27
+ return this.comparison(this.node.operator, left, right);
28
+ case 'in':
29
+ return this.checkIn(left, right);
30
+ case '**':
31
+ case '+':
32
+ case '-':
33
+ case '*':
34
+ case '/':
35
+ case '%':
36
+ return this.arithmetic(this.node.operator, left, right);
37
+ default:
38
+ throw new Error(`Unsupported operator: ${this.node.operator}`);
39
+ }
40
+ }
41
+ arithmetic(operator, left, right) {
42
+ if (operator === '+' && (typeof left === 'string' || typeof right === 'string')) {
43
+ return `${left}${right}`;
44
+ }
45
+ if (typeof left !== 'number' || typeof right !== 'number') {
46
+ throw new Error('Arithmetic operators are only supported for numbers');
47
+ }
48
+ switch (operator) {
49
+ case '+':
50
+ return left + right;
51
+ case '-':
52
+ return left - right;
53
+ case '*':
54
+ return left * right;
55
+ case '/':
56
+ return left / right;
57
+ case '%':
58
+ return left % right;
59
+ case '**':
60
+ return left ** right;
61
+ }
62
+ }
63
+ comparison(operator, left, right) {
64
+ if (typeof left !== 'number' || typeof right !== 'number') {
65
+ throw new Error('Comparison operators are only supported for numbers');
66
+ }
67
+ switch (operator) {
68
+ case '<':
69
+ return left < right;
70
+ case '<=':
71
+ return left <= right;
72
+ case '>':
73
+ return left > right;
74
+ case '>=':
75
+ return left >= right;
76
+ }
77
+ }
78
+ equality(operator, left, right) {
79
+ switch (operator) {
80
+ case '==':
81
+ // eslint-disable-next-line eqeqeq
82
+ return left == right;
83
+ case '!=':
84
+ // eslint-disable-next-line eqeqeq
85
+ return left != right;
86
+ case '===':
87
+ return left === right;
88
+ case '!==':
89
+ return left !== right;
90
+ }
91
+ }
92
+ checkIn(left, right) {
93
+ if (!Array.isArray(right)) {
94
+ throw new Error('The "in" operator is only supported for arrays');
95
+ }
96
+ return right.includes(left);
97
+ }
98
+ }
@@ -0,0 +1,8 @@
1
+ import { AnyNode, CallExpression } from 'acorn';
2
+ import Context from '../../Context';
3
+ import Node from '../Node';
4
+ export default class CallExpressionNode extends Node<CallExpression> {
5
+ constructor(node: AnyNode, context: Context);
6
+ run(): unknown;
7
+ private spreadArgs;
8
+ }