fast-json-stringify 7.0.1 → 7.1.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 (40) hide show
  1. package/.github/dependabot.yml +1 -0
  2. package/.github/workflows/ci.yml +1 -1
  3. package/.github/workflows/lock-threads.yml +1 -1
  4. package/.github/workflows/release.yml +24 -0
  5. package/README.md +20 -3
  6. package/benchmark/bench-cmp-lib.js +63 -38
  7. package/benchmark/bench-thread.js +8 -2
  8. package/benchmark/bench.js +86 -0
  9. package/benchmark/fixtures/api-responses.js +266 -0
  10. package/index.js +366 -98
  11. package/lib/location.js +3 -1
  12. package/lib/serializer.js +41 -37
  13. package/lib/standalone.js +22 -3
  14. package/lib/validator.js +22 -3
  15. package/package.json +4 -5
  16. package/test/additionalProperties.test.js +40 -0
  17. package/test/anyof.test.js +42 -0
  18. package/test/array.test.js +26 -1
  19. package/test/basic.test.js +1 -1
  20. package/test/code-generation-fallbacks.test.js +158 -0
  21. package/test/compile-validators.test.js +110 -0
  22. package/test/const.test.js +7 -6
  23. package/test/date.test.js +28 -1
  24. package/test/debug-mode.test.js +8 -0
  25. package/test/deopt.test.js +251 -0
  26. package/test/if-then-else.test.js +145 -0
  27. package/test/inferType.test.js +1 -1
  28. package/test/integer.test.js +1 -1
  29. package/test/oneof.test.js +242 -0
  30. package/test/optional-property-prefix.test.js +119 -0
  31. package/test/property-names.test.js +41 -0
  32. package/test/regex.test.js +1 -1
  33. package/test/schema-depth.test.js +227 -0
  34. package/test/standalone-mode.test.js +109 -2
  35. package/test/string.test.js +8 -0
  36. package/test/surrogate.test.js +1 -1
  37. package/test/typesArray.test.js +59 -0
  38. package/test/utils/validator.js +20 -0
  39. package/types/index.d.ts +20 -0
  40. package/types/index.tst.ts +13 -0
