mutts 1.0.0 → 1.0.1

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 (53) hide show
  1. package/dist/chunks/{decorator-BXsign4Z.js → decorator-8qjFb7dw.js} +2 -2
  2. package/dist/chunks/decorator-8qjFb7dw.js.map +1 -0
  3. package/dist/chunks/{decorator-CPbZNnsX.esm.js → decorator-AbRkXM5O.esm.js} +2 -2
  4. package/dist/chunks/decorator-AbRkXM5O.esm.js.map +1 -0
  5. package/dist/decorator.d.ts +1 -1
  6. package/dist/decorator.esm.js +1 -1
  7. package/dist/decorator.js +1 -1
  8. package/dist/destroyable.esm.js +1 -1
  9. package/dist/destroyable.js +1 -1
  10. package/dist/index.d.ts +1 -1
  11. package/dist/index.esm.js +2 -2
  12. package/dist/index.js +2 -1
  13. package/dist/index.js.map +1 -1
  14. package/dist/mutts.umd.js +1 -1
  15. package/dist/mutts.umd.js.map +1 -1
  16. package/dist/mutts.umd.min.js +1 -1
  17. package/dist/mutts.umd.min.js.map +1 -1
  18. package/dist/reactive.d.ts +4 -3
  19. package/dist/reactive.esm.js +61 -57
  20. package/dist/reactive.esm.js.map +1 -1
  21. package/dist/reactive.js +61 -56
  22. package/dist/reactive.js.map +1 -1
  23. package/dist/std-decorators.esm.js +1 -1
  24. package/dist/std-decorators.js +1 -1
  25. package/docs/reactive.md +616 -0
  26. package/package.json +1 -2
  27. package/dist/chunks/decorator-BXsign4Z.js.map +0 -1
  28. package/dist/chunks/decorator-CPbZNnsX.esm.js.map +0 -1
  29. package/src/decorator.test.ts +0 -495
  30. package/src/decorator.ts +0 -205
  31. package/src/destroyable.test.ts +0 -155
  32. package/src/destroyable.ts +0 -158
  33. package/src/eventful.test.ts +0 -380
  34. package/src/eventful.ts +0 -69
  35. package/src/index.ts +0 -7
  36. package/src/indexable.test.ts +0 -388
  37. package/src/indexable.ts +0 -124
  38. package/src/promiseChain.test.ts +0 -201
  39. package/src/promiseChain.ts +0 -99
  40. package/src/reactive/array.test.ts +0 -923
  41. package/src/reactive/array.ts +0 -352
  42. package/src/reactive/core.test.ts +0 -1663
  43. package/src/reactive/core.ts +0 -866
  44. package/src/reactive/index.ts +0 -28
  45. package/src/reactive/interface.test.ts +0 -1477
  46. package/src/reactive/interface.ts +0 -231
  47. package/src/reactive/map.test.ts +0 -866
  48. package/src/reactive/map.ts +0 -162
  49. package/src/reactive/set.test.ts +0 -289
  50. package/src/reactive/set.ts +0 -142
  51. package/src/std-decorators.test.ts +0 -679
  52. package/src/std-decorators.ts +0 -182
  53. package/src/utils.ts +0 -52
