wuira 0.2.5 → 0.2.6

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.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,19 @@ with the release date and highlight breaking changes explicitly.
7
7
 
8
8
  ## Unreleased
9
9
 
10
+ ## 0.2.6 — 2026-10-16
11
+
12
+ ### Changed
13
+
14
+ - Keep wuira source conversion callback-based. Case mappings and arithmetic
15
+ formula factories now belong to LumaUI's builder layer.
16
+
17
+ ### Removed
18
+
19
+ - **Breaking:** Remove the `CaseConverter` and `MathConverter` APIs introduced in
20
+ 0.2.5, including the expression parser. Use `new SourceConverter(source,
21
+ value => ...)` in wuira code; LumaUI owns its editable case/math definitions.
22
+
10
23
  ## 0.2.5 — 2026-10-16
11
24
 
12
25
  ### Added
package/README.md CHANGED
@@ -30,7 +30,7 @@ wuira.Mount(App);
30
30
  - **Native widgets** — Cells mount to WoW frames; progress bars use the native StatusBar widget.
31
31
  - **Layout primitives** — Arrange children with `flow` and `grid`.
32
32
  - **Data sources** — HP, Mana, auras, cooldowns, casts, swings, group members, class/spec, spellbook.
33
- - **Source converters** — Compose checked value transformations, case mappings and arithmetic expressions. See [source converters](docs/source-converters.md).
33
+ - **Source converters** — Transform checked values with typed callbacks. See [source converters](docs/source-converters.md).
34
34
  - **Slash commands** — Register commands with quoting and escaping support.
35
35
  - **Windows and settings** — `Window`, `AddonSettings`, `FrameMover`, `KeybindCapture`.
36
36
  - **Secure buttons** — Spell, item, macro, action, and target actions with `SecureButton`.
@@ -1,34 +1,27 @@
1
1
  # Source converters
2
2
 
3
- `SourceConverter` transforms a readable source value. It consumes the input's change notifications, shares one upstream subscription across its observers, and does not create a timer or dispose the shared input. Sources without notifications remain available as checked one-time reads.
3
+ `SourceConverter` transforms a readable source with a typed callback. It consumes change notifications, shares one upstream subscription across observers, and does not create a timer or dispose the shared input. Sources without notifications remain available as checked one-time reads.
4
4
 
5
5
  ```ts
6
- import { ClassSource, SourceConverter, CaseConverter, HP, MathConverter, Unit } from "wuira/datasources";
6
+ import { SourceConverter, HP, ClassSource, Unit } from "wuira/datasources";
7
7
 
8
+ const hp = new HP(Unit.Player);
9
+ const doubled = new SourceConverter(hp, value => value * 2);
8
10
  const characterClass = new ClassSource(Unit.Player);
9
11
  const token = new SourceConverter(characterClass, info => info?.token);
10
- const color = new CaseConverter(token, [
11
- { equals: "PALADIN", value: { r: 1, g: 0.5, b: 0.7, a: 1 } },
12
- { equals: "MAGE", value: { r: 0.4, g: 0.8, b: 1, a: 1 } },
13
- ], { r: 0.35, g: 0.4, b: 0.45, a: 1 });
14
-
15
- const hp = new HP(Unit.Player);
16
- const doubled = new MathConverter(hp, "2 * input");
17
12
  const stop = doubled.BindTo(value => console.log(value));
13
+
18
14
  // On removal:
19
15
  stop();
20
16
  doubled.Dispose();
21
- color.Dispose();
22
17
  token.Dispose();
23
18
  characterClass.Dispose();
24
19
  ```
25
20
 
26
- `Value` and `Compute()` are checked reads. `TryCompute()` performs one checked read and returns `{ readable: false }` for restricted data, without invoking the callback. Converter chains use it to avoid repeated leaf reads. `onChange()` observes future readable output changes; `BindTo()` also applies the current readable value immediately. Scalar outputs with the same value do not emit again. `When()` filters emissions without changing the checked `Value` getter. Call `Dispose()` when the converter is no longer owned by a component.
27
-
28
- Cases compare strings, finite numbers and booleans with strict equality; `1` and `"1"` are different keys. Duplicate keys are rejected. Unmatched values return the supplied default, or `undefined` when none was provided. Case outputs can be colors, texture IDs, text or other data.
21
+ `Value` and `Compute()` are checked reads. `TryCompute()` performs one checked read and returns `{ readable: false }` for restricted data without invoking the callback. Converter chains use it to read their leaf once, including when the data is restricted.
29
22
 