@@ -0,0 +1,266 @@
1
+ 'use strict'
2
+
3
+ const addressSchema = {
4
+ type: 'object',
5
+ properties: {
6
+ street: { type: 'string' },
7
+ city: { type: 'string' },
8
+ postalCode: { type: 'string' },
9
+ country: { type: 'string' },
10
+ apartment: { type: ['string', 'null'] }
11
+ }
12
+ }
13
+
14
+ const productSchema = {
15
+ type: 'object',
16
+ properties: {
17
+ id: { type: 'string' },
18
+ sku: { type: 'string' },
19
+ name: { type: 'string' },
20
+ description: { type: 'string' },
21
+ price: {
22
+ type: 'object',
23
+ properties: {
24
+ amount: { type: 'integer' },
25
+ currency: { type: 'string' },
26
+ discount: { type: ['number', 'null'] }
27
+ }
28
+ },
29
+ inventory: {
30
+ type: 'object',
31
+ properties: {
32
+ available: { type: 'boolean' },
33
+ quantity: { type: 'integer' },
34
+ warehouse: { type: 'string' }
35
+ }
36
+ },
37
+ tags: { type: 'array', items: { type: 'string' } },
38
+ images: {
39
+ type: 'array',
40
+ items: {
41
+ type: 'object',
42
+ properties: {
43
+ url: { type: 'string' },
44
+ alt: { type: 'string' },
45
+ primary: { type: 'boolean' }
46
+ }
47
+ }
48
+ },
49
+ rating: {
50
+ type: 'object',
51
+ properties: {
52
+ average: { type: 'number' },
53
+ count: { type: 'integer' }
54
+ }
55
+ },
56
+ updatedAt: { type: 'string' }
57
+ }
58
+ }
59
+
60
+ // Deterministic synthetic responses include Unicode and escaping, as API payloads do.
61
+ const address = {
62
+ street: '12 Example Street',
63
+ city: 'Łódź',
64
+ postalCode: '90-001',
65
+ country: 'PL',
66
+ apartment: null
67
+ }
68
+
69
+ const products = Array.from({ length: 50 }, (_, i) => ({
70
+ id: `product_${i + 1}`,
71
+ sku: `MUG-${1000 + i}`,
72
+ name: `Café mug "Everyday" — ${i + 1}`,
73
+ description: 'Handmade ceramic mug ☕\nDishwasher safe. Available in blue, green, and cream.',
74
+ price: {
75
+ amount: 1800 + i * 100,
76
+ currency: 'EUR',
77
+ discount: i % 2 === 0 ? null : 0.1
78
+ },
79
+ inventory: { available: i % 7 !== 0, quantity: i * 3, warehouse: 'eu-central' },
80
+ tags: ['home', 'kitchen', i % 2 === 0 ? 'ceramic' : 'stoneware'],
81
+ images: [
82
+ { url: `https://example.com/products/${i + 1}/front.jpg`, alt: 'Front view', primary: true },
83
+ { url: `https://example.com/products/${i + 1}/side.jpg`, alt: 'Side view', primary: false }
84
+ ],
85
+ rating: { average: 4.2 + (i % 5) / 10, count: 10 + i * 7 },
86
+ updatedAt: '2026-01-15T10:30:00.000Z'
87
+ }))
88
+
89
+ const orderItems = products.slice(0, 5).map((product, i) => ({
90
+ product,
91
+ quantity: i + 1,
92
+ unitPrice: product.price.amount,
93
+ total: product.price.amount * (i + 1)
94
+ }))
95
+ const subtotal = orderItems.reduce((sum, item) => sum + item.total, 0)
96
+
97
+ module.exports = [
98
+ {
99
+ name: 'user profile response',
100
+ schema: {
101
+ type: 'object',
102
+ properties: {
103
+ data: {
104
+ type: 'object',
105
+ properties: {
106
+ id: { type: 'string' },
107
+ email: { type: 'string' },
108
+ displayName: { type: 'string' },
109
+ bio: { type: 'string' },
110
+ avatarUrl: { type: ['string', 'null'] },
111
+ active: { type: 'boolean' },
112
+ roles: { type: 'array', items: { type: 'string' } },
113
+ address: addressSchema,
114
+ organization: {
115
+ type: 'object',
116
+ properties: {
117
+ id: { type: 'string' },
118
+ name: { type: 'string' },
119
+ plan: { type: 'string' }
120
+ }
121
+ },
122
+ preferences: {
123
+ type: 'object',
124
+ properties: {
125
+ locale: { type: 'string' },
126
+ timezone: { type: 'string' },
127
+ notifications: {
128
+ type: 'object',
129
+ properties: {
130
+ email: { type: 'boolean' },
131
+ push: { type: 'boolean' },
132
+ digest: { type: 'string' }
133
+ }
134
+ }
135
+ }
136
+ },
137
+ createdAt: { type: 'string' },
138
+ lastLoginAt: { type: ['string', 'null'] }
139
+ }
140
+ },
141
+ meta: {
142
+ type: 'object',
143
+ properties: { requestId: { type: 'string' }, version: { type: 'integer' } }
144
+ }
145
+ }
146
+ },
147
+ input: {
148
+ data: {
149
+ id: 'user_123',
150
+ email: 'alex@example.com',
151
+ displayName: 'Alex Żółć',
152
+ bio: 'Engineer & coffee enthusiast ☕\nWorking on "Project Atlas".',
153
+ avatarUrl: null,
154
+ active: true,
155
+ roles: ['member', 'billing-admin'],
156
+ address,
157
+ organization: { id: 'org_456', name: 'Example & Co.', plan: 'business' },
158
+ preferences: {
159
+ locale: 'pl-PL',
160
+ timezone: 'Europe/Warsaw',
161
+ notifications: { email: true, push: false, digest: 'weekly' }
162
+ },
163
+ createdAt: '2025-06-01T08:00:00.000Z',
164
+ lastLoginAt: '2026-01-15T10:30:00.000Z'
165
+ },
166
+ meta: { requestId: 'req_profile_123', version: 1 }
167
+ }
168
+ },
169
+ {
170
+ name: 'order response',
171
+ schema: {
172
+ type: 'object',
173
+ properties: {
174
+ id: { type: 'string' },
175
+ status: { type: 'string' },
176
+ customer: {
177
+ type: 'object',
178
+ properties: {
179
+ id: { type: 'string' },
180
+ name: { type: 'string' },
181
+ email: { type: 'string' }
182
+ }
183
+ },
184
+ shippingAddress: addressSchema,
185
+ items: {
186
+ type: 'array',
187
+ items: {
188
+ type: 'object',
189
+ properties: {
190
+ product: productSchema,
191
+ quantity: { type: 'integer' },
192
+ unitPrice: { type: 'integer' },
193
+ total: { type: 'integer' }
194
+ }
195
+ }
196
+ },
197
+ totals: {
198
+ type: 'object',
199
+ properties: {
200
+ subtotal: { type: 'integer' },
201
+ shipping: { type: 'integer' },
202
+ tax: { type: 'integer' },
203
+ total: { type: 'integer' },
204
+ currency: { type: 'string' }
205
+ }
206
+ },
207
+ payment: {
208
+ type: 'object',
209
+ properties: {
210
+ method: { type: 'string' },
211
+ paid: { type: 'boolean' },
212
+ transactionId: { type: ['string', 'null'] }
213
+ }
214
+ },
215
+ notes: { type: ['string', 'null'] },
216
+ createdAt: { type: 'string' }
217
+ }
218
+ },
219
+ input: {
220
+ id: 'order_789',
221
+ status: 'processing',
222
+ customer: { id: 'user_123', name: 'Alex Żółć', email: 'alex@example.com' },
223
+ shippingAddress: address,
224
+ items: orderItems,
225
+ totals: { subtotal, shipping: 500, tax: 4200, total: subtotal + 4700, currency: 'EUR' },
226
+ payment: { method: 'card', paid: true, transactionId: 'txn_987' },
227
+ notes: 'Please ring the bell marked "Office".\nLeave with reception if unavailable.',
228
+ createdAt: '2026-01-15T10:30:00.000Z'
229
+ }
230
+ },
231
+ {
232
+ name: 'paginated products response',
233
+ schema: {
234
+ type: 'object',
235
+ properties: {
236
+ data: { type: 'array', items: productSchema },
237
+ pagination: {
238
+ type: 'object',
239
+ properties: {
240
+ page: { type: 'integer' },
241
+ pageSize: { type: 'integer' },
242
+ total: { type: 'integer' },
243
+ hasNextPage: { type: 'boolean' }
244
+ }
245
+ },
246
+ links: {
247
+ type: 'object',
248
+ properties: {
249
+ self: { type: 'string' },
250
+ next: { type: 'string' },
251
+ previous: { type: ['string', 'null'] }
252
+ }
253
+ }
254
+ }
255
+ },
256
+ input: {
257
+ data: products,
258
+ pagination: { page: 1, pageSize: 50, total: 1248, hasNextPage: true },
259
+ links: {
260
+ self: '/products?page=1&pageSize=50',
261
+ next: '/products?page=2&pageSize=50',
262
+ previous: null
263
+ }
264
+ }
265
+ }
266
+ ]