pdf-lite 1.0.0

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 (206) hide show
  1. package/.commitlintrc.cjs +25 -0
  2. package/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
  3. package/.github/ISSUE_TEMPLATE/feature_request.md +19 -0
  4. package/.github/workflows/docs.yaml +93 -0
  5. package/.github/workflows/prepare-release.yaml +79 -0
  6. package/.github/workflows/release.yaml +80 -0
  7. package/.github/workflows/test.yaml +35 -0
  8. package/.husky/commit-msg +1 -0
  9. package/.husky/pre-commit +1 -0
  10. package/.prettierignore +4 -0
  11. package/.prettierrc +4 -0
  12. package/CONTRIBUTING.md +109 -0
  13. package/EXAMPLES.md +1515 -0
  14. package/LICENSE +21 -0
  15. package/README.md +285 -0
  16. package/examples/001-create-pdf.ts +112 -0
  17. package/examples/002-create-encrypted-pdf.ts +121 -0
  18. package/examples/003-sign-pdf.ts +347 -0
  19. package/examples/004-incremental-update.ts +206 -0
  20. package/examples/005-modify-acroform.ts +374 -0
  21. package/examples/006-tokeniser-example.ts +131 -0
  22. package/examples/007-decoder-example.ts +197 -0
  23. package/package.json +72 -0
  24. package/packages/pdf-lite/README.md +3 -0
  25. package/packages/pdf-lite/package.json +68 -0
  26. package/packages/pdf-lite/scripts/create-encryption-tests.sh +41 -0
  27. package/packages/pdf-lite/scripts/gen-signing-keys.sh +290 -0
  28. package/packages/pdf-lite/scripts/generate-all-signing-keys.sh +70 -0
  29. package/packages/pdf-lite/src/core/decoder.ts +454 -0
  30. package/packages/pdf-lite/src/core/generators.ts +128 -0
  31. package/packages/pdf-lite/src/core/incremental-parser.ts +221 -0
  32. package/packages/pdf-lite/src/core/index.ts +2 -0
  33. package/packages/pdf-lite/src/core/objects/pdf-array.ts +54 -0
  34. package/packages/pdf-lite/src/core/objects/pdf-boolean.ts +19 -0
  35. package/packages/pdf-lite/src/core/objects/pdf-comment.ts +50 -0
  36. package/packages/pdf-lite/src/core/objects/pdf-date.ts +74 -0
  37. package/packages/pdf-lite/src/core/objects/pdf-dictionary.ts +171 -0
  38. package/packages/pdf-lite/src/core/objects/pdf-hexadecimal.ts +54 -0
  39. package/packages/pdf-lite/src/core/objects/pdf-indirect-object.ts +137 -0
  40. package/packages/pdf-lite/src/core/objects/pdf-name.ts +19 -0
  41. package/packages/pdf-lite/src/core/objects/pdf-null.ts +15 -0
  42. package/packages/pdf-lite/src/core/objects/pdf-number.ts +98 -0
  43. package/packages/pdf-lite/src/core/objects/pdf-object-reference.ts +30 -0
  44. package/packages/pdf-lite/src/core/objects/pdf-object.ts +107 -0
  45. package/packages/pdf-lite/src/core/objects/pdf-start-xref.ts +39 -0
  46. package/packages/pdf-lite/src/core/objects/pdf-stream.ts +687 -0
  47. package/packages/pdf-lite/src/core/objects/pdf-string.ts +38 -0
  48. package/packages/pdf-lite/src/core/objects/pdf-trailer.ts +57 -0
  49. package/packages/pdf-lite/src/core/objects/pdf-xref-table.ts +264 -0
  50. package/packages/pdf-lite/src/core/parser.ts +22 -0
  51. package/packages/pdf-lite/src/core/ref.ts +102 -0
  52. package/packages/pdf-lite/src/core/serializer.ts +68 -0
  53. package/packages/pdf-lite/src/core/streams/object-stream.ts +20 -0
  54. package/packages/pdf-lite/src/core/tokeniser.ts +687 -0
  55. package/packages/pdf-lite/src/core/tokens/boolean-token.ts +20 -0
  56. package/packages/pdf-lite/src/core/tokens/byte-offset-token.ts +20 -0
  57. package/packages/pdf-lite/src/core/tokens/comment-token.ts +32 -0
  58. package/packages/pdf-lite/src/core/tokens/end-array-token.ts +10 -0
  59. package/packages/pdf-lite/src/core/tokens/end-dictionary-token.ts +10 -0
  60. package/packages/pdf-lite/src/core/tokens/end-object-token.ts +10 -0
  61. package/packages/pdf-lite/src/core/tokens/end-stream-token.ts +11 -0
  62. package/packages/pdf-lite/src/core/tokens/hexadecimal-token.ts +22 -0
  63. package/packages/pdf-lite/src/core/tokens/name-token.ts +19 -0
  64. package/packages/pdf-lite/src/core/tokens/null-token.ts +9 -0
  65. package/packages/pdf-lite/src/core/tokens/number-token.ts +164 -0
  66. package/packages/pdf-lite/src/core/tokens/object-reference-token.ts +24 -0
  67. package/packages/pdf-lite/src/core/tokens/start-array-token.ts +10 -0
  68. package/packages/pdf-lite/src/core/tokens/start-dictionary-token.ts +10 -0
  69. package/packages/pdf-lite/src/core/tokens/start-object-token.ts +28 -0
  70. package/packages/pdf-lite/src/core/tokens/start-stream-token.ts +52 -0
  71. package/packages/pdf-lite/src/core/tokens/start-xref-token.ts +10 -0
  72. package/packages/pdf-lite/src/core/tokens/stream-chunk-token.ts +8 -0
  73. package/packages/pdf-lite/src/core/tokens/string-token.ts +17 -0
  74. package/packages/pdf-lite/src/core/tokens/token.ts +43 -0
  75. package/packages/pdf-lite/src/core/tokens/trailer-token.ts +12 -0
  76. package/packages/pdf-lite/src/core/tokens/whitespace-token.ts +43 -0
  77. package/packages/pdf-lite/src/core/tokens/xref-table-entry-token.ts +65 -0
  78. package/packages/pdf-lite/src/core/tokens/xref-table-section-start-token.ts +31 -0
  79. package/packages/pdf-lite/src/core/tokens/xref-table-start-token.ts +13 -0
  80. package/packages/pdf-lite/src/crypto/ciphers/aes128.ts +63 -0
  81. package/packages/pdf-lite/src/crypto/ciphers/aes256.ts +50 -0
  82. package/packages/pdf-lite/src/crypto/ciphers/rc4.ts +82 -0
  83. package/packages/pdf-lite/src/crypto/constants.ts +10 -0
  84. package/packages/pdf-lite/src/crypto/key-derivation/key-derivation-aes256.ts +213 -0
  85. package/packages/pdf-lite/src/crypto/key-derivation/key-derivation.ts +122 -0
  86. package/packages/pdf-lite/src/crypto/key-gen/key-gen-aes256.ts +79 -0
  87. package/packages/pdf-lite/src/crypto/key-gen/key-gen-rc4-128.ts +190 -0
  88. package/packages/pdf-lite/src/crypto/key-gen/key-gen-rc4-40.ts +129 -0
  89. package/packages/pdf-lite/src/crypto/types.ts +6 -0
  90. package/packages/pdf-lite/src/crypto/utils.ts +81 -0
  91. package/packages/pdf-lite/src/filters/ascii85.ts +128 -0
  92. package/packages/pdf-lite/src/filters/asciihex.ts +55 -0
  93. package/packages/pdf-lite/src/filters/flate.ts +39 -0
  94. package/packages/pdf-lite/src/filters/lzw.ts +144 -0
  95. package/packages/pdf-lite/src/filters/pass-through.ts +37 -0
  96. package/packages/pdf-lite/src/filters/runlength.ts +92 -0
  97. package/packages/pdf-lite/src/filters/types.ts +21 -0
  98. package/packages/pdf-lite/src/index.ts +4 -0
  99. package/packages/pdf-lite/src/pdf/errors.ts +5 -0
  100. package/packages/pdf-lite/src/pdf/index.ts +4 -0
  101. package/packages/pdf-lite/src/pdf/pdf-document.ts +924 -0
  102. package/packages/pdf-lite/src/pdf/pdf-reader.ts +57 -0
  103. package/packages/pdf-lite/src/pdf/pdf-revision.ts +234 -0
  104. package/packages/pdf-lite/src/pdf/pdf-xref-lookup.ts +527 -0
  105. package/packages/pdf-lite/src/security/crypt-filters/aesv2.ts +58 -0
  106. package/packages/pdf-lite/src/security/crypt-filters/aesv3.ts +56 -0
  107. package/packages/pdf-lite/src/security/crypt-filters/base.ts +140 -0
  108. package/packages/pdf-lite/src/security/crypt-filters/identity.ts +40 -0
  109. package/packages/pdf-lite/src/security/crypt-filters/v2.ts +59 -0
  110. package/packages/pdf-lite/src/security/handlers/base.ts +625 -0
  111. package/packages/pdf-lite/src/security/handlers/pubSec.ts +413 -0
  112. package/packages/pdf-lite/src/security/handlers/utils.ts +304 -0
  113. package/packages/pdf-lite/src/security/handlers/v1.ts +225 -0
  114. package/packages/pdf-lite/src/security/handlers/v2.ts +128 -0
  115. package/packages/pdf-lite/src/security/handlers/v4.ts +379 -0
  116. package/packages/pdf-lite/src/security/handlers/v5.ts +298 -0
  117. package/packages/pdf-lite/src/security/types.ts +158 -0
  118. package/packages/pdf-lite/src/signing/document-security-store.ts +224 -0
  119. package/packages/pdf-lite/src/signing/index.ts +3 -0
  120. package/packages/pdf-lite/src/signing/signatures/adbe-pkcs7-detached.ts +154 -0
  121. package/packages/pdf-lite/src/signing/signatures/adbe-pkcs7-sha1.ts +161 -0
  122. package/packages/pdf-lite/src/signing/signatures/adbe-x509-rsa-sha1.ts +106 -0
  123. package/packages/pdf-lite/src/signing/signatures/base.ts +229 -0
  124. package/packages/pdf-lite/src/signing/signatures/etsi-cades-detached.ts +229 -0
  125. package/packages/pdf-lite/src/signing/signatures/etsi-rfc3161.ts +92 -0
  126. package/packages/pdf-lite/src/signing/signatures/index.ts +6 -0
  127. package/packages/pdf-lite/src/signing/signer.ts +120 -0
  128. package/packages/pdf-lite/src/signing/types.ts +86 -0
  129. package/packages/pdf-lite/src/signing/utils.ts +71 -0
  130. package/packages/pdf-lite/src/types.ts +44 -0
  131. package/packages/pdf-lite/src/utils/IterableReadableStream.ts +30 -0
  132. package/packages/pdf-lite/src/utils/algos.ts +446 -0
  133. package/packages/pdf-lite/src/utils/assert.ts +42 -0
  134. package/packages/pdf-lite/src/utils/bytesToHex.ts +18 -0
  135. package/packages/pdf-lite/src/utils/bytesToHexBytes.ts +27 -0
  136. package/packages/pdf-lite/src/utils/bytesToString.ts +17 -0
  137. package/packages/pdf-lite/src/utils/concatUint8Arrays.ts +26 -0
  138. package/packages/pdf-lite/src/utils/escapeString.ts +49 -0
  139. package/packages/pdf-lite/src/utils/hexBytesToBytes.ts +22 -0
  140. package/packages/pdf-lite/src/utils/hexBytesToString.ts +21 -0
  141. package/packages/pdf-lite/src/utils/hexToBytes.ts +18 -0
  142. package/packages/pdf-lite/src/utils/padBytes.ts +25 -0
  143. package/packages/pdf-lite/src/utils/predictors.ts +332 -0
  144. package/packages/pdf-lite/src/utils/replaceInBuffer.ts +56 -0
  145. package/packages/pdf-lite/src/utils/stringToBytes.ts +22 -0
  146. package/packages/pdf-lite/src/utils/stringToHexBytes.ts +23 -0
  147. package/packages/pdf-lite/src/utils/unescapeString.ts +123 -0
  148. package/packages/pdf-lite/test/acceptance/__snapshots__/versions.node.test.ts.snap +60766 -0
  149. package/packages/pdf-lite/test/acceptance/fixtures/1.3/basic.pdf +0 -0
  150. package/packages/pdf-lite/test/acceptance/fixtures/1.4/basic-aes-128.pdf +0 -0
  151. package/packages/pdf-lite/test/acceptance/fixtures/1.4/basic-aes-256.pdf +0 -0
  152. package/packages/pdf-lite/test/acceptance/fixtures/1.4/basic-rc4-128.pdf +0 -0
  153. package/packages/pdf-lite/test/acceptance/fixtures/1.4/basic-rc4-40.pdf +0 -0
  154. package/packages/pdf-lite/test/acceptance/fixtures/1.4/basic.pdf +0 -0
  155. package/packages/pdf-lite/test/acceptance/fixtures/1.5/basic.pdf +0 -0
  156. package/packages/pdf-lite/test/acceptance/fixtures/1.6/basic.pdf +0 -0
  157. package/packages/pdf-lite/test/acceptance/fixtures/1.7/basic.pdf +0 -0
  158. package/packages/pdf-lite/test/acceptance/fixtures/2.0/basic-aes-128.pdf +43 -0
  159. package/packages/pdf-lite/test/acceptance/fixtures/2.0/basic-aes-256.pdf +43 -0
  160. package/packages/pdf-lite/test/acceptance/fixtures/2.0/basic-rc4-128.pdf +43 -0
  161. package/packages/pdf-lite/test/acceptance/fixtures/2.0/basic-rc4-40.pdf +44 -0
  162. package/packages/pdf-lite/test/acceptance/fixtures/2.0/basic.pdf +79 -0
  163. package/packages/pdf-lite/test/acceptance/versions.node.test.ts +41 -0
  164. package/packages/pdf-lite/test/unit/__snapshots__/decoder.node.test.ts.snap +86947 -0
  165. package/packages/pdf-lite/test/unit/__snapshots__/tokeniser.node.test.ts.snap +131829 -0
  166. package/packages/pdf-lite/test/unit/ciphers.test.ts +61 -0
  167. package/packages/pdf-lite/test/unit/decoder.node.test.ts +21 -0
  168. package/packages/pdf-lite/test/unit/decoder.test.ts +567 -0
  169. package/packages/pdf-lite/test/unit/filters.test.ts +67 -0
  170. package/packages/pdf-lite/test/unit/fixtures/basic.pdf +0 -0
  171. package/packages/pdf-lite/test/unit/fixtures/encrypted_v1/basic-aes-128.pdf +0 -0
  172. package/packages/pdf-lite/test/unit/fixtures/encrypted_v1/basic-aes-256.pdf +0 -0
  173. package/packages/pdf-lite/test/unit/fixtures/encrypted_v1/basic-rc4-128.pdf +0 -0
  174. package/packages/pdf-lite/test/unit/fixtures/encrypted_v1/basic-rc4-40.pdf +43 -0
  175. package/packages/pdf-lite/test/unit/fixtures/protectedAdobeLivecycle.pdf +0 -0
  176. package/packages/pdf-lite/test/unit/fixtures/rsa-2048/index.ts +187 -0
  177. package/packages/pdf-lite/test/unit/fixtures/template.pdf +0 -0
  178. package/packages/pdf-lite/test/unit/incremental-update.test.ts +0 -0
  179. package/packages/pdf-lite/test/unit/objects.test.ts +0 -0
  180. package/packages/pdf-lite/test/unit/pdf-document-signing.test.ts +0 -0
  181. package/packages/pdf-lite/test/unit/pdf-revision.test.ts +195 -0
  182. package/packages/pdf-lite/test/unit/pdf.browser.test.ts +0 -0
  183. package/packages/pdf-lite/test/unit/predictors.test.ts +226 -0
  184. package/packages/pdf-lite/test/unit/ref.test.ts +158 -0
  185. package/packages/pdf-lite/test/unit/security-handlers.test.ts +645 -0
  186. package/packages/pdf-lite/test/unit/serializer.test.ts +81 -0
  187. package/packages/pdf-lite/test/unit/signature-objects.test.ts +814 -0
  188. package/packages/pdf-lite/test/unit/string-escaping.test.ts +84 -0
  189. package/packages/pdf-lite/test/unit/tokeniser.node.test.ts +38 -0
  190. package/packages/pdf-lite/test/unit/tokeniser.test.ts +1213 -0
  191. package/packages/pdf-lite/test/unit/utils.test.ts +248 -0
  192. package/packages/pdf-lite/test/unit/xref-lookup.test.ts +72 -0
  193. package/packages/pdf-lite/tsconfig.json +4 -0
  194. package/packages/pdf-lite/tsconfig.prod.json +8 -0
  195. package/packages/pdf-lite/typedoc.json +14 -0
  196. package/packages/pdf-lite/vitest.config.ts +43 -0
  197. package/pnpm-workspace.yaml +2 -0
  198. package/renovate.json +34 -0
  199. package/scripts/build-examples.ts +30 -0
  200. package/scripts/bump-version.sh +56 -0
  201. package/scripts/gen-html-docs.sh +21 -0
  202. package/scripts/gen-md-docs.sh +15 -0
  203. package/scripts/prepare-release.sh +33 -0
  204. package/tsconfig.json +22 -0
  205. package/tsconfig.prod.json +12 -0
  206. package/typedoc.json +34 -0
