@pyreon/core 0.11.5 → 0.11.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.
Files changed (47) hide show
  1. package/README.md +2 -2
  2. package/lib/analysis/index.js.html +1 -1
  3. package/lib/index.js +33 -5
  4. package/lib/index.js.map +1 -1
  5. package/lib/jsx-dev-runtime.js.map +1 -1
  6. package/lib/jsx-runtime.js.map +1 -1
  7. package/lib/types/index.d.ts +145 -98
  8. package/lib/types/index.d.ts.map +1 -1
  9. package/lib/types/jsx-dev-runtime.d.ts +94 -94
  10. package/lib/types/jsx-runtime.d.ts +94 -94
  11. package/package.json +11 -11
  12. package/src/component.ts +2 -2
  13. package/src/context.ts +75 -4
  14. package/src/dynamic.ts +4 -4
  15. package/src/error-boundary.ts +10 -10
  16. package/src/for.ts +8 -2
  17. package/src/h.ts +4 -4
  18. package/src/index.ts +30 -27
  19. package/src/jsx-dev-runtime.ts +1 -1
  20. package/src/jsx-runtime.ts +108 -108
  21. package/src/lazy.ts +4 -4
  22. package/src/lifecycle.ts +6 -6
  23. package/src/portal.ts +2 -2
  24. package/src/show.ts +4 -4
  25. package/src/style.ts +51 -51
  26. package/src/suspense.ts +8 -8
  27. package/src/telemetry.ts +1 -1
  28. package/src/tests/component.test.ts +60 -60
  29. package/src/tests/context.test.ts +102 -102
  30. package/src/tests/core.test.ts +376 -376
  31. package/src/tests/cx.test.ts +34 -34
  32. package/src/tests/dynamic.test.ts +28 -28
  33. package/src/tests/error-boundary.test.ts +51 -51
  34. package/src/tests/for.test.ts +26 -26
  35. package/src/tests/h.test.ts +100 -100
  36. package/src/tests/jsx-compat.test.tsx +41 -41
  37. package/src/tests/lazy.test.ts +28 -28
  38. package/src/tests/lifecycle.test.ts +35 -35
  39. package/src/tests/map-array.test.ts +36 -36
  40. package/src/tests/portal.test.ts +21 -21
  41. package/src/tests/props-extended.test.ts +51 -51
  42. package/src/tests/props.test.ts +62 -62
  43. package/src/tests/ref.test.ts +20 -20
  44. package/src/tests/show.test.ts +94 -94
  45. package/src/tests/style.test.ts +101 -101
  46. package/src/tests/suspense.test.ts +44 -44
  47. package/src/tests/telemetry.test.ts +35 -35
@@ -5,25 +5,25 @@ import {
5
5
  onUnmount,
6
6
  onUpdate,
7
7
  setCurrentHooks,
8
- } from "../lifecycle"
9
- import type { LifecycleHooks } from "../types"
8
+ } from '../lifecycle'
9
+ import type { LifecycleHooks } from '../types'
10
10
 