@@ -1,388 +0,0 @@
1
- import { getAt, Indexable, setAt } from './indexable'
2
-
3
- // TODO: get/set became this: based
4
- describe('Indexable', () => {
5
- describe('Indexable(base, accessor)', () => {
6
- it('should create indexable class with custom accessor', () => {
7
- class Base {
8
- constructor(public items: string[]) {}
9
- }
10
-
11
- const IndexableBase = Indexable(Base, {
12
- get: function (this: Base, index) {
13
- return this.items[index]
14
- },
15
- set: function (this: Base, index, value) {
16
- this.items[index] = value
17
- },
18
- })
19
- const instance = new IndexableBase(['a', 'b', 'c'])
20
-
21
- expect(instance[0]).toBe('a')
22
- expect(instance[1]).toBe('b')
23
- expect(instance[2]).toBe('c')
24
- expect(instance.items).toEqual(['a', 'b', 'c'])
25
- })
26
-
27
- it('should handle different item types', () => {
28
- class Base {
29
- constructor(public numbers: number[]) {}
30
- }
31
-
32
- const IndexableBase = Indexable(Base, {
33
- get: function (this: Base, index) {
34
- return this.numbers[index] * 2
35
- },
36
- set: function (this: Base, index, value) {
37
- this.numbers[index] = value / 2
38
- },
39
- })
40
- const instance = new IndexableBase([1, 2, 3])
41
-
42
- expect(instance[0]).toBe(2)
43
- expect(instance[1]).toBe(4)
44
- expect(instance[2]).toBe(6)
45
- })
46
-
47
- it('should preserve base class methods', () => {
48
- class Base {
49
- constructor(public items: string[]) {}
50
- getLength() {
51
- return this.items.length
52
- }
53
- }
54
-
55
- const IndexableBase = Indexable(Base, {
56
- get: function (this: Base, index) {
57
- return this.items[index]
58
- },
59
- set: function (this: Base, index, value) {
60
- this.items[index] = value
61
- },
62
- })
63
- const instance = new IndexableBase(['a', 'b'])
64
-
65
- expect(instance.getLength()).toBe(2)
66
- expect(instance[0]).toBe('a')
67
- })
68
- })
69
-
70
- describe('Indexable(base with getAt and setAt methods)', () => {
71
- it('should use the base class getAt and setAt methods', () => {
72
- class Base {
73
- constructor(public items: string[]) {}
74
- [getAt](index: number): string {
75
- return this.items[index]
76
- }
77
- [setAt](index: number, value: string): void {
78
- this.items[index] = value
79
- }
80
- }
81
-
82
- const IndexableBase = Indexable(Base)
83
- const instance = new IndexableBase(['x', 'y', 'z'])
84
-
85
- expect(instance[0]).toBe('x')
86
- instance[0] = 'a'
87
- expect(instance[0]).toBe('a')
88
- expect(instance.items[0]).toBe('a')
89
- })
90
-
91
- it('should work with custom getAt and setAt implementation', () => {
92
- class Base {
93
- constructor(public numbers: number[]) {}
94
- [getAt](index: number): number {
95
- return this.numbers[index] * 3
96
- }
97
- [setAt](index: number, value: number): void {
98
- this.numbers[index] = value / 3
99
- }
100
- }
101
-
102
- const IndexableBase = Indexable(Base)
103
- const instance = new IndexableBase([1, 2, 3])
104
-
105
- expect(instance[0]).toBe(3)
106
- instance[0] = 9
107
- expect(instance[0]).toBe(9)
108
- expect(instance.numbers[0]).toBe(3)
109
- })
110
-
111
- it('should throw when setAt method is missing', () => {
112
- class Base {
113
- constructor(public items: string[]) {}
114
- [getAt](index: number): string {
115
- return this.items[index]
116
- }
117
- // Missing setAt method
118
- }
119
-
120
- const IndexableBase = Indexable(Base)
121
- const instance = new IndexableBase(['x', 'y'])
122
-
123
- expect(instance[0]).toBe('x')
124
- expect(() => {
125
- instance[0] = 'z'
126
- }).toThrow('Indexable class has read-only numeric index access')
127
- })
128
- })
129
-
130
- describe('Indexable()', () => {
131
- it('should create abstract class with abstract getAt method', () => {
132
- const AbstractIndexable = Indexable<string>()
133
-
134
- //@ts-expect-error Should be abstract
135
- void new AbstractIndexable()
136
-
137
- // Should have abstract getAt method
138
- class Concrete extends AbstractIndexable {
139
- constructor(private items: string[]) {
140
- super()
141
- }
142
- [getAt](index: number): string {
143
- return this.items[index]
144
- }
145
- }
146
-
147
- const instance = new Concrete(['p', 'q', 'r'])
148
- expect(instance[0]).toBe('p')
149
- expect(instance[1]).toBe('q')
150
- expect(instance[2]).toBe('r')
151
- })
152
-
153
- it('should enforce getAt method implementation', () => {
154
- const AbstractIndexable = Indexable<number>()
155
- //@ts-expect-error Should be abstract
156
- class Invalid extends AbstractIndexable {}
157
-
158
- // JavaScript doesn't enforce abstract methods at runtime
159
- // So instantiation won't throw, but calling the missing method will
160
- const instance = new Invalid()
161
- expect(() => instance[0]).toThrow()
162
- })
163
- })
164
-
165
- describe('edge cases', () => {
166
- it('should handle out of bounds access', () => {
167
- class Base {
168
- constructor(public items: string[]) {}
169
- }
170
-
171
- const IndexableBase = Indexable(Base, {
172
- get: function (this: Base, index) {
173
- return this.items[index]
174
- },
175
- set: function (this: Base, index, value) {
176
- this.items[index] = value
177
- },
178
- })
179
- const instance = new IndexableBase(['a', 'b'])
180
-
181
- expect(instance[5]).toBeUndefined()
182
- expect(instance[-1]).toBeUndefined()
183
- })
184
-
185
- it('should handle empty arrays', () => {
186
- class Base {
187
- constructor(public items: string[]) {}
188
- }
189
-
190
- const IndexableBase = Indexable(Base, {
191
- get: function (this: Base, index) {
192
- return this.items[index]
193
- },
194
- set: function (this: Base, index, value) {
195
- this.items[index] = value
196
- },
197
- })
198
- const instance = new IndexableBase([])
199
-
200
- expect(instance[0]).toBeUndefined()
201
- })
202
-
203
- it('should not interfere with non-numeric properties', () => {
204
- class Base {
205
- constructor(public items: string[]) {}
206
- length = 42
207
- }
208
-
209
- const IndexableBase = Indexable(Base, {
210
- get: function (this: Base, index) {
211
- return this.items[index]
212
- },
213
- set: function (this: Base, index, value) {
214
- this.items[index] = value
215
- },
216
- })
217
- const instance = new IndexableBase(['a', 'b'])
218
-
219
- expect(instance.length).toBe(42)
220
- expect(instance.items).toEqual(['a', 'b'])
221
- })
222
-
223
- it('should not interfere with setting non-numeric properties', () => {
224
- class Base {
225
- constructor(public items: string[]) {}
226
- length = 42
227
- }
228
-
229
- const IndexableBase = Indexable(Base, {
230
- get: function (this: Base, index) {
231
- return this.items[index]
232
- },
233
- set: function (this: Base, index, value) {
234
- this.items[index] = value
235
- },
236
- })
237
- const instance = new IndexableBase(['a', 'b'])
238
-
239
- instance.length = 100
240
- expect(instance.length).toBe(100)
241
- })
242
- })
243
-
244
- describe('integration tests', () => {
245
- it('should work with complex objects', () => {
246
- class Person {
247
- constructor(
248
- public name: string,
249
- public age: number
250
- ) {}
251
- }
252
-
253
- class PersonList {
254
- constructor(public people: Person[]) {}
255
- [getAt](index: number): Person {
256
- return this.people[index]
257
- }
258
- [setAt](index: number, person: Person): void {
259
- this.people[index] = person
260
- }
261
- }
262
-
263
- const IndexablePersonList = Indexable(PersonList)
264
- const people = [new Person('Alice', 30), new Person('Bob', 25), new Person('Charlie', 35)]
265
- const instance = new IndexablePersonList(people)
266
-
267
- expect((instance[0] as Person).name).toBe('Alice')
268
- instance[0] = new Person('Alice Updated', 31)
269
- expect((instance[0] as Person).name).toBe('Alice Updated')
270
- expect((instance[0] as Person).age).toBe(31)
271
- expect(instance.people[0].name).toBe('Alice Updated')
272
- })
273
-
274
- it('should support multiple inheritance levels', () => {
275
- class Base {
276
- constructor(public items: string[]) {}
277
- }
278
-
279
- class Extended extends Indexable(Base, {
280
- get: function (this: Base, index) {
281
- return this.items[index]
282
- },
283
- set: function (this: Base, index, value) {
284
- this.items[index] = value
285
- },
286
- }) {
287
- constructor(
288
- items: string[],
289
- public extra: string
290
- ) {
291
- super(items)
292
- }
293
- }
294
-
295
- const instance = new Extended(['a', 'b'], 'extra')
296
- expect(instance[0]).toBe('a')
297
- expect(instance.extra).toBe('extra')
298
- })
299
-
300
- it('should support setting with custom logic', () => {
301
- class Base {
302
- constructor(public items: string[]) {}
303
- }
304
-
305
- const IndexableBase = Indexable(Base, {
306
- get: function (this: Base, index) {
307
- return this.items[index]
308
- },
309
- set: function (this: Base, index, value) {
310
- // Custom logic: convert to uppercase
311
- this.items[index] = value.toUpperCase()
312
- },
313
- })
314
- const instance = new IndexableBase(['a', 'b', 'c'])
315
-
316
- instance[1] = 'x'
317
- expect(instance[1]).toBe('X')
318
- expect(instance.items[1]).toBe('X')
319
- })
320
- })
321
-
322
- describe('Indexable(accessor)', () => {
323
- it('should create indexable object with custom accessor', () => {
324
- const IndexableObj = Indexable({
325
- get: function (this: any, index: number) {
326
- return this._arr?.[index]
327
- },
328
- set: function (this: any, index: number, value: any) {
329
- if (!this._arr) this._arr = []
330
- this._arr[index] = value
331
- },
332
- })
333
- const instance = new IndexableObj() as any
334
- instance._arr = ['a', 'b', 'c']
335
- expect(instance[0]).toBe('a')
336
- expect(instance[1]).toBe('b')
337
- expect(instance[2]).toBe('c')
338
- })
339
-
340
- it('should create indexable object with custom getter and setter', () => {
341
- const IndexableObj = Indexable({
342
- get: function (this: any, index: number) {
343
- return this._arr?.[index]
344
- },
345
- set: function (this: any, index: number, value: string) {
346
- if (!this._arr) this._arr = []
347
- this._arr[index] = value
348
- },
349
- })
350
- const instance = new IndexableObj() as any
351
- instance[0] = 'x'
352
- instance[1] = 'y'
353
- expect(instance[0]).toBe('x')
354
- expect(instance[1]).toBe('y')
355
- expect(instance._arr[0]).toBe('x')
356
- expect(instance._arr[1]).toBe('y')
357
- })
358
-
359
- it('should support transformation in setter', () => {
360
- const IndexableObj = Indexable({
361
- get: function (this: any, index: number) {
362
- return this._arr?.[index] * 2
363
- },
364
- set: function (this: any, index: number, value: number) {
365
- if (!this._arr) this._arr = []
366
- this._arr[index] = value / 2
367
- },
368
- })
369
- const instance = new IndexableObj() as any
370
- instance._arr = [1, 2, 3]
371
- instance[0] = 10
372
- expect(instance[0]).toBe(10)
373
- expect(instance._arr[0]).toBe(5)
374
- })
375
-
376
- it('should throw if setter is missing and assignment is attempted', () => {
377
- const IndexableObj = Indexable({
378
- get: function (this: any, index: number) {
379
- return this._arr?.[index]
380
- },
381
- })
382
- const instance = new IndexableObj() as any
383
- expect(() => {
384
- instance[0] = 'fail'
385
- }).toThrow('Indexable class has read-only numeric index access')
386
- })
387
- })
388
- })
package/src/indexable.ts DELETED
@@ -1,124 +0,0 @@
1
- export const getAt = Symbol('getAt')
2
- export const setAt = Symbol('setAt')
3
-
4
- interface IndexingAt<Items = any> {
5
- [getAt](index: number): Items
6
- }
7
-
8
- interface Accessor<T, Items> {
9
- get(this: T, index: number): Items
10
- set?(this: T, index: number, value: Items): void
11
- getLength?(this: T): number
12
- setLength?(this: T, value: number): void
13
- }
14
-
15
- abstract class AbstractGetAt<Items = any> {
16
- abstract [getAt](index: number): Items
17
- }
18
-
19
- export function Indexable<Items, Base extends abstract new (...args: any[]) => any>(
20
- base: Base,
21
- accessor: Accessor<InstanceType<Base>, Items>
22
- ): new (
23
- ...args: ConstructorParameters<Base>
24
- ) => InstanceType<Base> & { [x: number]: Items }
25
-
26
- export function Indexable<Items>(accessor: Accessor<any, Items>): new () => { [x: number]: Items }
27
-
28
- export function Indexable<Base extends new (...args: any[]) => IndexingAt>(
29
- base: Base
30
- ): new (
31
- ...args: ConstructorParameters<Base>
32
- ) => InstanceType<Base> & { [x: number]: AtReturnType<InstanceType<Base>> }
33
-
34
- export function Indexable<Items>(): abstract new (
35
- ...args: any[]
36
- ) => AbstractGetAt & { [x: number]: Items }
37
-
38
- export function Indexable<Items, Base extends abstract new (...args: any[]) => any>(
39
- base?: Base | Accessor<Base, Items>,
40
- accessor?: Accessor<Base, Items>
41
- ) {
42
- if (base && typeof base !== 'function') {
43
- accessor = base as Accessor<Base, Items>
44
- base = undefined
45
- }
46
- if (!base) {
47
- //@ts-expect-error
48
- base = class {} as Base
49
- }
50
- if (!accessor) {
51
- accessor = {
52
- get(this: any, index: number) {
53
- if (typeof this[getAt] !== 'function') {
54
- throw new Error('Indexable class must have an [getAt] method')
55
- }
56
- return this[getAt](index)
57
- },
58
- set(this: any, index: number, value: Items) {
59
- if (typeof this[setAt] !== 'function') {
60
- throw new Error('Indexable class has read-only numeric index access')
61
- }
62
- this[setAt](index, value)
63
- },
64
- }
65
- }
66
-
67
- abstract class Indexable extends (base as Base) {
68
- [x: number]: Items
69
- }
70
-
71
- Object.setPrototypeOf(
72
- Indexable.prototype,
73
- new Proxy((base as Base).prototype, {
74
- //@ts-expect-error
75
- [Symbol.toStringTag]: 'MutTs Indexable',
76
- get(target, prop, receiver) {
77
- if (prop in target) {
78
- const getter = Object.getOwnPropertyDescriptor(target, prop)?.get
79
- return getter ? getter.call(receiver) : target[prop]
80
- }
81
- if (typeof prop === 'string') {
82
- if (prop === 'length' && accessor.getLength) return accessor.getLength.call(receiver)
83
- const numProp = Number(prop)
84
- if (!Number.isNaN(numProp)) {
85
- return accessor.get!.call(receiver, numProp) as Items
86
- }
87
- }
88
- return undefined
89
- },
90
- set(target, prop, value, receiver) {
91
- if (prop in target) {
92
- const setter = Object.getOwnPropertyDescriptor(target, prop)?.set
93
- if (setter) setter.call(receiver, value)
94
- else target[prop] = value
95
- return true
96
- }
97
- if (typeof prop === 'string') {
98
- if (prop === 'length' && accessor.setLength) {
99
- accessor.setLength.call(receiver, value)
100
- return true
101
- }
102
- const numProp = Number(prop)
103
- if (!Number.isNaN(numProp)) {
104
- if (!accessor.set) {
105
- throw new Error('Indexable class has read-only numeric index access')
106
- }
107
- accessor.set!.call(receiver, numProp, value)
108
- return true
109
- }
110
- }
111
- Object.defineProperty(receiver, prop, {
112
- value,
113
- writable: true,
114
- enumerable: true,
115
- configurable: true,
116
- })
117
- return true
118
- },
119
- })
120
- )
121
- return Indexable
122
- }
123
-
124
- type AtReturnType<T> = T extends { [getAt](index: number): infer R } ? R : never
@@ -1,201 +0,0 @@
1
- import { chainPromise } from './promiseChain'
2
-
3
- describe('chain', () => {
4
- describe('basic functionality', () => {
5
- it('should return non-promise values as-is', () => {
6
- const value = { name: 'test', value: 42 }
7
- const result = chainPromise(value)
8
- expect(result).toEqual(value)
9
- })
10
-
11
- it('should chain method calls on resolved promise values', async () => {
12
- const obj = {
13
- getName: () => 'John',
14
- getAge: () => 30,
15
- getInfo: () => ({ name: 'John', age: 30 }),
16
- }
17
- const promise = Promise.resolve(obj)
18
-
19
- const result = await chainPromise(promise).getName()
20
- expect(result).toBe('John')
21
- })
22
-
23
- it('should support multiple method chaining', async () => {
24
- const obj = {
25
- getName: () => 'John',
26
- getAge: () => 30,
27
- getInfo: () => ({ name: 'John', age: 30 }),
28
- }
29
- const promise = Promise.resolve(obj)
30
-
31
- const result = await chainPromise(promise).getInfo().name
32
- expect(result).toBe('John')
33
- })
34
-
35
- it('should handle async methods', async () => {
36
- const obj = {
37
- getName: async () => 'John',
38
- getAge: async () => 30,
39
- }
40
- const promise = Promise.resolve(obj)
41
-
42
- //const result = await chainPromise(promise).getName()
43
- const chained = chainPromise(promise)
44
- const getName = chained.getName
45
- const result = await getName()
46
- expect(result).toBe('John')
47
- })
48
-
49
- it('should handle methods with parameters', async () => {
50
- const obj = {
51
- add: (a: number, b: number) => a + b,
52
- multiply: (a: number, b: number) => a * b,
53
- }
54
- const promise = Promise.resolve(obj)
55
-
56
- const result = await chainPromise(promise).add(5, 3)
57
- expect(result).toBe(8)
58
- })
59
- })
60
-
61
- describe('function calls', () => {
62
- it('should allow calling the resolved value as a function', async () => {
63
- const func = (name: string, age: number) => `${name} is ${age} years old`
64
- const promise = Promise.resolve(func)
65
-
66
- const result = await (chainPromise(promise) as any)('John', 30)
67
- expect(result).toBe('John is 30 years old')
68
- })
69
-
70
- it('should handle async functions', async () => {
71
- const asyncFunc = async (name: string) => `Hello ${name}`
72
- const promise = Promise.resolve(asyncFunc)
73
-
74
- const result = await (chainPromise(promise) as any)('World')
75
- expect(result).toBe('Hello World')
76
- })
77
- })
78
- /*
79
- describe('caching behavior', () => {
80
- it('should return the same proxy for the same promise', () => {
81
- const promise = Promise.resolve({ test: () => 'value' })
82
- const chain1 = chainPromise(promise)
83
- const chain2 = chainPromise(promise)
84
-
85
- expect(chain1).toBe(chain2)
86
- })
87
-
88
- it('should not cache different promises', async () => {
89
- const promise1 = Promise.resolve({ test: () => 'value1' })
90
- const promise2 = Promise.resolve({ test: () => 'value2' })
91
- const chain1 = chainPromise(promise1)
92
- const chain2 = chainPromise(promise2)
93
-
94
- expect(chain1).not.toBe(chain2)
95
- expect(await chain1).not.toBe(await chain2)
96
- })
97
- })*/
98
-
99
- describe('complex scenarios', () => {
100
- it('should handle nested object methods', async () => {
101
- const obj = {
102
- user: {
103
- getName: () => 'John',
104
- getProfile: () => ({
105
- age: 30,
106
- email: 'john@example.com',
107
- }),
108
- },
109
- }
110
- const promise = Promise.resolve(obj)
111
-
112
- const result = await chainPromise(promise).user.getProfile().email
113
- expect(result).toBe('john@example.com')
114
- })
115
-
116
- it('should handle array methods', async () => {
117
- const arr = [1, 2, 3, 4, 5]
118
- const promise = Promise.resolve(arr)
119
-
120
- //const result = await chainPromise(promise).filter(x => x > 2).map(x => x * 2)
121
-
122
- const chained = chainPromise(promise)
123
- const filtered = chained.filter((x) => {
124
- return x > 2
125
- })
126
- const result = filtered.map((x) => {
127
- return x * 2
128
- })
129
- const awaitResult = await result
130
- expect(awaitResult).toEqual([6, 8, 10])
131
- })
132
-
133
- it('should handle methods that return promises', async () => {
134
- const obj = {
135
- getData: () => Promise.resolve({ id: 1, name: 'test' }),
136
- processData: (data: any) => Promise.resolve({ ...data, processed: true }),
137
- }
138
- const promise = Promise.resolve(obj)
139
-
140
- const result = await chainPromise(promise).getData().name
141
- expect(result).toBe('test')
142
- })
143
-
144
- it('should handle methods that return promises recursively', async () => {
145
- const obj = {
146
- getData: () => Promise.resolve({ id: 1, name: 'test' }),
147
- processData: (data: any) => Promise.resolve({ ...data, processed: true }),
148
- }
149
-
150
- //const result = await chainPromise(obj).getData().name
151
- const chain = chainPromise(obj)
152
- const getData = chain.getData
153
- const data = getData()
154
- const result = await data.name
155
- expect(result).toBe('test')
156
- })
157
- })
158
-
159
- describe('error handling', () => {
160
- it('should handle methods that throw errors', async () => {
161
- const obj = {
162
- errorMethod: () => {
163
- throw new Error('Method error')
164
- },
165
- }
166
- const promise = Promise.resolve(obj)
167
-
168
- await expect(chainPromise(promise).errorMethod()).rejects.toThrow('Method error')
169
- })
170
- })
171
-
172
- describe('edge cases', () => {
173
- it('should handle null and undefined values', () => {
174
- expect(chainPromise(null)).toBeNull()
175
- expect(chainPromise(undefined)).toBeUndefined()
176
- })
177
-
178
- it('should handle primitive values', () => {
179
- expect(chainPromise(42)).toBe(42)
180
- expect(chainPromise('string')).toBe('string')
181
- expect(chainPromise(true)).toBe(true)
182
- })
183
-
184
- it('should handle objects without methods', async () => {
185
- const obj = { name: 'test', value: 42 }
186
- const promise = Promise.resolve(obj)
187
-
188
- const result = await chainPromise(promise).name
189
- expect(result).toBe('test')
190
- })
191
-
192
- it('should handle symbols as property names', async () => {
193
- const sym = Symbol('test')
194
- const obj = { [sym]: () => 'symbol value' }
195
- const promise = Promise.resolve(obj)
196
-
197
- const result = await chainPromise(promise)[sym]()
198
- expect(result).toBe('symbol value')
199
- })
200
- })
201
- })