@@ -0,0 +1,226 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { Predictor } from '../../src/utils/predictors.js'
3
+ import { ByteArray } from '../../src/types.js'
4
+
5
+ describe('PDF predictors', () => {
6
+ describe('TIFFPredictor', () => {
7
+ it('should decode and encode', () => {
8
+ const data = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7])
9
+ const params = {
10
+ Predictor: 2,
11
+ Columns: 4,
12
+ Colors: 1,
13
+ BitsPerComponent: 8,
14
+ }
15
+ const decoded = Predictor.decode(data, params)
16
+ const encoded = Predictor.encode(decoded, params)
17
+ expect(encoded).toEqual(data)
18
+ })
19
+ })
20
+
21
+ describe('PNGPredictor', () => {
22
+ function makePngRow(
23
+ PdfStreamFilterType: number,
24
+ row: number[],
25
+ rowLength: number,
26
+ ): ByteArray {
27
+ return new Uint8Array([
28
+ PdfStreamFilterType,
29
+ ...row.slice(0, rowLength),
30
+ ])
31
+ }
32
+
33
+ it('should decode and encode with None filter', () => {
34
+ const row = [10, 20, 30, 40]
35
+ const data = makePngRow(0, row, 4)
36
+ const params = {
37
+ Predictor: 10,
38
+ Columns: 4,
39
+ Colors: 1,
40
+ BitsPerComponent: 8,
41
+ }
42
+ const decoded = Predictor.decode(data, params)
43
+ const encoded = Predictor.encode(
44
+ decoded,
45
+ { ...params, Predictor: 10 },
46
+ 0,
47
+ )
48
+ expect(encoded).toEqual(data)
49
+ })
50
+
51
+ it('should decode and encode with Sub filter', () => {
52
+ const row = [10, 30, 50, 70]
53
+ const data = makePngRow(1, row, 4)
54
+ const params = {
55
+ Predictor: 11,
56
+ Columns: 4,
57
+ Colors: 1,
58
+ BitsPerComponent: 8,
59
+ }
60
+ const decoded = Predictor.decode(data, params)
61
+ const encoded = Predictor.encode(
62
+ decoded,
63
+ { ...params, Predictor: 11 },
64
+ 1,
65
+ )
66
+ expect(encoded).toEqual(data)
67
+ })
68
+
69
+ it('should decode and encode with Up filter', () => {
70
+ const row = [10, 20, 30, 40]
71
+ const data = makePngRow(2, row, 4)
72
+ const params = {
73
+ Predictor: 12,
74
+ Columns: 4,
75
+ Colors: 1,
76
+ BitsPerComponent: 8,
77
+ }
78
+ const decoded = Predictor.decode(data, params)
79
+ const encoded = Predictor.encode(
80
+ decoded,
81
+ { ...params, Predictor: 12 },
82
+ 2,
83
+ )
84
+ expect(encoded).toEqual(data)
85
+ })
86
+
87
+ it('should decode and encode with Average filter', () => {
88
+ const row = [10, 20, 30, 40]
89
+ const data = makePngRow(3, row, 4)
90
+ const params = {
91
+ Predictor: 13,
92
+ Columns: 4,
93
+ Colors: 1,
94
+ BitsPerComponent: 8,
95
+ }
96
+ const decoded = Predictor.decode(data, params)
97
+ const encoded = Predictor.encode(
98
+ decoded,
99
+ { ...params, Predictor: 13 },
100
+ 3,
101
+ )
102
+ expect(encoded).toEqual(data)
103
+ })
104
+
105
+ it('should decode and encode with Paeth filter', () => {
106
+ const row = [10, 20, 30, 40]
107
+ const data = makePngRow(4, row, 4)
108
+ const params = {
109
+ Predictor: 14,
110
+ Columns: 4,
111
+ Colors: 1,
112
+ BitsPerComponent: 8,
113
+ }
114
+ const decoded = Predictor.decode(data, params)
115
+ const encoded = Predictor.encode(
116
+ decoded,
117
+ { ...params, Predictor: 14 },
118
+ 4,
119
+ )
120
+ expect(encoded).toEqual(data)
121
+ })
122
+ })
123
+
124
+ describe('PNGPredictor - Isolated Filters', () => {
125
+ function makePngRow(
126
+ PdfStreamFilterType: number,
127
+ row: number[],
128
+ rowLength: number,
129
+ ): ByteArray {
130
+ return new Uint8Array([
131
+ PdfStreamFilterType,
132
+ ...row.slice(0, rowLength),
133
+ ])
134
+ }
135
+
136
+ it('should correctly encode and decode with None filter', () => {
137
+ const row = [10, 20, 30, 40]
138
+ const data = makePngRow(0, row, 4)
139
+ const params = {
140
+ Predictor: 10,
141
+ Columns: 4,
142
+ Colors: 1,
143
+ BitsPerComponent: 8,
144
+ }
145
+ const decoded = Predictor.decode(data, params)
146
+ const encoded = Predictor.encode(
147
+ decoded,
148
+ { ...params, Predictor: 10 },
149
+ 0,
150
+ )
151
+ expect(encoded).toEqual(data)
152
+ })
153
+
154
+ it('should correctly encode and decode with Sub filter', () => {
155
+ const row = [10, 30, 50, 70]
156
+ const data = makePngRow(1, row, 4)
157
+ const params = {
158
+ Predictor: 11,
159
+ Columns: 4,
160
+ Colors: 1,
161
+ BitsPerComponent: 8,
162
+ }
163
+ const decoded = Predictor.decode(data, params)
164
+ const encoded = Predictor.encode(
165
+ decoded,
166
+ { ...params, Predictor: 11 },
167
+ 1,
168
+ )
169
+ expect(encoded).toEqual(data)
170
+ })
171
+
172
+ it('should correctly encode and decode with Up filter', () => {
173
+ const row = [10, 20, 30, 40]
174
+ const data = makePngRow(2, row, 4)
175
+ const params = {
176
+ Predictor: 12,
177
+ Columns: 4,
178
+ Colors: 1,
179
+ BitsPerComponent: 8,
180
+ }
181
+ const decoded = Predictor.decode(data, params)
182
+ const encoded = Predictor.encode(
183
+ decoded,
184
+ { ...params, Predictor: 12 },
185
+ 2,
186
+ )
187
+ expect(encoded).toEqual(data)
188
+ })
189
+
190
+ it('should correctly encode and decode with Average filter', () => {
191
+ const row = [10, 20, 30, 40]
192
+ const data = makePngRow(3, row, 4)
193
+ const params = {
194
+ Predictor: 13,
195
+ Columns: 4,
196
+ Colors: 1,
197
+ BitsPerComponent: 8,
198
+ }
199
+ const decoded = Predictor.decode(data, params)
200
+ const encoded = Predictor.encode(
201
+ decoded,
202
+ { ...params, Predictor: 13 },
203
+ 3,
204
+ )
205
+ expect(encoded).toEqual(data)
206
+ })
207
+
208
+ it('should correctly encode and decode with Paeth filter', () => {
209
+ const row = [10, 20, 30, 40]
210
+ const data = makePngRow(4, row, 4)
211
+ const params = {
212
+ Predictor: 14,
213
+ Columns: 4,
214
+ Colors: 1,
215
+ BitsPerComponent: 8,
216
+ }
217
+ const decoded = Predictor.decode(data, params)
218
+ const encoded = Predictor.encode(
219
+ decoded,
220
+ { ...params, Predictor: 14 },
221
+ 4,
222
+ )
223
+ expect(encoded).toEqual(data)
224
+ })
225
+ })
226
+ })
@@ -0,0 +1,158 @@
1
+ import { describe, expect, it, vi } from 'vitest'
2
+ import { Ref } from '../../src/core/ref'
3
+
4
+ describe('Ref', () => {
5
+ describe('constructor', () => {
6
+ it('should create a Ref with a primitive value', () => {
7
+ const ref = new Ref(42)
8
+ expect(ref.value).toBe(42)
9
+ })
10
+
11
+ it('should create a Ref with a string value', () => {
12
+ const ref = new Ref('hello')
13
+ expect(ref.value).toBe('hello')
14
+ })
15
+
16
+ it('should create a Ref with another Ref as value', () => {
17
+ const innerRef = new Ref(10)
18
+ const outerRef = new Ref(innerRef)
19
+ expect(outerRef.value).toBe(innerRef)
20
+ })
21
+
22
+ // Note: The Ref constructor has a self-reference check (value === this),
23
+ // but this edge case cannot be practically tested because JavaScript/TypeScript
24
+ // doesn't allow passing an object to its own constructor before it's fully constructed.
25
+ })
26
+
27
+ describe('resolve', () => {
28
+ it('should resolve a primitive value', () => {
29
+ const ref = new Ref(100)
30
+ expect(ref.resolve()).toBe(100)
31
+ })
32
+
33
+ it('should resolve a nested Ref chain', () => {
34
+ const innerRef = new Ref(50)
35
+ const middleRef = new Ref(innerRef)
36
+ const outerRef = new Ref(middleRef)
37
+ expect(outerRef.resolve()).toBe(50)
38
+ })
39
+
40
+ it('should resolve a deeply nested Ref chain', () => {
41
+ const ref1 = new Ref('deep')
42
+ const ref2 = new Ref(ref1)
43
+ const ref3 = new Ref(ref2)
44
+ const ref4 = new Ref(ref3)
45
+ expect(ref4.resolve()).toBe('deep')
46
+ })
47
+ })
48
+
49
+ describe('update', () => {
50
+ it('should update the value of a Ref', () => {
51
+ const ref = new Ref(10)
52
+ ref.update(20)
53
+ expect(ref.value).toBe(20)
54
+ })
55
+
56
+ it('should update to point to another Ref', () => {
57
+ const ref = new Ref(10)
58
+ const anotherRef = new Ref(30)
59
+ ref.update(anotherRef)
60
+ expect(ref.resolve()).toBe(30)
61
+ })
62
+
63
+ it('should ignore update to itself', () => {
64
+ const ref = new Ref(10)
65
+ ref.update(ref)
66
+ expect(ref.value).toBe(10)
67
+ })
68
+
69
+ it('should notify callbacks on update', () => {
70
+ const ref = new Ref(10)
71
+ const callback = vi.fn()
72
+ ref.onUpdate(callback)
73
+ ref.update(20)
74
+ expect(callback).toHaveBeenCalledWith(10, 20)
75
+ })
76
+
77
+ it('should notify multiple callbacks on update', () => {
78
+ const ref = new Ref(5)
79
+ const callback1 = vi.fn()
80
+ const callback2 = vi.fn()
81
+ ref.onUpdate(callback1)
82
+ ref.onUpdate(callback2)
83
+ ref.update(15)
84
+ expect(callback1).toHaveBeenCalledWith(5, 15)
85
+ expect(callback2).toHaveBeenCalledWith(5, 15)
86
+ })
87
+
88
+ it('should resolve old and new values when notifying callbacks', () => {
89
+ const innerRef = new Ref(100)
90
+ const ref = new Ref(innerRef)
91
+ const callback = vi.fn()
92
+ ref.onUpdate(callback)
93
+ ref.update(200)
94
+ expect(callback).toHaveBeenCalledWith(100, 200)
95
+ })
96
+ })
97
+
98
+ describe('equals', () => {
99
+ it('should return true when comparing equal primitive values', () => {
100
+ const ref = new Ref(42)
101
+ expect(ref.equals(42)).toBe(true)
102
+ })
103
+
104
+ it('should return false when comparing unequal primitive values', () => {
105
+ const ref = new Ref(42)
106
+ expect(ref.equals(43)).toBe(false)
107
+ })
108
+
109
+ it('should return true when comparing two Refs with equal resolved values', () => {
110
+ const ref1 = new Ref(100)
111
+ const ref2 = new Ref(100)
112
+ expect(ref1.equals(ref2)).toBe(true)
113
+ })
114
+
115
+ it('should return false when comparing two Refs with different resolved values', () => {
116
+ const ref1 = new Ref(100)
117
+ const ref2 = new Ref(200)
118
+ expect(ref1.equals(ref2)).toBe(false)
119
+ })
120
+
121
+ it('should return true for nested Refs with equal resolved values', () => {
122
+ const inner1 = new Ref(50)
123
+ const outer1 = new Ref(inner1)
124
+ const inner2 = new Ref(50)
125
+ const outer2 = new Ref(inner2)
126
+ expect(outer1.equals(outer2)).toBe(true)
127
+ })
128
+
129
+ it('should return false when comparing with undefined', () => {
130
+ const ref = new Ref(42)
131
+ expect(ref.equals(undefined)).toBe(false)
132
+ })
133
+ })
134
+
135
+ describe('onUpdate', () => {
136
+ it('should register a callback that is called on update', () => {
137
+ const ref = new Ref(10)
138
+ const callback = vi.fn()
139
+ ref.onUpdate(callback)
140
+ ref.update(20)
141
+ expect(callback).toHaveBeenCalledOnce()
142
+ expect(callback).toHaveBeenCalledWith(10, 20)
143
+ })
144
+
145
+ it('should allow registering multiple callbacks and call all of them', () => {
146
+ const ref = new Ref(10)
147
+ const callback1 = vi.fn()
148
+ const callback2 = vi.fn()
149
+ ref.onUpdate(callback1)
150
+ ref.onUpdate(callback2)
151
+ ref.update(30)
152
+ expect(callback1).toHaveBeenCalledOnce()
153
+ expect(callback2).toHaveBeenCalledOnce()
154
+ expect(callback1).toHaveBeenCalledWith(10, 30)
155
+ expect(callback2).toHaveBeenCalledWith(10, 30)
156
+ })
157
+ })
158
+ })