@emuanalytics/flow-rdf-core 4.0.4 → 4.0.5

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 (89) hide show
  1. package/README.md +1 -64
  2. package/dist/index.d.mts +36 -31
  3. package/dist/index.d.ts +36 -31
  4. package/dist/index.mjs +20 -3249
  5. package/dist/lib/context/context-registry.mjs +63 -0
  6. package/dist/lib/context/debugger.mjs +240 -0
  7. package/dist/lib/context/flow-context.mjs +61 -0
  8. package/dist/lib/context/global-state.mjs +70 -0
  9. package/dist/lib/context/map-registry.mjs +59 -0
  10. package/dist/lib/context/query-registry.mjs +53 -0
  11. package/dist/lib/errors/flow-error.mjs +10 -0
  12. package/dist/lib/errors/flow-expression-error.mjs +14 -0
  13. package/dist/lib/errors/flow-map-error.mjs +9 -0
  14. package/dist/lib/expressions/expression-parser.mjs +198 -0
  15. package/dist/lib/expressions/functions/from-palette.mjs +75 -0
  16. package/dist/lib/expressions/functions/regex.mjs +25 -0
  17. package/dist/lib/expressions/functions/state.mjs +25 -0
  18. package/dist/lib/expressions/parser-helpers.mjs +37 -0
  19. package/dist/lib/expressions/reactive-expression.mjs +253 -0
  20. package/dist/lib/map/flow-core-map.mjs +749 -0
  21. package/dist/lib/map/plugins/annotations/annotation-layers.mjs +163 -0
  22. package/dist/lib/map/plugins/annotations/annotation.events.mjs +27 -0
  23. package/dist/lib/map/plugins/annotations/annotation.plugin.mjs +498 -0
  24. package/dist/lib/map/plugins/annotations/maplibre-gl-draw-circle/lib/modes/DirectModeOverride.mjs +67 -0
  25. package/dist/lib/map/plugins/annotations/maplibre-gl-draw-circle/lib/modes/DragCircleMode.mjs +65 -0
  26. package/dist/lib/map/plugins/annotations/maplibre-gl-draw-circle/lib/utils/create_supplementary_points_circle.mjs +15 -0
  27. package/dist/lib/map/plugins/annotations/maplibre-gl-draw-circle/lib/utils/drag_pan.mjs +20 -0
  28. package/dist/lib/map/plugins/data-join.plugin.mjs +173 -0
  29. package/dist/lib/map/plugins/export/export.plugin.mjs +21 -0
  30. package/dist/lib/map/plugins/export/map-exporter.mjs +177 -0
  31. package/dist/lib/map/plugins/hover-effect.plugin.mjs +58 -0
  32. package/dist/lib/map/plugins/index.d.mts +37 -72
  33. package/dist/lib/map/plugins/index.d.ts +37 -72
  34. package/dist/lib/map/plugins/index.mjs +11 -1810
  35. package/dist/lib/map/plugins/keyboard-shortcut-manager.mjs +120 -0
  36. package/dist/lib/map/plugins/map-plugin-base.mjs +25 -0
  37. package/dist/lib/map/plugins/plugin-manager.mjs +135 -0
  38. package/dist/lib/map/plugins/popup.plugin.mjs +55 -0
  39. package/dist/lib/map/plugins/selection/selection.events.mjs +21 -0
  40. package/dist/lib/map/plugins/selection/selection.plugin.mjs +136 -0
  41. package/dist/lib/map/plugins/selection/source-selection.mjs +156 -0
  42. package/dist/lib/map/plugins/url-sync.plugin.mjs +55 -0
  43. package/dist/lib/map/support/image-registry.mjs +82 -0
  44. package/dist/lib/map/support/map-events.mjs +71 -0
  45. package/dist/lib/map/support/map-feature-stream.mjs +158 -0
  46. package/dist/lib/map/support/map-popup.mjs +159 -0
  47. package/dist/lib/map/types.mjs +7 -0
  48. package/dist/lib/map-style/map-data-source.mjs +192 -0
  49. package/dist/lib/map-style/map-layer-style-builder-generated.mjs +1307 -0
  50. package/dist/lib/map-style/map-layer-style-builder.mjs +86 -0
  51. package/dist/lib/map-style/map-layer.mjs +223 -0
  52. package/dist/lib/map-style/map-style.mjs +178 -0
  53. package/dist/lib/queries/async-committer.mjs +30 -0
  54. package/dist/lib/queries/query.mjs +172 -0
  55. package/dist/lib/state-store/decorators.mjs +83 -0
  56. package/dist/lib/state-store/state-store.mjs +534 -0
  57. package/dist/lib/state-store/utils.mjs +7 -0
  58. package/dist/lib/utils/announce.mjs +12 -0
  59. package/dist/lib/utils/assert.mjs +12 -0
  60. package/dist/{shared/flow-rdf-core.187cb345.mjs → lib/utils/debounce.mjs} +3 -39
  61. package/dist/lib/utils/decorators.mjs +59 -0
  62. package/dist/lib/utils/deferred.mjs +11 -0
  63. package/dist/lib/utils/ensure-array.mjs +16 -0
  64. package/dist/{shared/flow-rdf-core.1f0901c5.mjs → lib/utils/feature-type-checks.mjs} +2 -2
  65. package/dist/lib/utils/functional.mjs +22 -0
  66. package/dist/lib/utils/global.mjs +18 -0
  67. package/dist/lib/utils/index.mjs +8 -5
  68. package/dist/lib/utils/random-id.mjs +9 -0
  69. package/dist/lib/utils/string-utils.mjs +3 -0
  70. package/dist/lib/utils/throttle.mjs +20 -0
  71. package/dist/{shared/flow-rdf-core.25e5f6d7.mjs → lib/utils/type-checks.mjs} +1 -26
  72. package/dist/lib/utils/watchable-promise.mjs +55 -0
  73. package/dist/package.json.mjs +3 -0
  74. package/dist/shared/{flow-rdf-core.4dde996e.d.cts → flow-rdf-core.8b5acf14.d.mts} +1315 -172
  75. package/dist/shared/{flow-rdf-core.4dde996e.d.ts → flow-rdf-core.8b5acf14.d.ts} +1315 -172
  76. package/package.json +4 -8
  77. package/dist/index.cjs +0 -3316
  78. package/dist/index.d.cts +0 -90
  79. package/dist/lib/map/plugins/index.cjs +0 -1850
  80. package/dist/lib/map/plugins/index.d.cts +0 -345
  81. package/dist/lib/utils/index.cjs +0 -48
  82. package/dist/lib/utils/index.d.cts +0 -160
  83. package/dist/shared/flow-rdf-core.06e85a6c.cjs +0 -1394
  84. package/dist/shared/flow-rdf-core.4dde996e.d.mts +0 -1916
  85. package/dist/shared/flow-rdf-core.55c169ca.cjs +0 -150
  86. package/dist/shared/flow-rdf-core.63162669.d.cts +0 -10
  87. package/dist/shared/flow-rdf-core.65045caf.mjs +0 -1360
  88. package/dist/shared/flow-rdf-core.a82b9be4.cjs +0 -34
  89. package/dist/shared/flow-rdf-core.f50908f0.cjs +0 -102