11
- describe("setCurrentHooks / getCurrentHooks", () => {
11
+ describe('setCurrentHooks / getCurrentHooks', () => {
12
12
  afterEach(() => {
13
13
  setCurrentHooks(null)
14
14
  })
15
15
 
16
- test("getCurrentHooks returns null by default", () => {
16
+ test('getCurrentHooks returns null by default', () => {
17
17
  expect(getCurrentHooks()).toBeNull()
18
18
  })
19
19
 
20
- test("setCurrentHooks sets the current hooks context", () => {
20
+ test('setCurrentHooks sets the current hooks context', () => {
21
21
  const hooks: LifecycleHooks = { mount: [], unmount: [], update: [], error: [] }
22
22
  setCurrentHooks(hooks)
23
23
  expect(getCurrentHooks()).toBe(hooks)
24
24
  })
25
25
 
26
- test("setCurrentHooks(null) clears the context", () => {
26
+ test('setCurrentHooks(null) clears the context', () => {
27
27
  const hooks: LifecycleHooks = { mount: [], unmount: [], update: [], error: [] }
28
28
  setCurrentHooks(hooks)
29
29
  expect(getCurrentHooks()).toBe(hooks)
@@ -32,12 +32,12 @@ describe("setCurrentHooks / getCurrentHooks", () => {
32
32
  })
33
33
  })
34
34
 
35
- describe("onMount", () => {
35
+ describe('onMount', () => {
36
36
  afterEach(() => {
37
37
  setCurrentHooks(null)
38
38
  })
39
39
 
40
- test("registers callback on current hooks", () => {
40
+ test('registers callback on current hooks', () => {
41
41
  const hooks: LifecycleHooks = { mount: [], unmount: [], update: [], error: [] }
42
42
  setCurrentHooks(hooks)
43
43
  const fn = () => undefined
@@ -46,7 +46,7 @@ describe("onMount", () => {
46
46
  expect(hooks.mount[0]).toBe(fn)
47
47
  })
48
48
 
49
- test("multiple onMount calls accumulate", () => {
49
+ test('multiple onMount calls accumulate', () => {
50
50
  const hooks: LifecycleHooks = { mount: [], unmount: [], update: [], error: [] }
51
51
  setCurrentHooks(hooks)
52
52
  onMount(() => undefined)
@@ -55,20 +55,20 @@ describe("onMount", () => {
55
55
  expect(hooks.mount).toHaveLength(3)
56
56
  })
57
57
 
58
- test("warns when called outside component setup", () => {
59
- const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {})
58
+ test('warns when called outside component setup', () => {
59
+ const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
60
60
  onMount(() => {})
61
61
  expect(warnSpy).toHaveBeenCalledWith(
62
- expect.stringContaining("onMount() called outside component setup"),
62
+ expect.stringContaining('onMount() called outside component setup'),
63
63
  )
64
64
  warnSpy.mockRestore()
65
65
  })
66
66
 
67
- test("is a no-op outside component setup (no crash)", () => {
67
+ test('is a no-op outside component setup (no crash)', () => {
68
68
  expect(() => onMount(() => {})).not.toThrow()
69
69
  })
70
70
 
71
- test("accepts callback returning cleanup function", () => {
71
+ test('accepts callback returning cleanup function', () => {
72
72
  const hooks: LifecycleHooks = { mount: [], unmount: [], update: [], error: [] }
73
73
  setCurrentHooks(hooks)
74
74
  const cleanup = () => {}
@@ -77,7 +77,7 @@ describe("onMount", () => {
77
77
  expect(hooks.mount[0]!()).toBe(cleanup)
78
78
  })
79
79
 
80
- test("accepts callback returning void", () => {
80
+ test('accepts callback returning void', () => {
81
81
  const hooks: LifecycleHooks = { mount: [], unmount: [], update: [], error: [] }
82
82
  setCurrentHooks(hooks)
83
83
  onMount(() => {})
@@ -86,12 +86,12 @@ describe("onMount", () => {
86
86
  })
87
87
  })
88
88
 
89
- describe("onUnmount", () => {
89
+ describe('onUnmount', () => {
90
90
  afterEach(() => {
91
91
  setCurrentHooks(null)
92
92
  })
93
93
 
94
- test("registers callback on current hooks", () => {
94
+ test('registers callback on current hooks', () => {
95
95
  const hooks: LifecycleHooks = { mount: [], unmount: [], update: [], error: [] }
96
96
  setCurrentHooks(hooks)
97
97
  const fn = () => {}
@@ -100,22 +100,22 @@ describe("onUnmount", () => {
100
100
  expect(hooks.unmount[0]).toBe(fn)
101
101
  })
102
102
 
103
- test("warns when called outside component setup", () => {
104
- const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {})
103
+ test('warns when called outside component setup', () => {
104
+ const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
105
105
  onUnmount(() => {})
106
106
  expect(warnSpy).toHaveBeenCalledWith(
107
- expect.stringContaining("onUnmount() called outside component setup"),
107
+ expect.stringContaining('onUnmount() called outside component setup'),
108
108
  )
109
109
  warnSpy.mockRestore()
110
110
  })
111
111
  })
112
112
 
113
- describe("onUpdate", () => {
113
+ describe('onUpdate', () => {
114
114
  afterEach(() => {
115
115
  setCurrentHooks(null)
116
116
  })
117
117
 
118
- test("registers callback on current hooks", () => {
118
+ test('registers callback on current hooks', () => {
119
119
  const hooks: LifecycleHooks = { mount: [], unmount: [], update: [], error: [] }
120
120
  setCurrentHooks(hooks)
121
121
  const fn = () => {}
@@ -124,22 +124,22 @@ describe("onUpdate", () => {
124
124
  expect(hooks.update[0]).toBe(fn)
125
125
  })
126
126
 
127
- test("warns when called outside component setup", () => {
128
- const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {})
127
+ test('warns when called outside component setup', () => {
128
+ const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
129
129
  onUpdate(() => {})
130
130
  expect(warnSpy).toHaveBeenCalledWith(
131
- expect.stringContaining("onUpdate() called outside component setup"),
131
+ expect.stringContaining('onUpdate() called outside component setup'),
132
132
  )
133
133
  warnSpy.mockRestore()
134
134
  })
135
135
  })
136
136
 
137
- describe("onErrorCaptured", () => {
137
+ describe('onErrorCaptured', () => {
138
138
  afterEach(() => {
139
139
  setCurrentHooks(null)
140
140
  })
141
141
 
142
- test("registers callback on current hooks", () => {
142
+ test('registers callback on current hooks', () => {
143
143
  const hooks: LifecycleHooks = { mount: [], unmount: [], update: [], error: [] }
144
144
  setCurrentHooks(hooks)
145
145
  const fn = () => true
@@ -148,16 +148,16 @@ describe("onErrorCaptured", () => {
148
148
  expect(hooks.error[0]).toBe(fn)
149
149
  })
150
150
 
151
- test("warns when called outside component setup", () => {
152
- const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {})
151
+ test('warns when called outside component setup', () => {
152
+ const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
153
153
  onErrorCaptured(() => true)
154
154
  expect(warnSpy).toHaveBeenCalledWith(
155
- expect.stringContaining("onErrorCaptured() called outside component setup"),
155
+ expect.stringContaining('onErrorCaptured() called outside component setup'),
156
156
  )
157
157
  warnSpy.mockRestore()
158
158
  })
159
159
 
160
- test("registered handler receives the error", () => {
160
+ test('registered handler receives the error', () => {
161
161
  const hooks: LifecycleHooks = { mount: [], unmount: [], update: [], error: [] }
162
162
  setCurrentHooks(hooks)
163
163
  let captured: unknown = null
@@ -166,18 +166,18 @@ describe("onErrorCaptured", () => {
166
166
  return true
167
167
  })
168
168
  // Simulate calling the handler
169
- const testError = new Error("test")
169
+ const testError = new Error('test')
170
170
  hooks.error[0]!(testError)
171
171
  expect(captured).toBe(testError)
172
172
  })
173
173
  })
174
174
 
175
- describe("lifecycle hooks interaction", () => {
175
+ describe('lifecycle hooks interaction', () => {
176
176
  afterEach(() => {
177
177
  setCurrentHooks(null)
178
178
  })
179
179
 
180
- test("all hook types can be registered in same context", () => {
180
+ test('all hook types can be registered in same context', () => {
181
181
  const hooks: LifecycleHooks = { mount: [], unmount: [], update: [], error: [] }
182
182
  setCurrentHooks(hooks)
183
183
 
@@ -192,7 +192,7 @@ describe("lifecycle hooks interaction", () => {
192
192
  expect(hooks.error).toHaveLength(1)
193
193
  })
194
194
 
195
- test("hooks from different setCurrentHooks calls go to different stores", () => {
195
+ test('hooks from different setCurrentHooks calls go to different stores', () => {
196
196
  const hooks1: LifecycleHooks = { mount: [], unmount: [], update: [], error: [] }
197
197
  const hooks2: LifecycleHooks = { mount: [], unmount: [], update: [], error: [] }
198
198
 
@@ -1,8 +1,8 @@
1
- import { mapArray } from "../map-array"
1
+ import { mapArray } from '../map-array'
2
2
 
3
- describe("mapArray", () => {
4
- describe("basic mapping", () => {
5
- test("maps all items on first call", () => {
3
+ describe('mapArray', () => {
4
+ describe('basic mapping', () => {
5
+ test('maps all items on first call', () => {
6
6
  const mapped = mapArray(
7
7
  () => [1, 2, 3],
8
8
  (item) => item,
@@ -11,7 +11,7 @@ describe("mapArray", () => {
11
11
  expect(mapped()).toEqual([10, 20, 30])
12
12
  })
13
13
 
14
- test("returns empty array for empty source", () => {
14
+ test('returns empty array for empty source', () => {
15
15
  const mapped = mapArray(
16
16
  () => [],
17
17
  (item: number) => item,
@@ -20,18 +20,18 @@ describe("mapArray", () => {
20
20
  expect(mapped()).toEqual([])
21
21
  })
22
22
 
23
- test("maps single item", () => {
23
+ test('maps single item', () => {
24
24
  const mapped = mapArray(
25
25
  () => [42],
26
26
  (item) => item,
27
27
  (item) => `value-${item}`,
28
28
  )
29
- expect(mapped()).toEqual(["value-42"])
29
+ expect(mapped()).toEqual(['value-42'])
30
30
  })
31
31
  })
32
32
 
33
- describe("caching behavior", () => {
34
- test("caches results — map function called once per key", () => {
33
+ describe('caching behavior', () => {
34
+ test('caches results — map function called once per key', () => {
35
35
  let callCount = 0
36
36
  const items = [1, 2, 3]
37
37
  const mapped = mapArray(
@@ -55,7 +55,7 @@ describe("mapArray", () => {
55
55
  expect(callCount).toBe(3)
56
56
  })
57
57
 
58
- test("only maps new keys when items are added", () => {
58
+ test('only maps new keys when items are added', () => {
59
59
  let callCount = 0
60
60
  let items = [1, 2, 3]
61
61
  const mapped = mapArray(
@@ -75,7 +75,7 @@ describe("mapArray", () => {
75
75
  expect(callCount).toBe(5) // only 4 and 5 are new
76
76
  })
77
77
 
78
- test("does not re-map when items are removed", () => {
78
+ test('does not re-map when items are removed', () => {
79
79
  let callCount = 0
80
80
  let items = [1, 2, 3, 4, 5]
81
81
  const mapped = mapArray(
@@ -97,8 +97,8 @@ describe("mapArray", () => {
97
97
  })
98
98
  })
99
99
 
100
- describe("key eviction", () => {
101
- test("evicted keys are re-mapped when they return", () => {
100
+ describe('key eviction', () => {
101
+ test('evicted keys are re-mapped when they return', () => {
102
102
  let callCount = 0
103
103
  let items = [1, 2, 3]
104
104
  const mapped = mapArray(
@@ -124,7 +124,7 @@ describe("mapArray", () => {
124
124
  expect(callCount).toBe(4) // key 2 re-mapped
125
125
  })
126
126
 
127
- test("evicts all keys when source becomes empty", () => {
127
+ test('evicts all keys when source becomes empty', () => {
128
128
  let callCount = 0
129
129
  let items: number[] = [1, 2, 3]
130
130
  const mapped = mapArray(
@@ -150,8 +150,8 @@ describe("mapArray", () => {
150
150
  })
151
151
  })
152
152
 
153
- describe("reordering", () => {
154
- test("reordered items use cached values (no re-mapping)", () => {
153
+ describe('reordering', () => {
154
+ test('reordered items use cached values (no re-mapping)', () => {
155
155
  let callCount = 0
156
156
  let items = [1, 2, 3]
157
157
  const mapped = mapArray(
@@ -172,7 +172,7 @@ describe("mapArray", () => {
172
172
  expect(callCount).toBe(3) // no new calls
173
173
  })
174
174
 
175
- test("reverse order uses cached values", () => {
175
+ test('reverse order uses cached values', () => {
176
176
  let callCount = 0
177
177
  let items = [1, 2, 3, 4]
178
178
  const mapped = mapArray(
@@ -187,21 +187,21 @@ describe("mapArray", () => {
187
187
  mapped()
188
188
  items = [4, 3, 2, 1]
189
189
  const result = mapped()
190
- expect(result).toEqual(["item-4", "item-3", "item-2", "item-1"])
190
+ expect(result).toEqual(['item-4', 'item-3', 'item-2', 'item-1'])
191
191
  expect(callCount).toBe(4) // initial 4 only
192
192
  })
193
193
  })
194
194
 
195
- describe("string keys", () => {
196
- test("works with string keys from objects", () => {
195
+ describe('string keys', () => {
196
+ test('works with string keys from objects', () => {
197
197
  interface User {
198
198
  id: string
199
199
  name: string
200
200
  }
201
201
  let callCount = 0
202
202
  let users: User[] = [
203
- { id: "a", name: "Alice" },
204
- { id: "b", name: "Bob" },
203
+ { id: 'a', name: 'Alice' },
204
+ { id: 'b', name: 'Bob' },
205
205
  ]
206
206
  const mapped = mapArray(
207
207
  () => users,
@@ -212,22 +212,22 @@ describe("mapArray", () => {
212
212
  },
213
213
  )
214
214
 
215
- expect(mapped()).toEqual(["ALICE", "BOB"])
215
+ expect(mapped()).toEqual(['ALICE', 'BOB'])
216
216
  expect(callCount).toBe(2)
217
217
 
218
218
  // Add new user
219
219
  users = [
220
- { id: "a", name: "Alice" },
221
- { id: "b", name: "Bob" },
222
- { id: "c", name: "Charlie" },
220
+ { id: 'a', name: 'Alice' },
221
+ { id: 'b', name: 'Bob' },
222
+ { id: 'c', name: 'Charlie' },
223
223
  ]
224
- expect(mapped()).toEqual(["ALICE", "BOB", "CHARLIE"])
224
+ expect(mapped()).toEqual(['ALICE', 'BOB', 'CHARLIE'])
225
225
  expect(callCount).toBe(3)
226
226
  })
227
227
  })
228
228
 
229
- describe("mixed additions and removals", () => {
230
- test("simultaneous add and remove", () => {
229
+ describe('mixed additions and removals', () => {
230
+ test('simultaneous add and remove', () => {
231
231
  let callCount = 0
232
232
  let items = [1, 2, 3]
233
233
  const mapped = mapArray(
@@ -249,7 +249,7 @@ describe("mapArray", () => {
249
249
  expect(callCount).toBe(4) // only key 4 is new
250
250
  })
251
251
 
252
- test("complete replacement of all items", () => {
252
+ test('complete replacement of all items', () => {
253
253
  let callCount = 0
254
254
  let items = [1, 2, 3]
255
255
  const mapped = mapArray(
@@ -271,8 +271,8 @@ describe("mapArray", () => {
271
271
  })
272
272
  })
273
273
 
274
- describe("duplicate keys", () => {
275
- test("duplicate keys in source share the same cached value", () => {
274
+ describe('duplicate keys', () => {
275
+ test('duplicate keys in source share the same cached value', () => {
276
276
  let callCount = 0
277
277
  const mapped = mapArray(
278
278
  () => [1, 1, 2],
@@ -291,12 +291,12 @@ describe("mapArray", () => {
291
291
  })
292
292
  })
293
293
 
294
- describe("map function receives correct item", () => {
295
- test("map receives the item, not the key", () => {
294
+ describe('map function receives correct item', () => {
295
+ test('map receives the item, not the key', () => {
296
296
  const received: Array<{ id: number; val: string }> = []
297
297
  const items = [
298
- { id: 1, val: "a" },
299
- { id: 2, val: "b" },
298
+ { id: 1, val: 'a' },
299
+ { id: 2, val: 'b' },
300
300
  ]
301
301
  const mapped = mapArray(
302
302
  () => items,
@@ -1,48 +1,48 @@
1
- import { h } from "../h"
2
- import { Portal, PortalSymbol } from "../portal"
3
- import type { VNode } from "../types"
1
+ import { h } from '../h'
2
+ import { Portal, PortalSymbol } from '../portal'
3
+ import type { VNode } from '../types'
4
4
 
5
- describe("Portal", () => {
6
- test("returns VNode with PortalSymbol type", () => {
5
+ describe('Portal', () => {
6
+ test('returns VNode with PortalSymbol type', () => {
7
7
  const fakeTarget = {} as Element
8
- const node = Portal({ target: fakeTarget, children: h("div", null) })
8
+ const node = Portal({ target: fakeTarget, children: h('div', null) })
9
9
  expect(node.type).toBe(PortalSymbol)
10
10
  })
11
11
 
12
- test("VNode has null key", () => {
13
- const node = Portal({ target: {} as Element, children: "content" })
12
+ test('VNode has null key', () => {
13
+ const node = Portal({ target: {} as Element, children: 'content' })
14
14
  expect(node.key).toBeNull()
15
15
  })
16
16
 
17
- test("VNode has empty children array", () => {
18
- const node = Portal({ target: {} as Element, children: "content" })
17
+ test('VNode has empty children array', () => {
18
+ const node = Portal({ target: {} as Element, children: 'content' })
19
19
  expect(node.children).toEqual([])
20
20
  })
21
21
 
22
- test("props contain target and children", () => {
22
+ test('props contain target and children', () => {
23
23
  const fakeTarget = {} as Element
24
- const child = h("span", null, "content")
24
+ const child = h('span', null, 'content')
25
25
  const node = Portal({ target: fakeTarget, children: child })
26
26
  const props = node.props as unknown as { target: Element; children: VNode }
27
27
  expect(props.target).toBe(fakeTarget)
28
28
  expect(props.children).toBe(child)
29
29
  })
30
30
 
31
- test("PortalSymbol is a unique symbol", () => {
32
- expect(typeof PortalSymbol).toBe("symbol")
33
- expect(PortalSymbol.toString()).toContain("pyreon.Portal")
31
+ test('PortalSymbol is a unique symbol', () => {
32
+ expect(typeof PortalSymbol).toBe('symbol')
33
+ expect(PortalSymbol.toString()).toContain('pyreon.Portal')
34
34
  })
35
35
 
36
- test("string children are stored in props", () => {
37
- const node = Portal({ target: {} as Element, children: "text content" })
36
+ test('string children are stored in props', () => {
37
+ const node = Portal({ target: {} as Element, children: 'text content' })
38
38
  const props = node.props as unknown as { children: string }
39
- expect(props.children).toBe("text content")
39
+ expect(props.children).toBe('text content')
40
40
  })
41
41
 
42
- test("multiple VNode children via fragment", () => {
43
- const children = h("div", null, h("span", null, "a"), h("span", null, "b"))
42
+ test('multiple VNode children via fragment', () => {
43
+ const children = h('div', null, h('span', null, 'a'), h('span', null, 'b'))
44
44
  const node = Portal({ target: {} as Element, children })
45
45
  const props = node.props as unknown as { children: VNode }
46
- expect((props.children as VNode).type).toBe("div")
46
+ expect((props.children as VNode).type).toBe('div')
47
47
  })
48
48
  })