@pyreon/core 0.24.4 → 0.24.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 (56) hide show
  1. package/lib/analysis/index.js.html +1 -1
  2. package/lib/index.js +53 -31
  3. package/package.json +2 -6
  4. package/src/compat-marker.ts +0 -79
  5. package/src/compat-shared.ts +0 -80
  6. package/src/component.ts +0 -98
  7. package/src/context.ts +0 -349
  8. package/src/defer.ts +0 -279
  9. package/src/dynamic.ts +0 -32
  10. package/src/env.d.ts +0 -6
  11. package/src/error-boundary.ts +0 -90
  12. package/src/for.ts +0 -51
  13. package/src/h.ts +0 -80
  14. package/src/index.ts +0 -80
  15. package/src/jsx-dev-runtime.ts +0 -2
  16. package/src/jsx-runtime.ts +0 -747
  17. package/src/lazy.ts +0 -25
  18. package/src/lifecycle.ts +0 -152
  19. package/src/manifest.ts +0 -579
  20. package/src/map-array.ts +0 -42
  21. package/src/portal.ts +0 -39
  22. package/src/props.ts +0 -269
  23. package/src/ref.ts +0 -32
  24. package/src/show.ts +0 -121
  25. package/src/style.ts +0 -102
  26. package/src/suspense.ts +0 -52
  27. package/src/telemetry.ts +0 -120
  28. package/src/tests/compat-marker.test.ts +0 -96
  29. package/src/tests/compat-shared.test.ts +0 -99
  30. package/src/tests/component.test.ts +0 -281
  31. package/src/tests/context.test.ts +0 -629
  32. package/src/tests/core.test.ts +0 -1290
  33. package/src/tests/cx.test.ts +0 -70
  34. package/src/tests/defer.test.ts +0 -359
  35. package/src/tests/dynamic.test.ts +0 -87
  36. package/src/tests/error-boundary.test.ts +0 -181
  37. package/src/tests/extract-props-overloads.types.test.ts +0 -135
  38. package/src/tests/for.test.ts +0 -117
  39. package/src/tests/h.test.ts +0 -221
  40. package/src/tests/jsx-compat.test.tsx +0 -86
  41. package/src/tests/lazy.test.ts +0 -100
  42. package/src/tests/lifecycle.test.ts +0 -350
  43. package/src/tests/manifest-snapshot.test.ts +0 -100
  44. package/src/tests/map-array.test.ts +0 -313
  45. package/src/tests/native-marker-error-boundary.test.ts +0 -12
  46. package/src/tests/portal.test.ts +0 -48
  47. package/src/tests/props-extended.test.ts +0 -157
  48. package/src/tests/props.test.ts +0 -250
  49. package/src/tests/reactive-context.test.ts +0 -69
  50. package/src/tests/reactive-props.test.ts +0 -157
  51. package/src/tests/ref.test.ts +0 -70
  52. package/src/tests/show.test.ts +0 -314
  53. package/src/tests/style.test.ts +0 -157
  54. package/src/tests/suspense.test.ts +0 -139
  55. package/src/tests/telemetry.test.ts +0 -297
  56. package/src/types.ts +0 -116
