@player-ui/player 0.3.0-next.2 → 0.3.0-next.4

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 (77) hide show
  1. package/dist/index.cjs.js +4128 -891
  2. package/dist/index.d.ts +1227 -50
  3. package/dist/index.esm.js +4065 -836
  4. package/package.json +9 -15
  5. package/src/binding/binding.ts +108 -0
  6. package/src/binding/index.ts +188 -0
  7. package/src/binding/resolver.ts +157 -0
  8. package/src/binding/utils.ts +51 -0
  9. package/src/binding-grammar/ast.ts +113 -0
  10. package/src/binding-grammar/custom/index.ts +304 -0
  11. package/src/binding-grammar/ebnf/binding.ebnf +22 -0
  12. package/src/binding-grammar/ebnf/index.ts +186 -0
  13. package/src/binding-grammar/ebnf/types.ts +104 -0
  14. package/src/binding-grammar/index.ts +4 -0
  15. package/src/binding-grammar/parsimmon/index.ts +78 -0
  16. package/src/controllers/constants/index.ts +85 -0
  17. package/src/controllers/constants/utils.ts +37 -0
  18. package/src/{data.ts → controllers/data.ts} +6 -6
  19. package/src/controllers/flow/controller.ts +95 -0
  20. package/src/controllers/flow/flow.ts +205 -0
  21. package/src/controllers/flow/index.ts +2 -0
  22. package/src/controllers/index.ts +5 -0
  23. package/src/{validation → controllers/validation}/binding-tracker.ts +5 -5
  24. package/src/{validation → controllers/validation}/controller.ts +15 -14
  25. package/src/{validation → controllers/validation}/index.ts +0 -0
  26. package/src/{view → controllers/view}/asset-transform.ts +2 -3
  27. package/src/{view → controllers/view}/controller.ts +9 -8
  28. package/src/controllers/view/index.ts +4 -0
  29. package/src/{view → controllers/view}/store.ts +0 -0
  30. package/src/{view → controllers/view}/types.ts +2 -1
  31. package/src/data/dependency-tracker.ts +187 -0
  32. package/src/data/index.ts +4 -0
  33. package/src/data/local-model.ts +41 -0
  34. package/src/data/model.ts +216 -0
  35. package/src/data/noop-model.ts +18 -0
  36. package/src/expressions/evaluator-functions.ts +29 -0
  37. package/src/expressions/evaluator.ts +405 -0
  38. package/src/expressions/index.ts +3 -0
  39. package/src/expressions/parser.ts +889 -0
  40. package/src/expressions/types.ts +200 -0
  41. package/src/expressions/utils.ts +8 -0
  42. package/src/index.ts +9 -12
  43. package/src/logger/consoleLogger.ts +49 -0
  44. package/src/logger/index.ts +5 -0
  45. package/src/logger/noopLogger.ts +13 -0
  46. package/src/logger/proxyLogger.ts +25 -0
  47. package/src/logger/tapableLogger.ts +38 -0
  48. package/src/logger/types.ts +6 -0
  49. package/src/player.ts +21 -18
  50. package/src/plugins/flow-exp-plugin.ts +2 -3
  51. package/src/schema/index.ts +2 -0
  52. package/src/schema/schema.ts +220 -0
  53. package/src/schema/types.ts +60 -0
  54. package/src/string-resolver/index.ts +188 -0
  55. package/src/types.ts +11 -13
  56. package/src/utils/index.ts +1 -0
  57. package/src/utils/replaceParams.ts +17 -0
  58. package/src/validator/index.ts +3 -0
  59. package/src/validator/registry.ts +20 -0
  60. package/src/validator/types.ts +75 -0
  61. package/src/validator/validation-middleware.ts +114 -0
  62. package/src/view/builder/index.ts +81 -0
  63. package/src/view/index.ts +5 -4
  64. package/src/view/parser/index.ts +318 -0
  65. package/src/view/parser/types.ts +141 -0
  66. package/src/view/plugins/applicability.ts +78 -0
  67. package/src/view/plugins/index.ts +5 -0
  68. package/src/view/plugins/options.ts +4 -0
  69. package/src/view/plugins/plugin.ts +21 -0
  70. package/src/view/plugins/string-resolver.ts +149 -0
  71. package/src/view/plugins/switch.ts +120 -0
  72. package/src/view/plugins/template-plugin.ts +172 -0
  73. package/src/view/resolver/index.ts +397 -0
  74. package/src/view/resolver/types.ts +161 -0
  75. package/src/view/resolver/utils.ts +57 -0
  76. package/src/view/view.ts +149 -0
  77. package/src/utils/desc.d.ts +0 -2