30
- Math expressions support `input`, finite numeric literals, parentheses, unary signs, `+`, `-`, `*`, `/`, `%`, and exponentiation (`^` or `**`). Exponents associate to the right; `-input^2` means `-(input^2)`. The bounded parser runs once at construction. It does not evaluate JavaScript or Lua, access globals, or call functions. Invalid syntax, division by zero and non-finite results throw errors. `MathConverter.Validate(expression)` checks syntax without reading the input.
23
+ `onChange()` observes future readable output changes; `BindTo()` also applies the current readable value immediately. Identical scalar outputs do not emit again. `When()` filters emissions without changing the checked getter. `Dispose()` releases observers while leaving the input available to other consumers.
31
24
 
32
- Restricted values, including nested secret fields, never reach a conversion callback. Restrictions are checked when values are consumed; combat alone does not mark a value secret. ClassSource subscriptions follow unit identity changes and relevant native events, and release those listeners when the last consumer unsubscribes.
25
+ Restricted values, including nested secret fields, never reach a conversion callback. Restrictions are checked when consumed; combat alone does not mark a value secret. ClassSource subscriptions follow unit identity changes and native events, releasing listeners when the last consumer unsubscribes.
33
26
 
34
- Converters produce data. They do not grant permission to change protected buttons or their actions during combat.
27
+ The SDK accepts TypeScript callbacks. Editable case rules and math formulas are LumaUI builder factories, implemented using this generic source. Converters produce data; protected button actions remain subject to native combat restrictions.
@@ -28,12 +28,3 @@ export declare class SourceConverter<TInput, TOutput> extends DataSource<TOutput
28
28
  BindTo(apply: (value: TOutput) => void): () => void;
29
29
  Dispose(): void;
30
30
  }
31
- export type CaseKey = string | number | boolean;
32
- export interface SourceCase<T> {
33
- readonly equals: CaseKey;
34
- readonly value: T;
35
- }
36
- /** Strict equality, first matching case, then the optional default value. */
37
- export declare class CaseConverter<TInput, TOutput> extends SourceConverter<TInput, TOutput | undefined> {
38
- constructor(input: ValueSource<TInput>, cases: readonly SourceCase<TOutput>[], fallback?: TOutput);
39
- }
@@ -319,62 +319,6 @@ local function __TS__ClassExtends(target, base)
319
319
  target.prototype.__tostring = base.prototype.__tostring
320
320
  end
321
321
  end
322
-
323
- local function __TS__ObjectAssign(target, ...)
324
- local sources = {...}
325
- for i = 1, #sources do
326
- local source = sources[i]
327
- if type(source) == "table" then
328
- for key in pairs(source) do
329
- target[key] = source[key]
330
- end
331
- end
332
- end
333
- return target
334
- end
335
-
336
- local function __TS__ArrayMap(self, callbackfn, thisArg)
337
- local result = {}
338
- for i = 1, #self do
339
- result[i] = callbackfn(thisArg, self[i], i - 1, self)
340
- end
341
- return result
342
- end
343
-
344
- local function __TS__TypeOf(value)
345
- local luaType = type(value)
346
- if luaType == "table" then
347
- return "object"
348
- elseif luaType == "nil" then
349
- return "undefined"
350
- else
351
- return luaType
352
- end
353
- end
354
-
355
- local function __TS__ArrayIncludes(self, searchElement, fromIndex)
356
- if fromIndex == nil then
357
- fromIndex = 0
358
- end
359
- local len = #self
360
- local k = fromIndex
361
- if fromIndex < 0 then
362
- k = len + fromIndex
363
- end
364
- if k < 0 then
365
- k = 0
366
- end
367
- for i = k + 1, len do
368
- if self[i] == searchElement then
369
- return true
370
- end
371
- end
372
- return false
373
- end
374
-
375
- local function __TS__NumberIsFinite(value)
376
- return type(value) == "number" and value == value and value ~= math.huge and value ~= -math.huge
377
- end
378
322
  -- End of Lua Library inline imports