@@ -1,313 +0,0 @@
1
- import { mapArray } from '../map-array'
2
-
3
- describe('mapArray', () => {
4
- describe('basic mapping', () => {
5
- test('maps all items on first call', () => {
6
- const mapped = mapArray(
7
- () => [1, 2, 3],
8
- (item) => item,
9
- (item) => item * 10,
10
- )
11
- expect(mapped()).toEqual([10, 20, 30])
12
- })
13
-
14
- test('returns empty array for empty source', () => {
15
- const mapped = mapArray(
16
- () => [],
17
- (item: number) => item,
18
- (item) => item * 10,
19
- )
20
- expect(mapped()).toEqual([])
21
- })
22
-
23
- test('maps single item', () => {
24
- const mapped = mapArray(
25
- () => [42],
26
- (item) => item,
27
- (item) => `value-${item}`,
28
- )
29
- expect(mapped()).toEqual(['value-42'])
30
- })
31
- })
32
-
33
- describe('caching behavior', () => {
34
- test('caches results — map function called once per key', () => {
35
- let callCount = 0
36
- const items = [1, 2, 3]
37
- const mapped = mapArray(
38
- () => items,
39
- (item) => item,
40
- (item) => {
41
- callCount++
42
- return item * 10
43
- },
44
- )
45
-
46
- mapped()
47
- expect(callCount).toBe(3)
48
-
49
- // Second call — all cached
50
- mapped()
51
- expect(callCount).toBe(3)
52
-
53
- // Third call — still cached
54
- mapped()
55
- expect(callCount).toBe(3)
56
- })
57
-
58
- test('only maps new keys when items are added', () => {
59
- let callCount = 0
60
- let items = [1, 2, 3]
61
- const mapped = mapArray(
62
- () => items,
63
- (item) => item,
64
- (item) => {
65
- callCount++
66
- return item * 10
67
- },
68
- )
69
-
70
- mapped()
71
- expect(callCount).toBe(3)
72
-
73
- items = [1, 2, 3, 4, 5]
74
- mapped()
75
- expect(callCount).toBe(5) // only 4 and 5 are new
76
- })
77
-
78
- test('does not re-map when items are removed', () => {
79
- let callCount = 0
80
- let items = [1, 2, 3, 4, 5]
81
- const mapped = mapArray(
82
- () => items,
83
- (item) => item,
84
- (item) => {
85
- callCount++
86
- return item * 10
87
- },
88
- )
89
-
90
- mapped()
91
- expect(callCount).toBe(5)
92
-
93
- items = [1, 3, 5] // remove 2 and 4
94
- const result = mapped()
95
- expect(result).toEqual([10, 30, 50])
96
- expect(callCount).toBe(5) // no new calls
97
- })
98
- })
99
-
100
- describe('key eviction', () => {
101
- test('evicted keys are re-mapped when they return', () => {
102
- let callCount = 0
103
- let items = [1, 2, 3]
104
- const mapped = mapArray(
105
- () => items,
106
- (item) => item,
107
- (item) => {
108
- callCount++
109
- return item * 10
110
- },
111
- )
112
-
113
- mapped()
114
- expect(callCount).toBe(3)
115
-
116
- // Remove key 2
117
- items = [1, 3]
118
- mapped()
119
- expect(callCount).toBe(3) // no new mapping
120
-
121
- // Re-add key 2 — should re-map since it was evicted
122
- items = [1, 2, 3]
123
- mapped()
124
- expect(callCount).toBe(4) // key 2 re-mapped
125
- })
126
-
127
- test('evicts all keys when source becomes empty', () => {
128
- let callCount = 0
129
- let items: number[] = [1, 2, 3]
130
- const mapped = mapArray(
131
- () => items,
132
- (item) => item,
133
- (item) => {
134
- callCount++
135
- return item * 10
136
- },
137
- )
138
-
139
- mapped()
140
- expect(callCount).toBe(3)
141
-
142
- items = []
143
- mapped()
144
- expect(callCount).toBe(3)
145
-
146
- // All keys were evicted — re-adding requires re-mapping
147
- items = [1, 2, 3]
148
- mapped()
149
- expect(callCount).toBe(6)
150
- })
151
- })
152
-
153
- describe('reordering', () => {
154
- test('reordered items use cached values (no re-mapping)', () => {
155
- let callCount = 0
156
- let items = [1, 2, 3]
157
- const mapped = mapArray(
158
- () => items,
159
- (item) => item,
160
- (item) => {
161
- callCount++
162
- return item * 10
163
- },
164
- )
165
-
166
- mapped()
167
- expect(callCount).toBe(3)
168
-
169
- items = [3, 1, 2]
170
- const result = mapped()
171
- expect(result).toEqual([30, 10, 20])
172
- expect(callCount).toBe(3) // no new calls
173
- })
174
-
175
- test('reverse order uses cached values', () => {
176
- let callCount = 0
177
- let items = [1, 2, 3, 4]
178
- const mapped = mapArray(
179
- () => items,
180
- (item) => item,
181
- (item) => {
182
- callCount++
183
- return `item-${item}`
184
- },
185
- )
186
-
187
- mapped()
188
- items = [4, 3, 2, 1]
189
- const result = mapped()
190
- expect(result).toEqual(['item-4', 'item-3', 'item-2', 'item-1'])
191
- expect(callCount).toBe(4) // initial 4 only
192
- })
193
- })
194
-
195
- describe('string keys', () => {
196
- test('works with string keys from objects', () => {
197
- interface User {
198
- id: string
199
- name: string
200
- }
201
- let callCount = 0
202
- let users: User[] = [
203
- { id: 'a', name: 'Alice' },
204
- { id: 'b', name: 'Bob' },
205
- ]
206
- const mapped = mapArray(
207
- () => users,
208
- (u) => u.id,
209
- (u) => {
210
- callCount++
211
- return u.name.toUpperCase()
212
- },
213
- )
214
-
215
- expect(mapped()).toEqual(['ALICE', 'BOB'])
216
- expect(callCount).toBe(2)
217
-
218
- // Add new user
219
- users = [
220
- { id: 'a', name: 'Alice' },
221
- { id: 'b', name: 'Bob' },
222
- { id: 'c', name: 'Charlie' },
223
- ]
224
- expect(mapped()).toEqual(['ALICE', 'BOB', 'CHARLIE'])
225
- expect(callCount).toBe(3)
226
- })
227
- })
228
-
229
- describe('mixed additions and removals', () => {
230
- test('simultaneous add and remove', () => {
231
- let callCount = 0
232
- let items = [1, 2, 3]
233
- const mapped = mapArray(
234
- () => items,
235
- (item) => item,
236
- (item) => {
237
- callCount++
238
- return item * 10
239
- },
240
- )
241
-
242
- mapped()
243
- expect(callCount).toBe(3)
244
-
245
- // Remove 2, add 4
246
- items = [1, 3, 4]
247
- const result = mapped()
248
- expect(result).toEqual([10, 30, 40])
249
- expect(callCount).toBe(4) // only key 4 is new
250
- })
251
-
252
- test('complete replacement of all items', () => {
253
- let callCount = 0
254
- let items = [1, 2, 3]
255
- const mapped = mapArray(
256
- () => items,
257
- (item) => item,
258
- (item) => {
259
- callCount++
260
- return item * 10
261
- },
262
- )
263
-
264
- mapped()
265
- expect(callCount).toBe(3)
266
-
267
- items = [4, 5, 6]
268
- const result = mapped()
269
- expect(result).toEqual([40, 50, 60])
270
- expect(callCount).toBe(6) // all new
271
- })
272
- })
273
-
274
- describe('duplicate keys', () => {
275
- test('duplicate keys in source share the same cached value', () => {
276
- let callCount = 0
277
- const mapped = mapArray(
278
- () => [1, 1, 2],
279
- (item) => item,
280
- (item) => {
281
- callCount++
282
- return item * 10
283
- },
284
- )
285
-
286
- const result = mapped()
287
- // Key 1 mapped once, key 2 mapped once
288
- expect(callCount).toBe(2)
289
- // Both occurrences of key 1 get the same cached value
290
- expect(result).toEqual([10, 10, 20])
291
- })
292
- })
293
-
294
- describe('map function receives correct item', () => {
295
- test('map receives the item, not the key', () => {
296
- const received: Array<{ id: number; val: string }> = []
297
- const items = [
298
- { id: 1, val: 'a' },
299
- { id: 2, val: 'b' },
300
- ]
301
- const mapped = mapArray(
302
- () => items,
303
- (item) => item.id,
304
- (item) => {
305
- received.push(item)
306
- return item.val
307
- },
308
- )
309
- mapped()
310
- expect(received).toEqual(items)
311
- })
312
- })
313
- })
@@ -1,12 +0,0 @@
1
- import { describe, expect, it } from 'vitest'
2
- import { isNativeCompat } from '../compat-marker'
3
- import { ErrorBoundary } from '../error-boundary'
4
-
5
- // Marker-presence assertion (PR 3 lock-in). Bisect-verified: removing the
6
- // `nativeCompat(ErrorBoundary)` call fails this test with
7
- // `expected false to be true`.
8
- describe('native-compat marker — @pyreon/core', () => {
9
- it('ErrorBoundary is marked native', () => {
10
- expect(isNativeCompat(ErrorBoundary)).toBe(true)
11
- })
12
- })
@@ -1,48 +0,0 @@
1
- import { h } from '../h'
2
- import { Portal, PortalSymbol } from '../portal'
3
- import type { VNode } from '../types'
4
-
5
- describe('Portal', () => {
6
- test('returns VNode with PortalSymbol type', () => {
7
- const fakeTarget = {} as Element
8
- const node = Portal({ target: fakeTarget, children: h('div', null) })
9
- expect(node.type).toBe(PortalSymbol)
10
- })
11
-
12
- test('VNode has null key', () => {
13
- const node = Portal({ target: {} as Element, children: 'content' })
14
- expect(node.key).toBeNull()
15
- })
16
-
17
- test('VNode has empty children array', () => {
18
- const node = Portal({ target: {} as Element, children: 'content' })
19
- expect(node.children).toEqual([])
20
- })
21
-
22
- test('props contain target and children', () => {
23
- const fakeTarget = {} as Element
24
- const child = h('span', null, 'content')
25
- const node = Portal({ target: fakeTarget, children: child })
26
- const props = node.props as unknown as { target: Element; children: VNode }
27
- expect(props.target).toBe(fakeTarget)
28
- expect(props.children).toBe(child)
29
- })
30
-
31
- test('PortalSymbol is a unique symbol', () => {
32
- expect(typeof PortalSymbol).toBe('symbol')
33
- expect(PortalSymbol.toString()).toContain('pyreon.Portal')
34
- })
35
-
36
- test('string children are stored in props', () => {
37
- const node = Portal({ target: {} as Element, children: 'text content' })
38
- const props = node.props as unknown as { children: string }
39
- expect(props.children).toBe('text content')
40
- })
41
-
42
- test('multiple VNode children via fragment', () => {
43
- const children = h('div', null, h('span', null, 'a'), h('span', null, 'b'))
44
- const node = Portal({ target: {} as Element, children })
45
- const props = node.props as unknown as { children: VNode }
46
- expect((props.children as VNode).type).toBe('div')
47
- })
48
- })
@@ -1,157 +0,0 @@
1
- import { _resetIdCounter, createUniqueId, mergeProps, splitProps } from '../props'
2
-
3
- describe('createUniqueId — extended', () => {
4
- test('returns pyreon- prefixed string', () => {
5
- const id = createUniqueId()
6
- expect(id).toMatch(/^pyreon-\d+$/)
7
- })
8
-
9
- test('returns incrementing values', () => {
10
- const id1 = createUniqueId()
11
- const id2 = createUniqueId()
12
- const id3 = createUniqueId()
13
- const num1 = Number.parseInt(id1.replace('pyreon-', ''), 10)
14
- const num2 = Number.parseInt(id2.replace('pyreon-', ''), 10)
15
- const num3 = Number.parseInt(id3.replace('pyreon-', ''), 10)
16
- expect(num2).toBe(num1 + 1)
17
- expect(num3).toBe(num2 + 1)
18
- })
19
-
20
- test('all IDs are unique', () => {
21
- const ids = new Set<string>()
22
- for (let i = 0; i < 100; i++) {
23
- ids.add(createUniqueId())
24
- }
25
- expect(ids.size).toBe(100)
26
- })
27
- })
28
-
29
- describe('_resetIdCounter', () => {
30
- test('resets the counter so IDs restart', () => {
31
- // Generate some IDs to advance counter
32
- createUniqueId()
33
- createUniqueId()
34
-
35
- _resetIdCounter()
36
-
37
- const id = createUniqueId()
38
- expect(id).toBe('pyreon-1')
39
- })
40
-
41
- test('subsequent calls after reset increment from 1', () => {
42
- _resetIdCounter()
43
- expect(createUniqueId()).toBe('pyreon-1')
44
- expect(createUniqueId()).toBe('pyreon-2')
45
- expect(createUniqueId()).toBe('pyreon-3')
46
- })
47
- })
48
-
49
- describe('splitProps — extended', () => {
50
- test('non-existent keys produce empty picked object', () => {
51
- const props = { a: 1, b: 2 }
52
- const [own, rest] = splitProps(props, ['c' as keyof typeof props])
53
- expect(Object.keys(own)).toEqual([])
54
- expect(rest).toEqual({ a: 1, b: 2 })
55
- })
56
-
57
- test('all keys in picked leaves rest empty', () => {
58
- const props = { x: 10, y: 20 }
59
- const [own, rest] = splitProps(props, ['x', 'y'])
60
- expect(own).toEqual({ x: 10, y: 20 })
61
- expect(Object.keys(rest)).toEqual([])
62
- })
63
-
64
- test('preserves getter on rest side', () => {
65
- let count = 0
66
- const props = {} as Record<string, unknown>
67
- Object.defineProperty(props, 'reactive', {
68
- get: () => ++count,
69
- enumerable: true,
70
- configurable: true,
71
- })
72
- Object.defineProperty(props, 'other', {
73
- value: 'static',
74
- enumerable: true,
75
- configurable: true,
76
- })
77
-
78
- const [_own, rest] = splitProps(props, ['other'])
79
- expect((rest as Record<string, unknown>).reactive).toBe(1)
80
- expect((rest as Record<string, unknown>).reactive).toBe(2) // getter called again
81
- })
82
-
83
- test('handles object with undefined values', () => {
84
- const props = { a: undefined, b: 'defined' }
85
- const [own, rest] = splitProps(props, ['a'])
86
- expect(own.a).toBeUndefined()
87
- expect((rest as Record<string, unknown>).b).toBe('defined')
88
- })
89
- })
90
-
91
- describe('mergeProps — extended', () => {
92
- test('single source returns copy', () => {
93
- const src = { a: 1, b: 2 }
94
- const result = mergeProps(src)
95
- expect(result).toEqual({ a: 1, b: 2 })
96
- expect(result).not.toBe(src) // should be a new object
97
- })
98
-
99
- test('three sources merge correctly', () => {
100
- const result = mergeProps({ a: 1 }, { b: 2 }, { c: 3 })
101
- expect(result).toEqual({ a: 1, b: 2, c: 3 })
102
- })
103
-
104
- test('later defined value overrides earlier', () => {
105
- const result = mergeProps({ x: 'first' }, { x: 'second' }, { x: 'third' })
106
- expect(result.x).toBe('third')
107
- })
108
-
109
- test('undefined in later source does not override earlier defined value', () => {
110
- const result = mergeProps({ x: 'keep' }, { x: undefined as string | undefined })
111
- expect(result.x).toBe('keep')
112
- })
113
-
114
- test('getter merging: later getter overrides earlier static when defined', () => {
115
- let dynamic: string | undefined = 'from-getter'
116
- const getterSrc = {} as Record<string, unknown>
117
- Object.defineProperty(getterSrc, 'val', {
118
- get: () => dynamic,
119
- enumerable: true,
120
- configurable: true,
121
- })
122
- const result = mergeProps({ val: 'static' }, getterSrc)
123
- expect(result.val).toBe('from-getter')
124
-
125
- // When getter returns undefined, falls back to static
126
- dynamic = undefined
127
- expect(result.val).toBe('static')
128
- })
129
-
130
- test('two getters: later getter wins when defined, falls to earlier getter', () => {
131
- let g1val: string | undefined = 'g1'
132
- let g2val: string | undefined = 'g2'
133
-
134
- const src1 = {} as Record<string, unknown>
135
- Object.defineProperty(src1, 'x', {
136
- get: () => g1val,
137
- enumerable: true,
138
- configurable: true,
139
- })
140
-
141
- const src2 = {} as Record<string, unknown>
142
- Object.defineProperty(src2, 'x', {
143
- get: () => g2val,
144
- enumerable: true,
145
- configurable: true,
146
- })
147
-
148
- const result = mergeProps(src1, src2)
149
- expect(result.x).toBe('g2')
150
-
151
- g2val = undefined
152
- expect(result.x).toBe('g1')
153
-
154
- g1val = undefined
155
- expect(result.x).toBeUndefined()
156
- })
157
- })