@@ -0,0 +1,198 @@
1
+ import P from 'parsimmon';
2
+ import { PREFIX, BINARY_RIGHT, BINARY_LEFT, TERNARY_RIGHT } from './parser-helpers.mjs';
3
+
4
+ var __defProp = Object.defineProperty;
5
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
6
+ var __publicField = (obj, key, value) => {
7
+ __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
8
+ return value;
9
+ };
10
+ class ExpressionParser {
11
+ constructor() {
12
+ __publicField(this, "language");
13
+ this.language = this.createLanguage();
14
+ }
15
+ /**
16
+ * Parse an expression
17
+ *
18
+ * @param expr Expression to parse
19
+ */
20
+ parseExpression(expr) {
21
+ return this.language.Expr.parse(expr);
22
+ }
23
+ /**
24
+ * Parse a complete assignment program of the form:
25
+ * a: expr1;
26
+ * b: expr2;
27
+ *
28
+ * @param program Program to parse
29
+ */
30
+ parseProgram(program) {
31
+ return this.language.ProgramOrEmpty.parse(program);
32
+ }
33
+ createLanguage() {
34
+ const operatorTable = [
35
+ { type: PREFIX, ops: this.operators({ "-": "-", "!": "!" }) },
36
+ { type: BINARY_RIGHT, ops: this.operators({ "^": "**" }) },
37
+ { type: BINARY_LEFT, ops: this.operators({ "*": "*", "/": "/", "%": "%" }) },
38
+ { type: BINARY_LEFT, ops: this.operators({ "+": "+", "-": "-" }) },
39
+ { type: BINARY_LEFT, ops: this.operators({ ">=": ">=", "<=": "<=" }) },
40
+ { type: BINARY_LEFT, ops: this.operators({ "<": "<", ">": ">" }) },
41
+ {
42
+ type: BINARY_LEFT,
43
+ ops: this.operators({ regexi: "~*", equali: "=*", "==": "==", "!=": "!=", "~": "~" })
44
+ },
45
+ { type: BINARY_LEFT, ops: this.operators({ all: "&&" }) },
46
+ { type: BINARY_LEFT, ops: this.operators({ any: "||" }) },
47
+ { type: TERNARY_RIGHT, ops: this.operators({ case: "?" }) }
48
+ ];
49
+ return P.createLanguage({
50
+ Whitespace: () => P.regexp(/[ \n]*/).desc("whitespace"),
51
+ NotNewline: () => P.regexp(/[^\n]*/).desc("non-newline"),
52
+ LineComment: (r) => r.NotNewline.wrap(P.string("//"), P.alt(P.string("\n"), P.eof)),
53
+ BlockComment: () => P.regexp(/\/\*[\s\S]*?\*\//),
54
+ Comment: (r) => P.alt(r.LineComment, r.BlockComment).desc("comment"),
55
+ End: () => P.alt(P.string(";"), P.eof),
56
+ _: (r) => r.Comment.sepBy(r.Whitespace).trim(r.Whitespace),
57
+ Integer: () => {
58
+ return P.regexp(/-?[0-9]+/).map(Number);
59
+ },
60
+ Decimal: () => {
61
+ return P.regexp(/-?[0-9]+\.[0-9]+/).map(Number);
62
+ },
63
+ Number: (r) => {
64
+ return P.alt(r.Decimal, r.Integer).desc("number");
65
+ },
66
+ Boolean: () => {
67
+ return P.alt(P.string("true"), P.string("false")).map((val) => val === "true").desc("boolean");
68
+ },
69
+ Null: () => {
70
+ return P.string("null").result(null).desc("null");
71
+ },
72
+ DoubleQuoteString: () => {
73
+ return P.regexp(/"((?:\\.|.)*?)"/, 1).desc("string");
74
+ },
75
+ SingleQuoteString: () => {
76
+ return P.regexp(/'((?:\\.|.)*?)'/, 1).desc("string");
77
+ },
78
+ UnquotedString: () => {
79
+ return P.regexp(/[a-zA-Z_][a-zA-Z0-9_-]*/).desc("string");
80
+ },
81
+ HexColor: () => {
82
+ return P.regexp(/#(?:[0-9a-f]{3}){1,2}/i).desc("hex color");
83
+ },
84
+ String: (r) => {
85
+ return P.alt(r.SingleQuoteString, r.DoubleQuoteString, r.HexColor, r.UnquotedString).map(
86
+ (s) => s.replace(/\\n/gm, "\n")
87
+ );
88
+ },
89
+ Symbol: () => {
90
+ return P.regexp(/[a-zA-Z_][a-zA-Z0-9_-]*/).desc("symbol");
91
+ },
92
+ PartialSymbol: () => {
93
+ return P.regexp(/[a-zA-Z0-9_-]+/).desc("symbol");
94
+ },
95
+ Feature: (r) => {
96
+ return P.seqMap(
97
+ P.string("@"),
98
+ r.PartialSymbol.sepBy(P.string(".")),
99
+ (_feat, symbols) => ["get"].concat(symbols)
100
+ );
101
+ },
102
+ FeatureState: (r) => {
103
+ return P.seqMap(
104
+ P.string("@@"),
105
+ r.PartialSymbol.sepBy(P.string(".")),
106
+ (_feat, symbols) => this.expandFeatureState(symbols)
107
+ );
108
+ },
109
+ Array: (r) => P.string("[").trim(r._).then(r.Value.trim(r._).sepBy(P.string(","))).skip(P.string("]")),
110
+ State: (r) => {
111
+ return P.seqMap(
112
+ P.string("$"),
113
+ r.PartialSymbol.sepBy(P.string(".")),
114
+ (_state, symbols) => ["state"].concat(symbols)
115
+ );
116
+ },
117
+ Paren: (r) => {
118
+ return P.seqMap(P.string("("), r.Expr.trim(r._), P.string(")"), (_lb, expr, _rb) => expr);
119
+ },
120
+ Func: (r) => {
121
+ return P.seqMap(
122
+ r.Symbol.trim(r._),
123
+ P.string("("),
124
+ r.Expr.trim(r._).sepBy(P.string(",")),
125
+ P.string(")"),
126
+ (func, _lb, args, _rb) => [func, ...args]
127
+ );
128
+ },
129
+ Value: (r) => {
130
+ return P.alt(
131
+ r.Paren,
132
+ r.Array,
133
+ r.Func,
134
+ r.Boolean,
135
+ r.Null,
136
+ r.Number,
137
+ r.String,
138
+ r.FeatureState,
139
+ r.Feature,
140
+ r.State
141
+ );
142
+ },
143
+ Expr: (r) => {
144
+ return this.tableParser(operatorTable, r.Value);
145
+ },
146
+ PropertyName: () => {
147
+ return P.regexp(/[a-zA-Z_-][a-zA-Z0-9_-]*/).desc("symbol");
148
+ },
149
+ Assignment: (r) => {
150
+ return P.seqMap(
151
+ r.PropertyName,
152
+ P.string(":").trim(r._),
153
+ r.Expr.trim(r._).node("expr"),
154
+ r.End,
155
+ (key, _colon, node) => [key, { expr: node.value, start: node.start, end: node.end }]
156
+ );
157
+ },
158
+ Program: (r) => {
159
+ return r.Assignment.trim(r._).many().map((statements) => {
160
+ return statements.filter((s) => s !== "").reduce((acc, s) => {
161
+ acc[s[0]] = s[1];
162
+ return acc;
163
+ }, {});
164
+ });
165
+ },
166
+ EmptyProgram: (r) => {
167
+ return P.seqMap(r._, P.eof, () => null);
168
+ },
169
+ ProgramOrEmpty: (r) => {
170
+ return P.alt(r.EmptyProgram, r.Program);
171
+ }
172
+ });
173
+ }
174
+ /**
175
+ * Helper function to create a parser that parsers a set of operators at the same precedence level
176
+ *
177
+ * @param ops Operator table
178
+ */
179
+ operators(ops) {
180
+ const keys = Object.keys(ops);
181
+ const ps = keys.map((k) => P.string(ops[k]).trim(P.optWhitespace).result(k));
182
+ return P.alt.apply(null, ps);
183
+ }
184
+ /**
185
+ * Helper function to create a table-driver operator parser
186
+ *
187
+ */
188
+ tableParser(operatorTable, valueParser) {
189
+ return operatorTable.reduce((acc, level) => level.type(level.ops, acc), valueParser);
190
+ }
191
+ expandFeatureState(symbols) {
192
+ let expandedFeatureState = ["feature-state", symbols.shift()];
193
+ symbols.forEach((s) => expandedFeatureState = ["get", s, expandedFeatureState]);
194
+ return expandedFeatureState;
195
+ }
196
+ }
197
+
198
+ export { ExpressionParser };
@@ -0,0 +1,75 @@
1
+ import colorbrewer from 'colorbrewer';
2
+ import { isString, isArray } from '../../utils/type-checks.mjs';
3
+ import { FlowCoreExpressionError } from '../../errors/flow-expression-error.mjs';
4
+
5
+ const palettes = {};
6
+ function registerColorBrewerPalettes() {
7
+ Object.keys(colorbrewer).filter((key) => key !== "schemeGroups").forEach(
8
+ (key) => palettes[key.toLowerCase()] = {
9
+ id: key,
10
+ colors: colorbrewer[key][5]
11
+ }
12
+ );
13
+ }
14
+ function fromPalette(expr, parentExpression) {
15
+ const paletteIdOrArray = expr[1];
16
+ const property = expr[2];
17
+ let colorStops = expr.slice(3);
18
+ let palette;
19
+ if (isString(paletteIdOrArray)) {
20
+ palette = palettes[paletteIdOrArray.toLowerCase()]?.colors || palettes["Greys"].colors;
21
+ } else if (isArray(paletteIdOrArray) && paletteIdOrArray[0] === "literal") {
22
+ palette = paletteIdOrArray[1];
23
+ } else {
24
+ throw new FlowCoreExpressionError(parentExpression.expression, "evaluationError", [
25
+ "from-palette requires palette id as string or custom palette array"
26
+ ]);
27
+ }
28
+ const numPaletteEntries = palette.length;
29
+ let reversePalette = false;
30
+ if (numPaletteEntries < 5) {
31
+ throw new FlowCoreExpressionError(parentExpression.expression, "evaluationError", [
32
+ "custom palette requires at least 5 color entries"
33
+ ]);
34
+ }
35
+ switch (colorStops.length) {
36
+ case 2:
37
+ break;
38
+ case 3:
39
+ reversePalette = !!colorStops[2];
40
+ colorStops = colorStops.slice(0, 2);
41
+ break;
42
+ case numPaletteEntries:
43
+ break;
44
+ case numPaletteEntries + 1:
45
+ reversePalette = !!colorStops[numPaletteEntries];
46
+ colorStops = colorStops.slice(0, numPaletteEntries);
47
+ break;
48
+ default:
49
+ throw new FlowCoreExpressionError(parentExpression.expression, "evaluationError", [
50
+ "incorrect color stop arguments"
51
+ ]);
52
+ }
53
+ if (colorStops.length === 2) {
54
+ const min = colorStops[0];
55
+ const max = colorStops[1];
56
+ colorStops = [];
57
+ const step = (max - min) / (numPaletteEntries - 1);
58
+ for (let i = 0; i < numPaletteEntries; i++) {
59
+ colorStops.push(min + i * step);
60
+ }
61
+ }
62
+ expr = ["interpolate-lab", ["linear"], property];
63
+ for (let i = 0; i < colorStops.length; i++) {
64
+ expr.push(colorStops[i]);
65
+ expr.push(reversePalette ? palette[colorStops.length - i - 1] : palette[i]);
66
+ }
67
+ return expr;
68
+ }
69
+ const customFromPaletteFn = {
70
+ name: "from-palette",
71
+ initialize: registerColorBrewerPalettes,
72
+ fn: fromPalette
73
+ };
74
+
75
+ export { customFromPaletteFn as default };
@@ -0,0 +1,25 @@
1
+ function regexFunction(operator, caseSensitive = true) {
2
+ return (expr, _) => {
3
+ expr[0] = operator;
4
+ expr.push(["collator", { "case-sensitive": caseSensitive }]);
5
+ return expr;
6
+ };
7
+ }
8
+ const equal = {
9
+ name: "equal",
10
+ fn: regexFunction("==", true)
11
+ };
12
+ const equali = {
13
+ name: "iequal",
14
+ fn: regexFunction("==", false)
15
+ };
16
+ const regex = {
17
+ name: "regex",
18
+ fn: regexFunction("~", true)
19
+ };
20
+ const regexi = {
21
+ name: "regexi",
22
+ fn: regexFunction("~", false)
23
+ };
24
+
25
+ export { equal, equali, regex, regexi };
@@ -0,0 +1,25 @@
1
+ import { isNil } from '../../utils/type-checks.mjs';
2
+
3
+ function stateCustomFunction(expr, parentExpression) {
4
+ if (!parentExpression.store) {
5
+ console.warn(
6
+ `The reactive expression '${parentExpression.expression}' references state properties but is not bound to a state store. The expression cannot be evaluated - returning null.`
7
+ );
8
+ return null;
9
+ }
10
+ const path = expr.slice(1).join(".");
11
+ const val = parentExpression.store.get(path);
12
+ if (isNil(val)) {
13
+ return null;
14
+ } else if (typeof val === "object") {
15
+ return ["literal", val];
16
+ } else {
17
+ return val;
18
+ }
19
+ }
20
+ const customStateFn = {
21
+ name: "state",
22
+ fn: stateCustomFunction
23
+ };
24
+
25
+ export { customStateFn as default };
@@ -0,0 +1,37 @@
1
+ import P from 'parsimmon';
2
+
3
+ function PREFIX(operatorsParser, nextParser) {
4
+ const parser = P.lazy(() => {
5
+ return P.seq(operatorsParser, parser).or(nextParser);
6
+ });
7
+ return parser;
8
+ }
9
+ function BINARY_RIGHT(operatorsParser, nextParser) {
10
+ const parser = P.lazy(() => nextParser.chain((next) => P.seq(operatorsParser, P.of(next), parser).or(P.of(next))));
11
+ return parser;
12
+ }
13
+ function TERNARY_RIGHT(operatorsParser, nextParser) {
14
+ const parser = P.lazy(
15
+ () => nextParser.chain(
16
+ (next) => P.seqMap(
17
+ operatorsParser,
18
+ P.of(next),
19
+ parser,
20
+ P.string(":").trim(P.optWhitespace),
21
+ parser,
22
+ (op, cond, val1, _colon, val2) => [op, ["to-boolean", cond], val1, val2]
23
+ ).or(P.of(next))
24
+ )
25
+ );
26
+ return parser;
27
+ }
28
+ function BINARY_LEFT(operatorsParser, nextParser) {
29
+ return P.seqMap(nextParser, P.seq(operatorsParser, nextParser).many(), (first, rest) => {
30
+ return rest.reduce((acc, ch) => {
31
+ const [op, another] = ch;
32
+ return [op, acc, another];
33
+ }, first);
34
+ });
35
+ }
36
+
37
+ export { BINARY_LEFT, BINARY_RIGHT, PREFIX, TERNARY_RIGHT };
@@ -0,0 +1,253 @@
1
+ import * as maplibreSpec from '@emuanalytics/maplibre-gl-style-spec';
2
+ import { makeObservable, observable, computed, action } from 'mobx';
3
+ import { toStream } from 'mobx-utils';
4
+ import { FlowCoreExpressionError } from '../errors/flow-expression-error.mjs';
5
+ import { isDefined, isString } from '../utils/type-checks.mjs';
6
+ import { ExpressionParser } from './expression-parser.mjs';
7
+ import customFromPaletteFn from './functions/from-palette.mjs';
8
+ import { regex, regexi, equal, equali } from './functions/regex.mjs';
9
+ import customStateFn from './functions/state.mjs';
10
+
11
+ var __defProp = Object.defineProperty;
12
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
13
+ var __publicField = (obj, key, value) => {
14
+ __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
15
+ return value;
16
+ };
17
+ const _ReactiveExpression = class _ReactiveExpression {
18
+ /**
19
+ * Constructs a new `ReactiveExpression`.
20
+ *
21
+ * @example
22
+ * ```js
23
+ * const ex = new ReactiveExpression('1 + 2');
24
+ * console.log(ex.value); // 3
25
+ * ```
26
+ *
27
+ * @param expression - the string representation of the expression
28
+ * @param parsedExpression - Used internally to bypass parsing when evaluating a program.
29
+ */
30
+ constructor(expression, parsedExpression) {
31
+ __publicField(this, "_expression", "");
32
+ __publicField(this, "parsedResult");
33
+ __publicField(this, "_store", null);
34
+ makeObservable(
35
+ this,
36
+ {
37
+ _store: observable.ref,
38
+ maplibreExpression: computed,
39
+ value: computed,
40
+ bindState: action
41
+ },
42
+ { name: this.formatName(expression) }
43
+ );
44
+ this._expression = expression;
45
+ if (isDefined(parsedExpression)) {
46
+ this.parsedResult = {
47
+ status: true,
48
+ value: parsedExpression
49
+ };
50
+ } else {
51
+ this.parsedResult = _ReactiveExpression.parser.parseExpression(expression);
52
+ }
53
+ }
54
+ /**
55
+ * Returns `true` if the specified custom function has been registered.
56
+ *
57
+ * @param fn - The custom function name
58
+ */
59
+ static hasCustomFunction(fn) {
60
+ return !!this.customFunctions[fn];
61
+ }
62
+ /**
63
+ * Register a custom function to be used in reactive expressions.
64
+ *
65
+ * @param fn - The custom function to register
66
+ */
67
+ static registerCustomFunction(fn) {
68
+ if (fn.initialize)
69
+ fn.initialize();
70
+ this.customFunctions[fn.name] = fn;
71
+ }
72
+ /**
73
+ * Parses a program string into a map of properties and their associated {@link ReactiveExpression}.
74
+ *
75
+ * The program string should be a series of property/expression pairs separated by semicolons as follows:
76
+ *
77
+ * ```js
78
+ * const program = `
79
+ * prop1: 1 + 2;
80
+ * prop2: 'a string';
81
+ * prop3: $stateProp * 3;
82
+ * `;
83
+ *
84
+ * const parsedProgram = ReactiveExpression.parseProgram(program);
85
+ * // {
86
+ * // prop1: ReactiveExpression,
87
+ * // prop2: ReactiveExpression,
88
+ * // prop3: ReactiveExpression
89
+ * // }
90
+ *
91
+ * ```
92
+ *
93
+ * @param program - The program to parse
94
+ * @returns A map of property names and their associated expressions.
95
+ */
96
+ static parseProgram(program) {
97
+ const parsedProgram = this.parser.parseProgram(program);
98
+ if (isString(program) && program.length === 0) {
99
+ return {};
100
+ }
101
+ if (parsedProgram.status) {
102
+ const keys = Object.keys(parsedProgram.value);
103
+ const expressions = keys.reduce((accum, key) => {
104
+ const expr = parsedProgram.value[key];
105
+ const expressionString = program.slice(expr.start.offset, expr.end.offset);
106
+ accum[key] = new _ReactiveExpression(expressionString, expr.expr);
107
+ return accum;
108
+ }, {});
109
+ return expressions;
110
+ } else {
111
+ throw new FlowCoreExpressionError(program, "parseError", [
112
+ `expected ${parsedProgram.expected.join(" ")} at line ${parsedProgram.index.line}, column ${parsedProgram.index.column}]`
113
+ ]);
114
+ }
115
+ }
116
+ /**
117
+ * Generates the MapLibre-compatible representation of this expression.
118
+ *
119
+ * This is used internally when creating MapLibre style specifications
120
+ * from Flo.w RDF {@link MapLayer} styles.
121
+ *
122
+ * This getter is a reactive `computed` and will re-evaluate within a
123
+ * reactive context when the expression value changes.
124
+ *
125
+ * @computed
126
+ *
127
+ * @returns The MapLibre-compatible representation of this expression
128
+ */
129
+ get maplibreExpression() {
130
+ if (this.isParseError(this.parsedResult)) {
131
+ throw this.parseError;
132
+ }
133
+ return this.expandCustomFunctions(this.parsedResult.value);
134
+ }
135
+ /**
136
+ * Evaluates the expression and returns the result.
137
+ *
138
+ * This getter is a reactive `computed` and will re-evaluate within a
139
+ * reactive context when the expression value changes.
140
+ *
141
+ * @example
142
+ * ```js
143
+ * const ex = new ReactiveExpression('1 + 2');
144
+ * console.log(ex.value); // 3
145
+ * ```
146
+ *
147
+ * @computed
148
+ *
149
+ * @returns - The result of evaluating the expression
150
+ */
151
+ get value() {
152
+ return this.evaluateExpression();
153
+ }
154
+ /**
155
+ * Returns details of parsing errors if the expression could not be parsed.
156
+ */
157
+ get parseError() {
158
+ if (this.isParseError(this.parsedResult)) {
159
+ return new FlowCoreExpressionError(this._expression, "parseError", [
160
+ `index: ${this.parsedResult.index.offset}`,
161
+ `expected: ${this.parsedResult.expected.join(" ")}`
162
+ ]);
163
+ }
164
+ return null;
165
+ }
166
+ /**
167
+ * Returns the original expression string passed to the constructor.
168
+ */
169
+ get expression() {
170
+ return this._expression;
171
+ }
172
+ /**
173
+ * Returns the state store bound to the expression.
174
+ */
175
+ get store() {
176
+ return this._store;
177
+ }
178
+ /**
179
+ * Binds a {@link StateStore} to the expression. The state store is used to resolve
180
+ * state variables in the expression.
181
+ *
182
+ * Note: If no state store is bound, the expression will not be able to resolve
183
+ * state variables and will return `null` when evaluated. A console warning will
184
+ * be logged.
185
+ *
186
+ * @param store {@link StateStore} to bind to the expression
187
+ * @returns
188
+ */
189
+ bindState(store) {
190
+ this._store = store;
191
+ return this;
192
+ }
193
+ /**
194
+ * Returns a subscribable stream, which produces the value of the expression
195
+ * whenever a reactive update causes the expression to re-evaluate.
196
+ *
197
+ * @example
198
+ * ```js
199
+ * const ex = new ReactiveExpression('$val * 2');
200
+ * const subscription = ex.toStream().subscribe((value) => console.log(value));
201
+ * ...
202
+ * subscription.unsubscribe();
203
+ * ```
204
+ *
205
+ * @param fireImmediately - If `true`, the stream will fire immediately with the current value.
206
+ * If `false` the stream will only fire when the value changes.
207
+ *
208
+ * @returns A subscribable stream of the expression value
209
+ */
210
+ toStream(fireImmediately = true) {
211
+ return toStream(() => this.value, fireImmediately);
212
+ }
213
+ evaluateExpression() {
214
+ const { result, value } = maplibreSpec.expression.createExpression(this.maplibreExpression);
215
+ if (result === "success") {
216
+ return value.evaluate({ zoom: 0 });
217
+ }
218
+ throw new FlowCoreExpressionError(
219
+ this.expression,
220
+ "evaluationError",
221
+ value.map((value2) => value2.message)
222
+ );
223
+ }
224
+ expandCustomFunctions(expr) {
225
+ if (this.isFunctionExpression(expr)) {
226
+ const func = expr[0];
227
+ const args = expr.slice(1).map((arg) => this.expandCustomFunctions(arg));
228
+ expr = [func, ...args];
229
+ return _ReactiveExpression.hasCustomFunction(func) ? _ReactiveExpression.customFunctions[func].fn(expr, this) : expr;
230
+ }
231
+ return expr;
232
+ }
233
+ isParseError(val) {
234
+ return !val.status;
235
+ }
236
+ isFunctionExpression(expr) {
237
+ return Array.isArray(expr);
238
+ }
239
+ formatName(expr) {
240
+ return `FlowCoreExpression<${expr}>}`;
241
+ }
242
+ };
243
+ __publicField(_ReactiveExpression, "parser", new ExpressionParser());
244
+ __publicField(_ReactiveExpression, "customFunctions", {});
245
+ let ReactiveExpression = _ReactiveExpression;
246
+ ReactiveExpression.registerCustomFunction(customStateFn);
247
+ ReactiveExpression.registerCustomFunction(customFromPaletteFn);
248
+ ReactiveExpression.registerCustomFunction(regex);
249
+ ReactiveExpression.registerCustomFunction(regexi);
250
+ ReactiveExpression.registerCustomFunction(equal);
251
+ ReactiveExpression.registerCustomFunction(equali);
252
+
253
+ export { ReactiveExpression };