379
323
  local ____exports = {}
380
324
  local ____DataSource = require("datasources.DataSource")
@@ -528,41 +472,4 @@ function SourceConverter.prototype.Dispose(self)
528
472
  self.stop = nil
529
473
  self.listeners:clear()
530
474
  end
531
- --- Strict equality, first matching case, then the optional default value.
532
- ____exports.CaseConverter = __TS__Class()
533
- local CaseConverter = ____exports.CaseConverter
534
- CaseConverter.name = "CaseConverter"
535
- __TS__ClassExtends(CaseConverter, ____exports.SourceConverter)
536
- function CaseConverter.prototype.____constructor(self, input, cases, fallback)
537
- local entries = __TS__ArrayMap(
538
- cases,
539
- function(____, entry) return __TS__ObjectAssign({}, entry) end
540
- )
541
- local seen = __TS__New(Set)
542
- for ____, entry in ipairs(entries) do
543
- local key = entry.equals
544
- if issecretvalue(key) or not __TS__ArrayIncludes(
545
- {"string", "number", "boolean"},
546
- __TS__TypeOf(key)
547
- ) or type(key) == "number" and not __TS__NumberIsFinite(key) then
548
- error("CaseConverter: invalid match value")
549
- end
550
- if seen:has(key) then
551
- error("CaseConverter: duplicate match value")
552
- end
553
- seen:add(key)
554
- end
555
- CaseConverter.____super.prototype.____constructor(
556
- self,
557
- input,
558
- function(____, value)
559
- for ____, entry in ipairs(entries) do
560
- if value == entry.equals then
561
- return entry.value
562
- end
563
- end
564
- return fallback
565
- end
566
- )
567
- end
568
475
  return ____exports
@@ -1,9 +1,8 @@
1
1
  /// <reference path="../platform/forever.d.ts" />
2
2
  export { DataSource } from "./DataSource";
3
3
  export type { ReadResult } from "./DataSource";
4
- export { SourceConverter, CaseConverter } from "./SourceConverter";
5
- export type { ValueSource, CaseKey, SourceCase } from "./SourceConverter";
6
- export { MathConverter } from "./MathConverter";
4
+ export { SourceConverter } from "./SourceConverter";
5
+ export type { ValueSource } from "./SourceConverter";
7
6
  export { EditModeLayouts } from "./EditModeLayouts";
8
7
  export type { EditModeLayout, EditModeLayoutSnapshot, EditModeLayoutsStatus } from "./EditModeLayouts";
9
8
  export { ProgressSource } from "./ProgressSource";
@@ -7,11 +7,6 @@ end
7
7
  do
8
8
  local ____SourceConverter = require("datasources.SourceConverter")
9
9
  ____exports.SourceConverter = ____SourceConverter.SourceConverter
10
- ____exports.CaseConverter = ____SourceConverter.CaseConverter
11
- end
12
- do
13
- local ____MathConverter = require("datasources.MathConverter")
14
- ____exports.MathConverter = ____MathConverter.MathConverter
15
10
  end
16
11
  do
17
12
  local ____EditModeLayouts = require("datasources.EditModeLayouts")
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "type": "git",
5
5
  "url": "git+https://github.com/KerimCETINBAS/wuira.git"
6
6
  },
