@tldraw/utils 4.1.0-next.b6dfe9bccde9 → 4.1.0-next.b73a0d46b63f
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/dist-cjs/index.d.ts +1350 -80
- package/dist-cjs/index.js +5 -5
- package/dist-cjs/lib/ExecutionQueue.js +79 -0
- package/dist-cjs/lib/ExecutionQueue.js.map +2 -2
- package/dist-cjs/lib/PerformanceTracker.js +43 -0
- package/dist-cjs/lib/PerformanceTracker.js.map +2 -2
- package/dist-cjs/lib/array.js +3 -1
- package/dist-cjs/lib/array.js.map +2 -2
- package/dist-cjs/lib/bind.js.map +2 -2
- package/dist-cjs/lib/cache.js +27 -5
- package/dist-cjs/lib/cache.js.map +2 -2
- package/dist-cjs/lib/control.js +12 -0
- package/dist-cjs/lib/control.js.map +2 -2
- package/dist-cjs/lib/debounce.js.map +2 -2
- package/dist-cjs/lib/error.js.map +2 -2
- package/dist-cjs/lib/file.js +76 -11
- package/dist-cjs/lib/file.js.map +2 -2
- package/dist-cjs/lib/function.js.map +2 -2
- package/dist-cjs/lib/hash.js.map +2 -2
- package/dist-cjs/lib/id.js.map +2 -2
- package/dist-cjs/lib/iterable.js.map +2 -2
- package/dist-cjs/lib/json-value.js.map +1 -1
- package/dist-cjs/lib/media/apng.js.map +2 -2
- package/dist-cjs/lib/media/avif.js.map +2 -2
- package/dist-cjs/lib/media/gif.js.map +2 -2
- package/dist-cjs/lib/media/media.js +130 -4
- package/dist-cjs/lib/media/media.js.map +2 -2
- package/dist-cjs/lib/media/png.js +141 -0
- package/dist-cjs/lib/media/png.js.map +2 -2
- package/dist-cjs/lib/media/webp.js +1 -0
- package/dist-cjs/lib/media/webp.js.map +2 -2
- package/dist-cjs/lib/network.js.map +2 -2
- package/dist-cjs/lib/number.js.map +2 -2
- package/dist-cjs/lib/object.js +1 -1
- package/dist-cjs/lib/object.js.map +2 -2
- package/dist-cjs/lib/perf.js.map +2 -2
- package/dist-cjs/lib/reordering.js.map +2 -2
- package/dist-cjs/lib/retry.js.map +2 -2
- package/dist-cjs/lib/sort.js.map +2 -2
- package/dist-cjs/lib/storage.js.map +2 -2
- package/dist-cjs/lib/stringEnum.js.map +2 -2
- package/dist-cjs/lib/throttle.js.map +2 -2
- package/dist-cjs/lib/timers.js +103 -4
- package/dist-cjs/lib/timers.js.map +2 -2
- package/dist-cjs/lib/types.js.map +1 -1
- package/dist-cjs/lib/url.js.map +2 -2
- package/dist-cjs/lib/value.js.map +2 -2
- package/dist-cjs/lib/version.js.map +2 -2
- package/dist-cjs/lib/warn.js.map +2 -2
- package/dist-esm/index.d.mts +1350 -80
- package/dist-esm/index.mjs +1 -1
- package/dist-esm/lib/ExecutionQueue.mjs +79 -0
- package/dist-esm/lib/ExecutionQueue.mjs.map +2 -2
- package/dist-esm/lib/PerformanceTracker.mjs +43 -0
- package/dist-esm/lib/PerformanceTracker.mjs.map +2 -2
- package/dist-esm/lib/array.mjs +3 -1
- package/dist-esm/lib/array.mjs.map +2 -2
- package/dist-esm/lib/bind.mjs.map +2 -2
- package/dist-esm/lib/cache.mjs +27 -5
- package/dist-esm/lib/cache.mjs.map +2 -2
- package/dist-esm/lib/control.mjs +12 -0
- package/dist-esm/lib/control.mjs.map +2 -2
- package/dist-esm/lib/debounce.mjs.map +2 -2
- package/dist-esm/lib/error.mjs.map +2 -2
- package/dist-esm/lib/file.mjs +76 -11
- package/dist-esm/lib/file.mjs.map +2 -2
- package/dist-esm/lib/function.mjs.map +2 -2
- package/dist-esm/lib/hash.mjs.map +2 -2
- package/dist-esm/lib/id.mjs.map +2 -2
- package/dist-esm/lib/iterable.mjs.map +2 -2
- package/dist-esm/lib/media/apng.mjs.map +2 -2
- package/dist-esm/lib/media/avif.mjs.map +2 -2
- package/dist-esm/lib/media/gif.mjs.map +2 -2
- package/dist-esm/lib/media/media.mjs +130 -4
- package/dist-esm/lib/media/media.mjs.map +2 -2
- package/dist-esm/lib/media/png.mjs +141 -0
- package/dist-esm/lib/media/png.mjs.map +2 -2
- package/dist-esm/lib/media/webp.mjs +1 -0
- package/dist-esm/lib/media/webp.mjs.map +2 -2
- package/dist-esm/lib/network.mjs.map +2 -2
- package/dist-esm/lib/number.mjs.map +2 -2
- package/dist-esm/lib/object.mjs.map +2 -2
- package/dist-esm/lib/perf.mjs.map +2 -2
- package/dist-esm/lib/reordering.mjs.map +2 -2
- package/dist-esm/lib/retry.mjs.map +2 -2
- package/dist-esm/lib/sort.mjs.map +2 -2
- package/dist-esm/lib/storage.mjs.map +2 -2
- package/dist-esm/lib/stringEnum.mjs.map +2 -2
- package/dist-esm/lib/throttle.mjs.map +2 -2
- package/dist-esm/lib/timers.mjs +103 -4
- package/dist-esm/lib/timers.mjs.map +2 -2
- package/dist-esm/lib/url.mjs.map +2 -2
- package/dist-esm/lib/value.mjs.map +2 -2
- package/dist-esm/lib/version.mjs.map +2 -2
- package/dist-esm/lib/warn.mjs.map +2 -2
- package/package.json +1 -1
- package/src/lib/ExecutionQueue.test.ts +162 -20
- package/src/lib/ExecutionQueue.ts +110 -1
- package/src/lib/PerformanceTracker.test.ts +124 -0
- package/src/lib/PerformanceTracker.ts +63 -1
- package/src/lib/array.test.ts +263 -1
- package/src/lib/array.ts +183 -14
- package/src/lib/bind.test.ts +47 -0
- package/src/lib/bind.ts +69 -4
- package/src/lib/cache.test.ts +73 -0
- package/src/lib/cache.ts +47 -6
- package/src/lib/control.test.ts +50 -0
- package/src/lib/control.ts +198 -9
- package/src/lib/debounce.ts +28 -3
- package/src/lib/error.test.ts +60 -0
- package/src/lib/error.ts +27 -1
- package/src/lib/file.test.ts +49 -0
- package/src/lib/file.ts +117 -12
- package/src/lib/function.ts +11 -0
- package/src/lib/hash.test.ts +99 -0
- package/src/lib/hash.ts +69 -2
- package/src/lib/id.test.ts +32 -0
- package/src/lib/id.ts +53 -5
- package/src/lib/iterable.test.ts +25 -0
- package/src/lib/iterable.ts +4 -5
- package/src/lib/json-value.ts +71 -4
- package/src/lib/media/apng.test.ts +67 -0
- package/src/lib/media/apng.ts +38 -21
- package/src/lib/media/avif.test.ts +26 -0
- package/src/lib/media/avif.ts +34 -0
- package/src/lib/media/gif.test.ts +52 -0
- package/src/lib/media/gif.ts +25 -2
- package/src/lib/media/media.test.ts +58 -0
- package/src/lib/media/media.ts +220 -11
- package/src/lib/media/png.ts +162 -1
- package/src/lib/media/webp.test.ts +81 -0
- package/src/lib/media/webp.ts +33 -1
- package/src/lib/network.test.ts +38 -0
- package/src/lib/network.ts +6 -0
- package/src/lib/number.test.ts +74 -0
- package/src/lib/number.ts +29 -5
- package/src/lib/object.test.ts +236 -0
- package/src/lib/object.ts +194 -14
- package/src/lib/perf.ts +75 -3
- package/src/lib/reordering.test.ts +168 -0
- package/src/lib/reordering.ts +62 -4
- package/src/lib/retry.test.ts +77 -0
- package/src/lib/retry.ts +47 -1
- package/src/lib/sort.test.ts +36 -0
- package/src/lib/sort.ts +22 -1
- package/src/lib/storage.test.ts +130 -0
- package/src/lib/storage.tsx +54 -8
- package/src/lib/stringEnum.ts +20 -1
- package/src/lib/throttle.ts +46 -8
- package/src/lib/timers.test.ts +75 -0
- package/src/lib/timers.ts +124 -5
- package/src/lib/types.ts +126 -4
- package/src/lib/url.test.ts +44 -0
- package/src/lib/url.ts +40 -1
- package/src/lib/value.test.ts +102 -0
- package/src/lib/value.ts +67 -3
- package/src/lib/version.test.ts +494 -56
- package/src/lib/version.ts +36 -1
- package/src/lib/warn.test.ts +64 -0
- package/src/lib/warn.ts +43 -2
package/src/lib/version.test.ts
CHANGED
|
@@ -1,83 +1,521 @@
|
|
|
1
|
-
import { vi } from 'vitest'
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
2
|
import { clearRegisteredVersionsForTests, registerTldrawLibraryVersion } from './version'
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
describe('version utilities', () => {
|
|
5
|
+
let mockConsoleLog: ReturnType<typeof vi.fn>
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
mockConsoleLog = vi.fn()
|
|
9
|
+
vi.stubGlobal('console', { log: mockConsoleLog })
|
|
10
|
+
vi.useFakeTimers()
|
|
8
11
|
clearRegisteredVersionsForTests()
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
vi.unstubAllGlobals()
|
|
9
16
|
vi.restoreAllMocks()
|
|
17
|
+
vi.useRealTimers()
|
|
18
|
+
clearRegisteredVersionsForTests()
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
describe('clearRegisteredVersionsForTests', () => {
|
|
22
|
+
it('should clear all registered versions', () => {
|
|
23
|
+
registerTldrawLibraryVersion('@tldraw/editor', '2.0.0', 'esm')
|
|
24
|
+
registerTldrawLibraryVersion('@tldraw/tldraw', '2.0.0', 'esm')
|
|
25
|
+
|
|
26
|
+
clearRegisteredVersionsForTests()
|
|
27
|
+
|
|
28
|
+
// After clearing, registering the same versions should not trigger warnings
|
|
29
|
+
registerTldrawLibraryVersion('@tldraw/editor', '2.0.0', 'esm')
|
|
30
|
+
registerTldrawLibraryVersion('@tldraw/tldraw', '2.0.0', 'esm')
|
|
31
|
+
|
|
32
|
+
vi.runAllTimers()
|
|
33
|
+
|
|
34
|
+
expect(mockConsoleLog).not.toHaveBeenCalled()
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('should reset warning state', () => {
|
|
38
|
+
// Register conflicting versions to trigger warning
|
|
39
|
+
registerTldrawLibraryVersion('@tldraw/editor', '2.0.0', 'esm')
|
|
40
|
+
registerTldrawLibraryVersion('@tldraw/editor', '1.9.0', 'esm')
|
|
41
|
+
|
|
42
|
+
vi.runAllTimers()
|
|
43
|
+
|
|
44
|
+
expect(mockConsoleLog).toHaveBeenCalled()
|
|
45
|
+
mockConsoleLog.mockClear()
|
|
46
|
+
|
|
47
|
+
clearRegisteredVersionsForTests()
|
|
48
|
+
|
|
49
|
+
// After clearing, same conflicting versions should trigger warning again
|
|
50
|
+
registerTldrawLibraryVersion('@tldraw/editor', '2.0.0', 'esm')
|
|
51
|
+
registerTldrawLibraryVersion('@tldraw/editor', '1.9.0', 'esm')
|
|
52
|
+
|
|
53
|
+
vi.runAllTimers()
|
|
54
|
+
|
|
55
|
+
expect(mockConsoleLog).toHaveBeenCalled()
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('should clear scheduled timeout', () => {
|
|
59
|
+
const clearTimeoutSpy = vi.spyOn(global, 'clearTimeout')
|
|
60
|
+
registerTldrawLibraryVersion('@tldraw/editor', '2.0.0', 'esm')
|
|
61
|
+
|
|
62
|
+
clearRegisteredVersionsForTests()
|
|
63
|
+
|
|
64
|
+
expect(clearTimeoutSpy).toHaveBeenCalled()
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('should handle multiple calls without error', () => {
|
|
68
|
+
clearRegisteredVersionsForTests()
|
|
69
|
+
clearRegisteredVersionsForTests()
|
|
70
|
+
clearRegisteredVersionsForTests()
|
|
71
|
+
|
|
72
|
+
expect(() => clearRegisteredVersionsForTests()).not.toThrow()
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('should handle clearing when no timeout is scheduled', () => {
|
|
76
|
+
const _clearTimeoutSpy = vi.spyOn(global, 'clearTimeout')
|
|
77
|
+
clearRegisteredVersionsForTests()
|
|
78
|
+
|
|
79
|
+
// Should not throw when clearing empty state
|
|
80
|
+
expect(() => clearRegisteredVersionsForTests()).not.toThrow()
|
|
81
|
+
})
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
describe('registerTldrawLibraryVersion', () => {
|
|
85
|
+
it('should register a library version with valid parameters', () => {
|
|
86
|
+
expect(() => registerTldrawLibraryVersion('@tldraw/editor', '2.0.0', 'esm')).not.toThrow()
|
|
87
|
+
|
|
88
|
+
// Should schedule a timeout check
|
|
89
|
+
expect(vi.getTimerCount()).toBeGreaterThan(0)
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
it('should handle missing name parameter', () => {
|
|
93
|
+
expect(() => registerTldrawLibraryVersion(undefined, '2.0.0', 'esm')).not.toThrow()
|
|
94
|
+
|
|
95
|
+
// Should not schedule timeout when parameters are missing
|
|
96
|
+
expect(vi.getTimerCount()).toBe(0)
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
it('should handle missing version parameter', () => {
|
|
100
|
+
expect(() => registerTldrawLibraryVersion('@tldraw/editor', undefined, 'esm')).not.toThrow()
|
|
101
|
+
|
|
102
|
+
expect(vi.getTimerCount()).toBe(0)
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it('should handle missing modules parameter', () => {
|
|
106
|
+
expect(() => registerTldrawLibraryVersion('@tldraw/editor', '2.0.0', undefined)).not.toThrow()
|
|
107
|
+
|
|
108
|
+
expect(vi.getTimerCount()).toBe(0)
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
it('should handle all missing parameters', () => {
|
|
112
|
+
expect(() => registerTldrawLibraryVersion()).not.toThrow()
|
|
113
|
+
|
|
114
|
+
expect(vi.getTimerCount()).toBe(0)
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
it('should throw error in build environment when parameters missing', () => {
|
|
118
|
+
const originalBuildFlag = (globalThis as any).TLDRAW_LIBRARY_IS_BUILD
|
|
119
|
+
;(globalThis as any).TLDRAW_LIBRARY_IS_BUILD = true
|
|
120
|
+
|
|
121
|
+
expect(() => registerTldrawLibraryVersion(undefined, '2.0.0', 'esm')).toThrow(
|
|
122
|
+
'Missing name/version/module system in built version of tldraw library'
|
|
123
|
+
)
|
|
124
|
+
;(globalThis as any).TLDRAW_LIBRARY_IS_BUILD = originalBuildFlag
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
it('should schedule timeout check on first registration', () => {
|
|
128
|
+
registerTldrawLibraryVersion('@tldraw/editor', '2.0.0', 'esm')
|
|
129
|
+
|
|
130
|
+
expect(vi.getTimerCount()).toBe(1)
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
it('should not schedule multiple timeouts for multiple registrations', () => {
|
|
134
|
+
registerTldrawLibraryVersion('@tldraw/editor', '2.0.0', 'esm')
|
|
135
|
+
registerTldrawLibraryVersion('@tldraw/tldraw', '2.0.0', 'esm')
|
|
136
|
+
|
|
137
|
+
expect(vi.getTimerCount()).toBe(1)
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
it('should handle setTimeout failure gracefully', () => {
|
|
141
|
+
vi.spyOn(global, 'setTimeout').mockImplementation(() => {
|
|
142
|
+
throw new Error('setTimeout not available')
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
// Should call check immediately when setTimeout fails
|
|
146
|
+
registerTldrawLibraryVersion('@tldraw/editor', '2.0.0', 'esm')
|
|
147
|
+
registerTldrawLibraryVersion('@tldraw/editor', '1.9.0', 'esm')
|
|
148
|
+
|
|
149
|
+
expect(mockConsoleLog).toHaveBeenCalled()
|
|
150
|
+
})
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
describe('version conflict detection', () => {
|
|
154
|
+
it('should detect version conflicts between different versions', () => {
|
|
155
|
+
registerTldrawLibraryVersion('@tldraw/editor', '2.0.0', 'esm')
|
|
156
|
+
registerTldrawLibraryVersion('@tldraw/editor', '1.9.0', 'esm')
|
|
157
|
+
|
|
158
|
+
vi.runAllTimers()
|
|
159
|
+
|
|
160
|
+
expect(mockConsoleLog).toHaveBeenCalled()
|
|
161
|
+
const logMessage = mockConsoleLog.mock.calls[0][0]
|
|
162
|
+
expect(logMessage).toContain('multiple versions of tldraw libraries')
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
it('should not warn for identical versions', () => {
|
|
166
|
+
registerTldrawLibraryVersion('@tldraw/editor', '2.0.0', 'esm')
|
|
167
|
+
registerTldrawLibraryVersion('@tldraw/tldraw', '2.0.0', 'esm')
|
|
168
|
+
|
|
169
|
+
vi.runAllTimers()
|
|
170
|
+
|
|
171
|
+
expect(mockConsoleLog).not.toHaveBeenCalled()
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
it('should detect module type conflicts', () => {
|
|
175
|
+
registerTldrawLibraryVersion('@tldraw/editor', '2.0.0', 'esm')
|
|
176
|
+
registerTldrawLibraryVersion('@tldraw/editor', '2.0.0', 'cjs')
|
|
177
|
+
|
|
178
|
+
vi.runAllTimers()
|
|
179
|
+
|
|
180
|
+
expect(mockConsoleLog).toHaveBeenCalled()
|
|
181
|
+
const logMessage = mockConsoleLog.mock.calls[0][0]
|
|
182
|
+
expect(logMessage).toContain('multiple instances')
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
it('should warn only once per session', () => {
|
|
186
|
+
registerTldrawLibraryVersion('@tldraw/editor', '2.0.0', 'esm')
|
|
187
|
+
registerTldrawLibraryVersion('@tldraw/editor', '1.9.0', 'esm')
|
|
188
|
+
|
|
189
|
+
vi.runAllTimers()
|
|
190
|
+
|
|
191
|
+
expect(mockConsoleLog).toHaveBeenCalledTimes(1)
|
|
192
|
+
|
|
193
|
+
// Register more conflicting versions
|
|
194
|
+
registerTldrawLibraryVersion('@tldraw/tldraw', '1.8.0', 'esm')
|
|
195
|
+
|
|
196
|
+
// Should not trigger another warning since didWarn is true
|
|
197
|
+
expect(mockConsoleLog).toHaveBeenCalledTimes(1)
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
it('should handle complex version scenarios', () => {
|
|
201
|
+
registerTldrawLibraryVersion('@tldraw/editor', '2.0.0', 'esm')
|
|
202
|
+
registerTldrawLibraryVersion('@tldraw/tldraw', '2.0.0', 'esm')
|
|
203
|
+
registerTldrawLibraryVersion('@tldraw/store', '1.9.0', 'esm')
|
|
204
|
+
registerTldrawLibraryVersion('@tldraw/validate', '2.0.0', 'esm')
|
|
205
|
+
|
|
206
|
+
vi.runAllTimers()
|
|
207
|
+
|
|
208
|
+
expect(mockConsoleLog).toHaveBeenCalled()
|
|
209
|
+
const logMessage = mockConsoleLog.mock.calls[0][0]
|
|
210
|
+
expect(logMessage).toContain('multiple versions')
|
|
211
|
+
expect(logMessage).toContain('v2.0.0')
|
|
212
|
+
expect(logMessage).toContain('@tldraw/store')
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
it('should handle pre-release versions', () => {
|
|
216
|
+
registerTldrawLibraryVersion('@tldraw/editor', '2.0.0-alpha.1', 'esm')
|
|
217
|
+
registerTldrawLibraryVersion('@tldraw/editor', '2.0.0-beta.1', 'esm')
|
|
218
|
+
|
|
219
|
+
vi.runAllTimers()
|
|
220
|
+
|
|
221
|
+
expect(mockConsoleLog).toHaveBeenCalled()
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
it('should sort versions correctly', () => {
|
|
225
|
+
registerTldrawLibraryVersion('@tldraw/editor', '1.0.0', 'esm')
|
|
226
|
+
registerTldrawLibraryVersion('@tldraw/editor', '2.0.0', 'esm')
|
|
227
|
+
registerTldrawLibraryVersion('@tldraw/editor', '1.5.0', 'esm')
|
|
228
|
+
|
|
229
|
+
vi.runAllTimers()
|
|
230
|
+
|
|
231
|
+
expect(mockConsoleLog).toHaveBeenCalled()
|
|
232
|
+
const logMessage = mockConsoleLog.mock.calls[0][0]
|
|
233
|
+
// Latest version should be 2.0.0
|
|
234
|
+
expect(logMessage).toContain('v2.0.0')
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
it('should handle invalid version formats gracefully', () => {
|
|
238
|
+
registerTldrawLibraryVersion('@tldraw/editor', 'invalid-version', 'esm')
|
|
239
|
+
registerTldrawLibraryVersion('@tldraw/editor', 'also-invalid', 'esm')
|
|
240
|
+
|
|
241
|
+
expect(() => vi.runAllTimers()).not.toThrow()
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
it('should handle empty version list', () => {
|
|
245
|
+
registerTldrawLibraryVersion('@tldraw/editor', '2.0.0', 'esm')
|
|
246
|
+
|
|
247
|
+
// Clear versions but let timer run
|
|
248
|
+
clearRegisteredVersionsForTests()
|
|
249
|
+
|
|
250
|
+
expect(() => vi.runAllTimers()).not.toThrow()
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
it('should format console output with colors', () => {
|
|
254
|
+
registerTldrawLibraryVersion('@tldraw/editor', '2.0.0', 'esm')
|
|
255
|
+
registerTldrawLibraryVersion('@tldraw/editor', '1.9.0', 'esm')
|
|
256
|
+
|
|
257
|
+
vi.runAllTimers()
|
|
258
|
+
|
|
259
|
+
expect(mockConsoleLog).toHaveBeenCalled()
|
|
260
|
+
const logMessage = mockConsoleLog.mock.calls[0][0]
|
|
261
|
+
// Check for ANSI color codes
|
|
262
|
+
expect(logMessage).toContain('\x1B[')
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
it('should show both version mismatches and module duplicates in output', () => {
|
|
266
|
+
registerTldrawLibraryVersion('@tldraw/editor', '2.0.0', 'esm')
|
|
267
|
+
registerTldrawLibraryVersion('@tldraw/tldraw', '1.9.0', 'esm')
|
|
268
|
+
|
|
269
|
+
vi.runAllTimers()
|
|
270
|
+
|
|
271
|
+
expect(mockConsoleLog).toHaveBeenCalled()
|
|
272
|
+
const logMessage = mockConsoleLog.mock.calls[0][0]
|
|
273
|
+
expect(logMessage).toContain('latest version')
|
|
274
|
+
expect(logMessage).toContain('not on the latest version')
|
|
275
|
+
})
|
|
10
276
|
})
|
|
11
277
|
|
|
12
|
-
|
|
13
|
-
|
|
278
|
+
describe('integration scenarios', () => {
|
|
279
|
+
it('should handle typical multi-package setup', () => {
|
|
280
|
+
const packages = [
|
|
281
|
+
{ name: '@tldraw/editor', version: '2.0.0', modules: 'esm' },
|
|
282
|
+
{ name: '@tldraw/tldraw', version: '2.0.0', modules: 'esm' },
|
|
283
|
+
{ name: '@tldraw/store', version: '2.0.0', modules: 'esm' },
|
|
284
|
+
{ name: '@tldraw/validate', version: '2.0.0', modules: 'esm' },
|
|
285
|
+
]
|
|
286
|
+
|
|
287
|
+
packages.forEach((pkg) => {
|
|
288
|
+
registerTldrawLibraryVersion(pkg.name, pkg.version, pkg.modules)
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
vi.runAllTimers()
|
|
292
|
+
|
|
293
|
+
// All same version - should not warn
|
|
294
|
+
expect(mockConsoleLog).not.toHaveBeenCalled()
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
it('should handle package manager deduplication scenario', () => {
|
|
298
|
+
// The current implementation actually treats multiple registrations of the same
|
|
299
|
+
// package+version+modules as duplicates, so this will trigger a warning
|
|
300
|
+
registerTldrawLibraryVersion('@tldraw/editor', '2.0.0', 'esm')
|
|
301
|
+
registerTldrawLibraryVersion('@tldraw/editor', '2.0.0', 'esm')
|
|
302
|
+
registerTldrawLibraryVersion('@tldraw/editor', '2.0.0', 'esm')
|
|
303
|
+
|
|
304
|
+
vi.runAllTimers()
|
|
305
|
+
|
|
306
|
+
// Multiple registrations of same version are detected as module duplicates
|
|
307
|
+
expect(mockConsoleLog).toHaveBeenCalled()
|
|
308
|
+
const logMessage = mockConsoleLog.mock.calls[0][0]
|
|
309
|
+
expect(logMessage).toContain('multiple instances')
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
it('should handle bundler misconfiguration scenario', () => {
|
|
313
|
+
registerTldrawLibraryVersion('@tldraw/editor', '2.0.0', 'esm')
|
|
314
|
+
registerTldrawLibraryVersion('@tldraw/editor', '2.0.0', 'cjs')
|
|
315
|
+
|
|
316
|
+
vi.runAllTimers()
|
|
317
|
+
|
|
318
|
+
expect(mockConsoleLog).toHaveBeenCalled()
|
|
319
|
+
const logMessage = mockConsoleLog.mock.calls[0][0]
|
|
320
|
+
expect(logMessage).toContain('bundler is misconfigured')
|
|
321
|
+
expect(logMessage).toContain('ES Modules')
|
|
322
|
+
expect(logMessage).toContain('CommonJS')
|
|
323
|
+
})
|
|
324
|
+
|
|
325
|
+
it('should handle mixed version and module conflicts', () => {
|
|
326
|
+
registerTldrawLibraryVersion('@tldraw/editor', '2.0.0', 'esm')
|
|
327
|
+
registerTldrawLibraryVersion('@tldraw/editor', '2.0.0', 'cjs')
|
|
328
|
+
registerTldrawLibraryVersion('@tldraw/tldraw', '1.9.0', 'esm')
|
|
329
|
+
|
|
330
|
+
vi.runAllTimers()
|
|
331
|
+
|
|
332
|
+
expect(mockConsoleLog).toHaveBeenCalled()
|
|
333
|
+
const logMessage = mockConsoleLog.mock.calls[0][0]
|
|
334
|
+
// Should show version conflict first (higher priority)
|
|
335
|
+
expect(logMessage).toContain('multiple versions')
|
|
336
|
+
})
|
|
14
337
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
registerTldrawLibraryVersion('@tldraw/tlschema', '1.0.0', 'esm')
|
|
338
|
+
it('should handle gradual package registration', () => {
|
|
339
|
+
// Register packages over time
|
|
340
|
+
registerTldrawLibraryVersion('@tldraw/editor', '2.0.0', 'esm')
|
|
19
341
|
|
|
20
|
-
|
|
342
|
+
vi.runAllTimers()
|
|
21
343
|
|
|
22
|
-
|
|
344
|
+
expect(mockConsoleLog).not.toHaveBeenCalled()
|
|
345
|
+
|
|
346
|
+
// Clear warning state and add conflicting version
|
|
347
|
+
clearRegisteredVersionsForTests()
|
|
348
|
+
registerTldrawLibraryVersion('@tldraw/editor', '2.0.0', 'esm')
|
|
349
|
+
registerTldrawLibraryVersion('@tldraw/tldraw', '1.9.0', 'esm')
|
|
350
|
+
|
|
351
|
+
vi.runAllTimers()
|
|
352
|
+
|
|
353
|
+
expect(mockConsoleLog).toHaveBeenCalled()
|
|
354
|
+
})
|
|
355
|
+
})
|
|
356
|
+
|
|
357
|
+
describe('edge cases and error handling', () => {
|
|
358
|
+
it('should handle extremely long package names', () => {
|
|
359
|
+
const longName = '@tldraw/' + 'x'.repeat(1000)
|
|
360
|
+
|
|
361
|
+
expect(() => registerTldrawLibraryVersion(longName, '2.0.0', 'esm')).not.toThrow()
|
|
362
|
+
})
|
|
363
|
+
|
|
364
|
+
it('should handle special characters in package names', () => {
|
|
365
|
+
const specialName = '@tldraw/test-特殊字符-🎨'
|
|
366
|
+
|
|
367
|
+
expect(() => registerTldrawLibraryVersion(specialName, '2.0.0', 'esm')).not.toThrow()
|
|
368
|
+
})
|
|
369
|
+
|
|
370
|
+
it('should handle unusual version strings', () => {
|
|
371
|
+
const versions = [
|
|
372
|
+
'2.0.0-alpha.1',
|
|
373
|
+
'2.0.0-beta.1+build.123',
|
|
374
|
+
'2.0.0-rc.1',
|
|
375
|
+
'0.0.0',
|
|
376
|
+
'999.999.999',
|
|
377
|
+
]
|
|
378
|
+
|
|
379
|
+
versions.forEach((version) => {
|
|
380
|
+
expect(() => registerTldrawLibraryVersion('@tldraw/test', version, 'esm')).not.toThrow()
|
|
381
|
+
})
|
|
382
|
+
})
|
|
383
|
+
|
|
384
|
+
it('should handle unusual module system values', () => {
|
|
385
|
+
const moduleSystems = ['esm', 'cjs', 'umd', 'amd', 'custom']
|
|
386
|
+
|
|
387
|
+
moduleSystems.forEach((modules) => {
|
|
388
|
+
expect(() => registerTldrawLibraryVersion('@tldraw/test', '2.0.0', modules)).not.toThrow()
|
|
389
|
+
})
|
|
390
|
+
})
|
|
391
|
+
|
|
392
|
+
it('should handle very large number of registrations', () => {
|
|
393
|
+
for (let i = 0; i < 100; i++) {
|
|
394
|
+
registerTldrawLibraryVersion(`@tldraw/package-${i}`, '2.0.0', 'esm')
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
expect(() => vi.runAllTimers()).not.toThrow()
|
|
398
|
+
})
|
|
399
|
+
|
|
400
|
+
it('should handle global object pollution', () => {
|
|
401
|
+
// This test checks that the system is robust against different global contexts
|
|
402
|
+
// We can't easily pollute the global object due to property descriptors,
|
|
403
|
+
// but we can test that the system works with different global contexts
|
|
404
|
+
expect(() => registerTldrawLibraryVersion('@tldraw/editor', '2.0.0', 'esm')).not.toThrow()
|
|
405
|
+
|
|
406
|
+
// Register a different library to verify state tracking works
|
|
407
|
+
expect(() => registerTldrawLibraryVersion('@tldraw/tldraw', '2.0.0', 'esm')).not.toThrow()
|
|
408
|
+
|
|
409
|
+
vi.runAllTimers()
|
|
410
|
+
|
|
411
|
+
// Should not warn for same versions
|
|
412
|
+
expect(mockConsoleLog).not.toHaveBeenCalled()
|
|
413
|
+
})
|
|
23
414
|
})
|
|
24
415
|
|
|
25
|
-
|
|
26
|
-
|
|
416
|
+
describe('timeout behavior', () => {
|
|
417
|
+
it('should use 100ms delay for setTimeout', () => {
|
|
418
|
+
const setTimeoutSpy = vi.spyOn(global, 'setTimeout')
|
|
419
|
+
registerTldrawLibraryVersion('@tldraw/editor', '2.0.0', 'esm')
|
|
420
|
+
|
|
421
|
+
expect(setTimeoutSpy).toHaveBeenCalledWith(expect.any(Function), 100)
|
|
422
|
+
})
|
|
423
|
+
|
|
424
|
+
it('should clear timeout after execution', () => {
|
|
425
|
+
registerTldrawLibraryVersion('@tldraw/editor', '2.0.0', 'esm')
|
|
426
|
+
|
|
427
|
+
vi.runAllTimers()
|
|
428
|
+
|
|
429
|
+
// The timeout should be nulled after execution
|
|
430
|
+
// This is internal behavior but we can test by checking that scheduling works again
|
|
431
|
+
registerTldrawLibraryVersion('@tldraw/tldraw', '2.0.0', 'esm')
|
|
432
|
+
|
|
433
|
+
expect(vi.getTimerCount()).toBe(1)
|
|
434
|
+
})
|
|
27
435
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
registerTldrawLibraryVersion('@tldraw/tlschema', '1.2.0', 'esm')
|
|
436
|
+
it('should not schedule new timeout if one is already pending', () => {
|
|
437
|
+
registerTldrawLibraryVersion('@tldraw/editor', '2.0.0', 'esm')
|
|
438
|
+
registerTldrawLibraryVersion('@tldraw/tldraw', '2.0.0', 'esm')
|
|
439
|
+
registerTldrawLibraryVersion('@tldraw/store', '2.0.0', 'esm')
|
|
33
440
|
|
|
34
|
-
|
|
441
|
+
expect(vi.getTimerCount()).toBe(1)
|
|
442
|
+
})
|
|
35
443
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"[1;41;97m[tldraw][m [31;1mYou have multiple versions of tldraw libraries installed. This can lead to bugs and unexpected behavior.[m
|
|
444
|
+
it('should handle timeout clearing correctly', () => {
|
|
445
|
+
const clearTimeoutSpy = vi.spyOn(global, 'clearTimeout')
|
|
446
|
+
registerTldrawLibraryVersion('@tldraw/editor', '2.0.0', 'esm')
|
|
40
447
|
|
|
41
|
-
|
|
42
|
-
• ✅ [1m@tldraw/tlschema[m
|
|
448
|
+
clearRegisteredVersionsForTests()
|
|
43
449
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
• ❌ [1m@tldraw/editor[m ([31mv1.1.0[m)
|
|
47
|
-
• ❌ [1m@tldraw/utils[m ([31mv1.1.0[m, [32mv1.2.0[m)",
|
|
48
|
-
]
|
|
49
|
-
`)
|
|
450
|
+
expect(clearTimeoutSpy).toHaveBeenCalled()
|
|
451
|
+
})
|
|
50
452
|
})
|
|
51
453
|
|
|
52
|
-
|
|
53
|
-
|
|
454
|
+
describe('original test cases (updated)', () => {
|
|
455
|
+
it('doesnt log anything if all versions are the same', () => {
|
|
456
|
+
registerTldrawLibraryVersion('tldraw', '1.0.0', 'esm')
|
|
457
|
+
registerTldrawLibraryVersion('@tldraw/editor', '1.0.0', 'esm')
|
|
458
|
+
registerTldrawLibraryVersion('@tldraw/utils', '1.0.0', 'esm')
|
|
459
|
+
registerTldrawLibraryVersion('@tldraw/tlschema', '1.0.0', 'esm')
|
|
460
|
+
|
|
461
|
+
vi.runAllTimers()
|
|
462
|
+
|
|
463
|
+
expect(mockConsoleLog).toHaveBeenCalledTimes(0)
|
|
464
|
+
})
|
|
465
|
+
|
|
466
|
+
it('logs if not all versions match', () => {
|
|
467
|
+
registerTldrawLibraryVersion('tldraw', '1.0.0', 'esm')
|
|
468
|
+
registerTldrawLibraryVersion('@tldraw/editor', '1.1.0', 'esm')
|
|
469
|
+
registerTldrawLibraryVersion('@tldraw/utils', '1.1.0', 'esm')
|
|
470
|
+
registerTldrawLibraryVersion('@tldraw/utils', '1.2.0', 'esm')
|
|
471
|
+
registerTldrawLibraryVersion('@tldraw/tlschema', '1.2.0', 'esm')
|
|
472
|
+
|
|
473
|
+
vi.runAllTimers()
|
|
474
|
+
|
|
475
|
+
expect(mockConsoleLog).toHaveBeenCalledTimes(1)
|
|
476
|
+
expect(mockConsoleLog.mock.calls[0]).toMatchInlineSnapshot(`
|
|
477
|
+
[
|
|
478
|
+
"[1;41;97m[tldraw][m [31;1mYou have multiple versions of tldraw libraries installed. This can lead to bugs and unexpected behavior.[m
|
|
479
|
+
|
|
480
|
+
The latest version you have installed is [1;94mv1.2.0[m. The following libraries are on the latest version:
|
|
481
|
+
• ✅ [1m@tldraw/tlschema[m
|
|
482
|
+
|
|
483
|
+
The following libraries are not on the latest version, or have multiple versions installed:
|
|
484
|
+
• ❌ [1mtldraw[m ([31mv1.0.0[m)
|
|
485
|
+
• ❌ [1m@tldraw/editor[m ([31mv1.1.0[m)
|
|
486
|
+
• ❌ [1m@tldraw/utils[m ([31mv1.1.0[m, [32mv1.2.0[m)",
|
|
487
|
+
]
|
|
488
|
+
`)
|
|
489
|
+
})
|
|
54
490
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
491
|
+
it('logs if multiple versions of the same library are installed', () => {
|
|
492
|
+
registerTldrawLibraryVersion('tldraw', '1.1.0', 'esm')
|
|
493
|
+
registerTldrawLibraryVersion('@tldraw/editor', '1.1.0', 'esm')
|
|
494
|
+
registerTldrawLibraryVersion('@tldraw/editor', '1.1.0', 'cjs')
|
|
495
|
+
registerTldrawLibraryVersion('@tldraw/utils', '1.1.0', 'esm')
|
|
496
|
+
registerTldrawLibraryVersion('@tldraw/utils', '1.1.0', 'cjs')
|
|
497
|
+
registerTldrawLibraryVersion('@tldraw/tlschema', '1.1.0', 'esm')
|
|
61
498
|
|
|
62
|
-
|
|
499
|
+
vi.runAllTimers()
|
|
63
500
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
501
|
+
expect(mockConsoleLog).toHaveBeenCalledTimes(1)
|
|
502
|
+
expect(mockConsoleLog.mock.calls[0]).toMatchInlineSnapshot(`
|
|
503
|
+
[
|
|
504
|
+
"[1;41;97m[tldraw][m [31;1mYou have multiple instances of some tldraw libraries active. This can lead to bugs and unexpected behavior. [m
|
|
68
505
|
|
|
69
|
-
|
|
506
|
+
This usually means that your bundler is misconfigured, and is importing the same library multiple times - usually once as an ES Module, and once as a CommonJS module.
|
|
70
507
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
508
|
+
The following libraries have been imported multiple times:
|
|
509
|
+
• ❌ [1m@tldraw/editor[m v1.1.0:
|
|
510
|
+
1. ES Modules
|
|
511
|
+
2. CommonJS
|
|
512
|
+
• ❌ [1m@tldraw/utils[m v1.1.0:
|
|
513
|
+
1. ES Modules
|
|
514
|
+
2. CommonJS
|
|
78
515
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
516
|
+
You should configure your bundler to only import one version of each library.",
|
|
517
|
+
]
|
|
518
|
+
`)
|
|
519
|
+
})
|
|
82
520
|
})
|
|
83
521
|
})
|
package/src/lib/version.ts
CHANGED
|
@@ -38,6 +38,22 @@ function getLibraryVersions(): TldrawLibraryVersionInfo {
|
|
|
38
38
|
return info
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Clears all registered library versions and resets warning state.
|
|
43
|
+
* This function is intended for testing purposes only to reset the global version tracking state.
|
|
44
|
+
* @returns void
|
|
45
|
+
* @example
|
|
46
|
+
* ```ts
|
|
47
|
+
* // In a test setup
|
|
48
|
+
* beforeEach(() => {
|
|
49
|
+
* clearRegisteredVersionsForTests()
|
|
50
|
+
* })
|
|
51
|
+
*
|
|
52
|
+
* // Now version tracking starts fresh for each test
|
|
53
|
+
* registerTldrawLibraryVersion('@tldraw/editor', '2.0.0', 'esm')
|
|
54
|
+
* ```
|
|
55
|
+
* @internal
|
|
56
|
+
*/
|
|
41
57
|
export function clearRegisteredVersionsForTests() {
|
|
42
58
|
const info = getLibraryVersions()
|
|
43
59
|
info.versions = []
|
|
@@ -48,7 +64,26 @@ export function clearRegisteredVersionsForTests() {
|
|
|
48
64
|
}
|
|
49
65
|
}
|
|
50
66
|
|
|
51
|
-
/**
|
|
67
|
+
/**
|
|
68
|
+
* Registers a tldraw library version for conflict detection.
|
|
69
|
+
* This function tracks different tldraw library versions to warn about potential conflicts
|
|
70
|
+
* when multiple versions are loaded simultaneously.
|
|
71
|
+
* @param name - The name of the tldraw library package (e.g., '\@tldraw/editor').
|
|
72
|
+
* @param version - The semantic version string (e.g., '2.0.0').
|
|
73
|
+
* @param modules - The module system being used ('esm' or 'cjs').
|
|
74
|
+
* @returns void
|
|
75
|
+
* @example
|
|
76
|
+
* ```ts
|
|
77
|
+
* // Register a library version during package initialization
|
|
78
|
+
* registerTldrawLibraryVersion('@tldraw/editor', '2.0.0', 'esm')
|
|
79
|
+
* registerTldrawLibraryVersion('@tldraw/tldraw', '2.0.0', 'esm')
|
|
80
|
+
*
|
|
81
|
+
* // If conflicting versions are detected, warnings will be logged:
|
|
82
|
+
* registerTldrawLibraryVersion('@tldraw/editor', '1.9.0', 'cjs')
|
|
83
|
+
* // Console warning about version mismatch will appear
|
|
84
|
+
* ```
|
|
85
|
+
* @internal
|
|
86
|
+
*/
|
|
52
87
|
export function registerTldrawLibraryVersion(name?: string, version?: string, modules?: string) {
|
|
53
88
|
if (!name || !version || !modules) {
|
|
54
89
|
if ((globalThis as any).TLDRAW_LIBRARY_IS_BUILD) {
|