@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,187 @@
1
+ import type { BindingInstance } from '../binding';
2
+ import type {
3
+ BatchSetTransaction,
4
+ DataModelImpl,
5
+ DataModelMiddleware,
6
+ DataModelOptions,
7
+ Updates,
8
+ } from './model';
9
+
10
+ export type DependencySets = 'core' | 'children';
11
+
12
+ /** A class to track usage of read/writes to/from a data model */
13
+ export class DependencyTracker {
14
+ protected readDeps: Set<BindingInstance>;
15
+ protected writeDeps: Set<BindingInstance>;
16
+ protected namedSet: DependencySets;
17
+
18
+ private namedDependencySets: Partial<
19
+ Record<
20
+ DependencySets,
21
+ {
22
+ /** readDeps */
23
+ readDeps: Set<BindingInstance>;
24
+ /** writeDeps */
25
+ writeDeps: Set<BindingInstance>;
26
+ }
27
+ >
28
+ >;
29
+
30
+ constructor() {
31
+ this.readDeps = new Set();
32
+ this.writeDeps = new Set();
33
+ this.namedDependencySets = {};
34
+ this.namedSet = 'core';
35
+
36
+ this.createSubset('core');
37
+ this.createSubset('children');
38
+ }
39
+
40
+ protected createSubset(name: DependencySets, force = false): void {
41
+ if (force || !this.namedDependencySets[name]) {
42
+ this.namedDependencySets[name] = {
43
+ readDeps: new Set(),
44
+ writeDeps: new Set(),
45
+ };
46
+ }
47
+ }
48
+
49
+ /** Grab all of the bindings that this depended on */
50
+ public getDependencies(name?: DependencySets): Set<BindingInstance> {
51
+ if (name !== undefined) {
52
+ return this.namedDependencySets?.[name]?.readDeps ?? new Set();
53
+ }
54
+
55
+ return this.readDeps;
56
+ }
57
+
58
+ public trackSubset(name: DependencySets) {
59
+ this.createSubset(name);
60
+ this.namedSet = name;
61
+ }
62
+
63
+ public trackDefault() {
64
+ this.namedSet = 'core';
65
+ }
66
+
67
+ /** Grab all of the bindings this wrote to */
68
+ public getModified(name?: DependencySets): Set<BindingInstance> {
69
+ if (name !== undefined) {
70
+ return this.namedDependencySets?.[name]?.writeDeps ?? new Set();
71
+ }
72
+
73
+ return this.writeDeps;
74
+ }
75
+
76
+ /**
77
+ * Check to see if the dataModel has read the value at the given binding
78
+ *
79
+ * @param binding - The binding you want to check for
80
+ */
81
+ public readsBinding(binding: BindingInstance): boolean {
82
+ return this.readDeps.has(binding);
83
+ }
84
+
85
+ /**
86
+ * Check to see if the dataModel has written to the binding
87
+ */
88
+ public writesBinding(binding: BindingInstance): boolean {
89
+ return this.writeDeps.has(binding);
90
+ }
91
+
92
+ /** Reset all tracking of dependencies */
93
+ public reset() {
94
+ this.readDeps = new Set();
95
+ this.writeDeps = new Set();
96
+ this.namedDependencySets = {};
97
+ this.namedSet = 'core';
98
+
99
+ this.createSubset('core', true);
100
+ this.createSubset('children', true);
101
+ }
102
+
103
+ protected addReadDep(
104
+ binding: BindingInstance,
105
+ namedSet = this.namedSet
106
+ ): void {
107
+ if (namedSet) {
108
+ this.namedDependencySets?.[namedSet]?.readDeps.add(binding);
109
+ }
110
+
111
+ this.readDeps.add(binding);
112
+ }
113
+
114
+ protected addWriteDep(
115
+ binding: BindingInstance,
116
+ namedSet = this.namedSet
117
+ ): void {
118
+ if (namedSet) {
119
+ this.namedDependencySets?.[namedSet]?.writeDeps.add(binding);
120
+ }
121
+
122
+ this.writeDeps.add(binding);
123
+ }
124
+
125
+ public addChildReadDep(binding: BindingInstance): void {
126
+ this.addReadDep(binding, 'children');
127
+ }
128
+ }
129
+
130
+ /** Middleware that tracks dependencies of read/written data */
131
+ export class DependencyMiddleware
132
+ extends DependencyTracker
133
+ implements DataModelMiddleware
134
+ {
135
+ constructor() {
136
+ super();
137
+ this.get = this.get.bind(this);
138
+ this.set = this.set.bind(this);
139
+ }
140
+
141
+ public set(
142
+ transaction: BatchSetTransaction,
143
+ options?: DataModelOptions,
144
+ next?: DataModelImpl | undefined
145
+ ): Updates {
146
+ transaction.forEach(([binding]) => this.addWriteDep(binding));
147
+
148
+ return next?.set(transaction, options) ?? [];
149
+ }
150
+
151
+ public get(
152
+ binding: BindingInstance,
153
+ options?: DataModelOptions,
154
+ next?: DataModelImpl | undefined
155
+ ) {
156
+ this.addReadDep(binding);
157
+
158
+ return next?.get(binding, options);
159
+ }
160
+ }
161
+
162
+ /** A data-model that tracks dependencies of read/written data */
163
+ export class DependencyModel<Options = DataModelOptions>
164
+ extends DependencyTracker
165
+ implements DataModelImpl<Options>
166
+ {
167
+ private readonly rootModel: DataModelImpl<Options>;
168
+
169
+ constructor(rootModel: DataModelImpl<Options>) {
170
+ super();
171
+ this.rootModel = rootModel;
172
+ this.set = this.set.bind(this);
173
+ this.get = this.get.bind(this);
174
+ }
175
+
176
+ public set(transaction: BatchSetTransaction, options?: Options): Updates {
177
+ transaction.forEach(([binding]) => this.addWriteDep(binding));
178
+
179
+ return this.rootModel.set(transaction, options);
180
+ }
181
+
182
+ public get(binding: BindingInstance, options?: Options) {
183
+ this.addReadDep(binding);
184
+
185
+ return this.rootModel.get(binding, options);
186
+ }
187
+ }
@@ -0,0 +1,4 @@
1
+ export * from './dependency-tracker';
2
+ export * from './model';
3
+ export * from './noop-model';
4
+ export * from './local-model';
@@ -0,0 +1,41 @@
1
+ import get from 'dlv';
2
+ import { setIn } from 'timm';
3
+ import type { BindingInstance } from '../binding';
4
+ import type { BatchSetTransaction, DataModelImpl, Updates } from './model';
5
+
6
+ /**
7
+ * A data model that stores data in an in-memory JS object
8
+ */
9
+ export class LocalModel implements DataModelImpl {
10
+ public model: {
11
+ [key: string]: any;
12
+ };
13
+
14
+ constructor(model = {}) {
15
+ this.model = model;
16
+ this.get = this.get.bind(this);
17
+ this.set = this.set.bind(this);
18
+ }
19
+
20
+ public reset(model = {}) {
21
+ this.model = model;
22
+ }
23
+
24
+ public get(binding?: BindingInstance) {
25
+ if (!binding || !binding.asString()) {
26
+ return this.model;
27
+ }
28
+
29
+ return get(this.model, binding.asArray() as string[]);
30
+ }
31
+
32
+ public set(transaction: BatchSetTransaction) {
33
+ const effectiveOperations: Updates = [];
34
+ transaction.forEach(([binding, value]) => {
35
+ const oldValue = this.get(binding);
36
+ this.model = setIn(this.model, binding.asArray(), value) as any;
37
+ effectiveOperations.push({ binding, oldValue, newValue: value });
38
+ });
39
+ return effectiveOperations;
40
+ }
41
+ }
@@ -0,0 +1,216 @@
1
+ import { SyncHook } from 'tapable-ts';
2
+ import type { BindingLike, BindingFactory } from '../binding';
3
+ import { BindingInstance, isBinding } from '../binding';
4
+ import { NOOP_MODEL } from './noop-model';
5
+
6
+ export const ROOT_BINDING = new BindingInstance([]);
7
+ export type BatchSetTransaction = [BindingInstance, any][];
8
+
9
+ export type Updates = Array<{
10
+ /** The updated binding */
11
+ binding: BindingInstance;
12
+
13
+ /** The old value */
14
+ oldValue: any;
15
+
16
+ /** The new value */
17
+ newValue: any;
18
+
19
+ /** Force the Update to be included even if no data changed */
20
+ force?: boolean;
21
+ }>;
22
+
23
+ /** Options to use when getting or setting data */
24
+ export interface DataModelOptions {
25
+ /**
26
+ * The data (either to set or get) should represent a formatted value
27
+ * For setting data, the data will be de-formatted before continuing in the pipeline
28
+ * For getting data, the data will be formatted before returning
29
+ */
30
+ formatted?: boolean;
31
+
32
+ /**
33
+ * By default, fetching data will ignore any invalid data.
34
+ * You can choose to grab the queued invalid data if you'd like
35
+ * This is usually the case for user-inputs
36
+ */
37
+ includeInvalid?: boolean;
38
+
39
+ /**
40
+ * A flag to set to ignore any default value in the schema, and just use the raw value
41
+ */
42
+ ignoreDefaultValue?: boolean;
43
+
44
+ /** Other context associated with this request */
45
+ context?: {
46
+ /** The data model to use when getting other data from the context of this request */
47
+ model: DataModelWithParser;
48
+ };
49
+ }
50
+
51
+ export interface DataModelWithParser<Options = DataModelOptions> {
52
+ get(binding: BindingLike, options?: Options): any;
53
+ set(transaction: [BindingLike, any][], options?: Options): Updates;
54
+ }
55
+
56
+ export interface DataModelImpl<Options = DataModelOptions> {
57
+ get(binding: BindingInstance, options?: Options): any;
58
+ set(transaction: BatchSetTransaction, options?: Options): Updates;
59
+ }
60
+
61
+ export interface DataModelMiddleware {
62
+ /** The name of the middleware */
63
+ name?: string;
64
+
65
+ set(
66
+ transaction: BatchSetTransaction,
67
+ options?: DataModelOptions,
68
+ next?: DataModelImpl
69
+ ): Updates;
70
+ get(
71
+ binding: BindingInstance,
72
+ options?: DataModelOptions,
73
+ next?: DataModelImpl
74
+ ): any;
75
+ reset?(): void;
76
+ }
77
+
78
+ /** Wrap the inputs of the DataModel with calls to parse raw binding inputs */
79
+ export function withParser<Options = unknown>(
80
+ model: DataModelImpl<Options>,
81
+ parseBinding: BindingFactory
82
+ ): DataModelWithParser<Options> {
83
+ /** Parse something into a binding if it requires it */
84
+ function maybeParse(binding: BindingLike): BindingInstance {
85
+ const parsed = isBinding(binding)
86
+ ? binding
87
+ : parseBinding(binding, {
88
+ get: model.get,
89
+ set: model.set,
90
+ });
91
+
92
+ if (!parsed) {
93
+ throw new Error('Unable to parse binding');
94
+ }
95
+
96
+ return parsed;
97
+ }
98
+
99
+ return {
100
+ get(binding, options?: Options) {
101
+ return model.get(maybeParse(binding), options);
102
+ },
103
+ set(transaction, options?: Options) {
104
+ return model.set(
105
+ transaction.map(([key, val]) => [maybeParse(key), val]),
106
+ options
107
+ );
108
+ },
109
+ };
110
+ }
111
+
112
+ /** Wrap a middleware instance in a DataModel compliant API */
113
+ export function toModel(
114
+ middleware: DataModelMiddleware,
115
+ defaultOptions?: DataModelOptions,
116
+ next?: DataModelImpl
117
+ ): DataModelImpl {
118
+ if (!next) {
119
+ return middleware as DataModelImpl;
120
+ }
121
+
122
+ return {
123
+ get: (binding: BindingInstance, options?: DataModelOptions) =>
124
+ middleware.get(binding, options ?? defaultOptions, next),
125
+ set: (transaction: BatchSetTransaction, options?: DataModelOptions) =>
126
+ middleware.set(transaction, options ?? defaultOptions, next),
127
+ };
128
+ }
129
+
130
+ export type DataPipeline = Array<DataModelMiddleware | DataModelImpl>;
131
+
132
+ /**
133
+ * Given a set of steps in a pipeline, create the effective data-model
134
+ */
135
+ export function constructModelForPipeline(
136
+ pipeline: DataPipeline
137
+ ): DataModelImpl {
138
+ if (pipeline.length === 0) {
139
+ return NOOP_MODEL;
140
+ }
141
+
142
+ if (pipeline.length === 1) {
143
+ return pipeline[0];
144
+ }
145
+
146
+ /** Default and propagate the options into the nested calls */
147
+ function createModelWithOptions(options?: DataModelOptions) {
148
+ const model: DataModelImpl =
149
+ pipeline.reduce<DataModelImpl | undefined>(
150
+ (nextModel, middleware) => toModel(middleware, options, nextModel),
151
+ undefined
152
+ ) ?? NOOP_MODEL;
153
+
154
+ return model;
155
+ }
156
+
157
+ return {
158
+ get: (binding: BindingInstance, options?: DataModelOptions) => {
159
+ return createModelWithOptions(options)?.get(binding, options);
160
+ },
161
+ set: (transaction, options) => {
162
+ return createModelWithOptions(options)?.set(transaction, options);
163
+ },
164
+ };
165
+ }
166
+
167
+ /** A DataModel that manages middleware data handlers */
168
+ export class PipelinedDataModel implements DataModelImpl {
169
+ private pipeline: DataPipeline;
170
+ private effectiveDataModel: DataModelImpl;
171
+
172
+ public readonly hooks = {
173
+ onSet: new SyncHook<[BatchSetTransaction]>(),
174
+ };
175
+
176
+ constructor(pipeline: DataPipeline = []) {
177
+ this.pipeline = pipeline;
178
+ this.effectiveDataModel = constructModelForPipeline(this.pipeline);
179
+ }
180
+
181
+ public setMiddleware(handlers: DataPipeline) {
182
+ this.pipeline = handlers;
183
+ this.effectiveDataModel = constructModelForPipeline(handlers);
184
+ }
185
+
186
+ public addMiddleware(handler: DataModelMiddleware) {
187
+ this.pipeline = [...this.pipeline, handler];
188
+ this.effectiveDataModel = constructModelForPipeline(this.pipeline);
189
+ }
190
+
191
+ public reset(model = {}) {
192
+ this.pipeline.forEach((middleware) => {
193
+ if ('reset' in middleware) {
194
+ middleware.reset?.();
195
+ }
196
+ });
197
+
198
+ this.set([[ROOT_BINDING, model]]);
199
+ }
200
+
201
+ public set(
202
+ transaction: BatchSetTransaction,
203
+ options?: DataModelOptions
204
+ ): Updates {
205
+ const appliedTransaction = this.effectiveDataModel.set(
206
+ transaction,
207
+ options
208
+ );
209
+ this.hooks.onSet.call(transaction);
210
+ return appliedTransaction;
211
+ }
212
+
213
+ public get(binding: BindingInstance, options?: DataModelOptions): any {
214
+ return this.effectiveDataModel.get(binding, options);
215
+ }
216
+ }
@@ -0,0 +1,18 @@
1
+ import type { DataModelImpl } from './model';
2
+
3
+ /**
4
+ * A model that does nothing
5
+ * Helpful for testing and other default DataModel applications
6
+ */
7
+ export class NOOPDataModel implements DataModelImpl {
8
+ get() {
9
+ return undefined;
10
+ }
11
+
12
+ set() {
13
+ return [];
14
+ }
15
+ }
16
+
17
+ /** You only really need 1 instance of the NOOP model */
18
+ export const NOOP_MODEL = new NOOPDataModel();
@@ -0,0 +1,29 @@
1
+ import type { Binding } from '@player-ui/types';
2
+
3
+ import type { BindingLike } from '../binding';
4
+ import type { ExpressionHandler, ExpressionContext } from './types';
5
+
6
+ /** Sets a value to the data-model */
7
+ export const setDataVal: ExpressionHandler<[Binding, any], any> = (
8
+ _context: ExpressionContext,
9
+ binding,
10
+ value
11
+ ) => {
12
+ _context.model.set([[binding as BindingLike, value]]);
13
+ };
14
+
15
+ /** Fetches a valid from the data-model */
16
+ export const getDataVal: ExpressionHandler<[Binding], unknown> = (
17
+ _context: ExpressionContext,
18
+ binding
19
+ ) => {
20
+ return _context.model.get(binding as BindingLike);
21
+ };
22
+
23
+ /** Deletes a value from the model */
24
+ export const deleteDataVal: ExpressionHandler<[Binding], void> = (
25
+ _context: ExpressionContext,
26
+ binding
27
+ ) => {
28
+ return _context.model.set([[binding as BindingLike, undefined]]);
29
+ };