@pyreon/core 0.7.12 → 0.7.13

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pyreon/core",
3
- "version": "0.7.12",
3
+ "version": "0.7.13",
4
4
  "description": "Core component model and lifecycle for Pyreon",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -49,7 +49,7 @@
49
49
  "prepublishOnly": "bun run build"
50
50
  },
51
51
  "dependencies": {
52
- "@pyreon/reactivity": "^0.7.12"
52
+ "@pyreon/reactivity": "^0.7.13"
53
53
  },
54
54
  "publishConfig": {
55
55
  "access": "public"
@@ -38,4 +38,33 @@ describe("cx", () => {
38
38
  expect(cx([])).toBe("")
39
39
  expect(cx({})).toBe("")
40
40
  })
41
+
42
+ test("object where ALL values are functions returning booleans", () => {
43
+ expect(
44
+ cx({
45
+ active: () => true,
46
+ hidden: () => false,
47
+ bold: () => true,
48
+ italic: () => false,
49
+ }),
50
+ ).toBe("active bold")
51
+ })
52
+
53
+ test("deeply nested arrays (3+ levels)", () => {
54
+ expect(cx([[["level3", [["level5"]]]]])).toBe("level3 level5")
55
+ })
56
+
57
+ test("mixed: string, object with function, deeply nested array", () => {
58
+ expect(cx(["base", { active: () => true }, [["deeply-nested"]]])).toBe(
59
+ "base active deeply-nested",
60
+ )
61
+ })
62
+
63
+ test("empty string values are filtered", () => {
64
+ expect(cx(["foo", "", "bar", ""])).toBe("foo bar")
65
+ })
66
+
67
+ test("number 0 is a valid class name as string", () => {
68
+ expect(cx(0)).toBe("0")
69
+ })
41
70
  })
@@ -66,6 +66,100 @@ describe("mergeProps", () => {
66
66
  })
67
67
  })
68
68
 
69
+ describe("mergeProps — edge cases", () => {
70
+ test("mixed getter/static value merging across 3+ sources", () => {
71
+ let dynamicSize = "sm"
72
+ const source1 = { size: "md", variant: "primary" }
73
+ const source2 = Object.defineProperty({} as Record<string, unknown>, "size", {
74
+ get: () => dynamicSize,
75
+ enumerable: true,
76
+ configurable: true,
77
+ })
78
+ const source3 = { color: "red" }
79
+ const result = mergeProps(source1, source2, source3)
80
+ expect(result.size).toBe("sm")
81
+ dynamicSize = "xl"
82
+ expect(result.size).toBe("xl")
83
+ expect((result as Record<string, unknown>).variant).toBe("primary")
84
+ expect((result as Record<string, unknown>).color).toBe("red")
85
+ })
86
+
87
+ test("getter returning undefined falling through multiple levels of defaults", () => {
88
+ let level2: string | undefined
89
+ let level1: string | undefined
90
+ const defaults = { theme: "light" }
91
+ const mid = Object.defineProperty({} as Record<string, unknown>, "theme", {
92
+ get: () => level1,
93
+ enumerable: true,
94
+ configurable: true,
95
+ })
96
+ const top = Object.defineProperty({} as Record<string, unknown>, "theme", {
97
+ get: () => level2,
98
+ enumerable: true,
99
+ configurable: true,
100
+ })
101
+ const result = mergeProps(defaults, mid, top)
102
+ // Both getters return undefined — falls back to "light"
103
+ expect(result.theme).toBe("light")
104
+ // Mid-level getter returns value — top still undefined, falls to mid
105
+ level1 = "dark"
106
+ expect(result.theme).toBe("dark")
107
+ // Top-level getter returns value — wins
108
+ level2 = "system"
109
+ expect(result.theme).toBe("system")
110
+ })
111
+
112
+ test("mergeProps with no sources (empty call)", () => {
113
+ const result = mergeProps()
114
+ expect(result).toEqual({})
115
+ })
116
+
117
+ test("later source static value overriding earlier getter", () => {
118
+ const dynamic = "from-getter"
119
+ const getterSource = Object.defineProperty({} as Record<string, unknown>, "val", {
120
+ get: () => dynamic,
121
+ enumerable: true,
122
+ configurable: true,
123
+ })
124
+ const staticSource = { val: "static-wins" }
125
+ const result = mergeProps(getterSource, staticSource)
126
+ // Static value should override getter
127
+ expect(result.val).toBe("static-wins")
128
+ })
129
+
130
+ test("later source static undefined does not override earlier getter", () => {
131
+ let dynamic = "from-getter"
132
+ const getterSource = Object.defineProperty({} as Record<string, unknown>, "val", {
133
+ get: () => dynamic,
134
+ enumerable: true,
135
+ configurable: true,
136
+ })
137
+ const staticSource = { val: undefined }
138
+ const result = mergeProps(getterSource, staticSource)
139
+ // Static undefined — getter should still be used
140
+ expect(result.val).toBe("from-getter")
141
+ dynamic = "updated"
142
+ expect(result.val).toBe("updated")
143
+ })
144
+ })
145
+
146
+ describe("splitProps — getter reactivity", () => {
147
+ test("preserves getter reactivity through multiple reads", () => {
148
+ let count = 0
149
+ const props = Object.defineProperty({ other: "x" } as Record<string, unknown>, "value", {
150
+ get: () => ++count,
151
+ enumerable: true,
152
+ configurable: true,
153
+ })
154
+ const [own, rest] = splitProps(props, ["value"])
155
+ expect(own.value).toBe(1)
156
+ expect(own.value).toBe(2)
157
+ expect(own.value).toBe(3)
158
+ // rest should not include the getter key
159
+ expect(rest).toEqual({ other: "x" })
160
+ })
161
+ })
162
+
69
163
  describe("createUniqueId", () => {
70
164
  test("returns incrementing IDs", () => {
71
165
  const id1 = createUniqueId()