@@ -0,0 +1,405 @@
1
+ import { SyncWaterfallHook, SyncBailHook } from 'tapable-ts';
2
+ import parse from './parser';
3
+ import * as DEFAULT_EXPRESSION_HANDLERS from './evaluator-functions';
4
+ import type {
5
+ ExpressionNode,
6
+ BinaryOperator,
7
+ UnaryOperator,
8
+ ExpressionType,
9
+ ExpressionContext,
10
+ ExpressionHandler,
11
+ } from './types';
12
+ import { isExpressionNode } from '.';
13
+
14
+ /** a && b -- but handles short cutting if the first value is false */
15
+ const andandOperator: BinaryOperator = (ctx, a, b) => {
16
+ return ctx.evaluate(a) && ctx.evaluate(b);
17
+ };
18
+
19
+ andandOperator.resolveParams = false;
20
+
21
+ /** a || b -- but with short cutting if first value is true */
22
+ const ororOperator: BinaryOperator = (ctx, a, b) => {
23
+ return ctx.evaluate(a) || ctx.evaluate(b);
24
+ };
25
+
26
+ ororOperator.resolveParams = false;
27
+
28
+ const DEFAULT_BINARY_OPERATORS: Record<string, BinaryOperator> = {
29
+ // TODO: A lot of these functions used to do type coercion. Not sure if we want to keep that behavior or not.
30
+ '+': (a: any, b: any) => a + b,
31
+ '-': (a: any, b: any) => a - b,
32
+ '*': (a: any, b: any) => a * b,
33
+ '/': (a: any, b: any) => a / b,
34
+ '%': (a: any, b: any) => a % b,
35
+
36
+ // eslint-disable-next-line
37
+ '==': (a: any, b: any) => a == b,
38
+
39
+ // eslint-disable-next-line
40
+ '!=': (a: any, b: any) => a != b,
41
+ '>': (a: any, b: any) => a > b,
42
+ '>=': (a: any, b: any) => a >= b,
43
+ '<': (a: any, b: any) => a < b,
44
+ '<=': (a: any, b: any) => a <= b,
45
+ '&&': andandOperator,
46
+ '||': ororOperator,
47
+ '!==': (a: any, b: any) => a !== b,
48
+ '===': (a: any, b: any) => a === b,
49
+
50
+ // eslint-disable-next-line
51
+ '|': (a: any, b: any) => a | b,
52
+
53
+ // eslint-disable-next-line
54
+ '&': (a: any, b: any) => a & b,
55
+ '+=': (a: any, b: any) => a + b,
56
+ '-=': (a: any, b: any) => a - b,
57
+
58
+ // eslint-disable-next-line
59
+ '&=': (a: any, b: any) => a & b,
60
+
61
+ // eslint-disable-next-line
62
+ '|=': (a: any, b: any) => a | b,
63
+ };
64
+
65
+ const DEFAULT_UNARY_OPERATORS: Record<string, UnaryOperator> = {
66
+ '-': (a: any) => -a,
67
+ '+': (a: any) => Number(a),
68
+ '!': (a: any) => !a,
69
+ };
70
+
71
+ export interface HookOptions extends ExpressionContext {
72
+ /** Given an expression node */
73
+ resolveNode: (node: ExpressionNode) => any;
74
+ }
75
+
76
+ export type ExpressionEvaluatorOptions = Omit<
77
+ HookOptions,
78
+ 'resolveNode' | 'evaluate'
79
+ >;
80
+
81
+ export type ExpressionEvaluatorFunction = (
82
+ exp: ExpressionType,
83
+ options?: ExpressionEvaluatorOptions
84
+ ) => any;
85
+
86
+ /**
87
+ * The expression evaluator is responsible for parsing and executing anything in the custom expression language
88
+ * */
89
+ export class ExpressionEvaluator {
90
+ private readonly vars: Record<string, any> = {};
91
+ public readonly hooks = {
92
+ /** Resolve an AST node for an expression to a value */
93
+ resolve: new SyncWaterfallHook<[any, ExpressionNode, HookOptions]>(),
94
+
95
+ /**
96
+ * An optional means of handling an error in the expression execution
97
+ * Return true if handled, to stop propagation of the error
98
+ */
99
+ onError: new SyncBailHook<[Error], true>(),
100
+ };
101
+
102
+ private readonly expressionsCache: Map<string, ExpressionNode> = new Map();
103
+
104
+ private readonly defaultHookOptions: HookOptions;
105
+
106
+ public readonly operators = {
107
+ binary: new Map(Object.entries(DEFAULT_BINARY_OPERATORS)),
108
+ unary: new Map(Object.entries(DEFAULT_UNARY_OPERATORS)),
109
+ expressions: new Map<string, ExpressionHandler<any, any>>(
110
+ Object.entries(DEFAULT_EXPRESSION_HANDLERS)
111
+ ),
112
+ };
113
+
114
+ public reset(): void {
115
+ this.expressionsCache.clear();
116
+ }
117
+
118
+ constructor(defaultOptions: ExpressionEvaluatorOptions) {
119
+ this.defaultHookOptions = {
120
+ ...defaultOptions,
121
+ evaluate: (expr) => this.evaluate(expr, this.defaultHookOptions),
122
+ resolveNode: (node: ExpressionNode) =>
123
+ this._execAST(node, this.defaultHookOptions),
124
+ };
125
+
126
+ this.hooks.resolve.tap('ExpressionEvaluator', this._resolveNode.bind(this));
127
+ this.evaluate = this.evaluate.bind(this);
128
+ }
129
+
130
+ public evaluate(
131
+ expression: ExpressionType,
132
+ options?: ExpressionEvaluatorOptions
133
+ ): any {
134
+ const opts = {
135
+ ...this.defaultHookOptions,
136
+ ...options,
137
+ resolveNode: (node: ExpressionNode) => this._execAST(node, opts),
138
+ };
139
+
140
+ // Check for literals
141
+ if (
142
+ typeof expression === 'number' ||
143
+ typeof expression === 'boolean' ||
144
+ expression === undefined ||
145
+ expression === null
146
+ ) {
147
+ return expression;
148
+ }
149
+
150
+ // Skip doing anything with objects that are _actually_ just parsed expression nodes
151
+ if (isExpressionNode(expression)) {
152
+ return this._execAST(expression, opts);
153
+ }
154
+
155
+ if (typeof expression === 'object') {
156
+ const values = Array.isArray(expression)
157
+ ? expression
158
+ : Object.values(expression);
159
+
160
+ return values.reduce(
161
+ (_nothing, exp) => this.evaluate(exp, options),
162
+ null
163
+ );
164
+ }
165
+
166
+ return this._execString(String(expression), opts);
167
+ }
168
+
169
+ public addExpressionFunction<T extends readonly unknown[], R>(
170
+ name: string,
171
+ handler: ExpressionHandler<T, R>
172
+ ): void {
173
+ this.operators.expressions.set(name, handler);
174
+ }
175
+
176
+ public addBinaryOperator(operator: string, handler: BinaryOperator) {
177
+ this.operators.binary.set(operator, handler);
178
+ }
179
+
180
+ public addUnaryOperator(operator: string, handler: UnaryOperator) {
181
+ this.operators.unary.set(operator, handler);
182
+ }
183
+
184
+ public setExpressionVariable(name: string, value: unknown) {
185
+ this.vars[name] = value;
186
+ }
187
+
188
+ public getExpressionVariable(name: string): unknown {
189
+ return this.vars[name];
190
+ }
191
+
192
+ private _execAST(node: ExpressionNode, options: HookOptions): any {
193
+ return this.hooks.resolve.call(undefined, node, options);
194
+ }
195
+
196
+ private _execString(exp: string, options: HookOptions) {
197
+ if (exp === '') {
198
+ return exp;
199
+ }
200
+
201
+ const matches = exp.match(/^@\[(.*)\]@$/);
202
+ let matchedExp = exp;
203
+
204
+ if (matches) {
205
+ [, matchedExp] = Array.from(matches); // In case the expression was surrounded by @[ ]@
206
+ }
207
+
208
+ try {
209
+ const storedAST = this.expressionsCache.get(matchedExp);
210
+
211
+ if (storedAST) {
212
+ return this._execAST(storedAST, options);
213
+ }
214
+
215
+ const expAST = parse(matchedExp);
216
+ this.expressionsCache.set(matchedExp, expAST);
217
+
218
+ return this._execAST(expAST, options);
219
+ } catch (e: any) {
220
+ if (!this.hooks.onError.call(e)) {
221
+ // Only throw the error if it's not handled by the hook
222
+ throw e;
223
+ }
224
+ }
225
+ }
226
+
227
+ private _resolveNode(
228
+ _currentValue: any,
229
+ node: ExpressionNode,
230
+ options: HookOptions
231
+ ) {
232
+ const { resolveNode, model } = options;
233
+
234
+ const expressionContext: ExpressionContext = {
235
+ ...options,
236
+ evaluate: (expr) => this.evaluate(expr, options),
237
+ };
238
+
239
+ if (node.type === 'Literal') {
240
+ return node.value;
241
+ }
242
+
243
+ if (node.type === 'Identifier') {
244
+ return this.vars[node.name];
245
+ }
246
+
247
+ if (node.type === 'Compound' || node.type === 'ThisExpression') {
248
+ throw new Error(`Expression type: ${node.type} is not supported`);
249
+ }
250
+
251
+ if (node.type === 'BinaryExpression' || node.type === 'LogicalExpression') {
252
+ const operator = this.operators.binary.get(node.operator);
253
+
254
+ if (operator) {
255
+ if ('resolveParams' in operator) {
256
+ if (operator.resolveParams === false) {
257
+ return operator(expressionContext, node.left, node.right);
258
+ }
259
+
260
+ return operator(
261
+ expressionContext,
262
+ resolveNode(node.left),
263
+ resolveNode(node.right)
264
+ );
265
+ }
266
+
267
+ return operator(resolveNode(node.left), resolveNode(node.right));
268
+ }
269
+
270
+ return;
271
+ }
272
+
273
+ if (node.type === 'UnaryExpression') {
274
+ const operator = this.operators.unary.get(node.operator);
275
+
276
+ if (operator) {
277
+ if ('resolveParams' in operator) {
278
+ return operator(
279
+ expressionContext,
280
+ operator.resolveParams === false
281
+ ? node.argument
282
+ : resolveNode(node.argument)
283
+ );
284
+ }
285
+
286
+ return operator(resolveNode(node.argument));
287
+ }
288
+
289
+ return;
290
+ }
291
+
292
+ if (node.type === 'Object') {
293
+ const { attributes } = node;
294
+ const resolvedAttributes: any = {};
295
+
296
+ attributes.forEach((attr) => {
297
+ const key = resolveNode(attr.key);
298
+ const value = resolveNode(attr.value);
299
+ resolvedAttributes[key] = value;
300
+ });
301
+
302
+ return resolvedAttributes;
303
+ }
304
+
305
+ if (node.type === 'CallExpression') {
306
+ const expressionName = node.callTarget.name;
307
+
308
+ // Treat the conditional operator as special.
309
+ // Don't exec the arguments that don't apply
310
+ if (expressionName === 'conditional') {
311
+ const condition = resolveNode(node.args[0]);
312
+
313
+ if (condition) {
314
+ return resolveNode(node.args[1]);
315
+ }
316
+
317
+ if (node.args[2]) {
318
+ return resolveNode(node.args[2]);
319
+ }
320
+
321
+ return null;
322
+ }
323
+
324
+ const operator = this.operators.expressions.get(expressionName);
325
+
326
+ if (!operator) {
327
+ throw new Error(`Unknown expression function: ${expressionName}`);
328
+ }
329
+
330
+ const args = node.args.map((n) => resolveNode(n));
331
+
332
+ return operator(expressionContext, ...args);
333
+ }
334
+
335
+ if (node.type === 'ModelRef') {
336
+ return model.get(node.ref);
337
+ }
338
+
339
+ if (node.type === 'MemberExpression') {
340
+ const obj = resolveNode(node.object);
341
+ const prop = resolveNode(node.property);
342
+
343
+ return obj[prop];
344
+ }
345
+
346
+ if (node.type === 'Assignment') {
347
+ if (node.left.type === 'ModelRef') {
348
+ const value = resolveNode(node.right);
349
+ model.set([[node.left.ref, value]]);
350
+
351
+ return value;
352
+ }
353
+
354
+ if (node.left.type === 'Identifier') {
355
+ const value = resolveNode(node.right);
356
+ this.vars[node.left.name] = value;
357
+ return value;
358
+ }
359
+
360
+ return;
361
+ }
362
+
363
+ if (node.type === 'ConditionalExpression') {
364
+ const result = resolveNode(node.test) ? node.consequent : node.alternate;
365
+
366
+ return resolveNode(result);
367
+ }
368
+
369
+ if (node.type === 'ArrayExpression') {
370
+ return node.elements.map((ele) => resolveNode(ele));
371
+ }
372
+
373
+ if (node.type === 'Modification') {
374
+ const operation = this.operators.binary.get(node.operator);
375
+
376
+ if (operation) {
377
+ let newValue;
378
+
379
+ if ('resolveParams' in operation) {
380
+ if (operation.resolveParams === false) {
381
+ newValue = operation(expressionContext, node.left, node.right);
382
+ } else {
383
+ newValue = operation(
384
+ expressionContext,
385
+ resolveNode(node.left),
386
+ resolveNode(node.right)
387
+ );
388
+ }
389
+ } else {
390
+ newValue = operation(resolveNode(node.left), resolveNode(node.right));
391
+ }
392
+
393
+ if (node.left.type === 'ModelRef') {
394
+ model.set([[node.left.ref, newValue]]);
395
+ } else if (node.left.type === 'Identifier') {
396
+ this.vars[node.left.name] = newValue;
397
+ }
398
+
399
+ return newValue;
400
+ }
401
+
402
+ return resolveNode(node.left);
403
+ }
404
+ }
405
+ }
@@ -0,0 +1,3 @@
1
+ export * from './evaluator';
2
+ export * from './types';
3
+ export * from './utils';