7
- "version": "0.2.5",
7
+ "version": "0.2.6",
8
8
  "author": {
9
9
  "name": "Kerim Cetinbas",
10
10
  "email": "a.cetinbas@icloud.com"
@@ -1,6 +0,0 @@
1
- import { SourceConverter } from "./SourceConverter";
2
- import type { ValueSource } from "./SourceConverter";
3
- export declare class MathConverter extends SourceConverter<number, number> {
4
- constructor(input: ValueSource<number>, expression: string);
5
- static Validate(expression: string): void;
6
- }
@@ -1,61 +0,0 @@
1
- --[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]]
2
- -- Lua Library inline imports
3
- local function __TS__Class(self)
4
- local c = {prototype = {}}
5
- c.prototype.__index = c.prototype
6
- c.prototype.constructor = c
7
- return c
8
- end
9
-
10
- local function __TS__ClassExtends(target, base)
11
- target.____super = base
12
- local staticMetatable = setmetatable({__index = base}, base)
13
- setmetatable(target, staticMetatable)
14
- local baseMetatable = getmetatable(base)
15
- if baseMetatable then
16
- if type(baseMetatable.__index) == "function" then
17
- staticMetatable.__index = baseMetatable.__index
18
- end
19
- if type(baseMetatable.__newindex) == "function" then
20
- staticMetatable.__newindex = baseMetatable.__newindex
21
- end
22
- end
23
- setmetatable(target.prototype, base.prototype)
24
- if type(base.prototype.__index) == "function" then
25
- target.prototype.__index = base.prototype.__index
26
- end
27
- if type(base.prototype.__newindex) == "function" then
28
- target.prototype.__newindex = base.prototype.__newindex
29
- end
30
- if type(base.prototype.__tostring) == "function" then
31
- target.prototype.__tostring = base.prototype.__tostring
32
- end
33
- end
34
-
35
- local function __TS__New(target, ...)
36
- local instance = setmetatable({}, target.prototype)
37
- instance:____constructor(...)
38
- return instance
39
- end
40
- -- End of Lua Library inline imports
41
- local ____exports = {}
42
- local ____SourceConverter = require("datasources.SourceConverter")
43
- local SourceConverter = ____SourceConverter.SourceConverter
44
- local ____MathExpression = require("datasources.MathExpression")
45
- local MathExpression = ____MathExpression.MathExpression
46
- ____exports.MathConverter = __TS__Class()
47
- local MathConverter = ____exports.MathConverter
48
- MathConverter.name = "MathConverter"
49
- __TS__ClassExtends(MathConverter, SourceConverter)
50
- function MathConverter.prototype.____constructor(self, input, expression)
51
- local formula = __TS__New(MathExpression, expression)
52
- SourceConverter.prototype.____constructor(
53
- self,
54
- input,
55
- function(____, value) return formula:Evaluate(value) end
56
- )
57
- end
58
- function MathConverter.Validate(self, expression)
59
- __TS__New(MathExpression, expression)
60
- end
61
- return ____exports
@@ -1,16 +0,0 @@
1
- /** Small bounded arithmetic grammar. Expressions cannot call functions or execute Lua. */
2
- export declare class MathExpression {
3
- private readonly text;
4
- private offset;
5
- private token;
6
- private tokens;
7
- private readonly tree;
8
- constructor(text: string);
9
- private Next;
10
- private Depth;
11
- private Add;
12
- private Multiply;
13
- private Unary;
14
- private Primary;
15
- Evaluate(input: number): number;
16
- }
@@ -1,281 +0,0 @@
1
- --[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]]
2
- -- Lua Library inline imports
3
- local function __TS__Class(self)
4
- local c = {prototype = {}}
5
- c.prototype.__index = c.prototype
6
- c.prototype.constructor = c
7
- return c
8
- end
9
-
10
- local function __TS__StringAccess(self, index)
11
- if index >= 0 and index < #self then
12
- return string.sub(self, index + 1, index + 1)
13
- end
14
- end
15
-
16
- local function __TS__StringIncludes(self, searchString, position)
17
- if not position then
18
- position = 1
19
- else
20
- position = position + 1
21
- end
22
- local index = string.find(self, searchString, position, true)
23
- return index ~= nil
24
- end
25
-
26
- local function __TS__StringSlice(self, start, ____end)
27
- if start == nil or start ~= start then
28
- start = 0
29
- end
30
- if ____end ~= ____end then
31
- ____end = 0
32
- end
33
- if start >= 0 then
34
- start = start + 1
35
- end
36
- if ____end ~= nil and ____end < 0 then
37
- ____end = ____end - 1
38
- end
39
- return string.sub(self, start, ____end)
40
- end
41
-
42
- local function __TS__Number(value)
43
- local valueType = type(value)
44
- if valueType == "number" then
45
- return value
46
- elseif valueType == "string" then
47
- local numberValue = tonumber(value)
48
- if numberValue then
49
- return numberValue
50
- end
51
- if value == "Infinity" then
52
- return math.huge
53
- end
54
- if value == "-Infinity" then
55
- return -math.huge
56
- end
57
- local stringWithoutSpaces = string.gsub(value, "%s", "")
58
- if stringWithoutSpaces == "" then
59
- return 0
60
- end
61
- return 0 / 0
62
- elseif valueType == "boolean" then
63
- return value and 1 or 0
64
- else
65
- return 0 / 0
66
- end
67
- end
68
-
69
- local function __TS__NumberIsFinite(value)
70
- return type(value) == "number" and value == value and value ~= math.huge and value ~= -math.huge
71
- end
72
-
73
- local function __TS__ArrayIncludes(self, searchElement, fromIndex)
74
- if fromIndex == nil then
75
- fromIndex = 0
76
- end
77
- local len = #self
78
- local k = fromIndex
79
- if fromIndex < 0 then
80
- k = len + fromIndex
81
- end
82
- if k < 0 then
83
- k = 0
84
- end
85
- for i = k + 1, len do
86
- if self[i] == searchElement then
87
- return true
88
- end
89
- end
90
- return false
91
- end
92
- -- End of Lua Library inline imports
93
- local ____exports = {}
94
- --- Small bounded arithmetic grammar. Expressions cannot call functions or execute Lua.
95
- ____exports.MathExpression = __TS__Class()
96
- local MathExpression = ____exports.MathExpression
97
- MathExpression.name = "MathExpression"
98
- function MathExpression.prototype.____constructor(self, text)
99
- self.text = text
100
- self.offset = 0
101
- self.token = ""
102
- self.tokens = 0
103
- if issecretvalue(text) or type(text) ~= "string" or #text == 0 or #text > 256 then
104
- error("MathConverter: expression must contain 1–256 characters")
105
- end
106
- self:Next()
107
- self.tree = self:Add(0)
108
- if self.token ~= "" then
109
- error("MathConverter: unexpected token " .. self.token)
110
- end
111
- end
112
- function MathExpression.prototype.Next(self)
113
- local ____self_0, ____tokens_1 = self, "tokens"
114
- local ____self_tokens_2 = ____self_0[____tokens_1] + 1
115
- ____self_0[____tokens_1] = ____self_tokens_2
116
- if ____self_tokens_2 > 128 then
117
- error("MathConverter: expression is too complex")
118
- end
119
- while __TS__StringIncludes(
120
- " \t\r\n",
121
- __TS__StringAccess(self.text, self.offset) or "\0"
122
- ) do
123
- self.offset = self.offset + 1
124
- end
125
- local char = __TS__StringAccess(self.text, self.offset)
126
- if char == nil then
127
- self.token = ""
128
- return
129
- end
130
- if __TS__StringSlice(self.text, self.offset, self.offset + 5) == "input" then
131
- self.offset = self.offset + 5
132
- self.token = "input"
133
- return
134
- end
135
- if __TS__StringIncludes("0123456789.", char) then
136
- local ____self_3, ____offset_4 = self, "offset"
137
- local ____self_offset_5 = ____self_3[____offset_4]
138
- ____self_3[____offset_4] = ____self_offset_5 + 1
139
- local start = ____self_offset_5
140
- while __TS__StringIncludes(
141
- "0123456789.",
142
- __TS__StringAccess(self.text, self.offset) or "\0"
143
- ) do
144
- self.offset = self.offset + 1
145
- end
146
- if __TS__StringAccess(self.text, self.offset) == "e" or __TS__StringAccess(self.text, self.offset) == "E" then
147
- self.offset = self.offset + 1
148
- if __TS__StringAccess(self.text, self.offset) == "+" or __TS__StringAccess(self.text, self.offset) == "-" then
149
- self.offset = self.offset + 1
150
- end
151
- while __TS__StringIncludes(
152
- "0123456789",
153
- __TS__StringAccess(self.text, self.offset) or "\0"
154
- ) do
155
- self.offset = self.offset + 1
156
- end
157
- end
158
- self.token = __TS__StringSlice(self.text, start, self.offset)
159
- if not __TS__NumberIsFinite(__TS__Number(self.token)) then
160
- error("MathConverter: invalid number")
161
- end
162
- return
163
- end
164
- if __TS__StringIncludes("+-*/%^()", char) then
165
- self.offset = self.offset + 1
166
- self.token = char
167
- if char == "*" and __TS__StringAccess(self.text, self.offset) == "*" then
168
- self.offset = self.offset + 1
169
- self.token = "^"
170
- end
171
- return
172
- end
173
- error("MathConverter: only input, numbers and arithmetic operators are allowed")
174
- end
175
- function MathExpression.prototype.Depth(self, depth)
176
- if depth > 32 then
177
- error("MathConverter: expression nesting is too deep")
178
- end
179
- end
180
- function MathExpression.prototype.Add(self, depth)
181
- local left = self:Multiply(depth)
182
- while self.token == "+" or self.token == "-" do
183
- local operator = self.token
184
- self:Next()
185
- left = {
186
- kind = "binary",
187
- operator = operator,
188
- left = left,
189
- right = self:Multiply(depth)
190
- }
191
- end
192
- return left
193
- end
194
- function MathExpression.prototype.Multiply(self, depth)
195
- local left = self:Unary(depth)
196
- while __TS__ArrayIncludes({"*", "/", "%"}, self.token) do
197
- local operator = self.token
198
- self:Next()
199
- left = {
200
- kind = "binary",
201
- operator = operator,
202
- left = left,
203
- right = self:Unary(depth)
204
- }
205
- end
206
- return left
207
- end
208
- function MathExpression.prototype.Unary(self, depth)
209
- self:Depth(depth)
210
- if self.token == "+" or self.token == "-" then
211
- local sign = self.token == "-" and -1 or 1
212
- self:Next()
213
- return {
214
- kind = "unary",
215
- sign = sign,
216
- child = self:Unary(depth + 1)
217
- }
218
- end
219
- local left = self:Primary(depth)
220
- if self.token == "^" then
221
- self:Next()
222
- left = {
223
- kind = "binary",
224
- operator = "^",
225
- left = left,
226
- right = self:Unary(depth + 1)
227
- }
228
- end
229
- return left
230
- end
231
- function MathExpression.prototype.Primary(self, depth)
232
- self:Depth(depth)
233
- if self.token == "input" then
234
- self:Next()
235
- return {kind = "input"}
236
- end
237
- if self.token == "(" then
238
- self:Next()
239
- local value = self:Add(depth + 1)
240
- if self.token ~= ")" then
241
- error("MathConverter: missing closing parenthesis")
242
- end
243
- self:Next()
244
- return value
245
- end
246
- if self.token ~= "" and __TS__NumberIsFinite(__TS__Number(self.token)) then
247
- local value = __TS__Number(self.token)
248
- self:Next()
249
- return {kind = "number", value = value}
250
- end
251
- error("MathConverter: expected input, a number or parentheses")
252
- end
253
- function MathExpression.prototype.Evaluate(self, input)
254
- if issecretvalue(input) or type(input) ~= "number" or not __TS__NumberIsFinite(input) then
255
- error("MathConverter: input must be a readable finite number")
256
- end
257
- local read
258
- read = function(____, node)
259
- if node.kind == "input" then
260
- return input
261
- end
262
- if node.kind == "number" then
263
- return node.value
264
- end
265
- if node.kind == "unary" then
266
- return node.sign * read(nil, node.child)
267
- end
268
- local left = read(nil, node.left)
269
- local right = read(nil, node.right)
270
- if (node.operator == "/" or node.operator == "%") and right == 0 then
271
- error("MathConverter: division by zero")
272
- end
273
- local result = node.operator == "+" and left + right or (node.operator == "-" and left - right or (node.operator == "*" and left * right or (node.operator == "/" and left / right or (node.operator == "%" and left % right or left ^ right))))
274
- if not __TS__NumberIsFinite(result) then
275
- error("MathConverter: result must be finite")
276
- end
277
- return result
278
- end
279
- return read(nil, self.tree)
280
- end
281
- return ____exports